Esempio n. 1
0
        protected static async Task <ProcessExecutionResult> RunAsyncInternal(
            Process process,
            ILog log,
            ILog stdout,
            ILog stderr,
            Action <int, int> kill,
            Func <ILog, int, IList <int> > getChildProcessIds,
            TimeSpan?timeout = null,
            Dictionary <string, string>?environmentVariables = null,
            CancellationToken?cancellationToken = null,
            bool?diagnostics = null)
        {
            var stdoutCompletion = new TaskCompletionSource <bool>();
            var stderrCompletion = new TaskCompletionSource <bool>();
            var result           = new ProcessExecutionResult();

            process.StartInfo.RedirectStandardError  = true;
            process.StartInfo.RedirectStandardOutput = true;
            // Make cute emojiis show up as cute emojiis in the output instead of ugly text symbols!
            process.StartInfo.StandardOutputEncoding = Encoding.UTF8;
            process.StartInfo.StandardErrorEncoding  = Encoding.UTF8;
            process.StartInfo.UseShellExecute        = false;

            if (environmentVariables != null)
            {
                foreach (var kvp in environmentVariables)
                {
                    if (kvp.Value == null)
                    {
                        process.StartInfo.EnvironmentVariables.Remove(kvp.Key);
                    }
                    else
                    {
                        process.StartInfo.EnvironmentVariables[kvp.Key] = kvp.Value;
                    }
                }
            }

            process.OutputDataReceived += (sender, e) =>
            {
                if (e.Data != null)
                {
                    lock (stdout)
                    {
                        stdout.WriteLine(e.Data);
                        stdout.Flush();
                    }
                }
                else
                {
                    stdoutCompletion.TrySetResult(true);
                }
            };

            process.ErrorDataReceived += (sender, e) =>
            {
                if (e.Data != null)
                {
                    lock (stderr)
                    {
                        stderr.WriteLine(e.Data);
                        stderr.Flush();
                    }
                }
                else
                {
                    stderrCompletion.TrySetResult(true);
                }
            };

            var sb = new StringBuilder();

            sb.AppendLine($"Running {StringUtils.Quote(process.StartInfo.FileName)} {process.StartInfo.Arguments}");

            if (process.StartInfo.EnvironmentVariables != null)
            {
                var currentEnvironment = Environment.GetEnvironmentVariables().Cast <DictionaryEntry>().ToDictionary(v => v.Key.ToString(), v => v.Value?.ToString(), StringComparer.Ordinal);
                var processEnvironment = process.StartInfo.EnvironmentVariables.Cast <DictionaryEntry>().ToDictionary(v => v.Key.ToString(), v => v.Value?.ToString(), StringComparer.Ordinal);
                var allVariables       = currentEnvironment.Keys.Union(processEnvironment.Keys).Distinct();

                bool headerShown = false;
                foreach (var variable in allVariables)
                {
                    if (variable == null)
                    {
                        continue;
                    }

                    currentEnvironment.TryGetValue(variable, out var a);
                    processEnvironment.TryGetValue(variable, out var b);

                    if (a != b)
                    {
                        if (!headerShown)
                        {
                            sb.Append("With env vars: ");
                            headerShown = true;
                        }

                        sb.Append($"{variable}={StringUtils.Quote(b)} ");
                    }
                }
            }

            log.WriteLine(sb.ToString());

            process.Start();
            var pid = process.Id;

            process.BeginErrorReadLine();
            process.BeginOutputReadLine();

            cancellationToken?.Register(() =>
            {
                var hasExited = false;
                try
                {
                    hasExited = process.HasExited;
                }
                catch
                {
                    // Process.HasExited can sometimes throw exceptions, so
                    // just ignore those and to be safe treat it as the
                    // process didn't exit (the safe option being to not leave
                    // processes behind).
                }

                if (!hasExited)
                {
                    stderr.WriteLine($"Killing process {pid} as it was cancelled");
                    kill(pid, 9);
                }
            });

            if (timeout.HasValue)
            {
                if (!await WaitForExitAsync(process, timeout.Value))
                {
                    log.WriteLine($"Process {pid} didn't exit within {timeout} and will be killed");

                    await KillTreeAsync(pid, log, kill, getChildProcessIds, diagnostics ?? true);

                    result.TimedOut = true;

                    lock (stderr)
                    {
                        log.WriteLine($"{pid} Execution timed out after {timeout.Value.TotalSeconds} seconds and the process was killed.");
                    }
                }
            }
            else
            {
                await WaitForExitAsync(process);
            }

            if (process.HasExited)
            {
                // make sure redirected output events are finished
                process.WaitForExit();
            }

            Task.WaitAll(new Task[] { stderrCompletion.Task, stdoutCompletion.Task }, TimeSpan.FromSeconds(1));

            try
            {
                result.ExitCode = process.ExitCode;
                log.WriteLine($"Process {Path.GetFileName(process.StartInfo.FileName)} exited with {result.ExitCode}");
            }
            catch (Exception e)
            {
                result.ExitCode = 12345678;
                log.WriteLine($"Failed to get ExitCode: {e}");
            }

            return(result);
        }
Esempio n. 2
0
        protected static async Task <ProcessExecutionResult> RunAsyncInternal(
            Process process,
            ILog log,
            ILog stdout,
            ILog stderr,
            Action <int, int> kill,
            Func <ILog, int, IList <int> > getChildrenPS,
            TimeSpan?timeout = null,
            Dictionary <string, string>?environmentVariables = null,
            CancellationToken?cancellationToken = null,
            bool?diagnostics = null)
        {
            var stdout_completion = new TaskCompletionSource <bool>();
            var stderr_completion = new TaskCompletionSource <bool>();
            var rv = new ProcessExecutionResult();

            process.StartInfo.RedirectStandardError  = true;
            process.StartInfo.RedirectStandardOutput = true;
            // Make cute emojiis show up as cute emojiis in the output instead of ugly text symbols!
            process.StartInfo.StandardOutputEncoding = Encoding.UTF8;
            process.StartInfo.StandardErrorEncoding  = Encoding.UTF8;
            process.StartInfo.UseShellExecute        = false;

            if (environmentVariables != null)
            {
                foreach (var kvp in environmentVariables)
                {
                    if (kvp.Value == null)
                    {
                        process.StartInfo.EnvironmentVariables.Remove(kvp.Key);
                    }
                    else
                    {
                        process.StartInfo.EnvironmentVariables[kvp.Key] = kvp.Value;
                    }
                }
            }

            process.OutputDataReceived += (sender, e) =>
            {
                if (e.Data != null)
                {
                    lock (stdout)
                    {
                        stdout.WriteLine(e.Data);
                        stdout.Flush();
                    }
                }
                else
                {
                    stdout_completion.TrySetResult(true);
                }
            };

            process.ErrorDataReceived += (sender, e) =>
            {
                if (e.Data != null)
                {
                    lock (stderr)
                    {
                        stderr.WriteLine(e.Data);
                        stderr.Flush();
                    }
                }
                else
                {
                    stderr_completion.TrySetResult(true);
                }
            };

            var sb = new StringBuilder();

            if (process.StartInfo.EnvironmentVariables != null)
            {
                var currentEnvironment = Environment.GetEnvironmentVariables().Cast <System.Collections.DictionaryEntry>().ToDictionary((v) => (string)v.Key, (v) => (string?)v.Value, StringComparer.Ordinal);
                var processEnvironment = process.StartInfo.EnvironmentVariables.Cast <System.Collections.DictionaryEntry>().ToDictionary((v) => (string)v.Key, (v) => (string?)v.Value, StringComparer.Ordinal);
                var allKeys            = currentEnvironment.Keys.Union(processEnvironment.Keys).Distinct();
                foreach (var key in allKeys)
                {
                    if (key == null)
                    {
                        continue;
                    }

                    string?a = null, b = null;
                    currentEnvironment?.TryGetValue(key !, out a);
                    processEnvironment?.TryGetValue(key !, out b);
                    if (a != b)
                    {
                        sb.Append($"{key}={StringUtils.Quote(b)} ");
                    }
                }
            }
            sb.Append($"{StringUtils.Quote(process.StartInfo.FileName)} {process.StartInfo.Arguments}");
            log.WriteLine(sb);

            process.Start();
            var pid = process.Id;

            process.BeginErrorReadLine();
            process.BeginOutputReadLine();

            cancellationToken?.Register(() =>
            {
                var hasExited = false;
                try
                {
                    hasExited = process.HasExited;
                }
                catch
                {
                    // Process.HasExited can sometimes throw exceptions, so
                    // just ignore those and to be safe treat it as the
                    // process didn't exit (the safe option being to not leave
                    // processes behind).
                }
                if (!hasExited)
                {
                    stderr.WriteLine($"Execution of {pid} was cancelled.");
                    kill(pid, 9);
                }
            });

            if (timeout.HasValue)
            {
                if (!await WaitForExitAsync(process, timeout.Value))
                {
                    await KillTreeAsyncInternal(process.Id, kill, getChildrenPS, log, diagnostics ?? true);

                    rv.TimedOut = true;
                    lock (stderr)
                        log.WriteLine($"{pid} Execution timed out after {timeout.Value.TotalSeconds} seconds and the process was killed.");
                }
            }
            await WaitForExitAsync(process);

            Task.WaitAll(new Task[] { stderr_completion.Task, stdout_completion.Task }, TimeSpan.FromSeconds(1));

            try
            {
                rv.ExitCode = process.ExitCode;
            }
            catch (Exception e)
            {
                rv.ExitCode = 12345678;
                log.WriteLine($"Failed to get ExitCode: {e}");
            }
            return(rv);
        }