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