Use UTF-8 functions when working with explicit encodings.
[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   /* Minimum usable environment would be A= NULL NULL. */\r
264   if (envsize < 4 * sizeof(TCHAR)) {\r
265     *env = 0;\r
266     return 3;\r
267   }\r
268 \r
269   /* Previously initialised? */\r
270   if (*env) HeapFree(GetProcessHeap(), 0, *env);\r
271 \r
272   *env = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, envsize);\r
273   if (! *env) {\r
274     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, value, _T("get_environment()"), 0);\r
275     return 4;\r
276   }\r
277 \r
278   /* Actually get the strings. */\r
279   ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) *env, &envsize);\r
280   if (ret != ERROR_SUCCESS) {\r
281     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(ret), 0);\r
282     HeapFree(GetProcessHeap(), 0, *env);\r
283     *env = 0;\r
284     return 5;\r
285   }\r
286 \r
287   /* Value retrieved by RegQueryValueEx() is SIZE not COUNT. */\r
288   *envlen = (unsigned long) environment_length(*env);\r
289 \r
290   return 0;\r
291 }\r
292 \r
293 \r
294 int get_string(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool expand, bool sanitise, bool must_exist) {\r
295   TCHAR *buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, datalen);\r
296   if (! buffer) {\r
297     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, value, _T("get_string()"), 0);\r
298     return 1;\r
299   }\r
300 \r
301   ZeroMemory(data, datalen);\r
302 \r
303   unsigned long type = REG_EXPAND_SZ;\r
304   unsigned long buflen = datalen;\r
305 \r
306   unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);\r
307   if (ret != ERROR_SUCCESS) {\r
308     HeapFree(GetProcessHeap(), 0, buffer);\r
309 \r
310     if (ret == ERROR_FILE_NOT_FOUND) {\r
311       if (! must_exist) return 0;\r
312     }\r
313 \r
314     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(ret), 0);\r
315     return 2;\r
316   }\r
317 \r
318   /* Paths aren't allowed to contain quotes. */\r
319   if (sanitise) PathUnquoteSpaces(buffer);\r
320 \r
321   /* Do we want to expand the string? */\r
322   if (! expand) {\r
323     if (type == REG_EXPAND_SZ) type = REG_SZ;\r
324   }\r
325 \r
326   /* Technically we shouldn't expand environment strings from REG_SZ values */\r
327   if (type != REG_EXPAND_SZ) {\r
328     memmove(data, buffer, buflen);\r
329     HeapFree(GetProcessHeap(), 0, buffer);\r
330     return 0;\r
331   }\r
332 \r
333   ret = ExpandEnvironmentStrings((TCHAR *) buffer, data, datalen);\r
334   if (! ret || ret > datalen) {\r
335     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_EXPANDENVIRONMENTSTRINGS_FAILED, buffer, error_string(GetLastError()), 0);\r
336     HeapFree(GetProcessHeap(), 0, buffer);\r
337     return 3;\r
338   }\r
339 \r
340   HeapFree(GetProcessHeap(), 0, buffer);\r
341   return 0;\r
342 }\r
343 \r
344 int get_string(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise) {\r
345   return get_string(key, value, data, datalen, false, sanitise, true);\r
346 }\r
347 \r
348 int expand_parameter(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise, bool must_exist) {\r
349   return get_string(key, value, data, datalen, true, sanitise, must_exist);\r
350 }\r
351 \r
352 int expand_parameter(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise) {\r
353   return expand_parameter(key, value, data, datalen, sanitise, true);\r
354 }\r
355 \r
356 /*\r
357   Sets a string in the registry.\r
358   Returns: 0 if it was set.\r
359            1 on error.\r
360 */\r
361 int set_string(HKEY key, TCHAR *value, TCHAR *string, bool expand) {\r
362   unsigned long type = expand ? REG_EXPAND_SZ : REG_SZ;\r
363   if (RegSetValueEx(key, value, 0, type, (const unsigned char *) string, (unsigned long) (_tcslen(string) + 1) * sizeof(TCHAR)) == ERROR_SUCCESS) return 0;\r
364   log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, value, error_string(GetLastError()), 0);\r
365   return 1;\r
366 }\r
367 \r
368 int set_string(HKEY key, TCHAR *value, TCHAR *string) {\r
369   return set_string(key, value, string, false);\r
370 }\r
371 \r
372 int set_expand_string(HKEY key, TCHAR *value, TCHAR *string) {\r
373   return set_string(key, value, string, true);\r
374 }\r
375 \r
376 /*\r
377   Set an unsigned long in the registry.\r
378   Returns: 0 if it was set.\r
379            1 on error.\r
380 */\r
381 int set_number(HKEY key, TCHAR *value, unsigned long number) {\r
382   if (RegSetValueEx(key, value, 0, REG_DWORD, (const unsigned char *) &number, sizeof(number)) == ERROR_SUCCESS) return 0;\r
383   log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, value, error_string(GetLastError()), 0);\r
384   return 1;\r
385 }\r
386 \r
387 /*\r
388   Query an unsigned long from the registry.\r
389   Returns:  1 if a number was retrieved.\r
390             0 if none was found and must_exist is false.\r
391            -1 if none was found and must_exist is true.\r
392            -2 otherwise.\r
393 */\r
394 int get_number(HKEY key, TCHAR *value, unsigned long *number, bool must_exist) {\r
395   unsigned long type = REG_DWORD;\r
396   unsigned long number_len = sizeof(unsigned long);\r
397 \r
398   int ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) number, &number_len);\r
399   if (ret == ERROR_SUCCESS) return 1;\r
400 \r
401   if (ret == ERROR_FILE_NOT_FOUND) {\r
402     if (! must_exist) return 0;\r
403   }\r
404 \r
405   log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(ret), 0);\r
406   if (ret == ERROR_FILE_NOT_FOUND) return -1;\r
407 \r
408   return -2;\r
409 }\r
410 \r
411 int get_number(HKEY key, TCHAR *value, unsigned long *number) {\r
412   return get_number(key, value, number, true);\r
413 }\r
414 \r
415 /* Replace NULL with CRLF. Leave NULL NULL as the end marker. */\r
416 int format_double_null(TCHAR *dn, unsigned long dnlen, TCHAR **formatted, unsigned long *newlen) {\r
417   unsigned long i, j;\r
418   *newlen = dnlen;\r
419 \r
420   if (! *newlen) {\r
421     *formatted = 0;\r
422     return 0;\r
423   }\r
424 \r
425   for (i = 0; i < dnlen; i++) if (! dn[i] && dn[i + 1]) ++*newlen;\r
426 \r
427   *formatted = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *newlen * sizeof(TCHAR));\r
428   if (! *formatted) {\r
429     *newlen = 0;\r
430     return 1;\r
431   }\r
432 \r
433   for (i = 0, j = 0; i < dnlen; i++) {\r
434     (*formatted)[j] = dn[i];\r
435     if (! dn[i]) {\r
436       if (dn[i + 1]) {\r
437         (*formatted)[j] = _T('\r');\r
438         (*formatted)[++j] = _T('\n');\r
439       }\r
440     }\r
441     j++;\r
442   }\r
443 \r
444   return 0;\r
445 }\r
446 \r
447 /* Strip CR and replace LF with NULL.  */\r
448 int unformat_double_null(TCHAR *formatted, unsigned long formattedlen, TCHAR **dn, unsigned long *newlen) {\r
449   unsigned long i, j;\r
450   *newlen = 0;\r
451 \r
452   /* Don't count trailing NULLs. */\r
453   for (i = 0; i < formattedlen; i++) {\r
454     if (! formatted[i]) {\r
455       formattedlen = i;\r
456       break;\r
457     }\r
458   }\r
459 \r
460   if (! formattedlen) {\r
461     *dn = 0;\r
462     return 0;\r
463   }\r
464 \r
465   for (i = 0; i < formattedlen; i++) if (formatted[i] != _T('\r')) ++*newlen;\r
466 \r
467   /* Skip blank lines. */\r
468   for (i = 0; i < formattedlen; i++) {\r
469     if (formatted[i] == _T('\r') && formatted[i + 1] == _T('\n')) {\r
470       /* This is the last CRLF. */\r
471       if (i >= formattedlen - 2) break;\r
472 \r
473       /*\r
474         Strip at the start of the block or if the next characters are\r
475         CRLF too.\r
476       */\r
477       if (! i || (formatted[i + 2] == _T('\r') && formatted[i + 3] == _T('\n'))) {\r
478         for (j = i + 2; j < formattedlen; j++) formatted[j - 2] = formatted[j];\r
479         formatted[formattedlen--] = _T('\0');\r
480         formatted[formattedlen--] = _T('\0');\r
481         i--;\r
482         --*newlen;\r
483       }\r
484     }\r
485   }\r
486 \r
487   /* Must end with two NULLs. */\r
488   *newlen += 2;\r
489 \r
490   *dn = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *newlen * sizeof(TCHAR));\r
491   if (! *dn) return 1;\r
492 \r
493   for (i = 0, j = 0; i < formattedlen; i++) {\r
494     if (formatted[i] == _T('\r')) continue;\r
495     if (formatted[i] == _T('\n')) (*dn)[j] = _T('\0');\r
496     else (*dn)[j] = formatted[i];\r
497     j++;\r
498   }\r
499 \r
500   return 0;\r
501 }\r
502 \r
503 /* Copy a block. */\r
504 int copy_double_null(TCHAR *dn, unsigned long dnlen, TCHAR **newdn) {\r
505   if (! newdn) return 1;\r
506 \r
507   *newdn = 0;\r
508   if (! dn) return 0;\r
509 \r
510   *newdn = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, dnlen * sizeof(TCHAR));\r
511   if (! *newdn) {\r
512     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("dn"), _T("copy_double_null()"), 0);\r
513     return 2;\r
514   }\r
515 \r
516   memmove(*newdn, dn, dnlen * sizeof(TCHAR));\r
517   return 0;\r
518 }\r
519 \r
520 /*\r
521   Create a new block with all the strings of the first block plus a new string.\r
522   The new string may be specified as <key> <delimiter> <value> and the keylen\r
523   gives the offset into the string to compare against existing entries.\r
524   If the key is already present its value will be overwritten in place.\r
525   If the key is blank or empty the new block will still be allocated and have\r
526   non-zero length.\r
527 */\r
528 int append_to_double_null(TCHAR *dn, unsigned long dnlen, TCHAR **newdn, unsigned long *newlen, TCHAR *append, size_t keylen, bool case_sensitive) {\r
529   if (! append || ! append[0]) return copy_double_null(dn, dnlen, newdn);\r
530   size_t appendlen = _tcslen(append);\r
531   int (*fn)(const TCHAR *, const TCHAR *, size_t) = (case_sensitive) ? _tcsncmp : _tcsnicmp;\r
532 \r
533   /* Identify the key, if any, or treat the whole string as the key. */\r
534   TCHAR *key = 0;\r
535   if (! keylen || keylen > appendlen) keylen = appendlen;\r
536   key = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (keylen + 1) * sizeof(TCHAR));\r
537   if (! key) {\r
538     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("key"), _T("append_to_double_null()"), 0);\r
539     return 1;\r
540   }\r
541   memmove(key, append, keylen * sizeof(TCHAR));\r
542   key[keylen] = _T('\0');\r
543 \r
544   /* Find the length of the block not including any existing key. */\r
545   size_t len = 0;\r
546   TCHAR *s;\r
547   for (s = dn; *s; s++) {\r
548     if (fn(s, key, keylen)) len += _tcslen(s) + 1;\r
549     for ( ; *s; s++);\r
550   }\r
551 \r
552   /* Account for new entry. */\r
553   len += _tcslen(append) + 1;\r
554 \r
555   /* Account for trailing NULL. */\r
556   len++;\r
557 \r
558   /* Allocate a new block. */\r
559   *newdn = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof(TCHAR));\r
560   if (! *newdn) {\r
561     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("newdn"), _T("append_to_double_null()"), 0);\r
562     HeapFree(GetProcessHeap(), 0, key);\r
563     return 2;\r
564   }\r
565 \r
566   /* Copy existing entries.*/\r
567   *newlen = (unsigned long) len;\r
568   TCHAR *t = *newdn;\r
569   TCHAR *u;\r
570   bool replaced = false;\r
571   for (s = dn; *s; s++) {\r
572     if (fn(s, key, keylen)) u = s;\r
573     else {\r
574       u = append;\r
575       replaced = true;\r
576     }\r
577     len = _tcslen(u) + 1;\r
578     memmove(t, u, len * sizeof(TCHAR));\r
579     t += len;\r
580     for ( ; *s; s++);\r
581   }\r
582 \r
583   /* Add the entry if it wasn't already replaced.  The buffer was zeroed. */\r
584   if (! replaced) memmove(t, append, _tcslen(append) * sizeof(TCHAR));\r
585 \r
586   HeapFree(GetProcessHeap(), 0, key);\r
587   return 0;\r
588 }\r
589 \r
590 /*\r
591   Create a new block with all the string of the first block minus the given\r
592   string.\r
593   The keylen parameter gives the offset into the string to compare against\r
594   existing entries.  If a substring of existing value matches the string to\r
595   the given length it will be removed.\r
596   If the last entry is removed the new block will still be allocated and\r
597   have non-zero length.\r
598 */\r
599 int remove_from_double_null(TCHAR *dn, unsigned long dnlen, TCHAR **newdn, unsigned long *newlen, TCHAR *remove, size_t keylen, bool case_sensitive) {\r
600   if (! remove || !remove[0]) return copy_double_null(dn, dnlen, newdn);\r
601   size_t removelen = _tcslen(remove);\r
602   int (*fn)(const TCHAR *, const TCHAR *, size_t) = (case_sensitive) ? _tcsncmp : _tcsnicmp;\r
603 \r
604   /* Identify the key, if any, or treat the whole string as the key. */\r
605   TCHAR *key = 0;\r
606   if (! keylen || keylen > removelen) keylen = removelen;\r
607   key = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (keylen + 1) * sizeof(TCHAR));\r
608   if (! key) {\r
609     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("key"), _T("remove_from_double_null()"), 0);\r
610     return 1;\r
611   }\r
612   memmove(key, remove, keylen * sizeof(TCHAR));\r
613   key[keylen] = _T('\0');\r
614 \r
615   /* Find the length of the block not including any existing key. */\r
616   size_t len = 0;\r
617   TCHAR *s;\r
618   for (s = dn; *s; s++) {\r
619     if (fn(s, key, keylen)) len += _tcslen(s) + 1;\r
620     for ( ; *s; s++);\r
621   }\r
622 \r
623   /* Account for trailing NULL. */\r
624   if (++len < 2) len = 2;\r
625 \r
626   /* Allocate a new block. */\r
627   *newdn = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof(TCHAR));\r
628   if (! *newdn) {\r
629     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("newdn"), _T("remove_from_double_null()"), 0);\r
630     HeapFree(GetProcessHeap(), 0, key);\r
631     return 2;\r
632   }\r
633 \r
634   /* Copy existing entries.*/\r
635   *newlen = (unsigned long) len;\r
636   TCHAR *t = *newdn;\r
637   for (s = dn; *s; s++) {\r
638     if (fn(s, key, keylen)) {\r
639       len = _tcslen(s) + 1;\r
640       memmove(t, s, len * sizeof(TCHAR));\r
641       t += len;\r
642     }\r
643     for ( ; *s; s++);\r
644   }\r
645 \r
646   HeapFree(GetProcessHeap(), 0, key);\r
647   return 0;\r
648 }\r
649 \r
650 void override_milliseconds(TCHAR *service_name, HKEY key, TCHAR *value, unsigned long *buffer, unsigned long default_value, unsigned long event) {\r
651   unsigned long type = REG_DWORD;\r
652   unsigned long buflen = sizeof(unsigned long);\r
653   bool ok = false;\r
654   unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);\r
655   if (ret != ERROR_SUCCESS) {\r
656     if (ret != ERROR_FILE_NOT_FOUND) {\r
657       if (type != REG_DWORD) {\r
658         TCHAR milliseconds[16];\r
659         _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), default_value);\r
660         log_event(EVENTLOG_WARNING_TYPE, event, service_name, value, milliseconds, 0);\r
661       }\r
662       else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(ret), 0);\r
663     }\r
664   }\r
665   else ok = true;\r
666 \r
667   if (! ok) *buffer = default_value;\r
668 }\r
669 \r
670 /* Open the key of the service itself Services\<service_name>. */\r
671 HKEY open_service_registry(const TCHAR *service_name, REGSAM sam, bool must_exist) {\r
672   /* Get registry */\r
673   TCHAR registry[KEY_LENGTH];\r
674   if (service_registry_path(service_name, false, 0, registry, _countof(registry)) < 0) {\r
675     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REGISTRY, _T("open_service_registry()"), 0);\r
676     return 0;\r
677   }\r
678 \r
679   return open_registry_key(registry, sam, must_exist);\r
680 }\r
681 \r
682 /* Open a subkey of the service Services\<service_name>\<sub>. */\r
683 long open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam, HKEY *key, bool must_exist) {\r
684   /* Get registry */\r
685   TCHAR registry[KEY_LENGTH];\r
686   if (service_registry_path(service_name, true, sub, registry, _countof(registry)) < 0) {\r
687     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REGISTRY, _T("open_registry()"), 0);\r
688     return 0;\r
689   }\r
690 \r
691   return open_registry_key(registry, sam, key, must_exist);\r
692 }\r
693 \r
694 HKEY open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam, bool must_exist) {\r
695   HKEY key;\r
696   long error = open_registry(service_name, sub, sam, &key, must_exist);\r
697   return key;\r
698 }\r
699 \r
700 HKEY open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam) {\r
701   return open_registry(service_name, sub, sam, true);\r
702 }\r
703 \r
704 HKEY open_registry(const TCHAR *service_name, REGSAM sam) {\r
705   return open_registry(service_name, 0, sam, true);\r
706 }\r
707 \r
708 int get_io_parameters(nssm_service_t *service, HKEY key) {\r
709   /* stdin */\r
710   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
711     service->stdin_sharing = service->stdin_disposition = service->stdin_flags = 0;\r
712     ZeroMemory(service->stdin_path, _countof(service->stdin_path) * sizeof(TCHAR));\r
713     return 1;\r
714   }\r
715 \r
716   /* stdout */\r
717   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
718     service->stdout_sharing = service->stdout_disposition = service->stdout_flags = 0;\r
719     ZeroMemory(service->stdout_path, _countof(service->stdout_path) * sizeof(TCHAR));\r
720     return 2;\r
721   }\r
722 \r
723   /* stderr */\r
724   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
725     service->stderr_sharing = service->stderr_disposition = service->stderr_flags = 0;\r
726     ZeroMemory(service->stderr_path, _countof(service->stderr_path) * sizeof(TCHAR));\r
727     return 3;\r
728   }\r
729 \r
730   return 0;\r
731 }\r
732 \r
733 int get_parameters(nssm_service_t *service, STARTUPINFO *si) {\r
734   unsigned long ret;\r
735 \r
736   /* Try to open the registry */\r
737   HKEY key = open_registry(service->name, KEY_READ);\r
738   if (! key) return 1;\r
739 \r
740   /* Don't expand parameters when retrieving for the GUI. */\r
741   bool expand = si ? true : false;\r
742 \r
743   /* Try to get environment variables - may fail */\r
744   get_environment(service->name, key, NSSM_REG_ENV, &service->env, &service->envlen);\r
745   /* Environment variables to add to existing rather than replace - may fail. */\r
746   get_environment(service->name, key, NSSM_REG_ENV_EXTRA, &service->env_extra, &service->env_extralen);\r
747 \r
748   /* Set environment if we are starting the service. */\r
749   if (si) set_service_environment(service);\r
750 \r
751   /* Try to get executable file - MUST succeed */\r
752   if (get_string(key, NSSM_REG_EXE, service->exe, sizeof(service->exe), expand, false, true)) {\r
753     RegCloseKey(key);\r
754     return 3;\r
755   }\r
756 \r
757   /* Try to get flags - may fail and we don't care */\r
758   if (get_string(key, NSSM_REG_FLAGS, service->flags, sizeof(service->flags), expand, false, true)) {\r
759     log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_FLAGS, NSSM_REG_FLAGS, service->name, service->exe, 0);\r
760     ZeroMemory(service->flags, sizeof(service->flags));\r
761   }\r
762 \r
763   /* Try to get startup directory - may fail and we fall back to a default */\r
764   if (get_string(key, NSSM_REG_DIR, service->dir, sizeof(service->dir), expand, true, true) || ! service->dir[0]) {\r
765     _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);\r
766     strip_basename(service->dir);\r
767     if (service->dir[0] == _T('\0')) {\r
768       /* Help! */\r
769       ret = GetWindowsDirectory(service->dir, sizeof(service->dir));\r
770       if (! ret || ret > sizeof(service->dir)) {\r
771         log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_NO_DIR_AND_NO_FALLBACK, NSSM_REG_DIR, service->name, 0);\r
772         RegCloseKey(key);\r
773         return 4;\r
774       }\r
775     }\r
776     log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_DIR, NSSM_REG_DIR, service->name, service->dir, 0);\r
777   }\r
778 \r
779   /* Try to get processor affinity - may fail. */\r
780   TCHAR buffer[512];\r
781   if (get_string(key, NSSM_REG_AFFINITY, buffer, sizeof(buffer), false, false, false) || ! buffer[0]) service->affinity = 0LL;\r
782   else if (affinity_string_to_mask(buffer, &service->affinity)) {\r
783     log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_AFFINITY_MASK, service->name, buffer);\r
784     service->affinity = 0LL;\r
785   }\r
786   else {\r
787     DWORD_PTR affinity, system_affinity;\r
788 \r
789     if (GetProcessAffinityMask(GetCurrentProcess(), &affinity, &system_affinity)) {\r
790       _int64 effective_affinity = service->affinity & system_affinity;\r
791       if (effective_affinity != service->affinity) {\r
792         TCHAR *system = 0;\r
793         if (! affinity_mask_to_string(system_affinity, &system)) {\r
794           TCHAR *effective = 0;\r
795           if (! affinity_mask_to_string(effective_affinity, &effective)) {\r
796             log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_EFFECTIVE_AFFINITY_MASK, service->name, buffer, system, effective, 0);\r
797           }\r
798           HeapFree(GetProcessHeap(), 0, effective);\r
799         }\r
800         HeapFree(GetProcessHeap(), 0, system);\r
801       }\r
802     }\r
803   }\r
804 \r
805   /* Try to get priority - may fail. */\r
806   unsigned long priority;\r
807   if (get_number(key, NSSM_REG_PRIORITY, &priority, false) == 1) {\r
808     if (priority == (priority & priority_mask())) service->priority = priority;\r
809     else log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_PRIORITY, service->name, NSSM_REG_PRIORITY, 0);\r
810   }\r
811 \r
812   /* Try to get hook I/O sharing - may fail. */\r
813   unsigned long hook_share_output_handles;\r
814   if (get_number(key, NSSM_REG_HOOK_SHARE_OUTPUT_HANDLES, &hook_share_output_handles, false) == 1) {\r
815     if (hook_share_output_handles) service->hook_share_output_handles = true;\r
816     else service->hook_share_output_handles = false;\r
817   }\r
818   else hook_share_output_handles = false;\r
819   /* Try to get file rotation settings - may fail. */\r
820   unsigned long rotate_files;\r
821   if (get_number(key, NSSM_REG_ROTATE, &rotate_files, false) == 1) {\r
822     if (rotate_files) service->rotate_files = true;\r
823     else service->rotate_files = false;\r
824   }\r
825   else service->rotate_files = false;\r
826   if (get_number(key, NSSM_REG_ROTATE_ONLINE, &rotate_files, false) == 1) {\r
827     if (rotate_files) service->rotate_stdout_online = service->rotate_stderr_online = true;\r
828     else service->rotate_stdout_online = service->rotate_stderr_online = false;\r
829   }\r
830   else service->rotate_stdout_online = service->rotate_stderr_online = false;\r
831   /* Hook I/O sharing and online rotation need a pipe. */\r
832   service->use_stdout_pipe = service->rotate_stdout_online || hook_share_output_handles;\r
833   service->use_stderr_pipe = service->rotate_stderr_online || hook_share_output_handles;\r
834   if (get_number(key, NSSM_REG_ROTATE_SECONDS, &service->rotate_seconds, false) != 1) service->rotate_seconds = 0;\r
835   if (get_number(key, NSSM_REG_ROTATE_BYTES_LOW, &service->rotate_bytes_low, false) != 1) service->rotate_bytes_low = 0;\r
836   if (get_number(key, NSSM_REG_ROTATE_BYTES_HIGH, &service->rotate_bytes_high, false) != 1) service->rotate_bytes_high = 0;\r
837   override_milliseconds(service->name, key, NSSM_REG_ROTATE_DELAY, &service->rotate_delay, NSSM_ROTATE_DELAY, NSSM_EVENT_BOGUS_THROTTLE);\r
838 \r
839   /* Try to get force new console setting - may fail. */\r
840   if (get_number(key, NSSM_REG_NO_CONSOLE, &service->no_console, false) != 1) service->no_console = 0;\r
841 \r
842   /* Change to startup directory in case stdout/stderr are relative paths. */\r
843   TCHAR cwd[PATH_LENGTH];\r
844   GetCurrentDirectory(_countof(cwd), cwd);\r
845   SetCurrentDirectory(service->dir);\r
846 \r
847   /* Try to get stdout and stderr */\r
848   if (get_io_parameters(service, key)) {\r
849     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);\r
850     RegCloseKey(key);\r
851     SetCurrentDirectory(cwd);\r
852     return 5;\r
853   }\r
854 \r
855   /* Change back in case the startup directory needs to be deleted. */\r
856   SetCurrentDirectory(cwd);\r
857 \r
858   /* Try to get mandatory restart delay */\r
859   override_milliseconds(service->name, key, NSSM_REG_RESTART_DELAY, &service->restart_delay, 0, NSSM_EVENT_BOGUS_RESTART_DELAY);\r
860 \r
861   /* Try to get throttle restart delay */\r
862   override_milliseconds(service->name, key, NSSM_REG_THROTTLE, &service->throttle_delay, NSSM_RESET_THROTTLE_RESTART, NSSM_EVENT_BOGUS_THROTTLE);\r
863 \r
864   /* Try to get service stop flags. */\r
865   unsigned long type = REG_DWORD;\r
866   unsigned long stop_method_skip;\r
867   unsigned long buflen = sizeof(stop_method_skip);\r
868   bool stop_ok = false;\r
869   ret = RegQueryValueEx(key, NSSM_REG_STOP_METHOD_SKIP, 0, &type, (unsigned char *) &stop_method_skip, &buflen);\r
870   if (ret != ERROR_SUCCESS) {\r
871     if (ret != ERROR_FILE_NOT_FOUND) {\r
872       if (type != REG_DWORD) {\r
873         log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_STOP_METHOD_SKIP, service->name, NSSM_REG_STOP_METHOD_SKIP, NSSM, 0);\r
874       }\r
875       else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, NSSM_REG_STOP_METHOD_SKIP, error_string(ret), 0);\r
876     }\r
877   }\r
878   else stop_ok = true;\r
879 \r
880   /* Try all methods except those requested to be skipped. */\r
881   service->stop_method = ~0;\r
882   if (stop_ok) service->stop_method &= ~stop_method_skip;\r
883 \r
884   /* Try to get kill delays - may fail. */\r
885   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
886   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
887   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
888 \r
889   /* Try to get process tree settings - may fail. */\r
890   unsigned long kill_process_tree;\r
891   if (get_number(key, NSSM_REG_KILL_PROCESS_TREE, &kill_process_tree, false) == 1) {\r
892     if (kill_process_tree) service->kill_process_tree = true;\r
893     else service->kill_process_tree = false;\r
894   }\r
895   else service->kill_process_tree = true;\r
896 \r
897   /* Try to get default exit action. */\r
898   bool default_action;\r
899   service->default_exit_action = NSSM_EXIT_RESTART;\r
900   TCHAR action_string[ACTION_LEN];\r
901   if (! get_exit_action(service->name, 0, action_string, &default_action)) {\r
902     for (int i = 0; exit_action_strings[i]; i++) {\r
903       if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {\r
904         service->default_exit_action = i;\r
905         break;\r
906       }\r
907     }\r
908   }\r
909 \r
910   /* Close registry */\r
911   RegCloseKey(key);\r
912 \r
913   return 0;\r
914 }\r
915 \r
916 /*\r
917   Sets the string for the exit action corresponding to the exit code.\r
918 \r
919   ret is a pointer to an unsigned long containing the exit code.\r
920   If ret is NULL, we retrieve the default exit action unconditionally.\r
921 \r
922   action is a buffer which receives the string.\r
923 \r
924   default_action is a pointer to a bool which is set to false if there\r
925   was an explicit string for the given exit code, or true if we are\r
926   returning the default action.\r
927 \r
928   Returns: 0 on success.\r
929            1 on error.\r
930 */\r
931 int get_exit_action(const TCHAR *service_name, unsigned long *ret, TCHAR *action, bool *default_action) {\r
932   /* Are we returning the default action or a status-specific one? */\r
933   *default_action = ! ret;\r
934 \r
935   /* Try to open the registry */\r
936   HKEY key = open_registry(service_name, NSSM_REG_EXIT, KEY_READ);\r
937   if (! key) return 1;\r
938 \r
939   unsigned long type = REG_SZ;\r
940   unsigned long action_len = ACTION_LEN;\r
941 \r
942   TCHAR code[16];\r
943   if (! ret) code[0] = _T('\0');\r
944   else if (_sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), *ret) < 0) {\r
945     RegCloseKey(key);\r
946     return get_exit_action(service_name, 0, action, default_action);\r
947   }\r
948   if (RegQueryValueEx(key, code, 0, &type, (unsigned char *) action, &action_len) != ERROR_SUCCESS) {\r
949     RegCloseKey(key);\r
950     /* Try again with * as the key if an exit code was defined */\r
951     if (ret) return get_exit_action(service_name, 0, action, default_action);\r
952     return 0;\r
953   }\r
954 \r
955   /* Close registry */\r
956   RegCloseKey(key);\r
957 \r
958   return 0;\r
959 }\r
960 \r
961 int set_hook(const TCHAR *service_name, const TCHAR *hook_event, const TCHAR *hook_action, TCHAR *cmd) {\r
962   /* Try to open the registry */\r
963   TCHAR registry[KEY_LENGTH];\r
964   if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, _T("%s\\%s"), NSSM_REG_HOOK, hook_event) < 0) {\r
965     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("hook registry"), _T("set_hook()"), 0);\r
966     return 1;\r
967   }\r
968 \r
969   HKEY key;\r
970   long error;\r
971 \r
972   /* Don't create keys needlessly. */\r
973   if (! _tcslen(cmd)) {\r
974     key = open_registry(service_name, registry, KEY_READ, false);\r
975     if (! key) return 0;\r
976     error = RegQueryValueEx(key, hook_action, 0, 0, 0, 0);\r
977     RegCloseKey(key);\r
978     if (error == ERROR_FILE_NOT_FOUND) return 0;\r
979   }\r
980 \r
981   key = open_registry(service_name, registry, KEY_WRITE);\r
982   if (! key) return 1;\r
983 \r
984   int ret = 1;\r
985   if (_tcslen(cmd)) ret = set_string(key, (TCHAR *) hook_action, cmd, true);\r
986   else {\r
987     error = RegDeleteValue(key, hook_action);\r
988     if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) ret = 0;\r
989   }\r
990 \r
991   /* Close registry */\r
992   RegCloseKey(key);\r
993 \r
994   return ret;\r
995 }\r
996 \r
997 int get_hook(const TCHAR *service_name, const TCHAR *hook_event, const TCHAR *hook_action, TCHAR *buffer, unsigned long buflen) {\r
998   /* Try to open the registry */\r
999   TCHAR registry[KEY_LENGTH];\r
1000   if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, _T("%s\\%s"), NSSM_REG_HOOK, hook_event) < 0) {\r
1001     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("hook registry"), _T("get_hook()"), 0);\r
1002     return 1;\r
1003   }\r
1004   HKEY key;\r
1005   long error = open_registry(service_name, registry, KEY_READ, &key, false);\r
1006   if (! key) {\r
1007     if (error == ERROR_FILE_NOT_FOUND) {\r
1008       ZeroMemory(buffer, buflen);\r
1009       return 0;\r
1010     }\r
1011     return 1;\r
1012   }\r
1013 \r
1014   int ret = expand_parameter(key, (TCHAR *) hook_action, buffer, buflen, true, false);\r
1015 \r
1016   /* Close registry */\r
1017   RegCloseKey(key);\r
1018 \r
1019   return ret;\r
1020 }\r