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