Browse Source

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.
Iain Patterson 9 years ago
parent
commit
728c4f6eb9
6 changed files with 88 additions and 12 deletions
  1. 20 0
      README.txt
  2. 15 0
      messages.mc
  3. 47 11
      registry.cpp
  4. 2 1
      registry.h
  5. 1 0
      service.cpp
  6. 3 0
      service.h

+ 20 - 0
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\<service>\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

+ 15 - 0
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:
+.

+ 47 - 11
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)) {

+ 2 - 1
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 *);

+ 1 - 0
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);

+ 3 - 0
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];