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