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[MAX_PATH];
\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 unsigned long stop_method_skip = ~service->stop_method;
\r
57 if (stop_method_skip) set_number(key, NSSM_REG_STOP_METHOD_SKIP, stop_method_skip);
\r
58 else if (editing) RegDeleteValue(key, NSSM_REG_STOP_METHOD_SKIP);
\r
59 if (service->default_exit_action < NSSM_NUM_EXIT_ACTIONS) create_exit_action(service->name, exit_action_strings[service->default_exit_action], editing);
\r
60 if (service->throttle_delay != NSSM_RESET_THROTTLE_RESTART) set_number(key, NSSM_REG_THROTTLE, service->throttle_delay);
\r
61 else if (editing) RegDeleteValue(key, NSSM_REG_THROTTLE);
\r
62 if (service->kill_console_delay != NSSM_KILL_CONSOLE_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, service->kill_console_delay);
\r
63 else if (editing) RegDeleteValue(key, NSSM_REG_KILL_CONSOLE_GRACE_PERIOD);
\r
64 if (service->kill_window_delay != NSSM_KILL_WINDOW_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_WINDOW_GRACE_PERIOD, service->kill_window_delay);
\r
65 else if (editing) RegDeleteValue(key, NSSM_REG_KILL_WINDOW_GRACE_PERIOD);
\r
66 if (service->kill_threads_delay != NSSM_KILL_THREADS_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_THREADS_GRACE_PERIOD, service->kill_threads_delay);
\r
67 else if (editing) RegDeleteValue(key, NSSM_REG_KILL_THREADS_GRACE_PERIOD);
\r
68 if (service->stdin_path[0] || editing) {
\r
69 if (service->stdin_path[0]) set_expand_string(key, NSSM_REG_STDIN, service->stdin_path);
\r
70 else if (editing) RegDeleteValue(key, NSSM_REG_STDIN);
\r
71 if (service->stdin_sharing != NSSM_STDIN_SHARING) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_SHARING, service->stdin_sharing);
\r
72 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_SHARING);
\r
73 if (service->stdin_disposition != NSSM_STDIN_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_DISPOSITION, service->stdin_disposition);
\r
74 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_DISPOSITION);
\r
75 if (service->stdin_flags != NSSM_STDIN_FLAGS) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_FLAGS, service->stdin_flags);
\r
76 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_FLAGS);
\r
78 if (service->stdout_path[0] || editing) {
\r
79 if (service->stdout_path[0]) set_expand_string(key, NSSM_REG_STDOUT, service->stdout_path);
\r
80 else if (editing) RegDeleteValue(key, NSSM_REG_STDOUT);
\r
81 if (service->stdout_sharing != NSSM_STDOUT_SHARING) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_SHARING, service->stdout_sharing);
\r
82 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_SHARING);
\r
83 if (service->stdout_disposition != NSSM_STDOUT_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_DISPOSITION, service->stdout_disposition);
\r
84 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_DISPOSITION);
\r
85 if (service->stdout_flags != NSSM_STDOUT_FLAGS) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_FLAGS, service->stdout_flags);
\r
86 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_FLAGS);
\r
88 if (service->stderr_path[0] || editing) {
\r
89 if (service->stderr_path[0]) set_expand_string(key, NSSM_REG_STDERR, service->stderr_path);
\r
90 else if (editing) RegDeleteValue(key, NSSM_REG_STDERR);
\r
91 if (service->stderr_sharing != NSSM_STDERR_SHARING) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_SHARING, service->stderr_sharing);
\r
92 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_SHARING);
\r
93 if (service->stderr_disposition != NSSM_STDERR_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_DISPOSITION, service->stderr_disposition);
\r
94 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_DISPOSITION);
\r
95 if (service->stderr_flags != NSSM_STDERR_FLAGS) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_FLAGS, service->stderr_flags);
\r
96 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_FLAGS);
\r
98 if (service->rotate_files) set_number(key, NSSM_REG_ROTATE, 1);
\r
99 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE);
\r
100 if (service->rotate_seconds) set_number(key, NSSM_REG_ROTATE_SECONDS, service->rotate_seconds);
\r
101 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_SECONDS);
\r
102 if (service->rotate_bytes_low) set_number(key, NSSM_REG_ROTATE_BYTES_LOW, service->rotate_bytes_low);
\r
103 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_BYTES_LOW);
\r
104 if (service->rotate_bytes_high) set_number(key, NSSM_REG_ROTATE_BYTES_HIGH, service->rotate_bytes_high);
\r
105 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_BYTES_HIGH);
\r
108 if (service->env) {
\r
109 if (RegSetValueEx(key, NSSM_REG_ENV, 0, REG_MULTI_SZ, (const unsigned char *) service->env, (unsigned long) service->envlen * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
110 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV, error_string(GetLastError()), 0);
\r
113 else if (editing) RegDeleteValue(key, NSSM_REG_ENV);
\r
114 if (service->env_extra) {
\r
115 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
116 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV_EXTRA, error_string(GetLastError()), 0);
\r
119 else if (editing) RegDeleteValue(key, NSSM_REG_ENV_EXTRA);
\r
121 /* Close registry. */
\r
127 int create_exit_action(TCHAR *service_name, const TCHAR *action_string, bool editing) {
\r
129 TCHAR registry[KEY_LENGTH];
\r
130 if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, NSSM_REGISTRY _T("\\%s"), service_name, NSSM_REG_EXIT) < 0) {
\r
131 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("NSSM_REG_EXIT"), _T("create_exit_action()"), 0);
\r
135 /* Try to open the registry */
\r
137 unsigned long disposition;
\r
138 if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, KEY_WRITE, 0, &key, &disposition) != ERROR_SUCCESS) {
\r
139 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
143 /* Do nothing if the key already existed */
\r
144 if (disposition == REG_OPENED_EXISTING_KEY && ! editing) {
\r
149 /* Create the default value */
\r
150 if (RegSetValueEx(key, 0, 0, REG_SZ, (const unsigned char *) action_string, (unsigned long) (_tcslen(action_string) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
151 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_EXIT, error_string(GetLastError()), 0);
\r
156 /* Close registry */
\r
162 int set_environment(TCHAR *service_name, HKEY key, TCHAR *value, TCHAR **env, unsigned long *envlen) {
\r
163 unsigned long type = REG_MULTI_SZ;
\r
165 /* Dummy test to find buffer size */
\r
166 unsigned long ret = RegQueryValueEx(key, value, 0, &type, NULL, envlen);
\r
167 if (ret != ERROR_SUCCESS) {
\r
170 /* The service probably doesn't have any environment configured */
\r
171 if (ret == ERROR_FILE_NOT_FOUND) return 0;
\r
172 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
176 if (type != REG_MULTI_SZ) {
\r
179 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_INVALID_ENVIRONMENT_STRING_TYPE, value, service_name, 0);
\r
183 /* Probably not possible */
\r
184 if (! *envlen) return 0;
\r
186 /* Previously initialised? */
\r
187 if (*env) HeapFree(GetProcessHeap(), 0, *env);
\r
189 *env = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, *envlen);
\r
192 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, value, _T("set_environment()"), 0);
\r
196 /* Actually get the strings */
\r
197 ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) *env, envlen);
\r
198 if (ret != ERROR_SUCCESS) {
\r
199 HeapFree(GetProcessHeap(), 0, *env);
\r
202 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
209 /* Replace NULL with CRLF. Leave NULL NULL as the end marker. */
\r
210 int format_environment(TCHAR *env, unsigned long envlen, TCHAR **formatted, unsigned long *newlen) {
\r
211 unsigned long i, j;
\r
219 for (i = 0; i < envlen; i++) if (! env[i] && env[i + 1]) ++*newlen;
\r
221 *formatted = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *newlen * sizeof(TCHAR));
\r
222 if (! *formatted) {
\r
227 for (i = 0, j = 0; i < envlen; i++) {
\r
228 (*formatted)[j] = env[i];
\r
231 (*formatted)[j] = _T('\r');
\r
232 (*formatted)[++j] = _T('\n');
\r
241 /* Strip CR and replace LF with NULL. */
\r
242 int unformat_environment(TCHAR *env, unsigned long envlen, TCHAR **unformatted, unsigned long *newlen) {
\r
243 unsigned long i, j;
\r
251 for (i = 0; i < envlen; i++) if (env[i] != _T('\r')) ++*newlen;
\r
252 /* Must end with two NULLs. */
\r
255 *unformatted = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *newlen * sizeof(TCHAR));
\r
256 if (! *unformatted) return 1;
\r
258 for (i = 0, j = 0; i < envlen; i++) {
\r
259 if (env[i] == _T('\r')) continue;
\r
260 if (env[i] == _T('\n')) (*unformatted)[j] = _T('\0');
\r
261 else (*unformatted)[j] = env[i];
\r
268 int expand_parameter(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise, bool must_exist) {
\r
269 TCHAR *buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, datalen);
\r
271 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, value, _T("expand_parameter()"), 0);
\r
275 ZeroMemory(data, datalen);
\r
277 unsigned long type = REG_EXPAND_SZ;
\r
278 unsigned long buflen = datalen;
\r
280 unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);
\r
281 if (ret != ERROR_SUCCESS) {
\r
282 unsigned long error = GetLastError();
\r
283 HeapFree(GetProcessHeap(), 0, buffer);
\r
285 if (ret == ERROR_FILE_NOT_FOUND) {
\r
286 if (! must_exist) return 0;
\r
289 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(error), 0);
\r
293 /* Paths aren't allowed to contain quotes. */
\r
294 if (sanitise) PathUnquoteSpaces(buffer);
\r
296 /* Technically we shouldn't expand environment strings from REG_SZ values */
\r
297 if (type != REG_EXPAND_SZ) {
\r
298 memmove(data, buffer, buflen);
\r
299 HeapFree(GetProcessHeap(), 0, buffer);
\r
303 ret = ExpandEnvironmentStrings((TCHAR *) buffer, data, datalen);
\r
304 if (! ret || ret > datalen) {
\r
305 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_EXPANDENVIRONMENTSTRINGS_FAILED, buffer, error_string(GetLastError()), 0);
\r
306 HeapFree(GetProcessHeap(), 0, buffer);
\r
310 HeapFree(GetProcessHeap(), 0, buffer);
\r
314 int expand_parameter(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise) {
\r
315 return expand_parameter(key, value, data, datalen, sanitise, true);
\r
319 Sets a string in the registry.
\r
320 Returns: 0 if it was set.
\r
323 int set_expand_string(HKEY key, TCHAR *value, TCHAR *string) {
\r
324 if (RegSetValueEx(key, value, 0, REG_EXPAND_SZ, (const unsigned char *) string, (unsigned long) (_tcslen(string) + 1) * sizeof(TCHAR)) == ERROR_SUCCESS) return 0;
\r
325 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
330 Set an unsigned long in the registry.
\r
331 Returns: 0 if it was set.
\r
334 int set_number(HKEY key, TCHAR *value, unsigned long number) {
\r
335 if (RegSetValueEx(key, value, 0, REG_DWORD, (const unsigned char *) &number, sizeof(number)) == ERROR_SUCCESS) return 0;
\r
336 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
341 Query an unsigned long from the registry.
\r
342 Returns: 1 if a number was retrieved.
\r
343 0 if none was found and must_exist is false.
\r
344 -1 if none was found and must_exist is true.
\r
347 int get_number(HKEY key, TCHAR *value, unsigned long *number, bool must_exist) {
\r
348 unsigned long type = REG_DWORD;
\r
349 unsigned long number_len = sizeof(unsigned long);
\r
351 int ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) number, &number_len);
\r
352 if (ret == ERROR_SUCCESS) return 1;
\r
354 if (ret == ERROR_FILE_NOT_FOUND) {
\r
355 if (! must_exist) return 0;
\r
358 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
359 if (ret == ERROR_FILE_NOT_FOUND) return -1;
\r
364 int get_number(HKEY key, TCHAR *value, unsigned long *number) {
\r
365 return get_number(key, value, number, true);
\r
368 void override_milliseconds(TCHAR *service_name, HKEY key, TCHAR *value, unsigned long *buffer, unsigned long default_value, unsigned long event) {
\r
369 unsigned long type = REG_DWORD;
\r
370 unsigned long buflen = sizeof(unsigned long);
\r
372 unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);
\r
373 if (ret != ERROR_SUCCESS) {
\r
374 if (ret != ERROR_FILE_NOT_FOUND) {
\r
375 if (type != REG_DWORD) {
\r
376 TCHAR milliseconds[16];
\r
377 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), default_value);
\r
378 log_event(EVENTLOG_WARNING_TYPE, event, service_name, value, milliseconds, 0);
\r
380 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
385 if (! ok) *buffer = default_value;
\r
388 HKEY open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam) {
\r
390 TCHAR registry[KEY_LENGTH];
\r
394 if (sub) ret = _sntprintf_s(registry, _countof(registry), _TRUNCATE, NSSM_REGISTRY _T("\\%s"), service_name, sub);
\r
395 else ret = _sntprintf_s(registry, _countof(registry), _TRUNCATE, NSSM_REGISTRY, service_name);
\r
397 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("NSSM_REGISTRY"), _T("open_registry()"), 0);
\r
401 if (sam & KEY_WRITE) {
\r
402 if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, sam, 0, &key, 0) != ERROR_SUCCESS) {
\r
403 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
408 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, registry, 0, sam, &key) != ERROR_SUCCESS) {
\r
409 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
417 HKEY open_registry(const TCHAR *service_name, REGSAM sam) {
\r
418 return open_registry(service_name, 0, sam);
\r
421 int get_parameters(nssm_service_t *service, STARTUPINFO *si) {
\r
424 /* Try to open the registry */
\r
425 HKEY key = open_registry(service->name, KEY_READ);
\r
426 if (! key) return 1;
\r
428 /* Try to get executable file - MUST succeed */
\r
429 if (expand_parameter(key, NSSM_REG_EXE, service->exe, sizeof(service->exe), false)) {
\r
434 /* Try to get flags - may fail and we don't care */
\r
435 if (expand_parameter(key, NSSM_REG_FLAGS, service->flags, sizeof(service->flags), false)) {
\r
436 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_FLAGS, NSSM_REG_FLAGS, service->name, service->exe, 0);
\r
437 ZeroMemory(service->flags, sizeof(service->flags));
\r
440 /* Try to get startup directory - may fail and we fall back to a default */
\r
441 if (expand_parameter(key, NSSM_REG_DIR, service->dir, sizeof(service->dir), true) || ! service->dir[0]) {
\r
442 _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);
\r
443 strip_basename(service->dir);
\r
444 if (service->dir[0] == _T('\0')) {
\r
446 ret = GetWindowsDirectory(service->dir, sizeof(service->dir));
\r
447 if (! ret || ret > sizeof(service->dir)) {
\r
448 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_NO_DIR_AND_NO_FALLBACK, NSSM_REG_DIR, service->name, 0);
\r
453 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_DIR, NSSM_REG_DIR, service->name, service->dir, 0);
\r
456 /* Try to get environment variables - may fail */
\r
457 set_environment(service->name, key, NSSM_REG_ENV, &service->env, &service->envlen);
\r
458 /* Environment variables to add to existing rather than replace - may fail. */
\r
459 set_environment(service->name, key, NSSM_REG_ENV_EXTRA, &service->env_extra, &service->env_extralen);
\r
462 if (service->env_extra) {
\r
463 /* Append these to any other environment variables set. */
\r
464 if (service->env) {
\r
465 /* Append extra variables to configured variables. */
\r
466 unsigned long envlen = service->envlen + service->env_extralen - 1;
\r
467 TCHAR *env = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, envlen);
\r
469 memmove(env, service->env, service->envlen - sizeof(TCHAR));
\r
470 /* envlen is in bytes. */
\r
471 memmove(env + (service->envlen / sizeof(TCHAR)) - 1, service->env_extra, service->env_extralen);
\r
473 HeapFree(GetProcessHeap(), 0, service->env);
\r
474 service->env = env;
\r
475 service->envlen = envlen;
\r
477 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("environment"), _T("get_parameters()"), 0);
\r
480 /* Append extra variables to our environment. */
\r
482 size_t envlen, len;
\r
484 env = service->env_extra;
\r
487 envlen = _tcslen(env) + 1;
\r
488 for (s = env; *s && *s != _T('='); s++);
\r
489 if (*s == _T('=')) *s++ = _T('\0');
\r
490 if (! SetEnvironmentVariable(env, s)) log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_SETENVIRONMENTVARIABLE_FAILED, env, s, error_string(GetLastError()), 0);
\r
497 /* Try to get priority - may fail. */
\r
498 unsigned long priority;
\r
499 if (get_number(key, NSSM_REG_PRIORITY, &priority) == 1) {
\r
500 if (priority == (priority & priority_mask())) service->priority = priority;
\r
501 else log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_PRIORITY, service->name, NSSM_REG_PRIORITY, 0);
\r
504 /* Try to get file rotation settings - may fail. */
\r
505 unsigned long rotate_files;
\r
506 if (get_number(key, NSSM_REG_ROTATE, &rotate_files, false) == 1) {
\r
507 if (rotate_files) service->rotate_files = true;
\r
508 else service->rotate_files = false;
\r
510 else service->rotate_files = false;
\r
511 if (get_number(key, NSSM_REG_ROTATE_SECONDS, &service->rotate_seconds, false) != 1) service->rotate_seconds = 0;
\r
512 if (get_number(key, NSSM_REG_ROTATE_BYTES_LOW, &service->rotate_bytes_low, false) != 1) service->rotate_bytes_low = 0;
\r
513 if (get_number(key, NSSM_REG_ROTATE_BYTES_HIGH, &service->rotate_bytes_high, false) != 1) service->rotate_bytes_high = 0;
\r
515 /* Change to startup directory in case stdout/stderr are relative paths. */
\r
516 TCHAR cwd[MAX_PATH];
\r
517 GetCurrentDirectory(_countof(cwd), cwd);
\r
518 SetCurrentDirectory(service->dir);
\r
520 /* Try to get stdout and stderr */
\r
521 if (get_output_handles(service, key, si)) {
\r
522 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);
\r
524 SetCurrentDirectory(cwd);
\r
528 /* Change back in case the startup directory needs to be deleted. */
\r
529 SetCurrentDirectory(cwd);
\r
531 /* Try to get throttle restart delay */
\r
532 override_milliseconds(service->name, key, NSSM_REG_THROTTLE, &service->throttle_delay, NSSM_RESET_THROTTLE_RESTART, NSSM_EVENT_BOGUS_THROTTLE);
\r
534 /* Try to get service stop flags. */
\r
535 unsigned long type = REG_DWORD;
\r
536 unsigned long stop_method_skip;
\r
537 unsigned long buflen = sizeof(stop_method_skip);
\r
538 bool stop_ok = false;
\r
539 ret = RegQueryValueEx(key, NSSM_REG_STOP_METHOD_SKIP, 0, &type, (unsigned char *) &stop_method_skip, &buflen);
\r
540 if (ret != ERROR_SUCCESS) {
\r
541 if (ret != ERROR_FILE_NOT_FOUND) {
\r
542 if (type != REG_DWORD) {
\r
543 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_STOP_METHOD_SKIP, service->name, NSSM_REG_STOP_METHOD_SKIP, NSSM, 0);
\r
545 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, NSSM_REG_STOP_METHOD_SKIP, error_string(GetLastError()), 0);
\r
548 else stop_ok = true;
\r
550 /* Try all methods except those requested to be skipped. */
\r
551 service->stop_method = ~0;
\r
552 if (stop_ok) service->stop_method &= ~stop_method_skip;
\r
554 /* Try to get kill delays - may fail. */
\r
555 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
556 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
557 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
559 /* Try to get default exit action. */
\r
560 bool default_action;
\r
561 service->default_exit_action = NSSM_EXIT_RESTART;
\r
562 TCHAR action_string[ACTION_LEN];
\r
563 if (! get_exit_action(service->name, 0, action_string, &default_action)) {
\r
564 for (int i = 0; exit_action_strings[i]; i++) {
\r
565 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
566 service->default_exit_action = i;
\r
572 /* Close registry */
\r
579 Sets the string for the exit action corresponding to the exit code.
\r
581 ret is a pointer to an unsigned long containing the exit code.
\r
582 If ret is NULL, we retrieve the default exit action unconditionally.
\r
584 action is a buffer which receives the string.
\r
586 default_action is a pointer to a bool which is set to false if there
\r
587 was an explicit string for the given exit code, or true if we are
\r
588 returning the default action.
\r
590 Returns: 0 on success.
\r
593 int get_exit_action(const TCHAR *service_name, unsigned long *ret, TCHAR *action, bool *default_action) {
\r
594 /* Are we returning the default action or a status-specific one? */
\r
595 *default_action = ! ret;
\r
597 /* Try to open the registry */
\r
598 HKEY key = open_registry(service_name, NSSM_REG_EXIT, KEY_READ);
\r
599 if (! key) return 1;
\r
601 unsigned long type = REG_SZ;
\r
602 unsigned long action_len = ACTION_LEN;
\r
605 if (! ret) code[0] = _T('\0');
\r
606 else if (_sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), *ret) < 0) {
\r
608 return get_exit_action(service_name, 0, action, default_action);
\r
610 if (RegQueryValueEx(key, code, 0, &type, (unsigned char *) action, &action_len) != ERROR_SUCCESS) {
\r
612 /* Try again with * as the key if an exit code was defined */
\r
613 if (ret) return get_exit_action(service_name, 0, action, default_action);
\r
617 /* Close registry */
\r