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->kill_process_tree) set_number(key, NSSM_REG_KILL_PROCESS_TREE, 0);
\r
83 else if (editing) RegDeleteValue(key, NSSM_REG_KILL_PROCESS_TREE);
\r
84 if (service->stdin_path[0] || editing) {
\r
85 if (service->stdin_path[0]) set_expand_string(key, NSSM_REG_STDIN, service->stdin_path);
\r
86 else if (editing) RegDeleteValue(key, NSSM_REG_STDIN);
\r
87 if (service->stdin_sharing != NSSM_STDIN_SHARING) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_SHARING, service->stdin_sharing);
\r
88 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_SHARING);
\r
89 if (service->stdin_disposition != NSSM_STDIN_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_DISPOSITION, service->stdin_disposition);
\r
90 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_DISPOSITION);
\r
91 if (service->stdin_flags != NSSM_STDIN_FLAGS) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_FLAGS, service->stdin_flags);
\r
92 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_FLAGS);
\r
94 if (service->stdout_path[0] || editing) {
\r
95 if (service->stdout_path[0]) set_expand_string(key, NSSM_REG_STDOUT, service->stdout_path);
\r
96 else if (editing) RegDeleteValue(key, NSSM_REG_STDOUT);
\r
97 if (service->stdout_sharing != NSSM_STDOUT_SHARING) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_SHARING, service->stdout_sharing);
\r
98 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_SHARING);
\r
99 if (service->stdout_disposition != NSSM_STDOUT_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_DISPOSITION, service->stdout_disposition);
\r
100 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_DISPOSITION);
\r
101 if (service->stdout_flags != NSSM_STDOUT_FLAGS) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_FLAGS, service->stdout_flags);
\r
102 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_FLAGS);
\r
103 if (service->stdout_copy_and_truncate) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_COPY_AND_TRUNCATE, 1);
\r
104 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_COPY_AND_TRUNCATE);
\r
106 if (service->stderr_path[0] || editing) {
\r
107 if (service->stderr_path[0]) set_expand_string(key, NSSM_REG_STDERR, service->stderr_path);
\r
108 else if (editing) RegDeleteValue(key, NSSM_REG_STDERR);
\r
109 if (service->stderr_sharing != NSSM_STDERR_SHARING) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_SHARING, service->stderr_sharing);
\r
110 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_SHARING);
\r
111 if (service->stderr_disposition != NSSM_STDERR_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_DISPOSITION, service->stderr_disposition);
\r
112 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_DISPOSITION);
\r
113 if (service->stderr_flags != NSSM_STDERR_FLAGS) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_FLAGS, service->stderr_flags);
\r
114 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_FLAGS);
\r
115 if (service->stderr_copy_and_truncate) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_COPY_AND_TRUNCATE, 1);
\r
116 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_COPY_AND_TRUNCATE);
\r
118 if (service->rotate_files) set_number(key, NSSM_REG_ROTATE, 1);
\r
119 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE);
\r
120 if (service->rotate_stdout_online) set_number(key, NSSM_REG_ROTATE_ONLINE, 1);
\r
121 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_ONLINE);
\r
122 if (service->rotate_seconds) set_number(key, NSSM_REG_ROTATE_SECONDS, service->rotate_seconds);
\r
123 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_SECONDS);
\r
124 if (service->rotate_bytes_low) set_number(key, NSSM_REG_ROTATE_BYTES_LOW, service->rotate_bytes_low);
\r
125 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_BYTES_LOW);
\r
126 if (service->rotate_bytes_high) set_number(key, NSSM_REG_ROTATE_BYTES_HIGH, service->rotate_bytes_high);
\r
127 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_BYTES_HIGH);
\r
128 if (service->rotate_delay != NSSM_ROTATE_DELAY) set_number(key, NSSM_REG_ROTATE_DELAY, service->rotate_delay);
\r
129 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_DELAY);
\r
130 if (service->no_console) set_number(key, NSSM_REG_NO_CONSOLE, 1);
\r
131 else if (editing) RegDeleteValue(key, NSSM_REG_NO_CONSOLE);
\r
134 if (service->env) {
\r
135 if (RegSetValueEx(key, NSSM_REG_ENV, 0, REG_MULTI_SZ, (const unsigned char *) service->env, (unsigned long) service->envlen * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
136 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV, error_string(GetLastError()), 0);
\r
139 else if (editing) RegDeleteValue(key, NSSM_REG_ENV);
\r
140 if (service->env_extra) {
\r
141 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
142 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV_EXTRA, error_string(GetLastError()), 0);
\r
145 else if (editing) RegDeleteValue(key, NSSM_REG_ENV_EXTRA);
\r
147 /* Close registry. */
\r
153 int create_exit_action(TCHAR *service_name, const TCHAR *action_string, bool editing) {
\r
155 TCHAR registry[KEY_LENGTH];
\r
156 if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, NSSM_REGISTRY _T("\\%s"), service_name, NSSM_REG_EXIT) < 0) {
\r
157 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("NSSM_REG_EXIT"), _T("create_exit_action()"), 0);
\r
161 /* Try to open the registry */
\r
163 unsigned long disposition;
\r
164 if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, KEY_WRITE, 0, &key, &disposition) != ERROR_SUCCESS) {
\r
165 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
169 /* Do nothing if the key already existed */
\r
170 if (disposition == REG_OPENED_EXISTING_KEY && ! editing) {
\r
175 /* Create the default value */
\r
176 if (RegSetValueEx(key, 0, 0, REG_SZ, (const unsigned char *) action_string, (unsigned long) (_tcslen(action_string) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
177 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_EXIT, error_string(GetLastError()), 0);
\r
182 /* Close registry */
\r
188 int get_environment(TCHAR *service_name, HKEY key, TCHAR *value, TCHAR **env, unsigned long *envlen) {
\r
189 unsigned long type = REG_MULTI_SZ;
\r
191 /* Dummy test to find buffer size */
\r
192 unsigned long ret = RegQueryValueEx(key, value, 0, &type, NULL, envlen);
\r
193 if (ret != ERROR_SUCCESS) {
\r
196 /* The service probably doesn't have any environment configured */
\r
197 if (ret == ERROR_FILE_NOT_FOUND) return 0;
\r
198 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
202 if (type != REG_MULTI_SZ) {
\r
205 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_INVALID_ENVIRONMENT_STRING_TYPE, value, service_name, 0);
\r
209 /* Probably not possible */
\r
210 if (! *envlen) return 0;
\r
212 /* Previously initialised? */
\r
213 if (*env) HeapFree(GetProcessHeap(), 0, *env);
\r
215 *env = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, *envlen);
\r
218 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, value, _T("get_environment()"), 0);
\r
222 /* Actually get the strings */
\r
223 ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) *env, envlen);
\r
224 if (ret != ERROR_SUCCESS) {
\r
225 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
226 HeapFree(GetProcessHeap(), 0, *env);
\r
236 int get_string(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool expand, bool sanitise, bool must_exist) {
\r
237 TCHAR *buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, datalen);
\r
239 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, value, _T("get_string()"), 0);
\r
243 ZeroMemory(data, datalen);
\r
245 unsigned long type = REG_EXPAND_SZ;
\r
246 unsigned long buflen = datalen;
\r
248 unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);
\r
249 if (ret != ERROR_SUCCESS) {
\r
250 unsigned long error = GetLastError();
\r
251 HeapFree(GetProcessHeap(), 0, buffer);
\r
253 if (ret == ERROR_FILE_NOT_FOUND) {
\r
254 if (! must_exist) return 0;
\r
257 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(error), 0);
\r
261 /* Paths aren't allowed to contain quotes. */
\r
262 if (sanitise) PathUnquoteSpaces(buffer);
\r
264 /* Do we want to expand the string? */
\r
266 if (type == REG_EXPAND_SZ) type = REG_SZ;
\r
269 /* Technically we shouldn't expand environment strings from REG_SZ values */
\r
270 if (type != REG_EXPAND_SZ) {
\r
271 memmove(data, buffer, buflen);
\r
272 HeapFree(GetProcessHeap(), 0, buffer);
\r
276 ret = ExpandEnvironmentStrings((TCHAR *) buffer, data, datalen);
\r
277 if (! ret || ret > datalen) {
\r
278 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_EXPANDENVIRONMENTSTRINGS_FAILED, buffer, error_string(GetLastError()), 0);
\r
279 HeapFree(GetProcessHeap(), 0, buffer);
\r
283 HeapFree(GetProcessHeap(), 0, buffer);
\r
287 int get_string(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise) {
\r
288 return get_string(key, value, data, datalen, false, sanitise, true);
\r
291 int expand_parameter(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise, bool must_exist) {
\r
292 return get_string(key, value, data, datalen, true, sanitise, must_exist);
\r
295 int expand_parameter(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise) {
\r
296 return expand_parameter(key, value, data, datalen, sanitise, true);
\r
300 Sets a string in the registry.
\r
301 Returns: 0 if it was set.
\r
304 int set_string(HKEY key, TCHAR *value, TCHAR *string, bool expand) {
\r
305 unsigned long type = expand ? REG_EXPAND_SZ : REG_SZ;
\r
306 if (RegSetValueEx(key, value, 0, type, (const unsigned char *) string, (unsigned long) (_tcslen(string) + 1) * sizeof(TCHAR)) == ERROR_SUCCESS) return 0;
\r
307 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
311 int set_string(HKEY key, TCHAR *value, TCHAR *string) {
\r
312 return set_string(key, value, string, false);
\r
316 int set_expand_string(HKEY key, TCHAR *value, TCHAR *string) {
\r
317 return set_string(key, value, string, true);
\r
322 Set an unsigned long in the registry.
\r
323 Returns: 0 if it was set.
\r
326 int set_number(HKEY key, TCHAR *value, unsigned long number) {
\r
327 if (RegSetValueEx(key, value, 0, REG_DWORD, (const unsigned char *) &number, sizeof(number)) == ERROR_SUCCESS) return 0;
\r
328 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
333 Query an unsigned long from the registry.
\r
334 Returns: 1 if a number was retrieved.
\r
335 0 if none was found and must_exist is false.
\r
336 -1 if none was found and must_exist is true.
\r
339 int get_number(HKEY key, TCHAR *value, unsigned long *number, bool must_exist) {
\r
340 unsigned long type = REG_DWORD;
\r
341 unsigned long number_len = sizeof(unsigned long);
\r
343 int ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) number, &number_len);
\r
344 if (ret == ERROR_SUCCESS) return 1;
\r
346 if (ret == ERROR_FILE_NOT_FOUND) {
\r
347 if (! must_exist) return 0;
\r
350 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
351 if (ret == ERROR_FILE_NOT_FOUND) return -1;
\r
356 int get_number(HKEY key, TCHAR *value, unsigned long *number) {
\r
357 return get_number(key, value, number, true);
\r
360 /* Replace NULL with CRLF. Leave NULL NULL as the end marker. */
\r
361 int format_double_null(TCHAR *dn, unsigned long dnlen, TCHAR **formatted, unsigned long *newlen) {
\r
362 unsigned long i, j;
\r
370 for (i = 0; i < dnlen; i++) if (! dn[i] && dn[i + 1]) ++*newlen;
\r
372 *formatted = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *newlen * sizeof(TCHAR));
\r
373 if (! *formatted) {
\r
378 for (i = 0, j = 0; i < dnlen; i++) {
\r
379 (*formatted)[j] = dn[i];
\r
382 (*formatted)[j] = _T('\r');
\r
383 (*formatted)[++j] = _T('\n');
\r
392 /* Strip CR and replace LF with NULL. */
\r
393 int unformat_double_null(TCHAR *dn, unsigned long dnlen, TCHAR **unformatted, unsigned long *newlen) {
\r
394 unsigned long i, j;
\r
402 for (i = 0; i < dnlen; i++) if (dn[i] != _T('\r')) ++*newlen;
\r
404 /* Skip blank lines. */
\r
405 for (i = 0; i < dnlen; i++) {
\r
406 if (dn[i] == _T('\r') && dn[i + 1] == _T('\n')) {
\r
407 /* This is the last CRLF. */
\r
408 if (i >= dnlen - 2) break;
\r
411 Strip at the start of the block or if the next characters are
\r
414 if (! i || (dn[i + 2] == _T('\r') && dn[i + 3] == _T('\n'))) {
\r
415 for (j = i + 2; j < dnlen; j++) dn[j - 2] = dn[j];
\r
416 dn[dnlen--] = _T('\0');
\r
417 dn[dnlen--] = _T('\0');
\r
424 /* Must end with two NULLs. */
\r
427 *unformatted = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *newlen * sizeof(TCHAR));
\r
428 if (! *unformatted) return 1;
\r
430 for (i = 0, j = 0; i < dnlen; i++) {
\r
431 if (dn[i] == _T('\r')) continue;
\r
432 if (dn[i] == _T('\n')) (*unformatted)[j] = _T('\0');
\r
433 else (*unformatted)[j] = dn[i];
\r
440 void override_milliseconds(TCHAR *service_name, HKEY key, TCHAR *value, unsigned long *buffer, unsigned long default_value, unsigned long event) {
\r
441 unsigned long type = REG_DWORD;
\r
442 unsigned long buflen = sizeof(unsigned long);
\r
444 unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);
\r
445 if (ret != ERROR_SUCCESS) {
\r
446 if (ret != ERROR_FILE_NOT_FOUND) {
\r
447 if (type != REG_DWORD) {
\r
448 TCHAR milliseconds[16];
\r
449 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), default_value);
\r
450 log_event(EVENTLOG_WARNING_TYPE, event, service_name, value, milliseconds, 0);
\r
452 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
457 if (! ok) *buffer = default_value;
\r
460 HKEY open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam) {
\r
462 TCHAR registry[KEY_LENGTH];
\r
466 if (sub) ret = _sntprintf_s(registry, _countof(registry), _TRUNCATE, NSSM_REGISTRY _T("\\%s"), service_name, sub);
\r
467 else ret = _sntprintf_s(registry, _countof(registry), _TRUNCATE, NSSM_REGISTRY, service_name);
\r
469 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("NSSM_REGISTRY"), _T("open_registry()"), 0);
\r
473 if (sam & KEY_WRITE) {
\r
474 if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, sam, 0, &key, 0) != ERROR_SUCCESS) {
\r
475 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
480 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, registry, 0, sam, &key) != ERROR_SUCCESS) {
\r
481 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
489 HKEY open_registry(const TCHAR *service_name, REGSAM sam) {
\r
490 return open_registry(service_name, 0, sam);
\r
493 int get_io_parameters(nssm_service_t *service, HKEY key) {
\r
495 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, 0)) {
\r
496 service->stdin_sharing = service->stdin_disposition = service->stdin_flags = 0;
\r
497 ZeroMemory(service->stdin_path, _countof(service->stdin_path) * sizeof(TCHAR));
\r
502 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, &service->stdout_copy_and_truncate)) {
\r
503 service->stdout_sharing = service->stdout_disposition = service->stdout_flags = 0;
\r
504 ZeroMemory(service->stdout_path, _countof(service->stdout_path) * sizeof(TCHAR));
\r
509 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, &service->stderr_copy_and_truncate)) {
\r
510 service->stderr_sharing = service->stderr_disposition = service->stderr_flags = 0;
\r
511 ZeroMemory(service->stderr_path, _countof(service->stderr_path) * sizeof(TCHAR));
\r
518 int get_parameters(nssm_service_t *service, STARTUPINFO *si) {
\r
521 /* Try to open the registry */
\r
522 HKEY key = open_registry(service->name, KEY_READ);
\r
523 if (! key) return 1;
\r
525 /* Don't expand parameters when retrieving for the GUI. */
\r
526 bool expand = si ? true : false;
\r
528 /* Try to get executable file - MUST succeed */
\r
529 if (get_string(key, NSSM_REG_EXE, service->exe, sizeof(service->exe), expand, false, true)) {
\r
534 /* Try to get flags - may fail and we don't care */
\r
535 if (get_string(key, NSSM_REG_FLAGS, service->flags, sizeof(service->flags), expand, false, true)) {
\r
536 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_FLAGS, NSSM_REG_FLAGS, service->name, service->exe, 0);
\r
537 ZeroMemory(service->flags, sizeof(service->flags));
\r
540 /* Try to get startup directory - may fail and we fall back to a default */
\r
541 if (get_string(key, NSSM_REG_DIR, service->dir, sizeof(service->dir), expand, true, true) || ! service->dir[0]) {
\r
542 _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);
\r
543 strip_basename(service->dir);
\r
544 if (service->dir[0] == _T('\0')) {
\r
546 ret = GetWindowsDirectory(service->dir, sizeof(service->dir));
\r
547 if (! ret || ret > sizeof(service->dir)) {
\r
548 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_NO_DIR_AND_NO_FALLBACK, NSSM_REG_DIR, service->name, 0);
\r
553 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_DIR, NSSM_REG_DIR, service->name, service->dir, 0);
\r
556 /* Try to get processor affinity - may fail. */
\r
558 if (get_string(key, NSSM_REG_AFFINITY, buffer, sizeof(buffer), false, false, false) || ! buffer[0]) service->affinity = 0LL;
\r
559 else if (affinity_string_to_mask(buffer, &service->affinity)) {
\r
560 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_AFFINITY_MASK, service->name, buffer);
\r
561 service->affinity = 0LL;
\r
564 DWORD_PTR affinity, system_affinity;
\r
566 if (GetProcessAffinityMask(GetCurrentProcess(), &affinity, &system_affinity)) {
\r
567 _int64 effective_affinity = service->affinity & system_affinity;
\r
568 if (effective_affinity != service->affinity) {
\r
570 if (! affinity_mask_to_string(system_affinity, &system)) {
\r
571 TCHAR *effective = 0;
\r
572 if (! affinity_mask_to_string(effective_affinity, &effective)) {
\r
573 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_EFFECTIVE_AFFINITY_MASK, service->name, buffer, system, effective, 0);
\r
575 HeapFree(GetProcessHeap(), 0, effective);
\r
577 HeapFree(GetProcessHeap(), 0, system);
\r
582 /* Try to get environment variables - may fail */
\r
583 get_environment(service->name, key, NSSM_REG_ENV, &service->env, &service->envlen);
\r
584 /* Environment variables to add to existing rather than replace - may fail. */
\r
585 get_environment(service->name, key, NSSM_REG_ENV_EXTRA, &service->env_extra, &service->env_extralen);
\r
587 /* Try to get priority - may fail. */
\r
588 unsigned long priority;
\r
589 if (get_number(key, NSSM_REG_PRIORITY, &priority, false) == 1) {
\r
590 if (priority == (priority & priority_mask())) service->priority = priority;
\r
591 else log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_PRIORITY, service->name, NSSM_REG_PRIORITY, 0);
\r
594 /* Try to get file rotation settings - may fail. */
\r
595 unsigned long rotate_files;
\r
596 if (get_number(key, NSSM_REG_ROTATE, &rotate_files, false) == 1) {
\r
597 if (rotate_files) service->rotate_files = true;
\r
598 else service->rotate_files = false;
\r
600 else service->rotate_files = false;
\r
601 if (get_number(key, NSSM_REG_ROTATE_ONLINE, &rotate_files, false) == 1) {
\r
602 if (rotate_files) service->rotate_stdout_online = service->rotate_stderr_online = true;
\r
603 else service->rotate_stdout_online = service->rotate_stderr_online = false;
\r
605 else service->rotate_stdout_online = service->rotate_stderr_online = false;
\r
606 if (get_number(key, NSSM_REG_ROTATE_SECONDS, &service->rotate_seconds, false) != 1) service->rotate_seconds = 0;
\r
607 if (get_number(key, NSSM_REG_ROTATE_BYTES_LOW, &service->rotate_bytes_low, false) != 1) service->rotate_bytes_low = 0;
\r
608 if (get_number(key, NSSM_REG_ROTATE_BYTES_HIGH, &service->rotate_bytes_high, false) != 1) service->rotate_bytes_high = 0;
\r
609 override_milliseconds(service->name, key, NSSM_REG_ROTATE_DELAY, &service->rotate_delay, NSSM_ROTATE_DELAY, NSSM_EVENT_BOGUS_THROTTLE);
\r
611 /* Try to get force new console setting - may fail. */
\r
612 if (get_number(key, NSSM_REG_NO_CONSOLE, &service->no_console, false) != 1) service->no_console = 0;
\r
614 /* Change to startup directory in case stdout/stderr are relative paths. */
\r
615 TCHAR cwd[PATH_LENGTH];
\r
616 GetCurrentDirectory(_countof(cwd), cwd);
\r
617 SetCurrentDirectory(service->dir);
\r
619 /* Try to get stdout and stderr */
\r
620 if (get_io_parameters(service, key)) {
\r
621 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);
\r
623 SetCurrentDirectory(cwd);
\r
627 /* Change back in case the startup directory needs to be deleted. */
\r
628 SetCurrentDirectory(cwd);
\r
630 /* Try to get mandatory restart delay */
\r
631 override_milliseconds(service->name, key, NSSM_REG_RESTART_DELAY, &service->restart_delay, 0, NSSM_EVENT_BOGUS_RESTART_DELAY);
\r
633 /* Try to get throttle restart delay */
\r
634 override_milliseconds(service->name, key, NSSM_REG_THROTTLE, &service->throttle_delay, NSSM_RESET_THROTTLE_RESTART, NSSM_EVENT_BOGUS_THROTTLE);
\r
636 /* Try to get service stop flags. */
\r
637 unsigned long type = REG_DWORD;
\r
638 unsigned long stop_method_skip;
\r
639 unsigned long buflen = sizeof(stop_method_skip);
\r
640 bool stop_ok = false;
\r
641 ret = RegQueryValueEx(key, NSSM_REG_STOP_METHOD_SKIP, 0, &type, (unsigned char *) &stop_method_skip, &buflen);
\r
642 if (ret != ERROR_SUCCESS) {
\r
643 if (ret != ERROR_FILE_NOT_FOUND) {
\r
644 if (type != REG_DWORD) {
\r
645 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_STOP_METHOD_SKIP, service->name, NSSM_REG_STOP_METHOD_SKIP, NSSM, 0);
\r
647 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, NSSM_REG_STOP_METHOD_SKIP, error_string(GetLastError()), 0);
\r
650 else stop_ok = true;
\r
652 /* Try all methods except those requested to be skipped. */
\r
653 service->stop_method = ~0;
\r
654 if (stop_ok) service->stop_method &= ~stop_method_skip;
\r
656 /* Try to get kill delays - may fail. */
\r
657 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
658 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
659 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
661 /* Try to get process tree settings - may fail. */
\r
662 unsigned long kill_process_tree;
\r
663 if (get_number(key, NSSM_REG_KILL_PROCESS_TREE, &kill_process_tree, false) == 1) {
\r
664 if (kill_process_tree) service->kill_process_tree = true;
\r
665 else service->kill_process_tree = false;
\r
667 else service->kill_process_tree = true;
\r
669 /* Try to get default exit action. */
\r
670 bool default_action;
\r
671 service->default_exit_action = NSSM_EXIT_RESTART;
\r
672 TCHAR action_string[ACTION_LEN];
\r
673 if (! get_exit_action(service->name, 0, action_string, &default_action)) {
\r
674 for (int i = 0; exit_action_strings[i]; i++) {
\r
675 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
676 service->default_exit_action = i;
\r
682 /* Close registry */
\r
689 Sets the string for the exit action corresponding to the exit code.
\r
691 ret is a pointer to an unsigned long containing the exit code.
\r
692 If ret is NULL, we retrieve the default exit action unconditionally.
\r
694 action is a buffer which receives the string.
\r
696 default_action is a pointer to a bool which is set to false if there
\r
697 was an explicit string for the given exit code, or true if we are
\r
698 returning the default action.
\r
700 Returns: 0 on success.
\r
703 int get_exit_action(const TCHAR *service_name, unsigned long *ret, TCHAR *action, bool *default_action) {
\r
704 /* Are we returning the default action or a status-specific one? */
\r
705 *default_action = ! ret;
\r
707 /* Try to open the registry */
\r
708 HKEY key = open_registry(service_name, NSSM_REG_EXIT, KEY_READ);
\r
709 if (! key) return 1;
\r
711 unsigned long type = REG_SZ;
\r
712 unsigned long action_len = ACTION_LEN;
\r
715 if (! ret) code[0] = _T('\0');
\r
716 else if (_sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), *ret) < 0) {
\r
718 return get_exit_action(service_name, 0, action, default_action);
\r
720 if (RegQueryValueEx(key, code, 0, &type, (unsigned char *) action, &action_len) != ERROR_SUCCESS) {
\r
722 /* Try again with * as the key if an exit code was defined */
\r
723 if (ret) return get_exit_action(service_name, 0, action, default_action);
\r
727 /* Close registry */
\r