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_dependongroup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
503 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
504 if (! service_handle) return -1;
\r
507 Get existing service dependencies because we must set both types together.
\r
510 unsigned long buflen;
\r
511 if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_SERVICES)) return -1;
\r
513 if (! value || ! value->string || ! value->string[0]) {
\r
514 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, buffer, 0, 0, 0)) {
\r
515 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
516 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
\r
520 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
\r
524 unsigned long len = (unsigned long) _tcslen(value->string) + 1;
\r
525 TCHAR *unformatted = 0;
\r
526 unsigned long newlen;
\r
527 if (unformat_double_null(value->string, len, &unformatted, &newlen)) {
\r
528 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
\r
532 /* Prepend group identifier. */
\r
533 unsigned long missing = 0;
\r
534 TCHAR *canon = unformatted;
\r
535 size_t canonlen = 0;
\r
537 for (s = unformatted; *s; s++) {
\r
538 if (*s != SC_GROUP_IDENTIFIER) missing++;
\r
539 size_t len = _tcslen(s);
\r
540 canonlen += len + 1;
\r
545 /* Missing identifiers plus double NULL terminator. */
\r
546 canonlen += missing + 1;
\r
547 newlen = (unsigned long) canonlen;
\r
549 canon = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, canonlen * sizeof(TCHAR));
\r
551 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canon"), _T("native_set_dependongroup"));
\r
552 if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);
\r
553 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
\r
558 for (s = unformatted; *s; s++) {
\r
559 if (*s != SC_GROUP_IDENTIFIER) canon[i++] = SC_GROUP_IDENTIFIER;
\r
560 size_t len = _tcslen(s);
\r
561 memmove(canon + i, s, (len + 1) * sizeof(TCHAR));
\r
567 TCHAR *dependencies;
\r
569 dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (newlen + buflen) * sizeof(TCHAR));
\r
570 if (! dependencies) {
\r
571 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("dependencies"), _T("native_set_dependongroup"));
\r
572 if (canon != unformatted) HeapFree(GetProcessHeap(), 0, canon);
\r
573 if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);
\r
574 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
\r
578 memmove(dependencies, buffer, buflen * sizeof(TCHAR));
\r
579 memmove(dependencies + buflen - 1, canon, newlen * sizeof(TCHAR));
\r
581 else dependencies = canon;
\r
584 if (set_service_dependencies(service_name, service_handle, dependencies)) ret = -1;
\r
585 if (dependencies != unformatted) HeapFree(GetProcessHeap(), 0, dependencies);
\r
586 if (canon != unformatted) HeapFree(GetProcessHeap(), 0, canon);
\r
587 if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);
\r
588 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
\r
593 static int native_get_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 unsigned long buflen;
\r
599 if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_GROUPS)) return -1;
\r
604 unsigned long newlen;
\r
605 if (format_double_null(buffer, buflen, &formatted, &newlen)) {
\r
606 HeapFree(GetProcessHeap(), 0, buffer);
\r
610 ret = value_from_string(name, value, formatted);
\r
611 HeapFree(GetProcessHeap(), 0, formatted);
\r
612 HeapFree(GetProcessHeap(), 0, buffer);
\r
622 static int native_set_dependonservice(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
623 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
624 if (! service_handle) return -1;
\r
627 Get existing group dependencies because we must set both types together.
\r
630 unsigned long buflen;
\r
631 if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_GROUPS)) return -1;
\r
633 if (! value || ! value->string || ! value->string[0]) {
\r
634 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, buffer, 0, 0, 0)) {
\r
635 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
636 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
\r
640 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
\r
644 unsigned long len = (unsigned long) _tcslen(value->string) + 1;
\r
645 TCHAR *unformatted = 0;
\r
646 unsigned long newlen;
\r
647 if (unformat_double_null(value->string, len, &unformatted, &newlen)) {
\r
648 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
\r
652 TCHAR *dependencies;
\r
654 dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (newlen + buflen) * sizeof(TCHAR));
\r
655 if (! dependencies) {
\r
656 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("dependencies"), _T("native_set_dependonservice"));
\r
657 if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);
\r
658 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
\r
662 memmove(dependencies, buffer, buflen * sizeof(TCHAR));
\r
663 memmove(dependencies + buflen - 1, unformatted, newlen * sizeof(TCHAR));
\r
665 else dependencies = unformatted;
\r
668 if (set_service_dependencies(service_name, service_handle, dependencies)) ret = -1;
\r
669 if (dependencies != unformatted) HeapFree(GetProcessHeap(), 0, dependencies);
\r
670 if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);
\r
671 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
\r
676 static int native_get_dependonservice(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
677 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
678 if (! service_handle) return -1;
\r
681 unsigned long buflen;
\r
682 if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_SERVICES)) return -1;
\r
687 unsigned long newlen;
\r
688 if (format_double_null(buffer, buflen, &formatted, &newlen)) {
\r
689 HeapFree(GetProcessHeap(), 0, buffer);
\r
693 ret = value_from_string(name, value, formatted);
\r
694 HeapFree(GetProcessHeap(), 0, formatted);
\r
695 HeapFree(GetProcessHeap(), 0, buffer);
\r
705 int native_set_description(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
706 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
707 if (! service_handle) return -1;
\r
709 TCHAR *description = 0;
\r
710 if (value) description = value->string;
\r
711 if (set_service_description(service_name, service_handle, description)) return -1;
\r
713 if (description && description[0]) return 1;
\r
718 int native_get_description(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
719 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
720 if (! service_handle) return -1;
\r
722 TCHAR buffer[VALUE_LENGTH];
\r
723 if (get_service_description(service_name, service_handle, _countof(buffer), buffer)) return -1;
\r
725 if (buffer[0]) return value_from_string(name, value, buffer);
\r
731 int native_set_displayname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
732 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
733 if (! service_handle) return -1;
\r
735 TCHAR *displayname = 0;
\r
736 if (value && value->string) displayname = value->string;
\r
737 else displayname = (TCHAR *) service_name;
\r
739 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, displayname)) {
\r
740 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
745 If the display name and service name differ only in case,
\r
746 ChangeServiceConfig() will return success but the display name will be
\r
747 set to the service name, NOT the value passed to the function.
\r
748 This appears to be a quirk of Windows rather than a bug here.
\r
750 if (displayname != service_name && ! str_equiv(displayname, service_name)) return 1;
\r
755 int native_get_displayname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
756 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
757 if (! service_handle) return -1;
\r
759 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
760 if (! qsc) return -1;
\r
762 int ret = value_from_string(name, value, qsc->lpDisplayName);
\r
763 HeapFree(GetProcessHeap(), 0, qsc);
\r
768 int native_set_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
769 HKEY key = open_service_registry(service_name, KEY_SET_VALUE, true);
\r
770 if (! key) return -1;
\r
772 int ret = setting_set_environment(service_name, (void *) key, name, default_value, value, additional);
\r
777 int native_get_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
778 HKEY key = open_service_registry(service_name, KEY_READ, true);
\r
779 if (! key) return -1;
\r
781 ZeroMemory(value, sizeof(value_t));
\r
782 int ret = setting_get_environment(service_name, (void *) key, name, default_value, value, additional);
\r
787 int native_set_imagepath(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
788 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
789 if (! service_handle) return -1;
\r
791 /* It makes no sense to try to reset the image path. */
\r
792 if (! value || ! value->string) {
\r
793 print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);
\r
797 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, value->string, 0, 0, 0, 0, 0, 0)) {
\r
798 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
805 int native_get_imagepath(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
806 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
807 if (! service_handle) return -1;
\r
809 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
810 if (! qsc) return -1;
\r
812 int ret = value_from_string(name, value, qsc->lpBinaryPathName);
\r
813 HeapFree(GetProcessHeap(), 0, qsc);
\r
818 int native_set_name(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
819 print_message(stderr, NSSM_MESSAGE_CANNOT_RENAME_SERVICE);
\r
823 int native_get_name(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
824 return value_from_string(name, value, service_name);
\r
827 int native_set_objectname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
828 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
829 if (! service_handle) return -1;
\r
832 Logical syntax is: nssm set <service> ObjectName <username> <password>
\r
833 That means the username is actually passed in the additional parameter.
\r
835 bool localsystem = false;
\r
836 TCHAR *username = NSSM_LOCALSYSTEM_ACCOUNT;
\r
837 TCHAR *password = 0;
\r
839 username = (TCHAR *) additional;
\r
840 if (value && value->string) password = value->string;
\r
842 else if (value && value->string) username = value->string;
\r
844 const TCHAR *well_known = well_known_username(username);
\r
845 size_t passwordsize = 0;
\r
847 if (str_equiv(well_known, NSSM_LOCALSYSTEM_ACCOUNT)) localsystem = true;
\r
848 username = (TCHAR *) well_known;
\r
851 else if (! password) {
\r
852 /* We need a password if the account requires it. */
\r
853 print_message(stderr, NSSM_MESSAGE_MISSING_PASSWORD, name);
\r
856 else passwordsize = _tcslen(password) * sizeof(TCHAR);
\r
859 ChangeServiceConfig() will fail to set the username if the service is set
\r
860 to interact with the desktop.
\r
862 unsigned long type = SERVICE_NO_CHANGE;
\r
863 if (! localsystem) {
\r
864 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
866 if (passwordsize) SecureZeroMemory(password, passwordsize);
\r
870 type = qsc->dwServiceType & ~SERVICE_INTERACTIVE_PROCESS;
\r
871 HeapFree(GetProcessHeap(), 0, qsc);
\r
874 if (! well_known) {
\r
875 if (grant_logon_as_service(username)) {
\r
876 if (passwordsize) SecureZeroMemory(password, passwordsize);
\r
877 print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);
\r
882 if (! ChangeServiceConfig(service_handle, type, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, username, password, 0)) {
\r
883 if (passwordsize) SecureZeroMemory(password, passwordsize);
\r
884 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
888 if (passwordsize) SecureZeroMemory(password, passwordsize);
\r
890 if (localsystem) return 0;
\r
895 int native_get_objectname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
896 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
897 if (! service_handle) return -1;
\r
899 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
900 if (! qsc) return -1;
\r
902 int ret = value_from_string(name, value, qsc->lpServiceStartName);
\r
903 HeapFree(GetProcessHeap(), 0, qsc);
\r
908 int native_set_startup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
909 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
910 if (! service_handle) return -1;
\r
912 /* It makes no sense to try to reset the startup type. */
\r
913 if (! value || ! value->string) {
\r
914 print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);
\r
918 /* Map NSSM_STARTUP_* constant to Windows SERVICE_*_START constant. */
\r
919 int service_startup = -1;
\r
921 for (i = 0; startup_strings[i]; i++) {
\r
922 if (str_equiv(value->string, startup_strings[i])) {
\r
923 service_startup = i;
\r
928 if (service_startup < 0) {
\r
929 print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE_STARTUP, value->string);
\r
930 for (i = 0; startup_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), startup_strings[i]);
\r
934 unsigned long startup;
\r
935 switch (service_startup) {
\r
936 case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;
\r
937 case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;
\r
938 default: startup = SERVICE_AUTO_START;
\r
941 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, startup, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) {
\r
942 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
946 SERVICE_DELAYED_AUTO_START_INFO delayed;
\r
947 ZeroMemory(&delayed, sizeof(delayed));
\r
948 if (service_startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;
\r
949 else delayed.fDelayedAutostart = 0;
\r
950 if (! ChangeServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {
\r
951 unsigned long error = GetLastError();
\r
952 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
953 if (error != ERROR_INVALID_LEVEL) {
\r
954 log_event(EVENTLOG_ERROR_TYPE, NSSM_MESSAGE_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service_name, error_string(error), 0);
\r
961 int native_get_startup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
962 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
963 if (! service_handle) return -1;
\r
965 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
966 if (! qsc) return -1;
\r
968 unsigned long startup;
\r
969 int ret = get_service_startup(service_name, service_handle, qsc, &startup);
\r
970 HeapFree(GetProcessHeap(), 0, qsc);
\r
972 if (ret) return -1;
\r
975 for (i = 0; startup_strings[i]; i++);
\r
976 if (startup >= i) return -1;
\r
978 return value_from_string(name, value, startup_strings[startup]);
\r
981 int native_set_type(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
982 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
983 if (! service_handle) return -1;
\r
985 /* It makes no sense to try to reset the service type. */
\r
986 if (! value || ! value->string) {
\r
987 print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);
\r
992 We can only manage services of type SERVICE_WIN32_OWN_PROCESS
\r
993 and SERVICE_INTERACTIVE_PROCESS.
\r
995 unsigned long type = SERVICE_WIN32_OWN_PROCESS;
\r
996 if (str_equiv(value->string, NSSM_INTERACTIVE_PROCESS)) type |= SERVICE_INTERACTIVE_PROCESS;
\r
997 else if (! str_equiv(value->string, NSSM_WIN32_OWN_PROCESS)) {
\r
998 print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE_TYPE, value->string);
\r
999 _ftprintf(stderr, _T("%s\n"), NSSM_WIN32_OWN_PROCESS);
\r
1000 _ftprintf(stderr, _T("%s\n"), NSSM_INTERACTIVE_PROCESS);
\r
1005 ChangeServiceConfig() will fail if the service runs under an account
\r
1006 other than LOCALSYSTEM and we try to make it interactive.
\r
1008 if (type & SERVICE_INTERACTIVE_PROCESS) {
\r
1009 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
1010 if (! qsc) return -1;
\r
1012 if (! str_equiv(qsc->lpServiceStartName, NSSM_LOCALSYSTEM_ACCOUNT)) {
\r
1013 HeapFree(GetProcessHeap(), 0, qsc);
\r
1014 print_message(stderr, NSSM_MESSAGE_INTERACTIVE_NOT_LOCALSYSTEM, value->string, service_name, NSSM_LOCALSYSTEM_ACCOUNT);
\r
1018 HeapFree(GetProcessHeap(), 0, qsc);
\r
1021 if (! ChangeServiceConfig(service_handle, type, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) {
\r
1022 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
1029 int native_get_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 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
1034 if (! qsc) return -1;
\r
1036 value->numeric = qsc->dwServiceType;
\r
1037 HeapFree(GetProcessHeap(), 0, qsc);
\r
1039 const TCHAR *string;
\r
1040 switch (value->numeric) {
\r
1041 case SERVICE_KERNEL_DRIVER: string = NSSM_KERNEL_DRIVER; break;
\r
1042 case SERVICE_FILE_SYSTEM_DRIVER: string = NSSM_FILE_SYSTEM_DRIVER; break;
\r
1043 case SERVICE_WIN32_OWN_PROCESS: string = NSSM_WIN32_OWN_PROCESS; break;
\r
1044 case SERVICE_WIN32_SHARE_PROCESS: string = NSSM_WIN32_SHARE_PROCESS; break;
\r
1045 case SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS: string = NSSM_INTERACTIVE_PROCESS; break;
\r
1046 case SERVICE_WIN32_SHARE_PROCESS|SERVICE_INTERACTIVE_PROCESS: string = NSSM_SHARE_INTERACTIVE_PROCESS; break;
\r
1047 default: string = NSSM_UNKNOWN;
\r
1050 return value_from_string(name, value, string);
\r
1053 int set_setting(const TCHAR *service_name, HKEY key, settings_t *setting, value_t *value, const TCHAR *additional) {
\r
1054 if (! key) return -1;
\r
1057 if (setting->set) ret = setting->set(service_name, (void *) key, setting->name, setting->default_value, value, additional);
\r
1060 if (! ret) print_message(stdout, NSSM_MESSAGE_RESET_SETTING, setting->name, service_name);
\r
1061 else if (ret > 0) print_message(stdout, NSSM_MESSAGE_SET_SETTING, setting->name, service_name);
\r
1062 else print_message(stderr, NSSM_MESSAGE_SET_SETTING_FAILED, setting->name, service_name);
\r
1067 int set_setting(const TCHAR *service_name, SC_HANDLE service_handle, settings_t *setting, value_t *value, const TCHAR *additional) {
\r
1068 if (! service_handle) return -1;
\r
1071 if (setting->set) ret = setting->set(service_name, service_handle, setting->name, setting->default_value, value, additional);
\r
1074 if (! ret) print_message(stdout, NSSM_MESSAGE_RESET_SETTING, setting->name, service_name);
\r
1075 else if (ret > 0) print_message(stdout, NSSM_MESSAGE_SET_SETTING, setting->name, service_name);
\r
1076 else print_message(stderr, NSSM_MESSAGE_SET_SETTING_FAILED, setting->name, service_name);
\r
1082 Returns: 1 if the value was retrieved.
\r
1083 0 if the default value was retrieved.
\r
1086 int get_setting(const TCHAR *service_name, HKEY key, settings_t *setting, value_t *value, const TCHAR *additional) {
\r
1087 if (! key) return -1;
\r
1090 switch (setting->type) {
\r
1091 case REG_EXPAND_SZ:
\r
1092 case REG_MULTI_SZ:
\r
1094 value->string = (TCHAR *) setting->default_value;
\r
1095 if (setting->get) ret = setting->get(service_name, (void *) key, setting->name, setting->default_value, value, additional);
\r
1100 value->numeric = PtrToUlong(setting->default_value);
\r
1101 if (setting->get) ret = setting->get(service_name, (void *) key, setting->name, setting->default_value, value, additional);
\r
1110 if (ret < 0) print_message(stderr, NSSM_MESSAGE_GET_SETTING_FAILED, setting->name, service_name);
\r
1115 int get_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
1117 return setting->get(service_name, service_handle, setting->name, 0, value, additional);
\r
1120 settings_t settings[] = {
\r
1121 { NSSM_REG_EXE, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },
\r
1122 { NSSM_REG_FLAGS, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },
\r
1123 { NSSM_REG_DIR, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },
\r
1124 { NSSM_REG_EXIT, REG_SZ, (void *) exit_action_strings[NSSM_EXIT_RESTART], false, ADDITIONAL_MANDATORY, setting_set_exit_action, setting_get_exit_action },
\r
1125 { NSSM_REG_HOOK, REG_SZ, (void *) _T(""), false, ADDITIONAL_MANDATORY, setting_set_hook, setting_get_hook },
\r
1126 { NSSM_REG_AFFINITY, REG_SZ, 0, false, 0, setting_set_affinity, setting_get_affinity },
\r
1127 { NSSM_REG_ENV, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment },
\r
1128 { NSSM_REG_ENV_EXTRA, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment },
\r
1129 { NSSM_REG_NO_CONSOLE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1130 { NSSM_REG_PRIORITY, REG_SZ, (void *) priority_strings[NSSM_NORMAL_PRIORITY], false, 0, setting_set_priority, setting_get_priority },
\r
1131 { NSSM_REG_RESTART_DELAY, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1132 { NSSM_REG_STDIN, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },
\r
1133 { NSSM_REG_STDIN NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDIN_SHARING, false, 0, setting_set_number, setting_get_number },
\r
1134 { NSSM_REG_STDIN NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDIN_DISPOSITION, false, 0, setting_set_number, setting_get_number },
\r
1135 { NSSM_REG_STDIN NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDIN_FLAGS, false, 0, setting_set_number, setting_get_number },
\r
1136 { NSSM_REG_STDOUT, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },
\r
1137 { NSSM_REG_STDOUT NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDOUT_SHARING, false, 0, setting_set_number, setting_get_number },
\r
1138 { NSSM_REG_STDOUT NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDOUT_DISPOSITION, false, 0, setting_set_number, setting_get_number },
\r
1139 { NSSM_REG_STDOUT NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDOUT_FLAGS, false, 0, setting_set_number, setting_get_number },
\r
1140 { NSSM_REG_STDOUT NSSM_REG_STDIO_COPY_AND_TRUNCATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1141 { NSSM_REG_STDERR, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },
\r
1142 { NSSM_REG_STDERR NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDERR_SHARING, false, 0, setting_set_number, setting_get_number },
\r
1143 { NSSM_REG_STDERR NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDERR_DISPOSITION, false, 0, setting_set_number, setting_get_number },
\r
1144 { NSSM_REG_STDERR NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDERR_FLAGS, false, 0, setting_set_number, setting_get_number },
\r
1145 { NSSM_REG_STDERR NSSM_REG_STDIO_COPY_AND_TRUNCATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1146 { NSSM_REG_STOP_METHOD_SKIP, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1147 { NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_CONSOLE_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },
\r
1148 { NSSM_REG_KILL_WINDOW_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_WINDOW_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },
\r
1149 { NSSM_REG_KILL_THREADS_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_THREADS_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },
\r
1150 { NSSM_REG_KILL_PROCESS_TREE, REG_DWORD, (void *) 1, false, 0, setting_set_number, setting_get_number },
\r
1151 { NSSM_REG_THROTTLE, REG_DWORD, (void *) NSSM_RESET_THROTTLE_RESTART, false, 0, setting_set_number, setting_get_number },
\r
1152 { NSSM_REG_HOOK_SHARE_OUTPUT_HANDLES, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1153 { NSSM_REG_ROTATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1154 { NSSM_REG_ROTATE_ONLINE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1155 { NSSM_REG_ROTATE_SECONDS, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1156 { NSSM_REG_ROTATE_BYTES_LOW, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1157 { NSSM_REG_ROTATE_BYTES_HIGH, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1158 { NSSM_REG_ROTATE_DELAY, REG_DWORD, (void *) NSSM_ROTATE_DELAY, false, 0, setting_set_number, setting_get_number },
\r
1159 { NSSM_NATIVE_DEPENDONGROUP, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_dependongroup, native_get_dependongroup },
\r
1160 { NSSM_NATIVE_DEPENDONSERVICE, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_dependonservice, native_get_dependonservice },
\r
1161 { NSSM_NATIVE_DESCRIPTION, REG_SZ, _T(""), true, 0, native_set_description, native_get_description },
\r
1162 { NSSM_NATIVE_DISPLAYNAME, REG_SZ, NULL, true, 0, native_set_displayname, native_get_displayname },
\r
1163 { NSSM_NATIVE_ENVIRONMENT, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_environment, native_get_environment },
\r
1164 { NSSM_NATIVE_IMAGEPATH, REG_EXPAND_SZ, NULL, true, 0, native_set_imagepath, native_get_imagepath },
\r
1165 { NSSM_NATIVE_OBJECTNAME, REG_SZ, NSSM_LOCALSYSTEM_ACCOUNT, true, 0, native_set_objectname, native_get_objectname },
\r
1166 { NSSM_NATIVE_NAME, REG_SZ, NULL, true, 0, native_set_name, native_get_name },
\r
1167 { NSSM_NATIVE_STARTUP, REG_SZ, NULL, true, 0, native_set_startup, native_get_startup },
\r
1168 { NSSM_NATIVE_TYPE, REG_SZ, NULL, true, 0, native_set_type, native_get_type },
\r
1169 { NULL, NULL, NULL, NULL, NULL }
\r