* NSSM can now set the priority class and processor
affinity of the managed application.
+ * NSSM can now apply an unconditional delay before
+ restarting the application.
+
* NSSM can now optionally rotate existing files when
redirecting I/O.
Since version 2.22, NSSM can set the managed application's process priority\r
and CPU affinity.\r
\r
+Since version 2.22, NSSM can apply an unconditional delay before restarting\r
+an application which has exited.\r
+\r
Since version 2.22, NSSM can rotate existing output files when redirecting I/O.\r
\r
Since version 2.22, NSSM can set service display name, description, startup\r
milliseconds as a REG_DWORD value in the registry at\r
HKLM\SYSTEM\CurrentControlSet\Services\<service>\Parameters\AppThrottle.\r
\r
+Alternatively, NSSM can pause for a configurable amount of time before\r
+attempting to restart the application even if it successfully ran for the\r
+amount of time specified by AppThrottle. NSSM will consult the REG_DWORD value\r
+at HKLM\SYSTEM\CurrentControlSet\Services\<service>\Parameters\AppRestartDelay\r
+for the number of milliseconds to wait before attempting a restart. If\r
+AppRestartDelay is set and the application is determined to be subject to\r
+throttling, NSSM will pause the service for whichever is longer of the\r
+configured restart delay and the calculated throttle period.\r
+\r
+If AppRestartDelay is missing or invalid, only throttling will be applied.\r
+\r
NSSM will look in the registry under\r
HKLM\SYSTEM\CurrentControlSet\Services\<service>\Parameters\AppExit for\r
string (REG_EXPAND_SZ) values corresponding to the exit code of the application.\r
Thanks to Арслан Сайдуганов for suggesting setting process priority.\r
Thanks to Robert Middleton for suggestion and draft implementation of process\r
affinity support.\r
+Thanks to Andrew RedzMax for suggesting an unconditional restart delay.\r
\r
Licence\r
-------\r
SetDlgItemInt(tablist[NSSM_TAB_EXIT], IDC_THROTTLE, service->throttle_delay, 0);\r
combo = GetDlgItem(tablist[NSSM_TAB_EXIT], IDC_APPEXIT);\r
SendMessage(combo, CB_SETCURSEL, service->default_exit_action, 0);\r
+ SetDlgItemInt(tablist[NSSM_TAB_EXIT], IDC_RESTART_DELAY, service->restart_delay, 0);\r
\r
/* I/O tab. */\r
SetDlgItemText(tablist[NSSM_TAB_IO], IDC_STDIN, service->stdin_path);\r
combo = GetDlgItem(tablist[NSSM_TAB_EXIT], IDC_APPEXIT);\r
service->default_exit_action = (unsigned long) SendMessage(combo, CB_GETCURSEL, 0, 0);\r
if (service->default_exit_action == CB_ERR) service->default_exit_action = 0;\r
+ check_number(tablist[NSSM_TAB_EXIT], IDC_RESTART_DELAY, &service->restart_delay);\r
\r
/* Get I/O stuff. */\r
check_io(window, _T("stdin"), service->stdin_path, _countof(service->stdin_path), IDC_STDIN);\r
SendMessage(combo, CB_INSERTSTRING, NSSM_EXIT_REALLY, (LPARAM) message_string(NSSM_GUI_EXIT_REALLY));\r
SendMessage(combo, CB_INSERTSTRING, NSSM_EXIT_UNCLEAN, (LPARAM) message_string(NSSM_GUI_EXIT_UNCLEAN));\r
SendMessage(combo, CB_SETCURSEL, NSSM_EXIT_RESTART, 0);\r
+ SetDlgItemInt(tablist[NSSM_TAB_EXIT], IDC_RESTART_DELAY, 0, 0);\r
\r
/* I/O tab. */\r
tab.pszText = message_string(NSSM_GUI_TAB_IO);\r
if (stop_method_skip) set_number(key, NSSM_REG_STOP_METHOD_SKIP, stop_method_skip);\r
else if (editing) RegDeleteValue(key, NSSM_REG_STOP_METHOD_SKIP);\r
if (service->default_exit_action < NSSM_NUM_EXIT_ACTIONS) create_exit_action(service->name, exit_action_strings[service->default_exit_action], editing);\r
+ if (service->restart_delay) set_number(key, NSSM_REG_RESTART_DELAY, service->restart_delay);\r
+ else if (editing) RegDeleteValue(key, NSSM_REG_RESTART_DELAY);\r
if (service->throttle_delay != NSSM_RESET_THROTTLE_RESTART) set_number(key, NSSM_REG_THROTTLE, service->throttle_delay);\r
else if (editing) RegDeleteValue(key, NSSM_REG_THROTTLE);\r
if (service->kill_console_delay != NSSM_KILL_CONSOLE_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, service->kill_console_delay);\r
/* Change back in case the startup directory needs to be deleted. */\r
SetCurrentDirectory(cwd);\r
\r
+ /* Try to get mandatory restart delay */\r
+ override_milliseconds(service->name, key, NSSM_REG_RESTART_DELAY, &service->restart_delay, 0, NSSM_EVENT_BOGUS_RESTART_DELAY);\r
+\r
/* Try to get throttle restart delay */\r
override_milliseconds(service->name, key, NSSM_REG_THROTTLE, &service->throttle_delay, NSSM_RESET_THROTTLE_RESTART, NSSM_EVENT_BOGUS_THROTTLE);\r
\r
#define NSSM_REG_ENV _T("AppEnvironment")\r
#define NSSM_REG_ENV_EXTRA _T("AppEnvironmentExtra")\r
#define NSSM_REG_EXIT _T("AppExit")\r
+#define NSSM_REG_RESTART_DELAY _T("AppRestartDelay")\r
#define NSSM_REG_THROTTLE _T("AppThrottle")\r
#define NSSM_REG_STOP_METHOD_SKIP _T("AppStopMethodSkip")\r
#define NSSM_REG_KILL_CONSOLE_GRACE_PERIOD _T("AppStopMethodConsole")\r
#define IDC_BROWSE_STDERR 1020\r
#define IDC_THROTTLE 1021\r
#define IDC_APPEXIT 1022\r
-#define IDC_DIR 1023\r
-#define IDC_BROWSE_DIR 1024\r
-#define IDC_ENVIRONMENT 1025\r
-#define IDC_ENVIRONMENT_REPLACE 1026\r
-#define IDC_TRUNCATE 1027\r
-#define IDC_ROTATE 1028\r
-#define IDC_ROTATE_SECONDS 1029\r
-#define IDC_ROTATE_BYTES_LOW 1030\r
-#define IDC_DISPLAYNAME 1031\r
-#define IDC_DESCRIPTION 1032\r
-#define IDC_STARTUP 1033\r
-#define IDC_LOCALSYSTEM 1034\r
-#define IDC_INTERACT 1035\r
-#define IDC_ACCOUNT 1036\r
-#define IDC_USERNAME 1037\r
-#define IDC_PASSWORD1 1038\r
-#define IDC_PASSWORD2 1039\r
-#define IDC_PRIORITY 1040\r
-#define IDC_AFFINITY_ALL 1041\r
-#define IDC_AFFINITY 1042\r
+#define IDC_RESTART_DELAY 1023\r
+#define IDC_DIR 1024\r
+#define IDC_BROWSE_DIR 1025\r
+#define IDC_ENVIRONMENT 1026\r
+#define IDC_ENVIRONMENT_REPLACE 1027\r
+#define IDC_TRUNCATE 1028\r
+#define IDC_ROTATE 1029\r
+#define IDC_ROTATE_SECONDS 1030\r
+#define IDC_ROTATE_BYTES_LOW 1031\r
+#define IDC_DISPLAYNAME 1032\r
+#define IDC_DESCRIPTION 1033\r
+#define IDC_STARTUP 1034\r
+#define IDC_LOCALSYSTEM 1035\r
+#define IDC_INTERACT 1036\r
+#define IDC_ACCOUNT 1037\r
+#define IDC_USERNAME 1038\r
+#define IDC_PASSWORD1 1039\r
+#define IDC_PASSWORD2 1040\r
+#define IDC_PRIORITY 1041\r
+#define IDC_AFFINITY_ALL 1042\r
+#define IDC_AFFINITY 1043\r
\r
// Next default values for new objects\r
// \r
#ifndef APSTUDIO_READONLY_SYMBOLS\r
#define _APS_NEXT_RESOURCE_VALUE 115\r
#define _APS_NEXT_COMMAND_VALUE 40001\r
-#define _APS_NEXT_CONTROL_VALUE 1043\r
+#define _APS_NEXT_CONTROL_VALUE 1044\r
#define _APS_NEXT_SYMED_VALUE 101\r
#endif\r
#endif\r
return NORMAL_PRIORITY_CLASS;\r
}\r
\r
-static inline int throttle_milliseconds(unsigned long throttle) {\r
+static inline unsigned long throttle_milliseconds(unsigned long throttle) {\r
/* pow() operates on doubles. */\r
- int ret = 1; for (unsigned long i = 1; i < throttle; i++) ret *= 2;\r
+ unsigned long ret = 1; for (unsigned long i = 1; i < throttle; i++) ret *= 2;\r
return ret * 1000;\r
}\r
\r
else service->throttle = 0;\r
}\r
\r
+ /* Ensure the restart delay is always applied. */\r
+ if (service->restart_delay && ! service->throttle) service->throttle++;\r
+\r
return 0;\r
}\r
\r
/* This can't be a restart if the service is already running. */\r
if (! service->throttle++) return;\r
\r
- int ms = throttle_milliseconds(service->throttle);\r
+ unsigned long ms;\r
+ unsigned long throttle_ms = throttle_milliseconds(service->throttle);\r
+ TCHAR threshold[8], milliseconds[8];\r
+\r
+ if (service->restart_delay > throttle_ms) ms = service->restart_delay;\r
+ else ms = throttle_ms;\r
\r
if (service->throttle > 7) service->throttle = 8;\r
\r
- TCHAR threshold[8], milliseconds[8];\r
- _sntprintf_s(threshold, _countof(threshold), _TRUNCATE, _T("%lu"), service->throttle_delay);\r
_sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), ms);\r
- log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service->name, threshold, milliseconds, 0);\r
+\r
+ if (service->throttle == 1 && service->restart_delay > throttle_ms) log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESTART_DELAY, service->name, milliseconds, 0);\r
+ else {\r
+ _sntprintf_s(threshold, _countof(threshold), _TRUNCATE, _T("%lu"), service->throttle_delay);\r
+ log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service->name, threshold, milliseconds, 0);\r
+ }\r
\r
if (use_critical_section) EnterCriticalSection(&service->throttle_section);\r
else if (service->throttle_timer) {\r
unsigned long rotate_bytes_low;\r
unsigned long rotate_bytes_high;\r
unsigned long default_exit_action;\r
+ unsigned long restart_delay;\r
unsigned long throttle_delay;\r
unsigned long stop_method;\r
unsigned long kill_console_delay;\r
{ NSSM_REG_ENV, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment },
{ NSSM_REG_ENV_EXTRA, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment },
{ NSSM_REG_PRIORITY, REG_SZ, (void *) priority_strings[NSSM_NORMAL_PRIORITY], false, 0, setting_set_priority, setting_get_priority },
+ { NSSM_REG_RESTART_DELAY, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
{ NSSM_REG_STDIN, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },
{ NSSM_REG_STDIN NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDIN_SHARING, false, 0, setting_set_number, setting_get_number },
{ NSSM_REG_STDIN NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDIN_DISPOSITION, false, 0, setting_set_number, setting_get_number },