Kill process tree when stopping service.
[nssm.git] / service.cpp
index 8c42972..4bcf8ab 100644 (file)
@@ -2,12 +2,14 @@
 \r
 SERVICE_STATUS service_status;\r
 SERVICE_STATUS_HANDLE service_handle;\r
+HANDLE process_handle;\r
 HANDLE wait_handle;\r
-HANDLE pid;\r
+unsigned long pid;\r
 static char service_name[SERVICE_NAME_LENGTH];\r
 char exe[EXE_LENGTH];\r
 char flags[CMD_LENGTH];\r
 char dir[MAX_PATH];\r
+bool stopping;\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
@@ -153,6 +155,7 @@ void WINAPI service_main(unsigned long argc, char **argv) {
   service_status.dwWaitHint = 1000;\r
 \r
   /* Signal we AREN'T running the server */\r
+  process_handle = 0;\r
   pid = 0;\r
 \r
   /* Register control handler */\r
@@ -213,7 +216,7 @@ int monitor_service() {
   log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, exe, flags, service_name, dir, 0);\r
 \r
   /* Monitor service service */\r
-  if (! RegisterWaitForSingleObject(&wait_handle, pid, end_service, 0, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {\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, GetLastError(), 0);\r
   }\r
 \r
@@ -235,7 +238,9 @@ unsigned long WINAPI service_control_handler(unsigned long control, unsigned lon
 \r
 /* Start the service */\r
 int start_service() {\r
-  if (pid) return 0;\r
+  stopping = false;\r
+\r
+  if (process_handle) return 0;\r
 \r
   /* Allocate a STARTUPINFO structure for a new process */\r
   STARTUPINFO si;\r
@@ -252,11 +257,12 @@ int start_service() {
     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "command line", "start_service", 0);\r
     return stop_service(2, true, true);\r
   }\r
-  if (! CreateProcess(0, cmd, 0, 0, 0, 0, 0, dir, &si, &pi)) {\r
+  if (! CreateProcess(0, cmd, 0, 0, false, 0, 0, dir, &si, &pi)) {\r
     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service_name, exe, GetLastError(), 0);\r
     return stop_service(3, true, true);\r
   }\r
-  pid = pi.hProcess;\r
+  process_handle = pi.hProcess;\r
+  pid = pi.dwProcessId;\r
 \r
   /* Signal successful start */\r
   service_status.dwCurrentState = SERVICE_RUNNING;\r
@@ -282,11 +288,13 @@ int stop_service(unsigned long exitcode, bool graceful, bool default_action) {
   if (pid) {\r
     /* Shut down server */\r
     log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service_name, exe, 0);\r
-    TerminateProcess(pid, 0);\r
-    pid = 0;\r
+    TerminateProcess(process_handle, 0);\r
+    process_handle = 0;\r
   }\r
   else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service_name, exe, 0);\r
 \r
+  end_service((void *) pid, true);\r
+\r
   /* Signal we stopped */\r
   if (graceful) {\r
     service_status.dwCurrentState = SERVICE_STOPPED;\r
@@ -306,19 +314,36 @@ int stop_service(unsigned long exitcode, bool graceful, bool default_action) {
 \r
 /* Callback function triggered when the server exits */\r
 void CALLBACK end_service(void *arg, unsigned char why) {\r
+  if (stopping) return;\r
+\r
+  stopping = true;\r
+\r
+  pid = (unsigned long) arg;\r
+\r
   /* Check exit code */\r
-  unsigned long ret = 0;\r
-  GetExitCodeProcess(pid, &ret);\r
+  unsigned long exitcode = 0;\r
+  GetExitCodeProcess(process_handle, &exitcode);\r
+\r
+  /* Clean up. */\r
+  kill_process_tree(service_name, pid, exitcode, pid);\r
+\r
+  /*\r
+    The why argument is true if our wait timed out or false otherwise.\r
+    Our wait is infinite so why will never be true when called by the system.\r
+    If it is indeed true, assume we were called from stop_service() because\r
+    this is a controlled shutdown, and don't take any restart action.\r
+  */\r
+  if (why) return;\r
 \r
   char code[16];\r
-  _snprintf(code, sizeof(code), "%d", ret);\r
+  _snprintf(code, sizeof(code), "%d", exitcode);\r
   log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, exe, service_name, code, 0);\r
 \r
   /* What action should we take? */\r
   int action = NSSM_EXIT_RESTART;\r
   unsigned char action_string[ACTION_LEN];\r
   bool default_action;\r
-  if (! get_exit_action(service_name, &ret, action_string, &default_action)) {\r
+  if (! get_exit_action(service_name, &exitcode, action_string, &default_action)) {\r
     for (int i = 0; exit_action_strings[i]; i++) {\r
       if (! _strnicmp((const char *) action_string, exit_action_strings[i], ACTION_LEN)) {\r
         action = i;\r
@@ -327,6 +352,7 @@ void CALLBACK end_service(void *arg, unsigned char why) {
     }\r
   }\r
 \r
+  process_handle = 0;\r
   pid = 0;\r
   switch (action) {\r
     /* Try to restart the service or return failure code to service manager */\r
@@ -347,13 +373,13 @@ void CALLBACK end_service(void *arg, unsigned char why) {
     /* Tell the service manager we are finished */\r
     case NSSM_EXIT_REALLY:\r
       log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service_name, code, exit_action_strings[action], 0);\r
-      stop_service(ret, true, default_action);\r
+      stop_service(exitcode, true, default_action);\r
     break;\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(ret, false, default_action));\r
+      exit(stop_service(exitcode, false, default_action));\r
     break;\r
   }\r
 }\r