From: Iain Patterson Date: Mon, 6 Jan 2014 17:28:24 +0000 (+0000) Subject: Allow setting application priority. X-Git-Tag: v2.22~79 X-Git-Url: http://git.iain.cx/?p=nssm.git;a=commitdiff_plain;h=02203cb8aff4be6a094b7a9ded867c3b5d743d77 Allow setting application priority. The REG_DWORD AppPriority entry corresponds to a priority class as accepted by SetProcessPriorityClass(). If a valid value is set, it will be passed to CreateProcess() and the application will start with the requested priority. --- diff --git a/ChangeLog.txt b/ChangeLog.txt index cfcbce6..141f31b 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -3,6 +3,9 @@ Changes since 2.21 * Existing services can now be managed using the GUI or on the command line. + * NSSM can now set the priority class of the managed + application. + * NSSM can now optionally rotate existing files when redirecting I/O. diff --git a/README.txt b/README.txt index 72f7ea4..98cec77 100644 --- a/README.txt +++ b/README.txt @@ -53,6 +53,8 @@ Since version 2.19, NSSM can add to the service's environment by setting AppEnvironmentExtra in place of or in addition to the srvany-compatible AppEnvironment. +Since version 2.22, NSSM can set the managed application's process priority. + 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 @@ -160,6 +162,15 @@ 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. +Application priority +-------------------- +NSSM can set the priority class of the managed application. NSSM will look in +the registry under HKLM\SYSTEM\CurrentControlSet\Services\\Parameters +for the REG_DWORD entry AppPriority. Valid values correspond to arguments to +SetProcessPriorityClass(). If AppPriority() is missing or invalid the +application will be launched with normal priority. + + Stopping the service -------------------- When stopping a service NSSM will attempt several different methods of killing @@ -382,6 +393,17 @@ exit code of 2, run nssm set AppExit 2 Exit +The AppPriority parameter is used to set the priority class of the +managed application. Valid priorities are as follows: + + REALTIME_PRIORITY_CLASS + HIGH_PRIORITY_CLASS + ABOVE_NORMAL_PRIORITY_CLASS + NORMAL_PRIORITY_CLASS + BELOW_NORMAL_PRIORITY_CLASS + IDLE_PRIORITY_CLASS + + The Name parameter can only be queried, not set. It returns the service's registry key name. This may be useful to know if you take advantage of the fact that you can substitute the service's display name anywhere where @@ -537,6 +559,7 @@ Thanks to Russ Holmann for suggesting that the shutdown timeout be configurable. Thanks to Paul Spause for spotting a bug with default registry entries. Thanks to BUGHUNTER for spotting more GUI bugs. Thanks to Doug Watson for suggesting file rotation. +Thanks to Арслан Сайдуганов for suggesting setting process priority. Licence ------- diff --git a/gui.cpp b/gui.cpp index 68ddb57..7697e26 100644 --- a/gui.cpp +++ b/gui.cpp @@ -1,6 +1,6 @@ #include "nssm.h" -static enum { NSSM_TAB_APPLICATION, NSSM_TAB_DETAILS, NSSM_TAB_LOGON, NSSM_TAB_SHUTDOWN, NSSM_TAB_EXIT, NSSM_TAB_IO, NSSM_TAB_ROTATION, NSSM_TAB_ENVIRONMENT, NSSM_NUM_TABS }; +static enum { NSSM_TAB_APPLICATION, NSSM_TAB_DETAILS, NSSM_TAB_LOGON, NSSM_TAB_PROCESS, NSSM_TAB_SHUTDOWN, NSSM_TAB_EXIT, NSSM_TAB_IO, NSSM_TAB_ROTATION, NSSM_TAB_ENVIRONMENT, NSSM_NUM_TABS }; static HWND tablist[NSSM_NUM_TABS]; static int selected_tab; @@ -94,6 +94,13 @@ int nssm_gui(int resource, nssm_service_t *service) { if (service->type & SERVICE_INTERACTIVE_PROCESS) SendDlgItemMessage(tablist[NSSM_TAB_LOGON], IDC_INTERACT, BM_SETCHECK, BST_CHECKED, 0); } + /* Process tab. */ + if (service->priority) { + int priority = priority_constant_to_index(service->priority); + combo = GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_PRIORITY); + SendMessage(combo, CB_SETCURSEL, priority, 0); + } + /* Shutdown tab. */ if (! (service->stop_method & NSSM_STOP_METHOD_CONSOLE)) { SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_CONSOLE, BM_SETCHECK, BST_UNCHECKED, 0); @@ -427,6 +434,10 @@ int configure(HWND window, nssm_service_t *service, nssm_service_t *orig_service /* Remaining tabs are only for services we manage. */ if (service->native) return 0; + /* Get process stuff. */ + combo = GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_PRIORITY); + service->priority = priority_index_to_constant((unsigned long) SendMessage(combo, CB_GETCURSEL, 0, 0)); + /* Get stop method stuff. */ check_stop_method(service, NSSM_STOP_METHOD_CONSOLE, IDC_METHOD_CONSOLE); check_stop_method(service, NSSM_STOP_METHOD_WINDOW, IDC_METHOD_WINDOW); @@ -873,6 +884,23 @@ INT_PTR CALLBACK nssm_dlg(HWND window, UINT message, WPARAM w, LPARAM l) { /* Remaining tabs are only for services we manage. */ if (service->native) return 1; + /* Process tab. */ + tab.pszText = message_string(NSSM_GUI_TAB_PROCESS); + tab.cchTextMax = (int) _tcslen(tab.pszText); + SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_PROCESS, (LPARAM) &tab); + tablist[NSSM_TAB_PROCESS] = dialog(MAKEINTRESOURCE(IDD_PROCESS), window, tab_dlg); + ShowWindow(tablist[NSSM_TAB_PROCESS], SW_HIDE); + + /* Set defaults. */ + combo = GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_PRIORITY); + SendMessage(combo, CB_INSERTSTRING, NSSM_REALTIME_PRIORITY, (LPARAM) message_string(NSSM_GUI_REALTIME_PRIORITY_CLASS)); + SendMessage(combo, CB_INSERTSTRING, NSSM_HIGH_PRIORITY, (LPARAM) message_string(NSSM_GUI_HIGH_PRIORITY_CLASS)); + SendMessage(combo, CB_INSERTSTRING, NSSM_ABOVE_NORMAL_PRIORITY, (LPARAM) message_string(NSSM_GUI_ABOVE_NORMAL_PRIORITY_CLASS)); + SendMessage(combo, CB_INSERTSTRING, NSSM_NORMAL_PRIORITY, (LPARAM) message_string(NSSM_GUI_NORMAL_PRIORITY_CLASS)); + SendMessage(combo, CB_INSERTSTRING, NSSM_BELOW_NORMAL_PRIORITY, (LPARAM) message_string(NSSM_GUI_BELOW_NORMAL_PRIORITY_CLASS)); + SendMessage(combo, CB_INSERTSTRING, NSSM_IDLE_PRIORITY, (LPARAM) message_string(NSSM_GUI_IDLE_PRIORITY_CLASS)); + SendMessage(combo, CB_SETCURSEL, NSSM_NORMAL_PRIORITY, 0); + /* Shutdown tab. */ tab.pszText = message_string(NSSM_GUI_TAB_SHUTDOWN); tab.cchTextMax = (int) _tcslen(tab.pszText); diff --git a/messages.mc b/messages.mc index 20e593a..1654085 100644 Binary files a/messages.mc and b/messages.mc differ diff --git a/nssm.h b/nssm.h index 784bd55..70d5396 100644 --- a/nssm.h +++ b/nssm.h @@ -80,6 +80,14 @@ int usage(int); #define NSSM_EXIT_UNCLEAN 3 #define NSSM_NUM_EXIT_ACTIONS 4 +/* Process priority. */ +#define NSSM_REALTIME_PRIORITY 0 +#define NSSM_HIGH_PRIORITY 1 +#define NSSM_ABOVE_NORMAL_PRIORITY 2 +#define NSSM_NORMAL_PRIORITY 3 +#define NSSM_BELOW_NORMAL_PRIORITY 4 +#define NSSM_IDLE_PRIORITY 5 + /* How many milliseconds to wait before updating service status. */ #define NSSM_SERVICE_STATUS_DEADLINE 20000 diff --git a/nssm.rc b/nssm.rc index 6befb69..b6f59d6 100644 Binary files a/nssm.rc and b/nssm.rc differ diff --git a/registry.cpp b/registry.cpp index cc871b7..c93d3ae 100644 --- a/registry.cpp +++ b/registry.cpp @@ -51,6 +51,8 @@ int create_parameters(nssm_service_t *service, bool editing) { } /* Other non-default parameters. May fail. */ + if (service->priority != NORMAL_PRIORITY_CLASS) set_number(key, NSSM_REG_PRIORITY, service->priority); + else if (editing) RegDeleteValue(key, NSSM_REG_PRIORITY); unsigned long stop_method_skip = ~service->stop_method; if (stop_method_skip) set_number(key, NSSM_REG_STOP_METHOD_SKIP, stop_method_skip); else if (editing) RegDeleteValue(key, NSSM_REG_STOP_METHOD_SKIP); @@ -492,6 +494,13 @@ int get_parameters(nssm_service_t *service, STARTUPINFO *si) { } } + /* Try to get priority - may fail. */ + unsigned long priority; + if (get_number(key, NSSM_REG_PRIORITY, &priority) == 1) { + if (priority == (priority & priority_mask())) service->priority = priority; + else log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_PRIORITY, service->name, NSSM_REG_PRIORITY, 0); + } + /* Try to get file rotation settings - may fail. */ unsigned long rotate_files; if (get_number(key, NSSM_REG_ROTATE, &rotate_files, false) == 1) { diff --git a/registry.h b/registry.h index 7af093c..6afa536 100644 --- a/registry.h +++ b/registry.h @@ -23,6 +23,7 @@ #define NSSM_REG_ROTATE_SECONDS _T("AppRotateSeconds") #define NSSM_REG_ROTATE_BYTES_LOW _T("AppRotateBytes") #define NSSM_REG_ROTATE_BYTES_HIGH _T("AppRotateBytesHigh") +#define NSSM_REG_PRIORITY _T("AppPriority") #define NSSM_STDIO_LENGTH 29 HKEY open_registry(const TCHAR *, const TCHAR *, REGSAM sam); diff --git a/resource.h b/resource.h index bebd411..baf15d8 100644 --- a/resource.h +++ b/resource.h @@ -16,6 +16,7 @@ #define IDD_SHUTDOWN 111 #define IDD_ENVIRONMENT 112 #define IDD_NATIVE 113 +#define IDD_PROCESS 114 #define IDC_PATH 1000 #define IDC_TAB1 1001 #define IDC_CANCEL 1002 @@ -55,14 +56,15 @@ #define IDC_USERNAME 1037 #define IDC_PASSWORD1 1038 #define IDC_PASSWORD2 1039 +#define IDC_PRIORITY 1040 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 114 +#define _APS_NEXT_RESOURCE_VALUE 115 #define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1040 +#define _APS_NEXT_CONTROL_VALUE 1041 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif diff --git a/service.cpp b/service.cpp index 4ec5133..72e0192 100644 --- a/service.cpp +++ b/service.cpp @@ -11,6 +11,33 @@ extern settings_t settings[]; const TCHAR *exit_action_strings[] = { _T("Restart"), _T("Ignore"), _T("Exit"), _T("Suicide"), 0 }; const TCHAR *startup_strings[] = { _T("SERVICE_AUTO_START"), _T("SERVICE_DELAYED_AUTO_START"), _T("SERVICE_DEMAND_START"), _T("SERVICE_DISABLED"), 0 }; +const TCHAR *priority_strings[] = { _T("REALTIME_PRIORITY_CLASS"), _T("HIGH_PRIORITY_CLASS"), _T("ABOVE_NORMAL_PRIORITY_CLASS"), _T("NORMAL_PRIORITY_CLASS"), _T("BELOW_NORMAL_PRIORITY_CLASS"), _T("IDLE_PRIORITY_CLASS"), 0 }; + +inline unsigned long priority_mask() { + return REALTIME_PRIORITY_CLASS | HIGH_PRIORITY_CLASS | ABOVE_NORMAL_PRIORITY_CLASS | NORMAL_PRIORITY_CLASS | BELOW_NORMAL_PRIORITY_CLASS | IDLE_PRIORITY_CLASS; +} + +int priority_constant_to_index(unsigned long constant) { + switch (constant & priority_mask()) { + case REALTIME_PRIORITY_CLASS: return NSSM_REALTIME_PRIORITY; + case HIGH_PRIORITY_CLASS: return NSSM_HIGH_PRIORITY; + case ABOVE_NORMAL_PRIORITY_CLASS: return NSSM_ABOVE_NORMAL_PRIORITY; + case BELOW_NORMAL_PRIORITY_CLASS: return NSSM_BELOW_NORMAL_PRIORITY; + case IDLE_PRIORITY_CLASS: return NSSM_IDLE_PRIORITY; + } + return NSSM_NORMAL_PRIORITY; +} + +unsigned long priority_index_to_constant(int index) { + switch (index) { + case NSSM_REALTIME_PRIORITY: return REALTIME_PRIORITY_CLASS; + case NSSM_HIGH_PRIORITY: return HIGH_PRIORITY_CLASS; + case NSSM_ABOVE_NORMAL_PRIORITY: return ABOVE_NORMAL_PRIORITY_CLASS; + case NSSM_BELOW_NORMAL_PRIORITY: return BELOW_NORMAL_PRIORITY_CLASS; + case NSSM_IDLE_PRIORITY: return IDLE_PRIORITY_CLASS; + } + return NORMAL_PRIORITY_CLASS; +} static inline int throttle_milliseconds(unsigned long throttle) { /* pow() operates on doubles. */ @@ -413,6 +440,7 @@ void set_nssm_service_defaults(nssm_service_t *service) { if (! service) return; service->type = SERVICE_WIN32_OWN_PROCESS; + service->priority = NORMAL_PRIORITY_CLASS; service->stdin_sharing = NSSM_STDIN_SHARING; service->stdin_disposition = NSSM_STDIN_DISPOSITION; service->stdin_flags = NSSM_STDIN_FLAGS; @@ -1259,7 +1287,7 @@ int start_service(nssm_service_t *service) { bool inherit_handles = false; if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true; - unsigned long flags = 0; + unsigned long flags = service->priority & priority_mask(); #ifdef UNICODE flags |= CREATE_UNICODE_ENVIRONMENT; #endif diff --git a/service.h b/service.h index e4049db..170ab42 100644 --- a/service.h +++ b/service.h @@ -47,6 +47,7 @@ typedef struct { unsigned long envlen; TCHAR *env_extra; unsigned long env_extralen; + unsigned long priority; TCHAR stdin_path[MAX_PATH]; unsigned long stdin_sharing; unsigned long stdin_disposition; @@ -92,6 +93,10 @@ TCHAR *service_control_text(unsigned long); void log_service_control(TCHAR *, unsigned long, bool); unsigned long WINAPI service_control_handler(unsigned long, unsigned long, void *, void *); +unsigned long priority_mask(); +int priority_constant_to_index(unsigned long); +unsigned long priority_index_to_constant(int); + nssm_service_t *alloc_nssm_service(); void set_nssm_service_defaults(nssm_service_t *); void cleanup_nssm_service(nssm_service_t *); diff --git a/settings.cpp b/settings.cpp index c83abb1..17a1cb3 100644 --- a/settings.cpp +++ b/settings.cpp @@ -3,6 +3,7 @@ extern const TCHAR *exit_action_strings[]; extern const TCHAR *startup_strings[]; +extern const TCHAR *priority_strings[]; /* Does the parameter refer to the default value of the AppExit setting? */ static inline int is_default_exit_action(const TCHAR *value) { @@ -256,6 +257,56 @@ static int setting_get_environment(const TCHAR *service_name, void *param, const return ret; } +static int setting_set_priority(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) { + HKEY key = (HKEY) param; + if (! param) return -1; + + TCHAR *priority_string; + int i; + long error; + + if (value && value->string) priority_string = value->string; + else if (default_value) priority_string = (TCHAR *) default_value; + else { + error = RegDeleteValue(key, name); + if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0; + print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error)); + return -1; + } + + for (i = 0; priority_strings[i]; i++) { + if (! str_equiv(priority_strings[i], priority_string)) continue; + + if (default_value && str_equiv(priority_string, (TCHAR *) default_value)) { + error = RegDeleteValue(key, name); + if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0; + print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error)); + return -1; + } + + if (set_number(key, (TCHAR *) name, priority_index_to_constant(i))) return -1; + return 1; + } + + print_message(stderr, NSSM_MESSAGE_INVALID_PRIORITY, priority_string); + for (i = 0; priority_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), priority_strings[i]); + + return -1; +} + +static int setting_get_priority(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) { + HKEY key = (HKEY) param; + if (! param) return -1; + + unsigned long constant; + switch (get_number(key, (TCHAR *) name, &constant, false)) { + case 0: return value_from_string(name, value, (const TCHAR *) default_value); + case -1: return -1; + } + + return value_from_string(name, value, priority_strings[priority_constant_to_index(constant)]); +} + /* Functions to manage native service settings. */ int native_set_description(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) { SC_HANDLE service_handle = (SC_HANDLE) param; @@ -650,6 +701,7 @@ settings_t settings[] = { { NSSM_REG_EXIT, REG_SZ, (void *) exit_action_strings[NSSM_EXIT_RESTART], false, ADDITIONAL_MANDATORY, setting_set_exit_action, setting_get_exit_action }, { 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_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 },