Don't suicide on exit status 0.
authorIain Patterson <me@iain.cx>
Sat, 25 Sep 2010 13:37:28 +0000 (14:37 +0100)
committerIain Patterson <me@iain.cx>
Sat, 25 Sep 2010 13:37:28 +0000 (14:37 +0100)
Suiciding when the application exits 0 will cause recovery actions to be
taken.  Usually this is inappropriate.  Only suicide if there is an
explicit AppExit value for 0 in the registry.

Technically such behaviour could be abused to do something like run a
script after successful completion of a service but in most cases a
suicide is undesirable when no actual failure occurred.

README.txt
messages.mc
registry.cpp
registry.h
service.cpp
service.h

index 022e5ed..d9e6ee0 100644 (file)
@@ -97,7 +97,10 @@ of Windows you should use "Suicide" instead.
 \r
 If the value data is "Suicide" NSSM will simulate a crash and exit without\r
 informing the service manager.  This option should only be used for\r
 \r
 If the value data is "Suicide" NSSM will simulate a crash and exit without\r
 informing the service manager.  This option should only be used for\r
-pre-Vista systems where you wish to apply a service recovery action.\r
+pre-Vista systems where you wish to apply a service recovery action.  Note\r
+that if the monitored application exits with code 0, NSSM will only honour a\r
+request to suicide if you explicitly configure a registry key for exit code 0.\r
+If only the default action is set to Suicide NSSM will instead exit gracefully.\r
 \r
 \r
 Removing services using the GUI\r
 \r
 \r
 Removing services using the GUI\r
index 4363171..6048077 100644 (file)
@@ -155,3 +155,13 @@ Language = English
 Service %1 action for exit code %2 is %3.
 Exiting.
 .
 Service %1 action for exit code %2 is %3.
 Exiting.
 .
+
+MessageId = +1
+SymbolicName = NSSM_EVENT_GRACEFUL_SUICIDE
+Severity = Informational
+Language = English
+Service %1 application %2 exited with exit code 0 but the default exit action is %3.
+Honouring the %4 action would result in the service being flagged as failed and subject to recovery actions.
+The service will instead be stopped gracefully.  To suppress this message, explicitly configure the exit action for exit code 0 to either %5 or %6.
+.
+
index 5dbadd2..ed01303 100644 (file)
@@ -146,7 +146,10 @@ int get_parameters(char *service_name, char *exe, int exelen, char *flags, int f
   return 0;\r
 }\r
 \r
   return 0;\r
 }\r
 \r
-int get_exit_action(char *service_name, unsigned long *ret, unsigned char *action) {\r
+int get_exit_action(char *service_name, unsigned long *ret, unsigned char *action, bool *default_action) {\r
+  /* Are we returning the default action or a status-specific one? */\r
+  *default_action = ! ret;\r
+\r
   /* Get registry */\r
   char registry[KEY_LENGTH];\r
   if (_snprintf(registry, sizeof(registry), NSSM_REGISTRY "\\%s", service_name, NSSM_REG_EXIT) < 0) {\r
   /* Get registry */\r
   char registry[KEY_LENGTH];\r
   if (_snprintf(registry, sizeof(registry), NSSM_REGISTRY "\\%s", service_name, NSSM_REG_EXIT) < 0) {\r
@@ -169,12 +172,12 @@ int get_exit_action(char *service_name, unsigned long *ret, unsigned char *actio
   if (! ret) code[0] = '\0';\r
   else if (_snprintf(code, sizeof(code), "%lu", *ret) < 0) {\r
     RegCloseKey(key);\r
   if (! ret) code[0] = '\0';\r
   else if (_snprintf(code, sizeof(code), "%lu", *ret) < 0) {\r
     RegCloseKey(key);\r
-    return get_exit_action(service_name, 0, action);\r
+    return get_exit_action(service_name, 0, action, default_action);\r
   }\r
   if (RegQueryValueEx(key, code, 0, &type, action, &action_len) != ERROR_SUCCESS) {\r
     RegCloseKey(key);\r
     /* Try again with * as the key if an exit code was defined */\r
   }\r
   if (RegQueryValueEx(key, code, 0, &type, action, &action_len) != ERROR_SUCCESS) {\r
     RegCloseKey(key);\r
     /* Try again with * as the key if an exit code was defined */\r
-    if (ret) return get_exit_action(service_name, 0, action);\r
+    if (ret) return get_exit_action(service_name, 0, action, default_action);\r
     return 0;\r
   }\r
 \r
     return 0;\r
   }\r
 \r
index 2f45f99..901a6ba 100644 (file)
@@ -11,6 +11,6 @@ int create_messages();
 int create_parameters(char *, char *, char *, char *);\r
 int create_exit_action(char *, const char *);\r
 int get_parameters(char *, char *, int, char *, int, char *, int);\r
 int create_parameters(char *, char *, char *, char *);\r
 int create_exit_action(char *, const char *);\r
 int get_parameters(char *, char *, int, char *, int, char *, int);\r
-int get_exit_action(char *, unsigned long *, unsigned char *);\r
+int get_exit_action(char *, unsigned long *, unsigned char *, bool *);\r
 \r
 #endif\r
 \r
 #endif\r
index ccc8e89..8c42972 100644 (file)
@@ -225,7 +225,7 @@ unsigned long WINAPI service_control_handler(unsigned long control, unsigned lon
   switch (control) {\r
     case SERVICE_CONTROL_SHUTDOWN:\r
     case SERVICE_CONTROL_STOP:\r
   switch (control) {\r
     case SERVICE_CONTROL_SHUTDOWN:\r
     case SERVICE_CONTROL_STOP:\r
-      stop_service(0, true);\r
+      stop_service(0, true, true);\r
       return NO_ERROR;\r
   }\r
 \r
       return NO_ERROR;\r
   }\r
 \r
@@ -250,11 +250,11 @@ int start_service() {
   char cmd[CMD_LENGTH];\r
   if (_snprintf(cmd, sizeof(cmd), "%s %s", exe, flags) < 0) {\r
     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "command line", "start_service", 0);\r
   char cmd[CMD_LENGTH];\r
   if (_snprintf(cmd, sizeof(cmd), "%s %s", exe, flags) < 0) {\r
     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "command line", "start_service", 0);\r
-    return stop_service(2, true);\r
+    return stop_service(2, true, true);\r
   }\r
   if (! CreateProcess(0, cmd, 0, 0, 0, 0, 0, dir, &si, &pi)) {\r
     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service_name, exe, GetLastError(), 0);\r
   }\r
   if (! CreateProcess(0, cmd, 0, 0, 0, 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);\r
+    return stop_service(3, true, true);\r
   }\r
   pid = pi.hProcess;\r
 \r
   }\r
   pid = pi.hProcess;\r
 \r
@@ -266,7 +266,12 @@ int start_service() {
 }\r
 \r
 /* Stop the service */\r
 }\r
 \r
 /* Stop the service */\r
-int stop_service(unsigned long exitcode, bool graceful) {\r
+int stop_service(unsigned long exitcode, bool graceful, bool default_action) {\r
+  if (default_action && ! exitcode && ! graceful) {\r
+    log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_GRACEFUL_SUICIDE, service_name, exe, exit_action_strings[NSSM_EXIT_UNCLEAN], exit_action_strings[NSSM_EXIT_UNCLEAN], exit_action_strings[NSSM_EXIT_UNCLEAN], exit_action_strings[NSSM_EXIT_REALLY] ,0);\r
+    graceful = true;\r
+  }\r
+\r
   /* Signal we are stopping */\r
   if (graceful) {\r
     service_status.dwCurrentState = SERVICE_STOP_PENDING;\r
   /* Signal we are stopping */\r
   if (graceful) {\r
     service_status.dwCurrentState = SERVICE_STOP_PENDING;\r
@@ -312,7 +317,8 @@ void CALLBACK end_service(void *arg, unsigned char why) {
   /* What action should we take? */\r
   int action = NSSM_EXIT_RESTART;\r
   unsigned char action_string[ACTION_LEN];\r
   /* What action should we take? */\r
   int action = NSSM_EXIT_RESTART;\r
   unsigned char action_string[ACTION_LEN];\r
-  if (! get_exit_action(service_name, &ret, action_string)) {\r
+  bool default_action;\r
+  if (! get_exit_action(service_name, &ret, 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
     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
@@ -341,13 +347,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
     /* 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);\r
+      stop_service(ret, 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
     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));\r
+      exit(stop_service(ret, false, default_action));\r
     break;\r
   }\r
 }\r
     break;\r
   }\r
 }\r
index 75d97ff..fb52c16 100644 (file)
--- a/service.h
+++ b/service.h
@@ -14,7 +14,7 @@ int remove_service(char *);
 void set_service_recovery(char *);\r
 int monitor_service();\r
 int start_service();\r
 void set_service_recovery(char *);\r
 int monitor_service();\r
 int start_service();\r
-int stop_service(unsigned long, bool);\r
+int stop_service(unsigned long, bool, bool);\r
 void CALLBACK end_service(void *, unsigned char);\r
 \r
 #endif\r
 void CALLBACK end_service(void *, unsigned char);\r
 \r
 #endif\r