You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

439 lines
14 KiB

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.Cryptography;
using System.Threading.Tasks;
using Sog;
using Google.Protobuf.WellKnownTypes;
namespace SMCenter
{
// 通用命令处理
public class CommCmdProc : BaseCmdProc
{
private long LastCheckTimeoutSecond;
public override void ClearData()
{
m_nowCmd = null;
LastCheckTimeoutSecond = 0;
}
// cmdParams[0] is serverId
public override int DoCmd(out string msg)
{
string lowerCMD = m_nowCmd.CMD;
if (m_nowCmd.CmdParams.Count < 1 || string.IsNullOrEmpty(m_nowCmd.CmdParams[0]))
{
msg = "invalid cmd param";
return -1;
}
if(lowerCMD == "list")
{
SMProcAppMgr.Instance.GetAllMatchingProc("*.*.*", m_nowCmd.m_procs);
if(m_nowCmd.CmdParams[0] != "*")
{
m_nowCmd.m_procs = m_nowCmd.m_procs.Where(p => p.Value.SMApp.HostName == m_nowCmd.CmdParams[0]).ToDictionary(p => p.Key, p => p.Value);
m_nowCmd.m_procs = m_nowCmd.m_procs.OrderBy(o => o.Value.SMApp.HostName).ToDictionary(p => p.Key, o => o.Value);
}
if(m_nowCmd.m_procs.Count == 0)
{
msg = "invalid cmd param or HostName is not exist";
return -1;
}
}
else
{
if (CmdUtils.CheckValidServerId(m_nowCmd.CmdParams[0], out string fixSvrId) == false)
{
msg = "invalid server id";
return -1;
}
if (fixSvrId != null)
{
m_nowCmd.CmdParams[0] = fixSvrId;
}
SMProcAppMgr.Instance.GetAllMatchingProc(m_nowCmd.CmdParams[0], m_nowCmd.m_procs);
}
TraceLog.Debug("CommCmdProc.DoCmd {0} match proc num {1}", m_nowCmd.CMD, m_nowCmd.m_procs.Count);
//需要排序的命令
if (lowerCMD == "start")
{
//根据sequence排序
m_nowCmd.m_procs = m_nowCmd.m_procs.OrderBy(p => p.Value.SMApp.HostName).ThenBy(o => o.Value.SMApp.Sequence).ToDictionary(p => p.Key, o => o.Value);
}
else if (lowerCMD == "stop")
{
//根据sequence排序
m_nowCmd.m_procs = m_nowCmd.m_procs.OrderBy(p => p.Value.SMApp.HostName).ThenByDescending(o => o.Value.SMApp.Sequence).ToDictionary(p => p.Key, o => o.Value);
}
if(m_nowCmd.m_procs.Count == 0)
{
msg ="not exist ServerID: " + m_nowCmd.CmdParams[0];
return -1;
}
var regHost = SMCenterNet.Instance.GetAllAgent();
foreach (var procInfo in m_nowCmd.m_procs)
{
if(regHost.Find( p => p.HostName == procInfo.Value.SMApp.HostName) == null)
{
procInfo.Value.SendCmdToHostTime = 1;
procInfo.Value.AckResultCode = ResResultCode.Fail;
procInfo.Value.AckMessage = procInfo.Value.SMApp.HostName + " Agent Not Running";
procInfo.Value.AckAddInfo = "0 KB";
}
}
if (IsAllProcCmdAcked())
{
AckConsoleCmdResult();
}
msg = m_nowCmd.ConsoleInput + " begin ...";
TraceLog.Trace("CommCmdProc.DoCmd {0}", msg);
return 0;
}
public override int UpdateCmd(long tMs)
{
if (m_nowCmd.m_procs.Count == 0)
{
return 0;
}
if (IsAllProcCmdAcked())
{
return 0;
}
CheckProcTimeout(tMs);
if (m_nowCmd.CMD =="start" || m_nowCmd.CMD == "stop")
{
UpdateSequenceCmd(tMs);
}
else
{
//send one cmd per tick
foreach (var cmdInfo in m_nowCmd.m_procs.Values)
{
if (cmdInfo.SendCmdToHostTime == 0)
{
SendCmdToHost(cmdInfo);
cmdInfo.SendCmdToHostTime = tMs / 1000;
break;
}
}
}
return 1;
}
private void CheckProcTimeout(long tMs)
{
long nowSecond = tMs / 1000;
if(nowSecond - LastCheckTimeoutSecond < 2)
{
return;
}
LastCheckTimeoutSecond = nowSecond;
foreach (var cmdInfo in m_nowCmd.m_procs.Values)
{
if (cmdInfo.SendCmdToHostTime > 0 && cmdInfo.AckResultCode == ResResultCode.NoRes)
{
int timeoutSecond = 15;
if(m_nowCmd.CMD == "stop")
{
timeoutSecond = 60;
}
if (nowSecond - cmdInfo.SendCmdToHostTime > timeoutSecond)
{
TraceLog.Error("CommCmdProc.CheckProcTimeout cmd {0} serverid {1} timeout"
, m_nowCmd.CMD, cmdInfo.SMApp.ServerIDStr);
cmdInfo.AckResultCode = ResResultCode.TimeOut;
if (IsAllProcCmdAcked())
{
AckConsoleCmdResult();
}
}
}
}
}
private int UpdateSequenceCmd(long tMs)
{
//根据Sequence一次发送一批命令
int thisSendSequence = -1;
bool needExit = false;
CmdProcInfo errorCmdProc = null;
foreach (CmdProcInfo cmdProc in m_nowCmd.m_procs.Values)
{
// 停服时, 如果是*.*.*这种形式的输入, 会跳过某些appType, 比如Version和GateVersion
if (m_nowCmd.CMD == "stop")
{
string appType = m_nowCmd.CmdParams[0].Split('.')[1];
if (appType == "*" && SMCenterUtils.Config.excludeAppType.Contains(cmdProc.SMApp.AppType))
{
cmdProc.SendCmdToHostTime = tMs / 1000;
cmdProc.AckResultCode = ResResultCode.Success;
cmdProc.AckMessage = " Cannot stop *.*.*, Run [stop *.app.*] or Run [list *] check server state";
continue;
}
}
//前面优先级的进程命令未成功完成,直接退出循环,超时的情况也不能继续往下走
if (cmdProc.SendCmdToHostTime > 0 && cmdProc.AckResultCode != ResResultCode.Success)
{
if (cmdProc.AckResultCode == ResResultCode.Exception
|| cmdProc.AckResultCode == ResResultCode.Fail
|| cmdProc.AckResultCode == ResResultCode.TimeOut)
{
errorCmdProc = cmdProc;
needExit = true;
}
break;
}
//发现没有发送CMD的进程,取出Sequence记录,相同Sequence的进程可以一起发送命令
if (cmdProc.SendCmdToHostTime == 0)
{
if(thisSendSequence == -1)
{
thisSendSequence = cmdProc.SMApp.Sequence;
}
else if (thisSendSequence != cmdProc.SMApp.Sequence)
{
break;
}
SendCmdToHost(cmdProc);
cmdProc.SendCmdToHostTime = tMs / 1000;
}
}
if(needExit && errorCmdProc != null)
{
// 退出时把本次执行cmd的所有proc.result都设置成出错的proc的错误码
foreach (CmdProcInfo cmdProc in m_nowCmd.m_procs.Values)
{
cmdProc.AckResultCode = errorCmdProc.AckResultCode;
}
AckConsoleCmdResult();
return -1;
}
return 1;
}
public override void OnAgentDoCommandRes(ClientInfo client, SMAgentDoCommandRes res)
{
if (m_nowCmd.SeqNum != res.SeqNum)
{
TraceLog.Error("CommCmdProc.OnAgentDoCommandRes CMD {0} serverId {1}, seqNum not same, skip, m_nowCmd.SeqNum {2}"
, res.Command, res.ServerId, m_nowCmd.SeqNum);
return;
}
if(m_nowCmd.m_procs.ContainsKey(res.ServerId) == false)
{
TraceLog.Error("CommCmdProc.OnAgentDoCommandRes CMD {0} serverId {1} not in m_procs, skip"
, res.Command, res.ServerId);
return;
}
CmdProcInfo proc = m_nowCmd.m_procs[res.ServerId];
proc.AckResultCode = res.ResultCode;
proc.AckMessage = res.Result;
proc.AckAddInfo = res.AddInfo;
TraceLog.Trace("+++proc+++ " + "\t\t\t" + proc.SMApp.Name+ "\t\t\t\t" + proc.SMApp.ServerIDStr + "\t\t" + res.Command + "\t\t" + res.Result + "\t\t" + res.AddInfo);
if(IsAllProcCmdAcked())
{
AckConsoleCmdResult();
}
TraceLog.Trace("CommCmdProc.OnAgentDoCommandRes CMD {0} serverId {1}, result {2}"
, res.Command, res.ServerId, res.Result);
}
private bool IsAllProcCmdAcked()
{
foreach(var proc in m_nowCmd.m_procs)
{
if(proc.Value.AckResultCode == ResResultCode.NoRes)
{
return false;
}
}
return true;
}
private void AckConsoleCmdResult()
{
SMConsoleCommandRes res = new SMConsoleCommandRes();
res.Command = m_nowCmd.ConsoleInput;
res.Message = "";
List<string> hostName = new List<string>();
foreach (var procInfo in m_nowCmd.m_procs)
{
string procResult = "";
if (! hostName.Contains(procInfo.Value.SMApp.HostName))
{
procResult += string.Format("\n--------------- HostName:{0} ---------------", procInfo.Value.SMApp.HostName);
hostName.Add(procInfo.Value.SMApp.HostName);
}
procResult += "\n " + procInfo.Value.SMApp.ServerIDStr + "\t";
switch(procInfo.Value.AckResultCode)
{
case ResResultCode.NoRes:
case ResResultCode.TimeOut:
procResult += "Timeout";
break;
case ResResultCode.Success:
procResult += "Success";
break;
case ResResultCode.Fail:
procResult += "Fail";
break;
case ResResultCode.Exception:
procResult += "Exception";
break;
case ResResultCode.Running:
procResult += "Running";
break;
case ResResultCode.NotRunning:
procResult += "Not Running";
break;
default:
break;
}
//不会空的情况下定义为错误信息
if(string.IsNullOrEmpty(procInfo.Value.AckMessage) == false)
{
procResult += " (INFO:" + procInfo.Value.AckMessage + ")";
}
//这个携带一些其他数据信息(目前只有内存消息)
if (string.IsNullOrEmpty(procInfo.Value.AckAddInfo))
{
procInfo.Value.AckAddInfo = "0 KB";
}
procResult += string.Format(" \tMemoryUsage:{0,-30}", procInfo.Value.AckAddInfo);
procResult += string.Format("\t\"{0}@{1}\"",procInfo.Value.SMApp.Name, procInfo.Value.SMApp.HostName);
procResult += string.Format("\t{0}", procInfo.Value.SMApp.CfgPath);
procResult += string.Format("\t{0}", procInfo.Value.SMApp.WorkPath);
res.Message += procResult;
}
SMCenterNet.Instance.SendMsg(m_nowCmd.SessionId, SMMsgID.ConsoleCommandRes, res, m_nowCmd.HttpId);
}
private void SendCmdToHost(CmdProcInfo cmdInfo)
{
string hostname = cmdInfo.SMApp.HostName;
ClientInfo client = SMCenterNet.Instance.GetClientInfoByName(hostname);
if(client == null)
{
TraceLog.Error("CommCmdProc.SendCmdToHost host {0} agent not register!", hostname);
return;
}
SMAgentDoCommandReq req = new SMAgentDoCommandReq();
req.Command = m_nowCmd.CMD;
req.SeqNum = m_nowCmd.SeqNum;
req.ServerId = cmdInfo.SMApp.AppId;
req.WorkPath = cmdInfo.SMApp.WorkPath;
req.StopTimeout = cmdInfo.SMApp.StopTimeout;
req.ExeFileName = cmdInfo.SMApp.ExeFileName;
//构造命令行参数
req.CmdArgs = GetCmdArgs(cmdInfo, m_nowCmd);
client.SendMsg(req, SMMsgID.AgentDoCommandReq);
}
public string GetExeFileName()
{
string exename = "SogLoader";
if (OSUtils.IsWindows())
{
exename += ".exe";
}
return exename;
}
private string GetCmdArgs(CmdProcInfo cmdProcInfo, CmdInfo cmdInfo)
{
//统一是这个格式,有些命令agent会特殊处理,比如check
//--name=Game这个参数纯粹是为了肉眼好看,只是起到注释作用,ps看进程的时候进程太多,只看id眼花
string args = string.Format("--id={0} --cluster={1}/cluster.json --name={2} {3}"
, cmdProcInfo.SMApp.ServerIDStr,cmdProcInfo.SMApp.CfgPath, cmdProcInfo.SMApp.Name, cmdInfo.CMD);
//gm指令的话需要带上参数
//这里的gm指令只能输入一些简单字符,比如中文没法输入,代码不好写,算了,复杂的gm指令通过经营分析系统去做,SMS就不管了
//gmcmd 1.200.1 AddChip 50000 -t10001
//类似这种简单的指令没有问题
//包含特殊字符,或者中文,其他非字符的gm指令非得用SMS输入也不是不可以
//gm指令本身支持base64转码
//gmcmd 1.200.1 AddChip 500000000 -t 1000001
//用base64编码替换AddChip后面的参数
//gmcmd 1.200.1 AddChip -bNTAwMDAwMDAwIC10IDEwMDAwMDE=
//gmcmd 1.200.1 AddChip -b NTAwMDAwMDAwIC10IDEwMDAwMDE=
//以上两种都可以
if (cmdInfo.CMD == "gmcmd" && cmdInfo.CmdParams.Count > 1)
{
//第0个是服务器id
for(int i= 1; i < cmdInfo.CmdParams.Count; i++)
{
args += cmdInfo.CmdParams[i];
if(i != cmdInfo.CmdParams.Count -1)
{
args += " ";
}
}
}
return args;
}
}
}