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