xref: /openbmc/obmc-console/console-server.c (revision b70f8713e37719b6dad0cf8e1bf7f6edae5911d6)
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;
243*b70f8713SAndrew Jeffery 	int i;
244*b70f8713SAndrew Jeffery 	int rc;
245f9c8f6caSCheng C Yang 
246f9c8f6caSCheng C Yang 	tty_init_termios(console);
247f9c8f6caSCheng C Yang 
248f9c8f6caSCheng C Yang 	for (i = 0; i < console->n_handlers; i++) {
249f9c8f6caSCheng C Yang 		handler = console->handlers[i];
2502834c5b1SAndrew Jeffery 		if (!handler->baudrate) {
251f9c8f6caSCheng C Yang 			continue;
2522834c5b1SAndrew Jeffery 		}
253f9c8f6caSCheng C Yang 
254f9c8f6caSCheng C Yang 		rc = handler->baudrate(handler, console->tty_baud);
2552834c5b1SAndrew Jeffery 		if (rc) {
256f9c8f6caSCheng C Yang 			warnx("Can't set terminal baudrate for handler %s",
257f9c8f6caSCheng C Yang 			      handler->name);
258f9c8f6caSCheng C Yang 		}
259f9c8f6caSCheng C Yang 	}
2602834c5b1SAndrew Jeffery }
261f9c8f6caSCheng C Yang 
26254e9569dSJeremy Kerr /**
263d831f960SJeremy Kerr  * Open and initialise the serial device
264d831f960SJeremy Kerr  */
2651a0e03b4SJeremy Kerr static int tty_init_io(struct console *console)
266d831f960SJeremy Kerr {
2672834c5b1SAndrew Jeffery 	if (console->tty_sirq) {
2681a0e03b4SJeremy Kerr 		tty_set_sysfs_attr(console, "sirq", console->tty_sirq);
2692834c5b1SAndrew Jeffery 	}
2702834c5b1SAndrew Jeffery 	if (console->tty_lpc_addr) {
2711a0e03b4SJeremy Kerr 		tty_set_sysfs_attr(console, "lpc_address",
2721a0e03b4SJeremy Kerr 				   console->tty_lpc_addr);
2732834c5b1SAndrew Jeffery 	}
274957818b4SJeremy Kerr 
2751a0e03b4SJeremy Kerr 	console->tty_fd = open(console->tty_dev, O_RDWR);
2761a0e03b4SJeremy Kerr 	if (console->tty_fd <= 0) {
2771a0e03b4SJeremy Kerr 		warn("Can't open tty %s", console->tty_dev);
278d831f960SJeremy Kerr 		return -1;
279d831f960SJeremy Kerr 	}
280d831f960SJeremy Kerr 
281d831f960SJeremy Kerr 	/* Disable character delay. We may want to later enable this when
282d831f960SJeremy Kerr 	 * we detect larger amounts of data
283d831f960SJeremy Kerr 	 */
2841a0e03b4SJeremy Kerr 	fcntl(console->tty_fd, F_SETFL, FNDELAY);
285d831f960SJeremy Kerr 
28654e9569dSJeremy Kerr 	tty_init_termios(console);
28754e9569dSJeremy Kerr 
288329a35f5SJeremy Kerr 	console->pollfds[console->n_pollers].fd = console->tty_fd;
289329a35f5SJeremy Kerr 	console->pollfds[console->n_pollers].events = POLLIN;
290329a35f5SJeremy Kerr 
291d831f960SJeremy Kerr 	return 0;
292d831f960SJeremy Kerr }
293d831f960SJeremy Kerr 
294d66195c1SJeremy Kerr static int tty_init(struct console *console, struct config *config)
295d66195c1SJeremy Kerr {
296fd883a88SAndrew Jeffery 	unsigned long parsed;
297d66195c1SJeremy Kerr 	const char *val;
298d66195c1SJeremy Kerr 	char *endp;
299d66195c1SJeremy Kerr 	int rc;
300d66195c1SJeremy Kerr 
301d66195c1SJeremy Kerr 	val = config_get_value(config, "lpc-address");
302d66195c1SJeremy Kerr 	if (val) {
303fd883a88SAndrew Jeffery 		errno = 0;
304fd883a88SAndrew Jeffery 		parsed = strtoul(val, &endp, 0);
305fd883a88SAndrew Jeffery 		if (parsed == ULONG_MAX && errno == ERANGE) {
306fd883a88SAndrew Jeffery 			warn("Cannot interpret 'lpc-address' value as an unsigned long: '%s'",
307fd883a88SAndrew Jeffery 			     val);
308fd883a88SAndrew Jeffery 			return -1;
309fd883a88SAndrew Jeffery 		}
310fd883a88SAndrew Jeffery 
311fd883a88SAndrew Jeffery 		if (parsed > UINT16_MAX) {
312fd883a88SAndrew Jeffery 			warn("Invalid LPC address '%s'", val);
313fd883a88SAndrew Jeffery 			return -1;
314fd883a88SAndrew Jeffery 		}
315fd883a88SAndrew Jeffery 
316fd883a88SAndrew Jeffery 		console->tty_lpc_addr = (uint16_t)parsed;
317d66195c1SJeremy Kerr 		if (endp == optarg) {
318d66195c1SJeremy Kerr 			warn("Invalid LPC address: '%s'", val);
319d66195c1SJeremy Kerr 			return -1;
320d66195c1SJeremy Kerr 		}
321d66195c1SJeremy Kerr 	}
322d66195c1SJeremy Kerr 
323d66195c1SJeremy Kerr 	val = config_get_value(config, "sirq");
324d66195c1SJeremy Kerr 	if (val) {
325fd883a88SAndrew Jeffery 		errno = 0;
326fd883a88SAndrew Jeffery 		parsed = strtoul(val, &endp, 0);
327fd883a88SAndrew Jeffery 		if (parsed == ULONG_MAX && errno == ERANGE) {
328fd883a88SAndrew Jeffery 			warn("Cannot interpret 'sirq' value as an unsigned long: '%s'",
329fd883a88SAndrew Jeffery 			     val);
330fd883a88SAndrew Jeffery 		}
331fd883a88SAndrew Jeffery 
3322834c5b1SAndrew Jeffery 		if (parsed > 16) {
333fd883a88SAndrew Jeffery 			warn("Invalid LPC SERIRQ: '%s'", val);
3342834c5b1SAndrew Jeffery 		}
335fd883a88SAndrew Jeffery 
336fd883a88SAndrew Jeffery 		console->tty_sirq = (int)parsed;
3372834c5b1SAndrew Jeffery 		if (endp == optarg) {
338d66195c1SJeremy Kerr 			warn("Invalid sirq: '%s'", val);
339d66195c1SJeremy Kerr 		}
3402834c5b1SAndrew Jeffery 	}
341d66195c1SJeremy Kerr 
342c7fbcd48SBenjamin Fair 	val = config_get_value(config, "baud");
343c7fbcd48SBenjamin Fair 	if (val) {
3442834c5b1SAndrew Jeffery 		if (config_parse_baud(&console->tty_baud, val)) {
345c7fbcd48SBenjamin Fair 			warnx("Invalid baud rate: '%s'", val);
346c7fbcd48SBenjamin Fair 		}
3472834c5b1SAndrew Jeffery 	}
348c7fbcd48SBenjamin Fair 
349d66195c1SJeremy Kerr 	if (!console->tty_kname) {
350d66195c1SJeremy Kerr 		warnx("Error: No TTY device specified");
351d66195c1SJeremy Kerr 		return -1;
352d66195c1SJeremy Kerr 	}
353d66195c1SJeremy Kerr 
354d66195c1SJeremy Kerr 	rc = tty_find_device(console);
3552834c5b1SAndrew Jeffery 	if (rc) {
356d66195c1SJeremy Kerr 		return rc;
3572834c5b1SAndrew Jeffery 	}
358d66195c1SJeremy Kerr 
359d66195c1SJeremy Kerr 	rc = tty_init_io(console);
360d66195c1SJeremy Kerr 	return rc;
361d66195c1SJeremy Kerr }
362d66195c1SJeremy Kerr 
3631a0e03b4SJeremy Kerr int console_data_out(struct console *console, const uint8_t *data, size_t len)
364d831f960SJeremy Kerr {
3651a0e03b4SJeremy Kerr 	return write_buf_to_fd(console->tty_fd, data, len);
366d831f960SJeremy Kerr }
367d831f960SJeremy Kerr 
368f9c8f6caSCheng C Yang static int method_set_baud_rate(sd_bus_message *msg, void *userdata,
369f9c8f6caSCheng C Yang 				sd_bus_error *err)
370f9c8f6caSCheng C Yang {
371f9c8f6caSCheng C Yang 	struct console *console = userdata;
372f9c8f6caSCheng C Yang 	uint32_t baudrate;
373f9c8f6caSCheng C Yang 	speed_t speed;
374f9c8f6caSCheng C Yang 	int r;
375f9c8f6caSCheng C Yang 
376f9c8f6caSCheng C Yang 	if (!console) {
377f9c8f6caSCheng C Yang 		sd_bus_error_set_const(err, DBUS_ERR, "Internal error");
378f9c8f6caSCheng C Yang 		return sd_bus_reply_method_return(msg, "x", 0);
379f9c8f6caSCheng C Yang 	}
380f9c8f6caSCheng C Yang 
381f9c8f6caSCheng C Yang 	r = sd_bus_message_read(msg, "u", &baudrate);
382f9c8f6caSCheng C Yang 	if (r < 0) {
383f9c8f6caSCheng C Yang 		sd_bus_error_set_const(err, DBUS_ERR, "Bad message");
384f9c8f6caSCheng C Yang 		return sd_bus_reply_method_return(msg, "x", -EINVAL);
385f9c8f6caSCheng C Yang 	}
386f9c8f6caSCheng C Yang 
387f9c8f6caSCheng C Yang 	speed = parse_int_to_baud(baudrate);
388f9c8f6caSCheng C Yang 	if (!speed) {
389f9c8f6caSCheng C Yang 		warnx("Invalid baud rate: '%u'", baudrate);
390f9c8f6caSCheng C Yang 		return sd_bus_reply_method_return(msg, "x", -EINVAL);
391f9c8f6caSCheng C Yang 	}
392f9c8f6caSCheng C Yang 
393f9c8f6caSCheng C Yang 	console->tty_baud = speed;
394f9c8f6caSCheng C Yang 	tty_change_baudrate(console);
395f9c8f6caSCheng C Yang 
396f9c8f6caSCheng C Yang 	return sd_bus_reply_method_return(msg, "x", r);
397f9c8f6caSCheng C Yang }
398f9c8f6caSCheng C Yang 
399fd048328SAndrew Jeffery static int get_handler(sd_bus *bus __attribute__((unused)),
400fd048328SAndrew Jeffery 		       const char *path __attribute__((unused)),
401fd048328SAndrew Jeffery 		       const char *interface __attribute__((unused)),
402fd048328SAndrew Jeffery 		       const char *property __attribute__((unused)),
403fd048328SAndrew Jeffery 		       sd_bus_message *reply, void *userdata,
404a72711afSAndrew Jeffery 		       sd_bus_error *error __attribute__((unused)))
405a72711afSAndrew Jeffery {
406f9c8f6caSCheng C Yang 	struct console *console = userdata;
407f9c8f6caSCheng C Yang 	uint32_t baudrate;
408f9c8f6caSCheng C Yang 	int r;
409f9c8f6caSCheng C Yang 
410f9c8f6caSCheng C Yang 	baudrate = parse_baud_to_int(console->tty_baud);
4112834c5b1SAndrew Jeffery 	if (!baudrate) {
412f9c8f6caSCheng C Yang 		warnx("Invalid baud rate: '%d'", console->tty_baud);
4132834c5b1SAndrew Jeffery 	}
414f9c8f6caSCheng C Yang 
415f9c8f6caSCheng C Yang 	r = sd_bus_message_append(reply, "u", baudrate);
416f9c8f6caSCheng C Yang 
417f9c8f6caSCheng C Yang 	return r;
418f9c8f6caSCheng C Yang }
419f9c8f6caSCheng C Yang 
420f9c8f6caSCheng C Yang static const sd_bus_vtable console_vtable[] = {
421f9c8f6caSCheng C Yang 	SD_BUS_VTABLE_START(0),
422f9c8f6caSCheng C Yang 	SD_BUS_METHOD("setBaudRate", "u", "x", method_set_baud_rate,
423f9c8f6caSCheng C Yang 		      SD_BUS_VTABLE_UNPRIVILEGED),
424f9c8f6caSCheng C Yang 	SD_BUS_PROPERTY("baudrate", "u", get_handler, 0, 0),
425a72711afSAndrew Jeffery 	SD_BUS_VTABLE_END,
426a72711afSAndrew Jeffery };
427f9c8f6caSCheng C Yang 
428a72711afSAndrew Jeffery static void dbus_init(struct console *console,
429a72711afSAndrew Jeffery 		      struct config *config __attribute__((unused)))
430f9c8f6caSCheng C Yang {
431f9c8f6caSCheng C Yang 	int dbus_poller = 0;
432*b70f8713SAndrew Jeffery 	int fd;
433*b70f8713SAndrew Jeffery 	int r;
434f9c8f6caSCheng C Yang 
435f9c8f6caSCheng C Yang 	if (!console) {
436f9c8f6caSCheng C Yang 		warnx("Couldn't get valid console");
437f9c8f6caSCheng C Yang 		return;
438f9c8f6caSCheng C Yang 	}
439f9c8f6caSCheng C Yang 
440f9c8f6caSCheng C Yang 	r = sd_bus_default_system(&console->bus);
441f9c8f6caSCheng C Yang 	if (r < 0) {
442f9c8f6caSCheng C Yang 		warnx("Failed to connect to system bus: %s", strerror(-r));
443f9c8f6caSCheng C Yang 		return;
444f9c8f6caSCheng C Yang 	}
445f9c8f6caSCheng C Yang 
446f9c8f6caSCheng C Yang 	r = sd_bus_add_object_vtable(console->bus, NULL, OBJ_NAME, DBUS_NAME,
447f9c8f6caSCheng C Yang 				     console_vtable, console);
448f9c8f6caSCheng C Yang 	if (r < 0) {
449f9c8f6caSCheng C Yang 		warnx("Failed to issue method call: %s", strerror(-r));
450f9c8f6caSCheng C Yang 		return;
451f9c8f6caSCheng C Yang 	}
452f9c8f6caSCheng C Yang 
453a72711afSAndrew Jeffery 	r = sd_bus_request_name(console->bus, DBUS_NAME,
454a72711afSAndrew Jeffery 				SD_BUS_NAME_ALLOW_REPLACEMENT |
455a72711afSAndrew Jeffery 					SD_BUS_NAME_REPLACE_EXISTING);
456f9c8f6caSCheng C Yang 	if (r < 0) {
457f9c8f6caSCheng C Yang 		warnx("Failed to acquire service name: %s", strerror(-r));
458f9c8f6caSCheng C Yang 		return;
459f9c8f6caSCheng C Yang 	}
460f9c8f6caSCheng C Yang 
461f9c8f6caSCheng C Yang 	fd = sd_bus_get_fd(console->bus);
462f9c8f6caSCheng C Yang 	if (fd < 0) {
463f9c8f6caSCheng C Yang 		warnx("Couldn't get the bus file descriptor");
464f9c8f6caSCheng C Yang 		return;
465f9c8f6caSCheng C Yang 	}
466f9c8f6caSCheng C Yang 
467f9c8f6caSCheng C Yang 	dbus_poller = POLLFD_DBUS;
468f9c8f6caSCheng C Yang 
469f9c8f6caSCheng C Yang 	console->pollfds[dbus_poller].fd = fd;
470f9c8f6caSCheng C Yang 	console->pollfds[dbus_poller].events = POLLIN;
471f9c8f6caSCheng C Yang }
472f9c8f6caSCheng C Yang 
473d47963e5SJeremy Kerr static void handlers_init(struct console *console, struct config *config)
474d831f960SJeremy Kerr {
475*b70f8713SAndrew Jeffery 	/* NOLINTBEGIN(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) */
476*b70f8713SAndrew Jeffery 	extern struct handler *__start_handlers;
477*b70f8713SAndrew Jeffery 	extern struct handler *__stop_handlers;
478*b70f8713SAndrew Jeffery 	/* NOLINTEND(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) */
4791a0e03b4SJeremy Kerr 	struct handler *handler;
480*b70f8713SAndrew Jeffery 	int i;
481*b70f8713SAndrew Jeffery 	int rc;
482d831f960SJeremy Kerr 
4831a0e03b4SJeremy Kerr 	console->n_handlers = &__stop_handlers - &__start_handlers;
4841a0e03b4SJeremy Kerr 	console->handlers = &__start_handlers;
485d831f960SJeremy Kerr 
4865c359cc6SAndrew Jeffery 	printf("%ld handler%s\n", console->n_handlers,
4871a0e03b4SJeremy Kerr 	       console->n_handlers == 1 ? "" : "s");
488d831f960SJeremy Kerr 
4891a0e03b4SJeremy Kerr 	for (i = 0; i < console->n_handlers; i++) {
4901a0e03b4SJeremy Kerr 		handler = console->handlers[i];
4911a0e03b4SJeremy Kerr 
492021b91f0SJeremy Kerr 		rc = 0;
4932834c5b1SAndrew Jeffery 		if (handler->init) {
494021b91f0SJeremy Kerr 			rc = handler->init(handler, console, config);
4952834c5b1SAndrew Jeffery 		}
496021b91f0SJeremy Kerr 
497021b91f0SJeremy Kerr 		handler->active = rc == 0;
498021b91f0SJeremy Kerr 
499021b91f0SJeremy Kerr 		printf("  %s [%sactive]\n", handler->name,
500021b91f0SJeremy Kerr 		       handler->active ? "" : "in");
501d831f960SJeremy Kerr 	}
502d831f960SJeremy Kerr }
503d831f960SJeremy Kerr 
5041a0e03b4SJeremy Kerr static void handlers_fini(struct console *console)
505d831f960SJeremy Kerr {
5061a0e03b4SJeremy Kerr 	struct handler *handler;
5071a0e03b4SJeremy Kerr 	int i;
5081a0e03b4SJeremy Kerr 
5091a0e03b4SJeremy Kerr 	for (i = 0; i < console->n_handlers; i++) {
5101a0e03b4SJeremy Kerr 		handler = console->handlers[i];
5112834c5b1SAndrew Jeffery 		if (handler->fini && handler->active) {
5121a0e03b4SJeremy Kerr 			handler->fini(handler);
5131a0e03b4SJeremy Kerr 		}
514d831f960SJeremy Kerr 	}
5152834c5b1SAndrew Jeffery }
516d831f960SJeremy Kerr 
5171cecc5deSJohnathan Mantey static int get_current_time(struct timeval *tv)
5181cecc5deSJohnathan Mantey {
5191cecc5deSJohnathan Mantey 	struct timespec t;
5201cecc5deSJohnathan Mantey 	int rc;
5211cecc5deSJohnathan Mantey 
5221cecc5deSJohnathan Mantey 	/*
5231cecc5deSJohnathan Mantey 	 * We use clock_gettime(CLOCK_MONOTONIC) so we're immune to
5241cecc5deSJohnathan Mantey 	 * local time changes. However, a struct timeval is more
5251cecc5deSJohnathan Mantey 	 * convenient for calculations, so convert to that.
5261cecc5deSJohnathan Mantey 	 */
5271cecc5deSJohnathan Mantey 	rc = clock_gettime(CLOCK_MONOTONIC, &t);
5282834c5b1SAndrew Jeffery 	if (rc) {
5291cecc5deSJohnathan Mantey 		return rc;
5302834c5b1SAndrew Jeffery 	}
5311cecc5deSJohnathan Mantey 
5321cecc5deSJohnathan Mantey 	tv->tv_sec = t.tv_sec;
5331cecc5deSJohnathan Mantey 	tv->tv_usec = t.tv_nsec / 1000;
5341cecc5deSJohnathan Mantey 
5351cecc5deSJohnathan Mantey 	return 0;
5361cecc5deSJohnathan Mantey }
5371cecc5deSJohnathan Mantey 
538a72711afSAndrew Jeffery struct ringbuffer_consumer *
539a72711afSAndrew Jeffery console_ringbuffer_consumer_register(struct console *console,
540f733c85aSJeremy Kerr 				     ringbuffer_poll_fn_t poll_fn, void *data)
541d831f960SJeremy Kerr {
542f733c85aSJeremy Kerr 	return ringbuffer_consumer_register(console->rb, poll_fn, data);
543d831f960SJeremy Kerr }
544d831f960SJeremy Kerr 
54555c9712dSJeremy Kerr struct poller *console_poller_register(struct console *console,
546a72711afSAndrew Jeffery 				       struct handler *handler,
547a72711afSAndrew Jeffery 				       poller_event_fn_t poller_fn,
5481cecc5deSJohnathan Mantey 				       poller_timeout_fn_t timeout_fn, int fd,
5491cecc5deSJohnathan Mantey 				       int events, void *data)
550d831f960SJeremy Kerr {
551329a35f5SJeremy Kerr 	struct poller *poller;
5525c359cc6SAndrew Jeffery 	long n;
553329a35f5SJeremy Kerr 
554329a35f5SJeremy Kerr 	poller = malloc(sizeof(*poller));
555329a35f5SJeremy Kerr 	poller->remove = false;
556329a35f5SJeremy Kerr 	poller->handler = handler;
5571cecc5deSJohnathan Mantey 	poller->event_fn = poller_fn;
5581cecc5deSJohnathan Mantey 	poller->timeout_fn = timeout_fn;
559329a35f5SJeremy Kerr 	poller->data = data;
560329a35f5SJeremy Kerr 
561329a35f5SJeremy Kerr 	/* add one to our pollers array */
562329a35f5SJeremy Kerr 	n = console->n_pollers++;
56391b52175SAndrew Jeffery 	/*
56491b52175SAndrew Jeffery 	 * We're managing an array of pointers to aggregates, so don't warn about sizeof() on a
56591b52175SAndrew Jeffery 	 * pointer type.
56691b52175SAndrew Jeffery 	 */
56791b52175SAndrew Jeffery 	/* NOLINTBEGIN(bugprone-sizeof-expression) */
56891b52175SAndrew Jeffery 	console->pollers = reallocarray(console->pollers, console->n_pollers,
56991b52175SAndrew Jeffery 					sizeof(*console->pollers));
57091b52175SAndrew Jeffery 	/* NOLINTEND(bugprone-sizeof-expression) */
571329a35f5SJeremy Kerr 
572329a35f5SJeremy Kerr 	console->pollers[n] = poller;
573329a35f5SJeremy Kerr 
574329a35f5SJeremy Kerr 	/* increase pollfds array too  */
575a72711afSAndrew Jeffery 	console->pollfds =
57691b52175SAndrew Jeffery 		reallocarray(console->pollfds,
57791b52175SAndrew Jeffery 			     (MAX_INTERNAL_POLLFD + console->n_pollers),
57891b52175SAndrew Jeffery 			     sizeof(*console->pollfds));
579329a35f5SJeremy Kerr 
580329a35f5SJeremy Kerr 	/* shift the end pollfds up by one */
581a72711afSAndrew Jeffery 	memcpy(&console->pollfds[n + 1], &console->pollfds[n],
582f9c8f6caSCheng C Yang 	       sizeof(*console->pollfds) * MAX_INTERNAL_POLLFD);
583329a35f5SJeremy Kerr 
584329a35f5SJeremy Kerr 	console->pollfds[n].fd = fd;
5855c359cc6SAndrew Jeffery 	console->pollfds[n].events = (short)(events & 0x7fff);
586329a35f5SJeremy Kerr 
587329a35f5SJeremy Kerr 	return poller;
588329a35f5SJeremy Kerr }
589329a35f5SJeremy Kerr 
590a72711afSAndrew Jeffery void console_poller_unregister(struct console *console, struct poller *poller)
591329a35f5SJeremy Kerr {
592329a35f5SJeremy Kerr 	int i;
593329a35f5SJeremy Kerr 
594329a35f5SJeremy Kerr 	/* find the entry in our pollers array */
5952834c5b1SAndrew Jeffery 	for (i = 0; i < console->n_pollers; i++) {
5962834c5b1SAndrew Jeffery 		if (console->pollers[i] == poller) {
597329a35f5SJeremy Kerr 			break;
5982834c5b1SAndrew Jeffery 		}
5992834c5b1SAndrew Jeffery 	}
600329a35f5SJeremy Kerr 
601329a35f5SJeremy Kerr 	assert(i < console->n_pollers);
602329a35f5SJeremy Kerr 
603329a35f5SJeremy Kerr 	console->n_pollers--;
604329a35f5SJeremy Kerr 
60591b52175SAndrew Jeffery 	/*
60691b52175SAndrew Jeffery 	 * Remove the item from the pollers array...
60791b52175SAndrew Jeffery 	 *
60891b52175SAndrew Jeffery 	 * We're managing an array of pointers to aggregates, so don't warn about sizeof() on a
60991b52175SAndrew Jeffery 	 * pointer type.
61091b52175SAndrew Jeffery 	 */
61191b52175SAndrew Jeffery 	/* NOLINTBEGIN(bugprone-sizeof-expression) */
612329a35f5SJeremy Kerr 	memmove(&console->pollers[i], &console->pollers[i + 1],
613a72711afSAndrew Jeffery 		sizeof(*console->pollers) * (console->n_pollers - i));
614329a35f5SJeremy Kerr 
61591b52175SAndrew Jeffery 	console->pollers = reallocarray(console->pollers, console->n_pollers,
61691b52175SAndrew Jeffery 					sizeof(*console->pollers));
61791b52175SAndrew Jeffery 	/* NOLINTEND(bugprone-sizeof-expression) */
618329a35f5SJeremy Kerr 
619329a35f5SJeremy Kerr 	/* ... and the pollfds array */
620329a35f5SJeremy Kerr 	memmove(&console->pollfds[i], &console->pollfds[i + 1],
621329a35f5SJeremy Kerr 		sizeof(*console->pollfds) *
622f9c8f6caSCheng C Yang 			(MAX_INTERNAL_POLLFD + console->n_pollers - i));
623329a35f5SJeremy Kerr 
624a72711afSAndrew Jeffery 	console->pollfds =
62591b52175SAndrew Jeffery 		reallocarray(console->pollfds,
62691b52175SAndrew Jeffery 			     (MAX_INTERNAL_POLLFD + console->n_pollers),
62791b52175SAndrew Jeffery 			     sizeof(*console->pollfds));
628329a35f5SJeremy Kerr 
629329a35f5SJeremy Kerr 	free(poller);
630329a35f5SJeremy Kerr }
631329a35f5SJeremy Kerr 
6326b1fed27SJeremy Kerr void console_poller_set_events(struct console *console, struct poller *poller,
6336b1fed27SJeremy Kerr 			       int events)
6346b1fed27SJeremy Kerr {
6356b1fed27SJeremy Kerr 	int i;
6366b1fed27SJeremy Kerr 
6376b1fed27SJeremy Kerr 	/* find the entry in our pollers array */
6382834c5b1SAndrew Jeffery 	for (i = 0; i < console->n_pollers; i++) {
6392834c5b1SAndrew Jeffery 		if (console->pollers[i] == poller) {
6406b1fed27SJeremy Kerr 			break;
6412834c5b1SAndrew Jeffery 		}
6422834c5b1SAndrew Jeffery 	}
6436b1fed27SJeremy Kerr 
6445c359cc6SAndrew Jeffery 	console->pollfds[i].events = (short)(events & 0x7fff);
6456b1fed27SJeremy Kerr }
6466b1fed27SJeremy Kerr 
647fd048328SAndrew Jeffery void console_poller_set_timeout(struct console *console __attribute__((unused)),
648fd048328SAndrew Jeffery 				struct poller *poller, const struct timeval *tv)
6491cecc5deSJohnathan Mantey {
6501cecc5deSJohnathan Mantey 	struct timeval now;
6511cecc5deSJohnathan Mantey 	int rc;
6521cecc5deSJohnathan Mantey 
6531cecc5deSJohnathan Mantey 	rc = get_current_time(&now);
6542834c5b1SAndrew Jeffery 	if (rc) {
6551cecc5deSJohnathan Mantey 		return;
6562834c5b1SAndrew Jeffery 	}
6571cecc5deSJohnathan Mantey 
6581cecc5deSJohnathan Mantey 	timeradd(&now, tv, &poller->timeout);
6591cecc5deSJohnathan Mantey }
6601cecc5deSJohnathan Mantey 
6615c359cc6SAndrew Jeffery static long get_poll_timeout(struct console *console, struct timeval *cur_time)
6621cecc5deSJohnathan Mantey {
663*b70f8713SAndrew Jeffery 	struct timeval *earliest;
664*b70f8713SAndrew Jeffery 	struct timeval interval;
6651cecc5deSJohnathan Mantey 	struct poller *poller;
6661cecc5deSJohnathan Mantey 	int i;
6671cecc5deSJohnathan Mantey 
6681cecc5deSJohnathan Mantey 	earliest = NULL;
6691cecc5deSJohnathan Mantey 
6701cecc5deSJohnathan Mantey 	for (i = 0; i < console->n_pollers; i++) {
6711cecc5deSJohnathan Mantey 		poller = console->pollers[i];
6721cecc5deSJohnathan Mantey 
6731cecc5deSJohnathan Mantey 		if (poller->timeout_fn && timerisset(&poller->timeout) &&
6741cecc5deSJohnathan Mantey 		    (!earliest ||
6751cecc5deSJohnathan Mantey 		     (earliest && timercmp(&poller->timeout, earliest, <)))) {
6761cecc5deSJohnathan Mantey 			// poller is buffering data and needs the poll
6771cecc5deSJohnathan Mantey 			// function to timeout.
6781cecc5deSJohnathan Mantey 			earliest = &poller->timeout;
6791cecc5deSJohnathan Mantey 		}
6801cecc5deSJohnathan Mantey 	}
6811cecc5deSJohnathan Mantey 
6821cecc5deSJohnathan Mantey 	if (earliest) {
6831cecc5deSJohnathan Mantey 		if (timercmp(earliest, cur_time, >)) {
6841cecc5deSJohnathan Mantey 			/* recalculate the timeout period, time period has
6851cecc5deSJohnathan Mantey 			 * not elapsed */
6861cecc5deSJohnathan Mantey 			timersub(earliest, cur_time, &interval);
6871cecc5deSJohnathan Mantey 			return ((interval.tv_sec * 1000) +
6881cecc5deSJohnathan Mantey 				(interval.tv_usec / 1000));
6890b7b0477SAndrew Jeffery 		} /* return from poll immediately */
6901cecc5deSJohnathan Mantey 		return 0;
6910b7b0477SAndrew Jeffery 
6920b7b0477SAndrew Jeffery 	} /* poll indefinitely */
6931cecc5deSJohnathan Mantey 	return -1;
6941cecc5deSJohnathan Mantey }
6951cecc5deSJohnathan Mantey 
6961cecc5deSJohnathan Mantey static int call_pollers(struct console *console, struct timeval *cur_time)
697329a35f5SJeremy Kerr {
698329a35f5SJeremy Kerr 	struct poller *poller;
699329a35f5SJeremy Kerr 	struct pollfd *pollfd;
700329a35f5SJeremy Kerr 	enum poller_ret prc;
701*b70f8713SAndrew Jeffery 	int i;
702*b70f8713SAndrew Jeffery 	int rc;
703d831f960SJeremy Kerr 
7041a0e03b4SJeremy Kerr 	rc = 0;
7051a0e03b4SJeremy Kerr 
706329a35f5SJeremy Kerr 	/*
707329a35f5SJeremy Kerr 	 * Process poll events by iterating through the pollers and pollfds
708329a35f5SJeremy Kerr 	 * in-step, calling any pollers that we've found revents for.
709329a35f5SJeremy Kerr 	 */
710329a35f5SJeremy Kerr 	for (i = 0; i < console->n_pollers; i++) {
711329a35f5SJeremy Kerr 		poller = console->pollers[i];
712329a35f5SJeremy Kerr 		pollfd = &console->pollfds[i];
7131cecc5deSJohnathan Mantey 		prc = POLLER_OK;
7141a0e03b4SJeremy Kerr 
7151cecc5deSJohnathan Mantey 		/* process pending events... */
7161cecc5deSJohnathan Mantey 		if (pollfd->revents) {
7171cecc5deSJohnathan Mantey 			prc = poller->event_fn(poller->handler, pollfd->revents,
718329a35f5SJeremy Kerr 					       poller->data);
7192834c5b1SAndrew Jeffery 			if (prc == POLLER_EXIT) {
720329a35f5SJeremy Kerr 				rc = -1;
7212834c5b1SAndrew Jeffery 			} else if (prc == POLLER_REMOVE) {
722329a35f5SJeremy Kerr 				poller->remove = true;
723329a35f5SJeremy Kerr 			}
7242834c5b1SAndrew Jeffery 		}
725329a35f5SJeremy Kerr 
7261cecc5deSJohnathan Mantey 		if ((prc == POLLER_OK) && poller->timeout_fn &&
7271cecc5deSJohnathan Mantey 		    timerisset(&poller->timeout) &&
7281cecc5deSJohnathan Mantey 		    timercmp(&poller->timeout, cur_time, <=)) {
7291cecc5deSJohnathan Mantey 			/* One of the ringbuffer consumers is buffering the
7301cecc5deSJohnathan Mantey 			data stream. The amount of idle time the consumer
7311cecc5deSJohnathan Mantey 			desired has expired.  Process the buffered data for
7321cecc5deSJohnathan Mantey 			transmission. */
7331cecc5deSJohnathan Mantey 			timerclear(&poller->timeout);
7341cecc5deSJohnathan Mantey 			prc = poller->timeout_fn(poller->handler, poller->data);
7351cecc5deSJohnathan Mantey 			if (prc == POLLER_EXIT) {
7361cecc5deSJohnathan Mantey 				rc = -1;
7371cecc5deSJohnathan Mantey 			} else if (prc == POLLER_REMOVE) {
7381cecc5deSJohnathan Mantey 				poller->remove = true;
7391cecc5deSJohnathan Mantey 			}
7401cecc5deSJohnathan Mantey 		}
7411cecc5deSJohnathan Mantey 	}
7421cecc5deSJohnathan Mantey 
743329a35f5SJeremy Kerr 	/**
744329a35f5SJeremy Kerr 	 * Process deferred removals; restarting each time we unregister, as
745329a35f5SJeremy Kerr 	 * the array will have changed
746329a35f5SJeremy Kerr 	 */
747329a35f5SJeremy Kerr 	for (;;) {
748329a35f5SJeremy Kerr 		bool removed = false;
749329a35f5SJeremy Kerr 
750329a35f5SJeremy Kerr 		for (i = 0; i < console->n_pollers; i++) {
751329a35f5SJeremy Kerr 			poller = console->pollers[i];
752329a35f5SJeremy Kerr 			if (poller->remove) {
75355c9712dSJeremy Kerr 				console_poller_unregister(console, poller);
754329a35f5SJeremy Kerr 				removed = true;
755329a35f5SJeremy Kerr 				break;
756329a35f5SJeremy Kerr 			}
757329a35f5SJeremy Kerr 		}
7582834c5b1SAndrew Jeffery 		if (!removed) {
759329a35f5SJeremy Kerr 			break;
7601a0e03b4SJeremy Kerr 		}
7612834c5b1SAndrew Jeffery 	}
7621a0e03b4SJeremy Kerr 
7631a0e03b4SJeremy Kerr 	return rc;
7641a0e03b4SJeremy Kerr }
7651a0e03b4SJeremy Kerr 
766769cee1aSJeremy Kerr static void sighandler(int signal)
767769cee1aSJeremy Kerr {
7682834c5b1SAndrew Jeffery 	if (signal == SIGINT) {
769769cee1aSJeremy Kerr 		sigint = true;
770769cee1aSJeremy Kerr 	}
7712834c5b1SAndrew Jeffery }
772769cee1aSJeremy Kerr 
7731a0e03b4SJeremy Kerr int run_console(struct console *console)
7741a0e03b4SJeremy Kerr {
7755c359cc6SAndrew Jeffery 	sighandler_t sighandler_save = signal(SIGINT, sighandler);
7761cecc5deSJohnathan Mantey 	struct timeval tv;
7775c359cc6SAndrew Jeffery 	long timeout;
7785c359cc6SAndrew Jeffery 	ssize_t rc;
779769cee1aSJeremy Kerr 
780769cee1aSJeremy Kerr 	rc = 0;
781769cee1aSJeremy Kerr 
782d831f960SJeremy Kerr 	for (;;) {
783d831f960SJeremy Kerr 		uint8_t buf[4096];
784d831f960SJeremy Kerr 
7851764145dSJeremy Kerr 		BUILD_ASSERT(sizeof(buf) <= buffer_size);
7861764145dSJeremy Kerr 
787769cee1aSJeremy Kerr 		if (sigint) {
788769cee1aSJeremy Kerr 			fprintf(stderr, "Received interrupt, exiting\n");
789769cee1aSJeremy Kerr 			break;
790769cee1aSJeremy Kerr 		}
791769cee1aSJeremy Kerr 
7921cecc5deSJohnathan Mantey 		rc = get_current_time(&tv);
7931cecc5deSJohnathan Mantey 		if (rc) {
7941cecc5deSJohnathan Mantey 			warn("Failed to read current time");
7951cecc5deSJohnathan Mantey 			break;
7961cecc5deSJohnathan Mantey 		}
7971cecc5deSJohnathan Mantey 
7981cecc5deSJohnathan Mantey 		timeout = get_poll_timeout(console, &tv);
7991cecc5deSJohnathan Mantey 
800329a35f5SJeremy Kerr 		rc = poll(console->pollfds,
8015c359cc6SAndrew Jeffery 			  console->n_pollers + MAX_INTERNAL_POLLFD,
8025c359cc6SAndrew Jeffery 			  (int)timeout);
8031cecc5deSJohnathan Mantey 
804d831f960SJeremy Kerr 		if (rc < 0) {
805769cee1aSJeremy Kerr 			if (errno == EINTR) {
806769cee1aSJeremy Kerr 				continue;
8070b7b0477SAndrew Jeffery 			}
808d831f960SJeremy Kerr 			warn("poll error");
809769cee1aSJeremy Kerr 			break;
810769cee1aSJeremy Kerr 		}
811d831f960SJeremy Kerr 
812329a35f5SJeremy Kerr 		/* process internal fd first */
813329a35f5SJeremy Kerr 		if (console->pollfds[console->n_pollers].revents) {
8141a0e03b4SJeremy Kerr 			rc = read(console->tty_fd, buf, sizeof(buf));
815d831f960SJeremy Kerr 			if (rc <= 0) {
816d831f960SJeremy Kerr 				warn("Error reading from tty device");
817769cee1aSJeremy Kerr 				rc = -1;
818769cee1aSJeremy Kerr 				break;
819d831f960SJeremy Kerr 			}
820f733c85aSJeremy Kerr 			rc = ringbuffer_queue(console->rb, buf, rc);
8212834c5b1SAndrew Jeffery 			if (rc) {
822769cee1aSJeremy Kerr 				break;
823d831f960SJeremy Kerr 			}
8242834c5b1SAndrew Jeffery 		}
825d831f960SJeremy Kerr 
826f9c8f6caSCheng C Yang 		if (console->pollfds[console->n_pollers + 1].revents) {
827f9c8f6caSCheng C Yang 			sd_bus_process(console->bus, NULL);
828f9c8f6caSCheng C Yang 		}
829f9c8f6caSCheng C Yang 
830329a35f5SJeremy Kerr 		/* ... and then the pollers */
8311cecc5deSJohnathan Mantey 		rc = call_pollers(console, &tv);
8322834c5b1SAndrew Jeffery 		if (rc) {
833769cee1aSJeremy Kerr 			break;
8341a0e03b4SJeremy Kerr 		}
8352834c5b1SAndrew Jeffery 	}
836769cee1aSJeremy Kerr 
837769cee1aSJeremy Kerr 	signal(SIGINT, sighandler_save);
838f9c8f6caSCheng C Yang 	sd_bus_unref(console->bus);
839769cee1aSJeremy Kerr 
840769cee1aSJeremy Kerr 	return rc ? -1 : 0;
8411a0e03b4SJeremy Kerr }
842d831f960SJeremy Kerr static const struct option options[] = {
843d66195c1SJeremy Kerr 	{ "config", required_argument, 0, 'c' },
844f5858b5bSJoel Stanley 	{ 0, 0, 0, 0 },
845d831f960SJeremy Kerr };
846d831f960SJeremy Kerr 
847d831f960SJeremy Kerr int main(int argc, char **argv)
848d831f960SJeremy Kerr {
849d66195c1SJeremy Kerr 	const char *config_filename = NULL;
8506221ce94SVishwanatha Subbanna 	const char *config_tty_kname = NULL;
8511a0e03b4SJeremy Kerr 	struct console *console;
852d66195c1SJeremy Kerr 	struct config *config;
853d66195c1SJeremy Kerr 	int rc;
854d831f960SJeremy Kerr 
855957818b4SJeremy Kerr 	rc = -1;
856d831f960SJeremy Kerr 
857d831f960SJeremy Kerr 	for (;;) {
858*b70f8713SAndrew Jeffery 		int c;
859*b70f8713SAndrew Jeffery 		int idx;
860d831f960SJeremy Kerr 
861d66195c1SJeremy Kerr 		c = getopt_long(argc, argv, "c:", options, &idx);
8622834c5b1SAndrew Jeffery 		if (c == -1) {
863d831f960SJeremy Kerr 			break;
8642834c5b1SAndrew Jeffery 		}
865d831f960SJeremy Kerr 
866d831f960SJeremy Kerr 		switch (c) {
867d66195c1SJeremy Kerr 		case 'c':
868d66195c1SJeremy Kerr 			config_filename = optarg;
869d831f960SJeremy Kerr 			break;
870d831f960SJeremy Kerr 		case 'h':
871d831f960SJeremy Kerr 		case '?':
872d831f960SJeremy Kerr 			usage(argv[0]);
873d66195c1SJeremy Kerr 			return EXIT_SUCCESS;
874d831f960SJeremy Kerr 		}
875d831f960SJeremy Kerr 	}
876d831f960SJeremy Kerr 
8772834c5b1SAndrew Jeffery 	if (optind < argc) {
8786221ce94SVishwanatha Subbanna 		config_tty_kname = argv[optind];
8792834c5b1SAndrew Jeffery 	}
8806221ce94SVishwanatha Subbanna 
881d66195c1SJeremy Kerr 	console = malloc(sizeof(struct console));
882d66195c1SJeremy Kerr 	memset(console, 0, sizeof(*console));
883a72711afSAndrew Jeffery 	console->pollfds =
884a72711afSAndrew Jeffery 		calloc(MAX_INTERNAL_POLLFD, sizeof(*console->pollfds));
885f733c85aSJeremy Kerr 	console->rb = ringbuffer_init(buffer_size);
886329a35f5SJeremy Kerr 
887d66195c1SJeremy Kerr 	config = config_init(config_filename);
888d66195c1SJeremy Kerr 	if (!config) {
889d66195c1SJeremy Kerr 		warnx("Can't read configuration, exiting.");
890d66195c1SJeremy Kerr 		goto out_free;
891d831f960SJeremy Kerr 	}
892d831f960SJeremy Kerr 
8932834c5b1SAndrew Jeffery 	if (!config_tty_kname) {
89491dde14eSAndrew Jeffery 		config_tty_kname = config_get_value(config, "upstream-tty");
8952834c5b1SAndrew Jeffery 	}
89691dde14eSAndrew Jeffery 
89791dde14eSAndrew Jeffery 	if (!config_tty_kname) {
89891dde14eSAndrew Jeffery 		warnx("No TTY device specified");
89991dde14eSAndrew Jeffery 		usage(argv[0]);
90091dde14eSAndrew Jeffery 		return EXIT_FAILURE;
90191dde14eSAndrew Jeffery 	}
90291dde14eSAndrew Jeffery 
9036221ce94SVishwanatha Subbanna 	console->tty_kname = config_tty_kname;
9046221ce94SVishwanatha Subbanna 
905d66195c1SJeremy Kerr 	rc = tty_init(console, config);
9062834c5b1SAndrew Jeffery 	if (rc) {
907d66195c1SJeremy Kerr 		goto out_config_fini;
9082834c5b1SAndrew Jeffery 	}
909d831f960SJeremy Kerr 
910f9c8f6caSCheng C Yang 	dbus_init(console, config);
911f9c8f6caSCheng C Yang 
912d47963e5SJeremy Kerr 	handlers_init(console, config);
913d831f960SJeremy Kerr 
9141a0e03b4SJeremy Kerr 	rc = run_console(console);
915d831f960SJeremy Kerr 
9161a0e03b4SJeremy Kerr 	handlers_fini(console);
917d831f960SJeremy Kerr 
918d66195c1SJeremy Kerr out_config_fini:
919d66195c1SJeremy Kerr 	config_fini(config);
920d66195c1SJeremy Kerr 
921957818b4SJeremy Kerr out_free:
92289ea8198SJeremy Kerr 	free(console->pollers);
92389ea8198SJeremy Kerr 	free(console->pollfds);
9241a0e03b4SJeremy Kerr 	free(console->tty_sysfs_devnode);
9251a0e03b4SJeremy Kerr 	free(console->tty_dev);
9261a0e03b4SJeremy Kerr 	free(console);
927d831f960SJeremy Kerr 
928d831f960SJeremy Kerr 	return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
929d831f960SJeremy Kerr }
930