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 int expand_parameter(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise, bool must_exist) {
\r
208 TCHAR *buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, datalen);
\r
210 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, value, _T("expand_parameter()"), 0);
\r
214 ZeroMemory(data, datalen);
\r
216 unsigned long type = REG_EXPAND_SZ;
\r
217 unsigned long buflen = datalen;
\r
219 unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);
\r
220 if (ret != ERROR_SUCCESS) {
\r
221 unsigned long error = GetLastError();
\r
222 HeapFree(GetProcessHeap(), 0, buffer);
\r
224 if (ret == ERROR_FILE_NOT_FOUND) {
\r
225 if (! must_exist) return 0;
\r
228 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(error), 0);
\r
232 /* Paths aren't allowed to contain quotes. */
\r
233 if (sanitise) PathUnquoteSpaces(buffer);
\r
235 /* Technically we shouldn't expand environment strings from REG_SZ values */
\r
236 if (type != REG_EXPAND_SZ) {
\r
237 memmove(data, buffer, buflen);
\r
238 HeapFree(GetProcessHeap(), 0, buffer);
\r
242 ret = ExpandEnvironmentStrings((TCHAR *) buffer, data, datalen);
\r
243 if (! ret || ret > datalen) {
\r
244 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_EXPANDENVIRONMENTSTRINGS_FAILED, buffer, error_string(GetLastError()), 0);
\r
245 HeapFree(GetProcessHeap(), 0, buffer);
\r
249 HeapFree(GetProcessHeap(), 0, buffer);
\r
253 int expand_parameter(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise) {
\r
254 return expand_parameter(key, value, data, datalen, sanitise, true);
\r
258 Sets a string in the registry.
\r
259 Returns: 0 if it was set.
\r
262 int set_expand_string(HKEY key, TCHAR *value, TCHAR *string) {
\r
263 if (RegSetValueEx(key, value, 0, REG_EXPAND_SZ, (const unsigned char *) string, (unsigned long) (_tcslen(string) + 1) * sizeof(TCHAR)) == ERROR_SUCCESS) return 0;
\r
264 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
269 Set an unsigned long in the registry.
\r
270 Returns: 0 if it was set.
\r
273 int set_number(HKEY key, TCHAR *value, unsigned long number) {
\r
274 if (RegSetValueEx(key, value, 0, REG_DWORD, (const unsigned char *) &number, sizeof(number)) == ERROR_SUCCESS) return 0;
\r
275 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
280 Query an unsigned long from the registry.
\r
281 Returns: 1 if a number was retrieved.
\r
282 0 if none was found and must_exist is false.
\r
283 -1 if none was found and must_exist is true.
\r
286 int get_number(HKEY key, TCHAR *value, unsigned long *number, bool must_exist) {
\r
287 unsigned long type = REG_DWORD;
\r
288 unsigned long number_len = sizeof(unsigned long);
\r
290 int ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) number, &number_len);
\r
291 if (ret == ERROR_SUCCESS) return 1;
\r
293 if (ret == ERROR_FILE_NOT_FOUND) {
\r
294 if (! must_exist) return 0;
\r
297 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
298 if (ret == ERROR_FILE_NOT_FOUND) return -1;
\r
303 int get_number(HKEY key, TCHAR *value, unsigned long *number) {
\r
304 return get_number(key, value, number, true);
\r
307 void override_milliseconds(TCHAR *service_name, HKEY key, TCHAR *value, unsigned long *buffer, unsigned long default_value, unsigned long event) {
\r
308 unsigned long type = REG_DWORD;
\r
309 unsigned long buflen = sizeof(unsigned long);
\r
311 unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);
\r
312 if (ret != ERROR_SUCCESS) {
\r
313 if (ret != ERROR_FILE_NOT_FOUND) {
\r
314 if (type != REG_DWORD) {
\r
315 TCHAR milliseconds[16];
\r
316 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), default_value);
\r
317 log_event(EVENTLOG_WARNING_TYPE, event, service_name, value, milliseconds, 0);
\r
319 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
324 if (! ok) *buffer = default_value;
\r
327 HKEY open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam) {
\r
329 TCHAR registry[KEY_LENGTH];
\r
333 if (sub) ret = _sntprintf_s(registry, _countof(registry), _TRUNCATE, NSSM_REGISTRY _T("\\%s"), service_name, sub);
\r
334 else ret = _sntprintf_s(registry, _countof(registry), _TRUNCATE, NSSM_REGISTRY, service_name);
\r
336 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("NSSM_REGISTRY"), _T("open_registry()"), 0);
\r
340 if (sam & KEY_WRITE) {
\r
341 if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, sam, 0, &key, 0) != ERROR_SUCCESS) {
\r
342 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
347 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, registry, 0, sam, &key) != ERROR_SUCCESS) {
\r
348 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
356 HKEY open_registry(const TCHAR *service_name, REGSAM sam) {
\r
357 return open_registry(service_name, 0, sam);
\r
360 int get_parameters(nssm_service_t *service, STARTUPINFO *si) {
\r
363 /* Try to open the registry */
\r
364 HKEY key = open_registry(service->name, KEY_READ);
\r
365 if (! key) return 1;
\r
367 /* Try to get executable file - MUST succeed */
\r
368 if (expand_parameter(key, NSSM_REG_EXE, service->exe, sizeof(service->exe), false)) {
\r
373 /* Try to get flags - may fail and we don't care */
\r
374 if (expand_parameter(key, NSSM_REG_FLAGS, service->flags, sizeof(service->flags), false)) {
\r
375 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_FLAGS, NSSM_REG_FLAGS, service->name, service->exe, 0);
\r
376 ZeroMemory(service->flags, sizeof(service->flags));
\r
379 /* Try to get startup directory - may fail and we fall back to a default */
\r
380 if (expand_parameter(key, NSSM_REG_DIR, service->dir, sizeof(service->dir), true) || ! service->dir[0]) {
\r
381 _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);
\r
382 strip_basename(service->dir);
\r
383 if (service->dir[0] == _T('\0')) {
\r
385 ret = GetWindowsDirectory(service->dir, sizeof(service->dir));
\r
386 if (! ret || ret > sizeof(service->dir)) {
\r
387 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_NO_DIR_AND_NO_FALLBACK, NSSM_REG_DIR, service->name, 0);
\r
392 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_DIR, NSSM_REG_DIR, service->name, service->dir, 0);
\r
395 /* Try to get environment variables - may fail */
\r
396 set_environment(service->name, key, NSSM_REG_ENV, &service->env, &service->envlen);
\r
397 /* Environment variables to add to existing rather than replace - may fail. */
\r
398 set_environment(service->name, key, NSSM_REG_ENV_EXTRA, &service->env_extra, &service->env_extralen);
\r
401 if (service->env_extra) {
\r
402 /* Append these to any other environment variables set. */
\r
403 if (service->env) {
\r
404 /* Append extra variables to configured variables. */
\r
405 unsigned long envlen = service->envlen + service->env_extralen - 1;
\r
406 TCHAR *env = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, envlen);
\r
408 memmove(env, service->env, service->envlen - sizeof(TCHAR));
\r
409 /* envlen is in bytes. */
\r
410 memmove(env + (service->envlen / sizeof(TCHAR)) - 1, service->env_extra, service->env_extralen);
\r
412 HeapFree(GetProcessHeap(), 0, service->env);
\r
413 service->env = env;
\r
414 service->envlen = envlen;
\r
416 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("environment"), _T("get_parameters()"), 0);
\r
419 /* Append extra variables to our environment. */
\r
421 size_t envlen, len;
\r
423 env = service->env_extra;
\r
426 envlen = _tcslen(env) + 1;
\r
427 for (s = env; *s && *s != _T('='); s++);
\r
428 if (*s == _T('=')) *s++ = _T('\0');
\r
429 if (! SetEnvironmentVariable(env, s)) log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_SETENVIRONMENTVARIABLE_FAILED, env, s, error_string(GetLastError()), 0);
\r
436 /* Try to get file rotation settings - may fail. */
\r
437 unsigned long rotate_files;
\r
438 if (get_number(key, NSSM_REG_ROTATE, &rotate_files, false) == 1) {
\r
439 if (rotate_files) service->rotate_files = true;
\r
440 else service->rotate_files = false;
\r
442 else service->rotate_files = false;
\r
443 if (get_number(key, NSSM_REG_ROTATE_SECONDS, &service->rotate_seconds, false) != 1) service->rotate_seconds = 0;
\r
444 if (get_number(key, NSSM_REG_ROTATE_BYTES_LOW, &service->rotate_bytes_low, false) != 1) service->rotate_bytes_low = 0;
\r
445 if (get_number(key, NSSM_REG_ROTATE_BYTES_HIGH, &service->rotate_bytes_high, false) != 1) service->rotate_bytes_high = 0;
\r
447 /* Change to startup directory in case stdout/stderr are relative paths. */
\r
448 TCHAR cwd[MAX_PATH];
\r
449 GetCurrentDirectory(_countof(cwd), cwd);
\r
450 SetCurrentDirectory(service->dir);
\r
452 /* Try to get stdout and stderr */
\r
453 if (get_output_handles(service, key, si)) {
\r
454 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);
\r
456 SetCurrentDirectory(cwd);
\r
460 /* Change back in case the startup directory needs to be deleted. */
\r
461 SetCurrentDirectory(cwd);
\r
463 /* Try to get throttle restart delay */
\r
464 override_milliseconds(service->name, key, NSSM_REG_THROTTLE, &service->throttle_delay, NSSM_RESET_THROTTLE_RESTART, NSSM_EVENT_BOGUS_THROTTLE);
\r
466 /* Try to get service stop flags. */
\r
467 unsigned long type = REG_DWORD;
\r
468 unsigned long stop_method_skip;
\r
469 unsigned long buflen = sizeof(stop_method_skip);
\r
470 bool stop_ok = false;
\r
471 ret = RegQueryValueEx(key, NSSM_REG_STOP_METHOD_SKIP, 0, &type, (unsigned char *) &stop_method_skip, &buflen);
\r
472 if (ret != ERROR_SUCCESS) {
\r
473 if (ret != ERROR_FILE_NOT_FOUND) {
\r
474 if (type != REG_DWORD) {
\r
475 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_STOP_METHOD_SKIP, service->name, NSSM_REG_STOP_METHOD_SKIP, NSSM, 0);
\r
477 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, NSSM_REG_STOP_METHOD_SKIP, error_string(GetLastError()), 0);
\r
480 else stop_ok = true;
\r
482 /* Try all methods except those requested to be skipped. */
\r
483 service->stop_method = ~0;
\r
484 if (stop_ok) service->stop_method &= ~stop_method_skip;
\r
486 /* Try to get kill delays - may fail. */
\r
487 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
488 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
489 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
491 /* Try to get default exit action. */
\r
492 bool default_action;
\r
493 service->default_exit_action = NSSM_EXIT_RESTART;
\r
494 TCHAR action_string[ACTION_LEN];
\r
495 if (! get_exit_action(service->name, 0, action_string, &default_action)) {
\r
496 for (int i = 0; exit_action_strings[i]; i++) {
\r
497 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
498 service->default_exit_action = i;
\r
504 /* Close registry */
\r
511 Sets the string for the exit action corresponding to the exit code.
\r
513 ret is a pointer to an unsigned long containing the exit code.
\r
514 If ret is NULL, we retrieve the default exit action unconditionally.
\r
516 action is a buffer which receives the string.
\r
518 default_action is a pointer to a bool which is set to false if there
\r
519 was an explicit string for the given exit code, or true if we are
\r
520 returning the default action.
\r
522 Returns: 0 on success.
\r
525 int get_exit_action(const TCHAR *service_name, unsigned long *ret, TCHAR *action, bool *default_action) {
\r
526 /* Are we returning the default action or a status-specific one? */
\r
527 *default_action = ! ret;
\r
529 /* Try to open the registry */
\r
530 HKEY key = open_registry(service_name, NSSM_REG_EXIT, KEY_READ);
\r
531 if (! key) return 1;
\r
533 unsigned long type = REG_SZ;
\r
534 unsigned long action_len = ACTION_LEN;
\r
537 if (! ret) code[0] = _T('\0');
\r
538 else if (_sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), *ret) < 0) {
\r
540 return get_exit_action(service_name, 0, action, default_action);
\r
542 if (RegQueryValueEx(key, code, 0, &type, (unsigned char *) action, &action_len) != ERROR_SUCCESS) {
\r
544 /* Try again with * as the key if an exit code was defined */
\r
545 if (ret) return get_exit_action(service_name, 0, action, default_action);
\r
549 /* Close registry */
\r