From 02203cb8aff4be6a094b7a9ded867c3b5d743d77 Mon Sep 17 00:00:00 2001 From: Iain Patterson Date: Mon, 6 Jan 2014 17:28:24 +0000 Subject: [PATCH] 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. --- ChangeLog.txt | 3 +++ README.txt | 23 +++++++++++++++++++++++ gui.cpp | 30 +++++++++++++++++++++++++++++- messages.mc | Bin 121070 -> 126090 bytes nssm.h | 8 ++++++++ nssm.rc | Bin 38368 -> 40722 bytes registry.cpp | 9 +++++++++ registry.h | 1 + resource.h | 6 ++++-- service.cpp | 30 +++++++++++++++++++++++++++++- service.h | 5 +++++ settings.cpp | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 12 files changed, 163 insertions(+), 4 deletions(-) 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 20e593a2d2bb2df5207c4da32decb584bef934f5..1654085386116d38087932b1d145f91501f2b12a 100644 GIT binary patch delta 1856 zcmeHHO;1xn6ulFHB2vDJ3rx_G2GL-U+OTl>P-2OtZA#-uOqEA@1x#OG?Q1c*GBIvQ zjL?zH+O5QhvhX7sH)>dz#x6;WaqEWIKVUpFt=bYIb*+oO%*UO3&OPVeZar}vN0Aq9Fvx7A?#>pU?7oU3D!=Pu8+LKK=&R+Sp zompI8zr@3Dn%BR-{^%Nv&=_eHpeX8|!1@Z+Xh0WnatikvP4d!W%ozccPD#o_3zJ`a z>Ftc-#H2Lf+yMVd24CLrh^bZ9Z1J2kmw#iz|N3=H!z1EAhR^bJ6{~fTBjUk z`Ga>OjS^m$nS2_J!me|Qcu*NPp~~5%43|dJ&}jkG-IP+Mn*82|Z*USAX%r!iHL%X8 zh%bmr0{>U6lmJY81Lb%_#mm-^`J_BGk&pyj2kPgi@5w-1@YxqEbNh+stlF!rG4B}6 z3DJ3IS%3bRYZOeA$bJ1cQ&s;s)JXhA{pI+&>i3M(X>i@O#To;Wm-2iRb=&A{D809` zF8hAFP<&P>GPgr~yUU{D6KAbE%x?Z-R{PQ|c(L9For4=p3tZnk9?(9dj8vdSVvO zP28u@wQl_6JY-jL7^upaSY znYDtpV$kI+`xoqx7>4*#)t)P%?> F`2p#%w&wr< delta 28 kcmeCW$^LF5`v#%h&2{;20yZByctN?At2{yx$dY2*yk<@s697194;KIc 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 }, -- 2.7.4