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