Allow setting application priority.
[nssm.git] / registry.cpp
1 #include "nssm.h"\r
2 \r
3 extern const TCHAR *exit_action_strings[];\r
4 \r
5 int create_messages() {\r
6   HKEY key;\r
7 \r
8   TCHAR registry[KEY_LENGTH];\r
9   if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, _T("SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\%s"), NSSM) < 0) {\r
10     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("eventlog registry"), _T("create_messages()"), 0);\r
11     return 1;\r
12   }\r
13 \r
14   if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, KEY_WRITE, 0, &key, 0) != ERROR_SUCCESS) {\r
15     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);\r
16     return 2;\r
17   }\r
18 \r
19   /* Get path of this program */\r
20   TCHAR path[MAX_PATH];\r
21   GetModuleFileName(0, path, _countof(path));\r
22 \r
23   /* Try to register the module but don't worry so much on failure */\r
24   RegSetValueEx(key, _T("EventMessageFile"), 0, REG_SZ, (const unsigned char *) path, (unsigned long) (_tcslen(path) +  1) * sizeof(TCHAR));\r
25   unsigned long types = EVENTLOG_INFORMATION_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_ERROR_TYPE;\r
26   RegSetValueEx(key, _T("TypesSupported"), 0, REG_DWORD, (const unsigned char *) &types, sizeof(types));\r
27 \r
28   return 0;\r
29 }\r
30 \r
31 int create_parameters(nssm_service_t *service, bool editing) {\r
32   /* Try to open the registry */\r
33   HKEY key = open_registry(service->name, KEY_WRITE);\r
34   if (! key) return 1;\r
35 \r
36   /* Try to create the parameters */\r
37   if (set_expand_string(key, NSSM_REG_EXE, service->exe)) {\r
38     RegDeleteKey(HKEY_LOCAL_MACHINE, NSSM_REGISTRY);\r
39     RegCloseKey(key);\r
40     return 2;\r
41   }\r
42   if (set_expand_string(key, NSSM_REG_FLAGS, service->flags)) {\r
43     RegDeleteKey(HKEY_LOCAL_MACHINE, NSSM_REGISTRY);\r
44     RegCloseKey(key);\r
45     return 3;\r
46   }\r
47   if (set_expand_string(key, NSSM_REG_DIR, service->dir)) {\r
48     RegDeleteKey(HKEY_LOCAL_MACHINE, NSSM_REGISTRY);\r
49     RegCloseKey(key);\r
50     return 4;\r
51   }\r
52 \r
53   /* Other non-default parameters. May fail. */\r
54   if (service->priority != NORMAL_PRIORITY_CLASS) set_number(key, NSSM_REG_PRIORITY, service->priority);\r
55   else if (editing) RegDeleteValue(key, NSSM_REG_PRIORITY);\r
56   unsigned long stop_method_skip = ~service->stop_method;\r
57   if (stop_method_skip) set_number(key, NSSM_REG_STOP_METHOD_SKIP, stop_method_skip);\r
58   else if (editing) RegDeleteValue(key, NSSM_REG_STOP_METHOD_SKIP);\r
59   if (service->default_exit_action < NSSM_NUM_EXIT_ACTIONS) create_exit_action(service->name, exit_action_strings[service->default_exit_action], editing);\r
60   if (service->throttle_delay != NSSM_RESET_THROTTLE_RESTART) set_number(key, NSSM_REG_THROTTLE, service->throttle_delay);\r
61   else if (editing) RegDeleteValue(key, NSSM_REG_THROTTLE);\r
62   if (service->kill_console_delay != NSSM_KILL_CONSOLE_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, service->kill_console_delay);\r
63   else if (editing) RegDeleteValue(key, NSSM_REG_KILL_CONSOLE_GRACE_PERIOD);\r
64   if (service->kill_window_delay != NSSM_KILL_WINDOW_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_WINDOW_GRACE_PERIOD, service->kill_window_delay);\r
65   else if (editing) RegDeleteValue(key, NSSM_REG_KILL_WINDOW_GRACE_PERIOD);\r
66   if (service->kill_threads_delay != NSSM_KILL_THREADS_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_THREADS_GRACE_PERIOD, service->kill_threads_delay);\r
67   else if (editing) RegDeleteValue(key, NSSM_REG_KILL_THREADS_GRACE_PERIOD);\r
68   if (service->stdin_path[0] || editing) {\r
69     if (service->stdin_path[0]) set_expand_string(key, NSSM_REG_STDIN, service->stdin_path);\r
70     else if (editing) RegDeleteValue(key, NSSM_REG_STDIN);\r
71     if (service->stdin_sharing != NSSM_STDIN_SHARING) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_SHARING, service->stdin_sharing);\r
72     else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_SHARING);\r
73     if (service->stdin_disposition != NSSM_STDIN_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_DISPOSITION, service->stdin_disposition);\r
74     else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_DISPOSITION);\r
75     if (service->stdin_flags != NSSM_STDIN_FLAGS) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_FLAGS, service->stdin_flags);\r
76     else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_FLAGS);\r
77   }\r
78   if (service->stdout_path[0] || editing) {\r
79     if (service->stdout_path[0]) set_expand_string(key, NSSM_REG_STDOUT, service->stdout_path);\r
80     else if (editing) RegDeleteValue(key, NSSM_REG_STDOUT);\r
81     if (service->stdout_sharing != NSSM_STDOUT_SHARING) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_SHARING, service->stdout_sharing);\r
82     else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_SHARING);\r
83     if (service->stdout_disposition != NSSM_STDOUT_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_DISPOSITION, service->stdout_disposition);\r
84     else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_DISPOSITION);\r
85     if (service->stdout_flags != NSSM_STDOUT_FLAGS) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_FLAGS, service->stdout_flags);\r
86     else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_FLAGS);\r
87   }\r
88   if (service->stderr_path[0] || editing) {\r
89     if (service->stderr_path[0]) set_expand_string(key, NSSM_REG_STDERR, service->stderr_path);\r
90     else if (editing) RegDeleteValue(key, NSSM_REG_STDERR);\r
91     if (service->stderr_sharing != NSSM_STDERR_SHARING) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_SHARING, service->stderr_sharing);\r
92     else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_SHARING);\r
93     if (service->stderr_disposition != NSSM_STDERR_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_DISPOSITION, service->stderr_disposition);\r
94     else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_DISPOSITION);\r
95     if (service->stderr_flags != NSSM_STDERR_FLAGS) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_FLAGS, service->stderr_flags);\r
96     else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_FLAGS);\r
97   }\r
98   if (service->rotate_files) set_number(key, NSSM_REG_ROTATE, 1);\r
99   else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE);\r
100   if (service->rotate_seconds) set_number(key, NSSM_REG_ROTATE_SECONDS, service->rotate_seconds);\r
101   else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_SECONDS);\r
102   if (service->rotate_bytes_low) set_number(key, NSSM_REG_ROTATE_BYTES_LOW, service->rotate_bytes_low);\r
103   else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_BYTES_LOW);\r
104   if (service->rotate_bytes_high) set_number(key, NSSM_REG_ROTATE_BYTES_HIGH, service->rotate_bytes_high);\r
105   else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_BYTES_HIGH);\r
106 \r
107   /* Environment */\r
108   if (service->env) {\r
109     if (RegSetValueEx(key, NSSM_REG_ENV, 0, REG_MULTI_SZ, (const unsigned char *) service->env, (unsigned long) service->envlen * sizeof(TCHAR)) != ERROR_SUCCESS) {\r
110       log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV, error_string(GetLastError()), 0);\r
111     }\r
112   }\r
113   else if (editing) RegDeleteValue(key, NSSM_REG_ENV);\r
114   if (service->env_extra) {\r
115     if (RegSetValueEx(key, NSSM_REG_ENV_EXTRA, 0, REG_MULTI_SZ, (const unsigned char *) service->env_extra, (unsigned long) service->env_extralen * sizeof(TCHAR)) != ERROR_SUCCESS) {\r
116       log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV_EXTRA, error_string(GetLastError()), 0);\r
117     }\r
118   }\r
119   else if (editing) RegDeleteValue(key, NSSM_REG_ENV_EXTRA);\r
120 \r
121   /* Close registry. */\r
122   RegCloseKey(key);\r
123 \r
124   return 0;\r
125 }\r
126 \r
127 int create_exit_action(TCHAR *service_name, const TCHAR *action_string, bool editing) {\r
128   /* Get registry */\r
129   TCHAR registry[KEY_LENGTH];\r
130   if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, NSSM_REGISTRY _T("\\%s"), service_name, NSSM_REG_EXIT) < 0) {\r
131     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("NSSM_REG_EXIT"), _T("create_exit_action()"), 0);\r
132     return 1;\r
133   }\r
134 \r
135   /* Try to open the registry */\r
136   HKEY key;\r
137   unsigned long disposition;\r
138   if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, KEY_WRITE, 0, &key, &disposition) != ERROR_SUCCESS) {\r
139     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);\r
140     return 2;\r
141   }\r
142 \r
143   /* Do nothing if the key already existed */\r
144   if (disposition == REG_OPENED_EXISTING_KEY && ! editing) {\r
145     RegCloseKey(key);\r
146     return 0;\r
147   }\r
148 \r
149   /* Create the default value */\r
150   if (RegSetValueEx(key, 0, 0, REG_SZ, (const unsigned char *) action_string, (unsigned long) (_tcslen(action_string) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) {\r
151     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_EXIT, error_string(GetLastError()), 0);\r
152     RegCloseKey(key);\r
153     return 3;\r
154   }\r
155 \r
156   /* Close registry */\r
157   RegCloseKey(key);\r
158 \r
159   return 0;\r
160 }\r
161 \r
162 int set_environment(TCHAR *service_name, HKEY key, TCHAR *value, TCHAR **env, unsigned long *envlen) {\r
163   unsigned long type = REG_MULTI_SZ;\r
164 \r
165   /* Dummy test to find buffer size */\r
166   unsigned long ret = RegQueryValueEx(key, value, 0, &type, NULL, envlen);\r
167   if (ret != ERROR_SUCCESS) {\r
168     *env = 0;\r
169     *envlen = 0;\r
170     /* The service probably doesn't have any environment configured */\r
171     if (ret == ERROR_FILE_NOT_FOUND) return 0;\r
172     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);\r
173     return 1;\r
174   }\r
175 \r
176   if (type != REG_MULTI_SZ) {\r
177     *env = 0;\r
178     *envlen = 0;\r
179     log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_INVALID_ENVIRONMENT_STRING_TYPE, value, service_name, 0);\r
180     return 2;\r
181   }\r
182 \r
183   /* Probably not possible */\r
184   if (! *envlen) return 0;\r
185 \r
186   /* Previously initialised? */\r
187   if (*env) HeapFree(GetProcessHeap(), 0, *env);\r
188 \r
189   *env = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, *envlen);\r
190   if (! *env) {\r
191     *envlen = 0;\r
192     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, value, _T("set_environment()"), 0);\r
193     return 3;\r
194   }\r
195 \r
196   /* Actually get the strings */\r
197   ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) *env, envlen);\r
198   if (ret != ERROR_SUCCESS) {\r
199     HeapFree(GetProcessHeap(), 0, *env);\r
200     *env = 0;\r
201     *envlen = 0;\r
202     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);\r
203     return 4;\r
204   }\r
205 \r
206   return 0;\r
207 }\r
208 \r
209 /* Replace NULL with CRLF. Leave NULL NULL as the end marker. */\r
210 int format_environment(TCHAR *env, unsigned long envlen, TCHAR **formatted, unsigned long *newlen) {\r
211   unsigned long i, j;\r
212   *newlen = envlen;\r
213 \r
214   if (! *newlen) {\r
215     *formatted = 0;\r
216     return 0;\r
217   }\r
218 \r
219   for (i = 0; i < envlen; i++) if (! env[i] && env[i + 1]) ++*newlen;\r
220 \r
221   *formatted = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *newlen * sizeof(TCHAR));\r
222   if (! *formatted) {\r
223     *newlen = 0;\r
224     return 1;\r
225   }\r
226 \r
227   for (i = 0, j = 0; i < envlen; i++) {\r
228     (*formatted)[j] = env[i];\r
229     if (! env[i]) {\r
230       if (env[i + 1]) {\r
231         (*formatted)[j] = _T('\r');\r
232         (*formatted)[++j] = _T('\n');\r
233       }\r
234     }\r
235     j++;\r
236   }\r
237 \r
238   return 0;\r
239 }\r
240 \r
241 /* Strip CR and replace LF with NULL. */\r
242 int unformat_environment(TCHAR *env, unsigned long envlen, TCHAR **unformatted, unsigned long *newlen) {\r
243   unsigned long i, j;\r
244   *newlen = 0;\r
245 \r
246   if (! envlen) {\r
247     *unformatted = 0;\r
248     return 0;\r
249   }\r
250 \r
251   for (i = 0; i < envlen; i++) if (env[i] != _T('\r')) ++*newlen;\r
252   /* Must end with two NULLs. */\r
253   *newlen += 2;\r
254 \r
255   *unformatted = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *newlen * sizeof(TCHAR));\r
256   if (! *unformatted) return 1;\r
257 \r
258   for (i = 0, j = 0; i < envlen; i++) {\r
259     if (env[i] == _T('\r')) continue;\r
260     if (env[i] == _T('\n')) (*unformatted)[j] = _T('\0');\r
261     else (*unformatted)[j] = env[i];\r
262     j++;\r
263   }\r
264 \r
265   return 0;\r
266 }\r
267 \r
268 int expand_parameter(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise, bool must_exist) {\r
269   TCHAR *buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, datalen);\r
270   if (! buffer) {\r
271     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, value, _T("expand_parameter()"), 0);\r
272     return 1;\r
273   }\r
274 \r
275   ZeroMemory(data, datalen);\r
276 \r
277   unsigned long type = REG_EXPAND_SZ;\r
278   unsigned long buflen = datalen;\r
279 \r
280   unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);\r
281   if (ret != ERROR_SUCCESS) {\r
282     unsigned long error = GetLastError();\r
283     HeapFree(GetProcessHeap(), 0, buffer);\r
284 \r
285     if (ret == ERROR_FILE_NOT_FOUND) {\r
286       if (! must_exist) return 0;\r
287     }\r
288 \r
289     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(error), 0);\r
290     return 2;\r
291   }\r
292 \r
293   /* Paths aren't allowed to contain quotes. */\r
294   if (sanitise) PathUnquoteSpaces(buffer);\r
295 \r
296   /* Technically we shouldn't expand environment strings from REG_SZ values */\r
297   if (type != REG_EXPAND_SZ) {\r
298     memmove(data, buffer, buflen);\r
299     HeapFree(GetProcessHeap(), 0, buffer);\r
300     return 0;\r
301   }\r
302 \r
303   ret = ExpandEnvironmentStrings((TCHAR *) buffer, data, datalen);\r
304   if (! ret || ret > datalen) {\r
305     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_EXPANDENVIRONMENTSTRINGS_FAILED, buffer, error_string(GetLastError()), 0);\r
306     HeapFree(GetProcessHeap(), 0, buffer);\r
307     return 3;\r
308   }\r
309 \r
310   HeapFree(GetProcessHeap(), 0, buffer);\r
311   return 0;\r
312 }\r
313 \r
314 int expand_parameter(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise) {\r
315   return expand_parameter(key, value, data, datalen, sanitise, true);\r
316 }\r
317 \r
318 /*\r
319   Sets a string in the registry.\r
320   Returns: 0 if it was set.\r
321            1 on error.\r
322 */\r
323 int set_expand_string(HKEY key, TCHAR *value, TCHAR *string) {\r
324   if (RegSetValueEx(key, value, 0, REG_EXPAND_SZ, (const unsigned char *) string, (unsigned long) (_tcslen(string) + 1) * sizeof(TCHAR)) == ERROR_SUCCESS) return 0;\r
325   log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, value, error_string(GetLastError()), 0);\r
326   return 1;\r
327 }\r
328 \r
329 /*\r
330   Set an unsigned long in the registry.\r
331   Returns: 0 if it was set.\r
332            1 on error.\r
333 */\r
334 int set_number(HKEY key, TCHAR *value, unsigned long number) {\r
335   if (RegSetValueEx(key, value, 0, REG_DWORD, (const unsigned char *) &number, sizeof(number)) == ERROR_SUCCESS) return 0;\r
336   log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, value, error_string(GetLastError()), 0);\r
337   return 1;\r
338 }\r
339 \r
340 /*\r
341   Query an unsigned long from the registry.\r
342   Returns:  1 if a number was retrieved.\r
343             0 if none was found and must_exist is false.\r
344            -1 if none was found and must_exist is true.\r
345            -2 otherwise.\r
346 */\r
347 int get_number(HKEY key, TCHAR *value, unsigned long *number, bool must_exist) {\r
348   unsigned long type = REG_DWORD;\r
349   unsigned long number_len = sizeof(unsigned long);\r
350 \r
351   int ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) number, &number_len);\r
352   if (ret == ERROR_SUCCESS) return 1;\r
353 \r
354   if (ret == ERROR_FILE_NOT_FOUND) {\r
355     if (! must_exist) return 0;\r
356   }\r
357 \r
358   log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);\r
359   if (ret == ERROR_FILE_NOT_FOUND) return -1;\r
360 \r
361   return -2;\r
362 }\r
363 \r
364 int get_number(HKEY key, TCHAR *value, unsigned long *number) {\r
365   return get_number(key, value, number, true);\r
366 }\r
367 \r
368 void override_milliseconds(TCHAR *service_name, HKEY key, TCHAR *value, unsigned long *buffer, unsigned long default_value, unsigned long event) {\r
369   unsigned long type = REG_DWORD;\r
370   unsigned long buflen = sizeof(unsigned long);\r
371   bool ok = false;\r
372   unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);\r
373   if (ret != ERROR_SUCCESS) {\r
374     if (ret != ERROR_FILE_NOT_FOUND) {\r
375       if (type != REG_DWORD) {\r
376         TCHAR milliseconds[16];\r
377         _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), default_value);\r
378         log_event(EVENTLOG_WARNING_TYPE, event, service_name, value, milliseconds, 0);\r
379       }\r
380       else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);\r
381     }\r
382   }\r
383   else ok = true;\r
384 \r
385   if (! ok) *buffer = default_value;\r
386 }\r
387 \r
388 HKEY open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam) {\r
389   /* Get registry */\r
390   TCHAR registry[KEY_LENGTH];\r
391   HKEY key;\r
392   int ret;\r
393 \r
394   if (sub) ret = _sntprintf_s(registry, _countof(registry), _TRUNCATE, NSSM_REGISTRY _T("\\%s"), service_name, sub);\r
395   else ret = _sntprintf_s(registry, _countof(registry), _TRUNCATE, NSSM_REGISTRY, service_name);\r
396   if (ret < 0) {\r
397     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("NSSM_REGISTRY"), _T("open_registry()"), 0);\r
398     return 0;\r
399   }\r
400 \r
401   if (sam & KEY_WRITE) {\r
402     if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, sam, 0, &key, 0) != ERROR_SUCCESS) {\r
403       log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);\r
404       return 0;\r
405     }\r
406   }\r
407   else {\r
408     if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, registry, 0, sam, &key) != ERROR_SUCCESS) {\r
409       log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);\r
410       return 0;\r
411     }\r
412   }\r
413 \r
414   return key;\r
415 }\r
416 \r
417 HKEY open_registry(const TCHAR *service_name, REGSAM sam) {\r
418   return open_registry(service_name, 0, sam);\r
419 }\r
420 \r
421 int get_parameters(nssm_service_t *service, STARTUPINFO *si) {\r
422   unsigned long ret;\r
423 \r
424   /* Try to open the registry */\r
425   HKEY key = open_registry(service->name, KEY_READ);\r
426   if (! key) return 1;\r
427 \r
428   /* Try to get executable file - MUST succeed */\r
429   if (expand_parameter(key, NSSM_REG_EXE, service->exe, sizeof(service->exe), false)) {\r
430     RegCloseKey(key);\r
431     return 3;\r
432   }\r
433 \r
434   /* Try to get flags - may fail and we don't care */\r
435   if (expand_parameter(key, NSSM_REG_FLAGS, service->flags, sizeof(service->flags), false)) {\r
436     log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_FLAGS, NSSM_REG_FLAGS, service->name, service->exe, 0);\r
437     ZeroMemory(service->flags, sizeof(service->flags));\r
438   }\r
439 \r
440   /* Try to get startup directory - may fail and we fall back to a default */\r
441   if (expand_parameter(key, NSSM_REG_DIR, service->dir, sizeof(service->dir), true) || ! service->dir[0]) {\r
442     _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);\r
443     strip_basename(service->dir);\r
444     if (service->dir[0] == _T('\0')) {\r
445       /* Help! */\r
446       ret = GetWindowsDirectory(service->dir, sizeof(service->dir));\r
447       if (! ret || ret > sizeof(service->dir)) {\r
448         log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_NO_DIR_AND_NO_FALLBACK, NSSM_REG_DIR, service->name, 0);\r
449         RegCloseKey(key);\r
450         return 4;\r
451       }\r
452     }\r
453     log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_DIR, NSSM_REG_DIR, service->name, service->dir, 0);\r
454   }\r
455 \r
456   /* Try to get environment variables - may fail */\r
457   set_environment(service->name, key, NSSM_REG_ENV, &service->env, &service->envlen);\r
458   /* Environment variables to add to existing rather than replace - may fail. */\r
459   set_environment(service->name, key, NSSM_REG_ENV_EXTRA, &service->env_extra, &service->env_extralen);\r
460 \r
461   if (si) {\r
462     if (service->env_extra) {\r
463       /* Append these to any other environment variables set. */\r
464       if (service->env) {\r
465         /* Append extra variables to configured variables. */\r
466         unsigned long envlen = service->envlen + service->env_extralen - 1;\r
467         TCHAR *env = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, envlen);\r
468         if (env) {\r
469           memmove(env, service->env, service->envlen - sizeof(TCHAR));\r
470           /* envlen is in bytes. */\r
471           memmove(env + (service->envlen / sizeof(TCHAR)) - 1, service->env_extra, service->env_extralen);\r
472 \r
473           HeapFree(GetProcessHeap(), 0, service->env);\r
474           service->env = env;\r
475           service->envlen = envlen;\r
476         }\r
477         else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("environment"), _T("get_parameters()"), 0);\r
478       }\r
479       else {\r
480         /* Append extra variables to our environment. */\r
481         TCHAR *env, *s;\r
482         size_t envlen, len;\r
483 \r
484         env = service->env_extra;\r
485         len = 0;\r
486         while (*env) {\r
487           envlen = _tcslen(env) + 1;\r
488           for (s = env; *s && *s != _T('='); s++);\r
489           if (*s == _T('=')) *s++ = _T('\0');\r
490           if (! SetEnvironmentVariable(env, s)) log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_SETENVIRONMENTVARIABLE_FAILED, env, s, error_string(GetLastError()), 0);\r
491           env += envlen;\r
492         }\r
493       }\r
494     }\r
495   }\r
496 \r
497   /* Try to get priority - may fail. */\r
498   unsigned long priority;\r
499   if (get_number(key, NSSM_REG_PRIORITY, &priority) == 1) {\r
500     if (priority == (priority & priority_mask())) service->priority = priority;\r
501     else log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_PRIORITY, service->name, NSSM_REG_PRIORITY, 0);\r
502   }\r
503 \r
504   /* Try to get file rotation settings - may fail. */\r
505   unsigned long rotate_files;\r
506   if (get_number(key, NSSM_REG_ROTATE, &rotate_files, false) == 1) {\r
507     if (rotate_files) service->rotate_files = true;\r
508     else service->rotate_files = false;\r
509   }\r
510   else service->rotate_files = false;\r
511   if (get_number(key, NSSM_REG_ROTATE_SECONDS, &service->rotate_seconds, false) != 1) service->rotate_seconds = 0;\r
512   if (get_number(key, NSSM_REG_ROTATE_BYTES_LOW, &service->rotate_bytes_low, false) != 1) service->rotate_bytes_low = 0;\r
513   if (get_number(key, NSSM_REG_ROTATE_BYTES_HIGH, &service->rotate_bytes_high, false) != 1) service->rotate_bytes_high = 0;\r
514 \r
515   /* Change to startup directory in case stdout/stderr are relative paths. */\r
516   TCHAR cwd[MAX_PATH];\r
517   GetCurrentDirectory(_countof(cwd), cwd);\r
518   SetCurrentDirectory(service->dir);\r
519 \r
520   /* Try to get stdout and stderr */\r
521   if (get_output_handles(service, key, si)) {\r
522     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);\r
523     RegCloseKey(key);\r
524     SetCurrentDirectory(cwd);\r
525     return 5;\r
526   }\r
527 \r
528   /* Change back in case the startup directory needs to be deleted. */\r
529   SetCurrentDirectory(cwd);\r
530 \r
531   /* Try to get throttle restart delay */\r
532   override_milliseconds(service->name, key, NSSM_REG_THROTTLE, &service->throttle_delay, NSSM_RESET_THROTTLE_RESTART, NSSM_EVENT_BOGUS_THROTTLE);\r
533 \r
534   /* Try to get service stop flags. */\r
535   unsigned long type = REG_DWORD;\r
536   unsigned long stop_method_skip;\r
537   unsigned long buflen = sizeof(stop_method_skip);\r
538   bool stop_ok = false;\r
539   ret = RegQueryValueEx(key, NSSM_REG_STOP_METHOD_SKIP, 0, &type, (unsigned char *) &stop_method_skip, &buflen);\r
540   if (ret != ERROR_SUCCESS) {\r
541     if (ret != ERROR_FILE_NOT_FOUND) {\r
542       if (type != REG_DWORD) {\r
543         log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_STOP_METHOD_SKIP, service->name, NSSM_REG_STOP_METHOD_SKIP, NSSM, 0);\r
544       }\r
545       else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, NSSM_REG_STOP_METHOD_SKIP, error_string(GetLastError()), 0);\r
546     }\r
547   }\r
548   else stop_ok = true;\r
549 \r
550   /* Try all methods except those requested to be skipped. */\r
551   service->stop_method = ~0;\r
552   if (stop_ok) service->stop_method &= ~stop_method_skip;\r
553 \r
554   /* Try to get kill delays - may fail. */\r
555   override_milliseconds(service->name, key, NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, &service->kill_console_delay, NSSM_KILL_CONSOLE_GRACE_PERIOD, NSSM_EVENT_BOGUS_KILL_CONSOLE_GRACE_PERIOD);\r
556   override_milliseconds(service->name, key, NSSM_REG_KILL_WINDOW_GRACE_PERIOD, &service->kill_window_delay, NSSM_KILL_WINDOW_GRACE_PERIOD, NSSM_EVENT_BOGUS_KILL_WINDOW_GRACE_PERIOD);\r
557   override_milliseconds(service->name, key, NSSM_REG_KILL_THREADS_GRACE_PERIOD, &service->kill_threads_delay, NSSM_KILL_THREADS_GRACE_PERIOD, NSSM_EVENT_BOGUS_KILL_THREADS_GRACE_PERIOD);\r
558 \r
559   /* Try to get default exit action. */\r
560   bool default_action;\r
561   service->default_exit_action = NSSM_EXIT_RESTART;\r
562   TCHAR action_string[ACTION_LEN];\r
563   if (! get_exit_action(service->name, 0, action_string, &default_action)) {\r
564     for (int i = 0; exit_action_strings[i]; i++) {\r
565       if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {\r
566         service->default_exit_action = i;\r
567         break;\r
568       }\r
569     }\r
570   }\r
571 \r
572   /* Close registry */\r
573   RegCloseKey(key);\r
574 \r
575   return 0;\r
576 }\r
577 \r
578 /*\r
579   Sets the string for the exit action corresponding to the exit code.\r
580 \r
581   ret is a pointer to an unsigned long containing the exit code.\r
582   If ret is NULL, we retrieve the default exit action unconditionally.\r
583 \r
584   action is a buffer which receives the string.\r
585 \r
586   default_action is a pointer to a bool which is set to false if there\r
587   was an explicit string for the given exit code, or true if we are\r
588   returning the default action.\r
589 \r
590   Returns: 0 on success.\r
591            1 on error.\r
592 */\r
593 int get_exit_action(const TCHAR *service_name, unsigned long *ret, TCHAR *action, bool *default_action) {\r
594   /* Are we returning the default action or a status-specific one? */\r
595   *default_action = ! ret;\r
596 \r
597   /* Try to open the registry */\r
598   HKEY key = open_registry(service_name, NSSM_REG_EXIT, KEY_READ);\r
599   if (! key) return 1;\r
600 \r
601   unsigned long type = REG_SZ;\r
602   unsigned long action_len = ACTION_LEN;\r
603 \r
604   TCHAR code[16];\r
605   if (! ret) code[0] = _T('\0');\r
606   else if (_sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), *ret) < 0) {\r
607     RegCloseKey(key);\r
608     return get_exit_action(service_name, 0, action, default_action);\r
609   }\r
610   if (RegQueryValueEx(key, code, 0, &type, (unsigned char *) action, &action_len) != ERROR_SUCCESS) {\r
611     RegCloseKey(key);\r
612     /* Try again with * as the key if an exit code was defined */\r
613     if (ret) return get_exit_action(service_name, 0, action, default_action);\r
614     return 0;\r
615   }\r
616 \r
617   /* Close registry */\r
618   RegCloseKey(key);\r
619 \r
620   return 0;\r
621 }\r