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; 47d831f960SJeremy Kerr int tty_fd; 48329a35f5SJeremy Kerr 491a0e03b4SJeremy Kerr struct handler **handlers; 501a0e03b4SJeremy Kerr int n_handlers; 51329a35f5SJeremy Kerr 52329a35f5SJeremy Kerr struct poller **pollers; 53329a35f5SJeremy Kerr int n_pollers; 54329a35f5SJeremy Kerr 55329a35f5SJeremy Kerr struct pollfd *pollfds; 56d831f960SJeremy Kerr }; 57d831f960SJeremy Kerr 58329a35f5SJeremy Kerr struct poller { 59329a35f5SJeremy Kerr struct handler *handler; 60329a35f5SJeremy Kerr void *data; 61329a35f5SJeremy Kerr poller_fn_t fn; 62329a35f5SJeremy Kerr bool remove; 63329a35f5SJeremy Kerr }; 64329a35f5SJeremy Kerr 65329a35f5SJeremy Kerr /* we have one extra entry in the pollfds array for the VUART tty */ 66329a35f5SJeremy Kerr static const int n_internal_pollfds = 1; 67329a35f5SJeremy Kerr 68769cee1aSJeremy Kerr /* state shared with the signal handler */ 69769cee1aSJeremy Kerr static bool sigint; 70329a35f5SJeremy Kerr 71d831f960SJeremy Kerr static void usage(const char *progname) 72d831f960SJeremy Kerr { 73d831f960SJeremy Kerr fprintf(stderr, 746221ce94SVishwanatha Subbanna "usage: %s [options] <DEVICE>\n" 75d831f960SJeremy Kerr "\n" 76d831f960SJeremy Kerr "Options:\n" 77d66195c1SJeremy Kerr " --config <FILE> Use FILE for configuration\n" 78d831f960SJeremy Kerr "", 79d831f960SJeremy Kerr progname); 80d831f960SJeremy Kerr } 81d831f960SJeremy Kerr 8217217845SJeremy Kerr /* populates tty_dev and tty_sysfs_devnode, using the tty kernel name */ 831a0e03b4SJeremy Kerr static int tty_find_device(struct console *console) 8417217845SJeremy Kerr { 8517217845SJeremy Kerr char *tty_class_device_link; 8617217845SJeremy Kerr char *tty_device_tty_dir; 8717217845SJeremy Kerr char *tty_device_reldir; 88*45ad7676SYi Li char *tty_path_input; 89*45ad7676SYi Li char *tty_path_input_real; 90*45ad7676SYi Li char *tty_kname_real; 9117217845SJeremy Kerr int rc; 9217217845SJeremy Kerr 9317217845SJeremy Kerr tty_class_device_link = NULL; 9417217845SJeremy Kerr tty_device_tty_dir = NULL; 9517217845SJeremy Kerr tty_device_reldir = NULL; 96*45ad7676SYi Li tty_path_input = NULL; 97*45ad7676SYi Li tty_path_input_real = NULL; 98*45ad7676SYi Li tty_kname_real = NULL; 9917217845SJeremy Kerr 100*45ad7676SYi Li /* udev may rename the tty name with a symbol link, try to resolve */ 101*45ad7676SYi Li rc = asprintf(&tty_path_input, "/dev/%s", console->tty_kname); 10217217845SJeremy Kerr if (rc < 0) 10317217845SJeremy Kerr return -1; 10417217845SJeremy Kerr 105*45ad7676SYi Li tty_path_input_real = realpath(tty_path_input, NULL); 106*45ad7676SYi Li if (!tty_path_input_real) { 107*45ad7676SYi Li warn("Can't find realpath for /dev/%s", console->tty_kname); 108*45ad7676SYi Li goto out_free; 109*45ad7676SYi Li } 110*45ad7676SYi Li 111*45ad7676SYi Li tty_kname_real = basename(tty_path_input_real); 112*45ad7676SYi Li if (!tty_kname_real) { 113*45ad7676SYi Li warn("Can't find real name for /dev/%s", console->tty_kname); 114*45ad7676SYi Li goto out_free; 115*45ad7676SYi Li } 116*45ad7676SYi Li 117*45ad7676SYi Li rc = asprintf(&tty_class_device_link, 118*45ad7676SYi Li "/sys/class/tty/%s", tty_kname_real); 119*45ad7676SYi Li if (rc < 0) 120*45ad7676SYi Li goto out_free; 121*45ad7676SYi Li 12217217845SJeremy Kerr tty_device_tty_dir = realpath(tty_class_device_link, NULL); 123*45ad7676SYi Li if (!tty_device_tty_dir) { 124*45ad7676SYi Li warn("Can't query sysfs for device %s", tty_kname_real); 12517217845SJeremy Kerr goto out_free; 12617217845SJeremy Kerr } 12717217845SJeremy Kerr 12817217845SJeremy Kerr rc = asprintf(&tty_device_reldir, "%s/../../", tty_device_tty_dir); 12917217845SJeremy Kerr if (rc < 0) 13017217845SJeremy Kerr goto out_free; 13117217845SJeremy Kerr 1321a0e03b4SJeremy Kerr console->tty_sysfs_devnode = realpath(tty_device_reldir, NULL); 1331a0e03b4SJeremy Kerr if (!console->tty_sysfs_devnode) 134*45ad7676SYi Li warn("Can't find parent device for %s", tty_kname_real); 13517217845SJeremy Kerr 136*45ad7676SYi Li rc = asprintf(&console->tty_dev, "/dev/%s", tty_kname_real); 13717217845SJeremy Kerr if (rc < 0) 13817217845SJeremy Kerr goto out_free; 13917217845SJeremy Kerr 14017217845SJeremy Kerr rc = 0; 14117217845SJeremy Kerr 14217217845SJeremy Kerr out_free: 14317217845SJeremy Kerr free(tty_class_device_link); 14417217845SJeremy Kerr free(tty_device_tty_dir); 14517217845SJeremy Kerr free(tty_device_reldir); 146*45ad7676SYi Li free(tty_path_input); 147*45ad7676SYi Li free(tty_path_input_real); 14817217845SJeremy Kerr return rc; 14917217845SJeremy Kerr } 15017217845SJeremy Kerr 1511a0e03b4SJeremy Kerr static int tty_set_sysfs_attr(struct console *console, const char *name, 152957818b4SJeremy Kerr int value) 153957818b4SJeremy Kerr { 154957818b4SJeremy Kerr char *path; 155957818b4SJeremy Kerr FILE *fp; 156957818b4SJeremy Kerr int rc; 157957818b4SJeremy Kerr 1581a0e03b4SJeremy Kerr rc = asprintf(&path, "%s/%s", console->tty_sysfs_devnode, name); 159957818b4SJeremy Kerr if (rc < 0) 160957818b4SJeremy Kerr return -1; 161957818b4SJeremy Kerr 162957818b4SJeremy Kerr fp = fopen(path, "w"); 163957818b4SJeremy Kerr if (!fp) { 164957818b4SJeremy Kerr warn("Can't access attribute %s on device %s", 1651a0e03b4SJeremy Kerr name, console->tty_kname); 166957818b4SJeremy Kerr rc = -1; 167957818b4SJeremy Kerr goto out_free; 168957818b4SJeremy Kerr } 169957818b4SJeremy Kerr setvbuf(fp, NULL, _IONBF, 0); 170957818b4SJeremy Kerr 171957818b4SJeremy Kerr rc = fprintf(fp, "0x%x", value); 172957818b4SJeremy Kerr if (rc < 0) 173957818b4SJeremy Kerr warn("Error writing to %s attribute of device %s", 1741a0e03b4SJeremy Kerr name, console->tty_kname); 175957818b4SJeremy Kerr fclose(fp); 176957818b4SJeremy Kerr 177957818b4SJeremy Kerr 178957818b4SJeremy Kerr 179957818b4SJeremy Kerr out_free: 180957818b4SJeremy Kerr free(path); 181957818b4SJeremy Kerr return rc; 182957818b4SJeremy Kerr } 183957818b4SJeremy Kerr 184d831f960SJeremy Kerr /** 18554e9569dSJeremy Kerr * Set console to raw mode: we don't want any processing to occur on 18654e9569dSJeremy Kerr * the underlying terminal input/output. 18754e9569dSJeremy Kerr */ 18854e9569dSJeremy Kerr static void tty_init_termios(struct console *console) 18954e9569dSJeremy Kerr { 19054e9569dSJeremy Kerr struct termios termios; 19154e9569dSJeremy Kerr int rc; 19254e9569dSJeremy Kerr 19354e9569dSJeremy Kerr rc = tcgetattr(console->tty_fd, &termios); 19454e9569dSJeremy Kerr if (rc) { 19554e9569dSJeremy Kerr warn("Can't read tty termios"); 19654e9569dSJeremy Kerr return; 19754e9569dSJeremy Kerr } 19854e9569dSJeremy Kerr 19954e9569dSJeremy Kerr cfmakeraw(&termios); 20054e9569dSJeremy Kerr rc = tcsetattr(console->tty_fd, TCSANOW, &termios); 20154e9569dSJeremy Kerr if (rc) 20254e9569dSJeremy Kerr warn("Can't set terminal raw mode for tty"); 20354e9569dSJeremy Kerr } 20454e9569dSJeremy Kerr 20554e9569dSJeremy Kerr /** 206d831f960SJeremy Kerr * Open and initialise the serial device 207d831f960SJeremy Kerr */ 2081a0e03b4SJeremy Kerr static int tty_init_io(struct console *console) 209d831f960SJeremy Kerr { 2101a0e03b4SJeremy Kerr if (console->tty_sirq) 2111a0e03b4SJeremy Kerr tty_set_sysfs_attr(console, "sirq", console->tty_sirq); 2121a0e03b4SJeremy Kerr if (console->tty_lpc_addr) 2131a0e03b4SJeremy Kerr tty_set_sysfs_attr(console, "lpc_address", 2141a0e03b4SJeremy Kerr console->tty_lpc_addr); 215957818b4SJeremy Kerr 2161a0e03b4SJeremy Kerr console->tty_fd = open(console->tty_dev, O_RDWR); 2171a0e03b4SJeremy Kerr if (console->tty_fd <= 0) { 2181a0e03b4SJeremy Kerr warn("Can't open tty %s", console->tty_dev); 219d831f960SJeremy Kerr return -1; 220d831f960SJeremy Kerr } 221d831f960SJeremy Kerr 222d831f960SJeremy Kerr /* Disable character delay. We may want to later enable this when 223d831f960SJeremy Kerr * we detect larger amounts of data 224d831f960SJeremy Kerr */ 2251a0e03b4SJeremy Kerr fcntl(console->tty_fd, F_SETFL, FNDELAY); 226d831f960SJeremy Kerr 22754e9569dSJeremy Kerr tty_init_termios(console); 22854e9569dSJeremy Kerr 229329a35f5SJeremy Kerr console->pollfds[console->n_pollers].fd = console->tty_fd; 230329a35f5SJeremy Kerr console->pollfds[console->n_pollers].events = POLLIN; 231329a35f5SJeremy Kerr 232d831f960SJeremy Kerr return 0; 233d831f960SJeremy Kerr } 234d831f960SJeremy Kerr 235d66195c1SJeremy Kerr static int tty_init(struct console *console, struct config *config) 236d66195c1SJeremy Kerr { 237d66195c1SJeremy Kerr const char *val; 238d66195c1SJeremy Kerr char *endp; 239d66195c1SJeremy Kerr int rc; 240d66195c1SJeremy Kerr 241d66195c1SJeremy Kerr val = config_get_value(config, "lpc-address"); 242d66195c1SJeremy Kerr if (val) { 243d66195c1SJeremy Kerr console->tty_lpc_addr = strtoul(val, &endp, 0); 244d66195c1SJeremy Kerr if (endp == optarg) { 245d66195c1SJeremy Kerr warn("Invalid LPC address: '%s'", val); 246d66195c1SJeremy Kerr return -1; 247d66195c1SJeremy Kerr } 248d66195c1SJeremy Kerr } 249d66195c1SJeremy Kerr 250d66195c1SJeremy Kerr val = config_get_value(config, "sirq"); 251d66195c1SJeremy Kerr if (val) { 252d66195c1SJeremy Kerr console->tty_sirq = strtoul(val, &endp, 0); 253d66195c1SJeremy Kerr if (endp == optarg) 254d66195c1SJeremy Kerr warn("Invalid sirq: '%s'", val); 255d66195c1SJeremy Kerr } 256d66195c1SJeremy Kerr 257d66195c1SJeremy Kerr if (!console->tty_kname) { 258d66195c1SJeremy Kerr warnx("Error: No TTY device specified"); 259d66195c1SJeremy Kerr return -1; 260d66195c1SJeremy Kerr } 261d66195c1SJeremy Kerr 262d66195c1SJeremy Kerr rc = tty_find_device(console); 263d66195c1SJeremy Kerr if (rc) 264d66195c1SJeremy Kerr return rc; 265d66195c1SJeremy Kerr 266d66195c1SJeremy Kerr rc = tty_init_io(console); 267d66195c1SJeremy Kerr return rc; 268d66195c1SJeremy Kerr } 269d66195c1SJeremy Kerr 2701a0e03b4SJeremy Kerr 2711a0e03b4SJeremy Kerr int console_data_out(struct console *console, const uint8_t *data, size_t len) 272d831f960SJeremy Kerr { 2731a0e03b4SJeremy Kerr return write_buf_to_fd(console->tty_fd, data, len); 274d831f960SJeremy Kerr } 275d831f960SJeremy Kerr 276d47963e5SJeremy Kerr static void handlers_init(struct console *console, struct config *config) 277d831f960SJeremy Kerr { 2781a0e03b4SJeremy Kerr extern struct handler *__start_handlers, *__stop_handlers; 2791a0e03b4SJeremy Kerr struct handler *handler; 280021b91f0SJeremy Kerr int i, rc; 281d831f960SJeremy Kerr 2821a0e03b4SJeremy Kerr console->n_handlers = &__stop_handlers - &__start_handlers; 2831a0e03b4SJeremy Kerr console->handlers = &__start_handlers; 284d831f960SJeremy Kerr 2851a0e03b4SJeremy Kerr printf("%d handler%s\n", console->n_handlers, 2861a0e03b4SJeremy Kerr console->n_handlers == 1 ? "" : "s"); 287d831f960SJeremy Kerr 2881a0e03b4SJeremy Kerr for (i = 0; i < console->n_handlers; i++) { 2891a0e03b4SJeremy Kerr handler = console->handlers[i]; 2901a0e03b4SJeremy Kerr 291021b91f0SJeremy Kerr rc = 0; 2921a0e03b4SJeremy Kerr if (handler->init) 293021b91f0SJeremy Kerr rc = handler->init(handler, console, config); 294021b91f0SJeremy Kerr 295021b91f0SJeremy Kerr handler->active = rc == 0; 296021b91f0SJeremy Kerr 297021b91f0SJeremy Kerr printf(" %s [%sactive]\n", handler->name, 298021b91f0SJeremy Kerr handler->active ? "" : "in"); 299d831f960SJeremy Kerr } 300d831f960SJeremy Kerr } 301d831f960SJeremy Kerr 3021a0e03b4SJeremy Kerr static void handlers_fini(struct console *console) 303d831f960SJeremy Kerr { 3041a0e03b4SJeremy Kerr struct handler *handler; 3051a0e03b4SJeremy Kerr int i; 3061a0e03b4SJeremy Kerr 3071a0e03b4SJeremy Kerr for (i = 0; i < console->n_handlers; i++) { 3081a0e03b4SJeremy Kerr handler = console->handlers[i]; 309021b91f0SJeremy Kerr if (handler->fini && handler->active) 3101a0e03b4SJeremy Kerr handler->fini(handler); 3111a0e03b4SJeremy Kerr } 312d831f960SJeremy Kerr } 313d831f960SJeremy Kerr 3141a0e03b4SJeremy Kerr static int handlers_data_in(struct console *console, uint8_t *buf, size_t len) 315d831f960SJeremy Kerr { 3161a0e03b4SJeremy Kerr struct handler *handler; 3171a0e03b4SJeremy Kerr int i, rc, tmp; 318d831f960SJeremy Kerr 3191a0e03b4SJeremy Kerr rc = 0; 3201a0e03b4SJeremy Kerr 3211a0e03b4SJeremy Kerr for (i = 0; i < console->n_handlers; i++) { 3221a0e03b4SJeremy Kerr handler = console->handlers[i]; 3231a0e03b4SJeremy Kerr 324021b91f0SJeremy Kerr if (!handler->active) 325021b91f0SJeremy Kerr continue; 326021b91f0SJeremy Kerr 3271a0e03b4SJeremy Kerr if (!handler->data_in) 3281a0e03b4SJeremy Kerr continue; 3291a0e03b4SJeremy Kerr 3301a0e03b4SJeremy Kerr tmp = handler->data_in(handler, buf, len); 3311a0e03b4SJeremy Kerr if (tmp == HANDLER_EXIT) 3321a0e03b4SJeremy Kerr rc = 1; 333d831f960SJeremy Kerr } 334d831f960SJeremy Kerr 3351a0e03b4SJeremy Kerr return rc; 336329a35f5SJeremy Kerr 337d831f960SJeremy Kerr } 338d831f960SJeremy Kerr 339329a35f5SJeremy Kerr struct poller *console_register_poller(struct console *console, 340329a35f5SJeremy Kerr struct handler *handler, poller_fn_t poller_fn, 341329a35f5SJeremy Kerr int fd, int events, void *data) 342d831f960SJeremy Kerr { 343329a35f5SJeremy Kerr struct poller *poller; 344329a35f5SJeremy Kerr int n; 345329a35f5SJeremy Kerr 346329a35f5SJeremy Kerr poller = malloc(sizeof(*poller)); 347329a35f5SJeremy Kerr poller->remove = false; 348329a35f5SJeremy Kerr poller->handler = handler; 349329a35f5SJeremy Kerr poller->fn = poller_fn; 350329a35f5SJeremy Kerr poller->data = data; 351329a35f5SJeremy Kerr 352329a35f5SJeremy Kerr /* add one to our pollers array */ 353329a35f5SJeremy Kerr n = console->n_pollers++; 354329a35f5SJeremy Kerr console->pollers = realloc(console->pollers, 355329a35f5SJeremy Kerr sizeof(*console->pollers) * console->n_pollers); 356329a35f5SJeremy Kerr 357329a35f5SJeremy Kerr console->pollers[n] = poller; 358329a35f5SJeremy Kerr 359329a35f5SJeremy Kerr /* increase pollfds array too */ 360329a35f5SJeremy Kerr console->pollfds = realloc(console->pollfds, 361329a35f5SJeremy Kerr sizeof(*console->pollfds) * 362329a35f5SJeremy Kerr (n_internal_pollfds + console->n_pollers)); 363329a35f5SJeremy Kerr 364329a35f5SJeremy Kerr /* shift the end pollfds up by one */ 365329a35f5SJeremy Kerr memcpy(&console->pollfds[n+n_internal_pollfds], 366329a35f5SJeremy Kerr &console->pollfds[n], 367329a35f5SJeremy Kerr sizeof(*console->pollfds) * n_internal_pollfds); 368329a35f5SJeremy Kerr 369329a35f5SJeremy Kerr console->pollfds[n].fd = fd; 370329a35f5SJeremy Kerr console->pollfds[n].events = events; 371329a35f5SJeremy Kerr 372329a35f5SJeremy Kerr return poller; 373329a35f5SJeremy Kerr } 374329a35f5SJeremy Kerr 375329a35f5SJeremy Kerr void console_unregister_poller(struct console *console, 376329a35f5SJeremy Kerr struct poller *poller) 377329a35f5SJeremy Kerr { 378329a35f5SJeremy Kerr int i; 379329a35f5SJeremy Kerr 380329a35f5SJeremy Kerr /* find the entry in our pollers array */ 381329a35f5SJeremy Kerr for (i = 0; i < console->n_pollers; i++) 382329a35f5SJeremy Kerr if (console->pollers[i] == poller) 383329a35f5SJeremy Kerr break; 384329a35f5SJeremy Kerr 385329a35f5SJeremy Kerr assert(i < console->n_pollers); 386329a35f5SJeremy Kerr 387329a35f5SJeremy Kerr console->n_pollers--; 388329a35f5SJeremy Kerr 389329a35f5SJeremy Kerr /* remove the item from the pollers array... */ 390329a35f5SJeremy Kerr memmove(&console->pollers[i], &console->pollers[i+1], 391329a35f5SJeremy Kerr sizeof(*console->pollers) 392329a35f5SJeremy Kerr * (console->n_pollers - i)); 393329a35f5SJeremy Kerr 394329a35f5SJeremy Kerr console->pollers = realloc(console->pollers, 395329a35f5SJeremy Kerr sizeof(*console->pollers) * console->n_pollers); 396329a35f5SJeremy Kerr 397329a35f5SJeremy Kerr /* ... and the pollfds array */ 398329a35f5SJeremy Kerr memmove(&console->pollfds[i], &console->pollfds[i+1], 399329a35f5SJeremy Kerr sizeof(*console->pollfds) * 400329a35f5SJeremy Kerr (n_internal_pollfds + console->n_pollers - i)); 401329a35f5SJeremy Kerr 402329a35f5SJeremy Kerr console->pollfds = realloc(console->pollfds, 403329a35f5SJeremy Kerr sizeof(*console->pollfds) * 404329a35f5SJeremy Kerr (n_internal_pollfds + console->n_pollers)); 405329a35f5SJeremy Kerr 406329a35f5SJeremy Kerr 407329a35f5SJeremy Kerr free(poller); 408329a35f5SJeremy Kerr } 409329a35f5SJeremy Kerr 410329a35f5SJeremy Kerr static int call_pollers(struct console *console) 411329a35f5SJeremy Kerr { 412329a35f5SJeremy Kerr struct poller *poller; 413329a35f5SJeremy Kerr struct pollfd *pollfd; 414329a35f5SJeremy Kerr enum poller_ret prc; 415329a35f5SJeremy Kerr int i, rc; 416d831f960SJeremy Kerr 4171a0e03b4SJeremy Kerr rc = 0; 4181a0e03b4SJeremy Kerr 419329a35f5SJeremy Kerr /* 420329a35f5SJeremy Kerr * Process poll events by iterating through the pollers and pollfds 421329a35f5SJeremy Kerr * in-step, calling any pollers that we've found revents for. 422329a35f5SJeremy Kerr */ 423329a35f5SJeremy Kerr for (i = 0; i < console->n_pollers; i++) { 424329a35f5SJeremy Kerr poller = console->pollers[i]; 425329a35f5SJeremy Kerr pollfd = &console->pollfds[i]; 4261a0e03b4SJeremy Kerr 427329a35f5SJeremy Kerr if (!pollfd->revents) 4281a0e03b4SJeremy Kerr continue; 4291a0e03b4SJeremy Kerr 430329a35f5SJeremy Kerr prc = poller->fn(poller->handler, pollfd->revents, 431329a35f5SJeremy Kerr poller->data); 432329a35f5SJeremy Kerr if (prc == POLLER_EXIT) 433329a35f5SJeremy Kerr rc = -1; 434329a35f5SJeremy Kerr else if (prc == POLLER_REMOVE) 435329a35f5SJeremy Kerr poller->remove = true; 436329a35f5SJeremy Kerr } 437329a35f5SJeremy Kerr 438329a35f5SJeremy Kerr /** 439329a35f5SJeremy Kerr * Process deferred removals; restarting each time we unregister, as 440329a35f5SJeremy Kerr * the array will have changed 441329a35f5SJeremy Kerr */ 442329a35f5SJeremy Kerr for (;;) { 443329a35f5SJeremy Kerr bool removed = false; 444329a35f5SJeremy Kerr 445329a35f5SJeremy Kerr for (i = 0; i < console->n_pollers; i++) { 446329a35f5SJeremy Kerr poller = console->pollers[i]; 447329a35f5SJeremy Kerr if (poller->remove) { 448329a35f5SJeremy Kerr console_unregister_poller(console, poller); 449329a35f5SJeremy Kerr removed = true; 450329a35f5SJeremy Kerr break; 451329a35f5SJeremy Kerr } 452329a35f5SJeremy Kerr } 453329a35f5SJeremy Kerr if (!removed) 454329a35f5SJeremy Kerr break; 4551a0e03b4SJeremy Kerr } 4561a0e03b4SJeremy Kerr 4571a0e03b4SJeremy Kerr return rc; 4581a0e03b4SJeremy Kerr } 4591a0e03b4SJeremy Kerr 460769cee1aSJeremy Kerr static void sighandler(int signal) 461769cee1aSJeremy Kerr { 462769cee1aSJeremy Kerr if (signal == SIGINT) 463769cee1aSJeremy Kerr sigint = true; 464769cee1aSJeremy Kerr } 465769cee1aSJeremy Kerr 4661a0e03b4SJeremy Kerr int run_console(struct console *console) 4671a0e03b4SJeremy Kerr { 468769cee1aSJeremy Kerr sighandler_t sighandler_save; 469329a35f5SJeremy Kerr int rc; 470d831f960SJeremy Kerr 471769cee1aSJeremy Kerr sighandler_save = signal(SIGINT, sighandler); 472769cee1aSJeremy Kerr 473769cee1aSJeremy Kerr rc = 0; 474769cee1aSJeremy Kerr 475d831f960SJeremy Kerr for (;;) { 476d831f960SJeremy Kerr uint8_t buf[4096]; 477d831f960SJeremy Kerr 478769cee1aSJeremy Kerr if (sigint) { 479769cee1aSJeremy Kerr fprintf(stderr, "Received interrupt, exiting\n"); 480769cee1aSJeremy Kerr break; 481769cee1aSJeremy Kerr } 482769cee1aSJeremy Kerr 483329a35f5SJeremy Kerr rc = poll(console->pollfds, 484329a35f5SJeremy Kerr console->n_pollers + n_internal_pollfds, -1); 485d831f960SJeremy Kerr if (rc < 0) { 486769cee1aSJeremy Kerr if (errno == EINTR) { 487769cee1aSJeremy Kerr continue; 488769cee1aSJeremy Kerr } else { 489d831f960SJeremy Kerr warn("poll error"); 490769cee1aSJeremy Kerr break; 491769cee1aSJeremy Kerr } 492d831f960SJeremy Kerr } 493d831f960SJeremy Kerr 494329a35f5SJeremy Kerr /* process internal fd first */ 495329a35f5SJeremy Kerr BUILD_ASSERT(n_internal_pollfds == 1); 496329a35f5SJeremy Kerr 497329a35f5SJeremy Kerr if (console->pollfds[console->n_pollers].revents) { 4981a0e03b4SJeremy Kerr rc = read(console->tty_fd, buf, sizeof(buf)); 499d831f960SJeremy Kerr if (rc <= 0) { 500d831f960SJeremy Kerr warn("Error reading from tty device"); 501769cee1aSJeremy Kerr rc = -1; 502769cee1aSJeremy Kerr break; 503d831f960SJeremy Kerr } 5041a0e03b4SJeremy Kerr rc = handlers_data_in(console, buf, rc); 5051a0e03b4SJeremy Kerr if (rc) 506769cee1aSJeremy Kerr break; 507d831f960SJeremy Kerr } 508d831f960SJeremy Kerr 509329a35f5SJeremy Kerr /* ... and then the pollers */ 510329a35f5SJeremy Kerr rc = call_pollers(console); 5111a0e03b4SJeremy Kerr if (rc) 512769cee1aSJeremy Kerr break; 5131a0e03b4SJeremy Kerr } 514769cee1aSJeremy Kerr 515769cee1aSJeremy Kerr signal(SIGINT, sighandler_save); 516769cee1aSJeremy Kerr 517769cee1aSJeremy Kerr return rc ? -1 : 0; 5181a0e03b4SJeremy Kerr } 519d831f960SJeremy Kerr static const struct option options[] = { 520d66195c1SJeremy Kerr { "config", required_argument, 0, 'c'}, 521f5858b5bSJoel Stanley { 0, 0, 0, 0}, 522d831f960SJeremy Kerr }; 523d831f960SJeremy Kerr 524d831f960SJeremy Kerr int main(int argc, char **argv) 525d831f960SJeremy Kerr { 526d66195c1SJeremy Kerr const char *config_filename = NULL; 5276221ce94SVishwanatha Subbanna const char *config_tty_kname = NULL; 5281a0e03b4SJeremy Kerr struct console *console; 529d66195c1SJeremy Kerr struct config *config; 530d66195c1SJeremy Kerr int rc; 531d831f960SJeremy Kerr 532957818b4SJeremy Kerr rc = -1; 533d831f960SJeremy Kerr 534d831f960SJeremy Kerr for (;;) { 535d831f960SJeremy Kerr int c, idx; 536d831f960SJeremy Kerr 537d66195c1SJeremy Kerr c = getopt_long(argc, argv, "c:", options, &idx); 538d831f960SJeremy Kerr if (c == -1) 539d831f960SJeremy Kerr break; 540d831f960SJeremy Kerr 541d831f960SJeremy Kerr switch (c) { 542d66195c1SJeremy Kerr case 'c': 543d66195c1SJeremy Kerr config_filename = optarg; 544d831f960SJeremy Kerr break; 545d831f960SJeremy Kerr case 'h': 546d831f960SJeremy Kerr case '?': 547d831f960SJeremy Kerr usage(argv[0]); 548d66195c1SJeremy Kerr return EXIT_SUCCESS; 549d831f960SJeremy Kerr } 550d831f960SJeremy Kerr } 551d831f960SJeremy Kerr 5526221ce94SVishwanatha Subbanna if (optind >= argc) { 5536221ce94SVishwanatha Subbanna warnx("Required argument <DEVICE> missing"); 5546221ce94SVishwanatha Subbanna usage(argv[0]); 5556221ce94SVishwanatha Subbanna return EXIT_FAILURE; 5566221ce94SVishwanatha Subbanna } 5576221ce94SVishwanatha Subbanna 5586221ce94SVishwanatha Subbanna config_tty_kname = argv[optind]; 5596221ce94SVishwanatha Subbanna 560d66195c1SJeremy Kerr console = malloc(sizeof(struct console)); 561d66195c1SJeremy Kerr memset(console, 0, sizeof(*console)); 562329a35f5SJeremy Kerr console->pollfds = calloc(n_internal_pollfds, 563329a35f5SJeremy Kerr sizeof(*console->pollfds)); 564329a35f5SJeremy Kerr 565d66195c1SJeremy Kerr config = config_init(config_filename); 566d66195c1SJeremy Kerr if (!config) { 567d66195c1SJeremy Kerr warnx("Can't read configuration, exiting."); 568d66195c1SJeremy Kerr goto out_free; 569d831f960SJeremy Kerr } 570d831f960SJeremy Kerr 5716221ce94SVishwanatha Subbanna console->tty_kname = config_tty_kname; 5726221ce94SVishwanatha Subbanna 573d66195c1SJeremy Kerr rc = tty_init(console, config); 57417217845SJeremy Kerr if (rc) 575d66195c1SJeremy Kerr goto out_config_fini; 576d831f960SJeremy Kerr 577d47963e5SJeremy Kerr handlers_init(console, config); 578d831f960SJeremy Kerr 5791a0e03b4SJeremy Kerr rc = run_console(console); 580d831f960SJeremy Kerr 5811a0e03b4SJeremy Kerr handlers_fini(console); 582d831f960SJeremy Kerr 583d66195c1SJeremy Kerr out_config_fini: 584d66195c1SJeremy Kerr config_fini(config); 585d66195c1SJeremy Kerr 586957818b4SJeremy Kerr out_free: 58789ea8198SJeremy Kerr free(console->pollers); 58889ea8198SJeremy Kerr free(console->pollfds); 5891a0e03b4SJeremy Kerr free(console->tty_sysfs_devnode); 5901a0e03b4SJeremy Kerr free(console->tty_dev); 5911a0e03b4SJeremy Kerr free(console); 592d831f960SJeremy Kerr 593d831f960SJeremy Kerr return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE; 594d831f960SJeremy Kerr } 595