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