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 const TCHAR *path = nssm_imagepath();
\r
22 /* Try to register the module but don't worry so much on failure */
\r
23 RegSetValueEx(key, _T("EventMessageFile"), 0, REG_SZ, (const unsigned char *) path, (unsigned long) (_tcslen(path) + 1) * sizeof(TCHAR));
\r
24 unsigned long types = EVENTLOG_INFORMATION_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_ERROR_TYPE;
\r
25 RegSetValueEx(key, _T("TypesSupported"), 0, REG_DWORD, (const unsigned char *) &types, sizeof(types));
\r
30 int create_parameters(nssm_service_t *service, bool editing) {
\r
31 /* Try to open the registry */
\r
32 HKEY key = open_registry(service->name, KEY_WRITE);
\r
33 if (! key) return 1;
\r
35 /* Try to create the parameters */
\r
36 if (set_expand_string(key, NSSM_REG_EXE, service->exe)) {
\r
37 RegDeleteKey(HKEY_LOCAL_MACHINE, NSSM_REGISTRY);
\r
41 if (set_expand_string(key, NSSM_REG_FLAGS, service->flags)) {
\r
42 RegDeleteKey(HKEY_LOCAL_MACHINE, NSSM_REGISTRY);
\r
46 if (set_expand_string(key, NSSM_REG_DIR, service->dir)) {
\r
47 RegDeleteKey(HKEY_LOCAL_MACHINE, NSSM_REGISTRY);
\r
52 /* Other non-default parameters. May fail. */
\r
53 if (service->priority != NORMAL_PRIORITY_CLASS) set_number(key, NSSM_REG_PRIORITY, service->priority);
\r
54 else if (editing) RegDeleteValue(key, NSSM_REG_PRIORITY);
\r
55 if (service->affinity) {
\r
57 if (! affinity_mask_to_string(service->affinity, &string)) {
\r
58 if (RegSetValueEx(key, NSSM_REG_AFFINITY, 0, REG_SZ, (const unsigned char *) string, (unsigned long) (_tcslen(string) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
59 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_AFFINITY, error_string(GetLastError()), 0);
\r
60 HeapFree(GetProcessHeap(), 0, string);
\r
64 if (string) HeapFree(GetProcessHeap(), 0, string);
\r
66 else if (editing) RegDeleteValue(key, NSSM_REG_AFFINITY);
\r
67 unsigned long stop_method_skip = ~service->stop_method;
\r
68 if (stop_method_skip) set_number(key, NSSM_REG_STOP_METHOD_SKIP, stop_method_skip);
\r
69 else if (editing) RegDeleteValue(key, NSSM_REG_STOP_METHOD_SKIP);
\r
70 if (service->default_exit_action < NSSM_NUM_EXIT_ACTIONS) create_exit_action(service->name, exit_action_strings[service->default_exit_action], editing);
\r
71 if (service->restart_delay) set_number(key, NSSM_REG_RESTART_DELAY, service->restart_delay);
\r
72 else if (editing) RegDeleteValue(key, NSSM_REG_RESTART_DELAY);
\r
73 if (service->throttle_delay != NSSM_RESET_THROTTLE_RESTART) set_number(key, NSSM_REG_THROTTLE, service->throttle_delay);
\r
74 else if (editing) RegDeleteValue(key, NSSM_REG_THROTTLE);
\r
75 if (service->kill_console_delay != NSSM_KILL_CONSOLE_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, service->kill_console_delay);
\r
76 else if (editing) RegDeleteValue(key, NSSM_REG_KILL_CONSOLE_GRACE_PERIOD);
\r
77 if (service->kill_window_delay != NSSM_KILL_WINDOW_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_WINDOW_GRACE_PERIOD, service->kill_window_delay);
\r
78 else if (editing) RegDeleteValue(key, NSSM_REG_KILL_WINDOW_GRACE_PERIOD);
\r
79 if (service->kill_threads_delay != NSSM_KILL_THREADS_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_THREADS_GRACE_PERIOD, service->kill_threads_delay);
\r
80 else if (editing) RegDeleteValue(key, NSSM_REG_KILL_THREADS_GRACE_PERIOD);
\r
81 if (! service->kill_process_tree) set_number(key, NSSM_REG_KILL_PROCESS_TREE, 0);
\r
82 else if (editing) RegDeleteValue(key, NSSM_REG_KILL_PROCESS_TREE);
\r
83 if (service->stdin_path[0] || editing) {
\r
84 if (service->stdin_path[0]) set_expand_string(key, NSSM_REG_STDIN, service->stdin_path);
\r
85 else if (editing) RegDeleteValue(key, NSSM_REG_STDIN);
\r
86 if (service->stdin_sharing != NSSM_STDIN_SHARING) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_SHARING, service->stdin_sharing);
\r
87 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_SHARING);
\r
88 if (service->stdin_disposition != NSSM_STDIN_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_DISPOSITION, service->stdin_disposition);
\r
89 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_DISPOSITION);
\r
90 if (service->stdin_flags != NSSM_STDIN_FLAGS) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_FLAGS, service->stdin_flags);
\r
91 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_FLAGS);
\r
93 if (service->stdout_path[0] || editing) {
\r
94 if (service->stdout_path[0]) set_expand_string(key, NSSM_REG_STDOUT, service->stdout_path);
\r
95 else if (editing) RegDeleteValue(key, NSSM_REG_STDOUT);
\r
96 if (service->stdout_sharing != NSSM_STDOUT_SHARING) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_SHARING, service->stdout_sharing);
\r
97 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_SHARING);
\r
98 if (service->stdout_disposition != NSSM_STDOUT_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_DISPOSITION, service->stdout_disposition);
\r
99 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_DISPOSITION);
\r
100 if (service->stdout_flags != NSSM_STDOUT_FLAGS) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_FLAGS, service->stdout_flags);
\r
101 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_FLAGS);
\r
102 if (service->stdout_copy_and_truncate) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_COPY_AND_TRUNCATE, 1);
\r
103 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_COPY_AND_TRUNCATE);
\r
105 if (service->stderr_path[0] || editing) {
\r
106 if (service->stderr_path[0]) set_expand_string(key, NSSM_REG_STDERR, service->stderr_path);
\r
107 else if (editing) RegDeleteValue(key, NSSM_REG_STDERR);
\r
108 if (service->stderr_sharing != NSSM_STDERR_SHARING) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_SHARING, service->stderr_sharing);
\r
109 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_SHARING);
\r
110 if (service->stderr_disposition != NSSM_STDERR_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_DISPOSITION, service->stderr_disposition);
\r
111 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_DISPOSITION);
\r
112 if (service->stderr_flags != NSSM_STDERR_FLAGS) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_FLAGS, service->stderr_flags);
\r
113 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_FLAGS);
\r
114 if (service->stderr_copy_and_truncate) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_COPY_AND_TRUNCATE, 1);
\r
115 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_COPY_AND_TRUNCATE);
\r
117 if (service->rotate_files) set_number(key, NSSM_REG_ROTATE, 1);
\r
118 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE);
\r
119 if (service->rotate_stdout_online) set_number(key, NSSM_REG_ROTATE_ONLINE, 1);
\r
120 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_ONLINE);
\r
121 if (service->rotate_seconds) set_number(key, NSSM_REG_ROTATE_SECONDS, service->rotate_seconds);
\r
122 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_SECONDS);
\r
123 if (service->rotate_bytes_low) set_number(key, NSSM_REG_ROTATE_BYTES_LOW, service->rotate_bytes_low);
\r
124 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_BYTES_LOW);
\r
125 if (service->rotate_bytes_high) set_number(key, NSSM_REG_ROTATE_BYTES_HIGH, service->rotate_bytes_high);
\r
126 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_BYTES_HIGH);
\r
127 if (service->rotate_delay != NSSM_ROTATE_DELAY) set_number(key, NSSM_REG_ROTATE_DELAY, service->rotate_delay);
\r
128 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_DELAY);
\r
129 if (service->no_console) set_number(key, NSSM_REG_NO_CONSOLE, 1);
\r
130 else if (editing) RegDeleteValue(key, NSSM_REG_NO_CONSOLE);
\r
133 if (service->env) {
\r
134 if (RegSetValueEx(key, NSSM_REG_ENV, 0, REG_MULTI_SZ, (const unsigned char *) service->env, (unsigned long) service->envlen * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
135 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV, error_string(GetLastError()), 0);
\r
138 else if (editing) RegDeleteValue(key, NSSM_REG_ENV);
\r
139 if (service->env_extra) {
\r
140 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
141 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV_EXTRA, error_string(GetLastError()), 0);
\r
144 else if (editing) RegDeleteValue(key, NSSM_REG_ENV_EXTRA);
\r
146 /* Close registry. */
\r
152 int create_exit_action(TCHAR *service_name, const TCHAR *action_string, bool editing) {
\r
154 TCHAR registry[KEY_LENGTH];
\r
155 if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, NSSM_REGISTRY _T("\\%s"), service_name, NSSM_REG_EXIT) < 0) {
\r
156 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("NSSM_REG_EXIT"), _T("create_exit_action()"), 0);
\r
160 /* Try to open the registry */
\r
162 unsigned long disposition;
\r
163 if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, KEY_WRITE, 0, &key, &disposition) != ERROR_SUCCESS) {
\r
164 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
168 /* Do nothing if the key already existed */
\r
169 if (disposition == REG_OPENED_EXISTING_KEY && ! editing) {
\r
174 /* Create the default value */
\r
175 if (RegSetValueEx(key, 0, 0, REG_SZ, (const unsigned char *) action_string, (unsigned long) (_tcslen(action_string) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
176 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_EXIT, error_string(GetLastError()), 0);
\r
181 /* Close registry */
\r
187 int get_environment(TCHAR *service_name, HKEY key, TCHAR *value, TCHAR **env, unsigned long *envlen) {
\r
188 unsigned long type = REG_MULTI_SZ;
\r
190 /* Dummy test to find buffer size */
\r
191 unsigned long ret = RegQueryValueEx(key, value, 0, &type, NULL, envlen);
\r
192 if (ret != ERROR_SUCCESS) {
\r
195 /* The service probably doesn't have any environment configured */
\r
196 if (ret == ERROR_FILE_NOT_FOUND) return 0;
\r
197 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
201 if (type != REG_MULTI_SZ) {
\r
204 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_INVALID_ENVIRONMENT_STRING_TYPE, value, service_name, 0);
\r
208 /* Probably not possible */
\r
209 if (! *envlen) return 0;
\r
211 /* Previously initialised? */
\r
212 if (*env) HeapFree(GetProcessHeap(), 0, *env);
\r
214 *env = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, *envlen);
\r
217 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, value, _T("get_environment()"), 0);
\r
221 /* Actually get the strings */
\r
222 ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) *env, envlen);
\r
223 if (ret != ERROR_SUCCESS) {
\r
224 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
225 HeapFree(GetProcessHeap(), 0, *env);
\r
235 int get_string(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool expand, bool sanitise, bool must_exist) {
\r
236 TCHAR *buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, datalen);
\r
238 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, value, _T("get_string()"), 0);
\r
242 ZeroMemory(data, datalen);
\r
244 unsigned long type = REG_EXPAND_SZ;
\r
245 unsigned long buflen = datalen;
\r
247 unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);
\r
248 if (ret != ERROR_SUCCESS) {
\r
249 unsigned long error = GetLastError();
\r
250 HeapFree(GetProcessHeap(), 0, buffer);
\r
252 if (ret == ERROR_FILE_NOT_FOUND) {
\r
253 if (! must_exist) return 0;
\r
256 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(error), 0);
\r
260 /* Paths aren't allowed to contain quotes. */
\r
261 if (sanitise) PathUnquoteSpaces(buffer);
\r
263 /* Do we want to expand the string? */
\r
265 if (type == REG_EXPAND_SZ) type = REG_SZ;
\r
268 /* Technically we shouldn't expand environment strings from REG_SZ values */
\r
269 if (type != REG_EXPAND_SZ) {
\r
270 memmove(data, buffer, buflen);
\r
271 HeapFree(GetProcessHeap(), 0, buffer);
\r
275 ret = ExpandEnvironmentStrings((TCHAR *) buffer, data, datalen);
\r
276 if (! ret || ret > datalen) {
\r
277 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_EXPANDENVIRONMENTSTRINGS_FAILED, buffer, error_string(GetLastError()), 0);
\r
278 HeapFree(GetProcessHeap(), 0, buffer);
\r
282 HeapFree(GetProcessHeap(), 0, buffer);
\r
286 int get_string(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise) {
\r
287 return get_string(key, value, data, datalen, false, sanitise, true);
\r
290 int expand_parameter(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise, bool must_exist) {
\r
291 return get_string(key, value, data, datalen, true, sanitise, must_exist);
\r
294 int expand_parameter(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise) {
\r
295 return expand_parameter(key, value, data, datalen, sanitise, true);
\r
299 Sets a string in the registry.
\r
300 Returns: 0 if it was set.
\r
303 int set_string(HKEY key, TCHAR *value, TCHAR *string, bool expand) {
\r
304 unsigned long type = expand ? REG_EXPAND_SZ : REG_SZ;
\r
305 if (RegSetValueEx(key, value, 0, type, (const unsigned char *) string, (unsigned long) (_tcslen(string) + 1) * sizeof(TCHAR)) == ERROR_SUCCESS) return 0;
\r
306 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
310 int set_string(HKEY key, TCHAR *value, TCHAR *string) {
\r
311 return set_string(key, value, string, false);
\r
315 int set_expand_string(HKEY key, TCHAR *value, TCHAR *string) {
\r
316 return set_string(key, value, string, true);
\r
321 Set an unsigned long in the registry.
\r
322 Returns: 0 if it was set.
\r
325 int set_number(HKEY key, TCHAR *value, unsigned long number) {
\r
326 if (RegSetValueEx(key, value, 0, REG_DWORD, (const unsigned char *) &number, sizeof(number)) == ERROR_SUCCESS) return 0;
\r
327 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
332 Query an unsigned long from the registry.
\r
333 Returns: 1 if a number was retrieved.
\r
334 0 if none was found and must_exist is false.
\r
335 -1 if none was found and must_exist is true.
\r
338 int get_number(HKEY key, TCHAR *value, unsigned long *number, bool must_exist) {
\r
339 unsigned long type = REG_DWORD;
\r
340 unsigned long number_len = sizeof(unsigned long);
\r
342 int ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) number, &number_len);
\r
343 if (ret == ERROR_SUCCESS) return 1;
\r
345 if (ret == ERROR_FILE_NOT_FOUND) {
\r
346 if (! must_exist) return 0;
\r
349 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
350 if (ret == ERROR_FILE_NOT_FOUND) return -1;
\r
355 int get_number(HKEY key, TCHAR *value, unsigned long *number) {
\r
356 return get_number(key, value, number, true);
\r
359 /* Replace NULL with CRLF. Leave NULL NULL as the end marker. */
\r
360 int format_double_null(TCHAR *dn, unsigned long dnlen, TCHAR **formatted, unsigned long *newlen) {
\r
361 unsigned long i, j;
\r
369 for (i = 0; i < dnlen; i++) if (! dn[i] && dn[i + 1]) ++*newlen;
\r
371 *formatted = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *newlen * sizeof(TCHAR));
\r
372 if (! *formatted) {
\r
377 for (i = 0, j = 0; i < dnlen; i++) {
\r
378 (*formatted)[j] = dn[i];
\r
381 (*formatted)[j] = _T('\r');
\r
382 (*formatted)[++j] = _T('\n');
\r
391 /* Strip CR and replace LF with NULL. */
\r
392 int unformat_double_null(TCHAR *dn, unsigned long dnlen, TCHAR **unformatted, unsigned long *newlen) {
\r
393 unsigned long i, j;
\r
401 for (i = 0; i < dnlen; i++) if (dn[i] != _T('\r')) ++*newlen;
\r
403 /* Skip blank lines. */
\r
404 for (i = 0; i < dnlen; i++) {
\r
405 if (dn[i] == _T('\r') && dn[i + 1] == _T('\n')) {
\r
406 /* This is the last CRLF. */
\r
407 if (i >= dnlen - 2) break;
\r
410 Strip at the start of the block or if the next characters are
\r
413 if (! i || (dn[i + 2] == _T('\r') && dn[i + 3] == _T('\n'))) {
\r
414 for (j = i + 2; j < dnlen; j++) dn[j - 2] = dn[j];
\r
415 dn[dnlen--] = _T('\0');
\r
416 dn[dnlen--] = _T('\0');
\r
423 /* Must end with two NULLs. */
\r
426 *unformatted = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *newlen * sizeof(TCHAR));
\r
427 if (! *unformatted) return 1;
\r
429 for (i = 0, j = 0; i < dnlen; i++) {
\r
430 if (dn[i] == _T('\r')) continue;
\r
431 if (dn[i] == _T('\n')) (*unformatted)[j] = _T('\0');
\r
432 else (*unformatted)[j] = dn[i];
\r
439 void override_milliseconds(TCHAR *service_name, HKEY key, TCHAR *value, unsigned long *buffer, unsigned long default_value, unsigned long event) {
\r
440 unsigned long type = REG_DWORD;
\r
441 unsigned long buflen = sizeof(unsigned long);
\r
443 unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);
\r
444 if (ret != ERROR_SUCCESS) {
\r
445 if (ret != ERROR_FILE_NOT_FOUND) {
\r
446 if (type != REG_DWORD) {
\r
447 TCHAR milliseconds[16];
\r
448 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), default_value);
\r
449 log_event(EVENTLOG_WARNING_TYPE, event, service_name, value, milliseconds, 0);
\r
451 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
456 if (! ok) *buffer = default_value;
\r
459 static int service_registry_path(const TCHAR *service_name, bool parameters, const TCHAR *sub, TCHAR *buffer, unsigned long buflen) {
\r
463 if (sub) ret = _sntprintf_s(buffer, buflen, _TRUNCATE, NSSM_REGISTRY _T("\\") NSSM_REG_PARAMETERS _T("\\%s"), service_name, sub);
\r
464 else ret = _sntprintf_s(buffer, buflen, _TRUNCATE, NSSM_REGISTRY _T("\\") NSSM_REG_PARAMETERS, service_name);
\r
466 else ret = _sntprintf_s(buffer, buflen, _TRUNCATE, NSSM_REGISTRY, service_name);
\r
471 static HKEY open_registry_key(const TCHAR *registry, REGSAM sam, bool must_exist) {
\r
474 if (sam & KEY_SET_VALUE) {
\r
475 if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, sam, 0, &key, 0) != ERROR_SUCCESS) {
\r
476 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
481 long error = RegOpenKeyEx(HKEY_LOCAL_MACHINE, registry, 0, sam, &key);
\r
482 if (error != ERROR_SUCCESS) {
\r
483 if (error == ERROR_FILE_NOT_FOUND && ! must_exist) return 0;
\r
484 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
492 HKEY open_service_registry(const TCHAR *service_name, REGSAM sam, bool must_exist) {
\r
494 TCHAR registry[KEY_LENGTH];
\r
495 if (service_registry_path(service_name, false, 0, registry, _countof(registry)) < 0) {
\r
496 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REGISTRY, _T("open_service_registry()"), 0);
\r
500 return open_registry_key(registry, sam, must_exist);
\r
503 HKEY open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam, bool must_exist) {
\r
505 TCHAR registry[KEY_LENGTH];
\r
506 if (service_registry_path(service_name, true, sub, registry, _countof(registry)) < 0) {
\r
507 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REGISTRY, _T("open_registry()"), 0);
\r
511 return open_registry_key(registry, sam, must_exist);
\r
514 HKEY open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam) {
\r
515 return open_registry(service_name, sub, sam, true);
\r
518 HKEY open_registry(const TCHAR *service_name, REGSAM sam) {
\r
519 return open_registry(service_name, 0, sam, true);
\r
522 int get_io_parameters(nssm_service_t *service, HKEY key) {
\r
524 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
525 service->stdin_sharing = service->stdin_disposition = service->stdin_flags = 0;
\r
526 ZeroMemory(service->stdin_path, _countof(service->stdin_path) * sizeof(TCHAR));
\r
531 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
532 service->stdout_sharing = service->stdout_disposition = service->stdout_flags = 0;
\r
533 ZeroMemory(service->stdout_path, _countof(service->stdout_path) * sizeof(TCHAR));
\r
538 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
539 service->stderr_sharing = service->stderr_disposition = service->stderr_flags = 0;
\r
540 ZeroMemory(service->stderr_path, _countof(service->stderr_path) * sizeof(TCHAR));
\r
547 int get_parameters(nssm_service_t *service, STARTUPINFO *si) {
\r
550 /* Try to open the registry */
\r
551 HKEY key = open_registry(service->name, KEY_READ);
\r
552 if (! key) return 1;
\r
554 /* Don't expand parameters when retrieving for the GUI. */
\r
555 bool expand = si ? true : false;
\r
557 /* Try to get environment variables - may fail */
\r
558 get_environment(service->name, key, NSSM_REG_ENV, &service->env, &service->envlen);
\r
559 /* Environment variables to add to existing rather than replace - may fail. */
\r
560 get_environment(service->name, key, NSSM_REG_ENV_EXTRA, &service->env_extra, &service->env_extralen);
\r
562 /* Set environment if we are starting the service. */
\r
563 if (si) set_service_environment(service);
\r
565 /* Try to get executable file - MUST succeed */
\r
566 if (get_string(key, NSSM_REG_EXE, service->exe, sizeof(service->exe), expand, false, true)) {
\r
571 /* Try to get flags - may fail and we don't care */
\r
572 if (get_string(key, NSSM_REG_FLAGS, service->flags, sizeof(service->flags), expand, false, true)) {
\r
573 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_FLAGS, NSSM_REG_FLAGS, service->name, service->exe, 0);
\r
574 ZeroMemory(service->flags, sizeof(service->flags));
\r
577 /* Try to get startup directory - may fail and we fall back to a default */
\r
578 if (get_string(key, NSSM_REG_DIR, service->dir, sizeof(service->dir), expand, true, true) || ! service->dir[0]) {
\r
579 _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);
\r
580 strip_basename(service->dir);
\r
581 if (service->dir[0] == _T('\0')) {
\r
583 ret = GetWindowsDirectory(service->dir, sizeof(service->dir));
\r
584 if (! ret || ret > sizeof(service->dir)) {
\r
585 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_NO_DIR_AND_NO_FALLBACK, NSSM_REG_DIR, service->name, 0);
\r
590 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_DIR, NSSM_REG_DIR, service->name, service->dir, 0);
\r
593 /* Try to get processor affinity - may fail. */
\r
595 if (get_string(key, NSSM_REG_AFFINITY, buffer, sizeof(buffer), false, false, false) || ! buffer[0]) service->affinity = 0LL;
\r
596 else if (affinity_string_to_mask(buffer, &service->affinity)) {
\r
597 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_AFFINITY_MASK, service->name, buffer);
\r
598 service->affinity = 0LL;
\r
601 DWORD_PTR affinity, system_affinity;
\r
603 if (GetProcessAffinityMask(GetCurrentProcess(), &affinity, &system_affinity)) {
\r
604 _int64 effective_affinity = service->affinity & system_affinity;
\r
605 if (effective_affinity != service->affinity) {
\r
607 if (! affinity_mask_to_string(system_affinity, &system)) {
\r
608 TCHAR *effective = 0;
\r
609 if (! affinity_mask_to_string(effective_affinity, &effective)) {
\r
610 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_EFFECTIVE_AFFINITY_MASK, service->name, buffer, system, effective, 0);
\r
612 HeapFree(GetProcessHeap(), 0, effective);
\r
614 HeapFree(GetProcessHeap(), 0, system);
\r
619 /* Try to get priority - may fail. */
\r
620 unsigned long priority;
\r
621 if (get_number(key, NSSM_REG_PRIORITY, &priority, false) == 1) {
\r
622 if (priority == (priority & priority_mask())) service->priority = priority;
\r
623 else log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_PRIORITY, service->name, NSSM_REG_PRIORITY, 0);
\r
626 /* Try to get file rotation settings - may fail. */
\r
627 unsigned long rotate_files;
\r
628 if (get_number(key, NSSM_REG_ROTATE, &rotate_files, false) == 1) {
\r
629 if (rotate_files) service->rotate_files = true;
\r
630 else service->rotate_files = false;
\r
632 else service->rotate_files = false;
\r
633 if (get_number(key, NSSM_REG_ROTATE_ONLINE, &rotate_files, false) == 1) {
\r
634 if (rotate_files) service->rotate_stdout_online = service->rotate_stderr_online = true;
\r
635 else service->rotate_stdout_online = service->rotate_stderr_online = false;
\r
637 else service->rotate_stdout_online = service->rotate_stderr_online = false;
\r
638 if (get_number(key, NSSM_REG_ROTATE_SECONDS, &service->rotate_seconds, false) != 1) service->rotate_seconds = 0;
\r
639 if (get_number(key, NSSM_REG_ROTATE_BYTES_LOW, &service->rotate_bytes_low, false) != 1) service->rotate_bytes_low = 0;
\r
640 if (get_number(key, NSSM_REG_ROTATE_BYTES_HIGH, &service->rotate_bytes_high, false) != 1) service->rotate_bytes_high = 0;
\r
641 override_milliseconds(service->name, key, NSSM_REG_ROTATE_DELAY, &service->rotate_delay, NSSM_ROTATE_DELAY, NSSM_EVENT_BOGUS_THROTTLE);
\r
643 /* Try to get force new console setting - may fail. */
\r
644 if (get_number(key, NSSM_REG_NO_CONSOLE, &service->no_console, false) != 1) service->no_console = 0;
\r
646 /* Change to startup directory in case stdout/stderr are relative paths. */
\r
647 TCHAR cwd[PATH_LENGTH];
\r
648 GetCurrentDirectory(_countof(cwd), cwd);
\r
649 SetCurrentDirectory(service->dir);
\r
651 /* Try to get stdout and stderr */
\r
652 if (get_io_parameters(service, key)) {
\r
653 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);
\r
655 SetCurrentDirectory(cwd);
\r
659 /* Change back in case the startup directory needs to be deleted. */
\r
660 SetCurrentDirectory(cwd);
\r
662 /* Try to get mandatory restart delay */
\r
663 override_milliseconds(service->name, key, NSSM_REG_RESTART_DELAY, &service->restart_delay, 0, NSSM_EVENT_BOGUS_RESTART_DELAY);
\r
665 /* Try to get throttle restart delay */
\r
666 override_milliseconds(service->name, key, NSSM_REG_THROTTLE, &service->throttle_delay, NSSM_RESET_THROTTLE_RESTART, NSSM_EVENT_BOGUS_THROTTLE);
\r
668 /* Try to get service stop flags. */
\r
669 unsigned long type = REG_DWORD;
\r
670 unsigned long stop_method_skip;
\r
671 unsigned long buflen = sizeof(stop_method_skip);
\r
672 bool stop_ok = false;
\r
673 ret = RegQueryValueEx(key, NSSM_REG_STOP_METHOD_SKIP, 0, &type, (unsigned char *) &stop_method_skip, &buflen);
\r
674 if (ret != ERROR_SUCCESS) {
\r
675 if (ret != ERROR_FILE_NOT_FOUND) {
\r
676 if (type != REG_DWORD) {
\r
677 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_STOP_METHOD_SKIP, service->name, NSSM_REG_STOP_METHOD_SKIP, NSSM, 0);
\r
679 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, NSSM_REG_STOP_METHOD_SKIP, error_string(GetLastError()), 0);
\r
682 else stop_ok = true;
\r
684 /* Try all methods except those requested to be skipped. */
\r
685 service->stop_method = ~0;
\r
686 if (stop_ok) service->stop_method &= ~stop_method_skip;
\r
688 /* Try to get kill delays - may fail. */
\r
689 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
690 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
691 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
693 /* Try to get process tree settings - may fail. */
\r
694 unsigned long kill_process_tree;
\r
695 if (get_number(key, NSSM_REG_KILL_PROCESS_TREE, &kill_process_tree, false) == 1) {
\r
696 if (kill_process_tree) service->kill_process_tree = true;
\r
697 else service->kill_process_tree = false;
\r
699 else service->kill_process_tree = true;
\r
701 /* Try to get default exit action. */
\r
702 bool default_action;
\r
703 service->default_exit_action = NSSM_EXIT_RESTART;
\r
704 TCHAR action_string[ACTION_LEN];
\r
705 if (! get_exit_action(service->name, 0, action_string, &default_action)) {
\r
706 for (int i = 0; exit_action_strings[i]; i++) {
\r
707 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
708 service->default_exit_action = i;
\r
714 /* Close registry */
\r
721 Sets the string for the exit action corresponding to the exit code.
\r
723 ret is a pointer to an unsigned long containing the exit code.
\r
724 If ret is NULL, we retrieve the default exit action unconditionally.
\r
726 action is a buffer which receives the string.
\r
728 default_action is a pointer to a bool which is set to false if there
\r
729 was an explicit string for the given exit code, or true if we are
\r
730 returning the default action.
\r
732 Returns: 0 on success.
\r
735 int get_exit_action(const TCHAR *service_name, unsigned long *ret, TCHAR *action, bool *default_action) {
\r
736 /* Are we returning the default action or a status-specific one? */
\r
737 *default_action = ! ret;
\r
739 /* Try to open the registry */
\r
740 HKEY key = open_registry(service_name, NSSM_REG_EXIT, KEY_READ);
\r
741 if (! key) return 1;
\r
743 unsigned long type = REG_SZ;
\r
744 unsigned long action_len = ACTION_LEN;
\r
747 if (! ret) code[0] = _T('\0');
\r
748 else if (_sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), *ret) < 0) {
\r
750 return get_exit_action(service_name, 0, action, default_action);
\r
752 if (RegQueryValueEx(key, code, 0, &type, (unsigned char *) action, &action_len) != ERROR_SUCCESS) {
\r
754 /* Try again with * as the key if an exit code was defined */
\r
755 if (ret) return get_exit_action(service_name, 0, action, default_action);
\r
759 /* Close registry */
\r
765 int set_hook(const TCHAR *service_name, const TCHAR *hook_event, const TCHAR *hook_action, TCHAR *cmd) {
\r
766 /* Try to open the registry */
\r
767 TCHAR registry[KEY_LENGTH];
\r
768 if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, _T("%s\\%s"), NSSM_REG_HOOK, hook_event) < 0) {
\r
769 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("hook registry"), _T("set_hook()"), 0);
\r
776 /* Don't create keys needlessly. */
\r
777 if (! _tcslen(cmd)) {
\r
778 key = open_registry(service_name, registry, KEY_READ, false);
\r
779 if (! key) return 0;
\r
780 error = RegQueryValueEx(key, hook_action, 0, 0, 0, 0);
\r
782 if (error == ERROR_FILE_NOT_FOUND) return 0;
\r
785 key = open_registry(service_name, registry, KEY_WRITE);
\r
786 if (! key) return 1;
\r
789 if (_tcslen(cmd)) ret = set_string(key, (TCHAR *) hook_action, cmd, true);
\r
791 error = RegDeleteValue(key, hook_action);
\r
792 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) ret = 0;
\r
795 /* Close registry */
\r
801 int get_hook(const TCHAR *service_name, const TCHAR *hook_event, const TCHAR *hook_action, TCHAR *buffer, unsigned long buflen) {
\r
802 /* Try to open the registry */
\r
803 TCHAR registry[KEY_LENGTH];
\r
804 if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, _T("%s\\%s"), NSSM_REG_HOOK, hook_event) < 0) {
\r
805 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("hook registry"), _T("get_hook()"), 0);
\r
808 HKEY key = open_registry(service_name, registry, KEY_READ, false);
\r
809 if (! key) return 1;
\r
811 int ret = expand_parameter(key, (TCHAR *) hook_action, buffer, buflen, true, false);
\r
813 /* Close registry */
\r