Added enumerate_registry_values().
[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 \r
7 extern const TCHAR *exit_action_strings[];\r
8 extern const TCHAR *startup_strings[];\r
9 extern const TCHAR *priority_strings[];\r
10 \r
11 /* Does the parameter refer to the default value of the setting? */\r
12 static inline int is_default(const TCHAR *value) {\r
13   return (str_equiv(value, _T("default")) || str_equiv(value, _T("*")) || ! value[0]);\r
14 }\r
15 \r
16 static int value_from_string(const TCHAR *name, value_t *value, const TCHAR *string) {\r
17   size_t len = _tcslen(string);\r
18   if (! len++) {\r
19     value->string = 0;\r
20     return 0;\r
21   }\r
22 \r
23   value->string = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));\r
24   if (! value->string) {\r
25     print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, name, _T("value_from_string()"));\r
26     return -1;\r
27   }\r
28 \r
29   if (_sntprintf_s(value->string, len, _TRUNCATE, _T("%s"), string) < 0) {\r
30     HeapFree(GetProcessHeap(), 0, value->string);\r
31     print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, name, _T("value_from_string()"));\r
32     return -1;\r
33   }\r
34 \r
35   return 1;\r
36 }\r
37 \r
38 /* Functions to manage NSSM-specific settings in the registry. */\r
39 static int setting_set_number(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
40   HKEY key = (HKEY) param;\r
41   if (! key) return -1;\r
42 \r
43   unsigned long number;\r
44   long error;\r
45 \r
46   /* Resetting to default? */\r
47   if (! value || ! value->string) {\r
48     error = RegDeleteValue(key, name);\r
49     if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;\r
50     print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));\r
51     return -1;\r
52   }\r
53   if (str_number(value->string, &number)) return -1;\r
54 \r
55   if (default_value && number == PtrToUlong(default_value)) {\r
56     error = RegDeleteValue(key, name);\r
57     if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;\r
58     print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));\r
59     return -1;\r
60   }\r
61 \r
62   if (set_number(key, (TCHAR *) name, number)) return -1;\r
63 \r
64   return 1;\r
65 }\r
66 \r
67 static int setting_get_number(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
68   HKEY key = (HKEY) param;\r
69   return get_number(key, (TCHAR *) name, &value->numeric, false);\r
70 }\r
71 \r
72 static int setting_set_string(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
73   HKEY key = (HKEY) param;\r
74   if (! key) return -1;\r
75 \r
76   long error;\r
77 \r
78   /* Resetting to default? */\r
79   if (! value || ! value->string) {\r
80     if (default_value) value->string = (TCHAR *) default_value;\r
81     else {\r
82       error = RegDeleteValue(key, name);\r
83       if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;\r
84       print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));\r
85       return -1;\r
86     }\r
87   }\r
88   if (default_value && _tcslen((TCHAR *) default_value) && str_equiv(value->string, (TCHAR *) default_value)) {\r
89     error = RegDeleteValue(key, name);\r
90     if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;\r
91     print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));\r
92     return -1;\r
93   }\r
94 \r
95   if (set_expand_string(key, (TCHAR *) name, value->string)) return -1;\r
96 \r
97   return 1;\r
98 }\r
99 \r
100 static int setting_get_string(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
101   HKEY key = (HKEY) param;\r
102   TCHAR buffer[VALUE_LENGTH];\r
103 \r
104   if (get_string(key, (TCHAR *) name, (TCHAR *) buffer, (unsigned long) sizeof(buffer), false, false, false)) return -1;\r
105 \r
106   return value_from_string(name, value, buffer);\r
107 }\r
108 \r
109 static int setting_set_exit_action(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
110   unsigned long exitcode;\r
111   TCHAR *code;\r
112   TCHAR action_string[ACTION_LEN];\r
113 \r
114   if (additional) {\r
115     /* Default action? */\r
116     if (is_default(additional)) code = 0;\r
117     else {\r
118       if (str_number(additional, &exitcode)) return -1;\r
119       code = (TCHAR *) additional;\r
120     }\r
121   }\r
122 \r
123   HKEY key = open_registry(service_name, name, KEY_WRITE);\r
124   if (! key) return -1;\r
125 \r
126   long error;\r
127   int ret = 1;\r
128 \r
129   /* Resetting to default? */\r
130   if (value && value->string) _sntprintf_s(action_string, _countof(action_string), _TRUNCATE, _T("%s"), value->string);\r
131   else {\r
132     if (code) {\r
133       /* Delete explicit action. */\r
134       error = RegDeleteValue(key, code);\r
135       RegCloseKey(key);\r
136       if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;\r
137       print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, code, service_name, error_string(error));\r
138       return -1;\r
139     }\r
140     else {\r
141       /* Explicitly keep the default action. */\r
142       if (default_value) _sntprintf_s(action_string, _countof(action_string), _TRUNCATE, _T("%s"), (TCHAR *) default_value);\r
143       ret = 0;\r
144     }\r
145   }\r
146 \r
147   /* Validate the string. */\r
148   for (int i = 0; exit_action_strings[i]; i++) {\r
149     if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {\r
150       if (default_value && str_equiv(action_string, (TCHAR *) default_value)) ret = 0;\r
151       if (RegSetValueEx(key, code, 0, REG_SZ, (const unsigned char *) exit_action_strings[i], (unsigned long) (_tcslen(action_string) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) {\r
152         print_message(stderr, NSSM_MESSAGE_SETVALUE_FAILED, code, service_name, error_string(GetLastError()));\r
153         RegCloseKey(key);\r
154         return -1;\r
155       }\r
156 \r
157       RegCloseKey(key);\r
158       return ret;\r
159     }\r
160   }\r
161 \r
162   print_message(stderr, NSSM_MESSAGE_INVALID_EXIT_ACTION, action_string);\r
163   for (int i = 0; exit_action_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), exit_action_strings[i]);\r
164 \r
165   return -1;\r
166 }\r
167 \r
168 static int setting_get_exit_action(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
169   unsigned long exitcode = 0;\r
170   unsigned long *code = 0;\r
171 \r
172   if (additional) {\r
173     if (! is_default(additional)) {\r
174       if (str_number(additional, &exitcode)) return -1;\r
175       code = &exitcode;\r
176     }\r
177   }\r
178 \r
179   TCHAR action_string[ACTION_LEN];\r
180   bool default_action;\r
181   if (get_exit_action(service_name, code, action_string, &default_action)) return -1;\r
182 \r
183   value_from_string(name, value, action_string);\r
184 \r
185   if (default_action && ! _tcsnicmp((const TCHAR *) action_string, (TCHAR *) default_value, ACTION_LEN)) return 0;\r
186   return 1;\r
187 }\r
188 \r
189 static inline bool split_hook_name(const TCHAR *hook_name, TCHAR *hook_event, TCHAR *hook_action) {\r
190   TCHAR *s;\r
191 \r
192   for (s = (TCHAR *) hook_name; *s; s++) {\r
193     if (*s == _T('/')) {\r
194       *s = _T('\0');\r
195       _sntprintf_s(hook_event, HOOK_NAME_LENGTH, _TRUNCATE, _T("%s"), hook_name);\r
196       _sntprintf_s(hook_action, HOOK_NAME_LENGTH, _TRUNCATE, _T("%s"), ++s);\r
197       return valid_hook_name(hook_event, hook_action, false);\r
198     }\r
199   }\r
200 \r
201   print_message(stderr, NSSM_MESSAGE_INVALID_HOOK_NAME, hook_name);\r
202   return false;\r
203 }\r
204 \r
205 static int setting_set_hook(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
206   TCHAR hook_event[HOOK_NAME_LENGTH];\r
207   TCHAR hook_action[HOOK_NAME_LENGTH];\r
208   if (! split_hook_name(additional, hook_event, hook_action)) return -1;\r
209 \r
210   TCHAR *cmd;\r
211   if (value && value->string) cmd = value->string;\r
212   else cmd = _T("");\r
213 \r
214   if (set_hook(service_name, hook_event, hook_action, cmd)) return -1;\r
215   if (! _tcslen(cmd)) return 0;\r
216   return 1;\r
217 }\r
218 \r
219 static int setting_get_hook(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
220   TCHAR hook_event[HOOK_NAME_LENGTH];\r
221   TCHAR hook_action[HOOK_NAME_LENGTH];\r
222   if (! split_hook_name(additional, hook_event, hook_action)) return -1;\r
223 \r
224   TCHAR cmd[CMD_LENGTH];\r
225   if (get_hook(service_name, hook_event, hook_action, cmd, sizeof(cmd))) return -1;\r
226 \r
227   value_from_string(name, value, cmd);\r
228 \r
229   if (! _tcslen(cmd)) return 0;\r
230   return 1;\r
231 }\r
232 \r
233 static int setting_set_affinity(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
234   HKEY key = (HKEY) param;\r
235   if (! key) return -1;\r
236 \r
237   long error;\r
238   __int64 mask;\r
239   __int64 system_affinity = 0LL;\r
240 \r
241   if (value && value->string) {\r
242     DWORD_PTR affinity;\r
243     if (! GetProcessAffinityMask(GetCurrentProcess(), &affinity, (DWORD_PTR *) &system_affinity)) system_affinity = ~0;\r
244 \r
245     if (is_default(value->string) || str_equiv(value->string, NSSM_AFFINITY_ALL)) mask = 0LL;\r
246     else if (affinity_string_to_mask(value->string, &mask)) {\r
247       print_message(stderr, NSSM_MESSAGE_BOGUS_AFFINITY_MASK, value->string, num_cpus() - 1);\r
248       return -1;\r
249     }\r
250   }\r
251   else mask = 0LL;\r
252 \r
253   if (! mask) {\r
254     error = RegDeleteValue(key, name);\r
255     if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;\r
256     print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));\r
257     return -1;\r
258   }\r
259 \r
260   /* Canonicalise. */\r
261   TCHAR *canon = 0;\r
262   if (affinity_mask_to_string(mask, &canon)) canon = value->string;\r
263 \r
264   __int64 effective_affinity = mask & system_affinity;\r
265   if (effective_affinity != mask) {\r
266     /* Requested CPUs did not intersect with available CPUs? */\r
267     if (! effective_affinity) mask = effective_affinity = system_affinity;\r
268 \r
269     TCHAR *system = 0;\r
270     if (! affinity_mask_to_string(system_affinity, &system)) {\r
271       TCHAR *effective = 0;\r
272       if (! affinity_mask_to_string(effective_affinity, &effective)) {\r
273         print_message(stderr, NSSM_MESSAGE_EFFECTIVE_AFFINITY_MASK, value->string, system, effective);\r
274         HeapFree(GetProcessHeap(), 0, effective);\r
275       }\r
276       HeapFree(GetProcessHeap(), 0, system);\r
277     }\r
278   }\r
279 \r
280   if (RegSetValueEx(key, name, 0, REG_SZ, (const unsigned char *) canon, (unsigned long) (_tcslen(canon) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) {\r
281     if (canon != value->string) HeapFree(GetProcessHeap(), 0, canon);\r
282     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, name, error_string(GetLastError()), 0);\r
283     return -1;\r
284   }\r
285 \r
286   if (canon != value->string) HeapFree(GetProcessHeap(), 0, canon);\r
287   return 1;\r
288 }\r
289 \r
290 static int setting_get_affinity(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
291   HKEY key = (HKEY) param;\r
292   if (! key) return -1;\r
293 \r
294   unsigned long type;\r
295   TCHAR *buffer = 0;\r
296   unsigned long buflen = 0;\r
297 \r
298   int ret = RegQueryValueEx(key, name, 0, &type, 0, &buflen);\r
299   if (ret == ERROR_FILE_NOT_FOUND) {\r
300     if (value_from_string(name, value, NSSM_AFFINITY_ALL) == 1) return 0;\r
301     return -1;\r
302   }\r
303   if (ret != ERROR_SUCCESS) return -1;\r
304 \r
305   if (type != REG_SZ) return -1;\r
306 \r
307   buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, buflen);\r
308   if (! buffer) {\r
309     print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("affinity"), _T("setting_get_affinity"));\r
310     return -1;\r
311   }\r
312 \r
313   if (get_string(key, (TCHAR *) name, buffer, buflen, false, false, true)) {\r
314     HeapFree(GetProcessHeap(), 0, buffer);\r
315     return -1;\r
316   }\r
317 \r
318   __int64 affinity;\r
319   if (affinity_string_to_mask(buffer, &affinity)) {\r
320     print_message(stderr, NSSM_MESSAGE_BOGUS_AFFINITY_MASK, buffer, num_cpus() - 1);\r
321     HeapFree(GetProcessHeap(), 0, buffer);\r
322     return -1;\r
323   }\r
324 \r
325   HeapFree(GetProcessHeap(), 0, buffer);\r
326 \r
327   /* Canonicalise. */\r
328   if (affinity_mask_to_string(affinity, &buffer)) {\r
329     if (buffer) HeapFree(GetProcessHeap(), 0, buffer);\r
330     return -1;\r
331   }\r
332 \r
333   ret = value_from_string(name, value, buffer);\r
334   HeapFree(GetProcessHeap(), 0, buffer);\r
335   return ret;\r
336 }\r
337 \r
338 static int setting_set_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
339   HKEY key = (HKEY) param;\r
340   if (! param) return -1;\r
341 \r
342   if (! value || ! value->string || ! value->string[0]) {\r
343     long error = RegDeleteValue(key, name);\r
344     if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;\r
345     print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));\r
346     return -1;\r
347   }\r
348 \r
349   unsigned long envlen = (unsigned long) _tcslen(value->string) + 1;\r
350   TCHAR *unformatted = 0;\r
351   unsigned long newlen;\r
352   if (unformat_double_null(value->string, envlen, &unformatted, &newlen)) return -1;\r
353 \r
354   if (test_environment(unformatted)) {\r
355     HeapFree(GetProcessHeap(), 0, unformatted);\r
356     print_message(stderr, NSSM_GUI_INVALID_ENVIRONMENT);\r
357     return -1;\r
358   }\r
359 \r
360   if (RegSetValueEx(key, name, 0, REG_MULTI_SZ, (const unsigned char *) unformatted, (unsigned long) newlen * sizeof(TCHAR)) != ERROR_SUCCESS) {\r
361     if (newlen) HeapFree(GetProcessHeap(), 0, unformatted);\r
362     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV, error_string(GetLastError()), 0);\r
363     return -1;\r
364   }\r
365 \r
366   if (newlen) HeapFree(GetProcessHeap(), 0, unformatted);\r
367   return 1;\r
368 }\r
369 \r
370 static int setting_get_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
371   HKEY key = (HKEY) param;\r
372   if (! param) return -1;\r
373 \r
374   TCHAR *env = 0;\r
375   unsigned long envlen;\r
376   if (get_environment((TCHAR *) service_name, key, (TCHAR *) name, &env, &envlen)) return -1;\r
377   if (! envlen) return 0;\r
378 \r
379   TCHAR *formatted;\r
380   unsigned long newlen;\r
381   if (format_double_null(env, envlen, &formatted, &newlen)) return -1;\r
382 \r
383   int ret;\r
384   if (additional) {\r
385     /* Find named environment variable. */\r
386     TCHAR *s;\r
387     size_t len = _tcslen(additional);\r
388     for (s = env; *s; s++) {\r
389       /* Look for <additional>=<string> NULL NULL */\r
390       if (! _tcsnicmp(s, additional, len) && s[len] == _T('=')) {\r
391         /* Strip <key>= */\r
392         s += len + 1;\r
393         ret = value_from_string(name, value, s);\r
394         HeapFree(GetProcessHeap(), 0, env);\r
395         return ret;\r
396       }\r
397 \r
398       /* Skip this string. */\r
399       for ( ; *s; s++);\r
400     }\r
401     HeapFree(GetProcessHeap(), 0, env);\r
402     return 0;\r
403   }\r
404 \r
405   HeapFree(GetProcessHeap(), 0, env);\r
406 \r
407   ret = value_from_string(name, value, formatted);\r
408   if (newlen) HeapFree(GetProcessHeap(), 0, formatted);\r
409   return ret;\r
410 }\r
411 \r
412 static int setting_set_priority(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
413   HKEY key = (HKEY) param;\r
414   if (! param) return -1;\r
415 \r
416   TCHAR *priority_string;\r
417   int i;\r
418   long error;\r
419 \r
420   if (value && value->string) priority_string = value->string;\r
421   else if (default_value) priority_string = (TCHAR *) default_value;\r
422   else {\r
423     error = RegDeleteValue(key, name);\r
424     if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;\r
425     print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));\r
426     return -1;\r
427   }\r
428 \r
429   for (i = 0; priority_strings[i]; i++) {\r
430     if (! str_equiv(priority_strings[i], priority_string)) continue;\r
431 \r
432     if (default_value && str_equiv(priority_string, (TCHAR *) default_value)) {\r
433       error = RegDeleteValue(key, name);\r
434       if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;\r
435       print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));\r
436       return -1;\r
437     }\r
438 \r
439     if (set_number(key, (TCHAR *) name, priority_index_to_constant(i))) return -1;\r
440     return 1;\r
441   }\r
442 \r
443   print_message(stderr, NSSM_MESSAGE_INVALID_PRIORITY, priority_string);\r
444   for (i = 0; priority_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), priority_strings[i]);\r
445 \r
446   return -1;\r
447 }\r
448 \r
449 static int setting_get_priority(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
450   HKEY key = (HKEY) param;\r
451   if (! param) return -1;\r
452 \r
453   unsigned long constant;\r
454   switch (get_number(key, (TCHAR *) name, &constant, false)) {\r
455     case 0:\r
456       if (value_from_string(name, value, (const TCHAR *) default_value) == -1) return -1;\r
457       return 0;\r
458     case -1: return -1;\r
459   }\r
460 \r
461   return value_from_string(name, value, priority_strings[priority_constant_to_index(constant)]);\r
462 }\r
463 \r
464 /* Functions to manage native service settings. */\r
465 static int native_set_dependongroup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
466   SC_HANDLE service_handle = (SC_HANDLE) param;\r
467   if (! service_handle) return -1;\r
468 \r
469   /*\r
470     Get existing service dependencies because we must set both types together.\r
471   */\r
472   TCHAR *buffer;\r
473   unsigned long buflen;\r
474   if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_SERVICES)) return -1;\r
475 \r
476   if (! value || ! value->string || ! value->string[0]) {\r
477     if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, buffer, 0, 0, 0)) {\r
478       print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));\r
479       if (buffer) HeapFree(GetProcessHeap(), 0, buffer);\r
480       return -1;\r
481     }\r
482 \r
483     if (buffer) HeapFree(GetProcessHeap(), 0, buffer);\r
484     return 0;\r
485   }\r
486 \r
487   unsigned long len = (unsigned long) _tcslen(value->string) + 1;\r
488   TCHAR *unformatted = 0;\r
489   unsigned long newlen;\r
490   if (unformat_double_null(value->string, len, &unformatted, &newlen)) {\r
491     if (buffer) HeapFree(GetProcessHeap(), 0, buffer);\r
492     return -1;\r
493   }\r
494 \r
495   /* Prepend group identifier. */\r
496   unsigned long missing = 0;\r
497   TCHAR *canon = unformatted;\r
498   size_t canonlen = 0;\r
499   TCHAR *s;\r
500   for (s = unformatted; *s; s++) {\r
501     if (*s != SC_GROUP_IDENTIFIER) missing++;\r
502     size_t len = _tcslen(s);\r
503     canonlen += len + 1;\r
504     s += len;\r
505   }\r
506 \r
507   if (missing) {\r
508     /* Missing identifiers plus double NULL terminator. */\r
509     canonlen += missing + 1;\r
510     newlen = (unsigned long) canonlen;\r
511 \r
512     canon = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, canonlen * sizeof(TCHAR));\r
513     if (! canon) {\r
514       print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canon"), _T("native_set_dependongroup"));\r
515       if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);\r
516       if (buffer) HeapFree(GetProcessHeap(), 0, buffer);\r
517       return -1;\r
518     }\r
519 \r
520     size_t i = 0;\r
521     for (s = unformatted; *s; s++) {\r
522       if (*s != SC_GROUP_IDENTIFIER) canon[i++] = SC_GROUP_IDENTIFIER;\r
523       size_t len = _tcslen(s);\r
524       memmove(canon + i, s, (len + 1) * sizeof(TCHAR));\r
525       i += len + 1;\r
526       s += len;\r
527     }\r
528   }\r
529 \r
530   TCHAR *dependencies;\r
531   if (buflen > 2) {\r
532     dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (newlen + buflen) * sizeof(TCHAR));\r
533     if (! dependencies) {\r
534       print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("dependencies"), _T("native_set_dependongroup"));\r
535       if (canon != unformatted) HeapFree(GetProcessHeap(), 0, canon);\r
536       if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);\r
537       if (buffer) HeapFree(GetProcessHeap(), 0, buffer);\r
538       return -1;\r
539     }\r
540 \r
541     memmove(dependencies, buffer, buflen * sizeof(TCHAR));\r
542     memmove(dependencies + buflen - 1, canon, newlen * sizeof(TCHAR));\r
543   }\r
544   else dependencies = canon;\r
545 \r
546   int ret = 1;\r
547   if (set_service_dependencies(service_name, service_handle, dependencies)) ret = -1;\r
548   if (dependencies != unformatted) HeapFree(GetProcessHeap(), 0, dependencies);\r
549   if (canon != unformatted) HeapFree(GetProcessHeap(), 0, canon);\r
550   if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);\r
551   if (buffer) HeapFree(GetProcessHeap(), 0, buffer);\r
552 \r
553   return ret;\r
554 }\r
555 \r
556 static int native_get_dependongroup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
557   SC_HANDLE service_handle = (SC_HANDLE) param;\r
558   if (! service_handle) return -1;\r
559 \r
560   TCHAR *buffer;\r
561   unsigned long buflen;\r
562   if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_GROUPS)) return -1;\r
563 \r
564   int ret;\r
565   if (buflen) {\r
566     TCHAR *formatted;\r
567     unsigned long newlen;\r
568     if (format_double_null(buffer, buflen, &formatted, &newlen)) {\r
569       HeapFree(GetProcessHeap(), 0, buffer);\r
570       return -1;\r
571     }\r
572 \r
573     ret = value_from_string(name, value, formatted);\r
574     HeapFree(GetProcessHeap(), 0, formatted);\r
575     HeapFree(GetProcessHeap(), 0, buffer);\r
576   }\r
577   else {\r
578     value->string = 0;\r
579     ret = 0;\r
580   }\r
581 \r
582   return ret;\r
583 }\r
584 \r
585 static int native_set_dependonservice(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
586   SC_HANDLE service_handle = (SC_HANDLE) param;\r
587   if (! service_handle) return -1;\r
588 \r
589   /*\r
590     Get existing group dependencies because we must set both types together.\r
591   */\r
592   TCHAR *buffer;\r
593   unsigned long buflen;\r
594   if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_GROUPS)) return -1;\r
595 \r
596   if (! value || ! value->string || ! value->string[0]) {\r
597     if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, buffer, 0, 0, 0)) {\r
598       print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));\r
599       if (buffer) HeapFree(GetProcessHeap(), 0, buffer);\r
600       return -1;\r
601     }\r
602 \r
603     if (buffer) HeapFree(GetProcessHeap(), 0, buffer);\r
604     return 0;\r
605   }\r
606 \r
607   unsigned long len = (unsigned long) _tcslen(value->string) + 1;\r
608   TCHAR *unformatted = 0;\r
609   unsigned long newlen;\r
610   if (unformat_double_null(value->string, len, &unformatted, &newlen)) {\r
611     if (buffer) HeapFree(GetProcessHeap(), 0, buffer);\r
612     return -1;\r
613   }\r
614 \r
615   TCHAR *dependencies;\r
616   if (buflen > 2) {\r
617     dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (newlen + buflen) * sizeof(TCHAR));\r
618     if (! dependencies) {\r
619       print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("dependencies"), _T("native_set_dependonservice"));\r
620       if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);\r
621       if (buffer) HeapFree(GetProcessHeap(), 0, buffer);\r
622       return -1;\r
623     }\r
624 \r
625     memmove(dependencies, buffer, buflen * sizeof(TCHAR));\r
626     memmove(dependencies + buflen - 1, unformatted, newlen * sizeof(TCHAR));\r
627   }\r
628   else dependencies = unformatted;\r
629 \r
630   int ret = 1;\r
631   if (set_service_dependencies(service_name, service_handle, dependencies)) ret = -1;\r
632   if (dependencies != unformatted) HeapFree(GetProcessHeap(), 0, dependencies);\r
633   if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);\r
634   if (buffer) HeapFree(GetProcessHeap(), 0, buffer);\r
635 \r
636   return ret;\r
637 }\r
638 \r
639 static int native_get_dependonservice(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
640   SC_HANDLE service_handle = (SC_HANDLE) param;\r
641   if (! service_handle) return -1;\r
642 \r
643   TCHAR *buffer;\r
644   unsigned long buflen;\r
645   if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_SERVICES)) return -1;\r
646 \r
647   int ret;\r
648   if (buflen) {\r
649     TCHAR *formatted;\r
650     unsigned long newlen;\r
651     if (format_double_null(buffer, buflen, &formatted, &newlen)) {\r
652       HeapFree(GetProcessHeap(), 0, buffer);\r
653       return -1;\r
654     }\r
655 \r
656     ret = value_from_string(name, value, formatted);\r
657     HeapFree(GetProcessHeap(), 0, formatted);\r
658     HeapFree(GetProcessHeap(), 0, buffer);\r
659   }\r
660   else {\r
661     value->string = 0;\r
662     ret = 0;\r
663   }\r
664 \r
665   return ret;\r
666 }\r
667 \r
668 int native_set_description(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
669   SC_HANDLE service_handle = (SC_HANDLE) param;\r
670   if (! service_handle) return -1;\r
671 \r
672   TCHAR *description = 0;\r
673   if (value) description = value->string;\r
674   if (set_service_description(service_name, service_handle, description)) return -1;\r
675 \r
676   if (description && description[0]) return 1;\r
677 \r
678   return 0;\r
679 }\r
680 \r
681 int native_get_description(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
682   SC_HANDLE service_handle = (SC_HANDLE) param;\r
683   if (! service_handle) return -1;\r
684 \r
685   TCHAR buffer[VALUE_LENGTH];\r
686   if (get_service_description(service_name, service_handle, _countof(buffer), buffer)) return -1;\r
687 \r
688   if (buffer[0]) return value_from_string(name, value, buffer);\r
689   value->string = 0;\r
690 \r
691   return 0;\r
692 }\r
693 \r
694 int native_set_displayname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
695   SC_HANDLE service_handle = (SC_HANDLE) param;\r
696   if (! service_handle) return -1;\r
697 \r
698   TCHAR *displayname = 0;\r
699   if (value && value->string) displayname = value->string;\r
700   else displayname = (TCHAR *) service_name;\r
701 \r
702   if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, displayname)) {\r
703     print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));\r
704     return -1;\r
705   }\r
706 \r
707   /*\r
708     If the display name and service name differ only in case,\r
709     ChangeServiceConfig() will return success but the display name will be\r
710     set to the service name, NOT the value passed to the function.\r
711     This appears to be a quirk of Windows rather than a bug here.\r
712   */\r
713   if (displayname != service_name && ! str_equiv(displayname, service_name)) return 1;\r
714 \r
715   return 0;\r
716 }\r
717 \r
718 int native_get_displayname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
719   SC_HANDLE service_handle = (SC_HANDLE) param;\r
720   if (! service_handle) return -1;\r
721 \r
722   QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);\r
723   if (! qsc) return -1;\r
724 \r
725   int ret = value_from_string(name, value, qsc->lpDisplayName);\r
726   HeapFree(GetProcessHeap(), 0, qsc);\r
727 \r
728   return ret;\r
729 }\r
730 \r
731 int native_set_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
732   HKEY key = open_service_registry(service_name, KEY_SET_VALUE, true);\r
733   if (! key) return -1;\r
734 \r
735   int ret = setting_set_environment(service_name, (void *) key, name, default_value, value, additional);\r
736   RegCloseKey(key);\r
737   return ret;\r
738 }\r
739 \r
740 int native_get_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
741   HKEY key = open_service_registry(service_name, KEY_READ, true);\r
742   if (! key) return -1;\r
743 \r
744   ZeroMemory(value, sizeof(value_t));\r
745   int ret = setting_get_environment(service_name, (void *) key, name, default_value, value, additional);\r
746   RegCloseKey(key);\r
747   return ret;\r
748 }\r
749 \r
750 int native_set_imagepath(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
751   SC_HANDLE service_handle = (SC_HANDLE) param;\r
752   if (! service_handle) return -1;\r
753 \r
754   /* It makes no sense to try to reset the image path. */\r
755   if (! value || ! value->string) {\r
756     print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);\r
757     return -1;\r
758   }\r
759 \r
760   if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, value->string, 0, 0, 0, 0, 0, 0)) {\r
761     print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));\r
762     return -1;\r
763   }\r
764 \r
765   return 1;\r
766 }\r
767 \r
768 int native_get_imagepath(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
769   SC_HANDLE service_handle = (SC_HANDLE) param;\r
770   if (! service_handle) return -1;\r
771 \r
772   QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);\r
773   if (! qsc) return -1;\r
774 \r
775   int ret = value_from_string(name, value, qsc->lpBinaryPathName);\r
776   HeapFree(GetProcessHeap(), 0, qsc);\r
777 \r
778   return ret;\r
779 }\r
780 \r
781 int native_set_name(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
782   print_message(stderr, NSSM_MESSAGE_CANNOT_RENAME_SERVICE);\r
783   return -1;\r
784 }\r
785 \r
786 int native_get_name(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
787   return value_from_string(name, value, service_name);\r
788 }\r
789 \r
790 int native_set_objectname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
791   SC_HANDLE service_handle = (SC_HANDLE) param;\r
792   if (! service_handle) return -1;\r
793 \r
794   /*\r
795     Logical syntax is: nssm set <service> ObjectName <username> <password>\r
796     That means the username is actually passed in the additional parameter.\r
797   */\r
798   bool localsystem = false;\r
799   TCHAR *username = NSSM_LOCALSYSTEM_ACCOUNT;\r
800   TCHAR *password = 0;\r
801   if (additional) {\r
802     username = (TCHAR *) additional;\r
803     if (value && value->string) password = value->string;\r
804   }\r
805   else if (value && value->string) username = value->string;\r
806 \r
807   const TCHAR *well_known = well_known_username(username);\r
808   size_t passwordsize = 0;\r
809   if (well_known) {\r
810     if (str_equiv(well_known, NSSM_LOCALSYSTEM_ACCOUNT)) localsystem = true;\r
811     username = (TCHAR *) well_known;\r
812     password = _T("");\r
813   }\r
814   else if (! password) {\r
815     /* We need a password if the account requires it. */\r
816     print_message(stderr, NSSM_MESSAGE_MISSING_PASSWORD, name);\r
817     return -1;\r
818   }\r
819   else passwordsize = _tcslen(password) * sizeof(TCHAR);\r
820 \r
821   /*\r
822     ChangeServiceConfig() will fail to set the username if the service is set\r
823     to interact with the desktop.\r
824   */\r
825   unsigned long type = SERVICE_NO_CHANGE;\r
826   if (! localsystem) {\r
827     QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);\r
828     if (! qsc) {\r
829       if (passwordsize) SecureZeroMemory(password, passwordsize);\r
830       return -1;\r
831     }\r
832 \r
833     type = qsc->dwServiceType & ~SERVICE_INTERACTIVE_PROCESS;\r
834     HeapFree(GetProcessHeap(), 0, qsc);\r
835   }\r
836 \r
837   if (! well_known) {\r
838     if (grant_logon_as_service(username)) {\r
839       if (passwordsize) SecureZeroMemory(password, passwordsize);\r
840       print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);\r
841       return -1;\r
842     }\r
843   }\r
844 \r
845   if (! ChangeServiceConfig(service_handle, type, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, username, password, 0)) {\r
846     if (passwordsize) SecureZeroMemory(password, passwordsize);\r
847     print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));\r
848     return -1;\r
849   }\r
850 \r
851   if (passwordsize) SecureZeroMemory(password, passwordsize);\r
852 \r
853   if (localsystem) return 0;\r
854 \r
855   return 1;\r
856 }\r
857 \r
858 int native_get_objectname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
859   SC_HANDLE service_handle = (SC_HANDLE) param;\r
860   if (! service_handle) return -1;\r
861 \r
862   QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);\r
863   if (! qsc) return -1;\r
864 \r
865   int ret = value_from_string(name, value, qsc->lpServiceStartName);\r
866   HeapFree(GetProcessHeap(), 0, qsc);\r
867 \r
868   return ret;\r
869 }\r
870 \r
871 int native_set_startup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
872   SC_HANDLE service_handle = (SC_HANDLE) param;\r
873   if (! service_handle) return -1;\r
874 \r
875   /* It makes no sense to try to reset the startup type. */\r
876   if (! value || ! value->string) {\r
877     print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);\r
878     return -1;\r
879   }\r
880 \r
881   /* Map NSSM_STARTUP_* constant to Windows SERVICE_*_START constant. */\r
882   int service_startup = -1;\r
883   int i;\r
884   for (i = 0; startup_strings[i]; i++) {\r
885     if (str_equiv(value->string, startup_strings[i])) {\r
886       service_startup = i;\r
887       break;\r
888     }\r
889   }\r
890 \r
891   if (service_startup < 0) {\r
892     print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE_STARTUP, value->string);\r
893     for (i = 0; startup_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), startup_strings[i]);\r
894     return -1;\r
895   }\r
896 \r
897   unsigned long startup;\r
898   switch (service_startup) {\r
899     case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;\r
900     case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;\r
901     default: startup = SERVICE_AUTO_START;\r
902   }\r
903 \r
904   if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, startup, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) {\r
905     print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));\r
906     return -1;\r
907   }\r
908 \r
909   SERVICE_DELAYED_AUTO_START_INFO delayed;\r
910   ZeroMemory(&delayed, sizeof(delayed));\r
911   if (service_startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;\r
912   else delayed.fDelayedAutostart = 0;\r
913   if (! ChangeServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {\r
914     unsigned long error = GetLastError();\r
915     /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */\r
916     if (error != ERROR_INVALID_LEVEL) {\r
917       log_event(EVENTLOG_ERROR_TYPE, NSSM_MESSAGE_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service_name, error_string(error), 0);\r
918     }\r
919   }\r
920 \r
921   return 1;\r
922 }\r
923 \r
924 int native_get_startup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
925   SC_HANDLE service_handle = (SC_HANDLE) param;\r
926   if (! service_handle) return -1;\r
927 \r
928   QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);\r
929   if (! qsc) return -1;\r
930 \r
931   unsigned long startup;\r
932   int ret = get_service_startup(service_name, service_handle, qsc, &startup);\r
933   HeapFree(GetProcessHeap(), 0, qsc);\r
934 \r
935   if (ret) return -1;\r
936 \r
937   unsigned long i;\r
938   for (i = 0; startup_strings[i]; i++);\r
939   if (startup >= i) return -1;\r
940 \r
941   return value_from_string(name, value, startup_strings[startup]);\r
942 }\r
943 \r
944 int native_set_type(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
945   SC_HANDLE service_handle = (SC_HANDLE) param;\r
946   if (! service_handle) return -1;\r
947 \r
948   /* It makes no sense to try to reset the service type. */\r
949   if (! value || ! value->string) {\r
950     print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);\r
951     return -1;\r
952   }\r
953 \r
954   /*\r
955     We can only manage services of type SERVICE_WIN32_OWN_PROCESS\r
956     and SERVICE_INTERACTIVE_PROCESS.\r
957   */\r
958   unsigned long type = SERVICE_WIN32_OWN_PROCESS;\r
959   if (str_equiv(value->string, NSSM_INTERACTIVE_PROCESS)) type |= SERVICE_INTERACTIVE_PROCESS;\r
960   else if (! str_equiv(value->string, NSSM_WIN32_OWN_PROCESS)) {\r
961     print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE_TYPE, value->string);\r
962     _ftprintf(stderr, _T("%s\n"), NSSM_WIN32_OWN_PROCESS);\r
963     _ftprintf(stderr, _T("%s\n"), NSSM_INTERACTIVE_PROCESS);\r
964     return -1;\r
965   }\r
966 \r
967   /*\r
968     ChangeServiceConfig() will fail if the service runs under an account\r
969     other than LOCALSYSTEM and we try to make it interactive.\r
970   */\r
971   if (type & SERVICE_INTERACTIVE_PROCESS) {\r
972     QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);\r
973     if (! qsc) return -1;\r
974 \r
975     if (! str_equiv(qsc->lpServiceStartName, NSSM_LOCALSYSTEM_ACCOUNT)) {\r
976       HeapFree(GetProcessHeap(), 0, qsc);\r
977       print_message(stderr, NSSM_MESSAGE_INTERACTIVE_NOT_LOCALSYSTEM, value->string, service_name, NSSM_LOCALSYSTEM_ACCOUNT);\r
978       return -1;\r
979     }\r
980 \r
981     HeapFree(GetProcessHeap(), 0, qsc);\r
982   }\r
983 \r
984   if (! ChangeServiceConfig(service_handle, type, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) {\r
985     print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));\r
986     return -1;\r
987   }\r
988 \r
989   return 1;\r
990 }\r
991 \r
992 int native_get_type(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
993   SC_HANDLE service_handle = (SC_HANDLE) param;\r
994   if (! service_handle) return -1;\r
995 \r
996   QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);\r
997   if (! qsc) return -1;\r
998 \r
999   value->numeric = qsc->dwServiceType;\r
1000   HeapFree(GetProcessHeap(), 0, qsc);\r
1001 \r
1002   const TCHAR *string;\r
1003   switch (value->numeric) {\r
1004     case SERVICE_KERNEL_DRIVER: string = NSSM_KERNEL_DRIVER; break;\r
1005     case SERVICE_FILE_SYSTEM_DRIVER: string = NSSM_FILE_SYSTEM_DRIVER; break;\r
1006     case SERVICE_WIN32_OWN_PROCESS: string = NSSM_WIN32_OWN_PROCESS; break;\r
1007     case SERVICE_WIN32_SHARE_PROCESS: string = NSSM_WIN32_SHARE_PROCESS; break;\r
1008     case SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS: string = NSSM_INTERACTIVE_PROCESS; break;\r
1009     case SERVICE_WIN32_SHARE_PROCESS|SERVICE_INTERACTIVE_PROCESS: string = NSSM_SHARE_INTERACTIVE_PROCESS; break;\r
1010     default: string = NSSM_UNKNOWN;\r
1011   }\r
1012 \r
1013   return value_from_string(name, value, string);\r
1014 }\r
1015 \r
1016 int set_setting(const TCHAR *service_name, HKEY key, settings_t *setting, value_t *value, const TCHAR *additional) {\r
1017   if (! key) return -1;\r
1018   int ret;\r
1019 \r
1020   if (setting->set) ret = setting->set(service_name, (void *) key, setting->name, setting->default_value, value, additional);\r
1021   else ret = -1;\r
1022 \r
1023   if (! ret) print_message(stdout, NSSM_MESSAGE_RESET_SETTING, setting->name, service_name);\r
1024   else if (ret > 0) print_message(stdout, NSSM_MESSAGE_SET_SETTING, setting->name, service_name);\r
1025   else print_message(stderr, NSSM_MESSAGE_SET_SETTING_FAILED, setting->name, service_name);\r
1026 \r
1027   return ret;\r
1028 }\r
1029 \r
1030 int set_setting(const TCHAR *service_name, SC_HANDLE service_handle, settings_t *setting, value_t *value, const TCHAR *additional) {\r
1031   if (! service_handle) return -1;\r
1032 \r
1033   int ret;\r
1034   if (setting->set) ret = setting->set(service_name, service_handle, setting->name, setting->default_value, value, additional);\r
1035   else ret = -1;\r
1036 \r
1037   if (! ret) print_message(stdout, NSSM_MESSAGE_RESET_SETTING, setting->name, service_name);\r
1038   else if (ret > 0) print_message(stdout, NSSM_MESSAGE_SET_SETTING, setting->name, service_name);\r
1039   else print_message(stderr, NSSM_MESSAGE_SET_SETTING_FAILED, setting->name, service_name);\r
1040 \r
1041   return ret;\r
1042 }\r
1043 \r
1044 /*\r
1045   Returns:  1 if the value was retrieved.\r
1046             0 if the default value was retrieved.\r
1047            -1 on error.\r
1048 */\r
1049 int get_setting(const TCHAR *service_name, HKEY key, settings_t *setting, value_t *value, const TCHAR *additional) {\r
1050   if (! key) return -1;\r
1051   int ret;\r
1052 \r
1053   switch (setting->type) {\r
1054     case REG_EXPAND_SZ:\r
1055     case REG_MULTI_SZ:\r
1056     case REG_SZ:\r
1057       value->string = (TCHAR *) setting->default_value;\r
1058       if (setting->get) ret = setting->get(service_name, (void *) key, setting->name, setting->default_value, value, additional);\r
1059       else ret = -1;\r
1060       break;\r
1061 \r
1062     case REG_DWORD:\r
1063       value->numeric = PtrToUlong(setting->default_value);\r
1064       if (setting->get) ret = setting->get(service_name, (void *) key, setting->name, setting->default_value, value, additional);\r
1065       else ret = -1;\r
1066       break;\r
1067 \r
1068     default:\r
1069       ret = -1;\r
1070       break;\r
1071   }\r
1072 \r
1073   if (ret < 0) print_message(stderr, NSSM_MESSAGE_GET_SETTING_FAILED, setting->name, service_name);\r
1074 \r
1075   return ret;\r
1076 }\r
1077 \r
1078 int get_setting(const TCHAR *service_name, SC_HANDLE service_handle, settings_t *setting, value_t *value, const TCHAR *additional) {\r
1079   if (! service_handle) return -1;\r
1080   return setting->get(service_name, service_handle, setting->name, 0, value, additional);\r
1081 }\r
1082 \r
1083 settings_t settings[] = {\r
1084   { NSSM_REG_EXE, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },\r
1085   { NSSM_REG_FLAGS, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },\r
1086   { NSSM_REG_DIR, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },\r
1087   { NSSM_REG_EXIT, REG_SZ, (void *) exit_action_strings[NSSM_EXIT_RESTART], false, ADDITIONAL_MANDATORY, setting_set_exit_action, setting_get_exit_action },\r
1088   { NSSM_REG_HOOK, REG_SZ, (void *) _T(""), false, ADDITIONAL_MANDATORY, setting_set_hook, setting_get_hook },\r
1089   { NSSM_REG_AFFINITY, REG_SZ, 0, false, 0, setting_set_affinity, setting_get_affinity },\r
1090   { NSSM_REG_ENV, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment },\r
1091   { NSSM_REG_ENV_EXTRA, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment },\r
1092   { NSSM_REG_NO_CONSOLE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },\r
1093   { NSSM_REG_PRIORITY, REG_SZ, (void *) priority_strings[NSSM_NORMAL_PRIORITY], false, 0, setting_set_priority, setting_get_priority },\r
1094   { NSSM_REG_RESTART_DELAY, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },\r
1095   { NSSM_REG_STDIN, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },\r
1096   { NSSM_REG_STDIN NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDIN_SHARING, false, 0, setting_set_number, setting_get_number },\r
1097   { NSSM_REG_STDIN NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDIN_DISPOSITION, false, 0, setting_set_number, setting_get_number },\r
1098   { NSSM_REG_STDIN NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDIN_FLAGS, false, 0, setting_set_number, setting_get_number },\r
1099   { NSSM_REG_STDOUT, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },\r
1100   { NSSM_REG_STDOUT NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDOUT_SHARING, false, 0, setting_set_number, setting_get_number },\r
1101   { NSSM_REG_STDOUT NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDOUT_DISPOSITION, false, 0, setting_set_number, setting_get_number },\r
1102   { NSSM_REG_STDOUT NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDOUT_FLAGS, false, 0, setting_set_number, setting_get_number },\r
1103   { NSSM_REG_STDOUT NSSM_REG_STDIO_COPY_AND_TRUNCATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },\r
1104   { NSSM_REG_STDERR, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },\r
1105   { NSSM_REG_STDERR NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDERR_SHARING, false, 0, setting_set_number, setting_get_number },\r
1106   { NSSM_REG_STDERR NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDERR_DISPOSITION, false, 0, setting_set_number, setting_get_number },\r
1107   { NSSM_REG_STDERR NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDERR_FLAGS, false, 0, setting_set_number, setting_get_number },\r
1108   { NSSM_REG_STDERR NSSM_REG_STDIO_COPY_AND_TRUNCATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },\r
1109   { NSSM_REG_STOP_METHOD_SKIP, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },\r
1110   { NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_CONSOLE_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },\r
1111   { NSSM_REG_KILL_WINDOW_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_WINDOW_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },\r
1112   { NSSM_REG_KILL_THREADS_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_THREADS_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },\r
1113   { NSSM_REG_KILL_PROCESS_TREE, REG_DWORD, (void *) 1, false, 0, setting_set_number, setting_get_number },\r
1114   { NSSM_REG_THROTTLE, REG_DWORD, (void *) NSSM_RESET_THROTTLE_RESTART, false, 0, setting_set_number, setting_get_number },\r
1115   { NSSM_REG_HOOK_SHARE_OUTPUT_HANDLES, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },\r
1116   { NSSM_REG_ROTATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },\r
1117   { NSSM_REG_ROTATE_ONLINE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },\r
1118   { NSSM_REG_ROTATE_SECONDS, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },\r
1119   { NSSM_REG_ROTATE_BYTES_LOW, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },\r
1120   { NSSM_REG_ROTATE_BYTES_HIGH, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },\r
1121   { NSSM_REG_ROTATE_DELAY, REG_DWORD, (void *) NSSM_ROTATE_DELAY, false, 0, setting_set_number, setting_get_number },\r
1122   { NSSM_NATIVE_DEPENDONGROUP, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_dependongroup, native_get_dependongroup },\r
1123   { NSSM_NATIVE_DEPENDONSERVICE, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_dependonservice, native_get_dependonservice },\r
1124   { NSSM_NATIVE_DESCRIPTION, REG_SZ, _T(""), true, 0, native_set_description, native_get_description },\r
1125   { NSSM_NATIVE_DISPLAYNAME, REG_SZ, NULL, true, 0, native_set_displayname, native_get_displayname },\r
1126   { NSSM_NATIVE_ENVIRONMENT, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_environment, native_get_environment },\r
1127   { NSSM_NATIVE_IMAGEPATH, REG_EXPAND_SZ, NULL, true, 0, native_set_imagepath, native_get_imagepath },\r
1128   { NSSM_NATIVE_OBJECTNAME, REG_SZ, NSSM_LOCALSYSTEM_ACCOUNT, true, 0, native_set_objectname, native_get_objectname },\r
1129   { NSSM_NATIVE_NAME, REG_SZ, NULL, true, 0, native_set_name, native_get_name },\r
1130   { NSSM_NATIVE_STARTUP, REG_SZ, NULL, true, 0, native_set_startup, native_get_startup },\r
1131   { NSSM_NATIVE_TYPE, REG_SZ, NULL, true, 0, native_set_type, native_get_type },\r
1132   { NULL, NULL, NULL, NULL, NULL }\r
1133 };\r