Allow listing all services.
[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   TCHAR *username = NSSM_LOCALSYSTEM_ACCOUNT;\r
1078   TCHAR *password = 0;\r
1079   if (additional) {\r
1080     username = (TCHAR *) additional;\r
1081     if (value && value->string) password = value->string;\r
1082   }\r
1083   else if (value && value->string) username = value->string;\r
1084 \r
1085   const TCHAR *well_known = well_known_username(username);\r
1086   size_t passwordsize = 0;\r
1087   if (well_known) {\r
1088     if (str_equiv(well_known, NSSM_LOCALSYSTEM_ACCOUNT)) localsystem = true;\r
1089     username = (TCHAR *) well_known;\r
1090     password = _T("");\r
1091   }\r
1092   else if (! password) {\r
1093     /* We need a password if the account requires it. */\r
1094     print_message(stderr, NSSM_MESSAGE_MISSING_PASSWORD, name);\r
1095     return -1;\r
1096   }\r
1097   else passwordsize = _tcslen(password) * sizeof(TCHAR);\r
1098 \r
1099   /*\r
1100     ChangeServiceConfig() will fail to set the username if the service is set\r
1101     to interact with the desktop.\r
1102   */\r
1103   unsigned long type = SERVICE_NO_CHANGE;\r
1104   if (! localsystem) {\r
1105     QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);\r
1106     if (! qsc) {\r
1107       if (passwordsize) SecureZeroMemory(password, passwordsize);\r
1108       return -1;\r
1109     }\r
1110 \r
1111     type = qsc->dwServiceType & ~SERVICE_INTERACTIVE_PROCESS;\r
1112     HeapFree(GetProcessHeap(), 0, qsc);\r
1113   }\r
1114 \r
1115   if (! well_known) {\r
1116     if (grant_logon_as_service(username)) {\r
1117       if (passwordsize) SecureZeroMemory(password, passwordsize);\r
1118       print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);\r
1119       return -1;\r
1120     }\r
1121   }\r
1122 \r
1123   if (! ChangeServiceConfig(service_handle, type, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, username, password, 0)) {\r
1124     if (passwordsize) SecureZeroMemory(password, passwordsize);\r
1125     print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));\r
1126     return -1;\r
1127   }\r
1128 \r
1129   if (passwordsize) SecureZeroMemory(password, passwordsize);\r
1130 \r
1131   if (localsystem) return 0;\r
1132 \r
1133   return 1;\r
1134 }\r
1135 \r
1136 int native_get_objectname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
1137   SC_HANDLE service_handle = (SC_HANDLE) param;\r
1138   if (! service_handle) return -1;\r
1139 \r
1140   QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);\r
1141   if (! qsc) return -1;\r
1142 \r
1143   int ret = value_from_string(name, value, qsc->lpServiceStartName);\r
1144   HeapFree(GetProcessHeap(), 0, qsc);\r
1145 \r
1146   return ret;\r
1147 }\r
1148 \r
1149 int native_dump_objectname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
1150   int ret = native_get_objectname(service_name, param, name, default_value, value, additional);\r
1151   if (ret != 1) return ret;\r
1152 \r
1153   /* Do we need to dump a dummy password? */\r
1154   if (! well_known_username(value->string)) {\r
1155     /* Parameters are the other way round. */\r
1156     value_t inverted;\r
1157     inverted.string = _T("****");\r
1158     return setting_dump_string(service_name, (void *) REG_SZ, name, &inverted, value->string);\r
1159   }\r
1160   return setting_dump_string(service_name, (void *) REG_SZ, name, value, 0);\r
1161 }\r
1162 \r
1163 int native_set_startup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
1164   SC_HANDLE service_handle = (SC_HANDLE) param;\r
1165   if (! service_handle) return -1;\r
1166 \r
1167   /* It makes no sense to try to reset the startup type. */\r
1168   if (! value || ! value->string) {\r
1169     print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);\r
1170     return -1;\r
1171   }\r
1172 \r
1173   /* Map NSSM_STARTUP_* constant to Windows SERVICE_*_START constant. */\r
1174   int service_startup = -1;\r
1175   int i;\r
1176   for (i = 0; startup_strings[i]; i++) {\r
1177     if (str_equiv(value->string, startup_strings[i])) {\r
1178       service_startup = i;\r
1179       break;\r
1180     }\r
1181   }\r
1182 \r
1183   if (service_startup < 0) {\r
1184     print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE_STARTUP, value->string);\r
1185     for (i = 0; startup_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), startup_strings[i]);\r
1186     return -1;\r
1187   }\r
1188 \r
1189   unsigned long startup;\r
1190   switch (service_startup) {\r
1191     case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;\r
1192     case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;\r
1193     default: startup = SERVICE_AUTO_START;\r
1194   }\r
1195 \r
1196   if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, startup, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) {\r
1197     print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));\r
1198     return -1;\r
1199   }\r
1200 \r
1201   SERVICE_DELAYED_AUTO_START_INFO delayed;\r
1202   ZeroMemory(&delayed, sizeof(delayed));\r
1203   if (service_startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;\r
1204   else delayed.fDelayedAutostart = 0;\r
1205   if (! ChangeServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {\r
1206     unsigned long error = GetLastError();\r
1207     /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */\r
1208     if (error != ERROR_INVALID_LEVEL) {\r
1209       log_event(EVENTLOG_ERROR_TYPE, NSSM_MESSAGE_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service_name, error_string(error), 0);\r
1210     }\r
1211   }\r
1212 \r
1213   return 1;\r
1214 }\r
1215 \r
1216 int native_get_startup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
1217   SC_HANDLE service_handle = (SC_HANDLE) param;\r
1218   if (! service_handle) return -1;\r
1219 \r
1220   QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);\r
1221   if (! qsc) return -1;\r
1222 \r
1223   unsigned long startup;\r
1224   int ret = get_service_startup(service_name, service_handle, qsc, &startup);\r
1225   HeapFree(GetProcessHeap(), 0, qsc);\r
1226 \r
1227   if (ret) return -1;\r
1228 \r
1229   unsigned long i;\r
1230   for (i = 0; startup_strings[i]; i++);\r
1231   if (startup >= i) return -1;\r
1232 \r
1233   return value_from_string(name, value, startup_strings[startup]);\r
1234 }\r
1235 \r
1236 int native_set_type(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
1237   SC_HANDLE service_handle = (SC_HANDLE) param;\r
1238   if (! service_handle) return -1;\r
1239 \r
1240   /* It makes no sense to try to reset the service type. */\r
1241   if (! value || ! value->string) {\r
1242     print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);\r
1243     return -1;\r
1244   }\r
1245 \r
1246   /*\r
1247     We can only manage services of type SERVICE_WIN32_OWN_PROCESS\r
1248     and SERVICE_INTERACTIVE_PROCESS.\r
1249   */\r
1250   unsigned long type = SERVICE_WIN32_OWN_PROCESS;\r
1251   if (str_equiv(value->string, NSSM_INTERACTIVE_PROCESS)) type |= SERVICE_INTERACTIVE_PROCESS;\r
1252   else if (! str_equiv(value->string, NSSM_WIN32_OWN_PROCESS)) {\r
1253     print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE_TYPE, value->string);\r
1254     _ftprintf(stderr, _T("%s\n"), NSSM_WIN32_OWN_PROCESS);\r
1255     _ftprintf(stderr, _T("%s\n"), NSSM_INTERACTIVE_PROCESS);\r
1256     return -1;\r
1257   }\r
1258 \r
1259   /*\r
1260     ChangeServiceConfig() will fail if the service runs under an account\r
1261     other than LOCALSYSTEM and we try to make it interactive.\r
1262   */\r
1263   if (type & SERVICE_INTERACTIVE_PROCESS) {\r
1264     QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);\r
1265     if (! qsc) return -1;\r
1266 \r
1267     if (! str_equiv(qsc->lpServiceStartName, NSSM_LOCALSYSTEM_ACCOUNT)) {\r
1268       HeapFree(GetProcessHeap(), 0, qsc);\r
1269       print_message(stderr, NSSM_MESSAGE_INTERACTIVE_NOT_LOCALSYSTEM, value->string, service_name, NSSM_LOCALSYSTEM_ACCOUNT);\r
1270       return -1;\r
1271     }\r
1272 \r
1273     HeapFree(GetProcessHeap(), 0, qsc);\r
1274   }\r
1275 \r
1276   if (! ChangeServiceConfig(service_handle, type, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) {\r
1277     print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));\r
1278     return -1;\r
1279   }\r
1280 \r
1281   return 1;\r
1282 }\r
1283 \r
1284 int native_get_type(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
1285   SC_HANDLE service_handle = (SC_HANDLE) param;\r
1286   if (! service_handle) return -1;\r
1287 \r
1288   QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);\r
1289   if (! qsc) return -1;\r
1290 \r
1291   value->numeric = qsc->dwServiceType;\r
1292   HeapFree(GetProcessHeap(), 0, qsc);\r
1293 \r
1294   const TCHAR *string;\r
1295   switch (value->numeric) {\r
1296     case SERVICE_KERNEL_DRIVER: string = NSSM_KERNEL_DRIVER; break;\r
1297     case SERVICE_FILE_SYSTEM_DRIVER: string = NSSM_FILE_SYSTEM_DRIVER; break;\r
1298     case SERVICE_WIN32_OWN_PROCESS: string = NSSM_WIN32_OWN_PROCESS; break;\r
1299     case SERVICE_WIN32_SHARE_PROCESS: string = NSSM_WIN32_SHARE_PROCESS; break;\r
1300     case SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS: string = NSSM_INTERACTIVE_PROCESS; break;\r
1301     case SERVICE_WIN32_SHARE_PROCESS|SERVICE_INTERACTIVE_PROCESS: string = NSSM_SHARE_INTERACTIVE_PROCESS; break;\r
1302     default: string = NSSM_UNKNOWN;\r
1303   }\r
1304 \r
1305   return value_from_string(name, value, string);\r
1306 }\r
1307 \r
1308 int set_setting(const TCHAR *service_name, HKEY key, settings_t *setting, value_t *value, const TCHAR *additional) {\r
1309   if (! key) return -1;\r
1310   int ret;\r
1311 \r
1312   if (setting->set) ret = setting->set(service_name, (void *) key, setting->name, setting->default_value, value, additional);\r
1313   else ret = -1;\r
1314 \r
1315   if (! ret) print_message(stdout, NSSM_MESSAGE_RESET_SETTING, setting->name, service_name);\r
1316   else if (ret > 0) print_message(stdout, NSSM_MESSAGE_SET_SETTING, setting->name, service_name);\r
1317   else print_message(stderr, NSSM_MESSAGE_SET_SETTING_FAILED, setting->name, service_name);\r
1318 \r
1319   return ret;\r
1320 }\r
1321 \r
1322 int set_setting(const TCHAR *service_name, SC_HANDLE service_handle, settings_t *setting, value_t *value, const TCHAR *additional) {\r
1323   if (! service_handle) return -1;\r
1324 \r
1325   int ret;\r
1326   if (setting->set) ret = setting->set(service_name, service_handle, setting->name, setting->default_value, value, additional);\r
1327   else ret = -1;\r
1328 \r
1329   if (! ret) print_message(stdout, NSSM_MESSAGE_RESET_SETTING, setting->name, service_name);\r
1330   else if (ret > 0) print_message(stdout, NSSM_MESSAGE_SET_SETTING, setting->name, service_name);\r
1331   else print_message(stderr, NSSM_MESSAGE_SET_SETTING_FAILED, setting->name, service_name);\r
1332 \r
1333   return ret;\r
1334 }\r
1335 \r
1336 /*\r
1337   Returns:  1 if the value was retrieved.\r
1338             0 if the default value was retrieved.\r
1339            -1 on error.\r
1340 */\r
1341 int get_setting(const TCHAR *service_name, HKEY key, settings_t *setting, value_t *value, const TCHAR *additional) {\r
1342   if (! key) return -1;\r
1343   int ret;\r
1344 \r
1345   if (is_string_type(setting->type)) {\r
1346     value->string = (TCHAR *) setting->default_value;\r
1347     if (setting->get) ret = setting->get(service_name, (void *) key, setting->name, setting->default_value, value, additional);\r
1348     else ret = -1;\r
1349   }\r
1350   else if (is_numeric_type(setting->type)) {\r
1351     value->numeric = PtrToUlong(setting->default_value);\r
1352     if (setting->get) ret = setting->get(service_name, (void *) key, setting->name, setting->default_value, value, additional);\r
1353     else ret = -1;\r
1354   }\r
1355   else ret = -1;\r
1356 \r
1357   if (ret < 0) print_message(stderr, NSSM_MESSAGE_GET_SETTING_FAILED, setting->name, service_name);\r
1358 \r
1359   return ret;\r
1360 }\r
1361 \r
1362 int get_setting(const TCHAR *service_name, SC_HANDLE service_handle, settings_t *setting, value_t *value, const TCHAR *additional) {\r
1363   if (! service_handle) return -1;\r
1364   return setting->get(service_name, service_handle, setting->name, 0, value, additional);\r
1365 }\r
1366 \r
1367 int dump_setting(const TCHAR *service_name, HKEY key, SC_HANDLE service_handle, settings_t *setting) {\r
1368   void *param;\r
1369   if (setting->native) {\r
1370     if (! service_handle) return -1;\r
1371     param = (void *) service_handle;\r
1372   }\r
1373   else {\r
1374     /* Will be null for native services. */\r
1375     param = (void *) key;\r
1376   }\r
1377 \r
1378   value_t value = { 0 };\r
1379   int ret;\r
1380 \r
1381   if (setting->dump) return setting->dump(service_name, param, setting->name, (void *) setting, &value, 0);\r
1382   if (setting->native) ret = get_setting(service_name, service_handle, setting, &value, 0);\r
1383   else ret = get_setting(service_name, key, setting, &value, 0);\r
1384   if (ret != 1) return ret;\r
1385   return setting_dump_string(service_name, (void *) setting->type, setting->name, &value, 0);\r
1386 }\r
1387 \r
1388 settings_t settings[] = {\r
1389   { NSSM_REG_EXE, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string, setting_not_dumpable },\r
1390   { NSSM_REG_FLAGS, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string, 0 },\r
1391   { NSSM_REG_DIR, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string, 0 },\r
1392   { 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
1393   { NSSM_REG_HOOK, REG_SZ, (void *) _T(""), false, ADDITIONAL_MANDATORY, setting_set_hook, setting_get_hook, setting_dump_hooks },\r
1394   { NSSM_REG_AFFINITY, REG_SZ, 0, false, 0, setting_set_affinity, setting_get_affinity, 0 },\r
1395   { NSSM_REG_ENV, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment, setting_dump_environment },\r
1396   { NSSM_REG_ENV_EXTRA, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment, setting_dump_environment },\r
1397   { NSSM_REG_NO_CONSOLE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },\r
1398   { NSSM_REG_PRIORITY, REG_SZ, (void *) priority_strings[NSSM_NORMAL_PRIORITY], false, 0, setting_set_priority, setting_get_priority, setting_dump_priority },\r
1399   { NSSM_REG_RESTART_DELAY, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },\r
1400   { NSSM_REG_STDIN, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string, 0 },\r
1401   { NSSM_REG_STDIN NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDIN_SHARING, false, 0, setting_set_number, setting_get_number, 0 },\r
1402   { NSSM_REG_STDIN NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDIN_DISPOSITION, false, 0, setting_set_number, setting_get_number, 0 },\r
1403   { NSSM_REG_STDIN NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDIN_FLAGS, false, 0, setting_set_number, setting_get_number, 0 },\r
1404   { NSSM_REG_STDOUT, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string, 0 },\r
1405   { NSSM_REG_STDOUT NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDOUT_SHARING, false, 0, setting_set_number, setting_get_number, 0 },\r
1406   { NSSM_REG_STDOUT NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDOUT_DISPOSITION, false, 0, setting_set_number, setting_get_number, 0 },\r
1407   { NSSM_REG_STDOUT NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDOUT_FLAGS, false, 0, setting_set_number, setting_get_number, 0 },\r
1408   { NSSM_REG_STDOUT NSSM_REG_STDIO_COPY_AND_TRUNCATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },\r
1409   { NSSM_REG_STDERR, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string, 0 },\r
1410   { NSSM_REG_STDERR NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDERR_SHARING, false, 0, setting_set_number, setting_get_number, 0 },\r
1411   { NSSM_REG_STDERR NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDERR_DISPOSITION, false, 0, setting_set_number, setting_get_number, 0 },\r
1412   { NSSM_REG_STDERR NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDERR_FLAGS, false, 0, setting_set_number, setting_get_number, 0 },\r
1413   { NSSM_REG_STDERR NSSM_REG_STDIO_COPY_AND_TRUNCATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },\r
1414   { NSSM_REG_STOP_METHOD_SKIP, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },\r
1415   { NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_CONSOLE_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number, 0 },\r
1416   { NSSM_REG_KILL_WINDOW_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_WINDOW_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number, 0 },\r
1417   { NSSM_REG_KILL_THREADS_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_THREADS_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number, 0 },\r
1418   { NSSM_REG_KILL_PROCESS_TREE, REG_DWORD, (void *) 1, false, 0, setting_set_number, setting_get_number, 0 },\r
1419   { NSSM_REG_THROTTLE, REG_DWORD, (void *) NSSM_RESET_THROTTLE_RESTART, false, 0, setting_set_number, setting_get_number, 0 },\r
1420   { NSSM_REG_HOOK_SHARE_OUTPUT_HANDLES, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },\r
1421   { NSSM_REG_ROTATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },\r
1422   { NSSM_REG_ROTATE_ONLINE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },\r
1423   { NSSM_REG_ROTATE_SECONDS, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },\r
1424   { NSSM_REG_ROTATE_BYTES_LOW, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },\r
1425   { NSSM_REG_ROTATE_BYTES_HIGH, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },\r
1426   { NSSM_REG_ROTATE_DELAY, REG_DWORD, (void *) NSSM_ROTATE_DELAY, false, 0, setting_set_number, setting_get_number, 0 },\r
1427   { NSSM_NATIVE_DEPENDONGROUP, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_dependongroup, native_get_dependongroup, native_dump_dependongroup },\r
1428   { NSSM_NATIVE_DEPENDONSERVICE, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_dependonservice, native_get_dependonservice, native_dump_dependonservice },\r
1429   { NSSM_NATIVE_DESCRIPTION, REG_SZ, _T(""), true, 0, native_set_description, native_get_description, 0 },\r
1430   { NSSM_NATIVE_DISPLAYNAME, REG_SZ, NULL, true, 0, native_set_displayname, native_get_displayname, 0 },\r
1431   { NSSM_NATIVE_ENVIRONMENT, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_environment, native_get_environment, native_dump_environment },\r
1432   { NSSM_NATIVE_IMAGEPATH, REG_EXPAND_SZ, NULL, true, 0, native_set_imagepath, native_get_imagepath, setting_not_dumpable },\r
1433   { NSSM_NATIVE_OBJECTNAME, REG_SZ, NSSM_LOCALSYSTEM_ACCOUNT, true, 0, native_set_objectname, native_get_objectname, native_dump_objectname },\r
1434   { NSSM_NATIVE_NAME, REG_SZ, NULL, true, 0, native_set_name, native_get_name, setting_not_dumpable },\r
1435   { NSSM_NATIVE_STARTUP, REG_SZ, NULL, true, 0, native_set_startup, native_get_startup, 0 },\r
1436   { NSSM_NATIVE_TYPE, REG_SZ, NULL, true, 0, native_set_type, native_get_type, 0 },\r
1437   { NULL, NULL, NULL, NULL, NULL }\r
1438 };\r