xref: /openbmc/obmc-console/console-server.c (revision 0b7b047715669f1b9f24686c8a26efa47020548b)
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);
1192834c5b1SAndrew Jeffery 	if (rc < 0) {
12017217845SJeremy Kerr 		return -1;
1212834c5b1SAndrew Jeffery 	}
12217217845SJeremy Kerr 
12345ad7676SYi Li 	tty_path_input_real = realpath(tty_path_input, NULL);
12445ad7676SYi Li 	if (!tty_path_input_real) {
12545ad7676SYi Li 		warn("Can't find realpath for /dev/%s", console->tty_kname);
12615792aa7SAndrew Jeffery 		rc = -1;
12745ad7676SYi Li 		goto out_free;
12845ad7676SYi Li 	}
12945ad7676SYi Li 
13045ad7676SYi Li 	tty_kname_real = basename(tty_path_input_real);
13145ad7676SYi Li 	if (!tty_kname_real) {
13245ad7676SYi Li 		warn("Can't find real name for /dev/%s", console->tty_kname);
13315792aa7SAndrew Jeffery 		rc = -1;
13445ad7676SYi Li 		goto out_free;
13545ad7676SYi Li 	}
13645ad7676SYi Li 
137a72711afSAndrew Jeffery 	rc = asprintf(&tty_class_device_link, "/sys/class/tty/%s",
138a72711afSAndrew Jeffery 		      tty_kname_real);
1392834c5b1SAndrew Jeffery 	if (rc < 0) {
14045ad7676SYi Li 		goto out_free;
1412834c5b1SAndrew Jeffery 	}
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);
1512834c5b1SAndrew Jeffery 	if (rc < 0) {
15217217845SJeremy Kerr 		goto out_free;
1532834c5b1SAndrew Jeffery 	}
15417217845SJeremy Kerr 
1551a0e03b4SJeremy Kerr 	console->tty_sysfs_devnode = realpath(tty_device_reldir, NULL);
1562834c5b1SAndrew Jeffery 	if (!console->tty_sysfs_devnode) {
15745ad7676SYi Li 		warn("Can't find parent device for %s", tty_kname_real);
1582834c5b1SAndrew Jeffery 	}
15917217845SJeremy Kerr 
16045ad7676SYi Li 	rc = asprintf(&console->tty_dev, "/dev/%s", tty_kname_real);
1612834c5b1SAndrew Jeffery 	if (rc < 0) {
16217217845SJeremy Kerr 		goto out_free;
1632834c5b1SAndrew Jeffery 	}
16417217845SJeremy Kerr 
16517217845SJeremy Kerr 	rc = 0;
16617217845SJeremy Kerr 
16717217845SJeremy Kerr out_free:
16817217845SJeremy Kerr 	free(tty_class_device_link);
16917217845SJeremy Kerr 	free(tty_device_tty_dir);
17017217845SJeremy Kerr 	free(tty_device_reldir);
17145ad7676SYi Li 	free(tty_path_input);
17245ad7676SYi Li 	free(tty_path_input_real);
17317217845SJeremy Kerr 	return rc;
17417217845SJeremy Kerr }
17517217845SJeremy Kerr 
1761a0e03b4SJeremy Kerr static int tty_set_sysfs_attr(struct console *console, const char *name,
177957818b4SJeremy Kerr 			      int value)
178957818b4SJeremy Kerr {
179957818b4SJeremy Kerr 	char *path;
180957818b4SJeremy Kerr 	FILE *fp;
181957818b4SJeremy Kerr 	int rc;
182957818b4SJeremy Kerr 
1831a0e03b4SJeremy Kerr 	rc = asprintf(&path, "%s/%s", console->tty_sysfs_devnode, name);
1842834c5b1SAndrew Jeffery 	if (rc < 0) {
185957818b4SJeremy Kerr 		return -1;
1862834c5b1SAndrew Jeffery 	}
187957818b4SJeremy Kerr 
188957818b4SJeremy Kerr 	fp = fopen(path, "w");
189957818b4SJeremy Kerr 	if (!fp) {
190a72711afSAndrew Jeffery 		warn("Can't access attribute %s on device %s", name,
191a72711afSAndrew Jeffery 		     console->tty_kname);
192957818b4SJeremy Kerr 		rc = -1;
193957818b4SJeremy Kerr 		goto out_free;
194957818b4SJeremy Kerr 	}
195957818b4SJeremy Kerr 	setvbuf(fp, NULL, _IONBF, 0);
196957818b4SJeremy Kerr 
197957818b4SJeremy Kerr 	rc = fprintf(fp, "0x%x", value);
1982834c5b1SAndrew Jeffery 	if (rc < 0) {
199a72711afSAndrew Jeffery 		warn("Error writing to %s attribute of device %s", name,
200a72711afSAndrew Jeffery 		     console->tty_kname);
2012834c5b1SAndrew Jeffery 	}
202957818b4SJeremy Kerr 	fclose(fp);
203957818b4SJeremy Kerr 
204957818b4SJeremy Kerr out_free:
205957818b4SJeremy Kerr 	free(path);
206957818b4SJeremy Kerr 	return rc;
207957818b4SJeremy Kerr }
208957818b4SJeremy Kerr 
209d831f960SJeremy Kerr /**
210c7fbcd48SBenjamin Fair  * Set termios attributes on the console tty.
21154e9569dSJeremy Kerr  */
21254e9569dSJeremy Kerr static void tty_init_termios(struct console *console)
21354e9569dSJeremy Kerr {
21454e9569dSJeremy Kerr 	struct termios termios;
21554e9569dSJeremy Kerr 	int rc;
21654e9569dSJeremy Kerr 
21754e9569dSJeremy Kerr 	rc = tcgetattr(console->tty_fd, &termios);
21854e9569dSJeremy Kerr 	if (rc) {
21954e9569dSJeremy Kerr 		warn("Can't read tty termios");
22054e9569dSJeremy Kerr 		return;
22154e9569dSJeremy Kerr 	}
22254e9569dSJeremy Kerr 
223c7fbcd48SBenjamin Fair 	if (console->tty_baud) {
2242834c5b1SAndrew Jeffery 		if (cfsetspeed(&termios, console->tty_baud) < 0) {
225c7fbcd48SBenjamin Fair 			warn("Couldn't set speeds for %s", console->tty_kname);
226c7fbcd48SBenjamin Fair 		}
2272834c5b1SAndrew Jeffery 	}
228c7fbcd48SBenjamin Fair 
229c7fbcd48SBenjamin Fair 	/* Set console to raw mode: we don't want any processing to occur on
230c7fbcd48SBenjamin Fair 	 * the underlying terminal input/output.
231c7fbcd48SBenjamin Fair 	 */
23254e9569dSJeremy Kerr 	cfmakeraw(&termios);
233c7fbcd48SBenjamin Fair 
23454e9569dSJeremy Kerr 	rc = tcsetattr(console->tty_fd, TCSANOW, &termios);
2352834c5b1SAndrew Jeffery 	if (rc) {
236c7fbcd48SBenjamin Fair 		warn("Can't set terminal options for %s", console->tty_kname);
23754e9569dSJeremy Kerr 	}
2382834c5b1SAndrew Jeffery }
23954e9569dSJeremy Kerr 
240f9c8f6caSCheng C Yang static void tty_change_baudrate(struct console *console)
241f9c8f6caSCheng C Yang {
242f9c8f6caSCheng C Yang 	struct handler *handler;
243f9c8f6caSCheng C Yang 	int i, rc;
244f9c8f6caSCheng C Yang 
245f9c8f6caSCheng C Yang 	tty_init_termios(console);
246f9c8f6caSCheng C Yang 
247f9c8f6caSCheng C Yang 	for (i = 0; i < console->n_handlers; i++) {
248f9c8f6caSCheng C Yang 		handler = console->handlers[i];
2492834c5b1SAndrew Jeffery 		if (!handler->baudrate) {
250f9c8f6caSCheng C Yang 			continue;
2512834c5b1SAndrew Jeffery 		}
252f9c8f6caSCheng C Yang 
253f9c8f6caSCheng C Yang 		rc = handler->baudrate(handler, console->tty_baud);
2542834c5b1SAndrew Jeffery 		if (rc) {
255f9c8f6caSCheng C Yang 			warnx("Can't set terminal baudrate for handler %s",
256f9c8f6caSCheng C Yang 			      handler->name);
257f9c8f6caSCheng C Yang 		}
258f9c8f6caSCheng C Yang 	}
2592834c5b1SAndrew Jeffery }
260f9c8f6caSCheng C Yang 
26154e9569dSJeremy Kerr /**
262d831f960SJeremy Kerr  * Open and initialise the serial device
263d831f960SJeremy Kerr  */
2641a0e03b4SJeremy Kerr static int tty_init_io(struct console *console)
265d831f960SJeremy Kerr {
2662834c5b1SAndrew Jeffery 	if (console->tty_sirq) {
2671a0e03b4SJeremy Kerr 		tty_set_sysfs_attr(console, "sirq", console->tty_sirq);
2682834c5b1SAndrew Jeffery 	}
2692834c5b1SAndrew Jeffery 	if (console->tty_lpc_addr) {
2701a0e03b4SJeremy Kerr 		tty_set_sysfs_attr(console, "lpc_address",
2711a0e03b4SJeremy Kerr 				   console->tty_lpc_addr);
2722834c5b1SAndrew Jeffery 	}
273957818b4SJeremy Kerr 
2741a0e03b4SJeremy Kerr 	console->tty_fd = open(console->tty_dev, O_RDWR);
2751a0e03b4SJeremy Kerr 	if (console->tty_fd <= 0) {
2761a0e03b4SJeremy Kerr 		warn("Can't open tty %s", console->tty_dev);
277d831f960SJeremy Kerr 		return -1;
278d831f960SJeremy Kerr 	}
279d831f960SJeremy Kerr 
280d831f960SJeremy Kerr 	/* Disable character delay. We may want to later enable this when
281d831f960SJeremy Kerr 	 * we detect larger amounts of data
282d831f960SJeremy Kerr 	 */
2831a0e03b4SJeremy Kerr 	fcntl(console->tty_fd, F_SETFL, FNDELAY);
284d831f960SJeremy Kerr 
28554e9569dSJeremy Kerr 	tty_init_termios(console);
28654e9569dSJeremy Kerr 
287329a35f5SJeremy Kerr 	console->pollfds[console->n_pollers].fd = console->tty_fd;
288329a35f5SJeremy Kerr 	console->pollfds[console->n_pollers].events = POLLIN;
289329a35f5SJeremy Kerr 
290d831f960SJeremy Kerr 	return 0;
291d831f960SJeremy Kerr }
292d831f960SJeremy Kerr 
293d66195c1SJeremy Kerr static int tty_init(struct console *console, struct config *config)
294d66195c1SJeremy Kerr {
295fd883a88SAndrew Jeffery 	unsigned long parsed;
296d66195c1SJeremy Kerr 	const char *val;
297d66195c1SJeremy Kerr 	char *endp;
298d66195c1SJeremy Kerr 	int rc;
299d66195c1SJeremy Kerr 
300d66195c1SJeremy Kerr 	val = config_get_value(config, "lpc-address");
301d66195c1SJeremy Kerr 	if (val) {
302fd883a88SAndrew Jeffery 		errno = 0;
303fd883a88SAndrew Jeffery 		parsed = strtoul(val, &endp, 0);
304fd883a88SAndrew Jeffery 		if (parsed == ULONG_MAX && errno == ERANGE) {
305fd883a88SAndrew Jeffery 			warn("Cannot interpret 'lpc-address' value as an unsigned long: '%s'",
306fd883a88SAndrew Jeffery 			     val);
307fd883a88SAndrew Jeffery 			return -1;
308fd883a88SAndrew Jeffery 		}
309fd883a88SAndrew Jeffery 
310fd883a88SAndrew Jeffery 		if (parsed > UINT16_MAX) {
311fd883a88SAndrew Jeffery 			warn("Invalid LPC address '%s'", val);
312fd883a88SAndrew Jeffery 			return -1;
313fd883a88SAndrew Jeffery 		}
314fd883a88SAndrew Jeffery 
315fd883a88SAndrew Jeffery 		console->tty_lpc_addr = (uint16_t)parsed;
316d66195c1SJeremy Kerr 		if (endp == optarg) {
317d66195c1SJeremy Kerr 			warn("Invalid LPC address: '%s'", val);
318d66195c1SJeremy Kerr 			return -1;
319d66195c1SJeremy Kerr 		}
320d66195c1SJeremy Kerr 	}
321d66195c1SJeremy Kerr 
322d66195c1SJeremy Kerr 	val = config_get_value(config, "sirq");
323d66195c1SJeremy Kerr 	if (val) {
324fd883a88SAndrew Jeffery 		errno = 0;
325fd883a88SAndrew Jeffery 		parsed = strtoul(val, &endp, 0);
326fd883a88SAndrew Jeffery 		if (parsed == ULONG_MAX && errno == ERANGE) {
327fd883a88SAndrew Jeffery 			warn("Cannot interpret 'sirq' value as an unsigned long: '%s'",
328fd883a88SAndrew Jeffery 			     val);
329fd883a88SAndrew Jeffery 		}
330fd883a88SAndrew Jeffery 
3312834c5b1SAndrew Jeffery 		if (parsed > 16) {
332fd883a88SAndrew Jeffery 			warn("Invalid LPC SERIRQ: '%s'", val);
3332834c5b1SAndrew Jeffery 		}
334fd883a88SAndrew Jeffery 
335fd883a88SAndrew Jeffery 		console->tty_sirq = (int)parsed;
3362834c5b1SAndrew Jeffery 		if (endp == optarg) {
337d66195c1SJeremy Kerr 			warn("Invalid sirq: '%s'", val);
338d66195c1SJeremy Kerr 		}
3392834c5b1SAndrew Jeffery 	}
340d66195c1SJeremy Kerr 
341c7fbcd48SBenjamin Fair 	val = config_get_value(config, "baud");
342c7fbcd48SBenjamin Fair 	if (val) {
3432834c5b1SAndrew Jeffery 		if (config_parse_baud(&console->tty_baud, val)) {
344c7fbcd48SBenjamin Fair 			warnx("Invalid baud rate: '%s'", val);
345c7fbcd48SBenjamin Fair 		}
3462834c5b1SAndrew Jeffery 	}
347c7fbcd48SBenjamin Fair 
348d66195c1SJeremy Kerr 	if (!console->tty_kname) {
349d66195c1SJeremy Kerr 		warnx("Error: No TTY device specified");
350d66195c1SJeremy Kerr 		return -1;
351d66195c1SJeremy Kerr 	}
352d66195c1SJeremy Kerr 
353d66195c1SJeremy Kerr 	rc = tty_find_device(console);
3542834c5b1SAndrew Jeffery 	if (rc) {
355d66195c1SJeremy Kerr 		return rc;
3562834c5b1SAndrew Jeffery 	}
357d66195c1SJeremy Kerr 
358d66195c1SJeremy Kerr 	rc = tty_init_io(console);
359d66195c1SJeremy Kerr 	return rc;
360d66195c1SJeremy Kerr }
361d66195c1SJeremy Kerr 
3621a0e03b4SJeremy Kerr int console_data_out(struct console *console, const uint8_t *data, size_t len)
363d831f960SJeremy Kerr {
3641a0e03b4SJeremy Kerr 	return write_buf_to_fd(console->tty_fd, data, len);
365d831f960SJeremy Kerr }
366d831f960SJeremy Kerr 
367f9c8f6caSCheng C Yang static int method_set_baud_rate(sd_bus_message *msg, void *userdata,
368f9c8f6caSCheng C Yang 				sd_bus_error *err)
369f9c8f6caSCheng C Yang {
370f9c8f6caSCheng C Yang 	struct console *console = userdata;
371f9c8f6caSCheng C Yang 	uint32_t baudrate;
372f9c8f6caSCheng C Yang 	speed_t speed;
373f9c8f6caSCheng C Yang 	int r;
374f9c8f6caSCheng C Yang 
375f9c8f6caSCheng C Yang 	if (!console) {
376f9c8f6caSCheng C Yang 		sd_bus_error_set_const(err, DBUS_ERR, "Internal error");
377f9c8f6caSCheng C Yang 		return sd_bus_reply_method_return(msg, "x", 0);
378f9c8f6caSCheng C Yang 	}
379f9c8f6caSCheng C Yang 
380f9c8f6caSCheng C Yang 	r = sd_bus_message_read(msg, "u", &baudrate);
381f9c8f6caSCheng C Yang 	if (r < 0) {
382f9c8f6caSCheng C Yang 		sd_bus_error_set_const(err, DBUS_ERR, "Bad message");
383f9c8f6caSCheng C Yang 		return sd_bus_reply_method_return(msg, "x", -EINVAL);
384f9c8f6caSCheng C Yang 	}
385f9c8f6caSCheng C Yang 
386f9c8f6caSCheng C Yang 	speed = parse_int_to_baud(baudrate);
387f9c8f6caSCheng C Yang 	if (!speed) {
388f9c8f6caSCheng C Yang 		warnx("Invalid baud rate: '%u'", baudrate);
389f9c8f6caSCheng C Yang 		return sd_bus_reply_method_return(msg, "x", -EINVAL);
390f9c8f6caSCheng C Yang 	}
391f9c8f6caSCheng C Yang 
392f9c8f6caSCheng C Yang 	console->tty_baud = speed;
393f9c8f6caSCheng C Yang 	tty_change_baudrate(console);
394f9c8f6caSCheng C Yang 
395f9c8f6caSCheng C Yang 	return sd_bus_reply_method_return(msg, "x", r);
396f9c8f6caSCheng C Yang }
397f9c8f6caSCheng C Yang 
398fd048328SAndrew Jeffery static int get_handler(sd_bus *bus __attribute__((unused)),
399fd048328SAndrew Jeffery 		       const char *path __attribute__((unused)),
400fd048328SAndrew Jeffery 		       const char *interface __attribute__((unused)),
401fd048328SAndrew Jeffery 		       const char *property __attribute__((unused)),
402fd048328SAndrew Jeffery 		       sd_bus_message *reply, void *userdata,
403a72711afSAndrew Jeffery 		       sd_bus_error *error __attribute__((unused)))
404a72711afSAndrew Jeffery {
405f9c8f6caSCheng C Yang 	struct console *console = userdata;
406f9c8f6caSCheng C Yang 	uint32_t baudrate;
407f9c8f6caSCheng C Yang 	int r;
408f9c8f6caSCheng C Yang 
409f9c8f6caSCheng C Yang 	baudrate = parse_baud_to_int(console->tty_baud);
4102834c5b1SAndrew Jeffery 	if (!baudrate) {
411f9c8f6caSCheng C Yang 		warnx("Invalid baud rate: '%d'", console->tty_baud);
4122834c5b1SAndrew Jeffery 	}
413f9c8f6caSCheng C Yang 
414f9c8f6caSCheng C Yang 	r = sd_bus_message_append(reply, "u", baudrate);
415f9c8f6caSCheng C Yang 
416f9c8f6caSCheng C Yang 	return r;
417f9c8f6caSCheng C Yang }
418f9c8f6caSCheng C Yang 
419f9c8f6caSCheng C Yang static const sd_bus_vtable console_vtable[] = {
420f9c8f6caSCheng C Yang 	SD_BUS_VTABLE_START(0),
421f9c8f6caSCheng C Yang 	SD_BUS_METHOD("setBaudRate", "u", "x", method_set_baud_rate,
422f9c8f6caSCheng C Yang 		      SD_BUS_VTABLE_UNPRIVILEGED),
423f9c8f6caSCheng C Yang 	SD_BUS_PROPERTY("baudrate", "u", get_handler, 0, 0),
424a72711afSAndrew Jeffery 	SD_BUS_VTABLE_END,
425a72711afSAndrew Jeffery };
426f9c8f6caSCheng C Yang 
427a72711afSAndrew Jeffery static void dbus_init(struct console *console,
428a72711afSAndrew Jeffery 		      struct config *config __attribute__((unused)))
429f9c8f6caSCheng C Yang {
430f9c8f6caSCheng C Yang 	int dbus_poller = 0;
431f9c8f6caSCheng C Yang 	int fd, r;
432f9c8f6caSCheng C Yang 
433f9c8f6caSCheng C Yang 	if (!console) {
434f9c8f6caSCheng C Yang 		warnx("Couldn't get valid console");
435f9c8f6caSCheng C Yang 		return;
436f9c8f6caSCheng C Yang 	}
437f9c8f6caSCheng C Yang 
438f9c8f6caSCheng C Yang 	r = sd_bus_default_system(&console->bus);
439f9c8f6caSCheng C Yang 	if (r < 0) {
440f9c8f6caSCheng C Yang 		warnx("Failed to connect to system bus: %s", strerror(-r));
441f9c8f6caSCheng C Yang 		return;
442f9c8f6caSCheng C Yang 	}
443f9c8f6caSCheng C Yang 
444f9c8f6caSCheng C Yang 	r = sd_bus_add_object_vtable(console->bus, NULL, OBJ_NAME, DBUS_NAME,
445f9c8f6caSCheng C Yang 				     console_vtable, console);
446f9c8f6caSCheng C Yang 	if (r < 0) {
447f9c8f6caSCheng C Yang 		warnx("Failed to issue method call: %s", strerror(-r));
448f9c8f6caSCheng C Yang 		return;
449f9c8f6caSCheng C Yang 	}
450f9c8f6caSCheng C Yang 
451a72711afSAndrew Jeffery 	r = sd_bus_request_name(console->bus, DBUS_NAME,
452a72711afSAndrew Jeffery 				SD_BUS_NAME_ALLOW_REPLACEMENT |
453a72711afSAndrew Jeffery 					SD_BUS_NAME_REPLACE_EXISTING);
454f9c8f6caSCheng C Yang 	if (r < 0) {
455f9c8f6caSCheng C Yang 		warnx("Failed to acquire service name: %s", strerror(-r));
456f9c8f6caSCheng C Yang 		return;
457f9c8f6caSCheng C Yang 	}
458f9c8f6caSCheng C Yang 
459f9c8f6caSCheng C Yang 	fd = sd_bus_get_fd(console->bus);
460f9c8f6caSCheng C Yang 	if (fd < 0) {
461f9c8f6caSCheng C Yang 		warnx("Couldn't get the bus file descriptor");
462f9c8f6caSCheng C Yang 		return;
463f9c8f6caSCheng C Yang 	}
464f9c8f6caSCheng C Yang 
465f9c8f6caSCheng C Yang 	dbus_poller = POLLFD_DBUS;
466f9c8f6caSCheng C Yang 
467f9c8f6caSCheng C Yang 	console->pollfds[dbus_poller].fd = fd;
468f9c8f6caSCheng C Yang 	console->pollfds[dbus_poller].events = POLLIN;
469f9c8f6caSCheng C Yang }
470f9c8f6caSCheng C Yang 
471d47963e5SJeremy Kerr static void handlers_init(struct console *console, struct config *config)
472d831f960SJeremy Kerr {
473750fb0c0SAndrew Jeffery 	/* NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) */
4741a0e03b4SJeremy Kerr 	extern struct handler *__start_handlers, *__stop_handlers;
4751a0e03b4SJeremy Kerr 	struct handler *handler;
476021b91f0SJeremy Kerr 	int i, rc;
477d831f960SJeremy Kerr 
4781a0e03b4SJeremy Kerr 	console->n_handlers = &__stop_handlers - &__start_handlers;
4791a0e03b4SJeremy Kerr 	console->handlers = &__start_handlers;
480d831f960SJeremy Kerr 
4815c359cc6SAndrew Jeffery 	printf("%ld handler%s\n", console->n_handlers,
4821a0e03b4SJeremy Kerr 	       console->n_handlers == 1 ? "" : "s");
483d831f960SJeremy Kerr 
4841a0e03b4SJeremy Kerr 	for (i = 0; i < console->n_handlers; i++) {
4851a0e03b4SJeremy Kerr 		handler = console->handlers[i];
4861a0e03b4SJeremy Kerr 
487021b91f0SJeremy Kerr 		rc = 0;
4882834c5b1SAndrew Jeffery 		if (handler->init) {
489021b91f0SJeremy Kerr 			rc = handler->init(handler, console, config);
4902834c5b1SAndrew Jeffery 		}
491021b91f0SJeremy Kerr 
492021b91f0SJeremy Kerr 		handler->active = rc == 0;
493021b91f0SJeremy Kerr 
494021b91f0SJeremy Kerr 		printf("  %s [%sactive]\n", handler->name,
495021b91f0SJeremy Kerr 		       handler->active ? "" : "in");
496d831f960SJeremy Kerr 	}
497d831f960SJeremy Kerr }
498d831f960SJeremy Kerr 
4991a0e03b4SJeremy Kerr static void handlers_fini(struct console *console)
500d831f960SJeremy Kerr {
5011a0e03b4SJeremy Kerr 	struct handler *handler;
5021a0e03b4SJeremy Kerr 	int i;
5031a0e03b4SJeremy Kerr 
5041a0e03b4SJeremy Kerr 	for (i = 0; i < console->n_handlers; i++) {
5051a0e03b4SJeremy Kerr 		handler = console->handlers[i];
5062834c5b1SAndrew Jeffery 		if (handler->fini && handler->active) {
5071a0e03b4SJeremy Kerr 			handler->fini(handler);
5081a0e03b4SJeremy Kerr 		}
509d831f960SJeremy Kerr 	}
5102834c5b1SAndrew Jeffery }
511d831f960SJeremy Kerr 
5121cecc5deSJohnathan Mantey static int get_current_time(struct timeval *tv)
5131cecc5deSJohnathan Mantey {
5141cecc5deSJohnathan Mantey 	struct timespec t;
5151cecc5deSJohnathan Mantey 	int rc;
5161cecc5deSJohnathan Mantey 
5171cecc5deSJohnathan Mantey 	/*
5181cecc5deSJohnathan Mantey 	 * We use clock_gettime(CLOCK_MONOTONIC) so we're immune to
5191cecc5deSJohnathan Mantey 	 * local time changes. However, a struct timeval is more
5201cecc5deSJohnathan Mantey 	 * convenient for calculations, so convert to that.
5211cecc5deSJohnathan Mantey 	 */
5221cecc5deSJohnathan Mantey 	rc = clock_gettime(CLOCK_MONOTONIC, &t);
5232834c5b1SAndrew Jeffery 	if (rc) {
5241cecc5deSJohnathan Mantey 		return rc;
5252834c5b1SAndrew Jeffery 	}
5261cecc5deSJohnathan Mantey 
5271cecc5deSJohnathan Mantey 	tv->tv_sec = t.tv_sec;
5281cecc5deSJohnathan Mantey 	tv->tv_usec = t.tv_nsec / 1000;
5291cecc5deSJohnathan Mantey 
5301cecc5deSJohnathan Mantey 	return 0;
5311cecc5deSJohnathan Mantey }
5321cecc5deSJohnathan Mantey 
533a72711afSAndrew Jeffery struct ringbuffer_consumer *
534a72711afSAndrew Jeffery console_ringbuffer_consumer_register(struct console *console,
535f733c85aSJeremy Kerr 				     ringbuffer_poll_fn_t poll_fn, void *data)
536d831f960SJeremy Kerr {
537f733c85aSJeremy Kerr 	return ringbuffer_consumer_register(console->rb, poll_fn, data);
538d831f960SJeremy Kerr }
539d831f960SJeremy Kerr 
54055c9712dSJeremy Kerr struct poller *console_poller_register(struct console *console,
541a72711afSAndrew Jeffery 				       struct handler *handler,
542a72711afSAndrew Jeffery 				       poller_event_fn_t poller_fn,
5431cecc5deSJohnathan Mantey 				       poller_timeout_fn_t timeout_fn, int fd,
5441cecc5deSJohnathan Mantey 				       int events, void *data)
545d831f960SJeremy Kerr {
546329a35f5SJeremy Kerr 	struct poller *poller;
5475c359cc6SAndrew Jeffery 	long n;
548329a35f5SJeremy Kerr 
549329a35f5SJeremy Kerr 	poller = malloc(sizeof(*poller));
550329a35f5SJeremy Kerr 	poller->remove = false;
551329a35f5SJeremy Kerr 	poller->handler = handler;
5521cecc5deSJohnathan Mantey 	poller->event_fn = poller_fn;
5531cecc5deSJohnathan Mantey 	poller->timeout_fn = timeout_fn;
554329a35f5SJeremy Kerr 	poller->data = data;
555329a35f5SJeremy Kerr 
556329a35f5SJeremy Kerr 	/* add one to our pollers array */
557329a35f5SJeremy Kerr 	n = console->n_pollers++;
55891b52175SAndrew Jeffery 	/*
55991b52175SAndrew Jeffery 	 * We're managing an array of pointers to aggregates, so don't warn about sizeof() on a
56091b52175SAndrew Jeffery 	 * pointer type.
56191b52175SAndrew Jeffery 	 */
56291b52175SAndrew Jeffery 	/* NOLINTBEGIN(bugprone-sizeof-expression) */
56391b52175SAndrew Jeffery 	console->pollers = reallocarray(console->pollers, console->n_pollers,
56491b52175SAndrew Jeffery 					sizeof(*console->pollers));
56591b52175SAndrew Jeffery 	/* NOLINTEND(bugprone-sizeof-expression) */
566329a35f5SJeremy Kerr 
567329a35f5SJeremy Kerr 	console->pollers[n] = poller;
568329a35f5SJeremy Kerr 
569329a35f5SJeremy Kerr 	/* increase pollfds array too  */
570a72711afSAndrew Jeffery 	console->pollfds =
57191b52175SAndrew Jeffery 		reallocarray(console->pollfds,
57291b52175SAndrew Jeffery 			     (MAX_INTERNAL_POLLFD + console->n_pollers),
57391b52175SAndrew Jeffery 			     sizeof(*console->pollfds));
574329a35f5SJeremy Kerr 
575329a35f5SJeremy Kerr 	/* shift the end pollfds up by one */
576a72711afSAndrew Jeffery 	memcpy(&console->pollfds[n + 1], &console->pollfds[n],
577f9c8f6caSCheng C Yang 	       sizeof(*console->pollfds) * MAX_INTERNAL_POLLFD);
578329a35f5SJeremy Kerr 
579329a35f5SJeremy Kerr 	console->pollfds[n].fd = fd;
5805c359cc6SAndrew Jeffery 	console->pollfds[n].events = (short)(events & 0x7fff);
581329a35f5SJeremy Kerr 
582329a35f5SJeremy Kerr 	return poller;
583329a35f5SJeremy Kerr }
584329a35f5SJeremy Kerr 
585a72711afSAndrew Jeffery void console_poller_unregister(struct console *console, struct poller *poller)
586329a35f5SJeremy Kerr {
587329a35f5SJeremy Kerr 	int i;
588329a35f5SJeremy Kerr 
589329a35f5SJeremy Kerr 	/* find the entry in our pollers array */
5902834c5b1SAndrew Jeffery 	for (i = 0; i < console->n_pollers; i++) {
5912834c5b1SAndrew Jeffery 		if (console->pollers[i] == poller) {
592329a35f5SJeremy Kerr 			break;
5932834c5b1SAndrew Jeffery 		}
5942834c5b1SAndrew Jeffery 	}
595329a35f5SJeremy Kerr 
596329a35f5SJeremy Kerr 	assert(i < console->n_pollers);
597329a35f5SJeremy Kerr 
598329a35f5SJeremy Kerr 	console->n_pollers--;
599329a35f5SJeremy Kerr 
60091b52175SAndrew Jeffery 	/*
60191b52175SAndrew Jeffery 	 * Remove the item from the pollers array...
60291b52175SAndrew Jeffery 	 *
60391b52175SAndrew Jeffery 	 * We're managing an array of pointers to aggregates, so don't warn about sizeof() on a
60491b52175SAndrew Jeffery 	 * pointer type.
60591b52175SAndrew Jeffery 	 */
60691b52175SAndrew Jeffery 	/* NOLINTBEGIN(bugprone-sizeof-expression) */
607329a35f5SJeremy Kerr 	memmove(&console->pollers[i], &console->pollers[i + 1],
608a72711afSAndrew Jeffery 		sizeof(*console->pollers) * (console->n_pollers - i));
609329a35f5SJeremy Kerr 
61091b52175SAndrew Jeffery 	console->pollers = reallocarray(console->pollers, console->n_pollers,
61191b52175SAndrew Jeffery 					sizeof(*console->pollers));
61291b52175SAndrew Jeffery 	/* NOLINTEND(bugprone-sizeof-expression) */
613329a35f5SJeremy Kerr 
614329a35f5SJeremy Kerr 	/* ... and the pollfds array */
615329a35f5SJeremy Kerr 	memmove(&console->pollfds[i], &console->pollfds[i + 1],
616329a35f5SJeremy Kerr 		sizeof(*console->pollfds) *
617f9c8f6caSCheng C Yang 			(MAX_INTERNAL_POLLFD + console->n_pollers - i));
618329a35f5SJeremy Kerr 
619a72711afSAndrew Jeffery 	console->pollfds =
62091b52175SAndrew Jeffery 		reallocarray(console->pollfds,
62191b52175SAndrew Jeffery 			     (MAX_INTERNAL_POLLFD + console->n_pollers),
62291b52175SAndrew Jeffery 			     sizeof(*console->pollfds));
623329a35f5SJeremy Kerr 
624329a35f5SJeremy Kerr 	free(poller);
625329a35f5SJeremy Kerr }
626329a35f5SJeremy Kerr 
6276b1fed27SJeremy Kerr void console_poller_set_events(struct console *console, struct poller *poller,
6286b1fed27SJeremy Kerr 			       int events)
6296b1fed27SJeremy Kerr {
6306b1fed27SJeremy Kerr 	int i;
6316b1fed27SJeremy Kerr 
6326b1fed27SJeremy Kerr 	/* find the entry in our pollers array */
6332834c5b1SAndrew Jeffery 	for (i = 0; i < console->n_pollers; i++) {
6342834c5b1SAndrew Jeffery 		if (console->pollers[i] == poller) {
6356b1fed27SJeremy Kerr 			break;
6362834c5b1SAndrew Jeffery 		}
6372834c5b1SAndrew Jeffery 	}
6386b1fed27SJeremy Kerr 
6395c359cc6SAndrew Jeffery 	console->pollfds[i].events = (short)(events & 0x7fff);
6406b1fed27SJeremy Kerr }
6416b1fed27SJeremy Kerr 
642fd048328SAndrew Jeffery void console_poller_set_timeout(struct console *console __attribute__((unused)),
643fd048328SAndrew Jeffery 				struct poller *poller, const struct timeval *tv)
6441cecc5deSJohnathan Mantey {
6451cecc5deSJohnathan Mantey 	struct timeval now;
6461cecc5deSJohnathan Mantey 	int rc;
6471cecc5deSJohnathan Mantey 
6481cecc5deSJohnathan Mantey 	rc = get_current_time(&now);
6492834c5b1SAndrew Jeffery 	if (rc) {
6501cecc5deSJohnathan Mantey 		return;
6512834c5b1SAndrew Jeffery 	}
6521cecc5deSJohnathan Mantey 
6531cecc5deSJohnathan Mantey 	timeradd(&now, tv, &poller->timeout);
6541cecc5deSJohnathan Mantey }
6551cecc5deSJohnathan Mantey 
6565c359cc6SAndrew Jeffery static long get_poll_timeout(struct console *console, struct timeval *cur_time)
6571cecc5deSJohnathan Mantey {
6581cecc5deSJohnathan Mantey 	struct timeval *earliest, interval;
6591cecc5deSJohnathan Mantey 	struct poller *poller;
6601cecc5deSJohnathan Mantey 	int i;
6611cecc5deSJohnathan Mantey 
6621cecc5deSJohnathan Mantey 	earliest = NULL;
6631cecc5deSJohnathan Mantey 
6641cecc5deSJohnathan Mantey 	for (i = 0; i < console->n_pollers; i++) {
6651cecc5deSJohnathan Mantey 		poller = console->pollers[i];
6661cecc5deSJohnathan Mantey 
6671cecc5deSJohnathan Mantey 		if (poller->timeout_fn && timerisset(&poller->timeout) &&
6681cecc5deSJohnathan Mantey 		    (!earliest ||
6691cecc5deSJohnathan Mantey 		     (earliest && timercmp(&poller->timeout, earliest, <)))) {
6701cecc5deSJohnathan Mantey 			// poller is buffering data and needs the poll
6711cecc5deSJohnathan Mantey 			// function to timeout.
6721cecc5deSJohnathan Mantey 			earliest = &poller->timeout;
6731cecc5deSJohnathan Mantey 		}
6741cecc5deSJohnathan Mantey 	}
6751cecc5deSJohnathan Mantey 
6761cecc5deSJohnathan Mantey 	if (earliest) {
6771cecc5deSJohnathan Mantey 		if (timercmp(earliest, cur_time, >)) {
6781cecc5deSJohnathan Mantey 			/* recalculate the timeout period, time period has
6791cecc5deSJohnathan Mantey 			 * not elapsed */
6801cecc5deSJohnathan Mantey 			timersub(earliest, cur_time, &interval);
6811cecc5deSJohnathan Mantey 			return ((interval.tv_sec * 1000) +
6821cecc5deSJohnathan Mantey 				(interval.tv_usec / 1000));
683*0b7b0477SAndrew Jeffery 		} /* return from poll immediately */
6841cecc5deSJohnathan Mantey 		return 0;
685*0b7b0477SAndrew Jeffery 
686*0b7b0477SAndrew Jeffery 	} /* poll indefinitely */
6871cecc5deSJohnathan Mantey 	return -1;
6881cecc5deSJohnathan Mantey }
6891cecc5deSJohnathan Mantey 
6901cecc5deSJohnathan Mantey static int call_pollers(struct console *console, struct timeval *cur_time)
691329a35f5SJeremy Kerr {
692329a35f5SJeremy Kerr 	struct poller *poller;
693329a35f5SJeremy Kerr 	struct pollfd *pollfd;
694329a35f5SJeremy Kerr 	enum poller_ret prc;
695329a35f5SJeremy Kerr 	int i, rc;
696d831f960SJeremy Kerr 
6971a0e03b4SJeremy Kerr 	rc = 0;
6981a0e03b4SJeremy Kerr 
699329a35f5SJeremy Kerr 	/*
700329a35f5SJeremy Kerr 	 * Process poll events by iterating through the pollers and pollfds
701329a35f5SJeremy Kerr 	 * in-step, calling any pollers that we've found revents for.
702329a35f5SJeremy Kerr 	 */
703329a35f5SJeremy Kerr 	for (i = 0; i < console->n_pollers; i++) {
704329a35f5SJeremy Kerr 		poller = console->pollers[i];
705329a35f5SJeremy Kerr 		pollfd = &console->pollfds[i];
7061cecc5deSJohnathan Mantey 		prc = POLLER_OK;
7071a0e03b4SJeremy Kerr 
7081cecc5deSJohnathan Mantey 		/* process pending events... */
7091cecc5deSJohnathan Mantey 		if (pollfd->revents) {
7101cecc5deSJohnathan Mantey 			prc = poller->event_fn(poller->handler, pollfd->revents,
711329a35f5SJeremy Kerr 					       poller->data);
7122834c5b1SAndrew Jeffery 			if (prc == POLLER_EXIT) {
713329a35f5SJeremy Kerr 				rc = -1;
7142834c5b1SAndrew Jeffery 			} else if (prc == POLLER_REMOVE) {
715329a35f5SJeremy Kerr 				poller->remove = true;
716329a35f5SJeremy Kerr 			}
7172834c5b1SAndrew Jeffery 		}
718329a35f5SJeremy Kerr 
7191cecc5deSJohnathan Mantey 		if ((prc == POLLER_OK) && poller->timeout_fn &&
7201cecc5deSJohnathan Mantey 		    timerisset(&poller->timeout) &&
7211cecc5deSJohnathan Mantey 		    timercmp(&poller->timeout, cur_time, <=)) {
7221cecc5deSJohnathan Mantey 			/* One of the ringbuffer consumers is buffering the
7231cecc5deSJohnathan Mantey 			data stream. The amount of idle time the consumer
7241cecc5deSJohnathan Mantey 			desired has expired.  Process the buffered data for
7251cecc5deSJohnathan Mantey 			transmission. */
7261cecc5deSJohnathan Mantey 			timerclear(&poller->timeout);
7271cecc5deSJohnathan Mantey 			prc = poller->timeout_fn(poller->handler, poller->data);
7281cecc5deSJohnathan Mantey 			if (prc == POLLER_EXIT) {
7291cecc5deSJohnathan Mantey 				rc = -1;
7301cecc5deSJohnathan Mantey 			} else if (prc == POLLER_REMOVE) {
7311cecc5deSJohnathan Mantey 				poller->remove = true;
7321cecc5deSJohnathan Mantey 			}
7331cecc5deSJohnathan Mantey 		}
7341cecc5deSJohnathan Mantey 	}
7351cecc5deSJohnathan Mantey 
736329a35f5SJeremy Kerr 	/**
737329a35f5SJeremy Kerr 	 * Process deferred removals; restarting each time we unregister, as
738329a35f5SJeremy Kerr 	 * the array will have changed
739329a35f5SJeremy Kerr 	 */
740329a35f5SJeremy Kerr 	for (;;) {
741329a35f5SJeremy Kerr 		bool removed = false;
742329a35f5SJeremy Kerr 
743329a35f5SJeremy Kerr 		for (i = 0; i < console->n_pollers; i++) {
744329a35f5SJeremy Kerr 			poller = console->pollers[i];
745329a35f5SJeremy Kerr 			if (poller->remove) {
74655c9712dSJeremy Kerr 				console_poller_unregister(console, poller);
747329a35f5SJeremy Kerr 				removed = true;
748329a35f5SJeremy Kerr 				break;
749329a35f5SJeremy Kerr 			}
750329a35f5SJeremy Kerr 		}
7512834c5b1SAndrew Jeffery 		if (!removed) {
752329a35f5SJeremy Kerr 			break;
7531a0e03b4SJeremy Kerr 		}
7542834c5b1SAndrew Jeffery 	}
7551a0e03b4SJeremy Kerr 
7561a0e03b4SJeremy Kerr 	return rc;
7571a0e03b4SJeremy Kerr }
7581a0e03b4SJeremy Kerr 
759769cee1aSJeremy Kerr static void sighandler(int signal)
760769cee1aSJeremy Kerr {
7612834c5b1SAndrew Jeffery 	if (signal == SIGINT) {
762769cee1aSJeremy Kerr 		sigint = true;
763769cee1aSJeremy Kerr 	}
7642834c5b1SAndrew Jeffery }
765769cee1aSJeremy Kerr 
7661a0e03b4SJeremy Kerr int run_console(struct console *console)
7671a0e03b4SJeremy Kerr {
7685c359cc6SAndrew Jeffery 	sighandler_t sighandler_save = signal(SIGINT, sighandler);
7691cecc5deSJohnathan Mantey 	struct timeval tv;
7705c359cc6SAndrew Jeffery 	long timeout;
7715c359cc6SAndrew Jeffery 	ssize_t rc;
772769cee1aSJeremy Kerr 
773769cee1aSJeremy Kerr 	rc = 0;
774769cee1aSJeremy Kerr 
775d831f960SJeremy Kerr 	for (;;) {
776d831f960SJeremy Kerr 		uint8_t buf[4096];
777d831f960SJeremy Kerr 
7781764145dSJeremy Kerr 		BUILD_ASSERT(sizeof(buf) <= buffer_size);
7791764145dSJeremy Kerr 
780769cee1aSJeremy Kerr 		if (sigint) {
781769cee1aSJeremy Kerr 			fprintf(stderr, "Received interrupt, exiting\n");
782769cee1aSJeremy Kerr 			break;
783769cee1aSJeremy Kerr 		}
784769cee1aSJeremy Kerr 
7851cecc5deSJohnathan Mantey 		rc = get_current_time(&tv);
7861cecc5deSJohnathan Mantey 		if (rc) {
7871cecc5deSJohnathan Mantey 			warn("Failed to read current time");
7881cecc5deSJohnathan Mantey 			break;
7891cecc5deSJohnathan Mantey 		}
7901cecc5deSJohnathan Mantey 
7911cecc5deSJohnathan Mantey 		timeout = get_poll_timeout(console, &tv);
7921cecc5deSJohnathan Mantey 
793329a35f5SJeremy Kerr 		rc = poll(console->pollfds,
7945c359cc6SAndrew Jeffery 			  console->n_pollers + MAX_INTERNAL_POLLFD,
7955c359cc6SAndrew Jeffery 			  (int)timeout);
7961cecc5deSJohnathan Mantey 
797d831f960SJeremy Kerr 		if (rc < 0) {
798769cee1aSJeremy Kerr 			if (errno == EINTR) {
799769cee1aSJeremy Kerr 				continue;
800*0b7b0477SAndrew Jeffery 			}
801d831f960SJeremy Kerr 			warn("poll error");
802769cee1aSJeremy Kerr 			break;
803769cee1aSJeremy Kerr 		}
804d831f960SJeremy Kerr 
805329a35f5SJeremy Kerr 		/* process internal fd first */
806329a35f5SJeremy Kerr 		if (console->pollfds[console->n_pollers].revents) {
8071a0e03b4SJeremy Kerr 			rc = read(console->tty_fd, buf, sizeof(buf));
808d831f960SJeremy Kerr 			if (rc <= 0) {
809d831f960SJeremy Kerr 				warn("Error reading from tty device");
810769cee1aSJeremy Kerr 				rc = -1;
811769cee1aSJeremy Kerr 				break;
812d831f960SJeremy Kerr 			}
813f733c85aSJeremy Kerr 			rc = ringbuffer_queue(console->rb, buf, rc);
8142834c5b1SAndrew Jeffery 			if (rc) {
815769cee1aSJeremy Kerr 				break;
816d831f960SJeremy Kerr 			}
8172834c5b1SAndrew Jeffery 		}
818d831f960SJeremy Kerr 
819f9c8f6caSCheng C Yang 		if (console->pollfds[console->n_pollers + 1].revents) {
820f9c8f6caSCheng C Yang 			sd_bus_process(console->bus, NULL);
821f9c8f6caSCheng C Yang 		}
822f9c8f6caSCheng C Yang 
823329a35f5SJeremy Kerr 		/* ... and then the pollers */
8241cecc5deSJohnathan Mantey 		rc = call_pollers(console, &tv);
8252834c5b1SAndrew Jeffery 		if (rc) {
826769cee1aSJeremy Kerr 			break;
8271a0e03b4SJeremy Kerr 		}
8282834c5b1SAndrew Jeffery 	}
829769cee1aSJeremy Kerr 
830769cee1aSJeremy Kerr 	signal(SIGINT, sighandler_save);
831f9c8f6caSCheng C Yang 	sd_bus_unref(console->bus);
832769cee1aSJeremy Kerr 
833769cee1aSJeremy Kerr 	return rc ? -1 : 0;
8341a0e03b4SJeremy Kerr }
835d831f960SJeremy Kerr static const struct option options[] = {
836d66195c1SJeremy Kerr 	{ "config", required_argument, 0, 'c' },
837f5858b5bSJoel Stanley 	{ 0, 0, 0, 0 },
838d831f960SJeremy Kerr };
839d831f960SJeremy Kerr 
840d831f960SJeremy Kerr int main(int argc, char **argv)
841d831f960SJeremy Kerr {
842d66195c1SJeremy Kerr 	const char *config_filename = NULL;
8436221ce94SVishwanatha Subbanna 	const char *config_tty_kname = NULL;
8441a0e03b4SJeremy Kerr 	struct console *console;
845d66195c1SJeremy Kerr 	struct config *config;
846d66195c1SJeremy Kerr 	int rc;
847d831f960SJeremy Kerr 
848957818b4SJeremy Kerr 	rc = -1;
849d831f960SJeremy Kerr 
850d831f960SJeremy Kerr 	for (;;) {
851d831f960SJeremy Kerr 		int c, idx;
852d831f960SJeremy Kerr 
853d66195c1SJeremy Kerr 		c = getopt_long(argc, argv, "c:", options, &idx);
8542834c5b1SAndrew Jeffery 		if (c == -1) {
855d831f960SJeremy Kerr 			break;
8562834c5b1SAndrew Jeffery 		}
857d831f960SJeremy Kerr 
858d831f960SJeremy Kerr 		switch (c) {
859d66195c1SJeremy Kerr 		case 'c':
860d66195c1SJeremy Kerr 			config_filename = optarg;
861d831f960SJeremy Kerr 			break;
862d831f960SJeremy Kerr 		case 'h':
863d831f960SJeremy Kerr 		case '?':
864d831f960SJeremy Kerr 			usage(argv[0]);
865d66195c1SJeremy Kerr 			return EXIT_SUCCESS;
866d831f960SJeremy Kerr 		}
867d831f960SJeremy Kerr 	}
868d831f960SJeremy Kerr 
8692834c5b1SAndrew Jeffery 	if (optind < argc) {
8706221ce94SVishwanatha Subbanna 		config_tty_kname = argv[optind];
8712834c5b1SAndrew Jeffery 	}
8726221ce94SVishwanatha Subbanna 
873d66195c1SJeremy Kerr 	console = malloc(sizeof(struct console));
874d66195c1SJeremy Kerr 	memset(console, 0, sizeof(*console));
875a72711afSAndrew Jeffery 	console->pollfds =
876a72711afSAndrew Jeffery 		calloc(MAX_INTERNAL_POLLFD, sizeof(*console->pollfds));
877f733c85aSJeremy Kerr 	console->rb = ringbuffer_init(buffer_size);
878329a35f5SJeremy Kerr 
879d66195c1SJeremy Kerr 	config = config_init(config_filename);
880d66195c1SJeremy Kerr 	if (!config) {
881d66195c1SJeremy Kerr 		warnx("Can't read configuration, exiting.");
882d66195c1SJeremy Kerr 		goto out_free;
883d831f960SJeremy Kerr 	}
884d831f960SJeremy Kerr 
8852834c5b1SAndrew Jeffery 	if (!config_tty_kname) {
88691dde14eSAndrew Jeffery 		config_tty_kname = config_get_value(config, "upstream-tty");
8872834c5b1SAndrew Jeffery 	}
88891dde14eSAndrew Jeffery 
88991dde14eSAndrew Jeffery 	if (!config_tty_kname) {
89091dde14eSAndrew Jeffery 		warnx("No TTY device specified");
89191dde14eSAndrew Jeffery 		usage(argv[0]);
89291dde14eSAndrew Jeffery 		return EXIT_FAILURE;
89391dde14eSAndrew Jeffery 	}
89491dde14eSAndrew Jeffery 
8956221ce94SVishwanatha Subbanna 	console->tty_kname = config_tty_kname;
8966221ce94SVishwanatha Subbanna 
897d66195c1SJeremy Kerr 	rc = tty_init(console, config);
8982834c5b1SAndrew Jeffery 	if (rc) {
899d66195c1SJeremy Kerr 		goto out_config_fini;
9002834c5b1SAndrew Jeffery 	}
901d831f960SJeremy Kerr 
902f9c8f6caSCheng C Yang 	dbus_init(console, config);
903f9c8f6caSCheng C Yang 
904d47963e5SJeremy Kerr 	handlers_init(console, config);
905d831f960SJeremy Kerr 
9061a0e03b4SJeremy Kerr 	rc = run_console(console);
907d831f960SJeremy Kerr 
9081a0e03b4SJeremy Kerr 	handlers_fini(console);
909d831f960SJeremy Kerr 
910d66195c1SJeremy Kerr out_config_fini:
911d66195c1SJeremy Kerr 	config_fini(config);
912d66195c1SJeremy Kerr 
913957818b4SJeremy Kerr out_free:
91489ea8198SJeremy Kerr 	free(console->pollers);
91589ea8198SJeremy Kerr 	free(console->pollfds);
9161a0e03b4SJeremy Kerr 	free(console->tty_sysfs_devnode);
9171a0e03b4SJeremy Kerr 	free(console->tty_dev);
9181a0e03b4SJeremy Kerr 	free(console);
919d831f960SJeremy Kerr 
920d831f960SJeremy Kerr 	return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
921d831f960SJeremy Kerr }
922