// this will send either Ctrl-C or Ctrl-Break to runner.listener // Ctrl-C will be used for OnStop() // Ctrl-Break will be used for OnShutdown() private void SendCtrlSignalToRunnerListener(uint signal) { try { if (RunnerListener != null && !RunnerListener.HasExited) { // Try to let the runner process know that we are stopping //Attach service process to console of Runner.Listener process. This is needed, //because windows service doesn't use its own console. if (AttachConsole((uint)RunnerListener.Id)) { //Prevent main service process from stopping because of Ctrl + C event with SetConsoleCtrlHandler SetConsoleCtrlHandler(null, true); try { //Generate console event for current console with GenerateConsoleCtrlEvent (processGroupId should be zero) GenerateConsoleCtrlEvent(signal, 0); //Wait for the process to finish (give it up to 30 seconds) RunnerListener.WaitForExit(30000); } finally { //Disconnect from console and restore Ctrl+C handling by main process FreeConsole(); SetConsoleCtrlHandler(null, false); } } // if runner is still running, kill it if (!RunnerListener.HasExited) { RunnerListener.Kill(); } } } catch (Exception exception) { // InvalidOperationException is thrown when there is no process associated to the process object. // There is no process to kill, Log the exception and shutdown the service. // If we don't handle this here, the service get into a state where it can neither be stoped nor restarted (Error 1061) WriteException(exception); } }
protected override void OnStart(string[] args) { RunningLoop = Task.Run( () => { try { bool stopping; WriteInfo("Starting Actions Runner Service"); TimeSpan timeBetweenRetries = TimeSpan.FromSeconds(5); lock (ServiceLock) { stopping = Stopping; } while (!stopping) { WriteInfo("Starting Actions Runner listener"); lock (ServiceLock) { RunnerListener = CreateRunnerListener(); RunnerListener.OutputDataReceived += RunnerListener_OutputDataReceived; RunnerListener.ErrorDataReceived += RunnerListener_ErrorDataReceived; RunnerListener.Start(); RunnerListener.BeginOutputReadLine(); RunnerListener.BeginErrorReadLine(); } RunnerListener.WaitForExit(); int exitCode = RunnerListener.ExitCode; // exit code 0 and 1 need stop service // exit code 2 and 3 need restart runner switch (exitCode) { case 0: Stopping = true; WriteInfo(Resource.RunnerExitWithoutError); break; case 1: Stopping = true; WriteInfo(Resource.RunnerExitWithTerminatedError); break; case 2: WriteInfo(Resource.RunnerExitWithError); break; case 3: WriteInfo(Resource.RunnerUpdateInProcess); var updateResult = HandleRunnerUpdate(); if (updateResult == RunnerUpdateResult.Succeed) { WriteInfo(Resource.RunnerUpdateSucceed); } else if (updateResult == RunnerUpdateResult.Failed) { WriteInfo(Resource.RunnerUpdateFailed); Stopping = true; } else if (updateResult == RunnerUpdateResult.SucceedNeedRestart) { WriteInfo(Resource.RunnerUpdateRestartNeeded); _restart = true; ExitCode = int.MaxValue; Stop(); } break; default: WriteInfo(Resource.RunnerExitWithUndefinedReturnCode); break; } if (Stopping) { ExitCode = exitCode; Stop(); } else { // wait for few seconds before restarting the process Thread.Sleep(timeBetweenRetries); } lock (ServiceLock) { RunnerListener.OutputDataReceived -= RunnerListener_OutputDataReceived; RunnerListener.ErrorDataReceived -= RunnerListener_ErrorDataReceived; RunnerListener.Dispose(); RunnerListener = null; stopping = Stopping; } } } catch (Exception exception) { WriteException(exception); ExitCode = 99; Stop(); } }); }