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