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 #define _GNU_SOURCE 20 21 #include <assert.h> 22 #include <errno.h> 23 #include <signal.h> 24 #include <stdint.h> 25 #include <stdbool.h> 26 #include <stdlib.h> 27 #include <stdio.h> 28 #include <fcntl.h> 29 #include <unistd.h> 30 #include <err.h> 31 #include <string.h> 32 #include <getopt.h> 33 #include <limits.h> 34 #include <termios.h> 35 36 #include <sys/types.h> 37 #include <sys/poll.h> 38 39 #include "console-server.h" 40 41 struct console { 42 const char *tty_kname; 43 char *tty_sysfs_devnode; 44 char *tty_dev; 45 int tty_sirq; 46 int tty_lpc_addr; 47 int tty_fd; 48 49 struct handler **handlers; 50 int n_handlers; 51 52 struct poller **pollers; 53 int n_pollers; 54 55 struct pollfd *pollfds; 56 }; 57 58 struct poller { 59 struct handler *handler; 60 void *data; 61 poller_fn_t fn; 62 bool remove; 63 }; 64 65 /* we have one extra entry in the pollfds array for the VUART tty */ 66 static const int n_internal_pollfds = 1; 67 68 /* state shared with the signal handler */ 69 static bool sigint; 70 71 static void usage(const char *progname) 72 { 73 fprintf(stderr, 74 "usage: %s [options] <DEVICE>\n" 75 "\n" 76 "Options:\n" 77 " --config <FILE> Use FILE for configuration\n" 78 "", 79 progname); 80 } 81 82 /* populates tty_dev and tty_sysfs_devnode, using the tty kernel name */ 83 static int tty_find_device(struct console *console) 84 { 85 char *tty_class_device_link; 86 char *tty_device_tty_dir; 87 char *tty_device_reldir; 88 int rc; 89 90 tty_class_device_link = NULL; 91 tty_device_tty_dir = NULL; 92 tty_device_reldir = NULL; 93 94 rc = asprintf(&tty_class_device_link, 95 "/sys/class/tty/%s", console->tty_kname); 96 if (rc < 0) 97 return -1; 98 99 tty_device_tty_dir = realpath(tty_class_device_link, NULL); 100 if (rc < 0) { 101 warn("Can't query sysfs for device %s", console->tty_kname); 102 goto out_free; 103 } 104 105 rc = asprintf(&tty_device_reldir, "%s/../../", tty_device_tty_dir); 106 if (rc < 0) 107 goto out_free; 108 109 console->tty_sysfs_devnode = realpath(tty_device_reldir, NULL); 110 if (!console->tty_sysfs_devnode) 111 warn("Can't find parent device for %s", console->tty_kname); 112 113 114 /* todo: lookup from major/minor info in sysfs, in case udev has 115 * renamed us */ 116 rc = asprintf(&console->tty_dev, "/dev/%s", console->tty_kname); 117 if (rc < 0) 118 goto out_free; 119 120 rc = 0; 121 122 out_free: 123 free(tty_class_device_link); 124 free(tty_device_tty_dir); 125 free(tty_device_reldir); 126 return rc; 127 } 128 129 static int tty_set_sysfs_attr(struct console *console, const char *name, 130 int value) 131 { 132 char *path; 133 FILE *fp; 134 int rc; 135 136 rc = asprintf(&path, "%s/%s", console->tty_sysfs_devnode, name); 137 if (rc < 0) 138 return -1; 139 140 fp = fopen(path, "w"); 141 if (!fp) { 142 warn("Can't access attribute %s on device %s", 143 name, console->tty_kname); 144 rc = -1; 145 goto out_free; 146 } 147 setvbuf(fp, NULL, _IONBF, 0); 148 149 rc = fprintf(fp, "0x%x", value); 150 if (rc < 0) 151 warn("Error writing to %s attribute of device %s", 152 name, console->tty_kname); 153 fclose(fp); 154 155 156 157 out_free: 158 free(path); 159 return rc; 160 } 161 162 /** 163 * Set console to raw mode: we don't want any processing to occur on 164 * the underlying terminal input/output. 165 */ 166 static void tty_init_termios(struct console *console) 167 { 168 struct termios termios; 169 int rc; 170 171 rc = tcgetattr(console->tty_fd, &termios); 172 if (rc) { 173 warn("Can't read tty termios"); 174 return; 175 } 176 177 cfmakeraw(&termios); 178 rc = tcsetattr(console->tty_fd, TCSANOW, &termios); 179 if (rc) 180 warn("Can't set terminal raw mode for tty"); 181 } 182 183 /** 184 * Open and initialise the serial device 185 */ 186 static int tty_init_io(struct console *console) 187 { 188 if (console->tty_sirq) 189 tty_set_sysfs_attr(console, "sirq", console->tty_sirq); 190 if (console->tty_lpc_addr) 191 tty_set_sysfs_attr(console, "lpc_address", 192 console->tty_lpc_addr); 193 194 console->tty_fd = open(console->tty_dev, O_RDWR); 195 if (console->tty_fd <= 0) { 196 warn("Can't open tty %s", console->tty_dev); 197 return -1; 198 } 199 200 /* Disable character delay. We may want to later enable this when 201 * we detect larger amounts of data 202 */ 203 fcntl(console->tty_fd, F_SETFL, FNDELAY); 204 205 tty_init_termios(console); 206 207 console->pollfds[console->n_pollers].fd = console->tty_fd; 208 console->pollfds[console->n_pollers].events = POLLIN; 209 210 return 0; 211 } 212 213 static int tty_init(struct console *console, struct config *config) 214 { 215 const char *val; 216 char *endp; 217 int rc; 218 219 val = config_get_value(config, "lpc-address"); 220 if (val) { 221 console->tty_lpc_addr = strtoul(val, &endp, 0); 222 if (endp == optarg) { 223 warn("Invalid LPC address: '%s'", val); 224 return -1; 225 } 226 } 227 228 val = config_get_value(config, "sirq"); 229 if (val) { 230 console->tty_sirq = strtoul(val, &endp, 0); 231 if (endp == optarg) 232 warn("Invalid sirq: '%s'", val); 233 } 234 235 if (!console->tty_kname) { 236 warnx("Error: No TTY device specified"); 237 return -1; 238 } 239 240 rc = tty_find_device(console); 241 if (rc) 242 return rc; 243 244 rc = tty_init_io(console); 245 return rc; 246 } 247 248 249 int console_data_out(struct console *console, const uint8_t *data, size_t len) 250 { 251 return write_buf_to_fd(console->tty_fd, data, len); 252 } 253 254 static void handlers_init(struct console *console, struct config *config) 255 { 256 extern struct handler *__start_handlers, *__stop_handlers; 257 struct handler *handler; 258 int i, rc; 259 260 console->n_handlers = &__stop_handlers - &__start_handlers; 261 console->handlers = &__start_handlers; 262 263 printf("%d handler%s\n", console->n_handlers, 264 console->n_handlers == 1 ? "" : "s"); 265 266 for (i = 0; i < console->n_handlers; i++) { 267 handler = console->handlers[i]; 268 269 rc = 0; 270 if (handler->init) 271 rc = handler->init(handler, console, config); 272 273 handler->active = rc == 0; 274 275 printf(" %s [%sactive]\n", handler->name, 276 handler->active ? "" : "in"); 277 } 278 } 279 280 static void handlers_fini(struct console *console) 281 { 282 struct handler *handler; 283 int i; 284 285 for (i = 0; i < console->n_handlers; i++) { 286 handler = console->handlers[i]; 287 if (handler->fini && handler->active) 288 handler->fini(handler); 289 } 290 } 291 292 static int handlers_data_in(struct console *console, uint8_t *buf, size_t len) 293 { 294 struct handler *handler; 295 int i, rc, tmp; 296 297 rc = 0; 298 299 for (i = 0; i < console->n_handlers; i++) { 300 handler = console->handlers[i]; 301 302 if (!handler->active) 303 continue; 304 305 if (!handler->data_in) 306 continue; 307 308 tmp = handler->data_in(handler, buf, len); 309 if (tmp == HANDLER_EXIT) 310 rc = 1; 311 } 312 313 return rc; 314 315 } 316 317 struct poller *console_register_poller(struct console *console, 318 struct handler *handler, poller_fn_t poller_fn, 319 int fd, int events, void *data) 320 { 321 struct poller *poller; 322 int n; 323 324 poller = malloc(sizeof(*poller)); 325 poller->remove = false; 326 poller->handler = handler; 327 poller->fn = poller_fn; 328 poller->data = data; 329 330 /* add one to our pollers array */ 331 n = console->n_pollers++; 332 console->pollers = realloc(console->pollers, 333 sizeof(*console->pollers) * console->n_pollers); 334 335 console->pollers[n] = poller; 336 337 /* increase pollfds array too */ 338 console->pollfds = realloc(console->pollfds, 339 sizeof(*console->pollfds) * 340 (n_internal_pollfds + console->n_pollers)); 341 342 /* shift the end pollfds up by one */ 343 memcpy(&console->pollfds[n+n_internal_pollfds], 344 &console->pollfds[n], 345 sizeof(*console->pollfds) * n_internal_pollfds); 346 347 console->pollfds[n].fd = fd; 348 console->pollfds[n].events = events; 349 350 return poller; 351 } 352 353 void console_unregister_poller(struct console *console, 354 struct poller *poller) 355 { 356 int i; 357 358 /* find the entry in our pollers array */ 359 for (i = 0; i < console->n_pollers; i++) 360 if (console->pollers[i] == poller) 361 break; 362 363 assert(i < console->n_pollers); 364 365 console->n_pollers--; 366 367 /* remove the item from the pollers array... */ 368 memmove(&console->pollers[i], &console->pollers[i+1], 369 sizeof(*console->pollers) 370 * (console->n_pollers - i)); 371 372 console->pollers = realloc(console->pollers, 373 sizeof(*console->pollers) * console->n_pollers); 374 375 /* ... and the pollfds array */ 376 memmove(&console->pollfds[i], &console->pollfds[i+1], 377 sizeof(*console->pollfds) * 378 (n_internal_pollfds + console->n_pollers - i)); 379 380 console->pollfds = realloc(console->pollfds, 381 sizeof(*console->pollfds) * 382 (n_internal_pollfds + console->n_pollers)); 383 384 385 free(poller); 386 } 387 388 static int call_pollers(struct console *console) 389 { 390 struct poller *poller; 391 struct pollfd *pollfd; 392 enum poller_ret prc; 393 int i, rc; 394 395 rc = 0; 396 397 /* 398 * Process poll events by iterating through the pollers and pollfds 399 * in-step, calling any pollers that we've found revents for. 400 */ 401 for (i = 0; i < console->n_pollers; i++) { 402 poller = console->pollers[i]; 403 pollfd = &console->pollfds[i]; 404 405 if (!pollfd->revents) 406 continue; 407 408 prc = poller->fn(poller->handler, pollfd->revents, 409 poller->data); 410 if (prc == POLLER_EXIT) 411 rc = -1; 412 else if (prc == POLLER_REMOVE) 413 poller->remove = true; 414 } 415 416 /** 417 * Process deferred removals; restarting each time we unregister, as 418 * the array will have changed 419 */ 420 for (;;) { 421 bool removed = false; 422 423 for (i = 0; i < console->n_pollers; i++) { 424 poller = console->pollers[i]; 425 if (poller->remove) { 426 console_unregister_poller(console, poller); 427 removed = true; 428 break; 429 } 430 } 431 if (!removed) 432 break; 433 } 434 435 return rc; 436 } 437 438 static void sighandler(int signal) 439 { 440 if (signal == SIGINT) 441 sigint = true; 442 } 443 444 int run_console(struct console *console) 445 { 446 sighandler_t sighandler_save; 447 int rc; 448 449 sighandler_save = signal(SIGINT, sighandler); 450 451 rc = 0; 452 453 for (;;) { 454 uint8_t buf[4096]; 455 456 if (sigint) { 457 fprintf(stderr, "Received interrupt, exiting\n"); 458 break; 459 } 460 461 rc = poll(console->pollfds, 462 console->n_pollers + n_internal_pollfds, -1); 463 if (rc < 0) { 464 if (errno == EINTR) { 465 continue; 466 } else { 467 warn("poll error"); 468 break; 469 } 470 } 471 472 /* process internal fd first */ 473 BUILD_ASSERT(n_internal_pollfds == 1); 474 475 if (console->pollfds[console->n_pollers].revents) { 476 rc = read(console->tty_fd, buf, sizeof(buf)); 477 if (rc <= 0) { 478 warn("Error reading from tty device"); 479 rc = -1; 480 break; 481 } 482 rc = handlers_data_in(console, buf, rc); 483 if (rc) 484 break; 485 } 486 487 /* ... and then the pollers */ 488 rc = call_pollers(console); 489 if (rc) 490 break; 491 } 492 493 signal(SIGINT, sighandler_save); 494 495 return rc ? -1 : 0; 496 } 497 static const struct option options[] = { 498 { "config", required_argument, 0, 'c'}, 499 { 0, 0, 0, 0}, 500 }; 501 502 int main(int argc, char **argv) 503 { 504 const char *config_filename = NULL; 505 const char *config_tty_kname = NULL; 506 struct console *console; 507 struct config *config; 508 int rc; 509 510 rc = -1; 511 512 for (;;) { 513 int c, idx; 514 515 c = getopt_long(argc, argv, "c:", options, &idx); 516 if (c == -1) 517 break; 518 519 switch (c) { 520 case 'c': 521 config_filename = optarg; 522 break; 523 case 'h': 524 case '?': 525 usage(argv[0]); 526 return EXIT_SUCCESS; 527 } 528 } 529 530 if (optind >= argc) { 531 warnx("Required argument <DEVICE> missing"); 532 usage(argv[0]); 533 return EXIT_FAILURE; 534 } 535 536 config_tty_kname = argv[optind]; 537 538 console = malloc(sizeof(struct console)); 539 memset(console, 0, sizeof(*console)); 540 console->pollfds = calloc(n_internal_pollfds, 541 sizeof(*console->pollfds)); 542 543 config = config_init(config_filename); 544 if (!config) { 545 warnx("Can't read configuration, exiting."); 546 goto out_free; 547 } 548 549 console->tty_kname = config_tty_kname; 550 551 rc = tty_init(console, config); 552 if (rc) 553 goto out_config_fini; 554 555 handlers_init(console, config); 556 557 rc = run_console(console); 558 559 handlers_fini(console); 560 561 out_config_fini: 562 config_fini(config); 563 564 out_free: 565 free(console->pollers); 566 free(console->pollfds); 567 free(console->tty_sysfs_devnode); 568 free(console->tty_dev); 569 free(console); 570 571 return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE; 572 } 573