xref: /openbmc/obmc-console/console-server.c (revision 91b5217505272f355868d08afecf585ec7f859b2)
1d831f960SJeremy Kerr /**
2d831f960SJeremy Kerr  * Console server process for OpenBMC
3d831f960SJeremy Kerr  *
49326d779SJeremy Kerr  * Copyright © 2016 IBM Corporation
59326d779SJeremy Kerr  *
69326d779SJeremy Kerr  * Licensed under the Apache License, Version 2.0 (the "License");
79326d779SJeremy Kerr  * you may not use this file except in compliance with the License.
89326d779SJeremy Kerr  * You may obtain a copy of the License at
99326d779SJeremy Kerr  *
109326d779SJeremy Kerr  *     http://www.apache.org/licenses/LICENSE-2.0
119326d779SJeremy Kerr  *
129326d779SJeremy Kerr  * Unless required by applicable law or agreed to in writing, software
139326d779SJeremy Kerr  * distributed under the License is distributed on an "AS IS" BASIS,
149326d779SJeremy Kerr  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
159326d779SJeremy Kerr  * See the License for the specific language governing permissions and
169326d779SJeremy Kerr  * limitations under the License.
17d831f960SJeremy Kerr  */
18d831f960SJeremy Kerr 
19329a35f5SJeremy Kerr #include <assert.h>
20769cee1aSJeremy Kerr #include <errno.h>
21769cee1aSJeremy Kerr #include <signal.h>
22d831f960SJeremy Kerr #include <stdint.h>
23d831f960SJeremy Kerr #include <stdbool.h>
24d831f960SJeremy Kerr #include <stdlib.h>
25d831f960SJeremy Kerr #include <stdio.h>
26d831f960SJeremy Kerr #include <fcntl.h>
27d831f960SJeremy Kerr #include <unistd.h>
28d831f960SJeremy Kerr #include <err.h>
29d831f960SJeremy Kerr #include <string.h>
30d831f960SJeremy Kerr #include <getopt.h>
3117217845SJeremy Kerr #include <limits.h>
321cecc5deSJohnathan Mantey #include <time.h>
3354e9569dSJeremy Kerr #include <termios.h>
34d831f960SJeremy Kerr 
35d831f960SJeremy Kerr #include <sys/types.h>
361cecc5deSJohnathan Mantey #include <sys/time.h>
3787e344cdSJoel Stanley #include <poll.h>
38d831f960SJeremy Kerr 
391a0e03b4SJeremy Kerr #include "console-server.h"
40d831f960SJeremy Kerr 
41f9c8f6caSCheng C Yang #define DBUS_ERR  "org.openbmc.error"
42f9c8f6caSCheng C Yang #define DBUS_NAME "xyz.openbmc_project.console"
43f9c8f6caSCheng C Yang #define OBJ_NAME  "/xyz/openbmc_project/console"
44f9c8f6caSCheng C Yang 
451a0e03b4SJeremy Kerr struct console {
4617217845SJeremy Kerr 	const char *tty_kname;
4717217845SJeremy Kerr 	char *tty_sysfs_devnode;
4817217845SJeremy Kerr 	char *tty_dev;
49957818b4SJeremy Kerr 	int tty_sirq;
50fd883a88SAndrew Jeffery 	uint16_t tty_lpc_addr;
51c7fbcd48SBenjamin Fair 	speed_t tty_baud;
52d831f960SJeremy Kerr 	int tty_fd;
53329a35f5SJeremy Kerr 
54f733c85aSJeremy Kerr 	struct ringbuffer *rb;
55f733c85aSJeremy Kerr 
561a0e03b4SJeremy Kerr 	struct handler **handlers;
575c359cc6SAndrew Jeffery 	long n_handlers;
58329a35f5SJeremy Kerr 
59329a35f5SJeremy Kerr 	struct poller **pollers;
605c359cc6SAndrew Jeffery 	long n_pollers;
61329a35f5SJeremy Kerr 
62329a35f5SJeremy Kerr 	struct pollfd *pollfds;
63f9c8f6caSCheng C Yang 	struct sd_bus *bus;
64d831f960SJeremy Kerr };
65d831f960SJeremy Kerr 
66329a35f5SJeremy Kerr struct poller {
67329a35f5SJeremy Kerr 	struct handler *handler;
68329a35f5SJeremy Kerr 	void *data;
691cecc5deSJohnathan Mantey 	poller_event_fn_t event_fn;
701cecc5deSJohnathan Mantey 	poller_timeout_fn_t timeout_fn;
711cecc5deSJohnathan Mantey 	struct timeval timeout;
72329a35f5SJeremy Kerr 	bool remove;
73329a35f5SJeremy Kerr };
74329a35f5SJeremy Kerr 
75f9c8f6caSCheng C Yang /* we have two extra entry in the pollfds array for the VUART tty */
76f9c8f6caSCheng C Yang enum internal_pollfds {
77f9c8f6caSCheng C Yang 	POLLFD_HOSTTTY = 0,
78f9c8f6caSCheng C Yang 	POLLFD_DBUS = 1,
79f9c8f6caSCheng C Yang 	MAX_INTERNAL_POLLFD = 2,
80f9c8f6caSCheng C Yang };
81329a35f5SJeremy Kerr 
82f733c85aSJeremy Kerr /* size of the shared backlog ringbuffer */
835db8c792SAndrew Jeffery const size_t buffer_size = 128ul * 1024ul;
84f733c85aSJeremy Kerr 
85769cee1aSJeremy Kerr /* state shared with the signal handler */
86769cee1aSJeremy Kerr static bool sigint;
87329a35f5SJeremy Kerr 
88d831f960SJeremy Kerr static void usage(const char *progname)
89d831f960SJeremy Kerr {
90d831f960SJeremy Kerr 	fprintf(stderr,
916221ce94SVishwanatha Subbanna 		"usage: %s [options] <DEVICE>\n"
92d831f960SJeremy Kerr 		"\n"
93d831f960SJeremy Kerr 		"Options:\n"
94d66195c1SJeremy Kerr 		"  --config <FILE>  Use FILE for configuration\n"
95d831f960SJeremy Kerr 		"",
96d831f960SJeremy Kerr 		progname);
97d831f960SJeremy Kerr }
98d831f960SJeremy Kerr 
9917217845SJeremy Kerr /* populates tty_dev and tty_sysfs_devnode, using the tty kernel name */
1001a0e03b4SJeremy Kerr static int tty_find_device(struct console *console)
10117217845SJeremy Kerr {
10217217845SJeremy Kerr 	char *tty_class_device_link;
10317217845SJeremy Kerr 	char *tty_device_tty_dir;
10417217845SJeremy Kerr 	char *tty_device_reldir;
10545ad7676SYi Li 	char *tty_path_input;
10645ad7676SYi Li 	char *tty_path_input_real;
10745ad7676SYi Li 	char *tty_kname_real;
10817217845SJeremy Kerr 	int rc;
10917217845SJeremy Kerr 
11017217845SJeremy Kerr 	tty_class_device_link = NULL;
11117217845SJeremy Kerr 	tty_device_tty_dir = NULL;
11217217845SJeremy Kerr 	tty_device_reldir = NULL;
11345ad7676SYi Li 	tty_path_input = NULL;
11445ad7676SYi Li 	tty_path_input_real = NULL;
11545ad7676SYi Li 	tty_kname_real = NULL;
11617217845SJeremy Kerr 
11745ad7676SYi Li 	/* udev may rename the tty name with a symbol link, try to resolve */
11845ad7676SYi Li 	rc = asprintf(&tty_path_input, "/dev/%s", console->tty_kname);
11917217845SJeremy Kerr 	if (rc < 0)
12017217845SJeremy Kerr 		return -1;
12117217845SJeremy Kerr 
12245ad7676SYi Li 	tty_path_input_real = realpath(tty_path_input, NULL);
12345ad7676SYi Li 	if (!tty_path_input_real) {
12445ad7676SYi Li 		warn("Can't find realpath for /dev/%s", console->tty_kname);
12515792aa7SAndrew Jeffery 		rc = -1;
12645ad7676SYi Li 		goto out_free;
12745ad7676SYi Li 	}
12845ad7676SYi Li 
12945ad7676SYi Li 	tty_kname_real = basename(tty_path_input_real);
13045ad7676SYi Li 	if (!tty_kname_real) {
13145ad7676SYi Li 		warn("Can't find real name for /dev/%s", console->tty_kname);
13215792aa7SAndrew Jeffery 		rc = -1;
13345ad7676SYi Li 		goto out_free;
13445ad7676SYi Li 	}
13545ad7676SYi Li 
136a72711afSAndrew Jeffery 	rc = asprintf(&tty_class_device_link, "/sys/class/tty/%s",
137a72711afSAndrew Jeffery 		      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);
14415792aa7SAndrew Jeffery 		rc = -1;
14517217845SJeremy Kerr 		goto out_free;
14617217845SJeremy Kerr 	}
14717217845SJeremy Kerr 
14817217845SJeremy Kerr 	rc = asprintf(&tty_device_reldir, "%s/../../", tty_device_tty_dir);
14917217845SJeremy Kerr 	if (rc < 0)
15017217845SJeremy Kerr 		goto out_free;
15117217845SJeremy Kerr 
1521a0e03b4SJeremy Kerr 	console->tty_sysfs_devnode = realpath(tty_device_reldir, NULL);
1531a0e03b4SJeremy Kerr 	if (!console->tty_sysfs_devnode)
15445ad7676SYi Li 		warn("Can't find parent device for %s", tty_kname_real);
15517217845SJeremy Kerr 
15645ad7676SYi Li 	rc = asprintf(&console->tty_dev, "/dev/%s", tty_kname_real);
15717217845SJeremy Kerr 	if (rc < 0)
15817217845SJeremy Kerr 		goto out_free;
15917217845SJeremy Kerr 
16017217845SJeremy Kerr 	rc = 0;
16117217845SJeremy Kerr 
16217217845SJeremy Kerr out_free:
16317217845SJeremy Kerr 	free(tty_class_device_link);
16417217845SJeremy Kerr 	free(tty_device_tty_dir);
16517217845SJeremy Kerr 	free(tty_device_reldir);
16645ad7676SYi Li 	free(tty_path_input);
16745ad7676SYi Li 	free(tty_path_input_real);
16817217845SJeremy Kerr 	return rc;
16917217845SJeremy Kerr }
17017217845SJeremy Kerr 
1711a0e03b4SJeremy Kerr static int tty_set_sysfs_attr(struct console *console, const char *name,
172957818b4SJeremy Kerr 			      int value)
173957818b4SJeremy Kerr {
174957818b4SJeremy Kerr 	char *path;
175957818b4SJeremy Kerr 	FILE *fp;
176957818b4SJeremy Kerr 	int rc;
177957818b4SJeremy Kerr 
1781a0e03b4SJeremy Kerr 	rc = asprintf(&path, "%s/%s", console->tty_sysfs_devnode, name);
179957818b4SJeremy Kerr 	if (rc < 0)
180957818b4SJeremy Kerr 		return -1;
181957818b4SJeremy Kerr 
182957818b4SJeremy Kerr 	fp = fopen(path, "w");
183957818b4SJeremy Kerr 	if (!fp) {
184a72711afSAndrew Jeffery 		warn("Can't access attribute %s on device %s", name,
185a72711afSAndrew Jeffery 		     console->tty_kname);
186957818b4SJeremy Kerr 		rc = -1;
187957818b4SJeremy Kerr 		goto out_free;
188957818b4SJeremy Kerr 	}
189957818b4SJeremy Kerr 	setvbuf(fp, NULL, _IONBF, 0);
190957818b4SJeremy Kerr 
191957818b4SJeremy Kerr 	rc = fprintf(fp, "0x%x", value);
192957818b4SJeremy Kerr 	if (rc < 0)
193a72711afSAndrew Jeffery 		warn("Error writing to %s attribute of device %s", name,
194a72711afSAndrew Jeffery 		     console->tty_kname);
195957818b4SJeremy Kerr 	fclose(fp);
196957818b4SJeremy Kerr 
197957818b4SJeremy Kerr out_free:
198957818b4SJeremy Kerr 	free(path);
199957818b4SJeremy Kerr 	return rc;
200957818b4SJeremy Kerr }
201957818b4SJeremy Kerr 
202d831f960SJeremy Kerr /**
203c7fbcd48SBenjamin Fair  * Set termios attributes on the console tty.
20454e9569dSJeremy Kerr  */
20554e9569dSJeremy Kerr static void tty_init_termios(struct console *console)
20654e9569dSJeremy Kerr {
20754e9569dSJeremy Kerr 	struct termios termios;
20854e9569dSJeremy Kerr 	int rc;
20954e9569dSJeremy Kerr 
21054e9569dSJeremy Kerr 	rc = tcgetattr(console->tty_fd, &termios);
21154e9569dSJeremy Kerr 	if (rc) {
21254e9569dSJeremy Kerr 		warn("Can't read tty termios");
21354e9569dSJeremy Kerr 		return;
21454e9569dSJeremy Kerr 	}
21554e9569dSJeremy Kerr 
216c7fbcd48SBenjamin Fair 	if (console->tty_baud) {
217c7fbcd48SBenjamin Fair 		if (cfsetspeed(&termios, console->tty_baud) < 0)
218c7fbcd48SBenjamin Fair 			warn("Couldn't set speeds for %s", console->tty_kname);
219c7fbcd48SBenjamin Fair 	}
220c7fbcd48SBenjamin Fair 
221c7fbcd48SBenjamin Fair 	/* Set console to raw mode: we don't want any processing to occur on
222c7fbcd48SBenjamin Fair 	 * the underlying terminal input/output.
223c7fbcd48SBenjamin Fair 	 */
22454e9569dSJeremy Kerr 	cfmakeraw(&termios);
225c7fbcd48SBenjamin Fair 
22654e9569dSJeremy Kerr 	rc = tcsetattr(console->tty_fd, TCSANOW, &termios);
22754e9569dSJeremy Kerr 	if (rc)
228c7fbcd48SBenjamin Fair 		warn("Can't set terminal options for %s", console->tty_kname);
22954e9569dSJeremy Kerr }
23054e9569dSJeremy Kerr 
231f9c8f6caSCheng C Yang static void tty_change_baudrate(struct console *console)
232f9c8f6caSCheng C Yang {
233f9c8f6caSCheng C Yang 	struct handler *handler;
234f9c8f6caSCheng C Yang 	int i, rc;
235f9c8f6caSCheng C Yang 
236f9c8f6caSCheng C Yang 	tty_init_termios(console);
237f9c8f6caSCheng C Yang 
238f9c8f6caSCheng C Yang 	for (i = 0; i < console->n_handlers; i++) {
239f9c8f6caSCheng C Yang 		handler = console->handlers[i];
240f9c8f6caSCheng C Yang 		if (!handler->baudrate)
241f9c8f6caSCheng C Yang 			continue;
242f9c8f6caSCheng C Yang 
243f9c8f6caSCheng C Yang 		rc = handler->baudrate(handler, console->tty_baud);
244f9c8f6caSCheng C Yang 		if (rc)
245f9c8f6caSCheng C Yang 			warnx("Can't set terminal baudrate for handler %s",
246f9c8f6caSCheng C Yang 			      handler->name);
247f9c8f6caSCheng C Yang 	}
248f9c8f6caSCheng C Yang }
249f9c8f6caSCheng C Yang 
25054e9569dSJeremy Kerr /**
251d831f960SJeremy Kerr  * Open and initialise the serial device
252d831f960SJeremy Kerr  */
2531a0e03b4SJeremy Kerr static int tty_init_io(struct console *console)
254d831f960SJeremy Kerr {
2551a0e03b4SJeremy Kerr 	if (console->tty_sirq)
2561a0e03b4SJeremy Kerr 		tty_set_sysfs_attr(console, "sirq", console->tty_sirq);
2571a0e03b4SJeremy Kerr 	if (console->tty_lpc_addr)
2581a0e03b4SJeremy Kerr 		tty_set_sysfs_attr(console, "lpc_address",
2591a0e03b4SJeremy Kerr 				   console->tty_lpc_addr);
260957818b4SJeremy Kerr 
2611a0e03b4SJeremy Kerr 	console->tty_fd = open(console->tty_dev, O_RDWR);
2621a0e03b4SJeremy Kerr 	if (console->tty_fd <= 0) {
2631a0e03b4SJeremy Kerr 		warn("Can't open tty %s", console->tty_dev);
264d831f960SJeremy Kerr 		return -1;
265d831f960SJeremy Kerr 	}
266d831f960SJeremy Kerr 
267d831f960SJeremy Kerr 	/* Disable character delay. We may want to later enable this when
268d831f960SJeremy Kerr 	 * we detect larger amounts of data
269d831f960SJeremy Kerr 	 */
2701a0e03b4SJeremy Kerr 	fcntl(console->tty_fd, F_SETFL, FNDELAY);
271d831f960SJeremy Kerr 
27254e9569dSJeremy Kerr 	tty_init_termios(console);
27354e9569dSJeremy Kerr 
274329a35f5SJeremy Kerr 	console->pollfds[console->n_pollers].fd = console->tty_fd;
275329a35f5SJeremy Kerr 	console->pollfds[console->n_pollers].events = POLLIN;
276329a35f5SJeremy Kerr 
277d831f960SJeremy Kerr 	return 0;
278d831f960SJeremy Kerr }
279d831f960SJeremy Kerr 
280d66195c1SJeremy Kerr static int tty_init(struct console *console, struct config *config)
281d66195c1SJeremy Kerr {
282fd883a88SAndrew Jeffery 	unsigned long parsed;
283d66195c1SJeremy Kerr 	const char *val;
284d66195c1SJeremy Kerr 	char *endp;
285d66195c1SJeremy Kerr 	int rc;
286d66195c1SJeremy Kerr 
287d66195c1SJeremy Kerr 	val = config_get_value(config, "lpc-address");
288d66195c1SJeremy Kerr 	if (val) {
289fd883a88SAndrew Jeffery 		errno = 0;
290fd883a88SAndrew Jeffery 		parsed = strtoul(val, &endp, 0);
291fd883a88SAndrew Jeffery 		if (parsed == ULONG_MAX && errno == ERANGE) {
292fd883a88SAndrew Jeffery 			warn("Cannot interpret 'lpc-address' value as an unsigned long: '%s'",
293fd883a88SAndrew Jeffery 			     val);
294fd883a88SAndrew Jeffery 			return -1;
295fd883a88SAndrew Jeffery 		}
296fd883a88SAndrew Jeffery 
297fd883a88SAndrew Jeffery 		if (parsed > UINT16_MAX) {
298fd883a88SAndrew Jeffery 			warn("Invalid LPC address '%s'", val);
299fd883a88SAndrew Jeffery 			return -1;
300fd883a88SAndrew Jeffery 		}
301fd883a88SAndrew Jeffery 
302fd883a88SAndrew Jeffery 		console->tty_lpc_addr = (uint16_t)parsed;
303d66195c1SJeremy Kerr 		if (endp == optarg) {
304d66195c1SJeremy Kerr 			warn("Invalid LPC address: '%s'", val);
305d66195c1SJeremy Kerr 			return -1;
306d66195c1SJeremy Kerr 		}
307d66195c1SJeremy Kerr 	}
308d66195c1SJeremy Kerr 
309d66195c1SJeremy Kerr 	val = config_get_value(config, "sirq");
310d66195c1SJeremy Kerr 	if (val) {
311fd883a88SAndrew Jeffery 		errno = 0;
312fd883a88SAndrew Jeffery 		parsed = strtoul(val, &endp, 0);
313fd883a88SAndrew Jeffery 		if (parsed == ULONG_MAX && errno == ERANGE) {
314fd883a88SAndrew Jeffery 			warn("Cannot interpret 'sirq' value as an unsigned long: '%s'",
315fd883a88SAndrew Jeffery 			     val);
316fd883a88SAndrew Jeffery 		}
317fd883a88SAndrew Jeffery 
318fd883a88SAndrew Jeffery 		if (parsed > 16)
319fd883a88SAndrew Jeffery 			warn("Invalid LPC SERIRQ: '%s'", val);
320fd883a88SAndrew Jeffery 
321fd883a88SAndrew Jeffery 		console->tty_sirq = (int)parsed;
322d66195c1SJeremy Kerr 		if (endp == optarg)
323d66195c1SJeremy Kerr 			warn("Invalid sirq: '%s'", val);
324d66195c1SJeremy Kerr 	}
325d66195c1SJeremy Kerr 
326c7fbcd48SBenjamin Fair 	val = config_get_value(config, "baud");
327c7fbcd48SBenjamin Fair 	if (val) {
328c7fbcd48SBenjamin Fair 		if (config_parse_baud(&console->tty_baud, val))
329c7fbcd48SBenjamin Fair 			warnx("Invalid baud rate: '%s'", val);
330c7fbcd48SBenjamin Fair 	}
331c7fbcd48SBenjamin Fair 
332d66195c1SJeremy Kerr 	if (!console->tty_kname) {
333d66195c1SJeremy Kerr 		warnx("Error: No TTY device specified");
334d66195c1SJeremy Kerr 		return -1;
335d66195c1SJeremy Kerr 	}
336d66195c1SJeremy Kerr 
337d66195c1SJeremy Kerr 	rc = tty_find_device(console);
338d66195c1SJeremy Kerr 	if (rc)
339d66195c1SJeremy Kerr 		return rc;
340d66195c1SJeremy Kerr 
341d66195c1SJeremy Kerr 	rc = tty_init_io(console);
342d66195c1SJeremy Kerr 	return rc;
343d66195c1SJeremy Kerr }
344d66195c1SJeremy Kerr 
3451a0e03b4SJeremy Kerr int console_data_out(struct console *console, const uint8_t *data, size_t len)
346d831f960SJeremy Kerr {
3471a0e03b4SJeremy Kerr 	return write_buf_to_fd(console->tty_fd, data, len);
348d831f960SJeremy Kerr }
349d831f960SJeremy Kerr 
350f9c8f6caSCheng C Yang static int method_set_baud_rate(sd_bus_message *msg, void *userdata,
351f9c8f6caSCheng C Yang 				sd_bus_error *err)
352f9c8f6caSCheng C Yang {
353f9c8f6caSCheng C Yang 	struct console *console = userdata;
354f9c8f6caSCheng C Yang 	uint32_t baudrate;
355f9c8f6caSCheng C Yang 	speed_t speed;
356f9c8f6caSCheng C Yang 	int r;
357f9c8f6caSCheng C Yang 
358f9c8f6caSCheng C Yang 	if (!console) {
359f9c8f6caSCheng C Yang 		sd_bus_error_set_const(err, DBUS_ERR, "Internal error");
360f9c8f6caSCheng C Yang 		return sd_bus_reply_method_return(msg, "x", 0);
361f9c8f6caSCheng C Yang 	}
362f9c8f6caSCheng C Yang 
363f9c8f6caSCheng C Yang 	r = sd_bus_message_read(msg, "u", &baudrate);
364f9c8f6caSCheng C Yang 	if (r < 0) {
365f9c8f6caSCheng C Yang 		sd_bus_error_set_const(err, DBUS_ERR, "Bad message");
366f9c8f6caSCheng C Yang 		return sd_bus_reply_method_return(msg, "x", -EINVAL);
367f9c8f6caSCheng C Yang 	}
368f9c8f6caSCheng C Yang 
369f9c8f6caSCheng C Yang 	speed = parse_int_to_baud(baudrate);
370f9c8f6caSCheng C Yang 	if (!speed) {
371f9c8f6caSCheng C Yang 		warnx("Invalid baud rate: '%u'", baudrate);
372f9c8f6caSCheng C Yang 		return sd_bus_reply_method_return(msg, "x", -EINVAL);
373f9c8f6caSCheng C Yang 	}
374f9c8f6caSCheng C Yang 
375f9c8f6caSCheng C Yang 	console->tty_baud = speed;
376f9c8f6caSCheng C Yang 	tty_change_baudrate(console);
377f9c8f6caSCheng C Yang 
378f9c8f6caSCheng C Yang 	return sd_bus_reply_method_return(msg, "x", r);
379f9c8f6caSCheng C Yang }
380f9c8f6caSCheng C Yang 
381fd048328SAndrew Jeffery static int get_handler(sd_bus *bus __attribute__((unused)),
382fd048328SAndrew Jeffery 		       const char *path __attribute__((unused)),
383fd048328SAndrew Jeffery 		       const char *interface __attribute__((unused)),
384fd048328SAndrew Jeffery 		       const char *property __attribute__((unused)),
385fd048328SAndrew Jeffery 		       sd_bus_message *reply, void *userdata,
386a72711afSAndrew Jeffery 		       sd_bus_error *error __attribute__((unused)))
387a72711afSAndrew Jeffery {
388f9c8f6caSCheng C Yang 	struct console *console = userdata;
389f9c8f6caSCheng C Yang 	uint32_t baudrate;
390f9c8f6caSCheng C Yang 	int r;
391f9c8f6caSCheng C Yang 
392f9c8f6caSCheng C Yang 	baudrate = parse_baud_to_int(console->tty_baud);
393f9c8f6caSCheng C Yang 	if (!baudrate)
394f9c8f6caSCheng C Yang 		warnx("Invalid baud rate: '%d'", console->tty_baud);
395f9c8f6caSCheng C Yang 
396f9c8f6caSCheng C Yang 	r = sd_bus_message_append(reply, "u", baudrate);
397f9c8f6caSCheng C Yang 
398f9c8f6caSCheng C Yang 	return r;
399f9c8f6caSCheng C Yang }
400f9c8f6caSCheng C Yang 
401f9c8f6caSCheng C Yang static const sd_bus_vtable console_vtable[] = {
402f9c8f6caSCheng C Yang 	SD_BUS_VTABLE_START(0),
403f9c8f6caSCheng C Yang 	SD_BUS_METHOD("setBaudRate", "u", "x", method_set_baud_rate,
404f9c8f6caSCheng C Yang 		      SD_BUS_VTABLE_UNPRIVILEGED),
405f9c8f6caSCheng C Yang 	SD_BUS_PROPERTY("baudrate", "u", get_handler, 0, 0),
406a72711afSAndrew Jeffery 	SD_BUS_VTABLE_END,
407a72711afSAndrew Jeffery };
408f9c8f6caSCheng C Yang 
409a72711afSAndrew Jeffery static void dbus_init(struct console *console,
410a72711afSAndrew Jeffery 		      struct config *config __attribute__((unused)))
411f9c8f6caSCheng C Yang {
412f9c8f6caSCheng C Yang 	int dbus_poller = 0;
413f9c8f6caSCheng C Yang 	int fd, r;
414f9c8f6caSCheng C Yang 
415f9c8f6caSCheng C Yang 	if (!console) {
416f9c8f6caSCheng C Yang 		warnx("Couldn't get valid console");
417f9c8f6caSCheng C Yang 		return;
418f9c8f6caSCheng C Yang 	}
419f9c8f6caSCheng C Yang 
420f9c8f6caSCheng C Yang 	r = sd_bus_default_system(&console->bus);
421f9c8f6caSCheng C Yang 	if (r < 0) {
422f9c8f6caSCheng C Yang 		warnx("Failed to connect to system bus: %s", strerror(-r));
423f9c8f6caSCheng C Yang 		return;
424f9c8f6caSCheng C Yang 	}
425f9c8f6caSCheng C Yang 
426f9c8f6caSCheng C Yang 	r = sd_bus_add_object_vtable(console->bus, NULL, OBJ_NAME, DBUS_NAME,
427f9c8f6caSCheng C Yang 				     console_vtable, console);
428f9c8f6caSCheng C Yang 	if (r < 0) {
429f9c8f6caSCheng C Yang 		warnx("Failed to issue method call: %s", strerror(-r));
430f9c8f6caSCheng C Yang 		return;
431f9c8f6caSCheng C Yang 	}
432f9c8f6caSCheng C Yang 
433a72711afSAndrew Jeffery 	r = sd_bus_request_name(console->bus, DBUS_NAME,
434a72711afSAndrew Jeffery 				SD_BUS_NAME_ALLOW_REPLACEMENT |
435a72711afSAndrew Jeffery 					SD_BUS_NAME_REPLACE_EXISTING);
436f9c8f6caSCheng C Yang 	if (r < 0) {
437f9c8f6caSCheng C Yang 		warnx("Failed to acquire service name: %s", strerror(-r));
438f9c8f6caSCheng C Yang 		return;
439f9c8f6caSCheng C Yang 	}
440f9c8f6caSCheng C Yang 
441f9c8f6caSCheng C Yang 	fd = sd_bus_get_fd(console->bus);
442f9c8f6caSCheng C Yang 	if (fd < 0) {
443f9c8f6caSCheng C Yang 		warnx("Couldn't get the bus file descriptor");
444f9c8f6caSCheng C Yang 		return;
445f9c8f6caSCheng C Yang 	}
446f9c8f6caSCheng C Yang 
447f9c8f6caSCheng C Yang 	dbus_poller = POLLFD_DBUS;
448f9c8f6caSCheng C Yang 
449f9c8f6caSCheng C Yang 	console->pollfds[dbus_poller].fd = fd;
450f9c8f6caSCheng C Yang 	console->pollfds[dbus_poller].events = POLLIN;
451f9c8f6caSCheng C Yang }
452f9c8f6caSCheng C Yang 
453d47963e5SJeremy Kerr static void handlers_init(struct console *console, struct config *config)
454d831f960SJeremy Kerr {
455750fb0c0SAndrew Jeffery 	/* NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) */
4561a0e03b4SJeremy Kerr 	extern struct handler *__start_handlers, *__stop_handlers;
4571a0e03b4SJeremy Kerr 	struct handler *handler;
458021b91f0SJeremy Kerr 	int i, rc;
459d831f960SJeremy Kerr 
4601a0e03b4SJeremy Kerr 	console->n_handlers = &__stop_handlers - &__start_handlers;
4611a0e03b4SJeremy Kerr 	console->handlers = &__start_handlers;
462d831f960SJeremy Kerr 
4635c359cc6SAndrew Jeffery 	printf("%ld handler%s\n", console->n_handlers,
4641a0e03b4SJeremy Kerr 	       console->n_handlers == 1 ? "" : "s");
465d831f960SJeremy Kerr 
4661a0e03b4SJeremy Kerr 	for (i = 0; i < console->n_handlers; i++) {
4671a0e03b4SJeremy Kerr 		handler = console->handlers[i];
4681a0e03b4SJeremy Kerr 
469021b91f0SJeremy Kerr 		rc = 0;
4701a0e03b4SJeremy Kerr 		if (handler->init)
471021b91f0SJeremy Kerr 			rc = handler->init(handler, console, config);
472021b91f0SJeremy Kerr 
473021b91f0SJeremy Kerr 		handler->active = rc == 0;
474021b91f0SJeremy Kerr 
475021b91f0SJeremy Kerr 		printf("  %s [%sactive]\n", handler->name,
476021b91f0SJeremy Kerr 		       handler->active ? "" : "in");
477d831f960SJeremy Kerr 	}
478d831f960SJeremy Kerr }
479d831f960SJeremy Kerr 
4801a0e03b4SJeremy Kerr static void handlers_fini(struct console *console)
481d831f960SJeremy Kerr {
4821a0e03b4SJeremy Kerr 	struct handler *handler;
4831a0e03b4SJeremy Kerr 	int i;
4841a0e03b4SJeremy Kerr 
4851a0e03b4SJeremy Kerr 	for (i = 0; i < console->n_handlers; i++) {
4861a0e03b4SJeremy Kerr 		handler = console->handlers[i];
487021b91f0SJeremy Kerr 		if (handler->fini && handler->active)
4881a0e03b4SJeremy Kerr 			handler->fini(handler);
4891a0e03b4SJeremy Kerr 	}
490d831f960SJeremy Kerr }
491d831f960SJeremy Kerr 
4921cecc5deSJohnathan Mantey static int get_current_time(struct timeval *tv)
4931cecc5deSJohnathan Mantey {
4941cecc5deSJohnathan Mantey 	struct timespec t;
4951cecc5deSJohnathan Mantey 	int rc;
4961cecc5deSJohnathan Mantey 
4971cecc5deSJohnathan Mantey 	/*
4981cecc5deSJohnathan Mantey 	 * We use clock_gettime(CLOCK_MONOTONIC) so we're immune to
4991cecc5deSJohnathan Mantey 	 * local time changes. However, a struct timeval is more
5001cecc5deSJohnathan Mantey 	 * convenient for calculations, so convert to that.
5011cecc5deSJohnathan Mantey 	 */
5021cecc5deSJohnathan Mantey 	rc = clock_gettime(CLOCK_MONOTONIC, &t);
5031cecc5deSJohnathan Mantey 	if (rc)
5041cecc5deSJohnathan Mantey 		return rc;
5051cecc5deSJohnathan Mantey 
5061cecc5deSJohnathan Mantey 	tv->tv_sec = t.tv_sec;
5071cecc5deSJohnathan Mantey 	tv->tv_usec = t.tv_nsec / 1000;
5081cecc5deSJohnathan Mantey 
5091cecc5deSJohnathan Mantey 	return 0;
5101cecc5deSJohnathan Mantey }
5111cecc5deSJohnathan Mantey 
512a72711afSAndrew Jeffery struct ringbuffer_consumer *
513a72711afSAndrew Jeffery console_ringbuffer_consumer_register(struct console *console,
514f733c85aSJeremy Kerr 				     ringbuffer_poll_fn_t poll_fn, void *data)
515d831f960SJeremy Kerr {
516f733c85aSJeremy Kerr 	return ringbuffer_consumer_register(console->rb, poll_fn, data);
517d831f960SJeremy Kerr }
518d831f960SJeremy Kerr 
51955c9712dSJeremy Kerr struct poller *console_poller_register(struct console *console,
520a72711afSAndrew Jeffery 				       struct handler *handler,
521a72711afSAndrew Jeffery 				       poller_event_fn_t poller_fn,
5221cecc5deSJohnathan Mantey 				       poller_timeout_fn_t timeout_fn, int fd,
5231cecc5deSJohnathan Mantey 				       int events, void *data)
524d831f960SJeremy Kerr {
525329a35f5SJeremy Kerr 	struct poller *poller;
5265c359cc6SAndrew Jeffery 	long n;
527329a35f5SJeremy Kerr 
528329a35f5SJeremy Kerr 	poller = malloc(sizeof(*poller));
529329a35f5SJeremy Kerr 	poller->remove = false;
530329a35f5SJeremy Kerr 	poller->handler = handler;
5311cecc5deSJohnathan Mantey 	poller->event_fn = poller_fn;
5321cecc5deSJohnathan Mantey 	poller->timeout_fn = timeout_fn;
533329a35f5SJeremy Kerr 	poller->data = data;
534329a35f5SJeremy Kerr 
535329a35f5SJeremy Kerr 	/* add one to our pollers array */
536329a35f5SJeremy Kerr 	n = console->n_pollers++;
537*91b52175SAndrew Jeffery 	/*
538*91b52175SAndrew Jeffery 	 * We're managing an array of pointers to aggregates, so don't warn about sizeof() on a
539*91b52175SAndrew Jeffery 	 * pointer type.
540*91b52175SAndrew Jeffery 	 */
541*91b52175SAndrew Jeffery 	/* NOLINTBEGIN(bugprone-sizeof-expression) */
542*91b52175SAndrew Jeffery 	console->pollers = reallocarray(console->pollers, console->n_pollers,
543*91b52175SAndrew Jeffery 					sizeof(*console->pollers));
544*91b52175SAndrew Jeffery 	/* NOLINTEND(bugprone-sizeof-expression) */
545329a35f5SJeremy Kerr 
546329a35f5SJeremy Kerr 	console->pollers[n] = poller;
547329a35f5SJeremy Kerr 
548329a35f5SJeremy Kerr 	/* increase pollfds array too  */
549a72711afSAndrew Jeffery 	console->pollfds =
550*91b52175SAndrew Jeffery 		reallocarray(console->pollfds,
551*91b52175SAndrew Jeffery 			     (MAX_INTERNAL_POLLFD + console->n_pollers),
552*91b52175SAndrew Jeffery 			     sizeof(*console->pollfds));
553329a35f5SJeremy Kerr 
554329a35f5SJeremy Kerr 	/* shift the end pollfds up by one */
555a72711afSAndrew Jeffery 	memcpy(&console->pollfds[n + 1], &console->pollfds[n],
556f9c8f6caSCheng C Yang 	       sizeof(*console->pollfds) * MAX_INTERNAL_POLLFD);
557329a35f5SJeremy Kerr 
558329a35f5SJeremy Kerr 	console->pollfds[n].fd = fd;
5595c359cc6SAndrew Jeffery 	console->pollfds[n].events = (short)(events & 0x7fff);
560329a35f5SJeremy Kerr 
561329a35f5SJeremy Kerr 	return poller;
562329a35f5SJeremy Kerr }
563329a35f5SJeremy Kerr 
564a72711afSAndrew Jeffery void console_poller_unregister(struct console *console, struct poller *poller)
565329a35f5SJeremy Kerr {
566329a35f5SJeremy Kerr 	int i;
567329a35f5SJeremy Kerr 
568329a35f5SJeremy Kerr 	/* find the entry in our pollers array */
569329a35f5SJeremy Kerr 	for (i = 0; i < console->n_pollers; i++)
570329a35f5SJeremy Kerr 		if (console->pollers[i] == poller)
571329a35f5SJeremy Kerr 			break;
572329a35f5SJeremy Kerr 
573329a35f5SJeremy Kerr 	assert(i < console->n_pollers);
574329a35f5SJeremy Kerr 
575329a35f5SJeremy Kerr 	console->n_pollers--;
576329a35f5SJeremy Kerr 
577*91b52175SAndrew Jeffery 	/*
578*91b52175SAndrew Jeffery 	 * Remove the item from the pollers array...
579*91b52175SAndrew Jeffery 	 *
580*91b52175SAndrew Jeffery 	 * We're managing an array of pointers to aggregates, so don't warn about sizeof() on a
581*91b52175SAndrew Jeffery 	 * pointer type.
582*91b52175SAndrew Jeffery 	 */
583*91b52175SAndrew Jeffery 	/* NOLINTBEGIN(bugprone-sizeof-expression) */
584329a35f5SJeremy Kerr 	memmove(&console->pollers[i], &console->pollers[i + 1],
585a72711afSAndrew Jeffery 		sizeof(*console->pollers) * (console->n_pollers - i));
586329a35f5SJeremy Kerr 
587*91b52175SAndrew Jeffery 	console->pollers = reallocarray(console->pollers, console->n_pollers,
588*91b52175SAndrew Jeffery 					sizeof(*console->pollers));
589*91b52175SAndrew Jeffery 	/* NOLINTEND(bugprone-sizeof-expression) */
590329a35f5SJeremy Kerr 
591329a35f5SJeremy Kerr 	/* ... and the pollfds array */
592329a35f5SJeremy Kerr 	memmove(&console->pollfds[i], &console->pollfds[i + 1],
593329a35f5SJeremy Kerr 		sizeof(*console->pollfds) *
594f9c8f6caSCheng C Yang 			(MAX_INTERNAL_POLLFD + console->n_pollers - i));
595329a35f5SJeremy Kerr 
596a72711afSAndrew Jeffery 	console->pollfds =
597*91b52175SAndrew Jeffery 		reallocarray(console->pollfds,
598*91b52175SAndrew Jeffery 			     (MAX_INTERNAL_POLLFD + console->n_pollers),
599*91b52175SAndrew Jeffery 			     sizeof(*console->pollfds));
600329a35f5SJeremy Kerr 
601329a35f5SJeremy Kerr 	free(poller);
602329a35f5SJeremy Kerr }
603329a35f5SJeremy Kerr 
6046b1fed27SJeremy Kerr void console_poller_set_events(struct console *console, struct poller *poller,
6056b1fed27SJeremy Kerr 			       int events)
6066b1fed27SJeremy Kerr {
6076b1fed27SJeremy Kerr 	int i;
6086b1fed27SJeremy Kerr 
6096b1fed27SJeremy Kerr 	/* find the entry in our pollers array */
6106b1fed27SJeremy Kerr 	for (i = 0; i < console->n_pollers; i++)
6116b1fed27SJeremy Kerr 		if (console->pollers[i] == poller)
6126b1fed27SJeremy Kerr 			break;
6136b1fed27SJeremy Kerr 
6145c359cc6SAndrew Jeffery 	console->pollfds[i].events = (short)(events & 0x7fff);
6156b1fed27SJeremy Kerr }
6166b1fed27SJeremy Kerr 
617fd048328SAndrew Jeffery void console_poller_set_timeout(struct console *console __attribute__((unused)),
618fd048328SAndrew Jeffery 				struct poller *poller, const struct timeval *tv)
6191cecc5deSJohnathan Mantey {
6201cecc5deSJohnathan Mantey 	struct timeval now;
6211cecc5deSJohnathan Mantey 	int rc;
6221cecc5deSJohnathan Mantey 
6231cecc5deSJohnathan Mantey 	rc = get_current_time(&now);
6241cecc5deSJohnathan Mantey 	if (rc)
6251cecc5deSJohnathan Mantey 		return;
6261cecc5deSJohnathan Mantey 
6271cecc5deSJohnathan Mantey 	timeradd(&now, tv, &poller->timeout);
6281cecc5deSJohnathan Mantey }
6291cecc5deSJohnathan Mantey 
6305c359cc6SAndrew Jeffery static long get_poll_timeout(struct console *console, struct timeval *cur_time)
6311cecc5deSJohnathan Mantey {
6321cecc5deSJohnathan Mantey 	struct timeval *earliest, interval;
6331cecc5deSJohnathan Mantey 	struct poller *poller;
6341cecc5deSJohnathan Mantey 	int i;
6351cecc5deSJohnathan Mantey 
6361cecc5deSJohnathan Mantey 	earliest = NULL;
6371cecc5deSJohnathan Mantey 
6381cecc5deSJohnathan Mantey 	for (i = 0; i < console->n_pollers; i++) {
6391cecc5deSJohnathan Mantey 		poller = console->pollers[i];
6401cecc5deSJohnathan Mantey 
6411cecc5deSJohnathan Mantey 		if (poller->timeout_fn && timerisset(&poller->timeout) &&
6421cecc5deSJohnathan Mantey 		    (!earliest ||
6431cecc5deSJohnathan Mantey 		     (earliest && timercmp(&poller->timeout, earliest, <)))) {
6441cecc5deSJohnathan Mantey 			// poller is buffering data and needs the poll
6451cecc5deSJohnathan Mantey 			// function to timeout.
6461cecc5deSJohnathan Mantey 			earliest = &poller->timeout;
6471cecc5deSJohnathan Mantey 		}
6481cecc5deSJohnathan Mantey 	}
6491cecc5deSJohnathan Mantey 
6501cecc5deSJohnathan Mantey 	if (earliest) {
6511cecc5deSJohnathan Mantey 		if (timercmp(earliest, cur_time, >)) {
6521cecc5deSJohnathan Mantey 			/* recalculate the timeout period, time period has
6531cecc5deSJohnathan Mantey 			 * not elapsed */
6541cecc5deSJohnathan Mantey 			timersub(earliest, cur_time, &interval);
6551cecc5deSJohnathan Mantey 			return ((interval.tv_sec * 1000) +
6561cecc5deSJohnathan Mantey 				(interval.tv_usec / 1000));
6571cecc5deSJohnathan Mantey 		} else {
6581cecc5deSJohnathan Mantey 			/* return from poll immediately */
6591cecc5deSJohnathan Mantey 			return 0;
6601cecc5deSJohnathan Mantey 		}
6611cecc5deSJohnathan Mantey 	} else {
6621cecc5deSJohnathan Mantey 		/* poll indefinitely */
6631cecc5deSJohnathan Mantey 		return -1;
6641cecc5deSJohnathan Mantey 	}
6651cecc5deSJohnathan Mantey }
6661cecc5deSJohnathan Mantey 
6671cecc5deSJohnathan Mantey static int call_pollers(struct console *console, struct timeval *cur_time)
668329a35f5SJeremy Kerr {
669329a35f5SJeremy Kerr 	struct poller *poller;
670329a35f5SJeremy Kerr 	struct pollfd *pollfd;
671329a35f5SJeremy Kerr 	enum poller_ret prc;
672329a35f5SJeremy Kerr 	int i, rc;
673d831f960SJeremy Kerr 
6741a0e03b4SJeremy Kerr 	rc = 0;
6751a0e03b4SJeremy Kerr 
676329a35f5SJeremy Kerr 	/*
677329a35f5SJeremy Kerr 	 * Process poll events by iterating through the pollers and pollfds
678329a35f5SJeremy Kerr 	 * in-step, calling any pollers that we've found revents for.
679329a35f5SJeremy Kerr 	 */
680329a35f5SJeremy Kerr 	for (i = 0; i < console->n_pollers; i++) {
681329a35f5SJeremy Kerr 		poller = console->pollers[i];
682329a35f5SJeremy Kerr 		pollfd = &console->pollfds[i];
6831cecc5deSJohnathan Mantey 		prc = POLLER_OK;
6841a0e03b4SJeremy Kerr 
6851cecc5deSJohnathan Mantey 		/* process pending events... */
6861cecc5deSJohnathan Mantey 		if (pollfd->revents) {
6871cecc5deSJohnathan Mantey 			prc = poller->event_fn(poller->handler, pollfd->revents,
688329a35f5SJeremy Kerr 					       poller->data);
689329a35f5SJeremy Kerr 			if (prc == POLLER_EXIT)
690329a35f5SJeremy Kerr 				rc = -1;
691329a35f5SJeremy Kerr 			else if (prc == POLLER_REMOVE)
692329a35f5SJeremy Kerr 				poller->remove = true;
693329a35f5SJeremy Kerr 		}
694329a35f5SJeremy Kerr 
6951cecc5deSJohnathan Mantey 		if ((prc == POLLER_OK) && poller->timeout_fn &&
6961cecc5deSJohnathan Mantey 		    timerisset(&poller->timeout) &&
6971cecc5deSJohnathan Mantey 		    timercmp(&poller->timeout, cur_time, <=)) {
6981cecc5deSJohnathan Mantey 			/* One of the ringbuffer consumers is buffering the
6991cecc5deSJohnathan Mantey 			data stream. The amount of idle time the consumer
7001cecc5deSJohnathan Mantey 			desired has expired.  Process the buffered data for
7011cecc5deSJohnathan Mantey 			transmission. */
7021cecc5deSJohnathan Mantey 			timerclear(&poller->timeout);
7031cecc5deSJohnathan Mantey 			prc = poller->timeout_fn(poller->handler, poller->data);
7041cecc5deSJohnathan Mantey 			if (prc == POLLER_EXIT) {
7051cecc5deSJohnathan Mantey 				rc = -1;
7061cecc5deSJohnathan Mantey 			} else if (prc == POLLER_REMOVE) {
7071cecc5deSJohnathan Mantey 				poller->remove = true;
7081cecc5deSJohnathan Mantey 			}
7091cecc5deSJohnathan Mantey 		}
7101cecc5deSJohnathan Mantey 	}
7111cecc5deSJohnathan Mantey 
712329a35f5SJeremy Kerr 	/**
713329a35f5SJeremy Kerr 	 * Process deferred removals; restarting each time we unregister, as
714329a35f5SJeremy Kerr 	 * the array will have changed
715329a35f5SJeremy Kerr 	 */
716329a35f5SJeremy Kerr 	for (;;) {
717329a35f5SJeremy Kerr 		bool removed = false;
718329a35f5SJeremy Kerr 
719329a35f5SJeremy Kerr 		for (i = 0; i < console->n_pollers; i++) {
720329a35f5SJeremy Kerr 			poller = console->pollers[i];
721329a35f5SJeremy Kerr 			if (poller->remove) {
72255c9712dSJeremy Kerr 				console_poller_unregister(console, poller);
723329a35f5SJeremy Kerr 				removed = true;
724329a35f5SJeremy Kerr 				break;
725329a35f5SJeremy Kerr 			}
726329a35f5SJeremy Kerr 		}
727329a35f5SJeremy Kerr 		if (!removed)
728329a35f5SJeremy Kerr 			break;
7291a0e03b4SJeremy Kerr 	}
7301a0e03b4SJeremy Kerr 
7311a0e03b4SJeremy Kerr 	return rc;
7321a0e03b4SJeremy Kerr }
7331a0e03b4SJeremy Kerr 
734769cee1aSJeremy Kerr static void sighandler(int signal)
735769cee1aSJeremy Kerr {
736769cee1aSJeremy Kerr 	if (signal == SIGINT)
737769cee1aSJeremy Kerr 		sigint = true;
738769cee1aSJeremy Kerr }
739769cee1aSJeremy Kerr 
7401a0e03b4SJeremy Kerr int run_console(struct console *console)
7411a0e03b4SJeremy Kerr {
7425c359cc6SAndrew Jeffery 	sighandler_t sighandler_save = signal(SIGINT, sighandler);
7431cecc5deSJohnathan Mantey 	struct timeval tv;
7445c359cc6SAndrew Jeffery 	long timeout;
7455c359cc6SAndrew Jeffery 	ssize_t rc;
746769cee1aSJeremy Kerr 
747769cee1aSJeremy Kerr 	rc = 0;
748769cee1aSJeremy Kerr 
749d831f960SJeremy Kerr 	for (;;) {
750d831f960SJeremy Kerr 		uint8_t buf[4096];
751d831f960SJeremy Kerr 
7521764145dSJeremy Kerr 		BUILD_ASSERT(sizeof(buf) <= buffer_size);
7531764145dSJeremy Kerr 
754769cee1aSJeremy Kerr 		if (sigint) {
755769cee1aSJeremy Kerr 			fprintf(stderr, "Received interrupt, exiting\n");
756769cee1aSJeremy Kerr 			break;
757769cee1aSJeremy Kerr 		}
758769cee1aSJeremy Kerr 
7591cecc5deSJohnathan Mantey 		rc = get_current_time(&tv);
7601cecc5deSJohnathan Mantey 		if (rc) {
7611cecc5deSJohnathan Mantey 			warn("Failed to read current time");
7621cecc5deSJohnathan Mantey 			break;
7631cecc5deSJohnathan Mantey 		}
7641cecc5deSJohnathan Mantey 
7651cecc5deSJohnathan Mantey 		timeout = get_poll_timeout(console, &tv);
7661cecc5deSJohnathan Mantey 
767329a35f5SJeremy Kerr 		rc = poll(console->pollfds,
7685c359cc6SAndrew Jeffery 			  console->n_pollers + MAX_INTERNAL_POLLFD,
7695c359cc6SAndrew Jeffery 			  (int)timeout);
7701cecc5deSJohnathan Mantey 
771d831f960SJeremy Kerr 		if (rc < 0) {
772769cee1aSJeremy Kerr 			if (errno == EINTR) {
773769cee1aSJeremy Kerr 				continue;
774769cee1aSJeremy Kerr 			} else {
775d831f960SJeremy Kerr 				warn("poll error");
776769cee1aSJeremy Kerr 				break;
777769cee1aSJeremy Kerr 			}
778d831f960SJeremy Kerr 		}
779d831f960SJeremy Kerr 
780329a35f5SJeremy Kerr 		/* process internal fd first */
781329a35f5SJeremy Kerr 		if (console->pollfds[console->n_pollers].revents) {
7821a0e03b4SJeremy Kerr 			rc = read(console->tty_fd, buf, sizeof(buf));
783d831f960SJeremy Kerr 			if (rc <= 0) {
784d831f960SJeremy Kerr 				warn("Error reading from tty device");
785769cee1aSJeremy Kerr 				rc = -1;
786769cee1aSJeremy Kerr 				break;
787d831f960SJeremy Kerr 			}
788f733c85aSJeremy Kerr 			rc = ringbuffer_queue(console->rb, buf, rc);
7891a0e03b4SJeremy Kerr 			if (rc)
790769cee1aSJeremy Kerr 				break;
791d831f960SJeremy Kerr 		}
792d831f960SJeremy Kerr 
793f9c8f6caSCheng C Yang 		if (console->pollfds[console->n_pollers + 1].revents) {
794f9c8f6caSCheng C Yang 			sd_bus_process(console->bus, NULL);
795f9c8f6caSCheng C Yang 		}
796f9c8f6caSCheng C Yang 
797329a35f5SJeremy Kerr 		/* ... and then the pollers */
7981cecc5deSJohnathan Mantey 		rc = call_pollers(console, &tv);
7991a0e03b4SJeremy Kerr 		if (rc)
800769cee1aSJeremy Kerr 			break;
8011a0e03b4SJeremy Kerr 	}
802769cee1aSJeremy Kerr 
803769cee1aSJeremy Kerr 	signal(SIGINT, sighandler_save);
804f9c8f6caSCheng C Yang 	sd_bus_unref(console->bus);
805769cee1aSJeremy Kerr 
806769cee1aSJeremy Kerr 	return rc ? -1 : 0;
8071a0e03b4SJeremy Kerr }
808d831f960SJeremy Kerr static const struct option options[] = {
809d66195c1SJeremy Kerr 	{ "config", required_argument, 0, 'c' },
810f5858b5bSJoel Stanley 	{ 0, 0, 0, 0 },
811d831f960SJeremy Kerr };
812d831f960SJeremy Kerr 
813d831f960SJeremy Kerr int main(int argc, char **argv)
814d831f960SJeremy Kerr {
815d66195c1SJeremy Kerr 	const char *config_filename = NULL;
8166221ce94SVishwanatha Subbanna 	const char *config_tty_kname = NULL;
8171a0e03b4SJeremy Kerr 	struct console *console;
818d66195c1SJeremy Kerr 	struct config *config;
819d66195c1SJeremy Kerr 	int rc;
820d831f960SJeremy Kerr 
821957818b4SJeremy Kerr 	rc = -1;
822d831f960SJeremy Kerr 
823d831f960SJeremy Kerr 	for (;;) {
824d831f960SJeremy Kerr 		int c, idx;
825d831f960SJeremy Kerr 
826d66195c1SJeremy Kerr 		c = getopt_long(argc, argv, "c:", options, &idx);
827d831f960SJeremy Kerr 		if (c == -1)
828d831f960SJeremy Kerr 			break;
829d831f960SJeremy Kerr 
830d831f960SJeremy Kerr 		switch (c) {
831d66195c1SJeremy Kerr 		case 'c':
832d66195c1SJeremy Kerr 			config_filename = optarg;
833d831f960SJeremy Kerr 			break;
834d831f960SJeremy Kerr 		case 'h':
835d831f960SJeremy Kerr 		case '?':
836d831f960SJeremy Kerr 			usage(argv[0]);
837d66195c1SJeremy Kerr 			return EXIT_SUCCESS;
838d831f960SJeremy Kerr 		}
839d831f960SJeremy Kerr 	}
840d831f960SJeremy Kerr 
84191dde14eSAndrew Jeffery 	if (optind < argc)
8426221ce94SVishwanatha Subbanna 		config_tty_kname = argv[optind];
8436221ce94SVishwanatha Subbanna 
844d66195c1SJeremy Kerr 	console = malloc(sizeof(struct console));
845d66195c1SJeremy Kerr 	memset(console, 0, sizeof(*console));
846a72711afSAndrew Jeffery 	console->pollfds =
847a72711afSAndrew Jeffery 		calloc(MAX_INTERNAL_POLLFD, sizeof(*console->pollfds));
848f733c85aSJeremy Kerr 	console->rb = ringbuffer_init(buffer_size);
849329a35f5SJeremy Kerr 
850d66195c1SJeremy Kerr 	config = config_init(config_filename);
851d66195c1SJeremy Kerr 	if (!config) {
852d66195c1SJeremy Kerr 		warnx("Can't read configuration, exiting.");
853d66195c1SJeremy Kerr 		goto out_free;
854d831f960SJeremy Kerr 	}
855d831f960SJeremy Kerr 
85691dde14eSAndrew Jeffery 	if (!config_tty_kname)
85791dde14eSAndrew Jeffery 		config_tty_kname = config_get_value(config, "upstream-tty");
85891dde14eSAndrew Jeffery 
85991dde14eSAndrew Jeffery 	if (!config_tty_kname) {
86091dde14eSAndrew Jeffery 		warnx("No TTY device specified");
86191dde14eSAndrew Jeffery 		usage(argv[0]);
86291dde14eSAndrew Jeffery 		return EXIT_FAILURE;
86391dde14eSAndrew Jeffery 	}
86491dde14eSAndrew Jeffery 
8656221ce94SVishwanatha Subbanna 	console->tty_kname = config_tty_kname;
8666221ce94SVishwanatha Subbanna 
867d66195c1SJeremy Kerr 	rc = tty_init(console, config);
86817217845SJeremy Kerr 	if (rc)
869d66195c1SJeremy Kerr 		goto out_config_fini;
870d831f960SJeremy Kerr 
871f9c8f6caSCheng C Yang 	dbus_init(console, config);
872f9c8f6caSCheng C Yang 
873d47963e5SJeremy Kerr 	handlers_init(console, config);
874d831f960SJeremy Kerr 
8751a0e03b4SJeremy Kerr 	rc = run_console(console);
876d831f960SJeremy Kerr 
8771a0e03b4SJeremy Kerr 	handlers_fini(console);
878d831f960SJeremy Kerr 
879d66195c1SJeremy Kerr out_config_fini:
880d66195c1SJeremy Kerr 	config_fini(config);
881d66195c1SJeremy Kerr 
882957818b4SJeremy Kerr out_free:
88389ea8198SJeremy Kerr 	free(console->pollers);
88489ea8198SJeremy Kerr 	free(console->pollfds);
8851a0e03b4SJeremy Kerr 	free(console->tty_sysfs_devnode);
8861a0e03b4SJeremy Kerr 	free(console->tty_dev);
8871a0e03b4SJeremy Kerr 	free(console);
888d831f960SJeremy Kerr 
889d831f960SJeremy Kerr 	return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
890d831f960SJeremy Kerr }
891