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_seconds) set_number(key, NSSM_REG_ROTATE_SECONDS, service->rotate_seconds);
\r
115 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_SECONDS);
\r
116 if (service->rotate_bytes_low) set_number(key, NSSM_REG_ROTATE_BYTES_LOW, service->rotate_bytes_low);
\r
117 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_BYTES_LOW);
\r
118 if (service->rotate_bytes_high) set_number(key, NSSM_REG_ROTATE_BYTES_HIGH, service->rotate_bytes_high);
\r
119 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_BYTES_HIGH);
\r
122 if (service->env) {
\r
123 if (RegSetValueEx(key, NSSM_REG_ENV, 0, REG_MULTI_SZ, (const unsigned char *) service->env, (unsigned long) service->envlen * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
124 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV, error_string(GetLastError()), 0);
\r
127 else if (editing) RegDeleteValue(key, NSSM_REG_ENV);
\r
128 if (service->env_extra) {
\r
129 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
130 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV_EXTRA, error_string(GetLastError()), 0);
\r
133 else if (editing) RegDeleteValue(key, NSSM_REG_ENV_EXTRA);
\r
135 /* Close registry. */
\r
141 int create_exit_action(TCHAR *service_name, const TCHAR *action_string, bool editing) {
\r
143 TCHAR registry[KEY_LENGTH];
\r
144 if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, NSSM_REGISTRY _T("\\%s"), service_name, NSSM_REG_EXIT) < 0) {
\r
145 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("NSSM_REG_EXIT"), _T("create_exit_action()"), 0);
\r
149 /* Try to open the registry */
\r
151 unsigned long disposition;
\r
152 if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, KEY_WRITE, 0, &key, &disposition) != ERROR_SUCCESS) {
\r
153 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
157 /* Do nothing if the key already existed */
\r
158 if (disposition == REG_OPENED_EXISTING_KEY && ! editing) {
\r
163 /* Create the default value */
\r
164 if (RegSetValueEx(key, 0, 0, REG_SZ, (const unsigned char *) action_string, (unsigned long) (_tcslen(action_string) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
165 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_EXIT, error_string(GetLastError()), 0);
\r
170 /* Close registry */
\r
176 int set_environment(TCHAR *service_name, HKEY key, TCHAR *value, TCHAR **env, unsigned long *envlen) {
\r
177 unsigned long type = REG_MULTI_SZ;
\r
179 /* Dummy test to find buffer size */
\r
180 unsigned long ret = RegQueryValueEx(key, value, 0, &type, NULL, envlen);
\r
181 if (ret != ERROR_SUCCESS) {
\r
184 /* The service probably doesn't have any environment configured */
\r
185 if (ret == ERROR_FILE_NOT_FOUND) return 0;
\r
186 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
190 if (type != REG_MULTI_SZ) {
\r
193 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_INVALID_ENVIRONMENT_STRING_TYPE, value, service_name, 0);
\r
197 /* Probably not possible */
\r
198 if (! *envlen) return 0;
\r
200 /* Previously initialised? */
\r
201 if (*env) HeapFree(GetProcessHeap(), 0, *env);
\r
203 *env = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, *envlen);
\r
206 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, value, _T("set_environment()"), 0);
\r
210 /* Actually get the strings */
\r
211 ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) *env, envlen);
\r
212 if (ret != ERROR_SUCCESS) {
\r
213 HeapFree(GetProcessHeap(), 0, *env);
\r
216 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
223 /* Replace NULL with CRLF. Leave NULL NULL as the end marker. */
\r
224 int format_environment(TCHAR *env, unsigned long envlen, TCHAR **formatted, unsigned long *newlen) {
\r
225 unsigned long i, j;
\r
233 for (i = 0; i < envlen; i++) if (! env[i] && env[i + 1]) ++*newlen;
\r
235 *formatted = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *newlen * sizeof(TCHAR));
\r
236 if (! *formatted) {
\r
241 for (i = 0, j = 0; i < envlen; i++) {
\r
242 (*formatted)[j] = env[i];
\r
245 (*formatted)[j] = _T('\r');
\r
246 (*formatted)[++j] = _T('\n');
\r
255 /* Strip CR and replace LF with NULL. */
\r
256 int unformat_environment(TCHAR *env, unsigned long envlen, TCHAR **unformatted, unsigned long *newlen) {
\r
257 unsigned long i, j;
\r
265 for (i = 0; i < envlen; i++) if (env[i] != _T('\r')) ++*newlen;
\r
266 /* Must end with two NULLs. */
\r
269 *unformatted = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *newlen * sizeof(TCHAR));
\r
270 if (! *unformatted) return 1;
\r
272 for (i = 0, j = 0; i < envlen; i++) {
\r
273 if (env[i] == _T('\r')) continue;
\r
274 if (env[i] == _T('\n')) (*unformatted)[j] = _T('\0');
\r
275 else (*unformatted)[j] = env[i];
\r
282 int expand_parameter(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise, bool must_exist) {
\r
283 TCHAR *buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, datalen);
\r
285 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, value, _T("expand_parameter()"), 0);
\r
289 ZeroMemory(data, datalen);
\r
291 unsigned long type = REG_EXPAND_SZ;
\r
292 unsigned long buflen = datalen;
\r
294 unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);
\r
295 if (ret != ERROR_SUCCESS) {
\r
296 unsigned long error = GetLastError();
\r
297 HeapFree(GetProcessHeap(), 0, buffer);
\r
299 if (ret == ERROR_FILE_NOT_FOUND) {
\r
300 if (! must_exist) return 0;
\r
303 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(error), 0);
\r
307 /* Paths aren't allowed to contain quotes. */
\r
308 if (sanitise) PathUnquoteSpaces(buffer);
\r
310 /* Technically we shouldn't expand environment strings from REG_SZ values */
\r
311 if (type != REG_EXPAND_SZ) {
\r
312 memmove(data, buffer, buflen);
\r
313 HeapFree(GetProcessHeap(), 0, buffer);
\r
317 ret = ExpandEnvironmentStrings((TCHAR *) buffer, data, datalen);
\r
318 if (! ret || ret > datalen) {
\r
319 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_EXPANDENVIRONMENTSTRINGS_FAILED, buffer, error_string(GetLastError()), 0);
\r
320 HeapFree(GetProcessHeap(), 0, buffer);
\r
324 HeapFree(GetProcessHeap(), 0, buffer);
\r
328 int expand_parameter(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise) {
\r
329 return expand_parameter(key, value, data, datalen, sanitise, true);
\r
333 Sets a string in the registry.
\r
334 Returns: 0 if it was set.
\r
337 int set_expand_string(HKEY key, TCHAR *value, TCHAR *string) {
\r
338 if (RegSetValueEx(key, value, 0, REG_EXPAND_SZ, (const unsigned char *) string, (unsigned long) (_tcslen(string) + 1) * sizeof(TCHAR)) == ERROR_SUCCESS) return 0;
\r
339 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
344 Set an unsigned long in the registry.
\r
345 Returns: 0 if it was set.
\r
348 int set_number(HKEY key, TCHAR *value, unsigned long number) {
\r
349 if (RegSetValueEx(key, value, 0, REG_DWORD, (const unsigned char *) &number, sizeof(number)) == ERROR_SUCCESS) return 0;
\r
350 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
355 Query an unsigned long from the registry.
\r
356 Returns: 1 if a number was retrieved.
\r
357 0 if none was found and must_exist is false.
\r
358 -1 if none was found and must_exist is true.
\r
361 int get_number(HKEY key, TCHAR *value, unsigned long *number, bool must_exist) {
\r
362 unsigned long type = REG_DWORD;
\r
363 unsigned long number_len = sizeof(unsigned long);
\r
365 int ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) number, &number_len);
\r
366 if (ret == ERROR_SUCCESS) return 1;
\r
368 if (ret == ERROR_FILE_NOT_FOUND) {
\r
369 if (! must_exist) return 0;
\r
372 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
373 if (ret == ERROR_FILE_NOT_FOUND) return -1;
\r
378 int get_number(HKEY key, TCHAR *value, unsigned long *number) {
\r
379 return get_number(key, value, number, true);
\r
382 void override_milliseconds(TCHAR *service_name, HKEY key, TCHAR *value, unsigned long *buffer, unsigned long default_value, unsigned long event) {
\r
383 unsigned long type = REG_DWORD;
\r
384 unsigned long buflen = sizeof(unsigned long);
\r
386 unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);
\r
387 if (ret != ERROR_SUCCESS) {
\r
388 if (ret != ERROR_FILE_NOT_FOUND) {
\r
389 if (type != REG_DWORD) {
\r
390 TCHAR milliseconds[16];
\r
391 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), default_value);
\r
392 log_event(EVENTLOG_WARNING_TYPE, event, service_name, value, milliseconds, 0);
\r
394 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
399 if (! ok) *buffer = default_value;
\r
402 HKEY open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam) {
\r
404 TCHAR registry[KEY_LENGTH];
\r
408 if (sub) ret = _sntprintf_s(registry, _countof(registry), _TRUNCATE, NSSM_REGISTRY _T("\\%s"), service_name, sub);
\r
409 else ret = _sntprintf_s(registry, _countof(registry), _TRUNCATE, NSSM_REGISTRY, service_name);
\r
411 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("NSSM_REGISTRY"), _T("open_registry()"), 0);
\r
415 if (sam & KEY_WRITE) {
\r
416 if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, sam, 0, &key, 0) != ERROR_SUCCESS) {
\r
417 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
422 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, registry, 0, sam, &key) != ERROR_SUCCESS) {
\r
423 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
431 HKEY open_registry(const TCHAR *service_name, REGSAM sam) {
\r
432 return open_registry(service_name, 0, sam);
\r
435 int get_parameters(nssm_service_t *service, STARTUPINFO *si) {
\r
438 /* Try to open the registry */
\r
439 HKEY key = open_registry(service->name, KEY_READ);
\r
440 if (! key) return 1;
\r
442 /* Try to get executable file - MUST succeed */
\r
443 if (expand_parameter(key, NSSM_REG_EXE, service->exe, sizeof(service->exe), false)) {
\r
448 /* Try to get flags - may fail and we don't care */
\r
449 if (expand_parameter(key, NSSM_REG_FLAGS, service->flags, sizeof(service->flags), false)) {
\r
450 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_FLAGS, NSSM_REG_FLAGS, service->name, service->exe, 0);
\r
451 ZeroMemory(service->flags, sizeof(service->flags));
\r
454 /* Try to get startup directory - may fail and we fall back to a default */
\r
455 if (expand_parameter(key, NSSM_REG_DIR, service->dir, sizeof(service->dir), true) || ! service->dir[0]) {
\r
456 _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);
\r
457 strip_basename(service->dir);
\r
458 if (service->dir[0] == _T('\0')) {
\r
460 ret = GetWindowsDirectory(service->dir, sizeof(service->dir));
\r
461 if (! ret || ret > sizeof(service->dir)) {
\r
462 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_NO_DIR_AND_NO_FALLBACK, NSSM_REG_DIR, service->name, 0);
\r
467 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_DIR, NSSM_REG_DIR, service->name, service->dir, 0);
\r
470 /* Try to get processor affinity - may fail. */
\r
472 if (expand_parameter(key, NSSM_REG_AFFINITY, buffer, sizeof(buffer), false, false) || ! buffer[0]) service->affinity = 0LL;
\r
473 else if (affinity_string_to_mask(buffer, &service->affinity)) {
\r
474 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_AFFINITY_MASK, service->name, buffer);
\r
475 service->affinity = 0LL;
\r
478 DWORD_PTR affinity, system_affinity;
\r
480 if (GetProcessAffinityMask(GetCurrentProcess(), &affinity, &system_affinity)) {
\r
481 _int64 effective_affinity = service->affinity & system_affinity;
\r
482 if (effective_affinity != service->affinity) {
\r
484 if (! affinity_mask_to_string(system_affinity, &system)) {
\r
485 TCHAR *effective = 0;
\r
486 if (! affinity_mask_to_string(effective_affinity, &effective)) {
\r
487 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_EFFECTIVE_AFFINITY_MASK, service->name, buffer, system, effective, 0);
\r
489 HeapFree(GetProcessHeap(), 0, effective);
\r
491 HeapFree(GetProcessHeap(), 0, system);
\r
496 /* Try to get environment variables - may fail */
\r
497 set_environment(service->name, key, NSSM_REG_ENV, &service->env, &service->envlen);
\r
498 /* Environment variables to add to existing rather than replace - may fail. */
\r
499 set_environment(service->name, key, NSSM_REG_ENV_EXTRA, &service->env_extra, &service->env_extralen);
\r
502 if (service->env_extra) {
\r
503 /* Append these to any other environment variables set. */
\r
504 if (service->env) {
\r
505 /* Append extra variables to configured variables. */
\r
506 unsigned long envlen = service->envlen + service->env_extralen - 1;
\r
507 TCHAR *env = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, envlen);
\r
509 memmove(env, service->env, service->envlen - sizeof(TCHAR));
\r
510 /* envlen is in bytes. */
\r
511 memmove(env + (service->envlen / sizeof(TCHAR)) - 1, service->env_extra, service->env_extralen);
\r
513 HeapFree(GetProcessHeap(), 0, service->env);
\r
514 service->env = env;
\r
515 service->envlen = envlen;
\r
517 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("environment"), _T("get_parameters()"), 0);
\r
520 /* Append extra variables to our environment. */
\r
522 size_t envlen, len;
\r
524 env = service->env_extra;
\r
527 envlen = _tcslen(env) + 1;
\r
528 for (s = env; *s && *s != _T('='); s++);
\r
529 if (*s == _T('=')) *s++ = _T('\0');
\r
530 if (! SetEnvironmentVariable(env, s)) log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_SETENVIRONMENTVARIABLE_FAILED, env, s, error_string(GetLastError()), 0);
\r
537 /* Try to get priority - may fail. */
\r
538 unsigned long priority;
\r
539 if (get_number(key, NSSM_REG_PRIORITY, &priority, false) == 1) {
\r
540 if (priority == (priority & priority_mask())) service->priority = priority;
\r
541 else log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_PRIORITY, service->name, NSSM_REG_PRIORITY, 0);
\r
544 /* Try to get file rotation settings - may fail. */
\r
545 unsigned long rotate_files;
\r
546 if (get_number(key, NSSM_REG_ROTATE, &rotate_files, false) == 1) {
\r
547 if (rotate_files) service->rotate_files = true;
\r
548 else service->rotate_files = false;
\r
550 else service->rotate_files = false;
\r
551 if (get_number(key, NSSM_REG_ROTATE_SECONDS, &service->rotate_seconds, false) != 1) service->rotate_seconds = 0;
\r
552 if (get_number(key, NSSM_REG_ROTATE_BYTES_LOW, &service->rotate_bytes_low, false) != 1) service->rotate_bytes_low = 0;
\r
553 if (get_number(key, NSSM_REG_ROTATE_BYTES_HIGH, &service->rotate_bytes_high, false) != 1) service->rotate_bytes_high = 0;
\r
555 /* Change to startup directory in case stdout/stderr are relative paths. */
\r
556 TCHAR cwd[MAX_PATH];
\r
557 GetCurrentDirectory(_countof(cwd), cwd);
\r
558 SetCurrentDirectory(service->dir);
\r
560 /* Try to get stdout and stderr */
\r
561 if (get_output_handles(service, key, si)) {
\r
562 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);
\r
564 SetCurrentDirectory(cwd);
\r
568 /* Change back in case the startup directory needs to be deleted. */
\r
569 SetCurrentDirectory(cwd);
\r
571 /* Try to get mandatory restart delay */
\r
572 override_milliseconds(service->name, key, NSSM_REG_RESTART_DELAY, &service->restart_delay, 0, NSSM_EVENT_BOGUS_RESTART_DELAY);
\r
574 /* Try to get throttle restart delay */
\r
575 override_milliseconds(service->name, key, NSSM_REG_THROTTLE, &service->throttle_delay, NSSM_RESET_THROTTLE_RESTART, NSSM_EVENT_BOGUS_THROTTLE);
\r
577 /* Try to get service stop flags. */
\r
578 unsigned long type = REG_DWORD;
\r
579 unsigned long stop_method_skip;
\r
580 unsigned long buflen = sizeof(stop_method_skip);
\r
581 bool stop_ok = false;
\r
582 ret = RegQueryValueEx(key, NSSM_REG_STOP_METHOD_SKIP, 0, &type, (unsigned char *) &stop_method_skip, &buflen);
\r
583 if (ret != ERROR_SUCCESS) {
\r
584 if (ret != ERROR_FILE_NOT_FOUND) {
\r
585 if (type != REG_DWORD) {
\r
586 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_STOP_METHOD_SKIP, service->name, NSSM_REG_STOP_METHOD_SKIP, NSSM, 0);
\r
588 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, NSSM_REG_STOP_METHOD_SKIP, error_string(GetLastError()), 0);
\r
591 else stop_ok = true;
\r
593 /* Try all methods except those requested to be skipped. */
\r
594 service->stop_method = ~0;
\r
595 if (stop_ok) service->stop_method &= ~stop_method_skip;
\r
597 /* Try to get kill delays - may fail. */
\r
598 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
599 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
600 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
602 /* Try to get default exit action. */
\r
603 bool default_action;
\r
604 service->default_exit_action = NSSM_EXIT_RESTART;
\r
605 TCHAR action_string[ACTION_LEN];
\r
606 if (! get_exit_action(service->name, 0, action_string, &default_action)) {
\r
607 for (int i = 0; exit_action_strings[i]; i++) {
\r
608 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
609 service->default_exit_action = i;
\r
615 /* Close registry */
\r
622 Sets the string for the exit action corresponding to the exit code.
\r
624 ret is a pointer to an unsigned long containing the exit code.
\r
625 If ret is NULL, we retrieve the default exit action unconditionally.
\r
627 action is a buffer which receives the string.
\r
629 default_action is a pointer to a bool which is set to false if there
\r
630 was an explicit string for the given exit code, or true if we are
\r
631 returning the default action.
\r
633 Returns: 0 on success.
\r
636 int get_exit_action(const TCHAR *service_name, unsigned long *ret, TCHAR *action, bool *default_action) {
\r
637 /* Are we returning the default action or a status-specific one? */
\r
638 *default_action = ! ret;
\r
640 /* Try to open the registry */
\r
641 HKEY key = open_registry(service_name, NSSM_REG_EXIT, KEY_READ);
\r
642 if (! key) return 1;
\r
644 unsigned long type = REG_SZ;
\r
645 unsigned long action_len = ACTION_LEN;
\r
648 if (! ret) code[0] = _T('\0');
\r
649 else if (_sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), *ret) < 0) {
\r
651 return get_exit_action(service_name, 0, action, default_action);
\r
653 if (RegQueryValueEx(key, code, 0, &type, (unsigned char *) action, &action_len) != ERROR_SUCCESS) {
\r
655 /* Try again with * as the key if an exit code was defined */
\r
656 if (ret) return get_exit_action(service_name, 0, action, default_action);
\r
660 /* Close registry */
\r