Browse Source

Set environment from the GUI at install time.

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.
Iain Patterson 9 years ago
parent
commit
277723e318
6 changed files with 139 additions and 4 deletions
  1. 3 0
      ChangeLog.txt
  2. 83 2
      gui.cpp
  3. 26 0
      messages.mc
  4. 10 0
      nssm.rc
  5. 12 0
      registry.cpp
  6. 5 2
      resource.h

+ 3 - 0
ChangeLog.txt

@@ -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

+ 83 - 2
gui.cpp

@@ -1,6 +1,6 @@
 #include "nssm.h"
 
-static enum { NSSM_TAB_APPLICATION, NSSM_TAB_SHUTDOWN, NSSM_TAB_EXIT, NSSM_TAB_IO, NSSM_NUM_TABS };
+static enum { NSSM_TAB_APPLICATION, NSSM_TAB_SHUTDOWN, NSSM_TAB_EXIT, NSSM_TAB_IO, NSSM_TAB_ENVIRONMENT, NSSM_NUM_TABS };
 static HWND tablist[NSSM_NUM_TABS];
 static int selected_tab;
 
@@ -120,7 +120,7 @@ int install(HWND window) {
       if (! GetDlgItemText(window, IDC_FLAGS, service->flags, sizeof(service->flags))) {
         popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_OPTIONS);
         return 4;
-      }
+      }
     }
 
     /* Get stop method stuff. */
@@ -143,6 +143,80 @@ int install(HWND window) {
     check_io("stdin", service->stdin_path, sizeof(service->stdin_path), IDC_STDIN);
     check_io("stdout", service->stdout_path, sizeof(service->stdout_path), IDC_STDOUT);
     check_io("stderr", service->stderr_path, sizeof(service->stderr_path), IDC_STDERR);
+
+    /* Get environment. */
+    unsigned long envlen = (unsigned long) SendMessage(GetDlgItem(tablist[NSSM_TAB_ENVIRONMENT], IDC_ENVIRONMENT), WM_GETTEXTLENGTH, 0, 0);
+    if (envlen) {
+      char *env = (char *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, envlen + 2);
+      if (! env) {
+        popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, "environment", "install()");
+        cleanup_nssm_service(service);
+        return 5;
+      }
+
+      if (! GetDlgItemText(tablist[NSSM_TAB_ENVIRONMENT], IDC_ENVIRONMENT, env, envlen + 1)) {
+        popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_ENVIRONMENT);
+        HeapFree(GetProcessHeap(), 0, env);
+        cleanup_nssm_service(service);
+        return 5;
+      }
+
+      /* Strip CR and replace LF with NULL. */
+      unsigned long newlen = 0;
+      unsigned long i, j;
+      for (i = 0; i < envlen; i++) if (env[i] != '\r') newlen++;
+      /* Must end with two NULLs. */
+      newlen++;
+
+      char *newenv = (char *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, newlen);
+      if (! newenv) {
+        HeapFree(GetProcessHeap(), 0, env);
+        popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, "environment", "install()");
+        cleanup_nssm_service(service);
+        return 5;
+      }
+
+      for (i = 0, j = 0; i < envlen; i++) {
+        if (env[i] == '\r') continue;
+        if (env[i] == '\n') newenv[j] = '\0';
+        else newenv[j] = env[i];
+        j++;
+      }
+
+      HeapFree(GetProcessHeap(), 0, env);
+      env = newenv;
+      envlen = newlen;
+
+      /* Test the environment is valid. */
+      char path[MAX_PATH];
+      GetModuleFileName(0, path, sizeof(path));
+      STARTUPINFO si;
+      ZeroMemory(&si, sizeof(si));
+      si.cb = sizeof(si);
+      PROCESS_INFORMATION pi;
+      ZeroMemory(&pi, sizeof(pi));
+
+      if (! CreateProcess(0, path, 0, 0, 0, CREATE_SUSPENDED, env, 0, &si, &pi)) {
+        unsigned long error = GetLastError();
+        if (error == ERROR_INVALID_PARAMETER) {
+          popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_ENVIRONMENT);
+          HeapFree(GetProcessHeap(), 0, env);
+          envlen = 0;
+        }
+        cleanup_nssm_service(service);
+        return 5;
+      }
+      TerminateProcess(pi.hProcess, 0);
+
+      if (SendDlgItemMessage(tablist[NSSM_TAB_ENVIRONMENT], IDC_ENVIRONMENT_REPLACE, BM_GETCHECK, 0, 0) & BST_CHECKED) {
+        service->env = env;
+        service->envlen = envlen;
+      }
+      else {
+        service->env_extra = env;
+        service->env_extralen = envlen;
+      }
+    }
   }
 
   /* See if it works. */
@@ -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);
       ShowWindow(tablist[NSSM_TAB_IO], SW_HIDE);
 
+      /* Environment tab. */
+      tab.pszText = message_string(NSSM_GUI_TAB_ENVIRONMENT);
+      tab.cchTextMax = (int) strlen(tab.pszText) + 1;
+      SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_ENVIRONMENT, (LPARAM) &tab);
+      tablist[NSSM_TAB_ENVIRONMENT] = CreateDialog(0, MAKEINTRESOURCE(IDD_ENVIRONMENT), window, tab_dlg);
+      ShowWindow(tablist[NSSM_TAB_ENVIRONMENT], SW_HIDE);
+
       selected_tab = 0;
 
       return 1;

+ 26 - 0
messages.mc

@@ -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

+ 10 - 0
nssm.rc

@@ -156,6 +156,16 @@ FONT 8, "MS Sans Serif"
     DEFPUSHBUTTON   "...", IDC_BROWSE_STDERR, 239, 47, 15, 14
 }
 
+LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
+IDD_ENVIRONMENT DIALOG 9, 20, 261, 75
+STYLE DS_SHELLFONT | WS_VISIBLE | WS_CHILD | DS_CONTROL
+FONT 8, "MS Sans Serif"
+{
+    GROUPBOX        "Environment variables", IDC_STATIC, 7, 7, 251, 68
+    EDITTEXT        IDC_ENVIRONMENT, 13, 18, 238, 36, ES_AUTOHSCROLL | ES_AUTOVSCROLL | ES_MULTILINE | ES_WANTRETURN
+    AUTOCHECKBOX    "Replace default environment (srvany compatible)", IDC_ENVIRONMENT_REPLACE, 13, 60, 238, 8
+}
+
 
 /////////////////////////////////////////////////////////////////////////////
 //

+ 12 - 0
registry.cpp

@@ -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);
   if (service->stderr_path[0]) set_expand_string(key, NSSM_REG_STDERR, service->stderr_path);
 
+  /* Environment */
+  if (service->env) {
+    if (RegSetValueEx(key, NSSM_REG_ENV, 0, REG_MULTI_SZ, (const unsigned char *) service->env, (unsigned long) service->envlen) != ERROR_SUCCESS) {
+      log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV, error_string(GetLastError()), 0);
+    }
+  }
+  if (service->env_extra) {
+    if (RegSetValueEx(key, NSSM_REG_ENV_EXTRA, 0, REG_MULTI_SZ, (const unsigned char *) service->env_extra, (unsigned long) service->env_extralen) != ERROR_SUCCESS) {
+      log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV_EXTRA, error_string(GetLastError()), 0);
+    }
+  }
+
   /* Close registry. */
   RegCloseKey(key);
 

+ 5 - 2
resource.h

@@ -10,6 +10,7 @@
 #define IDD_IO            105
 #define IDD_APPEXIT            106
 #define IDD_SHUTDOWN            107
+#define IDD_ENVIRONMENT            108
 #define IDC_PATH                        1000
 #define IDC_TAB1                        1001
 #define IDC_CANCEL                      1002
@@ -34,14 +35,16 @@
 #define IDC_APPEXIT                     1022
 #define IDC_DIR                         1023
 #define IDC_BROWSE_DIR                  1024
+#define IDC_ENVIRONMENT                 1025
+#define IDC_ENVIRONMENT_REPLACE         1026
 
 // Next default values for new objects
 // 
 #ifdef APSTUDIO_INVOKED
 #ifndef APSTUDIO_READONLY_SYMBOLS
-#define _APS_NEXT_RESOURCE_VALUE        108
+#define _APS_NEXT_RESOURCE_VALUE        109
 #define _APS_NEXT_COMMAND_VALUE         40001
-#define _APS_NEXT_CONTROL_VALUE         1024
+#define _APS_NEXT_CONTROL_VALUE         1027
 #define _APS_NEXT_SYMED_VALUE           101
 #endif
 #endif