X-Git-Url: http://git.iain.cx/?a=blobdiff_plain;f=service.cpp;h=fd25163cdb26bab421998f8a60c2b8d0893b755c;hb=5078d2360f13ed4e122d21202f06d0a97b75b458;hp=a90d5089316c3cb56d4ae317799fe88cf455680d;hpb=e9d9b2fa71473e96650fe3fc4ee0d5535c6725d8;p=nssm.git diff --git a/service.cpp b/service.cpp index a90d508..fd25163 100644 --- a/service.cpp +++ b/service.cpp @@ -10,8 +10,9 @@ char exe[EXE_LENGTH]; char flags[CMD_LENGTH]; char dir[MAX_PATH]; bool stopping; -CRITICAL_SECTION throttle_section; -CONDITION_VARIABLE throttle_condition; +unsigned long throttle_delay; +HANDLE throttle_timer; +LARGE_INTEGER throttle_duetime; static enum { NSSM_EXIT_RESTART, NSSM_EXIT_IGNORE, NSSM_EXIT_REALLY, NSSM_EXIT_UNCLEAN } exit_actions; static const char *exit_action_strings[] = { "Restart", "Ignore", "Exit", "Suicide", 0 }; @@ -95,13 +96,12 @@ int install_service(char *name, char *exe, char *flags) { /* Construct command */ char command[CMD_LENGTH]; - size_t runlen = strlen(NSSM_RUN); size_t pathlen = strlen(path); - if (pathlen + runlen + 2 >= VALUE_LENGTH) { + if (pathlen + 1 >= VALUE_LENGTH) { fprintf(stderr, "The full path to " NSSM " is too long!\n"); return 3; } - if (snprintf(command, sizeof(command), "\"%s\" %s", path, NSSM_RUN) < 0) { + if (_snprintf(command, sizeof(command), "\"%s\"", path) < 0) { fprintf(stderr, "Out of memory for ImagePath!\n"); return 4; } @@ -198,19 +198,10 @@ void WINAPI service_main(unsigned long argc, char **argv) { return; } - /* Get startup parameters */ - int ret = get_parameters(argv[0], exe, sizeof(exe), flags, sizeof(flags), dir, sizeof(dir)); - if (ret) { - log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, argv[0], 0); - service_status.dwCurrentState = SERVICE_STOPPED; - /* An accurate, if not particularly helpful, status */ - service_status.dwWin32ExitCode = ERROR_SERVICE_NOT_ACTIVE; - SetServiceStatus(service_handle, &service_status); - return; - } + log_service_control(service_name, 0, true); service_status.dwCurrentState = SERVICE_START_PENDING; - service_status.dwWaitHint = NSSM_RESET_THROTTLE_RESTART + NSSM_WAITHINT_MARGIN; + service_status.dwWaitHint = throttle_delay + NSSM_WAITHINT_MARGIN; SetServiceStatus(service_handle, &service_status); /* Try to create the exit action parameters; we don't care if it fails */ @@ -219,7 +210,10 @@ void WINAPI service_main(unsigned long argc, char **argv) { set_service_recovery(service_name); /* Used for signalling a resume if the service pauses when throttled. */ - InitializeCriticalSection(&throttle_section); + throttle_timer = CreateWaitableTimer(0, 1, 0); + if (! throttle_timer) { + log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service_name, error_string(GetLastError()), 0); + } monitor_service(); } @@ -246,7 +240,7 @@ int monitor_service() { int ret = start_service(); if (ret) { char code[16]; - snprintf(code, sizeof(code), "%d", ret); + _snprintf(code, sizeof(code), "%d", ret); log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, exe, service_name, ret, 0); return ret; } @@ -260,17 +254,63 @@ int monitor_service() { return 0; } +char *service_control_text(unsigned long control) { + switch (control) { + /* HACK: there is no SERVICE_CONTROL_START constant */ + case 0: return "START"; + case SERVICE_CONTROL_STOP: return "STOP"; + case SERVICE_CONTROL_SHUTDOWN: return "SHUTDOWN"; + case SERVICE_CONTROL_PAUSE: return "PAUSE"; + case SERVICE_CONTROL_CONTINUE: return "CONTINUE"; + case SERVICE_CONTROL_INTERROGATE: return "INTERROGATE"; + default: return 0; + } +} + +void log_service_control(char *service_name, unsigned long control, bool handled) { + char *text = service_control_text(control); + unsigned long event; + + if (! text) { + /* "0x" + 8 x hex + NULL */ + text = (char *) HeapAlloc(GetProcessHeap(), 0, 11); + if (! text) { + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "control code", "log_service_control", 0); + return; + } + if (_snprintf(text, 11, "0x%08x", control) < 0) { + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "control code", "log_service_control", 0); + HeapFree(GetProcessHeap(), 0, text); + return; + } + + event = NSSM_EVENT_SERVICE_CONTROL_UNKNOWN; + } + else if (handled) event = NSSM_EVENT_SERVICE_CONTROL_HANDLED; + else event = NSSM_EVENT_SERVICE_CONTROL_NOT_HANDLED; + + log_event(EVENTLOG_INFORMATION_TYPE, event, service_name, text, 0); + + if (event == NSSM_EVENT_SERVICE_CONTROL_UNKNOWN) { + HeapFree(GetProcessHeap(), 0, text); + } +} + /* Service control handler */ unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) { switch (control) { case SERVICE_CONTROL_SHUTDOWN: case SERVICE_CONTROL_STOP: + log_service_control(service_name, control, true); stop_service(0, true, true); return NO_ERROR; case SERVICE_CONTROL_CONTINUE: + log_service_control(service_name, control, true); + if (! throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED; throttle = 0; - WakeConditionVariable(&throttle_condition); + ZeroMemory(&throttle_duetime, sizeof(throttle_duetime)); + SetWaitableTimer(throttle_timer, &throttle_duetime, 0, 0, 0, 0); service_status.dwCurrentState = SERVICE_CONTINUE_PENDING; service_status.dwWaitHint = throttle_milliseconds() + NSSM_WAITHINT_MARGIN; log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service_name, 0); @@ -282,10 +322,12 @@ unsigned long WINAPI service_control_handler(unsigned long control, unsigned lon We don't accept pause messages but it isn't possible to register only for continue messages so we have to handle this case. */ + log_service_control(service_name, control, false); return ERROR_CALL_NOT_IMPLEMENTED; } /* Unknown control */ + log_service_control(service_name, control, false); return ERROR_CALL_NOT_IMPLEMENTED; } @@ -304,17 +346,27 @@ int start_service() { PROCESS_INFORMATION pi; ZeroMemory(&pi, sizeof(pi)); + /* Get startup parameters */ + char *env = 0; + int ret = get_parameters(service_name, exe, sizeof(exe), flags, sizeof(flags), dir, sizeof(dir), &env, &throttle_delay); + if (ret) { + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service_name, 0); + return stop_service(2, true, true); + } + /* Launch executable with arguments */ char cmd[CMD_LENGTH]; - if (_snprintf(cmd, sizeof(cmd), "%s %s", exe, flags) < 0) { + if (_snprintf(cmd, sizeof(cmd), "\"%s\" %s", exe, flags) < 0) { log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "command line", "start_service", 0); return stop_service(2, true, true); } throttle_restart(); - if (! CreateProcess(0, cmd, 0, 0, false, 0, 0, dir, &si, &pi)) { - log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service_name, exe, error_string(GetLastError()), 0); + if (! CreateProcess(0, cmd, 0, 0, false, 0, env, dir, &si, &pi)) { + unsigned long error = GetLastError(); + if (error == ERROR_INVALID_PARAMETER && env) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED_INVALID_ENVIRONMENT, service_name, exe, NSSM_REG_ENV, 0); + else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service_name, exe, error_string(error), 0); return stop_service(3, true, true); } process_handle = pi.hProcess; @@ -325,7 +377,7 @@ int start_service() { SetServiceStatus(service_handle, &service_status); /* Wait for a clean startup. */ - if (WaitForSingleObject(process_handle, NSSM_RESET_THROTTLE_RESTART) == WAIT_TIMEOUT) throttle = 0; + if (WaitForSingleObject(process_handle, throttle_delay) == WAIT_TIMEOUT) throttle = 0; return 0; } @@ -453,16 +505,19 @@ void throttle_restart() { if (throttle > 7) throttle = 8; char threshold[8], milliseconds[8]; - _snprintf(threshold, sizeof(threshold), "%d", NSSM_RESET_THROTTLE_RESTART); + _snprintf(threshold, sizeof(threshold), "%d", throttle_delay); _snprintf(milliseconds, sizeof(milliseconds), "%d", ms); log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service_name, threshold, milliseconds, 0); - EnterCriticalSection(&throttle_section); + if (throttle_timer) { + ZeroMemory(&throttle_duetime, sizeof(throttle_duetime)); + throttle_duetime.QuadPart = 0 - (ms * 10000LL); + SetWaitableTimer(throttle_timer, &throttle_duetime, 0, 0, 0, 0); + } service_status.dwCurrentState = SERVICE_PAUSED; SetServiceStatus(service_handle, &service_status); - SleepConditionVariableCS(&throttle_condition, &throttle_section, ms); - - LeaveCriticalSection(&throttle_section); + if (throttle_timer) WaitForSingleObject(throttle_timer, INFINITE); + else Sleep(ms); }