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
456 if (value_from_string(name, value, (const TCHAR *) default_value) == -1) return -1;
\r
458 case -1: return -1;
\r
461 return value_from_string(name, value, priority_strings[priority_constant_to_index(constant)]);
\r
464 /* Functions to manage native service settings. */
\r
465 static int native_set_dependongroup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
466 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
467 if (! service_handle) return -1;
\r
470 Get existing service dependencies because we must set both types together.
\r
473 unsigned long buflen;
\r
474 if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_SERVICES)) return -1;
\r
476 if (! value || ! value->string || ! value->string[0]) {
\r
477 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, buffer, 0, 0, 0)) {
\r
478 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
479 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
\r
483 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
\r
487 unsigned long len = (unsigned long) _tcslen(value->string) + 1;
\r
488 TCHAR *unformatted = 0;
\r
489 unsigned long newlen;
\r
490 if (unformat_double_null(value->string, len, &unformatted, &newlen)) {
\r
491 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
\r
495 /* Prepend group identifier. */
\r
496 unsigned long missing = 0;
\r
497 TCHAR *canon = unformatted;
\r
498 size_t canonlen = 0;
\r
500 for (s = unformatted; *s; s++) {
\r
501 if (*s != SC_GROUP_IDENTIFIER) missing++;
\r
502 size_t len = _tcslen(s);
\r
503 canonlen += len + 1;
\r
508 /* Missing identifiers plus double NULL terminator. */
\r
509 canonlen += missing + 1;
\r
510 newlen = (unsigned long) canonlen;
\r
512 canon = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, canonlen * sizeof(TCHAR));
\r
514 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canon"), _T("native_set_dependongroup"));
\r
515 if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);
\r
516 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
\r
521 for (s = unformatted; *s; s++) {
\r
522 if (*s != SC_GROUP_IDENTIFIER) canon[i++] = SC_GROUP_IDENTIFIER;
\r
523 size_t len = _tcslen(s);
\r
524 memmove(canon + i, s, (len + 1) * sizeof(TCHAR));
\r
530 TCHAR *dependencies;
\r
532 dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (newlen + buflen) * sizeof(TCHAR));
\r
533 if (! dependencies) {
\r
534 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("dependencies"), _T("native_set_dependongroup"));
\r
535 if (canon != unformatted) HeapFree(GetProcessHeap(), 0, canon);
\r
536 if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);
\r
537 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
\r
541 memmove(dependencies, buffer, buflen * sizeof(TCHAR));
\r
542 memmove(dependencies + buflen - 1, canon, newlen * sizeof(TCHAR));
\r
544 else dependencies = canon;
\r
547 if (set_service_dependencies(service_name, service_handle, dependencies)) ret = -1;
\r
548 if (dependencies != unformatted) HeapFree(GetProcessHeap(), 0, dependencies);
\r
549 if (canon != unformatted) HeapFree(GetProcessHeap(), 0, canon);
\r
550 if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);
\r
551 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
\r
556 static int native_get_dependongroup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
557 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
558 if (! service_handle) return -1;
\r
561 unsigned long buflen;
\r
562 if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_GROUPS)) return -1;
\r
567 unsigned long newlen;
\r
568 if (format_double_null(buffer, buflen, &formatted, &newlen)) {
\r
569 HeapFree(GetProcessHeap(), 0, buffer);
\r
573 ret = value_from_string(name, value, formatted);
\r
574 HeapFree(GetProcessHeap(), 0, formatted);
\r
575 HeapFree(GetProcessHeap(), 0, buffer);
\r
585 static int native_set_dependonservice(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
586 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
587 if (! service_handle) return -1;
\r
590 Get existing group dependencies because we must set both types together.
\r
593 unsigned long buflen;
\r
594 if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_GROUPS)) return -1;
\r
596 if (! value || ! value->string || ! value->string[0]) {
\r
597 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, buffer, 0, 0, 0)) {
\r
598 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
599 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
\r
603 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
\r
607 unsigned long len = (unsigned long) _tcslen(value->string) + 1;
\r
608 TCHAR *unformatted = 0;
\r
609 unsigned long newlen;
\r
610 if (unformat_double_null(value->string, len, &unformatted, &newlen)) {
\r
611 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
\r
615 TCHAR *dependencies;
\r
617 dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (newlen + buflen) * sizeof(TCHAR));
\r
618 if (! dependencies) {
\r
619 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("dependencies"), _T("native_set_dependonservice"));
\r
620 if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);
\r
621 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
\r
625 memmove(dependencies, buffer, buflen * sizeof(TCHAR));
\r
626 memmove(dependencies + buflen - 1, unformatted, newlen * sizeof(TCHAR));
\r
628 else dependencies = unformatted;
\r
631 if (set_service_dependencies(service_name, service_handle, dependencies)) ret = -1;
\r
632 if (dependencies != unformatted) HeapFree(GetProcessHeap(), 0, dependencies);
\r
633 if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);
\r
634 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
\r
639 static int native_get_dependonservice(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
640 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
641 if (! service_handle) return -1;
\r
644 unsigned long buflen;
\r
645 if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_SERVICES)) return -1;
\r
650 unsigned long newlen;
\r
651 if (format_double_null(buffer, buflen, &formatted, &newlen)) {
\r
652 HeapFree(GetProcessHeap(), 0, buffer);
\r
656 ret = value_from_string(name, value, formatted);
\r
657 HeapFree(GetProcessHeap(), 0, formatted);
\r
658 HeapFree(GetProcessHeap(), 0, buffer);
\r
668 int native_set_description(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
669 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
670 if (! service_handle) return -1;
\r
672 TCHAR *description = 0;
\r
673 if (value) description = value->string;
\r
674 if (set_service_description(service_name, service_handle, description)) return -1;
\r
676 if (description && description[0]) return 1;
\r
681 int native_get_description(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
682 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
683 if (! service_handle) return -1;
\r
685 TCHAR buffer[VALUE_LENGTH];
\r
686 if (get_service_description(service_name, service_handle, _countof(buffer), buffer)) return -1;
\r
688 if (buffer[0]) return value_from_string(name, value, buffer);
\r
694 int native_set_displayname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
695 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
696 if (! service_handle) return -1;
\r
698 TCHAR *displayname = 0;
\r
699 if (value && value->string) displayname = value->string;
\r
700 else displayname = (TCHAR *) service_name;
\r
702 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, displayname)) {
\r
703 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
708 If the display name and service name differ only in case,
\r
709 ChangeServiceConfig() will return success but the display name will be
\r
710 set to the service name, NOT the value passed to the function.
\r
711 This appears to be a quirk of Windows rather than a bug here.
\r
713 if (displayname != service_name && ! str_equiv(displayname, service_name)) return 1;
\r
718 int native_get_displayname(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 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
723 if (! qsc) return -1;
\r
725 int ret = value_from_string(name, value, qsc->lpDisplayName);
\r
726 HeapFree(GetProcessHeap(), 0, qsc);
\r
731 int native_set_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
732 HKEY key = open_service_registry(service_name, KEY_SET_VALUE, true);
\r
733 if (! key) return -1;
\r
735 int ret = setting_set_environment(service_name, (void *) key, name, default_value, value, additional);
\r
740 int native_get_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
741 HKEY key = open_service_registry(service_name, KEY_READ, true);
\r
742 if (! key) return -1;
\r
744 ZeroMemory(value, sizeof(value_t));
\r
745 int ret = setting_get_environment(service_name, (void *) key, name, default_value, value, additional);
\r
750 int native_set_imagepath(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 /* It makes no sense to try to reset the image path. */
\r
755 if (! value || ! value->string) {
\r
756 print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);
\r
760 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, value->string, 0, 0, 0, 0, 0, 0)) {
\r
761 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
768 int native_get_imagepath(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
769 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
770 if (! service_handle) return -1;
\r
772 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
773 if (! qsc) return -1;
\r
775 int ret = value_from_string(name, value, qsc->lpBinaryPathName);
\r
776 HeapFree(GetProcessHeap(), 0, qsc);
\r
781 int native_set_name(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
782 print_message(stderr, NSSM_MESSAGE_CANNOT_RENAME_SERVICE);
\r
786 int native_get_name(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
787 return value_from_string(name, value, service_name);
\r
790 int native_set_objectname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
791 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
792 if (! service_handle) return -1;
\r
795 Logical syntax is: nssm set <service> ObjectName <username> <password>
\r
796 That means the username is actually passed in the additional parameter.
\r
798 bool localsystem = false;
\r
799 TCHAR *username = NSSM_LOCALSYSTEM_ACCOUNT;
\r
800 TCHAR *password = 0;
\r
802 username = (TCHAR *) additional;
\r
803 if (value && value->string) password = value->string;
\r
805 else if (value && value->string) username = value->string;
\r
807 const TCHAR *well_known = well_known_username(username);
\r
808 size_t passwordsize = 0;
\r
810 if (str_equiv(well_known, NSSM_LOCALSYSTEM_ACCOUNT)) localsystem = true;
\r
811 username = (TCHAR *) well_known;
\r
814 else if (! password) {
\r
815 /* We need a password if the account requires it. */
\r
816 print_message(stderr, NSSM_MESSAGE_MISSING_PASSWORD, name);
\r
819 else passwordsize = _tcslen(password) * sizeof(TCHAR);
\r
822 ChangeServiceConfig() will fail to set the username if the service is set
\r
823 to interact with the desktop.
\r
825 unsigned long type = SERVICE_NO_CHANGE;
\r
826 if (! localsystem) {
\r
827 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
829 if (passwordsize) SecureZeroMemory(password, passwordsize);
\r
833 type = qsc->dwServiceType & ~SERVICE_INTERACTIVE_PROCESS;
\r
834 HeapFree(GetProcessHeap(), 0, qsc);
\r
837 if (! well_known) {
\r
838 if (grant_logon_as_service(username)) {
\r
839 if (passwordsize) SecureZeroMemory(password, passwordsize);
\r
840 print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);
\r
845 if (! ChangeServiceConfig(service_handle, type, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, username, password, 0)) {
\r
846 if (passwordsize) SecureZeroMemory(password, passwordsize);
\r
847 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
851 if (passwordsize) SecureZeroMemory(password, passwordsize);
\r
853 if (localsystem) return 0;
\r
858 int native_get_objectname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
859 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
860 if (! service_handle) return -1;
\r
862 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
863 if (! qsc) return -1;
\r
865 int ret = value_from_string(name, value, qsc->lpServiceStartName);
\r
866 HeapFree(GetProcessHeap(), 0, qsc);
\r
871 int native_set_startup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
872 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
873 if (! service_handle) return -1;
\r
875 /* It makes no sense to try to reset the startup type. */
\r
876 if (! value || ! value->string) {
\r
877 print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);
\r
881 /* Map NSSM_STARTUP_* constant to Windows SERVICE_*_START constant. */
\r
882 int service_startup = -1;
\r
884 for (i = 0; startup_strings[i]; i++) {
\r
885 if (str_equiv(value->string, startup_strings[i])) {
\r
886 service_startup = i;
\r
891 if (service_startup < 0) {
\r
892 print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE_STARTUP, value->string);
\r
893 for (i = 0; startup_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), startup_strings[i]);
\r
897 unsigned long startup;
\r
898 switch (service_startup) {
\r
899 case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;
\r
900 case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;
\r
901 default: startup = SERVICE_AUTO_START;
\r
904 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, startup, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) {
\r
905 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
909 SERVICE_DELAYED_AUTO_START_INFO delayed;
\r
910 ZeroMemory(&delayed, sizeof(delayed));
\r
911 if (service_startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;
\r
912 else delayed.fDelayedAutostart = 0;
\r
913 if (! ChangeServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {
\r
914 unsigned long error = GetLastError();
\r
915 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
916 if (error != ERROR_INVALID_LEVEL) {
\r
917 log_event(EVENTLOG_ERROR_TYPE, NSSM_MESSAGE_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service_name, error_string(error), 0);
\r
924 int native_get_startup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
925 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
926 if (! service_handle) return -1;
\r
928 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
929 if (! qsc) return -1;
\r
931 unsigned long startup;
\r
932 int ret = get_service_startup(service_name, service_handle, qsc, &startup);
\r
933 HeapFree(GetProcessHeap(), 0, qsc);
\r
935 if (ret) return -1;
\r
938 for (i = 0; startup_strings[i]; i++);
\r
939 if (startup >= i) return -1;
\r
941 return value_from_string(name, value, startup_strings[startup]);
\r
944 int native_set_type(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
945 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
946 if (! service_handle) return -1;
\r
948 /* It makes no sense to try to reset the service type. */
\r
949 if (! value || ! value->string) {
\r
950 print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);
\r
955 We can only manage services of type SERVICE_WIN32_OWN_PROCESS
\r
956 and SERVICE_INTERACTIVE_PROCESS.
\r
958 unsigned long type = SERVICE_WIN32_OWN_PROCESS;
\r
959 if (str_equiv(value->string, NSSM_INTERACTIVE_PROCESS)) type |= SERVICE_INTERACTIVE_PROCESS;
\r
960 else if (! str_equiv(value->string, NSSM_WIN32_OWN_PROCESS)) {
\r
961 print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE_TYPE, value->string);
\r
962 _ftprintf(stderr, _T("%s\n"), NSSM_WIN32_OWN_PROCESS);
\r
963 _ftprintf(stderr, _T("%s\n"), NSSM_INTERACTIVE_PROCESS);
\r
968 ChangeServiceConfig() will fail if the service runs under an account
\r
969 other than LOCALSYSTEM and we try to make it interactive.
\r
971 if (type & SERVICE_INTERACTIVE_PROCESS) {
\r
972 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
973 if (! qsc) return -1;
\r
975 if (! str_equiv(qsc->lpServiceStartName, NSSM_LOCALSYSTEM_ACCOUNT)) {
\r
976 HeapFree(GetProcessHeap(), 0, qsc);
\r
977 print_message(stderr, NSSM_MESSAGE_INTERACTIVE_NOT_LOCALSYSTEM, value->string, service_name, NSSM_LOCALSYSTEM_ACCOUNT);
\r
981 HeapFree(GetProcessHeap(), 0, qsc);
\r
984 if (! ChangeServiceConfig(service_handle, type, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) {
\r
985 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
992 int native_get_type(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
993 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
994 if (! service_handle) return -1;
\r
996 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
997 if (! qsc) return -1;
\r
999 value->numeric = qsc->dwServiceType;
\r
1000 HeapFree(GetProcessHeap(), 0, qsc);
\r
1002 const TCHAR *string;
\r
1003 switch (value->numeric) {
\r
1004 case SERVICE_KERNEL_DRIVER: string = NSSM_KERNEL_DRIVER; break;
\r
1005 case SERVICE_FILE_SYSTEM_DRIVER: string = NSSM_FILE_SYSTEM_DRIVER; break;
\r
1006 case SERVICE_WIN32_OWN_PROCESS: string = NSSM_WIN32_OWN_PROCESS; break;
\r
1007 case SERVICE_WIN32_SHARE_PROCESS: string = NSSM_WIN32_SHARE_PROCESS; break;
\r
1008 case SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS: string = NSSM_INTERACTIVE_PROCESS; break;
\r
1009 case SERVICE_WIN32_SHARE_PROCESS|SERVICE_INTERACTIVE_PROCESS: string = NSSM_SHARE_INTERACTIVE_PROCESS; break;
\r
1010 default: string = NSSM_UNKNOWN;
\r
1013 return value_from_string(name, value, string);
\r
1016 int set_setting(const TCHAR *service_name, HKEY key, settings_t *setting, value_t *value, const TCHAR *additional) {
\r
1017 if (! key) return -1;
\r
1020 if (setting->set) ret = setting->set(service_name, (void *) key, setting->name, setting->default_value, value, additional);
\r
1023 if (! ret) print_message(stdout, NSSM_MESSAGE_RESET_SETTING, setting->name, service_name);
\r
1024 else if (ret > 0) print_message(stdout, NSSM_MESSAGE_SET_SETTING, setting->name, service_name);
\r
1025 else print_message(stderr, NSSM_MESSAGE_SET_SETTING_FAILED, setting->name, service_name);
\r
1030 int set_setting(const TCHAR *service_name, SC_HANDLE service_handle, settings_t *setting, value_t *value, const TCHAR *additional) {
\r
1031 if (! service_handle) return -1;
\r
1034 if (setting->set) ret = setting->set(service_name, service_handle, setting->name, setting->default_value, value, additional);
\r
1037 if (! ret) print_message(stdout, NSSM_MESSAGE_RESET_SETTING, setting->name, service_name);
\r
1038 else if (ret > 0) print_message(stdout, NSSM_MESSAGE_SET_SETTING, setting->name, service_name);
\r
1039 else print_message(stderr, NSSM_MESSAGE_SET_SETTING_FAILED, setting->name, service_name);
\r
1045 Returns: 1 if the value was retrieved.
\r
1046 0 if the default value was retrieved.
\r
1049 int get_setting(const TCHAR *service_name, HKEY key, settings_t *setting, value_t *value, const TCHAR *additional) {
\r
1050 if (! key) return -1;
\r
1053 switch (setting->type) {
\r
1054 case REG_EXPAND_SZ:
\r
1055 case REG_MULTI_SZ:
\r
1057 value->string = (TCHAR *) setting->default_value;
\r
1058 if (setting->get) ret = setting->get(service_name, (void *) key, setting->name, setting->default_value, value, additional);
\r
1063 value->numeric = PtrToUlong(setting->default_value);
\r
1064 if (setting->get) ret = setting->get(service_name, (void *) key, setting->name, setting->default_value, value, additional);
\r
1073 if (ret < 0) print_message(stderr, NSSM_MESSAGE_GET_SETTING_FAILED, setting->name, service_name);
\r
1078 int get_setting(const TCHAR *service_name, SC_HANDLE service_handle, settings_t *setting, value_t *value, const TCHAR *additional) {
\r
1079 if (! service_handle) return -1;
\r
1080 return setting->get(service_name, service_handle, setting->name, 0, value, additional);
\r
1083 settings_t settings[] = {
\r
1084 { NSSM_REG_EXE, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },
\r
1085 { NSSM_REG_FLAGS, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },
\r
1086 { NSSM_REG_DIR, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },
\r
1087 { NSSM_REG_EXIT, REG_SZ, (void *) exit_action_strings[NSSM_EXIT_RESTART], false, ADDITIONAL_MANDATORY, setting_set_exit_action, setting_get_exit_action },
\r
1088 { NSSM_REG_HOOK, REG_SZ, (void *) _T(""), false, ADDITIONAL_MANDATORY, setting_set_hook, setting_get_hook },
\r
1089 { NSSM_REG_AFFINITY, REG_SZ, 0, false, 0, setting_set_affinity, setting_get_affinity },
\r
1090 { NSSM_REG_ENV, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment },
\r
1091 { NSSM_REG_ENV_EXTRA, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment },
\r
1092 { NSSM_REG_NO_CONSOLE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1093 { NSSM_REG_PRIORITY, REG_SZ, (void *) priority_strings[NSSM_NORMAL_PRIORITY], false, 0, setting_set_priority, setting_get_priority },
\r
1094 { NSSM_REG_RESTART_DELAY, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1095 { NSSM_REG_STDIN, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },
\r
1096 { NSSM_REG_STDIN NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDIN_SHARING, false, 0, setting_set_number, setting_get_number },
\r
1097 { NSSM_REG_STDIN NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDIN_DISPOSITION, false, 0, setting_set_number, setting_get_number },
\r
1098 { NSSM_REG_STDIN NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDIN_FLAGS, false, 0, setting_set_number, setting_get_number },
\r
1099 { NSSM_REG_STDOUT, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },
\r
1100 { NSSM_REG_STDOUT NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDOUT_SHARING, false, 0, setting_set_number, setting_get_number },
\r
1101 { NSSM_REG_STDOUT NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDOUT_DISPOSITION, false, 0, setting_set_number, setting_get_number },
\r
1102 { NSSM_REG_STDOUT NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDOUT_FLAGS, false, 0, setting_set_number, setting_get_number },
\r
1103 { NSSM_REG_STDOUT NSSM_REG_STDIO_COPY_AND_TRUNCATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1104 { NSSM_REG_STDERR, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },
\r
1105 { NSSM_REG_STDERR NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDERR_SHARING, false, 0, setting_set_number, setting_get_number },
\r
1106 { NSSM_REG_STDERR NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDERR_DISPOSITION, false, 0, setting_set_number, setting_get_number },
\r
1107 { NSSM_REG_STDERR NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDERR_FLAGS, false, 0, setting_set_number, setting_get_number },
\r
1108 { NSSM_REG_STDERR NSSM_REG_STDIO_COPY_AND_TRUNCATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1109 { NSSM_REG_STOP_METHOD_SKIP, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1110 { NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_CONSOLE_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },
\r
1111 { NSSM_REG_KILL_WINDOW_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_WINDOW_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },
\r
1112 { NSSM_REG_KILL_THREADS_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_THREADS_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },
\r
1113 { NSSM_REG_KILL_PROCESS_TREE, REG_DWORD, (void *) 1, false, 0, setting_set_number, setting_get_number },
\r
1114 { NSSM_REG_THROTTLE, REG_DWORD, (void *) NSSM_RESET_THROTTLE_RESTART, false, 0, setting_set_number, setting_get_number },
\r
1115 { NSSM_REG_HOOK_SHARE_OUTPUT_HANDLES, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1116 { NSSM_REG_ROTATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1117 { NSSM_REG_ROTATE_ONLINE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1118 { NSSM_REG_ROTATE_SECONDS, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1119 { NSSM_REG_ROTATE_BYTES_LOW, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1120 { NSSM_REG_ROTATE_BYTES_HIGH, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
\r
1121 { NSSM_REG_ROTATE_DELAY, REG_DWORD, (void *) NSSM_ROTATE_DELAY, false, 0, setting_set_number, setting_get_number },
\r
1122 { NSSM_NATIVE_DEPENDONGROUP, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_dependongroup, native_get_dependongroup },
\r
1123 { NSSM_NATIVE_DEPENDONSERVICE, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_dependonservice, native_get_dependonservice },
\r
1124 { NSSM_NATIVE_DESCRIPTION, REG_SZ, _T(""), true, 0, native_set_description, native_get_description },
\r
1125 { NSSM_NATIVE_DISPLAYNAME, REG_SZ, NULL, true, 0, native_set_displayname, native_get_displayname },
\r
1126 { NSSM_NATIVE_ENVIRONMENT, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_environment, native_get_environment },
\r
1127 { NSSM_NATIVE_IMAGEPATH, REG_EXPAND_SZ, NULL, true, 0, native_set_imagepath, native_get_imagepath },
\r
1128 { NSSM_NATIVE_OBJECTNAME, REG_SZ, NSSM_LOCALSYSTEM_ACCOUNT, true, 0, native_set_objectname, native_get_objectname },
\r
1129 { NSSM_NATIVE_NAME, REG_SZ, NULL, true, 0, native_set_name, native_get_name },
\r
1130 { NSSM_NATIVE_STARTUP, REG_SZ, NULL, true, 0, native_set_startup, native_get_startup },
\r
1131 { NSSM_NATIVE_TYPE, REG_SZ, NULL, true, 0, native_set_type, native_get_type },
\r
1132 { NULL, NULL, NULL, NULL, NULL }
\r