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 int setting_set_affinity(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
190 HKEY key = (HKEY) param;
191 if (! key) return -1;
195 __int64 system_affinity = 0LL;
197 if (value && value->string) {
199 if (! GetProcessAffinityMask(GetCurrentProcess(), &affinity, (DWORD_PTR *) &system_affinity)) system_affinity = ~0;
201 if (is_default(value->string) || str_equiv(value->string, NSSM_AFFINITY_ALL)) mask = 0LL;
202 else if (affinity_string_to_mask(value->string, &mask)) {
203 print_message(stderr, NSSM_MESSAGE_BOGUS_AFFINITY_MASK, value->string, num_cpus() - 1);
210 error = RegDeleteValue(key, name);
211 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
212 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
218 if (affinity_mask_to_string(mask, &canon)) canon = value->string;
220 __int64 effective_affinity = mask & system_affinity;
221 if (effective_affinity != mask) {
222 /* Requested CPUs did not intersect with available CPUs? */
223 if (! effective_affinity) mask = effective_affinity = system_affinity;
226 if (! affinity_mask_to_string(system_affinity, &system)) {
227 TCHAR *effective = 0;
228 if (! affinity_mask_to_string(effective_affinity, &effective)) {
229 print_message(stderr, NSSM_MESSAGE_EFFECTIVE_AFFINITY_MASK, value->string, system, effective);
230 HeapFree(GetProcessHeap(), 0, effective);
232 HeapFree(GetProcessHeap(), 0, system);
236 if (RegSetValueEx(key, name, 0, REG_SZ, (const unsigned char *) canon, (unsigned long) (_tcslen(canon) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) {
237 if (canon != value->string) HeapFree(GetProcessHeap(), 0, canon);
238 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, name, error_string(GetLastError()), 0);
242 if (canon != value->string) HeapFree(GetProcessHeap(), 0, canon);
246 static int setting_get_affinity(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
247 HKEY key = (HKEY) param;
248 if (! key) return -1;
252 unsigned long buflen = 0;
254 int ret = RegQueryValueEx(key, name, 0, &type, 0, &buflen);
255 if (ret == ERROR_FILE_NOT_FOUND) {
256 if (value_from_string(name, value, NSSM_AFFINITY_ALL) == 1) return 0;
259 if (ret != ERROR_SUCCESS) return -1;
261 if (type != REG_SZ) return -1;
263 buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, buflen);
265 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("affinity"), _T("setting_get_affinity"));
269 if (get_string(key, (TCHAR *) name, buffer, buflen, false, false, true)) {
270 HeapFree(GetProcessHeap(), 0, buffer);
275 if (affinity_string_to_mask(buffer, &affinity)) {
276 print_message(stderr, NSSM_MESSAGE_BOGUS_AFFINITY_MASK, buffer, num_cpus() - 1);
277 HeapFree(GetProcessHeap(), 0, buffer);
281 HeapFree(GetProcessHeap(), 0, buffer);
284 if (affinity_mask_to_string(affinity, &buffer)) {
285 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
289 ret = value_from_string(name, value, buffer);
290 HeapFree(GetProcessHeap(), 0, buffer);
294 static int setting_set_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
295 HKEY key = (HKEY) param;
296 if (! param) return -1;
298 if (! value || ! value->string || ! value->string[0]) {
299 long error = RegDeleteValue(key, name);
300 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
301 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
305 unsigned long envlen = (unsigned long) _tcslen(value->string) + 1;
306 TCHAR *unformatted = 0;
307 unsigned long newlen;
308 if (unformat_double_null(value->string, envlen, &unformatted, &newlen)) return -1;
310 if (test_environment(unformatted)) {
311 HeapFree(GetProcessHeap(), 0, unformatted);
312 print_message(stderr, NSSM_GUI_INVALID_ENVIRONMENT);
316 if (RegSetValueEx(key, name, 0, REG_MULTI_SZ, (const unsigned char *) unformatted, (unsigned long) newlen * sizeof(TCHAR)) != ERROR_SUCCESS) {
317 if (newlen) HeapFree(GetProcessHeap(), 0, unformatted);
318 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV, error_string(GetLastError()), 0);
322 if (newlen) HeapFree(GetProcessHeap(), 0, unformatted);
326 static int setting_get_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
327 HKEY key = (HKEY) param;
328 if (! param) return -1;
331 unsigned long envlen;
332 if (get_environment((TCHAR *) service_name, key, (TCHAR *) name, &env, &envlen)) return -1;
333 if (! envlen) return 0;
336 unsigned long newlen;
337 if (format_double_null(env, envlen, &formatted, &newlen)) return -1;
341 /* Find named environment variable. */
343 size_t len = _tcslen(additional);
344 for (s = env; *s; s++) {
345 /* Look for <additional>=<string> NULL NULL */
346 if (! _tcsnicmp(s, additional, len) && s[len] == _T('=')) {
349 ret = value_from_string(name, value, s);
350 HeapFree(GetProcessHeap(), 0, env);
354 /* Skip this string. */
357 HeapFree(GetProcessHeap(), 0, env);
361 HeapFree(GetProcessHeap(), 0, env);
363 ret = value_from_string(name, value, formatted);
364 if (newlen) HeapFree(GetProcessHeap(), 0, formatted);
368 static int setting_set_priority(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
369 HKEY key = (HKEY) param;
370 if (! param) return -1;
372 TCHAR *priority_string;
376 if (value && value->string) priority_string = value->string;
377 else if (default_value) priority_string = (TCHAR *) default_value;
379 error = RegDeleteValue(key, name);
380 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
381 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
385 for (i = 0; priority_strings[i]; i++) {
386 if (! str_equiv(priority_strings[i], priority_string)) continue;
388 if (default_value && str_equiv(priority_string, (TCHAR *) default_value)) {
389 error = RegDeleteValue(key, name);
390 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
391 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
395 if (set_number(key, (TCHAR *) name, priority_index_to_constant(i))) return -1;
399 print_message(stderr, NSSM_MESSAGE_INVALID_PRIORITY, priority_string);
400 for (i = 0; priority_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), priority_strings[i]);
405 static int setting_get_priority(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
406 HKEY key = (HKEY) param;
407 if (! param) return -1;
409 unsigned long constant;
410 switch (get_number(key, (TCHAR *) name, &constant, false)) {
411 case 0: return value_from_string(name, value, (const TCHAR *) default_value);
415 return value_from_string(name, value, priority_strings[priority_constant_to_index(constant)]);
418 /* Functions to manage native service settings. */
419 static int native_set_dependongroup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
420 SC_HANDLE service_handle = (SC_HANDLE) param;
421 if (! service_handle) return -1;
424 Get existing service dependencies because we must set both types together.
427 unsigned long buflen;
428 if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_SERVICES)) return -1;
430 if (! value || ! value->string || ! value->string[0]) {
431 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, buffer, 0, 0, 0)) {
432 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
433 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
437 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
441 unsigned long len = (unsigned long) _tcslen(value->string) + 1;
442 TCHAR *unformatted = 0;
443 unsigned long newlen;
444 if (unformat_double_null(value->string, len, &unformatted, &newlen)) {
445 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
449 /* Prepend group identifier. */
450 unsigned long missing = 0;
451 TCHAR *canon = unformatted;
454 for (s = unformatted; *s; s++) {
455 if (*s != SC_GROUP_IDENTIFIER) missing++;
456 size_t len = _tcslen(s);
462 /* Missing identifiers plus double NULL terminator. */
463 canonlen += missing + 1;
464 newlen = (unsigned long) canonlen;
466 canon = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, canonlen * sizeof(TCHAR));
468 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canon"), _T("native_set_dependongroup"));
469 if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);
470 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
475 for (s = unformatted; *s; s++) {
476 if (*s != SC_GROUP_IDENTIFIER) canon[i++] = SC_GROUP_IDENTIFIER;
477 size_t len = _tcslen(s);
478 memmove(canon + i, s, (len + 1) * sizeof(TCHAR));
486 dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (newlen + buflen) * sizeof(TCHAR));
487 if (! dependencies) {
488 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("dependencies"), _T("native_set_dependongroup"));
489 if (canon != unformatted) HeapFree(GetProcessHeap(), 0, canon);
490 if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);
491 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
495 memmove(dependencies, buffer, buflen * sizeof(TCHAR));
496 memmove(dependencies + buflen - 1, canon, newlen * sizeof(TCHAR));
498 else dependencies = canon;
501 if (set_service_dependencies(service_name, service_handle, dependencies)) ret = -1;
502 if (dependencies != unformatted) HeapFree(GetProcessHeap(), 0, dependencies);
503 if (canon != unformatted) HeapFree(GetProcessHeap(), 0, canon);
504 if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);
505 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
510 static int native_get_dependongroup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
511 SC_HANDLE service_handle = (SC_HANDLE) param;
512 if (! service_handle) return -1;
515 unsigned long buflen;
516 if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_GROUPS)) return -1;
521 unsigned long newlen;
522 if (format_double_null(buffer, buflen, &formatted, &newlen)) {
523 HeapFree(GetProcessHeap(), 0, buffer);
527 ret = value_from_string(name, value, formatted);
528 HeapFree(GetProcessHeap(), 0, formatted);
529 HeapFree(GetProcessHeap(), 0, buffer);
539 static int native_set_dependonservice(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
540 SC_HANDLE service_handle = (SC_HANDLE) param;
541 if (! service_handle) return -1;
544 Get existing group dependencies because we must set both types together.
547 unsigned long buflen;
548 if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_GROUPS)) return -1;
550 if (! value || ! value->string || ! value->string[0]) {
551 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, buffer, 0, 0, 0)) {
552 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
553 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
557 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
561 unsigned long len = (unsigned long) _tcslen(value->string) + 1;
562 TCHAR *unformatted = 0;
563 unsigned long newlen;
564 if (unformat_double_null(value->string, len, &unformatted, &newlen)) {
565 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
571 dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (newlen + buflen) * sizeof(TCHAR));
572 if (! dependencies) {
573 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("dependencies"), _T("native_set_dependonservice"));
574 if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);
575 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
579 memmove(dependencies, buffer, buflen * sizeof(TCHAR));
580 memmove(dependencies + buflen - 1, unformatted, newlen * sizeof(TCHAR));
582 else dependencies = unformatted;
585 if (set_service_dependencies(service_name, service_handle, dependencies)) ret = -1;
586 if (dependencies != unformatted) HeapFree(GetProcessHeap(), 0, dependencies);
587 if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);
588 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
593 static int native_get_dependonservice(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
594 SC_HANDLE service_handle = (SC_HANDLE) param;
595 if (! service_handle) return -1;
598 unsigned long buflen;
599 if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_SERVICES)) return -1;
604 unsigned long newlen;
605 if (format_double_null(buffer, buflen, &formatted, &newlen)) {
606 HeapFree(GetProcessHeap(), 0, buffer);
610 ret = value_from_string(name, value, formatted);
611 HeapFree(GetProcessHeap(), 0, formatted);
612 HeapFree(GetProcessHeap(), 0, buffer);
622 int native_set_description(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
623 SC_HANDLE service_handle = (SC_HANDLE) param;
624 if (! service_handle) return -1;
626 TCHAR *description = 0;
627 if (value) description = value->string;
628 if (set_service_description(service_name, service_handle, description)) return -1;
630 if (description && description[0]) return 1;
635 int native_get_description(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
636 SC_HANDLE service_handle = (SC_HANDLE) param;
637 if (! service_handle) return -1;
639 TCHAR buffer[VALUE_LENGTH];
640 if (get_service_description(service_name, service_handle, _countof(buffer), buffer)) return -1;
642 if (buffer[0]) return value_from_string(name, value, buffer);
648 int native_set_displayname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
649 SC_HANDLE service_handle = (SC_HANDLE) param;
650 if (! service_handle) return -1;
652 TCHAR *displayname = 0;
653 if (value && value->string) displayname = value->string;
654 else displayname = (TCHAR *) service_name;
656 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, displayname)) {
657 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
662 If the display name and service name differ only in case,
663 ChangeServiceConfig() will return success but the display name will be
664 set to the service name, NOT the value passed to the function.
665 This appears to be a quirk of Windows rather than a bug here.
667 if (displayname != service_name && ! str_equiv(displayname, service_name)) return 1;
672 int native_get_displayname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
673 SC_HANDLE service_handle = (SC_HANDLE) param;
674 if (! service_handle) return -1;
676 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
677 if (! qsc) return -1;
679 int ret = value_from_string(name, value, qsc->lpDisplayName);
680 HeapFree(GetProcessHeap(), 0, qsc);
685 int native_set_imagepath(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
686 SC_HANDLE service_handle = (SC_HANDLE) param;
687 if (! service_handle) return -1;
689 /* It makes no sense to try to reset the image path. */
690 if (! value || ! value->string) {
691 print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);
695 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, value->string, 0, 0, 0, 0, 0, 0)) {
696 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
703 int native_get_imagepath(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
704 SC_HANDLE service_handle = (SC_HANDLE) param;
705 if (! service_handle) return -1;
707 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
708 if (! qsc) return -1;
710 int ret = value_from_string(name, value, qsc->lpBinaryPathName);
711 HeapFree(GetProcessHeap(), 0, qsc);
716 int native_set_name(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
717 print_message(stderr, NSSM_MESSAGE_CANNOT_RENAME_SERVICE);
721 int native_get_name(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
722 return value_from_string(name, value, service_name);
725 int native_set_objectname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
726 SC_HANDLE service_handle = (SC_HANDLE) param;
727 if (! service_handle) return -1;
730 Logical syntax is: nssm set <service> ObjectName <username> <password>
731 That means the username is actually passed in the additional parameter.
733 bool localsystem = false;
734 TCHAR *username = NSSM_LOCALSYSTEM_ACCOUNT;
737 username = (TCHAR *) additional;
738 if (value && value->string) password = value->string;
740 else if (value && value->string) username = value->string;
742 const TCHAR *well_known = well_known_username(username);
743 size_t passwordsize = 0;
745 if (str_equiv(well_known, NSSM_LOCALSYSTEM_ACCOUNT)) localsystem = true;
746 username = (TCHAR *) well_known;
749 else if (! password) {
750 /* We need a password if the account requires it. */
751 print_message(stderr, NSSM_MESSAGE_MISSING_PASSWORD, name);
754 else passwordsize = _tcslen(password) * sizeof(TCHAR);
757 ChangeServiceConfig() will fail to set the username if the service is set
758 to interact with the desktop.
760 unsigned long type = SERVICE_NO_CHANGE;
762 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
764 if (passwordsize) SecureZeroMemory(password, passwordsize);
768 type = qsc->dwServiceType & ~SERVICE_INTERACTIVE_PROCESS;
769 HeapFree(GetProcessHeap(), 0, qsc);
773 if (grant_logon_as_service(username)) {
774 if (passwordsize) SecureZeroMemory(password, passwordsize);
775 print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);
780 if (! ChangeServiceConfig(service_handle, type, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, username, password, 0)) {
781 if (passwordsize) SecureZeroMemory(password, passwordsize);
782 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
786 if (passwordsize) SecureZeroMemory(password, passwordsize);
788 if (localsystem) return 0;
793 int native_get_objectname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
794 SC_HANDLE service_handle = (SC_HANDLE) param;
795 if (! service_handle) return -1;
797 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
798 if (! qsc) return -1;
800 int ret = value_from_string(name, value, qsc->lpServiceStartName);
801 HeapFree(GetProcessHeap(), 0, qsc);
806 int native_set_startup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
807 SC_HANDLE service_handle = (SC_HANDLE) param;
808 if (! service_handle) return -1;
810 /* It makes no sense to try to reset the startup type. */
811 if (! value || ! value->string) {
812 print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);
816 /* Map NSSM_STARTUP_* constant to Windows SERVICE_*_START constant. */
817 int service_startup = -1;
819 for (i = 0; startup_strings[i]; i++) {
820 if (str_equiv(value->string, startup_strings[i])) {
826 if (service_startup < 0) {
827 print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE_STARTUP, value->string);
828 for (i = 0; startup_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), startup_strings[i]);
832 unsigned long startup;
833 switch (service_startup) {
834 case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;
835 case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;
836 default: startup = SERVICE_AUTO_START;
839 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, startup, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) {
840 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
844 SERVICE_DELAYED_AUTO_START_INFO delayed;
845 ZeroMemory(&delayed, sizeof(delayed));
846 if (service_startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;
847 else delayed.fDelayedAutostart = 0;
848 if (! ChangeServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {
849 unsigned long error = GetLastError();
850 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
851 if (error != ERROR_INVALID_LEVEL) {
852 log_event(EVENTLOG_ERROR_TYPE, NSSM_MESSAGE_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service_name, error_string(error), 0);
859 int native_get_startup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
860 SC_HANDLE service_handle = (SC_HANDLE) param;
861 if (! service_handle) return -1;
863 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
864 if (! qsc) return -1;
866 unsigned long startup;
867 int ret = get_service_startup(service_name, service_handle, qsc, &startup);
868 HeapFree(GetProcessHeap(), 0, qsc);
873 for (i = 0; startup_strings[i]; i++);
874 if (startup >= i) return -1;
876 return value_from_string(name, value, startup_strings[startup]);
879 int native_set_type(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
880 SC_HANDLE service_handle = (SC_HANDLE) param;
881 if (! service_handle) return -1;
883 /* It makes no sense to try to reset the service type. */
884 if (! value || ! value->string) {
885 print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);
890 We can only manage services of type SERVICE_WIN32_OWN_PROCESS
891 and SERVICE_INTERACTIVE_PROCESS.
893 unsigned long type = SERVICE_WIN32_OWN_PROCESS;
894 if (str_equiv(value->string, NSSM_INTERACTIVE_PROCESS)) type |= SERVICE_INTERACTIVE_PROCESS;
895 else if (! str_equiv(value->string, NSSM_WIN32_OWN_PROCESS)) {
896 print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE_TYPE, value->string);
897 _ftprintf(stderr, _T("%s\n"), NSSM_WIN32_OWN_PROCESS);
898 _ftprintf(stderr, _T("%s\n"), NSSM_INTERACTIVE_PROCESS);
903 ChangeServiceConfig() will fail if the service runs under an account
904 other than LOCALSYSTEM and we try to make it interactive.
906 if (type & SERVICE_INTERACTIVE_PROCESS) {
907 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
908 if (! qsc) return -1;
910 if (! str_equiv(qsc->lpServiceStartName, NSSM_LOCALSYSTEM_ACCOUNT)) {
911 HeapFree(GetProcessHeap(), 0, qsc);
912 print_message(stderr, NSSM_MESSAGE_INTERACTIVE_NOT_LOCALSYSTEM, value->string, service_name, NSSM_LOCALSYSTEM_ACCOUNT);
916 HeapFree(GetProcessHeap(), 0, qsc);
919 if (! ChangeServiceConfig(service_handle, type, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) {
920 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
927 int native_get_type(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
928 SC_HANDLE service_handle = (SC_HANDLE) param;
929 if (! service_handle) return -1;
931 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
932 if (! qsc) return -1;
934 value->numeric = qsc->dwServiceType;
935 HeapFree(GetProcessHeap(), 0, qsc);
938 switch (value->numeric) {
939 case SERVICE_KERNEL_DRIVER: string = NSSM_KERNEL_DRIVER; break;
940 case SERVICE_FILE_SYSTEM_DRIVER: string = NSSM_FILE_SYSTEM_DRIVER; break;
941 case SERVICE_WIN32_OWN_PROCESS: string = NSSM_WIN32_OWN_PROCESS; break;
942 case SERVICE_WIN32_SHARE_PROCESS: string = NSSM_WIN32_SHARE_PROCESS; break;
943 case SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS: string = NSSM_INTERACTIVE_PROCESS; break;
944 case SERVICE_WIN32_SHARE_PROCESS|SERVICE_INTERACTIVE_PROCESS: string = NSSM_SHARE_INTERACTIVE_PROCESS; break;
945 default: string = NSSM_UNKNOWN;
948 return value_from_string(name, value, string);
951 int set_setting(const TCHAR *service_name, HKEY key, settings_t *setting, value_t *value, const TCHAR *additional) {
952 if (! key) return -1;
955 if (setting->set) ret = setting->set(service_name, (void *) key, setting->name, setting->default_value, value, additional);
958 if (! ret) print_message(stdout, NSSM_MESSAGE_RESET_SETTING, setting->name, service_name);
959 else if (ret > 0) print_message(stdout, NSSM_MESSAGE_SET_SETTING, setting->name, service_name);
960 else print_message(stderr, NSSM_MESSAGE_SET_SETTING_FAILED, setting->name, service_name);
965 int set_setting(const TCHAR *service_name, SC_HANDLE service_handle, settings_t *setting, value_t *value, const TCHAR *additional) {
966 if (! service_handle) return -1;
969 if (setting->set) ret = setting->set(service_name, service_handle, setting->name, setting->default_value, value, additional);
972 if (! ret) print_message(stdout, NSSM_MESSAGE_RESET_SETTING, setting->name, service_name);
973 else if (ret > 0) print_message(stdout, NSSM_MESSAGE_SET_SETTING, setting->name, service_name);
974 else print_message(stderr, NSSM_MESSAGE_SET_SETTING_FAILED, setting->name, service_name);
980 Returns: 1 if the value was retrieved.
981 0 if the default value was retrieved.
984 int get_setting(const TCHAR *service_name, HKEY key, settings_t *setting, value_t *value, const TCHAR *additional) {
985 if (! key) return -1;
988 switch (setting->type) {
992 value->string = (TCHAR *) setting->default_value;
993 if (setting->get) ret = setting->get(service_name, (void *) key, setting->name, setting->default_value, value, additional);
998 value->numeric = (unsigned long) setting->default_value;
999 if (setting->get) ret = setting->get(service_name, (void *) key, setting->name, setting->default_value, value, additional);
1008 if (ret < 0) print_message(stderr, NSSM_MESSAGE_GET_SETTING_FAILED, setting->name, service_name);
1013 int get_setting(const TCHAR *service_name, SC_HANDLE service_handle, settings_t *setting, value_t *value, const TCHAR *additional) {
1014 if (! service_handle) return -1;
1015 return setting->get(service_name, service_handle, setting->name, 0, value, additional);
1018 settings_t settings[] = {
1019 { NSSM_REG_EXE, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },
1020 { NSSM_REG_FLAGS, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },
1021 { NSSM_REG_DIR, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },
1022 { NSSM_REG_EXIT, REG_SZ, (void *) exit_action_strings[NSSM_EXIT_RESTART], false, ADDITIONAL_MANDATORY, setting_set_exit_action, setting_get_exit_action },
1023 { NSSM_REG_AFFINITY, REG_SZ, 0, false, 0, setting_set_affinity, setting_get_affinity },
1024 { NSSM_REG_ENV, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment },
1025 { NSSM_REG_ENV_EXTRA, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment },
1026 { NSSM_REG_NO_CONSOLE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
1027 { NSSM_REG_PRIORITY, REG_SZ, (void *) priority_strings[NSSM_NORMAL_PRIORITY], false, 0, setting_set_priority, setting_get_priority },
1028 { NSSM_REG_RESTART_DELAY, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
1029 { NSSM_REG_STDIN, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },
1030 { NSSM_REG_STDIN NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDIN_SHARING, false, 0, setting_set_number, setting_get_number },
1031 { NSSM_REG_STDIN NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDIN_DISPOSITION, false, 0, setting_set_number, setting_get_number },
1032 { NSSM_REG_STDIN NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDIN_FLAGS, false, 0, setting_set_number, setting_get_number },
1033 { NSSM_REG_STDOUT, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },
1034 { NSSM_REG_STDOUT NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDOUT_SHARING, false, 0, setting_set_number, setting_get_number },
1035 { NSSM_REG_STDOUT NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDOUT_DISPOSITION, false, 0, setting_set_number, setting_get_number },
1036 { NSSM_REG_STDOUT NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDOUT_FLAGS, false, 0, setting_set_number, setting_get_number },
1037 { NSSM_REG_STDOUT NSSM_REG_STDIO_COPY_AND_TRUNCATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
1038 { NSSM_REG_STDERR, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },
1039 { NSSM_REG_STDERR NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDERR_SHARING, false, 0, setting_set_number, setting_get_number },
1040 { NSSM_REG_STDERR NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDERR_DISPOSITION, false, 0, setting_set_number, setting_get_number },
1041 { NSSM_REG_STDERR NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDERR_FLAGS, false, 0, setting_set_number, setting_get_number },
1042 { NSSM_REG_STDERR NSSM_REG_STDIO_COPY_AND_TRUNCATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
1043 { NSSM_REG_STOP_METHOD_SKIP, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
1044 { NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_CONSOLE_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },
1045 { NSSM_REG_KILL_WINDOW_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_WINDOW_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },
1046 { NSSM_REG_KILL_THREADS_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_THREADS_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },
1047 { NSSM_REG_KILL_PROCESS_TREE, REG_DWORD, (void *) 1, false, 0, setting_set_number, setting_get_number },
1048 { NSSM_REG_THROTTLE, REG_DWORD, (void *) NSSM_RESET_THROTTLE_RESTART, false, 0, setting_set_number, setting_get_number },
1049 { NSSM_REG_ROTATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
1050 { NSSM_REG_ROTATE_ONLINE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
1051 { NSSM_REG_ROTATE_SECONDS, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
1052 { NSSM_REG_ROTATE_BYTES_LOW, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
1053 { NSSM_REG_ROTATE_BYTES_HIGH, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
1054 { NSSM_REG_ROTATE_DELAY, REG_DWORD, (void *) NSSM_ROTATE_DELAY, false, 0, setting_set_number, setting_get_number },
1055 { NSSM_NATIVE_DEPENDONGROUP, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_dependongroup, native_get_dependongroup },
1056 { NSSM_NATIVE_DEPENDONSERVICE, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_dependonservice, native_get_dependonservice },
1057 { NSSM_NATIVE_DESCRIPTION, REG_SZ, _T(""), true, 0, native_set_description, native_get_description },
1058 { NSSM_NATIVE_DISPLAYNAME, REG_SZ, NULL, true, 0, native_set_displayname, native_get_displayname },
1059 { NSSM_NATIVE_IMAGEPATH, REG_EXPAND_SZ, NULL, true, 0, native_set_imagepath, native_get_imagepath },
1060 { NSSM_NATIVE_OBJECTNAME, REG_SZ, NSSM_LOCALSYSTEM_ACCOUNT, true, 0, native_set_objectname, native_get_objectname },
1061 { NSSM_NATIVE_NAME, REG_SZ, NULL, true, 0, native_set_name, native_get_name },
1062 { NSSM_NATIVE_STARTUP, REG_SZ, NULL, true, 0, native_set_startup, native_get_startup },
1063 { NSSM_NATIVE_TYPE, REG_SZ, NULL, true, 0, native_set_type, native_get_type },
1064 { NULL, NULL, NULL, NULL, NULL }