From 277723e318f71a9ff9948e3286df509af79c1dc8 Mon Sep 17 00:00:00 2001 From: Iain Patterson Date: Thu, 21 Nov 2013 18:39:07 +0000 Subject: [PATCH] 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. --- ChangeLog.txt | 3 +++ gui.cpp | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- messages.mc | 26 ++++++++++++++++++ nssm.rc | 10 +++++++ registry.cpp | 12 +++++++++ resource.h | 7 +++-- 6 files changed, 139 insertions(+), 4 deletions(-) diff --git a/ChangeLog.txt b/ChangeLog.txt index 668cebb..e7cff08 100644 --- a/ChangeLog.txt +++ b/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 diff --git a/gui.cpp b/gui.cpp index 013f2f0..4bcb144 100644 --- a/gui.cpp +++ b/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; diff --git a/messages.mc b/messages.mc index eb893db..0fbfe95 100644 --- a/messages.mc +++ b/messages.mc @@ -302,6 +302,19 @@ oppure il mondo sta per finire oppure . 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 Language = English @@ -486,6 +499,19 @@ 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 Language = English diff --git a/nssm.rc b/nssm.rc index f5a5191..f825826 100644 --- a/nssm.rc +++ b/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 +} + ///////////////////////////////////////////////////////////////////////////// // diff --git a/registry.cpp b/registry.cpp index 382cb0f..458127c 100644 --- a/registry.cpp +++ b/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); diff --git a/resource.h b/resource.h index 8d9b821..63fb69e 100644 --- a/resource.h +++ b/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 -- 2.7.4