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 1917217845SJeremy Kerr #define _GNU_SOURCE 2017217845SJeremy Kerr 21329a35f5SJeremy Kerr #include <assert.h> 22769cee1aSJeremy Kerr #include <errno.h> 23769cee1aSJeremy Kerr #include <signal.h> 24d831f960SJeremy Kerr #include <stdint.h> 25d831f960SJeremy Kerr #include <stdbool.h> 26d831f960SJeremy Kerr #include <stdlib.h> 27d831f960SJeremy Kerr #include <stdio.h> 28d831f960SJeremy Kerr #include <fcntl.h> 29d831f960SJeremy Kerr #include <unistd.h> 30d831f960SJeremy Kerr #include <err.h> 31d831f960SJeremy Kerr #include <string.h> 32d831f960SJeremy Kerr #include <getopt.h> 3317217845SJeremy Kerr #include <limits.h> 3454e9569dSJeremy Kerr #include <termios.h> 35d831f960SJeremy Kerr 36d831f960SJeremy Kerr #include <sys/types.h> 3787e344cdSJoel Stanley #include <poll.h> 38d831f960SJeremy Kerr 391a0e03b4SJeremy Kerr #include "console-server.h" 40d831f960SJeremy Kerr 411a0e03b4SJeremy Kerr struct console { 4217217845SJeremy Kerr const char *tty_kname; 4317217845SJeremy Kerr char *tty_sysfs_devnode; 4417217845SJeremy Kerr char *tty_dev; 45957818b4SJeremy Kerr int tty_sirq; 46957818b4SJeremy Kerr int tty_lpc_addr; 47*c7fbcd48SBenjamin Fair speed_t tty_baud; 48d831f960SJeremy Kerr int tty_fd; 49329a35f5SJeremy Kerr 50f733c85aSJeremy Kerr struct ringbuffer *rb; 51f733c85aSJeremy Kerr 521a0e03b4SJeremy Kerr struct handler **handlers; 531a0e03b4SJeremy Kerr int n_handlers; 54329a35f5SJeremy Kerr 55329a35f5SJeremy Kerr struct poller **pollers; 56329a35f5SJeremy Kerr int n_pollers; 57329a35f5SJeremy Kerr 58329a35f5SJeremy Kerr struct pollfd *pollfds; 59d831f960SJeremy Kerr }; 60d831f960SJeremy Kerr 61329a35f5SJeremy Kerr struct poller { 62329a35f5SJeremy Kerr struct handler *handler; 63329a35f5SJeremy Kerr void *data; 64329a35f5SJeremy Kerr poller_fn_t fn; 65329a35f5SJeremy Kerr bool remove; 66329a35f5SJeremy Kerr }; 67329a35f5SJeremy Kerr 68329a35f5SJeremy Kerr /* we have one extra entry in the pollfds array for the VUART tty */ 69329a35f5SJeremy Kerr static const int n_internal_pollfds = 1; 70329a35f5SJeremy Kerr 71f733c85aSJeremy Kerr /* size of the shared backlog ringbuffer */ 72f733c85aSJeremy Kerr const size_t buffer_size = 128 * 1024; 73f733c85aSJeremy Kerr 74769cee1aSJeremy Kerr /* state shared with the signal handler */ 75769cee1aSJeremy Kerr static bool sigint; 76329a35f5SJeremy Kerr 77d831f960SJeremy Kerr static void usage(const char *progname) 78d831f960SJeremy Kerr { 79d831f960SJeremy Kerr fprintf(stderr, 806221ce94SVishwanatha Subbanna "usage: %s [options] <DEVICE>\n" 81d831f960SJeremy Kerr "\n" 82d831f960SJeremy Kerr "Options:\n" 83d66195c1SJeremy Kerr " --config <FILE> Use FILE for configuration\n" 84d831f960SJeremy Kerr "", 85d831f960SJeremy Kerr progname); 86d831f960SJeremy Kerr } 87d831f960SJeremy Kerr 8817217845SJeremy Kerr /* populates tty_dev and tty_sysfs_devnode, using the tty kernel name */ 891a0e03b4SJeremy Kerr static int tty_find_device(struct console *console) 9017217845SJeremy Kerr { 9117217845SJeremy Kerr char *tty_class_device_link; 9217217845SJeremy Kerr char *tty_device_tty_dir; 9317217845SJeremy Kerr char *tty_device_reldir; 9445ad7676SYi Li char *tty_path_input; 9545ad7676SYi Li char *tty_path_input_real; 9645ad7676SYi Li char *tty_kname_real; 9717217845SJeremy Kerr int rc; 9817217845SJeremy Kerr 9917217845SJeremy Kerr tty_class_device_link = NULL; 10017217845SJeremy Kerr tty_device_tty_dir = NULL; 10117217845SJeremy Kerr tty_device_reldir = NULL; 10245ad7676SYi Li tty_path_input = NULL; 10345ad7676SYi Li tty_path_input_real = NULL; 10445ad7676SYi Li tty_kname_real = NULL; 10517217845SJeremy Kerr 10645ad7676SYi Li /* udev may rename the tty name with a symbol link, try to resolve */ 10745ad7676SYi Li rc = asprintf(&tty_path_input, "/dev/%s", console->tty_kname); 10817217845SJeremy Kerr if (rc < 0) 10917217845SJeremy Kerr return -1; 11017217845SJeremy Kerr 11145ad7676SYi Li tty_path_input_real = realpath(tty_path_input, NULL); 11245ad7676SYi Li if (!tty_path_input_real) { 11345ad7676SYi Li warn("Can't find realpath for /dev/%s", console->tty_kname); 11445ad7676SYi Li goto out_free; 11545ad7676SYi Li } 11645ad7676SYi Li 11745ad7676SYi Li tty_kname_real = basename(tty_path_input_real); 11845ad7676SYi Li if (!tty_kname_real) { 11945ad7676SYi Li warn("Can't find real name for /dev/%s", console->tty_kname); 12045ad7676SYi Li goto out_free; 12145ad7676SYi Li } 12245ad7676SYi Li 12345ad7676SYi Li rc = asprintf(&tty_class_device_link, 12445ad7676SYi Li "/sys/class/tty/%s", tty_kname_real); 12545ad7676SYi Li if (rc < 0) 12645ad7676SYi Li goto out_free; 12745ad7676SYi Li 12817217845SJeremy Kerr tty_device_tty_dir = realpath(tty_class_device_link, NULL); 12945ad7676SYi Li if (!tty_device_tty_dir) { 13045ad7676SYi Li warn("Can't query sysfs for device %s", tty_kname_real); 13117217845SJeremy Kerr goto out_free; 13217217845SJeremy Kerr } 13317217845SJeremy Kerr 13417217845SJeremy Kerr rc = asprintf(&tty_device_reldir, "%s/../../", tty_device_tty_dir); 13517217845SJeremy Kerr if (rc < 0) 13617217845SJeremy Kerr goto out_free; 13717217845SJeremy Kerr 1381a0e03b4SJeremy Kerr console->tty_sysfs_devnode = realpath(tty_device_reldir, NULL); 1391a0e03b4SJeremy Kerr if (!console->tty_sysfs_devnode) 14045ad7676SYi Li warn("Can't find parent device for %s", tty_kname_real); 14117217845SJeremy Kerr 14245ad7676SYi Li rc = asprintf(&console->tty_dev, "/dev/%s", tty_kname_real); 14317217845SJeremy Kerr if (rc < 0) 14417217845SJeremy Kerr goto out_free; 14517217845SJeremy Kerr 14617217845SJeremy Kerr rc = 0; 14717217845SJeremy Kerr 14817217845SJeremy Kerr out_free: 14917217845SJeremy Kerr free(tty_class_device_link); 15017217845SJeremy Kerr free(tty_device_tty_dir); 15117217845SJeremy Kerr free(tty_device_reldir); 15245ad7676SYi Li free(tty_path_input); 15345ad7676SYi Li free(tty_path_input_real); 15417217845SJeremy Kerr return rc; 15517217845SJeremy Kerr } 15617217845SJeremy Kerr 1571a0e03b4SJeremy Kerr static int tty_set_sysfs_attr(struct console *console, const char *name, 158957818b4SJeremy Kerr int value) 159957818b4SJeremy Kerr { 160957818b4SJeremy Kerr char *path; 161957818b4SJeremy Kerr FILE *fp; 162957818b4SJeremy Kerr int rc; 163957818b4SJeremy Kerr 1641a0e03b4SJeremy Kerr rc = asprintf(&path, "%s/%s", console->tty_sysfs_devnode, name); 165957818b4SJeremy Kerr if (rc < 0) 166957818b4SJeremy Kerr return -1; 167957818b4SJeremy Kerr 168957818b4SJeremy Kerr fp = fopen(path, "w"); 169957818b4SJeremy Kerr if (!fp) { 170957818b4SJeremy Kerr warn("Can't access attribute %s on device %s", 1711a0e03b4SJeremy Kerr name, console->tty_kname); 172957818b4SJeremy Kerr rc = -1; 173957818b4SJeremy Kerr goto out_free; 174957818b4SJeremy Kerr } 175957818b4SJeremy Kerr setvbuf(fp, NULL, _IONBF, 0); 176957818b4SJeremy Kerr 177957818b4SJeremy Kerr rc = fprintf(fp, "0x%x", value); 178957818b4SJeremy Kerr if (rc < 0) 179957818b4SJeremy Kerr warn("Error writing to %s attribute of device %s", 1801a0e03b4SJeremy Kerr name, console->tty_kname); 181957818b4SJeremy Kerr fclose(fp); 182957818b4SJeremy Kerr 183957818b4SJeremy Kerr 184957818b4SJeremy Kerr 185957818b4SJeremy Kerr out_free: 186957818b4SJeremy Kerr free(path); 187957818b4SJeremy Kerr return rc; 188957818b4SJeremy Kerr } 189957818b4SJeremy Kerr 190d831f960SJeremy Kerr /** 191*c7fbcd48SBenjamin Fair * Set termios attributes on the console tty. 19254e9569dSJeremy Kerr */ 19354e9569dSJeremy Kerr static void tty_init_termios(struct console *console) 19454e9569dSJeremy Kerr { 19554e9569dSJeremy Kerr struct termios termios; 19654e9569dSJeremy Kerr int rc; 19754e9569dSJeremy Kerr 19854e9569dSJeremy Kerr rc = tcgetattr(console->tty_fd, &termios); 19954e9569dSJeremy Kerr if (rc) { 20054e9569dSJeremy Kerr warn("Can't read tty termios"); 20154e9569dSJeremy Kerr return; 20254e9569dSJeremy Kerr } 20354e9569dSJeremy Kerr 204*c7fbcd48SBenjamin Fair if (console->tty_baud) { 205*c7fbcd48SBenjamin Fair if (cfsetspeed(&termios, console->tty_baud) < 0) 206*c7fbcd48SBenjamin Fair warn("Couldn't set speeds for %s", console->tty_kname); 207*c7fbcd48SBenjamin Fair } 208*c7fbcd48SBenjamin Fair 209*c7fbcd48SBenjamin Fair /* Set console to raw mode: we don't want any processing to occur on 210*c7fbcd48SBenjamin Fair * the underlying terminal input/output. 211*c7fbcd48SBenjamin Fair */ 21254e9569dSJeremy Kerr cfmakeraw(&termios); 213*c7fbcd48SBenjamin Fair 21454e9569dSJeremy Kerr rc = tcsetattr(console->tty_fd, TCSANOW, &termios); 21554e9569dSJeremy Kerr if (rc) 216*c7fbcd48SBenjamin Fair warn("Can't set terminal options for %s", console->tty_kname); 21754e9569dSJeremy Kerr } 21854e9569dSJeremy Kerr 21954e9569dSJeremy Kerr /** 220d831f960SJeremy Kerr * Open and initialise the serial device 221d831f960SJeremy Kerr */ 2221a0e03b4SJeremy Kerr static int tty_init_io(struct console *console) 223d831f960SJeremy Kerr { 2241a0e03b4SJeremy Kerr if (console->tty_sirq) 2251a0e03b4SJeremy Kerr tty_set_sysfs_attr(console, "sirq", console->tty_sirq); 2261a0e03b4SJeremy Kerr if (console->tty_lpc_addr) 2271a0e03b4SJeremy Kerr tty_set_sysfs_attr(console, "lpc_address", 2281a0e03b4SJeremy Kerr console->tty_lpc_addr); 229957818b4SJeremy Kerr 2301a0e03b4SJeremy Kerr console->tty_fd = open(console->tty_dev, O_RDWR); 2311a0e03b4SJeremy Kerr if (console->tty_fd <= 0) { 2321a0e03b4SJeremy Kerr warn("Can't open tty %s", console->tty_dev); 233d831f960SJeremy Kerr return -1; 234d831f960SJeremy Kerr } 235d831f960SJeremy Kerr 236d831f960SJeremy Kerr /* Disable character delay. We may want to later enable this when 237d831f960SJeremy Kerr * we detect larger amounts of data 238d831f960SJeremy Kerr */ 2391a0e03b4SJeremy Kerr fcntl(console->tty_fd, F_SETFL, FNDELAY); 240d831f960SJeremy Kerr 24154e9569dSJeremy Kerr tty_init_termios(console); 24254e9569dSJeremy Kerr 243329a35f5SJeremy Kerr console->pollfds[console->n_pollers].fd = console->tty_fd; 244329a35f5SJeremy Kerr console->pollfds[console->n_pollers].events = POLLIN; 245329a35f5SJeremy Kerr 246d831f960SJeremy Kerr return 0; 247d831f960SJeremy Kerr } 248d831f960SJeremy Kerr 249d66195c1SJeremy Kerr static int tty_init(struct console *console, struct config *config) 250d66195c1SJeremy Kerr { 251d66195c1SJeremy Kerr const char *val; 252d66195c1SJeremy Kerr char *endp; 253d66195c1SJeremy Kerr int rc; 254d66195c1SJeremy Kerr 255d66195c1SJeremy Kerr val = config_get_value(config, "lpc-address"); 256d66195c1SJeremy Kerr if (val) { 257d66195c1SJeremy Kerr console->tty_lpc_addr = strtoul(val, &endp, 0); 258d66195c1SJeremy Kerr if (endp == optarg) { 259d66195c1SJeremy Kerr warn("Invalid LPC address: '%s'", val); 260d66195c1SJeremy Kerr return -1; 261d66195c1SJeremy Kerr } 262d66195c1SJeremy Kerr } 263d66195c1SJeremy Kerr 264d66195c1SJeremy Kerr val = config_get_value(config, "sirq"); 265d66195c1SJeremy Kerr if (val) { 266d66195c1SJeremy Kerr console->tty_sirq = strtoul(val, &endp, 0); 267d66195c1SJeremy Kerr if (endp == optarg) 268d66195c1SJeremy Kerr warn("Invalid sirq: '%s'", val); 269d66195c1SJeremy Kerr } 270d66195c1SJeremy Kerr 271*c7fbcd48SBenjamin Fair val = config_get_value(config, "baud"); 272*c7fbcd48SBenjamin Fair if (val) { 273*c7fbcd48SBenjamin Fair if (config_parse_baud(&console->tty_baud, val)) 274*c7fbcd48SBenjamin Fair warnx("Invalid baud rate: '%s'", val); 275*c7fbcd48SBenjamin Fair } 276*c7fbcd48SBenjamin Fair 277d66195c1SJeremy Kerr if (!console->tty_kname) { 278d66195c1SJeremy Kerr warnx("Error: No TTY device specified"); 279d66195c1SJeremy Kerr return -1; 280d66195c1SJeremy Kerr } 281d66195c1SJeremy Kerr 282d66195c1SJeremy Kerr rc = tty_find_device(console); 283d66195c1SJeremy Kerr if (rc) 284d66195c1SJeremy Kerr return rc; 285d66195c1SJeremy Kerr 286d66195c1SJeremy Kerr rc = tty_init_io(console); 287d66195c1SJeremy Kerr return rc; 288d66195c1SJeremy Kerr } 289d66195c1SJeremy Kerr 2901a0e03b4SJeremy Kerr 2911a0e03b4SJeremy Kerr int console_data_out(struct console *console, const uint8_t *data, size_t len) 292d831f960SJeremy Kerr { 2931a0e03b4SJeremy Kerr return write_buf_to_fd(console->tty_fd, data, len); 294d831f960SJeremy Kerr } 295d831f960SJeremy Kerr 296d47963e5SJeremy Kerr static void handlers_init(struct console *console, struct config *config) 297d831f960SJeremy Kerr { 2981a0e03b4SJeremy Kerr extern struct handler *__start_handlers, *__stop_handlers; 2991a0e03b4SJeremy Kerr struct handler *handler; 300021b91f0SJeremy Kerr int i, rc; 301d831f960SJeremy Kerr 3021a0e03b4SJeremy Kerr console->n_handlers = &__stop_handlers - &__start_handlers; 3031a0e03b4SJeremy Kerr console->handlers = &__start_handlers; 304d831f960SJeremy Kerr 3051a0e03b4SJeremy Kerr printf("%d handler%s\n", console->n_handlers, 3061a0e03b4SJeremy Kerr console->n_handlers == 1 ? "" : "s"); 307d831f960SJeremy Kerr 3081a0e03b4SJeremy Kerr for (i = 0; i < console->n_handlers; i++) { 3091a0e03b4SJeremy Kerr handler = console->handlers[i]; 3101a0e03b4SJeremy Kerr 311021b91f0SJeremy Kerr rc = 0; 3121a0e03b4SJeremy Kerr if (handler->init) 313021b91f0SJeremy Kerr rc = handler->init(handler, console, config); 314021b91f0SJeremy Kerr 315021b91f0SJeremy Kerr handler->active = rc == 0; 316021b91f0SJeremy Kerr 317021b91f0SJeremy Kerr printf(" %s [%sactive]\n", handler->name, 318021b91f0SJeremy Kerr handler->active ? "" : "in"); 319d831f960SJeremy Kerr } 320d831f960SJeremy Kerr } 321d831f960SJeremy Kerr 3221a0e03b4SJeremy Kerr static void handlers_fini(struct console *console) 323d831f960SJeremy Kerr { 3241a0e03b4SJeremy Kerr struct handler *handler; 3251a0e03b4SJeremy Kerr int i; 3261a0e03b4SJeremy Kerr 3271a0e03b4SJeremy Kerr for (i = 0; i < console->n_handlers; i++) { 3281a0e03b4SJeremy Kerr handler = console->handlers[i]; 329021b91f0SJeremy Kerr if (handler->fini && handler->active) 3301a0e03b4SJeremy Kerr handler->fini(handler); 3311a0e03b4SJeremy Kerr } 332d831f960SJeremy Kerr } 333d831f960SJeremy Kerr 334f733c85aSJeremy Kerr struct ringbuffer_consumer *console_ringbuffer_consumer_register( 335f733c85aSJeremy Kerr struct console *console, 336f733c85aSJeremy Kerr ringbuffer_poll_fn_t poll_fn, void *data) 337d831f960SJeremy Kerr { 338f733c85aSJeremy Kerr return ringbuffer_consumer_register(console->rb, poll_fn, data); 339d831f960SJeremy Kerr } 340d831f960SJeremy Kerr 34155c9712dSJeremy Kerr struct poller *console_poller_register(struct console *console, 342329a35f5SJeremy Kerr struct handler *handler, poller_fn_t poller_fn, 343329a35f5SJeremy Kerr int fd, int events, void *data) 344d831f960SJeremy Kerr { 345329a35f5SJeremy Kerr struct poller *poller; 346329a35f5SJeremy Kerr int n; 347329a35f5SJeremy Kerr 348329a35f5SJeremy Kerr poller = malloc(sizeof(*poller)); 349329a35f5SJeremy Kerr poller->remove = false; 350329a35f5SJeremy Kerr poller->handler = handler; 351329a35f5SJeremy Kerr poller->fn = poller_fn; 352329a35f5SJeremy Kerr poller->data = data; 353329a35f5SJeremy Kerr 354329a35f5SJeremy Kerr /* add one to our pollers array */ 355329a35f5SJeremy Kerr n = console->n_pollers++; 356329a35f5SJeremy Kerr console->pollers = realloc(console->pollers, 357329a35f5SJeremy Kerr sizeof(*console->pollers) * console->n_pollers); 358329a35f5SJeremy Kerr 359329a35f5SJeremy Kerr console->pollers[n] = poller; 360329a35f5SJeremy Kerr 361329a35f5SJeremy Kerr /* increase pollfds array too */ 362329a35f5SJeremy Kerr console->pollfds = realloc(console->pollfds, 363329a35f5SJeremy Kerr sizeof(*console->pollfds) * 364329a35f5SJeremy Kerr (n_internal_pollfds + console->n_pollers)); 365329a35f5SJeremy Kerr 366329a35f5SJeremy Kerr /* shift the end pollfds up by one */ 367329a35f5SJeremy Kerr memcpy(&console->pollfds[n+n_internal_pollfds], 368329a35f5SJeremy Kerr &console->pollfds[n], 369329a35f5SJeremy Kerr sizeof(*console->pollfds) * n_internal_pollfds); 370329a35f5SJeremy Kerr 371329a35f5SJeremy Kerr console->pollfds[n].fd = fd; 372329a35f5SJeremy Kerr console->pollfds[n].events = events; 373329a35f5SJeremy Kerr 374329a35f5SJeremy Kerr return poller; 375329a35f5SJeremy Kerr } 376329a35f5SJeremy Kerr 37755c9712dSJeremy Kerr void console_poller_unregister(struct console *console, 378329a35f5SJeremy Kerr struct poller *poller) 379329a35f5SJeremy Kerr { 380329a35f5SJeremy Kerr int i; 381329a35f5SJeremy Kerr 382329a35f5SJeremy Kerr /* find the entry in our pollers array */ 383329a35f5SJeremy Kerr for (i = 0; i < console->n_pollers; i++) 384329a35f5SJeremy Kerr if (console->pollers[i] == poller) 385329a35f5SJeremy Kerr break; 386329a35f5SJeremy Kerr 387329a35f5SJeremy Kerr assert(i < console->n_pollers); 388329a35f5SJeremy Kerr 389329a35f5SJeremy Kerr console->n_pollers--; 390329a35f5SJeremy Kerr 391329a35f5SJeremy Kerr /* remove the item from the pollers array... */ 392329a35f5SJeremy Kerr memmove(&console->pollers[i], &console->pollers[i+1], 393329a35f5SJeremy Kerr sizeof(*console->pollers) 394329a35f5SJeremy Kerr * (console->n_pollers - i)); 395329a35f5SJeremy Kerr 396329a35f5SJeremy Kerr console->pollers = realloc(console->pollers, 397329a35f5SJeremy Kerr sizeof(*console->pollers) * console->n_pollers); 398329a35f5SJeremy Kerr 399329a35f5SJeremy Kerr /* ... and the pollfds array */ 400329a35f5SJeremy Kerr memmove(&console->pollfds[i], &console->pollfds[i+1], 401329a35f5SJeremy Kerr sizeof(*console->pollfds) * 402329a35f5SJeremy Kerr (n_internal_pollfds + console->n_pollers - i)); 403329a35f5SJeremy Kerr 404329a35f5SJeremy Kerr console->pollfds = realloc(console->pollfds, 405329a35f5SJeremy Kerr sizeof(*console->pollfds) * 406329a35f5SJeremy Kerr (n_internal_pollfds + console->n_pollers)); 407329a35f5SJeremy Kerr 408329a35f5SJeremy Kerr 409329a35f5SJeremy Kerr free(poller); 410329a35f5SJeremy Kerr } 411329a35f5SJeremy Kerr 4126b1fed27SJeremy Kerr void console_poller_set_events(struct console *console, struct poller *poller, 4136b1fed27SJeremy Kerr int events) 4146b1fed27SJeremy Kerr { 4156b1fed27SJeremy Kerr int i; 4166b1fed27SJeremy Kerr 4176b1fed27SJeremy Kerr /* find the entry in our pollers array */ 4186b1fed27SJeremy Kerr for (i = 0; i < console->n_pollers; i++) 4196b1fed27SJeremy Kerr if (console->pollers[i] == poller) 4206b1fed27SJeremy Kerr break; 4216b1fed27SJeremy Kerr 4226b1fed27SJeremy Kerr console->pollfds[i].events = events; 4236b1fed27SJeremy Kerr } 4246b1fed27SJeremy Kerr 425329a35f5SJeremy Kerr static int call_pollers(struct console *console) 426329a35f5SJeremy Kerr { 427329a35f5SJeremy Kerr struct poller *poller; 428329a35f5SJeremy Kerr struct pollfd *pollfd; 429329a35f5SJeremy Kerr enum poller_ret prc; 430329a35f5SJeremy Kerr int i, rc; 431d831f960SJeremy Kerr 4321a0e03b4SJeremy Kerr rc = 0; 4331a0e03b4SJeremy Kerr 434329a35f5SJeremy Kerr /* 435329a35f5SJeremy Kerr * Process poll events by iterating through the pollers and pollfds 436329a35f5SJeremy Kerr * in-step, calling any pollers that we've found revents for. 437329a35f5SJeremy Kerr */ 438329a35f5SJeremy Kerr for (i = 0; i < console->n_pollers; i++) { 439329a35f5SJeremy Kerr poller = console->pollers[i]; 440329a35f5SJeremy Kerr pollfd = &console->pollfds[i]; 4411a0e03b4SJeremy Kerr 442329a35f5SJeremy Kerr if (!pollfd->revents) 4431a0e03b4SJeremy Kerr continue; 4441a0e03b4SJeremy Kerr 445329a35f5SJeremy Kerr prc = poller->fn(poller->handler, pollfd->revents, 446329a35f5SJeremy Kerr poller->data); 447329a35f5SJeremy Kerr if (prc == POLLER_EXIT) 448329a35f5SJeremy Kerr rc = -1; 449329a35f5SJeremy Kerr else if (prc == POLLER_REMOVE) 450329a35f5SJeremy Kerr poller->remove = true; 451329a35f5SJeremy Kerr } 452329a35f5SJeremy Kerr 453329a35f5SJeremy Kerr /** 454329a35f5SJeremy Kerr * Process deferred removals; restarting each time we unregister, as 455329a35f5SJeremy Kerr * the array will have changed 456329a35f5SJeremy Kerr */ 457329a35f5SJeremy Kerr for (;;) { 458329a35f5SJeremy Kerr bool removed = false; 459329a35f5SJeremy Kerr 460329a35f5SJeremy Kerr for (i = 0; i < console->n_pollers; i++) { 461329a35f5SJeremy Kerr poller = console->pollers[i]; 462329a35f5SJeremy Kerr if (poller->remove) { 46355c9712dSJeremy Kerr console_poller_unregister(console, poller); 464329a35f5SJeremy Kerr removed = true; 465329a35f5SJeremy Kerr break; 466329a35f5SJeremy Kerr } 467329a35f5SJeremy Kerr } 468329a35f5SJeremy Kerr if (!removed) 469329a35f5SJeremy Kerr break; 4701a0e03b4SJeremy Kerr } 4711a0e03b4SJeremy Kerr 4721a0e03b4SJeremy Kerr return rc; 4731a0e03b4SJeremy Kerr } 4741a0e03b4SJeremy Kerr 475769cee1aSJeremy Kerr static void sighandler(int signal) 476769cee1aSJeremy Kerr { 477769cee1aSJeremy Kerr if (signal == SIGINT) 478769cee1aSJeremy Kerr sigint = true; 479769cee1aSJeremy Kerr } 480769cee1aSJeremy Kerr 4811a0e03b4SJeremy Kerr int run_console(struct console *console) 4821a0e03b4SJeremy Kerr { 483769cee1aSJeremy Kerr sighandler_t sighandler_save; 484329a35f5SJeremy Kerr int rc; 485d831f960SJeremy Kerr 486769cee1aSJeremy Kerr sighandler_save = signal(SIGINT, sighandler); 487769cee1aSJeremy Kerr 488769cee1aSJeremy Kerr rc = 0; 489769cee1aSJeremy Kerr 490d831f960SJeremy Kerr for (;;) { 491d831f960SJeremy Kerr uint8_t buf[4096]; 492d831f960SJeremy Kerr 4931764145dSJeremy Kerr BUILD_ASSERT(sizeof(buf) <= buffer_size); 4941764145dSJeremy Kerr 495769cee1aSJeremy Kerr if (sigint) { 496769cee1aSJeremy Kerr fprintf(stderr, "Received interrupt, exiting\n"); 497769cee1aSJeremy Kerr break; 498769cee1aSJeremy Kerr } 499769cee1aSJeremy Kerr 500329a35f5SJeremy Kerr rc = poll(console->pollfds, 501329a35f5SJeremy Kerr console->n_pollers + n_internal_pollfds, -1); 502d831f960SJeremy Kerr if (rc < 0) { 503769cee1aSJeremy Kerr if (errno == EINTR) { 504769cee1aSJeremy Kerr continue; 505769cee1aSJeremy Kerr } else { 506d831f960SJeremy Kerr warn("poll error"); 507769cee1aSJeremy Kerr break; 508769cee1aSJeremy Kerr } 509d831f960SJeremy Kerr } 510d831f960SJeremy Kerr 511329a35f5SJeremy Kerr /* process internal fd first */ 512329a35f5SJeremy Kerr BUILD_ASSERT(n_internal_pollfds == 1); 513329a35f5SJeremy Kerr 514329a35f5SJeremy Kerr if (console->pollfds[console->n_pollers].revents) { 5151a0e03b4SJeremy Kerr rc = read(console->tty_fd, buf, sizeof(buf)); 516d831f960SJeremy Kerr if (rc <= 0) { 517d831f960SJeremy Kerr warn("Error reading from tty device"); 518769cee1aSJeremy Kerr rc = -1; 519769cee1aSJeremy Kerr break; 520d831f960SJeremy Kerr } 521f733c85aSJeremy Kerr rc = ringbuffer_queue(console->rb, buf, rc); 5221a0e03b4SJeremy Kerr if (rc) 523769cee1aSJeremy Kerr break; 524d831f960SJeremy Kerr } 525d831f960SJeremy Kerr 526329a35f5SJeremy Kerr /* ... and then the pollers */ 527329a35f5SJeremy Kerr rc = call_pollers(console); 5281a0e03b4SJeremy Kerr if (rc) 529769cee1aSJeremy Kerr break; 5301a0e03b4SJeremy Kerr } 531769cee1aSJeremy Kerr 532769cee1aSJeremy Kerr signal(SIGINT, sighandler_save); 533769cee1aSJeremy Kerr 534769cee1aSJeremy Kerr return rc ? -1 : 0; 5351a0e03b4SJeremy Kerr } 536d831f960SJeremy Kerr static const struct option options[] = { 537d66195c1SJeremy Kerr { "config", required_argument, 0, 'c'}, 538f5858b5bSJoel Stanley { 0, 0, 0, 0}, 539d831f960SJeremy Kerr }; 540d831f960SJeremy Kerr 541d831f960SJeremy Kerr int main(int argc, char **argv) 542d831f960SJeremy Kerr { 543d66195c1SJeremy Kerr const char *config_filename = NULL; 5446221ce94SVishwanatha Subbanna const char *config_tty_kname = NULL; 5451a0e03b4SJeremy Kerr struct console *console; 546d66195c1SJeremy Kerr struct config *config; 547d66195c1SJeremy Kerr int rc; 548d831f960SJeremy Kerr 549957818b4SJeremy Kerr rc = -1; 550d831f960SJeremy Kerr 551d831f960SJeremy Kerr for (;;) { 552d831f960SJeremy Kerr int c, idx; 553d831f960SJeremy Kerr 554d66195c1SJeremy Kerr c = getopt_long(argc, argv, "c:", options, &idx); 555d831f960SJeremy Kerr if (c == -1) 556d831f960SJeremy Kerr break; 557d831f960SJeremy Kerr 558d831f960SJeremy Kerr switch (c) { 559d66195c1SJeremy Kerr case 'c': 560d66195c1SJeremy Kerr config_filename = optarg; 561d831f960SJeremy Kerr break; 562d831f960SJeremy Kerr case 'h': 563d831f960SJeremy Kerr case '?': 564d831f960SJeremy Kerr usage(argv[0]); 565d66195c1SJeremy Kerr return EXIT_SUCCESS; 566d831f960SJeremy Kerr } 567d831f960SJeremy Kerr } 568d831f960SJeremy Kerr 5696221ce94SVishwanatha Subbanna if (optind >= argc) { 5706221ce94SVishwanatha Subbanna warnx("Required argument <DEVICE> missing"); 5716221ce94SVishwanatha Subbanna usage(argv[0]); 5726221ce94SVishwanatha Subbanna return EXIT_FAILURE; 5736221ce94SVishwanatha Subbanna } 5746221ce94SVishwanatha Subbanna 5756221ce94SVishwanatha Subbanna config_tty_kname = argv[optind]; 5766221ce94SVishwanatha Subbanna 577d66195c1SJeremy Kerr console = malloc(sizeof(struct console)); 578d66195c1SJeremy Kerr memset(console, 0, sizeof(*console)); 579329a35f5SJeremy Kerr console->pollfds = calloc(n_internal_pollfds, 580329a35f5SJeremy Kerr sizeof(*console->pollfds)); 581f733c85aSJeremy Kerr console->rb = ringbuffer_init(buffer_size); 582329a35f5SJeremy Kerr 583d66195c1SJeremy Kerr config = config_init(config_filename); 584d66195c1SJeremy Kerr if (!config) { 585d66195c1SJeremy Kerr warnx("Can't read configuration, exiting."); 586d66195c1SJeremy Kerr goto out_free; 587d831f960SJeremy Kerr } 588d831f960SJeremy Kerr 5896221ce94SVishwanatha Subbanna console->tty_kname = config_tty_kname; 5906221ce94SVishwanatha Subbanna 591d66195c1SJeremy Kerr rc = tty_init(console, config); 59217217845SJeremy Kerr if (rc) 593d66195c1SJeremy Kerr goto out_config_fini; 594d831f960SJeremy Kerr 595d47963e5SJeremy Kerr handlers_init(console, config); 596d831f960SJeremy Kerr 5971a0e03b4SJeremy Kerr rc = run_console(console); 598d831f960SJeremy Kerr 5991a0e03b4SJeremy Kerr handlers_fini(console); 600d831f960SJeremy Kerr 601d66195c1SJeremy Kerr out_config_fini: 602d66195c1SJeremy Kerr config_fini(config); 603d66195c1SJeremy Kerr 604957818b4SJeremy Kerr out_free: 60589ea8198SJeremy Kerr free(console->pollers); 60689ea8198SJeremy Kerr free(console->pollfds); 6071a0e03b4SJeremy Kerr free(console->tty_sysfs_devnode); 6081a0e03b4SJeremy Kerr free(console->tty_dev); 6091a0e03b4SJeremy Kerr free(console); 610d831f960SJeremy Kerr 611d831f960SJeremy Kerr return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE; 612d831f960SJeremy Kerr } 613