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
504 unsigned long envlen;
\r
506 /* Copy our environment for the application. */
\r
507 if (! service->env) {
\r
508 TCHAR *rawenv = GetEnvironmentStrings();
\r
512 The environment block starts with variables of the form
\r
513 =C:=C:\Windows\System32 which we ignore.
\r
515 while (*env == _T('=')) {
\r
516 for ( ; *env; env++);
\r
522 for ( ; env[envlen]; envlen++);
\r
523 if (! env[++envlen]) break;
\r
527 service->envlen = envlen * sizeof(TCHAR);
\r
528 service->env = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, service->envlen);
\r
529 memmove(service->env, env, service->envlen);
\r
530 FreeEnvironmentStrings(rawenv);
\r
535 /* Append extra variables to configured variables. */
\r
536 if (service->env) {
\r
537 envlen = service->envlen + service->env_extralen - sizeof(TCHAR)/*?*/;
\r
538 env = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, envlen);
\r
540 memmove(env, service->env, service->envlen - sizeof(TCHAR));
\r
541 /* envlen is in bytes but env[i] is in characters. */
\r
542 memmove(env + (service->envlen / sizeof(TCHAR)) - 1, service->env_extra, service->env_extralen);
\r
544 HeapFree(GetProcessHeap(), 0, service->env);
\r
545 HeapFree(GetProcessHeap(), 0, service->env_extra);
\r
546 service->env = env;
\r
547 service->envlen = envlen;
\r
549 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("environment"), _T("get_parameters()"), 0);
\r
552 /* Huh? No environment at all? */
\r
553 service->env = service->env_extra;
\r
554 service->envlen = service->env_extralen;
\r
558 service->env_extra = 0;
\r
559 service->env_extralen = 0;
\r
562 /* Try to get priority - may fail. */
\r
563 unsigned long priority;
\r
564 if (get_number(key, NSSM_REG_PRIORITY, &priority, false) == 1) {
\r
565 if (priority == (priority & priority_mask())) service->priority = priority;
\r
566 else log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_PRIORITY, service->name, NSSM_REG_PRIORITY, 0);
\r
569 /* Try to get file rotation settings - may fail. */
\r
570 unsigned long rotate_files;
\r
571 if (get_number(key, NSSM_REG_ROTATE, &rotate_files, false) == 1) {
\r
572 if (rotate_files) service->rotate_files = true;
\r
573 else service->rotate_files = false;
\r
575 else service->rotate_files = false;
\r
576 if (get_number(key, NSSM_REG_ROTATE_SECONDS, &service->rotate_seconds, false) != 1) service->rotate_seconds = 0;
\r
577 if (get_number(key, NSSM_REG_ROTATE_BYTES_LOW, &service->rotate_bytes_low, false) != 1) service->rotate_bytes_low = 0;
\r
578 if (get_number(key, NSSM_REG_ROTATE_BYTES_HIGH, &service->rotate_bytes_high, false) != 1) service->rotate_bytes_high = 0;
\r
580 /* Change to startup directory in case stdout/stderr are relative paths. */
\r
581 TCHAR cwd[MAX_PATH];
\r
582 GetCurrentDirectory(_countof(cwd), cwd);
\r
583 SetCurrentDirectory(service->dir);
\r
585 /* Try to get stdout and stderr */
\r
586 if (get_output_handles(service, key, si)) {
\r
587 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);
\r
589 SetCurrentDirectory(cwd);
\r
593 /* Change back in case the startup directory needs to be deleted. */
\r
594 SetCurrentDirectory(cwd);
\r
596 /* Try to get mandatory restart delay */
\r
597 override_milliseconds(service->name, key, NSSM_REG_RESTART_DELAY, &service->restart_delay, 0, NSSM_EVENT_BOGUS_RESTART_DELAY);
\r
599 /* Try to get throttle restart delay */
\r
600 override_milliseconds(service->name, key, NSSM_REG_THROTTLE, &service->throttle_delay, NSSM_RESET_THROTTLE_RESTART, NSSM_EVENT_BOGUS_THROTTLE);
\r
602 /* Try to get service stop flags. */
\r
603 unsigned long type = REG_DWORD;
\r
604 unsigned long stop_method_skip;
\r
605 unsigned long buflen = sizeof(stop_method_skip);
\r
606 bool stop_ok = false;
\r
607 ret = RegQueryValueEx(key, NSSM_REG_STOP_METHOD_SKIP, 0, &type, (unsigned char *) &stop_method_skip, &buflen);
\r
608 if (ret != ERROR_SUCCESS) {
\r
609 if (ret != ERROR_FILE_NOT_FOUND) {
\r
610 if (type != REG_DWORD) {
\r
611 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_STOP_METHOD_SKIP, service->name, NSSM_REG_STOP_METHOD_SKIP, NSSM, 0);
\r
613 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, NSSM_REG_STOP_METHOD_SKIP, error_string(GetLastError()), 0);
\r
616 else stop_ok = true;
\r
618 /* Try all methods except those requested to be skipped. */
\r
619 service->stop_method = ~0;
\r
620 if (stop_ok) service->stop_method &= ~stop_method_skip;
\r
622 /* Try to get kill delays - may fail. */
\r
623 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
624 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
625 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
627 /* Try to get default exit action. */
\r
628 bool default_action;
\r
629 service->default_exit_action = NSSM_EXIT_RESTART;
\r
630 TCHAR action_string[ACTION_LEN];
\r
631 if (! get_exit_action(service->name, 0, action_string, &default_action)) {
\r
632 for (int i = 0; exit_action_strings[i]; i++) {
\r
633 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
634 service->default_exit_action = i;
\r
640 /* Close registry */
\r
647 Sets the string for the exit action corresponding to the exit code.
\r
649 ret is a pointer to an unsigned long containing the exit code.
\r
650 If ret is NULL, we retrieve the default exit action unconditionally.
\r
652 action is a buffer which receives the string.
\r
654 default_action is a pointer to a bool which is set to false if there
\r
655 was an explicit string for the given exit code, or true if we are
\r
656 returning the default action.
\r
658 Returns: 0 on success.
\r
661 int get_exit_action(const TCHAR *service_name, unsigned long *ret, TCHAR *action, bool *default_action) {
\r
662 /* Are we returning the default action or a status-specific one? */
\r
663 *default_action = ! ret;
\r
665 /* Try to open the registry */
\r
666 HKEY key = open_registry(service_name, NSSM_REG_EXIT, KEY_READ);
\r
667 if (! key) return 1;
\r
669 unsigned long type = REG_SZ;
\r
670 unsigned long action_len = ACTION_LEN;
\r
673 if (! ret) code[0] = _T('\0');
\r
674 else if (_sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), *ret) < 0) {
\r
676 return get_exit_action(service_name, 0, action, default_action);
\r
678 if (RegQueryValueEx(key, code, 0, &type, (unsigned char *) action, &action_len) != ERROR_SUCCESS) {
\r
680 /* Try again with * as the key if an exit code was defined */
\r
681 if (ret) return get_exit_action(service_name, 0, action, default_action);
\r
685 /* Close registry */
\r