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
-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
index 4363171..6048077 100644 (file)
@@ -155,3 +155,13 @@ Language = English
 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
-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
@@ -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
-    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
-    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
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 get_exit_action(char *, unsigned long *, unsigned char *);\r
+int get_exit_action(char *, unsigned long *, unsigned char *, bool *);\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
-      stop_service(0, true);\r
+      stop_service(0, true, true);\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
-    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
-    return stop_service(3, true);\r
+    return stop_service(3, true, true);\r
   }\r
   pid = pi.hProcess;\r
 \r
@@ -266,7 +266,12 @@ int start_service() {
 }\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
@@ -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
-  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
@@ -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
-      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
-      exit(stop_service(ret, false));\r
+      exit(stop_service(ret, false, default_action));\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
-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