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