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