3 extern const TCHAR *exit_action_strings[];
\r
5 static int service_registry_path(const TCHAR *service_name, bool parameters, const TCHAR *sub, TCHAR *buffer, unsigned long buflen) {
\r
9 if (sub) ret = _sntprintf_s(buffer, buflen, _TRUNCATE, NSSM_REGISTRY _T("\\") NSSM_REG_PARAMETERS _T("\\%s"), service_name, sub);
\r
10 else ret = _sntprintf_s(buffer, buflen, _TRUNCATE, NSSM_REGISTRY _T("\\") NSSM_REG_PARAMETERS, service_name);
\r
12 else ret = _sntprintf_s(buffer, buflen, _TRUNCATE, NSSM_REGISTRY, service_name);
\r
17 static long open_registry_key(const TCHAR *registry, REGSAM sam, HKEY *key, bool must_exist) {
\r
20 if (sam & KEY_SET_VALUE) {
\r
21 error = RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, sam, 0, key, 0);
\r
22 if (error != ERROR_SUCCESS) {
\r
24 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
29 error = RegOpenKeyEx(HKEY_LOCAL_MACHINE, registry, 0, sam, key);
\r
30 if (error != ERROR_SUCCESS) {
\r
32 if (error != ERROR_FILE_NOT_FOUND || must_exist) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
39 static HKEY open_registry_key(const TCHAR *registry, REGSAM sam, bool must_exist) {
\r
41 long error = open_registry_key(registry, sam, &key, must_exist);
\r
45 int create_messages() {
\r
48 TCHAR registry[KEY_LENGTH];
\r
49 if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, _T("SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\%s"), NSSM) < 0) {
\r
50 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("eventlog registry"), _T("create_messages()"), 0);
\r
54 if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, KEY_WRITE, 0, &key, 0) != ERROR_SUCCESS) {
\r
55 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
59 /* Get path of this program */
\r
60 const TCHAR *path = nssm_unquoted_imagepath();
\r
62 /* Try to register the module but don't worry so much on failure */
\r
63 RegSetValueEx(key, _T("EventMessageFile"), 0, REG_SZ, (const unsigned char *) path, (unsigned long) (_tcslen(path) + 1) * sizeof(TCHAR));
\r
64 unsigned long types = EVENTLOG_INFORMATION_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_ERROR_TYPE;
\r
65 RegSetValueEx(key, _T("TypesSupported"), 0, REG_DWORD, (const unsigned char *) &types, sizeof(types));
\r
70 int create_parameters(nssm_service_t *service, bool editing) {
\r
71 /* Try to open the registry */
\r
72 HKEY key = open_registry(service->name, KEY_WRITE);
\r
73 if (! key) return 1;
\r
75 /* Remember parameters in case we need to delete them. */
\r
76 TCHAR registry[KEY_LENGTH];
\r
77 int ret = service_registry_path(service->name, true, 0, registry, _countof(registry));
\r
79 /* Try to create the parameters */
\r
80 if (set_expand_string(key, NSSM_REG_EXE, service->exe)) {
\r
81 if (ret > 0) RegDeleteKey(HKEY_LOCAL_MACHINE, registry);
\r
85 if (set_expand_string(key, NSSM_REG_FLAGS, service->flags)) {
\r
86 if (ret > 0) RegDeleteKey(HKEY_LOCAL_MACHINE, registry);
\r
90 if (set_expand_string(key, NSSM_REG_DIR, service->dir)) {
\r
91 if (ret > 0) RegDeleteKey(HKEY_LOCAL_MACHINE, registry);
\r
96 /* Other non-default parameters. May fail. */
\r
97 if (service->priority != NORMAL_PRIORITY_CLASS) set_number(key, NSSM_REG_PRIORITY, service->priority);
\r
98 else if (editing) RegDeleteValue(key, NSSM_REG_PRIORITY);
\r
99 if (service->affinity) {
\r
101 if (! affinity_mask_to_string(service->affinity, &string)) {
\r
102 if (RegSetValueEx(key, NSSM_REG_AFFINITY, 0, REG_SZ, (const unsigned char *) string, (unsigned long) (_tcslen(string) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
103 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_AFFINITY, error_string(GetLastError()), 0);
\r
104 HeapFree(GetProcessHeap(), 0, string);
\r
108 if (string) HeapFree(GetProcessHeap(), 0, string);
\r
110 else if (editing) RegDeleteValue(key, NSSM_REG_AFFINITY);
\r
111 unsigned long stop_method_skip = ~service->stop_method;
\r
112 if (stop_method_skip) set_number(key, NSSM_REG_STOP_METHOD_SKIP, stop_method_skip);
\r
113 else if (editing) RegDeleteValue(key, NSSM_REG_STOP_METHOD_SKIP);
\r
114 if (service->default_exit_action < NSSM_NUM_EXIT_ACTIONS) create_exit_action(service->name, exit_action_strings[service->default_exit_action], editing);
\r
115 if (service->restart_delay) set_number(key, NSSM_REG_RESTART_DELAY, service->restart_delay);
\r
116 else if (editing) RegDeleteValue(key, NSSM_REG_RESTART_DELAY);
\r
117 if (service->throttle_delay != NSSM_RESET_THROTTLE_RESTART) set_number(key, NSSM_REG_THROTTLE, service->throttle_delay);
\r
118 else if (editing) RegDeleteValue(key, NSSM_REG_THROTTLE);
\r
119 if (service->kill_console_delay != NSSM_KILL_CONSOLE_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, service->kill_console_delay);
\r
120 else if (editing) RegDeleteValue(key, NSSM_REG_KILL_CONSOLE_GRACE_PERIOD);
\r
121 if (service->kill_window_delay != NSSM_KILL_WINDOW_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_WINDOW_GRACE_PERIOD, service->kill_window_delay);
\r
122 else if (editing) RegDeleteValue(key, NSSM_REG_KILL_WINDOW_GRACE_PERIOD);
\r
123 if (service->kill_threads_delay != NSSM_KILL_THREADS_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_THREADS_GRACE_PERIOD, service->kill_threads_delay);
\r
124 else if (editing) RegDeleteValue(key, NSSM_REG_KILL_THREADS_GRACE_PERIOD);
\r
125 if (! service->kill_process_tree) set_number(key, NSSM_REG_KILL_PROCESS_TREE, 0);
\r
126 else if (editing) RegDeleteValue(key, NSSM_REG_KILL_PROCESS_TREE);
\r
127 if (service->stdin_path[0] || editing) {
\r
128 if (service->stdin_path[0]) set_expand_string(key, NSSM_REG_STDIN, service->stdin_path);
\r
129 else if (editing) RegDeleteValue(key, NSSM_REG_STDIN);
\r
130 if (service->stdin_sharing != NSSM_STDIN_SHARING) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_SHARING, service->stdin_sharing);
\r
131 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_SHARING);
\r
132 if (service->stdin_disposition != NSSM_STDIN_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_DISPOSITION, service->stdin_disposition);
\r
133 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_DISPOSITION);
\r
134 if (service->stdin_flags != NSSM_STDIN_FLAGS) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_FLAGS, service->stdin_flags);
\r
135 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_FLAGS);
\r
137 if (service->stdout_path[0] || editing) {
\r
138 if (service->stdout_path[0]) set_expand_string(key, NSSM_REG_STDOUT, service->stdout_path);
\r
139 else if (editing) RegDeleteValue(key, NSSM_REG_STDOUT);
\r
140 if (service->stdout_sharing != NSSM_STDOUT_SHARING) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_SHARING, service->stdout_sharing);
\r
141 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_SHARING);
\r
142 if (service->stdout_disposition != NSSM_STDOUT_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_DISPOSITION, service->stdout_disposition);
\r
143 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_DISPOSITION);
\r
144 if (service->stdout_flags != NSSM_STDOUT_FLAGS) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_FLAGS, service->stdout_flags);
\r
145 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_FLAGS);
\r
146 if (service->stdout_copy_and_truncate) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_COPY_AND_TRUNCATE, 1);
\r
147 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_COPY_AND_TRUNCATE);
\r
149 if (service->stderr_path[0] || editing) {
\r
150 if (service->stderr_path[0]) set_expand_string(key, NSSM_REG_STDERR, service->stderr_path);
\r
151 else if (editing) RegDeleteValue(key, NSSM_REG_STDERR);
\r
152 if (service->stderr_sharing != NSSM_STDERR_SHARING) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_SHARING, service->stderr_sharing);
\r
153 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_SHARING);
\r
154 if (service->stderr_disposition != NSSM_STDERR_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_DISPOSITION, service->stderr_disposition);
\r
155 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_DISPOSITION);
\r
156 if (service->stderr_flags != NSSM_STDERR_FLAGS) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_FLAGS, service->stderr_flags);
\r
157 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_FLAGS);
\r
158 if (service->stderr_copy_and_truncate) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_COPY_AND_TRUNCATE, 1);
\r
159 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_COPY_AND_TRUNCATE);
\r
161 if (service->hook_share_output_handles) set_number(key, NSSM_REG_HOOK_SHARE_OUTPUT_HANDLES, 1);
\r
162 else if (editing) RegDeleteValue(key, NSSM_REG_HOOK_SHARE_OUTPUT_HANDLES);
\r
163 if (service->rotate_files) set_number(key, NSSM_REG_ROTATE, 1);
\r
164 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE);
\r
165 if (service->rotate_stdout_online) set_number(key, NSSM_REG_ROTATE_ONLINE, 1);
\r
166 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_ONLINE);
\r
167 if (service->rotate_seconds) set_number(key, NSSM_REG_ROTATE_SECONDS, service->rotate_seconds);
\r
168 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_SECONDS);
\r
169 if (service->rotate_bytes_low) set_number(key, NSSM_REG_ROTATE_BYTES_LOW, service->rotate_bytes_low);
\r
170 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_BYTES_LOW);
\r
171 if (service->rotate_bytes_high) set_number(key, NSSM_REG_ROTATE_BYTES_HIGH, service->rotate_bytes_high);
\r
172 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_BYTES_HIGH);
\r
173 if (service->rotate_delay != NSSM_ROTATE_DELAY) set_number(key, NSSM_REG_ROTATE_DELAY, service->rotate_delay);
\r
174 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_DELAY);
\r
175 if (service->no_console) set_number(key, NSSM_REG_NO_CONSOLE, 1);
\r
176 else if (editing) RegDeleteValue(key, NSSM_REG_NO_CONSOLE);
\r
179 if (service->env) {
\r
180 if (RegSetValueEx(key, NSSM_REG_ENV, 0, REG_MULTI_SZ, (const unsigned char *) service->env, (unsigned long) service->envlen * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
181 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV, error_string(GetLastError()), 0);
\r
184 else if (editing) RegDeleteValue(key, NSSM_REG_ENV);
\r
185 if (service->env_extra) {
\r
186 if (RegSetValueEx(key, NSSM_REG_ENV_EXTRA, 0, REG_MULTI_SZ, (const unsigned char *) service->env_extra, (unsigned long) service->env_extralen * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
187 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV_EXTRA, error_string(GetLastError()), 0);
\r
190 else if (editing) RegDeleteValue(key, NSSM_REG_ENV_EXTRA);
\r
192 /* Close registry. */
\r
198 int create_exit_action(TCHAR *service_name, const TCHAR *action_string, bool editing) {
\r
200 TCHAR registry[KEY_LENGTH];
\r
201 if (service_registry_path(service_name, true, NSSM_REG_EXIT, registry, _countof(registry)) < 0) {
\r
202 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("NSSM_REG_EXIT"), _T("create_exit_action()"), 0);
\r
206 /* Try to open the registry */
\r
208 unsigned long disposition;
\r
209 if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, KEY_WRITE, 0, &key, &disposition) != ERROR_SUCCESS) {
\r
210 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
214 /* Do nothing if the key already existed */
\r
215 if (disposition == REG_OPENED_EXISTING_KEY && ! editing) {
\r
220 /* Create the default value */
\r
221 if (RegSetValueEx(key, 0, 0, REG_SZ, (const unsigned char *) action_string, (unsigned long) (_tcslen(action_string) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
222 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_EXIT, error_string(GetLastError()), 0);
\r
227 /* Close registry */
\r
233 int get_environment(TCHAR *service_name, HKEY key, TCHAR *value, TCHAR **env, unsigned long *envlen) {
\r
234 unsigned long type = REG_MULTI_SZ;
\r
235 unsigned long envsize;
\r
239 /* Dummy test to find buffer size */
\r
240 unsigned long ret = RegQueryValueEx(key, value, 0, &type, NULL, &envsize);
\r
241 if (ret != ERROR_SUCCESS) {
\r
243 /* The service probably doesn't have any environment configured */
\r
244 if (ret == ERROR_FILE_NOT_FOUND) return 0;
\r
245 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
249 if (type != REG_MULTI_SZ) {
\r
250 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_INVALID_ENVIRONMENT_STRING_TYPE, value, service_name, 0);
\r
255 /* Probably not possible */
\r
256 if (! envsize) return 0;
\r
258 /* Previously initialised? */
\r
259 if (*env) HeapFree(GetProcessHeap(), 0, *env);
\r
261 *env = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, envsize);
\r
263 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, value, _T("get_environment()"), 0);
\r
267 /* Actually get the strings. */
\r
268 ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) *env, &envsize);
\r
269 if (ret != ERROR_SUCCESS) {
\r
270 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(ret), 0);
\r
271 HeapFree(GetProcessHeap(), 0, *env);
\r
276 /* Value retrieved by RegQueryValueEx() is SIZE not COUNT. */
\r
277 *envlen = (unsigned long) environment_length(env);
\r
283 int get_string(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool expand, bool sanitise, bool must_exist) {
\r
284 TCHAR *buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, datalen);
\r
286 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, value, _T("get_string()"), 0);
\r
290 ZeroMemory(data, datalen);
\r
292 unsigned long type = REG_EXPAND_SZ;
\r
293 unsigned long buflen = datalen;
\r
295 unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);
\r
296 if (ret != ERROR_SUCCESS) {
\r
297 HeapFree(GetProcessHeap(), 0, buffer);
\r
299 if (ret == ERROR_FILE_NOT_FOUND) {
\r
300 if (! must_exist) return 0;
\r
303 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(ret), 0);
\r
307 /* Paths aren't allowed to contain quotes. */
\r
308 if (sanitise) PathUnquoteSpaces(buffer);
\r
310 /* Do we want to expand the string? */
\r
312 if (type == REG_EXPAND_SZ) type = REG_SZ;
\r
315 /* Technically we shouldn't expand environment strings from REG_SZ values */
\r
316 if (type != REG_EXPAND_SZ) {
\r
317 memmove(data, buffer, buflen);
\r
318 HeapFree(GetProcessHeap(), 0, buffer);
\r
322 ret = ExpandEnvironmentStrings((TCHAR *) buffer, data, datalen);
\r
323 if (! ret || ret > datalen) {
\r
324 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_EXPANDENVIRONMENTSTRINGS_FAILED, buffer, error_string(GetLastError()), 0);
\r
325 HeapFree(GetProcessHeap(), 0, buffer);
\r
329 HeapFree(GetProcessHeap(), 0, buffer);
\r
333 int get_string(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise) {
\r
334 return get_string(key, value, data, datalen, false, sanitise, true);
\r
337 int expand_parameter(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise, bool must_exist) {
\r
338 return get_string(key, value, data, datalen, true, sanitise, must_exist);
\r
341 int expand_parameter(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise) {
\r
342 return expand_parameter(key, value, data, datalen, sanitise, true);
\r
346 Sets a string in the registry.
\r
347 Returns: 0 if it was set.
\r
350 int set_string(HKEY key, TCHAR *value, TCHAR *string, bool expand) {
\r
351 unsigned long type = expand ? REG_EXPAND_SZ : REG_SZ;
\r
352 if (RegSetValueEx(key, value, 0, type, (const unsigned char *) string, (unsigned long) (_tcslen(string) + 1) * sizeof(TCHAR)) == ERROR_SUCCESS) return 0;
\r
353 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
357 int set_string(HKEY key, TCHAR *value, TCHAR *string) {
\r
358 return set_string(key, value, string, false);
\r
361 int set_expand_string(HKEY key, TCHAR *value, TCHAR *string) {
\r
362 return set_string(key, value, string, true);
\r
366 Set an unsigned long in the registry.
\r
367 Returns: 0 if it was set.
\r
370 int set_number(HKEY key, TCHAR *value, unsigned long number) {
\r
371 if (RegSetValueEx(key, value, 0, REG_DWORD, (const unsigned char *) &number, sizeof(number)) == ERROR_SUCCESS) return 0;
\r
372 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
377 Query an unsigned long from the registry.
\r
378 Returns: 1 if a number was retrieved.
\r
379 0 if none was found and must_exist is false.
\r
380 -1 if none was found and must_exist is true.
\r
383 int get_number(HKEY key, TCHAR *value, unsigned long *number, bool must_exist) {
\r
384 unsigned long type = REG_DWORD;
\r
385 unsigned long number_len = sizeof(unsigned long);
\r
387 int ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) number, &number_len);
\r
388 if (ret == ERROR_SUCCESS) return 1;
\r
390 if (ret == ERROR_FILE_NOT_FOUND) {
\r
391 if (! must_exist) return 0;
\r
394 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(ret), 0);
\r
395 if (ret == ERROR_FILE_NOT_FOUND) return -1;
\r
400 int get_number(HKEY key, TCHAR *value, unsigned long *number) {
\r
401 return get_number(key, value, number, true);
\r
404 /* Replace NULL with CRLF. Leave NULL NULL as the end marker. */
\r
405 int format_double_null(TCHAR *dn, unsigned long dnlen, TCHAR **formatted, unsigned long *newlen) {
\r
406 unsigned long i, j;
\r
414 for (i = 0; i < dnlen; i++) if (! dn[i] && dn[i + 1]) ++*newlen;
\r
416 *formatted = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *newlen * sizeof(TCHAR));
\r
417 if (! *formatted) {
\r
422 for (i = 0, j = 0; i < dnlen; i++) {
\r
423 (*formatted)[j] = dn[i];
\r
426 (*formatted)[j] = _T('\r');
\r
427 (*formatted)[++j] = _T('\n');
\r
436 /* Strip CR and replace LF with NULL. */
\r
437 int unformat_double_null(TCHAR *dn, unsigned long dnlen, TCHAR **unformatted, unsigned long *newlen) {
\r
438 unsigned long i, j;
\r
446 for (i = 0; i < dnlen; i++) if (dn[i] != _T('\r')) ++*newlen;
\r
448 /* Skip blank lines. */
\r
449 for (i = 0; i < dnlen; i++) {
\r
450 if (dn[i] == _T('\r') && dn[i + 1] == _T('\n')) {
\r
451 /* This is the last CRLF. */
\r
452 if (i >= dnlen - 2) break;
\r
455 Strip at the start of the block or if the next characters are
\r
458 if (! i || (dn[i + 2] == _T('\r') && dn[i + 3] == _T('\n'))) {
\r
459 for (j = i + 2; j < dnlen; j++) dn[j - 2] = dn[j];
\r
460 dn[dnlen--] = _T('\0');
\r
461 dn[dnlen--] = _T('\0');
\r
468 /* Must end with two NULLs. */
\r
471 *unformatted = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *newlen * sizeof(TCHAR));
\r
472 if (! *unformatted) return 1;
\r
474 for (i = 0, j = 0; i < dnlen; i++) {
\r
475 if (dn[i] == _T('\r')) continue;
\r
476 if (dn[i] == _T('\n')) (*unformatted)[j] = _T('\0');
\r
477 else (*unformatted)[j] = dn[i];
\r
484 void override_milliseconds(TCHAR *service_name, HKEY key, TCHAR *value, unsigned long *buffer, unsigned long default_value, unsigned long event) {
\r
485 unsigned long type = REG_DWORD;
\r
486 unsigned long buflen = sizeof(unsigned long);
\r
488 unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);
\r
489 if (ret != ERROR_SUCCESS) {
\r
490 if (ret != ERROR_FILE_NOT_FOUND) {
\r
491 if (type != REG_DWORD) {
\r
492 TCHAR milliseconds[16];
\r
493 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), default_value);
\r
494 log_event(EVENTLOG_WARNING_TYPE, event, service_name, value, milliseconds, 0);
\r
496 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(ret), 0);
\r
501 if (! ok) *buffer = default_value;
\r
504 HKEY open_service_registry(const TCHAR *service_name, REGSAM sam, bool must_exist) {
\r
506 TCHAR registry[KEY_LENGTH];
\r
507 if (service_registry_path(service_name, false, 0, registry, _countof(registry)) < 0) {
\r
508 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REGISTRY, _T("open_service_registry()"), 0);
\r
512 return open_registry_key(registry, sam, must_exist);
\r
515 long open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam, HKEY *key, bool must_exist) {
\r
517 TCHAR registry[KEY_LENGTH];
\r
518 if (service_registry_path(service_name, true, sub, registry, _countof(registry)) < 0) {
\r
519 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REGISTRY, _T("open_registry()"), 0);
\r
523 return open_registry_key(registry, sam, key, must_exist);
\r
526 HKEY open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam, bool must_exist) {
\r
528 long error = open_registry(service_name, sub, sam, &key, must_exist);
\r
532 HKEY open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam) {
\r
533 return open_registry(service_name, sub, sam, true);
\r
536 HKEY open_registry(const TCHAR *service_name, REGSAM sam) {
\r
537 return open_registry(service_name, 0, sam, true);
\r
540 int get_io_parameters(nssm_service_t *service, HKEY key) {
\r
542 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
543 service->stdin_sharing = service->stdin_disposition = service->stdin_flags = 0;
\r
544 ZeroMemory(service->stdin_path, _countof(service->stdin_path) * sizeof(TCHAR));
\r
549 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
550 service->stdout_sharing = service->stdout_disposition = service->stdout_flags = 0;
\r
551 ZeroMemory(service->stdout_path, _countof(service->stdout_path) * sizeof(TCHAR));
\r
556 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
557 service->stderr_sharing = service->stderr_disposition = service->stderr_flags = 0;
\r
558 ZeroMemory(service->stderr_path, _countof(service->stderr_path) * sizeof(TCHAR));
\r
565 int get_parameters(nssm_service_t *service, STARTUPINFO *si) {
\r
568 /* Try to open the registry */
\r
569 HKEY key = open_registry(service->name, KEY_READ);
\r
570 if (! key) return 1;
\r
572 /* Don't expand parameters when retrieving for the GUI. */
\r
573 bool expand = si ? true : false;
\r
575 /* Try to get environment variables - may fail */
\r
576 get_environment(service->name, key, NSSM_REG_ENV, &service->env, &service->envlen);
\r
577 /* Environment variables to add to existing rather than replace - may fail. */
\r
578 get_environment(service->name, key, NSSM_REG_ENV_EXTRA, &service->env_extra, &service->env_extralen);
\r
580 /* Set environment if we are starting the service. */
\r
581 if (si) set_service_environment(service);
\r
583 /* Try to get executable file - MUST succeed */
\r
584 if (get_string(key, NSSM_REG_EXE, service->exe, sizeof(service->exe), expand, false, true)) {
\r
589 /* Try to get flags - may fail and we don't care */
\r
590 if (get_string(key, NSSM_REG_FLAGS, service->flags, sizeof(service->flags), expand, false, true)) {
\r
591 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_FLAGS, NSSM_REG_FLAGS, service->name, service->exe, 0);
\r
592 ZeroMemory(service->flags, sizeof(service->flags));
\r
595 /* Try to get startup directory - may fail and we fall back to a default */
\r
596 if (get_string(key, NSSM_REG_DIR, service->dir, sizeof(service->dir), expand, true, true) || ! service->dir[0]) {
\r
597 _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);
\r
598 strip_basename(service->dir);
\r
599 if (service->dir[0] == _T('\0')) {
\r
601 ret = GetWindowsDirectory(service->dir, sizeof(service->dir));
\r
602 if (! ret || ret > sizeof(service->dir)) {
\r
603 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_NO_DIR_AND_NO_FALLBACK, NSSM_REG_DIR, service->name, 0);
\r
608 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_DIR, NSSM_REG_DIR, service->name, service->dir, 0);
\r
611 /* Try to get processor affinity - may fail. */
\r
613 if (get_string(key, NSSM_REG_AFFINITY, buffer, sizeof(buffer), false, false, false) || ! buffer[0]) service->affinity = 0LL;
\r
614 else if (affinity_string_to_mask(buffer, &service->affinity)) {
\r
615 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_AFFINITY_MASK, service->name, buffer);
\r
616 service->affinity = 0LL;
\r
619 DWORD_PTR affinity, system_affinity;
\r
621 if (GetProcessAffinityMask(GetCurrentProcess(), &affinity, &system_affinity)) {
\r
622 _int64 effective_affinity = service->affinity & system_affinity;
\r
623 if (effective_affinity != service->affinity) {
\r
625 if (! affinity_mask_to_string(system_affinity, &system)) {
\r
626 TCHAR *effective = 0;
\r
627 if (! affinity_mask_to_string(effective_affinity, &effective)) {
\r
628 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_EFFECTIVE_AFFINITY_MASK, service->name, buffer, system, effective, 0);
\r
630 HeapFree(GetProcessHeap(), 0, effective);
\r
632 HeapFree(GetProcessHeap(), 0, system);
\r
637 /* Try to get priority - may fail. */
\r
638 unsigned long priority;
\r
639 if (get_number(key, NSSM_REG_PRIORITY, &priority, false) == 1) {
\r
640 if (priority == (priority & priority_mask())) service->priority = priority;
\r
641 else log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_PRIORITY, service->name, NSSM_REG_PRIORITY, 0);
\r
644 /* Try to get hook I/O sharing - may fail. */
\r
645 unsigned long hook_share_output_handles;
\r
646 if (get_number(key, NSSM_REG_HOOK_SHARE_OUTPUT_HANDLES, &hook_share_output_handles, false) == 1) {
\r
647 if (hook_share_output_handles) service->hook_share_output_handles = true;
\r
648 else service->hook_share_output_handles = false;
\r
650 else hook_share_output_handles = false;
\r
651 /* Try to get file rotation settings - may fail. */
\r
652 unsigned long rotate_files;
\r
653 if (get_number(key, NSSM_REG_ROTATE, &rotate_files, false) == 1) {
\r
654 if (rotate_files) service->rotate_files = true;
\r
655 else service->rotate_files = false;
\r
657 else service->rotate_files = false;
\r
658 if (get_number(key, NSSM_REG_ROTATE_ONLINE, &rotate_files, false) == 1) {
\r
659 if (rotate_files) service->rotate_stdout_online = service->rotate_stderr_online = true;
\r
660 else service->rotate_stdout_online = service->rotate_stderr_online = false;
\r
662 else service->rotate_stdout_online = service->rotate_stderr_online = false;
\r
663 /* Hook I/O sharing and online rotation need a pipe. */
\r
664 service->use_stdout_pipe = service->rotate_stdout_online || hook_share_output_handles;
\r
665 service->use_stderr_pipe = service->rotate_stderr_online || hook_share_output_handles;
\r
666 if (get_number(key, NSSM_REG_ROTATE_SECONDS, &service->rotate_seconds, false) != 1) service->rotate_seconds = 0;
\r
667 if (get_number(key, NSSM_REG_ROTATE_BYTES_LOW, &service->rotate_bytes_low, false) != 1) service->rotate_bytes_low = 0;
\r
668 if (get_number(key, NSSM_REG_ROTATE_BYTES_HIGH, &service->rotate_bytes_high, false) != 1) service->rotate_bytes_high = 0;
\r
669 override_milliseconds(service->name, key, NSSM_REG_ROTATE_DELAY, &service->rotate_delay, NSSM_ROTATE_DELAY, NSSM_EVENT_BOGUS_THROTTLE);
\r
671 /* Try to get force new console setting - may fail. */
\r
672 if (get_number(key, NSSM_REG_NO_CONSOLE, &service->no_console, false) != 1) service->no_console = 0;
\r
674 /* Change to startup directory in case stdout/stderr are relative paths. */
\r
675 TCHAR cwd[PATH_LENGTH];
\r
676 GetCurrentDirectory(_countof(cwd), cwd);
\r
677 SetCurrentDirectory(service->dir);
\r
679 /* Try to get stdout and stderr */
\r
680 if (get_io_parameters(service, key)) {
\r
681 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);
\r
683 SetCurrentDirectory(cwd);
\r
687 /* Change back in case the startup directory needs to be deleted. */
\r
688 SetCurrentDirectory(cwd);
\r
690 /* Try to get mandatory restart delay */
\r
691 override_milliseconds(service->name, key, NSSM_REG_RESTART_DELAY, &service->restart_delay, 0, NSSM_EVENT_BOGUS_RESTART_DELAY);
\r
693 /* Try to get throttle restart delay */
\r
694 override_milliseconds(service->name, key, NSSM_REG_THROTTLE, &service->throttle_delay, NSSM_RESET_THROTTLE_RESTART, NSSM_EVENT_BOGUS_THROTTLE);
\r
696 /* Try to get service stop flags. */
\r
697 unsigned long type = REG_DWORD;
\r
698 unsigned long stop_method_skip;
\r
699 unsigned long buflen = sizeof(stop_method_skip);
\r
700 bool stop_ok = false;
\r
701 ret = RegQueryValueEx(key, NSSM_REG_STOP_METHOD_SKIP, 0, &type, (unsigned char *) &stop_method_skip, &buflen);
\r
702 if (ret != ERROR_SUCCESS) {
\r
703 if (ret != ERROR_FILE_NOT_FOUND) {
\r
704 if (type != REG_DWORD) {
\r
705 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_STOP_METHOD_SKIP, service->name, NSSM_REG_STOP_METHOD_SKIP, NSSM, 0);
\r
707 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, NSSM_REG_STOP_METHOD_SKIP, error_string(ret), 0);
\r
710 else stop_ok = true;
\r
712 /* Try all methods except those requested to be skipped. */
\r
713 service->stop_method = ~0;
\r
714 if (stop_ok) service->stop_method &= ~stop_method_skip;
\r
716 /* Try to get kill delays - may fail. */
\r
717 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
718 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
719 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
721 /* Try to get process tree settings - may fail. */
\r
722 unsigned long kill_process_tree;
\r
723 if (get_number(key, NSSM_REG_KILL_PROCESS_TREE, &kill_process_tree, false) == 1) {
\r
724 if (kill_process_tree) service->kill_process_tree = true;
\r
725 else service->kill_process_tree = false;
\r
727 else service->kill_process_tree = true;
\r
729 /* Try to get default exit action. */
\r
730 bool default_action;
\r
731 service->default_exit_action = NSSM_EXIT_RESTART;
\r
732 TCHAR action_string[ACTION_LEN];
\r
733 if (! get_exit_action(service->name, 0, action_string, &default_action)) {
\r
734 for (int i = 0; exit_action_strings[i]; i++) {
\r
735 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
736 service->default_exit_action = i;
\r
742 /* Close registry */
\r
749 Sets the string for the exit action corresponding to the exit code.
\r
751 ret is a pointer to an unsigned long containing the exit code.
\r
752 If ret is NULL, we retrieve the default exit action unconditionally.
\r
754 action is a buffer which receives the string.
\r
756 default_action is a pointer to a bool which is set to false if there
\r
757 was an explicit string for the given exit code, or true if we are
\r
758 returning the default action.
\r
760 Returns: 0 on success.
\r
763 int get_exit_action(const TCHAR *service_name, unsigned long *ret, TCHAR *action, bool *default_action) {
\r
764 /* Are we returning the default action or a status-specific one? */
\r
765 *default_action = ! ret;
\r
767 /* Try to open the registry */
\r
768 HKEY key = open_registry(service_name, NSSM_REG_EXIT, KEY_READ);
\r
769 if (! key) return 1;
\r
771 unsigned long type = REG_SZ;
\r
772 unsigned long action_len = ACTION_LEN;
\r
775 if (! ret) code[0] = _T('\0');
\r
776 else if (_sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), *ret) < 0) {
\r
778 return get_exit_action(service_name, 0, action, default_action);
\r
780 if (RegQueryValueEx(key, code, 0, &type, (unsigned char *) action, &action_len) != ERROR_SUCCESS) {
\r
782 /* Try again with * as the key if an exit code was defined */
\r
783 if (ret) return get_exit_action(service_name, 0, action, default_action);
\r
787 /* Close registry */
\r
793 int set_hook(const TCHAR *service_name, const TCHAR *hook_event, const TCHAR *hook_action, TCHAR *cmd) {
\r
794 /* Try to open the registry */
\r
795 TCHAR registry[KEY_LENGTH];
\r
796 if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, _T("%s\\%s"), NSSM_REG_HOOK, hook_event) < 0) {
\r
797 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("hook registry"), _T("set_hook()"), 0);
\r
804 /* Don't create keys needlessly. */
\r
805 if (! _tcslen(cmd)) {
\r
806 key = open_registry(service_name, registry, KEY_READ, false);
\r
807 if (! key) return 0;
\r
808 error = RegQueryValueEx(key, hook_action, 0, 0, 0, 0);
\r
810 if (error == ERROR_FILE_NOT_FOUND) return 0;
\r
813 key = open_registry(service_name, registry, KEY_WRITE);
\r
814 if (! key) return 1;
\r
817 if (_tcslen(cmd)) ret = set_string(key, (TCHAR *) hook_action, cmd, true);
\r
819 error = RegDeleteValue(key, hook_action);
\r
820 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) ret = 0;
\r
823 /* Close registry */
\r
829 int get_hook(const TCHAR *service_name, const TCHAR *hook_event, const TCHAR *hook_action, TCHAR *buffer, unsigned long buflen) {
\r
830 /* Try to open the registry */
\r
831 TCHAR registry[KEY_LENGTH];
\r
832 if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, _T("%s\\%s"), NSSM_REG_HOOK, hook_event) < 0) {
\r
833 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("hook registry"), _T("get_hook()"), 0);
\r
837 long error = open_registry(service_name, registry, KEY_READ, &key, false);
\r
839 if (error == ERROR_FILE_NOT_FOUND) {
\r
840 ZeroMemory(buffer, buflen);
\r
846 int ret = expand_parameter(key, (TCHAR *) hook_action, buffer, buflen, true, false);
\r
848 /* Close registry */
\r