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
200 _sntprintf_s(hook_action, HOOK_NAME_LENGTH, _TRUNCATE, _T("%s"), ++s);
\r
201 return valid_hook_name(hook_event, hook_action, false);
\r
205 print_message(stderr, NSSM_MESSAGE_INVALID_HOOK_NAME, hook_name);
\r
209 static int setting_set_hook(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
210 TCHAR hook_event[HOOK_NAME_LENGTH];
\r
211 TCHAR hook_action[HOOK_NAME_LENGTH];
\r
212 if (! split_hook_name(additional, hook_event, hook_action)) return -1;
\r
215 if (value && value->string) cmd = value->string;
\r
218 if (set_hook(service_name, hook_event, hook_action, cmd)) return -1;
\r
219 if (! _tcslen(cmd)) return 0;
\r
223 static int setting_get_hook(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
224 TCHAR hook_event[HOOK_NAME_LENGTH];
\r
225 TCHAR hook_action[HOOK_NAME_LENGTH];
\r
226 if (! split_hook_name(additional, hook_event, hook_action)) return -1;
\r
228 TCHAR cmd[CMD_LENGTH];
\r
229 if (get_hook(service_name, hook_event, hook_action, cmd, sizeof(cmd))) return -1;
\r
231 value_from_string(name, value, cmd);
\r
233 if (! _tcslen(cmd)) return 0;
\r
237 static int setting_set_affinity(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
238 HKEY key = (HKEY) param;
\r
239 if (! key) return -1;
\r
243 __int64 system_affinity = 0LL;
\r
245 if (value && value->string) {
\r
246 DWORD_PTR affinity;
\r
247 if (! GetProcessAffinityMask(GetCurrentProcess(), &affinity, (DWORD_PTR *) &system_affinity)) system_affinity = ~0;
\r
249 if (is_default(value->string) || str_equiv(value->string, NSSM_AFFINITY_ALL)) mask = 0LL;
\r
250 else if (affinity_string_to_mask(value->string, &mask)) {
\r
251 print_message(stderr, NSSM_MESSAGE_BOGUS_AFFINITY_MASK, value->string, num_cpus() - 1);
\r
258 error = RegDeleteValue(key, name);
\r
259 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
\r
260 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
\r
264 /* Canonicalise. */
\r
266 if (affinity_mask_to_string(mask, &canon)) canon = value->string;
\r
268 __int64 effective_affinity = mask & system_affinity;
\r
269 if (effective_affinity != mask) {
\r
270 /* Requested CPUs did not intersect with available CPUs? */
\r
271 if (! effective_affinity) mask = effective_affinity = system_affinity;
\r
274 if (! affinity_mask_to_string(system_affinity, &system)) {
\r
275 TCHAR *effective = 0;
\r
276 if (! affinity_mask_to_string(effective_affinity, &effective)) {
\r
277 print_message(stderr, NSSM_MESSAGE_EFFECTIVE_AFFINITY_MASK, value->string, system, effective);
\r
278 HeapFree(GetProcessHeap(), 0, effective);
\r
280 HeapFree(GetProcessHeap(), 0, system);
\r
284 if (RegSetValueEx(key, name, 0, REG_SZ, (const unsigned char *) canon, (unsigned long) (_tcslen(canon) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
285 if (canon != value->string) HeapFree(GetProcessHeap(), 0, canon);
\r
286 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, name, error_string(GetLastError()), 0);
\r
290 if (canon != value->string) HeapFree(GetProcessHeap(), 0, canon);
\r
294 static int setting_get_affinity(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
295 HKEY key = (HKEY) param;
\r
296 if (! key) return -1;
\r
298 unsigned long type;
\r
300 unsigned long buflen = 0;
\r
302 int ret = RegQueryValueEx(key, name, 0, &type, 0, &buflen);
\r
303 if (ret == ERROR_FILE_NOT_FOUND) {
\r
304 if (value_from_string(name, value, NSSM_AFFINITY_ALL) == 1) return 0;
\r
307 if (ret != ERROR_SUCCESS) return -1;
\r
309 if (type != REG_SZ) return -1;
\r
311 buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, buflen);
\r
313 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("affinity"), _T("setting_get_affinity"));
\r
317 if (get_string(key, (TCHAR *) name, buffer, buflen, false, false, true)) {
\r
318 HeapFree(GetProcessHeap(), 0, buffer);
\r
323 if (affinity_string_to_mask(buffer, &affinity)) {
\r
324 print_message(stderr, NSSM_MESSAGE_BOGUS_AFFINITY_MASK, buffer, num_cpus() - 1);
\r
325 HeapFree(GetProcessHeap(), 0, buffer);
\r
329 HeapFree(GetProcessHeap(), 0, buffer);
\r
331 /* Canonicalise. */
\r
332 if (affinity_mask_to_string(affinity, &buffer)) {
\r
333 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
\r
337 ret = value_from_string(name, value, buffer);
\r
338 HeapFree(GetProcessHeap(), 0, buffer);
\r
342 static int setting_set_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
343 HKEY key = (HKEY) param;
\r
344 if (! param) return -1;
\r
347 TCHAR *unformatted = 0;
\r
348 unsigned long envlen;
\r
349 unsigned long newlen = 0;
\r
351 if (value && value->string && value->string[0]) {
\r
352 string = value->string;
\r
353 switch (string[0]) {
\r
354 case _T('+'): op = 1; break;
\r
355 case _T('-'): op = -1; break;
\r
356 case _T(':'): string++; break;
\r
363 if (get_environment((TCHAR *) service_name, key, (TCHAR *) name, &env, &envlen)) return -1;
\r
366 if (op > 0) ret = append_to_environment_block(env, envlen, string, &unformatted, &newlen);
\r
367 else ret = remove_from_environment_block(env, envlen, string, &unformatted, &newlen);
\r
368 if (envlen) HeapFree(GetProcessHeap(), 0, env);
\r
369 if (ret) return -1;
\r
371 string = unformatted;
\r
375 No existing environment.
\r
376 We can't remove from an empty environment so just treat an add
\r
377 operation as setting a new string.
\r
379 if (op < 0) return 0;
\r
384 if (! string || ! string[0]) {
\r
385 long error = RegDeleteValue(key, name);
\r
386 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
\r
387 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
\r
392 if (unformat_double_null(string, (unsigned long) _tcslen(string), &unformatted, &newlen)) return -1;
\r
395 if (test_environment(unformatted)) {
\r
396 HeapFree(GetProcessHeap(), 0, unformatted);
\r
397 print_message(stderr, NSSM_GUI_INVALID_ENVIRONMENT);
\r
401 if (RegSetValueEx(key, name, 0, REG_MULTI_SZ, (const unsigned char *) unformatted, (unsigned long) newlen * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
402 if (newlen) HeapFree(GetProcessHeap(), 0, unformatted);
\r
403 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV, error_string(GetLastError()), 0);
\r
407 if (newlen) HeapFree(GetProcessHeap(), 0, unformatted);
\r
411 static int setting_get_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
412 HKEY key = (HKEY) param;
\r
413 if (! param) return -1;
\r
416 unsigned long envlen;
\r
417 if (get_environment((TCHAR *) service_name, key, (TCHAR *) name, &env, &envlen)) return -1;
\r
418 if (! envlen) return 0;
\r
421 unsigned long newlen;
\r
422 if (format_double_null(env, envlen, &formatted, &newlen)) return -1;
\r
426 /* Find named environment variable. */
\r
428 size_t len = _tcslen(additional);
\r
429 for (s = env; *s; s++) {
\r
430 /* Look for <additional>=<string> NULL NULL */
\r
431 if (! _tcsnicmp(s, additional, len) && s[len] == _T('=')) {
\r
434 ret = value_from_string(name, value, s);
\r
435 HeapFree(GetProcessHeap(), 0, env);
\r
439 /* Skip this string. */
\r
442 HeapFree(GetProcessHeap(), 0, env);
\r
446 HeapFree(GetProcessHeap(), 0, env);
\r
448 ret = value_from_string(name, value, formatted);
\r
449 if (newlen) HeapFree(GetProcessHeap(), 0, formatted);
\r
453 static int setting_set_priority(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
454 HKEY key = (HKEY) param;
\r
455 if (! param) return -1;
\r
457 TCHAR *priority_string;
\r
461 if (value && value->string) priority_string = value->string;
\r
462 else if (default_value) priority_string = (TCHAR *) default_value;
\r
464 error = RegDeleteValue(key, name);
\r
465 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
\r
466 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
\r
470 for (i = 0; priority_strings[i]; i++) {
\r
471 if (! str_equiv(priority_strings[i], priority_string)) continue;
\r
473 if (default_value && str_equiv(priority_string, (TCHAR *) default_value)) {
\r
474 error = RegDeleteValue(key, name);
\r
475 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
\r
476 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
\r
480 if (set_number(key, (TCHAR *) name, priority_index_to_constant(i))) return -1;
\r
484 print_message(stderr, NSSM_MESSAGE_INVALID_PRIORITY, priority_string);
\r
485 for (i = 0; priority_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), priority_strings[i]);
\r
490 static int setting_get_priority(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
491 HKEY key = (HKEY) param;
\r
492 if (! param) return -1;
\r
494 unsigned long constant;
\r
495 switch (get_number(key, (TCHAR *) name, &constant, false)) {
\r
497 if (value_from_string(name, value, (const TCHAR *) default_value) == -1) return -1;
\r
499 case -1: return -1;
\r
502 return value_from_string(name, value, priority_strings[priority_constant_to_index(constant)]);
\r
505 /* Functions to manage native service settings. */
\r
506 static int native_set_dependon(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR **dependencies, unsigned long *dependencieslen, value_t *value, int type) {
\r
507 *dependencieslen = 0;
\r
508 if (! value || ! value->string || ! value->string[0]) return 0;
\r
510 TCHAR *string = value->string;
\r
511 unsigned long buflen;
\r
513 switch (string[0]) {
\r
514 case _T('+'): op = 1; break;
\r
515 case _T('-'): op = -1; break;
\r
516 case _T(':'): string++; break;
\r
522 if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, type)) return -1;
\r
525 if (op > 0) ret = append_to_dependencies(buffer, buflen, string, dependencies, dependencieslen, type);
\r
526 else ret = remove_from_dependencies(buffer, buflen, string, dependencies, dependencieslen, type);
\r
527 if (buflen) HeapFree(GetProcessHeap(), 0, buffer);
\r
533 We can't remove from an empty list so just treat an add
\r
534 operation as setting a new string.
\r
536 if (op < 0) return 0;
\r
542 TCHAR *unformatted = 0;
\r
543 unsigned long newlen;
\r
544 if (unformat_double_null(string, (unsigned long) _tcslen(string), &unformatted, &newlen)) return -1;
\r
546 if (type == DEPENDENCY_GROUPS) {
\r
547 /* Prepend group identifier. */
\r
548 unsigned long missing = 0;
\r
549 TCHAR *canon = unformatted;
\r
550 size_t canonlen = 0;
\r
552 for (s = unformatted; *s; s++) {
\r
553 if (*s != SC_GROUP_IDENTIFIER) missing++;
\r
554 size_t len = _tcslen(s);
\r
555 canonlen += len + 1;
\r
560 /* Missing identifiers plus double NULL terminator. */
\r
561 canonlen += missing + 1;
\r
563 canon = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, canonlen * sizeof(TCHAR));
\r
565 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canon"), _T("native_set_dependon"));
\r
566 if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);
\r
571 for (s = unformatted; *s; s++) {
\r
572 if (*s != SC_GROUP_IDENTIFIER) canon[i++] = SC_GROUP_IDENTIFIER;
\r
573 size_t len = _tcslen(s);
\r
574 memmove(canon + i, s, (len + 1) * sizeof(TCHAR));
\r
579 HeapFree(GetProcessHeap(), 0, unformatted);
\r
580 unformatted = canon;
\r
581 newlen = (unsigned long) canonlen;
\r
585 *dependencies = unformatted;
\r
586 *dependencieslen = newlen;
\r
592 static int native_set_dependongroup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
593 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
594 if (! service_handle) return -1;
\r
597 Get existing service dependencies because we must set both types together.
\r
599 TCHAR *services_buffer;
\r
600 unsigned long services_buflen;
\r
601 if (get_service_dependencies(service_name, service_handle, &services_buffer, &services_buflen, DEPENDENCY_SERVICES)) return -1;
\r
603 if (! value || ! value->string || ! value->string[0]) {
\r
604 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, services_buffer, 0, 0, 0)) {
\r
605 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
606 if (services_buffer) HeapFree(GetProcessHeap(), 0, services_buffer);
\r
610 if (services_buffer) HeapFree(GetProcessHeap(), 0, services_buffer);
\r
614 /* Update the group list. */
\r
615 TCHAR *groups_buffer;
\r
616 unsigned long groups_buflen;
\r
617 if (native_set_dependon(service_name, service_handle, &groups_buffer, &groups_buflen, value, DEPENDENCY_GROUPS)) return -1;
\r
619 TCHAR *dependencies;
\r
620 if (services_buflen > 2) {
\r
621 dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (groups_buflen + services_buflen) * sizeof(TCHAR));
\r
622 if (! dependencies) {
\r
623 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("dependencies"), _T("native_set_dependongroup"));
\r
624 if (groups_buffer) HeapFree(GetProcessHeap(), 0, groups_buffer);
\r
625 if (services_buffer) HeapFree(GetProcessHeap(), 0, services_buffer);
\r
629 memmove(dependencies, services_buffer, services_buflen * sizeof(TCHAR));
\r
630 memmove(dependencies + services_buflen - 1, groups_buffer, groups_buflen * sizeof(TCHAR));
\r
632 else dependencies = groups_buffer;
\r
635 if (set_service_dependencies(service_name, service_handle, dependencies)) ret = -1;
\r
636 if (dependencies != groups_buffer) HeapFree(GetProcessHeap(), 0, dependencies);
\r
637 if (groups_buffer) HeapFree(GetProcessHeap(), 0, groups_buffer);
\r
638 if (services_buffer) HeapFree(GetProcessHeap(), 0, services_buffer);
\r
643 static int native_get_dependongroup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
644 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
645 if (! service_handle) return -1;
\r
648 unsigned long buflen;
\r
649 if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_GROUPS)) return -1;
\r
654 unsigned long newlen;
\r
655 if (format_double_null(buffer, buflen, &formatted, &newlen)) {
\r
656 HeapFree(GetProcessHeap(), 0, buffer);
\r
660 ret = value_from_string(name, value, formatted);
\r
661 HeapFree(GetProcessHeap(), 0, formatted);
\r
662 HeapFree(GetProcessHeap(), 0, buffer);
\r
672 static int native_set_dependonservice(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
673 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
674 if (! service_handle) return -1;
\r
677 Get existing group dependencies because we must set both types together.
\r
679 TCHAR *groups_buffer;
\r
680 unsigned long groups_buflen;
\r
681 if (get_service_dependencies(service_name, service_handle, &groups_buffer, &groups_buflen, DEPENDENCY_GROUPS)) return -1;
\r
683 if (! value || ! value->string || ! value->string[0]) {
\r
684 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, groups_buffer, 0, 0, 0)) {
\r
685 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
686 if (groups_buffer) HeapFree(GetProcessHeap(), 0, groups_buffer);
\r
690 if (groups_buffer) HeapFree(GetProcessHeap(), 0, groups_buffer);
\r
694 /* Update the service list. */
\r
695 TCHAR *services_buffer;
\r
696 unsigned long services_buflen;
\r
697 if (native_set_dependon(service_name, service_handle, &services_buffer, &services_buflen, value, DEPENDENCY_SERVICES)) return -1;
\r
699 TCHAR *dependencies;
\r
700 if (groups_buflen > 2) {
\r
701 dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (services_buflen + groups_buflen) * sizeof(TCHAR));
\r
702 if (! dependencies) {
\r
703 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("dependencies"), _T("native_set_dependonservice"));
\r
704 if (groups_buffer) HeapFree(GetProcessHeap(), 0, groups_buffer);
\r
705 if (services_buffer) HeapFree(GetProcessHeap(), 0, services_buffer);
\r
709 memmove(dependencies, services_buffer, services_buflen * sizeof(TCHAR));
\r
710 memmove(dependencies + services_buflen - 1, groups_buffer, groups_buflen * sizeof(TCHAR));
\r
712 else dependencies = services_buffer;
\r
715 if (set_service_dependencies(service_name, service_handle, dependencies)) ret = -1;
\r
716 if (dependencies != services_buffer) HeapFree(GetProcessHeap(), 0, dependencies);
\r
717 if (groups_buffer) HeapFree(GetProcessHeap(), 0, groups_buffer);
\r
718 if (services_buffer) HeapFree(GetProcessHeap(), 0, services_buffer);
\r
723 static int native_get_dependonservice(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
724 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
725 if (! service_handle) return -1;
\r
728 unsigned long buflen;
\r
729 if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_SERVICES)) return -1;
\r
734 unsigned long newlen;
\r
735 if (format_double_null(buffer, buflen, &formatted, &newlen)) {
\r
736 HeapFree(GetProcessHeap(), 0, buffer);
\r
740 ret = value_from_string(name, value, formatted);
\r
741 HeapFree(GetProcessHeap(), 0, formatted);
\r
742 HeapFree(GetProcessHeap(), 0, buffer);
\r
752 int native_set_description(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
753 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
754 if (! service_handle) return -1;
\r
756 TCHAR *description = 0;
\r
757 if (value) description = value->string;
\r
758 if (set_service_description(service_name, service_handle, description)) return -1;
\r
760 if (description && description[0]) return 1;
\r
765 int native_get_description(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
766 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
767 if (! service_handle) return -1;
\r
769 TCHAR buffer[VALUE_LENGTH];
\r
770 if (get_service_description(service_name, service_handle, _countof(buffer), buffer)) return -1;
\r
772 if (buffer[0]) return value_from_string(name, value, buffer);
\r
778 int native_set_displayname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
779 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
780 if (! service_handle) return -1;
\r
782 TCHAR *displayname = 0;
\r
783 if (value && value->string) displayname = value->string;
\r
784 else displayname = (TCHAR *) service_name;
\r
786 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, displayname)) {
\r
787 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
792 If the display name and service name differ only in case,
\r
793 ChangeServiceConfig() will return success but the display name will be
\r
794 set to the service name, NOT the value passed to the function.
\r
795 This appears to be a quirk of Windows rather than a bug here.
\r
797 if (displayname != service_name && ! str_equiv(displayname, service_name)) return 1;
\r
802 int native_get_displayname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
803 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
804 if (! service_handle) return -1;
\r
806 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
807 if (! qsc) return -1;
\r
809 int ret = value_from_string(name, value, qsc->lpDisplayName);
\r
810 HeapFree(GetProcessHeap(), 0, qsc);
\r
815 int native_set_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
816 HKEY key = open_service_registry(service_name, KEY_SET_VALUE, true);
\r
817 if (! key) return -1;
\r
819 int ret = setting_set_environment(service_name, (void *) key, name, default_value, value, additional);
\r
824 int native_get_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
825 HKEY key = open_service_registry(service_name, KEY_READ, true);
\r
826 if (! key) return -1;
\r
828 ZeroMemory(value, sizeof(value_t));
\r
829 int ret = setting_get_environment(service_name, (void *) key, name, default_value, value, additional);
\r
834 int native_set_imagepath(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
835 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
836 if (! service_handle) return -1;
\r
838 /* It makes no sense to try to reset the image path. */
\r
839 if (! value || ! value->string) {
\r
840 print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);
\r
844 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, value->string, 0, 0, 0, 0, 0, 0)) {
\r
845 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
852 int native_get_imagepath(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
853 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
854 if (! service_handle) return -1;
\r
856 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
857 if (! qsc) return -1;
\r
859 int ret = value_from_string(name, value, qsc->lpBinaryPathName);
\r
860 HeapFree(GetProcessHeap(), 0, qsc);
\r
865 int native_set_name(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
866 print_message(stderr, NSSM_MESSAGE_CANNOT_RENAME_SERVICE);
\r
870 int native_get_name(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
871 return value_from_string(name, value, service_name);
\r
874 int native_set_objectname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
875 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
876 if (! service_handle) return -1;
\r
879 Logical syntax is: nssm set <service> ObjectName <username> <password>
\r
880 That means the username is actually passed in the additional parameter.
\r
882 bool localsystem = false;
\r
883 TCHAR *username = NSSM_LOCALSYSTEM_ACCOUNT;
\r
884 TCHAR *password = 0;
\r
886 username = (TCHAR *) additional;
\r
887 if (value && value->string) password = value->string;
\r
889 else if (value && value->string) username = value->string;
\r
891 const TCHAR *well_known = well_known_username(username);
\r
892 size_t passwordsize = 0;
\r
894 if (str_equiv(well_known, NSSM_LOCALSYSTEM_ACCOUNT)) localsystem = true;
\r
895 username = (TCHAR *) well_known;
\r
898 else if (! password) {
\r
899 /* We need a password if the account requires it. */
\r
900 print_message(stderr, NSSM_MESSAGE_MISSING_PASSWORD, name);
\r
903 else passwordsize = _tcslen(password) * sizeof(TCHAR);
\r
906 ChangeServiceConfig() will fail to set the username if the service is set
\r
907 to interact with the desktop.
\r
909 unsigned long type = SERVICE_NO_CHANGE;
\r
910 if (! localsystem) {
\r
911 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
913 if (passwordsize) SecureZeroMemory(password, passwordsize);
\r
917 type = qsc->dwServiceType & ~SERVICE_INTERACTIVE_PROCESS;
\r
918 HeapFree(GetProcessHeap(), 0, qsc);
\r
921 if (! well_known) {
\r
922 if (grant_logon_as_service(username)) {
\r
923 if (passwordsize) SecureZeroMemory(password, passwordsize);
\r
924 print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);
\r
929 if (! ChangeServiceConfig(service_handle, type, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, username, password, 0)) {
\r
930 if (passwordsize) SecureZeroMemory(password, passwordsize);
\r
931 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
935 if (passwordsize) SecureZeroMemory(password, passwordsize);
\r
937 if (localsystem) return 0;
\r
942 int native_get_objectname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
943 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
944 if (! service_handle) return -1;
\r
946 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
947 if (! qsc) return -1;
\r
949 int ret = value_from_string(name, value, qsc->lpServiceStartName);
\r
950 HeapFree(GetProcessHeap(), 0, qsc);
\r
955 int native_set_startup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
956 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
957 if (! service_handle) return -1;
\r
959 /* It makes no sense to try to reset the startup type. */
\r
960 if (! value || ! value->string) {
\r
961 print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);
\r
965 /* Map NSSM_STARTUP_* constant to Windows SERVICE_*_START constant. */
\r
966 int service_startup = -1;
\r
968 for (i = 0; startup_strings[i]; i++) {
\r
969 if (str_equiv(value->string, startup_strings[i])) {
\r
970 service_startup = i;
\r
975 if (service_startup < 0) {
\r
976 print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE_STARTUP, value->string);
\r
977 for (i = 0; startup_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), startup_strings[i]);
\r
981 unsigned long startup;
\r
982 switch (service_startup) {
\r
983 case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;
\r
984 case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;
\r
985 default: startup = SERVICE_AUTO_START;
\r
988 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, startup, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) {
\r
989 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
993 SERVICE_DELAYED_AUTO_START_INFO delayed;
\r
994 ZeroMemory(&delayed, sizeof(delayed));
\r
995 if (service_startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;
\r
996 else delayed.fDelayedAutostart = 0;
\r
997 if (! ChangeServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {
\r
998 unsigned long error = GetLastError();
\r
999 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
1000 if (error != ERROR_INVALID_LEVEL) {
\r
1001 log_event(EVENTLOG_ERROR_TYPE, NSSM_MESSAGE_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service_name, error_string(error), 0);
\r
1008 int native_get_startup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
1009 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
1010 if (! service_handle) return -1;
\r
1012 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
1013 if (! qsc) return -1;
\r
1015 unsigned long startup;
\r
1016 int ret = get_service_startup(service_name, service_handle, qsc, &startup);
\r
1017 HeapFree(GetProcessHeap(), 0, qsc);
\r
1019 if (ret) return -1;
\r
1022 for (i = 0; startup_strings[i]; i++);
\r
1023 if (startup >= i) return -1;
\r
1025 return value_from_string(name, value, startup_strings[startup]);
\r
1028 int native_set_type(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
1029 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
1030 if (! service_handle) return -1;
\r
1032 /* It makes no sense to try to reset the service type. */
\r
1033 if (! value || ! value->string) {
\r
1034 print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);
\r
1039 We can only manage services of type SERVICE_WIN32_OWN_PROCESS
\r
1040 and SERVICE_INTERACTIVE_PROCESS.
\r
1042 unsigned long type = SERVICE_WIN32_OWN_PROCESS;
\r
1043 if (str_equiv(value->string, NSSM_INTERACTIVE_PROCESS)) type |= SERVICE_INTERACTIVE_PROCESS;
\r
1044 else if (! str_equiv(value->string, NSSM_WIN32_OWN_PROCESS)) {
\r
1045 print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE_TYPE, value->string);
\r
1046 _ftprintf(stderr, _T("%s\n"), NSSM_WIN32_OWN_PROCESS);
\r
1047 _ftprintf(stderr, _T("%s\n"), NSSM_INTERACTIVE_PROCESS);
\r
1052 ChangeServiceConfig() will fail if the service runs under an account
\r
1053 other than LOCALSYSTEM and we try to make it interactive.
\r
1055 if (type & SERVICE_INTERACTIVE_PROCESS) {
\r
1056 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
1057 if (! qsc) return -1;
\r
1059 if (! str_equiv(qsc->lpServiceStartName, NSSM_LOCALSYSTEM_ACCOUNT)) {
\r
1060 HeapFree(GetProcessHeap(), 0, qsc);
\r
1061 print_message(stderr, NSSM_MESSAGE_INTERACTIVE_NOT_LOCALSYSTEM, value->string, service_name, NSSM_LOCALSYSTEM_ACCOUNT);
\r
1065 HeapFree(GetProcessHeap(), 0, qsc);
\r
1068 if (! ChangeServiceConfig(service_handle, type, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) {
\r
1069 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
1076 int native_get_type(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
1077 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
1078 if (! service_handle) return -1;
\r
1080 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
1081 if (! qsc) return -1;
\r
1083 value->numeric = qsc->dwServiceType;
\r
1084 HeapFree(GetProcessHeap(), 0, qsc);
\r
1086 const TCHAR *string;
\r
1087 switch (value->numeric) {
\r
1088 case SERVICE_KERNEL_DRIVER: string = NSSM_KERNEL_DRIVER; break;
\r
1089 case SERVICE_FILE_SYSTEM_DRIVER: string = NSSM_FILE_SYSTEM_DRIVER; break;
\r
1090 case SERVICE_WIN32_OWN_PROCESS: string = NSSM_WIN32_OWN_PROCESS; break;
\r
1091 case SERVICE_WIN32_SHARE_PROCESS: string = NSSM_WIN32_SHARE_PROCESS; break;
\r
1092 case SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS: string = NSSM_INTERACTIVE_PROCESS; break;
\r
1093 case SERVICE_WIN32_SHARE_PROCESS|SERVICE_INTERACTIVE_PROCESS: string = NSSM_SHARE_INTERACTIVE_PROCESS; break;
\r
1094 default: string = NSSM_UNKNOWN;
\r
1097 return value_from_string(name, value, string);
\r
1100 int set_setting(const TCHAR *service_name, HKEY key, settings_t *setting, value_t *value, const TCHAR *additional) {
\r
1101 if (! key) return -1;
\r
1104 if (setting->set) ret = setting->set(service_name, (void *) key, setting->name, setting->default_value, value, additional);
\r
1107 if (! ret) print_message(stdout, NSSM_MESSAGE_RESET_SETTING, setting->name, service_name);
\r
1108 else if (ret > 0) print_message(stdout, NSSM_MESSAGE_SET_SETTING, setting->name, service_name);
\r
1109 else print_message(stderr, NSSM_MESSAGE_SET_SETTING_FAILED, setting->name, service_name);
\r
1114 int set_setting(const TCHAR *service_name, SC_HANDLE service_handle, settings_t *setting, value_t *value, const TCHAR *additional) {
\r
1115 if (! service_handle) return -1;
\r
1118 if (setting->set) ret = setting->set(service_name, service_handle, setting->name, setting->default_value, value, additional);
\r
1121 if (! ret) print_message(stdout, NSSM_MESSAGE_RESET_SETTING, setting->name, service_name);
\r
1122 else if (ret > 0) print_message(stdout, NSSM_MESSAGE_SET_SETTING, setting->name, service_name);
\r
1123 else print_message(stderr, NSSM_MESSAGE_SET_SETTING_FAILED, setting->name, service_name);
\r
1129 Returns: 1 if the value was retrieved.
\r
1130 0 if the default value was retrieved.
\r
1133 int get_setting(const TCHAR *service_name, HKEY key, settings_t *setting, value_t *value, const TCHAR *additional) {
\r
1134 if (! key) return -1;
\r
1137 switch (setting->type) {
\r
1138 case REG_EXPAND_SZ:
\r
1139 case REG_MULTI_SZ:
\r
1141 value->string = (TCHAR *) setting->default_value;
\r
1142 if (setting->get) ret = setting->get(service_name, (void *) key, setting->name, setting->default_value, value, additional);
\r
1147 value->numeric = PtrToUlong(setting->default_value);
\r
1148 if (setting->get) ret = setting->get(service_name, (void *) key, setting->name, setting->default_value, value, additional);
\r
1157 if (ret < 0) print_message(stderr, NSSM_MESSAGE_GET_SETTING_FAILED, setting->name, service_name);
\r
1162 int get_setting(const TCHAR *service_name, SC_HANDLE service_handle, settings_t *setting, value_t *value, const TCHAR *additional) {
\r
1163 if (! service_handle) return -1;
\r
1164 return setting->get(service_name, service_handle, setting->name, 0, value, additional);
\r
1167 settings_t settings[] = {
\r
1168 { NSSM_REG_EXE, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },
\r
1169 { NSSM_REG_FLAGS, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },
\r
1170 { NSSM_REG_DIR, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },
\r
1171 { NSSM_REG_EXIT, REG_SZ, (void *) exit_action_strings[NSSM_EXIT_RESTART], false, ADDITIONAL_MANDATORY, setting_set_exit_action, setting_get_exit_action },
\r
1172 { NSSM_REG_HOOK, REG_SZ, (void *) _T(""), false, ADDITIONAL_MANDATORY, setting_set_hook, setting_get_hook },
\r
1173 { NSSM_REG_AFFINITY, REG_SZ, 0, false, 0, setting_set_affinity, setting_get_affinity },
\r
1174 { NSSM_REG_ENV, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment },
\r
1175 { NSSM_REG_ENV_EXTRA, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment },
\r
1176 { NSSM_REG_NO_CONSOLE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1177 { NSSM_REG_PRIORITY, REG_SZ, (void *) priority_strings[NSSM_NORMAL_PRIORITY], false, 0, setting_set_priority, setting_get_priority },
\r
1178 { NSSM_REG_RESTART_DELAY, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1179 { NSSM_REG_STDIN, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },
\r
1180 { NSSM_REG_STDIN NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDIN_SHARING, false, 0, setting_set_number, setting_get_number },
\r
1181 { NSSM_REG_STDIN NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDIN_DISPOSITION, false, 0, setting_set_number, setting_get_number },
\r
1182 { NSSM_REG_STDIN NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDIN_FLAGS, false, 0, setting_set_number, setting_get_number },
\r
1183 { NSSM_REG_STDOUT, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },
\r
1184 { NSSM_REG_STDOUT NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDOUT_SHARING, false, 0, setting_set_number, setting_get_number },
\r
1185 { NSSM_REG_STDOUT NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDOUT_DISPOSITION, false, 0, setting_set_number, setting_get_number },
\r
1186 { NSSM_REG_STDOUT NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDOUT_FLAGS, false, 0, setting_set_number, setting_get_number },
\r
1187 { NSSM_REG_STDOUT NSSM_REG_STDIO_COPY_AND_TRUNCATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1188 { NSSM_REG_STDERR, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },
\r
1189 { NSSM_REG_STDERR NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDERR_SHARING, false, 0, setting_set_number, setting_get_number },
\r
1190 { NSSM_REG_STDERR NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDERR_DISPOSITION, false, 0, setting_set_number, setting_get_number },
\r
1191 { NSSM_REG_STDERR NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDERR_FLAGS, false, 0, setting_set_number, setting_get_number },
\r
1192 { NSSM_REG_STDERR NSSM_REG_STDIO_COPY_AND_TRUNCATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1193 { NSSM_REG_STOP_METHOD_SKIP, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1194 { NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_CONSOLE_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },
\r
1195 { NSSM_REG_KILL_WINDOW_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_WINDOW_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },
\r
1196 { NSSM_REG_KILL_THREADS_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_THREADS_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },
\r
1197 { NSSM_REG_KILL_PROCESS_TREE, REG_DWORD, (void *) 1, false, 0, setting_set_number, setting_get_number },
\r
1198 { NSSM_REG_THROTTLE, REG_DWORD, (void *) NSSM_RESET_THROTTLE_RESTART, false, 0, setting_set_number, setting_get_number },
\r
1199 { NSSM_REG_HOOK_SHARE_OUTPUT_HANDLES, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1200 { NSSM_REG_ROTATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1201 { NSSM_REG_ROTATE_ONLINE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1202 { NSSM_REG_ROTATE_SECONDS, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1203 { NSSM_REG_ROTATE_BYTES_LOW, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1204 { NSSM_REG_ROTATE_BYTES_HIGH, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1205 { NSSM_REG_ROTATE_DELAY, REG_DWORD, (void *) NSSM_ROTATE_DELAY, false, 0, setting_set_number, setting_get_number },
\r
1206 { NSSM_NATIVE_DEPENDONGROUP, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_dependongroup, native_get_dependongroup },
\r
1207 { NSSM_NATIVE_DEPENDONSERVICE, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_dependonservice, native_get_dependonservice },
\r
1208 { NSSM_NATIVE_DESCRIPTION, REG_SZ, _T(""), true, 0, native_set_description, native_get_description },
\r
1209 { NSSM_NATIVE_DISPLAYNAME, REG_SZ, NULL, true, 0, native_set_displayname, native_get_displayname },
\r
1210 { NSSM_NATIVE_ENVIRONMENT, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_environment, native_get_environment },
\r
1211 { NSSM_NATIVE_IMAGEPATH, REG_EXPAND_SZ, NULL, true, 0, native_set_imagepath, native_get_imagepath },
\r
1212 { NSSM_NATIVE_OBJECTNAME, REG_SZ, NSSM_LOCALSYSTEM_ACCOUNT, true, 0, native_set_objectname, native_get_objectname },
\r
1213 { NSSM_NATIVE_NAME, REG_SZ, NULL, true, 0, native_set_name, native_get_name },
\r
1214 { NSSM_NATIVE_STARTUP, REG_SZ, NULL, true, 0, native_set_startup, native_get_startup },
\r
1215 { NSSM_NATIVE_TYPE, REG_SZ, NULL, true, 0, native_set_type, native_get_type },
\r
1216 { NULL, NULL, NULL, NULL, NULL }
\r