xref: /openbmc/obmc-console/console-server.c (revision 7851a396511b623aca735d607382b93494c332a4)
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>
317dc08baaSZev Weiss #include <glob.h>
3217217845SJeremy Kerr #include <limits.h>
331cecc5deSJohnathan Mantey #include <time.h>
3454e9569dSJeremy Kerr #include <termios.h>
35d831f960SJeremy Kerr 
36d831f960SJeremy Kerr #include <sys/types.h>
371cecc5deSJohnathan Mantey #include <sys/time.h>
38b14ca19cSNinad Palsule #include <sys/socket.h>
3987e344cdSJoel Stanley #include <poll.h>
40d831f960SJeremy Kerr 
411a0e03b4SJeremy Kerr #include "console-server.h"
42d831f960SJeremy Kerr 
4330ea6385SAndrew Jeffery #define DEV_PTS_PATH "/dev/pts"
4430ea6385SAndrew Jeffery 
457f2bfb9bSMedicine Yeh /* default size of the shared backlog ringbuffer */
467f2bfb9bSMedicine Yeh const size_t default_buffer_size = 128ul * 1024ul;
47f733c85aSJeremy Kerr 
48769cee1aSJeremy Kerr /* state shared with the signal handler */
49553cb663SAndrew Jeffery static volatile sig_atomic_t sigint;
50329a35f5SJeremy Kerr 
51d831f960SJeremy Kerr static void usage(const char *progname)
52d831f960SJeremy Kerr {
53d831f960SJeremy Kerr 	fprintf(stderr,
546221ce94SVishwanatha Subbanna 		"usage: %s [options] <DEVICE>\n"
55d831f960SJeremy Kerr 		"\n"
56d831f960SJeremy Kerr 		"Options:\n"
57954be0fbSAndrew Jeffery 		"  --config <FILE>\tUse FILE for configuration\n"
58954be0fbSAndrew Jeffery 		"  --console-id <NAME>\tUse NAME in the UNIX domain socket address\n"
59d831f960SJeremy Kerr 		"",
60d831f960SJeremy Kerr 		progname);
61d831f960SJeremy Kerr }
62d831f960SJeremy Kerr 
6330ea6385SAndrew Jeffery /* populates console->tty.dev and console->tty.sysfs_devnode, using the tty kernel name */
641a0e03b4SJeremy Kerr static int tty_find_device(struct console *console)
6517217845SJeremy Kerr {
66d3cb9c22SAndrew Jeffery 	char *tty_class_device_link = NULL;
67d3cb9c22SAndrew Jeffery 	char *tty_path_input_real = NULL;
68d3cb9c22SAndrew Jeffery 	char *tty_device_tty_dir = NULL;
6930ea6385SAndrew Jeffery 	char *tty_vuart_lpc_addr = NULL;
70d3cb9c22SAndrew Jeffery 	char *tty_device_reldir = NULL;
7130ea6385SAndrew Jeffery 	char *tty_sysfs_devnode = NULL;
72d3cb9c22SAndrew Jeffery 	char *tty_kname_real = NULL;
7330ea6385SAndrew Jeffery 	char *tty_path_input = NULL;
7417217845SJeremy Kerr 	int rc;
7517217845SJeremy Kerr 
7630ea6385SAndrew Jeffery 	console->tty.type = TTY_DEVICE_UNDEFINED;
7730ea6385SAndrew Jeffery 
7830ea6385SAndrew Jeffery 	assert(console->tty.kname);
7930ea6385SAndrew Jeffery 	if (!strlen(console->tty.kname)) {
8030ea6385SAndrew Jeffery 		warnx("TTY kname must not be empty");
8130ea6385SAndrew Jeffery 		rc = -1;
8230ea6385SAndrew Jeffery 		goto out_free;
832834c5b1SAndrew Jeffery 	}
8417217845SJeremy Kerr 
8530ea6385SAndrew Jeffery 	if (console->tty.kname[0] == '/') {
8630ea6385SAndrew Jeffery 		tty_path_input = strdup(console->tty.kname);
8730ea6385SAndrew Jeffery 		if (!tty_path_input) {
8830ea6385SAndrew Jeffery 			rc = -1;
8930ea6385SAndrew Jeffery 			goto out_free;
9030ea6385SAndrew Jeffery 		}
9130ea6385SAndrew Jeffery 	} else {
9230ea6385SAndrew Jeffery 		rc = asprintf(&tty_path_input, "/dev/%s", console->tty.kname);
9330ea6385SAndrew Jeffery 		if (rc < 0) {
9430ea6385SAndrew Jeffery 			goto out_free;
9530ea6385SAndrew Jeffery 		}
9630ea6385SAndrew Jeffery 	}
9730ea6385SAndrew Jeffery 
9830ea6385SAndrew Jeffery 	/* udev may rename the tty name with a symbol link, try to resolve */
9945ad7676SYi Li 	tty_path_input_real = realpath(tty_path_input, NULL);
10045ad7676SYi Li 	if (!tty_path_input_real) {
10130ea6385SAndrew Jeffery 		warn("Can't find realpath for %s", tty_path_input);
10215792aa7SAndrew Jeffery 		rc = -1;
10345ad7676SYi Li 		goto out_free;
10445ad7676SYi Li 	}
10545ad7676SYi Li 
10630ea6385SAndrew Jeffery 	/*
10730ea6385SAndrew Jeffery 	 * Allow hooking obmc-console-server up to PTYs for testing
10830ea6385SAndrew Jeffery 	 *
10930ea6385SAndrew Jeffery 	 * https://amboar.github.io/notes/2023/05/02/testing-obmc-console-with-socat.html
11030ea6385SAndrew Jeffery 	 */
11130ea6385SAndrew Jeffery 	if (!strncmp(DEV_PTS_PATH, tty_path_input_real, strlen(DEV_PTS_PATH))) {
11230ea6385SAndrew Jeffery 		console->tty.type = TTY_DEVICE_PTY;
11330ea6385SAndrew Jeffery 		console->tty.dev = strdup(console->tty.kname);
11430ea6385SAndrew Jeffery 		rc = console->tty.dev ? 0 : -1;
11530ea6385SAndrew Jeffery 		goto out_free;
11630ea6385SAndrew Jeffery 	}
11730ea6385SAndrew Jeffery 
11845ad7676SYi Li 	tty_kname_real = basename(tty_path_input_real);
11945ad7676SYi Li 	if (!tty_kname_real) {
12030ea6385SAndrew Jeffery 		warn("Can't find real name for %s", console->tty.kname);
12115792aa7SAndrew Jeffery 		rc = -1;
12245ad7676SYi Li 		goto out_free;
12345ad7676SYi Li 	}
12445ad7676SYi Li 
125a72711afSAndrew Jeffery 	rc = asprintf(&tty_class_device_link, "/sys/class/tty/%s",
126a72711afSAndrew Jeffery 		      tty_kname_real);
1272834c5b1SAndrew Jeffery 	if (rc < 0) {
12845ad7676SYi Li 		goto out_free;
1292834c5b1SAndrew Jeffery 	}
13045ad7676SYi Li 
13117217845SJeremy Kerr 	tty_device_tty_dir = realpath(tty_class_device_link, NULL);
13245ad7676SYi Li 	if (!tty_device_tty_dir) {
13345ad7676SYi Li 		warn("Can't query sysfs for device %s", tty_kname_real);
13415792aa7SAndrew Jeffery 		rc = -1;
13517217845SJeremy Kerr 		goto out_free;
13617217845SJeremy Kerr 	}
13717217845SJeremy Kerr 
13817217845SJeremy Kerr 	rc = asprintf(&tty_device_reldir, "%s/../../", tty_device_tty_dir);
1392834c5b1SAndrew Jeffery 	if (rc < 0) {
14017217845SJeremy Kerr 		goto out_free;
1412834c5b1SAndrew Jeffery 	}
14217217845SJeremy Kerr 
14330ea6385SAndrew Jeffery 	tty_sysfs_devnode = realpath(tty_device_reldir, NULL);
14430ea6385SAndrew Jeffery 	if (!tty_sysfs_devnode) {
14545ad7676SYi Li 		warn("Can't find parent device for %s", tty_kname_real);
1462834c5b1SAndrew Jeffery 	}
14717217845SJeremy Kerr 
14830ea6385SAndrew Jeffery 	rc = asprintf(&console->tty.dev, "/dev/%s", tty_kname_real);
1492834c5b1SAndrew Jeffery 	if (rc < 0) {
15017217845SJeremy Kerr 		goto out_free;
1512834c5b1SAndrew Jeffery 	}
15217217845SJeremy Kerr 
153955d140eSOskar Senft 	// Default to non-VUART
154955d140eSOskar Senft 	console->tty.type = TTY_DEVICE_UART;
155955d140eSOskar Senft 
15630ea6385SAndrew Jeffery 	/* Arbitrarily pick an attribute to differentiate UART vs VUART */
157955d140eSOskar Senft 	if (tty_sysfs_devnode) {
158955d140eSOskar Senft 		rc = asprintf(&tty_vuart_lpc_addr, "%s/lpc_address",
159955d140eSOskar Senft 			      tty_sysfs_devnode);
16030ea6385SAndrew Jeffery 		if (rc < 0) {
16130ea6385SAndrew Jeffery 			goto out_free;
16230ea6385SAndrew Jeffery 		}
16330ea6385SAndrew Jeffery 
16430ea6385SAndrew Jeffery 		rc = access(tty_vuart_lpc_addr, F_OK);
165955d140eSOskar Senft 		if (!rc) {
166955d140eSOskar Senft 			console->tty.type = TTY_DEVICE_VUART;
167955d140eSOskar Senft 			console->tty.vuart.sysfs_devnode =
168955d140eSOskar Senft 				strdup(tty_sysfs_devnode);
169955d140eSOskar Senft 		}
170955d140eSOskar Senft 	}
17130ea6385SAndrew Jeffery 
17217217845SJeremy Kerr 	rc = 0;
17317217845SJeremy Kerr 
17417217845SJeremy Kerr out_free:
17530ea6385SAndrew Jeffery 	free(tty_vuart_lpc_addr);
17617217845SJeremy Kerr 	free(tty_class_device_link);
177982090d9SAndrew Jeffery 	free(tty_sysfs_devnode);
17817217845SJeremy Kerr 	free(tty_device_tty_dir);
17917217845SJeremy Kerr 	free(tty_device_reldir);
18045ad7676SYi Li 	free(tty_path_input);
18145ad7676SYi Li 	free(tty_path_input_real);
18217217845SJeremy Kerr 	return rc;
18317217845SJeremy Kerr }
18417217845SJeremy Kerr 
1851a0e03b4SJeremy Kerr static int tty_set_sysfs_attr(struct console *console, const char *name,
186957818b4SJeremy Kerr 			      int value)
187957818b4SJeremy Kerr {
188957818b4SJeremy Kerr 	char *path;
189957818b4SJeremy Kerr 	FILE *fp;
190957818b4SJeremy Kerr 	int rc;
191957818b4SJeremy Kerr 
19230ea6385SAndrew Jeffery 	assert(console->tty.type == TTY_DEVICE_VUART);
19330ea6385SAndrew Jeffery 
19430ea6385SAndrew Jeffery 	if (!console->tty.vuart.sysfs_devnode) {
19530ea6385SAndrew Jeffery 		return -1;
19630ea6385SAndrew Jeffery 	}
19730ea6385SAndrew Jeffery 
19830ea6385SAndrew Jeffery 	rc = asprintf(&path, "%s/%s", console->tty.vuart.sysfs_devnode, name);
1992834c5b1SAndrew Jeffery 	if (rc < 0) {
200957818b4SJeremy Kerr 		return -1;
2012834c5b1SAndrew Jeffery 	}
202957818b4SJeremy Kerr 
203957818b4SJeremy Kerr 	fp = fopen(path, "w");
204957818b4SJeremy Kerr 	if (!fp) {
205a72711afSAndrew Jeffery 		warn("Can't access attribute %s on device %s", name,
20630ea6385SAndrew Jeffery 		     console->tty.kname);
207957818b4SJeremy Kerr 		rc = -1;
208957818b4SJeremy Kerr 		goto out_free;
209957818b4SJeremy Kerr 	}
210957818b4SJeremy Kerr 	setvbuf(fp, NULL, _IONBF, 0);
211957818b4SJeremy Kerr 
212957818b4SJeremy Kerr 	rc = fprintf(fp, "0x%x", value);
2132834c5b1SAndrew Jeffery 	if (rc < 0) {
214a72711afSAndrew Jeffery 		warn("Error writing to %s attribute of device %s", name,
21530ea6385SAndrew Jeffery 		     console->tty.kname);
2162834c5b1SAndrew Jeffery 	}
217957818b4SJeremy Kerr 	fclose(fp);
218957818b4SJeremy Kerr 
219957818b4SJeremy Kerr out_free:
220957818b4SJeremy Kerr 	free(path);
221957818b4SJeremy Kerr 	return rc;
222957818b4SJeremy Kerr }
223957818b4SJeremy Kerr 
224d831f960SJeremy Kerr /**
225c7fbcd48SBenjamin Fair  * Set termios attributes on the console tty.
22654e9569dSJeremy Kerr  */
227e258e51fSNinad Palsule void tty_init_termios(struct console *console)
22854e9569dSJeremy Kerr {
22954e9569dSJeremy Kerr 	struct termios termios;
23054e9569dSJeremy Kerr 	int rc;
23154e9569dSJeremy Kerr 
23230ea6385SAndrew Jeffery 	rc = tcgetattr(console->tty.fd, &termios);
23354e9569dSJeremy Kerr 	if (rc) {
23454e9569dSJeremy Kerr 		warn("Can't read tty termios");
23554e9569dSJeremy Kerr 		return;
23654e9569dSJeremy Kerr 	}
23754e9569dSJeremy Kerr 
23830ea6385SAndrew Jeffery 	if (console->tty.type == TTY_DEVICE_UART && console->tty.uart.baud) {
23930ea6385SAndrew Jeffery 		if (cfsetspeed(&termios, console->tty.uart.baud) < 0) {
24030ea6385SAndrew Jeffery 			warn("Couldn't set speeds for %s", console->tty.kname);
241c7fbcd48SBenjamin Fair 		}
2422834c5b1SAndrew Jeffery 	}
243c7fbcd48SBenjamin Fair 
244c7fbcd48SBenjamin Fair 	/* Set console to raw mode: we don't want any processing to occur on
245c7fbcd48SBenjamin Fair 	 * the underlying terminal input/output.
246c7fbcd48SBenjamin Fair 	 */
24754e9569dSJeremy Kerr 	cfmakeraw(&termios);
248c7fbcd48SBenjamin Fair 
24930ea6385SAndrew Jeffery 	rc = tcsetattr(console->tty.fd, TCSANOW, &termios);
2502834c5b1SAndrew Jeffery 	if (rc) {
25130ea6385SAndrew Jeffery 		warn("Can't set terminal options for %s", console->tty.kname);
25254e9569dSJeremy Kerr 	}
2532834c5b1SAndrew Jeffery }
25454e9569dSJeremy Kerr 
25554e9569dSJeremy Kerr /**
256d831f960SJeremy Kerr  * Open and initialise the serial device
257d831f960SJeremy Kerr  */
25830ea6385SAndrew Jeffery static void tty_init_vuart_io(struct console *console)
259d831f960SJeremy Kerr {
26030ea6385SAndrew Jeffery 	assert(console->tty.type == TTY_DEVICE_VUART);
26130ea6385SAndrew Jeffery 
26230ea6385SAndrew Jeffery 	if (console->tty.vuart.sirq) {
26330ea6385SAndrew Jeffery 		tty_set_sysfs_attr(console, "sirq", console->tty.vuart.sirq);
2642834c5b1SAndrew Jeffery 	}
265957818b4SJeremy Kerr 
26630ea6385SAndrew Jeffery 	if (console->tty.vuart.lpc_addr) {
26730ea6385SAndrew Jeffery 		tty_set_sysfs_attr(console, "lpc_address",
26830ea6385SAndrew Jeffery 				   console->tty.vuart.lpc_addr);
26930ea6385SAndrew Jeffery 	}
27030ea6385SAndrew Jeffery }
27130ea6385SAndrew Jeffery 
27230ea6385SAndrew Jeffery static int tty_init_io(struct console *console)
27330ea6385SAndrew Jeffery {
27430ea6385SAndrew Jeffery 	console->tty.fd = open(console->tty.dev, O_RDWR);
27530ea6385SAndrew Jeffery 	if (console->tty.fd <= 0) {
27630ea6385SAndrew Jeffery 		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 	 */
28330ea6385SAndrew Jeffery 	fcntl(console->tty.fd, F_SETFL, FNDELAY);
284d831f960SJeremy Kerr 
28554e9569dSJeremy Kerr 	tty_init_termios(console);
28654e9569dSJeremy Kerr 
28730ea6385SAndrew Jeffery 	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 
29330ea6385SAndrew Jeffery static int tty_init_vuart(struct console *console, struct config *config)
294d66195c1SJeremy Kerr {
295fd883a88SAndrew Jeffery 	unsigned long parsed;
296d66195c1SJeremy Kerr 	const char *val;
297d66195c1SJeremy Kerr 	char *endp;
29830ea6385SAndrew Jeffery 
29930ea6385SAndrew Jeffery 	assert(console->tty.type == TTY_DEVICE_VUART);
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 
31630ea6385SAndrew Jeffery 		console->tty.vuart.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 
33630ea6385SAndrew Jeffery 		console->tty.vuart.sirq = (int)parsed;
3372834c5b1SAndrew Jeffery 		if (endp == optarg) {
338d66195c1SJeremy Kerr 			warn("Invalid sirq: '%s'", val);
339d66195c1SJeremy Kerr 		}
3402834c5b1SAndrew Jeffery 	}
341d66195c1SJeremy Kerr 
34230ea6385SAndrew Jeffery 	return 0;
3432834c5b1SAndrew Jeffery }
344c7fbcd48SBenjamin Fair 
34530ea6385SAndrew Jeffery static int tty_init(struct console *console, struct config *config,
34630ea6385SAndrew Jeffery 		    const char *tty_arg)
34730ea6385SAndrew Jeffery {
34830ea6385SAndrew Jeffery 	const char *val;
34930ea6385SAndrew Jeffery 	int rc;
35030ea6385SAndrew Jeffery 
351d769eecfSAndrew Jeffery 	if (tty_arg) {
35230ea6385SAndrew Jeffery 		console->tty.kname = tty_arg;
353d769eecfSAndrew Jeffery 	} else if ((val = config_get_value(config, "upstream-tty"))) {
35430ea6385SAndrew Jeffery 		console->tty.kname = val;
355d769eecfSAndrew Jeffery 	} else {
356d66195c1SJeremy Kerr 		warnx("Error: No TTY device specified");
357d66195c1SJeremy Kerr 		return -1;
358d66195c1SJeremy Kerr 	}
359d66195c1SJeremy Kerr 
360d66195c1SJeremy Kerr 	rc = tty_find_device(console);
3612834c5b1SAndrew Jeffery 	if (rc) {
362d66195c1SJeremy Kerr 		return rc;
3632834c5b1SAndrew Jeffery 	}
364d66195c1SJeremy Kerr 
36530ea6385SAndrew Jeffery 	switch (console->tty.type) {
36630ea6385SAndrew Jeffery 	case TTY_DEVICE_VUART:
36730ea6385SAndrew Jeffery 		rc = tty_init_vuart(console, config);
36830ea6385SAndrew Jeffery 		if (rc) {
369d66195c1SJeremy Kerr 			return rc;
370d66195c1SJeremy Kerr 		}
371d66195c1SJeremy Kerr 
37230ea6385SAndrew Jeffery 		tty_init_vuart_io(console);
37330ea6385SAndrew Jeffery 		break;
37430ea6385SAndrew Jeffery 	case TTY_DEVICE_UART:
37530ea6385SAndrew Jeffery 		val = config_get_value(config, "baud");
37630ea6385SAndrew Jeffery 		if (val) {
37730ea6385SAndrew Jeffery 			if (config_parse_baud(&console->tty.uart.baud, val)) {
37830ea6385SAndrew Jeffery 				warnx("Invalid baud rate: '%s'", val);
37930ea6385SAndrew Jeffery 			}
38030ea6385SAndrew Jeffery 		}
38130ea6385SAndrew Jeffery 		break;
38230ea6385SAndrew Jeffery 	case TTY_DEVICE_PTY:
38330ea6385SAndrew Jeffery 		break;
38430ea6385SAndrew Jeffery 	case TTY_DEVICE_UNDEFINED:
38530ea6385SAndrew Jeffery 	default:
38630ea6385SAndrew Jeffery 		warnx("Cannot configure unrecognised TTY device");
38730ea6385SAndrew Jeffery 		return -1;
38830ea6385SAndrew Jeffery 	}
38930ea6385SAndrew Jeffery 
39030ea6385SAndrew Jeffery 	return tty_init_io(console);
39130ea6385SAndrew Jeffery }
39230ea6385SAndrew Jeffery 
39330ea6385SAndrew Jeffery static void tty_fini(struct console *console)
39430ea6385SAndrew Jeffery {
39530ea6385SAndrew Jeffery 	if (console->tty.type == TTY_DEVICE_VUART) {
39630ea6385SAndrew Jeffery 		free(console->tty.vuart.sysfs_devnode);
39730ea6385SAndrew Jeffery 	}
39830ea6385SAndrew Jeffery 	free(console->tty.dev);
39930ea6385SAndrew Jeffery }
40030ea6385SAndrew Jeffery 
4017dc08baaSZev Weiss static int write_to_path(const char *path, const char *data)
4027dc08baaSZev Weiss {
4037dc08baaSZev Weiss 	int rc = 0;
4047dc08baaSZev Weiss 	FILE *f = fopen(path, "w");
4057dc08baaSZev Weiss 	if (!f) {
4067dc08baaSZev Weiss 		return -1;
4077dc08baaSZev Weiss 	}
4087dc08baaSZev Weiss 
4097dc08baaSZev Weiss 	if (fprintf(f, "%s", data) < 0) {
4107dc08baaSZev Weiss 		rc = -1;
4117dc08baaSZev Weiss 	}
4127dc08baaSZev Weiss 
4137dc08baaSZev Weiss 	if (fclose(f)) {
4147dc08baaSZev Weiss 		rc = -1;
4157dc08baaSZev Weiss 	}
4167dc08baaSZev Weiss 
4177dc08baaSZev Weiss 	return rc;
4187dc08baaSZev Weiss }
4197dc08baaSZev Weiss 
4207dc08baaSZev Weiss #define ASPEED_UART_ROUTING_PATTERN                                            \
4217dc08baaSZev Weiss 	"/sys/bus/platform/drivers/aspeed-uart-routing/*.uart-routing"
4227dc08baaSZev Weiss 
4237dc08baaSZev Weiss static void uart_routing_init(struct config *config)
4247dc08baaSZev Weiss {
4257dc08baaSZev Weiss 	const char *muxcfg;
4267dc08baaSZev Weiss 	const char *p;
4277dc08baaSZev Weiss 	size_t buflen;
4287dc08baaSZev Weiss 	char *sink;
4297dc08baaSZev Weiss 	char *source;
4307dc08baaSZev Weiss 	char *muxdir;
4317dc08baaSZev Weiss 	char *path;
4327dc08baaSZev Weiss 	glob_t globbuf;
4337dc08baaSZev Weiss 
4347dc08baaSZev Weiss 	muxcfg = config_get_value(config, "aspeed-uart-routing");
4357dc08baaSZev Weiss 	if (!muxcfg) {
4367dc08baaSZev Weiss 		return;
4377dc08baaSZev Weiss 	}
4387dc08baaSZev Weiss 
4397dc08baaSZev Weiss 	/* Find the driver's sysfs directory */
4407dc08baaSZev Weiss 	if (glob(ASPEED_UART_ROUTING_PATTERN, GLOB_ERR | GLOB_NOSORT, NULL,
4417dc08baaSZev Weiss 		 &globbuf) != 0) {
4427dc08baaSZev Weiss 		warn("Couldn't find uart-routing driver directory, cannot apply config");
4437dc08baaSZev Weiss 		return;
4447dc08baaSZev Weiss 	}
4457dc08baaSZev Weiss 	if (globbuf.gl_pathc != 1) {
4467dc08baaSZev Weiss 		warnx("Found %zd uart-routing driver directories, cannot apply config",
4477dc08baaSZev Weiss 		      globbuf.gl_pathc);
4487dc08baaSZev Weiss 		goto out_free_glob;
4497dc08baaSZev Weiss 	}
4507dc08baaSZev Weiss 	muxdir = globbuf.gl_pathv[0];
4517dc08baaSZev Weiss 
4527dc08baaSZev Weiss 	/*
4537dc08baaSZev Weiss 	 * Rather than faff about tracking a bunch of separate buffer sizes,
4547dc08baaSZev Weiss 	 * just use one (worst-case) size for all of them -- +2 for a trailing
4557dc08baaSZev Weiss 	 * NUL and a '/' separator to construct the sysfs file path.
4567dc08baaSZev Weiss 	 */
4577dc08baaSZev Weiss 	buflen = strlen(muxdir) + strlen(muxcfg) + 2;
4587dc08baaSZev Weiss 
4597dc08baaSZev Weiss 	sink = malloc(buflen);
4607dc08baaSZev Weiss 	source = malloc(buflen);
4617dc08baaSZev Weiss 	path = malloc(buflen);
4627dc08baaSZev Weiss 	if (!path || !sink || !source) {
4637dc08baaSZev Weiss 		warnx("Out of memory applying uart routing config");
4647dc08baaSZev Weiss 		goto out_free_bufs;
4657dc08baaSZev Weiss 	}
4667dc08baaSZev Weiss 
4677dc08baaSZev Weiss 	p = muxcfg;
4687dc08baaSZev Weiss 	while (*p) {
4697dc08baaSZev Weiss 		ssize_t bytes_scanned;
4707dc08baaSZev Weiss 
4717dc08baaSZev Weiss 		if (sscanf(p, " %[^:/ \t]:%[^: \t] %zn", sink, source,
4727dc08baaSZev Weiss 			   &bytes_scanned) != 2) {
4737dc08baaSZev Weiss 			warnx("Invalid syntax in aspeed uart config: '%s' not applied",
4747dc08baaSZev Weiss 			      p);
4757dc08baaSZev Weiss 			break;
4767dc08baaSZev Weiss 		}
4777dc08baaSZev Weiss 		p += bytes_scanned;
4787dc08baaSZev Weiss 
4797dc08baaSZev Weiss 		/*
4807dc08baaSZev Weiss 		 * Check that the sink name looks reasonable before proceeding
4817dc08baaSZev Weiss 		 * (there are other writable files in the same directory that
4827dc08baaSZev Weiss 		 * we shouldn't be touching, such as 'driver_override' and
4837dc08baaSZev Weiss 		 * 'uevent').
4847dc08baaSZev Weiss 		 */
4857dc08baaSZev Weiss 		if (strncmp(sink, "io", strlen("io")) != 0 &&
4867dc08baaSZev Weiss 		    strncmp(sink, "uart", strlen("uart")) != 0) {
4877dc08baaSZev Weiss 			warnx("Skipping invalid uart routing name '%s' (must be ioN or uartN)",
4887dc08baaSZev Weiss 			      sink);
4897dc08baaSZev Weiss 			continue;
4907dc08baaSZev Weiss 		}
4917dc08baaSZev Weiss 
4927dc08baaSZev Weiss 		snprintf(path, buflen, "%s/%s", muxdir, sink);
4937dc08baaSZev Weiss 		if (write_to_path(path, source)) {
4947dc08baaSZev Weiss 			warn("Failed to apply uart-routing config '%s:%s'",
4957dc08baaSZev Weiss 			     sink, source);
4967dc08baaSZev Weiss 		}
4977dc08baaSZev Weiss 	}
4987dc08baaSZev Weiss 
4997dc08baaSZev Weiss out_free_bufs:
5007dc08baaSZev Weiss 	free(path);
5017dc08baaSZev Weiss 	free(source);
5027dc08baaSZev Weiss 	free(sink);
5037dc08baaSZev Weiss out_free_glob:
5047dc08baaSZev Weiss 	globfree(&globbuf);
5057dc08baaSZev Weiss }
5067dc08baaSZev Weiss 
5071a0e03b4SJeremy Kerr int console_data_out(struct console *console, const uint8_t *data, size_t len)
508d831f960SJeremy Kerr {
50930ea6385SAndrew Jeffery 	return write_buf_to_fd(console->tty.fd, data, len);
510d831f960SJeremy Kerr }
511d831f960SJeremy Kerr 
5125ba20b5bSNinad Palsule /* Prepare a socket name */
513954be0fbSAndrew Jeffery static int set_socket_info(struct console *console, struct config *config,
514954be0fbSAndrew Jeffery 			   const char *console_id)
515b14ca19cSNinad Palsule {
516b14ca19cSNinad Palsule 	ssize_t len;
517b14ca19cSNinad Palsule 
5185ba20b5bSNinad Palsule 	/* Get console id */
5195ba20b5bSNinad Palsule 	console->console_id = config_resolve_console_id(config, console_id);
520954be0fbSAndrew Jeffery 
521b14ca19cSNinad Palsule 	/* Get the socket name/path */
5225ba20b5bSNinad Palsule 	len = console_socket_path(console->socket_name, console->console_id);
523b14ca19cSNinad Palsule 	if (len < 0) {
524b14ca19cSNinad Palsule 		warn("Failed to set socket path: %s", strerror(errno));
525b14ca19cSNinad Palsule 		return EXIT_FAILURE;
526b14ca19cSNinad Palsule 	}
527b14ca19cSNinad Palsule 
528b14ca19cSNinad Palsule 	/* Socket name is not a null terminated string hence save the length */
529b14ca19cSNinad Palsule 	console->socket_name_len = len;
530b14ca19cSNinad Palsule 
531b14ca19cSNinad Palsule 	return 0;
532b14ca19cSNinad Palsule }
533b14ca19cSNinad Palsule 
534d47963e5SJeremy Kerr static void handlers_init(struct console *console, struct config *config)
535d831f960SJeremy Kerr {
536b70f8713SAndrew Jeffery 	/* NOLINTBEGIN(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) */
537e2826c7dSJeremy Kerr 	extern const struct handler_type *const __start_handlers;
538e2826c7dSJeremy Kerr 	extern const struct handler_type *const __stop_handlers;
539b70f8713SAndrew Jeffery 	/* NOLINTEND(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) */
540e2826c7dSJeremy Kerr 	size_t n_types;
541e2826c7dSJeremy Kerr 	int j = 0;
542e2826c7dSJeremy Kerr 	size_t i;
543e2826c7dSJeremy Kerr 
544e2826c7dSJeremy Kerr 	n_types = &__stop_handlers - &__start_handlers;
545e2826c7dSJeremy Kerr 	console->handlers = calloc(n_types, sizeof(struct handler *));
546e2826c7dSJeremy Kerr 	if (!console->handlers) {
547e2826c7dSJeremy Kerr 		err(EXIT_FAILURE, "malloc(handlers)");
548e2826c7dSJeremy Kerr 	}
549e2826c7dSJeremy Kerr 
550079fc516SAndrew Jeffery 	printf("%zu handler type%s\n", n_types, n_types == 1 ? "" : "s");
551e2826c7dSJeremy Kerr 
552e2826c7dSJeremy Kerr 	for (i = 0; i < n_types; i++) {
553e2826c7dSJeremy Kerr 		const struct handler_type *type = &__start_handlers[i];
5541a0e03b4SJeremy Kerr 		struct handler *handler;
555d831f960SJeremy Kerr 
556e2826c7dSJeremy Kerr 		/* Should be picked up at build time by
557e2826c7dSJeremy Kerr 		 * console_handler_register, but check anyway
558e2826c7dSJeremy Kerr 		 */
559e2826c7dSJeremy Kerr 		if (!type->init || !type->fini) {
560e2826c7dSJeremy Kerr 			errx(EXIT_FAILURE,
561e2826c7dSJeremy Kerr 			     "invalid handler type %s: no init() / fini()",
562e2826c7dSJeremy Kerr 			     type->name);
5632834c5b1SAndrew Jeffery 		}
564021b91f0SJeremy Kerr 
565e2826c7dSJeremy Kerr 		handler = type->init(type, console, config);
566021b91f0SJeremy Kerr 
567e2826c7dSJeremy Kerr 		printf("  console '%s': handler %s [%sactive]\n",
568e2826c7dSJeremy Kerr 		       console->console_id, type->name, handler ? "" : "in");
569e2826c7dSJeremy Kerr 
570e2826c7dSJeremy Kerr 		if (handler) {
571e2826c7dSJeremy Kerr 			handler->type = type;
572e2826c7dSJeremy Kerr 			console->handlers[j++] = handler;
573d831f960SJeremy Kerr 		}
574d831f960SJeremy Kerr 	}
575d831f960SJeremy Kerr 
576e2826c7dSJeremy Kerr 	console->n_handlers = j;
577e2826c7dSJeremy Kerr }
578e2826c7dSJeremy Kerr 
5791a0e03b4SJeremy Kerr static void handlers_fini(struct console *console)
580d831f960SJeremy Kerr {
5811a0e03b4SJeremy Kerr 	struct handler *handler;
5821a0e03b4SJeremy Kerr 	int i;
5831a0e03b4SJeremy Kerr 
5841a0e03b4SJeremy Kerr 	for (i = 0; i < console->n_handlers; i++) {
5851a0e03b4SJeremy Kerr 		handler = console->handlers[i];
586e2826c7dSJeremy Kerr 		handler->type->fini(handler);
5871a0e03b4SJeremy Kerr 	}
588e2826c7dSJeremy Kerr 
589e2826c7dSJeremy Kerr 	free(console->handlers);
590e2826c7dSJeremy Kerr 	console->handlers = NULL;
591e2826c7dSJeremy Kerr 	console->n_handlers = 0;
5922834c5b1SAndrew Jeffery }
593d831f960SJeremy Kerr 
5941cecc5deSJohnathan Mantey static int get_current_time(struct timeval *tv)
5951cecc5deSJohnathan Mantey {
5961cecc5deSJohnathan Mantey 	struct timespec t;
5971cecc5deSJohnathan Mantey 	int rc;
5981cecc5deSJohnathan Mantey 
5991cecc5deSJohnathan Mantey 	/*
6001cecc5deSJohnathan Mantey 	 * We use clock_gettime(CLOCK_MONOTONIC) so we're immune to
6011cecc5deSJohnathan Mantey 	 * local time changes. However, a struct timeval is more
6021cecc5deSJohnathan Mantey 	 * convenient for calculations, so convert to that.
6031cecc5deSJohnathan Mantey 	 */
6041cecc5deSJohnathan Mantey 	rc = clock_gettime(CLOCK_MONOTONIC, &t);
6052834c5b1SAndrew Jeffery 	if (rc) {
6061cecc5deSJohnathan Mantey 		return rc;
6072834c5b1SAndrew Jeffery 	}
6081cecc5deSJohnathan Mantey 
6091cecc5deSJohnathan Mantey 	tv->tv_sec = t.tv_sec;
6101cecc5deSJohnathan Mantey 	tv->tv_usec = t.tv_nsec / 1000;
6111cecc5deSJohnathan Mantey 
6121cecc5deSJohnathan Mantey 	return 0;
6131cecc5deSJohnathan Mantey }
6141cecc5deSJohnathan Mantey 
615a72711afSAndrew Jeffery struct ringbuffer_consumer *
616a72711afSAndrew Jeffery console_ringbuffer_consumer_register(struct console *console,
617f733c85aSJeremy Kerr 				     ringbuffer_poll_fn_t poll_fn, void *data)
618d831f960SJeremy Kerr {
619f733c85aSJeremy Kerr 	return ringbuffer_consumer_register(console->rb, poll_fn, data);
620d831f960SJeremy Kerr }
621d831f960SJeremy Kerr 
62255c9712dSJeremy Kerr struct poller *console_poller_register(struct console *console,
623a72711afSAndrew Jeffery 				       struct handler *handler,
624a72711afSAndrew Jeffery 				       poller_event_fn_t poller_fn,
6251cecc5deSJohnathan Mantey 				       poller_timeout_fn_t timeout_fn, int fd,
6261cecc5deSJohnathan Mantey 				       int events, void *data)
627d831f960SJeremy Kerr {
628329a35f5SJeremy Kerr 	struct poller *poller;
6295c359cc6SAndrew Jeffery 	long n;
630329a35f5SJeremy Kerr 
631329a35f5SJeremy Kerr 	poller = malloc(sizeof(*poller));
632329a35f5SJeremy Kerr 	poller->remove = false;
633329a35f5SJeremy Kerr 	poller->handler = handler;
6341cecc5deSJohnathan Mantey 	poller->event_fn = poller_fn;
6351cecc5deSJohnathan Mantey 	poller->timeout_fn = timeout_fn;
6369cc8459aSAndrew Jeffery 	timerclear(&poller->timeout);
637329a35f5SJeremy Kerr 	poller->data = data;
638329a35f5SJeremy Kerr 
639329a35f5SJeremy Kerr 	/* add one to our pollers array */
640329a35f5SJeremy Kerr 	n = console->n_pollers++;
64191b52175SAndrew Jeffery 	/*
64291b52175SAndrew Jeffery 	 * We're managing an array of pointers to aggregates, so don't warn about sizeof() on a
64391b52175SAndrew Jeffery 	 * pointer type.
64491b52175SAndrew Jeffery 	 */
64591b52175SAndrew Jeffery 	/* NOLINTBEGIN(bugprone-sizeof-expression) */
64691b52175SAndrew Jeffery 	console->pollers = reallocarray(console->pollers, console->n_pollers,
64791b52175SAndrew Jeffery 					sizeof(*console->pollers));
64891b52175SAndrew Jeffery 	/* NOLINTEND(bugprone-sizeof-expression) */
649329a35f5SJeremy Kerr 
650329a35f5SJeremy Kerr 	console->pollers[n] = poller;
651329a35f5SJeremy Kerr 
652329a35f5SJeremy Kerr 	/* increase pollfds array too  */
6534e44c790SAndrew Jeffery 	console->pollfds = reallocarray(
6544e44c790SAndrew Jeffery 		console->pollfds, (MAX_INTERNAL_POLLFD + console->n_pollers),
65591b52175SAndrew Jeffery 		sizeof(*console->pollfds));
656329a35f5SJeremy Kerr 
657329a35f5SJeremy Kerr 	/* shift the end pollfds up by one */
658a72711afSAndrew Jeffery 	memcpy(&console->pollfds[n + 1], &console->pollfds[n],
659f9c8f6caSCheng C Yang 	       sizeof(*console->pollfds) * MAX_INTERNAL_POLLFD);
660329a35f5SJeremy Kerr 
661329a35f5SJeremy Kerr 	console->pollfds[n].fd = fd;
6625c359cc6SAndrew Jeffery 	console->pollfds[n].events = (short)(events & 0x7fff);
663329a35f5SJeremy Kerr 
664329a35f5SJeremy Kerr 	return poller;
665329a35f5SJeremy Kerr }
666329a35f5SJeremy Kerr 
667a72711afSAndrew Jeffery void console_poller_unregister(struct console *console, struct poller *poller)
668329a35f5SJeremy Kerr {
669329a35f5SJeremy Kerr 	int i;
670329a35f5SJeremy Kerr 
671329a35f5SJeremy Kerr 	/* find the entry in our pollers array */
6722834c5b1SAndrew Jeffery 	for (i = 0; i < console->n_pollers; i++) {
6732834c5b1SAndrew Jeffery 		if (console->pollers[i] == poller) {
674329a35f5SJeremy Kerr 			break;
6752834c5b1SAndrew Jeffery 		}
6762834c5b1SAndrew Jeffery 	}
677329a35f5SJeremy Kerr 
678329a35f5SJeremy Kerr 	assert(i < console->n_pollers);
679329a35f5SJeremy Kerr 
680329a35f5SJeremy Kerr 	console->n_pollers--;
681329a35f5SJeremy Kerr 
68291b52175SAndrew Jeffery 	/*
68391b52175SAndrew Jeffery 	 * Remove the item from the pollers array...
68491b52175SAndrew Jeffery 	 *
68591b52175SAndrew Jeffery 	 * We're managing an array of pointers to aggregates, so don't warn about sizeof() on a
68691b52175SAndrew Jeffery 	 * pointer type.
68791b52175SAndrew Jeffery 	 */
68891b52175SAndrew Jeffery 	/* NOLINTBEGIN(bugprone-sizeof-expression) */
689329a35f5SJeremy Kerr 	memmove(&console->pollers[i], &console->pollers[i + 1],
690a72711afSAndrew Jeffery 		sizeof(*console->pollers) * (console->n_pollers - i));
691329a35f5SJeremy Kerr 
692*7851a396SAndrew Jeffery 	if (console->n_pollers == 0) {
693*7851a396SAndrew Jeffery 		free(console->pollers);
694*7851a396SAndrew Jeffery 		console->pollers = NULL;
695*7851a396SAndrew Jeffery 	} else {
696*7851a396SAndrew Jeffery 		console->pollers = reallocarray(console->pollers,
697*7851a396SAndrew Jeffery 						console->n_pollers,
69891b52175SAndrew Jeffery 						sizeof(*console->pollers));
699*7851a396SAndrew Jeffery 	}
70091b52175SAndrew Jeffery 	/* NOLINTEND(bugprone-sizeof-expression) */
701329a35f5SJeremy Kerr 
702329a35f5SJeremy Kerr 	/* ... and the pollfds array */
703329a35f5SJeremy Kerr 	memmove(&console->pollfds[i], &console->pollfds[i + 1],
704329a35f5SJeremy Kerr 		sizeof(*console->pollfds) *
705f9c8f6caSCheng C Yang 			(MAX_INTERNAL_POLLFD + console->n_pollers - i));
706329a35f5SJeremy Kerr 
7074e44c790SAndrew Jeffery 	console->pollfds = reallocarray(
7084e44c790SAndrew Jeffery 		console->pollfds, (MAX_INTERNAL_POLLFD + console->n_pollers),
70991b52175SAndrew Jeffery 		sizeof(*console->pollfds));
710329a35f5SJeremy Kerr 
711329a35f5SJeremy Kerr 	free(poller);
712329a35f5SJeremy Kerr }
713329a35f5SJeremy Kerr 
7146b1fed27SJeremy Kerr void console_poller_set_events(struct console *console, struct poller *poller,
7156b1fed27SJeremy Kerr 			       int events)
7166b1fed27SJeremy Kerr {
7176b1fed27SJeremy Kerr 	int i;
7186b1fed27SJeremy Kerr 
7196b1fed27SJeremy Kerr 	/* find the entry in our pollers array */
7202834c5b1SAndrew Jeffery 	for (i = 0; i < console->n_pollers; i++) {
7212834c5b1SAndrew Jeffery 		if (console->pollers[i] == poller) {
7226b1fed27SJeremy Kerr 			break;
7232834c5b1SAndrew Jeffery 		}
7242834c5b1SAndrew Jeffery 	}
7256b1fed27SJeremy Kerr 
7265c359cc6SAndrew Jeffery 	console->pollfds[i].events = (short)(events & 0x7fff);
7276b1fed27SJeremy Kerr }
7286b1fed27SJeremy Kerr 
729fd048328SAndrew Jeffery void console_poller_set_timeout(struct console *console __attribute__((unused)),
730fd048328SAndrew Jeffery 				struct poller *poller, const struct timeval *tv)
7311cecc5deSJohnathan Mantey {
7321cecc5deSJohnathan Mantey 	struct timeval now;
7331cecc5deSJohnathan Mantey 	int rc;
7341cecc5deSJohnathan Mantey 
7351cecc5deSJohnathan Mantey 	rc = get_current_time(&now);
7362834c5b1SAndrew Jeffery 	if (rc) {
7371cecc5deSJohnathan Mantey 		return;
7382834c5b1SAndrew Jeffery 	}
7391cecc5deSJohnathan Mantey 
7401cecc5deSJohnathan Mantey 	timeradd(&now, tv, &poller->timeout);
7411cecc5deSJohnathan Mantey }
7421cecc5deSJohnathan Mantey 
7435c359cc6SAndrew Jeffery static long get_poll_timeout(struct console *console, struct timeval *cur_time)
7441cecc5deSJohnathan Mantey {
745b70f8713SAndrew Jeffery 	struct timeval *earliest;
746b70f8713SAndrew Jeffery 	struct timeval interval;
7471cecc5deSJohnathan Mantey 	struct poller *poller;
7481cecc5deSJohnathan Mantey 	int i;
7491cecc5deSJohnathan Mantey 
7501cecc5deSJohnathan Mantey 	earliest = NULL;
7511cecc5deSJohnathan Mantey 
7521cecc5deSJohnathan Mantey 	for (i = 0; i < console->n_pollers; i++) {
7531cecc5deSJohnathan Mantey 		poller = console->pollers[i];
7541cecc5deSJohnathan Mantey 
7551cecc5deSJohnathan Mantey 		if (poller->timeout_fn && timerisset(&poller->timeout) &&
7561cecc5deSJohnathan Mantey 		    (!earliest ||
7571cecc5deSJohnathan Mantey 		     (earliest && timercmp(&poller->timeout, earliest, <)))) {
7581cecc5deSJohnathan Mantey 			// poller is buffering data and needs the poll
7591cecc5deSJohnathan Mantey 			// function to timeout.
7601cecc5deSJohnathan Mantey 			earliest = &poller->timeout;
7611cecc5deSJohnathan Mantey 		}
7621cecc5deSJohnathan Mantey 	}
7631cecc5deSJohnathan Mantey 
7641cecc5deSJohnathan Mantey 	if (earliest) {
7651cecc5deSJohnathan Mantey 		if (timercmp(earliest, cur_time, >)) {
7661cecc5deSJohnathan Mantey 			/* recalculate the timeout period, time period has
7671cecc5deSJohnathan Mantey 			 * not elapsed */
7681cecc5deSJohnathan Mantey 			timersub(earliest, cur_time, &interval);
7691cecc5deSJohnathan Mantey 			return ((interval.tv_sec * 1000) +
7701cecc5deSJohnathan Mantey 				(interval.tv_usec / 1000));
7710b7b0477SAndrew Jeffery 		} /* return from poll immediately */
7721cecc5deSJohnathan Mantey 		return 0;
7730b7b0477SAndrew Jeffery 
7740b7b0477SAndrew Jeffery 	} /* poll indefinitely */
7751cecc5deSJohnathan Mantey 	return -1;
7761cecc5deSJohnathan Mantey }
7771cecc5deSJohnathan Mantey 
7781cecc5deSJohnathan Mantey static int call_pollers(struct console *console, struct timeval *cur_time)
779329a35f5SJeremy Kerr {
780329a35f5SJeremy Kerr 	struct poller *poller;
781329a35f5SJeremy Kerr 	struct pollfd *pollfd;
782329a35f5SJeremy Kerr 	enum poller_ret prc;
783b70f8713SAndrew Jeffery 	int i;
784b70f8713SAndrew Jeffery 	int rc;
785d831f960SJeremy Kerr 
7861a0e03b4SJeremy Kerr 	rc = 0;
7871a0e03b4SJeremy Kerr 
788329a35f5SJeremy Kerr 	/*
789329a35f5SJeremy Kerr 	 * Process poll events by iterating through the pollers and pollfds
790329a35f5SJeremy Kerr 	 * in-step, calling any pollers that we've found revents for.
791329a35f5SJeremy Kerr 	 */
792329a35f5SJeremy Kerr 	for (i = 0; i < console->n_pollers; i++) {
793329a35f5SJeremy Kerr 		poller = console->pollers[i];
794329a35f5SJeremy Kerr 		pollfd = &console->pollfds[i];
7951cecc5deSJohnathan Mantey 		prc = POLLER_OK;
7961a0e03b4SJeremy Kerr 
7971cecc5deSJohnathan Mantey 		/* process pending events... */
7981cecc5deSJohnathan Mantey 		if (pollfd->revents) {
7991cecc5deSJohnathan Mantey 			prc = poller->event_fn(poller->handler, pollfd->revents,
800329a35f5SJeremy Kerr 					       poller->data);
8012834c5b1SAndrew Jeffery 			if (prc == POLLER_EXIT) {
802329a35f5SJeremy Kerr 				rc = -1;
8032834c5b1SAndrew Jeffery 			} else if (prc == POLLER_REMOVE) {
804329a35f5SJeremy Kerr 				poller->remove = true;
805329a35f5SJeremy Kerr 			}
8062834c5b1SAndrew Jeffery 		}
807329a35f5SJeremy Kerr 
8081cecc5deSJohnathan Mantey 		if ((prc == POLLER_OK) && poller->timeout_fn &&
8091cecc5deSJohnathan Mantey 		    timerisset(&poller->timeout) &&
8101cecc5deSJohnathan Mantey 		    timercmp(&poller->timeout, cur_time, <=)) {
8111cecc5deSJohnathan Mantey 			/* One of the ringbuffer consumers is buffering the
8121cecc5deSJohnathan Mantey 			data stream. The amount of idle time the consumer
8131cecc5deSJohnathan Mantey 			desired has expired.  Process the buffered data for
8141cecc5deSJohnathan Mantey 			transmission. */
8151cecc5deSJohnathan Mantey 			timerclear(&poller->timeout);
8161cecc5deSJohnathan Mantey 			prc = poller->timeout_fn(poller->handler, poller->data);
8171cecc5deSJohnathan Mantey 			if (prc == POLLER_EXIT) {
8181cecc5deSJohnathan Mantey 				rc = -1;
8191cecc5deSJohnathan Mantey 			} else if (prc == POLLER_REMOVE) {
8201cecc5deSJohnathan Mantey 				poller->remove = true;
8211cecc5deSJohnathan Mantey 			}
8221cecc5deSJohnathan Mantey 		}
8231cecc5deSJohnathan Mantey 	}
8241cecc5deSJohnathan Mantey 
825329a35f5SJeremy Kerr 	/**
826329a35f5SJeremy Kerr 	 * Process deferred removals; restarting each time we unregister, as
827329a35f5SJeremy Kerr 	 * the array will have changed
828329a35f5SJeremy Kerr 	 */
829329a35f5SJeremy Kerr 	for (;;) {
830329a35f5SJeremy Kerr 		bool removed = false;
831329a35f5SJeremy Kerr 
832329a35f5SJeremy Kerr 		for (i = 0; i < console->n_pollers; i++) {
833329a35f5SJeremy Kerr 			poller = console->pollers[i];
834329a35f5SJeremy Kerr 			if (poller->remove) {
83555c9712dSJeremy Kerr 				console_poller_unregister(console, poller);
836329a35f5SJeremy Kerr 				removed = true;
837329a35f5SJeremy Kerr 				break;
838329a35f5SJeremy Kerr 			}
839329a35f5SJeremy Kerr 		}
8402834c5b1SAndrew Jeffery 		if (!removed) {
841329a35f5SJeremy Kerr 			break;
8421a0e03b4SJeremy Kerr 		}
8432834c5b1SAndrew Jeffery 	}
8441a0e03b4SJeremy Kerr 
8451a0e03b4SJeremy Kerr 	return rc;
8461a0e03b4SJeremy Kerr }
8471a0e03b4SJeremy Kerr 
848769cee1aSJeremy Kerr static void sighandler(int signal)
849769cee1aSJeremy Kerr {
8502834c5b1SAndrew Jeffery 	if (signal == SIGINT) {
851553cb663SAndrew Jeffery 		sigint = 1;
852769cee1aSJeremy Kerr 	}
8532834c5b1SAndrew Jeffery }
854769cee1aSJeremy Kerr 
8556925740dSAlexander Hansen static int run_console_iteration(struct console *console)
8561a0e03b4SJeremy Kerr {
8576925740dSAlexander Hansen 	uint8_t buf[4096];
8581cecc5deSJohnathan Mantey 	struct timeval tv;
8595c359cc6SAndrew Jeffery 	long timeout;
8605c359cc6SAndrew Jeffery 	ssize_t rc;
861769cee1aSJeremy Kerr 
8627f2bfb9bSMedicine Yeh 	if (console->rb->size < sizeof(buf)) {
8636925740dSAlexander Hansen 		fprintf(stderr, "Ringbuffer size should be greater than %zuB\n",
8647f2bfb9bSMedicine Yeh 			sizeof(buf));
8656925740dSAlexander Hansen 		return -1;
8667f2bfb9bSMedicine Yeh 	}
8671764145dSJeremy Kerr 
868769cee1aSJeremy Kerr 	if (sigint) {
869769cee1aSJeremy Kerr 		fprintf(stderr, "Received interrupt, exiting\n");
8706925740dSAlexander Hansen 		return -1;
871769cee1aSJeremy Kerr 	}
872769cee1aSJeremy Kerr 
8731cecc5deSJohnathan Mantey 	rc = get_current_time(&tv);
8741cecc5deSJohnathan Mantey 	if (rc) {
8751cecc5deSJohnathan Mantey 		warn("Failed to read current time");
8766925740dSAlexander Hansen 		return -1;
8771cecc5deSJohnathan Mantey 	}
8781cecc5deSJohnathan Mantey 
8791cecc5deSJohnathan Mantey 	timeout = get_poll_timeout(console, &tv);
8801cecc5deSJohnathan Mantey 
8816925740dSAlexander Hansen 	rc = poll(console->pollfds, console->n_pollers + MAX_INTERNAL_POLLFD,
8825c359cc6SAndrew Jeffery 		  (int)timeout);
8831cecc5deSJohnathan Mantey 
884d831f960SJeremy Kerr 	if (rc < 0) {
885769cee1aSJeremy Kerr 		if (errno == EINTR) {
8866925740dSAlexander Hansen 			return 0;
8870b7b0477SAndrew Jeffery 		}
888d831f960SJeremy Kerr 		warn("poll error");
8896925740dSAlexander Hansen 		return -1;
890769cee1aSJeremy Kerr 	}
891d831f960SJeremy Kerr 
892329a35f5SJeremy Kerr 	/* process internal fd first */
893329a35f5SJeremy Kerr 	if (console->pollfds[console->n_pollers].revents) {
89430ea6385SAndrew Jeffery 		rc = read(console->tty.fd, buf, sizeof(buf));
895d831f960SJeremy Kerr 		if (rc <= 0) {
896d831f960SJeremy Kerr 			warn("Error reading from tty device");
8976925740dSAlexander Hansen 			return -1;
898d831f960SJeremy Kerr 		}
899f733c85aSJeremy Kerr 		rc = ringbuffer_queue(console->rb, buf, rc);
9002834c5b1SAndrew Jeffery 		if (rc) {
9016925740dSAlexander Hansen 			return -1;
902d831f960SJeremy Kerr 		}
9032834c5b1SAndrew Jeffery 	}
904d831f960SJeremy Kerr 
905f9c8f6caSCheng C Yang 	if (console->pollfds[console->n_pollers + 1].revents) {
906f9c8f6caSCheng C Yang 		sd_bus_process(console->bus, NULL);
907f9c8f6caSCheng C Yang 	}
908f9c8f6caSCheng C Yang 
909329a35f5SJeremy Kerr 	/* ... and then the pollers */
9101cecc5deSJohnathan Mantey 	rc = call_pollers(console, &tv);
9112834c5b1SAndrew Jeffery 	if (rc) {
9126925740dSAlexander Hansen 		return -1;
9136925740dSAlexander Hansen 	}
9146925740dSAlexander Hansen 	return 0;
9156925740dSAlexander Hansen }
9166925740dSAlexander Hansen 
9176925740dSAlexander Hansen int run_console(struct console *console)
9186925740dSAlexander Hansen {
9196925740dSAlexander Hansen 	sighandler_t sighandler_save = signal(SIGINT, sighandler);
9206925740dSAlexander Hansen 	ssize_t rc = 0;
9216925740dSAlexander Hansen 
9226925740dSAlexander Hansen 	for (;;) {
9236925740dSAlexander Hansen 		rc = run_console_iteration(console);
9246925740dSAlexander Hansen 		if (rc) {
925769cee1aSJeremy Kerr 			break;
9261a0e03b4SJeremy Kerr 		}
9272834c5b1SAndrew Jeffery 	}
928769cee1aSJeremy Kerr 
929769cee1aSJeremy Kerr 	signal(SIGINT, sighandler_save);
930f9c8f6caSCheng C Yang 	sd_bus_unref(console->bus);
931769cee1aSJeremy Kerr 
932769cee1aSJeremy Kerr 	return rc ? -1 : 0;
9331a0e03b4SJeremy Kerr }
934d831f960SJeremy Kerr static const struct option options[] = {
935d66195c1SJeremy Kerr 	{ "config", required_argument, 0, 'c' },
936954be0fbSAndrew Jeffery 	{ "console-id", required_argument, 0, 'i' },
937f5858b5bSJoel Stanley 	{ 0, 0, 0, 0 },
938d831f960SJeremy Kerr };
939d831f960SJeremy Kerr 
940d831f960SJeremy Kerr int main(int argc, char **argv)
941d831f960SJeremy Kerr {
9427f2bfb9bSMedicine Yeh 	size_t buffer_size = default_buffer_size;
943d66195c1SJeremy Kerr 	const char *config_filename = NULL;
9446221ce94SVishwanatha Subbanna 	const char *config_tty_kname = NULL;
9457f2bfb9bSMedicine Yeh 	const char *buffer_size_str = NULL;
946954be0fbSAndrew Jeffery 	const char *console_id = NULL;
9471a0e03b4SJeremy Kerr 	struct console *console;
948d66195c1SJeremy Kerr 	struct config *config;
949d66195c1SJeremy Kerr 	int rc;
950d831f960SJeremy Kerr 
951d831f960SJeremy Kerr 	for (;;) {
952b70f8713SAndrew Jeffery 		int c;
953b70f8713SAndrew Jeffery 		int idx;
954d831f960SJeremy Kerr 
955954be0fbSAndrew Jeffery 		c = getopt_long(argc, argv, "c:i:", options, &idx);
9562834c5b1SAndrew Jeffery 		if (c == -1) {
957d831f960SJeremy Kerr 			break;
9582834c5b1SAndrew Jeffery 		}
959d831f960SJeremy Kerr 
960d831f960SJeremy Kerr 		switch (c) {
961d66195c1SJeremy Kerr 		case 'c':
962d66195c1SJeremy Kerr 			config_filename = optarg;
963d831f960SJeremy Kerr 			break;
964954be0fbSAndrew Jeffery 		case 'i':
965954be0fbSAndrew Jeffery 			console_id = optarg;
966954be0fbSAndrew Jeffery 			break;
967d831f960SJeremy Kerr 		case 'h':
968d831f960SJeremy Kerr 		case '?':
969d831f960SJeremy Kerr 			usage(argv[0]);
970d66195c1SJeremy Kerr 			return EXIT_SUCCESS;
971d831f960SJeremy Kerr 		}
972d831f960SJeremy Kerr 	}
973d831f960SJeremy Kerr 
9742834c5b1SAndrew Jeffery 	if (optind < argc) {
9756221ce94SVishwanatha Subbanna 		config_tty_kname = argv[optind];
9762834c5b1SAndrew Jeffery 	}
9776221ce94SVishwanatha Subbanna 
9787f2bfb9bSMedicine Yeh 	config = config_init(config_filename);
9797f2bfb9bSMedicine Yeh 
980d66195c1SJeremy Kerr 	console = malloc(sizeof(struct console));
981d66195c1SJeremy Kerr 	memset(console, 0, sizeof(*console));
982a72711afSAndrew Jeffery 	console->pollfds =
983a72711afSAndrew Jeffery 		calloc(MAX_INTERNAL_POLLFD, sizeof(*console->pollfds));
9847f2bfb9bSMedicine Yeh 	buffer_size_str = config_get_value(config, "ringbuffer-size");
9857f2bfb9bSMedicine Yeh 	if (buffer_size_str) {
9867f2bfb9bSMedicine Yeh 		rc = config_parse_bytesize(buffer_size_str, &buffer_size);
9877f2bfb9bSMedicine Yeh 		if (rc) {
9887f2bfb9bSMedicine Yeh 			warn("Invalid ringbuffer-size. Default to %zukB",
9897f2bfb9bSMedicine Yeh 			     buffer_size >> 10);
9907f2bfb9bSMedicine Yeh 		}
9917f2bfb9bSMedicine Yeh 	}
992f733c85aSJeremy Kerr 	console->rb = ringbuffer_init(buffer_size);
9933f8d5bebSAndrew Jeffery 	if (!console->rb) {
9943f8d5bebSAndrew Jeffery 		rc = -1;
9953f8d5bebSAndrew Jeffery 		goto out_config_fini;
9963f8d5bebSAndrew Jeffery 	}
997329a35f5SJeremy Kerr 
998954be0fbSAndrew Jeffery 	if (set_socket_info(console, config, console_id)) {
99929c59c44SAndrew Jeffery 		rc = -1;
10003f8d5bebSAndrew Jeffery 		goto out_ringbuffer_fini;
1001b14ca19cSNinad Palsule 	}
1002b14ca19cSNinad Palsule 
10037dc08baaSZev Weiss 	uart_routing_init(config);
10047dc08baaSZev Weiss 
1005d769eecfSAndrew Jeffery 	rc = tty_init(console, config, config_tty_kname);
10062834c5b1SAndrew Jeffery 	if (rc) {
10073f8d5bebSAndrew Jeffery 		goto out_ringbuffer_fini;
10082834c5b1SAndrew Jeffery 	}
1009d831f960SJeremy Kerr 
1010f9c8f6caSCheng C Yang 	dbus_init(console, config);
1011f9c8f6caSCheng C Yang 
1012d47963e5SJeremy Kerr 	handlers_init(console, config);
1013d831f960SJeremy Kerr 
10141a0e03b4SJeremy Kerr 	rc = run_console(console);
1015d831f960SJeremy Kerr 
10161a0e03b4SJeremy Kerr 	handlers_fini(console);
1017d831f960SJeremy Kerr 
101830ea6385SAndrew Jeffery 	tty_fini(console);
101930ea6385SAndrew Jeffery 
10203f8d5bebSAndrew Jeffery out_ringbuffer_fini:
10213f8d5bebSAndrew Jeffery 	ringbuffer_fini(console->rb);
10223f8d5bebSAndrew Jeffery 
1023d66195c1SJeremy Kerr out_config_fini:
1024d66195c1SJeremy Kerr 	config_fini(config);
1025d66195c1SJeremy Kerr 
102689ea8198SJeremy Kerr 	free(console->pollers);
102789ea8198SJeremy Kerr 	free(console->pollfds);
10281a0e03b4SJeremy Kerr 	free(console);
1029d831f960SJeremy Kerr 
1030d831f960SJeremy Kerr 	return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
1031d831f960SJeremy Kerr }
1032