xref: /openbmc/obmc-console/console-server.c (revision fd04832808ed77b900d76dc1c9e9fe9b691e2fbd)
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);
12745ad7676SYi Li 		goto out_free;
12845ad7676SYi Li 	}
12945ad7676SYi Li 
13045ad7676SYi Li 	tty_kname_real = basename(tty_path_input_real);
13145ad7676SYi Li 	if (!tty_kname_real) {
13245ad7676SYi Li 		warn("Can't find real name for /dev/%s", console->tty_kname);
13345ad7676SYi Li 		goto out_free;
13445ad7676SYi Li 	}
13545ad7676SYi Li 
13645ad7676SYi Li 	rc = asprintf(&tty_class_device_link,
13745ad7676SYi Li 			"/sys/class/tty/%s", tty_kname_real);
13845ad7676SYi Li 	if (rc < 0)
13945ad7676SYi Li 		goto out_free;
14045ad7676SYi Li 
14117217845SJeremy Kerr 	tty_device_tty_dir = realpath(tty_class_device_link, NULL);
14245ad7676SYi Li 	if (!tty_device_tty_dir) {
14345ad7676SYi Li 		warn("Can't query sysfs for device %s", tty_kname_real);
14417217845SJeremy Kerr 		goto out_free;
14517217845SJeremy Kerr 	}
14617217845SJeremy Kerr 
14717217845SJeremy Kerr 	rc = asprintf(&tty_device_reldir, "%s/../../", tty_device_tty_dir);
14817217845SJeremy Kerr 	if (rc < 0)
14917217845SJeremy Kerr 		goto out_free;
15017217845SJeremy Kerr 
1511a0e03b4SJeremy Kerr 	console->tty_sysfs_devnode = realpath(tty_device_reldir, NULL);
1521a0e03b4SJeremy Kerr 	if (!console->tty_sysfs_devnode)
15345ad7676SYi Li 		warn("Can't find parent device for %s", tty_kname_real);
15417217845SJeremy Kerr 
15545ad7676SYi Li 	rc = asprintf(&console->tty_dev, "/dev/%s", tty_kname_real);
15617217845SJeremy Kerr 	if (rc < 0)
15717217845SJeremy Kerr 		goto out_free;
15817217845SJeremy Kerr 
15917217845SJeremy Kerr 	rc = 0;
16017217845SJeremy Kerr 
16117217845SJeremy Kerr out_free:
16217217845SJeremy Kerr 	free(tty_class_device_link);
16317217845SJeremy Kerr 	free(tty_device_tty_dir);
16417217845SJeremy Kerr 	free(tty_device_reldir);
16545ad7676SYi Li 	free(tty_path_input);
16645ad7676SYi Li 	free(tty_path_input_real);
16717217845SJeremy Kerr 	return rc;
16817217845SJeremy Kerr }
16917217845SJeremy Kerr 
1701a0e03b4SJeremy Kerr static int tty_set_sysfs_attr(struct console *console, const char *name,
171957818b4SJeremy Kerr 		int value)
172957818b4SJeremy Kerr {
173957818b4SJeremy Kerr 	char *path;
174957818b4SJeremy Kerr 	FILE *fp;
175957818b4SJeremy Kerr 	int rc;
176957818b4SJeremy Kerr 
1771a0e03b4SJeremy Kerr 	rc = asprintf(&path, "%s/%s", console->tty_sysfs_devnode, name);
178957818b4SJeremy Kerr 	if (rc < 0)
179957818b4SJeremy Kerr 		return -1;
180957818b4SJeremy Kerr 
181957818b4SJeremy Kerr 	fp = fopen(path, "w");
182957818b4SJeremy Kerr 	if (!fp) {
183957818b4SJeremy Kerr 		warn("Can't access attribute %s on device %s",
1841a0e03b4SJeremy Kerr 				name, console->tty_kname);
185957818b4SJeremy Kerr 		rc = -1;
186957818b4SJeremy Kerr 		goto out_free;
187957818b4SJeremy Kerr 	}
188957818b4SJeremy Kerr 	setvbuf(fp, NULL, _IONBF, 0);
189957818b4SJeremy Kerr 
190957818b4SJeremy Kerr 	rc = fprintf(fp, "0x%x", value);
191957818b4SJeremy Kerr 	if (rc < 0)
192957818b4SJeremy Kerr 		warn("Error writing to %s attribute of device %s",
1931a0e03b4SJeremy Kerr 				name, console->tty_kname);
194957818b4SJeremy Kerr 	fclose(fp);
195957818b4SJeremy Kerr 
196957818b4SJeremy Kerr 
197957818b4SJeremy Kerr 
198957818b4SJeremy Kerr out_free:
199957818b4SJeremy Kerr 	free(path);
200957818b4SJeremy Kerr 	return rc;
201957818b4SJeremy Kerr }
202957818b4SJeremy Kerr 
203d831f960SJeremy Kerr /**
204c7fbcd48SBenjamin Fair  * Set termios attributes on the console tty.
20554e9569dSJeremy Kerr  */
20654e9569dSJeremy Kerr static void tty_init_termios(struct console *console)
20754e9569dSJeremy Kerr {
20854e9569dSJeremy Kerr 	struct termios termios;
20954e9569dSJeremy Kerr 	int rc;
21054e9569dSJeremy Kerr 
21154e9569dSJeremy Kerr 	rc = tcgetattr(console->tty_fd, &termios);
21254e9569dSJeremy Kerr 	if (rc) {
21354e9569dSJeremy Kerr 		warn("Can't read tty termios");
21454e9569dSJeremy Kerr 		return;
21554e9569dSJeremy Kerr 	}
21654e9569dSJeremy Kerr 
217c7fbcd48SBenjamin Fair 	if (console->tty_baud) {
218c7fbcd48SBenjamin Fair 		if (cfsetspeed(&termios, console->tty_baud) < 0)
219c7fbcd48SBenjamin Fair 			warn("Couldn't set speeds for %s", console->tty_kname);
220c7fbcd48SBenjamin Fair 	}
221c7fbcd48SBenjamin Fair 
222c7fbcd48SBenjamin Fair 	/* Set console to raw mode: we don't want any processing to occur on
223c7fbcd48SBenjamin Fair 	 * the underlying terminal input/output.
224c7fbcd48SBenjamin Fair 	 */
22554e9569dSJeremy Kerr 	cfmakeraw(&termios);
226c7fbcd48SBenjamin Fair 
22754e9569dSJeremy Kerr 	rc = tcsetattr(console->tty_fd, TCSANOW, &termios);
22854e9569dSJeremy Kerr 	if (rc)
229c7fbcd48SBenjamin Fair 		warn("Can't set terminal options for %s", console->tty_kname);
23054e9569dSJeremy Kerr }
23154e9569dSJeremy Kerr 
232f9c8f6caSCheng C Yang 
233f9c8f6caSCheng C Yang static void tty_change_baudrate(struct console *console)
234f9c8f6caSCheng C Yang {
235f9c8f6caSCheng C Yang 	struct handler *handler;
236f9c8f6caSCheng C Yang 	int i, rc;
237f9c8f6caSCheng C Yang 
238f9c8f6caSCheng C Yang 	tty_init_termios(console);
239f9c8f6caSCheng C Yang 
240f9c8f6caSCheng C Yang 	for (i = 0; i < console->n_handlers; i++) {
241f9c8f6caSCheng C Yang 		handler = console->handlers[i];
242f9c8f6caSCheng C Yang 		if (!handler->baudrate)
243f9c8f6caSCheng C Yang 			continue;
244f9c8f6caSCheng C Yang 
245f9c8f6caSCheng C Yang 		rc = handler->baudrate(handler, console->tty_baud);
246f9c8f6caSCheng C Yang 		if (rc)
247f9c8f6caSCheng C Yang 			warnx("Can't set terminal baudrate for handler %s",
248f9c8f6caSCheng C Yang 			     handler->name);
249f9c8f6caSCheng C Yang 	}
250f9c8f6caSCheng C Yang }
251f9c8f6caSCheng C Yang 
25254e9569dSJeremy Kerr /**
253d831f960SJeremy Kerr  * Open and initialise the serial device
254d831f960SJeremy Kerr  */
2551a0e03b4SJeremy Kerr static int tty_init_io(struct console *console)
256d831f960SJeremy Kerr {
2571a0e03b4SJeremy Kerr 	if (console->tty_sirq)
2581a0e03b4SJeremy Kerr 		tty_set_sysfs_attr(console, "sirq", console->tty_sirq);
2591a0e03b4SJeremy Kerr 	if (console->tty_lpc_addr)
2601a0e03b4SJeremy Kerr 		tty_set_sysfs_attr(console, "lpc_address",
2611a0e03b4SJeremy Kerr 				console->tty_lpc_addr);
262957818b4SJeremy Kerr 
2631a0e03b4SJeremy Kerr 	console->tty_fd = open(console->tty_dev, O_RDWR);
2641a0e03b4SJeremy Kerr 	if (console->tty_fd <= 0) {
2651a0e03b4SJeremy Kerr 		warn("Can't open tty %s", console->tty_dev);
266d831f960SJeremy Kerr 		return -1;
267d831f960SJeremy Kerr 	}
268d831f960SJeremy Kerr 
269d831f960SJeremy Kerr 	/* Disable character delay. We may want to later enable this when
270d831f960SJeremy Kerr 	 * we detect larger amounts of data
271d831f960SJeremy Kerr 	 */
2721a0e03b4SJeremy Kerr 	fcntl(console->tty_fd, F_SETFL, FNDELAY);
273d831f960SJeremy Kerr 
27454e9569dSJeremy Kerr 	tty_init_termios(console);
27554e9569dSJeremy Kerr 
276329a35f5SJeremy Kerr 	console->pollfds[console->n_pollers].fd = console->tty_fd;
277329a35f5SJeremy Kerr 	console->pollfds[console->n_pollers].events = POLLIN;
278329a35f5SJeremy Kerr 
279d831f960SJeremy Kerr 	return 0;
280d831f960SJeremy Kerr }
281d831f960SJeremy Kerr 
282d66195c1SJeremy Kerr static int tty_init(struct console *console, struct config *config)
283d66195c1SJeremy Kerr {
284d66195c1SJeremy Kerr 	const char *val;
285d66195c1SJeremy Kerr 	char *endp;
286d66195c1SJeremy Kerr 	int rc;
287d66195c1SJeremy Kerr 
288d66195c1SJeremy Kerr 	val = config_get_value(config, "lpc-address");
289d66195c1SJeremy Kerr 	if (val) {
290d66195c1SJeremy Kerr 		console->tty_lpc_addr = strtoul(val, &endp, 0);
291d66195c1SJeremy Kerr 		if (endp == optarg) {
292d66195c1SJeremy Kerr 			warn("Invalid LPC address: '%s'", val);
293d66195c1SJeremy Kerr 			return -1;
294d66195c1SJeremy Kerr 		}
295d66195c1SJeremy Kerr 	}
296d66195c1SJeremy Kerr 
297d66195c1SJeremy Kerr 	val = config_get_value(config, "sirq");
298d66195c1SJeremy Kerr 	if (val) {
299d66195c1SJeremy Kerr 		console->tty_sirq = strtoul(val, &endp, 0);
300d66195c1SJeremy Kerr 		if (endp == optarg)
301d66195c1SJeremy Kerr 			warn("Invalid sirq: '%s'", val);
302d66195c1SJeremy Kerr 	}
303d66195c1SJeremy Kerr 
304c7fbcd48SBenjamin Fair 	val = config_get_value(config, "baud");
305c7fbcd48SBenjamin Fair 	if (val) {
306c7fbcd48SBenjamin Fair 		if (config_parse_baud(&console->tty_baud, val))
307c7fbcd48SBenjamin Fair 			warnx("Invalid baud rate: '%s'", val);
308c7fbcd48SBenjamin Fair 	}
309c7fbcd48SBenjamin Fair 
310d66195c1SJeremy Kerr 	if (!console->tty_kname) {
311d66195c1SJeremy Kerr 		warnx("Error: No TTY device specified");
312d66195c1SJeremy Kerr 		return -1;
313d66195c1SJeremy Kerr 	}
314d66195c1SJeremy Kerr 
315d66195c1SJeremy Kerr 	rc = tty_find_device(console);
316d66195c1SJeremy Kerr 	if (rc)
317d66195c1SJeremy Kerr 		return rc;
318d66195c1SJeremy Kerr 
319d66195c1SJeremy Kerr 	rc = tty_init_io(console);
320d66195c1SJeremy Kerr 	return rc;
321d66195c1SJeremy Kerr }
322d66195c1SJeremy Kerr 
3231a0e03b4SJeremy Kerr 
3241a0e03b4SJeremy Kerr int console_data_out(struct console *console, const uint8_t *data, size_t len)
325d831f960SJeremy Kerr {
3261a0e03b4SJeremy Kerr 	return write_buf_to_fd(console->tty_fd, data, len);
327d831f960SJeremy Kerr }
328d831f960SJeremy Kerr 
329f9c8f6caSCheng C Yang static int method_set_baud_rate(sd_bus_message *msg, void *userdata,
330f9c8f6caSCheng C Yang 			 sd_bus_error *err)
331f9c8f6caSCheng C Yang {
332f9c8f6caSCheng C Yang 	struct console *console = userdata;
333f9c8f6caSCheng C Yang 	uint32_t baudrate;
334f9c8f6caSCheng C Yang 	speed_t speed;
335f9c8f6caSCheng C Yang 	int r;
336f9c8f6caSCheng C Yang 
337f9c8f6caSCheng C Yang 	if (!console) {
338f9c8f6caSCheng C Yang 		sd_bus_error_set_const(err, DBUS_ERR, "Internal error");
339f9c8f6caSCheng C Yang 		return sd_bus_reply_method_return(msg, "x", 0);
340f9c8f6caSCheng C Yang 	}
341f9c8f6caSCheng C Yang 
342f9c8f6caSCheng C Yang 	r = sd_bus_message_read(msg, "u", &baudrate);
343f9c8f6caSCheng C Yang 	if (r < 0) {
344f9c8f6caSCheng C Yang 		sd_bus_error_set_const(err, DBUS_ERR, "Bad message");
345f9c8f6caSCheng C Yang 		return sd_bus_reply_method_return(msg, "x", -EINVAL);
346f9c8f6caSCheng C Yang 	}
347f9c8f6caSCheng C Yang 
348f9c8f6caSCheng C Yang 	speed = parse_int_to_baud(baudrate);
349f9c8f6caSCheng C Yang 	if (!speed) {
350f9c8f6caSCheng C Yang 		warnx("Invalid baud rate: '%u'", baudrate);
351f9c8f6caSCheng C Yang 		return sd_bus_reply_method_return(msg, "x", -EINVAL);
352f9c8f6caSCheng C Yang 	}
353f9c8f6caSCheng C Yang 
354f9c8f6caSCheng C Yang 	console->tty_baud = speed;
355f9c8f6caSCheng C Yang 	tty_change_baudrate(console);
356f9c8f6caSCheng C Yang 
357f9c8f6caSCheng C Yang 	return sd_bus_reply_method_return(msg, "x", r);
358f9c8f6caSCheng C Yang }
359f9c8f6caSCheng C Yang 
360*fd048328SAndrew Jeffery static int get_handler(sd_bus *bus __attribute__((unused)),
361*fd048328SAndrew Jeffery 		       const char *path __attribute__((unused)),
362*fd048328SAndrew Jeffery 		       const char *interface __attribute__((unused)),
363*fd048328SAndrew Jeffery 		       const char *property __attribute__((unused)),
364*fd048328SAndrew Jeffery 		       sd_bus_message *reply, void *userdata,
365*fd048328SAndrew Jeffery 		       sd_bus_error *error __attribute__((unused))) {
366f9c8f6caSCheng C Yang 	struct console *console = userdata;
367f9c8f6caSCheng C Yang 	uint32_t baudrate;
368f9c8f6caSCheng C Yang 	int r;
369f9c8f6caSCheng C Yang 
370f9c8f6caSCheng C Yang 	baudrate = parse_baud_to_int(console->tty_baud);
371f9c8f6caSCheng C Yang 	if (!baudrate)
372f9c8f6caSCheng C Yang 		warnx("Invalid baud rate: '%d'", console->tty_baud);
373f9c8f6caSCheng C Yang 
374f9c8f6caSCheng C Yang 	r = sd_bus_message_append(reply, "u", baudrate);
375f9c8f6caSCheng C Yang 
376f9c8f6caSCheng C Yang 	return r;
377f9c8f6caSCheng C Yang }
378f9c8f6caSCheng C Yang 
379f9c8f6caSCheng C Yang static const sd_bus_vtable console_vtable[] = {
380f9c8f6caSCheng C Yang 	SD_BUS_VTABLE_START(0),
381f9c8f6caSCheng C Yang 	SD_BUS_METHOD("setBaudRate", "u", "x", method_set_baud_rate,
382f9c8f6caSCheng C Yang 		      SD_BUS_VTABLE_UNPRIVILEGED),
383f9c8f6caSCheng C Yang 	SD_BUS_PROPERTY("baudrate", "u", get_handler, 0, 0),
384f9c8f6caSCheng C Yang 	SD_BUS_VTABLE_END,};
385f9c8f6caSCheng C Yang 
386*fd048328SAndrew Jeffery static void dbus_init(struct console *console, struct config *config __attribute__((unused)))
387f9c8f6caSCheng C Yang {
388f9c8f6caSCheng C Yang 	int dbus_poller = 0;
389f9c8f6caSCheng C Yang 	int fd, r;
390f9c8f6caSCheng C Yang 
391f9c8f6caSCheng C Yang 	if (!console) {
392f9c8f6caSCheng C Yang 		warnx("Couldn't get valid console");
393f9c8f6caSCheng C Yang 		return;
394f9c8f6caSCheng C Yang 	}
395f9c8f6caSCheng C Yang 
396f9c8f6caSCheng C Yang 	r = sd_bus_default_system(&console->bus);
397f9c8f6caSCheng C Yang 	if (r < 0) {
398f9c8f6caSCheng C Yang 		warnx("Failed to connect to system bus: %s", strerror(-r));
399f9c8f6caSCheng C Yang 		return;
400f9c8f6caSCheng C Yang 	}
401f9c8f6caSCheng C Yang 
402f9c8f6caSCheng C Yang 	r = sd_bus_add_object_vtable(console->bus, NULL, OBJ_NAME, DBUS_NAME,
403f9c8f6caSCheng C Yang 				     console_vtable, console);
404f9c8f6caSCheng C Yang 	if (r < 0) {
405f9c8f6caSCheng C Yang 		warnx("Failed to issue method call: %s", strerror(-r));
406f9c8f6caSCheng C Yang 		return;
407f9c8f6caSCheng C Yang 	}
408f9c8f6caSCheng C Yang 
409f9c8f6caSCheng C Yang 	r = sd_bus_request_name(console->bus, DBUS_NAME, SD_BUS_NAME_ALLOW_REPLACEMENT
410f9c8f6caSCheng C Yang 				|SD_BUS_NAME_REPLACE_EXISTING);
411f9c8f6caSCheng C Yang 	if (r < 0) {
412f9c8f6caSCheng C Yang 		warnx("Failed to acquire service name: %s", strerror(-r));
413f9c8f6caSCheng C Yang 		return;
414f9c8f6caSCheng C Yang 	}
415f9c8f6caSCheng C Yang 
416f9c8f6caSCheng C Yang 	fd = sd_bus_get_fd(console->bus);
417f9c8f6caSCheng C Yang 	if (fd < 0) {
418f9c8f6caSCheng C Yang 		warnx("Couldn't get the bus file descriptor");
419f9c8f6caSCheng C Yang 		return;
420f9c8f6caSCheng C Yang 	}
421f9c8f6caSCheng C Yang 
422f9c8f6caSCheng C Yang 	dbus_poller = POLLFD_DBUS;
423f9c8f6caSCheng C Yang 
424f9c8f6caSCheng C Yang 	console->pollfds[dbus_poller].fd = fd;
425f9c8f6caSCheng C Yang 	console->pollfds[dbus_poller].events = POLLIN;
426f9c8f6caSCheng C Yang }
427f9c8f6caSCheng C Yang 
428d47963e5SJeremy Kerr static void handlers_init(struct console *console, struct config *config)
429d831f960SJeremy Kerr {
4301a0e03b4SJeremy Kerr 	extern struct handler *__start_handlers, *__stop_handlers;
4311a0e03b4SJeremy Kerr 	struct handler *handler;
432021b91f0SJeremy Kerr 	int i, rc;
433d831f960SJeremy Kerr 
4341a0e03b4SJeremy Kerr 	console->n_handlers = &__stop_handlers - &__start_handlers;
4351a0e03b4SJeremy Kerr 	console->handlers = &__start_handlers;
436d831f960SJeremy Kerr 
4371a0e03b4SJeremy Kerr 	printf("%d handler%s\n", console->n_handlers,
4381a0e03b4SJeremy Kerr 			console->n_handlers == 1 ? "" : "s");
439d831f960SJeremy Kerr 
4401a0e03b4SJeremy Kerr 	for (i = 0; i < console->n_handlers; i++) {
4411a0e03b4SJeremy Kerr 		handler = console->handlers[i];
4421a0e03b4SJeremy Kerr 
443021b91f0SJeremy Kerr 		rc = 0;
4441a0e03b4SJeremy Kerr 		if (handler->init)
445021b91f0SJeremy Kerr 			rc = handler->init(handler, console, config);
446021b91f0SJeremy Kerr 
447021b91f0SJeremy Kerr 		handler->active = rc == 0;
448021b91f0SJeremy Kerr 
449021b91f0SJeremy Kerr 		printf("  %s [%sactive]\n", handler->name,
450021b91f0SJeremy Kerr 				handler->active ? "" : "in");
451d831f960SJeremy Kerr 	}
452d831f960SJeremy Kerr }
453d831f960SJeremy Kerr 
4541a0e03b4SJeremy Kerr static void handlers_fini(struct console *console)
455d831f960SJeremy Kerr {
4561a0e03b4SJeremy Kerr 	struct handler *handler;
4571a0e03b4SJeremy Kerr 	int i;
4581a0e03b4SJeremy Kerr 
4591a0e03b4SJeremy Kerr 	for (i = 0; i < console->n_handlers; i++) {
4601a0e03b4SJeremy Kerr 		handler = console->handlers[i];
461021b91f0SJeremy Kerr 		if (handler->fini && handler->active)
4621a0e03b4SJeremy Kerr 			handler->fini(handler);
4631a0e03b4SJeremy Kerr 	}
464d831f960SJeremy Kerr }
465d831f960SJeremy Kerr 
4661cecc5deSJohnathan Mantey static int get_current_time(struct timeval *tv)
4671cecc5deSJohnathan Mantey {
4681cecc5deSJohnathan Mantey 	struct timespec t;
4691cecc5deSJohnathan Mantey 	int rc;
4701cecc5deSJohnathan Mantey 
4711cecc5deSJohnathan Mantey 	/*
4721cecc5deSJohnathan Mantey 	 * We use clock_gettime(CLOCK_MONOTONIC) so we're immune to
4731cecc5deSJohnathan Mantey 	 * local time changes. However, a struct timeval is more
4741cecc5deSJohnathan Mantey 	 * convenient for calculations, so convert to that.
4751cecc5deSJohnathan Mantey 	 */
4761cecc5deSJohnathan Mantey 	rc = clock_gettime(CLOCK_MONOTONIC, &t);
4771cecc5deSJohnathan Mantey 	if (rc)
4781cecc5deSJohnathan Mantey 		return rc;
4791cecc5deSJohnathan Mantey 
4801cecc5deSJohnathan Mantey 	tv->tv_sec = t.tv_sec;
4811cecc5deSJohnathan Mantey 	tv->tv_usec = t.tv_nsec / 1000;
4821cecc5deSJohnathan Mantey 
4831cecc5deSJohnathan Mantey 	return 0;
4841cecc5deSJohnathan Mantey }
4851cecc5deSJohnathan Mantey 
486f733c85aSJeremy Kerr struct ringbuffer_consumer *console_ringbuffer_consumer_register(
487f733c85aSJeremy Kerr 		struct console *console,
488f733c85aSJeremy Kerr 		ringbuffer_poll_fn_t poll_fn, void *data)
489d831f960SJeremy Kerr {
490f733c85aSJeremy Kerr 	return ringbuffer_consumer_register(console->rb, poll_fn, data);
491d831f960SJeremy Kerr }
492d831f960SJeremy Kerr 
49355c9712dSJeremy Kerr struct poller *console_poller_register(struct console *console,
4941cecc5deSJohnathan Mantey 		struct handler *handler, poller_event_fn_t poller_fn,
4951cecc5deSJohnathan Mantey 		poller_timeout_fn_t timeout_fn, int fd,
4961cecc5deSJohnathan Mantey 		int events, void *data)
497d831f960SJeremy Kerr {
498329a35f5SJeremy Kerr 	struct poller *poller;
499329a35f5SJeremy Kerr 	int n;
500329a35f5SJeremy Kerr 
501329a35f5SJeremy Kerr 	poller = malloc(sizeof(*poller));
502329a35f5SJeremy Kerr 	poller->remove = false;
503329a35f5SJeremy Kerr 	poller->handler = handler;
5041cecc5deSJohnathan Mantey 	poller->event_fn = poller_fn;
5051cecc5deSJohnathan Mantey 	poller->timeout_fn = timeout_fn;
506329a35f5SJeremy Kerr 	poller->data = data;
507329a35f5SJeremy Kerr 
508329a35f5SJeremy Kerr 	/* add one to our pollers array */
509329a35f5SJeremy Kerr 	n = console->n_pollers++;
510329a35f5SJeremy Kerr 	console->pollers = realloc(console->pollers,
511329a35f5SJeremy Kerr 			sizeof(*console->pollers) * console->n_pollers);
512329a35f5SJeremy Kerr 
513329a35f5SJeremy Kerr 	console->pollers[n] = poller;
514329a35f5SJeremy Kerr 
515329a35f5SJeremy Kerr 	/* increase pollfds array too  */
516329a35f5SJeremy Kerr 	console->pollfds = realloc(console->pollfds,
517329a35f5SJeremy Kerr 			sizeof(*console->pollfds) *
518f9c8f6caSCheng C Yang 				(MAX_INTERNAL_POLLFD + console->n_pollers));
519329a35f5SJeremy Kerr 
520329a35f5SJeremy Kerr 	/* shift the end pollfds up by one */
521f9c8f6caSCheng C Yang 	memcpy(&console->pollfds[n+1],
522329a35f5SJeremy Kerr 		&console->pollfds[n],
523f9c8f6caSCheng C Yang 		sizeof(*console->pollfds) * MAX_INTERNAL_POLLFD);
524329a35f5SJeremy Kerr 
525329a35f5SJeremy Kerr 	console->pollfds[n].fd = fd;
526329a35f5SJeremy Kerr 	console->pollfds[n].events = events;
527329a35f5SJeremy Kerr 
528329a35f5SJeremy Kerr 	return poller;
529329a35f5SJeremy Kerr }
530329a35f5SJeremy Kerr 
53155c9712dSJeremy Kerr void console_poller_unregister(struct console *console,
532329a35f5SJeremy Kerr 		struct poller *poller)
533329a35f5SJeremy Kerr {
534329a35f5SJeremy Kerr 	int i;
535329a35f5SJeremy Kerr 
536329a35f5SJeremy Kerr 	/* find the entry in our pollers array */
537329a35f5SJeremy Kerr 	for (i = 0; i < console->n_pollers; i++)
538329a35f5SJeremy Kerr 		if (console->pollers[i] == poller)
539329a35f5SJeremy Kerr 			break;
540329a35f5SJeremy Kerr 
541329a35f5SJeremy Kerr 	assert(i < console->n_pollers);
542329a35f5SJeremy Kerr 
543329a35f5SJeremy Kerr 	console->n_pollers--;
544329a35f5SJeremy Kerr 
545329a35f5SJeremy Kerr 	/* remove the item from the pollers array... */
546329a35f5SJeremy Kerr 	memmove(&console->pollers[i], &console->pollers[i+1],
547329a35f5SJeremy Kerr 			sizeof(*console->pollers)
548329a35f5SJeremy Kerr 				* (console->n_pollers - i));
549329a35f5SJeremy Kerr 
550329a35f5SJeremy Kerr 	console->pollers = realloc(console->pollers,
551329a35f5SJeremy Kerr 			sizeof(*console->pollers) * console->n_pollers);
552329a35f5SJeremy Kerr 
553329a35f5SJeremy Kerr 	/* ... and the pollfds array */
554329a35f5SJeremy Kerr 	memmove(&console->pollfds[i], &console->pollfds[i+1],
555329a35f5SJeremy Kerr 			sizeof(*console->pollfds) *
556f9c8f6caSCheng C Yang 				(MAX_INTERNAL_POLLFD + console->n_pollers - i));
557329a35f5SJeremy Kerr 
558329a35f5SJeremy Kerr 	console->pollfds = realloc(console->pollfds,
559329a35f5SJeremy Kerr 			sizeof(*console->pollfds) *
560f9c8f6caSCheng C Yang 				(MAX_INTERNAL_POLLFD + console->n_pollers));
561329a35f5SJeremy Kerr 
562329a35f5SJeremy Kerr 
563329a35f5SJeremy Kerr 	free(poller);
564329a35f5SJeremy Kerr }
565329a35f5SJeremy Kerr 
5666b1fed27SJeremy Kerr void console_poller_set_events(struct console *console, struct poller *poller,
5676b1fed27SJeremy Kerr 		int events)
5686b1fed27SJeremy Kerr {
5696b1fed27SJeremy Kerr 	int i;
5706b1fed27SJeremy Kerr 
5716b1fed27SJeremy Kerr 	/* find the entry in our pollers array */
5726b1fed27SJeremy Kerr 	for (i = 0; i < console->n_pollers; i++)
5736b1fed27SJeremy Kerr 		if (console->pollers[i] == poller)
5746b1fed27SJeremy Kerr 			break;
5756b1fed27SJeremy Kerr 
5766b1fed27SJeremy Kerr 	console->pollfds[i].events = events;
5776b1fed27SJeremy Kerr }
5786b1fed27SJeremy Kerr 
579*fd048328SAndrew Jeffery void console_poller_set_timeout(struct console *console __attribute__((unused)),
580*fd048328SAndrew Jeffery 		struct poller *poller, const struct timeval *tv)
5811cecc5deSJohnathan Mantey {
5821cecc5deSJohnathan Mantey 	struct timeval now;
5831cecc5deSJohnathan Mantey 	int rc;
5841cecc5deSJohnathan Mantey 
5851cecc5deSJohnathan Mantey 	rc = get_current_time(&now);
5861cecc5deSJohnathan Mantey 	if (rc)
5871cecc5deSJohnathan Mantey 		return;
5881cecc5deSJohnathan Mantey 
5891cecc5deSJohnathan Mantey 	timeradd(&now, tv, &poller->timeout);
5901cecc5deSJohnathan Mantey }
5911cecc5deSJohnathan Mantey 
5921cecc5deSJohnathan Mantey static int get_poll_timeout(struct console *console, struct timeval *cur_time)
5931cecc5deSJohnathan Mantey {
5941cecc5deSJohnathan Mantey 	struct timeval *earliest, interval;
5951cecc5deSJohnathan Mantey 	struct poller *poller;
5961cecc5deSJohnathan Mantey 	int i;
5971cecc5deSJohnathan Mantey 
5981cecc5deSJohnathan Mantey 	earliest = NULL;
5991cecc5deSJohnathan Mantey 
6001cecc5deSJohnathan Mantey 	for (i = 0; i < console->n_pollers; i++) {
6011cecc5deSJohnathan Mantey 		poller = console->pollers[i];
6021cecc5deSJohnathan Mantey 
6031cecc5deSJohnathan Mantey 		if (poller->timeout_fn && timerisset(&poller->timeout) &&
6041cecc5deSJohnathan Mantey 		    (!earliest ||
6051cecc5deSJohnathan Mantey 		     (earliest && timercmp(&poller->timeout, earliest, <)))){
6061cecc5deSJohnathan Mantey 			// poller is buffering data and needs the poll
6071cecc5deSJohnathan Mantey 			// function to timeout.
6081cecc5deSJohnathan Mantey 			earliest = &poller->timeout;
6091cecc5deSJohnathan Mantey 		}
6101cecc5deSJohnathan Mantey 	}
6111cecc5deSJohnathan Mantey 
6121cecc5deSJohnathan Mantey 	if (earliest) {
6131cecc5deSJohnathan Mantey 		if (timercmp(earliest, cur_time, >)) {
6141cecc5deSJohnathan Mantey 			/* recalculate the timeout period, time period has
6151cecc5deSJohnathan Mantey 			 * not elapsed */
6161cecc5deSJohnathan Mantey 			timersub(earliest, cur_time, &interval);
6171cecc5deSJohnathan Mantey 			return ((interval.tv_sec * 1000) +
6181cecc5deSJohnathan Mantey 				(interval.tv_usec / 1000));
6191cecc5deSJohnathan Mantey 		} else {
6201cecc5deSJohnathan Mantey 			/* return from poll immediately */
6211cecc5deSJohnathan Mantey 			return 0;
6221cecc5deSJohnathan Mantey 		}
6231cecc5deSJohnathan Mantey 	} else {
6241cecc5deSJohnathan Mantey 		/* poll indefinitely */
6251cecc5deSJohnathan Mantey 		return -1;
6261cecc5deSJohnathan Mantey 	}
6271cecc5deSJohnathan Mantey }
6281cecc5deSJohnathan Mantey 
6291cecc5deSJohnathan Mantey static int call_pollers(struct console *console, struct timeval *cur_time)
630329a35f5SJeremy Kerr {
631329a35f5SJeremy Kerr 	struct poller *poller;
632329a35f5SJeremy Kerr 	struct pollfd *pollfd;
633329a35f5SJeremy Kerr 	enum poller_ret prc;
634329a35f5SJeremy Kerr 	int i, rc;
635d831f960SJeremy Kerr 
6361a0e03b4SJeremy Kerr 	rc = 0;
6371a0e03b4SJeremy Kerr 
638329a35f5SJeremy Kerr 	/*
639329a35f5SJeremy Kerr 	 * Process poll events by iterating through the pollers and pollfds
640329a35f5SJeremy Kerr 	 * in-step, calling any pollers that we've found revents for.
641329a35f5SJeremy Kerr 	 */
642329a35f5SJeremy Kerr 	for (i = 0; i < console->n_pollers; i++) {
643329a35f5SJeremy Kerr 		poller = console->pollers[i];
644329a35f5SJeremy Kerr 		pollfd = &console->pollfds[i];
6451cecc5deSJohnathan Mantey 		prc = POLLER_OK;
6461a0e03b4SJeremy Kerr 
6471cecc5deSJohnathan Mantey 		/* process pending events... */
6481cecc5deSJohnathan Mantey 		if (pollfd->revents) {
6491cecc5deSJohnathan Mantey 			prc = poller->event_fn(poller->handler, pollfd->revents,
650329a35f5SJeremy Kerr 					poller->data);
651329a35f5SJeremy Kerr 			if (prc == POLLER_EXIT)
652329a35f5SJeremy Kerr 				rc = -1;
653329a35f5SJeremy Kerr 			else if (prc == POLLER_REMOVE)
654329a35f5SJeremy Kerr 				poller->remove = true;
655329a35f5SJeremy Kerr 		}
656329a35f5SJeremy Kerr 
6571cecc5deSJohnathan Mantey 		if ((prc == POLLER_OK) && poller->timeout_fn &&
6581cecc5deSJohnathan Mantey 		    timerisset(&poller->timeout) &&
6591cecc5deSJohnathan Mantey 		    timercmp(&poller->timeout, cur_time, <=)) {
6601cecc5deSJohnathan Mantey 			/* One of the ringbuffer consumers is buffering the
6611cecc5deSJohnathan Mantey 			data stream. The amount of idle time the consumer
6621cecc5deSJohnathan Mantey 			desired has expired.  Process the buffered data for
6631cecc5deSJohnathan Mantey 			transmission. */
6641cecc5deSJohnathan Mantey 			timerclear(&poller->timeout);
6651cecc5deSJohnathan Mantey 			prc = poller->timeout_fn(poller->handler, poller->data);
6661cecc5deSJohnathan Mantey 			if (prc == POLLER_EXIT) {
6671cecc5deSJohnathan Mantey 				rc = -1;
6681cecc5deSJohnathan Mantey 			} else if (prc == POLLER_REMOVE) {
6691cecc5deSJohnathan Mantey 				poller->remove = true;
6701cecc5deSJohnathan Mantey 			}
6711cecc5deSJohnathan Mantey 		}
6721cecc5deSJohnathan Mantey 	}
6731cecc5deSJohnathan Mantey 
674329a35f5SJeremy Kerr 	/**
675329a35f5SJeremy Kerr 	 * Process deferred removals; restarting each time we unregister, as
676329a35f5SJeremy Kerr 	 * the array will have changed
677329a35f5SJeremy Kerr 	 */
678329a35f5SJeremy Kerr 	for (;;) {
679329a35f5SJeremy Kerr 		bool removed = false;
680329a35f5SJeremy Kerr 
681329a35f5SJeremy Kerr 		for (i = 0; i < console->n_pollers; i++) {
682329a35f5SJeremy Kerr 			poller = console->pollers[i];
683329a35f5SJeremy Kerr 			if (poller->remove) {
68455c9712dSJeremy Kerr 				console_poller_unregister(console, poller);
685329a35f5SJeremy Kerr 				removed = true;
686329a35f5SJeremy Kerr 				break;
687329a35f5SJeremy Kerr 			}
688329a35f5SJeremy Kerr 		}
689329a35f5SJeremy Kerr 		if (!removed)
690329a35f5SJeremy Kerr 			break;
6911a0e03b4SJeremy Kerr 	}
6921a0e03b4SJeremy Kerr 
6931a0e03b4SJeremy Kerr 	return rc;
6941a0e03b4SJeremy Kerr }
6951a0e03b4SJeremy Kerr 
696769cee1aSJeremy Kerr static void sighandler(int signal)
697769cee1aSJeremy Kerr {
698769cee1aSJeremy Kerr 	if (signal == SIGINT)
699769cee1aSJeremy Kerr 		sigint = true;
700769cee1aSJeremy Kerr }
701769cee1aSJeremy Kerr 
7021a0e03b4SJeremy Kerr int run_console(struct console *console)
7031a0e03b4SJeremy Kerr {
704769cee1aSJeremy Kerr 	sighandler_t sighandler_save;
7051cecc5deSJohnathan Mantey 	struct timeval tv;
7061cecc5deSJohnathan Mantey 	int rc, timeout;
707d831f960SJeremy Kerr 
708769cee1aSJeremy Kerr 	sighandler_save = signal(SIGINT, sighandler);
709769cee1aSJeremy Kerr 
710769cee1aSJeremy Kerr 	rc = 0;
711769cee1aSJeremy Kerr 
712d831f960SJeremy Kerr 	for (;;) {
713d831f960SJeremy Kerr 		uint8_t buf[4096];
714d831f960SJeremy Kerr 
7151764145dSJeremy Kerr 		BUILD_ASSERT(sizeof(buf) <= buffer_size);
7161764145dSJeremy Kerr 
717769cee1aSJeremy Kerr 		if (sigint) {
718769cee1aSJeremy Kerr 			fprintf(stderr, "Received interrupt, exiting\n");
719769cee1aSJeremy Kerr 			break;
720769cee1aSJeremy Kerr 		}
721769cee1aSJeremy Kerr 
7221cecc5deSJohnathan Mantey 		rc = get_current_time(&tv);
7231cecc5deSJohnathan Mantey 		if (rc) {
7241cecc5deSJohnathan Mantey 			warn("Failed to read current time");
7251cecc5deSJohnathan Mantey 			break;
7261cecc5deSJohnathan Mantey 		}
7271cecc5deSJohnathan Mantey 
7281cecc5deSJohnathan Mantey 		timeout = get_poll_timeout(console, &tv);
7291cecc5deSJohnathan Mantey 
730329a35f5SJeremy Kerr 		rc = poll(console->pollfds,
7311cecc5deSJohnathan Mantey 				console->n_pollers + MAX_INTERNAL_POLLFD,
7321cecc5deSJohnathan Mantey 				timeout);
7331cecc5deSJohnathan Mantey 
734d831f960SJeremy Kerr 		if (rc < 0) {
735769cee1aSJeremy Kerr 			if (errno == EINTR) {
736769cee1aSJeremy Kerr 				continue;
737769cee1aSJeremy Kerr 			} else {
738d831f960SJeremy Kerr 				warn("poll error");
739769cee1aSJeremy Kerr 				break;
740769cee1aSJeremy Kerr 			}
741d831f960SJeremy Kerr 		}
742d831f960SJeremy Kerr 
743329a35f5SJeremy Kerr 		/* process internal fd first */
744329a35f5SJeremy Kerr 		if (console->pollfds[console->n_pollers].revents) {
7451a0e03b4SJeremy Kerr 			rc = read(console->tty_fd, buf, sizeof(buf));
746d831f960SJeremy Kerr 			if (rc <= 0) {
747d831f960SJeremy Kerr 				warn("Error reading from tty device");
748769cee1aSJeremy Kerr 				rc = -1;
749769cee1aSJeremy Kerr 				break;
750d831f960SJeremy Kerr 			}
751f733c85aSJeremy Kerr 			rc = ringbuffer_queue(console->rb, buf, rc);
7521a0e03b4SJeremy Kerr 			if (rc)
753769cee1aSJeremy Kerr 				break;
754d831f960SJeremy Kerr 		}
755d831f960SJeremy Kerr 
756f9c8f6caSCheng C Yang 		if (console->pollfds[console->n_pollers + 1].revents) {
757f9c8f6caSCheng C Yang 			sd_bus_process(console->bus, NULL);
758f9c8f6caSCheng C Yang 		}
759f9c8f6caSCheng C Yang 
760329a35f5SJeremy Kerr 		/* ... and then the pollers */
7611cecc5deSJohnathan Mantey 		rc = call_pollers(console, &tv);
7621a0e03b4SJeremy Kerr 		if (rc)
763769cee1aSJeremy Kerr 			break;
7641a0e03b4SJeremy Kerr 	}
765769cee1aSJeremy Kerr 
766769cee1aSJeremy Kerr 	signal(SIGINT, sighandler_save);
767f9c8f6caSCheng C Yang 	sd_bus_unref(console->bus);
768769cee1aSJeremy Kerr 
769769cee1aSJeremy Kerr 	return rc ? -1 : 0;
7701a0e03b4SJeremy Kerr }
771d831f960SJeremy Kerr static const struct option options[] = {
772d66195c1SJeremy Kerr 	{ "config",	required_argument,	0, 'c'},
773f5858b5bSJoel Stanley 	{ 0,  0, 0, 0},
774d831f960SJeremy Kerr };
775d831f960SJeremy Kerr 
776d831f960SJeremy Kerr int main(int argc, char **argv)
777d831f960SJeremy Kerr {
778d66195c1SJeremy Kerr 	const char *config_filename = NULL;
7796221ce94SVishwanatha Subbanna 	const char *config_tty_kname = NULL;
7801a0e03b4SJeremy Kerr 	struct console *console;
781d66195c1SJeremy Kerr 	struct config *config;
782d66195c1SJeremy Kerr 	int rc;
783d831f960SJeremy Kerr 
784957818b4SJeremy Kerr 	rc = -1;
785d831f960SJeremy Kerr 
786d831f960SJeremy Kerr 	for (;;) {
787d831f960SJeremy Kerr 		int c, idx;
788d831f960SJeremy Kerr 
789d66195c1SJeremy Kerr 		c = getopt_long(argc, argv, "c:", options, &idx);
790d831f960SJeremy Kerr 		if (c == -1)
791d831f960SJeremy Kerr 			break;
792d831f960SJeremy Kerr 
793d831f960SJeremy Kerr 		switch (c) {
794d66195c1SJeremy Kerr 		case 'c':
795d66195c1SJeremy Kerr 			config_filename = optarg;
796d831f960SJeremy Kerr 			break;
797d831f960SJeremy Kerr 		case 'h':
798d831f960SJeremy Kerr 		case '?':
799d831f960SJeremy Kerr 			usage(argv[0]);
800d66195c1SJeremy Kerr 			return EXIT_SUCCESS;
801d831f960SJeremy Kerr 		}
802d831f960SJeremy Kerr 	}
803d831f960SJeremy Kerr 
80491dde14eSAndrew Jeffery 	if (optind < argc)
8056221ce94SVishwanatha Subbanna 		config_tty_kname = argv[optind];
8066221ce94SVishwanatha Subbanna 
807d66195c1SJeremy Kerr 	console = malloc(sizeof(struct console));
808d66195c1SJeremy Kerr 	memset(console, 0, sizeof(*console));
809f9c8f6caSCheng C Yang 	console->pollfds = calloc(MAX_INTERNAL_POLLFD,
810329a35f5SJeremy Kerr 			sizeof(*console->pollfds));
811f733c85aSJeremy Kerr 	console->rb = ringbuffer_init(buffer_size);
812329a35f5SJeremy Kerr 
813d66195c1SJeremy Kerr 	config = config_init(config_filename);
814d66195c1SJeremy Kerr 	if (!config) {
815d66195c1SJeremy Kerr 		warnx("Can't read configuration, exiting.");
816d66195c1SJeremy Kerr 		goto out_free;
817d831f960SJeremy Kerr 	}
818d831f960SJeremy Kerr 
81991dde14eSAndrew Jeffery 	if (!config_tty_kname)
82091dde14eSAndrew Jeffery 		config_tty_kname = config_get_value(config, "upstream-tty");
82191dde14eSAndrew Jeffery 
82291dde14eSAndrew Jeffery 	if (!config_tty_kname) {
82391dde14eSAndrew Jeffery 		warnx("No TTY device specified");
82491dde14eSAndrew Jeffery 		usage(argv[0]);
82591dde14eSAndrew Jeffery 		return EXIT_FAILURE;
82691dde14eSAndrew Jeffery 	}
82791dde14eSAndrew Jeffery 
8286221ce94SVishwanatha Subbanna 	console->tty_kname = config_tty_kname;
8296221ce94SVishwanatha Subbanna 
830d66195c1SJeremy Kerr 	rc = tty_init(console, config);
83117217845SJeremy Kerr 	if (rc)
832d66195c1SJeremy Kerr 		goto out_config_fini;
833d831f960SJeremy Kerr 
834f9c8f6caSCheng C Yang 	dbus_init(console, config);
835f9c8f6caSCheng C Yang 
836d47963e5SJeremy Kerr 	handlers_init(console, config);
837d831f960SJeremy Kerr 
8381a0e03b4SJeremy Kerr 	rc = run_console(console);
839d831f960SJeremy Kerr 
8401a0e03b4SJeremy Kerr 	handlers_fini(console);
841d831f960SJeremy Kerr 
842d66195c1SJeremy Kerr out_config_fini:
843d66195c1SJeremy Kerr 	config_fini(config);
844d66195c1SJeremy Kerr 
845957818b4SJeremy Kerr out_free:
84689ea8198SJeremy Kerr 	free(console->pollers);
84789ea8198SJeremy Kerr 	free(console->pollfds);
8481a0e03b4SJeremy Kerr 	free(console->tty_sysfs_devnode);
8491a0e03b4SJeremy Kerr 	free(console->tty_dev);
8501a0e03b4SJeremy Kerr 	free(console);
851d831f960SJeremy Kerr 
852d831f960SJeremy Kerr 	return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
853d831f960SJeremy Kerr }
854