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 HKEY open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam, bool must_exist) {
\r
461 TCHAR registry[KEY_LENGTH];
\r
465 if (sub) ret = _sntprintf_s(registry, _countof(registry), _TRUNCATE, NSSM_REGISTRY _T("\\%s"), service_name, sub);
\r
466 else ret = _sntprintf_s(registry, _countof(registry), _TRUNCATE, NSSM_REGISTRY, service_name);
\r
468 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("NSSM_REGISTRY"), _T("open_registry()"), 0);
\r
472 if (sam & KEY_SET_VALUE) {
\r
473 if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, sam, 0, &key, 0) != ERROR_SUCCESS) {
\r
474 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
479 long error = RegOpenKeyEx(HKEY_LOCAL_MACHINE, registry, 0, sam, &key);
\r
480 if (error != ERROR_SUCCESS) {
\r
481 if (error == ERROR_FILE_NOT_FOUND && ! must_exist) return 0;
\r
482 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
490 HKEY open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam) {
\r
491 return open_registry(service_name, sub, sam, true);
\r
494 HKEY open_registry(const TCHAR *service_name, REGSAM sam) {
\r
495 return open_registry(service_name, 0, sam, true);
\r
498 int get_io_parameters(nssm_service_t *service, HKEY key) {
\r
500 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
501 service->stdin_sharing = service->stdin_disposition = service->stdin_flags = 0;
\r
502 ZeroMemory(service->stdin_path, _countof(service->stdin_path) * sizeof(TCHAR));
\r
507 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
508 service->stdout_sharing = service->stdout_disposition = service->stdout_flags = 0;
\r
509 ZeroMemory(service->stdout_path, _countof(service->stdout_path) * sizeof(TCHAR));
\r
514 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
515 service->stderr_sharing = service->stderr_disposition = service->stderr_flags = 0;
\r
516 ZeroMemory(service->stderr_path, _countof(service->stderr_path) * sizeof(TCHAR));
\r
523 int get_parameters(nssm_service_t *service, STARTUPINFO *si) {
\r
526 /* Try to open the registry */
\r
527 HKEY key = open_registry(service->name, KEY_READ);
\r
528 if (! key) return 1;
\r
530 /* Don't expand parameters when retrieving for the GUI. */
\r
531 bool expand = si ? true : false;
\r
533 /* Try to get environment variables - may fail */
\r
534 get_environment(service->name, key, NSSM_REG_ENV, &service->env, &service->envlen);
\r
535 /* Environment variables to add to existing rather than replace - may fail. */
\r
536 get_environment(service->name, key, NSSM_REG_ENV_EXTRA, &service->env_extra, &service->env_extralen);
\r
538 /* Set environment if we are starting the service. */
\r
539 if (si) set_service_environment(service);
\r
541 /* Try to get executable file - MUST succeed */
\r
542 if (get_string(key, NSSM_REG_EXE, service->exe, sizeof(service->exe), expand, false, true)) {
\r
547 /* Try to get flags - may fail and we don't care */
\r
548 if (get_string(key, NSSM_REG_FLAGS, service->flags, sizeof(service->flags), expand, false, true)) {
\r
549 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_FLAGS, NSSM_REG_FLAGS, service->name, service->exe, 0);
\r
550 ZeroMemory(service->flags, sizeof(service->flags));
\r
553 /* Try to get startup directory - may fail and we fall back to a default */
\r
554 if (get_string(key, NSSM_REG_DIR, service->dir, sizeof(service->dir), expand, true, true) || ! service->dir[0]) {
\r
555 _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);
\r
556 strip_basename(service->dir);
\r
557 if (service->dir[0] == _T('\0')) {
\r
559 ret = GetWindowsDirectory(service->dir, sizeof(service->dir));
\r
560 if (! ret || ret > sizeof(service->dir)) {
\r
561 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_NO_DIR_AND_NO_FALLBACK, NSSM_REG_DIR, service->name, 0);
\r
566 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_DIR, NSSM_REG_DIR, service->name, service->dir, 0);
\r
569 /* Try to get processor affinity - may fail. */
\r
571 if (get_string(key, NSSM_REG_AFFINITY, buffer, sizeof(buffer), false, false, false) || ! buffer[0]) service->affinity = 0LL;
\r
572 else if (affinity_string_to_mask(buffer, &service->affinity)) {
\r
573 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_AFFINITY_MASK, service->name, buffer);
\r
574 service->affinity = 0LL;
\r
577 DWORD_PTR affinity, system_affinity;
\r
579 if (GetProcessAffinityMask(GetCurrentProcess(), &affinity, &system_affinity)) {
\r
580 _int64 effective_affinity = service->affinity & system_affinity;
\r
581 if (effective_affinity != service->affinity) {
\r
583 if (! affinity_mask_to_string(system_affinity, &system)) {
\r
584 TCHAR *effective = 0;
\r
585 if (! affinity_mask_to_string(effective_affinity, &effective)) {
\r
586 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_EFFECTIVE_AFFINITY_MASK, service->name, buffer, system, effective, 0);
\r
588 HeapFree(GetProcessHeap(), 0, effective);
\r
590 HeapFree(GetProcessHeap(), 0, system);
\r
595 /* Try to get priority - may fail. */
\r
596 unsigned long priority;
\r
597 if (get_number(key, NSSM_REG_PRIORITY, &priority, false) == 1) {
\r
598 if (priority == (priority & priority_mask())) service->priority = priority;
\r
599 else log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_PRIORITY, service->name, NSSM_REG_PRIORITY, 0);
\r
602 /* Try to get file rotation settings - may fail. */
\r
603 unsigned long rotate_files;
\r
604 if (get_number(key, NSSM_REG_ROTATE, &rotate_files, false) == 1) {
\r
605 if (rotate_files) service->rotate_files = true;
\r
606 else service->rotate_files = false;
\r
608 else service->rotate_files = false;
\r
609 if (get_number(key, NSSM_REG_ROTATE_ONLINE, &rotate_files, false) == 1) {
\r
610 if (rotate_files) service->rotate_stdout_online = service->rotate_stderr_online = true;
\r
611 else service->rotate_stdout_online = service->rotate_stderr_online = false;
\r
613 else service->rotate_stdout_online = service->rotate_stderr_online = false;
\r
614 if (get_number(key, NSSM_REG_ROTATE_SECONDS, &service->rotate_seconds, false) != 1) service->rotate_seconds = 0;
\r
615 if (get_number(key, NSSM_REG_ROTATE_BYTES_LOW, &service->rotate_bytes_low, false) != 1) service->rotate_bytes_low = 0;
\r
616 if (get_number(key, NSSM_REG_ROTATE_BYTES_HIGH, &service->rotate_bytes_high, false) != 1) service->rotate_bytes_high = 0;
\r
617 override_milliseconds(service->name, key, NSSM_REG_ROTATE_DELAY, &service->rotate_delay, NSSM_ROTATE_DELAY, NSSM_EVENT_BOGUS_THROTTLE);
\r
619 /* Try to get force new console setting - may fail. */
\r
620 if (get_number(key, NSSM_REG_NO_CONSOLE, &service->no_console, false) != 1) service->no_console = 0;
\r
622 /* Change to startup directory in case stdout/stderr are relative paths. */
\r
623 TCHAR cwd[PATH_LENGTH];
\r
624 GetCurrentDirectory(_countof(cwd), cwd);
\r
625 SetCurrentDirectory(service->dir);
\r
627 /* Try to get stdout and stderr */
\r
628 if (get_io_parameters(service, key)) {
\r
629 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);
\r
631 SetCurrentDirectory(cwd);
\r
635 /* Change back in case the startup directory needs to be deleted. */
\r
636 SetCurrentDirectory(cwd);
\r
638 /* Try to get mandatory restart delay */
\r
639 override_milliseconds(service->name, key, NSSM_REG_RESTART_DELAY, &service->restart_delay, 0, NSSM_EVENT_BOGUS_RESTART_DELAY);
\r
641 /* Try to get throttle restart delay */
\r
642 override_milliseconds(service->name, key, NSSM_REG_THROTTLE, &service->throttle_delay, NSSM_RESET_THROTTLE_RESTART, NSSM_EVENT_BOGUS_THROTTLE);
\r
644 /* Try to get service stop flags. */
\r
645 unsigned long type = REG_DWORD;
\r
646 unsigned long stop_method_skip;
\r
647 unsigned long buflen = sizeof(stop_method_skip);
\r
648 bool stop_ok = false;
\r
649 ret = RegQueryValueEx(key, NSSM_REG_STOP_METHOD_SKIP, 0, &type, (unsigned char *) &stop_method_skip, &buflen);
\r
650 if (ret != ERROR_SUCCESS) {
\r
651 if (ret != ERROR_FILE_NOT_FOUND) {
\r
652 if (type != REG_DWORD) {
\r
653 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_STOP_METHOD_SKIP, service->name, NSSM_REG_STOP_METHOD_SKIP, NSSM, 0);
\r
655 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, NSSM_REG_STOP_METHOD_SKIP, error_string(GetLastError()), 0);
\r
658 else stop_ok = true;
\r
660 /* Try all methods except those requested to be skipped. */
\r
661 service->stop_method = ~0;
\r
662 if (stop_ok) service->stop_method &= ~stop_method_skip;
\r
664 /* Try to get kill delays - may fail. */
\r
665 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
666 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
667 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
669 /* Try to get process tree settings - may fail. */
\r
670 unsigned long kill_process_tree;
\r
671 if (get_number(key, NSSM_REG_KILL_PROCESS_TREE, &kill_process_tree, false) == 1) {
\r
672 if (kill_process_tree) service->kill_process_tree = true;
\r
673 else service->kill_process_tree = false;
\r
675 else service->kill_process_tree = true;
\r
677 /* Try to get default exit action. */
\r
678 bool default_action;
\r
679 service->default_exit_action = NSSM_EXIT_RESTART;
\r
680 TCHAR action_string[ACTION_LEN];
\r
681 if (! get_exit_action(service->name, 0, action_string, &default_action)) {
\r
682 for (int i = 0; exit_action_strings[i]; i++) {
\r
683 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
684 service->default_exit_action = i;
\r
690 /* Close registry */
\r
697 Sets the string for the exit action corresponding to the exit code.
\r
699 ret is a pointer to an unsigned long containing the exit code.
\r
700 If ret is NULL, we retrieve the default exit action unconditionally.
\r
702 action is a buffer which receives the string.
\r
704 default_action is a pointer to a bool which is set to false if there
\r
705 was an explicit string for the given exit code, or true if we are
\r
706 returning the default action.
\r
708 Returns: 0 on success.
\r
711 int get_exit_action(const TCHAR *service_name, unsigned long *ret, TCHAR *action, bool *default_action) {
\r
712 /* Are we returning the default action or a status-specific one? */
\r
713 *default_action = ! ret;
\r
715 /* Try to open the registry */
\r
716 HKEY key = open_registry(service_name, NSSM_REG_EXIT, KEY_READ);
\r
717 if (! key) return 1;
\r
719 unsigned long type = REG_SZ;
\r
720 unsigned long action_len = ACTION_LEN;
\r
723 if (! ret) code[0] = _T('\0');
\r
724 else if (_sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), *ret) < 0) {
\r
726 return get_exit_action(service_name, 0, action, default_action);
\r
728 if (RegQueryValueEx(key, code, 0, &type, (unsigned char *) action, &action_len) != ERROR_SUCCESS) {
\r
730 /* Try again with * as the key if an exit code was defined */
\r
731 if (ret) return get_exit_action(service_name, 0, action, default_action);
\r
735 /* Close registry */
\r
741 int set_hook(const TCHAR *service_name, const TCHAR *hook_event, const TCHAR *hook_action, TCHAR *cmd) {
\r
742 /* Try to open the registry */
\r
743 TCHAR registry[KEY_LENGTH];
\r
744 if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, _T("%s\\%s"), NSSM_REG_HOOK, hook_event) < 0) {
\r
745 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("hook registry"), _T("set_hook()"), 0);
\r
752 /* Don't create keys needlessly. */
\r
753 if (! _tcslen(cmd)) {
\r
754 key = open_registry(service_name, registry, KEY_READ, false);
\r
755 if (! key) return 0;
\r
756 error = RegQueryValueEx(key, hook_action, 0, 0, 0, 0);
\r
758 if (error == ERROR_FILE_NOT_FOUND) return 0;
\r
761 key = open_registry(service_name, registry, KEY_WRITE);
\r
762 if (! key) return 1;
\r
765 if (_tcslen(cmd)) ret = set_string(key, (TCHAR *) hook_action, cmd, true);
\r
767 error = RegDeleteValue(key, hook_action);
\r
768 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) ret = 0;
\r
771 /* Close registry */
\r
777 int get_hook(const TCHAR *service_name, const TCHAR *hook_event, const TCHAR *hook_action, TCHAR *buffer, unsigned long buflen) {
\r
778 /* Try to open the registry */
\r
779 TCHAR registry[KEY_LENGTH];
\r
780 if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, _T("%s\\%s"), NSSM_REG_HOOK, hook_event) < 0) {
\r
781 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("hook registry"), _T("get_hook()"), 0);
\r
784 HKEY key = open_registry(service_name, registry, KEY_READ, false);
\r
785 if (! key) return 1;
\r
787 int ret = expand_parameter(key, (TCHAR *) hook_action, buffer, buflen, true, false);
\r
789 /* Close registry */
\r