3 extern const TCHAR *exit_action_strings[];
\r
5 int create_messages() {
\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
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
19 /* Get path of this program */
\r
20 TCHAR path[PATH_LENGTH];
\r
21 GetModuleFileName(0, path, _countof(path));
\r
23 /* Try to register the module but don't worry so much on failure */
\r
24 RegSetValueEx(key, _T("EventMessageFile"), 0, REG_SZ, (const unsigned char *) path, (unsigned long) (_tcslen(path) + 1) * sizeof(TCHAR));
\r
25 unsigned long types = EVENTLOG_INFORMATION_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_ERROR_TYPE;
\r
26 RegSetValueEx(key, _T("TypesSupported"), 0, REG_DWORD, (const unsigned char *) &types, sizeof(types));
\r
31 int create_parameters(nssm_service_t *service, bool editing) {
\r
32 /* Try to open the registry */
\r
33 HKEY key = open_registry(service->name, KEY_WRITE);
\r
34 if (! key) return 1;
\r
36 /* Try to create the parameters */
\r
37 if (set_expand_string(key, NSSM_REG_EXE, service->exe)) {
\r
38 RegDeleteKey(HKEY_LOCAL_MACHINE, NSSM_REGISTRY);
\r
42 if (set_expand_string(key, NSSM_REG_FLAGS, service->flags)) {
\r
43 RegDeleteKey(HKEY_LOCAL_MACHINE, NSSM_REGISTRY);
\r
47 if (set_expand_string(key, NSSM_REG_DIR, service->dir)) {
\r
48 RegDeleteKey(HKEY_LOCAL_MACHINE, NSSM_REGISTRY);
\r
53 /* Other non-default parameters. May fail. */
\r
54 if (service->priority != NORMAL_PRIORITY_CLASS) set_number(key, NSSM_REG_PRIORITY, service->priority);
\r
55 else if (editing) RegDeleteValue(key, NSSM_REG_PRIORITY);
\r
56 if (service->affinity) {
\r
58 if (! affinity_mask_to_string(service->affinity, &string)) {
\r
59 if (RegSetValueEx(key, NSSM_REG_AFFINITY, 0, REG_SZ, (const unsigned char *) string, (unsigned long) (_tcslen(string) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
60 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_AFFINITY, error_string(GetLastError()), 0);
\r
61 HeapFree(GetProcessHeap(), 0, string);
\r
65 if (string) HeapFree(GetProcessHeap(), 0, string);
\r
67 else if (editing) RegDeleteValue(key, NSSM_REG_AFFINITY);
\r
68 unsigned long stop_method_skip = ~service->stop_method;
\r
69 if (stop_method_skip) set_number(key, NSSM_REG_STOP_METHOD_SKIP, stop_method_skip);
\r
70 else if (editing) RegDeleteValue(key, NSSM_REG_STOP_METHOD_SKIP);
\r
71 if (service->default_exit_action < NSSM_NUM_EXIT_ACTIONS) create_exit_action(service->name, exit_action_strings[service->default_exit_action], editing);
\r
72 if (service->restart_delay) set_number(key, NSSM_REG_RESTART_DELAY, service->restart_delay);
\r
73 else if (editing) RegDeleteValue(key, NSSM_REG_RESTART_DELAY);
\r
74 if (service->throttle_delay != NSSM_RESET_THROTTLE_RESTART) set_number(key, NSSM_REG_THROTTLE, service->throttle_delay);
\r
75 else if (editing) RegDeleteValue(key, NSSM_REG_THROTTLE);
\r
76 if (service->kill_console_delay != NSSM_KILL_CONSOLE_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, service->kill_console_delay);
\r
77 else if (editing) RegDeleteValue(key, NSSM_REG_KILL_CONSOLE_GRACE_PERIOD);
\r
78 if (service->kill_window_delay != NSSM_KILL_WINDOW_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_WINDOW_GRACE_PERIOD, service->kill_window_delay);
\r
79 else if (editing) RegDeleteValue(key, NSSM_REG_KILL_WINDOW_GRACE_PERIOD);
\r
80 if (service->kill_threads_delay != NSSM_KILL_THREADS_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_THREADS_GRACE_PERIOD, service->kill_threads_delay);
\r
81 else if (editing) RegDeleteValue(key, NSSM_REG_KILL_THREADS_GRACE_PERIOD);
\r
82 if (! service->kill_process_tree) set_number(key, NSSM_REG_KILL_PROCESS_TREE, 0);
\r
83 else if (editing) RegDeleteValue(key, NSSM_REG_KILL_PROCESS_TREE);
\r
84 if (service->stdin_path[0] || editing) {
\r
85 if (service->stdin_path[0]) set_expand_string(key, NSSM_REG_STDIN, service->stdin_path);
\r
86 else if (editing) RegDeleteValue(key, NSSM_REG_STDIN);
\r
87 if (service->stdin_sharing != NSSM_STDIN_SHARING) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_SHARING, service->stdin_sharing);
\r
88 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_SHARING);
\r
89 if (service->stdin_disposition != NSSM_STDIN_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_DISPOSITION, service->stdin_disposition);
\r
90 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_DISPOSITION);
\r
91 if (service->stdin_flags != NSSM_STDIN_FLAGS) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_FLAGS, service->stdin_flags);
\r
92 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_FLAGS);
\r
94 if (service->stdout_path[0] || editing) {
\r
95 if (service->stdout_path[0]) set_expand_string(key, NSSM_REG_STDOUT, service->stdout_path);
\r
96 else if (editing) RegDeleteValue(key, NSSM_REG_STDOUT);
\r
97 if (service->stdout_sharing != NSSM_STDOUT_SHARING) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_SHARING, service->stdout_sharing);
\r
98 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_SHARING);
\r
99 if (service->stdout_disposition != NSSM_STDOUT_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_DISPOSITION, service->stdout_disposition);
\r
100 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_DISPOSITION);
\r
101 if (service->stdout_flags != NSSM_STDOUT_FLAGS) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_FLAGS, service->stdout_flags);
\r
102 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_FLAGS);
\r
104 if (service->stderr_path[0] || editing) {
\r
105 if (service->stderr_path[0]) set_expand_string(key, NSSM_REG_STDERR, service->stderr_path);
\r
106 else if (editing) RegDeleteValue(key, NSSM_REG_STDERR);
\r
107 if (service->stderr_sharing != NSSM_STDERR_SHARING) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_SHARING, service->stderr_sharing);
\r
108 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_SHARING);
\r
109 if (service->stderr_disposition != NSSM_STDERR_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_DISPOSITION, service->stderr_disposition);
\r
110 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_DISPOSITION);
\r
111 if (service->stderr_flags != NSSM_STDERR_FLAGS) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_FLAGS, service->stderr_flags);
\r
112 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_FLAGS);
\r
114 if (service->rotate_files) set_number(key, NSSM_REG_ROTATE, 1);
\r
115 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE);
\r
116 if (service->rotate_stdout_online) set_number(key, NSSM_REG_ROTATE_ONLINE, 1);
\r
117 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_ONLINE);
\r
118 if (service->rotate_seconds) set_number(key, NSSM_REG_ROTATE_SECONDS, service->rotate_seconds);
\r
119 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_SECONDS);
\r
120 if (service->rotate_bytes_low) set_number(key, NSSM_REG_ROTATE_BYTES_LOW, service->rotate_bytes_low);
\r
121 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_BYTES_LOW);
\r
122 if (service->rotate_bytes_high) set_number(key, NSSM_REG_ROTATE_BYTES_HIGH, service->rotate_bytes_high);
\r
123 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_BYTES_HIGH);
\r
124 if (service->no_console) set_number(key, NSSM_REG_NO_CONSOLE, 1);
\r
125 else if (editing) RegDeleteValue(key, NSSM_REG_NO_CONSOLE);
\r
128 if (service->env) {
\r
129 if (RegSetValueEx(key, NSSM_REG_ENV, 0, REG_MULTI_SZ, (const unsigned char *) service->env, (unsigned long) service->envlen * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
130 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV, error_string(GetLastError()), 0);
\r
133 else if (editing) RegDeleteValue(key, NSSM_REG_ENV);
\r
134 if (service->env_extra) {
\r
135 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
136 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV_EXTRA, error_string(GetLastError()), 0);
\r
139 else if (editing) RegDeleteValue(key, NSSM_REG_ENV_EXTRA);
\r
141 /* Close registry. */
\r
147 int create_exit_action(TCHAR *service_name, const TCHAR *action_string, bool editing) {
\r
149 TCHAR registry[KEY_LENGTH];
\r
150 if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, NSSM_REGISTRY _T("\\%s"), service_name, NSSM_REG_EXIT) < 0) {
\r
151 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("NSSM_REG_EXIT"), _T("create_exit_action()"), 0);
\r
155 /* Try to open the registry */
\r
157 unsigned long disposition;
\r
158 if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, KEY_WRITE, 0, &key, &disposition) != ERROR_SUCCESS) {
\r
159 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
163 /* Do nothing if the key already existed */
\r
164 if (disposition == REG_OPENED_EXISTING_KEY && ! editing) {
\r
169 /* Create the default value */
\r
170 if (RegSetValueEx(key, 0, 0, REG_SZ, (const unsigned char *) action_string, (unsigned long) (_tcslen(action_string) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
171 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_EXIT, error_string(GetLastError()), 0);
\r
176 /* Close registry */
\r
182 int get_environment(TCHAR *service_name, HKEY key, TCHAR *value, TCHAR **env, unsigned long *envlen) {
\r
183 unsigned long type = REG_MULTI_SZ;
\r
185 /* Dummy test to find buffer size */
\r
186 unsigned long ret = RegQueryValueEx(key, value, 0, &type, NULL, envlen);
\r
187 if (ret != ERROR_SUCCESS) {
\r
190 /* The service probably doesn't have any environment configured */
\r
191 if (ret == ERROR_FILE_NOT_FOUND) return 0;
\r
192 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
196 if (type != REG_MULTI_SZ) {
\r
199 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_INVALID_ENVIRONMENT_STRING_TYPE, value, service_name, 0);
\r
203 /* Probably not possible */
\r
204 if (! *envlen) return 0;
\r
206 /* Previously initialised? */
\r
207 if (*env) HeapFree(GetProcessHeap(), 0, *env);
\r
209 *env = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, *envlen);
\r
212 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, value, _T("get_environment()"), 0);
\r
216 /* Actually get the strings */
\r
217 ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) *env, envlen);
\r
218 if (ret != ERROR_SUCCESS) {
\r
219 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
220 HeapFree(GetProcessHeap(), 0, *env);
\r
230 int get_string(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool expand, bool sanitise, bool must_exist) {
\r
231 TCHAR *buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, datalen);
\r
233 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, value, _T("get_string()"), 0);
\r
237 ZeroMemory(data, datalen);
\r
239 unsigned long type = REG_EXPAND_SZ;
\r
240 unsigned long buflen = datalen;
\r
242 unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);
\r
243 if (ret != ERROR_SUCCESS) {
\r
244 unsigned long error = GetLastError();
\r
245 HeapFree(GetProcessHeap(), 0, buffer);
\r
247 if (ret == ERROR_FILE_NOT_FOUND) {
\r
248 if (! must_exist) return 0;
\r
251 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(error), 0);
\r
255 /* Paths aren't allowed to contain quotes. */
\r
256 if (sanitise) PathUnquoteSpaces(buffer);
\r
258 /* Do we want to expand the string? */
\r
260 if (type == REG_EXPAND_SZ) type = REG_SZ;
\r
263 /* Technically we shouldn't expand environment strings from REG_SZ values */
\r
264 if (type != REG_EXPAND_SZ) {
\r
265 memmove(data, buffer, buflen);
\r
266 HeapFree(GetProcessHeap(), 0, buffer);
\r
270 ret = ExpandEnvironmentStrings((TCHAR *) buffer, data, datalen);
\r
271 if (! ret || ret > datalen) {
\r
272 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_EXPANDENVIRONMENTSTRINGS_FAILED, buffer, error_string(GetLastError()), 0);
\r
273 HeapFree(GetProcessHeap(), 0, buffer);
\r
277 HeapFree(GetProcessHeap(), 0, buffer);
\r
281 int get_string(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise) {
\r
282 return get_string(key, value, data, datalen, false, sanitise, true);
\r
285 int expand_parameter(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise, bool must_exist) {
\r
286 return get_string(key, value, data, datalen, true, sanitise, must_exist);
\r
289 int expand_parameter(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise) {
\r
290 return expand_parameter(key, value, data, datalen, sanitise, true);
\r
294 Sets a string in the registry.
\r
295 Returns: 0 if it was set.
\r
298 int set_string(HKEY key, TCHAR *value, TCHAR *string, bool expand) {
\r
299 unsigned long type = expand ? REG_EXPAND_SZ : REG_SZ;
\r
300 if (RegSetValueEx(key, value, 0, type, (const unsigned char *) string, (unsigned long) (_tcslen(string) + 1) * sizeof(TCHAR)) == ERROR_SUCCESS) return 0;
\r
301 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
305 int set_string(HKEY key, TCHAR *value, TCHAR *string) {
\r
306 return set_string(key, value, string, false);
\r
310 int set_expand_string(HKEY key, TCHAR *value, TCHAR *string) {
\r
311 return set_string(key, value, string, true);
\r
316 Set an unsigned long in the registry.
\r
317 Returns: 0 if it was set.
\r
320 int set_number(HKEY key, TCHAR *value, unsigned long number) {
\r
321 if (RegSetValueEx(key, value, 0, REG_DWORD, (const unsigned char *) &number, sizeof(number)) == ERROR_SUCCESS) return 0;
\r
322 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
327 Query an unsigned long from the registry.
\r
328 Returns: 1 if a number was retrieved.
\r
329 0 if none was found and must_exist is false.
\r
330 -1 if none was found and must_exist is true.
\r
333 int get_number(HKEY key, TCHAR *value, unsigned long *number, bool must_exist) {
\r
334 unsigned long type = REG_DWORD;
\r
335 unsigned long number_len = sizeof(unsigned long);
\r
337 int ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) number, &number_len);
\r
338 if (ret == ERROR_SUCCESS) return 1;
\r
340 if (ret == ERROR_FILE_NOT_FOUND) {
\r
341 if (! must_exist) return 0;
\r
344 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
345 if (ret == ERROR_FILE_NOT_FOUND) return -1;
\r
350 int get_number(HKEY key, TCHAR *value, unsigned long *number) {
\r
351 return get_number(key, value, number, true);
\r
354 /* Replace NULL with CRLF. Leave NULL NULL as the end marker. */
\r
355 int format_double_null(TCHAR *dn, unsigned long dnlen, TCHAR **formatted, unsigned long *newlen) {
\r
356 unsigned long i, j;
\r
364 for (i = 0; i < dnlen; i++) if (! dn[i] && dn[i + 1]) ++*newlen;
\r
366 *formatted = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *newlen * sizeof(TCHAR));
\r
367 if (! *formatted) {
\r
372 for (i = 0, j = 0; i < dnlen; i++) {
\r
373 (*formatted)[j] = dn[i];
\r
376 (*formatted)[j] = _T('\r');
\r
377 (*formatted)[++j] = _T('\n');
\r
386 /* Strip CR and replace LF with NULL. */
\r
387 int unformat_double_null(TCHAR *dn, unsigned long dnlen, TCHAR **unformatted, unsigned long *newlen) {
\r
388 unsigned long i, j;
\r
396 for (i = 0; i < dnlen; i++) if (dn[i] != _T('\r')) ++*newlen;
\r
398 /* Skip blank lines. */
\r
399 for (i = 0; i < dnlen; i++) {
\r
400 if (dn[i] == _T('\r') && dn[i + 1] == _T('\n')) {
\r
401 /* This is the last CRLF. */
\r
402 if (i >= dnlen - 2) break;
\r
405 Strip at the start of the block or if the next characters are
\r
408 if (! i || (dn[i + 2] == _T('\r') && dn[i + 3] == _T('\n'))) {
\r
409 for (j = i + 2; j < dnlen; j++) dn[j - 2] = dn[j];
\r
410 dn[dnlen--] = _T('\0');
\r
411 dn[dnlen--] = _T('\0');
\r
418 /* Must end with two NULLs. */
\r
421 *unformatted = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *newlen * sizeof(TCHAR));
\r
422 if (! *unformatted) return 1;
\r
424 for (i = 0, j = 0; i < dnlen; i++) {
\r
425 if (dn[i] == _T('\r')) continue;
\r
426 if (dn[i] == _T('\n')) (*unformatted)[j] = _T('\0');
\r
427 else (*unformatted)[j] = dn[i];
\r
434 void override_milliseconds(TCHAR *service_name, HKEY key, TCHAR *value, unsigned long *buffer, unsigned long default_value, unsigned long event) {
\r
435 unsigned long type = REG_DWORD;
\r
436 unsigned long buflen = sizeof(unsigned long);
\r
438 unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);
\r
439 if (ret != ERROR_SUCCESS) {
\r
440 if (ret != ERROR_FILE_NOT_FOUND) {
\r
441 if (type != REG_DWORD) {
\r
442 TCHAR milliseconds[16];
\r
443 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), default_value);
\r
444 log_event(EVENTLOG_WARNING_TYPE, event, service_name, value, milliseconds, 0);
\r
446 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
451 if (! ok) *buffer = default_value;
\r
454 HKEY open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam) {
\r
456 TCHAR registry[KEY_LENGTH];
\r
460 if (sub) ret = _sntprintf_s(registry, _countof(registry), _TRUNCATE, NSSM_REGISTRY _T("\\%s"), service_name, sub);
\r
461 else ret = _sntprintf_s(registry, _countof(registry), _TRUNCATE, NSSM_REGISTRY, service_name);
\r
463 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("NSSM_REGISTRY"), _T("open_registry()"), 0);
\r
467 if (sam & KEY_WRITE) {
\r
468 if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, sam, 0, &key, 0) != ERROR_SUCCESS) {
\r
469 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
474 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, registry, 0, sam, &key) != ERROR_SUCCESS) {
\r
475 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
483 HKEY open_registry(const TCHAR *service_name, REGSAM sam) {
\r
484 return open_registry(service_name, 0, sam);
\r
487 int get_io_parameters(nssm_service_t *service, HKEY key) {
\r
489 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)) {
\r
490 service->stdin_sharing = service->stdin_disposition = service->stdin_flags = 0;
\r
491 ZeroMemory(service->stdin_path, _countof(service->stdin_path) * sizeof(TCHAR));
\r
496 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)) {
\r
497 service->stdout_sharing = service->stdout_disposition = service->stdout_flags = 0;
\r
498 ZeroMemory(service->stdout_path, _countof(service->stdout_path) * sizeof(TCHAR));
\r
503 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)) {
\r
504 service->stderr_sharing = service->stderr_disposition = service->stderr_flags = 0;
\r
505 ZeroMemory(service->stderr_path, _countof(service->stderr_path) * sizeof(TCHAR));
\r
512 int get_parameters(nssm_service_t *service, STARTUPINFO *si) {
\r
515 /* Try to open the registry */
\r
516 HKEY key = open_registry(service->name, KEY_READ);
\r
517 if (! key) return 1;
\r
519 /* Don't expand parameters when retrieving for the GUI. */
\r
520 bool expand = si ? true : false;
\r
522 /* Try to get executable file - MUST succeed */
\r
523 if (get_string(key, NSSM_REG_EXE, service->exe, sizeof(service->exe), expand, false, true)) {
\r
528 /* Try to get flags - may fail and we don't care */
\r
529 if (get_string(key, NSSM_REG_FLAGS, service->flags, sizeof(service->flags), expand, false, true)) {
\r
530 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_FLAGS, NSSM_REG_FLAGS, service->name, service->exe, 0);
\r
531 ZeroMemory(service->flags, sizeof(service->flags));
\r
534 /* Try to get startup directory - may fail and we fall back to a default */
\r
535 if (get_string(key, NSSM_REG_DIR, service->dir, sizeof(service->dir), expand, true, true) || ! service->dir[0]) {
\r
536 _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);
\r
537 strip_basename(service->dir);
\r
538 if (service->dir[0] == _T('\0')) {
\r
540 ret = GetWindowsDirectory(service->dir, sizeof(service->dir));
\r
541 if (! ret || ret > sizeof(service->dir)) {
\r
542 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_NO_DIR_AND_NO_FALLBACK, NSSM_REG_DIR, service->name, 0);
\r
547 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_DIR, NSSM_REG_DIR, service->name, service->dir, 0);
\r
550 /* Try to get processor affinity - may fail. */
\r
552 if (get_string(key, NSSM_REG_AFFINITY, buffer, sizeof(buffer), false, false, false) || ! buffer[0]) service->affinity = 0LL;
\r
553 else if (affinity_string_to_mask(buffer, &service->affinity)) {
\r
554 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_AFFINITY_MASK, service->name, buffer);
\r
555 service->affinity = 0LL;
\r
558 DWORD_PTR affinity, system_affinity;
\r
560 if (GetProcessAffinityMask(GetCurrentProcess(), &affinity, &system_affinity)) {
\r
561 _int64 effective_affinity = service->affinity & system_affinity;
\r
562 if (effective_affinity != service->affinity) {
\r
564 if (! affinity_mask_to_string(system_affinity, &system)) {
\r
565 TCHAR *effective = 0;
\r
566 if (! affinity_mask_to_string(effective_affinity, &effective)) {
\r
567 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_EFFECTIVE_AFFINITY_MASK, service->name, buffer, system, effective, 0);
\r
569 HeapFree(GetProcessHeap(), 0, effective);
\r
571 HeapFree(GetProcessHeap(), 0, system);
\r
576 /* Try to get environment variables - may fail */
\r
577 get_environment(service->name, key, NSSM_REG_ENV, &service->env, &service->envlen);
\r
578 /* Environment variables to add to existing rather than replace - may fail. */
\r
579 get_environment(service->name, key, NSSM_REG_ENV_EXTRA, &service->env_extra, &service->env_extralen);
\r
581 /* Try to get priority - may fail. */
\r
582 unsigned long priority;
\r
583 if (get_number(key, NSSM_REG_PRIORITY, &priority, false) == 1) {
\r
584 if (priority == (priority & priority_mask())) service->priority = priority;
\r
585 else log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_PRIORITY, service->name, NSSM_REG_PRIORITY, 0);
\r
588 /* Try to get file rotation settings - may fail. */
\r
589 unsigned long rotate_files;
\r
590 if (get_number(key, NSSM_REG_ROTATE, &rotate_files, false) == 1) {
\r
591 if (rotate_files) service->rotate_files = true;
\r
592 else service->rotate_files = false;
\r
594 else service->rotate_files = false;
\r
595 if (get_number(key, NSSM_REG_ROTATE_ONLINE, &rotate_files, false) == 1) {
\r
596 if (rotate_files) service->rotate_stdout_online = service->rotate_stderr_online = true;
\r
597 else service->rotate_stdout_online = service->rotate_stderr_online = false;
\r
599 else service->rotate_stdout_online = service->rotate_stderr_online = false;
\r
600 if (get_number(key, NSSM_REG_ROTATE_SECONDS, &service->rotate_seconds, false) != 1) service->rotate_seconds = 0;
\r
601 if (get_number(key, NSSM_REG_ROTATE_BYTES_LOW, &service->rotate_bytes_low, false) != 1) service->rotate_bytes_low = 0;
\r
602 if (get_number(key, NSSM_REG_ROTATE_BYTES_HIGH, &service->rotate_bytes_high, false) != 1) service->rotate_bytes_high = 0;
\r
604 /* Try to get force new console setting - may fail. */
\r
605 if (get_number(key, NSSM_REG_NO_CONSOLE, &service->no_console, false) != 1) service->no_console = 0;
\r
607 /* Change to startup directory in case stdout/stderr are relative paths. */
\r
608 TCHAR cwd[PATH_LENGTH];
\r
609 GetCurrentDirectory(_countof(cwd), cwd);
\r
610 SetCurrentDirectory(service->dir);
\r
612 /* Try to get stdout and stderr */
\r
613 if (get_io_parameters(service, key)) {
\r
614 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);
\r
616 SetCurrentDirectory(cwd);
\r
620 /* Change back in case the startup directory needs to be deleted. */
\r
621 SetCurrentDirectory(cwd);
\r
623 /* Try to get mandatory restart delay */
\r
624 override_milliseconds(service->name, key, NSSM_REG_RESTART_DELAY, &service->restart_delay, 0, NSSM_EVENT_BOGUS_RESTART_DELAY);
\r
626 /* Try to get throttle restart delay */
\r
627 override_milliseconds(service->name, key, NSSM_REG_THROTTLE, &service->throttle_delay, NSSM_RESET_THROTTLE_RESTART, NSSM_EVENT_BOGUS_THROTTLE);
\r
629 /* Try to get service stop flags. */
\r
630 unsigned long type = REG_DWORD;
\r
631 unsigned long stop_method_skip;
\r
632 unsigned long buflen = sizeof(stop_method_skip);
\r
633 bool stop_ok = false;
\r
634 ret = RegQueryValueEx(key, NSSM_REG_STOP_METHOD_SKIP, 0, &type, (unsigned char *) &stop_method_skip, &buflen);
\r
635 if (ret != ERROR_SUCCESS) {
\r
636 if (ret != ERROR_FILE_NOT_FOUND) {
\r
637 if (type != REG_DWORD) {
\r
638 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_STOP_METHOD_SKIP, service->name, NSSM_REG_STOP_METHOD_SKIP, NSSM, 0);
\r
640 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, NSSM_REG_STOP_METHOD_SKIP, error_string(GetLastError()), 0);
\r
643 else stop_ok = true;
\r
645 /* Try all methods except those requested to be skipped. */
\r
646 service->stop_method = ~0;
\r
647 if (stop_ok) service->stop_method &= ~stop_method_skip;
\r
649 /* Try to get kill delays - may fail. */
\r
650 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
651 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
652 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
654 /* Try to get process tree settings - may fail. */
\r
655 unsigned long kill_process_tree;
\r
656 if (get_number(key, NSSM_REG_KILL_PROCESS_TREE, &kill_process_tree, false) == 1) {
\r
657 if (kill_process_tree) service->kill_process_tree = true;
\r
658 else service->kill_process_tree = false;
\r
660 else service->kill_process_tree = true;
\r
662 /* Try to get default exit action. */
\r
663 bool default_action;
\r
664 service->default_exit_action = NSSM_EXIT_RESTART;
\r
665 TCHAR action_string[ACTION_LEN];
\r
666 if (! get_exit_action(service->name, 0, action_string, &default_action)) {
\r
667 for (int i = 0; exit_action_strings[i]; i++) {
\r
668 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
669 service->default_exit_action = i;
\r
675 /* Close registry */
\r
682 Sets the string for the exit action corresponding to the exit code.
\r
684 ret is a pointer to an unsigned long containing the exit code.
\r
685 If ret is NULL, we retrieve the default exit action unconditionally.
\r
687 action is a buffer which receives the string.
\r
689 default_action is a pointer to a bool which is set to false if there
\r
690 was an explicit string for the given exit code, or true if we are
\r
691 returning the default action.
\r
693 Returns: 0 on success.
\r
696 int get_exit_action(const TCHAR *service_name, unsigned long *ret, TCHAR *action, bool *default_action) {
\r
697 /* Are we returning the default action or a status-specific one? */
\r
698 *default_action = ! ret;
\r
700 /* Try to open the registry */
\r
701 HKEY key = open_registry(service_name, NSSM_REG_EXIT, KEY_READ);
\r
702 if (! key) return 1;
\r
704 unsigned long type = REG_SZ;
\r
705 unsigned long action_len = ACTION_LEN;
\r
708 if (! ret) code[0] = _T('\0');
\r
709 else if (_sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), *ret) < 0) {
\r
711 return get_exit_action(service_name, 0, action, default_action);
\r
713 if (RegQueryValueEx(key, code, 0, &type, (unsigned char *) action, &action_len) != ERROR_SUCCESS) {
\r
715 /* Try again with * as the key if an exit code was defined */
\r
716 if (ret) return get_exit_action(service_name, 0, action, default_action);
\r
720 /* Close registry */
\r