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 TCHAR path[PATH_LENGTH];
\r
21 GetModuleFileName(0, path, _countof(path));
\r
23 /* Try to register the module but don't worry so much on failure */
\r
24 RegSetValueEx(key, _T("EventMessageFile"), 0, REG_SZ, (const unsigned char *) path, (unsigned long) (_tcslen(path) + 1) * sizeof(TCHAR));
\r
25 unsigned long types = EVENTLOG_INFORMATION_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_ERROR_TYPE;
\r
26 RegSetValueEx(key, _T("TypesSupported"), 0, REG_DWORD, (const unsigned char *) &types, sizeof(types));
\r
31 int create_parameters(nssm_service_t *service, bool editing) {
\r
32 /* Try to open the registry */
\r
33 HKEY key = open_registry(service->name, KEY_WRITE);
\r
34 if (! key) return 1;
\r
36 /* Try to create the parameters */
\r
37 if (set_expand_string(key, NSSM_REG_EXE, service->exe)) {
\r
38 RegDeleteKey(HKEY_LOCAL_MACHINE, NSSM_REGISTRY);
\r
42 if (set_expand_string(key, NSSM_REG_FLAGS, service->flags)) {
\r
43 RegDeleteKey(HKEY_LOCAL_MACHINE, NSSM_REGISTRY);
\r
47 if (set_expand_string(key, NSSM_REG_DIR, service->dir)) {
\r
48 RegDeleteKey(HKEY_LOCAL_MACHINE, NSSM_REGISTRY);
\r
53 /* Other non-default parameters. May fail. */
\r
54 if (service->priority != NORMAL_PRIORITY_CLASS) set_number(key, NSSM_REG_PRIORITY, service->priority);
\r
55 else if (editing) RegDeleteValue(key, NSSM_REG_PRIORITY);
\r
56 if (service->affinity) {
\r
58 if (! affinity_mask_to_string(service->affinity, &string)) {
\r
59 if (RegSetValueEx(key, NSSM_REG_AFFINITY, 0, REG_SZ, (const unsigned char *) string, (unsigned long) (_tcslen(string) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
60 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_AFFINITY, error_string(GetLastError()), 0);
\r
61 HeapFree(GetProcessHeap(), 0, string);
\r
65 if (string) HeapFree(GetProcessHeap(), 0, string);
\r
67 else if (editing) RegDeleteValue(key, NSSM_REG_AFFINITY);
\r
68 unsigned long stop_method_skip = ~service->stop_method;
\r
69 if (stop_method_skip) set_number(key, NSSM_REG_STOP_METHOD_SKIP, stop_method_skip);
\r
70 else if (editing) RegDeleteValue(key, NSSM_REG_STOP_METHOD_SKIP);
\r
71 if (service->default_exit_action < NSSM_NUM_EXIT_ACTIONS) create_exit_action(service->name, exit_action_strings[service->default_exit_action], editing);
\r
72 if (service->restart_delay) set_number(key, NSSM_REG_RESTART_DELAY, service->restart_delay);
\r
73 else if (editing) RegDeleteValue(key, NSSM_REG_RESTART_DELAY);
\r
74 if (service->throttle_delay != NSSM_RESET_THROTTLE_RESTART) set_number(key, NSSM_REG_THROTTLE, service->throttle_delay);
\r
75 else if (editing) RegDeleteValue(key, NSSM_REG_THROTTLE);
\r
76 if (service->kill_console_delay != NSSM_KILL_CONSOLE_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, service->kill_console_delay);
\r
77 else if (editing) RegDeleteValue(key, NSSM_REG_KILL_CONSOLE_GRACE_PERIOD);
\r
78 if (service->kill_window_delay != NSSM_KILL_WINDOW_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_WINDOW_GRACE_PERIOD, service->kill_window_delay);
\r
79 else if (editing) RegDeleteValue(key, NSSM_REG_KILL_WINDOW_GRACE_PERIOD);
\r
80 if (service->kill_threads_delay != NSSM_KILL_THREADS_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_THREADS_GRACE_PERIOD, service->kill_threads_delay);
\r
81 else if (editing) RegDeleteValue(key, NSSM_REG_KILL_THREADS_GRACE_PERIOD);
\r
82 if (service->stdin_path[0] || editing) {
\r
83 if (service->stdin_path[0]) set_expand_string(key, NSSM_REG_STDIN, service->stdin_path);
\r
84 else if (editing) RegDeleteValue(key, NSSM_REG_STDIN);
\r
85 if (service->stdin_sharing != NSSM_STDIN_SHARING) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_SHARING, service->stdin_sharing);
\r
86 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_SHARING);
\r
87 if (service->stdin_disposition != NSSM_STDIN_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_DISPOSITION, service->stdin_disposition);
\r
88 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_DISPOSITION);
\r
89 if (service->stdin_flags != NSSM_STDIN_FLAGS) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_FLAGS, service->stdin_flags);
\r
90 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_FLAGS);
\r
92 if (service->stdout_path[0] || editing) {
\r
93 if (service->stdout_path[0]) set_expand_string(key, NSSM_REG_STDOUT, service->stdout_path);
\r
94 else if (editing) RegDeleteValue(key, NSSM_REG_STDOUT);
\r
95 if (service->stdout_sharing != NSSM_STDOUT_SHARING) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_SHARING, service->stdout_sharing);
\r
96 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_SHARING);
\r
97 if (service->stdout_disposition != NSSM_STDOUT_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_DISPOSITION, service->stdout_disposition);
\r
98 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_DISPOSITION);
\r
99 if (service->stdout_flags != NSSM_STDOUT_FLAGS) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_FLAGS, service->stdout_flags);
\r
100 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_FLAGS);
\r
102 if (service->stderr_path[0] || editing) {
\r
103 if (service->stderr_path[0]) set_expand_string(key, NSSM_REG_STDERR, service->stderr_path);
\r
104 else if (editing) RegDeleteValue(key, NSSM_REG_STDERR);
\r
105 if (service->stderr_sharing != NSSM_STDERR_SHARING) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_SHARING, service->stderr_sharing);
\r
106 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_SHARING);
\r
107 if (service->stderr_disposition != NSSM_STDERR_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_DISPOSITION, service->stderr_disposition);
\r
108 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_DISPOSITION);
\r
109 if (service->stderr_flags != NSSM_STDERR_FLAGS) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_FLAGS, service->stderr_flags);
\r
110 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_FLAGS);
\r
112 if (service->rotate_files) set_number(key, NSSM_REG_ROTATE, 1);
\r
113 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE);
\r
114 if (service->rotate_stdout_online) set_number(key, NSSM_REG_ROTATE_ONLINE, 1);
\r
115 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_ONLINE);
\r
116 if (service->rotate_seconds) set_number(key, NSSM_REG_ROTATE_SECONDS, service->rotate_seconds);
\r
117 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_SECONDS);
\r
118 if (service->rotate_bytes_low) set_number(key, NSSM_REG_ROTATE_BYTES_LOW, service->rotate_bytes_low);
\r
119 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_BYTES_LOW);
\r
120 if (service->rotate_bytes_high) set_number(key, NSSM_REG_ROTATE_BYTES_HIGH, service->rotate_bytes_high);
\r
121 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_BYTES_HIGH);
\r
122 if (service->no_console) set_number(key, NSSM_REG_NO_CONSOLE, 1);
\r
123 else if (editing) RegDeleteValue(key, NSSM_REG_NO_CONSOLE);
\r
126 if (service->env) {
\r
127 if (RegSetValueEx(key, NSSM_REG_ENV, 0, REG_MULTI_SZ, (const unsigned char *) service->env, (unsigned long) service->envlen * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
128 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV, error_string(GetLastError()), 0);
\r
131 else if (editing) RegDeleteValue(key, NSSM_REG_ENV);
\r
132 if (service->env_extra) {
\r
133 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
134 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV_EXTRA, error_string(GetLastError()), 0);
\r
137 else if (editing) RegDeleteValue(key, NSSM_REG_ENV_EXTRA);
\r
139 /* Close registry. */
\r
145 int create_exit_action(TCHAR *service_name, const TCHAR *action_string, bool editing) {
\r
147 TCHAR registry[KEY_LENGTH];
\r
148 if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, NSSM_REGISTRY _T("\\%s"), service_name, NSSM_REG_EXIT) < 0) {
\r
149 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("NSSM_REG_EXIT"), _T("create_exit_action()"), 0);
\r
153 /* Try to open the registry */
\r
155 unsigned long disposition;
\r
156 if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, KEY_WRITE, 0, &key, &disposition) != ERROR_SUCCESS) {
\r
157 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
161 /* Do nothing if the key already existed */
\r
162 if (disposition == REG_OPENED_EXISTING_KEY && ! editing) {
\r
167 /* Create the default value */
\r
168 if (RegSetValueEx(key, 0, 0, REG_SZ, (const unsigned char *) action_string, (unsigned long) (_tcslen(action_string) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
169 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_EXIT, error_string(GetLastError()), 0);
\r
174 /* Close registry */
\r
180 int get_environment(TCHAR *service_name, HKEY key, TCHAR *value, TCHAR **env, unsigned long *envlen) {
\r
181 unsigned long type = REG_MULTI_SZ;
\r
183 /* Dummy test to find buffer size */
\r
184 unsigned long ret = RegQueryValueEx(key, value, 0, &type, NULL, envlen);
\r
185 if (ret != ERROR_SUCCESS) {
\r
188 /* The service probably doesn't have any environment configured */
\r
189 if (ret == ERROR_FILE_NOT_FOUND) return 0;
\r
190 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
194 if (type != REG_MULTI_SZ) {
\r
197 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_INVALID_ENVIRONMENT_STRING_TYPE, value, service_name, 0);
\r
201 /* Probably not possible */
\r
202 if (! *envlen) return 0;
\r
204 /* Previously initialised? */
\r
205 if (*env) HeapFree(GetProcessHeap(), 0, *env);
\r
207 *env = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, *envlen);
\r
210 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, value, _T("get_environment()"), 0);
\r
214 /* Actually get the strings */
\r
215 ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) *env, envlen);
\r
216 if (ret != ERROR_SUCCESS) {
\r
217 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
218 HeapFree(GetProcessHeap(), 0, *env);
\r
228 int get_string(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool expand, bool sanitise, bool must_exist) {
\r
229 TCHAR *buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, datalen);
\r
231 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, value, _T("get_string()"), 0);
\r
235 ZeroMemory(data, datalen);
\r
237 unsigned long type = REG_EXPAND_SZ;
\r
238 unsigned long buflen = datalen;
\r
240 unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);
\r
241 if (ret != ERROR_SUCCESS) {
\r
242 unsigned long error = GetLastError();
\r
243 HeapFree(GetProcessHeap(), 0, buffer);
\r
245 if (ret == ERROR_FILE_NOT_FOUND) {
\r
246 if (! must_exist) return 0;
\r
249 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(error), 0);
\r
253 /* Paths aren't allowed to contain quotes. */
\r
254 if (sanitise) PathUnquoteSpaces(buffer);
\r
256 /* Do we want to expand the string? */
\r
258 if (type == REG_EXPAND_SZ) type = REG_SZ;
\r
261 /* Technically we shouldn't expand environment strings from REG_SZ values */
\r
262 if (type != REG_EXPAND_SZ) {
\r
263 memmove(data, buffer, buflen);
\r
264 HeapFree(GetProcessHeap(), 0, buffer);
\r
268 ret = ExpandEnvironmentStrings((TCHAR *) buffer, data, datalen);
\r
269 if (! ret || ret > datalen) {
\r
270 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_EXPANDENVIRONMENTSTRINGS_FAILED, buffer, error_string(GetLastError()), 0);
\r
271 HeapFree(GetProcessHeap(), 0, buffer);
\r
275 HeapFree(GetProcessHeap(), 0, buffer);
\r
279 int get_string(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise) {
\r
280 return get_string(key, value, data, datalen, false, sanitise, true);
\r
283 int expand_parameter(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise, bool must_exist) {
\r
284 return get_string(key, value, data, datalen, true, sanitise, must_exist);
\r
287 int expand_parameter(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise) {
\r
288 return expand_parameter(key, value, data, datalen, sanitise, true);
\r
292 Sets a string in the registry.
\r
293 Returns: 0 if it was set.
\r
296 int set_string(HKEY key, TCHAR *value, TCHAR *string, bool expand) {
\r
297 unsigned long type = expand ? REG_EXPAND_SZ : REG_SZ;
\r
298 if (RegSetValueEx(key, value, 0, type, (const unsigned char *) string, (unsigned long) (_tcslen(string) + 1) * sizeof(TCHAR)) == ERROR_SUCCESS) return 0;
\r
299 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
303 int set_string(HKEY key, TCHAR *value, TCHAR *string) {
\r
304 return set_string(key, value, string, false);
\r
308 int set_expand_string(HKEY key, TCHAR *value, TCHAR *string) {
\r
309 return set_string(key, value, string, true);
\r
314 Set an unsigned long in the registry.
\r
315 Returns: 0 if it was set.
\r
318 int set_number(HKEY key, TCHAR *value, unsigned long number) {
\r
319 if (RegSetValueEx(key, value, 0, REG_DWORD, (const unsigned char *) &number, sizeof(number)) == ERROR_SUCCESS) return 0;
\r
320 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
325 Query an unsigned long from the registry.
\r
326 Returns: 1 if a number was retrieved.
\r
327 0 if none was found and must_exist is false.
\r
328 -1 if none was found and must_exist is true.
\r
331 int get_number(HKEY key, TCHAR *value, unsigned long *number, bool must_exist) {
\r
332 unsigned long type = REG_DWORD;
\r
333 unsigned long number_len = sizeof(unsigned long);
\r
335 int ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) number, &number_len);
\r
336 if (ret == ERROR_SUCCESS) return 1;
\r
338 if (ret == ERROR_FILE_NOT_FOUND) {
\r
339 if (! must_exist) return 0;
\r
342 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
343 if (ret == ERROR_FILE_NOT_FOUND) return -1;
\r
348 int get_number(HKEY key, TCHAR *value, unsigned long *number) {
\r
349 return get_number(key, value, number, true);
\r
352 /* Replace NULL with CRLF. Leave NULL NULL as the end marker. */
\r
353 int format_double_null(TCHAR *dn, unsigned long dnlen, TCHAR **formatted, unsigned long *newlen) {
\r
354 unsigned long i, j;
\r
362 for (i = 0; i < dnlen; i++) if (! dn[i] && dn[i + 1]) ++*newlen;
\r
364 *formatted = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *newlen * sizeof(TCHAR));
\r
365 if (! *formatted) {
\r
370 for (i = 0, j = 0; i < dnlen; i++) {
\r
371 (*formatted)[j] = dn[i];
\r
374 (*formatted)[j] = _T('\r');
\r
375 (*formatted)[++j] = _T('\n');
\r
384 /* Strip CR and replace LF with NULL. */
\r
385 int unformat_double_null(TCHAR *dn, unsigned long dnlen, TCHAR **unformatted, unsigned long *newlen) {
\r
386 unsigned long i, j;
\r
394 for (i = 0; i < dnlen; i++) if (dn[i] != _T('\r')) ++*newlen;
\r
396 /* Skip blank lines. */
\r
397 for (i = 0; i < dnlen; i++) {
\r
398 if (dn[i] == _T('\r') && dn[i + 1] == _T('\n')) {
\r
399 /* This is the last CRLF. */
\r
400 if (i >= dnlen - 2) break;
\r
403 Strip at the start of the block or if the next characters are
\r
406 if (! i || (dn[i + 2] == _T('\r') && dn[i + 3] == _T('\n'))) {
\r
407 for (j = i + 2; j < dnlen; j++) dn[j - 2] = dn[j];
\r
408 dn[dnlen--] = _T('\0');
\r
409 dn[dnlen--] = _T('\0');
\r
416 /* Must end with two NULLs. */
\r
419 *unformatted = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *newlen * sizeof(TCHAR));
\r
420 if (! *unformatted) return 1;
\r
422 for (i = 0, j = 0; i < dnlen; i++) {
\r
423 if (dn[i] == _T('\r')) continue;
\r
424 if (dn[i] == _T('\n')) (*unformatted)[j] = _T('\0');
\r
425 else (*unformatted)[j] = dn[i];
\r
432 void override_milliseconds(TCHAR *service_name, HKEY key, TCHAR *value, unsigned long *buffer, unsigned long default_value, unsigned long event) {
\r
433 unsigned long type = REG_DWORD;
\r
434 unsigned long buflen = sizeof(unsigned long);
\r
436 unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);
\r
437 if (ret != ERROR_SUCCESS) {
\r
438 if (ret != ERROR_FILE_NOT_FOUND) {
\r
439 if (type != REG_DWORD) {
\r
440 TCHAR milliseconds[16];
\r
441 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), default_value);
\r
442 log_event(EVENTLOG_WARNING_TYPE, event, service_name, value, milliseconds, 0);
\r
444 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
449 if (! ok) *buffer = default_value;
\r
452 HKEY open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam) {
\r
454 TCHAR registry[KEY_LENGTH];
\r
458 if (sub) ret = _sntprintf_s(registry, _countof(registry), _TRUNCATE, NSSM_REGISTRY _T("\\%s"), service_name, sub);
\r
459 else ret = _sntprintf_s(registry, _countof(registry), _TRUNCATE, NSSM_REGISTRY, service_name);
\r
461 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("NSSM_REGISTRY"), _T("open_registry()"), 0);
\r
465 if (sam & KEY_WRITE) {
\r
466 if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, sam, 0, &key, 0) != ERROR_SUCCESS) {
\r
467 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
472 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, registry, 0, sam, &key) != ERROR_SUCCESS) {
\r
473 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
481 HKEY open_registry(const TCHAR *service_name, REGSAM sam) {
\r
482 return open_registry(service_name, 0, sam);
\r
485 int get_io_parameters(nssm_service_t *service, HKEY key) {
\r
487 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)) {
\r
488 service->stdin_sharing = service->stdin_disposition = service->stdin_flags = 0;
\r
489 ZeroMemory(service->stdin_path, _countof(service->stdin_path) * sizeof(TCHAR));
\r
494 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)) {
\r
495 service->stdout_sharing = service->stdout_disposition = service->stdout_flags = 0;
\r
496 ZeroMemory(service->stdout_path, _countof(service->stdout_path) * sizeof(TCHAR));
\r
501 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)) {
\r
502 service->stderr_sharing = service->stderr_disposition = service->stderr_flags = 0;
\r
503 ZeroMemory(service->stderr_path, _countof(service->stderr_path) * sizeof(TCHAR));
\r
510 int get_parameters(nssm_service_t *service, STARTUPINFO *si) {
\r
513 /* Try to open the registry */
\r
514 HKEY key = open_registry(service->name, KEY_READ);
\r
515 if (! key) return 1;
\r
517 /* Don't expand parameters when retrieving for the GUI. */
\r
518 bool expand = si ? true : false;
\r
520 /* Try to get executable file - MUST succeed */
\r
521 if (get_string(key, NSSM_REG_EXE, service->exe, sizeof(service->exe), expand, false, true)) {
\r
526 /* Try to get flags - may fail and we don't care */
\r
527 if (get_string(key, NSSM_REG_FLAGS, service->flags, sizeof(service->flags), expand, false, true)) {
\r
528 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_FLAGS, NSSM_REG_FLAGS, service->name, service->exe, 0);
\r
529 ZeroMemory(service->flags, sizeof(service->flags));
\r
532 /* Try to get startup directory - may fail and we fall back to a default */
\r
533 if (get_string(key, NSSM_REG_DIR, service->dir, sizeof(service->dir), expand, true, true) || ! service->dir[0]) {
\r
534 _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);
\r
535 strip_basename(service->dir);
\r
536 if (service->dir[0] == _T('\0')) {
\r
538 ret = GetWindowsDirectory(service->dir, sizeof(service->dir));
\r
539 if (! ret || ret > sizeof(service->dir)) {
\r
540 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_NO_DIR_AND_NO_FALLBACK, NSSM_REG_DIR, service->name, 0);
\r
545 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_DIR, NSSM_REG_DIR, service->name, service->dir, 0);
\r
548 /* Try to get processor affinity - may fail. */
\r
550 if (get_string(key, NSSM_REG_AFFINITY, buffer, sizeof(buffer), false, false, false) || ! buffer[0]) service->affinity = 0LL;
\r
551 else if (affinity_string_to_mask(buffer, &service->affinity)) {
\r
552 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_AFFINITY_MASK, service->name, buffer);
\r
553 service->affinity = 0LL;
\r
556 DWORD_PTR affinity, system_affinity;
\r
558 if (GetProcessAffinityMask(GetCurrentProcess(), &affinity, &system_affinity)) {
\r
559 _int64 effective_affinity = service->affinity & system_affinity;
\r
560 if (effective_affinity != service->affinity) {
\r
562 if (! affinity_mask_to_string(system_affinity, &system)) {
\r
563 TCHAR *effective = 0;
\r
564 if (! affinity_mask_to_string(effective_affinity, &effective)) {
\r
565 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_EFFECTIVE_AFFINITY_MASK, service->name, buffer, system, effective, 0);
\r
567 HeapFree(GetProcessHeap(), 0, effective);
\r
569 HeapFree(GetProcessHeap(), 0, system);
\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 /* Try to get priority - may fail. */
\r
580 unsigned long priority;
\r
581 if (get_number(key, NSSM_REG_PRIORITY, &priority, false) == 1) {
\r
582 if (priority == (priority & priority_mask())) service->priority = priority;
\r
583 else log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_PRIORITY, service->name, NSSM_REG_PRIORITY, 0);
\r
586 /* Try to get file rotation settings - may fail. */
\r
587 unsigned long rotate_files;
\r
588 if (get_number(key, NSSM_REG_ROTATE, &rotate_files, false) == 1) {
\r
589 if (rotate_files) service->rotate_files = true;
\r
590 else service->rotate_files = false;
\r
592 else service->rotate_files = false;
\r
593 if (get_number(key, NSSM_REG_ROTATE_ONLINE, &rotate_files, false) == 1) {
\r
594 if (rotate_files) service->rotate_stdout_online = service->rotate_stderr_online = true;
\r
595 else service->rotate_stdout_online = service->rotate_stderr_online = false;
\r
597 else service->rotate_stdout_online = service->rotate_stderr_online = false;
\r
598 if (get_number(key, NSSM_REG_ROTATE_SECONDS, &service->rotate_seconds, false) != 1) service->rotate_seconds = 0;
\r
599 if (get_number(key, NSSM_REG_ROTATE_BYTES_LOW, &service->rotate_bytes_low, false) != 1) service->rotate_bytes_low = 0;
\r
600 if (get_number(key, NSSM_REG_ROTATE_BYTES_HIGH, &service->rotate_bytes_high, false) != 1) service->rotate_bytes_high = 0;
\r
602 /* Try to get force new console setting - may fail. */
\r
603 if (get_number(key, NSSM_REG_NO_CONSOLE, &service->no_console, false) != 1) service->no_console = 0;
\r
605 /* Change to startup directory in case stdout/stderr are relative paths. */
\r
606 TCHAR cwd[PATH_LENGTH];
\r
607 GetCurrentDirectory(_countof(cwd), cwd);
\r
608 SetCurrentDirectory(service->dir);
\r
610 /* Try to get stdout and stderr */
\r
611 if (get_io_parameters(service, key)) {
\r
612 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);
\r
614 SetCurrentDirectory(cwd);
\r
618 /* Change back in case the startup directory needs to be deleted. */
\r
619 SetCurrentDirectory(cwd);
\r
621 /* Try to get mandatory restart delay */
\r
622 override_milliseconds(service->name, key, NSSM_REG_RESTART_DELAY, &service->restart_delay, 0, NSSM_EVENT_BOGUS_RESTART_DELAY);
\r
624 /* Try to get throttle restart delay */
\r
625 override_milliseconds(service->name, key, NSSM_REG_THROTTLE, &service->throttle_delay, NSSM_RESET_THROTTLE_RESTART, NSSM_EVENT_BOGUS_THROTTLE);
\r
627 /* Try to get service stop flags. */
\r
628 unsigned long type = REG_DWORD;
\r
629 unsigned long stop_method_skip;
\r
630 unsigned long buflen = sizeof(stop_method_skip);
\r
631 bool stop_ok = false;
\r
632 ret = RegQueryValueEx(key, NSSM_REG_STOP_METHOD_SKIP, 0, &type, (unsigned char *) &stop_method_skip, &buflen);
\r
633 if (ret != ERROR_SUCCESS) {
\r
634 if (ret != ERROR_FILE_NOT_FOUND) {
\r
635 if (type != REG_DWORD) {
\r
636 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_STOP_METHOD_SKIP, service->name, NSSM_REG_STOP_METHOD_SKIP, NSSM, 0);
\r
638 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, NSSM_REG_STOP_METHOD_SKIP, error_string(GetLastError()), 0);
\r
641 else stop_ok = true;
\r
643 /* Try all methods except those requested to be skipped. */
\r
644 service->stop_method = ~0;
\r
645 if (stop_ok) service->stop_method &= ~stop_method_skip;
\r
647 /* Try to get kill delays - may fail. */
\r
648 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
649 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
650 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
652 /* Try to get default exit action. */
\r
653 bool default_action;
\r
654 service->default_exit_action = NSSM_EXIT_RESTART;
\r
655 TCHAR action_string[ACTION_LEN];
\r
656 if (! get_exit_action(service->name, 0, action_string, &default_action)) {
\r
657 for (int i = 0; exit_action_strings[i]; i++) {
\r
658 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
659 service->default_exit_action = i;
\r
665 /* Close registry */
\r
672 Sets the string for the exit action corresponding to the exit code.
\r
674 ret is a pointer to an unsigned long containing the exit code.
\r
675 If ret is NULL, we retrieve the default exit action unconditionally.
\r
677 action is a buffer which receives the string.
\r
679 default_action is a pointer to a bool which is set to false if there
\r
680 was an explicit string for the given exit code, or true if we are
\r
681 returning the default action.
\r
683 Returns: 0 on success.
\r
686 int get_exit_action(const TCHAR *service_name, unsigned long *ret, TCHAR *action, bool *default_action) {
\r
687 /* Are we returning the default action or a status-specific one? */
\r
688 *default_action = ! ret;
\r
690 /* Try to open the registry */
\r
691 HKEY key = open_registry(service_name, NSSM_REG_EXIT, KEY_READ);
\r
692 if (! key) return 1;
\r
694 unsigned long type = REG_SZ;
\r
695 unsigned long action_len = ACTION_LEN;
\r
698 if (! ret) code[0] = _T('\0');
\r
699 else if (_sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), *ret) < 0) {
\r
701 return get_exit_action(service_name, 0, action, default_action);
\r
703 if (RegQueryValueEx(key, code, 0, &type, (unsigned char *) action, &action_len) != ERROR_SUCCESS) {
\r
705 /* Try again with * as the key if an exit code was defined */
\r
706 if (ret) return get_exit_action(service_name, 0, action, default_action);
\r
710 /* Close registry */
\r