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