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 TCHAR *username = NSSM_LOCALSYSTEM_ACCOUNT;
\r
1078 TCHAR *password = 0;
\r
1080 username = (TCHAR *) additional;
\r
1081 if (value && value->string) password = value->string;
\r
1083 else if (value && value->string) username = value->string;
\r
1085 const TCHAR *well_known = well_known_username(username);
\r
1086 size_t passwordsize = 0;
\r
1088 if (str_equiv(well_known, NSSM_LOCALSYSTEM_ACCOUNT)) localsystem = true;
\r
1089 username = (TCHAR *) well_known;
\r
1090 password = _T("");
\r
1092 else if (! password) {
\r
1093 /* We need a password if the account requires it. */
\r
1094 print_message(stderr, NSSM_MESSAGE_MISSING_PASSWORD, name);
\r
1097 else passwordsize = _tcslen(password) * sizeof(TCHAR);
\r
1100 ChangeServiceConfig() will fail to set the username if the service is set
\r
1101 to interact with the desktop.
\r
1103 unsigned long type = SERVICE_NO_CHANGE;
\r
1104 if (! localsystem) {
\r
1105 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
1107 if (passwordsize) SecureZeroMemory(password, passwordsize);
\r
1111 type = qsc->dwServiceType & ~SERVICE_INTERACTIVE_PROCESS;
\r
1112 HeapFree(GetProcessHeap(), 0, qsc);
\r
1115 if (! well_known) {
\r
1116 if (grant_logon_as_service(username)) {
\r
1117 if (passwordsize) SecureZeroMemory(password, passwordsize);
\r
1118 print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);
\r
1123 if (! ChangeServiceConfig(service_handle, type, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, username, password, 0)) {
\r
1124 if (passwordsize) SecureZeroMemory(password, passwordsize);
\r
1125 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
1129 if (passwordsize) SecureZeroMemory(password, passwordsize);
\r
1131 if (localsystem) return 0;
\r
1136 int native_get_objectname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
1137 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
1138 if (! service_handle) return -1;
\r
1140 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
1141 if (! qsc) return -1;
\r
1143 int ret = value_from_string(name, value, qsc->lpServiceStartName);
\r
1144 HeapFree(GetProcessHeap(), 0, qsc);
\r
1149 int native_dump_objectname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
1150 int ret = native_get_objectname(service_name, param, name, default_value, value, additional);
\r
1151 if (ret != 1) return ret;
\r
1153 /* Do we need to dump a dummy password? */
\r
1154 if (! well_known_username(value->string)) {
\r
1155 /* Parameters are the other way round. */
\r
1157 inverted.string = _T("****");
\r
1158 return setting_dump_string(service_name, (void *) REG_SZ, name, &inverted, value->string);
\r
1160 return setting_dump_string(service_name, (void *) REG_SZ, name, value, 0);
\r
1163 int native_set_startup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
1164 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
1165 if (! service_handle) return -1;
\r
1167 /* It makes no sense to try to reset the startup type. */
\r
1168 if (! value || ! value->string) {
\r
1169 print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);
\r
1173 /* Map NSSM_STARTUP_* constant to Windows SERVICE_*_START constant. */
\r
1174 int service_startup = -1;
\r
1176 for (i = 0; startup_strings[i]; i++) {
\r
1177 if (str_equiv(value->string, startup_strings[i])) {
\r
1178 service_startup = i;
\r
1183 if (service_startup < 0) {
\r
1184 print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE_STARTUP, value->string);
\r
1185 for (i = 0; startup_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), startup_strings[i]);
\r
1189 unsigned long startup;
\r
1190 switch (service_startup) {
\r
1191 case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;
\r
1192 case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;
\r
1193 default: startup = SERVICE_AUTO_START;
\r
1196 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, startup, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) {
\r
1197 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
1201 SERVICE_DELAYED_AUTO_START_INFO delayed;
\r
1202 ZeroMemory(&delayed, sizeof(delayed));
\r
1203 if (service_startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;
\r
1204 else delayed.fDelayedAutostart = 0;
\r
1205 if (! ChangeServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {
\r
1206 unsigned long error = GetLastError();
\r
1207 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
1208 if (error != ERROR_INVALID_LEVEL) {
\r
1209 log_event(EVENTLOG_ERROR_TYPE, NSSM_MESSAGE_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service_name, error_string(error), 0);
\r
1216 int native_get_startup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
1217 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
1218 if (! service_handle) return -1;
\r
1220 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
1221 if (! qsc) return -1;
\r
1223 unsigned long startup;
\r
1224 int ret = get_service_startup(service_name, service_handle, qsc, &startup);
\r
1225 HeapFree(GetProcessHeap(), 0, qsc);
\r
1227 if (ret) return -1;
\r
1230 for (i = 0; startup_strings[i]; i++);
\r
1231 if (startup >= i) return -1;
\r
1233 return value_from_string(name, value, startup_strings[startup]);
\r
1236 int native_set_type(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
1237 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
1238 if (! service_handle) return -1;
\r
1240 /* It makes no sense to try to reset the service type. */
\r
1241 if (! value || ! value->string) {
\r
1242 print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);
\r
1247 We can only manage services of type SERVICE_WIN32_OWN_PROCESS
\r
1248 and SERVICE_INTERACTIVE_PROCESS.
\r
1250 unsigned long type = SERVICE_WIN32_OWN_PROCESS;
\r
1251 if (str_equiv(value->string, NSSM_INTERACTIVE_PROCESS)) type |= SERVICE_INTERACTIVE_PROCESS;
\r
1252 else if (! str_equiv(value->string, NSSM_WIN32_OWN_PROCESS)) {
\r
1253 print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE_TYPE, value->string);
\r
1254 _ftprintf(stderr, _T("%s\n"), NSSM_WIN32_OWN_PROCESS);
\r
1255 _ftprintf(stderr, _T("%s\n"), NSSM_INTERACTIVE_PROCESS);
\r
1260 ChangeServiceConfig() will fail if the service runs under an account
\r
1261 other than LOCALSYSTEM and we try to make it interactive.
\r
1263 if (type & SERVICE_INTERACTIVE_PROCESS) {
\r
1264 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
1265 if (! qsc) return -1;
\r
1267 if (! str_equiv(qsc->lpServiceStartName, NSSM_LOCALSYSTEM_ACCOUNT)) {
\r
1268 HeapFree(GetProcessHeap(), 0, qsc);
\r
1269 print_message(stderr, NSSM_MESSAGE_INTERACTIVE_NOT_LOCALSYSTEM, value->string, service_name, NSSM_LOCALSYSTEM_ACCOUNT);
\r
1273 HeapFree(GetProcessHeap(), 0, qsc);
\r
1276 if (! ChangeServiceConfig(service_handle, type, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) {
\r
1277 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
1284 int native_get_type(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
\r
1285 SC_HANDLE service_handle = (SC_HANDLE) param;
\r
1286 if (! service_handle) return -1;
\r
1288 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
1289 if (! qsc) return -1;
\r
1291 value->numeric = qsc->dwServiceType;
\r
1292 HeapFree(GetProcessHeap(), 0, qsc);
\r
1294 const TCHAR *string;
\r
1295 switch (value->numeric) {
\r
1296 case SERVICE_KERNEL_DRIVER: string = NSSM_KERNEL_DRIVER; break;
\r
1297 case SERVICE_FILE_SYSTEM_DRIVER: string = NSSM_FILE_SYSTEM_DRIVER; break;
\r
1298 case SERVICE_WIN32_OWN_PROCESS: string = NSSM_WIN32_OWN_PROCESS; break;
\r
1299 case SERVICE_WIN32_SHARE_PROCESS: string = NSSM_WIN32_SHARE_PROCESS; break;
\r
1300 case SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS: string = NSSM_INTERACTIVE_PROCESS; break;
\r
1301 case SERVICE_WIN32_SHARE_PROCESS|SERVICE_INTERACTIVE_PROCESS: string = NSSM_SHARE_INTERACTIVE_PROCESS; break;
\r
1302 default: string = NSSM_UNKNOWN;
\r
1305 return value_from_string(name, value, string);
\r
1308 int set_setting(const TCHAR *service_name, HKEY key, settings_t *setting, value_t *value, const TCHAR *additional) {
\r
1309 if (! key) return -1;
\r
1312 if (setting->set) ret = setting->set(service_name, (void *) key, setting->name, setting->default_value, value, additional);
\r
1315 if (! ret) print_message(stdout, NSSM_MESSAGE_RESET_SETTING, setting->name, service_name);
\r
1316 else if (ret > 0) print_message(stdout, NSSM_MESSAGE_SET_SETTING, setting->name, service_name);
\r
1317 else print_message(stderr, NSSM_MESSAGE_SET_SETTING_FAILED, setting->name, service_name);
\r
1322 int set_setting(const TCHAR *service_name, SC_HANDLE service_handle, settings_t *setting, value_t *value, const TCHAR *additional) {
\r
1323 if (! service_handle) return -1;
\r
1326 if (setting->set) ret = setting->set(service_name, service_handle, setting->name, setting->default_value, value, additional);
\r
1329 if (! ret) print_message(stdout, NSSM_MESSAGE_RESET_SETTING, setting->name, service_name);
\r
1330 else if (ret > 0) print_message(stdout, NSSM_MESSAGE_SET_SETTING, setting->name, service_name);
\r
1331 else print_message(stderr, NSSM_MESSAGE_SET_SETTING_FAILED, setting->name, service_name);
\r
1337 Returns: 1 if the value was retrieved.
\r
1338 0 if the default value was retrieved.
\r
1341 int get_setting(const TCHAR *service_name, HKEY key, settings_t *setting, value_t *value, const TCHAR *additional) {
\r
1342 if (! key) return -1;
\r
1345 if (is_string_type(setting->type)) {
\r
1346 value->string = (TCHAR *) setting->default_value;
\r
1347 if (setting->get) ret = setting->get(service_name, (void *) key, setting->name, setting->default_value, value, additional);
\r
1350 else if (is_numeric_type(setting->type)) {
\r
1351 value->numeric = PtrToUlong(setting->default_value);
\r
1352 if (setting->get) ret = setting->get(service_name, (void *) key, setting->name, setting->default_value, value, additional);
\r
1357 if (ret < 0) print_message(stderr, NSSM_MESSAGE_GET_SETTING_FAILED, setting->name, service_name);
\r
1362 int get_setting(const TCHAR *service_name, SC_HANDLE service_handle, settings_t *setting, value_t *value, const TCHAR *additional) {
\r
1363 if (! service_handle) return -1;
\r
1364 return setting->get(service_name, service_handle, setting->name, 0, value, additional);
\r
1367 int dump_setting(const TCHAR *service_name, HKEY key, SC_HANDLE service_handle, settings_t *setting) {
\r
1369 if (setting->native) {
\r
1370 if (! service_handle) return -1;
\r
1371 param = (void *) service_handle;
\r
1374 /* Will be null for native services. */
\r
1375 param = (void *) key;
\r
1378 value_t value = { 0 };
\r
1381 if (setting->dump) return setting->dump(service_name, param, setting->name, (void *) setting, &value, 0);
\r
1382 if (setting->native) ret = get_setting(service_name, service_handle, setting, &value, 0);
\r
1383 else ret = get_setting(service_name, key, setting, &value, 0);
\r
1384 if (ret != 1) return ret;
\r
1385 return setting_dump_string(service_name, (void *) setting->type, setting->name, &value, 0);
\r
1388 settings_t settings[] = {
\r
1389 { NSSM_REG_EXE, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string, setting_not_dumpable },
\r
1390 { NSSM_REG_FLAGS, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string, 0 },
\r
1391 { NSSM_REG_DIR, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string, 0 },
\r
1392 { 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
1393 { NSSM_REG_HOOK, REG_SZ, (void *) _T(""), false, ADDITIONAL_MANDATORY, setting_set_hook, setting_get_hook, setting_dump_hooks },
\r
1394 { NSSM_REG_AFFINITY, REG_SZ, 0, false, 0, setting_set_affinity, setting_get_affinity, 0 },
\r
1395 { NSSM_REG_ENV, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment, setting_dump_environment },
\r
1396 { NSSM_REG_ENV_EXTRA, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment, setting_dump_environment },
\r
1397 { NSSM_REG_NO_CONSOLE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },
\r
1398 { NSSM_REG_PRIORITY, REG_SZ, (void *) priority_strings[NSSM_NORMAL_PRIORITY], false, 0, setting_set_priority, setting_get_priority, setting_dump_priority },
\r
1399 { NSSM_REG_RESTART_DELAY, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },
\r
1400 { NSSM_REG_STDIN, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string, 0 },
\r
1401 { NSSM_REG_STDIN NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDIN_SHARING, false, 0, setting_set_number, setting_get_number, 0 },
\r
1402 { NSSM_REG_STDIN NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDIN_DISPOSITION, false, 0, setting_set_number, setting_get_number, 0 },
\r
1403 { NSSM_REG_STDIN NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDIN_FLAGS, false, 0, setting_set_number, setting_get_number, 0 },
\r
1404 { NSSM_REG_STDOUT, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string, 0 },
\r
1405 { NSSM_REG_STDOUT NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDOUT_SHARING, false, 0, setting_set_number, setting_get_number, 0 },
\r
1406 { NSSM_REG_STDOUT NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDOUT_DISPOSITION, false, 0, setting_set_number, setting_get_number, 0 },
\r
1407 { NSSM_REG_STDOUT NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDOUT_FLAGS, false, 0, setting_set_number, setting_get_number, 0 },
\r
1408 { NSSM_REG_STDOUT NSSM_REG_STDIO_COPY_AND_TRUNCATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },
\r
1409 { NSSM_REG_STDERR, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string, 0 },
\r
1410 { NSSM_REG_STDERR NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDERR_SHARING, false, 0, setting_set_number, setting_get_number, 0 },
\r
1411 { NSSM_REG_STDERR NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDERR_DISPOSITION, false, 0, setting_set_number, setting_get_number, 0 },
\r
1412 { NSSM_REG_STDERR NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDERR_FLAGS, false, 0, setting_set_number, setting_get_number, 0 },
\r
1413 { NSSM_REG_STDERR NSSM_REG_STDIO_COPY_AND_TRUNCATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },
\r
1414 { NSSM_REG_STOP_METHOD_SKIP, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },
\r
1415 { NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_CONSOLE_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number, 0 },
\r
1416 { NSSM_REG_KILL_WINDOW_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_WINDOW_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number, 0 },
\r
1417 { NSSM_REG_KILL_THREADS_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_THREADS_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number, 0 },
\r
1418 { NSSM_REG_KILL_PROCESS_TREE, REG_DWORD, (void *) 1, false, 0, setting_set_number, setting_get_number, 0 },
\r
1419 { NSSM_REG_THROTTLE, REG_DWORD, (void *) NSSM_RESET_THROTTLE_RESTART, false, 0, setting_set_number, setting_get_number, 0 },
\r
1420 { NSSM_REG_HOOK_SHARE_OUTPUT_HANDLES, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },
\r
1421 { NSSM_REG_ROTATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },
\r
1422 { NSSM_REG_ROTATE_ONLINE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },
\r
1423 { NSSM_REG_ROTATE_SECONDS, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },
\r
1424 { NSSM_REG_ROTATE_BYTES_LOW, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },
\r
1425 { NSSM_REG_ROTATE_BYTES_HIGH, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },
\r
1426 { NSSM_REG_ROTATE_DELAY, REG_DWORD, (void *) NSSM_ROTATE_DELAY, false, 0, setting_set_number, setting_get_number, 0 },
\r
1427 { NSSM_NATIVE_DEPENDONGROUP, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_dependongroup, native_get_dependongroup, native_dump_dependongroup },
\r
1428 { NSSM_NATIVE_DEPENDONSERVICE, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_dependonservice, native_get_dependonservice, native_dump_dependonservice },
\r
1429 { NSSM_NATIVE_DESCRIPTION, REG_SZ, _T(""), true, 0, native_set_description, native_get_description, 0 },
\r
1430 { NSSM_NATIVE_DISPLAYNAME, REG_SZ, NULL, true, 0, native_set_displayname, native_get_displayname, 0 },
\r
1431 { NSSM_NATIVE_ENVIRONMENT, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_environment, native_get_environment, native_dump_environment },
\r
1432 { NSSM_NATIVE_IMAGEPATH, REG_EXPAND_SZ, NULL, true, 0, native_set_imagepath, native_get_imagepath, setting_not_dumpable },
\r
1433 { NSSM_NATIVE_OBJECTNAME, REG_SZ, NSSM_LOCALSYSTEM_ACCOUNT, true, 0, native_set_objectname, native_get_objectname, native_dump_objectname },
\r
1434 { NSSM_NATIVE_NAME, REG_SZ, NULL, true, 0, native_set_name, native_get_name, setting_not_dumpable },
\r
1435 { NSSM_NATIVE_STARTUP, REG_SZ, NULL, true, 0, native_set_startup, native_get_startup, 0 },
\r
1436 { NSSM_NATIVE_TYPE, REG_SZ, NULL, true, 0, native_set_type, native_get_type, 0 },
\r
1437 { NULL, NULL, NULL, NULL, NULL }
\r