From 0dfa0fdb3a851b1ff69a0f0b308a3e1371286a57 Mon Sep 17 00:00:00 2001 From: Iain Patterson Date: Sun, 2 Mar 2014 18:29:45 +0000 Subject: [PATCH] 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 DependOnService RpcSs which is equivalent to: nssm set 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 DependOnGroup +UIGroup which is equivalent to: nssm set 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. --- README.txt | 10 ++- gui.cpp | 49 +++++++++++++- messages.mc | Bin 146650 -> 147452 bytes nssm.rc | Bin 53034 -> 55400 bytes registry.h | 2 + resource.h | 4 +- service.cpp | 197 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- service.h | 5 ++ settings.cpp | 205 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ settings.h | 6 ++ 10 files changed, 474 insertions(+), 4 deletions(-) diff --git a/README.txt b/README.txt index df12238..32051ab 100644 --- a/README.txt +++ b/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 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 diff --git a/gui.cpp b/gui.cpp index ce4d6bf..a69bf3b 100644 --- a/gui.cpp +++ b/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; diff --git a/messages.mc b/messages.mc index 0da67eff199668e6e65321bbe4886ad2969929f4..65ea5153405a13f7f2e0992c260e9658d68e7709 100644 GIT binary patch delta 217 zcmcchljF~Sjt#eVPIg$w$M4Dzz~IW@$KV1cohL6mmpFM}v4D6ALn=c75a%&~Sa}S| z44FW>c=E$__JSBPlVvrGgV on@-+vjfEd(R3g~&)X5jm8Usy0vtn}ooz&(XH@5G%!Kjo108j`*(*OVf delta 23 hcmV+y0O6$q<3e^u_7H{)hD?Uy$q)U7 z15+4M847?nk0FJjm_dOdAIMT*C;_TS1=49iehv`kGn4~$Y2ju67E(7pnFrnE=IVZm?0750)&~M@0J*3H7#1M=QW=VX_GL0u0WnA<1xUj}8swrBpj$GbD$~JwL2d_$ zAbbQ3Q?MvVKge8Q%t1pxkwJ+;2PozVjAG}>w&|9W^Q<*U4g1N{RwZp-wy;GP0E%sl AmH+?% delta 45 zcmaE{fqB(D<_%JMo6QWpyf)7kHBOB1G7oS2Aiwaywe5%pkfjn diff --git a/registry.h b/registry.h index 6d8e33d..7bf49b0 100644 --- a/registry.h +++ b/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") diff --git a/resource.h b/resource.h index 0ddd90d..36477aa 100644 --- a/resource.h +++ b/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 diff --git a/service.cpp b/service.cpp index 4b14d17..5b10233 100644 --- a/service.cpp +++ b/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); } diff --git a/service.h b/service.h index 00428d0..9208b2e 100644 --- a/service.h +++ b/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 *); diff --git a/settings.cpp b/settings.cpp index 43ae559..14b279a 100644 --- a/settings.cpp +++ b/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 }, diff --git a/settings.h b/settings.h index 0499dbe..a629839 100644 --- a/settings.h +++ b/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; -- 2.7.4