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