xref: /openbmc/obmc-console/console-server.c (revision 29c59c44c8f1e45f2a63f23fc0afa3b5c29891bd)
1d831f960SJeremy Kerr /**
2d831f960SJeremy Kerr  * Console server process for OpenBMC
3d831f960SJeremy Kerr  *
49326d779SJeremy Kerr  * Copyright © 2016 IBM Corporation
59326d779SJeremy Kerr  *
69326d779SJeremy Kerr  * Licensed under the Apache License, Version 2.0 (the "License");
79326d779SJeremy Kerr  * you may not use this file except in compliance with the License.
89326d779SJeremy Kerr  * You may obtain a copy of the License at
99326d779SJeremy Kerr  *
109326d779SJeremy Kerr  *     http://www.apache.org/licenses/LICENSE-2.0
119326d779SJeremy Kerr  *
129326d779SJeremy Kerr  * Unless required by applicable law or agreed to in writing, software
139326d779SJeremy Kerr  * distributed under the License is distributed on an "AS IS" BASIS,
149326d779SJeremy Kerr  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
159326d779SJeremy Kerr  * See the License for the specific language governing permissions and
169326d779SJeremy Kerr  * limitations under the License.
17d831f960SJeremy Kerr  */
18d831f960SJeremy Kerr 
19329a35f5SJeremy Kerr #include <assert.h>
20769cee1aSJeremy Kerr #include <errno.h>
21769cee1aSJeremy Kerr #include <signal.h>
22d831f960SJeremy Kerr #include <stdint.h>
23d831f960SJeremy Kerr #include <stdbool.h>
24d831f960SJeremy Kerr #include <stdlib.h>
25d831f960SJeremy Kerr #include <stdio.h>
26d831f960SJeremy Kerr #include <fcntl.h>
27d831f960SJeremy Kerr #include <unistd.h>
28d831f960SJeremy Kerr #include <err.h>
29d831f960SJeremy Kerr #include <string.h>
30d831f960SJeremy Kerr #include <getopt.h>
3117217845SJeremy Kerr #include <limits.h>
321cecc5deSJohnathan Mantey #include <time.h>
3354e9569dSJeremy Kerr #include <termios.h>
34d831f960SJeremy Kerr 
35d831f960SJeremy Kerr #include <sys/types.h>
361cecc5deSJohnathan Mantey #include <sys/time.h>
37b14ca19cSNinad Palsule #include <sys/socket.h>
3887e344cdSJoel Stanley #include <poll.h>
39d831f960SJeremy Kerr 
401a0e03b4SJeremy Kerr #include "console-server.h"
41d831f960SJeremy Kerr 
42f733c85aSJeremy Kerr /* size of the shared backlog ringbuffer */
435db8c792SAndrew Jeffery const size_t buffer_size = 128ul * 1024ul;
44f733c85aSJeremy Kerr 
45769cee1aSJeremy Kerr /* state shared with the signal handler */
46769cee1aSJeremy Kerr static bool sigint;
47329a35f5SJeremy Kerr 
48d831f960SJeremy Kerr static void usage(const char *progname)
49d831f960SJeremy Kerr {
50d831f960SJeremy Kerr 	fprintf(stderr,
516221ce94SVishwanatha Subbanna 		"usage: %s [options] <DEVICE>\n"
52d831f960SJeremy Kerr 		"\n"
53d831f960SJeremy Kerr 		"Options:\n"
54d66195c1SJeremy Kerr 		"  --config <FILE>  Use FILE for configuration\n"
55d831f960SJeremy Kerr 		"",
56d831f960SJeremy Kerr 		progname);
57d831f960SJeremy Kerr }
58d831f960SJeremy Kerr 
5917217845SJeremy Kerr /* populates tty_dev and tty_sysfs_devnode, using the tty kernel name */
601a0e03b4SJeremy Kerr static int tty_find_device(struct console *console)
6117217845SJeremy Kerr {
62d3cb9c22SAndrew Jeffery 	char *tty_class_device_link = NULL;
63d3cb9c22SAndrew Jeffery 	char *tty_path_input_real = NULL;
64d3cb9c22SAndrew Jeffery 	char *tty_device_tty_dir = NULL;
65d3cb9c22SAndrew Jeffery 	char *tty_device_reldir = NULL;
66d3cb9c22SAndrew Jeffery 	char *tty_path_input = NULL;
67d3cb9c22SAndrew Jeffery 	char *tty_kname_real = NULL;
6817217845SJeremy Kerr 	int rc;
6917217845SJeremy Kerr 
7045ad7676SYi Li 	/* udev may rename the tty name with a symbol link, try to resolve */
7145ad7676SYi Li 	rc = asprintf(&tty_path_input, "/dev/%s", console->tty_kname);
722834c5b1SAndrew Jeffery 	if (rc < 0) {
7317217845SJeremy Kerr 		return -1;
742834c5b1SAndrew Jeffery 	}
7517217845SJeremy Kerr 
7645ad7676SYi Li 	tty_path_input_real = realpath(tty_path_input, NULL);
7745ad7676SYi Li 	if (!tty_path_input_real) {
7845ad7676SYi Li 		warn("Can't find realpath for /dev/%s", console->tty_kname);
7915792aa7SAndrew Jeffery 		rc = -1;
8045ad7676SYi Li 		goto out_free;
8145ad7676SYi Li 	}
8245ad7676SYi Li 
8345ad7676SYi Li 	tty_kname_real = basename(tty_path_input_real);
8445ad7676SYi Li 	if (!tty_kname_real) {
8545ad7676SYi Li 		warn("Can't find real name for /dev/%s", console->tty_kname);
8615792aa7SAndrew Jeffery 		rc = -1;
8745ad7676SYi Li 		goto out_free;
8845ad7676SYi Li 	}
8945ad7676SYi Li 
90a72711afSAndrew Jeffery 	rc = asprintf(&tty_class_device_link, "/sys/class/tty/%s",
91a72711afSAndrew Jeffery 		      tty_kname_real);
922834c5b1SAndrew Jeffery 	if (rc < 0) {
9345ad7676SYi Li 		goto out_free;
942834c5b1SAndrew Jeffery 	}
9545ad7676SYi Li 
9617217845SJeremy Kerr 	tty_device_tty_dir = realpath(tty_class_device_link, NULL);
9745ad7676SYi Li 	if (!tty_device_tty_dir) {
9845ad7676SYi Li 		warn("Can't query sysfs for device %s", tty_kname_real);
9915792aa7SAndrew Jeffery 		rc = -1;
10017217845SJeremy Kerr 		goto out_free;
10117217845SJeremy Kerr 	}
10217217845SJeremy Kerr 
10317217845SJeremy Kerr 	rc = asprintf(&tty_device_reldir, "%s/../../", tty_device_tty_dir);
1042834c5b1SAndrew Jeffery 	if (rc < 0) {
10517217845SJeremy Kerr 		goto out_free;
1062834c5b1SAndrew Jeffery 	}
10717217845SJeremy Kerr 
1081a0e03b4SJeremy Kerr 	console->tty_sysfs_devnode = realpath(tty_device_reldir, NULL);
1092834c5b1SAndrew Jeffery 	if (!console->tty_sysfs_devnode) {
11045ad7676SYi Li 		warn("Can't find parent device for %s", tty_kname_real);
1112834c5b1SAndrew Jeffery 	}
11217217845SJeremy Kerr 
11345ad7676SYi Li 	rc = asprintf(&console->tty_dev, "/dev/%s", tty_kname_real);
1142834c5b1SAndrew Jeffery 	if (rc < 0) {
11517217845SJeremy Kerr 		goto out_free;
1162834c5b1SAndrew Jeffery 	}
11717217845SJeremy Kerr 
11817217845SJeremy Kerr 	rc = 0;
11917217845SJeremy Kerr 
12017217845SJeremy Kerr out_free:
12117217845SJeremy Kerr 	free(tty_class_device_link);
12217217845SJeremy Kerr 	free(tty_device_tty_dir);
12317217845SJeremy Kerr 	free(tty_device_reldir);
12445ad7676SYi Li 	free(tty_path_input);
12545ad7676SYi Li 	free(tty_path_input_real);
12617217845SJeremy Kerr 	return rc;
12717217845SJeremy Kerr }
12817217845SJeremy Kerr 
1291a0e03b4SJeremy Kerr static int tty_set_sysfs_attr(struct console *console, const char *name,
130957818b4SJeremy Kerr 			      int value)
131957818b4SJeremy Kerr {
132957818b4SJeremy Kerr 	char *path;
133957818b4SJeremy Kerr 	FILE *fp;
134957818b4SJeremy Kerr 	int rc;
135957818b4SJeremy Kerr 
1361a0e03b4SJeremy Kerr 	rc = asprintf(&path, "%s/%s", console->tty_sysfs_devnode, name);
1372834c5b1SAndrew Jeffery 	if (rc < 0) {
138957818b4SJeremy Kerr 		return -1;
1392834c5b1SAndrew Jeffery 	}
140957818b4SJeremy Kerr 
141957818b4SJeremy Kerr 	fp = fopen(path, "w");
142957818b4SJeremy Kerr 	if (!fp) {
143a72711afSAndrew Jeffery 		warn("Can't access attribute %s on device %s", name,
144a72711afSAndrew Jeffery 		     console->tty_kname);
145957818b4SJeremy Kerr 		rc = -1;
146957818b4SJeremy Kerr 		goto out_free;
147957818b4SJeremy Kerr 	}
148957818b4SJeremy Kerr 	setvbuf(fp, NULL, _IONBF, 0);
149957818b4SJeremy Kerr 
150957818b4SJeremy Kerr 	rc = fprintf(fp, "0x%x", value);
1512834c5b1SAndrew Jeffery 	if (rc < 0) {
152a72711afSAndrew Jeffery 		warn("Error writing to %s attribute of device %s", name,
153a72711afSAndrew Jeffery 		     console->tty_kname);
1542834c5b1SAndrew Jeffery 	}
155957818b4SJeremy Kerr 	fclose(fp);
156957818b4SJeremy Kerr 
157957818b4SJeremy Kerr out_free:
158957818b4SJeremy Kerr 	free(path);
159957818b4SJeremy Kerr 	return rc;
160957818b4SJeremy Kerr }
161957818b4SJeremy Kerr 
162d831f960SJeremy Kerr /**
163c7fbcd48SBenjamin Fair  * Set termios attributes on the console tty.
16454e9569dSJeremy Kerr  */
165e258e51fSNinad Palsule void tty_init_termios(struct console *console)
16654e9569dSJeremy Kerr {
16754e9569dSJeremy Kerr 	struct termios termios;
16854e9569dSJeremy Kerr 	int rc;
16954e9569dSJeremy Kerr 
17054e9569dSJeremy Kerr 	rc = tcgetattr(console->tty_fd, &termios);
17154e9569dSJeremy Kerr 	if (rc) {
17254e9569dSJeremy Kerr 		warn("Can't read tty termios");
17354e9569dSJeremy Kerr 		return;
17454e9569dSJeremy Kerr 	}
17554e9569dSJeremy Kerr 
176c7fbcd48SBenjamin Fair 	if (console->tty_baud) {
1772834c5b1SAndrew Jeffery 		if (cfsetspeed(&termios, console->tty_baud) < 0) {
178c7fbcd48SBenjamin Fair 			warn("Couldn't set speeds for %s", console->tty_kname);
179c7fbcd48SBenjamin Fair 		}
1802834c5b1SAndrew Jeffery 	}
181c7fbcd48SBenjamin Fair 
182c7fbcd48SBenjamin Fair 	/* Set console to raw mode: we don't want any processing to occur on
183c7fbcd48SBenjamin Fair 	 * the underlying terminal input/output.
184c7fbcd48SBenjamin Fair 	 */
18554e9569dSJeremy Kerr 	cfmakeraw(&termios);
186c7fbcd48SBenjamin Fair 
18754e9569dSJeremy Kerr 	rc = tcsetattr(console->tty_fd, TCSANOW, &termios);
1882834c5b1SAndrew Jeffery 	if (rc) {
189c7fbcd48SBenjamin Fair 		warn("Can't set terminal options for %s", console->tty_kname);
19054e9569dSJeremy Kerr 	}
1912834c5b1SAndrew Jeffery }
19254e9569dSJeremy Kerr 
19354e9569dSJeremy Kerr /**
194d831f960SJeremy Kerr  * Open and initialise the serial device
195d831f960SJeremy Kerr  */
1961a0e03b4SJeremy Kerr static int tty_init_io(struct console *console)
197d831f960SJeremy Kerr {
1982834c5b1SAndrew Jeffery 	if (console->tty_sirq) {
1991a0e03b4SJeremy Kerr 		tty_set_sysfs_attr(console, "sirq", console->tty_sirq);
2002834c5b1SAndrew Jeffery 	}
2012834c5b1SAndrew Jeffery 	if (console->tty_lpc_addr) {
2021a0e03b4SJeremy Kerr 		tty_set_sysfs_attr(console, "lpc_address",
2031a0e03b4SJeremy Kerr 				   console->tty_lpc_addr);
2042834c5b1SAndrew Jeffery 	}
205957818b4SJeremy Kerr 
2061a0e03b4SJeremy Kerr 	console->tty_fd = open(console->tty_dev, O_RDWR);
2071a0e03b4SJeremy Kerr 	if (console->tty_fd <= 0) {
2081a0e03b4SJeremy Kerr 		warn("Can't open tty %s", console->tty_dev);
209d831f960SJeremy Kerr 		return -1;
210d831f960SJeremy Kerr 	}
211d831f960SJeremy Kerr 
212d831f960SJeremy Kerr 	/* Disable character delay. We may want to later enable this when
213d831f960SJeremy Kerr 	 * we detect larger amounts of data
214d831f960SJeremy Kerr 	 */
2151a0e03b4SJeremy Kerr 	fcntl(console->tty_fd, F_SETFL, FNDELAY);
216d831f960SJeremy Kerr 
21754e9569dSJeremy Kerr 	tty_init_termios(console);
21854e9569dSJeremy Kerr 
219329a35f5SJeremy Kerr 	console->pollfds[console->n_pollers].fd = console->tty_fd;
220329a35f5SJeremy Kerr 	console->pollfds[console->n_pollers].events = POLLIN;
221329a35f5SJeremy Kerr 
222d831f960SJeremy Kerr 	return 0;
223d831f960SJeremy Kerr }
224d831f960SJeremy Kerr 
225d66195c1SJeremy Kerr static int tty_init(struct console *console, struct config *config)
226d66195c1SJeremy Kerr {
227fd883a88SAndrew Jeffery 	unsigned long parsed;
228d66195c1SJeremy Kerr 	const char *val;
229d66195c1SJeremy Kerr 	char *endp;
230d66195c1SJeremy Kerr 	int rc;
231d66195c1SJeremy Kerr 
232d66195c1SJeremy Kerr 	val = config_get_value(config, "lpc-address");
233d66195c1SJeremy Kerr 	if (val) {
234fd883a88SAndrew Jeffery 		errno = 0;
235fd883a88SAndrew Jeffery 		parsed = strtoul(val, &endp, 0);
236fd883a88SAndrew Jeffery 		if (parsed == ULONG_MAX && errno == ERANGE) {
237fd883a88SAndrew Jeffery 			warn("Cannot interpret 'lpc-address' value as an unsigned long: '%s'",
238fd883a88SAndrew Jeffery 			     val);
239fd883a88SAndrew Jeffery 			return -1;
240fd883a88SAndrew Jeffery 		}
241fd883a88SAndrew Jeffery 
242fd883a88SAndrew Jeffery 		if (parsed > UINT16_MAX) {
243fd883a88SAndrew Jeffery 			warn("Invalid LPC address '%s'", val);
244fd883a88SAndrew Jeffery 			return -1;
245fd883a88SAndrew Jeffery 		}
246fd883a88SAndrew Jeffery 
247fd883a88SAndrew Jeffery 		console->tty_lpc_addr = (uint16_t)parsed;
248d66195c1SJeremy Kerr 		if (endp == optarg) {
249d66195c1SJeremy Kerr 			warn("Invalid LPC address: '%s'", val);
250d66195c1SJeremy Kerr 			return -1;
251d66195c1SJeremy Kerr 		}
252d66195c1SJeremy Kerr 	}
253d66195c1SJeremy Kerr 
254d66195c1SJeremy Kerr 	val = config_get_value(config, "sirq");
255d66195c1SJeremy Kerr 	if (val) {
256fd883a88SAndrew Jeffery 		errno = 0;
257fd883a88SAndrew Jeffery 		parsed = strtoul(val, &endp, 0);
258fd883a88SAndrew Jeffery 		if (parsed == ULONG_MAX && errno == ERANGE) {
259fd883a88SAndrew Jeffery 			warn("Cannot interpret 'sirq' value as an unsigned long: '%s'",
260fd883a88SAndrew Jeffery 			     val);
261fd883a88SAndrew Jeffery 		}
262fd883a88SAndrew Jeffery 
2632834c5b1SAndrew Jeffery 		if (parsed > 16) {
264fd883a88SAndrew Jeffery 			warn("Invalid LPC SERIRQ: '%s'", val);
2652834c5b1SAndrew Jeffery 		}
266fd883a88SAndrew Jeffery 
267fd883a88SAndrew Jeffery 		console->tty_sirq = (int)parsed;
2682834c5b1SAndrew Jeffery 		if (endp == optarg) {
269d66195c1SJeremy Kerr 			warn("Invalid sirq: '%s'", val);
270d66195c1SJeremy Kerr 		}
2712834c5b1SAndrew Jeffery 	}
272d66195c1SJeremy Kerr 
273c7fbcd48SBenjamin Fair 	val = config_get_value(config, "baud");
274c7fbcd48SBenjamin Fair 	if (val) {
2752834c5b1SAndrew Jeffery 		if (config_parse_baud(&console->tty_baud, val)) {
276c7fbcd48SBenjamin Fair 			warnx("Invalid baud rate: '%s'", val);
277c7fbcd48SBenjamin Fair 		}
2782834c5b1SAndrew Jeffery 	}
279c7fbcd48SBenjamin Fair 
280d66195c1SJeremy Kerr 	if (!console->tty_kname) {
281d66195c1SJeremy Kerr 		warnx("Error: No TTY device specified");
282d66195c1SJeremy Kerr 		return -1;
283d66195c1SJeremy Kerr 	}
284d66195c1SJeremy Kerr 
285d66195c1SJeremy Kerr 	rc = tty_find_device(console);
2862834c5b1SAndrew Jeffery 	if (rc) {
287d66195c1SJeremy Kerr 		return rc;
2882834c5b1SAndrew Jeffery 	}
289d66195c1SJeremy Kerr 
290d66195c1SJeremy Kerr 	rc = tty_init_io(console);
291d66195c1SJeremy Kerr 	return rc;
292d66195c1SJeremy Kerr }
293d66195c1SJeremy Kerr 
2941a0e03b4SJeremy Kerr int console_data_out(struct console *console, const uint8_t *data, size_t len)
295d831f960SJeremy Kerr {
2961a0e03b4SJeremy Kerr 	return write_buf_to_fd(console->tty_fd, data, len);
297d831f960SJeremy Kerr }
298d831f960SJeremy Kerr 
299b14ca19cSNinad Palsule /* Read console if from config and prepare a socket name */
300b14ca19cSNinad Palsule static int set_socket_info(struct console *console, struct config *config)
301b14ca19cSNinad Palsule {
302b14ca19cSNinad Palsule 	ssize_t len;
303b14ca19cSNinad Palsule 
304b14ca19cSNinad Palsule 	console->console_id = config_get_value(config, "socket-id");
305b14ca19cSNinad Palsule 	if (!console->console_id) {
306b14ca19cSNinad Palsule 		warnx("Error: The socket-id is not set in the config file");
307b14ca19cSNinad Palsule 		return EXIT_FAILURE;
308b14ca19cSNinad Palsule 	}
309b14ca19cSNinad Palsule 
310b14ca19cSNinad Palsule 	/* Get the socket name/path */
311b14ca19cSNinad Palsule 	len = console_socket_path(console->socket_name, console->console_id);
312b14ca19cSNinad Palsule 	if (len < 0) {
313b14ca19cSNinad Palsule 		warn("Failed to set socket path: %s", strerror(errno));
314b14ca19cSNinad Palsule 		return EXIT_FAILURE;
315b14ca19cSNinad Palsule 	}
316b14ca19cSNinad Palsule 
317b14ca19cSNinad Palsule 	/* Socket name is not a null terminated string hence save the length */
318b14ca19cSNinad Palsule 	console->socket_name_len = len;
319b14ca19cSNinad Palsule 
320b14ca19cSNinad Palsule 	return 0;
321b14ca19cSNinad Palsule }
322b14ca19cSNinad Palsule 
323d47963e5SJeremy Kerr static void handlers_init(struct console *console, struct config *config)
324d831f960SJeremy Kerr {
325b70f8713SAndrew Jeffery 	/* NOLINTBEGIN(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) */
326b70f8713SAndrew Jeffery 	extern struct handler *__start_handlers;
327b70f8713SAndrew Jeffery 	extern struct handler *__stop_handlers;
328b70f8713SAndrew Jeffery 	/* NOLINTEND(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) */
3291a0e03b4SJeremy Kerr 	struct handler *handler;
330b70f8713SAndrew Jeffery 	int i;
331b70f8713SAndrew Jeffery 	int rc;
332d831f960SJeremy Kerr 
3331a0e03b4SJeremy Kerr 	console->n_handlers = &__stop_handlers - &__start_handlers;
3341a0e03b4SJeremy Kerr 	console->handlers = &__start_handlers;
335d831f960SJeremy Kerr 
3365c359cc6SAndrew Jeffery 	printf("%ld handler%s\n", console->n_handlers,
3371a0e03b4SJeremy Kerr 	       console->n_handlers == 1 ? "" : "s");
338d831f960SJeremy Kerr 
3391a0e03b4SJeremy Kerr 	for (i = 0; i < console->n_handlers; i++) {
3401a0e03b4SJeremy Kerr 		handler = console->handlers[i];
3411a0e03b4SJeremy Kerr 
342021b91f0SJeremy Kerr 		rc = 0;
3432834c5b1SAndrew Jeffery 		if (handler->init) {
344021b91f0SJeremy Kerr 			rc = handler->init(handler, console, config);
3452834c5b1SAndrew Jeffery 		}
346021b91f0SJeremy Kerr 
347021b91f0SJeremy Kerr 		handler->active = rc == 0;
348021b91f0SJeremy Kerr 
349021b91f0SJeremy Kerr 		printf("  %s [%sactive]\n", handler->name,
350021b91f0SJeremy Kerr 		       handler->active ? "" : "in");
351d831f960SJeremy Kerr 	}
352d831f960SJeremy Kerr }
353d831f960SJeremy Kerr 
3541a0e03b4SJeremy Kerr static void handlers_fini(struct console *console)
355d831f960SJeremy Kerr {
3561a0e03b4SJeremy Kerr 	struct handler *handler;
3571a0e03b4SJeremy Kerr 	int i;
3581a0e03b4SJeremy Kerr 
3591a0e03b4SJeremy Kerr 	for (i = 0; i < console->n_handlers; i++) {
3601a0e03b4SJeremy Kerr 		handler = console->handlers[i];
3612834c5b1SAndrew Jeffery 		if (handler->fini && handler->active) {
3621a0e03b4SJeremy Kerr 			handler->fini(handler);
3631a0e03b4SJeremy Kerr 		}
364d831f960SJeremy Kerr 	}
3652834c5b1SAndrew Jeffery }
366d831f960SJeremy Kerr 
3671cecc5deSJohnathan Mantey static int get_current_time(struct timeval *tv)
3681cecc5deSJohnathan Mantey {
3691cecc5deSJohnathan Mantey 	struct timespec t;
3701cecc5deSJohnathan Mantey 	int rc;
3711cecc5deSJohnathan Mantey 
3721cecc5deSJohnathan Mantey 	/*
3731cecc5deSJohnathan Mantey 	 * We use clock_gettime(CLOCK_MONOTONIC) so we're immune to
3741cecc5deSJohnathan Mantey 	 * local time changes. However, a struct timeval is more
3751cecc5deSJohnathan Mantey 	 * convenient for calculations, so convert to that.
3761cecc5deSJohnathan Mantey 	 */
3771cecc5deSJohnathan Mantey 	rc = clock_gettime(CLOCK_MONOTONIC, &t);
3782834c5b1SAndrew Jeffery 	if (rc) {
3791cecc5deSJohnathan Mantey 		return rc;
3802834c5b1SAndrew Jeffery 	}
3811cecc5deSJohnathan Mantey 
3821cecc5deSJohnathan Mantey 	tv->tv_sec = t.tv_sec;
3831cecc5deSJohnathan Mantey 	tv->tv_usec = t.tv_nsec / 1000;
3841cecc5deSJohnathan Mantey 
3851cecc5deSJohnathan Mantey 	return 0;
3861cecc5deSJohnathan Mantey }
3871cecc5deSJohnathan Mantey 
388a72711afSAndrew Jeffery struct ringbuffer_consumer *
389a72711afSAndrew Jeffery console_ringbuffer_consumer_register(struct console *console,
390f733c85aSJeremy Kerr 				     ringbuffer_poll_fn_t poll_fn, void *data)
391d831f960SJeremy Kerr {
392f733c85aSJeremy Kerr 	return ringbuffer_consumer_register(console->rb, poll_fn, data);
393d831f960SJeremy Kerr }
394d831f960SJeremy Kerr 
39555c9712dSJeremy Kerr struct poller *console_poller_register(struct console *console,
396a72711afSAndrew Jeffery 				       struct handler *handler,
397a72711afSAndrew Jeffery 				       poller_event_fn_t poller_fn,
3981cecc5deSJohnathan Mantey 				       poller_timeout_fn_t timeout_fn, int fd,
3991cecc5deSJohnathan Mantey 				       int events, void *data)
400d831f960SJeremy Kerr {
401329a35f5SJeremy Kerr 	struct poller *poller;
4025c359cc6SAndrew Jeffery 	long n;
403329a35f5SJeremy Kerr 
404329a35f5SJeremy Kerr 	poller = malloc(sizeof(*poller));
405329a35f5SJeremy Kerr 	poller->remove = false;
406329a35f5SJeremy Kerr 	poller->handler = handler;
4071cecc5deSJohnathan Mantey 	poller->event_fn = poller_fn;
4081cecc5deSJohnathan Mantey 	poller->timeout_fn = timeout_fn;
409329a35f5SJeremy Kerr 	poller->data = data;
410329a35f5SJeremy Kerr 
411329a35f5SJeremy Kerr 	/* add one to our pollers array */
412329a35f5SJeremy Kerr 	n = console->n_pollers++;
41391b52175SAndrew Jeffery 	/*
41491b52175SAndrew Jeffery 	 * We're managing an array of pointers to aggregates, so don't warn about sizeof() on a
41591b52175SAndrew Jeffery 	 * pointer type.
41691b52175SAndrew Jeffery 	 */
41791b52175SAndrew Jeffery 	/* NOLINTBEGIN(bugprone-sizeof-expression) */
41891b52175SAndrew Jeffery 	console->pollers = reallocarray(console->pollers, console->n_pollers,
41991b52175SAndrew Jeffery 					sizeof(*console->pollers));
42091b52175SAndrew Jeffery 	/* NOLINTEND(bugprone-sizeof-expression) */
421329a35f5SJeremy Kerr 
422329a35f5SJeremy Kerr 	console->pollers[n] = poller;
423329a35f5SJeremy Kerr 
424329a35f5SJeremy Kerr 	/* increase pollfds array too  */
425a72711afSAndrew Jeffery 	console->pollfds =
42691b52175SAndrew Jeffery 		reallocarray(console->pollfds,
42791b52175SAndrew Jeffery 			     (MAX_INTERNAL_POLLFD + console->n_pollers),
42891b52175SAndrew Jeffery 			     sizeof(*console->pollfds));
429329a35f5SJeremy Kerr 
430329a35f5SJeremy Kerr 	/* shift the end pollfds up by one */
431a72711afSAndrew Jeffery 	memcpy(&console->pollfds[n + 1], &console->pollfds[n],
432f9c8f6caSCheng C Yang 	       sizeof(*console->pollfds) * MAX_INTERNAL_POLLFD);
433329a35f5SJeremy Kerr 
434329a35f5SJeremy Kerr 	console->pollfds[n].fd = fd;
4355c359cc6SAndrew Jeffery 	console->pollfds[n].events = (short)(events & 0x7fff);
436329a35f5SJeremy Kerr 
437329a35f5SJeremy Kerr 	return poller;
438329a35f5SJeremy Kerr }
439329a35f5SJeremy Kerr 
440a72711afSAndrew Jeffery void console_poller_unregister(struct console *console, struct poller *poller)
441329a35f5SJeremy Kerr {
442329a35f5SJeremy Kerr 	int i;
443329a35f5SJeremy Kerr 
444329a35f5SJeremy Kerr 	/* find the entry in our pollers array */
4452834c5b1SAndrew Jeffery 	for (i = 0; i < console->n_pollers; i++) {
4462834c5b1SAndrew Jeffery 		if (console->pollers[i] == poller) {
447329a35f5SJeremy Kerr 			break;
4482834c5b1SAndrew Jeffery 		}
4492834c5b1SAndrew Jeffery 	}
450329a35f5SJeremy Kerr 
451329a35f5SJeremy Kerr 	assert(i < console->n_pollers);
452329a35f5SJeremy Kerr 
453329a35f5SJeremy Kerr 	console->n_pollers--;
454329a35f5SJeremy Kerr 
45591b52175SAndrew Jeffery 	/*
45691b52175SAndrew Jeffery 	 * Remove the item from the pollers array...
45791b52175SAndrew Jeffery 	 *
45891b52175SAndrew Jeffery 	 * We're managing an array of pointers to aggregates, so don't warn about sizeof() on a
45991b52175SAndrew Jeffery 	 * pointer type.
46091b52175SAndrew Jeffery 	 */
46191b52175SAndrew Jeffery 	/* NOLINTBEGIN(bugprone-sizeof-expression) */
462329a35f5SJeremy Kerr 	memmove(&console->pollers[i], &console->pollers[i + 1],
463a72711afSAndrew Jeffery 		sizeof(*console->pollers) * (console->n_pollers - i));
464329a35f5SJeremy Kerr 
46591b52175SAndrew Jeffery 	console->pollers = reallocarray(console->pollers, console->n_pollers,
46691b52175SAndrew Jeffery 					sizeof(*console->pollers));
46791b52175SAndrew Jeffery 	/* NOLINTEND(bugprone-sizeof-expression) */
468329a35f5SJeremy Kerr 
469329a35f5SJeremy Kerr 	/* ... and the pollfds array */
470329a35f5SJeremy Kerr 	memmove(&console->pollfds[i], &console->pollfds[i + 1],
471329a35f5SJeremy Kerr 		sizeof(*console->pollfds) *
472f9c8f6caSCheng C Yang 			(MAX_INTERNAL_POLLFD + console->n_pollers - i));
473329a35f5SJeremy Kerr 
474a72711afSAndrew Jeffery 	console->pollfds =
47591b52175SAndrew Jeffery 		reallocarray(console->pollfds,
47691b52175SAndrew Jeffery 			     (MAX_INTERNAL_POLLFD + console->n_pollers),
47791b52175SAndrew Jeffery 			     sizeof(*console->pollfds));
478329a35f5SJeremy Kerr 
479329a35f5SJeremy Kerr 	free(poller);
480329a35f5SJeremy Kerr }
481329a35f5SJeremy Kerr 
4826b1fed27SJeremy Kerr void console_poller_set_events(struct console *console, struct poller *poller,
4836b1fed27SJeremy Kerr 			       int events)
4846b1fed27SJeremy Kerr {
4856b1fed27SJeremy Kerr 	int i;
4866b1fed27SJeremy Kerr 
4876b1fed27SJeremy Kerr 	/* find the entry in our pollers array */
4882834c5b1SAndrew Jeffery 	for (i = 0; i < console->n_pollers; i++) {
4892834c5b1SAndrew Jeffery 		if (console->pollers[i] == poller) {
4906b1fed27SJeremy Kerr 			break;
4912834c5b1SAndrew Jeffery 		}
4922834c5b1SAndrew Jeffery 	}
4936b1fed27SJeremy Kerr 
4945c359cc6SAndrew Jeffery 	console->pollfds[i].events = (short)(events & 0x7fff);
4956b1fed27SJeremy Kerr }
4966b1fed27SJeremy Kerr 
497fd048328SAndrew Jeffery void console_poller_set_timeout(struct console *console __attribute__((unused)),
498fd048328SAndrew Jeffery 				struct poller *poller, const struct timeval *tv)
4991cecc5deSJohnathan Mantey {
5001cecc5deSJohnathan Mantey 	struct timeval now;
5011cecc5deSJohnathan Mantey 	int rc;
5021cecc5deSJohnathan Mantey 
5031cecc5deSJohnathan Mantey 	rc = get_current_time(&now);
5042834c5b1SAndrew Jeffery 	if (rc) {
5051cecc5deSJohnathan Mantey 		return;
5062834c5b1SAndrew Jeffery 	}
5071cecc5deSJohnathan Mantey 
5081cecc5deSJohnathan Mantey 	timeradd(&now, tv, &poller->timeout);
5091cecc5deSJohnathan Mantey }
5101cecc5deSJohnathan Mantey 
5115c359cc6SAndrew Jeffery static long get_poll_timeout(struct console *console, struct timeval *cur_time)
5121cecc5deSJohnathan Mantey {
513b70f8713SAndrew Jeffery 	struct timeval *earliest;
514b70f8713SAndrew Jeffery 	struct timeval interval;
5151cecc5deSJohnathan Mantey 	struct poller *poller;
5161cecc5deSJohnathan Mantey 	int i;
5171cecc5deSJohnathan Mantey 
5181cecc5deSJohnathan Mantey 	earliest = NULL;
5191cecc5deSJohnathan Mantey 
5201cecc5deSJohnathan Mantey 	for (i = 0; i < console->n_pollers; i++) {
5211cecc5deSJohnathan Mantey 		poller = console->pollers[i];
5221cecc5deSJohnathan Mantey 
5231cecc5deSJohnathan Mantey 		if (poller->timeout_fn && timerisset(&poller->timeout) &&
5241cecc5deSJohnathan Mantey 		    (!earliest ||
5251cecc5deSJohnathan Mantey 		     (earliest && timercmp(&poller->timeout, earliest, <)))) {
5261cecc5deSJohnathan Mantey 			// poller is buffering data and needs the poll
5271cecc5deSJohnathan Mantey 			// function to timeout.
5281cecc5deSJohnathan Mantey 			earliest = &poller->timeout;
5291cecc5deSJohnathan Mantey 		}
5301cecc5deSJohnathan Mantey 	}
5311cecc5deSJohnathan Mantey 
5321cecc5deSJohnathan Mantey 	if (earliest) {
5331cecc5deSJohnathan Mantey 		if (timercmp(earliest, cur_time, >)) {
5341cecc5deSJohnathan Mantey 			/* recalculate the timeout period, time period has
5351cecc5deSJohnathan Mantey 			 * not elapsed */
5361cecc5deSJohnathan Mantey 			timersub(earliest, cur_time, &interval);
5371cecc5deSJohnathan Mantey 			return ((interval.tv_sec * 1000) +
5381cecc5deSJohnathan Mantey 				(interval.tv_usec / 1000));
5390b7b0477SAndrew Jeffery 		} /* return from poll immediately */
5401cecc5deSJohnathan Mantey 		return 0;
5410b7b0477SAndrew Jeffery 
5420b7b0477SAndrew Jeffery 	} /* poll indefinitely */
5431cecc5deSJohnathan Mantey 	return -1;
5441cecc5deSJohnathan Mantey }
5451cecc5deSJohnathan Mantey 
5461cecc5deSJohnathan Mantey static int call_pollers(struct console *console, struct timeval *cur_time)
547329a35f5SJeremy Kerr {
548329a35f5SJeremy Kerr 	struct poller *poller;
549329a35f5SJeremy Kerr 	struct pollfd *pollfd;
550329a35f5SJeremy Kerr 	enum poller_ret prc;
551b70f8713SAndrew Jeffery 	int i;
552b70f8713SAndrew Jeffery 	int rc;
553d831f960SJeremy Kerr 
5541a0e03b4SJeremy Kerr 	rc = 0;
5551a0e03b4SJeremy Kerr 
556329a35f5SJeremy Kerr 	/*
557329a35f5SJeremy Kerr 	 * Process poll events by iterating through the pollers and pollfds
558329a35f5SJeremy Kerr 	 * in-step, calling any pollers that we've found revents for.
559329a35f5SJeremy Kerr 	 */
560329a35f5SJeremy Kerr 	for (i = 0; i < console->n_pollers; i++) {
561329a35f5SJeremy Kerr 		poller = console->pollers[i];
562329a35f5SJeremy Kerr 		pollfd = &console->pollfds[i];
5631cecc5deSJohnathan Mantey 		prc = POLLER_OK;
5641a0e03b4SJeremy Kerr 
5651cecc5deSJohnathan Mantey 		/* process pending events... */
5661cecc5deSJohnathan Mantey 		if (pollfd->revents) {
5671cecc5deSJohnathan Mantey 			prc = poller->event_fn(poller->handler, pollfd->revents,
568329a35f5SJeremy Kerr 					       poller->data);
5692834c5b1SAndrew Jeffery 			if (prc == POLLER_EXIT) {
570329a35f5SJeremy Kerr 				rc = -1;
5712834c5b1SAndrew Jeffery 			} else if (prc == POLLER_REMOVE) {
572329a35f5SJeremy Kerr 				poller->remove = true;
573329a35f5SJeremy Kerr 			}
5742834c5b1SAndrew Jeffery 		}
575329a35f5SJeremy Kerr 
5761cecc5deSJohnathan Mantey 		if ((prc == POLLER_OK) && poller->timeout_fn &&
5771cecc5deSJohnathan Mantey 		    timerisset(&poller->timeout) &&
5781cecc5deSJohnathan Mantey 		    timercmp(&poller->timeout, cur_time, <=)) {
5791cecc5deSJohnathan Mantey 			/* One of the ringbuffer consumers is buffering the
5801cecc5deSJohnathan Mantey 			data stream. The amount of idle time the consumer
5811cecc5deSJohnathan Mantey 			desired has expired.  Process the buffered data for
5821cecc5deSJohnathan Mantey 			transmission. */
5831cecc5deSJohnathan Mantey 			timerclear(&poller->timeout);
5841cecc5deSJohnathan Mantey 			prc = poller->timeout_fn(poller->handler, poller->data);
5851cecc5deSJohnathan Mantey 			if (prc == POLLER_EXIT) {
5861cecc5deSJohnathan Mantey 				rc = -1;
5871cecc5deSJohnathan Mantey 			} else if (prc == POLLER_REMOVE) {
5881cecc5deSJohnathan Mantey 				poller->remove = true;
5891cecc5deSJohnathan Mantey 			}
5901cecc5deSJohnathan Mantey 		}
5911cecc5deSJohnathan Mantey 	}
5921cecc5deSJohnathan Mantey 
593329a35f5SJeremy Kerr 	/**
594329a35f5SJeremy Kerr 	 * Process deferred removals; restarting each time we unregister, as
595329a35f5SJeremy Kerr 	 * the array will have changed
596329a35f5SJeremy Kerr 	 */
597329a35f5SJeremy Kerr 	for (;;) {
598329a35f5SJeremy Kerr 		bool removed = false;
599329a35f5SJeremy Kerr 
600329a35f5SJeremy Kerr 		for (i = 0; i < console->n_pollers; i++) {
601329a35f5SJeremy Kerr 			poller = console->pollers[i];
602329a35f5SJeremy Kerr 			if (poller->remove) {
60355c9712dSJeremy Kerr 				console_poller_unregister(console, poller);
604329a35f5SJeremy Kerr 				removed = true;
605329a35f5SJeremy Kerr 				break;
606329a35f5SJeremy Kerr 			}
607329a35f5SJeremy Kerr 		}
6082834c5b1SAndrew Jeffery 		if (!removed) {
609329a35f5SJeremy Kerr 			break;
6101a0e03b4SJeremy Kerr 		}
6112834c5b1SAndrew Jeffery 	}
6121a0e03b4SJeremy Kerr 
6131a0e03b4SJeremy Kerr 	return rc;
6141a0e03b4SJeremy Kerr }
6151a0e03b4SJeremy Kerr 
616769cee1aSJeremy Kerr static void sighandler(int signal)
617769cee1aSJeremy Kerr {
6182834c5b1SAndrew Jeffery 	if (signal == SIGINT) {
619769cee1aSJeremy Kerr 		sigint = true;
620769cee1aSJeremy Kerr 	}
6212834c5b1SAndrew Jeffery }
622769cee1aSJeremy Kerr 
6231a0e03b4SJeremy Kerr int run_console(struct console *console)
6241a0e03b4SJeremy Kerr {
6255c359cc6SAndrew Jeffery 	sighandler_t sighandler_save = signal(SIGINT, sighandler);
6261cecc5deSJohnathan Mantey 	struct timeval tv;
6275c359cc6SAndrew Jeffery 	long timeout;
6285c359cc6SAndrew Jeffery 	ssize_t rc;
629769cee1aSJeremy Kerr 
630769cee1aSJeremy Kerr 	rc = 0;
631769cee1aSJeremy Kerr 
632d831f960SJeremy Kerr 	for (;;) {
633d831f960SJeremy Kerr 		uint8_t buf[4096];
634d831f960SJeremy Kerr 
6351764145dSJeremy Kerr 		BUILD_ASSERT(sizeof(buf) <= buffer_size);
6361764145dSJeremy Kerr 
637769cee1aSJeremy Kerr 		if (sigint) {
638769cee1aSJeremy Kerr 			fprintf(stderr, "Received interrupt, exiting\n");
639769cee1aSJeremy Kerr 			break;
640769cee1aSJeremy Kerr 		}
641769cee1aSJeremy Kerr 
6421cecc5deSJohnathan Mantey 		rc = get_current_time(&tv);
6431cecc5deSJohnathan Mantey 		if (rc) {
6441cecc5deSJohnathan Mantey 			warn("Failed to read current time");
6451cecc5deSJohnathan Mantey 			break;
6461cecc5deSJohnathan Mantey 		}
6471cecc5deSJohnathan Mantey 
6481cecc5deSJohnathan Mantey 		timeout = get_poll_timeout(console, &tv);
6491cecc5deSJohnathan Mantey 
650329a35f5SJeremy Kerr 		rc = poll(console->pollfds,
6515c359cc6SAndrew Jeffery 			  console->n_pollers + MAX_INTERNAL_POLLFD,
6525c359cc6SAndrew Jeffery 			  (int)timeout);
6531cecc5deSJohnathan Mantey 
654d831f960SJeremy Kerr 		if (rc < 0) {
655769cee1aSJeremy Kerr 			if (errno == EINTR) {
656769cee1aSJeremy Kerr 				continue;
6570b7b0477SAndrew Jeffery 			}
658d831f960SJeremy Kerr 			warn("poll error");
659769cee1aSJeremy Kerr 			break;
660769cee1aSJeremy Kerr 		}
661d831f960SJeremy Kerr 
662329a35f5SJeremy Kerr 		/* process internal fd first */
663329a35f5SJeremy Kerr 		if (console->pollfds[console->n_pollers].revents) {
6641a0e03b4SJeremy Kerr 			rc = read(console->tty_fd, buf, sizeof(buf));
665d831f960SJeremy Kerr 			if (rc <= 0) {
666d831f960SJeremy Kerr 				warn("Error reading from tty device");
667769cee1aSJeremy Kerr 				rc = -1;
668769cee1aSJeremy Kerr 				break;
669d831f960SJeremy Kerr 			}
670f733c85aSJeremy Kerr 			rc = ringbuffer_queue(console->rb, buf, rc);
6712834c5b1SAndrew Jeffery 			if (rc) {
672769cee1aSJeremy Kerr 				break;
673d831f960SJeremy Kerr 			}
6742834c5b1SAndrew Jeffery 		}
675d831f960SJeremy Kerr 
676f9c8f6caSCheng C Yang 		if (console->pollfds[console->n_pollers + 1].revents) {
677f9c8f6caSCheng C Yang 			sd_bus_process(console->bus, NULL);
678f9c8f6caSCheng C Yang 		}
679f9c8f6caSCheng C Yang 
680329a35f5SJeremy Kerr 		/* ... and then the pollers */
6811cecc5deSJohnathan Mantey 		rc = call_pollers(console, &tv);
6822834c5b1SAndrew Jeffery 		if (rc) {
683769cee1aSJeremy Kerr 			break;
6841a0e03b4SJeremy Kerr 		}
6852834c5b1SAndrew Jeffery 	}
686769cee1aSJeremy Kerr 
687769cee1aSJeremy Kerr 	signal(SIGINT, sighandler_save);
688f9c8f6caSCheng C Yang 	sd_bus_unref(console->bus);
689769cee1aSJeremy Kerr 
690769cee1aSJeremy Kerr 	return rc ? -1 : 0;
6911a0e03b4SJeremy Kerr }
692d831f960SJeremy Kerr static const struct option options[] = {
693d66195c1SJeremy Kerr 	{ "config", required_argument, 0, 'c' },
694f5858b5bSJoel Stanley 	{ 0, 0, 0, 0 },
695d831f960SJeremy Kerr };
696d831f960SJeremy Kerr 
697d831f960SJeremy Kerr int main(int argc, char **argv)
698d831f960SJeremy Kerr {
699d66195c1SJeremy Kerr 	const char *config_filename = NULL;
7006221ce94SVishwanatha Subbanna 	const char *config_tty_kname = NULL;
7011a0e03b4SJeremy Kerr 	struct console *console;
702d66195c1SJeremy Kerr 	struct config *config;
703d66195c1SJeremy Kerr 	int rc;
704d831f960SJeremy Kerr 
705d831f960SJeremy Kerr 	for (;;) {
706b70f8713SAndrew Jeffery 		int c;
707b70f8713SAndrew Jeffery 		int idx;
708d831f960SJeremy Kerr 
709d66195c1SJeremy Kerr 		c = getopt_long(argc, argv, "c:", options, &idx);
7102834c5b1SAndrew Jeffery 		if (c == -1) {
711d831f960SJeremy Kerr 			break;
7122834c5b1SAndrew Jeffery 		}
713d831f960SJeremy Kerr 
714d831f960SJeremy Kerr 		switch (c) {
715d66195c1SJeremy Kerr 		case 'c':
716d66195c1SJeremy Kerr 			config_filename = optarg;
717d831f960SJeremy Kerr 			break;
718d831f960SJeremy Kerr 		case 'h':
719d831f960SJeremy Kerr 		case '?':
720d831f960SJeremy Kerr 			usage(argv[0]);
721d66195c1SJeremy Kerr 			return EXIT_SUCCESS;
722d831f960SJeremy Kerr 		}
723d831f960SJeremy Kerr 	}
724d831f960SJeremy Kerr 
7252834c5b1SAndrew Jeffery 	if (optind < argc) {
7266221ce94SVishwanatha Subbanna 		config_tty_kname = argv[optind];
7272834c5b1SAndrew Jeffery 	}
7286221ce94SVishwanatha Subbanna 
729d66195c1SJeremy Kerr 	console = malloc(sizeof(struct console));
730d66195c1SJeremy Kerr 	memset(console, 0, sizeof(*console));
731a72711afSAndrew Jeffery 	console->pollfds =
732a72711afSAndrew Jeffery 		calloc(MAX_INTERNAL_POLLFD, sizeof(*console->pollfds));
733f733c85aSJeremy Kerr 	console->rb = ringbuffer_init(buffer_size);
734329a35f5SJeremy Kerr 
735d66195c1SJeremy Kerr 	config = config_init(config_filename);
736d66195c1SJeremy Kerr 	if (!config) {
737d66195c1SJeremy Kerr 		warnx("Can't read configuration, exiting.");
738*29c59c44SAndrew Jeffery 		rc = -1;
739d66195c1SJeremy Kerr 		goto out_free;
740d831f960SJeremy Kerr 	}
741d831f960SJeremy Kerr 
7422834c5b1SAndrew Jeffery 	if (!config_tty_kname) {
74391dde14eSAndrew Jeffery 		config_tty_kname = config_get_value(config, "upstream-tty");
7442834c5b1SAndrew Jeffery 	}
74591dde14eSAndrew Jeffery 
74691dde14eSAndrew Jeffery 	if (!config_tty_kname) {
74791dde14eSAndrew Jeffery 		warnx("No TTY device specified");
74891dde14eSAndrew Jeffery 		usage(argv[0]);
749*29c59c44SAndrew Jeffery 		rc = -1;
750*29c59c44SAndrew Jeffery 		goto out_config_fini;
75191dde14eSAndrew Jeffery 	}
75291dde14eSAndrew Jeffery 
753b14ca19cSNinad Palsule 	if (set_socket_info(console, config)) {
754*29c59c44SAndrew Jeffery 		rc = -1;
755*29c59c44SAndrew Jeffery 		goto out_config_fini;
756b14ca19cSNinad Palsule 	}
757b14ca19cSNinad Palsule 
7586221ce94SVishwanatha Subbanna 	console->tty_kname = config_tty_kname;
7596221ce94SVishwanatha Subbanna 
760d66195c1SJeremy Kerr 	rc = tty_init(console, config);
7612834c5b1SAndrew Jeffery 	if (rc) {
762d66195c1SJeremy Kerr 		goto out_config_fini;
7632834c5b1SAndrew Jeffery 	}
764d831f960SJeremy Kerr 
765f9c8f6caSCheng C Yang 	dbus_init(console, config);
766f9c8f6caSCheng C Yang 
767d47963e5SJeremy Kerr 	handlers_init(console, config);
768d831f960SJeremy Kerr 
7691a0e03b4SJeremy Kerr 	rc = run_console(console);
770d831f960SJeremy Kerr 
7711a0e03b4SJeremy Kerr 	handlers_fini(console);
772d831f960SJeremy Kerr 
773d66195c1SJeremy Kerr out_config_fini:
774d66195c1SJeremy Kerr 	config_fini(config);
775d66195c1SJeremy Kerr 
776957818b4SJeremy Kerr out_free:
77789ea8198SJeremy Kerr 	free(console->pollers);
77889ea8198SJeremy Kerr 	free(console->pollfds);
7791a0e03b4SJeremy Kerr 	free(console->tty_sysfs_devnode);
7801a0e03b4SJeremy Kerr 	free(console->tty_dev);
7811a0e03b4SJeremy Kerr 	free(console);
782d831f960SJeremy Kerr 
783d831f960SJeremy Kerr 	return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
784d831f960SJeremy Kerr }
785