Handle well-known account names.
[nssm.git] / settings.cpp
1 #include "nssm.h"
2 /* XXX: (value && value->string) is probably bogus because value is probably never null */
3
4 /* Affinity. */
5 #define NSSM_AFFINITY_ALL _T("All")
6
7 extern const TCHAR *exit_action_strings[];
8 extern const TCHAR *startup_strings[];
9 extern const TCHAR *priority_strings[];
10
11 /* Does the parameter refer to the default value of the setting? */
12 static inline int is_default(const TCHAR *value) {
13   return (str_equiv(value, _T("default")) || str_equiv(value, _T("*")) || ! value[0]);
14 }
15
16 static int value_from_string(const TCHAR *name, value_t *value, const TCHAR *string) {
17   size_t len = _tcslen(string);
18   if (! len++) {
19     value->string = 0;
20     return 0;
21   }
22
23   value->string = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));
24   if (! value->string) {
25     print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, name, _T("value_from_string()"));
26     return -1;
27   }
28
29   if (_sntprintf_s(value->string, len, _TRUNCATE, _T("%s"), string) < 0) {
30     HeapFree(GetProcessHeap(), 0, value->string);
31     print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, name, _T("value_from_string()"));
32     return -1;
33   }
34
35   return 1;
36 }
37
38 /* Functions to manage NSSM-specific settings in the registry. */
39 static int setting_set_number(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
40   HKEY key = (HKEY) param;
41   if (! key) return -1;
42
43   unsigned long number;
44   long error;
45
46   /* Resetting to default? */
47   if (! value || ! value->string) {
48     error = RegDeleteValue(key, name);
49     if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
50     print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
51     return -1;
52   }
53   if (str_number(value->string, &number)) return -1;
54
55   if (default_value && number == (unsigned long) default_value) {
56     error = RegDeleteValue(key, name);
57     if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
58     print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
59     return -1;
60   }
61
62   if (set_number(key, (TCHAR *) name, number)) return -1;
63
64   return 1;
65 }
66
67 static int setting_get_number(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
68   HKEY key = (HKEY) param;
69   return get_number(key, (TCHAR *) name, &value->numeric, false);
70 }
71
72 static int setting_set_string(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
73   HKEY key = (HKEY) param;
74   if (! key) return -1;
75
76   long error;
77
78   /* Resetting to default? */
79   if (! value || ! value->string) {
80     if (default_value) value->string = (TCHAR *) default_value;
81     else {
82       error = RegDeleteValue(key, name);
83       if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
84       print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
85       return -1;
86     }
87   }
88   if (default_value && _tcslen((TCHAR *) default_value) && str_equiv(value->string, (TCHAR *) default_value)) {
89     error = RegDeleteValue(key, name);
90     if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
91     print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
92     return -1;
93   }
94
95   if (set_expand_string(key, (TCHAR *) name, value->string)) return -1;
96
97   return 1;
98 }
99
100 static int setting_get_string(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
101   HKEY key = (HKEY) param;
102   TCHAR buffer[VALUE_LENGTH];
103
104   if (get_string(key, (TCHAR *) name, (TCHAR *) buffer, (unsigned long) sizeof(buffer), false, false, false)) return -1;
105
106   return value_from_string(name, value, buffer);
107 }
108
109 static int setting_set_exit_action(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
110   unsigned long exitcode;
111   TCHAR *code;
112   TCHAR action_string[ACTION_LEN];
113
114   if (additional) {
115     /* Default action? */
116     if (is_default(additional)) code = 0;
117     else {
118       if (str_number(additional, &exitcode)) return -1;
119       code = (TCHAR *) additional;
120     }
121   }
122
123   HKEY key = open_registry(service_name, name, KEY_WRITE);
124   if (! key) return -1;
125
126   long error;
127   int ret = 1;
128
129   /* Resetting to default? */
130   if (value && value->string) _sntprintf_s(action_string, _countof(action_string), _TRUNCATE, _T("%s"), value->string);
131   else {
132     if (code) {
133       /* Delete explicit action. */
134       error = RegDeleteValue(key, code);
135       RegCloseKey(key);
136       if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
137       print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, code, service_name, error_string(error));
138       return -1;
139     }
140     else {
141       /* Explicitly keep the default action. */
142       if (default_value) _sntprintf_s(action_string, _countof(action_string), _TRUNCATE, _T("%s"), (TCHAR *) default_value);
143       ret = 0;
144     }
145   }
146
147   /* Validate the string. */
148   for (int i = 0; exit_action_strings[i]; i++) {
149     if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
150       if (default_value && str_equiv(action_string, (TCHAR *) default_value)) ret = 0;
151       if (RegSetValueEx(key, code, 0, REG_SZ, (const unsigned char *) exit_action_strings[i], (unsigned long) (_tcslen(action_string) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) {
152         print_message(stderr, NSSM_MESSAGE_SETVALUE_FAILED, code, service_name, error_string(GetLastError()));
153         RegCloseKey(key);
154         return -1;
155       }
156
157       RegCloseKey(key);
158       return ret;
159     }
160   }
161
162   print_message(stderr, NSSM_MESSAGE_INVALID_EXIT_ACTION, action_string);
163   for (int i = 0; exit_action_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), exit_action_strings[i]);
164
165   return -1;
166 }
167
168 static int setting_get_exit_action(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
169   unsigned long exitcode = 0;
170   unsigned long *code = 0;
171
172   if (additional) {
173     if (! is_default(additional)) {
174       if (str_number(additional, &exitcode)) return -1;
175       code = &exitcode;
176     }
177   }
178
179   TCHAR action_string[ACTION_LEN];
180   bool default_action;
181   if (get_exit_action(service_name, code, action_string, &default_action)) return -1;
182
183   value_from_string(name, value, action_string);
184
185   if (default_action && ! _tcsnicmp((const TCHAR *) action_string, (TCHAR *) default_value, ACTION_LEN)) return 0;
186   return 1;
187 }
188
189 static int setting_set_affinity(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
190   HKEY key = (HKEY) param;
191   if (! key) return -1;
192
193   long error;
194   __int64 mask;
195   __int64 system_affinity = 0LL;
196
197   if (value && value->string) {
198     DWORD_PTR affinity;
199     if (! GetProcessAffinityMask(GetCurrentProcess(), &affinity, (DWORD_PTR *) &system_affinity)) system_affinity = ~0;
200
201     if (is_default(value->string) || str_equiv(value->string, NSSM_AFFINITY_ALL)) mask = 0LL;
202     else if (affinity_string_to_mask(value->string, &mask)) {
203       print_message(stderr, NSSM_MESSAGE_BOGUS_AFFINITY_MASK, value->string, num_cpus() - 1);
204       return -1;
205     }
206   }
207   else mask = 0LL;
208
209   if (! mask) {
210     error = RegDeleteValue(key, name);
211     if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
212     print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
213     return -1;
214   }
215
216   /* Canonicalise. */
217   TCHAR *canon = 0;
218   if (affinity_mask_to_string(mask, &canon)) canon = value->string;
219
220   __int64 effective_affinity = mask & system_affinity;
221   if (effective_affinity != mask) {
222     /* Requested CPUs did not intersect with available CPUs? */
223     if (! effective_affinity) mask = effective_affinity = system_affinity;
224
225     TCHAR *system = 0;
226     if (! affinity_mask_to_string(system_affinity, &system)) {
227       TCHAR *effective = 0;
228       if (! affinity_mask_to_string(effective_affinity, &effective)) {
229         print_message(stderr, NSSM_MESSAGE_EFFECTIVE_AFFINITY_MASK, value->string, system, effective);
230         HeapFree(GetProcessHeap(), 0, effective);
231       }
232       HeapFree(GetProcessHeap(), 0, system);
233     }
234   }
235
236   if (RegSetValueEx(key, name, 0, REG_SZ, (const unsigned char *) canon, (unsigned long) (_tcslen(canon) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) {
237     if (canon != value->string) HeapFree(GetProcessHeap(), 0, canon);
238     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, name, error_string(GetLastError()), 0);
239     return -1;
240   }
241
242   if (canon != value->string) HeapFree(GetProcessHeap(), 0, canon);
243   return 1;
244 }
245
246 static int setting_get_affinity(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
247   HKEY key = (HKEY) param;
248   if (! key) return -1;
249
250   unsigned long type;
251   TCHAR *buffer = 0;
252   unsigned long buflen = 0;
253
254   int ret = RegQueryValueEx(key, name, 0, &type, 0, &buflen);
255   if (ret == ERROR_FILE_NOT_FOUND) {
256     if (value_from_string(name, value, NSSM_AFFINITY_ALL) == 1) return 0;
257     return -1;
258   }
259   if (ret != ERROR_SUCCESS) return -1;
260
261   if (type != REG_SZ) return -1;
262
263   buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, buflen);
264   if (! buffer) {
265     print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("affinity"), _T("setting_get_affinity"));
266     return -1;
267   }
268
269   if (get_string(key, (TCHAR *) name, buffer, buflen, false, false, true)) {
270     HeapFree(GetProcessHeap(), 0, buffer);
271     return -1;
272   }
273
274   __int64 affinity;
275   if (affinity_string_to_mask(buffer, &affinity)) {
276     print_message(stderr, NSSM_MESSAGE_BOGUS_AFFINITY_MASK, buffer, num_cpus() - 1);
277     HeapFree(GetProcessHeap(), 0, buffer);
278     return -1;
279   }
280
281   HeapFree(GetProcessHeap(), 0, buffer);
282
283   /* Canonicalise. */
284   if (affinity_mask_to_string(affinity, &buffer)) {
285     if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
286     return -1;
287   }
288
289   ret = value_from_string(name, value, buffer);
290   HeapFree(GetProcessHeap(), 0, buffer);
291   return ret;
292 }
293
294 static int setting_set_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
295   HKEY key = (HKEY) param;
296   if (! param) return -1;
297
298   if (! value || ! value->string || ! value->string[0]) {
299     long error = RegDeleteValue(key, name);
300     if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
301     print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
302     return -1;
303   }
304
305   unsigned long envlen = (unsigned long) _tcslen(value->string) + 1;
306   TCHAR *unformatted = 0;
307   unsigned long newlen;
308   if (unformat_environment(value->string, envlen, &unformatted, &newlen)) return -1;
309
310   if (test_environment(unformatted)) {
311     HeapFree(GetProcessHeap(), 0, unformatted);
312     print_message(stderr, NSSM_GUI_INVALID_ENVIRONMENT);
313     return -1;
314   }
315
316   if (RegSetValueEx(key, name, 0, REG_MULTI_SZ, (const unsigned char *) unformatted, (unsigned long) newlen * sizeof(TCHAR)) != ERROR_SUCCESS) {
317     if (newlen) HeapFree(GetProcessHeap(), 0, unformatted);
318     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV, error_string(GetLastError()), 0);
319     return -1;
320   }
321
322   if (newlen) HeapFree(GetProcessHeap(), 0, unformatted);
323   return 1;
324 }
325
326 static int setting_get_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
327   HKEY key = (HKEY) param;
328   if (! param) return -1;
329
330   TCHAR *env = 0;
331   unsigned long envlen;
332   if (get_environment((TCHAR *) service_name, key, (TCHAR *) name, &env, &envlen)) return -1;
333   if (! envlen) return 0;
334
335   TCHAR *formatted;
336   unsigned long newlen;
337   if (format_environment(env, envlen, &formatted, &newlen)) return -1;
338
339   int ret;
340   if (additional) {
341     /* Find named environment variable. */
342     TCHAR *s;
343     size_t len = _tcslen(additional);
344     for (s = env; *s; s++) {
345       /* Look for <additional>=<string> NULL NULL */
346       if (! _tcsnicmp(s, additional, len) && s[len] == _T('=')) {
347         /* Strip <key>= */
348         s += len + 1;
349         ret = value_from_string(name, value, s);
350         HeapFree(GetProcessHeap(), 0, env);
351         return ret;
352       }
353
354       /* Skip this string. */
355       for ( ; *s; s++);
356     }
357     HeapFree(GetProcessHeap(), 0, env);
358     return 0;
359   }
360
361   HeapFree(GetProcessHeap(), 0, env);
362
363   ret = value_from_string(name, value, formatted);
364   if (newlen) HeapFree(GetProcessHeap(), 0, formatted);
365   return ret;
366 }
367
368 static int setting_set_priority(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
369   HKEY key = (HKEY) param;
370   if (! param) return -1;
371
372   TCHAR *priority_string;
373   int i;
374   long error;
375
376   if (value && value->string) priority_string = value->string;
377   else if (default_value) priority_string = (TCHAR *) default_value;
378   else {
379     error = RegDeleteValue(key, name);
380     if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
381     print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
382     return -1;
383   }
384
385   for (i = 0; priority_strings[i]; i++) {
386     if (! str_equiv(priority_strings[i], priority_string)) continue;
387
388     if (default_value && str_equiv(priority_string, (TCHAR *) default_value)) {
389       error = RegDeleteValue(key, name);
390       if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
391       print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
392       return -1;
393     }
394
395     if (set_number(key, (TCHAR *) name, priority_index_to_constant(i))) return -1;
396     return 1;
397   }
398
399   print_message(stderr, NSSM_MESSAGE_INVALID_PRIORITY, priority_string);
400   for (i = 0; priority_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), priority_strings[i]);
401
402   return -1;
403 }
404
405 static int setting_get_priority(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
406   HKEY key = (HKEY) param;
407   if (! param) return -1;
408
409   unsigned long constant;
410   switch (get_number(key, (TCHAR *) name, &constant, false)) {
411     case 0: return value_from_string(name, value, (const TCHAR *) default_value);
412     case -1: return -1;
413   }
414
415   return value_from_string(name, value, priority_strings[priority_constant_to_index(constant)]);
416 }
417
418 /* Functions to manage native service settings. */
419 int native_set_description(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
420   SC_HANDLE service_handle = (SC_HANDLE) param;
421   if (! service_handle) return -1;
422
423   TCHAR *description = 0;
424   if (value) description = value->string;
425   if (set_service_description(service_name, service_handle, description)) return -1;
426
427   if (description && description[0]) return 1;
428
429   return 0;
430 }
431
432 int native_get_description(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
433   SC_HANDLE service_handle = (SC_HANDLE) param;
434   if (! service_handle) return -1;
435
436   TCHAR buffer[VALUE_LENGTH];
437   if (get_service_description(service_name, service_handle, _countof(buffer), buffer)) return -1;
438
439   if (buffer[0]) return value_from_string(name, value, buffer);
440   value->string = 0;
441
442   return 0;
443 }
444
445 int native_set_displayname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
446   SC_HANDLE service_handle = (SC_HANDLE) param;
447   if (! service_handle) return -1;
448
449   TCHAR *displayname = 0;
450   if (value && value->string) displayname = value->string;
451   else displayname = (TCHAR *) service_name;
452
453   if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, displayname)) {
454     print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
455     return -1;
456   }
457
458   /*
459     If the display name and service name differ only in case,
460     ChangeServiceConfig() will return success but the display name will be
461     set to the service name, NOT the value passed to the function.
462     This appears to be a quirk of Windows rather than a bug here.
463   */
464   if (displayname != service_name && ! str_equiv(displayname, service_name)) return 1;
465
466   return 0;
467 }
468
469 int native_get_displayname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
470   SC_HANDLE service_handle = (SC_HANDLE) param;
471   if (! service_handle) return -1;
472
473   QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
474   if (! qsc) return -1;
475
476   int ret = value_from_string(name, value, qsc->lpDisplayName);
477   HeapFree(GetProcessHeap(), 0, qsc);
478
479   return ret;
480 }
481
482 int native_set_imagepath(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
483   SC_HANDLE service_handle = (SC_HANDLE) param;
484   if (! service_handle) return -1;
485
486   /* It makes no sense to try to reset the image path. */
487   if (! value || ! value->string) {
488     print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);
489     return -1;
490   }
491
492   if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, value->string, 0, 0, 0, 0, 0, 0)) {
493     print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
494     return -1;
495   }
496
497   return 1;
498 }
499
500 int native_get_imagepath(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
501   SC_HANDLE service_handle = (SC_HANDLE) param;
502   if (! service_handle) return -1;
503
504   QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
505   if (! qsc) return -1;
506
507   int ret = value_from_string(name, value, qsc->lpBinaryPathName);
508   HeapFree(GetProcessHeap(), 0, qsc);
509
510   return ret;
511 }
512
513 int native_set_name(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
514   print_message(stderr, NSSM_MESSAGE_CANNOT_RENAME_SERVICE);
515   return -1;
516 }
517
518 int native_get_name(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
519   return value_from_string(name, value, service_name);
520 }
521
522 int native_set_objectname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
523   SC_HANDLE service_handle = (SC_HANDLE) param;
524   if (! service_handle) return -1;
525
526   /*
527     Logical syntax is: nssm set <service> ObjectName <username> <password>
528     That means the username is actually passed in the additional parameter.
529   */
530   bool localsystem = false;
531   TCHAR *username = NSSM_LOCALSYSTEM_ACCOUNT;
532   TCHAR *password = 0;
533   TCHAR *canon = 0;
534   if (additional) {
535     username = (TCHAR *) additional;
536     if (value && value->string) password = value->string;
537   }
538   else if (value && value->string) username = value->string;
539
540   if (requires_password(username)) {
541     if (! password) {
542       /* We need a password if the account requires it. */
543       print_message(stderr, NSSM_MESSAGE_MISSING_PASSWORD, name);
544       return -1;
545     }
546   }
547   else {
548     password = 0;
549     if (is_localsystem(username)) {
550       localsystem = true;
551       username = NSSM_LOCALSYSTEM_ACCOUNT;
552     }
553     else {
554       canon = canonical_username(username);
555       if (canon) username = canon;
556     }
557   }
558
559   /*
560     ChangeServiceConfig() will fail to set the username if the service is set
561     to interact with the desktop.
562   */
563   unsigned long type = SERVICE_NO_CHANGE;
564   if (! localsystem) {
565     QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
566     if (! qsc) {
567       if (password) SecureZeroMemory(password, _tcslen(password) * sizeof(TCHAR));
568       if (canon) LocalFree(canon);
569       return -1;
570     }
571
572     type = qsc->dwServiceType & ~SERVICE_INTERACTIVE_PROCESS;
573     HeapFree(GetProcessHeap(), 0, qsc);
574   }
575
576   if (password) {
577     if (grant_logon_as_service(username)) {
578       if (password) SecureZeroMemory(password, _tcslen(password) * sizeof(TCHAR));
579       if (canon) LocalFree(canon);
580       print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);
581       return -1;
582     }
583   }
584
585   if (! ChangeServiceConfig(service_handle, type, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, username, password, 0)) {
586     if (password) SecureZeroMemory(password, _tcslen(password) * sizeof(TCHAR));
587     if (canon) LocalFree(canon);
588     print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
589     return -1;
590   }
591   if (password) SecureZeroMemory(password, _tcslen(password) * sizeof(TCHAR));
592
593   if (canon) LocalFree(canon);
594   if (localsystem) return 0;
595
596   return 1;
597 }
598
599 int native_get_objectname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
600   SC_HANDLE service_handle = (SC_HANDLE) param;
601   if (! service_handle) return -1;
602
603   QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
604   if (! qsc) return -1;
605
606   int ret = value_from_string(name, value, qsc->lpServiceStartName);
607   HeapFree(GetProcessHeap(), 0, qsc);
608
609   return ret;
610 }
611
612 int native_set_startup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
613   SC_HANDLE service_handle = (SC_HANDLE) param;
614   if (! service_handle) return -1;
615
616   /* It makes no sense to try to reset the startup type. */
617   if (! value || ! value->string) {
618     print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);
619     return -1;
620   }
621
622   /* Map NSSM_STARTUP_* constant to Windows SERVICE_*_START constant. */
623   int service_startup = -1;
624   int i;
625   for (i = 0; startup_strings[i]; i++) {
626     if (str_equiv(value->string, startup_strings[i])) {
627       service_startup = i;
628       break;
629     }
630   }
631
632   if (service_startup < 0) {
633     print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE_STARTUP, value->string);
634     for (i = 0; startup_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), startup_strings[i]);
635     return -1;
636   }
637
638   unsigned long startup;
639   switch (service_startup) {
640     case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;
641     case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;
642     default: startup = SERVICE_AUTO_START;
643   }
644
645   if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, startup, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) {
646     print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
647     return -1;
648   }
649
650   SERVICE_DELAYED_AUTO_START_INFO delayed;
651   ZeroMemory(&delayed, sizeof(delayed));
652   if (service_startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;
653   else delayed.fDelayedAutostart = 0;
654   if (! ChangeServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {
655     unsigned long error = GetLastError();
656     /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
657     if (error != ERROR_INVALID_LEVEL) {
658       log_event(EVENTLOG_ERROR_TYPE, NSSM_MESSAGE_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service_name, error_string(error), 0);
659     }
660   }
661
662   return 1;
663 }
664
665 int native_get_startup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
666   SC_HANDLE service_handle = (SC_HANDLE) param;
667   if (! service_handle) return -1;
668
669   QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
670   if (! qsc) return -1;
671
672   unsigned long startup;
673   int ret = get_service_startup(service_name, service_handle, qsc, &startup);
674   HeapFree(GetProcessHeap(), 0, qsc);
675
676   if (ret) return -1;
677
678   unsigned long i;
679   for (i = 0; startup_strings[i]; i++);
680   if (startup >= i) return -1;
681
682   return value_from_string(name, value, startup_strings[startup]);
683 }
684
685 int native_set_type(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
686   SC_HANDLE service_handle = (SC_HANDLE) param;
687   if (! service_handle) return -1;
688
689   /* It makes no sense to try to reset the service type. */
690   if (! value || ! value->string) {
691     print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);
692     return -1;
693   }
694
695   /*
696     We can only manage services of type SERVICE_WIN32_OWN_PROCESS
697     and SERVICE_INTERACTIVE_PROCESS.
698   */
699   unsigned long type = SERVICE_WIN32_OWN_PROCESS;
700   if (str_equiv(value->string, NSSM_INTERACTIVE_PROCESS)) type |= SERVICE_INTERACTIVE_PROCESS;
701   else if (! str_equiv(value->string, NSSM_WIN32_OWN_PROCESS)) {
702     print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE_TYPE, value->string);
703     _ftprintf(stderr, _T("%s\n"), NSSM_WIN32_OWN_PROCESS);
704     _ftprintf(stderr, _T("%s\n"), NSSM_INTERACTIVE_PROCESS);
705     return -1;
706   }
707
708   /*
709     ChangeServiceConfig() will fail if the service runs under an account
710     other than LOCALSYSTEM and we try to make it interactive.
711   */
712   if (type & SERVICE_INTERACTIVE_PROCESS) {
713     QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
714     if (! qsc) return -1;
715
716     if (! str_equiv(qsc->lpServiceStartName, NSSM_LOCALSYSTEM_ACCOUNT)) {
717       HeapFree(GetProcessHeap(), 0, qsc);
718       print_message(stderr, NSSM_MESSAGE_INTERACTIVE_NOT_LOCALSYSTEM, value->string, service_name, NSSM_LOCALSYSTEM_ACCOUNT);
719       return -1;
720     }
721
722     HeapFree(GetProcessHeap(), 0, qsc);
723   }
724
725   if (! ChangeServiceConfig(service_handle, type, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) {
726     print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
727     return -1;
728   }
729
730   return 1;
731 }
732
733 int native_get_type(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
734   SC_HANDLE service_handle = (SC_HANDLE) param;
735   if (! service_handle) return -1;
736
737   QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
738   if (! qsc) return -1;
739
740   value->numeric = qsc->dwServiceType;
741   HeapFree(GetProcessHeap(), 0, qsc);
742
743   const TCHAR *string;
744   switch (value->numeric) {
745     case SERVICE_KERNEL_DRIVER: string = NSSM_KERNEL_DRIVER; break;
746     case SERVICE_FILE_SYSTEM_DRIVER: string = NSSM_FILE_SYSTEM_DRIVER; break;
747     case SERVICE_WIN32_OWN_PROCESS: string = NSSM_WIN32_OWN_PROCESS; break;
748     case SERVICE_WIN32_SHARE_PROCESS: string = NSSM_WIN32_SHARE_PROCESS; break;
749     case SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS: string = NSSM_INTERACTIVE_PROCESS; break;
750     case SERVICE_WIN32_SHARE_PROCESS|SERVICE_INTERACTIVE_PROCESS: string = NSSM_SHARE_INTERACTIVE_PROCESS; break;
751     default: string = NSSM_UNKNOWN;
752   }
753
754   return value_from_string(name, value, string);
755 }
756
757 int set_setting(const TCHAR *service_name, HKEY key, settings_t *setting, value_t *value, const TCHAR *additional) {
758   if (! key) return -1;
759   int ret;
760
761   if (setting->set) ret = setting->set(service_name, (void *) key, setting->name, setting->default_value, value, additional);
762   else ret = -1;
763
764   if (! ret) print_message(stdout, NSSM_MESSAGE_RESET_SETTING, setting->name, service_name);
765   else if (ret > 0) print_message(stdout, NSSM_MESSAGE_SET_SETTING, setting->name, service_name);
766   else print_message(stderr, NSSM_MESSAGE_SET_SETTING_FAILED, setting->name, service_name);
767
768   return ret;
769 }
770
771 int set_setting(const TCHAR *service_name, SC_HANDLE service_handle, settings_t *setting, value_t *value, const TCHAR *additional) {
772   if (! service_handle) return -1;
773
774   int ret;
775   if (setting->set) ret = setting->set(service_name, service_handle, setting->name, setting->default_value, value, additional);
776   else ret = -1;
777
778   if (! ret) print_message(stdout, NSSM_MESSAGE_RESET_SETTING, setting->name, service_name);
779   else if (ret > 0) print_message(stdout, NSSM_MESSAGE_SET_SETTING, setting->name, service_name);
780   else print_message(stderr, NSSM_MESSAGE_SET_SETTING_FAILED, setting->name, service_name);
781
782   return ret;
783 }
784
785 /*
786   Returns:  1 if the value was retrieved.
787             0 if the default value was retrieved.
788            -1 on error.
789 */
790 int get_setting(const TCHAR *service_name, HKEY key, settings_t *setting, value_t *value, const TCHAR *additional) {
791   if (! key) return -1;
792   int ret;
793
794   switch (setting->type) {
795     case REG_EXPAND_SZ:
796     case REG_MULTI_SZ:
797     case REG_SZ:
798       value->string = (TCHAR *) setting->default_value;
799       if (setting->get) ret = setting->get(service_name, (void *) key, setting->name, setting->default_value, value, additional);
800       else ret = -1;
801       break;
802
803     case REG_DWORD:
804       value->numeric = (unsigned long) setting->default_value;
805       if (setting->get) ret = setting->get(service_name, (void *) key, setting->name, setting->default_value, value, additional);
806       else ret = -1;
807       break;
808
809     default:
810       ret = -1;
811       break;
812   }
813
814   if (ret < 0) print_message(stderr, NSSM_MESSAGE_GET_SETTING_FAILED, setting->name, service_name);
815
816   return ret;
817 }
818
819 int get_setting(const TCHAR *service_name, SC_HANDLE service_handle, settings_t *setting, value_t *value, const TCHAR *additional) {
820   if (! service_handle) return -1;
821   return setting->get(service_name, service_handle, setting->name, 0, value, additional);
822 }
823
824 settings_t settings[] = {
825   { NSSM_REG_EXE, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },
826   { NSSM_REG_FLAGS, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },
827   { NSSM_REG_DIR, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },
828   { NSSM_REG_EXIT, REG_SZ, (void *) exit_action_strings[NSSM_EXIT_RESTART], false, ADDITIONAL_MANDATORY, setting_set_exit_action, setting_get_exit_action },
829   { NSSM_REG_AFFINITY, REG_SZ, 0, false, 0, setting_set_affinity, setting_get_affinity },
830   { NSSM_REG_ENV, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment },
831   { NSSM_REG_ENV_EXTRA, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment },
832   { NSSM_REG_NO_CONSOLE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
833   { NSSM_REG_PRIORITY, REG_SZ, (void *) priority_strings[NSSM_NORMAL_PRIORITY], false, 0, setting_set_priority, setting_get_priority },
834   { NSSM_REG_RESTART_DELAY, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
835   { NSSM_REG_STDIN, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },
836   { NSSM_REG_STDIN NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDIN_SHARING, false, 0, setting_set_number, setting_get_number },
837   { NSSM_REG_STDIN NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDIN_DISPOSITION, false, 0, setting_set_number, setting_get_number },
838   { NSSM_REG_STDIN NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDIN_FLAGS, false, 0, setting_set_number, setting_get_number },
839   { NSSM_REG_STDOUT, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },
840   { NSSM_REG_STDOUT NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDOUT_SHARING, false, 0, setting_set_number, setting_get_number },
841   { NSSM_REG_STDOUT NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDOUT_DISPOSITION, false, 0, setting_set_number, setting_get_number },
842   { NSSM_REG_STDOUT NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDOUT_FLAGS, false, 0, setting_set_number, setting_get_number },
843   { NSSM_REG_STDERR, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },
844   { NSSM_REG_STDERR NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDERR_SHARING, false, 0, setting_set_number, setting_get_number },
845   { NSSM_REG_STDERR NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDERR_DISPOSITION, false, 0, setting_set_number, setting_get_number },
846   { NSSM_REG_STDERR NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDERR_FLAGS, false, 0, setting_set_number, setting_get_number },
847   { NSSM_REG_STOP_METHOD_SKIP, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
848   { NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_CONSOLE_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },
849   { NSSM_REG_KILL_WINDOW_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_WINDOW_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },
850   { NSSM_REG_KILL_THREADS_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_THREADS_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },
851   { NSSM_REG_THROTTLE, REG_DWORD, (void *) NSSM_RESET_THROTTLE_RESTART, false, 0, setting_set_number, setting_get_number },
852   { NSSM_REG_ROTATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
853   { NSSM_REG_ROTATE_ONLINE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
854   { NSSM_REG_ROTATE_SECONDS, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
855   { NSSM_REG_ROTATE_BYTES_LOW, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
856   { NSSM_REG_ROTATE_BYTES_HIGH, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
857   { NSSM_NATIVE_DESCRIPTION, REG_SZ, _T(""), true, 0, native_set_description, native_get_description },
858   { NSSM_NATIVE_DISPLAYNAME, REG_SZ, NULL, true, 0, native_set_displayname, native_get_displayname },
859   { NSSM_NATIVE_IMAGEPATH, REG_EXPAND_SZ, NULL, true, 0, native_set_imagepath, native_get_imagepath },
860   { NSSM_NATIVE_OBJECTNAME, REG_SZ, NSSM_LOCALSYSTEM_ACCOUNT, true, 0, native_set_objectname, native_get_objectname },
861   { NSSM_NATIVE_NAME, REG_SZ, NULL, true, 0, native_set_name, native_get_name },
862   { NSSM_NATIVE_STARTUP, REG_SZ, NULL, true, 0, native_set_startup, native_get_startup },
863   { NSSM_NATIVE_TYPE, REG_SZ, NULL, true, 0, native_set_type, native_get_type },
864   { NULL, NULL, NULL, NULL, NULL }
865 };