3 extern const TCHAR *exit_action_strings[];
\r
5 int create_messages() {
\r
8 TCHAR registry[KEY_LENGTH];
\r
9 if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, _T("SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\%s"), NSSM) < 0) {
\r
10 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("eventlog registry"), _T("create_messages()"), 0);
\r
14 if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, KEY_WRITE, 0, &key, 0) != ERROR_SUCCESS) {
\r
15 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
19 /* Get path of this program */
\r
20 const TCHAR *path = nssm_imagepath();
\r
22 /* Try to register the module but don't worry so much on failure */
\r
23 RegSetValueEx(key, _T("EventMessageFile"), 0, REG_SZ, (const unsigned char *) path, (unsigned long) (_tcslen(path) + 1) * sizeof(TCHAR));
\r
24 unsigned long types = EVENTLOG_INFORMATION_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_ERROR_TYPE;
\r
25 RegSetValueEx(key, _T("TypesSupported"), 0, REG_DWORD, (const unsigned char *) &types, sizeof(types));
\r
30 int create_parameters(nssm_service_t *service, bool editing) {
\r
31 /* Try to open the registry */
\r
32 HKEY key = open_registry(service->name, KEY_WRITE);
\r
33 if (! key) return 1;
\r
35 /* Remember parameters in case we need to delete them. */
\r
36 TCHAR registry[KEY_LENGTH];
\r
37 int ret = service_registry_path(service->name, true, 0, registry, _countof(registry));
\r
39 /* Try to create the parameters */
\r
40 if (set_expand_string(key, NSSM_REG_EXE, service->exe)) {
\r
41 if (ret > 0) RegDeleteKey(HKEY_LOCAL_MACHINE, registry);
\r
45 if (set_expand_string(key, NSSM_REG_FLAGS, service->flags)) {
\r
46 if (ret > 0) RegDeleteKey(HKEY_LOCAL_MACHINE, registry);
\r
50 if (set_expand_string(key, NSSM_REG_DIR, service->dir)) {
\r
51 if (ret > 0) RegDeleteKey(HKEY_LOCAL_MACHINE, registry);
\r
56 /* Other non-default parameters. May fail. */
\r
57 if (service->priority != NORMAL_PRIORITY_CLASS) set_number(key, NSSM_REG_PRIORITY, service->priority);
\r
58 else if (editing) RegDeleteValue(key, NSSM_REG_PRIORITY);
\r
59 if (service->affinity) {
\r
61 if (! affinity_mask_to_string(service->affinity, &string)) {
\r
62 if (RegSetValueEx(key, NSSM_REG_AFFINITY, 0, REG_SZ, (const unsigned char *) string, (unsigned long) (_tcslen(string) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
63 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_AFFINITY, error_string(GetLastError()), 0);
\r
64 HeapFree(GetProcessHeap(), 0, string);
\r
68 if (string) HeapFree(GetProcessHeap(), 0, string);
\r
70 else if (editing) RegDeleteValue(key, NSSM_REG_AFFINITY);
\r
71 unsigned long stop_method_skip = ~service->stop_method;
\r
72 if (stop_method_skip) set_number(key, NSSM_REG_STOP_METHOD_SKIP, stop_method_skip);
\r
73 else if (editing) RegDeleteValue(key, NSSM_REG_STOP_METHOD_SKIP);
\r
74 if (service->default_exit_action < NSSM_NUM_EXIT_ACTIONS) create_exit_action(service->name, exit_action_strings[service->default_exit_action], editing);
\r
75 if (service->restart_delay) set_number(key, NSSM_REG_RESTART_DELAY, service->restart_delay);
\r
76 else if (editing) RegDeleteValue(key, NSSM_REG_RESTART_DELAY);
\r
77 if (service->throttle_delay != NSSM_RESET_THROTTLE_RESTART) set_number(key, NSSM_REG_THROTTLE, service->throttle_delay);
\r
78 else if (editing) RegDeleteValue(key, NSSM_REG_THROTTLE);
\r
79 if (service->kill_console_delay != NSSM_KILL_CONSOLE_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, service->kill_console_delay);
\r
80 else if (editing) RegDeleteValue(key, NSSM_REG_KILL_CONSOLE_GRACE_PERIOD);
\r
81 if (service->kill_window_delay != NSSM_KILL_WINDOW_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_WINDOW_GRACE_PERIOD, service->kill_window_delay);
\r
82 else if (editing) RegDeleteValue(key, NSSM_REG_KILL_WINDOW_GRACE_PERIOD);
\r
83 if (service->kill_threads_delay != NSSM_KILL_THREADS_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_THREADS_GRACE_PERIOD, service->kill_threads_delay);
\r
84 else if (editing) RegDeleteValue(key, NSSM_REG_KILL_THREADS_GRACE_PERIOD);
\r
85 if (! service->kill_process_tree) set_number(key, NSSM_REG_KILL_PROCESS_TREE, 0);
\r
86 else if (editing) RegDeleteValue(key, NSSM_REG_KILL_PROCESS_TREE);
\r
87 if (service->stdin_path[0] || editing) {
\r
88 if (service->stdin_path[0]) set_expand_string(key, NSSM_REG_STDIN, service->stdin_path);
\r
89 else if (editing) RegDeleteValue(key, NSSM_REG_STDIN);
\r
90 if (service->stdin_sharing != NSSM_STDIN_SHARING) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_SHARING, service->stdin_sharing);
\r
91 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_SHARING);
\r
92 if (service->stdin_disposition != NSSM_STDIN_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_DISPOSITION, service->stdin_disposition);
\r
93 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_DISPOSITION);
\r
94 if (service->stdin_flags != NSSM_STDIN_FLAGS) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_FLAGS, service->stdin_flags);
\r
95 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_FLAGS);
\r
97 if (service->stdout_path[0] || editing) {
\r
98 if (service->stdout_path[0]) set_expand_string(key, NSSM_REG_STDOUT, service->stdout_path);
\r
99 else if (editing) RegDeleteValue(key, NSSM_REG_STDOUT);
\r
100 if (service->stdout_sharing != NSSM_STDOUT_SHARING) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_SHARING, service->stdout_sharing);
\r
101 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_SHARING);
\r
102 if (service->stdout_disposition != NSSM_STDOUT_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_DISPOSITION, service->stdout_disposition);
\r
103 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_DISPOSITION);
\r
104 if (service->stdout_flags != NSSM_STDOUT_FLAGS) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_FLAGS, service->stdout_flags);
\r
105 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_FLAGS);
\r
106 if (service->stdout_copy_and_truncate) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_COPY_AND_TRUNCATE, 1);
\r
107 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_COPY_AND_TRUNCATE);
\r
109 if (service->stderr_path[0] || editing) {
\r
110 if (service->stderr_path[0]) set_expand_string(key, NSSM_REG_STDERR, service->stderr_path);
\r
111 else if (editing) RegDeleteValue(key, NSSM_REG_STDERR);
\r
112 if (service->stderr_sharing != NSSM_STDERR_SHARING) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_SHARING, service->stderr_sharing);
\r
113 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_SHARING);
\r
114 if (service->stderr_disposition != NSSM_STDERR_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_DISPOSITION, service->stderr_disposition);
\r
115 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_DISPOSITION);
\r
116 if (service->stderr_flags != NSSM_STDERR_FLAGS) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_FLAGS, service->stderr_flags);
\r
117 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_FLAGS);
\r
118 if (service->stderr_copy_and_truncate) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_COPY_AND_TRUNCATE, 1);
\r
119 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_COPY_AND_TRUNCATE);
\r
121 if (service->rotate_files) set_number(key, NSSM_REG_ROTATE, 1);
\r
122 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE);
\r
123 if (service->rotate_stdout_online) set_number(key, NSSM_REG_ROTATE_ONLINE, 1);
\r
124 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_ONLINE);
\r
125 if (service->rotate_seconds) set_number(key, NSSM_REG_ROTATE_SECONDS, service->rotate_seconds);
\r
126 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_SECONDS);
\r
127 if (service->rotate_bytes_low) set_number(key, NSSM_REG_ROTATE_BYTES_LOW, service->rotate_bytes_low);
\r
128 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_BYTES_LOW);
\r
129 if (service->rotate_bytes_high) set_number(key, NSSM_REG_ROTATE_BYTES_HIGH, service->rotate_bytes_high);
\r
130 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_BYTES_HIGH);
\r
131 if (service->rotate_delay != NSSM_ROTATE_DELAY) set_number(key, NSSM_REG_ROTATE_DELAY, service->rotate_delay);
\r
132 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_DELAY);
\r
133 if (service->no_console) set_number(key, NSSM_REG_NO_CONSOLE, 1);
\r
134 else if (editing) RegDeleteValue(key, NSSM_REG_NO_CONSOLE);
\r
137 if (service->env) {
\r
138 if (RegSetValueEx(key, NSSM_REG_ENV, 0, REG_MULTI_SZ, (const unsigned char *) service->env, (unsigned long) service->envlen * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
139 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV, error_string(GetLastError()), 0);
\r
142 else if (editing) RegDeleteValue(key, NSSM_REG_ENV);
\r
143 if (service->env_extra) {
\r
144 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
145 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV_EXTRA, error_string(GetLastError()), 0);
\r
148 else if (editing) RegDeleteValue(key, NSSM_REG_ENV_EXTRA);
\r
150 /* Close registry. */
\r
156 int create_exit_action(TCHAR *service_name, const TCHAR *action_string, bool editing) {
\r
158 TCHAR registry[KEY_LENGTH];
\r
159 if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, NSSM_REGISTRY _T("\\%s"), service_name, NSSM_REG_EXIT) < 0) {
\r
160 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("NSSM_REG_EXIT"), _T("create_exit_action()"), 0);
\r
164 /* Try to open the registry */
\r
166 unsigned long disposition;
\r
167 if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, KEY_WRITE, 0, &key, &disposition) != ERROR_SUCCESS) {
\r
168 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
172 /* Do nothing if the key already existed */
\r
173 if (disposition == REG_OPENED_EXISTING_KEY && ! editing) {
\r
178 /* Create the default value */
\r
179 if (RegSetValueEx(key, 0, 0, REG_SZ, (const unsigned char *) action_string, (unsigned long) (_tcslen(action_string) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
180 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_EXIT, error_string(GetLastError()), 0);
\r
185 /* Close registry */
\r
191 int get_environment(TCHAR *service_name, HKEY key, TCHAR *value, TCHAR **env, unsigned long *envlen) {
\r
192 unsigned long type = REG_MULTI_SZ;
\r
194 /* Dummy test to find buffer size */
\r
195 unsigned long ret = RegQueryValueEx(key, value, 0, &type, NULL, envlen);
\r
196 if (ret != ERROR_SUCCESS) {
\r
199 /* The service probably doesn't have any environment configured */
\r
200 if (ret == ERROR_FILE_NOT_FOUND) return 0;
\r
201 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
205 if (type != REG_MULTI_SZ) {
\r
208 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_INVALID_ENVIRONMENT_STRING_TYPE, value, service_name, 0);
\r
212 /* Probably not possible */
\r
213 if (! *envlen) return 0;
\r
215 /* Previously initialised? */
\r
216 if (*env) HeapFree(GetProcessHeap(), 0, *env);
\r
218 *env = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, *envlen);
\r
221 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, value, _T("get_environment()"), 0);
\r
225 /* Actually get the strings */
\r
226 ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) *env, envlen);
\r
227 if (ret != ERROR_SUCCESS) {
\r
228 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
229 HeapFree(GetProcessHeap(), 0, *env);
\r
239 int get_string(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool expand, bool sanitise, bool must_exist) {
\r
240 TCHAR *buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, datalen);
\r
242 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, value, _T("get_string()"), 0);
\r
246 ZeroMemory(data, datalen);
\r
248 unsigned long type = REG_EXPAND_SZ;
\r
249 unsigned long buflen = datalen;
\r
251 unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);
\r
252 if (ret != ERROR_SUCCESS) {
\r
253 unsigned long error = GetLastError();
\r
254 HeapFree(GetProcessHeap(), 0, buffer);
\r
256 if (ret == ERROR_FILE_NOT_FOUND) {
\r
257 if (! must_exist) return 0;
\r
260 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(error), 0);
\r
264 /* Paths aren't allowed to contain quotes. */
\r
265 if (sanitise) PathUnquoteSpaces(buffer);
\r
267 /* Do we want to expand the string? */
\r
269 if (type == REG_EXPAND_SZ) type = REG_SZ;
\r
272 /* Technically we shouldn't expand environment strings from REG_SZ values */
\r
273 if (type != REG_EXPAND_SZ) {
\r
274 memmove(data, buffer, buflen);
\r
275 HeapFree(GetProcessHeap(), 0, buffer);
\r
279 ret = ExpandEnvironmentStrings((TCHAR *) buffer, data, datalen);
\r
280 if (! ret || ret > datalen) {
\r
281 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_EXPANDENVIRONMENTSTRINGS_FAILED, buffer, error_string(GetLastError()), 0);
\r
282 HeapFree(GetProcessHeap(), 0, buffer);
\r
286 HeapFree(GetProcessHeap(), 0, buffer);
\r
290 int get_string(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise) {
\r
291 return get_string(key, value, data, datalen, false, sanitise, true);
\r
294 int expand_parameter(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise, bool must_exist) {
\r
295 return get_string(key, value, data, datalen, true, sanitise, must_exist);
\r
298 int expand_parameter(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise) {
\r
299 return expand_parameter(key, value, data, datalen, sanitise, true);
\r
303 Sets a string in the registry.
\r
304 Returns: 0 if it was set.
\r
307 int set_string(HKEY key, TCHAR *value, TCHAR *string, bool expand) {
\r
308 unsigned long type = expand ? REG_EXPAND_SZ : REG_SZ;
\r
309 if (RegSetValueEx(key, value, 0, type, (const unsigned char *) string, (unsigned long) (_tcslen(string) + 1) * sizeof(TCHAR)) == ERROR_SUCCESS) return 0;
\r
310 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
314 int set_string(HKEY key, TCHAR *value, TCHAR *string) {
\r
315 return set_string(key, value, string, false);
\r
319 int set_expand_string(HKEY key, TCHAR *value, TCHAR *string) {
\r
320 return set_string(key, value, string, true);
\r
325 Set an unsigned long in the registry.
\r
326 Returns: 0 if it was set.
\r
329 int set_number(HKEY key, TCHAR *value, unsigned long number) {
\r
330 if (RegSetValueEx(key, value, 0, REG_DWORD, (const unsigned char *) &number, sizeof(number)) == ERROR_SUCCESS) return 0;
\r
331 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
336 Query an unsigned long from the registry.
\r
337 Returns: 1 if a number was retrieved.
\r
338 0 if none was found and must_exist is false.
\r
339 -1 if none was found and must_exist is true.
\r
342 int get_number(HKEY key, TCHAR *value, unsigned long *number, bool must_exist) {
\r
343 unsigned long type = REG_DWORD;
\r
344 unsigned long number_len = sizeof(unsigned long);
\r
346 int ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) number, &number_len);
\r
347 if (ret == ERROR_SUCCESS) return 1;
\r
349 if (ret == ERROR_FILE_NOT_FOUND) {
\r
350 if (! must_exist) return 0;
\r
353 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
354 if (ret == ERROR_FILE_NOT_FOUND) return -1;
\r
359 int get_number(HKEY key, TCHAR *value, unsigned long *number) {
\r
360 return get_number(key, value, number, true);
\r
363 /* Replace NULL with CRLF. Leave NULL NULL as the end marker. */
\r
364 int format_double_null(TCHAR *dn, unsigned long dnlen, TCHAR **formatted, unsigned long *newlen) {
\r
365 unsigned long i, j;
\r
373 for (i = 0; i < dnlen; i++) if (! dn[i] && dn[i + 1]) ++*newlen;
\r
375 *formatted = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *newlen * sizeof(TCHAR));
\r
376 if (! *formatted) {
\r
381 for (i = 0, j = 0; i < dnlen; i++) {
\r
382 (*formatted)[j] = dn[i];
\r
385 (*formatted)[j] = _T('\r');
\r
386 (*formatted)[++j] = _T('\n');
\r
395 /* Strip CR and replace LF with NULL. */
\r
396 int unformat_double_null(TCHAR *dn, unsigned long dnlen, TCHAR **unformatted, unsigned long *newlen) {
\r
397 unsigned long i, j;
\r
405 for (i = 0; i < dnlen; i++) if (dn[i] != _T('\r')) ++*newlen;
\r
407 /* Skip blank lines. */
\r
408 for (i = 0; i < dnlen; i++) {
\r
409 if (dn[i] == _T('\r') && dn[i + 1] == _T('\n')) {
\r
410 /* This is the last CRLF. */
\r
411 if (i >= dnlen - 2) break;
\r
414 Strip at the start of the block or if the next characters are
\r
417 if (! i || (dn[i + 2] == _T('\r') && dn[i + 3] == _T('\n'))) {
\r
418 for (j = i + 2; j < dnlen; j++) dn[j - 2] = dn[j];
\r
419 dn[dnlen--] = _T('\0');
\r
420 dn[dnlen--] = _T('\0');
\r
427 /* Must end with two NULLs. */
\r
430 *unformatted = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *newlen * sizeof(TCHAR));
\r
431 if (! *unformatted) return 1;
\r
433 for (i = 0, j = 0; i < dnlen; i++) {
\r
434 if (dn[i] == _T('\r')) continue;
\r
435 if (dn[i] == _T('\n')) (*unformatted)[j] = _T('\0');
\r
436 else (*unformatted)[j] = dn[i];
\r
443 void override_milliseconds(TCHAR *service_name, HKEY key, TCHAR *value, unsigned long *buffer, unsigned long default_value, unsigned long event) {
\r
444 unsigned long type = REG_DWORD;
\r
445 unsigned long buflen = sizeof(unsigned long);
\r
447 unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);
\r
448 if (ret != ERROR_SUCCESS) {
\r
449 if (ret != ERROR_FILE_NOT_FOUND) {
\r
450 if (type != REG_DWORD) {
\r
451 TCHAR milliseconds[16];
\r
452 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), default_value);
\r
453 log_event(EVENTLOG_WARNING_TYPE, event, service_name, value, milliseconds, 0);
\r
455 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
460 if (! ok) *buffer = default_value;
\r
463 static int service_registry_path(const TCHAR *service_name, bool parameters, const TCHAR *sub, TCHAR *buffer, unsigned long buflen) {
\r
467 if (sub) ret = _sntprintf_s(buffer, buflen, _TRUNCATE, NSSM_REGISTRY _T("\\") NSSM_REG_PARAMETERS _T("\\%s"), service_name, sub);
\r
468 else ret = _sntprintf_s(buffer, buflen, _TRUNCATE, NSSM_REGISTRY _T("\\") NSSM_REG_PARAMETERS, service_name);
\r
470 else ret = _sntprintf_s(buffer, buflen, _TRUNCATE, NSSM_REGISTRY, service_name);
\r
475 static HKEY open_registry_key(const TCHAR *registry, REGSAM sam, bool must_exist) {
\r
478 if (sam & KEY_SET_VALUE) {
\r
479 if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, sam, 0, &key, 0) != ERROR_SUCCESS) {
\r
480 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
485 long error = RegOpenKeyEx(HKEY_LOCAL_MACHINE, registry, 0, sam, &key);
\r
486 if (error != ERROR_SUCCESS) {
\r
487 if (error == ERROR_FILE_NOT_FOUND && ! must_exist) return 0;
\r
488 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\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