xref: /openbmc/obmc-console/console-server.c (revision a6b291048fcb0fb58ada2c47b29cbc80b5f5f514)
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 
41*a6b29104SAlexander Hansen #include "console-mux.h"
42*a6b29104SAlexander Hansen 
431a0e03b4SJeremy Kerr #include "console-server.h"
441e04f449SAlexander Hansen #include "config.h"
45d831f960SJeremy Kerr 
4630ea6385SAndrew Jeffery #define DEV_PTS_PATH "/dev/pts"
4730ea6385SAndrew Jeffery 
487f2bfb9bSMedicine Yeh /* default size of the shared backlog ringbuffer */
497f2bfb9bSMedicine Yeh const size_t default_buffer_size = 128ul * 1024ul;
50f733c85aSJeremy Kerr 
51769cee1aSJeremy Kerr /* state shared with the signal handler */
52553cb663SAndrew Jeffery static volatile sig_atomic_t sigint;
53329a35f5SJeremy Kerr 
usage(const char * progname)54d831f960SJeremy Kerr static void usage(const char *progname)
55d831f960SJeremy Kerr {
56d831f960SJeremy Kerr 	fprintf(stderr,
576221ce94SVishwanatha Subbanna 		"usage: %s [options] <DEVICE>\n"
58d831f960SJeremy Kerr 		"\n"
59d831f960SJeremy Kerr 		"Options:\n"
60954be0fbSAndrew Jeffery 		"  --config <FILE>\tUse FILE for configuration\n"
61954be0fbSAndrew Jeffery 		"  --console-id <NAME>\tUse NAME in the UNIX domain socket address\n"
62d831f960SJeremy Kerr 		"",
63d831f960SJeremy Kerr 		progname);
64d831f960SJeremy Kerr }
65d831f960SJeremy Kerr 
console_server_pollfd_reclaimable(struct pollfd * p)662325d5d5SAlexander Hansen static bool console_server_pollfd_reclaimable(struct pollfd *p)
672325d5d5SAlexander Hansen {
682325d5d5SAlexander Hansen 	return p->fd == -1 && p->events == 0 && p->revents == ~0;
692325d5d5SAlexander Hansen }
702325d5d5SAlexander Hansen 
712325d5d5SAlexander Hansen static ssize_t
console_server_find_released_pollfd(struct console_server * server)722325d5d5SAlexander Hansen console_server_find_released_pollfd(struct console_server *server)
732325d5d5SAlexander Hansen {
742325d5d5SAlexander Hansen 	for (size_t i = 0; i < server->capacity_pollfds; i++) {
752325d5d5SAlexander Hansen 		struct pollfd *p = &server->pollfds[i];
762325d5d5SAlexander Hansen 		if (console_server_pollfd_reclaimable(p)) {
772325d5d5SAlexander Hansen 			return (ssize_t)i;
782325d5d5SAlexander Hansen 		}
792325d5d5SAlexander Hansen 	}
802325d5d5SAlexander Hansen 	return -1;
812325d5d5SAlexander Hansen }
822325d5d5SAlexander Hansen 
832325d5d5SAlexander Hansen // returns the index of that pollfd in server->pollfds
842325d5d5SAlexander Hansen // we cannot return a pointer because 'realloc' may move server->pollfds
console_server_request_pollfd(struct console_server * server,int fd,short int events)852325d5d5SAlexander Hansen ssize_t console_server_request_pollfd(struct console_server *server, int fd,
862325d5d5SAlexander Hansen 				      short int events)
872325d5d5SAlexander Hansen {
882325d5d5SAlexander Hansen 	ssize_t index;
892325d5d5SAlexander Hansen 	struct pollfd *pollfd;
902325d5d5SAlexander Hansen 
912325d5d5SAlexander Hansen 	index = console_server_find_released_pollfd(server);
922325d5d5SAlexander Hansen 
932325d5d5SAlexander Hansen 	if (index < 0) {
942325d5d5SAlexander Hansen 		const size_t newcap = server->capacity_pollfds + 1;
952325d5d5SAlexander Hansen 
962325d5d5SAlexander Hansen 		struct pollfd *newarr = reallocarray(server->pollfds, newcap,
972325d5d5SAlexander Hansen 						     sizeof(struct pollfd));
982325d5d5SAlexander Hansen 		if (newarr == NULL) {
992325d5d5SAlexander Hansen 			return -1;
1002325d5d5SAlexander Hansen 		}
1012325d5d5SAlexander Hansen 		server->pollfds = newarr;
1022325d5d5SAlexander Hansen 
1032325d5d5SAlexander Hansen 		index = (ssize_t)server->capacity_pollfds;
1042325d5d5SAlexander Hansen 
1052325d5d5SAlexander Hansen 		server->capacity_pollfds = newcap;
1062325d5d5SAlexander Hansen 	}
1072325d5d5SAlexander Hansen 
1082325d5d5SAlexander Hansen 	pollfd = &server->pollfds[index];
1092325d5d5SAlexander Hansen 	pollfd->fd = fd;
1102325d5d5SAlexander Hansen 	pollfd->events = events;
1112325d5d5SAlexander Hansen 	pollfd->revents = 0;
1122325d5d5SAlexander Hansen 
1132325d5d5SAlexander Hansen 	return index;
1142325d5d5SAlexander Hansen }
1152325d5d5SAlexander Hansen 
console_server_release_pollfd(struct console_server * server,size_t pollfd_index)1162325d5d5SAlexander Hansen int console_server_release_pollfd(struct console_server *server,
1172325d5d5SAlexander Hansen 				  size_t pollfd_index)
1182325d5d5SAlexander Hansen {
1192325d5d5SAlexander Hansen 	if (pollfd_index >= server->capacity_pollfds) {
1202325d5d5SAlexander Hansen 		return -1;
1212325d5d5SAlexander Hansen 	}
1222325d5d5SAlexander Hansen 
1232325d5d5SAlexander Hansen 	struct pollfd *pfd = &server->pollfds[pollfd_index];
1242325d5d5SAlexander Hansen 
1252325d5d5SAlexander Hansen 	// mark pollfd as reclaimable
1262325d5d5SAlexander Hansen 
1272325d5d5SAlexander Hansen 	// ignore this file descriptor when calling 'poll'
1282325d5d5SAlexander Hansen 	// https://www.man7.org/linux/man-pages/man2/poll.2.html
1292325d5d5SAlexander Hansen 	pfd->fd = -1;
1302325d5d5SAlexander Hansen 	pfd->events = 0;
1312325d5d5SAlexander Hansen 	pfd->revents = ~0;
1322325d5d5SAlexander Hansen 
1332325d5d5SAlexander Hansen 	return 0;
1342325d5d5SAlexander Hansen }
1352325d5d5SAlexander Hansen 
136c2b0d8c7SAlexander Hansen /* populates server->tty.dev and server->tty.sysfs_devnode, using the tty kernel name */
tty_find_device(struct console_server * server)137c2b0d8c7SAlexander Hansen static int tty_find_device(struct console_server *server)
13817217845SJeremy Kerr {
139d3cb9c22SAndrew Jeffery 	char *tty_class_device_link = NULL;
140d3cb9c22SAndrew Jeffery 	char *tty_path_input_real = NULL;
141d3cb9c22SAndrew Jeffery 	char *tty_device_tty_dir = NULL;
14230ea6385SAndrew Jeffery 	char *tty_vuart_lpc_addr = NULL;
143d3cb9c22SAndrew Jeffery 	char *tty_device_reldir = NULL;
14430ea6385SAndrew Jeffery 	char *tty_sysfs_devnode = NULL;
145d3cb9c22SAndrew Jeffery 	char *tty_kname_real = NULL;
14630ea6385SAndrew Jeffery 	char *tty_path_input = NULL;
14717217845SJeremy Kerr 	int rc;
14817217845SJeremy Kerr 
149c2b0d8c7SAlexander Hansen 	server->tty.type = TTY_DEVICE_UNDEFINED;
15030ea6385SAndrew Jeffery 
151c2b0d8c7SAlexander Hansen 	assert(server->tty.kname);
152c2b0d8c7SAlexander Hansen 	if (!strlen(server->tty.kname)) {
15330ea6385SAndrew Jeffery 		warnx("TTY kname must not be empty");
15430ea6385SAndrew Jeffery 		rc = -1;
15530ea6385SAndrew Jeffery 		goto out_free;
1562834c5b1SAndrew Jeffery 	}
15717217845SJeremy Kerr 
158c2b0d8c7SAlexander Hansen 	if (server->tty.kname[0] == '/') {
159c2b0d8c7SAlexander Hansen 		tty_path_input = strdup(server->tty.kname);
16030ea6385SAndrew Jeffery 		if (!tty_path_input) {
16130ea6385SAndrew Jeffery 			rc = -1;
16230ea6385SAndrew Jeffery 			goto out_free;
16330ea6385SAndrew Jeffery 		}
16430ea6385SAndrew Jeffery 	} else {
165c2b0d8c7SAlexander Hansen 		rc = asprintf(&tty_path_input, "/dev/%s", server->tty.kname);
16630ea6385SAndrew Jeffery 		if (rc < 0) {
16730ea6385SAndrew Jeffery 			goto out_free;
16830ea6385SAndrew Jeffery 		}
16930ea6385SAndrew Jeffery 	}
17030ea6385SAndrew Jeffery 
17130ea6385SAndrew Jeffery 	/* udev may rename the tty name with a symbol link, try to resolve */
17245ad7676SYi Li 	tty_path_input_real = realpath(tty_path_input, NULL);
17345ad7676SYi Li 	if (!tty_path_input_real) {
17430ea6385SAndrew Jeffery 		warn("Can't find realpath for %s", tty_path_input);
17515792aa7SAndrew Jeffery 		rc = -1;
17645ad7676SYi Li 		goto out_free;
17745ad7676SYi Li 	}
17845ad7676SYi Li 
17930ea6385SAndrew Jeffery 	/*
18030ea6385SAndrew Jeffery 	 * Allow hooking obmc-console-server up to PTYs for testing
18130ea6385SAndrew Jeffery 	 *
18230ea6385SAndrew Jeffery 	 * https://amboar.github.io/notes/2023/05/02/testing-obmc-console-with-socat.html
18330ea6385SAndrew Jeffery 	 */
18430ea6385SAndrew Jeffery 	if (!strncmp(DEV_PTS_PATH, tty_path_input_real, strlen(DEV_PTS_PATH))) {
185c2b0d8c7SAlexander Hansen 		server->tty.type = TTY_DEVICE_PTY;
186c2b0d8c7SAlexander Hansen 		server->tty.dev = strdup(server->tty.kname);
187c2b0d8c7SAlexander Hansen 		rc = server->tty.dev ? 0 : -1;
18830ea6385SAndrew Jeffery 		goto out_free;
18930ea6385SAndrew Jeffery 	}
19030ea6385SAndrew Jeffery 
19145ad7676SYi Li 	tty_kname_real = basename(tty_path_input_real);
19245ad7676SYi Li 	if (!tty_kname_real) {
193c2b0d8c7SAlexander Hansen 		warn("Can't find real name for %s", server->tty.kname);
19415792aa7SAndrew Jeffery 		rc = -1;
19545ad7676SYi Li 		goto out_free;
19645ad7676SYi Li 	}
19745ad7676SYi Li 
198a72711afSAndrew Jeffery 	rc = asprintf(&tty_class_device_link, "/sys/class/tty/%s",
199a72711afSAndrew Jeffery 		      tty_kname_real);
2002834c5b1SAndrew Jeffery 	if (rc < 0) {
20145ad7676SYi Li 		goto out_free;
2022834c5b1SAndrew Jeffery 	}
20345ad7676SYi Li 
20417217845SJeremy Kerr 	tty_device_tty_dir = realpath(tty_class_device_link, NULL);
20545ad7676SYi Li 	if (!tty_device_tty_dir) {
20645ad7676SYi Li 		warn("Can't query sysfs for device %s", tty_kname_real);
20715792aa7SAndrew Jeffery 		rc = -1;
20817217845SJeremy Kerr 		goto out_free;
20917217845SJeremy Kerr 	}
21017217845SJeremy Kerr 
21117217845SJeremy Kerr 	rc = asprintf(&tty_device_reldir, "%s/../../", tty_device_tty_dir);
2122834c5b1SAndrew Jeffery 	if (rc < 0) {
21317217845SJeremy Kerr 		goto out_free;
2142834c5b1SAndrew Jeffery 	}
21517217845SJeremy Kerr 
21630ea6385SAndrew Jeffery 	tty_sysfs_devnode = realpath(tty_device_reldir, NULL);
21730ea6385SAndrew Jeffery 	if (!tty_sysfs_devnode) {
21845ad7676SYi Li 		warn("Can't find parent device for %s", tty_kname_real);
2192834c5b1SAndrew Jeffery 	}
22017217845SJeremy Kerr 
221c2b0d8c7SAlexander Hansen 	rc = asprintf(&server->tty.dev, "/dev/%s", tty_kname_real);
2222834c5b1SAndrew Jeffery 	if (rc < 0) {
22317217845SJeremy Kerr 		goto out_free;
2242834c5b1SAndrew Jeffery 	}
22517217845SJeremy Kerr 
226955d140eSOskar Senft 	// Default to non-VUART
227c2b0d8c7SAlexander Hansen 	server->tty.type = TTY_DEVICE_UART;
228955d140eSOskar Senft 
22930ea6385SAndrew Jeffery 	/* Arbitrarily pick an attribute to differentiate UART vs VUART */
230955d140eSOskar Senft 	if (tty_sysfs_devnode) {
231955d140eSOskar Senft 		rc = asprintf(&tty_vuart_lpc_addr, "%s/lpc_address",
232955d140eSOskar Senft 			      tty_sysfs_devnode);
23330ea6385SAndrew Jeffery 		if (rc < 0) {
23430ea6385SAndrew Jeffery 			goto out_free;
23530ea6385SAndrew Jeffery 		}
23630ea6385SAndrew Jeffery 
23730ea6385SAndrew Jeffery 		rc = access(tty_vuart_lpc_addr, F_OK);
238955d140eSOskar Senft 		if (!rc) {
239c2b0d8c7SAlexander Hansen 			server->tty.type = TTY_DEVICE_VUART;
240c2b0d8c7SAlexander Hansen 			server->tty.vuart.sysfs_devnode =
241955d140eSOskar Senft 				strdup(tty_sysfs_devnode);
242955d140eSOskar Senft 		}
243955d140eSOskar Senft 	}
24430ea6385SAndrew Jeffery 
24517217845SJeremy Kerr 	rc = 0;
24617217845SJeremy Kerr 
24717217845SJeremy Kerr out_free:
24830ea6385SAndrew Jeffery 	free(tty_vuart_lpc_addr);
24917217845SJeremy Kerr 	free(tty_class_device_link);
250982090d9SAndrew Jeffery 	free(tty_sysfs_devnode);
25117217845SJeremy Kerr 	free(tty_device_tty_dir);
25217217845SJeremy Kerr 	free(tty_device_reldir);
25345ad7676SYi Li 	free(tty_path_input);
25445ad7676SYi Li 	free(tty_path_input_real);
25517217845SJeremy Kerr 	return rc;
25617217845SJeremy Kerr }
25717217845SJeremy Kerr 
tty_set_sysfs_attr(struct console_server * server,const char * name,int value)258c2b0d8c7SAlexander Hansen static int tty_set_sysfs_attr(struct console_server *server, const char *name,
259957818b4SJeremy Kerr 			      int value)
260957818b4SJeremy Kerr {
261957818b4SJeremy Kerr 	char *path;
262957818b4SJeremy Kerr 	FILE *fp;
263957818b4SJeremy Kerr 	int rc;
264957818b4SJeremy Kerr 
265c2b0d8c7SAlexander Hansen 	assert(server->tty.type == TTY_DEVICE_VUART);
26630ea6385SAndrew Jeffery 
267c2b0d8c7SAlexander Hansen 	if (!server->tty.vuart.sysfs_devnode) {
26830ea6385SAndrew Jeffery 		return -1;
26930ea6385SAndrew Jeffery 	}
27030ea6385SAndrew Jeffery 
271c2b0d8c7SAlexander Hansen 	rc = asprintf(&path, "%s/%s", server->tty.vuart.sysfs_devnode, name);
2722834c5b1SAndrew Jeffery 	if (rc < 0) {
273957818b4SJeremy Kerr 		return -1;
2742834c5b1SAndrew Jeffery 	}
275957818b4SJeremy Kerr 
276957818b4SJeremy Kerr 	fp = fopen(path, "w");
277957818b4SJeremy Kerr 	if (!fp) {
278a72711afSAndrew Jeffery 		warn("Can't access attribute %s on device %s", name,
279c2b0d8c7SAlexander Hansen 		     server->tty.kname);
280957818b4SJeremy Kerr 		rc = -1;
281957818b4SJeremy Kerr 		goto out_free;
282957818b4SJeremy Kerr 	}
283957818b4SJeremy Kerr 	setvbuf(fp, NULL, _IONBF, 0);
284957818b4SJeremy Kerr 
285957818b4SJeremy Kerr 	rc = fprintf(fp, "0x%x", value);
2862834c5b1SAndrew Jeffery 	if (rc < 0) {
287a72711afSAndrew Jeffery 		warn("Error writing to %s attribute of device %s", name,
288c2b0d8c7SAlexander Hansen 		     server->tty.kname);
2892834c5b1SAndrew Jeffery 	}
290957818b4SJeremy Kerr 	fclose(fp);
291957818b4SJeremy Kerr 
292957818b4SJeremy Kerr out_free:
293957818b4SJeremy Kerr 	free(path);
294957818b4SJeremy Kerr 	return rc;
295957818b4SJeremy Kerr }
296957818b4SJeremy Kerr 
297d831f960SJeremy Kerr /**
298c7fbcd48SBenjamin Fair  * Set termios attributes on the console tty.
29954e9569dSJeremy Kerr  */
tty_init_termios(struct console_server * server)300c2b0d8c7SAlexander Hansen void tty_init_termios(struct console_server *server)
30154e9569dSJeremy Kerr {
30254e9569dSJeremy Kerr 	struct termios termios;
30354e9569dSJeremy Kerr 	int rc;
30454e9569dSJeremy Kerr 
305c2b0d8c7SAlexander Hansen 	rc = tcgetattr(server->tty.fd, &termios);
30654e9569dSJeremy Kerr 	if (rc) {
30754e9569dSJeremy Kerr 		warn("Can't read tty termios");
30854e9569dSJeremy Kerr 		return;
30954e9569dSJeremy Kerr 	}
31054e9569dSJeremy Kerr 
311c2b0d8c7SAlexander Hansen 	if (server->tty.type == TTY_DEVICE_UART && server->tty.uart.baud) {
312c2b0d8c7SAlexander Hansen 		if (cfsetspeed(&termios, server->tty.uart.baud) < 0) {
313c2b0d8c7SAlexander Hansen 			warn("Couldn't set speeds for %s", server->tty.kname);
314c7fbcd48SBenjamin Fair 		}
3152834c5b1SAndrew Jeffery 	}
316c7fbcd48SBenjamin Fair 
317c7fbcd48SBenjamin Fair 	/* Set console to raw mode: we don't want any processing to occur on
318c7fbcd48SBenjamin Fair 	 * the underlying terminal input/output.
319c7fbcd48SBenjamin Fair 	 */
32054e9569dSJeremy Kerr 	cfmakeraw(&termios);
321c7fbcd48SBenjamin Fair 
322c2b0d8c7SAlexander Hansen 	rc = tcsetattr(server->tty.fd, TCSANOW, &termios);
3232834c5b1SAndrew Jeffery 	if (rc) {
324c2b0d8c7SAlexander Hansen 		warn("Can't set terminal options for %s", server->tty.kname);
32554e9569dSJeremy Kerr 	}
3262834c5b1SAndrew Jeffery }
32754e9569dSJeremy Kerr 
32854e9569dSJeremy Kerr /**
329d831f960SJeremy Kerr  * Open and initialise the serial device
330d831f960SJeremy Kerr  */
tty_init_vuart_io(struct console_server * server)331c2b0d8c7SAlexander Hansen static void tty_init_vuart_io(struct console_server *server)
332d831f960SJeremy Kerr {
333c2b0d8c7SAlexander Hansen 	assert(server->tty.type == TTY_DEVICE_VUART);
33430ea6385SAndrew Jeffery 
335c2b0d8c7SAlexander Hansen 	if (server->tty.vuart.sirq) {
336c2b0d8c7SAlexander Hansen 		tty_set_sysfs_attr(server, "sirq", server->tty.vuart.sirq);
3372834c5b1SAndrew Jeffery 	}
338957818b4SJeremy Kerr 
339c2b0d8c7SAlexander Hansen 	if (server->tty.vuart.lpc_addr) {
340c2b0d8c7SAlexander Hansen 		tty_set_sysfs_attr(server, "lpc_address",
341c2b0d8c7SAlexander Hansen 				   server->tty.vuart.lpc_addr);
34230ea6385SAndrew Jeffery 	}
34330ea6385SAndrew Jeffery }
34430ea6385SAndrew Jeffery 
tty_init_io(struct console_server * server)345c2b0d8c7SAlexander Hansen static int tty_init_io(struct console_server *server)
34630ea6385SAndrew Jeffery {
347c2b0d8c7SAlexander Hansen 	server->tty.fd = open(server->tty.dev, O_RDWR);
348c2b0d8c7SAlexander Hansen 	if (server->tty.fd <= 0) {
349c2b0d8c7SAlexander Hansen 		warn("Can't open tty %s", server->tty.dev);
350d831f960SJeremy Kerr 		return -1;
351d831f960SJeremy Kerr 	}
352d831f960SJeremy Kerr 
353d831f960SJeremy Kerr 	/* Disable character delay. We may want to later enable this when
354d831f960SJeremy Kerr 	 * we detect larger amounts of data
355d831f960SJeremy Kerr 	 */
356c2b0d8c7SAlexander Hansen 	fcntl(server->tty.fd, F_SETFL, FNDELAY);
357d831f960SJeremy Kerr 
358c2b0d8c7SAlexander Hansen 	tty_init_termios(server);
35954e9569dSJeremy Kerr 
3602325d5d5SAlexander Hansen 	ssize_t index =
3612325d5d5SAlexander Hansen 		console_server_request_pollfd(server, server->tty.fd, POLLIN);
3622325d5d5SAlexander Hansen 
3632325d5d5SAlexander Hansen 	if (index < 0) {
3642325d5d5SAlexander Hansen 		return -1;
3652325d5d5SAlexander Hansen 	}
3662325d5d5SAlexander Hansen 
3672325d5d5SAlexander Hansen 	server->tty_pollfd_index = (size_t)index;
368329a35f5SJeremy Kerr 
369d831f960SJeremy Kerr 	return 0;
370d831f960SJeremy Kerr }
371d831f960SJeremy Kerr 
tty_init_vuart(struct console_server * server,struct config * config)372c2b0d8c7SAlexander Hansen static int tty_init_vuart(struct console_server *server, struct config *config)
373d66195c1SJeremy Kerr {
374fd883a88SAndrew Jeffery 	unsigned long parsed;
375d66195c1SJeremy Kerr 	const char *val;
376d66195c1SJeremy Kerr 	char *endp;
37730ea6385SAndrew Jeffery 
378c2b0d8c7SAlexander Hansen 	assert(server->tty.type == TTY_DEVICE_VUART);
379d66195c1SJeremy Kerr 
380d66195c1SJeremy Kerr 	val = config_get_value(config, "lpc-address");
381d66195c1SJeremy Kerr 	if (val) {
382fd883a88SAndrew Jeffery 		errno = 0;
383fd883a88SAndrew Jeffery 		parsed = strtoul(val, &endp, 0);
384fd883a88SAndrew Jeffery 		if (parsed == ULONG_MAX && errno == ERANGE) {
385fd883a88SAndrew Jeffery 			warn("Cannot interpret 'lpc-address' value as an unsigned long: '%s'",
386fd883a88SAndrew Jeffery 			     val);
387fd883a88SAndrew Jeffery 			return -1;
388fd883a88SAndrew Jeffery 		}
389fd883a88SAndrew Jeffery 
390fd883a88SAndrew Jeffery 		if (parsed > UINT16_MAX) {
391fd883a88SAndrew Jeffery 			warn("Invalid LPC address '%s'", val);
392fd883a88SAndrew Jeffery 			return -1;
393fd883a88SAndrew Jeffery 		}
394fd883a88SAndrew Jeffery 
395c2b0d8c7SAlexander Hansen 		server->tty.vuart.lpc_addr = (uint16_t)parsed;
396d66195c1SJeremy Kerr 		if (endp == optarg) {
397d66195c1SJeremy Kerr 			warn("Invalid LPC address: '%s'", val);
398d66195c1SJeremy Kerr 			return -1;
399d66195c1SJeremy Kerr 		}
400d66195c1SJeremy Kerr 	}
401d66195c1SJeremy Kerr 
402d66195c1SJeremy Kerr 	val = config_get_value(config, "sirq");
403d66195c1SJeremy Kerr 	if (val) {
404fd883a88SAndrew Jeffery 		errno = 0;
405fd883a88SAndrew Jeffery 		parsed = strtoul(val, &endp, 0);
406fd883a88SAndrew Jeffery 		if (parsed == ULONG_MAX && errno == ERANGE) {
407fd883a88SAndrew Jeffery 			warn("Cannot interpret 'sirq' value as an unsigned long: '%s'",
408fd883a88SAndrew Jeffery 			     val);
409fd883a88SAndrew Jeffery 		}
410fd883a88SAndrew Jeffery 
4112834c5b1SAndrew Jeffery 		if (parsed > 16) {
412fd883a88SAndrew Jeffery 			warn("Invalid LPC SERIRQ: '%s'", val);
4132834c5b1SAndrew Jeffery 		}
414fd883a88SAndrew Jeffery 
415c2b0d8c7SAlexander Hansen 		server->tty.vuart.sirq = (int)parsed;
4162834c5b1SAndrew Jeffery 		if (endp == optarg) {
417d66195c1SJeremy Kerr 			warn("Invalid sirq: '%s'", val);
418d66195c1SJeremy Kerr 		}
4192834c5b1SAndrew Jeffery 	}
420d66195c1SJeremy Kerr 
42130ea6385SAndrew Jeffery 	return 0;
4222834c5b1SAndrew Jeffery }
423c7fbcd48SBenjamin Fair 
tty_init(struct console_server * server,struct config * config,const char * tty_arg)424c2b0d8c7SAlexander Hansen static int tty_init(struct console_server *server, struct config *config,
42530ea6385SAndrew Jeffery 		    const char *tty_arg)
42630ea6385SAndrew Jeffery {
42730ea6385SAndrew Jeffery 	const char *val;
42830ea6385SAndrew Jeffery 	int rc;
42930ea6385SAndrew Jeffery 
430d769eecfSAndrew Jeffery 	if (tty_arg) {
431c2b0d8c7SAlexander Hansen 		server->tty.kname = tty_arg;
432d769eecfSAndrew Jeffery 	} else if ((val = config_get_value(config, "upstream-tty"))) {
433c2b0d8c7SAlexander Hansen 		server->tty.kname = val;
434d769eecfSAndrew Jeffery 	} else {
435d66195c1SJeremy Kerr 		warnx("Error: No TTY device specified");
436d66195c1SJeremy Kerr 		return -1;
437d66195c1SJeremy Kerr 	}
438d66195c1SJeremy Kerr 
439c2b0d8c7SAlexander Hansen 	rc = tty_find_device(server);
4402834c5b1SAndrew Jeffery 	if (rc) {
441d66195c1SJeremy Kerr 		return rc;
4422834c5b1SAndrew Jeffery 	}
443d66195c1SJeremy Kerr 
444c2b0d8c7SAlexander Hansen 	switch (server->tty.type) {
44530ea6385SAndrew Jeffery 	case TTY_DEVICE_VUART:
446c2b0d8c7SAlexander Hansen 		rc = tty_init_vuart(server, config);
44730ea6385SAndrew Jeffery 		if (rc) {
448d66195c1SJeremy Kerr 			return rc;
449d66195c1SJeremy Kerr 		}
450d66195c1SJeremy Kerr 
451c2b0d8c7SAlexander Hansen 		tty_init_vuart_io(server);
45230ea6385SAndrew Jeffery 		break;
45330ea6385SAndrew Jeffery 	case TTY_DEVICE_UART:
45430ea6385SAndrew Jeffery 		val = config_get_value(config, "baud");
45530ea6385SAndrew Jeffery 		if (val) {
456c2b0d8c7SAlexander Hansen 			if (config_parse_baud(&server->tty.uart.baud, val)) {
45730ea6385SAndrew Jeffery 				warnx("Invalid baud rate: '%s'", val);
45830ea6385SAndrew Jeffery 			}
45930ea6385SAndrew Jeffery 		}
46030ea6385SAndrew Jeffery 		break;
46130ea6385SAndrew Jeffery 	case TTY_DEVICE_PTY:
46230ea6385SAndrew Jeffery 		break;
46330ea6385SAndrew Jeffery 	case TTY_DEVICE_UNDEFINED:
46430ea6385SAndrew Jeffery 	default:
46530ea6385SAndrew Jeffery 		warnx("Cannot configure unrecognised TTY device");
46630ea6385SAndrew Jeffery 		return -1;
46730ea6385SAndrew Jeffery 	}
46830ea6385SAndrew Jeffery 
469c2b0d8c7SAlexander Hansen 	return tty_init_io(server);
47030ea6385SAndrew Jeffery }
47130ea6385SAndrew Jeffery 
tty_fini(struct console_server * server)472c2b0d8c7SAlexander Hansen static void tty_fini(struct console_server *server)
47330ea6385SAndrew Jeffery {
4742325d5d5SAlexander Hansen 	if (server->tty_pollfd_index < server->capacity_pollfds) {
4752325d5d5SAlexander Hansen 		console_server_release_pollfd(server, server->tty_pollfd_index);
4762325d5d5SAlexander Hansen 		server->tty_pollfd_index = SIZE_MAX;
4772325d5d5SAlexander Hansen 	}
4782325d5d5SAlexander Hansen 
479c2b0d8c7SAlexander Hansen 	if (server->tty.type == TTY_DEVICE_VUART) {
480c2b0d8c7SAlexander Hansen 		free(server->tty.vuart.sysfs_devnode);
48130ea6385SAndrew Jeffery 	}
4822325d5d5SAlexander Hansen 
483c2b0d8c7SAlexander Hansen 	free(server->tty.dev);
48430ea6385SAndrew Jeffery }
48530ea6385SAndrew Jeffery 
write_to_path(const char * path,const char * data)4867dc08baaSZev Weiss static int write_to_path(const char *path, const char *data)
4877dc08baaSZev Weiss {
4887dc08baaSZev Weiss 	int rc = 0;
4897dc08baaSZev Weiss 	FILE *f = fopen(path, "w");
4907dc08baaSZev Weiss 	if (!f) {
4917dc08baaSZev Weiss 		return -1;
4927dc08baaSZev Weiss 	}
4937dc08baaSZev Weiss 
4947dc08baaSZev Weiss 	if (fprintf(f, "%s", data) < 0) {
4957dc08baaSZev Weiss 		rc = -1;
4967dc08baaSZev Weiss 	}
4977dc08baaSZev Weiss 
4987dc08baaSZev Weiss 	if (fclose(f)) {
4997dc08baaSZev Weiss 		rc = -1;
5007dc08baaSZev Weiss 	}
5017dc08baaSZev Weiss 
5027dc08baaSZev Weiss 	return rc;
5037dc08baaSZev Weiss }
5047dc08baaSZev Weiss 
5057dc08baaSZev Weiss #define ASPEED_UART_ROUTING_PATTERN                                            \
5067dc08baaSZev Weiss 	"/sys/bus/platform/drivers/aspeed-uart-routing/*.uart-routing"
5077dc08baaSZev Weiss 
uart_routing_init(struct config * config)5087dc08baaSZev Weiss static void uart_routing_init(struct config *config)
5097dc08baaSZev Weiss {
5107dc08baaSZev Weiss 	const char *muxcfg;
5117dc08baaSZev Weiss 	const char *p;
5127dc08baaSZev Weiss 	size_t buflen;
5137dc08baaSZev Weiss 	char *sink;
5147dc08baaSZev Weiss 	char *source;
5157dc08baaSZev Weiss 	char *muxdir;
5167dc08baaSZev Weiss 	char *path;
5177dc08baaSZev Weiss 	glob_t globbuf;
5187dc08baaSZev Weiss 
5197dc08baaSZev Weiss 	muxcfg = config_get_value(config, "aspeed-uart-routing");
5207dc08baaSZev Weiss 	if (!muxcfg) {
5217dc08baaSZev Weiss 		return;
5227dc08baaSZev Weiss 	}
5237dc08baaSZev Weiss 
5247dc08baaSZev Weiss 	/* Find the driver's sysfs directory */
5257dc08baaSZev Weiss 	if (glob(ASPEED_UART_ROUTING_PATTERN, GLOB_ERR | GLOB_NOSORT, NULL,
5267dc08baaSZev Weiss 		 &globbuf) != 0) {
5277dc08baaSZev Weiss 		warn("Couldn't find uart-routing driver directory, cannot apply config");
5287dc08baaSZev Weiss 		return;
5297dc08baaSZev Weiss 	}
5307dc08baaSZev Weiss 	if (globbuf.gl_pathc != 1) {
5317dc08baaSZev Weiss 		warnx("Found %zd uart-routing driver directories, cannot apply config",
5327dc08baaSZev Weiss 		      globbuf.gl_pathc);
5337dc08baaSZev Weiss 		goto out_free_glob;
5347dc08baaSZev Weiss 	}
5357dc08baaSZev Weiss 	muxdir = globbuf.gl_pathv[0];
5367dc08baaSZev Weiss 
5377dc08baaSZev Weiss 	/*
5387dc08baaSZev Weiss 	 * Rather than faff about tracking a bunch of separate buffer sizes,
5397dc08baaSZev Weiss 	 * just use one (worst-case) size for all of them -- +2 for a trailing
5407dc08baaSZev Weiss 	 * NUL and a '/' separator to construct the sysfs file path.
5417dc08baaSZev Weiss 	 */
5427dc08baaSZev Weiss 	buflen = strlen(muxdir) + strlen(muxcfg) + 2;
5437dc08baaSZev Weiss 
5447dc08baaSZev Weiss 	sink = malloc(buflen);
5457dc08baaSZev Weiss 	source = malloc(buflen);
5467dc08baaSZev Weiss 	path = malloc(buflen);
5477dc08baaSZev Weiss 	if (!path || !sink || !source) {
5487dc08baaSZev Weiss 		warnx("Out of memory applying uart routing config");
5497dc08baaSZev Weiss 		goto out_free_bufs;
5507dc08baaSZev Weiss 	}
5517dc08baaSZev Weiss 
5527dc08baaSZev Weiss 	p = muxcfg;
5537dc08baaSZev Weiss 	while (*p) {
5547dc08baaSZev Weiss 		ssize_t bytes_scanned;
5557dc08baaSZev Weiss 
5567dc08baaSZev Weiss 		if (sscanf(p, " %[^:/ \t]:%[^: \t] %zn", sink, source,
5577dc08baaSZev Weiss 			   &bytes_scanned) != 2) {
5587dc08baaSZev Weiss 			warnx("Invalid syntax in aspeed uart config: '%s' not applied",
5597dc08baaSZev Weiss 			      p);
5607dc08baaSZev Weiss 			break;
5617dc08baaSZev Weiss 		}
5627dc08baaSZev Weiss 		p += bytes_scanned;
5637dc08baaSZev Weiss 
5647dc08baaSZev Weiss 		/*
5657dc08baaSZev Weiss 		 * Check that the sink name looks reasonable before proceeding
5667dc08baaSZev Weiss 		 * (there are other writable files in the same directory that
5677dc08baaSZev Weiss 		 * we shouldn't be touching, such as 'driver_override' and
5687dc08baaSZev Weiss 		 * 'uevent').
5697dc08baaSZev Weiss 		 */
5707dc08baaSZev Weiss 		if (strncmp(sink, "io", strlen("io")) != 0 &&
5717dc08baaSZev Weiss 		    strncmp(sink, "uart", strlen("uart")) != 0) {
5727dc08baaSZev Weiss 			warnx("Skipping invalid uart routing name '%s' (must be ioN or uartN)",
5737dc08baaSZev Weiss 			      sink);
5747dc08baaSZev Weiss 			continue;
5757dc08baaSZev Weiss 		}
5767dc08baaSZev Weiss 
5777dc08baaSZev Weiss 		snprintf(path, buflen, "%s/%s", muxdir, sink);
5787dc08baaSZev Weiss 		if (write_to_path(path, source)) {
5797dc08baaSZev Weiss 			warn("Failed to apply uart-routing config '%s:%s'",
5807dc08baaSZev Weiss 			     sink, source);
5817dc08baaSZev Weiss 		}
5827dc08baaSZev Weiss 	}
5837dc08baaSZev Weiss 
5847dc08baaSZev Weiss out_free_bufs:
5857dc08baaSZev Weiss 	free(path);
5867dc08baaSZev Weiss 	free(source);
5877dc08baaSZev Weiss 	free(sink);
5887dc08baaSZev Weiss out_free_glob:
5897dc08baaSZev Weiss 	globfree(&globbuf);
5907dc08baaSZev Weiss }
5917dc08baaSZev Weiss 
console_data_out(struct console * console,const uint8_t * data,size_t len)5921a0e03b4SJeremy Kerr int console_data_out(struct console *console, const uint8_t *data, size_t len)
593d831f960SJeremy Kerr {
594c2b0d8c7SAlexander Hansen 	return write_buf_to_fd(console->server->tty.fd, data, len);
595d831f960SJeremy Kerr }
596d831f960SJeremy Kerr 
5975ba20b5bSNinad Palsule /* Prepare a socket name */
set_socket_info(struct console * console,struct config * config,const char * console_id)598954be0fbSAndrew Jeffery static int set_socket_info(struct console *console, struct config *config,
599954be0fbSAndrew Jeffery 			   const char *console_id)
600b14ca19cSNinad Palsule {
601b14ca19cSNinad Palsule 	ssize_t len;
602b14ca19cSNinad Palsule 
6035ba20b5bSNinad Palsule 	/* Get console id */
6045ba20b5bSNinad Palsule 	console->console_id = config_resolve_console_id(config, console_id);
605954be0fbSAndrew Jeffery 
606b14ca19cSNinad Palsule 	/* Get the socket name/path */
6075ba20b5bSNinad Palsule 	len = console_socket_path(console->socket_name, console->console_id);
608b14ca19cSNinad Palsule 	if (len < 0) {
609b14ca19cSNinad Palsule 		warn("Failed to set socket path: %s", strerror(errno));
610b14ca19cSNinad Palsule 		return EXIT_FAILURE;
611b14ca19cSNinad Palsule 	}
612b14ca19cSNinad Palsule 
613b14ca19cSNinad Palsule 	/* Socket name is not a null terminated string hence save the length */
614b14ca19cSNinad Palsule 	console->socket_name_len = len;
615b14ca19cSNinad Palsule 
616b14ca19cSNinad Palsule 	return 0;
617b14ca19cSNinad Palsule }
618b14ca19cSNinad Palsule 
handlers_init(struct console * console,struct config * config)619d47963e5SJeremy Kerr static void handlers_init(struct console *console, struct config *config)
620d831f960SJeremy Kerr {
621b70f8713SAndrew Jeffery 	/* NOLINTBEGIN(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) */
622adedc333SAndrew Jeffery 	extern const struct handler_type *const __start_handlers[];
623adedc333SAndrew Jeffery 	extern const struct handler_type *const __stop_handlers[];
624b70f8713SAndrew Jeffery 	/* NOLINTEND(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) */
625e2826c7dSJeremy Kerr 	size_t n_types;
626e2826c7dSJeremy Kerr 	int j = 0;
627e2826c7dSJeremy Kerr 	size_t i;
628e2826c7dSJeremy Kerr 
629adedc333SAndrew Jeffery 	n_types = __stop_handlers - __start_handlers;
630e2826c7dSJeremy Kerr 	console->handlers = calloc(n_types, sizeof(struct handler *));
631e2826c7dSJeremy Kerr 	if (!console->handlers) {
632e2826c7dSJeremy Kerr 		err(EXIT_FAILURE, "malloc(handlers)");
633e2826c7dSJeremy Kerr 	}
634e2826c7dSJeremy Kerr 
635079fc516SAndrew Jeffery 	printf("%zu handler type%s\n", n_types, n_types == 1 ? "" : "s");
636e2826c7dSJeremy Kerr 
637e2826c7dSJeremy Kerr 	for (i = 0; i < n_types; i++) {
638adedc333SAndrew Jeffery 		const struct handler_type *type = __start_handlers[i];
6391a0e03b4SJeremy Kerr 		struct handler *handler;
640d831f960SJeremy Kerr 
641e2826c7dSJeremy Kerr 		/* Should be picked up at build time by
642e2826c7dSJeremy Kerr 		 * console_handler_register, but check anyway
643e2826c7dSJeremy Kerr 		 */
644e2826c7dSJeremy Kerr 		if (!type->init || !type->fini) {
645e2826c7dSJeremy Kerr 			errx(EXIT_FAILURE,
646e2826c7dSJeremy Kerr 			     "invalid handler type %s: no init() / fini()",
647e2826c7dSJeremy Kerr 			     type->name);
6482834c5b1SAndrew Jeffery 		}
649021b91f0SJeremy Kerr 
650e2826c7dSJeremy Kerr 		handler = type->init(type, console, config);
651021b91f0SJeremy Kerr 
652e2826c7dSJeremy Kerr 		printf("  console '%s': handler %s [%sactive]\n",
653e2826c7dSJeremy Kerr 		       console->console_id, type->name, handler ? "" : "in");
654e2826c7dSJeremy Kerr 
655e2826c7dSJeremy Kerr 		if (handler) {
656e2826c7dSJeremy Kerr 			handler->type = type;
657e2826c7dSJeremy Kerr 			console->handlers[j++] = handler;
658d831f960SJeremy Kerr 		}
659d831f960SJeremy Kerr 	}
660d831f960SJeremy Kerr 
661e2826c7dSJeremy Kerr 	console->n_handlers = j;
662e2826c7dSJeremy Kerr }
663e2826c7dSJeremy Kerr 
handlers_fini(struct console * console)6641a0e03b4SJeremy Kerr static void handlers_fini(struct console *console)
665d831f960SJeremy Kerr {
6661a0e03b4SJeremy Kerr 	struct handler *handler;
6671a0e03b4SJeremy Kerr 	int i;
6681a0e03b4SJeremy Kerr 
6691a0e03b4SJeremy Kerr 	for (i = 0; i < console->n_handlers; i++) {
6701a0e03b4SJeremy Kerr 		handler = console->handlers[i];
671e2826c7dSJeremy Kerr 		handler->type->fini(handler);
6721a0e03b4SJeremy Kerr 	}
673e2826c7dSJeremy Kerr 
674e2826c7dSJeremy Kerr 	free(console->handlers);
675e2826c7dSJeremy Kerr 	console->handlers = NULL;
676e2826c7dSJeremy Kerr 	console->n_handlers = 0;
6772834c5b1SAndrew Jeffery }
678d831f960SJeremy Kerr 
get_current_time(struct timeval * tv)6791cecc5deSJohnathan Mantey static int get_current_time(struct timeval *tv)
6801cecc5deSJohnathan Mantey {
6811cecc5deSJohnathan Mantey 	struct timespec t;
6821cecc5deSJohnathan Mantey 	int rc;
6831cecc5deSJohnathan Mantey 
6841cecc5deSJohnathan Mantey 	/*
6851cecc5deSJohnathan Mantey 	 * We use clock_gettime(CLOCK_MONOTONIC) so we're immune to
6861cecc5deSJohnathan Mantey 	 * local time changes. However, a struct timeval is more
6871cecc5deSJohnathan Mantey 	 * convenient for calculations, so convert to that.
6881cecc5deSJohnathan Mantey 	 */
6891cecc5deSJohnathan Mantey 	rc = clock_gettime(CLOCK_MONOTONIC, &t);
6902834c5b1SAndrew Jeffery 	if (rc) {
6911cecc5deSJohnathan Mantey 		return rc;
6922834c5b1SAndrew Jeffery 	}
6931cecc5deSJohnathan Mantey 
6941cecc5deSJohnathan Mantey 	tv->tv_sec = t.tv_sec;
6951cecc5deSJohnathan Mantey 	tv->tv_usec = t.tv_nsec / 1000;
6961cecc5deSJohnathan Mantey 
6971cecc5deSJohnathan Mantey 	return 0;
6981cecc5deSJohnathan Mantey }
6991cecc5deSJohnathan Mantey 
700a72711afSAndrew Jeffery struct ringbuffer_consumer *
console_ringbuffer_consumer_register(struct console * console,ringbuffer_poll_fn_t poll_fn,void * data)701a72711afSAndrew Jeffery console_ringbuffer_consumer_register(struct console *console,
702f733c85aSJeremy Kerr 				     ringbuffer_poll_fn_t poll_fn, void *data)
703d831f960SJeremy Kerr {
704f733c85aSJeremy Kerr 	return ringbuffer_consumer_register(console->rb, poll_fn, data);
705d831f960SJeremy Kerr }
706d831f960SJeremy Kerr 
console_poller_register(struct console * console,struct handler * handler,poller_event_fn_t poller_fn,poller_timeout_fn_t timeout_fn,int fd,int events,void * data)70755c9712dSJeremy Kerr struct poller *console_poller_register(struct console *console,
708a72711afSAndrew Jeffery 				       struct handler *handler,
709a72711afSAndrew Jeffery 				       poller_event_fn_t poller_fn,
7101cecc5deSJohnathan Mantey 				       poller_timeout_fn_t timeout_fn, int fd,
7111cecc5deSJohnathan Mantey 				       int events, void *data)
712d831f960SJeremy Kerr {
713329a35f5SJeremy Kerr 	struct poller *poller;
7145c359cc6SAndrew Jeffery 	long n;
715329a35f5SJeremy Kerr 
7162325d5d5SAlexander Hansen 	const ssize_t index = console_server_request_pollfd(
7172325d5d5SAlexander Hansen 		console->server, fd, (short)(events & 0x7fff));
7182325d5d5SAlexander Hansen 	if (index < 0) {
7192325d5d5SAlexander Hansen 		fprintf(stderr, "Error requesting pollfd\n");
7202325d5d5SAlexander Hansen 		return NULL;
7212325d5d5SAlexander Hansen 	}
7222325d5d5SAlexander Hansen 
723329a35f5SJeremy Kerr 	poller = malloc(sizeof(*poller));
7242325d5d5SAlexander Hansen 	// TODO: check for error case of malloc here and release previously requested pollfd
725329a35f5SJeremy Kerr 	poller->remove = false;
726329a35f5SJeremy Kerr 	poller->handler = handler;
7271cecc5deSJohnathan Mantey 	poller->event_fn = poller_fn;
7281cecc5deSJohnathan Mantey 	poller->timeout_fn = timeout_fn;
7299cc8459aSAndrew Jeffery 	timerclear(&poller->timeout);
730329a35f5SJeremy Kerr 	poller->data = data;
7312325d5d5SAlexander Hansen 	poller->pollfd_index = index;
732329a35f5SJeremy Kerr 
733329a35f5SJeremy Kerr 	/* add one to our pollers array */
734329a35f5SJeremy Kerr 	n = console->n_pollers++;
73591b52175SAndrew Jeffery 	/*
73691b52175SAndrew Jeffery 	 * We're managing an array of pointers to aggregates, so don't warn about sizeof() on a
73791b52175SAndrew Jeffery 	 * pointer type.
73891b52175SAndrew Jeffery 	 */
73991b52175SAndrew Jeffery 	/* NOLINTBEGIN(bugprone-sizeof-expression) */
74091b52175SAndrew Jeffery 	console->pollers = reallocarray(console->pollers, console->n_pollers,
74191b52175SAndrew Jeffery 					sizeof(*console->pollers));
7422325d5d5SAlexander Hansen 	// TODO: check for the error case of reallocarray and release previously requested pollfd
74391b52175SAndrew Jeffery 	/* NOLINTEND(bugprone-sizeof-expression) */
744329a35f5SJeremy Kerr 
745329a35f5SJeremy Kerr 	console->pollers[n] = poller;
746329a35f5SJeremy Kerr 
747329a35f5SJeremy Kerr 	return poller;
748329a35f5SJeremy Kerr }
749329a35f5SJeremy Kerr 
console_poller_unregister(struct console * console,struct poller * poller)750a72711afSAndrew Jeffery void console_poller_unregister(struct console *console, struct poller *poller)
751329a35f5SJeremy Kerr {
752329a35f5SJeremy Kerr 	int i;
753329a35f5SJeremy Kerr 
754329a35f5SJeremy Kerr 	/* find the entry in our pollers array */
7552834c5b1SAndrew Jeffery 	for (i = 0; i < console->n_pollers; i++) {
7562834c5b1SAndrew Jeffery 		if (console->pollers[i] == poller) {
757329a35f5SJeremy Kerr 			break;
7582834c5b1SAndrew Jeffery 		}
7592834c5b1SAndrew Jeffery 	}
760329a35f5SJeremy Kerr 
761329a35f5SJeremy Kerr 	assert(i < console->n_pollers);
762329a35f5SJeremy Kerr 
763329a35f5SJeremy Kerr 	console->n_pollers--;
764329a35f5SJeremy Kerr 
76591b52175SAndrew Jeffery 	/*
76691b52175SAndrew Jeffery 	 * Remove the item from the pollers array...
76791b52175SAndrew Jeffery 	 *
76891b52175SAndrew Jeffery 	 * We're managing an array of pointers to aggregates, so don't warn about sizeof() on a
76991b52175SAndrew Jeffery 	 * pointer type.
77091b52175SAndrew Jeffery 	 */
77191b52175SAndrew Jeffery 	/* NOLINTBEGIN(bugprone-sizeof-expression) */
772329a35f5SJeremy Kerr 	memmove(&console->pollers[i], &console->pollers[i + 1],
773a72711afSAndrew Jeffery 		sizeof(*console->pollers) * (console->n_pollers - i));
774329a35f5SJeremy Kerr 
7757851a396SAndrew Jeffery 	if (console->n_pollers == 0) {
7767851a396SAndrew Jeffery 		free(console->pollers);
7777851a396SAndrew Jeffery 		console->pollers = NULL;
7787851a396SAndrew Jeffery 	} else {
7797851a396SAndrew Jeffery 		console->pollers = reallocarray(console->pollers,
7807851a396SAndrew Jeffery 						console->n_pollers,
78191b52175SAndrew Jeffery 						sizeof(*console->pollers));
7827851a396SAndrew Jeffery 	}
78391b52175SAndrew Jeffery 	/* NOLINTEND(bugprone-sizeof-expression) */
784329a35f5SJeremy Kerr 
7852325d5d5SAlexander Hansen 	console_server_release_pollfd(console->server, poller->pollfd_index);
786329a35f5SJeremy Kerr 
787329a35f5SJeremy Kerr 	free(poller);
788329a35f5SJeremy Kerr }
789329a35f5SJeremy Kerr 
console_poller_set_events(struct console * console,struct poller * poller,int events)7906b1fed27SJeremy Kerr void console_poller_set_events(struct console *console, struct poller *poller,
7916b1fed27SJeremy Kerr 			       int events)
7926b1fed27SJeremy Kerr {
7932325d5d5SAlexander Hansen 	console->server->pollfds[poller->pollfd_index].events =
7942325d5d5SAlexander Hansen 		(short)(events & 0x7fff);
7956b1fed27SJeremy Kerr }
7966b1fed27SJeremy Kerr 
console_poller_set_timeout(struct console * console,struct poller * poller,const struct timeval * tv)797fd048328SAndrew Jeffery void console_poller_set_timeout(struct console *console __attribute__((unused)),
798fd048328SAndrew Jeffery 				struct poller *poller, const struct timeval *tv)
7991cecc5deSJohnathan Mantey {
8001cecc5deSJohnathan Mantey 	struct timeval now;
8011cecc5deSJohnathan Mantey 	int rc;
8021cecc5deSJohnathan Mantey 
8031cecc5deSJohnathan Mantey 	rc = get_current_time(&now);
8042834c5b1SAndrew Jeffery 	if (rc) {
8051cecc5deSJohnathan Mantey 		return;
8062834c5b1SAndrew Jeffery 	}
8071cecc5deSJohnathan Mantey 
8081cecc5deSJohnathan Mantey 	timeradd(&now, tv, &poller->timeout);
8091cecc5deSJohnathan Mantey }
8101cecc5deSJohnathan Mantey 
get_poll_timeout(struct console * console,struct timeval * cur_time)8115c359cc6SAndrew Jeffery static long get_poll_timeout(struct console *console, struct timeval *cur_time)
8121cecc5deSJohnathan Mantey {
813b70f8713SAndrew Jeffery 	struct timeval *earliest;
814b70f8713SAndrew Jeffery 	struct timeval interval;
8151cecc5deSJohnathan Mantey 	struct poller *poller;
8161cecc5deSJohnathan Mantey 	int i;
8171cecc5deSJohnathan Mantey 
8181cecc5deSJohnathan Mantey 	earliest = NULL;
8191cecc5deSJohnathan Mantey 
8201cecc5deSJohnathan Mantey 	for (i = 0; i < console->n_pollers; i++) {
8211cecc5deSJohnathan Mantey 		poller = console->pollers[i];
8221cecc5deSJohnathan Mantey 
8231cecc5deSJohnathan Mantey 		if (poller->timeout_fn && timerisset(&poller->timeout) &&
8241cecc5deSJohnathan Mantey 		    (!earliest ||
8251cecc5deSJohnathan Mantey 		     (earliest && timercmp(&poller->timeout, earliest, <)))) {
8261cecc5deSJohnathan Mantey 			// poller is buffering data and needs the poll
8271cecc5deSJohnathan Mantey 			// function to timeout.
8281cecc5deSJohnathan Mantey 			earliest = &poller->timeout;
8291cecc5deSJohnathan Mantey 		}
8301cecc5deSJohnathan Mantey 	}
8311cecc5deSJohnathan Mantey 
8321cecc5deSJohnathan Mantey 	if (earliest) {
8331cecc5deSJohnathan Mantey 		if (timercmp(earliest, cur_time, >)) {
8341cecc5deSJohnathan Mantey 			/* recalculate the timeout period, time period has
8351cecc5deSJohnathan Mantey 			 * not elapsed */
8361cecc5deSJohnathan Mantey 			timersub(earliest, cur_time, &interval);
8371cecc5deSJohnathan Mantey 			return ((interval.tv_sec * 1000) +
8381cecc5deSJohnathan Mantey 				(interval.tv_usec / 1000));
8390b7b0477SAndrew Jeffery 		} /* return from poll immediately */
8401cecc5deSJohnathan Mantey 		return 0;
8410b7b0477SAndrew Jeffery 
8420b7b0477SAndrew Jeffery 	} /* poll indefinitely */
8431cecc5deSJohnathan Mantey 	return -1;
8441cecc5deSJohnathan Mantey }
8451cecc5deSJohnathan Mantey 
call_pollers(struct console * console,struct timeval * cur_time)8461cecc5deSJohnathan Mantey static int call_pollers(struct console *console, struct timeval *cur_time)
847329a35f5SJeremy Kerr {
848329a35f5SJeremy Kerr 	struct poller *poller;
849329a35f5SJeremy Kerr 	struct pollfd *pollfd;
850329a35f5SJeremy Kerr 	enum poller_ret prc;
851b70f8713SAndrew Jeffery 	int i;
852b70f8713SAndrew Jeffery 	int rc;
853d831f960SJeremy Kerr 
8541a0e03b4SJeremy Kerr 	rc = 0;
8551a0e03b4SJeremy Kerr 
856329a35f5SJeremy Kerr 	/*
857329a35f5SJeremy Kerr 	 * Process poll events by iterating through the pollers and pollfds
858329a35f5SJeremy Kerr 	 * in-step, calling any pollers that we've found revents for.
859329a35f5SJeremy Kerr 	 */
860329a35f5SJeremy Kerr 	for (i = 0; i < console->n_pollers; i++) {
861329a35f5SJeremy Kerr 		poller = console->pollers[i];
8622325d5d5SAlexander Hansen 		pollfd = &console->server->pollfds[poller->pollfd_index];
8632325d5d5SAlexander Hansen 		if (pollfd->fd < 0) {
8642325d5d5SAlexander Hansen 			// pollfd has already been released
8652325d5d5SAlexander Hansen 			continue;
8662325d5d5SAlexander Hansen 		}
8672325d5d5SAlexander Hansen 
8681cecc5deSJohnathan Mantey 		prc = POLLER_OK;
8691a0e03b4SJeremy Kerr 
8701cecc5deSJohnathan Mantey 		/* process pending events... */
8711cecc5deSJohnathan Mantey 		if (pollfd->revents) {
8721cecc5deSJohnathan Mantey 			prc = poller->event_fn(poller->handler, pollfd->revents,
873329a35f5SJeremy Kerr 					       poller->data);
8742834c5b1SAndrew Jeffery 			if (prc == POLLER_EXIT) {
875329a35f5SJeremy Kerr 				rc = -1;
8762834c5b1SAndrew Jeffery 			} else if (prc == POLLER_REMOVE) {
877329a35f5SJeremy Kerr 				poller->remove = true;
878329a35f5SJeremy Kerr 			}
8792834c5b1SAndrew Jeffery 		}
880329a35f5SJeremy Kerr 
8811cecc5deSJohnathan Mantey 		if ((prc == POLLER_OK) && poller->timeout_fn &&
8821cecc5deSJohnathan Mantey 		    timerisset(&poller->timeout) &&
8831cecc5deSJohnathan Mantey 		    timercmp(&poller->timeout, cur_time, <=)) {
8841cecc5deSJohnathan Mantey 			/* One of the ringbuffer consumers is buffering the
8851cecc5deSJohnathan Mantey 			data stream. The amount of idle time the consumer
8861cecc5deSJohnathan Mantey 			desired has expired.  Process the buffered data for
8871cecc5deSJohnathan Mantey 			transmission. */
8881cecc5deSJohnathan Mantey 			timerclear(&poller->timeout);
8891cecc5deSJohnathan Mantey 			prc = poller->timeout_fn(poller->handler, poller->data);
8901cecc5deSJohnathan Mantey 			if (prc == POLLER_EXIT) {
8911cecc5deSJohnathan Mantey 				rc = -1;
8921cecc5deSJohnathan Mantey 			} else if (prc == POLLER_REMOVE) {
8931cecc5deSJohnathan Mantey 				poller->remove = true;
8941cecc5deSJohnathan Mantey 			}
8951cecc5deSJohnathan Mantey 		}
8961cecc5deSJohnathan Mantey 	}
8971cecc5deSJohnathan Mantey 
898329a35f5SJeremy Kerr 	/**
899329a35f5SJeremy Kerr 	 * Process deferred removals; restarting each time we unregister, as
900329a35f5SJeremy Kerr 	 * the array will have changed
901329a35f5SJeremy Kerr 	 */
902329a35f5SJeremy Kerr 	for (;;) {
903329a35f5SJeremy Kerr 		bool removed = false;
904329a35f5SJeremy Kerr 
905329a35f5SJeremy Kerr 		for (i = 0; i < console->n_pollers; i++) {
906329a35f5SJeremy Kerr 			poller = console->pollers[i];
907329a35f5SJeremy Kerr 			if (poller->remove) {
90855c9712dSJeremy Kerr 				console_poller_unregister(console, poller);
909329a35f5SJeremy Kerr 				removed = true;
910329a35f5SJeremy Kerr 				break;
911329a35f5SJeremy Kerr 			}
912329a35f5SJeremy Kerr 		}
9132834c5b1SAndrew Jeffery 		if (!removed) {
914329a35f5SJeremy Kerr 			break;
9151a0e03b4SJeremy Kerr 		}
9162834c5b1SAndrew Jeffery 	}
9171a0e03b4SJeremy Kerr 
9181a0e03b4SJeremy Kerr 	return rc;
9191a0e03b4SJeremy Kerr }
9201a0e03b4SJeremy Kerr 
sighandler(int signal)921769cee1aSJeremy Kerr static void sighandler(int signal)
922769cee1aSJeremy Kerr {
9232834c5b1SAndrew Jeffery 	if (signal == SIGINT) {
924553cb663SAndrew Jeffery 		sigint = 1;
925769cee1aSJeremy Kerr 	}
9262834c5b1SAndrew Jeffery }
927769cee1aSJeremy Kerr 
run_console_per_console(struct console * console,size_t buf_size,struct timeval * tv)928312ecdc2SAlexander Hansen static int run_console_per_console(struct console *console, size_t buf_size,
929312ecdc2SAlexander Hansen 				   struct timeval *tv)
9301a0e03b4SJeremy Kerr {
931312ecdc2SAlexander Hansen 	int rc;
932769cee1aSJeremy Kerr 
933312ecdc2SAlexander Hansen 	if (console->rb->size < buf_size) {
9346925740dSAlexander Hansen 		fprintf(stderr, "Ringbuffer size should be greater than %zuB\n",
935312ecdc2SAlexander Hansen 			buf_size);
9366925740dSAlexander Hansen 		return -1;
9377f2bfb9bSMedicine Yeh 	}
9381764145dSJeremy Kerr 
939769cee1aSJeremy Kerr 	if (sigint) {
940312ecdc2SAlexander Hansen 		warnx("Received interrupt, exiting\n");
9416925740dSAlexander Hansen 		return -1;
942769cee1aSJeremy Kerr 	}
943769cee1aSJeremy Kerr 
944312ecdc2SAlexander Hansen 	/* ... and then the pollers */
945312ecdc2SAlexander Hansen 	rc = call_pollers(console, tv);
946312ecdc2SAlexander Hansen 	if (rc) {
947312ecdc2SAlexander Hansen 		return -1;
948312ecdc2SAlexander Hansen 	}
949312ecdc2SAlexander Hansen 
950312ecdc2SAlexander Hansen 	return 0;
951312ecdc2SAlexander Hansen }
952312ecdc2SAlexander Hansen 
run_console_iteration(struct console_server * server)953312ecdc2SAlexander Hansen static int run_console_iteration(struct console_server *server)
954312ecdc2SAlexander Hansen {
955312ecdc2SAlexander Hansen 	struct timeval tv;
956312ecdc2SAlexander Hansen 	uint8_t buf[4096];
957312ecdc2SAlexander Hansen 	long timeout;
958312ecdc2SAlexander Hansen 	ssize_t rc;
959312ecdc2SAlexander Hansen 
9601cecc5deSJohnathan Mantey 	rc = get_current_time(&tv);
9611cecc5deSJohnathan Mantey 	if (rc) {
9621cecc5deSJohnathan Mantey 		warn("Failed to read current time");
9636925740dSAlexander Hansen 		return -1;
9641cecc5deSJohnathan Mantey 	}
9651cecc5deSJohnathan Mantey 
966312ecdc2SAlexander Hansen 	timeout = get_poll_timeout(server->active, &tv);
9671cecc5deSJohnathan Mantey 
968312ecdc2SAlexander Hansen 	rc = poll(server->pollfds, server->capacity_pollfds, (int)timeout);
969312ecdc2SAlexander Hansen 
970312ecdc2SAlexander Hansen 	if (sigint) {
971312ecdc2SAlexander Hansen 		warnx("Received interrupt, exiting\n");
972312ecdc2SAlexander Hansen 		return -1;
973312ecdc2SAlexander Hansen 	}
9741cecc5deSJohnathan Mantey 
975d831f960SJeremy Kerr 	if (rc < 0) {
976769cee1aSJeremy Kerr 		if (errno == EINTR) {
9776925740dSAlexander Hansen 			return 0;
9780b7b0477SAndrew Jeffery 		}
979d831f960SJeremy Kerr 		warn("poll error");
9806925740dSAlexander Hansen 		return -1;
981769cee1aSJeremy Kerr 	}
982d831f960SJeremy Kerr 
983329a35f5SJeremy Kerr 	/* process internal fd first */
984312ecdc2SAlexander Hansen 	if (server->pollfds[server->tty_pollfd_index].revents) {
985312ecdc2SAlexander Hansen 		rc = read(server->tty.fd, buf, sizeof(buf));
986d831f960SJeremy Kerr 		if (rc <= 0) {
987d831f960SJeremy Kerr 			warn("Error reading from tty device");
9886925740dSAlexander Hansen 			return -1;
989d831f960SJeremy Kerr 		}
990312ecdc2SAlexander Hansen 
991312ecdc2SAlexander Hansen 		rc = ringbuffer_queue(server->active->rb, buf, rc);
9922834c5b1SAndrew Jeffery 		if (rc) {
9936925740dSAlexander Hansen 			return -1;
994d831f960SJeremy Kerr 		}
9952834c5b1SAndrew Jeffery 	}
996d831f960SJeremy Kerr 
997312ecdc2SAlexander Hansen 	// process dbus
998312ecdc2SAlexander Hansen 	struct pollfd *dbus_pollfd =
999312ecdc2SAlexander Hansen 		&(server->pollfds[server->dbus_pollfd_index]);
1000312ecdc2SAlexander Hansen 	if (dbus_pollfd->revents) {
1001312ecdc2SAlexander Hansen 		sd_bus_process(server->bus, NULL);
1002f9c8f6caSCheng C Yang 	}
1003f9c8f6caSCheng C Yang 
1004312ecdc2SAlexander Hansen 	for (size_t i = 0; i < server->n_consoles; i++) {
1005312ecdc2SAlexander Hansen 		struct console *console = server->consoles[i];
1006312ecdc2SAlexander Hansen 
1007312ecdc2SAlexander Hansen 		rc = run_console_per_console(console, sizeof(buf), &tv);
1008312ecdc2SAlexander Hansen 		if (rc != 0) {
10096925740dSAlexander Hansen 			return -1;
10106925740dSAlexander Hansen 		}
1011312ecdc2SAlexander Hansen 	}
1012312ecdc2SAlexander Hansen 
10136925740dSAlexander Hansen 	return 0;
10146925740dSAlexander Hansen }
10156925740dSAlexander Hansen 
run_server(struct console_server * server)1016312ecdc2SAlexander Hansen int run_server(struct console_server *server)
10176925740dSAlexander Hansen {
1018312ecdc2SAlexander Hansen 	sighandler_t sighandler_save;
10196925740dSAlexander Hansen 	ssize_t rc = 0;
10206925740dSAlexander Hansen 
1021312ecdc2SAlexander Hansen 	if (server->n_consoles == 0) {
1022312ecdc2SAlexander Hansen 		warnx("no console configured for this server");
1023312ecdc2SAlexander Hansen 		return -1;
1024312ecdc2SAlexander Hansen 	}
1025312ecdc2SAlexander Hansen 
1026312ecdc2SAlexander Hansen 	sighandler_save = signal(SIGINT, sighandler);
10276925740dSAlexander Hansen 	for (;;) {
1028312ecdc2SAlexander Hansen 		rc = run_console_iteration(server);
10296925740dSAlexander Hansen 		if (rc) {
1030769cee1aSJeremy Kerr 			break;
10311a0e03b4SJeremy Kerr 		}
10322834c5b1SAndrew Jeffery 	}
1033769cee1aSJeremy Kerr 	signal(SIGINT, sighandler_save);
1034769cee1aSJeremy Kerr 
1035769cee1aSJeremy Kerr 	return rc ? -1 : 0;
10361a0e03b4SJeremy Kerr }
1037312ecdc2SAlexander Hansen 
1038d831f960SJeremy Kerr static const struct option options[] = {
1039d66195c1SJeremy Kerr 	{ "config", required_argument, 0, 'c' },
1040954be0fbSAndrew Jeffery 	{ "console-id", required_argument, 0, 'i' },
1041f5858b5bSJoel Stanley 	{ 0, 0, 0, 0 },
1042d831f960SJeremy Kerr };
1043d831f960SJeremy Kerr 
console_init(struct console_server * server,struct config * config,const char * console_id)1044312ecdc2SAlexander Hansen static struct console *console_init(struct console_server *server,
1045312ecdc2SAlexander Hansen 				    struct config *config,
1046312ecdc2SAlexander Hansen 				    const char *console_id)
1047d831f960SJeremy Kerr {
10487f2bfb9bSMedicine Yeh 	size_t buffer_size = default_buffer_size;
1049312ecdc2SAlexander Hansen 	const char *buffer_size_str = NULL;
1050312ecdc2SAlexander Hansen 	int rc;
1051312ecdc2SAlexander Hansen 
1052312ecdc2SAlexander Hansen 	struct console *console = calloc(1, sizeof(struct console));
1053312ecdc2SAlexander Hansen 	if (console == NULL) {
1054312ecdc2SAlexander Hansen 		return NULL;
1055312ecdc2SAlexander Hansen 	}
1056312ecdc2SAlexander Hansen 
1057312ecdc2SAlexander Hansen 	console->server = server;
1058312ecdc2SAlexander Hansen 	console->console_id = console_id;
1059312ecdc2SAlexander Hansen 
1060312ecdc2SAlexander Hansen 	buffer_size_str =
1061312ecdc2SAlexander Hansen 		config_get_section_value(config, console_id, "ringbuffer-size");
1062312ecdc2SAlexander Hansen 
1063312ecdc2SAlexander Hansen 	if (!buffer_size_str) {
1064312ecdc2SAlexander Hansen 		buffer_size_str = config_get_value(config, "ringbuffer-size");
1065312ecdc2SAlexander Hansen 	}
1066312ecdc2SAlexander Hansen 
1067312ecdc2SAlexander Hansen 	if (buffer_size_str) {
1068312ecdc2SAlexander Hansen 		rc = config_parse_bytesize(buffer_size_str, &buffer_size);
1069312ecdc2SAlexander Hansen 		if (rc) {
1070312ecdc2SAlexander Hansen 			warn("Invalid ringbuffer-size. Default to %zukB",
1071312ecdc2SAlexander Hansen 			     buffer_size >> 10);
1072312ecdc2SAlexander Hansen 		}
1073312ecdc2SAlexander Hansen 	}
1074312ecdc2SAlexander Hansen 
1075312ecdc2SAlexander Hansen 	console->rb = ringbuffer_init(buffer_size);
1076312ecdc2SAlexander Hansen 	if (!console->rb) {
1077312ecdc2SAlexander Hansen 		goto cleanup_console;
1078312ecdc2SAlexander Hansen 	}
1079312ecdc2SAlexander Hansen 
1080*a6b29104SAlexander Hansen 	rc = console_mux_init(console, config);
1081*a6b29104SAlexander Hansen 	if (rc) {
1082*a6b29104SAlexander Hansen 		warnx("could not set mux gpios from config, exiting");
1083*a6b29104SAlexander Hansen 		goto cleanup_rb;
1084*a6b29104SAlexander Hansen 	}
1085*a6b29104SAlexander Hansen 
1086312ecdc2SAlexander Hansen 	if (set_socket_info(console, config, console_id)) {
1087312ecdc2SAlexander Hansen 		warnx("set_socket_info failed");
1088312ecdc2SAlexander Hansen 		goto cleanup_rb;
1089312ecdc2SAlexander Hansen 	}
1090312ecdc2SAlexander Hansen 
1091312ecdc2SAlexander Hansen 	rc = dbus_init(console, config);
1092312ecdc2SAlexander Hansen 	if (rc != 0) {
1093312ecdc2SAlexander Hansen 		goto cleanup_rb;
1094312ecdc2SAlexander Hansen 	}
1095312ecdc2SAlexander Hansen 
1096312ecdc2SAlexander Hansen 	handlers_init(console, config);
1097312ecdc2SAlexander Hansen 
1098312ecdc2SAlexander Hansen 	return console;
1099312ecdc2SAlexander Hansen 
1100312ecdc2SAlexander Hansen cleanup_rb:
1101312ecdc2SAlexander Hansen 	free(console->rb);
1102312ecdc2SAlexander Hansen cleanup_console:
1103312ecdc2SAlexander Hansen 	free(console);
1104312ecdc2SAlexander Hansen 
1105312ecdc2SAlexander Hansen 	return NULL;
1106312ecdc2SAlexander Hansen }
1107312ecdc2SAlexander Hansen 
console_fini(struct console * console)1108312ecdc2SAlexander Hansen static void console_fini(struct console *console)
1109312ecdc2SAlexander Hansen {
1110312ecdc2SAlexander Hansen 	handlers_fini(console);
1111312ecdc2SAlexander Hansen 	ringbuffer_fini(console->rb);
1112312ecdc2SAlexander Hansen 	free(console->pollers);
1113312ecdc2SAlexander Hansen 	free(console);
1114312ecdc2SAlexander Hansen }
1115312ecdc2SAlexander Hansen 
1116312ecdc2SAlexander Hansen // 'opt_console_id' may be NULL
console_server_add_console(struct console_server * server,struct config * config,const char * opt_console_id)1117312ecdc2SAlexander Hansen static int console_server_add_console(struct console_server *server,
1118312ecdc2SAlexander Hansen 				      struct config *config,
1119312ecdc2SAlexander Hansen 				      const char *opt_console_id)
1120312ecdc2SAlexander Hansen {
1121312ecdc2SAlexander Hansen 	const char *console_id;
1122312ecdc2SAlexander Hansen 	struct console *console;
1123312ecdc2SAlexander Hansen 
1124312ecdc2SAlexander Hansen 	console_id = config_resolve_console_id(config, opt_console_id);
1125312ecdc2SAlexander Hansen 
1126312ecdc2SAlexander Hansen 	struct console **tmp = reallocarray(server->consoles,
1127312ecdc2SAlexander Hansen 					    server->n_consoles + 1,
1128312ecdc2SAlexander Hansen 					    sizeof(struct console *));
1129312ecdc2SAlexander Hansen 	if (tmp == NULL) {
1130312ecdc2SAlexander Hansen 		warnx("could not realloc server->consoles");
1131312ecdc2SAlexander Hansen 		return -1;
1132312ecdc2SAlexander Hansen 	}
1133312ecdc2SAlexander Hansen 	server->consoles = tmp;
1134312ecdc2SAlexander Hansen 
1135312ecdc2SAlexander Hansen 	console = console_init(server, config, console_id);
1136312ecdc2SAlexander Hansen 	if (console == NULL) {
1137312ecdc2SAlexander Hansen 		warnx("console_init failed");
1138312ecdc2SAlexander Hansen 		return -1;
1139312ecdc2SAlexander Hansen 	}
1140312ecdc2SAlexander Hansen 
1141312ecdc2SAlexander Hansen 	server->consoles[server->n_consoles++] = console;
1142312ecdc2SAlexander Hansen 
1143312ecdc2SAlexander Hansen 	return 0;
1144312ecdc2SAlexander Hansen }
1145312ecdc2SAlexander Hansen 
1146312ecdc2SAlexander Hansen // returns NULL on error
1147312ecdc2SAlexander Hansen static struct console *
console_server_add_consoles(struct console_server * server,const char * arg_console_id)1148312ecdc2SAlexander Hansen console_server_add_consoles(struct console_server *server,
1149312ecdc2SAlexander Hansen 			    const char *arg_console_id)
1150312ecdc2SAlexander Hansen {
1151312ecdc2SAlexander Hansen 	int rc;
1152312ecdc2SAlexander Hansen 
1153312ecdc2SAlexander Hansen 	const int nsections = config_count_sections(server->config);
1154312ecdc2SAlexander Hansen 	if (nsections < 0) {
1155312ecdc2SAlexander Hansen 		return NULL;
1156312ecdc2SAlexander Hansen 	}
1157312ecdc2SAlexander Hansen 
1158312ecdc2SAlexander Hansen 	if (nsections == 0) {
1159312ecdc2SAlexander Hansen 		const char *console_id = arg_console_id;
1160312ecdc2SAlexander Hansen 
1161312ecdc2SAlexander Hansen 		rc = console_server_add_console(server, server->config,
1162312ecdc2SAlexander Hansen 						console_id);
1163312ecdc2SAlexander Hansen 		if (rc != 0) {
1164312ecdc2SAlexander Hansen 			return NULL;
1165312ecdc2SAlexander Hansen 		}
1166312ecdc2SAlexander Hansen 	}
1167312ecdc2SAlexander Hansen 
1168312ecdc2SAlexander Hansen 	for (int i = 0; i < nsections; i++) {
1169312ecdc2SAlexander Hansen 		const char *console_id =
1170312ecdc2SAlexander Hansen 			config_get_section_name(server->config, i);
1171312ecdc2SAlexander Hansen 
1172312ecdc2SAlexander Hansen 		if (console_id == NULL) {
1173312ecdc2SAlexander Hansen 			warnx("no console id provided\n");
1174312ecdc2SAlexander Hansen 			return NULL;
1175312ecdc2SAlexander Hansen 		}
1176312ecdc2SAlexander Hansen 
1177312ecdc2SAlexander Hansen 		rc = console_server_add_console(server, server->config,
1178312ecdc2SAlexander Hansen 						console_id);
1179312ecdc2SAlexander Hansen 		if (rc != 0) {
1180312ecdc2SAlexander Hansen 			return NULL;
1181312ecdc2SAlexander Hansen 		}
1182312ecdc2SAlexander Hansen 	}
1183312ecdc2SAlexander Hansen 
1184312ecdc2SAlexander Hansen 	const char *initially_active =
1185312ecdc2SAlexander Hansen 		config_get_value(server->config, "active-console");
1186312ecdc2SAlexander Hansen 	if (!initially_active) {
1187312ecdc2SAlexander Hansen 		return server->consoles[0];
1188312ecdc2SAlexander Hansen 	}
1189312ecdc2SAlexander Hansen 
1190312ecdc2SAlexander Hansen 	printf("setting console-id '%s' as the initially active console\n",
1191312ecdc2SAlexander Hansen 	       initially_active);
1192312ecdc2SAlexander Hansen 
1193312ecdc2SAlexander Hansen 	for (size_t i = 0; i < server->n_consoles; i++) {
1194312ecdc2SAlexander Hansen 		struct console *console = server->consoles[i];
1195312ecdc2SAlexander Hansen 
1196312ecdc2SAlexander Hansen 		if (strcmp(console->console_id, initially_active) == 0) {
1197312ecdc2SAlexander Hansen 			return console;
1198312ecdc2SAlexander Hansen 		}
1199312ecdc2SAlexander Hansen 	}
1200312ecdc2SAlexander Hansen 
1201312ecdc2SAlexander Hansen 	warnx("'active-console' '%s' not found among console ids\n",
1202312ecdc2SAlexander Hansen 	      initially_active);
1203312ecdc2SAlexander Hansen 
1204312ecdc2SAlexander Hansen 	return NULL;
1205312ecdc2SAlexander Hansen }
1206312ecdc2SAlexander Hansen 
console_server_init(struct console_server * server,const char * config_filename,const char * config_tty_kname,const char * console_id)1207312ecdc2SAlexander Hansen int console_server_init(struct console_server *server,
1208312ecdc2SAlexander Hansen 			const char *config_filename,
1209312ecdc2SAlexander Hansen 			const char *config_tty_kname, const char *console_id)
1210312ecdc2SAlexander Hansen {
1211312ecdc2SAlexander Hansen 	int rc;
1212312ecdc2SAlexander Hansen 	memset(server, 0, sizeof(struct console_server));
1213312ecdc2SAlexander Hansen 
1214312ecdc2SAlexander Hansen 	server->tty_pollfd_index = -1;
1215312ecdc2SAlexander Hansen 
1216312ecdc2SAlexander Hansen 	server->config = config_init(config_filename);
1217312ecdc2SAlexander Hansen 	if (server->config == NULL) {
1218312ecdc2SAlexander Hansen 		return -1;
1219312ecdc2SAlexander Hansen 	}
1220312ecdc2SAlexander Hansen 
1221*a6b29104SAlexander Hansen 	rc = console_server_mux_init(server);
1222*a6b29104SAlexander Hansen 	if (rc != 0) {
1223*a6b29104SAlexander Hansen 		return -1;
1224*a6b29104SAlexander Hansen 	}
1225*a6b29104SAlexander Hansen 
1226312ecdc2SAlexander Hansen 	uart_routing_init(server->config);
1227312ecdc2SAlexander Hansen 
1228312ecdc2SAlexander Hansen 	rc = tty_init(server, server->config, config_tty_kname);
1229312ecdc2SAlexander Hansen 	if (rc != 0) {
1230312ecdc2SAlexander Hansen 		warnx("error during tty_init, exiting.\n");
1231312ecdc2SAlexander Hansen 		return -1;
1232312ecdc2SAlexander Hansen 	}
1233312ecdc2SAlexander Hansen 
1234312ecdc2SAlexander Hansen 	rc = dbus_server_init(server);
1235312ecdc2SAlexander Hansen 	if (rc != 0) {
1236312ecdc2SAlexander Hansen 		warnx("error during dbus init for console server");
1237312ecdc2SAlexander Hansen 		return -1;
1238312ecdc2SAlexander Hansen 	}
1239312ecdc2SAlexander Hansen 
1240*a6b29104SAlexander Hansen 	struct console *initial_active =
1241*a6b29104SAlexander Hansen 		console_server_add_consoles(server, console_id);
1242*a6b29104SAlexander Hansen 	if (initial_active == NULL) {
1243*a6b29104SAlexander Hansen 		return -1;
1244*a6b29104SAlexander Hansen 	}
1245*a6b29104SAlexander Hansen 
1246*a6b29104SAlexander Hansen 	rc = console_mux_activate(initial_active);
1247*a6b29104SAlexander Hansen 	if (rc != 0) {
1248312ecdc2SAlexander Hansen 		return -1;
1249312ecdc2SAlexander Hansen 	}
1250312ecdc2SAlexander Hansen 
1251312ecdc2SAlexander Hansen 	return 0;
1252312ecdc2SAlexander Hansen }
1253312ecdc2SAlexander Hansen 
console_server_fini(struct console_server * server)1254312ecdc2SAlexander Hansen void console_server_fini(struct console_server *server)
1255312ecdc2SAlexander Hansen {
1256312ecdc2SAlexander Hansen 	for (size_t i = 0; i < server->n_consoles; i++) {
1257312ecdc2SAlexander Hansen 		console_fini(server->consoles[i]);
1258312ecdc2SAlexander Hansen 	}
1259312ecdc2SAlexander Hansen 
1260312ecdc2SAlexander Hansen 	free(server->consoles);
1261312ecdc2SAlexander Hansen 	dbus_server_fini(server);
1262312ecdc2SAlexander Hansen 	tty_fini(server);
1263312ecdc2SAlexander Hansen 	free(server->pollfds);
1264*a6b29104SAlexander Hansen 	console_server_mux_fini(server);
1265312ecdc2SAlexander Hansen 	config_fini(server->config);
1266312ecdc2SAlexander Hansen }
1267312ecdc2SAlexander Hansen 
main(int argc,char ** argv)1268312ecdc2SAlexander Hansen int main(int argc, char **argv)
1269312ecdc2SAlexander Hansen {
1270d66195c1SJeremy Kerr 	const char *config_filename = NULL;
12716221ce94SVishwanatha Subbanna 	const char *config_tty_kname = NULL;
1272954be0fbSAndrew Jeffery 	const char *console_id = NULL;
1273c2b0d8c7SAlexander Hansen 	struct console_server server = { 0 };
1274312ecdc2SAlexander Hansen 	int rc = 0;
1275d831f960SJeremy Kerr 
1276d831f960SJeremy Kerr 	for (;;) {
1277b70f8713SAndrew Jeffery 		int c;
1278b70f8713SAndrew Jeffery 		int idx;
1279d831f960SJeremy Kerr 
1280954be0fbSAndrew Jeffery 		c = getopt_long(argc, argv, "c:i:", options, &idx);
12812834c5b1SAndrew Jeffery 		if (c == -1) {
1282d831f960SJeremy Kerr 			break;
12832834c5b1SAndrew Jeffery 		}
1284d831f960SJeremy Kerr 
1285d831f960SJeremy Kerr 		switch (c) {
1286d66195c1SJeremy Kerr 		case 'c':
1287d66195c1SJeremy Kerr 			config_filename = optarg;
1288d831f960SJeremy Kerr 			break;
1289954be0fbSAndrew Jeffery 		case 'i':
1290954be0fbSAndrew Jeffery 			console_id = optarg;
1291954be0fbSAndrew Jeffery 			break;
1292d831f960SJeremy Kerr 		case 'h':
1293d831f960SJeremy Kerr 		case '?':
1294d831f960SJeremy Kerr 			usage(argv[0]);
1295d66195c1SJeremy Kerr 			return EXIT_SUCCESS;
1296d831f960SJeremy Kerr 		}
1297d831f960SJeremy Kerr 	}
1298d831f960SJeremy Kerr 
12992834c5b1SAndrew Jeffery 	if (optind < argc) {
13006221ce94SVishwanatha Subbanna 		config_tty_kname = argv[optind];
1301312ecdc2SAlexander Hansen 	} else {
1302312ecdc2SAlexander Hansen 		errx(EXIT_FAILURE, "no tty device path has been provided\n");
13032834c5b1SAndrew Jeffery 	}
13046221ce94SVishwanatha Subbanna 
1305312ecdc2SAlexander Hansen 	rc = console_server_init(&server, config_filename, config_tty_kname,
1306312ecdc2SAlexander Hansen 				 console_id);
1307312ecdc2SAlexander Hansen 
1308312ecdc2SAlexander Hansen 	if (rc == 0) {
1309312ecdc2SAlexander Hansen 		rc = run_server(&server);
131028a1761aSAndrew Jeffery 	}
13117f2bfb9bSMedicine Yeh 
1312312ecdc2SAlexander Hansen 	console_server_fini(&server);
131328a1761aSAndrew Jeffery 
1314d831f960SJeremy Kerr 	return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
1315d831f960SJeremy Kerr }
1316