diff options
Diffstat (limited to 'sysdeps/mach/hurd/select.c')
-rw-r--r-- | sysdeps/mach/hurd/select.c | 275 |
1 files changed, 275 insertions, 0 deletions
diff --git a/sysdeps/mach/hurd/select.c b/sysdeps/mach/hurd/select.c new file mode 100644 index 0000000000..d1c5913cb8 --- /dev/null +++ b/sysdeps/mach/hurd/select.c @@ -0,0 +1,275 @@ +/* Copyright (C) 1991, 1992, 1993, 1994, 1995 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#include <ansidecl.h> +#include <sys/types.h> +#include <hurd.h> +#include <hurd/fd.h> +#include <stdlib.h> +#include <string.h> + + +/* Check the first NFDS descriptors each in READFDS (if not NULL) for read + readiness, in WRITEFDS (if not NULL) for write readiness, and in EXCEPTFDS + (if not NULL) for exceptional conditions. If TIMEOUT is not NULL, time out + after waiting the interval specified therein. Returns the number of ready + descriptors, or -1 for errors. */ +int +DEFUN(__select, (nfds, readfds, writefds, exceptfds, timeout), + int nfds AND fd_set *readfds AND fd_set *writefds AND + fd_set *exceptfds AND struct timeval *timeout) +{ + int i; + mach_port_t port; + int got; + int *types; + struct hurd_userlink *ulink; + mach_port_t *ports; + struct hurd_fd **cells; + error_t err; + fd_set rfds, wfds, xfds; + int firstfd, lastfd; + mach_msg_timeout_t to = (timeout != NULL ? + (timeout->tv_sec * 1000 + + timeout->tv_usec / 1000) : + 0); + + /* Use local copies so we can't crash from user bogosity. */ + if (readfds == NULL) + FD_ZERO (&rfds); + else + rfds = *readfds; + if (writefds == NULL) + FD_ZERO (&wfds); + else + wfds = *writefds; + if (exceptfds == NULL) + FD_ZERO (&xfds); + else + xfds = *exceptfds; + + HURD_CRITICAL_BEGIN; + __mutex_lock (&_hurd_dtable_lock); + + if (nfds > _hurd_dtablesize) + nfds = _hurd_dtablesize; + + /* Collect the ports for interesting FDs. */ + cells = __alloca (nfds * sizeof (*cells)); + ports = __alloca (nfds * sizeof (*ports)); + types = __alloca (nfds * sizeof (*types)); + ulink = __alloca (nfds * sizeof (*ulink)); + firstfd = lastfd = -1; + for (i = 0; i < nfds; ++i) + { + int type = 0; + if (readfds != NULL && FD_ISSET (i, &rfds)) + type |= SELECT_READ; + if (writefds != NULL && FD_ISSET (i, &wfds)) + type |= SELECT_WRITE; + if (exceptfds != NULL && FD_ISSET (i, &xfds)) + type |= SELECT_URG; + types[i] = type; + if (type) + { + cells[i] = _hurd_dtable[i]; + ports[i] = _hurd_port_get (&cells[i]->port, &ulink[i]); + if (ports[i] == MACH_PORT_NULL) + { + /* If one descriptor is bogus, we fail completely. */ + while (i-- > 0) + _hurd_port_free (&cells[i]->port, &ulink[i], ports[i]); + errno = EBADF; + break; + } + lastfd = i; + if (firstfd == -1) + firstfd = i; + } + } + + __mutex_unlock (&_hurd_dtable_lock); + HURD_CRITICAL_END; + + if (i < nfds) + return -1; + + /* Get a port to receive the io_select_reply messages on. */ + port = __mach_reply_port (); + + /* Send them all io_select request messages. */ + got = 0; + err = 0; + for (i = firstfd; i <= lastfd; ++i) + if (types[i]) + { + if (!err) + { + int tag = i; + err = __io_select (ports[i], port, + /* Poll for each but the last. */ + (i == lastfd && got == 0) ? to : 0, + &types[i], &tag); + if (!err) + { + if (tag != i) + err = EGRATUITOUS; + else if (types[i] & (SELECT_READ|SELECT_URG|SELECT_WRITE)) + ++got; + } + } + _hurd_port_free (&cells[i]->port, &ulink[i], ports[i]); + } + + /* Now wait for reply messages. */ + if (!err && got == 0 && port != MACH_PORT_NULL) + { + /* Now wait for io_select_reply messages on PORT, + timing out as appropriate. */ + + union + { + mach_msg_header_t head; + struct + { + mach_msg_header_t head; + mach_msg_type_t err_type; + error_t err; + } error; + struct + { + mach_msg_header_t head; + mach_msg_type_t err_type; + error_t err; + mach_msg_type_t result_type; + int result; + mach_msg_type_t tag_type; + int tag; + } success; + } msg; + mach_msg_option_t options = (timeout == NULL ? 0 : MACH_RCV_TIMEOUT); + error_t msgerr; + while ((msgerr = __mach_msg (&msg.head, + MACH_RCV_MSG | options, + sizeof msg, 0, port, to, + MACH_PORT_NULL)) == MACH_MSG_SUCCESS) + { + /* We got a message. Decode it. */ +#define IO_SELECT_REPLY_MSGID (21012 + 100) /* XXX */ + const mach_msg_type_t inttype = + { MACH_MSG_TYPE_INTEGER_32, 32, 1, 1, 0, 0 }; + if (msg.head.msgh_id == IO_SELECT_REPLY_MSGID && + msg.head.msgh_size >= sizeof msg.error && + !(msg.head.msgh_bits & MACH_MSGH_BITS_COMPLEX) && + *(int *) &msg.error.err_type == *(int *) &inttype) + { + /* This is a properly formatted message so far. + See if it is a success or a failure. */ + if (msg.error.err) + { + err = msg.error.err; + if (msg.head.msgh_size != sizeof msg.error) + __mach_msg_destroy (&msg); + } + else if (msg.head.msgh_size != sizeof msg.success || + *(int *) &msg.success.tag_type != *(int *) &inttype || + *(int *) &msg.success.result_type != *(int *) &inttype) + __mach_msg_destroy (&msg); + else if ((msg.success.result & + (SELECT_READ|SELECT_WRITE|SELECT_URG)) == 0 || + msg.success.tag < firstfd || msg.success.tag > lastfd) + err = EGRATUITOUS; + else + { + /* This is a winning io_select_reply message! + Record the readiness it indicates and send a reply. */ + if (types[msg.success.tag] == 0) + /* This descriptor is ready and it was not before, + so we increment our count of ready descriptors. */ + ++got; + types[msg.success.tag] |= msg.success.result; + } + } + + if (msg.head.msgh_remote_port != MACH_PORT_NULL) + __mach_port_deallocate (__mach_task_self (), + msg.head.msgh_remote_port); + + if (got || err == EINTR) + { + /* Poll for another message. */ + to = 0; + options |= MACH_RCV_TIMEOUT; + } + } + + if (err == MACH_RCV_TIMED_OUT) + /* This is the normal value for ERR. We might have timed out and + read no messages. Otherwise, after receiving the first message, + we poll for more messages. We receive with a timeout of 0 to + effect a poll, so ERR is MACH_RCV_TIMED_OUT when the poll finds no + message waiting. */ + err = 0; + + if (got && err == EINTR) + /* Some calls were interrupted, but at least one descriptor + is known to be ready now, so we will return success. */ + err = 0; + } + + if (port != MACH_PORT_NULL) + /* We must destroy the port if we made some select requests + that might send notification on that port after we no longer care. + If the port were reused, that notification could confuse the next + select call to use the port. The notification might be valid, + but the descriptor may have changed to a different server. */ + __mach_port_destroy (__mach_task_self (), port); + + if (timeout && got == 0 && err == MACH_RCV_TIMED_OUT) + /* No io_select call returned success immediately, and the last call + blocked for our full timeout period and then timed out. So the + multiplex times out too. */ + return 0; + + if (err) + return __hurd_fail (err); + + /* Set the user bitarrays. */ + for (i = 0; i < nfds; ++i) + { + if (readfds != NULL) + if (types[i] & SELECT_READ) + FD_SET (i, readfds); + else + FD_CLR (i, readfds); + if (writefds != NULL) + if (types[i] & SELECT_WRITE) + FD_SET (i, writefds); + else + FD_CLR (i, writefds); + if (exceptfds != NULL) + if (types[i] & SELECT_URG) + FD_SET (i, exceptfds); + else + FD_CLR (i, exceptfds); + } + + return got; +} + +weak_alias (__select, select) |