xref: /openbmc/obmc-console/console-server.c (revision d769eecf1c0edc47f86c9325e03275d876dbc05d)
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>
3117217845SJeremy Kerr #include <limits.h>
321cecc5deSJohnathan Mantey #include <time.h>
3354e9569dSJeremy Kerr #include <termios.h>
34d831f960SJeremy Kerr 
35d831f960SJeremy Kerr #include <sys/types.h>
361cecc5deSJohnathan Mantey #include <sys/time.h>
37b14ca19cSNinad Palsule #include <sys/socket.h>
3887e344cdSJoel Stanley #include <poll.h>
39d831f960SJeremy Kerr 
401a0e03b4SJeremy Kerr #include "console-server.h"
41d831f960SJeremy Kerr 
42f733c85aSJeremy Kerr /* size of the shared backlog ringbuffer */
435db8c792SAndrew Jeffery const size_t buffer_size = 128ul * 1024ul;
44f733c85aSJeremy Kerr 
45769cee1aSJeremy Kerr /* state shared with the signal handler */
46769cee1aSJeremy Kerr static bool sigint;
47329a35f5SJeremy Kerr 
48d831f960SJeremy Kerr static void usage(const char *progname)
49d831f960SJeremy Kerr {
50d831f960SJeremy Kerr 	fprintf(stderr,
516221ce94SVishwanatha Subbanna 		"usage: %s [options] <DEVICE>\n"
52d831f960SJeremy Kerr 		"\n"
53d831f960SJeremy Kerr 		"Options:\n"
54d66195c1SJeremy Kerr 		"  --config <FILE>  Use FILE for configuration\n"
55d831f960SJeremy Kerr 		"",
56d831f960SJeremy Kerr 		progname);
57d831f960SJeremy Kerr }
58d831f960SJeremy Kerr 
5917217845SJeremy Kerr /* populates tty_dev and tty_sysfs_devnode, using the tty kernel name */
601a0e03b4SJeremy Kerr static int tty_find_device(struct console *console)
6117217845SJeremy Kerr {
62d3cb9c22SAndrew Jeffery 	char *tty_class_device_link = NULL;
63d3cb9c22SAndrew Jeffery 	char *tty_path_input_real = NULL;
64d3cb9c22SAndrew Jeffery 	char *tty_device_tty_dir = NULL;
65d3cb9c22SAndrew Jeffery 	char *tty_device_reldir = NULL;
66d3cb9c22SAndrew Jeffery 	char *tty_path_input = NULL;
67d3cb9c22SAndrew Jeffery 	char *tty_kname_real = NULL;
6817217845SJeremy Kerr 	int rc;
6917217845SJeremy Kerr 
7045ad7676SYi Li 	/* udev may rename the tty name with a symbol link, try to resolve */
7145ad7676SYi Li 	rc = asprintf(&tty_path_input, "/dev/%s", console->tty_kname);
722834c5b1SAndrew Jeffery 	if (rc < 0) {
7317217845SJeremy Kerr 		return -1;
742834c5b1SAndrew Jeffery 	}
7517217845SJeremy Kerr 
7645ad7676SYi Li 	tty_path_input_real = realpath(tty_path_input, NULL);
7745ad7676SYi Li 	if (!tty_path_input_real) {
7845ad7676SYi Li 		warn("Can't find realpath for /dev/%s", console->tty_kname);
7915792aa7SAndrew Jeffery 		rc = -1;
8045ad7676SYi Li 		goto out_free;
8145ad7676SYi Li 	}
8245ad7676SYi Li 
8345ad7676SYi Li 	tty_kname_real = basename(tty_path_input_real);
8445ad7676SYi Li 	if (!tty_kname_real) {
8545ad7676SYi Li 		warn("Can't find real name for /dev/%s", console->tty_kname);
8615792aa7SAndrew Jeffery 		rc = -1;
8745ad7676SYi Li 		goto out_free;
8845ad7676SYi Li 	}
8945ad7676SYi Li 
90a72711afSAndrew Jeffery 	rc = asprintf(&tty_class_device_link, "/sys/class/tty/%s",
91a72711afSAndrew Jeffery 		      tty_kname_real);
922834c5b1SAndrew Jeffery 	if (rc < 0) {
9345ad7676SYi Li 		goto out_free;
942834c5b1SAndrew Jeffery 	}
9545ad7676SYi Li 
9617217845SJeremy Kerr 	tty_device_tty_dir = realpath(tty_class_device_link, NULL);
9745ad7676SYi Li 	if (!tty_device_tty_dir) {
9845ad7676SYi Li 		warn("Can't query sysfs for device %s", tty_kname_real);
9915792aa7SAndrew Jeffery 		rc = -1;
10017217845SJeremy Kerr 		goto out_free;
10117217845SJeremy Kerr 	}
10217217845SJeremy Kerr 
10317217845SJeremy Kerr 	rc = asprintf(&tty_device_reldir, "%s/../../", tty_device_tty_dir);
1042834c5b1SAndrew Jeffery 	if (rc < 0) {
10517217845SJeremy Kerr 		goto out_free;
1062834c5b1SAndrew Jeffery 	}
10717217845SJeremy Kerr 
1081a0e03b4SJeremy Kerr 	console->tty_sysfs_devnode = realpath(tty_device_reldir, NULL);
1092834c5b1SAndrew Jeffery 	if (!console->tty_sysfs_devnode) {
11045ad7676SYi Li 		warn("Can't find parent device for %s", tty_kname_real);
1112834c5b1SAndrew Jeffery 	}
11217217845SJeremy Kerr 
11345ad7676SYi Li 	rc = asprintf(&console->tty_dev, "/dev/%s", tty_kname_real);
1142834c5b1SAndrew Jeffery 	if (rc < 0) {
11517217845SJeremy Kerr 		goto out_free;
1162834c5b1SAndrew Jeffery 	}
11717217845SJeremy Kerr 
11817217845SJeremy Kerr 	rc = 0;
11917217845SJeremy Kerr 
12017217845SJeremy Kerr out_free:
12117217845SJeremy Kerr 	free(tty_class_device_link);
12217217845SJeremy Kerr 	free(tty_device_tty_dir);
12317217845SJeremy Kerr 	free(tty_device_reldir);
12445ad7676SYi Li 	free(tty_path_input);
12545ad7676SYi Li 	free(tty_path_input_real);
12617217845SJeremy Kerr 	return rc;
12717217845SJeremy Kerr }
12817217845SJeremy Kerr 
1291a0e03b4SJeremy Kerr static int tty_set_sysfs_attr(struct console *console, const char *name,
130957818b4SJeremy Kerr 			      int value)
131957818b4SJeremy Kerr {
132957818b4SJeremy Kerr 	char *path;
133957818b4SJeremy Kerr 	FILE *fp;
134957818b4SJeremy Kerr 	int rc;
135957818b4SJeremy Kerr 
1361a0e03b4SJeremy Kerr 	rc = asprintf(&path, "%s/%s", console->tty_sysfs_devnode, name);
1372834c5b1SAndrew Jeffery 	if (rc < 0) {
138957818b4SJeremy Kerr 		return -1;
1392834c5b1SAndrew Jeffery 	}
140957818b4SJeremy Kerr 
141957818b4SJeremy Kerr 	fp = fopen(path, "w");
142957818b4SJeremy Kerr 	if (!fp) {
143a72711afSAndrew Jeffery 		warn("Can't access attribute %s on device %s", name,
144a72711afSAndrew Jeffery 		     console->tty_kname);
145957818b4SJeremy Kerr 		rc = -1;
146957818b4SJeremy Kerr 		goto out_free;
147957818b4SJeremy Kerr 	}
148957818b4SJeremy Kerr 	setvbuf(fp, NULL, _IONBF, 0);
149957818b4SJeremy Kerr 
150957818b4SJeremy Kerr 	rc = fprintf(fp, "0x%x", value);
1512834c5b1SAndrew Jeffery 	if (rc < 0) {
152a72711afSAndrew Jeffery 		warn("Error writing to %s attribute of device %s", name,
153a72711afSAndrew Jeffery 		     console->tty_kname);
1542834c5b1SAndrew Jeffery 	}
155957818b4SJeremy Kerr 	fclose(fp);
156957818b4SJeremy Kerr 
157957818b4SJeremy Kerr out_free:
158957818b4SJeremy Kerr 	free(path);
159957818b4SJeremy Kerr 	return rc;
160957818b4SJeremy Kerr }
161957818b4SJeremy Kerr 
162d831f960SJeremy Kerr /**
163c7fbcd48SBenjamin Fair  * Set termios attributes on the console tty.
16454e9569dSJeremy Kerr  */
165e258e51fSNinad Palsule void tty_init_termios(struct console *console)
16654e9569dSJeremy Kerr {
16754e9569dSJeremy Kerr 	struct termios termios;
16854e9569dSJeremy Kerr 	int rc;
16954e9569dSJeremy Kerr 
17054e9569dSJeremy Kerr 	rc = tcgetattr(console->tty_fd, &termios);
17154e9569dSJeremy Kerr 	if (rc) {
17254e9569dSJeremy Kerr 		warn("Can't read tty termios");
17354e9569dSJeremy Kerr 		return;
17454e9569dSJeremy Kerr 	}
17554e9569dSJeremy Kerr 
176c7fbcd48SBenjamin Fair 	if (console->tty_baud) {
1772834c5b1SAndrew Jeffery 		if (cfsetspeed(&termios, console->tty_baud) < 0) {
178c7fbcd48SBenjamin Fair 			warn("Couldn't set speeds for %s", console->tty_kname);
179c7fbcd48SBenjamin Fair 		}
1802834c5b1SAndrew Jeffery 	}
181c7fbcd48SBenjamin Fair 
182c7fbcd48SBenjamin Fair 	/* Set console to raw mode: we don't want any processing to occur on
183c7fbcd48SBenjamin Fair 	 * the underlying terminal input/output.
184c7fbcd48SBenjamin Fair 	 */
18554e9569dSJeremy Kerr 	cfmakeraw(&termios);
186c7fbcd48SBenjamin Fair 
18754e9569dSJeremy Kerr 	rc = tcsetattr(console->tty_fd, TCSANOW, &termios);
1882834c5b1SAndrew Jeffery 	if (rc) {
189c7fbcd48SBenjamin Fair 		warn("Can't set terminal options for %s", console->tty_kname);
19054e9569dSJeremy Kerr 	}
1912834c5b1SAndrew Jeffery }
19254e9569dSJeremy Kerr 
19354e9569dSJeremy Kerr /**
194d831f960SJeremy Kerr  * Open and initialise the serial device
195d831f960SJeremy Kerr  */
1961a0e03b4SJeremy Kerr static int tty_init_io(struct console *console)
197d831f960SJeremy Kerr {
1982834c5b1SAndrew Jeffery 	if (console->tty_sirq) {
1991a0e03b4SJeremy Kerr 		tty_set_sysfs_attr(console, "sirq", console->tty_sirq);
2002834c5b1SAndrew Jeffery 	}
2012834c5b1SAndrew Jeffery 	if (console->tty_lpc_addr) {
2021a0e03b4SJeremy Kerr 		tty_set_sysfs_attr(console, "lpc_address",
2031a0e03b4SJeremy Kerr 				   console->tty_lpc_addr);
2042834c5b1SAndrew Jeffery 	}
205957818b4SJeremy Kerr 
2061a0e03b4SJeremy Kerr 	console->tty_fd = open(console->tty_dev, O_RDWR);
2071a0e03b4SJeremy Kerr 	if (console->tty_fd <= 0) {
2081a0e03b4SJeremy Kerr 		warn("Can't open tty %s", console->tty_dev);
209d831f960SJeremy Kerr 		return -1;
210d831f960SJeremy Kerr 	}
211d831f960SJeremy Kerr 
212d831f960SJeremy Kerr 	/* Disable character delay. We may want to later enable this when
213d831f960SJeremy Kerr 	 * we detect larger amounts of data
214d831f960SJeremy Kerr 	 */
2151a0e03b4SJeremy Kerr 	fcntl(console->tty_fd, F_SETFL, FNDELAY);
216d831f960SJeremy Kerr 
21754e9569dSJeremy Kerr 	tty_init_termios(console);
21854e9569dSJeremy Kerr 
219329a35f5SJeremy Kerr 	console->pollfds[console->n_pollers].fd = console->tty_fd;
220329a35f5SJeremy Kerr 	console->pollfds[console->n_pollers].events = POLLIN;
221329a35f5SJeremy Kerr 
222d831f960SJeremy Kerr 	return 0;
223d831f960SJeremy Kerr }
224d831f960SJeremy Kerr 
225*d769eecfSAndrew Jeffery static int tty_init(struct console *console, struct config *config,
226*d769eecfSAndrew Jeffery 		    const char *tty_arg)
227d66195c1SJeremy Kerr {
228fd883a88SAndrew Jeffery 	unsigned long parsed;
229d66195c1SJeremy Kerr 	const char *val;
230d66195c1SJeremy Kerr 	char *endp;
231d66195c1SJeremy Kerr 	int rc;
232d66195c1SJeremy Kerr 
233d66195c1SJeremy Kerr 	val = config_get_value(config, "lpc-address");
234d66195c1SJeremy Kerr 	if (val) {
235fd883a88SAndrew Jeffery 		errno = 0;
236fd883a88SAndrew Jeffery 		parsed = strtoul(val, &endp, 0);
237fd883a88SAndrew Jeffery 		if (parsed == ULONG_MAX && errno == ERANGE) {
238fd883a88SAndrew Jeffery 			warn("Cannot interpret 'lpc-address' value as an unsigned long: '%s'",
239fd883a88SAndrew Jeffery 			     val);
240fd883a88SAndrew Jeffery 			return -1;
241fd883a88SAndrew Jeffery 		}
242fd883a88SAndrew Jeffery 
243fd883a88SAndrew Jeffery 		if (parsed > UINT16_MAX) {
244fd883a88SAndrew Jeffery 			warn("Invalid LPC address '%s'", val);
245fd883a88SAndrew Jeffery 			return -1;
246fd883a88SAndrew Jeffery 		}
247fd883a88SAndrew Jeffery 
248fd883a88SAndrew Jeffery 		console->tty_lpc_addr = (uint16_t)parsed;
249d66195c1SJeremy Kerr 		if (endp == optarg) {
250d66195c1SJeremy Kerr 			warn("Invalid LPC address: '%s'", val);
251d66195c1SJeremy Kerr 			return -1;
252d66195c1SJeremy Kerr 		}
253d66195c1SJeremy Kerr 	}
254d66195c1SJeremy Kerr 
255d66195c1SJeremy Kerr 	val = config_get_value(config, "sirq");
256d66195c1SJeremy Kerr 	if (val) {
257fd883a88SAndrew Jeffery 		errno = 0;
258fd883a88SAndrew Jeffery 		parsed = strtoul(val, &endp, 0);
259fd883a88SAndrew Jeffery 		if (parsed == ULONG_MAX && errno == ERANGE) {
260fd883a88SAndrew Jeffery 			warn("Cannot interpret 'sirq' value as an unsigned long: '%s'",
261fd883a88SAndrew Jeffery 			     val);
262fd883a88SAndrew Jeffery 		}
263fd883a88SAndrew Jeffery 
2642834c5b1SAndrew Jeffery 		if (parsed > 16) {
265fd883a88SAndrew Jeffery 			warn("Invalid LPC SERIRQ: '%s'", val);
2662834c5b1SAndrew Jeffery 		}
267fd883a88SAndrew Jeffery 
268fd883a88SAndrew Jeffery 		console->tty_sirq = (int)parsed;
2692834c5b1SAndrew Jeffery 		if (endp == optarg) {
270d66195c1SJeremy Kerr 			warn("Invalid sirq: '%s'", val);
271d66195c1SJeremy Kerr 		}
2722834c5b1SAndrew Jeffery 	}
273d66195c1SJeremy Kerr 
274c7fbcd48SBenjamin Fair 	val = config_get_value(config, "baud");
275c7fbcd48SBenjamin Fair 	if (val) {
2762834c5b1SAndrew Jeffery 		if (config_parse_baud(&console->tty_baud, val)) {
277c7fbcd48SBenjamin Fair 			warnx("Invalid baud rate: '%s'", val);
278c7fbcd48SBenjamin Fair 		}
2792834c5b1SAndrew Jeffery 	}
280c7fbcd48SBenjamin Fair 
281*d769eecfSAndrew Jeffery 	if (tty_arg) {
282*d769eecfSAndrew Jeffery 		console->tty_kname = tty_arg;
283*d769eecfSAndrew Jeffery 	} else if ((val = config_get_value(config, "upstream-tty"))) {
284*d769eecfSAndrew Jeffery 		console->tty_kname = val;
285*d769eecfSAndrew Jeffery 	} else {
286d66195c1SJeremy Kerr 		warnx("Error: No TTY device specified");
287d66195c1SJeremy Kerr 		return -1;
288d66195c1SJeremy Kerr 	}
289d66195c1SJeremy Kerr 
290d66195c1SJeremy Kerr 	rc = tty_find_device(console);
2912834c5b1SAndrew Jeffery 	if (rc) {
292d66195c1SJeremy Kerr 		return rc;
2932834c5b1SAndrew Jeffery 	}
294d66195c1SJeremy Kerr 
295d66195c1SJeremy Kerr 	rc = tty_init_io(console);
296d66195c1SJeremy Kerr 	return rc;
297d66195c1SJeremy Kerr }
298d66195c1SJeremy Kerr 
2991a0e03b4SJeremy Kerr int console_data_out(struct console *console, const uint8_t *data, size_t len)
300d831f960SJeremy Kerr {
3011a0e03b4SJeremy Kerr 	return write_buf_to_fd(console->tty_fd, data, len);
302d831f960SJeremy Kerr }
303d831f960SJeremy Kerr 
304b14ca19cSNinad Palsule /* Read console if from config and prepare a socket name */
305b14ca19cSNinad Palsule static int set_socket_info(struct console *console, struct config *config)
306b14ca19cSNinad Palsule {
307b14ca19cSNinad Palsule 	ssize_t len;
308b14ca19cSNinad Palsule 
309b14ca19cSNinad Palsule 	console->console_id = config_get_value(config, "socket-id");
310b14ca19cSNinad Palsule 	if (!console->console_id) {
311b14ca19cSNinad Palsule 		warnx("Error: The socket-id is not set in the config file");
312b14ca19cSNinad Palsule 		return EXIT_FAILURE;
313b14ca19cSNinad Palsule 	}
314b14ca19cSNinad Palsule 
315b14ca19cSNinad Palsule 	/* Get the socket name/path */
316b14ca19cSNinad Palsule 	len = console_socket_path(console->socket_name, console->console_id);
317b14ca19cSNinad Palsule 	if (len < 0) {
318b14ca19cSNinad Palsule 		warn("Failed to set socket path: %s", strerror(errno));
319b14ca19cSNinad Palsule 		return EXIT_FAILURE;
320b14ca19cSNinad Palsule 	}
321b14ca19cSNinad Palsule 
322b14ca19cSNinad Palsule 	/* Socket name is not a null terminated string hence save the length */
323b14ca19cSNinad Palsule 	console->socket_name_len = len;
324b14ca19cSNinad Palsule 
325b14ca19cSNinad Palsule 	return 0;
326b14ca19cSNinad Palsule }
327b14ca19cSNinad Palsule 
328d47963e5SJeremy Kerr static void handlers_init(struct console *console, struct config *config)
329d831f960SJeremy Kerr {
330b70f8713SAndrew Jeffery 	/* NOLINTBEGIN(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) */
331b70f8713SAndrew Jeffery 	extern struct handler *__start_handlers;
332b70f8713SAndrew Jeffery 	extern struct handler *__stop_handlers;
333b70f8713SAndrew Jeffery 	/* NOLINTEND(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) */
3341a0e03b4SJeremy Kerr 	struct handler *handler;
335b70f8713SAndrew Jeffery 	int i;
336b70f8713SAndrew Jeffery 	int rc;
337d831f960SJeremy Kerr 
3381a0e03b4SJeremy Kerr 	console->n_handlers = &__stop_handlers - &__start_handlers;
3391a0e03b4SJeremy Kerr 	console->handlers = &__start_handlers;
340d831f960SJeremy Kerr 
3415c359cc6SAndrew Jeffery 	printf("%ld handler%s\n", console->n_handlers,
3421a0e03b4SJeremy Kerr 	       console->n_handlers == 1 ? "" : "s");
343d831f960SJeremy Kerr 
3441a0e03b4SJeremy Kerr 	for (i = 0; i < console->n_handlers; i++) {
3451a0e03b4SJeremy Kerr 		handler = console->handlers[i];
3461a0e03b4SJeremy Kerr 
347021b91f0SJeremy Kerr 		rc = 0;
3482834c5b1SAndrew Jeffery 		if (handler->init) {
349021b91f0SJeremy Kerr 			rc = handler->init(handler, console, config);
3502834c5b1SAndrew Jeffery 		}
351021b91f0SJeremy Kerr 
352021b91f0SJeremy Kerr 		handler->active = rc == 0;
353021b91f0SJeremy Kerr 
354021b91f0SJeremy Kerr 		printf("  %s [%sactive]\n", handler->name,
355021b91f0SJeremy Kerr 		       handler->active ? "" : "in");
356d831f960SJeremy Kerr 	}
357d831f960SJeremy Kerr }
358d831f960SJeremy Kerr 
3591a0e03b4SJeremy Kerr static void handlers_fini(struct console *console)
360d831f960SJeremy Kerr {
3611a0e03b4SJeremy Kerr 	struct handler *handler;
3621a0e03b4SJeremy Kerr 	int i;
3631a0e03b4SJeremy Kerr 
3641a0e03b4SJeremy Kerr 	for (i = 0; i < console->n_handlers; i++) {
3651a0e03b4SJeremy Kerr 		handler = console->handlers[i];
3662834c5b1SAndrew Jeffery 		if (handler->fini && handler->active) {
3671a0e03b4SJeremy Kerr 			handler->fini(handler);
3681a0e03b4SJeremy Kerr 		}
369d831f960SJeremy Kerr 	}
3702834c5b1SAndrew Jeffery }
371d831f960SJeremy Kerr 
3721cecc5deSJohnathan Mantey static int get_current_time(struct timeval *tv)
3731cecc5deSJohnathan Mantey {
3741cecc5deSJohnathan Mantey 	struct timespec t;
3751cecc5deSJohnathan Mantey 	int rc;
3761cecc5deSJohnathan Mantey 
3771cecc5deSJohnathan Mantey 	/*
3781cecc5deSJohnathan Mantey 	 * We use clock_gettime(CLOCK_MONOTONIC) so we're immune to
3791cecc5deSJohnathan Mantey 	 * local time changes. However, a struct timeval is more
3801cecc5deSJohnathan Mantey 	 * convenient for calculations, so convert to that.
3811cecc5deSJohnathan Mantey 	 */
3821cecc5deSJohnathan Mantey 	rc = clock_gettime(CLOCK_MONOTONIC, &t);
3832834c5b1SAndrew Jeffery 	if (rc) {
3841cecc5deSJohnathan Mantey 		return rc;
3852834c5b1SAndrew Jeffery 	}
3861cecc5deSJohnathan Mantey 
3871cecc5deSJohnathan Mantey 	tv->tv_sec = t.tv_sec;
3881cecc5deSJohnathan Mantey 	tv->tv_usec = t.tv_nsec / 1000;
3891cecc5deSJohnathan Mantey 
3901cecc5deSJohnathan Mantey 	return 0;
3911cecc5deSJohnathan Mantey }
3921cecc5deSJohnathan Mantey 
393a72711afSAndrew Jeffery struct ringbuffer_consumer *
394a72711afSAndrew Jeffery console_ringbuffer_consumer_register(struct console *console,
395f733c85aSJeremy Kerr 				     ringbuffer_poll_fn_t poll_fn, void *data)
396d831f960SJeremy Kerr {
397f733c85aSJeremy Kerr 	return ringbuffer_consumer_register(console->rb, poll_fn, data);
398d831f960SJeremy Kerr }
399d831f960SJeremy Kerr 
40055c9712dSJeremy Kerr struct poller *console_poller_register(struct console *console,
401a72711afSAndrew Jeffery 				       struct handler *handler,
402a72711afSAndrew Jeffery 				       poller_event_fn_t poller_fn,
4031cecc5deSJohnathan Mantey 				       poller_timeout_fn_t timeout_fn, int fd,
4041cecc5deSJohnathan Mantey 				       int events, void *data)
405d831f960SJeremy Kerr {
406329a35f5SJeremy Kerr 	struct poller *poller;
4075c359cc6SAndrew Jeffery 	long n;
408329a35f5SJeremy Kerr 
409329a35f5SJeremy Kerr 	poller = malloc(sizeof(*poller));
410329a35f5SJeremy Kerr 	poller->remove = false;
411329a35f5SJeremy Kerr 	poller->handler = handler;
4121cecc5deSJohnathan Mantey 	poller->event_fn = poller_fn;
4131cecc5deSJohnathan Mantey 	poller->timeout_fn = timeout_fn;
414329a35f5SJeremy Kerr 	poller->data = data;
415329a35f5SJeremy Kerr 
416329a35f5SJeremy Kerr 	/* add one to our pollers array */
417329a35f5SJeremy Kerr 	n = console->n_pollers++;
41891b52175SAndrew Jeffery 	/*
41991b52175SAndrew Jeffery 	 * We're managing an array of pointers to aggregates, so don't warn about sizeof() on a
42091b52175SAndrew Jeffery 	 * pointer type.
42191b52175SAndrew Jeffery 	 */
42291b52175SAndrew Jeffery 	/* NOLINTBEGIN(bugprone-sizeof-expression) */
42391b52175SAndrew Jeffery 	console->pollers = reallocarray(console->pollers, console->n_pollers,
42491b52175SAndrew Jeffery 					sizeof(*console->pollers));
42591b52175SAndrew Jeffery 	/* NOLINTEND(bugprone-sizeof-expression) */
426329a35f5SJeremy Kerr 
427329a35f5SJeremy Kerr 	console->pollers[n] = poller;
428329a35f5SJeremy Kerr 
429329a35f5SJeremy Kerr 	/* increase pollfds array too  */
430a72711afSAndrew Jeffery 	console->pollfds =
43191b52175SAndrew Jeffery 		reallocarray(console->pollfds,
43291b52175SAndrew Jeffery 			     (MAX_INTERNAL_POLLFD + console->n_pollers),
43391b52175SAndrew Jeffery 			     sizeof(*console->pollfds));
434329a35f5SJeremy Kerr 
435329a35f5SJeremy Kerr 	/* shift the end pollfds up by one */
436a72711afSAndrew Jeffery 	memcpy(&console->pollfds[n + 1], &console->pollfds[n],
437f9c8f6caSCheng C Yang 	       sizeof(*console->pollfds) * MAX_INTERNAL_POLLFD);
438329a35f5SJeremy Kerr 
439329a35f5SJeremy Kerr 	console->pollfds[n].fd = fd;
4405c359cc6SAndrew Jeffery 	console->pollfds[n].events = (short)(events & 0x7fff);
441329a35f5SJeremy Kerr 
442329a35f5SJeremy Kerr 	return poller;
443329a35f5SJeremy Kerr }
444329a35f5SJeremy Kerr 
445a72711afSAndrew Jeffery void console_poller_unregister(struct console *console, struct poller *poller)
446329a35f5SJeremy Kerr {
447329a35f5SJeremy Kerr 	int i;
448329a35f5SJeremy Kerr 
449329a35f5SJeremy Kerr 	/* find the entry in our pollers array */
4502834c5b1SAndrew Jeffery 	for (i = 0; i < console->n_pollers; i++) {
4512834c5b1SAndrew Jeffery 		if (console->pollers[i] == poller) {
452329a35f5SJeremy Kerr 			break;
4532834c5b1SAndrew Jeffery 		}
4542834c5b1SAndrew Jeffery 	}
455329a35f5SJeremy Kerr 
456329a35f5SJeremy Kerr 	assert(i < console->n_pollers);
457329a35f5SJeremy Kerr 
458329a35f5SJeremy Kerr 	console->n_pollers--;
459329a35f5SJeremy Kerr 
46091b52175SAndrew Jeffery 	/*
46191b52175SAndrew Jeffery 	 * Remove the item from the pollers array...
46291b52175SAndrew Jeffery 	 *
46391b52175SAndrew Jeffery 	 * We're managing an array of pointers to aggregates, so don't warn about sizeof() on a
46491b52175SAndrew Jeffery 	 * pointer type.
46591b52175SAndrew Jeffery 	 */
46691b52175SAndrew Jeffery 	/* NOLINTBEGIN(bugprone-sizeof-expression) */
467329a35f5SJeremy Kerr 	memmove(&console->pollers[i], &console->pollers[i + 1],
468a72711afSAndrew Jeffery 		sizeof(*console->pollers) * (console->n_pollers - i));
469329a35f5SJeremy Kerr 
47091b52175SAndrew Jeffery 	console->pollers = reallocarray(console->pollers, console->n_pollers,
47191b52175SAndrew Jeffery 					sizeof(*console->pollers));
47291b52175SAndrew Jeffery 	/* NOLINTEND(bugprone-sizeof-expression) */
473329a35f5SJeremy Kerr 
474329a35f5SJeremy Kerr 	/* ... and the pollfds array */
475329a35f5SJeremy Kerr 	memmove(&console->pollfds[i], &console->pollfds[i + 1],
476329a35f5SJeremy Kerr 		sizeof(*console->pollfds) *
477f9c8f6caSCheng C Yang 			(MAX_INTERNAL_POLLFD + console->n_pollers - i));
478329a35f5SJeremy Kerr 
479a72711afSAndrew Jeffery 	console->pollfds =
48091b52175SAndrew Jeffery 		reallocarray(console->pollfds,
48191b52175SAndrew Jeffery 			     (MAX_INTERNAL_POLLFD + console->n_pollers),
48291b52175SAndrew Jeffery 			     sizeof(*console->pollfds));
483329a35f5SJeremy Kerr 
484329a35f5SJeremy Kerr 	free(poller);
485329a35f5SJeremy Kerr }
486329a35f5SJeremy Kerr 
4876b1fed27SJeremy Kerr void console_poller_set_events(struct console *console, struct poller *poller,
4886b1fed27SJeremy Kerr 			       int events)
4896b1fed27SJeremy Kerr {
4906b1fed27SJeremy Kerr 	int i;
4916b1fed27SJeremy Kerr 
4926b1fed27SJeremy Kerr 	/* find the entry in our pollers array */
4932834c5b1SAndrew Jeffery 	for (i = 0; i < console->n_pollers; i++) {
4942834c5b1SAndrew Jeffery 		if (console->pollers[i] == poller) {
4956b1fed27SJeremy Kerr 			break;
4962834c5b1SAndrew Jeffery 		}
4972834c5b1SAndrew Jeffery 	}
4986b1fed27SJeremy Kerr 
4995c359cc6SAndrew Jeffery 	console->pollfds[i].events = (short)(events & 0x7fff);
5006b1fed27SJeremy Kerr }
5016b1fed27SJeremy Kerr 
502fd048328SAndrew Jeffery void console_poller_set_timeout(struct console *console __attribute__((unused)),
503fd048328SAndrew Jeffery 				struct poller *poller, const struct timeval *tv)
5041cecc5deSJohnathan Mantey {
5051cecc5deSJohnathan Mantey 	struct timeval now;
5061cecc5deSJohnathan Mantey 	int rc;
5071cecc5deSJohnathan Mantey 
5081cecc5deSJohnathan Mantey 	rc = get_current_time(&now);
5092834c5b1SAndrew Jeffery 	if (rc) {
5101cecc5deSJohnathan Mantey 		return;
5112834c5b1SAndrew Jeffery 	}
5121cecc5deSJohnathan Mantey 
5131cecc5deSJohnathan Mantey 	timeradd(&now, tv, &poller->timeout);
5141cecc5deSJohnathan Mantey }
5151cecc5deSJohnathan Mantey 
5165c359cc6SAndrew Jeffery static long get_poll_timeout(struct console *console, struct timeval *cur_time)
5171cecc5deSJohnathan Mantey {
518b70f8713SAndrew Jeffery 	struct timeval *earliest;
519b70f8713SAndrew Jeffery 	struct timeval interval;
5201cecc5deSJohnathan Mantey 	struct poller *poller;
5211cecc5deSJohnathan Mantey 	int i;
5221cecc5deSJohnathan Mantey 
5231cecc5deSJohnathan Mantey 	earliest = NULL;
5241cecc5deSJohnathan Mantey 
5251cecc5deSJohnathan Mantey 	for (i = 0; i < console->n_pollers; i++) {
5261cecc5deSJohnathan Mantey 		poller = console->pollers[i];
5271cecc5deSJohnathan Mantey 
5281cecc5deSJohnathan Mantey 		if (poller->timeout_fn && timerisset(&poller->timeout) &&
5291cecc5deSJohnathan Mantey 		    (!earliest ||
5301cecc5deSJohnathan Mantey 		     (earliest && timercmp(&poller->timeout, earliest, <)))) {
5311cecc5deSJohnathan Mantey 			// poller is buffering data and needs the poll
5321cecc5deSJohnathan Mantey 			// function to timeout.
5331cecc5deSJohnathan Mantey 			earliest = &poller->timeout;
5341cecc5deSJohnathan Mantey 		}
5351cecc5deSJohnathan Mantey 	}
5361cecc5deSJohnathan Mantey 
5371cecc5deSJohnathan Mantey 	if (earliest) {
5381cecc5deSJohnathan Mantey 		if (timercmp(earliest, cur_time, >)) {
5391cecc5deSJohnathan Mantey 			/* recalculate the timeout period, time period has
5401cecc5deSJohnathan Mantey 			 * not elapsed */
5411cecc5deSJohnathan Mantey 			timersub(earliest, cur_time, &interval);
5421cecc5deSJohnathan Mantey 			return ((interval.tv_sec * 1000) +
5431cecc5deSJohnathan Mantey 				(interval.tv_usec / 1000));
5440b7b0477SAndrew Jeffery 		} /* return from poll immediately */
5451cecc5deSJohnathan Mantey 		return 0;
5460b7b0477SAndrew Jeffery 
5470b7b0477SAndrew Jeffery 	} /* poll indefinitely */
5481cecc5deSJohnathan Mantey 	return -1;
5491cecc5deSJohnathan Mantey }
5501cecc5deSJohnathan Mantey 
5511cecc5deSJohnathan Mantey static int call_pollers(struct console *console, struct timeval *cur_time)
552329a35f5SJeremy Kerr {
553329a35f5SJeremy Kerr 	struct poller *poller;
554329a35f5SJeremy Kerr 	struct pollfd *pollfd;
555329a35f5SJeremy Kerr 	enum poller_ret prc;
556b70f8713SAndrew Jeffery 	int i;
557b70f8713SAndrew Jeffery 	int rc;
558d831f960SJeremy Kerr 
5591a0e03b4SJeremy Kerr 	rc = 0;
5601a0e03b4SJeremy Kerr 
561329a35f5SJeremy Kerr 	/*
562329a35f5SJeremy Kerr 	 * Process poll events by iterating through the pollers and pollfds
563329a35f5SJeremy Kerr 	 * in-step, calling any pollers that we've found revents for.
564329a35f5SJeremy Kerr 	 */
565329a35f5SJeremy Kerr 	for (i = 0; i < console->n_pollers; i++) {
566329a35f5SJeremy Kerr 		poller = console->pollers[i];
567329a35f5SJeremy Kerr 		pollfd = &console->pollfds[i];
5681cecc5deSJohnathan Mantey 		prc = POLLER_OK;
5691a0e03b4SJeremy Kerr 
5701cecc5deSJohnathan Mantey 		/* process pending events... */
5711cecc5deSJohnathan Mantey 		if (pollfd->revents) {
5721cecc5deSJohnathan Mantey 			prc = poller->event_fn(poller->handler, pollfd->revents,
573329a35f5SJeremy Kerr 					       poller->data);
5742834c5b1SAndrew Jeffery 			if (prc == POLLER_EXIT) {
575329a35f5SJeremy Kerr 				rc = -1;
5762834c5b1SAndrew Jeffery 			} else if (prc == POLLER_REMOVE) {
577329a35f5SJeremy Kerr 				poller->remove = true;
578329a35f5SJeremy Kerr 			}
5792834c5b1SAndrew Jeffery 		}
580329a35f5SJeremy Kerr 
5811cecc5deSJohnathan Mantey 		if ((prc == POLLER_OK) && poller->timeout_fn &&
5821cecc5deSJohnathan Mantey 		    timerisset(&poller->timeout) &&
5831cecc5deSJohnathan Mantey 		    timercmp(&poller->timeout, cur_time, <=)) {
5841cecc5deSJohnathan Mantey 			/* One of the ringbuffer consumers is buffering the
5851cecc5deSJohnathan Mantey 			data stream. The amount of idle time the consumer
5861cecc5deSJohnathan Mantey 			desired has expired.  Process the buffered data for
5871cecc5deSJohnathan Mantey 			transmission. */
5881cecc5deSJohnathan Mantey 			timerclear(&poller->timeout);
5891cecc5deSJohnathan Mantey 			prc = poller->timeout_fn(poller->handler, poller->data);
5901cecc5deSJohnathan Mantey 			if (prc == POLLER_EXIT) {
5911cecc5deSJohnathan Mantey 				rc = -1;
5921cecc5deSJohnathan Mantey 			} else if (prc == POLLER_REMOVE) {
5931cecc5deSJohnathan Mantey 				poller->remove = true;
5941cecc5deSJohnathan Mantey 			}
5951cecc5deSJohnathan Mantey 		}
5961cecc5deSJohnathan Mantey 	}
5971cecc5deSJohnathan Mantey 
598329a35f5SJeremy Kerr 	/**
599329a35f5SJeremy Kerr 	 * Process deferred removals; restarting each time we unregister, as
600329a35f5SJeremy Kerr 	 * the array will have changed
601329a35f5SJeremy Kerr 	 */
602329a35f5SJeremy Kerr 	for (;;) {
603329a35f5SJeremy Kerr 		bool removed = false;
604329a35f5SJeremy Kerr 
605329a35f5SJeremy Kerr 		for (i = 0; i < console->n_pollers; i++) {
606329a35f5SJeremy Kerr 			poller = console->pollers[i];
607329a35f5SJeremy Kerr 			if (poller->remove) {
60855c9712dSJeremy Kerr 				console_poller_unregister(console, poller);
609329a35f5SJeremy Kerr 				removed = true;
610329a35f5SJeremy Kerr 				break;
611329a35f5SJeremy Kerr 			}
612329a35f5SJeremy Kerr 		}
6132834c5b1SAndrew Jeffery 		if (!removed) {
614329a35f5SJeremy Kerr 			break;
6151a0e03b4SJeremy Kerr 		}
6162834c5b1SAndrew Jeffery 	}
6171a0e03b4SJeremy Kerr 
6181a0e03b4SJeremy Kerr 	return rc;
6191a0e03b4SJeremy Kerr }
6201a0e03b4SJeremy Kerr 
621769cee1aSJeremy Kerr static void sighandler(int signal)
622769cee1aSJeremy Kerr {
6232834c5b1SAndrew Jeffery 	if (signal == SIGINT) {
624769cee1aSJeremy Kerr 		sigint = true;
625769cee1aSJeremy Kerr 	}
6262834c5b1SAndrew Jeffery }
627769cee1aSJeremy Kerr 
6281a0e03b4SJeremy Kerr int run_console(struct console *console)
6291a0e03b4SJeremy Kerr {
6305c359cc6SAndrew Jeffery 	sighandler_t sighandler_save = signal(SIGINT, sighandler);
6311cecc5deSJohnathan Mantey 	struct timeval tv;
6325c359cc6SAndrew Jeffery 	long timeout;
6335c359cc6SAndrew Jeffery 	ssize_t rc;
634769cee1aSJeremy Kerr 
635769cee1aSJeremy Kerr 	rc = 0;
636769cee1aSJeremy Kerr 
637d831f960SJeremy Kerr 	for (;;) {
638d831f960SJeremy Kerr 		uint8_t buf[4096];
639d831f960SJeremy Kerr 
6401764145dSJeremy Kerr 		BUILD_ASSERT(sizeof(buf) <= buffer_size);
6411764145dSJeremy Kerr 
642769cee1aSJeremy Kerr 		if (sigint) {
643769cee1aSJeremy Kerr 			fprintf(stderr, "Received interrupt, exiting\n");
644769cee1aSJeremy Kerr 			break;
645769cee1aSJeremy Kerr 		}
646769cee1aSJeremy Kerr 
6471cecc5deSJohnathan Mantey 		rc = get_current_time(&tv);
6481cecc5deSJohnathan Mantey 		if (rc) {
6491cecc5deSJohnathan Mantey 			warn("Failed to read current time");
6501cecc5deSJohnathan Mantey 			break;
6511cecc5deSJohnathan Mantey 		}
6521cecc5deSJohnathan Mantey 
6531cecc5deSJohnathan Mantey 		timeout = get_poll_timeout(console, &tv);
6541cecc5deSJohnathan Mantey 
655329a35f5SJeremy Kerr 		rc = poll(console->pollfds,
6565c359cc6SAndrew Jeffery 			  console->n_pollers + MAX_INTERNAL_POLLFD,
6575c359cc6SAndrew Jeffery 			  (int)timeout);
6581cecc5deSJohnathan Mantey 
659d831f960SJeremy Kerr 		if (rc < 0) {
660769cee1aSJeremy Kerr 			if (errno == EINTR) {
661769cee1aSJeremy Kerr 				continue;
6620b7b0477SAndrew Jeffery 			}
663d831f960SJeremy Kerr 			warn("poll error");
664769cee1aSJeremy Kerr 			break;
665769cee1aSJeremy Kerr 		}
666d831f960SJeremy Kerr 
667329a35f5SJeremy Kerr 		/* process internal fd first */
668329a35f5SJeremy Kerr 		if (console->pollfds[console->n_pollers].revents) {
6691a0e03b4SJeremy Kerr 			rc = read(console->tty_fd, buf, sizeof(buf));
670d831f960SJeremy Kerr 			if (rc <= 0) {
671d831f960SJeremy Kerr 				warn("Error reading from tty device");
672769cee1aSJeremy Kerr 				rc = -1;
673769cee1aSJeremy Kerr 				break;
674d831f960SJeremy Kerr 			}
675f733c85aSJeremy Kerr 			rc = ringbuffer_queue(console->rb, buf, rc);
6762834c5b1SAndrew Jeffery 			if (rc) {
677769cee1aSJeremy Kerr 				break;
678d831f960SJeremy Kerr 			}
6792834c5b1SAndrew Jeffery 		}
680d831f960SJeremy Kerr 
681f9c8f6caSCheng C Yang 		if (console->pollfds[console->n_pollers + 1].revents) {
682f9c8f6caSCheng C Yang 			sd_bus_process(console->bus, NULL);
683f9c8f6caSCheng C Yang 		}
684f9c8f6caSCheng C Yang 
685329a35f5SJeremy Kerr 		/* ... and then the pollers */
6861cecc5deSJohnathan Mantey 		rc = call_pollers(console, &tv);
6872834c5b1SAndrew Jeffery 		if (rc) {
688769cee1aSJeremy Kerr 			break;
6891a0e03b4SJeremy Kerr 		}
6902834c5b1SAndrew Jeffery 	}
691769cee1aSJeremy Kerr 
692769cee1aSJeremy Kerr 	signal(SIGINT, sighandler_save);
693f9c8f6caSCheng C Yang 	sd_bus_unref(console->bus);
694769cee1aSJeremy Kerr 
695769cee1aSJeremy Kerr 	return rc ? -1 : 0;
6961a0e03b4SJeremy Kerr }
697d831f960SJeremy Kerr static const struct option options[] = {
698d66195c1SJeremy Kerr 	{ "config", required_argument, 0, 'c' },
699f5858b5bSJoel Stanley 	{ 0, 0, 0, 0 },
700d831f960SJeremy Kerr };
701d831f960SJeremy Kerr 
702d831f960SJeremy Kerr int main(int argc, char **argv)
703d831f960SJeremy Kerr {
704d66195c1SJeremy Kerr 	const char *config_filename = NULL;
7056221ce94SVishwanatha Subbanna 	const char *config_tty_kname = NULL;
7061a0e03b4SJeremy Kerr 	struct console *console;
707d66195c1SJeremy Kerr 	struct config *config;
708d66195c1SJeremy Kerr 	int rc;
709d831f960SJeremy Kerr 
710d831f960SJeremy Kerr 	for (;;) {
711b70f8713SAndrew Jeffery 		int c;
712b70f8713SAndrew Jeffery 		int idx;
713d831f960SJeremy Kerr 
714d66195c1SJeremy Kerr 		c = getopt_long(argc, argv, "c:", options, &idx);
7152834c5b1SAndrew Jeffery 		if (c == -1) {
716d831f960SJeremy Kerr 			break;
7172834c5b1SAndrew Jeffery 		}
718d831f960SJeremy Kerr 
719d831f960SJeremy Kerr 		switch (c) {
720d66195c1SJeremy Kerr 		case 'c':
721d66195c1SJeremy Kerr 			config_filename = optarg;
722d831f960SJeremy Kerr 			break;
723d831f960SJeremy Kerr 		case 'h':
724d831f960SJeremy Kerr 		case '?':
725d831f960SJeremy Kerr 			usage(argv[0]);
726d66195c1SJeremy Kerr 			return EXIT_SUCCESS;
727d831f960SJeremy Kerr 		}
728d831f960SJeremy Kerr 	}
729d831f960SJeremy Kerr 
7302834c5b1SAndrew Jeffery 	if (optind < argc) {
7316221ce94SVishwanatha Subbanna 		config_tty_kname = argv[optind];
7322834c5b1SAndrew Jeffery 	}
7336221ce94SVishwanatha Subbanna 
734d66195c1SJeremy Kerr 	console = malloc(sizeof(struct console));
735d66195c1SJeremy Kerr 	memset(console, 0, sizeof(*console));
736a72711afSAndrew Jeffery 	console->pollfds =
737a72711afSAndrew Jeffery 		calloc(MAX_INTERNAL_POLLFD, sizeof(*console->pollfds));
738f733c85aSJeremy Kerr 	console->rb = ringbuffer_init(buffer_size);
739329a35f5SJeremy Kerr 
740d66195c1SJeremy Kerr 	config = config_init(config_filename);
741d66195c1SJeremy Kerr 	if (!config) {
742d66195c1SJeremy Kerr 		warnx("Can't read configuration, exiting.");
74329c59c44SAndrew Jeffery 		rc = -1;
744d66195c1SJeremy Kerr 		goto out_free;
745d831f960SJeremy Kerr 	}
746d831f960SJeremy Kerr 
747b14ca19cSNinad Palsule 	if (set_socket_info(console, config)) {
74829c59c44SAndrew Jeffery 		rc = -1;
74929c59c44SAndrew Jeffery 		goto out_config_fini;
750b14ca19cSNinad Palsule 	}
751b14ca19cSNinad Palsule 
752*d769eecfSAndrew Jeffery 	rc = tty_init(console, config, config_tty_kname);
7532834c5b1SAndrew Jeffery 	if (rc) {
754d66195c1SJeremy Kerr 		goto out_config_fini;
7552834c5b1SAndrew Jeffery 	}
756d831f960SJeremy Kerr 
757f9c8f6caSCheng C Yang 	dbus_init(console, config);
758f9c8f6caSCheng C Yang 
759d47963e5SJeremy Kerr 	handlers_init(console, config);
760d831f960SJeremy Kerr 
7611a0e03b4SJeremy Kerr 	rc = run_console(console);
762d831f960SJeremy Kerr 
7631a0e03b4SJeremy Kerr 	handlers_fini(console);
764d831f960SJeremy Kerr 
765d66195c1SJeremy Kerr out_config_fini:
766d66195c1SJeremy Kerr 	config_fini(config);
767d66195c1SJeremy Kerr 
768957818b4SJeremy Kerr out_free:
76989ea8198SJeremy Kerr 	free(console->pollers);
77089ea8198SJeremy Kerr 	free(console->pollfds);
7711a0e03b4SJeremy Kerr 	free(console->tty_sysfs_devnode);
7721a0e03b4SJeremy Kerr 	free(console->tty_dev);
7731a0e03b4SJeremy Kerr 	free(console);
774d831f960SJeremy Kerr 
775d831f960SJeremy Kerr 	return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
776d831f960SJeremy Kerr }
777