From 7fdd3f6c44421a7c4872ecea56e550c19489c949 Mon Sep 17 00:00:00 2001 From: Iain Patterson Date: Thu, 9 Jan 2014 08:53:32 +0000 Subject: [PATCH] Added mandatory restart delay. 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 | 3 +++ README.txt | 15 +++++++++++++++ gui.cpp | 3 +++ messages.mc | Bin 134690 -> 136708 bytes nssm.rc | Bin 42814 -> 43262 bytes registry.cpp | 5 +++++ registry.h | 1 + resource.h | 43 ++++++++++++++++++++++--------------------- service.cpp | 23 +++++++++++++++++------ service.h | 1 + settings.cpp | 1 + 11 files changed, 68 insertions(+), 27 deletions(-) diff --git a/ChangeLog.txt b/ChangeLog.txt index 671b7d0..ddfb5f7 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -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. diff --git a/README.txt b/README.txt index 685807a..830ca67 100644 --- a/README.txt +++ b/README.txt @@ -56,6 +56,9 @@ AppEnvironment. Since version 2.22, NSSM can set the managed application's process priority and CPU affinity. +Since version 2.22, NSSM can apply an unconditional delay before restarting +an application which has exited. + Since version 2.22, NSSM can rotate existing output files when redirecting I/O. Since version 2.22, NSSM can set service display name, description, startup @@ -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 HKLM\SYSTEM\CurrentControlSet\Services\\Parameters\AppThrottle. +Alternatively, NSSM can pause for a configurable amount of time before +attempting to restart the application even if it successfully ran for the +amount of time specified by AppThrottle. NSSM will consult the REG_DWORD value +at HKLM\SYSTEM\CurrentControlSet\Services\\Parameters\AppRestartDelay +for the number of milliseconds to wait before attempting a restart. If +AppRestartDelay is set and the application is determined to be subject to +throttling, NSSM will pause the service for whichever is longer of the +configured restart delay and the calculated throttle period. + +If AppRestartDelay is missing or invalid, only throttling will be applied. + NSSM will look in the registry under HKLM\SYSTEM\CurrentControlSet\Services\\Parameters\AppExit for string (REG_EXPAND_SZ) values corresponding to the exit code of the application. @@ -588,6 +602,7 @@ Thanks to Doug Watson for suggesting file rotation. Thanks to Арслан Сайдуганов for suggesting setting process priority. Thanks to Robert Middleton for suggestion and draft implementation of process affinity support. +Thanks to Andrew RedzMax for suggesting an unconditional restart delay. Licence ------- diff --git a/gui.cpp b/gui.cpp index 935198f..4fdd3ca 100644 --- 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); combo = GetDlgItem(tablist[NSSM_TAB_EXIT], IDC_APPEXIT); SendMessage(combo, CB_SETCURSEL, service->default_exit_action, 0); + SetDlgItemInt(tablist[NSSM_TAB_EXIT], IDC_RESTART_DELAY, service->restart_delay, 0); /* I/O tab. */ SetDlgItemText(tablist[NSSM_TAB_IO], IDC_STDIN, service->stdin_path); @@ -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); service->default_exit_action = (unsigned long) SendMessage(combo, CB_GETCURSEL, 0, 0); if (service->default_exit_action == CB_ERR) service->default_exit_action = 0; + check_number(tablist[NSSM_TAB_EXIT], IDC_RESTART_DELAY, &service->restart_delay); /* Get I/O stuff. */ check_io(window, _T("stdin"), service->stdin_path, _countof(service->stdin_path), IDC_STDIN); @@ -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)); SendMessage(combo, CB_INSERTSTRING, NSSM_EXIT_UNCLEAN, (LPARAM) message_string(NSSM_GUI_EXIT_UNCLEAN)); SendMessage(combo, CB_SETCURSEL, NSSM_EXIT_RESTART, 0); + SetDlgItemInt(tablist[NSSM_TAB_EXIT], IDC_RESTART_DELAY, 0, 0); /* I/O tab. */ tab.pszText = message_string(NSSM_GUI_TAB_IO); diff --git a/messages.mc b/messages.mc index 2ee1f8047585c0896bed186bc8ab1f2566b93e5f..fdac57c8b82d2e9a2fd71489c218684c15fa539c 100644 GIT binary patch delta 553 zcmc&xJxf9X6g?*-%uEanOemfyjbbfDQ$lTGWfBB|y;4Z1-iMFE%|K{sdemK-nkD)g z`U??3Lr63=*4EI{RF{UJUmzC__rSU5!Z~NB!B#uiZ1JXNEF$?Smc)Kwc({mpETe=R zii%~-DbFig6=#hLa`D9#`MhH$xq$`@>|+ZxY$_UXQAHgdOx--x#Y%HT7OxqW^>6k{ z$6`dTI-E|0P=KWeJ1X5pMbT64{u)-)WE-YGcr?^sRfVn0LPHLYIU&Yl_r9kj`p&r6 z_bhZJfXiE&@)2XS@2?U+cUbmEU&7+1nfRY{Tsm)@@w1JXJk>cV%~=NI*`^^UmTJ#! f1|{JzJK$=4CiZk{uw-eRNqKdck+wr4kUEr(A2Yn7s^h5<}(WLNi5fQwB2z9R>vkQwEF42b)5* zgBV;Hf*C>>92tUuWITflknaQJMS@isF&HpdK-HT~&b6G$ZNy;AUdefault_exit_action < NSSM_NUM_EXIT_ACTIONS) create_exit_action(service->name, exit_action_strings[service->default_exit_action], editing); + if (service->restart_delay) set_number(key, NSSM_REG_RESTART_DELAY, service->restart_delay); + else if (editing) RegDeleteValue(key, NSSM_REG_RESTART_DELAY); if (service->throttle_delay != NSSM_RESET_THROTTLE_RESTART) set_number(key, NSSM_REG_THROTTLE, service->throttle_delay); else if (editing) RegDeleteValue(key, NSSM_REG_THROTTLE); if (service->kill_console_delay != NSSM_KILL_CONSOLE_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, service->kill_console_delay); @@ -566,6 +568,9 @@ int get_parameters(nssm_service_t *service, STARTUPINFO *si) { /* Change back in case the startup directory needs to be deleted. */ SetCurrentDirectory(cwd); + /* Try to get mandatory restart delay */ + override_milliseconds(service->name, key, NSSM_REG_RESTART_DELAY, &service->restart_delay, 0, NSSM_EVENT_BOGUS_RESTART_DELAY); + /* Try to get throttle restart delay */ override_milliseconds(service->name, key, NSSM_REG_THROTTLE, &service->throttle_delay, NSSM_RESET_THROTTLE_RESTART, NSSM_EVENT_BOGUS_THROTTLE); diff --git a/registry.h b/registry.h index e0055b3..5552063 100644 --- a/registry.h +++ b/registry.h @@ -8,6 +8,7 @@ #define NSSM_REG_ENV _T("AppEnvironment") #define NSSM_REG_ENV_EXTRA _T("AppEnvironmentExtra") #define NSSM_REG_EXIT _T("AppExit") +#define NSSM_REG_RESTART_DELAY _T("AppRestartDelay") #define NSSM_REG_THROTTLE _T("AppThrottle") #define NSSM_REG_STOP_METHOD_SKIP _T("AppStopMethodSkip") #define NSSM_REG_KILL_CONSOLE_GRACE_PERIOD _T("AppStopMethodConsole") diff --git a/resource.h b/resource.h index 0589119..fdab376 100644 --- a/resource.h +++ b/resource.h @@ -39,26 +39,27 @@ #define IDC_BROWSE_STDERR 1020 #define IDC_THROTTLE 1021 #define IDC_APPEXIT 1022 -#define IDC_DIR 1023 -#define IDC_BROWSE_DIR 1024 -#define IDC_ENVIRONMENT 1025 -#define IDC_ENVIRONMENT_REPLACE 1026 -#define IDC_TRUNCATE 1027 -#define IDC_ROTATE 1028 -#define IDC_ROTATE_SECONDS 1029 -#define IDC_ROTATE_BYTES_LOW 1030 -#define IDC_DISPLAYNAME 1031 -#define IDC_DESCRIPTION 1032 -#define IDC_STARTUP 1033 -#define IDC_LOCALSYSTEM 1034 -#define IDC_INTERACT 1035 -#define IDC_ACCOUNT 1036 -#define IDC_USERNAME 1037 -#define IDC_PASSWORD1 1038 -#define IDC_PASSWORD2 1039 -#define IDC_PRIORITY 1040 -#define IDC_AFFINITY_ALL 1041 -#define IDC_AFFINITY 1042 +#define IDC_RESTART_DELAY 1023 +#define IDC_DIR 1024 +#define IDC_BROWSE_DIR 1025 +#define IDC_ENVIRONMENT 1026 +#define IDC_ENVIRONMENT_REPLACE 1027 +#define IDC_TRUNCATE 1028 +#define IDC_ROTATE 1029 +#define IDC_ROTATE_SECONDS 1030 +#define IDC_ROTATE_BYTES_LOW 1031 +#define IDC_DISPLAYNAME 1032 +#define IDC_DESCRIPTION 1033 +#define IDC_STARTUP 1034 +#define IDC_LOCALSYSTEM 1035 +#define IDC_INTERACT 1036 +#define IDC_ACCOUNT 1037 +#define IDC_USERNAME 1038 +#define IDC_PASSWORD1 1039 +#define IDC_PASSWORD2 1040 +#define IDC_PRIORITY 1041 +#define IDC_AFFINITY_ALL 1042 +#define IDC_AFFINITY 1043 // Next default values for new objects // @@ -66,7 +67,7 @@ #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 115 #define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1043 +#define _APS_NEXT_CONTROL_VALUE 1044 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif diff --git a/service.cpp b/service.cpp index 93b7d4a..c3a5008 100644 --- a/service.cpp +++ b/service.cpp @@ -162,9 +162,9 @@ unsigned long priority_index_to_constant(int index) { return NORMAL_PRIORITY_CLASS; } -static inline int throttle_milliseconds(unsigned long throttle) { +static inline unsigned long throttle_milliseconds(unsigned long throttle) { /* pow() operates on doubles. */ - int ret = 1; for (unsigned long i = 1; i < throttle; i++) ret *= 2; + unsigned long ret = 1; for (unsigned long i = 1; i < throttle; i++) ret *= 2; return ret * 1000; } @@ -1490,6 +1490,9 @@ int start_service(nssm_service_t *service) { else service->throttle = 0; } + /* Ensure the restart delay is always applied. */ + if (service->restart_delay && ! service->throttle) service->throttle++; + return 0; } @@ -1632,14 +1635,22 @@ void throttle_restart(nssm_service_t *service) { /* This can't be a restart if the service is already running. */ if (! service->throttle++) return; - int ms = throttle_milliseconds(service->throttle); + unsigned long ms; + unsigned long throttle_ms = throttle_milliseconds(service->throttle); + TCHAR threshold[8], milliseconds[8]; + + if (service->restart_delay > throttle_ms) ms = service->restart_delay; + else ms = throttle_ms; if (service->throttle > 7) service->throttle = 8; - TCHAR threshold[8], milliseconds[8]; - _sntprintf_s(threshold, _countof(threshold), _TRUNCATE, _T("%lu"), service->throttle_delay); _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), ms); - log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service->name, threshold, milliseconds, 0); + + if (service->throttle == 1 && service->restart_delay > throttle_ms) log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESTART_DELAY, service->name, milliseconds, 0); + else { + _sntprintf_s(threshold, _countof(threshold), _TRUNCATE, _T("%lu"), service->throttle_delay); + log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service->name, threshold, milliseconds, 0); + } if (use_critical_section) EnterCriticalSection(&service->throttle_section); else if (service->throttle_timer) { diff --git a/service.h b/service.h index 0e5da20..155586a 100644 --- a/service.h +++ b/service.h @@ -66,6 +66,7 @@ typedef struct { unsigned long rotate_bytes_low; unsigned long rotate_bytes_high; unsigned long default_exit_action; + unsigned long restart_delay; unsigned long throttle_delay; unsigned long stop_method; unsigned long kill_console_delay; diff --git a/settings.cpp b/settings.cpp index 25c7921..707e6c1 100644 --- a/settings.cpp +++ b/settings.cpp @@ -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 }, -- 2.20.1