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 HKEY open_registry_key(const TCHAR *registry, REGSAM sam, bool must_exist) {
\r
20 if (sam & KEY_SET_VALUE) {
\r
21 if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, sam, 0, &key, 0) != ERROR_SUCCESS) {
\r
22 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
27 long error = RegOpenKeyEx(HKEY_LOCAL_MACHINE, registry, 0, sam, &key);
\r
28 if (error != ERROR_SUCCESS) {
\r
29 if (error == ERROR_FILE_NOT_FOUND && ! must_exist) return 0;
\r
30 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
38 int create_messages() {
\r
41 TCHAR registry[KEY_LENGTH];
\r
42 if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, _T("SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\%s"), NSSM) < 0) {
\r
43 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("eventlog registry"), _T("create_messages()"), 0);
\r
47 if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, KEY_WRITE, 0, &key, 0) != ERROR_SUCCESS) {
\r
48 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
52 /* Get path of this program */
\r
53 const TCHAR *path = nssm_unquoted_imagepath();
\r
55 /* Try to register the module but don't worry so much on failure */
\r
56 RegSetValueEx(key, _T("EventMessageFile"), 0, REG_SZ, (const unsigned char *) path, (unsigned long) (_tcslen(path) + 1) * sizeof(TCHAR));
\r
57 unsigned long types = EVENTLOG_INFORMATION_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_ERROR_TYPE;
\r
58 RegSetValueEx(key, _T("TypesSupported"), 0, REG_DWORD, (const unsigned char *) &types, sizeof(types));
\r
63 int create_parameters(nssm_service_t *service, bool editing) {
\r
64 /* Try to open the registry */
\r
65 HKEY key = open_registry(service->name, KEY_WRITE);
\r
66 if (! key) return 1;
\r
68 /* Remember parameters in case we need to delete them. */
\r
69 TCHAR registry[KEY_LENGTH];
\r
70 int ret = service_registry_path(service->name, true, 0, registry, _countof(registry));
\r
72 /* Try to create the parameters */
\r
73 if (set_expand_string(key, NSSM_REG_EXE, service->exe)) {
\r
74 if (ret > 0) RegDeleteKey(HKEY_LOCAL_MACHINE, registry);
\r
78 if (set_expand_string(key, NSSM_REG_FLAGS, service->flags)) {
\r
79 if (ret > 0) RegDeleteKey(HKEY_LOCAL_MACHINE, registry);
\r
83 if (set_expand_string(key, NSSM_REG_DIR, service->dir)) {
\r
84 if (ret > 0) RegDeleteKey(HKEY_LOCAL_MACHINE, registry);
\r
89 /* Other non-default parameters. May fail. */
\r
90 if (service->priority != NORMAL_PRIORITY_CLASS) set_number(key, NSSM_REG_PRIORITY, service->priority);
\r
91 else if (editing) RegDeleteValue(key, NSSM_REG_PRIORITY);
\r
92 if (service->affinity) {
\r
94 if (! affinity_mask_to_string(service->affinity, &string)) {
\r
95 if (RegSetValueEx(key, NSSM_REG_AFFINITY, 0, REG_SZ, (const unsigned char *) string, (unsigned long) (_tcslen(string) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
96 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_AFFINITY, error_string(GetLastError()), 0);
\r
97 HeapFree(GetProcessHeap(), 0, string);
\r
101 if (string) HeapFree(GetProcessHeap(), 0, string);
\r
103 else if (editing) RegDeleteValue(key, NSSM_REG_AFFINITY);
\r
104 unsigned long stop_method_skip = ~service->stop_method;
\r
105 if (stop_method_skip) set_number(key, NSSM_REG_STOP_METHOD_SKIP, stop_method_skip);
\r
106 else if (editing) RegDeleteValue(key, NSSM_REG_STOP_METHOD_SKIP);
\r
107 if (service->default_exit_action < NSSM_NUM_EXIT_ACTIONS) create_exit_action(service->name, exit_action_strings[service->default_exit_action], editing);
\r
108 if (service->restart_delay) set_number(key, NSSM_REG_RESTART_DELAY, service->restart_delay);
\r
109 else if (editing) RegDeleteValue(key, NSSM_REG_RESTART_DELAY);
\r
110 if (service->throttle_delay != NSSM_RESET_THROTTLE_RESTART) set_number(key, NSSM_REG_THROTTLE, service->throttle_delay);
\r
111 else if (editing) RegDeleteValue(key, NSSM_REG_THROTTLE);
\r
112 if (service->kill_console_delay != NSSM_KILL_CONSOLE_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, service->kill_console_delay);
\r
113 else if (editing) RegDeleteValue(key, NSSM_REG_KILL_CONSOLE_GRACE_PERIOD);
\r
114 if (service->kill_window_delay != NSSM_KILL_WINDOW_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_WINDOW_GRACE_PERIOD, service->kill_window_delay);
\r
115 else if (editing) RegDeleteValue(key, NSSM_REG_KILL_WINDOW_GRACE_PERIOD);
\r
116 if (service->kill_threads_delay != NSSM_KILL_THREADS_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_THREADS_GRACE_PERIOD, service->kill_threads_delay);
\r
117 else if (editing) RegDeleteValue(key, NSSM_REG_KILL_THREADS_GRACE_PERIOD);
\r
118 if (! service->kill_process_tree) set_number(key, NSSM_REG_KILL_PROCESS_TREE, 0);
\r
119 else if (editing) RegDeleteValue(key, NSSM_REG_KILL_PROCESS_TREE);
\r
120 if (service->stdin_path[0] || editing) {
\r
121 if (service->stdin_path[0]) set_expand_string(key, NSSM_REG_STDIN, service->stdin_path);
\r
122 else if (editing) RegDeleteValue(key, NSSM_REG_STDIN);
\r
123 if (service->stdin_sharing != NSSM_STDIN_SHARING) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_SHARING, service->stdin_sharing);
\r
124 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_SHARING);
\r
125 if (service->stdin_disposition != NSSM_STDIN_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_DISPOSITION, service->stdin_disposition);
\r
126 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_DISPOSITION);
\r
127 if (service->stdin_flags != NSSM_STDIN_FLAGS) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_FLAGS, service->stdin_flags);
\r
128 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_FLAGS);
\r
130 if (service->stdout_path[0] || editing) {
\r
131 if (service->stdout_path[0]) set_expand_string(key, NSSM_REG_STDOUT, service->stdout_path);
\r
132 else if (editing) RegDeleteValue(key, NSSM_REG_STDOUT);
\r
133 if (service->stdout_sharing != NSSM_STDOUT_SHARING) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_SHARING, service->stdout_sharing);
\r
134 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_SHARING);
\r
135 if (service->stdout_disposition != NSSM_STDOUT_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_DISPOSITION, service->stdout_disposition);
\r
136 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_DISPOSITION);
\r
137 if (service->stdout_flags != NSSM_STDOUT_FLAGS) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_FLAGS, service->stdout_flags);
\r
138 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_FLAGS);
\r
139 if (service->stdout_copy_and_truncate) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_COPY_AND_TRUNCATE, 1);
\r
140 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_COPY_AND_TRUNCATE);
\r
142 if (service->stderr_path[0] || editing) {
\r
143 if (service->stderr_path[0]) set_expand_string(key, NSSM_REG_STDERR, service->stderr_path);
\r
144 else if (editing) RegDeleteValue(key, NSSM_REG_STDERR);
\r
145 if (service->stderr_sharing != NSSM_STDERR_SHARING) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_SHARING, service->stderr_sharing);
\r
146 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_SHARING);
\r
147 if (service->stderr_disposition != NSSM_STDERR_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_DISPOSITION, service->stderr_disposition);
\r
148 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_DISPOSITION);
\r
149 if (service->stderr_flags != NSSM_STDERR_FLAGS) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_FLAGS, service->stderr_flags);
\r
150 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_FLAGS);
\r
151 if (service->stderr_copy_and_truncate) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_COPY_AND_TRUNCATE, 1);
\r
152 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_COPY_AND_TRUNCATE);
\r
154 if (service->rotate_files) set_number(key, NSSM_REG_ROTATE, 1);
\r
155 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE);
\r
156 if (service->rotate_stdout_online) set_number(key, NSSM_REG_ROTATE_ONLINE, 1);
\r
157 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_ONLINE);
\r
158 if (service->rotate_seconds) set_number(key, NSSM_REG_ROTATE_SECONDS, service->rotate_seconds);
\r
159 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_SECONDS);
\r
160 if (service->rotate_bytes_low) set_number(key, NSSM_REG_ROTATE_BYTES_LOW, service->rotate_bytes_low);
\r
161 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_BYTES_LOW);
\r
162 if (service->rotate_bytes_high) set_number(key, NSSM_REG_ROTATE_BYTES_HIGH, service->rotate_bytes_high);
\r
163 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_BYTES_HIGH);
\r
164 if (service->rotate_delay != NSSM_ROTATE_DELAY) set_number(key, NSSM_REG_ROTATE_DELAY, service->rotate_delay);
\r
165 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_DELAY);
\r
166 if (service->no_console) set_number(key, NSSM_REG_NO_CONSOLE, 1);
\r
167 else if (editing) RegDeleteValue(key, NSSM_REG_NO_CONSOLE);
\r
170 if (service->env) {
\r
171 if (RegSetValueEx(key, NSSM_REG_ENV, 0, REG_MULTI_SZ, (const unsigned char *) service->env, (unsigned long) service->envlen * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
172 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV, error_string(GetLastError()), 0);
\r
175 else if (editing) RegDeleteValue(key, NSSM_REG_ENV);
\r
176 if (service->env_extra) {
\r
177 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
178 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV_EXTRA, error_string(GetLastError()), 0);
\r
181 else if (editing) RegDeleteValue(key, NSSM_REG_ENV_EXTRA);
\r
183 /* Close registry. */
\r
189 int create_exit_action(TCHAR *service_name, const TCHAR *action_string, bool editing) {
\r
191 TCHAR registry[KEY_LENGTH];
\r
192 if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, NSSM_REGISTRY _T("\\%s"), service_name, NSSM_REG_EXIT) < 0) {
\r
193 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("NSSM_REG_EXIT"), _T("create_exit_action()"), 0);
\r
197 /* Try to open the registry */
\r
199 unsigned long disposition;
\r
200 if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, KEY_WRITE, 0, &key, &disposition) != ERROR_SUCCESS) {
\r
201 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
205 /* Do nothing if the key already existed */
\r
206 if (disposition == REG_OPENED_EXISTING_KEY && ! editing) {
\r
211 /* Create the default value */
\r
212 if (RegSetValueEx(key, 0, 0, REG_SZ, (const unsigned char *) action_string, (unsigned long) (_tcslen(action_string) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
213 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_EXIT, error_string(GetLastError()), 0);
\r
218 /* Close registry */
\r
224 int get_environment(TCHAR *service_name, HKEY key, TCHAR *value, TCHAR **env, unsigned long *envlen) {
\r
225 unsigned long type = REG_MULTI_SZ;
\r
227 /* Dummy test to find buffer size */
\r
228 unsigned long ret = RegQueryValueEx(key, value, 0, &type, NULL, envlen);
\r
229 if (ret != ERROR_SUCCESS) {
\r
232 /* The service probably doesn't have any environment configured */
\r
233 if (ret == ERROR_FILE_NOT_FOUND) return 0;
\r
234 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
238 if (type != REG_MULTI_SZ) {
\r
241 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_INVALID_ENVIRONMENT_STRING_TYPE, value, service_name, 0);
\r
245 /* Probably not possible */
\r
246 if (! *envlen) return 0;
\r
248 /* Previously initialised? */
\r
249 if (*env) HeapFree(GetProcessHeap(), 0, *env);
\r
251 *env = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, *envlen);
\r
254 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, value, _T("get_environment()"), 0);
\r
258 /* Actually get the strings */
\r
259 ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) *env, envlen);
\r
260 if (ret != ERROR_SUCCESS) {
\r
261 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
262 HeapFree(GetProcessHeap(), 0, *env);
\r
272 int get_string(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool expand, bool sanitise, bool must_exist) {
\r
273 TCHAR *buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, datalen);
\r
275 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, value, _T("get_string()"), 0);
\r
279 ZeroMemory(data, datalen);
\r
281 unsigned long type = REG_EXPAND_SZ;
\r
282 unsigned long buflen = datalen;
\r
284 unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);
\r
285 if (ret != ERROR_SUCCESS) {
\r
286 unsigned long error = GetLastError();
\r
287 HeapFree(GetProcessHeap(), 0, buffer);
\r
289 if (ret == ERROR_FILE_NOT_FOUND) {
\r
290 if (! must_exist) return 0;
\r
293 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(error), 0);
\r
297 /* Paths aren't allowed to contain quotes. */
\r
298 if (sanitise) PathUnquoteSpaces(buffer);
\r
300 /* Do we want to expand the string? */
\r
302 if (type == REG_EXPAND_SZ) type = REG_SZ;
\r
305 /* Technically we shouldn't expand environment strings from REG_SZ values */
\r
306 if (type != REG_EXPAND_SZ) {
\r
307 memmove(data, buffer, buflen);
\r
308 HeapFree(GetProcessHeap(), 0, buffer);
\r
312 ret = ExpandEnvironmentStrings((TCHAR *) buffer, data, datalen);
\r
313 if (! ret || ret > datalen) {
\r
314 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_EXPANDENVIRONMENTSTRINGS_FAILED, buffer, error_string(GetLastError()), 0);
\r
315 HeapFree(GetProcessHeap(), 0, buffer);
\r
319 HeapFree(GetProcessHeap(), 0, buffer);
\r
323 int get_string(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise) {
\r
324 return get_string(key, value, data, datalen, false, sanitise, true);
\r
327 int expand_parameter(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise, bool must_exist) {
\r
328 return get_string(key, value, data, datalen, true, sanitise, must_exist);
\r
331 int expand_parameter(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise) {
\r
332 return expand_parameter(key, value, data, datalen, sanitise, true);
\r
336 Sets a string in the registry.
\r
337 Returns: 0 if it was set.
\r
340 int set_string(HKEY key, TCHAR *value, TCHAR *string, bool expand) {
\r
341 unsigned long type = expand ? REG_EXPAND_SZ : REG_SZ;
\r
342 if (RegSetValueEx(key, value, 0, type, (const unsigned char *) string, (unsigned long) (_tcslen(string) + 1) * sizeof(TCHAR)) == ERROR_SUCCESS) return 0;
\r
343 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
347 int set_string(HKEY key, TCHAR *value, TCHAR *string) {
\r
348 return set_string(key, value, string, false);
\r
352 int set_expand_string(HKEY key, TCHAR *value, TCHAR *string) {
\r
353 return set_string(key, value, string, true);
\r
358 Set an unsigned long in the registry.
\r
359 Returns: 0 if it was set.
\r
362 int set_number(HKEY key, TCHAR *value, unsigned long number) {
\r
363 if (RegSetValueEx(key, value, 0, REG_DWORD, (const unsigned char *) &number, sizeof(number)) == ERROR_SUCCESS) return 0;
\r
364 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
369 Query an unsigned long from the registry.
\r
370 Returns: 1 if a number was retrieved.
\r
371 0 if none was found and must_exist is false.
\r
372 -1 if none was found and must_exist is true.
\r
375 int get_number(HKEY key, TCHAR *value, unsigned long *number, bool must_exist) {
\r
376 unsigned long type = REG_DWORD;
\r
377 unsigned long number_len = sizeof(unsigned long);
\r
379 int ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) number, &number_len);
\r
380 if (ret == ERROR_SUCCESS) return 1;
\r
382 if (ret == ERROR_FILE_NOT_FOUND) {
\r
383 if (! must_exist) return 0;
\r
386 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
387 if (ret == ERROR_FILE_NOT_FOUND) return -1;
\r
392 int get_number(HKEY key, TCHAR *value, unsigned long *number) {
\r
393 return get_number(key, value, number, true);
\r
396 /* Replace NULL with CRLF. Leave NULL NULL as the end marker. */
\r
397 int format_double_null(TCHAR *dn, unsigned long dnlen, TCHAR **formatted, unsigned long *newlen) {
\r
398 unsigned long i, j;
\r
406 for (i = 0; i < dnlen; i++) if (! dn[i] && dn[i + 1]) ++*newlen;
\r
408 *formatted = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *newlen * sizeof(TCHAR));
\r
409 if (! *formatted) {
\r
414 for (i = 0, j = 0; i < dnlen; i++) {
\r
415 (*formatted)[j] = dn[i];
\r
418 (*formatted)[j] = _T('\r');
\r
419 (*formatted)[++j] = _T('\n');
\r
428 /* Strip CR and replace LF with NULL. */
\r
429 int unformat_double_null(TCHAR *dn, unsigned long dnlen, TCHAR **unformatted, unsigned long *newlen) {
\r
430 unsigned long i, j;
\r
438 for (i = 0; i < dnlen; i++) if (dn[i] != _T('\r')) ++*newlen;
\r
440 /* Skip blank lines. */
\r
441 for (i = 0; i < dnlen; i++) {
\r
442 if (dn[i] == _T('\r') && dn[i + 1] == _T('\n')) {
\r
443 /* This is the last CRLF. */
\r
444 if (i >= dnlen - 2) break;
\r
447 Strip at the start of the block or if the next characters are
\r
450 if (! i || (dn[i + 2] == _T('\r') && dn[i + 3] == _T('\n'))) {
\r
451 for (j = i + 2; j < dnlen; j++) dn[j - 2] = dn[j];
\r
452 dn[dnlen--] = _T('\0');
\r
453 dn[dnlen--] = _T('\0');
\r
460 /* Must end with two NULLs. */
\r
463 *unformatted = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *newlen * sizeof(TCHAR));
\r
464 if (! *unformatted) return 1;
\r
466 for (i = 0, j = 0; i < dnlen; i++) {
\r
467 if (dn[i] == _T('\r')) continue;
\r
468 if (dn[i] == _T('\n')) (*unformatted)[j] = _T('\0');
\r
469 else (*unformatted)[j] = dn[i];
\r
476 void override_milliseconds(TCHAR *service_name, HKEY key, TCHAR *value, unsigned long *buffer, unsigned long default_value, unsigned long event) {
\r
477 unsigned long type = REG_DWORD;
\r
478 unsigned long buflen = sizeof(unsigned long);
\r
480 unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);
\r
481 if (ret != ERROR_SUCCESS) {
\r
482 if (ret != ERROR_FILE_NOT_FOUND) {
\r
483 if (type != REG_DWORD) {
\r
484 TCHAR milliseconds[16];
\r
485 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), default_value);
\r
486 log_event(EVENTLOG_WARNING_TYPE, event, service_name, value, milliseconds, 0);
\r
488 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
493 if (! ok) *buffer = default_value;
\r
496 HKEY open_service_registry(const TCHAR *service_name, REGSAM sam, bool must_exist) {
\r
498 TCHAR registry[KEY_LENGTH];
\r
499 if (service_registry_path(service_name, false, 0, registry, _countof(registry)) < 0) {
\r
500 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REGISTRY, _T("open_service_registry()"), 0);
\r
504 return open_registry_key(registry, sam, must_exist);
\r
507 HKEY open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam, bool must_exist) {
\r
509 TCHAR registry[KEY_LENGTH];
\r
510 if (service_registry_path(service_name, true, sub, registry, _countof(registry)) < 0) {
\r
511 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REGISTRY, _T("open_registry()"), 0);
\r
515 return open_registry_key(registry, sam, must_exist);
\r
518 HKEY open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam) {
\r
519 return open_registry(service_name, sub, sam, true);
\r
522 HKEY open_registry(const TCHAR *service_name, REGSAM sam) {
\r
523 return open_registry(service_name, 0, sam, true);
\r
526 int get_io_parameters(nssm_service_t *service, HKEY key) {
\r
528 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
529 service->stdin_sharing = service->stdin_disposition = service->stdin_flags = 0;
\r
530 ZeroMemory(service->stdin_path, _countof(service->stdin_path) * sizeof(TCHAR));
\r
535 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
536 service->stdout_sharing = service->stdout_disposition = service->stdout_flags = 0;
\r
537 ZeroMemory(service->stdout_path, _countof(service->stdout_path) * sizeof(TCHAR));
\r
542 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
543 service->stderr_sharing = service->stderr_disposition = service->stderr_flags = 0;
\r
544 ZeroMemory(service->stderr_path, _countof(service->stderr_path) * sizeof(TCHAR));
\r
551 int get_parameters(nssm_service_t *service, STARTUPINFO *si) {
\r
554 /* Try to open the registry */
\r
555 HKEY key = open_registry(service->name, KEY_READ);
\r
556 if (! key) return 1;
\r
558 /* Don't expand parameters when retrieving for the GUI. */
\r
559 bool expand = si ? true : false;
\r
561 /* Try to get environment variables - may fail */
\r
562 get_environment(service->name, key, NSSM_REG_ENV, &service->env, &service->envlen);
\r
563 /* Environment variables to add to existing rather than replace - may fail. */
\r
564 get_environment(service->name, key, NSSM_REG_ENV_EXTRA, &service->env_extra, &service->env_extralen);
\r
566 /* Set environment if we are starting the service. */
\r
567 if (si) set_service_environment(service);
\r
569 /* Try to get executable file - MUST succeed */
\r
570 if (get_string(key, NSSM_REG_EXE, service->exe, sizeof(service->exe), expand, false, true)) {
\r
575 /* Try to get flags - may fail and we don't care */
\r
576 if (get_string(key, NSSM_REG_FLAGS, service->flags, sizeof(service->flags), expand, false, true)) {
\r
577 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_FLAGS, NSSM_REG_FLAGS, service->name, service->exe, 0);
\r
578 ZeroMemory(service->flags, sizeof(service->flags));
\r
581 /* Try to get startup directory - may fail and we fall back to a default */
\r
582 if (get_string(key, NSSM_REG_DIR, service->dir, sizeof(service->dir), expand, true, true) || ! service->dir[0]) {
\r
583 _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);
\r
584 strip_basename(service->dir);
\r
585 if (service->dir[0] == _T('\0')) {
\r
587 ret = GetWindowsDirectory(service->dir, sizeof(service->dir));
\r
588 if (! ret || ret > sizeof(service->dir)) {
\r
589 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_NO_DIR_AND_NO_FALLBACK, NSSM_REG_DIR, service->name, 0);
\r
594 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_DIR, NSSM_REG_DIR, service->name, service->dir, 0);
\r
597 /* Try to get processor affinity - may fail. */
\r
599 if (get_string(key, NSSM_REG_AFFINITY, buffer, sizeof(buffer), false, false, false) || ! buffer[0]) service->affinity = 0LL;
\r
600 else if (affinity_string_to_mask(buffer, &service->affinity)) {
\r
601 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_AFFINITY_MASK, service->name, buffer);
\r
602 service->affinity = 0LL;
\r
605 DWORD_PTR affinity, system_affinity;
\r
607 if (GetProcessAffinityMask(GetCurrentProcess(), &affinity, &system_affinity)) {
\r
608 _int64 effective_affinity = service->affinity & system_affinity;
\r
609 if (effective_affinity != service->affinity) {
\r
611 if (! affinity_mask_to_string(system_affinity, &system)) {
\r
612 TCHAR *effective = 0;
\r
613 if (! affinity_mask_to_string(effective_affinity, &effective)) {
\r
614 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_EFFECTIVE_AFFINITY_MASK, service->name, buffer, system, effective, 0);
\r
616 HeapFree(GetProcessHeap(), 0, effective);
\r
618 HeapFree(GetProcessHeap(), 0, system);
\r
623 /* Try to get priority - may fail. */
\r
624 unsigned long priority;
\r
625 if (get_number(key, NSSM_REG_PRIORITY, &priority, false) == 1) {
\r
626 if (priority == (priority & priority_mask())) service->priority = priority;
\r
627 else log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_PRIORITY, service->name, NSSM_REG_PRIORITY, 0);
\r
630 /* Try to get file rotation settings - may fail. */
\r
631 unsigned long rotate_files;
\r
632 if (get_number(key, NSSM_REG_ROTATE, &rotate_files, false) == 1) {
\r
633 if (rotate_files) service->rotate_files = true;
\r
634 else service->rotate_files = false;
\r
636 else service->rotate_files = false;
\r
637 if (get_number(key, NSSM_REG_ROTATE_ONLINE, &rotate_files, false) == 1) {
\r
638 if (rotate_files) service->rotate_stdout_online = service->rotate_stderr_online = true;
\r
639 else service->rotate_stdout_online = service->rotate_stderr_online = false;
\r
641 else service->rotate_stdout_online = service->rotate_stderr_online = false;
\r
642 if (get_number(key, NSSM_REG_ROTATE_SECONDS, &service->rotate_seconds, false) != 1) service->rotate_seconds = 0;
\r
643 if (get_number(key, NSSM_REG_ROTATE_BYTES_LOW, &service->rotate_bytes_low, false) != 1) service->rotate_bytes_low = 0;
\r
644 if (get_number(key, NSSM_REG_ROTATE_BYTES_HIGH, &service->rotate_bytes_high, false) != 1) service->rotate_bytes_high = 0;
\r
645 override_milliseconds(service->name, key, NSSM_REG_ROTATE_DELAY, &service->rotate_delay, NSSM_ROTATE_DELAY, NSSM_EVENT_BOGUS_THROTTLE);
\r
647 /* Try to get force new console setting - may fail. */
\r
648 if (get_number(key, NSSM_REG_NO_CONSOLE, &service->no_console, false) != 1) service->no_console = 0;
\r
650 /* Change to startup directory in case stdout/stderr are relative paths. */
\r
651 TCHAR cwd[PATH_LENGTH];
\r
652 GetCurrentDirectory(_countof(cwd), cwd);
\r
653 SetCurrentDirectory(service->dir);
\r
655 /* Try to get stdout and stderr */
\r
656 if (get_io_parameters(service, key)) {
\r
657 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);
\r
659 SetCurrentDirectory(cwd);
\r
663 /* Change back in case the startup directory needs to be deleted. */
\r
664 SetCurrentDirectory(cwd);
\r
666 /* Try to get mandatory restart delay */
\r
667 override_milliseconds(service->name, key, NSSM_REG_RESTART_DELAY, &service->restart_delay, 0, NSSM_EVENT_BOGUS_RESTART_DELAY);
\r
669 /* Try to get throttle restart delay */
\r
670 override_milliseconds(service->name, key, NSSM_REG_THROTTLE, &service->throttle_delay, NSSM_RESET_THROTTLE_RESTART, NSSM_EVENT_BOGUS_THROTTLE);
\r
672 /* Try to get service stop flags. */
\r
673 unsigned long type = REG_DWORD;
\r
674 unsigned long stop_method_skip;
\r
675 unsigned long buflen = sizeof(stop_method_skip);
\r
676 bool stop_ok = false;
\r
677 ret = RegQueryValueEx(key, NSSM_REG_STOP_METHOD_SKIP, 0, &type, (unsigned char *) &stop_method_skip, &buflen);
\r
678 if (ret != ERROR_SUCCESS) {
\r
679 if (ret != ERROR_FILE_NOT_FOUND) {
\r
680 if (type != REG_DWORD) {
\r
681 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_STOP_METHOD_SKIP, service->name, NSSM_REG_STOP_METHOD_SKIP, NSSM, 0);
\r
683 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, NSSM_REG_STOP_METHOD_SKIP, error_string(GetLastError()), 0);
\r
686 else stop_ok = true;
\r
688 /* Try all methods except those requested to be skipped. */
\r
689 service->stop_method = ~0;
\r
690 if (stop_ok) service->stop_method &= ~stop_method_skip;
\r
692 /* Try to get kill delays - may fail. */
\r
693 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
694 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
695 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
697 /* Try to get process tree settings - may fail. */
\r
698 unsigned long kill_process_tree;
\r
699 if (get_number(key, NSSM_REG_KILL_PROCESS_TREE, &kill_process_tree, false) == 1) {
\r
700 if (kill_process_tree) service->kill_process_tree = true;
\r
701 else service->kill_process_tree = false;
\r
703 else service->kill_process_tree = true;
\r
705 /* Try to get default exit action. */
\r
706 bool default_action;
\r
707 service->default_exit_action = NSSM_EXIT_RESTART;
\r
708 TCHAR action_string[ACTION_LEN];
\r
709 if (! get_exit_action(service->name, 0, action_string, &default_action)) {
\r
710 for (int i = 0; exit_action_strings[i]; i++) {
\r
711 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
712 service->default_exit_action = i;
\r
718 /* Close registry */
\r
725 Sets the string for the exit action corresponding to the exit code.
\r
727 ret is a pointer to an unsigned long containing the exit code.
\r
728 If ret is NULL, we retrieve the default exit action unconditionally.
\r
730 action is a buffer which receives the string.
\r
732 default_action is a pointer to a bool which is set to false if there
\r
733 was an explicit string for the given exit code, or true if we are
\r
734 returning the default action.
\r
736 Returns: 0 on success.
\r
739 int get_exit_action(const TCHAR *service_name, unsigned long *ret, TCHAR *action, bool *default_action) {
\r
740 /* Are we returning the default action or a status-specific one? */
\r
741 *default_action = ! ret;
\r
743 /* Try to open the registry */
\r
744 HKEY key = open_registry(service_name, NSSM_REG_EXIT, KEY_READ);
\r
745 if (! key) return 1;
\r
747 unsigned long type = REG_SZ;
\r
748 unsigned long action_len = ACTION_LEN;
\r
751 if (! ret) code[0] = _T('\0');
\r
752 else if (_sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), *ret) < 0) {
\r
754 return get_exit_action(service_name, 0, action, default_action);
\r
756 if (RegQueryValueEx(key, code, 0, &type, (unsigned char *) action, &action_len) != ERROR_SUCCESS) {
\r
758 /* Try again with * as the key if an exit code was defined */
\r
759 if (ret) return get_exit_action(service_name, 0, action, default_action);
\r
763 /* Close registry */
\r
769 int set_hook(const TCHAR *service_name, const TCHAR *hook_event, const TCHAR *hook_action, TCHAR *cmd) {
\r
770 /* Try to open the registry */
\r
771 TCHAR registry[KEY_LENGTH];
\r
772 if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, _T("%s\\%s"), NSSM_REG_HOOK, hook_event) < 0) {
\r
773 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("hook registry"), _T("set_hook()"), 0);
\r
780 /* Don't create keys needlessly. */
\r
781 if (! _tcslen(cmd)) {
\r
782 key = open_registry(service_name, registry, KEY_READ, false);
\r
783 if (! key) return 0;
\r
784 error = RegQueryValueEx(key, hook_action, 0, 0, 0, 0);
\r
786 if (error == ERROR_FILE_NOT_FOUND) return 0;
\r
789 key = open_registry(service_name, registry, KEY_WRITE);
\r
790 if (! key) return 1;
\r
793 if (_tcslen(cmd)) ret = set_string(key, (TCHAR *) hook_action, cmd, true);
\r
795 error = RegDeleteValue(key, hook_action);
\r
796 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) ret = 0;
\r
799 /* Close registry */
\r
805 int get_hook(const TCHAR *service_name, const TCHAR *hook_event, const TCHAR *hook_action, TCHAR *buffer, unsigned long buflen) {
\r
806 /* Try to open the registry */
\r
807 TCHAR registry[KEY_LENGTH];
\r
808 if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, _T("%s\\%s"), NSSM_REG_HOOK, hook_event) < 0) {
\r
809 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("hook registry"), _T("get_hook()"), 0);
\r
812 HKEY key = open_registry(service_name, registry, KEY_READ, false);
\r
813 if (! key) return 1;
\r
815 int ret = expand_parameter(key, (TCHAR *) hook_action, buffer, buflen, true, false);
\r
817 /* Close registry */
\r