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
10 extern const TCHAR *hook_event_strings[];
\r
11 extern const TCHAR *hook_action_strings[];
\r
13 /* Does the parameter refer to the default value of the setting? */
\r
14 static inline int is_default(const TCHAR *value) {
\r
15 return (str_equiv(value, _T("default")) || str_equiv(value, _T("*")) || ! value[0]);
\r
18 static int value_from_string(const TCHAR *name, value_t *value, const TCHAR *string) {
\r
19 size_t len = _tcslen(string);
\r
25 value->string = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));
\r
26 if (! value->string) {
\r
27 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, name, _T("value_from_string()"));
\r
31 if (_sntprintf_s(value->string, len, _TRUNCATE, _T("%s"), string) < 0) {
\r
32 HeapFree(GetProcessHeap(), 0, value->string);
\r
33 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, name, _T("value_from_string()"));
\r
40 /* Functions to manage NSSM-specific settings in the registry. */
\r
41 static int setting_set_number(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
42 HKEY key = (HKEY) param;
\r
43 if (! key) return -1;
\r
45 unsigned long number;
\r
48 /* Resetting to default? */
\r
49 if (! value || ! value->string) {
\r
50 error = RegDeleteValue(key, name);
\r
51 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
\r
52 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
\r
55 if (str_number(value->string, &number)) return -1;
\r
57 if (default_value && number == PtrToUlong(default_value)) {
\r
58 error = RegDeleteValue(key, name);
\r
59 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
\r
60 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
\r
64 if (set_number(key, (TCHAR *) name, number)) return -1;
\r
69 static int setting_get_number(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
70 HKEY key = (HKEY) param;
\r
71 return get_number(key, (TCHAR *) name, &value->numeric, false);
\r
74 static int setting_set_string(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
75 HKEY key = (HKEY) param;
\r
76 if (! key) return -1;
\r
80 /* Resetting to default? */
\r
81 if (! value || ! value->string) {
\r
82 if (default_value) value->string = (TCHAR *) default_value;
\r
84 error = RegDeleteValue(key, name);
\r
85 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
\r
86 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
\r
90 if (default_value && _tcslen((TCHAR *) default_value) && str_equiv(value->string, (TCHAR *) default_value)) {
\r
91 error = RegDeleteValue(key, name);
\r
92 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
\r
93 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
\r
97 if (set_expand_string(key, (TCHAR *) name, value->string)) return -1;
\r
102 static int setting_get_string(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
103 HKEY key = (HKEY) param;
\r
104 TCHAR buffer[VALUE_LENGTH];
\r
106 if (get_string(key, (TCHAR *) name, (TCHAR *) buffer, (unsigned long) sizeof(buffer), false, false, false)) return -1;
\r
108 return value_from_string(name, value, buffer);
\r
111 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
112 unsigned long exitcode;
\r
114 TCHAR action_string[ACTION_LEN];
\r
117 /* Default action? */
\r
118 if (is_default(additional)) code = 0;
\r
120 if (str_number(additional, &exitcode)) return -1;
\r
121 code = (TCHAR *) additional;
\r
125 HKEY key = open_registry(service_name, name, KEY_WRITE);
\r
126 if (! key) return -1;
\r
131 /* Resetting to default? */
\r
132 if (value && value->string) _sntprintf_s(action_string, _countof(action_string), _TRUNCATE, _T("%s"), value->string);
\r
135 /* Delete explicit action. */
\r
136 error = RegDeleteValue(key, code);
\r
138 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
\r
139 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, code, service_name, error_string(error));
\r
143 /* Explicitly keep the default action. */
\r
144 if (default_value) _sntprintf_s(action_string, _countof(action_string), _TRUNCATE, _T("%s"), (TCHAR *) default_value);
\r
149 /* Validate the string. */
\r
150 for (int i = 0; exit_action_strings[i]; i++) {
\r
151 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
152 if (default_value && str_equiv(action_string, (TCHAR *) default_value)) ret = 0;
\r
153 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
154 print_message(stderr, NSSM_MESSAGE_SETVALUE_FAILED, code, service_name, error_string(GetLastError()));
\r
164 print_message(stderr, NSSM_MESSAGE_INVALID_EXIT_ACTION, action_string);
\r
165 for (int i = 0; exit_action_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), exit_action_strings[i]);
\r
170 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
171 unsigned long exitcode = 0;
\r
172 unsigned long *code = 0;
\r
175 if (! is_default(additional)) {
\r
176 if (str_number(additional, &exitcode)) return -1;
\r
181 TCHAR action_string[ACTION_LEN];
\r
182 bool default_action;
\r
183 if (get_exit_action(service_name, code, action_string, &default_action)) return -1;
\r
185 value_from_string(name, value, action_string);
\r
187 if (default_action && ! _tcsnicmp((const TCHAR *) action_string, (TCHAR *) default_value, ACTION_LEN)) return 0;
\r
191 static inline bool split_hook_name(const TCHAR *hook_name, TCHAR *hook_event, TCHAR *hook_action) {
\r
194 for (s = (TCHAR *) hook_name; *s; s++) {
\r
195 if (*s == _T('/')) {
\r
197 _sntprintf_s(hook_event, HOOK_NAME_LENGTH, _TRUNCATE, _T("%s"), hook_name);
\r
198 _sntprintf_s(hook_action, HOOK_NAME_LENGTH, _TRUNCATE, _T("%s"), ++s);
\r
199 return valid_hook_name(hook_event, hook_action, false);
\r
203 print_message(stderr, NSSM_MESSAGE_INVALID_HOOK_NAME, hook_name);
\r
207 static int setting_set_hook(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
208 TCHAR hook_event[HOOK_NAME_LENGTH];
\r
209 TCHAR hook_action[HOOK_NAME_LENGTH];
\r
210 if (! split_hook_name(additional, hook_event, hook_action)) return -1;
\r
213 if (value && value->string) cmd = value->string;
\r
216 if (set_hook(service_name, hook_event, hook_action, cmd)) return -1;
\r
217 if (! _tcslen(cmd)) return 0;
\r
221 static int setting_get_hook(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
222 TCHAR hook_event[HOOK_NAME_LENGTH];
\r
223 TCHAR hook_action[HOOK_NAME_LENGTH];
\r
224 if (! split_hook_name(additional, hook_event, hook_action)) return -1;
\r
226 TCHAR cmd[CMD_LENGTH];
\r
227 if (get_hook(service_name, hook_event, hook_action, cmd, sizeof(cmd))) return -1;
\r
229 value_from_string(name, value, cmd);
\r
231 if (! _tcslen(cmd)) return 0;
\r
235 static int setting_set_affinity(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
236 HKEY key = (HKEY) param;
\r
237 if (! key) return -1;
\r
241 __int64 system_affinity = 0LL;
\r
243 if (value && value->string) {
\r
244 DWORD_PTR affinity;
\r
245 if (! GetProcessAffinityMask(GetCurrentProcess(), &affinity, (DWORD_PTR *) &system_affinity)) system_affinity = ~0;
\r
247 if (is_default(value->string) || str_equiv(value->string, NSSM_AFFINITY_ALL)) mask = 0LL;
\r
248 else if (affinity_string_to_mask(value->string, &mask)) {
\r
249 print_message(stderr, NSSM_MESSAGE_BOGUS_AFFINITY_MASK, value->string, num_cpus() - 1);
\r
256 error = RegDeleteValue(key, name);
\r
257 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
\r
258 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
\r
262 /* Canonicalise. */
\r
264 if (affinity_mask_to_string(mask, &canon)) canon = value->string;
\r
266 __int64 effective_affinity = mask & system_affinity;
\r
267 if (effective_affinity != mask) {
\r
268 /* Requested CPUs did not intersect with available CPUs? */
\r
269 if (! effective_affinity) mask = effective_affinity = system_affinity;
\r
272 if (! affinity_mask_to_string(system_affinity, &system)) {
\r
273 TCHAR *effective = 0;
\r
274 if (! affinity_mask_to_string(effective_affinity, &effective)) {
\r
275 print_message(stderr, NSSM_MESSAGE_EFFECTIVE_AFFINITY_MASK, value->string, system, effective);
\r
276 HeapFree(GetProcessHeap(), 0, effective);
\r
278 HeapFree(GetProcessHeap(), 0, system);
\r
282 if (RegSetValueEx(key, name, 0, REG_SZ, (const unsigned char *) canon, (unsigned long) (_tcslen(canon) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
283 if (canon != value->string) HeapFree(GetProcessHeap(), 0, canon);
\r
284 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, name, error_string(GetLastError()), 0);
\r
288 if (canon != value->string) HeapFree(GetProcessHeap(), 0, canon);
\r
292 static int setting_get_affinity(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
293 HKEY key = (HKEY) param;
\r
294 if (! key) return -1;
\r
296 unsigned long type;
\r
298 unsigned long buflen = 0;
\r
300 int ret = RegQueryValueEx(key, name, 0, &type, 0, &buflen);
\r
301 if (ret == ERROR_FILE_NOT_FOUND) {
\r
302 if (value_from_string(name, value, NSSM_AFFINITY_ALL) == 1) return 0;
\r
305 if (ret != ERROR_SUCCESS) return -1;
\r
307 if (type != REG_SZ) return -1;
\r
309 buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, buflen);
\r
311 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("affinity"), _T("setting_get_affinity"));
\r
315 if (get_string(key, (TCHAR *) name, buffer, buflen, false, false, true)) {
\r
316 HeapFree(GetProcessHeap(), 0, buffer);
\r
321 if (affinity_string_to_mask(buffer, &affinity)) {
\r
322 print_message(stderr, NSSM_MESSAGE_BOGUS_AFFINITY_MASK, buffer, num_cpus() - 1);
\r
323 HeapFree(GetProcessHeap(), 0, buffer);
\r
327 HeapFree(GetProcessHeap(), 0, buffer);
\r
329 /* Canonicalise. */
\r
330 if (affinity_mask_to_string(affinity, &buffer)) {
\r
331 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
\r
335 ret = value_from_string(name, value, buffer);
\r
336 HeapFree(GetProcessHeap(), 0, buffer);
\r
340 static int setting_set_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
341 HKEY key = (HKEY) param;
\r
342 if (! param) return -1;
\r
345 TCHAR *unformatted = 0;
\r
346 unsigned long envlen;
\r
347 unsigned long newlen = 0;
\r
349 if (value && value->string && value->string[0]) {
\r
350 string = value->string;
\r
351 switch (string[0]) {
\r
352 case _T('+'): op = 1; break;
\r
353 case _T('-'): op = -1; break;
\r
354 case _T(':'): string++; break;
\r
361 if (get_environment((TCHAR *) service_name, key, (TCHAR *) name, &env, &envlen)) return -1;
\r
364 if (op > 0) ret = append_to_environment_block(env, envlen, string, &unformatted, &newlen);
\r
365 else ret = remove_from_environment_block(env, envlen, string, &unformatted, &newlen);
\r
366 if (envlen) HeapFree(GetProcessHeap(), 0, env);
\r
367 if (ret) return -1;
\r
369 string = unformatted;
\r
373 No existing environment.
\r
374 We can't remove from an empty environment so just treat an add
\r
375 operation as setting a new string.
\r
377 if (op < 0) return 0;
\r
382 if (! string || ! string[0]) {
\r
383 long error = RegDeleteValue(key, name);
\r
384 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
\r
385 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
\r
390 if (unformat_double_null(string, (unsigned long) _tcslen(string), &unformatted, &newlen)) return -1;
\r
393 if (test_environment(unformatted)) {
\r
394 HeapFree(GetProcessHeap(), 0, unformatted);
\r
395 print_message(stderr, NSSM_GUI_INVALID_ENVIRONMENT);
\r
399 if (RegSetValueEx(key, name, 0, REG_MULTI_SZ, (const unsigned char *) unformatted, (unsigned long) newlen * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
400 if (newlen) HeapFree(GetProcessHeap(), 0, unformatted);
\r
401 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV, error_string(GetLastError()), 0);
\r
405 if (newlen) HeapFree(GetProcessHeap(), 0, unformatted);
\r
409 static int setting_get_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
410 HKEY key = (HKEY) param;
\r
411 if (! param) return -1;
\r
414 unsigned long envlen;
\r
415 if (get_environment((TCHAR *) service_name, key, (TCHAR *) name, &env, &envlen)) return -1;
\r
416 if (! envlen) return 0;
\r
419 unsigned long newlen;
\r
420 if (format_double_null(env, envlen, &formatted, &newlen)) return -1;
\r
424 /* Find named environment variable. */
\r
426 size_t len = _tcslen(additional);
\r
427 for (s = env; *s; s++) {
\r
428 /* Look for <additional>=<string> NULL NULL */
\r
429 if (! _tcsnicmp(s, additional, len) && s[len] == _T('=')) {
\r
432 ret = value_from_string(name, value, s);
\r
433 HeapFree(GetProcessHeap(), 0, env);
\r
437 /* Skip this string. */
\r
440 HeapFree(GetProcessHeap(), 0, env);
\r
444 HeapFree(GetProcessHeap(), 0, env);
\r
446 ret = value_from_string(name, value, formatted);
\r
447 if (newlen) HeapFree(GetProcessHeap(), 0, formatted);
\r
451 static int setting_set_priority(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
452 HKEY key = (HKEY) param;
\r
453 if (! param) return -1;
\r
455 TCHAR *priority_string;
\r
459 if (value && value->string) priority_string = value->string;
\r
460 else if (default_value) priority_string = (TCHAR *) default_value;
\r
462 error = RegDeleteValue(key, name);
\r
463 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
\r
464 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
\r
468 for (i = 0; priority_strings[i]; i++) {
\r
469 if (! str_equiv(priority_strings[i], priority_string)) continue;
\r
471 if (default_value && str_equiv(priority_string, (TCHAR *) default_value)) {
\r
472 error = RegDeleteValue(key, name);
\r
473 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
\r
474 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
\r
478 if (set_number(key, (TCHAR *) name, priority_index_to_constant(i))) return -1;
\r
482 print_message(stderr, NSSM_MESSAGE_INVALID_PRIORITY, priority_string);
\r
483 for (i = 0; priority_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), priority_strings[i]);
\r
488 static int setting_get_priority(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
489 HKEY key = (HKEY) param;
\r
490 if (! param) return -1;
\r
492 unsigned long constant;
\r
493 switch (get_number(key, (TCHAR *) name, &constant, false)) {
\r
495 if (value_from_string(name, value, (const TCHAR *) default_value) == -1) return -1;
\r
497 case -1: return -1;
\r
500 return value_from_string(name, value, priority_strings[priority_constant_to_index(constant)]);
\r
503 /* Functions to manage native service settings. */
\r
504 static int native_set_dependon(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR **dependencies, unsigned long *dependencieslen, value_t *value, int type) {
\r
505 *dependencieslen = 0;
\r
506 if (! value || ! value->string || ! value->string[0]) return 0;
\r
508 TCHAR *string = value->string;
\r
509 unsigned long buflen;
\r
511 switch (string[0]) {
\r
512 case _T('+'): op = 1; break;
\r
513 case _T('-'): op = -1; break;
\r
514 case _T(':'): string++; break;
\r
520 if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, type)) return -1;
\r
523 if (op > 0) ret = append_to_dependencies(buffer, buflen, string, dependencies, dependencieslen, type);
\r
524 else ret = remove_from_dependencies(buffer, buflen, string, dependencies, dependencieslen, type);
\r
525 if (buflen) HeapFree(GetProcessHeap(), 0, buffer);
\r
531 We can't remove from an empty list so just treat an add
\r
532 operation as setting a new string.
\r
534 if (op < 0) return 0;
\r
540 TCHAR *unformatted = 0;
\r
541 unsigned long newlen;
\r
542 if (unformat_double_null(string, (unsigned long) _tcslen(string), &unformatted, &newlen)) return -1;
\r
544 if (type == DEPENDENCY_GROUPS) {
\r
545 /* Prepend group identifier. */
\r
546 unsigned long missing = 0;
\r
547 TCHAR *canon = unformatted;
\r
548 size_t canonlen = 0;
\r
550 for (s = unformatted; *s; s++) {
\r
551 if (*s != SC_GROUP_IDENTIFIER) missing++;
\r
552 size_t len = _tcslen(s);
\r
553 canonlen += len + 1;
\r
558 /* Missing identifiers plus double NULL terminator. */
\r
559 canonlen += missing + 1;
\r
561 canon = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, canonlen * sizeof(TCHAR));
\r
563 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canon"), _T("native_set_dependon"));
\r
564 if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);
\r
569 for (s = unformatted; *s; s++) {
\r
570 if (*s != SC_GROUP_IDENTIFIER) canon[i++] = SC_GROUP_IDENTIFIER;
\r
571 size_t len = _tcslen(s);
\r
572 memmove(canon + i, s, (len + 1) * sizeof(TCHAR));
\r
577 HeapFree(GetProcessHeap(), 0, unformatted);
\r
578 unformatted = canon;
\r
579 newlen = (unsigned long) canonlen;
\r
583 *dependencies = unformatted;
\r
584 *dependencieslen = newlen;
\r
590 static int native_set_dependongroup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
591 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
592 if (! service_handle) return -1;
\r
595 Get existing service dependencies because we must set both types together.
\r
597 TCHAR *services_buffer;
\r
598 unsigned long services_buflen;
\r
599 if (get_service_dependencies(service_name, service_handle, &services_buffer, &services_buflen, DEPENDENCY_SERVICES)) return -1;
\r
601 if (! value || ! value->string || ! value->string[0]) {
\r
602 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, services_buffer, 0, 0, 0)) {
\r
603 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
604 if (services_buffer) HeapFree(GetProcessHeap(), 0, services_buffer);
\r
608 if (services_buffer) HeapFree(GetProcessHeap(), 0, services_buffer);
\r
612 /* Update the group list. */
\r
613 TCHAR *groups_buffer;
\r
614 unsigned long groups_buflen;
\r
615 if (native_set_dependon(service_name, service_handle, &groups_buffer, &groups_buflen, value, DEPENDENCY_GROUPS)) return -1;
\r
617 TCHAR *dependencies;
\r
618 if (services_buflen > 2) {
\r
619 dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (groups_buflen + services_buflen) * sizeof(TCHAR));
\r
620 if (! dependencies) {
\r
621 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("dependencies"), _T("native_set_dependongroup"));
\r
622 if (groups_buffer) HeapFree(GetProcessHeap(), 0, groups_buffer);
\r
623 if (services_buffer) HeapFree(GetProcessHeap(), 0, services_buffer);
\r
627 memmove(dependencies, services_buffer, services_buflen * sizeof(TCHAR));
\r
628 memmove(dependencies + services_buflen - 1, groups_buffer, groups_buflen * sizeof(TCHAR));
\r
630 else dependencies = groups_buffer;
\r
633 if (set_service_dependencies(service_name, service_handle, dependencies)) ret = -1;
\r
634 if (dependencies != groups_buffer) HeapFree(GetProcessHeap(), 0, dependencies);
\r
635 if (groups_buffer) HeapFree(GetProcessHeap(), 0, groups_buffer);
\r
636 if (services_buffer) HeapFree(GetProcessHeap(), 0, services_buffer);
\r
641 static int native_get_dependongroup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
642 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
643 if (! service_handle) return -1;
\r
646 unsigned long buflen;
\r
647 if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_GROUPS)) return -1;
\r
652 unsigned long newlen;
\r
653 if (format_double_null(buffer, buflen, &formatted, &newlen)) {
\r
654 HeapFree(GetProcessHeap(), 0, buffer);
\r
658 ret = value_from_string(name, value, formatted);
\r
659 HeapFree(GetProcessHeap(), 0, formatted);
\r
660 HeapFree(GetProcessHeap(), 0, buffer);
\r
670 static int native_set_dependonservice(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
671 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
672 if (! service_handle) return -1;
\r
675 Get existing group dependencies because we must set both types together.
\r
677 TCHAR *groups_buffer;
\r
678 unsigned long groups_buflen;
\r
679 if (get_service_dependencies(service_name, service_handle, &groups_buffer, &groups_buflen, DEPENDENCY_GROUPS)) return -1;
\r
681 if (! value || ! value->string || ! value->string[0]) {
\r
682 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, groups_buffer, 0, 0, 0)) {
\r
683 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
684 if (groups_buffer) HeapFree(GetProcessHeap(), 0, groups_buffer);
\r
688 if (groups_buffer) HeapFree(GetProcessHeap(), 0, groups_buffer);
\r
692 /* Update the service list. */
\r
693 TCHAR *services_buffer;
\r
694 unsigned long services_buflen;
\r
695 if (native_set_dependon(service_name, service_handle, &services_buffer, &services_buflen, value, DEPENDENCY_SERVICES)) return -1;
\r
697 TCHAR *dependencies;
\r
698 if (groups_buflen > 2) {
\r
699 dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (services_buflen + groups_buflen) * sizeof(TCHAR));
\r
700 if (! dependencies) {
\r
701 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("dependencies"), _T("native_set_dependonservice"));
\r
702 if (groups_buffer) HeapFree(GetProcessHeap(), 0, groups_buffer);
\r
703 if (services_buffer) HeapFree(GetProcessHeap(), 0, services_buffer);
\r
707 memmove(dependencies, services_buffer, services_buflen * sizeof(TCHAR));
\r
708 memmove(dependencies + services_buflen - 1, groups_buffer, groups_buflen * sizeof(TCHAR));
\r
710 else dependencies = services_buffer;
\r
713 if (set_service_dependencies(service_name, service_handle, dependencies)) ret = -1;
\r
714 if (dependencies != services_buffer) HeapFree(GetProcessHeap(), 0, dependencies);
\r
715 if (groups_buffer) HeapFree(GetProcessHeap(), 0, groups_buffer);
\r
716 if (services_buffer) HeapFree(GetProcessHeap(), 0, services_buffer);
\r
721 static int native_get_dependonservice(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
722 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
723 if (! service_handle) return -1;
\r
726 unsigned long buflen;
\r
727 if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_SERVICES)) return -1;
\r
732 unsigned long newlen;
\r
733 if (format_double_null(buffer, buflen, &formatted, &newlen)) {
\r
734 HeapFree(GetProcessHeap(), 0, buffer);
\r
738 ret = value_from_string(name, value, formatted);
\r
739 HeapFree(GetProcessHeap(), 0, formatted);
\r
740 HeapFree(GetProcessHeap(), 0, buffer);
\r
750 int native_set_description(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
751 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
752 if (! service_handle) return -1;
\r
754 TCHAR *description = 0;
\r
755 if (value) description = value->string;
\r
756 if (set_service_description(service_name, service_handle, description)) return -1;
\r
758 if (description && description[0]) return 1;
\r
763 int native_get_description(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
764 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
765 if (! service_handle) return -1;
\r
767 TCHAR buffer[VALUE_LENGTH];
\r
768 if (get_service_description(service_name, service_handle, _countof(buffer), buffer)) return -1;
\r
770 if (buffer[0]) return value_from_string(name, value, buffer);
\r
776 int native_set_displayname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
777 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
778 if (! service_handle) return -1;
\r
780 TCHAR *displayname = 0;
\r
781 if (value && value->string) displayname = value->string;
\r
782 else displayname = (TCHAR *) service_name;
\r
784 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, displayname)) {
\r
785 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
790 If the display name and service name differ only in case,
\r
791 ChangeServiceConfig() will return success but the display name will be
\r
792 set to the service name, NOT the value passed to the function.
\r
793 This appears to be a quirk of Windows rather than a bug here.
\r
795 if (displayname != service_name && ! str_equiv(displayname, service_name)) return 1;
\r
800 int native_get_displayname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
801 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
802 if (! service_handle) return -1;
\r
804 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
805 if (! qsc) return -1;
\r
807 int ret = value_from_string(name, value, qsc->lpDisplayName);
\r
808 HeapFree(GetProcessHeap(), 0, qsc);
\r
813 int native_set_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
814 HKEY key = open_service_registry(service_name, KEY_SET_VALUE, true);
\r
815 if (! key) return -1;
\r
817 int ret = setting_set_environment(service_name, (void *) key, name, default_value, value, additional);
\r
822 int native_get_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
823 HKEY key = open_service_registry(service_name, KEY_READ, true);
\r
824 if (! key) return -1;
\r
826 ZeroMemory(value, sizeof(value_t));
\r
827 int ret = setting_get_environment(service_name, (void *) key, name, default_value, value, additional);
\r
832 int native_set_imagepath(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
833 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
834 if (! service_handle) return -1;
\r
836 /* It makes no sense to try to reset the image path. */
\r
837 if (! value || ! value->string) {
\r
838 print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);
\r
842 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, value->string, 0, 0, 0, 0, 0, 0)) {
\r
843 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
850 int native_get_imagepath(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
851 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
852 if (! service_handle) return -1;
\r
854 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
855 if (! qsc) return -1;
\r
857 int ret = value_from_string(name, value, qsc->lpBinaryPathName);
\r
858 HeapFree(GetProcessHeap(), 0, qsc);
\r
863 int native_set_name(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
864 print_message(stderr, NSSM_MESSAGE_CANNOT_RENAME_SERVICE);
\r
868 int native_get_name(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
869 return value_from_string(name, value, service_name);
\r
872 int native_set_objectname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
873 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
874 if (! service_handle) return -1;
\r
877 Logical syntax is: nssm set <service> ObjectName <username> <password>
\r
878 That means the username is actually passed in the additional parameter.
\r
880 bool localsystem = false;
\r
881 TCHAR *username = NSSM_LOCALSYSTEM_ACCOUNT;
\r
882 TCHAR *password = 0;
\r
884 username = (TCHAR *) additional;
\r
885 if (value && value->string) password = value->string;
\r
887 else if (value && value->string) username = value->string;
\r
889 const TCHAR *well_known = well_known_username(username);
\r
890 size_t passwordsize = 0;
\r
892 if (str_equiv(well_known, NSSM_LOCALSYSTEM_ACCOUNT)) localsystem = true;
\r
893 username = (TCHAR *) well_known;
\r
896 else if (! password) {
\r
897 /* We need a password if the account requires it. */
\r
898 print_message(stderr, NSSM_MESSAGE_MISSING_PASSWORD, name);
\r
901 else passwordsize = _tcslen(password) * sizeof(TCHAR);
\r
904 ChangeServiceConfig() will fail to set the username if the service is set
\r
905 to interact with the desktop.
\r
907 unsigned long type = SERVICE_NO_CHANGE;
\r
908 if (! localsystem) {
\r
909 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
911 if (passwordsize) SecureZeroMemory(password, passwordsize);
\r
915 type = qsc->dwServiceType & ~SERVICE_INTERACTIVE_PROCESS;
\r
916 HeapFree(GetProcessHeap(), 0, qsc);
\r
919 if (! well_known) {
\r
920 if (grant_logon_as_service(username)) {
\r
921 if (passwordsize) SecureZeroMemory(password, passwordsize);
\r
922 print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);
\r
927 if (! ChangeServiceConfig(service_handle, type, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, username, password, 0)) {
\r
928 if (passwordsize) SecureZeroMemory(password, passwordsize);
\r
929 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
933 if (passwordsize) SecureZeroMemory(password, passwordsize);
\r
935 if (localsystem) return 0;
\r
940 int native_get_objectname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
941 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
942 if (! service_handle) return -1;
\r
944 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
945 if (! qsc) return -1;
\r
947 int ret = value_from_string(name, value, qsc->lpServiceStartName);
\r
948 HeapFree(GetProcessHeap(), 0, qsc);
\r
953 int native_set_startup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
954 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
955 if (! service_handle) return -1;
\r
957 /* It makes no sense to try to reset the startup type. */
\r
958 if (! value || ! value->string) {
\r
959 print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);
\r
963 /* Map NSSM_STARTUP_* constant to Windows SERVICE_*_START constant. */
\r
964 int service_startup = -1;
\r
966 for (i = 0; startup_strings[i]; i++) {
\r
967 if (str_equiv(value->string, startup_strings[i])) {
\r
968 service_startup = i;
\r
973 if (service_startup < 0) {
\r
974 print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE_STARTUP, value->string);
\r
975 for (i = 0; startup_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), startup_strings[i]);
\r
979 unsigned long startup;
\r
980 switch (service_startup) {
\r
981 case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;
\r
982 case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;
\r
983 default: startup = SERVICE_AUTO_START;
\r
986 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, startup, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) {
\r
987 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
991 SERVICE_DELAYED_AUTO_START_INFO delayed;
\r
992 ZeroMemory(&delayed, sizeof(delayed));
\r
993 if (service_startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;
\r
994 else delayed.fDelayedAutostart = 0;
\r
995 if (! ChangeServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {
\r
996 unsigned long error = GetLastError();
\r
997 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
998 if (error != ERROR_INVALID_LEVEL) {
\r
999 log_event(EVENTLOG_ERROR_TYPE, NSSM_MESSAGE_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service_name, error_string(error), 0);
\r
1006 int native_get_startup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
1007 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
1008 if (! service_handle) return -1;
\r
1010 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
1011 if (! qsc) return -1;
\r
1013 unsigned long startup;
\r
1014 int ret = get_service_startup(service_name, service_handle, qsc, &startup);
\r
1015 HeapFree(GetProcessHeap(), 0, qsc);
\r
1017 if (ret) return -1;
\r
1020 for (i = 0; startup_strings[i]; i++);
\r
1021 if (startup >= i) return -1;
\r
1023 return value_from_string(name, value, startup_strings[startup]);
\r
1026 int native_set_type(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
1027 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
1028 if (! service_handle) return -1;
\r
1030 /* It makes no sense to try to reset the service type. */
\r
1031 if (! value || ! value->string) {
\r
1032 print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);
\r
1037 We can only manage services of type SERVICE_WIN32_OWN_PROCESS
\r
1038 and SERVICE_INTERACTIVE_PROCESS.
\r
1040 unsigned long type = SERVICE_WIN32_OWN_PROCESS;
\r
1041 if (str_equiv(value->string, NSSM_INTERACTIVE_PROCESS)) type |= SERVICE_INTERACTIVE_PROCESS;
\r
1042 else if (! str_equiv(value->string, NSSM_WIN32_OWN_PROCESS)) {
\r
1043 print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE_TYPE, value->string);
\r
1044 _ftprintf(stderr, _T("%s\n"), NSSM_WIN32_OWN_PROCESS);
\r
1045 _ftprintf(stderr, _T("%s\n"), NSSM_INTERACTIVE_PROCESS);
\r
1050 ChangeServiceConfig() will fail if the service runs under an account
\r
1051 other than LOCALSYSTEM and we try to make it interactive.
\r
1053 if (type & SERVICE_INTERACTIVE_PROCESS) {
\r
1054 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
1055 if (! qsc) return -1;
\r
1057 if (! str_equiv(qsc->lpServiceStartName, NSSM_LOCALSYSTEM_ACCOUNT)) {
\r
1058 HeapFree(GetProcessHeap(), 0, qsc);
\r
1059 print_message(stderr, NSSM_MESSAGE_INTERACTIVE_NOT_LOCALSYSTEM, value->string, service_name, NSSM_LOCALSYSTEM_ACCOUNT);
\r
1063 HeapFree(GetProcessHeap(), 0, qsc);
\r
1066 if (! ChangeServiceConfig(service_handle, type, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) {
\r
1067 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
1074 int native_get_type(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
1075 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
1076 if (! service_handle) return -1;
\r
1078 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
1079 if (! qsc) return -1;
\r
1081 value->numeric = qsc->dwServiceType;
\r
1082 HeapFree(GetProcessHeap(), 0, qsc);
\r
1084 const TCHAR *string;
\r
1085 switch (value->numeric) {
\r
1086 case SERVICE_KERNEL_DRIVER: string = NSSM_KERNEL_DRIVER; break;
\r
1087 case SERVICE_FILE_SYSTEM_DRIVER: string = NSSM_FILE_SYSTEM_DRIVER; break;
\r
1088 case SERVICE_WIN32_OWN_PROCESS: string = NSSM_WIN32_OWN_PROCESS; break;
\r
1089 case SERVICE_WIN32_SHARE_PROCESS: string = NSSM_WIN32_SHARE_PROCESS; break;
\r
1090 case SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS: string = NSSM_INTERACTIVE_PROCESS; break;
\r
1091 case SERVICE_WIN32_SHARE_PROCESS|SERVICE_INTERACTIVE_PROCESS: string = NSSM_SHARE_INTERACTIVE_PROCESS; break;
\r
1092 default: string = NSSM_UNKNOWN;
\r
1095 return value_from_string(name, value, string);
\r
1098 int set_setting(const TCHAR *service_name, HKEY key, settings_t *setting, value_t *value, const TCHAR *additional) {
\r
1099 if (! key) return -1;
\r
1102 if (setting->set) ret = setting->set(service_name, (void *) key, setting->name, setting->default_value, value, additional);
\r
1105 if (! ret) print_message(stdout, NSSM_MESSAGE_RESET_SETTING, setting->name, service_name);
\r
1106 else if (ret > 0) print_message(stdout, NSSM_MESSAGE_SET_SETTING, setting->name, service_name);
\r
1107 else print_message(stderr, NSSM_MESSAGE_SET_SETTING_FAILED, setting->name, service_name);
\r
1112 int set_setting(const TCHAR *service_name, SC_HANDLE service_handle, settings_t *setting, value_t *value, const TCHAR *additional) {
\r
1113 if (! service_handle) return -1;
\r
1116 if (setting->set) ret = setting->set(service_name, service_handle, setting->name, setting->default_value, value, additional);
\r
1119 if (! ret) print_message(stdout, NSSM_MESSAGE_RESET_SETTING, setting->name, service_name);
\r
1120 else if (ret > 0) print_message(stdout, NSSM_MESSAGE_SET_SETTING, setting->name, service_name);
\r
1121 else print_message(stderr, NSSM_MESSAGE_SET_SETTING_FAILED, setting->name, service_name);
\r
1127 Returns: 1 if the value was retrieved.
\r
1128 0 if the default value was retrieved.
\r
1131 int get_setting(const TCHAR *service_name, HKEY key, settings_t *setting, value_t *value, const TCHAR *additional) {
\r
1132 if (! key) return -1;
\r
1135 switch (setting->type) {
\r
1136 case REG_EXPAND_SZ:
\r
1137 case REG_MULTI_SZ:
\r
1139 value->string = (TCHAR *) setting->default_value;
\r
1140 if (setting->get) ret = setting->get(service_name, (void *) key, setting->name, setting->default_value, value, additional);
\r
1145 value->numeric = PtrToUlong(setting->default_value);
\r
1146 if (setting->get) ret = setting->get(service_name, (void *) key, setting->name, setting->default_value, value, additional);
\r
1155 if (ret < 0) print_message(stderr, NSSM_MESSAGE_GET_SETTING_FAILED, setting->name, service_name);
\r
1160 int get_setting(const TCHAR *service_name, SC_HANDLE service_handle, settings_t *setting, value_t *value, const TCHAR *additional) {
\r
1161 if (! service_handle) return -1;
\r
1162 return setting->get(service_name, service_handle, setting->name, 0, value, additional);
\r
1165 settings_t settings[] = {
\r
1166 { NSSM_REG_EXE, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },
\r
1167 { NSSM_REG_FLAGS, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },
\r
1168 { NSSM_REG_DIR, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },
\r
1169 { NSSM_REG_EXIT, REG_SZ, (void *) exit_action_strings[NSSM_EXIT_RESTART], false, ADDITIONAL_MANDATORY, setting_set_exit_action, setting_get_exit_action },
\r
1170 { NSSM_REG_HOOK, REG_SZ, (void *) _T(""), false, ADDITIONAL_MANDATORY, setting_set_hook, setting_get_hook },
\r
1171 { NSSM_REG_AFFINITY, REG_SZ, 0, false, 0, setting_set_affinity, setting_get_affinity },
\r
1172 { NSSM_REG_ENV, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment },
\r
1173 { NSSM_REG_ENV_EXTRA, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment },
\r
1174 { NSSM_REG_NO_CONSOLE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1175 { NSSM_REG_PRIORITY, REG_SZ, (void *) priority_strings[NSSM_NORMAL_PRIORITY], false, 0, setting_set_priority, setting_get_priority },
\r
1176 { NSSM_REG_RESTART_DELAY, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1177 { NSSM_REG_STDIN, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },
\r
1178 { NSSM_REG_STDIN NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDIN_SHARING, false, 0, setting_set_number, setting_get_number },
\r
1179 { NSSM_REG_STDIN NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDIN_DISPOSITION, false, 0, setting_set_number, setting_get_number },
\r
1180 { NSSM_REG_STDIN NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDIN_FLAGS, false, 0, setting_set_number, setting_get_number },
\r
1181 { NSSM_REG_STDOUT, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },
\r
1182 { NSSM_REG_STDOUT NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDOUT_SHARING, false, 0, setting_set_number, setting_get_number },
\r
1183 { NSSM_REG_STDOUT NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDOUT_DISPOSITION, false, 0, setting_set_number, setting_get_number },
\r
1184 { NSSM_REG_STDOUT NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDOUT_FLAGS, false, 0, setting_set_number, setting_get_number },
\r
1185 { NSSM_REG_STDOUT NSSM_REG_STDIO_COPY_AND_TRUNCATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1186 { NSSM_REG_STDERR, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },
\r
1187 { NSSM_REG_STDERR NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDERR_SHARING, false, 0, setting_set_number, setting_get_number },
\r
1188 { NSSM_REG_STDERR NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDERR_DISPOSITION, false, 0, setting_set_number, setting_get_number },
\r
1189 { NSSM_REG_STDERR NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDERR_FLAGS, false, 0, setting_set_number, setting_get_number },
\r
1190 { NSSM_REG_STDERR NSSM_REG_STDIO_COPY_AND_TRUNCATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1191 { NSSM_REG_STOP_METHOD_SKIP, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1192 { NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_CONSOLE_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },
\r
1193 { NSSM_REG_KILL_WINDOW_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_WINDOW_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },
\r
1194 { NSSM_REG_KILL_THREADS_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_THREADS_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },
\r
1195 { NSSM_REG_KILL_PROCESS_TREE, REG_DWORD, (void *) 1, false, 0, setting_set_number, setting_get_number },
\r
1196 { NSSM_REG_THROTTLE, REG_DWORD, (void *) NSSM_RESET_THROTTLE_RESTART, false, 0, setting_set_number, setting_get_number },
\r
1197 { NSSM_REG_HOOK_SHARE_OUTPUT_HANDLES, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1198 { NSSM_REG_ROTATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1199 { NSSM_REG_ROTATE_ONLINE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1200 { NSSM_REG_ROTATE_SECONDS, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1201 { NSSM_REG_ROTATE_BYTES_LOW, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1202 { NSSM_REG_ROTATE_BYTES_HIGH, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1203 { NSSM_REG_ROTATE_DELAY, REG_DWORD, (void *) NSSM_ROTATE_DELAY, false, 0, setting_set_number, setting_get_number },
\r
1204 { NSSM_NATIVE_DEPENDONGROUP, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_dependongroup, native_get_dependongroup },
\r
1205 { NSSM_NATIVE_DEPENDONSERVICE, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_dependonservice, native_get_dependonservice },
\r
1206 { NSSM_NATIVE_DESCRIPTION, REG_SZ, _T(""), true, 0, native_set_description, native_get_description },
\r
1207 { NSSM_NATIVE_DISPLAYNAME, REG_SZ, NULL, true, 0, native_set_displayname, native_get_displayname },
\r
1208 { NSSM_NATIVE_ENVIRONMENT, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_environment, native_get_environment },
\r
1209 { NSSM_NATIVE_IMAGEPATH, REG_EXPAND_SZ, NULL, true, 0, native_set_imagepath, native_get_imagepath },
\r
1210 { NSSM_NATIVE_OBJECTNAME, REG_SZ, NSSM_LOCALSYSTEM_ACCOUNT, true, 0, native_set_objectname, native_get_objectname },
\r
1211 { NSSM_NATIVE_NAME, REG_SZ, NULL, true, 0, native_set_name, native_get_name },
\r
1212 { NSSM_NATIVE_STARTUP, REG_SZ, NULL, true, 0, native_set_startup, native_get_startup },
\r
1213 { NSSM_NATIVE_TYPE, REG_SZ, NULL, true, 0, native_set_type, native_get_type },
\r
1214 { NULL, NULL, NULL, NULL, NULL }
\r