xref: /openbmc/obmc-console/console-server.c (revision 7f2bfb9b9f760f9599ce24e772e78f9ade43cc0f)
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 
45*7f2bfb9bSMedicine Yeh /* default size of the shared backlog ringbuffer */
46*7f2bfb9bSMedicine Yeh const size_t default_buffer_size = 128ul * 1024ul;
47f733c85aSJeremy Kerr 
48769cee1aSJeremy Kerr /* state shared with the signal handler */
49769cee1aSJeremy Kerr static bool 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);
17717217845SJeremy Kerr 	free(tty_device_tty_dir);
17817217845SJeremy Kerr 	free(tty_device_reldir);
17945ad7676SYi Li 	free(tty_path_input);
18045ad7676SYi Li 	free(tty_path_input_real);
18117217845SJeremy Kerr 	return rc;
18217217845SJeremy Kerr }
18317217845SJeremy Kerr 
1841a0e03b4SJeremy Kerr static int tty_set_sysfs_attr(struct console *console, const char *name,
185957818b4SJeremy Kerr 			      int value)
186957818b4SJeremy Kerr {
187957818b4SJeremy Kerr 	char *path;
188957818b4SJeremy Kerr 	FILE *fp;
189957818b4SJeremy Kerr 	int rc;
190957818b4SJeremy Kerr 
19130ea6385SAndrew Jeffery 	assert(console->tty.type == TTY_DEVICE_VUART);
19230ea6385SAndrew Jeffery 
19330ea6385SAndrew Jeffery 	if (!console->tty.vuart.sysfs_devnode) {
19430ea6385SAndrew Jeffery 		return -1;
19530ea6385SAndrew Jeffery 	}
19630ea6385SAndrew Jeffery 
19730ea6385SAndrew Jeffery 	rc = asprintf(&path, "%s/%s", console->tty.vuart.sysfs_devnode, name);
1982834c5b1SAndrew Jeffery 	if (rc < 0) {
199957818b4SJeremy Kerr 		return -1;
2002834c5b1SAndrew Jeffery 	}
201957818b4SJeremy Kerr 
202957818b4SJeremy Kerr 	fp = fopen(path, "w");
203957818b4SJeremy Kerr 	if (!fp) {
204a72711afSAndrew Jeffery 		warn("Can't access attribute %s on device %s", name,
20530ea6385SAndrew Jeffery 		     console->tty.kname);
206957818b4SJeremy Kerr 		rc = -1;
207957818b4SJeremy Kerr 		goto out_free;
208957818b4SJeremy Kerr 	}
209957818b4SJeremy Kerr 	setvbuf(fp, NULL, _IONBF, 0);
210957818b4SJeremy Kerr 
211957818b4SJeremy Kerr 	rc = fprintf(fp, "0x%x", value);
2122834c5b1SAndrew Jeffery 	if (rc < 0) {
213a72711afSAndrew Jeffery 		warn("Error writing to %s attribute of device %s", name,
21430ea6385SAndrew Jeffery 		     console->tty.kname);
2152834c5b1SAndrew Jeffery 	}
216957818b4SJeremy Kerr 	fclose(fp);
217957818b4SJeremy Kerr 
218957818b4SJeremy Kerr out_free:
219957818b4SJeremy Kerr 	free(path);
220957818b4SJeremy Kerr 	return rc;
221957818b4SJeremy Kerr }
222957818b4SJeremy Kerr 
223d831f960SJeremy Kerr /**
224c7fbcd48SBenjamin Fair  * Set termios attributes on the console tty.
22554e9569dSJeremy Kerr  */
226e258e51fSNinad Palsule void tty_init_termios(struct console *console)
22754e9569dSJeremy Kerr {
22854e9569dSJeremy Kerr 	struct termios termios;
22954e9569dSJeremy Kerr 	int rc;
23054e9569dSJeremy Kerr 
23130ea6385SAndrew Jeffery 	rc = tcgetattr(console->tty.fd, &termios);
23254e9569dSJeremy Kerr 	if (rc) {
23354e9569dSJeremy Kerr 		warn("Can't read tty termios");
23454e9569dSJeremy Kerr 		return;
23554e9569dSJeremy Kerr 	}
23654e9569dSJeremy Kerr 
23730ea6385SAndrew Jeffery 	if (console->tty.type == TTY_DEVICE_UART && console->tty.uart.baud) {
23830ea6385SAndrew Jeffery 		if (cfsetspeed(&termios, console->tty.uart.baud) < 0) {
23930ea6385SAndrew Jeffery 			warn("Couldn't set speeds for %s", console->tty.kname);
240c7fbcd48SBenjamin Fair 		}
2412834c5b1SAndrew Jeffery 	}
242c7fbcd48SBenjamin Fair 
243c7fbcd48SBenjamin Fair 	/* Set console to raw mode: we don't want any processing to occur on
244c7fbcd48SBenjamin Fair 	 * the underlying terminal input/output.
245c7fbcd48SBenjamin Fair 	 */
24654e9569dSJeremy Kerr 	cfmakeraw(&termios);
247c7fbcd48SBenjamin Fair 
24830ea6385SAndrew Jeffery 	rc = tcsetattr(console->tty.fd, TCSANOW, &termios);
2492834c5b1SAndrew Jeffery 	if (rc) {
25030ea6385SAndrew Jeffery 		warn("Can't set terminal options for %s", console->tty.kname);
25154e9569dSJeremy Kerr 	}
2522834c5b1SAndrew Jeffery }
25354e9569dSJeremy Kerr 
25454e9569dSJeremy Kerr /**
255d831f960SJeremy Kerr  * Open and initialise the serial device
256d831f960SJeremy Kerr  */
25730ea6385SAndrew Jeffery static void tty_init_vuart_io(struct console *console)
258d831f960SJeremy Kerr {
25930ea6385SAndrew Jeffery 	assert(console->tty.type == TTY_DEVICE_VUART);
26030ea6385SAndrew Jeffery 
26130ea6385SAndrew Jeffery 	if (console->tty.vuart.sirq) {
26230ea6385SAndrew Jeffery 		tty_set_sysfs_attr(console, "sirq", console->tty.vuart.sirq);
2632834c5b1SAndrew Jeffery 	}
264957818b4SJeremy Kerr 
26530ea6385SAndrew Jeffery 	if (console->tty.vuart.lpc_addr) {
26630ea6385SAndrew Jeffery 		tty_set_sysfs_attr(console, "lpc_address",
26730ea6385SAndrew Jeffery 				   console->tty.vuart.lpc_addr);
26830ea6385SAndrew Jeffery 	}
26930ea6385SAndrew Jeffery }
27030ea6385SAndrew Jeffery 
27130ea6385SAndrew Jeffery static int tty_init_io(struct console *console)
27230ea6385SAndrew Jeffery {
27330ea6385SAndrew Jeffery 	console->tty.fd = open(console->tty.dev, O_RDWR);
27430ea6385SAndrew Jeffery 	if (console->tty.fd <= 0) {
27530ea6385SAndrew Jeffery 		warn("Can't open tty %s", console->tty.dev);
276d831f960SJeremy Kerr 		return -1;
277d831f960SJeremy Kerr 	}
278d831f960SJeremy Kerr 
279d831f960SJeremy Kerr 	/* Disable character delay. We may want to later enable this when
280d831f960SJeremy Kerr 	 * we detect larger amounts of data
281d831f960SJeremy Kerr 	 */
28230ea6385SAndrew Jeffery 	fcntl(console->tty.fd, F_SETFL, FNDELAY);
283d831f960SJeremy Kerr 
28454e9569dSJeremy Kerr 	tty_init_termios(console);
28554e9569dSJeremy Kerr 
28630ea6385SAndrew Jeffery 	console->pollfds[console->n_pollers].fd = console->tty.fd;
287329a35f5SJeremy Kerr 	console->pollfds[console->n_pollers].events = POLLIN;
288329a35f5SJeremy Kerr 
289d831f960SJeremy Kerr 	return 0;
290d831f960SJeremy Kerr }
291d831f960SJeremy Kerr 
29230ea6385SAndrew Jeffery static int tty_init_vuart(struct console *console, struct config *config)
293d66195c1SJeremy Kerr {
294fd883a88SAndrew Jeffery 	unsigned long parsed;
295d66195c1SJeremy Kerr 	const char *val;
296d66195c1SJeremy Kerr 	char *endp;
29730ea6385SAndrew Jeffery 
29830ea6385SAndrew Jeffery 	assert(console->tty.type == TTY_DEVICE_VUART);
299d66195c1SJeremy Kerr 
300d66195c1SJeremy Kerr 	val = config_get_value(config, "lpc-address");
301d66195c1SJeremy Kerr 	if (val) {
302fd883a88SAndrew Jeffery 		errno = 0;
303fd883a88SAndrew Jeffery 		parsed = strtoul(val, &endp, 0);
304fd883a88SAndrew Jeffery 		if (parsed == ULONG_MAX && errno == ERANGE) {
305fd883a88SAndrew Jeffery 			warn("Cannot interpret 'lpc-address' value as an unsigned long: '%s'",
306fd883a88SAndrew Jeffery 			     val);
307fd883a88SAndrew Jeffery 			return -1;
308fd883a88SAndrew Jeffery 		}
309fd883a88SAndrew Jeffery 
310fd883a88SAndrew Jeffery 		if (parsed > UINT16_MAX) {
311fd883a88SAndrew Jeffery 			warn("Invalid LPC address '%s'", val);
312fd883a88SAndrew Jeffery 			return -1;
313fd883a88SAndrew Jeffery 		}
314fd883a88SAndrew Jeffery 
31530ea6385SAndrew Jeffery 		console->tty.vuart.lpc_addr = (uint16_t)parsed;
316d66195c1SJeremy Kerr 		if (endp == optarg) {
317d66195c1SJeremy Kerr 			warn("Invalid LPC address: '%s'", val);
318d66195c1SJeremy Kerr 			return -1;
319d66195c1SJeremy Kerr 		}
320d66195c1SJeremy Kerr 	}
321d66195c1SJeremy Kerr 
322d66195c1SJeremy Kerr 	val = config_get_value(config, "sirq");
323d66195c1SJeremy Kerr 	if (val) {
324fd883a88SAndrew Jeffery 		errno = 0;
325fd883a88SAndrew Jeffery 		parsed = strtoul(val, &endp, 0);
326fd883a88SAndrew Jeffery 		if (parsed == ULONG_MAX && errno == ERANGE) {
327fd883a88SAndrew Jeffery 			warn("Cannot interpret 'sirq' value as an unsigned long: '%s'",
328fd883a88SAndrew Jeffery 			     val);
329fd883a88SAndrew Jeffery 		}
330fd883a88SAndrew Jeffery 
3312834c5b1SAndrew Jeffery 		if (parsed > 16) {
332fd883a88SAndrew Jeffery 			warn("Invalid LPC SERIRQ: '%s'", val);
3332834c5b1SAndrew Jeffery 		}
334fd883a88SAndrew Jeffery 
33530ea6385SAndrew Jeffery 		console->tty.vuart.sirq = (int)parsed;
3362834c5b1SAndrew Jeffery 		if (endp == optarg) {
337d66195c1SJeremy Kerr 			warn("Invalid sirq: '%s'", val);
338d66195c1SJeremy Kerr 		}
3392834c5b1SAndrew Jeffery 	}
340d66195c1SJeremy Kerr 
34130ea6385SAndrew Jeffery 	return 0;
3422834c5b1SAndrew Jeffery }
343c7fbcd48SBenjamin Fair 
34430ea6385SAndrew Jeffery static int tty_init(struct console *console, struct config *config,
34530ea6385SAndrew Jeffery 		    const char *tty_arg)
34630ea6385SAndrew Jeffery {
34730ea6385SAndrew Jeffery 	const char *val;
34830ea6385SAndrew Jeffery 	int rc;
34930ea6385SAndrew Jeffery 
350d769eecfSAndrew Jeffery 	if (tty_arg) {
35130ea6385SAndrew Jeffery 		console->tty.kname = tty_arg;
352d769eecfSAndrew Jeffery 	} else if ((val = config_get_value(config, "upstream-tty"))) {
35330ea6385SAndrew Jeffery 		console->tty.kname = val;
354d769eecfSAndrew Jeffery 	} else {
355d66195c1SJeremy Kerr 		warnx("Error: No TTY device specified");
356d66195c1SJeremy Kerr 		return -1;
357d66195c1SJeremy Kerr 	}
358d66195c1SJeremy Kerr 
359d66195c1SJeremy Kerr 	rc = tty_find_device(console);
3602834c5b1SAndrew Jeffery 	if (rc) {
361d66195c1SJeremy Kerr 		return rc;
3622834c5b1SAndrew Jeffery 	}
363d66195c1SJeremy Kerr 
36430ea6385SAndrew Jeffery 	switch (console->tty.type) {
36530ea6385SAndrew Jeffery 	case TTY_DEVICE_VUART:
36630ea6385SAndrew Jeffery 		rc = tty_init_vuart(console, config);
36730ea6385SAndrew Jeffery 		if (rc) {
368d66195c1SJeremy Kerr 			return rc;
369d66195c1SJeremy Kerr 		}
370d66195c1SJeremy Kerr 
37130ea6385SAndrew Jeffery 		tty_init_vuart_io(console);
37230ea6385SAndrew Jeffery 		break;
37330ea6385SAndrew Jeffery 	case TTY_DEVICE_UART:
37430ea6385SAndrew Jeffery 		val = config_get_value(config, "baud");
37530ea6385SAndrew Jeffery 		if (val) {
37630ea6385SAndrew Jeffery 			if (config_parse_baud(&console->tty.uart.baud, val)) {
37730ea6385SAndrew Jeffery 				warnx("Invalid baud rate: '%s'", val);
37830ea6385SAndrew Jeffery 			}
37930ea6385SAndrew Jeffery 		}
38030ea6385SAndrew Jeffery 		break;
38130ea6385SAndrew Jeffery 	case TTY_DEVICE_PTY:
38230ea6385SAndrew Jeffery 		break;
38330ea6385SAndrew Jeffery 	case TTY_DEVICE_UNDEFINED:
38430ea6385SAndrew Jeffery 	default:
38530ea6385SAndrew Jeffery 		warnx("Cannot configure unrecognised TTY device");
38630ea6385SAndrew Jeffery 		return -1;
38730ea6385SAndrew Jeffery 	}
38830ea6385SAndrew Jeffery 
38930ea6385SAndrew Jeffery 	return tty_init_io(console);
39030ea6385SAndrew Jeffery }
39130ea6385SAndrew Jeffery 
39230ea6385SAndrew Jeffery static void tty_fini(struct console *console)
39330ea6385SAndrew Jeffery {
39430ea6385SAndrew Jeffery 	if (console->tty.type == TTY_DEVICE_VUART) {
39530ea6385SAndrew Jeffery 		free(console->tty.vuart.sysfs_devnode);
39630ea6385SAndrew Jeffery 	}
39730ea6385SAndrew Jeffery 	free(console->tty.dev);
39830ea6385SAndrew Jeffery }
39930ea6385SAndrew Jeffery 
4007dc08baaSZev Weiss static int write_to_path(const char *path, const char *data)
4017dc08baaSZev Weiss {
4027dc08baaSZev Weiss 	int rc = 0;
4037dc08baaSZev Weiss 	FILE *f = fopen(path, "w");
4047dc08baaSZev Weiss 	if (!f) {
4057dc08baaSZev Weiss 		return -1;
4067dc08baaSZev Weiss 	}
4077dc08baaSZev Weiss 
4087dc08baaSZev Weiss 	if (fprintf(f, "%s", data) < 0) {
4097dc08baaSZev Weiss 		rc = -1;
4107dc08baaSZev Weiss 	}
4117dc08baaSZev Weiss 
4127dc08baaSZev Weiss 	if (fclose(f)) {
4137dc08baaSZev Weiss 		rc = -1;
4147dc08baaSZev Weiss 	}
4157dc08baaSZev Weiss 
4167dc08baaSZev Weiss 	return rc;
4177dc08baaSZev Weiss }
4187dc08baaSZev Weiss 
4197dc08baaSZev Weiss #define ASPEED_UART_ROUTING_PATTERN                                            \
4207dc08baaSZev Weiss 	"/sys/bus/platform/drivers/aspeed-uart-routing/*.uart-routing"
4217dc08baaSZev Weiss 
4227dc08baaSZev Weiss static void uart_routing_init(struct config *config)
4237dc08baaSZev Weiss {
4247dc08baaSZev Weiss 	const char *muxcfg;
4257dc08baaSZev Weiss 	const char *p;
4267dc08baaSZev Weiss 	size_t buflen;
4277dc08baaSZev Weiss 	char *sink;
4287dc08baaSZev Weiss 	char *source;
4297dc08baaSZev Weiss 	char *muxdir;
4307dc08baaSZev Weiss 	char *path;
4317dc08baaSZev Weiss 	glob_t globbuf;
4327dc08baaSZev Weiss 
4337dc08baaSZev Weiss 	muxcfg = config_get_value(config, "aspeed-uart-routing");
4347dc08baaSZev Weiss 	if (!muxcfg) {
4357dc08baaSZev Weiss 		return;
4367dc08baaSZev Weiss 	}
4377dc08baaSZev Weiss 
4387dc08baaSZev Weiss 	/* Find the driver's sysfs directory */
4397dc08baaSZev Weiss 	if (glob(ASPEED_UART_ROUTING_PATTERN, GLOB_ERR | GLOB_NOSORT, NULL,
4407dc08baaSZev Weiss 		 &globbuf) != 0) {
4417dc08baaSZev Weiss 		warn("Couldn't find uart-routing driver directory, cannot apply config");
4427dc08baaSZev Weiss 		return;
4437dc08baaSZev Weiss 	}
4447dc08baaSZev Weiss 	if (globbuf.gl_pathc != 1) {
4457dc08baaSZev Weiss 		warnx("Found %zd uart-routing driver directories, cannot apply config",
4467dc08baaSZev Weiss 		      globbuf.gl_pathc);
4477dc08baaSZev Weiss 		goto out_free_glob;
4487dc08baaSZev Weiss 	}
4497dc08baaSZev Weiss 	muxdir = globbuf.gl_pathv[0];
4507dc08baaSZev Weiss 
4517dc08baaSZev Weiss 	/*
4527dc08baaSZev Weiss 	 * Rather than faff about tracking a bunch of separate buffer sizes,
4537dc08baaSZev Weiss 	 * just use one (worst-case) size for all of them -- +2 for a trailing
4547dc08baaSZev Weiss 	 * NUL and a '/' separator to construct the sysfs file path.
4557dc08baaSZev Weiss 	 */
4567dc08baaSZev Weiss 	buflen = strlen(muxdir) + strlen(muxcfg) + 2;
4577dc08baaSZev Weiss 
4587dc08baaSZev Weiss 	sink = malloc(buflen);
4597dc08baaSZev Weiss 	source = malloc(buflen);
4607dc08baaSZev Weiss 	path = malloc(buflen);
4617dc08baaSZev Weiss 	if (!path || !sink || !source) {
4627dc08baaSZev Weiss 		warnx("Out of memory applying uart routing config");
4637dc08baaSZev Weiss 		goto out_free_bufs;
4647dc08baaSZev Weiss 	}
4657dc08baaSZev Weiss 
4667dc08baaSZev Weiss 	p = muxcfg;
4677dc08baaSZev Weiss 	while (*p) {
4687dc08baaSZev Weiss 		ssize_t bytes_scanned;
4697dc08baaSZev Weiss 
4707dc08baaSZev Weiss 		if (sscanf(p, " %[^:/ \t]:%[^: \t] %zn", sink, source,
4717dc08baaSZev Weiss 			   &bytes_scanned) != 2) {
4727dc08baaSZev Weiss 			warnx("Invalid syntax in aspeed uart config: '%s' not applied",
4737dc08baaSZev Weiss 			      p);
4747dc08baaSZev Weiss 			break;
4757dc08baaSZev Weiss 		}
4767dc08baaSZev Weiss 		p += bytes_scanned;
4777dc08baaSZev Weiss 
4787dc08baaSZev Weiss 		/*
4797dc08baaSZev Weiss 		 * Check that the sink name looks reasonable before proceeding
4807dc08baaSZev Weiss 		 * (there are other writable files in the same directory that
4817dc08baaSZev Weiss 		 * we shouldn't be touching, such as 'driver_override' and
4827dc08baaSZev Weiss 		 * 'uevent').
4837dc08baaSZev Weiss 		 */
4847dc08baaSZev Weiss 		if (strncmp(sink, "io", strlen("io")) != 0 &&
4857dc08baaSZev Weiss 		    strncmp(sink, "uart", strlen("uart")) != 0) {
4867dc08baaSZev Weiss 			warnx("Skipping invalid uart routing name '%s' (must be ioN or uartN)",
4877dc08baaSZev Weiss 			      sink);
4887dc08baaSZev Weiss 			continue;
4897dc08baaSZev Weiss 		}
4907dc08baaSZev Weiss 
4917dc08baaSZev Weiss 		snprintf(path, buflen, "%s/%s", muxdir, sink);
4927dc08baaSZev Weiss 		if (write_to_path(path, source)) {
4937dc08baaSZev Weiss 			warn("Failed to apply uart-routing config '%s:%s'",
4947dc08baaSZev Weiss 			     sink, source);
4957dc08baaSZev Weiss 		}
4967dc08baaSZev Weiss 	}
4977dc08baaSZev Weiss 
4987dc08baaSZev Weiss out_free_bufs:
4997dc08baaSZev Weiss 	free(path);
5007dc08baaSZev Weiss 	free(source);
5017dc08baaSZev Weiss 	free(sink);
5027dc08baaSZev Weiss out_free_glob:
5037dc08baaSZev Weiss 	globfree(&globbuf);
5047dc08baaSZev Weiss }
5057dc08baaSZev Weiss 
5061a0e03b4SJeremy Kerr int console_data_out(struct console *console, const uint8_t *data, size_t len)
507d831f960SJeremy Kerr {
50830ea6385SAndrew Jeffery 	return write_buf_to_fd(console->tty.fd, data, len);
509d831f960SJeremy Kerr }
510d831f960SJeremy Kerr 
5115ba20b5bSNinad Palsule /* Prepare a socket name */
512954be0fbSAndrew Jeffery static int set_socket_info(struct console *console, struct config *config,
513954be0fbSAndrew Jeffery 			   const char *console_id)
514b14ca19cSNinad Palsule {
515b14ca19cSNinad Palsule 	ssize_t len;
516b14ca19cSNinad Palsule 
5175ba20b5bSNinad Palsule 	/* Get console id */
5185ba20b5bSNinad Palsule 	console->console_id = config_resolve_console_id(config, console_id);
519954be0fbSAndrew Jeffery 
520b14ca19cSNinad Palsule 	/* Get the socket name/path */
5215ba20b5bSNinad Palsule 	len = console_socket_path(console->socket_name, console->console_id);
522b14ca19cSNinad Palsule 	if (len < 0) {
523b14ca19cSNinad Palsule 		warn("Failed to set socket path: %s", strerror(errno));
524b14ca19cSNinad Palsule 		return EXIT_FAILURE;
525b14ca19cSNinad Palsule 	}
526b14ca19cSNinad Palsule 
527b14ca19cSNinad Palsule 	/* Socket name is not a null terminated string hence save the length */
528b14ca19cSNinad Palsule 	console->socket_name_len = len;
529b14ca19cSNinad Palsule 
530b14ca19cSNinad Palsule 	return 0;
531b14ca19cSNinad Palsule }
532b14ca19cSNinad Palsule 
533d47963e5SJeremy Kerr static void handlers_init(struct console *console, struct config *config)
534d831f960SJeremy Kerr {
535b70f8713SAndrew Jeffery 	/* NOLINTBEGIN(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) */
536b70f8713SAndrew Jeffery 	extern struct handler *__start_handlers;
537b70f8713SAndrew Jeffery 	extern struct handler *__stop_handlers;
538b70f8713SAndrew Jeffery 	/* NOLINTEND(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) */
5391a0e03b4SJeremy Kerr 	struct handler *handler;
540b70f8713SAndrew Jeffery 	int i;
541b70f8713SAndrew Jeffery 	int rc;
542d831f960SJeremy Kerr 
5431a0e03b4SJeremy Kerr 	console->n_handlers = &__stop_handlers - &__start_handlers;
5441a0e03b4SJeremy Kerr 	console->handlers = &__start_handlers;
545d831f960SJeremy Kerr 
5465c359cc6SAndrew Jeffery 	printf("%ld handler%s\n", console->n_handlers,
5471a0e03b4SJeremy Kerr 	       console->n_handlers == 1 ? "" : "s");
548d831f960SJeremy Kerr 
5491a0e03b4SJeremy Kerr 	for (i = 0; i < console->n_handlers; i++) {
5501a0e03b4SJeremy Kerr 		handler = console->handlers[i];
5511a0e03b4SJeremy Kerr 
552021b91f0SJeremy Kerr 		rc = 0;
5532834c5b1SAndrew Jeffery 		if (handler->init) {
554021b91f0SJeremy Kerr 			rc = handler->init(handler, console, config);
5552834c5b1SAndrew Jeffery 		}
556021b91f0SJeremy Kerr 
557021b91f0SJeremy Kerr 		handler->active = rc == 0;
558021b91f0SJeremy Kerr 
559021b91f0SJeremy Kerr 		printf("  %s [%sactive]\n", handler->name,
560021b91f0SJeremy Kerr 		       handler->active ? "" : "in");
561d831f960SJeremy Kerr 	}
562d831f960SJeremy Kerr }
563d831f960SJeremy Kerr 
5641a0e03b4SJeremy Kerr static void handlers_fini(struct console *console)
565d831f960SJeremy Kerr {
5661a0e03b4SJeremy Kerr 	struct handler *handler;
5671a0e03b4SJeremy Kerr 	int i;
5681a0e03b4SJeremy Kerr 
5691a0e03b4SJeremy Kerr 	for (i = 0; i < console->n_handlers; i++) {
5701a0e03b4SJeremy Kerr 		handler = console->handlers[i];
5712834c5b1SAndrew Jeffery 		if (handler->fini && handler->active) {
5721a0e03b4SJeremy Kerr 			handler->fini(handler);
5731a0e03b4SJeremy Kerr 		}
574d831f960SJeremy Kerr 	}
5752834c5b1SAndrew Jeffery }
576d831f960SJeremy Kerr 
5771cecc5deSJohnathan Mantey static int get_current_time(struct timeval *tv)
5781cecc5deSJohnathan Mantey {
5791cecc5deSJohnathan Mantey 	struct timespec t;
5801cecc5deSJohnathan Mantey 	int rc;
5811cecc5deSJohnathan Mantey 
5821cecc5deSJohnathan Mantey 	/*
5831cecc5deSJohnathan Mantey 	 * We use clock_gettime(CLOCK_MONOTONIC) so we're immune to
5841cecc5deSJohnathan Mantey 	 * local time changes. However, a struct timeval is more
5851cecc5deSJohnathan Mantey 	 * convenient for calculations, so convert to that.
5861cecc5deSJohnathan Mantey 	 */
5871cecc5deSJohnathan Mantey 	rc = clock_gettime(CLOCK_MONOTONIC, &t);
5882834c5b1SAndrew Jeffery 	if (rc) {
5891cecc5deSJohnathan Mantey 		return rc;
5902834c5b1SAndrew Jeffery 	}
5911cecc5deSJohnathan Mantey 
5921cecc5deSJohnathan Mantey 	tv->tv_sec = t.tv_sec;
5931cecc5deSJohnathan Mantey 	tv->tv_usec = t.tv_nsec / 1000;
5941cecc5deSJohnathan Mantey 
5951cecc5deSJohnathan Mantey 	return 0;
5961cecc5deSJohnathan Mantey }
5971cecc5deSJohnathan Mantey 
598a72711afSAndrew Jeffery struct ringbuffer_consumer *
599a72711afSAndrew Jeffery console_ringbuffer_consumer_register(struct console *console,
600f733c85aSJeremy Kerr 				     ringbuffer_poll_fn_t poll_fn, void *data)
601d831f960SJeremy Kerr {
602f733c85aSJeremy Kerr 	return ringbuffer_consumer_register(console->rb, poll_fn, data);
603d831f960SJeremy Kerr }
604d831f960SJeremy Kerr 
60555c9712dSJeremy Kerr struct poller *console_poller_register(struct console *console,
606a72711afSAndrew Jeffery 				       struct handler *handler,
607a72711afSAndrew Jeffery 				       poller_event_fn_t poller_fn,
6081cecc5deSJohnathan Mantey 				       poller_timeout_fn_t timeout_fn, int fd,
6091cecc5deSJohnathan Mantey 				       int events, void *data)
610d831f960SJeremy Kerr {
611329a35f5SJeremy Kerr 	struct poller *poller;
6125c359cc6SAndrew Jeffery 	long n;
613329a35f5SJeremy Kerr 
614329a35f5SJeremy Kerr 	poller = malloc(sizeof(*poller));
615329a35f5SJeremy Kerr 	poller->remove = false;
616329a35f5SJeremy Kerr 	poller->handler = handler;
6171cecc5deSJohnathan Mantey 	poller->event_fn = poller_fn;
6181cecc5deSJohnathan Mantey 	poller->timeout_fn = timeout_fn;
619329a35f5SJeremy Kerr 	poller->data = data;
620329a35f5SJeremy Kerr 
621329a35f5SJeremy Kerr 	/* add one to our pollers array */
622329a35f5SJeremy Kerr 	n = console->n_pollers++;
62391b52175SAndrew Jeffery 	/*
62491b52175SAndrew Jeffery 	 * We're managing an array of pointers to aggregates, so don't warn about sizeof() on a
62591b52175SAndrew Jeffery 	 * pointer type.
62691b52175SAndrew Jeffery 	 */
62791b52175SAndrew Jeffery 	/* NOLINTBEGIN(bugprone-sizeof-expression) */
62891b52175SAndrew Jeffery 	console->pollers = reallocarray(console->pollers, console->n_pollers,
62991b52175SAndrew Jeffery 					sizeof(*console->pollers));
63091b52175SAndrew Jeffery 	/* NOLINTEND(bugprone-sizeof-expression) */
631329a35f5SJeremy Kerr 
632329a35f5SJeremy Kerr 	console->pollers[n] = poller;
633329a35f5SJeremy Kerr 
634329a35f5SJeremy Kerr 	/* increase pollfds array too  */
6354e44c790SAndrew Jeffery 	console->pollfds = reallocarray(
6364e44c790SAndrew Jeffery 		console->pollfds, (MAX_INTERNAL_POLLFD + console->n_pollers),
63791b52175SAndrew Jeffery 		sizeof(*console->pollfds));
638329a35f5SJeremy Kerr 
639329a35f5SJeremy Kerr 	/* shift the end pollfds up by one */
640a72711afSAndrew Jeffery 	memcpy(&console->pollfds[n + 1], &console->pollfds[n],
641f9c8f6caSCheng C Yang 	       sizeof(*console->pollfds) * MAX_INTERNAL_POLLFD);
642329a35f5SJeremy Kerr 
643329a35f5SJeremy Kerr 	console->pollfds[n].fd = fd;
6445c359cc6SAndrew Jeffery 	console->pollfds[n].events = (short)(events & 0x7fff);
645329a35f5SJeremy Kerr 
646329a35f5SJeremy Kerr 	return poller;
647329a35f5SJeremy Kerr }
648329a35f5SJeremy Kerr 
649a72711afSAndrew Jeffery void console_poller_unregister(struct console *console, struct poller *poller)
650329a35f5SJeremy Kerr {
651329a35f5SJeremy Kerr 	int i;
652329a35f5SJeremy Kerr 
653329a35f5SJeremy Kerr 	/* find the entry in our pollers array */
6542834c5b1SAndrew Jeffery 	for (i = 0; i < console->n_pollers; i++) {
6552834c5b1SAndrew Jeffery 		if (console->pollers[i] == poller) {
656329a35f5SJeremy Kerr 			break;
6572834c5b1SAndrew Jeffery 		}
6582834c5b1SAndrew Jeffery 	}
659329a35f5SJeremy Kerr 
660329a35f5SJeremy Kerr 	assert(i < console->n_pollers);
661329a35f5SJeremy Kerr 
662329a35f5SJeremy Kerr 	console->n_pollers--;
663329a35f5SJeremy Kerr 
66491b52175SAndrew Jeffery 	/*
66591b52175SAndrew Jeffery 	 * Remove the item from the pollers array...
66691b52175SAndrew Jeffery 	 *
66791b52175SAndrew Jeffery 	 * We're managing an array of pointers to aggregates, so don't warn about sizeof() on a
66891b52175SAndrew Jeffery 	 * pointer type.
66991b52175SAndrew Jeffery 	 */
67091b52175SAndrew Jeffery 	/* NOLINTBEGIN(bugprone-sizeof-expression) */
671329a35f5SJeremy Kerr 	memmove(&console->pollers[i], &console->pollers[i + 1],
672a72711afSAndrew Jeffery 		sizeof(*console->pollers) * (console->n_pollers - i));
673329a35f5SJeremy Kerr 
67491b52175SAndrew Jeffery 	console->pollers = reallocarray(console->pollers, console->n_pollers,
67591b52175SAndrew Jeffery 					sizeof(*console->pollers));
67691b52175SAndrew Jeffery 	/* NOLINTEND(bugprone-sizeof-expression) */
677329a35f5SJeremy Kerr 
678329a35f5SJeremy Kerr 	/* ... and the pollfds array */
679329a35f5SJeremy Kerr 	memmove(&console->pollfds[i], &console->pollfds[i + 1],
680329a35f5SJeremy Kerr 		sizeof(*console->pollfds) *
681f9c8f6caSCheng C Yang 			(MAX_INTERNAL_POLLFD + console->n_pollers - i));
682329a35f5SJeremy Kerr 
6834e44c790SAndrew Jeffery 	console->pollfds = reallocarray(
6844e44c790SAndrew Jeffery 		console->pollfds, (MAX_INTERNAL_POLLFD + console->n_pollers),
68591b52175SAndrew Jeffery 		sizeof(*console->pollfds));
686329a35f5SJeremy Kerr 
687329a35f5SJeremy Kerr 	free(poller);
688329a35f5SJeremy Kerr }
689329a35f5SJeremy Kerr 
6906b1fed27SJeremy Kerr void console_poller_set_events(struct console *console, struct poller *poller,
6916b1fed27SJeremy Kerr 			       int events)
6926b1fed27SJeremy Kerr {
6936b1fed27SJeremy Kerr 	int i;
6946b1fed27SJeremy Kerr 
6956b1fed27SJeremy Kerr 	/* find the entry in our pollers array */
6962834c5b1SAndrew Jeffery 	for (i = 0; i < console->n_pollers; i++) {
6972834c5b1SAndrew Jeffery 		if (console->pollers[i] == poller) {
6986b1fed27SJeremy Kerr 			break;
6992834c5b1SAndrew Jeffery 		}
7002834c5b1SAndrew Jeffery 	}
7016b1fed27SJeremy Kerr 
7025c359cc6SAndrew Jeffery 	console->pollfds[i].events = (short)(events & 0x7fff);
7036b1fed27SJeremy Kerr }
7046b1fed27SJeremy Kerr 
705fd048328SAndrew Jeffery void console_poller_set_timeout(struct console *console __attribute__((unused)),
706fd048328SAndrew Jeffery 				struct poller *poller, const struct timeval *tv)
7071cecc5deSJohnathan Mantey {
7081cecc5deSJohnathan Mantey 	struct timeval now;
7091cecc5deSJohnathan Mantey 	int rc;
7101cecc5deSJohnathan Mantey 
7111cecc5deSJohnathan Mantey 	rc = get_current_time(&now);
7122834c5b1SAndrew Jeffery 	if (rc) {
7131cecc5deSJohnathan Mantey 		return;
7142834c5b1SAndrew Jeffery 	}
7151cecc5deSJohnathan Mantey 
7161cecc5deSJohnathan Mantey 	timeradd(&now, tv, &poller->timeout);
7171cecc5deSJohnathan Mantey }
7181cecc5deSJohnathan Mantey 
7195c359cc6SAndrew Jeffery static long get_poll_timeout(struct console *console, struct timeval *cur_time)
7201cecc5deSJohnathan Mantey {
721b70f8713SAndrew Jeffery 	struct timeval *earliest;
722b70f8713SAndrew Jeffery 	struct timeval interval;
7231cecc5deSJohnathan Mantey 	struct poller *poller;
7241cecc5deSJohnathan Mantey 	int i;
7251cecc5deSJohnathan Mantey 
7261cecc5deSJohnathan Mantey 	earliest = NULL;
7271cecc5deSJohnathan Mantey 
7281cecc5deSJohnathan Mantey 	for (i = 0; i < console->n_pollers; i++) {
7291cecc5deSJohnathan Mantey 		poller = console->pollers[i];
7301cecc5deSJohnathan Mantey 
7311cecc5deSJohnathan Mantey 		if (poller->timeout_fn && timerisset(&poller->timeout) &&
7321cecc5deSJohnathan Mantey 		    (!earliest ||
7331cecc5deSJohnathan Mantey 		     (earliest && timercmp(&poller->timeout, earliest, <)))) {
7341cecc5deSJohnathan Mantey 			// poller is buffering data and needs the poll
7351cecc5deSJohnathan Mantey 			// function to timeout.
7361cecc5deSJohnathan Mantey 			earliest = &poller->timeout;
7371cecc5deSJohnathan Mantey 		}
7381cecc5deSJohnathan Mantey 	}
7391cecc5deSJohnathan Mantey 
7401cecc5deSJohnathan Mantey 	if (earliest) {
7411cecc5deSJohnathan Mantey 		if (timercmp(earliest, cur_time, >)) {
7421cecc5deSJohnathan Mantey 			/* recalculate the timeout period, time period has
7431cecc5deSJohnathan Mantey 			 * not elapsed */
7441cecc5deSJohnathan Mantey 			timersub(earliest, cur_time, &interval);
7451cecc5deSJohnathan Mantey 			return ((interval.tv_sec * 1000) +
7461cecc5deSJohnathan Mantey 				(interval.tv_usec / 1000));
7470b7b0477SAndrew Jeffery 		} /* return from poll immediately */
7481cecc5deSJohnathan Mantey 		return 0;
7490b7b0477SAndrew Jeffery 
7500b7b0477SAndrew Jeffery 	} /* poll indefinitely */
7511cecc5deSJohnathan Mantey 	return -1;
7521cecc5deSJohnathan Mantey }
7531cecc5deSJohnathan Mantey 
7541cecc5deSJohnathan Mantey static int call_pollers(struct console *console, struct timeval *cur_time)
755329a35f5SJeremy Kerr {
756329a35f5SJeremy Kerr 	struct poller *poller;
757329a35f5SJeremy Kerr 	struct pollfd *pollfd;
758329a35f5SJeremy Kerr 	enum poller_ret prc;
759b70f8713SAndrew Jeffery 	int i;
760b70f8713SAndrew Jeffery 	int rc;
761d831f960SJeremy Kerr 
7621a0e03b4SJeremy Kerr 	rc = 0;
7631a0e03b4SJeremy Kerr 
764329a35f5SJeremy Kerr 	/*
765329a35f5SJeremy Kerr 	 * Process poll events by iterating through the pollers and pollfds
766329a35f5SJeremy Kerr 	 * in-step, calling any pollers that we've found revents for.
767329a35f5SJeremy Kerr 	 */
768329a35f5SJeremy Kerr 	for (i = 0; i < console->n_pollers; i++) {
769329a35f5SJeremy Kerr 		poller = console->pollers[i];
770329a35f5SJeremy Kerr 		pollfd = &console->pollfds[i];
7711cecc5deSJohnathan Mantey 		prc = POLLER_OK;
7721a0e03b4SJeremy Kerr 
7731cecc5deSJohnathan Mantey 		/* process pending events... */
7741cecc5deSJohnathan Mantey 		if (pollfd->revents) {
7751cecc5deSJohnathan Mantey 			prc = poller->event_fn(poller->handler, pollfd->revents,
776329a35f5SJeremy Kerr 					       poller->data);
7772834c5b1SAndrew Jeffery 			if (prc == POLLER_EXIT) {
778329a35f5SJeremy Kerr 				rc = -1;
7792834c5b1SAndrew Jeffery 			} else if (prc == POLLER_REMOVE) {
780329a35f5SJeremy Kerr 				poller->remove = true;
781329a35f5SJeremy Kerr 			}
7822834c5b1SAndrew Jeffery 		}
783329a35f5SJeremy Kerr 
7841cecc5deSJohnathan Mantey 		if ((prc == POLLER_OK) && poller->timeout_fn &&
7851cecc5deSJohnathan Mantey 		    timerisset(&poller->timeout) &&
7861cecc5deSJohnathan Mantey 		    timercmp(&poller->timeout, cur_time, <=)) {
7871cecc5deSJohnathan Mantey 			/* One of the ringbuffer consumers is buffering the
7881cecc5deSJohnathan Mantey 			data stream. The amount of idle time the consumer
7891cecc5deSJohnathan Mantey 			desired has expired.  Process the buffered data for
7901cecc5deSJohnathan Mantey 			transmission. */
7911cecc5deSJohnathan Mantey 			timerclear(&poller->timeout);
7921cecc5deSJohnathan Mantey 			prc = poller->timeout_fn(poller->handler, poller->data);
7931cecc5deSJohnathan Mantey 			if (prc == POLLER_EXIT) {
7941cecc5deSJohnathan Mantey 				rc = -1;
7951cecc5deSJohnathan Mantey 			} else if (prc == POLLER_REMOVE) {
7961cecc5deSJohnathan Mantey 				poller->remove = true;
7971cecc5deSJohnathan Mantey 			}
7981cecc5deSJohnathan Mantey 		}
7991cecc5deSJohnathan Mantey 	}
8001cecc5deSJohnathan Mantey 
801329a35f5SJeremy Kerr 	/**
802329a35f5SJeremy Kerr 	 * Process deferred removals; restarting each time we unregister, as
803329a35f5SJeremy Kerr 	 * the array will have changed
804329a35f5SJeremy Kerr 	 */
805329a35f5SJeremy Kerr 	for (;;) {
806329a35f5SJeremy Kerr 		bool removed = false;
807329a35f5SJeremy Kerr 
808329a35f5SJeremy Kerr 		for (i = 0; i < console->n_pollers; i++) {
809329a35f5SJeremy Kerr 			poller = console->pollers[i];
810329a35f5SJeremy Kerr 			if (poller->remove) {
81155c9712dSJeremy Kerr 				console_poller_unregister(console, poller);
812329a35f5SJeremy Kerr 				removed = true;
813329a35f5SJeremy Kerr 				break;
814329a35f5SJeremy Kerr 			}
815329a35f5SJeremy Kerr 		}
8162834c5b1SAndrew Jeffery 		if (!removed) {
817329a35f5SJeremy Kerr 			break;
8181a0e03b4SJeremy Kerr 		}
8192834c5b1SAndrew Jeffery 	}
8201a0e03b4SJeremy Kerr 
8211a0e03b4SJeremy Kerr 	return rc;
8221a0e03b4SJeremy Kerr }
8231a0e03b4SJeremy Kerr 
824769cee1aSJeremy Kerr static void sighandler(int signal)
825769cee1aSJeremy Kerr {
8262834c5b1SAndrew Jeffery 	if (signal == SIGINT) {
827769cee1aSJeremy Kerr 		sigint = true;
828769cee1aSJeremy Kerr 	}
8292834c5b1SAndrew Jeffery }
830769cee1aSJeremy Kerr 
8311a0e03b4SJeremy Kerr int run_console(struct console *console)
8321a0e03b4SJeremy Kerr {
8335c359cc6SAndrew Jeffery 	sighandler_t sighandler_save = signal(SIGINT, sighandler);
8341cecc5deSJohnathan Mantey 	struct timeval tv;
8355c359cc6SAndrew Jeffery 	long timeout;
8365c359cc6SAndrew Jeffery 	ssize_t rc;
837769cee1aSJeremy Kerr 
838769cee1aSJeremy Kerr 	rc = 0;
839769cee1aSJeremy Kerr 
840d831f960SJeremy Kerr 	for (;;) {
841d831f960SJeremy Kerr 		uint8_t buf[4096];
842d831f960SJeremy Kerr 
843*7f2bfb9bSMedicine Yeh 		if (console->rb->size < sizeof(buf)) {
844*7f2bfb9bSMedicine Yeh 			fprintf(stderr,
845*7f2bfb9bSMedicine Yeh 				"Ringbuffer size should be greater than %zuB\n",
846*7f2bfb9bSMedicine Yeh 				sizeof(buf));
847*7f2bfb9bSMedicine Yeh 			rc = -1;
848*7f2bfb9bSMedicine Yeh 			break;
849*7f2bfb9bSMedicine Yeh 		}
8501764145dSJeremy Kerr 
851769cee1aSJeremy Kerr 		if (sigint) {
852769cee1aSJeremy Kerr 			fprintf(stderr, "Received interrupt, exiting\n");
853769cee1aSJeremy Kerr 			break;
854769cee1aSJeremy Kerr 		}
855769cee1aSJeremy Kerr 
8561cecc5deSJohnathan Mantey 		rc = get_current_time(&tv);
8571cecc5deSJohnathan Mantey 		if (rc) {
8581cecc5deSJohnathan Mantey 			warn("Failed to read current time");
8591cecc5deSJohnathan Mantey 			break;
8601cecc5deSJohnathan Mantey 		}
8611cecc5deSJohnathan Mantey 
8621cecc5deSJohnathan Mantey 		timeout = get_poll_timeout(console, &tv);
8631cecc5deSJohnathan Mantey 
864329a35f5SJeremy Kerr 		rc = poll(console->pollfds,
8655c359cc6SAndrew Jeffery 			  console->n_pollers + MAX_INTERNAL_POLLFD,
8665c359cc6SAndrew Jeffery 			  (int)timeout);
8671cecc5deSJohnathan Mantey 
868d831f960SJeremy Kerr 		if (rc < 0) {
869769cee1aSJeremy Kerr 			if (errno == EINTR) {
870769cee1aSJeremy Kerr 				continue;
8710b7b0477SAndrew Jeffery 			}
872d831f960SJeremy Kerr 			warn("poll error");
873769cee1aSJeremy Kerr 			break;
874769cee1aSJeremy Kerr 		}
875d831f960SJeremy Kerr 
876329a35f5SJeremy Kerr 		/* process internal fd first */
877329a35f5SJeremy Kerr 		if (console->pollfds[console->n_pollers].revents) {
87830ea6385SAndrew Jeffery 			rc = read(console->tty.fd, buf, sizeof(buf));
879d831f960SJeremy Kerr 			if (rc <= 0) {
880d831f960SJeremy Kerr 				warn("Error reading from tty device");
881769cee1aSJeremy Kerr 				rc = -1;
882769cee1aSJeremy Kerr 				break;
883d831f960SJeremy Kerr 			}
884f733c85aSJeremy Kerr 			rc = ringbuffer_queue(console->rb, buf, rc);
8852834c5b1SAndrew Jeffery 			if (rc) {
886769cee1aSJeremy Kerr 				break;
887d831f960SJeremy Kerr 			}
8882834c5b1SAndrew Jeffery 		}
889d831f960SJeremy Kerr 
890f9c8f6caSCheng C Yang 		if (console->pollfds[console->n_pollers + 1].revents) {
891f9c8f6caSCheng C Yang 			sd_bus_process(console->bus, NULL);
892f9c8f6caSCheng C Yang 		}
893f9c8f6caSCheng C Yang 
894329a35f5SJeremy Kerr 		/* ... and then the pollers */
8951cecc5deSJohnathan Mantey 		rc = call_pollers(console, &tv);
8962834c5b1SAndrew Jeffery 		if (rc) {
897769cee1aSJeremy Kerr 			break;
8981a0e03b4SJeremy Kerr 		}
8992834c5b1SAndrew Jeffery 	}
900769cee1aSJeremy Kerr 
901769cee1aSJeremy Kerr 	signal(SIGINT, sighandler_save);
902f9c8f6caSCheng C Yang 	sd_bus_unref(console->bus);
903769cee1aSJeremy Kerr 
904769cee1aSJeremy Kerr 	return rc ? -1 : 0;
9051a0e03b4SJeremy Kerr }
906d831f960SJeremy Kerr static const struct option options[] = {
907d66195c1SJeremy Kerr 	{ "config", required_argument, 0, 'c' },
908954be0fbSAndrew Jeffery 	{ "console-id", required_argument, 0, 'i' },
909f5858b5bSJoel Stanley 	{ 0, 0, 0, 0 },
910d831f960SJeremy Kerr };
911d831f960SJeremy Kerr 
912d831f960SJeremy Kerr int main(int argc, char **argv)
913d831f960SJeremy Kerr {
914*7f2bfb9bSMedicine Yeh 	size_t buffer_size = default_buffer_size;
915d66195c1SJeremy Kerr 	const char *config_filename = NULL;
9166221ce94SVishwanatha Subbanna 	const char *config_tty_kname = NULL;
917*7f2bfb9bSMedicine Yeh 	const char *buffer_size_str = NULL;
918954be0fbSAndrew Jeffery 	const char *console_id = NULL;
9191a0e03b4SJeremy Kerr 	struct console *console;
920d66195c1SJeremy Kerr 	struct config *config;
921d66195c1SJeremy Kerr 	int rc;
922d831f960SJeremy Kerr 
923d831f960SJeremy Kerr 	for (;;) {
924b70f8713SAndrew Jeffery 		int c;
925b70f8713SAndrew Jeffery 		int idx;
926d831f960SJeremy Kerr 
927954be0fbSAndrew Jeffery 		c = getopt_long(argc, argv, "c:i:", options, &idx);
9282834c5b1SAndrew Jeffery 		if (c == -1) {
929d831f960SJeremy Kerr 			break;
9302834c5b1SAndrew Jeffery 		}
931d831f960SJeremy Kerr 
932d831f960SJeremy Kerr 		switch (c) {
933d66195c1SJeremy Kerr 		case 'c':
934d66195c1SJeremy Kerr 			config_filename = optarg;
935d831f960SJeremy Kerr 			break;
936954be0fbSAndrew Jeffery 		case 'i':
937954be0fbSAndrew Jeffery 			console_id = optarg;
938954be0fbSAndrew Jeffery 			break;
939d831f960SJeremy Kerr 		case 'h':
940d831f960SJeremy Kerr 		case '?':
941d831f960SJeremy Kerr 			usage(argv[0]);
942d66195c1SJeremy Kerr 			return EXIT_SUCCESS;
943d831f960SJeremy Kerr 		}
944d831f960SJeremy Kerr 	}
945d831f960SJeremy Kerr 
9462834c5b1SAndrew Jeffery 	if (optind < argc) {
9476221ce94SVishwanatha Subbanna 		config_tty_kname = argv[optind];
9482834c5b1SAndrew Jeffery 	}
9496221ce94SVishwanatha Subbanna 
950*7f2bfb9bSMedicine Yeh 	config = config_init(config_filename);
951*7f2bfb9bSMedicine Yeh 
952d66195c1SJeremy Kerr 	console = malloc(sizeof(struct console));
953d66195c1SJeremy Kerr 	memset(console, 0, sizeof(*console));
954a72711afSAndrew Jeffery 	console->pollfds =
955a72711afSAndrew Jeffery 		calloc(MAX_INTERNAL_POLLFD, sizeof(*console->pollfds));
956*7f2bfb9bSMedicine Yeh 	buffer_size_str = config_get_value(config, "ringbuffer-size");
957*7f2bfb9bSMedicine Yeh 	if (buffer_size_str) {
958*7f2bfb9bSMedicine Yeh 		rc = config_parse_bytesize(buffer_size_str, &buffer_size);
959*7f2bfb9bSMedicine Yeh 		if (rc) {
960*7f2bfb9bSMedicine Yeh 			warn("Invalid ringbuffer-size. Default to %zukB",
961*7f2bfb9bSMedicine Yeh 			     buffer_size >> 10);
962*7f2bfb9bSMedicine Yeh 		}
963*7f2bfb9bSMedicine Yeh 	}
964f733c85aSJeremy Kerr 	console->rb = ringbuffer_init(buffer_size);
965329a35f5SJeremy Kerr 
966954be0fbSAndrew Jeffery 	if (set_socket_info(console, config, console_id)) {
96729c59c44SAndrew Jeffery 		rc = -1;
96829c59c44SAndrew Jeffery 		goto out_config_fini;
969b14ca19cSNinad Palsule 	}
970b14ca19cSNinad Palsule 
9717dc08baaSZev Weiss 	uart_routing_init(config);
9727dc08baaSZev Weiss 
973d769eecfSAndrew Jeffery 	rc = tty_init(console, config, config_tty_kname);
9742834c5b1SAndrew Jeffery 	if (rc) {
975d66195c1SJeremy Kerr 		goto out_config_fini;
9762834c5b1SAndrew Jeffery 	}
977d831f960SJeremy Kerr 
978f9c8f6caSCheng C Yang 	dbus_init(console, config);
979f9c8f6caSCheng C Yang 
980d47963e5SJeremy Kerr 	handlers_init(console, config);
981d831f960SJeremy Kerr 
9821a0e03b4SJeremy Kerr 	rc = run_console(console);
983d831f960SJeremy Kerr 
9841a0e03b4SJeremy Kerr 	handlers_fini(console);
985d831f960SJeremy Kerr 
98630ea6385SAndrew Jeffery 	tty_fini(console);
98730ea6385SAndrew Jeffery 
988d66195c1SJeremy Kerr out_config_fini:
989d66195c1SJeremy Kerr 	config_fini(config);
990d66195c1SJeremy Kerr 
99189ea8198SJeremy Kerr 	free(console->pollers);
99289ea8198SJeremy Kerr 	free(console->pollfds);
9931a0e03b4SJeremy Kerr 	free(console);
994d831f960SJeremy Kerr 
995d831f960SJeremy Kerr 	return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
996d831f960SJeremy Kerr }
997