using System; using System.Collections.Generic; using System.IO; using MxGateway.Worker.Bootstrap; using MxGateway.Worker.Ipc; namespace MxGateway.Worker; /// Entry point for the worker process. public static class WorkerApplication { /// Initializes and runs the worker with default environment and logging. /// Command-line arguments. public static int Run(string[] args) { return Run( args, new EnvironmentVariableWorkerEnvironment(), new WorkerConsoleLogger(Console.Error)); } /// Initializes and runs the worker with custom environment and logging. /// Command-line arguments. /// Worker environment for resolving configuration. /// Worker logger for diagnostics. public static int Run( string[] args, IWorkerEnvironment environment, IWorkerLogger logger) { return Run( args, environment, logger, new WorkerPipeClient(logger)); } /// Parses arguments, bootstraps the handshake, and runs the worker until shutdown. /// Command-line arguments. /// Worker environment for resolving configuration. /// Worker logger for diagnostics. /// Named pipe client for gateway communication. public static int Run( string[] args, IWorkerEnvironment environment, IWorkerLogger logger, IWorkerPipeClient pipeClient) { if (args is null) { throw new ArgumentNullException(nameof(args)); } if (environment is null) { throw new ArgumentNullException(nameof(environment)); } if (logger is null) { throw new ArgumentNullException(nameof(logger)); } if (pipeClient is null) { throw new ArgumentNullException(nameof(pipeClient)); } try { WorkerOptionsParser parser = new(environment); WorkerBootstrapResult result = parser.Parse(args); if (!result.Succeeded) { logger.Error("WorkerBootstrapFailed", new Dictionary { ["exit_code"] = result.ExitCode, ["errors"] = string.Join(";", result.Errors), }); return (int)result.ExitCode; } WorkerOptions options = result.Options ?? throw new InvalidOperationException("Successful bootstrap result did not include worker options."); logger.Information("WorkerBootstrapSucceeded", new Dictionary { ["session_id"] = options.SessionId, ["pipe_name"] = options.PipeName, ["protocol_version"] = options.ProtocolVersion, ["nonce"] = options.Nonce, }); pipeClient.RunAsync(options).GetAwaiter().GetResult(); logger.Information("WorkerPipeSessionCompleted", new Dictionary { ["session_id"] = options.SessionId, ["pipe_name"] = options.PipeName, ["protocol_version"] = options.ProtocolVersion, }); return (int)WorkerExitCode.Success; } catch (WorkerFrameProtocolException exception) { logger.Error("WorkerPipeProtocolFailure", new Dictionary { ["exit_code"] = WorkerExitCode.ProtocolViolation, ["error_code"] = exception.ErrorCode, ["exception_type"] = exception.GetType().FullName, }); return (int)WorkerExitCode.ProtocolViolation; } catch (Exception exception) when (exception is IOException or TimeoutException) { logger.Error("WorkerPipeConnectionFailed", new Dictionary { ["exit_code"] = WorkerExitCode.PipeConnectionFailed, ["exception_type"] = exception.GetType().FullName, }); return (int)WorkerExitCode.PipeConnectionFailed; } catch (Exception exception) { logger.Error("WorkerBootstrapUnexpectedFailure", new Dictionary { ["exit_code"] = WorkerExitCode.UnexpectedFailure, ["exception_type"] = exception.GetType().FullName, }); return (int)WorkerExitCode.UnexpectedFailure; } } }