2 /* XXX: (value && value->string) is probably bogus because value is probably never null */
\r
5 #define NSSM_AFFINITY_ALL _T("All")
\r
7 extern const TCHAR *exit_action_strings[];
\r
8 extern const TCHAR *startup_strings[];
\r
9 extern const TCHAR *priority_strings[];
\r
11 /* Does the parameter refer to the default value of the setting? */
\r
12 static inline int is_default(const TCHAR *value) {
\r
13 return (str_equiv(value, _T("default")) || str_equiv(value, _T("*")) || ! value[0]);
\r
16 static int value_from_string(const TCHAR *name, value_t *value, const TCHAR *string) {
\r
17 size_t len = _tcslen(string);
\r
23 value->string = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));
\r
24 if (! value->string) {
\r
25 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, name, _T("value_from_string()"));
\r
29 if (_sntprintf_s(value->string, len, _TRUNCATE, _T("%s"), string) < 0) {
\r
30 HeapFree(GetProcessHeap(), 0, value->string);
\r
31 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, name, _T("value_from_string()"));
\r
38 /* Functions to manage NSSM-specific settings in the registry. */
\r
39 static int setting_set_number(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
40 HKEY key = (HKEY) param;
\r
41 if (! key) return -1;
\r
43 unsigned long number;
\r
46 /* Resetting to default? */
\r
47 if (! value || ! value->string) {
\r
48 error = RegDeleteValue(key, name);
\r
49 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
\r
50 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
\r
53 if (str_number(value->string, &number)) return -1;
\r
55 if (default_value && number == PtrToUlong(default_value)) {
\r
56 error = RegDeleteValue(key, name);
\r
57 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
\r
58 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
\r
62 if (set_number(key, (TCHAR *) name, number)) return -1;
\r
67 static int setting_get_number(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
68 HKEY key = (HKEY) param;
\r
69 return get_number(key, (TCHAR *) name, &value->numeric, false);
\r
72 static int setting_set_string(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
73 HKEY key = (HKEY) param;
\r
74 if (! key) return -1;
\r
78 /* Resetting to default? */
\r
79 if (! value || ! value->string) {
\r
80 if (default_value) value->string = (TCHAR *) default_value;
\r
82 error = RegDeleteValue(key, name);
\r
83 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
\r
84 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
\r
88 if (default_value && _tcslen((TCHAR *) default_value) && str_equiv(value->string, (TCHAR *) default_value)) {
\r
89 error = RegDeleteValue(key, name);
\r
90 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
\r
91 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
\r
95 if (set_expand_string(key, (TCHAR *) name, value->string)) return -1;
\r
100 static int setting_get_string(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
101 HKEY key = (HKEY) param;
\r
102 TCHAR buffer[VALUE_LENGTH];
\r
104 if (get_string(key, (TCHAR *) name, (TCHAR *) buffer, (unsigned long) sizeof(buffer), false, false, false)) return -1;
\r
106 return value_from_string(name, value, buffer);
\r
109 static int setting_set_exit_action(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
110 unsigned long exitcode;
\r
112 TCHAR action_string[ACTION_LEN];
\r
115 /* Default action? */
\r
116 if (is_default(additional)) code = 0;
\r
118 if (str_number(additional, &exitcode)) return -1;
\r
119 code = (TCHAR *) additional;
\r
123 HKEY key = open_registry(service_name, name, KEY_WRITE);
\r
124 if (! key) return -1;
\r
129 /* Resetting to default? */
\r
130 if (value && value->string) _sntprintf_s(action_string, _countof(action_string), _TRUNCATE, _T("%s"), value->string);
\r
133 /* Delete explicit action. */
\r
134 error = RegDeleteValue(key, code);
\r
136 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
\r
137 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, code, service_name, error_string(error));
\r
141 /* Explicitly keep the default action. */
\r
142 if (default_value) _sntprintf_s(action_string, _countof(action_string), _TRUNCATE, _T("%s"), (TCHAR *) default_value);
\r
147 /* Validate the string. */
\r
148 for (int i = 0; exit_action_strings[i]; i++) {
\r
149 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
150 if (default_value && str_equiv(action_string, (TCHAR *) default_value)) ret = 0;
\r
151 if (RegSetValueEx(key, code, 0, REG_SZ, (const unsigned char *) exit_action_strings[i], (unsigned long) (_tcslen(action_string) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
152 print_message(stderr, NSSM_MESSAGE_SETVALUE_FAILED, code, service_name, error_string(GetLastError()));
\r
162 print_message(stderr, NSSM_MESSAGE_INVALID_EXIT_ACTION, action_string);
\r
163 for (int i = 0; exit_action_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), exit_action_strings[i]);
\r
168 static int setting_get_exit_action(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
169 unsigned long exitcode = 0;
\r
170 unsigned long *code = 0;
\r
173 if (! is_default(additional)) {
\r
174 if (str_number(additional, &exitcode)) return -1;
\r
179 TCHAR action_string[ACTION_LEN];
\r
180 bool default_action;
\r
181 if (get_exit_action(service_name, code, action_string, &default_action)) return -1;
\r
183 value_from_string(name, value, action_string);
\r
185 if (default_action && ! _tcsnicmp((const TCHAR *) action_string, (TCHAR *) default_value, ACTION_LEN)) return 0;
\r
189 static inline bool split_hook_name(const TCHAR *hook_name, TCHAR *hook_event, TCHAR *hook_action) {
\r
192 for (s = (TCHAR *) hook_name; *s; s++) {
\r
193 if (*s == _T('/')) {
\r
195 _sntprintf_s(hook_event, HOOK_NAME_LENGTH, _TRUNCATE, _T("%s"), hook_name);
\r
196 _sntprintf_s(hook_action, HOOK_NAME_LENGTH, _TRUNCATE, _T("%s"), ++s);
\r
197 return valid_hook_name(hook_event, hook_action, false);
\r
201 print_message(stderr, NSSM_MESSAGE_INVALID_HOOK_NAME, hook_name);
\r
205 static int setting_set_hook(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
206 TCHAR hook_event[HOOK_NAME_LENGTH];
\r
207 TCHAR hook_action[HOOK_NAME_LENGTH];
\r
208 if (! split_hook_name(additional, hook_event, hook_action)) return -1;
\r
211 if (value && value->string) cmd = value->string;
\r
214 if (set_hook(service_name, hook_event, hook_action, cmd)) return -1;
\r
215 if (! _tcslen(cmd)) return 0;
\r
219 static int setting_get_hook(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
220 TCHAR hook_event[HOOK_NAME_LENGTH];
\r
221 TCHAR hook_action[HOOK_NAME_LENGTH];
\r
222 if (! split_hook_name(additional, hook_event, hook_action)) return -1;
\r
224 TCHAR cmd[CMD_LENGTH];
\r
225 if (get_hook(service_name, hook_event, hook_action, cmd, sizeof(cmd))) return -1;
\r
227 value_from_string(name, value, cmd);
\r
229 if (! _tcslen(cmd)) return 0;
\r
233 static int setting_set_affinity(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
234 HKEY key = (HKEY) param;
\r
235 if (! key) return -1;
\r
239 __int64 system_affinity = 0LL;
\r
241 if (value && value->string) {
\r
242 DWORD_PTR affinity;
\r
243 if (! GetProcessAffinityMask(GetCurrentProcess(), &affinity, (DWORD_PTR *) &system_affinity)) system_affinity = ~0;
\r
245 if (is_default(value->string) || str_equiv(value->string, NSSM_AFFINITY_ALL)) mask = 0LL;
\r
246 else if (affinity_string_to_mask(value->string, &mask)) {
\r
247 print_message(stderr, NSSM_MESSAGE_BOGUS_AFFINITY_MASK, value->string, num_cpus() - 1);
\r
254 error = RegDeleteValue(key, name);
\r
255 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
\r
256 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
\r
260 /* Canonicalise. */
\r
262 if (affinity_mask_to_string(mask, &canon)) canon = value->string;
\r
264 __int64 effective_affinity = mask & system_affinity;
\r
265 if (effective_affinity != mask) {
\r
266 /* Requested CPUs did not intersect with available CPUs? */
\r
267 if (! effective_affinity) mask = effective_affinity = system_affinity;
\r
270 if (! affinity_mask_to_string(system_affinity, &system)) {
\r
271 TCHAR *effective = 0;
\r
272 if (! affinity_mask_to_string(effective_affinity, &effective)) {
\r
273 print_message(stderr, NSSM_MESSAGE_EFFECTIVE_AFFINITY_MASK, value->string, system, effective);
\r
274 HeapFree(GetProcessHeap(), 0, effective);
\r
276 HeapFree(GetProcessHeap(), 0, system);
\r
280 if (RegSetValueEx(key, name, 0, REG_SZ, (const unsigned char *) canon, (unsigned long) (_tcslen(canon) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
281 if (canon != value->string) HeapFree(GetProcessHeap(), 0, canon);
\r
282 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, name, error_string(GetLastError()), 0);
\r
286 if (canon != value->string) HeapFree(GetProcessHeap(), 0, canon);
\r
290 static int setting_get_affinity(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
291 HKEY key = (HKEY) param;
\r
292 if (! key) return -1;
\r
294 unsigned long type;
\r
296 unsigned long buflen = 0;
\r
298 int ret = RegQueryValueEx(key, name, 0, &type, 0, &buflen);
\r
299 if (ret == ERROR_FILE_NOT_FOUND) {
\r
300 if (value_from_string(name, value, NSSM_AFFINITY_ALL) == 1) return 0;
\r
303 if (ret != ERROR_SUCCESS) return -1;
\r
305 if (type != REG_SZ) return -1;
\r
307 buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, buflen);
\r
309 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("affinity"), _T("setting_get_affinity"));
\r
313 if (get_string(key, (TCHAR *) name, buffer, buflen, false, false, true)) {
\r
314 HeapFree(GetProcessHeap(), 0, buffer);
\r
319 if (affinity_string_to_mask(buffer, &affinity)) {
\r
320 print_message(stderr, NSSM_MESSAGE_BOGUS_AFFINITY_MASK, buffer, num_cpus() - 1);
\r
321 HeapFree(GetProcessHeap(), 0, buffer);
\r
325 HeapFree(GetProcessHeap(), 0, buffer);
\r
327 /* Canonicalise. */
\r
328 if (affinity_mask_to_string(affinity, &buffer)) {
\r
329 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
\r
333 ret = value_from_string(name, value, buffer);
\r
334 HeapFree(GetProcessHeap(), 0, buffer);
\r
338 static int setting_set_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
339 HKEY key = (HKEY) param;
\r
340 if (! param) return -1;
\r
343 TCHAR *unformatted = 0;
\r
344 unsigned long envlen;
\r
345 unsigned long newlen = 0;
\r
347 if (value && value->string && value->string[0]) {
\r
348 string = value->string;
\r
349 switch (string[0]) {
\r
350 case _T('+'): op = 1; break;
\r
351 case _T('-'): op = -1; break;
\r
352 case _T(':'): string++; break;
\r
359 if (get_environment((TCHAR *) service_name, key, (TCHAR *) name, &env, &envlen)) return -1;
\r
362 if (op > 0) ret = append_to_environment_block(env, envlen, string, &unformatted, &newlen);
\r
363 else ret = remove_from_environment_block(env, envlen, string, &unformatted, &newlen);
\r
364 if (envlen) HeapFree(GetProcessHeap(), 0, env);
\r
365 if (ret) return -1;
\r
367 string = unformatted;
\r
371 No existing environment.
\r
372 We can't remove from an empty environment so just treat an add
\r
373 operation as setting a new string.
\r
375 if (op < 0) return 0;
\r
380 if (! string || ! string[0]) {
\r
381 long error = RegDeleteValue(key, name);
\r
382 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
\r
383 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
\r
388 if (unformat_double_null(string, (unsigned long) _tcslen(string), &unformatted, &newlen)) return -1;
\r
391 if (test_environment(unformatted)) {
\r
392 HeapFree(GetProcessHeap(), 0, unformatted);
\r
393 print_message(stderr, NSSM_GUI_INVALID_ENVIRONMENT);
\r
397 if (RegSetValueEx(key, name, 0, REG_MULTI_SZ, (const unsigned char *) unformatted, (unsigned long) newlen * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
398 if (newlen) HeapFree(GetProcessHeap(), 0, unformatted);
\r
399 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV, error_string(GetLastError()), 0);
\r
403 if (newlen) HeapFree(GetProcessHeap(), 0, unformatted);
\r
407 static int setting_get_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
408 HKEY key = (HKEY) param;
\r
409 if (! param) return -1;
\r
412 unsigned long envlen;
\r
413 if (get_environment((TCHAR *) service_name, key, (TCHAR *) name, &env, &envlen)) return -1;
\r
414 if (! envlen) return 0;
\r
417 unsigned long newlen;
\r
418 if (format_double_null(env, envlen, &formatted, &newlen)) return -1;
\r
422 /* Find named environment variable. */
\r
424 size_t len = _tcslen(additional);
\r
425 for (s = env; *s; s++) {
\r
426 /* Look for <additional>=<string> NULL NULL */
\r
427 if (! _tcsnicmp(s, additional, len) && s[len] == _T('=')) {
\r
430 ret = value_from_string(name, value, s);
\r
431 HeapFree(GetProcessHeap(), 0, env);
\r
435 /* Skip this string. */
\r
438 HeapFree(GetProcessHeap(), 0, env);
\r
442 HeapFree(GetProcessHeap(), 0, env);
\r
444 ret = value_from_string(name, value, formatted);
\r
445 if (newlen) HeapFree(GetProcessHeap(), 0, formatted);
\r
449 static int setting_set_priority(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
450 HKEY key = (HKEY) param;
\r
451 if (! param) return -1;
\r
453 TCHAR *priority_string;
\r
457 if (value && value->string) priority_string = value->string;
\r
458 else if (default_value) priority_string = (TCHAR *) default_value;
\r
460 error = RegDeleteValue(key, name);
\r
461 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
\r
462 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
\r
466 for (i = 0; priority_strings[i]; i++) {
\r
467 if (! str_equiv(priority_strings[i], priority_string)) continue;
\r
469 if (default_value && str_equiv(priority_string, (TCHAR *) default_value)) {
\r
470 error = RegDeleteValue(key, name);
\r
471 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
\r
472 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
\r
476 if (set_number(key, (TCHAR *) name, priority_index_to_constant(i))) return -1;
\r
480 print_message(stderr, NSSM_MESSAGE_INVALID_PRIORITY, priority_string);
\r
481 for (i = 0; priority_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), priority_strings[i]);
\r
486 static int setting_get_priority(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
487 HKEY key = (HKEY) param;
\r
488 if (! param) return -1;
\r
490 unsigned long constant;
\r
491 switch (get_number(key, (TCHAR *) name, &constant, false)) {
\r
493 if (value_from_string(name, value, (const TCHAR *) default_value) == -1) return -1;
\r
495 case -1: return -1;
\r
498 return value_from_string(name, value, priority_strings[priority_constant_to_index(constant)]);
\r
501 /* Functions to manage native service settings. */
\r
502 static int native_set_dependon(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR **dependencies, unsigned long *dependencieslen, value_t *value, int type) {
\r
503 *dependencieslen = 0;
\r
504 if (! value || ! value->string || ! value->string[0]) return 0;
\r
506 TCHAR *string = value->string;
\r
507 unsigned long buflen;
\r
509 switch (string[0]) {
\r
510 case _T('+'): op = 1; break;
\r
511 case _T('-'): op = -1; break;
\r
512 case _T(':'): string++; break;
\r
518 if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, type)) return -1;
\r
521 if (op > 0) ret = append_to_dependencies(buffer, buflen, string, dependencies, dependencieslen, type);
\r
522 else ret = remove_from_dependencies(buffer, buflen, string, dependencies, dependencieslen, type);
\r
523 if (buflen) HeapFree(GetProcessHeap(), 0, buffer);
\r
529 We can't remove from an empty list so just treat an add
\r
530 operation as setting a new string.
\r
532 if (op < 0) return 0;
\r
538 TCHAR *unformatted = 0;
\r
539 unsigned long newlen;
\r
540 if (unformat_double_null(string, (unsigned long) _tcslen(string), &unformatted, &newlen)) return -1;
\r
542 if (type == DEPENDENCY_GROUPS) {
\r
543 /* Prepend group identifier. */
\r
544 unsigned long missing = 0;
\r
545 TCHAR *canon = unformatted;
\r
546 size_t canonlen = 0;
\r
548 for (s = unformatted; *s; s++) {
\r
549 if (*s != SC_GROUP_IDENTIFIER) missing++;
\r
550 size_t len = _tcslen(s);
\r
551 canonlen += len + 1;
\r
556 /* Missing identifiers plus double NULL terminator. */
\r
557 canonlen += missing + 1;
\r
559 canon = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, canonlen * sizeof(TCHAR));
\r
561 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canon"), _T("native_set_dependon"));
\r
562 if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);
\r
567 for (s = unformatted; *s; s++) {
\r
568 if (*s != SC_GROUP_IDENTIFIER) canon[i++] = SC_GROUP_IDENTIFIER;
\r
569 size_t len = _tcslen(s);
\r
570 memmove(canon + i, s, (len + 1) * sizeof(TCHAR));
\r
575 HeapFree(GetProcessHeap(), 0, unformatted);
\r
576 unformatted = canon;
\r
577 newlen = (unsigned long) canonlen;
\r
581 *dependencies = unformatted;
\r
582 *dependencieslen = newlen;
\r
588 static int native_set_dependongroup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
589 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
590 if (! service_handle) return -1;
\r
593 Get existing service dependencies because we must set both types together.
\r
595 TCHAR *services_buffer;
\r
596 unsigned long services_buflen;
\r
597 if (get_service_dependencies(service_name, service_handle, &services_buffer, &services_buflen, DEPENDENCY_SERVICES)) return -1;
\r
599 if (! value || ! value->string || ! value->string[0]) {
\r
600 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, services_buffer, 0, 0, 0)) {
\r
601 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
602 if (services_buffer) HeapFree(GetProcessHeap(), 0, services_buffer);
\r
606 if (services_buffer) HeapFree(GetProcessHeap(), 0, services_buffer);
\r
610 /* Update the group list. */
\r
611 TCHAR *groups_buffer;
\r
612 unsigned long groups_buflen;
\r
613 if (native_set_dependon(service_name, service_handle, &groups_buffer, &groups_buflen, value, DEPENDENCY_GROUPS)) return -1;
\r
615 TCHAR *dependencies;
\r
616 if (services_buflen > 2) {
\r
617 dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (groups_buflen + services_buflen) * sizeof(TCHAR));
\r
618 if (! dependencies) {
\r
619 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("dependencies"), _T("native_set_dependongroup"));
\r
620 if (groups_buffer) HeapFree(GetProcessHeap(), 0, groups_buffer);
\r
621 if (services_buffer) HeapFree(GetProcessHeap(), 0, services_buffer);
\r
625 memmove(dependencies, services_buffer, services_buflen * sizeof(TCHAR));
\r
626 memmove(dependencies + services_buflen - 1, groups_buffer, groups_buflen * sizeof(TCHAR));
\r
628 else dependencies = groups_buffer;
\r
631 if (set_service_dependencies(service_name, service_handle, dependencies)) ret = -1;
\r
632 if (dependencies != groups_buffer) HeapFree(GetProcessHeap(), 0, dependencies);
\r
633 if (groups_buffer) HeapFree(GetProcessHeap(), 0, groups_buffer);
\r
634 if (services_buffer) HeapFree(GetProcessHeap(), 0, services_buffer);
\r
639 static int native_get_dependongroup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
640 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
641 if (! service_handle) return -1;
\r
644 unsigned long buflen;
\r
645 if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_GROUPS)) return -1;
\r
650 unsigned long newlen;
\r
651 if (format_double_null(buffer, buflen, &formatted, &newlen)) {
\r
652 HeapFree(GetProcessHeap(), 0, buffer);
\r
656 ret = value_from_string(name, value, formatted);
\r
657 HeapFree(GetProcessHeap(), 0, formatted);
\r
658 HeapFree(GetProcessHeap(), 0, buffer);
\r
668 static int native_set_dependonservice(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
669 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
670 if (! service_handle) return -1;
\r
673 Get existing group dependencies because we must set both types together.
\r
675 TCHAR *groups_buffer;
\r
676 unsigned long groups_buflen;
\r
677 if (get_service_dependencies(service_name, service_handle, &groups_buffer, &groups_buflen, DEPENDENCY_GROUPS)) return -1;
\r
679 if (! value || ! value->string || ! value->string[0]) {
\r
680 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, groups_buffer, 0, 0, 0)) {
\r
681 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
682 if (groups_buffer) HeapFree(GetProcessHeap(), 0, groups_buffer);
\r
686 if (groups_buffer) HeapFree(GetProcessHeap(), 0, groups_buffer);
\r
690 /* Update the service list. */
\r
691 TCHAR *services_buffer;
\r
692 unsigned long services_buflen;
\r
693 if (native_set_dependon(service_name, service_handle, &services_buffer, &services_buflen, value, DEPENDENCY_SERVICES)) return -1;
\r
695 TCHAR *dependencies;
\r
696 if (groups_buflen > 2) {
\r
697 dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (services_buflen + groups_buflen) * sizeof(TCHAR));
\r
698 if (! dependencies) {
\r
699 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("dependencies"), _T("native_set_dependonservice"));
\r
700 if (groups_buffer) HeapFree(GetProcessHeap(), 0, groups_buffer);
\r
701 if (services_buffer) HeapFree(GetProcessHeap(), 0, services_buffer);
\r
705 memmove(dependencies, services_buffer, services_buflen * sizeof(TCHAR));
\r
706 memmove(dependencies + services_buflen - 1, groups_buffer, groups_buflen * sizeof(TCHAR));
\r
708 else dependencies = services_buffer;
\r
711 if (set_service_dependencies(service_name, service_handle, dependencies)) ret = -1;
\r
712 if (dependencies != services_buffer) HeapFree(GetProcessHeap(), 0, dependencies);
\r
713 if (groups_buffer) HeapFree(GetProcessHeap(), 0, groups_buffer);
\r
714 if (services_buffer) HeapFree(GetProcessHeap(), 0, services_buffer);
\r
719 static int native_get_dependonservice(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
720 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
721 if (! service_handle) return -1;
\r
724 unsigned long buflen;
\r
725 if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_SERVICES)) return -1;
\r
730 unsigned long newlen;
\r
731 if (format_double_null(buffer, buflen, &formatted, &newlen)) {
\r
732 HeapFree(GetProcessHeap(), 0, buffer);
\r
736 ret = value_from_string(name, value, formatted);
\r
737 HeapFree(GetProcessHeap(), 0, formatted);
\r
738 HeapFree(GetProcessHeap(), 0, buffer);
\r
748 int native_set_description(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
749 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
750 if (! service_handle) return -1;
\r
752 TCHAR *description = 0;
\r
753 if (value) description = value->string;
\r
754 if (set_service_description(service_name, service_handle, description)) return -1;
\r
756 if (description && description[0]) return 1;
\r
761 int native_get_description(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
762 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
763 if (! service_handle) return -1;
\r
765 TCHAR buffer[VALUE_LENGTH];
\r
766 if (get_service_description(service_name, service_handle, _countof(buffer), buffer)) return -1;
\r
768 if (buffer[0]) return value_from_string(name, value, buffer);
\r
774 int native_set_displayname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
775 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
776 if (! service_handle) return -1;
\r
778 TCHAR *displayname = 0;
\r
779 if (value && value->string) displayname = value->string;
\r
780 else displayname = (TCHAR *) service_name;
\r
782 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, displayname)) {
\r
783 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
788 If the display name and service name differ only in case,
\r
789 ChangeServiceConfig() will return success but the display name will be
\r
790 set to the service name, NOT the value passed to the function.
\r
791 This appears to be a quirk of Windows rather than a bug here.
\r
793 if (displayname != service_name && ! str_equiv(displayname, service_name)) return 1;
\r
798 int native_get_displayname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
799 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
800 if (! service_handle) return -1;
\r
802 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
803 if (! qsc) return -1;
\r
805 int ret = value_from_string(name, value, qsc->lpDisplayName);
\r
806 HeapFree(GetProcessHeap(), 0, qsc);
\r
811 int native_set_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
812 HKEY key = open_service_registry(service_name, KEY_SET_VALUE, true);
\r
813 if (! key) return -1;
\r
815 int ret = setting_set_environment(service_name, (void *) key, name, default_value, value, additional);
\r
820 int native_get_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
821 HKEY key = open_service_registry(service_name, KEY_READ, true);
\r
822 if (! key) return -1;
\r
824 ZeroMemory(value, sizeof(value_t));
\r
825 int ret = setting_get_environment(service_name, (void *) key, name, default_value, value, additional);
\r
830 int native_set_imagepath(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
831 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
832 if (! service_handle) return -1;
\r
834 /* It makes no sense to try to reset the image path. */
\r
835 if (! value || ! value->string) {
\r
836 print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);
\r
840 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, value->string, 0, 0, 0, 0, 0, 0)) {
\r
841 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
848 int native_get_imagepath(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
849 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
850 if (! service_handle) return -1;
\r
852 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
853 if (! qsc) return -1;
\r
855 int ret = value_from_string(name, value, qsc->lpBinaryPathName);
\r
856 HeapFree(GetProcessHeap(), 0, qsc);
\r
861 int native_set_name(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
862 print_message(stderr, NSSM_MESSAGE_CANNOT_RENAME_SERVICE);
\r
866 int native_get_name(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
867 return value_from_string(name, value, service_name);
\r
870 int native_set_objectname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
871 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
872 if (! service_handle) return -1;
\r
875 Logical syntax is: nssm set <service> ObjectName <username> <password>
\r
876 That means the username is actually passed in the additional parameter.
\r
878 bool localsystem = false;
\r
879 TCHAR *username = NSSM_LOCALSYSTEM_ACCOUNT;
\r
880 TCHAR *password = 0;
\r
882 username = (TCHAR *) additional;
\r
883 if (value && value->string) password = value->string;
\r
885 else if (value && value->string) username = value->string;
\r
887 const TCHAR *well_known = well_known_username(username);
\r
888 size_t passwordsize = 0;
\r
890 if (str_equiv(well_known, NSSM_LOCALSYSTEM_ACCOUNT)) localsystem = true;
\r
891 username = (TCHAR *) well_known;
\r
894 else if (! password) {
\r
895 /* We need a password if the account requires it. */
\r
896 print_message(stderr, NSSM_MESSAGE_MISSING_PASSWORD, name);
\r
899 else passwordsize = _tcslen(password) * sizeof(TCHAR);
\r
902 ChangeServiceConfig() will fail to set the username if the service is set
\r
903 to interact with the desktop.
\r
905 unsigned long type = SERVICE_NO_CHANGE;
\r
906 if (! localsystem) {
\r
907 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
909 if (passwordsize) SecureZeroMemory(password, passwordsize);
\r
913 type = qsc->dwServiceType & ~SERVICE_INTERACTIVE_PROCESS;
\r
914 HeapFree(GetProcessHeap(), 0, qsc);
\r
917 if (! well_known) {
\r
918 if (grant_logon_as_service(username)) {
\r
919 if (passwordsize) SecureZeroMemory(password, passwordsize);
\r
920 print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);
\r
925 if (! ChangeServiceConfig(service_handle, type, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, username, password, 0)) {
\r
926 if (passwordsize) SecureZeroMemory(password, passwordsize);
\r
927 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
931 if (passwordsize) SecureZeroMemory(password, passwordsize);
\r
933 if (localsystem) return 0;
\r
938 int native_get_objectname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
939 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
940 if (! service_handle) return -1;
\r
942 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
943 if (! qsc) return -1;
\r
945 int ret = value_from_string(name, value, qsc->lpServiceStartName);
\r
946 HeapFree(GetProcessHeap(), 0, qsc);
\r
951 int native_set_startup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
952 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
953 if (! service_handle) return -1;
\r
955 /* It makes no sense to try to reset the startup type. */
\r
956 if (! value || ! value->string) {
\r
957 print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);
\r
961 /* Map NSSM_STARTUP_* constant to Windows SERVICE_*_START constant. */
\r
962 int service_startup = -1;
\r
964 for (i = 0; startup_strings[i]; i++) {
\r
965 if (str_equiv(value->string, startup_strings[i])) {
\r
966 service_startup = i;
\r
971 if (service_startup < 0) {
\r
972 print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE_STARTUP, value->string);
\r
973 for (i = 0; startup_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), startup_strings[i]);
\r
977 unsigned long startup;
\r
978 switch (service_startup) {
\r
979 case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;
\r
980 case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;
\r
981 default: startup = SERVICE_AUTO_START;
\r
984 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, startup, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) {
\r
985 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
989 SERVICE_DELAYED_AUTO_START_INFO delayed;
\r
990 ZeroMemory(&delayed, sizeof(delayed));
\r
991 if (service_startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;
\r
992 else delayed.fDelayedAutostart = 0;
\r
993 if (! ChangeServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {
\r
994 unsigned long error = GetLastError();
\r
995 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
996 if (error != ERROR_INVALID_LEVEL) {
\r
997 log_event(EVENTLOG_ERROR_TYPE, NSSM_MESSAGE_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service_name, error_string(error), 0);
\r
1004 int native_get_startup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
1005 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
1006 if (! service_handle) return -1;
\r
1008 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
1009 if (! qsc) return -1;
\r
1011 unsigned long startup;
\r
1012 int ret = get_service_startup(service_name, service_handle, qsc, &startup);
\r
1013 HeapFree(GetProcessHeap(), 0, qsc);
\r
1015 if (ret) return -1;
\r
1018 for (i = 0; startup_strings[i]; i++);
\r
1019 if (startup >= i) return -1;
\r
1021 return value_from_string(name, value, startup_strings[startup]);
\r
1024 int native_set_type(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
1025 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
1026 if (! service_handle) return -1;
\r
1028 /* It makes no sense to try to reset the service type. */
\r
1029 if (! value || ! value->string) {
\r
1030 print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);
\r
1035 We can only manage services of type SERVICE_WIN32_OWN_PROCESS
\r
1036 and SERVICE_INTERACTIVE_PROCESS.
\r
1038 unsigned long type = SERVICE_WIN32_OWN_PROCESS;
\r
1039 if (str_equiv(value->string, NSSM_INTERACTIVE_PROCESS)) type |= SERVICE_INTERACTIVE_PROCESS;
\r
1040 else if (! str_equiv(value->string, NSSM_WIN32_OWN_PROCESS)) {
\r
1041 print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE_TYPE, value->string);
\r
1042 _ftprintf(stderr, _T("%s\n"), NSSM_WIN32_OWN_PROCESS);
\r
1043 _ftprintf(stderr, _T("%s\n"), NSSM_INTERACTIVE_PROCESS);
\r
1048 ChangeServiceConfig() will fail if the service runs under an account
\r
1049 other than LOCALSYSTEM and we try to make it interactive.
\r
1051 if (type & SERVICE_INTERACTIVE_PROCESS) {
\r
1052 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
1053 if (! qsc) return -1;
\r
1055 if (! str_equiv(qsc->lpServiceStartName, NSSM_LOCALSYSTEM_ACCOUNT)) {
\r
1056 HeapFree(GetProcessHeap(), 0, qsc);
\r
1057 print_message(stderr, NSSM_MESSAGE_INTERACTIVE_NOT_LOCALSYSTEM, value->string, service_name, NSSM_LOCALSYSTEM_ACCOUNT);
\r
1061 HeapFree(GetProcessHeap(), 0, qsc);
\r
1064 if (! ChangeServiceConfig(service_handle, type, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) {
\r
1065 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
1072 int native_get_type(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
1073 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
1074 if (! service_handle) return -1;
\r
1076 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
1077 if (! qsc) return -1;
\r
1079 value->numeric = qsc->dwServiceType;
\r
1080 HeapFree(GetProcessHeap(), 0, qsc);
\r
1082 const TCHAR *string;
\r
1083 switch (value->numeric) {
\r
1084 case SERVICE_KERNEL_DRIVER: string = NSSM_KERNEL_DRIVER; break;
\r
1085 case SERVICE_FILE_SYSTEM_DRIVER: string = NSSM_FILE_SYSTEM_DRIVER; break;
\r
1086 case SERVICE_WIN32_OWN_PROCESS: string = NSSM_WIN32_OWN_PROCESS; break;
\r
1087 case SERVICE_WIN32_SHARE_PROCESS: string = NSSM_WIN32_SHARE_PROCESS; break;
\r
1088 case SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS: string = NSSM_INTERACTIVE_PROCESS; break;
\r
1089 case SERVICE_WIN32_SHARE_PROCESS|SERVICE_INTERACTIVE_PROCESS: string = NSSM_SHARE_INTERACTIVE_PROCESS; break;
\r
1090 default: string = NSSM_UNKNOWN;
\r
1093 return value_from_string(name, value, string);
\r
1096 int set_setting(const TCHAR *service_name, HKEY key, settings_t *setting, value_t *value, const TCHAR *additional) {
\r
1097 if (! key) return -1;
\r
1100 if (setting->set) ret = setting->set(service_name, (void *) key, setting->name, setting->default_value, value, additional);
\r
1103 if (! ret) print_message(stdout, NSSM_MESSAGE_RESET_SETTING, setting->name, service_name);
\r
1104 else if (ret > 0) print_message(stdout, NSSM_MESSAGE_SET_SETTING, setting->name, service_name);
\r
1105 else print_message(stderr, NSSM_MESSAGE_SET_SETTING_FAILED, setting->name, service_name);
\r
1110 int set_setting(const TCHAR *service_name, SC_HANDLE service_handle, settings_t *setting, value_t *value, const TCHAR *additional) {
\r
1111 if (! service_handle) return -1;
\r
1114 if (setting->set) ret = setting->set(service_name, service_handle, setting->name, setting->default_value, value, additional);
\r
1117 if (! ret) print_message(stdout, NSSM_MESSAGE_RESET_SETTING, setting->name, service_name);
\r
1118 else if (ret > 0) print_message(stdout, NSSM_MESSAGE_SET_SETTING, setting->name, service_name);
\r
1119 else print_message(stderr, NSSM_MESSAGE_SET_SETTING_FAILED, setting->name, service_name);
\r
1125 Returns: 1 if the value was retrieved.
\r
1126 0 if the default value was retrieved.
\r
1129 int get_setting(const TCHAR *service_name, HKEY key, settings_t *setting, value_t *value, const TCHAR *additional) {
\r
1130 if (! key) return -1;
\r
1133 switch (setting->type) {
\r
1134 case REG_EXPAND_SZ:
\r
1135 case REG_MULTI_SZ:
\r
1137 value->string = (TCHAR *) setting->default_value;
\r
1138 if (setting->get) ret = setting->get(service_name, (void *) key, setting->name, setting->default_value, value, additional);
\r
1143 value->numeric = PtrToUlong(setting->default_value);
\r
1144 if (setting->get) ret = setting->get(service_name, (void *) key, setting->name, setting->default_value, value, additional);
\r
1153 if (ret < 0) print_message(stderr, NSSM_MESSAGE_GET_SETTING_FAILED, setting->name, service_name);
\r
1158 int get_setting(const TCHAR *service_name, SC_HANDLE service_handle, settings_t *setting, value_t *value, const TCHAR *additional) {
\r
1159 if (! service_handle) return -1;
\r
1160 return setting->get(service_name, service_handle, setting->name, 0, value, additional);
\r
1163 settings_t settings[] = {
\r
1164 { NSSM_REG_EXE, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },
\r
1165 { NSSM_REG_FLAGS, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },
\r
1166 { NSSM_REG_DIR, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },
\r
1167 { NSSM_REG_EXIT, REG_SZ, (void *) exit_action_strings[NSSM_EXIT_RESTART], false, ADDITIONAL_MANDATORY, setting_set_exit_action, setting_get_exit_action },
\r
1168 { NSSM_REG_HOOK, REG_SZ, (void *) _T(""), false, ADDITIONAL_MANDATORY, setting_set_hook, setting_get_hook },
\r
1169 { NSSM_REG_AFFINITY, REG_SZ, 0, false, 0, setting_set_affinity, setting_get_affinity },
\r
1170 { NSSM_REG_ENV, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment },
\r
1171 { NSSM_REG_ENV_EXTRA, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment },
\r
1172 { NSSM_REG_NO_CONSOLE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1173 { NSSM_REG_PRIORITY, REG_SZ, (void *) priority_strings[NSSM_NORMAL_PRIORITY], false, 0, setting_set_priority, setting_get_priority },
\r
1174 { NSSM_REG_RESTART_DELAY, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1175 { NSSM_REG_STDIN, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },
\r
1176 { NSSM_REG_STDIN NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDIN_SHARING, false, 0, setting_set_number, setting_get_number },
\r
1177 { NSSM_REG_STDIN NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDIN_DISPOSITION, false, 0, setting_set_number, setting_get_number },
\r
1178 { NSSM_REG_STDIN NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDIN_FLAGS, false, 0, setting_set_number, setting_get_number },
\r
1179 { NSSM_REG_STDOUT, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },
\r
1180 { NSSM_REG_STDOUT NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDOUT_SHARING, false, 0, setting_set_number, setting_get_number },
\r
1181 { NSSM_REG_STDOUT NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDOUT_DISPOSITION, false, 0, setting_set_number, setting_get_number },
\r
1182 { NSSM_REG_STDOUT NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDOUT_FLAGS, false, 0, setting_set_number, setting_get_number },
\r
1183 { NSSM_REG_STDOUT NSSM_REG_STDIO_COPY_AND_TRUNCATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1184 { NSSM_REG_STDERR, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },
\r
1185 { NSSM_REG_STDERR NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDERR_SHARING, false, 0, setting_set_number, setting_get_number },
\r
1186 { NSSM_REG_STDERR NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDERR_DISPOSITION, false, 0, setting_set_number, setting_get_number },
\r
1187 { NSSM_REG_STDERR NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDERR_FLAGS, false, 0, setting_set_number, setting_get_number },
\r
1188 { NSSM_REG_STDERR NSSM_REG_STDIO_COPY_AND_TRUNCATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1189 { NSSM_REG_STOP_METHOD_SKIP, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1190 { NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_CONSOLE_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },
\r
1191 { NSSM_REG_KILL_WINDOW_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_WINDOW_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },
\r
1192 { NSSM_REG_KILL_THREADS_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_THREADS_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },
\r
1193 { NSSM_REG_KILL_PROCESS_TREE, REG_DWORD, (void *) 1, false, 0, setting_set_number, setting_get_number },
\r
1194 { NSSM_REG_THROTTLE, REG_DWORD, (void *) NSSM_RESET_THROTTLE_RESTART, false, 0, setting_set_number, setting_get_number },
\r
1195 { NSSM_REG_HOOK_SHARE_OUTPUT_HANDLES, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1196 { NSSM_REG_ROTATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1197 { NSSM_REG_ROTATE_ONLINE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1198 { NSSM_REG_ROTATE_SECONDS, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1199 { NSSM_REG_ROTATE_BYTES_LOW, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1200 { NSSM_REG_ROTATE_BYTES_HIGH, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1201 { NSSM_REG_ROTATE_DELAY, REG_DWORD, (void *) NSSM_ROTATE_DELAY, false, 0, setting_set_number, setting_get_number },
\r
1202 { NSSM_NATIVE_DEPENDONGROUP, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_dependongroup, native_get_dependongroup },
\r
1203 { NSSM_NATIVE_DEPENDONSERVICE, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_dependonservice, native_get_dependonservice },
\r
1204 { NSSM_NATIVE_DESCRIPTION, REG_SZ, _T(""), true, 0, native_set_description, native_get_description },
\r
1205 { NSSM_NATIVE_DISPLAYNAME, REG_SZ, NULL, true, 0, native_set_displayname, native_get_displayname },
\r
1206 { NSSM_NATIVE_ENVIRONMENT, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_environment, native_get_environment },
\r
1207 { NSSM_NATIVE_IMAGEPATH, REG_EXPAND_SZ, NULL, true, 0, native_set_imagepath, native_get_imagepath },
\r
1208 { NSSM_NATIVE_OBJECTNAME, REG_SZ, NSSM_LOCALSYSTEM_ACCOUNT, true, 0, native_set_objectname, native_get_objectname },
\r
1209 { NSSM_NATIVE_NAME, REG_SZ, NULL, true, 0, native_set_name, native_get_name },
\r
1210 { NSSM_NATIVE_STARTUP, REG_SZ, NULL, true, 0, native_set_startup, native_get_startup },
\r
1211 { NSSM_NATIVE_TYPE, REG_SZ, NULL, true, 0, native_set_type, native_get_type },
\r
1212 { NULL, NULL, NULL, NULL, NULL }
\r