1d831f960SJeremy Kerr /** 2d831f960SJeremy Kerr * Console server process for OpenBMC 3d831f960SJeremy Kerr * 4d831f960SJeremy Kerr * Copyright © 2016 IBM Corporation <jk@ozlabs.org> 5d831f960SJeremy Kerr */ 6d831f960SJeremy Kerr 7*17217845SJeremy Kerr #define _GNU_SOURCE 8*17217845SJeremy Kerr 9d831f960SJeremy Kerr #include <stdint.h> 10d831f960SJeremy Kerr #include <stdbool.h> 11d831f960SJeremy Kerr #include <stdlib.h> 12d831f960SJeremy Kerr #include <stdio.h> 13d831f960SJeremy Kerr #include <fcntl.h> 14d831f960SJeremy Kerr #include <unistd.h> 15d831f960SJeremy Kerr #include <err.h> 16d831f960SJeremy Kerr #include <termios.h> 17d831f960SJeremy Kerr #include <string.h> 18d831f960SJeremy Kerr #include <getopt.h> 19*17217845SJeremy Kerr #include <limits.h> 20d831f960SJeremy Kerr 21d831f960SJeremy Kerr #include <sys/types.h> 22d831f960SJeremy Kerr #include <sys/poll.h> 23d831f960SJeremy Kerr 24d831f960SJeremy Kerr #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) 25d831f960SJeremy Kerr 26d831f960SJeremy Kerr static const char esc_str[] = { '\r', '~', '.' }; 27d831f960SJeremy Kerr 28d831f960SJeremy Kerr struct console_ctx { 29*17217845SJeremy Kerr const char *tty_kname; 30*17217845SJeremy Kerr char *tty_sysfs_devnode; 31*17217845SJeremy Kerr char *tty_dev; 32d831f960SJeremy Kerr int tty_fd; 33d831f960SJeremy Kerr int console_fd_in; 34d831f960SJeremy Kerr int console_fd_out; 35d831f960SJeremy Kerr bool console_is_tty; 36d831f960SJeremy Kerr struct termios orig_termios; 37d831f960SJeremy Kerr int esc_str_pos; 38d831f960SJeremy Kerr }; 39d831f960SJeremy Kerr 40d831f960SJeremy Kerr static void usage(const char *progname) 41d831f960SJeremy Kerr { 42d831f960SJeremy Kerr fprintf(stderr, 43d831f960SJeremy Kerr "usage: %s [options]\n" 44d831f960SJeremy Kerr "\n" 45d831f960SJeremy Kerr "Options:\n" 46*17217845SJeremy Kerr " --device <TTY> Use serial device TTY (eg, ttyS0)\n" 47d831f960SJeremy Kerr "", 48d831f960SJeremy Kerr progname); 49d831f960SJeremy Kerr } 50d831f960SJeremy Kerr 51*17217845SJeremy Kerr /* populates tty_dev and tty_sysfs_devnode, using the tty kernel name */ 52*17217845SJeremy Kerr static int tty_find_device(struct console_ctx *ctx) 53*17217845SJeremy Kerr { 54*17217845SJeremy Kerr char *tty_class_device_link; 55*17217845SJeremy Kerr char *tty_device_tty_dir; 56*17217845SJeremy Kerr char *tty_device_reldir; 57*17217845SJeremy Kerr int rc; 58*17217845SJeremy Kerr 59*17217845SJeremy Kerr rc = -1; 60*17217845SJeremy Kerr tty_class_device_link = NULL; 61*17217845SJeremy Kerr tty_device_tty_dir = NULL; 62*17217845SJeremy Kerr tty_device_reldir = NULL; 63*17217845SJeremy Kerr 64*17217845SJeremy Kerr rc = asprintf(&tty_class_device_link, 65*17217845SJeremy Kerr "/sys/class/tty/%s", ctx->tty_kname); 66*17217845SJeremy Kerr if (rc < 0) 67*17217845SJeremy Kerr return -1; 68*17217845SJeremy Kerr 69*17217845SJeremy Kerr tty_device_tty_dir = realpath(tty_class_device_link, NULL); 70*17217845SJeremy Kerr if (rc < 0) { 71*17217845SJeremy Kerr warn("Can't query sysfs for device %s", ctx->tty_kname); 72*17217845SJeremy Kerr goto out_free; 73*17217845SJeremy Kerr } 74*17217845SJeremy Kerr 75*17217845SJeremy Kerr rc = asprintf(&tty_device_reldir, "%s/../../", tty_device_tty_dir); 76*17217845SJeremy Kerr if (rc < 0) 77*17217845SJeremy Kerr goto out_free; 78*17217845SJeremy Kerr 79*17217845SJeremy Kerr ctx->tty_sysfs_devnode = realpath(tty_device_reldir, NULL); 80*17217845SJeremy Kerr if (!ctx->tty_sysfs_devnode) 81*17217845SJeremy Kerr warn("Can't find parent device for %s", ctx->tty_kname); 82*17217845SJeremy Kerr 83*17217845SJeremy Kerr 84*17217845SJeremy Kerr /* todo: lookup from major/minor info in sysfs, in case udev has 85*17217845SJeremy Kerr * renamed us */ 86*17217845SJeremy Kerr rc = asprintf(&ctx->tty_dev, "/dev/%s", ctx->tty_kname); 87*17217845SJeremy Kerr if (rc < 0) 88*17217845SJeremy Kerr goto out_free; 89*17217845SJeremy Kerr 90*17217845SJeremy Kerr rc = 0; 91*17217845SJeremy Kerr 92*17217845SJeremy Kerr out_free: 93*17217845SJeremy Kerr free(tty_class_device_link); 94*17217845SJeremy Kerr free(tty_device_tty_dir); 95*17217845SJeremy Kerr free(tty_device_reldir); 96*17217845SJeremy Kerr return rc; 97*17217845SJeremy Kerr } 98*17217845SJeremy Kerr 99d831f960SJeremy Kerr /** 100d831f960SJeremy Kerr * Open and initialise the serial device 101d831f960SJeremy Kerr */ 102d831f960SJeremy Kerr static int tty_init_io(struct console_ctx *ctx) 103d831f960SJeremy Kerr { 104*17217845SJeremy Kerr 105d831f960SJeremy Kerr ctx->tty_fd = open(ctx->tty_dev, O_RDWR); 106d831f960SJeremy Kerr if (ctx->tty_fd <= 0) { 107d831f960SJeremy Kerr warn("Can't open tty %s", ctx->tty_dev); 108d831f960SJeremy Kerr return -1; 109d831f960SJeremy Kerr } 110d831f960SJeremy Kerr 111d831f960SJeremy Kerr /* Disable character delay. We may want to later enable this when 112d831f960SJeremy Kerr * we detect larger amounts of data 113d831f960SJeremy Kerr */ 114d831f960SJeremy Kerr fcntl(ctx->tty_fd, F_SETFL, FNDELAY); 115d831f960SJeremy Kerr 116d831f960SJeremy Kerr return 0; 117d831f960SJeremy Kerr } 118d831f960SJeremy Kerr 119d831f960SJeremy Kerr /* 120d831f960SJeremy Kerr * Setup our console channel for IO: use stdin/stdout, and if we're on a TTY, 121d831f960SJeremy Kerr * put it in canonical mode 122d831f960SJeremy Kerr */ 123d831f960SJeremy Kerr static int console_init_io(struct console_ctx *ctx) 124d831f960SJeremy Kerr { 125d831f960SJeremy Kerr struct termios termios; 126d831f960SJeremy Kerr int rc; 127d831f960SJeremy Kerr 128d831f960SJeremy Kerr ctx->console_fd_in = STDIN_FILENO; 129d831f960SJeremy Kerr ctx->console_fd_out = STDOUT_FILENO; 130d831f960SJeremy Kerr ctx->console_is_tty = isatty(ctx->console_fd_in); 131d831f960SJeremy Kerr 132d831f960SJeremy Kerr if (!ctx->console_is_tty) 133d831f960SJeremy Kerr return 0; 134d831f960SJeremy Kerr 135d831f960SJeremy Kerr rc = tcgetattr(ctx->console_fd_in, &termios); 136d831f960SJeremy Kerr if (rc) { 137d831f960SJeremy Kerr warn("Can't get terminal attributes for console"); 138d831f960SJeremy Kerr return -1; 139d831f960SJeremy Kerr } 140d831f960SJeremy Kerr memcpy(&ctx->orig_termios, &termios, sizeof(ctx->orig_termios)); 141d831f960SJeremy Kerr cfmakeraw(&termios); 142d831f960SJeremy Kerr 143d831f960SJeremy Kerr rc = tcsetattr(ctx->console_fd_in, TCSANOW, &termios); 144d831f960SJeremy Kerr if (rc) { 145d831f960SJeremy Kerr warn("Can't set terminal attributes for console"); 146d831f960SJeremy Kerr return -1; 147d831f960SJeremy Kerr } 148d831f960SJeremy Kerr 149d831f960SJeremy Kerr return 0; 150d831f960SJeremy Kerr } 151d831f960SJeremy Kerr 152d831f960SJeremy Kerr static int console_process_input(struct console_ctx *ctx, 153d831f960SJeremy Kerr uint8_t *buf, size_t len) 154d831f960SJeremy Kerr { 155d831f960SJeremy Kerr unsigned long i; 156d831f960SJeremy Kerr uint8_t e; 157d831f960SJeremy Kerr 158d831f960SJeremy Kerr e = esc_str[ctx->esc_str_pos]; 159d831f960SJeremy Kerr 160d831f960SJeremy Kerr for (i = 0; i < len; i++) { 161d831f960SJeremy Kerr if (buf[i] == e) { 162d831f960SJeremy Kerr ctx->esc_str_pos++; 163d831f960SJeremy Kerr if (ctx->esc_str_pos == ARRAY_SIZE(esc_str)) 164d831f960SJeremy Kerr return 1; 165d831f960SJeremy Kerr e = esc_str[ctx->esc_str_pos]; 166d831f960SJeremy Kerr } else { 167d831f960SJeremy Kerr 168d831f960SJeremy Kerr ctx->esc_str_pos = 0; 169d831f960SJeremy Kerr } 170d831f960SJeremy Kerr } 171d831f960SJeremy Kerr return 0; 172d831f960SJeremy Kerr } 173d831f960SJeremy Kerr 174d831f960SJeremy Kerr static void console_restore_termios(struct console_ctx *ctx) 175d831f960SJeremy Kerr { 176d831f960SJeremy Kerr if (ctx->console_is_tty) 177d831f960SJeremy Kerr tcsetattr(ctx->console_fd_in, TCSANOW, &ctx->orig_termios); 178d831f960SJeremy Kerr } 179d831f960SJeremy Kerr 180d831f960SJeremy Kerr static int write_buf_to_fd(int fd, uint8_t *buf, size_t len) 181d831f960SJeremy Kerr { 182d831f960SJeremy Kerr size_t pos; 183d831f960SJeremy Kerr ssize_t rc; 184d831f960SJeremy Kerr 185d831f960SJeremy Kerr for (pos = 0; pos < len; pos += rc) { 186d831f960SJeremy Kerr rc = write(fd, buf + pos, len - pos); 187d831f960SJeremy Kerr if (rc <= 0) { 188d831f960SJeremy Kerr warn("Write error"); 189d831f960SJeremy Kerr return -1; 190d831f960SJeremy Kerr } 191d831f960SJeremy Kerr } 192d831f960SJeremy Kerr 193d831f960SJeremy Kerr return 0; 194d831f960SJeremy Kerr } 195d831f960SJeremy Kerr 196d831f960SJeremy Kerr int run_console(struct console_ctx *ctx) 197d831f960SJeremy Kerr { 198d831f960SJeremy Kerr struct pollfd pollfds[2]; 199d831f960SJeremy Kerr int rc, len; 200d831f960SJeremy Kerr 201d831f960SJeremy Kerr pollfds[0].fd = ctx->tty_fd; 202d831f960SJeremy Kerr pollfds[0].events = POLLIN; 203d831f960SJeremy Kerr pollfds[1].fd = ctx->console_fd_in; 204d831f960SJeremy Kerr pollfds[1].events = POLLIN; 205d831f960SJeremy Kerr 206d831f960SJeremy Kerr for (;;) { 207d831f960SJeremy Kerr uint8_t buf[4096]; 208d831f960SJeremy Kerr 209d831f960SJeremy Kerr rc = poll(pollfds, 2, -1); 210d831f960SJeremy Kerr if (rc < 0) { 211d831f960SJeremy Kerr warn("poll error"); 212d831f960SJeremy Kerr return -1; 213d831f960SJeremy Kerr } 214d831f960SJeremy Kerr 215d831f960SJeremy Kerr if (pollfds[0].revents) { 216d831f960SJeremy Kerr rc = read(ctx->tty_fd, buf, sizeof(buf)); 217d831f960SJeremy Kerr if (rc <= 0) { 218d831f960SJeremy Kerr warn("Error reading from tty device"); 219d831f960SJeremy Kerr return -1; 220d831f960SJeremy Kerr } 221d831f960SJeremy Kerr rc = write_buf_to_fd(ctx->console_fd_out, buf, rc); 222d831f960SJeremy Kerr if (rc < 0) 223d831f960SJeremy Kerr return -1; 224d831f960SJeremy Kerr } 225d831f960SJeremy Kerr if (pollfds[1].revents) { 226d831f960SJeremy Kerr rc = read(ctx->console_fd_in, buf, sizeof(buf)); 227d831f960SJeremy Kerr if (rc == 0) 228d831f960SJeremy Kerr return 0; 229d831f960SJeremy Kerr 230d831f960SJeremy Kerr if (rc <= 0) { 231d831f960SJeremy Kerr warn("Error reading from console"); 232d831f960SJeremy Kerr return -1; 233d831f960SJeremy Kerr } 234d831f960SJeremy Kerr len = rc; 235d831f960SJeremy Kerr rc = console_process_input(ctx, buf, len); 236d831f960SJeremy Kerr if (rc) { 237d831f960SJeremy Kerr rc = 0; 238d831f960SJeremy Kerr return 0; 239d831f960SJeremy Kerr } 240d831f960SJeremy Kerr rc = write_buf_to_fd(ctx->tty_fd, buf, len); 241d831f960SJeremy Kerr if (rc < 0) 242d831f960SJeremy Kerr return -1; 243d831f960SJeremy Kerr } 244d831f960SJeremy Kerr } 245d831f960SJeremy Kerr } 246d831f960SJeremy Kerr 247d831f960SJeremy Kerr static const struct option options[] = { 248d831f960SJeremy Kerr { "device", required_argument, 0, 'd'}, 249d831f960SJeremy Kerr { }, 250d831f960SJeremy Kerr }; 251d831f960SJeremy Kerr 252d831f960SJeremy Kerr int main(int argc, char **argv) 253d831f960SJeremy Kerr { 254d831f960SJeremy Kerr struct console_ctx *ctx; 255d831f960SJeremy Kerr int rc; 256d831f960SJeremy Kerr 257d831f960SJeremy Kerr ctx = malloc(sizeof(struct console_ctx)); 258d831f960SJeremy Kerr memset(ctx, 0, sizeof(*ctx)); 259d831f960SJeremy Kerr 260d831f960SJeremy Kerr for (;;) { 261d831f960SJeremy Kerr int c, idx; 262d831f960SJeremy Kerr 263d831f960SJeremy Kerr c = getopt_long(argc, argv, "d", options, &idx); 264d831f960SJeremy Kerr if (c == -1) 265d831f960SJeremy Kerr break; 266d831f960SJeremy Kerr 267d831f960SJeremy Kerr switch (c) { 268d831f960SJeremy Kerr case 'd': 269*17217845SJeremy Kerr ctx->tty_kname = optarg; 270d831f960SJeremy Kerr break; 271d831f960SJeremy Kerr 272d831f960SJeremy Kerr case 'h': 273d831f960SJeremy Kerr case '?': 274d831f960SJeremy Kerr usage(argv[0]); 275d831f960SJeremy Kerr break; 276d831f960SJeremy Kerr } 277d831f960SJeremy Kerr } 278d831f960SJeremy Kerr 279*17217845SJeremy Kerr if (!ctx->tty_kname) { 280d831f960SJeremy Kerr fprintf(stderr, 281d831f960SJeremy Kerr "Error: No TTY device specified (use --device)\n"); 282d831f960SJeremy Kerr return EXIT_FAILURE; 283d831f960SJeremy Kerr } 284d831f960SJeremy Kerr 285*17217845SJeremy Kerr rc = tty_find_device(ctx); 286*17217845SJeremy Kerr if (rc) 287*17217845SJeremy Kerr return EXIT_FAILURE; 288*17217845SJeremy Kerr 289*17217845SJeremy Kerr return EXIT_SUCCESS; 290*17217845SJeremy Kerr 291d831f960SJeremy Kerr rc = tty_init_io(ctx); 292d831f960SJeremy Kerr if (rc) 293d831f960SJeremy Kerr return EXIT_FAILURE; 294d831f960SJeremy Kerr 295d831f960SJeremy Kerr rc = console_init_io(ctx); 296d831f960SJeremy Kerr if (rc) 297d831f960SJeremy Kerr return EXIT_FAILURE; 298d831f960SJeremy Kerr 299d831f960SJeremy Kerr rc = run_console(ctx); 300d831f960SJeremy Kerr 301d831f960SJeremy Kerr console_restore_termios(ctx); 302d831f960SJeremy Kerr 303*17217845SJeremy Kerr free(ctx->tty_sysfs_devnode); 304*17217845SJeremy Kerr free(ctx->tty_dev); 305d831f960SJeremy Kerr free(ctx); 306d831f960SJeremy Kerr 307d831f960SJeremy Kerr return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE; 308d831f960SJeremy Kerr } 309