Allow appending to the service environment.
authorIain Patterson <me@iain.cx>
Thu, 21 Nov 2013 11:55:31 +0000 (11:55 +0000)
committerIain Patterson <me@iain.cx>
Thu, 21 Nov 2013 11:55:31 +0000 (11:55 +0000)
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
messages.mc
registry.cpp
registry.h
service.cpp
service.h

index f5e6ed8..3bf6ab9 100644 (file)
@@ -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\r
 GUI installer as well as via the registry.\r
 \r
+Since version 2.19, NSSM can add to the service's environment by setting\r
+AppEnvironmentExtra in place of or in addition to the srvany-compatible\r
+AppEnvironment.\r
+\r
 \r
 Usage\r
 -----\r
@@ -230,6 +234,22 @@ work.  Remember, however, that the path must be accessible to the user
 running the service.\r
 \r
 \r
+Environment variables\r
+---------------------\r
+NSSM can replace or append to the managed application's environment.  Two\r
+multi-valued string (REG_MULTI_SZ) registry values are recognised under\r
+HKLM\SYSTEM\CurrentControlSet\Services\<service>\Parameters.\r
+\r
+AppEnvironment defines a list of environment variables which will override\r
+the service's environment.  AppEnvironmentExtra defines a list of\r
+environment variables which will be added to the service's environment.\r
+\r
+Each entry in the list should be of the form KEY=VALUE.  It is possible to\r
+omit the VALUE but the = symbol is mandatory.\r
+\r
+srvany only supports AppEnvironment.\r
+\r
+\r
 Removing services using the GUI\r
 -------------------------------\r
 NSSM can also remove services.  Run\r
index f88da49..eb893db 100644 (file)
@@ -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:
+.
index 169cd08..382cb0f 100644 (file)
@@ -113,42 +113,43 @@ int create_exit_action(char *service_name, const char *action_string) {
   return 0;\r
 }\r
 \r
-int set_environment(char *service_name, HKEY key, char **env) {\r
+int set_environment(char *service_name, HKEY key, char *value, char **env, unsigned long *envlen) {\r
   unsigned long type = REG_MULTI_SZ;\r
-  unsigned long envlen = 0;\r
 \r
   /* Dummy test to find buffer size */\r
-  unsigned long ret = RegQueryValueEx(key, NSSM_REG_ENV, 0, &type, NULL, &envlen);\r
+  unsigned long ret = RegQueryValueEx(key, value, 0, &type, NULL, envlen);\r
   if (ret != ERROR_SUCCESS) {\r
+    *envlen = 0;\r
     /* The service probably doesn't have any environment configured */\r
     if (ret == ERROR_FILE_NOT_FOUND) return 0;\r
-    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, NSSM_REG_ENV, error_string(GetLastError()), 0);\r
+    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);\r
     return 1;\r
   }\r
 \r
   if (type != REG_MULTI_SZ) {\r
-    log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_INVALID_ENVIRONMENT_STRING_TYPE, NSSM_REG_ENV, service_name, 0);\r
+    log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_INVALID_ENVIRONMENT_STRING_TYPE, value, service_name, 0);\r
     return 2;\r
   }\r
 \r
   /* Probably not possible */\r
-  if (! envlen) return 0;\r
+  if (! *envlen) return 0;\r
 \r
   /* Previously initialised? */\r
   if (*env) HeapFree(GetProcessHeap(), 0, *env);\r
 \r
-  *env = (char *) HeapAlloc(GetProcessHeap(), 0, envlen);\r
+  *env = (char *) HeapAlloc(GetProcessHeap(), 0, *envlen);\r
   if (! *env) {\r
-    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "environment registry", "set_environment()", 0);\r
+    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, value, "set_environment()", 0);\r
     return 3;\r
   }\r
 \r
   /* Actually get the strings */\r
-  ret = RegQueryValueEx(key, NSSM_REG_ENV, 0, &type, (unsigned char *) *env, &envlen);\r
+  ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) *env, envlen);\r
   if (ret != ERROR_SUCCESS) {\r
+    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);\r
     HeapFree(GetProcessHeap(), 0, *env);\r
     *env = 0;\r
-    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, NSSM_REG_ENV, error_string(GetLastError()), 0);\r
+    *envlen = 0;\r
     return 4;\r
   }\r
 \r
@@ -326,7 +327,42 @@ int get_parameters(nssm_service_t *service, STARTUPINFO *si) {
   }\r
 \r
   /* Try to get environment variables - may fail */\r
-  set_environment(service->name, key, &service->env);\r
+  set_environment(service->name, key, NSSM_REG_ENV, &service->env, &service->envlen);\r
+  /* Environment variables to add to existing rather than replace - may fail. */\r
+  set_environment(service->name, key, NSSM_REG_ENV_EXTRA, &service->env_extra, &service->env_extralen);\r
+\r
+  if (service->env_extra) {\r
+    /* Append these to any other environment variables set. */\r
+    if (service->env) {\r
+      /* Append extra variables to configured variables. */\r
+      unsigned long envlen = service->envlen + service->env_extralen - 1;\r
+      char *env = (char *) HeapAlloc(GetProcessHeap(), 0, envlen);\r
+      if (env) {\r
+        memmove(env, service->env, service->envlen - 1);\r
+        memmove(env + service->envlen - 1, service->env_extra, service->env_extralen);\r
+\r
+        HeapFree(GetProcessHeap(), 0, service->env);\r
+        service->env = env;\r
+        service->envlen = envlen;\r
+      }\r
+      else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "environment", "get_parameters()", 0);\r
+    }\r
+    else {\r
+      /* Append extra variables to our environment. */\r
+      char *env, *s;\r
+      size_t envlen, len;\r
+\r
+      env = service->env_extra;\r
+      len = 0;\r
+      while (*env) {\r
+        envlen = strlen(env) + 1;\r
+        for (s = env; *s && *s != '='; s++);\r
+        if (*s == '=') *s++ = '\0';\r
+        if (! SetEnvironmentVariable(env, s)) log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_SETENVIRONMENTVARIABLE_FAILED, env, s, error_string(GetLastError()));\r
+        env += envlen;\r
+      }\r
+    }\r
+  }\r
 \r
   /* Try to get stdout and stderr */\r
   if (get_output_handles(key, si)) {\r
index 0661b73..3c64b5d 100644 (file)
@@ -6,6 +6,7 @@
 #define NSSM_REG_FLAGS "AppParameters"\r
 #define NSSM_REG_DIR "AppDirectory"\r
 #define NSSM_REG_ENV "AppEnvironment"\r
+#define NSSM_REG_ENV_EXTRA "AppEnvironmentExtra"\r
 #define NSSM_REG_EXIT "AppExit"\r
 #define NSSM_REG_THROTTLE "AppThrottle"\r
 #define NSSM_REG_STOP_METHOD_SKIP "AppStopMethodSkip"\r
@@ -23,7 +24,7 @@
 int create_messages();\r
 int create_parameters(nssm_service_t *);\r
 int create_exit_action(char *, const char *);\r
-int set_environment(char *, HKEY, char **);\r
+int set_environment(char *, HKEY, char *, char **, unsigned long *);\r
 int expand_parameter(HKEY, char *, char *, unsigned long, bool, bool);\r
 int expand_parameter(HKEY, char *, char *, unsigned long, bool);\r
 int set_expand_string(HKEY, char *, char *);\r
index 906ac64..ea5a460 100644 (file)
@@ -43,6 +43,7 @@ nssm_service_t *alloc_nssm_service() {
 void cleanup_nssm_service(nssm_service_t *service) {\r
   if (! service) return;\r
   if (service->env) HeapFree(GetProcessHeap(), 0, service->env);\r
+  if (service->env_extra) HeapFree(GetProcessHeap(), 0, service->env_extra);\r
   if (service->handle) CloseServiceHandle(service->handle);\r
   if (service->process_handle) CloseHandle(service->process_handle);\r
   if (service->wait_handle) UnregisterWait(service->process_handle);\r
index e9f36ba..5c56ed5 100644 (file)
--- a/service.h
+++ b/service.h
@@ -22,6 +22,9 @@ typedef struct {
   char flags[VALUE_LENGTH];\r
   char dir[MAX_PATH];\r
   char *env;\r
+  unsigned long envlen;\r
+  char *env_extra;\r
+  unsigned long env_extralen;\r
   char stdin_path[MAX_PATH];\r
   char stdout_path[MAX_PATH];\r
   char stderr_path[MAX_PATH];\r