1d831f960SJeremy Kerr /** 2d831f960SJeremy Kerr * Console server process for OpenBMC 3d831f960SJeremy Kerr * 49326d779SJeremy Kerr * Copyright © 2016 IBM Corporation 59326d779SJeremy Kerr * 69326d779SJeremy Kerr * Licensed under the Apache License, Version 2.0 (the "License"); 79326d779SJeremy Kerr * you may not use this file except in compliance with the License. 89326d779SJeremy Kerr * You may obtain a copy of the License at 99326d779SJeremy Kerr * 109326d779SJeremy Kerr * http://www.apache.org/licenses/LICENSE-2.0 119326d779SJeremy Kerr * 129326d779SJeremy Kerr * Unless required by applicable law or agreed to in writing, software 139326d779SJeremy Kerr * distributed under the License is distributed on an "AS IS" BASIS, 149326d779SJeremy Kerr * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 159326d779SJeremy Kerr * See the License for the specific language governing permissions and 169326d779SJeremy Kerr * limitations under the License. 17d831f960SJeremy Kerr */ 18d831f960SJeremy Kerr 19329a35f5SJeremy Kerr #include <assert.h> 20769cee1aSJeremy Kerr #include <errno.h> 21769cee1aSJeremy Kerr #include <signal.h> 22d831f960SJeremy Kerr #include <stdint.h> 23d831f960SJeremy Kerr #include <stdbool.h> 24d831f960SJeremy Kerr #include <stdlib.h> 25d831f960SJeremy Kerr #include <stdio.h> 26d831f960SJeremy Kerr #include <fcntl.h> 27d831f960SJeremy Kerr #include <unistd.h> 28d831f960SJeremy Kerr #include <err.h> 29d831f960SJeremy Kerr #include <string.h> 30d831f960SJeremy Kerr #include <getopt.h> 3117217845SJeremy Kerr #include <limits.h> 321cecc5deSJohnathan Mantey #include <time.h> 3354e9569dSJeremy Kerr #include <termios.h> 34d831f960SJeremy Kerr 35d831f960SJeremy Kerr #include <sys/types.h> 361cecc5deSJohnathan Mantey #include <sys/time.h> 37b14ca19cSNinad Palsule #include <sys/socket.h> 3887e344cdSJoel Stanley #include <poll.h> 39d831f960SJeremy Kerr 401a0e03b4SJeremy Kerr #include "console-server.h" 41d831f960SJeremy Kerr 42f733c85aSJeremy Kerr /* size of the shared backlog ringbuffer */ 435db8c792SAndrew Jeffery const size_t buffer_size = 128ul * 1024ul; 44f733c85aSJeremy Kerr 45769cee1aSJeremy Kerr /* state shared with the signal handler */ 46769cee1aSJeremy Kerr static bool sigint; 47329a35f5SJeremy Kerr 48d831f960SJeremy Kerr static void usage(const char *progname) 49d831f960SJeremy Kerr { 50d831f960SJeremy Kerr fprintf(stderr, 516221ce94SVishwanatha Subbanna "usage: %s [options] <DEVICE>\n" 52d831f960SJeremy Kerr "\n" 53d831f960SJeremy Kerr "Options:\n" 54d66195c1SJeremy Kerr " --config <FILE> Use FILE for configuration\n" 55d831f960SJeremy Kerr "", 56d831f960SJeremy Kerr progname); 57d831f960SJeremy Kerr } 58d831f960SJeremy Kerr 5917217845SJeremy Kerr /* populates tty_dev and tty_sysfs_devnode, using the tty kernel name */ 601a0e03b4SJeremy Kerr static int tty_find_device(struct console *console) 6117217845SJeremy Kerr { 62d3cb9c22SAndrew Jeffery char *tty_class_device_link = NULL; 63d3cb9c22SAndrew Jeffery char *tty_path_input_real = NULL; 64d3cb9c22SAndrew Jeffery char *tty_device_tty_dir = NULL; 65d3cb9c22SAndrew Jeffery char *tty_device_reldir = NULL; 66d3cb9c22SAndrew Jeffery char *tty_path_input = NULL; 67d3cb9c22SAndrew Jeffery char *tty_kname_real = NULL; 6817217845SJeremy Kerr int rc; 6917217845SJeremy Kerr 7045ad7676SYi Li /* udev may rename the tty name with a symbol link, try to resolve */ 7145ad7676SYi Li rc = asprintf(&tty_path_input, "/dev/%s", console->tty_kname); 722834c5b1SAndrew Jeffery if (rc < 0) { 7317217845SJeremy Kerr return -1; 742834c5b1SAndrew Jeffery } 7517217845SJeremy Kerr 7645ad7676SYi Li tty_path_input_real = realpath(tty_path_input, NULL); 7745ad7676SYi Li if (!tty_path_input_real) { 7845ad7676SYi Li warn("Can't find realpath for /dev/%s", console->tty_kname); 7915792aa7SAndrew Jeffery rc = -1; 8045ad7676SYi Li goto out_free; 8145ad7676SYi Li } 8245ad7676SYi Li 8345ad7676SYi Li tty_kname_real = basename(tty_path_input_real); 8445ad7676SYi Li if (!tty_kname_real) { 8545ad7676SYi Li warn("Can't find real name for /dev/%s", console->tty_kname); 8615792aa7SAndrew Jeffery rc = -1; 8745ad7676SYi Li goto out_free; 8845ad7676SYi Li } 8945ad7676SYi Li 90a72711afSAndrew Jeffery rc = asprintf(&tty_class_device_link, "/sys/class/tty/%s", 91a72711afSAndrew Jeffery tty_kname_real); 922834c5b1SAndrew Jeffery if (rc < 0) { 9345ad7676SYi Li goto out_free; 942834c5b1SAndrew Jeffery } 9545ad7676SYi Li 9617217845SJeremy Kerr tty_device_tty_dir = realpath(tty_class_device_link, NULL); 9745ad7676SYi Li if (!tty_device_tty_dir) { 9845ad7676SYi Li warn("Can't query sysfs for device %s", tty_kname_real); 9915792aa7SAndrew Jeffery rc = -1; 10017217845SJeremy Kerr goto out_free; 10117217845SJeremy Kerr } 10217217845SJeremy Kerr 10317217845SJeremy Kerr rc = asprintf(&tty_device_reldir, "%s/../../", tty_device_tty_dir); 1042834c5b1SAndrew Jeffery if (rc < 0) { 10517217845SJeremy Kerr goto out_free; 1062834c5b1SAndrew Jeffery } 10717217845SJeremy Kerr 1081a0e03b4SJeremy Kerr console->tty_sysfs_devnode = realpath(tty_device_reldir, NULL); 1092834c5b1SAndrew Jeffery if (!console->tty_sysfs_devnode) { 11045ad7676SYi Li warn("Can't find parent device for %s", tty_kname_real); 1112834c5b1SAndrew Jeffery } 11217217845SJeremy Kerr 11345ad7676SYi Li rc = asprintf(&console->tty_dev, "/dev/%s", tty_kname_real); 1142834c5b1SAndrew Jeffery if (rc < 0) { 11517217845SJeremy Kerr goto out_free; 1162834c5b1SAndrew Jeffery } 11717217845SJeremy Kerr 11817217845SJeremy Kerr rc = 0; 11917217845SJeremy Kerr 12017217845SJeremy Kerr out_free: 12117217845SJeremy Kerr free(tty_class_device_link); 12217217845SJeremy Kerr free(tty_device_tty_dir); 12317217845SJeremy Kerr free(tty_device_reldir); 12445ad7676SYi Li free(tty_path_input); 12545ad7676SYi Li free(tty_path_input_real); 12617217845SJeremy Kerr return rc; 12717217845SJeremy Kerr } 12817217845SJeremy Kerr 1291a0e03b4SJeremy Kerr static int tty_set_sysfs_attr(struct console *console, const char *name, 130957818b4SJeremy Kerr int value) 131957818b4SJeremy Kerr { 132957818b4SJeremy Kerr char *path; 133957818b4SJeremy Kerr FILE *fp; 134957818b4SJeremy Kerr int rc; 135957818b4SJeremy Kerr 1361a0e03b4SJeremy Kerr rc = asprintf(&path, "%s/%s", console->tty_sysfs_devnode, name); 1372834c5b1SAndrew Jeffery if (rc < 0) { 138957818b4SJeremy Kerr return -1; 1392834c5b1SAndrew Jeffery } 140957818b4SJeremy Kerr 141957818b4SJeremy Kerr fp = fopen(path, "w"); 142957818b4SJeremy Kerr if (!fp) { 143a72711afSAndrew Jeffery warn("Can't access attribute %s on device %s", name, 144a72711afSAndrew Jeffery console->tty_kname); 145957818b4SJeremy Kerr rc = -1; 146957818b4SJeremy Kerr goto out_free; 147957818b4SJeremy Kerr } 148957818b4SJeremy Kerr setvbuf(fp, NULL, _IONBF, 0); 149957818b4SJeremy Kerr 150957818b4SJeremy Kerr rc = fprintf(fp, "0x%x", value); 1512834c5b1SAndrew Jeffery if (rc < 0) { 152a72711afSAndrew Jeffery warn("Error writing to %s attribute of device %s", name, 153a72711afSAndrew Jeffery console->tty_kname); 1542834c5b1SAndrew Jeffery } 155957818b4SJeremy Kerr fclose(fp); 156957818b4SJeremy Kerr 157957818b4SJeremy Kerr out_free: 158957818b4SJeremy Kerr free(path); 159957818b4SJeremy Kerr return rc; 160957818b4SJeremy Kerr } 161957818b4SJeremy Kerr 162d831f960SJeremy Kerr /** 163c7fbcd48SBenjamin Fair * Set termios attributes on the console tty. 16454e9569dSJeremy Kerr */ 165e258e51fSNinad Palsule void tty_init_termios(struct console *console) 16654e9569dSJeremy Kerr { 16754e9569dSJeremy Kerr struct termios termios; 16854e9569dSJeremy Kerr int rc; 16954e9569dSJeremy Kerr 17054e9569dSJeremy Kerr rc = tcgetattr(console->tty_fd, &termios); 17154e9569dSJeremy Kerr if (rc) { 17254e9569dSJeremy Kerr warn("Can't read tty termios"); 17354e9569dSJeremy Kerr return; 17454e9569dSJeremy Kerr } 17554e9569dSJeremy Kerr 176c7fbcd48SBenjamin Fair if (console->tty_baud) { 1772834c5b1SAndrew Jeffery if (cfsetspeed(&termios, console->tty_baud) < 0) { 178c7fbcd48SBenjamin Fair warn("Couldn't set speeds for %s", console->tty_kname); 179c7fbcd48SBenjamin Fair } 1802834c5b1SAndrew Jeffery } 181c7fbcd48SBenjamin Fair 182c7fbcd48SBenjamin Fair /* Set console to raw mode: we don't want any processing to occur on 183c7fbcd48SBenjamin Fair * the underlying terminal input/output. 184c7fbcd48SBenjamin Fair */ 18554e9569dSJeremy Kerr cfmakeraw(&termios); 186c7fbcd48SBenjamin Fair 18754e9569dSJeremy Kerr rc = tcsetattr(console->tty_fd, TCSANOW, &termios); 1882834c5b1SAndrew Jeffery if (rc) { 189c7fbcd48SBenjamin Fair warn("Can't set terminal options for %s", console->tty_kname); 19054e9569dSJeremy Kerr } 1912834c5b1SAndrew Jeffery } 19254e9569dSJeremy Kerr 19354e9569dSJeremy Kerr /** 194d831f960SJeremy Kerr * Open and initialise the serial device 195d831f960SJeremy Kerr */ 1961a0e03b4SJeremy Kerr static int tty_init_io(struct console *console) 197d831f960SJeremy Kerr { 1982834c5b1SAndrew Jeffery if (console->tty_sirq) { 1991a0e03b4SJeremy Kerr tty_set_sysfs_attr(console, "sirq", console->tty_sirq); 2002834c5b1SAndrew Jeffery } 2012834c5b1SAndrew Jeffery if (console->tty_lpc_addr) { 2021a0e03b4SJeremy Kerr tty_set_sysfs_attr(console, "lpc_address", 2031a0e03b4SJeremy Kerr console->tty_lpc_addr); 2042834c5b1SAndrew Jeffery } 205957818b4SJeremy Kerr 2061a0e03b4SJeremy Kerr console->tty_fd = open(console->tty_dev, O_RDWR); 2071a0e03b4SJeremy Kerr if (console->tty_fd <= 0) { 2081a0e03b4SJeremy Kerr warn("Can't open tty %s", console->tty_dev); 209d831f960SJeremy Kerr return -1; 210d831f960SJeremy Kerr } 211d831f960SJeremy Kerr 212d831f960SJeremy Kerr /* Disable character delay. We may want to later enable this when 213d831f960SJeremy Kerr * we detect larger amounts of data 214d831f960SJeremy Kerr */ 2151a0e03b4SJeremy Kerr fcntl(console->tty_fd, F_SETFL, FNDELAY); 216d831f960SJeremy Kerr 21754e9569dSJeremy Kerr tty_init_termios(console); 21854e9569dSJeremy Kerr 219329a35f5SJeremy Kerr console->pollfds[console->n_pollers].fd = console->tty_fd; 220329a35f5SJeremy Kerr console->pollfds[console->n_pollers].events = POLLIN; 221329a35f5SJeremy Kerr 222d831f960SJeremy Kerr return 0; 223d831f960SJeremy Kerr } 224d831f960SJeremy Kerr 225d66195c1SJeremy Kerr static int tty_init(struct console *console, struct config *config) 226d66195c1SJeremy Kerr { 227fd883a88SAndrew Jeffery unsigned long parsed; 228d66195c1SJeremy Kerr const char *val; 229d66195c1SJeremy Kerr char *endp; 230d66195c1SJeremy Kerr int rc; 231d66195c1SJeremy Kerr 232d66195c1SJeremy Kerr val = config_get_value(config, "lpc-address"); 233d66195c1SJeremy Kerr if (val) { 234fd883a88SAndrew Jeffery errno = 0; 235fd883a88SAndrew Jeffery parsed = strtoul(val, &endp, 0); 236fd883a88SAndrew Jeffery if (parsed == ULONG_MAX && errno == ERANGE) { 237fd883a88SAndrew Jeffery warn("Cannot interpret 'lpc-address' value as an unsigned long: '%s'", 238fd883a88SAndrew Jeffery val); 239fd883a88SAndrew Jeffery return -1; 240fd883a88SAndrew Jeffery } 241fd883a88SAndrew Jeffery 242fd883a88SAndrew Jeffery if (parsed > UINT16_MAX) { 243fd883a88SAndrew Jeffery warn("Invalid LPC address '%s'", val); 244fd883a88SAndrew Jeffery return -1; 245fd883a88SAndrew Jeffery } 246fd883a88SAndrew Jeffery 247fd883a88SAndrew Jeffery console->tty_lpc_addr = (uint16_t)parsed; 248d66195c1SJeremy Kerr if (endp == optarg) { 249d66195c1SJeremy Kerr warn("Invalid LPC address: '%s'", val); 250d66195c1SJeremy Kerr return -1; 251d66195c1SJeremy Kerr } 252d66195c1SJeremy Kerr } 253d66195c1SJeremy Kerr 254d66195c1SJeremy Kerr val = config_get_value(config, "sirq"); 255d66195c1SJeremy Kerr if (val) { 256fd883a88SAndrew Jeffery errno = 0; 257fd883a88SAndrew Jeffery parsed = strtoul(val, &endp, 0); 258fd883a88SAndrew Jeffery if (parsed == ULONG_MAX && errno == ERANGE) { 259fd883a88SAndrew Jeffery warn("Cannot interpret 'sirq' value as an unsigned long: '%s'", 260fd883a88SAndrew Jeffery val); 261fd883a88SAndrew Jeffery } 262fd883a88SAndrew Jeffery 2632834c5b1SAndrew Jeffery if (parsed > 16) { 264fd883a88SAndrew Jeffery warn("Invalid LPC SERIRQ: '%s'", val); 2652834c5b1SAndrew Jeffery } 266fd883a88SAndrew Jeffery 267fd883a88SAndrew Jeffery console->tty_sirq = (int)parsed; 2682834c5b1SAndrew Jeffery if (endp == optarg) { 269d66195c1SJeremy Kerr warn("Invalid sirq: '%s'", val); 270d66195c1SJeremy Kerr } 2712834c5b1SAndrew Jeffery } 272d66195c1SJeremy Kerr 273c7fbcd48SBenjamin Fair val = config_get_value(config, "baud"); 274c7fbcd48SBenjamin Fair if (val) { 2752834c5b1SAndrew Jeffery if (config_parse_baud(&console->tty_baud, val)) { 276c7fbcd48SBenjamin Fair warnx("Invalid baud rate: '%s'", val); 277c7fbcd48SBenjamin Fair } 2782834c5b1SAndrew Jeffery } 279c7fbcd48SBenjamin Fair 280d66195c1SJeremy Kerr if (!console->tty_kname) { 281d66195c1SJeremy Kerr warnx("Error: No TTY device specified"); 282d66195c1SJeremy Kerr return -1; 283d66195c1SJeremy Kerr } 284d66195c1SJeremy Kerr 285d66195c1SJeremy Kerr rc = tty_find_device(console); 2862834c5b1SAndrew Jeffery if (rc) { 287d66195c1SJeremy Kerr return rc; 2882834c5b1SAndrew Jeffery } 289d66195c1SJeremy Kerr 290d66195c1SJeremy Kerr rc = tty_init_io(console); 291d66195c1SJeremy Kerr return rc; 292d66195c1SJeremy Kerr } 293d66195c1SJeremy Kerr 2941a0e03b4SJeremy Kerr int console_data_out(struct console *console, const uint8_t *data, size_t len) 295d831f960SJeremy Kerr { 2961a0e03b4SJeremy Kerr return write_buf_to_fd(console->tty_fd, data, len); 297d831f960SJeremy Kerr } 298d831f960SJeremy Kerr 299b14ca19cSNinad Palsule /* Read console if from config and prepare a socket name */ 300b14ca19cSNinad Palsule static int set_socket_info(struct console *console, struct config *config) 301b14ca19cSNinad Palsule { 302b14ca19cSNinad Palsule ssize_t len; 303b14ca19cSNinad Palsule 304b14ca19cSNinad Palsule console->console_id = config_get_value(config, "socket-id"); 305b14ca19cSNinad Palsule if (!console->console_id) { 306b14ca19cSNinad Palsule warnx("Error: The socket-id is not set in the config file"); 307b14ca19cSNinad Palsule return EXIT_FAILURE; 308b14ca19cSNinad Palsule } 309b14ca19cSNinad Palsule 310b14ca19cSNinad Palsule /* Get the socket name/path */ 311b14ca19cSNinad Palsule len = console_socket_path(console->socket_name, console->console_id); 312b14ca19cSNinad Palsule if (len < 0) { 313b14ca19cSNinad Palsule warn("Failed to set socket path: %s", strerror(errno)); 314b14ca19cSNinad Palsule return EXIT_FAILURE; 315b14ca19cSNinad Palsule } 316b14ca19cSNinad Palsule 317b14ca19cSNinad Palsule /* Socket name is not a null terminated string hence save the length */ 318b14ca19cSNinad Palsule console->socket_name_len = len; 319b14ca19cSNinad Palsule 320b14ca19cSNinad Palsule return 0; 321b14ca19cSNinad Palsule } 322b14ca19cSNinad Palsule 323d47963e5SJeremy Kerr static void handlers_init(struct console *console, struct config *config) 324d831f960SJeremy Kerr { 325b70f8713SAndrew Jeffery /* NOLINTBEGIN(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) */ 326b70f8713SAndrew Jeffery extern struct handler *__start_handlers; 327b70f8713SAndrew Jeffery extern struct handler *__stop_handlers; 328b70f8713SAndrew Jeffery /* NOLINTEND(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) */ 3291a0e03b4SJeremy Kerr struct handler *handler; 330b70f8713SAndrew Jeffery int i; 331b70f8713SAndrew Jeffery int rc; 332d831f960SJeremy Kerr 3331a0e03b4SJeremy Kerr console->n_handlers = &__stop_handlers - &__start_handlers; 3341a0e03b4SJeremy Kerr console->handlers = &__start_handlers; 335d831f960SJeremy Kerr 3365c359cc6SAndrew Jeffery printf("%ld handler%s\n", console->n_handlers, 3371a0e03b4SJeremy Kerr console->n_handlers == 1 ? "" : "s"); 338d831f960SJeremy Kerr 3391a0e03b4SJeremy Kerr for (i = 0; i < console->n_handlers; i++) { 3401a0e03b4SJeremy Kerr handler = console->handlers[i]; 3411a0e03b4SJeremy Kerr 342021b91f0SJeremy Kerr rc = 0; 3432834c5b1SAndrew Jeffery if (handler->init) { 344021b91f0SJeremy Kerr rc = handler->init(handler, console, config); 3452834c5b1SAndrew Jeffery } 346021b91f0SJeremy Kerr 347021b91f0SJeremy Kerr handler->active = rc == 0; 348021b91f0SJeremy Kerr 349021b91f0SJeremy Kerr printf(" %s [%sactive]\n", handler->name, 350021b91f0SJeremy Kerr handler->active ? "" : "in"); 351d831f960SJeremy Kerr } 352d831f960SJeremy Kerr } 353d831f960SJeremy Kerr 3541a0e03b4SJeremy Kerr static void handlers_fini(struct console *console) 355d831f960SJeremy Kerr { 3561a0e03b4SJeremy Kerr struct handler *handler; 3571a0e03b4SJeremy Kerr int i; 3581a0e03b4SJeremy Kerr 3591a0e03b4SJeremy Kerr for (i = 0; i < console->n_handlers; i++) { 3601a0e03b4SJeremy Kerr handler = console->handlers[i]; 3612834c5b1SAndrew Jeffery if (handler->fini && handler->active) { 3621a0e03b4SJeremy Kerr handler->fini(handler); 3631a0e03b4SJeremy Kerr } 364d831f960SJeremy Kerr } 3652834c5b1SAndrew Jeffery } 366d831f960SJeremy Kerr 3671cecc5deSJohnathan Mantey static int get_current_time(struct timeval *tv) 3681cecc5deSJohnathan Mantey { 3691cecc5deSJohnathan Mantey struct timespec t; 3701cecc5deSJohnathan Mantey int rc; 3711cecc5deSJohnathan Mantey 3721cecc5deSJohnathan Mantey /* 3731cecc5deSJohnathan Mantey * We use clock_gettime(CLOCK_MONOTONIC) so we're immune to 3741cecc5deSJohnathan Mantey * local time changes. However, a struct timeval is more 3751cecc5deSJohnathan Mantey * convenient for calculations, so convert to that. 3761cecc5deSJohnathan Mantey */ 3771cecc5deSJohnathan Mantey rc = clock_gettime(CLOCK_MONOTONIC, &t); 3782834c5b1SAndrew Jeffery if (rc) { 3791cecc5deSJohnathan Mantey return rc; 3802834c5b1SAndrew Jeffery } 3811cecc5deSJohnathan Mantey 3821cecc5deSJohnathan Mantey tv->tv_sec = t.tv_sec; 3831cecc5deSJohnathan Mantey tv->tv_usec = t.tv_nsec / 1000; 3841cecc5deSJohnathan Mantey 3851cecc5deSJohnathan Mantey return 0; 3861cecc5deSJohnathan Mantey } 3871cecc5deSJohnathan Mantey 388a72711afSAndrew Jeffery struct ringbuffer_consumer * 389a72711afSAndrew Jeffery console_ringbuffer_consumer_register(struct console *console, 390f733c85aSJeremy Kerr ringbuffer_poll_fn_t poll_fn, void *data) 391d831f960SJeremy Kerr { 392f733c85aSJeremy Kerr return ringbuffer_consumer_register(console->rb, poll_fn, data); 393d831f960SJeremy Kerr } 394d831f960SJeremy Kerr 39555c9712dSJeremy Kerr struct poller *console_poller_register(struct console *console, 396a72711afSAndrew Jeffery struct handler *handler, 397a72711afSAndrew Jeffery poller_event_fn_t poller_fn, 3981cecc5deSJohnathan Mantey poller_timeout_fn_t timeout_fn, int fd, 3991cecc5deSJohnathan Mantey int events, void *data) 400d831f960SJeremy Kerr { 401329a35f5SJeremy Kerr struct poller *poller; 4025c359cc6SAndrew Jeffery long n; 403329a35f5SJeremy Kerr 404329a35f5SJeremy Kerr poller = malloc(sizeof(*poller)); 405329a35f5SJeremy Kerr poller->remove = false; 406329a35f5SJeremy Kerr poller->handler = handler; 4071cecc5deSJohnathan Mantey poller->event_fn = poller_fn; 4081cecc5deSJohnathan Mantey poller->timeout_fn = timeout_fn; 409329a35f5SJeremy Kerr poller->data = data; 410329a35f5SJeremy Kerr 411329a35f5SJeremy Kerr /* add one to our pollers array */ 412329a35f5SJeremy Kerr n = console->n_pollers++; 41391b52175SAndrew Jeffery /* 41491b52175SAndrew Jeffery * We're managing an array of pointers to aggregates, so don't warn about sizeof() on a 41591b52175SAndrew Jeffery * pointer type. 41691b52175SAndrew Jeffery */ 41791b52175SAndrew Jeffery /* NOLINTBEGIN(bugprone-sizeof-expression) */ 41891b52175SAndrew Jeffery console->pollers = reallocarray(console->pollers, console->n_pollers, 41991b52175SAndrew Jeffery sizeof(*console->pollers)); 42091b52175SAndrew Jeffery /* NOLINTEND(bugprone-sizeof-expression) */ 421329a35f5SJeremy Kerr 422329a35f5SJeremy Kerr console->pollers[n] = poller; 423329a35f5SJeremy Kerr 424329a35f5SJeremy Kerr /* increase pollfds array too */ 425a72711afSAndrew Jeffery console->pollfds = 42691b52175SAndrew Jeffery reallocarray(console->pollfds, 42791b52175SAndrew Jeffery (MAX_INTERNAL_POLLFD + console->n_pollers), 42891b52175SAndrew Jeffery sizeof(*console->pollfds)); 429329a35f5SJeremy Kerr 430329a35f5SJeremy Kerr /* shift the end pollfds up by one */ 431a72711afSAndrew Jeffery memcpy(&console->pollfds[n + 1], &console->pollfds[n], 432f9c8f6caSCheng C Yang sizeof(*console->pollfds) * MAX_INTERNAL_POLLFD); 433329a35f5SJeremy Kerr 434329a35f5SJeremy Kerr console->pollfds[n].fd = fd; 4355c359cc6SAndrew Jeffery console->pollfds[n].events = (short)(events & 0x7fff); 436329a35f5SJeremy Kerr 437329a35f5SJeremy Kerr return poller; 438329a35f5SJeremy Kerr } 439329a35f5SJeremy Kerr 440a72711afSAndrew Jeffery void console_poller_unregister(struct console *console, struct poller *poller) 441329a35f5SJeremy Kerr { 442329a35f5SJeremy Kerr int i; 443329a35f5SJeremy Kerr 444329a35f5SJeremy Kerr /* find the entry in our pollers array */ 4452834c5b1SAndrew Jeffery for (i = 0; i < console->n_pollers; i++) { 4462834c5b1SAndrew Jeffery if (console->pollers[i] == poller) { 447329a35f5SJeremy Kerr break; 4482834c5b1SAndrew Jeffery } 4492834c5b1SAndrew Jeffery } 450329a35f5SJeremy Kerr 451329a35f5SJeremy Kerr assert(i < console->n_pollers); 452329a35f5SJeremy Kerr 453329a35f5SJeremy Kerr console->n_pollers--; 454329a35f5SJeremy Kerr 45591b52175SAndrew Jeffery /* 45691b52175SAndrew Jeffery * Remove the item from the pollers array... 45791b52175SAndrew Jeffery * 45891b52175SAndrew Jeffery * We're managing an array of pointers to aggregates, so don't warn about sizeof() on a 45991b52175SAndrew Jeffery * pointer type. 46091b52175SAndrew Jeffery */ 46191b52175SAndrew Jeffery /* NOLINTBEGIN(bugprone-sizeof-expression) */ 462329a35f5SJeremy Kerr memmove(&console->pollers[i], &console->pollers[i + 1], 463a72711afSAndrew Jeffery sizeof(*console->pollers) * (console->n_pollers - i)); 464329a35f5SJeremy Kerr 46591b52175SAndrew Jeffery console->pollers = reallocarray(console->pollers, console->n_pollers, 46691b52175SAndrew Jeffery sizeof(*console->pollers)); 46791b52175SAndrew Jeffery /* NOLINTEND(bugprone-sizeof-expression) */ 468329a35f5SJeremy Kerr 469329a35f5SJeremy Kerr /* ... and the pollfds array */ 470329a35f5SJeremy Kerr memmove(&console->pollfds[i], &console->pollfds[i + 1], 471329a35f5SJeremy Kerr sizeof(*console->pollfds) * 472f9c8f6caSCheng C Yang (MAX_INTERNAL_POLLFD + console->n_pollers - i)); 473329a35f5SJeremy Kerr 474a72711afSAndrew Jeffery console->pollfds = 47591b52175SAndrew Jeffery reallocarray(console->pollfds, 47691b52175SAndrew Jeffery (MAX_INTERNAL_POLLFD + console->n_pollers), 47791b52175SAndrew Jeffery sizeof(*console->pollfds)); 478329a35f5SJeremy Kerr 479329a35f5SJeremy Kerr free(poller); 480329a35f5SJeremy Kerr } 481329a35f5SJeremy Kerr 4826b1fed27SJeremy Kerr void console_poller_set_events(struct console *console, struct poller *poller, 4836b1fed27SJeremy Kerr int events) 4846b1fed27SJeremy Kerr { 4856b1fed27SJeremy Kerr int i; 4866b1fed27SJeremy Kerr 4876b1fed27SJeremy Kerr /* find the entry in our pollers array */ 4882834c5b1SAndrew Jeffery for (i = 0; i < console->n_pollers; i++) { 4892834c5b1SAndrew Jeffery if (console->pollers[i] == poller) { 4906b1fed27SJeremy Kerr break; 4912834c5b1SAndrew Jeffery } 4922834c5b1SAndrew Jeffery } 4936b1fed27SJeremy Kerr 4945c359cc6SAndrew Jeffery console->pollfds[i].events = (short)(events & 0x7fff); 4956b1fed27SJeremy Kerr } 4966b1fed27SJeremy Kerr 497fd048328SAndrew Jeffery void console_poller_set_timeout(struct console *console __attribute__((unused)), 498fd048328SAndrew Jeffery struct poller *poller, const struct timeval *tv) 4991cecc5deSJohnathan Mantey { 5001cecc5deSJohnathan Mantey struct timeval now; 5011cecc5deSJohnathan Mantey int rc; 5021cecc5deSJohnathan Mantey 5031cecc5deSJohnathan Mantey rc = get_current_time(&now); 5042834c5b1SAndrew Jeffery if (rc) { 5051cecc5deSJohnathan Mantey return; 5062834c5b1SAndrew Jeffery } 5071cecc5deSJohnathan Mantey 5081cecc5deSJohnathan Mantey timeradd(&now, tv, &poller->timeout); 5091cecc5deSJohnathan Mantey } 5101cecc5deSJohnathan Mantey 5115c359cc6SAndrew Jeffery static long get_poll_timeout(struct console *console, struct timeval *cur_time) 5121cecc5deSJohnathan Mantey { 513b70f8713SAndrew Jeffery struct timeval *earliest; 514b70f8713SAndrew Jeffery struct timeval interval; 5151cecc5deSJohnathan Mantey struct poller *poller; 5161cecc5deSJohnathan Mantey int i; 5171cecc5deSJohnathan Mantey 5181cecc5deSJohnathan Mantey earliest = NULL; 5191cecc5deSJohnathan Mantey 5201cecc5deSJohnathan Mantey for (i = 0; i < console->n_pollers; i++) { 5211cecc5deSJohnathan Mantey poller = console->pollers[i]; 5221cecc5deSJohnathan Mantey 5231cecc5deSJohnathan Mantey if (poller->timeout_fn && timerisset(&poller->timeout) && 5241cecc5deSJohnathan Mantey (!earliest || 5251cecc5deSJohnathan Mantey (earliest && timercmp(&poller->timeout, earliest, <)))) { 5261cecc5deSJohnathan Mantey // poller is buffering data and needs the poll 5271cecc5deSJohnathan Mantey // function to timeout. 5281cecc5deSJohnathan Mantey earliest = &poller->timeout; 5291cecc5deSJohnathan Mantey } 5301cecc5deSJohnathan Mantey } 5311cecc5deSJohnathan Mantey 5321cecc5deSJohnathan Mantey if (earliest) { 5331cecc5deSJohnathan Mantey if (timercmp(earliest, cur_time, >)) { 5341cecc5deSJohnathan Mantey /* recalculate the timeout period, time period has 5351cecc5deSJohnathan Mantey * not elapsed */ 5361cecc5deSJohnathan Mantey timersub(earliest, cur_time, &interval); 5371cecc5deSJohnathan Mantey return ((interval.tv_sec * 1000) + 5381cecc5deSJohnathan Mantey (interval.tv_usec / 1000)); 5390b7b0477SAndrew Jeffery } /* return from poll immediately */ 5401cecc5deSJohnathan Mantey return 0; 5410b7b0477SAndrew Jeffery 5420b7b0477SAndrew Jeffery } /* poll indefinitely */ 5431cecc5deSJohnathan Mantey return -1; 5441cecc5deSJohnathan Mantey } 5451cecc5deSJohnathan Mantey 5461cecc5deSJohnathan Mantey static int call_pollers(struct console *console, struct timeval *cur_time) 547329a35f5SJeremy Kerr { 548329a35f5SJeremy Kerr struct poller *poller; 549329a35f5SJeremy Kerr struct pollfd *pollfd; 550329a35f5SJeremy Kerr enum poller_ret prc; 551b70f8713SAndrew Jeffery int i; 552b70f8713SAndrew Jeffery int rc; 553d831f960SJeremy Kerr 5541a0e03b4SJeremy Kerr rc = 0; 5551a0e03b4SJeremy Kerr 556329a35f5SJeremy Kerr /* 557329a35f5SJeremy Kerr * Process poll events by iterating through the pollers and pollfds 558329a35f5SJeremy Kerr * in-step, calling any pollers that we've found revents for. 559329a35f5SJeremy Kerr */ 560329a35f5SJeremy Kerr for (i = 0; i < console->n_pollers; i++) { 561329a35f5SJeremy Kerr poller = console->pollers[i]; 562329a35f5SJeremy Kerr pollfd = &console->pollfds[i]; 5631cecc5deSJohnathan Mantey prc = POLLER_OK; 5641a0e03b4SJeremy Kerr 5651cecc5deSJohnathan Mantey /* process pending events... */ 5661cecc5deSJohnathan Mantey if (pollfd->revents) { 5671cecc5deSJohnathan Mantey prc = poller->event_fn(poller->handler, pollfd->revents, 568329a35f5SJeremy Kerr poller->data); 5692834c5b1SAndrew Jeffery if (prc == POLLER_EXIT) { 570329a35f5SJeremy Kerr rc = -1; 5712834c5b1SAndrew Jeffery } else if (prc == POLLER_REMOVE) { 572329a35f5SJeremy Kerr poller->remove = true; 573329a35f5SJeremy Kerr } 5742834c5b1SAndrew Jeffery } 575329a35f5SJeremy Kerr 5761cecc5deSJohnathan Mantey if ((prc == POLLER_OK) && poller->timeout_fn && 5771cecc5deSJohnathan Mantey timerisset(&poller->timeout) && 5781cecc5deSJohnathan Mantey timercmp(&poller->timeout, cur_time, <=)) { 5791cecc5deSJohnathan Mantey /* One of the ringbuffer consumers is buffering the 5801cecc5deSJohnathan Mantey data stream. The amount of idle time the consumer 5811cecc5deSJohnathan Mantey desired has expired. Process the buffered data for 5821cecc5deSJohnathan Mantey transmission. */ 5831cecc5deSJohnathan Mantey timerclear(&poller->timeout); 5841cecc5deSJohnathan Mantey prc = poller->timeout_fn(poller->handler, poller->data); 5851cecc5deSJohnathan Mantey if (prc == POLLER_EXIT) { 5861cecc5deSJohnathan Mantey rc = -1; 5871cecc5deSJohnathan Mantey } else if (prc == POLLER_REMOVE) { 5881cecc5deSJohnathan Mantey poller->remove = true; 5891cecc5deSJohnathan Mantey } 5901cecc5deSJohnathan Mantey } 5911cecc5deSJohnathan Mantey } 5921cecc5deSJohnathan Mantey 593329a35f5SJeremy Kerr /** 594329a35f5SJeremy Kerr * Process deferred removals; restarting each time we unregister, as 595329a35f5SJeremy Kerr * the array will have changed 596329a35f5SJeremy Kerr */ 597329a35f5SJeremy Kerr for (;;) { 598329a35f5SJeremy Kerr bool removed = false; 599329a35f5SJeremy Kerr 600329a35f5SJeremy Kerr for (i = 0; i < console->n_pollers; i++) { 601329a35f5SJeremy Kerr poller = console->pollers[i]; 602329a35f5SJeremy Kerr if (poller->remove) { 60355c9712dSJeremy Kerr console_poller_unregister(console, poller); 604329a35f5SJeremy Kerr removed = true; 605329a35f5SJeremy Kerr break; 606329a35f5SJeremy Kerr } 607329a35f5SJeremy Kerr } 6082834c5b1SAndrew Jeffery if (!removed) { 609329a35f5SJeremy Kerr break; 6101a0e03b4SJeremy Kerr } 6112834c5b1SAndrew Jeffery } 6121a0e03b4SJeremy Kerr 6131a0e03b4SJeremy Kerr return rc; 6141a0e03b4SJeremy Kerr } 6151a0e03b4SJeremy Kerr 616769cee1aSJeremy Kerr static void sighandler(int signal) 617769cee1aSJeremy Kerr { 6182834c5b1SAndrew Jeffery if (signal == SIGINT) { 619769cee1aSJeremy Kerr sigint = true; 620769cee1aSJeremy Kerr } 6212834c5b1SAndrew Jeffery } 622769cee1aSJeremy Kerr 6231a0e03b4SJeremy Kerr int run_console(struct console *console) 6241a0e03b4SJeremy Kerr { 6255c359cc6SAndrew Jeffery sighandler_t sighandler_save = signal(SIGINT, sighandler); 6261cecc5deSJohnathan Mantey struct timeval tv; 6275c359cc6SAndrew Jeffery long timeout; 6285c359cc6SAndrew Jeffery ssize_t rc; 629769cee1aSJeremy Kerr 630769cee1aSJeremy Kerr rc = 0; 631769cee1aSJeremy Kerr 632d831f960SJeremy Kerr for (;;) { 633d831f960SJeremy Kerr uint8_t buf[4096]; 634d831f960SJeremy Kerr 6351764145dSJeremy Kerr BUILD_ASSERT(sizeof(buf) <= buffer_size); 6361764145dSJeremy Kerr 637769cee1aSJeremy Kerr if (sigint) { 638769cee1aSJeremy Kerr fprintf(stderr, "Received interrupt, exiting\n"); 639769cee1aSJeremy Kerr break; 640769cee1aSJeremy Kerr } 641769cee1aSJeremy Kerr 6421cecc5deSJohnathan Mantey rc = get_current_time(&tv); 6431cecc5deSJohnathan Mantey if (rc) { 6441cecc5deSJohnathan Mantey warn("Failed to read current time"); 6451cecc5deSJohnathan Mantey break; 6461cecc5deSJohnathan Mantey } 6471cecc5deSJohnathan Mantey 6481cecc5deSJohnathan Mantey timeout = get_poll_timeout(console, &tv); 6491cecc5deSJohnathan Mantey 650329a35f5SJeremy Kerr rc = poll(console->pollfds, 6515c359cc6SAndrew Jeffery console->n_pollers + MAX_INTERNAL_POLLFD, 6525c359cc6SAndrew Jeffery (int)timeout); 6531cecc5deSJohnathan Mantey 654d831f960SJeremy Kerr if (rc < 0) { 655769cee1aSJeremy Kerr if (errno == EINTR) { 656769cee1aSJeremy Kerr continue; 6570b7b0477SAndrew Jeffery } 658d831f960SJeremy Kerr warn("poll error"); 659769cee1aSJeremy Kerr break; 660769cee1aSJeremy Kerr } 661d831f960SJeremy Kerr 662329a35f5SJeremy Kerr /* process internal fd first */ 663329a35f5SJeremy Kerr if (console->pollfds[console->n_pollers].revents) { 6641a0e03b4SJeremy Kerr rc = read(console->tty_fd, buf, sizeof(buf)); 665d831f960SJeremy Kerr if (rc <= 0) { 666d831f960SJeremy Kerr warn("Error reading from tty device"); 667769cee1aSJeremy Kerr rc = -1; 668769cee1aSJeremy Kerr break; 669d831f960SJeremy Kerr } 670f733c85aSJeremy Kerr rc = ringbuffer_queue(console->rb, buf, rc); 6712834c5b1SAndrew Jeffery if (rc) { 672769cee1aSJeremy Kerr break; 673d831f960SJeremy Kerr } 6742834c5b1SAndrew Jeffery } 675d831f960SJeremy Kerr 676f9c8f6caSCheng C Yang if (console->pollfds[console->n_pollers + 1].revents) { 677f9c8f6caSCheng C Yang sd_bus_process(console->bus, NULL); 678f9c8f6caSCheng C Yang } 679f9c8f6caSCheng C Yang 680329a35f5SJeremy Kerr /* ... and then the pollers */ 6811cecc5deSJohnathan Mantey rc = call_pollers(console, &tv); 6822834c5b1SAndrew Jeffery if (rc) { 683769cee1aSJeremy Kerr break; 6841a0e03b4SJeremy Kerr } 6852834c5b1SAndrew Jeffery } 686769cee1aSJeremy Kerr 687769cee1aSJeremy Kerr signal(SIGINT, sighandler_save); 688f9c8f6caSCheng C Yang sd_bus_unref(console->bus); 689769cee1aSJeremy Kerr 690769cee1aSJeremy Kerr return rc ? -1 : 0; 6911a0e03b4SJeremy Kerr } 692d831f960SJeremy Kerr static const struct option options[] = { 693d66195c1SJeremy Kerr { "config", required_argument, 0, 'c' }, 694f5858b5bSJoel Stanley { 0, 0, 0, 0 }, 695d831f960SJeremy Kerr }; 696d831f960SJeremy Kerr 697d831f960SJeremy Kerr int main(int argc, char **argv) 698d831f960SJeremy Kerr { 699d66195c1SJeremy Kerr const char *config_filename = NULL; 7006221ce94SVishwanatha Subbanna const char *config_tty_kname = NULL; 7011a0e03b4SJeremy Kerr struct console *console; 702d66195c1SJeremy Kerr struct config *config; 703d66195c1SJeremy Kerr int rc; 704d831f960SJeremy Kerr 705d831f960SJeremy Kerr for (;;) { 706b70f8713SAndrew Jeffery int c; 707b70f8713SAndrew Jeffery int idx; 708d831f960SJeremy Kerr 709d66195c1SJeremy Kerr c = getopt_long(argc, argv, "c:", options, &idx); 7102834c5b1SAndrew Jeffery if (c == -1) { 711d831f960SJeremy Kerr break; 7122834c5b1SAndrew Jeffery } 713d831f960SJeremy Kerr 714d831f960SJeremy Kerr switch (c) { 715d66195c1SJeremy Kerr case 'c': 716d66195c1SJeremy Kerr config_filename = optarg; 717d831f960SJeremy Kerr break; 718d831f960SJeremy Kerr case 'h': 719d831f960SJeremy Kerr case '?': 720d831f960SJeremy Kerr usage(argv[0]); 721d66195c1SJeremy Kerr return EXIT_SUCCESS; 722d831f960SJeremy Kerr } 723d831f960SJeremy Kerr } 724d831f960SJeremy Kerr 7252834c5b1SAndrew Jeffery if (optind < argc) { 7266221ce94SVishwanatha Subbanna config_tty_kname = argv[optind]; 7272834c5b1SAndrew Jeffery } 7286221ce94SVishwanatha Subbanna 729d66195c1SJeremy Kerr console = malloc(sizeof(struct console)); 730d66195c1SJeremy Kerr memset(console, 0, sizeof(*console)); 731a72711afSAndrew Jeffery console->pollfds = 732a72711afSAndrew Jeffery calloc(MAX_INTERNAL_POLLFD, sizeof(*console->pollfds)); 733f733c85aSJeremy Kerr console->rb = ringbuffer_init(buffer_size); 734329a35f5SJeremy Kerr 735d66195c1SJeremy Kerr config = config_init(config_filename); 736d66195c1SJeremy Kerr if (!config) { 737d66195c1SJeremy Kerr warnx("Can't read configuration, exiting."); 738*29c59c44SAndrew Jeffery rc = -1; 739d66195c1SJeremy Kerr goto out_free; 740d831f960SJeremy Kerr } 741d831f960SJeremy Kerr 7422834c5b1SAndrew Jeffery if (!config_tty_kname) { 74391dde14eSAndrew Jeffery config_tty_kname = config_get_value(config, "upstream-tty"); 7442834c5b1SAndrew Jeffery } 74591dde14eSAndrew Jeffery 74691dde14eSAndrew Jeffery if (!config_tty_kname) { 74791dde14eSAndrew Jeffery warnx("No TTY device specified"); 74891dde14eSAndrew Jeffery usage(argv[0]); 749*29c59c44SAndrew Jeffery rc = -1; 750*29c59c44SAndrew Jeffery goto out_config_fini; 75191dde14eSAndrew Jeffery } 75291dde14eSAndrew Jeffery 753b14ca19cSNinad Palsule if (set_socket_info(console, config)) { 754*29c59c44SAndrew Jeffery rc = -1; 755*29c59c44SAndrew Jeffery goto out_config_fini; 756b14ca19cSNinad Palsule } 757b14ca19cSNinad Palsule 7586221ce94SVishwanatha Subbanna console->tty_kname = config_tty_kname; 7596221ce94SVishwanatha Subbanna 760d66195c1SJeremy Kerr rc = tty_init(console, config); 7612834c5b1SAndrew Jeffery if (rc) { 762d66195c1SJeremy Kerr goto out_config_fini; 7632834c5b1SAndrew Jeffery } 764d831f960SJeremy Kerr 765f9c8f6caSCheng C Yang dbus_init(console, config); 766f9c8f6caSCheng C Yang 767d47963e5SJeremy Kerr handlers_init(console, config); 768d831f960SJeremy Kerr 7691a0e03b4SJeremy Kerr rc = run_console(console); 770d831f960SJeremy Kerr 7711a0e03b4SJeremy Kerr handlers_fini(console); 772d831f960SJeremy Kerr 773d66195c1SJeremy Kerr out_config_fini: 774d66195c1SJeremy Kerr config_fini(config); 775d66195c1SJeremy Kerr 776957818b4SJeremy Kerr out_free: 77789ea8198SJeremy Kerr free(console->pollers); 77889ea8198SJeremy Kerr free(console->pollfds); 7791a0e03b4SJeremy Kerr free(console->tty_sysfs_devnode); 7801a0e03b4SJeremy Kerr free(console->tty_dev); 7811a0e03b4SJeremy Kerr free(console); 782d831f960SJeremy Kerr 783d831f960SJeremy Kerr return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE; 784d831f960SJeremy Kerr } 785