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 <limits.h> 32 #include <time.h> 33 #include <termios.h> 34 35 #include <sys/types.h> 36 #include <sys/time.h> 37 #include <sys/socket.h> 38 #include <poll.h> 39 40 #include "console-server.h" 41 42 /* size of the shared backlog ringbuffer */ 43 const size_t buffer_size = 128ul * 1024ul; 44 45 /* state shared with the signal handler */ 46 static bool sigint; 47 48 static void usage(const char *progname) 49 { 50 fprintf(stderr, 51 "usage: %s [options] <DEVICE>\n" 52 "\n" 53 "Options:\n" 54 " --config <FILE> Use FILE for configuration\n" 55 "", 56 progname); 57 } 58 59 /* populates tty_dev and tty_sysfs_devnode, using the tty kernel name */ 60 static int tty_find_device(struct console *console) 61 { 62 char *tty_class_device_link; 63 char *tty_device_tty_dir; 64 char *tty_device_reldir; 65 char *tty_path_input; 66 char *tty_path_input_real; 67 char *tty_kname_real; 68 int rc; 69 70 tty_class_device_link = NULL; 71 tty_device_tty_dir = NULL; 72 tty_device_reldir = NULL; 73 tty_path_input = NULL; 74 tty_path_input_real = NULL; 75 tty_kname_real = NULL; 76 77 /* udev may rename the tty name with a symbol link, try to resolve */ 78 rc = asprintf(&tty_path_input, "/dev/%s", console->tty_kname); 79 if (rc < 0) { 80 return -1; 81 } 82 83 tty_path_input_real = realpath(tty_path_input, NULL); 84 if (!tty_path_input_real) { 85 warn("Can't find realpath for /dev/%s", console->tty_kname); 86 rc = -1; 87 goto out_free; 88 } 89 90 tty_kname_real = basename(tty_path_input_real); 91 if (!tty_kname_real) { 92 warn("Can't find real name for /dev/%s", console->tty_kname); 93 rc = -1; 94 goto out_free; 95 } 96 97 rc = asprintf(&tty_class_device_link, "/sys/class/tty/%s", 98 tty_kname_real); 99 if (rc < 0) { 100 goto out_free; 101 } 102 103 tty_device_tty_dir = realpath(tty_class_device_link, NULL); 104 if (!tty_device_tty_dir) { 105 warn("Can't query sysfs for device %s", tty_kname_real); 106 rc = -1; 107 goto out_free; 108 } 109 110 rc = asprintf(&tty_device_reldir, "%s/../../", tty_device_tty_dir); 111 if (rc < 0) { 112 goto out_free; 113 } 114 115 console->tty_sysfs_devnode = realpath(tty_device_reldir, NULL); 116 if (!console->tty_sysfs_devnode) { 117 warn("Can't find parent device for %s", tty_kname_real); 118 } 119 120 rc = asprintf(&console->tty_dev, "/dev/%s", tty_kname_real); 121 if (rc < 0) { 122 goto out_free; 123 } 124 125 rc = 0; 126 127 out_free: 128 free(tty_class_device_link); 129 free(tty_device_tty_dir); 130 free(tty_device_reldir); 131 free(tty_path_input); 132 free(tty_path_input_real); 133 return rc; 134 } 135 136 static int tty_set_sysfs_attr(struct console *console, const char *name, 137 int value) 138 { 139 char *path; 140 FILE *fp; 141 int rc; 142 143 rc = asprintf(&path, "%s/%s", console->tty_sysfs_devnode, name); 144 if (rc < 0) { 145 return -1; 146 } 147 148 fp = fopen(path, "w"); 149 if (!fp) { 150 warn("Can't access attribute %s on device %s", name, 151 console->tty_kname); 152 rc = -1; 153 goto out_free; 154 } 155 setvbuf(fp, NULL, _IONBF, 0); 156 157 rc = fprintf(fp, "0x%x", value); 158 if (rc < 0) { 159 warn("Error writing to %s attribute of device %s", name, 160 console->tty_kname); 161 } 162 fclose(fp); 163 164 out_free: 165 free(path); 166 return rc; 167 } 168 169 /** 170 * Set termios attributes on the console tty. 171 */ 172 void tty_init_termios(struct console *console) 173 { 174 struct termios termios; 175 int rc; 176 177 rc = tcgetattr(console->tty_fd, &termios); 178 if (rc) { 179 warn("Can't read tty termios"); 180 return; 181 } 182 183 if (console->tty_baud) { 184 if (cfsetspeed(&termios, console->tty_baud) < 0) { 185 warn("Couldn't set speeds for %s", console->tty_kname); 186 } 187 } 188 189 /* Set console to raw mode: we don't want any processing to occur on 190 * the underlying terminal input/output. 191 */ 192 cfmakeraw(&termios); 193 194 rc = tcsetattr(console->tty_fd, TCSANOW, &termios); 195 if (rc) { 196 warn("Can't set terminal options for %s", console->tty_kname); 197 } 198 } 199 200 /** 201 * Open and initialise the serial device 202 */ 203 static int tty_init_io(struct console *console) 204 { 205 if (console->tty_sirq) { 206 tty_set_sysfs_attr(console, "sirq", console->tty_sirq); 207 } 208 if (console->tty_lpc_addr) { 209 tty_set_sysfs_attr(console, "lpc_address", 210 console->tty_lpc_addr); 211 } 212 213 console->tty_fd = open(console->tty_dev, O_RDWR); 214 if (console->tty_fd <= 0) { 215 warn("Can't open tty %s", console->tty_dev); 216 return -1; 217 } 218 219 /* Disable character delay. We may want to later enable this when 220 * we detect larger amounts of data 221 */ 222 fcntl(console->tty_fd, F_SETFL, FNDELAY); 223 224 tty_init_termios(console); 225 226 console->pollfds[console->n_pollers].fd = console->tty_fd; 227 console->pollfds[console->n_pollers].events = POLLIN; 228 229 return 0; 230 } 231 232 static int tty_init(struct console *console, struct config *config) 233 { 234 unsigned long parsed; 235 const char *val; 236 char *endp; 237 int rc; 238 239 val = config_get_value(config, "lpc-address"); 240 if (val) { 241 errno = 0; 242 parsed = strtoul(val, &endp, 0); 243 if (parsed == ULONG_MAX && errno == ERANGE) { 244 warn("Cannot interpret 'lpc-address' value as an unsigned long: '%s'", 245 val); 246 return -1; 247 } 248 249 if (parsed > UINT16_MAX) { 250 warn("Invalid LPC address '%s'", val); 251 return -1; 252 } 253 254 console->tty_lpc_addr = (uint16_t)parsed; 255 if (endp == optarg) { 256 warn("Invalid LPC address: '%s'", val); 257 return -1; 258 } 259 } 260 261 val = config_get_value(config, "sirq"); 262 if (val) { 263 errno = 0; 264 parsed = strtoul(val, &endp, 0); 265 if (parsed == ULONG_MAX && errno == ERANGE) { 266 warn("Cannot interpret 'sirq' value as an unsigned long: '%s'", 267 val); 268 } 269 270 if (parsed > 16) { 271 warn("Invalid LPC SERIRQ: '%s'", val); 272 } 273 274 console->tty_sirq = (int)parsed; 275 if (endp == optarg) { 276 warn("Invalid sirq: '%s'", val); 277 } 278 } 279 280 val = config_get_value(config, "baud"); 281 if (val) { 282 if (config_parse_baud(&console->tty_baud, val)) { 283 warnx("Invalid baud rate: '%s'", val); 284 } 285 } 286 287 if (!console->tty_kname) { 288 warnx("Error: No TTY device specified"); 289 return -1; 290 } 291 292 rc = tty_find_device(console); 293 if (rc) { 294 return rc; 295 } 296 297 rc = tty_init_io(console); 298 return rc; 299 } 300 301 int console_data_out(struct console *console, const uint8_t *data, size_t len) 302 { 303 return write_buf_to_fd(console->tty_fd, data, len); 304 } 305 306 /* Read console if from config and prepare a socket name */ 307 static int set_socket_info(struct console *console, struct config *config) 308 { 309 ssize_t len; 310 311 console->console_id = config_get_value(config, "socket-id"); 312 if (!console->console_id) { 313 warnx("Error: The socket-id is not set in the config file"); 314 return EXIT_FAILURE; 315 } 316 317 /* Get the socket name/path */ 318 len = console_socket_path(console->socket_name, console->console_id); 319 if (len < 0) { 320 warn("Failed to set socket path: %s", strerror(errno)); 321 return EXIT_FAILURE; 322 } 323 324 /* Socket name is not a null terminated string hence save the length */ 325 console->socket_name_len = len; 326 327 return 0; 328 } 329 330 static void handlers_init(struct console *console, struct config *config) 331 { 332 /* NOLINTBEGIN(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) */ 333 extern struct handler *__start_handlers; 334 extern struct handler *__stop_handlers; 335 /* NOLINTEND(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) */ 336 struct handler *handler; 337 int i; 338 int rc; 339 340 console->n_handlers = &__stop_handlers - &__start_handlers; 341 console->handlers = &__start_handlers; 342 343 printf("%ld handler%s\n", console->n_handlers, 344 console->n_handlers == 1 ? "" : "s"); 345 346 for (i = 0; i < console->n_handlers; i++) { 347 handler = console->handlers[i]; 348 349 rc = 0; 350 if (handler->init) { 351 rc = handler->init(handler, console, config); 352 } 353 354 handler->active = rc == 0; 355 356 printf(" %s [%sactive]\n", handler->name, 357 handler->active ? "" : "in"); 358 } 359 } 360 361 static void handlers_fini(struct console *console) 362 { 363 struct handler *handler; 364 int i; 365 366 for (i = 0; i < console->n_handlers; i++) { 367 handler = console->handlers[i]; 368 if (handler->fini && handler->active) { 369 handler->fini(handler); 370 } 371 } 372 } 373 374 static int get_current_time(struct timeval *tv) 375 { 376 struct timespec t; 377 int rc; 378 379 /* 380 * We use clock_gettime(CLOCK_MONOTONIC) so we're immune to 381 * local time changes. However, a struct timeval is more 382 * convenient for calculations, so convert to that. 383 */ 384 rc = clock_gettime(CLOCK_MONOTONIC, &t); 385 if (rc) { 386 return rc; 387 } 388 389 tv->tv_sec = t.tv_sec; 390 tv->tv_usec = t.tv_nsec / 1000; 391 392 return 0; 393 } 394 395 struct ringbuffer_consumer * 396 console_ringbuffer_consumer_register(struct console *console, 397 ringbuffer_poll_fn_t poll_fn, void *data) 398 { 399 return ringbuffer_consumer_register(console->rb, poll_fn, data); 400 } 401 402 struct poller *console_poller_register(struct console *console, 403 struct handler *handler, 404 poller_event_fn_t poller_fn, 405 poller_timeout_fn_t timeout_fn, int fd, 406 int events, void *data) 407 { 408 struct poller *poller; 409 long n; 410 411 poller = malloc(sizeof(*poller)); 412 poller->remove = false; 413 poller->handler = handler; 414 poller->event_fn = poller_fn; 415 poller->timeout_fn = timeout_fn; 416 poller->data = data; 417 418 /* add one to our pollers array */ 419 n = console->n_pollers++; 420 /* 421 * We're managing an array of pointers to aggregates, so don't warn about sizeof() on a 422 * pointer type. 423 */ 424 /* NOLINTBEGIN(bugprone-sizeof-expression) */ 425 console->pollers = reallocarray(console->pollers, console->n_pollers, 426 sizeof(*console->pollers)); 427 /* NOLINTEND(bugprone-sizeof-expression) */ 428 429 console->pollers[n] = poller; 430 431 /* increase pollfds array too */ 432 console->pollfds = 433 reallocarray(console->pollfds, 434 (MAX_INTERNAL_POLLFD + console->n_pollers), 435 sizeof(*console->pollfds)); 436 437 /* shift the end pollfds up by one */ 438 memcpy(&console->pollfds[n + 1], &console->pollfds[n], 439 sizeof(*console->pollfds) * MAX_INTERNAL_POLLFD); 440 441 console->pollfds[n].fd = fd; 442 console->pollfds[n].events = (short)(events & 0x7fff); 443 444 return poller; 445 } 446 447 void console_poller_unregister(struct console *console, struct poller *poller) 448 { 449 int i; 450 451 /* find the entry in our pollers array */ 452 for (i = 0; i < console->n_pollers; i++) { 453 if (console->pollers[i] == poller) { 454 break; 455 } 456 } 457 458 assert(i < console->n_pollers); 459 460 console->n_pollers--; 461 462 /* 463 * Remove the item from the pollers array... 464 * 465 * We're managing an array of pointers to aggregates, so don't warn about sizeof() on a 466 * pointer type. 467 */ 468 /* NOLINTBEGIN(bugprone-sizeof-expression) */ 469 memmove(&console->pollers[i], &console->pollers[i + 1], 470 sizeof(*console->pollers) * (console->n_pollers - i)); 471 472 console->pollers = reallocarray(console->pollers, console->n_pollers, 473 sizeof(*console->pollers)); 474 /* NOLINTEND(bugprone-sizeof-expression) */ 475 476 /* ... and the pollfds array */ 477 memmove(&console->pollfds[i], &console->pollfds[i + 1], 478 sizeof(*console->pollfds) * 479 (MAX_INTERNAL_POLLFD + console->n_pollers - i)); 480 481 console->pollfds = 482 reallocarray(console->pollfds, 483 (MAX_INTERNAL_POLLFD + console->n_pollers), 484 sizeof(*console->pollfds)); 485 486 free(poller); 487 } 488 489 void console_poller_set_events(struct console *console, struct poller *poller, 490 int events) 491 { 492 int i; 493 494 /* find the entry in our pollers array */ 495 for (i = 0; i < console->n_pollers; i++) { 496 if (console->pollers[i] == poller) { 497 break; 498 } 499 } 500 501 console->pollfds[i].events = (short)(events & 0x7fff); 502 } 503 504 void console_poller_set_timeout(struct console *console __attribute__((unused)), 505 struct poller *poller, const struct timeval *tv) 506 { 507 struct timeval now; 508 int rc; 509 510 rc = get_current_time(&now); 511 if (rc) { 512 return; 513 } 514 515 timeradd(&now, tv, &poller->timeout); 516 } 517 518 static long get_poll_timeout(struct console *console, struct timeval *cur_time) 519 { 520 struct timeval *earliest; 521 struct timeval interval; 522 struct poller *poller; 523 int i; 524 525 earliest = NULL; 526 527 for (i = 0; i < console->n_pollers; i++) { 528 poller = console->pollers[i]; 529 530 if (poller->timeout_fn && timerisset(&poller->timeout) && 531 (!earliest || 532 (earliest && timercmp(&poller->timeout, earliest, <)))) { 533 // poller is buffering data and needs the poll 534 // function to timeout. 535 earliest = &poller->timeout; 536 } 537 } 538 539 if (earliest) { 540 if (timercmp(earliest, cur_time, >)) { 541 /* recalculate the timeout period, time period has 542 * not elapsed */ 543 timersub(earliest, cur_time, &interval); 544 return ((interval.tv_sec * 1000) + 545 (interval.tv_usec / 1000)); 546 } /* return from poll immediately */ 547 return 0; 548 549 } /* poll indefinitely */ 550 return -1; 551 } 552 553 static int call_pollers(struct console *console, struct timeval *cur_time) 554 { 555 struct poller *poller; 556 struct pollfd *pollfd; 557 enum poller_ret prc; 558 int i; 559 int rc; 560 561 rc = 0; 562 563 /* 564 * Process poll events by iterating through the pollers and pollfds 565 * in-step, calling any pollers that we've found revents for. 566 */ 567 for (i = 0; i < console->n_pollers; i++) { 568 poller = console->pollers[i]; 569 pollfd = &console->pollfds[i]; 570 prc = POLLER_OK; 571 572 /* process pending events... */ 573 if (pollfd->revents) { 574 prc = poller->event_fn(poller->handler, pollfd->revents, 575 poller->data); 576 if (prc == POLLER_EXIT) { 577 rc = -1; 578 } else if (prc == POLLER_REMOVE) { 579 poller->remove = true; 580 } 581 } 582 583 if ((prc == POLLER_OK) && poller->timeout_fn && 584 timerisset(&poller->timeout) && 585 timercmp(&poller->timeout, cur_time, <=)) { 586 /* One of the ringbuffer consumers is buffering the 587 data stream. The amount of idle time the consumer 588 desired has expired. Process the buffered data for 589 transmission. */ 590 timerclear(&poller->timeout); 591 prc = poller->timeout_fn(poller->handler, poller->data); 592 if (prc == POLLER_EXIT) { 593 rc = -1; 594 } else if (prc == POLLER_REMOVE) { 595 poller->remove = true; 596 } 597 } 598 } 599 600 /** 601 * Process deferred removals; restarting each time we unregister, as 602 * the array will have changed 603 */ 604 for (;;) { 605 bool removed = false; 606 607 for (i = 0; i < console->n_pollers; i++) { 608 poller = console->pollers[i]; 609 if (poller->remove) { 610 console_poller_unregister(console, poller); 611 removed = true; 612 break; 613 } 614 } 615 if (!removed) { 616 break; 617 } 618 } 619 620 return rc; 621 } 622 623 static void sighandler(int signal) 624 { 625 if (signal == SIGINT) { 626 sigint = true; 627 } 628 } 629 630 int run_console(struct console *console) 631 { 632 sighandler_t sighandler_save = signal(SIGINT, sighandler); 633 struct timeval tv; 634 long timeout; 635 ssize_t rc; 636 637 rc = 0; 638 639 for (;;) { 640 uint8_t buf[4096]; 641 642 BUILD_ASSERT(sizeof(buf) <= buffer_size); 643 644 if (sigint) { 645 fprintf(stderr, "Received interrupt, exiting\n"); 646 break; 647 } 648 649 rc = get_current_time(&tv); 650 if (rc) { 651 warn("Failed to read current time"); 652 break; 653 } 654 655 timeout = get_poll_timeout(console, &tv); 656 657 rc = poll(console->pollfds, 658 console->n_pollers + MAX_INTERNAL_POLLFD, 659 (int)timeout); 660 661 if (rc < 0) { 662 if (errno == EINTR) { 663 continue; 664 } 665 warn("poll error"); 666 break; 667 } 668 669 /* process internal fd first */ 670 if (console->pollfds[console->n_pollers].revents) { 671 rc = read(console->tty_fd, buf, sizeof(buf)); 672 if (rc <= 0) { 673 warn("Error reading from tty device"); 674 rc = -1; 675 break; 676 } 677 rc = ringbuffer_queue(console->rb, buf, rc); 678 if (rc) { 679 break; 680 } 681 } 682 683 if (console->pollfds[console->n_pollers + 1].revents) { 684 sd_bus_process(console->bus, NULL); 685 } 686 687 /* ... and then the pollers */ 688 rc = call_pollers(console, &tv); 689 if (rc) { 690 break; 691 } 692 } 693 694 signal(SIGINT, sighandler_save); 695 sd_bus_unref(console->bus); 696 697 return rc ? -1 : 0; 698 } 699 static const struct option options[] = { 700 { "config", required_argument, 0, 'c' }, 701 { 0, 0, 0, 0 }, 702 }; 703 704 int main(int argc, char **argv) 705 { 706 const char *config_filename = NULL; 707 const char *config_tty_kname = NULL; 708 struct console *console; 709 struct config *config; 710 int rc; 711 712 rc = -1; 713 714 for (;;) { 715 int c; 716 int idx; 717 718 c = getopt_long(argc, argv, "c:", options, &idx); 719 if (c == -1) { 720 break; 721 } 722 723 switch (c) { 724 case 'c': 725 config_filename = optarg; 726 break; 727 case 'h': 728 case '?': 729 usage(argv[0]); 730 return EXIT_SUCCESS; 731 } 732 } 733 734 if (optind < argc) { 735 config_tty_kname = argv[optind]; 736 } 737 738 console = malloc(sizeof(struct console)); 739 memset(console, 0, sizeof(*console)); 740 console->pollfds = 741 calloc(MAX_INTERNAL_POLLFD, sizeof(*console->pollfds)); 742 console->rb = ringbuffer_init(buffer_size); 743 744 config = config_init(config_filename); 745 if (!config) { 746 warnx("Can't read configuration, exiting."); 747 goto out_free; 748 } 749 750 if (!config_tty_kname) { 751 config_tty_kname = config_get_value(config, "upstream-tty"); 752 } 753 754 if (!config_tty_kname) { 755 warnx("No TTY device specified"); 756 usage(argv[0]); 757 return EXIT_FAILURE; 758 } 759 760 if (set_socket_info(console, config)) { 761 return EXIT_FAILURE; 762 } 763 764 console->tty_kname = config_tty_kname; 765 766 console->console_id = config_get_value(config, "socket-id"); 767 if (!console->console_id) { 768 warnx("Error: The socket-id is not set in the config file"); 769 return EXIT_FAILURE; 770 } 771 772 rc = tty_init(console, config); 773 if (rc) { 774 goto out_config_fini; 775 } 776 777 dbus_init(console, config); 778 779 handlers_init(console, config); 780 781 rc = run_console(console); 782 783 handlers_fini(console); 784 785 out_config_fini: 786 config_fini(config); 787 788 out_free: 789 free(console->pollers); 790 free(console->pollfds); 791 free(console->tty_sysfs_devnode); 792 free(console->tty_dev); 793 free(console); 794 795 return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE; 796 } 797