Allow setting application priority.
authorIain Patterson <me@iain.cx>
Mon, 6 Jan 2014 17:28:24 +0000 (17:28 +0000)
committerIain Patterson <me@iain.cx>
Mon, 6 Jan 2014 17:49:18 +0000 (17:49 +0000)
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.

12 files changed:
ChangeLog.txt
README.txt
gui.cpp
messages.mc
nssm.h
nssm.rc
registry.cpp
registry.h
resource.h
service.cpp
service.h
settings.cpp

index cfcbce6..141f31b 100644 (file)
@@ -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.
 
index 72f7ea4..98cec77 100644 (file)
@@ -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\r
 AppEnvironment.\r
 \r
+Since version 2.22, NSSM can set the managed application's process priority.\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
@@ -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.\r
 \r
 \r
+Application priority\r
+--------------------\r
+NSSM can set the priority class of the managed application.  NSSM will look in\r
+the registry under HKLM\SYSTEM\CurrentControlSet\Services\<service>\Parameters\r
+for the REG_DWORD entry AppPriority.  Valid values correspond to arguments to\r
+SetProcessPriorityClass().  If AppPriority() is missing or invalid the\r
+application will be launched with normal priority.\r
+\r
+\r
 Stopping the service\r
 --------------------\r
 When stopping a service NSSM will attempt several different methods of killing\r
@@ -382,6 +393,17 @@ exit code of 2, run
     nssm set <servicename> AppExit 2 Exit\r
 \r
 \r
+The AppPriority parameter is used to set the priority class of the\r
+managed application.  Valid priorities are as follows:\r
+\r
+  REALTIME_PRIORITY_CLASS\r
+  HIGH_PRIORITY_CLASS\r
+  ABOVE_NORMAL_PRIORITY_CLASS\r
+  NORMAL_PRIORITY_CLASS\r
+  BELOW_NORMAL_PRIORITY_CLASS\r
+  IDLE_PRIORITY_CLASS\r
+\r
+\r
 The Name parameter can only be queried, not set.  It returns the service's\r
 registry key name.  This may be useful to know if you take advantage of\r
 the fact that you can substitute the service's display name anywhere where\r
@@ -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.\r
 Thanks to BUGHUNTER for spotting more GUI bugs.\r
 Thanks to Doug Watson for suggesting file rotation.\r
+Thanks to Арслан Сайдуганов for suggesting setting process priority.\r
 \r
 Licence\r
 -------\r
diff --git a/gui.cpp b/gui.cpp
index 68ddb57..7697e26 100644 (file)
--- a/gui.cpp
+++ b/gui.cpp
@@ -1,6 +1,6 @@
 #include "nssm.h"\r
 \r
-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 };\r
+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 };\r
 static HWND tablist[NSSM_NUM_TABS];\r
 static int selected_tab;\r
 \r
@@ -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);\r
     }\r
 \r
+    /* Process tab. */\r
+    if (service->priority) {\r
+      int priority = priority_constant_to_index(service->priority);\r
+      combo = GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_PRIORITY);\r
+      SendMessage(combo, CB_SETCURSEL, priority, 0);\r
+    }\r
+\r
     /* Shutdown tab. */\r
     if (! (service->stop_method & NSSM_STOP_METHOD_CONSOLE)) {\r
       SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_CONSOLE, BM_SETCHECK, BST_UNCHECKED, 0);\r
@@ -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. */\r
   if (service->native) return 0;\r
 \r
+  /* Get process stuff. */\r
+  combo = GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_PRIORITY);\r
+  service->priority = priority_index_to_constant((unsigned long) SendMessage(combo, CB_GETCURSEL, 0, 0));\r
+\r
   /* Get stop method stuff. */\r
   check_stop_method(service, NSSM_STOP_METHOD_CONSOLE, IDC_METHOD_CONSOLE);\r
   check_stop_method(service, NSSM_STOP_METHOD_WINDOW, IDC_METHOD_WINDOW);\r
@@ -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. */\r
       if (service->native) return 1;\r
 \r
+      /* Process tab. */\r
+      tab.pszText = message_string(NSSM_GUI_TAB_PROCESS);\r
+      tab.cchTextMax = (int) _tcslen(tab.pszText);\r
+      SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_PROCESS, (LPARAM) &tab);\r
+      tablist[NSSM_TAB_PROCESS] = dialog(MAKEINTRESOURCE(IDD_PROCESS), window, tab_dlg);\r
+      ShowWindow(tablist[NSSM_TAB_PROCESS], SW_HIDE);\r
+\r
+      /* Set defaults. */\r
+      combo = GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_PRIORITY);\r
+      SendMessage(combo, CB_INSERTSTRING, NSSM_REALTIME_PRIORITY, (LPARAM) message_string(NSSM_GUI_REALTIME_PRIORITY_CLASS));\r
+      SendMessage(combo, CB_INSERTSTRING, NSSM_HIGH_PRIORITY, (LPARAM) message_string(NSSM_GUI_HIGH_PRIORITY_CLASS));\r
+      SendMessage(combo, CB_INSERTSTRING, NSSM_ABOVE_NORMAL_PRIORITY, (LPARAM) message_string(NSSM_GUI_ABOVE_NORMAL_PRIORITY_CLASS));\r
+      SendMessage(combo, CB_INSERTSTRING, NSSM_NORMAL_PRIORITY, (LPARAM) message_string(NSSM_GUI_NORMAL_PRIORITY_CLASS));\r
+      SendMessage(combo, CB_INSERTSTRING, NSSM_BELOW_NORMAL_PRIORITY, (LPARAM) message_string(NSSM_GUI_BELOW_NORMAL_PRIORITY_CLASS));\r
+      SendMessage(combo, CB_INSERTSTRING, NSSM_IDLE_PRIORITY, (LPARAM) message_string(NSSM_GUI_IDLE_PRIORITY_CLASS));\r
+      SendMessage(combo, CB_SETCURSEL, NSSM_NORMAL_PRIORITY, 0);\r
+\r
       /* Shutdown tab. */\r
       tab.pszText = message_string(NSSM_GUI_TAB_SHUTDOWN);\r
       tab.cchTextMax = (int) _tcslen(tab.pszText);\r
index 20e593a..1654085 100644 (file)
Binary files a/messages.mc and b/messages.mc differ
diff --git a/nssm.h b/nssm.h
index 784bd55..70d5396 100644 (file)
--- a/nssm.h
+++ b/nssm.h
@@ -80,6 +80,14 @@ int usage(int);
 #define NSSM_EXIT_UNCLEAN 3\r
 #define NSSM_NUM_EXIT_ACTIONS 4\r
 \r
+/* Process priority. */\r
+#define NSSM_REALTIME_PRIORITY 0\r
+#define NSSM_HIGH_PRIORITY 1\r
+#define NSSM_ABOVE_NORMAL_PRIORITY 2\r
+#define NSSM_NORMAL_PRIORITY 3\r
+#define NSSM_BELOW_NORMAL_PRIORITY 4\r
+#define NSSM_IDLE_PRIORITY 5\r
+\r
 /* How many milliseconds to wait before updating service status. */\r
 #define NSSM_SERVICE_STATUS_DEADLINE 20000\r
 \r
diff --git a/nssm.rc b/nssm.rc
index 6befb69..b6f59d6 100644 (file)
Binary files a/nssm.rc and b/nssm.rc differ
index cc871b7..c93d3ae 100644 (file)
@@ -51,6 +51,8 @@ int create_parameters(nssm_service_t *service, bool editing) {
   }\r
 \r
   /* Other non-default parameters. May fail. */\r
+  if (service->priority != NORMAL_PRIORITY_CLASS) set_number(key, NSSM_REG_PRIORITY, service->priority);\r
+  else if (editing) RegDeleteValue(key, NSSM_REG_PRIORITY);\r
   unsigned long stop_method_skip = ~service->stop_method;\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
@@ -492,6 +494,13 @@ int get_parameters(nssm_service_t *service, STARTUPINFO *si) {
     }\r
   }\r
 \r
+  /* Try to get priority - may fail. */\r
+  unsigned long priority;\r
+  if (get_number(key, NSSM_REG_PRIORITY, &priority) == 1) {\r
+    if (priority == (priority & priority_mask())) service->priority = priority;\r
+    else log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_PRIORITY, service->name, NSSM_REG_PRIORITY, 0);\r
+  }\r
+\r
   /* Try to get file rotation settings - may fail. */\r
   unsigned long rotate_files;\r
   if (get_number(key, NSSM_REG_ROTATE, &rotate_files, false) == 1) {\r
index 7af093c..6afa536 100644 (file)
@@ -23,6 +23,7 @@
 #define NSSM_REG_ROTATE_SECONDS _T("AppRotateSeconds")\r
 #define NSSM_REG_ROTATE_BYTES_LOW _T("AppRotateBytes")\r
 #define NSSM_REG_ROTATE_BYTES_HIGH _T("AppRotateBytesHigh")\r
+#define NSSM_REG_PRIORITY _T("AppPriority")\r
 #define NSSM_STDIO_LENGTH 29\r
 \r
 HKEY open_registry(const TCHAR *, const TCHAR *, REGSAM sam);\r
index bebd411..baf15d8 100644 (file)
@@ -16,6 +16,7 @@
 #define IDD_SHUTDOWN                    111\r
 #define IDD_ENVIRONMENT                 112\r
 #define IDD_NATIVE                      113\r
+#define IDD_PROCESS                     114\r
 #define IDC_PATH                        1000\r
 #define IDC_TAB1                        1001\r
 #define IDC_CANCEL                      1002\r
 #define IDC_USERNAME                    1037\r
 #define IDC_PASSWORD1                   1038\r
 #define IDC_PASSWORD2                   1039\r
+#define IDC_PRIORITY                    1040\r
 \r
 // Next default values for new objects\r
 // \r
 #ifdef APSTUDIO_INVOKED\r
 #ifndef APSTUDIO_READONLY_SYMBOLS\r
-#define _APS_NEXT_RESOURCE_VALUE        114\r
+#define _APS_NEXT_RESOURCE_VALUE        115\r
 #define _APS_NEXT_COMMAND_VALUE         40001\r
-#define _APS_NEXT_CONTROL_VALUE         1040\r
+#define _APS_NEXT_CONTROL_VALUE         1041\r
 #define _APS_NEXT_SYMED_VALUE           101\r
 #endif\r
 #endif\r
index 4ec5133..72e0192 100644 (file)
@@ -11,6 +11,33 @@ extern settings_t settings[];
 \r
 const TCHAR *exit_action_strings[] = { _T("Restart"), _T("Ignore"), _T("Exit"), _T("Suicide"), 0 };\r
 const TCHAR *startup_strings[] = { _T("SERVICE_AUTO_START"), _T("SERVICE_DELAYED_AUTO_START"), _T("SERVICE_DEMAND_START"), _T("SERVICE_DISABLED"), 0 };\r
+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 };\r
+\r
+inline unsigned long priority_mask() {\r
+ return REALTIME_PRIORITY_CLASS | HIGH_PRIORITY_CLASS | ABOVE_NORMAL_PRIORITY_CLASS | NORMAL_PRIORITY_CLASS | BELOW_NORMAL_PRIORITY_CLASS | IDLE_PRIORITY_CLASS;\r
+}\r
+\r
+int priority_constant_to_index(unsigned long constant) {\r
+  switch (constant & priority_mask()) {\r
+    case REALTIME_PRIORITY_CLASS: return NSSM_REALTIME_PRIORITY;\r
+    case HIGH_PRIORITY_CLASS: return NSSM_HIGH_PRIORITY;\r
+    case ABOVE_NORMAL_PRIORITY_CLASS: return NSSM_ABOVE_NORMAL_PRIORITY;\r
+    case BELOW_NORMAL_PRIORITY_CLASS: return NSSM_BELOW_NORMAL_PRIORITY;\r
+    case IDLE_PRIORITY_CLASS: return NSSM_IDLE_PRIORITY;\r
+  }\r
+  return NSSM_NORMAL_PRIORITY;\r
+}\r
+\r
+unsigned long priority_index_to_constant(int index) {\r
+  switch (index) {\r
+    case NSSM_REALTIME_PRIORITY: return REALTIME_PRIORITY_CLASS;\r
+    case NSSM_HIGH_PRIORITY: return HIGH_PRIORITY_CLASS;\r
+    case NSSM_ABOVE_NORMAL_PRIORITY: return ABOVE_NORMAL_PRIORITY_CLASS;\r
+    case NSSM_BELOW_NORMAL_PRIORITY: return BELOW_NORMAL_PRIORITY_CLASS;\r
+    case NSSM_IDLE_PRIORITY: return IDLE_PRIORITY_CLASS;\r
+  }\r
+  return NORMAL_PRIORITY_CLASS;\r
+}\r
 \r
 static inline int throttle_milliseconds(unsigned long throttle) {\r
   /* pow() operates on doubles. */\r
@@ -413,6 +440,7 @@ void set_nssm_service_defaults(nssm_service_t *service) {
   if (! service) return;\r
 \r
   service->type = SERVICE_WIN32_OWN_PROCESS;\r
+  service->priority = NORMAL_PRIORITY_CLASS;\r
   service->stdin_sharing = NSSM_STDIN_SHARING;\r
   service->stdin_disposition = NSSM_STDIN_DISPOSITION;\r
   service->stdin_flags = NSSM_STDIN_FLAGS;\r
@@ -1259,7 +1287,7 @@ int start_service(nssm_service_t *service) {
 \r
   bool inherit_handles = false;\r
   if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;\r
-  unsigned long flags = 0;\r
+  unsigned long flags = service->priority & priority_mask();\r
 #ifdef UNICODE\r
   flags |= CREATE_UNICODE_ENVIRONMENT;\r
 #endif\r
index e4049db..170ab42 100644 (file)
--- a/service.h
+++ b/service.h
@@ -47,6 +47,7 @@ typedef struct {
   unsigned long envlen;\r
   TCHAR *env_extra;\r
   unsigned long env_extralen;\r
+  unsigned long priority;\r
   TCHAR stdin_path[MAX_PATH];\r
   unsigned long stdin_sharing;\r
   unsigned long stdin_disposition;\r
@@ -92,6 +93,10 @@ TCHAR *service_control_text(unsigned long);
 void log_service_control(TCHAR *, unsigned long, bool);\r
 unsigned long WINAPI service_control_handler(unsigned long, unsigned long, void *, void *);\r
 \r
+unsigned long priority_mask();\r
+int priority_constant_to_index(unsigned long);\r
+unsigned long priority_index_to_constant(int);\r
+\r
 nssm_service_t *alloc_nssm_service();\r
 void set_nssm_service_defaults(nssm_service_t *);\r
 void cleanup_nssm_service(nssm_service_t *);\r
index c83abb1..17a1cb3 100644 (file)
@@ -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 },