Browse Source

Don't suicide on exit status 0.

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.
Iain Patterson 12 years ago
parent
commit
b465216f3e
6 changed files with 35 additions and 13 deletions
  1. 4 1
      README.txt
  2. 10 0
      messages.mc
  3. 6 3
      registry.cpp
  4. 1 1
      registry.h
  5. 13 7
      service.cpp
  6. 1 1
      service.h

+ 4 - 1
README.txt

@@ -97,7 +97,10 @@ of Windows you should use "Suicide" instead.
 
 If the value data is "Suicide" NSSM will simulate a crash and exit without
 informing the service manager.  This option should only be used for
-pre-Vista systems where you wish to apply a service recovery action.
+pre-Vista systems where you wish to apply a service recovery action.  Note
+that if the monitored application exits with code 0, NSSM will only honour a
+request to suicide if you explicitly configure a registry key for exit code 0.
+If only the default action is set to Suicide NSSM will instead exit gracefully.
 
 
 Removing services using the GUI

+ 10 - 0
messages.mc

@@ -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.
+.
+

+ 6 - 3
registry.cpp

@@ -146,7 +146,10 @@ int get_parameters(char *service_name, char *exe, int exelen, char *flags, int f
   return 0;
 }
 
-int get_exit_action(char *service_name, unsigned long *ret, unsigned char *action) {
+int get_exit_action(char *service_name, unsigned long *ret, unsigned char *action, bool *default_action) {
+  /* Are we returning the default action or a status-specific one? */
+  *default_action = ! ret;
+
   /* Get registry */
   char registry[KEY_LENGTH];
   if (_snprintf(registry, sizeof(registry), NSSM_REGISTRY "\\%s", service_name, NSSM_REG_EXIT) < 0) {
@@ -169,12 +172,12 @@ int get_exit_action(char *service_name, unsigned long *ret, unsigned char *actio
   if (! ret) code[0] = '\0';
   else if (_snprintf(code, sizeof(code), "%lu", *ret) < 0) {
     RegCloseKey(key);
-    return get_exit_action(service_name, 0, action);
+    return get_exit_action(service_name, 0, action, default_action);
   }
   if (RegQueryValueEx(key, code, 0, &type, action, &action_len) != ERROR_SUCCESS) {
     RegCloseKey(key);
     /* Try again with * as the key if an exit code was defined */
-    if (ret) return get_exit_action(service_name, 0, action);
+    if (ret) return get_exit_action(service_name, 0, action, default_action);
     return 0;
   }
 

+ 1 - 1
registry.h

@@ -11,6 +11,6 @@ int create_messages();
 int create_parameters(char *, char *, char *, char *);
 int create_exit_action(char *, const char *);
 int get_parameters(char *, char *, int, char *, int, char *, int);
-int get_exit_action(char *, unsigned long *, unsigned char *);
+int get_exit_action(char *, unsigned long *, unsigned char *, bool *);
 
 #endif

+ 13 - 7
service.cpp

@@ -225,7 +225,7 @@ unsigned long WINAPI service_control_handler(unsigned long control, unsigned lon
   switch (control) {
     case SERVICE_CONTROL_SHUTDOWN:
     case SERVICE_CONTROL_STOP:
-      stop_service(0, true);
+      stop_service(0, true, true);
       return NO_ERROR;
   }
 
@@ -250,11 +250,11 @@ int start_service() {
   char cmd[CMD_LENGTH];
   if (_snprintf(cmd, sizeof(cmd), "%s %s", exe, flags) < 0) {
     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "command line", "start_service", 0);
-    return stop_service(2, true);
+    return stop_service(2, true, true);
   }
   if (! CreateProcess(0, cmd, 0, 0, 0, 0, 0, dir, &si, &pi)) {
     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service_name, exe, GetLastError(), 0);
-    return stop_service(3, true);
+    return stop_service(3, true, true);
   }
   pid = pi.hProcess;
 
@@ -266,7 +266,12 @@ int start_service() {
 }
 
 /* Stop the service */
-int stop_service(unsigned long exitcode, bool graceful) {
+int stop_service(unsigned long exitcode, bool graceful, bool default_action) {
+  if (default_action && ! exitcode && ! graceful) {
+    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);
+    graceful = true;
+  }
+
   /* Signal we are stopping */
   if (graceful) {
     service_status.dwCurrentState = SERVICE_STOP_PENDING;
@@ -312,7 +317,8 @@ void CALLBACK end_service(void *arg, unsigned char why) {
   /* What action should we take? */
   int action = NSSM_EXIT_RESTART;
   unsigned char action_string[ACTION_LEN];
-  if (! get_exit_action(service_name, &ret, action_string)) {
+  bool default_action;
+  if (! get_exit_action(service_name, &ret, action_string, &default_action)) {
     for (int i = 0; exit_action_strings[i]; i++) {
       if (! _strnicmp((const char *) action_string, exit_action_strings[i], ACTION_LEN)) {
         action = i;
@@ -341,13 +347,13 @@ void CALLBACK end_service(void *arg, unsigned char why) {
     /* Tell the service manager we are finished */
     case NSSM_EXIT_REALLY:
       log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service_name, code, exit_action_strings[action], 0);
-      stop_service(ret, true);
+      stop_service(ret, true, default_action);
     break;
 
     /* Fake a crash so pre-Vista service managers will run recovery actions. */
     case NSSM_EXIT_UNCLEAN:
       log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service_name, code, exit_action_strings[action], 0);
-      exit(stop_service(ret, false));
+      exit(stop_service(ret, false, default_action));
     break;
   }
 }

+ 1 - 1
service.h

@@ -14,7 +14,7 @@ int remove_service(char *);
 void set_service_recovery(char *);
 int monitor_service();
 int start_service();
-int stop_service(unsigned long, bool);
+int stop_service(unsigned long, bool, bool);
 void CALLBACK end_service(void *, unsigned char);
 
 #endif