using franz; using Newtonsoft.Json; using silver_messages.directorial; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Net; using System.Net.Http; using System.Text; using System.Threading; using System.Threading.Tasks; namespace directors_assistant { class Program { public static Config conf; public static Telefranz telefranz; static async Task Main(string[] args) { conf = JsonConvert.DeserializeObject(File.ReadAllText("appsettings.json")); telefranz = new Telefranz(conf.name, conf.bootstrap_servers, conf.commands.Select(c => c.name).ToList(), conf.checks.Select(c => c.name).ToList()); telefranz.addHandler((silver_messages.directorial.execute_command ec) => { var matchedCommands = conf.commands.Where(c => c.name == ec.command)?.ToList(); //I swear I have a vague memory of foreach in linq'd query throwing an exception when results empty. if (matchedCommands != null && matchedCommands.Count > 0) { foreach (var cmd in matchedCommands) { Console.WriteLine($"executing {cmd.name}{(ec.args != null ? ", given args " + string.Join(' ', ec.args) : "")} with timeout {ec.timeout}"); var cmdAndArgs = cmd.shell.Split(' '); var justArgs = cmdAndArgs.Skip(1).ToList(); justArgs.AddRange(ec.args); var commandPath = cmdAndArgs[0]; var commandArguments = String.Join(' ', justArgs); if (ec.timeout > 0) { executeTimed(cmd.name, commandPath, commandArguments, ec.timeout); } else { Task.Run(() => { executeUntimed(cmd.name, commandPath, commandArguments); }); } } } }); telefranz.addHandler((silver_messages.directorial.execute_check ec) => { //TODO }); await Task.Delay(1000); telefranz.ProduceMessage(new silver_messages.global.sound_off()); await Task.Delay(-1); } private static void executeTimed(string commandName, string commandPath, string commandArguments, int timeout) { var process = readableProcess(commandPath, commandArguments); var outputs = new List(); var errors = new List(); process.OutputDataReceived += new DataReceivedEventHandler((s, e) => { outputs.Add(e.Data); }); process.ErrorDataReceived += new DataReceivedEventHandler((s, e) => { errors.Add(e.Data); }); var stopwatch = new Stopwatch(); stopwatch.Start(); process.Start(); process.BeginErrorReadLine(); process.BeginOutputReadLine(); Task.WaitAny( Task.Run(() => { process.WaitForExit(); }), Task.Delay(new TimeSpan(0, 0, timeout)) ); if (process.HasExited) { stopwatch.Stop(); Console.WriteLine($"{commandName} completed"); telefranz.ProduceMessage(new silver_messages.directorial.command_completed() { command = commandName, runtimeMilliseconds = stopwatch.ElapsedMilliseconds, exit_code = process.ExitCode, stdout = string.Join('\n', outputs), stderr = string.Join('\n', errors) }); } else { process.Kill(); Console.WriteLine($"{commandName} expired"); telefranz.ProduceMessage(new silver_messages.directorial.command_expired() { command = commandName, stdout = string.Join('\n', outputs), stderr = string.Join('\n', errors) }); } } private static void executeUntimed(string commandName, string commandPath, string commandArguments) { var stopwatch = new Stopwatch(); var process = readableProcess(commandPath, commandArguments); process.OutputDataReceived += new DataReceivedEventHandler((s, e) => { Console.WriteLine($"{commandName} output: {e.Data}"); if(string.IsNullOrWhiteSpace(e.Data)) { return; } telefranz.ProduceMessage(new silver_messages.directorial.command_output() { stdout = e.Data, command = commandName, runtime = (uint)stopwatch.ElapsedMilliseconds }); }); process.ErrorDataReceived += new DataReceivedEventHandler((s, e) => { if(string.IsNullOrWhiteSpace(e.Data)) { return; } Console.Error.WriteLine($"{commandName} err (but not necessarily dead?): {e.Data}"); telefranz.ProduceMessage(new silver_messages.directorial.command_error() { stderr = e.Data, command = commandName, runtime = (uint)stopwatch.ElapsedMilliseconds }); }); stopwatch.Start(); process.Start(); process.BeginErrorReadLine(); process.BeginOutputReadLine(); process.WaitForExit(); stopwatch.Stop(); Console.WriteLine($"{commandName} returned {process.ExitCode} after {stopwatch.ElapsedMilliseconds}ms"); telefranz.ProduceMessage(new silver_messages.directorial.command_completed() { command = commandName, runtimeMilliseconds = stopwatch.ElapsedMilliseconds, exit_code = process.ExitCode }); } private static Process readableProcess(string commandPath, string commandArguments) { var pi = new ProcessStartInfo(commandPath, commandArguments); pi.UseShellExecute = false; pi.CreateNoWindow = true; pi.RedirectStandardError = true; pi.RedirectStandardOutput = true; var process = new Process(); process.StartInfo = pi; return process; } } }