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