2 /* XXX: (value && value->string) is probably bogus because value is probably never null */
5 #define NSSM_AFFINITY_ALL _T("All")
7 extern const TCHAR *exit_action_strings[];
8 extern const TCHAR *startup_strings[];
9 extern const TCHAR *priority_strings[];
11 /* Does the parameter refer to the default value of the setting? */
12 static inline int is_default(const TCHAR *value) {
13 return (str_equiv(value, _T("default")) || str_equiv(value, _T("*")) || ! value[0]);
16 static int value_from_string(const TCHAR *name, value_t *value, const TCHAR *string) {
17 size_t len = _tcslen(string);
23 value->string = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));
24 if (! value->string) {
25 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, name, _T("value_from_string()"));
29 if (_sntprintf_s(value->string, len, _TRUNCATE, _T("%s"), string) < 0) {
30 HeapFree(GetProcessHeap(), 0, value->string);
31 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, name, _T("value_from_string()"));
38 /* Functions to manage NSSM-specific settings in the registry. */
39 static int setting_set_number(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
40 HKEY key = (HKEY) param;
46 /* Resetting to default? */
47 if (! value || ! value->string) {
48 error = RegDeleteValue(key, name);
49 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
50 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
53 if (str_number(value->string, &number)) return -1;
55 if (default_value && number == (unsigned long) default_value) {
56 error = RegDeleteValue(key, name);
57 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
58 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
62 if (set_number(key, (TCHAR *) name, number)) return -1;
67 static int setting_get_number(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
68 HKEY key = (HKEY) param;
69 return get_number(key, (TCHAR *) name, &value->numeric, false);
72 static int setting_set_string(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
73 HKEY key = (HKEY) param;
78 /* Resetting to default? */
79 if (! value || ! value->string) {
80 if (default_value) value->string = (TCHAR *) default_value;
82 error = RegDeleteValue(key, name);
83 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
84 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
88 if (default_value && _tcslen((TCHAR *) default_value) && str_equiv(value->string, (TCHAR *) default_value)) {
89 error = RegDeleteValue(key, name);
90 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
91 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
95 if (set_expand_string(key, (TCHAR *) name, value->string)) return -1;
100 static int setting_get_string(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
101 HKEY key = (HKEY) param;
102 TCHAR buffer[VALUE_LENGTH];
104 if (get_string(key, (TCHAR *) name, (TCHAR *) buffer, (unsigned long) sizeof(buffer), false, false, false)) return -1;
106 return value_from_string(name, value, buffer);
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) {
110 unsigned long exitcode;
112 TCHAR action_string[ACTION_LEN];
115 /* Default action? */
116 if (is_default(additional)) code = 0;
118 if (str_number(additional, &exitcode)) return -1;
119 code = (TCHAR *) additional;
123 HKEY key = open_registry(service_name, name, KEY_WRITE);
124 if (! key) return -1;
129 /* Resetting to default? */
130 if (value && value->string) _sntprintf_s(action_string, _countof(action_string), _TRUNCATE, _T("%s"), value->string);
133 /* Delete explicit action. */
134 error = RegDeleteValue(key, code);
136 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
137 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, code, service_name, error_string(error));
141 /* Explicitly keep the default action. */
142 if (default_value) _sntprintf_s(action_string, _countof(action_string), _TRUNCATE, _T("%s"), (TCHAR *) default_value);
147 /* Validate the string. */
148 for (int i = 0; exit_action_strings[i]; i++) {
149 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
150 if (default_value && str_equiv(action_string, (TCHAR *) default_value)) ret = 0;
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) {
152 print_message(stderr, NSSM_MESSAGE_SETVALUE_FAILED, code, service_name, error_string(GetLastError()));
162 print_message(stderr, NSSM_MESSAGE_INVALID_EXIT_ACTION, action_string);
163 for (int i = 0; exit_action_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), exit_action_strings[i]);
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) {
169 unsigned long exitcode = 0;
170 unsigned long *code = 0;
173 if (! is_default(additional)) {
174 if (str_number(additional, &exitcode)) return -1;
179 TCHAR action_string[ACTION_LEN];
181 if (get_exit_action(service_name, code, action_string, &default_action)) return -1;
183 value_from_string(name, value, action_string);
185 if (default_action && ! _tcsnicmp((const TCHAR *) action_string, (TCHAR *) default_value, ACTION_LEN)) return 0;
189 static inline bool split_hook_name(const TCHAR *hook_name, TCHAR *hook_event, TCHAR *hook_action) {
192 for (s = (TCHAR *) hook_name; *s; s++) {
195 _sntprintf_s(hook_event, HOOK_NAME_LENGTH, _TRUNCATE, _T("%s"), hook_name);
196 _sntprintf_s(hook_action, HOOK_NAME_LENGTH, _TRUNCATE, _T("%s"), ++s);
197 return valid_hook_name(hook_event, hook_action, false);
201 print_message(stderr, NSSM_MESSAGE_INVALID_HOOK_NAME, hook_name);
205 static int setting_set_hook(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
206 TCHAR hook_event[HOOK_NAME_LENGTH];
207 TCHAR hook_action[HOOK_NAME_LENGTH];
208 if (! split_hook_name(additional, hook_event, hook_action)) return -1;
211 if (value && value->string) cmd = value->string;
214 if (set_hook(service_name, hook_event, hook_action, cmd)) return -1;
215 if (! _tcslen(cmd)) return 0;
219 static int setting_get_hook(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
220 TCHAR hook_event[HOOK_NAME_LENGTH];
221 TCHAR hook_action[HOOK_NAME_LENGTH];
222 if (! split_hook_name(additional, hook_event, hook_action)) return -1;
224 TCHAR cmd[CMD_LENGTH];
225 if (get_hook(service_name, hook_event, hook_action, cmd, sizeof(cmd))) return -1;
227 value_from_string(name, value, cmd);
229 if (! _tcslen(cmd)) return 0;
233 static int setting_set_affinity(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
234 HKEY key = (HKEY) param;
235 if (! key) return -1;
239 __int64 system_affinity = 0LL;
241 if (value && value->string) {
243 if (! GetProcessAffinityMask(GetCurrentProcess(), &affinity, (DWORD_PTR *) &system_affinity)) system_affinity = ~0;
245 if (is_default(value->string) || str_equiv(value->string, NSSM_AFFINITY_ALL)) mask = 0LL;
246 else if (affinity_string_to_mask(value->string, &mask)) {
247 print_message(stderr, NSSM_MESSAGE_BOGUS_AFFINITY_MASK, value->string, num_cpus() - 1);
254 error = RegDeleteValue(key, name);
255 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
256 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
262 if (affinity_mask_to_string(mask, &canon)) canon = value->string;
264 __int64 effective_affinity = mask & system_affinity;
265 if (effective_affinity != mask) {
266 /* Requested CPUs did not intersect with available CPUs? */
267 if (! effective_affinity) mask = effective_affinity = system_affinity;
270 if (! affinity_mask_to_string(system_affinity, &system)) {
271 TCHAR *effective = 0;
272 if (! affinity_mask_to_string(effective_affinity, &effective)) {
273 print_message(stderr, NSSM_MESSAGE_EFFECTIVE_AFFINITY_MASK, value->string, system, effective);
274 HeapFree(GetProcessHeap(), 0, effective);
276 HeapFree(GetProcessHeap(), 0, system);
280 if (RegSetValueEx(key, name, 0, REG_SZ, (const unsigned char *) canon, (unsigned long) (_tcslen(canon) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) {
281 if (canon != value->string) HeapFree(GetProcessHeap(), 0, canon);
282 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, name, error_string(GetLastError()), 0);
286 if (canon != value->string) HeapFree(GetProcessHeap(), 0, canon);
290 static int setting_get_affinity(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
291 HKEY key = (HKEY) param;
292 if (! key) return -1;
296 unsigned long buflen = 0;
298 int ret = RegQueryValueEx(key, name, 0, &type, 0, &buflen);
299 if (ret == ERROR_FILE_NOT_FOUND) {
300 if (value_from_string(name, value, NSSM_AFFINITY_ALL) == 1) return 0;
303 if (ret != ERROR_SUCCESS) return -1;
305 if (type != REG_SZ) return -1;
307 buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, buflen);
309 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("affinity"), _T("setting_get_affinity"));
313 if (get_string(key, (TCHAR *) name, buffer, buflen, false, false, true)) {
314 HeapFree(GetProcessHeap(), 0, buffer);
319 if (affinity_string_to_mask(buffer, &affinity)) {
320 print_message(stderr, NSSM_MESSAGE_BOGUS_AFFINITY_MASK, buffer, num_cpus() - 1);
321 HeapFree(GetProcessHeap(), 0, buffer);
325 HeapFree(GetProcessHeap(), 0, buffer);
328 if (affinity_mask_to_string(affinity, &buffer)) {
329 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
333 ret = value_from_string(name, value, buffer);
334 HeapFree(GetProcessHeap(), 0, buffer);
338 static int setting_set_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
339 HKEY key = (HKEY) param;
340 if (! param) return -1;
342 if (! value || ! value->string || ! value->string[0]) {
343 long error = RegDeleteValue(key, name);
344 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
345 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
349 unsigned long envlen = (unsigned long) _tcslen(value->string) + 1;
350 TCHAR *unformatted = 0;
351 unsigned long newlen;
352 if (unformat_double_null(value->string, envlen, &unformatted, &newlen)) return -1;
354 if (test_environment(unformatted)) {
355 HeapFree(GetProcessHeap(), 0, unformatted);
356 print_message(stderr, NSSM_GUI_INVALID_ENVIRONMENT);
360 if (RegSetValueEx(key, name, 0, REG_MULTI_SZ, (const unsigned char *) unformatted, (unsigned long) newlen * sizeof(TCHAR)) != ERROR_SUCCESS) {
361 if (newlen) HeapFree(GetProcessHeap(), 0, unformatted);
362 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV, error_string(GetLastError()), 0);
366 if (newlen) HeapFree(GetProcessHeap(), 0, unformatted);
370 static int setting_get_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
371 HKEY key = (HKEY) param;
372 if (! param) return -1;
375 unsigned long envlen;
376 if (get_environment((TCHAR *) service_name, key, (TCHAR *) name, &env, &envlen)) return -1;
377 if (! envlen) return 0;
380 unsigned long newlen;
381 if (format_double_null(env, envlen, &formatted, &newlen)) return -1;
385 /* Find named environment variable. */
387 size_t len = _tcslen(additional);
388 for (s = env; *s; s++) {
389 /* Look for <additional>=<string> NULL NULL */
390 if (! _tcsnicmp(s, additional, len) && s[len] == _T('=')) {
393 ret = value_from_string(name, value, s);
394 HeapFree(GetProcessHeap(), 0, env);
398 /* Skip this string. */
401 HeapFree(GetProcessHeap(), 0, env);
405 HeapFree(GetProcessHeap(), 0, env);
407 ret = value_from_string(name, value, formatted);
408 if (newlen) HeapFree(GetProcessHeap(), 0, formatted);
412 static int setting_set_priority(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
413 HKEY key = (HKEY) param;
414 if (! param) return -1;
416 TCHAR *priority_string;
420 if (value && value->string) priority_string = value->string;
421 else if (default_value) priority_string = (TCHAR *) default_value;
423 error = RegDeleteValue(key, name);
424 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
425 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
429 for (i = 0; priority_strings[i]; i++) {
430 if (! str_equiv(priority_strings[i], priority_string)) continue;
432 if (default_value && str_equiv(priority_string, (TCHAR *) default_value)) {
433 error = RegDeleteValue(key, name);
434 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
435 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
439 if (set_number(key, (TCHAR *) name, priority_index_to_constant(i))) return -1;
443 print_message(stderr, NSSM_MESSAGE_INVALID_PRIORITY, priority_string);
444 for (i = 0; priority_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), priority_strings[i]);
449 static int setting_get_priority(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
450 HKEY key = (HKEY) param;
451 if (! param) return -1;
453 unsigned long constant;
454 switch (get_number(key, (TCHAR *) name, &constant, false)) {
455 case 0: return value_from_string(name, value, (const TCHAR *) default_value);
459 return value_from_string(name, value, priority_strings[priority_constant_to_index(constant)]);
462 /* Functions to manage native service settings. */
463 static int native_set_dependongroup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
464 SC_HANDLE service_handle = (SC_HANDLE) param;
465 if (! service_handle) return -1;
468 Get existing service dependencies because we must set both types together.
471 unsigned long buflen;
472 if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_SERVICES)) return -1;
474 if (! value || ! value->string || ! value->string[0]) {
475 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, buffer, 0, 0, 0)) {
476 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
477 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
481 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
485 unsigned long len = (unsigned long) _tcslen(value->string) + 1;
486 TCHAR *unformatted = 0;
487 unsigned long newlen;
488 if (unformat_double_null(value->string, len, &unformatted, &newlen)) {
489 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
493 /* Prepend group identifier. */
494 unsigned long missing = 0;
495 TCHAR *canon = unformatted;
498 for (s = unformatted; *s; s++) {
499 if (*s != SC_GROUP_IDENTIFIER) missing++;
500 size_t len = _tcslen(s);
506 /* Missing identifiers plus double NULL terminator. */
507 canonlen += missing + 1;
508 newlen = (unsigned long) canonlen;
510 canon = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, canonlen * sizeof(TCHAR));
512 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canon"), _T("native_set_dependongroup"));
513 if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);
514 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
519 for (s = unformatted; *s; s++) {
520 if (*s != SC_GROUP_IDENTIFIER) canon[i++] = SC_GROUP_IDENTIFIER;
521 size_t len = _tcslen(s);
522 memmove(canon + i, s, (len + 1) * sizeof(TCHAR));
530 dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (newlen + buflen) * sizeof(TCHAR));
531 if (! dependencies) {
532 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("dependencies"), _T("native_set_dependongroup"));
533 if (canon != unformatted) HeapFree(GetProcessHeap(), 0, canon);
534 if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);
535 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
539 memmove(dependencies, buffer, buflen * sizeof(TCHAR));
540 memmove(dependencies + buflen - 1, canon, newlen * sizeof(TCHAR));
542 else dependencies = canon;
545 if (set_service_dependencies(service_name, service_handle, dependencies)) ret = -1;
546 if (dependencies != unformatted) HeapFree(GetProcessHeap(), 0, dependencies);
547 if (canon != unformatted) HeapFree(GetProcessHeap(), 0, canon);
548 if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);
549 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
554 static int native_get_dependongroup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
555 SC_HANDLE service_handle = (SC_HANDLE) param;
556 if (! service_handle) return -1;
559 unsigned long buflen;
560 if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_GROUPS)) return -1;
565 unsigned long newlen;
566 if (format_double_null(buffer, buflen, &formatted, &newlen)) {
567 HeapFree(GetProcessHeap(), 0, buffer);
571 ret = value_from_string(name, value, formatted);
572 HeapFree(GetProcessHeap(), 0, formatted);
573 HeapFree(GetProcessHeap(), 0, buffer);
583 static int native_set_dependonservice(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
584 SC_HANDLE service_handle = (SC_HANDLE) param;
585 if (! service_handle) return -1;
588 Get existing group dependencies because we must set both types together.
591 unsigned long buflen;
592 if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_GROUPS)) return -1;
594 if (! value || ! value->string || ! value->string[0]) {
595 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, buffer, 0, 0, 0)) {
596 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
597 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
601 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
605 unsigned long len = (unsigned long) _tcslen(value->string) + 1;
606 TCHAR *unformatted = 0;
607 unsigned long newlen;
608 if (unformat_double_null(value->string, len, &unformatted, &newlen)) {
609 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
615 dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (newlen + buflen) * sizeof(TCHAR));
616 if (! dependencies) {
617 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("dependencies"), _T("native_set_dependonservice"));
618 if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);
619 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
623 memmove(dependencies, buffer, buflen * sizeof(TCHAR));
624 memmove(dependencies + buflen - 1, unformatted, newlen * sizeof(TCHAR));
626 else dependencies = unformatted;
629 if (set_service_dependencies(service_name, service_handle, dependencies)) ret = -1;
630 if (dependencies != unformatted) HeapFree(GetProcessHeap(), 0, dependencies);
631 if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);
632 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
637 static int native_get_dependonservice(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
638 SC_HANDLE service_handle = (SC_HANDLE) param;
639 if (! service_handle) return -1;
642 unsigned long buflen;
643 if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_SERVICES)) return -1;
648 unsigned long newlen;
649 if (format_double_null(buffer, buflen, &formatted, &newlen)) {
650 HeapFree(GetProcessHeap(), 0, buffer);
654 ret = value_from_string(name, value, formatted);
655 HeapFree(GetProcessHeap(), 0, formatted);
656 HeapFree(GetProcessHeap(), 0, buffer);
666 int native_set_description(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
667 SC_HANDLE service_handle = (SC_HANDLE) param;
668 if (! service_handle) return -1;
670 TCHAR *description = 0;
671 if (value) description = value->string;
672 if (set_service_description(service_name, service_handle, description)) return -1;
674 if (description && description[0]) return 1;
679 int native_get_description(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
680 SC_HANDLE service_handle = (SC_HANDLE) param;
681 if (! service_handle) return -1;
683 TCHAR buffer[VALUE_LENGTH];
684 if (get_service_description(service_name, service_handle, _countof(buffer), buffer)) return -1;
686 if (buffer[0]) return value_from_string(name, value, buffer);
692 int native_set_displayname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
693 SC_HANDLE service_handle = (SC_HANDLE) param;
694 if (! service_handle) return -1;
696 TCHAR *displayname = 0;
697 if (value && value->string) displayname = value->string;
698 else displayname = (TCHAR *) service_name;
700 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, displayname)) {
701 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
706 If the display name and service name differ only in case,
707 ChangeServiceConfig() will return success but the display name will be
708 set to the service name, NOT the value passed to the function.
709 This appears to be a quirk of Windows rather than a bug here.
711 if (displayname != service_name && ! str_equiv(displayname, service_name)) return 1;
716 int native_get_displayname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
717 SC_HANDLE service_handle = (SC_HANDLE) param;
718 if (! service_handle) return -1;
720 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
721 if (! qsc) return -1;
723 int ret = value_from_string(name, value, qsc->lpDisplayName);
724 HeapFree(GetProcessHeap(), 0, qsc);
729 int native_set_imagepath(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
730 SC_HANDLE service_handle = (SC_HANDLE) param;
731 if (! service_handle) return -1;
733 /* It makes no sense to try to reset the image path. */
734 if (! value || ! value->string) {
735 print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);
739 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, value->string, 0, 0, 0, 0, 0, 0)) {
740 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
747 int native_get_imagepath(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
748 SC_HANDLE service_handle = (SC_HANDLE) param;
749 if (! service_handle) return -1;
751 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
752 if (! qsc) return -1;
754 int ret = value_from_string(name, value, qsc->lpBinaryPathName);
755 HeapFree(GetProcessHeap(), 0, qsc);
760 int native_set_name(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
761 print_message(stderr, NSSM_MESSAGE_CANNOT_RENAME_SERVICE);
765 int native_get_name(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
766 return value_from_string(name, value, service_name);
769 int native_set_objectname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
770 SC_HANDLE service_handle = (SC_HANDLE) param;
771 if (! service_handle) return -1;
774 Logical syntax is: nssm set <service> ObjectName <username> <password>
775 That means the username is actually passed in the additional parameter.
777 bool localsystem = false;
778 TCHAR *username = NSSM_LOCALSYSTEM_ACCOUNT;
781 username = (TCHAR *) additional;
782 if (value && value->string) password = value->string;
784 else if (value && value->string) username = value->string;
786 const TCHAR *well_known = well_known_username(username);
787 size_t passwordsize = 0;
789 if (str_equiv(well_known, NSSM_LOCALSYSTEM_ACCOUNT)) localsystem = true;
790 username = (TCHAR *) well_known;
793 else if (! password) {
794 /* We need a password if the account requires it. */
795 print_message(stderr, NSSM_MESSAGE_MISSING_PASSWORD, name);
798 else passwordsize = _tcslen(password) * sizeof(TCHAR);
801 ChangeServiceConfig() will fail to set the username if the service is set
802 to interact with the desktop.
804 unsigned long type = SERVICE_NO_CHANGE;
806 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
808 if (passwordsize) SecureZeroMemory(password, passwordsize);
812 type = qsc->dwServiceType & ~SERVICE_INTERACTIVE_PROCESS;
813 HeapFree(GetProcessHeap(), 0, qsc);
817 if (grant_logon_as_service(username)) {
818 if (passwordsize) SecureZeroMemory(password, passwordsize);
819 print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);
824 if (! ChangeServiceConfig(service_handle, type, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, username, password, 0)) {
825 if (passwordsize) SecureZeroMemory(password, passwordsize);
826 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
830 if (passwordsize) SecureZeroMemory(password, passwordsize);
832 if (localsystem) return 0;
837 int native_get_objectname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
838 SC_HANDLE service_handle = (SC_HANDLE) param;
839 if (! service_handle) return -1;
841 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
842 if (! qsc) return -1;
844 int ret = value_from_string(name, value, qsc->lpServiceStartName);
845 HeapFree(GetProcessHeap(), 0, qsc);
850 int native_set_startup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
851 SC_HANDLE service_handle = (SC_HANDLE) param;
852 if (! service_handle) return -1;
854 /* It makes no sense to try to reset the startup type. */
855 if (! value || ! value->string) {
856 print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);
860 /* Map NSSM_STARTUP_* constant to Windows SERVICE_*_START constant. */
861 int service_startup = -1;
863 for (i = 0; startup_strings[i]; i++) {
864 if (str_equiv(value->string, startup_strings[i])) {
870 if (service_startup < 0) {
871 print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE_STARTUP, value->string);
872 for (i = 0; startup_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), startup_strings[i]);
876 unsigned long startup;
877 switch (service_startup) {
878 case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;
879 case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;
880 default: startup = SERVICE_AUTO_START;
883 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, startup, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) {
884 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
888 SERVICE_DELAYED_AUTO_START_INFO delayed;
889 ZeroMemory(&delayed, sizeof(delayed));
890 if (service_startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;
891 else delayed.fDelayedAutostart = 0;
892 if (! ChangeServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {
893 unsigned long error = GetLastError();
894 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
895 if (error != ERROR_INVALID_LEVEL) {
896 log_event(EVENTLOG_ERROR_TYPE, NSSM_MESSAGE_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service_name, error_string(error), 0);
903 int native_get_startup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
904 SC_HANDLE service_handle = (SC_HANDLE) param;
905 if (! service_handle) return -1;
907 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
908 if (! qsc) return -1;
910 unsigned long startup;
911 int ret = get_service_startup(service_name, service_handle, qsc, &startup);
912 HeapFree(GetProcessHeap(), 0, qsc);
917 for (i = 0; startup_strings[i]; i++);
918 if (startup >= i) return -1;
920 return value_from_string(name, value, startup_strings[startup]);
923 int native_set_type(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
924 SC_HANDLE service_handle = (SC_HANDLE) param;
925 if (! service_handle) return -1;
927 /* It makes no sense to try to reset the service type. */
928 if (! value || ! value->string) {
929 print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);
934 We can only manage services of type SERVICE_WIN32_OWN_PROCESS
935 and SERVICE_INTERACTIVE_PROCESS.
937 unsigned long type = SERVICE_WIN32_OWN_PROCESS;
938 if (str_equiv(value->string, NSSM_INTERACTIVE_PROCESS)) type |= SERVICE_INTERACTIVE_PROCESS;
939 else if (! str_equiv(value->string, NSSM_WIN32_OWN_PROCESS)) {
940 print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE_TYPE, value->string);
941 _ftprintf(stderr, _T("%s\n"), NSSM_WIN32_OWN_PROCESS);
942 _ftprintf(stderr, _T("%s\n"), NSSM_INTERACTIVE_PROCESS);
947 ChangeServiceConfig() will fail if the service runs under an account
948 other than LOCALSYSTEM and we try to make it interactive.
950 if (type & SERVICE_INTERACTIVE_PROCESS) {
951 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
952 if (! qsc) return -1;
954 if (! str_equiv(qsc->lpServiceStartName, NSSM_LOCALSYSTEM_ACCOUNT)) {
955 HeapFree(GetProcessHeap(), 0, qsc);
956 print_message(stderr, NSSM_MESSAGE_INTERACTIVE_NOT_LOCALSYSTEM, value->string, service_name, NSSM_LOCALSYSTEM_ACCOUNT);
960 HeapFree(GetProcessHeap(), 0, qsc);
963 if (! ChangeServiceConfig(service_handle, type, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) {
964 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
971 int native_get_type(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
972 SC_HANDLE service_handle = (SC_HANDLE) param;
973 if (! service_handle) return -1;
975 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
976 if (! qsc) return -1;
978 value->numeric = qsc->dwServiceType;
979 HeapFree(GetProcessHeap(), 0, qsc);
982 switch (value->numeric) {
983 case SERVICE_KERNEL_DRIVER: string = NSSM_KERNEL_DRIVER; break;
984 case SERVICE_FILE_SYSTEM_DRIVER: string = NSSM_FILE_SYSTEM_DRIVER; break;
985 case SERVICE_WIN32_OWN_PROCESS: string = NSSM_WIN32_OWN_PROCESS; break;
986 case SERVICE_WIN32_SHARE_PROCESS: string = NSSM_WIN32_SHARE_PROCESS; break;
987 case SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS: string = NSSM_INTERACTIVE_PROCESS; break;
988 case SERVICE_WIN32_SHARE_PROCESS|SERVICE_INTERACTIVE_PROCESS: string = NSSM_SHARE_INTERACTIVE_PROCESS; break;
989 default: string = NSSM_UNKNOWN;
992 return value_from_string(name, value, string);
995 int set_setting(const TCHAR *service_name, HKEY key, settings_t *setting, value_t *value, const TCHAR *additional) {
996 if (! key) return -1;
999 if (setting->set) ret = setting->set(service_name, (void *) key, setting->name, setting->default_value, value, additional);
1002 if (! ret) print_message(stdout, NSSM_MESSAGE_RESET_SETTING, setting->name, service_name);
1003 else if (ret > 0) print_message(stdout, NSSM_MESSAGE_SET_SETTING, setting->name, service_name);
1004 else print_message(stderr, NSSM_MESSAGE_SET_SETTING_FAILED, setting->name, service_name);
1009 int set_setting(const TCHAR *service_name, SC_HANDLE service_handle, settings_t *setting, value_t *value, const TCHAR *additional) {
1010 if (! service_handle) return -1;
1013 if (setting->set) ret = setting->set(service_name, service_handle, setting->name, setting->default_value, value, additional);
1016 if (! ret) print_message(stdout, NSSM_MESSAGE_RESET_SETTING, setting->name, service_name);
1017 else if (ret > 0) print_message(stdout, NSSM_MESSAGE_SET_SETTING, setting->name, service_name);
1018 else print_message(stderr, NSSM_MESSAGE_SET_SETTING_FAILED, setting->name, service_name);
1024 Returns: 1 if the value was retrieved.
1025 0 if the default value was retrieved.
1028 int get_setting(const TCHAR *service_name, HKEY key, settings_t *setting, value_t *value, const TCHAR *additional) {
1029 if (! key) return -1;
1032 switch (setting->type) {
1036 value->string = (TCHAR *) setting->default_value;
1037 if (setting->get) ret = setting->get(service_name, (void *) key, setting->name, setting->default_value, value, additional);
1042 value->numeric = (unsigned long) setting->default_value;
1043 if (setting->get) ret = setting->get(service_name, (void *) key, setting->name, setting->default_value, value, additional);
1052 if (ret < 0) print_message(stderr, NSSM_MESSAGE_GET_SETTING_FAILED, setting->name, service_name);
1057 int get_setting(const TCHAR *service_name, SC_HANDLE service_handle, settings_t *setting, value_t *value, const TCHAR *additional) {
1058 if (! service_handle) return -1;
1059 return setting->get(service_name, service_handle, setting->name, 0, value, additional);
1062 settings_t settings[] = {
1063 { NSSM_REG_EXE, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },
1064 { NSSM_REG_FLAGS, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },
1065 { NSSM_REG_DIR, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },
1066 { NSSM_REG_EXIT, REG_SZ, (void *) exit_action_strings[NSSM_EXIT_RESTART], false, ADDITIONAL_MANDATORY, setting_set_exit_action, setting_get_exit_action },
1067 { NSSM_REG_HOOK, REG_SZ, (void *) _T(""), false, ADDITIONAL_MANDATORY, setting_set_hook, setting_get_hook },
1068 { NSSM_REG_AFFINITY, REG_SZ, 0, false, 0, setting_set_affinity, setting_get_affinity },
1069 { NSSM_REG_ENV, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment },
1070 { NSSM_REG_ENV_EXTRA, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment },
1071 { NSSM_REG_NO_CONSOLE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
1072 { NSSM_REG_PRIORITY, REG_SZ, (void *) priority_strings[NSSM_NORMAL_PRIORITY], false, 0, setting_set_priority, setting_get_priority },
1073 { NSSM_REG_RESTART_DELAY, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
1074 { NSSM_REG_STDIN, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },
1075 { NSSM_REG_STDIN NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDIN_SHARING, false, 0, setting_set_number, setting_get_number },
1076 { NSSM_REG_STDIN NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDIN_DISPOSITION, false, 0, setting_set_number, setting_get_number },
1077 { NSSM_REG_STDIN NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDIN_FLAGS, false, 0, setting_set_number, setting_get_number },
1078 { NSSM_REG_STDOUT, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },
1079 { NSSM_REG_STDOUT NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDOUT_SHARING, false, 0, setting_set_number, setting_get_number },
1080 { NSSM_REG_STDOUT NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDOUT_DISPOSITION, false, 0, setting_set_number, setting_get_number },
1081 { NSSM_REG_STDOUT NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDOUT_FLAGS, false, 0, setting_set_number, setting_get_number },
1082 { NSSM_REG_STDOUT NSSM_REG_STDIO_COPY_AND_TRUNCATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
1083 { NSSM_REG_STDERR, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },
1084 { NSSM_REG_STDERR NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDERR_SHARING, false, 0, setting_set_number, setting_get_number },
1085 { NSSM_REG_STDERR NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDERR_DISPOSITION, false, 0, setting_set_number, setting_get_number },
1086 { NSSM_REG_STDERR NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDERR_FLAGS, false, 0, setting_set_number, setting_get_number },
1087 { NSSM_REG_STDERR NSSM_REG_STDIO_COPY_AND_TRUNCATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
1088 { NSSM_REG_STOP_METHOD_SKIP, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
1089 { NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_CONSOLE_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },
1090 { NSSM_REG_KILL_WINDOW_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_WINDOW_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },
1091 { NSSM_REG_KILL_THREADS_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_THREADS_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },
1092 { NSSM_REG_KILL_PROCESS_TREE, REG_DWORD, (void *) 1, false, 0, setting_set_number, setting_get_number },
1093 { NSSM_REG_THROTTLE, REG_DWORD, (void *) NSSM_RESET_THROTTLE_RESTART, false, 0, setting_set_number, setting_get_number },
1094 { NSSM_REG_ROTATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
1095 { NSSM_REG_ROTATE_ONLINE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
1096 { NSSM_REG_ROTATE_SECONDS, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
1097 { NSSM_REG_ROTATE_BYTES_LOW, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
1098 { NSSM_REG_ROTATE_BYTES_HIGH, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
1099 { NSSM_REG_ROTATE_DELAY, REG_DWORD, (void *) NSSM_ROTATE_DELAY, false, 0, setting_set_number, setting_get_number },
1100 { NSSM_NATIVE_DEPENDONGROUP, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_dependongroup, native_get_dependongroup },
1101 { NSSM_NATIVE_DEPENDONSERVICE, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_dependonservice, native_get_dependonservice },
1102 { NSSM_NATIVE_DESCRIPTION, REG_SZ, _T(""), true, 0, native_set_description, native_get_description },
1103 { NSSM_NATIVE_DISPLAYNAME, REG_SZ, NULL, true, 0, native_set_displayname, native_get_displayname },
1104 { NSSM_NATIVE_IMAGEPATH, REG_EXPAND_SZ, NULL, true, 0, native_set_imagepath, native_get_imagepath },
1105 { NSSM_NATIVE_OBJECTNAME, REG_SZ, NSSM_LOCALSYSTEM_ACCOUNT, true, 0, native_set_objectname, native_get_objectname },
1106 { NSSM_NATIVE_NAME, REG_SZ, NULL, true, 0, native_set_name, native_get_name },
1107 { NSSM_NATIVE_STARTUP, REG_SZ, NULL, true, 0, native_set_startup, native_get_startup },
1108 { NSSM_NATIVE_TYPE, REG_SZ, NULL, true, 0, native_set_type, native_get_type },
1109 { NULL, NULL, NULL, NULL, NULL }