Handle second parameter of unformat_double_null().
[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 *formatted, unsigned long formattedlen, TCHAR **dn, unsigned long *newlen) {\r
446   unsigned long i, j;\r
447   *newlen = 0;\r
448 \r
449   /* Don't count trailing NULLs. */\r
450   for (i = 0; i < formattedlen; i++) {\r
451     if (! formatted[i]) {\r
452       formattedlen = i;\r
453       break;\r
454     }\r
455   }\r
456 \r
457   if (! formattedlen) {\r
458     *dn = 0;\r
459     return 0;\r
460   }\r
461 \r
462   for (i = 0; i < formattedlen; i++) if (formatted[i] != _T('\r')) ++*newlen;\r
463 \r
464   /* Skip blank lines. */\r
465   for (i = 0; i < formattedlen; i++) {\r
466     if (formatted[i] == _T('\r') && formatted[i + 1] == _T('\n')) {\r
467       /* This is the last CRLF. */\r
468       if (i >= formattedlen - 2) break;\r
469 \r
470       /*\r
471         Strip at the start of the block or if the next characters are\r
472         CRLF too.\r
473       */\r
474       if (! i || (formatted[i + 2] == _T('\r') && formatted[i + 3] == _T('\n'))) {\r
475         for (j = i + 2; j < formattedlen; j++) formatted[j - 2] = formatted[j];\r
476         formatted[formattedlen--] = _T('\0');\r
477         formatted[formattedlen--] = _T('\0');\r
478         i--;\r
479         --*newlen;\r
480       }\r
481     }\r
482   }\r
483 \r
484   /* Must end with two NULLs. */\r
485   *newlen += 2;\r
486 \r
487   *dn = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *newlen * sizeof(TCHAR));\r
488   if (! *dn) return 1;\r
489 \r
490   for (i = 0, j = 0; i < formattedlen; i++) {\r
491     if (formatted[i] == _T('\r')) continue;\r
492     if (formatted[i] == _T('\n')) (*dn)[j] = _T('\0');\r
493     else (*dn)[j] = formatted[i];\r
494     j++;\r
495   }\r
496 \r
497   return 0;\r
498 }\r
499 \r
500 void override_milliseconds(TCHAR *service_name, HKEY key, TCHAR *value, unsigned long *buffer, unsigned long default_value, unsigned long event) {\r
501   unsigned long type = REG_DWORD;\r
502   unsigned long buflen = sizeof(unsigned long);\r
503   bool ok = false;\r
504   unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);\r
505   if (ret != ERROR_SUCCESS) {\r
506     if (ret != ERROR_FILE_NOT_FOUND) {\r
507       if (type != REG_DWORD) {\r
508         TCHAR milliseconds[16];\r
509         _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), default_value);\r
510         log_event(EVENTLOG_WARNING_TYPE, event, service_name, value, milliseconds, 0);\r
511       }\r
512       else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(ret), 0);\r
513     }\r
514   }\r
515   else ok = true;\r
516 \r
517   if (! ok) *buffer = default_value;\r
518 }\r
519 \r
520 HKEY open_service_registry(const TCHAR *service_name, REGSAM sam, bool must_exist) {\r
521   /* Get registry */\r
522   TCHAR registry[KEY_LENGTH];\r
523   if (service_registry_path(service_name, false, 0, registry, _countof(registry)) < 0) {\r
524     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REGISTRY, _T("open_service_registry()"), 0);\r
525     return 0;\r
526   }\r
527 \r
528   return open_registry_key(registry, sam, must_exist);\r
529 }\r
530 \r
531 long open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam, HKEY *key, bool must_exist) {\r
532   /* Get registry */\r
533   TCHAR registry[KEY_LENGTH];\r
534   if (service_registry_path(service_name, true, sub, registry, _countof(registry)) < 0) {\r
535     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REGISTRY, _T("open_registry()"), 0);\r
536     return 0;\r
537   }\r
538 \r
539   return open_registry_key(registry, sam, key, must_exist);\r
540 }\r
541 \r
542 HKEY open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam, bool must_exist) {\r
543   HKEY key;\r
544   long error = open_registry(service_name, sub, sam, &key, must_exist);\r
545   return key;\r
546 }\r
547 \r
548 HKEY open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam) {\r
549   return open_registry(service_name, sub, sam, true);\r
550 }\r
551 \r
552 HKEY open_registry(const TCHAR *service_name, REGSAM sam) {\r
553   return open_registry(service_name, 0, sam, true);\r
554 }\r
555 \r
556 int get_io_parameters(nssm_service_t *service, HKEY key) {\r
557   /* stdin */\r
558   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
559     service->stdin_sharing = service->stdin_disposition = service->stdin_flags = 0;\r
560     ZeroMemory(service->stdin_path, _countof(service->stdin_path) * sizeof(TCHAR));\r
561     return 1;\r
562   }\r
563 \r
564   /* stdout */\r
565   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
566     service->stdout_sharing = service->stdout_disposition = service->stdout_flags = 0;\r
567     ZeroMemory(service->stdout_path, _countof(service->stdout_path) * sizeof(TCHAR));\r
568     return 2;\r
569   }\r
570 \r
571   /* stderr */\r
572   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
573     service->stderr_sharing = service->stderr_disposition = service->stderr_flags = 0;\r
574     ZeroMemory(service->stderr_path, _countof(service->stderr_path) * sizeof(TCHAR));\r
575     return 3;\r
576   }\r
577 \r
578   return 0;\r
579 }\r
580 \r
581 int get_parameters(nssm_service_t *service, STARTUPINFO *si) {\r
582   unsigned long ret;\r
583 \r
584   /* Try to open the registry */\r
585   HKEY key = open_registry(service->name, KEY_READ);\r
586   if (! key) return 1;\r
587 \r
588   /* Don't expand parameters when retrieving for the GUI. */\r
589   bool expand = si ? true : false;\r
590 \r
591   /* Try to get environment variables - may fail */\r
592   get_environment(service->name, key, NSSM_REG_ENV, &service->env, &service->envlen);\r
593   /* Environment variables to add to existing rather than replace - may fail. */\r
594   get_environment(service->name, key, NSSM_REG_ENV_EXTRA, &service->env_extra, &service->env_extralen);\r
595 \r
596   /* Set environment if we are starting the service. */\r
597   if (si) set_service_environment(service);\r
598 \r
599   /* Try to get executable file - MUST succeed */\r
600   if (get_string(key, NSSM_REG_EXE, service->exe, sizeof(service->exe), expand, false, true)) {\r
601     RegCloseKey(key);\r
602     return 3;\r
603   }\r
604 \r
605   /* Try to get flags - may fail and we don't care */\r
606   if (get_string(key, NSSM_REG_FLAGS, service->flags, sizeof(service->flags), expand, false, true)) {\r
607     log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_FLAGS, NSSM_REG_FLAGS, service->name, service->exe, 0);\r
608     ZeroMemory(service->flags, sizeof(service->flags));\r
609   }\r
610 \r
611   /* Try to get startup directory - may fail and we fall back to a default */\r
612   if (get_string(key, NSSM_REG_DIR, service->dir, sizeof(service->dir), expand, true, true) || ! service->dir[0]) {\r
613     _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);\r
614     strip_basename(service->dir);\r
615     if (service->dir[0] == _T('\0')) {\r
616       /* Help! */\r
617       ret = GetWindowsDirectory(service->dir, sizeof(service->dir));\r
618       if (! ret || ret > sizeof(service->dir)) {\r
619         log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_NO_DIR_AND_NO_FALLBACK, NSSM_REG_DIR, service->name, 0);\r
620         RegCloseKey(key);\r
621         return 4;\r
622       }\r
623     }\r
624     log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_DIR, NSSM_REG_DIR, service->name, service->dir, 0);\r
625   }\r
626 \r
627   /* Try to get processor affinity - may fail. */\r
628   TCHAR buffer[512];\r
629   if (get_string(key, NSSM_REG_AFFINITY, buffer, sizeof(buffer), false, false, false) || ! buffer[0]) service->affinity = 0LL;\r
630   else if (affinity_string_to_mask(buffer, &service->affinity)) {\r
631     log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_AFFINITY_MASK, service->name, buffer);\r
632     service->affinity = 0LL;\r
633   }\r
634   else {\r
635     DWORD_PTR affinity, system_affinity;\r
636 \r
637     if (GetProcessAffinityMask(GetCurrentProcess(), &affinity, &system_affinity)) {\r
638       _int64 effective_affinity = service->affinity & system_affinity;\r
639       if (effective_affinity != service->affinity) {\r
640         TCHAR *system = 0;\r
641         if (! affinity_mask_to_string(system_affinity, &system)) {\r
642           TCHAR *effective = 0;\r
643           if (! affinity_mask_to_string(effective_affinity, &effective)) {\r
644             log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_EFFECTIVE_AFFINITY_MASK, service->name, buffer, system, effective, 0);\r
645           }\r
646           HeapFree(GetProcessHeap(), 0, effective);\r
647         }\r
648         HeapFree(GetProcessHeap(), 0, system);\r
649       }\r
650     }\r
651   }\r
652 \r
653   /* Try to get priority - may fail. */\r
654   unsigned long priority;\r
655   if (get_number(key, NSSM_REG_PRIORITY, &priority, false) == 1) {\r
656     if (priority == (priority & priority_mask())) service->priority = priority;\r
657     else log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_PRIORITY, service->name, NSSM_REG_PRIORITY, 0);\r
658   }\r
659 \r
660   /* Try to get hook I/O sharing - may fail. */\r
661   unsigned long hook_share_output_handles;\r
662   if (get_number(key, NSSM_REG_HOOK_SHARE_OUTPUT_HANDLES, &hook_share_output_handles, false) == 1) {\r
663     if (hook_share_output_handles) service->hook_share_output_handles = true;\r
664     else service->hook_share_output_handles = false;\r
665   }\r
666   else hook_share_output_handles = false;\r
667   /* Try to get file rotation settings - may fail. */\r
668   unsigned long rotate_files;\r
669   if (get_number(key, NSSM_REG_ROTATE, &rotate_files, false) == 1) {\r
670     if (rotate_files) service->rotate_files = true;\r
671     else service->rotate_files = false;\r
672   }\r
673   else service->rotate_files = false;\r
674   if (get_number(key, NSSM_REG_ROTATE_ONLINE, &rotate_files, false) == 1) {\r
675     if (rotate_files) service->rotate_stdout_online = service->rotate_stderr_online = true;\r
676     else service->rotate_stdout_online = service->rotate_stderr_online = false;\r
677   }\r
678   else service->rotate_stdout_online = service->rotate_stderr_online = false;\r
679   /* Hook I/O sharing and online rotation need a pipe. */\r
680   service->use_stdout_pipe = service->rotate_stdout_online || hook_share_output_handles;\r
681   service->use_stderr_pipe = service->rotate_stderr_online || hook_share_output_handles;\r
682   if (get_number(key, NSSM_REG_ROTATE_SECONDS, &service->rotate_seconds, false) != 1) service->rotate_seconds = 0;\r
683   if (get_number(key, NSSM_REG_ROTATE_BYTES_LOW, &service->rotate_bytes_low, false) != 1) service->rotate_bytes_low = 0;\r
684   if (get_number(key, NSSM_REG_ROTATE_BYTES_HIGH, &service->rotate_bytes_high, false) != 1) service->rotate_bytes_high = 0;\r
685   override_milliseconds(service->name, key, NSSM_REG_ROTATE_DELAY, &service->rotate_delay, NSSM_ROTATE_DELAY, NSSM_EVENT_BOGUS_THROTTLE);\r
686 \r
687   /* Try to get force new console setting - may fail. */\r
688   if (get_number(key, NSSM_REG_NO_CONSOLE, &service->no_console, false) != 1) service->no_console = 0;\r
689 \r
690   /* Change to startup directory in case stdout/stderr are relative paths. */\r
691   TCHAR cwd[PATH_LENGTH];\r
692   GetCurrentDirectory(_countof(cwd), cwd);\r
693   SetCurrentDirectory(service->dir);\r
694 \r
695   /* Try to get stdout and stderr */\r
696   if (get_io_parameters(service, key)) {\r
697     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);\r
698     RegCloseKey(key);\r
699     SetCurrentDirectory(cwd);\r
700     return 5;\r
701   }\r
702 \r
703   /* Change back in case the startup directory needs to be deleted. */\r
704   SetCurrentDirectory(cwd);\r
705 \r
706   /* Try to get mandatory restart delay */\r
707   override_milliseconds(service->name, key, NSSM_REG_RESTART_DELAY, &service->restart_delay, 0, NSSM_EVENT_BOGUS_RESTART_DELAY);\r
708 \r
709   /* Try to get throttle restart delay */\r
710   override_milliseconds(service->name, key, NSSM_REG_THROTTLE, &service->throttle_delay, NSSM_RESET_THROTTLE_RESTART, NSSM_EVENT_BOGUS_THROTTLE);\r
711 \r
712   /* Try to get service stop flags. */\r
713   unsigned long type = REG_DWORD;\r
714   unsigned long stop_method_skip;\r
715   unsigned long buflen = sizeof(stop_method_skip);\r
716   bool stop_ok = false;\r
717   ret = RegQueryValueEx(key, NSSM_REG_STOP_METHOD_SKIP, 0, &type, (unsigned char *) &stop_method_skip, &buflen);\r
718   if (ret != ERROR_SUCCESS) {\r
719     if (ret != ERROR_FILE_NOT_FOUND) {\r
720       if (type != REG_DWORD) {\r
721         log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_STOP_METHOD_SKIP, service->name, NSSM_REG_STOP_METHOD_SKIP, NSSM, 0);\r
722       }\r
723       else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, NSSM_REG_STOP_METHOD_SKIP, error_string(ret), 0);\r
724     }\r
725   }\r
726   else stop_ok = true;\r
727 \r
728   /* Try all methods except those requested to be skipped. */\r
729   service->stop_method = ~0;\r
730   if (stop_ok) service->stop_method &= ~stop_method_skip;\r
731 \r
732   /* Try to get kill delays - may fail. */\r
733   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
734   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
735   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
736 \r
737   /* Try to get process tree settings - may fail. */\r
738   unsigned long kill_process_tree;\r
739   if (get_number(key, NSSM_REG_KILL_PROCESS_TREE, &kill_process_tree, false) == 1) {\r
740     if (kill_process_tree) service->kill_process_tree = true;\r
741     else service->kill_process_tree = false;\r
742   }\r
743   else service->kill_process_tree = true;\r
744 \r
745   /* Try to get default exit action. */\r
746   bool default_action;\r
747   service->default_exit_action = NSSM_EXIT_RESTART;\r
748   TCHAR action_string[ACTION_LEN];\r
749   if (! get_exit_action(service->name, 0, action_string, &default_action)) {\r
750     for (int i = 0; exit_action_strings[i]; i++) {\r
751       if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {\r
752         service->default_exit_action = i;\r
753         break;\r
754       }\r
755     }\r
756   }\r
757 \r
758   /* Close registry */\r
759   RegCloseKey(key);\r
760 \r
761   return 0;\r
762 }\r
763 \r
764 /*\r
765   Sets the string for the exit action corresponding to the exit code.\r
766 \r
767   ret is a pointer to an unsigned long containing the exit code.\r
768   If ret is NULL, we retrieve the default exit action unconditionally.\r
769 \r
770   action is a buffer which receives the string.\r
771 \r
772   default_action is a pointer to a bool which is set to false if there\r
773   was an explicit string for the given exit code, or true if we are\r
774   returning the default action.\r
775 \r
776   Returns: 0 on success.\r
777            1 on error.\r
778 */\r
779 int get_exit_action(const TCHAR *service_name, unsigned long *ret, TCHAR *action, bool *default_action) {\r
780   /* Are we returning the default action or a status-specific one? */\r
781   *default_action = ! ret;\r
782 \r
783   /* Try to open the registry */\r
784   HKEY key = open_registry(service_name, NSSM_REG_EXIT, KEY_READ);\r
785   if (! key) return 1;\r
786 \r
787   unsigned long type = REG_SZ;\r
788   unsigned long action_len = ACTION_LEN;\r
789 \r
790   TCHAR code[16];\r
791   if (! ret) code[0] = _T('\0');\r
792   else if (_sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), *ret) < 0) {\r
793     RegCloseKey(key);\r
794     return get_exit_action(service_name, 0, action, default_action);\r
795   }\r
796   if (RegQueryValueEx(key, code, 0, &type, (unsigned char *) action, &action_len) != ERROR_SUCCESS) {\r
797     RegCloseKey(key);\r
798     /* Try again with * as the key if an exit code was defined */\r
799     if (ret) return get_exit_action(service_name, 0, action, default_action);\r
800     return 0;\r
801   }\r
802 \r
803   /* Close registry */\r
804   RegCloseKey(key);\r
805 \r
806   return 0;\r
807 }\r
808 \r
809 int set_hook(const TCHAR *service_name, const TCHAR *hook_event, const TCHAR *hook_action, TCHAR *cmd) {\r
810   /* Try to open the registry */\r
811   TCHAR registry[KEY_LENGTH];\r
812   if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, _T("%s\\%s"), NSSM_REG_HOOK, hook_event) < 0) {\r
813     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("hook registry"), _T("set_hook()"), 0);\r
814     return 1;\r
815   }\r
816 \r
817   HKEY key;\r
818   long error;\r
819 \r
820   /* Don't create keys needlessly. */\r
821   if (! _tcslen(cmd)) {\r
822     key = open_registry(service_name, registry, KEY_READ, false);\r
823     if (! key) return 0;\r
824     error = RegQueryValueEx(key, hook_action, 0, 0, 0, 0);\r
825     RegCloseKey(key);\r
826     if (error == ERROR_FILE_NOT_FOUND) return 0;\r
827   }\r
828 \r
829   key = open_registry(service_name, registry, KEY_WRITE);\r
830   if (! key) return 1;\r
831 \r
832   int ret = 1;\r
833   if (_tcslen(cmd)) ret = set_string(key, (TCHAR *) hook_action, cmd, true);\r
834   else {\r
835     error = RegDeleteValue(key, hook_action);\r
836     if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) ret = 0;\r
837   }\r
838 \r
839   /* Close registry */\r
840   RegCloseKey(key);\r
841 \r
842   return ret;\r
843 }\r
844 \r
845 int get_hook(const TCHAR *service_name, const TCHAR *hook_event, const TCHAR *hook_action, TCHAR *buffer, unsigned long buflen) {\r
846   /* Try to open the registry */\r
847   TCHAR registry[KEY_LENGTH];\r
848   if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, _T("%s\\%s"), NSSM_REG_HOOK, hook_event) < 0) {\r
849     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("hook registry"), _T("get_hook()"), 0);\r
850     return 1;\r
851   }\r
852   HKEY key;\r
853   long error = open_registry(service_name, registry, KEY_READ, &key, false);\r
854   if (! key) {\r
855     if (error == ERROR_FILE_NOT_FOUND) {\r
856       ZeroMemory(buffer, buflen);\r
857       return 0;\r
858     }\r
859     return 1;\r
860   }\r
861 \r
862   int ret = expand_parameter(key, (TCHAR *) hook_action, buffer, buflen, true, false);\r
863 \r
864   /* Close registry */\r
865   RegCloseKey(key);\r
866 \r
867   return ret;\r
868 }\r