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