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 12 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
 Since version 2.19, many more service options can be configured with the
 GUI installer as well as via the registry.
 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
 Usage
 -----
 -----
@@ -230,6 +234,22 @@ work.  Remember, however, that the path must be accessible to the user
 running the service.
 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
 Removing services using the GUI
 -------------------------------
 -------------------------------
 NSSM can also remove services.  Run
 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
 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.
 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;
   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 type = REG_MULTI_SZ;
-  unsigned long envlen = 0;
 
 
   /* Dummy test to find buffer size */
   /* 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) {
   if (ret != ERROR_SUCCESS) {
+    *envlen = 0;
     /* The service probably doesn't have any environment configured */
     /* The service probably doesn't have any environment configured */
     if (ret == ERROR_FILE_NOT_FOUND) return 0;
     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;
     return 1;
   }
   }
 
 
   if (type != REG_MULTI_SZ) {
   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;
     return 2;
   }
   }
 
 
   /* Probably not possible */
   /* Probably not possible */
-  if (! envlen) return 0;
+  if (! *envlen) return 0;
 
 
   /* Previously initialised? */
   /* Previously initialised? */
   if (*env) HeapFree(GetProcessHeap(), 0, *env);
   if (*env) HeapFree(GetProcessHeap(), 0, *env);
 
 
-  *env = (char *) HeapAlloc(GetProcessHeap(), 0, envlen);
+  *env = (char *) HeapAlloc(GetProcessHeap(), 0, *envlen);
   if (! *env) {
   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;
     return 3;
   }
   }
 
 
   /* Actually get the strings */
   /* 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) {
   if (ret != ERROR_SUCCESS) {
+    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
     HeapFree(GetProcessHeap(), 0, *env);
     HeapFree(GetProcessHeap(), 0, *env);
     *env = 0;
     *env = 0;
-    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, NSSM_REG_ENV, error_string(GetLastError()), 0);
+    *envlen = 0;
     return 4;
     return 4;
   }
   }
 
 
@@ -326,7 +327,42 @@ int get_parameters(nssm_service_t *service, STARTUPINFO *si) {
   }
   }
 
 
   /* Try to get environment variables - may fail */
   /* 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 */
   /* Try to get stdout and stderr */
   if (get_output_handles(key, si)) {
   if (get_output_handles(key, si)) {

+ 2 - 1
registry.h

@@ -6,6 +6,7 @@
 #define NSSM_REG_FLAGS "AppParameters"
 #define NSSM_REG_FLAGS "AppParameters"
 #define NSSM_REG_DIR "AppDirectory"
 #define NSSM_REG_DIR "AppDirectory"
 #define NSSM_REG_ENV "AppEnvironment"
 #define NSSM_REG_ENV "AppEnvironment"
+#define NSSM_REG_ENV_EXTRA "AppEnvironmentExtra"
 #define NSSM_REG_EXIT "AppExit"
 #define NSSM_REG_EXIT "AppExit"
 #define NSSM_REG_THROTTLE "AppThrottle"
 #define NSSM_REG_THROTTLE "AppThrottle"
 #define NSSM_REG_STOP_METHOD_SKIP "AppStopMethodSkip"
 #define NSSM_REG_STOP_METHOD_SKIP "AppStopMethodSkip"
@@ -23,7 +24,7 @@
 int create_messages();
 int create_messages();
 int create_parameters(nssm_service_t *);
 int create_parameters(nssm_service_t *);
 int create_exit_action(char *, const char *);
 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, bool);
 int expand_parameter(HKEY, char *, char *, unsigned long, bool);
 int expand_parameter(HKEY, char *, char *, unsigned long, bool);
 int set_expand_string(HKEY, char *, char *);
 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) {
 void cleanup_nssm_service(nssm_service_t *service) {
   if (! service) return;
   if (! service) return;
   if (service->env) HeapFree(GetProcessHeap(), 0, service->env);
   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->handle) CloseServiceHandle(service->handle);
   if (service->process_handle) CloseHandle(service->process_handle);
   if (service->process_handle) CloseHandle(service->process_handle);
   if (service->wait_handle) UnregisterWait(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 flags[VALUE_LENGTH];
   char dir[MAX_PATH];
   char dir[MAX_PATH];
   char *env;
   char *env;
+  unsigned long envlen;
+  char *env_extra;
+  unsigned long env_extralen;
   char stdin_path[MAX_PATH];
   char stdin_path[MAX_PATH];
   char stdout_path[MAX_PATH];
   char stdout_path[MAX_PATH];
   char stderr_path[MAX_PATH];
   char stderr_path[MAX_PATH];