remove dead breaks and returns
[nssm.git] / registry.cpp
1 #include "nssm.h"\r
2 \r
3 extern const TCHAR *exit_action_strings[];\r
4 \r
5 static int service_registry_path(const TCHAR *service_name, bool parameters, const TCHAR *sub, TCHAR *buffer, unsigned long buflen) {\r
6   int ret;\r
7 \r
8   if (parameters) {\r
9     if (sub) ret = _sntprintf_s(buffer, buflen, _TRUNCATE, NSSM_REGISTRY _T("\\") NSSM_REG_PARAMETERS _T("\\%s"), service_name, sub);\r
10     else ret = _sntprintf_s(buffer, buflen, _TRUNCATE, NSSM_REGISTRY _T("\\") NSSM_REG_PARAMETERS, service_name);\r
11   }\r
12   else ret = _sntprintf_s(buffer, buflen, _TRUNCATE, NSSM_REGISTRY, service_name);\r
13 \r
14   return ret;\r
15 }\r
16 \r
17 static long open_registry_key(const TCHAR *registry, REGSAM sam, HKEY *key, bool must_exist) {\r
18   long error;\r
19 \r
20   if (sam & KEY_SET_VALUE) {\r
21     error = RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, sam, 0, key, 0);\r
22     if (error != ERROR_SUCCESS) {\r
23       *key = 0;\r
24       log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);\r
25       return error;\r
26     }\r
27   }\r
28   else {\r
29     error = RegOpenKeyEx(HKEY_LOCAL_MACHINE, registry, 0, sam, key);\r
30     if (error != ERROR_SUCCESS) {\r
31       *key = 0;\r
32       if (error != ERROR_FILE_NOT_FOUND || must_exist) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);\r
33     }\r
34   }\r
35 \r
36   return error;\r
37 }\r
38 \r
39 static HKEY open_registry_key(const TCHAR *registry, REGSAM sam, bool must_exist) {\r
40   HKEY key;\r
41   long error = open_registry_key(registry, sam, &key, must_exist);\r
42   return key;\r
43 }\r
44 \r
45 int create_messages() {\r
46   HKEY key;\r
47 \r
48   TCHAR registry[KEY_LENGTH];\r
49   if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, _T("SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\%s"), NSSM) < 0) {\r
50     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("eventlog registry"), _T("create_messages()"), 0);\r
51     return 1;\r
52   }\r
53 \r
54   if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, KEY_WRITE, 0, &key, 0) != ERROR_SUCCESS) {\r
55     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);\r
56     return 2;\r
57   }\r
58 \r
59   /* Get path of this program */\r
60   const TCHAR *path = nssm_unquoted_imagepath();\r
61 \r
62   /* Try to register the module but don't worry so much on failure */\r
63   RegSetValueEx(key, _T("EventMessageFile"), 0, REG_SZ, (const unsigned char *) path, (unsigned long) (_tcslen(path) +  1) * sizeof(TCHAR));\r
64   unsigned long types = EVENTLOG_INFORMATION_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_ERROR_TYPE;\r
65   RegSetValueEx(key, _T("TypesSupported"), 0, REG_DWORD, (const unsigned char *) &types, sizeof(types));\r
66 \r
67   return 0;\r
68 }\r
69 \r
70 int create_parameters(nssm_service_t *service, bool editing) {\r
71   /* Try to open the registry */\r
72   HKEY key = open_registry(service->name, KEY_WRITE);\r
73   if (! key) return 1;\r
74 \r
75   /* Remember parameters in case we need to delete them. */\r
76   TCHAR registry[KEY_LENGTH];\r
77   int ret = service_registry_path(service->name, true, 0, registry, _countof(registry));\r
78 \r
79   /* Try to create the parameters */\r
80   if (set_expand_string(key, NSSM_REG_EXE, service->exe)) {\r
81     if (ret > 0) RegDeleteKey(HKEY_LOCAL_MACHINE, registry);\r
82     RegCloseKey(key);\r
83     return 2;\r
84   }\r
85   if (set_expand_string(key, NSSM_REG_FLAGS, service->flags)) {\r
86     if (ret > 0) RegDeleteKey(HKEY_LOCAL_MACHINE, registry);\r
87     RegCloseKey(key);\r
88     return 3;\r
89   }\r
90   if (set_expand_string(key, NSSM_REG_DIR, service->dir)) {\r
91     if (ret > 0) RegDeleteKey(HKEY_LOCAL_MACHINE, registry);\r
92     RegCloseKey(key);\r
93     return 4;\r
94   }\r
95 \r
96   /* Other non-default parameters. May fail. */\r
97   if (service->priority != NORMAL_PRIORITY_CLASS) set_number(key, NSSM_REG_PRIORITY, service->priority);\r
98   else if (editing) RegDeleteValue(key, NSSM_REG_PRIORITY);\r
99   if (service->affinity) {\r
100     TCHAR *string;\r
101     if (! affinity_mask_to_string(service->affinity, &string)) {\r
102       if (RegSetValueEx(key, NSSM_REG_AFFINITY, 0, REG_SZ, (const unsigned char *) string, (unsigned long) (_tcslen(string) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) {\r
103         log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_AFFINITY, error_string(GetLastError()), 0);\r
104         HeapFree(GetProcessHeap(), 0, string);\r
105         return 5;\r
106       }\r
107     }\r
108     if (string) HeapFree(GetProcessHeap(), 0, string);\r
109   }\r
110   else if (editing) RegDeleteValue(key, NSSM_REG_AFFINITY);\r
111   unsigned long stop_method_skip = ~service->stop_method;\r
112   if (stop_method_skip) set_number(key, NSSM_REG_STOP_METHOD_SKIP, stop_method_skip);\r
113   else if (editing) RegDeleteValue(key, NSSM_REG_STOP_METHOD_SKIP);\r
114   if (service->default_exit_action < NSSM_NUM_EXIT_ACTIONS) create_exit_action(service->name, exit_action_strings[service->default_exit_action], editing);\r
115   if (service->restart_delay) set_number(key, NSSM_REG_RESTART_DELAY, service->restart_delay);\r
116   else if (editing) RegDeleteValue(key, NSSM_REG_RESTART_DELAY);\r
117   if (service->throttle_delay != NSSM_RESET_THROTTLE_RESTART) set_number(key, NSSM_REG_THROTTLE, service->throttle_delay);\r
118   else if (editing) RegDeleteValue(key, NSSM_REG_THROTTLE);\r
119   if (service->kill_console_delay != NSSM_KILL_CONSOLE_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, service->kill_console_delay);\r
120   else if (editing) RegDeleteValue(key, NSSM_REG_KILL_CONSOLE_GRACE_PERIOD);\r
121   if (service->kill_window_delay != NSSM_KILL_WINDOW_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_WINDOW_GRACE_PERIOD, service->kill_window_delay);\r
122   else if (editing) RegDeleteValue(key, NSSM_REG_KILL_WINDOW_GRACE_PERIOD);\r
123   if (service->kill_threads_delay != NSSM_KILL_THREADS_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_THREADS_GRACE_PERIOD, service->kill_threads_delay);\r
124   else if (editing) RegDeleteValue(key, NSSM_REG_KILL_THREADS_GRACE_PERIOD);\r
125   if (! service->kill_process_tree) set_number(key, NSSM_REG_KILL_PROCESS_TREE, 0);\r
126   else if (editing) RegDeleteValue(key, NSSM_REG_KILL_PROCESS_TREE);\r
127   if (service->stdin_path[0] || editing) {\r
128     if (service->stdin_path[0]) set_expand_string(key, NSSM_REG_STDIN, service->stdin_path);\r
129     else if (editing) RegDeleteValue(key, NSSM_REG_STDIN);\r
130     if (service->stdin_sharing != NSSM_STDIN_SHARING) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_SHARING, service->stdin_sharing);\r
131     else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_SHARING);\r
132     if (service->stdin_disposition != NSSM_STDIN_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_DISPOSITION, service->stdin_disposition);\r
133     else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_DISPOSITION);\r
134     if (service->stdin_flags != NSSM_STDIN_FLAGS) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_FLAGS, service->stdin_flags);\r
135     else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_FLAGS);\r
136   }\r
137   if (service->stdout_path[0] || editing) {\r
138     if (service->stdout_path[0]) set_expand_string(key, NSSM_REG_STDOUT, service->stdout_path);\r
139     else if (editing) RegDeleteValue(key, NSSM_REG_STDOUT);\r
140     if (service->stdout_sharing != NSSM_STDOUT_SHARING) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_SHARING, service->stdout_sharing);\r
141     else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_SHARING);\r
142     if (service->stdout_disposition != NSSM_STDOUT_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_DISPOSITION, service->stdout_disposition);\r
143     else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_DISPOSITION);\r
144     if (service->stdout_flags != NSSM_STDOUT_FLAGS) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_FLAGS, service->stdout_flags);\r
145     else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_FLAGS);\r
146     if (service->stdout_copy_and_truncate) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_COPY_AND_TRUNCATE, 1);\r
147     else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_COPY_AND_TRUNCATE);\r
148   }\r
149   if (service->stderr_path[0] || editing) {\r
150     if (service->stderr_path[0]) set_expand_string(key, NSSM_REG_STDERR, service->stderr_path);\r
151     else if (editing) RegDeleteValue(key, NSSM_REG_STDERR);\r
152     if (service->stderr_sharing != NSSM_STDERR_SHARING) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_SHARING, service->stderr_sharing);\r
153     else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_SHARING);\r
154     if (service->stderr_disposition != NSSM_STDERR_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_DISPOSITION, service->stderr_disposition);\r
155     else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_DISPOSITION);\r
156     if (service->stderr_flags != NSSM_STDERR_FLAGS) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_FLAGS, service->stderr_flags);\r
157     else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_FLAGS);\r
158     if (service->stderr_copy_and_truncate) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_COPY_AND_TRUNCATE, 1);\r
159     else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_COPY_AND_TRUNCATE);\r
160   }\r
161   if (service->rotate_files) set_number(key, NSSM_REG_ROTATE, 1);\r
162   else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE);\r
163   if (service->rotate_stdout_online) set_number(key, NSSM_REG_ROTATE_ONLINE, 1);\r
164   else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_ONLINE);\r
165   if (service->rotate_seconds) set_number(key, NSSM_REG_ROTATE_SECONDS, service->rotate_seconds);\r
166   else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_SECONDS);\r
167   if (service->rotate_bytes_low) set_number(key, NSSM_REG_ROTATE_BYTES_LOW, service->rotate_bytes_low);\r
168   else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_BYTES_LOW);\r
169   if (service->rotate_bytes_high) set_number(key, NSSM_REG_ROTATE_BYTES_HIGH, service->rotate_bytes_high);\r
170   else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_BYTES_HIGH);\r
171   if (service->rotate_delay != NSSM_ROTATE_DELAY) set_number(key, NSSM_REG_ROTATE_DELAY, service->rotate_delay);\r
172   else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_DELAY);\r
173   if (service->no_console) set_number(key, NSSM_REG_NO_CONSOLE, 1);\r
174   else if (editing) RegDeleteValue(key, NSSM_REG_NO_CONSOLE);\r
175 \r
176   /* Environment */\r
177   if (service->env) {\r
178     if (RegSetValueEx(key, NSSM_REG_ENV, 0, REG_MULTI_SZ, (const unsigned char *) service->env, (unsigned long) service->envlen * sizeof(TCHAR)) != ERROR_SUCCESS) {\r
179       log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV, error_string(GetLastError()), 0);\r
180     }\r
181   }\r
182   else if (editing) RegDeleteValue(key, NSSM_REG_ENV);\r
183   if (service->env_extra) {\r
184     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
185       log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV_EXTRA, error_string(GetLastError()), 0);\r
186     }\r
187   }\r
188   else if (editing) RegDeleteValue(key, NSSM_REG_ENV_EXTRA);\r
189 \r
190   /* Close registry. */\r
191   RegCloseKey(key);\r
192 \r
193   return 0;\r
194 }\r
195 \r
196 int create_exit_action(TCHAR *service_name, const TCHAR *action_string, bool editing) {\r
197   /* Get registry */\r
198   TCHAR registry[KEY_LENGTH];\r
199   if (service_registry_path(service_name, true, NSSM_REG_EXIT, registry, _countof(registry)) < 0) {\r
200     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("NSSM_REG_EXIT"), _T("create_exit_action()"), 0);\r
201     return 1;\r
202   }\r
203 \r
204   /* Try to open the registry */\r
205   HKEY key;\r
206   unsigned long disposition;\r
207   if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, KEY_WRITE, 0, &key, &disposition) != ERROR_SUCCESS) {\r
208     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);\r
209     return 2;\r
210   }\r
211 \r
212   /* Do nothing if the key already existed */\r
213   if (disposition == REG_OPENED_EXISTING_KEY && ! editing) {\r
214     RegCloseKey(key);\r
215     return 0;\r
216   }\r
217 \r
218   /* Create the default value */\r
219   if (RegSetValueEx(key, 0, 0, REG_SZ, (const unsigned char *) action_string, (unsigned long) (_tcslen(action_string) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) {\r
220     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_EXIT, error_string(GetLastError()), 0);\r
221     RegCloseKey(key);\r
222     return 3;\r
223   }\r
224 \r
225   /* Close registry */\r
226   RegCloseKey(key);\r
227 \r
228   return 0;\r
229 }\r
230 \r
231 int get_environment(TCHAR *service_name, HKEY key, TCHAR *value, TCHAR **env, unsigned long *envlen) {\r
232   unsigned long type = REG_MULTI_SZ;\r
233 \r
234   /* Dummy test to find buffer size */\r
235   unsigned long ret = RegQueryValueEx(key, value, 0, &type, NULL, envlen);\r
236   if (ret != ERROR_SUCCESS) {\r
237     *env = 0;\r
238     *envlen = 0;\r
239     /* The service probably doesn't have any environment configured */\r
240     if (ret == ERROR_FILE_NOT_FOUND) return 0;\r
241     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);\r
242     return 1;\r
243   }\r
244 \r
245   if (type != REG_MULTI_SZ) {\r
246     *env = 0;\r
247     *envlen = 0;\r
248     log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_INVALID_ENVIRONMENT_STRING_TYPE, value, service_name, 0);\r
249     return 2;\r
250   }\r
251 \r
252   /* Probably not possible */\r
253   if (! *envlen) return 0;\r
254 \r
255   /* Previously initialised? */\r
256   if (*env) HeapFree(GetProcessHeap(), 0, *env);\r
257 \r
258   *env = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, *envlen);\r
259   if (! *env) {\r
260     *envlen = 0;\r
261     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, value, _T("get_environment()"), 0);\r
262     return 3;\r
263   }\r
264 \r
265   /* Actually get the strings */\r
266   ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) *env, envlen);\r
267   if (ret != ERROR_SUCCESS) {\r
268     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);\r
269     HeapFree(GetProcessHeap(), 0, *env);\r
270     *env = 0;\r
271     *envlen = 0;\r
272     return 4;\r
273   }\r
274 \r
275   return 0;\r
276 }\r
277 \r
278 \r
279 int get_string(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool expand, bool sanitise, bool must_exist) {\r
280   TCHAR *buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, datalen);\r
281   if (! buffer) {\r
282     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, value, _T("get_string()"), 0);\r
283     return 1;\r
284   }\r
285 \r
286   ZeroMemory(data, datalen);\r
287 \r
288   unsigned long type = REG_EXPAND_SZ;\r
289   unsigned long buflen = datalen;\r
290 \r
291   unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);\r
292   if (ret != ERROR_SUCCESS) {\r
293     unsigned long error = GetLastError();\r
294     HeapFree(GetProcessHeap(), 0, buffer);\r
295 \r
296     if (ret == ERROR_FILE_NOT_FOUND) {\r
297       if (! must_exist) return 0;\r
298     }\r
299 \r
300     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(error), 0);\r
301     return 2;\r
302   }\r
303 \r
304   /* Paths aren't allowed to contain quotes. */\r
305   if (sanitise) PathUnquoteSpaces(buffer);\r
306 \r
307   /* Do we want to expand the string? */\r
308   if (! expand) {\r
309     if (type == REG_EXPAND_SZ) type = REG_SZ;\r
310   }\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 get_string(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise) {\r
331   return get_string(key, value, data, datalen, false, sanitise, true);\r
332 }\r
333 \r
334 int expand_parameter(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise, bool must_exist) {\r
335   return get_string(key, value, data, datalen, true, sanitise, must_exist);\r
336 }\r
337 \r
338 int expand_parameter(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise) {\r
339   return expand_parameter(key, value, data, datalen, sanitise, true);\r
340 }\r
341 \r
342 /*\r
343   Sets a string in the registry.\r
344   Returns: 0 if it was set.\r
345            1 on error.\r
346 */\r
347 int set_string(HKEY key, TCHAR *value, TCHAR *string, bool expand) {\r
348   unsigned long type = expand ? REG_EXPAND_SZ : REG_SZ;\r
349   if (RegSetValueEx(key, value, 0, type, (const unsigned char *) string, (unsigned long) (_tcslen(string) + 1) * sizeof(TCHAR)) == ERROR_SUCCESS) return 0;\r
350   log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, value, error_string(GetLastError()), 0);\r
351   return 1;\r
352 }\r
353 \r
354 int set_string(HKEY key, TCHAR *value, TCHAR *string) {\r
355   return set_string(key, value, string, false);\r
356 }\r
357 \r
358 int set_expand_string(HKEY key, TCHAR *value, TCHAR *string) {\r
359   return set_string(key, value, string, true);\r
360 }\r
361 \r
362 /*\r
363   Set an unsigned long in the registry.\r
364   Returns: 0 if it was set.\r
365            1 on error.\r
366 */\r
367 int set_number(HKEY key, TCHAR *value, unsigned long number) {\r
368   if (RegSetValueEx(key, value, 0, REG_DWORD, (const unsigned char *) &number, sizeof(number)) == ERROR_SUCCESS) return 0;\r
369   log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, value, error_string(GetLastError()), 0);\r
370   return 1;\r
371 }\r
372 \r
373 /*\r
374   Query an unsigned long from the registry.\r
375   Returns:  1 if a number was retrieved.\r
376             0 if none was found and must_exist is false.\r
377            -1 if none was found and must_exist is true.\r
378            -2 otherwise.\r
379 */\r
380 int get_number(HKEY key, TCHAR *value, unsigned long *number, bool must_exist) {\r
381   unsigned long type = REG_DWORD;\r
382   unsigned long number_len = sizeof(unsigned long);\r
383 \r
384   int ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) number, &number_len);\r
385   if (ret == ERROR_SUCCESS) return 1;\r
386 \r
387   if (ret == ERROR_FILE_NOT_FOUND) {\r
388     if (! must_exist) return 0;\r
389   }\r
390 \r
391   log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);\r
392   if (ret == ERROR_FILE_NOT_FOUND) return -1;\r
393 \r
394   return -2;\r
395 }\r
396 \r
397 int get_number(HKEY key, TCHAR *value, unsigned long *number) {\r
398   return get_number(key, value, number, true);\r
399 }\r
400 \r
401 /* Replace NULL with CRLF. Leave NULL NULL as the end marker. */\r
402 int format_double_null(TCHAR *dn, unsigned long dnlen, TCHAR **formatted, unsigned long *newlen) {\r
403   unsigned long i, j;\r
404   *newlen = dnlen;\r
405 \r
406   if (! *newlen) {\r
407     *formatted = 0;\r
408     return 0;\r
409   }\r
410 \r
411   for (i = 0; i < dnlen; i++) if (! dn[i] && dn[i + 1]) ++*newlen;\r
412 \r
413   *formatted = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *newlen * sizeof(TCHAR));\r
414   if (! *formatted) {\r
415     *newlen = 0;\r
416     return 1;\r
417   }\r
418 \r
419   for (i = 0, j = 0; i < dnlen; i++) {\r
420     (*formatted)[j] = dn[i];\r
421     if (! dn[i]) {\r
422       if (dn[i + 1]) {\r
423         (*formatted)[j] = _T('\r');\r
424         (*formatted)[++j] = _T('\n');\r
425       }\r
426     }\r
427     j++;\r
428   }\r
429 \r
430   return 0;\r
431 }\r
432 \r
433 /* Strip CR and replace LF with NULL. */\r
434 int unformat_double_null(TCHAR *dn, unsigned long dnlen, TCHAR **unformatted, unsigned long *newlen) {\r
435   unsigned long i, j;\r
436   *newlen = 0;\r
437 \r
438   if (! dnlen) {\r
439     *unformatted = 0;\r
440     return 0;\r
441   }\r
442 \r
443   for (i = 0; i < dnlen; i++) if (dn[i] != _T('\r')) ++*newlen;\r
444 \r
445   /* Skip blank lines. */\r
446   for (i = 0; i < dnlen; i++) {\r
447     if (dn[i] == _T('\r') && dn[i + 1] == _T('\n')) {\r
448       /* This is the last CRLF. */\r
449       if (i >= dnlen - 2) break;\r
450 \r
451       /*\r
452         Strip at the start of the block or if the next characters are\r
453         CRLF too.\r
454       */\r
455       if (! i || (dn[i + 2] == _T('\r') && dn[i + 3] == _T('\n'))) {\r
456         for (j = i + 2; j < dnlen; j++) dn[j - 2] = dn[j];\r
457         dn[dnlen--] = _T('\0');\r
458         dn[dnlen--] = _T('\0');\r
459         i--;\r
460         --*newlen;\r
461       }\r
462     }\r
463   }\r
464 \r
465   /* Must end with two NULLs. */\r
466   *newlen += 2;\r
467 \r
468   *unformatted = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *newlen * sizeof(TCHAR));\r
469   if (! *unformatted) return 1;\r
470 \r
471   for (i = 0, j = 0; i < dnlen; i++) {\r
472     if (dn[i] == _T('\r')) continue;\r
473     if (dn[i] == _T('\n')) (*unformatted)[j] = _T('\0');\r
474     else (*unformatted)[j] = dn[i];\r
475     j++;\r
476   }\r
477 \r
478   return 0;\r
479 }\r
480 \r
481 void override_milliseconds(TCHAR *service_name, HKEY key, TCHAR *value, unsigned long *buffer, unsigned long default_value, unsigned long event) {\r
482   unsigned long type = REG_DWORD;\r
483   unsigned long buflen = sizeof(unsigned long);\r
484   bool ok = false;\r
485   unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);\r
486   if (ret != ERROR_SUCCESS) {\r
487     if (ret != ERROR_FILE_NOT_FOUND) {\r
488       if (type != REG_DWORD) {\r
489         TCHAR milliseconds[16];\r
490         _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), default_value);\r
491         log_event(EVENTLOG_WARNING_TYPE, event, service_name, value, milliseconds, 0);\r
492       }\r
493       else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);\r
494     }\r
495   }\r
496   else ok = true;\r
497 \r
498   if (! ok) *buffer = default_value;\r
499 }\r
500 \r
501 HKEY open_service_registry(const TCHAR *service_name, REGSAM sam, bool must_exist) {\r
502   /* Get registry */\r
503   TCHAR registry[KEY_LENGTH];\r
504   if (service_registry_path(service_name, false, 0, registry, _countof(registry)) < 0) {\r
505     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REGISTRY, _T("open_service_registry()"), 0);\r
506     return 0;\r
507   }\r
508 \r
509   return open_registry_key(registry, sam, must_exist);\r
510 }\r
511 \r
512 long open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam, HKEY *key, bool must_exist) {\r
513   /* Get registry */\r
514   TCHAR registry[KEY_LENGTH];\r
515   if (service_registry_path(service_name, true, sub, registry, _countof(registry)) < 0) {\r
516     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REGISTRY, _T("open_registry()"), 0);\r
517     return 0;\r
518   }\r
519 \r
520   return open_registry_key(registry, sam, key, must_exist);\r
521 }\r
522 \r
523 HKEY open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam, bool must_exist) {\r
524   HKEY key;\r
525   long error = open_registry(service_name, sub, sam, &key, must_exist);\r
526   return key;\r
527 }\r
528 \r
529 HKEY open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam) {\r
530   return open_registry(service_name, sub, sam, true);\r
531 }\r
532 \r
533 HKEY open_registry(const TCHAR *service_name, REGSAM sam) {\r
534   return open_registry(service_name, 0, sam, true);\r
535 }\r
536 \r
537 int get_io_parameters(nssm_service_t *service, HKEY key) {\r
538   /* stdin */\r
539   if (get_createfile_parameters(key, NSSM_REG_STDIN, service->stdin_path, &service->stdin_sharing, NSSM_STDIN_SHARING, &service->stdin_disposition, NSSM_STDIN_DISPOSITION, &service->stdin_flags, NSSM_STDIN_FLAGS, 0)) {\r
540     service->stdin_sharing = service->stdin_disposition = service->stdin_flags = 0;\r
541     ZeroMemory(service->stdin_path, _countof(service->stdin_path) * sizeof(TCHAR));\r
542     return 1;\r
543   }\r
544 \r
545   /* stdout */\r
546   if (get_createfile_parameters(key, NSSM_REG_STDOUT, service->stdout_path, &service->stdout_sharing, NSSM_STDOUT_SHARING, &service->stdout_disposition, NSSM_STDOUT_DISPOSITION, &service->stdout_flags, NSSM_STDOUT_FLAGS, &service->stdout_copy_and_truncate)) {\r
547     service->stdout_sharing = service->stdout_disposition = service->stdout_flags = 0;\r
548     ZeroMemory(service->stdout_path, _countof(service->stdout_path) * sizeof(TCHAR));\r
549     return 2;\r
550   }\r
551 \r
552   /* stderr */\r
553   if (get_createfile_parameters(key, NSSM_REG_STDERR, service->stderr_path, &service->stderr_sharing, NSSM_STDERR_SHARING, &service->stderr_disposition, NSSM_STDERR_DISPOSITION, &service->stderr_flags, NSSM_STDERR_FLAGS, &service->stderr_copy_and_truncate)) {\r
554     service->stderr_sharing = service->stderr_disposition = service->stderr_flags = 0;\r
555     ZeroMemory(service->stderr_path, _countof(service->stderr_path) * sizeof(TCHAR));\r
556     return 3;\r
557   }\r
558 \r
559   return 0;\r
560 }\r
561 \r
562 int get_parameters(nssm_service_t *service, STARTUPINFO *si) {\r
563   unsigned long ret;\r
564 \r
565   /* Try to open the registry */\r
566   HKEY key = open_registry(service->name, KEY_READ);\r
567   if (! key) return 1;\r
568 \r
569   /* Don't expand parameters when retrieving for the GUI. */\r
570   bool expand = si ? true : false;\r
571 \r
572   /* Try to get environment variables - may fail */\r
573   get_environment(service->name, key, NSSM_REG_ENV, &service->env, &service->envlen);\r
574   /* Environment variables to add to existing rather than replace - may fail. */\r
575   get_environment(service->name, key, NSSM_REG_ENV_EXTRA, &service->env_extra, &service->env_extralen);\r
576 \r
577   /* Set environment if we are starting the service. */\r
578   if (si) set_service_environment(service);\r
579 \r
580   /* Try to get executable file - MUST succeed */\r
581   if (get_string(key, NSSM_REG_EXE, service->exe, sizeof(service->exe), expand, false, true)) {\r
582     RegCloseKey(key);\r
583     return 3;\r
584   }\r
585 \r
586   /* Try to get flags - may fail and we don't care */\r
587   if (get_string(key, NSSM_REG_FLAGS, service->flags, sizeof(service->flags), expand, false, true)) {\r
588     log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_FLAGS, NSSM_REG_FLAGS, service->name, service->exe, 0);\r
589     ZeroMemory(service->flags, sizeof(service->flags));\r
590   }\r
591 \r
592   /* Try to get startup directory - may fail and we fall back to a default */\r
593   if (get_string(key, NSSM_REG_DIR, service->dir, sizeof(service->dir), expand, true, true) || ! service->dir[0]) {\r
594     _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);\r
595     strip_basename(service->dir);\r
596     if (service->dir[0] == _T('\0')) {\r
597       /* Help! */\r
598       ret = GetWindowsDirectory(service->dir, sizeof(service->dir));\r
599       if (! ret || ret > sizeof(service->dir)) {\r
600         log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_NO_DIR_AND_NO_FALLBACK, NSSM_REG_DIR, service->name, 0);\r
601         RegCloseKey(key);\r
602         return 4;\r
603       }\r
604     }\r
605     log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_DIR, NSSM_REG_DIR, service->name, service->dir, 0);\r
606   }\r
607 \r
608   /* Try to get processor affinity - may fail. */\r
609   TCHAR buffer[512];\r
610   if (get_string(key, NSSM_REG_AFFINITY, buffer, sizeof(buffer), false, false, false) || ! buffer[0]) service->affinity = 0LL;\r
611   else if (affinity_string_to_mask(buffer, &service->affinity)) {\r
612     log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_AFFINITY_MASK, service->name, buffer);\r
613     service->affinity = 0LL;\r
614   }\r
615   else {\r
616     DWORD_PTR affinity, system_affinity;\r
617 \r
618     if (GetProcessAffinityMask(GetCurrentProcess(), &affinity, &system_affinity)) {\r
619       _int64 effective_affinity = service->affinity & system_affinity;\r
620       if (effective_affinity != service->affinity) {\r
621         TCHAR *system = 0;\r
622         if (! affinity_mask_to_string(system_affinity, &system)) {\r
623           TCHAR *effective = 0;\r
624           if (! affinity_mask_to_string(effective_affinity, &effective)) {\r
625             log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_EFFECTIVE_AFFINITY_MASK, service->name, buffer, system, effective, 0);\r
626           }\r
627           HeapFree(GetProcessHeap(), 0, effective);\r
628         }\r
629         HeapFree(GetProcessHeap(), 0, system);\r
630       }\r
631     }\r
632   }\r
633 \r
634   /* Try to get priority - may fail. */\r
635   unsigned long priority;\r
636   if (get_number(key, NSSM_REG_PRIORITY, &priority, false) == 1) {\r
637     if (priority == (priority & priority_mask())) service->priority = priority;\r
638     else log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_PRIORITY, service->name, NSSM_REG_PRIORITY, 0);\r
639   }\r
640 \r
641   /* Try to get file rotation settings - may fail. */\r
642   unsigned long rotate_files;\r
643   if (get_number(key, NSSM_REG_ROTATE, &rotate_files, false) == 1) {\r
644     if (rotate_files) service->rotate_files = true;\r
645     else service->rotate_files = false;\r
646   }\r
647   else service->rotate_files = false;\r
648   if (get_number(key, NSSM_REG_ROTATE_ONLINE, &rotate_files, false) == 1) {\r
649     if (rotate_files) service->rotate_stdout_online = service->rotate_stderr_online = true;\r
650     else service->rotate_stdout_online = service->rotate_stderr_online = false;\r
651   }\r
652   else service->rotate_stdout_online = service->rotate_stderr_online = false;\r
653   if (get_number(key, NSSM_REG_ROTATE_SECONDS, &service->rotate_seconds, false) != 1) service->rotate_seconds = 0;\r
654   if (get_number(key, NSSM_REG_ROTATE_BYTES_LOW, &service->rotate_bytes_low, false) != 1) service->rotate_bytes_low = 0;\r
655   if (get_number(key, NSSM_REG_ROTATE_BYTES_HIGH, &service->rotate_bytes_high, false) != 1) service->rotate_bytes_high = 0;\r
656   override_milliseconds(service->name, key, NSSM_REG_ROTATE_DELAY, &service->rotate_delay, NSSM_ROTATE_DELAY, NSSM_EVENT_BOGUS_THROTTLE);\r
657 \r
658   /* Try to get force new console setting - may fail. */\r
659   if (get_number(key, NSSM_REG_NO_CONSOLE, &service->no_console, false) != 1) service->no_console = 0;\r
660 \r
661   /* Change to startup directory in case stdout/stderr are relative paths. */\r
662   TCHAR cwd[PATH_LENGTH];\r
663   GetCurrentDirectory(_countof(cwd), cwd);\r
664   SetCurrentDirectory(service->dir);\r
665 \r
666   /* Try to get stdout and stderr */\r
667   if (get_io_parameters(service, key)) {\r
668     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);\r
669     RegCloseKey(key);\r
670     SetCurrentDirectory(cwd);\r
671     return 5;\r
672   }\r
673 \r
674   /* Change back in case the startup directory needs to be deleted. */\r
675   SetCurrentDirectory(cwd);\r
676 \r
677   /* Try to get mandatory restart delay */\r
678   override_milliseconds(service->name, key, NSSM_REG_RESTART_DELAY, &service->restart_delay, 0, NSSM_EVENT_BOGUS_RESTART_DELAY);\r
679 \r
680   /* Try to get throttle restart delay */\r
681   override_milliseconds(service->name, key, NSSM_REG_THROTTLE, &service->throttle_delay, NSSM_RESET_THROTTLE_RESTART, NSSM_EVENT_BOGUS_THROTTLE);\r
682 \r
683   /* Try to get service stop flags. */\r
684   unsigned long type = REG_DWORD;\r
685   unsigned long stop_method_skip;\r
686   unsigned long buflen = sizeof(stop_method_skip);\r
687   bool stop_ok = false;\r
688   ret = RegQueryValueEx(key, NSSM_REG_STOP_METHOD_SKIP, 0, &type, (unsigned char *) &stop_method_skip, &buflen);\r
689   if (ret != ERROR_SUCCESS) {\r
690     if (ret != ERROR_FILE_NOT_FOUND) {\r
691       if (type != REG_DWORD) {\r
692         log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_STOP_METHOD_SKIP, service->name, NSSM_REG_STOP_METHOD_SKIP, NSSM, 0);\r
693       }\r
694       else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, NSSM_REG_STOP_METHOD_SKIP, error_string(GetLastError()), 0);\r
695     }\r
696   }\r
697   else stop_ok = true;\r
698 \r
699   /* Try all methods except those requested to be skipped. */\r
700   service->stop_method = ~0;\r
701   if (stop_ok) service->stop_method &= ~stop_method_skip;\r
702 \r
703   /* Try to get kill delays - may fail. */\r
704   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
705   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
706   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
707 \r
708   /* Try to get process tree settings - may fail. */\r
709   unsigned long kill_process_tree;\r
710   if (get_number(key, NSSM_REG_KILL_PROCESS_TREE, &kill_process_tree, false) == 1) {\r
711     if (kill_process_tree) service->kill_process_tree = true;\r
712     else service->kill_process_tree = false;\r
713   }\r
714   else service->kill_process_tree = true;\r
715 \r
716   /* Try to get default exit action. */\r
717   bool default_action;\r
718   service->default_exit_action = NSSM_EXIT_RESTART;\r
719   TCHAR action_string[ACTION_LEN];\r
720   if (! get_exit_action(service->name, 0, action_string, &default_action)) {\r
721     for (int i = 0; exit_action_strings[i]; i++) {\r
722       if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {\r
723         service->default_exit_action = i;\r
724         break;\r
725       }\r
726     }\r
727   }\r
728 \r
729   /* Close registry */\r
730   RegCloseKey(key);\r
731 \r
732   return 0;\r
733 }\r
734 \r
735 /*\r
736   Sets the string for the exit action corresponding to the exit code.\r
737 \r
738   ret is a pointer to an unsigned long containing the exit code.\r
739   If ret is NULL, we retrieve the default exit action unconditionally.\r
740 \r
741   action is a buffer which receives the string.\r
742 \r
743   default_action is a pointer to a bool which is set to false if there\r
744   was an explicit string for the given exit code, or true if we are\r
745   returning the default action.\r
746 \r
747   Returns: 0 on success.\r
748            1 on error.\r
749 */\r
750 int get_exit_action(const TCHAR *service_name, unsigned long *ret, TCHAR *action, bool *default_action) {\r
751   /* Are we returning the default action or a status-specific one? */\r
752   *default_action = ! ret;\r
753 \r
754   /* Try to open the registry */\r
755   HKEY key = open_registry(service_name, NSSM_REG_EXIT, KEY_READ);\r
756   if (! key) return 1;\r
757 \r
758   unsigned long type = REG_SZ;\r
759   unsigned long action_len = ACTION_LEN;\r
760 \r
761   TCHAR code[16];\r
762   if (! ret) code[0] = _T('\0');\r
763   else if (_sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), *ret) < 0) {\r
764     RegCloseKey(key);\r
765     return get_exit_action(service_name, 0, action, default_action);\r
766   }\r
767   if (RegQueryValueEx(key, code, 0, &type, (unsigned char *) action, &action_len) != ERROR_SUCCESS) {\r
768     RegCloseKey(key);\r
769     /* Try again with * as the key if an exit code was defined */\r
770     if (ret) return get_exit_action(service_name, 0, action, default_action);\r
771     return 0;\r
772   }\r
773 \r
774   /* Close registry */\r
775   RegCloseKey(key);\r
776 \r
777   return 0;\r
778 }\r
779 \r
780 int set_hook(const TCHAR *service_name, const TCHAR *hook_event, const TCHAR *hook_action, TCHAR *cmd) {\r
781   /* Try to open the registry */\r
782   TCHAR registry[KEY_LENGTH];\r
783   if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, _T("%s\\%s"), NSSM_REG_HOOK, hook_event) < 0) {\r
784     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("hook registry"), _T("set_hook()"), 0);\r
785     return 1;\r
786   }\r
787 \r
788   HKEY key;\r
789   long error;\r
790 \r
791   /* Don't create keys needlessly. */\r
792   if (! _tcslen(cmd)) {\r
793     key = open_registry(service_name, registry, KEY_READ, false);\r
794     if (! key) return 0;\r
795     error = RegQueryValueEx(key, hook_action, 0, 0, 0, 0);\r
796     RegCloseKey(key);\r
797     if (error == ERROR_FILE_NOT_FOUND) return 0;\r
798   }\r
799 \r
800   key = open_registry(service_name, registry, KEY_WRITE);\r
801   if (! key) return 1;\r
802 \r
803   int ret = 1;\r
804   if (_tcslen(cmd)) ret = set_string(key, (TCHAR *) hook_action, cmd, true);\r
805   else {\r
806     error = RegDeleteValue(key, hook_action);\r
807     if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) ret = 0;\r
808   }\r
809 \r
810   /* Close registry */\r
811   RegCloseKey(key);\r
812 \r
813   return ret;\r
814 }\r
815 \r
816 int get_hook(const TCHAR *service_name, const TCHAR *hook_event, const TCHAR *hook_action, TCHAR *buffer, unsigned long buflen) {\r
817   /* Try to open the registry */\r
818   TCHAR registry[KEY_LENGTH];\r
819   if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, _T("%s\\%s"), NSSM_REG_HOOK, hook_event) < 0) {\r
820     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("hook registry"), _T("get_hook()"), 0);\r
821     return 1;\r
822   }\r
823   HKEY key;\r
824   long error = open_registry(service_name, registry, KEY_READ, &key, false);\r
825   if (! key) {\r
826     if (error == ERROR_FILE_NOT_FOUND) {\r
827       ZeroMemory(buffer, buflen);\r
828       return 0;\r
829     }\r
830     return 1;\r
831   }\r
832 \r
833   int ret = expand_parameter(key, (TCHAR *) hook_action, buffer, buflen, true, false);\r
834 \r
835   /* Close registry */\r
836   RegCloseKey(key);\r
837 \r
838   return ret;\r
839 }\r