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.
master
Iain Patterson 15 years ago
parent 37215c1032
commit b465216f3e

@ -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 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 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 Removing services using the GUI

@ -155,3 +155,13 @@ Language = English
Service %1 action for exit code %2 is %3. Service %1 action for exit code %2 is %3.
Exiting. 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.
.

@ -146,7 +146,10 @@ int get_parameters(char *service_name, char *exe, int exelen, char *flags, int f
return 0; 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 */ /* Get registry */
char registry[KEY_LENGTH]; char registry[KEY_LENGTH];
if (_snprintf(registry, sizeof(registry), NSSM_REGISTRY "\\%s", service_name, NSSM_REG_EXIT) < 0) { 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'; if (! ret) code[0] = '\0';
else if (_snprintf(code, sizeof(code), "%lu", *ret) < 0) { else if (_snprintf(code, sizeof(code), "%lu", *ret) < 0) {
RegCloseKey(key); 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) { if (RegQueryValueEx(key, code, 0, &type, action, &action_len) != ERROR_SUCCESS) {
RegCloseKey(key); RegCloseKey(key);
/* Try again with * as the key if an exit code was defined */ /* 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; return 0;
} }

@ -11,6 +11,6 @@ int create_messages();
int create_parameters(char *, char *, char *, char *); int create_parameters(char *, char *, char *, char *);
int create_exit_action(char *, const char *); int create_exit_action(char *, const char *);
int get_parameters(char *, char *, int, char *, int, char *, int); 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 #endif

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

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

Loading…
Cancel
Save