X-Git-Url: http://git.iain.cx/?a=blobdiff_plain;f=service.cpp;h=028b035c71281cededbc12b7cca52e18701f31ff;hb=f3d91adc48a7618fd9c94cbc45143f89b47b59e4;hp=f14390f717e3020ae19a0468e348c0a0514f382a;hpb=636b16d3702049145143511c6461ff20bf1f245d;p=nssm.git diff --git a/service.cpp b/service.cpp index f14390f..028b035 100644 --- a/service.cpp +++ b/service.cpp @@ -599,3 +599,89 @@ void throttle_restart() { else Sleep(ms); } } + +/* + When responding to a stop (or any other) request we need to set dwWaitHint to + the number of milliseconds we expect the operation to take, and optionally + increase dwCheckPoint. If dwWaitHint milliseconds elapses without the + operation completing or dwCheckPoint increasing, the system will consider the + service to be hung. + + However the system will consider the service to be hung after 30000 + milliseconds regardless of the value of dwWaitHint if dwCheckPoint has not + changed. Therefore if we want to wait longer than that we must periodically + increase dwCheckPoint. + + Furthermore, it will consider the service to be hung after 60000 milliseconds + regardless of the value of dwCheckPoint unless dwWaitHint is increased every + time dwCheckPoint is also increased. + + Our strategy then is to retrieve the initial dwWaitHint and wait for + NSSM_SHUTDOWN_CHECKPOINT milliseconds. If the process is still running and + we haven't finished waiting we increment dwCheckPoint and add whichever is + smaller of NSSM_SHUTDOWN_CHECKPOINT or the remaining timeout to dwWaitHint. + + Only doing both these things will prevent the system from killing the service. + + Returns: 1 if the wait timed out. + 0 if the wait completed. + -1 on error. +*/ +int await_shutdown(char *function_name, char *service_name, SERVICE_STATUS_HANDLE service_handle, SERVICE_STATUS *service_status, HANDLE process_handle, unsigned long timeout) { + unsigned long interval; + unsigned long waithint; + unsigned long ret; + unsigned long waited; + char interval_milliseconds[16]; + char timeout_milliseconds[16]; + char waited_milliseconds[16]; + char *function = function_name; + + /* Add brackets to function name. */ + size_t funclen = strlen(function_name) + 3; + char *func = (char *) HeapAlloc(GetProcessHeap(), 0, funclen); + if (func) { + if (_snprintf_s(func, funclen, _TRUNCATE, "%s()", function_name) > -1) function = func; + } + + _snprintf_s(timeout_milliseconds, sizeof(timeout_milliseconds), _TRUNCATE, "%lu", timeout); + + waithint = service_status->dwWaitHint; + waited = 0; + while (waited < timeout) { + interval = timeout - waited; + if (interval > NSSM_SHUTDOWN_CHECKPOINT) interval = NSSM_SHUTDOWN_CHECKPOINT; + + service_status->dwCurrentState = SERVICE_STOP_PENDING; + service_status->dwWaitHint += interval; + service_status->dwCheckPoint++; + SetServiceStatus(service_handle, service_status); + + if (waited) { + _snprintf_s(waited_milliseconds, sizeof(waited_milliseconds), _TRUNCATE, "%lu", waited); + _snprintf_s(interval_milliseconds, sizeof(interval_milliseconds), _TRUNCATE, "%lu", interval); + log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SHUTDOWN, function, service_name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0); + } + + switch (WaitForSingleObject(process_handle, interval)) { + case WAIT_OBJECT_0: + ret = 0; + goto awaited; + + case WAIT_TIMEOUT: + ret = 1; + break; + + default: + ret = -1; + goto awaited; + } + + waited += interval; + } + +awaited: + if (func) HeapFree(GetProcessHeap(), 0, func); + + return ret; +}