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
359 int set_expand_string(HKEY key, TCHAR *value, TCHAR *string) {
\r
360 return set_string(key, value, string, true);
\r
365 Set an unsigned long in the registry.
\r
366 Returns: 0 if it was set.
\r
369 int set_number(HKEY key, TCHAR *value, unsigned long number) {
\r
370 if (RegSetValueEx(key, value, 0, REG_DWORD, (const unsigned char *) &number, sizeof(number)) == ERROR_SUCCESS) return 0;
\r
371 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
376 Query an unsigned long from the registry.
\r
377 Returns: 1 if a number was retrieved.
\r
378 0 if none was found and must_exist is false.
\r
379 -1 if none was found and must_exist is true.
\r
382 int get_number(HKEY key, TCHAR *value, unsigned long *number, bool must_exist) {
\r
383 unsigned long type = REG_DWORD;
\r
384 unsigned long number_len = sizeof(unsigned long);
\r
386 int ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) number, &number_len);
\r
387 if (ret == ERROR_SUCCESS) return 1;
\r
389 if (ret == ERROR_FILE_NOT_FOUND) {
\r
390 if (! must_exist) return 0;
\r
393 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
394 if (ret == ERROR_FILE_NOT_FOUND) return -1;
\r
399 int get_number(HKEY key, TCHAR *value, unsigned long *number) {
\r
400 return get_number(key, value, number, true);
\r
403 /* Replace NULL with CRLF. Leave NULL NULL as the end marker. */
\r
404 int format_double_null(TCHAR *dn, unsigned long dnlen, TCHAR **formatted, unsigned long *newlen) {
\r
405 unsigned long i, j;
\r
413 for (i = 0; i < dnlen; i++) if (! dn[i] && dn[i + 1]) ++*newlen;
\r
415 *formatted = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *newlen * sizeof(TCHAR));
\r
416 if (! *formatted) {
\r
421 for (i = 0, j = 0; i < dnlen; i++) {
\r
422 (*formatted)[j] = dn[i];
\r
425 (*formatted)[j] = _T('\r');
\r
426 (*formatted)[++j] = _T('\n');
\r
435 /* Strip CR and replace LF with NULL. */
\r
436 int unformat_double_null(TCHAR *dn, unsigned long dnlen, TCHAR **unformatted, unsigned long *newlen) {
\r
437 unsigned long i, j;
\r
445 for (i = 0; i < dnlen; i++) if (dn[i] != _T('\r')) ++*newlen;
\r
447 /* Skip blank lines. */
\r
448 for (i = 0; i < dnlen; i++) {
\r
449 if (dn[i] == _T('\r') && dn[i + 1] == _T('\n')) {
\r
450 /* This is the last CRLF. */
\r
451 if (i >= dnlen - 2) break;
\r
454 Strip at the start of the block or if the next characters are
\r
457 if (! i || (dn[i + 2] == _T('\r') && dn[i + 3] == _T('\n'))) {
\r
458 for (j = i + 2; j < dnlen; j++) dn[j - 2] = dn[j];
\r
459 dn[dnlen--] = _T('\0');
\r
460 dn[dnlen--] = _T('\0');
\r
467 /* Must end with two NULLs. */
\r
470 *unformatted = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *newlen * sizeof(TCHAR));
\r
471 if (! *unformatted) return 1;
\r
473 for (i = 0, j = 0; i < dnlen; i++) {
\r
474 if (dn[i] == _T('\r')) continue;
\r
475 if (dn[i] == _T('\n')) (*unformatted)[j] = _T('\0');
\r
476 else (*unformatted)[j] = dn[i];
\r
483 void override_milliseconds(TCHAR *service_name, HKEY key, TCHAR *value, unsigned long *buffer, unsigned long default_value, unsigned long event) {
\r
484 unsigned long type = REG_DWORD;
\r
485 unsigned long buflen = sizeof(unsigned long);
\r
487 unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);
\r
488 if (ret != ERROR_SUCCESS) {
\r
489 if (ret != ERROR_FILE_NOT_FOUND) {
\r
490 if (type != REG_DWORD) {
\r
491 TCHAR milliseconds[16];
\r
492 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), default_value);
\r
493 log_event(EVENTLOG_WARNING_TYPE, event, service_name, value, milliseconds, 0);
\r
495 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
500 if (! ok) *buffer = default_value;
\r
503 HKEY open_service_registry(const TCHAR *service_name, REGSAM sam, bool must_exist) {
\r
505 TCHAR registry[KEY_LENGTH];
\r
506 if (service_registry_path(service_name, false, 0, registry, _countof(registry)) < 0) {
\r
507 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REGISTRY, _T("open_service_registry()"), 0);
\r
511 return open_registry_key(registry, sam, must_exist);
\r
514 long open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam, HKEY *key, bool must_exist) {
\r
516 TCHAR registry[KEY_LENGTH];
\r
517 if (service_registry_path(service_name, true, sub, registry, _countof(registry)) < 0) {
\r
518 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REGISTRY, _T("open_registry()"), 0);
\r
522 return open_registry_key(registry, sam, key, must_exist);
\r
525 HKEY open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam, bool must_exist) {
\r
527 long error = open_registry(service_name, sub, sam, &key, true);
\r
531 HKEY open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam) {
\r
532 return open_registry(service_name, sub, sam, true);
\r
535 HKEY open_registry(const TCHAR *service_name, REGSAM sam) {
\r
536 return open_registry(service_name, 0, sam, true);
\r
539 int get_io_parameters(nssm_service_t *service, HKEY key) {
\r
541 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
542 service->stdin_sharing = service->stdin_disposition = service->stdin_flags = 0;
\r
543 ZeroMemory(service->stdin_path, _countof(service->stdin_path) * sizeof(TCHAR));
\r
548 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
549 service->stdout_sharing = service->stdout_disposition = service->stdout_flags = 0;
\r
550 ZeroMemory(service->stdout_path, _countof(service->stdout_path) * sizeof(TCHAR));
\r
555 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
556 service->stderr_sharing = service->stderr_disposition = service->stderr_flags = 0;
\r
557 ZeroMemory(service->stderr_path, _countof(service->stderr_path) * sizeof(TCHAR));
\r
564 int get_parameters(nssm_service_t *service, STARTUPINFO *si) {
\r
567 /* Try to open the registry */
\r
568 HKEY key = open_registry(service->name, KEY_READ);
\r
569 if (! key) return 1;
\r
571 /* Don't expand parameters when retrieving for the GUI. */
\r
572 bool expand = si ? true : false;
\r
574 /* Try to get environment variables - may fail */
\r
575 get_environment(service->name, key, NSSM_REG_ENV, &service->env, &service->envlen);
\r
576 /* Environment variables to add to existing rather than replace - may fail. */
\r
577 get_environment(service->name, key, NSSM_REG_ENV_EXTRA, &service->env_extra, &service->env_extralen);
\r
579 /* Set environment if we are starting the service. */
\r
580 if (si) set_service_environment(service);
\r
582 /* Try to get executable file - MUST succeed */
\r
583 if (get_string(key, NSSM_REG_EXE, service->exe, sizeof(service->exe), expand, false, true)) {
\r
588 /* Try to get flags - may fail and we don't care */
\r
589 if (get_string(key, NSSM_REG_FLAGS, service->flags, sizeof(service->flags), expand, false, true)) {
\r
590 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_FLAGS, NSSM_REG_FLAGS, service->name, service->exe, 0);
\r
591 ZeroMemory(service->flags, sizeof(service->flags));
\r
594 /* Try to get startup directory - may fail and we fall back to a default */
\r
595 if (get_string(key, NSSM_REG_DIR, service->dir, sizeof(service->dir), expand, true, true) || ! service->dir[0]) {
\r
596 _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);
\r
597 strip_basename(service->dir);
\r
598 if (service->dir[0] == _T('\0')) {
\r
600 ret = GetWindowsDirectory(service->dir, sizeof(service->dir));
\r
601 if (! ret || ret > sizeof(service->dir)) {
\r
602 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_NO_DIR_AND_NO_FALLBACK, NSSM_REG_DIR, service->name, 0);
\r
607 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_DIR, NSSM_REG_DIR, service->name, service->dir, 0);
\r
610 /* Try to get processor affinity - may fail. */
\r
612 if (get_string(key, NSSM_REG_AFFINITY, buffer, sizeof(buffer), false, false, false) || ! buffer[0]) service->affinity = 0LL;
\r
613 else if (affinity_string_to_mask(buffer, &service->affinity)) {
\r
614 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_AFFINITY_MASK, service->name, buffer);
\r
615 service->affinity = 0LL;
\r
618 DWORD_PTR affinity, system_affinity;
\r
620 if (GetProcessAffinityMask(GetCurrentProcess(), &affinity, &system_affinity)) {
\r
621 _int64 effective_affinity = service->affinity & system_affinity;
\r
622 if (effective_affinity != service->affinity) {
\r
624 if (! affinity_mask_to_string(system_affinity, &system)) {
\r
625 TCHAR *effective = 0;
\r
626 if (! affinity_mask_to_string(effective_affinity, &effective)) {
\r
627 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_EFFECTIVE_AFFINITY_MASK, service->name, buffer, system, effective, 0);
\r
629 HeapFree(GetProcessHeap(), 0, effective);
\r
631 HeapFree(GetProcessHeap(), 0, system);
\r
636 /* Try to get priority - may fail. */
\r
637 unsigned long priority;
\r
638 if (get_number(key, NSSM_REG_PRIORITY, &priority, false) == 1) {
\r
639 if (priority == (priority & priority_mask())) service->priority = priority;
\r
640 else log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_PRIORITY, service->name, NSSM_REG_PRIORITY, 0);
\r
643 /* Try to get file rotation settings - may fail. */
\r
644 unsigned long rotate_files;
\r
645 if (get_number(key, NSSM_REG_ROTATE, &rotate_files, false) == 1) {
\r
646 if (rotate_files) service->rotate_files = true;
\r
647 else service->rotate_files = false;
\r
649 else service->rotate_files = false;
\r
650 if (get_number(key, NSSM_REG_ROTATE_ONLINE, &rotate_files, false) == 1) {
\r
651 if (rotate_files) service->rotate_stdout_online = service->rotate_stderr_online = true;
\r
652 else service->rotate_stdout_online = service->rotate_stderr_online = false;
\r
654 else service->rotate_stdout_online = service->rotate_stderr_online = false;
\r
655 if (get_number(key, NSSM_REG_ROTATE_SECONDS, &service->rotate_seconds, false) != 1) service->rotate_seconds = 0;
\r
656 if (get_number(key, NSSM_REG_ROTATE_BYTES_LOW, &service->rotate_bytes_low, false) != 1) service->rotate_bytes_low = 0;
\r
657 if (get_number(key, NSSM_REG_ROTATE_BYTES_HIGH, &service->rotate_bytes_high, false) != 1) service->rotate_bytes_high = 0;
\r
658 override_milliseconds(service->name, key, NSSM_REG_ROTATE_DELAY, &service->rotate_delay, NSSM_ROTATE_DELAY, NSSM_EVENT_BOGUS_THROTTLE);
\r
660 /* Try to get force new console setting - may fail. */
\r
661 if (get_number(key, NSSM_REG_NO_CONSOLE, &service->no_console, false) != 1) service->no_console = 0;
\r
663 /* Change to startup directory in case stdout/stderr are relative paths. */
\r
664 TCHAR cwd[PATH_LENGTH];
\r
665 GetCurrentDirectory(_countof(cwd), cwd);
\r
666 SetCurrentDirectory(service->dir);
\r
668 /* Try to get stdout and stderr */
\r
669 if (get_io_parameters(service, key)) {
\r
670 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);
\r
672 SetCurrentDirectory(cwd);
\r
676 /* Change back in case the startup directory needs to be deleted. */
\r
677 SetCurrentDirectory(cwd);
\r
679 /* Try to get mandatory restart delay */
\r
680 override_milliseconds(service->name, key, NSSM_REG_RESTART_DELAY, &service->restart_delay, 0, NSSM_EVENT_BOGUS_RESTART_DELAY);
\r
682 /* Try to get throttle restart delay */
\r
683 override_milliseconds(service->name, key, NSSM_REG_THROTTLE, &service->throttle_delay, NSSM_RESET_THROTTLE_RESTART, NSSM_EVENT_BOGUS_THROTTLE);
\r
685 /* Try to get service stop flags. */
\r
686 unsigned long type = REG_DWORD;
\r
687 unsigned long stop_method_skip;
\r
688 unsigned long buflen = sizeof(stop_method_skip);
\r
689 bool stop_ok = false;
\r
690 ret = RegQueryValueEx(key, NSSM_REG_STOP_METHOD_SKIP, 0, &type, (unsigned char *) &stop_method_skip, &buflen);
\r
691 if (ret != ERROR_SUCCESS) {
\r
692 if (ret != ERROR_FILE_NOT_FOUND) {
\r
693 if (type != REG_DWORD) {
\r
694 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_STOP_METHOD_SKIP, service->name, NSSM_REG_STOP_METHOD_SKIP, NSSM, 0);
\r
696 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, NSSM_REG_STOP_METHOD_SKIP, error_string(GetLastError()), 0);
\r
699 else stop_ok = true;
\r
701 /* Try all methods except those requested to be skipped. */
\r
702 service->stop_method = ~0;
\r
703 if (stop_ok) service->stop_method &= ~stop_method_skip;
\r
705 /* Try to get kill delays - may fail. */
\r
706 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
707 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
708 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
710 /* Try to get process tree settings - may fail. */
\r
711 unsigned long kill_process_tree;
\r
712 if (get_number(key, NSSM_REG_KILL_PROCESS_TREE, &kill_process_tree, false) == 1) {
\r
713 if (kill_process_tree) service->kill_process_tree = true;
\r
714 else service->kill_process_tree = false;
\r
716 else service->kill_process_tree = true;
\r
718 /* Try to get default exit action. */
\r
719 bool default_action;
\r
720 service->default_exit_action = NSSM_EXIT_RESTART;
\r
721 TCHAR action_string[ACTION_LEN];
\r
722 if (! get_exit_action(service->name, 0, action_string, &default_action)) {
\r
723 for (int i = 0; exit_action_strings[i]; i++) {
\r
724 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
725 service->default_exit_action = i;
\r
731 /* Close registry */
\r
738 Sets the string for the exit action corresponding to the exit code.
\r
740 ret is a pointer to an unsigned long containing the exit code.
\r
741 If ret is NULL, we retrieve the default exit action unconditionally.
\r
743 action is a buffer which receives the string.
\r
745 default_action is a pointer to a bool which is set to false if there
\r
746 was an explicit string for the given exit code, or true if we are
\r
747 returning the default action.
\r
749 Returns: 0 on success.
\r
752 int get_exit_action(const TCHAR *service_name, unsigned long *ret, TCHAR *action, bool *default_action) {
\r
753 /* Are we returning the default action or a status-specific one? */
\r
754 *default_action = ! ret;
\r
756 /* Try to open the registry */
\r
757 HKEY key = open_registry(service_name, NSSM_REG_EXIT, KEY_READ);
\r
758 if (! key) return 1;
\r
760 unsigned long type = REG_SZ;
\r
761 unsigned long action_len = ACTION_LEN;
\r
764 if (! ret) code[0] = _T('\0');
\r
765 else if (_sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), *ret) < 0) {
\r
767 return get_exit_action(service_name, 0, action, default_action);
\r
769 if (RegQueryValueEx(key, code, 0, &type, (unsigned char *) action, &action_len) != ERROR_SUCCESS) {
\r
771 /* Try again with * as the key if an exit code was defined */
\r
772 if (ret) return get_exit_action(service_name, 0, action, default_action);
\r
776 /* Close registry */
\r
782 int set_hook(const TCHAR *service_name, const TCHAR *hook_event, const TCHAR *hook_action, TCHAR *cmd) {
\r
783 /* Try to open the registry */
\r
784 TCHAR registry[KEY_LENGTH];
\r
785 if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, _T("%s\\%s"), NSSM_REG_HOOK, hook_event) < 0) {
\r
786 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("hook registry"), _T("set_hook()"), 0);
\r
793 /* Don't create keys needlessly. */
\r
794 if (! _tcslen(cmd)) {
\r
795 key = open_registry(service_name, registry, KEY_READ, false);
\r
796 if (! key) return 0;
\r
797 error = RegQueryValueEx(key, hook_action, 0, 0, 0, 0);
\r
799 if (error == ERROR_FILE_NOT_FOUND) return 0;
\r
802 key = open_registry(service_name, registry, KEY_WRITE);
\r
803 if (! key) return 1;
\r
806 if (_tcslen(cmd)) ret = set_string(key, (TCHAR *) hook_action, cmd, true);
\r
808 error = RegDeleteValue(key, hook_action);
\r
809 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) ret = 0;
\r
812 /* Close registry */
\r
818 int get_hook(const TCHAR *service_name, const TCHAR *hook_event, const TCHAR *hook_action, TCHAR *buffer, unsigned long buflen) {
\r
819 /* Try to open the registry */
\r
820 TCHAR registry[KEY_LENGTH];
\r
821 if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, _T("%s\\%s"), NSSM_REG_HOOK, hook_event) < 0) {
\r
822 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("hook registry"), _T("get_hook()"), 0);
\r
825 HKEY key = open_registry(service_name, registry, KEY_READ, false);
\r
826 if (! key) return 1;
\r
828 int ret = expand_parameter(key, (TCHAR *) hook_action, buffer, buflen, true, false);
\r
830 /* Close registry */
\r