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