1/* 2 * Copyright (c) 2011, 2012, 2013, ETH Zurich. 3 * All rights reserved. 4 * 5 * This file is distributed under the terms in the attached LICENSE file. 6 * If you do not find this file, copies can be found by writing to: 7 * ETH Zurich D-INFK, CAB F.78, Universitaetstrasse 6, CH-8092 Zurich, 8 * Attn: Systems Group. 9 */ 10 11#include <barrelfish/barrelfish.h> 12#include <barrelfish/waitset.h> 13#include <barrelfish/deferred.h> 14#include <if/monitor_defs.h> 15#include <lwip/sys.h> 16#include <lwip/sockets.h> 17#include <lwip/sock_chan_support.h> 18#include <term/server/server.h> 19#include <vfs/fdtab.h> 20 21#include "posixcompat.h" 22#include "pty.h" 23#include "unixsock.h" 24 25#include <assert.h> 26#include <unistd.h> 27 28#define READ_SLOT 0 29#define WRITE_SLOT 1 30 31#if 0 32# define SELECT_DEBUG(x...) debug_printf("select(): " x) 33# define SELECT_DEBUG_ENABLED 34#else 35# define SELECT_DEBUG(x...) ((void)0) 36#endif 37 38/* Internal functions */ 39static int check_fds(int maxfdp1, fd_set *readfds, fd_set *writefds, 40 fd_set *exceptfds); 41 42static void debug_print_fdsets(int maxfdp1, fd_set *readfds, fd_set *writefds, 43 fd_set *exceptfds); 44 45static void debug_print_waitset(struct waitset *ws); 46 47static int pack_on_waitset(int maxfdp1, fd_set *readfds, fd_set *writefds, 48 fd_set *exceptfds, fd_set *changed_ws[], 49 struct waitset *ws_store[][maxfdp1], 50 struct waitset *ws); 51 52static void timeout_fired(void *arg); 53 54static int update_waitset(int maxfdp1, fd_set *readfds, fd_set *writefds, 55 fd_set *exceptfds, fd_set *changed_ws[], 56 struct waitset *ws_store[][maxfdp1], 57 struct waitset *ws); 58 59static inline void zero_fdsets(fd_set *readfds, fd_set *writefds, 60 fd_set *exceptfds); 61 62struct timeout_event { 63 bool fired; 64}; 65 66/** 67 * \brief select - synchronous I/O multiplexing 68 * 69 * In a nutshell, i.e. not considering corner cases, this functions checks if 70 * any of the fds associated with the readfds, writefds and exceptfds is ready 71 * for the corresponding operation. If this is not the case, we pack every 72 * event we are interessted in on a waitset and dispatch on this waitset. 73 * 74 * FIXME: The exception set is currently ignored. Find out what to map them to. 75 */ 76int select(int maxfdp1, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, 77 struct timeval *timeout) 78{ 79 errval_t err; 80 int ret; 81 82 SELECT_DEBUG("called with the following fdsets:\n"); 83 debug_print_fdsets(maxfdp1, readfds, writefds, exceptfds); 84 85 /* Waitset on which all events were are interessted in are packed. */ 86 struct waitset ws; 87 88 /* 89 * The monitor binding is needed in case we have to wait for a bind to 90 * complete. 91 */ 92 struct monitor_binding *mb = get_monitor_binding(); 93 94 /* Total number of file descriptors that are ready. */ 95 int retfds = 0; 96 97 /* Indicates whether or not the timout expired. */ 98 struct timeout_event toe = { 99 .fired = false 100 }; 101 struct deferred_event timeout_event; 102 103 /* 104 * FD sets keep track of file descriptors, for which we changed the waitset 105 * and therefore have to restore it before we return. 106 * 107 * FD_ISSET(changed_read_ws, maxfdp1) indicates whether or not we changed the 108 * monitor binding. 109 */ 110 fd_set changed_read_ws; 111 fd_set changed_write_ws; 112 fd_set *changed_ws[2]; 113 changed_ws[READ_SLOT] = &changed_read_ws; 114 changed_ws[WRITE_SLOT] = &changed_write_ws; 115 FD_ZERO(&changed_read_ws); 116 FD_ZERO(&changed_write_ws); 117 118 /* Backup of the waitsets that were changed. */ 119 struct waitset *ws_store[2][maxfdp1]; 120 121 /* check validity of maxfdp1 */ 122 if (maxfdp1 < MIN_FD || maxfdp1 > MAX_FD || maxfdp1 > FD_SETSIZE) { 123 errno = EINVAL; 124 return -1; 125 } 126 127 /* Initialize the waitset well use to block in the select */ 128 waitset_init(&ws); 129 130 /* 131 * If the user specified a timeout, setup a deferred event. Otherwise we 132 * block indefinitely until an event happens on the specified FDs. 133 * 134 * The POSIX standard states that if timeout.tv_sec = 0 and 135 * timeout.tv_usec = 0, select shall just poll. In this case we do not 136 * setup a deferred event but set the timeout_event.fired to true. This way 137 * we do not block at all but bail out as soon as we checked if any file 138 * descriptors are already ready. 139 */ 140 if (timeout != NULL) { 141 if (timeout->tv_sec == 0 && timeout->tv_usec == 0) { 142 toe.fired = true; 143 } else { 144 delayus_t delay = timeout->tv_sec * 1000000 + timeout->tv_usec; 145 deferred_event_init(&timeout_event); 146 err = deferred_event_register(&timeout_event, &ws, delay, 147 MKCLOSURE(timeout_fired, &toe)); 148 if (err_is_fail(err)) { 149 errno = EINVAL; 150 return -1; 151 } 152 } 153 } 154 155 retfds = check_fds(maxfdp1, readfds, writefds, exceptfds); 156 if (retfds < 0) { 157 /* error occured */ 158 return -1; 159 } else if (retfds > 0) { 160 /* some file descriptors are ready */ 161 goto finish_no_ws_changed; 162 } 163 164 /* retfds == 0 */ 165 166 if (toe.fired) { 167 /* 168 * No file descriptors are ready but timeout fired, zero all FD sets and 169 * return. 170 */ 171 zero_fdsets(readfds, writefds, exceptfds); 172 goto finish_no_ws_changed; 173 } 174 175 /* 176 * Since no file descriptors were ready, we pack all relevant events on our 177 * own waitset and dispatch on that waitset until some file descriptors 178 * are ready or a timeout occurs. 179 * Note that the occurence of an event on our waitset does not imply that at 180 * least one file descriptor is ready, we need to check this condition in 181 * a loop. 182 */ 183 184 ret = pack_on_waitset(maxfdp1, readfds, writefds, exceptfds, changed_ws, 185 ws_store, &ws); 186 if (ret < 0) { 187 return -1; 188 } 189 190 /* Loop until something we're waiting for has happened */ 191 while (true) { 192 SELECT_DEBUG("calling event_dispatch()\n"); 193 debug_print_waitset(&ws); 194 err = event_dispatch(&ws); 195 if (err_is_fail(err)) { 196 USER_PANIC_ERR(err, "Error in event_dispatch."); 197 } 198 SELECT_DEBUG("returned from event_dispatch()\n"); 199 200 retfds = check_fds(maxfdp1, readfds, writefds, exceptfds); 201 if (retfds < 0) { 202 /* error occured */ 203 return -1; 204 } else if (retfds > 0) { 205 /* some file descriptors are ready */ 206 goto finish; 207 } else { 208 ret = update_waitset(maxfdp1, readfds, writefds, exceptfds, 209 changed_ws, ws_store, &ws); 210 SELECT_DEBUG("update_waitset()\n"); 211 if (ret < 0) { 212 return -1; 213 } 214 } 215 216 /* retfds == 0 */ 217 if (toe.fired) { 218 /* 219 * No file descriptors are ready but timeout fired, zero all FD 220 * sets and return. 221 */ 222 zero_fdsets(readfds, writefds, exceptfds); 223 goto finish; 224 } 225 } 226 227finish: 228 /* If we waited on monitor, restore its old waitset */ 229 if (FD_ISSET(maxfdp1, changed_ws[READ_SLOT])) { 230 SELECT_DEBUG("restoring monitor ws\n"); 231 err = mb->change_waitset(mb, ws_store[READ_SLOT][maxfdp1]); 232 if (err_is_fail(err)) { 233 USER_PANIC_ERR(err, "monitor change_waitset"); 234 } 235 } 236 237 /* Restore all waitsets changed becase fd was in readset */ 238 for (int fd = 0; fd < maxfdp1; fd++) { 239 if (FD_ISSET(fd, changed_ws[READ_SLOT])) { 240 SELECT_DEBUG("fd %d: restore read waitset\n", fd); 241 struct fdtab_entry *e = fdtab_get(fd); 242 243 switch (e->type) { 244 case FDTAB_TYPE_AVAILABLE: 245 { 246 errno = EBADF; 247 return -1; 248 } 249 break; 250 251 case FDTAB_TYPE_LWIP_SOCKET: 252 { 253 lwip_mutex_lock(); 254 /* 255 * In case the socket did not yet became ready, the 256 * previously registered channel is still on the waitset. 257 * We deregister it so that waitset_destroy() completes 258 * without error. 259 */ 260 err = lwip_sock_waitset_deregister_read(e->fd); 261 if (err_is_fail(err) && 262 err_no(err) != LIB_ERR_CHAN_NOT_REGISTERED) { 263 USER_PANIC_ERR(err, "error deregister read channel for " 264 "lwip socket"); 265 } 266 lwip_mutex_unlock(); 267 } 268 break; 269 270 case FDTAB_TYPE_UNIX_SOCKET: 271 { 272 struct _unix_socket *us = e->handle; 273 err = us->u.active.binding->change_waitset 274 (us->u.active.binding, ws_store[READ_SLOT][fd]); 275 if (err_is_fail(err)) { 276 USER_PANIC_ERR(err, "change_waitset"); 277 } 278 } 279 break; 280 281 case FDTAB_TYPE_PTM: 282 { 283 struct _pty *pty = e->handle; 284 err = term_server_change_in_ws 285 (pty->ts, ws_store[READ_SLOT][fd]); 286 if (err_is_fail(err)) { 287 USER_PANIC_ERR(err, "change_waitset"); 288 } 289 } 290 break; 291 292 default: 293 fprintf(stderr, "Restore waitset on FD type %d NYI.\n", 294 e->type); 295 assert(!"NYI"); 296 errno = EBADF; 297 return -1; 298 } 299 } 300 } 301 302 /* Restore all waitsets changed becase fd was in writeset */ 303 for (int fd = 0; fd < maxfdp1; fd++) { 304 if (FD_ISSET(fd, changed_ws[WRITE_SLOT])) { 305 SELECT_DEBUG("fd %d: restore write waitset\n", fd); 306 struct fdtab_entry *e = fdtab_get(fd); 307 308 switch (e->type) { 309 case FDTAB_TYPE_AVAILABLE: 310 { 311 errno = EBADF; 312 return -1; 313 } 314 break; 315 316 case FDTAB_TYPE_LWIP_SOCKET: 317 { 318 lwip_mutex_lock(); 319 err = lwip_sock_waitset_deregister_write(e->fd); 320 if (err_is_fail(err) && 321 err_no(err) != LIB_ERR_CHAN_NOT_REGISTERED) { 322 USER_PANIC_ERR(err, "error deregister write channel for " 323 "lwip socket"); 324 } 325 lwip_mutex_unlock(); 326 } 327 break; 328 329 case FDTAB_TYPE_UNIX_SOCKET: 330 { 331 struct _unix_socket *us = e->handle; 332 err = us->u.active.binding->change_waitset 333 (us->u.active.binding, ws_store[WRITE_SLOT][fd]); 334 if (err_is_fail(err)) { 335 USER_PANIC_ERR(err, "change_waitset"); 336 } 337 } 338 break; 339 340 case FDTAB_TYPE_PTM: 341 { 342 struct _pty *pty = e->handle; 343 err = term_server_change_out_ws 344 (pty->ts, ws_store[WRITE_SLOT][fd]); 345 if (err_is_fail(err)) { 346 USER_PANIC_ERR(err, "change_waitset"); 347 } 348 } 349 break; 350 351 default: 352 fprintf(stderr, "Restore waitset on FD type %d NYI.\n", 353 e->type); 354 assert(!"NYI"); 355 errno = EBADF; 356 return -1; 357 } 358 } 359 } 360 361finish_no_ws_changed: 362 // Remove timeout from waitset if it was set 363 if(timeout != NULL && !toe.fired) { 364 deferred_event_cancel(&timeout_event); 365 } 366 367 err = waitset_destroy(&ws); 368 if (err_is_fail(err)) { 369 SELECT_DEBUG("Error destroying waitset.\n"); 370 debug_print_waitset(&ws); 371 USER_PANIC_ERR(err, "waitset_destroy"); 372 } 373 374 SELECT_DEBUG("returing %d fds:\n", retfds); 375 debug_print_fdsets(maxfdp1, readfds, writefds, exceptfds); 376 377 return retfds; 378} 379 380/*************************** internal functions *******************************/ 381 382/** 383 * \brief Check all FD sets if a file descriptor is ready without blocking. 384 * 385 * \param maxfdp1 Maximum file descriptor in all sets + 1. 386 * \param readfds The set of file descriptors to be checked for being ready 387 * to read. 388 * \param writefds The set of file descriptors to be checked for being ready 389 * to write. 390 * \param exceptfds The set of file descriptors to be checked for pending 391 * error conditions. 392 * 393 * \return The number of file descriptors in all sets being ready. 394 * If no file descriptor is ready, 0 is returned and the FD 395 * sets are left unchanged. Otherwise, the FD sets are 396 * modified and indicate which FDs are ready. 397 * If an error -1 is returned and errno is set, the FD sets 398 * are left unchanged. 399 */ 400static int check_fds(int maxfdp1, fd_set *readfds, fd_set *writefds, 401 fd_set *exceptfds) 402{ 403 int retfds = 0; 404 fd_set oreadfds, owritefds, oexceptfds; 405 FD_ZERO(&oreadfds); 406 FD_ZERO(&owritefds); 407 FD_ZERO(&oexceptfds); 408 409 /* Check list of readfds for events */ 410 if (readfds != NULL) { 411 for (int fd = 0; fd < maxfdp1; fd++) { 412 if (FD_ISSET(fd, readfds)) { 413 SELECT_DEBUG("fd %d: check for read readiness\n", fd); 414 struct fdtab_entry *e = fdtab_get(fd); 415 416 switch (e->type) { 417 case FDTAB_TYPE_AVAILABLE: 418 { 419 errno = EBADF; 420 return -1; 421 } 422 break; 423 424 case FDTAB_TYPE_LWIP_SOCKET: 425 { 426 lwip_mutex_lock(); 427 if (lwip_sock_ready_read(e->fd)) { 428 SELECT_DEBUG("fd %d: read ready\n", fd); 429 FD_SET(fd, &oreadfds); 430 retfds++; 431 } 432 lwip_mutex_unlock(); 433 } 434 break; 435 436 case FDTAB_TYPE_UNIX_SOCKET: 437 { 438 struct _unix_socket *us = e->handle; 439 440 if (us->passive) { /* passive side */ 441 /* Check for pending connection requests. */ 442 for (int i = 0; i < us->u.passive.max_backlog; i++) 443 { 444 if (us->u.passive.backlog[i] != NULL) { 445 SELECT_DEBUG("fd %d: read ready\n", fd); 446 FD_SET(fd, &oreadfds); 447 retfds++; 448 break; 449 } 450 } 451 } else { /* active side */ 452 /* Check for incoming data. */ 453 if (us->recv_buf_valid > 0) { 454 SELECT_DEBUG("fd %d: read ready\n", fd); 455 FD_SET(fd, &oreadfds); 456 retfds++; 457 } 458 } 459 } 460 break; 461 462 case FDTAB_TYPE_PTM: 463 { 464 struct _pty *pty = e->handle; 465 466 /* check if data is available for read */ 467 if (pty->mreadbuf_start != NULL) { 468 SELECT_DEBUG("fd %d: read ready\n", fd); 469 FD_SET(fd, &oreadfds); 470 retfds++; 471 } 472 } 473 break; 474 475 default: 476 { 477 fprintf(stderr, "select() on FD type %d NYI.\n", 478 e->type); 479 assert(!"NYI"); 480 errno = EBADF; 481 return -1; 482 } 483 } 484 } 485 } 486 } 487 488 /* Check list of writefds for events */ 489 if (writefds != NULL) { 490 for (int fd = 0; fd < maxfdp1; fd++) { 491 if (FD_ISSET(fd, writefds)) { 492 SELECT_DEBUG("fd %d: check for write readiness\n", fd); 493 struct fdtab_entry *e = fdtab_get(fd); 494 495 switch (e->type) { 496 case FDTAB_TYPE_AVAILABLE: 497 { 498 errno = EBADF; 499 return -1; 500 } 501 break; 502 503 case FDTAB_TYPE_LWIP_SOCKET: 504 { 505 lwip_mutex_lock(); 506 if (lwip_sock_ready_write(e->fd)) { 507 SELECT_DEBUG("fd %d: write ready\n", fd); 508 FD_SET(fd, &owritefds); 509 retfds++; 510 } 511 lwip_mutex_unlock(); 512 } 513 break; 514 515 case FDTAB_TYPE_UNIX_SOCKET: 516 { 517 struct _unix_socket *us = e->handle; 518 assert(!us->passive); 519 520 switch (us->u.active.mode) { 521 case _UNIX_SOCKET_MODE_CONNECTING: 522 break; 523 524 case _UNIX_SOCKET_MODE_CONNECTED: 525 if (us->send_buf == NULL) { 526 SELECT_DEBUG("fd %d: write ready\n", fd); 527 FD_SET(fd, &owritefds); 528 retfds++; 529 } 530 break; 531 } 532 } 533 break; 534 535 case FDTAB_TYPE_PTM: 536 { 537 struct _pty *pty = e->handle; 538 539 /* check if we can write to the pty master side */ 540 if (!term_server_sending(pty->ts)) { 541 SELECT_DEBUG("fd %d: write ready\n", fd); 542 FD_SET(fd, &owritefds); 543 retfds++; 544 } 545 } 546 break; 547 548 default: 549 { 550 fprintf(stderr, "select() on FD type %d NYI.\n", 551 e->type); 552 assert(!"NYI"); 553 errno = EBADF; 554 return -1; 555 } 556 } 557 } 558 } 559 } 560 561 /* Update FD sets if at least one FD is ready */ 562 if (retfds > 0) { 563 if (readfds != NULL) { 564 memcpy(readfds, &oreadfds, sizeof(fd_set)); 565 } 566 if (writefds != NULL) { 567 memcpy(writefds, &owritefds, sizeof(fd_set)); 568 } 569 if (exceptfds != NULL) { 570 memcpy(exceptfds, &oexceptfds, sizeof(fd_set)); 571 } 572 } 573 574 return retfds; 575} 576 577/** 578 * \brief Pack all relevant channels on the waitset 'ws'. 579 * 580 * \param maxfdp1 Maximum file descriptor in all sets +1. 581 * \param readfds read FD set 582 * \param writefds write FD set 583 * \param exceptfds exceptions FD set 584 * \param changed_ws Indicates for which file descriptors the waitset was 585 * changed. Filled-in by function. 586 * \param ws_store Backup of the old waitsets for file descriptors for which 587 * function changed waitset. Filled-in by function. 588 * \param ws Waitset on which to pack channels. 589 * 590 * \return -1 if error, 0 if successful 591 */ 592static int pack_on_waitset(int maxfdp1, fd_set *readfds, fd_set *writefds, 593 fd_set *exceptfds, fd_set *changed_ws[], 594 struct waitset *ws_store[][maxfdp1], 595 struct waitset *ws) 596{ 597 errval_t err; 598 struct monitor_binding *mb = get_monitor_binding(); 599 struct waitset *monitor_ws = mb->waitset; 600 601 FD_ZERO(changed_ws[READ_SLOT]); 602 FD_ZERO(changed_ws[WRITE_SLOT]); 603 604 /* go through readfds and change waitsets */ 605 if (readfds != NULL) { 606 for (int fd = 0; fd < maxfdp1; fd++) { 607 if (FD_ISSET(fd, readfds)) { 608 struct fdtab_entry *e = fdtab_get(fd); 609 610 switch (e->type) { 611 case FDTAB_TYPE_AVAILABLE: 612 { 613 errno = EBADF; 614 return -1; 615 } 616 break; 617 618 case FDTAB_TYPE_LWIP_SOCKET: 619 { 620 int retval; 621 622 lwip_mutex_lock(); 623 retval = lwip_sock_waitset_register_read(e->fd, ws); 624 assert(retval == 0); 625 lwip_mutex_unlock(); 626 FD_SET(fd, changed_ws[READ_SLOT]); 627 } 628 break; 629 630 case FDTAB_TYPE_UNIX_SOCKET: 631 { 632 struct _unix_socket *us = e->handle; 633 634 if (us->passive) { /* passive side */ 635 int i; 636 637 /* Check for pending connection requests. */ 638 for (i = 0; i < us->u.passive.max_backlog; i++) 639 { 640 if (us->u.passive.backlog[i] != NULL) { 641 break; 642 } 643 } 644 645 /* 646 * If there are not pending connection request 647 * wait on monitor binding. 648 */ 649 if (i == us->u.passive.max_backlog) { 650 /* wait on monitor */ 651 FD_SET(maxfdp1, changed_ws[READ_SLOT]); 652 } 653 } else { /* active side */ 654 /* Check for incoming data */ 655 if (us->recv_buf_valid <= 0) { 656 /* backup waitset */ 657 FD_SET(fd, changed_ws[READ_SLOT]); 658 ws_store[READ_SLOT][fd] = 659 us->u.active.binding->waitset; 660 661 /* change waitset */ 662 err = us->u.active.binding->change_waitset 663 (us->u.active.binding, ws); 664 if (err_is_fail(err)) { 665 USER_PANIC_ERR(err, "change waitset"); 666 } 667 } 668 } 669 } 670 break; 671 672 case FDTAB_TYPE_PTM: 673 { 674 struct _pty *pty = e->handle; 675 676 if (!pty->connected) { 677 /* wait on monitor */ 678 FD_SET(maxfdp1, changed_ws[READ_SLOT]); 679 } else if (pty->mreadbuf_start == NULL) { 680 /* check if data is available for read */ 681 SELECT_DEBUG("fd %d: change in ws\n", fd); 682 683 /* backup waitset */ 684 FD_SET(fd, changed_ws[READ_SLOT]); 685 ws_store[READ_SLOT][fd] = &pty->in_ws; 686 687 /* change waitset */ 688 err = term_server_change_in_ws(pty->ts, ws); 689 if (err_is_fail(err)) { 690 USER_PANIC_ERR(err, "change waitset"); 691 } 692 693 /* Also wait on monitor to allow new pseudo-terminal 694 slaves to connect. */ 695 FD_SET(maxfdp1, changed_ws[READ_SLOT]); 696 } 697 } 698 break; 699 700 default: 701 { 702 fprintf(stderr, "change waitset on FD type %d NYI.\n", 703 e->type); 704 assert(!"NYI"); 705 errno = EBADF; 706 return -1; 707 } 708 } 709 } 710 } 711 } 712 713 /* go through writefds and change waitsets */ 714 if (writefds != NULL) { 715 for (int fd = 0; fd < maxfdp1; fd++) { 716 if (FD_ISSET(fd, writefds)) { 717 struct fdtab_entry *e = fdtab_get(fd); 718 719 switch (e->type) { 720 case FDTAB_TYPE_AVAILABLE: 721 { 722 errno = EBADF; 723 return -1; 724 } 725 break; 726 727 case FDTAB_TYPE_LWIP_SOCKET: 728 { 729 int retval; 730 731 lwip_mutex_lock(); 732 retval = lwip_sock_waitset_register_write(e->fd, ws); 733 assert(retval == 0); 734 lwip_mutex_unlock(); 735 FD_SET(fd, changed_ws[WRITE_SLOT]); 736 } 737 break; 738 739 case FDTAB_TYPE_UNIX_SOCKET: 740 { 741 struct _unix_socket *us = e->handle; 742 assert(!us->passive); 743 744 switch (us->u.active.mode) { 745 case _UNIX_SOCKET_MODE_CONNECTING: 746 /* wait on monitor */ 747 FD_SET(maxfdp1, changed_ws[READ_SLOT]); 748 break; 749 750 case _UNIX_SOCKET_MODE_CONNECTED: 751 if (us->send_buf != NULL) { 752 /* 753 * Only change waitset if we did not already 754 * change it because the fd also was in the 755 * read fdset. 756 */ 757 if (!FD_ISSET(fd, changed_ws[READ_SLOT])) { 758 /* backup waitset */ 759 FD_SET(fd, changed_ws[WRITE_SLOT]); 760 ws_store[WRITE_SLOT][fd] = 761 us->u.active.binding->waitset; 762 763 /* change binding */ 764 err = us->u.active.binding->change_waitset 765 (us->u.active.binding, ws); 766 if (err_is_fail(err)) { 767 USER_PANIC_ERR(err, "change_waitset"); 768 } 769 } 770 } 771 break; 772 } 773 } 774 break; 775 776 case FDTAB_TYPE_PTM: 777 { 778 struct _pty *pty = e->handle; 779 780 if (!pty->connected) { 781 /* wait on monitor */ 782 FD_SET(maxfdp1, changed_ws[READ_SLOT]); 783 } else if (term_server_sending(pty->ts)) { 784 /* check if we can write to the pty master side */ 785 SELECT_DEBUG("fd %d: change out ws\n", fd); 786 787 /* backup waitset */ 788 FD_SET(fd, changed_ws[WRITE_SLOT]); 789 ws_store[WRITE_SLOT][fd] = &pty->out_ws; 790 791 /* change waitset */ 792 err = term_server_change_out_ws(pty->ts, ws); 793 if (err_is_fail(err)) { 794 USER_PANIC_ERR(err, "change_waitset"); 795 } 796 797 /* Also wait on monitor to allow new pseudo-terminal 798 slaves to connect. */ 799 FD_SET(maxfdp1, changed_ws[READ_SLOT]); 800 } 801 } 802 break; 803 804 default: 805 { 806 fprintf(stderr, "change waitset on FD type %d NYI.\n", 807 e->type); 808 assert(!"NYI"); 809 errno = EBADF; 810 return -1; 811 } 812 } 813 } 814 } 815 } 816 817 /* Change monitor binding if we wait on the monitor. */ 818 if (FD_ISSET(maxfdp1, changed_ws[READ_SLOT])) { 819 SELECT_DEBUG("changed monitor ws\n"); 820 ws_store[READ_SLOT][maxfdp1] = monitor_ws; 821 err = mb->change_waitset(mb, ws); 822 if (err_is_fail(err)) { 823 USER_PANIC_ERR(err, "change_waitset"); 824 } 825 } 826 827 return 0; 828} 829 830/** 831 * \brief Helper function called when deferred event happens. 832 */ 833static void timeout_fired(void *arg) 834{ 835 struct timeout_event *toe = arg; 836 assert(toe != NULL); 837 toe->fired = true; 838} 839 840 841/** 842 * \brief Possibly update the internal waitset that select dispatches on. 843 * 844 * This is necessary for example if a pseudo-terminal master file descriptor is 845 * in the read FD set but no slave has yet connected. In this particular case we 846 * pack (in the function pack_on_waitset()) the monitor binding onto selects 847 * internal waitset but since the binding for the slave side is not yet available 848 * we can't put it on the internal waitset as well. After a slave connected we 849 * need to update selects internal waitset to include the binding of the slave or 850 * select blocks indefinetely. 851 * 852 * \param maxfdp1 Maximum file descriptor in all sets +1. 853 * \param readfds read FD set 854 * \param writefds write FD set 855 * \param exceptfds exceptions FD set 856 * \param changed_ws Indicates for which file descriptors the waitset was 857 * changed. Updated by function. 858 * \param ws_store Backup of the old waitsets for file descriptors for which 859 * function changed waitset. Updated by function. 860 * \param ws Waitset on which to pack channels. 861 * 862 * \return -1 if error, 0 if successful 863 */ 864static int update_waitset(int maxfdp1, fd_set *readfds, fd_set *writefds, 865 fd_set *exceptfds, fd_set *changed_ws[], 866 struct waitset *ws_store[][maxfdp1], 867 struct waitset *ws) 868{ 869 errval_t err; 870 871 /* go through readfds and update waitsets if necessary. */ 872 if (readfds != NULL) { 873 for (int fd = 0; fd < maxfdp1; fd++) { 874 if (FD_ISSET(fd, readfds)) { 875 struct fdtab_entry *e = fdtab_get(fd); 876 877 switch (e->type) { 878 case FDTAB_TYPE_AVAILABLE: 879 { 880 errno = EBADF; 881 return -1; 882 } 883 break; 884 885 case FDTAB_TYPE_LWIP_SOCKET: 886 /* No update necessary. */ 887 break; 888 889 case FDTAB_TYPE_UNIX_SOCKET: 890 /* TODO: is update of internal ws necessary? */ 891 break; 892 893 case FDTAB_TYPE_PTM: 894 { 895 struct _pty *pty = e->handle; 896 897 /* If we're now connected but weren't before. */ 898 if (pty->connected && 899 !FD_ISSET(fd, changed_ws[READ_SLOT])) { 900 SELECT_DEBUG("fd %d: added in ws", fd); 901 902 /* backup waitset */ 903 FD_SET(fd, changed_ws[READ_SLOT]); 904 ws_store[READ_SLOT][fd] = &pty->in_ws; 905 906 /* change waitset */ 907 err = term_server_change_in_ws(pty->ts, ws); 908 if (err_is_fail(err)) { 909 USER_PANIC_ERR(err, "change waitset"); 910 } 911 } 912 break; 913 914 default: 915 { 916 fprintf(stderr, 917 "update waitset on FD type %d NYI.\n", 918 e->type); 919 assert(!"NYI"); 920 errno = EBADF; 921 return -1; 922 } 923 } 924 } 925 } 926 } 927 } 928 929 /* go through writefds and update waitsets if necessary. */ 930 if (writefds != NULL) { 931 for (int fd = 0; fd < maxfdp1; fd++) { 932 if (FD_ISSET(fd, writefds)) { 933 struct fdtab_entry *e = fdtab_get(fd); 934 935 switch (e->type) { 936 case FDTAB_TYPE_AVAILABLE: 937 { 938 errno = EBADF; 939 return -1; 940 } 941 break; 942 943 case FDTAB_TYPE_LWIP_SOCKET: 944 /* No update necessary. */ 945 break; 946 947 case FDTAB_TYPE_UNIX_SOCKET: 948 /* TODO: is update of internal ws necessary? */ 949 break; 950 951 case FDTAB_TYPE_PTM: 952 { 953 struct _pty *pty = e->handle; 954 955 /* If we're now connected but weren't before. */ 956 if (pty->connected && 957 !FD_ISSET(fd, changed_ws[WRITE_SLOT])) { 958 SELECT_DEBUG("fd %d: added out ws", fd); 959 960 /* backup waitset */ 961 FD_SET(fd, changed_ws[WRITE_SLOT]); 962 ws_store[WRITE_SLOT][fd] = &pty->out_ws; 963 964 /* change waitset */ 965 err = term_server_change_out_ws(pty->ts, ws); 966 if (err_is_fail(err)) { 967 USER_PANIC_ERR(err, "change_waitset"); 968 } 969 970 } 971 break; 972 973 default: 974 { 975 fprintf(stderr, 976 "update waitset on FD type %d NYI.\n", 977 e->type); 978 assert(!"NYI"); 979 errno = EBADF; 980 return -1; 981 } 982 } 983 } 984 } 985 } 986 } 987 988 return 0; 989} 990 991/** 992 * \brief Zero passed waitsets. 993 */ 994static inline void zero_fdsets(fd_set *readfds, fd_set *writefds, 995 fd_set *exceptfds) 996{ 997 if (readfds != NULL) { 998 FD_ZERO(readfds); 999 } 1000 if (writefds != NULL) { 1001 FD_ZERO(writefds); 1002 } 1003 if (exceptfds != NULL) { 1004 FD_ZERO(exceptfds); 1005 } 1006} 1007 1008/*************************** debugging functions *******************************/ 1009 1010#if defined(SELECT_DEBUG_ENABLED) 1011static void debug_fdset_to_string(char *str, size_t size, int maxfdp1, 1012 fd_set *fdset) 1013{ 1014 assert(str != NULL); 1015 size_t strlength = 0; 1016 1017 if (size < 1) { 1018 return; 1019 } 1020 str[0] = '\0'; 1021 if (fdset == NULL) { 1022 strncat(str, "(null)", size - 1); 1023 return; 1024 } 1025 1026 strncat(str, "{ ", size - 1); 1027 strlength += 2; 1028 1029 for (int fd = 0; fd < maxfdp1; fd++) { 1030 int prt = 0; 1031 if (FD_ISSET(fd, fdset)) { 1032 struct fdtab_entry *e = fdtab_get(fd); 1033 switch (e->type) { 1034 case FDTAB_TYPE_AVAILABLE: 1035 prt = snprintf(str + strlength, size - strlength, 1036 "%d (avail), ", fd); 1037 strlength += prt; 1038 break; 1039 1040 case FDTAB_TYPE_LWIP_SOCKET: 1041 prt = snprintf(str + strlength, size - strlength, "%d (lwip), ", 1042 fd); 1043 strlength += prt; 1044 break; 1045 1046 case FDTAB_TYPE_UNIX_SOCKET: 1047 prt = snprintf(str + strlength, size - strlength, 1048 "%d (unix sock), ", fd); 1049 strlength += prt; 1050 break; 1051 1052 case FDTAB_TYPE_PTM: 1053 prt = snprintf(str + strlength, size - strlength, "%d (ptm), ", 1054 fd); 1055 strlength += prt; 1056 break; 1057 1058 default: 1059 prt = snprintf(str + strlength, size - strlength, 1060 "%d (unknown), ", fd); 1061 strlength += prt; 1062 break; 1063 1064 } 1065 } 1066 } 1067 1068 if (str[strlength - 2] == ',') { 1069 str[strlength - 2] = ' '; 1070 str[strlength - 1] = '}'; 1071 } else { 1072 strncat(str, " }", size - 1); 1073 strlength += 2; 1074 } 1075} 1076#else /* SELECT_DEBUG_ENABLED */ 1077static inline void debug_fdset_to_string(char *str, size_t size, int maxfdp1, 1078 fd_set *fdset) 1079{ 1080} 1081#endif /* SELECT_DEBUG_ENABLED */ 1082 1083#if defined(SELECT_DEBUG_ENABLED) 1084#define PRINT_BUFSZ 128 1085static void debug_print_fdsets(int maxfdp1, fd_set *readfds, fd_set *writefds, 1086 fd_set *exceptfds) 1087{ 1088 char rbuffer[PRINT_BUFSZ]; 1089 char wbuffer[PRINT_BUFSZ]; 1090 char ebuffer[PRINT_BUFSZ]; 1091 debug_fdset_to_string(rbuffer, PRINT_BUFSZ, maxfdp1, readfds); 1092 debug_fdset_to_string(wbuffer, PRINT_BUFSZ, maxfdp1, writefds); 1093 debug_fdset_to_string(ebuffer, PRINT_BUFSZ, maxfdp1, exceptfds); 1094 SELECT_DEBUG("readfds %s | writefds %s | exceptfds %s\n", rbuffer, wbuffer, 1095 ebuffer); 1096} 1097#else /* defined(SELECT_DEBUG_ENABLED) */ 1098static inline void debug_print_fdsets(int maxfdp1, fd_set *readfds, 1099 fd_set *writefds, fd_set *exceptfds) 1100{ 1101} 1102#endif /* defined(SELECT_DEBUG_ENABLED) */ 1103 1104#if defined(SELECT_DEBUG_ENABLED) 1105static void debug_print_chanstate(struct waitset_chanstate *start) 1106{ 1107 struct waitset_chanstate *i = start; 1108 do { 1109 switch (i->chantype) { 1110 case CHANTYPE_LMP_IN: 1111 case CHANTYPE_LMP_OUT: 1112 SELECT_DEBUG("LMP\n"); 1113 break; 1114 1115 case CHANTYPE_UMP_IN: 1116 SELECT_DEBUG("UMP\n"); 1117 break; 1118 1119 case CHANTYPE_DEFERRED: 1120 SELECT_DEBUG("deferred\n"); 1121 break; 1122 1123 case CHANTYPE_LWIP_SOCKET: 1124 SELECT_DEBUG("lwip\n"); 1125 break; 1126 1127 default: 1128 SELECT_DEBUG("other\n"); 1129 } 1130 i = i->next; 1131 } while (i != start && i != NULL); 1132} 1133#else /* defined(SELECT_DEBUG_ENABLED) */ 1134static inline void debug_print_chanstate(struct waitset_chanstate *start) 1135{ 1136} 1137#endif /* defined(SELECT_DEBUG_ENABLED) */ 1138 1139#if defined(SELECT_DEBUG_ENABLED) 1140static void debug_print_waitset(struct waitset *ws) 1141{ 1142 if (ws->pending != NULL) { 1143 SELECT_DEBUG("Ws contains the follwoing channels on the pending " 1144 "queue:\n"); 1145 debug_print_chanstate(ws->pending); 1146 } 1147 1148 if (ws->polled != NULL) { 1149 SELECT_DEBUG("Ws contains the following channels on the polled " 1150 "queue:\n"); 1151 debug_print_chanstate(ws->polled); 1152 } 1153 1154 if (ws->idle != NULL) { 1155 SELECT_DEBUG("Ws contains the following channels on the polled " 1156 "queue:\n"); 1157 debug_print_chanstate(ws->idle); 1158 } 1159 1160 if (ws->waiting_threads != NULL) { 1161 SELECT_DEBUG("Threads are blocked on this waitset.\n"); 1162 } 1163} 1164#else /* defined(SELECT_DEBUG_ENABLED) */ 1165static inline void debug_print_waitset(struct waitset *ws) 1166{ 1167} 1168#endif /* defined(SELECT_DEBUG_ENABLED) */ 1169