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