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->hook_share_output_handles) set_number(key, NSSM_REG_HOOK_SHARE_OUTPUT_HANDLES, 1);
\r
162 else if (editing) RegDeleteValue(key, NSSM_REG_HOOK_SHARE_OUTPUT_HANDLES);
\r
163 if (service->rotate_files) set_number(key, NSSM_REG_ROTATE, 1);
\r
164 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE);
\r
165 if (service->rotate_stdout_online) set_number(key, NSSM_REG_ROTATE_ONLINE, 1);
\r
166 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_ONLINE);
\r
167 if (service->rotate_seconds) set_number(key, NSSM_REG_ROTATE_SECONDS, service->rotate_seconds);
\r
168 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_SECONDS);
\r
169 if (service->rotate_bytes_low) set_number(key, NSSM_REG_ROTATE_BYTES_LOW, service->rotate_bytes_low);
\r
170 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_BYTES_LOW);
\r
171 if (service->rotate_bytes_high) set_number(key, NSSM_REG_ROTATE_BYTES_HIGH, service->rotate_bytes_high);
\r
172 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_BYTES_HIGH);
\r
173 if (service->rotate_delay != NSSM_ROTATE_DELAY) set_number(key, NSSM_REG_ROTATE_DELAY, service->rotate_delay);
\r
174 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_DELAY);
\r
175 if (service->no_console) set_number(key, NSSM_REG_NO_CONSOLE, 1);
\r
176 else if (editing) RegDeleteValue(key, NSSM_REG_NO_CONSOLE);
\r
179 if (service->env) {
\r
180 if (RegSetValueEx(key, NSSM_REG_ENV, 0, REG_MULTI_SZ, (const unsigned char *) service->env, (unsigned long) service->envlen * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
181 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV, error_string(GetLastError()), 0);
\r
184 else if (editing) RegDeleteValue(key, NSSM_REG_ENV);
\r
185 if (service->env_extra) {
\r
186 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
187 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV_EXTRA, error_string(GetLastError()), 0);
\r
190 else if (editing) RegDeleteValue(key, NSSM_REG_ENV_EXTRA);
\r
192 /* Close registry. */
\r
198 int create_exit_action(TCHAR *service_name, const TCHAR *action_string, bool editing) {
\r
200 TCHAR registry[KEY_LENGTH];
\r
201 if (service_registry_path(service_name, true, NSSM_REG_EXIT, registry, _countof(registry)) < 0) {
\r
202 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("NSSM_REG_EXIT"), _T("create_exit_action()"), 0);
\r
206 /* Try to open the registry */
\r
208 unsigned long disposition;
\r
209 if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, KEY_WRITE, 0, &key, &disposition) != ERROR_SUCCESS) {
\r
210 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
214 /* Do nothing if the key already existed */
\r
215 if (disposition == REG_OPENED_EXISTING_KEY && ! editing) {
\r
220 /* Create the default value */
\r
221 if (RegSetValueEx(key, 0, 0, REG_SZ, (const unsigned char *) action_string, (unsigned long) (_tcslen(action_string) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
222 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_EXIT, error_string(GetLastError()), 0);
\r
227 /* Close registry */
\r
233 int get_environment(TCHAR *service_name, HKEY key, TCHAR *value, TCHAR **env, unsigned long *envlen) {
\r
234 unsigned long type = REG_MULTI_SZ;
\r
236 /* Dummy test to find buffer size */
\r
237 unsigned long ret = RegQueryValueEx(key, value, 0, &type, NULL, envlen);
\r
238 if (ret != ERROR_SUCCESS) {
\r
241 /* The service probably doesn't have any environment configured */
\r
242 if (ret == ERROR_FILE_NOT_FOUND) return 0;
\r
243 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
247 if (type != REG_MULTI_SZ) {
\r
250 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_INVALID_ENVIRONMENT_STRING_TYPE, value, service_name, 0);
\r
254 /* Probably not possible */
\r
255 if (! *envlen) return 0;
\r
257 /* Previously initialised? */
\r
258 if (*env) HeapFree(GetProcessHeap(), 0, *env);
\r
260 *env = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, *envlen);
\r
263 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, value, _T("get_environment()"), 0);
\r
267 /* Actually get the strings */
\r
268 ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) *env, envlen);
\r
269 if (ret != ERROR_SUCCESS) {
\r
270 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
271 HeapFree(GetProcessHeap(), 0, *env);
\r
281 int get_string(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool expand, bool sanitise, bool must_exist) {
\r
282 TCHAR *buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, datalen);
\r
284 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, value, _T("get_string()"), 0);
\r
288 ZeroMemory(data, datalen);
\r
290 unsigned long type = REG_EXPAND_SZ;
\r
291 unsigned long buflen = datalen;
\r
293 unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);
\r
294 if (ret != ERROR_SUCCESS) {
\r
295 unsigned long error = GetLastError();
\r
296 HeapFree(GetProcessHeap(), 0, buffer);
\r
298 if (ret == ERROR_FILE_NOT_FOUND) {
\r
299 if (! must_exist) return 0;
\r
302 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(error), 0);
\r
306 /* Paths aren't allowed to contain quotes. */
\r
307 if (sanitise) PathUnquoteSpaces(buffer);
\r
309 /* Do we want to expand the string? */
\r
311 if (type == REG_EXPAND_SZ) type = REG_SZ;
\r
314 /* Technically we shouldn't expand environment strings from REG_SZ values */
\r
315 if (type != REG_EXPAND_SZ) {
\r
316 memmove(data, buffer, buflen);
\r
317 HeapFree(GetProcessHeap(), 0, buffer);
\r
321 ret = ExpandEnvironmentStrings((TCHAR *) buffer, data, datalen);
\r
322 if (! ret || ret > datalen) {
\r
323 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_EXPANDENVIRONMENTSTRINGS_FAILED, buffer, error_string(GetLastError()), 0);
\r
324 HeapFree(GetProcessHeap(), 0, buffer);
\r
328 HeapFree(GetProcessHeap(), 0, buffer);
\r
332 int get_string(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise) {
\r
333 return get_string(key, value, data, datalen, false, sanitise, true);
\r
336 int expand_parameter(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise, bool must_exist) {
\r
337 return get_string(key, value, data, datalen, true, sanitise, must_exist);
\r
340 int expand_parameter(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise) {
\r
341 return expand_parameter(key, value, data, datalen, sanitise, true);
\r
345 Sets a string in the registry.
\r
346 Returns: 0 if it was set.
\r
349 int set_string(HKEY key, TCHAR *value, TCHAR *string, bool expand) {
\r
350 unsigned long type = expand ? REG_EXPAND_SZ : REG_SZ;
\r
351 if (RegSetValueEx(key, value, 0, type, (const unsigned char *) string, (unsigned long) (_tcslen(string) + 1) * sizeof(TCHAR)) == ERROR_SUCCESS) return 0;
\r
352 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
356 int set_string(HKEY key, TCHAR *value, TCHAR *string) {
\r
357 return set_string(key, value, string, false);
\r
360 int set_expand_string(HKEY key, TCHAR *value, TCHAR *string) {
\r
361 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, must_exist);
\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 hook I/O sharing - may fail. */
\r
644 unsigned long hook_share_output_handles;
\r
645 if (get_number(key, NSSM_REG_HOOK_SHARE_OUTPUT_HANDLES, &hook_share_output_handles, false) == 1) {
\r
646 if (hook_share_output_handles) service->hook_share_output_handles = true;
\r
647 else service->hook_share_output_handles = false;
\r
649 else hook_share_output_handles = false;
\r
650 /* Try to get file rotation settings - may fail. */
\r
651 unsigned long rotate_files;
\r
652 if (get_number(key, NSSM_REG_ROTATE, &rotate_files, false) == 1) {
\r
653 if (rotate_files) service->rotate_files = true;
\r
654 else service->rotate_files = false;
\r
656 else service->rotate_files = false;
\r
657 if (get_number(key, NSSM_REG_ROTATE_ONLINE, &rotate_files, false) == 1) {
\r
658 if (rotate_files) service->rotate_stdout_online = service->rotate_stderr_online = true;
\r
659 else service->rotate_stdout_online = service->rotate_stderr_online = false;
\r
661 else service->rotate_stdout_online = service->rotate_stderr_online = false;
\r
662 /* Hook I/O sharing and online rotation need a pipe. */
\r
663 service->use_stdout_pipe = service->rotate_stdout_online || hook_share_output_handles;
\r
664 service->use_stderr_pipe = service->rotate_stderr_online || hook_share_output_handles;
\r
665 if (get_number(key, NSSM_REG_ROTATE_SECONDS, &service->rotate_seconds, false) != 1) service->rotate_seconds = 0;
\r
666 if (get_number(key, NSSM_REG_ROTATE_BYTES_LOW, &service->rotate_bytes_low, false) != 1) service->rotate_bytes_low = 0;
\r
667 if (get_number(key, NSSM_REG_ROTATE_BYTES_HIGH, &service->rotate_bytes_high, false) != 1) service->rotate_bytes_high = 0;
\r
668 override_milliseconds(service->name, key, NSSM_REG_ROTATE_DELAY, &service->rotate_delay, NSSM_ROTATE_DELAY, NSSM_EVENT_BOGUS_THROTTLE);
\r
670 /* Try to get force new console setting - may fail. */
\r
671 if (get_number(key, NSSM_REG_NO_CONSOLE, &service->no_console, false) != 1) service->no_console = 0;
\r
673 /* Change to startup directory in case stdout/stderr are relative paths. */
\r
674 TCHAR cwd[PATH_LENGTH];
\r
675 GetCurrentDirectory(_countof(cwd), cwd);
\r
676 SetCurrentDirectory(service->dir);
\r
678 /* Try to get stdout and stderr */
\r
679 if (get_io_parameters(service, key)) {
\r
680 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);
\r
682 SetCurrentDirectory(cwd);
\r
686 /* Change back in case the startup directory needs to be deleted. */
\r
687 SetCurrentDirectory(cwd);
\r
689 /* Try to get mandatory restart delay */
\r
690 override_milliseconds(service->name, key, NSSM_REG_RESTART_DELAY, &service->restart_delay, 0, NSSM_EVENT_BOGUS_RESTART_DELAY);
\r
692 /* Try to get throttle restart delay */
\r
693 override_milliseconds(service->name, key, NSSM_REG_THROTTLE, &service->throttle_delay, NSSM_RESET_THROTTLE_RESTART, NSSM_EVENT_BOGUS_THROTTLE);
\r
695 /* Try to get service stop flags. */
\r
696 unsigned long type = REG_DWORD;
\r
697 unsigned long stop_method_skip;
\r
698 unsigned long buflen = sizeof(stop_method_skip);
\r
699 bool stop_ok = false;
\r
700 ret = RegQueryValueEx(key, NSSM_REG_STOP_METHOD_SKIP, 0, &type, (unsigned char *) &stop_method_skip, &buflen);
\r
701 if (ret != ERROR_SUCCESS) {
\r
702 if (ret != ERROR_FILE_NOT_FOUND) {
\r
703 if (type != REG_DWORD) {
\r
704 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_STOP_METHOD_SKIP, service->name, NSSM_REG_STOP_METHOD_SKIP, NSSM, 0);
\r
706 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, NSSM_REG_STOP_METHOD_SKIP, error_string(GetLastError()), 0);
\r
709 else stop_ok = true;
\r
711 /* Try all methods except those requested to be skipped. */
\r
712 service->stop_method = ~0;
\r
713 if (stop_ok) service->stop_method &= ~stop_method_skip;
\r
715 /* Try to get kill delays - may fail. */
\r
716 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
717 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
718 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
720 /* Try to get process tree settings - may fail. */
\r
721 unsigned long kill_process_tree;
\r
722 if (get_number(key, NSSM_REG_KILL_PROCESS_TREE, &kill_process_tree, false) == 1) {
\r
723 if (kill_process_tree) service->kill_process_tree = true;
\r
724 else service->kill_process_tree = false;
\r
726 else service->kill_process_tree = true;
\r
728 /* Try to get default exit action. */
\r
729 bool default_action;
\r
730 service->default_exit_action = NSSM_EXIT_RESTART;
\r
731 TCHAR action_string[ACTION_LEN];
\r
732 if (! get_exit_action(service->name, 0, action_string, &default_action)) {
\r
733 for (int i = 0; exit_action_strings[i]; i++) {
\r
734 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
735 service->default_exit_action = i;
\r
741 /* Close registry */
\r
748 Sets the string for the exit action corresponding to the exit code.
\r
750 ret is a pointer to an unsigned long containing the exit code.
\r
751 If ret is NULL, we retrieve the default exit action unconditionally.
\r
753 action is a buffer which receives the string.
\r
755 default_action is a pointer to a bool which is set to false if there
\r
756 was an explicit string for the given exit code, or true if we are
\r
757 returning the default action.
\r
759 Returns: 0 on success.
\r
762 int get_exit_action(const TCHAR *service_name, unsigned long *ret, TCHAR *action, bool *default_action) {
\r
763 /* Are we returning the default action or a status-specific one? */
\r
764 *default_action = ! ret;
\r
766 /* Try to open the registry */
\r
767 HKEY key = open_registry(service_name, NSSM_REG_EXIT, KEY_READ);
\r
768 if (! key) return 1;
\r
770 unsigned long type = REG_SZ;
\r
771 unsigned long action_len = ACTION_LEN;
\r
774 if (! ret) code[0] = _T('\0');
\r
775 else if (_sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), *ret) < 0) {
\r
777 return get_exit_action(service_name, 0, action, default_action);
\r
779 if (RegQueryValueEx(key, code, 0, &type, (unsigned char *) action, &action_len) != ERROR_SUCCESS) {
\r
781 /* Try again with * as the key if an exit code was defined */
\r
782 if (ret) return get_exit_action(service_name, 0, action, default_action);
\r
786 /* Close registry */
\r
792 int set_hook(const TCHAR *service_name, const TCHAR *hook_event, const TCHAR *hook_action, TCHAR *cmd) {
\r
793 /* Try to open the registry */
\r
794 TCHAR registry[KEY_LENGTH];
\r
795 if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, _T("%s\\%s"), NSSM_REG_HOOK, hook_event) < 0) {
\r
796 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("hook registry"), _T("set_hook()"), 0);
\r
803 /* Don't create keys needlessly. */
\r
804 if (! _tcslen(cmd)) {
\r
805 key = open_registry(service_name, registry, KEY_READ, false);
\r
806 if (! key) return 0;
\r
807 error = RegQueryValueEx(key, hook_action, 0, 0, 0, 0);
\r
809 if (error == ERROR_FILE_NOT_FOUND) return 0;
\r
812 key = open_registry(service_name, registry, KEY_WRITE);
\r
813 if (! key) return 1;
\r
816 if (_tcslen(cmd)) ret = set_string(key, (TCHAR *) hook_action, cmd, true);
\r
818 error = RegDeleteValue(key, hook_action);
\r
819 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) ret = 0;
\r
822 /* Close registry */
\r
828 int get_hook(const TCHAR *service_name, const TCHAR *hook_event, const TCHAR *hook_action, TCHAR *buffer, unsigned long buflen) {
\r
829 /* Try to open the registry */
\r
830 TCHAR registry[KEY_LENGTH];
\r
831 if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, _T("%s\\%s"), NSSM_REG_HOOK, hook_event) < 0) {
\r
832 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("hook registry"), _T("get_hook()"), 0);
\r
836 long error = open_registry(service_name, registry, KEY_READ, &key, false);
\r
838 if (error == ERROR_FILE_NOT_FOUND) {
\r
839 ZeroMemory(buffer, buflen);
\r
845 int ret = expand_parameter(key, (TCHAR *) hook_action, buffer, buflen, true, false);
\r
847 /* Close registry */
\r