3 extern const TCHAR *exit_action_strings[];
\r
5 static int service_registry_path(const TCHAR *service_name, bool parameters, const TCHAR *sub, TCHAR *buffer, unsigned long buflen) {
\r
9 if (sub) ret = _sntprintf_s(buffer, buflen, _TRUNCATE, NSSM_REGISTRY _T("\\") NSSM_REG_PARAMETERS _T("\\%s"), service_name, sub);
\r
10 else ret = _sntprintf_s(buffer, buflen, _TRUNCATE, NSSM_REGISTRY _T("\\") NSSM_REG_PARAMETERS, service_name);
\r
12 else ret = _sntprintf_s(buffer, buflen, _TRUNCATE, NSSM_REGISTRY, service_name);
\r
17 static long open_registry_key(const TCHAR *registry, REGSAM sam, HKEY *key, bool must_exist) {
\r
20 if (sam & KEY_SET_VALUE) {
\r
21 error = RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, sam, 0, key, 0);
\r
22 if (error != ERROR_SUCCESS) {
\r
24 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
29 error = RegOpenKeyEx(HKEY_LOCAL_MACHINE, registry, 0, sam, key);
\r
30 if (error != ERROR_SUCCESS) {
\r
32 if (error != ERROR_FILE_NOT_FOUND || must_exist) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
39 static HKEY open_registry_key(const TCHAR *registry, REGSAM sam, bool must_exist) {
\r
41 long error = open_registry_key(registry, sam, &key, must_exist);
\r
45 int create_messages() {
\r
48 TCHAR registry[KEY_LENGTH];
\r
49 if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, _T("SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\%s"), NSSM) < 0) {
\r
50 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("eventlog registry"), _T("create_messages()"), 0);
\r
54 if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, KEY_WRITE, 0, &key, 0) != ERROR_SUCCESS) {
\r
55 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
59 /* Get path of this program */
\r
60 const TCHAR *path = nssm_unquoted_imagepath();
\r
62 /* Try to register the module but don't worry so much on failure */
\r
63 RegSetValueEx(key, _T("EventMessageFile"), 0, REG_SZ, (const unsigned char *) path, (unsigned long) (_tcslen(path) + 1) * sizeof(TCHAR));
\r
64 unsigned long types = EVENTLOG_INFORMATION_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_ERROR_TYPE;
\r
65 RegSetValueEx(key, _T("TypesSupported"), 0, REG_DWORD, (const unsigned char *) &types, sizeof(types));
\r
70 int create_parameters(nssm_service_t *service, bool editing) {
\r
71 /* Try to open the registry */
\r
72 HKEY key = open_registry(service->name, KEY_WRITE);
\r
73 if (! key) return 1;
\r
75 /* Remember parameters in case we need to delete them. */
\r
76 TCHAR registry[KEY_LENGTH];
\r
77 int ret = service_registry_path(service->name, true, 0, registry, _countof(registry));
\r
79 /* Try to create the parameters */
\r
80 if (set_expand_string(key, NSSM_REG_EXE, service->exe)) {
\r
81 if (ret > 0) RegDeleteKey(HKEY_LOCAL_MACHINE, registry);
\r
85 if (set_expand_string(key, NSSM_REG_FLAGS, service->flags)) {
\r
86 if (ret > 0) RegDeleteKey(HKEY_LOCAL_MACHINE, registry);
\r
90 if (set_expand_string(key, NSSM_REG_DIR, service->dir)) {
\r
91 if (ret > 0) RegDeleteKey(HKEY_LOCAL_MACHINE, registry);
\r
96 /* Other non-default parameters. May fail. */
\r
97 if (service->priority != NORMAL_PRIORITY_CLASS) set_number(key, NSSM_REG_PRIORITY, service->priority);
\r
98 else if (editing) RegDeleteValue(key, NSSM_REG_PRIORITY);
\r
99 if (service->affinity) {
\r
101 if (! affinity_mask_to_string(service->affinity, &string)) {
\r
102 if (RegSetValueEx(key, NSSM_REG_AFFINITY, 0, REG_SZ, (const unsigned char *) string, (unsigned long) (_tcslen(string) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
103 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_AFFINITY, error_string(GetLastError()), 0);
\r
104 HeapFree(GetProcessHeap(), 0, string);
\r
108 if (string) HeapFree(GetProcessHeap(), 0, string);
\r
110 else if (editing) RegDeleteValue(key, NSSM_REG_AFFINITY);
\r
111 unsigned long stop_method_skip = ~service->stop_method;
\r
112 if (stop_method_skip) set_number(key, NSSM_REG_STOP_METHOD_SKIP, stop_method_skip);
\r
113 else if (editing) RegDeleteValue(key, NSSM_REG_STOP_METHOD_SKIP);
\r
114 if (service->default_exit_action < NSSM_NUM_EXIT_ACTIONS) create_exit_action(service->name, exit_action_strings[service->default_exit_action], editing);
\r
115 if (service->restart_delay) set_number(key, NSSM_REG_RESTART_DELAY, service->restart_delay);
\r
116 else if (editing) RegDeleteValue(key, NSSM_REG_RESTART_DELAY);
\r
117 if (service->throttle_delay != NSSM_RESET_THROTTLE_RESTART) set_number(key, NSSM_REG_THROTTLE, service->throttle_delay);
\r
118 else if (editing) RegDeleteValue(key, NSSM_REG_THROTTLE);
\r
119 if (service->kill_console_delay != NSSM_KILL_CONSOLE_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, service->kill_console_delay);
\r
120 else if (editing) RegDeleteValue(key, NSSM_REG_KILL_CONSOLE_GRACE_PERIOD);
\r
121 if (service->kill_window_delay != NSSM_KILL_WINDOW_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_WINDOW_GRACE_PERIOD, service->kill_window_delay);
\r
122 else if (editing) RegDeleteValue(key, NSSM_REG_KILL_WINDOW_GRACE_PERIOD);
\r
123 if (service->kill_threads_delay != NSSM_KILL_THREADS_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_THREADS_GRACE_PERIOD, service->kill_threads_delay);
\r
124 else if (editing) RegDeleteValue(key, NSSM_REG_KILL_THREADS_GRACE_PERIOD);
\r
125 if (! service->kill_process_tree) set_number(key, NSSM_REG_KILL_PROCESS_TREE, 0);
\r
126 else if (editing) RegDeleteValue(key, NSSM_REG_KILL_PROCESS_TREE);
\r
127 if (service->stdin_path[0] || editing) {
\r
128 if (service->stdin_path[0]) set_expand_string(key, NSSM_REG_STDIN, service->stdin_path);
\r
129 else if (editing) RegDeleteValue(key, NSSM_REG_STDIN);
\r
130 if (service->stdin_sharing != NSSM_STDIN_SHARING) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_SHARING, service->stdin_sharing);
\r
131 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_SHARING);
\r
132 if (service->stdin_disposition != NSSM_STDIN_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_DISPOSITION, service->stdin_disposition);
\r
133 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_DISPOSITION);
\r
134 if (service->stdin_flags != NSSM_STDIN_FLAGS) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_FLAGS, service->stdin_flags);
\r
135 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_FLAGS);
\r
137 if (service->stdout_path[0] || editing) {
\r
138 if (service->stdout_path[0]) set_expand_string(key, NSSM_REG_STDOUT, service->stdout_path);
\r
139 else if (editing) RegDeleteValue(key, NSSM_REG_STDOUT);
\r
140 if (service->stdout_sharing != NSSM_STDOUT_SHARING) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_SHARING, service->stdout_sharing);
\r
141 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_SHARING);
\r
142 if (service->stdout_disposition != NSSM_STDOUT_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_DISPOSITION, service->stdout_disposition);
\r
143 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_DISPOSITION);
\r
144 if (service->stdout_flags != NSSM_STDOUT_FLAGS) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_FLAGS, service->stdout_flags);
\r
145 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_FLAGS);
\r
146 if (service->stdout_copy_and_truncate) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_COPY_AND_TRUNCATE, 1);
\r
147 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_COPY_AND_TRUNCATE);
\r
149 if (service->stderr_path[0] || editing) {
\r
150 if (service->stderr_path[0]) set_expand_string(key, NSSM_REG_STDERR, service->stderr_path);
\r
151 else if (editing) RegDeleteValue(key, NSSM_REG_STDERR);
\r
152 if (service->stderr_sharing != NSSM_STDERR_SHARING) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_SHARING, service->stderr_sharing);
\r
153 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_SHARING);
\r
154 if (service->stderr_disposition != NSSM_STDERR_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_DISPOSITION, service->stderr_disposition);
\r
155 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_DISPOSITION);
\r
156 if (service->stderr_flags != NSSM_STDERR_FLAGS) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_FLAGS, service->stderr_flags);
\r
157 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_FLAGS);
\r
158 if (service->stderr_copy_and_truncate) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_COPY_AND_TRUNCATE, 1);
\r
159 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_COPY_AND_TRUNCATE);
\r
161 if (service->rotate_files) set_number(key, NSSM_REG_ROTATE, 1);
\r
162 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE);
\r
163 if (service->rotate_stdout_online) set_number(key, NSSM_REG_ROTATE_ONLINE, 1);
\r
164 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_ONLINE);
\r
165 if (service->rotate_seconds) set_number(key, NSSM_REG_ROTATE_SECONDS, service->rotate_seconds);
\r
166 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_SECONDS);
\r
167 if (service->rotate_bytes_low) set_number(key, NSSM_REG_ROTATE_BYTES_LOW, service->rotate_bytes_low);
\r
168 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_BYTES_LOW);
\r
169 if (service->rotate_bytes_high) set_number(key, NSSM_REG_ROTATE_BYTES_HIGH, service->rotate_bytes_high);
\r
170 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_BYTES_HIGH);
\r
171 if (service->rotate_delay != NSSM_ROTATE_DELAY) set_number(key, NSSM_REG_ROTATE_DELAY, service->rotate_delay);
\r
172 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_DELAY);
\r
173 if (service->no_console) set_number(key, NSSM_REG_NO_CONSOLE, 1);
\r
174 else if (editing) RegDeleteValue(key, NSSM_REG_NO_CONSOLE);
\r
177 if (service->env) {
\r
178 if (RegSetValueEx(key, NSSM_REG_ENV, 0, REG_MULTI_SZ, (const unsigned char *) service->env, (unsigned long) service->envlen * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
179 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV, error_string(GetLastError()), 0);
\r
182 else if (editing) RegDeleteValue(key, NSSM_REG_ENV);
\r
183 if (service->env_extra) {
\r
184 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
185 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV_EXTRA, error_string(GetLastError()), 0);
\r
188 else if (editing) RegDeleteValue(key, NSSM_REG_ENV_EXTRA);
\r
190 /* Close registry. */
\r
196 int create_exit_action(TCHAR *service_name, const TCHAR *action_string, bool editing) {
\r
198 TCHAR registry[KEY_LENGTH];
\r
199 if (service_registry_path(service_name, true, NSSM_REG_EXIT, registry, _countof(registry)) < 0) {
\r
200 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("NSSM_REG_EXIT"), _T("create_exit_action()"), 0);
\r
204 /* Try to open the registry */
\r
206 unsigned long disposition;
\r
207 if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, KEY_WRITE, 0, &key, &disposition) != ERROR_SUCCESS) {
\r
208 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
212 /* Do nothing if the key already existed */
\r
213 if (disposition == REG_OPENED_EXISTING_KEY && ! editing) {
\r
218 /* Create the default value */
\r
219 if (RegSetValueEx(key, 0, 0, REG_SZ, (const unsigned char *) action_string, (unsigned long) (_tcslen(action_string) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
220 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_EXIT, error_string(GetLastError()), 0);
\r
225 /* Close registry */
\r
231 int get_environment(TCHAR *service_name, HKEY key, TCHAR *value, TCHAR **env, unsigned long *envlen) {
\r
232 unsigned long type = REG_MULTI_SZ;
\r
234 /* Dummy test to find buffer size */
\r
235 unsigned long ret = RegQueryValueEx(key, value, 0, &type, NULL, envlen);
\r
236 if (ret != ERROR_SUCCESS) {
\r
239 /* The service probably doesn't have any environment configured */
\r
240 if (ret == ERROR_FILE_NOT_FOUND) return 0;
\r
241 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
245 if (type != REG_MULTI_SZ) {
\r
248 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_INVALID_ENVIRONMENT_STRING_TYPE, value, service_name, 0);
\r
252 /* Probably not possible */
\r
253 if (! *envlen) return 0;
\r
255 /* Previously initialised? */
\r
256 if (*env) HeapFree(GetProcessHeap(), 0, *env);
\r
258 *env = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, *envlen);
\r
261 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, value, _T("get_environment()"), 0);
\r
265 /* Actually get the strings */
\r
266 ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) *env, envlen);
\r
267 if (ret != ERROR_SUCCESS) {
\r
268 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
269 HeapFree(GetProcessHeap(), 0, *env);
\r
279 int get_string(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool expand, bool sanitise, bool must_exist) {
\r
280 TCHAR *buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, datalen);
\r
282 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, value, _T("get_string()"), 0);
\r
286 ZeroMemory(data, datalen);
\r
288 unsigned long type = REG_EXPAND_SZ;
\r
289 unsigned long buflen = datalen;
\r
291 unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);
\r
292 if (ret != ERROR_SUCCESS) {
\r
293 unsigned long error = GetLastError();
\r
294 HeapFree(GetProcessHeap(), 0, buffer);
\r
296 if (ret == ERROR_FILE_NOT_FOUND) {
\r
297 if (! must_exist) return 0;
\r
300 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(error), 0);
\r
304 /* Paths aren't allowed to contain quotes. */
\r
305 if (sanitise) PathUnquoteSpaces(buffer);
\r
307 /* Do we want to expand the string? */
\r
309 if (type == REG_EXPAND_SZ) type = REG_SZ;
\r
312 /* Technically we shouldn't expand environment strings from REG_SZ values */
\r
313 if (type != REG_EXPAND_SZ) {
\r
314 memmove(data, buffer, buflen);
\r
315 HeapFree(GetProcessHeap(), 0, buffer);
\r
319 ret = ExpandEnvironmentStrings((TCHAR *) buffer, data, datalen);
\r
320 if (! ret || ret > datalen) {
\r
321 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_EXPANDENVIRONMENTSTRINGS_FAILED, buffer, error_string(GetLastError()), 0);
\r
322 HeapFree(GetProcessHeap(), 0, buffer);
\r
326 HeapFree(GetProcessHeap(), 0, buffer);
\r
330 int get_string(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise) {
\r
331 return get_string(key, value, data, datalen, false, sanitise, true);
\r
334 int expand_parameter(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise, bool must_exist) {
\r
335 return get_string(key, value, data, datalen, true, sanitise, must_exist);
\r
338 int expand_parameter(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise) {
\r
339 return expand_parameter(key, value, data, datalen, sanitise, true);
\r
343 Sets a string in the registry.
\r
344 Returns: 0 if it was set.
\r
347 int set_string(HKEY key, TCHAR *value, TCHAR *string, bool expand) {
\r
348 unsigned long type = expand ? REG_EXPAND_SZ : REG_SZ;
\r
349 if (RegSetValueEx(key, value, 0, type, (const unsigned char *) string, (unsigned long) (_tcslen(string) + 1) * sizeof(TCHAR)) == ERROR_SUCCESS) return 0;
\r
350 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
354 int set_string(HKEY key, TCHAR *value, TCHAR *string) {
\r
355 return set_string(key, value, string, false);
\r
358 int set_expand_string(HKEY key, TCHAR *value, TCHAR *string) {
\r
359 return set_string(key, value, string, true);
\r
363 Set an unsigned long in the registry.
\r
364 Returns: 0 if it was set.
\r
367 int set_number(HKEY key, TCHAR *value, unsigned long number) {
\r
368 if (RegSetValueEx(key, value, 0, REG_DWORD, (const unsigned char *) &number, sizeof(number)) == ERROR_SUCCESS) return 0;
\r
369 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
374 Query an unsigned long from the registry.
\r
375 Returns: 1 if a number was retrieved.
\r
376 0 if none was found and must_exist is false.
\r
377 -1 if none was found and must_exist is true.
\r
380 int get_number(HKEY key, TCHAR *value, unsigned long *number, bool must_exist) {
\r
381 unsigned long type = REG_DWORD;
\r
382 unsigned long number_len = sizeof(unsigned long);
\r
384 int ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) number, &number_len);
\r
385 if (ret == ERROR_SUCCESS) return 1;
\r
387 if (ret == ERROR_FILE_NOT_FOUND) {
\r
388 if (! must_exist) return 0;
\r
391 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
392 if (ret == ERROR_FILE_NOT_FOUND) return -1;
\r
397 int get_number(HKEY key, TCHAR *value, unsigned long *number) {
\r
398 return get_number(key, value, number, true);
\r
401 /* Replace NULL with CRLF. Leave NULL NULL as the end marker. */
\r
402 int format_double_null(TCHAR *dn, unsigned long dnlen, TCHAR **formatted, unsigned long *newlen) {
\r
403 unsigned long i, j;
\r
411 for (i = 0; i < dnlen; i++) if (! dn[i] && dn[i + 1]) ++*newlen;
\r
413 *formatted = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *newlen * sizeof(TCHAR));
\r
414 if (! *formatted) {
\r
419 for (i = 0, j = 0; i < dnlen; i++) {
\r
420 (*formatted)[j] = dn[i];
\r
423 (*formatted)[j] = _T('\r');
\r
424 (*formatted)[++j] = _T('\n');
\r
433 /* Strip CR and replace LF with NULL. */
\r
434 int unformat_double_null(TCHAR *dn, unsigned long dnlen, TCHAR **unformatted, unsigned long *newlen) {
\r
435 unsigned long i, j;
\r
443 for (i = 0; i < dnlen; i++) if (dn[i] != _T('\r')) ++*newlen;
\r
445 /* Skip blank lines. */
\r
446 for (i = 0; i < dnlen; i++) {
\r
447 if (dn[i] == _T('\r') && dn[i + 1] == _T('\n')) {
\r
448 /* This is the last CRLF. */
\r
449 if (i >= dnlen - 2) break;
\r
452 Strip at the start of the block or if the next characters are
\r
455 if (! i || (dn[i + 2] == _T('\r') && dn[i + 3] == _T('\n'))) {
\r
456 for (j = i + 2; j < dnlen; j++) dn[j - 2] = dn[j];
\r
457 dn[dnlen--] = _T('\0');
\r
458 dn[dnlen--] = _T('\0');
\r
465 /* Must end with two NULLs. */
\r
468 *unformatted = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *newlen * sizeof(TCHAR));
\r
469 if (! *unformatted) return 1;
\r
471 for (i = 0, j = 0; i < dnlen; i++) {
\r
472 if (dn[i] == _T('\r')) continue;
\r
473 if (dn[i] == _T('\n')) (*unformatted)[j] = _T('\0');
\r
474 else (*unformatted)[j] = dn[i];
\r
481 void override_milliseconds(TCHAR *service_name, HKEY key, TCHAR *value, unsigned long *buffer, unsigned long default_value, unsigned long event) {
\r
482 unsigned long type = REG_DWORD;
\r
483 unsigned long buflen = sizeof(unsigned long);
\r
485 unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);
\r
486 if (ret != ERROR_SUCCESS) {
\r
487 if (ret != ERROR_FILE_NOT_FOUND) {
\r
488 if (type != REG_DWORD) {
\r
489 TCHAR milliseconds[16];
\r
490 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), default_value);
\r
491 log_event(EVENTLOG_WARNING_TYPE, event, service_name, value, milliseconds, 0);
\r
493 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
498 if (! ok) *buffer = default_value;
\r
501 HKEY open_service_registry(const TCHAR *service_name, REGSAM sam, bool must_exist) {
\r
503 TCHAR registry[KEY_LENGTH];
\r
504 if (service_registry_path(service_name, false, 0, registry, _countof(registry)) < 0) {
\r
505 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REGISTRY, _T("open_service_registry()"), 0);
\r
509 return open_registry_key(registry, sam, must_exist);
\r
512 long open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam, HKEY *key, bool must_exist) {
\r
514 TCHAR registry[KEY_LENGTH];
\r
515 if (service_registry_path(service_name, true, sub, registry, _countof(registry)) < 0) {
\r
516 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REGISTRY, _T("open_registry()"), 0);
\r
520 return open_registry_key(registry, sam, key, must_exist);
\r
523 HKEY open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam, bool must_exist) {
\r
525 long error = open_registry(service_name, sub, sam, &key, must_exist);
\r
529 HKEY open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam) {
\r
530 return open_registry(service_name, sub, sam, true);
\r
533 HKEY open_registry(const TCHAR *service_name, REGSAM sam) {
\r
534 return open_registry(service_name, 0, sam, true);
\r
537 int get_io_parameters(nssm_service_t *service, HKEY key) {
\r
539 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
540 service->stdin_sharing = service->stdin_disposition = service->stdin_flags = 0;
\r
541 ZeroMemory(service->stdin_path, _countof(service->stdin_path) * sizeof(TCHAR));
\r
546 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
547 service->stdout_sharing = service->stdout_disposition = service->stdout_flags = 0;
\r
548 ZeroMemory(service->stdout_path, _countof(service->stdout_path) * sizeof(TCHAR));
\r
553 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
554 service->stderr_sharing = service->stderr_disposition = service->stderr_flags = 0;
\r
555 ZeroMemory(service->stderr_path, _countof(service->stderr_path) * sizeof(TCHAR));
\r
562 int get_parameters(nssm_service_t *service, STARTUPINFO *si) {
\r
565 /* Try to open the registry */
\r
566 HKEY key = open_registry(service->name, KEY_READ);
\r
567 if (! key) return 1;
\r
569 /* Don't expand parameters when retrieving for the GUI. */
\r
570 bool expand = si ? true : false;
\r
572 /* Try to get environment variables - may fail */
\r
573 get_environment(service->name, key, NSSM_REG_ENV, &service->env, &service->envlen);
\r
574 /* Environment variables to add to existing rather than replace - may fail. */
\r
575 get_environment(service->name, key, NSSM_REG_ENV_EXTRA, &service->env_extra, &service->env_extralen);
\r
577 /* Set environment if we are starting the service. */
\r
578 if (si) set_service_environment(service);
\r
580 /* Try to get executable file - MUST succeed */
\r
581 if (get_string(key, NSSM_REG_EXE, service->exe, sizeof(service->exe), expand, false, true)) {
\r
586 /* Try to get flags - may fail and we don't care */
\r
587 if (get_string(key, NSSM_REG_FLAGS, service->flags, sizeof(service->flags), expand, false, true)) {
\r
588 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_FLAGS, NSSM_REG_FLAGS, service->name, service->exe, 0);
\r
589 ZeroMemory(service->flags, sizeof(service->flags));
\r
592 /* Try to get startup directory - may fail and we fall back to a default */
\r
593 if (get_string(key, NSSM_REG_DIR, service->dir, sizeof(service->dir), expand, true, true) || ! service->dir[0]) {
\r
594 _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);
\r
595 strip_basename(service->dir);
\r
596 if (service->dir[0] == _T('\0')) {
\r
598 ret = GetWindowsDirectory(service->dir, sizeof(service->dir));
\r
599 if (! ret || ret > sizeof(service->dir)) {
\r
600 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_NO_DIR_AND_NO_FALLBACK, NSSM_REG_DIR, service->name, 0);
\r
605 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_DIR, NSSM_REG_DIR, service->name, service->dir, 0);
\r
608 /* Try to get processor affinity - may fail. */
\r
610 if (get_string(key, NSSM_REG_AFFINITY, buffer, sizeof(buffer), false, false, false) || ! buffer[0]) service->affinity = 0LL;
\r
611 else if (affinity_string_to_mask(buffer, &service->affinity)) {
\r
612 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_AFFINITY_MASK, service->name, buffer);
\r
613 service->affinity = 0LL;
\r
616 DWORD_PTR affinity, system_affinity;
\r
618 if (GetProcessAffinityMask(GetCurrentProcess(), &affinity, &system_affinity)) {
\r
619 _int64 effective_affinity = service->affinity & system_affinity;
\r
620 if (effective_affinity != service->affinity) {
\r
622 if (! affinity_mask_to_string(system_affinity, &system)) {
\r
623 TCHAR *effective = 0;
\r
624 if (! affinity_mask_to_string(effective_affinity, &effective)) {
\r
625 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_EFFECTIVE_AFFINITY_MASK, service->name, buffer, system, effective, 0);
\r
627 HeapFree(GetProcessHeap(), 0, effective);
\r
629 HeapFree(GetProcessHeap(), 0, system);
\r
634 /* Try to get priority - may fail. */
\r
635 unsigned long priority;
\r
636 if (get_number(key, NSSM_REG_PRIORITY, &priority, false) == 1) {
\r
637 if (priority == (priority & priority_mask())) service->priority = priority;
\r
638 else log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_PRIORITY, service->name, NSSM_REG_PRIORITY, 0);
\r
641 /* Try to get file rotation settings - may fail. */
\r
642 unsigned long rotate_files;
\r
643 if (get_number(key, NSSM_REG_ROTATE, &rotate_files, false) == 1) {
\r
644 if (rotate_files) service->rotate_files = true;
\r
645 else service->rotate_files = false;
\r
647 else service->rotate_files = false;
\r
648 if (get_number(key, NSSM_REG_ROTATE_ONLINE, &rotate_files, false) == 1) {
\r
649 if (rotate_files) service->rotate_stdout_online = service->rotate_stderr_online = true;
\r
650 else service->rotate_stdout_online = service->rotate_stderr_online = false;
\r
652 else service->rotate_stdout_online = service->rotate_stderr_online = false;
\r
653 if (get_number(key, NSSM_REG_ROTATE_SECONDS, &service->rotate_seconds, false) != 1) service->rotate_seconds = 0;
\r
654 if (get_number(key, NSSM_REG_ROTATE_BYTES_LOW, &service->rotate_bytes_low, false) != 1) service->rotate_bytes_low = 0;
\r
655 if (get_number(key, NSSM_REG_ROTATE_BYTES_HIGH, &service->rotate_bytes_high, false) != 1) service->rotate_bytes_high = 0;
\r
656 override_milliseconds(service->name, key, NSSM_REG_ROTATE_DELAY, &service->rotate_delay, NSSM_ROTATE_DELAY, NSSM_EVENT_BOGUS_THROTTLE);
\r
658 /* Try to get force new console setting - may fail. */
\r
659 if (get_number(key, NSSM_REG_NO_CONSOLE, &service->no_console, false) != 1) service->no_console = 0;
\r
661 /* Change to startup directory in case stdout/stderr are relative paths. */
\r
662 TCHAR cwd[PATH_LENGTH];
\r
663 GetCurrentDirectory(_countof(cwd), cwd);
\r
664 SetCurrentDirectory(service->dir);
\r
666 /* Try to get stdout and stderr */
\r
667 if (get_io_parameters(service, key)) {
\r
668 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);
\r
670 SetCurrentDirectory(cwd);
\r
674 /* Change back in case the startup directory needs to be deleted. */
\r
675 SetCurrentDirectory(cwd);
\r
677 /* Try to get mandatory restart delay */
\r
678 override_milliseconds(service->name, key, NSSM_REG_RESTART_DELAY, &service->restart_delay, 0, NSSM_EVENT_BOGUS_RESTART_DELAY);
\r
680 /* Try to get throttle restart delay */
\r
681 override_milliseconds(service->name, key, NSSM_REG_THROTTLE, &service->throttle_delay, NSSM_RESET_THROTTLE_RESTART, NSSM_EVENT_BOGUS_THROTTLE);
\r
683 /* Try to get service stop flags. */
\r
684 unsigned long type = REG_DWORD;
\r
685 unsigned long stop_method_skip;
\r
686 unsigned long buflen = sizeof(stop_method_skip);
\r
687 bool stop_ok = false;
\r
688 ret = RegQueryValueEx(key, NSSM_REG_STOP_METHOD_SKIP, 0, &type, (unsigned char *) &stop_method_skip, &buflen);
\r
689 if (ret != ERROR_SUCCESS) {
\r
690 if (ret != ERROR_FILE_NOT_FOUND) {
\r
691 if (type != REG_DWORD) {
\r
692 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_STOP_METHOD_SKIP, service->name, NSSM_REG_STOP_METHOD_SKIP, NSSM, 0);
\r
694 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, NSSM_REG_STOP_METHOD_SKIP, error_string(GetLastError()), 0);
\r
697 else stop_ok = true;
\r
699 /* Try all methods except those requested to be skipped. */
\r
700 service->stop_method = ~0;
\r
701 if (stop_ok) service->stop_method &= ~stop_method_skip;
\r
703 /* Try to get kill delays - may fail. */
\r
704 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
705 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
706 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
708 /* Try to get process tree settings - may fail. */
\r
709 unsigned long kill_process_tree;
\r
710 if (get_number(key, NSSM_REG_KILL_PROCESS_TREE, &kill_process_tree, false) == 1) {
\r
711 if (kill_process_tree) service->kill_process_tree = true;
\r
712 else service->kill_process_tree = false;
\r
714 else service->kill_process_tree = true;
\r
716 /* Try to get default exit action. */
\r
717 bool default_action;
\r
718 service->default_exit_action = NSSM_EXIT_RESTART;
\r
719 TCHAR action_string[ACTION_LEN];
\r
720 if (! get_exit_action(service->name, 0, action_string, &default_action)) {
\r
721 for (int i = 0; exit_action_strings[i]; i++) {
\r
722 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
723 service->default_exit_action = i;
\r
729 /* Close registry */
\r
736 Sets the string for the exit action corresponding to the exit code.
\r
738 ret is a pointer to an unsigned long containing the exit code.
\r
739 If ret is NULL, we retrieve the default exit action unconditionally.
\r
741 action is a buffer which receives the string.
\r
743 default_action is a pointer to a bool which is set to false if there
\r
744 was an explicit string for the given exit code, or true if we are
\r
745 returning the default action.
\r
747 Returns: 0 on success.
\r
750 int get_exit_action(const TCHAR *service_name, unsigned long *ret, TCHAR *action, bool *default_action) {
\r
751 /* Are we returning the default action or a status-specific one? */
\r
752 *default_action = ! ret;
\r
754 /* Try to open the registry */
\r
755 HKEY key = open_registry(service_name, NSSM_REG_EXIT, KEY_READ);
\r
756 if (! key) return 1;
\r
758 unsigned long type = REG_SZ;
\r
759 unsigned long action_len = ACTION_LEN;
\r
762 if (! ret) code[0] = _T('\0');
\r
763 else if (_sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), *ret) < 0) {
\r
765 return get_exit_action(service_name, 0, action, default_action);
\r
767 if (RegQueryValueEx(key, code, 0, &type, (unsigned char *) action, &action_len) != ERROR_SUCCESS) {
\r
769 /* Try again with * as the key if an exit code was defined */
\r
770 if (ret) return get_exit_action(service_name, 0, action, default_action);
\r
774 /* Close registry */
\r
780 int set_hook(const TCHAR *service_name, const TCHAR *hook_event, const TCHAR *hook_action, TCHAR *cmd) {
\r
781 /* Try to open the registry */
\r
782 TCHAR registry[KEY_LENGTH];
\r
783 if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, _T("%s\\%s"), NSSM_REG_HOOK, hook_event) < 0) {
\r
784 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("hook registry"), _T("set_hook()"), 0);
\r
791 /* Don't create keys needlessly. */
\r
792 if (! _tcslen(cmd)) {
\r
793 key = open_registry(service_name, registry, KEY_READ, false);
\r
794 if (! key) return 0;
\r
795 error = RegQueryValueEx(key, hook_action, 0, 0, 0, 0);
\r
797 if (error == ERROR_FILE_NOT_FOUND) return 0;
\r
800 key = open_registry(service_name, registry, KEY_WRITE);
\r
801 if (! key) return 1;
\r
804 if (_tcslen(cmd)) ret = set_string(key, (TCHAR *) hook_action, cmd, true);
\r
806 error = RegDeleteValue(key, hook_action);
\r
807 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) ret = 0;
\r
810 /* Close registry */
\r
816 int get_hook(const TCHAR *service_name, const TCHAR *hook_event, const TCHAR *hook_action, TCHAR *buffer, unsigned long buflen) {
\r
817 /* Try to open the registry */
\r
818 TCHAR registry[KEY_LENGTH];
\r
819 if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, _T("%s\\%s"), NSSM_REG_HOOK, hook_event) < 0) {
\r
820 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("hook registry"), _T("get_hook()"), 0);
\r
824 long error = open_registry(service_name, registry, KEY_READ, &key, false);
\r
826 if (error == ERROR_FILE_NOT_FOUND) {
\r
827 ZeroMemory(buffer, buflen);
\r
833 int ret = expand_parameter(key, (TCHAR *) hook_action, buffer, buflen, true, false);
\r
835 /* Close registry */
\r