protected override void OnStop() { lock (ServiceLock) { Stopping = true; // TODO If agent service is killed make sure AgentListener also is killed try { if (AgentListener != null && !AgentListener.HasExited) { // Try to let the agent process know that we are stopping // TODO: This is not working, fix it AgentListener.StandardInput.WriteLine("\x03"); // Wait for the process to finish (give it up to 30 seconds) AgentListener.WaitForExit(30000); // if agent is still running, kill it if (!AgentListener.HasExited) { AgentListener.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 OnStop() { lock (ServiceLock) { Stopping = true; // throw exception during OnStop() will make SCM think the service crash and trigger recovery option. // in this way we can self-update the service host. if (_restart) { throw new Exception(Resource.CrashServiceHost); } // TODO If agent service is killed make sure AgentListener also is killed try { if (AgentListener != null && !AgentListener.HasExited) { // Try to let the agent process know that we are stopping //Attach service process to console of Agent.Listener process. This is needed, //because windows service doesn't use its own console. if (AttachConsole((uint)AgentListener.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(CTRL_C_EVENT, 0); //Wait for the process to finish (give it up to 30 seconds) AgentListener.WaitForExit(30000); } finally { //Disconnect from console and restore Ctrl+C handling by main process FreeConsole(); SetConsoleCtrlHandler(null, false); } } // if agent is still running, kill it if (!AgentListener.HasExited) { AgentListener.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); } } }
// this will send either Ctrl-C or Ctrl-Break to agent.listener // Ctrl-C will be used for OnStop() // Ctrl-Break will be used for OnShutdown() private void SendCtrlSignalToAgentListener(uint signal) { try { if (AgentListener != null && !AgentListener.HasExited) { // Try to let the agent process know that we are stopping //Attach service process to console of Agent.Listener process. This is needed, //because windows service doesn't use its own console. if (AttachConsole((uint)AgentListener.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) AgentListener.WaitForExit(30000); } finally { //Disconnect from console and restore Ctrl+C handling by main process FreeConsole(); SetConsoleCtrlHandler(null, false); } } // if agent is still running, kill it if (!AgentListener.HasExited) { AgentListener.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 VSTS Agent Service"); TimeSpan timeBetweenRetries = TimeSpan.FromSeconds(5); lock (ServiceLock) { stopping = Stopping; } while (!stopping) { WriteInfo("Starting VSTS Agent listener"); lock (ServiceLock) { AgentListener = CreateAgentListener(); AgentListener.OutputDataReceived += AgentListener_OutputDataReceived; AgentListener.ErrorDataReceived += AgentListener_ErrorDataReceived; AgentListener.Start(); AgentListener.BeginOutputReadLine(); AgentListener.BeginErrorReadLine(); } AgentListener.WaitForExit(); int exitCode = AgentListener.ExitCode; // exit code 0 and 1 need stop service // exit code 2 and 3 need restart agent switch (exitCode) { case 0: Stopping = true; WriteInfo(Resource.AgentExitWithoutError); break; case 1: Stopping = true; WriteInfo(Resource.AgentExitWithTerminatedError); break; case 2: WriteInfo(Resource.AgentExitWithError); break; case 3: WriteInfo(Resource.AgentUpdateInProcess); var updateResult = HandleAgentUpdate(); if (updateResult == AgentUpdateResult.Succeed) { WriteInfo(Resource.AgentUpdateSucceed); } else if (updateResult == AgentUpdateResult.Failed) { WriteInfo(Resource.AgentUpdateFailed); Stopping = true; } else if (updateResult == AgentUpdateResult.SucceedNeedRestart) { WriteInfo(Resource.AgentUpdateRestartNeeded); _restart = true; ExitCode = int.MaxValue; Stop(); } break; default: WriteInfo(Resource.AgentExitWithUndefinedReturnCode); break; } if (Stopping) { ExitCode = exitCode; Stop(); } else { // wait for few seconds before restarting the process Thread.Sleep(timeBetweenRetries); } lock (ServiceLock) { AgentListener.OutputDataReceived -= AgentListener_OutputDataReceived; AgentListener.ErrorDataReceived -= AgentListener_ErrorDataReceived; AgentListener.Dispose(); AgentListener = null; stopping = Stopping; } } } catch (Exception exception) { WriteException(exception); ExitCode = 99; Stop(); } }); }
protected override void OnStart(string[] args) { RunningLoop = Task.Run( () => { try { bool stopping; WriteInfo("Starting VSTS Agent Service"); TimeSpan timeBetweenRetries = TimeSpan.FromSeconds(5); lock (ServiceLock) { stopping = Stopping; } while (!stopping) { WriteInfo("Starting VSTS Agent listener"); lock (ServiceLock) { AgentListener = CreateAgentListener(); AgentListener.Start(); } AgentListener.WaitForExit(); int exitCode = AgentListener.ExitCode; // Handle error code 1? // If agent fails (because its not configured) also returns error code 1, in such case we dont want to run the service // Killing a process also returns error code 1, but we want to restart the process here. // TODO: change the error code for run method if agent is not configured? if (exitCode == 2) { // Agent wants to stop the service as well Stopping = true; WriteInfo(Resource.ServiceRequestedToStop); ExitCode = exitCode; Stop(); } else { // wait for few seconds before restarting the process Thread.Sleep(timeBetweenRetries); } } lock (ServiceLock) { AgentListener.Dispose(); AgentListener = null; } } catch (Exception exception) { WriteException(exception); ExitCode = 1; Stop(); } }); }