xref: /openbmc/obmc-console/console-server.c (revision 954be0fbc4acdd0322041c4c86fa08fd33ded594)
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>
37b14ca19cSNinad Palsule #include <sys/socket.h>
3887e344cdSJoel Stanley #include <poll.h>
39d831f960SJeremy Kerr 
401a0e03b4SJeremy Kerr #include "console-server.h"
41d831f960SJeremy Kerr 
4230ea6385SAndrew Jeffery #define DEV_PTS_PATH "/dev/pts"
4330ea6385SAndrew Jeffery 
44f733c85aSJeremy Kerr /* size of the shared backlog ringbuffer */
455db8c792SAndrew Jeffery const size_t buffer_size = 128ul * 1024ul;
46f733c85aSJeremy Kerr 
47769cee1aSJeremy Kerr /* state shared with the signal handler */
48769cee1aSJeremy Kerr static bool sigint;
49329a35f5SJeremy Kerr 
50d831f960SJeremy Kerr static void usage(const char *progname)
51d831f960SJeremy Kerr {
52d831f960SJeremy Kerr 	fprintf(stderr,
536221ce94SVishwanatha Subbanna 		"usage: %s [options] <DEVICE>\n"
54d831f960SJeremy Kerr 		"\n"
55d831f960SJeremy Kerr 		"Options:\n"
56*954be0fbSAndrew Jeffery 		"  --config <FILE>\tUse FILE for configuration\n"
57*954be0fbSAndrew Jeffery 		"  --console-id <NAME>\tUse NAME in the UNIX domain socket address\n"
58d831f960SJeremy Kerr 		"",
59d831f960SJeremy Kerr 		progname);
60d831f960SJeremy Kerr }
61d831f960SJeremy Kerr 
6230ea6385SAndrew Jeffery /* populates console->tty.dev and console->tty.sysfs_devnode, using the tty kernel name */
631a0e03b4SJeremy Kerr static int tty_find_device(struct console *console)
6417217845SJeremy Kerr {
65d3cb9c22SAndrew Jeffery 	char *tty_class_device_link = NULL;
66d3cb9c22SAndrew Jeffery 	char *tty_path_input_real = NULL;
67d3cb9c22SAndrew Jeffery 	char *tty_device_tty_dir = NULL;
6830ea6385SAndrew Jeffery 	char *tty_vuart_lpc_addr = NULL;
69d3cb9c22SAndrew Jeffery 	char *tty_device_reldir = NULL;
7030ea6385SAndrew Jeffery 	char *tty_sysfs_devnode = NULL;
71d3cb9c22SAndrew Jeffery 	char *tty_kname_real = NULL;
7230ea6385SAndrew Jeffery 	char *tty_path_input = NULL;
7317217845SJeremy Kerr 	int rc;
7417217845SJeremy Kerr 
7530ea6385SAndrew Jeffery 	console->tty.type = TTY_DEVICE_UNDEFINED;
7630ea6385SAndrew Jeffery 
7730ea6385SAndrew Jeffery 	assert(console->tty.kname);
7830ea6385SAndrew Jeffery 	if (!strlen(console->tty.kname)) {
7930ea6385SAndrew Jeffery 		warnx("TTY kname must not be empty");
8030ea6385SAndrew Jeffery 		rc = -1;
8130ea6385SAndrew Jeffery 		goto out_free;
822834c5b1SAndrew Jeffery 	}
8317217845SJeremy Kerr 
8430ea6385SAndrew Jeffery 	if (console->tty.kname[0] == '/') {
8530ea6385SAndrew Jeffery 		tty_path_input = strdup(console->tty.kname);
8630ea6385SAndrew Jeffery 		if (!tty_path_input) {
8730ea6385SAndrew Jeffery 			rc = -1;
8830ea6385SAndrew Jeffery 			goto out_free;
8930ea6385SAndrew Jeffery 		}
9030ea6385SAndrew Jeffery 	} else {
9130ea6385SAndrew Jeffery 		rc = asprintf(&tty_path_input, "/dev/%s", console->tty.kname);
9230ea6385SAndrew Jeffery 		if (rc < 0) {
9330ea6385SAndrew Jeffery 			goto out_free;
9430ea6385SAndrew Jeffery 		}
9530ea6385SAndrew Jeffery 	}
9630ea6385SAndrew Jeffery 
9730ea6385SAndrew Jeffery 	/* udev may rename the tty name with a symbol link, try to resolve */
9845ad7676SYi Li 	tty_path_input_real = realpath(tty_path_input, NULL);
9945ad7676SYi Li 	if (!tty_path_input_real) {
10030ea6385SAndrew Jeffery 		warn("Can't find realpath for %s", tty_path_input);
10115792aa7SAndrew Jeffery 		rc = -1;
10245ad7676SYi Li 		goto out_free;
10345ad7676SYi Li 	}
10445ad7676SYi Li 
10530ea6385SAndrew Jeffery 	/*
10630ea6385SAndrew Jeffery 	 * Allow hooking obmc-console-server up to PTYs for testing
10730ea6385SAndrew Jeffery 	 *
10830ea6385SAndrew Jeffery 	 * https://amboar.github.io/notes/2023/05/02/testing-obmc-console-with-socat.html
10930ea6385SAndrew Jeffery 	 */
11030ea6385SAndrew Jeffery 	if (!strncmp(DEV_PTS_PATH, tty_path_input_real, strlen(DEV_PTS_PATH))) {
11130ea6385SAndrew Jeffery 		console->tty.type = TTY_DEVICE_PTY;
11230ea6385SAndrew Jeffery 		console->tty.dev = strdup(console->tty.kname);
11330ea6385SAndrew Jeffery 		rc = console->tty.dev ? 0 : -1;
11430ea6385SAndrew Jeffery 		goto out_free;
11530ea6385SAndrew Jeffery 	}
11630ea6385SAndrew Jeffery 
11745ad7676SYi Li 	tty_kname_real = basename(tty_path_input_real);
11845ad7676SYi Li 	if (!tty_kname_real) {
11930ea6385SAndrew Jeffery 		warn("Can't find real name for %s", console->tty.kname);
12015792aa7SAndrew Jeffery 		rc = -1;
12145ad7676SYi Li 		goto out_free;
12245ad7676SYi Li 	}
12345ad7676SYi Li 
124a72711afSAndrew Jeffery 	rc = asprintf(&tty_class_device_link, "/sys/class/tty/%s",
125a72711afSAndrew Jeffery 		      tty_kname_real);
1262834c5b1SAndrew Jeffery 	if (rc < 0) {
12745ad7676SYi Li 		goto out_free;
1282834c5b1SAndrew Jeffery 	}
12945ad7676SYi Li 
13017217845SJeremy Kerr 	tty_device_tty_dir = realpath(tty_class_device_link, NULL);
13145ad7676SYi Li 	if (!tty_device_tty_dir) {
13245ad7676SYi Li 		warn("Can't query sysfs for device %s", tty_kname_real);
13315792aa7SAndrew Jeffery 		rc = -1;
13417217845SJeremy Kerr 		goto out_free;
13517217845SJeremy Kerr 	}
13617217845SJeremy Kerr 
13717217845SJeremy Kerr 	rc = asprintf(&tty_device_reldir, "%s/../../", tty_device_tty_dir);
1382834c5b1SAndrew Jeffery 	if (rc < 0) {
13917217845SJeremy Kerr 		goto out_free;
1402834c5b1SAndrew Jeffery 	}
14117217845SJeremy Kerr 
14230ea6385SAndrew Jeffery 	tty_sysfs_devnode = realpath(tty_device_reldir, NULL);
14330ea6385SAndrew Jeffery 	if (!tty_sysfs_devnode) {
14445ad7676SYi Li 		warn("Can't find parent device for %s", tty_kname_real);
1452834c5b1SAndrew Jeffery 	}
14617217845SJeremy Kerr 
14730ea6385SAndrew Jeffery 	rc = asprintf(&console->tty.dev, "/dev/%s", tty_kname_real);
1482834c5b1SAndrew Jeffery 	if (rc < 0) {
14917217845SJeremy Kerr 		goto out_free;
1502834c5b1SAndrew Jeffery 	}
15117217845SJeremy Kerr 
15230ea6385SAndrew Jeffery 	/* Arbitrarily pick an attribute to differentiate UART vs VUART */
15330ea6385SAndrew Jeffery 	rc = asprintf(&tty_vuart_lpc_addr, "%s/lpc_addr", tty_sysfs_devnode);
15430ea6385SAndrew Jeffery 	if (rc < 0) {
15530ea6385SAndrew Jeffery 		goto out_free;
15630ea6385SAndrew Jeffery 	}
15730ea6385SAndrew Jeffery 
15830ea6385SAndrew Jeffery 	rc = access(tty_vuart_lpc_addr, F_OK);
15930ea6385SAndrew Jeffery 	console->tty.type = (!rc) ? TTY_DEVICE_VUART : TTY_DEVICE_UART;
16030ea6385SAndrew Jeffery 
16117217845SJeremy Kerr 	rc = 0;
16217217845SJeremy Kerr 
16317217845SJeremy Kerr out_free:
16430ea6385SAndrew Jeffery 	free(tty_vuart_lpc_addr);
16517217845SJeremy Kerr 	free(tty_class_device_link);
16617217845SJeremy Kerr 	free(tty_device_tty_dir);
16717217845SJeremy Kerr 	free(tty_device_reldir);
16845ad7676SYi Li 	free(tty_path_input);
16945ad7676SYi Li 	free(tty_path_input_real);
17017217845SJeremy Kerr 	return rc;
17117217845SJeremy Kerr }
17217217845SJeremy Kerr 
1731a0e03b4SJeremy Kerr static int tty_set_sysfs_attr(struct console *console, const char *name,
174957818b4SJeremy Kerr 			      int value)
175957818b4SJeremy Kerr {
176957818b4SJeremy Kerr 	char *path;
177957818b4SJeremy Kerr 	FILE *fp;
178957818b4SJeremy Kerr 	int rc;
179957818b4SJeremy Kerr 
18030ea6385SAndrew Jeffery 	assert(console->tty.type == TTY_DEVICE_VUART);
18130ea6385SAndrew Jeffery 
18230ea6385SAndrew Jeffery 	if (!console->tty.vuart.sysfs_devnode) {
18330ea6385SAndrew Jeffery 		return -1;
18430ea6385SAndrew Jeffery 	}
18530ea6385SAndrew Jeffery 
18630ea6385SAndrew Jeffery 	rc = asprintf(&path, "%s/%s", console->tty.vuart.sysfs_devnode, name);
1872834c5b1SAndrew Jeffery 	if (rc < 0) {
188957818b4SJeremy Kerr 		return -1;
1892834c5b1SAndrew Jeffery 	}
190957818b4SJeremy Kerr 
191957818b4SJeremy Kerr 	fp = fopen(path, "w");
192957818b4SJeremy Kerr 	if (!fp) {
193a72711afSAndrew Jeffery 		warn("Can't access attribute %s on device %s", name,
19430ea6385SAndrew Jeffery 		     console->tty.kname);
195957818b4SJeremy Kerr 		rc = -1;
196957818b4SJeremy Kerr 		goto out_free;
197957818b4SJeremy Kerr 	}
198957818b4SJeremy Kerr 	setvbuf(fp, NULL, _IONBF, 0);
199957818b4SJeremy Kerr 
200957818b4SJeremy Kerr 	rc = fprintf(fp, "0x%x", value);
2012834c5b1SAndrew Jeffery 	if (rc < 0) {
202a72711afSAndrew Jeffery 		warn("Error writing to %s attribute of device %s", name,
20330ea6385SAndrew Jeffery 		     console->tty.kname);
2042834c5b1SAndrew Jeffery 	}
205957818b4SJeremy Kerr 	fclose(fp);
206957818b4SJeremy Kerr 
207957818b4SJeremy Kerr out_free:
208957818b4SJeremy Kerr 	free(path);
209957818b4SJeremy Kerr 	return rc;
210957818b4SJeremy Kerr }
211957818b4SJeremy Kerr 
212d831f960SJeremy Kerr /**
213c7fbcd48SBenjamin Fair  * Set termios attributes on the console tty.
21454e9569dSJeremy Kerr  */
215e258e51fSNinad Palsule void tty_init_termios(struct console *console)
21654e9569dSJeremy Kerr {
21754e9569dSJeremy Kerr 	struct termios termios;
21854e9569dSJeremy Kerr 	int rc;
21954e9569dSJeremy Kerr 
22030ea6385SAndrew Jeffery 	rc = tcgetattr(console->tty.fd, &termios);
22154e9569dSJeremy Kerr 	if (rc) {
22254e9569dSJeremy Kerr 		warn("Can't read tty termios");
22354e9569dSJeremy Kerr 		return;
22454e9569dSJeremy Kerr 	}
22554e9569dSJeremy Kerr 
22630ea6385SAndrew Jeffery 	if (console->tty.type == TTY_DEVICE_UART && console->tty.uart.baud) {
22730ea6385SAndrew Jeffery 		if (cfsetspeed(&termios, console->tty.uart.baud) < 0) {
22830ea6385SAndrew Jeffery 			warn("Couldn't set speeds for %s", console->tty.kname);
229c7fbcd48SBenjamin Fair 		}
2302834c5b1SAndrew Jeffery 	}
231c7fbcd48SBenjamin Fair 
232c7fbcd48SBenjamin Fair 	/* Set console to raw mode: we don't want any processing to occur on
233c7fbcd48SBenjamin Fair 	 * the underlying terminal input/output.
234c7fbcd48SBenjamin Fair 	 */
23554e9569dSJeremy Kerr 	cfmakeraw(&termios);
236c7fbcd48SBenjamin Fair 
23730ea6385SAndrew Jeffery 	rc = tcsetattr(console->tty.fd, TCSANOW, &termios);
2382834c5b1SAndrew Jeffery 	if (rc) {
23930ea6385SAndrew Jeffery 		warn("Can't set terminal options for %s", console->tty.kname);
24054e9569dSJeremy Kerr 	}
2412834c5b1SAndrew Jeffery }
24254e9569dSJeremy Kerr 
24354e9569dSJeremy Kerr /**
244d831f960SJeremy Kerr  * Open and initialise the serial device
245d831f960SJeremy Kerr  */
24630ea6385SAndrew Jeffery static void tty_init_vuart_io(struct console *console)
247d831f960SJeremy Kerr {
24830ea6385SAndrew Jeffery 	assert(console->tty.type == TTY_DEVICE_VUART);
24930ea6385SAndrew Jeffery 
25030ea6385SAndrew Jeffery 	if (console->tty.vuart.sirq) {
25130ea6385SAndrew Jeffery 		tty_set_sysfs_attr(console, "sirq", console->tty.vuart.sirq);
2522834c5b1SAndrew Jeffery 	}
253957818b4SJeremy Kerr 
25430ea6385SAndrew Jeffery 	if (console->tty.vuart.lpc_addr) {
25530ea6385SAndrew Jeffery 		tty_set_sysfs_attr(console, "lpc_address",
25630ea6385SAndrew Jeffery 				   console->tty.vuart.lpc_addr);
25730ea6385SAndrew Jeffery 	}
25830ea6385SAndrew Jeffery }
25930ea6385SAndrew Jeffery 
26030ea6385SAndrew Jeffery static int tty_init_io(struct console *console)
26130ea6385SAndrew Jeffery {
26230ea6385SAndrew Jeffery 	console->tty.fd = open(console->tty.dev, O_RDWR);
26330ea6385SAndrew Jeffery 	if (console->tty.fd <= 0) {
26430ea6385SAndrew Jeffery 		warn("Can't open tty %s", console->tty.dev);
265d831f960SJeremy Kerr 		return -1;
266d831f960SJeremy Kerr 	}
267d831f960SJeremy Kerr 
268d831f960SJeremy Kerr 	/* Disable character delay. We may want to later enable this when
269d831f960SJeremy Kerr 	 * we detect larger amounts of data
270d831f960SJeremy Kerr 	 */
27130ea6385SAndrew Jeffery 	fcntl(console->tty.fd, F_SETFL, FNDELAY);
272d831f960SJeremy Kerr 
27354e9569dSJeremy Kerr 	tty_init_termios(console);
27454e9569dSJeremy Kerr 
27530ea6385SAndrew Jeffery 	console->pollfds[console->n_pollers].fd = console->tty.fd;
276329a35f5SJeremy Kerr 	console->pollfds[console->n_pollers].events = POLLIN;
277329a35f5SJeremy Kerr 
278d831f960SJeremy Kerr 	return 0;
279d831f960SJeremy Kerr }
280d831f960SJeremy Kerr 
28130ea6385SAndrew Jeffery static int tty_init_vuart(struct console *console, struct config *config)
282d66195c1SJeremy Kerr {
283fd883a88SAndrew Jeffery 	unsigned long parsed;
284d66195c1SJeremy Kerr 	const char *val;
285d66195c1SJeremy Kerr 	char *endp;
28630ea6385SAndrew Jeffery 
28730ea6385SAndrew Jeffery 	assert(console->tty.type == TTY_DEVICE_VUART);
288d66195c1SJeremy Kerr 
289d66195c1SJeremy Kerr 	val = config_get_value(config, "lpc-address");
290d66195c1SJeremy Kerr 	if (val) {
291fd883a88SAndrew Jeffery 		errno = 0;
292fd883a88SAndrew Jeffery 		parsed = strtoul(val, &endp, 0);
293fd883a88SAndrew Jeffery 		if (parsed == ULONG_MAX && errno == ERANGE) {
294fd883a88SAndrew Jeffery 			warn("Cannot interpret 'lpc-address' value as an unsigned long: '%s'",
295fd883a88SAndrew Jeffery 			     val);
296fd883a88SAndrew Jeffery 			return -1;
297fd883a88SAndrew Jeffery 		}
298fd883a88SAndrew Jeffery 
299fd883a88SAndrew Jeffery 		if (parsed > UINT16_MAX) {
300fd883a88SAndrew Jeffery 			warn("Invalid LPC address '%s'", val);
301fd883a88SAndrew Jeffery 			return -1;
302fd883a88SAndrew Jeffery 		}
303fd883a88SAndrew Jeffery 
30430ea6385SAndrew Jeffery 		console->tty.vuart.lpc_addr = (uint16_t)parsed;
305d66195c1SJeremy Kerr 		if (endp == optarg) {
306d66195c1SJeremy Kerr 			warn("Invalid LPC address: '%s'", val);
307d66195c1SJeremy Kerr 			return -1;
308d66195c1SJeremy Kerr 		}
309d66195c1SJeremy Kerr 	}
310d66195c1SJeremy Kerr 
311d66195c1SJeremy Kerr 	val = config_get_value(config, "sirq");
312d66195c1SJeremy Kerr 	if (val) {
313fd883a88SAndrew Jeffery 		errno = 0;
314fd883a88SAndrew Jeffery 		parsed = strtoul(val, &endp, 0);
315fd883a88SAndrew Jeffery 		if (parsed == ULONG_MAX && errno == ERANGE) {
316fd883a88SAndrew Jeffery 			warn("Cannot interpret 'sirq' value as an unsigned long: '%s'",
317fd883a88SAndrew Jeffery 			     val);
318fd883a88SAndrew Jeffery 		}
319fd883a88SAndrew Jeffery 
3202834c5b1SAndrew Jeffery 		if (parsed > 16) {
321fd883a88SAndrew Jeffery 			warn("Invalid LPC SERIRQ: '%s'", val);
3222834c5b1SAndrew Jeffery 		}
323fd883a88SAndrew Jeffery 
32430ea6385SAndrew Jeffery 		console->tty.vuart.sirq = (int)parsed;
3252834c5b1SAndrew Jeffery 		if (endp == optarg) {
326d66195c1SJeremy Kerr 			warn("Invalid sirq: '%s'", val);
327d66195c1SJeremy Kerr 		}
3282834c5b1SAndrew Jeffery 	}
329d66195c1SJeremy Kerr 
33030ea6385SAndrew Jeffery 	return 0;
3312834c5b1SAndrew Jeffery }
332c7fbcd48SBenjamin Fair 
33330ea6385SAndrew Jeffery static int tty_init(struct console *console, struct config *config,
33430ea6385SAndrew Jeffery 		    const char *tty_arg)
33530ea6385SAndrew Jeffery {
33630ea6385SAndrew Jeffery 	const char *val;
33730ea6385SAndrew Jeffery 	int rc;
33830ea6385SAndrew Jeffery 
339d769eecfSAndrew Jeffery 	if (tty_arg) {
34030ea6385SAndrew Jeffery 		console->tty.kname = tty_arg;
341d769eecfSAndrew Jeffery 	} else if ((val = config_get_value(config, "upstream-tty"))) {
34230ea6385SAndrew Jeffery 		console->tty.kname = val;
343d769eecfSAndrew Jeffery 	} else {
344d66195c1SJeremy Kerr 		warnx("Error: No TTY device specified");
345d66195c1SJeremy Kerr 		return -1;
346d66195c1SJeremy Kerr 	}
347d66195c1SJeremy Kerr 
348d66195c1SJeremy Kerr 	rc = tty_find_device(console);
3492834c5b1SAndrew Jeffery 	if (rc) {
350d66195c1SJeremy Kerr 		return rc;
3512834c5b1SAndrew Jeffery 	}
352d66195c1SJeremy Kerr 
35330ea6385SAndrew Jeffery 	switch (console->tty.type) {
35430ea6385SAndrew Jeffery 	case TTY_DEVICE_VUART:
35530ea6385SAndrew Jeffery 		rc = tty_init_vuart(console, config);
35630ea6385SAndrew Jeffery 		if (rc) {
357d66195c1SJeremy Kerr 			return rc;
358d66195c1SJeremy Kerr 		}
359d66195c1SJeremy Kerr 
36030ea6385SAndrew Jeffery 		tty_init_vuart_io(console);
36130ea6385SAndrew Jeffery 		break;
36230ea6385SAndrew Jeffery 	case TTY_DEVICE_UART:
36330ea6385SAndrew Jeffery 		val = config_get_value(config, "baud");
36430ea6385SAndrew Jeffery 		if (val) {
36530ea6385SAndrew Jeffery 			if (config_parse_baud(&console->tty.uart.baud, val)) {
36630ea6385SAndrew Jeffery 				warnx("Invalid baud rate: '%s'", val);
36730ea6385SAndrew Jeffery 			}
36830ea6385SAndrew Jeffery 		}
36930ea6385SAndrew Jeffery 		break;
37030ea6385SAndrew Jeffery 	case TTY_DEVICE_PTY:
37130ea6385SAndrew Jeffery 		break;
37230ea6385SAndrew Jeffery 	case TTY_DEVICE_UNDEFINED:
37330ea6385SAndrew Jeffery 	default:
37430ea6385SAndrew Jeffery 		warnx("Cannot configure unrecognised TTY device");
37530ea6385SAndrew Jeffery 		return -1;
37630ea6385SAndrew Jeffery 	}
37730ea6385SAndrew Jeffery 
37830ea6385SAndrew Jeffery 	return tty_init_io(console);
37930ea6385SAndrew Jeffery }
38030ea6385SAndrew Jeffery 
38130ea6385SAndrew Jeffery static void tty_fini(struct console *console)
38230ea6385SAndrew Jeffery {
38330ea6385SAndrew Jeffery 	if (console->tty.type == TTY_DEVICE_VUART) {
38430ea6385SAndrew Jeffery 		free(console->tty.vuart.sysfs_devnode);
38530ea6385SAndrew Jeffery 	}
38630ea6385SAndrew Jeffery 	free(console->tty.dev);
38730ea6385SAndrew Jeffery }
38830ea6385SAndrew Jeffery 
3891a0e03b4SJeremy Kerr int console_data_out(struct console *console, const uint8_t *data, size_t len)
390d831f960SJeremy Kerr {
39130ea6385SAndrew Jeffery 	return write_buf_to_fd(console->tty.fd, data, len);
392d831f960SJeremy Kerr }
393d831f960SJeremy Kerr 
394b14ca19cSNinad Palsule /* Read console if from config and prepare a socket name */
395*954be0fbSAndrew Jeffery static int set_socket_info(struct console *console, struct config *config,
396*954be0fbSAndrew Jeffery 			   const char *console_id)
397b14ca19cSNinad Palsule {
398*954be0fbSAndrew Jeffery 	const char *resolved_id;
399b14ca19cSNinad Palsule 	ssize_t len;
400b14ca19cSNinad Palsule 
401*954be0fbSAndrew Jeffery 	if (console_id) {
402*954be0fbSAndrew Jeffery 		resolved_id = console_id;
403*954be0fbSAndrew Jeffery 	} else {
404*954be0fbSAndrew Jeffery 		resolved_id = config_get_value(config, "console-id");
4059a8f30ecSAndrew Jeffery 
4069a8f30ecSAndrew Jeffery 		/* socket-id is deprecated */
407*954be0fbSAndrew Jeffery 		if (!resolved_id) {
408*954be0fbSAndrew Jeffery 			resolved_id = config_get_value(config, "socket-id");
409*954be0fbSAndrew Jeffery 		}
4109a8f30ecSAndrew Jeffery 	}
4119a8f30ecSAndrew Jeffery 
412*954be0fbSAndrew Jeffery 	if (!resolved_id) {
413*954be0fbSAndrew Jeffery 		warnx("console-id was not specified");
414b14ca19cSNinad Palsule 		return EXIT_FAILURE;
415b14ca19cSNinad Palsule 	}
416b14ca19cSNinad Palsule 
417*954be0fbSAndrew Jeffery 	console->console_id = resolved_id;
418*954be0fbSAndrew Jeffery 
419b14ca19cSNinad Palsule 	/* Get the socket name/path */
420*954be0fbSAndrew Jeffery 	len = console_socket_path(console->socket_name, resolved_id);
421b14ca19cSNinad Palsule 	if (len < 0) {
422b14ca19cSNinad Palsule 		warn("Failed to set socket path: %s", strerror(errno));
423b14ca19cSNinad Palsule 		return EXIT_FAILURE;
424b14ca19cSNinad Palsule 	}
425b14ca19cSNinad Palsule 
426b14ca19cSNinad Palsule 	/* Socket name is not a null terminated string hence save the length */
427b14ca19cSNinad Palsule 	console->socket_name_len = len;
428b14ca19cSNinad Palsule 
429b14ca19cSNinad Palsule 	return 0;
430b14ca19cSNinad Palsule }
431b14ca19cSNinad Palsule 
432d47963e5SJeremy Kerr static void handlers_init(struct console *console, struct config *config)
433d831f960SJeremy Kerr {
434b70f8713SAndrew Jeffery 	/* NOLINTBEGIN(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) */
435b70f8713SAndrew Jeffery 	extern struct handler *__start_handlers;
436b70f8713SAndrew Jeffery 	extern struct handler *__stop_handlers;
437b70f8713SAndrew Jeffery 	/* NOLINTEND(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) */
4381a0e03b4SJeremy Kerr 	struct handler *handler;
439b70f8713SAndrew Jeffery 	int i;
440b70f8713SAndrew Jeffery 	int rc;
441d831f960SJeremy Kerr 
4421a0e03b4SJeremy Kerr 	console->n_handlers = &__stop_handlers - &__start_handlers;
4431a0e03b4SJeremy Kerr 	console->handlers = &__start_handlers;
444d831f960SJeremy Kerr 
4455c359cc6SAndrew Jeffery 	printf("%ld handler%s\n", console->n_handlers,
4461a0e03b4SJeremy Kerr 	       console->n_handlers == 1 ? "" : "s");
447d831f960SJeremy Kerr 
4481a0e03b4SJeremy Kerr 	for (i = 0; i < console->n_handlers; i++) {
4491a0e03b4SJeremy Kerr 		handler = console->handlers[i];
4501a0e03b4SJeremy Kerr 
451021b91f0SJeremy Kerr 		rc = 0;
4522834c5b1SAndrew Jeffery 		if (handler->init) {
453021b91f0SJeremy Kerr 			rc = handler->init(handler, console, config);
4542834c5b1SAndrew Jeffery 		}
455021b91f0SJeremy Kerr 
456021b91f0SJeremy Kerr 		handler->active = rc == 0;
457021b91f0SJeremy Kerr 
458021b91f0SJeremy Kerr 		printf("  %s [%sactive]\n", handler->name,
459021b91f0SJeremy Kerr 		       handler->active ? "" : "in");
460d831f960SJeremy Kerr 	}
461d831f960SJeremy Kerr }
462d831f960SJeremy Kerr 
4631a0e03b4SJeremy Kerr static void handlers_fini(struct console *console)
464d831f960SJeremy Kerr {
4651a0e03b4SJeremy Kerr 	struct handler *handler;
4661a0e03b4SJeremy Kerr 	int i;
4671a0e03b4SJeremy Kerr 
4681a0e03b4SJeremy Kerr 	for (i = 0; i < console->n_handlers; i++) {
4691a0e03b4SJeremy Kerr 		handler = console->handlers[i];
4702834c5b1SAndrew Jeffery 		if (handler->fini && handler->active) {
4711a0e03b4SJeremy Kerr 			handler->fini(handler);
4721a0e03b4SJeremy Kerr 		}
473d831f960SJeremy Kerr 	}
4742834c5b1SAndrew Jeffery }
475d831f960SJeremy Kerr 
4761cecc5deSJohnathan Mantey static int get_current_time(struct timeval *tv)
4771cecc5deSJohnathan Mantey {
4781cecc5deSJohnathan Mantey 	struct timespec t;
4791cecc5deSJohnathan Mantey 	int rc;
4801cecc5deSJohnathan Mantey 
4811cecc5deSJohnathan Mantey 	/*
4821cecc5deSJohnathan Mantey 	 * We use clock_gettime(CLOCK_MONOTONIC) so we're immune to
4831cecc5deSJohnathan Mantey 	 * local time changes. However, a struct timeval is more
4841cecc5deSJohnathan Mantey 	 * convenient for calculations, so convert to that.
4851cecc5deSJohnathan Mantey 	 */
4861cecc5deSJohnathan Mantey 	rc = clock_gettime(CLOCK_MONOTONIC, &t);
4872834c5b1SAndrew Jeffery 	if (rc) {
4881cecc5deSJohnathan Mantey 		return rc;
4892834c5b1SAndrew Jeffery 	}
4901cecc5deSJohnathan Mantey 
4911cecc5deSJohnathan Mantey 	tv->tv_sec = t.tv_sec;
4921cecc5deSJohnathan Mantey 	tv->tv_usec = t.tv_nsec / 1000;
4931cecc5deSJohnathan Mantey 
4941cecc5deSJohnathan Mantey 	return 0;
4951cecc5deSJohnathan Mantey }
4961cecc5deSJohnathan Mantey 
497a72711afSAndrew Jeffery struct ringbuffer_consumer *
498a72711afSAndrew Jeffery console_ringbuffer_consumer_register(struct console *console,
499f733c85aSJeremy Kerr 				     ringbuffer_poll_fn_t poll_fn, void *data)
500d831f960SJeremy Kerr {
501f733c85aSJeremy Kerr 	return ringbuffer_consumer_register(console->rb, poll_fn, data);
502d831f960SJeremy Kerr }
503d831f960SJeremy Kerr 
50455c9712dSJeremy Kerr struct poller *console_poller_register(struct console *console,
505a72711afSAndrew Jeffery 				       struct handler *handler,
506a72711afSAndrew Jeffery 				       poller_event_fn_t poller_fn,
5071cecc5deSJohnathan Mantey 				       poller_timeout_fn_t timeout_fn, int fd,
5081cecc5deSJohnathan Mantey 				       int events, void *data)
509d831f960SJeremy Kerr {
510329a35f5SJeremy Kerr 	struct poller *poller;
5115c359cc6SAndrew Jeffery 	long n;
512329a35f5SJeremy Kerr 
513329a35f5SJeremy Kerr 	poller = malloc(sizeof(*poller));
514329a35f5SJeremy Kerr 	poller->remove = false;
515329a35f5SJeremy Kerr 	poller->handler = handler;
5161cecc5deSJohnathan Mantey 	poller->event_fn = poller_fn;
5171cecc5deSJohnathan Mantey 	poller->timeout_fn = timeout_fn;
518329a35f5SJeremy Kerr 	poller->data = data;
519329a35f5SJeremy Kerr 
520329a35f5SJeremy Kerr 	/* add one to our pollers array */
521329a35f5SJeremy Kerr 	n = console->n_pollers++;
52291b52175SAndrew Jeffery 	/*
52391b52175SAndrew Jeffery 	 * We're managing an array of pointers to aggregates, so don't warn about sizeof() on a
52491b52175SAndrew Jeffery 	 * pointer type.
52591b52175SAndrew Jeffery 	 */
52691b52175SAndrew Jeffery 	/* NOLINTBEGIN(bugprone-sizeof-expression) */
52791b52175SAndrew Jeffery 	console->pollers = reallocarray(console->pollers, console->n_pollers,
52891b52175SAndrew Jeffery 					sizeof(*console->pollers));
52991b52175SAndrew Jeffery 	/* NOLINTEND(bugprone-sizeof-expression) */
530329a35f5SJeremy Kerr 
531329a35f5SJeremy Kerr 	console->pollers[n] = poller;
532329a35f5SJeremy Kerr 
533329a35f5SJeremy Kerr 	/* increase pollfds array too  */
534a72711afSAndrew Jeffery 	console->pollfds =
53591b52175SAndrew Jeffery 		reallocarray(console->pollfds,
53691b52175SAndrew Jeffery 			     (MAX_INTERNAL_POLLFD + console->n_pollers),
53791b52175SAndrew Jeffery 			     sizeof(*console->pollfds));
538329a35f5SJeremy Kerr 
539329a35f5SJeremy Kerr 	/* shift the end pollfds up by one */
540a72711afSAndrew Jeffery 	memcpy(&console->pollfds[n + 1], &console->pollfds[n],
541f9c8f6caSCheng C Yang 	       sizeof(*console->pollfds) * MAX_INTERNAL_POLLFD);
542329a35f5SJeremy Kerr 
543329a35f5SJeremy Kerr 	console->pollfds[n].fd = fd;
5445c359cc6SAndrew Jeffery 	console->pollfds[n].events = (short)(events & 0x7fff);
545329a35f5SJeremy Kerr 
546329a35f5SJeremy Kerr 	return poller;
547329a35f5SJeremy Kerr }
548329a35f5SJeremy Kerr 
549a72711afSAndrew Jeffery void console_poller_unregister(struct console *console, struct poller *poller)
550329a35f5SJeremy Kerr {
551329a35f5SJeremy Kerr 	int i;
552329a35f5SJeremy Kerr 
553329a35f5SJeremy Kerr 	/* find the entry in our pollers array */
5542834c5b1SAndrew Jeffery 	for (i = 0; i < console->n_pollers; i++) {
5552834c5b1SAndrew Jeffery 		if (console->pollers[i] == poller) {
556329a35f5SJeremy Kerr 			break;
5572834c5b1SAndrew Jeffery 		}
5582834c5b1SAndrew Jeffery 	}
559329a35f5SJeremy Kerr 
560329a35f5SJeremy Kerr 	assert(i < console->n_pollers);
561329a35f5SJeremy Kerr 
562329a35f5SJeremy Kerr 	console->n_pollers--;
563329a35f5SJeremy Kerr 
56491b52175SAndrew Jeffery 	/*
56591b52175SAndrew Jeffery 	 * Remove the item from the pollers array...
56691b52175SAndrew Jeffery 	 *
56791b52175SAndrew Jeffery 	 * We're managing an array of pointers to aggregates, so don't warn about sizeof() on a
56891b52175SAndrew Jeffery 	 * pointer type.
56991b52175SAndrew Jeffery 	 */
57091b52175SAndrew Jeffery 	/* NOLINTBEGIN(bugprone-sizeof-expression) */
571329a35f5SJeremy Kerr 	memmove(&console->pollers[i], &console->pollers[i + 1],
572a72711afSAndrew Jeffery 		sizeof(*console->pollers) * (console->n_pollers - i));
573329a35f5SJeremy Kerr 
57491b52175SAndrew Jeffery 	console->pollers = reallocarray(console->pollers, console->n_pollers,
57591b52175SAndrew Jeffery 					sizeof(*console->pollers));
57691b52175SAndrew Jeffery 	/* NOLINTEND(bugprone-sizeof-expression) */
577329a35f5SJeremy Kerr 
578329a35f5SJeremy Kerr 	/* ... and the pollfds array */
579329a35f5SJeremy Kerr 	memmove(&console->pollfds[i], &console->pollfds[i + 1],
580329a35f5SJeremy Kerr 		sizeof(*console->pollfds) *
581f9c8f6caSCheng C Yang 			(MAX_INTERNAL_POLLFD + console->n_pollers - i));
582329a35f5SJeremy Kerr 
583a72711afSAndrew Jeffery 	console->pollfds =
58491b52175SAndrew Jeffery 		reallocarray(console->pollfds,
58591b52175SAndrew Jeffery 			     (MAX_INTERNAL_POLLFD + console->n_pollers),
58691b52175SAndrew Jeffery 			     sizeof(*console->pollfds));
587329a35f5SJeremy Kerr 
588329a35f5SJeremy Kerr 	free(poller);
589329a35f5SJeremy Kerr }
590329a35f5SJeremy Kerr 
5916b1fed27SJeremy Kerr void console_poller_set_events(struct console *console, struct poller *poller,
5926b1fed27SJeremy Kerr 			       int events)
5936b1fed27SJeremy Kerr {
5946b1fed27SJeremy Kerr 	int i;
5956b1fed27SJeremy Kerr 
5966b1fed27SJeremy Kerr 	/* find the entry in our pollers array */
5972834c5b1SAndrew Jeffery 	for (i = 0; i < console->n_pollers; i++) {
5982834c5b1SAndrew Jeffery 		if (console->pollers[i] == poller) {
5996b1fed27SJeremy Kerr 			break;
6002834c5b1SAndrew Jeffery 		}
6012834c5b1SAndrew Jeffery 	}
6026b1fed27SJeremy Kerr 
6035c359cc6SAndrew Jeffery 	console->pollfds[i].events = (short)(events & 0x7fff);
6046b1fed27SJeremy Kerr }
6056b1fed27SJeremy Kerr 
606fd048328SAndrew Jeffery void console_poller_set_timeout(struct console *console __attribute__((unused)),
607fd048328SAndrew Jeffery 				struct poller *poller, const struct timeval *tv)
6081cecc5deSJohnathan Mantey {
6091cecc5deSJohnathan Mantey 	struct timeval now;
6101cecc5deSJohnathan Mantey 	int rc;
6111cecc5deSJohnathan Mantey 
6121cecc5deSJohnathan Mantey 	rc = get_current_time(&now);
6132834c5b1SAndrew Jeffery 	if (rc) {
6141cecc5deSJohnathan Mantey 		return;
6152834c5b1SAndrew Jeffery 	}
6161cecc5deSJohnathan Mantey 
6171cecc5deSJohnathan Mantey 	timeradd(&now, tv, &poller->timeout);
6181cecc5deSJohnathan Mantey }
6191cecc5deSJohnathan Mantey 
6205c359cc6SAndrew Jeffery static long get_poll_timeout(struct console *console, struct timeval *cur_time)
6211cecc5deSJohnathan Mantey {
622b70f8713SAndrew Jeffery 	struct timeval *earliest;
623b70f8713SAndrew Jeffery 	struct timeval interval;
6241cecc5deSJohnathan Mantey 	struct poller *poller;
6251cecc5deSJohnathan Mantey 	int i;
6261cecc5deSJohnathan Mantey 
6271cecc5deSJohnathan Mantey 	earliest = NULL;
6281cecc5deSJohnathan Mantey 
6291cecc5deSJohnathan Mantey 	for (i = 0; i < console->n_pollers; i++) {
6301cecc5deSJohnathan Mantey 		poller = console->pollers[i];
6311cecc5deSJohnathan Mantey 
6321cecc5deSJohnathan Mantey 		if (poller->timeout_fn && timerisset(&poller->timeout) &&
6331cecc5deSJohnathan Mantey 		    (!earliest ||
6341cecc5deSJohnathan Mantey 		     (earliest && timercmp(&poller->timeout, earliest, <)))) {
6351cecc5deSJohnathan Mantey 			// poller is buffering data and needs the poll
6361cecc5deSJohnathan Mantey 			// function to timeout.
6371cecc5deSJohnathan Mantey 			earliest = &poller->timeout;
6381cecc5deSJohnathan Mantey 		}
6391cecc5deSJohnathan Mantey 	}
6401cecc5deSJohnathan Mantey 
6411cecc5deSJohnathan Mantey 	if (earliest) {
6421cecc5deSJohnathan Mantey 		if (timercmp(earliest, cur_time, >)) {
6431cecc5deSJohnathan Mantey 			/* recalculate the timeout period, time period has
6441cecc5deSJohnathan Mantey 			 * not elapsed */
6451cecc5deSJohnathan Mantey 			timersub(earliest, cur_time, &interval);
6461cecc5deSJohnathan Mantey 			return ((interval.tv_sec * 1000) +
6471cecc5deSJohnathan Mantey 				(interval.tv_usec / 1000));
6480b7b0477SAndrew Jeffery 		} /* return from poll immediately */
6491cecc5deSJohnathan Mantey 		return 0;
6500b7b0477SAndrew Jeffery 
6510b7b0477SAndrew Jeffery 	} /* poll indefinitely */
6521cecc5deSJohnathan Mantey 	return -1;
6531cecc5deSJohnathan Mantey }
6541cecc5deSJohnathan Mantey 
6551cecc5deSJohnathan Mantey static int call_pollers(struct console *console, struct timeval *cur_time)
656329a35f5SJeremy Kerr {
657329a35f5SJeremy Kerr 	struct poller *poller;
658329a35f5SJeremy Kerr 	struct pollfd *pollfd;
659329a35f5SJeremy Kerr 	enum poller_ret prc;
660b70f8713SAndrew Jeffery 	int i;
661b70f8713SAndrew Jeffery 	int rc;
662d831f960SJeremy Kerr 
6631a0e03b4SJeremy Kerr 	rc = 0;
6641a0e03b4SJeremy Kerr 
665329a35f5SJeremy Kerr 	/*
666329a35f5SJeremy Kerr 	 * Process poll events by iterating through the pollers and pollfds
667329a35f5SJeremy Kerr 	 * in-step, calling any pollers that we've found revents for.
668329a35f5SJeremy Kerr 	 */
669329a35f5SJeremy Kerr 	for (i = 0; i < console->n_pollers; i++) {
670329a35f5SJeremy Kerr 		poller = console->pollers[i];
671329a35f5SJeremy Kerr 		pollfd = &console->pollfds[i];
6721cecc5deSJohnathan Mantey 		prc = POLLER_OK;
6731a0e03b4SJeremy Kerr 
6741cecc5deSJohnathan Mantey 		/* process pending events... */
6751cecc5deSJohnathan Mantey 		if (pollfd->revents) {
6761cecc5deSJohnathan Mantey 			prc = poller->event_fn(poller->handler, pollfd->revents,
677329a35f5SJeremy Kerr 					       poller->data);
6782834c5b1SAndrew Jeffery 			if (prc == POLLER_EXIT) {
679329a35f5SJeremy Kerr 				rc = -1;
6802834c5b1SAndrew Jeffery 			} else if (prc == POLLER_REMOVE) {
681329a35f5SJeremy Kerr 				poller->remove = true;
682329a35f5SJeremy Kerr 			}
6832834c5b1SAndrew Jeffery 		}
684329a35f5SJeremy Kerr 
6851cecc5deSJohnathan Mantey 		if ((prc == POLLER_OK) && poller->timeout_fn &&
6861cecc5deSJohnathan Mantey 		    timerisset(&poller->timeout) &&
6871cecc5deSJohnathan Mantey 		    timercmp(&poller->timeout, cur_time, <=)) {
6881cecc5deSJohnathan Mantey 			/* One of the ringbuffer consumers is buffering the
6891cecc5deSJohnathan Mantey 			data stream. The amount of idle time the consumer
6901cecc5deSJohnathan Mantey 			desired has expired.  Process the buffered data for
6911cecc5deSJohnathan Mantey 			transmission. */
6921cecc5deSJohnathan Mantey 			timerclear(&poller->timeout);
6931cecc5deSJohnathan Mantey 			prc = poller->timeout_fn(poller->handler, poller->data);
6941cecc5deSJohnathan Mantey 			if (prc == POLLER_EXIT) {
6951cecc5deSJohnathan Mantey 				rc = -1;
6961cecc5deSJohnathan Mantey 			} else if (prc == POLLER_REMOVE) {
6971cecc5deSJohnathan Mantey 				poller->remove = true;
6981cecc5deSJohnathan Mantey 			}
6991cecc5deSJohnathan Mantey 		}
7001cecc5deSJohnathan Mantey 	}
7011cecc5deSJohnathan Mantey 
702329a35f5SJeremy Kerr 	/**
703329a35f5SJeremy Kerr 	 * Process deferred removals; restarting each time we unregister, as
704329a35f5SJeremy Kerr 	 * the array will have changed
705329a35f5SJeremy Kerr 	 */
706329a35f5SJeremy Kerr 	for (;;) {
707329a35f5SJeremy Kerr 		bool removed = false;
708329a35f5SJeremy Kerr 
709329a35f5SJeremy Kerr 		for (i = 0; i < console->n_pollers; i++) {
710329a35f5SJeremy Kerr 			poller = console->pollers[i];
711329a35f5SJeremy Kerr 			if (poller->remove) {
71255c9712dSJeremy Kerr 				console_poller_unregister(console, poller);
713329a35f5SJeremy Kerr 				removed = true;
714329a35f5SJeremy Kerr 				break;
715329a35f5SJeremy Kerr 			}
716329a35f5SJeremy Kerr 		}
7172834c5b1SAndrew Jeffery 		if (!removed) {
718329a35f5SJeremy Kerr 			break;
7191a0e03b4SJeremy Kerr 		}
7202834c5b1SAndrew Jeffery 	}
7211a0e03b4SJeremy Kerr 
7221a0e03b4SJeremy Kerr 	return rc;
7231a0e03b4SJeremy Kerr }
7241a0e03b4SJeremy Kerr 
725769cee1aSJeremy Kerr static void sighandler(int signal)
726769cee1aSJeremy Kerr {
7272834c5b1SAndrew Jeffery 	if (signal == SIGINT) {
728769cee1aSJeremy Kerr 		sigint = true;
729769cee1aSJeremy Kerr 	}
7302834c5b1SAndrew Jeffery }
731769cee1aSJeremy Kerr 
7321a0e03b4SJeremy Kerr int run_console(struct console *console)
7331a0e03b4SJeremy Kerr {
7345c359cc6SAndrew Jeffery 	sighandler_t sighandler_save = signal(SIGINT, sighandler);
7351cecc5deSJohnathan Mantey 	struct timeval tv;
7365c359cc6SAndrew Jeffery 	long timeout;
7375c359cc6SAndrew Jeffery 	ssize_t rc;
738769cee1aSJeremy Kerr 
739769cee1aSJeremy Kerr 	rc = 0;
740769cee1aSJeremy Kerr 
741d831f960SJeremy Kerr 	for (;;) {
742d831f960SJeremy Kerr 		uint8_t buf[4096];
743d831f960SJeremy Kerr 
7441764145dSJeremy Kerr 		BUILD_ASSERT(sizeof(buf) <= buffer_size);
7451764145dSJeremy Kerr 
746769cee1aSJeremy Kerr 		if (sigint) {
747769cee1aSJeremy Kerr 			fprintf(stderr, "Received interrupt, exiting\n");
748769cee1aSJeremy Kerr 			break;
749769cee1aSJeremy Kerr 		}
750769cee1aSJeremy Kerr 
7511cecc5deSJohnathan Mantey 		rc = get_current_time(&tv);
7521cecc5deSJohnathan Mantey 		if (rc) {
7531cecc5deSJohnathan Mantey 			warn("Failed to read current time");
7541cecc5deSJohnathan Mantey 			break;
7551cecc5deSJohnathan Mantey 		}
7561cecc5deSJohnathan Mantey 
7571cecc5deSJohnathan Mantey 		timeout = get_poll_timeout(console, &tv);
7581cecc5deSJohnathan Mantey 
759329a35f5SJeremy Kerr 		rc = poll(console->pollfds,
7605c359cc6SAndrew Jeffery 			  console->n_pollers + MAX_INTERNAL_POLLFD,
7615c359cc6SAndrew Jeffery 			  (int)timeout);
7621cecc5deSJohnathan Mantey 
763d831f960SJeremy Kerr 		if (rc < 0) {
764769cee1aSJeremy Kerr 			if (errno == EINTR) {
765769cee1aSJeremy Kerr 				continue;
7660b7b0477SAndrew Jeffery 			}
767d831f960SJeremy Kerr 			warn("poll error");
768769cee1aSJeremy Kerr 			break;
769769cee1aSJeremy Kerr 		}
770d831f960SJeremy Kerr 
771329a35f5SJeremy Kerr 		/* process internal fd first */
772329a35f5SJeremy Kerr 		if (console->pollfds[console->n_pollers].revents) {
77330ea6385SAndrew Jeffery 			rc = read(console->tty.fd, buf, sizeof(buf));
774d831f960SJeremy Kerr 			if (rc <= 0) {
775d831f960SJeremy Kerr 				warn("Error reading from tty device");
776769cee1aSJeremy Kerr 				rc = -1;
777769cee1aSJeremy Kerr 				break;
778d831f960SJeremy Kerr 			}
779f733c85aSJeremy Kerr 			rc = ringbuffer_queue(console->rb, buf, rc);
7802834c5b1SAndrew Jeffery 			if (rc) {
781769cee1aSJeremy Kerr 				break;
782d831f960SJeremy Kerr 			}
7832834c5b1SAndrew Jeffery 		}
784d831f960SJeremy Kerr 
785f9c8f6caSCheng C Yang 		if (console->pollfds[console->n_pollers + 1].revents) {
786f9c8f6caSCheng C Yang 			sd_bus_process(console->bus, NULL);
787f9c8f6caSCheng C Yang 		}
788f9c8f6caSCheng C Yang 
789329a35f5SJeremy Kerr 		/* ... and then the pollers */
7901cecc5deSJohnathan Mantey 		rc = call_pollers(console, &tv);
7912834c5b1SAndrew Jeffery 		if (rc) {
792769cee1aSJeremy Kerr 			break;
7931a0e03b4SJeremy Kerr 		}
7942834c5b1SAndrew Jeffery 	}
795769cee1aSJeremy Kerr 
796769cee1aSJeremy Kerr 	signal(SIGINT, sighandler_save);
797f9c8f6caSCheng C Yang 	sd_bus_unref(console->bus);
798769cee1aSJeremy Kerr 
799769cee1aSJeremy Kerr 	return rc ? -1 : 0;
8001a0e03b4SJeremy Kerr }
801d831f960SJeremy Kerr static const struct option options[] = {
802d66195c1SJeremy Kerr 	{ "config", required_argument, 0, 'c' },
803*954be0fbSAndrew Jeffery 	{ "console-id", required_argument, 0, 'i' },
804f5858b5bSJoel Stanley 	{ 0, 0, 0, 0 },
805d831f960SJeremy Kerr };
806d831f960SJeremy Kerr 
807d831f960SJeremy Kerr int main(int argc, char **argv)
808d831f960SJeremy Kerr {
809d66195c1SJeremy Kerr 	const char *config_filename = NULL;
8106221ce94SVishwanatha Subbanna 	const char *config_tty_kname = NULL;
811*954be0fbSAndrew Jeffery 	const char *console_id = NULL;
8121a0e03b4SJeremy Kerr 	struct console *console;
813d66195c1SJeremy Kerr 	struct config *config;
814d66195c1SJeremy Kerr 	int rc;
815d831f960SJeremy Kerr 
816d831f960SJeremy Kerr 	for (;;) {
817b70f8713SAndrew Jeffery 		int c;
818b70f8713SAndrew Jeffery 		int idx;
819d831f960SJeremy Kerr 
820*954be0fbSAndrew Jeffery 		c = getopt_long(argc, argv, "c:i:", options, &idx);
8212834c5b1SAndrew Jeffery 		if (c == -1) {
822d831f960SJeremy Kerr 			break;
8232834c5b1SAndrew Jeffery 		}
824d831f960SJeremy Kerr 
825d831f960SJeremy Kerr 		switch (c) {
826d66195c1SJeremy Kerr 		case 'c':
827d66195c1SJeremy Kerr 			config_filename = optarg;
828d831f960SJeremy Kerr 			break;
829*954be0fbSAndrew Jeffery 		case 'i':
830*954be0fbSAndrew Jeffery 			console_id = optarg;
831*954be0fbSAndrew Jeffery 			break;
832d831f960SJeremy Kerr 		case 'h':
833d831f960SJeremy Kerr 		case '?':
834d831f960SJeremy Kerr 			usage(argv[0]);
835d66195c1SJeremy Kerr 			return EXIT_SUCCESS;
836d831f960SJeremy Kerr 		}
837d831f960SJeremy Kerr 	}
838d831f960SJeremy Kerr 
8392834c5b1SAndrew Jeffery 	if (optind < argc) {
8406221ce94SVishwanatha Subbanna 		config_tty_kname = argv[optind];
8412834c5b1SAndrew Jeffery 	}
8426221ce94SVishwanatha Subbanna 
843d66195c1SJeremy Kerr 	console = malloc(sizeof(struct console));
844d66195c1SJeremy Kerr 	memset(console, 0, sizeof(*console));
845a72711afSAndrew Jeffery 	console->pollfds =
846a72711afSAndrew Jeffery 		calloc(MAX_INTERNAL_POLLFD, sizeof(*console->pollfds));
847f733c85aSJeremy Kerr 	console->rb = ringbuffer_init(buffer_size);
848329a35f5SJeremy Kerr 
849d66195c1SJeremy Kerr 	config = config_init(config_filename);
850d66195c1SJeremy Kerr 	if (!config) {
851d66195c1SJeremy Kerr 		warnx("Can't read configuration, exiting.");
85229c59c44SAndrew Jeffery 		rc = -1;
853d66195c1SJeremy Kerr 		goto out_free;
854d831f960SJeremy Kerr 	}
855d831f960SJeremy Kerr 
856*954be0fbSAndrew Jeffery 	if (set_socket_info(console, config, console_id)) {
85729c59c44SAndrew Jeffery 		rc = -1;
85829c59c44SAndrew Jeffery 		goto out_config_fini;
859b14ca19cSNinad Palsule 	}
860b14ca19cSNinad Palsule 
861d769eecfSAndrew Jeffery 	rc = tty_init(console, config, config_tty_kname);
8622834c5b1SAndrew Jeffery 	if (rc) {
863d66195c1SJeremy Kerr 		goto out_config_fini;
8642834c5b1SAndrew Jeffery 	}
865d831f960SJeremy Kerr 
866f9c8f6caSCheng C Yang 	dbus_init(console, config);
867f9c8f6caSCheng C Yang 
868d47963e5SJeremy Kerr 	handlers_init(console, config);
869d831f960SJeremy Kerr 
8701a0e03b4SJeremy Kerr 	rc = run_console(console);
871d831f960SJeremy Kerr 
8721a0e03b4SJeremy Kerr 	handlers_fini(console);
873d831f960SJeremy Kerr 
87430ea6385SAndrew Jeffery 	tty_fini(console);
87530ea6385SAndrew Jeffery 
876d66195c1SJeremy Kerr out_config_fini:
877d66195c1SJeremy Kerr 	config_fini(config);
878d66195c1SJeremy Kerr 
879957818b4SJeremy Kerr out_free:
88089ea8198SJeremy Kerr 	free(console->pollers);
88189ea8198SJeremy Kerr 	free(console->pollfds);
8821a0e03b4SJeremy Kerr 	free(console);
883d831f960SJeremy Kerr 
884d831f960SJeremy Kerr 	return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
885d831f960SJeremy Kerr }
886