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