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 41*f9c8f6caSCheng C Yang #define DBUS_ERR "org.openbmc.error" 42*f9c8f6caSCheng C Yang #define DBUS_NAME "xyz.openbmc_project.console" 43*f9c8f6caSCheng C Yang #define OBJ_NAME "/xyz/openbmc_project/console" 44*f9c8f6caSCheng C Yang 451a0e03b4SJeremy Kerr struct console { 4617217845SJeremy Kerr const char *tty_kname; 4717217845SJeremy Kerr char *tty_sysfs_devnode; 4817217845SJeremy Kerr char *tty_dev; 49957818b4SJeremy Kerr int tty_sirq; 50957818b4SJeremy Kerr int tty_lpc_addr; 51c7fbcd48SBenjamin Fair speed_t tty_baud; 52d831f960SJeremy Kerr int tty_fd; 53329a35f5SJeremy Kerr 54f733c85aSJeremy Kerr struct ringbuffer *rb; 55f733c85aSJeremy Kerr 561a0e03b4SJeremy Kerr struct handler **handlers; 571a0e03b4SJeremy Kerr int n_handlers; 58329a35f5SJeremy Kerr 59329a35f5SJeremy Kerr struct poller **pollers; 60329a35f5SJeremy Kerr int n_pollers; 61329a35f5SJeremy Kerr 62329a35f5SJeremy Kerr struct pollfd *pollfds; 63*f9c8f6caSCheng C Yang struct sd_bus *bus; 64d831f960SJeremy Kerr }; 65d831f960SJeremy Kerr 66329a35f5SJeremy Kerr struct poller { 67329a35f5SJeremy Kerr struct handler *handler; 68329a35f5SJeremy Kerr void *data; 69329a35f5SJeremy Kerr poller_fn_t fn; 70329a35f5SJeremy Kerr bool remove; 71329a35f5SJeremy Kerr }; 72329a35f5SJeremy Kerr 73*f9c8f6caSCheng C Yang /* we have two extra entry in the pollfds array for the VUART tty */ 74*f9c8f6caSCheng C Yang enum internal_pollfds { 75*f9c8f6caSCheng C Yang POLLFD_HOSTTTY = 0, 76*f9c8f6caSCheng C Yang POLLFD_DBUS = 1, 77*f9c8f6caSCheng C Yang MAX_INTERNAL_POLLFD = 2, 78*f9c8f6caSCheng C Yang }; 79329a35f5SJeremy Kerr 80f733c85aSJeremy Kerr /* size of the shared backlog ringbuffer */ 81f733c85aSJeremy Kerr const size_t buffer_size = 128 * 1024; 82f733c85aSJeremy Kerr 83769cee1aSJeremy Kerr /* state shared with the signal handler */ 84769cee1aSJeremy Kerr static bool sigint; 85329a35f5SJeremy Kerr 86d831f960SJeremy Kerr static void usage(const char *progname) 87d831f960SJeremy Kerr { 88d831f960SJeremy Kerr fprintf(stderr, 896221ce94SVishwanatha Subbanna "usage: %s [options] <DEVICE>\n" 90d831f960SJeremy Kerr "\n" 91d831f960SJeremy Kerr "Options:\n" 92d66195c1SJeremy Kerr " --config <FILE> Use FILE for configuration\n" 93d831f960SJeremy Kerr "", 94d831f960SJeremy Kerr progname); 95d831f960SJeremy Kerr } 96d831f960SJeremy Kerr 9717217845SJeremy Kerr /* populates tty_dev and tty_sysfs_devnode, using the tty kernel name */ 981a0e03b4SJeremy Kerr static int tty_find_device(struct console *console) 9917217845SJeremy Kerr { 10017217845SJeremy Kerr char *tty_class_device_link; 10117217845SJeremy Kerr char *tty_device_tty_dir; 10217217845SJeremy Kerr char *tty_device_reldir; 10345ad7676SYi Li char *tty_path_input; 10445ad7676SYi Li char *tty_path_input_real; 10545ad7676SYi Li char *tty_kname_real; 10617217845SJeremy Kerr int rc; 10717217845SJeremy Kerr 10817217845SJeremy Kerr tty_class_device_link = NULL; 10917217845SJeremy Kerr tty_device_tty_dir = NULL; 11017217845SJeremy Kerr tty_device_reldir = NULL; 11145ad7676SYi Li tty_path_input = NULL; 11245ad7676SYi Li tty_path_input_real = NULL; 11345ad7676SYi Li tty_kname_real = NULL; 11417217845SJeremy Kerr 11545ad7676SYi Li /* udev may rename the tty name with a symbol link, try to resolve */ 11645ad7676SYi Li rc = asprintf(&tty_path_input, "/dev/%s", console->tty_kname); 11717217845SJeremy Kerr if (rc < 0) 11817217845SJeremy Kerr return -1; 11917217845SJeremy Kerr 12045ad7676SYi Li tty_path_input_real = realpath(tty_path_input, NULL); 12145ad7676SYi Li if (!tty_path_input_real) { 12245ad7676SYi Li warn("Can't find realpath for /dev/%s", console->tty_kname); 12345ad7676SYi Li goto out_free; 12445ad7676SYi Li } 12545ad7676SYi Li 12645ad7676SYi Li tty_kname_real = basename(tty_path_input_real); 12745ad7676SYi Li if (!tty_kname_real) { 12845ad7676SYi Li warn("Can't find real name for /dev/%s", console->tty_kname); 12945ad7676SYi Li goto out_free; 13045ad7676SYi Li } 13145ad7676SYi Li 13245ad7676SYi Li rc = asprintf(&tty_class_device_link, 13345ad7676SYi Li "/sys/class/tty/%s", tty_kname_real); 13445ad7676SYi Li if (rc < 0) 13545ad7676SYi Li goto out_free; 13645ad7676SYi Li 13717217845SJeremy Kerr tty_device_tty_dir = realpath(tty_class_device_link, NULL); 13845ad7676SYi Li if (!tty_device_tty_dir) { 13945ad7676SYi Li warn("Can't query sysfs for device %s", tty_kname_real); 14017217845SJeremy Kerr goto out_free; 14117217845SJeremy Kerr } 14217217845SJeremy Kerr 14317217845SJeremy Kerr rc = asprintf(&tty_device_reldir, "%s/../../", tty_device_tty_dir); 14417217845SJeremy Kerr if (rc < 0) 14517217845SJeremy Kerr goto out_free; 14617217845SJeremy Kerr 1471a0e03b4SJeremy Kerr console->tty_sysfs_devnode = realpath(tty_device_reldir, NULL); 1481a0e03b4SJeremy Kerr if (!console->tty_sysfs_devnode) 14945ad7676SYi Li warn("Can't find parent device for %s", tty_kname_real); 15017217845SJeremy Kerr 15145ad7676SYi Li rc = asprintf(&console->tty_dev, "/dev/%s", tty_kname_real); 15217217845SJeremy Kerr if (rc < 0) 15317217845SJeremy Kerr goto out_free; 15417217845SJeremy Kerr 15517217845SJeremy Kerr rc = 0; 15617217845SJeremy Kerr 15717217845SJeremy Kerr out_free: 15817217845SJeremy Kerr free(tty_class_device_link); 15917217845SJeremy Kerr free(tty_device_tty_dir); 16017217845SJeremy Kerr free(tty_device_reldir); 16145ad7676SYi Li free(tty_path_input); 16245ad7676SYi Li free(tty_path_input_real); 16317217845SJeremy Kerr return rc; 16417217845SJeremy Kerr } 16517217845SJeremy Kerr 1661a0e03b4SJeremy Kerr static int tty_set_sysfs_attr(struct console *console, const char *name, 167957818b4SJeremy Kerr int value) 168957818b4SJeremy Kerr { 169957818b4SJeremy Kerr char *path; 170957818b4SJeremy Kerr FILE *fp; 171957818b4SJeremy Kerr int rc; 172957818b4SJeremy Kerr 1731a0e03b4SJeremy Kerr rc = asprintf(&path, "%s/%s", console->tty_sysfs_devnode, name); 174957818b4SJeremy Kerr if (rc < 0) 175957818b4SJeremy Kerr return -1; 176957818b4SJeremy Kerr 177957818b4SJeremy Kerr fp = fopen(path, "w"); 178957818b4SJeremy Kerr if (!fp) { 179957818b4SJeremy Kerr warn("Can't access attribute %s on device %s", 1801a0e03b4SJeremy Kerr name, console->tty_kname); 181957818b4SJeremy Kerr rc = -1; 182957818b4SJeremy Kerr goto out_free; 183957818b4SJeremy Kerr } 184957818b4SJeremy Kerr setvbuf(fp, NULL, _IONBF, 0); 185957818b4SJeremy Kerr 186957818b4SJeremy Kerr rc = fprintf(fp, "0x%x", value); 187957818b4SJeremy Kerr if (rc < 0) 188957818b4SJeremy Kerr warn("Error writing to %s attribute of device %s", 1891a0e03b4SJeremy Kerr name, console->tty_kname); 190957818b4SJeremy Kerr fclose(fp); 191957818b4SJeremy Kerr 192957818b4SJeremy Kerr 193957818b4SJeremy Kerr 194957818b4SJeremy Kerr out_free: 195957818b4SJeremy Kerr free(path); 196957818b4SJeremy Kerr return rc; 197957818b4SJeremy Kerr } 198957818b4SJeremy Kerr 199d831f960SJeremy Kerr /** 200c7fbcd48SBenjamin Fair * Set termios attributes on the console tty. 20154e9569dSJeremy Kerr */ 20254e9569dSJeremy Kerr static void tty_init_termios(struct console *console) 20354e9569dSJeremy Kerr { 20454e9569dSJeremy Kerr struct termios termios; 20554e9569dSJeremy Kerr int rc; 20654e9569dSJeremy Kerr 20754e9569dSJeremy Kerr rc = tcgetattr(console->tty_fd, &termios); 20854e9569dSJeremy Kerr if (rc) { 20954e9569dSJeremy Kerr warn("Can't read tty termios"); 21054e9569dSJeremy Kerr return; 21154e9569dSJeremy Kerr } 21254e9569dSJeremy Kerr 213c7fbcd48SBenjamin Fair if (console->tty_baud) { 214c7fbcd48SBenjamin Fair if (cfsetspeed(&termios, console->tty_baud) < 0) 215c7fbcd48SBenjamin Fair warn("Couldn't set speeds for %s", console->tty_kname); 216c7fbcd48SBenjamin Fair } 217c7fbcd48SBenjamin Fair 218c7fbcd48SBenjamin Fair /* Set console to raw mode: we don't want any processing to occur on 219c7fbcd48SBenjamin Fair * the underlying terminal input/output. 220c7fbcd48SBenjamin Fair */ 22154e9569dSJeremy Kerr cfmakeraw(&termios); 222c7fbcd48SBenjamin Fair 22354e9569dSJeremy Kerr rc = tcsetattr(console->tty_fd, TCSANOW, &termios); 22454e9569dSJeremy Kerr if (rc) 225c7fbcd48SBenjamin Fair warn("Can't set terminal options for %s", console->tty_kname); 22654e9569dSJeremy Kerr } 22754e9569dSJeremy Kerr 228*f9c8f6caSCheng C Yang 229*f9c8f6caSCheng C Yang static void tty_change_baudrate(struct console *console) 230*f9c8f6caSCheng C Yang { 231*f9c8f6caSCheng C Yang struct handler *handler; 232*f9c8f6caSCheng C Yang int i, rc; 233*f9c8f6caSCheng C Yang 234*f9c8f6caSCheng C Yang tty_init_termios(console); 235*f9c8f6caSCheng C Yang 236*f9c8f6caSCheng C Yang for (i = 0; i < console->n_handlers; i++) { 237*f9c8f6caSCheng C Yang handler = console->handlers[i]; 238*f9c8f6caSCheng C Yang if (!handler->baudrate) 239*f9c8f6caSCheng C Yang continue; 240*f9c8f6caSCheng C Yang 241*f9c8f6caSCheng C Yang rc = handler->baudrate(handler, console->tty_baud); 242*f9c8f6caSCheng C Yang if (rc) 243*f9c8f6caSCheng C Yang warnx("Can't set terminal baudrate for handler %s", 244*f9c8f6caSCheng C Yang handler->name); 245*f9c8f6caSCheng C Yang } 246*f9c8f6caSCheng C Yang } 247*f9c8f6caSCheng C Yang 24854e9569dSJeremy Kerr /** 249d831f960SJeremy Kerr * Open and initialise the serial device 250d831f960SJeremy Kerr */ 2511a0e03b4SJeremy Kerr static int tty_init_io(struct console *console) 252d831f960SJeremy Kerr { 2531a0e03b4SJeremy Kerr if (console->tty_sirq) 2541a0e03b4SJeremy Kerr tty_set_sysfs_attr(console, "sirq", console->tty_sirq); 2551a0e03b4SJeremy Kerr if (console->tty_lpc_addr) 2561a0e03b4SJeremy Kerr tty_set_sysfs_attr(console, "lpc_address", 2571a0e03b4SJeremy Kerr console->tty_lpc_addr); 258957818b4SJeremy Kerr 2591a0e03b4SJeremy Kerr console->tty_fd = open(console->tty_dev, O_RDWR); 2601a0e03b4SJeremy Kerr if (console->tty_fd <= 0) { 2611a0e03b4SJeremy Kerr warn("Can't open tty %s", console->tty_dev); 262d831f960SJeremy Kerr return -1; 263d831f960SJeremy Kerr } 264d831f960SJeremy Kerr 265d831f960SJeremy Kerr /* Disable character delay. We may want to later enable this when 266d831f960SJeremy Kerr * we detect larger amounts of data 267d831f960SJeremy Kerr */ 2681a0e03b4SJeremy Kerr fcntl(console->tty_fd, F_SETFL, FNDELAY); 269d831f960SJeremy Kerr 27054e9569dSJeremy Kerr tty_init_termios(console); 27154e9569dSJeremy Kerr 272329a35f5SJeremy Kerr console->pollfds[console->n_pollers].fd = console->tty_fd; 273329a35f5SJeremy Kerr console->pollfds[console->n_pollers].events = POLLIN; 274329a35f5SJeremy Kerr 275d831f960SJeremy Kerr return 0; 276d831f960SJeremy Kerr } 277d831f960SJeremy Kerr 278d66195c1SJeremy Kerr static int tty_init(struct console *console, struct config *config) 279d66195c1SJeremy Kerr { 280d66195c1SJeremy Kerr const char *val; 281d66195c1SJeremy Kerr char *endp; 282d66195c1SJeremy Kerr int rc; 283d66195c1SJeremy Kerr 284d66195c1SJeremy Kerr val = config_get_value(config, "lpc-address"); 285d66195c1SJeremy Kerr if (val) { 286d66195c1SJeremy Kerr console->tty_lpc_addr = strtoul(val, &endp, 0); 287d66195c1SJeremy Kerr if (endp == optarg) { 288d66195c1SJeremy Kerr warn("Invalid LPC address: '%s'", val); 289d66195c1SJeremy Kerr return -1; 290d66195c1SJeremy Kerr } 291d66195c1SJeremy Kerr } 292d66195c1SJeremy Kerr 293d66195c1SJeremy Kerr val = config_get_value(config, "sirq"); 294d66195c1SJeremy Kerr if (val) { 295d66195c1SJeremy Kerr console->tty_sirq = strtoul(val, &endp, 0); 296d66195c1SJeremy Kerr if (endp == optarg) 297d66195c1SJeremy Kerr warn("Invalid sirq: '%s'", val); 298d66195c1SJeremy Kerr } 299d66195c1SJeremy Kerr 300c7fbcd48SBenjamin Fair val = config_get_value(config, "baud"); 301c7fbcd48SBenjamin Fair if (val) { 302c7fbcd48SBenjamin Fair if (config_parse_baud(&console->tty_baud, val)) 303c7fbcd48SBenjamin Fair warnx("Invalid baud rate: '%s'", val); 304c7fbcd48SBenjamin Fair } 305c7fbcd48SBenjamin Fair 306d66195c1SJeremy Kerr if (!console->tty_kname) { 307d66195c1SJeremy Kerr warnx("Error: No TTY device specified"); 308d66195c1SJeremy Kerr return -1; 309d66195c1SJeremy Kerr } 310d66195c1SJeremy Kerr 311d66195c1SJeremy Kerr rc = tty_find_device(console); 312d66195c1SJeremy Kerr if (rc) 313d66195c1SJeremy Kerr return rc; 314d66195c1SJeremy Kerr 315d66195c1SJeremy Kerr rc = tty_init_io(console); 316d66195c1SJeremy Kerr return rc; 317d66195c1SJeremy Kerr } 318d66195c1SJeremy Kerr 3191a0e03b4SJeremy Kerr 3201a0e03b4SJeremy Kerr int console_data_out(struct console *console, const uint8_t *data, size_t len) 321d831f960SJeremy Kerr { 3221a0e03b4SJeremy Kerr return write_buf_to_fd(console->tty_fd, data, len); 323d831f960SJeremy Kerr } 324d831f960SJeremy Kerr 325*f9c8f6caSCheng C Yang static int method_set_baud_rate(sd_bus_message *msg, void *userdata, 326*f9c8f6caSCheng C Yang sd_bus_error *err) 327*f9c8f6caSCheng C Yang { 328*f9c8f6caSCheng C Yang struct console *console = userdata; 329*f9c8f6caSCheng C Yang uint32_t baudrate; 330*f9c8f6caSCheng C Yang speed_t speed; 331*f9c8f6caSCheng C Yang int r; 332*f9c8f6caSCheng C Yang 333*f9c8f6caSCheng C Yang if (!console) { 334*f9c8f6caSCheng C Yang sd_bus_error_set_const(err, DBUS_ERR, "Internal error"); 335*f9c8f6caSCheng C Yang return sd_bus_reply_method_return(msg, "x", 0); 336*f9c8f6caSCheng C Yang } 337*f9c8f6caSCheng C Yang 338*f9c8f6caSCheng C Yang r = sd_bus_message_read(msg, "u", &baudrate); 339*f9c8f6caSCheng C Yang if (r < 0) { 340*f9c8f6caSCheng C Yang sd_bus_error_set_const(err, DBUS_ERR, "Bad message"); 341*f9c8f6caSCheng C Yang return sd_bus_reply_method_return(msg, "x", -EINVAL); 342*f9c8f6caSCheng C Yang } 343*f9c8f6caSCheng C Yang 344*f9c8f6caSCheng C Yang speed = parse_int_to_baud(baudrate); 345*f9c8f6caSCheng C Yang if (!speed) { 346*f9c8f6caSCheng C Yang warnx("Invalid baud rate: '%u'", baudrate); 347*f9c8f6caSCheng C Yang return sd_bus_reply_method_return(msg, "x", -EINVAL); 348*f9c8f6caSCheng C Yang } 349*f9c8f6caSCheng C Yang 350*f9c8f6caSCheng C Yang console->tty_baud = speed; 351*f9c8f6caSCheng C Yang tty_change_baudrate(console); 352*f9c8f6caSCheng C Yang 353*f9c8f6caSCheng C Yang return sd_bus_reply_method_return(msg, "x", r); 354*f9c8f6caSCheng C Yang } 355*f9c8f6caSCheng C Yang 356*f9c8f6caSCheng C Yang static int get_handler(sd_bus *bus, const char *path, const char *interface, 357*f9c8f6caSCheng C Yang const char *property, sd_bus_message *reply, void *userdata, 358*f9c8f6caSCheng C Yang sd_bus_error *error) { 359*f9c8f6caSCheng C Yang struct console *console = userdata; 360*f9c8f6caSCheng C Yang uint32_t baudrate; 361*f9c8f6caSCheng C Yang int r; 362*f9c8f6caSCheng C Yang 363*f9c8f6caSCheng C Yang baudrate = parse_baud_to_int(console->tty_baud); 364*f9c8f6caSCheng C Yang if (!baudrate) 365*f9c8f6caSCheng C Yang warnx("Invalid baud rate: '%d'", console->tty_baud); 366*f9c8f6caSCheng C Yang 367*f9c8f6caSCheng C Yang r = sd_bus_message_append(reply, "u", baudrate); 368*f9c8f6caSCheng C Yang 369*f9c8f6caSCheng C Yang return r; 370*f9c8f6caSCheng C Yang } 371*f9c8f6caSCheng C Yang 372*f9c8f6caSCheng C Yang static const sd_bus_vtable console_vtable[] = { 373*f9c8f6caSCheng C Yang SD_BUS_VTABLE_START(0), 374*f9c8f6caSCheng C Yang SD_BUS_METHOD("setBaudRate", "u", "x", method_set_baud_rate, 375*f9c8f6caSCheng C Yang SD_BUS_VTABLE_UNPRIVILEGED), 376*f9c8f6caSCheng C Yang SD_BUS_PROPERTY("baudrate", "u", get_handler, 0, 0), 377*f9c8f6caSCheng C Yang SD_BUS_VTABLE_END,}; 378*f9c8f6caSCheng C Yang 379*f9c8f6caSCheng C Yang static void dbus_init(struct console *console, struct config *config) 380*f9c8f6caSCheng C Yang { 381*f9c8f6caSCheng C Yang int dbus_poller = 0; 382*f9c8f6caSCheng C Yang int fd, r; 383*f9c8f6caSCheng C Yang 384*f9c8f6caSCheng C Yang if (!console) { 385*f9c8f6caSCheng C Yang warnx("Couldn't get valid console"); 386*f9c8f6caSCheng C Yang return; 387*f9c8f6caSCheng C Yang } 388*f9c8f6caSCheng C Yang 389*f9c8f6caSCheng C Yang r = sd_bus_default_system(&console->bus); 390*f9c8f6caSCheng C Yang if (r < 0) { 391*f9c8f6caSCheng C Yang warnx("Failed to connect to system bus: %s", strerror(-r)); 392*f9c8f6caSCheng C Yang return; 393*f9c8f6caSCheng C Yang } 394*f9c8f6caSCheng C Yang 395*f9c8f6caSCheng C Yang r = sd_bus_add_object_vtable(console->bus, NULL, OBJ_NAME, DBUS_NAME, 396*f9c8f6caSCheng C Yang console_vtable, console); 397*f9c8f6caSCheng C Yang if (r < 0) { 398*f9c8f6caSCheng C Yang warnx("Failed to issue method call: %s", strerror(-r)); 399*f9c8f6caSCheng C Yang return; 400*f9c8f6caSCheng C Yang } 401*f9c8f6caSCheng C Yang 402*f9c8f6caSCheng C Yang r = sd_bus_request_name(console->bus, DBUS_NAME, SD_BUS_NAME_ALLOW_REPLACEMENT 403*f9c8f6caSCheng C Yang |SD_BUS_NAME_REPLACE_EXISTING); 404*f9c8f6caSCheng C Yang if (r < 0) { 405*f9c8f6caSCheng C Yang warnx("Failed to acquire service name: %s", strerror(-r)); 406*f9c8f6caSCheng C Yang return; 407*f9c8f6caSCheng C Yang } 408*f9c8f6caSCheng C Yang 409*f9c8f6caSCheng C Yang fd = sd_bus_get_fd(console->bus); 410*f9c8f6caSCheng C Yang if (fd < 0) { 411*f9c8f6caSCheng C Yang warnx("Couldn't get the bus file descriptor"); 412*f9c8f6caSCheng C Yang return; 413*f9c8f6caSCheng C Yang } 414*f9c8f6caSCheng C Yang 415*f9c8f6caSCheng C Yang dbus_poller = POLLFD_DBUS; 416*f9c8f6caSCheng C Yang 417*f9c8f6caSCheng C Yang console->pollfds[dbus_poller].fd = fd; 418*f9c8f6caSCheng C Yang console->pollfds[dbus_poller].events = POLLIN; 419*f9c8f6caSCheng C Yang } 420*f9c8f6caSCheng C Yang 421d47963e5SJeremy Kerr static void handlers_init(struct console *console, struct config *config) 422d831f960SJeremy Kerr { 4231a0e03b4SJeremy Kerr extern struct handler *__start_handlers, *__stop_handlers; 4241a0e03b4SJeremy Kerr struct handler *handler; 425021b91f0SJeremy Kerr int i, rc; 426d831f960SJeremy Kerr 4271a0e03b4SJeremy Kerr console->n_handlers = &__stop_handlers - &__start_handlers; 4281a0e03b4SJeremy Kerr console->handlers = &__start_handlers; 429d831f960SJeremy Kerr 4301a0e03b4SJeremy Kerr printf("%d handler%s\n", console->n_handlers, 4311a0e03b4SJeremy Kerr console->n_handlers == 1 ? "" : "s"); 432d831f960SJeremy Kerr 4331a0e03b4SJeremy Kerr for (i = 0; i < console->n_handlers; i++) { 4341a0e03b4SJeremy Kerr handler = console->handlers[i]; 4351a0e03b4SJeremy Kerr 436021b91f0SJeremy Kerr rc = 0; 4371a0e03b4SJeremy Kerr if (handler->init) 438021b91f0SJeremy Kerr rc = handler->init(handler, console, config); 439021b91f0SJeremy Kerr 440021b91f0SJeremy Kerr handler->active = rc == 0; 441021b91f0SJeremy Kerr 442021b91f0SJeremy Kerr printf(" %s [%sactive]\n", handler->name, 443021b91f0SJeremy Kerr handler->active ? "" : "in"); 444d831f960SJeremy Kerr } 445d831f960SJeremy Kerr } 446d831f960SJeremy Kerr 4471a0e03b4SJeremy Kerr static void handlers_fini(struct console *console) 448d831f960SJeremy Kerr { 4491a0e03b4SJeremy Kerr struct handler *handler; 4501a0e03b4SJeremy Kerr int i; 4511a0e03b4SJeremy Kerr 4521a0e03b4SJeremy Kerr for (i = 0; i < console->n_handlers; i++) { 4531a0e03b4SJeremy Kerr handler = console->handlers[i]; 454021b91f0SJeremy Kerr if (handler->fini && handler->active) 4551a0e03b4SJeremy Kerr handler->fini(handler); 4561a0e03b4SJeremy Kerr } 457d831f960SJeremy Kerr } 458d831f960SJeremy Kerr 459f733c85aSJeremy Kerr struct ringbuffer_consumer *console_ringbuffer_consumer_register( 460f733c85aSJeremy Kerr struct console *console, 461f733c85aSJeremy Kerr ringbuffer_poll_fn_t poll_fn, void *data) 462d831f960SJeremy Kerr { 463f733c85aSJeremy Kerr return ringbuffer_consumer_register(console->rb, poll_fn, data); 464d831f960SJeremy Kerr } 465d831f960SJeremy Kerr 46655c9712dSJeremy Kerr struct poller *console_poller_register(struct console *console, 467329a35f5SJeremy Kerr struct handler *handler, poller_fn_t poller_fn, 468329a35f5SJeremy Kerr int fd, int events, void *data) 469d831f960SJeremy Kerr { 470329a35f5SJeremy Kerr struct poller *poller; 471329a35f5SJeremy Kerr int n; 472329a35f5SJeremy Kerr 473329a35f5SJeremy Kerr poller = malloc(sizeof(*poller)); 474329a35f5SJeremy Kerr poller->remove = false; 475329a35f5SJeremy Kerr poller->handler = handler; 476329a35f5SJeremy Kerr poller->fn = poller_fn; 477329a35f5SJeremy Kerr poller->data = data; 478329a35f5SJeremy Kerr 479329a35f5SJeremy Kerr /* add one to our pollers array */ 480329a35f5SJeremy Kerr n = console->n_pollers++; 481329a35f5SJeremy Kerr console->pollers = realloc(console->pollers, 482329a35f5SJeremy Kerr sizeof(*console->pollers) * console->n_pollers); 483329a35f5SJeremy Kerr 484329a35f5SJeremy Kerr console->pollers[n] = poller; 485329a35f5SJeremy Kerr 486329a35f5SJeremy Kerr /* increase pollfds array too */ 487329a35f5SJeremy Kerr console->pollfds = realloc(console->pollfds, 488329a35f5SJeremy Kerr sizeof(*console->pollfds) * 489*f9c8f6caSCheng C Yang (MAX_INTERNAL_POLLFD + console->n_pollers)); 490329a35f5SJeremy Kerr 491329a35f5SJeremy Kerr /* shift the end pollfds up by one */ 492*f9c8f6caSCheng C Yang memcpy(&console->pollfds[n+1], 493329a35f5SJeremy Kerr &console->pollfds[n], 494*f9c8f6caSCheng C Yang sizeof(*console->pollfds) * MAX_INTERNAL_POLLFD); 495329a35f5SJeremy Kerr 496329a35f5SJeremy Kerr console->pollfds[n].fd = fd; 497329a35f5SJeremy Kerr console->pollfds[n].events = events; 498329a35f5SJeremy Kerr 499329a35f5SJeremy Kerr return poller; 500329a35f5SJeremy Kerr } 501329a35f5SJeremy Kerr 50255c9712dSJeremy Kerr void console_poller_unregister(struct console *console, 503329a35f5SJeremy Kerr struct poller *poller) 504329a35f5SJeremy Kerr { 505329a35f5SJeremy Kerr int i; 506329a35f5SJeremy Kerr 507329a35f5SJeremy Kerr /* find the entry in our pollers array */ 508329a35f5SJeremy Kerr for (i = 0; i < console->n_pollers; i++) 509329a35f5SJeremy Kerr if (console->pollers[i] == poller) 510329a35f5SJeremy Kerr break; 511329a35f5SJeremy Kerr 512329a35f5SJeremy Kerr assert(i < console->n_pollers); 513329a35f5SJeremy Kerr 514329a35f5SJeremy Kerr console->n_pollers--; 515329a35f5SJeremy Kerr 516329a35f5SJeremy Kerr /* remove the item from the pollers array... */ 517329a35f5SJeremy Kerr memmove(&console->pollers[i], &console->pollers[i+1], 518329a35f5SJeremy Kerr sizeof(*console->pollers) 519329a35f5SJeremy Kerr * (console->n_pollers - i)); 520329a35f5SJeremy Kerr 521329a35f5SJeremy Kerr console->pollers = realloc(console->pollers, 522329a35f5SJeremy Kerr sizeof(*console->pollers) * console->n_pollers); 523329a35f5SJeremy Kerr 524329a35f5SJeremy Kerr /* ... and the pollfds array */ 525329a35f5SJeremy Kerr memmove(&console->pollfds[i], &console->pollfds[i+1], 526329a35f5SJeremy Kerr sizeof(*console->pollfds) * 527*f9c8f6caSCheng C Yang (MAX_INTERNAL_POLLFD + console->n_pollers - i)); 528329a35f5SJeremy Kerr 529329a35f5SJeremy Kerr console->pollfds = realloc(console->pollfds, 530329a35f5SJeremy Kerr sizeof(*console->pollfds) * 531*f9c8f6caSCheng C Yang (MAX_INTERNAL_POLLFD + console->n_pollers)); 532329a35f5SJeremy Kerr 533329a35f5SJeremy Kerr 534329a35f5SJeremy Kerr free(poller); 535329a35f5SJeremy Kerr } 536329a35f5SJeremy Kerr 5376b1fed27SJeremy Kerr void console_poller_set_events(struct console *console, struct poller *poller, 5386b1fed27SJeremy Kerr int events) 5396b1fed27SJeremy Kerr { 5406b1fed27SJeremy Kerr int i; 5416b1fed27SJeremy Kerr 5426b1fed27SJeremy Kerr /* find the entry in our pollers array */ 5436b1fed27SJeremy Kerr for (i = 0; i < console->n_pollers; i++) 5446b1fed27SJeremy Kerr if (console->pollers[i] == poller) 5456b1fed27SJeremy Kerr break; 5466b1fed27SJeremy Kerr 5476b1fed27SJeremy Kerr console->pollfds[i].events = events; 5486b1fed27SJeremy Kerr } 5496b1fed27SJeremy Kerr 550329a35f5SJeremy Kerr static int call_pollers(struct console *console) 551329a35f5SJeremy Kerr { 552329a35f5SJeremy Kerr struct poller *poller; 553329a35f5SJeremy Kerr struct pollfd *pollfd; 554329a35f5SJeremy Kerr enum poller_ret prc; 555329a35f5SJeremy Kerr int i, rc; 556d831f960SJeremy Kerr 5571a0e03b4SJeremy Kerr rc = 0; 5581a0e03b4SJeremy Kerr 559329a35f5SJeremy Kerr /* 560329a35f5SJeremy Kerr * Process poll events by iterating through the pollers and pollfds 561329a35f5SJeremy Kerr * in-step, calling any pollers that we've found revents for. 562329a35f5SJeremy Kerr */ 563329a35f5SJeremy Kerr for (i = 0; i < console->n_pollers; i++) { 564329a35f5SJeremy Kerr poller = console->pollers[i]; 565329a35f5SJeremy Kerr pollfd = &console->pollfds[i]; 5661a0e03b4SJeremy Kerr 567329a35f5SJeremy Kerr if (!pollfd->revents) 5681a0e03b4SJeremy Kerr continue; 5691a0e03b4SJeremy Kerr 570329a35f5SJeremy Kerr prc = poller->fn(poller->handler, pollfd->revents, 571329a35f5SJeremy Kerr poller->data); 572329a35f5SJeremy Kerr if (prc == POLLER_EXIT) 573329a35f5SJeremy Kerr rc = -1; 574329a35f5SJeremy Kerr else if (prc == POLLER_REMOVE) 575329a35f5SJeremy Kerr poller->remove = true; 576329a35f5SJeremy Kerr } 577329a35f5SJeremy Kerr 578329a35f5SJeremy Kerr /** 579329a35f5SJeremy Kerr * Process deferred removals; restarting each time we unregister, as 580329a35f5SJeremy Kerr * the array will have changed 581329a35f5SJeremy Kerr */ 582329a35f5SJeremy Kerr for (;;) { 583329a35f5SJeremy Kerr bool removed = false; 584329a35f5SJeremy Kerr 585329a35f5SJeremy Kerr for (i = 0; i < console->n_pollers; i++) { 586329a35f5SJeremy Kerr poller = console->pollers[i]; 587329a35f5SJeremy Kerr if (poller->remove) { 58855c9712dSJeremy Kerr console_poller_unregister(console, poller); 589329a35f5SJeremy Kerr removed = true; 590329a35f5SJeremy Kerr break; 591329a35f5SJeremy Kerr } 592329a35f5SJeremy Kerr } 593329a35f5SJeremy Kerr if (!removed) 594329a35f5SJeremy Kerr break; 5951a0e03b4SJeremy Kerr } 5961a0e03b4SJeremy Kerr 5971a0e03b4SJeremy Kerr return rc; 5981a0e03b4SJeremy Kerr } 5991a0e03b4SJeremy Kerr 600769cee1aSJeremy Kerr static void sighandler(int signal) 601769cee1aSJeremy Kerr { 602769cee1aSJeremy Kerr if (signal == SIGINT) 603769cee1aSJeremy Kerr sigint = true; 604769cee1aSJeremy Kerr } 605769cee1aSJeremy Kerr 6061a0e03b4SJeremy Kerr int run_console(struct console *console) 6071a0e03b4SJeremy Kerr { 608769cee1aSJeremy Kerr sighandler_t sighandler_save; 609329a35f5SJeremy Kerr int rc; 610d831f960SJeremy Kerr 611769cee1aSJeremy Kerr sighandler_save = signal(SIGINT, sighandler); 612769cee1aSJeremy Kerr 613769cee1aSJeremy Kerr rc = 0; 614769cee1aSJeremy Kerr 615d831f960SJeremy Kerr for (;;) { 616d831f960SJeremy Kerr uint8_t buf[4096]; 617d831f960SJeremy Kerr 6181764145dSJeremy Kerr BUILD_ASSERT(sizeof(buf) <= buffer_size); 6191764145dSJeremy Kerr 620769cee1aSJeremy Kerr if (sigint) { 621769cee1aSJeremy Kerr fprintf(stderr, "Received interrupt, exiting\n"); 622769cee1aSJeremy Kerr break; 623769cee1aSJeremy Kerr } 624769cee1aSJeremy Kerr 625329a35f5SJeremy Kerr rc = poll(console->pollfds, 626*f9c8f6caSCheng C Yang console->n_pollers + MAX_INTERNAL_POLLFD, -1); 627d831f960SJeremy Kerr if (rc < 0) { 628769cee1aSJeremy Kerr if (errno == EINTR) { 629769cee1aSJeremy Kerr continue; 630769cee1aSJeremy Kerr } else { 631d831f960SJeremy Kerr warn("poll error"); 632769cee1aSJeremy Kerr break; 633769cee1aSJeremy Kerr } 634d831f960SJeremy Kerr } 635d831f960SJeremy Kerr 636329a35f5SJeremy Kerr /* process internal fd first */ 637329a35f5SJeremy Kerr if (console->pollfds[console->n_pollers].revents) { 6381a0e03b4SJeremy Kerr rc = read(console->tty_fd, buf, sizeof(buf)); 639d831f960SJeremy Kerr if (rc <= 0) { 640d831f960SJeremy Kerr warn("Error reading from tty device"); 641769cee1aSJeremy Kerr rc = -1; 642769cee1aSJeremy Kerr break; 643d831f960SJeremy Kerr } 644f733c85aSJeremy Kerr rc = ringbuffer_queue(console->rb, buf, rc); 6451a0e03b4SJeremy Kerr if (rc) 646769cee1aSJeremy Kerr break; 647d831f960SJeremy Kerr } 648d831f960SJeremy Kerr 649*f9c8f6caSCheng C Yang if (console->pollfds[console->n_pollers + 1].revents) { 650*f9c8f6caSCheng C Yang sd_bus_process(console->bus, NULL); 651*f9c8f6caSCheng C Yang } 652*f9c8f6caSCheng C Yang 653329a35f5SJeremy Kerr /* ... and then the pollers */ 654329a35f5SJeremy Kerr rc = call_pollers(console); 6551a0e03b4SJeremy Kerr if (rc) 656769cee1aSJeremy Kerr break; 6571a0e03b4SJeremy Kerr } 658769cee1aSJeremy Kerr 659769cee1aSJeremy Kerr signal(SIGINT, sighandler_save); 660*f9c8f6caSCheng C Yang sd_bus_unref(console->bus); 661769cee1aSJeremy Kerr 662769cee1aSJeremy Kerr return rc ? -1 : 0; 6631a0e03b4SJeremy Kerr } 664d831f960SJeremy Kerr static const struct option options[] = { 665d66195c1SJeremy Kerr { "config", required_argument, 0, 'c'}, 666f5858b5bSJoel Stanley { 0, 0, 0, 0}, 667d831f960SJeremy Kerr }; 668d831f960SJeremy Kerr 669d831f960SJeremy Kerr int main(int argc, char **argv) 670d831f960SJeremy Kerr { 671d66195c1SJeremy Kerr const char *config_filename = NULL; 6726221ce94SVishwanatha Subbanna const char *config_tty_kname = NULL; 6731a0e03b4SJeremy Kerr struct console *console; 674d66195c1SJeremy Kerr struct config *config; 675d66195c1SJeremy Kerr int rc; 676d831f960SJeremy Kerr 677957818b4SJeremy Kerr rc = -1; 678d831f960SJeremy Kerr 679d831f960SJeremy Kerr for (;;) { 680d831f960SJeremy Kerr int c, idx; 681d831f960SJeremy Kerr 682d66195c1SJeremy Kerr c = getopt_long(argc, argv, "c:", options, &idx); 683d831f960SJeremy Kerr if (c == -1) 684d831f960SJeremy Kerr break; 685d831f960SJeremy Kerr 686d831f960SJeremy Kerr switch (c) { 687d66195c1SJeremy Kerr case 'c': 688d66195c1SJeremy Kerr config_filename = optarg; 689d831f960SJeremy Kerr break; 690d831f960SJeremy Kerr case 'h': 691d831f960SJeremy Kerr case '?': 692d831f960SJeremy Kerr usage(argv[0]); 693d66195c1SJeremy Kerr return EXIT_SUCCESS; 694d831f960SJeremy Kerr } 695d831f960SJeremy Kerr } 696d831f960SJeremy Kerr 6976221ce94SVishwanatha Subbanna if (optind >= argc) { 6986221ce94SVishwanatha Subbanna warnx("Required argument <DEVICE> missing"); 6996221ce94SVishwanatha Subbanna usage(argv[0]); 7006221ce94SVishwanatha Subbanna return EXIT_FAILURE; 7016221ce94SVishwanatha Subbanna } 7026221ce94SVishwanatha Subbanna 7036221ce94SVishwanatha Subbanna config_tty_kname = argv[optind]; 7046221ce94SVishwanatha Subbanna 705d66195c1SJeremy Kerr console = malloc(sizeof(struct console)); 706d66195c1SJeremy Kerr memset(console, 0, sizeof(*console)); 707*f9c8f6caSCheng C Yang console->pollfds = calloc(MAX_INTERNAL_POLLFD, 708329a35f5SJeremy Kerr sizeof(*console->pollfds)); 709f733c85aSJeremy Kerr console->rb = ringbuffer_init(buffer_size); 710329a35f5SJeremy Kerr 711d66195c1SJeremy Kerr config = config_init(config_filename); 712d66195c1SJeremy Kerr if (!config) { 713d66195c1SJeremy Kerr warnx("Can't read configuration, exiting."); 714d66195c1SJeremy Kerr goto out_free; 715d831f960SJeremy Kerr } 716d831f960SJeremy Kerr 7176221ce94SVishwanatha Subbanna console->tty_kname = config_tty_kname; 7186221ce94SVishwanatha Subbanna 719d66195c1SJeremy Kerr rc = tty_init(console, config); 72017217845SJeremy Kerr if (rc) 721d66195c1SJeremy Kerr goto out_config_fini; 722d831f960SJeremy Kerr 723*f9c8f6caSCheng C Yang dbus_init(console, config); 724*f9c8f6caSCheng C Yang 725d47963e5SJeremy Kerr handlers_init(console, config); 726d831f960SJeremy Kerr 7271a0e03b4SJeremy Kerr rc = run_console(console); 728d831f960SJeremy Kerr 7291a0e03b4SJeremy Kerr handlers_fini(console); 730d831f960SJeremy Kerr 731d66195c1SJeremy Kerr out_config_fini: 732d66195c1SJeremy Kerr config_fini(config); 733d66195c1SJeremy Kerr 734957818b4SJeremy Kerr out_free: 73589ea8198SJeremy Kerr free(console->pollers); 73689ea8198SJeremy Kerr free(console->pollfds); 7371a0e03b4SJeremy Kerr free(console->tty_sysfs_devnode); 7381a0e03b4SJeremy Kerr free(console->tty_dev); 7391a0e03b4SJeremy Kerr free(console); 740d831f960SJeremy Kerr 741d831f960SJeremy Kerr return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE; 742d831f960SJeremy Kerr } 743