bool allow_restart;\r
unsigned long throttle_delay;\r
unsigned long stop_method;\r
+unsigned long kill_console_delay;\r
+unsigned long kill_window_delay;\r
+unsigned long kill_threads_delay;\r
+CRITICAL_SECTION throttle_section;\r
+CONDITION_VARIABLE throttle_condition;\r
HANDLE throttle_timer;\r
LARGE_INTEGER throttle_duetime;\r
+bool use_critical_section;\r
FILETIME creation_time;\r
\r
+extern imports_t imports;\r
+\r
static enum { NSSM_EXIT_RESTART, NSSM_EXIT_IGNORE, NSSM_EXIT_REALLY, NSSM_EXIT_UNCLEAN } exit_actions;\r
static const char *exit_action_strings[] = { "Restart", "Ignore", "Exit", "Suicide", 0 };\r
\r
return;\r
}\r
\r
+ /* We can use a condition variable in a critical section on Vista or later. */\r
+ if (imports.SleepConditionVariableCS && imports.WakeConditionVariable) use_critical_section = true;\r
+ else use_critical_section = false;\r
+\r
/* Initialise status */\r
ZeroMemory(&service_status, sizeof(service_status));\r
service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;\r
}\r
\r
/* Used for signalling a resume if the service pauses when throttled. */\r
- throttle_timer = CreateWaitableTimer(0, 1, 0);\r
- if (! throttle_timer) {\r
- log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service_name, error_string(GetLastError()), 0);\r
+ if (use_critical_section) InitializeCriticalSection(&throttle_section);\r
+ else {\r
+ throttle_timer = CreateWaitableTimer(0, 1, 0);\r
+ if (! throttle_timer) {\r
+ log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service_name, error_string(GetLastError()), 0);\r
+ }\r
}\r
\r
monitor_service();\r
}\r
log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, exe, flags, service_name, dir, 0);\r
\r
- /* Monitor service service */\r
+ /* Monitor service */\r
if (! RegisterWaitForSingleObject(&wait_handle, process_handle, end_service, (void *) pid, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {\r
log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service_name, exe, error_string(GetLastError()), 0);\r
}\r
/* "0x" + 8 x hex + NULL */\r
text = (char *) HeapAlloc(GetProcessHeap(), 0, 11);\r
if (! text) {\r
- log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "control code", "log_service_control", 0);\r
+ log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "control code", "log_service_control()", 0);\r
return;\r
}\r
if (_snprintf_s(text, 11, _TRUNCATE, "0x%08x", control) < 0) {\r
- log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "control code", "log_service_control", 0);\r
+ log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "control code", "log_service_control()", 0);\r
HeapFree(GetProcessHeap(), 0, text);\r
return;\r
}\r
\r
case SERVICE_CONTROL_CONTINUE:\r
log_service_control(service_name, control, true);\r
- if (! throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;\r
throttle = 0;\r
- ZeroMemory(&throttle_duetime, sizeof(throttle_duetime));\r
- SetWaitableTimer(throttle_timer, &throttle_duetime, 0, 0, 0, 0);\r
+ if (use_critical_section) imports.WakeConditionVariable(&throttle_condition);\r
+ else {\r
+ if (! throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;\r
+ ZeroMemory(&throttle_duetime, sizeof(throttle_duetime));\r
+ SetWaitableTimer(throttle_timer, &throttle_duetime, 0, 0, 0, 0);\r
+ }\r
service_status.dwCurrentState = SERVICE_CONTINUE_PENDING;\r
service_status.dwWaitHint = throttle_milliseconds() + NSSM_WAITHINT_MARGIN;\r
log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service_name, 0);\r
\r
/* Get startup parameters */\r
char *env = 0;\r
- int ret = get_parameters(service_name, exe, sizeof(exe), flags, sizeof(flags), dir, sizeof(dir), &env, &throttle_delay, &stop_method, &si);\r
+ int ret = get_parameters(service_name, exe, sizeof(exe), flags, sizeof(flags), dir, sizeof(dir), &env, &throttle_delay, &stop_method, &kill_console_delay, &kill_window_delay, &kill_threads_delay, &si);\r
if (ret) {\r
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service_name, 0);\r
return stop_service(2, true, true);\r
/* Signal we are stopping */\r
if (graceful) {\r
service_status.dwCurrentState = SERVICE_STOP_PENDING;\r
- service_status.dwWaitHint = NSSM_KILL_WINDOW_GRACE_PERIOD + NSSM_KILL_THREADS_GRACE_PERIOD + NSSM_WAITHINT_MARGIN;\r
+ service_status.dwWaitHint = NSSM_WAITHINT_MARGIN;\r
+ if (stop_method & NSSM_STOP_METHOD_CONSOLE && imports.AttachConsole) service_status.dwWaitHint += kill_console_delay;\r
+ if (stop_method & NSSM_STOP_METHOD_WINDOW) service_status.dwWaitHint += kill_window_delay;\r
+ if (stop_method & NSSM_STOP_METHOD_THREADS) service_status.dwWaitHint += kill_threads_delay;\r
SetServiceStatus(service_handle, &service_status);\r
}\r
\r
tree. See below for the possible values of the why argument.\r
*/\r
if (! why) {\r
- _snprintf_s(code, sizeof(code), _TRUNCATE, "%d", exitcode);\r
+ _snprintf_s(code, sizeof(code), _TRUNCATE, "%lu", exitcode);\r
log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, exe, service_name, code, 0);\r
}\r
\r
/* Clean up. */\r
+ if (exitcode == STILL_ACTIVE) exitcode = 0;\r
kill_process_tree(service_name, stop_method, pid, exitcode, pid, &creation_time, &exit_time);\r
\r
/*\r
/* Fake a crash so pre-Vista service managers will run recovery actions. */\r
case NSSM_EXIT_UNCLEAN:\r
log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service_name, code, exit_action_strings[action], 0);\r
- exit(stop_service(exitcode, false, default_action));\r
+ stop_service(exitcode, false, default_action);\r
+ free_imports();\r
+ exit(exitcode);\r
break;\r
}\r
}\r
if (throttle > 7) throttle = 8;\r
\r
char threshold[8], milliseconds[8];\r
- _snprintf_s(threshold, sizeof(threshold), _TRUNCATE, "%d", throttle_delay);\r
- _snprintf_s(milliseconds, sizeof(milliseconds), _TRUNCATE, "%d", ms);\r
+ _snprintf_s(threshold, sizeof(threshold), _TRUNCATE, "%lu", throttle_delay);\r
+ _snprintf_s(milliseconds, sizeof(milliseconds), _TRUNCATE, "%lu", ms);\r
log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service_name, threshold, milliseconds, 0);\r
\r
- if (throttle_timer) {\r
+ if (use_critical_section) EnterCriticalSection(&throttle_section);\r
+ else if (throttle_timer) {\r
ZeroMemory(&throttle_duetime, sizeof(throttle_duetime));\r
throttle_duetime.QuadPart = 0 - (ms * 10000LL);\r
SetWaitableTimer(throttle_timer, &throttle_duetime, 0, 0, 0, 0);\r
service_status.dwCurrentState = SERVICE_PAUSED;\r
SetServiceStatus(service_handle, &service_status);\r
\r
- if (throttle_timer) WaitForSingleObject(throttle_timer, INFINITE);\r
- else Sleep(ms);\r
+ if (use_critical_section) {\r
+ imports.SleepConditionVariableCS(&throttle_condition, &throttle_section, ms);\r
+ LeaveCriticalSection(&throttle_section);\r
+ }\r
+ else {\r
+ if (throttle_timer) WaitForSingleObject(throttle_timer, INFINITE);\r
+ else Sleep(ms);\r
+ }\r
}\r