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 environment variables - may fail */
\r
535 get_environment(service->name, key, NSSM_REG_ENV, &service->env, &service->envlen);
\r
536 /* Environment variables to add to existing rather than replace - may fail. */
\r
537 get_environment(service->name, key, NSSM_REG_ENV_EXTRA, &service->env_extra, &service->env_extralen);
\r
539 /* Set environment if we are starting the service. */
\r
540 if (si) set_service_environment(service);
\r
542 /* Try to get executable file - MUST succeed */
\r
543 if (get_string(key, NSSM_REG_EXE, service->exe, sizeof(service->exe), expand, false, true)) {
\r
548 /* Try to get flags - may fail and we don't care */
\r
549 if (get_string(key, NSSM_REG_FLAGS, service->flags, sizeof(service->flags), expand, false, true)) {
\r
550 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_FLAGS, NSSM_REG_FLAGS, service->name, service->exe, 0);
\r
551 ZeroMemory(service->flags, sizeof(service->flags));
\r
554 /* Try to get startup directory - may fail and we fall back to a default */
\r
555 if (get_string(key, NSSM_REG_DIR, service->dir, sizeof(service->dir), expand, true, true) || ! service->dir[0]) {
\r
556 _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);
\r
557 strip_basename(service->dir);
\r
558 if (service->dir[0] == _T('\0')) {
\r
560 ret = GetWindowsDirectory(service->dir, sizeof(service->dir));
\r
561 if (! ret || ret > sizeof(service->dir)) {
\r
562 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_NO_DIR_AND_NO_FALLBACK, NSSM_REG_DIR, service->name, 0);
\r
567 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_DIR, NSSM_REG_DIR, service->name, service->dir, 0);
\r
570 /* Try to get processor affinity - may fail. */
\r
572 if (get_string(key, NSSM_REG_AFFINITY, buffer, sizeof(buffer), false, false, false) || ! buffer[0]) service->affinity = 0LL;
\r
573 else if (affinity_string_to_mask(buffer, &service->affinity)) {
\r
574 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_AFFINITY_MASK, service->name, buffer);
\r
575 service->affinity = 0LL;
\r
578 DWORD_PTR affinity, system_affinity;
\r
580 if (GetProcessAffinityMask(GetCurrentProcess(), &affinity, &system_affinity)) {
\r
581 _int64 effective_affinity = service->affinity & system_affinity;
\r
582 if (effective_affinity != service->affinity) {
\r
584 if (! affinity_mask_to_string(system_affinity, &system)) {
\r
585 TCHAR *effective = 0;
\r
586 if (! affinity_mask_to_string(effective_affinity, &effective)) {
\r
587 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_EFFECTIVE_AFFINITY_MASK, service->name, buffer, system, effective, 0);
\r
589 HeapFree(GetProcessHeap(), 0, effective);
\r
591 HeapFree(GetProcessHeap(), 0, system);
\r
596 /* Try to get priority - may fail. */
\r
597 unsigned long priority;
\r
598 if (get_number(key, NSSM_REG_PRIORITY, &priority, false) == 1) {
\r
599 if (priority == (priority & priority_mask())) service->priority = priority;
\r
600 else log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_PRIORITY, service->name, NSSM_REG_PRIORITY, 0);
\r
603 /* Try to get file rotation settings - may fail. */
\r
604 unsigned long rotate_files;
\r
605 if (get_number(key, NSSM_REG_ROTATE, &rotate_files, false) == 1) {
\r
606 if (rotate_files) service->rotate_files = true;
\r
607 else service->rotate_files = false;
\r
609 else service->rotate_files = false;
\r
610 if (get_number(key, NSSM_REG_ROTATE_ONLINE, &rotate_files, false) == 1) {
\r
611 if (rotate_files) service->rotate_stdout_online = service->rotate_stderr_online = true;
\r
612 else service->rotate_stdout_online = service->rotate_stderr_online = false;
\r
614 else service->rotate_stdout_online = service->rotate_stderr_online = false;
\r
615 if (get_number(key, NSSM_REG_ROTATE_SECONDS, &service->rotate_seconds, false) != 1) service->rotate_seconds = 0;
\r
616 if (get_number(key, NSSM_REG_ROTATE_BYTES_LOW, &service->rotate_bytes_low, false) != 1) service->rotate_bytes_low = 0;
\r
617 if (get_number(key, NSSM_REG_ROTATE_BYTES_HIGH, &service->rotate_bytes_high, false) != 1) service->rotate_bytes_high = 0;
\r
618 override_milliseconds(service->name, key, NSSM_REG_ROTATE_DELAY, &service->rotate_delay, NSSM_ROTATE_DELAY, NSSM_EVENT_BOGUS_THROTTLE);
\r
620 /* Try to get force new console setting - may fail. */
\r
621 if (get_number(key, NSSM_REG_NO_CONSOLE, &service->no_console, false) != 1) service->no_console = 0;
\r
623 /* Change to startup directory in case stdout/stderr are relative paths. */
\r
624 TCHAR cwd[PATH_LENGTH];
\r
625 GetCurrentDirectory(_countof(cwd), cwd);
\r
626 SetCurrentDirectory(service->dir);
\r
628 /* Try to get stdout and stderr */
\r
629 if (get_io_parameters(service, key)) {
\r
630 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);
\r
632 SetCurrentDirectory(cwd);
\r
636 /* Change back in case the startup directory needs to be deleted. */
\r
637 SetCurrentDirectory(cwd);
\r
639 /* Try to get mandatory restart delay */
\r
640 override_milliseconds(service->name, key, NSSM_REG_RESTART_DELAY, &service->restart_delay, 0, NSSM_EVENT_BOGUS_RESTART_DELAY);
\r
642 /* Try to get throttle restart delay */
\r
643 override_milliseconds(service->name, key, NSSM_REG_THROTTLE, &service->throttle_delay, NSSM_RESET_THROTTLE_RESTART, NSSM_EVENT_BOGUS_THROTTLE);
\r
645 /* Try to get service stop flags. */
\r
646 unsigned long type = REG_DWORD;
\r
647 unsigned long stop_method_skip;
\r
648 unsigned long buflen = sizeof(stop_method_skip);
\r
649 bool stop_ok = false;
\r
650 ret = RegQueryValueEx(key, NSSM_REG_STOP_METHOD_SKIP, 0, &type, (unsigned char *) &stop_method_skip, &buflen);
\r
651 if (ret != ERROR_SUCCESS) {
\r
652 if (ret != ERROR_FILE_NOT_FOUND) {
\r
653 if (type != REG_DWORD) {
\r
654 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_STOP_METHOD_SKIP, service->name, NSSM_REG_STOP_METHOD_SKIP, NSSM, 0);
\r
656 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, NSSM_REG_STOP_METHOD_SKIP, error_string(GetLastError()), 0);
\r
659 else stop_ok = true;
\r
661 /* Try all methods except those requested to be skipped. */
\r
662 service->stop_method = ~0;
\r
663 if (stop_ok) service->stop_method &= ~stop_method_skip;
\r
665 /* Try to get kill delays - may fail. */
\r
666 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
667 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
668 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
670 /* Try to get process tree settings - may fail. */
\r
671 unsigned long kill_process_tree;
\r
672 if (get_number(key, NSSM_REG_KILL_PROCESS_TREE, &kill_process_tree, false) == 1) {
\r
673 if (kill_process_tree) service->kill_process_tree = true;
\r
674 else service->kill_process_tree = false;
\r
676 else service->kill_process_tree = true;
\r
678 /* Try to get default exit action. */
\r
679 bool default_action;
\r
680 service->default_exit_action = NSSM_EXIT_RESTART;
\r
681 TCHAR action_string[ACTION_LEN];
\r
682 if (! get_exit_action(service->name, 0, action_string, &default_action)) {
\r
683 for (int i = 0; exit_action_strings[i]; i++) {
\r
684 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
685 service->default_exit_action = i;
\r
691 /* Close registry */
\r
698 Sets the string for the exit action corresponding to the exit code.
\r
700 ret is a pointer to an unsigned long containing the exit code.
\r
701 If ret is NULL, we retrieve the default exit action unconditionally.
\r
703 action is a buffer which receives the string.
\r
705 default_action is a pointer to a bool which is set to false if there
\r
706 was an explicit string for the given exit code, or true if we are
\r
707 returning the default action.
\r
709 Returns: 0 on success.
\r
712 int get_exit_action(const TCHAR *service_name, unsigned long *ret, TCHAR *action, bool *default_action) {
\r
713 /* Are we returning the default action or a status-specific one? */
\r
714 *default_action = ! ret;
\r
716 /* Try to open the registry */
\r
717 HKEY key = open_registry(service_name, NSSM_REG_EXIT, KEY_READ);
\r
718 if (! key) return 1;
\r
720 unsigned long type = REG_SZ;
\r
721 unsigned long action_len = ACTION_LEN;
\r
724 if (! ret) code[0] = _T('\0');
\r
725 else if (_sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), *ret) < 0) {
\r
727 return get_exit_action(service_name, 0, action, default_action);
\r
729 if (RegQueryValueEx(key, code, 0, &type, (unsigned char *) action, &action_len) != ERROR_SUCCESS) {
\r
731 /* Try again with * as the key if an exit code was defined */
\r
732 if (ret) return get_exit_action(service_name, 0, action, default_action);
\r
736 /* Close registry */
\r
742 int set_hook(const TCHAR *service_name, const TCHAR *hook_event, const TCHAR *hook_action, TCHAR *cmd) {
\r
743 /* Try to open the registry */
\r
744 TCHAR registry[KEY_LENGTH];
\r
745 if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, _T("%s\\%s"), NSSM_REG_HOOK, hook_event) < 0) {
\r
746 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("hook registry"), _T("set_hook()"), 0);
\r
753 /* Don't create keys needlessly. */
\r
754 if (! _tcslen(cmd)) {
\r
755 key = open_registry(service_name, registry, KEY_READ, false);
\r
756 if (! key) return 0;
\r
757 error = RegQueryValueEx(key, hook_action, 0, 0, 0, 0);
\r
759 if (error == ERROR_FILE_NOT_FOUND) return 0;
\r
762 key = open_registry(service_name, registry, KEY_WRITE);
\r
763 if (! key) return 1;
\r
766 if (_tcslen(cmd)) ret = set_string(key, (TCHAR *) hook_action, cmd, true);
\r
768 error = RegDeleteValue(key, hook_action);
\r
769 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) ret = 0;
\r
772 /* Close registry */
\r
778 int get_hook(const TCHAR *service_name, const TCHAR *hook_event, const TCHAR *hook_action, TCHAR *buffer, unsigned long buflen) {
\r
779 /* Try to open the registry */
\r
780 TCHAR registry[KEY_LENGTH];
\r
781 if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, _T("%s\\%s"), NSSM_REG_HOOK, hook_event) < 0) {
\r
782 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("hook registry"), _T("get_hook()"), 0);
\r
785 HKEY key = open_registry(service_name, registry, KEY_READ, false);
\r
786 if (! key) return 1;
\r
788 int ret = expand_parameter(key, (TCHAR *) hook_action, buffer, buflen, true, false);
\r
790 /* Close registry */
\r