xref: /openbmc/obmc-console/console-server.c (revision 5c359cc6ab8a0d40c86556ea37378c4a73d8200b)
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;
52fd883a88SAndrew Jeffery 	uint16_t 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;
59*5c359cc6SAndrew Jeffery 	long n_handlers;
60329a35f5SJeremy Kerr 
61329a35f5SJeremy Kerr 	struct poller **pollers;
62*5c359cc6SAndrew Jeffery 	long 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 */
855db8c792SAndrew Jeffery const size_t buffer_size = 128ul * 1024ul;
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 
138a72711afSAndrew Jeffery 	rc = asprintf(&tty_class_device_link, "/sys/class/tty/%s",
139a72711afSAndrew 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) {
186a72711afSAndrew Jeffery 		warn("Can't access attribute %s on device %s", name,
187a72711afSAndrew 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)
195a72711afSAndrew Jeffery 		warn("Error writing to %s attribute of device %s", name,
196a72711afSAndrew 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 {
284fd883a88SAndrew Jeffery 	unsigned long parsed;
285d66195c1SJeremy Kerr 	const char *val;
286d66195c1SJeremy Kerr 	char *endp;
287d66195c1SJeremy Kerr 	int rc;
288d66195c1SJeremy Kerr 
289d66195c1SJeremy Kerr 	val = config_get_value(config, "lpc-address");
290d66195c1SJeremy Kerr 	if (val) {
291fd883a88SAndrew Jeffery 		errno = 0;
292fd883a88SAndrew Jeffery 		parsed = strtoul(val, &endp, 0);
293fd883a88SAndrew Jeffery 		if (parsed == ULONG_MAX && errno == ERANGE) {
294fd883a88SAndrew Jeffery 			warn("Cannot interpret 'lpc-address' value as an unsigned long: '%s'",
295fd883a88SAndrew Jeffery 			     val);
296fd883a88SAndrew Jeffery 			return -1;
297fd883a88SAndrew Jeffery 		}
298fd883a88SAndrew Jeffery 
299fd883a88SAndrew Jeffery 		if (parsed > UINT16_MAX) {
300fd883a88SAndrew Jeffery 			warn("Invalid LPC address '%s'", val);
301fd883a88SAndrew Jeffery 			return -1;
302fd883a88SAndrew Jeffery 		}
303fd883a88SAndrew Jeffery 
304fd883a88SAndrew Jeffery 		console->tty_lpc_addr = (uint16_t)parsed;
305d66195c1SJeremy Kerr 		if (endp == optarg) {
306d66195c1SJeremy Kerr 			warn("Invalid LPC address: '%s'", val);
307d66195c1SJeremy Kerr 			return -1;
308d66195c1SJeremy Kerr 		}
309d66195c1SJeremy Kerr 	}
310d66195c1SJeremy Kerr 
311d66195c1SJeremy Kerr 	val = config_get_value(config, "sirq");
312d66195c1SJeremy Kerr 	if (val) {
313fd883a88SAndrew Jeffery 		errno = 0;
314fd883a88SAndrew Jeffery 		parsed = strtoul(val, &endp, 0);
315fd883a88SAndrew Jeffery 		if (parsed == ULONG_MAX && errno == ERANGE) {
316fd883a88SAndrew Jeffery 			warn("Cannot interpret 'sirq' value as an unsigned long: '%s'",
317fd883a88SAndrew Jeffery 			     val);
318fd883a88SAndrew Jeffery 		}
319fd883a88SAndrew Jeffery 
320fd883a88SAndrew Jeffery 		if (parsed > 16)
321fd883a88SAndrew Jeffery 			warn("Invalid LPC SERIRQ: '%s'", val);
322fd883a88SAndrew Jeffery 
323fd883a88SAndrew Jeffery 		console->tty_sirq = (int)parsed;
324d66195c1SJeremy Kerr 		if (endp == optarg)
325d66195c1SJeremy Kerr 			warn("Invalid sirq: '%s'", val);
326d66195c1SJeremy Kerr 	}
327d66195c1SJeremy Kerr 
328c7fbcd48SBenjamin Fair 	val = config_get_value(config, "baud");
329c7fbcd48SBenjamin Fair 	if (val) {
330c7fbcd48SBenjamin Fair 		if (config_parse_baud(&console->tty_baud, val))
331c7fbcd48SBenjamin Fair 			warnx("Invalid baud rate: '%s'", val);
332c7fbcd48SBenjamin Fair 	}
333c7fbcd48SBenjamin Fair 
334d66195c1SJeremy Kerr 	if (!console->tty_kname) {
335d66195c1SJeremy Kerr 		warnx("Error: No TTY device specified");
336d66195c1SJeremy Kerr 		return -1;
337d66195c1SJeremy Kerr 	}
338d66195c1SJeremy Kerr 
339d66195c1SJeremy Kerr 	rc = tty_find_device(console);
340d66195c1SJeremy Kerr 	if (rc)
341d66195c1SJeremy Kerr 		return rc;
342d66195c1SJeremy Kerr 
343d66195c1SJeremy Kerr 	rc = tty_init_io(console);
344d66195c1SJeremy Kerr 	return rc;
345d66195c1SJeremy Kerr }
346d66195c1SJeremy Kerr 
3471a0e03b4SJeremy Kerr int console_data_out(struct console *console, const uint8_t *data, size_t len)
348d831f960SJeremy Kerr {
3491a0e03b4SJeremy Kerr 	return write_buf_to_fd(console->tty_fd, data, len);
350d831f960SJeremy Kerr }
351d831f960SJeremy Kerr 
352f9c8f6caSCheng C Yang static int method_set_baud_rate(sd_bus_message *msg, void *userdata,
353f9c8f6caSCheng C Yang 				sd_bus_error *err)
354f9c8f6caSCheng C Yang {
355f9c8f6caSCheng C Yang 	struct console *console = userdata;
356f9c8f6caSCheng C Yang 	uint32_t baudrate;
357f9c8f6caSCheng C Yang 	speed_t speed;
358f9c8f6caSCheng C Yang 	int r;
359f9c8f6caSCheng C Yang 
360f9c8f6caSCheng C Yang 	if (!console) {
361f9c8f6caSCheng C Yang 		sd_bus_error_set_const(err, DBUS_ERR, "Internal error");
362f9c8f6caSCheng C Yang 		return sd_bus_reply_method_return(msg, "x", 0);
363f9c8f6caSCheng C Yang 	}
364f9c8f6caSCheng C Yang 
365f9c8f6caSCheng C Yang 	r = sd_bus_message_read(msg, "u", &baudrate);
366f9c8f6caSCheng C Yang 	if (r < 0) {
367f9c8f6caSCheng C Yang 		sd_bus_error_set_const(err, DBUS_ERR, "Bad message");
368f9c8f6caSCheng C Yang 		return sd_bus_reply_method_return(msg, "x", -EINVAL);
369f9c8f6caSCheng C Yang 	}
370f9c8f6caSCheng C Yang 
371f9c8f6caSCheng C Yang 	speed = parse_int_to_baud(baudrate);
372f9c8f6caSCheng C Yang 	if (!speed) {
373f9c8f6caSCheng C Yang 		warnx("Invalid baud rate: '%u'", baudrate);
374f9c8f6caSCheng C Yang 		return sd_bus_reply_method_return(msg, "x", -EINVAL);
375f9c8f6caSCheng C Yang 	}
376f9c8f6caSCheng C Yang 
377f9c8f6caSCheng C Yang 	console->tty_baud = speed;
378f9c8f6caSCheng C Yang 	tty_change_baudrate(console);
379f9c8f6caSCheng C Yang 
380f9c8f6caSCheng C Yang 	return sd_bus_reply_method_return(msg, "x", r);
381f9c8f6caSCheng C Yang }
382f9c8f6caSCheng C Yang 
383fd048328SAndrew Jeffery static int get_handler(sd_bus *bus __attribute__((unused)),
384fd048328SAndrew Jeffery 		       const char *path __attribute__((unused)),
385fd048328SAndrew Jeffery 		       const char *interface __attribute__((unused)),
386fd048328SAndrew Jeffery 		       const char *property __attribute__((unused)),
387fd048328SAndrew Jeffery 		       sd_bus_message *reply, void *userdata,
388a72711afSAndrew Jeffery 		       sd_bus_error *error __attribute__((unused)))
389a72711afSAndrew Jeffery {
390f9c8f6caSCheng C Yang 	struct console *console = userdata;
391f9c8f6caSCheng C Yang 	uint32_t baudrate;
392f9c8f6caSCheng C Yang 	int r;
393f9c8f6caSCheng C Yang 
394f9c8f6caSCheng C Yang 	baudrate = parse_baud_to_int(console->tty_baud);
395f9c8f6caSCheng C Yang 	if (!baudrate)
396f9c8f6caSCheng C Yang 		warnx("Invalid baud rate: '%d'", console->tty_baud);
397f9c8f6caSCheng C Yang 
398f9c8f6caSCheng C Yang 	r = sd_bus_message_append(reply, "u", baudrate);
399f9c8f6caSCheng C Yang 
400f9c8f6caSCheng C Yang 	return r;
401f9c8f6caSCheng C Yang }
402f9c8f6caSCheng C Yang 
403f9c8f6caSCheng C Yang static const sd_bus_vtable console_vtable[] = {
404f9c8f6caSCheng C Yang 	SD_BUS_VTABLE_START(0),
405f9c8f6caSCheng C Yang 	SD_BUS_METHOD("setBaudRate", "u", "x", method_set_baud_rate,
406f9c8f6caSCheng C Yang 		      SD_BUS_VTABLE_UNPRIVILEGED),
407f9c8f6caSCheng C Yang 	SD_BUS_PROPERTY("baudrate", "u", get_handler, 0, 0),
408a72711afSAndrew Jeffery 	SD_BUS_VTABLE_END,
409a72711afSAndrew Jeffery };
410f9c8f6caSCheng C Yang 
411a72711afSAndrew Jeffery static void dbus_init(struct console *console,
412a72711afSAndrew Jeffery 		      struct config *config __attribute__((unused)))
413f9c8f6caSCheng C Yang {
414f9c8f6caSCheng C Yang 	int dbus_poller = 0;
415f9c8f6caSCheng C Yang 	int fd, r;
416f9c8f6caSCheng C Yang 
417f9c8f6caSCheng C Yang 	if (!console) {
418f9c8f6caSCheng C Yang 		warnx("Couldn't get valid console");
419f9c8f6caSCheng C Yang 		return;
420f9c8f6caSCheng C Yang 	}
421f9c8f6caSCheng C Yang 
422f9c8f6caSCheng C Yang 	r = sd_bus_default_system(&console->bus);
423f9c8f6caSCheng C Yang 	if (r < 0) {
424f9c8f6caSCheng C Yang 		warnx("Failed to connect to system bus: %s", strerror(-r));
425f9c8f6caSCheng C Yang 		return;
426f9c8f6caSCheng C Yang 	}
427f9c8f6caSCheng C Yang 
428f9c8f6caSCheng C Yang 	r = sd_bus_add_object_vtable(console->bus, NULL, OBJ_NAME, DBUS_NAME,
429f9c8f6caSCheng C Yang 				     console_vtable, console);
430f9c8f6caSCheng C Yang 	if (r < 0) {
431f9c8f6caSCheng C Yang 		warnx("Failed to issue method call: %s", strerror(-r));
432f9c8f6caSCheng C Yang 		return;
433f9c8f6caSCheng C Yang 	}
434f9c8f6caSCheng C Yang 
435a72711afSAndrew Jeffery 	r = sd_bus_request_name(console->bus, DBUS_NAME,
436a72711afSAndrew Jeffery 				SD_BUS_NAME_ALLOW_REPLACEMENT |
437a72711afSAndrew Jeffery 					SD_BUS_NAME_REPLACE_EXISTING);
438f9c8f6caSCheng C Yang 	if (r < 0) {
439f9c8f6caSCheng C Yang 		warnx("Failed to acquire service name: %s", strerror(-r));
440f9c8f6caSCheng C Yang 		return;
441f9c8f6caSCheng C Yang 	}
442f9c8f6caSCheng C Yang 
443f9c8f6caSCheng C Yang 	fd = sd_bus_get_fd(console->bus);
444f9c8f6caSCheng C Yang 	if (fd < 0) {
445f9c8f6caSCheng C Yang 		warnx("Couldn't get the bus file descriptor");
446f9c8f6caSCheng C Yang 		return;
447f9c8f6caSCheng C Yang 	}
448f9c8f6caSCheng C Yang 
449f9c8f6caSCheng C Yang 	dbus_poller = POLLFD_DBUS;
450f9c8f6caSCheng C Yang 
451f9c8f6caSCheng C Yang 	console->pollfds[dbus_poller].fd = fd;
452f9c8f6caSCheng C Yang 	console->pollfds[dbus_poller].events = POLLIN;
453f9c8f6caSCheng C Yang }
454f9c8f6caSCheng C Yang 
455d47963e5SJeremy Kerr static void handlers_init(struct console *console, struct config *config)
456d831f960SJeremy Kerr {
4571a0e03b4SJeremy Kerr 	extern struct handler *__start_handlers, *__stop_handlers;
4581a0e03b4SJeremy Kerr 	struct handler *handler;
459021b91f0SJeremy Kerr 	int i, rc;
460d831f960SJeremy Kerr 
4611a0e03b4SJeremy Kerr 	console->n_handlers = &__stop_handlers - &__start_handlers;
4621a0e03b4SJeremy Kerr 	console->handlers = &__start_handlers;
463d831f960SJeremy Kerr 
464*5c359cc6SAndrew Jeffery 	printf("%ld handler%s\n", console->n_handlers,
4651a0e03b4SJeremy Kerr 	       console->n_handlers == 1 ? "" : "s");
466d831f960SJeremy Kerr 
4671a0e03b4SJeremy Kerr 	for (i = 0; i < console->n_handlers; i++) {
4681a0e03b4SJeremy Kerr 		handler = console->handlers[i];
4691a0e03b4SJeremy Kerr 
470021b91f0SJeremy Kerr 		rc = 0;
4711a0e03b4SJeremy Kerr 		if (handler->init)
472021b91f0SJeremy Kerr 			rc = handler->init(handler, console, config);
473021b91f0SJeremy Kerr 
474021b91f0SJeremy Kerr 		handler->active = rc == 0;
475021b91f0SJeremy Kerr 
476021b91f0SJeremy Kerr 		printf("  %s [%sactive]\n", handler->name,
477021b91f0SJeremy Kerr 		       handler->active ? "" : "in");
478d831f960SJeremy Kerr 	}
479d831f960SJeremy Kerr }
480d831f960SJeremy Kerr 
4811a0e03b4SJeremy Kerr static void handlers_fini(struct console *console)
482d831f960SJeremy Kerr {
4831a0e03b4SJeremy Kerr 	struct handler *handler;
4841a0e03b4SJeremy Kerr 	int i;
4851a0e03b4SJeremy Kerr 
4861a0e03b4SJeremy Kerr 	for (i = 0; i < console->n_handlers; i++) {
4871a0e03b4SJeremy Kerr 		handler = console->handlers[i];
488021b91f0SJeremy Kerr 		if (handler->fini && handler->active)
4891a0e03b4SJeremy Kerr 			handler->fini(handler);
4901a0e03b4SJeremy Kerr 	}
491d831f960SJeremy Kerr }
492d831f960SJeremy Kerr 
4931cecc5deSJohnathan Mantey static int get_current_time(struct timeval *tv)
4941cecc5deSJohnathan Mantey {
4951cecc5deSJohnathan Mantey 	struct timespec t;
4961cecc5deSJohnathan Mantey 	int rc;
4971cecc5deSJohnathan Mantey 
4981cecc5deSJohnathan Mantey 	/*
4991cecc5deSJohnathan Mantey 	 * We use clock_gettime(CLOCK_MONOTONIC) so we're immune to
5001cecc5deSJohnathan Mantey 	 * local time changes. However, a struct timeval is more
5011cecc5deSJohnathan Mantey 	 * convenient for calculations, so convert to that.
5021cecc5deSJohnathan Mantey 	 */
5031cecc5deSJohnathan Mantey 	rc = clock_gettime(CLOCK_MONOTONIC, &t);
5041cecc5deSJohnathan Mantey 	if (rc)
5051cecc5deSJohnathan Mantey 		return rc;
5061cecc5deSJohnathan Mantey 
5071cecc5deSJohnathan Mantey 	tv->tv_sec = t.tv_sec;
5081cecc5deSJohnathan Mantey 	tv->tv_usec = t.tv_nsec / 1000;
5091cecc5deSJohnathan Mantey 
5101cecc5deSJohnathan Mantey 	return 0;
5111cecc5deSJohnathan Mantey }
5121cecc5deSJohnathan Mantey 
513a72711afSAndrew Jeffery struct ringbuffer_consumer *
514a72711afSAndrew Jeffery console_ringbuffer_consumer_register(struct console *console,
515f733c85aSJeremy Kerr 				     ringbuffer_poll_fn_t poll_fn, void *data)
516d831f960SJeremy Kerr {
517f733c85aSJeremy Kerr 	return ringbuffer_consumer_register(console->rb, poll_fn, data);
518d831f960SJeremy Kerr }
519d831f960SJeremy Kerr 
52055c9712dSJeremy Kerr struct poller *console_poller_register(struct console *console,
521a72711afSAndrew Jeffery 				       struct handler *handler,
522a72711afSAndrew Jeffery 				       poller_event_fn_t poller_fn,
5231cecc5deSJohnathan Mantey 				       poller_timeout_fn_t timeout_fn, int fd,
5241cecc5deSJohnathan Mantey 				       int events, void *data)
525d831f960SJeremy Kerr {
526329a35f5SJeremy Kerr 	struct poller *poller;
527*5c359cc6SAndrew Jeffery 	long n;
528329a35f5SJeremy Kerr 
529329a35f5SJeremy Kerr 	poller = malloc(sizeof(*poller));
530329a35f5SJeremy Kerr 	poller->remove = false;
531329a35f5SJeremy Kerr 	poller->handler = handler;
5321cecc5deSJohnathan Mantey 	poller->event_fn = poller_fn;
5331cecc5deSJohnathan Mantey 	poller->timeout_fn = timeout_fn;
534329a35f5SJeremy Kerr 	poller->data = data;
535329a35f5SJeremy Kerr 
536329a35f5SJeremy Kerr 	/* add one to our pollers array */
537329a35f5SJeremy Kerr 	n = console->n_pollers++;
538a72711afSAndrew Jeffery 	console->pollers =
539a72711afSAndrew Jeffery 		realloc(console->pollers,
540329a35f5SJeremy Kerr 			sizeof(*console->pollers) * console->n_pollers);
541329a35f5SJeremy Kerr 
542329a35f5SJeremy Kerr 	console->pollers[n] = poller;
543329a35f5SJeremy Kerr 
544329a35f5SJeremy Kerr 	/* increase pollfds array too  */
545a72711afSAndrew Jeffery 	console->pollfds =
546a72711afSAndrew Jeffery 		realloc(console->pollfds,
547329a35f5SJeremy Kerr 			sizeof(*console->pollfds) *
548f9c8f6caSCheng C Yang 				(MAX_INTERNAL_POLLFD + console->n_pollers));
549329a35f5SJeremy Kerr 
550329a35f5SJeremy Kerr 	/* shift the end pollfds up by one */
551a72711afSAndrew Jeffery 	memcpy(&console->pollfds[n + 1], &console->pollfds[n],
552f9c8f6caSCheng C Yang 	       sizeof(*console->pollfds) * MAX_INTERNAL_POLLFD);
553329a35f5SJeremy Kerr 
554329a35f5SJeremy Kerr 	console->pollfds[n].fd = fd;
555*5c359cc6SAndrew Jeffery 	console->pollfds[n].events = (short)(events & 0x7fff);
556329a35f5SJeremy Kerr 
557329a35f5SJeremy Kerr 	return poller;
558329a35f5SJeremy Kerr }
559329a35f5SJeremy Kerr 
560a72711afSAndrew Jeffery void console_poller_unregister(struct console *console, struct poller *poller)
561329a35f5SJeremy Kerr {
562329a35f5SJeremy Kerr 	int i;
563329a35f5SJeremy Kerr 
564329a35f5SJeremy Kerr 	/* find the entry in our pollers array */
565329a35f5SJeremy Kerr 	for (i = 0; i < console->n_pollers; i++)
566329a35f5SJeremy Kerr 		if (console->pollers[i] == poller)
567329a35f5SJeremy Kerr 			break;
568329a35f5SJeremy Kerr 
569329a35f5SJeremy Kerr 	assert(i < console->n_pollers);
570329a35f5SJeremy Kerr 
571329a35f5SJeremy Kerr 	console->n_pollers--;
572329a35f5SJeremy Kerr 
573329a35f5SJeremy Kerr 	/* remove the item from the pollers array... */
574329a35f5SJeremy Kerr 	memmove(&console->pollers[i], &console->pollers[i + 1],
575a72711afSAndrew Jeffery 		sizeof(*console->pollers) * (console->n_pollers - i));
576329a35f5SJeremy Kerr 
577a72711afSAndrew Jeffery 	console->pollers =
578a72711afSAndrew Jeffery 		realloc(console->pollers,
579329a35f5SJeremy Kerr 			sizeof(*console->pollers) * console->n_pollers);
580329a35f5SJeremy Kerr 
581329a35f5SJeremy Kerr 	/* ... and the pollfds array */
582329a35f5SJeremy Kerr 	memmove(&console->pollfds[i], &console->pollfds[i + 1],
583329a35f5SJeremy Kerr 		sizeof(*console->pollfds) *
584f9c8f6caSCheng C Yang 			(MAX_INTERNAL_POLLFD + console->n_pollers - i));
585329a35f5SJeremy Kerr 
586a72711afSAndrew Jeffery 	console->pollfds =
587a72711afSAndrew Jeffery 		realloc(console->pollfds,
588329a35f5SJeremy Kerr 			sizeof(*console->pollfds) *
589f9c8f6caSCheng C Yang 				(MAX_INTERNAL_POLLFD + console->n_pollers));
590329a35f5SJeremy Kerr 
591329a35f5SJeremy Kerr 	free(poller);
592329a35f5SJeremy Kerr }
593329a35f5SJeremy Kerr 
5946b1fed27SJeremy Kerr void console_poller_set_events(struct console *console, struct poller *poller,
5956b1fed27SJeremy Kerr 			       int events)
5966b1fed27SJeremy Kerr {
5976b1fed27SJeremy Kerr 	int i;
5986b1fed27SJeremy Kerr 
5996b1fed27SJeremy Kerr 	/* find the entry in our pollers array */
6006b1fed27SJeremy Kerr 	for (i = 0; i < console->n_pollers; i++)
6016b1fed27SJeremy Kerr 		if (console->pollers[i] == poller)
6026b1fed27SJeremy Kerr 			break;
6036b1fed27SJeremy Kerr 
604*5c359cc6SAndrew Jeffery 	console->pollfds[i].events = (short)(events & 0x7fff);
6056b1fed27SJeremy Kerr }
6066b1fed27SJeremy Kerr 
607fd048328SAndrew Jeffery void console_poller_set_timeout(struct console *console __attribute__((unused)),
608fd048328SAndrew Jeffery 				struct poller *poller, const struct timeval *tv)
6091cecc5deSJohnathan Mantey {
6101cecc5deSJohnathan Mantey 	struct timeval now;
6111cecc5deSJohnathan Mantey 	int rc;
6121cecc5deSJohnathan Mantey 
6131cecc5deSJohnathan Mantey 	rc = get_current_time(&now);
6141cecc5deSJohnathan Mantey 	if (rc)
6151cecc5deSJohnathan Mantey 		return;
6161cecc5deSJohnathan Mantey 
6171cecc5deSJohnathan Mantey 	timeradd(&now, tv, &poller->timeout);
6181cecc5deSJohnathan Mantey }
6191cecc5deSJohnathan Mantey 
620*5c359cc6SAndrew Jeffery static long get_poll_timeout(struct console *console, struct timeval *cur_time)
6211cecc5deSJohnathan Mantey {
6221cecc5deSJohnathan Mantey 	struct timeval *earliest, interval;
6231cecc5deSJohnathan Mantey 	struct poller *poller;
6241cecc5deSJohnathan Mantey 	int i;
6251cecc5deSJohnathan Mantey 
6261cecc5deSJohnathan Mantey 	earliest = NULL;
6271cecc5deSJohnathan Mantey 
6281cecc5deSJohnathan Mantey 	for (i = 0; i < console->n_pollers; i++) {
6291cecc5deSJohnathan Mantey 		poller = console->pollers[i];
6301cecc5deSJohnathan Mantey 
6311cecc5deSJohnathan Mantey 		if (poller->timeout_fn && timerisset(&poller->timeout) &&
6321cecc5deSJohnathan Mantey 		    (!earliest ||
6331cecc5deSJohnathan Mantey 		     (earliest && timercmp(&poller->timeout, earliest, <)))) {
6341cecc5deSJohnathan Mantey 			// poller is buffering data and needs the poll
6351cecc5deSJohnathan Mantey 			// function to timeout.
6361cecc5deSJohnathan Mantey 			earliest = &poller->timeout;
6371cecc5deSJohnathan Mantey 		}
6381cecc5deSJohnathan Mantey 	}
6391cecc5deSJohnathan Mantey 
6401cecc5deSJohnathan Mantey 	if (earliest) {
6411cecc5deSJohnathan Mantey 		if (timercmp(earliest, cur_time, >)) {
6421cecc5deSJohnathan Mantey 			/* recalculate the timeout period, time period has
6431cecc5deSJohnathan Mantey 			 * not elapsed */
6441cecc5deSJohnathan Mantey 			timersub(earliest, cur_time, &interval);
6451cecc5deSJohnathan Mantey 			return ((interval.tv_sec * 1000) +
6461cecc5deSJohnathan Mantey 				(interval.tv_usec / 1000));
6471cecc5deSJohnathan Mantey 		} else {
6481cecc5deSJohnathan Mantey 			/* return from poll immediately */
6491cecc5deSJohnathan Mantey 			return 0;
6501cecc5deSJohnathan Mantey 		}
6511cecc5deSJohnathan Mantey 	} else {
6521cecc5deSJohnathan Mantey 		/* poll indefinitely */
6531cecc5deSJohnathan Mantey 		return -1;
6541cecc5deSJohnathan Mantey 	}
6551cecc5deSJohnathan Mantey }
6561cecc5deSJohnathan Mantey 
6571cecc5deSJohnathan Mantey static int call_pollers(struct console *console, struct timeval *cur_time)
658329a35f5SJeremy Kerr {
659329a35f5SJeremy Kerr 	struct poller *poller;
660329a35f5SJeremy Kerr 	struct pollfd *pollfd;
661329a35f5SJeremy Kerr 	enum poller_ret prc;
662329a35f5SJeremy Kerr 	int i, rc;
663d831f960SJeremy Kerr 
6641a0e03b4SJeremy Kerr 	rc = 0;
6651a0e03b4SJeremy Kerr 
666329a35f5SJeremy Kerr 	/*
667329a35f5SJeremy Kerr 	 * Process poll events by iterating through the pollers and pollfds
668329a35f5SJeremy Kerr 	 * in-step, calling any pollers that we've found revents for.
669329a35f5SJeremy Kerr 	 */
670329a35f5SJeremy Kerr 	for (i = 0; i < console->n_pollers; i++) {
671329a35f5SJeremy Kerr 		poller = console->pollers[i];
672329a35f5SJeremy Kerr 		pollfd = &console->pollfds[i];
6731cecc5deSJohnathan Mantey 		prc = POLLER_OK;
6741a0e03b4SJeremy Kerr 
6751cecc5deSJohnathan Mantey 		/* process pending events... */
6761cecc5deSJohnathan Mantey 		if (pollfd->revents) {
6771cecc5deSJohnathan Mantey 			prc = poller->event_fn(poller->handler, pollfd->revents,
678329a35f5SJeremy Kerr 					       poller->data);
679329a35f5SJeremy Kerr 			if (prc == POLLER_EXIT)
680329a35f5SJeremy Kerr 				rc = -1;
681329a35f5SJeremy Kerr 			else if (prc == POLLER_REMOVE)
682329a35f5SJeremy Kerr 				poller->remove = true;
683329a35f5SJeremy Kerr 		}
684329a35f5SJeremy Kerr 
6851cecc5deSJohnathan Mantey 		if ((prc == POLLER_OK) && poller->timeout_fn &&
6861cecc5deSJohnathan Mantey 		    timerisset(&poller->timeout) &&
6871cecc5deSJohnathan Mantey 		    timercmp(&poller->timeout, cur_time, <=)) {
6881cecc5deSJohnathan Mantey 			/* One of the ringbuffer consumers is buffering the
6891cecc5deSJohnathan Mantey 			data stream. The amount of idle time the consumer
6901cecc5deSJohnathan Mantey 			desired has expired.  Process the buffered data for
6911cecc5deSJohnathan Mantey 			transmission. */
6921cecc5deSJohnathan Mantey 			timerclear(&poller->timeout);
6931cecc5deSJohnathan Mantey 			prc = poller->timeout_fn(poller->handler, poller->data);
6941cecc5deSJohnathan Mantey 			if (prc == POLLER_EXIT) {
6951cecc5deSJohnathan Mantey 				rc = -1;
6961cecc5deSJohnathan Mantey 			} else if (prc == POLLER_REMOVE) {
6971cecc5deSJohnathan Mantey 				poller->remove = true;
6981cecc5deSJohnathan Mantey 			}
6991cecc5deSJohnathan Mantey 		}
7001cecc5deSJohnathan Mantey 	}
7011cecc5deSJohnathan Mantey 
702329a35f5SJeremy Kerr 	/**
703329a35f5SJeremy Kerr 	 * Process deferred removals; restarting each time we unregister, as
704329a35f5SJeremy Kerr 	 * the array will have changed
705329a35f5SJeremy Kerr 	 */
706329a35f5SJeremy Kerr 	for (;;) {
707329a35f5SJeremy Kerr 		bool removed = false;
708329a35f5SJeremy Kerr 
709329a35f5SJeremy Kerr 		for (i = 0; i < console->n_pollers; i++) {
710329a35f5SJeremy Kerr 			poller = console->pollers[i];
711329a35f5SJeremy Kerr 			if (poller->remove) {
71255c9712dSJeremy Kerr 				console_poller_unregister(console, poller);
713329a35f5SJeremy Kerr 				removed = true;
714329a35f5SJeremy Kerr 				break;
715329a35f5SJeremy Kerr 			}
716329a35f5SJeremy Kerr 		}
717329a35f5SJeremy Kerr 		if (!removed)
718329a35f5SJeremy Kerr 			break;
7191a0e03b4SJeremy Kerr 	}
7201a0e03b4SJeremy Kerr 
7211a0e03b4SJeremy Kerr 	return rc;
7221a0e03b4SJeremy Kerr }
7231a0e03b4SJeremy Kerr 
724769cee1aSJeremy Kerr static void sighandler(int signal)
725769cee1aSJeremy Kerr {
726769cee1aSJeremy Kerr 	if (signal == SIGINT)
727769cee1aSJeremy Kerr 		sigint = true;
728769cee1aSJeremy Kerr }
729769cee1aSJeremy Kerr 
7301a0e03b4SJeremy Kerr int run_console(struct console *console)
7311a0e03b4SJeremy Kerr {
732*5c359cc6SAndrew Jeffery 	sighandler_t sighandler_save = signal(SIGINT, sighandler);
7331cecc5deSJohnathan Mantey 	struct timeval tv;
734*5c359cc6SAndrew Jeffery 	long timeout;
735*5c359cc6SAndrew Jeffery 	ssize_t rc;
736769cee1aSJeremy Kerr 
737769cee1aSJeremy Kerr 	rc = 0;
738769cee1aSJeremy Kerr 
739d831f960SJeremy Kerr 	for (;;) {
740d831f960SJeremy Kerr 		uint8_t buf[4096];
741d831f960SJeremy Kerr 
7421764145dSJeremy Kerr 		BUILD_ASSERT(sizeof(buf) <= buffer_size);
7431764145dSJeremy Kerr 
744769cee1aSJeremy Kerr 		if (sigint) {
745769cee1aSJeremy Kerr 			fprintf(stderr, "Received interrupt, exiting\n");
746769cee1aSJeremy Kerr 			break;
747769cee1aSJeremy Kerr 		}
748769cee1aSJeremy Kerr 
7491cecc5deSJohnathan Mantey 		rc = get_current_time(&tv);
7501cecc5deSJohnathan Mantey 		if (rc) {
7511cecc5deSJohnathan Mantey 			warn("Failed to read current time");
7521cecc5deSJohnathan Mantey 			break;
7531cecc5deSJohnathan Mantey 		}
7541cecc5deSJohnathan Mantey 
7551cecc5deSJohnathan Mantey 		timeout = get_poll_timeout(console, &tv);
7561cecc5deSJohnathan Mantey 
757329a35f5SJeremy Kerr 		rc = poll(console->pollfds,
758*5c359cc6SAndrew Jeffery 			  console->n_pollers + MAX_INTERNAL_POLLFD,
759*5c359cc6SAndrew Jeffery 			  (int)timeout);
7601cecc5deSJohnathan Mantey 
761d831f960SJeremy Kerr 		if (rc < 0) {
762769cee1aSJeremy Kerr 			if (errno == EINTR) {
763769cee1aSJeremy Kerr 				continue;
764769cee1aSJeremy Kerr 			} else {
765d831f960SJeremy Kerr 				warn("poll error");
766769cee1aSJeremy Kerr 				break;
767769cee1aSJeremy Kerr 			}
768d831f960SJeremy Kerr 		}
769d831f960SJeremy Kerr 
770329a35f5SJeremy Kerr 		/* process internal fd first */
771329a35f5SJeremy Kerr 		if (console->pollfds[console->n_pollers].revents) {
7721a0e03b4SJeremy Kerr 			rc = read(console->tty_fd, buf, sizeof(buf));
773d831f960SJeremy Kerr 			if (rc <= 0) {
774d831f960SJeremy Kerr 				warn("Error reading from tty device");
775769cee1aSJeremy Kerr 				rc = -1;
776769cee1aSJeremy Kerr 				break;
777d831f960SJeremy Kerr 			}
778f733c85aSJeremy Kerr 			rc = ringbuffer_queue(console->rb, buf, rc);
7791a0e03b4SJeremy Kerr 			if (rc)
780769cee1aSJeremy Kerr 				break;
781d831f960SJeremy Kerr 		}
782d831f960SJeremy Kerr 
783f9c8f6caSCheng C Yang 		if (console->pollfds[console->n_pollers + 1].revents) {
784f9c8f6caSCheng C Yang 			sd_bus_process(console->bus, NULL);
785f9c8f6caSCheng C Yang 		}
786f9c8f6caSCheng C Yang 
787329a35f5SJeremy Kerr 		/* ... and then the pollers */
7881cecc5deSJohnathan Mantey 		rc = call_pollers(console, &tv);
7891a0e03b4SJeremy Kerr 		if (rc)
790769cee1aSJeremy Kerr 			break;
7911a0e03b4SJeremy Kerr 	}
792769cee1aSJeremy Kerr 
793769cee1aSJeremy Kerr 	signal(SIGINT, sighandler_save);
794f9c8f6caSCheng C Yang 	sd_bus_unref(console->bus);
795769cee1aSJeremy Kerr 
796769cee1aSJeremy Kerr 	return rc ? -1 : 0;
7971a0e03b4SJeremy Kerr }
798d831f960SJeremy Kerr static const struct option options[] = {
799d66195c1SJeremy Kerr 	{ "config", required_argument, 0, 'c' },
800f5858b5bSJoel Stanley 	{ 0, 0, 0, 0 },
801d831f960SJeremy Kerr };
802d831f960SJeremy Kerr 
803d831f960SJeremy Kerr int main(int argc, char **argv)
804d831f960SJeremy Kerr {
805d66195c1SJeremy Kerr 	const char *config_filename = NULL;
8066221ce94SVishwanatha Subbanna 	const char *config_tty_kname = NULL;
8071a0e03b4SJeremy Kerr 	struct console *console;
808d66195c1SJeremy Kerr 	struct config *config;
809d66195c1SJeremy Kerr 	int rc;
810d831f960SJeremy Kerr 
811957818b4SJeremy Kerr 	rc = -1;
812d831f960SJeremy Kerr 
813d831f960SJeremy Kerr 	for (;;) {
814d831f960SJeremy Kerr 		int c, idx;
815d831f960SJeremy Kerr 
816d66195c1SJeremy Kerr 		c = getopt_long(argc, argv, "c:", options, &idx);
817d831f960SJeremy Kerr 		if (c == -1)
818d831f960SJeremy Kerr 			break;
819d831f960SJeremy Kerr 
820d831f960SJeremy Kerr 		switch (c) {
821d66195c1SJeremy Kerr 		case 'c':
822d66195c1SJeremy Kerr 			config_filename = optarg;
823d831f960SJeremy Kerr 			break;
824d831f960SJeremy Kerr 		case 'h':
825d831f960SJeremy Kerr 		case '?':
826d831f960SJeremy Kerr 			usage(argv[0]);
827d66195c1SJeremy Kerr 			return EXIT_SUCCESS;
828d831f960SJeremy Kerr 		}
829d831f960SJeremy Kerr 	}
830d831f960SJeremy Kerr 
83191dde14eSAndrew Jeffery 	if (optind < argc)
8326221ce94SVishwanatha Subbanna 		config_tty_kname = argv[optind];
8336221ce94SVishwanatha Subbanna 
834d66195c1SJeremy Kerr 	console = malloc(sizeof(struct console));
835d66195c1SJeremy Kerr 	memset(console, 0, sizeof(*console));
836a72711afSAndrew Jeffery 	console->pollfds =
837a72711afSAndrew Jeffery 		calloc(MAX_INTERNAL_POLLFD, sizeof(*console->pollfds));
838f733c85aSJeremy Kerr 	console->rb = ringbuffer_init(buffer_size);
839329a35f5SJeremy Kerr 
840d66195c1SJeremy Kerr 	config = config_init(config_filename);
841d66195c1SJeremy Kerr 	if (!config) {
842d66195c1SJeremy Kerr 		warnx("Can't read configuration, exiting.");
843d66195c1SJeremy Kerr 		goto out_free;
844d831f960SJeremy Kerr 	}
845d831f960SJeremy Kerr 
84691dde14eSAndrew Jeffery 	if (!config_tty_kname)
84791dde14eSAndrew Jeffery 		config_tty_kname = config_get_value(config, "upstream-tty");
84891dde14eSAndrew Jeffery 
84991dde14eSAndrew Jeffery 	if (!config_tty_kname) {
85091dde14eSAndrew Jeffery 		warnx("No TTY device specified");
85191dde14eSAndrew Jeffery 		usage(argv[0]);
85291dde14eSAndrew Jeffery 		return EXIT_FAILURE;
85391dde14eSAndrew Jeffery 	}
85491dde14eSAndrew Jeffery 
8556221ce94SVishwanatha Subbanna 	console->tty_kname = config_tty_kname;
8566221ce94SVishwanatha Subbanna 
857d66195c1SJeremy Kerr 	rc = tty_init(console, config);
85817217845SJeremy Kerr 	if (rc)
859d66195c1SJeremy Kerr 		goto out_config_fini;
860d831f960SJeremy Kerr 
861f9c8f6caSCheng C Yang 	dbus_init(console, config);
862f9c8f6caSCheng C Yang 
863d47963e5SJeremy Kerr 	handlers_init(console, config);
864d831f960SJeremy Kerr 
8651a0e03b4SJeremy Kerr 	rc = run_console(console);
866d831f960SJeremy Kerr 
8671a0e03b4SJeremy Kerr 	handlers_fini(console);
868d831f960SJeremy Kerr 
869d66195c1SJeremy Kerr out_config_fini:
870d66195c1SJeremy Kerr 	config_fini(config);
871d66195c1SJeremy Kerr 
872957818b4SJeremy Kerr out_free:
87389ea8198SJeremy Kerr 	free(console->pollers);
87489ea8198SJeremy Kerr 	free(console->pollfds);
8751a0e03b4SJeremy Kerr 	free(console->tty_sysfs_devnode);
8761a0e03b4SJeremy Kerr 	free(console->tty_dev);
8771a0e03b4SJeremy Kerr 	free(console);
878d831f960SJeremy Kerr 
879d831f960SJeremy Kerr 	return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
880d831f960SJeremy Kerr }
881