1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2012 NetApp, Inc. 5 * Copyright (c) 2013 Neel Natu <neel@freebsd.org> 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #include <sys/types.h> 31 #include <sys/socket.h> 32 33 #include <machine/vmm.h> 34 #include <machine/vmm_snapshot.h> 35 36 #include <netinet/in.h> 37 38 #include <arpa/inet.h> 39 #include <assert.h> 40 #include <capsicum_helpers.h> 41 #include <err.h> 42 #include <netdb.h> 43 #include <pthread.h> 44 #include <stdbool.h> 45 #include <stdlib.h> 46 #include <string.h> 47 #include <sysexits.h> 48 #include <termios.h> 49 #include <unistd.h> 50 51 #include "debug.h" 52 #include "mevent.h" 53 #include "uart_backend.h" 54 55 struct ttyfd { 56 bool opened; 57 bool is_socket; 58 int rfd; /* fd for reading */ 59 int wfd; /* fd for writing, may be == rfd */ 60 }; 61 62 #define FIFOSZ 16 63 64 struct fifo { 65 uint8_t buf[FIFOSZ]; 66 int rindex; /* index to read from */ 67 int windex; /* index to write to */ 68 int num; /* number of characters in the fifo */ 69 int size; /* size of the fifo */ 70 }; 71 72 struct uart_softc { 73 struct ttyfd tty; 74 struct fifo rxfifo; 75 struct mevent *mev; 76 pthread_mutex_t mtx; 77 }; 78 79 struct uart_socket_softc { 80 struct uart_softc *softc; 81 void (*drain)(int, enum ev_type, void *); 82 void *arg; 83 }; 84 85 static bool uart_stdio; /* stdio in use for i/o */ 86 static struct termios tio_stdio_orig; 87 88 static void uart_tcp_disconnect(struct uart_softc *); 89 90 static void 91 ttyclose(void) 92 { 93 tcsetattr(STDIN_FILENO, TCSANOW, &tio_stdio_orig); 94 } 95 96 static void 97 ttyopen(struct ttyfd *tf) 98 { 99 struct termios orig, new; 100 101 tcgetattr(tf->rfd, &orig); 102 new = orig; 103 cfmakeraw(&new); 104 new.c_cflag |= CLOCAL; 105 tcsetattr(tf->rfd, TCSANOW, &new); 106 if (uart_stdio) { 107 tio_stdio_orig = orig; 108 atexit(ttyclose); 109 } 110 raw_stdio = 1; 111 } 112 113 static int 114 ttyread(struct ttyfd *tf, uint8_t *ret) 115 { 116 uint8_t rb; 117 int len; 118 119 len = read(tf->rfd, &rb, 1); 120 if (ret && len == 1) 121 *ret = rb; 122 123 return (len); 124 } 125 126 static int 127 ttywrite(struct ttyfd *tf, unsigned char wb) 128 { 129 return (write(tf->wfd, &wb, 1)); 130 } 131 132 static bool 133 rxfifo_available(struct uart_softc *sc) 134 { 135 return (sc->rxfifo.num < sc->rxfifo.size); 136 } 137 138 int 139 uart_rxfifo_getchar(struct uart_softc *sc) 140 { 141 struct fifo *fifo; 142 int c, error, wasfull; 143 144 wasfull = 0; 145 fifo = &sc->rxfifo; 146 if (fifo->num > 0) { 147 if (!rxfifo_available(sc)) 148 wasfull = 1; 149 c = fifo->buf[fifo->rindex]; 150 fifo->rindex = (fifo->rindex + 1) % fifo->size; 151 fifo->num--; 152 if (wasfull) { 153 if (sc->tty.opened) { 154 error = mevent_enable(sc->mev); 155 assert(error == 0); 156 } 157 } 158 return (c); 159 } else 160 return (-1); 161 } 162 163 int 164 uart_rxfifo_numchars(struct uart_softc *sc) 165 { 166 return (sc->rxfifo.num); 167 } 168 169 static int 170 rxfifo_putchar(struct uart_softc *sc, uint8_t ch) 171 { 172 struct fifo *fifo; 173 int error; 174 175 fifo = &sc->rxfifo; 176 177 if (fifo->num < fifo->size) { 178 fifo->buf[fifo->windex] = ch; 179 fifo->windex = (fifo->windex + 1) % fifo->size; 180 fifo->num++; 181 if (!rxfifo_available(sc)) { 182 if (sc->tty.opened) { 183 /* 184 * Disable mevent callback if the FIFO is full. 185 */ 186 error = mevent_disable(sc->mev); 187 assert(error == 0); 188 } 189 } 190 return (0); 191 } else 192 return (-1); 193 } 194 195 void 196 uart_rxfifo_drain(struct uart_softc *sc, bool loopback) 197 { 198 uint8_t ch; 199 int len; 200 201 if (loopback) { 202 if (ttyread(&sc->tty, &ch) == 0 && sc->tty.is_socket) 203 uart_tcp_disconnect(sc); 204 } else { 205 while (rxfifo_available(sc)) { 206 len = ttyread(&sc->tty, &ch); 207 if (len <= 0) { 208 /* read returning 0 means disconnected. */ 209 if (len == 0 && sc->tty.is_socket) 210 uart_tcp_disconnect(sc); 211 break; 212 } 213 214 rxfifo_putchar(sc, ch); 215 } 216 } 217 } 218 219 int 220 uart_rxfifo_putchar(struct uart_softc *sc, uint8_t ch, bool loopback) 221 { 222 if (loopback) { 223 return (rxfifo_putchar(sc, ch)); 224 } else if (sc->tty.opened) { 225 /* write returning -1 means disconnected. */ 226 if (ttywrite(&sc->tty, ch) == -1 && sc->tty.is_socket) 227 uart_tcp_disconnect(sc); 228 return (0); 229 } else { 230 /* Drop on the floor. */ 231 return (0); 232 } 233 } 234 235 void 236 uart_rxfifo_reset(struct uart_softc *sc, int size) 237 { 238 char flushbuf[32]; 239 struct fifo *fifo; 240 ssize_t nread; 241 int error; 242 243 fifo = &sc->rxfifo; 244 bzero(fifo, sizeof(struct fifo)); 245 fifo->size = size; 246 247 if (sc->tty.opened) { 248 /* 249 * Flush any unread input from the tty buffer. 250 */ 251 while (1) { 252 nread = read(sc->tty.rfd, flushbuf, sizeof(flushbuf)); 253 if (nread != sizeof(flushbuf)) 254 break; 255 } 256 257 /* 258 * Enable mevent to trigger when new characters are available 259 * on the tty fd. 260 */ 261 error = mevent_enable(sc->mev); 262 assert(error == 0); 263 } 264 } 265 266 int 267 uart_rxfifo_size(struct uart_softc *sc __unused) 268 { 269 return (FIFOSZ); 270 } 271 272 #ifdef BHYVE_SNAPSHOT 273 int 274 uart_rxfifo_snapshot(struct uart_softc *sc, struct vm_snapshot_meta *meta) 275 { 276 int ret; 277 278 SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.rindex, meta, ret, done); 279 SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.windex, meta, ret, done); 280 SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.num, meta, ret, done); 281 SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.size, meta, ret, done); 282 SNAPSHOT_BUF_OR_LEAVE(sc->rxfifo.buf, sizeof(sc->rxfifo.buf), 283 meta, ret, done); 284 285 done: 286 return (ret); 287 } 288 #endif 289 290 /* 291 * Listen on the TCP port, wait for a connection, then accept it. 292 */ 293 static void 294 uart_tcp_listener(int fd, enum ev_type type __unused, void *arg) 295 { 296 static const char tcp_error_msg[] = "Socket already connected\n"; 297 struct uart_socket_softc *socket_softc = (struct uart_socket_softc *) 298 arg; 299 struct uart_softc *sc = socket_softc->softc; 300 int conn_fd; 301 302 conn_fd = accept(fd, NULL, NULL); 303 if (conn_fd == -1) 304 goto clean; 305 306 if (fcntl(conn_fd, F_SETFL, O_NONBLOCK) != 0) 307 goto clean; 308 309 pthread_mutex_lock(&sc->mtx); 310 311 if (sc->tty.opened) { 312 (void)send(conn_fd, tcp_error_msg, sizeof(tcp_error_msg), 0); 313 pthread_mutex_unlock(&sc->mtx); 314 goto clean; 315 } else { 316 sc->tty.rfd = sc->tty.wfd = conn_fd; 317 sc->tty.opened = true; 318 sc->mev = mevent_add(sc->tty.rfd, EVF_READ, socket_softc->drain, 319 socket_softc->arg); 320 } 321 322 pthread_mutex_unlock(&sc->mtx); 323 return; 324 325 clean: 326 if (conn_fd != -1) 327 close(conn_fd); 328 } 329 330 /* 331 * When a connection-oriented protocol disconnects, this handler is used to 332 * clean it up. 333 * 334 * Note that this function is a helper, so the caller is responsible for 335 * locking the softc. 336 */ 337 static void 338 uart_tcp_disconnect(struct uart_softc *sc) 339 { 340 mevent_delete_close(sc->mev); 341 sc->mev = NULL; 342 sc->tty.opened = false; 343 sc->tty.rfd = sc->tty.wfd = -1; 344 } 345 346 static int 347 uart_stdio_backend(struct uart_softc *sc) 348 { 349 #ifndef WITHOUT_CAPSICUM 350 cap_rights_t rights; 351 cap_ioctl_t cmds[] = { TIOCGETA, TIOCSETA, TIOCGWINSZ }; 352 #endif 353 354 if (uart_stdio) 355 return (-1); 356 357 sc->tty.rfd = STDIN_FILENO; 358 sc->tty.wfd = STDOUT_FILENO; 359 sc->tty.opened = true; 360 361 if (fcntl(sc->tty.rfd, F_SETFL, O_NONBLOCK) != 0) 362 return (-1); 363 if (fcntl(sc->tty.wfd, F_SETFL, O_NONBLOCK) != 0) 364 return (-1); 365 366 #ifndef WITHOUT_CAPSICUM 367 cap_rights_init(&rights, CAP_EVENT, CAP_IOCTL, CAP_READ); 368 if (caph_rights_limit(sc->tty.rfd, &rights) == -1) 369 errx(EX_OSERR, "Unable to apply rights for sandbox"); 370 if (caph_ioctls_limit(sc->tty.rfd, cmds, nitems(cmds)) == -1) 371 errx(EX_OSERR, "Unable to apply rights for sandbox"); 372 #endif 373 374 uart_stdio = true; 375 376 return (0); 377 } 378 379 static int 380 uart_tty_backend(struct uart_softc *sc, const char *path) 381 { 382 #ifndef WITHOUT_CAPSICUM 383 cap_rights_t rights; 384 cap_ioctl_t cmds[] = { TIOCGETA, TIOCSETA, TIOCGWINSZ }; 385 #endif 386 int fd; 387 388 fd = open(path, O_RDWR | O_NONBLOCK); 389 if (fd < 0) 390 return (-1); 391 392 if (!isatty(fd)) { 393 close(fd); 394 return (-1); 395 } 396 397 sc->tty.rfd = sc->tty.wfd = fd; 398 sc->tty.opened = true; 399 400 #ifndef WITHOUT_CAPSICUM 401 cap_rights_init(&rights, CAP_EVENT, CAP_IOCTL, CAP_READ, CAP_WRITE); 402 if (caph_rights_limit(fd, &rights) == -1) 403 errx(EX_OSERR, "Unable to apply rights for sandbox"); 404 if (caph_ioctls_limit(fd, cmds, nitems(cmds)) == -1) 405 errx(EX_OSERR, "Unable to apply rights for sandbox"); 406 #endif 407 408 return (0); 409 } 410 411 /* 412 * Listen on the address and add it to the kqueue. 413 * 414 * If a connection is established (e.g., the TCP handler is triggered), 415 * replace the handler with the connected handler. 416 */ 417 static int 418 uart_tcp_backend(struct uart_softc *sc, const char *path, 419 void (*drain)(int, enum ev_type, void *), void *arg) 420 { 421 #ifndef WITHOUT_CAPSICUM 422 cap_rights_t rights; 423 cap_ioctl_t cmds[] = { TIOCGETA, TIOCSETA, TIOCGWINSZ }; 424 #endif 425 int bind_fd = -1; 426 char addr[256], port[6]; 427 int domain; 428 struct addrinfo hints, *src_addr = NULL; 429 struct uart_socket_softc *socket_softc = NULL; 430 431 if (sscanf(path, "tcp=[%255[^]]]:%5s", addr, port) == 2) { 432 domain = AF_INET6; 433 } else if (sscanf(path, "tcp=%255[^:]:%5s", addr, port) == 2) { 434 domain = AF_INET; 435 } else { 436 warnx("Invalid number of parameter"); 437 goto clean; 438 } 439 440 bind_fd = socket(domain, SOCK_STREAM, 0); 441 if (bind_fd < 0) 442 goto clean; 443 444 memset(&hints, 0, sizeof(hints)); 445 hints.ai_family = domain; 446 hints.ai_socktype = SOCK_STREAM; 447 hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV | AI_PASSIVE; 448 449 if (getaddrinfo(addr, port, &hints, &src_addr) != 0) { 450 warnx("Invalid address %s:%s", addr, port); 451 goto clean; 452 } 453 454 if (bind(bind_fd, src_addr->ai_addr, src_addr->ai_addrlen) == -1) { 455 warn( 456 "bind(%s:%s)", 457 addr, port); 458 goto clean; 459 } 460 461 freeaddrinfo(src_addr); 462 src_addr = NULL; 463 464 if (fcntl(bind_fd, F_SETFL, O_NONBLOCK) == -1) 465 goto clean; 466 467 if (listen(bind_fd, 1) == -1) { 468 warnx("listen(%s:%s)", addr, port); 469 goto clean; 470 } 471 472 /* 473 * Set the connection softc structure, which includes both the softc 474 * and the drain function provided by the frontend. 475 */ 476 if ((socket_softc = calloc(1, sizeof(struct uart_socket_softc))) == 477 NULL) 478 goto clean; 479 480 sc->tty.is_socket = true; 481 482 socket_softc->softc = sc; 483 socket_softc->drain = drain; 484 socket_softc->arg = arg; 485 486 #ifndef WITHOUT_CAPSICUM 487 cap_rights_init(&rights, CAP_EVENT, CAP_ACCEPT, CAP_RECV, CAP_SEND, 488 CAP_FCNTL, CAP_IOCTL); 489 if (caph_rights_limit(bind_fd, &rights) == -1) 490 errx(EX_OSERR, "Unable to apply rights for sandbox"); 491 if (caph_ioctls_limit(bind_fd, cmds, nitems(cmds)) == -1) 492 errx(EX_OSERR, "Unable to apply ioctls for sandbox"); 493 if (caph_fcntls_limit(bind_fd, CAP_FCNTL_SETFL) == -1) 494 errx(EX_OSERR, "Unable to apply fcntls for sandbox"); 495 #endif 496 497 if ((sc->mev = mevent_add(bind_fd, EVF_READ, uart_tcp_listener, 498 socket_softc)) == NULL) 499 goto clean; 500 501 return (0); 502 503 clean: 504 if (bind_fd != -1) 505 close(bind_fd); 506 if (socket_softc != NULL) 507 free(socket_softc); 508 if (src_addr) 509 freeaddrinfo(src_addr); 510 return (-1); 511 } 512 513 struct uart_softc * 514 uart_init(void) 515 { 516 struct uart_softc *sc = calloc(1, sizeof(struct uart_softc)); 517 if (sc == NULL) 518 return (NULL); 519 520 pthread_mutex_init(&sc->mtx, NULL); 521 522 return (sc); 523 } 524 525 int 526 uart_tty_open(struct uart_softc *sc, const char *path, 527 void (*drain)(int, enum ev_type, void *), void *arg) 528 { 529 int retval; 530 531 if (strcmp("stdio", path) == 0) 532 retval = uart_stdio_backend(sc); 533 else if (strncmp("tcp", path, 3) == 0) 534 retval = uart_tcp_backend(sc, path, drain, arg); 535 else 536 retval = uart_tty_backend(sc, path); 537 538 /* 539 * A connection-oriented protocol should wait for a connection, 540 * so it may not listen to anything during initialization. 541 */ 542 if (retval == 0 && !sc->tty.is_socket) { 543 ttyopen(&sc->tty); 544 sc->mev = mevent_add(sc->tty.rfd, EVF_READ, drain, arg); 545 assert(sc->mev != NULL); 546 } 547 548 return (retval); 549 } 550 551 void 552 uart_softc_lock(struct uart_softc *sc) 553 { 554 pthread_mutex_lock(&sc->mtx); 555 } 556 557 void 558 uart_softc_unlock(struct uart_softc *sc) 559 { 560 pthread_mutex_unlock(&sc->mtx); 561 } 562