xref: /openbmc/obmc-console/console-server.c (revision 15792aa711c573c5f2c252b7c7eb7e7fa523909d)
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 
1917217845SJeremy Kerr #define _GNU_SOURCE
2017217845SJeremy Kerr 
21329a35f5SJeremy Kerr #include <assert.h>
22769cee1aSJeremy Kerr #include <errno.h>
23769cee1aSJeremy Kerr #include <signal.h>
24d831f960SJeremy Kerr #include <stdint.h>
25d831f960SJeremy Kerr #include <stdbool.h>
26d831f960SJeremy Kerr #include <stdlib.h>
27d831f960SJeremy Kerr #include <stdio.h>
28d831f960SJeremy Kerr #include <fcntl.h>
29d831f960SJeremy Kerr #include <unistd.h>
30d831f960SJeremy Kerr #include <err.h>
31d831f960SJeremy Kerr #include <string.h>
32d831f960SJeremy Kerr #include <getopt.h>
3317217845SJeremy Kerr #include <limits.h>
341cecc5deSJohnathan Mantey #include <time.h>
3554e9569dSJeremy Kerr #include <termios.h>
36d831f960SJeremy Kerr 
37d831f960SJeremy Kerr #include <sys/types.h>
381cecc5deSJohnathan Mantey #include <sys/time.h>
3987e344cdSJoel Stanley #include <poll.h>
40d831f960SJeremy Kerr 
411a0e03b4SJeremy Kerr #include "console-server.h"
42d831f960SJeremy Kerr 
43f9c8f6caSCheng C Yang #define DBUS_ERR "org.openbmc.error"
44f9c8f6caSCheng C Yang #define DBUS_NAME "xyz.openbmc_project.console"
45f9c8f6caSCheng C Yang #define OBJ_NAME "/xyz/openbmc_project/console"
46f9c8f6caSCheng C Yang 
471a0e03b4SJeremy Kerr struct console {
4817217845SJeremy Kerr 	const char	*tty_kname;
4917217845SJeremy Kerr 	char		*tty_sysfs_devnode;
5017217845SJeremy Kerr 	char		*tty_dev;
51957818b4SJeremy Kerr 	int		tty_sirq;
52957818b4SJeremy Kerr 	int		tty_lpc_addr;
53c7fbcd48SBenjamin Fair 	speed_t		tty_baud;
54d831f960SJeremy Kerr 	int		tty_fd;
55329a35f5SJeremy Kerr 
56f733c85aSJeremy Kerr 	struct ringbuffer	*rb;
57f733c85aSJeremy Kerr 
581a0e03b4SJeremy Kerr 	struct handler	**handlers;
591a0e03b4SJeremy Kerr 	int		n_handlers;
60329a35f5SJeremy Kerr 
61329a35f5SJeremy Kerr 	struct poller	**pollers;
62329a35f5SJeremy Kerr 	int		n_pollers;
63329a35f5SJeremy Kerr 
64329a35f5SJeremy Kerr 	struct pollfd	*pollfds;
65f9c8f6caSCheng C Yang 	struct sd_bus	*bus;
66d831f960SJeremy Kerr };
67d831f960SJeremy Kerr 
68329a35f5SJeremy Kerr struct poller {
69329a35f5SJeremy Kerr 	struct handler	*handler;
70329a35f5SJeremy Kerr 	void		*data;
711cecc5deSJohnathan Mantey 	poller_event_fn_t	event_fn;
721cecc5deSJohnathan Mantey 	poller_timeout_fn_t	timeout_fn;
731cecc5deSJohnathan Mantey 	struct timeval	timeout;
74329a35f5SJeremy Kerr 	bool		remove;
75329a35f5SJeremy Kerr };
76329a35f5SJeremy Kerr 
77f9c8f6caSCheng C Yang /* we have two extra entry in the pollfds array for the VUART tty */
78f9c8f6caSCheng C Yang enum internal_pollfds {
79f9c8f6caSCheng C Yang 	POLLFD_HOSTTTY = 0,
80f9c8f6caSCheng C Yang 	POLLFD_DBUS = 1,
81f9c8f6caSCheng C Yang 	MAX_INTERNAL_POLLFD = 2,
82f9c8f6caSCheng C Yang };
83329a35f5SJeremy Kerr 
84f733c85aSJeremy Kerr /* size of the shared backlog ringbuffer */
85f733c85aSJeremy Kerr const size_t buffer_size = 128 * 1024;
86f733c85aSJeremy Kerr 
87769cee1aSJeremy Kerr /* state shared with the signal handler */
88769cee1aSJeremy Kerr static bool sigint;
89329a35f5SJeremy Kerr 
90d831f960SJeremy Kerr static void usage(const char *progname)
91d831f960SJeremy Kerr {
92d831f960SJeremy Kerr 	fprintf(stderr,
936221ce94SVishwanatha Subbanna "usage: %s [options] <DEVICE>\n"
94d831f960SJeremy Kerr "\n"
95d831f960SJeremy Kerr "Options:\n"
96d66195c1SJeremy Kerr "  --config <FILE>  Use FILE for configuration\n"
97d831f960SJeremy Kerr "",
98d831f960SJeremy Kerr 		progname);
99d831f960SJeremy Kerr }
100d831f960SJeremy Kerr 
10117217845SJeremy Kerr /* populates tty_dev and tty_sysfs_devnode, using the tty kernel name */
1021a0e03b4SJeremy Kerr static int tty_find_device(struct console *console)
10317217845SJeremy Kerr {
10417217845SJeremy Kerr 	char *tty_class_device_link;
10517217845SJeremy Kerr 	char *tty_device_tty_dir;
10617217845SJeremy Kerr 	char *tty_device_reldir;
10745ad7676SYi Li 	char *tty_path_input;
10845ad7676SYi Li 	char *tty_path_input_real;
10945ad7676SYi Li 	char *tty_kname_real;
11017217845SJeremy Kerr 	int rc;
11117217845SJeremy Kerr 
11217217845SJeremy Kerr 	tty_class_device_link = NULL;
11317217845SJeremy Kerr 	tty_device_tty_dir = NULL;
11417217845SJeremy Kerr 	tty_device_reldir = NULL;
11545ad7676SYi Li 	tty_path_input = NULL;
11645ad7676SYi Li 	tty_path_input_real = NULL;
11745ad7676SYi Li 	tty_kname_real = NULL;
11817217845SJeremy Kerr 
11945ad7676SYi Li 	/* udev may rename the tty name with a symbol link, try to resolve */
12045ad7676SYi Li 	rc = asprintf(&tty_path_input, "/dev/%s", console->tty_kname);
12117217845SJeremy Kerr 	if (rc < 0)
12217217845SJeremy Kerr 		return -1;
12317217845SJeremy Kerr 
12445ad7676SYi Li 	tty_path_input_real = realpath(tty_path_input, NULL);
12545ad7676SYi Li 	if (!tty_path_input_real) {
12645ad7676SYi Li 		warn("Can't find realpath for /dev/%s", console->tty_kname);
127*15792aa7SAndrew Jeffery 		rc = -1;
12845ad7676SYi Li 		goto out_free;
12945ad7676SYi Li 	}
13045ad7676SYi Li 
13145ad7676SYi Li 	tty_kname_real = basename(tty_path_input_real);
13245ad7676SYi Li 	if (!tty_kname_real) {
13345ad7676SYi Li 		warn("Can't find real name for /dev/%s", console->tty_kname);
134*15792aa7SAndrew Jeffery 		rc = -1;
13545ad7676SYi Li 		goto out_free;
13645ad7676SYi Li 	}
13745ad7676SYi Li 
13845ad7676SYi Li 	rc = asprintf(&tty_class_device_link,
13945ad7676SYi Li 			"/sys/class/tty/%s", tty_kname_real);
14045ad7676SYi Li 	if (rc < 0)
14145ad7676SYi Li 		goto out_free;
14245ad7676SYi Li 
14317217845SJeremy Kerr 	tty_device_tty_dir = realpath(tty_class_device_link, NULL);
14445ad7676SYi Li 	if (!tty_device_tty_dir) {
14545ad7676SYi Li 		warn("Can't query sysfs for device %s", tty_kname_real);
146*15792aa7SAndrew Jeffery 		rc = -1;
14717217845SJeremy Kerr 		goto out_free;
14817217845SJeremy Kerr 	}
14917217845SJeremy Kerr 
15017217845SJeremy Kerr 	rc = asprintf(&tty_device_reldir, "%s/../../", tty_device_tty_dir);
15117217845SJeremy Kerr 	if (rc < 0)
15217217845SJeremy Kerr 		goto out_free;
15317217845SJeremy Kerr 
1541a0e03b4SJeremy Kerr 	console->tty_sysfs_devnode = realpath(tty_device_reldir, NULL);
1551a0e03b4SJeremy Kerr 	if (!console->tty_sysfs_devnode)
15645ad7676SYi Li 		warn("Can't find parent device for %s", tty_kname_real);
15717217845SJeremy Kerr 
15845ad7676SYi Li 	rc = asprintf(&console->tty_dev, "/dev/%s", tty_kname_real);
15917217845SJeremy Kerr 	if (rc < 0)
16017217845SJeremy Kerr 		goto out_free;
16117217845SJeremy Kerr 
16217217845SJeremy Kerr 	rc = 0;
16317217845SJeremy Kerr 
16417217845SJeremy Kerr out_free:
16517217845SJeremy Kerr 	free(tty_class_device_link);
16617217845SJeremy Kerr 	free(tty_device_tty_dir);
16717217845SJeremy Kerr 	free(tty_device_reldir);
16845ad7676SYi Li 	free(tty_path_input);
16945ad7676SYi Li 	free(tty_path_input_real);
17017217845SJeremy Kerr 	return rc;
17117217845SJeremy Kerr }
17217217845SJeremy Kerr 
1731a0e03b4SJeremy Kerr static int tty_set_sysfs_attr(struct console *console, const char *name,
174957818b4SJeremy Kerr 		int value)
175957818b4SJeremy Kerr {
176957818b4SJeremy Kerr 	char *path;
177957818b4SJeremy Kerr 	FILE *fp;
178957818b4SJeremy Kerr 	int rc;
179957818b4SJeremy Kerr 
1801a0e03b4SJeremy Kerr 	rc = asprintf(&path, "%s/%s", console->tty_sysfs_devnode, name);
181957818b4SJeremy Kerr 	if (rc < 0)
182957818b4SJeremy Kerr 		return -1;
183957818b4SJeremy Kerr 
184957818b4SJeremy Kerr 	fp = fopen(path, "w");
185957818b4SJeremy Kerr 	if (!fp) {
186957818b4SJeremy Kerr 		warn("Can't access attribute %s on device %s",
1871a0e03b4SJeremy Kerr 				name, console->tty_kname);
188957818b4SJeremy Kerr 		rc = -1;
189957818b4SJeremy Kerr 		goto out_free;
190957818b4SJeremy Kerr 	}
191957818b4SJeremy Kerr 	setvbuf(fp, NULL, _IONBF, 0);
192957818b4SJeremy Kerr 
193957818b4SJeremy Kerr 	rc = fprintf(fp, "0x%x", value);
194957818b4SJeremy Kerr 	if (rc < 0)
195957818b4SJeremy Kerr 		warn("Error writing to %s attribute of device %s",
1961a0e03b4SJeremy Kerr 				name, console->tty_kname);
197957818b4SJeremy Kerr 	fclose(fp);
198957818b4SJeremy Kerr 
199957818b4SJeremy Kerr 
200957818b4SJeremy Kerr 
201957818b4SJeremy Kerr out_free:
202957818b4SJeremy Kerr 	free(path);
203957818b4SJeremy Kerr 	return rc;
204957818b4SJeremy Kerr }
205957818b4SJeremy Kerr 
206d831f960SJeremy Kerr /**
207c7fbcd48SBenjamin Fair  * Set termios attributes on the console tty.
20854e9569dSJeremy Kerr  */
20954e9569dSJeremy Kerr static void tty_init_termios(struct console *console)
21054e9569dSJeremy Kerr {
21154e9569dSJeremy Kerr 	struct termios termios;
21254e9569dSJeremy Kerr 	int rc;
21354e9569dSJeremy Kerr 
21454e9569dSJeremy Kerr 	rc = tcgetattr(console->tty_fd, &termios);
21554e9569dSJeremy Kerr 	if (rc) {
21654e9569dSJeremy Kerr 		warn("Can't read tty termios");
21754e9569dSJeremy Kerr 		return;
21854e9569dSJeremy Kerr 	}
21954e9569dSJeremy Kerr 
220c7fbcd48SBenjamin Fair 	if (console->tty_baud) {
221c7fbcd48SBenjamin Fair 		if (cfsetspeed(&termios, console->tty_baud) < 0)
222c7fbcd48SBenjamin Fair 			warn("Couldn't set speeds for %s", console->tty_kname);
223c7fbcd48SBenjamin Fair 	}
224c7fbcd48SBenjamin Fair 
225c7fbcd48SBenjamin Fair 	/* Set console to raw mode: we don't want any processing to occur on
226c7fbcd48SBenjamin Fair 	 * the underlying terminal input/output.
227c7fbcd48SBenjamin Fair 	 */
22854e9569dSJeremy Kerr 	cfmakeraw(&termios);
229c7fbcd48SBenjamin Fair 
23054e9569dSJeremy Kerr 	rc = tcsetattr(console->tty_fd, TCSANOW, &termios);
23154e9569dSJeremy Kerr 	if (rc)
232c7fbcd48SBenjamin Fair 		warn("Can't set terminal options for %s", console->tty_kname);
23354e9569dSJeremy Kerr }
23454e9569dSJeremy Kerr 
235f9c8f6caSCheng C Yang 
236f9c8f6caSCheng C Yang static void tty_change_baudrate(struct console *console)
237f9c8f6caSCheng C Yang {
238f9c8f6caSCheng C Yang 	struct handler *handler;
239f9c8f6caSCheng C Yang 	int i, rc;
240f9c8f6caSCheng C Yang 
241f9c8f6caSCheng C Yang 	tty_init_termios(console);
242f9c8f6caSCheng C Yang 
243f9c8f6caSCheng C Yang 	for (i = 0; i < console->n_handlers; i++) {
244f9c8f6caSCheng C Yang 		handler = console->handlers[i];
245f9c8f6caSCheng C Yang 		if (!handler->baudrate)
246f9c8f6caSCheng C Yang 			continue;
247f9c8f6caSCheng C Yang 
248f9c8f6caSCheng C Yang 		rc = handler->baudrate(handler, console->tty_baud);
249f9c8f6caSCheng C Yang 		if (rc)
250f9c8f6caSCheng C Yang 			warnx("Can't set terminal baudrate for handler %s",
251f9c8f6caSCheng C Yang 			     handler->name);
252f9c8f6caSCheng C Yang 	}
253f9c8f6caSCheng C Yang }
254f9c8f6caSCheng C Yang 
25554e9569dSJeremy Kerr /**
256d831f960SJeremy Kerr  * Open and initialise the serial device
257d831f960SJeremy Kerr  */
2581a0e03b4SJeremy Kerr static int tty_init_io(struct console *console)
259d831f960SJeremy Kerr {
2601a0e03b4SJeremy Kerr 	if (console->tty_sirq)
2611a0e03b4SJeremy Kerr 		tty_set_sysfs_attr(console, "sirq", console->tty_sirq);
2621a0e03b4SJeremy Kerr 	if (console->tty_lpc_addr)
2631a0e03b4SJeremy Kerr 		tty_set_sysfs_attr(console, "lpc_address",
2641a0e03b4SJeremy Kerr 				console->tty_lpc_addr);
265957818b4SJeremy Kerr 
2661a0e03b4SJeremy Kerr 	console->tty_fd = open(console->tty_dev, O_RDWR);
2671a0e03b4SJeremy Kerr 	if (console->tty_fd <= 0) {
2681a0e03b4SJeremy Kerr 		warn("Can't open tty %s", console->tty_dev);
269d831f960SJeremy Kerr 		return -1;
270d831f960SJeremy Kerr 	}
271d831f960SJeremy Kerr 
272d831f960SJeremy Kerr 	/* Disable character delay. We may want to later enable this when
273d831f960SJeremy Kerr 	 * we detect larger amounts of data
274d831f960SJeremy Kerr 	 */
2751a0e03b4SJeremy Kerr 	fcntl(console->tty_fd, F_SETFL, FNDELAY);
276d831f960SJeremy Kerr 
27754e9569dSJeremy Kerr 	tty_init_termios(console);
27854e9569dSJeremy Kerr 
279329a35f5SJeremy Kerr 	console->pollfds[console->n_pollers].fd = console->tty_fd;
280329a35f5SJeremy Kerr 	console->pollfds[console->n_pollers].events = POLLIN;
281329a35f5SJeremy Kerr 
282d831f960SJeremy Kerr 	return 0;
283d831f960SJeremy Kerr }
284d831f960SJeremy Kerr 
285d66195c1SJeremy Kerr static int tty_init(struct console *console, struct config *config)
286d66195c1SJeremy Kerr {
287d66195c1SJeremy Kerr 	const char *val;
288d66195c1SJeremy Kerr 	char *endp;
289d66195c1SJeremy Kerr 	int rc;
290d66195c1SJeremy Kerr 
291d66195c1SJeremy Kerr 	val = config_get_value(config, "lpc-address");
292d66195c1SJeremy Kerr 	if (val) {
293d66195c1SJeremy Kerr 		console->tty_lpc_addr = strtoul(val, &endp, 0);
294d66195c1SJeremy Kerr 		if (endp == optarg) {
295d66195c1SJeremy Kerr 			warn("Invalid LPC address: '%s'", val);
296d66195c1SJeremy Kerr 			return -1;
297d66195c1SJeremy Kerr 		}
298d66195c1SJeremy Kerr 	}
299d66195c1SJeremy Kerr 
300d66195c1SJeremy Kerr 	val = config_get_value(config, "sirq");
301d66195c1SJeremy Kerr 	if (val) {
302d66195c1SJeremy Kerr 		console->tty_sirq = strtoul(val, &endp, 0);
303d66195c1SJeremy Kerr 		if (endp == optarg)
304d66195c1SJeremy Kerr 			warn("Invalid sirq: '%s'", val);
305d66195c1SJeremy Kerr 	}
306d66195c1SJeremy Kerr 
307c7fbcd48SBenjamin Fair 	val = config_get_value(config, "baud");
308c7fbcd48SBenjamin Fair 	if (val) {
309c7fbcd48SBenjamin Fair 		if (config_parse_baud(&console->tty_baud, val))
310c7fbcd48SBenjamin Fair 			warnx("Invalid baud rate: '%s'", val);
311c7fbcd48SBenjamin Fair 	}
312c7fbcd48SBenjamin Fair 
313d66195c1SJeremy Kerr 	if (!console->tty_kname) {
314d66195c1SJeremy Kerr 		warnx("Error: No TTY device specified");
315d66195c1SJeremy Kerr 		return -1;
316d66195c1SJeremy Kerr 	}
317d66195c1SJeremy Kerr 
318d66195c1SJeremy Kerr 	rc = tty_find_device(console);
319d66195c1SJeremy Kerr 	if (rc)
320d66195c1SJeremy Kerr 		return rc;
321d66195c1SJeremy Kerr 
322d66195c1SJeremy Kerr 	rc = tty_init_io(console);
323d66195c1SJeremy Kerr 	return rc;
324d66195c1SJeremy Kerr }
325d66195c1SJeremy Kerr 
3261a0e03b4SJeremy Kerr 
3271a0e03b4SJeremy Kerr int console_data_out(struct console *console, const uint8_t *data, size_t len)
328d831f960SJeremy Kerr {
3291a0e03b4SJeremy Kerr 	return write_buf_to_fd(console->tty_fd, data, len);
330d831f960SJeremy Kerr }
331d831f960SJeremy Kerr 
332f9c8f6caSCheng C Yang static int method_set_baud_rate(sd_bus_message *msg, void *userdata,
333f9c8f6caSCheng C Yang 			 sd_bus_error *err)
334f9c8f6caSCheng C Yang {
335f9c8f6caSCheng C Yang 	struct console *console = userdata;
336f9c8f6caSCheng C Yang 	uint32_t baudrate;
337f9c8f6caSCheng C Yang 	speed_t speed;
338f9c8f6caSCheng C Yang 	int r;
339f9c8f6caSCheng C Yang 
340f9c8f6caSCheng C Yang 	if (!console) {
341f9c8f6caSCheng C Yang 		sd_bus_error_set_const(err, DBUS_ERR, "Internal error");
342f9c8f6caSCheng C Yang 		return sd_bus_reply_method_return(msg, "x", 0);
343f9c8f6caSCheng C Yang 	}
344f9c8f6caSCheng C Yang 
345f9c8f6caSCheng C Yang 	r = sd_bus_message_read(msg, "u", &baudrate);
346f9c8f6caSCheng C Yang 	if (r < 0) {
347f9c8f6caSCheng C Yang 		sd_bus_error_set_const(err, DBUS_ERR, "Bad message");
348f9c8f6caSCheng C Yang 		return sd_bus_reply_method_return(msg, "x", -EINVAL);
349f9c8f6caSCheng C Yang 	}
350f9c8f6caSCheng C Yang 
351f9c8f6caSCheng C Yang 	speed = parse_int_to_baud(baudrate);
352f9c8f6caSCheng C Yang 	if (!speed) {
353f9c8f6caSCheng C Yang 		warnx("Invalid baud rate: '%u'", baudrate);
354f9c8f6caSCheng C Yang 		return sd_bus_reply_method_return(msg, "x", -EINVAL);
355f9c8f6caSCheng C Yang 	}
356f9c8f6caSCheng C Yang 
357f9c8f6caSCheng C Yang 	console->tty_baud = speed;
358f9c8f6caSCheng C Yang 	tty_change_baudrate(console);
359f9c8f6caSCheng C Yang 
360f9c8f6caSCheng C Yang 	return sd_bus_reply_method_return(msg, "x", r);
361f9c8f6caSCheng C Yang }
362f9c8f6caSCheng C Yang 
363fd048328SAndrew Jeffery static int get_handler(sd_bus *bus __attribute__((unused)),
364fd048328SAndrew Jeffery 		       const char *path __attribute__((unused)),
365fd048328SAndrew Jeffery 		       const char *interface __attribute__((unused)),
366fd048328SAndrew Jeffery 		       const char *property __attribute__((unused)),
367fd048328SAndrew Jeffery 		       sd_bus_message *reply, void *userdata,
368fd048328SAndrew Jeffery 		       sd_bus_error *error __attribute__((unused))) {
369f9c8f6caSCheng C Yang 	struct console *console = userdata;
370f9c8f6caSCheng C Yang 	uint32_t baudrate;
371f9c8f6caSCheng C Yang 	int r;
372f9c8f6caSCheng C Yang 
373f9c8f6caSCheng C Yang 	baudrate = parse_baud_to_int(console->tty_baud);
374f9c8f6caSCheng C Yang 	if (!baudrate)
375f9c8f6caSCheng C Yang 		warnx("Invalid baud rate: '%d'", console->tty_baud);
376f9c8f6caSCheng C Yang 
377f9c8f6caSCheng C Yang 	r = sd_bus_message_append(reply, "u", baudrate);
378f9c8f6caSCheng C Yang 
379f9c8f6caSCheng C Yang 	return r;
380f9c8f6caSCheng C Yang }
381f9c8f6caSCheng C Yang 
382f9c8f6caSCheng C Yang static const sd_bus_vtable console_vtable[] = {
383f9c8f6caSCheng C Yang 	SD_BUS_VTABLE_START(0),
384f9c8f6caSCheng C Yang 	SD_BUS_METHOD("setBaudRate", "u", "x", method_set_baud_rate,
385f9c8f6caSCheng C Yang 		      SD_BUS_VTABLE_UNPRIVILEGED),
386f9c8f6caSCheng C Yang 	SD_BUS_PROPERTY("baudrate", "u", get_handler, 0, 0),
387f9c8f6caSCheng C Yang 	SD_BUS_VTABLE_END,};
388f9c8f6caSCheng C Yang 
389fd048328SAndrew Jeffery static void dbus_init(struct console *console, struct config *config __attribute__((unused)))
390f9c8f6caSCheng C Yang {
391f9c8f6caSCheng C Yang 	int dbus_poller = 0;
392f9c8f6caSCheng C Yang 	int fd, r;
393f9c8f6caSCheng C Yang 
394f9c8f6caSCheng C Yang 	if (!console) {
395f9c8f6caSCheng C Yang 		warnx("Couldn't get valid console");
396f9c8f6caSCheng C Yang 		return;
397f9c8f6caSCheng C Yang 	}
398f9c8f6caSCheng C Yang 
399f9c8f6caSCheng C Yang 	r = sd_bus_default_system(&console->bus);
400f9c8f6caSCheng C Yang 	if (r < 0) {
401f9c8f6caSCheng C Yang 		warnx("Failed to connect to system bus: %s", strerror(-r));
402f9c8f6caSCheng C Yang 		return;
403f9c8f6caSCheng C Yang 	}
404f9c8f6caSCheng C Yang 
405f9c8f6caSCheng C Yang 	r = sd_bus_add_object_vtable(console->bus, NULL, OBJ_NAME, DBUS_NAME,
406f9c8f6caSCheng C Yang 				     console_vtable, console);
407f9c8f6caSCheng C Yang 	if (r < 0) {
408f9c8f6caSCheng C Yang 		warnx("Failed to issue method call: %s", strerror(-r));
409f9c8f6caSCheng C Yang 		return;
410f9c8f6caSCheng C Yang 	}
411f9c8f6caSCheng C Yang 
412f9c8f6caSCheng C Yang 	r = sd_bus_request_name(console->bus, DBUS_NAME, SD_BUS_NAME_ALLOW_REPLACEMENT
413f9c8f6caSCheng C Yang 				|SD_BUS_NAME_REPLACE_EXISTING);
414f9c8f6caSCheng C Yang 	if (r < 0) {
415f9c8f6caSCheng C Yang 		warnx("Failed to acquire service name: %s", strerror(-r));
416f9c8f6caSCheng C Yang 		return;
417f9c8f6caSCheng C Yang 	}
418f9c8f6caSCheng C Yang 
419f9c8f6caSCheng C Yang 	fd = sd_bus_get_fd(console->bus);
420f9c8f6caSCheng C Yang 	if (fd < 0) {
421f9c8f6caSCheng C Yang 		warnx("Couldn't get the bus file descriptor");
422f9c8f6caSCheng C Yang 		return;
423f9c8f6caSCheng C Yang 	}
424f9c8f6caSCheng C Yang 
425f9c8f6caSCheng C Yang 	dbus_poller = POLLFD_DBUS;
426f9c8f6caSCheng C Yang 
427f9c8f6caSCheng C Yang 	console->pollfds[dbus_poller].fd = fd;
428f9c8f6caSCheng C Yang 	console->pollfds[dbus_poller].events = POLLIN;
429f9c8f6caSCheng C Yang }
430f9c8f6caSCheng C Yang 
431d47963e5SJeremy Kerr static void handlers_init(struct console *console, struct config *config)
432d831f960SJeremy Kerr {
4331a0e03b4SJeremy Kerr 	extern struct handler *__start_handlers, *__stop_handlers;
4341a0e03b4SJeremy Kerr 	struct handler *handler;
435021b91f0SJeremy Kerr 	int i, rc;
436d831f960SJeremy Kerr 
4371a0e03b4SJeremy Kerr 	console->n_handlers = &__stop_handlers - &__start_handlers;
4381a0e03b4SJeremy Kerr 	console->handlers = &__start_handlers;
439d831f960SJeremy Kerr 
4401a0e03b4SJeremy Kerr 	printf("%d handler%s\n", console->n_handlers,
4411a0e03b4SJeremy Kerr 			console->n_handlers == 1 ? "" : "s");
442d831f960SJeremy Kerr 
4431a0e03b4SJeremy Kerr 	for (i = 0; i < console->n_handlers; i++) {
4441a0e03b4SJeremy Kerr 		handler = console->handlers[i];
4451a0e03b4SJeremy Kerr 
446021b91f0SJeremy Kerr 		rc = 0;
4471a0e03b4SJeremy Kerr 		if (handler->init)
448021b91f0SJeremy Kerr 			rc = handler->init(handler, console, config);
449021b91f0SJeremy Kerr 
450021b91f0SJeremy Kerr 		handler->active = rc == 0;
451021b91f0SJeremy Kerr 
452021b91f0SJeremy Kerr 		printf("  %s [%sactive]\n", handler->name,
453021b91f0SJeremy Kerr 				handler->active ? "" : "in");
454d831f960SJeremy Kerr 	}
455d831f960SJeremy Kerr }
456d831f960SJeremy Kerr 
4571a0e03b4SJeremy Kerr static void handlers_fini(struct console *console)
458d831f960SJeremy Kerr {
4591a0e03b4SJeremy Kerr 	struct handler *handler;
4601a0e03b4SJeremy Kerr 	int i;
4611a0e03b4SJeremy Kerr 
4621a0e03b4SJeremy Kerr 	for (i = 0; i < console->n_handlers; i++) {
4631a0e03b4SJeremy Kerr 		handler = console->handlers[i];
464021b91f0SJeremy Kerr 		if (handler->fini && handler->active)
4651a0e03b4SJeremy Kerr 			handler->fini(handler);
4661a0e03b4SJeremy Kerr 	}
467d831f960SJeremy Kerr }
468d831f960SJeremy Kerr 
4691cecc5deSJohnathan Mantey static int get_current_time(struct timeval *tv)
4701cecc5deSJohnathan Mantey {
4711cecc5deSJohnathan Mantey 	struct timespec t;
4721cecc5deSJohnathan Mantey 	int rc;
4731cecc5deSJohnathan Mantey 
4741cecc5deSJohnathan Mantey 	/*
4751cecc5deSJohnathan Mantey 	 * We use clock_gettime(CLOCK_MONOTONIC) so we're immune to
4761cecc5deSJohnathan Mantey 	 * local time changes. However, a struct timeval is more
4771cecc5deSJohnathan Mantey 	 * convenient for calculations, so convert to that.
4781cecc5deSJohnathan Mantey 	 */
4791cecc5deSJohnathan Mantey 	rc = clock_gettime(CLOCK_MONOTONIC, &t);
4801cecc5deSJohnathan Mantey 	if (rc)
4811cecc5deSJohnathan Mantey 		return rc;
4821cecc5deSJohnathan Mantey 
4831cecc5deSJohnathan Mantey 	tv->tv_sec = t.tv_sec;
4841cecc5deSJohnathan Mantey 	tv->tv_usec = t.tv_nsec / 1000;
4851cecc5deSJohnathan Mantey 
4861cecc5deSJohnathan Mantey 	return 0;
4871cecc5deSJohnathan Mantey }
4881cecc5deSJohnathan Mantey 
489f733c85aSJeremy Kerr struct ringbuffer_consumer *console_ringbuffer_consumer_register(
490f733c85aSJeremy Kerr 		struct console *console,
491f733c85aSJeremy Kerr 		ringbuffer_poll_fn_t poll_fn, void *data)
492d831f960SJeremy Kerr {
493f733c85aSJeremy Kerr 	return ringbuffer_consumer_register(console->rb, poll_fn, data);
494d831f960SJeremy Kerr }
495d831f960SJeremy Kerr 
49655c9712dSJeremy Kerr struct poller *console_poller_register(struct console *console,
4971cecc5deSJohnathan Mantey 		struct handler *handler, poller_event_fn_t poller_fn,
4981cecc5deSJohnathan Mantey 		poller_timeout_fn_t timeout_fn, int fd,
4991cecc5deSJohnathan Mantey 		int events, void *data)
500d831f960SJeremy Kerr {
501329a35f5SJeremy Kerr 	struct poller *poller;
502329a35f5SJeremy Kerr 	int n;
503329a35f5SJeremy Kerr 
504329a35f5SJeremy Kerr 	poller = malloc(sizeof(*poller));
505329a35f5SJeremy Kerr 	poller->remove = false;
506329a35f5SJeremy Kerr 	poller->handler = handler;
5071cecc5deSJohnathan Mantey 	poller->event_fn = poller_fn;
5081cecc5deSJohnathan Mantey 	poller->timeout_fn = timeout_fn;
509329a35f5SJeremy Kerr 	poller->data = data;
510329a35f5SJeremy Kerr 
511329a35f5SJeremy Kerr 	/* add one to our pollers array */
512329a35f5SJeremy Kerr 	n = console->n_pollers++;
513329a35f5SJeremy Kerr 	console->pollers = realloc(console->pollers,
514329a35f5SJeremy Kerr 			sizeof(*console->pollers) * console->n_pollers);
515329a35f5SJeremy Kerr 
516329a35f5SJeremy Kerr 	console->pollers[n] = poller;
517329a35f5SJeremy Kerr 
518329a35f5SJeremy Kerr 	/* increase pollfds array too  */
519329a35f5SJeremy Kerr 	console->pollfds = realloc(console->pollfds,
520329a35f5SJeremy Kerr 			sizeof(*console->pollfds) *
521f9c8f6caSCheng C Yang 				(MAX_INTERNAL_POLLFD + console->n_pollers));
522329a35f5SJeremy Kerr 
523329a35f5SJeremy Kerr 	/* shift the end pollfds up by one */
524f9c8f6caSCheng C Yang 	memcpy(&console->pollfds[n+1],
525329a35f5SJeremy Kerr 		&console->pollfds[n],
526f9c8f6caSCheng C Yang 		sizeof(*console->pollfds) * MAX_INTERNAL_POLLFD);
527329a35f5SJeremy Kerr 
528329a35f5SJeremy Kerr 	console->pollfds[n].fd = fd;
529329a35f5SJeremy Kerr 	console->pollfds[n].events = events;
530329a35f5SJeremy Kerr 
531329a35f5SJeremy Kerr 	return poller;
532329a35f5SJeremy Kerr }
533329a35f5SJeremy Kerr 
53455c9712dSJeremy Kerr void console_poller_unregister(struct console *console,
535329a35f5SJeremy Kerr 		struct poller *poller)
536329a35f5SJeremy Kerr {
537329a35f5SJeremy Kerr 	int i;
538329a35f5SJeremy Kerr 
539329a35f5SJeremy Kerr 	/* find the entry in our pollers array */
540329a35f5SJeremy Kerr 	for (i = 0; i < console->n_pollers; i++)
541329a35f5SJeremy Kerr 		if (console->pollers[i] == poller)
542329a35f5SJeremy Kerr 			break;
543329a35f5SJeremy Kerr 
544329a35f5SJeremy Kerr 	assert(i < console->n_pollers);
545329a35f5SJeremy Kerr 
546329a35f5SJeremy Kerr 	console->n_pollers--;
547329a35f5SJeremy Kerr 
548329a35f5SJeremy Kerr 	/* remove the item from the pollers array... */
549329a35f5SJeremy Kerr 	memmove(&console->pollers[i], &console->pollers[i+1],
550329a35f5SJeremy Kerr 			sizeof(*console->pollers)
551329a35f5SJeremy Kerr 				* (console->n_pollers - i));
552329a35f5SJeremy Kerr 
553329a35f5SJeremy Kerr 	console->pollers = realloc(console->pollers,
554329a35f5SJeremy Kerr 			sizeof(*console->pollers) * console->n_pollers);
555329a35f5SJeremy Kerr 
556329a35f5SJeremy Kerr 	/* ... and the pollfds array */
557329a35f5SJeremy Kerr 	memmove(&console->pollfds[i], &console->pollfds[i+1],
558329a35f5SJeremy Kerr 			sizeof(*console->pollfds) *
559f9c8f6caSCheng C Yang 				(MAX_INTERNAL_POLLFD + console->n_pollers - i));
560329a35f5SJeremy Kerr 
561329a35f5SJeremy Kerr 	console->pollfds = realloc(console->pollfds,
562329a35f5SJeremy Kerr 			sizeof(*console->pollfds) *
563f9c8f6caSCheng C Yang 				(MAX_INTERNAL_POLLFD + console->n_pollers));
564329a35f5SJeremy Kerr 
565329a35f5SJeremy Kerr 
566329a35f5SJeremy Kerr 	free(poller);
567329a35f5SJeremy Kerr }
568329a35f5SJeremy Kerr 
5696b1fed27SJeremy Kerr void console_poller_set_events(struct console *console, struct poller *poller,
5706b1fed27SJeremy Kerr 		int events)
5716b1fed27SJeremy Kerr {
5726b1fed27SJeremy Kerr 	int i;
5736b1fed27SJeremy Kerr 
5746b1fed27SJeremy Kerr 	/* find the entry in our pollers array */
5756b1fed27SJeremy Kerr 	for (i = 0; i < console->n_pollers; i++)
5766b1fed27SJeremy Kerr 		if (console->pollers[i] == poller)
5776b1fed27SJeremy Kerr 			break;
5786b1fed27SJeremy Kerr 
5796b1fed27SJeremy Kerr 	console->pollfds[i].events = events;
5806b1fed27SJeremy Kerr }
5816b1fed27SJeremy Kerr 
582fd048328SAndrew Jeffery void console_poller_set_timeout(struct console *console __attribute__((unused)),
583fd048328SAndrew Jeffery 		struct poller *poller, const struct timeval *tv)
5841cecc5deSJohnathan Mantey {
5851cecc5deSJohnathan Mantey 	struct timeval now;
5861cecc5deSJohnathan Mantey 	int rc;
5871cecc5deSJohnathan Mantey 
5881cecc5deSJohnathan Mantey 	rc = get_current_time(&now);
5891cecc5deSJohnathan Mantey 	if (rc)
5901cecc5deSJohnathan Mantey 		return;
5911cecc5deSJohnathan Mantey 
5921cecc5deSJohnathan Mantey 	timeradd(&now, tv, &poller->timeout);
5931cecc5deSJohnathan Mantey }
5941cecc5deSJohnathan Mantey 
5951cecc5deSJohnathan Mantey static int get_poll_timeout(struct console *console, struct timeval *cur_time)
5961cecc5deSJohnathan Mantey {
5971cecc5deSJohnathan Mantey 	struct timeval *earliest, interval;
5981cecc5deSJohnathan Mantey 	struct poller *poller;
5991cecc5deSJohnathan Mantey 	int i;
6001cecc5deSJohnathan Mantey 
6011cecc5deSJohnathan Mantey 	earliest = NULL;
6021cecc5deSJohnathan Mantey 
6031cecc5deSJohnathan Mantey 	for (i = 0; i < console->n_pollers; i++) {
6041cecc5deSJohnathan Mantey 		poller = console->pollers[i];
6051cecc5deSJohnathan Mantey 
6061cecc5deSJohnathan Mantey 		if (poller->timeout_fn && timerisset(&poller->timeout) &&
6071cecc5deSJohnathan Mantey 		    (!earliest ||
6081cecc5deSJohnathan Mantey 		     (earliest && timercmp(&poller->timeout, earliest, <)))){
6091cecc5deSJohnathan Mantey 			// poller is buffering data and needs the poll
6101cecc5deSJohnathan Mantey 			// function to timeout.
6111cecc5deSJohnathan Mantey 			earliest = &poller->timeout;
6121cecc5deSJohnathan Mantey 		}
6131cecc5deSJohnathan Mantey 	}
6141cecc5deSJohnathan Mantey 
6151cecc5deSJohnathan Mantey 	if (earliest) {
6161cecc5deSJohnathan Mantey 		if (timercmp(earliest, cur_time, >)) {
6171cecc5deSJohnathan Mantey 			/* recalculate the timeout period, time period has
6181cecc5deSJohnathan Mantey 			 * not elapsed */
6191cecc5deSJohnathan Mantey 			timersub(earliest, cur_time, &interval);
6201cecc5deSJohnathan Mantey 			return ((interval.tv_sec * 1000) +
6211cecc5deSJohnathan Mantey 				(interval.tv_usec / 1000));
6221cecc5deSJohnathan Mantey 		} else {
6231cecc5deSJohnathan Mantey 			/* return from poll immediately */
6241cecc5deSJohnathan Mantey 			return 0;
6251cecc5deSJohnathan Mantey 		}
6261cecc5deSJohnathan Mantey 	} else {
6271cecc5deSJohnathan Mantey 		/* poll indefinitely */
6281cecc5deSJohnathan Mantey 		return -1;
6291cecc5deSJohnathan Mantey 	}
6301cecc5deSJohnathan Mantey }
6311cecc5deSJohnathan Mantey 
6321cecc5deSJohnathan Mantey static int call_pollers(struct console *console, struct timeval *cur_time)
633329a35f5SJeremy Kerr {
634329a35f5SJeremy Kerr 	struct poller *poller;
635329a35f5SJeremy Kerr 	struct pollfd *pollfd;
636329a35f5SJeremy Kerr 	enum poller_ret prc;
637329a35f5SJeremy Kerr 	int i, rc;
638d831f960SJeremy Kerr 
6391a0e03b4SJeremy Kerr 	rc = 0;
6401a0e03b4SJeremy Kerr 
641329a35f5SJeremy Kerr 	/*
642329a35f5SJeremy Kerr 	 * Process poll events by iterating through the pollers and pollfds
643329a35f5SJeremy Kerr 	 * in-step, calling any pollers that we've found revents for.
644329a35f5SJeremy Kerr 	 */
645329a35f5SJeremy Kerr 	for (i = 0; i < console->n_pollers; i++) {
646329a35f5SJeremy Kerr 		poller = console->pollers[i];
647329a35f5SJeremy Kerr 		pollfd = &console->pollfds[i];
6481cecc5deSJohnathan Mantey 		prc = POLLER_OK;
6491a0e03b4SJeremy Kerr 
6501cecc5deSJohnathan Mantey 		/* process pending events... */
6511cecc5deSJohnathan Mantey 		if (pollfd->revents) {
6521cecc5deSJohnathan Mantey 			prc = poller->event_fn(poller->handler, pollfd->revents,
653329a35f5SJeremy Kerr 					poller->data);
654329a35f5SJeremy Kerr 			if (prc == POLLER_EXIT)
655329a35f5SJeremy Kerr 				rc = -1;
656329a35f5SJeremy Kerr 			else if (prc == POLLER_REMOVE)
657329a35f5SJeremy Kerr 				poller->remove = true;
658329a35f5SJeremy Kerr 		}
659329a35f5SJeremy Kerr 
6601cecc5deSJohnathan Mantey 		if ((prc == POLLER_OK) && poller->timeout_fn &&
6611cecc5deSJohnathan Mantey 		    timerisset(&poller->timeout) &&
6621cecc5deSJohnathan Mantey 		    timercmp(&poller->timeout, cur_time, <=)) {
6631cecc5deSJohnathan Mantey 			/* One of the ringbuffer consumers is buffering the
6641cecc5deSJohnathan Mantey 			data stream. The amount of idle time the consumer
6651cecc5deSJohnathan Mantey 			desired has expired.  Process the buffered data for
6661cecc5deSJohnathan Mantey 			transmission. */
6671cecc5deSJohnathan Mantey 			timerclear(&poller->timeout);
6681cecc5deSJohnathan Mantey 			prc = poller->timeout_fn(poller->handler, poller->data);
6691cecc5deSJohnathan Mantey 			if (prc == POLLER_EXIT) {
6701cecc5deSJohnathan Mantey 				rc = -1;
6711cecc5deSJohnathan Mantey 			} else if (prc == POLLER_REMOVE) {
6721cecc5deSJohnathan Mantey 				poller->remove = true;
6731cecc5deSJohnathan Mantey 			}
6741cecc5deSJohnathan Mantey 		}
6751cecc5deSJohnathan Mantey 	}
6761cecc5deSJohnathan Mantey 
677329a35f5SJeremy Kerr 	/**
678329a35f5SJeremy Kerr 	 * Process deferred removals; restarting each time we unregister, as
679329a35f5SJeremy Kerr 	 * the array will have changed
680329a35f5SJeremy Kerr 	 */
681329a35f5SJeremy Kerr 	for (;;) {
682329a35f5SJeremy Kerr 		bool removed = false;
683329a35f5SJeremy Kerr 
684329a35f5SJeremy Kerr 		for (i = 0; i < console->n_pollers; i++) {
685329a35f5SJeremy Kerr 			poller = console->pollers[i];
686329a35f5SJeremy Kerr 			if (poller->remove) {
68755c9712dSJeremy Kerr 				console_poller_unregister(console, poller);
688329a35f5SJeremy Kerr 				removed = true;
689329a35f5SJeremy Kerr 				break;
690329a35f5SJeremy Kerr 			}
691329a35f5SJeremy Kerr 		}
692329a35f5SJeremy Kerr 		if (!removed)
693329a35f5SJeremy Kerr 			break;
6941a0e03b4SJeremy Kerr 	}
6951a0e03b4SJeremy Kerr 
6961a0e03b4SJeremy Kerr 	return rc;
6971a0e03b4SJeremy Kerr }
6981a0e03b4SJeremy Kerr 
699769cee1aSJeremy Kerr static void sighandler(int signal)
700769cee1aSJeremy Kerr {
701769cee1aSJeremy Kerr 	if (signal == SIGINT)
702769cee1aSJeremy Kerr 		sigint = true;
703769cee1aSJeremy Kerr }
704769cee1aSJeremy Kerr 
7051a0e03b4SJeremy Kerr int run_console(struct console *console)
7061a0e03b4SJeremy Kerr {
707769cee1aSJeremy Kerr 	sighandler_t sighandler_save;
7081cecc5deSJohnathan Mantey 	struct timeval tv;
7091cecc5deSJohnathan Mantey 	int rc, timeout;
710d831f960SJeremy Kerr 
711769cee1aSJeremy Kerr 	sighandler_save = signal(SIGINT, sighandler);
712769cee1aSJeremy Kerr 
713769cee1aSJeremy Kerr 	rc = 0;
714769cee1aSJeremy Kerr 
715d831f960SJeremy Kerr 	for (;;) {
716d831f960SJeremy Kerr 		uint8_t buf[4096];
717d831f960SJeremy Kerr 
7181764145dSJeremy Kerr 		BUILD_ASSERT(sizeof(buf) <= buffer_size);
7191764145dSJeremy Kerr 
720769cee1aSJeremy Kerr 		if (sigint) {
721769cee1aSJeremy Kerr 			fprintf(stderr, "Received interrupt, exiting\n");
722769cee1aSJeremy Kerr 			break;
723769cee1aSJeremy Kerr 		}
724769cee1aSJeremy Kerr 
7251cecc5deSJohnathan Mantey 		rc = get_current_time(&tv);
7261cecc5deSJohnathan Mantey 		if (rc) {
7271cecc5deSJohnathan Mantey 			warn("Failed to read current time");
7281cecc5deSJohnathan Mantey 			break;
7291cecc5deSJohnathan Mantey 		}
7301cecc5deSJohnathan Mantey 
7311cecc5deSJohnathan Mantey 		timeout = get_poll_timeout(console, &tv);
7321cecc5deSJohnathan Mantey 
733329a35f5SJeremy Kerr 		rc = poll(console->pollfds,
7341cecc5deSJohnathan Mantey 				console->n_pollers + MAX_INTERNAL_POLLFD,
7351cecc5deSJohnathan Mantey 				timeout);
7361cecc5deSJohnathan Mantey 
737d831f960SJeremy Kerr 		if (rc < 0) {
738769cee1aSJeremy Kerr 			if (errno == EINTR) {
739769cee1aSJeremy Kerr 				continue;
740769cee1aSJeremy Kerr 			} else {
741d831f960SJeremy Kerr 				warn("poll error");
742769cee1aSJeremy Kerr 				break;
743769cee1aSJeremy Kerr 			}
744d831f960SJeremy Kerr 		}
745d831f960SJeremy Kerr 
746329a35f5SJeremy Kerr 		/* process internal fd first */
747329a35f5SJeremy Kerr 		if (console->pollfds[console->n_pollers].revents) {
7481a0e03b4SJeremy Kerr 			rc = read(console->tty_fd, buf, sizeof(buf));
749d831f960SJeremy Kerr 			if (rc <= 0) {
750d831f960SJeremy Kerr 				warn("Error reading from tty device");
751769cee1aSJeremy Kerr 				rc = -1;
752769cee1aSJeremy Kerr 				break;
753d831f960SJeremy Kerr 			}
754f733c85aSJeremy Kerr 			rc = ringbuffer_queue(console->rb, buf, rc);
7551a0e03b4SJeremy Kerr 			if (rc)
756769cee1aSJeremy Kerr 				break;
757d831f960SJeremy Kerr 		}
758d831f960SJeremy Kerr 
759f9c8f6caSCheng C Yang 		if (console->pollfds[console->n_pollers + 1].revents) {
760f9c8f6caSCheng C Yang 			sd_bus_process(console->bus, NULL);
761f9c8f6caSCheng C Yang 		}
762f9c8f6caSCheng C Yang 
763329a35f5SJeremy Kerr 		/* ... and then the pollers */
7641cecc5deSJohnathan Mantey 		rc = call_pollers(console, &tv);
7651a0e03b4SJeremy Kerr 		if (rc)
766769cee1aSJeremy Kerr 			break;
7671a0e03b4SJeremy Kerr 	}
768769cee1aSJeremy Kerr 
769769cee1aSJeremy Kerr 	signal(SIGINT, sighandler_save);
770f9c8f6caSCheng C Yang 	sd_bus_unref(console->bus);
771769cee1aSJeremy Kerr 
772769cee1aSJeremy Kerr 	return rc ? -1 : 0;
7731a0e03b4SJeremy Kerr }
774d831f960SJeremy Kerr static const struct option options[] = {
775d66195c1SJeremy Kerr 	{ "config",	required_argument,	0, 'c'},
776f5858b5bSJoel Stanley 	{ 0,  0, 0, 0},
777d831f960SJeremy Kerr };
778d831f960SJeremy Kerr 
779d831f960SJeremy Kerr int main(int argc, char **argv)
780d831f960SJeremy Kerr {
781d66195c1SJeremy Kerr 	const char *config_filename = NULL;
7826221ce94SVishwanatha Subbanna 	const char *config_tty_kname = NULL;
7831a0e03b4SJeremy Kerr 	struct console *console;
784d66195c1SJeremy Kerr 	struct config *config;
785d66195c1SJeremy Kerr 	int rc;
786d831f960SJeremy Kerr 
787957818b4SJeremy Kerr 	rc = -1;
788d831f960SJeremy Kerr 
789d831f960SJeremy Kerr 	for (;;) {
790d831f960SJeremy Kerr 		int c, idx;
791d831f960SJeremy Kerr 
792d66195c1SJeremy Kerr 		c = getopt_long(argc, argv, "c:", options, &idx);
793d831f960SJeremy Kerr 		if (c == -1)
794d831f960SJeremy Kerr 			break;
795d831f960SJeremy Kerr 
796d831f960SJeremy Kerr 		switch (c) {
797d66195c1SJeremy Kerr 		case 'c':
798d66195c1SJeremy Kerr 			config_filename = optarg;
799d831f960SJeremy Kerr 			break;
800d831f960SJeremy Kerr 		case 'h':
801d831f960SJeremy Kerr 		case '?':
802d831f960SJeremy Kerr 			usage(argv[0]);
803d66195c1SJeremy Kerr 			return EXIT_SUCCESS;
804d831f960SJeremy Kerr 		}
805d831f960SJeremy Kerr 	}
806d831f960SJeremy Kerr 
80791dde14eSAndrew Jeffery 	if (optind < argc)
8086221ce94SVishwanatha Subbanna 		config_tty_kname = argv[optind];
8096221ce94SVishwanatha Subbanna 
810d66195c1SJeremy Kerr 	console = malloc(sizeof(struct console));
811d66195c1SJeremy Kerr 	memset(console, 0, sizeof(*console));
812f9c8f6caSCheng C Yang 	console->pollfds = calloc(MAX_INTERNAL_POLLFD,
813329a35f5SJeremy Kerr 			sizeof(*console->pollfds));
814f733c85aSJeremy Kerr 	console->rb = ringbuffer_init(buffer_size);
815329a35f5SJeremy Kerr 
816d66195c1SJeremy Kerr 	config = config_init(config_filename);
817d66195c1SJeremy Kerr 	if (!config) {
818d66195c1SJeremy Kerr 		warnx("Can't read configuration, exiting.");
819d66195c1SJeremy Kerr 		goto out_free;
820d831f960SJeremy Kerr 	}
821d831f960SJeremy Kerr 
82291dde14eSAndrew Jeffery 	if (!config_tty_kname)
82391dde14eSAndrew Jeffery 		config_tty_kname = config_get_value(config, "upstream-tty");
82491dde14eSAndrew Jeffery 
82591dde14eSAndrew Jeffery 	if (!config_tty_kname) {
82691dde14eSAndrew Jeffery 		warnx("No TTY device specified");
82791dde14eSAndrew Jeffery 		usage(argv[0]);
82891dde14eSAndrew Jeffery 		return EXIT_FAILURE;
82991dde14eSAndrew Jeffery 	}
83091dde14eSAndrew Jeffery 
8316221ce94SVishwanatha Subbanna 	console->tty_kname = config_tty_kname;
8326221ce94SVishwanatha Subbanna 
833d66195c1SJeremy Kerr 	rc = tty_init(console, config);
83417217845SJeremy Kerr 	if (rc)
835d66195c1SJeremy Kerr 		goto out_config_fini;
836d831f960SJeremy Kerr 
837f9c8f6caSCheng C Yang 	dbus_init(console, config);
838f9c8f6caSCheng C Yang 
839d47963e5SJeremy Kerr 	handlers_init(console, config);
840d831f960SJeremy Kerr 
8411a0e03b4SJeremy Kerr 	rc = run_console(console);
842d831f960SJeremy Kerr 
8431a0e03b4SJeremy Kerr 	handlers_fini(console);
844d831f960SJeremy Kerr 
845d66195c1SJeremy Kerr out_config_fini:
846d66195c1SJeremy Kerr 	config_fini(config);
847d66195c1SJeremy Kerr 
848957818b4SJeremy Kerr out_free:
84989ea8198SJeremy Kerr 	free(console->pollers);
85089ea8198SJeremy Kerr 	free(console->pollfds);
8511a0e03b4SJeremy Kerr 	free(console->tty_sysfs_devnode);
8521a0e03b4SJeremy Kerr 	free(console->tty_dev);
8531a0e03b4SJeremy Kerr 	free(console);
854d831f960SJeremy Kerr 
855d831f960SJeremy Kerr 	return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
856d831f960SJeremy Kerr }
857