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->throttle_delay != NSSM_RESET_THROTTLE_RESTART) set_number(key, NSSM_REG_THROTTLE, service->throttle_delay);
\r
73 else if (editing) RegDeleteValue(key, NSSM_REG_THROTTLE);
\r
74 if (service->kill_console_delay != NSSM_KILL_CONSOLE_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, service->kill_console_delay);
\r
75 else if (editing) RegDeleteValue(key, NSSM_REG_KILL_CONSOLE_GRACE_PERIOD);
\r
76 if (service->kill_window_delay != NSSM_KILL_WINDOW_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_WINDOW_GRACE_PERIOD, service->kill_window_delay);
\r
77 else if (editing) RegDeleteValue(key, NSSM_REG_KILL_WINDOW_GRACE_PERIOD);
\r
78 if (service->kill_threads_delay != NSSM_KILL_THREADS_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_THREADS_GRACE_PERIOD, service->kill_threads_delay);
\r
79 else if (editing) RegDeleteValue(key, NSSM_REG_KILL_THREADS_GRACE_PERIOD);
\r
80 if (service->stdin_path[0] || editing) {
\r
81 if (service->stdin_path[0]) set_expand_string(key, NSSM_REG_STDIN, service->stdin_path);
\r
82 else if (editing) RegDeleteValue(key, NSSM_REG_STDIN);
\r
83 if (service->stdin_sharing != NSSM_STDIN_SHARING) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_SHARING, service->stdin_sharing);
\r
84 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_SHARING);
\r
85 if (service->stdin_disposition != NSSM_STDIN_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_DISPOSITION, service->stdin_disposition);
\r
86 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_DISPOSITION);
\r
87 if (service->stdin_flags != NSSM_STDIN_FLAGS) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_FLAGS, service->stdin_flags);
\r
88 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_FLAGS);
\r
90 if (service->stdout_path[0] || editing) {
\r
91 if (service->stdout_path[0]) set_expand_string(key, NSSM_REG_STDOUT, service->stdout_path);
\r
92 else if (editing) RegDeleteValue(key, NSSM_REG_STDOUT);
\r
93 if (service->stdout_sharing != NSSM_STDOUT_SHARING) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_SHARING, service->stdout_sharing);
\r
94 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_SHARING);
\r
95 if (service->stdout_disposition != NSSM_STDOUT_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_DISPOSITION, service->stdout_disposition);
\r
96 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_DISPOSITION);
\r
97 if (service->stdout_flags != NSSM_STDOUT_FLAGS) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_FLAGS, service->stdout_flags);
\r
98 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_FLAGS);
\r
100 if (service->stderr_path[0] || editing) {
\r
101 if (service->stderr_path[0]) set_expand_string(key, NSSM_REG_STDERR, service->stderr_path);
\r
102 else if (editing) RegDeleteValue(key, NSSM_REG_STDERR);
\r
103 if (service->stderr_sharing != NSSM_STDERR_SHARING) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_SHARING, service->stderr_sharing);
\r
104 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_SHARING);
\r
105 if (service->stderr_disposition != NSSM_STDERR_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_DISPOSITION, service->stderr_disposition);
\r
106 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_DISPOSITION);
\r
107 if (service->stderr_flags != NSSM_STDERR_FLAGS) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_FLAGS, service->stderr_flags);
\r
108 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_FLAGS);
\r
110 if (service->rotate_files) set_number(key, NSSM_REG_ROTATE, 1);
\r
111 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE);
\r
112 if (service->rotate_seconds) set_number(key, NSSM_REG_ROTATE_SECONDS, service->rotate_seconds);
\r
113 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_SECONDS);
\r
114 if (service->rotate_bytes_low) set_number(key, NSSM_REG_ROTATE_BYTES_LOW, service->rotate_bytes_low);
\r
115 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_BYTES_LOW);
\r
116 if (service->rotate_bytes_high) set_number(key, NSSM_REG_ROTATE_BYTES_HIGH, service->rotate_bytes_high);
\r
117 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_BYTES_HIGH);
\r
120 if (service->env) {
\r
121 if (RegSetValueEx(key, NSSM_REG_ENV, 0, REG_MULTI_SZ, (const unsigned char *) service->env, (unsigned long) service->envlen * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
122 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV, error_string(GetLastError()), 0);
\r
125 else if (editing) RegDeleteValue(key, NSSM_REG_ENV);
\r
126 if (service->env_extra) {
\r
127 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
128 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV_EXTRA, error_string(GetLastError()), 0);
\r
131 else if (editing) RegDeleteValue(key, NSSM_REG_ENV_EXTRA);
\r
133 /* Close registry. */
\r
139 int create_exit_action(TCHAR *service_name, const TCHAR *action_string, bool editing) {
\r
141 TCHAR registry[KEY_LENGTH];
\r
142 if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, NSSM_REGISTRY _T("\\%s"), service_name, NSSM_REG_EXIT) < 0) {
\r
143 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("NSSM_REG_EXIT"), _T("create_exit_action()"), 0);
\r
147 /* Try to open the registry */
\r
149 unsigned long disposition;
\r
150 if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, KEY_WRITE, 0, &key, &disposition) != ERROR_SUCCESS) {
\r
151 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
155 /* Do nothing if the key already existed */
\r
156 if (disposition == REG_OPENED_EXISTING_KEY && ! editing) {
\r
161 /* Create the default value */
\r
162 if (RegSetValueEx(key, 0, 0, REG_SZ, (const unsigned char *) action_string, (unsigned long) (_tcslen(action_string) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
163 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_EXIT, error_string(GetLastError()), 0);
\r
168 /* Close registry */
\r
174 int set_environment(TCHAR *service_name, HKEY key, TCHAR *value, TCHAR **env, unsigned long *envlen) {
\r
175 unsigned long type = REG_MULTI_SZ;
\r
177 /* Dummy test to find buffer size */
\r
178 unsigned long ret = RegQueryValueEx(key, value, 0, &type, NULL, envlen);
\r
179 if (ret != ERROR_SUCCESS) {
\r
182 /* The service probably doesn't have any environment configured */
\r
183 if (ret == ERROR_FILE_NOT_FOUND) return 0;
\r
184 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
188 if (type != REG_MULTI_SZ) {
\r
191 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_INVALID_ENVIRONMENT_STRING_TYPE, value, service_name, 0);
\r
195 /* Probably not possible */
\r
196 if (! *envlen) return 0;
\r
198 /* Previously initialised? */
\r
199 if (*env) HeapFree(GetProcessHeap(), 0, *env);
\r
201 *env = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, *envlen);
\r
204 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, value, _T("set_environment()"), 0);
\r
208 /* Actually get the strings */
\r
209 ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) *env, envlen);
\r
210 if (ret != ERROR_SUCCESS) {
\r
211 HeapFree(GetProcessHeap(), 0, *env);
\r
214 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
221 /* Replace NULL with CRLF. Leave NULL NULL as the end marker. */
\r
222 int format_environment(TCHAR *env, unsigned long envlen, TCHAR **formatted, unsigned long *newlen) {
\r
223 unsigned long i, j;
\r
231 for (i = 0; i < envlen; i++) if (! env[i] && env[i + 1]) ++*newlen;
\r
233 *formatted = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *newlen * sizeof(TCHAR));
\r
234 if (! *formatted) {
\r
239 for (i = 0, j = 0; i < envlen; i++) {
\r
240 (*formatted)[j] = env[i];
\r
243 (*formatted)[j] = _T('\r');
\r
244 (*formatted)[++j] = _T('\n');
\r
253 /* Strip CR and replace LF with NULL. */
\r
254 int unformat_environment(TCHAR *env, unsigned long envlen, TCHAR **unformatted, unsigned long *newlen) {
\r
255 unsigned long i, j;
\r
263 for (i = 0; i < envlen; i++) if (env[i] != _T('\r')) ++*newlen;
\r
264 /* Must end with two NULLs. */
\r
267 *unformatted = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *newlen * sizeof(TCHAR));
\r
268 if (! *unformatted) return 1;
\r
270 for (i = 0, j = 0; i < envlen; i++) {
\r
271 if (env[i] == _T('\r')) continue;
\r
272 if (env[i] == _T('\n')) (*unformatted)[j] = _T('\0');
\r
273 else (*unformatted)[j] = env[i];
\r
280 int expand_parameter(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise, bool must_exist) {
\r
281 TCHAR *buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, datalen);
\r
283 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, value, _T("expand_parameter()"), 0);
\r
287 ZeroMemory(data, datalen);
\r
289 unsigned long type = REG_EXPAND_SZ;
\r
290 unsigned long buflen = datalen;
\r
292 unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);
\r
293 if (ret != ERROR_SUCCESS) {
\r
294 unsigned long error = GetLastError();
\r
295 HeapFree(GetProcessHeap(), 0, buffer);
\r
297 if (ret == ERROR_FILE_NOT_FOUND) {
\r
298 if (! must_exist) return 0;
\r
301 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(error), 0);
\r
305 /* Paths aren't allowed to contain quotes. */
\r
306 if (sanitise) PathUnquoteSpaces(buffer);
\r
308 /* Technically we shouldn't expand environment strings from REG_SZ values */
\r
309 if (type != REG_EXPAND_SZ) {
\r
310 memmove(data, buffer, buflen);
\r
311 HeapFree(GetProcessHeap(), 0, buffer);
\r
315 ret = ExpandEnvironmentStrings((TCHAR *) buffer, data, datalen);
\r
316 if (! ret || ret > datalen) {
\r
317 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_EXPANDENVIRONMENTSTRINGS_FAILED, buffer, error_string(GetLastError()), 0);
\r
318 HeapFree(GetProcessHeap(), 0, buffer);
\r
322 HeapFree(GetProcessHeap(), 0, buffer);
\r
326 int expand_parameter(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise) {
\r
327 return expand_parameter(key, value, data, datalen, sanitise, true);
\r
331 Sets a string in the registry.
\r
332 Returns: 0 if it was set.
\r
335 int set_expand_string(HKEY key, TCHAR *value, TCHAR *string) {
\r
336 if (RegSetValueEx(key, value, 0, REG_EXPAND_SZ, (const unsigned char *) string, (unsigned long) (_tcslen(string) + 1) * sizeof(TCHAR)) == ERROR_SUCCESS) return 0;
\r
337 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
342 Set an unsigned long in the registry.
\r
343 Returns: 0 if it was set.
\r
346 int set_number(HKEY key, TCHAR *value, unsigned long number) {
\r
347 if (RegSetValueEx(key, value, 0, REG_DWORD, (const unsigned char *) &number, sizeof(number)) == ERROR_SUCCESS) return 0;
\r
348 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
353 Query an unsigned long from the registry.
\r
354 Returns: 1 if a number was retrieved.
\r
355 0 if none was found and must_exist is false.
\r
356 -1 if none was found and must_exist is true.
\r
359 int get_number(HKEY key, TCHAR *value, unsigned long *number, bool must_exist) {
\r
360 unsigned long type = REG_DWORD;
\r
361 unsigned long number_len = sizeof(unsigned long);
\r
363 int ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) number, &number_len);
\r
364 if (ret == ERROR_SUCCESS) return 1;
\r
366 if (ret == ERROR_FILE_NOT_FOUND) {
\r
367 if (! must_exist) return 0;
\r
370 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
371 if (ret == ERROR_FILE_NOT_FOUND) return -1;
\r
376 int get_number(HKEY key, TCHAR *value, unsigned long *number) {
\r
377 return get_number(key, value, number, true);
\r
380 void override_milliseconds(TCHAR *service_name, HKEY key, TCHAR *value, unsigned long *buffer, unsigned long default_value, unsigned long event) {
\r
381 unsigned long type = REG_DWORD;
\r
382 unsigned long buflen = sizeof(unsigned long);
\r
384 unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);
\r
385 if (ret != ERROR_SUCCESS) {
\r
386 if (ret != ERROR_FILE_NOT_FOUND) {
\r
387 if (type != REG_DWORD) {
\r
388 TCHAR milliseconds[16];
\r
389 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), default_value);
\r
390 log_event(EVENTLOG_WARNING_TYPE, event, service_name, value, milliseconds, 0);
\r
392 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
397 if (! ok) *buffer = default_value;
\r
400 HKEY open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam) {
\r
402 TCHAR registry[KEY_LENGTH];
\r
406 if (sub) ret = _sntprintf_s(registry, _countof(registry), _TRUNCATE, NSSM_REGISTRY _T("\\%s"), service_name, sub);
\r
407 else ret = _sntprintf_s(registry, _countof(registry), _TRUNCATE, NSSM_REGISTRY, service_name);
\r
409 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("NSSM_REGISTRY"), _T("open_registry()"), 0);
\r
413 if (sam & KEY_WRITE) {
\r
414 if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, sam, 0, &key, 0) != ERROR_SUCCESS) {
\r
415 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
420 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, registry, 0, sam, &key) != ERROR_SUCCESS) {
\r
421 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
429 HKEY open_registry(const TCHAR *service_name, REGSAM sam) {
\r
430 return open_registry(service_name, 0, sam);
\r
433 int get_parameters(nssm_service_t *service, STARTUPINFO *si) {
\r
436 /* Try to open the registry */
\r
437 HKEY key = open_registry(service->name, KEY_READ);
\r
438 if (! key) return 1;
\r
440 /* Try to get executable file - MUST succeed */
\r
441 if (expand_parameter(key, NSSM_REG_EXE, service->exe, sizeof(service->exe), false)) {
\r
446 /* Try to get flags - may fail and we don't care */
\r
447 if (expand_parameter(key, NSSM_REG_FLAGS, service->flags, sizeof(service->flags), false)) {
\r
448 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_FLAGS, NSSM_REG_FLAGS, service->name, service->exe, 0);
\r
449 ZeroMemory(service->flags, sizeof(service->flags));
\r
452 /* Try to get startup directory - may fail and we fall back to a default */
\r
453 if (expand_parameter(key, NSSM_REG_DIR, service->dir, sizeof(service->dir), true) || ! service->dir[0]) {
\r
454 _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);
\r
455 strip_basename(service->dir);
\r
456 if (service->dir[0] == _T('\0')) {
\r
458 ret = GetWindowsDirectory(service->dir, sizeof(service->dir));
\r
459 if (! ret || ret > sizeof(service->dir)) {
\r
460 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_NO_DIR_AND_NO_FALLBACK, NSSM_REG_DIR, service->name, 0);
\r
465 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_DIR, NSSM_REG_DIR, service->name, service->dir, 0);
\r
468 /* Try to get processor affinity - may fail. */
\r
470 if (expand_parameter(key, NSSM_REG_AFFINITY, buffer, sizeof(buffer), false, false) || ! buffer[0]) service->affinity = 0LL;
\r
471 else if (affinity_string_to_mask(buffer, &service->affinity)) {
\r
472 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_AFFINITY_MASK, service->name, buffer);
\r
473 service->affinity = 0LL;
\r
476 DWORD_PTR affinity, system_affinity;
\r
478 if (GetProcessAffinityMask(GetCurrentProcess(), &affinity, &system_affinity)) {
\r
479 _int64 effective_affinity = service->affinity & system_affinity;
\r
480 if (effective_affinity != service->affinity) {
\r
482 if (! affinity_mask_to_string(system_affinity, &system)) {
\r
483 TCHAR *effective = 0;
\r
484 if (! affinity_mask_to_string(effective_affinity, &effective)) {
\r
485 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_EFFECTIVE_AFFINITY_MASK, service->name, buffer, system, effective, 0);
\r
487 HeapFree(GetProcessHeap(), 0, effective);
\r
489 HeapFree(GetProcessHeap(), 0, system);
\r
494 /* Try to get environment variables - may fail */
\r
495 set_environment(service->name, key, NSSM_REG_ENV, &service->env, &service->envlen);
\r
496 /* Environment variables to add to existing rather than replace - may fail. */
\r
497 set_environment(service->name, key, NSSM_REG_ENV_EXTRA, &service->env_extra, &service->env_extralen);
\r
500 if (service->env_extra) {
\r
501 /* Append these to any other environment variables set. */
\r
502 if (service->env) {
\r
503 /* Append extra variables to configured variables. */
\r
504 unsigned long envlen = service->envlen + service->env_extralen - 1;
\r
505 TCHAR *env = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, envlen);
\r
507 memmove(env, service->env, service->envlen - sizeof(TCHAR));
\r
508 /* envlen is in bytes. */
\r
509 memmove(env + (service->envlen / sizeof(TCHAR)) - 1, service->env_extra, service->env_extralen);
\r
511 HeapFree(GetProcessHeap(), 0, service->env);
\r
512 service->env = env;
\r
513 service->envlen = envlen;
\r
515 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("environment"), _T("get_parameters()"), 0);
\r
518 /* Append extra variables to our environment. */
\r
520 size_t envlen, len;
\r
522 env = service->env_extra;
\r
525 envlen = _tcslen(env) + 1;
\r
526 for (s = env; *s && *s != _T('='); s++);
\r
527 if (*s == _T('=')) *s++ = _T('\0');
\r
528 if (! SetEnvironmentVariable(env, s)) log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_SETENVIRONMENTVARIABLE_FAILED, env, s, error_string(GetLastError()), 0);
\r
535 /* Try to get priority - may fail. */
\r
536 unsigned long priority;
\r
537 if (get_number(key, NSSM_REG_PRIORITY, &priority, false) == 1) {
\r
538 if (priority == (priority & priority_mask())) service->priority = priority;
\r
539 else log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_PRIORITY, service->name, NSSM_REG_PRIORITY, 0);
\r
542 /* Try to get file rotation settings - may fail. */
\r
543 unsigned long rotate_files;
\r
544 if (get_number(key, NSSM_REG_ROTATE, &rotate_files, false) == 1) {
\r
545 if (rotate_files) service->rotate_files = true;
\r
546 else service->rotate_files = false;
\r
548 else service->rotate_files = false;
\r
549 if (get_number(key, NSSM_REG_ROTATE_SECONDS, &service->rotate_seconds, false) != 1) service->rotate_seconds = 0;
\r
550 if (get_number(key, NSSM_REG_ROTATE_BYTES_LOW, &service->rotate_bytes_low, false) != 1) service->rotate_bytes_low = 0;
\r
551 if (get_number(key, NSSM_REG_ROTATE_BYTES_HIGH, &service->rotate_bytes_high, false) != 1) service->rotate_bytes_high = 0;
\r
553 /* Change to startup directory in case stdout/stderr are relative paths. */
\r
554 TCHAR cwd[MAX_PATH];
\r
555 GetCurrentDirectory(_countof(cwd), cwd);
\r
556 SetCurrentDirectory(service->dir);
\r
558 /* Try to get stdout and stderr */
\r
559 if (get_output_handles(service, key, si)) {
\r
560 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);
\r
562 SetCurrentDirectory(cwd);
\r
566 /* Change back in case the startup directory needs to be deleted. */
\r
567 SetCurrentDirectory(cwd);
\r
569 /* Try to get throttle restart delay */
\r
570 override_milliseconds(service->name, key, NSSM_REG_THROTTLE, &service->throttle_delay, NSSM_RESET_THROTTLE_RESTART, NSSM_EVENT_BOGUS_THROTTLE);
\r
572 /* Try to get service stop flags. */
\r
573 unsigned long type = REG_DWORD;
\r
574 unsigned long stop_method_skip;
\r
575 unsigned long buflen = sizeof(stop_method_skip);
\r
576 bool stop_ok = false;
\r
577 ret = RegQueryValueEx(key, NSSM_REG_STOP_METHOD_SKIP, 0, &type, (unsigned char *) &stop_method_skip, &buflen);
\r
578 if (ret != ERROR_SUCCESS) {
\r
579 if (ret != ERROR_FILE_NOT_FOUND) {
\r
580 if (type != REG_DWORD) {
\r
581 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_STOP_METHOD_SKIP, service->name, NSSM_REG_STOP_METHOD_SKIP, NSSM, 0);
\r
583 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, NSSM_REG_STOP_METHOD_SKIP, error_string(GetLastError()), 0);
\r
586 else stop_ok = true;
\r
588 /* Try all methods except those requested to be skipped. */
\r
589 service->stop_method = ~0;
\r
590 if (stop_ok) service->stop_method &= ~stop_method_skip;
\r
592 /* Try to get kill delays - may fail. */
\r
593 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
594 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
595 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
597 /* Try to get default exit action. */
\r
598 bool default_action;
\r
599 service->default_exit_action = NSSM_EXIT_RESTART;
\r
600 TCHAR action_string[ACTION_LEN];
\r
601 if (! get_exit_action(service->name, 0, action_string, &default_action)) {
\r
602 for (int i = 0; exit_action_strings[i]; i++) {
\r
603 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
604 service->default_exit_action = i;
\r
610 /* Close registry */
\r
617 Sets the string for the exit action corresponding to the exit code.
\r
619 ret is a pointer to an unsigned long containing the exit code.
\r
620 If ret is NULL, we retrieve the default exit action unconditionally.
\r
622 action is a buffer which receives the string.
\r
624 default_action is a pointer to a bool which is set to false if there
\r
625 was an explicit string for the given exit code, or true if we are
\r
626 returning the default action.
\r
628 Returns: 0 on success.
\r
631 int get_exit_action(const TCHAR *service_name, unsigned long *ret, TCHAR *action, bool *default_action) {
\r
632 /* Are we returning the default action or a status-specific one? */
\r
633 *default_action = ! ret;
\r
635 /* Try to open the registry */
\r
636 HKEY key = open_registry(service_name, NSSM_REG_EXIT, KEY_READ);
\r
637 if (! key) return 1;
\r
639 unsigned long type = REG_SZ;
\r
640 unsigned long action_len = ACTION_LEN;
\r
643 if (! ret) code[0] = _T('\0');
\r
644 else if (_sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), *ret) < 0) {
\r
646 return get_exit_action(service_name, 0, action, default_action);
\r
648 if (RegQueryValueEx(key, code, 0, &type, (unsigned char *) action, &action_len) != ERROR_SUCCESS) {
\r
650 /* Try again with * as the key if an exit code was defined */
\r
651 if (ret) return get_exit_action(service_name, 0, action, default_action);
\r
655 /* Close registry */
\r