\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
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
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
\r
unsigned long constant;\r
switch (get_number(key, (TCHAR *) name, &constant, false)) {\r
- case 0: return value_from_string(name, value, (const TCHAR *) default_value);\r
+ case 0:\r
+ if (value_from_string(name, value, (const TCHAR *) default_value) == -1) return -1;\r
+ return 0;\r
case -1: return -1;\r
}\r
\r
}\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
/*\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
/*\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
}\r
\r
int native_set_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
- HKEY key = open_service_registry(service_name, KEY_SET_VALUE, false);\r
+ HKEY key = open_service_registry(service_name, KEY_SET_VALUE, true);\r
if (! key) return -1;\r
\r
- int ret = setting_set_environment(service_name, (void *) key, NSSM_NATIVE_ENVIRONMENT, default_value, value, additional);\r
+ int ret = setting_set_environment(service_name, (void *) key, name, default_value, value, additional);\r
RegCloseKey(key);\r
return ret;\r
}\r
\r
int native_get_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
- HKEY key = open_service_registry(service_name, KEY_READ, false);\r
+ HKEY key = open_service_registry(service_name, KEY_READ, true);\r
if (! key) return -1;\r
\r
ZeroMemory(value, sizeof(value_t));\r
- int ret = setting_get_environment(service_name, (void *) key, NSSM_NATIVE_ENVIRONMENT, default_value, value, additional);\r
+ int ret = setting_get_environment(service_name, (void *) key, name, default_value, value, additional);\r
RegCloseKey(key);\r
return ret;\r
}\r
{ NSSM_REG_KILL_THREADS_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_THREADS_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },\r
{ NSSM_REG_KILL_PROCESS_TREE, REG_DWORD, (void *) 1, false, 0, setting_set_number, setting_get_number },\r
{ NSSM_REG_THROTTLE, REG_DWORD, (void *) NSSM_RESET_THROTTLE_RESTART, false, 0, setting_set_number, setting_get_number },\r
+ { NSSM_REG_HOOK_SHARE_OUTPUT_HANDLES, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },\r
{ NSSM_REG_ROTATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },\r
{ NSSM_REG_ROTATE_ONLINE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },\r
{ NSSM_REG_ROTATE_SECONDS, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },\r