Allow slow service startup again.
[nssm.git] / service.cpp
index 61d4b84..7e13da7 100644 (file)
@@ -689,6 +689,7 @@ void set_nssm_service_defaults(nssm_service_t *service) {
   service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;\r
   service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;\r
   service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;\r
+  service->kill_process_tree = 1;\r
 }\r
 \r
 /* Allocate and zero memory for a service. */\r
@@ -1362,7 +1363,7 @@ void WINAPI service_main(unsigned long argc, TCHAR **argv) {
   /* Initialise status */\r
   ZeroMemory(&service->status, sizeof(service->status));\r
   service->status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;\r
-  service->status.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;\r
+  service->status.dwControlsAccepted = SERVICE_ACCEPT_POWEREVENT | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;\r
   service->status.dwWin32ExitCode = NO_ERROR;\r
   service->status.dwServiceSpecificExitCode = 0;\r
   service->status.dwCheckPoint = 0;\r
@@ -1393,6 +1394,7 @@ void WINAPI service_main(unsigned long argc, TCHAR **argv) {
     if (services) {\r
       service->handle = open_service(services, service->name, SERVICE_CHANGE_CONFIG, 0, 0);\r
       set_service_recovery(service);\r
+\r
       CloseServiceHandle(services);\r
     }\r
   }\r
@@ -1460,6 +1462,7 @@ TCHAR *service_control_text(unsigned long control) {
     case SERVICE_CONTROL_CONTINUE: return _T("CONTINUE");\r
     case SERVICE_CONTROL_INTERROGATE: return _T("INTERROGATE");\r
     case NSSM_SERVICE_CONTROL_ROTATE: return _T("ROTATE");\r
+    case SERVICE_CONTROL_POWEREVENT: return _T("POWEREVENT");\r
     default: return 0;\r
   }\r
 }\r
@@ -1567,6 +1570,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;\r
       if (service->rotate_stderr_online == NSSM_ROTATE_ONLINE) service->rotate_stderr_online = NSSM_ROTATE_ONLINE_ASAP;\r
       return NO_ERROR;\r
+\r
+    case SERVICE_CONTROL_POWEREVENT:\r
+      if (event != PBT_APMRESUMEAUTOMATIC) {\r
+        log_service_control(service->name, control, false);\r
+        return NO_ERROR;\r
+      }\r
+      log_service_control(service->name, control, true);\r
+      end_service((void *) service, false);\r
+      return NO_ERROR;\r
   }\r
 \r
   /* Unknown control */\r
@@ -1676,16 +1688,7 @@ int start_service(nssm_service_t *service) {
     but be mindful of the fact that we are blocking the service control manager\r
     so abandon the wait before too much time has elapsed.\r
   */\r
-  unsigned long delay = service->throttle_delay;\r
-  if (delay > NSSM_SERVICE_STATUS_DEADLINE) {\r
-    TCHAR delay_milliseconds[16];\r
-    _sntprintf_s(delay_milliseconds, _countof(delay_milliseconds), _TRUNCATE, _T("%lu"), delay);\r
-    TCHAR deadline_milliseconds[16];\r
-    _sntprintf_s(deadline_milliseconds, _countof(deadline_milliseconds), _TRUNCATE, _T("%lu"), NSSM_SERVICE_STATUS_DEADLINE);\r
-    log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_STARTUP_DELAY_TOO_LONG, service->name, delay_milliseconds, NSSM, deadline_milliseconds, 0);\r
-    delay = NSSM_SERVICE_STATUS_DEADLINE;\r
-  }\r
-  unsigned long deadline = WaitForSingleObject(service->process_handle, delay);\r
+  await_startup(service);\r
 \r
   /* Signal successful start */\r
   service->status.dwCurrentState = SERVICE_RUNNING;\r
@@ -1790,7 +1793,7 @@ void CALLBACK end_service(void *arg, unsigned char why) {
 \r
   /* Clean up. */\r
   if (exitcode == STILL_ACTIVE) exitcode = 0;\r
-  if (service->pid) kill_process_tree(service, service->pid, exitcode, service->pid);\r
+  if (service->pid && service->kill_process_tree) kill_process_tree(service, service->pid, exitcode, service->pid);\r
   service->pid = 0;\r
 \r
   /*\r
@@ -1974,3 +1977,38 @@ awaited:
 \r
   return ret;\r
 }\r
+\r
+int await_startup(nssm_service_t *service) {\r
+  unsigned long interval;\r
+  unsigned long waithint;\r
+  unsigned long waited;\r
+\r
+  waithint = service->status.dwWaitHint;\r
+  waited = 0;\r
+  while (waited < service->throttle_delay) {\r
+    interval = service->throttle_delay - waited;\r
+    if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE;\r
+\r
+    service->status.dwCurrentState = SERVICE_START_PENDING;\r
+    service->status.dwWaitHint += interval;\r
+    service->status.dwCheckPoint++;\r
+    SetServiceStatus(service->status_handle, &service->status);\r
+\r
+    switch (WaitForSingleObject(service->process_handle, interval)) {\r
+      case WAIT_OBJECT_0:\r
+        return 1;\r
+\r
+      case WAIT_TIMEOUT:\r
+      break;\r
+\r
+      default:\r
+        return -1;\r
+    }\r
+\r
+    waited += interval;\r
+  }\r
+\r
+  service->throttle = 0;\r
+\r
+  return 0;\r
+}\r