Set environment from the GUI at install time.
authorIain Patterson <me@iain.cx>
Thu, 21 Nov 2013 18:39:07 +0000 (18:39 +0000)
committerIain Patterson <me@iain.cx>
Thu, 21 Nov 2013 18:39:07 +0000 (18:39 +0000)
Added an extra Environment tab to set a newline-separated list of
environment variables to replace or be added to the service environment.

Input is validated by relaunching nssm itself with the proposed
environment set.

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

index 668cebb..e7cff08 100644 (file)
@@ -1,5 +1,8 @@
 Changes since 2.18
 -----------------
+  * Support AppEnvironmentExtra to append to the environment
+    instead of replacing it.
+
   * The GUI is significantly less sucky.
 
 Changes since 2.17
diff --git a/gui.cpp b/gui.cpp
index 013f2f0..4bcb144 100644 (file)
--- a/gui.cpp
+++ b/gui.cpp
@@ -1,6 +1,6 @@
 #include "nssm.h"\r
 \r
-static enum { NSSM_TAB_APPLICATION, NSSM_TAB_SHUTDOWN, NSSM_TAB_EXIT, NSSM_TAB_IO, NSSM_NUM_TABS };\r
+static enum { NSSM_TAB_APPLICATION, NSSM_TAB_SHUTDOWN, NSSM_TAB_EXIT, NSSM_TAB_IO, NSSM_TAB_ENVIRONMENT, NSSM_NUM_TABS };\r
 static HWND tablist[NSSM_NUM_TABS];\r
 static int selected_tab;\r
 \r
@@ -120,7 +120,7 @@ int install(HWND window) {
       if (! GetDlgItemText(window, IDC_FLAGS, service->flags, sizeof(service->flags))) {\r
         popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_OPTIONS);\r
         return 4;\r
-      }
+      }\r
     }\r
 \r
     /* Get stop method stuff. */\r
@@ -143,6 +143,80 @@ int install(HWND window) {
     check_io("stdin", service->stdin_path, sizeof(service->stdin_path), IDC_STDIN);\r
     check_io("stdout", service->stdout_path, sizeof(service->stdout_path), IDC_STDOUT);\r
     check_io("stderr", service->stderr_path, sizeof(service->stderr_path), IDC_STDERR);\r
+\r
+    /* Get environment. */\r
+    unsigned long envlen = (unsigned long) SendMessage(GetDlgItem(tablist[NSSM_TAB_ENVIRONMENT], IDC_ENVIRONMENT), WM_GETTEXTLENGTH, 0, 0);\r
+    if (envlen) {\r
+      char *env = (char *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, envlen + 2);\r
+      if (! env) {\r
+        popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, "environment", "install()");\r
+        cleanup_nssm_service(service);\r
+        return 5;\r
+      }\r
+\r
+      if (! GetDlgItemText(tablist[NSSM_TAB_ENVIRONMENT], IDC_ENVIRONMENT, env, envlen + 1)) {\r
+        popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_ENVIRONMENT);\r
+        HeapFree(GetProcessHeap(), 0, env);\r
+        cleanup_nssm_service(service);\r
+        return 5;\r
+      }\r
+\r
+      /* Strip CR and replace LF with NULL. */\r
+      unsigned long newlen = 0;\r
+      unsigned long i, j;\r
+      for (i = 0; i < envlen; i++) if (env[i] != '\r') newlen++;\r
+      /* Must end with two NULLs. */\r
+      newlen++;\r
+\r
+      char *newenv = (char *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, newlen);\r
+      if (! newenv) {\r
+        HeapFree(GetProcessHeap(), 0, env);\r
+        popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, "environment", "install()");\r
+        cleanup_nssm_service(service);\r
+        return 5;\r
+      }\r
+\r
+      for (i = 0, j = 0; i < envlen; i++) {\r
+        if (env[i] == '\r') continue;\r
+        if (env[i] == '\n') newenv[j] = '\0';\r
+        else newenv[j] = env[i];\r
+        j++;\r
+      }\r
+\r
+      HeapFree(GetProcessHeap(), 0, env);\r
+      env = newenv;\r
+      envlen = newlen;\r
+\r
+      /* Test the environment is valid. */\r
+      char path[MAX_PATH];\r
+      GetModuleFileName(0, path, sizeof(path));\r
+      STARTUPINFO si;\r
+      ZeroMemory(&si, sizeof(si));\r
+      si.cb = sizeof(si);\r
+      PROCESS_INFORMATION pi;\r
+      ZeroMemory(&pi, sizeof(pi));\r
+\r
+      if (! CreateProcess(0, path, 0, 0, 0, CREATE_SUSPENDED, env, 0, &si, &pi)) {\r
+        unsigned long error = GetLastError();\r
+        if (error == ERROR_INVALID_PARAMETER) {\r
+          popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_ENVIRONMENT);\r
+          HeapFree(GetProcessHeap(), 0, env);\r
+          envlen = 0;\r
+        }\r
+        cleanup_nssm_service(service);\r
+        return 5;\r
+      }\r
+      TerminateProcess(pi.hProcess, 0);\r
+\r
+      if (SendDlgItemMessage(tablist[NSSM_TAB_ENVIRONMENT], IDC_ENVIRONMENT_REPLACE, BM_GETCHECK, 0, 0) & BST_CHECKED) {\r
+        service->env = env;\r
+        service->envlen = envlen;\r
+      }\r
+      else {\r
+        service->env_extra = env;\r
+        service->env_extralen = envlen;\r
+      }\r
+    }\r
   }\r
 \r
   /* See if it works. */\r
@@ -426,6 +500,13 @@ INT_PTR CALLBACK install_dlg(HWND window, UINT message, WPARAM w, LPARAM l) {
       tablist[NSSM_TAB_IO] = CreateDialog(0, MAKEINTRESOURCE(IDD_IO), window, tab_dlg);\r
       ShowWindow(tablist[NSSM_TAB_IO], SW_HIDE);\r
 \r
+      /* Environment tab. */\r
+      tab.pszText = message_string(NSSM_GUI_TAB_ENVIRONMENT);\r
+      tab.cchTextMax = (int) strlen(tab.pszText) + 1;\r
+      SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_ENVIRONMENT, (LPARAM) &tab);\r
+      tablist[NSSM_TAB_ENVIRONMENT] = CreateDialog(0, MAKEINTRESOURCE(IDD_ENVIRONMENT), window, tab_dlg);\r
+      ShowWindow(tablist[NSSM_TAB_ENVIRONMENT], SW_HIDE);\r
+\r
       selected_tab = 0;\r
 \r
       return 1;\r
index eb893db..0fbfe95 100644 (file)
@@ -301,6 +301,19 @@ Errore durante la costruzione di ImagePath!\nQesto errore 
 oppure il mondo sta per finire oppure è accaduto qualcosa di ugualmente grave!
 .
 
+MessageId = +1
+SymbolicName = NSSM_GUI_INVALID_ENVIRONMENT
+Severity = Informational
+Language = English
+Environment should comprise strings of the form KEY=VALUE.
+.
+Language = French
+L'environnement devrait comprendre des chaînes sous la forme KEY=VALUE.
+.
+Language = Italian
+L'ambiente dovrebbe comprendere stringhe nella forma CHIAVE=VALORE.
+.
+
 MessageId = +1
 SymbolicName = NSSM_GUI_INSTALL_SERVICE_FAILED
 Severity = Informational
@@ -485,6 +498,19 @@ Language = Italian
 I/O
 .
 
+MessageId = +1
+SymbolicName = NSSM_GUI_TAB_ENVIRONMENT
+Severity = Informational
+Language = English
+Environment
+.
+Language = French
+Environnement
+.
+Language = Italian
+Ambiente
+.
+
 MessageId = +1
 SymbolicName = NSSM_GUI_EXIT_RESTART
 Severity = Informational
diff --git a/nssm.rc b/nssm.rc
index f5a5191..f825826 100644 (file)
--- a/nssm.rc
+++ b/nssm.rc
@@ -156,6 +156,16 @@ FONT 8, "MS Sans Serif"
     DEFPUSHBUTTON   "...", IDC_BROWSE_STDERR, 239, 47, 15, 14\r
 }\r
 \r
+LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL\r
+IDD_ENVIRONMENT DIALOG 9, 20, 261, 75\r
+STYLE DS_SHELLFONT | WS_VISIBLE | WS_CHILD | DS_CONTROL\r
+FONT 8, "MS Sans Serif"\r
+{\r
+    GROUPBOX        "Environment variables", IDC_STATIC, 7, 7, 251, 68\r
+    EDITTEXT        IDC_ENVIRONMENT, 13, 18, 238, 36, ES_AUTOHSCROLL | ES_AUTOVSCROLL | ES_MULTILINE | ES_WANTRETURN\r
+    AUTOCHECKBOX    "Replace default environment (srvany compatible)", IDC_ENVIRONMENT_REPLACE, 13, 60, 238, 8\r
+}\r
+\r
 \r
 /////////////////////////////////////////////////////////////////////////////\r
 //\r
index 382cb0f..458127c 100644 (file)
@@ -72,6 +72,18 @@ int create_parameters(nssm_service_t *service) {
   if (service->stdout_path[0]) set_expand_string(key, NSSM_REG_STDOUT, service->stdout_path);\r
   if (service->stderr_path[0]) set_expand_string(key, NSSM_REG_STDERR, service->stderr_path);\r
 \r
+  /* Environment */\r
+  if (service->env) {\r
+    if (RegSetValueEx(key, NSSM_REG_ENV, 0, REG_MULTI_SZ, (const unsigned char *) service->env, (unsigned long) service->envlen) != ERROR_SUCCESS) {\r
+      log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV, error_string(GetLastError()), 0);\r
+    }\r
+  }\r
+  if (service->env_extra) {\r
+    if (RegSetValueEx(key, NSSM_REG_ENV_EXTRA, 0, REG_MULTI_SZ, (const unsigned char *) service->env_extra, (unsigned long) service->env_extralen) != ERROR_SUCCESS) {\r
+      log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV_EXTRA, error_string(GetLastError()), 0);\r
+    }\r
+  }\r
+\r
   /* Close registry. */\r
   RegCloseKey(key);\r
 \r
index 8d9b821..63fb69e 100644 (file)
@@ -10,6 +10,7 @@
 #define IDD_IO            105\r
 #define IDD_APPEXIT            106\r
 #define IDD_SHUTDOWN            107\r
+#define IDD_ENVIRONMENT            108\r
 #define IDC_PATH                        1000\r
 #define IDC_TAB1                        1001\r
 #define IDC_CANCEL                      1002\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
 \r
 // Next default values for new objects\r
 // \r
 #ifdef APSTUDIO_INVOKED\r
 #ifndef APSTUDIO_READONLY_SYMBOLS\r
-#define _APS_NEXT_RESOURCE_VALUE        108\r
+#define _APS_NEXT_RESOURCE_VALUE        109\r
 #define _APS_NEXT_COMMAND_VALUE         40001\r
-#define _APS_NEXT_CONTROL_VALUE         1024\r
+#define _APS_NEXT_CONTROL_VALUE         1027\r
 #define _APS_NEXT_SYMED_VALUE           101\r
 #endif\r
 #endif\r