Allocate new console by default.
[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_parameters(nssm_service_t *service, STARTUPINFO *si) {\r
406   unsigned long ret;\r
407 \r
408   /* Try to open the registry */\r
409   HKEY key = open_registry(service->name, KEY_READ);\r
410   if (! key) return 1;\r
411 \r
412   /* Try to get executable file - MUST succeed */\r
413   if (expand_parameter(key, NSSM_REG_EXE, service->exe, sizeof(service->exe), false)) {\r
414     RegCloseKey(key);\r
415     return 3;\r
416   }\r
417 \r
418   /* Try to get flags - may fail and we don't care */\r
419   if (expand_parameter(key, NSSM_REG_FLAGS, service->flags, sizeof(service->flags), false)) {\r
420     log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_FLAGS, NSSM_REG_FLAGS, service->name, service->exe, 0);\r
421     ZeroMemory(service->flags, sizeof(service->flags));\r
422   }\r
423 \r
424   /* Try to get startup directory - may fail and we fall back to a default */\r
425   if (expand_parameter(key, NSSM_REG_DIR, service->dir, sizeof(service->dir), true) || ! service->dir[0]) {\r
426     _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);\r
427     strip_basename(service->dir);\r
428     if (service->dir[0] == _T('\0')) {\r
429       /* Help! */\r
430       ret = GetWindowsDirectory(service->dir, sizeof(service->dir));\r
431       if (! ret || ret > sizeof(service->dir)) {\r
432         log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_NO_DIR_AND_NO_FALLBACK, NSSM_REG_DIR, service->name, 0);\r
433         RegCloseKey(key);\r
434         return 4;\r
435       }\r
436     }\r
437     log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_DIR, NSSM_REG_DIR, service->name, service->dir, 0);\r
438   }\r
439 \r
440   /* Try to get processor affinity - may fail. */\r
441   TCHAR buffer[512];\r
442   if (expand_parameter(key, NSSM_REG_AFFINITY, buffer, sizeof(buffer), false, false) || ! buffer[0]) service->affinity = 0LL;\r
443   else if (affinity_string_to_mask(buffer, &service->affinity)) {\r
444     log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_AFFINITY_MASK, service->name, buffer);\r
445     service->affinity = 0LL;\r
446   }\r
447   else {\r
448     DWORD_PTR affinity, system_affinity;\r
449 \r
450     if (GetProcessAffinityMask(GetCurrentProcess(), &affinity, &system_affinity)) {\r
451       _int64 effective_affinity = service->affinity & system_affinity;\r
452       if (effective_affinity != service->affinity) {\r
453         TCHAR *system = 0;\r
454         if (! affinity_mask_to_string(system_affinity, &system)) {\r
455           TCHAR *effective = 0;\r
456           if (! affinity_mask_to_string(effective_affinity, &effective)) {\r
457             log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_EFFECTIVE_AFFINITY_MASK, service->name, buffer, system, effective, 0);\r
458           }\r
459           HeapFree(GetProcessHeap(), 0, effective);\r
460         }\r
461         HeapFree(GetProcessHeap(), 0, system);\r
462       }\r
463     }\r
464   }\r
465 \r
466   /* Try to get environment variables - may fail */\r
467   get_environment(service->name, key, NSSM_REG_ENV, &service->env, &service->envlen);\r
468   /* Environment variables to add to existing rather than replace - may fail. */\r
469   get_environment(service->name, key, NSSM_REG_ENV_EXTRA, &service->env_extra, &service->env_extralen);\r
470 \r
471   /* Try to get priority - may fail. */\r
472   unsigned long priority;\r
473   if (get_number(key, NSSM_REG_PRIORITY, &priority, false) == 1) {\r
474     if (priority == (priority & priority_mask())) service->priority = priority;\r
475     else log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_PRIORITY, service->name, NSSM_REG_PRIORITY, 0);\r
476   }\r
477 \r
478   /* Try to get file rotation settings - may fail. */\r
479   unsigned long rotate_files;\r
480   if (get_number(key, NSSM_REG_ROTATE, &rotate_files, false) == 1) {\r
481     if (rotate_files) service->rotate_files = true;\r
482     else service->rotate_files = false;\r
483   }\r
484   else service->rotate_files = false;\r
485   if (get_number(key, NSSM_REG_ROTATE_ONLINE, &rotate_files, false) == 1) {\r
486     if (rotate_files) service->rotate_stdout_online = service->rotate_stderr_online = true;\r
487     else service->rotate_stdout_online = service->rotate_stderr_online = false;\r
488   }\r
489   else service->rotate_stdout_online = service->rotate_stderr_online = false;\r
490   if (get_number(key, NSSM_REG_ROTATE_SECONDS, &service->rotate_seconds, false) != 1) service->rotate_seconds = 0;\r
491   if (get_number(key, NSSM_REG_ROTATE_BYTES_LOW, &service->rotate_bytes_low, false) != 1) service->rotate_bytes_low = 0;\r
492   if (get_number(key, NSSM_REG_ROTATE_BYTES_HIGH, &service->rotate_bytes_high, false) != 1) service->rotate_bytes_high = 0;\r
493 \r
494   /* Try to get force new console setting - may fail. */\r
495   if (get_number(key, NSSM_REG_NO_CONSOLE, &service->no_console, false) != 1) service->no_console = 0;\r
496 \r
497   /* Change to startup directory in case stdout/stderr are relative paths. */\r
498   TCHAR cwd[PATH_LENGTH];\r
499   GetCurrentDirectory(_countof(cwd), cwd);\r
500   SetCurrentDirectory(service->dir);\r
501 \r
502   /* Try to get stdout and stderr */\r
503   if (get_output_handles(service, key, si)) {\r
504     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);\r
505     RegCloseKey(key);\r
506     SetCurrentDirectory(cwd);\r
507     return 5;\r
508   }\r
509 \r
510   /* Change back in case the startup directory needs to be deleted. */\r
511   SetCurrentDirectory(cwd);\r
512 \r
513   /* Try to get mandatory restart delay */\r
514   override_milliseconds(service->name, key, NSSM_REG_RESTART_DELAY, &service->restart_delay, 0, NSSM_EVENT_BOGUS_RESTART_DELAY);\r
515 \r
516   /* Try to get throttle restart delay */\r
517   override_milliseconds(service->name, key, NSSM_REG_THROTTLE, &service->throttle_delay, NSSM_RESET_THROTTLE_RESTART, NSSM_EVENT_BOGUS_THROTTLE);\r
518 \r
519   /* Try to get service stop flags. */\r
520   unsigned long type = REG_DWORD;\r
521   unsigned long stop_method_skip;\r
522   unsigned long buflen = sizeof(stop_method_skip);\r
523   bool stop_ok = false;\r
524   ret = RegQueryValueEx(key, NSSM_REG_STOP_METHOD_SKIP, 0, &type, (unsigned char *) &stop_method_skip, &buflen);\r
525   if (ret != ERROR_SUCCESS) {\r
526     if (ret != ERROR_FILE_NOT_FOUND) {\r
527       if (type != REG_DWORD) {\r
528         log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_STOP_METHOD_SKIP, service->name, NSSM_REG_STOP_METHOD_SKIP, NSSM, 0);\r
529       }\r
530       else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, NSSM_REG_STOP_METHOD_SKIP, error_string(GetLastError()), 0);\r
531     }\r
532   }\r
533   else stop_ok = true;\r
534 \r
535   /* Try all methods except those requested to be skipped. */\r
536   service->stop_method = ~0;\r
537   if (stop_ok) service->stop_method &= ~stop_method_skip;\r
538 \r
539   /* Try to get kill delays - may fail. */\r
540   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
541   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
542   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
543 \r
544   /* Try to get default exit action. */\r
545   bool default_action;\r
546   service->default_exit_action = NSSM_EXIT_RESTART;\r
547   TCHAR action_string[ACTION_LEN];\r
548   if (! get_exit_action(service->name, 0, action_string, &default_action)) {\r
549     for (int i = 0; exit_action_strings[i]; i++) {\r
550       if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {\r
551         service->default_exit_action = i;\r
552         break;\r
553       }\r
554     }\r
555   }\r
556 \r
557   /* Close registry */\r
558   RegCloseKey(key);\r
559 \r
560   return 0;\r
561 }\r
562 \r
563 /*\r
564   Sets the string for the exit action corresponding to the exit code.\r
565 \r
566   ret is a pointer to an unsigned long containing the exit code.\r
567   If ret is NULL, we retrieve the default exit action unconditionally.\r
568 \r
569   action is a buffer which receives the string.\r
570 \r
571   default_action is a pointer to a bool which is set to false if there\r
572   was an explicit string for the given exit code, or true if we are\r
573   returning the default action.\r
574 \r
575   Returns: 0 on success.\r
576            1 on error.\r
577 */\r
578 int get_exit_action(const TCHAR *service_name, unsigned long *ret, TCHAR *action, bool *default_action) {\r
579   /* Are we returning the default action or a status-specific one? */\r
580   *default_action = ! ret;\r
581 \r
582   /* Try to open the registry */\r
583   HKEY key = open_registry(service_name, NSSM_REG_EXIT, KEY_READ);\r
584   if (! key) return 1;\r
585 \r
586   unsigned long type = REG_SZ;\r
587   unsigned long action_len = ACTION_LEN;\r
588 \r
589   TCHAR code[16];\r
590   if (! ret) code[0] = _T('\0');\r
591   else if (_sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), *ret) < 0) {\r
592     RegCloseKey(key);\r
593     return get_exit_action(service_name, 0, action, default_action);\r
594   }\r
595   if (RegQueryValueEx(key, code, 0, &type, (unsigned char *) action, &action_len) != ERROR_SUCCESS) {\r
596     RegCloseKey(key);\r
597     /* Try again with * as the key if an exit code was defined */\r
598     if (ret) return get_exit_action(service_name, 0, action, default_action);\r
599     return 0;\r
600   }\r
601 \r
602   /* Close registry */\r
603   RegCloseKey(key);\r
604 \r
605   return 0;\r
606 }\r