RegisterPowerSettingNotification is unnecessary.
[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 static int native_set_dependongroup(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   /*
424     Get existing service dependencies because we must set both types together.
425   */
426   TCHAR *buffer;
427   unsigned long buflen;
428   if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_SERVICES)) return -1;
429
430   if (! value || ! value->string || ! value->string[0]) {
431     if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, buffer, 0, 0, 0)) {
432       print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
433       if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
434       return -1;
435     }
436
437     if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
438     return 0;
439   }
440
441   unsigned long len = (unsigned long) _tcslen(value->string) + 1;
442   TCHAR *unformatted = 0;
443   unsigned long newlen;
444   if (unformat_double_null(value->string, len, &unformatted, &newlen)) {
445     if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
446     return -1;
447   }
448
449   /* Prepend group identifier. */
450   unsigned long missing = 0;
451   TCHAR *canon = unformatted;
452   size_t canonlen = 0;
453   TCHAR *s;
454   for (s = unformatted; *s; s++) {
455     if (*s != SC_GROUP_IDENTIFIER) missing++;
456     size_t len = _tcslen(s);
457     canonlen += len + 1;
458     s += len;
459   }
460
461   if (missing) {
462     /* Missing identifiers plus double NULL terminator. */
463     canonlen += missing + 1;
464     newlen = (unsigned long) canonlen;
465
466     canon = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, canonlen * sizeof(TCHAR));
467     if (! canon) {
468       print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canon"), _T("native_set_dependongroup"));
469       if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);
470       if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
471       return -1;
472     }
473
474     size_t i = 0;
475     for (s = unformatted; *s; s++) {
476       if (*s != SC_GROUP_IDENTIFIER) canon[i++] = SC_GROUP_IDENTIFIER;
477       size_t len = _tcslen(s);
478       memmove(canon + i, s, (len + 1) * sizeof(TCHAR));
479       i += len + 1;
480       s += len;
481     }
482   }
483
484   TCHAR *dependencies;
485   if (buflen > 2) {
486     dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (newlen + buflen) * sizeof(TCHAR));
487     if (! dependencies) {
488       print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("dependencies"), _T("native_set_dependongroup"));
489       if (canon != unformatted) HeapFree(GetProcessHeap(), 0, canon);
490       if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);
491       if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
492       return -1;
493     }
494
495     memmove(dependencies, buffer, buflen * sizeof(TCHAR));
496     memmove(dependencies + buflen - 1, canon, newlen * sizeof(TCHAR));
497   }
498   else dependencies = canon;
499
500   int ret = 1;
501   if (set_service_dependencies(service_name, service_handle, dependencies)) ret = -1;
502   if (dependencies != unformatted) HeapFree(GetProcessHeap(), 0, dependencies);
503   if (canon != unformatted) HeapFree(GetProcessHeap(), 0, canon);
504   if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);
505   if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
506
507   return ret;
508 }
509
510 static int native_get_dependongroup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
511   SC_HANDLE service_handle = (SC_HANDLE) param;
512   if (! service_handle) return -1;
513
514   TCHAR *buffer;
515   unsigned long buflen;
516   if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_GROUPS)) return -1;
517
518   int ret;
519   if (buflen) {
520     TCHAR *formatted;
521     unsigned long newlen;
522     if (format_double_null(buffer, buflen, &formatted, &newlen)) {
523       HeapFree(GetProcessHeap(), 0, buffer);
524       return -1;
525     }
526
527     ret = value_from_string(name, value, formatted);
528     HeapFree(GetProcessHeap(), 0, formatted);
529     HeapFree(GetProcessHeap(), 0, buffer);
530   }
531   else {
532     value->string = 0;
533     ret = 0;
534   }
535
536   return ret;
537 }
538
539 static int native_set_dependonservice(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
540   SC_HANDLE service_handle = (SC_HANDLE) param;
541   if (! service_handle) return -1;
542
543   /*
544     Get existing group dependencies because we must set both types together.
545   */
546   TCHAR *buffer;
547   unsigned long buflen;
548   if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_GROUPS)) return -1;
549
550   if (! value || ! value->string || ! value->string[0]) {
551     if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, buffer, 0, 0, 0)) {
552       print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
553       if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
554       return -1;
555     }
556
557     if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
558     return 0;
559   }
560
561   unsigned long len = (unsigned long) _tcslen(value->string) + 1;
562   TCHAR *unformatted = 0;
563   unsigned long newlen;
564   if (unformat_double_null(value->string, len, &unformatted, &newlen)) {
565     if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
566     return -1;
567   }
568
569   TCHAR *dependencies;
570   if (buflen > 2) {
571     dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (newlen + buflen) * sizeof(TCHAR));
572     if (! dependencies) {
573       print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("dependencies"), _T("native_set_dependonservice"));
574       if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);
575       if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
576       return -1;
577     }
578
579     memmove(dependencies, buffer, buflen * sizeof(TCHAR));
580     memmove(dependencies + buflen - 1, unformatted, newlen * sizeof(TCHAR));
581   }
582   else dependencies = unformatted;
583
584   int ret = 1;
585   if (set_service_dependencies(service_name, service_handle, dependencies)) ret = -1;
586   if (dependencies != unformatted) HeapFree(GetProcessHeap(), 0, dependencies);
587   if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);
588   if (buffer) HeapFree(GetProcessHeap(), 0, buffer);
589
590   return ret;
591 }
592
593 static int native_get_dependonservice(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   TCHAR *buffer;
598   unsigned long buflen;
599   if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_SERVICES)) return -1;
600
601   int ret;
602   if (buflen) {
603     TCHAR *formatted;
604     unsigned long newlen;
605     if (format_double_null(buffer, buflen, &formatted, &newlen)) {
606       HeapFree(GetProcessHeap(), 0, buffer);
607       return -1;
608     }
609
610     ret = value_from_string(name, value, formatted);
611     HeapFree(GetProcessHeap(), 0, formatted);
612     HeapFree(GetProcessHeap(), 0, buffer);
613   }
614   else {
615     value->string = 0;
616     ret = 0;
617   }
618
619   return ret;
620 }
621
622 int native_set_description(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
623   SC_HANDLE service_handle = (SC_HANDLE) param;
624   if (! service_handle) return -1;
625
626   TCHAR *description = 0;
627   if (value) description = value->string;
628   if (set_service_description(service_name, service_handle, description)) return -1;
629
630   if (description && description[0]) return 1;
631
632   return 0;
633 }
634
635 int native_get_description(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
636   SC_HANDLE service_handle = (SC_HANDLE) param;
637   if (! service_handle) return -1;
638
639   TCHAR buffer[VALUE_LENGTH];
640   if (get_service_description(service_name, service_handle, _countof(buffer), buffer)) return -1;
641
642   if (buffer[0]) return value_from_string(name, value, buffer);
643   value->string = 0;
644
645   return 0;
646 }
647
648 int native_set_displayname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
649   SC_HANDLE service_handle = (SC_HANDLE) param;
650   if (! service_handle) return -1;
651
652   TCHAR *displayname = 0;
653   if (value && value->string) displayname = value->string;
654   else displayname = (TCHAR *) service_name;
655
656   if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, displayname)) {
657     print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
658     return -1;
659   }
660
661   /*
662     If the display name and service name differ only in case,
663     ChangeServiceConfig() will return success but the display name will be
664     set to the service name, NOT the value passed to the function.
665     This appears to be a quirk of Windows rather than a bug here.
666   */
667   if (displayname != service_name && ! str_equiv(displayname, service_name)) return 1;
668
669   return 0;
670 }
671
672 int native_get_displayname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
673   SC_HANDLE service_handle = (SC_HANDLE) param;
674   if (! service_handle) return -1;
675
676   QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
677   if (! qsc) return -1;
678
679   int ret = value_from_string(name, value, qsc->lpDisplayName);
680   HeapFree(GetProcessHeap(), 0, qsc);
681
682   return ret;
683 }
684
685 int native_set_imagepath(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 image path. */
690   if (! value || ! value->string) {
691     print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);
692     return -1;
693   }
694
695   if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, value->string, 0, 0, 0, 0, 0, 0)) {
696     print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
697     return -1;
698   }
699
700   return 1;
701 }
702
703 int native_get_imagepath(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
704   SC_HANDLE service_handle = (SC_HANDLE) param;
705   if (! service_handle) return -1;
706
707   QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
708   if (! qsc) return -1;
709
710   int ret = value_from_string(name, value, qsc->lpBinaryPathName);
711   HeapFree(GetProcessHeap(), 0, qsc);
712
713   return ret;
714 }
715
716 int native_set_name(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
717   print_message(stderr, NSSM_MESSAGE_CANNOT_RENAME_SERVICE);
718   return -1;
719 }
720
721 int native_get_name(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
722   return value_from_string(name, value, service_name);
723 }
724
725 int native_set_objectname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
726   SC_HANDLE service_handle = (SC_HANDLE) param;
727   if (! service_handle) return -1;
728
729   /*
730     Logical syntax is: nssm set <service> ObjectName <username> <password>
731     That means the username is actually passed in the additional parameter.
732   */
733   bool localsystem = false;
734   TCHAR *username = NSSM_LOCALSYSTEM_ACCOUNT;
735   TCHAR *password = 0;
736   if (additional) {
737     username = (TCHAR *) additional;
738     if (value && value->string) password = value->string;
739   }
740   else if (value && value->string) username = value->string;
741
742   const TCHAR *well_known = well_known_username(username);
743   size_t passwordsize = 0;
744   if (well_known) {
745     if (str_equiv(well_known, NSSM_LOCALSYSTEM_ACCOUNT)) localsystem = true;
746     username = (TCHAR *) well_known;
747     password = _T("");
748   }
749   else if (! password) {
750     /* We need a password if the account requires it. */
751     print_message(stderr, NSSM_MESSAGE_MISSING_PASSWORD, name);
752     return -1;
753   }
754   else passwordsize = _tcslen(password) * sizeof(TCHAR);
755
756   /*
757     ChangeServiceConfig() will fail to set the username if the service is set
758     to interact with the desktop.
759   */
760   unsigned long type = SERVICE_NO_CHANGE;
761   if (! localsystem) {
762     QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
763     if (! qsc) {
764       if (passwordsize) SecureZeroMemory(password, passwordsize);
765       return -1;
766     }
767
768     type = qsc->dwServiceType & ~SERVICE_INTERACTIVE_PROCESS;
769     HeapFree(GetProcessHeap(), 0, qsc);
770   }
771
772   if (! well_known) {
773     if (grant_logon_as_service(username)) {
774       if (passwordsize) SecureZeroMemory(password, passwordsize);
775       print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);
776       return -1;
777     }
778   }
779
780   if (! ChangeServiceConfig(service_handle, type, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, username, password, 0)) {
781     if (passwordsize) SecureZeroMemory(password, passwordsize);
782     print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
783     return -1;
784   }
785
786   if (passwordsize) SecureZeroMemory(password, passwordsize);
787
788   if (localsystem) return 0;
789
790   return 1;
791 }
792
793 int native_get_objectname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
794   SC_HANDLE service_handle = (SC_HANDLE) param;
795   if (! service_handle) return -1;
796
797   QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
798   if (! qsc) return -1;
799
800   int ret = value_from_string(name, value, qsc->lpServiceStartName);
801   HeapFree(GetProcessHeap(), 0, qsc);
802
803   return ret;
804 }
805
806 int native_set_startup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
807   SC_HANDLE service_handle = (SC_HANDLE) param;
808   if (! service_handle) return -1;
809
810   /* It makes no sense to try to reset the startup type. */
811   if (! value || ! value->string) {
812     print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);
813     return -1;
814   }
815
816   /* Map NSSM_STARTUP_* constant to Windows SERVICE_*_START constant. */
817   int service_startup = -1;
818   int i;
819   for (i = 0; startup_strings[i]; i++) {
820     if (str_equiv(value->string, startup_strings[i])) {
821       service_startup = i;
822       break;
823     }
824   }
825
826   if (service_startup < 0) {
827     print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE_STARTUP, value->string);
828     for (i = 0; startup_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), startup_strings[i]);
829     return -1;
830   }
831
832   unsigned long startup;
833   switch (service_startup) {
834     case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;
835     case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;
836     default: startup = SERVICE_AUTO_START;
837   }
838
839   if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, startup, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) {
840     print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
841     return -1;
842   }
843
844   SERVICE_DELAYED_AUTO_START_INFO delayed;
845   ZeroMemory(&delayed, sizeof(delayed));
846   if (service_startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;
847   else delayed.fDelayedAutostart = 0;
848   if (! ChangeServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {
849     unsigned long error = GetLastError();
850     /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
851     if (error != ERROR_INVALID_LEVEL) {
852       log_event(EVENTLOG_ERROR_TYPE, NSSM_MESSAGE_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service_name, error_string(error), 0);
853     }
854   }
855
856   return 1;
857 }
858
859 int native_get_startup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
860   SC_HANDLE service_handle = (SC_HANDLE) param;
861   if (! service_handle) return -1;
862
863   QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
864   if (! qsc) return -1;
865
866   unsigned long startup;
867   int ret = get_service_startup(service_name, service_handle, qsc, &startup);
868   HeapFree(GetProcessHeap(), 0, qsc);
869
870   if (ret) return -1;
871
872   unsigned long i;
873   for (i = 0; startup_strings[i]; i++);
874   if (startup >= i) return -1;
875
876   return value_from_string(name, value, startup_strings[startup]);
877 }
878
879 int native_set_type(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
880   SC_HANDLE service_handle = (SC_HANDLE) param;
881   if (! service_handle) return -1;
882
883   /* It makes no sense to try to reset the service type. */
884   if (! value || ! value->string) {
885     print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);
886     return -1;
887   }
888
889   /*
890     We can only manage services of type SERVICE_WIN32_OWN_PROCESS
891     and SERVICE_INTERACTIVE_PROCESS.
892   */
893   unsigned long type = SERVICE_WIN32_OWN_PROCESS;
894   if (str_equiv(value->string, NSSM_INTERACTIVE_PROCESS)) type |= SERVICE_INTERACTIVE_PROCESS;
895   else if (! str_equiv(value->string, NSSM_WIN32_OWN_PROCESS)) {
896     print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE_TYPE, value->string);
897     _ftprintf(stderr, _T("%s\n"), NSSM_WIN32_OWN_PROCESS);
898     _ftprintf(stderr, _T("%s\n"), NSSM_INTERACTIVE_PROCESS);
899     return -1;
900   }
901
902   /*
903     ChangeServiceConfig() will fail if the service runs under an account
904     other than LOCALSYSTEM and we try to make it interactive.
905   */
906   if (type & SERVICE_INTERACTIVE_PROCESS) {
907     QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
908     if (! qsc) return -1;
909
910     if (! str_equiv(qsc->lpServiceStartName, NSSM_LOCALSYSTEM_ACCOUNT)) {
911       HeapFree(GetProcessHeap(), 0, qsc);
912       print_message(stderr, NSSM_MESSAGE_INTERACTIVE_NOT_LOCALSYSTEM, value->string, service_name, NSSM_LOCALSYSTEM_ACCOUNT);
913       return -1;
914     }
915
916     HeapFree(GetProcessHeap(), 0, qsc);
917   }
918
919   if (! ChangeServiceConfig(service_handle, type, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) {
920     print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
921     return -1;
922   }
923
924   return 1;
925 }
926
927 int native_get_type(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
928   SC_HANDLE service_handle = (SC_HANDLE) param;
929   if (! service_handle) return -1;
930
931   QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
932   if (! qsc) return -1;
933
934   value->numeric = qsc->dwServiceType;
935   HeapFree(GetProcessHeap(), 0, qsc);
936
937   const TCHAR *string;
938   switch (value->numeric) {
939     case SERVICE_KERNEL_DRIVER: string = NSSM_KERNEL_DRIVER; break;
940     case SERVICE_FILE_SYSTEM_DRIVER: string = NSSM_FILE_SYSTEM_DRIVER; break;
941     case SERVICE_WIN32_OWN_PROCESS: string = NSSM_WIN32_OWN_PROCESS; break;
942     case SERVICE_WIN32_SHARE_PROCESS: string = NSSM_WIN32_SHARE_PROCESS; break;
943     case SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS: string = NSSM_INTERACTIVE_PROCESS; break;
944     case SERVICE_WIN32_SHARE_PROCESS|SERVICE_INTERACTIVE_PROCESS: string = NSSM_SHARE_INTERACTIVE_PROCESS; break;
945     default: string = NSSM_UNKNOWN;
946   }
947
948   return value_from_string(name, value, string);
949 }
950
951 int set_setting(const TCHAR *service_name, HKEY key, settings_t *setting, value_t *value, const TCHAR *additional) {
952   if (! key) return -1;
953   int ret;
954
955   if (setting->set) ret = setting->set(service_name, (void *) key, setting->name, setting->default_value, value, additional);
956   else ret = -1;
957
958   if (! ret) print_message(stdout, NSSM_MESSAGE_RESET_SETTING, setting->name, service_name);
959   else if (ret > 0) print_message(stdout, NSSM_MESSAGE_SET_SETTING, setting->name, service_name);
960   else print_message(stderr, NSSM_MESSAGE_SET_SETTING_FAILED, setting->name, service_name);
961
962   return ret;
963 }
964
965 int set_setting(const TCHAR *service_name, SC_HANDLE service_handle, settings_t *setting, value_t *value, const TCHAR *additional) {
966   if (! service_handle) return -1;
967
968   int ret;
969   if (setting->set) ret = setting->set(service_name, service_handle, setting->name, setting->default_value, value, additional);
970   else ret = -1;
971
972   if (! ret) print_message(stdout, NSSM_MESSAGE_RESET_SETTING, setting->name, service_name);
973   else if (ret > 0) print_message(stdout, NSSM_MESSAGE_SET_SETTING, setting->name, service_name);
974   else print_message(stderr, NSSM_MESSAGE_SET_SETTING_FAILED, setting->name, service_name);
975
976   return ret;
977 }
978
979 /*
980   Returns:  1 if the value was retrieved.
981             0 if the default value was retrieved.
982            -1 on error.
983 */
984 int get_setting(const TCHAR *service_name, HKEY key, settings_t *setting, value_t *value, const TCHAR *additional) {
985   if (! key) return -1;
986   int ret;
987
988   switch (setting->type) {
989     case REG_EXPAND_SZ:
990     case REG_MULTI_SZ:
991     case REG_SZ:
992       value->string = (TCHAR *) setting->default_value;
993       if (setting->get) ret = setting->get(service_name, (void *) key, setting->name, setting->default_value, value, additional);
994       else ret = -1;
995       break;
996
997     case REG_DWORD:
998       value->numeric = (unsigned long) setting->default_value;
999       if (setting->get) ret = setting->get(service_name, (void *) key, setting->name, setting->default_value, value, additional);
1000       else ret = -1;
1001       break;
1002
1003     default:
1004       ret = -1;
1005       break;
1006   }
1007
1008   if (ret < 0) print_message(stderr, NSSM_MESSAGE_GET_SETTING_FAILED, setting->name, service_name);
1009
1010   return ret;
1011 }
1012
1013 int get_setting(const TCHAR *service_name, SC_HANDLE service_handle, settings_t *setting, value_t *value, const TCHAR *additional) {
1014   if (! service_handle) return -1;
1015   return setting->get(service_name, service_handle, setting->name, 0, value, additional);
1016 }
1017
1018 settings_t settings[] = {
1019   { NSSM_REG_EXE, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },
1020   { NSSM_REG_FLAGS, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },
1021   { NSSM_REG_DIR, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },
1022   { NSSM_REG_EXIT, REG_SZ, (void *) exit_action_strings[NSSM_EXIT_RESTART], false, ADDITIONAL_MANDATORY, setting_set_exit_action, setting_get_exit_action },
1023   { NSSM_REG_AFFINITY, REG_SZ, 0, false, 0, setting_set_affinity, setting_get_affinity },
1024   { NSSM_REG_ENV, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment },
1025   { NSSM_REG_ENV_EXTRA, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment },
1026   { NSSM_REG_NO_CONSOLE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
1027   { NSSM_REG_PRIORITY, REG_SZ, (void *) priority_strings[NSSM_NORMAL_PRIORITY], false, 0, setting_set_priority, setting_get_priority },
1028   { NSSM_REG_RESTART_DELAY, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
1029   { NSSM_REG_STDIN, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },
1030   { NSSM_REG_STDIN NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDIN_SHARING, false, 0, setting_set_number, setting_get_number },
1031   { NSSM_REG_STDIN NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDIN_DISPOSITION, false, 0, setting_set_number, setting_get_number },
1032   { NSSM_REG_STDIN NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDIN_FLAGS, false, 0, setting_set_number, setting_get_number },
1033   { NSSM_REG_STDOUT, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },
1034   { NSSM_REG_STDOUT NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDOUT_SHARING, false, 0, setting_set_number, setting_get_number },
1035   { NSSM_REG_STDOUT NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDOUT_DISPOSITION, false, 0, setting_set_number, setting_get_number },
1036   { NSSM_REG_STDOUT NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDOUT_FLAGS, false, 0, setting_set_number, setting_get_number },
1037   { NSSM_REG_STDOUT NSSM_REG_STDIO_COPY_AND_TRUNCATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
1038   { NSSM_REG_STDERR, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },
1039   { NSSM_REG_STDERR NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDERR_SHARING, false, 0, setting_set_number, setting_get_number },
1040   { NSSM_REG_STDERR NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDERR_DISPOSITION, false, 0, setting_set_number, setting_get_number },
1041   { NSSM_REG_STDERR NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDERR_FLAGS, false, 0, setting_set_number, setting_get_number },
1042   { NSSM_REG_STDERR NSSM_REG_STDIO_COPY_AND_TRUNCATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
1043   { NSSM_REG_STOP_METHOD_SKIP, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
1044   { NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_CONSOLE_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },
1045   { NSSM_REG_KILL_WINDOW_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_WINDOW_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },
1046   { NSSM_REG_KILL_THREADS_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_THREADS_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },
1047   { NSSM_REG_KILL_PROCESS_TREE, REG_DWORD, (void *) 1, false, 0, setting_set_number, setting_get_number },
1048   { NSSM_REG_THROTTLE, REG_DWORD, (void *) NSSM_RESET_THROTTLE_RESTART, false, 0, setting_set_number, setting_get_number },
1049   { NSSM_REG_ROTATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
1050   { NSSM_REG_ROTATE_ONLINE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
1051   { NSSM_REG_ROTATE_SECONDS, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
1052   { NSSM_REG_ROTATE_BYTES_LOW, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
1053   { NSSM_REG_ROTATE_BYTES_HIGH, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
1054   { NSSM_REG_ROTATE_DELAY, REG_DWORD, (void *) NSSM_ROTATE_DELAY, false, 0, setting_set_number, setting_get_number },
1055   { NSSM_NATIVE_DEPENDONGROUP, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_dependongroup, native_get_dependongroup },
1056   { NSSM_NATIVE_DEPENDONSERVICE, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_dependonservice, native_get_dependonservice },
1057   { NSSM_NATIVE_DESCRIPTION, REG_SZ, _T(""), true, 0, native_set_description, native_get_description },
1058   { NSSM_NATIVE_DISPLAYNAME, REG_SZ, NULL, true, 0, native_set_displayname, native_get_displayname },
1059   { NSSM_NATIVE_IMAGEPATH, REG_EXPAND_SZ, NULL, true, 0, native_set_imagepath, native_get_imagepath },
1060   { NSSM_NATIVE_OBJECTNAME, REG_SZ, NSSM_LOCALSYSTEM_ACCOUNT, true, 0, native_set_objectname, native_get_objectname },
1061   { NSSM_NATIVE_NAME, REG_SZ, NULL, true, 0, native_set_name, native_get_name },
1062   { NSSM_NATIVE_STARTUP, REG_SZ, NULL, true, 0, native_set_startup, native_get_startup },
1063   { NSSM_NATIVE_TYPE, REG_SZ, NULL, true, 0, native_set_type, native_get_type },
1064   { NULL, NULL, NULL, NULL, NULL }
1065 };