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