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