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