Allow service editing on the command line.
[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_REGDELETEVALUE_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_objectname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
355   SC_HANDLE service_handle = (SC_HANDLE) param;
356   if (! service_handle) return -1;
357
358   /*
359     Logical syntax is: nssm set <service> ObjectName <username> <password>
360     That means the username is actually passed in the additional parameter.
361   */
362   bool localsystem = true;
363   TCHAR *username = NSSM_LOCALSYSTEM_ACCOUNT;
364   TCHAR *password = 0;
365   if (additional) {
366     if (! str_equiv(additional, NSSM_LOCALSYSTEM_ACCOUNT)) {
367       localsystem = false;
368       username = (TCHAR *) additional;
369       if (value && value->string) password = value->string;
370       else {
371         /* We need a password if the account is not LOCALSYSTEM. */
372         print_message(stderr, NSSM_MESSAGE_MISSING_PASSWORD, name);
373         return -1;
374       }
375     }
376   }
377
378   /*
379     ChangeServiceConfig() will fail to set the username if the service is set
380     to interact with the desktop.
381   */
382   unsigned long type = SERVICE_NO_CHANGE;
383   if (! localsystem) {
384     QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
385     if (! qsc) {
386       if (password) SecureZeroMemory(password, _tcslen(password) * sizeof(TCHAR));
387       return -1;
388     }
389
390     type = qsc->dwServiceType & ~SERVICE_INTERACTIVE_PROCESS;
391     HeapFree(GetProcessHeap(), 0, qsc);
392   }
393
394   if (grant_logon_as_service(username)) {
395     if (password) SecureZeroMemory(password, _tcslen(password) * sizeof(TCHAR));
396     print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);
397     return -1;
398   }
399
400   if (! ChangeServiceConfig(service_handle, type, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, username, password, 0)) {
401     if (password) SecureZeroMemory(password, _tcslen(password) * sizeof(TCHAR));
402     print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
403     return -1;
404   }
405   if (password) SecureZeroMemory(password, _tcslen(password) * sizeof(TCHAR));
406
407   if (localsystem) return 0;
408
409   return 1;
410 }
411
412 int native_get_objectname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
413   SC_HANDLE service_handle = (SC_HANDLE) param;
414   if (! service_handle) return -1;
415
416   QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
417   if (! qsc) return -1;
418
419   int ret = value_from_string(name, value, qsc->lpServiceStartName);
420   HeapFree(GetProcessHeap(), 0, qsc);
421
422   return ret;
423 }
424
425 int native_set_startup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
426   SC_HANDLE service_handle = (SC_HANDLE) param;
427   if (! service_handle) return -1;
428
429   /* It makes no sense to try to reset the startup type. */
430   if (! value || ! value->string) {
431     print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);
432     return -1;
433   }
434
435   /* Map NSSM_STARTUP_* constant to Windows SERVICE_*_START constant. */
436   int service_startup = -1;
437   int i;
438   for (i = 0; startup_strings[i]; i++) {
439     if (str_equiv(value->string, startup_strings[i])) {
440       service_startup = i;
441       break;
442     }
443   }
444
445   if (service_startup < 0) {
446     print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE_STARTUP, value->string);
447     for (i = 0; startup_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), startup_strings[i]);
448     return -1;
449   }
450
451   unsigned long startup;
452   switch (service_startup) {
453     case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;
454     case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;
455     default: startup = SERVICE_AUTO_START;
456   }
457
458   if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, startup, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) {
459     print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
460     return -1;
461   }
462
463   SERVICE_DELAYED_AUTO_START_INFO delayed;
464   ZeroMemory(&delayed, sizeof(delayed));
465   if (service_startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;
466   else delayed.fDelayedAutostart = 0;
467   if (! ChangeServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {
468     unsigned long error = GetLastError();
469     /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
470     if (error != ERROR_INVALID_LEVEL) {
471       log_event(EVENTLOG_ERROR_TYPE, NSSM_MESSAGE_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service_name, error_string(error), 0);
472     }
473   }
474
475   return 1;
476 }
477
478 int native_get_startup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
479   SC_HANDLE service_handle = (SC_HANDLE) param;
480   if (! service_handle) return -1;
481
482   QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
483   if (! qsc) return -1;
484
485   unsigned long startup;
486   int ret = get_service_startup(service_name, service_handle, qsc, &startup);
487   HeapFree(GetProcessHeap(), 0, qsc);
488
489   if (ret) return -1;
490
491   unsigned long i;
492   for (i = 0; startup_strings[i]; i++);
493   if (startup >= i) return -1;
494
495   return value_from_string(name, value, startup_strings[startup]);
496 }
497
498 int native_set_type(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
499   SC_HANDLE service_handle = (SC_HANDLE) param;
500   if (! service_handle) return -1;
501
502   /* It makes no sense to try to reset the service type. */
503   if (! value || ! value->string) {
504     print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);
505     return -1;
506   }
507
508   /*
509     We can only manage services of type SERVICE_WIN32_OWN_PROCESS
510     and SERVICE_INTERACTIVE_PROCESS.
511   */
512   unsigned long type = SERVICE_WIN32_OWN_PROCESS;
513   if (str_equiv(value->string, NSSM_INTERACTIVE_PROCESS)) type |= SERVICE_INTERACTIVE_PROCESS;
514   else if (! str_equiv(value->string, NSSM_WIN32_OWN_PROCESS)) {
515     print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE_TYPE, value->string);
516     _ftprintf(stderr, _T("%s\n"), NSSM_WIN32_OWN_PROCESS);
517     _ftprintf(stderr, _T("%s\n"), NSSM_INTERACTIVE_PROCESS);
518     return -1;
519   }
520
521   /*
522     ChangeServiceConfig() will fail if the service runs under an account
523     other than LOCALSYSTEM and we try to make it interactive.
524   */
525   if (type & SERVICE_INTERACTIVE_PROCESS) {
526     QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
527     if (! qsc) return -1;
528
529     if (! str_equiv(qsc->lpServiceStartName, NSSM_LOCALSYSTEM_ACCOUNT)) {
530       HeapFree(GetProcessHeap(), 0, qsc);
531       print_message(stderr, NSSM_MESSAGE_INTERACTIVE_NOT_LOCALSYSTEM, value->string, service_name, NSSM_LOCALSYSTEM_ACCOUNT);
532       return -1;
533     }
534
535     HeapFree(GetProcessHeap(), 0, qsc);
536   }
537
538   if (! ChangeServiceConfig(service_handle, type, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) {
539     print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
540     return -1;
541   }
542
543   return 1;
544 }
545
546 int native_get_type(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
547   SC_HANDLE service_handle = (SC_HANDLE) param;
548   if (! service_handle) return -1;
549
550   QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
551   if (! qsc) return -1;
552
553   value->numeric = qsc->dwServiceType;
554   HeapFree(GetProcessHeap(), 0, qsc);
555
556   const TCHAR *string;
557   switch (value->numeric) {
558     case SERVICE_KERNEL_DRIVER: string = NSSM_KERNEL_DRIVER; break;
559     case SERVICE_FILE_SYSTEM_DRIVER: string = NSSM_FILE_SYSTEM_DRIVER; break;
560     case SERVICE_WIN32_OWN_PROCESS: string = NSSM_WIN32_OWN_PROCESS; break;
561     case SERVICE_WIN32_SHARE_PROCESS: string = NSSM_WIN32_SHARE_PROCESS; break;
562     case SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS: string = NSSM_INTERACTIVE_PROCESS; break;
563     case SERVICE_WIN32_SHARE_PROCESS|SERVICE_INTERACTIVE_PROCESS: string = NSSM_SHARE_INTERACTIVE_PROCESS; break;
564     default: string = NSSM_UNKNOWN;
565   }
566
567   return value_from_string(name, value, string);
568 }
569
570 int set_setting(const TCHAR *service_name, HKEY key, settings_t *setting, value_t *value, const TCHAR *additional) {
571   if (! key) return -1;
572   int ret;
573
574   if (setting->set) ret = setting->set(service_name, (void *) key, setting->name, setting->default_value, value, additional);
575   else ret = -1;
576
577   if (! ret) print_message(stdout, NSSM_MESSAGE_RESET_SETTING, setting->name, service_name);
578   else if (ret > 0) print_message(stdout, NSSM_MESSAGE_SET_SETTING, setting->name, service_name);
579   else print_message(stderr, NSSM_MESSAGE_SET_SETTING_FAILED, setting->name, service_name);
580
581   return ret;
582 }
583
584 int set_setting(const TCHAR *service_name, SC_HANDLE service_handle, settings_t *setting, value_t *value, const TCHAR *additional) {
585   if (! service_handle) return -1;
586
587   int ret;
588   if (setting->set) ret = setting->set(service_name, service_handle, setting->name, setting->default_value, value, additional);
589   else ret = -1;
590
591   if (! ret) print_message(stdout, NSSM_MESSAGE_RESET_SETTING, setting->name, service_name);
592   else if (ret > 0) print_message(stdout, NSSM_MESSAGE_SET_SETTING, setting->name, service_name);
593   else print_message(stderr, NSSM_MESSAGE_SET_SETTING_FAILED, setting->name, service_name);
594
595   return ret;
596 }
597
598 /*
599   Returns:  1 if the value was retrieved.
600             0 if the default value was retrieved.
601            -1 on error.
602 */
603 int get_setting(const TCHAR *service_name, HKEY key, settings_t *setting, value_t *value, const TCHAR *additional) {
604   if (! key) return -1;
605   int ret;
606
607   switch (setting->type) {
608     case REG_EXPAND_SZ:
609     case REG_MULTI_SZ:
610     case REG_SZ:
611       value->string = (TCHAR *) setting->default_value;
612       if (setting->get) ret = setting->get(service_name, (void *) key, setting->name, setting->default_value, value, additional);
613       else ret = -1;
614       break;
615
616     case REG_DWORD:
617       value->numeric = (unsigned long) setting->default_value;
618       if (setting->get) ret = setting->get(service_name, (void *) key, setting->name, setting->default_value, value, additional);
619       else ret = -1;
620       break;
621
622     default:
623       ret = -1;
624       break;
625   }
626
627   if (ret < 0) print_message(stderr, NSSM_MESSAGE_GET_SETTING_FAILED, setting->name, service_name);
628
629   return ret;
630 }
631
632 int get_setting(const TCHAR *service_name, SC_HANDLE service_handle, settings_t *setting, value_t *value, const TCHAR *additional) {
633   if (! service_handle) return -1;
634   return setting->get(service_name, service_handle, setting->name, 0, value, additional);
635 }
636
637 settings_t settings[] = {
638   { NSSM_REG_EXE, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },
639   { NSSM_REG_FLAGS, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },
640   { NSSM_REG_DIR, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },
641   { NSSM_REG_EXIT, REG_SZ, (void *) exit_action_strings[NSSM_EXIT_RESTART], false, ADDITIONAL_MANDATORY, setting_set_exit_action, setting_get_exit_action },
642   { NSSM_REG_ENV, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment },
643   { NSSM_REG_ENV_EXTRA, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment },
644   { NSSM_REG_STDIN, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },
645   { NSSM_REG_STDIN NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDIN_SHARING, false, 0, setting_set_number, setting_get_number },
646   { NSSM_REG_STDIN NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDIN_DISPOSITION, false, 0, setting_set_number, setting_get_number },
647   { NSSM_REG_STDIN NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDIN_FLAGS, false, 0, setting_set_number, setting_get_number },
648   { NSSM_REG_STDOUT, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },
649   { NSSM_REG_STDOUT NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDOUT_SHARING, false, 0, setting_set_number, setting_get_number },
650   { NSSM_REG_STDOUT NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDOUT_DISPOSITION, false, 0, setting_set_number, setting_get_number },
651   { NSSM_REG_STDOUT NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDOUT_FLAGS, false, 0, setting_set_number, setting_get_number },
652   { NSSM_REG_STDERR, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },
653   { NSSM_REG_STDERR NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDERR_SHARING, false, 0, setting_set_number, setting_get_number },
654   { NSSM_REG_STDERR NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDERR_DISPOSITION, false, 0, setting_set_number, setting_get_number },
655   { NSSM_REG_STDERR NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDERR_FLAGS, false, 0, setting_set_number, setting_get_number },
656   { NSSM_REG_STOP_METHOD_SKIP, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
657   { NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_CONSOLE_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },
658   { NSSM_REG_KILL_WINDOW_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_WINDOW_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },
659   { NSSM_REG_KILL_THREADS_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_THREADS_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },
660   { NSSM_REG_THROTTLE, REG_DWORD, (void *) NSSM_RESET_THROTTLE_RESTART, false, 0, setting_set_number, setting_get_number },
661   { NSSM_REG_ROTATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
662   { NSSM_REG_ROTATE_SECONDS, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
663   { NSSM_REG_ROTATE_BYTES_LOW, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
664   { NSSM_REG_ROTATE_BYTES_HIGH, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
665   { NSSM_NATIVE_DESCRIPTION, REG_SZ, _T(""), true, 0, native_set_description, native_get_description },
666   { NSSM_NATIVE_DISPLAYNAME, REG_SZ, NULL, true, 0, native_set_displayname, native_get_displayname },
667   { NSSM_NATIVE_IMAGEPATH, REG_EXPAND_SZ, NULL, true, 0, native_set_imagepath, native_get_imagepath },
668   { NSSM_NATIVE_OBJECTNAME, REG_SZ, NSSM_LOCALSYSTEM_ACCOUNT, true, ADDITIONAL_SETTING, native_set_objectname, native_get_objectname },
669   { NSSM_NATIVE_STARTUP, REG_SZ, NULL, true, 0, native_set_startup, native_get_startup },
670   { NSSM_NATIVE_TYPE, REG_SZ, NULL, true, 0, native_set_type, native_get_type },
671   { NULL, NULL, NULL, NULL, NULL }
672 };