From 728c4f6eb96313e764600810aa754663a5091f38 Mon Sep 17 00:00:00 2001 From: Iain Patterson Date: Thu, 21 Nov 2013 11:55:31 +0000 Subject: [PATCH] Allow appending to the service environment. The new registry value AppEnvironmentExtra allows defining a list of environment variables which will be appended to the environment, as opposed to those defined by the srvany-compatible AppEnvironment, which replaces the environment. Though it would serve no useful purpose beyond verifying whether or not I went to the effort of writing a code path for it, it is possible to define bother lists and have them behave intuitively. --- README.txt | 20 ++++++++++++++++++++ messages.mc | 15 +++++++++++++++ registry.cpp | 58 +++++++++++++++++++++++++++++++++++++++++++++++----------- registry.h | 3 ++- service.cpp | 1 + service.h | 3 +++ 6 files changed, 88 insertions(+), 12 deletions(-) diff --git a/README.txt b/README.txt index f5e6ed8..3bf6ab9 100644 --- a/README.txt +++ b/README.txt @@ -49,6 +49,10 @@ of time for the application to exit when shutting down. Since version 2.19, many more service options can be configured with the GUI installer as well as via the registry. +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. + Usage ----- @@ -230,6 +234,22 @@ work. Remember, however, that the path must be accessible to the user running the service. +Environment variables +--------------------- +NSSM can replace or append to the managed application's environment. Two +multi-valued string (REG_MULTI_SZ) registry values are recognised under +HKLM\SYSTEM\CurrentControlSet\Services\\Parameters. + +AppEnvironment defines a list of environment variables which will override +the service's environment. AppEnvironmentExtra defines a list of +environment variables which will be added to the service's environment. + +Each entry in the list should be of the form KEY=VALUE. It is possible to +omit the VALUE but the = symbol is mandatory. + +srvany only supports AppEnvironment. + + Removing services using the GUI ------------------------------- NSSM can also remove services. Run diff --git a/messages.mc b/messages.mc index f88da49..eb893db 100644 --- a/messages.mc +++ b/messages.mc @@ -1493,3 +1493,18 @@ The minimum number of milliseconds which must pass before service %1 is consider Language = Italian The minimum number of milliseconds which must pass before service %1 is considered to have been started successfully is set to %2. Access to the Windows service control manager is blocked until the service updates its status, therefore %3 will wait a maximum of %4 milliseconds before reporting the service's state as running. Service restart throttling will be enforced if the service runs for less than the full %2 milliseconds. . + +MessageId = +1 +SymbolicName = NSSM_EVENT_SETENVIRONMENTVARIABLE_FAILED +Severity = Warning +Language = English +SetEnvironmentVariable(%1=%2) failed: +%3 +. +Language = French +SetEnvironmentVariable(%1=%2) a échoué: +%3 +. +Language = Italian +Chiamata a SetEnvironmentVariable(%1=%2) fallita: +. diff --git a/registry.cpp b/registry.cpp index 169cd08..382cb0f 100644 --- a/registry.cpp +++ b/registry.cpp @@ -113,42 +113,43 @@ int create_exit_action(char *service_name, const char *action_string) { return 0; } -int set_environment(char *service_name, HKEY key, char **env) { +int set_environment(char *service_name, HKEY key, char *value, char **env, unsigned long *envlen) { unsigned long type = REG_MULTI_SZ; - unsigned long envlen = 0; /* Dummy test to find buffer size */ - unsigned long ret = RegQueryValueEx(key, NSSM_REG_ENV, 0, &type, NULL, &envlen); + unsigned long ret = RegQueryValueEx(key, value, 0, &type, NULL, envlen); if (ret != ERROR_SUCCESS) { + *envlen = 0; /* The service probably doesn't have any environment configured */ if (ret == ERROR_FILE_NOT_FOUND) return 0; - log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, NSSM_REG_ENV, error_string(GetLastError()), 0); + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0); return 1; } if (type != REG_MULTI_SZ) { - log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_INVALID_ENVIRONMENT_STRING_TYPE, NSSM_REG_ENV, service_name, 0); + log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_INVALID_ENVIRONMENT_STRING_TYPE, value, service_name, 0); return 2; } /* Probably not possible */ - if (! envlen) return 0; + if (! *envlen) return 0; /* Previously initialised? */ if (*env) HeapFree(GetProcessHeap(), 0, *env); - *env = (char *) HeapAlloc(GetProcessHeap(), 0, envlen); + *env = (char *) HeapAlloc(GetProcessHeap(), 0, *envlen); if (! *env) { - log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "environment registry", "set_environment()", 0); + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, value, "set_environment()", 0); return 3; } /* Actually get the strings */ - ret = RegQueryValueEx(key, NSSM_REG_ENV, 0, &type, (unsigned char *) *env, &envlen); + ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) *env, envlen); if (ret != ERROR_SUCCESS) { + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0); HeapFree(GetProcessHeap(), 0, *env); *env = 0; - log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, NSSM_REG_ENV, error_string(GetLastError()), 0); + *envlen = 0; return 4; } @@ -326,7 +327,42 @@ int get_parameters(nssm_service_t *service, STARTUPINFO *si) { } /* Try to get environment variables - may fail */ - set_environment(service->name, key, &service->env); + set_environment(service->name, key, NSSM_REG_ENV, &service->env, &service->envlen); + /* Environment variables to add to existing rather than replace - may fail. */ + set_environment(service->name, key, NSSM_REG_ENV_EXTRA, &service->env_extra, &service->env_extralen); + + if (service->env_extra) { + /* Append these to any other environment variables set. */ + if (service->env) { + /* Append extra variables to configured variables. */ + unsigned long envlen = service->envlen + service->env_extralen - 1; + char *env = (char *) HeapAlloc(GetProcessHeap(), 0, envlen); + if (env) { + memmove(env, service->env, service->envlen - 1); + memmove(env + service->envlen - 1, service->env_extra, service->env_extralen); + + HeapFree(GetProcessHeap(), 0, service->env); + service->env = env; + service->envlen = envlen; + } + else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "environment", "get_parameters()", 0); + } + else { + /* Append extra variables to our environment. */ + char *env, *s; + size_t envlen, len; + + env = service->env_extra; + len = 0; + while (*env) { + envlen = strlen(env) + 1; + for (s = env; *s && *s != '='; s++); + if (*s == '=') *s++ = '\0'; + if (! SetEnvironmentVariable(env, s)) log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_SETENVIRONMENTVARIABLE_FAILED, env, s, error_string(GetLastError())); + env += envlen; + } + } + } /* Try to get stdout and stderr */ if (get_output_handles(key, si)) { diff --git a/registry.h b/registry.h index 0661b73..3c64b5d 100644 --- a/registry.h +++ b/registry.h @@ -6,6 +6,7 @@ #define NSSM_REG_FLAGS "AppParameters" #define NSSM_REG_DIR "AppDirectory" #define NSSM_REG_ENV "AppEnvironment" +#define NSSM_REG_ENV_EXTRA "AppEnvironmentExtra" #define NSSM_REG_EXIT "AppExit" #define NSSM_REG_THROTTLE "AppThrottle" #define NSSM_REG_STOP_METHOD_SKIP "AppStopMethodSkip" @@ -23,7 +24,7 @@ int create_messages(); int create_parameters(nssm_service_t *); int create_exit_action(char *, const char *); -int set_environment(char *, HKEY, char **); +int set_environment(char *, HKEY, char *, char **, unsigned long *); int expand_parameter(HKEY, char *, char *, unsigned long, bool, bool); int expand_parameter(HKEY, char *, char *, unsigned long, bool); int set_expand_string(HKEY, char *, char *); diff --git a/service.cpp b/service.cpp index 906ac64..ea5a460 100644 --- a/service.cpp +++ b/service.cpp @@ -43,6 +43,7 @@ nssm_service_t *alloc_nssm_service() { void cleanup_nssm_service(nssm_service_t *service) { if (! service) return; if (service->env) HeapFree(GetProcessHeap(), 0, service->env); + if (service->env_extra) HeapFree(GetProcessHeap(), 0, service->env_extra); if (service->handle) CloseServiceHandle(service->handle); if (service->process_handle) CloseHandle(service->process_handle); if (service->wait_handle) UnregisterWait(service->process_handle); diff --git a/service.h b/service.h index e9f36ba..5c56ed5 100644 --- a/service.h +++ b/service.h @@ -22,6 +22,9 @@ typedef struct { char flags[VALUE_LENGTH]; char dir[MAX_PATH]; char *env; + unsigned long envlen; + char *env_extra; + unsigned long env_extralen; char stdin_path[MAX_PATH]; char stdout_path[MAX_PATH]; char stderr_path[MAX_PATH]; -- 2.7.4