Ejemplo n.º 1
0
        /// <summary>
        /// Attach the command to all available stream readers.
        /// </summary>
        /// <param name="logger"></param>
        /// <param name="commandHolder"></param>
        public static void LatchQuickly(ILogger <ProcessRunner> logger, CommandHolder commandHolder)
        {
            // Start the standard stream reader.
            new Thread(() => { Standard(logger, commandHolder); }).Start();

            // Start the error stream reader.
            new Thread(() => { Error(logger, commandHolder); }).Start();
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Attach the command to the error stream reader.
        /// </summary>
        /// <param name="logger"></param>
        /// <param name="commandHolder"></param>
        private static async void Error(ILogger <ProcessRunner> logger, CommandHolder commandHolder)
        {
            string line;

            while ((line = await commandHolder.Command.StandardError.ReadLineAsync().ConfigureAwait(false)) != null)
            {
                logger.LogError(line);
            }
        }
Ejemplo n.º 3
0
 /// <summary>
 /// Stop tracking a process.
 /// </summary>
 /// <param name="commandHolder"></param>
 /// <returns></returns>
 public bool Untrack(CommandHolder commandHolder)
 {
     // Check to see if the process is already being tracked.
     if (_runningCommands.Contains(commandHolder))
     {
         // The process is being tracked, remove it.
         _runningCommands.Remove(commandHolder);
         return(true);
     }
     else
     {
         // The process is not being tracked.
         return(false);
     }
 }
Ejemplo n.º 4
0
        /// <summary>
        /// Function to attach a logging instance to the commandHolder using the classes _logger.
        /// </summary>
        private void AttachLoggerToCommandHolder(CommandHolder commandHolder, string parent)
        {
            _logger.LogDebug(
                "{0} Object is attaching logger to command: {1}",
                parent,
                commandHolder.Options.Executable
                );

            var streamProcessor = GetStreamProcessor(commandHolder);

            // Attach command objects
            streamProcessor.StandardOutputProcessor = CommandLogger.StandardAction();
            streamProcessor.ErrorOutputProcessor    = CommandLogger.ErrorAction();

            // Actually begin logging.
            streamProcessor.StandardOutputProcessor(_logger, commandHolder);
            streamProcessor.ErrorOutputProcessor(_logger, commandHolder);
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Start a new command holder based on the provided process options and caller.
        /// </summary>
        /// <param name="processOptions"></param>
        /// <param name="caller"></param>
        /// <returns></returns>
        public CommandHolder Run(ProcessOptions processOptions, IService caller = null)
        {
            // Convert the options into a new command holder.
            CommandHolder commandHolder = null;

            // Check the state of the service.
            var currentState  = caller?.GetState();
            var allowedStates = new[] { ServiceState.Running, ServiceState.Restarting };

            if (currentState != null && !allowedStates.Any(x => x == currentState))
            {
                _logger.LogInformation("The service state prohibited a proccess from running.");
                throw new InvalidOperationException($"Service state was {currentState}, invocation can NOT continue.");
            }

            // Validate that the provided directory exists.
            if (processOptions.WorkingDirectory != null)
            {
                if (!Directory.Exists(processOptions.WorkingDirectory))
                {
                    throw WorkingDirectoryDoesntExistException(processOptions.WorkingDirectory);
                }
            }


            // Check if we should run as root/admin.
            if (!processOptions.InvokeAsSuperuser)
            {
                // Nope - platform independent specification, go wild.
                commandHolder = new CommandHolder
                {
                    Options = processOptions,
                    Caller  = caller,
                    Command = new Shell(
                        e => e.ThrowOnError(processOptions.ThrowOnError)
                        ).Run(
                        executable: processOptions.Executable,
                        arguments: processOptions.Arguments,
                        options: o => o
                        .DisposeOnExit(processOptions.DisposeOnExit)
                        .WorkingDirectory(processOptions.WorkingDirectory)
                        )
                };
            }
            else
            {
                // Yes, check the operating system to know what we have to do.
                if (AppConfig.isWindows)
                {
                    // WINDOWS =====
                    // runas verb, build the command holder with the runas verb.
                    commandHolder = new CommandHolder
                    {
                        Options = processOptions,
                        Caller  = caller,
                        Command = new Shell(
                            e => e.ThrowOnError(processOptions.ThrowOnError)
                            ).Run(
                            executable: processOptions.Executable,
                            arguments: processOptions.Arguments,
                            options: o => o
                            .StartInfo(s => s
                                       // The runas attribute will run as administrator.
                                       .Verb = "runas"
                                       )
                            .DisposeOnExit(processOptions.DisposeOnExit)
                            .WorkingDirectory(processOptions.WorkingDirectory)
                            )
                    };
                }
                else if (AppConfig.isUnix)
                {
                    // LINUX/MACOS =====

                    // Build the argument array.
                    var arguments = new List <string>();
                    arguments.Add(processOptions.Executable);
                    for (var i = 0; i != processOptions.Arguments.Length; i++)
                    {
                        arguments.Add(processOptions.Arguments[i] ?? "");
                    }
                    var procArgStr = string.Join(" ", arguments);

                    // Write to the console.
                    _logger.LogDebug("Built linux specific superuser argument array: " + procArgStr);

                    // Build the command holder with a sudo as the executable.
                    commandHolder = new CommandHolder
                    {
                        Options = processOptions,
                        Caller  = caller,
                        Command = new Shell(
                            e => e.ThrowOnError(processOptions.ThrowOnError)
                            ).Run(
                            executable: GetSudoPath(),
                            arguments: arguments,
                            options: o => o
                            .DisposeOnExit(processOptions.DisposeOnExit)
                            .WorkingDirectory(processOptions.WorkingDirectory)
                            )
                    };
                }
            }

            // Attach Loggers if needed.
            if (processOptions.AttachLogToConsole)
            {
                AttachLoggerToCommandHolder(commandHolder, "Start");
            }

            // Check if we should monitor.
            if (commandHolder.Options.Monitor && caller != null)
            {
                var monitoringThread = new Thread(() => Monitor(commandHolder, caller));
                commandHolder.MonitoringThread = monitoringThread;
                monitoringThread.Start();
            }

            // Keep track of the object.
            Track(commandHolder);

            // Return
            return(commandHolder);
        }
Ejemplo n.º 6
0
 /// <summary>
 /// Reassign the command holder's command with the provided.
 /// </summary>
 /// <param name="commandHolder"></param>
 /// <param name="command"></param>
 /// <returns></returns>
 public CommandHolder ReassignCommand(CommandHolder commandHolder, Command command)
 {
     commandHolder.Command = command;
     return(commandHolder);
 }
Ejemplo n.º 7
0
 /// <summary>
 /// Gets the stream processor for the command holder
 /// </summary>
 /// <param name="commandHolder"></param>
 /// <returns></returns>
 public StreamProcessor GetStreamProcessor(CommandHolder commandHolder) => commandHolder.Options.streamProcessor;
Ejemplo n.º 8
0
 /// <summary>
 /// Get the IService caller object of the provided CommandHolder.
 /// </summary>
 /// <param name="referencedCommandHolder"></param>
 /// <returns></returns>
 public IService GetCommandCallerService(CommandHolder referencedCommandHolder) =>
 referencedCommandHolder.Caller;
Ejemplo n.º 9
0
 /// <summary>
 /// Start tracking a command.
 /// </summary>
 /// <param name="commandHolder"></param>
 public void Track(CommandHolder commandHolder) => _runningCommands.Add(commandHolder);
Ejemplo n.º 10
0
        public void Monitor(CommandHolder commandHolder, IService service)
        {
            // Wait for it to be tracked.
            while (!_runningCommands.Contains(commandHolder))
            {
                Thread.Sleep(100);
            }

            // While we should track the process.
            while (_runningCommands.Contains(commandHolder))
            {
                // Check if we should dispose.
                if (commandHolder.Options.DisposeOnExit)
                {
                    // Make sure the service is still running
                    if (service.GetState() == ServiceState.Running)
                    {
                        // Check to see if the process has exited gracefully
                        if (commandHolder.Command.Process.HasExited)
                        {
                            _logger.LogWarning("A process has exited gracefully.");

                            // Stop tracking the command.
                            _runningCommands.Remove(commandHolder);
                        }
                    }
                    else
                    {
                        // Check if the command has not exited.
                        if (!commandHolder.Command.Process.HasExited)
                        {
                            // tell the console
                            _logger.LogWarning(
                                string.Format(
                                    "The service state has changed, Process ID {0} will be killed.",
                                    commandHolder.Command.Process.Id
                                    )
                                );

                            // Gracefully close the process
                            commandHolder.Command.Process.Close();

                            // Stop tracking the command.
                            _runningCommands.Remove(commandHolder);
                        }
                    }
                }
                else // if process not dispose on exit
                {
                    if (service.GetState() == ServiceState.Running)
                    {
                        if (commandHolder.Command.Process.HasExited)
                        {
                            // TODO: address DAEM-112
                            // Tell the console.
                            _logger.LogWarning(
                                "A process has exited unexpectedly, and will be restarted. Command output redirection will cease (DAEM-112)."
                                );

                            // Restart the process.
                            commandHolder.Command.Process.Start();

                            // Attach the logger if needed.
                            if (commandHolder.Options.AttachLogToConsole)
                            {
                                AttachLoggerToCommandHolder(commandHolder, "Monitor");
                            }
                        }
                    }
                    else
                    {
                        if (!commandHolder.Command.Process.HasExited)
                        {
                            _logger.LogWarning(
                                string.Format(
                                    "The service state has changed, Process ID {0} will be killed.",
                                    commandHolder.Command.Process.Id
                                    )
                                );
                            commandHolder.Command.Process.Close();
                        }
                    }
                }

                // Wait for the monitoring interval.
                Thread.Sleep(commandHolder.Options.MonitoringInterval * 1000);
            }

            _logger.LogDebug($"Monitor Thread {Thread.CurrentThread.ManagedThreadId}: Command ({commandHolder.Options.Executable}) is no longer tracked, terminating...");
        }