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 *formatted, unsigned long formattedlen, TCHAR **dn, unsigned long *newlen) {
\r
446 unsigned long i, j;
\r
449 /* Don't count trailing NULLs. */
\r
450 for (i = 0; i < formattedlen; i++) {
\r
451 if (! formatted[i]) {
\r
457 if (! formattedlen) {
\r
462 for (i = 0; i < formattedlen; i++) if (formatted[i] != _T('\r')) ++*newlen;
\r
464 /* Skip blank lines. */
\r
465 for (i = 0; i < formattedlen; i++) {
\r
466 if (formatted[i] == _T('\r') && formatted[i + 1] == _T('\n')) {
\r
467 /* This is the last CRLF. */
\r
468 if (i >= formattedlen - 2) break;
\r
471 Strip at the start of the block or if the next characters are
\r
474 if (! i || (formatted[i + 2] == _T('\r') && formatted[i + 3] == _T('\n'))) {
\r
475 for (j = i + 2; j < formattedlen; j++) formatted[j - 2] = formatted[j];
\r
476 formatted[formattedlen--] = _T('\0');
\r
477 formatted[formattedlen--] = _T('\0');
\r
484 /* Must end with two NULLs. */
\r
487 *dn = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *newlen * sizeof(TCHAR));
\r
488 if (! *dn) return 1;
\r
490 for (i = 0, j = 0; i < formattedlen; i++) {
\r
491 if (formatted[i] == _T('\r')) continue;
\r
492 if (formatted[i] == _T('\n')) (*dn)[j] = _T('\0');
\r
493 else (*dn)[j] = formatted[i];
\r
500 void override_milliseconds(TCHAR *service_name, HKEY key, TCHAR *value, unsigned long *buffer, unsigned long default_value, unsigned long event) {
\r
501 unsigned long type = REG_DWORD;
\r
502 unsigned long buflen = sizeof(unsigned long);
\r
504 unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);
\r
505 if (ret != ERROR_SUCCESS) {
\r
506 if (ret != ERROR_FILE_NOT_FOUND) {
\r
507 if (type != REG_DWORD) {
\r
508 TCHAR milliseconds[16];
\r
509 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), default_value);
\r
510 log_event(EVENTLOG_WARNING_TYPE, event, service_name, value, milliseconds, 0);
\r
512 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(ret), 0);
\r
517 if (! ok) *buffer = default_value;
\r
520 HKEY open_service_registry(const TCHAR *service_name, REGSAM sam, bool must_exist) {
\r
522 TCHAR registry[KEY_LENGTH];
\r
523 if (service_registry_path(service_name, false, 0, registry, _countof(registry)) < 0) {
\r
524 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REGISTRY, _T("open_service_registry()"), 0);
\r
528 return open_registry_key(registry, sam, must_exist);
\r
531 long open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam, HKEY *key, bool must_exist) {
\r
533 TCHAR registry[KEY_LENGTH];
\r
534 if (service_registry_path(service_name, true, sub, registry, _countof(registry)) < 0) {
\r
535 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REGISTRY, _T("open_registry()"), 0);
\r
539 return open_registry_key(registry, sam, key, must_exist);
\r
542 HKEY open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam, bool must_exist) {
\r
544 long error = open_registry(service_name, sub, sam, &key, must_exist);
\r
548 HKEY open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam) {
\r
549 return open_registry(service_name, sub, sam, true);
\r
552 HKEY open_registry(const TCHAR *service_name, REGSAM sam) {
\r
553 return open_registry(service_name, 0, sam, true);
\r
556 int get_io_parameters(nssm_service_t *service, HKEY key) {
\r
558 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
559 service->stdin_sharing = service->stdin_disposition = service->stdin_flags = 0;
\r
560 ZeroMemory(service->stdin_path, _countof(service->stdin_path) * sizeof(TCHAR));
\r
565 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
566 service->stdout_sharing = service->stdout_disposition = service->stdout_flags = 0;
\r
567 ZeroMemory(service->stdout_path, _countof(service->stdout_path) * sizeof(TCHAR));
\r
572 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
573 service->stderr_sharing = service->stderr_disposition = service->stderr_flags = 0;
\r
574 ZeroMemory(service->stderr_path, _countof(service->stderr_path) * sizeof(TCHAR));
\r
581 int get_parameters(nssm_service_t *service, STARTUPINFO *si) {
\r
584 /* Try to open the registry */
\r
585 HKEY key = open_registry(service->name, KEY_READ);
\r
586 if (! key) return 1;
\r
588 /* Don't expand parameters when retrieving for the GUI. */
\r
589 bool expand = si ? true : false;
\r
591 /* Try to get environment variables - may fail */
\r
592 get_environment(service->name, key, NSSM_REG_ENV, &service->env, &service->envlen);
\r
593 /* Environment variables to add to existing rather than replace - may fail. */
\r
594 get_environment(service->name, key, NSSM_REG_ENV_EXTRA, &service->env_extra, &service->env_extralen);
\r
596 /* Set environment if we are starting the service. */
\r
597 if (si) set_service_environment(service);
\r
599 /* Try to get executable file - MUST succeed */
\r
600 if (get_string(key, NSSM_REG_EXE, service->exe, sizeof(service->exe), expand, false, true)) {
\r
605 /* Try to get flags - may fail and we don't care */
\r
606 if (get_string(key, NSSM_REG_FLAGS, service->flags, sizeof(service->flags), expand, false, true)) {
\r
607 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_FLAGS, NSSM_REG_FLAGS, service->name, service->exe, 0);
\r
608 ZeroMemory(service->flags, sizeof(service->flags));
\r
611 /* Try to get startup directory - may fail and we fall back to a default */
\r
612 if (get_string(key, NSSM_REG_DIR, service->dir, sizeof(service->dir), expand, true, true) || ! service->dir[0]) {
\r
613 _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);
\r
614 strip_basename(service->dir);
\r
615 if (service->dir[0] == _T('\0')) {
\r
617 ret = GetWindowsDirectory(service->dir, sizeof(service->dir));
\r
618 if (! ret || ret > sizeof(service->dir)) {
\r
619 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_NO_DIR_AND_NO_FALLBACK, NSSM_REG_DIR, service->name, 0);
\r
624 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_DIR, NSSM_REG_DIR, service->name, service->dir, 0);
\r
627 /* Try to get processor affinity - may fail. */
\r
629 if (get_string(key, NSSM_REG_AFFINITY, buffer, sizeof(buffer), false, false, false) || ! buffer[0]) service->affinity = 0LL;
\r
630 else if (affinity_string_to_mask(buffer, &service->affinity)) {
\r
631 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_AFFINITY_MASK, service->name, buffer);
\r
632 service->affinity = 0LL;
\r
635 DWORD_PTR affinity, system_affinity;
\r
637 if (GetProcessAffinityMask(GetCurrentProcess(), &affinity, &system_affinity)) {
\r
638 _int64 effective_affinity = service->affinity & system_affinity;
\r
639 if (effective_affinity != service->affinity) {
\r
641 if (! affinity_mask_to_string(system_affinity, &system)) {
\r
642 TCHAR *effective = 0;
\r
643 if (! affinity_mask_to_string(effective_affinity, &effective)) {
\r
644 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_EFFECTIVE_AFFINITY_MASK, service->name, buffer, system, effective, 0);
\r
646 HeapFree(GetProcessHeap(), 0, effective);
\r
648 HeapFree(GetProcessHeap(), 0, system);
\r
653 /* Try to get priority - may fail. */
\r
654 unsigned long priority;
\r
655 if (get_number(key, NSSM_REG_PRIORITY, &priority, false) == 1) {
\r
656 if (priority == (priority & priority_mask())) service->priority = priority;
\r
657 else log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_PRIORITY, service->name, NSSM_REG_PRIORITY, 0);
\r
660 /* Try to get hook I/O sharing - may fail. */
\r
661 unsigned long hook_share_output_handles;
\r
662 if (get_number(key, NSSM_REG_HOOK_SHARE_OUTPUT_HANDLES, &hook_share_output_handles, false) == 1) {
\r
663 if (hook_share_output_handles) service->hook_share_output_handles = true;
\r
664 else service->hook_share_output_handles = false;
\r
666 else hook_share_output_handles = false;
\r
667 /* Try to get file rotation settings - may fail. */
\r
668 unsigned long rotate_files;
\r
669 if (get_number(key, NSSM_REG_ROTATE, &rotate_files, false) == 1) {
\r
670 if (rotate_files) service->rotate_files = true;
\r
671 else service->rotate_files = false;
\r
673 else service->rotate_files = false;
\r
674 if (get_number(key, NSSM_REG_ROTATE_ONLINE, &rotate_files, false) == 1) {
\r
675 if (rotate_files) service->rotate_stdout_online = service->rotate_stderr_online = true;
\r
676 else service->rotate_stdout_online = service->rotate_stderr_online = false;
\r
678 else service->rotate_stdout_online = service->rotate_stderr_online = false;
\r
679 /* Hook I/O sharing and online rotation need a pipe. */
\r
680 service->use_stdout_pipe = service->rotate_stdout_online || hook_share_output_handles;
\r
681 service->use_stderr_pipe = service->rotate_stderr_online || hook_share_output_handles;
\r
682 if (get_number(key, NSSM_REG_ROTATE_SECONDS, &service->rotate_seconds, false) != 1) service->rotate_seconds = 0;
\r
683 if (get_number(key, NSSM_REG_ROTATE_BYTES_LOW, &service->rotate_bytes_low, false) != 1) service->rotate_bytes_low = 0;
\r
684 if (get_number(key, NSSM_REG_ROTATE_BYTES_HIGH, &service->rotate_bytes_high, false) != 1) service->rotate_bytes_high = 0;
\r
685 override_milliseconds(service->name, key, NSSM_REG_ROTATE_DELAY, &service->rotate_delay, NSSM_ROTATE_DELAY, NSSM_EVENT_BOGUS_THROTTLE);
\r
687 /* Try to get force new console setting - may fail. */
\r
688 if (get_number(key, NSSM_REG_NO_CONSOLE, &service->no_console, false) != 1) service->no_console = 0;
\r
690 /* Change to startup directory in case stdout/stderr are relative paths. */
\r
691 TCHAR cwd[PATH_LENGTH];
\r
692 GetCurrentDirectory(_countof(cwd), cwd);
\r
693 SetCurrentDirectory(service->dir);
\r
695 /* Try to get stdout and stderr */
\r
696 if (get_io_parameters(service, key)) {
\r
697 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);
\r
699 SetCurrentDirectory(cwd);
\r
703 /* Change back in case the startup directory needs to be deleted. */
\r
704 SetCurrentDirectory(cwd);
\r
706 /* Try to get mandatory restart delay */
\r
707 override_milliseconds(service->name, key, NSSM_REG_RESTART_DELAY, &service->restart_delay, 0, NSSM_EVENT_BOGUS_RESTART_DELAY);
\r
709 /* Try to get throttle restart delay */
\r
710 override_milliseconds(service->name, key, NSSM_REG_THROTTLE, &service->throttle_delay, NSSM_RESET_THROTTLE_RESTART, NSSM_EVENT_BOGUS_THROTTLE);
\r
712 /* Try to get service stop flags. */
\r
713 unsigned long type = REG_DWORD;
\r
714 unsigned long stop_method_skip;
\r
715 unsigned long buflen = sizeof(stop_method_skip);
\r
716 bool stop_ok = false;
\r
717 ret = RegQueryValueEx(key, NSSM_REG_STOP_METHOD_SKIP, 0, &type, (unsigned char *) &stop_method_skip, &buflen);
\r
718 if (ret != ERROR_SUCCESS) {
\r
719 if (ret != ERROR_FILE_NOT_FOUND) {
\r
720 if (type != REG_DWORD) {
\r
721 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_STOP_METHOD_SKIP, service->name, NSSM_REG_STOP_METHOD_SKIP, NSSM, 0);
\r
723 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, NSSM_REG_STOP_METHOD_SKIP, error_string(ret), 0);
\r
726 else stop_ok = true;
\r
728 /* Try all methods except those requested to be skipped. */
\r
729 service->stop_method = ~0;
\r
730 if (stop_ok) service->stop_method &= ~stop_method_skip;
\r
732 /* Try to get kill delays - may fail. */
\r
733 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
734 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
735 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
737 /* Try to get process tree settings - may fail. */
\r
738 unsigned long kill_process_tree;
\r
739 if (get_number(key, NSSM_REG_KILL_PROCESS_TREE, &kill_process_tree, false) == 1) {
\r
740 if (kill_process_tree) service->kill_process_tree = true;
\r
741 else service->kill_process_tree = false;
\r
743 else service->kill_process_tree = true;
\r
745 /* Try to get default exit action. */
\r
746 bool default_action;
\r
747 service->default_exit_action = NSSM_EXIT_RESTART;
\r
748 TCHAR action_string[ACTION_LEN];
\r
749 if (! get_exit_action(service->name, 0, action_string, &default_action)) {
\r
750 for (int i = 0; exit_action_strings[i]; i++) {
\r
751 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
752 service->default_exit_action = i;
\r
758 /* Close registry */
\r
765 Sets the string for the exit action corresponding to the exit code.
\r
767 ret is a pointer to an unsigned long containing the exit code.
\r
768 If ret is NULL, we retrieve the default exit action unconditionally.
\r
770 action is a buffer which receives the string.
\r
772 default_action is a pointer to a bool which is set to false if there
\r
773 was an explicit string for the given exit code, or true if we are
\r
774 returning the default action.
\r
776 Returns: 0 on success.
\r
779 int get_exit_action(const TCHAR *service_name, unsigned long *ret, TCHAR *action, bool *default_action) {
\r
780 /* Are we returning the default action or a status-specific one? */
\r
781 *default_action = ! ret;
\r
783 /* Try to open the registry */
\r
784 HKEY key = open_registry(service_name, NSSM_REG_EXIT, KEY_READ);
\r
785 if (! key) return 1;
\r
787 unsigned long type = REG_SZ;
\r
788 unsigned long action_len = ACTION_LEN;
\r
791 if (! ret) code[0] = _T('\0');
\r
792 else if (_sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), *ret) < 0) {
\r
794 return get_exit_action(service_name, 0, action, default_action);
\r
796 if (RegQueryValueEx(key, code, 0, &type, (unsigned char *) action, &action_len) != ERROR_SUCCESS) {
\r
798 /* Try again with * as the key if an exit code was defined */
\r
799 if (ret) return get_exit_action(service_name, 0, action, default_action);
\r
803 /* Close registry */
\r
809 int set_hook(const TCHAR *service_name, const TCHAR *hook_event, const TCHAR *hook_action, TCHAR *cmd) {
\r
810 /* Try to open the registry */
\r
811 TCHAR registry[KEY_LENGTH];
\r
812 if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, _T("%s\\%s"), NSSM_REG_HOOK, hook_event) < 0) {
\r
813 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("hook registry"), _T("set_hook()"), 0);
\r
820 /* Don't create keys needlessly. */
\r
821 if (! _tcslen(cmd)) {
\r
822 key = open_registry(service_name, registry, KEY_READ, false);
\r
823 if (! key) return 0;
\r
824 error = RegQueryValueEx(key, hook_action, 0, 0, 0, 0);
\r
826 if (error == ERROR_FILE_NOT_FOUND) return 0;
\r
829 key = open_registry(service_name, registry, KEY_WRITE);
\r
830 if (! key) return 1;
\r
833 if (_tcslen(cmd)) ret = set_string(key, (TCHAR *) hook_action, cmd, true);
\r
835 error = RegDeleteValue(key, hook_action);
\r
836 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) ret = 0;
\r
839 /* Close registry */
\r
845 int get_hook(const TCHAR *service_name, const TCHAR *hook_event, const TCHAR *hook_action, TCHAR *buffer, unsigned long buflen) {
\r
846 /* Try to open the registry */
\r
847 TCHAR registry[KEY_LENGTH];
\r
848 if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, _T("%s\\%s"), NSSM_REG_HOOK, hook_event) < 0) {
\r
849 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("hook registry"), _T("get_hook()"), 0);
\r
853 long error = open_registry(service_name, registry, KEY_READ, &key, false);
\r
855 if (error == ERROR_FILE_NOT_FOUND) {
\r
856 ZeroMemory(buffer, buflen);
\r
862 int ret = expand_parameter(key, (TCHAR *) hook_action, buffer, buflen, true, false);
\r
864 /* Close registry */
\r