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->stdin_path[0] || editing) {
\r
83 if (service->stdin_path[0]) set_expand_string(key, NSSM_REG_STDIN, service->stdin_path);
\r
84 else if (editing) RegDeleteValue(key, NSSM_REG_STDIN);
\r
85 if (service->stdin_sharing != NSSM_STDIN_SHARING) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_SHARING, service->stdin_sharing);
\r
86 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_SHARING);
\r
87 if (service->stdin_disposition != NSSM_STDIN_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_DISPOSITION, service->stdin_disposition);
\r
88 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_DISPOSITION);
\r
89 if (service->stdin_flags != NSSM_STDIN_FLAGS) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_FLAGS, service->stdin_flags);
\r
90 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_FLAGS);
\r
92 if (service->stdout_path[0] || editing) {
\r
93 if (service->stdout_path[0]) set_expand_string(key, NSSM_REG_STDOUT, service->stdout_path);
\r
94 else if (editing) RegDeleteValue(key, NSSM_REG_STDOUT);
\r
95 if (service->stdout_sharing != NSSM_STDOUT_SHARING) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_SHARING, service->stdout_sharing);
\r
96 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_SHARING);
\r
97 if (service->stdout_disposition != NSSM_STDOUT_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_DISPOSITION, service->stdout_disposition);
\r
98 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_DISPOSITION);
\r
99 if (service->stdout_flags != NSSM_STDOUT_FLAGS) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_FLAGS, service->stdout_flags);
\r
100 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_FLAGS);
\r
102 if (service->stderr_path[0] || editing) {
\r
103 if (service->stderr_path[0]) set_expand_string(key, NSSM_REG_STDERR, service->stderr_path);
\r
104 else if (editing) RegDeleteValue(key, NSSM_REG_STDERR);
\r
105 if (service->stderr_sharing != NSSM_STDERR_SHARING) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_SHARING, service->stderr_sharing);
\r
106 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_SHARING);
\r
107 if (service->stderr_disposition != NSSM_STDERR_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_DISPOSITION, service->stderr_disposition);
\r
108 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_DISPOSITION);
\r
109 if (service->stderr_flags != NSSM_STDERR_FLAGS) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_FLAGS, service->stderr_flags);
\r
110 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_FLAGS);
\r
112 if (service->rotate_files) set_number(key, NSSM_REG_ROTATE, 1);
\r
113 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE);
\r
114 if (service->rotate_stdout_online) set_number(key, NSSM_REG_ROTATE_ONLINE, 1);
\r
115 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_ONLINE);
\r
116 if (service->rotate_seconds) set_number(key, NSSM_REG_ROTATE_SECONDS, service->rotate_seconds);
\r
117 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_SECONDS);
\r
118 if (service->rotate_bytes_low) set_number(key, NSSM_REG_ROTATE_BYTES_LOW, service->rotate_bytes_low);
\r
119 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_BYTES_LOW);
\r
120 if (service->rotate_bytes_high) set_number(key, NSSM_REG_ROTATE_BYTES_HIGH, service->rotate_bytes_high);
\r
121 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_BYTES_HIGH);
\r
124 if (service->env) {
\r
125 if (RegSetValueEx(key, NSSM_REG_ENV, 0, REG_MULTI_SZ, (const unsigned char *) service->env, (unsigned long) service->envlen * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
126 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV, error_string(GetLastError()), 0);
\r
129 else if (editing) RegDeleteValue(key, NSSM_REG_ENV);
\r
130 if (service->env_extra) {
\r
131 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
132 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV_EXTRA, error_string(GetLastError()), 0);
\r
135 else if (editing) RegDeleteValue(key, NSSM_REG_ENV_EXTRA);
\r
137 /* Close registry. */
\r
143 int create_exit_action(TCHAR *service_name, const TCHAR *action_string, bool editing) {
\r
145 TCHAR registry[KEY_LENGTH];
\r
146 if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, NSSM_REGISTRY _T("\\%s"), service_name, NSSM_REG_EXIT) < 0) {
\r
147 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("NSSM_REG_EXIT"), _T("create_exit_action()"), 0);
\r
151 /* Try to open the registry */
\r
153 unsigned long disposition;
\r
154 if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, KEY_WRITE, 0, &key, &disposition) != ERROR_SUCCESS) {
\r
155 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
159 /* Do nothing if the key already existed */
\r
160 if (disposition == REG_OPENED_EXISTING_KEY && ! editing) {
\r
165 /* Create the default value */
\r
166 if (RegSetValueEx(key, 0, 0, REG_SZ, (const unsigned char *) action_string, (unsigned long) (_tcslen(action_string) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
167 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_EXIT, error_string(GetLastError()), 0);
\r
172 /* Close registry */
\r
178 int set_environment(TCHAR *service_name, HKEY key, TCHAR *value, TCHAR **env, unsigned long *envlen) {
\r
179 unsigned long type = REG_MULTI_SZ;
\r
181 /* Dummy test to find buffer size */
\r
182 unsigned long ret = RegQueryValueEx(key, value, 0, &type, NULL, envlen);
\r
183 if (ret != ERROR_SUCCESS) {
\r
186 /* The service probably doesn't have any environment configured */
\r
187 if (ret == ERROR_FILE_NOT_FOUND) return 0;
\r
188 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
192 if (type != REG_MULTI_SZ) {
\r
195 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_INVALID_ENVIRONMENT_STRING_TYPE, value, service_name, 0);
\r
199 /* Probably not possible */
\r
200 if (! *envlen) return 0;
\r
202 /* Previously initialised? */
\r
203 if (*env) HeapFree(GetProcessHeap(), 0, *env);
\r
205 *env = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, *envlen);
\r
208 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, value, _T("set_environment()"), 0);
\r
212 /* Actually get the strings */
\r
213 ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) *env, envlen);
\r
214 if (ret != ERROR_SUCCESS) {
\r
215 HeapFree(GetProcessHeap(), 0, *env);
\r
218 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
225 /* Replace NULL with CRLF. Leave NULL NULL as the end marker. */
\r
226 int format_environment(TCHAR *env, unsigned long envlen, TCHAR **formatted, unsigned long *newlen) {
\r
227 unsigned long i, j;
\r
235 for (i = 0; i < envlen; i++) if (! env[i] && env[i + 1]) ++*newlen;
\r
237 *formatted = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *newlen * sizeof(TCHAR));
\r
238 if (! *formatted) {
\r
243 for (i = 0, j = 0; i < envlen; i++) {
\r
244 (*formatted)[j] = env[i];
\r
247 (*formatted)[j] = _T('\r');
\r
248 (*formatted)[++j] = _T('\n');
\r
257 /* Strip CR and replace LF with NULL. */
\r
258 int unformat_environment(TCHAR *env, unsigned long envlen, TCHAR **unformatted, unsigned long *newlen) {
\r
259 unsigned long i, j;
\r
267 for (i = 0; i < envlen; i++) if (env[i] != _T('\r')) ++*newlen;
\r
268 /* Must end with two NULLs. */
\r
271 *unformatted = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *newlen * sizeof(TCHAR));
\r
272 if (! *unformatted) return 1;
\r
274 for (i = 0, j = 0; i < envlen; i++) {
\r
275 if (env[i] == _T('\r')) continue;
\r
276 if (env[i] == _T('\n')) (*unformatted)[j] = _T('\0');
\r
277 else (*unformatted)[j] = env[i];
\r
284 int get_string(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool expand, bool sanitise, bool must_exist) {
\r
285 TCHAR *buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, datalen);
\r
287 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, value, _T("get_string()"), 0);
\r
291 ZeroMemory(data, datalen);
\r
293 unsigned long type = REG_EXPAND_SZ;
\r
294 unsigned long buflen = datalen;
\r
296 unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);
\r
297 if (ret != ERROR_SUCCESS) {
\r
298 unsigned long error = GetLastError();
\r
299 HeapFree(GetProcessHeap(), 0, buffer);
\r
301 if (ret == ERROR_FILE_NOT_FOUND) {
\r
302 if (! must_exist) return 0;
\r
305 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(error), 0);
\r
309 /* Paths aren't allowed to contain quotes. */
\r
310 if (sanitise) PathUnquoteSpaces(buffer);
\r
312 /* Do we want to expand the string? */
\r
314 if (type == REG_EXPAND_SZ) type = REG_SZ;
\r
317 /* Technically we shouldn't expand environment strings from REG_SZ values */
\r
318 if (type != REG_EXPAND_SZ) {
\r
319 memmove(data, buffer, buflen);
\r
320 HeapFree(GetProcessHeap(), 0, buffer);
\r
324 ret = ExpandEnvironmentStrings((TCHAR *) buffer, data, datalen);
\r
325 if (! ret || ret > datalen) {
\r
326 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_EXPANDENVIRONMENTSTRINGS_FAILED, buffer, error_string(GetLastError()), 0);
\r
327 HeapFree(GetProcessHeap(), 0, buffer);
\r
331 HeapFree(GetProcessHeap(), 0, buffer);
\r
335 int get_string(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise) {
\r
336 return get_string(key, value, data, datalen, false, sanitise, true);
\r
339 int expand_parameter(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise, bool must_exist) {
\r
340 return get_string(key, value, data, datalen, true, sanitise, must_exist);
\r
343 int expand_parameter(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise) {
\r
344 return expand_parameter(key, value, data, datalen, sanitise, true);
\r
348 Sets a string in the registry.
\r
349 Returns: 0 if it was set.
\r
352 int set_string(HKEY key, TCHAR *value, TCHAR *string, bool expand) {
\r
353 unsigned long type = expand ? REG_EXPAND_SZ : REG_SZ;
\r
354 if (RegSetValueEx(key, value, 0, type, (const unsigned char *) string, (unsigned long) (_tcslen(string) + 1) * sizeof(TCHAR)) == ERROR_SUCCESS) return 0;
\r
355 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
359 int set_string(HKEY key, TCHAR *value, TCHAR *string) {
\r
360 return set_string(key, value, string, false);
\r
364 int set_expand_string(HKEY key, TCHAR *value, TCHAR *string) {
\r
365 return set_string(key, value, string, true);
\r
370 Set an unsigned long in the registry.
\r
371 Returns: 0 if it was set.
\r
374 int set_number(HKEY key, TCHAR *value, unsigned long number) {
\r
375 if (RegSetValueEx(key, value, 0, REG_DWORD, (const unsigned char *) &number, sizeof(number)) == ERROR_SUCCESS) return 0;
\r
376 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
381 Query an unsigned long from the registry.
\r
382 Returns: 1 if a number was retrieved.
\r
383 0 if none was found and must_exist is false.
\r
384 -1 if none was found and must_exist is true.
\r
387 int get_number(HKEY key, TCHAR *value, unsigned long *number, bool must_exist) {
\r
388 unsigned long type = REG_DWORD;
\r
389 unsigned long number_len = sizeof(unsigned long);
\r
391 int ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) number, &number_len);
\r
392 if (ret == ERROR_SUCCESS) return 1;
\r
394 if (ret == ERROR_FILE_NOT_FOUND) {
\r
395 if (! must_exist) return 0;
\r
398 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
399 if (ret == ERROR_FILE_NOT_FOUND) return -1;
\r
404 int get_number(HKEY key, TCHAR *value, unsigned long *number) {
\r
405 return get_number(key, value, number, true);
\r
408 void override_milliseconds(TCHAR *service_name, HKEY key, TCHAR *value, unsigned long *buffer, unsigned long default_value, unsigned long event) {
\r
409 unsigned long type = REG_DWORD;
\r
410 unsigned long buflen = sizeof(unsigned long);
\r
412 unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);
\r
413 if (ret != ERROR_SUCCESS) {
\r
414 if (ret != ERROR_FILE_NOT_FOUND) {
\r
415 if (type != REG_DWORD) {
\r
416 TCHAR milliseconds[16];
\r
417 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), default_value);
\r
418 log_event(EVENTLOG_WARNING_TYPE, event, service_name, value, milliseconds, 0);
\r
420 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
425 if (! ok) *buffer = default_value;
\r
428 HKEY open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam) {
\r
430 TCHAR registry[KEY_LENGTH];
\r
434 if (sub) ret = _sntprintf_s(registry, _countof(registry), _TRUNCATE, NSSM_REGISTRY _T("\\%s"), service_name, sub);
\r
435 else ret = _sntprintf_s(registry, _countof(registry), _TRUNCATE, NSSM_REGISTRY, service_name);
\r
437 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("NSSM_REGISTRY"), _T("open_registry()"), 0);
\r
441 if (sam & KEY_WRITE) {
\r
442 if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, sam, 0, &key, 0) != ERROR_SUCCESS) {
\r
443 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
448 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, registry, 0, sam, &key) != ERROR_SUCCESS) {
\r
449 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
457 HKEY open_registry(const TCHAR *service_name, REGSAM sam) {
\r
458 return open_registry(service_name, 0, sam);
\r
461 int get_parameters(nssm_service_t *service, STARTUPINFO *si) {
\r
464 /* Try to open the registry */
\r
465 HKEY key = open_registry(service->name, KEY_READ);
\r
466 if (! key) return 1;
\r
468 /* Try to get executable file - MUST succeed */
\r
469 if (expand_parameter(key, NSSM_REG_EXE, service->exe, sizeof(service->exe), false)) {
\r
474 /* Try to get flags - may fail and we don't care */
\r
475 if (expand_parameter(key, NSSM_REG_FLAGS, service->flags, sizeof(service->flags), false)) {
\r
476 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_FLAGS, NSSM_REG_FLAGS, service->name, service->exe, 0);
\r
477 ZeroMemory(service->flags, sizeof(service->flags));
\r
480 /* Try to get startup directory - may fail and we fall back to a default */
\r
481 if (expand_parameter(key, NSSM_REG_DIR, service->dir, sizeof(service->dir), true) || ! service->dir[0]) {
\r
482 _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);
\r
483 strip_basename(service->dir);
\r
484 if (service->dir[0] == _T('\0')) {
\r
486 ret = GetWindowsDirectory(service->dir, sizeof(service->dir));
\r
487 if (! ret || ret > sizeof(service->dir)) {
\r
488 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_NO_DIR_AND_NO_FALLBACK, NSSM_REG_DIR, service->name, 0);
\r
493 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_DIR, NSSM_REG_DIR, service->name, service->dir, 0);
\r
496 /* Try to get processor affinity - may fail. */
\r
498 if (expand_parameter(key, NSSM_REG_AFFINITY, buffer, sizeof(buffer), false, false) || ! buffer[0]) service->affinity = 0LL;
\r
499 else if (affinity_string_to_mask(buffer, &service->affinity)) {
\r
500 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_AFFINITY_MASK, service->name, buffer);
\r
501 service->affinity = 0LL;
\r
504 DWORD_PTR affinity, system_affinity;
\r
506 if (GetProcessAffinityMask(GetCurrentProcess(), &affinity, &system_affinity)) {
\r
507 _int64 effective_affinity = service->affinity & system_affinity;
\r
508 if (effective_affinity != service->affinity) {
\r
510 if (! affinity_mask_to_string(system_affinity, &system)) {
\r
511 TCHAR *effective = 0;
\r
512 if (! affinity_mask_to_string(effective_affinity, &effective)) {
\r
513 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_EFFECTIVE_AFFINITY_MASK, service->name, buffer, system, effective, 0);
\r
515 HeapFree(GetProcessHeap(), 0, effective);
\r
517 HeapFree(GetProcessHeap(), 0, system);
\r
522 /* Try to get environment variables - may fail */
\r
523 set_environment(service->name, key, NSSM_REG_ENV, &service->env, &service->envlen);
\r
524 /* Environment variables to add to existing rather than replace - may fail. */
\r
525 set_environment(service->name, key, NSSM_REG_ENV_EXTRA, &service->env_extra, &service->env_extralen);
\r
528 if (service->env_extra) {
\r
530 unsigned long envlen;
\r
532 /* Copy our environment for the application. */
\r
533 if (! service->env) {
\r
534 TCHAR *rawenv = GetEnvironmentStrings();
\r
538 The environment block starts with variables of the form
\r
539 =C:=C:\Windows\System32 which we ignore.
\r
541 while (*env == _T('=')) {
\r
542 for ( ; *env; env++);
\r
548 for ( ; env[envlen]; envlen++);
\r
549 if (! env[++envlen]) break;
\r
553 service->envlen = envlen * sizeof(TCHAR);
\r
554 service->env = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, service->envlen);
\r
555 memmove(service->env, env, service->envlen);
\r
556 FreeEnvironmentStrings(rawenv);
\r
561 /* Append extra variables to configured variables. */
\r
562 if (service->env) {
\r
563 envlen = service->envlen + service->env_extralen - sizeof(TCHAR)/*?*/;
\r
564 env = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, envlen);
\r
566 memmove(env, service->env, service->envlen - sizeof(TCHAR));
\r
567 /* envlen is in bytes but env[i] is in characters. */
\r
568 memmove(env + (service->envlen / sizeof(TCHAR)) - 1, service->env_extra, service->env_extralen);
\r
570 HeapFree(GetProcessHeap(), 0, service->env);
\r
571 HeapFree(GetProcessHeap(), 0, service->env_extra);
\r
572 service->env = env;
\r
573 service->envlen = envlen;
\r
575 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("environment"), _T("get_parameters()"), 0);
\r
578 /* Huh? No environment at all? */
\r
579 service->env = service->env_extra;
\r
580 service->envlen = service->env_extralen;
\r
584 service->env_extra = 0;
\r
585 service->env_extralen = 0;
\r
588 /* Try to get priority - may fail. */
\r
589 unsigned long priority;
\r
590 if (get_number(key, NSSM_REG_PRIORITY, &priority, false) == 1) {
\r
591 if (priority == (priority & priority_mask())) service->priority = priority;
\r
592 else log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_PRIORITY, service->name, NSSM_REG_PRIORITY, 0);
\r
595 /* Try to get file rotation settings - may fail. */
\r
596 unsigned long rotate_files;
\r
597 if (get_number(key, NSSM_REG_ROTATE, &rotate_files, false) == 1) {
\r
598 if (rotate_files) service->rotate_files = true;
\r
599 else service->rotate_files = false;
\r
601 else service->rotate_files = false;
\r
602 if (get_number(key, NSSM_REG_ROTATE_ONLINE, &rotate_files, false) == 1) {
\r
603 if (rotate_files) service->rotate_stdout_online = service->rotate_stderr_online = true;
\r
604 else service->rotate_stdout_online = service->rotate_stderr_online = false;
\r
606 else service->rotate_stdout_online = service->rotate_stderr_online = false;
\r
607 if (get_number(key, NSSM_REG_ROTATE_SECONDS, &service->rotate_seconds, false) != 1) service->rotate_seconds = 0;
\r
608 if (get_number(key, NSSM_REG_ROTATE_BYTES_LOW, &service->rotate_bytes_low, false) != 1) service->rotate_bytes_low = 0;
\r
609 if (get_number(key, NSSM_REG_ROTATE_BYTES_HIGH, &service->rotate_bytes_high, false) != 1) service->rotate_bytes_high = 0;
\r
611 /* Change to startup directory in case stdout/stderr are relative paths. */
\r
612 TCHAR cwd[PATH_LENGTH];
\r
613 GetCurrentDirectory(_countof(cwd), cwd);
\r
614 SetCurrentDirectory(service->dir);
\r
616 /* Try to get stdout and stderr */
\r
617 if (get_output_handles(service, key, si)) {
\r
618 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);
\r
620 SetCurrentDirectory(cwd);
\r
624 /* Change back in case the startup directory needs to be deleted. */
\r
625 SetCurrentDirectory(cwd);
\r
627 /* Try to get mandatory restart delay */
\r
628 override_milliseconds(service->name, key, NSSM_REG_RESTART_DELAY, &service->restart_delay, 0, NSSM_EVENT_BOGUS_RESTART_DELAY);
\r
630 /* Try to get throttle restart delay */
\r
631 override_milliseconds(service->name, key, NSSM_REG_THROTTLE, &service->throttle_delay, NSSM_RESET_THROTTLE_RESTART, NSSM_EVENT_BOGUS_THROTTLE);
\r
633 /* Try to get service stop flags. */
\r
634 unsigned long type = REG_DWORD;
\r
635 unsigned long stop_method_skip;
\r
636 unsigned long buflen = sizeof(stop_method_skip);
\r
637 bool stop_ok = false;
\r
638 ret = RegQueryValueEx(key, NSSM_REG_STOP_METHOD_SKIP, 0, &type, (unsigned char *) &stop_method_skip, &buflen);
\r
639 if (ret != ERROR_SUCCESS) {
\r
640 if (ret != ERROR_FILE_NOT_FOUND) {
\r
641 if (type != REG_DWORD) {
\r
642 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_STOP_METHOD_SKIP, service->name, NSSM_REG_STOP_METHOD_SKIP, NSSM, 0);
\r
644 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, NSSM_REG_STOP_METHOD_SKIP, error_string(GetLastError()), 0);
\r
647 else stop_ok = true;
\r
649 /* Try all methods except those requested to be skipped. */
\r
650 service->stop_method = ~0;
\r
651 if (stop_ok) service->stop_method &= ~stop_method_skip;
\r
653 /* Try to get kill delays - may fail. */
\r
654 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
655 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
656 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
658 /* Try to get default exit action. */
\r
659 bool default_action;
\r
660 service->default_exit_action = NSSM_EXIT_RESTART;
\r
661 TCHAR action_string[ACTION_LEN];
\r
662 if (! get_exit_action(service->name, 0, action_string, &default_action)) {
\r
663 for (int i = 0; exit_action_strings[i]; i++) {
\r
664 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
665 service->default_exit_action = i;
\r
671 /* Close registry */
\r
678 Sets the string for the exit action corresponding to the exit code.
\r
680 ret is a pointer to an unsigned long containing the exit code.
\r
681 If ret is NULL, we retrieve the default exit action unconditionally.
\r
683 action is a buffer which receives the string.
\r
685 default_action is a pointer to a bool which is set to false if there
\r
686 was an explicit string for the given exit code, or true if we are
\r
687 returning the default action.
\r
689 Returns: 0 on success.
\r
692 int get_exit_action(const TCHAR *service_name, unsigned long *ret, TCHAR *action, bool *default_action) {
\r
693 /* Are we returning the default action or a status-specific one? */
\r
694 *default_action = ! ret;
\r
696 /* Try to open the registry */
\r
697 HKEY key = open_registry(service_name, NSSM_REG_EXIT, KEY_READ);
\r
698 if (! key) return 1;
\r
700 unsigned long type = REG_SZ;
\r
701 unsigned long action_len = ACTION_LEN;
\r
704 if (! ret) code[0] = _T('\0');
\r
705 else if (_sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), *ret) < 0) {
\r
707 return get_exit_action(service_name, 0, action, default_action);
\r
709 if (RegQueryValueEx(key, code, 0, &type, (unsigned char *) action, &action_len) != ERROR_SUCCESS) {
\r
711 /* Try again with * as the key if an exit code was defined */
\r
712 if (ret) return get_exit_action(service_name, 0, action, default_action);
\r
716 /* Close registry */
\r