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 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 expand_parameter(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, 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("expand_parameter()"), 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 /* Technically we shouldn't expand environment strings from REG_SZ values */
\r
313 if (type != REG_EXPAND_SZ) {
\r
314 memmove(data, buffer, buflen);
\r
315 HeapFree(GetProcessHeap(), 0, buffer);
\r
319 ret = ExpandEnvironmentStrings((TCHAR *) buffer, data, datalen);
\r
320 if (! ret || ret > datalen) {
\r
321 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_EXPANDENVIRONMENTSTRINGS_FAILED, buffer, error_string(GetLastError()), 0);
\r
322 HeapFree(GetProcessHeap(), 0, buffer);
\r
326 HeapFree(GetProcessHeap(), 0, buffer);
\r
330 int expand_parameter(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise) {
\r
331 return expand_parameter(key, value, data, datalen, sanitise, true);
\r
335 Sets a string in the registry.
\r
336 Returns: 0 if it was set.
\r
339 int set_expand_string(HKEY key, TCHAR *value, TCHAR *string) {
\r
340 if (RegSetValueEx(key, value, 0, REG_EXPAND_SZ, (const unsigned char *) string, (unsigned long) (_tcslen(string) + 1) * sizeof(TCHAR)) == ERROR_SUCCESS) return 0;
\r
341 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
346 Set an unsigned long in the registry.
\r
347 Returns: 0 if it was set.
\r
350 int set_number(HKEY key, TCHAR *value, unsigned long number) {
\r
351 if (RegSetValueEx(key, value, 0, REG_DWORD, (const unsigned char *) &number, sizeof(number)) == ERROR_SUCCESS) return 0;
\r
352 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
357 Query an unsigned long from the registry.
\r
358 Returns: 1 if a number was retrieved.
\r
359 0 if none was found and must_exist is false.
\r
360 -1 if none was found and must_exist is true.
\r
363 int get_number(HKEY key, TCHAR *value, unsigned long *number, bool must_exist) {
\r
364 unsigned long type = REG_DWORD;
\r
365 unsigned long number_len = sizeof(unsigned long);
\r
367 int ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) number, &number_len);
\r
368 if (ret == ERROR_SUCCESS) return 1;
\r
370 if (ret == ERROR_FILE_NOT_FOUND) {
\r
371 if (! must_exist) return 0;
\r
374 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
375 if (ret == ERROR_FILE_NOT_FOUND) return -1;
\r
380 int get_number(HKEY key, TCHAR *value, unsigned long *number) {
\r
381 return get_number(key, value, number, true);
\r
384 void override_milliseconds(TCHAR *service_name, HKEY key, TCHAR *value, unsigned long *buffer, unsigned long default_value, unsigned long event) {
\r
385 unsigned long type = REG_DWORD;
\r
386 unsigned long buflen = sizeof(unsigned long);
\r
388 unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);
\r
389 if (ret != ERROR_SUCCESS) {
\r
390 if (ret != ERROR_FILE_NOT_FOUND) {
\r
391 if (type != REG_DWORD) {
\r
392 TCHAR milliseconds[16];
\r
393 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), default_value);
\r
394 log_event(EVENTLOG_WARNING_TYPE, event, service_name, value, milliseconds, 0);
\r
396 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
401 if (! ok) *buffer = default_value;
\r
404 HKEY open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam) {
\r
406 TCHAR registry[KEY_LENGTH];
\r
410 if (sub) ret = _sntprintf_s(registry, _countof(registry), _TRUNCATE, NSSM_REGISTRY _T("\\%s"), service_name, sub);
\r
411 else ret = _sntprintf_s(registry, _countof(registry), _TRUNCATE, NSSM_REGISTRY, service_name);
\r
413 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("NSSM_REGISTRY"), _T("open_registry()"), 0);
\r
417 if (sam & KEY_WRITE) {
\r
418 if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, sam, 0, &key, 0) != ERROR_SUCCESS) {
\r
419 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
424 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, registry, 0, sam, &key) != ERROR_SUCCESS) {
\r
425 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
433 HKEY open_registry(const TCHAR *service_name, REGSAM sam) {
\r
434 return open_registry(service_name, 0, sam);
\r
437 int get_parameters(nssm_service_t *service, STARTUPINFO *si) {
\r
440 /* Try to open the registry */
\r
441 HKEY key = open_registry(service->name, KEY_READ);
\r
442 if (! key) return 1;
\r
444 /* Try to get executable file - MUST succeed */
\r
445 if (expand_parameter(key, NSSM_REG_EXE, service->exe, sizeof(service->exe), false)) {
\r
450 /* Try to get flags - may fail and we don't care */
\r
451 if (expand_parameter(key, NSSM_REG_FLAGS, service->flags, sizeof(service->flags), false)) {
\r
452 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_FLAGS, NSSM_REG_FLAGS, service->name, service->exe, 0);
\r
453 ZeroMemory(service->flags, sizeof(service->flags));
\r
456 /* Try to get startup directory - may fail and we fall back to a default */
\r
457 if (expand_parameter(key, NSSM_REG_DIR, service->dir, sizeof(service->dir), true) || ! service->dir[0]) {
\r
458 _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);
\r
459 strip_basename(service->dir);
\r
460 if (service->dir[0] == _T('\0')) {
\r
462 ret = GetWindowsDirectory(service->dir, sizeof(service->dir));
\r
463 if (! ret || ret > sizeof(service->dir)) {
\r
464 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_NO_DIR_AND_NO_FALLBACK, NSSM_REG_DIR, service->name, 0);
\r
469 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_DIR, NSSM_REG_DIR, service->name, service->dir, 0);
\r
472 /* Try to get processor affinity - may fail. */
\r
474 if (expand_parameter(key, NSSM_REG_AFFINITY, buffer, sizeof(buffer), false, false) || ! buffer[0]) service->affinity = 0LL;
\r
475 else if (affinity_string_to_mask(buffer, &service->affinity)) {
\r
476 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_AFFINITY_MASK, service->name, buffer);
\r
477 service->affinity = 0LL;
\r
480 DWORD_PTR affinity, system_affinity;
\r
482 if (GetProcessAffinityMask(GetCurrentProcess(), &affinity, &system_affinity)) {
\r
483 _int64 effective_affinity = service->affinity & system_affinity;
\r
484 if (effective_affinity != service->affinity) {
\r
486 if (! affinity_mask_to_string(system_affinity, &system)) {
\r
487 TCHAR *effective = 0;
\r
488 if (! affinity_mask_to_string(effective_affinity, &effective)) {
\r
489 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_EFFECTIVE_AFFINITY_MASK, service->name, buffer, system, effective, 0);
\r
491 HeapFree(GetProcessHeap(), 0, effective);
\r
493 HeapFree(GetProcessHeap(), 0, system);
\r
498 /* Try to get environment variables - may fail */
\r
499 set_environment(service->name, key, NSSM_REG_ENV, &service->env, &service->envlen);
\r
500 /* Environment variables to add to existing rather than replace - may fail. */
\r
501 set_environment(service->name, key, NSSM_REG_ENV_EXTRA, &service->env_extra, &service->env_extralen);
\r
504 if (service->env_extra) {
\r
506 unsigned long envlen;
\r
508 /* Copy our environment for the application. */
\r
509 if (! service->env) {
\r
510 TCHAR *rawenv = GetEnvironmentStrings();
\r
514 The environment block starts with variables of the form
\r
515 =C:=C:\Windows\System32 which we ignore.
\r
517 while (*env == _T('=')) {
\r
518 for ( ; *env; env++);
\r
524 for ( ; env[envlen]; envlen++);
\r
525 if (! env[++envlen]) break;
\r
529 service->envlen = envlen * sizeof(TCHAR);
\r
530 service->env = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, service->envlen);
\r
531 memmove(service->env, env, service->envlen);
\r
532 FreeEnvironmentStrings(rawenv);
\r
537 /* Append extra variables to configured variables. */
\r
538 if (service->env) {
\r
539 envlen = service->envlen + service->env_extralen - sizeof(TCHAR)/*?*/;
\r
540 env = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, envlen);
\r
542 memmove(env, service->env, service->envlen - sizeof(TCHAR));
\r
543 /* envlen is in bytes but env[i] is in characters. */
\r
544 memmove(env + (service->envlen / sizeof(TCHAR)) - 1, service->env_extra, service->env_extralen);
\r
546 HeapFree(GetProcessHeap(), 0, service->env);
\r
547 HeapFree(GetProcessHeap(), 0, service->env_extra);
\r
548 service->env = env;
\r
549 service->envlen = envlen;
\r
551 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("environment"), _T("get_parameters()"), 0);
\r
554 /* Huh? No environment at all? */
\r
555 service->env = service->env_extra;
\r
556 service->envlen = service->env_extralen;
\r
560 service->env_extra = 0;
\r
561 service->env_extralen = 0;
\r
564 /* Try to get priority - may fail. */
\r
565 unsigned long priority;
\r
566 if (get_number(key, NSSM_REG_PRIORITY, &priority, false) == 1) {
\r
567 if (priority == (priority & priority_mask())) service->priority = priority;
\r
568 else log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_PRIORITY, service->name, NSSM_REG_PRIORITY, 0);
\r
571 /* Try to get file rotation settings - may fail. */
\r
572 unsigned long rotate_files;
\r
573 if (get_number(key, NSSM_REG_ROTATE, &rotate_files, false) == 1) {
\r
574 if (rotate_files) service->rotate_files = true;
\r
575 else service->rotate_files = false;
\r
577 else service->rotate_files = false;
\r
578 if (get_number(key, NSSM_REG_ROTATE_ONLINE, &rotate_files, false) == 1) {
\r
579 if (rotate_files) service->rotate_stdout_online = service->rotate_stderr_online = true;
\r
580 else service->rotate_stdout_online = service->rotate_stderr_online = false;
\r
582 else service->rotate_stdout_online = service->rotate_stderr_online = false;
\r
583 if (get_number(key, NSSM_REG_ROTATE_SECONDS, &service->rotate_seconds, false) != 1) service->rotate_seconds = 0;
\r
584 if (get_number(key, NSSM_REG_ROTATE_BYTES_LOW, &service->rotate_bytes_low, false) != 1) service->rotate_bytes_low = 0;
\r
585 if (get_number(key, NSSM_REG_ROTATE_BYTES_HIGH, &service->rotate_bytes_high, false) != 1) service->rotate_bytes_high = 0;
\r
587 /* Change to startup directory in case stdout/stderr are relative paths. */
\r
588 TCHAR cwd[MAX_PATH];
\r
589 GetCurrentDirectory(_countof(cwd), cwd);
\r
590 SetCurrentDirectory(service->dir);
\r
592 /* Try to get stdout and stderr */
\r
593 if (get_output_handles(service, key, si)) {
\r
594 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);
\r
596 SetCurrentDirectory(cwd);
\r
600 /* Change back in case the startup directory needs to be deleted. */
\r
601 SetCurrentDirectory(cwd);
\r
603 /* Try to get mandatory restart delay */
\r
604 override_milliseconds(service->name, key, NSSM_REG_RESTART_DELAY, &service->restart_delay, 0, NSSM_EVENT_BOGUS_RESTART_DELAY);
\r
606 /* Try to get throttle restart delay */
\r
607 override_milliseconds(service->name, key, NSSM_REG_THROTTLE, &service->throttle_delay, NSSM_RESET_THROTTLE_RESTART, NSSM_EVENT_BOGUS_THROTTLE);
\r
609 /* Try to get service stop flags. */
\r
610 unsigned long type = REG_DWORD;
\r
611 unsigned long stop_method_skip;
\r
612 unsigned long buflen = sizeof(stop_method_skip);
\r
613 bool stop_ok = false;
\r
614 ret = RegQueryValueEx(key, NSSM_REG_STOP_METHOD_SKIP, 0, &type, (unsigned char *) &stop_method_skip, &buflen);
\r
615 if (ret != ERROR_SUCCESS) {
\r
616 if (ret != ERROR_FILE_NOT_FOUND) {
\r
617 if (type != REG_DWORD) {
\r
618 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_STOP_METHOD_SKIP, service->name, NSSM_REG_STOP_METHOD_SKIP, NSSM, 0);
\r
620 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, NSSM_REG_STOP_METHOD_SKIP, error_string(GetLastError()), 0);
\r
623 else stop_ok = true;
\r
625 /* Try all methods except those requested to be skipped. */
\r
626 service->stop_method = ~0;
\r
627 if (stop_ok) service->stop_method &= ~stop_method_skip;
\r
629 /* Try to get kill delays - may fail. */
\r
630 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
631 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
632 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
634 /* Try to get default exit action. */
\r
635 bool default_action;
\r
636 service->default_exit_action = NSSM_EXIT_RESTART;
\r
637 TCHAR action_string[ACTION_LEN];
\r
638 if (! get_exit_action(service->name, 0, action_string, &default_action)) {
\r
639 for (int i = 0; exit_action_strings[i]; i++) {
\r
640 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
641 service->default_exit_action = i;
\r
647 /* Close registry */
\r
654 Sets the string for the exit action corresponding to the exit code.
\r
656 ret is a pointer to an unsigned long containing the exit code.
\r
657 If ret is NULL, we retrieve the default exit action unconditionally.
\r
659 action is a buffer which receives the string.
\r
661 default_action is a pointer to a bool which is set to false if there
\r
662 was an explicit string for the given exit code, or true if we are
\r
663 returning the default action.
\r
665 Returns: 0 on success.
\r
668 int get_exit_action(const TCHAR *service_name, unsigned long *ret, TCHAR *action, bool *default_action) {
\r
669 /* Are we returning the default action or a status-specific one? */
\r
670 *default_action = ! ret;
\r
672 /* Try to open the registry */
\r
673 HKEY key = open_registry(service_name, NSSM_REG_EXIT, KEY_READ);
\r
674 if (! key) return 1;
\r
676 unsigned long type = REG_SZ;
\r
677 unsigned long action_len = ACTION_LEN;
\r
680 if (! ret) code[0] = _T('\0');
\r
681 else if (_sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), *ret) < 0) {
\r
683 return get_exit_action(service_name, 0, action, default_action);
\r
685 if (RegQueryValueEx(key, code, 0, &type, (unsigned char *) action, &action_len) != ERROR_SUCCESS) {
\r
687 /* Try again with * as the key if an exit code was defined */
\r
688 if (ret) return get_exit_action(service_name, 0, action, default_action);
\r
692 /* Close registry */
\r