Use close_handle().
[nssm.git] / settings.cpp
1 #include "nssm.h"\r
2 /* XXX: (value && value->string) is probably bogus because value is probably never null */\r
3 \r
4 /* Affinity. */\r
5 #define NSSM_AFFINITY_ALL _T("All")\r
6 /* Default value. */\r
7 #define NSSM_DEFAULT_STRING _T("Default")\r
8 \r
9 extern const TCHAR *exit_action_strings[];\r
10 extern const TCHAR *startup_strings[];\r
11 extern const TCHAR *priority_strings[];\r
12 extern const TCHAR *hook_event_strings[];\r
13 extern const TCHAR *hook_action_strings[];\r
14 \r
15 /* Does the parameter refer to the default value of the setting? */\r
16 static inline int is_default(const TCHAR *value) {\r
17   return (str_equiv(value, NSSM_DEFAULT_STRING) || str_equiv(value, _T("*")) || ! value[0]);\r
18 }\r
19 \r
20 /* What type of parameter is it parameter? */\r
21 static inline bool is_string_type(const unsigned long type) {\r
22   return (type == REG_MULTI_SZ || type == REG_EXPAND_SZ || type == REG_SZ);\r
23 }\r
24 static inline bool is_numeric_type(const unsigned long type) {\r
25   return (type == REG_DWORD);\r
26 }\r
27 \r
28 static int value_from_string(const TCHAR *name, value_t *value, const TCHAR *string) {\r
29   size_t len = _tcslen(string);\r
30   if (! len++) {\r
31     value->string = 0;\r
32     return 0;\r
33   }\r
34 \r
35   value->string = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));\r
36   if (! value->string) {\r
37     print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, name, _T("value_from_string()"));\r
38     return -1;\r
39   }\r
40 \r
41   if (_sntprintf_s(value->string, len, _TRUNCATE, _T("%s"), string) < 0) {\r
42     HeapFree(GetProcessHeap(), 0, value->string);\r
43     print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, name, _T("value_from_string()"));\r
44     return -1;\r
45   }\r
46 \r
47   return 1;\r
48 }\r
49 \r
50 /* Functions to manage NSSM-specific settings in the registry. */\r
51 static int setting_set_number(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
52   HKEY key = (HKEY) param;\r
53   if (! key) return -1;\r
54 \r
55   unsigned long number;\r
56   long error;\r
57 \r
58   /* Resetting to default? */\r
59   if (! value || ! value->string) {\r
60     error = RegDeleteValue(key, name);\r
61     if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;\r
62     print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));\r
63     return -1;\r
64   }\r
65   if (str_number(value->string, &number)) return -1;\r
66 \r
67   if (default_value && number == PtrToUlong(default_value)) {\r
68     error = RegDeleteValue(key, name);\r
69     if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;\r
70     print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));\r
71     return -1;\r
72   }\r
73 \r
74   if (set_number(key, (TCHAR *) name, number)) return -1;\r
75 \r
76   return 1;\r
77 }\r
78 \r
79 static int setting_get_number(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
80   HKEY key = (HKEY) param;\r
81   return get_number(key, (TCHAR *) name, &value->numeric, false);\r
82 }\r
83 \r
84 static int setting_set_string(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
85   HKEY key = (HKEY) param;\r
86   if (! key) return -1;\r
87 \r
88   long error;\r
89 \r
90   /* Resetting to default? */\r
91   if (! value || ! value->string) {\r
92     if (default_value) value->string = (TCHAR *) default_value;\r
93     else {\r
94       error = RegDeleteValue(key, name);\r
95       if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;\r
96       print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));\r
97       return -1;\r
98     }\r
99   }\r
100   if (default_value && _tcslen((TCHAR *) default_value) && str_equiv(value->string, (TCHAR *) default_value)) {\r
101     error = RegDeleteValue(key, name);\r
102     if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;\r
103     print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));\r
104     return -1;\r
105   }\r
106 \r
107   if (set_expand_string(key, (TCHAR *) name, value->string)) return -1;\r
108 \r
109   return 1;\r
110 }\r
111 \r
112 static int setting_get_string(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
113   HKEY key = (HKEY) param;\r
114   TCHAR buffer[VALUE_LENGTH];\r
115 \r
116   if (get_string(key, (TCHAR *) name, (TCHAR *) buffer, (unsigned long) sizeof(buffer), false, false, false)) return -1;\r
117 \r
118   return value_from_string(name, value, buffer);\r
119 }\r
120 \r
121 static int setting_not_dumpable(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
122   return 0;\r
123 }\r
124 \r
125 static int setting_dump_string(const TCHAR *service_name, void *param, const TCHAR *name, const value_t *value, const TCHAR *additional) {\r
126   TCHAR quoted_service_name[SERVICE_NAME_LENGTH * 2];\r
127   TCHAR quoted_value[VALUE_LENGTH * 2];\r
128   TCHAR quoted_additional[VALUE_LENGTH * 2];\r
129   TCHAR quoted_nssm[EXE_LENGTH * 2];\r
130 \r
131   if (quote(service_name, quoted_service_name, _countof(quoted_service_name))) return 1;\r
132 \r
133   if (additional) {\r
134     if (_tcslen(additional)) {\r
135       if (quote(additional, quoted_additional, _countof(quoted_additional))) return 3;\r
136     }\r
137   else _sntprintf_s(quoted_additional, _countof(quoted_additional), _TRUNCATE, _T("\"\""));\r
138   }\r
139   else quoted_additional[0] = _T('\0');\r
140 \r
141   unsigned long type = (unsigned long) param;\r
142   if (is_string_type(type)) {\r
143     if (_tcslen(value->string)) {\r
144       if (quote(value->string, quoted_value, _countof(quoted_value))) return 2;\r
145     }\r
146     else _sntprintf_s(quoted_value, _countof(quoted_value), _TRUNCATE, _T("\"\""));\r
147   }\r
148   else if (is_numeric_type(type)) _sntprintf_s(quoted_value, _countof(quoted_value), _TRUNCATE, _T("%lu"), value->numeric);\r
149   else return 2;\r
150 \r
151   if (quote(nssm_exe(), quoted_nssm, _countof(quoted_nssm))) return 3;\r
152   if (_tcslen(quoted_additional)) _tprintf(_T("%s set %s %s %s %s\n"), quoted_nssm, quoted_service_name, name, quoted_additional, quoted_value);\r
153   else _tprintf(_T("%s set %s %s %s\n"), quoted_nssm, quoted_service_name, name, quoted_value);\r
154   return 0;\r
155 }\r
156 \r
157 static int setting_set_exit_action(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
158   unsigned long exitcode;\r
159   TCHAR *code;\r
160   TCHAR action_string[ACTION_LEN];\r
161 \r
162   if (additional) {\r
163     /* Default action? */\r
164     if (is_default(additional)) code = 0;\r
165     else {\r
166       if (str_number(additional, &exitcode)) return -1;\r
167       code = (TCHAR *) additional;\r
168     }\r
169   }\r
170 \r
171   HKEY key = open_registry(service_name, name, KEY_WRITE);\r
172   if (! key) return -1;\r
173 \r
174   long error;\r
175   int ret = 1;\r
176 \r
177   /* Resetting to default? */\r
178   if (value && value->string) _sntprintf_s(action_string, _countof(action_string), _TRUNCATE, _T("%s"), value->string);\r
179   else {\r
180     if (code) {\r
181       /* Delete explicit action. */\r
182       error = RegDeleteValue(key, code);\r
183       RegCloseKey(key);\r
184       if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;\r
185       print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, code, service_name, error_string(error));\r
186       return -1;\r
187     }\r
188     else {\r
189       /* Explicitly keep the default action. */\r
190       if (default_value) _sntprintf_s(action_string, _countof(action_string), _TRUNCATE, _T("%s"), (TCHAR *) default_value);\r
191       ret = 0;\r
192     }\r
193   }\r
194 \r
195   /* Validate the string. */\r
196   for (int i = 0; exit_action_strings[i]; i++) {\r
197     if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {\r
198       if (default_value && str_equiv(action_string, (TCHAR *) default_value)) ret = 0;\r
199       if (RegSetValueEx(key, code, 0, REG_SZ, (const unsigned char *) exit_action_strings[i], (unsigned long) (_tcslen(action_string) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) {\r
200         print_message(stderr, NSSM_MESSAGE_SETVALUE_FAILED, code, service_name, error_string(GetLastError()));\r
201         RegCloseKey(key);\r
202         return -1;\r
203       }\r
204 \r
205       RegCloseKey(key);\r
206       return ret;\r
207     }\r
208   }\r
209 \r
210   print_message(stderr, NSSM_MESSAGE_INVALID_EXIT_ACTION, action_string);\r
211   for (int i = 0; exit_action_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), exit_action_strings[i]);\r
212 \r
213   return -1;\r
214 }\r
215 \r
216 static int setting_get_exit_action(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
217   unsigned long exitcode = 0;\r
218   unsigned long *code = 0;\r
219 \r
220   if (additional) {\r
221     if (! is_default(additional)) {\r
222       if (str_number(additional, &exitcode)) return -1;\r
223       code = &exitcode;\r
224     }\r
225   }\r
226 \r
227   TCHAR action_string[ACTION_LEN];\r
228   bool default_action;\r
229   if (get_exit_action(service_name, code, action_string, &default_action)) return -1;\r
230 \r
231   value_from_string(name, value, action_string);\r
232 \r
233   if (default_action && ! _tcsnicmp((const TCHAR *) action_string, (TCHAR *) default_value, ACTION_LEN)) return 0;\r
234   return 1;\r
235 }\r
236 \r
237 static int setting_dump_exit_action(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
238   int errors = 0;\r
239   HKEY key = open_registry(service_name, NSSM_REG_EXIT, KEY_READ);\r
240   if (! key) return -1;\r
241 \r
242   TCHAR code[16];\r
243   unsigned long index = 0;\r
244   while (true) {\r
245     int ret = enumerate_registry_values(key, &index, code, _countof(code));\r
246     if (ret == ERROR_NO_MORE_ITEMS) break;\r
247     if (ret != ERROR_SUCCESS) continue;\r
248     bool valid = true;\r
249     int i;\r
250     for (i = 0; i < _countof(code); i++) {\r
251       if (! code[i]) break;\r
252       if (code[i] >= _T('0') || code[i] <= _T('9')) continue;\r
253       valid = false;\r
254       break;\r
255     }\r
256     if (! valid) continue;\r
257 \r
258     TCHAR *additional = (code[i]) ? code : NSSM_DEFAULT_STRING;\r
259 \r
260     ret = setting_get_exit_action(service_name, 0, name, default_value, value, additional);\r
261     if (ret == 1) {\r
262       if (setting_dump_string(service_name, (void *) REG_SZ, name, value, additional)) errors++;\r
263     }\r
264     else if (ret < 0) errors++;\r
265   }\r
266 \r
267   if (errors) return -1;\r
268   return 0;\r
269 }\r
270 \r
271 static inline bool split_hook_name(const TCHAR *hook_name, TCHAR *hook_event, TCHAR *hook_action) {\r
272   TCHAR *s;\r
273 \r
274   for (s = (TCHAR *) hook_name; *s; s++) {\r
275     if (*s == _T('/')) {\r
276       *s = _T('\0');\r
277       _sntprintf_s(hook_event, HOOK_NAME_LENGTH, _TRUNCATE, _T("%s"), hook_name);\r
278       *s++ = _T('/');\r
279       _sntprintf_s(hook_action, HOOK_NAME_LENGTH, _TRUNCATE, _T("%s"), s);\r
280       return valid_hook_name(hook_event, hook_action, false);\r
281     }\r
282   }\r
283 \r
284   print_message(stderr, NSSM_MESSAGE_INVALID_HOOK_NAME, hook_name);\r
285   return false;\r
286 }\r
287 \r
288 static int setting_set_hook(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
289   TCHAR hook_event[HOOK_NAME_LENGTH];\r
290   TCHAR hook_action[HOOK_NAME_LENGTH];\r
291   if (! split_hook_name(additional, hook_event, hook_action)) return -1;\r
292 \r
293   TCHAR *cmd;\r
294   if (value && value->string) cmd = value->string;\r
295   else cmd = _T("");\r
296 \r
297   if (set_hook(service_name, hook_event, hook_action, cmd)) return -1;\r
298   if (! _tcslen(cmd)) return 0;\r
299   return 1;\r
300 }\r
301 \r
302 static int setting_get_hook(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
303   TCHAR hook_event[HOOK_NAME_LENGTH];\r
304   TCHAR hook_action[HOOK_NAME_LENGTH];\r
305   if (! split_hook_name(additional, hook_event, hook_action)) return -1;\r
306 \r
307   TCHAR cmd[CMD_LENGTH];\r
308   if (get_hook(service_name, hook_event, hook_action, cmd, sizeof(cmd))) return -1;\r
309 \r
310   value_from_string(name, value, cmd);\r
311 \r
312   if (! _tcslen(cmd)) return 0;\r
313   return 1;\r
314 }\r
315 \r
316 static int setting_dump_hooks(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
317   int i, j;\r
318 \r
319   int errors = 0;\r
320   for (i = 0; hook_event_strings[i]; i++) {\r
321     const TCHAR *hook_event = hook_event_strings[i];\r
322     for (j = 0; hook_action_strings[j]; j++) {\r
323       const TCHAR *hook_action = hook_action_strings[j];\r
324       if (! valid_hook_name(hook_event, hook_action, true)) continue;\r
325 \r
326       TCHAR hook_name[HOOK_NAME_LENGTH];\r
327       _sntprintf_s(hook_name, _countof(hook_name), _TRUNCATE, _T("%s/%s"), hook_event, hook_action);\r
328 \r
329       int ret = setting_get_hook(service_name, param, name, default_value, value, hook_name);\r
330       if (ret != 1) {\r
331         if (ret < 0) errors++;\r
332         continue;\r
333       }\r
334 \r
335       if (setting_dump_string(service_name, (void *) REG_SZ, name, value, hook_name)) errors++;\r
336     }\r
337   }\r
338 \r
339   if (errors) return -1;\r
340   return 0;\r
341 }\r
342 \r
343 static int setting_set_affinity(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
344   HKEY key = (HKEY) param;\r
345   if (! key) return -1;\r
346 \r
347   long error;\r
348   __int64 mask;\r
349   __int64 system_affinity = 0LL;\r
350 \r
351   if (value && value->string) {\r
352     DWORD_PTR affinity;\r
353     if (! GetProcessAffinityMask(GetCurrentProcess(), &affinity, (DWORD_PTR *) &system_affinity)) system_affinity = ~0;\r
354 \r
355     if (is_default(value->string) || str_equiv(value->string, NSSM_AFFINITY_ALL)) mask = 0LL;\r
356     else if (affinity_string_to_mask(value->string, &mask)) {\r
357       print_message(stderr, NSSM_MESSAGE_BOGUS_AFFINITY_MASK, value->string, num_cpus() - 1);\r
358       return -1;\r
359     }\r
360   }\r
361   else mask = 0LL;\r
362 \r
363   if (! mask) {\r
364     error = RegDeleteValue(key, name);\r
365     if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;\r
366     print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));\r
367     return -1;\r
368   }\r
369 \r
370   /* Canonicalise. */\r
371   TCHAR *canon = 0;\r
372   if (affinity_mask_to_string(mask, &canon)) canon = value->string;\r
373 \r
374   __int64 effective_affinity = mask & system_affinity;\r
375   if (effective_affinity != mask) {\r
376     /* Requested CPUs did not intersect with available CPUs? */\r
377     if (! effective_affinity) mask = effective_affinity = system_affinity;\r
378 \r
379     TCHAR *system = 0;\r
380     if (! affinity_mask_to_string(system_affinity, &system)) {\r
381       TCHAR *effective = 0;\r
382       if (! affinity_mask_to_string(effective_affinity, &effective)) {\r
383         print_message(stderr, NSSM_MESSAGE_EFFECTIVE_AFFINITY_MASK, value->string, system, effective);\r
384         HeapFree(GetProcessHeap(), 0, effective);\r
385       }\r
386       HeapFree(GetProcessHeap(), 0, system);\r
387     }\r
388   }\r
389 \r
390   if (RegSetValueEx(key, name, 0, REG_SZ, (const unsigned char *) canon, (unsigned long) (_tcslen(canon) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) {\r
391     if (canon != value->string) HeapFree(GetProcessHeap(), 0, canon);\r
392     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, name, error_string(GetLastError()), 0);\r
393     return -1;\r
394   }\r
395 \r
396   if (canon != value->string) HeapFree(GetProcessHeap(), 0, canon);\r
397   return 1;\r
398 }\r
399 \r
400 static int setting_get_affinity(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
401   HKEY key = (HKEY) param;\r
402   if (! key) return -1;\r
403 \r
404   unsigned long type;\r
405   TCHAR *buffer = 0;\r
406   unsigned long buflen = 0;\r
407 \r
408   int ret = RegQueryValueEx(key, name, 0, &type, 0, &buflen);\r
409   if (ret == ERROR_FILE_NOT_FOUND) {\r
410     if (value_from_string(name, value, NSSM_AFFINITY_ALL) == 1) return 0;\r
411     return -1;\r
412   }\r
413   if (ret != ERROR_SUCCESS) return -1;\r
414 \r
415   if (type != REG_SZ) return -1;\r
416 \r
417   buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, buflen);\r
418   if (! buffer) {\r
419     print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("affinity"), _T("setting_get_affinity"));\r
420     return -1;\r
421   }\r
422 \r
423   if (get_string(key, (TCHAR *) name, buffer, buflen, false, false, true)) {\r
424     HeapFree(GetProcessHeap(), 0, buffer);\r
425     return -1;\r
426   }\r
427 \r
428   __int64 affinity;\r
429   if (affinity_string_to_mask(buffer, &affinity)) {\r
430     print_message(stderr, NSSM_MESSAGE_BOGUS_AFFINITY_MASK, buffer, num_cpus() - 1);\r
431     HeapFree(GetProcessHeap(), 0, buffer);\r
432     return -1;\r
433   }\r
434 \r
435   HeapFree(GetProcessHeap(), 0, buffer);\r
436 \r
437   /* Canonicalise. */\r
438   if (affinity_mask_to_string(affinity, &buffer)) {\r
439     if (buffer) HeapFree(GetProcessHeap(), 0, buffer);\r
440     return -1;\r
441   }\r
442 \r
443   ret = value_from_string(name, value, buffer);\r
444   HeapFree(GetProcessHeap(), 0, buffer);\r
445   return ret;\r
446 }\r
447 \r
448 static int setting_set_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
449   HKEY key = (HKEY) param;\r
450   if (! param) return -1;\r
451 \r
452   TCHAR *string = 0;\r
453   TCHAR *unformatted = 0;\r
454   unsigned long envlen;\r
455   unsigned long newlen = 0;\r
456   int op = 0;\r
457   if (value && value->string && value->string[0]) {\r
458     string = value->string;\r
459     switch (string[0]) {\r
460       case _T('+'): op = 1; break;\r
461       case _T('-'): op = -1; break;\r
462       case _T(':'): string++; break;\r
463     }\r
464   }\r
465 \r
466   if (op) {\r
467     string++;\r
468     TCHAR *env = 0;\r
469     if (get_environment((TCHAR *) service_name, key, (TCHAR *) name, &env, &envlen)) return -1;\r
470     if (env) {\r
471       int ret;\r
472       if (op > 0) ret = append_to_environment_block(env, envlen, string, &unformatted, &newlen);\r
473       else ret = remove_from_environment_block(env, envlen, string, &unformatted, &newlen);\r
474       if (envlen) HeapFree(GetProcessHeap(), 0, env);\r
475       if (ret) return -1;\r
476 \r
477       string = unformatted;\r
478     }\r
479     else {\r
480       /*\r
481         No existing environment.\r
482         We can't remove from an empty environment so just treat an add\r
483         operation as setting a new string.\r
484       */\r
485       if (op < 0) return 0;\r
486       op = 0;\r
487     }\r
488   }\r
489 \r
490   if (! string || ! string[0]) {\r
491     long error = RegDeleteValue(key, name);\r
492     if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;\r
493     print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));\r
494     return -1;\r
495   }\r
496 \r
497   if (! op) {\r
498     if (unformat_double_null(string, (unsigned long) _tcslen(string), &unformatted, &newlen)) return -1;\r
499   }\r
500 \r
501   if (test_environment(unformatted)) {\r
502     HeapFree(GetProcessHeap(), 0, unformatted);\r
503     print_message(stderr, NSSM_GUI_INVALID_ENVIRONMENT);\r
504     return -1;\r
505   }\r
506 \r
507   if (RegSetValueEx(key, name, 0, REG_MULTI_SZ, (const unsigned char *) unformatted, (unsigned long) newlen * sizeof(TCHAR)) != ERROR_SUCCESS) {\r
508     if (newlen) HeapFree(GetProcessHeap(), 0, unformatted);\r
509     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV, error_string(GetLastError()), 0);\r
510     return -1;\r
511   }\r
512 \r
513   if (newlen) HeapFree(GetProcessHeap(), 0, unformatted);\r
514   return 1;\r
515 }\r
516 \r
517 static int setting_get_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
518   HKEY key = (HKEY) param;\r
519   if (! param) return -1;\r
520 \r
521   TCHAR *env = 0;\r
522   unsigned long envlen;\r
523   if (get_environment((TCHAR *) service_name, key, (TCHAR *) name, &env, &envlen)) return -1;\r
524   if (! envlen) return 0;\r
525 \r
526   TCHAR *formatted;\r
527   unsigned long newlen;\r
528   if (format_double_null(env, envlen, &formatted, &newlen)) return -1;\r
529 \r
530   int ret;\r
531   if (additional) {\r
532     /* Find named environment variable. */\r
533     TCHAR *s;\r
534     size_t len = _tcslen(additional);\r
535     for (s = env; *s; s++) {\r
536       /* Look for <additional>=<string> NULL NULL */\r
537       if (! _tcsnicmp(s, additional, len) && s[len] == _T('=')) {\r
538         /* Strip <key>= */\r
539         s += len + 1;\r
540         ret = value_from_string(name, value, s);\r
541         HeapFree(GetProcessHeap(), 0, env);\r
542         return ret;\r
543       }\r
544 \r
545       /* Skip this string. */\r
546       for ( ; *s; s++);\r
547     }\r
548     HeapFree(GetProcessHeap(), 0, env);\r
549     return 0;\r
550   }\r
551 \r
552   HeapFree(GetProcessHeap(), 0, env);\r
553 \r
554   ret = value_from_string(name, value, formatted);\r
555   if (newlen) HeapFree(GetProcessHeap(), 0, formatted);\r
556   return ret;\r
557 }\r
558 \r
559 static int setting_dump_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
560   int errors = 0;\r
561   HKEY key = (HKEY) param;\r
562   if (! param) return -1;\r
563 \r
564   TCHAR *env = 0;\r
565   unsigned long envlen;\r
566   if (get_environment((TCHAR *) service_name, key, (TCHAR *) name, &env, &envlen)) return -1;\r
567   if (! envlen) return 0;\r
568 \r
569   TCHAR *s;\r
570   for (s = env; *s; s++) {\r
571     size_t len = _tcslen(s) + 2;\r
572     value->string = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));\r
573     if (! value->string) {\r
574       print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("dump"), _T("setting_dump_environment"));\r
575       break;\r
576     }\r
577 \r
578     _sntprintf_s(value->string, len, _TRUNCATE, _T("%c%s"), (s > env) ? _T('+') : _T(':'), s);\r
579     if (setting_dump_string(service_name, (void *) REG_SZ, name, value, 0)) errors++;\r
580     HeapFree(GetProcessHeap(), 0, value->string);\r
581     value->string = 0;\r
582 \r
583     for ( ; *s; s++);\r
584   }\r
585 \r
586   HeapFree(GetProcessHeap(), 0, env);\r
587 \r
588   if (errors) return 1;\r
589   return 0;\r
590 }\r
591 \r
592 static int setting_set_priority(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
593   HKEY key = (HKEY) param;\r
594   if (! param) return -1;\r
595 \r
596   TCHAR *priority_string;\r
597   int i;\r
598   long error;\r
599 \r
600   if (value && value->string) priority_string = value->string;\r
601   else if (default_value) priority_string = (TCHAR *) default_value;\r
602   else {\r
603     error = RegDeleteValue(key, name);\r
604     if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;\r
605     print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));\r
606     return -1;\r
607   }\r
608 \r
609   for (i = 0; priority_strings[i]; i++) {\r
610     if (! str_equiv(priority_strings[i], priority_string)) continue;\r
611 \r
612     if (default_value && str_equiv(priority_string, (TCHAR *) default_value)) {\r
613       error = RegDeleteValue(key, name);\r
614       if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;\r
615       print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));\r
616       return -1;\r
617     }\r
618 \r
619     if (set_number(key, (TCHAR *) name, priority_index_to_constant(i))) return -1;\r
620     return 1;\r
621   }\r
622 \r
623   print_message(stderr, NSSM_MESSAGE_INVALID_PRIORITY, priority_string);\r
624   for (i = 0; priority_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), priority_strings[i]);\r
625 \r
626   return -1;\r
627 }\r
628 \r
629 static int setting_get_priority(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
630   HKEY key = (HKEY) param;\r
631   if (! param) return -1;\r
632 \r
633   unsigned long constant;\r
634   switch (get_number(key, (TCHAR *) name, &constant, false)) {\r
635     case 0:\r
636       if (value_from_string(name, value, (const TCHAR *) default_value) == -1) return -1;\r
637       return 0;\r
638     case -1: return -1;\r
639   }\r
640 \r
641   return value_from_string(name, value, priority_strings[priority_constant_to_index(constant)]);\r
642 }\r
643 \r
644 static int setting_dump_priority(const TCHAR *service_name, void *key_ptr, const TCHAR *name, void *setting_ptr, value_t *value, const TCHAR *additional) {\r
645   settings_t *setting = (settings_t *) setting_ptr;\r
646   int ret = setting_get_priority(service_name, key_ptr, name, (void *) setting->default_value, value, 0);\r
647   if (ret != 1) return ret;\r
648   return setting_dump_string(service_name, (void *) REG_SZ, name, value, 0);\r
649 }\r
650 \r
651 /* Functions to manage native service settings. */\r
652 static int native_set_dependon(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR **dependencies, unsigned long *dependencieslen, value_t *value, int type) {\r
653   *dependencieslen = 0;\r
654   if (! value || ! value->string || ! value->string[0]) return 0;\r
655 \r
656   TCHAR *string = value->string;\r
657   unsigned long buflen;\r
658   int op = 0;\r
659   switch (string[0]) {\r
660     case _T('+'): op = 1; break;\r
661     case _T('-'): op = -1; break;\r
662     case _T(':'): string++; break;\r
663   }\r
664 \r
665   if (op) {\r
666     string++;\r
667     TCHAR *buffer = 0;\r
668     if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, type)) return -1;\r
669     if (buffer) {\r
670       int ret;\r
671       if (op > 0) ret = append_to_dependencies(buffer, buflen, string, dependencies, dependencieslen, type);\r
672       else ret = remove_from_dependencies(buffer, buflen, string, dependencies, dependencieslen, type);\r
673       if (buflen) HeapFree(GetProcessHeap(), 0, buffer);\r
674       return ret;\r
675     }\r
676     else {\r
677       /*\r
678         No existing list.\r
679         We can't remove from an empty list so just treat an add\r
680         operation as setting a new string.\r
681       */\r
682       if (op < 0) return 0;\r
683       op = 0;\r
684     }\r
685   }\r
686 \r
687   if (! op) {\r
688     TCHAR *unformatted = 0;\r
689     unsigned long newlen;\r
690     if (unformat_double_null(string, (unsigned long) _tcslen(string), &unformatted, &newlen)) return -1;\r
691 \r
692     if (type == DEPENDENCY_GROUPS) {\r
693       /* Prepend group identifier. */\r
694       unsigned long missing = 0;\r
695       TCHAR *canon = unformatted;\r
696       size_t canonlen = 0;\r
697       TCHAR *s;\r
698       for (s = unformatted; *s; s++) {\r
699         if (*s != SC_GROUP_IDENTIFIER) missing++;\r
700         size_t len = _tcslen(s);\r
701         canonlen += len + 1;\r
702         s += len;\r
703       }\r
704 \r
705       if (missing) {\r
706         /* Missing identifiers plus double NULL terminator. */\r
707         canonlen += missing + 1;\r
708 \r
709         canon = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, canonlen * sizeof(TCHAR));\r
710         if (! canon) {\r
711           print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canon"), _T("native_set_dependon"));\r
712           if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);\r
713           return -1;\r
714         }\r
715 \r
716         size_t i = 0;\r
717         for (s = unformatted; *s; s++) {\r
718           if (*s != SC_GROUP_IDENTIFIER) canon[i++] = SC_GROUP_IDENTIFIER;\r
719           size_t len = _tcslen(s);\r
720           memmove(canon + i, s, (len + 1) * sizeof(TCHAR));\r
721           i += len + 1;\r
722           s += len;\r
723         }\r
724 \r
725         HeapFree(GetProcessHeap(), 0, unformatted);\r
726         unformatted = canon;\r
727         newlen = (unsigned long) canonlen;\r
728       }\r
729     }\r
730 \r
731     *dependencies = unformatted;\r
732     *dependencieslen = newlen;\r
733   }\r
734 \r
735   return 0;\r
736 }\r
737 \r
738 static int native_set_dependongroup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
739   SC_HANDLE service_handle = (SC_HANDLE) param;\r
740   if (! service_handle) return -1;\r
741 \r
742   /*\r
743     Get existing service dependencies because we must set both types together.\r
744   */\r
745   TCHAR *services_buffer;\r
746   unsigned long services_buflen;\r
747   if (get_service_dependencies(service_name, service_handle, &services_buffer, &services_buflen, DEPENDENCY_SERVICES)) return -1;\r
748 \r
749   if (! value || ! value->string || ! value->string[0]) {\r
750     if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, services_buffer, 0, 0, 0)) {\r
751       print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));\r
752       if (services_buffer) HeapFree(GetProcessHeap(), 0, services_buffer);\r
753       return -1;\r
754     }\r
755 \r
756     if (services_buffer) HeapFree(GetProcessHeap(), 0, services_buffer);\r
757     return 0;\r
758   }\r
759 \r
760   /* Update the group list. */\r
761   TCHAR *groups_buffer;\r
762   unsigned long groups_buflen;\r
763   if (native_set_dependon(service_name, service_handle, &groups_buffer, &groups_buflen, value, DEPENDENCY_GROUPS)) return -1;\r
764 \r
765   TCHAR *dependencies;\r
766   if (services_buflen > 2) {\r
767     dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (groups_buflen + services_buflen) * sizeof(TCHAR));\r
768     if (! dependencies) {\r
769       print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("dependencies"), _T("native_set_dependongroup"));\r
770       if (groups_buffer) HeapFree(GetProcessHeap(), 0, groups_buffer);\r
771       if (services_buffer) HeapFree(GetProcessHeap(), 0, services_buffer);\r
772       return -1;\r
773     }\r
774 \r
775     memmove(dependencies, services_buffer, services_buflen * sizeof(TCHAR));\r
776     memmove(dependencies + services_buflen - 1, groups_buffer, groups_buflen * sizeof(TCHAR));\r
777   }\r
778   else dependencies = groups_buffer;\r
779 \r
780   int ret = 1;\r
781   if (set_service_dependencies(service_name, service_handle, dependencies)) ret = -1;\r
782   if (dependencies != groups_buffer) HeapFree(GetProcessHeap(), 0, dependencies);\r
783   if (groups_buffer) HeapFree(GetProcessHeap(), 0, groups_buffer);\r
784   if (services_buffer) HeapFree(GetProcessHeap(), 0, services_buffer);\r
785 \r
786   return ret;\r
787 }\r
788 \r
789 static int native_get_dependongroup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
790   SC_HANDLE service_handle = (SC_HANDLE) param;\r
791   if (! service_handle) return -1;\r
792 \r
793   TCHAR *buffer;\r
794   unsigned long buflen;\r
795   if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_GROUPS)) return -1;\r
796 \r
797   int ret;\r
798   if (buflen) {\r
799     TCHAR *formatted;\r
800     unsigned long newlen;\r
801     if (format_double_null(buffer, buflen, &formatted, &newlen)) {\r
802       HeapFree(GetProcessHeap(), 0, buffer);\r
803       return -1;\r
804     }\r
805 \r
806     ret = value_from_string(name, value, formatted);\r
807     HeapFree(GetProcessHeap(), 0, formatted);\r
808     HeapFree(GetProcessHeap(), 0, buffer);\r
809   }\r
810   else {\r
811     value->string = 0;\r
812     ret = 0;\r
813   }\r
814 \r
815   return ret;\r
816 }\r
817 \r
818 static int setting_dump_dependon(const TCHAR *service_name, SC_HANDLE service_handle, const TCHAR *name, int type, value_t *value) {\r
819   int errors = 0;\r
820 \r
821   TCHAR *dependencies = 0;\r
822   unsigned long dependencieslen;\r
823   if (get_service_dependencies(service_name, service_handle, &dependencies, &dependencieslen, type)) return -1;\r
824   if (! dependencieslen) return 0;\r
825 \r
826   TCHAR *s;\r
827   for (s = dependencies; *s; s++) {\r
828     size_t len = _tcslen(s) + 2;\r
829     value->string = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));\r
830     if (! value->string) {\r
831       print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("dump"), _T("setting_dump_dependon"));\r
832       break;\r
833     }\r
834 \r
835     _sntprintf_s(value->string, len, _TRUNCATE, _T("%c%s"), (s > dependencies) ? _T('+') : _T(':'), s);\r
836     if (setting_dump_string(service_name, (void *) REG_SZ, name, value, 0)) errors++;\r
837     HeapFree(GetProcessHeap(), 0, value->string);\r
838     value->string = 0;\r
839 \r
840     for ( ; *s; s++);\r
841   }\r
842 \r
843   HeapFree(GetProcessHeap(), 0, dependencies);\r
844 \r
845   if (errors) return 1;\r
846   return 0;\r
847 }\r
848 \r
849 static int native_dump_dependongroup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
850   return setting_dump_dependon(service_name, (SC_HANDLE) param, name, DEPENDENCY_GROUPS, value);\r
851 }\r
852 \r
853 static int native_set_dependonservice(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
854   SC_HANDLE service_handle = (SC_HANDLE) param;\r
855   if (! service_handle) return -1;\r
856 \r
857   /*\r
858     Get existing group dependencies because we must set both types together.\r
859   */\r
860   TCHAR *groups_buffer;\r
861   unsigned long groups_buflen;\r
862   if (get_service_dependencies(service_name, service_handle, &groups_buffer, &groups_buflen, DEPENDENCY_GROUPS)) return -1;\r
863 \r
864   if (! value || ! value->string || ! value->string[0]) {\r
865     if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, groups_buffer, 0, 0, 0)) {\r
866       print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));\r
867       if (groups_buffer) HeapFree(GetProcessHeap(), 0, groups_buffer);\r
868       return -1;\r
869     }\r
870 \r
871     if (groups_buffer) HeapFree(GetProcessHeap(), 0, groups_buffer);\r
872     return 0;\r
873   }\r
874 \r
875   /* Update the service list. */\r
876   TCHAR *services_buffer;\r
877   unsigned long services_buflen;\r
878   if (native_set_dependon(service_name, service_handle, &services_buffer, &services_buflen, value, DEPENDENCY_SERVICES)) return -1;\r
879 \r
880   TCHAR *dependencies;\r
881   if (groups_buflen > 2) {\r
882     dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (services_buflen + groups_buflen) * sizeof(TCHAR));\r
883     if (! dependencies) {\r
884       print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("dependencies"), _T("native_set_dependonservice"));\r
885       if (groups_buffer) HeapFree(GetProcessHeap(), 0, groups_buffer);\r
886       if (services_buffer) HeapFree(GetProcessHeap(), 0, services_buffer);\r
887       return -1;\r
888     }\r
889 \r
890     memmove(dependencies, services_buffer, services_buflen * sizeof(TCHAR));\r
891     memmove(dependencies + services_buflen - 1, groups_buffer, groups_buflen * sizeof(TCHAR));\r
892   }\r
893   else dependencies = services_buffer;\r
894 \r
895   int ret = 1;\r
896   if (set_service_dependencies(service_name, service_handle, dependencies)) ret = -1;\r
897   if (dependencies != services_buffer) HeapFree(GetProcessHeap(), 0, dependencies);\r
898   if (groups_buffer) HeapFree(GetProcessHeap(), 0, groups_buffer);\r
899   if (services_buffer) HeapFree(GetProcessHeap(), 0, services_buffer);\r
900 \r
901   return ret;\r
902 }\r
903 \r
904 static int native_get_dependonservice(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
905   SC_HANDLE service_handle = (SC_HANDLE) param;\r
906   if (! service_handle) return -1;\r
907 \r
908   TCHAR *buffer;\r
909   unsigned long buflen;\r
910   if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_SERVICES)) return -1;\r
911 \r
912   int ret;\r
913   if (buflen) {\r
914     TCHAR *formatted;\r
915     unsigned long newlen;\r
916     if (format_double_null(buffer, buflen, &formatted, &newlen)) {\r
917       HeapFree(GetProcessHeap(), 0, buffer);\r
918       return -1;\r
919     }\r
920 \r
921     ret = value_from_string(name, value, formatted);\r
922     HeapFree(GetProcessHeap(), 0, formatted);\r
923     HeapFree(GetProcessHeap(), 0, buffer);\r
924   }\r
925   else {\r
926     value->string = 0;\r
927     ret = 0;\r
928   }\r
929 \r
930   return ret;\r
931 }\r
932 \r
933 static int native_dump_dependonservice(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
934   return setting_dump_dependon(service_name, (SC_HANDLE) param, name, DEPENDENCY_SERVICES, value);\r
935 }\r
936 \r
937 int native_set_description(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
938   SC_HANDLE service_handle = (SC_HANDLE) param;\r
939   if (! service_handle) return -1;\r
940 \r
941   TCHAR *description = 0;\r
942   if (value) description = value->string;\r
943   if (set_service_description(service_name, service_handle, description)) return -1;\r
944 \r
945   if (description && description[0]) return 1;\r
946 \r
947   return 0;\r
948 }\r
949 \r
950 int native_get_description(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
951   SC_HANDLE service_handle = (SC_HANDLE) param;\r
952   if (! service_handle) return -1;\r
953 \r
954   TCHAR buffer[VALUE_LENGTH];\r
955   if (get_service_description(service_name, service_handle, _countof(buffer), buffer)) return -1;\r
956 \r
957   if (buffer[0]) return value_from_string(name, value, buffer);\r
958   value->string = 0;\r
959 \r
960   return 0;\r
961 }\r
962 \r
963 int native_set_displayname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
964   SC_HANDLE service_handle = (SC_HANDLE) param;\r
965   if (! service_handle) return -1;\r
966 \r
967   TCHAR *displayname = 0;\r
968   if (value && value->string) displayname = value->string;\r
969   else displayname = (TCHAR *) service_name;\r
970 \r
971   if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, displayname)) {\r
972     print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));\r
973     return -1;\r
974   }\r
975 \r
976   /*\r
977     If the display name and service name differ only in case,\r
978     ChangeServiceConfig() will return success but the display name will be\r
979     set to the service name, NOT the value passed to the function.\r
980     This appears to be a quirk of Windows rather than a bug here.\r
981   */\r
982   if (displayname != service_name && ! str_equiv(displayname, service_name)) return 1;\r
983 \r
984   return 0;\r
985 }\r
986 \r
987 int native_get_displayname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
988   SC_HANDLE service_handle = (SC_HANDLE) param;\r
989   if (! service_handle) return -1;\r
990 \r
991   QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);\r
992   if (! qsc) return -1;\r
993 \r
994   int ret = value_from_string(name, value, qsc->lpDisplayName);\r
995   HeapFree(GetProcessHeap(), 0, qsc);\r
996 \r
997   return ret;\r
998 }\r
999 \r
1000 int native_set_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
1001   HKEY key = open_service_registry(service_name, KEY_SET_VALUE, true);\r
1002   if (! key) return -1;\r
1003 \r
1004   int ret = setting_set_environment(service_name, (void *) key, name, default_value, value, additional);\r
1005   RegCloseKey(key);\r
1006   return ret;\r
1007 }\r
1008 \r
1009 int native_get_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
1010   HKEY key = open_service_registry(service_name, KEY_READ, true);\r
1011   if (! key) return -1;\r
1012 \r
1013   ZeroMemory(value, sizeof(value_t));\r
1014   int ret = setting_get_environment(service_name, (void *) key, name, default_value, value, additional);\r
1015   RegCloseKey(key);\r
1016   return ret;\r
1017 }\r
1018 \r
1019 static int native_dump_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
1020   HKEY key = open_service_registry(service_name, KEY_READ, true);\r
1021   if (! key) return -1;\r
1022 \r
1023   int ret = setting_dump_environment(service_name, (void *) key, name, default_value, value, additional);\r
1024   RegCloseKey(key);\r
1025   return ret;\r
1026 }\r
1027 \r
1028 int native_set_imagepath(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
1029   SC_HANDLE service_handle = (SC_HANDLE) param;\r
1030   if (! service_handle) return -1;\r
1031 \r
1032   /* It makes no sense to try to reset the image path. */\r
1033   if (! value || ! value->string) {\r
1034     print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);\r
1035     return -1;\r
1036   }\r
1037 \r
1038   if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, value->string, 0, 0, 0, 0, 0, 0)) {\r
1039     print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));\r
1040     return -1;\r
1041   }\r
1042 \r
1043   return 1;\r
1044 }\r
1045 \r
1046 int native_get_imagepath(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
1047   SC_HANDLE service_handle = (SC_HANDLE) param;\r
1048   if (! service_handle) return -1;\r
1049 \r
1050   QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);\r
1051   if (! qsc) return -1;\r
1052 \r
1053   int ret = value_from_string(name, value, qsc->lpBinaryPathName);\r
1054   HeapFree(GetProcessHeap(), 0, qsc);\r
1055 \r
1056   return ret;\r
1057 }\r
1058 \r
1059 int native_set_name(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
1060   print_message(stderr, NSSM_MESSAGE_CANNOT_RENAME_SERVICE);\r
1061   return -1;\r
1062 }\r
1063 \r
1064 int native_get_name(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
1065   return value_from_string(name, value, service_name);\r
1066 }\r
1067 \r
1068 int native_set_objectname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
1069   SC_HANDLE service_handle = (SC_HANDLE) param;\r
1070   if (! service_handle) return -1;\r
1071 \r
1072   /*\r
1073     Logical syntax is: nssm set <service> ObjectName <username> <password>\r
1074     That means the username is actually passed in the additional parameter.\r
1075   */\r
1076   bool localsystem = false;\r
1077   bool virtual_account = false;\r
1078   TCHAR *username = NSSM_LOCALSYSTEM_ACCOUNT;\r
1079   TCHAR *password = 0;\r
1080   if (additional) {\r
1081     username = (TCHAR *) additional;\r
1082     if (value && value->string) password = value->string;\r
1083   }\r
1084   else if (value && value->string) username = value->string;\r
1085 \r
1086   const TCHAR *well_known = well_known_username(username);\r
1087   size_t passwordsize = 0;\r
1088   if (well_known) {\r
1089     if (str_equiv(well_known, NSSM_LOCALSYSTEM_ACCOUNT)) localsystem = true;\r
1090     username = (TCHAR *) well_known;\r
1091     password = _T("");\r
1092   }\r
1093   else if (is_virtual_account(service_name, username)) virtual_account = true;\r
1094   else if (! password) {\r
1095     /* We need a password if the account requires it. */\r
1096     print_message(stderr, NSSM_MESSAGE_MISSING_PASSWORD, name);\r
1097     return -1;\r
1098   }\r
1099   else passwordsize = _tcslen(password) * sizeof(TCHAR);\r
1100 \r
1101   /*\r
1102     ChangeServiceConfig() will fail to set the username if the service is set\r
1103     to interact with the desktop.\r
1104   */\r
1105   unsigned long type = SERVICE_NO_CHANGE;\r
1106   if (! localsystem) {\r
1107     QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);\r
1108     if (! qsc) {\r
1109       if (passwordsize) SecureZeroMemory(password, passwordsize);\r
1110       return -1;\r
1111     }\r
1112 \r
1113     type = qsc->dwServiceType & ~SERVICE_INTERACTIVE_PROCESS;\r
1114     HeapFree(GetProcessHeap(), 0, qsc);\r
1115   }\r
1116 \r
1117   if (! well_known && ! virtual_account) {\r
1118     if (grant_logon_as_service(username)) {\r
1119       if (passwordsize) SecureZeroMemory(password, passwordsize);\r
1120       print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);\r
1121       return -1;\r
1122     }\r
1123   }\r
1124 \r
1125   if (! ChangeServiceConfig(service_handle, type, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, username, password, 0)) {\r
1126     if (passwordsize) SecureZeroMemory(password, passwordsize);\r
1127     print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));\r
1128     return -1;\r
1129   }\r
1130 \r
1131   if (passwordsize) SecureZeroMemory(password, passwordsize);\r
1132 \r
1133   if (localsystem) return 0;\r
1134 \r
1135   return 1;\r
1136 }\r
1137 \r
1138 int native_get_objectname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
1139   SC_HANDLE service_handle = (SC_HANDLE) param;\r
1140   if (! service_handle) return -1;\r
1141 \r
1142   QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);\r
1143   if (! qsc) return -1;\r
1144 \r
1145   int ret = value_from_string(name, value, qsc->lpServiceStartName);\r
1146   HeapFree(GetProcessHeap(), 0, qsc);\r
1147 \r
1148   return ret;\r
1149 }\r
1150 \r
1151 int native_dump_objectname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
1152   int ret = native_get_objectname(service_name, param, name, default_value, value, additional);\r
1153   if (ret != 1) return ret;\r
1154 \r
1155   /* Properly checking for a virtual account requires the actual service name. */\r
1156   if (! _tcsnicmp(NSSM_VIRTUAL_SERVICE_ACCOUNT_DOMAIN, value->string, _tcslen(NSSM_VIRTUAL_SERVICE_ACCOUNT_DOMAIN))) {\r
1157     TCHAR *name = virtual_account(service_name);\r
1158     if (! name) return -1;\r
1159     HeapFree(GetProcessHeap(), 0, value->string);\r
1160     value->string = name;\r
1161   }\r
1162   else {\r
1163     /* Do we need to dump a dummy password? */\r
1164     if (! well_known_username(value->string)) {\r
1165       /* Parameters are the other way round. */\r
1166       value_t inverted;\r
1167       inverted.string = _T("****");\r
1168       return setting_dump_string(service_name, (void *) REG_SZ, name, &inverted, value->string);\r
1169     }\r
1170   }\r
1171   return setting_dump_string(service_name, (void *) REG_SZ, name, value, 0);\r
1172 }\r
1173 \r
1174 int native_set_startup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
1175   SC_HANDLE service_handle = (SC_HANDLE) param;\r
1176   if (! service_handle) return -1;\r
1177 \r
1178   /* It makes no sense to try to reset the startup type. */\r
1179   if (! value || ! value->string) {\r
1180     print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);\r
1181     return -1;\r
1182   }\r
1183 \r
1184   /* Map NSSM_STARTUP_* constant to Windows SERVICE_*_START constant. */\r
1185   int service_startup = -1;\r
1186   int i;\r
1187   for (i = 0; startup_strings[i]; i++) {\r
1188     if (str_equiv(value->string, startup_strings[i])) {\r
1189       service_startup = i;\r
1190       break;\r
1191     }\r
1192   }\r
1193 \r
1194   if (service_startup < 0) {\r
1195     print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE_STARTUP, value->string);\r
1196     for (i = 0; startup_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), startup_strings[i]);\r
1197     return -1;\r
1198   }\r
1199 \r
1200   unsigned long startup;\r
1201   switch (service_startup) {\r
1202     case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;\r
1203     case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;\r
1204     default: startup = SERVICE_AUTO_START;\r
1205   }\r
1206 \r
1207   if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, startup, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) {\r
1208     print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));\r
1209     return -1;\r
1210   }\r
1211 \r
1212   SERVICE_DELAYED_AUTO_START_INFO delayed;\r
1213   ZeroMemory(&delayed, sizeof(delayed));\r
1214   if (service_startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;\r
1215   else delayed.fDelayedAutostart = 0;\r
1216   if (! ChangeServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {\r
1217     unsigned long error = GetLastError();\r
1218     /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */\r
1219     if (error != ERROR_INVALID_LEVEL) {\r
1220       log_event(EVENTLOG_ERROR_TYPE, NSSM_MESSAGE_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service_name, error_string(error), 0);\r
1221     }\r
1222   }\r
1223 \r
1224   return 1;\r
1225 }\r
1226 \r
1227 int native_get_startup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
1228   SC_HANDLE service_handle = (SC_HANDLE) param;\r
1229   if (! service_handle) return -1;\r
1230 \r
1231   QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);\r
1232   if (! qsc) return -1;\r
1233 \r
1234   unsigned long startup;\r
1235   int ret = get_service_startup(service_name, service_handle, qsc, &startup);\r
1236   HeapFree(GetProcessHeap(), 0, qsc);\r
1237 \r
1238   if (ret) return -1;\r
1239 \r
1240   unsigned long i;\r
1241   for (i = 0; startup_strings[i]; i++);\r
1242   if (startup >= i) return -1;\r
1243 \r
1244   return value_from_string(name, value, startup_strings[startup]);\r
1245 }\r
1246 \r
1247 int native_set_type(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
1248   SC_HANDLE service_handle = (SC_HANDLE) param;\r
1249   if (! service_handle) return -1;\r
1250 \r
1251   /* It makes no sense to try to reset the service type. */\r
1252   if (! value || ! value->string) {\r
1253     print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);\r
1254     return -1;\r
1255   }\r
1256 \r
1257   /*\r
1258     We can only manage services of type SERVICE_WIN32_OWN_PROCESS\r
1259     and SERVICE_INTERACTIVE_PROCESS.\r
1260   */\r
1261   unsigned long type = SERVICE_WIN32_OWN_PROCESS;\r
1262   if (str_equiv(value->string, NSSM_INTERACTIVE_PROCESS)) type |= SERVICE_INTERACTIVE_PROCESS;\r
1263   else if (! str_equiv(value->string, NSSM_WIN32_OWN_PROCESS)) {\r
1264     print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE_TYPE, value->string);\r
1265     _ftprintf(stderr, _T("%s\n"), NSSM_WIN32_OWN_PROCESS);\r
1266     _ftprintf(stderr, _T("%s\n"), NSSM_INTERACTIVE_PROCESS);\r
1267     return -1;\r
1268   }\r
1269 \r
1270   /*\r
1271     ChangeServiceConfig() will fail if the service runs under an account\r
1272     other than LOCALSYSTEM and we try to make it interactive.\r
1273   */\r
1274   if (type & SERVICE_INTERACTIVE_PROCESS) {\r
1275     QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);\r
1276     if (! qsc) return -1;\r
1277 \r
1278     if (! str_equiv(qsc->lpServiceStartName, NSSM_LOCALSYSTEM_ACCOUNT)) {\r
1279       HeapFree(GetProcessHeap(), 0, qsc);\r
1280       print_message(stderr, NSSM_MESSAGE_INTERACTIVE_NOT_LOCALSYSTEM, value->string, service_name, NSSM_LOCALSYSTEM_ACCOUNT);\r
1281       return -1;\r
1282     }\r
1283 \r
1284     HeapFree(GetProcessHeap(), 0, qsc);\r
1285   }\r
1286 \r
1287   if (! ChangeServiceConfig(service_handle, type, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) {\r
1288     print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));\r
1289     return -1;\r
1290   }\r
1291 \r
1292   return 1;\r
1293 }\r
1294 \r
1295 int native_get_type(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
1296   SC_HANDLE service_handle = (SC_HANDLE) param;\r
1297   if (! service_handle) return -1;\r
1298 \r
1299   QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);\r
1300   if (! qsc) return -1;\r
1301 \r
1302   value->numeric = qsc->dwServiceType;\r
1303   HeapFree(GetProcessHeap(), 0, qsc);\r
1304 \r
1305   const TCHAR *string;\r
1306   switch (value->numeric) {\r
1307     case SERVICE_KERNEL_DRIVER: string = NSSM_KERNEL_DRIVER; break;\r
1308     case SERVICE_FILE_SYSTEM_DRIVER: string = NSSM_FILE_SYSTEM_DRIVER; break;\r
1309     case SERVICE_WIN32_OWN_PROCESS: string = NSSM_WIN32_OWN_PROCESS; break;\r
1310     case SERVICE_WIN32_SHARE_PROCESS: string = NSSM_WIN32_SHARE_PROCESS; break;\r
1311     case SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS: string = NSSM_INTERACTIVE_PROCESS; break;\r
1312     case SERVICE_WIN32_SHARE_PROCESS|SERVICE_INTERACTIVE_PROCESS: string = NSSM_SHARE_INTERACTIVE_PROCESS; break;\r
1313     default: string = NSSM_UNKNOWN;\r
1314   }\r
1315 \r
1316   return value_from_string(name, value, string);\r
1317 }\r
1318 \r
1319 int set_setting(const TCHAR *service_name, HKEY key, settings_t *setting, value_t *value, const TCHAR *additional) {\r
1320   if (! key) return -1;\r
1321   int ret;\r
1322 \r
1323   if (setting->set) ret = setting->set(service_name, (void *) key, setting->name, setting->default_value, value, additional);\r
1324   else ret = -1;\r
1325 \r
1326   if (! ret) print_message(stdout, NSSM_MESSAGE_RESET_SETTING, setting->name, service_name);\r
1327   else if (ret > 0) print_message(stdout, NSSM_MESSAGE_SET_SETTING, setting->name, service_name);\r
1328   else print_message(stderr, NSSM_MESSAGE_SET_SETTING_FAILED, setting->name, service_name);\r
1329 \r
1330   return ret;\r
1331 }\r
1332 \r
1333 int set_setting(const TCHAR *service_name, SC_HANDLE service_handle, settings_t *setting, value_t *value, const TCHAR *additional) {\r
1334   if (! service_handle) return -1;\r
1335 \r
1336   int ret;\r
1337   if (setting->set) ret = setting->set(service_name, service_handle, setting->name, setting->default_value, value, additional);\r
1338   else ret = -1;\r
1339 \r
1340   if (! ret) print_message(stdout, NSSM_MESSAGE_RESET_SETTING, setting->name, service_name);\r
1341   else if (ret > 0) print_message(stdout, NSSM_MESSAGE_SET_SETTING, setting->name, service_name);\r
1342   else print_message(stderr, NSSM_MESSAGE_SET_SETTING_FAILED, setting->name, service_name);\r
1343 \r
1344   return ret;\r
1345 }\r
1346 \r
1347 /*\r
1348   Returns:  1 if the value was retrieved.\r
1349             0 if the default value was retrieved.\r
1350            -1 on error.\r
1351 */\r
1352 int get_setting(const TCHAR *service_name, HKEY key, settings_t *setting, value_t *value, const TCHAR *additional) {\r
1353   if (! key) return -1;\r
1354   int ret;\r
1355 \r
1356   if (is_string_type(setting->type)) {\r
1357     value->string = (TCHAR *) setting->default_value;\r
1358     if (setting->get) ret = setting->get(service_name, (void *) key, setting->name, setting->default_value, value, additional);\r
1359     else ret = -1;\r
1360   }\r
1361   else if (is_numeric_type(setting->type)) {\r
1362     value->numeric = PtrToUlong(setting->default_value);\r
1363     if (setting->get) ret = setting->get(service_name, (void *) key, setting->name, setting->default_value, value, additional);\r
1364     else ret = -1;\r
1365   }\r
1366   else ret = -1;\r
1367 \r
1368   if (ret < 0) print_message(stderr, NSSM_MESSAGE_GET_SETTING_FAILED, setting->name, service_name);\r
1369 \r
1370   return ret;\r
1371 }\r
1372 \r
1373 int get_setting(const TCHAR *service_name, SC_HANDLE service_handle, settings_t *setting, value_t *value, const TCHAR *additional) {\r
1374   if (! service_handle) return -1;\r
1375   return setting->get(service_name, service_handle, setting->name, 0, value, additional);\r
1376 }\r
1377 \r
1378 int dump_setting(const TCHAR *service_name, HKEY key, SC_HANDLE service_handle, settings_t *setting) {\r
1379   void *param;\r
1380   if (setting->native) {\r
1381     if (! service_handle) return -1;\r
1382     param = (void *) service_handle;\r
1383   }\r
1384   else {\r
1385     /* Will be null for native services. */\r
1386     param = (void *) key;\r
1387   }\r
1388 \r
1389   value_t value = { 0 };\r
1390   int ret;\r
1391 \r
1392   if (setting->dump) return setting->dump(service_name, param, setting->name, (void *) setting, &value, 0);\r
1393   if (setting->native) ret = get_setting(service_name, service_handle, setting, &value, 0);\r
1394   else ret = get_setting(service_name, key, setting, &value, 0);\r
1395   if (ret != 1) return ret;\r
1396   return setting_dump_string(service_name, (void *) setting->type, setting->name, &value, 0);\r
1397 }\r
1398 \r
1399 settings_t settings[] = {\r
1400   { NSSM_REG_EXE, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string, setting_not_dumpable },\r
1401   { NSSM_REG_FLAGS, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string, 0 },\r
1402   { NSSM_REG_DIR, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string, 0 },\r
1403   { NSSM_REG_EXIT, REG_SZ, (void *) exit_action_strings[NSSM_EXIT_RESTART], false, ADDITIONAL_MANDATORY, setting_set_exit_action, setting_get_exit_action, setting_dump_exit_action },\r
1404   { NSSM_REG_HOOK, REG_SZ, (void *) _T(""), false, ADDITIONAL_MANDATORY, setting_set_hook, setting_get_hook, setting_dump_hooks },\r
1405   { NSSM_REG_AFFINITY, REG_SZ, 0, false, 0, setting_set_affinity, setting_get_affinity, 0 },\r
1406   { NSSM_REG_ENV, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment, setting_dump_environment },\r
1407   { NSSM_REG_ENV_EXTRA, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment, setting_dump_environment },\r
1408   { NSSM_REG_NO_CONSOLE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },\r
1409   { NSSM_REG_PRIORITY, REG_SZ, (void *) priority_strings[NSSM_NORMAL_PRIORITY], false, 0, setting_set_priority, setting_get_priority, setting_dump_priority },\r
1410   { NSSM_REG_RESTART_DELAY, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },\r
1411   { NSSM_REG_STDIN, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string, 0 },\r
1412   { NSSM_REG_STDIN NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDIN_SHARING, false, 0, setting_set_number, setting_get_number, 0 },\r
1413   { NSSM_REG_STDIN NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDIN_DISPOSITION, false, 0, setting_set_number, setting_get_number, 0 },\r
1414   { NSSM_REG_STDIN NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDIN_FLAGS, false, 0, setting_set_number, setting_get_number, 0 },\r
1415   { NSSM_REG_STDOUT, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string, 0 },\r
1416   { NSSM_REG_STDOUT NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDOUT_SHARING, false, 0, setting_set_number, setting_get_number, 0 },\r
1417   { NSSM_REG_STDOUT NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDOUT_DISPOSITION, false, 0, setting_set_number, setting_get_number, 0 },\r
1418   { NSSM_REG_STDOUT NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDOUT_FLAGS, false, 0, setting_set_number, setting_get_number, 0 },\r
1419   { NSSM_REG_STDOUT NSSM_REG_STDIO_COPY_AND_TRUNCATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },\r
1420   { NSSM_REG_STDERR, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string, 0 },\r
1421   { NSSM_REG_STDERR NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDERR_SHARING, false, 0, setting_set_number, setting_get_number, 0 },\r
1422   { NSSM_REG_STDERR NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDERR_DISPOSITION, false, 0, setting_set_number, setting_get_number, 0 },\r
1423   { NSSM_REG_STDERR NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDERR_FLAGS, false, 0, setting_set_number, setting_get_number, 0 },\r
1424   { NSSM_REG_STDERR NSSM_REG_STDIO_COPY_AND_TRUNCATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },\r
1425   { NSSM_REG_STOP_METHOD_SKIP, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },\r
1426   { NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_CONSOLE_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number, 0 },\r
1427   { NSSM_REG_KILL_WINDOW_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_WINDOW_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number, 0 },\r
1428   { NSSM_REG_KILL_THREADS_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_THREADS_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number, 0 },\r
1429   { NSSM_REG_KILL_PROCESS_TREE, REG_DWORD, (void *) 1, false, 0, setting_set_number, setting_get_number, 0 },\r
1430   { NSSM_REG_THROTTLE, REG_DWORD, (void *) NSSM_RESET_THROTTLE_RESTART, false, 0, setting_set_number, setting_get_number, 0 },\r
1431   { NSSM_REG_HOOK_SHARE_OUTPUT_HANDLES, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },\r
1432   { NSSM_REG_ROTATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },\r
1433   { NSSM_REG_ROTATE_ONLINE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },\r
1434   { NSSM_REG_ROTATE_SECONDS, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },\r
1435   { NSSM_REG_ROTATE_BYTES_LOW, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },\r
1436   { NSSM_REG_ROTATE_BYTES_HIGH, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },\r
1437   { NSSM_REG_ROTATE_DELAY, REG_DWORD, (void *) NSSM_ROTATE_DELAY, false, 0, setting_set_number, setting_get_number, 0 },\r
1438   { NSSM_NATIVE_DEPENDONGROUP, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_dependongroup, native_get_dependongroup, native_dump_dependongroup },\r
1439   { NSSM_NATIVE_DEPENDONSERVICE, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_dependonservice, native_get_dependonservice, native_dump_dependonservice },\r
1440   { NSSM_NATIVE_DESCRIPTION, REG_SZ, _T(""), true, 0, native_set_description, native_get_description, 0 },\r
1441   { NSSM_NATIVE_DISPLAYNAME, REG_SZ, NULL, true, 0, native_set_displayname, native_get_displayname, 0 },\r
1442   { NSSM_NATIVE_ENVIRONMENT, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_environment, native_get_environment, native_dump_environment },\r
1443   { NSSM_NATIVE_IMAGEPATH, REG_EXPAND_SZ, NULL, true, 0, native_set_imagepath, native_get_imagepath, setting_not_dumpable },\r
1444   { NSSM_NATIVE_OBJECTNAME, REG_SZ, NSSM_LOCALSYSTEM_ACCOUNT, true, 0, native_set_objectname, native_get_objectname, native_dump_objectname },\r
1445   { NSSM_NATIVE_NAME, REG_SZ, NULL, true, 0, native_set_name, native_get_name, setting_not_dumpable },\r
1446   { NSSM_NATIVE_STARTUP, REG_SZ, NULL, true, 0, native_set_startup, native_get_startup, 0 },\r
1447   { NSSM_NATIVE_TYPE, REG_SZ, NULL, true, 0, native_set_type, native_get_type, 0 },\r
1448   { NULL, NULL, NULL, NULL, NULL }\r
1449 };\r