예제 #1
0
        private static async Task <List <string> > runDockerCommandWithOutputAsync(string args, int timeout = 10000, CancellationToken cancellationToken = default)
        {
            var command        = CommandInfo.DockerCommand(args);
            var processPayload = new ProcessPayload();
            var final          = new List <string>();

            var processTask = OperationSystemService.RunCommandAsync(command, processPayload, tempLogDir(), null, log: false,
                                                                     outputReceived: line => final.Add(line),
                                                                     cancellationToken: cancellationToken);

            await Task.WhenAny(processTask, Task.Delay(timeout, cancellationToken));

            processPayload.Kill();

            return(final.ToListThreadSafe());
        }
        public static async Task RunCommandAsync(
            CommandInfo commandInfo,
            ProcessPayload processPayload,
            DirectoryInfo logDir,
            string workingDirectory,
            bool log = true,
            Action <string> outputReceived = null,
            Action <string> errorReceived  = null,
            Action exited = null,
            CancellationToken cancellationToken = default)
        {
            cancellationToken.ThrowIfCancellationRequested();

            var process = new Process
            {
                StartInfo =
                {
                    CreateNoWindow         = true,
                    RedirectStandardError  = true,
                    RedirectStandardInput  = true,
                    RedirectStandardOutput = true,
                    UseShellExecute        = false,
                    WorkingDirectory       = workingDirectory,
                    FileName  = commandInfo.FileName,
                    Arguments = commandInfo.Args
                }
            };

            var logCommandInfoSemaphore = new SemaphoreSlim(1, 1);

            async Task <(string stdOut, string stdErr)> logFilePathAsync()
            {
                var commandInfoFilePath = Path.Combine(logDir.FullName, "command.info");
                await logCommandInfoSemaphore.WaitAsync(cancellationToken);

                try
                {
                    if (!File.Exists(commandInfoFilePath))
                    {
                        await File.WriteAllTextAsync(commandInfoFilePath, JsonConvert.SerializeObject(commandInfo, Formatting.Indented), cancellationToken);
                    }
                }
                finally
                {
                    logCommandInfoSemaphore.Release();
                }

                var stdOutFile = Path.Combine(logDir.FullName, "out.txt");
                var stdErrFile = Path.Combine(logDir.FullName, "err.txt");

                return(stdOutFile, stdErrFile);
            }

            #region EventListeners
            outputReceived = outputReceived ?? (x => { });
            errorReceived  = errorReceived ?? (x => { });
            exited         = exited ?? (() => { });

            var processEndLocks = new HashSet <object>();

            process.OutputDataReceived += async(sender, data) =>
            {
                var endLock = new object();
                processEndLocks.AddThreadSafe(endLock);
                try
                {
                    if (data.Data.IsNullOrWhiteSpace().Not())
                    {
                        await writeCommandLogAsync((await logFilePathAsync()).stdOut, $"{DateTime.Now:s}: {data.Data}", log, cancellationToken);

                        outputReceived(data.Data);
                    }
                }
                finally
                {
                    processEndLocks.RemoveThreadSafe(endLock);
                }
            };

            process.ErrorDataReceived += async(sender, data) =>
            {
                var endLock = new object();
                processEndLocks.AddThreadSafe(endLock);
                try
                {
                    if (data.Data.IsNullOrWhiteSpace().Not())
                    {
                        await writeCommandLogAsync((await logFilePathAsync()).stdErr, $"{DateTime.Now:s}: {data.Data}", log, cancellationToken);

                        errorReceived(data.Data);
                    }
                }
                finally
                {
                    processEndLocks.RemoveThreadSafe(endLock);
                }
            };

            process.Exited += async(_, __) =>
            {
                var endLock = new object();
                processEndLocks.AddThreadSafe(endLock);
                try
                {
                    await writeCommandLogAsync((await logFilePathAsync()).stdOut, $"{DateTime.Now:s}: exited.", log, cancellationToken);

                    exited();
                }
                finally
                {
                    processEndLocks.RemoveThreadSafe(endLock);
                }
            };
            #endregion

            cancellationToken.Register(() =>
            {
                if (processPayload.IsRunning())
                {
                    processPayload.Kill();
                }
            });

            process.EnableRaisingEvents = true;

            _processes.AddThreadSafe(process);

            if (process.Start())
            {
                processPayload.ProcessId = process.Id;
                await writeCommandLogAsync((await logFilePathAsync()).stdOut, $"{DateTime.Now:s}: started.", log, cancellationToken);

                process.BeginErrorReadLine();
                process.BeginOutputReadLine();
            }
            else
            {
                throw new Exception("could not start process");
            }

            await process.WaitForExitAsync(cancellationToken);

            if (cancellationToken.IsCancellationRequested)
            {
                process.Kill();
            }

            await Task.Run(async() => { while (processPayload.IsRunning())
                                        {
                                            await Task.Delay(100, cancellationToken);
                                        }
                           }, cancellationToken);

            await Task.Run(async() => { while (processEndLocks.ToList().Any())
                                        {
                                            await Task.Delay(100, cancellationToken);
                                        }
                           }, cancellationToken);

            _processes.RemoveThreadSafe(process);
        }