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