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> 37*b14ca19cSNinad 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 { 6217217845SJeremy Kerr char *tty_class_device_link; 6317217845SJeremy Kerr char *tty_device_tty_dir; 6417217845SJeremy Kerr char *tty_device_reldir; 6545ad7676SYi Li char *tty_path_input; 6645ad7676SYi Li char *tty_path_input_real; 6745ad7676SYi Li char *tty_kname_real; 6817217845SJeremy Kerr int rc; 6917217845SJeremy Kerr 7017217845SJeremy Kerr tty_class_device_link = NULL; 7117217845SJeremy Kerr tty_device_tty_dir = NULL; 7217217845SJeremy Kerr tty_device_reldir = NULL; 7345ad7676SYi Li tty_path_input = NULL; 7445ad7676SYi Li tty_path_input_real = NULL; 7545ad7676SYi Li tty_kname_real = NULL; 7617217845SJeremy Kerr 7745ad7676SYi Li /* udev may rename the tty name with a symbol link, try to resolve */ 7845ad7676SYi Li rc = asprintf(&tty_path_input, "/dev/%s", console->tty_kname); 792834c5b1SAndrew Jeffery if (rc < 0) { 8017217845SJeremy Kerr return -1; 812834c5b1SAndrew Jeffery } 8217217845SJeremy Kerr 8345ad7676SYi Li tty_path_input_real = realpath(tty_path_input, NULL); 8445ad7676SYi Li if (!tty_path_input_real) { 8545ad7676SYi Li warn("Can't find realpath for /dev/%s", console->tty_kname); 8615792aa7SAndrew Jeffery rc = -1; 8745ad7676SYi Li goto out_free; 8845ad7676SYi Li } 8945ad7676SYi Li 9045ad7676SYi Li tty_kname_real = basename(tty_path_input_real); 9145ad7676SYi Li if (!tty_kname_real) { 9245ad7676SYi Li warn("Can't find real name for /dev/%s", console->tty_kname); 9315792aa7SAndrew Jeffery rc = -1; 9445ad7676SYi Li goto out_free; 9545ad7676SYi Li } 9645ad7676SYi Li 97a72711afSAndrew Jeffery rc = asprintf(&tty_class_device_link, "/sys/class/tty/%s", 98a72711afSAndrew Jeffery tty_kname_real); 992834c5b1SAndrew Jeffery if (rc < 0) { 10045ad7676SYi Li goto out_free; 1012834c5b1SAndrew Jeffery } 10245ad7676SYi Li 10317217845SJeremy Kerr tty_device_tty_dir = realpath(tty_class_device_link, NULL); 10445ad7676SYi Li if (!tty_device_tty_dir) { 10545ad7676SYi Li warn("Can't query sysfs for device %s", tty_kname_real); 10615792aa7SAndrew Jeffery rc = -1; 10717217845SJeremy Kerr goto out_free; 10817217845SJeremy Kerr } 10917217845SJeremy Kerr 11017217845SJeremy Kerr rc = asprintf(&tty_device_reldir, "%s/../../", tty_device_tty_dir); 1112834c5b1SAndrew Jeffery if (rc < 0) { 11217217845SJeremy Kerr goto out_free; 1132834c5b1SAndrew Jeffery } 11417217845SJeremy Kerr 1151a0e03b4SJeremy Kerr console->tty_sysfs_devnode = realpath(tty_device_reldir, NULL); 1162834c5b1SAndrew Jeffery if (!console->tty_sysfs_devnode) { 11745ad7676SYi Li warn("Can't find parent device for %s", tty_kname_real); 1182834c5b1SAndrew Jeffery } 11917217845SJeremy Kerr 12045ad7676SYi Li rc = asprintf(&console->tty_dev, "/dev/%s", tty_kname_real); 1212834c5b1SAndrew Jeffery if (rc < 0) { 12217217845SJeremy Kerr goto out_free; 1232834c5b1SAndrew Jeffery } 12417217845SJeremy Kerr 12517217845SJeremy Kerr rc = 0; 12617217845SJeremy Kerr 12717217845SJeremy Kerr out_free: 12817217845SJeremy Kerr free(tty_class_device_link); 12917217845SJeremy Kerr free(tty_device_tty_dir); 13017217845SJeremy Kerr free(tty_device_reldir); 13145ad7676SYi Li free(tty_path_input); 13245ad7676SYi Li free(tty_path_input_real); 13317217845SJeremy Kerr return rc; 13417217845SJeremy Kerr } 13517217845SJeremy Kerr 1361a0e03b4SJeremy Kerr static int tty_set_sysfs_attr(struct console *console, const char *name, 137957818b4SJeremy Kerr int value) 138957818b4SJeremy Kerr { 139957818b4SJeremy Kerr char *path; 140957818b4SJeremy Kerr FILE *fp; 141957818b4SJeremy Kerr int rc; 142957818b4SJeremy Kerr 1431a0e03b4SJeremy Kerr rc = asprintf(&path, "%s/%s", console->tty_sysfs_devnode, name); 1442834c5b1SAndrew Jeffery if (rc < 0) { 145957818b4SJeremy Kerr return -1; 1462834c5b1SAndrew Jeffery } 147957818b4SJeremy Kerr 148957818b4SJeremy Kerr fp = fopen(path, "w"); 149957818b4SJeremy Kerr if (!fp) { 150a72711afSAndrew Jeffery warn("Can't access attribute %s on device %s", name, 151a72711afSAndrew Jeffery console->tty_kname); 152957818b4SJeremy Kerr rc = -1; 153957818b4SJeremy Kerr goto out_free; 154957818b4SJeremy Kerr } 155957818b4SJeremy Kerr setvbuf(fp, NULL, _IONBF, 0); 156957818b4SJeremy Kerr 157957818b4SJeremy Kerr rc = fprintf(fp, "0x%x", value); 1582834c5b1SAndrew Jeffery if (rc < 0) { 159a72711afSAndrew Jeffery warn("Error writing to %s attribute of device %s", name, 160a72711afSAndrew Jeffery console->tty_kname); 1612834c5b1SAndrew Jeffery } 162957818b4SJeremy Kerr fclose(fp); 163957818b4SJeremy Kerr 164957818b4SJeremy Kerr out_free: 165957818b4SJeremy Kerr free(path); 166957818b4SJeremy Kerr return rc; 167957818b4SJeremy Kerr } 168957818b4SJeremy Kerr 169d831f960SJeremy Kerr /** 170c7fbcd48SBenjamin Fair * Set termios attributes on the console tty. 17154e9569dSJeremy Kerr */ 172e258e51fSNinad Palsule void tty_init_termios(struct console *console) 17354e9569dSJeremy Kerr { 17454e9569dSJeremy Kerr struct termios termios; 17554e9569dSJeremy Kerr int rc; 17654e9569dSJeremy Kerr 17754e9569dSJeremy Kerr rc = tcgetattr(console->tty_fd, &termios); 17854e9569dSJeremy Kerr if (rc) { 17954e9569dSJeremy Kerr warn("Can't read tty termios"); 18054e9569dSJeremy Kerr return; 18154e9569dSJeremy Kerr } 18254e9569dSJeremy Kerr 183c7fbcd48SBenjamin Fair if (console->tty_baud) { 1842834c5b1SAndrew Jeffery if (cfsetspeed(&termios, console->tty_baud) < 0) { 185c7fbcd48SBenjamin Fair warn("Couldn't set speeds for %s", console->tty_kname); 186c7fbcd48SBenjamin Fair } 1872834c5b1SAndrew Jeffery } 188c7fbcd48SBenjamin Fair 189c7fbcd48SBenjamin Fair /* Set console to raw mode: we don't want any processing to occur on 190c7fbcd48SBenjamin Fair * the underlying terminal input/output. 191c7fbcd48SBenjamin Fair */ 19254e9569dSJeremy Kerr cfmakeraw(&termios); 193c7fbcd48SBenjamin Fair 19454e9569dSJeremy Kerr rc = tcsetattr(console->tty_fd, TCSANOW, &termios); 1952834c5b1SAndrew Jeffery if (rc) { 196c7fbcd48SBenjamin Fair warn("Can't set terminal options for %s", console->tty_kname); 19754e9569dSJeremy Kerr } 1982834c5b1SAndrew Jeffery } 19954e9569dSJeremy Kerr 20054e9569dSJeremy Kerr /** 201d831f960SJeremy Kerr * Open and initialise the serial device 202d831f960SJeremy Kerr */ 2031a0e03b4SJeremy Kerr static int tty_init_io(struct console *console) 204d831f960SJeremy Kerr { 2052834c5b1SAndrew Jeffery if (console->tty_sirq) { 2061a0e03b4SJeremy Kerr tty_set_sysfs_attr(console, "sirq", console->tty_sirq); 2072834c5b1SAndrew Jeffery } 2082834c5b1SAndrew Jeffery if (console->tty_lpc_addr) { 2091a0e03b4SJeremy Kerr tty_set_sysfs_attr(console, "lpc_address", 2101a0e03b4SJeremy Kerr console->tty_lpc_addr); 2112834c5b1SAndrew Jeffery } 212957818b4SJeremy Kerr 2131a0e03b4SJeremy Kerr console->tty_fd = open(console->tty_dev, O_RDWR); 2141a0e03b4SJeremy Kerr if (console->tty_fd <= 0) { 2151a0e03b4SJeremy Kerr warn("Can't open tty %s", console->tty_dev); 216d831f960SJeremy Kerr return -1; 217d831f960SJeremy Kerr } 218d831f960SJeremy Kerr 219d831f960SJeremy Kerr /* Disable character delay. We may want to later enable this when 220d831f960SJeremy Kerr * we detect larger amounts of data 221d831f960SJeremy Kerr */ 2221a0e03b4SJeremy Kerr fcntl(console->tty_fd, F_SETFL, FNDELAY); 223d831f960SJeremy Kerr 22454e9569dSJeremy Kerr tty_init_termios(console); 22554e9569dSJeremy Kerr 226329a35f5SJeremy Kerr console->pollfds[console->n_pollers].fd = console->tty_fd; 227329a35f5SJeremy Kerr console->pollfds[console->n_pollers].events = POLLIN; 228329a35f5SJeremy Kerr 229d831f960SJeremy Kerr return 0; 230d831f960SJeremy Kerr } 231d831f960SJeremy Kerr 232d66195c1SJeremy Kerr static int tty_init(struct console *console, struct config *config) 233d66195c1SJeremy Kerr { 234fd883a88SAndrew Jeffery unsigned long parsed; 235d66195c1SJeremy Kerr const char *val; 236d66195c1SJeremy Kerr char *endp; 237d66195c1SJeremy Kerr int rc; 238d66195c1SJeremy Kerr 239d66195c1SJeremy Kerr val = config_get_value(config, "lpc-address"); 240d66195c1SJeremy Kerr if (val) { 241fd883a88SAndrew Jeffery errno = 0; 242fd883a88SAndrew Jeffery parsed = strtoul(val, &endp, 0); 243fd883a88SAndrew Jeffery if (parsed == ULONG_MAX && errno == ERANGE) { 244fd883a88SAndrew Jeffery warn("Cannot interpret 'lpc-address' value as an unsigned long: '%s'", 245fd883a88SAndrew Jeffery val); 246fd883a88SAndrew Jeffery return -1; 247fd883a88SAndrew Jeffery } 248fd883a88SAndrew Jeffery 249fd883a88SAndrew Jeffery if (parsed > UINT16_MAX) { 250fd883a88SAndrew Jeffery warn("Invalid LPC address '%s'", val); 251fd883a88SAndrew Jeffery return -1; 252fd883a88SAndrew Jeffery } 253fd883a88SAndrew Jeffery 254fd883a88SAndrew Jeffery console->tty_lpc_addr = (uint16_t)parsed; 255d66195c1SJeremy Kerr if (endp == optarg) { 256d66195c1SJeremy Kerr warn("Invalid LPC address: '%s'", val); 257d66195c1SJeremy Kerr return -1; 258d66195c1SJeremy Kerr } 259d66195c1SJeremy Kerr } 260d66195c1SJeremy Kerr 261d66195c1SJeremy Kerr val = config_get_value(config, "sirq"); 262d66195c1SJeremy Kerr if (val) { 263fd883a88SAndrew Jeffery errno = 0; 264fd883a88SAndrew Jeffery parsed = strtoul(val, &endp, 0); 265fd883a88SAndrew Jeffery if (parsed == ULONG_MAX && errno == ERANGE) { 266fd883a88SAndrew Jeffery warn("Cannot interpret 'sirq' value as an unsigned long: '%s'", 267fd883a88SAndrew Jeffery val); 268fd883a88SAndrew Jeffery } 269fd883a88SAndrew Jeffery 2702834c5b1SAndrew Jeffery if (parsed > 16) { 271fd883a88SAndrew Jeffery warn("Invalid LPC SERIRQ: '%s'", val); 2722834c5b1SAndrew Jeffery } 273fd883a88SAndrew Jeffery 274fd883a88SAndrew Jeffery console->tty_sirq = (int)parsed; 2752834c5b1SAndrew Jeffery if (endp == optarg) { 276d66195c1SJeremy Kerr warn("Invalid sirq: '%s'", val); 277d66195c1SJeremy Kerr } 2782834c5b1SAndrew Jeffery } 279d66195c1SJeremy Kerr 280c7fbcd48SBenjamin Fair val = config_get_value(config, "baud"); 281c7fbcd48SBenjamin Fair if (val) { 2822834c5b1SAndrew Jeffery if (config_parse_baud(&console->tty_baud, val)) { 283c7fbcd48SBenjamin Fair warnx("Invalid baud rate: '%s'", val); 284c7fbcd48SBenjamin Fair } 2852834c5b1SAndrew Jeffery } 286c7fbcd48SBenjamin Fair 287d66195c1SJeremy Kerr if (!console->tty_kname) { 288d66195c1SJeremy Kerr warnx("Error: No TTY device specified"); 289d66195c1SJeremy Kerr return -1; 290d66195c1SJeremy Kerr } 291d66195c1SJeremy Kerr 292d66195c1SJeremy Kerr rc = tty_find_device(console); 2932834c5b1SAndrew Jeffery if (rc) { 294d66195c1SJeremy Kerr return rc; 2952834c5b1SAndrew Jeffery } 296d66195c1SJeremy Kerr 297d66195c1SJeremy Kerr rc = tty_init_io(console); 298d66195c1SJeremy Kerr return rc; 299d66195c1SJeremy Kerr } 300d66195c1SJeremy Kerr 3011a0e03b4SJeremy Kerr int console_data_out(struct console *console, const uint8_t *data, size_t len) 302d831f960SJeremy Kerr { 3031a0e03b4SJeremy Kerr return write_buf_to_fd(console->tty_fd, data, len); 304d831f960SJeremy Kerr } 305d831f960SJeremy Kerr 306*b14ca19cSNinad Palsule /* Read console if from config and prepare a socket name */ 307*b14ca19cSNinad Palsule static int set_socket_info(struct console *console, struct config *config) 308*b14ca19cSNinad Palsule { 309*b14ca19cSNinad Palsule ssize_t len; 310*b14ca19cSNinad Palsule 311*b14ca19cSNinad Palsule console->console_id = config_get_value(config, "socket-id"); 312*b14ca19cSNinad Palsule if (!console->console_id) { 313*b14ca19cSNinad Palsule warnx("Error: The socket-id is not set in the config file"); 314*b14ca19cSNinad Palsule return EXIT_FAILURE; 315*b14ca19cSNinad Palsule } 316*b14ca19cSNinad Palsule 317*b14ca19cSNinad Palsule /* Get the socket name/path */ 318*b14ca19cSNinad Palsule len = console_socket_path(console->socket_name, console->console_id); 319*b14ca19cSNinad Palsule if (len < 0) { 320*b14ca19cSNinad Palsule warn("Failed to set socket path: %s", strerror(errno)); 321*b14ca19cSNinad Palsule return EXIT_FAILURE; 322*b14ca19cSNinad Palsule } 323*b14ca19cSNinad Palsule 324*b14ca19cSNinad Palsule /* Socket name is not a null terminated string hence save the length */ 325*b14ca19cSNinad Palsule console->socket_name_len = len; 326*b14ca19cSNinad Palsule 327*b14ca19cSNinad Palsule return 0; 328*b14ca19cSNinad Palsule } 329*b14ca19cSNinad Palsule 330d47963e5SJeremy Kerr static void handlers_init(struct console *console, struct config *config) 331d831f960SJeremy Kerr { 332b70f8713SAndrew Jeffery /* NOLINTBEGIN(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) */ 333b70f8713SAndrew Jeffery extern struct handler *__start_handlers; 334b70f8713SAndrew Jeffery extern struct handler *__stop_handlers; 335b70f8713SAndrew Jeffery /* NOLINTEND(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) */ 3361a0e03b4SJeremy Kerr struct handler *handler; 337b70f8713SAndrew Jeffery int i; 338b70f8713SAndrew Jeffery int rc; 339d831f960SJeremy Kerr 3401a0e03b4SJeremy Kerr console->n_handlers = &__stop_handlers - &__start_handlers; 3411a0e03b4SJeremy Kerr console->handlers = &__start_handlers; 342d831f960SJeremy Kerr 3435c359cc6SAndrew Jeffery printf("%ld handler%s\n", console->n_handlers, 3441a0e03b4SJeremy Kerr console->n_handlers == 1 ? "" : "s"); 345d831f960SJeremy Kerr 3461a0e03b4SJeremy Kerr for (i = 0; i < console->n_handlers; i++) { 3471a0e03b4SJeremy Kerr handler = console->handlers[i]; 3481a0e03b4SJeremy Kerr 349021b91f0SJeremy Kerr rc = 0; 3502834c5b1SAndrew Jeffery if (handler->init) { 351021b91f0SJeremy Kerr rc = handler->init(handler, console, config); 3522834c5b1SAndrew Jeffery } 353021b91f0SJeremy Kerr 354021b91f0SJeremy Kerr handler->active = rc == 0; 355021b91f0SJeremy Kerr 356021b91f0SJeremy Kerr printf(" %s [%sactive]\n", handler->name, 357021b91f0SJeremy Kerr handler->active ? "" : "in"); 358d831f960SJeremy Kerr } 359d831f960SJeremy Kerr } 360d831f960SJeremy Kerr 3611a0e03b4SJeremy Kerr static void handlers_fini(struct console *console) 362d831f960SJeremy Kerr { 3631a0e03b4SJeremy Kerr struct handler *handler; 3641a0e03b4SJeremy Kerr int i; 3651a0e03b4SJeremy Kerr 3661a0e03b4SJeremy Kerr for (i = 0; i < console->n_handlers; i++) { 3671a0e03b4SJeremy Kerr handler = console->handlers[i]; 3682834c5b1SAndrew Jeffery if (handler->fini && handler->active) { 3691a0e03b4SJeremy Kerr handler->fini(handler); 3701a0e03b4SJeremy Kerr } 371d831f960SJeremy Kerr } 3722834c5b1SAndrew Jeffery } 373d831f960SJeremy Kerr 3741cecc5deSJohnathan Mantey static int get_current_time(struct timeval *tv) 3751cecc5deSJohnathan Mantey { 3761cecc5deSJohnathan Mantey struct timespec t; 3771cecc5deSJohnathan Mantey int rc; 3781cecc5deSJohnathan Mantey 3791cecc5deSJohnathan Mantey /* 3801cecc5deSJohnathan Mantey * We use clock_gettime(CLOCK_MONOTONIC) so we're immune to 3811cecc5deSJohnathan Mantey * local time changes. However, a struct timeval is more 3821cecc5deSJohnathan Mantey * convenient for calculations, so convert to that. 3831cecc5deSJohnathan Mantey */ 3841cecc5deSJohnathan Mantey rc = clock_gettime(CLOCK_MONOTONIC, &t); 3852834c5b1SAndrew Jeffery if (rc) { 3861cecc5deSJohnathan Mantey return rc; 3872834c5b1SAndrew Jeffery } 3881cecc5deSJohnathan Mantey 3891cecc5deSJohnathan Mantey tv->tv_sec = t.tv_sec; 3901cecc5deSJohnathan Mantey tv->tv_usec = t.tv_nsec / 1000; 3911cecc5deSJohnathan Mantey 3921cecc5deSJohnathan Mantey return 0; 3931cecc5deSJohnathan Mantey } 3941cecc5deSJohnathan Mantey 395a72711afSAndrew Jeffery struct ringbuffer_consumer * 396a72711afSAndrew Jeffery console_ringbuffer_consumer_register(struct console *console, 397f733c85aSJeremy Kerr ringbuffer_poll_fn_t poll_fn, void *data) 398d831f960SJeremy Kerr { 399f733c85aSJeremy Kerr return ringbuffer_consumer_register(console->rb, poll_fn, data); 400d831f960SJeremy Kerr } 401d831f960SJeremy Kerr 40255c9712dSJeremy Kerr struct poller *console_poller_register(struct console *console, 403a72711afSAndrew Jeffery struct handler *handler, 404a72711afSAndrew Jeffery poller_event_fn_t poller_fn, 4051cecc5deSJohnathan Mantey poller_timeout_fn_t timeout_fn, int fd, 4061cecc5deSJohnathan Mantey int events, void *data) 407d831f960SJeremy Kerr { 408329a35f5SJeremy Kerr struct poller *poller; 4095c359cc6SAndrew Jeffery long n; 410329a35f5SJeremy Kerr 411329a35f5SJeremy Kerr poller = malloc(sizeof(*poller)); 412329a35f5SJeremy Kerr poller->remove = false; 413329a35f5SJeremy Kerr poller->handler = handler; 4141cecc5deSJohnathan Mantey poller->event_fn = poller_fn; 4151cecc5deSJohnathan Mantey poller->timeout_fn = timeout_fn; 416329a35f5SJeremy Kerr poller->data = data; 417329a35f5SJeremy Kerr 418329a35f5SJeremy Kerr /* add one to our pollers array */ 419329a35f5SJeremy Kerr n = console->n_pollers++; 42091b52175SAndrew Jeffery /* 42191b52175SAndrew Jeffery * We're managing an array of pointers to aggregates, so don't warn about sizeof() on a 42291b52175SAndrew Jeffery * pointer type. 42391b52175SAndrew Jeffery */ 42491b52175SAndrew Jeffery /* NOLINTBEGIN(bugprone-sizeof-expression) */ 42591b52175SAndrew Jeffery console->pollers = reallocarray(console->pollers, console->n_pollers, 42691b52175SAndrew Jeffery sizeof(*console->pollers)); 42791b52175SAndrew Jeffery /* NOLINTEND(bugprone-sizeof-expression) */ 428329a35f5SJeremy Kerr 429329a35f5SJeremy Kerr console->pollers[n] = poller; 430329a35f5SJeremy Kerr 431329a35f5SJeremy Kerr /* increase pollfds array too */ 432a72711afSAndrew Jeffery console->pollfds = 43391b52175SAndrew Jeffery reallocarray(console->pollfds, 43491b52175SAndrew Jeffery (MAX_INTERNAL_POLLFD + console->n_pollers), 43591b52175SAndrew Jeffery sizeof(*console->pollfds)); 436329a35f5SJeremy Kerr 437329a35f5SJeremy Kerr /* shift the end pollfds up by one */ 438a72711afSAndrew Jeffery memcpy(&console->pollfds[n + 1], &console->pollfds[n], 439f9c8f6caSCheng C Yang sizeof(*console->pollfds) * MAX_INTERNAL_POLLFD); 440329a35f5SJeremy Kerr 441329a35f5SJeremy Kerr console->pollfds[n].fd = fd; 4425c359cc6SAndrew Jeffery console->pollfds[n].events = (short)(events & 0x7fff); 443329a35f5SJeremy Kerr 444329a35f5SJeremy Kerr return poller; 445329a35f5SJeremy Kerr } 446329a35f5SJeremy Kerr 447a72711afSAndrew Jeffery void console_poller_unregister(struct console *console, struct poller *poller) 448329a35f5SJeremy Kerr { 449329a35f5SJeremy Kerr int i; 450329a35f5SJeremy Kerr 451329a35f5SJeremy Kerr /* find the entry in our pollers array */ 4522834c5b1SAndrew Jeffery for (i = 0; i < console->n_pollers; i++) { 4532834c5b1SAndrew Jeffery if (console->pollers[i] == poller) { 454329a35f5SJeremy Kerr break; 4552834c5b1SAndrew Jeffery } 4562834c5b1SAndrew Jeffery } 457329a35f5SJeremy Kerr 458329a35f5SJeremy Kerr assert(i < console->n_pollers); 459329a35f5SJeremy Kerr 460329a35f5SJeremy Kerr console->n_pollers--; 461329a35f5SJeremy Kerr 46291b52175SAndrew Jeffery /* 46391b52175SAndrew Jeffery * Remove the item from the pollers array... 46491b52175SAndrew Jeffery * 46591b52175SAndrew Jeffery * We're managing an array of pointers to aggregates, so don't warn about sizeof() on a 46691b52175SAndrew Jeffery * pointer type. 46791b52175SAndrew Jeffery */ 46891b52175SAndrew Jeffery /* NOLINTBEGIN(bugprone-sizeof-expression) */ 469329a35f5SJeremy Kerr memmove(&console->pollers[i], &console->pollers[i + 1], 470a72711afSAndrew Jeffery sizeof(*console->pollers) * (console->n_pollers - i)); 471329a35f5SJeremy Kerr 47291b52175SAndrew Jeffery console->pollers = reallocarray(console->pollers, console->n_pollers, 47391b52175SAndrew Jeffery sizeof(*console->pollers)); 47491b52175SAndrew Jeffery /* NOLINTEND(bugprone-sizeof-expression) */ 475329a35f5SJeremy Kerr 476329a35f5SJeremy Kerr /* ... and the pollfds array */ 477329a35f5SJeremy Kerr memmove(&console->pollfds[i], &console->pollfds[i + 1], 478329a35f5SJeremy Kerr sizeof(*console->pollfds) * 479f9c8f6caSCheng C Yang (MAX_INTERNAL_POLLFD + console->n_pollers - i)); 480329a35f5SJeremy Kerr 481a72711afSAndrew Jeffery console->pollfds = 48291b52175SAndrew Jeffery reallocarray(console->pollfds, 48391b52175SAndrew Jeffery (MAX_INTERNAL_POLLFD + console->n_pollers), 48491b52175SAndrew Jeffery sizeof(*console->pollfds)); 485329a35f5SJeremy Kerr 486329a35f5SJeremy Kerr free(poller); 487329a35f5SJeremy Kerr } 488329a35f5SJeremy Kerr 4896b1fed27SJeremy Kerr void console_poller_set_events(struct console *console, struct poller *poller, 4906b1fed27SJeremy Kerr int events) 4916b1fed27SJeremy Kerr { 4926b1fed27SJeremy Kerr int i; 4936b1fed27SJeremy Kerr 4946b1fed27SJeremy Kerr /* find the entry in our pollers array */ 4952834c5b1SAndrew Jeffery for (i = 0; i < console->n_pollers; i++) { 4962834c5b1SAndrew Jeffery if (console->pollers[i] == poller) { 4976b1fed27SJeremy Kerr break; 4982834c5b1SAndrew Jeffery } 4992834c5b1SAndrew Jeffery } 5006b1fed27SJeremy Kerr 5015c359cc6SAndrew Jeffery console->pollfds[i].events = (short)(events & 0x7fff); 5026b1fed27SJeremy Kerr } 5036b1fed27SJeremy Kerr 504fd048328SAndrew Jeffery void console_poller_set_timeout(struct console *console __attribute__((unused)), 505fd048328SAndrew Jeffery struct poller *poller, const struct timeval *tv) 5061cecc5deSJohnathan Mantey { 5071cecc5deSJohnathan Mantey struct timeval now; 5081cecc5deSJohnathan Mantey int rc; 5091cecc5deSJohnathan Mantey 5101cecc5deSJohnathan Mantey rc = get_current_time(&now); 5112834c5b1SAndrew Jeffery if (rc) { 5121cecc5deSJohnathan Mantey return; 5132834c5b1SAndrew Jeffery } 5141cecc5deSJohnathan Mantey 5151cecc5deSJohnathan Mantey timeradd(&now, tv, &poller->timeout); 5161cecc5deSJohnathan Mantey } 5171cecc5deSJohnathan Mantey 5185c359cc6SAndrew Jeffery static long get_poll_timeout(struct console *console, struct timeval *cur_time) 5191cecc5deSJohnathan Mantey { 520b70f8713SAndrew Jeffery struct timeval *earliest; 521b70f8713SAndrew Jeffery struct timeval interval; 5221cecc5deSJohnathan Mantey struct poller *poller; 5231cecc5deSJohnathan Mantey int i; 5241cecc5deSJohnathan Mantey 5251cecc5deSJohnathan Mantey earliest = NULL; 5261cecc5deSJohnathan Mantey 5271cecc5deSJohnathan Mantey for (i = 0; i < console->n_pollers; i++) { 5281cecc5deSJohnathan Mantey poller = console->pollers[i]; 5291cecc5deSJohnathan Mantey 5301cecc5deSJohnathan Mantey if (poller->timeout_fn && timerisset(&poller->timeout) && 5311cecc5deSJohnathan Mantey (!earliest || 5321cecc5deSJohnathan Mantey (earliest && timercmp(&poller->timeout, earliest, <)))) { 5331cecc5deSJohnathan Mantey // poller is buffering data and needs the poll 5341cecc5deSJohnathan Mantey // function to timeout. 5351cecc5deSJohnathan Mantey earliest = &poller->timeout; 5361cecc5deSJohnathan Mantey } 5371cecc5deSJohnathan Mantey } 5381cecc5deSJohnathan Mantey 5391cecc5deSJohnathan Mantey if (earliest) { 5401cecc5deSJohnathan Mantey if (timercmp(earliest, cur_time, >)) { 5411cecc5deSJohnathan Mantey /* recalculate the timeout period, time period has 5421cecc5deSJohnathan Mantey * not elapsed */ 5431cecc5deSJohnathan Mantey timersub(earliest, cur_time, &interval); 5441cecc5deSJohnathan Mantey return ((interval.tv_sec * 1000) + 5451cecc5deSJohnathan Mantey (interval.tv_usec / 1000)); 5460b7b0477SAndrew Jeffery } /* return from poll immediately */ 5471cecc5deSJohnathan Mantey return 0; 5480b7b0477SAndrew Jeffery 5490b7b0477SAndrew Jeffery } /* poll indefinitely */ 5501cecc5deSJohnathan Mantey return -1; 5511cecc5deSJohnathan Mantey } 5521cecc5deSJohnathan Mantey 5531cecc5deSJohnathan Mantey static int call_pollers(struct console *console, struct timeval *cur_time) 554329a35f5SJeremy Kerr { 555329a35f5SJeremy Kerr struct poller *poller; 556329a35f5SJeremy Kerr struct pollfd *pollfd; 557329a35f5SJeremy Kerr enum poller_ret prc; 558b70f8713SAndrew Jeffery int i; 559b70f8713SAndrew Jeffery int rc; 560d831f960SJeremy Kerr 5611a0e03b4SJeremy Kerr rc = 0; 5621a0e03b4SJeremy Kerr 563329a35f5SJeremy Kerr /* 564329a35f5SJeremy Kerr * Process poll events by iterating through the pollers and pollfds 565329a35f5SJeremy Kerr * in-step, calling any pollers that we've found revents for. 566329a35f5SJeremy Kerr */ 567329a35f5SJeremy Kerr for (i = 0; i < console->n_pollers; i++) { 568329a35f5SJeremy Kerr poller = console->pollers[i]; 569329a35f5SJeremy Kerr pollfd = &console->pollfds[i]; 5701cecc5deSJohnathan Mantey prc = POLLER_OK; 5711a0e03b4SJeremy Kerr 5721cecc5deSJohnathan Mantey /* process pending events... */ 5731cecc5deSJohnathan Mantey if (pollfd->revents) { 5741cecc5deSJohnathan Mantey prc = poller->event_fn(poller->handler, pollfd->revents, 575329a35f5SJeremy Kerr poller->data); 5762834c5b1SAndrew Jeffery if (prc == POLLER_EXIT) { 577329a35f5SJeremy Kerr rc = -1; 5782834c5b1SAndrew Jeffery } else if (prc == POLLER_REMOVE) { 579329a35f5SJeremy Kerr poller->remove = true; 580329a35f5SJeremy Kerr } 5812834c5b1SAndrew Jeffery } 582329a35f5SJeremy Kerr 5831cecc5deSJohnathan Mantey if ((prc == POLLER_OK) && poller->timeout_fn && 5841cecc5deSJohnathan Mantey timerisset(&poller->timeout) && 5851cecc5deSJohnathan Mantey timercmp(&poller->timeout, cur_time, <=)) { 5861cecc5deSJohnathan Mantey /* One of the ringbuffer consumers is buffering the 5871cecc5deSJohnathan Mantey data stream. The amount of idle time the consumer 5881cecc5deSJohnathan Mantey desired has expired. Process the buffered data for 5891cecc5deSJohnathan Mantey transmission. */ 5901cecc5deSJohnathan Mantey timerclear(&poller->timeout); 5911cecc5deSJohnathan Mantey prc = poller->timeout_fn(poller->handler, poller->data); 5921cecc5deSJohnathan Mantey if (prc == POLLER_EXIT) { 5931cecc5deSJohnathan Mantey rc = -1; 5941cecc5deSJohnathan Mantey } else if (prc == POLLER_REMOVE) { 5951cecc5deSJohnathan Mantey poller->remove = true; 5961cecc5deSJohnathan Mantey } 5971cecc5deSJohnathan Mantey } 5981cecc5deSJohnathan Mantey } 5991cecc5deSJohnathan Mantey 600329a35f5SJeremy Kerr /** 601329a35f5SJeremy Kerr * Process deferred removals; restarting each time we unregister, as 602329a35f5SJeremy Kerr * the array will have changed 603329a35f5SJeremy Kerr */ 604329a35f5SJeremy Kerr for (;;) { 605329a35f5SJeremy Kerr bool removed = false; 606329a35f5SJeremy Kerr 607329a35f5SJeremy Kerr for (i = 0; i < console->n_pollers; i++) { 608329a35f5SJeremy Kerr poller = console->pollers[i]; 609329a35f5SJeremy Kerr if (poller->remove) { 61055c9712dSJeremy Kerr console_poller_unregister(console, poller); 611329a35f5SJeremy Kerr removed = true; 612329a35f5SJeremy Kerr break; 613329a35f5SJeremy Kerr } 614329a35f5SJeremy Kerr } 6152834c5b1SAndrew Jeffery if (!removed) { 616329a35f5SJeremy Kerr break; 6171a0e03b4SJeremy Kerr } 6182834c5b1SAndrew Jeffery } 6191a0e03b4SJeremy Kerr 6201a0e03b4SJeremy Kerr return rc; 6211a0e03b4SJeremy Kerr } 6221a0e03b4SJeremy Kerr 623769cee1aSJeremy Kerr static void sighandler(int signal) 624769cee1aSJeremy Kerr { 6252834c5b1SAndrew Jeffery if (signal == SIGINT) { 626769cee1aSJeremy Kerr sigint = true; 627769cee1aSJeremy Kerr } 6282834c5b1SAndrew Jeffery } 629769cee1aSJeremy Kerr 6301a0e03b4SJeremy Kerr int run_console(struct console *console) 6311a0e03b4SJeremy Kerr { 6325c359cc6SAndrew Jeffery sighandler_t sighandler_save = signal(SIGINT, sighandler); 6331cecc5deSJohnathan Mantey struct timeval tv; 6345c359cc6SAndrew Jeffery long timeout; 6355c359cc6SAndrew Jeffery ssize_t rc; 636769cee1aSJeremy Kerr 637769cee1aSJeremy Kerr rc = 0; 638769cee1aSJeremy Kerr 639d831f960SJeremy Kerr for (;;) { 640d831f960SJeremy Kerr uint8_t buf[4096]; 641d831f960SJeremy Kerr 6421764145dSJeremy Kerr BUILD_ASSERT(sizeof(buf) <= buffer_size); 6431764145dSJeremy Kerr 644769cee1aSJeremy Kerr if (sigint) { 645769cee1aSJeremy Kerr fprintf(stderr, "Received interrupt, exiting\n"); 646769cee1aSJeremy Kerr break; 647769cee1aSJeremy Kerr } 648769cee1aSJeremy Kerr 6491cecc5deSJohnathan Mantey rc = get_current_time(&tv); 6501cecc5deSJohnathan Mantey if (rc) { 6511cecc5deSJohnathan Mantey warn("Failed to read current time"); 6521cecc5deSJohnathan Mantey break; 6531cecc5deSJohnathan Mantey } 6541cecc5deSJohnathan Mantey 6551cecc5deSJohnathan Mantey timeout = get_poll_timeout(console, &tv); 6561cecc5deSJohnathan Mantey 657329a35f5SJeremy Kerr rc = poll(console->pollfds, 6585c359cc6SAndrew Jeffery console->n_pollers + MAX_INTERNAL_POLLFD, 6595c359cc6SAndrew Jeffery (int)timeout); 6601cecc5deSJohnathan Mantey 661d831f960SJeremy Kerr if (rc < 0) { 662769cee1aSJeremy Kerr if (errno == EINTR) { 663769cee1aSJeremy Kerr continue; 6640b7b0477SAndrew Jeffery } 665d831f960SJeremy Kerr warn("poll error"); 666769cee1aSJeremy Kerr break; 667769cee1aSJeremy Kerr } 668d831f960SJeremy Kerr 669329a35f5SJeremy Kerr /* process internal fd first */ 670329a35f5SJeremy Kerr if (console->pollfds[console->n_pollers].revents) { 6711a0e03b4SJeremy Kerr rc = read(console->tty_fd, buf, sizeof(buf)); 672d831f960SJeremy Kerr if (rc <= 0) { 673d831f960SJeremy Kerr warn("Error reading from tty device"); 674769cee1aSJeremy Kerr rc = -1; 675769cee1aSJeremy Kerr break; 676d831f960SJeremy Kerr } 677f733c85aSJeremy Kerr rc = ringbuffer_queue(console->rb, buf, rc); 6782834c5b1SAndrew Jeffery if (rc) { 679769cee1aSJeremy Kerr break; 680d831f960SJeremy Kerr } 6812834c5b1SAndrew Jeffery } 682d831f960SJeremy Kerr 683f9c8f6caSCheng C Yang if (console->pollfds[console->n_pollers + 1].revents) { 684f9c8f6caSCheng C Yang sd_bus_process(console->bus, NULL); 685f9c8f6caSCheng C Yang } 686f9c8f6caSCheng C Yang 687329a35f5SJeremy Kerr /* ... and then the pollers */ 6881cecc5deSJohnathan Mantey rc = call_pollers(console, &tv); 6892834c5b1SAndrew Jeffery if (rc) { 690769cee1aSJeremy Kerr break; 6911a0e03b4SJeremy Kerr } 6922834c5b1SAndrew Jeffery } 693769cee1aSJeremy Kerr 694769cee1aSJeremy Kerr signal(SIGINT, sighandler_save); 695f9c8f6caSCheng C Yang sd_bus_unref(console->bus); 696769cee1aSJeremy Kerr 697769cee1aSJeremy Kerr return rc ? -1 : 0; 6981a0e03b4SJeremy Kerr } 699d831f960SJeremy Kerr static const struct option options[] = { 700d66195c1SJeremy Kerr { "config", required_argument, 0, 'c' }, 701f5858b5bSJoel Stanley { 0, 0, 0, 0 }, 702d831f960SJeremy Kerr }; 703d831f960SJeremy Kerr 704d831f960SJeremy Kerr int main(int argc, char **argv) 705d831f960SJeremy Kerr { 706d66195c1SJeremy Kerr const char *config_filename = NULL; 7076221ce94SVishwanatha Subbanna const char *config_tty_kname = NULL; 7081a0e03b4SJeremy Kerr struct console *console; 709d66195c1SJeremy Kerr struct config *config; 710d66195c1SJeremy Kerr int rc; 711d831f960SJeremy Kerr 712957818b4SJeremy Kerr rc = -1; 713d831f960SJeremy Kerr 714d831f960SJeremy Kerr for (;;) { 715b70f8713SAndrew Jeffery int c; 716b70f8713SAndrew Jeffery int idx; 717d831f960SJeremy Kerr 718d66195c1SJeremy Kerr c = getopt_long(argc, argv, "c:", options, &idx); 7192834c5b1SAndrew Jeffery if (c == -1) { 720d831f960SJeremy Kerr break; 7212834c5b1SAndrew Jeffery } 722d831f960SJeremy Kerr 723d831f960SJeremy Kerr switch (c) { 724d66195c1SJeremy Kerr case 'c': 725d66195c1SJeremy Kerr config_filename = optarg; 726d831f960SJeremy Kerr break; 727d831f960SJeremy Kerr case 'h': 728d831f960SJeremy Kerr case '?': 729d831f960SJeremy Kerr usage(argv[0]); 730d66195c1SJeremy Kerr return EXIT_SUCCESS; 731d831f960SJeremy Kerr } 732d831f960SJeremy Kerr } 733d831f960SJeremy Kerr 7342834c5b1SAndrew Jeffery if (optind < argc) { 7356221ce94SVishwanatha Subbanna config_tty_kname = argv[optind]; 7362834c5b1SAndrew Jeffery } 7376221ce94SVishwanatha Subbanna 738d66195c1SJeremy Kerr console = malloc(sizeof(struct console)); 739d66195c1SJeremy Kerr memset(console, 0, sizeof(*console)); 740a72711afSAndrew Jeffery console->pollfds = 741a72711afSAndrew Jeffery calloc(MAX_INTERNAL_POLLFD, sizeof(*console->pollfds)); 742f733c85aSJeremy Kerr console->rb = ringbuffer_init(buffer_size); 743329a35f5SJeremy Kerr 744d66195c1SJeremy Kerr config = config_init(config_filename); 745d66195c1SJeremy Kerr if (!config) { 746d66195c1SJeremy Kerr warnx("Can't read configuration, exiting."); 747d66195c1SJeremy Kerr goto out_free; 748d831f960SJeremy Kerr } 749d831f960SJeremy Kerr 7502834c5b1SAndrew Jeffery if (!config_tty_kname) { 75191dde14eSAndrew Jeffery config_tty_kname = config_get_value(config, "upstream-tty"); 7522834c5b1SAndrew Jeffery } 75391dde14eSAndrew Jeffery 75491dde14eSAndrew Jeffery if (!config_tty_kname) { 75591dde14eSAndrew Jeffery warnx("No TTY device specified"); 75691dde14eSAndrew Jeffery usage(argv[0]); 75791dde14eSAndrew Jeffery return EXIT_FAILURE; 75891dde14eSAndrew Jeffery } 75991dde14eSAndrew Jeffery 760*b14ca19cSNinad Palsule if (set_socket_info(console, config)) { 761*b14ca19cSNinad Palsule return EXIT_FAILURE; 762*b14ca19cSNinad Palsule } 763*b14ca19cSNinad Palsule 7646221ce94SVishwanatha Subbanna console->tty_kname = config_tty_kname; 7656221ce94SVishwanatha Subbanna 7664e718691SNinad Palsule console->console_id = config_get_value(config, "socket-id"); 7674e718691SNinad Palsule if (!console->console_id) { 7684e718691SNinad Palsule warnx("Error: The socket-id is not set in the config file"); 7694e718691SNinad Palsule return EXIT_FAILURE; 7704e718691SNinad Palsule } 7714e718691SNinad Palsule 772d66195c1SJeremy Kerr rc = tty_init(console, config); 7732834c5b1SAndrew Jeffery if (rc) { 774d66195c1SJeremy Kerr goto out_config_fini; 7752834c5b1SAndrew Jeffery } 776d831f960SJeremy Kerr 777f9c8f6caSCheng C Yang dbus_init(console, config); 778f9c8f6caSCheng C Yang 779d47963e5SJeremy Kerr handlers_init(console, config); 780d831f960SJeremy Kerr 7811a0e03b4SJeremy Kerr rc = run_console(console); 782d831f960SJeremy Kerr 7831a0e03b4SJeremy Kerr handlers_fini(console); 784d831f960SJeremy Kerr 785d66195c1SJeremy Kerr out_config_fini: 786d66195c1SJeremy Kerr config_fini(config); 787d66195c1SJeremy Kerr 788957818b4SJeremy Kerr out_free: 78989ea8198SJeremy Kerr free(console->pollers); 79089ea8198SJeremy Kerr free(console->pollfds); 7911a0e03b4SJeremy Kerr free(console->tty_sysfs_devnode); 7921a0e03b4SJeremy Kerr free(console->tty_dev); 7931a0e03b4SJeremy Kerr free(console); 794d831f960SJeremy Kerr 795d831f960SJeremy Kerr return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE; 796d831f960SJeremy Kerr } 797