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