1 /** 2 * Console server process for OpenBMC 3 * 4 * Copyright © 2016 IBM Corporation 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 #include <assert.h> 20 #include <errno.h> 21 #include <signal.h> 22 #include <stdint.h> 23 #include <stdbool.h> 24 #include <stdlib.h> 25 #include <stdio.h> 26 #include <fcntl.h> 27 #include <unistd.h> 28 #include <err.h> 29 #include <string.h> 30 #include <getopt.h> 31 #include <glob.h> 32 #include <limits.h> 33 #include <time.h> 34 #include <termios.h> 35 36 #include <sys/types.h> 37 #include <sys/time.h> 38 #include <sys/socket.h> 39 #include <poll.h> 40 41 #include "console-server.h" 42 43 #define DEV_PTS_PATH "/dev/pts" 44 45 /* default size of the shared backlog ringbuffer */ 46 const size_t default_buffer_size = 128ul * 1024ul; 47 48 /* state shared with the signal handler */ 49 static bool sigint; 50 51 static void usage(const char *progname) 52 { 53 fprintf(stderr, 54 "usage: %s [options] <DEVICE>\n" 55 "\n" 56 "Options:\n" 57 " --config <FILE>\tUse FILE for configuration\n" 58 " --console-id <NAME>\tUse NAME in the UNIX domain socket address\n" 59 "", 60 progname); 61 } 62 63 /* populates console->tty.dev and console->tty.sysfs_devnode, using the tty kernel name */ 64 static int tty_find_device(struct console *console) 65 { 66 char *tty_class_device_link = NULL; 67 char *tty_path_input_real = NULL; 68 char *tty_device_tty_dir = NULL; 69 char *tty_vuart_lpc_addr = NULL; 70 char *tty_device_reldir = NULL; 71 char *tty_sysfs_devnode = NULL; 72 char *tty_kname_real = NULL; 73 char *tty_path_input = NULL; 74 int rc; 75 76 console->tty.type = TTY_DEVICE_UNDEFINED; 77 78 assert(console->tty.kname); 79 if (!strlen(console->tty.kname)) { 80 warnx("TTY kname must not be empty"); 81 rc = -1; 82 goto out_free; 83 } 84 85 if (console->tty.kname[0] == '/') { 86 tty_path_input = strdup(console->tty.kname); 87 if (!tty_path_input) { 88 rc = -1; 89 goto out_free; 90 } 91 } else { 92 rc = asprintf(&tty_path_input, "/dev/%s", console->tty.kname); 93 if (rc < 0) { 94 goto out_free; 95 } 96 } 97 98 /* udev may rename the tty name with a symbol link, try to resolve */ 99 tty_path_input_real = realpath(tty_path_input, NULL); 100 if (!tty_path_input_real) { 101 warn("Can't find realpath for %s", tty_path_input); 102 rc = -1; 103 goto out_free; 104 } 105 106 /* 107 * Allow hooking obmc-console-server up to PTYs for testing 108 * 109 * https://amboar.github.io/notes/2023/05/02/testing-obmc-console-with-socat.html 110 */ 111 if (!strncmp(DEV_PTS_PATH, tty_path_input_real, strlen(DEV_PTS_PATH))) { 112 console->tty.type = TTY_DEVICE_PTY; 113 console->tty.dev = strdup(console->tty.kname); 114 rc = console->tty.dev ? 0 : -1; 115 goto out_free; 116 } 117 118 tty_kname_real = basename(tty_path_input_real); 119 if (!tty_kname_real) { 120 warn("Can't find real name for %s", console->tty.kname); 121 rc = -1; 122 goto out_free; 123 } 124 125 rc = asprintf(&tty_class_device_link, "/sys/class/tty/%s", 126 tty_kname_real); 127 if (rc < 0) { 128 goto out_free; 129 } 130 131 tty_device_tty_dir = realpath(tty_class_device_link, NULL); 132 if (!tty_device_tty_dir) { 133 warn("Can't query sysfs for device %s", tty_kname_real); 134 rc = -1; 135 goto out_free; 136 } 137 138 rc = asprintf(&tty_device_reldir, "%s/../../", tty_device_tty_dir); 139 if (rc < 0) { 140 goto out_free; 141 } 142 143 tty_sysfs_devnode = realpath(tty_device_reldir, NULL); 144 if (!tty_sysfs_devnode) { 145 warn("Can't find parent device for %s", tty_kname_real); 146 } 147 148 rc = asprintf(&console->tty.dev, "/dev/%s", tty_kname_real); 149 if (rc < 0) { 150 goto out_free; 151 } 152 153 // Default to non-VUART 154 console->tty.type = TTY_DEVICE_UART; 155 156 /* Arbitrarily pick an attribute to differentiate UART vs VUART */ 157 if (tty_sysfs_devnode) { 158 rc = asprintf(&tty_vuart_lpc_addr, "%s/lpc_address", 159 tty_sysfs_devnode); 160 if (rc < 0) { 161 goto out_free; 162 } 163 164 rc = access(tty_vuart_lpc_addr, F_OK); 165 if (!rc) { 166 console->tty.type = TTY_DEVICE_VUART; 167 console->tty.vuart.sysfs_devnode = 168 strdup(tty_sysfs_devnode); 169 } 170 } 171 172 rc = 0; 173 174 out_free: 175 free(tty_vuart_lpc_addr); 176 free(tty_class_device_link); 177 free(tty_device_tty_dir); 178 free(tty_device_reldir); 179 free(tty_path_input); 180 free(tty_path_input_real); 181 return rc; 182 } 183 184 static int tty_set_sysfs_attr(struct console *console, const char *name, 185 int value) 186 { 187 char *path; 188 FILE *fp; 189 int rc; 190 191 assert(console->tty.type == TTY_DEVICE_VUART); 192 193 if (!console->tty.vuart.sysfs_devnode) { 194 return -1; 195 } 196 197 rc = asprintf(&path, "%s/%s", console->tty.vuart.sysfs_devnode, name); 198 if (rc < 0) { 199 return -1; 200 } 201 202 fp = fopen(path, "w"); 203 if (!fp) { 204 warn("Can't access attribute %s on device %s", name, 205 console->tty.kname); 206 rc = -1; 207 goto out_free; 208 } 209 setvbuf(fp, NULL, _IONBF, 0); 210 211 rc = fprintf(fp, "0x%x", value); 212 if (rc < 0) { 213 warn("Error writing to %s attribute of device %s", name, 214 console->tty.kname); 215 } 216 fclose(fp); 217 218 out_free: 219 free(path); 220 return rc; 221 } 222 223 /** 224 * Set termios attributes on the console tty. 225 */ 226 void tty_init_termios(struct console *console) 227 { 228 struct termios termios; 229 int rc; 230 231 rc = tcgetattr(console->tty.fd, &termios); 232 if (rc) { 233 warn("Can't read tty termios"); 234 return; 235 } 236 237 if (console->tty.type == TTY_DEVICE_UART && console->tty.uart.baud) { 238 if (cfsetspeed(&termios, console->tty.uart.baud) < 0) { 239 warn("Couldn't set speeds for %s", console->tty.kname); 240 } 241 } 242 243 /* Set console to raw mode: we don't want any processing to occur on 244 * the underlying terminal input/output. 245 */ 246 cfmakeraw(&termios); 247 248 rc = tcsetattr(console->tty.fd, TCSANOW, &termios); 249 if (rc) { 250 warn("Can't set terminal options for %s", console->tty.kname); 251 } 252 } 253 254 /** 255 * Open and initialise the serial device 256 */ 257 static void tty_init_vuart_io(struct console *console) 258 { 259 assert(console->tty.type == TTY_DEVICE_VUART); 260 261 if (console->tty.vuart.sirq) { 262 tty_set_sysfs_attr(console, "sirq", console->tty.vuart.sirq); 263 } 264 265 if (console->tty.vuart.lpc_addr) { 266 tty_set_sysfs_attr(console, "lpc_address", 267 console->tty.vuart.lpc_addr); 268 } 269 } 270 271 static int tty_init_io(struct console *console) 272 { 273 console->tty.fd = open(console->tty.dev, O_RDWR); 274 if (console->tty.fd <= 0) { 275 warn("Can't open tty %s", console->tty.dev); 276 return -1; 277 } 278 279 /* Disable character delay. We may want to later enable this when 280 * we detect larger amounts of data 281 */ 282 fcntl(console->tty.fd, F_SETFL, FNDELAY); 283 284 tty_init_termios(console); 285 286 console->pollfds[console->n_pollers].fd = console->tty.fd; 287 console->pollfds[console->n_pollers].events = POLLIN; 288 289 return 0; 290 } 291 292 static int tty_init_vuart(struct console *console, struct config *config) 293 { 294 unsigned long parsed; 295 const char *val; 296 char *endp; 297 298 assert(console->tty.type == TTY_DEVICE_VUART); 299 300 val = config_get_value(config, "lpc-address"); 301 if (val) { 302 errno = 0; 303 parsed = strtoul(val, &endp, 0); 304 if (parsed == ULONG_MAX && errno == ERANGE) { 305 warn("Cannot interpret 'lpc-address' value as an unsigned long: '%s'", 306 val); 307 return -1; 308 } 309 310 if (parsed > UINT16_MAX) { 311 warn("Invalid LPC address '%s'", val); 312 return -1; 313 } 314 315 console->tty.vuart.lpc_addr = (uint16_t)parsed; 316 if (endp == optarg) { 317 warn("Invalid LPC address: '%s'", val); 318 return -1; 319 } 320 } 321 322 val = config_get_value(config, "sirq"); 323 if (val) { 324 errno = 0; 325 parsed = strtoul(val, &endp, 0); 326 if (parsed == ULONG_MAX && errno == ERANGE) { 327 warn("Cannot interpret 'sirq' value as an unsigned long: '%s'", 328 val); 329 } 330 331 if (parsed > 16) { 332 warn("Invalid LPC SERIRQ: '%s'", val); 333 } 334 335 console->tty.vuart.sirq = (int)parsed; 336 if (endp == optarg) { 337 warn("Invalid sirq: '%s'", val); 338 } 339 } 340 341 return 0; 342 } 343 344 static int tty_init(struct console *console, struct config *config, 345 const char *tty_arg) 346 { 347 const char *val; 348 int rc; 349 350 if (tty_arg) { 351 console->tty.kname = tty_arg; 352 } else if ((val = config_get_value(config, "upstream-tty"))) { 353 console->tty.kname = val; 354 } else { 355 warnx("Error: No TTY device specified"); 356 return -1; 357 } 358 359 rc = tty_find_device(console); 360 if (rc) { 361 return rc; 362 } 363 364 switch (console->tty.type) { 365 case TTY_DEVICE_VUART: 366 rc = tty_init_vuart(console, config); 367 if (rc) { 368 return rc; 369 } 370 371 tty_init_vuart_io(console); 372 break; 373 case TTY_DEVICE_UART: 374 val = config_get_value(config, "baud"); 375 if (val) { 376 if (config_parse_baud(&console->tty.uart.baud, val)) { 377 warnx("Invalid baud rate: '%s'", val); 378 } 379 } 380 break; 381 case TTY_DEVICE_PTY: 382 break; 383 case TTY_DEVICE_UNDEFINED: 384 default: 385 warnx("Cannot configure unrecognised TTY device"); 386 return -1; 387 } 388 389 return tty_init_io(console); 390 } 391 392 static void tty_fini(struct console *console) 393 { 394 if (console->tty.type == TTY_DEVICE_VUART) { 395 free(console->tty.vuart.sysfs_devnode); 396 } 397 free(console->tty.dev); 398 } 399 400 static int write_to_path(const char *path, const char *data) 401 { 402 int rc = 0; 403 FILE *f = fopen(path, "w"); 404 if (!f) { 405 return -1; 406 } 407 408 if (fprintf(f, "%s", data) < 0) { 409 rc = -1; 410 } 411 412 if (fclose(f)) { 413 rc = -1; 414 } 415 416 return rc; 417 } 418 419 #define ASPEED_UART_ROUTING_PATTERN \ 420 "/sys/bus/platform/drivers/aspeed-uart-routing/*.uart-routing" 421 422 static void uart_routing_init(struct config *config) 423 { 424 const char *muxcfg; 425 const char *p; 426 size_t buflen; 427 char *sink; 428 char *source; 429 char *muxdir; 430 char *path; 431 glob_t globbuf; 432 433 muxcfg = config_get_value(config, "aspeed-uart-routing"); 434 if (!muxcfg) { 435 return; 436 } 437 438 /* Find the driver's sysfs directory */ 439 if (glob(ASPEED_UART_ROUTING_PATTERN, GLOB_ERR | GLOB_NOSORT, NULL, 440 &globbuf) != 0) { 441 warn("Couldn't find uart-routing driver directory, cannot apply config"); 442 return; 443 } 444 if (globbuf.gl_pathc != 1) { 445 warnx("Found %zd uart-routing driver directories, cannot apply config", 446 globbuf.gl_pathc); 447 goto out_free_glob; 448 } 449 muxdir = globbuf.gl_pathv[0]; 450 451 /* 452 * Rather than faff about tracking a bunch of separate buffer sizes, 453 * just use one (worst-case) size for all of them -- +2 for a trailing 454 * NUL and a '/' separator to construct the sysfs file path. 455 */ 456 buflen = strlen(muxdir) + strlen(muxcfg) + 2; 457 458 sink = malloc(buflen); 459 source = malloc(buflen); 460 path = malloc(buflen); 461 if (!path || !sink || !source) { 462 warnx("Out of memory applying uart routing config"); 463 goto out_free_bufs; 464 } 465 466 p = muxcfg; 467 while (*p) { 468 ssize_t bytes_scanned; 469 470 if (sscanf(p, " %[^:/ \t]:%[^: \t] %zn", sink, source, 471 &bytes_scanned) != 2) { 472 warnx("Invalid syntax in aspeed uart config: '%s' not applied", 473 p); 474 break; 475 } 476 p += bytes_scanned; 477 478 /* 479 * Check that the sink name looks reasonable before proceeding 480 * (there are other writable files in the same directory that 481 * we shouldn't be touching, such as 'driver_override' and 482 * 'uevent'). 483 */ 484 if (strncmp(sink, "io", strlen("io")) != 0 && 485 strncmp(sink, "uart", strlen("uart")) != 0) { 486 warnx("Skipping invalid uart routing name '%s' (must be ioN or uartN)", 487 sink); 488 continue; 489 } 490 491 snprintf(path, buflen, "%s/%s", muxdir, sink); 492 if (write_to_path(path, source)) { 493 warn("Failed to apply uart-routing config '%s:%s'", 494 sink, source); 495 } 496 } 497 498 out_free_bufs: 499 free(path); 500 free(source); 501 free(sink); 502 out_free_glob: 503 globfree(&globbuf); 504 } 505 506 int console_data_out(struct console *console, const uint8_t *data, size_t len) 507 { 508 return write_buf_to_fd(console->tty.fd, data, len); 509 } 510 511 /* Prepare a socket name */ 512 static int set_socket_info(struct console *console, struct config *config, 513 const char *console_id) 514 { 515 ssize_t len; 516 517 /* Get console id */ 518 console->console_id = config_resolve_console_id(config, console_id); 519 520 /* Get the socket name/path */ 521 len = console_socket_path(console->socket_name, console->console_id); 522 if (len < 0) { 523 warn("Failed to set socket path: %s", strerror(errno)); 524 return EXIT_FAILURE; 525 } 526 527 /* Socket name is not a null terminated string hence save the length */ 528 console->socket_name_len = len; 529 530 return 0; 531 } 532 533 static void handlers_init(struct console *console, struct config *config) 534 { 535 /* NOLINTBEGIN(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) */ 536 extern struct handler *__start_handlers; 537 extern struct handler *__stop_handlers; 538 /* NOLINTEND(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) */ 539 struct handler *handler; 540 int i; 541 int rc; 542 543 console->n_handlers = &__stop_handlers - &__start_handlers; 544 console->handlers = &__start_handlers; 545 546 printf("%ld handler%s\n", console->n_handlers, 547 console->n_handlers == 1 ? "" : "s"); 548 549 for (i = 0; i < console->n_handlers; i++) { 550 handler = console->handlers[i]; 551 552 rc = 0; 553 if (handler->init) { 554 rc = handler->init(handler, console, config); 555 } 556 557 handler->active = rc == 0; 558 559 printf(" %s [%sactive]\n", handler->name, 560 handler->active ? "" : "in"); 561 } 562 } 563 564 static void handlers_fini(struct console *console) 565 { 566 struct handler *handler; 567 int i; 568 569 for (i = 0; i < console->n_handlers; i++) { 570 handler = console->handlers[i]; 571 if (handler->fini && handler->active) { 572 handler->fini(handler); 573 } 574 } 575 } 576 577 static int get_current_time(struct timeval *tv) 578 { 579 struct timespec t; 580 int rc; 581 582 /* 583 * We use clock_gettime(CLOCK_MONOTONIC) so we're immune to 584 * local time changes. However, a struct timeval is more 585 * convenient for calculations, so convert to that. 586 */ 587 rc = clock_gettime(CLOCK_MONOTONIC, &t); 588 if (rc) { 589 return rc; 590 } 591 592 tv->tv_sec = t.tv_sec; 593 tv->tv_usec = t.tv_nsec / 1000; 594 595 return 0; 596 } 597 598 struct ringbuffer_consumer * 599 console_ringbuffer_consumer_register(struct console *console, 600 ringbuffer_poll_fn_t poll_fn, void *data) 601 { 602 return ringbuffer_consumer_register(console->rb, poll_fn, data); 603 } 604 605 struct poller *console_poller_register(struct console *console, 606 struct handler *handler, 607 poller_event_fn_t poller_fn, 608 poller_timeout_fn_t timeout_fn, int fd, 609 int events, void *data) 610 { 611 struct poller *poller; 612 long n; 613 614 poller = malloc(sizeof(*poller)); 615 poller->remove = false; 616 poller->handler = handler; 617 poller->event_fn = poller_fn; 618 poller->timeout_fn = timeout_fn; 619 poller->data = data; 620 621 /* add one to our pollers array */ 622 n = console->n_pollers++; 623 /* 624 * We're managing an array of pointers to aggregates, so don't warn about sizeof() on a 625 * pointer type. 626 */ 627 /* NOLINTBEGIN(bugprone-sizeof-expression) */ 628 console->pollers = reallocarray(console->pollers, console->n_pollers, 629 sizeof(*console->pollers)); 630 /* NOLINTEND(bugprone-sizeof-expression) */ 631 632 console->pollers[n] = poller; 633 634 /* increase pollfds array too */ 635 console->pollfds = reallocarray( 636 console->pollfds, (MAX_INTERNAL_POLLFD + console->n_pollers), 637 sizeof(*console->pollfds)); 638 639 /* shift the end pollfds up by one */ 640 memcpy(&console->pollfds[n + 1], &console->pollfds[n], 641 sizeof(*console->pollfds) * MAX_INTERNAL_POLLFD); 642 643 console->pollfds[n].fd = fd; 644 console->pollfds[n].events = (short)(events & 0x7fff); 645 646 return poller; 647 } 648 649 void console_poller_unregister(struct console *console, struct poller *poller) 650 { 651 int i; 652 653 /* find the entry in our pollers array */ 654 for (i = 0; i < console->n_pollers; i++) { 655 if (console->pollers[i] == poller) { 656 break; 657 } 658 } 659 660 assert(i < console->n_pollers); 661 662 console->n_pollers--; 663 664 /* 665 * Remove the item from the pollers array... 666 * 667 * We're managing an array of pointers to aggregates, so don't warn about sizeof() on a 668 * pointer type. 669 */ 670 /* NOLINTBEGIN(bugprone-sizeof-expression) */ 671 memmove(&console->pollers[i], &console->pollers[i + 1], 672 sizeof(*console->pollers) * (console->n_pollers - i)); 673 674 console->pollers = reallocarray(console->pollers, console->n_pollers, 675 sizeof(*console->pollers)); 676 /* NOLINTEND(bugprone-sizeof-expression) */ 677 678 /* ... and the pollfds array */ 679 memmove(&console->pollfds[i], &console->pollfds[i + 1], 680 sizeof(*console->pollfds) * 681 (MAX_INTERNAL_POLLFD + console->n_pollers - i)); 682 683 console->pollfds = reallocarray( 684 console->pollfds, (MAX_INTERNAL_POLLFD + console->n_pollers), 685 sizeof(*console->pollfds)); 686 687 free(poller); 688 } 689 690 void console_poller_set_events(struct console *console, struct poller *poller, 691 int events) 692 { 693 int i; 694 695 /* find the entry in our pollers array */ 696 for (i = 0; i < console->n_pollers; i++) { 697 if (console->pollers[i] == poller) { 698 break; 699 } 700 } 701 702 console->pollfds[i].events = (short)(events & 0x7fff); 703 } 704 705 void console_poller_set_timeout(struct console *console __attribute__((unused)), 706 struct poller *poller, const struct timeval *tv) 707 { 708 struct timeval now; 709 int rc; 710 711 rc = get_current_time(&now); 712 if (rc) { 713 return; 714 } 715 716 timeradd(&now, tv, &poller->timeout); 717 } 718 719 static long get_poll_timeout(struct console *console, struct timeval *cur_time) 720 { 721 struct timeval *earliest; 722 struct timeval interval; 723 struct poller *poller; 724 int i; 725 726 earliest = NULL; 727 728 for (i = 0; i < console->n_pollers; i++) { 729 poller = console->pollers[i]; 730 731 if (poller->timeout_fn && timerisset(&poller->timeout) && 732 (!earliest || 733 (earliest && timercmp(&poller->timeout, earliest, <)))) { 734 // poller is buffering data and needs the poll 735 // function to timeout. 736 earliest = &poller->timeout; 737 } 738 } 739 740 if (earliest) { 741 if (timercmp(earliest, cur_time, >)) { 742 /* recalculate the timeout period, time period has 743 * not elapsed */ 744 timersub(earliest, cur_time, &interval); 745 return ((interval.tv_sec * 1000) + 746 (interval.tv_usec / 1000)); 747 } /* return from poll immediately */ 748 return 0; 749 750 } /* poll indefinitely */ 751 return -1; 752 } 753 754 static int call_pollers(struct console *console, struct timeval *cur_time) 755 { 756 struct poller *poller; 757 struct pollfd *pollfd; 758 enum poller_ret prc; 759 int i; 760 int rc; 761 762 rc = 0; 763 764 /* 765 * Process poll events by iterating through the pollers and pollfds 766 * in-step, calling any pollers that we've found revents for. 767 */ 768 for (i = 0; i < console->n_pollers; i++) { 769 poller = console->pollers[i]; 770 pollfd = &console->pollfds[i]; 771 prc = POLLER_OK; 772 773 /* process pending events... */ 774 if (pollfd->revents) { 775 prc = poller->event_fn(poller->handler, pollfd->revents, 776 poller->data); 777 if (prc == POLLER_EXIT) { 778 rc = -1; 779 } else if (prc == POLLER_REMOVE) { 780 poller->remove = true; 781 } 782 } 783 784 if ((prc == POLLER_OK) && poller->timeout_fn && 785 timerisset(&poller->timeout) && 786 timercmp(&poller->timeout, cur_time, <=)) { 787 /* One of the ringbuffer consumers is buffering the 788 data stream. The amount of idle time the consumer 789 desired has expired. Process the buffered data for 790 transmission. */ 791 timerclear(&poller->timeout); 792 prc = poller->timeout_fn(poller->handler, poller->data); 793 if (prc == POLLER_EXIT) { 794 rc = -1; 795 } else if (prc == POLLER_REMOVE) { 796 poller->remove = true; 797 } 798 } 799 } 800 801 /** 802 * Process deferred removals; restarting each time we unregister, as 803 * the array will have changed 804 */ 805 for (;;) { 806 bool removed = false; 807 808 for (i = 0; i < console->n_pollers; i++) { 809 poller = console->pollers[i]; 810 if (poller->remove) { 811 console_poller_unregister(console, poller); 812 removed = true; 813 break; 814 } 815 } 816 if (!removed) { 817 break; 818 } 819 } 820 821 return rc; 822 } 823 824 static void sighandler(int signal) 825 { 826 if (signal == SIGINT) { 827 sigint = true; 828 } 829 } 830 831 int run_console(struct console *console) 832 { 833 sighandler_t sighandler_save = signal(SIGINT, sighandler); 834 struct timeval tv; 835 long timeout; 836 ssize_t rc; 837 838 rc = 0; 839 840 for (;;) { 841 uint8_t buf[4096]; 842 843 if (console->rb->size < sizeof(buf)) { 844 fprintf(stderr, 845 "Ringbuffer size should be greater than %zuB\n", 846 sizeof(buf)); 847 rc = -1; 848 break; 849 } 850 851 if (sigint) { 852 fprintf(stderr, "Received interrupt, exiting\n"); 853 break; 854 } 855 856 rc = get_current_time(&tv); 857 if (rc) { 858 warn("Failed to read current time"); 859 break; 860 } 861 862 timeout = get_poll_timeout(console, &tv); 863 864 rc = poll(console->pollfds, 865 console->n_pollers + MAX_INTERNAL_POLLFD, 866 (int)timeout); 867 868 if (rc < 0) { 869 if (errno == EINTR) { 870 continue; 871 } 872 warn("poll error"); 873 break; 874 } 875 876 /* process internal fd first */ 877 if (console->pollfds[console->n_pollers].revents) { 878 rc = read(console->tty.fd, buf, sizeof(buf)); 879 if (rc <= 0) { 880 warn("Error reading from tty device"); 881 rc = -1; 882 break; 883 } 884 rc = ringbuffer_queue(console->rb, buf, rc); 885 if (rc) { 886 break; 887 } 888 } 889 890 if (console->pollfds[console->n_pollers + 1].revents) { 891 sd_bus_process(console->bus, NULL); 892 } 893 894 /* ... and then the pollers */ 895 rc = call_pollers(console, &tv); 896 if (rc) { 897 break; 898 } 899 } 900 901 signal(SIGINT, sighandler_save); 902 sd_bus_unref(console->bus); 903 904 return rc ? -1 : 0; 905 } 906 static const struct option options[] = { 907 { "config", required_argument, 0, 'c' }, 908 { "console-id", required_argument, 0, 'i' }, 909 { 0, 0, 0, 0 }, 910 }; 911 912 int main(int argc, char **argv) 913 { 914 size_t buffer_size = default_buffer_size; 915 const char *config_filename = NULL; 916 const char *config_tty_kname = NULL; 917 const char *buffer_size_str = NULL; 918 const char *console_id = NULL; 919 struct console *console; 920 struct config *config; 921 int rc; 922 923 for (;;) { 924 int c; 925 int idx; 926 927 c = getopt_long(argc, argv, "c:i:", options, &idx); 928 if (c == -1) { 929 break; 930 } 931 932 switch (c) { 933 case 'c': 934 config_filename = optarg; 935 break; 936 case 'i': 937 console_id = optarg; 938 break; 939 case 'h': 940 case '?': 941 usage(argv[0]); 942 return EXIT_SUCCESS; 943 } 944 } 945 946 if (optind < argc) { 947 config_tty_kname = argv[optind]; 948 } 949 950 config = config_init(config_filename); 951 952 console = malloc(sizeof(struct console)); 953 memset(console, 0, sizeof(*console)); 954 console->pollfds = 955 calloc(MAX_INTERNAL_POLLFD, sizeof(*console->pollfds)); 956 buffer_size_str = config_get_value(config, "ringbuffer-size"); 957 if (buffer_size_str) { 958 rc = config_parse_bytesize(buffer_size_str, &buffer_size); 959 if (rc) { 960 warn("Invalid ringbuffer-size. Default to %zukB", 961 buffer_size >> 10); 962 } 963 } 964 console->rb = ringbuffer_init(buffer_size); 965 966 if (set_socket_info(console, config, console_id)) { 967 rc = -1; 968 goto out_config_fini; 969 } 970 971 uart_routing_init(config); 972 973 rc = tty_init(console, config, config_tty_kname); 974 if (rc) { 975 goto out_config_fini; 976 } 977 978 dbus_init(console, config); 979 980 handlers_init(console, config); 981 982 rc = run_console(console); 983 984 handlers_fini(console); 985 986 tty_fini(console); 987 988 out_config_fini: 989 config_fini(config); 990 991 free(console->pollers); 992 free(console->pollfds); 993 free(console); 994 995 return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE; 996 } 997