aboutsummaryrefslogtreecommitdiff
path: root/hurd
diff options
context:
space:
mode:
Diffstat (limited to 'hurd')
-rw-r--r--hurd/Makefile2
-rw-r--r--hurd/hurd/fd.h16
-rw-r--r--hurd/hurdselect.c444
3 files changed, 460 insertions, 2 deletions
diff --git a/hurd/Makefile b/hurd/Makefile
index 250e441acf..07e22ebe1b 100644
--- a/hurd/Makefile
+++ b/hurd/Makefile
@@ -41,7 +41,7 @@ user-interfaces := $(addprefix hurd/,\
server-interfaces := hurd/msg faultexc
routines = hurdstartup hurdinit \
- hurdid hurdlookup hurdpid hurdrlimit hurdprio hurdexec \
+ hurdid hurdlookup hurdpid hurdrlimit hurdprio hurdexec hurdselect \
get-host set-host \
path-lookup \
setauth \
diff --git a/hurd/hurd/fd.h b/hurd/hurd/fd.h
index 10fd563089..aa48f21acf 100644
--- a/hurd/hurd/fd.h
+++ b/hurd/hurd/fd.h
@@ -1,5 +1,5 @@
/* File descriptors.
- Copyright (C) 1993, 1994, 1995, 1996, 1997 Free Software Foundation, Inc.
+ Copyright (C) 1993,94,95,96,97,98 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
@@ -223,4 +223,18 @@ extern error_t _hurd_ctty_input (io_t port, io_t ctty, error_t (*rpc) (io_t));
extern error_t _hurd_ctty_output (io_t port, io_t ctty, error_t (*rpc) (io_t));
+/* The guts of `select' and `poll'. Check the first NFDS descriptors
+ either in POLLFDS (if nonnull) or in each of READFDS, WRITEFDS,
+ EXCEPTFDS that is nonnull. If TIMEOUT is not NULL, time out after
+ waiting the interval specified therein. If SIGMASK is nonnull,
+ the set of blocked signals is temporarily set to that during this call.
+ Returns the number of ready descriptors, or -1 for errors. */
+struct pollfd;
+struct timespec;
+extern int _hurd_select (int nfds, struct pollfd *pollfds,
+ fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
+ const struct timespec *timeout,
+ const sigset_t *sigmask);
+
+
#endif /* hurd/fd.h */
diff --git a/hurd/hurdselect.c b/hurd/hurdselect.c
new file mode 100644
index 0000000000..e65def5912
--- /dev/null
+++ b/hurd/hurdselect.c
@@ -0,0 +1,444 @@
+/* Guts of both `select' and `poll' for Hurd.
+ Copyright (C) 1991,92,93,94,95,96,97,98 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., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#include <sys/types.h>
+#include <sys/poll.h>
+#include <hurd.h>
+#include <hurd/fd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <stdint.h>
+
+/* All user select types. */
+#define SELECT_ALL (SELECT_READ | SELECT_WRITE | SELECT_URG)
+
+/* Used to record that a particular select rpc returned. Must be distinct
+ from SELECT_ALL (which better not have the high bit set). */
+#define SELECT_RETURNED ((SELECT_ALL << 1) & ~SELECT_ALL)
+
+/* Check the first NFDS descriptors either in POLLFDS (if nonnnull) or in
+ each of READFDS, WRITEFDS, EXCEPTFDS that is nonnull. If TIMEOUT is not
+ NULL, time out after waiting the interval specified therein. Returns
+ the number of ready descriptors, or -1 for errors. */
+int
+_hurd_select (int nfds,
+ struct pollfd *pollfds,
+ fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
+ const struct timespec *timeout, const sigset_t *sigmask)
+{
+ int i;
+ mach_port_t portset;
+ int got;
+ error_t err;
+ fd_set rfds, wfds, xfds;
+ int firstfd, lastfd;
+ mach_msg_timeout_t to = (timeout != NULL ?
+ (timeout->tv_sec * 1000 +
+ timeout->tv_nsec / 1000000) :
+ 0);
+ struct
+ {
+ struct hurd_userlink ulink;
+ struct hurd_fd *cell;
+ mach_port_t io_port;
+ int type;
+ mach_port_t reply_port;
+ } d[nfds];
+ sigset_t oset;
+
+ if (sigmask && __sigprocmask (SIG_SETMASK, sigmask, &oset))
+ return -1;
+
+ if (pollfds)
+ {
+ /* Collect interesting descriptors from the user's `pollfd' array.
+ We do a first pass that reads the user's array before taking
+ any locks. The second pass then only touches our own stack,
+ and gets the port references. */
+
+ for (i = 0; i < nfds; ++i)
+ if (pollfds[i].fd >= 0)
+ {
+ int type = 0;
+ if (pollfds[i].events & POLLIN)
+ type |= SELECT_READ;
+ if (pollfds[i].events & POLLOUT)
+ type |= SELECT_WRITE;
+ if (pollfds[i].events & POLLPRI)
+ type |= SELECT_URG;
+
+ d[i].io_port = pollfds[i].fd;
+ d[i].type = type;
+ }
+ else
+ d[i].type = 0;
+
+ HURD_CRITICAL_BEGIN;
+ __mutex_lock (&_hurd_dtable_lock);
+
+ for (i = 0; i < nfds; ++i)
+ if (d[i].type != 0)
+ {
+ const int fd = (int) d[i].io_port;
+
+ if (fd < _hurd_dtablesize)
+ {
+ d[i].cell = _hurd_dtable[fd];
+ d[i].io_port = _hurd_port_get (&d[i].cell->port, &d[i].ulink);
+ if (d[i].io_port != MACH_PORT_NULL)
+ continue;
+ }
+
+ /* If one descriptor is bogus, we fail completely. */
+ while (i-- > 0)
+ if (d[i].type != 0)
+ _hurd_port_free (&d[i].cell->port,
+ &d[i].ulink, d[i].io_port);
+ break;
+ }
+
+ __mutex_unlock (&_hurd_dtable_lock);
+ HURD_CRITICAL_END;
+
+ if (i < nfds)
+ {
+ if (sigmask)
+ __sigprocmask (SIG_SETMASK, &oset, NULL);
+ errno = EBADF;
+ return -1;
+ }
+
+ lastfd = i - 1;
+ firstfd = i == 0 ? lastfd : 0;
+ }
+ else
+ {
+ /* Collect interested descriptors from the user's fd_set arguments.
+ 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. */
+ 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;
+ d[i].type = type;
+ if (type)
+ {
+ d[i].cell = _hurd_dtable[i];
+ d[i].io_port = _hurd_port_get (&d[i].cell->port, &d[i].ulink);
+ if (d[i].io_port == MACH_PORT_NULL)
+ {
+ /* If one descriptor is bogus, we fail completely. */
+ while (i-- > 0)
+ _hurd_port_free (&d[i].cell->port, &d[i].ulink,
+ d[i].io_port);
+ break;
+ }
+ lastfd = i;
+ if (firstfd == -1)
+ firstfd = i;
+ }
+ }
+
+ __mutex_unlock (&_hurd_dtable_lock);
+ HURD_CRITICAL_END;
+
+ if (i < nfds)
+ {
+ if (sigmask)
+ __sigprocmask (SIG_SETMASK, &oset, NULL);
+ errno = EBADF;
+ return -1;
+ }
+ }
+
+
+ err = 0;
+ got = 0;
+
+ /* Send them all io_select request messages. */
+
+ if (firstfd == -1)
+ /* But not if there were no ports to deal with at all.
+ We are just a pure timeout. */
+ portset = __mach_reply_port ();
+ else
+ {
+ portset = MACH_PORT_NULL;
+
+ for (i = firstfd; i <= lastfd; ++i)
+ if (d[i].type)
+ {
+ int type = d[i].type;
+ d[i].reply_port = __mach_reply_port ();
+ err = __io_select (d[i].io_port, d[i].reply_port,
+ /* Poll only if there's a single descriptor. */
+ (firstfd == lastfd) ? to : 0,
+ &type);
+ switch (err)
+ {
+ case MACH_RCV_TIMED_OUT:
+ /* No immediate response. This is normal. */
+ err = 0;
+ if (firstfd == lastfd)
+ /* When there's a single descriptor, we don't need a
+ portset, so just pretend we have one, but really
+ use the single reply port. */
+ portset = d[i].reply_port;
+ else if (got == 0)
+ /* We've got multiple reply ports, so we need a port set to
+ multiplex them. */
+ {
+ /* We will wait again for a reply later. */
+ if (portset == MACH_PORT_NULL)
+ /* Create the portset to receive all the replies on. */
+ err = __mach_port_allocate (__mach_task_self (),
+ MACH_PORT_RIGHT_PORT_SET,
+ &portset);
+ if (! err)
+ /* Put this reply port in the port set. */
+ __mach_port_move_member (__mach_task_self (),
+ d[i].reply_port, portset);
+ }
+ break;
+
+ default:
+ /* No other error should happen. Callers of select
+ don't expect to see errors, so we simulate
+ readiness of the erring object and the next call
+ hopefully will get the error again. */
+ type = SELECT_ALL;
+ /* FALLTHROUGH */
+
+ case 0:
+ /* We got an answer. */
+ if ((type & SELECT_ALL) == 0)
+ /* Bogus answer; treat like an error, as a fake positive. */
+ type = SELECT_ALL;
+
+ /* This port is already ready already. */
+ d[i].type &= type;
+ d[i].type |= SELECT_RETURNED;
+ ++got;
+ break;
+ }
+ _hurd_port_free (&d[i].cell->port, &d[i].ulink, d[i].io_port);
+ }
+ }
+
+ /* Now wait for reply messages. */
+ if (!err && got == 0)
+ {
+ /* 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;
+ } 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,
+ 0, sizeof msg, portset, 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_T, sizeof (MACH_MSG_TYPE_INTEGER_T) * 8,
+ 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 == EINTR &&
+ msg.head.msgh_size == sizeof msg.error)
+ {
+ /* EINTR response; poll for further responses
+ and then return quickly. */
+ err = EINTR;
+ goto poll;
+ }
+ if (msg.error.err ||
+ msg.head.msgh_size != sizeof msg.success ||
+ *(int *) &msg.success.result_type != *(int *) &inttype ||
+ (msg.success.result & SELECT_ALL) == 0)
+ {
+ /* Error or bogus reply. Simulate readiness. */
+ __mach_msg_destroy (&msg.head);
+ msg.success.result = SELECT_ALL;
+ }
+
+ /* Look up the respondent's reply port and record its
+ readiness. */
+ {
+ int had = got;
+ if (firstfd != -1)
+ for (i = firstfd; i <= lastfd; ++i)
+ if (d[i].type
+ && d[i].reply_port == msg.head.msgh_local_port)
+ {
+ d[i].type &= msg.success.result;
+ d[i].type |= SELECT_RETURNED;
+ ++got;
+ }
+ assert (got > had);
+ }
+ }
+
+ if (msg.head.msgh_remote_port != MACH_PORT_NULL)
+ __mach_port_deallocate (__mach_task_self (),
+ msg.head.msgh_remote_port);
+
+ if (got)
+ poll:
+ {
+ /* 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)
+ /* At least one descriptor is known to be ready now, so we will
+ return success. */
+ err = 0;
+ }
+
+ if (firstfd != -1)
+ for (i = firstfd; i <= lastfd; ++i)
+ if (d[i].type)
+ __mach_port_destroy (__mach_task_self (), d[i].reply_port);
+ if (firstfd == -1 || (firstfd != lastfd && portset != MACH_PORT_NULL))
+ /* Destroy PORTSET, but only if it's not actually the reply port for a
+ single descriptor (in which case it's destroyed in the previous loop;
+ not doing it here is just a bit more efficient). */
+ __mach_port_destroy (__mach_task_self (), portset);
+
+ if (err)
+ {
+ if (sigmask)
+ __sigprocmask (SIG_SETMASK, &oset, NULL);
+ return __hurd_fail (err);
+ }
+
+ if (pollfds)
+ /* Fill in the `revents' members of the user's array. */
+ for (i = 0; i < nfds; ++i)
+ {
+ const int type = d[i].type;
+ int_fast16_t revents = 0;
+
+ if (type & SELECT_READ)
+ revents |= POLLIN;
+ if (type & SELECT_WRITE)
+ revents |= POLLOUT;
+ if (type & SELECT_URG)
+ revents |= POLLPRI;
+
+ pollfds[i].revents = revents;
+ }
+ else
+ {
+ /* Below we recalculate GOT to include an increment for each operation
+ allowed on each fd. */
+ got = 0;
+
+ /* Set the user bitarrays. We only ever have to clear bits, as all
+ desired ones are initially set. */
+ if (firstfd != -1)
+ for (i = firstfd; i <= lastfd; ++i)
+ {
+ int type = d[i].type;
+
+ if ((type & SELECT_RETURNED) == 0)
+ type = 0;
+
+ if (type & SELECT_READ)
+ got++;
+ else if (readfds)
+ FD_CLR (i, readfds);
+ if (type & SELECT_WRITE)
+ got++;
+ else if (writefds)
+ FD_CLR (i, writefds);
+ if (type & SELECT_URG)
+ got++;
+ else if (exceptfds)
+ FD_CLR (i, exceptfds);
+ }
+ }
+
+ if (sigmask && __sigprocmask (SIG_SETMASK, &oset, NULL))
+ return -1;
+
+ return got;
+}