Support srvany's AppEnvironment registry value.
authorIain Patterson <me@iain.cx>
Sun, 1 Apr 2012 10:35:54 +0000 (11:35 +0100)
committerIain Patterson <me@iain.cx>
Wed, 4 Apr 2012 16:41:13 +0000 (17:41 +0100)
Allow setting one or more environment variables for the application by
creating the AppEnvironment value of type REG_MULTI_SZ.  Each string
should be of the form KEY=VALUE.  VALUE may be omitted to set an empty
environment variable but the equals sign must be present.

This support is for compatibility with srvany.  In accordance with the
documented behaviour of srvany, only environment variables specified in
AppEnvironment will be passed to the service.  Any other variables, even
system variables such as %PATH%, will be ignored if they are not
explicitly listed.

Note that Windows supports adding environment variables to the service's
existing environment by creating the Environment value (also of type
REG_MULTI_SZ) under HKLM\SYSTEM\CurrentControlSet\Services\<service>
instead.  It is recommended that new services use this standard
functionality rather than AppEnvironment.

Thanks Rob Sharp.

README.txt
messages.mc
registry.cpp
registry.h
service.cpp

index 590e396..5494575 100644 (file)
@@ -180,6 +180,8 @@ registry value for AppDirectory confused NSSM.
 Thanks to Peter Wagemans and Laszlo Keresztfalvi for suggesting throttling restarts.\r
 Thanks to Eugene Lifshitz for finding an edge case in CreateProcess() and for\r
 advising how to build messages.mc correctly in paths containing spaces.\r
+Thanks to Rob Sharp for pointing out that NSSM did not respect the\r
+AppEnvironment registry value used by srvany.\r
 \r
 Licence\r
 -------\r
index 07ed432..28f12e8 100644 (file)
@@ -290,3 +290,18 @@ Failed to create waitable timer for service %1:
 Throttled restarts will not be interruptible.
 .
 
+MessageId = +1
+SymbolicName = NSSM_EVENT_CREATEPROCESS_FAILED_INVALID_ENVIRONMENT
+Severity = Error
+Language = English
+Failed to start service %1.  Program %2 couldn't be launched.
+CreateProcess() failed with ERROR_INVALID_PARAMETER and a process environment was set in the %3 registry value.  It is likely that the environment was incorrectly specified.  %3 should be a REG_MULTI_SZ value comprising strings of the form KEY=VALUE.
+.
+
+MessageId = +1
+SymbolicName = NSSM_EVENT_INVALID_ENVIRONMENT_STRING_TYPE
+Severity = Warning
+Language = English
+Environment declaration %1 for service %2 is not of type REG_MULTI_SZ and will be ignored.
+.
+
index 6b70a96..5172c50 100644 (file)
@@ -102,6 +102,45 @@ 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
+  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
+  if (ret != ERROR_SUCCESS) {\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
+    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
+    return 2;\r
+  }\r
+\r
+  /* Probably not possible */\r
+  if (! envlen) return 0;\r
+\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
+    return 3;\r
+  }\r
+\r
+  /* Actually get the strings */\r
+  ret = RegQueryValueEx(key, NSSM_REG_ENV, 0, &type, (unsigned char *) *env, &envlen);\r
+  if (ret != ERROR_SUCCESS) {\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
+    return 4;\r
+  }\r
+\r
+  return 0;\r
+}\r
+\r
 int expand_parameter(HKEY key, char *value, char *data, unsigned long datalen, bool sanitise) {\r
   unsigned char *buffer = (unsigned char *) HeapAlloc(GetProcessHeap(), 0, datalen);\r
   if (! buffer) {\r
@@ -142,7 +181,7 @@ int expand_parameter(HKEY key, char *value, char *data, unsigned long datalen, b
   return 0;\r
 }\r
 \r
-int get_parameters(char *service_name, char *exe, int exelen, char *flags, int flagslen, char *dir, int dirlen, unsigned long *throttle_delay) {\r
+int get_parameters(char *service_name, char *exe, int exelen, char *flags, int flagslen, char *dir, int dirlen, char **env, unsigned long *throttle_delay) {\r
   unsigned long ret;\r
 \r
   /* Get registry */\r
@@ -192,6 +231,9 @@ int get_parameters(char *service_name, char *exe, int exelen, char *flags, int f
     log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_DIR, NSSM_REG_DIR, service_name, dir, 0);\r
   }\r
 \r
+  /* Try to get environment variables - may fail */\r
+  set_environment(service_name, key, env);\r
+\r
   /* Try to get throttle restart delay */\r
   unsigned long type = REG_DWORD;\r
   unsigned long buflen = sizeof(*throttle_delay);\r
index 261b0e9..aae45ac 100644 (file)
@@ -5,14 +5,16 @@
 #define NSSM_REG_EXE "Application"\r
 #define NSSM_REG_FLAGS "AppParameters"\r
 #define NSSM_REG_DIR "AppDirectory"\r
+#define NSSM_REG_ENV "AppEnvironment"\r
 #define NSSM_REG_EXIT "AppExit"\r
 #define NSSM_REG_THROTTLE "AppThrottle"\r
 \r
 int create_messages();\r
 int create_parameters(char *, char *, char *, char *);\r
 int create_exit_action(char *, const char *);\r
+int set_environment(char *, HKEY, char **);\r
 int expand_parameter(HKEY, char *, char *, unsigned long, bool);\r
-int get_parameters(char *, char *, int, char *, int, char *, int, unsigned long *);\r
+int get_parameters(char *, char *, int, char *, int, char *, int, char **, unsigned long *);\r
 int get_exit_action(char *, unsigned long *, unsigned char *, bool *);\r
 \r
 #endif\r
index 4011484..ac329bc 100644 (file)
@@ -300,7 +300,8 @@ int start_service() {
   ZeroMemory(&pi, sizeof(pi));\r
 \r
   /* Get startup parameters */\r
-  int ret = get_parameters(service_name, exe, sizeof(exe), flags, sizeof(flags), dir, sizeof(dir), &throttle_delay);\r
+  char *env = 0;\r
+  int ret = get_parameters(service_name, exe, sizeof(exe), flags, sizeof(flags), dir, sizeof(dir), &env, &throttle_delay);\r
   if (ret) {\r
     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service_name, 0);\r
     return stop_service(2, true, true);\r
@@ -315,8 +316,10 @@ int start_service() {
 \r
   throttle_restart();\r
 \r
-  if (! CreateProcess(0, cmd, 0, 0, false, 0, 0, dir, &si, &pi)) {\r
-    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service_name, exe, error_string(GetLastError()), 0);\r
+  if (! CreateProcess(0, cmd, 0, 0, false, 0, env, dir, &si, &pi)) {\r
+    unsigned long error = GetLastError();\r
+    if (error == ERROR_INVALID_PARAMETER && env) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED_INVALID_ENVIRONMENT, service_name, exe, NSSM_REG_ENV, 0);\r
+    else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service_name, exe, error_string(error), 0);\r
     return stop_service(3, true, true);\r
   }\r
   process_handle = pi.hProcess;\r