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