xref: /openbmc/obmc-console/console-server.c (revision a72711af868568481db9767db902bbbacc2d62d9)
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);
12715792aa7SAndrew 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);
13415792aa7SAndrew Jeffery 		rc = -1;
13545ad7676SYi Li 		goto out_free;
13645ad7676SYi Li 	}
13745ad7676SYi Li 
138*a72711afSAndrew Jeffery 	rc = asprintf(&tty_class_device_link, "/sys/class/tty/%s",
139*a72711afSAndrew Jeffery 		      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);
14615792aa7SAndrew 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) {
186*a72711afSAndrew Jeffery 		warn("Can't access attribute %s on device %s", name,
187*a72711afSAndrew Jeffery 		     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)
195*a72711afSAndrew Jeffery 		warn("Error writing to %s attribute of device %s", name,
196*a72711afSAndrew Jeffery 		     console->tty_kname);
197957818b4SJeremy Kerr 	fclose(fp);
198957818b4SJeremy Kerr 
199957818b4SJeremy Kerr out_free:
200957818b4SJeremy Kerr 	free(path);
201957818b4SJeremy Kerr 	return rc;
202957818b4SJeremy Kerr }
203957818b4SJeremy Kerr 
204d831f960SJeremy Kerr /**
205c7fbcd48SBenjamin Fair  * Set termios attributes on the console tty.
20654e9569dSJeremy Kerr  */
20754e9569dSJeremy Kerr static void tty_init_termios(struct console *console)
20854e9569dSJeremy Kerr {
20954e9569dSJeremy Kerr 	struct termios termios;
21054e9569dSJeremy Kerr 	int rc;
21154e9569dSJeremy Kerr 
21254e9569dSJeremy Kerr 	rc = tcgetattr(console->tty_fd, &termios);
21354e9569dSJeremy Kerr 	if (rc) {
21454e9569dSJeremy Kerr 		warn("Can't read tty termios");
21554e9569dSJeremy Kerr 		return;
21654e9569dSJeremy Kerr 	}
21754e9569dSJeremy Kerr 
218c7fbcd48SBenjamin Fair 	if (console->tty_baud) {
219c7fbcd48SBenjamin Fair 		if (cfsetspeed(&termios, console->tty_baud) < 0)
220c7fbcd48SBenjamin Fair 			warn("Couldn't set speeds for %s", console->tty_kname);
221c7fbcd48SBenjamin Fair 	}
222c7fbcd48SBenjamin Fair 
223c7fbcd48SBenjamin Fair 	/* Set console to raw mode: we don't want any processing to occur on
224c7fbcd48SBenjamin Fair 	 * the underlying terminal input/output.
225c7fbcd48SBenjamin Fair 	 */
22654e9569dSJeremy Kerr 	cfmakeraw(&termios);
227c7fbcd48SBenjamin Fair 
22854e9569dSJeremy Kerr 	rc = tcsetattr(console->tty_fd, TCSANOW, &termios);
22954e9569dSJeremy Kerr 	if (rc)
230c7fbcd48SBenjamin Fair 		warn("Can't set terminal options for %s", console->tty_kname);
23154e9569dSJeremy Kerr }
23254e9569dSJeremy Kerr 
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 int console_data_out(struct console *console, const uint8_t *data, size_t len)
324d831f960SJeremy Kerr {
3251a0e03b4SJeremy Kerr 	return write_buf_to_fd(console->tty_fd, data, len);
326d831f960SJeremy Kerr }
327d831f960SJeremy Kerr 
328f9c8f6caSCheng C Yang static int method_set_baud_rate(sd_bus_message *msg, void *userdata,
329f9c8f6caSCheng C Yang 				sd_bus_error *err)
330f9c8f6caSCheng C Yang {
331f9c8f6caSCheng C Yang 	struct console *console = userdata;
332f9c8f6caSCheng C Yang 	uint32_t baudrate;
333f9c8f6caSCheng C Yang 	speed_t speed;
334f9c8f6caSCheng C Yang 	int r;
335f9c8f6caSCheng C Yang 
336f9c8f6caSCheng C Yang 	if (!console) {
337f9c8f6caSCheng C Yang 		sd_bus_error_set_const(err, DBUS_ERR, "Internal error");
338f9c8f6caSCheng C Yang 		return sd_bus_reply_method_return(msg, "x", 0);
339f9c8f6caSCheng C Yang 	}
340f9c8f6caSCheng C Yang 
341f9c8f6caSCheng C Yang 	r = sd_bus_message_read(msg, "u", &baudrate);
342f9c8f6caSCheng C Yang 	if (r < 0) {
343f9c8f6caSCheng C Yang 		sd_bus_error_set_const(err, DBUS_ERR, "Bad message");
344f9c8f6caSCheng C Yang 		return sd_bus_reply_method_return(msg, "x", -EINVAL);
345f9c8f6caSCheng C Yang 	}
346f9c8f6caSCheng C Yang 
347f9c8f6caSCheng C Yang 	speed = parse_int_to_baud(baudrate);
348f9c8f6caSCheng C Yang 	if (!speed) {
349f9c8f6caSCheng C Yang 		warnx("Invalid baud rate: '%u'", baudrate);
350f9c8f6caSCheng C Yang 		return sd_bus_reply_method_return(msg, "x", -EINVAL);
351f9c8f6caSCheng C Yang 	}
352f9c8f6caSCheng C Yang 
353f9c8f6caSCheng C Yang 	console->tty_baud = speed;
354f9c8f6caSCheng C Yang 	tty_change_baudrate(console);
355f9c8f6caSCheng C Yang 
356f9c8f6caSCheng C Yang 	return sd_bus_reply_method_return(msg, "x", r);
357f9c8f6caSCheng C Yang }
358f9c8f6caSCheng C Yang 
359fd048328SAndrew Jeffery static int get_handler(sd_bus *bus __attribute__((unused)),
360fd048328SAndrew Jeffery 		       const char *path __attribute__((unused)),
361fd048328SAndrew Jeffery 		       const char *interface __attribute__((unused)),
362fd048328SAndrew Jeffery 		       const char *property __attribute__((unused)),
363fd048328SAndrew Jeffery 		       sd_bus_message *reply, void *userdata,
364*a72711afSAndrew Jeffery 		       sd_bus_error *error __attribute__((unused)))
365*a72711afSAndrew Jeffery {
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),
384*a72711afSAndrew Jeffery 	SD_BUS_VTABLE_END,
385*a72711afSAndrew Jeffery };
386f9c8f6caSCheng C Yang 
387*a72711afSAndrew Jeffery static void dbus_init(struct console *console,
388*a72711afSAndrew Jeffery 		      struct config *config __attribute__((unused)))
389f9c8f6caSCheng C Yang {
390f9c8f6caSCheng C Yang 	int dbus_poller = 0;
391f9c8f6caSCheng C Yang 	int fd, r;
392f9c8f6caSCheng C Yang 
393f9c8f6caSCheng C Yang 	if (!console) {
394f9c8f6caSCheng C Yang 		warnx("Couldn't get valid console");
395f9c8f6caSCheng C Yang 		return;
396f9c8f6caSCheng C Yang 	}
397f9c8f6caSCheng C Yang 
398f9c8f6caSCheng C Yang 	r = sd_bus_default_system(&console->bus);
399f9c8f6caSCheng C Yang 	if (r < 0) {
400f9c8f6caSCheng C Yang 		warnx("Failed to connect to system bus: %s", strerror(-r));
401f9c8f6caSCheng C Yang 		return;
402f9c8f6caSCheng C Yang 	}
403f9c8f6caSCheng C Yang 
404f9c8f6caSCheng C Yang 	r = sd_bus_add_object_vtable(console->bus, NULL, OBJ_NAME, DBUS_NAME,
405f9c8f6caSCheng C Yang 				     console_vtable, console);
406f9c8f6caSCheng C Yang 	if (r < 0) {
407f9c8f6caSCheng C Yang 		warnx("Failed to issue method call: %s", strerror(-r));
408f9c8f6caSCheng C Yang 		return;
409f9c8f6caSCheng C Yang 	}
410f9c8f6caSCheng C Yang 
411*a72711afSAndrew Jeffery 	r = sd_bus_request_name(console->bus, DBUS_NAME,
412*a72711afSAndrew Jeffery 				SD_BUS_NAME_ALLOW_REPLACEMENT |
413*a72711afSAndrew Jeffery 					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 
489*a72711afSAndrew Jeffery struct ringbuffer_consumer *
490*a72711afSAndrew Jeffery console_ringbuffer_consumer_register(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,
497*a72711afSAndrew Jeffery 				       struct handler *handler,
498*a72711afSAndrew Jeffery 				       poller_event_fn_t poller_fn,
4991cecc5deSJohnathan Mantey 				       poller_timeout_fn_t timeout_fn, int fd,
5001cecc5deSJohnathan Mantey 				       int events, void *data)
501d831f960SJeremy Kerr {
502329a35f5SJeremy Kerr 	struct poller *poller;
503329a35f5SJeremy Kerr 	int n;
504329a35f5SJeremy Kerr 
505329a35f5SJeremy Kerr 	poller = malloc(sizeof(*poller));
506329a35f5SJeremy Kerr 	poller->remove = false;
507329a35f5SJeremy Kerr 	poller->handler = handler;
5081cecc5deSJohnathan Mantey 	poller->event_fn = poller_fn;
5091cecc5deSJohnathan Mantey 	poller->timeout_fn = timeout_fn;
510329a35f5SJeremy Kerr 	poller->data = data;
511329a35f5SJeremy Kerr 
512329a35f5SJeremy Kerr 	/* add one to our pollers array */
513329a35f5SJeremy Kerr 	n = console->n_pollers++;
514*a72711afSAndrew Jeffery 	console->pollers =
515*a72711afSAndrew Jeffery 		realloc(console->pollers,
516329a35f5SJeremy Kerr 			sizeof(*console->pollers) * console->n_pollers);
517329a35f5SJeremy Kerr 
518329a35f5SJeremy Kerr 	console->pollers[n] = poller;
519329a35f5SJeremy Kerr 
520329a35f5SJeremy Kerr 	/* increase pollfds array too  */
521*a72711afSAndrew Jeffery 	console->pollfds =
522*a72711afSAndrew Jeffery 		realloc(console->pollfds,
523329a35f5SJeremy Kerr 			sizeof(*console->pollfds) *
524f9c8f6caSCheng C Yang 				(MAX_INTERNAL_POLLFD + console->n_pollers));
525329a35f5SJeremy Kerr 
526329a35f5SJeremy Kerr 	/* shift the end pollfds up by one */
527*a72711afSAndrew Jeffery 	memcpy(&console->pollfds[n + 1], &console->pollfds[n],
528f9c8f6caSCheng C Yang 	       sizeof(*console->pollfds) * MAX_INTERNAL_POLLFD);
529329a35f5SJeremy Kerr 
530329a35f5SJeremy Kerr 	console->pollfds[n].fd = fd;
531329a35f5SJeremy Kerr 	console->pollfds[n].events = events;
532329a35f5SJeremy Kerr 
533329a35f5SJeremy Kerr 	return poller;
534329a35f5SJeremy Kerr }
535329a35f5SJeremy Kerr 
536*a72711afSAndrew Jeffery void console_poller_unregister(struct console *console, struct poller *poller)
537329a35f5SJeremy Kerr {
538329a35f5SJeremy Kerr 	int i;
539329a35f5SJeremy Kerr 
540329a35f5SJeremy Kerr 	/* find the entry in our pollers array */
541329a35f5SJeremy Kerr 	for (i = 0; i < console->n_pollers; i++)
542329a35f5SJeremy Kerr 		if (console->pollers[i] == poller)
543329a35f5SJeremy Kerr 			break;
544329a35f5SJeremy Kerr 
545329a35f5SJeremy Kerr 	assert(i < console->n_pollers);
546329a35f5SJeremy Kerr 
547329a35f5SJeremy Kerr 	console->n_pollers--;
548329a35f5SJeremy Kerr 
549329a35f5SJeremy Kerr 	/* remove the item from the pollers array... */
550329a35f5SJeremy Kerr 	memmove(&console->pollers[i], &console->pollers[i + 1],
551*a72711afSAndrew Jeffery 		sizeof(*console->pollers) * (console->n_pollers - i));
552329a35f5SJeremy Kerr 
553*a72711afSAndrew Jeffery 	console->pollers =
554*a72711afSAndrew Jeffery 		realloc(console->pollers,
555329a35f5SJeremy Kerr 			sizeof(*console->pollers) * console->n_pollers);
556329a35f5SJeremy Kerr 
557329a35f5SJeremy Kerr 	/* ... and the pollfds array */
558329a35f5SJeremy Kerr 	memmove(&console->pollfds[i], &console->pollfds[i + 1],
559329a35f5SJeremy Kerr 		sizeof(*console->pollfds) *
560f9c8f6caSCheng C Yang 			(MAX_INTERNAL_POLLFD + console->n_pollers - i));
561329a35f5SJeremy Kerr 
562*a72711afSAndrew Jeffery 	console->pollfds =
563*a72711afSAndrew Jeffery 		realloc(console->pollfds,
564329a35f5SJeremy Kerr 			sizeof(*console->pollfds) *
565f9c8f6caSCheng C Yang 				(MAX_INTERNAL_POLLFD + console->n_pollers));
566329a35f5SJeremy Kerr 
567329a35f5SJeremy Kerr 	free(poller);
568329a35f5SJeremy Kerr }
569329a35f5SJeremy Kerr 
5706b1fed27SJeremy Kerr void console_poller_set_events(struct console *console, struct poller *poller,
5716b1fed27SJeremy Kerr 			       int events)
5726b1fed27SJeremy Kerr {
5736b1fed27SJeremy Kerr 	int i;
5746b1fed27SJeremy Kerr 
5756b1fed27SJeremy Kerr 	/* find the entry in our pollers array */
5766b1fed27SJeremy Kerr 	for (i = 0; i < console->n_pollers; i++)
5776b1fed27SJeremy Kerr 		if (console->pollers[i] == poller)
5786b1fed27SJeremy Kerr 			break;
5796b1fed27SJeremy Kerr 
5806b1fed27SJeremy Kerr 	console->pollfds[i].events = events;
5816b1fed27SJeremy Kerr }
5826b1fed27SJeremy Kerr 
583fd048328SAndrew Jeffery void console_poller_set_timeout(struct console *console __attribute__((unused)),
584fd048328SAndrew Jeffery 				struct poller *poller, const struct timeval *tv)
5851cecc5deSJohnathan Mantey {
5861cecc5deSJohnathan Mantey 	struct timeval now;
5871cecc5deSJohnathan Mantey 	int rc;
5881cecc5deSJohnathan Mantey 
5891cecc5deSJohnathan Mantey 	rc = get_current_time(&now);
5901cecc5deSJohnathan Mantey 	if (rc)
5911cecc5deSJohnathan Mantey 		return;
5921cecc5deSJohnathan Mantey 
5931cecc5deSJohnathan Mantey 	timeradd(&now, tv, &poller->timeout);
5941cecc5deSJohnathan Mantey }
5951cecc5deSJohnathan Mantey 
5961cecc5deSJohnathan Mantey static int get_poll_timeout(struct console *console, struct timeval *cur_time)
5971cecc5deSJohnathan Mantey {
5981cecc5deSJohnathan Mantey 	struct timeval *earliest, interval;
5991cecc5deSJohnathan Mantey 	struct poller *poller;
6001cecc5deSJohnathan Mantey 	int i;
6011cecc5deSJohnathan Mantey 
6021cecc5deSJohnathan Mantey 	earliest = NULL;
6031cecc5deSJohnathan Mantey 
6041cecc5deSJohnathan Mantey 	for (i = 0; i < console->n_pollers; i++) {
6051cecc5deSJohnathan Mantey 		poller = console->pollers[i];
6061cecc5deSJohnathan Mantey 
6071cecc5deSJohnathan Mantey 		if (poller->timeout_fn && timerisset(&poller->timeout) &&
6081cecc5deSJohnathan Mantey 		    (!earliest ||
6091cecc5deSJohnathan Mantey 		     (earliest && timercmp(&poller->timeout, earliest, <)))) {
6101cecc5deSJohnathan Mantey 			// poller is buffering data and needs the poll
6111cecc5deSJohnathan Mantey 			// function to timeout.
6121cecc5deSJohnathan Mantey 			earliest = &poller->timeout;
6131cecc5deSJohnathan Mantey 		}
6141cecc5deSJohnathan Mantey 	}
6151cecc5deSJohnathan Mantey 
6161cecc5deSJohnathan Mantey 	if (earliest) {
6171cecc5deSJohnathan Mantey 		if (timercmp(earliest, cur_time, >)) {
6181cecc5deSJohnathan Mantey 			/* recalculate the timeout period, time period has
6191cecc5deSJohnathan Mantey 			 * not elapsed */
6201cecc5deSJohnathan Mantey 			timersub(earliest, cur_time, &interval);
6211cecc5deSJohnathan Mantey 			return ((interval.tv_sec * 1000) +
6221cecc5deSJohnathan Mantey 				(interval.tv_usec / 1000));
6231cecc5deSJohnathan Mantey 		} else {
6241cecc5deSJohnathan Mantey 			/* return from poll immediately */
6251cecc5deSJohnathan Mantey 			return 0;
6261cecc5deSJohnathan Mantey 		}
6271cecc5deSJohnathan Mantey 	} else {
6281cecc5deSJohnathan Mantey 		/* poll indefinitely */
6291cecc5deSJohnathan Mantey 		return -1;
6301cecc5deSJohnathan Mantey 	}
6311cecc5deSJohnathan Mantey }
6321cecc5deSJohnathan Mantey 
6331cecc5deSJohnathan Mantey static int call_pollers(struct console *console, struct timeval *cur_time)
634329a35f5SJeremy Kerr {
635329a35f5SJeremy Kerr 	struct poller *poller;
636329a35f5SJeremy Kerr 	struct pollfd *pollfd;
637329a35f5SJeremy Kerr 	enum poller_ret prc;
638329a35f5SJeremy Kerr 	int i, rc;
639d831f960SJeremy Kerr 
6401a0e03b4SJeremy Kerr 	rc = 0;
6411a0e03b4SJeremy Kerr 
642329a35f5SJeremy Kerr 	/*
643329a35f5SJeremy Kerr 	 * Process poll events by iterating through the pollers and pollfds
644329a35f5SJeremy Kerr 	 * in-step, calling any pollers that we've found revents for.
645329a35f5SJeremy Kerr 	 */
646329a35f5SJeremy Kerr 	for (i = 0; i < console->n_pollers; i++) {
647329a35f5SJeremy Kerr 		poller = console->pollers[i];
648329a35f5SJeremy Kerr 		pollfd = &console->pollfds[i];
6491cecc5deSJohnathan Mantey 		prc = POLLER_OK;
6501a0e03b4SJeremy Kerr 
6511cecc5deSJohnathan Mantey 		/* process pending events... */
6521cecc5deSJohnathan Mantey 		if (pollfd->revents) {
6531cecc5deSJohnathan Mantey 			prc = poller->event_fn(poller->handler, pollfd->revents,
654329a35f5SJeremy Kerr 					       poller->data);
655329a35f5SJeremy Kerr 			if (prc == POLLER_EXIT)
656329a35f5SJeremy Kerr 				rc = -1;
657329a35f5SJeremy Kerr 			else if (prc == POLLER_REMOVE)
658329a35f5SJeremy Kerr 				poller->remove = true;
659329a35f5SJeremy Kerr 		}
660329a35f5SJeremy Kerr 
6611cecc5deSJohnathan Mantey 		if ((prc == POLLER_OK) && poller->timeout_fn &&
6621cecc5deSJohnathan Mantey 		    timerisset(&poller->timeout) &&
6631cecc5deSJohnathan Mantey 		    timercmp(&poller->timeout, cur_time, <=)) {
6641cecc5deSJohnathan Mantey 			/* One of the ringbuffer consumers is buffering the
6651cecc5deSJohnathan Mantey 			data stream. The amount of idle time the consumer
6661cecc5deSJohnathan Mantey 			desired has expired.  Process the buffered data for
6671cecc5deSJohnathan Mantey 			transmission. */
6681cecc5deSJohnathan Mantey 			timerclear(&poller->timeout);
6691cecc5deSJohnathan Mantey 			prc = poller->timeout_fn(poller->handler, poller->data);
6701cecc5deSJohnathan Mantey 			if (prc == POLLER_EXIT) {
6711cecc5deSJohnathan Mantey 				rc = -1;
6721cecc5deSJohnathan Mantey 			} else if (prc == POLLER_REMOVE) {
6731cecc5deSJohnathan Mantey 				poller->remove = true;
6741cecc5deSJohnathan Mantey 			}
6751cecc5deSJohnathan Mantey 		}
6761cecc5deSJohnathan Mantey 	}
6771cecc5deSJohnathan Mantey 
678329a35f5SJeremy Kerr 	/**
679329a35f5SJeremy Kerr 	 * Process deferred removals; restarting each time we unregister, as
680329a35f5SJeremy Kerr 	 * the array will have changed
681329a35f5SJeremy Kerr 	 */
682329a35f5SJeremy Kerr 	for (;;) {
683329a35f5SJeremy Kerr 		bool removed = false;
684329a35f5SJeremy Kerr 
685329a35f5SJeremy Kerr 		for (i = 0; i < console->n_pollers; i++) {
686329a35f5SJeremy Kerr 			poller = console->pollers[i];
687329a35f5SJeremy Kerr 			if (poller->remove) {
68855c9712dSJeremy Kerr 				console_poller_unregister(console, poller);
689329a35f5SJeremy Kerr 				removed = true;
690329a35f5SJeremy Kerr 				break;
691329a35f5SJeremy Kerr 			}
692329a35f5SJeremy Kerr 		}
693329a35f5SJeremy Kerr 		if (!removed)
694329a35f5SJeremy Kerr 			break;
6951a0e03b4SJeremy Kerr 	}
6961a0e03b4SJeremy Kerr 
6971a0e03b4SJeremy Kerr 	return rc;
6981a0e03b4SJeremy Kerr }
6991a0e03b4SJeremy Kerr 
700769cee1aSJeremy Kerr static void sighandler(int signal)
701769cee1aSJeremy Kerr {
702769cee1aSJeremy Kerr 	if (signal == SIGINT)
703769cee1aSJeremy Kerr 		sigint = true;
704769cee1aSJeremy Kerr }
705769cee1aSJeremy Kerr 
7061a0e03b4SJeremy Kerr int run_console(struct console *console)
7071a0e03b4SJeremy Kerr {
708769cee1aSJeremy Kerr 	sighandler_t sighandler_save;
7091cecc5deSJohnathan Mantey 	struct timeval tv;
7101cecc5deSJohnathan Mantey 	int rc, timeout;
711d831f960SJeremy Kerr 
712769cee1aSJeremy Kerr 	sighandler_save = signal(SIGINT, sighandler);
713769cee1aSJeremy Kerr 
714769cee1aSJeremy Kerr 	rc = 0;
715769cee1aSJeremy Kerr 
716d831f960SJeremy Kerr 	for (;;) {
717d831f960SJeremy Kerr 		uint8_t buf[4096];
718d831f960SJeremy Kerr 
7191764145dSJeremy Kerr 		BUILD_ASSERT(sizeof(buf) <= buffer_size);
7201764145dSJeremy Kerr 
721769cee1aSJeremy Kerr 		if (sigint) {
722769cee1aSJeremy Kerr 			fprintf(stderr, "Received interrupt, exiting\n");
723769cee1aSJeremy Kerr 			break;
724769cee1aSJeremy Kerr 		}
725769cee1aSJeremy Kerr 
7261cecc5deSJohnathan Mantey 		rc = get_current_time(&tv);
7271cecc5deSJohnathan Mantey 		if (rc) {
7281cecc5deSJohnathan Mantey 			warn("Failed to read current time");
7291cecc5deSJohnathan Mantey 			break;
7301cecc5deSJohnathan Mantey 		}
7311cecc5deSJohnathan Mantey 
7321cecc5deSJohnathan Mantey 		timeout = get_poll_timeout(console, &tv);
7331cecc5deSJohnathan Mantey 
734329a35f5SJeremy Kerr 		rc = poll(console->pollfds,
735*a72711afSAndrew Jeffery 			  console->n_pollers + MAX_INTERNAL_POLLFD, 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));
812*a72711afSAndrew Jeffery 	console->pollfds =
813*a72711afSAndrew Jeffery 		calloc(MAX_INTERNAL_POLLFD, 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