Don't mangle the input to split_hook_name().
[nssm.git] / settings.cpp
index f50dcc4..c825fbc 100644 (file)
@@ -3,14 +3,18 @@
 \r
 /* Affinity. */\r
 #define NSSM_AFFINITY_ALL _T("All")\r
+/* Default value. */\r
+#define NSSM_DEFAULT_STRING _T("Default")\r
 \r
 extern const TCHAR *exit_action_strings[];\r
 extern const TCHAR *startup_strings[];\r
 extern const TCHAR *priority_strings[];\r
+extern const TCHAR *hook_event_strings[];\r
+extern const TCHAR *hook_action_strings[];\r
 \r
 /* Does the parameter refer to the default value of the setting? */\r
 static inline int is_default(const TCHAR *value) {\r
-  return (str_equiv(value, _T("default")) || str_equiv(value, _T("*")) || ! value[0]);\r
+  return (str_equiv(value, NSSM_DEFAULT_STRING) || str_equiv(value, _T("*")) || ! value[0]);\r
 }\r
 \r
 static int value_from_string(const TCHAR *name, value_t *value, const TCHAR *string) {\r
@@ -193,7 +197,8 @@ static inline bool split_hook_name(const TCHAR *hook_name, TCHAR *hook_event, TC
     if (*s == _T('/')) {\r
       *s = _T('\0');\r
       _sntprintf_s(hook_event, HOOK_NAME_LENGTH, _TRUNCATE, _T("%s"), hook_name);\r
-      _sntprintf_s(hook_action, HOOK_NAME_LENGTH, _TRUNCATE, _T("%s"), ++s);\r
+      *s++ = _T('/');\r
+      _sntprintf_s(hook_action, HOOK_NAME_LENGTH, _TRUNCATE, _T("%s"), s);\r
       return valid_hook_name(hook_event, hook_action, false);\r
     }\r
   }\r
@@ -339,17 +344,54 @@ static int setting_set_environment(const TCHAR *service_name, void *param, const
   HKEY key = (HKEY) param;\r
   if (! param) return -1;\r
 \r
-  if (! value || ! value->string || ! value->string[0]) {\r
+  TCHAR *string = 0;\r
+  TCHAR *unformatted = 0;\r
+  unsigned long envlen;\r
+  unsigned long newlen = 0;\r
+  int op = 0;\r
+  if (value && value->string && value->string[0]) {\r
+    string = value->string;\r
+    switch (string[0]) {\r
+      case _T('+'): op = 1; break;\r
+      case _T('-'): op = -1; break;\r
+      case _T(':'): string++; break;\r
+    }\r
+  }\r
+\r
+  if (op) {\r
+    string++;\r
+    TCHAR *env = 0;\r
+    if (get_environment((TCHAR *) service_name, key, (TCHAR *) name, &env, &envlen)) return -1;\r
+    if (env) {\r
+      int ret;\r
+      if (op > 0) ret = append_to_environment_block(env, envlen, string, &unformatted, &newlen);\r
+      else ret = remove_from_environment_block(env, envlen, string, &unformatted, &newlen);\r
+      if (envlen) HeapFree(GetProcessHeap(), 0, env);\r
+      if (ret) return -1;\r
+\r
+      string = unformatted;\r
+    }\r
+    else {\r
+      /*\r
+        No existing environment.\r
+        We can't remove from an empty environment so just treat an add\r
+        operation as setting a new string.\r
+      */\r
+      if (op < 0) return 0;\r
+      op = 0;\r
+    }\r
+  }\r
+\r
+  if (! string || ! string[0]) {\r
     long error = RegDeleteValue(key, name);\r
     if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;\r
     print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));\r
     return -1;\r
   }\r
 \r
-  unsigned long envlen = (unsigned long) _tcslen(value->string) + 1;\r
-  TCHAR *unformatted = 0;\r
-  unsigned long newlen;\r
-  if (unformat_double_null(value->string, envlen, &unformatted, &newlen)) return -1;\r
+  if (! op) {\r
+    if (unformat_double_null(string, (unsigned long) _tcslen(string), &unformatted, &newlen)) return -1;\r
+  }\r
 \r
   if (test_environment(unformatted)) {\r
     HeapFree(GetProcessHeap(), 0, unformatted);\r
@@ -462,6 +504,92 @@ static int setting_get_priority(const TCHAR *service_name, void *param, const TC
 }\r
 \r
 /* Functions to manage native service settings. */\r
+static int native_set_dependon(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR **dependencies, unsigned long *dependencieslen, value_t *value, int type) {\r
+  *dependencieslen = 0;\r
+  if (! value || ! value->string || ! value->string[0]) return 0;\r
+\r
+  TCHAR *string = value->string;\r
+  unsigned long buflen;\r
+  int op = 0;\r
+  switch (string[0]) {\r
+    case _T('+'): op = 1; break;\r
+    case _T('-'): op = -1; break;\r
+    case _T(':'): string++; break;\r
+  }\r
+\r
+  if (op) {\r
+    string++;\r
+    TCHAR *buffer = 0;\r
+    if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, type)) return -1;\r
+    if (buffer) {\r
+      int ret;\r
+      if (op > 0) ret = append_to_dependencies(buffer, buflen, string, dependencies, dependencieslen, type);\r
+      else ret = remove_from_dependencies(buffer, buflen, string, dependencies, dependencieslen, type);\r
+      if (buflen) HeapFree(GetProcessHeap(), 0, buffer);\r
+      return ret;\r
+    }\r
+    else {\r
+      /*\r
+        No existing list.\r
+        We can't remove from an empty list so just treat an add\r
+        operation as setting a new string.\r
+      */\r
+      if (op < 0) return 0;\r
+      op = 0;\r
+    }\r
+  }\r
+\r
+  if (! op) {\r
+    TCHAR *unformatted = 0;\r
+    unsigned long newlen;\r
+    if (unformat_double_null(string, (unsigned long) _tcslen(string), &unformatted, &newlen)) return -1;\r
+\r
+    if (type == DEPENDENCY_GROUPS) {\r
+      /* Prepend group identifier. */\r
+      unsigned long missing = 0;\r
+      TCHAR *canon = unformatted;\r
+      size_t canonlen = 0;\r
+      TCHAR *s;\r
+      for (s = unformatted; *s; s++) {\r
+        if (*s != SC_GROUP_IDENTIFIER) missing++;\r
+        size_t len = _tcslen(s);\r
+        canonlen += len + 1;\r
+        s += len;\r
+      }\r
+\r
+      if (missing) {\r
+        /* Missing identifiers plus double NULL terminator. */\r
+        canonlen += missing + 1;\r
+\r
+        canon = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, canonlen * sizeof(TCHAR));\r
+        if (! canon) {\r
+          print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canon"), _T("native_set_dependon"));\r
+          if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);\r
+          return -1;\r
+        }\r
+\r
+        size_t i = 0;\r
+        for (s = unformatted; *s; s++) {\r
+          if (*s != SC_GROUP_IDENTIFIER) canon[i++] = SC_GROUP_IDENTIFIER;\r
+          size_t len = _tcslen(s);\r
+          memmove(canon + i, s, (len + 1) * sizeof(TCHAR));\r
+          i += len + 1;\r
+          s += len;\r
+        }\r
+\r
+        HeapFree(GetProcessHeap(), 0, unformatted);\r
+        unformatted = canon;\r
+        newlen = (unsigned long) canonlen;\r
+      }\r
+    }\r
+\r
+    *dependencies = unformatted;\r
+    *dependencieslen = newlen;\r
+  }\r
+\r
+  return 0;\r
+}\r
+\r
 static int native_set_dependongroup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
   SC_HANDLE service_handle = (SC_HANDLE) param;\r
   if (! service_handle) return -1;\r
@@ -469,86 +597,46 @@ static int native_set_dependongroup(const TCHAR *service_name, void *param, cons
   /*\r
     Get existing service dependencies because we must set both types together.\r
   */\r
-  TCHAR *buffer;\r
-  unsigned long buflen;\r
-  if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_SERVICES)) return -1;\r
+  TCHAR *services_buffer;\r
+  unsigned long services_buflen;\r
+  if (get_service_dependencies(service_name, service_handle, &services_buffer, &services_buflen, DEPENDENCY_SERVICES)) return -1;\r
 \r
   if (! value || ! value->string || ! value->string[0]) {\r
-    if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, buffer, 0, 0, 0)) {\r
+    if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, services_buffer, 0, 0, 0)) {\r
       print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));\r
-      if (buffer) HeapFree(GetProcessHeap(), 0, buffer);\r
+      if (services_buffer) HeapFree(GetProcessHeap(), 0, services_buffer);\r
       return -1;\r
     }\r
 \r
-    if (buffer) HeapFree(GetProcessHeap(), 0, buffer);\r
+    if (services_buffer) HeapFree(GetProcessHeap(), 0, services_buffer);\r
     return 0;\r
   }\r
 \r
-  unsigned long len = (unsigned long) _tcslen(value->string) + 1;\r
-  TCHAR *unformatted = 0;\r
-  unsigned long newlen;\r
-  if (unformat_double_null(value->string, len, &unformatted, &newlen)) {\r
-    if (buffer) HeapFree(GetProcessHeap(), 0, buffer);\r
-    return -1;\r
-  }\r
-\r
-  /* Prepend group identifier. */\r
-  unsigned long missing = 0;\r
-  TCHAR *canon = unformatted;\r
-  size_t canonlen = 0;\r
-  TCHAR *s;\r
-  for (s = unformatted; *s; s++) {\r
-    if (*s != SC_GROUP_IDENTIFIER) missing++;\r
-    size_t len = _tcslen(s);\r
-    canonlen += len + 1;\r
-    s += len;\r
-  }\r
-\r
-  if (missing) {\r
-    /* Missing identifiers plus double NULL terminator. */\r
-    canonlen += missing + 1;\r
-    newlen = (unsigned long) canonlen;\r
-\r
-    canon = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, canonlen * sizeof(TCHAR));\r
-    if (! canon) {\r
-      print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canon"), _T("native_set_dependongroup"));\r
-      if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);\r
-      if (buffer) HeapFree(GetProcessHeap(), 0, buffer);\r
-      return -1;\r
-    }\r
-\r
-    size_t i = 0;\r
-    for (s = unformatted; *s; s++) {\r
-      if (*s != SC_GROUP_IDENTIFIER) canon[i++] = SC_GROUP_IDENTIFIER;\r
-      size_t len = _tcslen(s);\r
-      memmove(canon + i, s, (len + 1) * sizeof(TCHAR));\r
-      i += len + 1;\r
-      s += len;\r
-    }\r
-  }\r
+  /* Update the group list. */\r
+  TCHAR *groups_buffer;\r
+  unsigned long groups_buflen;\r
+  if (native_set_dependon(service_name, service_handle, &groups_buffer, &groups_buflen, value, DEPENDENCY_GROUPS)) return -1;\r
 \r
   TCHAR *dependencies;\r
-  if (buflen > 2) {\r
-    dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (newlen + buflen) * sizeof(TCHAR));\r
+  if (services_buflen > 2) {\r
+    dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (groups_buflen + services_buflen) * sizeof(TCHAR));\r
     if (! dependencies) {\r
       print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("dependencies"), _T("native_set_dependongroup"));\r
-      if (canon != unformatted) HeapFree(GetProcessHeap(), 0, canon);\r
-      if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);\r
-      if (buffer) HeapFree(GetProcessHeap(), 0, buffer);\r
+      if (groups_buffer) HeapFree(GetProcessHeap(), 0, groups_buffer);\r
+      if (services_buffer) HeapFree(GetProcessHeap(), 0, services_buffer);\r
       return -1;\r
     }\r
 \r
-    memmove(dependencies, buffer, buflen * sizeof(TCHAR));\r
-    memmove(dependencies + buflen - 1, canon, newlen * sizeof(TCHAR));\r
+    memmove(dependencies, services_buffer, services_buflen * sizeof(TCHAR));\r
+    memmove(dependencies + services_buflen - 1, groups_buffer, groups_buflen * sizeof(TCHAR));\r
   }\r
-  else dependencies = canon;\r
+  else dependencies = groups_buffer;\r
 \r
   int ret = 1;\r
   if (set_service_dependencies(service_name, service_handle, dependencies)) ret = -1;\r
-  if (dependencies != unformatted) HeapFree(GetProcessHeap(), 0, dependencies);\r
-  if (canon != unformatted) HeapFree(GetProcessHeap(), 0, canon);\r
-  if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);\r
-  if (buffer) HeapFree(GetProcessHeap(), 0, buffer);\r
+  if (dependencies != groups_buffer) HeapFree(GetProcessHeap(), 0, dependencies);\r
+  if (groups_buffer) HeapFree(GetProcessHeap(), 0, groups_buffer);\r
+  if (services_buffer) HeapFree(GetProcessHeap(), 0, services_buffer);\r
 \r
   return ret;\r
 }\r
@@ -589,49 +677,46 @@ static int native_set_dependonservice(const TCHAR *service_name, void *param, co
   /*\r
     Get existing group dependencies because we must set both types together.\r
   */\r
-  TCHAR *buffer;\r
-  unsigned long buflen;\r
-  if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_GROUPS)) return -1;\r
+  TCHAR *groups_buffer;\r
+  unsigned long groups_buflen;\r
+  if (get_service_dependencies(service_name, service_handle, &groups_buffer, &groups_buflen, DEPENDENCY_GROUPS)) return -1;\r
 \r
   if (! value || ! value->string || ! value->string[0]) {\r
-    if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, buffer, 0, 0, 0)) {\r
+    if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, groups_buffer, 0, 0, 0)) {\r
       print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));\r
-      if (buffer) HeapFree(GetProcessHeap(), 0, buffer);\r
+      if (groups_buffer) HeapFree(GetProcessHeap(), 0, groups_buffer);\r
       return -1;\r
     }\r
 \r
-    if (buffer) HeapFree(GetProcessHeap(), 0, buffer);\r
+    if (groups_buffer) HeapFree(GetProcessHeap(), 0, groups_buffer);\r
     return 0;\r
   }\r
 \r
-  unsigned long len = (unsigned long) _tcslen(value->string) + 1;\r
-  TCHAR *unformatted = 0;\r
-  unsigned long newlen;\r
-  if (unformat_double_null(value->string, len, &unformatted, &newlen)) {\r
-    if (buffer) HeapFree(GetProcessHeap(), 0, buffer);\r
-    return -1;\r
-  }\r
+  /* Update the service list. */\r
+  TCHAR *services_buffer;\r
+  unsigned long services_buflen;\r
+  if (native_set_dependon(service_name, service_handle, &services_buffer, &services_buflen, value, DEPENDENCY_SERVICES)) return -1;\r
 \r
   TCHAR *dependencies;\r
-  if (buflen > 2) {\r
-    dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (newlen + buflen) * sizeof(TCHAR));\r
+  if (groups_buflen > 2) {\r
+    dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (services_buflen + groups_buflen) * sizeof(TCHAR));\r
     if (! dependencies) {\r
       print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("dependencies"), _T("native_set_dependonservice"));\r
-      if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);\r
-      if (buffer) HeapFree(GetProcessHeap(), 0, buffer);\r
+      if (groups_buffer) HeapFree(GetProcessHeap(), 0, groups_buffer);\r
+      if (services_buffer) HeapFree(GetProcessHeap(), 0, services_buffer);\r
       return -1;\r
     }\r
 \r
-    memmove(dependencies, buffer, buflen * sizeof(TCHAR));\r
-    memmove(dependencies + buflen - 1, unformatted, newlen * sizeof(TCHAR));\r
+    memmove(dependencies, services_buffer, services_buflen * sizeof(TCHAR));\r
+    memmove(dependencies + services_buflen - 1, groups_buffer, groups_buflen * sizeof(TCHAR));\r
   }\r
-  else dependencies = unformatted;\r
+  else dependencies = services_buffer;\r
 \r
   int ret = 1;\r
   if (set_service_dependencies(service_name, service_handle, dependencies)) ret = -1;\r
-  if (dependencies != unformatted) HeapFree(GetProcessHeap(), 0, dependencies);\r
-  if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);\r
-  if (buffer) HeapFree(GetProcessHeap(), 0, buffer);\r
+  if (dependencies != services_buffer) HeapFree(GetProcessHeap(), 0, dependencies);\r
+  if (groups_buffer) HeapFree(GetProcessHeap(), 0, groups_buffer);\r
+  if (services_buffer) HeapFree(GetProcessHeap(), 0, services_buffer);\r
 \r
   return ret;\r
 }\r