Fixed permissions check in open_registry().
[nssm.git] / service.cpp
index 406196b..5a28641 100644 (file)
@@ -38,6 +38,7 @@ static inline int service_control_response(unsigned long control, unsigned long
     case SERVICE_CONTROL_STOP:\r
     case SERVICE_CONTROL_SHUTDOWN:\r
       switch (status) {\r
+        case SERVICE_RUNNING:\r
         case SERVICE_STOP_PENDING:\r
           return 1;\r
 \r
@@ -73,6 +74,7 @@ static inline int service_control_response(unsigned long control, unsigned long
       }\r
 \r
     case SERVICE_CONTROL_INTERROGATE:\r
+    case NSSM_SERVICE_CONTROL_ROTATE:\r
       return 0;\r
   }\r
 \r
@@ -81,12 +83,17 @@ static inline int service_control_response(unsigned long control, unsigned long
 \r
 static inline int await_service_control_response(unsigned long control, SC_HANDLE service_handle, SERVICE_STATUS *service_status, unsigned long initial_status) {\r
   int tries = 0;\r
+  unsigned long checkpoint = 0;\r
+  unsigned long waithint = 0;\r
   while (QueryServiceStatus(service_handle, service_status)) {\r
     int response = service_control_response(control, service_status->dwCurrentState);\r
     /* Alas we can't WaitForSingleObject() on an SC_HANDLE. */\r
     if (! response) return response;\r
     if (response > 0 || service_status->dwCurrentState == initial_status) {\r
-      if (++tries > 10) return response;\r
+      if (service_status->dwCheckPoint != checkpoint || service_status->dwWaitHint != waithint) tries = 0;\r
+      checkpoint = service_status->dwCheckPoint;\r
+      waithint = service_status->dwWaitHint;\r
+      if (++tries > 10) tries = 10;\r
       Sleep(50 * tries);\r
     }\r
     else return response;\r
@@ -1363,7 +1370,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_POWEREVENT | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;\r
+  service->status.dwControlsAccepted = 0;\r
   service->status.dwWin32ExitCode = NO_ERROR;\r
   service->status.dwServiceSpecificExitCode = 0;\r
   service->status.dwCheckPoint = 0;\r
@@ -1688,10 +1695,13 @@ 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
-  if (await_service_status_change(service, SERVICE_START_PENDING, _T("start_service"), service->throttle_delay) == 1) service->throttle = 0;\r
+  service->status.dwCurrentState = SERVICE_START_PENDING;\r
+  service->status.dwControlsAccepted = SERVICE_ACCEPT_POWEREVENT | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP;\r
+  if (await_single_handle(service->status_handle, &service->status, service->process_handle, service->name, _T("start_service"), service->throttle_delay) == 1) service->throttle = 0;\r
 \r
   /* Signal successful start */\r
   service->status.dwCurrentState = SERVICE_RUNNING;\r
+  service->status.dwControlsAccepted &= ~SERVICE_ACCEPT_PAUSE_CONTINUE;\r
   SetServiceStatus(service->status_handle, &service->status);\r
 \r
   /* Continue waiting for a clean startup. */\r
@@ -1727,14 +1737,18 @@ int stop_service(nssm_service_t *service, unsigned long exitcode, bool graceful,
   if (graceful) {\r
     service->status.dwCurrentState = SERVICE_STOP_PENDING;\r
     service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;\r
-    SetServiceStatus(service->status_handle, &service->status);\r
   }\r
+  service->status.dwControlsAccepted = 0;\r
+  SetServiceStatus(service->status_handle, &service->status);\r
 \r
   /* Nothing to do if service isn't running */\r
   if (service->pid) {\r
     /* Shut down service */\r
     log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service->name, service->exe, 0);\r
-    kill_process(service, service->process_handle, service->pid, 0);\r
+    kill_t k;\r
+    service_kill_t(service, &k);\r
+    k.exitcode = 0;\r
+    kill_process(&k);\r
   }\r
   else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service->name, service->exe, 0);\r
 \r
@@ -1793,7 +1807,11 @@ void CALLBACK end_service(void *arg, unsigned char why) {
 \r
   /* Clean up. */\r
   if (exitcode == STILL_ACTIVE) exitcode = 0;\r
-  if (service->pid && service->kill_process_tree) kill_process_tree(service, service->pid, exitcode, service->pid);\r
+  if (service->pid && service->kill_process_tree) {\r
+    kill_t k;\r
+    service_kill_t(service, &k);\r
+    kill_process_tree(&k, service->pid);\r
+  }\r
   service->pid = 0;\r
 \r
   /*\r
@@ -1879,6 +1897,7 @@ void throttle_restart(nssm_service_t *service) {
   }\r
 \r
   service->status.dwCurrentState = SERVICE_PAUSED;\r
+  service->status.dwControlsAccepted |= SERVICE_ACCEPT_PAUSE_CONTINUE;\r
   SetServiceStatus(service->status_handle, &service->status);\r
 \r
   if (use_critical_section) {\r
@@ -1915,13 +1934,16 @@ void throttle_restart(nssm_service_t *service) {
 \r
   Only doing both these things will prevent the system from killing the service.\r
 \r
+  If the status_handle and service_status arguments are omitted, this function\r
+  will not try to update the service manager but it will still log to the\r
+  event log that it is waiting for a handle.\r
+\r
   Returns: 1 if the wait timed out.\r
            0 if the wait completed.\r
           -1 on error.\r
 */\r
-int await_service_status_change(nssm_service_t *service, unsigned long status, unsigned long desired, TCHAR *function_name, unsigned long timeout) {\r
+int await_single_handle(SERVICE_STATUS_HANDLE status_handle, SERVICE_STATUS *status, HANDLE handle, TCHAR *name, TCHAR *function_name, unsigned long timeout) {\r
   unsigned long interval;\r
-  unsigned long waithint;\r
   unsigned long ret;\r
   unsigned long waited;\r
   TCHAR interval_milliseconds[16];\r
@@ -1938,24 +1960,24 @@ int await_service_status_change(nssm_service_t *service, unsigned long status, u
 \r
   _sntprintf_s(timeout_milliseconds, _countof(timeout_milliseconds), _TRUNCATE, _T("%lu"), timeout);\r
 \r
-  waithint = service->status.dwWaitHint;\r
   waited = 0;\r
   while (waited < timeout) {\r
     interval = timeout - waited;\r
     if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE;\r
 \r
-    service->status.dwCurrentState = control;\r
-    service->status.dwWaitHint += interval;\r
-    service->status.dwCheckPoint++;\r
-    SetServiceStatus(service->status_handle, &service->status);\r
+    if (status) {\r
+      status->dwWaitHint += interval;\r
+      status->dwCheckPoint++;\r
+      SetServiceStatus(status_handle, status);\r
+    }\r
 \r
     if (waited) {\r
       _sntprintf_s(waited_milliseconds, _countof(waited_milliseconds), _TRUNCATE, _T("%lu"), waited);\r
       _sntprintf_s(interval_milliseconds, _countof(interval_milliseconds), _TRUNCATE, _T("%lu"), interval);\r
-      log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SERVICE, function, service->name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, service_status_text(status), 0);\r
+      log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SINGLE_HANDLE, function, name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0);\r
     }\r
 \r
-    switch (WaitForSingleObject(service->process_handle, interval)) {\r
+    switch (WaitForSingleObject(handle, interval)) {\r
       case WAIT_OBJECT_0:\r
         ret = 0;\r
         goto awaited;\r