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, bool must_exist) {
\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_SET_VALUE) {
\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 long error = RegOpenKeyEx(HKEY_LOCAL_MACHINE, registry, 0, sam, &key);
\r
481 if (error != ERROR_SUCCESS) {
\r
482 if (error == ERROR_FILE_NOT_FOUND && ! must_exist) return 0;
\r
483 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
491 HKEY open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam) {
\r
492 return open_registry(service_name, sub, sam, true);
\r
495 HKEY open_registry(const TCHAR *service_name, REGSAM sam) {
\r
496 return open_registry(service_name, 0, sam, true);
\r
499 int get_io_parameters(nssm_service_t *service, HKEY key) {
\r
501 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
502 service->stdin_sharing = service->stdin_disposition = service->stdin_flags = 0;
\r
503 ZeroMemory(service->stdin_path, _countof(service->stdin_path) * sizeof(TCHAR));
\r
508 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
509 service->stdout_sharing = service->stdout_disposition = service->stdout_flags = 0;
\r
510 ZeroMemory(service->stdout_path, _countof(service->stdout_path) * sizeof(TCHAR));
\r
515 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
516 service->stderr_sharing = service->stderr_disposition = service->stderr_flags = 0;
\r
517 ZeroMemory(service->stderr_path, _countof(service->stderr_path) * sizeof(TCHAR));
\r
524 int get_parameters(nssm_service_t *service, STARTUPINFO *si) {
\r
527 /* Try to open the registry */
\r
528 HKEY key = open_registry(service->name, KEY_READ);
\r
529 if (! key) return 1;
\r
531 /* Don't expand parameters when retrieving for the GUI. */
\r
532 bool expand = si ? true : false;
\r
534 /* Try to get executable file - MUST succeed */
\r
535 if (get_string(key, NSSM_REG_EXE, service->exe, sizeof(service->exe), expand, false, true)) {
\r
540 /* Try to get flags - may fail and we don't care */
\r
541 if (get_string(key, NSSM_REG_FLAGS, service->flags, sizeof(service->flags), expand, false, true)) {
\r
542 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_FLAGS, NSSM_REG_FLAGS, service->name, service->exe, 0);
\r
543 ZeroMemory(service->flags, sizeof(service->flags));
\r
546 /* Try to get startup directory - may fail and we fall back to a default */
\r
547 if (get_string(key, NSSM_REG_DIR, service->dir, sizeof(service->dir), expand, true, true) || ! service->dir[0]) {
\r
548 _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);
\r
549 strip_basename(service->dir);
\r
550 if (service->dir[0] == _T('\0')) {
\r
552 ret = GetWindowsDirectory(service->dir, sizeof(service->dir));
\r
553 if (! ret || ret > sizeof(service->dir)) {
\r
554 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_NO_DIR_AND_NO_FALLBACK, NSSM_REG_DIR, service->name, 0);
\r
559 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_DIR, NSSM_REG_DIR, service->name, service->dir, 0);
\r
562 /* Try to get processor affinity - may fail. */
\r
564 if (get_string(key, NSSM_REG_AFFINITY, buffer, sizeof(buffer), false, false, false) || ! buffer[0]) service->affinity = 0LL;
\r
565 else if (affinity_string_to_mask(buffer, &service->affinity)) {
\r
566 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_AFFINITY_MASK, service->name, buffer);
\r
567 service->affinity = 0LL;
\r
570 DWORD_PTR affinity, system_affinity;
\r
572 if (GetProcessAffinityMask(GetCurrentProcess(), &affinity, &system_affinity)) {
\r
573 _int64 effective_affinity = service->affinity & system_affinity;
\r
574 if (effective_affinity != service->affinity) {
\r
576 if (! affinity_mask_to_string(system_affinity, &system)) {
\r
577 TCHAR *effective = 0;
\r
578 if (! affinity_mask_to_string(effective_affinity, &effective)) {
\r
579 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_EFFECTIVE_AFFINITY_MASK, service->name, buffer, system, effective, 0);
\r
581 HeapFree(GetProcessHeap(), 0, effective);
\r
583 HeapFree(GetProcessHeap(), 0, system);
\r
588 /* Try to get environment variables - may fail */
\r
589 get_environment(service->name, key, NSSM_REG_ENV, &service->env, &service->envlen);
\r
590 /* Environment variables to add to existing rather than replace - may fail. */
\r
591 get_environment(service->name, key, NSSM_REG_ENV_EXTRA, &service->env_extra, &service->env_extralen);
\r
593 /* Try to get priority - may fail. */
\r
594 unsigned long priority;
\r
595 if (get_number(key, NSSM_REG_PRIORITY, &priority, false) == 1) {
\r
596 if (priority == (priority & priority_mask())) service->priority = priority;
\r
597 else log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_PRIORITY, service->name, NSSM_REG_PRIORITY, 0);
\r
600 /* Try to get file rotation settings - may fail. */
\r
601 unsigned long rotate_files;
\r
602 if (get_number(key, NSSM_REG_ROTATE, &rotate_files, false) == 1) {
\r
603 if (rotate_files) service->rotate_files = true;
\r
604 else service->rotate_files = false;
\r
606 else service->rotate_files = false;
\r
607 if (get_number(key, NSSM_REG_ROTATE_ONLINE, &rotate_files, false) == 1) {
\r
608 if (rotate_files) service->rotate_stdout_online = service->rotate_stderr_online = true;
\r
609 else service->rotate_stdout_online = service->rotate_stderr_online = false;
\r
611 else service->rotate_stdout_online = service->rotate_stderr_online = false;
\r
612 if (get_number(key, NSSM_REG_ROTATE_SECONDS, &service->rotate_seconds, false) != 1) service->rotate_seconds = 0;
\r
613 if (get_number(key, NSSM_REG_ROTATE_BYTES_LOW, &service->rotate_bytes_low, false) != 1) service->rotate_bytes_low = 0;
\r
614 if (get_number(key, NSSM_REG_ROTATE_BYTES_HIGH, &service->rotate_bytes_high, false) != 1) service->rotate_bytes_high = 0;
\r
615 override_milliseconds(service->name, key, NSSM_REG_ROTATE_DELAY, &service->rotate_delay, NSSM_ROTATE_DELAY, NSSM_EVENT_BOGUS_THROTTLE);
\r
617 /* Try to get force new console setting - may fail. */
\r
618 if (get_number(key, NSSM_REG_NO_CONSOLE, &service->no_console, false) != 1) service->no_console = 0;
\r
620 /* Change to startup directory in case stdout/stderr are relative paths. */
\r
621 TCHAR cwd[PATH_LENGTH];
\r
622 GetCurrentDirectory(_countof(cwd), cwd);
\r
623 SetCurrentDirectory(service->dir);
\r
625 /* Try to get stdout and stderr */
\r
626 if (get_io_parameters(service, key)) {
\r
627 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);
\r
629 SetCurrentDirectory(cwd);
\r
633 /* Change back in case the startup directory needs to be deleted. */
\r
634 SetCurrentDirectory(cwd);
\r
636 /* Try to get mandatory restart delay */
\r
637 override_milliseconds(service->name, key, NSSM_REG_RESTART_DELAY, &service->restart_delay, 0, NSSM_EVENT_BOGUS_RESTART_DELAY);
\r
639 /* Try to get throttle restart delay */
\r
640 override_milliseconds(service->name, key, NSSM_REG_THROTTLE, &service->throttle_delay, NSSM_RESET_THROTTLE_RESTART, NSSM_EVENT_BOGUS_THROTTLE);
\r
642 /* Try to get service stop flags. */
\r
643 unsigned long type = REG_DWORD;
\r
644 unsigned long stop_method_skip;
\r
645 unsigned long buflen = sizeof(stop_method_skip);
\r
646 bool stop_ok = false;
\r
647 ret = RegQueryValueEx(key, NSSM_REG_STOP_METHOD_SKIP, 0, &type, (unsigned char *) &stop_method_skip, &buflen);
\r
648 if (ret != ERROR_SUCCESS) {
\r
649 if (ret != ERROR_FILE_NOT_FOUND) {
\r
650 if (type != REG_DWORD) {
\r
651 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_STOP_METHOD_SKIP, service->name, NSSM_REG_STOP_METHOD_SKIP, NSSM, 0);
\r
653 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, NSSM_REG_STOP_METHOD_SKIP, error_string(GetLastError()), 0);
\r
656 else stop_ok = true;
\r
658 /* Try all methods except those requested to be skipped. */
\r
659 service->stop_method = ~0;
\r
660 if (stop_ok) service->stop_method &= ~stop_method_skip;
\r
662 /* Try to get kill delays - may fail. */
\r
663 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
664 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
665 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
667 /* Try to get process tree settings - may fail. */
\r
668 unsigned long kill_process_tree;
\r
669 if (get_number(key, NSSM_REG_KILL_PROCESS_TREE, &kill_process_tree, false) == 1) {
\r
670 if (kill_process_tree) service->kill_process_tree = true;
\r
671 else service->kill_process_tree = false;
\r
673 else service->kill_process_tree = true;
\r
675 /* Try to get default exit action. */
\r
676 bool default_action;
\r
677 service->default_exit_action = NSSM_EXIT_RESTART;
\r
678 TCHAR action_string[ACTION_LEN];
\r
679 if (! get_exit_action(service->name, 0, action_string, &default_action)) {
\r
680 for (int i = 0; exit_action_strings[i]; i++) {
\r
681 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
682 service->default_exit_action = i;
\r
688 /* Close registry */
\r
695 Sets the string for the exit action corresponding to the exit code.
\r
697 ret is a pointer to an unsigned long containing the exit code.
\r
698 If ret is NULL, we retrieve the default exit action unconditionally.
\r
700 action is a buffer which receives the string.
\r
702 default_action is a pointer to a bool which is set to false if there
\r
703 was an explicit string for the given exit code, or true if we are
\r
704 returning the default action.
\r
706 Returns: 0 on success.
\r
709 int get_exit_action(const TCHAR *service_name, unsigned long *ret, TCHAR *action, bool *default_action) {
\r
710 /* Are we returning the default action or a status-specific one? */
\r
711 *default_action = ! ret;
\r
713 /* Try to open the registry */
\r
714 HKEY key = open_registry(service_name, NSSM_REG_EXIT, KEY_READ);
\r
715 if (! key) return 1;
\r
717 unsigned long type = REG_SZ;
\r
718 unsigned long action_len = ACTION_LEN;
\r
721 if (! ret) code[0] = _T('\0');
\r
722 else if (_sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), *ret) < 0) {
\r
724 return get_exit_action(service_name, 0, action, default_action);
\r
726 if (RegQueryValueEx(key, code, 0, &type, (unsigned char *) action, &action_len) != ERROR_SUCCESS) {
\r
728 /* Try again with * as the key if an exit code was defined */
\r
729 if (ret) return get_exit_action(service_name, 0, action, default_action);
\r
733 /* Close registry */
\r
739 int set_hook(const TCHAR *service_name, const TCHAR *hook_event, const TCHAR *hook_action, TCHAR *cmd) {
\r
740 /* Try to open the registry */
\r
741 TCHAR registry[KEY_LENGTH];
\r
742 if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, _T("%s\\%s"), NSSM_REG_HOOK, hook_event) < 0) {
\r
743 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("hook registry"), _T("set_hook()"), 0);
\r
750 /* Don't create keys needlessly. */
\r
751 if (! _tcslen(cmd)) {
\r
752 key = open_registry(service_name, registry, KEY_READ, false);
\r
753 if (! key) return 0;
\r
754 error = RegQueryValueEx(key, hook_action, 0, 0, 0, 0);
\r
756 if (error == ERROR_FILE_NOT_FOUND) return 0;
\r
759 key = open_registry(service_name, registry, KEY_WRITE);
\r
760 if (! key) return 1;
\r
763 if (_tcslen(cmd)) ret = set_string(key, (TCHAR *) hook_action, cmd, true);
\r
765 error = RegDeleteValue(key, hook_action);
\r
766 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) ret = 0;
\r
769 /* Close registry */
\r
775 int get_hook(const TCHAR *service_name, const TCHAR *hook_event, const TCHAR *hook_action, TCHAR *buffer, unsigned long buflen) {
\r
776 /* Try to open the registry */
\r
777 TCHAR registry[KEY_LENGTH];
\r
778 if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, _T("%s\\%s"), NSSM_REG_HOOK, hook_event) < 0) {
\r
779 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("hook registry"), _T("get_hook()"), 0);
\r
782 HKEY key = open_registry(service_name, registry, KEY_READ, false);
\r
783 if (! key) return 1;
\r
785 int ret = expand_parameter(key, (TCHAR *) hook_action, buffer, buflen, true, false);
\r
787 /* Close registry */
\r