2 /* XXX: (value && value->string) is probably bogus because value is probably never null */
\r
5 #define NSSM_AFFINITY_ALL _T("All")
\r
7 #define NSSM_DEFAULT_STRING _T("Default")
\r
9 extern const TCHAR *exit_action_strings[];
\r
10 extern const TCHAR *startup_strings[];
\r
11 extern const TCHAR *priority_strings[];
\r
12 extern const TCHAR *hook_event_strings[];
\r
13 extern const TCHAR *hook_action_strings[];
\r
15 /* Does the parameter refer to the default value of the setting? */
\r
16 static inline int is_default(const TCHAR *value) {
\r
17 return (str_equiv(value, NSSM_DEFAULT_STRING) || str_equiv(value, _T("*")) || ! value[0]);
\r
20 /* What type of parameter is it parameter? */
\r
21 static inline bool is_string_type(const unsigned long type) {
\r
22 return (type == REG_MULTI_SZ || type == REG_EXPAND_SZ || type == REG_SZ);
\r
24 static inline bool is_numeric_type(const unsigned long type) {
\r
25 return (type == REG_DWORD);
\r
28 static int value_from_string(const TCHAR *name, value_t *value, const TCHAR *string) {
\r
29 size_t len = _tcslen(string);
\r
35 value->string = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));
\r
36 if (! value->string) {
\r
37 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, name, _T("value_from_string()"));
\r
41 if (_sntprintf_s(value->string, len, _TRUNCATE, _T("%s"), string) < 0) {
\r
42 HeapFree(GetProcessHeap(), 0, value->string);
\r
43 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, name, _T("value_from_string()"));
\r
50 /* Functions to manage NSSM-specific settings in the registry. */
\r
51 static int setting_set_number(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
52 HKEY key = (HKEY) param;
\r
53 if (! key) return -1;
\r
55 unsigned long number;
\r
58 /* Resetting to default? */
\r
59 if (! value || ! value->string) {
\r
60 error = RegDeleteValue(key, name);
\r
61 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
\r
62 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
\r
65 if (str_number(value->string, &number)) return -1;
\r
67 if (default_value && number == PtrToUlong(default_value)) {
\r
68 error = RegDeleteValue(key, name);
\r
69 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
\r
70 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
\r
74 if (set_number(key, (TCHAR *) name, number)) return -1;
\r
79 static int setting_get_number(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
80 HKEY key = (HKEY) param;
\r
81 return get_number(key, (TCHAR *) name, &value->numeric, false);
\r
84 static int setting_set_string(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
85 HKEY key = (HKEY) param;
\r
86 if (! key) return -1;
\r
90 /* Resetting to default? */
\r
91 if (! value || ! value->string) {
\r
92 if (default_value) value->string = (TCHAR *) default_value;
\r
94 error = RegDeleteValue(key, name);
\r
95 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
\r
96 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
\r
100 if (default_value && _tcslen((TCHAR *) default_value) && str_equiv(value->string, (TCHAR *) default_value)) {
\r
101 error = RegDeleteValue(key, name);
\r
102 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
\r
103 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
\r
107 if (set_expand_string(key, (TCHAR *) name, value->string)) return -1;
\r
112 static int setting_get_string(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
113 HKEY key = (HKEY) param;
\r
114 TCHAR buffer[VALUE_LENGTH];
\r
116 if (get_string(key, (TCHAR *) name, (TCHAR *) buffer, (unsigned long) sizeof(buffer), false, false, false)) return -1;
\r
118 return value_from_string(name, value, buffer);
\r
121 static int setting_not_dumpable(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
125 static int setting_dump_string(const TCHAR *service_name, void *param, const TCHAR *name, const value_t *value, const TCHAR *additional) {
\r
126 TCHAR quoted_service_name[SERVICE_NAME_LENGTH * 2];
\r
127 TCHAR quoted_value[VALUE_LENGTH * 2];
\r
128 TCHAR quoted_additional[VALUE_LENGTH * 2];
\r
129 TCHAR quoted_nssm[EXE_LENGTH * 2];
\r
131 if (quote(service_name, quoted_service_name, _countof(quoted_service_name))) return 1;
\r
134 if (_tcslen(additional)) {
\r
135 if (quote(additional, quoted_additional, _countof(quoted_additional))) return 3;
\r
137 else _sntprintf_s(quoted_additional, _countof(quoted_additional), _TRUNCATE, _T("\"\""));
\r
139 else quoted_additional[0] = _T('\0');
\r
141 unsigned long type = (unsigned long) param;
\r
142 if (is_string_type(type)) {
\r
143 if (_tcslen(value->string)) {
\r
144 if (quote(value->string, quoted_value, _countof(quoted_value))) return 2;
\r
146 else _sntprintf_s(quoted_value, _countof(quoted_value), _TRUNCATE, _T("\"\""));
\r
148 else if (is_numeric_type(type)) _sntprintf_s(quoted_value, _countof(quoted_value), _TRUNCATE, _T("%lu"), value->numeric);
\r
151 if (quote(nssm_exe(), quoted_nssm, _countof(quoted_nssm))) return 3;
\r
152 if (_tcslen(quoted_additional)) _tprintf(_T("%s set %s %s %s %s\n"), quoted_nssm, quoted_service_name, name, quoted_additional, quoted_value);
\r
153 else _tprintf(_T("%s set %s %s %s\n"), quoted_nssm, quoted_service_name, name, quoted_value);
\r
157 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
158 unsigned long exitcode;
\r
160 TCHAR action_string[ACTION_LEN];
\r
163 /* Default action? */
\r
164 if (is_default(additional)) code = 0;
\r
166 if (str_number(additional, &exitcode)) return -1;
\r
167 code = (TCHAR *) additional;
\r
171 HKEY key = open_registry(service_name, name, KEY_WRITE);
\r
172 if (! key) return -1;
\r
177 /* Resetting to default? */
\r
178 if (value && value->string) _sntprintf_s(action_string, _countof(action_string), _TRUNCATE, _T("%s"), value->string);
\r
181 /* Delete explicit action. */
\r
182 error = RegDeleteValue(key, code);
\r
184 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
\r
185 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, code, service_name, error_string(error));
\r
189 /* Explicitly keep the default action. */
\r
190 if (default_value) _sntprintf_s(action_string, _countof(action_string), _TRUNCATE, _T("%s"), (TCHAR *) default_value);
\r
195 /* Validate the string. */
\r
196 for (int i = 0; exit_action_strings[i]; i++) {
\r
197 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
198 if (default_value && str_equiv(action_string, (TCHAR *) default_value)) ret = 0;
\r
199 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
200 print_message(stderr, NSSM_MESSAGE_SETVALUE_FAILED, code, service_name, error_string(GetLastError()));
\r
210 print_message(stderr, NSSM_MESSAGE_INVALID_EXIT_ACTION, action_string);
\r
211 for (int i = 0; exit_action_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), exit_action_strings[i]);
\r
216 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
217 unsigned long exitcode = 0;
\r
218 unsigned long *code = 0;
\r
221 if (! is_default(additional)) {
\r
222 if (str_number(additional, &exitcode)) return -1;
\r
227 TCHAR action_string[ACTION_LEN];
\r
228 bool default_action;
\r
229 if (get_exit_action(service_name, code, action_string, &default_action)) return -1;
\r
231 value_from_string(name, value, action_string);
\r
233 if (default_action && ! _tcsnicmp((const TCHAR *) action_string, (TCHAR *) default_value, ACTION_LEN)) return 0;
\r
237 static int setting_dump_exit_action(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
239 HKEY key = open_registry(service_name, NSSM_REG_EXIT, KEY_READ);
\r
240 if (! key) return -1;
\r
243 unsigned long index = 0;
\r
245 int ret = enumerate_registry_values(key, &index, code, _countof(code));
\r
246 if (ret == ERROR_NO_MORE_ITEMS) break;
\r
247 if (ret != ERROR_SUCCESS) continue;
\r
250 for (i = 0; i < _countof(code); i++) {
\r
251 if (! code[i]) break;
\r
252 if (code[i] >= _T('0') || code[i] <= _T('9')) continue;
\r
256 if (! valid) continue;
\r
258 TCHAR *additional = (code[i]) ? code : NSSM_DEFAULT_STRING;
\r
260 ret = setting_get_exit_action(service_name, 0, name, default_value, value, additional);
\r
262 if (setting_dump_string(service_name, (void *) REG_SZ, name, value, additional)) errors++;
\r
264 else if (ret < 0) errors++;
\r
267 if (errors) return -1;
\r
271 static inline bool split_hook_name(const TCHAR *hook_name, TCHAR *hook_event, TCHAR *hook_action) {
\r
274 for (s = (TCHAR *) hook_name; *s; s++) {
\r
275 if (*s == _T('/')) {
\r
277 _sntprintf_s(hook_event, HOOK_NAME_LENGTH, _TRUNCATE, _T("%s"), hook_name);
\r
279 _sntprintf_s(hook_action, HOOK_NAME_LENGTH, _TRUNCATE, _T("%s"), s);
\r
280 return valid_hook_name(hook_event, hook_action, false);
\r
284 print_message(stderr, NSSM_MESSAGE_INVALID_HOOK_NAME, hook_name);
\r
288 static int setting_set_hook(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
289 TCHAR hook_event[HOOK_NAME_LENGTH];
\r
290 TCHAR hook_action[HOOK_NAME_LENGTH];
\r
291 if (! split_hook_name(additional, hook_event, hook_action)) return -1;
\r
294 if (value && value->string) cmd = value->string;
\r
297 if (set_hook(service_name, hook_event, hook_action, cmd)) return -1;
\r
298 if (! _tcslen(cmd)) return 0;
\r
302 static int setting_get_hook(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
303 TCHAR hook_event[HOOK_NAME_LENGTH];
\r
304 TCHAR hook_action[HOOK_NAME_LENGTH];
\r
305 if (! split_hook_name(additional, hook_event, hook_action)) return -1;
\r
307 TCHAR cmd[CMD_LENGTH];
\r
308 if (get_hook(service_name, hook_event, hook_action, cmd, sizeof(cmd))) return -1;
\r
310 value_from_string(name, value, cmd);
\r
312 if (! _tcslen(cmd)) return 0;
\r
316 static int setting_dump_hooks(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
320 for (i = 0; hook_event_strings[i]; i++) {
\r
321 const TCHAR *hook_event = hook_event_strings[i];
\r
322 for (j = 0; hook_action_strings[j]; j++) {
\r
323 const TCHAR *hook_action = hook_action_strings[j];
\r
324 if (! valid_hook_name(hook_event, hook_action, true)) continue;
\r
326 TCHAR hook_name[HOOK_NAME_LENGTH];
\r
327 _sntprintf_s(hook_name, _countof(hook_name), _TRUNCATE, _T("%s/%s"), hook_event, hook_action);
\r
329 int ret = setting_get_hook(service_name, param, name, default_value, value, hook_name);
\r
331 if (ret < 0) errors++;
\r
335 if (setting_dump_string(service_name, (void *) REG_SZ, name, value, hook_name)) errors++;
\r
339 if (errors) return -1;
\r
343 static int setting_set_affinity(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
344 HKEY key = (HKEY) param;
\r
345 if (! key) return -1;
\r
349 __int64 system_affinity = 0LL;
\r
351 if (value && value->string) {
\r
352 DWORD_PTR affinity;
\r
353 if (! GetProcessAffinityMask(GetCurrentProcess(), &affinity, (DWORD_PTR *) &system_affinity)) system_affinity = ~0;
\r
355 if (is_default(value->string) || str_equiv(value->string, NSSM_AFFINITY_ALL)) mask = 0LL;
\r
356 else if (affinity_string_to_mask(value->string, &mask)) {
\r
357 print_message(stderr, NSSM_MESSAGE_BOGUS_AFFINITY_MASK, value->string, num_cpus() - 1);
\r
364 error = RegDeleteValue(key, name);
\r
365 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
\r
366 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
\r
370 /* Canonicalise. */
\r
372 if (affinity_mask_to_string(mask, &canon)) canon = value->string;
\r
374 __int64 effective_affinity = mask & system_affinity;
\r
375 if (effective_affinity != mask) {
\r
376 /* Requested CPUs did not intersect with available CPUs? */
\r
377 if (! effective_affinity) mask = effective_affinity = system_affinity;
\r
380 if (! affinity_mask_to_string(system_affinity, &system)) {
\r
381 TCHAR *effective = 0;
\r
382 if (! affinity_mask_to_string(effective_affinity, &effective)) {
\r
383 print_message(stderr, NSSM_MESSAGE_EFFECTIVE_AFFINITY_MASK, value->string, system, effective);
\r
384 HeapFree(GetProcessHeap(), 0, effective);
\r
386 HeapFree(GetProcessHeap(), 0, system);
\r
390 if (RegSetValueEx(key, name, 0, REG_SZ, (const unsigned char *) canon, (unsigned long) (_tcslen(canon) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
391 if (canon != value->string) HeapFree(GetProcessHeap(), 0, canon);
\r
392 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, name, error_string(GetLastError()), 0);
\r
396 if (canon != value->string) HeapFree(GetProcessHeap(), 0, canon);
\r
400 static int setting_get_affinity(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
401 HKEY key = (HKEY) param;
\r
402 if (! key) return -1;
\r
404 unsigned long type;
\r
406 unsigned long buflen = 0;
\r
408 int ret = RegQueryValueEx(key, name, 0, &type, 0, &buflen);
\r
409 if (ret == ERROR_FILE_NOT_FOUND) {
\r
410 if (value_from_string(name, value, NSSM_AFFINITY_ALL) == 1) return 0;
\r
413 if (ret != ERROR_SUCCESS) return -1;
\r
415 if (type != REG_SZ) return -1;
\r
417 buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, buflen);
\r
419 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("affinity"), _T("setting_get_affinity"));
\r
423 if (get_string(key, (TCHAR *) name, buffer, buflen, false, false, true)) {
\r
424 HeapFree(GetProcessHeap(), 0, buffer);
\r
429 if (affinity_string_to_mask(buffer, &affinity)) {
\r
430 print_message(stderr, NSSM_MESSAGE_BOGUS_AFFINITY_MASK, buffer, num_cpus() - 1);
\r
431 HeapFree(GetProcessHeap(), 0, buffer);
\r
435 HeapFree(GetProcessHeap(), 0, buffer);
\r
437 /* Canonicalise. */
\r
438 if (affinity_mask_to_string(affinity, &buffer)) {
\r
439 if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
\r
443 ret = value_from_string(name, value, buffer);
\r
444 HeapFree(GetProcessHeap(), 0, buffer);
\r
448 static int setting_set_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
449 HKEY key = (HKEY) param;
\r
450 if (! param) return -1;
\r
453 TCHAR *unformatted = 0;
\r
454 unsigned long envlen;
\r
455 unsigned long newlen = 0;
\r
457 if (value && value->string && value->string[0]) {
\r
458 string = value->string;
\r
459 switch (string[0]) {
\r
460 case _T('+'): op = 1; break;
\r
461 case _T('-'): op = -1; break;
\r
462 case _T(':'): string++; break;
\r
469 if (get_environment((TCHAR *) service_name, key, (TCHAR *) name, &env, &envlen)) return -1;
\r
472 if (op > 0) ret = append_to_environment_block(env, envlen, string, &unformatted, &newlen);
\r
473 else ret = remove_from_environment_block(env, envlen, string, &unformatted, &newlen);
\r
474 if (envlen) HeapFree(GetProcessHeap(), 0, env);
\r
475 if (ret) return -1;
\r
477 string = unformatted;
\r
481 No existing environment.
\r
482 We can't remove from an empty environment so just treat an add
\r
483 operation as setting a new string.
\r
485 if (op < 0) return 0;
\r
490 if (! string || ! string[0]) {
\r
491 long error = RegDeleteValue(key, name);
\r
492 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
\r
493 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
\r
498 if (unformat_double_null(string, (unsigned long) _tcslen(string), &unformatted, &newlen)) return -1;
\r
501 if (test_environment(unformatted)) {
\r
502 HeapFree(GetProcessHeap(), 0, unformatted);
\r
503 print_message(stderr, NSSM_GUI_INVALID_ENVIRONMENT);
\r
507 if (RegSetValueEx(key, name, 0, REG_MULTI_SZ, (const unsigned char *) unformatted, (unsigned long) newlen * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
508 if (newlen) HeapFree(GetProcessHeap(), 0, unformatted);
\r
509 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV, error_string(GetLastError()), 0);
\r
513 if (newlen) HeapFree(GetProcessHeap(), 0, unformatted);
\r
517 static int setting_get_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
518 HKEY key = (HKEY) param;
\r
519 if (! param) return -1;
\r
522 unsigned long envlen;
\r
523 if (get_environment((TCHAR *) service_name, key, (TCHAR *) name, &env, &envlen)) return -1;
\r
524 if (! envlen) return 0;
\r
527 unsigned long newlen;
\r
528 if (format_double_null(env, envlen, &formatted, &newlen)) return -1;
\r
532 /* Find named environment variable. */
\r
534 size_t len = _tcslen(additional);
\r
535 for (s = env; *s; s++) {
\r
536 /* Look for <additional>=<string> NULL NULL */
\r
537 if (! _tcsnicmp(s, additional, len) && s[len] == _T('=')) {
\r
540 ret = value_from_string(name, value, s);
\r
541 HeapFree(GetProcessHeap(), 0, env);
\r
545 /* Skip this string. */
\r
548 HeapFree(GetProcessHeap(), 0, env);
\r
552 HeapFree(GetProcessHeap(), 0, env);
\r
554 ret = value_from_string(name, value, formatted);
\r
555 if (newlen) HeapFree(GetProcessHeap(), 0, formatted);
\r
559 static int setting_dump_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
561 HKEY key = (HKEY) param;
\r
562 if (! param) return -1;
\r
565 unsigned long envlen;
\r
566 if (get_environment((TCHAR *) service_name, key, (TCHAR *) name, &env, &envlen)) return -1;
\r
567 if (! envlen) return 0;
\r
570 for (s = env; *s; s++) {
\r
571 size_t len = _tcslen(s) + 2;
\r
572 value->string = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));
\r
573 if (! value->string) {
\r
574 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("dump"), _T("setting_dump_environment"));
\r
578 _sntprintf_s(value->string, len, _TRUNCATE, _T("%c%s"), (s > env) ? _T('+') : _T(':'), s);
\r
579 if (setting_dump_string(service_name, (void *) REG_SZ, name, value, 0)) errors++;
\r
580 HeapFree(GetProcessHeap(), 0, value->string);
\r
586 HeapFree(GetProcessHeap(), 0, env);
\r
588 if (errors) return 1;
\r
592 static int setting_set_priority(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
593 HKEY key = (HKEY) param;
\r
594 if (! param) return -1;
\r
596 TCHAR *priority_string;
\r
600 if (value && value->string) priority_string = value->string;
\r
601 else if (default_value) priority_string = (TCHAR *) default_value;
\r
603 error = RegDeleteValue(key, name);
\r
604 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
\r
605 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
\r
609 for (i = 0; priority_strings[i]; i++) {
\r
610 if (! str_equiv(priority_strings[i], priority_string)) continue;
\r
612 if (default_value && str_equiv(priority_string, (TCHAR *) default_value)) {
\r
613 error = RegDeleteValue(key, name);
\r
614 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
\r
615 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
\r
619 if (set_number(key, (TCHAR *) name, priority_index_to_constant(i))) return -1;
\r
623 print_message(stderr, NSSM_MESSAGE_INVALID_PRIORITY, priority_string);
\r
624 for (i = 0; priority_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), priority_strings[i]);
\r
629 static int setting_get_priority(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
630 HKEY key = (HKEY) param;
\r
631 if (! param) return -1;
\r
633 unsigned long constant;
\r
634 switch (get_number(key, (TCHAR *) name, &constant, false)) {
\r
636 if (value_from_string(name, value, (const TCHAR *) default_value) == -1) return -1;
\r
638 case -1: return -1;
\r
641 return value_from_string(name, value, priority_strings[priority_constant_to_index(constant)]);
\r
644 static int setting_dump_priority(const TCHAR *service_name, void *key_ptr, const TCHAR *name, void *setting_ptr, value_t *value, const TCHAR *additional) {
\r
645 settings_t *setting = (settings_t *) setting_ptr;
\r
646 int ret = setting_get_priority(service_name, key_ptr, name, (void *) setting->default_value, value, 0);
\r
647 if (ret != 1) return ret;
\r
648 return setting_dump_string(service_name, (void *) REG_SZ, name, value, 0);
\r
651 /* Functions to manage native service settings. */
\r
652 static int native_set_dependon(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR **dependencies, unsigned long *dependencieslen, value_t *value, int type) {
\r
653 *dependencieslen = 0;
\r
654 if (! value || ! value->string || ! value->string[0]) return 0;
\r
656 TCHAR *string = value->string;
\r
657 unsigned long buflen;
\r
659 switch (string[0]) {
\r
660 case _T('+'): op = 1; break;
\r
661 case _T('-'): op = -1; break;
\r
662 case _T(':'): string++; break;
\r
668 if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, type)) return -1;
\r
671 if (op > 0) ret = append_to_dependencies(buffer, buflen, string, dependencies, dependencieslen, type);
\r
672 else ret = remove_from_dependencies(buffer, buflen, string, dependencies, dependencieslen, type);
\r
673 if (buflen) HeapFree(GetProcessHeap(), 0, buffer);
\r
679 We can't remove from an empty list so just treat an add
\r
680 operation as setting a new string.
\r
682 if (op < 0) return 0;
\r
688 TCHAR *unformatted = 0;
\r
689 unsigned long newlen;
\r
690 if (unformat_double_null(string, (unsigned long) _tcslen(string), &unformatted, &newlen)) return -1;
\r
692 if (type == DEPENDENCY_GROUPS) {
\r
693 /* Prepend group identifier. */
\r
694 unsigned long missing = 0;
\r
695 TCHAR *canon = unformatted;
\r
696 size_t canonlen = 0;
\r
698 for (s = unformatted; *s; s++) {
\r
699 if (*s != SC_GROUP_IDENTIFIER) missing++;
\r
700 size_t len = _tcslen(s);
\r
701 canonlen += len + 1;
\r
706 /* Missing identifiers plus double NULL terminator. */
\r
707 canonlen += missing + 1;
\r
709 canon = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, canonlen * sizeof(TCHAR));
\r
711 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canon"), _T("native_set_dependon"));
\r
712 if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);
\r
717 for (s = unformatted; *s; s++) {
\r
718 if (*s != SC_GROUP_IDENTIFIER) canon[i++] = SC_GROUP_IDENTIFIER;
\r
719 size_t len = _tcslen(s);
\r
720 memmove(canon + i, s, (len + 1) * sizeof(TCHAR));
\r
725 HeapFree(GetProcessHeap(), 0, unformatted);
\r
726 unformatted = canon;
\r
727 newlen = (unsigned long) canonlen;
\r
731 *dependencies = unformatted;
\r
732 *dependencieslen = newlen;
\r
738 static int native_set_dependongroup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
739 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
740 if (! service_handle) return -1;
\r
743 Get existing service dependencies because we must set both types together.
\r
745 TCHAR *services_buffer;
\r
746 unsigned long services_buflen;
\r
747 if (get_service_dependencies(service_name, service_handle, &services_buffer, &services_buflen, DEPENDENCY_SERVICES)) return -1;
\r
749 if (! value || ! value->string || ! value->string[0]) {
\r
750 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, services_buffer, 0, 0, 0)) {
\r
751 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
752 if (services_buffer) HeapFree(GetProcessHeap(), 0, services_buffer);
\r
756 if (services_buffer) HeapFree(GetProcessHeap(), 0, services_buffer);
\r
760 /* Update the group list. */
\r
761 TCHAR *groups_buffer;
\r
762 unsigned long groups_buflen;
\r
763 if (native_set_dependon(service_name, service_handle, &groups_buffer, &groups_buflen, value, DEPENDENCY_GROUPS)) return -1;
\r
765 TCHAR *dependencies;
\r
766 if (services_buflen > 2) {
\r
767 dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (groups_buflen + services_buflen) * sizeof(TCHAR));
\r
768 if (! dependencies) {
\r
769 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("dependencies"), _T("native_set_dependongroup"));
\r
770 if (groups_buffer) HeapFree(GetProcessHeap(), 0, groups_buffer);
\r
771 if (services_buffer) HeapFree(GetProcessHeap(), 0, services_buffer);
\r
775 memmove(dependencies, services_buffer, services_buflen * sizeof(TCHAR));
\r
776 memmove(dependencies + services_buflen - 1, groups_buffer, groups_buflen * sizeof(TCHAR));
\r
778 else dependencies = groups_buffer;
\r
781 if (set_service_dependencies(service_name, service_handle, dependencies)) ret = -1;
\r
782 if (dependencies != groups_buffer) HeapFree(GetProcessHeap(), 0, dependencies);
\r
783 if (groups_buffer) HeapFree(GetProcessHeap(), 0, groups_buffer);
\r
784 if (services_buffer) HeapFree(GetProcessHeap(), 0, services_buffer);
\r
789 static int native_get_dependongroup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
790 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
791 if (! service_handle) return -1;
\r
794 unsigned long buflen;
\r
795 if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_GROUPS)) return -1;
\r
800 unsigned long newlen;
\r
801 if (format_double_null(buffer, buflen, &formatted, &newlen)) {
\r
802 HeapFree(GetProcessHeap(), 0, buffer);
\r
806 ret = value_from_string(name, value, formatted);
\r
807 HeapFree(GetProcessHeap(), 0, formatted);
\r
808 HeapFree(GetProcessHeap(), 0, buffer);
\r
818 static int setting_dump_dependon(const TCHAR *service_name, SC_HANDLE service_handle, const TCHAR *name, int type, value_t *value) {
\r
821 TCHAR *dependencies = 0;
\r
822 unsigned long dependencieslen;
\r
823 if (get_service_dependencies(service_name, service_handle, &dependencies, &dependencieslen, type)) return -1;
\r
824 if (! dependencieslen) return 0;
\r
827 for (s = dependencies; *s; s++) {
\r
828 size_t len = _tcslen(s) + 2;
\r
829 value->string = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));
\r
830 if (! value->string) {
\r
831 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("dump"), _T("setting_dump_dependon"));
\r
835 _sntprintf_s(value->string, len, _TRUNCATE, _T("%c%s"), (s > dependencies) ? _T('+') : _T(':'), s);
\r
836 if (setting_dump_string(service_name, (void *) REG_SZ, name, value, 0)) errors++;
\r
837 HeapFree(GetProcessHeap(), 0, value->string);
\r
843 HeapFree(GetProcessHeap(), 0, dependencies);
\r
845 if (errors) return 1;
\r
849 static int native_dump_dependongroup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
850 return setting_dump_dependon(service_name, (SC_HANDLE) param, name, DEPENDENCY_GROUPS, value);
\r
853 static int native_set_dependonservice(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
854 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
855 if (! service_handle) return -1;
\r
858 Get existing group dependencies because we must set both types together.
\r
860 TCHAR *groups_buffer;
\r
861 unsigned long groups_buflen;
\r
862 if (get_service_dependencies(service_name, service_handle, &groups_buffer, &groups_buflen, DEPENDENCY_GROUPS)) return -1;
\r
864 if (! value || ! value->string || ! value->string[0]) {
\r
865 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, groups_buffer, 0, 0, 0)) {
\r
866 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
867 if (groups_buffer) HeapFree(GetProcessHeap(), 0, groups_buffer);
\r
871 if (groups_buffer) HeapFree(GetProcessHeap(), 0, groups_buffer);
\r
875 /* Update the service list. */
\r
876 TCHAR *services_buffer;
\r
877 unsigned long services_buflen;
\r
878 if (native_set_dependon(service_name, service_handle, &services_buffer, &services_buflen, value, DEPENDENCY_SERVICES)) return -1;
\r
880 TCHAR *dependencies;
\r
881 if (groups_buflen > 2) {
\r
882 dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (services_buflen + groups_buflen) * sizeof(TCHAR));
\r
883 if (! dependencies) {
\r
884 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("dependencies"), _T("native_set_dependonservice"));
\r
885 if (groups_buffer) HeapFree(GetProcessHeap(), 0, groups_buffer);
\r
886 if (services_buffer) HeapFree(GetProcessHeap(), 0, services_buffer);
\r
890 memmove(dependencies, services_buffer, services_buflen * sizeof(TCHAR));
\r
891 memmove(dependencies + services_buflen - 1, groups_buffer, groups_buflen * sizeof(TCHAR));
\r
893 else dependencies = services_buffer;
\r
896 if (set_service_dependencies(service_name, service_handle, dependencies)) ret = -1;
\r
897 if (dependencies != services_buffer) HeapFree(GetProcessHeap(), 0, dependencies);
\r
898 if (groups_buffer) HeapFree(GetProcessHeap(), 0, groups_buffer);
\r
899 if (services_buffer) HeapFree(GetProcessHeap(), 0, services_buffer);
\r
904 static int native_get_dependonservice(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
905 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
906 if (! service_handle) return -1;
\r
909 unsigned long buflen;
\r
910 if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_SERVICES)) return -1;
\r
915 unsigned long newlen;
\r
916 if (format_double_null(buffer, buflen, &formatted, &newlen)) {
\r
917 HeapFree(GetProcessHeap(), 0, buffer);
\r
921 ret = value_from_string(name, value, formatted);
\r
922 HeapFree(GetProcessHeap(), 0, formatted);
\r
923 HeapFree(GetProcessHeap(), 0, buffer);
\r
933 static int native_dump_dependonservice(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
934 return setting_dump_dependon(service_name, (SC_HANDLE) param, name, DEPENDENCY_SERVICES, value);
\r
937 int native_set_description(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
938 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
939 if (! service_handle) return -1;
\r
941 TCHAR *description = 0;
\r
942 if (value) description = value->string;
\r
943 if (set_service_description(service_name, service_handle, description)) return -1;
\r
945 if (description && description[0]) return 1;
\r
950 int native_get_description(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
951 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
952 if (! service_handle) return -1;
\r
954 TCHAR buffer[VALUE_LENGTH];
\r
955 if (get_service_description(service_name, service_handle, _countof(buffer), buffer)) return -1;
\r
957 if (buffer[0]) return value_from_string(name, value, buffer);
\r
963 int native_set_displayname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
964 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
965 if (! service_handle) return -1;
\r
967 TCHAR *displayname = 0;
\r
968 if (value && value->string) displayname = value->string;
\r
969 else displayname = (TCHAR *) service_name;
\r
971 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, displayname)) {
\r
972 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
977 If the display name and service name differ only in case,
\r
978 ChangeServiceConfig() will return success but the display name will be
\r
979 set to the service name, NOT the value passed to the function.
\r
980 This appears to be a quirk of Windows rather than a bug here.
\r
982 if (displayname != service_name && ! str_equiv(displayname, service_name)) return 1;
\r
987 int native_get_displayname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
988 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
989 if (! service_handle) return -1;
\r
991 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
992 if (! qsc) return -1;
\r
994 int ret = value_from_string(name, value, qsc->lpDisplayName);
\r
995 HeapFree(GetProcessHeap(), 0, qsc);
\r
1000 int native_set_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
1001 HKEY key = open_service_registry(service_name, KEY_SET_VALUE, true);
\r
1002 if (! key) return -1;
\r
1004 int ret = setting_set_environment(service_name, (void *) key, name, default_value, value, additional);
\r
1009 int native_get_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
1010 HKEY key = open_service_registry(service_name, KEY_READ, true);
\r
1011 if (! key) return -1;
\r
1013 ZeroMemory(value, sizeof(value_t));
\r
1014 int ret = setting_get_environment(service_name, (void *) key, name, default_value, value, additional);
\r
1019 static int native_dump_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
1020 HKEY key = open_service_registry(service_name, KEY_READ, true);
\r
1021 if (! key) return -1;
\r
1023 int ret = setting_dump_environment(service_name, (void *) key, name, default_value, value, additional);
\r
1028 int native_set_imagepath(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
1029 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
1030 if (! service_handle) return -1;
\r
1032 /* It makes no sense to try to reset the image path. */
\r
1033 if (! value || ! value->string) {
\r
1034 print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);
\r
1038 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, value->string, 0, 0, 0, 0, 0, 0)) {
\r
1039 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
1046 int native_get_imagepath(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
1047 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
1048 if (! service_handle) return -1;
\r
1050 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
1051 if (! qsc) return -1;
\r
1053 int ret = value_from_string(name, value, qsc->lpBinaryPathName);
\r
1054 HeapFree(GetProcessHeap(), 0, qsc);
\r
1059 int native_set_name(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
1060 print_message(stderr, NSSM_MESSAGE_CANNOT_RENAME_SERVICE);
\r
1064 int native_get_name(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
1065 return value_from_string(name, value, service_name);
\r
1068 int native_set_objectname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
1069 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
1070 if (! service_handle) return -1;
\r
1073 Logical syntax is: nssm set <service> ObjectName <username> <password>
\r
1074 That means the username is actually passed in the additional parameter.
\r
1076 bool localsystem = false;
\r
1077 bool virtual_account = false;
\r
1078 TCHAR *username = NSSM_LOCALSYSTEM_ACCOUNT;
\r
1079 TCHAR *password = 0;
\r
1081 username = (TCHAR *) additional;
\r
1082 if (value && value->string) password = value->string;
\r
1084 else if (value && value->string) username = value->string;
\r
1086 const TCHAR *well_known = well_known_username(username);
\r
1087 size_t passwordsize = 0;
\r
1089 if (str_equiv(well_known, NSSM_LOCALSYSTEM_ACCOUNT)) localsystem = true;
\r
1090 username = (TCHAR *) well_known;
\r
1091 password = _T("");
\r
1093 else if (is_virtual_account(service_name, username)) virtual_account = true;
\r
1094 else if (! password) {
\r
1095 /* We need a password if the account requires it. */
\r
1096 print_message(stderr, NSSM_MESSAGE_MISSING_PASSWORD, name);
\r
1099 else passwordsize = _tcslen(password) * sizeof(TCHAR);
\r
1102 ChangeServiceConfig() will fail to set the username if the service is set
\r
1103 to interact with the desktop.
\r
1105 unsigned long type = SERVICE_NO_CHANGE;
\r
1106 if (! localsystem) {
\r
1107 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
1109 if (passwordsize) SecureZeroMemory(password, passwordsize);
\r
1113 type = qsc->dwServiceType & ~SERVICE_INTERACTIVE_PROCESS;
\r
1114 HeapFree(GetProcessHeap(), 0, qsc);
\r
1117 if (! well_known && ! virtual_account) {
\r
1118 if (grant_logon_as_service(username)) {
\r
1119 if (passwordsize) SecureZeroMemory(password, passwordsize);
\r
1120 print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);
\r
1125 if (! ChangeServiceConfig(service_handle, type, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, username, password, 0)) {
\r
1126 if (passwordsize) SecureZeroMemory(password, passwordsize);
\r
1127 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
1131 if (passwordsize) SecureZeroMemory(password, passwordsize);
\r
1133 if (localsystem) return 0;
\r
1138 int native_get_objectname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
1139 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
1140 if (! service_handle) return -1;
\r
1142 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
1143 if (! qsc) return -1;
\r
1145 int ret = value_from_string(name, value, qsc->lpServiceStartName);
\r
1146 HeapFree(GetProcessHeap(), 0, qsc);
\r
1151 int native_dump_objectname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
1152 int ret = native_get_objectname(service_name, param, name, default_value, value, additional);
\r
1153 if (ret != 1) return ret;
\r
1155 /* Properly checking for a virtual account requires the actual service name. */
\r
1156 if (! _tcsnicmp(NSSM_VIRTUAL_SERVICE_ACCOUNT_DOMAIN, value->string, _tcslen(NSSM_VIRTUAL_SERVICE_ACCOUNT_DOMAIN))) {
\r
1157 TCHAR *name = virtual_account(service_name);
\r
1158 if (! name) return -1;
\r
1159 HeapFree(GetProcessHeap(), 0, value->string);
\r
1160 value->string = name;
\r
1163 /* Do we need to dump a dummy password? */
\r
1164 if (! well_known_username(value->string)) {
\r
1165 /* Parameters are the other way round. */
\r
1167 inverted.string = _T("****");
\r
1168 return setting_dump_string(service_name, (void *) REG_SZ, name, &inverted, value->string);
\r
1171 return setting_dump_string(service_name, (void *) REG_SZ, name, value, 0);
\r
1174 int native_set_startup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
1175 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
1176 if (! service_handle) return -1;
\r
1178 /* It makes no sense to try to reset the startup type. */
\r
1179 if (! value || ! value->string) {
\r
1180 print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);
\r
1184 /* Map NSSM_STARTUP_* constant to Windows SERVICE_*_START constant. */
\r
1185 int service_startup = -1;
\r
1187 for (i = 0; startup_strings[i]; i++) {
\r
1188 if (str_equiv(value->string, startup_strings[i])) {
\r
1189 service_startup = i;
\r
1194 if (service_startup < 0) {
\r
1195 print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE_STARTUP, value->string);
\r
1196 for (i = 0; startup_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), startup_strings[i]);
\r
1200 unsigned long startup;
\r
1201 switch (service_startup) {
\r
1202 case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;
\r
1203 case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;
\r
1204 default: startup = SERVICE_AUTO_START;
\r
1207 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, startup, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) {
\r
1208 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
1212 SERVICE_DELAYED_AUTO_START_INFO delayed;
\r
1213 ZeroMemory(&delayed, sizeof(delayed));
\r
1214 if (service_startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;
\r
1215 else delayed.fDelayedAutostart = 0;
\r
1216 if (! ChangeServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {
\r
1217 unsigned long error = GetLastError();
\r
1218 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
1219 if (error != ERROR_INVALID_LEVEL) {
\r
1220 log_event(EVENTLOG_ERROR_TYPE, NSSM_MESSAGE_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service_name, error_string(error), 0);
\r
1227 int native_get_startup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
1228 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
1229 if (! service_handle) return -1;
\r
1231 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
1232 if (! qsc) return -1;
\r
1234 unsigned long startup;
\r
1235 int ret = get_service_startup(service_name, service_handle, qsc, &startup);
\r
1236 HeapFree(GetProcessHeap(), 0, qsc);
\r
1238 if (ret) return -1;
\r
1241 for (i = 0; startup_strings[i]; i++);
\r
1242 if (startup >= i) return -1;
\r
1244 return value_from_string(name, value, startup_strings[startup]);
\r
1247 int native_set_type(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
1248 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
1249 if (! service_handle) return -1;
\r
1251 /* It makes no sense to try to reset the service type. */
\r
1252 if (! value || ! value->string) {
\r
1253 print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);
\r
1258 We can only manage services of type SERVICE_WIN32_OWN_PROCESS
\r
1259 and SERVICE_INTERACTIVE_PROCESS.
\r
1261 unsigned long type = SERVICE_WIN32_OWN_PROCESS;
\r
1262 if (str_equiv(value->string, NSSM_INTERACTIVE_PROCESS)) type |= SERVICE_INTERACTIVE_PROCESS;
\r
1263 else if (! str_equiv(value->string, NSSM_WIN32_OWN_PROCESS)) {
\r
1264 print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE_TYPE, value->string);
\r
1265 _ftprintf(stderr, _T("%s\n"), NSSM_WIN32_OWN_PROCESS);
\r
1266 _ftprintf(stderr, _T("%s\n"), NSSM_INTERACTIVE_PROCESS);
\r
1271 ChangeServiceConfig() will fail if the service runs under an account
\r
1272 other than LOCALSYSTEM and we try to make it interactive.
\r
1274 if (type & SERVICE_INTERACTIVE_PROCESS) {
\r
1275 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
1276 if (! qsc) return -1;
\r
1278 if (! str_equiv(qsc->lpServiceStartName, NSSM_LOCALSYSTEM_ACCOUNT)) {
\r
1279 HeapFree(GetProcessHeap(), 0, qsc);
\r
1280 print_message(stderr, NSSM_MESSAGE_INTERACTIVE_NOT_LOCALSYSTEM, value->string, service_name, NSSM_LOCALSYSTEM_ACCOUNT);
\r
1284 HeapFree(GetProcessHeap(), 0, qsc);
\r
1287 if (! ChangeServiceConfig(service_handle, type, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) {
\r
1288 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
1295 int native_get_type(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
1296 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
1297 if (! service_handle) return -1;
\r
1299 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
1300 if (! qsc) return -1;
\r
1302 value->numeric = qsc->dwServiceType;
\r
1303 HeapFree(GetProcessHeap(), 0, qsc);
\r
1305 const TCHAR *string;
\r
1306 switch (value->numeric) {
\r
1307 case SERVICE_KERNEL_DRIVER: string = NSSM_KERNEL_DRIVER; break;
\r
1308 case SERVICE_FILE_SYSTEM_DRIVER: string = NSSM_FILE_SYSTEM_DRIVER; break;
\r
1309 case SERVICE_WIN32_OWN_PROCESS: string = NSSM_WIN32_OWN_PROCESS; break;
\r
1310 case SERVICE_WIN32_SHARE_PROCESS: string = NSSM_WIN32_SHARE_PROCESS; break;
\r
1311 case SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS: string = NSSM_INTERACTIVE_PROCESS; break;
\r
1312 case SERVICE_WIN32_SHARE_PROCESS|SERVICE_INTERACTIVE_PROCESS: string = NSSM_SHARE_INTERACTIVE_PROCESS; break;
\r
1313 default: string = NSSM_UNKNOWN;
\r
1316 return value_from_string(name, value, string);
\r
1319 int set_setting(const TCHAR *service_name, HKEY key, settings_t *setting, value_t *value, const TCHAR *additional) {
\r
1320 if (! key) return -1;
\r
1323 if (setting->set) ret = setting->set(service_name, (void *) key, setting->name, setting->default_value, value, additional);
\r
1326 if (! ret) print_message(stdout, NSSM_MESSAGE_RESET_SETTING, setting->name, service_name);
\r
1327 else if (ret > 0) print_message(stdout, NSSM_MESSAGE_SET_SETTING, setting->name, service_name);
\r
1328 else print_message(stderr, NSSM_MESSAGE_SET_SETTING_FAILED, setting->name, service_name);
\r
1333 int set_setting(const TCHAR *service_name, SC_HANDLE service_handle, settings_t *setting, value_t *value, const TCHAR *additional) {
\r
1334 if (! service_handle) return -1;
\r
1337 if (setting->set) ret = setting->set(service_name, service_handle, setting->name, setting->default_value, value, additional);
\r
1340 if (! ret) print_message(stdout, NSSM_MESSAGE_RESET_SETTING, setting->name, service_name);
\r
1341 else if (ret > 0) print_message(stdout, NSSM_MESSAGE_SET_SETTING, setting->name, service_name);
\r
1342 else print_message(stderr, NSSM_MESSAGE_SET_SETTING_FAILED, setting->name, service_name);
\r
1348 Returns: 1 if the value was retrieved.
\r
1349 0 if the default value was retrieved.
\r
1352 int get_setting(const TCHAR *service_name, HKEY key, settings_t *setting, value_t *value, const TCHAR *additional) {
\r
1353 if (! key) return -1;
\r
1356 if (is_string_type(setting->type)) {
\r
1357 value->string = (TCHAR *) setting->default_value;
\r
1358 if (setting->get) ret = setting->get(service_name, (void *) key, setting->name, setting->default_value, value, additional);
\r
1361 else if (is_numeric_type(setting->type)) {
\r
1362 value->numeric = PtrToUlong(setting->default_value);
\r
1363 if (setting->get) ret = setting->get(service_name, (void *) key, setting->name, setting->default_value, value, additional);
\r
1368 if (ret < 0) print_message(stderr, NSSM_MESSAGE_GET_SETTING_FAILED, setting->name, service_name);
\r
1373 int get_setting(const TCHAR *service_name, SC_HANDLE service_handle, settings_t *setting, value_t *value, const TCHAR *additional) {
\r
1374 if (! service_handle) return -1;
\r
1375 return setting->get(service_name, service_handle, setting->name, 0, value, additional);
\r
1378 int dump_setting(const TCHAR *service_name, HKEY key, SC_HANDLE service_handle, settings_t *setting) {
\r
1380 if (setting->native) {
\r
1381 if (! service_handle) return -1;
\r
1382 param = (void *) service_handle;
\r
1385 /* Will be null for native services. */
\r
1386 param = (void *) key;
\r
1389 value_t value = { 0 };
\r
1392 if (setting->dump) return setting->dump(service_name, param, setting->name, (void *) setting, &value, 0);
\r
1393 if (setting->native) ret = get_setting(service_name, service_handle, setting, &value, 0);
\r
1394 else ret = get_setting(service_name, key, setting, &value, 0);
\r
1395 if (ret != 1) return ret;
\r
1396 return setting_dump_string(service_name, (void *) setting->type, setting->name, &value, 0);
\r
1399 settings_t settings[] = {
\r
1400 { NSSM_REG_EXE, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string, setting_not_dumpable },
\r
1401 { NSSM_REG_FLAGS, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string, 0 },
\r
1402 { NSSM_REG_DIR, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string, 0 },
\r
1403 { NSSM_REG_EXIT, REG_SZ, (void *) exit_action_strings[NSSM_EXIT_RESTART], false, ADDITIONAL_MANDATORY, setting_set_exit_action, setting_get_exit_action, setting_dump_exit_action },
\r
1404 { NSSM_REG_HOOK, REG_SZ, (void *) _T(""), false, ADDITIONAL_MANDATORY, setting_set_hook, setting_get_hook, setting_dump_hooks },
\r
1405 { NSSM_REG_AFFINITY, REG_SZ, 0, false, 0, setting_set_affinity, setting_get_affinity, 0 },
\r
1406 { NSSM_REG_ENV, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment, setting_dump_environment },
\r
1407 { NSSM_REG_ENV_EXTRA, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment, setting_dump_environment },
\r
1408 { NSSM_REG_NO_CONSOLE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },
\r
1409 { NSSM_REG_PRIORITY, REG_SZ, (void *) priority_strings[NSSM_NORMAL_PRIORITY], false, 0, setting_set_priority, setting_get_priority, setting_dump_priority },
\r
1410 { NSSM_REG_RESTART_DELAY, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },
\r
1411 { NSSM_REG_STDIN, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string, 0 },
\r
1412 { NSSM_REG_STDIN NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDIN_SHARING, false, 0, setting_set_number, setting_get_number, 0 },
\r
1413 { NSSM_REG_STDIN NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDIN_DISPOSITION, false, 0, setting_set_number, setting_get_number, 0 },
\r
1414 { NSSM_REG_STDIN NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDIN_FLAGS, false, 0, setting_set_number, setting_get_number, 0 },
\r
1415 { NSSM_REG_STDOUT, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string, 0 },
\r
1416 { NSSM_REG_STDOUT NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDOUT_SHARING, false, 0, setting_set_number, setting_get_number, 0 },
\r
1417 { NSSM_REG_STDOUT NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDOUT_DISPOSITION, false, 0, setting_set_number, setting_get_number, 0 },
\r
1418 { NSSM_REG_STDOUT NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDOUT_FLAGS, false, 0, setting_set_number, setting_get_number, 0 },
\r
1419 { NSSM_REG_STDOUT NSSM_REG_STDIO_COPY_AND_TRUNCATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },
\r
1420 { NSSM_REG_STDERR, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string, 0 },
\r
1421 { NSSM_REG_STDERR NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDERR_SHARING, false, 0, setting_set_number, setting_get_number, 0 },
\r
1422 { NSSM_REG_STDERR NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDERR_DISPOSITION, false, 0, setting_set_number, setting_get_number, 0 },
\r
1423 { NSSM_REG_STDERR NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDERR_FLAGS, false, 0, setting_set_number, setting_get_number, 0 },
\r
1424 { NSSM_REG_STDERR NSSM_REG_STDIO_COPY_AND_TRUNCATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },
\r
1425 { NSSM_REG_STOP_METHOD_SKIP, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },
\r
1426 { NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_CONSOLE_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number, 0 },
\r
1427 { NSSM_REG_KILL_WINDOW_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_WINDOW_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number, 0 },
\r
1428 { NSSM_REG_KILL_THREADS_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_THREADS_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number, 0 },
\r
1429 { NSSM_REG_KILL_PROCESS_TREE, REG_DWORD, (void *) 1, false, 0, setting_set_number, setting_get_number, 0 },
\r
1430 { NSSM_REG_THROTTLE, REG_DWORD, (void *) NSSM_RESET_THROTTLE_RESTART, false, 0, setting_set_number, setting_get_number, 0 },
\r
1431 { NSSM_REG_HOOK_SHARE_OUTPUT_HANDLES, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },
\r
1432 { NSSM_REG_ROTATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },
\r
1433 { NSSM_REG_ROTATE_ONLINE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },
\r
1434 { NSSM_REG_ROTATE_SECONDS, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },
\r
1435 { NSSM_REG_ROTATE_BYTES_LOW, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },
\r
1436 { NSSM_REG_ROTATE_BYTES_HIGH, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },
\r
1437 { NSSM_REG_ROTATE_DELAY, REG_DWORD, (void *) NSSM_ROTATE_DELAY, false, 0, setting_set_number, setting_get_number, 0 },
\r
1438 { NSSM_REG_TIMESTAMP_LOG, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },
\r
1439 { NSSM_NATIVE_DEPENDONGROUP, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_dependongroup, native_get_dependongroup, native_dump_dependongroup },
\r
1440 { NSSM_NATIVE_DEPENDONSERVICE, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_dependonservice, native_get_dependonservice, native_dump_dependonservice },
\r
1441 { NSSM_NATIVE_DESCRIPTION, REG_SZ, _T(""), true, 0, native_set_description, native_get_description, 0 },
\r
1442 { NSSM_NATIVE_DISPLAYNAME, REG_SZ, NULL, true, 0, native_set_displayname, native_get_displayname, 0 },
\r
1443 { NSSM_NATIVE_ENVIRONMENT, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_environment, native_get_environment, native_dump_environment },
\r
1444 { NSSM_NATIVE_IMAGEPATH, REG_EXPAND_SZ, NULL, true, 0, native_set_imagepath, native_get_imagepath, setting_not_dumpable },
\r
1445 { NSSM_NATIVE_OBJECTNAME, REG_SZ, NSSM_LOCALSYSTEM_ACCOUNT, true, 0, native_set_objectname, native_get_objectname, native_dump_objectname },
\r
1446 { NSSM_NATIVE_NAME, REG_SZ, NULL, true, 0, native_set_name, native_get_name, setting_not_dumpable },
\r
1447 { NSSM_NATIVE_STARTUP, REG_SZ, NULL, true, 0, native_set_startup, native_get_startup, 0 },
\r
1448 { NSSM_NATIVE_TYPE, REG_SZ, NULL, true, 0, native_set_type, native_get_type, 0 },
\r
1449 { NULL, NULL, NULL, NULL, NULL }
\r