Added mandatory restart delay.
authorIain Patterson <me@iain.cx>
Thu, 9 Jan 2014 08:53:32 +0000 (08:53 +0000)
committerIain Patterson <me@iain.cx>
Thu, 9 Jan 2014 09:03:58 +0000 (09:03 +0000)
The REG_DWORD AppRestartDelay entry specifies a minimum number of
milliseconds by which we will delay the restart (not the initial start)
of the managed application.  If the application exited soon enough to
trigger throttling, the actual delay will be whichever is longer of
AppRestartDelay and the calculated throttle time.

Thanks Andrew RedzMax.

ChangeLog.txt
README.txt
gui.cpp
messages.mc
nssm.rc
registry.cpp
registry.h
resource.h
service.cpp
service.h
settings.cpp

index 671b7d0..ddfb5f7 100644 (file)
@@ -6,6 +6,9 @@ Changes since 2.21
   * 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.
 
index 685807a..830ca67 100644 (file)
@@ -56,6 +56,9 @@ AppEnvironment.
 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
@@ -129,6 +132,17 @@ You can change the threshold for the service by setting the number of
 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
@@ -588,6 +602,7 @@ Thanks to Doug Watson for suggesting file rotation.
 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
diff --git a/gui.cpp b/gui.cpp
index 935198f..4fdd3ca 100644 (file)
--- a/gui.cpp
+++ b/gui.cpp
@@ -141,6 +141,7 @@ int nssm_gui(int resource, nssm_service_t *service) {
     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
@@ -488,6 +489,7 @@ int configure(HWND window, nssm_service_t *service, nssm_service_t *orig_service
   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
@@ -1008,6 +1010,7 @@ INT_PTR CALLBACK nssm_dlg(HWND window, UINT message, WPARAM w, LPARAM l) {
       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
index 2ee1f80..fdac57c 100644 (file)
Binary files a/messages.mc and b/messages.mc differ
diff --git a/nssm.rc b/nssm.rc
index 39bd585..f014b60 100644 (file)
Binary files a/nssm.rc and b/nssm.rc differ
index c6189fe..caa0fb1 100644 (file)
@@ -69,6 +69,8 @@ int create_parameters(nssm_service_t *service, bool editing) {
   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
@@ -566,6 +568,9 @@ int get_parameters(nssm_service_t *service, STARTUPINFO *si) {
   /* 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
index e0055b3..5552063 100644 (file)
@@ -8,6 +8,7 @@
 #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
index 0589119..fdab376 100644 (file)
 #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
@@ -66,7 +67,7 @@
 #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
index 93b7d4a..c3a5008 100644 (file)
@@ -162,9 +162,9 @@ unsigned long priority_index_to_constant(int index) {
   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
@@ -1490,6 +1490,9 @@ int start_service(nssm_service_t *service) {
     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
@@ -1632,14 +1635,22 @@ void throttle_restart(nssm_service_t *service) {
   /* 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
index 0e5da20..155586a 100644 (file)
--- a/service.h
+++ b/service.h
@@ -66,6 +66,7 @@ typedef struct {
   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
index 25c7921..707e6c1 100644 (file)
@@ -811,6 +811,7 @@ settings_t settings[] = {
   { 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 },