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