c83abb13b646a57869c48ca12343f8f35843893f
[nssm.git] / settings.cpp
1 #include "nssm.h"
2 /* XXX: (value && value->string) is probably bogus because value is probably never null */
3
4 extern const TCHAR *exit_action_strings[];
5 extern const TCHAR *startup_strings[];
6
7 /* Does the parameter refer to the default value of the AppExit setting? */
8 static inline int is_default_exit_action(const TCHAR *value) {
9   return (str_equiv(value, _T("default")) || str_equiv(value, _T("*")) || ! value[0]);
10 }
11
12 static int value_from_string(const TCHAR *name, value_t *value, const TCHAR *string) {
13   size_t len = _tcslen(string);
14   if (! len++) {
15     value->string = 0;
16     return 0;
17   }
18
19   value->string = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));
20   if (! value->string) {
21     print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, name, _T("value_from_string()"));
22     return -1;
23   }
24
25   if (_sntprintf_s(value->string, len, _TRUNCATE, _T("%s"), string) < 0) {
26     HeapFree(GetProcessHeap(), 0, value->string);
27     print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, name, _T("value_from_string()"));
28     return -1;
29   }
30
31   return 1;
32 }
33
34 /* Functions to manage NSSM-specific settings in the registry. */
35 static int setting_set_number(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
36   HKEY key = (HKEY) param;
37   if (! key) return -1;
38
39   unsigned long number;
40   long error;
41
42   /* Resetting to default? */
43   if (! value || ! value->string) {
44     error = RegDeleteValue(key, name);
45     if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
46     print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
47     return -1;
48   }
49   if (str_number(value->string, &number)) return -1;
50
51   if (default_value && number == (unsigned long) default_value) {
52     error = RegDeleteValue(key, name);
53     if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
54     print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
55     return -1;
56   }
57
58   if (set_number(key, (TCHAR *) name, number)) return -1;
59
60   return 1;
61 }
62
63 static int setting_get_number(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
64   HKEY key = (HKEY) param;
65   return get_number(key, (TCHAR *) name, &value->numeric, false);
66 }
67
68 static int setting_set_string(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
69   HKEY key = (HKEY) param;
70   if (! key) return -1;
71
72   long error;
73
74   /* Resetting to default? */
75   if (! value || ! value->string) {
76     if (default_value) value->string = (TCHAR *) default_value;
77     else {
78       error = RegDeleteValue(key, name);
79       if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
80       print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
81       return -1;
82     }
83   }
84   if (default_value && _tcslen((TCHAR *) default_value) && str_equiv(value->string, (TCHAR *) default_value)) {
85     error = RegDeleteValue(key, name);
86     if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
87     print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
88     return -1;
89   }
90
91   if (set_expand_string(key, (TCHAR *) name, value->string)) return -1;
92
93   return 1;
94 }
95
96 static int setting_get_string(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
97   HKEY key = (HKEY) param;
98   TCHAR buffer[VALUE_LENGTH];
99
100   if (expand_parameter(key, (TCHAR *) name, (TCHAR *) buffer, (unsigned long) sizeof(buffer), false, false)) return -1;
101
102   return value_from_string(name, value, buffer);
103 }
104
105 static int setting_set_exit_action(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
106   unsigned long exitcode;
107   TCHAR *code;
108   TCHAR action_string[ACTION_LEN];
109
110   if (additional) {
111     /* Default action? */
112     if (is_default_exit_action(additional)) code = 0;
113     else {
114       if (str_number(additional, &exitcode)) return -1;
115       code = (TCHAR *) additional;
116     }
117   }
118
119   HKEY key = open_registry(service_name, name, KEY_WRITE);
120   if (! key) return -1;
121
122   long error;
123   int ret = 1;
124
125   /* Resetting to default? */
126   if (value && value->string) _sntprintf_s(action_string, _countof(action_string), _TRUNCATE, _T("%s"), value->string);
127   else {
128     if (code) {
129       /* Delete explicit action. */
130       error = RegDeleteValue(key, code);
131       RegCloseKey(key);
132       if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
133       print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, code, service_name, error_string(error));
134       return -1;
135     }
136     else {
137       /* Explicitly keep the default action. */
138       if (default_value) _sntprintf_s(action_string, _countof(action_string), _TRUNCATE, _T("%s"), (TCHAR *) default_value);
139       ret = 0;
140     }
141   }
142
143   /* Validate the string. */
144   for (int i = 0; exit_action_strings[i]; i++) {
145     if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
146       if (default_value && str_equiv(action_string, (TCHAR *) default_value)) ret = 0;
147       if (RegSetValueEx(key, code, 0, REG_SZ, (const unsigned char *) action_string, (unsigned long) (_tcslen(action_string) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) {
148         print_message(stderr, NSSM_MESSAGE_SETVALUE_FAILED, code, service_name, error_string(GetLastError()));
149         RegCloseKey(key);
150         return -1;
151       }
152
153       RegCloseKey(key);
154       return ret;
155     }
156   }
157
158   print_message(stderr, NSSM_MESSAGE_INVALID_EXIT_ACTION, action_string);
159   for (int i = 0; exit_action_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), exit_action_strings[i]);
160
161   return -1;
162 }
163
164 static int setting_get_exit_action(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
165   unsigned long exitcode = 0;
166   unsigned long *code = 0;
167
168   if (additional) {
169     if (! is_default_exit_action(additional)) {
170       if (str_number(additional, &exitcode)) return -1;
171       code = &exitcode;
172     }
173   }
174
175   TCHAR action_string[ACTION_LEN];
176   bool default_action;
177   if (get_exit_action(service_name, code, action_string, &default_action)) return -1;
178
179   value_from_string(name, value, action_string);
180
181   if (default_action && ! _tcsnicmp((const TCHAR *) action_string, (TCHAR *) default_value, ACTION_LEN)) return 0;
182   return 1;
183 }
184
185 static int setting_set_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
186   HKEY key = (HKEY) param;
187   if (! param) return -1;
188
189   if (! value || ! value->string || ! value->string[0]) {
190     long error = RegDeleteValue(key, name);
191     if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
192     print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
193     return -1;
194   }
195
196   unsigned long envlen = (unsigned long) _tcslen(value->string) + 1;
197   TCHAR *unformatted = 0;
198   unsigned long newlen;
199   if (unformat_environment(value->string, envlen, &unformatted, &newlen)) return -1;
200
201   if (test_environment(unformatted)) {
202     HeapFree(GetProcessHeap(), 0, unformatted);
203     print_message(stderr, NSSM_GUI_INVALID_ENVIRONMENT);
204     return -1;
205   }
206
207   if (RegSetValueEx(key, name, 0, REG_MULTI_SZ, (const unsigned char *) unformatted, (unsigned long) newlen * sizeof(TCHAR)) != ERROR_SUCCESS) {
208     if (newlen) HeapFree(GetProcessHeap(), 0, unformatted);
209     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV, error_string(GetLastError()), 0);
210     return -1;
211   }
212
213   if (newlen) HeapFree(GetProcessHeap(), 0, unformatted);
214   return 1;
215 }
216
217 static int setting_get_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
218   HKEY key = (HKEY) param;
219   if (! param) return -1;
220
221   TCHAR *env = 0;
222   unsigned long envlen;
223   if (set_environment((TCHAR *) service_name, key, (TCHAR *) name, &env, &envlen)) return -1;
224   if (! envlen) return 0;
225
226   TCHAR *formatted;
227   unsigned long newlen;
228   if (format_environment(env, envlen, &formatted, &newlen)) return -1;
229
230   int ret;
231   if (additional) {
232     /* Find named environment variable. */
233     TCHAR *s;
234     size_t len = _tcslen(additional);
235     for (s = env; *s; s++) {
236       /* Look for <additional>=<string> NULL NULL */
237       if (! _tcsnicmp(s, additional, len) && s[len] == _T('=')) {
238         /* Strip <key>= */
239         s += len + 1;
240         ret = value_from_string(name, value, s);
241         HeapFree(GetProcessHeap(), 0, env);
242         return ret;
243       }
244
245       /* Skip this string. */
246       for ( ; *s; s++);
247     }
248     HeapFree(GetProcessHeap(), 0, env);
249     return 0;
250   }
251
252   HeapFree(GetProcessHeap(), 0, env);
253
254   ret = value_from_string(name, value, formatted);
255   if (newlen) HeapFree(GetProcessHeap(), 0, formatted);
256   return ret;
257 }
258
259 /* Functions to manage native service settings. */
260 int native_set_description(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
261   SC_HANDLE service_handle = (SC_HANDLE) param;
262   if (! service_handle) return -1;
263
264   TCHAR *description = 0;
265   if (value) description = value->string;
266   if (set_service_description(service_name, service_handle, description)) return -1;
267
268   if (description && description[0]) return 1;
269
270   return 0;
271 }
272
273 int native_get_description(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
274   SC_HANDLE service_handle = (SC_HANDLE) param;
275   if (! service_handle) return -1;
276
277   TCHAR buffer[VALUE_LENGTH];
278   if (get_service_description(service_name, service_handle, _countof(buffer), buffer)) return -1;
279
280   if (buffer[0]) return value_from_string(name, value, buffer);
281   value->string = 0;
282
283   return 0;
284 }
285
286 int native_set_displayname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
287   SC_HANDLE service_handle = (SC_HANDLE) param;
288   if (! service_handle) return -1;
289
290   TCHAR *displayname = 0;
291   if (value && value->string) displayname = value->string;
292   else displayname = (TCHAR *) service_name;
293
294   if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, displayname)) {
295     print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
296     return -1;
297   }
298
299   /*
300     If the display name and service name differ only in case,
301     ChangeServiceConfig() will return success but the display name will be
302     set to the service name, NOT the value passed to the function.
303     This appears to be a quirk of Windows rather than a bug here.
304   */
305   if (displayname != service_name && ! str_equiv(displayname, service_name)) return 1;
306
307   return 0;
308 }
309
310 int native_get_displayname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
311   SC_HANDLE service_handle = (SC_HANDLE) param;
312   if (! service_handle) return -1;
313
314   QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
315   if (! qsc) return -1;
316
317   int ret = value_from_string(name, value, qsc->lpDisplayName);
318   HeapFree(GetProcessHeap(), 0, qsc);
319
320   return ret;
321 }
322
323 int native_set_imagepath(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
324   SC_HANDLE service_handle = (SC_HANDLE) param;
325   if (! service_handle) return -1;
326
327   /* It makes no sense to try to reset the image path. */
328   if (! value || ! value->string) {
329     print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);
330     return -1;
331   }
332
333   if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, value->string, 0, 0, 0, 0, 0, 0)) {
334     print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
335     return -1;
336   }
337
338   return 1;
339 }
340
341 int native_get_imagepath(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
342   SC_HANDLE service_handle = (SC_HANDLE) param;
343   if (! service_handle) return -1;
344
345   QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
346   if (! qsc) return -1;
347
348   int ret = value_from_string(name, value, qsc->lpBinaryPathName);
349   HeapFree(GetProcessHeap(), 0, qsc);
350
351   return ret;
352 }
353
354 int native_set_name(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
355   print_message(stderr, NSSM_MESSAGE_CANNOT_RENAME_SERVICE);
356   return -1;
357 }
358
359 int native_get_name(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
360   return value_from_string(name, value, service_name);
361 }
362
363 int native_set_objectname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
364   SC_HANDLE service_handle = (SC_HANDLE) param;
365   if (! service_handle) return -1;
366
367   /*
368     Logical syntax is: nssm set <service> ObjectName <username> <password>
369     That means the username is actually passed in the additional parameter.
370   */
371   bool localsystem = true;
372   TCHAR *username = NSSM_LOCALSYSTEM_ACCOUNT;
373   TCHAR *password = 0;
374   if (additional) {
375     if (! str_equiv(additional, NSSM_LOCALSYSTEM_ACCOUNT)) {
376       localsystem = false;
377       username = (TCHAR *) additional;
378       if (value && value->string) password = value->string;
379       else {
380         /* We need a password if the account is not LOCALSYSTEM. */
381         print_message(stderr, NSSM_MESSAGE_MISSING_PASSWORD, name);
382         return -1;
383       }
384     }
385   }
386
387   /*
388     ChangeServiceConfig() will fail to set the username if the service is set
389     to interact with the desktop.
390   */
391   unsigned long type = SERVICE_NO_CHANGE;
392   if (! localsystem) {
393     QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
394     if (! qsc) {
395       if (password) SecureZeroMemory(password, _tcslen(password) * sizeof(TCHAR));
396       return -1;
397     }
398
399     type = qsc->dwServiceType & ~SERVICE_INTERACTIVE_PROCESS;
400     HeapFree(GetProcessHeap(), 0, qsc);
401   }
402
403   if (grant_logon_as_service(username)) {
404     if (password) SecureZeroMemory(password, _tcslen(password) * sizeof(TCHAR));
405     print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);
406     return -1;
407   }
408
409   if (! ChangeServiceConfig(service_handle, type, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, username, password, 0)) {
410     if (password) SecureZeroMemory(password, _tcslen(password) * sizeof(TCHAR));
411     print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
412     return -1;
413   }
414   if (password) SecureZeroMemory(password, _tcslen(password) * sizeof(TCHAR));
415
416   if (localsystem) return 0;
417
418   return 1;
419 }
420
421 int native_get_objectname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
422   SC_HANDLE service_handle = (SC_HANDLE) param;
423   if (! service_handle) return -1;
424
425   QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
426   if (! qsc) return -1;
427
428   int ret = value_from_string(name, value, qsc->lpServiceStartName);
429   HeapFree(GetProcessHeap(), 0, qsc);
430
431   return ret;
432 }
433
434 int native_set_startup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
435   SC_HANDLE service_handle = (SC_HANDLE) param;
436   if (! service_handle) return -1;
437
438   /* It makes no sense to try to reset the startup type. */
439   if (! value || ! value->string) {
440     print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);
441     return -1;
442   }
443
444   /* Map NSSM_STARTUP_* constant to Windows SERVICE_*_START constant. */
445   int service_startup = -1;
446   int i;
447   for (i = 0; startup_strings[i]; i++) {
448     if (str_equiv(value->string, startup_strings[i])) {
449       service_startup = i;
450       break;
451     }
452   }
453
454   if (service_startup < 0) {
455     print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE_STARTUP, value->string);
456     for (i = 0; startup_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), startup_strings[i]);
457     return -1;
458   }
459
460   unsigned long startup;
461   switch (service_startup) {
462     case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;
463     case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;
464     default: startup = SERVICE_AUTO_START;
465   }
466
467   if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, startup, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) {
468     print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
469     return -1;
470   }
471
472   SERVICE_DELAYED_AUTO_START_INFO delayed;
473   ZeroMemory(&delayed, sizeof(delayed));
474   if (service_startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;
475   else delayed.fDelayedAutostart = 0;
476   if (! ChangeServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {
477     unsigned long error = GetLastError();
478     /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
479     if (error != ERROR_INVALID_LEVEL) {
480       log_event(EVENTLOG_ERROR_TYPE, NSSM_MESSAGE_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service_name, error_string(error), 0);
481     }
482   }
483
484   return 1;
485 }
486
487 int native_get_startup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
488   SC_HANDLE service_handle = (SC_HANDLE) param;
489   if (! service_handle) return -1;
490
491   QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
492   if (! qsc) return -1;
493
494   unsigned long startup;
495   int ret = get_service_startup(service_name, service_handle, qsc, &startup);
496   HeapFree(GetProcessHeap(), 0, qsc);
497
498   if (ret) return -1;
499
500   unsigned long i;
501   for (i = 0; startup_strings[i]; i++);
502   if (startup >= i) return -1;
503
504   return value_from_string(name, value, startup_strings[startup]);
505 }
506
507 int native_set_type(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
508   SC_HANDLE service_handle = (SC_HANDLE) param;
509   if (! service_handle) return -1;
510
511   /* It makes no sense to try to reset the service type. */
512   if (! value || ! value->string) {
513     print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);
514     return -1;
515   }
516
517   /*
518     We can only manage services of type SERVICE_WIN32_OWN_PROCESS
519     and SERVICE_INTERACTIVE_PROCESS.
520   */
521   unsigned long type = SERVICE_WIN32_OWN_PROCESS;
522   if (str_equiv(value->string, NSSM_INTERACTIVE_PROCESS)) type |= SERVICE_INTERACTIVE_PROCESS;
523   else if (! str_equiv(value->string, NSSM_WIN32_OWN_PROCESS)) {
524     print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE_TYPE, value->string);
525     _ftprintf(stderr, _T("%s\n"), NSSM_WIN32_OWN_PROCESS);
526     _ftprintf(stderr, _T("%s\n"), NSSM_INTERACTIVE_PROCESS);
527     return -1;
528   }
529
530   /*
531     ChangeServiceConfig() will fail if the service runs under an account
532     other than LOCALSYSTEM and we try to make it interactive.
533   */
534   if (type & SERVICE_INTERACTIVE_PROCESS) {
535     QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
536     if (! qsc) return -1;
537
538     if (! str_equiv(qsc->lpServiceStartName, NSSM_LOCALSYSTEM_ACCOUNT)) {
539       HeapFree(GetProcessHeap(), 0, qsc);
540       print_message(stderr, NSSM_MESSAGE_INTERACTIVE_NOT_LOCALSYSTEM, value->string, service_name, NSSM_LOCALSYSTEM_ACCOUNT);
541       return -1;
542     }
543
544     HeapFree(GetProcessHeap(), 0, qsc);
545   }
546
547   if (! ChangeServiceConfig(service_handle, type, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) {
548     print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
549     return -1;
550   }
551
552   return 1;
553 }
554
555 int native_get_type(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
556   SC_HANDLE service_handle = (SC_HANDLE) param;
557   if (! service_handle) return -1;
558
559   QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
560   if (! qsc) return -1;
561
562   value->numeric = qsc->dwServiceType;
563   HeapFree(GetProcessHeap(), 0, qsc);
564
565   const TCHAR *string;
566   switch (value->numeric) {
567     case SERVICE_KERNEL_DRIVER: string = NSSM_KERNEL_DRIVER; break;
568     case SERVICE_FILE_SYSTEM_DRIVER: string = NSSM_FILE_SYSTEM_DRIVER; break;
569     case SERVICE_WIN32_OWN_PROCESS: string = NSSM_WIN32_OWN_PROCESS; break;
570     case SERVICE_WIN32_SHARE_PROCESS: string = NSSM_WIN32_SHARE_PROCESS; break;
571     case SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS: string = NSSM_INTERACTIVE_PROCESS; break;
572     case SERVICE_WIN32_SHARE_PROCESS|SERVICE_INTERACTIVE_PROCESS: string = NSSM_SHARE_INTERACTIVE_PROCESS; break;
573     default: string = NSSM_UNKNOWN;
574   }
575
576   return value_from_string(name, value, string);
577 }
578
579 int set_setting(const TCHAR *service_name, HKEY key, settings_t *setting, value_t *value, const TCHAR *additional) {
580   if (! key) return -1;
581   int ret;
582
583   if (setting->set) ret = setting->set(service_name, (void *) key, setting->name, setting->default_value, value, additional);
584   else ret = -1;
585
586   if (! ret) print_message(stdout, NSSM_MESSAGE_RESET_SETTING, setting->name, service_name);
587   else if (ret > 0) print_message(stdout, NSSM_MESSAGE_SET_SETTING, setting->name, service_name);
588   else print_message(stderr, NSSM_MESSAGE_SET_SETTING_FAILED, setting->name, service_name);
589
590   return ret;
591 }
592
593 int set_setting(const TCHAR *service_name, SC_HANDLE service_handle, settings_t *setting, value_t *value, const TCHAR *additional) {
594   if (! service_handle) return -1;
595
596   int ret;
597   if (setting->set) ret = setting->set(service_name, service_handle, setting->name, setting->default_value, value, additional);
598   else ret = -1;
599
600   if (! ret) print_message(stdout, NSSM_MESSAGE_RESET_SETTING, setting->name, service_name);
601   else if (ret > 0) print_message(stdout, NSSM_MESSAGE_SET_SETTING, setting->name, service_name);
602   else print_message(stderr, NSSM_MESSAGE_SET_SETTING_FAILED, setting->name, service_name);
603
604   return ret;
605 }
606
607 /*
608   Returns:  1 if the value was retrieved.
609             0 if the default value was retrieved.
610            -1 on error.
611 */
612 int get_setting(const TCHAR *service_name, HKEY key, settings_t *setting, value_t *value, const TCHAR *additional) {
613   if (! key) return -1;
614   int ret;
615
616   switch (setting->type) {
617     case REG_EXPAND_SZ:
618     case REG_MULTI_SZ:
619     case REG_SZ:
620       value->string = (TCHAR *) setting->default_value;
621       if (setting->get) ret = setting->get(service_name, (void *) key, setting->name, setting->default_value, value, additional);
622       else ret = -1;
623       break;
624
625     case REG_DWORD:
626       value->numeric = (unsigned long) setting->default_value;
627       if (setting->get) ret = setting->get(service_name, (void *) key, setting->name, setting->default_value, value, additional);
628       else ret = -1;
629       break;
630
631     default:
632       ret = -1;
633       break;
634   }
635
636   if (ret < 0) print_message(stderr, NSSM_MESSAGE_GET_SETTING_FAILED, setting->name, service_name);
637
638   return ret;
639 }
640
641 int get_setting(const TCHAR *service_name, SC_HANDLE service_handle, settings_t *setting, value_t *value, const TCHAR *additional) {
642   if (! service_handle) return -1;
643   return setting->get(service_name, service_handle, setting->name, 0, value, additional);
644 }
645
646 settings_t settings[] = {
647   { NSSM_REG_EXE, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },
648   { NSSM_REG_FLAGS, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },
649   { NSSM_REG_DIR, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },
650   { NSSM_REG_EXIT, REG_SZ, (void *) exit_action_strings[NSSM_EXIT_RESTART], false, ADDITIONAL_MANDATORY, setting_set_exit_action, setting_get_exit_action },
651   { NSSM_REG_ENV, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment },
652   { NSSM_REG_ENV_EXTRA, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment },
653   { NSSM_REG_STDIN, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },
654   { NSSM_REG_STDIN NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDIN_SHARING, false, 0, setting_set_number, setting_get_number },
655   { NSSM_REG_STDIN NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDIN_DISPOSITION, false, 0, setting_set_number, setting_get_number },
656   { NSSM_REG_STDIN NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDIN_FLAGS, false, 0, setting_set_number, setting_get_number },
657   { NSSM_REG_STDOUT, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },
658   { NSSM_REG_STDOUT NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDOUT_SHARING, false, 0, setting_set_number, setting_get_number },
659   { NSSM_REG_STDOUT NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDOUT_DISPOSITION, false, 0, setting_set_number, setting_get_number },
660   { NSSM_REG_STDOUT NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDOUT_FLAGS, false, 0, setting_set_number, setting_get_number },
661   { NSSM_REG_STDERR, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },
662   { NSSM_REG_STDERR NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDERR_SHARING, false, 0, setting_set_number, setting_get_number },
663   { NSSM_REG_STDERR NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDERR_DISPOSITION, false, 0, setting_set_number, setting_get_number },
664   { NSSM_REG_STDERR NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDERR_FLAGS, false, 0, setting_set_number, setting_get_number },
665   { NSSM_REG_STOP_METHOD_SKIP, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
666   { NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_CONSOLE_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },
667   { NSSM_REG_KILL_WINDOW_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_WINDOW_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },
668   { NSSM_REG_KILL_THREADS_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_THREADS_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },
669   { NSSM_REG_THROTTLE, REG_DWORD, (void *) NSSM_RESET_THROTTLE_RESTART, false, 0, setting_set_number, setting_get_number },
670   { NSSM_REG_ROTATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
671   { NSSM_REG_ROTATE_SECONDS, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
672   { NSSM_REG_ROTATE_BYTES_LOW, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
673   { NSSM_REG_ROTATE_BYTES_HIGH, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
674   { NSSM_NATIVE_DESCRIPTION, REG_SZ, _T(""), true, 0, native_set_description, native_get_description },
675   { NSSM_NATIVE_DISPLAYNAME, REG_SZ, NULL, true, 0, native_set_displayname, native_get_displayname },
676   { NSSM_NATIVE_IMAGEPATH, REG_EXPAND_SZ, NULL, true, 0, native_set_imagepath, native_get_imagepath },
677   { NSSM_NATIVE_OBJECTNAME, REG_SZ, NSSM_LOCALSYSTEM_ACCOUNT, true, ADDITIONAL_SETTING, native_set_objectname, native_get_objectname },
678   { NSSM_NATIVE_NAME, REG_SZ, NULL, true, 0, native_set_name, native_get_name },
679   { NSSM_NATIVE_STARTUP, REG_SZ, NULL, true, 0, native_set_startup, native_get_startup },
680   { NSSM_NATIVE_TYPE, REG_SZ, NULL, true, 0, native_set_type, native_get_type },
681   { NULL, NULL, NULL, NULL, NULL }
682 };