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 long enumerate_registry_values(HKEY key, unsigned long *index, TCHAR *name, unsigned long namelen) {
\r
72 unsigned long datalen = namelen;
\r
73 long error = RegEnumValue(key, *index, name, &datalen, 0, &type, 0, 0);
\r
74 if (error == ERROR_SUCCESS) ++*index;
\r
78 int create_parameters(nssm_service_t *service, bool editing) {
\r
79 /* Try to open the registry */
\r
80 HKEY key = open_registry(service->name, KEY_WRITE);
\r
81 if (! key) return 1;
\r
83 /* Remember parameters in case we need to delete them. */
\r
84 TCHAR registry[KEY_LENGTH];
\r
85 int ret = service_registry_path(service->name, true, 0, registry, _countof(registry));
\r
87 /* Try to create the parameters */
\r
88 if (set_expand_string(key, NSSM_REG_EXE, service->exe)) {
\r
89 if (ret > 0) RegDeleteKey(HKEY_LOCAL_MACHINE, registry);
\r
93 if (set_expand_string(key, NSSM_REG_FLAGS, service->flags)) {
\r
94 if (ret > 0) RegDeleteKey(HKEY_LOCAL_MACHINE, registry);
\r
98 if (set_expand_string(key, NSSM_REG_DIR, service->dir)) {
\r
99 if (ret > 0) RegDeleteKey(HKEY_LOCAL_MACHINE, registry);
\r
104 /* Other non-default parameters. May fail. */
\r
105 if (service->priority != NORMAL_PRIORITY_CLASS) set_number(key, NSSM_REG_PRIORITY, service->priority);
\r
106 else if (editing) RegDeleteValue(key, NSSM_REG_PRIORITY);
\r
107 if (service->affinity) {
\r
109 if (! affinity_mask_to_string(service->affinity, &string)) {
\r
110 if (RegSetValueEx(key, NSSM_REG_AFFINITY, 0, REG_SZ, (const unsigned char *) string, (unsigned long) (_tcslen(string) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
111 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_AFFINITY, error_string(GetLastError()), 0);
\r
112 HeapFree(GetProcessHeap(), 0, string);
\r
116 if (string) HeapFree(GetProcessHeap(), 0, string);
\r
118 else if (editing) RegDeleteValue(key, NSSM_REG_AFFINITY);
\r
119 unsigned long stop_method_skip = ~service->stop_method;
\r
120 if (stop_method_skip) set_number(key, NSSM_REG_STOP_METHOD_SKIP, stop_method_skip);
\r
121 else if (editing) RegDeleteValue(key, NSSM_REG_STOP_METHOD_SKIP);
\r
122 if (service->default_exit_action < NSSM_NUM_EXIT_ACTIONS) create_exit_action(service->name, exit_action_strings[service->default_exit_action], editing);
\r
123 if (service->restart_delay) set_number(key, NSSM_REG_RESTART_DELAY, service->restart_delay);
\r
124 else if (editing) RegDeleteValue(key, NSSM_REG_RESTART_DELAY);
\r
125 if (service->throttle_delay != NSSM_RESET_THROTTLE_RESTART) set_number(key, NSSM_REG_THROTTLE, service->throttle_delay);
\r
126 else if (editing) RegDeleteValue(key, NSSM_REG_THROTTLE);
\r
127 if (service->kill_console_delay != NSSM_KILL_CONSOLE_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, service->kill_console_delay);
\r
128 else if (editing) RegDeleteValue(key, NSSM_REG_KILL_CONSOLE_GRACE_PERIOD);
\r
129 if (service->kill_window_delay != NSSM_KILL_WINDOW_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_WINDOW_GRACE_PERIOD, service->kill_window_delay);
\r
130 else if (editing) RegDeleteValue(key, NSSM_REG_KILL_WINDOW_GRACE_PERIOD);
\r
131 if (service->kill_threads_delay != NSSM_KILL_THREADS_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_THREADS_GRACE_PERIOD, service->kill_threads_delay);
\r
132 else if (editing) RegDeleteValue(key, NSSM_REG_KILL_THREADS_GRACE_PERIOD);
\r
133 if (! service->kill_process_tree) set_number(key, NSSM_REG_KILL_PROCESS_TREE, 0);
\r
134 else if (editing) RegDeleteValue(key, NSSM_REG_KILL_PROCESS_TREE);
\r
135 if (service->stdin_path[0] || editing) {
\r
136 if (service->stdin_path[0]) set_expand_string(key, NSSM_REG_STDIN, service->stdin_path);
\r
137 else if (editing) RegDeleteValue(key, NSSM_REG_STDIN);
\r
138 if (service->stdin_sharing != NSSM_STDIN_SHARING) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_SHARING, service->stdin_sharing);
\r
139 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_SHARING);
\r
140 if (service->stdin_disposition != NSSM_STDIN_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_DISPOSITION, service->stdin_disposition);
\r
141 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_DISPOSITION);
\r
142 if (service->stdin_flags != NSSM_STDIN_FLAGS) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_FLAGS, service->stdin_flags);
\r
143 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_FLAGS);
\r
145 if (service->stdout_path[0] || editing) {
\r
146 if (service->stdout_path[0]) set_expand_string(key, NSSM_REG_STDOUT, service->stdout_path);
\r
147 else if (editing) RegDeleteValue(key, NSSM_REG_STDOUT);
\r
148 if (service->stdout_sharing != NSSM_STDOUT_SHARING) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_SHARING, service->stdout_sharing);
\r
149 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_SHARING);
\r
150 if (service->stdout_disposition != NSSM_STDOUT_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_DISPOSITION, service->stdout_disposition);
\r
151 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_DISPOSITION);
\r
152 if (service->stdout_flags != NSSM_STDOUT_FLAGS) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_FLAGS, service->stdout_flags);
\r
153 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_FLAGS);
\r
154 if (service->stdout_copy_and_truncate) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_COPY_AND_TRUNCATE, 1);
\r
155 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_COPY_AND_TRUNCATE);
\r
157 if (service->stderr_path[0] || editing) {
\r
158 if (service->stderr_path[0]) set_expand_string(key, NSSM_REG_STDERR, service->stderr_path);
\r
159 else if (editing) RegDeleteValue(key, NSSM_REG_STDERR);
\r
160 if (service->stderr_sharing != NSSM_STDERR_SHARING) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_SHARING, service->stderr_sharing);
\r
161 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_SHARING);
\r
162 if (service->stderr_disposition != NSSM_STDERR_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_DISPOSITION, service->stderr_disposition);
\r
163 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_DISPOSITION);
\r
164 if (service->stderr_flags != NSSM_STDERR_FLAGS) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_FLAGS, service->stderr_flags);
\r
165 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_FLAGS);
\r
166 if (service->stderr_copy_and_truncate) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_COPY_AND_TRUNCATE, 1);
\r
167 else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_COPY_AND_TRUNCATE);
\r
169 if (service->hook_share_output_handles) set_number(key, NSSM_REG_HOOK_SHARE_OUTPUT_HANDLES, 1);
\r
170 else if (editing) RegDeleteValue(key, NSSM_REG_HOOK_SHARE_OUTPUT_HANDLES);
\r
171 if (service->rotate_files) set_number(key, NSSM_REG_ROTATE, 1);
\r
172 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE);
\r
173 if (service->rotate_stdout_online) set_number(key, NSSM_REG_ROTATE_ONLINE, 1);
\r
174 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_ONLINE);
\r
175 if (service->rotate_seconds) set_number(key, NSSM_REG_ROTATE_SECONDS, service->rotate_seconds);
\r
176 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_SECONDS);
\r
177 if (service->rotate_bytes_low) set_number(key, NSSM_REG_ROTATE_BYTES_LOW, service->rotate_bytes_low);
\r
178 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_BYTES_LOW);
\r
179 if (service->rotate_bytes_high) set_number(key, NSSM_REG_ROTATE_BYTES_HIGH, service->rotate_bytes_high);
\r
180 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_BYTES_HIGH);
\r
181 if (service->rotate_delay != NSSM_ROTATE_DELAY) set_number(key, NSSM_REG_ROTATE_DELAY, service->rotate_delay);
\r
182 else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_DELAY);
\r
183 if (service->no_console) set_number(key, NSSM_REG_NO_CONSOLE, 1);
\r
184 else if (editing) RegDeleteValue(key, NSSM_REG_NO_CONSOLE);
\r
187 if (service->env) {
\r
188 if (RegSetValueEx(key, NSSM_REG_ENV, 0, REG_MULTI_SZ, (const unsigned char *) service->env, (unsigned long) service->envlen * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
189 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV, error_string(GetLastError()), 0);
\r
192 else if (editing) RegDeleteValue(key, NSSM_REG_ENV);
\r
193 if (service->env_extra) {
\r
194 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
195 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV_EXTRA, error_string(GetLastError()), 0);
\r
198 else if (editing) RegDeleteValue(key, NSSM_REG_ENV_EXTRA);
\r
200 /* Close registry. */
\r
206 int create_exit_action(TCHAR *service_name, const TCHAR *action_string, bool editing) {
\r
208 TCHAR registry[KEY_LENGTH];
\r
209 if (service_registry_path(service_name, true, NSSM_REG_EXIT, registry, _countof(registry)) < 0) {
\r
210 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("NSSM_REG_EXIT"), _T("create_exit_action()"), 0);
\r
214 /* Try to open the registry */
\r
216 unsigned long disposition;
\r
217 if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, KEY_WRITE, 0, &key, &disposition) != ERROR_SUCCESS) {
\r
218 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
\r
222 /* Do nothing if the key already existed */
\r
223 if (disposition == REG_OPENED_EXISTING_KEY && ! editing) {
\r
228 /* Create the default value */
\r
229 if (RegSetValueEx(key, 0, 0, REG_SZ, (const unsigned char *) action_string, (unsigned long) (_tcslen(action_string) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) {
\r
230 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_EXIT, error_string(GetLastError()), 0);
\r
235 /* Close registry */
\r
241 int get_environment(TCHAR *service_name, HKEY key, TCHAR *value, TCHAR **env, unsigned long *envlen) {
\r
242 unsigned long type = REG_MULTI_SZ;
\r
243 unsigned long envsize;
\r
247 /* Dummy test to find buffer size */
\r
248 unsigned long ret = RegQueryValueEx(key, value, 0, &type, NULL, &envsize);
\r
249 if (ret != ERROR_SUCCESS) {
\r
251 /* The service probably doesn't have any environment configured */
\r
252 if (ret == ERROR_FILE_NOT_FOUND) return 0;
\r
253 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(ret), 0);
\r
257 if (type != REG_MULTI_SZ) {
\r
258 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_INVALID_ENVIRONMENT_STRING_TYPE, value, service_name, 0);
\r
263 /* Probably not possible */
\r
264 if (! envsize) return 0;
\r
266 /* Previously initialised? */
\r
267 if (*env) HeapFree(GetProcessHeap(), 0, *env);
\r
269 *env = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, envsize);
\r
271 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, value, _T("get_environment()"), 0);
\r
275 /* Actually get the strings. */
\r
276 ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) *env, &envsize);
\r
277 if (ret != ERROR_SUCCESS) {
\r
278 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(ret), 0);
\r
279 HeapFree(GetProcessHeap(), 0, *env);
\r
284 /* Value retrieved by RegQueryValueEx() is SIZE not COUNT. */
\r
285 *envlen = (unsigned long) environment_length(env);
\r
291 int get_string(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool expand, bool sanitise, bool must_exist) {
\r
292 TCHAR *buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, datalen);
\r
294 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, value, _T("get_string()"), 0);
\r
298 ZeroMemory(data, datalen);
\r
300 unsigned long type = REG_EXPAND_SZ;
\r
301 unsigned long buflen = datalen;
\r
303 unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);
\r
304 if (ret != ERROR_SUCCESS) {
\r
305 HeapFree(GetProcessHeap(), 0, buffer);
\r
307 if (ret == ERROR_FILE_NOT_FOUND) {
\r
308 if (! must_exist) return 0;
\r
311 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(ret), 0);
\r
315 /* Paths aren't allowed to contain quotes. */
\r
316 if (sanitise) PathUnquoteSpaces(buffer);
\r
318 /* Do we want to expand the string? */
\r
320 if (type == REG_EXPAND_SZ) type = REG_SZ;
\r
323 /* Technically we shouldn't expand environment strings from REG_SZ values */
\r
324 if (type != REG_EXPAND_SZ) {
\r
325 memmove(data, buffer, buflen);
\r
326 HeapFree(GetProcessHeap(), 0, buffer);
\r
330 ret = ExpandEnvironmentStrings((TCHAR *) buffer, data, datalen);
\r
331 if (! ret || ret > datalen) {
\r
332 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_EXPANDENVIRONMENTSTRINGS_FAILED, buffer, error_string(GetLastError()), 0);
\r
333 HeapFree(GetProcessHeap(), 0, buffer);
\r
337 HeapFree(GetProcessHeap(), 0, buffer);
\r
341 int get_string(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise) {
\r
342 return get_string(key, value, data, datalen, false, sanitise, true);
\r
345 int expand_parameter(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise, bool must_exist) {
\r
346 return get_string(key, value, data, datalen, true, sanitise, must_exist);
\r
349 int expand_parameter(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise) {
\r
350 return expand_parameter(key, value, data, datalen, sanitise, true);
\r
354 Sets a string in the registry.
\r
355 Returns: 0 if it was set.
\r
358 int set_string(HKEY key, TCHAR *value, TCHAR *string, bool expand) {
\r
359 unsigned long type = expand ? REG_EXPAND_SZ : REG_SZ;
\r
360 if (RegSetValueEx(key, value, 0, type, (const unsigned char *) string, (unsigned long) (_tcslen(string) + 1) * sizeof(TCHAR)) == ERROR_SUCCESS) return 0;
\r
361 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
365 int set_string(HKEY key, TCHAR *value, TCHAR *string) {
\r
366 return set_string(key, value, string, false);
\r
369 int set_expand_string(HKEY key, TCHAR *value, TCHAR *string) {
\r
370 return set_string(key, value, string, true);
\r
374 Set an unsigned long in the registry.
\r
375 Returns: 0 if it was set.
\r
378 int set_number(HKEY key, TCHAR *value, unsigned long number) {
\r
379 if (RegSetValueEx(key, value, 0, REG_DWORD, (const unsigned char *) &number, sizeof(number)) == ERROR_SUCCESS) return 0;
\r
380 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, value, error_string(GetLastError()), 0);
\r
385 Query an unsigned long from the registry.
\r
386 Returns: 1 if a number was retrieved.
\r
387 0 if none was found and must_exist is false.
\r
388 -1 if none was found and must_exist is true.
\r
391 int get_number(HKEY key, TCHAR *value, unsigned long *number, bool must_exist) {
\r
392 unsigned long type = REG_DWORD;
\r
393 unsigned long number_len = sizeof(unsigned long);
\r
395 int ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) number, &number_len);
\r
396 if (ret == ERROR_SUCCESS) return 1;
\r
398 if (ret == ERROR_FILE_NOT_FOUND) {
\r
399 if (! must_exist) return 0;
\r
402 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(ret), 0);
\r
403 if (ret == ERROR_FILE_NOT_FOUND) return -1;
\r
408 int get_number(HKEY key, TCHAR *value, unsigned long *number) {
\r
409 return get_number(key, value, number, true);
\r
412 /* Replace NULL with CRLF. Leave NULL NULL as the end marker. */
\r
413 int format_double_null(TCHAR *dn, unsigned long dnlen, TCHAR **formatted, unsigned long *newlen) {
\r
414 unsigned long i, j;
\r
422 for (i = 0; i < dnlen; i++) if (! dn[i] && dn[i + 1]) ++*newlen;
\r
424 *formatted = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *newlen * sizeof(TCHAR));
\r
425 if (! *formatted) {
\r
430 for (i = 0, j = 0; i < dnlen; i++) {
\r
431 (*formatted)[j] = dn[i];
\r
434 (*formatted)[j] = _T('\r');
\r
435 (*formatted)[++j] = _T('\n');
\r
444 /* Strip CR and replace LF with NULL. */
\r
445 int unformat_double_null(TCHAR *dn, unsigned long dnlen, TCHAR **unformatted, unsigned long *newlen) {
\r
446 unsigned long i, j;
\r
454 for (i = 0; i < dnlen; i++) if (dn[i] != _T('\r')) ++*newlen;
\r
456 /* Skip blank lines. */
\r
457 for (i = 0; i < dnlen; i++) {
\r
458 if (dn[i] == _T('\r') && dn[i + 1] == _T('\n')) {
\r
459 /* This is the last CRLF. */
\r
460 if (i >= dnlen - 2) break;
\r
463 Strip at the start of the block or if the next characters are
\r
466 if (! i || (dn[i + 2] == _T('\r') && dn[i + 3] == _T('\n'))) {
\r
467 for (j = i + 2; j < dnlen; j++) dn[j - 2] = dn[j];
\r
468 dn[dnlen--] = _T('\0');
\r
469 dn[dnlen--] = _T('\0');
\r
476 /* Must end with two NULLs. */
\r
479 *unformatted = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *newlen * sizeof(TCHAR));
\r
480 if (! *unformatted) return 1;
\r
482 for (i = 0, j = 0; i < dnlen; i++) {
\r
483 if (dn[i] == _T('\r')) continue;
\r
484 if (dn[i] == _T('\n')) (*unformatted)[j] = _T('\0');
\r
485 else (*unformatted)[j] = dn[i];
\r
492 void override_milliseconds(TCHAR *service_name, HKEY key, TCHAR *value, unsigned long *buffer, unsigned long default_value, unsigned long event) {
\r
493 unsigned long type = REG_DWORD;
\r
494 unsigned long buflen = sizeof(unsigned long);
\r
496 unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);
\r
497 if (ret != ERROR_SUCCESS) {
\r
498 if (ret != ERROR_FILE_NOT_FOUND) {
\r
499 if (type != REG_DWORD) {
\r
500 TCHAR milliseconds[16];
\r
501 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), default_value);
\r
502 log_event(EVENTLOG_WARNING_TYPE, event, service_name, value, milliseconds, 0);
\r
504 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(ret), 0);
\r
509 if (! ok) *buffer = default_value;
\r
512 HKEY open_service_registry(const TCHAR *service_name, REGSAM sam, bool must_exist) {
\r
514 TCHAR registry[KEY_LENGTH];
\r
515 if (service_registry_path(service_name, false, 0, registry, _countof(registry)) < 0) {
\r
516 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REGISTRY, _T("open_service_registry()"), 0);
\r
520 return open_registry_key(registry, sam, must_exist);
\r
523 long open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam, HKEY *key, bool must_exist) {
\r
525 TCHAR registry[KEY_LENGTH];
\r
526 if (service_registry_path(service_name, true, sub, registry, _countof(registry)) < 0) {
\r
527 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REGISTRY, _T("open_registry()"), 0);
\r
531 return open_registry_key(registry, sam, key, must_exist);
\r
534 HKEY open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam, bool must_exist) {
\r
536 long error = open_registry(service_name, sub, sam, &key, must_exist);
\r
540 HKEY open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam) {
\r
541 return open_registry(service_name, sub, sam, true);
\r
544 HKEY open_registry(const TCHAR *service_name, REGSAM sam) {
\r
545 return open_registry(service_name, 0, sam, true);
\r
548 int get_io_parameters(nssm_service_t *service, HKEY key) {
\r
550 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
551 service->stdin_sharing = service->stdin_disposition = service->stdin_flags = 0;
\r
552 ZeroMemory(service->stdin_path, _countof(service->stdin_path) * sizeof(TCHAR));
\r
557 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
558 service->stdout_sharing = service->stdout_disposition = service->stdout_flags = 0;
\r
559 ZeroMemory(service->stdout_path, _countof(service->stdout_path) * sizeof(TCHAR));
\r
564 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
565 service->stderr_sharing = service->stderr_disposition = service->stderr_flags = 0;
\r
566 ZeroMemory(service->stderr_path, _countof(service->stderr_path) * sizeof(TCHAR));
\r
573 int get_parameters(nssm_service_t *service, STARTUPINFO *si) {
\r
576 /* Try to open the registry */
\r
577 HKEY key = open_registry(service->name, KEY_READ);
\r
578 if (! key) return 1;
\r
580 /* Don't expand parameters when retrieving for the GUI. */
\r
581 bool expand = si ? true : false;
\r
583 /* Try to get environment variables - may fail */
\r
584 get_environment(service->name, key, NSSM_REG_ENV, &service->env, &service->envlen);
\r
585 /* Environment variables to add to existing rather than replace - may fail. */
\r
586 get_environment(service->name, key, NSSM_REG_ENV_EXTRA, &service->env_extra, &service->env_extralen);
\r
588 /* Set environment if we are starting the service. */
\r
589 if (si) set_service_environment(service);
\r
591 /* Try to get executable file - MUST succeed */
\r
592 if (get_string(key, NSSM_REG_EXE, service->exe, sizeof(service->exe), expand, false, true)) {
\r
597 /* Try to get flags - may fail and we don't care */
\r
598 if (get_string(key, NSSM_REG_FLAGS, service->flags, sizeof(service->flags), expand, false, true)) {
\r
599 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_FLAGS, NSSM_REG_FLAGS, service->name, service->exe, 0);
\r
600 ZeroMemory(service->flags, sizeof(service->flags));
\r
603 /* Try to get startup directory - may fail and we fall back to a default */
\r
604 if (get_string(key, NSSM_REG_DIR, service->dir, sizeof(service->dir), expand, true, true) || ! service->dir[0]) {
\r
605 _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);
\r
606 strip_basename(service->dir);
\r
607 if (service->dir[0] == _T('\0')) {
\r
609 ret = GetWindowsDirectory(service->dir, sizeof(service->dir));
\r
610 if (! ret || ret > sizeof(service->dir)) {
\r
611 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_NO_DIR_AND_NO_FALLBACK, NSSM_REG_DIR, service->name, 0);
\r
616 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_DIR, NSSM_REG_DIR, service->name, service->dir, 0);
\r
619 /* Try to get processor affinity - may fail. */
\r
621 if (get_string(key, NSSM_REG_AFFINITY, buffer, sizeof(buffer), false, false, false) || ! buffer[0]) service->affinity = 0LL;
\r
622 else if (affinity_string_to_mask(buffer, &service->affinity)) {
\r
623 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_AFFINITY_MASK, service->name, buffer);
\r
624 service->affinity = 0LL;
\r
627 DWORD_PTR affinity, system_affinity;
\r
629 if (GetProcessAffinityMask(GetCurrentProcess(), &affinity, &system_affinity)) {
\r
630 _int64 effective_affinity = service->affinity & system_affinity;
\r
631 if (effective_affinity != service->affinity) {
\r
633 if (! affinity_mask_to_string(system_affinity, &system)) {
\r
634 TCHAR *effective = 0;
\r
635 if (! affinity_mask_to_string(effective_affinity, &effective)) {
\r
636 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_EFFECTIVE_AFFINITY_MASK, service->name, buffer, system, effective, 0);
\r
638 HeapFree(GetProcessHeap(), 0, effective);
\r
640 HeapFree(GetProcessHeap(), 0, system);
\r
645 /* Try to get priority - may fail. */
\r
646 unsigned long priority;
\r
647 if (get_number(key, NSSM_REG_PRIORITY, &priority, false) == 1) {
\r
648 if (priority == (priority & priority_mask())) service->priority = priority;
\r
649 else log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_PRIORITY, service->name, NSSM_REG_PRIORITY, 0);
\r
652 /* Try to get hook I/O sharing - may fail. */
\r
653 unsigned long hook_share_output_handles;
\r
654 if (get_number(key, NSSM_REG_HOOK_SHARE_OUTPUT_HANDLES, &hook_share_output_handles, false) == 1) {
\r
655 if (hook_share_output_handles) service->hook_share_output_handles = true;
\r
656 else service->hook_share_output_handles = false;
\r
658 else hook_share_output_handles = false;
\r
659 /* Try to get file rotation settings - may fail. */
\r
660 unsigned long rotate_files;
\r
661 if (get_number(key, NSSM_REG_ROTATE, &rotate_files, false) == 1) {
\r
662 if (rotate_files) service->rotate_files = true;
\r
663 else service->rotate_files = false;
\r
665 else service->rotate_files = false;
\r
666 if (get_number(key, NSSM_REG_ROTATE_ONLINE, &rotate_files, false) == 1) {
\r
667 if (rotate_files) service->rotate_stdout_online = service->rotate_stderr_online = true;
\r
668 else service->rotate_stdout_online = service->rotate_stderr_online = false;
\r
670 else service->rotate_stdout_online = service->rotate_stderr_online = false;
\r
671 /* Hook I/O sharing and online rotation need a pipe. */
\r
672 service->use_stdout_pipe = service->rotate_stdout_online || hook_share_output_handles;
\r
673 service->use_stderr_pipe = service->rotate_stderr_online || hook_share_output_handles;
\r
674 if (get_number(key, NSSM_REG_ROTATE_SECONDS, &service->rotate_seconds, false) != 1) service->rotate_seconds = 0;
\r
675 if (get_number(key, NSSM_REG_ROTATE_BYTES_LOW, &service->rotate_bytes_low, false) != 1) service->rotate_bytes_low = 0;
\r
676 if (get_number(key, NSSM_REG_ROTATE_BYTES_HIGH, &service->rotate_bytes_high, false) != 1) service->rotate_bytes_high = 0;
\r
677 override_milliseconds(service->name, key, NSSM_REG_ROTATE_DELAY, &service->rotate_delay, NSSM_ROTATE_DELAY, NSSM_EVENT_BOGUS_THROTTLE);
\r
679 /* Try to get force new console setting - may fail. */
\r
680 if (get_number(key, NSSM_REG_NO_CONSOLE, &service->no_console, false) != 1) service->no_console = 0;
\r
682 /* Change to startup directory in case stdout/stderr are relative paths. */
\r
683 TCHAR cwd[PATH_LENGTH];
\r
684 GetCurrentDirectory(_countof(cwd), cwd);
\r
685 SetCurrentDirectory(service->dir);
\r
687 /* Try to get stdout and stderr */
\r
688 if (get_io_parameters(service, key)) {
\r
689 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);
\r
691 SetCurrentDirectory(cwd);
\r
695 /* Change back in case the startup directory needs to be deleted. */
\r
696 SetCurrentDirectory(cwd);
\r
698 /* Try to get mandatory restart delay */
\r
699 override_milliseconds(service->name, key, NSSM_REG_RESTART_DELAY, &service->restart_delay, 0, NSSM_EVENT_BOGUS_RESTART_DELAY);
\r
701 /* Try to get throttle restart delay */
\r
702 override_milliseconds(service->name, key, NSSM_REG_THROTTLE, &service->throttle_delay, NSSM_RESET_THROTTLE_RESTART, NSSM_EVENT_BOGUS_THROTTLE);
\r
704 /* Try to get service stop flags. */
\r
705 unsigned long type = REG_DWORD;
\r
706 unsigned long stop_method_skip;
\r
707 unsigned long buflen = sizeof(stop_method_skip);
\r
708 bool stop_ok = false;
\r
709 ret = RegQueryValueEx(key, NSSM_REG_STOP_METHOD_SKIP, 0, &type, (unsigned char *) &stop_method_skip, &buflen);
\r
710 if (ret != ERROR_SUCCESS) {
\r
711 if (ret != ERROR_FILE_NOT_FOUND) {
\r
712 if (type != REG_DWORD) {
\r
713 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_STOP_METHOD_SKIP, service->name, NSSM_REG_STOP_METHOD_SKIP, NSSM, 0);
\r
715 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, NSSM_REG_STOP_METHOD_SKIP, error_string(ret), 0);
\r
718 else stop_ok = true;
\r
720 /* Try all methods except those requested to be skipped. */
\r
721 service->stop_method = ~0;
\r
722 if (stop_ok) service->stop_method &= ~stop_method_skip;
\r
724 /* Try to get kill delays - may fail. */
\r
725 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
726 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
727 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
729 /* Try to get process tree settings - may fail. */
\r
730 unsigned long kill_process_tree;
\r
731 if (get_number(key, NSSM_REG_KILL_PROCESS_TREE, &kill_process_tree, false) == 1) {
\r
732 if (kill_process_tree) service->kill_process_tree = true;
\r
733 else service->kill_process_tree = false;
\r
735 else service->kill_process_tree = true;
\r
737 /* Try to get default exit action. */
\r
738 bool default_action;
\r
739 service->default_exit_action = NSSM_EXIT_RESTART;
\r
740 TCHAR action_string[ACTION_LEN];
\r
741 if (! get_exit_action(service->name, 0, action_string, &default_action)) {
\r
742 for (int i = 0; exit_action_strings[i]; i++) {
\r
743 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
744 service->default_exit_action = i;
\r
750 /* Close registry */
\r
757 Sets the string for the exit action corresponding to the exit code.
\r
759 ret is a pointer to an unsigned long containing the exit code.
\r
760 If ret is NULL, we retrieve the default exit action unconditionally.
\r
762 action is a buffer which receives the string.
\r
764 default_action is a pointer to a bool which is set to false if there
\r
765 was an explicit string for the given exit code, or true if we are
\r
766 returning the default action.
\r
768 Returns: 0 on success.
\r
771 int get_exit_action(const TCHAR *service_name, unsigned long *ret, TCHAR *action, bool *default_action) {
\r
772 /* Are we returning the default action or a status-specific one? */
\r
773 *default_action = ! ret;
\r
775 /* Try to open the registry */
\r
776 HKEY key = open_registry(service_name, NSSM_REG_EXIT, KEY_READ);
\r
777 if (! key) return 1;
\r
779 unsigned long type = REG_SZ;
\r
780 unsigned long action_len = ACTION_LEN;
\r
783 if (! ret) code[0] = _T('\0');
\r
784 else if (_sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), *ret) < 0) {
\r
786 return get_exit_action(service_name, 0, action, default_action);
\r
788 if (RegQueryValueEx(key, code, 0, &type, (unsigned char *) action, &action_len) != ERROR_SUCCESS) {
\r
790 /* Try again with * as the key if an exit code was defined */
\r
791 if (ret) return get_exit_action(service_name, 0, action, default_action);
\r
795 /* Close registry */
\r
801 int set_hook(const TCHAR *service_name, const TCHAR *hook_event, const TCHAR *hook_action, TCHAR *cmd) {
\r
802 /* Try to open the registry */
\r
803 TCHAR registry[KEY_LENGTH];
\r
804 if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, _T("%s\\%s"), NSSM_REG_HOOK, hook_event) < 0) {
\r
805 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("hook registry"), _T("set_hook()"), 0);
\r
812 /* Don't create keys needlessly. */
\r
813 if (! _tcslen(cmd)) {
\r
814 key = open_registry(service_name, registry, KEY_READ, false);
\r
815 if (! key) return 0;
\r
816 error = RegQueryValueEx(key, hook_action, 0, 0, 0, 0);
\r
818 if (error == ERROR_FILE_NOT_FOUND) return 0;
\r
821 key = open_registry(service_name, registry, KEY_WRITE);
\r
822 if (! key) return 1;
\r
825 if (_tcslen(cmd)) ret = set_string(key, (TCHAR *) hook_action, cmd, true);
\r
827 error = RegDeleteValue(key, hook_action);
\r
828 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) ret = 0;
\r
831 /* Close registry */
\r
837 int get_hook(const TCHAR *service_name, const TCHAR *hook_event, const TCHAR *hook_action, TCHAR *buffer, unsigned long buflen) {
\r
838 /* Try to open the registry */
\r
839 TCHAR registry[KEY_LENGTH];
\r
840 if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, _T("%s\\%s"), NSSM_REG_HOOK, hook_event) < 0) {
\r
841 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("hook registry"), _T("get_hook()"), 0);
\r
845 long error = open_registry(service_name, registry, KEY_READ, &key, false);
\r
847 if (error == ERROR_FILE_NOT_FOUND) {
\r
848 ZeroMemory(buffer, buflen);
\r
854 int ret = expand_parameter(key, (TCHAR *) hook_action, buffer, buflen, true, false);
\r
856 /* Close registry */
\r