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