using System; using System.Collections.Generic; using System.IO; using System.Threading; using System.Diagnostics; using System.Security.Cryptography; using Sog; using Sog.IO; using Google.Protobuf.WellKnownTypes; using Google.Protobuf; using System.Linq; using Enum = Google.Protobuf.WellKnownTypes.Enum; namespace SMAgent { public class SMAgentCommand : Singleton { // 显示的内存单位 private readonly int MEM_UNIT = 1024 * 1024; private Queue m_cmdQueue = new Queue(); private SMAgentDoCommandReq m_nowCmd = null; private long m_nowCmdStartTimeSecond = 0;//超时用 private SMAgentDoCommandRes m_nowCmdRes = null; //回应 private bool m_nowCmdDoFinish = false; private Thread m_workThread; private object m_locker = new object(); private bool m_bRunning = true; private void CheckStart() { if (m_workThread == null) { m_workThread = new Thread(WorkThreadFun); m_workThread.Start(); } } public void Stop() { m_bRunning = false; } private void WorkThreadFun() { while(m_bRunning) { try { bool needDoCmd = false; lock (m_locker) { if (m_nowCmd != null && m_nowCmdDoFinish == false) { needDoCmd = true; } } if(needDoCmd) { var res = DoCommand(m_nowCmd); lock (m_locker) { m_nowCmdRes = res; m_nowCmdDoFinish = true; } } Thread.Sleep(10); } catch(Exception ex) { TraceLog.Exception(ex); } } } public void Update(long tMs) { lock (m_locker) { //取出新的请求,工作线程会处理这个cmd if (m_nowCmd == null && m_nowCmdDoFinish == false && m_cmdQueue.Count > 0) { m_nowCmdStartTimeSecond = SMAgentUtils.GetTimeSecond(); m_nowCmdRes = null; m_nowCmdDoFinish = false; m_nowCmd = m_cmdQueue.Dequeue(); } //有正在执行的命令 //这个说明这个命令执行完成了,工作线程完成了这个任务,那么主线程回应消息 if (m_nowCmd != null && m_nowCmdDoFinish) { if (m_nowCmdRes != null) { SMAgentNet.Instance.SendMsgToCenter(m_nowCmdRes, SMMsgID.AgentDoCommandRes); } m_nowCmdDoFinish = false; m_nowCmdStartTimeSecond = 0; m_nowCmdRes = null; //这个m_nowCmd赋值放在最后,工作线程以这个m_nowCmd是否为空来判断是否有任务处理 m_nowCmd = null; } } } public void OnCommand(SMAgentDoCommandReq req) { m_cmdQueue.Enqueue(req); //开启工作线程 CheckStart(); } private SMAgentDoCommandRes DoCommand(SMAgentDoCommandReq req) { switch (req.Command) { case "start": return DoStartCommand(req); case "stop": return DoStopCommand(req); case "hotfix": return DoHotfixCommand(req); case "reloadconfig": return DoReloadConfigCommand(req); case "reloadcluster": return DoReloadClusterCommand(req); case "check": case "list": return DoCheckCommand(req); case "kill": return DoKillCommand(req); case "gmcmd": return DoGmCmdCommand(req); case "updateagent": return DoUpdateAgentCommand(req); case "checkagent": case "checkpushfile": return DoCheckAgentCommand(req); case "shell": return DoShellCommand(req); case "cancelshell": return DoCancelShellCommand(req); case "pull": case "pullagentlog": return DoPullFileCmd(req); case "checkagentmem": return DoCheckAgentMemCmd(req); default: TraceLog.Error("SMAgentCommand.DoCommand invalid cmd {0}", req.Command); return null; } } private SMAgentDoCommandRes DoStartCommand(SMAgentDoCommandReq req) { SMAgentDoCommandRes res = new SMAgentDoCommandRes(); CheckSogLoaderIsRunning(req.WorkPath, req.ServerId, res); if (res.ResultCode == ResResultCode.NotRunning) { DoSogLoaderCommand(req, "start",res); return CheckAndAckReq(req,600,res); } else { return CheckAndAckReq(req, 0,res); } } private SMAgentDoCommandRes DoStopCommand(SMAgentDoCommandReq req) { SMAgentDoCommandRes res = new SMAgentDoCommandRes(); CheckSogLoaderIsRunning(req.WorkPath, req.ServerId, res); if (res.ResultCode == ResResultCode.Running) { DoSogLoaderCommand(req, "stop", res); return CheckAndAckReq(req, 600, res); } else { return CheckAndAckReq(req, 0, res); } } private SMAgentDoCommandRes DoHotfixCommand(SMAgentDoCommandReq req) { SMAgentDoCommandRes res = new SMAgentDoCommandRes(); CheckSogLoaderIsRunning(req.WorkPath, req.ServerId, res); //运行着才能reload if (res.ResultCode == ResResultCode.Running) { DoSogLoaderCommand(req, "hotfix", res); return CheckAndAckReq(req, 500, res); } else { return CheckAndAckReq(req, 0, res); } } private SMAgentDoCommandRes DoReloadConfigCommand(SMAgentDoCommandReq req) { SMAgentDoCommandRes res = new SMAgentDoCommandRes(); CheckSogLoaderIsRunning(req.WorkPath, req.ServerId, res); //运行着才能reload if (res.ResultCode == ResResultCode.Running) { DoSogLoaderCommand(req, "reloadconfig", res); return CheckAndAckReq(req, 200, res); } else { return CheckAndAckReq(req, 0, res); } } private SMAgentDoCommandRes DoReloadClusterCommand(SMAgentDoCommandReq req) { SMAgentDoCommandRes res = new SMAgentDoCommandRes(); CheckSogLoaderIsRunning(req.WorkPath, req.ServerId, res); //运行着才能reload if (res.ResultCode == ResResultCode.Running) { DoSogLoaderCommand(req, "reloadcluster", res); return CheckAndAckReq(req, 500, res); } else { return CheckAndAckReq(req, 0, res); } } private SMAgentDoCommandRes DoUpdateAgentCommand(SMAgentDoCommandReq req) { var res = new SMAgentDoCommandRes(); res.Command = req.Command; res.SeqNum = req.SeqNum; res.ServerId = req.ServerId; res.HostName = SMAgentUtils.HostName; res.ResultCode = ResResultCode.Success; try { var procs = Process.GetProcessesByName(req.ExeFileName); if (procs.Length > 0) { res.ResultCode = ResResultCode.Fail; res.Result = "update fail, last update not finish..."; return res; } string curPath = System.Environment.CurrentDirectory; SMAgentUtils.StartProcess(req.ExeFileName, curPath, ""); return res; } catch (Exception ex) { TraceLog.Exception(ex); res.ResultCode = ResResultCode.Exception; res.Result += "agent updater can not start... Exception:" + ex.Message; return res; } } private void CheckAgentFile(SMAgentDoCommandReq req, SMAgentDoCommandRes res) { try { string succ = ""; string fail = ""; res.ResultCode = ResResultCode.Success; foreach (var fileAttr in req.FileList) { string fileFullPath = Path.Combine(fileAttr.FilePath, fileAttr.FileName); var fileMd5 = HashHelper.MD5File(fileFullPath); if (fileAttr.FileMd5 == fileMd5) { succ += string.Format("\n{0}:\t\t{1}", fileAttr.FileName, fileMd5); } else { fail += string.Format("\n{0}: center {1} \t agent {2}", fileAttr.FileName, fileAttr.FileMd5, fileMd5); if (! string.IsNullOrEmpty(fileMd5)) { res.ResultCode = ResResultCode.Fail; } } } if (!string.IsNullOrEmpty(succ)) { res.Result += "\n--------------- Check Succ ---------------"; res.Result += succ; } if (! string.IsNullOrEmpty(fail)) { res.Result += "\n--------------- Check Fail ---------------"; res.Result += fail; } } catch (Exception e) { TraceLog.Exception(e); res.ResultCode = ResResultCode.Exception; res.Result += "\n" + e.Message; } } private SMAgentDoCommandRes DoCheckAgentCommand(SMAgentDoCommandReq req) { SMAgentDoCommandRes res = new SMAgentDoCommandRes(); CheckAgentFile(req, res); res.Command = req.Command; res.SeqNum = req.SeqNum; res.ServerId = req.ServerId; res.AddInfo = "\n\nRunning SMAgent.dll: " + HashHelper.MD5File("./SMAgent.dll") + "\t" + File.GetLastWriteTimeUtc("./SMAgent.dll"); res.HostName = SMAgentUtils.HostName; return res; } private void DoSogLoaderCommand(SMAgentDoCommandReq req,string command, SMAgentDoCommandRes res) { if (Directory.Exists(req.WorkPath) == false) { res.ResultCode = ResResultCode.Exception; res.Result += " Directory not exist (" + req.WorkPath + ")"; return; } string exeFullPath = req.WorkPath + "/" + req.ExeFileName; // windows平台下如果exe文件不存在, 尝试加上".exe"后缀 if (! File.Exists(exeFullPath) && OSUtils.IsWindows() && ! exeFullPath.Contains(".exe")) { exeFullPath += ".exe"; if (! File.Exists(exeFullPath)) { res.ResultCode = ResResultCode.Exception; res.Result += " Not such file or directory(" + exeFullPath + ")"; return; } } TraceLog.Trace("SMAgentCommand.DoSogLoaderCommand cmd {0} {1}", exeFullPath, req.CmdArgs); try { SMAgentUtils.StartProcess(exeFullPath, req.WorkPath, req.CmdArgs); } catch (Exception ex) { TraceLog.Exception(ex); res.ResultCode = ResResultCode.Exception; res.Result += " " + ex.Message; } } private SMAgentDoCommandRes DoCheckAgentMemCmd(SMAgentDoCommandReq req) { SMAgentDoCommandRes res = new SMAgentDoCommandRes(); res.Command = req.Command; res.SeqNum = req.SeqNum; res.ServerId = req.ServerId; res.HostName = SMAgentUtils.HostName; ShareMemoryCommand shareCommand = null; res.AddInfo = "0 KB"; try { shareCommand = new ShareMemoryCommand("./", SMAgentUtils.GetAppID()); bool attachSuccess = shareCommand.Attach(); if (attachSuccess == false) { res.ResultCode = ResResultCode.NotRunning; return res; } int procId = shareCommand.ReadProcessId(); if (procId <= 0) { res.ResultCode = ResResultCode.NotRunning; return res; } using (Process proc = Process.GetProcessById(procId)) { if (proc == null || proc.HasExited) { res.ResultCode = ResResultCode.NotRunning; return res; } if (proc.ProcessName != "SMAgent") { res.ResultCode = ResResultCode.NotRunning; return res; } res.AddInfo = string.Format("{0:F} MB", proc.WorkingSet64 * 1.0 / MEM_UNIT); res.ResultCode = ResResultCode.Success; return res; } } catch (Exception ex) { TraceLog.Exception(ex); //算了,这个异常多半是GetProcessById抛出的,说明没运行 res.ResultCode = ResResultCode.NotRunning; } finally { if (shareCommand != null) { shareCommand.Close(); } } return res; } /// /// /// /// /// /// 0 running,-1,notrunning ,-2 notack /// /// private int CheckSogLoaderIsRunning(string workPath, uint serverId, SMAgentDoCommandRes res) { ShareMemoryCommand shareCommand = null; res.AddInfo = "0 KB"; try { string strserverId = ServerIDUtils.IDToString(serverId); string shmFile = "sog_shf_" + strserverId; shareCommand = new ShareMemoryCommand(workPath, serverId); bool attachSuccess = shareCommand.Attach(); if (attachSuccess == false) { res.ResultCode = ResResultCode.NotRunning; return -1; } int procId = shareCommand.ReadProcessId(); if(procId <= 0) { res.ResultCode = ResResultCode.NotRunning; return -1; } using (Process proc = Process.GetProcesses().FirstOrDefault(p => p.Id == procId)) { if (proc == null || proc.HasExited) { res.ResultCode = ResResultCode.NotRunning; return -1; } if (proc.ProcessName != "SogLoader") { res.ResultCode = ResResultCode.NotRunning; return -1; } res.AddInfo = string.Format("{0:F} MB", proc.WorkingSet64 * 1.0 / MEM_UNIT); res.ResultCode = ResResultCode.Running; return 0; } } catch(Exception ex) { TraceLog.Exception(ex); //算了,这个异常多半是GetProcessById抛出的,说明没运行 res.ResultCode = ResResultCode.NotRunning; } finally { if(shareCommand != null) { shareCommand.Close(); } } return -1; } private SMAgentDoCommandRes CheckAndAckReq(SMAgentDoCommandReq req, int firstWaitTimeMs, SMAgentDoCommandRes res) { TraceLog.Trace("SMAgentCommand.CheckAndAckReq begin result {0} req timeout {1}", res.ResultCode, req.StopTimeout); bool success = false; long startTime = SMAgentUtils.GetTimeSecond(); if (res.ResultCode == ResResultCode.NotRunning && (req.Command == "stop" || req.Command == "kill" || req.Command == "reload" || req.Command == "reloadconfig" || req.Command == "reloadcluster")) { success = true; res.Result += " Dont Need or Cannot Run [" + req.Command + "], Server not Running"; } else if(res.ResultCode == ResResultCode.Running && req.Command == "start") { success = true; res.Result += " Dont Need [" + req.Command + "], Server is Running"; } else if(res.ResultCode != ResResultCode.Exception && res.ResultCode != ResResultCode.Fail && res.ResultCode != ResResultCode.TimeOut) { for (int i = 0; ; i++) { Thread.Sleep(i == 0 ? firstWaitTimeMs : 200); int ret = CheckSogLoaderIsRunning(req.WorkPath, req.ServerId, res); if (req.Command == "stop" || req.Command == "kill") { if (ret == -1) { success = true; break; } } else if (ret == 0) { success = true; break; } //超过10次后计算超时时间 if(i > 10 && success == false && SMAgentUtils.GetTimeSecond() > startTime + req.StopTimeout ) { success = false; break; } } } res.Command = req.Command; res.SeqNum = req.SeqNum; res.ServerId = req.ServerId; res.ServerIdStr = ServerIDUtils.IDToString(req.ServerId); res.ServerType = System.Enum.GetName(typeof(ServerType), ServerIDUtils.GetServerType(req.ServerId)); res.HostName = SMAgentUtils.HostName; if (success) { res.ResultCode = ResResultCode.Success; } else if(res.ResultCode != ResResultCode.Exception && firstWaitTimeMs > 0) { res.ResultCode = ResResultCode.TimeOut; res.Result += " maybe the CMD: " + req.Command + " Because of Exception Kill then Server " + req.ServerId; } TraceLog.Trace("SMAgentCommand.CheckAndAckReq end result {0}", res.ResultCode); return res; } private SMAgentDoCommandRes DoCheckCommand(SMAgentDoCommandReq req) { SMAgentDoCommandRes res = new SMAgentDoCommandRes(); int iResult = CheckSogLoaderIsRunning(req.WorkPath, req.ServerId, res); res.Command = req.Command; res.SeqNum = req.SeqNum; res.ServerId = req.ServerId; res.HostName = SMAgentUtils.HostName; return res; } private void DoSogLoaderKill(string workPath, uint serverId) { ShareMemoryCommand shareCommand = null; try { string strserverId = ServerIDUtils.IDToString(serverId); string shmFile = "sog_shf_" + strserverId; shareCommand = new ShareMemoryCommand(workPath, serverId); bool attachSuccess = shareCommand.Attach(); if (attachSuccess == false) { return; } int procId = shareCommand.ReadProcessId(); if (procId <= 0) { return; } using (Process proc = Process.GetProcessById(procId)) { if (proc == null) { return; } if (proc.ProcessName != "SogLoader") { return; } proc.Kill(); return; } } catch (Exception ex) { TraceLog.Exception(ex); } finally { if (shareCommand != null) { shareCommand.Close(); } } return; } private SMAgentDoCommandRes DoKillCommand(SMAgentDoCommandReq req) { SMAgentDoCommandRes res = new SMAgentDoCommandRes(); CheckSogLoaderIsRunning(req.WorkPath, req.ServerId, res); //运行着才能kill if (res.ResultCode == ResResultCode.Running) { DoSogLoaderKill(req.WorkPath, req.ServerId); Thread.Sleep(10); return CheckAndAckReq(req, 500, res); } else { return CheckAndAckReq(req, 0, res); } } private SMAgentDoCommandRes DoGmCmdCommand(SMAgentDoCommandReq req) { SMAgentDoCommandRes res = new SMAgentDoCommandRes(); CheckSogLoaderIsRunning(req.WorkPath, req.ServerId, res); //运行着才能gmcmd if (res.ResultCode == ResResultCode.Running) { DoSogLoaderCommand(req, "gmcmd", res); Thread.Sleep(10); } res.Command = req.Command; res.SeqNum = req.SeqNum; res.ServerId = req.ServerId; res.HostName = SMAgentUtils.HostName; return res; } private SMAgentDoCommandRes DoShellCommand(SMAgentDoCommandReq req) { SMShell.req = req; string msg; int ret = SMShell.Exec(req.CmdArgs, out msg); if(ret == -1) { SMAgentDoCommandRes res = new SMAgentDoCommandRes(); res.Command = req.Command; res.SeqNum = req.SeqNum; res.ServerId = req.ServerId; res.HostName = SMAgentUtils.HostName; res.ResultCode = ResResultCode.Fail; res.Result = msg; return res; } return null; } private SMAgentDoCommandRes DoCancelShellCommand(SMAgentDoCommandReq req) { SMAgentDoCommandRes res = new SMAgentDoCommandRes(); res.Command = req.Command; res.SeqNum = req.SeqNum; res.ServerId = req.ServerId; res.HostName = SMAgentUtils.HostName; res.ResultCode = ResResultCode.Success; try { SMShell.CancelShell(); } catch (Exception ex) { res.ResultCode = ResResultCode.Exception; res.Result = ex.Message; } return res; } private SMAgentDoCommandRes DoPullFileCmd(SMAgentDoCommandReq req) { SMAgentDoCommandRes res = new SMAgentDoCommandRes(); res.Command = req.Command; res.SeqNum = req.SeqNum; res.ServerId = req.ServerId; res.HostName = SMAgentUtils.HostName; int ret = AgentFileTransMng.Instance.DoTransCmd(req); if (ret == 0) { res.ResultCode = ResResultCode.Success; } else { res.ResultCode = ResResultCode.Fail; res.Result = AgentFileTransMng.Instance.errorMsg; } return res; } } }