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