43ae55964139d271659d40afc14f5f089a87e32a
[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_double_null(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_double_null(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   if (additional) {
534     username = (TCHAR *) additional;
535     if (value && value->string) password = value->string;
536   }
537   else if (value && value->string) username = value->string;
538
539   const TCHAR *well_known = well_known_username(username);
540   size_t passwordsize = 0;
541   if (well_known) {
542     if (str_equiv(well_known, NSSM_LOCALSYSTEM_ACCOUNT)) localsystem = true;
543     username = (TCHAR *) well_known;
544     password = _T("");
545   }
546   else if (! password) {
547     /* We need a password if the account requires it. */
548     print_message(stderr, NSSM_MESSAGE_MISSING_PASSWORD, name);
549     return -1;
550   }
551   else passwordsize = _tcslen(password) * sizeof(TCHAR);
552
553   /*
554     ChangeServiceConfig() will fail to set the username if the service is set
555     to interact with the desktop.
556   */
557   unsigned long type = SERVICE_NO_CHANGE;
558   if (! localsystem) {
559     QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
560     if (! qsc) {
561       if (passwordsize) SecureZeroMemory(password, passwordsize);
562       return -1;
563     }
564
565     type = qsc->dwServiceType & ~SERVICE_INTERACTIVE_PROCESS;
566     HeapFree(GetProcessHeap(), 0, qsc);
567   }
568
569   if (! well_known) {
570     if (grant_logon_as_service(username)) {
571       if (passwordsize) SecureZeroMemory(password, passwordsize);
572       print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);
573       return -1;
574     }
575   }
576
577   if (! ChangeServiceConfig(service_handle, type, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, username, password, 0)) {
578     if (passwordsize) SecureZeroMemory(password, passwordsize);
579     print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
580     return -1;
581   }
582
583   if (passwordsize) SecureZeroMemory(password, passwordsize);
584
585   if (localsystem) return 0;
586
587   return 1;
588 }
589
590 int native_get_objectname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
591   SC_HANDLE service_handle = (SC_HANDLE) param;
592   if (! service_handle) return -1;
593
594   QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
595   if (! qsc) return -1;
596
597   int ret = value_from_string(name, value, qsc->lpServiceStartName);
598   HeapFree(GetProcessHeap(), 0, qsc);
599
600   return ret;
601 }
602
603 int native_set_startup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
604   SC_HANDLE service_handle = (SC_HANDLE) param;
605   if (! service_handle) return -1;
606
607   /* It makes no sense to try to reset the startup type. */
608   if (! value || ! value->string) {
609     print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);
610     return -1;
611   }
612
613   /* Map NSSM_STARTUP_* constant to Windows SERVICE_*_START constant. */
614   int service_startup = -1;
615   int i;
616   for (i = 0; startup_strings[i]; i++) {
617     if (str_equiv(value->string, startup_strings[i])) {
618       service_startup = i;
619       break;
620     }
621   }
622
623   if (service_startup < 0) {
624     print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE_STARTUP, value->string);
625     for (i = 0; startup_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), startup_strings[i]);
626     return -1;
627   }
628
629   unsigned long startup;
630   switch (service_startup) {
631     case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;
632     case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;
633     default: startup = SERVICE_AUTO_START;
634   }
635
636   if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, startup, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) {
637     print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
638     return -1;
639   }
640
641   SERVICE_DELAYED_AUTO_START_INFO delayed;
642   ZeroMemory(&delayed, sizeof(delayed));
643   if (service_startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;
644   else delayed.fDelayedAutostart = 0;
645   if (! ChangeServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {
646     unsigned long error = GetLastError();
647     /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
648     if (error != ERROR_INVALID_LEVEL) {
649       log_event(EVENTLOG_ERROR_TYPE, NSSM_MESSAGE_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service_name, error_string(error), 0);
650     }
651   }
652
653   return 1;
654 }
655
656 int native_get_startup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
657   SC_HANDLE service_handle = (SC_HANDLE) param;
658   if (! service_handle) return -1;
659
660   QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
661   if (! qsc) return -1;
662
663   unsigned long startup;
664   int ret = get_service_startup(service_name, service_handle, qsc, &startup);
665   HeapFree(GetProcessHeap(), 0, qsc);
666
667   if (ret) return -1;
668
669   unsigned long i;
670   for (i = 0; startup_strings[i]; i++);
671   if (startup >= i) return -1;
672
673   return value_from_string(name, value, startup_strings[startup]);
674 }
675
676 int native_set_type(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
677   SC_HANDLE service_handle = (SC_HANDLE) param;
678   if (! service_handle) return -1;
679
680   /* It makes no sense to try to reset the service type. */
681   if (! value || ! value->string) {
682     print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);
683     return -1;
684   }
685
686   /*
687     We can only manage services of type SERVICE_WIN32_OWN_PROCESS
688     and SERVICE_INTERACTIVE_PROCESS.
689   */
690   unsigned long type = SERVICE_WIN32_OWN_PROCESS;
691   if (str_equiv(value->string, NSSM_INTERACTIVE_PROCESS)) type |= SERVICE_INTERACTIVE_PROCESS;
692   else if (! str_equiv(value->string, NSSM_WIN32_OWN_PROCESS)) {
693     print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE_TYPE, value->string);
694     _ftprintf(stderr, _T("%s\n"), NSSM_WIN32_OWN_PROCESS);
695     _ftprintf(stderr, _T("%s\n"), NSSM_INTERACTIVE_PROCESS);
696     return -1;
697   }
698
699   /*
700     ChangeServiceConfig() will fail if the service runs under an account
701     other than LOCALSYSTEM and we try to make it interactive.
702   */
703   if (type & SERVICE_INTERACTIVE_PROCESS) {
704     QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
705     if (! qsc) return -1;
706
707     if (! str_equiv(qsc->lpServiceStartName, NSSM_LOCALSYSTEM_ACCOUNT)) {
708       HeapFree(GetProcessHeap(), 0, qsc);
709       print_message(stderr, NSSM_MESSAGE_INTERACTIVE_NOT_LOCALSYSTEM, value->string, service_name, NSSM_LOCALSYSTEM_ACCOUNT);
710       return -1;
711     }
712
713     HeapFree(GetProcessHeap(), 0, qsc);
714   }
715
716   if (! ChangeServiceConfig(service_handle, type, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) {
717     print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
718     return -1;
719   }
720
721   return 1;
722 }
723
724 int native_get_type(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
725   SC_HANDLE service_handle = (SC_HANDLE) param;
726   if (! service_handle) return -1;
727
728   QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
729   if (! qsc) return -1;
730
731   value->numeric = qsc->dwServiceType;
732   HeapFree(GetProcessHeap(), 0, qsc);
733
734   const TCHAR *string;
735   switch (value->numeric) {
736     case SERVICE_KERNEL_DRIVER: string = NSSM_KERNEL_DRIVER; break;
737     case SERVICE_FILE_SYSTEM_DRIVER: string = NSSM_FILE_SYSTEM_DRIVER; break;
738     case SERVICE_WIN32_OWN_PROCESS: string = NSSM_WIN32_OWN_PROCESS; break;
739     case SERVICE_WIN32_SHARE_PROCESS: string = NSSM_WIN32_SHARE_PROCESS; break;
740     case SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS: string = NSSM_INTERACTIVE_PROCESS; break;
741     case SERVICE_WIN32_SHARE_PROCESS|SERVICE_INTERACTIVE_PROCESS: string = NSSM_SHARE_INTERACTIVE_PROCESS; break;
742     default: string = NSSM_UNKNOWN;
743   }
744
745   return value_from_string(name, value, string);
746 }
747
748 int set_setting(const TCHAR *service_name, HKEY key, settings_t *setting, value_t *value, const TCHAR *additional) {
749   if (! key) return -1;
750   int ret;
751
752   if (setting->set) ret = setting->set(service_name, (void *) key, setting->name, setting->default_value, value, additional);
753   else ret = -1;
754
755   if (! ret) print_message(stdout, NSSM_MESSAGE_RESET_SETTING, setting->name, service_name);
756   else if (ret > 0) print_message(stdout, NSSM_MESSAGE_SET_SETTING, setting->name, service_name);
757   else print_message(stderr, NSSM_MESSAGE_SET_SETTING_FAILED, setting->name, service_name);
758
759   return ret;
760 }
761
762 int set_setting(const TCHAR *service_name, SC_HANDLE service_handle, settings_t *setting, value_t *value, const TCHAR *additional) {
763   if (! service_handle) return -1;
764
765   int ret;
766   if (setting->set) ret = setting->set(service_name, service_handle, setting->name, setting->default_value, value, additional);
767   else ret = -1;
768
769   if (! ret) print_message(stdout, NSSM_MESSAGE_RESET_SETTING, setting->name, service_name);
770   else if (ret > 0) print_message(stdout, NSSM_MESSAGE_SET_SETTING, setting->name, service_name);
771   else print_message(stderr, NSSM_MESSAGE_SET_SETTING_FAILED, setting->name, service_name);
772
773   return ret;
774 }
775
776 /*
777   Returns:  1 if the value was retrieved.
778             0 if the default value was retrieved.
779            -1 on error.
780 */
781 int get_setting(const TCHAR *service_name, HKEY key, settings_t *setting, value_t *value, const TCHAR *additional) {
782   if (! key) return -1;
783   int ret;
784
785   switch (setting->type) {
786     case REG_EXPAND_SZ:
787     case REG_MULTI_SZ:
788     case REG_SZ:
789       value->string = (TCHAR *) setting->default_value;
790       if (setting->get) ret = setting->get(service_name, (void *) key, setting->name, setting->default_value, value, additional);
791       else ret = -1;
792       break;
793
794     case REG_DWORD:
795       value->numeric = (unsigned long) setting->default_value;
796       if (setting->get) ret = setting->get(service_name, (void *) key, setting->name, setting->default_value, value, additional);
797       else ret = -1;
798       break;
799
800     default:
801       ret = -1;
802       break;
803   }
804
805   if (ret < 0) print_message(stderr, NSSM_MESSAGE_GET_SETTING_FAILED, setting->name, service_name);
806
807   return ret;
808 }
809
810 int get_setting(const TCHAR *service_name, SC_HANDLE service_handle, settings_t *setting, value_t *value, const TCHAR *additional) {
811   if (! service_handle) return -1;
812   return setting->get(service_name, service_handle, setting->name, 0, value, additional);
813 }
814
815 settings_t settings[] = {
816   { NSSM_REG_EXE, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },
817   { NSSM_REG_FLAGS, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },
818   { NSSM_REG_DIR, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },
819   { NSSM_REG_EXIT, REG_SZ, (void *) exit_action_strings[NSSM_EXIT_RESTART], false, ADDITIONAL_MANDATORY, setting_set_exit_action, setting_get_exit_action },
820   { NSSM_REG_AFFINITY, REG_SZ, 0, false, 0, setting_set_affinity, setting_get_affinity },
821   { NSSM_REG_ENV, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment },
822   { NSSM_REG_ENV_EXTRA, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment },
823   { NSSM_REG_NO_CONSOLE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
824   { NSSM_REG_PRIORITY, REG_SZ, (void *) priority_strings[NSSM_NORMAL_PRIORITY], false, 0, setting_set_priority, setting_get_priority },
825   { NSSM_REG_RESTART_DELAY, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
826   { NSSM_REG_STDIN, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },
827   { NSSM_REG_STDIN NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDIN_SHARING, false, 0, setting_set_number, setting_get_number },
828   { NSSM_REG_STDIN NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDIN_DISPOSITION, false, 0, setting_set_number, setting_get_number },
829   { NSSM_REG_STDIN NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDIN_FLAGS, false, 0, setting_set_number, setting_get_number },
830   { NSSM_REG_STDOUT, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },
831   { NSSM_REG_STDOUT NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDOUT_SHARING, false, 0, setting_set_number, setting_get_number },
832   { NSSM_REG_STDOUT NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDOUT_DISPOSITION, false, 0, setting_set_number, setting_get_number },
833   { NSSM_REG_STDOUT NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDOUT_FLAGS, false, 0, setting_set_number, setting_get_number },
834   { NSSM_REG_STDERR, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },
835   { NSSM_REG_STDERR NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDERR_SHARING, false, 0, setting_set_number, setting_get_number },
836   { NSSM_REG_STDERR NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDERR_DISPOSITION, false, 0, setting_set_number, setting_get_number },
837   { NSSM_REG_STDERR NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDERR_FLAGS, false, 0, setting_set_number, setting_get_number },
838   { NSSM_REG_STOP_METHOD_SKIP, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
839   { NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_CONSOLE_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },
840   { NSSM_REG_KILL_WINDOW_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_WINDOW_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },
841   { NSSM_REG_KILL_THREADS_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_THREADS_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },
842   { NSSM_REG_THROTTLE, REG_DWORD, (void *) NSSM_RESET_THROTTLE_RESTART, false, 0, setting_set_number, setting_get_number },
843   { NSSM_REG_ROTATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
844   { NSSM_REG_ROTATE_ONLINE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
845   { NSSM_REG_ROTATE_SECONDS, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
846   { NSSM_REG_ROTATE_BYTES_LOW, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
847   { NSSM_REG_ROTATE_BYTES_HIGH, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
848   { NSSM_NATIVE_DESCRIPTION, REG_SZ, _T(""), true, 0, native_set_description, native_get_description },
849   { NSSM_NATIVE_DISPLAYNAME, REG_SZ, NULL, true, 0, native_set_displayname, native_get_displayname },
850   { NSSM_NATIVE_IMAGEPATH, REG_EXPAND_SZ, NULL, true, 0, native_set_imagepath, native_get_imagepath },
851   { NSSM_NATIVE_OBJECTNAME, REG_SZ, NSSM_LOCALSYSTEM_ACCOUNT, true, 0, native_set_objectname, native_get_objectname },
852   { NSSM_NATIVE_NAME, REG_SZ, NULL, true, 0, native_set_name, native_get_name },
853   { NSSM_NATIVE_STARTUP, REG_SZ, NULL, true, 0, native_set_startup, native_get_startup },
854   { NSSM_NATIVE_TYPE, REG_SZ, NULL, true, 0, native_set_type, native_get_type },
855   { NULL, NULL, NULL, NULL, NULL }
856 };