xref: /openbmc/obmc-console/console-server.c (revision 1e04f449b7f00a7b426615533a44519149d2ea38)
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"
42*1e04f449SAlexander Hansen #include "config.h"
43d831f960SJeremy Kerr 
4430ea6385SAndrew Jeffery #define DEV_PTS_PATH "/dev/pts"
4530ea6385SAndrew Jeffery 
467f2bfb9bSMedicine Yeh /* default size of the shared backlog ringbuffer */
477f2bfb9bSMedicine Yeh const size_t default_buffer_size = 128ul * 1024ul;
48f733c85aSJeremy Kerr 
49769cee1aSJeremy Kerr /* state shared with the signal handler */
50553cb663SAndrew Jeffery static volatile sig_atomic_t sigint;
51329a35f5SJeremy Kerr 
52d831f960SJeremy Kerr static void usage(const char *progname)
53d831f960SJeremy Kerr {
54d831f960SJeremy Kerr 	fprintf(stderr,
556221ce94SVishwanatha Subbanna 		"usage: %s [options] <DEVICE>\n"
56d831f960SJeremy Kerr 		"\n"
57d831f960SJeremy Kerr 		"Options:\n"
58954be0fbSAndrew Jeffery 		"  --config <FILE>\tUse FILE for configuration\n"
59954be0fbSAndrew Jeffery 		"  --console-id <NAME>\tUse NAME in the UNIX domain socket address\n"
60d831f960SJeremy Kerr 		"",
61d831f960SJeremy Kerr 		progname);
62d831f960SJeremy Kerr }
63d831f960SJeremy Kerr 
6430ea6385SAndrew Jeffery /* populates console->tty.dev and console->tty.sysfs_devnode, using the tty kernel name */
651a0e03b4SJeremy Kerr static int tty_find_device(struct console *console)
6617217845SJeremy Kerr {
67d3cb9c22SAndrew Jeffery 	char *tty_class_device_link = NULL;
68d3cb9c22SAndrew Jeffery 	char *tty_path_input_real = NULL;
69d3cb9c22SAndrew Jeffery 	char *tty_device_tty_dir = NULL;
7030ea6385SAndrew Jeffery 	char *tty_vuart_lpc_addr = NULL;
71d3cb9c22SAndrew Jeffery 	char *tty_device_reldir = NULL;
7230ea6385SAndrew Jeffery 	char *tty_sysfs_devnode = NULL;
73d3cb9c22SAndrew Jeffery 	char *tty_kname_real = NULL;
7430ea6385SAndrew Jeffery 	char *tty_path_input = NULL;
7517217845SJeremy Kerr 	int rc;
7617217845SJeremy Kerr 
7730ea6385SAndrew Jeffery 	console->tty.type = TTY_DEVICE_UNDEFINED;
7830ea6385SAndrew Jeffery 
7930ea6385SAndrew Jeffery 	assert(console->tty.kname);
8030ea6385SAndrew Jeffery 	if (!strlen(console->tty.kname)) {
8130ea6385SAndrew Jeffery 		warnx("TTY kname must not be empty");
8230ea6385SAndrew Jeffery 		rc = -1;
8330ea6385SAndrew Jeffery 		goto out_free;
842834c5b1SAndrew Jeffery 	}
8517217845SJeremy Kerr 
8630ea6385SAndrew Jeffery 	if (console->tty.kname[0] == '/') {
8730ea6385SAndrew Jeffery 		tty_path_input = strdup(console->tty.kname);
8830ea6385SAndrew Jeffery 		if (!tty_path_input) {
8930ea6385SAndrew Jeffery 			rc = -1;
9030ea6385SAndrew Jeffery 			goto out_free;
9130ea6385SAndrew Jeffery 		}
9230ea6385SAndrew Jeffery 	} else {
9330ea6385SAndrew Jeffery 		rc = asprintf(&tty_path_input, "/dev/%s", console->tty.kname);
9430ea6385SAndrew Jeffery 		if (rc < 0) {
9530ea6385SAndrew Jeffery 			goto out_free;
9630ea6385SAndrew Jeffery 		}
9730ea6385SAndrew Jeffery 	}
9830ea6385SAndrew Jeffery 
9930ea6385SAndrew Jeffery 	/* udev may rename the tty name with a symbol link, try to resolve */
10045ad7676SYi Li 	tty_path_input_real = realpath(tty_path_input, NULL);
10145ad7676SYi Li 	if (!tty_path_input_real) {
10230ea6385SAndrew Jeffery 		warn("Can't find realpath for %s", tty_path_input);
10315792aa7SAndrew Jeffery 		rc = -1;
10445ad7676SYi Li 		goto out_free;
10545ad7676SYi Li 	}
10645ad7676SYi Li 
10730ea6385SAndrew Jeffery 	/*
10830ea6385SAndrew Jeffery 	 * Allow hooking obmc-console-server up to PTYs for testing
10930ea6385SAndrew Jeffery 	 *
11030ea6385SAndrew Jeffery 	 * https://amboar.github.io/notes/2023/05/02/testing-obmc-console-with-socat.html
11130ea6385SAndrew Jeffery 	 */
11230ea6385SAndrew Jeffery 	if (!strncmp(DEV_PTS_PATH, tty_path_input_real, strlen(DEV_PTS_PATH))) {
11330ea6385SAndrew Jeffery 		console->tty.type = TTY_DEVICE_PTY;
11430ea6385SAndrew Jeffery 		console->tty.dev = strdup(console->tty.kname);
11530ea6385SAndrew Jeffery 		rc = console->tty.dev ? 0 : -1;
11630ea6385SAndrew Jeffery 		goto out_free;
11730ea6385SAndrew Jeffery 	}
11830ea6385SAndrew Jeffery 
11945ad7676SYi Li 	tty_kname_real = basename(tty_path_input_real);
12045ad7676SYi Li 	if (!tty_kname_real) {
12130ea6385SAndrew Jeffery 		warn("Can't find real name for %s", console->tty.kname);
12215792aa7SAndrew Jeffery 		rc = -1;
12345ad7676SYi Li 		goto out_free;
12445ad7676SYi Li 	}
12545ad7676SYi Li 
126a72711afSAndrew Jeffery 	rc = asprintf(&tty_class_device_link, "/sys/class/tty/%s",
127a72711afSAndrew Jeffery 		      tty_kname_real);
1282834c5b1SAndrew Jeffery 	if (rc < 0) {
12945ad7676SYi Li 		goto out_free;
1302834c5b1SAndrew Jeffery 	}
13145ad7676SYi Li 
13217217845SJeremy Kerr 	tty_device_tty_dir = realpath(tty_class_device_link, NULL);
13345ad7676SYi Li 	if (!tty_device_tty_dir) {
13445ad7676SYi Li 		warn("Can't query sysfs for device %s", tty_kname_real);
13515792aa7SAndrew Jeffery 		rc = -1;
13617217845SJeremy Kerr 		goto out_free;
13717217845SJeremy Kerr 	}
13817217845SJeremy Kerr 
13917217845SJeremy Kerr 	rc = asprintf(&tty_device_reldir, "%s/../../", tty_device_tty_dir);
1402834c5b1SAndrew Jeffery 	if (rc < 0) {
14117217845SJeremy Kerr 		goto out_free;
1422834c5b1SAndrew Jeffery 	}
14317217845SJeremy Kerr 
14430ea6385SAndrew Jeffery 	tty_sysfs_devnode = realpath(tty_device_reldir, NULL);
14530ea6385SAndrew Jeffery 	if (!tty_sysfs_devnode) {
14645ad7676SYi Li 		warn("Can't find parent device for %s", tty_kname_real);
1472834c5b1SAndrew Jeffery 	}
14817217845SJeremy Kerr 
14930ea6385SAndrew Jeffery 	rc = asprintf(&console->tty.dev, "/dev/%s", tty_kname_real);
1502834c5b1SAndrew Jeffery 	if (rc < 0) {
15117217845SJeremy Kerr 		goto out_free;
1522834c5b1SAndrew Jeffery 	}
15317217845SJeremy Kerr 
154955d140eSOskar Senft 	// Default to non-VUART
155955d140eSOskar Senft 	console->tty.type = TTY_DEVICE_UART;
156955d140eSOskar Senft 
15730ea6385SAndrew Jeffery 	/* Arbitrarily pick an attribute to differentiate UART vs VUART */
158955d140eSOskar Senft 	if (tty_sysfs_devnode) {
159955d140eSOskar Senft 		rc = asprintf(&tty_vuart_lpc_addr, "%s/lpc_address",
160955d140eSOskar Senft 			      tty_sysfs_devnode);
16130ea6385SAndrew Jeffery 		if (rc < 0) {
16230ea6385SAndrew Jeffery 			goto out_free;
16330ea6385SAndrew Jeffery 		}
16430ea6385SAndrew Jeffery 
16530ea6385SAndrew Jeffery 		rc = access(tty_vuart_lpc_addr, F_OK);
166955d140eSOskar Senft 		if (!rc) {
167955d140eSOskar Senft 			console->tty.type = TTY_DEVICE_VUART;
168955d140eSOskar Senft 			console->tty.vuart.sysfs_devnode =
169955d140eSOskar Senft 				strdup(tty_sysfs_devnode);
170955d140eSOskar Senft 		}
171955d140eSOskar Senft 	}
17230ea6385SAndrew Jeffery 
17317217845SJeremy Kerr 	rc = 0;
17417217845SJeremy Kerr 
17517217845SJeremy Kerr out_free:
17630ea6385SAndrew Jeffery 	free(tty_vuart_lpc_addr);
17717217845SJeremy Kerr 	free(tty_class_device_link);
178982090d9SAndrew Jeffery 	free(tty_sysfs_devnode);
17917217845SJeremy Kerr 	free(tty_device_tty_dir);
18017217845SJeremy Kerr 	free(tty_device_reldir);
18145ad7676SYi Li 	free(tty_path_input);
18245ad7676SYi Li 	free(tty_path_input_real);
18317217845SJeremy Kerr 	return rc;
18417217845SJeremy Kerr }
18517217845SJeremy Kerr 
1861a0e03b4SJeremy Kerr static int tty_set_sysfs_attr(struct console *console, const char *name,
187957818b4SJeremy Kerr 			      int value)
188957818b4SJeremy Kerr {
189957818b4SJeremy Kerr 	char *path;
190957818b4SJeremy Kerr 	FILE *fp;
191957818b4SJeremy Kerr 	int rc;
192957818b4SJeremy Kerr 
19330ea6385SAndrew Jeffery 	assert(console->tty.type == TTY_DEVICE_VUART);
19430ea6385SAndrew Jeffery 
19530ea6385SAndrew Jeffery 	if (!console->tty.vuart.sysfs_devnode) {
19630ea6385SAndrew Jeffery 		return -1;
19730ea6385SAndrew Jeffery 	}
19830ea6385SAndrew Jeffery 
19930ea6385SAndrew Jeffery 	rc = asprintf(&path, "%s/%s", console->tty.vuart.sysfs_devnode, name);
2002834c5b1SAndrew Jeffery 	if (rc < 0) {
201957818b4SJeremy Kerr 		return -1;
2022834c5b1SAndrew Jeffery 	}
203957818b4SJeremy Kerr 
204957818b4SJeremy Kerr 	fp = fopen(path, "w");
205957818b4SJeremy Kerr 	if (!fp) {
206a72711afSAndrew Jeffery 		warn("Can't access attribute %s on device %s", name,
20730ea6385SAndrew Jeffery 		     console->tty.kname);
208957818b4SJeremy Kerr 		rc = -1;
209957818b4SJeremy Kerr 		goto out_free;
210957818b4SJeremy Kerr 	}
211957818b4SJeremy Kerr 	setvbuf(fp, NULL, _IONBF, 0);
212957818b4SJeremy Kerr 
213957818b4SJeremy Kerr 	rc = fprintf(fp, "0x%x", value);
2142834c5b1SAndrew Jeffery 	if (rc < 0) {
215a72711afSAndrew Jeffery 		warn("Error writing to %s attribute of device %s", name,
21630ea6385SAndrew Jeffery 		     console->tty.kname);
2172834c5b1SAndrew Jeffery 	}
218957818b4SJeremy Kerr 	fclose(fp);
219957818b4SJeremy Kerr 
220957818b4SJeremy Kerr out_free:
221957818b4SJeremy Kerr 	free(path);
222957818b4SJeremy Kerr 	return rc;
223957818b4SJeremy Kerr }
224957818b4SJeremy Kerr 
225d831f960SJeremy Kerr /**
226c7fbcd48SBenjamin Fair  * Set termios attributes on the console tty.
22754e9569dSJeremy Kerr  */
228e258e51fSNinad Palsule void tty_init_termios(struct console *console)
22954e9569dSJeremy Kerr {
23054e9569dSJeremy Kerr 	struct termios termios;
23154e9569dSJeremy Kerr 	int rc;
23254e9569dSJeremy Kerr 
23330ea6385SAndrew Jeffery 	rc = tcgetattr(console->tty.fd, &termios);
23454e9569dSJeremy Kerr 	if (rc) {
23554e9569dSJeremy Kerr 		warn("Can't read tty termios");
23654e9569dSJeremy Kerr 		return;
23754e9569dSJeremy Kerr 	}
23854e9569dSJeremy Kerr 
23930ea6385SAndrew Jeffery 	if (console->tty.type == TTY_DEVICE_UART && console->tty.uart.baud) {
24030ea6385SAndrew Jeffery 		if (cfsetspeed(&termios, console->tty.uart.baud) < 0) {
24130ea6385SAndrew Jeffery 			warn("Couldn't set speeds for %s", console->tty.kname);
242c7fbcd48SBenjamin Fair 		}
2432834c5b1SAndrew Jeffery 	}
244c7fbcd48SBenjamin Fair 
245c7fbcd48SBenjamin Fair 	/* Set console to raw mode: we don't want any processing to occur on
246c7fbcd48SBenjamin Fair 	 * the underlying terminal input/output.
247c7fbcd48SBenjamin Fair 	 */
24854e9569dSJeremy Kerr 	cfmakeraw(&termios);
249c7fbcd48SBenjamin Fair 
25030ea6385SAndrew Jeffery 	rc = tcsetattr(console->tty.fd, TCSANOW, &termios);
2512834c5b1SAndrew Jeffery 	if (rc) {
25230ea6385SAndrew Jeffery 		warn("Can't set terminal options for %s", console->tty.kname);
25354e9569dSJeremy Kerr 	}
2542834c5b1SAndrew Jeffery }
25554e9569dSJeremy Kerr 
25654e9569dSJeremy Kerr /**
257d831f960SJeremy Kerr  * Open and initialise the serial device
258d831f960SJeremy Kerr  */
25930ea6385SAndrew Jeffery static void tty_init_vuart_io(struct console *console)
260d831f960SJeremy Kerr {
26130ea6385SAndrew Jeffery 	assert(console->tty.type == TTY_DEVICE_VUART);
26230ea6385SAndrew Jeffery 
26330ea6385SAndrew Jeffery 	if (console->tty.vuart.sirq) {
26430ea6385SAndrew Jeffery 		tty_set_sysfs_attr(console, "sirq", console->tty.vuart.sirq);
2652834c5b1SAndrew Jeffery 	}
266957818b4SJeremy Kerr 
26730ea6385SAndrew Jeffery 	if (console->tty.vuart.lpc_addr) {
26830ea6385SAndrew Jeffery 		tty_set_sysfs_attr(console, "lpc_address",
26930ea6385SAndrew Jeffery 				   console->tty.vuart.lpc_addr);
27030ea6385SAndrew Jeffery 	}
27130ea6385SAndrew Jeffery }
27230ea6385SAndrew Jeffery 
27330ea6385SAndrew Jeffery static int tty_init_io(struct console *console)
27430ea6385SAndrew Jeffery {
27530ea6385SAndrew Jeffery 	console->tty.fd = open(console->tty.dev, O_RDWR);
27630ea6385SAndrew Jeffery 	if (console->tty.fd <= 0) {
27730ea6385SAndrew Jeffery 		warn("Can't open tty %s", console->tty.dev);
278d831f960SJeremy Kerr 		return -1;
279d831f960SJeremy Kerr 	}
280d831f960SJeremy Kerr 
281d831f960SJeremy Kerr 	/* Disable character delay. We may want to later enable this when
282d831f960SJeremy Kerr 	 * we detect larger amounts of data
283d831f960SJeremy Kerr 	 */
28430ea6385SAndrew Jeffery 	fcntl(console->tty.fd, F_SETFL, FNDELAY);
285d831f960SJeremy Kerr 
28654e9569dSJeremy Kerr 	tty_init_termios(console);
28754e9569dSJeremy Kerr 
28830ea6385SAndrew Jeffery 	console->pollfds[console->n_pollers].fd = console->tty.fd;
289329a35f5SJeremy Kerr 	console->pollfds[console->n_pollers].events = POLLIN;
290329a35f5SJeremy Kerr 
291d831f960SJeremy Kerr 	return 0;
292d831f960SJeremy Kerr }
293d831f960SJeremy Kerr 
29430ea6385SAndrew Jeffery static int tty_init_vuart(struct console *console, struct config *config)
295d66195c1SJeremy Kerr {
296fd883a88SAndrew Jeffery 	unsigned long parsed;
297d66195c1SJeremy Kerr 	const char *val;
298d66195c1SJeremy Kerr 	char *endp;
29930ea6385SAndrew Jeffery 
30030ea6385SAndrew Jeffery 	assert(console->tty.type == TTY_DEVICE_VUART);
301d66195c1SJeremy Kerr 
302d66195c1SJeremy Kerr 	val = config_get_value(config, "lpc-address");
303d66195c1SJeremy Kerr 	if (val) {
304fd883a88SAndrew Jeffery 		errno = 0;
305fd883a88SAndrew Jeffery 		parsed = strtoul(val, &endp, 0);
306fd883a88SAndrew Jeffery 		if (parsed == ULONG_MAX && errno == ERANGE) {
307fd883a88SAndrew Jeffery 			warn("Cannot interpret 'lpc-address' value as an unsigned long: '%s'",
308fd883a88SAndrew Jeffery 			     val);
309fd883a88SAndrew Jeffery 			return -1;
310fd883a88SAndrew Jeffery 		}
311fd883a88SAndrew Jeffery 
312fd883a88SAndrew Jeffery 		if (parsed > UINT16_MAX) {
313fd883a88SAndrew Jeffery 			warn("Invalid LPC address '%s'", val);
314fd883a88SAndrew Jeffery 			return -1;
315fd883a88SAndrew Jeffery 		}
316fd883a88SAndrew Jeffery 
31730ea6385SAndrew Jeffery 		console->tty.vuart.lpc_addr = (uint16_t)parsed;
318d66195c1SJeremy Kerr 		if (endp == optarg) {
319d66195c1SJeremy Kerr 			warn("Invalid LPC address: '%s'", val);
320d66195c1SJeremy Kerr 			return -1;
321d66195c1SJeremy Kerr 		}
322d66195c1SJeremy Kerr 	}
323d66195c1SJeremy Kerr 
324d66195c1SJeremy Kerr 	val = config_get_value(config, "sirq");
325d66195c1SJeremy Kerr 	if (val) {
326fd883a88SAndrew Jeffery 		errno = 0;
327fd883a88SAndrew Jeffery 		parsed = strtoul(val, &endp, 0);
328fd883a88SAndrew Jeffery 		if (parsed == ULONG_MAX && errno == ERANGE) {
329fd883a88SAndrew Jeffery 			warn("Cannot interpret 'sirq' value as an unsigned long: '%s'",
330fd883a88SAndrew Jeffery 			     val);
331fd883a88SAndrew Jeffery 		}
332fd883a88SAndrew Jeffery 
3332834c5b1SAndrew Jeffery 		if (parsed > 16) {
334fd883a88SAndrew Jeffery 			warn("Invalid LPC SERIRQ: '%s'", val);
3352834c5b1SAndrew Jeffery 		}
336fd883a88SAndrew Jeffery 
33730ea6385SAndrew Jeffery 		console->tty.vuart.sirq = (int)parsed;
3382834c5b1SAndrew Jeffery 		if (endp == optarg) {
339d66195c1SJeremy Kerr 			warn("Invalid sirq: '%s'", val);
340d66195c1SJeremy Kerr 		}
3412834c5b1SAndrew Jeffery 	}
342d66195c1SJeremy Kerr 
34330ea6385SAndrew Jeffery 	return 0;
3442834c5b1SAndrew Jeffery }
345c7fbcd48SBenjamin Fair 
34630ea6385SAndrew Jeffery static int tty_init(struct console *console, struct config *config,
34730ea6385SAndrew Jeffery 		    const char *tty_arg)
34830ea6385SAndrew Jeffery {
34930ea6385SAndrew Jeffery 	const char *val;
35030ea6385SAndrew Jeffery 	int rc;
35130ea6385SAndrew Jeffery 
352d769eecfSAndrew Jeffery 	if (tty_arg) {
35330ea6385SAndrew Jeffery 		console->tty.kname = tty_arg;
354d769eecfSAndrew Jeffery 	} else if ((val = config_get_value(config, "upstream-tty"))) {
35530ea6385SAndrew Jeffery 		console->tty.kname = val;
356d769eecfSAndrew Jeffery 	} else {
357d66195c1SJeremy Kerr 		warnx("Error: No TTY device specified");
358d66195c1SJeremy Kerr 		return -1;
359d66195c1SJeremy Kerr 	}
360d66195c1SJeremy Kerr 
361d66195c1SJeremy Kerr 	rc = tty_find_device(console);
3622834c5b1SAndrew Jeffery 	if (rc) {
363d66195c1SJeremy Kerr 		return rc;
3642834c5b1SAndrew Jeffery 	}
365d66195c1SJeremy Kerr 
36630ea6385SAndrew Jeffery 	switch (console->tty.type) {
36730ea6385SAndrew Jeffery 	case TTY_DEVICE_VUART:
36830ea6385SAndrew Jeffery 		rc = tty_init_vuart(console, config);
36930ea6385SAndrew Jeffery 		if (rc) {
370d66195c1SJeremy Kerr 			return rc;
371d66195c1SJeremy Kerr 		}
372d66195c1SJeremy Kerr 
37330ea6385SAndrew Jeffery 		tty_init_vuart_io(console);
37430ea6385SAndrew Jeffery 		break;
37530ea6385SAndrew Jeffery 	case TTY_DEVICE_UART:
37630ea6385SAndrew Jeffery 		val = config_get_value(config, "baud");
37730ea6385SAndrew Jeffery 		if (val) {
37830ea6385SAndrew Jeffery 			if (config_parse_baud(&console->tty.uart.baud, val)) {
37930ea6385SAndrew Jeffery 				warnx("Invalid baud rate: '%s'", val);
38030ea6385SAndrew Jeffery 			}
38130ea6385SAndrew Jeffery 		}
38230ea6385SAndrew Jeffery 		break;
38330ea6385SAndrew Jeffery 	case TTY_DEVICE_PTY:
38430ea6385SAndrew Jeffery 		break;
38530ea6385SAndrew Jeffery 	case TTY_DEVICE_UNDEFINED:
38630ea6385SAndrew Jeffery 	default:
38730ea6385SAndrew Jeffery 		warnx("Cannot configure unrecognised TTY device");
38830ea6385SAndrew Jeffery 		return -1;
38930ea6385SAndrew Jeffery 	}
39030ea6385SAndrew Jeffery 
39130ea6385SAndrew Jeffery 	return tty_init_io(console);
39230ea6385SAndrew Jeffery }
39330ea6385SAndrew Jeffery 
39430ea6385SAndrew Jeffery static void tty_fini(struct console *console)
39530ea6385SAndrew Jeffery {
39630ea6385SAndrew Jeffery 	if (console->tty.type == TTY_DEVICE_VUART) {
39730ea6385SAndrew Jeffery 		free(console->tty.vuart.sysfs_devnode);
39830ea6385SAndrew Jeffery 	}
39930ea6385SAndrew Jeffery 	free(console->tty.dev);
40030ea6385SAndrew Jeffery }
40130ea6385SAndrew Jeffery 
4027dc08baaSZev Weiss static int write_to_path(const char *path, const char *data)
4037dc08baaSZev Weiss {
4047dc08baaSZev Weiss 	int rc = 0;
4057dc08baaSZev Weiss 	FILE *f = fopen(path, "w");
4067dc08baaSZev Weiss 	if (!f) {
4077dc08baaSZev Weiss 		return -1;
4087dc08baaSZev Weiss 	}
4097dc08baaSZev Weiss 
4107dc08baaSZev Weiss 	if (fprintf(f, "%s", data) < 0) {
4117dc08baaSZev Weiss 		rc = -1;
4127dc08baaSZev Weiss 	}
4137dc08baaSZev Weiss 
4147dc08baaSZev Weiss 	if (fclose(f)) {
4157dc08baaSZev Weiss 		rc = -1;
4167dc08baaSZev Weiss 	}
4177dc08baaSZev Weiss 
4187dc08baaSZev Weiss 	return rc;
4197dc08baaSZev Weiss }
4207dc08baaSZev Weiss 
4217dc08baaSZev Weiss #define ASPEED_UART_ROUTING_PATTERN                                            \
4227dc08baaSZev Weiss 	"/sys/bus/platform/drivers/aspeed-uart-routing/*.uart-routing"
4237dc08baaSZev Weiss 
4247dc08baaSZev Weiss static void uart_routing_init(struct config *config)
4257dc08baaSZev Weiss {
4267dc08baaSZev Weiss 	const char *muxcfg;
4277dc08baaSZev Weiss 	const char *p;
4287dc08baaSZev Weiss 	size_t buflen;
4297dc08baaSZev Weiss 	char *sink;
4307dc08baaSZev Weiss 	char *source;
4317dc08baaSZev Weiss 	char *muxdir;
4327dc08baaSZev Weiss 	char *path;
4337dc08baaSZev Weiss 	glob_t globbuf;
4347dc08baaSZev Weiss 
4357dc08baaSZev Weiss 	muxcfg = config_get_value(config, "aspeed-uart-routing");
4367dc08baaSZev Weiss 	if (!muxcfg) {
4377dc08baaSZev Weiss 		return;
4387dc08baaSZev Weiss 	}
4397dc08baaSZev Weiss 
4407dc08baaSZev Weiss 	/* Find the driver's sysfs directory */
4417dc08baaSZev Weiss 	if (glob(ASPEED_UART_ROUTING_PATTERN, GLOB_ERR | GLOB_NOSORT, NULL,
4427dc08baaSZev Weiss 		 &globbuf) != 0) {
4437dc08baaSZev Weiss 		warn("Couldn't find uart-routing driver directory, cannot apply config");
4447dc08baaSZev Weiss 		return;
4457dc08baaSZev Weiss 	}
4467dc08baaSZev Weiss 	if (globbuf.gl_pathc != 1) {
4477dc08baaSZev Weiss 		warnx("Found %zd uart-routing driver directories, cannot apply config",
4487dc08baaSZev Weiss 		      globbuf.gl_pathc);
4497dc08baaSZev Weiss 		goto out_free_glob;
4507dc08baaSZev Weiss 	}
4517dc08baaSZev Weiss 	muxdir = globbuf.gl_pathv[0];
4527dc08baaSZev Weiss 
4537dc08baaSZev Weiss 	/*
4547dc08baaSZev Weiss 	 * Rather than faff about tracking a bunch of separate buffer sizes,
4557dc08baaSZev Weiss 	 * just use one (worst-case) size for all of them -- +2 for a trailing
4567dc08baaSZev Weiss 	 * NUL and a '/' separator to construct the sysfs file path.
4577dc08baaSZev Weiss 	 */
4587dc08baaSZev Weiss 	buflen = strlen(muxdir) + strlen(muxcfg) + 2;
4597dc08baaSZev Weiss 
4607dc08baaSZev Weiss 	sink = malloc(buflen);
4617dc08baaSZev Weiss 	source = malloc(buflen);
4627dc08baaSZev Weiss 	path = malloc(buflen);
4637dc08baaSZev Weiss 	if (!path || !sink || !source) {
4647dc08baaSZev Weiss 		warnx("Out of memory applying uart routing config");
4657dc08baaSZev Weiss 		goto out_free_bufs;
4667dc08baaSZev Weiss 	}
4677dc08baaSZev Weiss 
4687dc08baaSZev Weiss 	p = muxcfg;
4697dc08baaSZev Weiss 	while (*p) {
4707dc08baaSZev Weiss 		ssize_t bytes_scanned;
4717dc08baaSZev Weiss 
4727dc08baaSZev Weiss 		if (sscanf(p, " %[^:/ \t]:%[^: \t] %zn", sink, source,
4737dc08baaSZev Weiss 			   &bytes_scanned) != 2) {
4747dc08baaSZev Weiss 			warnx("Invalid syntax in aspeed uart config: '%s' not applied",
4757dc08baaSZev Weiss 			      p);
4767dc08baaSZev Weiss 			break;
4777dc08baaSZev Weiss 		}
4787dc08baaSZev Weiss 		p += bytes_scanned;
4797dc08baaSZev Weiss 
4807dc08baaSZev Weiss 		/*
4817dc08baaSZev Weiss 		 * Check that the sink name looks reasonable before proceeding
4827dc08baaSZev Weiss 		 * (there are other writable files in the same directory that
4837dc08baaSZev Weiss 		 * we shouldn't be touching, such as 'driver_override' and
4847dc08baaSZev Weiss 		 * 'uevent').
4857dc08baaSZev Weiss 		 */
4867dc08baaSZev Weiss 		if (strncmp(sink, "io", strlen("io")) != 0 &&
4877dc08baaSZev Weiss 		    strncmp(sink, "uart", strlen("uart")) != 0) {
4887dc08baaSZev Weiss 			warnx("Skipping invalid uart routing name '%s' (must be ioN or uartN)",
4897dc08baaSZev Weiss 			      sink);
4907dc08baaSZev Weiss 			continue;
4917dc08baaSZev Weiss 		}
4927dc08baaSZev Weiss 
4937dc08baaSZev Weiss 		snprintf(path, buflen, "%s/%s", muxdir, sink);
4947dc08baaSZev Weiss 		if (write_to_path(path, source)) {
4957dc08baaSZev Weiss 			warn("Failed to apply uart-routing config '%s:%s'",
4967dc08baaSZev Weiss 			     sink, source);
4977dc08baaSZev Weiss 		}
4987dc08baaSZev Weiss 	}
4997dc08baaSZev Weiss 
5007dc08baaSZev Weiss out_free_bufs:
5017dc08baaSZev Weiss 	free(path);
5027dc08baaSZev Weiss 	free(source);
5037dc08baaSZev Weiss 	free(sink);
5047dc08baaSZev Weiss out_free_glob:
5057dc08baaSZev Weiss 	globfree(&globbuf);
5067dc08baaSZev Weiss }
5077dc08baaSZev Weiss 
5081a0e03b4SJeremy Kerr int console_data_out(struct console *console, const uint8_t *data, size_t len)
509d831f960SJeremy Kerr {
51030ea6385SAndrew Jeffery 	return write_buf_to_fd(console->tty.fd, data, len);
511d831f960SJeremy Kerr }
512d831f960SJeremy Kerr 
5135ba20b5bSNinad Palsule /* Prepare a socket name */
514954be0fbSAndrew Jeffery static int set_socket_info(struct console *console, struct config *config,
515954be0fbSAndrew Jeffery 			   const char *console_id)
516b14ca19cSNinad Palsule {
517b14ca19cSNinad Palsule 	ssize_t len;
518b14ca19cSNinad Palsule 
5195ba20b5bSNinad Palsule 	/* Get console id */
5205ba20b5bSNinad Palsule 	console->console_id = config_resolve_console_id(config, console_id);
521954be0fbSAndrew Jeffery 
522b14ca19cSNinad Palsule 	/* Get the socket name/path */
5235ba20b5bSNinad Palsule 	len = console_socket_path(console->socket_name, console->console_id);
524b14ca19cSNinad Palsule 	if (len < 0) {
525b14ca19cSNinad Palsule 		warn("Failed to set socket path: %s", strerror(errno));
526b14ca19cSNinad Palsule 		return EXIT_FAILURE;
527b14ca19cSNinad Palsule 	}
528b14ca19cSNinad Palsule 
529b14ca19cSNinad Palsule 	/* Socket name is not a null terminated string hence save the length */
530b14ca19cSNinad Palsule 	console->socket_name_len = len;
531b14ca19cSNinad Palsule 
532b14ca19cSNinad Palsule 	return 0;
533b14ca19cSNinad Palsule }
534b14ca19cSNinad Palsule 
535d47963e5SJeremy Kerr static void handlers_init(struct console *console, struct config *config)
536d831f960SJeremy Kerr {
537b70f8713SAndrew Jeffery 	/* NOLINTBEGIN(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) */
538e2826c7dSJeremy Kerr 	extern const struct handler_type *const __start_handlers;
539e2826c7dSJeremy Kerr 	extern const struct handler_type *const __stop_handlers;
540b70f8713SAndrew Jeffery 	/* NOLINTEND(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) */
541e2826c7dSJeremy Kerr 	size_t n_types;
542e2826c7dSJeremy Kerr 	int j = 0;
543e2826c7dSJeremy Kerr 	size_t i;
544e2826c7dSJeremy Kerr 
545e2826c7dSJeremy Kerr 	n_types = &__stop_handlers - &__start_handlers;
546e2826c7dSJeremy Kerr 	console->handlers = calloc(n_types, sizeof(struct handler *));
547e2826c7dSJeremy Kerr 	if (!console->handlers) {
548e2826c7dSJeremy Kerr 		err(EXIT_FAILURE, "malloc(handlers)");
549e2826c7dSJeremy Kerr 	}
550e2826c7dSJeremy Kerr 
551079fc516SAndrew Jeffery 	printf("%zu handler type%s\n", n_types, n_types == 1 ? "" : "s");
552e2826c7dSJeremy Kerr 
553e2826c7dSJeremy Kerr 	for (i = 0; i < n_types; i++) {
554e2826c7dSJeremy Kerr 		const struct handler_type *type = &__start_handlers[i];
5551a0e03b4SJeremy Kerr 		struct handler *handler;
556d831f960SJeremy Kerr 
557e2826c7dSJeremy Kerr 		/* Should be picked up at build time by
558e2826c7dSJeremy Kerr 		 * console_handler_register, but check anyway
559e2826c7dSJeremy Kerr 		 */
560e2826c7dSJeremy Kerr 		if (!type->init || !type->fini) {
561e2826c7dSJeremy Kerr 			errx(EXIT_FAILURE,
562e2826c7dSJeremy Kerr 			     "invalid handler type %s: no init() / fini()",
563e2826c7dSJeremy Kerr 			     type->name);
5642834c5b1SAndrew Jeffery 		}
565021b91f0SJeremy Kerr 
566e2826c7dSJeremy Kerr 		handler = type->init(type, console, config);
567021b91f0SJeremy Kerr 
568e2826c7dSJeremy Kerr 		printf("  console '%s': handler %s [%sactive]\n",
569e2826c7dSJeremy Kerr 		       console->console_id, type->name, handler ? "" : "in");
570e2826c7dSJeremy Kerr 
571e2826c7dSJeremy Kerr 		if (handler) {
572e2826c7dSJeremy Kerr 			handler->type = type;
573e2826c7dSJeremy Kerr 			console->handlers[j++] = handler;
574d831f960SJeremy Kerr 		}
575d831f960SJeremy Kerr 	}
576d831f960SJeremy Kerr 
577e2826c7dSJeremy Kerr 	console->n_handlers = j;
578e2826c7dSJeremy Kerr }
579e2826c7dSJeremy Kerr 
5801a0e03b4SJeremy Kerr static void handlers_fini(struct console *console)
581d831f960SJeremy Kerr {
5821a0e03b4SJeremy Kerr 	struct handler *handler;
5831a0e03b4SJeremy Kerr 	int i;
5841a0e03b4SJeremy Kerr 
5851a0e03b4SJeremy Kerr 	for (i = 0; i < console->n_handlers; i++) {
5861a0e03b4SJeremy Kerr 		handler = console->handlers[i];
587e2826c7dSJeremy Kerr 		handler->type->fini(handler);
5881a0e03b4SJeremy Kerr 	}
589e2826c7dSJeremy Kerr 
590e2826c7dSJeremy Kerr 	free(console->handlers);
591e2826c7dSJeremy Kerr 	console->handlers = NULL;
592e2826c7dSJeremy Kerr 	console->n_handlers = 0;
5932834c5b1SAndrew Jeffery }
594d831f960SJeremy Kerr 
5951cecc5deSJohnathan Mantey static int get_current_time(struct timeval *tv)
5961cecc5deSJohnathan Mantey {
5971cecc5deSJohnathan Mantey 	struct timespec t;
5981cecc5deSJohnathan Mantey 	int rc;
5991cecc5deSJohnathan Mantey 
6001cecc5deSJohnathan Mantey 	/*
6011cecc5deSJohnathan Mantey 	 * We use clock_gettime(CLOCK_MONOTONIC) so we're immune to
6021cecc5deSJohnathan Mantey 	 * local time changes. However, a struct timeval is more
6031cecc5deSJohnathan Mantey 	 * convenient for calculations, so convert to that.
6041cecc5deSJohnathan Mantey 	 */
6051cecc5deSJohnathan Mantey 	rc = clock_gettime(CLOCK_MONOTONIC, &t);
6062834c5b1SAndrew Jeffery 	if (rc) {
6071cecc5deSJohnathan Mantey 		return rc;
6082834c5b1SAndrew Jeffery 	}
6091cecc5deSJohnathan Mantey 
6101cecc5deSJohnathan Mantey 	tv->tv_sec = t.tv_sec;
6111cecc5deSJohnathan Mantey 	tv->tv_usec = t.tv_nsec / 1000;
6121cecc5deSJohnathan Mantey 
6131cecc5deSJohnathan Mantey 	return 0;
6141cecc5deSJohnathan Mantey }
6151cecc5deSJohnathan Mantey 
616a72711afSAndrew Jeffery struct ringbuffer_consumer *
617a72711afSAndrew Jeffery console_ringbuffer_consumer_register(struct console *console,
618f733c85aSJeremy Kerr 				     ringbuffer_poll_fn_t poll_fn, void *data)
619d831f960SJeremy Kerr {
620f733c85aSJeremy Kerr 	return ringbuffer_consumer_register(console->rb, poll_fn, data);
621d831f960SJeremy Kerr }
622d831f960SJeremy Kerr 
62355c9712dSJeremy Kerr struct poller *console_poller_register(struct console *console,
624a72711afSAndrew Jeffery 				       struct handler *handler,
625a72711afSAndrew Jeffery 				       poller_event_fn_t poller_fn,
6261cecc5deSJohnathan Mantey 				       poller_timeout_fn_t timeout_fn, int fd,
6271cecc5deSJohnathan Mantey 				       int events, void *data)
628d831f960SJeremy Kerr {
629329a35f5SJeremy Kerr 	struct poller *poller;
6305c359cc6SAndrew Jeffery 	long n;
631329a35f5SJeremy Kerr 
632329a35f5SJeremy Kerr 	poller = malloc(sizeof(*poller));
633329a35f5SJeremy Kerr 	poller->remove = false;
634329a35f5SJeremy Kerr 	poller->handler = handler;
6351cecc5deSJohnathan Mantey 	poller->event_fn = poller_fn;
6361cecc5deSJohnathan Mantey 	poller->timeout_fn = timeout_fn;
6379cc8459aSAndrew Jeffery 	timerclear(&poller->timeout);
638329a35f5SJeremy Kerr 	poller->data = data;
639329a35f5SJeremy Kerr 
640329a35f5SJeremy Kerr 	/* add one to our pollers array */
641329a35f5SJeremy Kerr 	n = console->n_pollers++;
64291b52175SAndrew Jeffery 	/*
64391b52175SAndrew Jeffery 	 * We're managing an array of pointers to aggregates, so don't warn about sizeof() on a
64491b52175SAndrew Jeffery 	 * pointer type.
64591b52175SAndrew Jeffery 	 */
64691b52175SAndrew Jeffery 	/* NOLINTBEGIN(bugprone-sizeof-expression) */
64791b52175SAndrew Jeffery 	console->pollers = reallocarray(console->pollers, console->n_pollers,
64891b52175SAndrew Jeffery 					sizeof(*console->pollers));
64991b52175SAndrew Jeffery 	/* NOLINTEND(bugprone-sizeof-expression) */
650329a35f5SJeremy Kerr 
651329a35f5SJeremy Kerr 	console->pollers[n] = poller;
652329a35f5SJeremy Kerr 
653329a35f5SJeremy Kerr 	/* increase pollfds array too  */
6544e44c790SAndrew Jeffery 	console->pollfds = reallocarray(
6554e44c790SAndrew Jeffery 		console->pollfds, (MAX_INTERNAL_POLLFD + console->n_pollers),
65691b52175SAndrew Jeffery 		sizeof(*console->pollfds));
657329a35f5SJeremy Kerr 
658329a35f5SJeremy Kerr 	/* shift the end pollfds up by one */
659a72711afSAndrew Jeffery 	memcpy(&console->pollfds[n + 1], &console->pollfds[n],
660f9c8f6caSCheng C Yang 	       sizeof(*console->pollfds) * MAX_INTERNAL_POLLFD);
661329a35f5SJeremy Kerr 
662329a35f5SJeremy Kerr 	console->pollfds[n].fd = fd;
6635c359cc6SAndrew Jeffery 	console->pollfds[n].events = (short)(events & 0x7fff);
664329a35f5SJeremy Kerr 
665329a35f5SJeremy Kerr 	return poller;
666329a35f5SJeremy Kerr }
667329a35f5SJeremy Kerr 
668a72711afSAndrew Jeffery void console_poller_unregister(struct console *console, struct poller *poller)
669329a35f5SJeremy Kerr {
670329a35f5SJeremy Kerr 	int i;
671329a35f5SJeremy Kerr 
672329a35f5SJeremy Kerr 	/* find the entry in our pollers array */
6732834c5b1SAndrew Jeffery 	for (i = 0; i < console->n_pollers; i++) {
6742834c5b1SAndrew Jeffery 		if (console->pollers[i] == poller) {
675329a35f5SJeremy Kerr 			break;
6762834c5b1SAndrew Jeffery 		}
6772834c5b1SAndrew Jeffery 	}
678329a35f5SJeremy Kerr 
679329a35f5SJeremy Kerr 	assert(i < console->n_pollers);
680329a35f5SJeremy Kerr 
681329a35f5SJeremy Kerr 	console->n_pollers--;
682329a35f5SJeremy Kerr 
68391b52175SAndrew Jeffery 	/*
68491b52175SAndrew Jeffery 	 * Remove the item from the pollers array...
68591b52175SAndrew Jeffery 	 *
68691b52175SAndrew Jeffery 	 * We're managing an array of pointers to aggregates, so don't warn about sizeof() on a
68791b52175SAndrew Jeffery 	 * pointer type.
68891b52175SAndrew Jeffery 	 */
68991b52175SAndrew Jeffery 	/* NOLINTBEGIN(bugprone-sizeof-expression) */
690329a35f5SJeremy Kerr 	memmove(&console->pollers[i], &console->pollers[i + 1],
691a72711afSAndrew Jeffery 		sizeof(*console->pollers) * (console->n_pollers - i));
692329a35f5SJeremy Kerr 
6937851a396SAndrew Jeffery 	if (console->n_pollers == 0) {
6947851a396SAndrew Jeffery 		free(console->pollers);
6957851a396SAndrew Jeffery 		console->pollers = NULL;
6967851a396SAndrew Jeffery 	} else {
6977851a396SAndrew Jeffery 		console->pollers = reallocarray(console->pollers,
6987851a396SAndrew Jeffery 						console->n_pollers,
69991b52175SAndrew Jeffery 						sizeof(*console->pollers));
7007851a396SAndrew Jeffery 	}
70191b52175SAndrew Jeffery 	/* NOLINTEND(bugprone-sizeof-expression) */
702329a35f5SJeremy Kerr 
703329a35f5SJeremy Kerr 	/* ... and the pollfds array */
704329a35f5SJeremy Kerr 	memmove(&console->pollfds[i], &console->pollfds[i + 1],
705329a35f5SJeremy Kerr 		sizeof(*console->pollfds) *
706f9c8f6caSCheng C Yang 			(MAX_INTERNAL_POLLFD + console->n_pollers - i));
707329a35f5SJeremy Kerr 
7084e44c790SAndrew Jeffery 	console->pollfds = reallocarray(
7094e44c790SAndrew Jeffery 		console->pollfds, (MAX_INTERNAL_POLLFD + console->n_pollers),
71091b52175SAndrew Jeffery 		sizeof(*console->pollfds));
711329a35f5SJeremy Kerr 
712329a35f5SJeremy Kerr 	free(poller);
713329a35f5SJeremy Kerr }
714329a35f5SJeremy Kerr 
7156b1fed27SJeremy Kerr void console_poller_set_events(struct console *console, struct poller *poller,
7166b1fed27SJeremy Kerr 			       int events)
7176b1fed27SJeremy Kerr {
7186b1fed27SJeremy Kerr 	int i;
7196b1fed27SJeremy Kerr 
7206b1fed27SJeremy Kerr 	/* find the entry in our pollers array */
7212834c5b1SAndrew Jeffery 	for (i = 0; i < console->n_pollers; i++) {
7222834c5b1SAndrew Jeffery 		if (console->pollers[i] == poller) {
7236b1fed27SJeremy Kerr 			break;
7242834c5b1SAndrew Jeffery 		}
7252834c5b1SAndrew Jeffery 	}
7266b1fed27SJeremy Kerr 
7275c359cc6SAndrew Jeffery 	console->pollfds[i].events = (short)(events & 0x7fff);
7286b1fed27SJeremy Kerr }
7296b1fed27SJeremy Kerr 
730fd048328SAndrew Jeffery void console_poller_set_timeout(struct console *console __attribute__((unused)),
731fd048328SAndrew Jeffery 				struct poller *poller, const struct timeval *tv)
7321cecc5deSJohnathan Mantey {
7331cecc5deSJohnathan Mantey 	struct timeval now;
7341cecc5deSJohnathan Mantey 	int rc;
7351cecc5deSJohnathan Mantey 
7361cecc5deSJohnathan Mantey 	rc = get_current_time(&now);
7372834c5b1SAndrew Jeffery 	if (rc) {
7381cecc5deSJohnathan Mantey 		return;
7392834c5b1SAndrew Jeffery 	}
7401cecc5deSJohnathan Mantey 
7411cecc5deSJohnathan Mantey 	timeradd(&now, tv, &poller->timeout);
7421cecc5deSJohnathan Mantey }
7431cecc5deSJohnathan Mantey 
7445c359cc6SAndrew Jeffery static long get_poll_timeout(struct console *console, struct timeval *cur_time)
7451cecc5deSJohnathan Mantey {
746b70f8713SAndrew Jeffery 	struct timeval *earliest;
747b70f8713SAndrew Jeffery 	struct timeval interval;
7481cecc5deSJohnathan Mantey 	struct poller *poller;
7491cecc5deSJohnathan Mantey 	int i;
7501cecc5deSJohnathan Mantey 
7511cecc5deSJohnathan Mantey 	earliest = NULL;
7521cecc5deSJohnathan Mantey 
7531cecc5deSJohnathan Mantey 	for (i = 0; i < console->n_pollers; i++) {
7541cecc5deSJohnathan Mantey 		poller = console->pollers[i];
7551cecc5deSJohnathan Mantey 
7561cecc5deSJohnathan Mantey 		if (poller->timeout_fn && timerisset(&poller->timeout) &&
7571cecc5deSJohnathan Mantey 		    (!earliest ||
7581cecc5deSJohnathan Mantey 		     (earliest && timercmp(&poller->timeout, earliest, <)))) {
7591cecc5deSJohnathan Mantey 			// poller is buffering data and needs the poll
7601cecc5deSJohnathan Mantey 			// function to timeout.
7611cecc5deSJohnathan Mantey 			earliest = &poller->timeout;
7621cecc5deSJohnathan Mantey 		}
7631cecc5deSJohnathan Mantey 	}
7641cecc5deSJohnathan Mantey 
7651cecc5deSJohnathan Mantey 	if (earliest) {
7661cecc5deSJohnathan Mantey 		if (timercmp(earliest, cur_time, >)) {
7671cecc5deSJohnathan Mantey 			/* recalculate the timeout period, time period has
7681cecc5deSJohnathan Mantey 			 * not elapsed */
7691cecc5deSJohnathan Mantey 			timersub(earliest, cur_time, &interval);
7701cecc5deSJohnathan Mantey 			return ((interval.tv_sec * 1000) +
7711cecc5deSJohnathan Mantey 				(interval.tv_usec / 1000));
7720b7b0477SAndrew Jeffery 		} /* return from poll immediately */
7731cecc5deSJohnathan Mantey 		return 0;
7740b7b0477SAndrew Jeffery 
7750b7b0477SAndrew Jeffery 	} /* poll indefinitely */
7761cecc5deSJohnathan Mantey 	return -1;
7771cecc5deSJohnathan Mantey }
7781cecc5deSJohnathan Mantey 
7791cecc5deSJohnathan Mantey static int call_pollers(struct console *console, struct timeval *cur_time)
780329a35f5SJeremy Kerr {
781329a35f5SJeremy Kerr 	struct poller *poller;
782329a35f5SJeremy Kerr 	struct pollfd *pollfd;
783329a35f5SJeremy Kerr 	enum poller_ret prc;
784b70f8713SAndrew Jeffery 	int i;
785b70f8713SAndrew Jeffery 	int rc;
786d831f960SJeremy Kerr 
7871a0e03b4SJeremy Kerr 	rc = 0;
7881a0e03b4SJeremy Kerr 
789329a35f5SJeremy Kerr 	/*
790329a35f5SJeremy Kerr 	 * Process poll events by iterating through the pollers and pollfds
791329a35f5SJeremy Kerr 	 * in-step, calling any pollers that we've found revents for.
792329a35f5SJeremy Kerr 	 */
793329a35f5SJeremy Kerr 	for (i = 0; i < console->n_pollers; i++) {
794329a35f5SJeremy Kerr 		poller = console->pollers[i];
795329a35f5SJeremy Kerr 		pollfd = &console->pollfds[i];
7961cecc5deSJohnathan Mantey 		prc = POLLER_OK;
7971a0e03b4SJeremy Kerr 
7981cecc5deSJohnathan Mantey 		/* process pending events... */
7991cecc5deSJohnathan Mantey 		if (pollfd->revents) {
8001cecc5deSJohnathan Mantey 			prc = poller->event_fn(poller->handler, pollfd->revents,
801329a35f5SJeremy Kerr 					       poller->data);
8022834c5b1SAndrew Jeffery 			if (prc == POLLER_EXIT) {
803329a35f5SJeremy Kerr 				rc = -1;
8042834c5b1SAndrew Jeffery 			} else if (prc == POLLER_REMOVE) {
805329a35f5SJeremy Kerr 				poller->remove = true;
806329a35f5SJeremy Kerr 			}
8072834c5b1SAndrew Jeffery 		}
808329a35f5SJeremy Kerr 
8091cecc5deSJohnathan Mantey 		if ((prc == POLLER_OK) && poller->timeout_fn &&
8101cecc5deSJohnathan Mantey 		    timerisset(&poller->timeout) &&
8111cecc5deSJohnathan Mantey 		    timercmp(&poller->timeout, cur_time, <=)) {
8121cecc5deSJohnathan Mantey 			/* One of the ringbuffer consumers is buffering the
8131cecc5deSJohnathan Mantey 			data stream. The amount of idle time the consumer
8141cecc5deSJohnathan Mantey 			desired has expired.  Process the buffered data for
8151cecc5deSJohnathan Mantey 			transmission. */
8161cecc5deSJohnathan Mantey 			timerclear(&poller->timeout);
8171cecc5deSJohnathan Mantey 			prc = poller->timeout_fn(poller->handler, poller->data);
8181cecc5deSJohnathan Mantey 			if (prc == POLLER_EXIT) {
8191cecc5deSJohnathan Mantey 				rc = -1;
8201cecc5deSJohnathan Mantey 			} else if (prc == POLLER_REMOVE) {
8211cecc5deSJohnathan Mantey 				poller->remove = true;
8221cecc5deSJohnathan Mantey 			}
8231cecc5deSJohnathan Mantey 		}
8241cecc5deSJohnathan Mantey 	}
8251cecc5deSJohnathan Mantey 
826329a35f5SJeremy Kerr 	/**
827329a35f5SJeremy Kerr 	 * Process deferred removals; restarting each time we unregister, as
828329a35f5SJeremy Kerr 	 * the array will have changed
829329a35f5SJeremy Kerr 	 */
830329a35f5SJeremy Kerr 	for (;;) {
831329a35f5SJeremy Kerr 		bool removed = false;
832329a35f5SJeremy Kerr 
833329a35f5SJeremy Kerr 		for (i = 0; i < console->n_pollers; i++) {
834329a35f5SJeremy Kerr 			poller = console->pollers[i];
835329a35f5SJeremy Kerr 			if (poller->remove) {
83655c9712dSJeremy Kerr 				console_poller_unregister(console, poller);
837329a35f5SJeremy Kerr 				removed = true;
838329a35f5SJeremy Kerr 				break;
839329a35f5SJeremy Kerr 			}
840329a35f5SJeremy Kerr 		}
8412834c5b1SAndrew Jeffery 		if (!removed) {
842329a35f5SJeremy Kerr 			break;
8431a0e03b4SJeremy Kerr 		}
8442834c5b1SAndrew Jeffery 	}
8451a0e03b4SJeremy Kerr 
8461a0e03b4SJeremy Kerr 	return rc;
8471a0e03b4SJeremy Kerr }
8481a0e03b4SJeremy Kerr 
849769cee1aSJeremy Kerr static void sighandler(int signal)
850769cee1aSJeremy Kerr {
8512834c5b1SAndrew Jeffery 	if (signal == SIGINT) {
852553cb663SAndrew Jeffery 		sigint = 1;
853769cee1aSJeremy Kerr 	}
8542834c5b1SAndrew Jeffery }
855769cee1aSJeremy Kerr 
8566925740dSAlexander Hansen static int run_console_iteration(struct console *console)
8571a0e03b4SJeremy Kerr {
8586925740dSAlexander Hansen 	uint8_t buf[4096];
8591cecc5deSJohnathan Mantey 	struct timeval tv;
8605c359cc6SAndrew Jeffery 	long timeout;
8615c359cc6SAndrew Jeffery 	ssize_t rc;
862769cee1aSJeremy Kerr 
8637f2bfb9bSMedicine Yeh 	if (console->rb->size < sizeof(buf)) {
8646925740dSAlexander Hansen 		fprintf(stderr, "Ringbuffer size should be greater than %zuB\n",
8657f2bfb9bSMedicine Yeh 			sizeof(buf));
8666925740dSAlexander Hansen 		return -1;
8677f2bfb9bSMedicine Yeh 	}
8681764145dSJeremy Kerr 
869769cee1aSJeremy Kerr 	if (sigint) {
870769cee1aSJeremy Kerr 		fprintf(stderr, "Received interrupt, exiting\n");
8716925740dSAlexander Hansen 		return -1;
872769cee1aSJeremy Kerr 	}
873769cee1aSJeremy Kerr 
8741cecc5deSJohnathan Mantey 	rc = get_current_time(&tv);
8751cecc5deSJohnathan Mantey 	if (rc) {
8761cecc5deSJohnathan Mantey 		warn("Failed to read current time");
8776925740dSAlexander Hansen 		return -1;
8781cecc5deSJohnathan Mantey 	}
8791cecc5deSJohnathan Mantey 
8801cecc5deSJohnathan Mantey 	timeout = get_poll_timeout(console, &tv);
8811cecc5deSJohnathan Mantey 
8826925740dSAlexander Hansen 	rc = poll(console->pollfds, console->n_pollers + MAX_INTERNAL_POLLFD,
8835c359cc6SAndrew Jeffery 		  (int)timeout);
8841cecc5deSJohnathan Mantey 
885d831f960SJeremy Kerr 	if (rc < 0) {
886769cee1aSJeremy Kerr 		if (errno == EINTR) {
8876925740dSAlexander Hansen 			return 0;
8880b7b0477SAndrew Jeffery 		}
889d831f960SJeremy Kerr 		warn("poll error");
8906925740dSAlexander Hansen 		return -1;
891769cee1aSJeremy Kerr 	}
892d831f960SJeremy Kerr 
893329a35f5SJeremy Kerr 	/* process internal fd first */
894329a35f5SJeremy Kerr 	if (console->pollfds[console->n_pollers].revents) {
89530ea6385SAndrew Jeffery 		rc = read(console->tty.fd, buf, sizeof(buf));
896d831f960SJeremy Kerr 		if (rc <= 0) {
897d831f960SJeremy Kerr 			warn("Error reading from tty device");
8986925740dSAlexander Hansen 			return -1;
899d831f960SJeremy Kerr 		}
900f733c85aSJeremy Kerr 		rc = ringbuffer_queue(console->rb, buf, rc);
9012834c5b1SAndrew Jeffery 		if (rc) {
9026925740dSAlexander Hansen 			return -1;
903d831f960SJeremy Kerr 		}
9042834c5b1SAndrew Jeffery 	}
905d831f960SJeremy Kerr 
906f9c8f6caSCheng C Yang 	if (console->pollfds[console->n_pollers + 1].revents) {
907f9c8f6caSCheng C Yang 		sd_bus_process(console->bus, NULL);
908f9c8f6caSCheng C Yang 	}
909f9c8f6caSCheng C Yang 
910329a35f5SJeremy Kerr 	/* ... and then the pollers */
9111cecc5deSJohnathan Mantey 	rc = call_pollers(console, &tv);
9122834c5b1SAndrew Jeffery 	if (rc) {
9136925740dSAlexander Hansen 		return -1;
9146925740dSAlexander Hansen 	}
9156925740dSAlexander Hansen 	return 0;
9166925740dSAlexander Hansen }
9176925740dSAlexander Hansen 
9186925740dSAlexander Hansen int run_console(struct console *console)
9196925740dSAlexander Hansen {
9206925740dSAlexander Hansen 	sighandler_t sighandler_save = signal(SIGINT, sighandler);
9216925740dSAlexander Hansen 	ssize_t rc = 0;
9226925740dSAlexander Hansen 
9236925740dSAlexander Hansen 	for (;;) {
9246925740dSAlexander Hansen 		rc = run_console_iteration(console);
9256925740dSAlexander Hansen 		if (rc) {
926769cee1aSJeremy Kerr 			break;
9271a0e03b4SJeremy Kerr 		}
9282834c5b1SAndrew Jeffery 	}
929769cee1aSJeremy Kerr 
930769cee1aSJeremy Kerr 	signal(SIGINT, sighandler_save);
931f9c8f6caSCheng C Yang 	sd_bus_unref(console->bus);
932769cee1aSJeremy Kerr 
933769cee1aSJeremy Kerr 	return rc ? -1 : 0;
9341a0e03b4SJeremy Kerr }
935d831f960SJeremy Kerr static const struct option options[] = {
936d66195c1SJeremy Kerr 	{ "config", required_argument, 0, 'c' },
937954be0fbSAndrew Jeffery 	{ "console-id", required_argument, 0, 'i' },
938f5858b5bSJoel Stanley 	{ 0, 0, 0, 0 },
939d831f960SJeremy Kerr };
940d831f960SJeremy Kerr 
941d831f960SJeremy Kerr int main(int argc, char **argv)
942d831f960SJeremy Kerr {
9437f2bfb9bSMedicine Yeh 	size_t buffer_size = default_buffer_size;
944d66195c1SJeremy Kerr 	const char *config_filename = NULL;
9456221ce94SVishwanatha Subbanna 	const char *config_tty_kname = NULL;
9467f2bfb9bSMedicine Yeh 	const char *buffer_size_str = NULL;
947954be0fbSAndrew Jeffery 	const char *console_id = NULL;
9481a0e03b4SJeremy Kerr 	struct console *console;
949d66195c1SJeremy Kerr 	struct config *config;
950d66195c1SJeremy Kerr 	int rc;
951d831f960SJeremy Kerr 
952d831f960SJeremy Kerr 	for (;;) {
953b70f8713SAndrew Jeffery 		int c;
954b70f8713SAndrew Jeffery 		int idx;
955d831f960SJeremy Kerr 
956954be0fbSAndrew Jeffery 		c = getopt_long(argc, argv, "c:i:", options, &idx);
9572834c5b1SAndrew Jeffery 		if (c == -1) {
958d831f960SJeremy Kerr 			break;
9592834c5b1SAndrew Jeffery 		}
960d831f960SJeremy Kerr 
961d831f960SJeremy Kerr 		switch (c) {
962d66195c1SJeremy Kerr 		case 'c':
963d66195c1SJeremy Kerr 			config_filename = optarg;
964d831f960SJeremy Kerr 			break;
965954be0fbSAndrew Jeffery 		case 'i':
966954be0fbSAndrew Jeffery 			console_id = optarg;
967954be0fbSAndrew Jeffery 			break;
968d831f960SJeremy Kerr 		case 'h':
969d831f960SJeremy Kerr 		case '?':
970d831f960SJeremy Kerr 			usage(argv[0]);
971d66195c1SJeremy Kerr 			return EXIT_SUCCESS;
972d831f960SJeremy Kerr 		}
973d831f960SJeremy Kerr 	}
974d831f960SJeremy Kerr 
9752834c5b1SAndrew Jeffery 	if (optind < argc) {
9766221ce94SVishwanatha Subbanna 		config_tty_kname = argv[optind];
9772834c5b1SAndrew Jeffery 	}
9786221ce94SVishwanatha Subbanna 
9797f2bfb9bSMedicine Yeh 	config = config_init(config_filename);
9807f2bfb9bSMedicine Yeh 
981d66195c1SJeremy Kerr 	console = malloc(sizeof(struct console));
982d66195c1SJeremy Kerr 	memset(console, 0, sizeof(*console));
983a72711afSAndrew Jeffery 	console->pollfds =
984a72711afSAndrew Jeffery 		calloc(MAX_INTERNAL_POLLFD, sizeof(*console->pollfds));
9857f2bfb9bSMedicine Yeh 	buffer_size_str = config_get_value(config, "ringbuffer-size");
9867f2bfb9bSMedicine Yeh 	if (buffer_size_str) {
9877f2bfb9bSMedicine Yeh 		rc = config_parse_bytesize(buffer_size_str, &buffer_size);
9887f2bfb9bSMedicine Yeh 		if (rc) {
9897f2bfb9bSMedicine Yeh 			warn("Invalid ringbuffer-size. Default to %zukB",
9907f2bfb9bSMedicine Yeh 			     buffer_size >> 10);
9917f2bfb9bSMedicine Yeh 		}
9927f2bfb9bSMedicine Yeh 	}
993f733c85aSJeremy Kerr 	console->rb = ringbuffer_init(buffer_size);
9943f8d5bebSAndrew Jeffery 	if (!console->rb) {
9953f8d5bebSAndrew Jeffery 		rc = -1;
9963f8d5bebSAndrew Jeffery 		goto out_config_fini;
9973f8d5bebSAndrew Jeffery 	}
998329a35f5SJeremy Kerr 
999954be0fbSAndrew Jeffery 	if (set_socket_info(console, config, console_id)) {
100029c59c44SAndrew Jeffery 		rc = -1;
10013f8d5bebSAndrew Jeffery 		goto out_ringbuffer_fini;
1002b14ca19cSNinad Palsule 	}
1003b14ca19cSNinad Palsule 
10047dc08baaSZev Weiss 	uart_routing_init(config);
10057dc08baaSZev Weiss 
1006d769eecfSAndrew Jeffery 	rc = tty_init(console, config, config_tty_kname);
10072834c5b1SAndrew Jeffery 	if (rc) {
10083f8d5bebSAndrew Jeffery 		goto out_ringbuffer_fini;
10092834c5b1SAndrew Jeffery 	}
1010d831f960SJeremy Kerr 
1011498a4a81SAndrew Jeffery 	rc = dbus_init(console, config);
1012498a4a81SAndrew Jeffery 	if (rc) {
1013498a4a81SAndrew Jeffery 		goto out_tty_fini;
1014498a4a81SAndrew Jeffery 	}
1015f9c8f6caSCheng C Yang 
1016d47963e5SJeremy Kerr 	handlers_init(console, config);
1017d831f960SJeremy Kerr 
10181a0e03b4SJeremy Kerr 	rc = run_console(console);
1019d831f960SJeremy Kerr 
10201a0e03b4SJeremy Kerr 	handlers_fini(console);
1021d831f960SJeremy Kerr 
1022498a4a81SAndrew Jeffery out_tty_fini:
102330ea6385SAndrew Jeffery 	tty_fini(console);
102430ea6385SAndrew Jeffery 
10253f8d5bebSAndrew Jeffery out_ringbuffer_fini:
10263f8d5bebSAndrew Jeffery 	ringbuffer_fini(console->rb);
10273f8d5bebSAndrew Jeffery 
1028d66195c1SJeremy Kerr out_config_fini:
1029d66195c1SJeremy Kerr 	config_fini(config);
1030d66195c1SJeremy Kerr 
103189ea8198SJeremy Kerr 	free(console->pollers);
103289ea8198SJeremy Kerr 	free(console->pollfds);
10331a0e03b4SJeremy Kerr 	free(console);
1034d831f960SJeremy Kerr 
1035d831f960SJeremy Kerr 	return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
1036d831f960SJeremy Kerr }
1037