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[PATH_LENGTH];
\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 get_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("get_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
226 int get_string(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool expand, bool sanitise, bool must_exist) {
\r
227 TCHAR *buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, datalen);
\r
229 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, value, _T("get_string()"), 0);
\r
233 ZeroMemory(data, datalen);
\r
235 unsigned long type = REG_EXPAND_SZ;
\r
236 unsigned long buflen = datalen;
\r
238 unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);
\r
239 if (ret != ERROR_SUCCESS) {
\r
240 unsigned long error = GetLastError();
\r
241 HeapFree(GetProcessHeap(), 0, buffer);
\r
243 if (ret == ERROR_FILE_NOT_FOUND) {
\r
244 if (! must_exist) return 0;
\r
247 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(error), 0);
\r
251 /* Paths aren't allowed to contain quotes. */
\r
252 if (sanitise) PathUnquoteSpaces(buffer);
\r
254 /* Do we want to expand the string? */
\r
256 if (type == REG_EXPAND_SZ) type = REG_SZ;
\r
259 /* Technically we shouldn't expand environment strings from REG_SZ values */
\r
260 if (type != REG_EXPAND_SZ) {
\r
261 memmove(data, buffer, buflen);
\r
262 HeapFree(GetProcessHeap(), 0, buffer);
\r
266 ret = ExpandEnvironmentStrings((TCHAR *) buffer, data, datalen);
\r
267 if (! ret || ret > datalen) {
\r
268 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_EXPANDENVIRONMENTSTRINGS_FAILED, buffer, error_string(GetLastError()), 0);
\r
269 HeapFree(GetProcessHeap(), 0, buffer);
\r
273 HeapFree(GetProcessHeap(), 0, buffer);
\r
277 int get_string(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise) {
\r
278 return get_string(key, value, data, datalen, false, sanitise, true);
\r
281 int expand_parameter(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise, bool must_exist) {
\r
282 return get_string(key, value, data, datalen, true, sanitise, must_exist);
\r
285 int expand_parameter(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise) {
\r
286 return expand_parameter(key, value, data, datalen, sanitise, true);
\r
290 Sets a string in the registry.
\r
291 Returns: 0 if it was set.
\r
294 int set_string(HKEY key, TCHAR *value, TCHAR *string, bool expand) {
\r
295 unsigned long type = expand ? REG_EXPAND_SZ : REG_SZ;
\r
296 if (RegSetValueEx(key, value, 0, type, (const unsigned char *) string, (unsigned long) (_tcslen(string) + 1) * sizeof(TCHAR)) == ERROR_SUCCESS) return 0;
\r
297 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
301 int set_string(HKEY key, TCHAR *value, TCHAR *string) {
\r
302 return set_string(key, value, string, false);
\r
306 int set_expand_string(HKEY key, TCHAR *value, TCHAR *string) {
\r
307 return set_string(key, value, string, true);
\r
312 Set an unsigned long in the registry.
\r
313 Returns: 0 if it was set.
\r
316 int set_number(HKEY key, TCHAR *value, unsigned long number) {
\r
317 if (RegSetValueEx(key, value, 0, REG_DWORD, (const unsigned char *) &number, sizeof(number)) == ERROR_SUCCESS) return 0;
\r
318 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
323 Query an unsigned long from the registry.
\r
324 Returns: 1 if a number was retrieved.
\r
325 0 if none was found and must_exist is false.
\r
326 -1 if none was found and must_exist is true.
\r
329 int get_number(HKEY key, TCHAR *value, unsigned long *number, bool must_exist) {
\r
330 unsigned long type = REG_DWORD;
\r
331 unsigned long number_len = sizeof(unsigned long);
\r
333 int ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) number, &number_len);
\r
334 if (ret == ERROR_SUCCESS) return 1;
\r
336 if (ret == ERROR_FILE_NOT_FOUND) {
\r
337 if (! must_exist) return 0;
\r
340 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
341 if (ret == ERROR_FILE_NOT_FOUND) return -1;
\r
346 int get_number(HKEY key, TCHAR *value, unsigned long *number) {
\r
347 return get_number(key, value, number, true);
\r
350 void override_milliseconds(TCHAR *service_name, HKEY key, TCHAR *value, unsigned long *buffer, unsigned long default_value, unsigned long event) {
\r
351 unsigned long type = REG_DWORD;
\r
352 unsigned long buflen = sizeof(unsigned long);
\r
354 unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);
\r
355 if (ret != ERROR_SUCCESS) {
\r
356 if (ret != ERROR_FILE_NOT_FOUND) {
\r
357 if (type != REG_DWORD) {
\r
358 TCHAR milliseconds[16];
\r
359 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), default_value);
\r
360 log_event(EVENTLOG_WARNING_TYPE, event, service_name, value, milliseconds, 0);
\r
362 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
367 if (! ok) *buffer = default_value;
\r
370 HKEY open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam) {
\r
372 TCHAR registry[KEY_LENGTH];
\r
376 if (sub) ret = _sntprintf_s(registry, _countof(registry), _TRUNCATE, NSSM_REGISTRY _T("\\%s"), service_name, sub);
\r
377 else ret = _sntprintf_s(registry, _countof(registry), _TRUNCATE, NSSM_REGISTRY, service_name);
\r
379 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("NSSM_REGISTRY"), _T("open_registry()"), 0);
\r
383 if (sam & KEY_WRITE) {
\r
384 if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, sam, 0, &key, 0) != ERROR_SUCCESS) {
\r
385 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
390 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, registry, 0, sam, &key) != ERROR_SUCCESS) {
\r
391 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
399 HKEY open_registry(const TCHAR *service_name, REGSAM sam) {
\r
400 return open_registry(service_name, 0, sam);
\r
403 int get_parameters(nssm_service_t *service, STARTUPINFO *si) {
\r
406 /* Try to open the registry */
\r
407 HKEY key = open_registry(service->name, KEY_READ);
\r
408 if (! key) return 1;
\r
410 /* Try to get executable file - MUST succeed */
\r
411 if (expand_parameter(key, NSSM_REG_EXE, service->exe, sizeof(service->exe), false)) {
\r
416 /* Try to get flags - may fail and we don't care */
\r
417 if (expand_parameter(key, NSSM_REG_FLAGS, service->flags, sizeof(service->flags), false)) {
\r
418 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_FLAGS, NSSM_REG_FLAGS, service->name, service->exe, 0);
\r
419 ZeroMemory(service->flags, sizeof(service->flags));
\r
422 /* Try to get startup directory - may fail and we fall back to a default */
\r
423 if (expand_parameter(key, NSSM_REG_DIR, service->dir, sizeof(service->dir), true) || ! service->dir[0]) {
\r
424 _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);
\r
425 strip_basename(service->dir);
\r
426 if (service->dir[0] == _T('\0')) {
\r
428 ret = GetWindowsDirectory(service->dir, sizeof(service->dir));
\r
429 if (! ret || ret > sizeof(service->dir)) {
\r
430 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_NO_DIR_AND_NO_FALLBACK, NSSM_REG_DIR, service->name, 0);
\r
435 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_DIR, NSSM_REG_DIR, service->name, service->dir, 0);
\r
438 /* Try to get processor affinity - may fail. */
\r
440 if (expand_parameter(key, NSSM_REG_AFFINITY, buffer, sizeof(buffer), false, false) || ! buffer[0]) service->affinity = 0LL;
\r
441 else if (affinity_string_to_mask(buffer, &service->affinity)) {
\r
442 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_AFFINITY_MASK, service->name, buffer);
\r
443 service->affinity = 0LL;
\r
446 DWORD_PTR affinity, system_affinity;
\r
448 if (GetProcessAffinityMask(GetCurrentProcess(), &affinity, &system_affinity)) {
\r
449 _int64 effective_affinity = service->affinity & system_affinity;
\r
450 if (effective_affinity != service->affinity) {
\r
452 if (! affinity_mask_to_string(system_affinity, &system)) {
\r
453 TCHAR *effective = 0;
\r
454 if (! affinity_mask_to_string(effective_affinity, &effective)) {
\r
455 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_EFFECTIVE_AFFINITY_MASK, service->name, buffer, system, effective, 0);
\r
457 HeapFree(GetProcessHeap(), 0, effective);
\r
459 HeapFree(GetProcessHeap(), 0, system);
\r
464 /* Try to get environment variables - may fail */
\r
465 get_environment(service->name, key, NSSM_REG_ENV, &service->env, &service->envlen);
\r
466 /* Environment variables to add to existing rather than replace - may fail. */
\r
467 get_environment(service->name, key, NSSM_REG_ENV_EXTRA, &service->env_extra, &service->env_extralen);
\r
470 if (service->env_extra) {
\r
472 unsigned long envlen;
\r
474 /* Copy our environment for the application. */
\r
475 if (! service->env) {
\r
476 TCHAR *rawenv = GetEnvironmentStrings();
\r
480 The environment block starts with variables of the form
\r
481 =C:=C:\Windows\System32 which we ignore.
\r
483 while (*env == _T('=')) {
\r
484 for ( ; *env; env++);
\r
490 for ( ; env[envlen]; envlen++);
\r
491 if (! env[++envlen]) break;
\r
495 service->envlen = envlen * sizeof(TCHAR);
\r
496 service->env = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, service->envlen);
\r
497 memmove(service->env, env, service->envlen);
\r
498 FreeEnvironmentStrings(rawenv);
\r
503 /* Append extra variables to configured variables. */
\r
504 if (service->env) {
\r
505 envlen = service->envlen + service->env_extralen - sizeof(TCHAR)/*?*/;
\r
506 env = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, envlen);
\r
508 memmove(env, service->env, service->envlen - sizeof(TCHAR));
\r
509 /* envlen is in bytes but env[i] is in characters. */
\r
510 memmove(env + (service->envlen / sizeof(TCHAR)) - 1, service->env_extra, service->env_extralen);
\r
512 HeapFree(GetProcessHeap(), 0, service->env);
\r
513 HeapFree(GetProcessHeap(), 0, service->env_extra);
\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 /* Huh? No environment at all? */
\r
521 service->env = service->env_extra;
\r
522 service->envlen = service->env_extralen;
\r
526 service->env_extra = 0;
\r
527 service->env_extralen = 0;
\r
530 /* Try to get priority - may fail. */
\r
531 unsigned long priority;
\r
532 if (get_number(key, NSSM_REG_PRIORITY, &priority, false) == 1) {
\r
533 if (priority == (priority & priority_mask())) service->priority = priority;
\r
534 else log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_PRIORITY, service->name, NSSM_REG_PRIORITY, 0);
\r
537 /* Try to get file rotation settings - may fail. */
\r
538 unsigned long rotate_files;
\r
539 if (get_number(key, NSSM_REG_ROTATE, &rotate_files, false) == 1) {
\r
540 if (rotate_files) service->rotate_files = true;
\r
541 else service->rotate_files = false;
\r
543 else service->rotate_files = false;
\r
544 if (get_number(key, NSSM_REG_ROTATE_ONLINE, &rotate_files, false) == 1) {
\r
545 if (rotate_files) service->rotate_stdout_online = service->rotate_stderr_online = true;
\r
546 else service->rotate_stdout_online = service->rotate_stderr_online = false;
\r
548 else service->rotate_stdout_online = service->rotate_stderr_online = 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[PATH_LENGTH];
\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 mandatory restart delay */
\r
570 override_milliseconds(service->name, key, NSSM_REG_RESTART_DELAY, &service->restart_delay, 0, NSSM_EVENT_BOGUS_RESTART_DELAY);
\r
572 /* Try to get throttle restart delay */
\r
573 override_milliseconds(service->name, key, NSSM_REG_THROTTLE, &service->throttle_delay, NSSM_RESET_THROTTLE_RESTART, NSSM_EVENT_BOGUS_THROTTLE);
\r
575 /* Try to get service stop flags. */
\r
576 unsigned long type = REG_DWORD;
\r
577 unsigned long stop_method_skip;
\r
578 unsigned long buflen = sizeof(stop_method_skip);
\r
579 bool stop_ok = false;
\r
580 ret = RegQueryValueEx(key, NSSM_REG_STOP_METHOD_SKIP, 0, &type, (unsigned char *) &stop_method_skip, &buflen);
\r
581 if (ret != ERROR_SUCCESS) {
\r
582 if (ret != ERROR_FILE_NOT_FOUND) {
\r
583 if (type != REG_DWORD) {
\r
584 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_STOP_METHOD_SKIP, service->name, NSSM_REG_STOP_METHOD_SKIP, NSSM, 0);
\r
586 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, NSSM_REG_STOP_METHOD_SKIP, error_string(GetLastError()), 0);
\r
589 else stop_ok = true;
\r
591 /* Try all methods except those requested to be skipped. */
\r
592 service->stop_method = ~0;
\r
593 if (stop_ok) service->stop_method &= ~stop_method_skip;
\r
595 /* Try to get kill delays - may fail. */
\r
596 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
597 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
598 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
600 /* Try to get default exit action. */
\r
601 bool default_action;
\r
602 service->default_exit_action = NSSM_EXIT_RESTART;
\r
603 TCHAR action_string[ACTION_LEN];
\r
604 if (! get_exit_action(service->name, 0, action_string, &default_action)) {
\r
605 for (int i = 0; exit_action_strings[i]; i++) {
\r
606 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
607 service->default_exit_action = i;
\r
613 /* Close registry */
\r
620 Sets the string for the exit action corresponding to the exit code.
\r
622 ret is a pointer to an unsigned long containing the exit code.
\r
623 If ret is NULL, we retrieve the default exit action unconditionally.
\r
625 action is a buffer which receives the string.
\r
627 default_action is a pointer to a bool which is set to false if there
\r
628 was an explicit string for the given exit code, or true if we are
\r
629 returning the default action.
\r
631 Returns: 0 on success.
\r
634 int get_exit_action(const TCHAR *service_name, unsigned long *ret, TCHAR *action, bool *default_action) {
\r
635 /* Are we returning the default action or a status-specific one? */
\r
636 *default_action = ! ret;
\r
638 /* Try to open the registry */
\r
639 HKEY key = open_registry(service_name, NSSM_REG_EXIT, KEY_READ);
\r
640 if (! key) return 1;
\r
642 unsigned long type = REG_SZ;
\r
643 unsigned long action_len = ACTION_LEN;
\r
646 if (! ret) code[0] = _T('\0');
\r
647 else if (_sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), *ret) < 0) {
\r
649 return get_exit_action(service_name, 0, action, default_action);
\r
651 if (RegQueryValueEx(key, code, 0, &type, (unsigned char *) action, &action_len) != ERROR_SUCCESS) {
\r
653 /* Try again with * as the key if an exit code was defined */
\r
654 if (ret) return get_exit_action(service_name, 0, action, default_action);
\r
658 /* Close registry */
\r