xref: /openbmc/obmc-console/console-server.c (revision 7dc08baac216253b289f60c3cb0b39179f7aaefc)
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>
31*7dc08baaSZev 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 
45f733c85aSJeremy Kerr /* size of the shared backlog ringbuffer */
465db8c792SAndrew Jeffery const size_t 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 
15330ea6385SAndrew Jeffery 	/* Arbitrarily pick an attribute to differentiate UART vs VUART */
1547c02ae1eSAndrew Jeffery 	rc = asprintf(&tty_vuart_lpc_addr, "%s/lpc_address", tty_sysfs_devnode);
15530ea6385SAndrew Jeffery 	if (rc < 0) {
15630ea6385SAndrew Jeffery 		goto out_free;
15730ea6385SAndrew Jeffery 	}
15830ea6385SAndrew Jeffery 
15930ea6385SAndrew Jeffery 	rc = access(tty_vuart_lpc_addr, F_OK);
16030ea6385SAndrew Jeffery 	console->tty.type = (!rc) ? TTY_DEVICE_VUART : TTY_DEVICE_UART;
16130ea6385SAndrew Jeffery 
16217217845SJeremy Kerr 	rc = 0;
16317217845SJeremy Kerr 
16417217845SJeremy Kerr out_free:
16530ea6385SAndrew Jeffery 	free(tty_vuart_lpc_addr);
16617217845SJeremy Kerr 	free(tty_class_device_link);
16717217845SJeremy Kerr 	free(tty_device_tty_dir);
16817217845SJeremy Kerr 	free(tty_device_reldir);
16945ad7676SYi Li 	free(tty_path_input);
17045ad7676SYi Li 	free(tty_path_input_real);
17117217845SJeremy Kerr 	return rc;
17217217845SJeremy Kerr }
17317217845SJeremy Kerr 
1741a0e03b4SJeremy Kerr static int tty_set_sysfs_attr(struct console *console, const char *name,
175957818b4SJeremy Kerr 			      int value)
176957818b4SJeremy Kerr {
177957818b4SJeremy Kerr 	char *path;
178957818b4SJeremy Kerr 	FILE *fp;
179957818b4SJeremy Kerr 	int rc;
180957818b4SJeremy Kerr 
18130ea6385SAndrew Jeffery 	assert(console->tty.type == TTY_DEVICE_VUART);
18230ea6385SAndrew Jeffery 
18330ea6385SAndrew Jeffery 	if (!console->tty.vuart.sysfs_devnode) {
18430ea6385SAndrew Jeffery 		return -1;
18530ea6385SAndrew Jeffery 	}
18630ea6385SAndrew Jeffery 
18730ea6385SAndrew Jeffery 	rc = asprintf(&path, "%s/%s", console->tty.vuart.sysfs_devnode, name);
1882834c5b1SAndrew Jeffery 	if (rc < 0) {
189957818b4SJeremy Kerr 		return -1;
1902834c5b1SAndrew Jeffery 	}
191957818b4SJeremy Kerr 
192957818b4SJeremy Kerr 	fp = fopen(path, "w");
193957818b4SJeremy Kerr 	if (!fp) {
194a72711afSAndrew Jeffery 		warn("Can't access attribute %s on device %s", name,
19530ea6385SAndrew Jeffery 		     console->tty.kname);
196957818b4SJeremy Kerr 		rc = -1;
197957818b4SJeremy Kerr 		goto out_free;
198957818b4SJeremy Kerr 	}
199957818b4SJeremy Kerr 	setvbuf(fp, NULL, _IONBF, 0);
200957818b4SJeremy Kerr 
201957818b4SJeremy Kerr 	rc = fprintf(fp, "0x%x", value);
2022834c5b1SAndrew Jeffery 	if (rc < 0) {
203a72711afSAndrew Jeffery 		warn("Error writing to %s attribute of device %s", name,
20430ea6385SAndrew Jeffery 		     console->tty.kname);
2052834c5b1SAndrew Jeffery 	}
206957818b4SJeremy Kerr 	fclose(fp);
207957818b4SJeremy Kerr 
208957818b4SJeremy Kerr out_free:
209957818b4SJeremy Kerr 	free(path);
210957818b4SJeremy Kerr 	return rc;
211957818b4SJeremy Kerr }
212957818b4SJeremy Kerr 
213d831f960SJeremy Kerr /**
214c7fbcd48SBenjamin Fair  * Set termios attributes on the console tty.
21554e9569dSJeremy Kerr  */
216e258e51fSNinad Palsule void tty_init_termios(struct console *console)
21754e9569dSJeremy Kerr {
21854e9569dSJeremy Kerr 	struct termios termios;
21954e9569dSJeremy Kerr 	int rc;
22054e9569dSJeremy Kerr 
22130ea6385SAndrew Jeffery 	rc = tcgetattr(console->tty.fd, &termios);
22254e9569dSJeremy Kerr 	if (rc) {
22354e9569dSJeremy Kerr 		warn("Can't read tty termios");
22454e9569dSJeremy Kerr 		return;
22554e9569dSJeremy Kerr 	}
22654e9569dSJeremy Kerr 
22730ea6385SAndrew Jeffery 	if (console->tty.type == TTY_DEVICE_UART && console->tty.uart.baud) {
22830ea6385SAndrew Jeffery 		if (cfsetspeed(&termios, console->tty.uart.baud) < 0) {
22930ea6385SAndrew Jeffery 			warn("Couldn't set speeds for %s", console->tty.kname);
230c7fbcd48SBenjamin Fair 		}
2312834c5b1SAndrew Jeffery 	}
232c7fbcd48SBenjamin Fair 
233c7fbcd48SBenjamin Fair 	/* Set console to raw mode: we don't want any processing to occur on
234c7fbcd48SBenjamin Fair 	 * the underlying terminal input/output.
235c7fbcd48SBenjamin Fair 	 */
23654e9569dSJeremy Kerr 	cfmakeraw(&termios);
237c7fbcd48SBenjamin Fair 
23830ea6385SAndrew Jeffery 	rc = tcsetattr(console->tty.fd, TCSANOW, &termios);
2392834c5b1SAndrew Jeffery 	if (rc) {
24030ea6385SAndrew Jeffery 		warn("Can't set terminal options for %s", console->tty.kname);
24154e9569dSJeremy Kerr 	}
2422834c5b1SAndrew Jeffery }
24354e9569dSJeremy Kerr 
24454e9569dSJeremy Kerr /**
245d831f960SJeremy Kerr  * Open and initialise the serial device
246d831f960SJeremy Kerr  */
24730ea6385SAndrew Jeffery static void tty_init_vuart_io(struct console *console)
248d831f960SJeremy Kerr {
24930ea6385SAndrew Jeffery 	assert(console->tty.type == TTY_DEVICE_VUART);
25030ea6385SAndrew Jeffery 
25130ea6385SAndrew Jeffery 	if (console->tty.vuart.sirq) {
25230ea6385SAndrew Jeffery 		tty_set_sysfs_attr(console, "sirq", console->tty.vuart.sirq);
2532834c5b1SAndrew Jeffery 	}
254957818b4SJeremy Kerr 
25530ea6385SAndrew Jeffery 	if (console->tty.vuart.lpc_addr) {
25630ea6385SAndrew Jeffery 		tty_set_sysfs_attr(console, "lpc_address",
25730ea6385SAndrew Jeffery 				   console->tty.vuart.lpc_addr);
25830ea6385SAndrew Jeffery 	}
25930ea6385SAndrew Jeffery }
26030ea6385SAndrew Jeffery 
26130ea6385SAndrew Jeffery static int tty_init_io(struct console *console)
26230ea6385SAndrew Jeffery {
26330ea6385SAndrew Jeffery 	console->tty.fd = open(console->tty.dev, O_RDWR);
26430ea6385SAndrew Jeffery 	if (console->tty.fd <= 0) {
26530ea6385SAndrew Jeffery 		warn("Can't open tty %s", console->tty.dev);
266d831f960SJeremy Kerr 		return -1;
267d831f960SJeremy Kerr 	}
268d831f960SJeremy Kerr 
269d831f960SJeremy Kerr 	/* Disable character delay. We may want to later enable this when
270d831f960SJeremy Kerr 	 * we detect larger amounts of data
271d831f960SJeremy Kerr 	 */
27230ea6385SAndrew Jeffery 	fcntl(console->tty.fd, F_SETFL, FNDELAY);
273d831f960SJeremy Kerr 
27454e9569dSJeremy Kerr 	tty_init_termios(console);
27554e9569dSJeremy Kerr 
27630ea6385SAndrew Jeffery 	console->pollfds[console->n_pollers].fd = console->tty.fd;
277329a35f5SJeremy Kerr 	console->pollfds[console->n_pollers].events = POLLIN;
278329a35f5SJeremy Kerr 
279d831f960SJeremy Kerr 	return 0;
280d831f960SJeremy Kerr }
281d831f960SJeremy Kerr 
28230ea6385SAndrew Jeffery static int tty_init_vuart(struct console *console, struct config *config)
283d66195c1SJeremy Kerr {
284fd883a88SAndrew Jeffery 	unsigned long parsed;
285d66195c1SJeremy Kerr 	const char *val;
286d66195c1SJeremy Kerr 	char *endp;
28730ea6385SAndrew Jeffery 
28830ea6385SAndrew Jeffery 	assert(console->tty.type == TTY_DEVICE_VUART);
289d66195c1SJeremy Kerr 
290d66195c1SJeremy Kerr 	val = config_get_value(config, "lpc-address");
291d66195c1SJeremy Kerr 	if (val) {
292fd883a88SAndrew Jeffery 		errno = 0;
293fd883a88SAndrew Jeffery 		parsed = strtoul(val, &endp, 0);
294fd883a88SAndrew Jeffery 		if (parsed == ULONG_MAX && errno == ERANGE) {
295fd883a88SAndrew Jeffery 			warn("Cannot interpret 'lpc-address' value as an unsigned long: '%s'",
296fd883a88SAndrew Jeffery 			     val);
297fd883a88SAndrew Jeffery 			return -1;
298fd883a88SAndrew Jeffery 		}
299fd883a88SAndrew Jeffery 
300fd883a88SAndrew Jeffery 		if (parsed > UINT16_MAX) {
301fd883a88SAndrew Jeffery 			warn("Invalid LPC address '%s'", val);
302fd883a88SAndrew Jeffery 			return -1;
303fd883a88SAndrew Jeffery 		}
304fd883a88SAndrew Jeffery 
30530ea6385SAndrew Jeffery 		console->tty.vuart.lpc_addr = (uint16_t)parsed;
306d66195c1SJeremy Kerr 		if (endp == optarg) {
307d66195c1SJeremy Kerr 			warn("Invalid LPC address: '%s'", val);
308d66195c1SJeremy Kerr 			return -1;
309d66195c1SJeremy Kerr 		}
310d66195c1SJeremy Kerr 	}
311d66195c1SJeremy Kerr 
312d66195c1SJeremy Kerr 	val = config_get_value(config, "sirq");
313d66195c1SJeremy Kerr 	if (val) {
314fd883a88SAndrew Jeffery 		errno = 0;
315fd883a88SAndrew Jeffery 		parsed = strtoul(val, &endp, 0);
316fd883a88SAndrew Jeffery 		if (parsed == ULONG_MAX && errno == ERANGE) {
317fd883a88SAndrew Jeffery 			warn("Cannot interpret 'sirq' value as an unsigned long: '%s'",
318fd883a88SAndrew Jeffery 			     val);
319fd883a88SAndrew Jeffery 		}
320fd883a88SAndrew Jeffery 
3212834c5b1SAndrew Jeffery 		if (parsed > 16) {
322fd883a88SAndrew Jeffery 			warn("Invalid LPC SERIRQ: '%s'", val);
3232834c5b1SAndrew Jeffery 		}
324fd883a88SAndrew Jeffery 
32530ea6385SAndrew Jeffery 		console->tty.vuart.sirq = (int)parsed;
3262834c5b1SAndrew Jeffery 		if (endp == optarg) {
327d66195c1SJeremy Kerr 			warn("Invalid sirq: '%s'", val);
328d66195c1SJeremy Kerr 		}
3292834c5b1SAndrew Jeffery 	}
330d66195c1SJeremy Kerr 
33130ea6385SAndrew Jeffery 	return 0;
3322834c5b1SAndrew Jeffery }
333c7fbcd48SBenjamin Fair 
33430ea6385SAndrew Jeffery static int tty_init(struct console *console, struct config *config,
33530ea6385SAndrew Jeffery 		    const char *tty_arg)
33630ea6385SAndrew Jeffery {
33730ea6385SAndrew Jeffery 	const char *val;
33830ea6385SAndrew Jeffery 	int rc;
33930ea6385SAndrew Jeffery 
340d769eecfSAndrew Jeffery 	if (tty_arg) {
34130ea6385SAndrew Jeffery 		console->tty.kname = tty_arg;
342d769eecfSAndrew Jeffery 	} else if ((val = config_get_value(config, "upstream-tty"))) {
34330ea6385SAndrew Jeffery 		console->tty.kname = val;
344d769eecfSAndrew Jeffery 	} else {
345d66195c1SJeremy Kerr 		warnx("Error: No TTY device specified");
346d66195c1SJeremy Kerr 		return -1;
347d66195c1SJeremy Kerr 	}
348d66195c1SJeremy Kerr 
349d66195c1SJeremy Kerr 	rc = tty_find_device(console);
3502834c5b1SAndrew Jeffery 	if (rc) {
351d66195c1SJeremy Kerr 		return rc;
3522834c5b1SAndrew Jeffery 	}
353d66195c1SJeremy Kerr 
35430ea6385SAndrew Jeffery 	switch (console->tty.type) {
35530ea6385SAndrew Jeffery 	case TTY_DEVICE_VUART:
35630ea6385SAndrew Jeffery 		rc = tty_init_vuart(console, config);
35730ea6385SAndrew Jeffery 		if (rc) {
358d66195c1SJeremy Kerr 			return rc;
359d66195c1SJeremy Kerr 		}
360d66195c1SJeremy Kerr 
36130ea6385SAndrew Jeffery 		tty_init_vuart_io(console);
36230ea6385SAndrew Jeffery 		break;
36330ea6385SAndrew Jeffery 	case TTY_DEVICE_UART:
36430ea6385SAndrew Jeffery 		val = config_get_value(config, "baud");
36530ea6385SAndrew Jeffery 		if (val) {
36630ea6385SAndrew Jeffery 			if (config_parse_baud(&console->tty.uart.baud, val)) {
36730ea6385SAndrew Jeffery 				warnx("Invalid baud rate: '%s'", val);
36830ea6385SAndrew Jeffery 			}
36930ea6385SAndrew Jeffery 		}
37030ea6385SAndrew Jeffery 		break;
37130ea6385SAndrew Jeffery 	case TTY_DEVICE_PTY:
37230ea6385SAndrew Jeffery 		break;
37330ea6385SAndrew Jeffery 	case TTY_DEVICE_UNDEFINED:
37430ea6385SAndrew Jeffery 	default:
37530ea6385SAndrew Jeffery 		warnx("Cannot configure unrecognised TTY device");
37630ea6385SAndrew Jeffery 		return -1;
37730ea6385SAndrew Jeffery 	}
37830ea6385SAndrew Jeffery 
37930ea6385SAndrew Jeffery 	return tty_init_io(console);
38030ea6385SAndrew Jeffery }
38130ea6385SAndrew Jeffery 
38230ea6385SAndrew Jeffery static void tty_fini(struct console *console)
38330ea6385SAndrew Jeffery {
38430ea6385SAndrew Jeffery 	if (console->tty.type == TTY_DEVICE_VUART) {
38530ea6385SAndrew Jeffery 		free(console->tty.vuart.sysfs_devnode);
38630ea6385SAndrew Jeffery 	}
38730ea6385SAndrew Jeffery 	free(console->tty.dev);
38830ea6385SAndrew Jeffery }
38930ea6385SAndrew Jeffery 
390*7dc08baaSZev Weiss static int write_to_path(const char *path, const char *data)
391*7dc08baaSZev Weiss {
392*7dc08baaSZev Weiss 	int rc = 0;
393*7dc08baaSZev Weiss 	FILE *f = fopen(path, "w");
394*7dc08baaSZev Weiss 	if (!f) {
395*7dc08baaSZev Weiss 		return -1;
396*7dc08baaSZev Weiss 	}
397*7dc08baaSZev Weiss 
398*7dc08baaSZev Weiss 	if (fprintf(f, "%s", data) < 0) {
399*7dc08baaSZev Weiss 		rc = -1;
400*7dc08baaSZev Weiss 	}
401*7dc08baaSZev Weiss 
402*7dc08baaSZev Weiss 	if (fclose(f)) {
403*7dc08baaSZev Weiss 		rc = -1;
404*7dc08baaSZev Weiss 	}
405*7dc08baaSZev Weiss 
406*7dc08baaSZev Weiss 	return rc;
407*7dc08baaSZev Weiss }
408*7dc08baaSZev Weiss 
409*7dc08baaSZev Weiss #define ASPEED_UART_ROUTING_PATTERN                                            \
410*7dc08baaSZev Weiss 	"/sys/bus/platform/drivers/aspeed-uart-routing/*.uart-routing"
411*7dc08baaSZev Weiss 
412*7dc08baaSZev Weiss static void uart_routing_init(struct config *config)
413*7dc08baaSZev Weiss {
414*7dc08baaSZev Weiss 	const char *muxcfg;
415*7dc08baaSZev Weiss 	const char *p;
416*7dc08baaSZev Weiss 	size_t buflen;
417*7dc08baaSZev Weiss 	char *sink;
418*7dc08baaSZev Weiss 	char *source;
419*7dc08baaSZev Weiss 	char *muxdir;
420*7dc08baaSZev Weiss 	char *path;
421*7dc08baaSZev Weiss 	glob_t globbuf;
422*7dc08baaSZev Weiss 
423*7dc08baaSZev Weiss 	muxcfg = config_get_value(config, "aspeed-uart-routing");
424*7dc08baaSZev Weiss 	if (!muxcfg) {
425*7dc08baaSZev Weiss 		return;
426*7dc08baaSZev Weiss 	}
427*7dc08baaSZev Weiss 
428*7dc08baaSZev Weiss 	/* Find the driver's sysfs directory */
429*7dc08baaSZev Weiss 	if (glob(ASPEED_UART_ROUTING_PATTERN, GLOB_ERR | GLOB_NOSORT, NULL,
430*7dc08baaSZev Weiss 		 &globbuf) != 0) {
431*7dc08baaSZev Weiss 		warn("Couldn't find uart-routing driver directory, cannot apply config");
432*7dc08baaSZev Weiss 		return;
433*7dc08baaSZev Weiss 	}
434*7dc08baaSZev Weiss 	if (globbuf.gl_pathc != 1) {
435*7dc08baaSZev Weiss 		warnx("Found %zd uart-routing driver directories, cannot apply config",
436*7dc08baaSZev Weiss 		      globbuf.gl_pathc);
437*7dc08baaSZev Weiss 		goto out_free_glob;
438*7dc08baaSZev Weiss 	}
439*7dc08baaSZev Weiss 	muxdir = globbuf.gl_pathv[0];
440*7dc08baaSZev Weiss 
441*7dc08baaSZev Weiss 	/*
442*7dc08baaSZev Weiss 	 * Rather than faff about tracking a bunch of separate buffer sizes,
443*7dc08baaSZev Weiss 	 * just use one (worst-case) size for all of them -- +2 for a trailing
444*7dc08baaSZev Weiss 	 * NUL and a '/' separator to construct the sysfs file path.
445*7dc08baaSZev Weiss 	 */
446*7dc08baaSZev Weiss 	buflen = strlen(muxdir) + strlen(muxcfg) + 2;
447*7dc08baaSZev Weiss 
448*7dc08baaSZev Weiss 	sink = malloc(buflen);
449*7dc08baaSZev Weiss 	source = malloc(buflen);
450*7dc08baaSZev Weiss 	path = malloc(buflen);
451*7dc08baaSZev Weiss 	if (!path || !sink || !source) {
452*7dc08baaSZev Weiss 		warnx("Out of memory applying uart routing config");
453*7dc08baaSZev Weiss 		goto out_free_bufs;
454*7dc08baaSZev Weiss 	}
455*7dc08baaSZev Weiss 
456*7dc08baaSZev Weiss 	p = muxcfg;
457*7dc08baaSZev Weiss 	while (*p) {
458*7dc08baaSZev Weiss 		ssize_t bytes_scanned;
459*7dc08baaSZev Weiss 
460*7dc08baaSZev Weiss 		if (sscanf(p, " %[^:/ \t]:%[^: \t] %zn", sink, source,
461*7dc08baaSZev Weiss 			   &bytes_scanned) != 2) {
462*7dc08baaSZev Weiss 			warnx("Invalid syntax in aspeed uart config: '%s' not applied",
463*7dc08baaSZev Weiss 			      p);
464*7dc08baaSZev Weiss 			break;
465*7dc08baaSZev Weiss 		}
466*7dc08baaSZev Weiss 		p += bytes_scanned;
467*7dc08baaSZev Weiss 
468*7dc08baaSZev Weiss 		/*
469*7dc08baaSZev Weiss 		 * Check that the sink name looks reasonable before proceeding
470*7dc08baaSZev Weiss 		 * (there are other writable files in the same directory that
471*7dc08baaSZev Weiss 		 * we shouldn't be touching, such as 'driver_override' and
472*7dc08baaSZev Weiss 		 * 'uevent').
473*7dc08baaSZev Weiss 		 */
474*7dc08baaSZev Weiss 		if (strncmp(sink, "io", strlen("io")) != 0 &&
475*7dc08baaSZev Weiss 		    strncmp(sink, "uart", strlen("uart")) != 0) {
476*7dc08baaSZev Weiss 			warnx("Skipping invalid uart routing name '%s' (must be ioN or uartN)",
477*7dc08baaSZev Weiss 			      sink);
478*7dc08baaSZev Weiss 			continue;
479*7dc08baaSZev Weiss 		}
480*7dc08baaSZev Weiss 
481*7dc08baaSZev Weiss 		snprintf(path, buflen, "%s/%s", muxdir, sink);
482*7dc08baaSZev Weiss 		if (write_to_path(path, source)) {
483*7dc08baaSZev Weiss 			warn("Failed to apply uart-routing config '%s:%s'",
484*7dc08baaSZev Weiss 			     sink, source);
485*7dc08baaSZev Weiss 		}
486*7dc08baaSZev Weiss 	}
487*7dc08baaSZev Weiss 
488*7dc08baaSZev Weiss out_free_bufs:
489*7dc08baaSZev Weiss 	free(path);
490*7dc08baaSZev Weiss 	free(source);
491*7dc08baaSZev Weiss 	free(sink);
492*7dc08baaSZev Weiss out_free_glob:
493*7dc08baaSZev Weiss 	globfree(&globbuf);
494*7dc08baaSZev Weiss }
495*7dc08baaSZev Weiss 
4961a0e03b4SJeremy Kerr int console_data_out(struct console *console, const uint8_t *data, size_t len)
497d831f960SJeremy Kerr {
49830ea6385SAndrew Jeffery 	return write_buf_to_fd(console->tty.fd, data, len);
499d831f960SJeremy Kerr }
500d831f960SJeremy Kerr 
5015ba20b5bSNinad Palsule /* Prepare a socket name */
502954be0fbSAndrew Jeffery static int set_socket_info(struct console *console, struct config *config,
503954be0fbSAndrew Jeffery 			   const char *console_id)
504b14ca19cSNinad Palsule {
505b14ca19cSNinad Palsule 	ssize_t len;
506b14ca19cSNinad Palsule 
5075ba20b5bSNinad Palsule 	/* Get console id */
5085ba20b5bSNinad Palsule 	console->console_id = config_resolve_console_id(config, console_id);
509954be0fbSAndrew Jeffery 
510b14ca19cSNinad Palsule 	/* Get the socket name/path */
5115ba20b5bSNinad Palsule 	len = console_socket_path(console->socket_name, console->console_id);
512b14ca19cSNinad Palsule 	if (len < 0) {
513b14ca19cSNinad Palsule 		warn("Failed to set socket path: %s", strerror(errno));
514b14ca19cSNinad Palsule 		return EXIT_FAILURE;
515b14ca19cSNinad Palsule 	}
516b14ca19cSNinad Palsule 
517b14ca19cSNinad Palsule 	/* Socket name is not a null terminated string hence save the length */
518b14ca19cSNinad Palsule 	console->socket_name_len = len;
519b14ca19cSNinad Palsule 
520b14ca19cSNinad Palsule 	return 0;
521b14ca19cSNinad Palsule }
522b14ca19cSNinad Palsule 
523d47963e5SJeremy Kerr static void handlers_init(struct console *console, struct config *config)
524d831f960SJeremy Kerr {
525b70f8713SAndrew Jeffery 	/* NOLINTBEGIN(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) */
526b70f8713SAndrew Jeffery 	extern struct handler *__start_handlers;
527b70f8713SAndrew Jeffery 	extern struct handler *__stop_handlers;
528b70f8713SAndrew Jeffery 	/* NOLINTEND(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) */
5291a0e03b4SJeremy Kerr 	struct handler *handler;
530b70f8713SAndrew Jeffery 	int i;
531b70f8713SAndrew Jeffery 	int rc;
532d831f960SJeremy Kerr 
5331a0e03b4SJeremy Kerr 	console->n_handlers = &__stop_handlers - &__start_handlers;
5341a0e03b4SJeremy Kerr 	console->handlers = &__start_handlers;
535d831f960SJeremy Kerr 
5365c359cc6SAndrew Jeffery 	printf("%ld handler%s\n", console->n_handlers,
5371a0e03b4SJeremy Kerr 	       console->n_handlers == 1 ? "" : "s");
538d831f960SJeremy Kerr 
5391a0e03b4SJeremy Kerr 	for (i = 0; i < console->n_handlers; i++) {
5401a0e03b4SJeremy Kerr 		handler = console->handlers[i];
5411a0e03b4SJeremy Kerr 
542021b91f0SJeremy Kerr 		rc = 0;
5432834c5b1SAndrew Jeffery 		if (handler->init) {
544021b91f0SJeremy Kerr 			rc = handler->init(handler, console, config);
5452834c5b1SAndrew Jeffery 		}
546021b91f0SJeremy Kerr 
547021b91f0SJeremy Kerr 		handler->active = rc == 0;
548021b91f0SJeremy Kerr 
549021b91f0SJeremy Kerr 		printf("  %s [%sactive]\n", handler->name,
550021b91f0SJeremy Kerr 		       handler->active ? "" : "in");
551d831f960SJeremy Kerr 	}
552d831f960SJeremy Kerr }
553d831f960SJeremy Kerr 
5541a0e03b4SJeremy Kerr static void handlers_fini(struct console *console)
555d831f960SJeremy Kerr {
5561a0e03b4SJeremy Kerr 	struct handler *handler;
5571a0e03b4SJeremy Kerr 	int i;
5581a0e03b4SJeremy Kerr 
5591a0e03b4SJeremy Kerr 	for (i = 0; i < console->n_handlers; i++) {
5601a0e03b4SJeremy Kerr 		handler = console->handlers[i];
5612834c5b1SAndrew Jeffery 		if (handler->fini && handler->active) {
5621a0e03b4SJeremy Kerr 			handler->fini(handler);
5631a0e03b4SJeremy Kerr 		}
564d831f960SJeremy Kerr 	}
5652834c5b1SAndrew Jeffery }
566d831f960SJeremy Kerr 
5671cecc5deSJohnathan Mantey static int get_current_time(struct timeval *tv)
5681cecc5deSJohnathan Mantey {
5691cecc5deSJohnathan Mantey 	struct timespec t;
5701cecc5deSJohnathan Mantey 	int rc;
5711cecc5deSJohnathan Mantey 
5721cecc5deSJohnathan Mantey 	/*
5731cecc5deSJohnathan Mantey 	 * We use clock_gettime(CLOCK_MONOTONIC) so we're immune to
5741cecc5deSJohnathan Mantey 	 * local time changes. However, a struct timeval is more
5751cecc5deSJohnathan Mantey 	 * convenient for calculations, so convert to that.
5761cecc5deSJohnathan Mantey 	 */
5771cecc5deSJohnathan Mantey 	rc = clock_gettime(CLOCK_MONOTONIC, &t);
5782834c5b1SAndrew Jeffery 	if (rc) {
5791cecc5deSJohnathan Mantey 		return rc;
5802834c5b1SAndrew Jeffery 	}
5811cecc5deSJohnathan Mantey 
5821cecc5deSJohnathan Mantey 	tv->tv_sec = t.tv_sec;
5831cecc5deSJohnathan Mantey 	tv->tv_usec = t.tv_nsec / 1000;
5841cecc5deSJohnathan Mantey 
5851cecc5deSJohnathan Mantey 	return 0;
5861cecc5deSJohnathan Mantey }
5871cecc5deSJohnathan Mantey 
588a72711afSAndrew Jeffery struct ringbuffer_consumer *
589a72711afSAndrew Jeffery console_ringbuffer_consumer_register(struct console *console,
590f733c85aSJeremy Kerr 				     ringbuffer_poll_fn_t poll_fn, void *data)
591d831f960SJeremy Kerr {
592f733c85aSJeremy Kerr 	return ringbuffer_consumer_register(console->rb, poll_fn, data);
593d831f960SJeremy Kerr }
594d831f960SJeremy Kerr 
59555c9712dSJeremy Kerr struct poller *console_poller_register(struct console *console,
596a72711afSAndrew Jeffery 				       struct handler *handler,
597a72711afSAndrew Jeffery 				       poller_event_fn_t poller_fn,
5981cecc5deSJohnathan Mantey 				       poller_timeout_fn_t timeout_fn, int fd,
5991cecc5deSJohnathan Mantey 				       int events, void *data)
600d831f960SJeremy Kerr {
601329a35f5SJeremy Kerr 	struct poller *poller;
6025c359cc6SAndrew Jeffery 	long n;
603329a35f5SJeremy Kerr 
604329a35f5SJeremy Kerr 	poller = malloc(sizeof(*poller));
605329a35f5SJeremy Kerr 	poller->remove = false;
606329a35f5SJeremy Kerr 	poller->handler = handler;
6071cecc5deSJohnathan Mantey 	poller->event_fn = poller_fn;
6081cecc5deSJohnathan Mantey 	poller->timeout_fn = timeout_fn;
609329a35f5SJeremy Kerr 	poller->data = data;
610329a35f5SJeremy Kerr 
611329a35f5SJeremy Kerr 	/* add one to our pollers array */
612329a35f5SJeremy Kerr 	n = console->n_pollers++;
61391b52175SAndrew Jeffery 	/*
61491b52175SAndrew Jeffery 	 * We're managing an array of pointers to aggregates, so don't warn about sizeof() on a
61591b52175SAndrew Jeffery 	 * pointer type.
61691b52175SAndrew Jeffery 	 */
61791b52175SAndrew Jeffery 	/* NOLINTBEGIN(bugprone-sizeof-expression) */
61891b52175SAndrew Jeffery 	console->pollers = reallocarray(console->pollers, console->n_pollers,
61991b52175SAndrew Jeffery 					sizeof(*console->pollers));
62091b52175SAndrew Jeffery 	/* NOLINTEND(bugprone-sizeof-expression) */
621329a35f5SJeremy Kerr 
622329a35f5SJeremy Kerr 	console->pollers[n] = poller;
623329a35f5SJeremy Kerr 
624329a35f5SJeremy Kerr 	/* increase pollfds array too  */
6254e44c790SAndrew Jeffery 	console->pollfds = reallocarray(
6264e44c790SAndrew Jeffery 		console->pollfds, (MAX_INTERNAL_POLLFD + console->n_pollers),
62791b52175SAndrew Jeffery 		sizeof(*console->pollfds));
628329a35f5SJeremy Kerr 
629329a35f5SJeremy Kerr 	/* shift the end pollfds up by one */
630a72711afSAndrew Jeffery 	memcpy(&console->pollfds[n + 1], &console->pollfds[n],
631f9c8f6caSCheng C Yang 	       sizeof(*console->pollfds) * MAX_INTERNAL_POLLFD);
632329a35f5SJeremy Kerr 
633329a35f5SJeremy Kerr 	console->pollfds[n].fd = fd;
6345c359cc6SAndrew Jeffery 	console->pollfds[n].events = (short)(events & 0x7fff);
635329a35f5SJeremy Kerr 
636329a35f5SJeremy Kerr 	return poller;
637329a35f5SJeremy Kerr }
638329a35f5SJeremy Kerr 
639a72711afSAndrew Jeffery void console_poller_unregister(struct console *console, struct poller *poller)
640329a35f5SJeremy Kerr {
641329a35f5SJeremy Kerr 	int i;
642329a35f5SJeremy Kerr 
643329a35f5SJeremy Kerr 	/* find the entry in our pollers array */
6442834c5b1SAndrew Jeffery 	for (i = 0; i < console->n_pollers; i++) {
6452834c5b1SAndrew Jeffery 		if (console->pollers[i] == poller) {
646329a35f5SJeremy Kerr 			break;
6472834c5b1SAndrew Jeffery 		}
6482834c5b1SAndrew Jeffery 	}
649329a35f5SJeremy Kerr 
650329a35f5SJeremy Kerr 	assert(i < console->n_pollers);
651329a35f5SJeremy Kerr 
652329a35f5SJeremy Kerr 	console->n_pollers--;
653329a35f5SJeremy Kerr 
65491b52175SAndrew Jeffery 	/*
65591b52175SAndrew Jeffery 	 * Remove the item from the pollers array...
65691b52175SAndrew Jeffery 	 *
65791b52175SAndrew Jeffery 	 * We're managing an array of pointers to aggregates, so don't warn about sizeof() on a
65891b52175SAndrew Jeffery 	 * pointer type.
65991b52175SAndrew Jeffery 	 */
66091b52175SAndrew Jeffery 	/* NOLINTBEGIN(bugprone-sizeof-expression) */
661329a35f5SJeremy Kerr 	memmove(&console->pollers[i], &console->pollers[i + 1],
662a72711afSAndrew Jeffery 		sizeof(*console->pollers) * (console->n_pollers - i));
663329a35f5SJeremy Kerr 
66491b52175SAndrew Jeffery 	console->pollers = reallocarray(console->pollers, console->n_pollers,
66591b52175SAndrew Jeffery 					sizeof(*console->pollers));
66691b52175SAndrew Jeffery 	/* NOLINTEND(bugprone-sizeof-expression) */
667329a35f5SJeremy Kerr 
668329a35f5SJeremy Kerr 	/* ... and the pollfds array */
669329a35f5SJeremy Kerr 	memmove(&console->pollfds[i], &console->pollfds[i + 1],
670329a35f5SJeremy Kerr 		sizeof(*console->pollfds) *
671f9c8f6caSCheng C Yang 			(MAX_INTERNAL_POLLFD + console->n_pollers - i));
672329a35f5SJeremy Kerr 
6734e44c790SAndrew Jeffery 	console->pollfds = reallocarray(
6744e44c790SAndrew Jeffery 		console->pollfds, (MAX_INTERNAL_POLLFD + console->n_pollers),
67591b52175SAndrew Jeffery 		sizeof(*console->pollfds));
676329a35f5SJeremy Kerr 
677329a35f5SJeremy Kerr 	free(poller);
678329a35f5SJeremy Kerr }
679329a35f5SJeremy Kerr 
6806b1fed27SJeremy Kerr void console_poller_set_events(struct console *console, struct poller *poller,
6816b1fed27SJeremy Kerr 			       int events)
6826b1fed27SJeremy Kerr {
6836b1fed27SJeremy Kerr 	int i;
6846b1fed27SJeremy Kerr 
6856b1fed27SJeremy Kerr 	/* find the entry in our pollers array */
6862834c5b1SAndrew Jeffery 	for (i = 0; i < console->n_pollers; i++) {
6872834c5b1SAndrew Jeffery 		if (console->pollers[i] == poller) {
6886b1fed27SJeremy Kerr 			break;
6892834c5b1SAndrew Jeffery 		}
6902834c5b1SAndrew Jeffery 	}
6916b1fed27SJeremy Kerr 
6925c359cc6SAndrew Jeffery 	console->pollfds[i].events = (short)(events & 0x7fff);
6936b1fed27SJeremy Kerr }
6946b1fed27SJeremy Kerr 
695fd048328SAndrew Jeffery void console_poller_set_timeout(struct console *console __attribute__((unused)),
696fd048328SAndrew Jeffery 				struct poller *poller, const struct timeval *tv)
6971cecc5deSJohnathan Mantey {
6981cecc5deSJohnathan Mantey 	struct timeval now;
6991cecc5deSJohnathan Mantey 	int rc;
7001cecc5deSJohnathan Mantey 
7011cecc5deSJohnathan Mantey 	rc = get_current_time(&now);
7022834c5b1SAndrew Jeffery 	if (rc) {
7031cecc5deSJohnathan Mantey 		return;
7042834c5b1SAndrew Jeffery 	}
7051cecc5deSJohnathan Mantey 
7061cecc5deSJohnathan Mantey 	timeradd(&now, tv, &poller->timeout);
7071cecc5deSJohnathan Mantey }
7081cecc5deSJohnathan Mantey 
7095c359cc6SAndrew Jeffery static long get_poll_timeout(struct console *console, struct timeval *cur_time)
7101cecc5deSJohnathan Mantey {
711b70f8713SAndrew Jeffery 	struct timeval *earliest;
712b70f8713SAndrew Jeffery 	struct timeval interval;
7131cecc5deSJohnathan Mantey 	struct poller *poller;
7141cecc5deSJohnathan Mantey 	int i;
7151cecc5deSJohnathan Mantey 
7161cecc5deSJohnathan Mantey 	earliest = NULL;
7171cecc5deSJohnathan Mantey 
7181cecc5deSJohnathan Mantey 	for (i = 0; i < console->n_pollers; i++) {
7191cecc5deSJohnathan Mantey 		poller = console->pollers[i];
7201cecc5deSJohnathan Mantey 
7211cecc5deSJohnathan Mantey 		if (poller->timeout_fn && timerisset(&poller->timeout) &&
7221cecc5deSJohnathan Mantey 		    (!earliest ||
7231cecc5deSJohnathan Mantey 		     (earliest && timercmp(&poller->timeout, earliest, <)))) {
7241cecc5deSJohnathan Mantey 			// poller is buffering data and needs the poll
7251cecc5deSJohnathan Mantey 			// function to timeout.
7261cecc5deSJohnathan Mantey 			earliest = &poller->timeout;
7271cecc5deSJohnathan Mantey 		}
7281cecc5deSJohnathan Mantey 	}
7291cecc5deSJohnathan Mantey 
7301cecc5deSJohnathan Mantey 	if (earliest) {
7311cecc5deSJohnathan Mantey 		if (timercmp(earliest, cur_time, >)) {
7321cecc5deSJohnathan Mantey 			/* recalculate the timeout period, time period has
7331cecc5deSJohnathan Mantey 			 * not elapsed */
7341cecc5deSJohnathan Mantey 			timersub(earliest, cur_time, &interval);
7351cecc5deSJohnathan Mantey 			return ((interval.tv_sec * 1000) +
7361cecc5deSJohnathan Mantey 				(interval.tv_usec / 1000));
7370b7b0477SAndrew Jeffery 		} /* return from poll immediately */
7381cecc5deSJohnathan Mantey 		return 0;
7390b7b0477SAndrew Jeffery 
7400b7b0477SAndrew Jeffery 	} /* poll indefinitely */
7411cecc5deSJohnathan Mantey 	return -1;
7421cecc5deSJohnathan Mantey }
7431cecc5deSJohnathan Mantey 
7441cecc5deSJohnathan Mantey static int call_pollers(struct console *console, struct timeval *cur_time)
745329a35f5SJeremy Kerr {
746329a35f5SJeremy Kerr 	struct poller *poller;
747329a35f5SJeremy Kerr 	struct pollfd *pollfd;
748329a35f5SJeremy Kerr 	enum poller_ret prc;
749b70f8713SAndrew Jeffery 	int i;
750b70f8713SAndrew Jeffery 	int rc;
751d831f960SJeremy Kerr 
7521a0e03b4SJeremy Kerr 	rc = 0;
7531a0e03b4SJeremy Kerr 
754329a35f5SJeremy Kerr 	/*
755329a35f5SJeremy Kerr 	 * Process poll events by iterating through the pollers and pollfds
756329a35f5SJeremy Kerr 	 * in-step, calling any pollers that we've found revents for.
757329a35f5SJeremy Kerr 	 */
758329a35f5SJeremy Kerr 	for (i = 0; i < console->n_pollers; i++) {
759329a35f5SJeremy Kerr 		poller = console->pollers[i];
760329a35f5SJeremy Kerr 		pollfd = &console->pollfds[i];
7611cecc5deSJohnathan Mantey 		prc = POLLER_OK;
7621a0e03b4SJeremy Kerr 
7631cecc5deSJohnathan Mantey 		/* process pending events... */
7641cecc5deSJohnathan Mantey 		if (pollfd->revents) {
7651cecc5deSJohnathan Mantey 			prc = poller->event_fn(poller->handler, pollfd->revents,
766329a35f5SJeremy Kerr 					       poller->data);
7672834c5b1SAndrew Jeffery 			if (prc == POLLER_EXIT) {
768329a35f5SJeremy Kerr 				rc = -1;
7692834c5b1SAndrew Jeffery 			} else if (prc == POLLER_REMOVE) {
770329a35f5SJeremy Kerr 				poller->remove = true;
771329a35f5SJeremy Kerr 			}
7722834c5b1SAndrew Jeffery 		}
773329a35f5SJeremy Kerr 
7741cecc5deSJohnathan Mantey 		if ((prc == POLLER_OK) && poller->timeout_fn &&
7751cecc5deSJohnathan Mantey 		    timerisset(&poller->timeout) &&
7761cecc5deSJohnathan Mantey 		    timercmp(&poller->timeout, cur_time, <=)) {
7771cecc5deSJohnathan Mantey 			/* One of the ringbuffer consumers is buffering the
7781cecc5deSJohnathan Mantey 			data stream. The amount of idle time the consumer
7791cecc5deSJohnathan Mantey 			desired has expired.  Process the buffered data for
7801cecc5deSJohnathan Mantey 			transmission. */
7811cecc5deSJohnathan Mantey 			timerclear(&poller->timeout);
7821cecc5deSJohnathan Mantey 			prc = poller->timeout_fn(poller->handler, poller->data);
7831cecc5deSJohnathan Mantey 			if (prc == POLLER_EXIT) {
7841cecc5deSJohnathan Mantey 				rc = -1;
7851cecc5deSJohnathan Mantey 			} else if (prc == POLLER_REMOVE) {
7861cecc5deSJohnathan Mantey 				poller->remove = true;
7871cecc5deSJohnathan Mantey 			}
7881cecc5deSJohnathan Mantey 		}
7891cecc5deSJohnathan Mantey 	}
7901cecc5deSJohnathan Mantey 
791329a35f5SJeremy Kerr 	/**
792329a35f5SJeremy Kerr 	 * Process deferred removals; restarting each time we unregister, as
793329a35f5SJeremy Kerr 	 * the array will have changed
794329a35f5SJeremy Kerr 	 */
795329a35f5SJeremy Kerr 	for (;;) {
796329a35f5SJeremy Kerr 		bool removed = false;
797329a35f5SJeremy Kerr 
798329a35f5SJeremy Kerr 		for (i = 0; i < console->n_pollers; i++) {
799329a35f5SJeremy Kerr 			poller = console->pollers[i];
800329a35f5SJeremy Kerr 			if (poller->remove) {
80155c9712dSJeremy Kerr 				console_poller_unregister(console, poller);
802329a35f5SJeremy Kerr 				removed = true;
803329a35f5SJeremy Kerr 				break;
804329a35f5SJeremy Kerr 			}
805329a35f5SJeremy Kerr 		}
8062834c5b1SAndrew Jeffery 		if (!removed) {
807329a35f5SJeremy Kerr 			break;
8081a0e03b4SJeremy Kerr 		}
8092834c5b1SAndrew Jeffery 	}
8101a0e03b4SJeremy Kerr 
8111a0e03b4SJeremy Kerr 	return rc;
8121a0e03b4SJeremy Kerr }
8131a0e03b4SJeremy Kerr 
814769cee1aSJeremy Kerr static void sighandler(int signal)
815769cee1aSJeremy Kerr {
8162834c5b1SAndrew Jeffery 	if (signal == SIGINT) {
817769cee1aSJeremy Kerr 		sigint = true;
818769cee1aSJeremy Kerr 	}
8192834c5b1SAndrew Jeffery }
820769cee1aSJeremy Kerr 
8211a0e03b4SJeremy Kerr int run_console(struct console *console)
8221a0e03b4SJeremy Kerr {
8235c359cc6SAndrew Jeffery 	sighandler_t sighandler_save = signal(SIGINT, sighandler);
8241cecc5deSJohnathan Mantey 	struct timeval tv;
8255c359cc6SAndrew Jeffery 	long timeout;
8265c359cc6SAndrew Jeffery 	ssize_t rc;
827769cee1aSJeremy Kerr 
828769cee1aSJeremy Kerr 	rc = 0;
829769cee1aSJeremy Kerr 
830d831f960SJeremy Kerr 	for (;;) {
831d831f960SJeremy Kerr 		uint8_t buf[4096];
832d831f960SJeremy Kerr 
8331764145dSJeremy Kerr 		BUILD_ASSERT(sizeof(buf) <= buffer_size);
8341764145dSJeremy Kerr 
835769cee1aSJeremy Kerr 		if (sigint) {
836769cee1aSJeremy Kerr 			fprintf(stderr, "Received interrupt, exiting\n");
837769cee1aSJeremy Kerr 			break;
838769cee1aSJeremy Kerr 		}
839769cee1aSJeremy Kerr 
8401cecc5deSJohnathan Mantey 		rc = get_current_time(&tv);
8411cecc5deSJohnathan Mantey 		if (rc) {
8421cecc5deSJohnathan Mantey 			warn("Failed to read current time");
8431cecc5deSJohnathan Mantey 			break;
8441cecc5deSJohnathan Mantey 		}
8451cecc5deSJohnathan Mantey 
8461cecc5deSJohnathan Mantey 		timeout = get_poll_timeout(console, &tv);
8471cecc5deSJohnathan Mantey 
848329a35f5SJeremy Kerr 		rc = poll(console->pollfds,
8495c359cc6SAndrew Jeffery 			  console->n_pollers + MAX_INTERNAL_POLLFD,
8505c359cc6SAndrew Jeffery 			  (int)timeout);
8511cecc5deSJohnathan Mantey 
852d831f960SJeremy Kerr 		if (rc < 0) {
853769cee1aSJeremy Kerr 			if (errno == EINTR) {
854769cee1aSJeremy Kerr 				continue;
8550b7b0477SAndrew Jeffery 			}
856d831f960SJeremy Kerr 			warn("poll error");
857769cee1aSJeremy Kerr 			break;
858769cee1aSJeremy Kerr 		}
859d831f960SJeremy Kerr 
860329a35f5SJeremy Kerr 		/* process internal fd first */
861329a35f5SJeremy Kerr 		if (console->pollfds[console->n_pollers].revents) {
86230ea6385SAndrew Jeffery 			rc = read(console->tty.fd, buf, sizeof(buf));
863d831f960SJeremy Kerr 			if (rc <= 0) {
864d831f960SJeremy Kerr 				warn("Error reading from tty device");
865769cee1aSJeremy Kerr 				rc = -1;
866769cee1aSJeremy Kerr 				break;
867d831f960SJeremy Kerr 			}
868f733c85aSJeremy Kerr 			rc = ringbuffer_queue(console->rb, buf, rc);
8692834c5b1SAndrew Jeffery 			if (rc) {
870769cee1aSJeremy Kerr 				break;
871d831f960SJeremy Kerr 			}
8722834c5b1SAndrew Jeffery 		}
873d831f960SJeremy Kerr 
874f9c8f6caSCheng C Yang 		if (console->pollfds[console->n_pollers + 1].revents) {
875f9c8f6caSCheng C Yang 			sd_bus_process(console->bus, NULL);
876f9c8f6caSCheng C Yang 		}
877f9c8f6caSCheng C Yang 
878329a35f5SJeremy Kerr 		/* ... and then the pollers */
8791cecc5deSJohnathan Mantey 		rc = call_pollers(console, &tv);
8802834c5b1SAndrew Jeffery 		if (rc) {
881769cee1aSJeremy Kerr 			break;
8821a0e03b4SJeremy Kerr 		}
8832834c5b1SAndrew Jeffery 	}
884769cee1aSJeremy Kerr 
885769cee1aSJeremy Kerr 	signal(SIGINT, sighandler_save);
886f9c8f6caSCheng C Yang 	sd_bus_unref(console->bus);
887769cee1aSJeremy Kerr 
888769cee1aSJeremy Kerr 	return rc ? -1 : 0;
8891a0e03b4SJeremy Kerr }
890d831f960SJeremy Kerr static const struct option options[] = {
891d66195c1SJeremy Kerr 	{ "config", required_argument, 0, 'c' },
892954be0fbSAndrew Jeffery 	{ "console-id", required_argument, 0, 'i' },
893f5858b5bSJoel Stanley 	{ 0, 0, 0, 0 },
894d831f960SJeremy Kerr };
895d831f960SJeremy Kerr 
896d831f960SJeremy Kerr int main(int argc, char **argv)
897d831f960SJeremy Kerr {
898d66195c1SJeremy Kerr 	const char *config_filename = NULL;
8996221ce94SVishwanatha Subbanna 	const char *config_tty_kname = NULL;
900954be0fbSAndrew Jeffery 	const char *console_id = NULL;
9011a0e03b4SJeremy Kerr 	struct console *console;
902d66195c1SJeremy Kerr 	struct config *config;
903d66195c1SJeremy Kerr 	int rc;
904d831f960SJeremy Kerr 
905d831f960SJeremy Kerr 	for (;;) {
906b70f8713SAndrew Jeffery 		int c;
907b70f8713SAndrew Jeffery 		int idx;
908d831f960SJeremy Kerr 
909954be0fbSAndrew Jeffery 		c = getopt_long(argc, argv, "c:i:", options, &idx);
9102834c5b1SAndrew Jeffery 		if (c == -1) {
911d831f960SJeremy Kerr 			break;
9122834c5b1SAndrew Jeffery 		}
913d831f960SJeremy Kerr 
914d831f960SJeremy Kerr 		switch (c) {
915d66195c1SJeremy Kerr 		case 'c':
916d66195c1SJeremy Kerr 			config_filename = optarg;
917d831f960SJeremy Kerr 			break;
918954be0fbSAndrew Jeffery 		case 'i':
919954be0fbSAndrew Jeffery 			console_id = optarg;
920954be0fbSAndrew Jeffery 			break;
921d831f960SJeremy Kerr 		case 'h':
922d831f960SJeremy Kerr 		case '?':
923d831f960SJeremy Kerr 			usage(argv[0]);
924d66195c1SJeremy Kerr 			return EXIT_SUCCESS;
925d831f960SJeremy Kerr 		}
926d831f960SJeremy Kerr 	}
927d831f960SJeremy Kerr 
9282834c5b1SAndrew Jeffery 	if (optind < argc) {
9296221ce94SVishwanatha Subbanna 		config_tty_kname = argv[optind];
9302834c5b1SAndrew Jeffery 	}
9316221ce94SVishwanatha Subbanna 
932d66195c1SJeremy Kerr 	console = malloc(sizeof(struct console));
933d66195c1SJeremy Kerr 	memset(console, 0, sizeof(*console));
934a72711afSAndrew Jeffery 	console->pollfds =
935a72711afSAndrew Jeffery 		calloc(MAX_INTERNAL_POLLFD, sizeof(*console->pollfds));
936f733c85aSJeremy Kerr 	console->rb = ringbuffer_init(buffer_size);
937329a35f5SJeremy Kerr 
938d66195c1SJeremy Kerr 	config = config_init(config_filename);
939d831f960SJeremy Kerr 
940954be0fbSAndrew Jeffery 	if (set_socket_info(console, config, console_id)) {
94129c59c44SAndrew Jeffery 		rc = -1;
94229c59c44SAndrew Jeffery 		goto out_config_fini;
943b14ca19cSNinad Palsule 	}
944b14ca19cSNinad Palsule 
945*7dc08baaSZev Weiss 	uart_routing_init(config);
946*7dc08baaSZev Weiss 
947d769eecfSAndrew Jeffery 	rc = tty_init(console, config, config_tty_kname);
9482834c5b1SAndrew Jeffery 	if (rc) {
949d66195c1SJeremy Kerr 		goto out_config_fini;
9502834c5b1SAndrew Jeffery 	}
951d831f960SJeremy Kerr 
952f9c8f6caSCheng C Yang 	dbus_init(console, config);
953f9c8f6caSCheng C Yang 
954d47963e5SJeremy Kerr 	handlers_init(console, config);
955d831f960SJeremy Kerr 
9561a0e03b4SJeremy Kerr 	rc = run_console(console);
957d831f960SJeremy Kerr 
9581a0e03b4SJeremy Kerr 	handlers_fini(console);
959d831f960SJeremy Kerr 
96030ea6385SAndrew Jeffery 	tty_fini(console);
96130ea6385SAndrew Jeffery 
962d66195c1SJeremy Kerr out_config_fini:
963d66195c1SJeremy Kerr 	config_fini(config);
964d66195c1SJeremy Kerr 
96589ea8198SJeremy Kerr 	free(console->pollers);
96689ea8198SJeremy Kerr 	free(console->pollfds);
9671a0e03b4SJeremy Kerr 	free(console);
968d831f960SJeremy Kerr 
969d831f960SJeremy Kerr 	return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
970d831f960SJeremy Kerr }
971