X-Git-Url: http://git.iain.cx/?a=blobdiff_plain;f=service.cpp;h=5a286417526cd80167cde4499bbfb58c9c29e4f1;hb=2dbaf62e06018a7d0a934c79bd9f39b81dc43694;hp=4327a012a98d94e6c6f03e557cd175255c78dcca;hpb=4798232daef193da47e021879f1f6653137be8bb;p=nssm.git diff --git a/service.cpp b/service.cpp index 4327a01..5a28641 100644 --- a/service.cpp +++ b/service.cpp @@ -38,6 +38,7 @@ static inline int service_control_response(unsigned long control, unsigned long case SERVICE_CONTROL_STOP: case SERVICE_CONTROL_SHUTDOWN: switch (status) { + case SERVICE_RUNNING: case SERVICE_STOP_PENDING: return 1; @@ -73,6 +74,7 @@ static inline int service_control_response(unsigned long control, unsigned long } case SERVICE_CONTROL_INTERROGATE: + case NSSM_SERVICE_CONTROL_ROTATE: return 0; } @@ -81,12 +83,17 @@ static inline int service_control_response(unsigned long control, unsigned long static inline int await_service_control_response(unsigned long control, SC_HANDLE service_handle, SERVICE_STATUS *service_status, unsigned long initial_status) { int tries = 0; + unsigned long checkpoint = 0; + unsigned long waithint = 0; while (QueryServiceStatus(service_handle, service_status)) { int response = service_control_response(control, service_status->dwCurrentState); /* Alas we can't WaitForSingleObject() on an SC_HANDLE. */ if (! response) return response; if (response > 0 || service_status->dwCurrentState == initial_status) { - if (++tries > 10) return response; + if (service_status->dwCheckPoint != checkpoint || service_status->dwWaitHint != waithint) tries = 0; + checkpoint = service_status->dwCheckPoint; + waithint = service_status->dwWaitHint; + if (++tries > 10) tries = 10; Sleep(50 * tries); } else return response; @@ -689,6 +696,7 @@ void set_nssm_service_defaults(nssm_service_t *service) { service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD; service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD; service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD; + service->kill_process_tree = 1; } /* Allocate and zero memory for a service. */ @@ -709,7 +717,7 @@ void cleanup_nssm_service(nssm_service_t *service) { if (service->dependencies) HeapFree(GetProcessHeap(), 0, service->dependencies); if (service->env) HeapFree(GetProcessHeap(), 0, service->env); if (service->env_extra) HeapFree(GetProcessHeap(), 0, service->env_extra); - if (service->handle) CloseHandle(service->handle); + if (service->handle) CloseServiceHandle(service->handle); if (service->process_handle) CloseHandle(service->process_handle); if (service->wait_handle) UnregisterWait(service->wait_handle); if (service->throttle_section_initialised) DeleteCriticalSection(&service->throttle_section); @@ -856,7 +864,7 @@ int pre_edit_service(int argc, TCHAR **argv) { /* Get system details. */ QUERY_SERVICE_CONFIG *qsc = query_service_config(service->name, service->handle); if (! qsc) { - CloseHandle(service->handle); + CloseServiceHandle(service->handle); CloseServiceHandle(services); return 4; } @@ -865,7 +873,7 @@ int pre_edit_service(int argc, TCHAR **argv) { if (! (service->type & SERVICE_WIN32_OWN_PROCESS)) { if (mode != MODE_GETTING) { HeapFree(GetProcessHeap(), 0, qsc); - CloseHandle(service->handle); + CloseServiceHandle(service->handle); CloseServiceHandle(services); print_message(stderr, NSSM_MESSAGE_CANNOT_EDIT, service->name, NSSM_WIN32_OWN_PROCESS, 0); return 3; @@ -875,7 +883,7 @@ int pre_edit_service(int argc, TCHAR **argv) { if (get_service_startup(service->name, service->handle, qsc, &service->startup)) { if (mode != MODE_GETTING) { HeapFree(GetProcessHeap(), 0, qsc); - CloseHandle(service->handle); + CloseServiceHandle(service->handle); CloseServiceHandle(services); return 4; } @@ -884,7 +892,7 @@ int pre_edit_service(int argc, TCHAR **argv) { if (get_service_username(service->name, qsc, &service->username, &service->usernamelen)) { if (mode != MODE_GETTING) { HeapFree(GetProcessHeap(), 0, qsc); - CloseHandle(service->handle); + CloseServiceHandle(service->handle); CloseServiceHandle(services); return 5; } @@ -903,7 +911,7 @@ int pre_edit_service(int argc, TCHAR **argv) { /* Get extended system details. */ if (get_service_description(service->name, service->handle, _countof(service->description), service->description)) { if (mode != MODE_GETTING) { - CloseHandle(service->handle); + CloseServiceHandle(service->handle); CloseServiceHandle(services); return 6; } @@ -911,7 +919,7 @@ int pre_edit_service(int argc, TCHAR **argv) { if (get_service_dependencies(service->name, service->handle, &service->dependencies, &service->dependencieslen)) { if (mode != MODE_GETTING) { - CloseHandle(service->handle); + CloseServiceHandle(service->handle); CloseServiceHandle(services); return 7; } @@ -935,7 +943,7 @@ int pre_edit_service(int argc, TCHAR **argv) { /* Trying to manage App* parameters for a non-NSSM service. */ if (! setting->native && service->native) { - CloseHandle(service->handle); + CloseServiceHandle(service->handle); print_message(stderr, NSSM_MESSAGE_NATIVE_PARAMETER, setting->name, NSSM); return 1; } @@ -953,7 +961,7 @@ int pre_edit_service(int argc, TCHAR **argv) { if (setting->native) ret = get_setting(service->name, service->handle, setting, &value, additional); else ret = get_setting(service->name, key, setting, &value, additional); if (ret < 0) { - CloseHandle(service->handle); + CloseServiceHandle(service->handle); return 5; } @@ -971,7 +979,7 @@ int pre_edit_service(int argc, TCHAR **argv) { } if (! service->native) RegCloseKey(key); - CloseHandle(service->handle); + CloseServiceHandle(service->handle); return 0; } @@ -993,7 +1001,7 @@ int pre_edit_service(int argc, TCHAR **argv) { value.string = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR)); if (! value.string) { print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("value"), _T("edit_service()")); - CloseHandle(service->handle); + CloseServiceHandle(service->handle); return 2; } @@ -1026,12 +1034,12 @@ int pre_edit_service(int argc, TCHAR **argv) { if (value.string) HeapFree(GetProcessHeap(), 0, value.string); if (ret < 0) { if (! service->native) RegCloseKey(key); - CloseHandle(service->handle); + CloseServiceHandle(service->handle); return 6; } if (! service->native) RegCloseKey(key); - CloseHandle(service->handle); + CloseServiceHandle(service->handle); return 0; } @@ -1241,7 +1249,7 @@ int control_service(unsigned long control, int argc, TCHAR **argv) { if (ret) { int response = await_service_control_response(control, service_handle, &service_status, initial_status); - CloseHandle(service_handle); + CloseServiceHandle(service_handle); if (response) { print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control)); @@ -1251,7 +1259,7 @@ int control_service(unsigned long control, int argc, TCHAR **argv) { return 0; } else { - CloseHandle(service_handle); + CloseServiceHandle(service_handle); _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error)); return 1; } @@ -1287,7 +1295,7 @@ int control_service(unsigned long control, int argc, TCHAR **argv) { if (ret) { int response = await_service_control_response(control, service_handle, &service_status, initial_status); - CloseHandle(service_handle); + CloseServiceHandle(service_handle); if (response) { print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control)); @@ -1297,7 +1305,7 @@ int control_service(unsigned long control, int argc, TCHAR **argv) { return 0; } else { - CloseHandle(service_handle); + CloseServiceHandle(service_handle); _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error)); if (error == ERROR_SERVICE_NOT_ACTIVE) { if (control == SERVICE_CONTROL_SHUTDOWN || control == SERVICE_CONTROL_STOP) return 0; @@ -1362,7 +1370,7 @@ void WINAPI service_main(unsigned long argc, TCHAR **argv) { /* Initialise status */ ZeroMemory(&service->status, sizeof(service->status)); service->status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS; - service->status.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE; + service->status.dwControlsAccepted = 0; service->status.dwWin32ExitCode = NO_ERROR; service->status.dwServiceSpecificExitCode = 0; service->status.dwCheckPoint = 0; @@ -1393,6 +1401,7 @@ void WINAPI service_main(unsigned long argc, TCHAR **argv) { if (services) { service->handle = open_service(services, service->name, SERVICE_CHANGE_CONFIG, 0, 0); set_service_recovery(service); + CloseServiceHandle(services); } } @@ -1460,6 +1469,7 @@ TCHAR *service_control_text(unsigned long control) { case SERVICE_CONTROL_CONTINUE: return _T("CONTINUE"); case SERVICE_CONTROL_INTERROGATE: return _T("INTERROGATE"); case NSSM_SERVICE_CONTROL_ROTATE: return _T("ROTATE"); + case SERVICE_CONTROL_POWEREVENT: return _T("POWEREVENT"); default: return 0; } } @@ -1567,6 +1577,15 @@ unsigned long WINAPI service_control_handler(unsigned long control, unsigned lon if (service->rotate_stdout_online == NSSM_ROTATE_ONLINE) service->rotate_stdout_online = NSSM_ROTATE_ONLINE_ASAP; if (service->rotate_stderr_online == NSSM_ROTATE_ONLINE) service->rotate_stderr_online = NSSM_ROTATE_ONLINE_ASAP; return NO_ERROR; + + case SERVICE_CONTROL_POWEREVENT: + if (event != PBT_APMRESUMEAUTOMATIC) { + log_service_control(service->name, control, false); + return NO_ERROR; + } + log_service_control(service->name, control, true); + end_service((void *) service, false); + return NO_ERROR; } /* Unknown control */ @@ -1676,19 +1695,13 @@ int start_service(nssm_service_t *service) { but be mindful of the fact that we are blocking the service control manager so abandon the wait before too much time has elapsed. */ - unsigned long delay = service->throttle_delay; - if (delay > NSSM_SERVICE_STATUS_DEADLINE) { - TCHAR delay_milliseconds[16]; - _sntprintf_s(delay_milliseconds, _countof(delay_milliseconds), _TRUNCATE, _T("%lu"), delay); - TCHAR deadline_milliseconds[16]; - _sntprintf_s(deadline_milliseconds, _countof(deadline_milliseconds), _TRUNCATE, _T("%lu"), NSSM_SERVICE_STATUS_DEADLINE); - log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_STARTUP_DELAY_TOO_LONG, service->name, delay_milliseconds, NSSM, deadline_milliseconds, 0); - delay = NSSM_SERVICE_STATUS_DEADLINE; - } - unsigned long deadline = WaitForSingleObject(service->process_handle, delay); + service->status.dwCurrentState = SERVICE_START_PENDING; + service->status.dwControlsAccepted = SERVICE_ACCEPT_POWEREVENT | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP; + if (await_single_handle(service->status_handle, &service->status, service->process_handle, service->name, _T("start_service"), service->throttle_delay) == 1) service->throttle = 0; /* Signal successful start */ service->status.dwCurrentState = SERVICE_RUNNING; + service->status.dwControlsAccepted &= ~SERVICE_ACCEPT_PAUSE_CONTINUE; SetServiceStatus(service->status_handle, &service->status); /* Continue waiting for a clean startup. */ @@ -1724,14 +1737,18 @@ int stop_service(nssm_service_t *service, unsigned long exitcode, bool graceful, if (graceful) { service->status.dwCurrentState = SERVICE_STOP_PENDING; service->status.dwWaitHint = NSSM_WAITHINT_MARGIN; - SetServiceStatus(service->status_handle, &service->status); } + service->status.dwControlsAccepted = 0; + SetServiceStatus(service->status_handle, &service->status); /* Nothing to do if service isn't running */ if (service->pid) { /* Shut down service */ log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service->name, service->exe, 0); - kill_process(service, service->process_handle, service->pid, 0); + kill_t k; + service_kill_t(service, &k); + k.exitcode = 0; + kill_process(&k); } else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service->name, service->exe, 0); @@ -1790,7 +1807,11 @@ void CALLBACK end_service(void *arg, unsigned char why) { /* Clean up. */ if (exitcode == STILL_ACTIVE) exitcode = 0; - if (service->pid) kill_process_tree(service, service->pid, exitcode, service->pid); + if (service->pid && service->kill_process_tree) { + kill_t k; + service_kill_t(service, &k); + kill_process_tree(&k, service->pid); + } service->pid = 0; /* @@ -1876,6 +1897,7 @@ void throttle_restart(nssm_service_t *service) { } service->status.dwCurrentState = SERVICE_PAUSED; + service->status.dwControlsAccepted |= SERVICE_ACCEPT_PAUSE_CONTINUE; SetServiceStatus(service->status_handle, &service->status); if (use_critical_section) { @@ -1912,13 +1934,16 @@ void throttle_restart(nssm_service_t *service) { Only doing both these things will prevent the system from killing the service. + If the status_handle and service_status arguments are omitted, this function + will not try to update the service manager but it will still log to the + event log that it is waiting for a handle. + Returns: 1 if the wait timed out. 0 if the wait completed. -1 on error. */ -int await_shutdown(nssm_service_t *service, TCHAR *function_name, unsigned long timeout) { +int await_single_handle(SERVICE_STATUS_HANDLE status_handle, SERVICE_STATUS *status, HANDLE handle, TCHAR *name, TCHAR *function_name, unsigned long timeout) { unsigned long interval; - unsigned long waithint; unsigned long ret; unsigned long waited; TCHAR interval_milliseconds[16]; @@ -1935,31 +1960,31 @@ int await_shutdown(nssm_service_t *service, TCHAR *function_name, unsigned long _sntprintf_s(timeout_milliseconds, _countof(timeout_milliseconds), _TRUNCATE, _T("%lu"), timeout); - waithint = service->status.dwWaitHint; waited = 0; while (waited < timeout) { interval = timeout - waited; if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE; - service->status.dwCurrentState = SERVICE_STOP_PENDING; - service->status.dwWaitHint += interval; - service->status.dwCheckPoint++; - SetServiceStatus(service->status_handle, &service->status); + if (status) { + status->dwWaitHint += interval; + status->dwCheckPoint++; + SetServiceStatus(status_handle, status); + } if (waited) { _sntprintf_s(waited_milliseconds, _countof(waited_milliseconds), _TRUNCATE, _T("%lu"), waited); _sntprintf_s(interval_milliseconds, _countof(interval_milliseconds), _TRUNCATE, _T("%lu"), interval); - log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SHUTDOWN, function, service->name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0); + log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SINGLE_HANDLE, function, name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0); } - switch (WaitForSingleObject(service->process_handle, interval)) { + switch (WaitForSingleObject(handle, interval)) { case WAIT_OBJECT_0: ret = 0; goto awaited; case WAIT_TIMEOUT: ret = 1; - break; + break; default: ret = -1;