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
122 if (service->no_console) set_number(key, NSSM_REG_NO_CONSOLE, 1);
\r
123 else if (editing) RegDeleteValue(key, NSSM_REG_NO_CONSOLE);
\r
126 if (service->env) {
\r
127 if (RegSetValueEx(key, NSSM_REG_ENV, 0, REG_MULTI_SZ, (const unsigned char *) service->env, (unsigned long) service->envlen * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
128 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV, error_string(GetLastError()), 0);
\r
131 else if (editing) RegDeleteValue(key, NSSM_REG_ENV);
\r
132 if (service->env_extra) {
\r
133 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
134 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV_EXTRA, error_string(GetLastError()), 0);
\r
137 else if (editing) RegDeleteValue(key, NSSM_REG_ENV_EXTRA);
\r
139 /* Close registry. */
\r
145 int create_exit_action(TCHAR *service_name, const TCHAR *action_string, bool editing) {
\r
147 TCHAR registry[KEY_LENGTH];
\r
148 if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, NSSM_REGISTRY _T("\\%s"), service_name, NSSM_REG_EXIT) < 0) {
\r
149 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("NSSM_REG_EXIT"), _T("create_exit_action()"), 0);
\r
153 /* Try to open the registry */
\r
155 unsigned long disposition;
\r
156 if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, KEY_WRITE, 0, &key, &disposition) != ERROR_SUCCESS) {
\r
157 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
161 /* Do nothing if the key already existed */
\r
162 if (disposition == REG_OPENED_EXISTING_KEY && ! editing) {
\r
167 /* Create the default value */
\r
168 if (RegSetValueEx(key, 0, 0, REG_SZ, (const unsigned char *) action_string, (unsigned long) (_tcslen(action_string) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
169 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_EXIT, error_string(GetLastError()), 0);
\r
174 /* Close registry */
\r
180 int get_environment(TCHAR *service_name, HKEY key, TCHAR *value, TCHAR **env, unsigned long *envlen) {
\r
181 unsigned long type = REG_MULTI_SZ;
\r
183 /* Dummy test to find buffer size */
\r
184 unsigned long ret = RegQueryValueEx(key, value, 0, &type, NULL, envlen);
\r
185 if (ret != ERROR_SUCCESS) {
\r
188 /* The service probably doesn't have any environment configured */
\r
189 if (ret == ERROR_FILE_NOT_FOUND) return 0;
\r
190 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
194 if (type != REG_MULTI_SZ) {
\r
197 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_INVALID_ENVIRONMENT_STRING_TYPE, value, service_name, 0);
\r
201 /* Probably not possible */
\r
202 if (! *envlen) return 0;
\r
204 /* Previously initialised? */
\r
205 if (*env) HeapFree(GetProcessHeap(), 0, *env);
\r
207 *env = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, *envlen);
\r
210 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, value, _T("get_environment()"), 0);
\r
214 /* Actually get the strings */
\r
215 ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) *env, envlen);
\r
216 if (ret != ERROR_SUCCESS) {
\r
217 HeapFree(GetProcessHeap(), 0, *env);
\r
220 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
228 int get_string(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool expand, bool sanitise, bool must_exist) {
\r
229 TCHAR *buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, datalen);
\r
231 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, value, _T("get_string()"), 0);
\r
235 ZeroMemory(data, datalen);
\r
237 unsigned long type = REG_EXPAND_SZ;
\r
238 unsigned long buflen = datalen;
\r
240 unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);
\r
241 if (ret != ERROR_SUCCESS) {
\r
242 unsigned long error = GetLastError();
\r
243 HeapFree(GetProcessHeap(), 0, buffer);
\r
245 if (ret == ERROR_FILE_NOT_FOUND) {
\r
246 if (! must_exist) return 0;
\r
249 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(error), 0);
\r
253 /* Paths aren't allowed to contain quotes. */
\r
254 if (sanitise) PathUnquoteSpaces(buffer);
\r
256 /* Do we want to expand the string? */
\r
258 if (type == REG_EXPAND_SZ) type = REG_SZ;
\r
261 /* Technically we shouldn't expand environment strings from REG_SZ values */
\r
262 if (type != REG_EXPAND_SZ) {
\r
263 memmove(data, buffer, buflen);
\r
264 HeapFree(GetProcessHeap(), 0, buffer);
\r
268 ret = ExpandEnvironmentStrings((TCHAR *) buffer, data, datalen);
\r
269 if (! ret || ret > datalen) {
\r
270 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_EXPANDENVIRONMENTSTRINGS_FAILED, buffer, error_string(GetLastError()), 0);
\r
271 HeapFree(GetProcessHeap(), 0, buffer);
\r
275 HeapFree(GetProcessHeap(), 0, buffer);
\r
279 int get_string(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise) {
\r
280 return get_string(key, value, data, datalen, false, sanitise, true);
\r
283 int expand_parameter(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise, bool must_exist) {
\r
284 return get_string(key, value, data, datalen, true, sanitise, must_exist);
\r
287 int expand_parameter(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise) {
\r
288 return expand_parameter(key, value, data, datalen, sanitise, true);
\r
292 Sets a string in the registry.
\r
293 Returns: 0 if it was set.
\r
296 int set_string(HKEY key, TCHAR *value, TCHAR *string, bool expand) {
\r
297 unsigned long type = expand ? REG_EXPAND_SZ : REG_SZ;
\r
298 if (RegSetValueEx(key, value, 0, type, (const unsigned char *) string, (unsigned long) (_tcslen(string) + 1) * sizeof(TCHAR)) == ERROR_SUCCESS) return 0;
\r
299 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
303 int set_string(HKEY key, TCHAR *value, TCHAR *string) {
\r
304 return set_string(key, value, string, false);
\r
308 int set_expand_string(HKEY key, TCHAR *value, TCHAR *string) {
\r
309 return set_string(key, value, string, true);
\r
314 Set an unsigned long in the registry.
\r
315 Returns: 0 if it was set.
\r
318 int set_number(HKEY key, TCHAR *value, unsigned long number) {
\r
319 if (RegSetValueEx(key, value, 0, REG_DWORD, (const unsigned char *) &number, sizeof(number)) == ERROR_SUCCESS) return 0;
\r
320 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
325 Query an unsigned long from the registry.
\r
326 Returns: 1 if a number was retrieved.
\r
327 0 if none was found and must_exist is false.
\r
328 -1 if none was found and must_exist is true.
\r
331 int get_number(HKEY key, TCHAR *value, unsigned long *number, bool must_exist) {
\r
332 unsigned long type = REG_DWORD;
\r
333 unsigned long number_len = sizeof(unsigned long);
\r
335 int ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) number, &number_len);
\r
336 if (ret == ERROR_SUCCESS) return 1;
\r
338 if (ret == ERROR_FILE_NOT_FOUND) {
\r
339 if (! must_exist) return 0;
\r
342 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
343 if (ret == ERROR_FILE_NOT_FOUND) return -1;
\r
348 int get_number(HKEY key, TCHAR *value, unsigned long *number) {
\r
349 return get_number(key, value, number, true);
\r
352 /* Replace NULL with CRLF. Leave NULL NULL as the end marker. */
\r
353 int format_double_null(TCHAR *dn, unsigned long dnlen, TCHAR **formatted, unsigned long *newlen) {
\r
354 unsigned long i, j;
\r
362 for (i = 0; i < dnlen; i++) if (! dn[i] && dn[i + 1]) ++*newlen;
\r
364 *formatted = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *newlen * sizeof(TCHAR));
\r
365 if (! *formatted) {
\r
370 for (i = 0, j = 0; i < dnlen; i++) {
\r
371 (*formatted)[j] = dn[i];
\r
374 (*formatted)[j] = _T('\r');
\r
375 (*formatted)[++j] = _T('\n');
\r
384 /* Strip CR and replace LF with NULL. */
\r
385 int unformat_double_null(TCHAR *dn, unsigned long dnlen, TCHAR **unformatted, unsigned long *newlen) {
\r
386 unsigned long i, j;
\r
394 for (i = 0; i < dnlen; i++) if (dn[i] != _T('\r')) ++*newlen;
\r
395 /* Must end with two NULLs. */
\r
398 *unformatted = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *newlen * sizeof(TCHAR));
\r
399 if (! *unformatted) return 1;
\r
401 for (i = 0, j = 0; i < dnlen; i++) {
\r
402 if (dn[i] == _T('\r')) continue;
\r
403 if (dn[i] == _T('\n')) (*unformatted)[j] = _T('\0');
\r
404 else (*unformatted)[j] = dn[i];
\r
411 void override_milliseconds(TCHAR *service_name, HKEY key, TCHAR *value, unsigned long *buffer, unsigned long default_value, unsigned long event) {
\r
412 unsigned long type = REG_DWORD;
\r
413 unsigned long buflen = sizeof(unsigned long);
\r
415 unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);
\r
416 if (ret != ERROR_SUCCESS) {
\r
417 if (ret != ERROR_FILE_NOT_FOUND) {
\r
418 if (type != REG_DWORD) {
\r
419 TCHAR milliseconds[16];
\r
420 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), default_value);
\r
421 log_event(EVENTLOG_WARNING_TYPE, event, service_name, value, milliseconds, 0);
\r
423 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
428 if (! ok) *buffer = default_value;
\r
431 HKEY open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam) {
\r
433 TCHAR registry[KEY_LENGTH];
\r
437 if (sub) ret = _sntprintf_s(registry, _countof(registry), _TRUNCATE, NSSM_REGISTRY _T("\\%s"), service_name, sub);
\r
438 else ret = _sntprintf_s(registry, _countof(registry), _TRUNCATE, NSSM_REGISTRY, service_name);
\r
440 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("NSSM_REGISTRY"), _T("open_registry()"), 0);
\r
444 if (sam & KEY_WRITE) {
\r
445 if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, sam, 0, &key, 0) != ERROR_SUCCESS) {
\r
446 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
451 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, registry, 0, sam, &key) != ERROR_SUCCESS) {
\r
452 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
460 HKEY open_registry(const TCHAR *service_name, REGSAM sam) {
\r
461 return open_registry(service_name, 0, sam);
\r
464 int get_io_parameters(nssm_service_t *service, HKEY key) {
\r
466 if (get_createfile_parameters(key, NSSM_REG_STDIN, service->stdin_path, &service->stdin_sharing, NSSM_STDIN_SHARING, &service->stdin_disposition, NSSM_STDIN_DISPOSITION, &service->stdin_flags, NSSM_STDIN_FLAGS)) {
\r
467 service->stdin_sharing = service->stdin_disposition = service->stdin_flags = 0;
\r
468 ZeroMemory(service->stdin_path, _countof(service->stdin_path) * sizeof(TCHAR));
\r
473 if (get_createfile_parameters(key, NSSM_REG_STDOUT, service->stdout_path, &service->stdout_sharing, NSSM_STDOUT_SHARING, &service->stdout_disposition, NSSM_STDOUT_DISPOSITION, &service->stdout_flags, NSSM_STDOUT_FLAGS)) {
\r
474 service->stdout_sharing = service->stdout_disposition = service->stdout_flags = 0;
\r
475 ZeroMemory(service->stdout_path, _countof(service->stdout_path) * sizeof(TCHAR));
\r
480 if (get_createfile_parameters(key, NSSM_REG_STDERR, service->stderr_path, &service->stderr_sharing, NSSM_STDERR_SHARING, &service->stderr_disposition, NSSM_STDERR_DISPOSITION, &service->stderr_flags, NSSM_STDERR_FLAGS)) {
\r
481 service->stderr_sharing = service->stderr_disposition = service->stderr_flags = 0;
\r
482 ZeroMemory(service->stderr_path, _countof(service->stderr_path) * sizeof(TCHAR));
\r
489 int get_parameters(nssm_service_t *service, STARTUPINFO *si) {
\r
492 /* Try to open the registry */
\r
493 HKEY key = open_registry(service->name, KEY_READ);
\r
494 if (! key) return 1;
\r
496 /* Don't expand parameters when retrieving for the GUI. */
\r
497 bool expand = si ? true : false;
\r
499 /* Try to get executable file - MUST succeed */
\r
500 if (get_string(key, NSSM_REG_EXE, service->exe, sizeof(service->exe), expand, false, true)) {
\r
505 /* Try to get flags - may fail and we don't care */
\r
506 if (get_string(key, NSSM_REG_FLAGS, service->flags, sizeof(service->flags), expand, false, true)) {
\r
507 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_FLAGS, NSSM_REG_FLAGS, service->name, service->exe, 0);
\r
508 ZeroMemory(service->flags, sizeof(service->flags));
\r
511 /* Try to get startup directory - may fail and we fall back to a default */
\r
512 if (get_string(key, NSSM_REG_DIR, service->dir, sizeof(service->dir), expand, true, true) || ! service->dir[0]) {
\r
513 _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);
\r
514 strip_basename(service->dir);
\r
515 if (service->dir[0] == _T('\0')) {
\r
517 ret = GetWindowsDirectory(service->dir, sizeof(service->dir));
\r
518 if (! ret || ret > sizeof(service->dir)) {
\r
519 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_NO_DIR_AND_NO_FALLBACK, NSSM_REG_DIR, service->name, 0);
\r
524 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_DIR, NSSM_REG_DIR, service->name, service->dir, 0);
\r
527 /* Try to get processor affinity - may fail. */
\r
529 if (get_string(key, NSSM_REG_AFFINITY, buffer, sizeof(buffer), false, false, false) || ! buffer[0]) service->affinity = 0LL;
\r
530 else if (affinity_string_to_mask(buffer, &service->affinity)) {
\r
531 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_AFFINITY_MASK, service->name, buffer);
\r
532 service->affinity = 0LL;
\r
535 DWORD_PTR affinity, system_affinity;
\r
537 if (GetProcessAffinityMask(GetCurrentProcess(), &affinity, &system_affinity)) {
\r
538 _int64 effective_affinity = service->affinity & system_affinity;
\r
539 if (effective_affinity != service->affinity) {
\r
541 if (! affinity_mask_to_string(system_affinity, &system)) {
\r
542 TCHAR *effective = 0;
\r
543 if (! affinity_mask_to_string(effective_affinity, &effective)) {
\r
544 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_EFFECTIVE_AFFINITY_MASK, service->name, buffer, system, effective, 0);
\r
546 HeapFree(GetProcessHeap(), 0, effective);
\r
548 HeapFree(GetProcessHeap(), 0, system);
\r
553 /* Try to get environment variables - may fail */
\r
554 get_environment(service->name, key, NSSM_REG_ENV, &service->env, &service->envlen);
\r
555 /* Environment variables to add to existing rather than replace - may fail. */
\r
556 get_environment(service->name, key, NSSM_REG_ENV_EXTRA, &service->env_extra, &service->env_extralen);
\r
558 /* Try to get priority - may fail. */
\r
559 unsigned long priority;
\r
560 if (get_number(key, NSSM_REG_PRIORITY, &priority, false) == 1) {
\r
561 if (priority == (priority & priority_mask())) service->priority = priority;
\r
562 else log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_PRIORITY, service->name, NSSM_REG_PRIORITY, 0);
\r
565 /* Try to get file rotation settings - may fail. */
\r
566 unsigned long rotate_files;
\r
567 if (get_number(key, NSSM_REG_ROTATE, &rotate_files, false) == 1) {
\r
568 if (rotate_files) service->rotate_files = true;
\r
569 else service->rotate_files = false;
\r
571 else service->rotate_files = false;
\r
572 if (get_number(key, NSSM_REG_ROTATE_ONLINE, &rotate_files, false) == 1) {
\r
573 if (rotate_files) service->rotate_stdout_online = service->rotate_stderr_online = true;
\r
574 else service->rotate_stdout_online = service->rotate_stderr_online = false;
\r
576 else service->rotate_stdout_online = service->rotate_stderr_online = false;
\r
577 if (get_number(key, NSSM_REG_ROTATE_SECONDS, &service->rotate_seconds, false) != 1) service->rotate_seconds = 0;
\r
578 if (get_number(key, NSSM_REG_ROTATE_BYTES_LOW, &service->rotate_bytes_low, false) != 1) service->rotate_bytes_low = 0;
\r
579 if (get_number(key, NSSM_REG_ROTATE_BYTES_HIGH, &service->rotate_bytes_high, false) != 1) service->rotate_bytes_high = 0;
\r
581 /* Try to get force new console setting - may fail. */
\r
582 if (get_number(key, NSSM_REG_NO_CONSOLE, &service->no_console, false) != 1) service->no_console = 0;
\r
584 /* Change to startup directory in case stdout/stderr are relative paths. */
\r
585 TCHAR cwd[PATH_LENGTH];
\r
586 GetCurrentDirectory(_countof(cwd), cwd);
\r
587 SetCurrentDirectory(service->dir);
\r
589 /* Try to get stdout and stderr */
\r
590 if (get_io_parameters(service, key)) {
\r
591 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);
\r
593 SetCurrentDirectory(cwd);
\r
597 /* Change back in case the startup directory needs to be deleted. */
\r
598 SetCurrentDirectory(cwd);
\r
600 /* Try to get mandatory restart delay */
\r
601 override_milliseconds(service->name, key, NSSM_REG_RESTART_DELAY, &service->restart_delay, 0, NSSM_EVENT_BOGUS_RESTART_DELAY);
\r
603 /* Try to get throttle restart delay */
\r
604 override_milliseconds(service->name, key, NSSM_REG_THROTTLE, &service->throttle_delay, NSSM_RESET_THROTTLE_RESTART, NSSM_EVENT_BOGUS_THROTTLE);
\r
606 /* Try to get service stop flags. */
\r
607 unsigned long type = REG_DWORD;
\r
608 unsigned long stop_method_skip;
\r
609 unsigned long buflen = sizeof(stop_method_skip);
\r
610 bool stop_ok = false;
\r
611 ret = RegQueryValueEx(key, NSSM_REG_STOP_METHOD_SKIP, 0, &type, (unsigned char *) &stop_method_skip, &buflen);
\r
612 if (ret != ERROR_SUCCESS) {
\r
613 if (ret != ERROR_FILE_NOT_FOUND) {
\r
614 if (type != REG_DWORD) {
\r
615 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_STOP_METHOD_SKIP, service->name, NSSM_REG_STOP_METHOD_SKIP, NSSM, 0);
\r
617 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, NSSM_REG_STOP_METHOD_SKIP, error_string(GetLastError()), 0);
\r
620 else stop_ok = true;
\r
622 /* Try all methods except those requested to be skipped. */
\r
623 service->stop_method = ~0;
\r
624 if (stop_ok) service->stop_method &= ~stop_method_skip;
\r
626 /* Try to get kill delays - may fail. */
\r
627 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
628 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
629 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
631 /* Try to get default exit action. */
\r
632 bool default_action;
\r
633 service->default_exit_action = NSSM_EXIT_RESTART;
\r
634 TCHAR action_string[ACTION_LEN];
\r
635 if (! get_exit_action(service->name, 0, action_string, &default_action)) {
\r
636 for (int i = 0; exit_action_strings[i]; i++) {
\r
637 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
638 service->default_exit_action = i;
\r
644 /* Close registry */
\r
651 Sets the string for the exit action corresponding to the exit code.
\r
653 ret is a pointer to an unsigned long containing the exit code.
\r
654 If ret is NULL, we retrieve the default exit action unconditionally.
\r
656 action is a buffer which receives the string.
\r
658 default_action is a pointer to a bool which is set to false if there
\r
659 was an explicit string for the given exit code, or true if we are
\r
660 returning the default action.
\r
662 Returns: 0 on success.
\r
665 int get_exit_action(const TCHAR *service_name, unsigned long *ret, TCHAR *action, bool *default_action) {
\r
666 /* Are we returning the default action or a status-specific one? */
\r
667 *default_action = ! ret;
\r
669 /* Try to open the registry */
\r
670 HKEY key = open_registry(service_name, NSSM_REG_EXIT, KEY_READ);
\r
671 if (! key) return 1;
\r
673 unsigned long type = REG_SZ;
\r
674 unsigned long action_len = ACTION_LEN;
\r
677 if (! ret) code[0] = _T('\0');
\r
678 else if (_sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), *ret) < 0) {
\r
680 return get_exit_action(service_name, 0, action, default_action);
\r
682 if (RegQueryValueEx(key, code, 0, &type, (unsigned char *) action, &action_len) != ERROR_SUCCESS) {
\r
684 /* Try again with * as the key if an exit code was defined */
\r
685 if (ret) return get_exit_action(service_name, 0, action, default_action);
\r
689 /* Close registry */
\r