xref: /openbmc/obmc-console/console-server.c (revision 312ecdc2b4bab980e7baeba2d3a75cd5266d6061)
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"
421e04f449SAlexander 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 
642325d5d5SAlexander Hansen static bool console_server_pollfd_reclaimable(struct pollfd *p)
652325d5d5SAlexander Hansen {
662325d5d5SAlexander Hansen 	return p->fd == -1 && p->events == 0 && p->revents == ~0;
672325d5d5SAlexander Hansen }
682325d5d5SAlexander Hansen 
692325d5d5SAlexander Hansen static ssize_t
702325d5d5SAlexander Hansen console_server_find_released_pollfd(struct console_server *server)
712325d5d5SAlexander Hansen {
722325d5d5SAlexander Hansen 	for (size_t i = 0; i < server->capacity_pollfds; i++) {
732325d5d5SAlexander Hansen 		struct pollfd *p = &server->pollfds[i];
742325d5d5SAlexander Hansen 		if (console_server_pollfd_reclaimable(p)) {
752325d5d5SAlexander Hansen 			return (ssize_t)i;
762325d5d5SAlexander Hansen 		}
772325d5d5SAlexander Hansen 	}
782325d5d5SAlexander Hansen 	return -1;
792325d5d5SAlexander Hansen }
802325d5d5SAlexander Hansen 
812325d5d5SAlexander Hansen // returns the index of that pollfd in server->pollfds
822325d5d5SAlexander Hansen // we cannot return a pointer because 'realloc' may move server->pollfds
832325d5d5SAlexander Hansen ssize_t console_server_request_pollfd(struct console_server *server, int fd,
842325d5d5SAlexander Hansen 				      short int events)
852325d5d5SAlexander Hansen {
862325d5d5SAlexander Hansen 	ssize_t index;
872325d5d5SAlexander Hansen 	struct pollfd *pollfd;
882325d5d5SAlexander Hansen 
892325d5d5SAlexander Hansen 	index = console_server_find_released_pollfd(server);
902325d5d5SAlexander Hansen 
912325d5d5SAlexander Hansen 	if (index < 0) {
922325d5d5SAlexander Hansen 		const size_t newcap = server->capacity_pollfds + 1;
932325d5d5SAlexander Hansen 
942325d5d5SAlexander Hansen 		struct pollfd *newarr = reallocarray(server->pollfds, newcap,
952325d5d5SAlexander Hansen 						     sizeof(struct pollfd));
962325d5d5SAlexander Hansen 		if (newarr == NULL) {
972325d5d5SAlexander Hansen 			return -1;
982325d5d5SAlexander Hansen 		}
992325d5d5SAlexander Hansen 		server->pollfds = newarr;
1002325d5d5SAlexander Hansen 
1012325d5d5SAlexander Hansen 		index = (ssize_t)server->capacity_pollfds;
1022325d5d5SAlexander Hansen 
1032325d5d5SAlexander Hansen 		server->capacity_pollfds = newcap;
1042325d5d5SAlexander Hansen 	}
1052325d5d5SAlexander Hansen 
1062325d5d5SAlexander Hansen 	pollfd = &server->pollfds[index];
1072325d5d5SAlexander Hansen 	pollfd->fd = fd;
1082325d5d5SAlexander Hansen 	pollfd->events = events;
1092325d5d5SAlexander Hansen 	pollfd->revents = 0;
1102325d5d5SAlexander Hansen 
1112325d5d5SAlexander Hansen 	return index;
1122325d5d5SAlexander Hansen }
1132325d5d5SAlexander Hansen 
1142325d5d5SAlexander Hansen int console_server_release_pollfd(struct console_server *server,
1152325d5d5SAlexander Hansen 				  size_t pollfd_index)
1162325d5d5SAlexander Hansen {
1172325d5d5SAlexander Hansen 	if (pollfd_index >= server->capacity_pollfds) {
1182325d5d5SAlexander Hansen 		return -1;
1192325d5d5SAlexander Hansen 	}
1202325d5d5SAlexander Hansen 
1212325d5d5SAlexander Hansen 	struct pollfd *pfd = &server->pollfds[pollfd_index];
1222325d5d5SAlexander Hansen 
1232325d5d5SAlexander Hansen 	// mark pollfd as reclaimable
1242325d5d5SAlexander Hansen 
1252325d5d5SAlexander Hansen 	// ignore this file descriptor when calling 'poll'
1262325d5d5SAlexander Hansen 	// https://www.man7.org/linux/man-pages/man2/poll.2.html
1272325d5d5SAlexander Hansen 	pfd->fd = -1;
1282325d5d5SAlexander Hansen 	pfd->events = 0;
1292325d5d5SAlexander Hansen 	pfd->revents = ~0;
1302325d5d5SAlexander Hansen 
1312325d5d5SAlexander Hansen 	return 0;
1322325d5d5SAlexander Hansen }
1332325d5d5SAlexander Hansen 
134c2b0d8c7SAlexander Hansen /* populates server->tty.dev and server->tty.sysfs_devnode, using the tty kernel name */
135c2b0d8c7SAlexander Hansen static int tty_find_device(struct console_server *server)
13617217845SJeremy Kerr {
137d3cb9c22SAndrew Jeffery 	char *tty_class_device_link = NULL;
138d3cb9c22SAndrew Jeffery 	char *tty_path_input_real = NULL;
139d3cb9c22SAndrew Jeffery 	char *tty_device_tty_dir = NULL;
14030ea6385SAndrew Jeffery 	char *tty_vuart_lpc_addr = NULL;
141d3cb9c22SAndrew Jeffery 	char *tty_device_reldir = NULL;
14230ea6385SAndrew Jeffery 	char *tty_sysfs_devnode = NULL;
143d3cb9c22SAndrew Jeffery 	char *tty_kname_real = NULL;
14430ea6385SAndrew Jeffery 	char *tty_path_input = NULL;
14517217845SJeremy Kerr 	int rc;
14617217845SJeremy Kerr 
147c2b0d8c7SAlexander Hansen 	server->tty.type = TTY_DEVICE_UNDEFINED;
14830ea6385SAndrew Jeffery 
149c2b0d8c7SAlexander Hansen 	assert(server->tty.kname);
150c2b0d8c7SAlexander Hansen 	if (!strlen(server->tty.kname)) {
15130ea6385SAndrew Jeffery 		warnx("TTY kname must not be empty");
15230ea6385SAndrew Jeffery 		rc = -1;
15330ea6385SAndrew Jeffery 		goto out_free;
1542834c5b1SAndrew Jeffery 	}
15517217845SJeremy Kerr 
156c2b0d8c7SAlexander Hansen 	if (server->tty.kname[0] == '/') {
157c2b0d8c7SAlexander Hansen 		tty_path_input = strdup(server->tty.kname);
15830ea6385SAndrew Jeffery 		if (!tty_path_input) {
15930ea6385SAndrew Jeffery 			rc = -1;
16030ea6385SAndrew Jeffery 			goto out_free;
16130ea6385SAndrew Jeffery 		}
16230ea6385SAndrew Jeffery 	} else {
163c2b0d8c7SAlexander Hansen 		rc = asprintf(&tty_path_input, "/dev/%s", server->tty.kname);
16430ea6385SAndrew Jeffery 		if (rc < 0) {
16530ea6385SAndrew Jeffery 			goto out_free;
16630ea6385SAndrew Jeffery 		}
16730ea6385SAndrew Jeffery 	}
16830ea6385SAndrew Jeffery 
16930ea6385SAndrew Jeffery 	/* udev may rename the tty name with a symbol link, try to resolve */
17045ad7676SYi Li 	tty_path_input_real = realpath(tty_path_input, NULL);
17145ad7676SYi Li 	if (!tty_path_input_real) {
17230ea6385SAndrew Jeffery 		warn("Can't find realpath for %s", tty_path_input);
17315792aa7SAndrew Jeffery 		rc = -1;
17445ad7676SYi Li 		goto out_free;
17545ad7676SYi Li 	}
17645ad7676SYi Li 
17730ea6385SAndrew Jeffery 	/*
17830ea6385SAndrew Jeffery 	 * Allow hooking obmc-console-server up to PTYs for testing
17930ea6385SAndrew Jeffery 	 *
18030ea6385SAndrew Jeffery 	 * https://amboar.github.io/notes/2023/05/02/testing-obmc-console-with-socat.html
18130ea6385SAndrew Jeffery 	 */
18230ea6385SAndrew Jeffery 	if (!strncmp(DEV_PTS_PATH, tty_path_input_real, strlen(DEV_PTS_PATH))) {
183c2b0d8c7SAlexander Hansen 		server->tty.type = TTY_DEVICE_PTY;
184c2b0d8c7SAlexander Hansen 		server->tty.dev = strdup(server->tty.kname);
185c2b0d8c7SAlexander Hansen 		rc = server->tty.dev ? 0 : -1;
18630ea6385SAndrew Jeffery 		goto out_free;
18730ea6385SAndrew Jeffery 	}
18830ea6385SAndrew Jeffery 
18945ad7676SYi Li 	tty_kname_real = basename(tty_path_input_real);
19045ad7676SYi Li 	if (!tty_kname_real) {
191c2b0d8c7SAlexander Hansen 		warn("Can't find real name for %s", server->tty.kname);
19215792aa7SAndrew Jeffery 		rc = -1;
19345ad7676SYi Li 		goto out_free;
19445ad7676SYi Li 	}
19545ad7676SYi Li 
196a72711afSAndrew Jeffery 	rc = asprintf(&tty_class_device_link, "/sys/class/tty/%s",
197a72711afSAndrew Jeffery 		      tty_kname_real);
1982834c5b1SAndrew Jeffery 	if (rc < 0) {
19945ad7676SYi Li 		goto out_free;
2002834c5b1SAndrew Jeffery 	}
20145ad7676SYi Li 
20217217845SJeremy Kerr 	tty_device_tty_dir = realpath(tty_class_device_link, NULL);
20345ad7676SYi Li 	if (!tty_device_tty_dir) {
20445ad7676SYi Li 		warn("Can't query sysfs for device %s", tty_kname_real);
20515792aa7SAndrew Jeffery 		rc = -1;
20617217845SJeremy Kerr 		goto out_free;
20717217845SJeremy Kerr 	}
20817217845SJeremy Kerr 
20917217845SJeremy Kerr 	rc = asprintf(&tty_device_reldir, "%s/../../", tty_device_tty_dir);
2102834c5b1SAndrew Jeffery 	if (rc < 0) {
21117217845SJeremy Kerr 		goto out_free;
2122834c5b1SAndrew Jeffery 	}
21317217845SJeremy Kerr 
21430ea6385SAndrew Jeffery 	tty_sysfs_devnode = realpath(tty_device_reldir, NULL);
21530ea6385SAndrew Jeffery 	if (!tty_sysfs_devnode) {
21645ad7676SYi Li 		warn("Can't find parent device for %s", tty_kname_real);
2172834c5b1SAndrew Jeffery 	}
21817217845SJeremy Kerr 
219c2b0d8c7SAlexander Hansen 	rc = asprintf(&server->tty.dev, "/dev/%s", tty_kname_real);
2202834c5b1SAndrew Jeffery 	if (rc < 0) {
22117217845SJeremy Kerr 		goto out_free;
2222834c5b1SAndrew Jeffery 	}
22317217845SJeremy Kerr 
224955d140eSOskar Senft 	// Default to non-VUART
225c2b0d8c7SAlexander Hansen 	server->tty.type = TTY_DEVICE_UART;
226955d140eSOskar Senft 
22730ea6385SAndrew Jeffery 	/* Arbitrarily pick an attribute to differentiate UART vs VUART */
228955d140eSOskar Senft 	if (tty_sysfs_devnode) {
229955d140eSOskar Senft 		rc = asprintf(&tty_vuart_lpc_addr, "%s/lpc_address",
230955d140eSOskar Senft 			      tty_sysfs_devnode);
23130ea6385SAndrew Jeffery 		if (rc < 0) {
23230ea6385SAndrew Jeffery 			goto out_free;
23330ea6385SAndrew Jeffery 		}
23430ea6385SAndrew Jeffery 
23530ea6385SAndrew Jeffery 		rc = access(tty_vuart_lpc_addr, F_OK);
236955d140eSOskar Senft 		if (!rc) {
237c2b0d8c7SAlexander Hansen 			server->tty.type = TTY_DEVICE_VUART;
238c2b0d8c7SAlexander Hansen 			server->tty.vuart.sysfs_devnode =
239955d140eSOskar Senft 				strdup(tty_sysfs_devnode);
240955d140eSOskar Senft 		}
241955d140eSOskar Senft 	}
24230ea6385SAndrew Jeffery 
24317217845SJeremy Kerr 	rc = 0;
24417217845SJeremy Kerr 
24517217845SJeremy Kerr out_free:
24630ea6385SAndrew Jeffery 	free(tty_vuart_lpc_addr);
24717217845SJeremy Kerr 	free(tty_class_device_link);
248982090d9SAndrew Jeffery 	free(tty_sysfs_devnode);
24917217845SJeremy Kerr 	free(tty_device_tty_dir);
25017217845SJeremy Kerr 	free(tty_device_reldir);
25145ad7676SYi Li 	free(tty_path_input);
25245ad7676SYi Li 	free(tty_path_input_real);
25317217845SJeremy Kerr 	return rc;
25417217845SJeremy Kerr }
25517217845SJeremy Kerr 
256c2b0d8c7SAlexander Hansen static int tty_set_sysfs_attr(struct console_server *server, const char *name,
257957818b4SJeremy Kerr 			      int value)
258957818b4SJeremy Kerr {
259957818b4SJeremy Kerr 	char *path;
260957818b4SJeremy Kerr 	FILE *fp;
261957818b4SJeremy Kerr 	int rc;
262957818b4SJeremy Kerr 
263c2b0d8c7SAlexander Hansen 	assert(server->tty.type == TTY_DEVICE_VUART);
26430ea6385SAndrew Jeffery 
265c2b0d8c7SAlexander Hansen 	if (!server->tty.vuart.sysfs_devnode) {
26630ea6385SAndrew Jeffery 		return -1;
26730ea6385SAndrew Jeffery 	}
26830ea6385SAndrew Jeffery 
269c2b0d8c7SAlexander Hansen 	rc = asprintf(&path, "%s/%s", server->tty.vuart.sysfs_devnode, name);
2702834c5b1SAndrew Jeffery 	if (rc < 0) {
271957818b4SJeremy Kerr 		return -1;
2722834c5b1SAndrew Jeffery 	}
273957818b4SJeremy Kerr 
274957818b4SJeremy Kerr 	fp = fopen(path, "w");
275957818b4SJeremy Kerr 	if (!fp) {
276a72711afSAndrew Jeffery 		warn("Can't access attribute %s on device %s", name,
277c2b0d8c7SAlexander Hansen 		     server->tty.kname);
278957818b4SJeremy Kerr 		rc = -1;
279957818b4SJeremy Kerr 		goto out_free;
280957818b4SJeremy Kerr 	}
281957818b4SJeremy Kerr 	setvbuf(fp, NULL, _IONBF, 0);
282957818b4SJeremy Kerr 
283957818b4SJeremy Kerr 	rc = fprintf(fp, "0x%x", value);
2842834c5b1SAndrew Jeffery 	if (rc < 0) {
285a72711afSAndrew Jeffery 		warn("Error writing to %s attribute of device %s", name,
286c2b0d8c7SAlexander Hansen 		     server->tty.kname);
2872834c5b1SAndrew Jeffery 	}
288957818b4SJeremy Kerr 	fclose(fp);
289957818b4SJeremy Kerr 
290957818b4SJeremy Kerr out_free:
291957818b4SJeremy Kerr 	free(path);
292957818b4SJeremy Kerr 	return rc;
293957818b4SJeremy Kerr }
294957818b4SJeremy Kerr 
295d831f960SJeremy Kerr /**
296c7fbcd48SBenjamin Fair  * Set termios attributes on the console tty.
29754e9569dSJeremy Kerr  */
298c2b0d8c7SAlexander Hansen void tty_init_termios(struct console_server *server)
29954e9569dSJeremy Kerr {
30054e9569dSJeremy Kerr 	struct termios termios;
30154e9569dSJeremy Kerr 	int rc;
30254e9569dSJeremy Kerr 
303c2b0d8c7SAlexander Hansen 	rc = tcgetattr(server->tty.fd, &termios);
30454e9569dSJeremy Kerr 	if (rc) {
30554e9569dSJeremy Kerr 		warn("Can't read tty termios");
30654e9569dSJeremy Kerr 		return;
30754e9569dSJeremy Kerr 	}
30854e9569dSJeremy Kerr 
309c2b0d8c7SAlexander Hansen 	if (server->tty.type == TTY_DEVICE_UART && server->tty.uart.baud) {
310c2b0d8c7SAlexander Hansen 		if (cfsetspeed(&termios, server->tty.uart.baud) < 0) {
311c2b0d8c7SAlexander Hansen 			warn("Couldn't set speeds for %s", server->tty.kname);
312c7fbcd48SBenjamin Fair 		}
3132834c5b1SAndrew Jeffery 	}
314c7fbcd48SBenjamin Fair 
315c7fbcd48SBenjamin Fair 	/* Set console to raw mode: we don't want any processing to occur on
316c7fbcd48SBenjamin Fair 	 * the underlying terminal input/output.
317c7fbcd48SBenjamin Fair 	 */
31854e9569dSJeremy Kerr 	cfmakeraw(&termios);
319c7fbcd48SBenjamin Fair 
320c2b0d8c7SAlexander Hansen 	rc = tcsetattr(server->tty.fd, TCSANOW, &termios);
3212834c5b1SAndrew Jeffery 	if (rc) {
322c2b0d8c7SAlexander Hansen 		warn("Can't set terminal options for %s", server->tty.kname);
32354e9569dSJeremy Kerr 	}
3242834c5b1SAndrew Jeffery }
32554e9569dSJeremy Kerr 
32654e9569dSJeremy Kerr /**
327d831f960SJeremy Kerr  * Open and initialise the serial device
328d831f960SJeremy Kerr  */
329c2b0d8c7SAlexander Hansen static void tty_init_vuart_io(struct console_server *server)
330d831f960SJeremy Kerr {
331c2b0d8c7SAlexander Hansen 	assert(server->tty.type == TTY_DEVICE_VUART);
33230ea6385SAndrew Jeffery 
333c2b0d8c7SAlexander Hansen 	if (server->tty.vuart.sirq) {
334c2b0d8c7SAlexander Hansen 		tty_set_sysfs_attr(server, "sirq", server->tty.vuart.sirq);
3352834c5b1SAndrew Jeffery 	}
336957818b4SJeremy Kerr 
337c2b0d8c7SAlexander Hansen 	if (server->tty.vuart.lpc_addr) {
338c2b0d8c7SAlexander Hansen 		tty_set_sysfs_attr(server, "lpc_address",
339c2b0d8c7SAlexander Hansen 				   server->tty.vuart.lpc_addr);
34030ea6385SAndrew Jeffery 	}
34130ea6385SAndrew Jeffery }
34230ea6385SAndrew Jeffery 
343c2b0d8c7SAlexander Hansen static int tty_init_io(struct console_server *server)
34430ea6385SAndrew Jeffery {
345c2b0d8c7SAlexander Hansen 	server->tty.fd = open(server->tty.dev, O_RDWR);
346c2b0d8c7SAlexander Hansen 	if (server->tty.fd <= 0) {
347c2b0d8c7SAlexander Hansen 		warn("Can't open tty %s", server->tty.dev);
348d831f960SJeremy Kerr 		return -1;
349d831f960SJeremy Kerr 	}
350d831f960SJeremy Kerr 
351d831f960SJeremy Kerr 	/* Disable character delay. We may want to later enable this when
352d831f960SJeremy Kerr 	 * we detect larger amounts of data
353d831f960SJeremy Kerr 	 */
354c2b0d8c7SAlexander Hansen 	fcntl(server->tty.fd, F_SETFL, FNDELAY);
355d831f960SJeremy Kerr 
356c2b0d8c7SAlexander Hansen 	tty_init_termios(server);
35754e9569dSJeremy Kerr 
3582325d5d5SAlexander Hansen 	ssize_t index =
3592325d5d5SAlexander Hansen 		console_server_request_pollfd(server, server->tty.fd, POLLIN);
3602325d5d5SAlexander Hansen 
3612325d5d5SAlexander Hansen 	if (index < 0) {
3622325d5d5SAlexander Hansen 		return -1;
3632325d5d5SAlexander Hansen 	}
3642325d5d5SAlexander Hansen 
3652325d5d5SAlexander Hansen 	server->tty_pollfd_index = (size_t)index;
366329a35f5SJeremy Kerr 
367d831f960SJeremy Kerr 	return 0;
368d831f960SJeremy Kerr }
369d831f960SJeremy Kerr 
370c2b0d8c7SAlexander Hansen static int tty_init_vuart(struct console_server *server, struct config *config)
371d66195c1SJeremy Kerr {
372fd883a88SAndrew Jeffery 	unsigned long parsed;
373d66195c1SJeremy Kerr 	const char *val;
374d66195c1SJeremy Kerr 	char *endp;
37530ea6385SAndrew Jeffery 
376c2b0d8c7SAlexander Hansen 	assert(server->tty.type == TTY_DEVICE_VUART);
377d66195c1SJeremy Kerr 
378d66195c1SJeremy Kerr 	val = config_get_value(config, "lpc-address");
379d66195c1SJeremy Kerr 	if (val) {
380fd883a88SAndrew Jeffery 		errno = 0;
381fd883a88SAndrew Jeffery 		parsed = strtoul(val, &endp, 0);
382fd883a88SAndrew Jeffery 		if (parsed == ULONG_MAX && errno == ERANGE) {
383fd883a88SAndrew Jeffery 			warn("Cannot interpret 'lpc-address' value as an unsigned long: '%s'",
384fd883a88SAndrew Jeffery 			     val);
385fd883a88SAndrew Jeffery 			return -1;
386fd883a88SAndrew Jeffery 		}
387fd883a88SAndrew Jeffery 
388fd883a88SAndrew Jeffery 		if (parsed > UINT16_MAX) {
389fd883a88SAndrew Jeffery 			warn("Invalid LPC address '%s'", val);
390fd883a88SAndrew Jeffery 			return -1;
391fd883a88SAndrew Jeffery 		}
392fd883a88SAndrew Jeffery 
393c2b0d8c7SAlexander Hansen 		server->tty.vuart.lpc_addr = (uint16_t)parsed;
394d66195c1SJeremy Kerr 		if (endp == optarg) {
395d66195c1SJeremy Kerr 			warn("Invalid LPC address: '%s'", val);
396d66195c1SJeremy Kerr 			return -1;
397d66195c1SJeremy Kerr 		}
398d66195c1SJeremy Kerr 	}
399d66195c1SJeremy Kerr 
400d66195c1SJeremy Kerr 	val = config_get_value(config, "sirq");
401d66195c1SJeremy Kerr 	if (val) {
402fd883a88SAndrew Jeffery 		errno = 0;
403fd883a88SAndrew Jeffery 		parsed = strtoul(val, &endp, 0);
404fd883a88SAndrew Jeffery 		if (parsed == ULONG_MAX && errno == ERANGE) {
405fd883a88SAndrew Jeffery 			warn("Cannot interpret 'sirq' value as an unsigned long: '%s'",
406fd883a88SAndrew Jeffery 			     val);
407fd883a88SAndrew Jeffery 		}
408fd883a88SAndrew Jeffery 
4092834c5b1SAndrew Jeffery 		if (parsed > 16) {
410fd883a88SAndrew Jeffery 			warn("Invalid LPC SERIRQ: '%s'", val);
4112834c5b1SAndrew Jeffery 		}
412fd883a88SAndrew Jeffery 
413c2b0d8c7SAlexander Hansen 		server->tty.vuart.sirq = (int)parsed;
4142834c5b1SAndrew Jeffery 		if (endp == optarg) {
415d66195c1SJeremy Kerr 			warn("Invalid sirq: '%s'", val);
416d66195c1SJeremy Kerr 		}
4172834c5b1SAndrew Jeffery 	}
418d66195c1SJeremy Kerr 
41930ea6385SAndrew Jeffery 	return 0;
4202834c5b1SAndrew Jeffery }
421c7fbcd48SBenjamin Fair 
422c2b0d8c7SAlexander Hansen static int tty_init(struct console_server *server, struct config *config,
42330ea6385SAndrew Jeffery 		    const char *tty_arg)
42430ea6385SAndrew Jeffery {
42530ea6385SAndrew Jeffery 	const char *val;
42630ea6385SAndrew Jeffery 	int rc;
42730ea6385SAndrew Jeffery 
428d769eecfSAndrew Jeffery 	if (tty_arg) {
429c2b0d8c7SAlexander Hansen 		server->tty.kname = tty_arg;
430d769eecfSAndrew Jeffery 	} else if ((val = config_get_value(config, "upstream-tty"))) {
431c2b0d8c7SAlexander Hansen 		server->tty.kname = val;
432d769eecfSAndrew Jeffery 	} else {
433d66195c1SJeremy Kerr 		warnx("Error: No TTY device specified");
434d66195c1SJeremy Kerr 		return -1;
435d66195c1SJeremy Kerr 	}
436d66195c1SJeremy Kerr 
437c2b0d8c7SAlexander Hansen 	rc = tty_find_device(server);
4382834c5b1SAndrew Jeffery 	if (rc) {
439d66195c1SJeremy Kerr 		return rc;
4402834c5b1SAndrew Jeffery 	}
441d66195c1SJeremy Kerr 
442c2b0d8c7SAlexander Hansen 	switch (server->tty.type) {
44330ea6385SAndrew Jeffery 	case TTY_DEVICE_VUART:
444c2b0d8c7SAlexander Hansen 		rc = tty_init_vuart(server, config);
44530ea6385SAndrew Jeffery 		if (rc) {
446d66195c1SJeremy Kerr 			return rc;
447d66195c1SJeremy Kerr 		}
448d66195c1SJeremy Kerr 
449c2b0d8c7SAlexander Hansen 		tty_init_vuart_io(server);
45030ea6385SAndrew Jeffery 		break;
45130ea6385SAndrew Jeffery 	case TTY_DEVICE_UART:
45230ea6385SAndrew Jeffery 		val = config_get_value(config, "baud");
45330ea6385SAndrew Jeffery 		if (val) {
454c2b0d8c7SAlexander Hansen 			if (config_parse_baud(&server->tty.uart.baud, val)) {
45530ea6385SAndrew Jeffery 				warnx("Invalid baud rate: '%s'", val);
45630ea6385SAndrew Jeffery 			}
45730ea6385SAndrew Jeffery 		}
45830ea6385SAndrew Jeffery 		break;
45930ea6385SAndrew Jeffery 	case TTY_DEVICE_PTY:
46030ea6385SAndrew Jeffery 		break;
46130ea6385SAndrew Jeffery 	case TTY_DEVICE_UNDEFINED:
46230ea6385SAndrew Jeffery 	default:
46330ea6385SAndrew Jeffery 		warnx("Cannot configure unrecognised TTY device");
46430ea6385SAndrew Jeffery 		return -1;
46530ea6385SAndrew Jeffery 	}
46630ea6385SAndrew Jeffery 
467c2b0d8c7SAlexander Hansen 	return tty_init_io(server);
46830ea6385SAndrew Jeffery }
46930ea6385SAndrew Jeffery 
470c2b0d8c7SAlexander Hansen static void tty_fini(struct console_server *server)
47130ea6385SAndrew Jeffery {
4722325d5d5SAlexander Hansen 	if (server->tty_pollfd_index < server->capacity_pollfds) {
4732325d5d5SAlexander Hansen 		console_server_release_pollfd(server, server->tty_pollfd_index);
4742325d5d5SAlexander Hansen 		server->tty_pollfd_index = SIZE_MAX;
4752325d5d5SAlexander Hansen 	}
4762325d5d5SAlexander Hansen 
477c2b0d8c7SAlexander Hansen 	if (server->tty.type == TTY_DEVICE_VUART) {
478c2b0d8c7SAlexander Hansen 		free(server->tty.vuart.sysfs_devnode);
47930ea6385SAndrew Jeffery 	}
4802325d5d5SAlexander Hansen 
481c2b0d8c7SAlexander Hansen 	free(server->tty.dev);
48230ea6385SAndrew Jeffery }
48330ea6385SAndrew Jeffery 
4847dc08baaSZev Weiss static int write_to_path(const char *path, const char *data)
4857dc08baaSZev Weiss {
4867dc08baaSZev Weiss 	int rc = 0;
4877dc08baaSZev Weiss 	FILE *f = fopen(path, "w");
4887dc08baaSZev Weiss 	if (!f) {
4897dc08baaSZev Weiss 		return -1;
4907dc08baaSZev Weiss 	}
4917dc08baaSZev Weiss 
4927dc08baaSZev Weiss 	if (fprintf(f, "%s", data) < 0) {
4937dc08baaSZev Weiss 		rc = -1;
4947dc08baaSZev Weiss 	}
4957dc08baaSZev Weiss 
4967dc08baaSZev Weiss 	if (fclose(f)) {
4977dc08baaSZev Weiss 		rc = -1;
4987dc08baaSZev Weiss 	}
4997dc08baaSZev Weiss 
5007dc08baaSZev Weiss 	return rc;
5017dc08baaSZev Weiss }
5027dc08baaSZev Weiss 
5037dc08baaSZev Weiss #define ASPEED_UART_ROUTING_PATTERN                                            \
5047dc08baaSZev Weiss 	"/sys/bus/platform/drivers/aspeed-uart-routing/*.uart-routing"
5057dc08baaSZev Weiss 
5067dc08baaSZev Weiss static void uart_routing_init(struct config *config)
5077dc08baaSZev Weiss {
5087dc08baaSZev Weiss 	const char *muxcfg;
5097dc08baaSZev Weiss 	const char *p;
5107dc08baaSZev Weiss 	size_t buflen;
5117dc08baaSZev Weiss 	char *sink;
5127dc08baaSZev Weiss 	char *source;
5137dc08baaSZev Weiss 	char *muxdir;
5147dc08baaSZev Weiss 	char *path;
5157dc08baaSZev Weiss 	glob_t globbuf;
5167dc08baaSZev Weiss 
5177dc08baaSZev Weiss 	muxcfg = config_get_value(config, "aspeed-uart-routing");
5187dc08baaSZev Weiss 	if (!muxcfg) {
5197dc08baaSZev Weiss 		return;
5207dc08baaSZev Weiss 	}
5217dc08baaSZev Weiss 
5227dc08baaSZev Weiss 	/* Find the driver's sysfs directory */
5237dc08baaSZev Weiss 	if (glob(ASPEED_UART_ROUTING_PATTERN, GLOB_ERR | GLOB_NOSORT, NULL,
5247dc08baaSZev Weiss 		 &globbuf) != 0) {
5257dc08baaSZev Weiss 		warn("Couldn't find uart-routing driver directory, cannot apply config");
5267dc08baaSZev Weiss 		return;
5277dc08baaSZev Weiss 	}
5287dc08baaSZev Weiss 	if (globbuf.gl_pathc != 1) {
5297dc08baaSZev Weiss 		warnx("Found %zd uart-routing driver directories, cannot apply config",
5307dc08baaSZev Weiss 		      globbuf.gl_pathc);
5317dc08baaSZev Weiss 		goto out_free_glob;
5327dc08baaSZev Weiss 	}
5337dc08baaSZev Weiss 	muxdir = globbuf.gl_pathv[0];
5347dc08baaSZev Weiss 
5357dc08baaSZev Weiss 	/*
5367dc08baaSZev Weiss 	 * Rather than faff about tracking a bunch of separate buffer sizes,
5377dc08baaSZev Weiss 	 * just use one (worst-case) size for all of them -- +2 for a trailing
5387dc08baaSZev Weiss 	 * NUL and a '/' separator to construct the sysfs file path.
5397dc08baaSZev Weiss 	 */
5407dc08baaSZev Weiss 	buflen = strlen(muxdir) + strlen(muxcfg) + 2;
5417dc08baaSZev Weiss 
5427dc08baaSZev Weiss 	sink = malloc(buflen);
5437dc08baaSZev Weiss 	source = malloc(buflen);
5447dc08baaSZev Weiss 	path = malloc(buflen);
5457dc08baaSZev Weiss 	if (!path || !sink || !source) {
5467dc08baaSZev Weiss 		warnx("Out of memory applying uart routing config");
5477dc08baaSZev Weiss 		goto out_free_bufs;
5487dc08baaSZev Weiss 	}
5497dc08baaSZev Weiss 
5507dc08baaSZev Weiss 	p = muxcfg;
5517dc08baaSZev Weiss 	while (*p) {
5527dc08baaSZev Weiss 		ssize_t bytes_scanned;
5537dc08baaSZev Weiss 
5547dc08baaSZev Weiss 		if (sscanf(p, " %[^:/ \t]:%[^: \t] %zn", sink, source,
5557dc08baaSZev Weiss 			   &bytes_scanned) != 2) {
5567dc08baaSZev Weiss 			warnx("Invalid syntax in aspeed uart config: '%s' not applied",
5577dc08baaSZev Weiss 			      p);
5587dc08baaSZev Weiss 			break;
5597dc08baaSZev Weiss 		}
5607dc08baaSZev Weiss 		p += bytes_scanned;
5617dc08baaSZev Weiss 
5627dc08baaSZev Weiss 		/*
5637dc08baaSZev Weiss 		 * Check that the sink name looks reasonable before proceeding
5647dc08baaSZev Weiss 		 * (there are other writable files in the same directory that
5657dc08baaSZev Weiss 		 * we shouldn't be touching, such as 'driver_override' and
5667dc08baaSZev Weiss 		 * 'uevent').
5677dc08baaSZev Weiss 		 */
5687dc08baaSZev Weiss 		if (strncmp(sink, "io", strlen("io")) != 0 &&
5697dc08baaSZev Weiss 		    strncmp(sink, "uart", strlen("uart")) != 0) {
5707dc08baaSZev Weiss 			warnx("Skipping invalid uart routing name '%s' (must be ioN or uartN)",
5717dc08baaSZev Weiss 			      sink);
5727dc08baaSZev Weiss 			continue;
5737dc08baaSZev Weiss 		}
5747dc08baaSZev Weiss 
5757dc08baaSZev Weiss 		snprintf(path, buflen, "%s/%s", muxdir, sink);
5767dc08baaSZev Weiss 		if (write_to_path(path, source)) {
5777dc08baaSZev Weiss 			warn("Failed to apply uart-routing config '%s:%s'",
5787dc08baaSZev Weiss 			     sink, source);
5797dc08baaSZev Weiss 		}
5807dc08baaSZev Weiss 	}
5817dc08baaSZev Weiss 
5827dc08baaSZev Weiss out_free_bufs:
5837dc08baaSZev Weiss 	free(path);
5847dc08baaSZev Weiss 	free(source);
5857dc08baaSZev Weiss 	free(sink);
5867dc08baaSZev Weiss out_free_glob:
5877dc08baaSZev Weiss 	globfree(&globbuf);
5887dc08baaSZev Weiss }
5897dc08baaSZev Weiss 
5901a0e03b4SJeremy Kerr int console_data_out(struct console *console, const uint8_t *data, size_t len)
591d831f960SJeremy Kerr {
592c2b0d8c7SAlexander Hansen 	return write_buf_to_fd(console->server->tty.fd, data, len);
593d831f960SJeremy Kerr }
594d831f960SJeremy Kerr 
5955ba20b5bSNinad Palsule /* Prepare a socket name */
596954be0fbSAndrew Jeffery static int set_socket_info(struct console *console, struct config *config,
597954be0fbSAndrew Jeffery 			   const char *console_id)
598b14ca19cSNinad Palsule {
599b14ca19cSNinad Palsule 	ssize_t len;
600b14ca19cSNinad Palsule 
6015ba20b5bSNinad Palsule 	/* Get console id */
6025ba20b5bSNinad Palsule 	console->console_id = config_resolve_console_id(config, console_id);
603954be0fbSAndrew Jeffery 
604b14ca19cSNinad Palsule 	/* Get the socket name/path */
6055ba20b5bSNinad Palsule 	len = console_socket_path(console->socket_name, console->console_id);
606b14ca19cSNinad Palsule 	if (len < 0) {
607b14ca19cSNinad Palsule 		warn("Failed to set socket path: %s", strerror(errno));
608b14ca19cSNinad Palsule 		return EXIT_FAILURE;
609b14ca19cSNinad Palsule 	}
610b14ca19cSNinad Palsule 
611b14ca19cSNinad Palsule 	/* Socket name is not a null terminated string hence save the length */
612b14ca19cSNinad Palsule 	console->socket_name_len = len;
613b14ca19cSNinad Palsule 
614b14ca19cSNinad Palsule 	return 0;
615b14ca19cSNinad Palsule }
616b14ca19cSNinad Palsule 
617d47963e5SJeremy Kerr static void handlers_init(struct console *console, struct config *config)
618d831f960SJeremy Kerr {
619b70f8713SAndrew Jeffery 	/* NOLINTBEGIN(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) */
620adedc333SAndrew Jeffery 	extern const struct handler_type *const __start_handlers[];
621adedc333SAndrew Jeffery 	extern const struct handler_type *const __stop_handlers[];
622b70f8713SAndrew Jeffery 	/* NOLINTEND(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) */
623e2826c7dSJeremy Kerr 	size_t n_types;
624e2826c7dSJeremy Kerr 	int j = 0;
625e2826c7dSJeremy Kerr 	size_t i;
626e2826c7dSJeremy Kerr 
627adedc333SAndrew Jeffery 	n_types = __stop_handlers - __start_handlers;
628e2826c7dSJeremy Kerr 	console->handlers = calloc(n_types, sizeof(struct handler *));
629e2826c7dSJeremy Kerr 	if (!console->handlers) {
630e2826c7dSJeremy Kerr 		err(EXIT_FAILURE, "malloc(handlers)");
631e2826c7dSJeremy Kerr 	}
632e2826c7dSJeremy Kerr 
633079fc516SAndrew Jeffery 	printf("%zu handler type%s\n", n_types, n_types == 1 ? "" : "s");
634e2826c7dSJeremy Kerr 
635e2826c7dSJeremy Kerr 	for (i = 0; i < n_types; i++) {
636adedc333SAndrew Jeffery 		const struct handler_type *type = __start_handlers[i];
6371a0e03b4SJeremy Kerr 		struct handler *handler;
638d831f960SJeremy Kerr 
639e2826c7dSJeremy Kerr 		/* Should be picked up at build time by
640e2826c7dSJeremy Kerr 		 * console_handler_register, but check anyway
641e2826c7dSJeremy Kerr 		 */
642e2826c7dSJeremy Kerr 		if (!type->init || !type->fini) {
643e2826c7dSJeremy Kerr 			errx(EXIT_FAILURE,
644e2826c7dSJeremy Kerr 			     "invalid handler type %s: no init() / fini()",
645e2826c7dSJeremy Kerr 			     type->name);
6462834c5b1SAndrew Jeffery 		}
647021b91f0SJeremy Kerr 
648e2826c7dSJeremy Kerr 		handler = type->init(type, console, config);
649021b91f0SJeremy Kerr 
650e2826c7dSJeremy Kerr 		printf("  console '%s': handler %s [%sactive]\n",
651e2826c7dSJeremy Kerr 		       console->console_id, type->name, handler ? "" : "in");
652e2826c7dSJeremy Kerr 
653e2826c7dSJeremy Kerr 		if (handler) {
654e2826c7dSJeremy Kerr 			handler->type = type;
655e2826c7dSJeremy Kerr 			console->handlers[j++] = handler;
656d831f960SJeremy Kerr 		}
657d831f960SJeremy Kerr 	}
658d831f960SJeremy Kerr 
659e2826c7dSJeremy Kerr 	console->n_handlers = j;
660e2826c7dSJeremy Kerr }
661e2826c7dSJeremy Kerr 
6621a0e03b4SJeremy Kerr static void handlers_fini(struct console *console)
663d831f960SJeremy Kerr {
6641a0e03b4SJeremy Kerr 	struct handler *handler;
6651a0e03b4SJeremy Kerr 	int i;
6661a0e03b4SJeremy Kerr 
6671a0e03b4SJeremy Kerr 	for (i = 0; i < console->n_handlers; i++) {
6681a0e03b4SJeremy Kerr 		handler = console->handlers[i];
669e2826c7dSJeremy Kerr 		handler->type->fini(handler);
6701a0e03b4SJeremy Kerr 	}
671e2826c7dSJeremy Kerr 
672e2826c7dSJeremy Kerr 	free(console->handlers);
673e2826c7dSJeremy Kerr 	console->handlers = NULL;
674e2826c7dSJeremy Kerr 	console->n_handlers = 0;
6752834c5b1SAndrew Jeffery }
676d831f960SJeremy Kerr 
6771cecc5deSJohnathan Mantey static int get_current_time(struct timeval *tv)
6781cecc5deSJohnathan Mantey {
6791cecc5deSJohnathan Mantey 	struct timespec t;
6801cecc5deSJohnathan Mantey 	int rc;
6811cecc5deSJohnathan Mantey 
6821cecc5deSJohnathan Mantey 	/*
6831cecc5deSJohnathan Mantey 	 * We use clock_gettime(CLOCK_MONOTONIC) so we're immune to
6841cecc5deSJohnathan Mantey 	 * local time changes. However, a struct timeval is more
6851cecc5deSJohnathan Mantey 	 * convenient for calculations, so convert to that.
6861cecc5deSJohnathan Mantey 	 */
6871cecc5deSJohnathan Mantey 	rc = clock_gettime(CLOCK_MONOTONIC, &t);
6882834c5b1SAndrew Jeffery 	if (rc) {
6891cecc5deSJohnathan Mantey 		return rc;
6902834c5b1SAndrew Jeffery 	}
6911cecc5deSJohnathan Mantey 
6921cecc5deSJohnathan Mantey 	tv->tv_sec = t.tv_sec;
6931cecc5deSJohnathan Mantey 	tv->tv_usec = t.tv_nsec / 1000;
6941cecc5deSJohnathan Mantey 
6951cecc5deSJohnathan Mantey 	return 0;
6961cecc5deSJohnathan Mantey }
6971cecc5deSJohnathan Mantey 
698a72711afSAndrew Jeffery struct ringbuffer_consumer *
699a72711afSAndrew Jeffery console_ringbuffer_consumer_register(struct console *console,
700f733c85aSJeremy Kerr 				     ringbuffer_poll_fn_t poll_fn, void *data)
701d831f960SJeremy Kerr {
702f733c85aSJeremy Kerr 	return ringbuffer_consumer_register(console->rb, poll_fn, data);
703d831f960SJeremy Kerr }
704d831f960SJeremy Kerr 
70555c9712dSJeremy Kerr struct poller *console_poller_register(struct console *console,
706a72711afSAndrew Jeffery 				       struct handler *handler,
707a72711afSAndrew Jeffery 				       poller_event_fn_t poller_fn,
7081cecc5deSJohnathan Mantey 				       poller_timeout_fn_t timeout_fn, int fd,
7091cecc5deSJohnathan Mantey 				       int events, void *data)
710d831f960SJeremy Kerr {
711329a35f5SJeremy Kerr 	struct poller *poller;
7125c359cc6SAndrew Jeffery 	long n;
713329a35f5SJeremy Kerr 
7142325d5d5SAlexander Hansen 	const ssize_t index = console_server_request_pollfd(
7152325d5d5SAlexander Hansen 		console->server, fd, (short)(events & 0x7fff));
7162325d5d5SAlexander Hansen 	if (index < 0) {
7172325d5d5SAlexander Hansen 		fprintf(stderr, "Error requesting pollfd\n");
7182325d5d5SAlexander Hansen 		return NULL;
7192325d5d5SAlexander Hansen 	}
7202325d5d5SAlexander Hansen 
721329a35f5SJeremy Kerr 	poller = malloc(sizeof(*poller));
7222325d5d5SAlexander Hansen 	// TODO: check for error case of malloc here and release previously requested pollfd
723329a35f5SJeremy Kerr 	poller->remove = false;
724329a35f5SJeremy Kerr 	poller->handler = handler;
7251cecc5deSJohnathan Mantey 	poller->event_fn = poller_fn;
7261cecc5deSJohnathan Mantey 	poller->timeout_fn = timeout_fn;
7279cc8459aSAndrew Jeffery 	timerclear(&poller->timeout);
728329a35f5SJeremy Kerr 	poller->data = data;
7292325d5d5SAlexander Hansen 	poller->pollfd_index = index;
730329a35f5SJeremy Kerr 
731329a35f5SJeremy Kerr 	/* add one to our pollers array */
732329a35f5SJeremy Kerr 	n = console->n_pollers++;
73391b52175SAndrew Jeffery 	/*
73491b52175SAndrew Jeffery 	 * We're managing an array of pointers to aggregates, so don't warn about sizeof() on a
73591b52175SAndrew Jeffery 	 * pointer type.
73691b52175SAndrew Jeffery 	 */
73791b52175SAndrew Jeffery 	/* NOLINTBEGIN(bugprone-sizeof-expression) */
73891b52175SAndrew Jeffery 	console->pollers = reallocarray(console->pollers, console->n_pollers,
73991b52175SAndrew Jeffery 					sizeof(*console->pollers));
7402325d5d5SAlexander Hansen 	// TODO: check for the error case of reallocarray and release previously requested pollfd
74191b52175SAndrew Jeffery 	/* NOLINTEND(bugprone-sizeof-expression) */
742329a35f5SJeremy Kerr 
743329a35f5SJeremy Kerr 	console->pollers[n] = poller;
744329a35f5SJeremy Kerr 
745329a35f5SJeremy Kerr 	return poller;
746329a35f5SJeremy Kerr }
747329a35f5SJeremy Kerr 
748a72711afSAndrew Jeffery void console_poller_unregister(struct console *console, struct poller *poller)
749329a35f5SJeremy Kerr {
750329a35f5SJeremy Kerr 	int i;
751329a35f5SJeremy Kerr 
752329a35f5SJeremy Kerr 	/* find the entry in our pollers array */
7532834c5b1SAndrew Jeffery 	for (i = 0; i < console->n_pollers; i++) {
7542834c5b1SAndrew Jeffery 		if (console->pollers[i] == poller) {
755329a35f5SJeremy Kerr 			break;
7562834c5b1SAndrew Jeffery 		}
7572834c5b1SAndrew Jeffery 	}
758329a35f5SJeremy Kerr 
759329a35f5SJeremy Kerr 	assert(i < console->n_pollers);
760329a35f5SJeremy Kerr 
761329a35f5SJeremy Kerr 	console->n_pollers--;
762329a35f5SJeremy Kerr 
76391b52175SAndrew Jeffery 	/*
76491b52175SAndrew Jeffery 	 * Remove the item from the pollers array...
76591b52175SAndrew Jeffery 	 *
76691b52175SAndrew Jeffery 	 * We're managing an array of pointers to aggregates, so don't warn about sizeof() on a
76791b52175SAndrew Jeffery 	 * pointer type.
76891b52175SAndrew Jeffery 	 */
76991b52175SAndrew Jeffery 	/* NOLINTBEGIN(bugprone-sizeof-expression) */
770329a35f5SJeremy Kerr 	memmove(&console->pollers[i], &console->pollers[i + 1],
771a72711afSAndrew Jeffery 		sizeof(*console->pollers) * (console->n_pollers - i));
772329a35f5SJeremy Kerr 
7737851a396SAndrew Jeffery 	if (console->n_pollers == 0) {
7747851a396SAndrew Jeffery 		free(console->pollers);
7757851a396SAndrew Jeffery 		console->pollers = NULL;
7767851a396SAndrew Jeffery 	} else {
7777851a396SAndrew Jeffery 		console->pollers = reallocarray(console->pollers,
7787851a396SAndrew Jeffery 						console->n_pollers,
77991b52175SAndrew Jeffery 						sizeof(*console->pollers));
7807851a396SAndrew Jeffery 	}
78191b52175SAndrew Jeffery 	/* NOLINTEND(bugprone-sizeof-expression) */
782329a35f5SJeremy Kerr 
7832325d5d5SAlexander Hansen 	console_server_release_pollfd(console->server, poller->pollfd_index);
784329a35f5SJeremy Kerr 
785329a35f5SJeremy Kerr 	free(poller);
786329a35f5SJeremy Kerr }
787329a35f5SJeremy Kerr 
7886b1fed27SJeremy Kerr void console_poller_set_events(struct console *console, struct poller *poller,
7896b1fed27SJeremy Kerr 			       int events)
7906b1fed27SJeremy Kerr {
7912325d5d5SAlexander Hansen 	console->server->pollfds[poller->pollfd_index].events =
7922325d5d5SAlexander Hansen 		(short)(events & 0x7fff);
7936b1fed27SJeremy Kerr }
7946b1fed27SJeremy Kerr 
795fd048328SAndrew Jeffery void console_poller_set_timeout(struct console *console __attribute__((unused)),
796fd048328SAndrew Jeffery 				struct poller *poller, const struct timeval *tv)
7971cecc5deSJohnathan Mantey {
7981cecc5deSJohnathan Mantey 	struct timeval now;
7991cecc5deSJohnathan Mantey 	int rc;
8001cecc5deSJohnathan Mantey 
8011cecc5deSJohnathan Mantey 	rc = get_current_time(&now);
8022834c5b1SAndrew Jeffery 	if (rc) {
8031cecc5deSJohnathan Mantey 		return;
8042834c5b1SAndrew Jeffery 	}
8051cecc5deSJohnathan Mantey 
8061cecc5deSJohnathan Mantey 	timeradd(&now, tv, &poller->timeout);
8071cecc5deSJohnathan Mantey }
8081cecc5deSJohnathan Mantey 
8095c359cc6SAndrew Jeffery static long get_poll_timeout(struct console *console, struct timeval *cur_time)
8101cecc5deSJohnathan Mantey {
811b70f8713SAndrew Jeffery 	struct timeval *earliest;
812b70f8713SAndrew Jeffery 	struct timeval interval;
8131cecc5deSJohnathan Mantey 	struct poller *poller;
8141cecc5deSJohnathan Mantey 	int i;
8151cecc5deSJohnathan Mantey 
8161cecc5deSJohnathan Mantey 	earliest = NULL;
8171cecc5deSJohnathan Mantey 
8181cecc5deSJohnathan Mantey 	for (i = 0; i < console->n_pollers; i++) {
8191cecc5deSJohnathan Mantey 		poller = console->pollers[i];
8201cecc5deSJohnathan Mantey 
8211cecc5deSJohnathan Mantey 		if (poller->timeout_fn && timerisset(&poller->timeout) &&
8221cecc5deSJohnathan Mantey 		    (!earliest ||
8231cecc5deSJohnathan Mantey 		     (earliest && timercmp(&poller->timeout, earliest, <)))) {
8241cecc5deSJohnathan Mantey 			// poller is buffering data and needs the poll
8251cecc5deSJohnathan Mantey 			// function to timeout.
8261cecc5deSJohnathan Mantey 			earliest = &poller->timeout;
8271cecc5deSJohnathan Mantey 		}
8281cecc5deSJohnathan Mantey 	}
8291cecc5deSJohnathan Mantey 
8301cecc5deSJohnathan Mantey 	if (earliest) {
8311cecc5deSJohnathan Mantey 		if (timercmp(earliest, cur_time, >)) {
8321cecc5deSJohnathan Mantey 			/* recalculate the timeout period, time period has
8331cecc5deSJohnathan Mantey 			 * not elapsed */
8341cecc5deSJohnathan Mantey 			timersub(earliest, cur_time, &interval);
8351cecc5deSJohnathan Mantey 			return ((interval.tv_sec * 1000) +
8361cecc5deSJohnathan Mantey 				(interval.tv_usec / 1000));
8370b7b0477SAndrew Jeffery 		} /* return from poll immediately */
8381cecc5deSJohnathan Mantey 		return 0;
8390b7b0477SAndrew Jeffery 
8400b7b0477SAndrew Jeffery 	} /* poll indefinitely */
8411cecc5deSJohnathan Mantey 	return -1;
8421cecc5deSJohnathan Mantey }
8431cecc5deSJohnathan Mantey 
8441cecc5deSJohnathan Mantey static int call_pollers(struct console *console, struct timeval *cur_time)
845329a35f5SJeremy Kerr {
846329a35f5SJeremy Kerr 	struct poller *poller;
847329a35f5SJeremy Kerr 	struct pollfd *pollfd;
848329a35f5SJeremy Kerr 	enum poller_ret prc;
849b70f8713SAndrew Jeffery 	int i;
850b70f8713SAndrew Jeffery 	int rc;
851d831f960SJeremy Kerr 
8521a0e03b4SJeremy Kerr 	rc = 0;
8531a0e03b4SJeremy Kerr 
854329a35f5SJeremy Kerr 	/*
855329a35f5SJeremy Kerr 	 * Process poll events by iterating through the pollers and pollfds
856329a35f5SJeremy Kerr 	 * in-step, calling any pollers that we've found revents for.
857329a35f5SJeremy Kerr 	 */
858329a35f5SJeremy Kerr 	for (i = 0; i < console->n_pollers; i++) {
859329a35f5SJeremy Kerr 		poller = console->pollers[i];
8602325d5d5SAlexander Hansen 		pollfd = &console->server->pollfds[poller->pollfd_index];
8612325d5d5SAlexander Hansen 		if (pollfd->fd < 0) {
8622325d5d5SAlexander Hansen 			// pollfd has already been released
8632325d5d5SAlexander Hansen 			continue;
8642325d5d5SAlexander Hansen 		}
8652325d5d5SAlexander Hansen 
8661cecc5deSJohnathan Mantey 		prc = POLLER_OK;
8671a0e03b4SJeremy Kerr 
8681cecc5deSJohnathan Mantey 		/* process pending events... */
8691cecc5deSJohnathan Mantey 		if (pollfd->revents) {
8701cecc5deSJohnathan Mantey 			prc = poller->event_fn(poller->handler, pollfd->revents,
871329a35f5SJeremy Kerr 					       poller->data);
8722834c5b1SAndrew Jeffery 			if (prc == POLLER_EXIT) {
873329a35f5SJeremy Kerr 				rc = -1;
8742834c5b1SAndrew Jeffery 			} else if (prc == POLLER_REMOVE) {
875329a35f5SJeremy Kerr 				poller->remove = true;
876329a35f5SJeremy Kerr 			}
8772834c5b1SAndrew Jeffery 		}
878329a35f5SJeremy Kerr 
8791cecc5deSJohnathan Mantey 		if ((prc == POLLER_OK) && poller->timeout_fn &&
8801cecc5deSJohnathan Mantey 		    timerisset(&poller->timeout) &&
8811cecc5deSJohnathan Mantey 		    timercmp(&poller->timeout, cur_time, <=)) {
8821cecc5deSJohnathan Mantey 			/* One of the ringbuffer consumers is buffering the
8831cecc5deSJohnathan Mantey 			data stream. The amount of idle time the consumer
8841cecc5deSJohnathan Mantey 			desired has expired.  Process the buffered data for
8851cecc5deSJohnathan Mantey 			transmission. */
8861cecc5deSJohnathan Mantey 			timerclear(&poller->timeout);
8871cecc5deSJohnathan Mantey 			prc = poller->timeout_fn(poller->handler, poller->data);
8881cecc5deSJohnathan Mantey 			if (prc == POLLER_EXIT) {
8891cecc5deSJohnathan Mantey 				rc = -1;
8901cecc5deSJohnathan Mantey 			} else if (prc == POLLER_REMOVE) {
8911cecc5deSJohnathan Mantey 				poller->remove = true;
8921cecc5deSJohnathan Mantey 			}
8931cecc5deSJohnathan Mantey 		}
8941cecc5deSJohnathan Mantey 	}
8951cecc5deSJohnathan Mantey 
896329a35f5SJeremy Kerr 	/**
897329a35f5SJeremy Kerr 	 * Process deferred removals; restarting each time we unregister, as
898329a35f5SJeremy Kerr 	 * the array will have changed
899329a35f5SJeremy Kerr 	 */
900329a35f5SJeremy Kerr 	for (;;) {
901329a35f5SJeremy Kerr 		bool removed = false;
902329a35f5SJeremy Kerr 
903329a35f5SJeremy Kerr 		for (i = 0; i < console->n_pollers; i++) {
904329a35f5SJeremy Kerr 			poller = console->pollers[i];
905329a35f5SJeremy Kerr 			if (poller->remove) {
90655c9712dSJeremy Kerr 				console_poller_unregister(console, poller);
907329a35f5SJeremy Kerr 				removed = true;
908329a35f5SJeremy Kerr 				break;
909329a35f5SJeremy Kerr 			}
910329a35f5SJeremy Kerr 		}
9112834c5b1SAndrew Jeffery 		if (!removed) {
912329a35f5SJeremy Kerr 			break;
9131a0e03b4SJeremy Kerr 		}
9142834c5b1SAndrew Jeffery 	}
9151a0e03b4SJeremy Kerr 
9161a0e03b4SJeremy Kerr 	return rc;
9171a0e03b4SJeremy Kerr }
9181a0e03b4SJeremy Kerr 
919769cee1aSJeremy Kerr static void sighandler(int signal)
920769cee1aSJeremy Kerr {
9212834c5b1SAndrew Jeffery 	if (signal == SIGINT) {
922553cb663SAndrew Jeffery 		sigint = 1;
923769cee1aSJeremy Kerr 	}
9242834c5b1SAndrew Jeffery }
925769cee1aSJeremy Kerr 
926*312ecdc2SAlexander Hansen static int run_console_per_console(struct console *console, size_t buf_size,
927*312ecdc2SAlexander Hansen 				   struct timeval *tv)
9281a0e03b4SJeremy Kerr {
929*312ecdc2SAlexander Hansen 	int rc;
930769cee1aSJeremy Kerr 
931*312ecdc2SAlexander Hansen 	if (console->rb->size < buf_size) {
9326925740dSAlexander Hansen 		fprintf(stderr, "Ringbuffer size should be greater than %zuB\n",
933*312ecdc2SAlexander Hansen 			buf_size);
9346925740dSAlexander Hansen 		return -1;
9357f2bfb9bSMedicine Yeh 	}
9361764145dSJeremy Kerr 
937769cee1aSJeremy Kerr 	if (sigint) {
938*312ecdc2SAlexander Hansen 		warnx("Received interrupt, exiting\n");
9396925740dSAlexander Hansen 		return -1;
940769cee1aSJeremy Kerr 	}
941769cee1aSJeremy Kerr 
942*312ecdc2SAlexander Hansen 	/* ... and then the pollers */
943*312ecdc2SAlexander Hansen 	rc = call_pollers(console, tv);
944*312ecdc2SAlexander Hansen 	if (rc) {
945*312ecdc2SAlexander Hansen 		return -1;
946*312ecdc2SAlexander Hansen 	}
947*312ecdc2SAlexander Hansen 
948*312ecdc2SAlexander Hansen 	return 0;
949*312ecdc2SAlexander Hansen }
950*312ecdc2SAlexander Hansen 
951*312ecdc2SAlexander Hansen static int run_console_iteration(struct console_server *server)
952*312ecdc2SAlexander Hansen {
953*312ecdc2SAlexander Hansen 	struct timeval tv;
954*312ecdc2SAlexander Hansen 	uint8_t buf[4096];
955*312ecdc2SAlexander Hansen 	long timeout;
956*312ecdc2SAlexander Hansen 	ssize_t rc;
957*312ecdc2SAlexander Hansen 
9581cecc5deSJohnathan Mantey 	rc = get_current_time(&tv);
9591cecc5deSJohnathan Mantey 	if (rc) {
9601cecc5deSJohnathan Mantey 		warn("Failed to read current time");
9616925740dSAlexander Hansen 		return -1;
9621cecc5deSJohnathan Mantey 	}
9631cecc5deSJohnathan Mantey 
964*312ecdc2SAlexander Hansen 	timeout = get_poll_timeout(server->active, &tv);
9651cecc5deSJohnathan Mantey 
966*312ecdc2SAlexander Hansen 	rc = poll(server->pollfds, server->capacity_pollfds, (int)timeout);
967*312ecdc2SAlexander Hansen 
968*312ecdc2SAlexander Hansen 	if (sigint) {
969*312ecdc2SAlexander Hansen 		warnx("Received interrupt, exiting\n");
970*312ecdc2SAlexander Hansen 		return -1;
971*312ecdc2SAlexander Hansen 	}
9721cecc5deSJohnathan Mantey 
973d831f960SJeremy Kerr 	if (rc < 0) {
974769cee1aSJeremy Kerr 		if (errno == EINTR) {
9756925740dSAlexander Hansen 			return 0;
9760b7b0477SAndrew Jeffery 		}
977d831f960SJeremy Kerr 		warn("poll error");
9786925740dSAlexander Hansen 		return -1;
979769cee1aSJeremy Kerr 	}
980d831f960SJeremy Kerr 
981329a35f5SJeremy Kerr 	/* process internal fd first */
982*312ecdc2SAlexander Hansen 	if (server->pollfds[server->tty_pollfd_index].revents) {
983*312ecdc2SAlexander Hansen 		rc = read(server->tty.fd, buf, sizeof(buf));
984d831f960SJeremy Kerr 		if (rc <= 0) {
985d831f960SJeremy Kerr 			warn("Error reading from tty device");
9866925740dSAlexander Hansen 			return -1;
987d831f960SJeremy Kerr 		}
988*312ecdc2SAlexander Hansen 
989*312ecdc2SAlexander Hansen 		rc = ringbuffer_queue(server->active->rb, buf, rc);
9902834c5b1SAndrew Jeffery 		if (rc) {
9916925740dSAlexander Hansen 			return -1;
992d831f960SJeremy Kerr 		}
9932834c5b1SAndrew Jeffery 	}
994d831f960SJeremy Kerr 
995*312ecdc2SAlexander Hansen 	// process dbus
996*312ecdc2SAlexander Hansen 	struct pollfd *dbus_pollfd =
997*312ecdc2SAlexander Hansen 		&(server->pollfds[server->dbus_pollfd_index]);
998*312ecdc2SAlexander Hansen 	if (dbus_pollfd->revents) {
999*312ecdc2SAlexander Hansen 		sd_bus_process(server->bus, NULL);
1000f9c8f6caSCheng C Yang 	}
1001f9c8f6caSCheng C Yang 
1002*312ecdc2SAlexander Hansen 	for (size_t i = 0; i < server->n_consoles; i++) {
1003*312ecdc2SAlexander Hansen 		struct console *console = server->consoles[i];
1004*312ecdc2SAlexander Hansen 
1005*312ecdc2SAlexander Hansen 		rc = run_console_per_console(console, sizeof(buf), &tv);
1006*312ecdc2SAlexander Hansen 		if (rc != 0) {
10076925740dSAlexander Hansen 			return -1;
10086925740dSAlexander Hansen 		}
1009*312ecdc2SAlexander Hansen 	}
1010*312ecdc2SAlexander Hansen 
10116925740dSAlexander Hansen 	return 0;
10126925740dSAlexander Hansen }
10136925740dSAlexander Hansen 
1014*312ecdc2SAlexander Hansen int run_server(struct console_server *server)
10156925740dSAlexander Hansen {
1016*312ecdc2SAlexander Hansen 	sighandler_t sighandler_save;
10176925740dSAlexander Hansen 	ssize_t rc = 0;
10186925740dSAlexander Hansen 
1019*312ecdc2SAlexander Hansen 	if (server->n_consoles == 0) {
1020*312ecdc2SAlexander Hansen 		warnx("no console configured for this server");
1021*312ecdc2SAlexander Hansen 		return -1;
1022*312ecdc2SAlexander Hansen 	}
1023*312ecdc2SAlexander Hansen 
1024*312ecdc2SAlexander Hansen 	sighandler_save = signal(SIGINT, sighandler);
10256925740dSAlexander Hansen 	for (;;) {
1026*312ecdc2SAlexander Hansen 		rc = run_console_iteration(server);
10276925740dSAlexander Hansen 		if (rc) {
1028769cee1aSJeremy Kerr 			break;
10291a0e03b4SJeremy Kerr 		}
10302834c5b1SAndrew Jeffery 	}
1031769cee1aSJeremy Kerr 	signal(SIGINT, sighandler_save);
1032769cee1aSJeremy Kerr 
1033769cee1aSJeremy Kerr 	return rc ? -1 : 0;
10341a0e03b4SJeremy Kerr }
1035*312ecdc2SAlexander Hansen 
1036d831f960SJeremy Kerr static const struct option options[] = {
1037d66195c1SJeremy Kerr 	{ "config", required_argument, 0, 'c' },
1038954be0fbSAndrew Jeffery 	{ "console-id", required_argument, 0, 'i' },
1039f5858b5bSJoel Stanley 	{ 0, 0, 0, 0 },
1040d831f960SJeremy Kerr };
1041d831f960SJeremy Kerr 
1042*312ecdc2SAlexander Hansen static struct console *console_init(struct console_server *server,
1043*312ecdc2SAlexander Hansen 				    struct config *config,
1044*312ecdc2SAlexander Hansen 				    const char *console_id)
1045d831f960SJeremy Kerr {
10467f2bfb9bSMedicine Yeh 	size_t buffer_size = default_buffer_size;
1047*312ecdc2SAlexander Hansen 	const char *buffer_size_str = NULL;
1048*312ecdc2SAlexander Hansen 	int rc;
1049*312ecdc2SAlexander Hansen 
1050*312ecdc2SAlexander Hansen 	struct console *console = calloc(1, sizeof(struct console));
1051*312ecdc2SAlexander Hansen 	if (console == NULL) {
1052*312ecdc2SAlexander Hansen 		return NULL;
1053*312ecdc2SAlexander Hansen 	}
1054*312ecdc2SAlexander Hansen 
1055*312ecdc2SAlexander Hansen 	console->server = server;
1056*312ecdc2SAlexander Hansen 	console->console_id = console_id;
1057*312ecdc2SAlexander Hansen 
1058*312ecdc2SAlexander Hansen 	buffer_size_str =
1059*312ecdc2SAlexander Hansen 		config_get_section_value(config, console_id, "ringbuffer-size");
1060*312ecdc2SAlexander Hansen 
1061*312ecdc2SAlexander Hansen 	if (!buffer_size_str) {
1062*312ecdc2SAlexander Hansen 		buffer_size_str = config_get_value(config, "ringbuffer-size");
1063*312ecdc2SAlexander Hansen 	}
1064*312ecdc2SAlexander Hansen 
1065*312ecdc2SAlexander Hansen 	if (buffer_size_str) {
1066*312ecdc2SAlexander Hansen 		rc = config_parse_bytesize(buffer_size_str, &buffer_size);
1067*312ecdc2SAlexander Hansen 		if (rc) {
1068*312ecdc2SAlexander Hansen 			warn("Invalid ringbuffer-size. Default to %zukB",
1069*312ecdc2SAlexander Hansen 			     buffer_size >> 10);
1070*312ecdc2SAlexander Hansen 		}
1071*312ecdc2SAlexander Hansen 	}
1072*312ecdc2SAlexander Hansen 
1073*312ecdc2SAlexander Hansen 	console->rb = ringbuffer_init(buffer_size);
1074*312ecdc2SAlexander Hansen 	if (!console->rb) {
1075*312ecdc2SAlexander Hansen 		goto cleanup_console;
1076*312ecdc2SAlexander Hansen 	}
1077*312ecdc2SAlexander Hansen 
1078*312ecdc2SAlexander Hansen 	if (set_socket_info(console, config, console_id)) {
1079*312ecdc2SAlexander Hansen 		warnx("set_socket_info failed");
1080*312ecdc2SAlexander Hansen 		goto cleanup_rb;
1081*312ecdc2SAlexander Hansen 	}
1082*312ecdc2SAlexander Hansen 
1083*312ecdc2SAlexander Hansen 	rc = dbus_init(console, config);
1084*312ecdc2SAlexander Hansen 	if (rc != 0) {
1085*312ecdc2SAlexander Hansen 		goto cleanup_rb;
1086*312ecdc2SAlexander Hansen 	}
1087*312ecdc2SAlexander Hansen 
1088*312ecdc2SAlexander Hansen 	handlers_init(console, config);
1089*312ecdc2SAlexander Hansen 
1090*312ecdc2SAlexander Hansen 	return console;
1091*312ecdc2SAlexander Hansen 
1092*312ecdc2SAlexander Hansen cleanup_rb:
1093*312ecdc2SAlexander Hansen 	free(console->rb);
1094*312ecdc2SAlexander Hansen cleanup_console:
1095*312ecdc2SAlexander Hansen 	free(console);
1096*312ecdc2SAlexander Hansen 
1097*312ecdc2SAlexander Hansen 	return NULL;
1098*312ecdc2SAlexander Hansen }
1099*312ecdc2SAlexander Hansen 
1100*312ecdc2SAlexander Hansen static void console_fini(struct console *console)
1101*312ecdc2SAlexander Hansen {
1102*312ecdc2SAlexander Hansen 	handlers_fini(console);
1103*312ecdc2SAlexander Hansen 	ringbuffer_fini(console->rb);
1104*312ecdc2SAlexander Hansen 	free(console->pollers);
1105*312ecdc2SAlexander Hansen 	free(console);
1106*312ecdc2SAlexander Hansen }
1107*312ecdc2SAlexander Hansen 
1108*312ecdc2SAlexander Hansen // 'opt_console_id' may be NULL
1109*312ecdc2SAlexander Hansen static int console_server_add_console(struct console_server *server,
1110*312ecdc2SAlexander Hansen 				      struct config *config,
1111*312ecdc2SAlexander Hansen 				      const char *opt_console_id)
1112*312ecdc2SAlexander Hansen {
1113*312ecdc2SAlexander Hansen 	const char *console_id;
1114*312ecdc2SAlexander Hansen 	struct console *console;
1115*312ecdc2SAlexander Hansen 
1116*312ecdc2SAlexander Hansen 	console_id = config_resolve_console_id(config, opt_console_id);
1117*312ecdc2SAlexander Hansen 
1118*312ecdc2SAlexander Hansen 	struct console **tmp = reallocarray(server->consoles,
1119*312ecdc2SAlexander Hansen 					    server->n_consoles + 1,
1120*312ecdc2SAlexander Hansen 					    sizeof(struct console *));
1121*312ecdc2SAlexander Hansen 	if (tmp == NULL) {
1122*312ecdc2SAlexander Hansen 		warnx("could not realloc server->consoles");
1123*312ecdc2SAlexander Hansen 		return -1;
1124*312ecdc2SAlexander Hansen 	}
1125*312ecdc2SAlexander Hansen 	server->consoles = tmp;
1126*312ecdc2SAlexander Hansen 
1127*312ecdc2SAlexander Hansen 	console = console_init(server, config, console_id);
1128*312ecdc2SAlexander Hansen 	if (console == NULL) {
1129*312ecdc2SAlexander Hansen 		warnx("console_init failed");
1130*312ecdc2SAlexander Hansen 		return -1;
1131*312ecdc2SAlexander Hansen 	}
1132*312ecdc2SAlexander Hansen 
1133*312ecdc2SAlexander Hansen 	server->consoles[server->n_consoles++] = console;
1134*312ecdc2SAlexander Hansen 
1135*312ecdc2SAlexander Hansen 	return 0;
1136*312ecdc2SAlexander Hansen }
1137*312ecdc2SAlexander Hansen 
1138*312ecdc2SAlexander Hansen // returns NULL on error
1139*312ecdc2SAlexander Hansen static struct console *
1140*312ecdc2SAlexander Hansen console_server_add_consoles(struct console_server *server,
1141*312ecdc2SAlexander Hansen 			    const char *arg_console_id)
1142*312ecdc2SAlexander Hansen {
1143*312ecdc2SAlexander Hansen 	int rc;
1144*312ecdc2SAlexander Hansen 
1145*312ecdc2SAlexander Hansen 	const int nsections = config_count_sections(server->config);
1146*312ecdc2SAlexander Hansen 	if (nsections < 0) {
1147*312ecdc2SAlexander Hansen 		return NULL;
1148*312ecdc2SAlexander Hansen 	}
1149*312ecdc2SAlexander Hansen 
1150*312ecdc2SAlexander Hansen 	if (nsections == 0) {
1151*312ecdc2SAlexander Hansen 		const char *console_id = arg_console_id;
1152*312ecdc2SAlexander Hansen 
1153*312ecdc2SAlexander Hansen 		rc = console_server_add_console(server, server->config,
1154*312ecdc2SAlexander Hansen 						console_id);
1155*312ecdc2SAlexander Hansen 		if (rc != 0) {
1156*312ecdc2SAlexander Hansen 			return NULL;
1157*312ecdc2SAlexander Hansen 		}
1158*312ecdc2SAlexander Hansen 	}
1159*312ecdc2SAlexander Hansen 
1160*312ecdc2SAlexander Hansen 	for (int i = 0; i < nsections; i++) {
1161*312ecdc2SAlexander Hansen 		const char *console_id =
1162*312ecdc2SAlexander Hansen 			config_get_section_name(server->config, i);
1163*312ecdc2SAlexander Hansen 
1164*312ecdc2SAlexander Hansen 		if (console_id == NULL) {
1165*312ecdc2SAlexander Hansen 			warnx("no console id provided\n");
1166*312ecdc2SAlexander Hansen 			return NULL;
1167*312ecdc2SAlexander Hansen 		}
1168*312ecdc2SAlexander Hansen 
1169*312ecdc2SAlexander Hansen 		rc = console_server_add_console(server, server->config,
1170*312ecdc2SAlexander Hansen 						console_id);
1171*312ecdc2SAlexander Hansen 		if (rc != 0) {
1172*312ecdc2SAlexander Hansen 			return NULL;
1173*312ecdc2SAlexander Hansen 		}
1174*312ecdc2SAlexander Hansen 	}
1175*312ecdc2SAlexander Hansen 
1176*312ecdc2SAlexander Hansen 	const char *initially_active =
1177*312ecdc2SAlexander Hansen 		config_get_value(server->config, "active-console");
1178*312ecdc2SAlexander Hansen 	if (!initially_active) {
1179*312ecdc2SAlexander Hansen 		return server->consoles[0];
1180*312ecdc2SAlexander Hansen 	}
1181*312ecdc2SAlexander Hansen 
1182*312ecdc2SAlexander Hansen 	printf("setting console-id '%s' as the initially active console\n",
1183*312ecdc2SAlexander Hansen 	       initially_active);
1184*312ecdc2SAlexander Hansen 
1185*312ecdc2SAlexander Hansen 	for (size_t i = 0; i < server->n_consoles; i++) {
1186*312ecdc2SAlexander Hansen 		struct console *console = server->consoles[i];
1187*312ecdc2SAlexander Hansen 
1188*312ecdc2SAlexander Hansen 		if (strcmp(console->console_id, initially_active) == 0) {
1189*312ecdc2SAlexander Hansen 			return console;
1190*312ecdc2SAlexander Hansen 		}
1191*312ecdc2SAlexander Hansen 	}
1192*312ecdc2SAlexander Hansen 
1193*312ecdc2SAlexander Hansen 	warnx("'active-console' '%s' not found among console ids\n",
1194*312ecdc2SAlexander Hansen 	      initially_active);
1195*312ecdc2SAlexander Hansen 
1196*312ecdc2SAlexander Hansen 	return NULL;
1197*312ecdc2SAlexander Hansen }
1198*312ecdc2SAlexander Hansen 
1199*312ecdc2SAlexander Hansen int console_server_init(struct console_server *server,
1200*312ecdc2SAlexander Hansen 			const char *config_filename,
1201*312ecdc2SAlexander Hansen 			const char *config_tty_kname, const char *console_id)
1202*312ecdc2SAlexander Hansen {
1203*312ecdc2SAlexander Hansen 	int rc;
1204*312ecdc2SAlexander Hansen 	memset(server, 0, sizeof(struct console_server));
1205*312ecdc2SAlexander Hansen 
1206*312ecdc2SAlexander Hansen 	server->tty_pollfd_index = -1;
1207*312ecdc2SAlexander Hansen 
1208*312ecdc2SAlexander Hansen 	server->config = config_init(config_filename);
1209*312ecdc2SAlexander Hansen 	if (server->config == NULL) {
1210*312ecdc2SAlexander Hansen 		return -1;
1211*312ecdc2SAlexander Hansen 	}
1212*312ecdc2SAlexander Hansen 
1213*312ecdc2SAlexander Hansen 	uart_routing_init(server->config);
1214*312ecdc2SAlexander Hansen 
1215*312ecdc2SAlexander Hansen 	rc = tty_init(server, server->config, config_tty_kname);
1216*312ecdc2SAlexander Hansen 	if (rc != 0) {
1217*312ecdc2SAlexander Hansen 		warnx("error during tty_init, exiting.\n");
1218*312ecdc2SAlexander Hansen 		return -1;
1219*312ecdc2SAlexander Hansen 	}
1220*312ecdc2SAlexander Hansen 
1221*312ecdc2SAlexander Hansen 	rc = dbus_server_init(server);
1222*312ecdc2SAlexander Hansen 	if (rc != 0) {
1223*312ecdc2SAlexander Hansen 		warnx("error during dbus init for console server");
1224*312ecdc2SAlexander Hansen 		return -1;
1225*312ecdc2SAlexander Hansen 	}
1226*312ecdc2SAlexander Hansen 
1227*312ecdc2SAlexander Hansen 	server->active = console_server_add_consoles(server, console_id);
1228*312ecdc2SAlexander Hansen 	if (server->active == NULL) {
1229*312ecdc2SAlexander Hansen 		return -1;
1230*312ecdc2SAlexander Hansen 	}
1231*312ecdc2SAlexander Hansen 
1232*312ecdc2SAlexander Hansen 	return 0;
1233*312ecdc2SAlexander Hansen }
1234*312ecdc2SAlexander Hansen 
1235*312ecdc2SAlexander Hansen void console_server_fini(struct console_server *server)
1236*312ecdc2SAlexander Hansen {
1237*312ecdc2SAlexander Hansen 	for (size_t i = 0; i < server->n_consoles; i++) {
1238*312ecdc2SAlexander Hansen 		console_fini(server->consoles[i]);
1239*312ecdc2SAlexander Hansen 	}
1240*312ecdc2SAlexander Hansen 
1241*312ecdc2SAlexander Hansen 	free(server->consoles);
1242*312ecdc2SAlexander Hansen 	dbus_server_fini(server);
1243*312ecdc2SAlexander Hansen 	tty_fini(server);
1244*312ecdc2SAlexander Hansen 	free(server->pollfds);
1245*312ecdc2SAlexander Hansen 	config_fini(server->config);
1246*312ecdc2SAlexander Hansen }
1247*312ecdc2SAlexander Hansen 
1248*312ecdc2SAlexander Hansen int main(int argc, char **argv)
1249*312ecdc2SAlexander Hansen {
1250d66195c1SJeremy Kerr 	const char *config_filename = NULL;
12516221ce94SVishwanatha Subbanna 	const char *config_tty_kname = NULL;
1252954be0fbSAndrew Jeffery 	const char *console_id = NULL;
1253c2b0d8c7SAlexander Hansen 	struct console_server server = { 0 };
1254*312ecdc2SAlexander Hansen 	int rc = 0;
1255d831f960SJeremy Kerr 
1256d831f960SJeremy Kerr 	for (;;) {
1257b70f8713SAndrew Jeffery 		int c;
1258b70f8713SAndrew Jeffery 		int idx;
1259d831f960SJeremy Kerr 
1260954be0fbSAndrew Jeffery 		c = getopt_long(argc, argv, "c:i:", options, &idx);
12612834c5b1SAndrew Jeffery 		if (c == -1) {
1262d831f960SJeremy Kerr 			break;
12632834c5b1SAndrew Jeffery 		}
1264d831f960SJeremy Kerr 
1265d831f960SJeremy Kerr 		switch (c) {
1266d66195c1SJeremy Kerr 		case 'c':
1267d66195c1SJeremy Kerr 			config_filename = optarg;
1268d831f960SJeremy Kerr 			break;
1269954be0fbSAndrew Jeffery 		case 'i':
1270954be0fbSAndrew Jeffery 			console_id = optarg;
1271954be0fbSAndrew Jeffery 			break;
1272d831f960SJeremy Kerr 		case 'h':
1273d831f960SJeremy Kerr 		case '?':
1274d831f960SJeremy Kerr 			usage(argv[0]);
1275d66195c1SJeremy Kerr 			return EXIT_SUCCESS;
1276d831f960SJeremy Kerr 		}
1277d831f960SJeremy Kerr 	}
1278d831f960SJeremy Kerr 
12792834c5b1SAndrew Jeffery 	if (optind < argc) {
12806221ce94SVishwanatha Subbanna 		config_tty_kname = argv[optind];
1281*312ecdc2SAlexander Hansen 	} else {
1282*312ecdc2SAlexander Hansen 		errx(EXIT_FAILURE, "no tty device path has been provided\n");
12832834c5b1SAndrew Jeffery 	}
12846221ce94SVishwanatha Subbanna 
1285*312ecdc2SAlexander Hansen 	rc = console_server_init(&server, config_filename, config_tty_kname,
1286*312ecdc2SAlexander Hansen 				 console_id);
1287*312ecdc2SAlexander Hansen 
1288*312ecdc2SAlexander Hansen 	if (rc == 0) {
1289*312ecdc2SAlexander Hansen 		rc = run_server(&server);
129028a1761aSAndrew Jeffery 	}
12917f2bfb9bSMedicine Yeh 
1292*312ecdc2SAlexander Hansen 	console_server_fini(&server);
129328a1761aSAndrew Jeffery 
1294d831f960SJeremy Kerr 	return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
1295d831f960SJeremy Kerr }
1296