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 unsigned long stop_method_skip = ~service->stop_method;
\r
55 if (stop_method_skip) set_number(key, NSSM_REG_STOP_METHOD_SKIP, stop_method_skip);
\r
56 else if (editing) RegDeleteValue(key, NSSM_REG_STOP_METHOD_SKIP);
\r
57 if (service->default_exit_action < NSSM_NUM_EXIT_ACTIONS) create_exit_action(service->name, exit_action_strings[service->default_exit_action], editing);
\r
58 if (service->throttle_delay != NSSM_RESET_THROTTLE_RESTART) set_number(key, NSSM_REG_THROTTLE, service->throttle_delay);
\r
59 else if (editing) RegDeleteValue(key, NSSM_REG_THROTTLE);
\r
60 if (service->kill_console_delay != NSSM_KILL_CONSOLE_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, service->kill_console_delay);
\r
61 else if (editing) RegDeleteValue(key, NSSM_REG_KILL_CONSOLE_GRACE_PERIOD);
\r
62 if (service->kill_window_delay != NSSM_KILL_WINDOW_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_WINDOW_GRACE_PERIOD, service->kill_window_delay);
\r
63 else if (editing) RegDeleteValue(key, NSSM_REG_KILL_WINDOW_GRACE_PERIOD);
\r
64 if (service->kill_threads_delay != NSSM_KILL_THREADS_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_THREADS_GRACE_PERIOD, service->kill_threads_delay);
\r
65 else if (editing) RegDeleteValue(key, NSSM_REG_KILL_THREADS_GRACE_PERIOD);
\r
66 if (service->stdin_path[0] || editing) {
\r
67 if (service->stdin_path[0]) set_expand_string(key, NSSM_REG_STDIN, service->stdin_path);
\r
68 else if (editing) RegDeleteValue(key, NSSM_REG_STDIN);
\r
69 if (service->stdin_sharing != NSSM_STDIN_SHARING) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_SHARING, service->stdin_sharing);
\r
70 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_SHARING);
\r
71 if (service->stdin_disposition != NSSM_STDIN_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_DISPOSITION, service->stdin_disposition);
\r
72 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_DISPOSITION);
\r
73 if (service->stdin_flags != NSSM_STDIN_FLAGS) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_FLAGS, service->stdin_flags);
\r
74 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_FLAGS);
\r
76 if (service->stdout_path[0] || editing) {
\r
77 if (service->stdout_path[0]) set_expand_string(key, NSSM_REG_STDOUT, service->stdout_path);
\r
78 else if (editing) RegDeleteValue(key, NSSM_REG_STDOUT);
\r
79 if (service->stdout_sharing != NSSM_STDOUT_SHARING) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_SHARING, service->stdout_sharing);
\r
80 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_SHARING);
\r
81 if (service->stdout_disposition != NSSM_STDOUT_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_DISPOSITION, service->stdout_disposition);
\r
82 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_DISPOSITION);
\r
83 if (service->stdout_flags != NSSM_STDOUT_FLAGS) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_FLAGS, service->stdout_flags);
\r
84 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_FLAGS);
\r
86 if (service->stderr_path[0] || editing) {
\r
87 if (service->stderr_path[0]) set_expand_string(key, NSSM_REG_STDERR, service->stderr_path);
\r
88 else if (editing) RegDeleteValue(key, NSSM_REG_STDERR);
\r
89 if (service->stderr_sharing != NSSM_STDERR_SHARING) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_SHARING, service->stderr_sharing);
\r
90 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_SHARING);
\r
91 if (service->stderr_disposition != NSSM_STDERR_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_DISPOSITION, service->stderr_disposition);
\r
92 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_DISPOSITION);
\r
93 if (service->stderr_flags != NSSM_STDERR_FLAGS) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_FLAGS, service->stderr_flags);
\r
94 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_FLAGS);
\r
96 if (service->rotate_files) set_number(key, NSSM_REG_ROTATE, 1);
\r
97 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE);
\r
98 if (service->rotate_seconds) set_number(key, NSSM_REG_ROTATE_SECONDS, service->rotate_seconds);
\r
99 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_SECONDS);
\r
100 if (service->rotate_bytes_low) set_number(key, NSSM_REG_ROTATE_BYTES_LOW, service->rotate_bytes_low);
\r
101 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_BYTES_LOW);
\r
102 if (service->rotate_bytes_high) set_number(key, NSSM_REG_ROTATE_BYTES_HIGH, service->rotate_bytes_high);
\r
103 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_BYTES_HIGH);
\r
106 if (service->env) {
\r
107 if (RegSetValueEx(key, NSSM_REG_ENV, 0, REG_MULTI_SZ, (const unsigned char *) service->env, (unsigned long) service->envlen * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
108 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV, error_string(GetLastError()), 0);
\r
111 else if (editing) RegDeleteValue(key, NSSM_REG_ENV);
\r
112 if (service->env_extra) {
\r
113 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
114 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV_EXTRA, error_string(GetLastError()), 0);
\r
117 else if (editing) RegDeleteValue(key, NSSM_REG_ENV_EXTRA);
\r
119 /* Close registry. */
\r
125 int create_exit_action(TCHAR *service_name, const TCHAR *action_string, bool editing) {
\r
127 TCHAR registry[KEY_LENGTH];
\r
128 if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, NSSM_REGISTRY _T("\\%s"), service_name, NSSM_REG_EXIT) < 0) {
\r
129 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("NSSM_REG_EXIT"), _T("create_exit_action()"), 0);
\r
133 /* Try to open the registry */
\r
135 unsigned long disposition;
\r
136 if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, KEY_WRITE, 0, &key, &disposition) != ERROR_SUCCESS) {
\r
137 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
141 /* Do nothing if the key already existed */
\r
142 if (disposition == REG_OPENED_EXISTING_KEY && ! editing) {
\r
147 /* Create the default value */
\r
148 if (RegSetValueEx(key, 0, 0, REG_SZ, (const unsigned char *) action_string, (unsigned long) (_tcslen(action_string) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
149 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_EXIT, error_string(GetLastError()), 0);
\r
154 /* Close registry */
\r
160 int set_environment(TCHAR *service_name, HKEY key, TCHAR *value, TCHAR **env, unsigned long *envlen) {
\r
161 unsigned long type = REG_MULTI_SZ;
\r
163 /* Dummy test to find buffer size */
\r
164 unsigned long ret = RegQueryValueEx(key, value, 0, &type, NULL, envlen);
\r
165 if (ret != ERROR_SUCCESS) {
\r
168 /* The service probably doesn't have any environment configured */
\r
169 if (ret == ERROR_FILE_NOT_FOUND) return 0;
\r
170 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
174 if (type != REG_MULTI_SZ) {
\r
177 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_INVALID_ENVIRONMENT_STRING_TYPE, value, service_name, 0);
\r
181 /* Probably not possible */
\r
182 if (! *envlen) return 0;
\r
184 /* Previously initialised? */
\r
185 if (*env) HeapFree(GetProcessHeap(), 0, *env);
\r
187 *env = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, *envlen);
\r
190 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, value, _T("set_environment()"), 0);
\r
194 /* Actually get the strings */
\r
195 ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) *env, envlen);
\r
196 if (ret != ERROR_SUCCESS) {
\r
197 HeapFree(GetProcessHeap(), 0, *env);
\r
200 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
207 /* Replace NULL with CRLF. Leave NULL NULL as the end marker. */
\r
208 int format_environment(TCHAR *env, unsigned long envlen, TCHAR **formatted, unsigned long *newlen) {
\r
209 unsigned long i, j;
\r
217 for (i = 0; i < envlen; i++) if (! env[i] && env[i + 1]) ++*newlen;
\r
219 *formatted = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *newlen * sizeof(TCHAR));
\r
220 if (! *formatted) {
\r
225 for (i = 0, j = 0; i < envlen; i++) {
\r
226 (*formatted)[j] = env[i];
\r
229 (*formatted)[j] = _T('\r');
\r
230 (*formatted)[++j] = _T('\n');
\r
239 /* Strip CR and replace LF with NULL. */
\r
240 int unformat_environment(TCHAR *env, unsigned long envlen, TCHAR **unformatted, unsigned long *newlen) {
\r
241 unsigned long i, j;
\r
249 for (i = 0; i < envlen; i++) if (env[i] != _T('\r')) ++*newlen;
\r
250 /* Must end with two NULLs. */
\r
253 *unformatted = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *newlen * sizeof(TCHAR));
\r
254 if (! *unformatted) return 1;
\r
256 for (i = 0, j = 0; i < envlen; i++) {
\r
257 if (env[i] == _T('\r')) continue;
\r
258 if (env[i] == _T('\n')) (*unformatted)[j] = _T('\0');
\r
259 else (*unformatted)[j] = env[i];
\r
266 int expand_parameter(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise, bool must_exist) {
\r
267 TCHAR *buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, datalen);
\r
269 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, value, _T("expand_parameter()"), 0);
\r
273 ZeroMemory(data, datalen);
\r
275 unsigned long type = REG_EXPAND_SZ;
\r
276 unsigned long buflen = datalen;
\r
278 unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);
\r
279 if (ret != ERROR_SUCCESS) {
\r
280 unsigned long error = GetLastError();
\r
281 HeapFree(GetProcessHeap(), 0, buffer);
\r
283 if (ret == ERROR_FILE_NOT_FOUND) {
\r
284 if (! must_exist) return 0;
\r
287 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(error), 0);
\r
291 /* Paths aren't allowed to contain quotes. */
\r
292 if (sanitise) PathUnquoteSpaces(buffer);
\r
294 /* Technically we shouldn't expand environment strings from REG_SZ values */
\r
295 if (type != REG_EXPAND_SZ) {
\r
296 memmove(data, buffer, buflen);
\r
297 HeapFree(GetProcessHeap(), 0, buffer);
\r
301 ret = ExpandEnvironmentStrings((TCHAR *) buffer, data, datalen);
\r
302 if (! ret || ret > datalen) {
\r
303 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_EXPANDENVIRONMENTSTRINGS_FAILED, buffer, error_string(GetLastError()), 0);
\r
304 HeapFree(GetProcessHeap(), 0, buffer);
\r
308 HeapFree(GetProcessHeap(), 0, buffer);
\r
312 int expand_parameter(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise) {
\r
313 return expand_parameter(key, value, data, datalen, sanitise, true);
\r
317 Sets a string in the registry.
\r
318 Returns: 0 if it was set.
\r
321 int set_expand_string(HKEY key, TCHAR *value, TCHAR *string) {
\r
322 if (RegSetValueEx(key, value, 0, REG_EXPAND_SZ, (const unsigned char *) string, (unsigned long) (_tcslen(string) + 1) * sizeof(TCHAR)) == ERROR_SUCCESS) return 0;
\r
323 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
328 Set an unsigned long in the registry.
\r
329 Returns: 0 if it was set.
\r
332 int set_number(HKEY key, TCHAR *value, unsigned long number) {
\r
333 if (RegSetValueEx(key, value, 0, REG_DWORD, (const unsigned char *) &number, sizeof(number)) == ERROR_SUCCESS) return 0;
\r
334 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
339 Query an unsigned long from the registry.
\r
340 Returns: 1 if a number was retrieved.
\r
341 0 if none was found and must_exist is false.
\r
342 -1 if none was found and must_exist is true.
\r
345 int get_number(HKEY key, TCHAR *value, unsigned long *number, bool must_exist) {
\r
346 unsigned long type = REG_DWORD;
\r
347 unsigned long number_len = sizeof(unsigned long);
\r
349 int ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) number, &number_len);
\r
350 if (ret == ERROR_SUCCESS) return 1;
\r
352 if (ret == ERROR_FILE_NOT_FOUND) {
\r
353 if (! must_exist) return 0;
\r
356 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
357 if (ret == ERROR_FILE_NOT_FOUND) return -1;
\r
362 int get_number(HKEY key, TCHAR *value, unsigned long *number) {
\r
363 return get_number(key, value, number, true);
\r
366 void override_milliseconds(TCHAR *service_name, HKEY key, TCHAR *value, unsigned long *buffer, unsigned long default_value, unsigned long event) {
\r
367 unsigned long type = REG_DWORD;
\r
368 unsigned long buflen = sizeof(unsigned long);
\r
370 unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);
\r
371 if (ret != ERROR_SUCCESS) {
\r
372 if (ret != ERROR_FILE_NOT_FOUND) {
\r
373 if (type != REG_DWORD) {
\r
374 TCHAR milliseconds[16];
\r
375 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), default_value);
\r
376 log_event(EVENTLOG_WARNING_TYPE, event, service_name, value, milliseconds, 0);
\r
378 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
383 if (! ok) *buffer = default_value;
\r
386 HKEY open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam) {
\r
388 TCHAR registry[KEY_LENGTH];
\r
392 if (sub) ret = _sntprintf_s(registry, _countof(registry), _TRUNCATE, NSSM_REGISTRY _T("\\%s"), service_name, sub);
\r
393 else ret = _sntprintf_s(registry, _countof(registry), _TRUNCATE, NSSM_REGISTRY, service_name);
\r
395 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("NSSM_REGISTRY"), _T("open_registry()"), 0);
\r
399 if (sam & KEY_WRITE) {
\r
400 if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, sam, 0, &key, 0) != ERROR_SUCCESS) {
\r
401 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
406 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, registry, 0, sam, &key) != ERROR_SUCCESS) {
\r
407 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
415 HKEY open_registry(const TCHAR *service_name, REGSAM sam) {
\r
416 return open_registry(service_name, 0, sam);
\r
419 int get_parameters(nssm_service_t *service, STARTUPINFO *si) {
\r
422 /* Try to open the registry */
\r
423 HKEY key = open_registry(service->name, KEY_READ);
\r
424 if (! key) return 1;
\r
426 /* Try to get executable file - MUST succeed */
\r
427 if (expand_parameter(key, NSSM_REG_EXE, service->exe, sizeof(service->exe), false)) {
\r
432 /* Try to get flags - may fail and we don't care */
\r
433 if (expand_parameter(key, NSSM_REG_FLAGS, service->flags, sizeof(service->flags), false)) {
\r
434 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_FLAGS, NSSM_REG_FLAGS, service->name, service->exe, 0);
\r
435 ZeroMemory(service->flags, sizeof(service->flags));
\r
438 /* Try to get startup directory - may fail and we fall back to a default */
\r
439 if (expand_parameter(key, NSSM_REG_DIR, service->dir, sizeof(service->dir), true) || ! service->dir[0]) {
\r
440 _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);
\r
441 strip_basename(service->dir);
\r
442 if (service->dir[0] == _T('\0')) {
\r
444 ret = GetWindowsDirectory(service->dir, sizeof(service->dir));
\r
445 if (! ret || ret > sizeof(service->dir)) {
\r
446 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_NO_DIR_AND_NO_FALLBACK, NSSM_REG_DIR, service->name, 0);
\r
451 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_DIR, NSSM_REG_DIR, service->name, service->dir, 0);
\r
454 /* Try to get environment variables - may fail */
\r
455 set_environment(service->name, key, NSSM_REG_ENV, &service->env, &service->envlen);
\r
456 /* Environment variables to add to existing rather than replace - may fail. */
\r
457 set_environment(service->name, key, NSSM_REG_ENV_EXTRA, &service->env_extra, &service->env_extralen);
\r
460 if (service->env_extra) {
\r
461 /* Append these to any other environment variables set. */
\r
462 if (service->env) {
\r
463 /* Append extra variables to configured variables. */
\r
464 unsigned long envlen = service->envlen + service->env_extralen - 1;
\r
465 TCHAR *env = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, envlen);
\r
467 memmove(env, service->env, service->envlen - sizeof(TCHAR));
\r
468 /* envlen is in bytes. */
\r
469 memmove(env + (service->envlen / sizeof(TCHAR)) - 1, service->env_extra, service->env_extralen);
\r
471 HeapFree(GetProcessHeap(), 0, service->env);
\r
472 service->env = env;
\r
473 service->envlen = envlen;
\r
475 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("environment"), _T("get_parameters()"), 0);
\r
478 /* Append extra variables to our environment. */
\r
480 size_t envlen, len;
\r
482 env = service->env_extra;
\r
485 envlen = _tcslen(env) + 1;
\r
486 for (s = env; *s && *s != _T('='); s++);
\r
487 if (*s == _T('=')) *s++ = _T('\0');
\r
488 if (! SetEnvironmentVariable(env, s)) log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_SETENVIRONMENTVARIABLE_FAILED, env, s, error_string(GetLastError()), 0);
\r
495 /* Try to get file rotation settings - may fail. */
\r
496 unsigned long rotate_files;
\r
497 if (get_number(key, NSSM_REG_ROTATE, &rotate_files, false) == 1) {
\r
498 if (rotate_files) service->rotate_files = true;
\r
499 else service->rotate_files = false;
\r
501 else service->rotate_files = false;
\r
502 if (get_number(key, NSSM_REG_ROTATE_SECONDS, &service->rotate_seconds, false) != 1) service->rotate_seconds = 0;
\r
503 if (get_number(key, NSSM_REG_ROTATE_BYTES_LOW, &service->rotate_bytes_low, false) != 1) service->rotate_bytes_low = 0;
\r
504 if (get_number(key, NSSM_REG_ROTATE_BYTES_HIGH, &service->rotate_bytes_high, false) != 1) service->rotate_bytes_high = 0;
\r
506 /* Change to startup directory in case stdout/stderr are relative paths. */
\r
507 TCHAR cwd[MAX_PATH];
\r
508 GetCurrentDirectory(_countof(cwd), cwd);
\r
509 SetCurrentDirectory(service->dir);
\r
511 /* Try to get stdout and stderr */
\r
512 if (get_output_handles(service, key, si)) {
\r
513 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);
\r
515 SetCurrentDirectory(cwd);
\r
519 /* Change back in case the startup directory needs to be deleted. */
\r
520 SetCurrentDirectory(cwd);
\r
522 /* Try to get throttle restart delay */
\r
523 override_milliseconds(service->name, key, NSSM_REG_THROTTLE, &service->throttle_delay, NSSM_RESET_THROTTLE_RESTART, NSSM_EVENT_BOGUS_THROTTLE);
\r
525 /* Try to get service stop flags. */
\r
526 unsigned long type = REG_DWORD;
\r
527 unsigned long stop_method_skip;
\r
528 unsigned long buflen = sizeof(stop_method_skip);
\r
529 bool stop_ok = false;
\r
530 ret = RegQueryValueEx(key, NSSM_REG_STOP_METHOD_SKIP, 0, &type, (unsigned char *) &stop_method_skip, &buflen);
\r
531 if (ret != ERROR_SUCCESS) {
\r
532 if (ret != ERROR_FILE_NOT_FOUND) {
\r
533 if (type != REG_DWORD) {
\r
534 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_STOP_METHOD_SKIP, service->name, NSSM_REG_STOP_METHOD_SKIP, NSSM, 0);
\r
536 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, NSSM_REG_STOP_METHOD_SKIP, error_string(GetLastError()), 0);
\r
539 else stop_ok = true;
\r
541 /* Try all methods except those requested to be skipped. */
\r
542 service->stop_method = ~0;
\r
543 if (stop_ok) service->stop_method &= ~stop_method_skip;
\r
545 /* Try to get kill delays - may fail. */
\r
546 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
547 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
548 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
550 /* Try to get default exit action. */
\r
551 bool default_action;
\r
552 service->default_exit_action = NSSM_EXIT_RESTART;
\r
553 TCHAR action_string[ACTION_LEN];
\r
554 if (! get_exit_action(service->name, 0, action_string, &default_action)) {
\r
555 for (int i = 0; exit_action_strings[i]; i++) {
\r
556 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
557 service->default_exit_action = i;
\r
563 /* Close registry */
\r
570 Sets the string for the exit action corresponding to the exit code.
\r
572 ret is a pointer to an unsigned long containing the exit code.
\r
573 If ret is NULL, we retrieve the default exit action unconditionally.
\r
575 action is a buffer which receives the string.
\r
577 default_action is a pointer to a bool which is set to false if there
\r
578 was an explicit string for the given exit code, or true if we are
\r
579 returning the default action.
\r
581 Returns: 0 on success.
\r
584 int get_exit_action(const TCHAR *service_name, unsigned long *ret, TCHAR *action, bool *default_action) {
\r
585 /* Are we returning the default action or a status-specific one? */
\r
586 *default_action = ! ret;
\r
588 /* Try to open the registry */
\r
589 HKEY key = open_registry(service_name, NSSM_REG_EXIT, KEY_READ);
\r
590 if (! key) return 1;
\r
592 unsigned long type = REG_SZ;
\r
593 unsigned long action_len = ACTION_LEN;
\r
596 if (! ret) code[0] = _T('\0');
\r
597 else if (_sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), *ret) < 0) {
\r
599 return get_exit_action(service_name, 0, action, default_action);
\r
601 if (RegQueryValueEx(key, code, 0, &type, (unsigned char *) action, &action_len) != ERROR_SUCCESS) {
\r
603 /* Try again with * as the key if an exit code was defined */
\r
604 if (ret) return get_exit_action(service_name, 0, action, default_action);
\r
608 /* Close registry */
\r