Use override_milliseconds() to find throttle restart delay.
[nssm.git] / service.cpp
index f82b9bd..596aa7e 100644 (file)
@@ -14,10 +14,15 @@ bool stopping;
 bool allow_restart;\r
 unsigned long throttle_delay;\r
 unsigned long stop_method;\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
@@ -184,6 +189,10 @@ void WINAPI service_main(unsigned long argc, char **argv) {
     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
@@ -218,9 +227,12 @@ void WINAPI service_main(unsigned long argc, char **argv) {
   }\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
@@ -268,7 +280,7 @@ int monitor_service() {
   }\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
@@ -297,11 +309,11 @@ void log_service_control(char *service_name, unsigned long control, bool handled
     /* "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
@@ -333,10 +345,13 @@ unsigned long WINAPI service_control_handler(unsigned long control, unsigned lon
 \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
@@ -430,7 +445,10 @@ int stop_service(unsigned long exitcode, bool graceful, bool default_action) {
   /* 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 += NSSM_KILL_CONSOLE_GRACE_PERIOD;\r
+    if (stop_method & NSSM_STOP_METHOD_WINDOW) service_status.dwWaitHint += NSSM_KILL_WINDOW_GRACE_PERIOD;\r
+    if (stop_method & NSSM_STOP_METHOD_THREADS) service_status.dwWaitHint += NSSM_KILL_THREADS_GRACE_PERIOD;\r
     SetServiceStatus(service_handle, &service_status);\r
   }\r
 \r
@@ -482,11 +500,12 @@ void CALLBACK end_service(void *arg, unsigned char why) {
     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
@@ -538,7 +557,9 @@ void CALLBACK end_service(void *arg, unsigned char why) {
     /* 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
@@ -552,11 +573,12 @@ void throttle_restart() {
   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
@@ -565,6 +587,12 @@ void throttle_restart() {
   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