2 /* XXX: (value && value->string) is probably bogus because value is probably never null */
\r
5 #define NSSM_AFFINITY_ALL _T("All")
\r
7 #define NSSM_DEFAULT_STRING _T("Default")
\r
9 extern const TCHAR *exit_action_strings[];
\r
10 extern const TCHAR *startup_strings[];
\r
11 extern const TCHAR *priority_strings[];
\r
12 extern const TCHAR *hook_event_strings[];
\r
13 extern const TCHAR *hook_action_strings[];
\r
15 /* Does the parameter refer to the default value of the setting? */
\r
16 static inline int is_default(const TCHAR *value) {
\r
17 return (str_equiv(value, NSSM_DEFAULT_STRING) || str_equiv(value, _T("*")) || ! value[0]);
\r
20 static int value_from_string(const TCHAR *name, value_t *value, const TCHAR *string) {
\r
21 size_t len = _tcslen(string);
\r
27 value->string = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));
\r
28 if (! value->string) {
\r
29 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, name, _T("value_from_string()"));
\r
33 if (_sntprintf_s(value->string, len, _TRUNCATE, _T("%s"), string) < 0) {
\r
34 HeapFree(GetProcessHeap(), 0, value->string);
\r
35 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, name, _T("value_from_string()"));
\r
42 /* Functions to manage NSSM-specific settings in the registry. */
\r
43 static int setting_set_number(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
44 HKEY key = (HKEY) param;
\r
45 if (! key) return -1;
\r
47 unsigned long number;
\r
50 /* Resetting to default? */
\r
51 if (! value || ! value->string) {
\r
52 error = RegDeleteValue(key, name);
\r
53 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
\r
54 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
\r
57 if (str_number(value->string, &number)) return -1;
\r
59 if (default_value && number == PtrToUlong(default_value)) {
\r
60 error = RegDeleteValue(key, name);
\r
61 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
\r
62 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
\r
66 if (set_number(key, (TCHAR *) name, number)) return -1;
\r
71 static int setting_get_number(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
72 HKEY key = (HKEY) param;
\r
73 return get_number(key, (TCHAR *) name, &value->numeric, false);
\r
76 static int setting_set_string(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
77 HKEY key = (HKEY) param;
\r
78 if (! key) return -1;
\r
82 /* Resetting to default? */
\r
83 if (! value || ! value->string) {
\r
84 if (default_value) value->string = (TCHAR *) default_value;
\r
86 error = RegDeleteValue(key, name);
\r
87 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
\r
88 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
\r
92 if (default_value && _tcslen((TCHAR *) default_value) && str_equiv(value->string, (TCHAR *) default_value)) {
\r
93 error = RegDeleteValue(key, name);
\r
94 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
\r
95 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
\r
99 if (set_expand_string(key, (TCHAR *) name, value->string)) return -1;
\r
104 static int setting_get_string(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
105 HKEY key = (HKEY) param;
\r
106 TCHAR buffer[VALUE_LENGTH];
\r
108 if (get_string(key, (TCHAR *) name, (TCHAR *) buffer, (unsigned long) sizeof(buffer), false, false, false)) return -1;
\r
110 return value_from_string(name, value, buffer);
\r
113 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
114 unsigned long exitcode;
\r
116 TCHAR action_string[ACTION_LEN];
\r
119 /* Default action? */
\r
120 if (is_default(additional)) code = 0;
\r
122 if (str_number(additional, &exitcode)) return -1;
\r
123 code = (TCHAR *) additional;
\r
127 HKEY key = open_registry(service_name, name, KEY_WRITE);
\r
128 if (! key) return -1;
\r
133 /* Resetting to default? */
\r
134 if (value && value->string) _sntprintf_s(action_string, _countof(action_string), _TRUNCATE, _T("%s"), value->string);
\r
137 /* Delete explicit action. */
\r
138 error = RegDeleteValue(key, code);
\r
140 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
\r
141 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, code, service_name, error_string(error));
\r
145 /* Explicitly keep the default action. */
\r
146 if (default_value) _sntprintf_s(action_string, _countof(action_string), _TRUNCATE, _T("%s"), (TCHAR *) default_value);
\r
151 /* Validate the string. */
\r
152 for (int i = 0; exit_action_strings[i]; i++) {
\r
153 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
154 if (default_value && str_equiv(action_string, (TCHAR *) default_value)) ret = 0;
\r
155 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
156 print_message(stderr, NSSM_MESSAGE_SETVALUE_FAILED, code, service_name, error_string(GetLastError()));
\r
166 print_message(stderr, NSSM_MESSAGE_INVALID_EXIT_ACTION, action_string);
\r
167 for (int i = 0; exit_action_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), exit_action_strings[i]);
\r
172 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
173 unsigned long exitcode = 0;
\r
174 unsigned long *code = 0;
\r
177 if (! is_default(additional)) {
\r
178 if (str_number(additional, &exitcode)) return -1;
\r
183 TCHAR action_string[ACTION_LEN];
\r
184 bool default_action;
\r
185 if (get_exit_action(service_name, code, action_string, &default_action)) return -1;
\r
187 value_from_string(name, value, action_string);
\r
189 if (default_action && ! _tcsnicmp((const TCHAR *) action_string, (TCHAR *) default_value, ACTION_LEN)) return 0;
\r
193 static inline bool split_hook_name(const TCHAR *hook_name, TCHAR *hook_event, TCHAR *hook_action) {
\r
196 for (s = (TCHAR *) hook_name; *s; s++) {
\r
197 if (*s == _T('/')) {
\r
199 _sntprintf_s(hook_event, HOOK_NAME_LENGTH, _TRUNCATE, _T("%s"), hook_name);
\r
201 _sntprintf_s(hook_action, HOOK_NAME_LENGTH, _TRUNCATE, _T("%s"), s);
\r
202 return valid_hook_name(hook_event, hook_action, false);
\r
206 print_message(stderr, NSSM_MESSAGE_INVALID_HOOK_NAME, hook_name);
\r
210 static int setting_set_hook(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
211 TCHAR hook_event[HOOK_NAME_LENGTH];
\r
212 TCHAR hook_action[HOOK_NAME_LENGTH];
\r
213 if (! split_hook_name(additional, hook_event, hook_action)) return -1;
\r
216 if (value && value->string) cmd = value->string;
\r
219 if (set_hook(service_name, hook_event, hook_action, cmd)) return -1;
\r
220 if (! _tcslen(cmd)) return 0;
\r
224 static int setting_get_hook(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
225 TCHAR hook_event[HOOK_NAME_LENGTH];
\r
226 TCHAR hook_action[HOOK_NAME_LENGTH];
\r
227 if (! split_hook_name(additional, hook_event, hook_action)) return -1;
\r
229 TCHAR cmd[CMD_LENGTH];
\r
230 if (get_hook(service_name, hook_event, hook_action, cmd, sizeof(cmd))) return -1;
\r
232 value_from_string(name, value, cmd);
\r
234 if (! _tcslen(cmd)) return 0;
\r
238 static int setting_set_affinity(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
239 HKEY key = (HKEY) param;
\r
240 if (! key) return -1;
\r
244 __int64 system_affinity = 0LL;
\r
246 if (value && value->string) {
\r
247 DWORD_PTR affinity;
\r
248 if (! GetProcessAffinityMask(GetCurrentProcess(), &affinity, (DWORD_PTR *) &system_affinity)) system_affinity = ~0;
\r
250 if (is_default(value->string) || str_equiv(value->string, NSSM_AFFINITY_ALL)) mask = 0LL;
\r
251 else if (affinity_string_to_mask(value->string, &mask)) {
\r
252 print_message(stderr, NSSM_MESSAGE_BOGUS_AFFINITY_MASK, value->string, num_cpus() - 1);
\r
259 error = RegDeleteValue(key, name);
\r
260 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
\r
261 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
\r
265 /* Canonicalise. */
\r
267 if (affinity_mask_to_string(mask, &canon)) canon = value->string;
\r
269 __int64 effective_affinity = mask & system_affinity;
\r
270 if (effective_affinity != mask) {
\r
271 /* Requested CPUs did not intersect with available CPUs? */
\r
272 if (! effective_affinity) mask = effective_affinity = system_affinity;
\r
275 if (! affinity_mask_to_string(system_affinity, &system)) {
\r
276 TCHAR *effective = 0;
\r
277 if (! affinity_mask_to_string(effective_affinity, &effective)) {
\r
278 print_message(stderr, NSSM_MESSAGE_EFFECTIVE_AFFINITY_MASK, value->string, system, effective);
\r
279 HeapFree(GetProcessHeap(), 0, effective);
\r
281 HeapFree(GetProcessHeap(), 0, system);
\r
285 if (RegSetValueEx(key, name, 0, REG_SZ, (const unsigned char *) canon, (unsigned long) (_tcslen(canon) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
286 if (canon != value->string) HeapFree(GetProcessHeap(), 0, canon);
\r
287 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, name, error_string(GetLastError()), 0);
\r
291 if (canon != value->string) HeapFree(GetProcessHeap(), 0, canon);
\r
295 static int setting_get_affinity(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
296 HKEY key = (HKEY) param;
\r
297 if (! key) return -1;
\r
299 unsigned long type;
\r
301 unsigned long buflen = 0;
\r
303 int ret = RegQueryValueEx(key, name, 0, &type, 0, &buflen);
\r
304 if (ret == ERROR_FILE_NOT_FOUND) {
\r
305 if (value_from_string(name, value, NSSM_AFFINITY_ALL) == 1) return 0;
\r
308 if (ret != ERROR_SUCCESS) return -1;
\r
310 if (type != REG_SZ) return -1;
\r
312 buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, buflen);
\r
314 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("affinity"), _T("setting_get_affinity"));
\r
318 if (get_string(key, (TCHAR *) name, buffer, buflen, false, false, true)) {
\r
319 HeapFree(GetProcessHeap(), 0, buffer);
\r
324 if (affinity_string_to_mask(buffer, &affinity)) {
\r
325 print_message(stderr, NSSM_MESSAGE_BOGUS_AFFINITY_MASK, buffer, num_cpus() - 1);
\r
326 HeapFree(GetProcessHeap(), 0, buffer);
\r
330 HeapFree(GetProcessHeap(), 0, buffer);
\r
332 /* Canonicalise. */
\r
333 if (affinity_mask_to_string(affinity, &buffer)) {
\r
334 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
\r
338 ret = value_from_string(name, value, buffer);
\r
339 HeapFree(GetProcessHeap(), 0, buffer);
\r
343 static int setting_set_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
344 HKEY key = (HKEY) param;
\r
345 if (! param) return -1;
\r
348 TCHAR *unformatted = 0;
\r
349 unsigned long envlen;
\r
350 unsigned long newlen = 0;
\r
352 if (value && value->string && value->string[0]) {
\r
353 string = value->string;
\r
354 switch (string[0]) {
\r
355 case _T('+'): op = 1; break;
\r
356 case _T('-'): op = -1; break;
\r
357 case _T(':'): string++; break;
\r
364 if (get_environment((TCHAR *) service_name, key, (TCHAR *) name, &env, &envlen)) return -1;
\r
367 if (op > 0) ret = append_to_environment_block(env, envlen, string, &unformatted, &newlen);
\r
368 else ret = remove_from_environment_block(env, envlen, string, &unformatted, &newlen);
\r
369 if (envlen) HeapFree(GetProcessHeap(), 0, env);
\r
370 if (ret) return -1;
\r
372 string = unformatted;
\r
376 No existing environment.
\r
377 We can't remove from an empty environment so just treat an add
\r
378 operation as setting a new string.
\r
380 if (op < 0) return 0;
\r
385 if (! string || ! string[0]) {
\r
386 long error = RegDeleteValue(key, name);
\r
387 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
\r
388 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
\r
393 if (unformat_double_null(string, (unsigned long) _tcslen(string), &unformatted, &newlen)) return -1;
\r
396 if (test_environment(unformatted)) {
\r
397 HeapFree(GetProcessHeap(), 0, unformatted);
\r
398 print_message(stderr, NSSM_GUI_INVALID_ENVIRONMENT);
\r
402 if (RegSetValueEx(key, name, 0, REG_MULTI_SZ, (const unsigned char *) unformatted, (unsigned long) newlen * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
403 if (newlen) HeapFree(GetProcessHeap(), 0, unformatted);
\r
404 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV, error_string(GetLastError()), 0);
\r
408 if (newlen) HeapFree(GetProcessHeap(), 0, unformatted);
\r
412 static int setting_get_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
413 HKEY key = (HKEY) param;
\r
414 if (! param) return -1;
\r
417 unsigned long envlen;
\r
418 if (get_environment((TCHAR *) service_name, key, (TCHAR *) name, &env, &envlen)) return -1;
\r
419 if (! envlen) return 0;
\r
422 unsigned long newlen;
\r
423 if (format_double_null(env, envlen, &formatted, &newlen)) return -1;
\r
427 /* Find named environment variable. */
\r
429 size_t len = _tcslen(additional);
\r
430 for (s = env; *s; s++) {
\r
431 /* Look for <additional>=<string> NULL NULL */
\r
432 if (! _tcsnicmp(s, additional, len) && s[len] == _T('=')) {
\r
435 ret = value_from_string(name, value, s);
\r
436 HeapFree(GetProcessHeap(), 0, env);
\r
440 /* Skip this string. */
\r
443 HeapFree(GetProcessHeap(), 0, env);
\r
447 HeapFree(GetProcessHeap(), 0, env);
\r
449 ret = value_from_string(name, value, formatted);
\r
450 if (newlen) HeapFree(GetProcessHeap(), 0, formatted);
\r
454 static int setting_set_priority(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
455 HKEY key = (HKEY) param;
\r
456 if (! param) return -1;
\r
458 TCHAR *priority_string;
\r
462 if (value && value->string) priority_string = value->string;
\r
463 else if (default_value) priority_string = (TCHAR *) default_value;
\r
465 error = RegDeleteValue(key, name);
\r
466 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
\r
467 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
\r
471 for (i = 0; priority_strings[i]; i++) {
\r
472 if (! str_equiv(priority_strings[i], priority_string)) continue;
\r
474 if (default_value && str_equiv(priority_string, (TCHAR *) default_value)) {
\r
475 error = RegDeleteValue(key, name);
\r
476 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
\r
477 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
\r
481 if (set_number(key, (TCHAR *) name, priority_index_to_constant(i))) return -1;
\r
485 print_message(stderr, NSSM_MESSAGE_INVALID_PRIORITY, priority_string);
\r
486 for (i = 0; priority_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), priority_strings[i]);
\r
491 static int setting_get_priority(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
492 HKEY key = (HKEY) param;
\r
493 if (! param) return -1;
\r
495 unsigned long constant;
\r
496 switch (get_number(key, (TCHAR *) name, &constant, false)) {
\r
498 if (value_from_string(name, value, (const TCHAR *) default_value) == -1) return -1;
\r
500 case -1: return -1;
\r
503 return value_from_string(name, value, priority_strings[priority_constant_to_index(constant)]);
\r
506 /* Functions to manage native service settings. */
\r
507 static int native_set_dependon(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR **dependencies, unsigned long *dependencieslen, value_t *value, int type) {
\r
508 *dependencieslen = 0;
\r
509 if (! value || ! value->string || ! value->string[0]) return 0;
\r
511 TCHAR *string = value->string;
\r
512 unsigned long buflen;
\r
514 switch (string[0]) {
\r
515 case _T('+'): op = 1; break;
\r
516 case _T('-'): op = -1; break;
\r
517 case _T(':'): string++; break;
\r
523 if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, type)) return -1;
\r
526 if (op > 0) ret = append_to_dependencies(buffer, buflen, string, dependencies, dependencieslen, type);
\r
527 else ret = remove_from_dependencies(buffer, buflen, string, dependencies, dependencieslen, type);
\r
528 if (buflen) HeapFree(GetProcessHeap(), 0, buffer);
\r
534 We can't remove from an empty list so just treat an add
\r
535 operation as setting a new string.
\r
537 if (op < 0) return 0;
\r
543 TCHAR *unformatted = 0;
\r
544 unsigned long newlen;
\r
545 if (unformat_double_null(string, (unsigned long) _tcslen(string), &unformatted, &newlen)) return -1;
\r
547 if (type == DEPENDENCY_GROUPS) {
\r
548 /* Prepend group identifier. */
\r
549 unsigned long missing = 0;
\r
550 TCHAR *canon = unformatted;
\r
551 size_t canonlen = 0;
\r
553 for (s = unformatted; *s; s++) {
\r
554 if (*s != SC_GROUP_IDENTIFIER) missing++;
\r
555 size_t len = _tcslen(s);
\r
556 canonlen += len + 1;
\r
561 /* Missing identifiers plus double NULL terminator. */
\r
562 canonlen += missing + 1;
\r
564 canon = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, canonlen * sizeof(TCHAR));
\r
566 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canon"), _T("native_set_dependon"));
\r
567 if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);
\r
572 for (s = unformatted; *s; s++) {
\r
573 if (*s != SC_GROUP_IDENTIFIER) canon[i++] = SC_GROUP_IDENTIFIER;
\r
574 size_t len = _tcslen(s);
\r
575 memmove(canon + i, s, (len + 1) * sizeof(TCHAR));
\r
580 HeapFree(GetProcessHeap(), 0, unformatted);
\r
581 unformatted = canon;
\r
582 newlen = (unsigned long) canonlen;
\r
586 *dependencies = unformatted;
\r
587 *dependencieslen = newlen;
\r
593 static int native_set_dependongroup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
594 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
595 if (! service_handle) return -1;
\r
598 Get existing service dependencies because we must set both types together.
\r
600 TCHAR *services_buffer;
\r
601 unsigned long services_buflen;
\r
602 if (get_service_dependencies(service_name, service_handle, &services_buffer, &services_buflen, DEPENDENCY_SERVICES)) return -1;
\r
604 if (! value || ! value->string || ! value->string[0]) {
\r
605 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, services_buffer, 0, 0, 0)) {
\r
606 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
607 if (services_buffer) HeapFree(GetProcessHeap(), 0, services_buffer);
\r
611 if (services_buffer) HeapFree(GetProcessHeap(), 0, services_buffer);
\r
615 /* Update the group list. */
\r
616 TCHAR *groups_buffer;
\r
617 unsigned long groups_buflen;
\r
618 if (native_set_dependon(service_name, service_handle, &groups_buffer, &groups_buflen, value, DEPENDENCY_GROUPS)) return -1;
\r
620 TCHAR *dependencies;
\r
621 if (services_buflen > 2) {
\r
622 dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (groups_buflen + services_buflen) * sizeof(TCHAR));
\r
623 if (! dependencies) {
\r
624 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("dependencies"), _T("native_set_dependongroup"));
\r
625 if (groups_buffer) HeapFree(GetProcessHeap(), 0, groups_buffer);
\r
626 if (services_buffer) HeapFree(GetProcessHeap(), 0, services_buffer);
\r
630 memmove(dependencies, services_buffer, services_buflen * sizeof(TCHAR));
\r
631 memmove(dependencies + services_buflen - 1, groups_buffer, groups_buflen * sizeof(TCHAR));
\r
633 else dependencies = groups_buffer;
\r
636 if (set_service_dependencies(service_name, service_handle, dependencies)) ret = -1;
\r
637 if (dependencies != groups_buffer) HeapFree(GetProcessHeap(), 0, dependencies);
\r
638 if (groups_buffer) HeapFree(GetProcessHeap(), 0, groups_buffer);
\r
639 if (services_buffer) HeapFree(GetProcessHeap(), 0, services_buffer);
\r
644 static int native_get_dependongroup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
645 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
646 if (! service_handle) return -1;
\r
649 unsigned long buflen;
\r
650 if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_GROUPS)) return -1;
\r
655 unsigned long newlen;
\r
656 if (format_double_null(buffer, buflen, &formatted, &newlen)) {
\r
657 HeapFree(GetProcessHeap(), 0, buffer);
\r
661 ret = value_from_string(name, value, formatted);
\r
662 HeapFree(GetProcessHeap(), 0, formatted);
\r
663 HeapFree(GetProcessHeap(), 0, buffer);
\r
673 static int native_set_dependonservice(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
674 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
675 if (! service_handle) return -1;
\r
678 Get existing group dependencies because we must set both types together.
\r
680 TCHAR *groups_buffer;
\r
681 unsigned long groups_buflen;
\r
682 if (get_service_dependencies(service_name, service_handle, &groups_buffer, &groups_buflen, DEPENDENCY_GROUPS)) return -1;
\r
684 if (! value || ! value->string || ! value->string[0]) {
\r
685 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, groups_buffer, 0, 0, 0)) {
\r
686 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
687 if (groups_buffer) HeapFree(GetProcessHeap(), 0, groups_buffer);
\r
691 if (groups_buffer) HeapFree(GetProcessHeap(), 0, groups_buffer);
\r
695 /* Update the service list. */
\r
696 TCHAR *services_buffer;
\r
697 unsigned long services_buflen;
\r
698 if (native_set_dependon(service_name, service_handle, &services_buffer, &services_buflen, value, DEPENDENCY_SERVICES)) return -1;
\r
700 TCHAR *dependencies;
\r
701 if (groups_buflen > 2) {
\r
702 dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (services_buflen + groups_buflen) * sizeof(TCHAR));
\r
703 if (! dependencies) {
\r
704 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("dependencies"), _T("native_set_dependonservice"));
\r
705 if (groups_buffer) HeapFree(GetProcessHeap(), 0, groups_buffer);
\r
706 if (services_buffer) HeapFree(GetProcessHeap(), 0, services_buffer);
\r
710 memmove(dependencies, services_buffer, services_buflen * sizeof(TCHAR));
\r
711 memmove(dependencies + services_buflen - 1, groups_buffer, groups_buflen * sizeof(TCHAR));
\r
713 else dependencies = services_buffer;
\r
716 if (set_service_dependencies(service_name, service_handle, dependencies)) ret = -1;
\r
717 if (dependencies != services_buffer) HeapFree(GetProcessHeap(), 0, dependencies);
\r
718 if (groups_buffer) HeapFree(GetProcessHeap(), 0, groups_buffer);
\r
719 if (services_buffer) HeapFree(GetProcessHeap(), 0, services_buffer);
\r
724 static int native_get_dependonservice(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
725 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
726 if (! service_handle) return -1;
\r
729 unsigned long buflen;
\r
730 if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_SERVICES)) return -1;
\r
735 unsigned long newlen;
\r
736 if (format_double_null(buffer, buflen, &formatted, &newlen)) {
\r
737 HeapFree(GetProcessHeap(), 0, buffer);
\r
741 ret = value_from_string(name, value, formatted);
\r
742 HeapFree(GetProcessHeap(), 0, formatted);
\r
743 HeapFree(GetProcessHeap(), 0, buffer);
\r
753 int native_set_description(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
754 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
755 if (! service_handle) return -1;
\r
757 TCHAR *description = 0;
\r
758 if (value) description = value->string;
\r
759 if (set_service_description(service_name, service_handle, description)) return -1;
\r
761 if (description && description[0]) return 1;
\r
766 int native_get_description(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
767 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
768 if (! service_handle) return -1;
\r
770 TCHAR buffer[VALUE_LENGTH];
\r
771 if (get_service_description(service_name, service_handle, _countof(buffer), buffer)) return -1;
\r
773 if (buffer[0]) return value_from_string(name, value, buffer);
\r
779 int native_set_displayname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
780 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
781 if (! service_handle) return -1;
\r
783 TCHAR *displayname = 0;
\r
784 if (value && value->string) displayname = value->string;
\r
785 else displayname = (TCHAR *) service_name;
\r
787 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, displayname)) {
\r
788 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
793 If the display name and service name differ only in case,
\r
794 ChangeServiceConfig() will return success but the display name will be
\r
795 set to the service name, NOT the value passed to the function.
\r
796 This appears to be a quirk of Windows rather than a bug here.
\r
798 if (displayname != service_name && ! str_equiv(displayname, service_name)) return 1;
\r
803 int native_get_displayname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
804 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
805 if (! service_handle) return -1;
\r
807 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
808 if (! qsc) return -1;
\r
810 int ret = value_from_string(name, value, qsc->lpDisplayName);
\r
811 HeapFree(GetProcessHeap(), 0, qsc);
\r
816 int native_set_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
817 HKEY key = open_service_registry(service_name, KEY_SET_VALUE, true);
\r
818 if (! key) return -1;
\r
820 int ret = setting_set_environment(service_name, (void *) key, name, default_value, value, additional);
\r
825 int native_get_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
826 HKEY key = open_service_registry(service_name, KEY_READ, true);
\r
827 if (! key) return -1;
\r
829 ZeroMemory(value, sizeof(value_t));
\r
830 int ret = setting_get_environment(service_name, (void *) key, name, default_value, value, additional);
\r
835 int native_set_imagepath(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
836 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
837 if (! service_handle) return -1;
\r
839 /* It makes no sense to try to reset the image path. */
\r
840 if (! value || ! value->string) {
\r
841 print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);
\r
845 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, value->string, 0, 0, 0, 0, 0, 0)) {
\r
846 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
853 int native_get_imagepath(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
854 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
855 if (! service_handle) return -1;
\r
857 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
858 if (! qsc) return -1;
\r
860 int ret = value_from_string(name, value, qsc->lpBinaryPathName);
\r
861 HeapFree(GetProcessHeap(), 0, qsc);
\r
866 int native_set_name(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
867 print_message(stderr, NSSM_MESSAGE_CANNOT_RENAME_SERVICE);
\r
871 int native_get_name(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
872 return value_from_string(name, value, service_name);
\r
875 int native_set_objectname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
876 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
877 if (! service_handle) return -1;
\r
880 Logical syntax is: nssm set <service> ObjectName <username> <password>
\r
881 That means the username is actually passed in the additional parameter.
\r
883 bool localsystem = false;
\r
884 TCHAR *username = NSSM_LOCALSYSTEM_ACCOUNT;
\r
885 TCHAR *password = 0;
\r
887 username = (TCHAR *) additional;
\r
888 if (value && value->string) password = value->string;
\r
890 else if (value && value->string) username = value->string;
\r
892 const TCHAR *well_known = well_known_username(username);
\r
893 size_t passwordsize = 0;
\r
895 if (str_equiv(well_known, NSSM_LOCALSYSTEM_ACCOUNT)) localsystem = true;
\r
896 username = (TCHAR *) well_known;
\r
899 else if (! password) {
\r
900 /* We need a password if the account requires it. */
\r
901 print_message(stderr, NSSM_MESSAGE_MISSING_PASSWORD, name);
\r
904 else passwordsize = _tcslen(password) * sizeof(TCHAR);
\r
907 ChangeServiceConfig() will fail to set the username if the service is set
\r
908 to interact with the desktop.
\r
910 unsigned long type = SERVICE_NO_CHANGE;
\r
911 if (! localsystem) {
\r
912 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
914 if (passwordsize) SecureZeroMemory(password, passwordsize);
\r
918 type = qsc->dwServiceType & ~SERVICE_INTERACTIVE_PROCESS;
\r
919 HeapFree(GetProcessHeap(), 0, qsc);
\r
922 if (! well_known) {
\r
923 if (grant_logon_as_service(username)) {
\r
924 if (passwordsize) SecureZeroMemory(password, passwordsize);
\r
925 print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);
\r
930 if (! ChangeServiceConfig(service_handle, type, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, username, password, 0)) {
\r
931 if (passwordsize) SecureZeroMemory(password, passwordsize);
\r
932 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
936 if (passwordsize) SecureZeroMemory(password, passwordsize);
\r
938 if (localsystem) return 0;
\r
943 int native_get_objectname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
944 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
945 if (! service_handle) return -1;
\r
947 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
948 if (! qsc) return -1;
\r
950 int ret = value_from_string(name, value, qsc->lpServiceStartName);
\r
951 HeapFree(GetProcessHeap(), 0, qsc);
\r
956 int native_set_startup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
957 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
958 if (! service_handle) return -1;
\r
960 /* It makes no sense to try to reset the startup type. */
\r
961 if (! value || ! value->string) {
\r
962 print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);
\r
966 /* Map NSSM_STARTUP_* constant to Windows SERVICE_*_START constant. */
\r
967 int service_startup = -1;
\r
969 for (i = 0; startup_strings[i]; i++) {
\r
970 if (str_equiv(value->string, startup_strings[i])) {
\r
971 service_startup = i;
\r
976 if (service_startup < 0) {
\r
977 print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE_STARTUP, value->string);
\r
978 for (i = 0; startup_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), startup_strings[i]);
\r
982 unsigned long startup;
\r
983 switch (service_startup) {
\r
984 case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;
\r
985 case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;
\r
986 default: startup = SERVICE_AUTO_START;
\r
989 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, startup, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) {
\r
990 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
994 SERVICE_DELAYED_AUTO_START_INFO delayed;
\r
995 ZeroMemory(&delayed, sizeof(delayed));
\r
996 if (service_startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;
\r
997 else delayed.fDelayedAutostart = 0;
\r
998 if (! ChangeServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {
\r
999 unsigned long error = GetLastError();
\r
1000 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
1001 if (error != ERROR_INVALID_LEVEL) {
\r
1002 log_event(EVENTLOG_ERROR_TYPE, NSSM_MESSAGE_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service_name, error_string(error), 0);
\r
1009 int native_get_startup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
1010 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
1011 if (! service_handle) return -1;
\r
1013 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
1014 if (! qsc) return -1;
\r
1016 unsigned long startup;
\r
1017 int ret = get_service_startup(service_name, service_handle, qsc, &startup);
\r
1018 HeapFree(GetProcessHeap(), 0, qsc);
\r
1020 if (ret) return -1;
\r
1023 for (i = 0; startup_strings[i]; i++);
\r
1024 if (startup >= i) return -1;
\r
1026 return value_from_string(name, value, startup_strings[startup]);
\r
1029 int native_set_type(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
1030 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
1031 if (! service_handle) return -1;
\r
1033 /* It makes no sense to try to reset the service type. */
\r
1034 if (! value || ! value->string) {
\r
1035 print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);
\r
1040 We can only manage services of type SERVICE_WIN32_OWN_PROCESS
\r
1041 and SERVICE_INTERACTIVE_PROCESS.
\r
1043 unsigned long type = SERVICE_WIN32_OWN_PROCESS;
\r
1044 if (str_equiv(value->string, NSSM_INTERACTIVE_PROCESS)) type |= SERVICE_INTERACTIVE_PROCESS;
\r
1045 else if (! str_equiv(value->string, NSSM_WIN32_OWN_PROCESS)) {
\r
1046 print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE_TYPE, value->string);
\r
1047 _ftprintf(stderr, _T("%s\n"), NSSM_WIN32_OWN_PROCESS);
\r
1048 _ftprintf(stderr, _T("%s\n"), NSSM_INTERACTIVE_PROCESS);
\r
1053 ChangeServiceConfig() will fail if the service runs under an account
\r
1054 other than LOCALSYSTEM and we try to make it interactive.
\r
1056 if (type & SERVICE_INTERACTIVE_PROCESS) {
\r
1057 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
1058 if (! qsc) return -1;
\r
1060 if (! str_equiv(qsc->lpServiceStartName, NSSM_LOCALSYSTEM_ACCOUNT)) {
\r
1061 HeapFree(GetProcessHeap(), 0, qsc);
\r
1062 print_message(stderr, NSSM_MESSAGE_INTERACTIVE_NOT_LOCALSYSTEM, value->string, service_name, NSSM_LOCALSYSTEM_ACCOUNT);
\r
1066 HeapFree(GetProcessHeap(), 0, qsc);
\r
1069 if (! ChangeServiceConfig(service_handle, type, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) {
\r
1070 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
1077 int native_get_type(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
1078 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
1079 if (! service_handle) return -1;
\r
1081 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
1082 if (! qsc) return -1;
\r
1084 value->numeric = qsc->dwServiceType;
\r
1085 HeapFree(GetProcessHeap(), 0, qsc);
\r
1087 const TCHAR *string;
\r
1088 switch (value->numeric) {
\r
1089 case SERVICE_KERNEL_DRIVER: string = NSSM_KERNEL_DRIVER; break;
\r
1090 case SERVICE_FILE_SYSTEM_DRIVER: string = NSSM_FILE_SYSTEM_DRIVER; break;
\r
1091 case SERVICE_WIN32_OWN_PROCESS: string = NSSM_WIN32_OWN_PROCESS; break;
\r
1092 case SERVICE_WIN32_SHARE_PROCESS: string = NSSM_WIN32_SHARE_PROCESS; break;
\r
1093 case SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS: string = NSSM_INTERACTIVE_PROCESS; break;
\r
1094 case SERVICE_WIN32_SHARE_PROCESS|SERVICE_INTERACTIVE_PROCESS: string = NSSM_SHARE_INTERACTIVE_PROCESS; break;
\r
1095 default: string = NSSM_UNKNOWN;
\r
1098 return value_from_string(name, value, string);
\r
1101 int set_setting(const TCHAR *service_name, HKEY key, settings_t *setting, value_t *value, const TCHAR *additional) {
\r
1102 if (! key) return -1;
\r
1105 if (setting->set) ret = setting->set(service_name, (void *) key, setting->name, setting->default_value, value, additional);
\r
1108 if (! ret) print_message(stdout, NSSM_MESSAGE_RESET_SETTING, setting->name, service_name);
\r
1109 else if (ret > 0) print_message(stdout, NSSM_MESSAGE_SET_SETTING, setting->name, service_name);
\r
1110 else print_message(stderr, NSSM_MESSAGE_SET_SETTING_FAILED, setting->name, service_name);
\r
1115 int set_setting(const TCHAR *service_name, SC_HANDLE service_handle, settings_t *setting, value_t *value, const TCHAR *additional) {
\r
1116 if (! service_handle) return -1;
\r
1119 if (setting->set) ret = setting->set(service_name, service_handle, setting->name, setting->default_value, value, additional);
\r
1122 if (! ret) print_message(stdout, NSSM_MESSAGE_RESET_SETTING, setting->name, service_name);
\r
1123 else if (ret > 0) print_message(stdout, NSSM_MESSAGE_SET_SETTING, setting->name, service_name);
\r
1124 else print_message(stderr, NSSM_MESSAGE_SET_SETTING_FAILED, setting->name, service_name);
\r
1130 Returns: 1 if the value was retrieved.
\r
1131 0 if the default value was retrieved.
\r
1134 int get_setting(const TCHAR *service_name, HKEY key, settings_t *setting, value_t *value, const TCHAR *additional) {
\r
1135 if (! key) return -1;
\r
1138 switch (setting->type) {
\r
1139 case REG_EXPAND_SZ:
\r
1140 case REG_MULTI_SZ:
\r
1142 value->string = (TCHAR *) setting->default_value;
\r
1143 if (setting->get) ret = setting->get(service_name, (void *) key, setting->name, setting->default_value, value, additional);
\r
1148 value->numeric = PtrToUlong(setting->default_value);
\r
1149 if (setting->get) ret = setting->get(service_name, (void *) key, setting->name, setting->default_value, value, additional);
\r
1158 if (ret < 0) print_message(stderr, NSSM_MESSAGE_GET_SETTING_FAILED, setting->name, service_name);
\r
1163 int get_setting(const TCHAR *service_name, SC_HANDLE service_handle, settings_t *setting, value_t *value, const TCHAR *additional) {
\r
1164 if (! service_handle) return -1;
\r
1165 return setting->get(service_name, service_handle, setting->name, 0, value, additional);
\r
1168 settings_t settings[] = {
\r
1169 { NSSM_REG_EXE, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },
\r
1170 { NSSM_REG_FLAGS, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },
\r
1171 { NSSM_REG_DIR, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },
\r
1172 { NSSM_REG_EXIT, REG_SZ, (void *) exit_action_strings[NSSM_EXIT_RESTART], false, ADDITIONAL_MANDATORY, setting_set_exit_action, setting_get_exit_action },
\r
1173 { NSSM_REG_HOOK, REG_SZ, (void *) _T(""), false, ADDITIONAL_MANDATORY, setting_set_hook, setting_get_hook },
\r
1174 { NSSM_REG_AFFINITY, REG_SZ, 0, false, 0, setting_set_affinity, setting_get_affinity },
\r
1175 { NSSM_REG_ENV, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment },
\r
1176 { NSSM_REG_ENV_EXTRA, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment },
\r
1177 { NSSM_REG_NO_CONSOLE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1178 { NSSM_REG_PRIORITY, REG_SZ, (void *) priority_strings[NSSM_NORMAL_PRIORITY], false, 0, setting_set_priority, setting_get_priority },
\r
1179 { NSSM_REG_RESTART_DELAY, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1180 { NSSM_REG_STDIN, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },
\r
1181 { NSSM_REG_STDIN NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDIN_SHARING, false, 0, setting_set_number, setting_get_number },
\r
1182 { NSSM_REG_STDIN NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDIN_DISPOSITION, false, 0, setting_set_number, setting_get_number },
\r
1183 { NSSM_REG_STDIN NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDIN_FLAGS, false, 0, setting_set_number, setting_get_number },
\r
1184 { NSSM_REG_STDOUT, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },
\r
1185 { NSSM_REG_STDOUT NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDOUT_SHARING, false, 0, setting_set_number, setting_get_number },
\r
1186 { NSSM_REG_STDOUT NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDOUT_DISPOSITION, false, 0, setting_set_number, setting_get_number },
\r
1187 { NSSM_REG_STDOUT NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDOUT_FLAGS, false, 0, setting_set_number, setting_get_number },
\r
1188 { NSSM_REG_STDOUT NSSM_REG_STDIO_COPY_AND_TRUNCATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1189 { NSSM_REG_STDERR, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },
\r
1190 { NSSM_REG_STDERR NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDERR_SHARING, false, 0, setting_set_number, setting_get_number },
\r
1191 { NSSM_REG_STDERR NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDERR_DISPOSITION, false, 0, setting_set_number, setting_get_number },
\r
1192 { NSSM_REG_STDERR NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDERR_FLAGS, false, 0, setting_set_number, setting_get_number },
\r
1193 { NSSM_REG_STDERR NSSM_REG_STDIO_COPY_AND_TRUNCATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1194 { NSSM_REG_STOP_METHOD_SKIP, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1195 { NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_CONSOLE_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },
\r
1196 { NSSM_REG_KILL_WINDOW_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_WINDOW_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },
\r
1197 { NSSM_REG_KILL_THREADS_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_THREADS_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },
\r
1198 { NSSM_REG_KILL_PROCESS_TREE, REG_DWORD, (void *) 1, false, 0, setting_set_number, setting_get_number },
\r
1199 { NSSM_REG_THROTTLE, REG_DWORD, (void *) NSSM_RESET_THROTTLE_RESTART, false, 0, setting_set_number, setting_get_number },
\r
1200 { NSSM_REG_HOOK_SHARE_OUTPUT_HANDLES, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1201 { NSSM_REG_ROTATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1202 { NSSM_REG_ROTATE_ONLINE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1203 { NSSM_REG_ROTATE_SECONDS, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1204 { NSSM_REG_ROTATE_BYTES_LOW, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1205 { NSSM_REG_ROTATE_BYTES_HIGH, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1206 { NSSM_REG_ROTATE_DELAY, REG_DWORD, (void *) NSSM_ROTATE_DELAY, false, 0, setting_set_number, setting_get_number },
\r
1207 { NSSM_NATIVE_DEPENDONGROUP, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_dependongroup, native_get_dependongroup },
\r
1208 { NSSM_NATIVE_DEPENDONSERVICE, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_dependonservice, native_get_dependonservice },
\r
1209 { NSSM_NATIVE_DESCRIPTION, REG_SZ, _T(""), true, 0, native_set_description, native_get_description },
\r
1210 { NSSM_NATIVE_DISPLAYNAME, REG_SZ, NULL, true, 0, native_set_displayname, native_get_displayname },
\r
1211 { NSSM_NATIVE_ENVIRONMENT, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_environment, native_get_environment },
\r
1212 { NSSM_NATIVE_IMAGEPATH, REG_EXPAND_SZ, NULL, true, 0, native_set_imagepath, native_get_imagepath },
\r
1213 { NSSM_NATIVE_OBJECTNAME, REG_SZ, NSSM_LOCALSYSTEM_ACCOUNT, true, 0, native_set_objectname, native_get_objectname },
\r
1214 { NSSM_NATIVE_NAME, REG_SZ, NULL, true, 0, native_set_name, native_get_name },
\r
1215 { NSSM_NATIVE_STARTUP, REG_SZ, NULL, true, 0, native_set_startup, native_get_startup },
\r
1216 { NSSM_NATIVE_TYPE, REG_SZ, NULL, true, 0, native_set_type, native_get_type },
\r
1217 { NULL, NULL, NULL, NULL, NULL }
\r