Пример #1
0
        /// <summary>
        /// Cleanly terminates the current process (for internal use).
        /// </summary>
        /// <param name="exitCode">Optional process exit code (defaults to <b>0</b>).</param>
        /// <param name="explicitTermination">Optionally indicates that termination is not due to receiving an external signal.</param>
        private void ExitInternal(int exitCode = 0, bool explicitTermination = false)
        {
            if (readyToExit)
            {
                // Application has already indicated that it has terminated.

                return;
            }

            var isTerminating = terminating;

            terminating = true;

            if (isTerminating)
            {
                return;     // Already terminating.
            }

            if (explicitTermination)
            {
                log?.LogInfo(() => $"INTERNAL stop request: [timeout={Timeout}]");
            }
            else
            {
                log?.LogInfo(() => $"SIGTERM received: Stopping process [timeout={Timeout}]");
            }

            cts.Cancel();

            lock (handlers)
            {
                foreach (var handler in handlers)
                {
                    new Thread(new ThreadStart(handler)).Start();
                }
            }

            try
            {
                NeonHelper.WaitFor(() => readyToExit, Timeout);
                log?.LogInfo(() => "Process stopped gracefully.");
            }
            catch (TimeoutException)
            {
                log?.LogWarn(() => $"Process did not stop within [{Timeout}].");
            }

            Environment.Exit(exitCode);
        }
Пример #2
0
        /// <summary>
        /// Starts a process to run an executable file and then waits for the process to terminate.
        /// </summary>
        /// <param name="path">Path to the executable file.</param>
        /// <param name="args">Command line arguments (or <c>null</c>).</param>
        /// <param name="timeout">
        /// Optional maximum time to wait for the process to complete or <c>null</c> to wait
        /// indefinitely.
        /// </param>
        /// <param name="process">
        /// The optional <see cref="Process"/> instance to use to launch the process.
        /// </param>
        /// <returns>The process exit code.</returns>
        /// <exception cref="TimeoutException">Thrown if the process did not exit within the <paramref name="timeout"/> limit.</exception>
        /// <remarks>
        /// <note>
        /// If <paramref name="timeout"/> is exceeded and execution has not commpleted in time
        /// then a <see cref="TimeoutException"/> will be thrown and the process will be killed
        /// if it was created by this method.  Process instances passed via the <paramref name="process"/>
        /// parameter will not be killed in this case.
        /// </note>
        /// </remarks>
        public static int Execute(string path, string args, TimeSpan?timeout = null, Process process = null)
        {
            var processInfo   = new ProcessStartInfo(GetProgramPath(path), args ?? string.Empty);
            var killOnTimeout = process == null;

            if (process == null)
            {
                process = new Process();
            }

            try
            {
                processInfo.UseShellExecute        = false;
                processInfo.CreateNoWindow         = true;
                processInfo.RedirectStandardError  = true;
                processInfo.RedirectStandardOutput = true;
                processInfo.WorkingDirectory       = Environment.CurrentDirectory;

                process.StartInfo           = processInfo;
                process.EnableRaisingEvents = true;

                // Configure the sub-process STDOUT and STDERR streams to use
                // code page 1252 which simply passes byte values through.

                processInfo.StandardErrorEncoding      =
                    processInfo.StandardOutputEncoding = ByteEncoding.Instance;

                // Relay STDOUT and STDERR output from the child process
                // to this process's STDOUT and STDERR streams.

                // $todo(jefflill):
                //
                // This won't work properly for binary data streaming
                // back from the process because we're not going to be
                // sure whether the "line" was terminated by a CRLF or
                // just a CR.  I'm not sure if there's a clean way to
                // address this in .NET code.
                //
                //      https://github.com/nforgeio/neonKUBE/issues/461

                var stdErrClosed = false;
                var stdOutClosed = false;

                process.ErrorDataReceived +=
                    (s, a) =>
                {
                    if (a.Data == null)
                    {
                        stdErrClosed = true;
                    }
                    else
                    {
                        Console.Error.WriteLine(a.Data);
                    }
                };

                process.OutputDataReceived +=
                    (s, a) =>
                {
                    if (a.Data == null)
                    {
                        stdOutClosed = true;
                    }
                    else
                    {
                        Console.Out.WriteLine(a.Data);
                    }
                };

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

                if (!timeout.HasValue || timeout.Value >= TimeSpan.FromDays(1))
                {
                    NeonHelper.WaitFor(() => stdErrClosed && stdOutClosed, timeout: timeout ?? TimeSpan.FromDays(365), pollTime: TimeSpan.FromMilliseconds(250));
                    process.WaitForExit();
                }
                else
                {
                    NeonHelper.WaitFor(() => stdErrClosed && stdOutClosed, timeout: timeout.Value, pollTime: TimeSpan.FromMilliseconds(250));
                    process.WaitForExit((int)timeout.Value.TotalMilliseconds);

                    if (!process.HasExited)
                    {
                        if (killOnTimeout)
                        {
                            process.Kill();
                        }

                        throw new TimeoutException(string.Format("Process [{0}] execute has timed out.", path));
                    }
                }

                return(process.ExitCode);
            }
            finally
            {
                process.Dispose();
            }
        }