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
342 if (! value || ! value->string || ! value->string[0]) {
\r
343 long error = RegDeleteValue(key, name);
\r
344 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
\r
345 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
\r
349 unsigned long envlen = (unsigned long) _tcslen(value->string) + 1;
\r
350 TCHAR *unformatted = 0;
\r
351 unsigned long newlen;
\r
352 if (unformat_double_null(value->string, envlen, &unformatted, &newlen)) return -1;
\r
354 if (test_environment(unformatted)) {
\r
355 HeapFree(GetProcessHeap(), 0, unformatted);
\r
356 print_message(stderr, NSSM_GUI_INVALID_ENVIRONMENT);
\r
360 if (RegSetValueEx(key, name, 0, REG_MULTI_SZ, (const unsigned char *) unformatted, (unsigned long) newlen * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
361 if (newlen) HeapFree(GetProcessHeap(), 0, unformatted);
\r
362 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV, error_string(GetLastError()), 0);
\r
366 if (newlen) HeapFree(GetProcessHeap(), 0, unformatted);
\r
370 static int setting_get_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
371 HKEY key = (HKEY) param;
\r
372 if (! param) return -1;
\r
375 unsigned long envlen;
\r
376 if (get_environment((TCHAR *) service_name, key, (TCHAR *) name, &env, &envlen)) return -1;
\r
377 if (! envlen) return 0;
\r
380 unsigned long newlen;
\r
381 if (format_double_null(env, envlen, &formatted, &newlen)) return -1;
\r
385 /* Find named environment variable. */
\r
387 size_t len = _tcslen(additional);
\r
388 for (s = env; *s; s++) {
\r
389 /* Look for <additional>=<string> NULL NULL */
\r
390 if (! _tcsnicmp(s, additional, len) && s[len] == _T('=')) {
\r
393 ret = value_from_string(name, value, s);
\r
394 HeapFree(GetProcessHeap(), 0, env);
\r
398 /* Skip this string. */
\r
401 HeapFree(GetProcessHeap(), 0, env);
\r
405 HeapFree(GetProcessHeap(), 0, env);
\r
407 ret = value_from_string(name, value, formatted);
\r
408 if (newlen) HeapFree(GetProcessHeap(), 0, formatted);
\r
412 static int setting_set_priority(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
413 HKEY key = (HKEY) param;
\r
414 if (! param) return -1;
\r
416 TCHAR *priority_string;
\r
420 if (value && value->string) priority_string = value->string;
\r
421 else if (default_value) priority_string = (TCHAR *) default_value;
\r
423 error = RegDeleteValue(key, name);
\r
424 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
\r
425 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
\r
429 for (i = 0; priority_strings[i]; i++) {
\r
430 if (! str_equiv(priority_strings[i], priority_string)) continue;
\r
432 if (default_value && str_equiv(priority_string, (TCHAR *) default_value)) {
\r
433 error = RegDeleteValue(key, name);
\r
434 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
\r
435 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
\r
439 if (set_number(key, (TCHAR *) name, priority_index_to_constant(i))) return -1;
\r
443 print_message(stderr, NSSM_MESSAGE_INVALID_PRIORITY, priority_string);
\r
444 for (i = 0; priority_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), priority_strings[i]);
\r
449 static int setting_get_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 unsigned long constant;
\r
454 switch (get_number(key, (TCHAR *) name, &constant, false)) {
\r
455 case 0: return value_from_string(name, value, (const TCHAR *) default_value);
\r
456 case -1: return -1;
\r
459 return value_from_string(name, value, priority_strings[priority_constant_to_index(constant)]);
\r
462 /* Functions to manage native service settings. */
\r
463 static int native_set_dependongroup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
464 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
465 if (! service_handle) return -1;
\r
468 Get existing service dependencies because we must set both types together.
\r
471 unsigned long buflen;
\r
472 if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_SERVICES)) return -1;
\r
474 if (! value || ! value->string || ! value->string[0]) {
\r
475 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, buffer, 0, 0, 0)) {
\r
476 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
477 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
\r
481 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
\r
485 unsigned long len = (unsigned long) _tcslen(value->string) + 1;
\r
486 TCHAR *unformatted = 0;
\r
487 unsigned long newlen;
\r
488 if (unformat_double_null(value->string, len, &unformatted, &newlen)) {
\r
489 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
\r
493 /* Prepend group identifier. */
\r
494 unsigned long missing = 0;
\r
495 TCHAR *canon = unformatted;
\r
496 size_t canonlen = 0;
\r
498 for (s = unformatted; *s; s++) {
\r
499 if (*s != SC_GROUP_IDENTIFIER) missing++;
\r
500 size_t len = _tcslen(s);
\r
501 canonlen += len + 1;
\r
506 /* Missing identifiers plus double NULL terminator. */
\r
507 canonlen += missing + 1;
\r
508 newlen = (unsigned long) canonlen;
\r
510 canon = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, canonlen * sizeof(TCHAR));
\r
512 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canon"), _T("native_set_dependongroup"));
\r
513 if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);
\r
514 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
\r
519 for (s = unformatted; *s; s++) {
\r
520 if (*s != SC_GROUP_IDENTIFIER) canon[i++] = SC_GROUP_IDENTIFIER;
\r
521 size_t len = _tcslen(s);
\r
522 memmove(canon + i, s, (len + 1) * sizeof(TCHAR));
\r
528 TCHAR *dependencies;
\r
530 dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (newlen + buflen) * sizeof(TCHAR));
\r
531 if (! dependencies) {
\r
532 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("dependencies"), _T("native_set_dependongroup"));
\r
533 if (canon != unformatted) HeapFree(GetProcessHeap(), 0, canon);
\r
534 if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);
\r
535 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
\r
539 memmove(dependencies, buffer, buflen * sizeof(TCHAR));
\r
540 memmove(dependencies + buflen - 1, canon, newlen * sizeof(TCHAR));
\r
542 else dependencies = canon;
\r
545 if (set_service_dependencies(service_name, service_handle, dependencies)) ret = -1;
\r
546 if (dependencies != unformatted) HeapFree(GetProcessHeap(), 0, dependencies);
\r
547 if (canon != unformatted) HeapFree(GetProcessHeap(), 0, canon);
\r
548 if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);
\r
549 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
\r
554 static int native_get_dependongroup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
555 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
556 if (! service_handle) return -1;
\r
559 unsigned long buflen;
\r
560 if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_GROUPS)) return -1;
\r
565 unsigned long newlen;
\r
566 if (format_double_null(buffer, buflen, &formatted, &newlen)) {
\r
567 HeapFree(GetProcessHeap(), 0, buffer);
\r
571 ret = value_from_string(name, value, formatted);
\r
572 HeapFree(GetProcessHeap(), 0, formatted);
\r
573 HeapFree(GetProcessHeap(), 0, buffer);
\r
583 static int native_set_dependonservice(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
584 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
585 if (! service_handle) return -1;
\r
588 Get existing group dependencies because we must set both types together.
\r
591 unsigned long buflen;
\r
592 if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_GROUPS)) return -1;
\r
594 if (! value || ! value->string || ! value->string[0]) {
\r
595 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, buffer, 0, 0, 0)) {
\r
596 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
597 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
\r
601 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
\r
605 unsigned long len = (unsigned long) _tcslen(value->string) + 1;
\r
606 TCHAR *unformatted = 0;
\r
607 unsigned long newlen;
\r
608 if (unformat_double_null(value->string, len, &unformatted, &newlen)) {
\r
609 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
\r
613 TCHAR *dependencies;
\r
615 dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (newlen + buflen) * sizeof(TCHAR));
\r
616 if (! dependencies) {
\r
617 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("dependencies"), _T("native_set_dependonservice"));
\r
618 if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);
\r
619 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
\r
623 memmove(dependencies, buffer, buflen * sizeof(TCHAR));
\r
624 memmove(dependencies + buflen - 1, unformatted, newlen * sizeof(TCHAR));
\r
626 else dependencies = unformatted;
\r
629 if (set_service_dependencies(service_name, service_handle, dependencies)) ret = -1;
\r
630 if (dependencies != unformatted) HeapFree(GetProcessHeap(), 0, dependencies);
\r
631 if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);
\r
632 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
\r
637 static int native_get_dependonservice(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
638 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
639 if (! service_handle) return -1;
\r
642 unsigned long buflen;
\r
643 if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_SERVICES)) return -1;
\r
648 unsigned long newlen;
\r
649 if (format_double_null(buffer, buflen, &formatted, &newlen)) {
\r
650 HeapFree(GetProcessHeap(), 0, buffer);
\r
654 ret = value_from_string(name, value, formatted);
\r
655 HeapFree(GetProcessHeap(), 0, formatted);
\r
656 HeapFree(GetProcessHeap(), 0, buffer);
\r
666 int native_set_description(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
667 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
668 if (! service_handle) return -1;
\r
670 TCHAR *description = 0;
\r
671 if (value) description = value->string;
\r
672 if (set_service_description(service_name, service_handle, description)) return -1;
\r
674 if (description && description[0]) return 1;
\r
679 int native_get_description(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
680 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
681 if (! service_handle) return -1;
\r
683 TCHAR buffer[VALUE_LENGTH];
\r
684 if (get_service_description(service_name, service_handle, _countof(buffer), buffer)) return -1;
\r
686 if (buffer[0]) return value_from_string(name, value, buffer);
\r
692 int native_set_displayname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
693 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
694 if (! service_handle) return -1;
\r
696 TCHAR *displayname = 0;
\r
697 if (value && value->string) displayname = value->string;
\r
698 else displayname = (TCHAR *) service_name;
\r
700 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, displayname)) {
\r
701 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
706 If the display name and service name differ only in case,
\r
707 ChangeServiceConfig() will return success but the display name will be
\r
708 set to the service name, NOT the value passed to the function.
\r
709 This appears to be a quirk of Windows rather than a bug here.
\r
711 if (displayname != service_name && ! str_equiv(displayname, service_name)) return 1;
\r
716 int native_get_displayname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
717 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
718 if (! service_handle) return -1;
\r
720 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
721 if (! qsc) return -1;
\r
723 int ret = value_from_string(name, value, qsc->lpDisplayName);
\r
724 HeapFree(GetProcessHeap(), 0, qsc);
\r
729 int native_set_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
730 HKEY key = open_service_registry(service_name, KEY_SET_VALUE, false);
\r
731 if (! key) return -1;
\r
733 int ret = setting_set_environment(service_name, (void *) key, NSSM_NATIVE_ENVIRONMENT, default_value, value, additional);
\r
738 int native_get_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
739 HKEY key = open_service_registry(service_name, KEY_READ, false);
\r
740 if (! key) return -1;
\r
742 ZeroMemory(value, sizeof(value_t));
\r
743 int ret = setting_get_environment(service_name, (void *) key, NSSM_NATIVE_ENVIRONMENT, default_value, value, additional);
\r
748 int native_set_imagepath(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
749 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
750 if (! service_handle) return -1;
\r
752 /* It makes no sense to try to reset the image path. */
\r
753 if (! value || ! value->string) {
\r
754 print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);
\r
758 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, value->string, 0, 0, 0, 0, 0, 0)) {
\r
759 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
766 int native_get_imagepath(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
767 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
768 if (! service_handle) return -1;
\r
770 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
771 if (! qsc) return -1;
\r
773 int ret = value_from_string(name, value, qsc->lpBinaryPathName);
\r
774 HeapFree(GetProcessHeap(), 0, qsc);
\r
779 int native_set_name(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
780 print_message(stderr, NSSM_MESSAGE_CANNOT_RENAME_SERVICE);
\r
784 int native_get_name(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
785 return value_from_string(name, value, service_name);
\r
788 int native_set_objectname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
789 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
790 if (! service_handle) return -1;
\r
793 Logical syntax is: nssm set <service> ObjectName <username> <password>
\r
794 That means the username is actually passed in the additional parameter.
\r
796 bool localsystem = false;
\r
797 TCHAR *username = NSSM_LOCALSYSTEM_ACCOUNT;
\r
798 TCHAR *password = 0;
\r
800 username = (TCHAR *) additional;
\r
801 if (value && value->string) password = value->string;
\r
803 else if (value && value->string) username = value->string;
\r
805 const TCHAR *well_known = well_known_username(username);
\r
806 size_t passwordsize = 0;
\r
808 if (str_equiv(well_known, NSSM_LOCALSYSTEM_ACCOUNT)) localsystem = true;
\r
809 username = (TCHAR *) well_known;
\r
812 else if (! password) {
\r
813 /* We need a password if the account requires it. */
\r
814 print_message(stderr, NSSM_MESSAGE_MISSING_PASSWORD, name);
\r
817 else passwordsize = _tcslen(password) * sizeof(TCHAR);
\r
820 ChangeServiceConfig() will fail to set the username if the service is set
\r
821 to interact with the desktop.
\r
823 unsigned long type = SERVICE_NO_CHANGE;
\r
824 if (! localsystem) {
\r
825 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
827 if (passwordsize) SecureZeroMemory(password, passwordsize);
\r
831 type = qsc->dwServiceType & ~SERVICE_INTERACTIVE_PROCESS;
\r
832 HeapFree(GetProcessHeap(), 0, qsc);
\r
835 if (! well_known) {
\r
836 if (grant_logon_as_service(username)) {
\r
837 if (passwordsize) SecureZeroMemory(password, passwordsize);
\r
838 print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);
\r
843 if (! ChangeServiceConfig(service_handle, type, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, username, password, 0)) {
\r
844 if (passwordsize) SecureZeroMemory(password, passwordsize);
\r
845 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
849 if (passwordsize) SecureZeroMemory(password, passwordsize);
\r
851 if (localsystem) return 0;
\r
856 int native_get_objectname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
857 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
858 if (! service_handle) return -1;
\r
860 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
861 if (! qsc) return -1;
\r
863 int ret = value_from_string(name, value, qsc->lpServiceStartName);
\r
864 HeapFree(GetProcessHeap(), 0, qsc);
\r
869 int native_set_startup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
870 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
871 if (! service_handle) return -1;
\r
873 /* It makes no sense to try to reset the startup type. */
\r
874 if (! value || ! value->string) {
\r
875 print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);
\r
879 /* Map NSSM_STARTUP_* constant to Windows SERVICE_*_START constant. */
\r
880 int service_startup = -1;
\r
882 for (i = 0; startup_strings[i]; i++) {
\r
883 if (str_equiv(value->string, startup_strings[i])) {
\r
884 service_startup = i;
\r
889 if (service_startup < 0) {
\r
890 print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE_STARTUP, value->string);
\r
891 for (i = 0; startup_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), startup_strings[i]);
\r
895 unsigned long startup;
\r
896 switch (service_startup) {
\r
897 case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;
\r
898 case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;
\r
899 default: startup = SERVICE_AUTO_START;
\r
902 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, startup, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) {
\r
903 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
907 SERVICE_DELAYED_AUTO_START_INFO delayed;
\r
908 ZeroMemory(&delayed, sizeof(delayed));
\r
909 if (service_startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;
\r
910 else delayed.fDelayedAutostart = 0;
\r
911 if (! ChangeServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {
\r
912 unsigned long error = GetLastError();
\r
913 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
914 if (error != ERROR_INVALID_LEVEL) {
\r
915 log_event(EVENTLOG_ERROR_TYPE, NSSM_MESSAGE_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service_name, error_string(error), 0);
\r
922 int native_get_startup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
923 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
924 if (! service_handle) return -1;
\r
926 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
927 if (! qsc) return -1;
\r
929 unsigned long startup;
\r
930 int ret = get_service_startup(service_name, service_handle, qsc, &startup);
\r
931 HeapFree(GetProcessHeap(), 0, qsc);
\r
933 if (ret) return -1;
\r
936 for (i = 0; startup_strings[i]; i++);
\r
937 if (startup >= i) return -1;
\r
939 return value_from_string(name, value, startup_strings[startup]);
\r
942 int native_set_type(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 /* It makes no sense to try to reset the service type. */
\r
947 if (! value || ! value->string) {
\r
948 print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);
\r
953 We can only manage services of type SERVICE_WIN32_OWN_PROCESS
\r
954 and SERVICE_INTERACTIVE_PROCESS.
\r
956 unsigned long type = SERVICE_WIN32_OWN_PROCESS;
\r
957 if (str_equiv(value->string, NSSM_INTERACTIVE_PROCESS)) type |= SERVICE_INTERACTIVE_PROCESS;
\r
958 else if (! str_equiv(value->string, NSSM_WIN32_OWN_PROCESS)) {
\r
959 print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE_TYPE, value->string);
\r
960 _ftprintf(stderr, _T("%s\n"), NSSM_WIN32_OWN_PROCESS);
\r
961 _ftprintf(stderr, _T("%s\n"), NSSM_INTERACTIVE_PROCESS);
\r
966 ChangeServiceConfig() will fail if the service runs under an account
\r
967 other than LOCALSYSTEM and we try to make it interactive.
\r
969 if (type & SERVICE_INTERACTIVE_PROCESS) {
\r
970 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
971 if (! qsc) return -1;
\r
973 if (! str_equiv(qsc->lpServiceStartName, NSSM_LOCALSYSTEM_ACCOUNT)) {
\r
974 HeapFree(GetProcessHeap(), 0, qsc);
\r
975 print_message(stderr, NSSM_MESSAGE_INTERACTIVE_NOT_LOCALSYSTEM, value->string, service_name, NSSM_LOCALSYSTEM_ACCOUNT);
\r
979 HeapFree(GetProcessHeap(), 0, qsc);
\r
982 if (! ChangeServiceConfig(service_handle, type, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) {
\r
983 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
990 int native_get_type(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
991 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
992 if (! service_handle) return -1;
\r
994 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
995 if (! qsc) return -1;
\r
997 value->numeric = qsc->dwServiceType;
\r
998 HeapFree(GetProcessHeap(), 0, qsc);
\r
1000 const TCHAR *string;
\r
1001 switch (value->numeric) {
\r
1002 case SERVICE_KERNEL_DRIVER: string = NSSM_KERNEL_DRIVER; break;
\r
1003 case SERVICE_FILE_SYSTEM_DRIVER: string = NSSM_FILE_SYSTEM_DRIVER; break;
\r
1004 case SERVICE_WIN32_OWN_PROCESS: string = NSSM_WIN32_OWN_PROCESS; break;
\r
1005 case SERVICE_WIN32_SHARE_PROCESS: string = NSSM_WIN32_SHARE_PROCESS; break;
\r
1006 case SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS: string = NSSM_INTERACTIVE_PROCESS; break;
\r
1007 case SERVICE_WIN32_SHARE_PROCESS|SERVICE_INTERACTIVE_PROCESS: string = NSSM_SHARE_INTERACTIVE_PROCESS; break;
\r
1008 default: string = NSSM_UNKNOWN;
\r
1011 return value_from_string(name, value, string);
\r
1014 int set_setting(const TCHAR *service_name, HKEY key, settings_t *setting, value_t *value, const TCHAR *additional) {
\r
1015 if (! key) return -1;
\r
1018 if (setting->set) ret = setting->set(service_name, (void *) key, setting->name, setting->default_value, value, additional);
\r
1021 if (! ret) print_message(stdout, NSSM_MESSAGE_RESET_SETTING, setting->name, service_name);
\r
1022 else if (ret > 0) print_message(stdout, NSSM_MESSAGE_SET_SETTING, setting->name, service_name);
\r
1023 else print_message(stderr, NSSM_MESSAGE_SET_SETTING_FAILED, setting->name, service_name);
\r
1028 int set_setting(const TCHAR *service_name, SC_HANDLE service_handle, settings_t *setting, value_t *value, const TCHAR *additional) {
\r
1029 if (! service_handle) return -1;
\r
1032 if (setting->set) ret = setting->set(service_name, service_handle, setting->name, setting->default_value, value, additional);
\r
1035 if (! ret) print_message(stdout, NSSM_MESSAGE_RESET_SETTING, setting->name, service_name);
\r
1036 else if (ret > 0) print_message(stdout, NSSM_MESSAGE_SET_SETTING, setting->name, service_name);
\r
1037 else print_message(stderr, NSSM_MESSAGE_SET_SETTING_FAILED, setting->name, service_name);
\r
1043 Returns: 1 if the value was retrieved.
\r
1044 0 if the default value was retrieved.
\r
1047 int get_setting(const TCHAR *service_name, HKEY key, settings_t *setting, value_t *value, const TCHAR *additional) {
\r
1048 if (! key) return -1;
\r
1051 switch (setting->type) {
\r
1052 case REG_EXPAND_SZ:
\r
1053 case REG_MULTI_SZ:
\r
1055 value->string = (TCHAR *) setting->default_value;
\r
1056 if (setting->get) ret = setting->get(service_name, (void *) key, setting->name, setting->default_value, value, additional);
\r
1061 value->numeric = PtrToUlong(setting->default_value);
\r
1062 if (setting->get) ret = setting->get(service_name, (void *) key, setting->name, setting->default_value, value, additional);
\r
1071 if (ret < 0) print_message(stderr, NSSM_MESSAGE_GET_SETTING_FAILED, setting->name, service_name);
\r
1076 int get_setting(const TCHAR *service_name, SC_HANDLE service_handle, settings_t *setting, value_t *value, const TCHAR *additional) {
\r
1077 if (! service_handle) return -1;
\r
1078 return setting->get(service_name, service_handle, setting->name, 0, value, additional);
\r
1081 settings_t settings[] = {
\r
1082 { NSSM_REG_EXE, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },
\r
1083 { NSSM_REG_FLAGS, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },
\r
1084 { NSSM_REG_DIR, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },
\r
1085 { NSSM_REG_EXIT, REG_SZ, (void *) exit_action_strings[NSSM_EXIT_RESTART], false, ADDITIONAL_MANDATORY, setting_set_exit_action, setting_get_exit_action },
\r
1086 { NSSM_REG_HOOK, REG_SZ, (void *) _T(""), false, ADDITIONAL_MANDATORY, setting_set_hook, setting_get_hook },
\r
1087 { NSSM_REG_AFFINITY, REG_SZ, 0, false, 0, setting_set_affinity, setting_get_affinity },
\r
1088 { NSSM_REG_ENV, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment },
\r
1089 { NSSM_REG_ENV_EXTRA, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment },
\r
1090 { NSSM_REG_NO_CONSOLE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1091 { NSSM_REG_PRIORITY, REG_SZ, (void *) priority_strings[NSSM_NORMAL_PRIORITY], false, 0, setting_set_priority, setting_get_priority },
\r
1092 { NSSM_REG_RESTART_DELAY, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1093 { NSSM_REG_STDIN, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },
\r
1094 { NSSM_REG_STDIN NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDIN_SHARING, false, 0, setting_set_number, setting_get_number },
\r
1095 { NSSM_REG_STDIN NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDIN_DISPOSITION, false, 0, setting_set_number, setting_get_number },
\r
1096 { NSSM_REG_STDIN NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDIN_FLAGS, false, 0, setting_set_number, setting_get_number },
\r
1097 { NSSM_REG_STDOUT, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },
\r
1098 { NSSM_REG_STDOUT NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDOUT_SHARING, false, 0, setting_set_number, setting_get_number },
\r
1099 { NSSM_REG_STDOUT NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDOUT_DISPOSITION, false, 0, setting_set_number, setting_get_number },
\r
1100 { NSSM_REG_STDOUT NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDOUT_FLAGS, false, 0, setting_set_number, setting_get_number },
\r
1101 { NSSM_REG_STDOUT NSSM_REG_STDIO_COPY_AND_TRUNCATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1102 { NSSM_REG_STDERR, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },
\r
1103 { NSSM_REG_STDERR NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDERR_SHARING, false, 0, setting_set_number, setting_get_number },
\r
1104 { NSSM_REG_STDERR NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDERR_DISPOSITION, false, 0, setting_set_number, setting_get_number },
\r
1105 { NSSM_REG_STDERR NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDERR_FLAGS, false, 0, setting_set_number, setting_get_number },
\r
1106 { NSSM_REG_STDERR NSSM_REG_STDIO_COPY_AND_TRUNCATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1107 { NSSM_REG_STOP_METHOD_SKIP, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1108 { NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_CONSOLE_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },
\r
1109 { NSSM_REG_KILL_WINDOW_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_WINDOW_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },
\r
1110 { NSSM_REG_KILL_THREADS_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_THREADS_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },
\r
1111 { NSSM_REG_KILL_PROCESS_TREE, REG_DWORD, (void *) 1, false, 0, setting_set_number, setting_get_number },
\r
1112 { NSSM_REG_THROTTLE, REG_DWORD, (void *) NSSM_RESET_THROTTLE_RESTART, false, 0, setting_set_number, setting_get_number },
\r
1113 { NSSM_REG_ROTATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1114 { NSSM_REG_ROTATE_ONLINE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1115 { NSSM_REG_ROTATE_SECONDS, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1116 { NSSM_REG_ROTATE_BYTES_LOW, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1117 { NSSM_REG_ROTATE_BYTES_HIGH, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1118 { NSSM_REG_ROTATE_DELAY, REG_DWORD, (void *) NSSM_ROTATE_DELAY, false, 0, setting_set_number, setting_get_number },
\r
1119 { NSSM_NATIVE_DEPENDONGROUP, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_dependongroup, native_get_dependongroup },
\r
1120 { NSSM_NATIVE_DEPENDONSERVICE, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_dependonservice, native_get_dependonservice },
\r
1121 { NSSM_NATIVE_DESCRIPTION, REG_SZ, _T(""), true, 0, native_set_description, native_get_description },
\r
1122 { NSSM_NATIVE_DISPLAYNAME, REG_SZ, NULL, true, 0, native_set_displayname, native_get_displayname },
\r
1123 { NSSM_NATIVE_ENVIRONMENT, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_environment, native_get_environment },
\r
1124 { NSSM_NATIVE_IMAGEPATH, REG_EXPAND_SZ, NULL, true, 0, native_set_imagepath, native_get_imagepath },
\r
1125 { NSSM_NATIVE_OBJECTNAME, REG_SZ, NSSM_LOCALSYSTEM_ACCOUNT, true, 0, native_set_objectname, native_get_objectname },
\r
1126 { NSSM_NATIVE_NAME, REG_SZ, NULL, true, 0, native_set_name, native_get_name },
\r
1127 { NSSM_NATIVE_STARTUP, REG_SZ, NULL, true, 0, native_set_startup, native_get_startup },
\r
1128 { NSSM_NATIVE_TYPE, REG_SZ, NULL, true, 0, native_set_type, native_get_type },
\r
1129 { NULL, NULL, NULL, NULL, NULL }
\r