c78cb8cfa3551dd75c6fa63021b130adb831e679
[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 HKEY open_service_registry(const TCHAR *service_name, REGSAM sam, bool must_exist) {\r
671   /* Get registry */\r
672   TCHAR registry[KEY_LENGTH];\r
673   if (service_registry_path(service_name, false, 0, registry, _countof(registry)) < 0) {\r
674     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REGISTRY, _T("open_service_registry()"), 0);\r
675     return 0;\r
676   }\r
677 \r
678   return open_registry_key(registry, sam, must_exist);\r
679 }\r
680 \r
681 long open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam, HKEY *key, bool must_exist) {\r
682   /* Get registry */\r
683   TCHAR registry[KEY_LENGTH];\r
684   if (service_registry_path(service_name, true, sub, registry, _countof(registry)) < 0) {\r
685     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REGISTRY, _T("open_registry()"), 0);\r
686     return 0;\r
687   }\r
688 \r
689   return open_registry_key(registry, sam, key, must_exist);\r
690 }\r
691 \r
692 HKEY open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam, bool must_exist) {\r
693   HKEY key;\r
694   long error = open_registry(service_name, sub, sam, &key, must_exist);\r
695   return key;\r
696 }\r
697 \r
698 HKEY open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam) {\r
699   return open_registry(service_name, sub, sam, true);\r
700 }\r
701 \r
702 HKEY open_registry(const TCHAR *service_name, REGSAM sam) {\r
703   return open_registry(service_name, 0, sam, true);\r
704 }\r
705 \r
706 int get_io_parameters(nssm_service_t *service, HKEY key) {\r
707   /* stdin */\r
708   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
709     service->stdin_sharing = service->stdin_disposition = service->stdin_flags = 0;\r
710     ZeroMemory(service->stdin_path, _countof(service->stdin_path) * sizeof(TCHAR));\r
711     return 1;\r
712   }\r
713 \r
714   /* stdout */\r
715   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
716     service->stdout_sharing = service->stdout_disposition = service->stdout_flags = 0;\r
717     ZeroMemory(service->stdout_path, _countof(service->stdout_path) * sizeof(TCHAR));\r
718     return 2;\r
719   }\r
720 \r
721   /* stderr */\r
722   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
723     service->stderr_sharing = service->stderr_disposition = service->stderr_flags = 0;\r
724     ZeroMemory(service->stderr_path, _countof(service->stderr_path) * sizeof(TCHAR));\r
725     return 3;\r
726   }\r
727 \r
728   return 0;\r
729 }\r
730 \r
731 int get_parameters(nssm_service_t *service, STARTUPINFO *si) {\r
732   unsigned long ret;\r
733 \r
734   /* Try to open the registry */\r
735   HKEY key = open_registry(service->name, KEY_READ);\r
736   if (! key) return 1;\r
737 \r
738   /* Don't expand parameters when retrieving for the GUI. */\r
739   bool expand = si ? true : false;\r
740 \r
741   /* Try to get environment variables - may fail */\r
742   get_environment(service->name, key, NSSM_REG_ENV, &service->env, &service->envlen);\r
743   /* Environment variables to add to existing rather than replace - may fail. */\r
744   get_environment(service->name, key, NSSM_REG_ENV_EXTRA, &service->env_extra, &service->env_extralen);\r
745 \r
746   /* Set environment if we are starting the service. */\r
747   if (si) set_service_environment(service);\r
748 \r
749   /* Try to get executable file - MUST succeed */\r
750   if (get_string(key, NSSM_REG_EXE, service->exe, sizeof(service->exe), expand, false, true)) {\r
751     RegCloseKey(key);\r
752     return 3;\r
753   }\r
754 \r
755   /* Try to get flags - may fail and we don't care */\r
756   if (get_string(key, NSSM_REG_FLAGS, service->flags, sizeof(service->flags), expand, false, true)) {\r
757     log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_FLAGS, NSSM_REG_FLAGS, service->name, service->exe, 0);\r
758     ZeroMemory(service->flags, sizeof(service->flags));\r
759   }\r
760 \r
761   /* Try to get startup directory - may fail and we fall back to a default */\r
762   if (get_string(key, NSSM_REG_DIR, service->dir, sizeof(service->dir), expand, true, true) || ! service->dir[0]) {\r
763     _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);\r
764     strip_basename(service->dir);\r
765     if (service->dir[0] == _T('\0')) {\r
766       /* Help! */\r
767       ret = GetWindowsDirectory(service->dir, sizeof(service->dir));\r
768       if (! ret || ret > sizeof(service->dir)) {\r
769         log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_NO_DIR_AND_NO_FALLBACK, NSSM_REG_DIR, service->name, 0);\r
770         RegCloseKey(key);\r
771         return 4;\r
772       }\r
773     }\r
774     log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_DIR, NSSM_REG_DIR, service->name, service->dir, 0);\r
775   }\r
776 \r
777   /* Try to get processor affinity - may fail. */\r
778   TCHAR buffer[512];\r
779   if (get_string(key, NSSM_REG_AFFINITY, buffer, sizeof(buffer), false, false, false) || ! buffer[0]) service->affinity = 0LL;\r
780   else if (affinity_string_to_mask(buffer, &service->affinity)) {\r
781     log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_AFFINITY_MASK, service->name, buffer);\r
782     service->affinity = 0LL;\r
783   }\r
784   else {\r
785     DWORD_PTR affinity, system_affinity;\r
786 \r
787     if (GetProcessAffinityMask(GetCurrentProcess(), &affinity, &system_affinity)) {\r
788       _int64 effective_affinity = service->affinity & system_affinity;\r
789       if (effective_affinity != service->affinity) {\r
790         TCHAR *system = 0;\r
791         if (! affinity_mask_to_string(system_affinity, &system)) {\r
792           TCHAR *effective = 0;\r
793           if (! affinity_mask_to_string(effective_affinity, &effective)) {\r
794             log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_EFFECTIVE_AFFINITY_MASK, service->name, buffer, system, effective, 0);\r
795           }\r
796           HeapFree(GetProcessHeap(), 0, effective);\r
797         }\r
798         HeapFree(GetProcessHeap(), 0, system);\r
799       }\r
800     }\r
801   }\r
802 \r
803   /* Try to get priority - may fail. */\r
804   unsigned long priority;\r
805   if (get_number(key, NSSM_REG_PRIORITY, &priority, false) == 1) {\r
806     if (priority == (priority & priority_mask())) service->priority = priority;\r
807     else log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_PRIORITY, service->name, NSSM_REG_PRIORITY, 0);\r
808   }\r
809 \r
810   /* Try to get hook I/O sharing - may fail. */\r
811   unsigned long hook_share_output_handles;\r
812   if (get_number(key, NSSM_REG_HOOK_SHARE_OUTPUT_HANDLES, &hook_share_output_handles, false) == 1) {\r
813     if (hook_share_output_handles) service->hook_share_output_handles = true;\r
814     else service->hook_share_output_handles = false;\r
815   }\r
816   else hook_share_output_handles = false;\r
817   /* Try to get file rotation settings - may fail. */\r
818   unsigned long rotate_files;\r
819   if (get_number(key, NSSM_REG_ROTATE, &rotate_files, false) == 1) {\r
820     if (rotate_files) service->rotate_files = true;\r
821     else service->rotate_files = false;\r
822   }\r
823   else service->rotate_files = false;\r
824   if (get_number(key, NSSM_REG_ROTATE_ONLINE, &rotate_files, false) == 1) {\r
825     if (rotate_files) service->rotate_stdout_online = service->rotate_stderr_online = true;\r
826     else service->rotate_stdout_online = service->rotate_stderr_online = false;\r
827   }\r
828   else service->rotate_stdout_online = service->rotate_stderr_online = false;\r
829   /* Hook I/O sharing and online rotation need a pipe. */\r
830   service->use_stdout_pipe = service->rotate_stdout_online || hook_share_output_handles;\r
831   service->use_stderr_pipe = service->rotate_stderr_online || hook_share_output_handles;\r
832   if (get_number(key, NSSM_REG_ROTATE_SECONDS, &service->rotate_seconds, false) != 1) service->rotate_seconds = 0;\r
833   if (get_number(key, NSSM_REG_ROTATE_BYTES_LOW, &service->rotate_bytes_low, false) != 1) service->rotate_bytes_low = 0;\r
834   if (get_number(key, NSSM_REG_ROTATE_BYTES_HIGH, &service->rotate_bytes_high, false) != 1) service->rotate_bytes_high = 0;\r
835   override_milliseconds(service->name, key, NSSM_REG_ROTATE_DELAY, &service->rotate_delay, NSSM_ROTATE_DELAY, NSSM_EVENT_BOGUS_THROTTLE);\r
836 \r
837   /* Try to get force new console setting - may fail. */\r
838   if (get_number(key, NSSM_REG_NO_CONSOLE, &service->no_console, false) != 1) service->no_console = 0;\r
839 \r
840   /* Change to startup directory in case stdout/stderr are relative paths. */\r
841   TCHAR cwd[PATH_LENGTH];\r
842   GetCurrentDirectory(_countof(cwd), cwd);\r
843   SetCurrentDirectory(service->dir);\r
844 \r
845   /* Try to get stdout and stderr */\r
846   if (get_io_parameters(service, key)) {\r
847     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);\r
848     RegCloseKey(key);\r
849     SetCurrentDirectory(cwd);\r
850     return 5;\r
851   }\r
852 \r
853   /* Change back in case the startup directory needs to be deleted. */\r
854   SetCurrentDirectory(cwd);\r
855 \r
856   /* Try to get mandatory restart delay */\r
857   override_milliseconds(service->name, key, NSSM_REG_RESTART_DELAY, &service->restart_delay, 0, NSSM_EVENT_BOGUS_RESTART_DELAY);\r
858 \r
859   /* Try to get throttle restart delay */\r
860   override_milliseconds(service->name, key, NSSM_REG_THROTTLE, &service->throttle_delay, NSSM_RESET_THROTTLE_RESTART, NSSM_EVENT_BOGUS_THROTTLE);\r
861 \r
862   /* Try to get service stop flags. */\r
863   unsigned long type = REG_DWORD;\r
864   unsigned long stop_method_skip;\r
865   unsigned long buflen = sizeof(stop_method_skip);\r
866   bool stop_ok = false;\r
867   ret = RegQueryValueEx(key, NSSM_REG_STOP_METHOD_SKIP, 0, &type, (unsigned char *) &stop_method_skip, &buflen);\r
868   if (ret != ERROR_SUCCESS) {\r
869     if (ret != ERROR_FILE_NOT_FOUND) {\r
870       if (type != REG_DWORD) {\r
871         log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_STOP_METHOD_SKIP, service->name, NSSM_REG_STOP_METHOD_SKIP, NSSM, 0);\r
872       }\r
873       else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, NSSM_REG_STOP_METHOD_SKIP, error_string(ret), 0);\r
874     }\r
875   }\r
876   else stop_ok = true;\r
877 \r
878   /* Try all methods except those requested to be skipped. */\r
879   service->stop_method = ~0;\r
880   if (stop_ok) service->stop_method &= ~stop_method_skip;\r
881 \r
882   /* Try to get kill delays - may fail. */\r
883   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
884   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
885   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
886 \r
887   /* Try to get process tree settings - may fail. */\r
888   unsigned long kill_process_tree;\r
889   if (get_number(key, NSSM_REG_KILL_PROCESS_TREE, &kill_process_tree, false) == 1) {\r
890     if (kill_process_tree) service->kill_process_tree = true;\r
891     else service->kill_process_tree = false;\r
892   }\r
893   else service->kill_process_tree = true;\r
894 \r
895   /* Try to get default exit action. */\r
896   bool default_action;\r
897   service->default_exit_action = NSSM_EXIT_RESTART;\r
898   TCHAR action_string[ACTION_LEN];\r
899   if (! get_exit_action(service->name, 0, action_string, &default_action)) {\r
900     for (int i = 0; exit_action_strings[i]; i++) {\r
901       if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {\r
902         service->default_exit_action = i;\r
903         break;\r
904       }\r
905     }\r
906   }\r
907 \r
908   /* Close registry */\r
909   RegCloseKey(key);\r
910 \r
911   return 0;\r
912 }\r
913 \r
914 /*\r
915   Sets the string for the exit action corresponding to the exit code.\r
916 \r
917   ret is a pointer to an unsigned long containing the exit code.\r
918   If ret is NULL, we retrieve the default exit action unconditionally.\r
919 \r
920   action is a buffer which receives the string.\r
921 \r
922   default_action is a pointer to a bool which is set to false if there\r
923   was an explicit string for the given exit code, or true if we are\r
924   returning the default action.\r
925 \r
926   Returns: 0 on success.\r
927            1 on error.\r
928 */\r
929 int get_exit_action(const TCHAR *service_name, unsigned long *ret, TCHAR *action, bool *default_action) {\r
930   /* Are we returning the default action or a status-specific one? */\r
931   *default_action = ! ret;\r
932 \r
933   /* Try to open the registry */\r
934   HKEY key = open_registry(service_name, NSSM_REG_EXIT, KEY_READ);\r
935   if (! key) return 1;\r
936 \r
937   unsigned long type = REG_SZ;\r
938   unsigned long action_len = ACTION_LEN;\r
939 \r
940   TCHAR code[16];\r
941   if (! ret) code[0] = _T('\0');\r
942   else if (_sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), *ret) < 0) {\r
943     RegCloseKey(key);\r
944     return get_exit_action(service_name, 0, action, default_action);\r
945   }\r
946   if (RegQueryValueEx(key, code, 0, &type, (unsigned char *) action, &action_len) != ERROR_SUCCESS) {\r
947     RegCloseKey(key);\r
948     /* Try again with * as the key if an exit code was defined */\r
949     if (ret) return get_exit_action(service_name, 0, action, default_action);\r
950     return 0;\r
951   }\r
952 \r
953   /* Close registry */\r
954   RegCloseKey(key);\r
955 \r
956   return 0;\r
957 }\r
958 \r
959 int set_hook(const TCHAR *service_name, const TCHAR *hook_event, const TCHAR *hook_action, TCHAR *cmd) {\r
960   /* Try to open the registry */\r
961   TCHAR registry[KEY_LENGTH];\r
962   if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, _T("%s\\%s"), NSSM_REG_HOOK, hook_event) < 0) {\r
963     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("hook registry"), _T("set_hook()"), 0);\r
964     return 1;\r
965   }\r
966 \r
967   HKEY key;\r
968   long error;\r
969 \r
970   /* Don't create keys needlessly. */\r
971   if (! _tcslen(cmd)) {\r
972     key = open_registry(service_name, registry, KEY_READ, false);\r
973     if (! key) return 0;\r
974     error = RegQueryValueEx(key, hook_action, 0, 0, 0, 0);\r
975     RegCloseKey(key);\r
976     if (error == ERROR_FILE_NOT_FOUND) return 0;\r
977   }\r
978 \r
979   key = open_registry(service_name, registry, KEY_WRITE);\r
980   if (! key) return 1;\r
981 \r
982   int ret = 1;\r
983   if (_tcslen(cmd)) ret = set_string(key, (TCHAR *) hook_action, cmd, true);\r
984   else {\r
985     error = RegDeleteValue(key, hook_action);\r
986     if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) ret = 0;\r
987   }\r
988 \r
989   /* Close registry */\r
990   RegCloseKey(key);\r
991 \r
992   return ret;\r
993 }\r
994 \r
995 int get_hook(const TCHAR *service_name, const TCHAR *hook_event, const TCHAR *hook_action, TCHAR *buffer, unsigned long buflen) {\r
996   /* Try to open the registry */\r
997   TCHAR registry[KEY_LENGTH];\r
998   if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, _T("%s\\%s"), NSSM_REG_HOOK, hook_event) < 0) {\r
999     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("hook registry"), _T("get_hook()"), 0);\r
1000     return 1;\r
1001   }\r
1002   HKEY key;\r
1003   long error = open_registry(service_name, registry, KEY_READ, &key, false);\r
1004   if (! key) {\r
1005     if (error == ERROR_FILE_NOT_FOUND) {\r
1006       ZeroMemory(buffer, buflen);\r
1007       return 0;\r
1008     }\r
1009     return 1;\r
1010   }\r
1011 \r
1012   int ret = expand_parameter(key, (TCHAR *) hook_action, buffer, buflen, true, false);\r
1013 \r
1014   /* Close registry */\r
1015   RegCloseKey(key);\r
1016 \r
1017   return ret;\r
1018 }\r