using System; using System.Linq; using System.IO; using System.Threading; using System.Collections.Generic; using Sog; using Sog.Log; using Sog.IO; using Google.Protobuf.WellKnownTypes; using Google.Protobuf; using System.Diagnostics; using System.Text; namespace SMConsole { /* */ public class ConsoleInfo { public int m_historyPos = -1; public List m_history = new List(); public int m_maxHistoryLen = 100; public string m_input = ""; public string INPUT_PRE_HEAD; public string m_historyfile; public ConsoleInfo(string inputHead,string historyFile) { INPUT_PRE_HEAD = inputHead; m_historyfile = historyFile; } } public class SMConsoleInput : Singleton { private bool firstHead = false; private bool exited = false; private bool IsConsoleModle = true; private Dictionary m_consoleInfo = new Dictionary(); private static string m_consoleInputHead = "smconsole >"; private static string m_consoleHistoryfile = "./inputHistory"; private static string m_shellInputHead = "shell $:"; private static string m_shellHistoryfile = "./inputShellHistory"; private ConsoleInfo m_ModleInfo => m_consoleInfo[IsConsoleModle]; private bool m_isWindows = OSUtils.IsWindows(); public SMConsoleInput() { ConsoleInfo info = new ConsoleInfo(m_consoleInputHead, m_consoleHistoryfile); m_consoleInfo.Add(true, info); info = new ConsoleInfo(m_shellInputHead, m_shellHistoryfile); m_consoleInfo.Add(false, info); InitHistoryFromFile(); } public void Update() { try { if (firstHead == false) { firstHead = true; WriteInputHeadInfo(); //System.Threading.Thread runTask = new System.Threading.Thread(this.TryUseThread); //runTask.Start(); } CheckReadyKey(); } catch(Exception ex) { TraceLog.Exception(ex); } } //读取历史记录文件 private void InitHistoryFromFile() { try { foreach (var info in m_consoleInfo.Values) { string[] allline = File.ReadAllLines(info.m_historyfile); if (allline.Length > 0) { info.m_history.AddRange(allline); } } } catch (Exception) { } } //保存历史记录 private void SaveHistoryToFile() { try { foreach (var info in m_consoleInfo.Values) { File.WriteAllLines(info.m_historyfile, info.m_history.ToArray()); } } catch (Exception) { } } private void WriteToHistTory(string cmd) { if(string.IsNullOrEmpty(cmd)) { return; } if (m_ModleInfo.m_history.Count > 0 && m_ModleInfo.m_history.Last() == cmd) { return; } m_ModleInfo.m_history.Add(cmd); if (m_ModleInfo.m_history.Count > m_ModleInfo.m_maxHistoryLen) { m_ModleInfo.m_history.RemoveAt(0); } SaveHistoryToFile(); } private void RewriteHistoryCmd(string cmd) { if (!string.IsNullOrEmpty(m_ModleInfo.m_input)) { int x = Console.CursorLeft - m_ModleInfo.m_input.Length; int y = Console.CursorTop; Console.SetCursorPosition(x, y); for (int i = 0; i < m_ModleInfo.m_input.Length; i++) { Console.Write(' '); } Console.SetCursorPosition(x, y); } m_ModleInfo.m_input = cmd; Console.Write(m_ModleInfo.m_input); } private void GoBackHistTory() { if(m_ModleInfo.m_history.Count == 0) { return; } if(m_ModleInfo.m_historyPos == -1) { m_ModleInfo.m_historyPos = 0; } else { m_ModleInfo.m_historyPos++; } if(m_ModleInfo.m_historyPos >= m_ModleInfo.m_history.Count) { m_ModleInfo.m_historyPos = m_ModleInfo.m_history.Count - 1; } string historyCmd = m_ModleInfo.m_history[m_ModleInfo.m_historyPos]; RewriteHistoryCmd(historyCmd); } private void GoFrontHistTory() { if (m_ModleInfo.m_history.Count == 0) { return; } if (m_ModleInfo.m_historyPos == -1) { m_ModleInfo.m_historyPos = m_ModleInfo.m_history.Count-1; } else { m_ModleInfo.m_historyPos--; } if(m_ModleInfo.m_historyPos < 0) { m_ModleInfo.m_historyPos = 0; } string historyCmd = m_ModleInfo.m_history[m_ModleInfo.m_historyPos]; RewriteHistoryCmd(historyCmd); } private void ClearSpecialCharNum(int charNum) { int x = Console.CursorLeft; int y = Console.CursorTop; Console.SetCursorPosition(x - charNum, y); for (int i=0; i 0 && x > m_ModleInfo.INPUT_PRE_HEAD.Length) { int y = Console.CursorTop; if( x == m_ModleInfo.INPUT_PRE_HEAD.Length + m_ModleInfo.m_input.Length) { Console.SetCursorPosition(x - 1, y); Console.Write(' '); Console.SetCursorPosition(x - 1, y); m_ModleInfo.m_input = m_ModleInfo.m_input.Substring(0, m_ModleInfo.m_input.Length - 1); }else { m_ModleInfo.m_input = m_ModleInfo.m_input.Remove(x - m_ModleInfo.INPUT_PRE_HEAD.Length - 1,1); WriteRemainChar(-1); } } } private void CheckReadyKey() { if (! Console.KeyAvailable) { return; } int oldX = Console.CursorLeft; ConsoleKeyInfo info; info = Console.ReadKey(true); int newX = Console.CursorLeft; //TraceLog.Trace("SMConsoleInput.CheckReadyKey input key {0} char {1} oldX {2} newX {3}", info.Key, info.KeyChar, oldX, newX); if (newX > oldX) { ClearSpecialCharNum(newX - oldX); } newX = Console.CursorLeft; //乱套了 if (newX > m_ModleInfo.m_input.Length + m_ModleInfo.INPUT_PRE_HEAD.Length) { TraceLog.Trace("SMConsoleInput.CheckReadyKey invlaid input key {0} char {1} newX {2} m_input.Length {3} INPUT_PRE_HEAD len {4}" , info.Key, info.KeyChar, newX, m_ModleInfo.m_input.Length, m_ModleInfo.INPUT_PRE_HEAD.Length); int y = Console.CursorTop; Console.SetCursorPosition(0, y); for(int i=0; i< newX + 10; i++) { Console.Write(' '); } Console.SetCursorPosition(0, y); WriteInputHeadInfo(); Console.Write(m_ModleInfo.m_input); } if (info.Key == ConsoleKey.Enter) { m_ModleInfo.m_historyPos = -1; ProcessSMCommand(m_ModleInfo.m_input); if (exited == false) { WriteToHistTory(m_ModleInfo.m_input); m_ModleInfo.m_input = ""; Console.WriteLine(); WriteInputHeadInfo(); } } else if(info.Key == ConsoleKey.Tab) { m_ModleInfo.m_input = m_ModleInfo.m_input + " #tab"; ProcessSMCommand(m_ModleInfo.m_input); } //处理退格 else if (info.Key == ConsoleKey.Backspace) { DoOnBackspace(); } else if (info.Key == ConsoleKey.Delete) { DoOnBackspace(); } //Windows else if((info.Key == ConsoleKey.UpArrow || info.KeyChar == '[') && m_isWindows) { GoFrontHistTory(); } else if ((info.Key == ConsoleKey.DownArrow || info.KeyChar == ']')) { GoBackHistTory(); }else if(info.Key == ConsoleKey.LeftArrow && m_isWindows) { DoOnMoveCursorPosition(-1); } else if (info.Key == ConsoleKey.RightArrow && m_isWindows) { DoOnMoveCursorPosition(1); } //Linux else if (!m_isWindows && info.KeyChar == 79 && Console.KeyAvailable) { ConsoleKeyInfo exinfo = Console.ReadKey(true); if (exinfo.KeyChar == 65) { GoFrontHistTory(); } else if (exinfo.KeyChar == 66) { GoBackHistTory(); } else if (exinfo.KeyChar == 68) { DoOnMoveCursorPosition(-1); } else if (exinfo.KeyChar == 67) { DoOnMoveCursorPosition(1); } } else if (IsValidInputChar(info.KeyChar)) { //TraceLog.Trace("CheckReadyKey valid input key {0} char {1} oldX {2} newX {3}", info.Key, info.KeyChar, oldX , newX); //回显 int NowX = Console.CursorLeft; if (NowX == m_ModleInfo.INPUT_PRE_HEAD.Length + m_ModleInfo.m_input.Length) { Console.Write(info.KeyChar); m_ModleInfo.m_input += info.KeyChar; } else { m_ModleInfo.m_input = m_ModleInfo.m_input.Insert(NowX - m_ModleInfo.INPUT_PRE_HEAD.Length, info.KeyChar.ToString()); WriteRemainChar(1); } } } private void WriteRemainChar(int addPosition) { int NowX = Console.CursorLeft; if(addPosition < 0 ) { Console.SetCursorPosition(NowX + addPosition, Console.CursorTop); NowX = Console.CursorLeft; } for(int i = NowX - m_ModleInfo.INPUT_PRE_HEAD.Length; i < m_ModleInfo.m_input.Length;i++) { Console.Write(m_ModleInfo.m_input[i]); } if(addPosition < 0 ) { Console.Write(' '); } if (addPosition > 0) { Console.SetCursorPosition(NowX + addPosition, Console.CursorTop); }else { Console.SetCursorPosition(NowX, Console.CursorTop); } } private void DoOnMoveCursorPosition(int symbol) { int NowX = Console.CursorLeft; int NowY = Console.CursorTop; if(symbol < 0 && NowX > m_ModleInfo.INPUT_PRE_HEAD.Length) { Console.SetCursorPosition(Console.CursorLeft + symbol, Console.CursorTop); }else if(symbol > 0 && NowX < m_ModleInfo.m_input.Length + m_ModleInfo.INPUT_PRE_HEAD.Length) { Console.SetCursorPosition(Console.CursorLeft + symbol, Console.CursorTop); } } private void WriteInputHeadInfo() { if(IsConsoleModle) { Console.ForegroundColor = ConsoleColor.White; }else { Console.ForegroundColor = ConsoleColor.DarkGreen; } Console.Write(m_ModleInfo.INPUT_PRE_HEAD); } private bool IsValidInputChar(char key) { if(key >= 'a' && key <= 'z') { return true; } if (key >= 'A' && key <= 'Z') { return true; } if (key >= '0' && key <= '9') { return true; } //服务器id *.*.* //gm指令-t -b //base64编码 //win目录 //文件名,中文文件名这里不支持 //shell if (key == '*' || key == '.' || key == ' ' || key == '-' || key == '+' || key == '/' || key == '=' || key == ':' || key == '\\' || key == '_' || key == '>' || key == '<' || key == '|' || key == '$' || key == '@' || key == '!' || key == '&' || key == '(' || key == ')' || key == ';' || key == ',' || key == '~' ) { return true; } return false; } private void ProcessSMCommand(string command) { if (string.IsNullOrEmpty(command)) { return; } if ((command.ToLower() == "exit" || command.ToLower() == "quit" || command.ToLower() == "q") && IsConsoleModle) { SMConsoleUtils.GetApp().StopServer(); SMConsoleUtils.GetApp().SetStopSuccess(); exited = true; Thread.Sleep(10); Console.WriteLine(); return; } if(command.ToLower() == "history") { for (int i = 0; i < m_ModleInfo.m_history.Count; i ++) { string w = string.Format("{0,-50}", m_ModleInfo.m_history[i]); if(i % 3 == 0) { Console.WriteLine(); } Console.Write(w); } //ProcessSMShellCommand(command.ToLower()); return; } if(command.ToLower() == "clear") { Console.Clear(); //清屏专用 return; } SMConsoleCommandReq req = new SMConsoleCommandReq(); req.Command = command; SMConsoleNet.Instance.SendMsgToCenter(req, SMMsgID.ConsoleCommandReq); } private void ProcessSMShellCommand(string cmd) { Process process; if (OSUtils.IsWindows()) { process = new Process { StartInfo = new ProcessStartInfo { RedirectStandardOutput = true, RedirectStandardInput = true, RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true, FileName = "cmd.exe", Arguments = $"/C \"{cmd}\"" } }; } else { process = new Process { StartInfo = new ProcessStartInfo { RedirectStandardOutput = true, RedirectStandardInput = true, RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true, FileName = "/bin/bash", Arguments = $"-c \"{cmd}\"" } }; } process.EnableRaisingEvents = true; process.Start(); //process.StandardInput.WriteLine("exit"); string errorStr = process.StandardError.ReadToEnd(); if (string.IsNullOrEmpty(errorStr) == false) { Console.Write("Run Commond:{" + process.StartInfo.Arguments + "} return error =>"); Console.Write(errorStr); } string contentStr = process.StandardOutput.ReadToEnd(); if (string.IsNullOrEmpty(contentStr) == false) { Console.Write(contentStr); } Console.WriteLine(); process.WaitForExit(); process.Close(); } private void AckExTabCommand(SMConsoleCommandRes res) { if (string.IsNullOrEmpty(res.Message) == false) { string[] messageSplit = res.Message.Split(' '); if (messageSplit.Length > 2) { m_ModleInfo.m_input = m_ModleInfo.m_input.Split(" #")[0]; Console.WriteLine(); Console.WriteLine("{0}", res.Message); WriteInputHeadInfo(); if (string.IsNullOrEmpty(m_ModleInfo.m_input) == false) { Console.Write(m_ModleInfo.m_input); } } else { Console.SetCursorPosition(0, Console.CursorTop); WriteInputHeadInfo(); m_ModleInfo.m_input = messageSplit[0]; Console.Write(m_ModleInfo.m_input); } } else { m_ModleInfo.m_input = m_ModleInfo.m_input.Split(" #")[0]; Console.SetCursorPosition(0, Console.CursorTop); WriteInputHeadInfo(); Console.Write(m_ModleInfo.m_input); } } public void AckCommand(SMConsoleCommandRes res) { Console.ForegroundColor = ConsoleColor.Green; if (m_ModleInfo.m_input.Contains(" #tab")) { AckExTabCommand(res); return; } if(res.Command.Contains("enter_agent_shell")) { string[] message = res.Message.Split("#"); if (message.Length == 2) { if(message[1] == "success") { Console.SetCursorPosition(0, Console.CursorTop); IsConsoleModle = false; Console.WriteLine("{0} Success! ", res.Command); WriteInputHeadInfo(); } return; } } if(res.Message == "#exit") { Console.SetCursorPosition(0, Console.CursorTop); IsConsoleModle = true; Console.WriteLine("exit agent shell Success! "); WriteInputHeadInfo(); return; } //接收消息显示 int length = Console.CursorLeft + 1; Console.SetCursorPosition(0, Console.CursorTop); Console.Write(new char[length]); Console.SetCursorPosition(0, Console.CursorTop); if (res.Nonewline == 0) { Console.ForegroundColor = ConsoleColor.DarkRed; Console.WriteLine(" ACK:{0}",res.Command); Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine("{0}", res.Message); } else if(res.Nonewline == 1) { Console.WriteLine("{0}", res.Message); } WriteInputHeadInfo(); //之前输入一半的补回去 if (m_ModleInfo.m_input.Length > 0) { Console.Write(m_ModleInfo.m_input); } } } }