Browse Source

Allow editing service dependencies.

Service dependencies can now be queried or set on the command line via
the DependOnService and DependOnGroup parameters, or via the GUI in the
new Dependencies tab.

Service dependencies can be set using either their key name or display
name.  For example:

    nssm set <servicename> DependOnService RpcSs

which is equivalent to:

    nssm set <servicename> DependOnService "Remote Procedure Call (RPC)"

Group dependencies can be set with or without the group identifier
prefix, which for some reason is not actually described in the
documentation; instead readers are invited to consult the header files
for the value of SC_GROUP_IDENTIFIER, which it turns out is the +
symbol.  For example:

    nssm set <servicename> DependOnGroup +UIGroup

which is equivalent to:

    nssm set <servicename> DependOnGroup UIGroup

The prefix is always printed when query group dependencies.

The GUI presents a editbox into which either service or group
dependencies can be typed.  The group prefix is mandatory when editing
dependencies via the GUI.
Iain Patterson 7 years ago
parent
commit
0dfa0fdb3a
10 changed files with 474 additions and 4 deletions
  1. 9 1
      README.txt
  2. 48 1
      gui.cpp
  3. BIN
      messages.mc
  4. BIN
      nssm.rc
  5. 2 0
      registry.h
  6. 3 1
      resource.h
  7. 196 1
      service.cpp
  8. 5 0
      service.h
  9. 205 0
      settings.cpp
  10. 6 0
      settings.h

+ 9 - 1
README.txt

@@ -62,7 +62,7 @@ an application which has exited.
 Since version 2.22, NSSM can rotate existing output files when redirecting I/O.
 
 Since version 2.22, NSSM can set service display name, description, startup
-type and log on details.
+type, log on details and dependencies.
 
 Since version 2.22, NSSM can manage existing services.
 
@@ -494,6 +494,14 @@ managed application.  Valid priorities are as follows:
   IDLE_PRIORITY_CLASS
 
 
+The DependOnGroup and DependOnService parameters are used to query or set
+the dependencies for the service.  When setting dependencies, each service
+or service group (preceded with the + symbol) should be specified in
+separate command line arguments.  For example:
+
+    nssm set <servicename> DependOnService RpcSs LanmanWorkstation
+
+
 The Name parameter can only be queried, not set.  It returns the service's
 registry key name.  This may be useful to know if you take advantage of
 the fact that you can substitute the service's display name anywhere where

+ 48 - 1
gui.cpp

@@ -1,6 +1,6 @@
 #include "nssm.h"
 
-static enum { NSSM_TAB_APPLICATION, NSSM_TAB_DETAILS, NSSM_TAB_LOGON, NSSM_TAB_PROCESS, NSSM_TAB_SHUTDOWN, NSSM_TAB_EXIT, NSSM_TAB_IO, NSSM_TAB_ROTATION, NSSM_TAB_ENVIRONMENT, NSSM_NUM_TABS };
+static enum { NSSM_TAB_APPLICATION, NSSM_TAB_DETAILS, NSSM_TAB_LOGON, NSSM_TAB_DEPENDENCIES, NSSM_TAB_PROCESS, NSSM_TAB_SHUTDOWN, NSSM_TAB_EXIT, NSSM_TAB_IO, NSSM_TAB_ROTATION, NSSM_TAB_ENVIRONMENT, NSSM_NUM_TABS };
 static HWND tablist[NSSM_NUM_TABS];
 static int selected_tab;
 
@@ -95,6 +95,19 @@ int nssm_gui(int resource, nssm_service_t *service) {
       if (service->type & SERVICE_INTERACTIVE_PROCESS) SendDlgItemMessage(tablist[NSSM_TAB_LOGON], IDC_INTERACT, BM_SETCHECK, BST_CHECKED, 0);
     }
 
+    /* Dependencies tab. */
+    if (service->dependencieslen) {
+      TCHAR *formatted;
+      unsigned long newlen;
+      if (format_double_null(service->dependencies, service->dependencieslen, &formatted, &newlen)) {
+        popup_message(dlg, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("dependencies"), _T("nssm_dlg()"));
+      }
+      else {
+        SetDlgItemText(tablist[NSSM_TAB_DEPENDENCIES], IDC_DEPENDENCIES, formatted);
+        HeapFree(GetProcessHeap(), 0, formatted);
+      }
+    }
+
     /* Process tab. */
     if (service->priority) {
       int priority = priority_constant_to_index(service->priority);
@@ -471,6 +484,33 @@ int configure(HWND window, nssm_service_t *service, nssm_service_t *orig_service
     }
   }
 
+  /* Get dependencies. */
+  unsigned long dependencieslen = (unsigned long) SendMessage(GetDlgItem(tablist[NSSM_TAB_DEPENDENCIES], IDC_DEPENDENCIES), WM_GETTEXTLENGTH, 0, 0);
+  if (dependencieslen) {
+    TCHAR *dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (dependencieslen + 2) * sizeof(TCHAR));
+    if (! dependencies) {
+      popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("dependencies"), _T("install()"));
+      cleanup_nssm_service(service);
+      return 6;
+    }
+
+    if (! GetDlgItemText(tablist[NSSM_TAB_DEPENDENCIES], IDC_DEPENDENCIES, dependencies, dependencieslen + 1)) {
+      popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_DEPENDENCIES);
+      HeapFree(GetProcessHeap(), 0, dependencies);
+      cleanup_nssm_service(service);
+      return 6;
+    }
+
+    if (unformat_double_null(dependencies, dependencieslen, &service->dependencies, &service->dependencieslen)) {
+      HeapFree(GetProcessHeap(), 0, dependencies);
+      popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("dependencies"), _T("install()"));
+      cleanup_nssm_service(service);
+      return 6;
+    }
+
+    HeapFree(GetProcessHeap(), 0, dependencies);
+  }
+
   /* Remaining tabs are only for services we manage. */
   if (service->native) return 0;
 
@@ -955,6 +995,13 @@ INT_PTR CALLBACK nssm_dlg(HWND window, UINT message, WPARAM w, LPARAM l) {
       CheckRadioButton(tablist[NSSM_TAB_LOGON], IDC_LOCALSYSTEM, IDC_ACCOUNT, IDC_LOCALSYSTEM);
       set_logon_enabled(0);
 
+      /* Dependencies tab. */
+      tab.pszText = message_string(NSSM_GUI_TAB_DEPENDENCIES);
+      tab.cchTextMax = (int) _tcslen(tab.pszText);
+      SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_DEPENDENCIES, (LPARAM) &tab);
+      tablist[NSSM_TAB_DEPENDENCIES] = dialog(MAKEINTRESOURCE(IDD_DEPENDENCIES), window, tab_dlg);
+      ShowWindow(tablist[NSSM_TAB_DEPENDENCIES], SW_HIDE);
+
       /* Remaining tabs are only for services we manage. */
       if (service->native) return 1;
 

BIN
messages.mc


BIN
nssm.rc


+ 2 - 0
registry.h

@@ -2,6 +2,8 @@
 #define REGISTRY_H
 
 #define NSSM_REGISTRY _T("SYSTEM\\CurrentControlSet\\Services\\%s\\Parameters")
+#define NSSM_REGISTRY_GROUPS _T("SYSTEM\\CurrentControlSet\\Control\\ServiceGroupOrder")
+#define NSSM_REG_GROUPS _T("List")
 #define NSSM_REG_EXE _T("Application")
 #define NSSM_REG_FLAGS _T("AppParameters")
 #define NSSM_REG_DIR _T("AppDirectory")

+ 3 - 1
resource.h

@@ -17,6 +17,7 @@
 #define IDD_ENVIRONMENT                 112
 #define IDD_NATIVE                      113
 #define IDD_PROCESS                     114
+#define IDD_DEPENDENCIES                115
 #define IDC_PATH                        1000
 #define IDC_TAB1                        1001
 #define IDC_CANCEL                      1002
@@ -62,6 +63,7 @@
 #define IDC_AFFINITY_ALL                1043
 #define IDC_AFFINITY                    1044
 #define IDC_CONSOLE                     1045
+#define IDC_DEPENDENCIES                1046
 
 // Next default values for new objects
 // 
@@ -69,7 +71,7 @@
 #ifndef APSTUDIO_READONLY_SYMBOLS
 #define _APS_NEXT_RESOURCE_VALUE        115
 #define _APS_NEXT_COMMAND_VALUE         40001
-#define _APS_NEXT_CONTROL_VALUE         1046
+#define _APS_NEXT_CONTROL_VALUE         1047
 #define _APS_NEXT_SYMED_VALUE           101
 #endif
 #endif

+ 196 - 1
service.cpp

@@ -371,6 +371,185 @@ QUERY_SERVICE_CONFIG *query_service_config(const TCHAR *service_name, SC_HANDLE
   return qsc;
 }
 
+int set_service_dependencies(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR *buffer) {
+  TCHAR *dependencies = _T("");
+  unsigned long num_dependencies = 0;
+
+  if (buffer && buffer[0]) {
+    SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
+    if (! services) {
+      print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
+      return 1;
+    }
+
+    /*
+      Count the dependencies then allocate a buffer big enough for their
+      canonical names, ie n * SERVICE_NAME_LENGTH.
+    */
+    TCHAR *s;
+    TCHAR *groups = 0;
+    for (s = buffer; *s; s++) {
+      num_dependencies++;
+      if (*s == SC_GROUP_IDENTIFIER) groups = s;
+      while (*s) s++;
+    }
+
+    /* At least one dependency is a group so we need to verify them. */
+    if (groups) {
+      HKEY key;
+      if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, NSSM_REGISTRY_GROUPS, 0, KEY_READ, &key)) {
+        _ftprintf(stderr, _T("%s: %s\n"), NSSM_REGISTRY_GROUPS, error_string(GetLastError()));
+        return 2;
+      }
+
+      unsigned long type;
+      unsigned long groupslen;
+      unsigned long ret = RegQueryValueEx(key, NSSM_REG_GROUPS, 0, &type, NULL, &groupslen);
+      if (ret == ERROR_SUCCESS) {
+        groups = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, groupslen);
+        if (! groups) {
+          print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("groups"), _T("set_service_dependencies()"));
+          return 3;
+        }
+
+        ret = RegQueryValueEx(key, NSSM_REG_GROUPS, 0, &type, (unsigned char *) groups, &groupslen);
+        if (ret != ERROR_SUCCESS) {
+          _ftprintf(stderr, _T("%s\\%s: %s"), NSSM_REGISTRY_GROUPS, NSSM_REG_GROUPS, error_string(GetLastError()));
+          HeapFree(GetProcessHeap(), 0, groups);
+          RegCloseKey(key);
+          return 4;
+        }
+      }
+      else if (ret != ERROR_FILE_NOT_FOUND) {
+        _ftprintf(stderr, _T("%s\\%s: %s"), NSSM_REGISTRY_GROUPS, NSSM_REG_GROUPS, error_string(GetLastError()));
+        RegCloseKey(key);
+        return 4;
+      }
+
+      RegCloseKey(key);
+
+    }
+
+    unsigned long dependencieslen = (num_dependencies * SERVICE_NAME_LENGTH) + 2;
+    dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dependencieslen * sizeof(TCHAR));
+    size_t i = 0;
+
+    TCHAR dependency[SERVICE_NAME_LENGTH];
+    for (s = buffer; *s; s++) {
+      /* Group? */
+      if (*s == SC_GROUP_IDENTIFIER) {
+        TCHAR *group = s + 1;
+
+        bool ok = false;
+        if (*group) {
+          for (TCHAR *g = groups; *g; g++) {
+            if (str_equiv(g, group)) {
+              ok = true;
+              /* Set canonical name. */
+              memmove(group, g, _tcslen(g) * sizeof(TCHAR));
+              break;
+            }
+
+            while (*g) g++;
+          }
+        }
+
+        if (ok) _sntprintf_s(dependency, _countof(dependency), _TRUNCATE, _T("%s"), s);
+        else {
+          HeapFree(GetProcessHeap(), 0, dependencies);
+          if (groups) HeapFree(GetProcessHeap(), 0, groups);
+          _ftprintf(stderr, _T("%s: %s"), s, error_string(ERROR_SERVICE_DEPENDENCY_DELETED));
+          return 5;
+        }
+      }
+      else {
+        SC_HANDLE dependency_handle = open_service(services, s, SERVICE_QUERY_STATUS, dependency, _countof(dependency));
+        if (! dependency_handle) {
+          HeapFree(GetProcessHeap(), 0, dependencies);
+          if (groups) HeapFree(GetProcessHeap(), 0, groups);
+          CloseServiceHandle(services);
+          _ftprintf(stderr, _T("%s: %s"), s, error_string(ERROR_SERVICE_DEPENDENCY_DELETED));
+          return 5;
+        }
+      }
+
+      size_t len = _tcslen(dependency) + 1;
+      memmove(dependencies + i, dependency, len * sizeof(TCHAR));
+      i += len;
+
+      while (*s) s++;
+    }
+
+    if (groups) HeapFree(GetProcessHeap(), 0, groups);
+    CloseServiceHandle(services);
+  }
+
+  if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, dependencies, 0, 0, 0)) {
+    if (num_dependencies) HeapFree(GetProcessHeap(), 0, dependencies);
+    print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
+    return -1;
+  }
+
+  if (num_dependencies) HeapFree(GetProcessHeap(), 0, dependencies);
+  return 0;
+}
+
+int get_service_dependencies(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR **buffer, unsigned long *bufsize, int type) {
+  if (! buffer) return 1;
+  if (! bufsize) return 2;
+
+  *buffer = 0;
+  *bufsize = 0;
+
+  QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
+  if (! qsc) return 3;
+
+  if (! qsc->lpDependencies) return 0;
+  if (! qsc->lpDependencies[0]) return 0;
+
+  /* lpDependencies is doubly NULL terminated. */
+  while (qsc->lpDependencies[*bufsize]) {
+    while (qsc->lpDependencies[*bufsize]) ++*bufsize;
+    ++*bufsize;
+  }
+
+  *bufsize += 2;
+
+  *buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *bufsize * sizeof(TCHAR));
+  if (! *buffer) {
+    *bufsize = 0;
+    print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("lpDependencies"), _T("get_service_dependencies()"));
+    return 4;
+  }
+
+  if (type == DEPENDENCY_ALL) memmove(*buffer, qsc->lpDependencies, *bufsize * sizeof(TCHAR));
+  else {
+    TCHAR *s;
+    size_t i = 0;
+    *bufsize = 0;
+    for (s = qsc->lpDependencies; *s; s++) {
+      /* Only copy the appropriate type of dependency. */
+      if ((*s == SC_GROUP_IDENTIFIER && type & DEPENDENCY_GROUPS) || (*s != SC_GROUP_IDENTIFIER && type & DEPENDENCY_SERVICES)) {
+        size_t len = _tcslen(s) + 1;
+        *bufsize += (unsigned long) len;
+        memmove(*buffer + i, s, len * sizeof(TCHAR));
+        i += len;
+      }
+
+      while (*s) s++;
+    }
+    ++*bufsize;
+  }
+
+  HeapFree(GetProcessHeap(), 0, qsc);
+
+  return 0;
+}
+
+int get_service_dependencies(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR **buffer, unsigned long *bufsize) {
+  return get_service_dependencies(service_name, service_handle, buffer, bufsize, DEPENDENCY_ALL);
+}
+
 int set_service_description(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR *buffer) {
   SERVICE_DESCRIPTION description;
   ZeroMemory(&description, sizeof(description));
@@ -527,6 +706,7 @@ void cleanup_nssm_service(nssm_service_t *service) {
     SecureZeroMemory(service->password, service->passwordlen);
     HeapFree(GetProcessHeap(), 0, service->password);
   }
+  if (service->dependencies) HeapFree(GetProcessHeap(), 0, service->dependencies);
   if (service->env) HeapFree(GetProcessHeap(), 0, service->env);
   if (service->env_extra) HeapFree(GetProcessHeap(), 0, service->env_extra);
   if (service->handle) CloseHandle(service->handle);
@@ -729,6 +909,14 @@ int pre_edit_service(int argc, TCHAR **argv) {
     }
   }
 
+  if (get_service_dependencies(service->name, service->handle, &service->dependencies, &service->dependencieslen)) {
+    if (mode != MODE_GETTING) {
+      CloseHandle(service->handle);
+      CloseServiceHandle(services);
+      return 7;
+    }
+  }
+
   /* Get NSSM details. */
   get_parameters(service, 0);
 
@@ -947,11 +1135,18 @@ int edit_service(nssm_service_t *service, bool editing) {
     }
   }
 
-  if (! ChangeServiceConfig(service->handle, service->type, startup, SERVICE_NO_CHANGE, 0, 0, 0, 0, username, password, service->displayname)) {
+  TCHAR *dependencies = _T("");
+  if (service->dependencieslen) dependencies = 0; /* Change later. */
+
+  if (! ChangeServiceConfig(service->handle, service->type, startup, SERVICE_NO_CHANGE, 0, 0, 0, dependencies, username, password, service->displayname)) {
     print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
     return 5;
   }
 
+  if (service->dependencieslen) {
+    if (set_service_dependencies(service->name, service->handle, service->dependencies)) return 5;
+  }
+
   if (service->description[0] || editing) {
     set_service_description(service->name, service->handle, service->description);
   }

+ 5 - 0
service.h

@@ -46,6 +46,8 @@ typedef struct {
   TCHAR dir[DIR_LENGTH];
   TCHAR *env;
   __int64 affinity;
+  TCHAR *dependencies;
+  unsigned long dependencieslen;
   unsigned long envlen;
   TCHAR *env_extra;
   unsigned long env_extralen;
@@ -119,6 +121,9 @@ void cleanup_nssm_service(nssm_service_t *);
 SC_HANDLE open_service_manager(unsigned long);
 SC_HANDLE open_service(SC_HANDLE, TCHAR *, unsigned long, TCHAR *, unsigned long);
 QUERY_SERVICE_CONFIG *query_service_config(const TCHAR *, SC_HANDLE);
+int set_service_dependencies(const TCHAR *, SC_HANDLE, TCHAR *);
+int get_service_dependencies(const TCHAR *, SC_HANDLE, TCHAR **, unsigned long *, int);
+int get_service_dependencies(const TCHAR *, SC_HANDLE, TCHAR **, unsigned long *);
 int set_service_description(const TCHAR *, SC_HANDLE, TCHAR *);
 int get_service_description(const TCHAR *, SC_HANDLE, unsigned long, TCHAR *);
 int get_service_startup(const TCHAR *, SC_HANDLE, const QUERY_SERVICE_CONFIG *, unsigned long *);

+ 205 - 0
settings.cpp

@@ -416,6 +416,209 @@ static int setting_get_priority(const TCHAR *service_name, void *param, const TC
 }
 
 /* Functions to manage native service settings. */
+static int native_set_dependongroup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
+  SC_HANDLE service_handle = (SC_HANDLE) param;
+  if (! service_handle) return -1;
+
+  /*
+    Get existing service dependencies because we must set both types together.
+  */
+  TCHAR *buffer;
+  unsigned long buflen;
+  if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_SERVICES)) return -1;
+
+  if (! value || ! value->string || ! value->string[0]) {
+    if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, buffer, 0, 0, 0)) {
+      print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
+      if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
+      return -1;
+    }
+
+    if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
+    return 0;
+  }
+
+  unsigned long len = (unsigned long) _tcslen(value->string) + 1;
+  TCHAR *unformatted = 0;
+  unsigned long newlen;
+  if (unformat_double_null(value->string, len, &unformatted, &newlen)) {
+    if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
+    return -1;
+  }
+
+  /* Prepend group identifier. */
+  unsigned long missing = 0;
+  TCHAR *canon = unformatted;
+  size_t canonlen = 0;
+  TCHAR *s;
+  for (s = unformatted; *s; s++) {
+    if (*s != SC_GROUP_IDENTIFIER) missing++;
+    size_t len = _tcslen(s);
+    canonlen += len + 1;
+    s += len;
+  }
+
+  if (missing) {
+    /* Missing identifiers plus double NULL terminator. */
+    canonlen += missing + 1;
+    newlen = (unsigned long) canonlen;
+
+    canon = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, canonlen * sizeof(TCHAR));
+    if (! canon) {
+      print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canon"), _T("native_set_dependongroup"));
+      if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);
+      if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
+      return -1;
+    }
+
+    size_t i = 0;
+    for (s = unformatted; *s; s++) {
+      if (*s != SC_GROUP_IDENTIFIER) canon[i++] = SC_GROUP_IDENTIFIER;
+      size_t len = _tcslen(s);
+      memmove(canon + i, s, (len + 1) * sizeof(TCHAR));
+      i += len + 1;
+      s += len;
+    }
+  }
+
+  TCHAR *dependencies;
+  if (buflen > 2) {
+    dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (newlen + buflen) * sizeof(TCHAR));
+    if (! dependencies) {
+      print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("dependencies"), _T("native_set_dependongroup"));
+      if (canon != unformatted) HeapFree(GetProcessHeap(), 0, canon);
+      if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);
+      if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
+      return -1;
+    }
+
+    memmove(dependencies, buffer, buflen * sizeof(TCHAR));
+    memmove(dependencies + buflen - 1, canon, newlen * sizeof(TCHAR));
+  }
+  else dependencies = canon;
+
+  int ret = 1;
+  if (set_service_dependencies(service_name, service_handle, dependencies)) ret = -1;
+  if (dependencies != unformatted) HeapFree(GetProcessHeap(), 0, dependencies);
+  if (canon != unformatted) HeapFree(GetProcessHeap(), 0, canon);
+  if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);
+  if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
+
+  return ret;
+}
+
+static int native_get_dependongroup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
+  SC_HANDLE service_handle = (SC_HANDLE) param;
+  if (! service_handle) return -1;
+
+  TCHAR *buffer;
+  unsigned long buflen;
+  if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_GROUPS)) return -1;
+
+  int ret;
+  if (buflen) {
+    TCHAR *formatted;
+    unsigned long newlen;
+    if (format_double_null(buffer, buflen, &formatted, &newlen)) {
+      HeapFree(GetProcessHeap(), 0, buffer);
+      return -1;
+    }
+
+    ret = value_from_string(name, value, formatted);
+    HeapFree(GetProcessHeap(), 0, formatted);
+    HeapFree(GetProcessHeap(), 0, buffer);
+  }
+  else {
+    value->string = 0;
+    ret = 0;
+  }
+
+  return ret;
+}
+
+static int native_set_dependonservice(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
+  SC_HANDLE service_handle = (SC_HANDLE) param;
+  if (! service_handle) return -1;
+
+  /*
+    Get existing group dependencies because we must set both types together.
+  */
+  TCHAR *buffer;
+  unsigned long buflen;
+  if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_GROUPS)) return -1;
+
+  if (! value || ! value->string || ! value->string[0]) {
+    if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, buffer, 0, 0, 0)) {
+      print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
+      if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
+      return -1;
+    }
+
+    if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
+    return 0;
+  }
+
+  unsigned long len = (unsigned long) _tcslen(value->string) + 1;
+  TCHAR *unformatted = 0;
+  unsigned long newlen;
+  if (unformat_double_null(value->string, len, &unformatted, &newlen)) {
+    if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
+    return -1;
+  }
+
+  TCHAR *dependencies;
+  if (buflen > 2) {
+    dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (newlen + buflen) * sizeof(TCHAR));
+    if (! dependencies) {
+      print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("dependencies"), _T("native_set_dependonservice"));
+      if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);
+      if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
+      return -1;
+    }
+
+    memmove(dependencies, buffer, buflen * sizeof(TCHAR));
+    memmove(dependencies + buflen - 1, unformatted, newlen * sizeof(TCHAR));
+  }
+  else dependencies = unformatted;
+
+  int ret = 1;
+  if (set_service_dependencies(service_name, service_handle, dependencies)) ret = -1;
+  if (dependencies != unformatted) HeapFree(GetProcessHeap(), 0, dependencies);
+  if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);
+  if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
+
+  return ret;
+}
+
+static int native_get_dependonservice(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
+  SC_HANDLE service_handle = (SC_HANDLE) param;
+  if (! service_handle) return -1;
+
+  TCHAR *buffer;
+  unsigned long buflen;
+  if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_SERVICES)) return -1;
+
+  int ret;
+  if (buflen) {
+    TCHAR *formatted;
+    unsigned long newlen;
+    if (format_double_null(buffer, buflen, &formatted, &newlen)) {
+      HeapFree(GetProcessHeap(), 0, buffer);
+      return -1;
+    }
+
+    ret = value_from_string(name, value, formatted);
+    HeapFree(GetProcessHeap(), 0, formatted);
+    HeapFree(GetProcessHeap(), 0, buffer);
+  }
+  else {
+    value->string = 0;
+    ret = 0;
+  }
+
+  return ret;
+}
+
 int native_set_description(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
   SC_HANDLE service_handle = (SC_HANDLE) param;
   if (! service_handle) return -1;
@@ -845,6 +1048,8 @@ settings_t settings[] = {
   { NSSM_REG_ROTATE_SECONDS, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
   { NSSM_REG_ROTATE_BYTES_LOW, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
   { NSSM_REG_ROTATE_BYTES_HIGH, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
+  { NSSM_NATIVE_DEPENDONGROUP, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_dependongroup, native_get_dependongroup },
+  { NSSM_NATIVE_DEPENDONSERVICE, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_dependonservice, native_get_dependonservice },
   { NSSM_NATIVE_DESCRIPTION, REG_SZ, _T(""), true, 0, native_set_description, native_get_description },
   { NSSM_NATIVE_DISPLAYNAME, REG_SZ, NULL, true, 0, native_set_displayname, native_get_displayname },
   { NSSM_NATIVE_IMAGEPATH, REG_EXPAND_SZ, NULL, true, 0, native_set_imagepath, native_get_imagepath },

+ 6 - 0
settings.h

@@ -1,6 +1,8 @@
 #ifndef SETTINGS_H
 #define SETTINGS_H
 
+#define NSSM_NATIVE_DEPENDONGROUP _T("DependOnGroup")
+#define NSSM_NATIVE_DEPENDONSERVICE _T("DependOnService")
 #define NSSM_NATIVE_DESCRIPTION _T("Description")
 #define NSSM_NATIVE_DISPLAYNAME _T("DisplayName")
 #define NSSM_NATIVE_IMAGEPATH _T("ImagePath")
@@ -16,6 +18,10 @@
 #define ADDITIONAL_CRLF (1 << 3)
 #define ADDITIONAL_MANDATORY ADDITIONAL_GETTING|ADDITIONAL_SETTING|ADDITIONAL_RESETTING
 
+#define DEPENDENCY_SERVICES (1 << 0)
+#define DEPENDENCY_GROUPS (1 << 1)
+#define DEPENDENCY_ALL (DEPENDENCY_SERVICES|DEPENDENCY_GROUPS)
+
 typedef union {
   unsigned long numeric;
   TCHAR *string;