summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog19
-rw-r--r--hurd/Makefile2
-rw-r--r--hurd/hurd/fd.h16
-rw-r--r--hurd/hurdselect.c444
-rw-r--r--sysdeps/mach/hurd/pselect.c45
-rw-r--r--sysdeps/mach/hurd/select.c307
6 files changed, 531 insertions, 302 deletions
diff --git a/ChangeLog b/ChangeLog
index 70039a8cf3..6d84569448 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,20 @@
+1998-12-05  Roland McGrath  <roland@baalperazim.frob.com>
+
+	* sysdeps/mach/hurd/pselect.c: New file.
+
+	* sysdeps/mach/hurd/poll.c: New file.
+
+	* hurd/Makefile (routines): Add hurdselect.
+	* hurd/hurdselect.c: New file.
+	(_hurd_select): New function, guts taken from ...
+	* sysdeps/mach/hurd/select.c (__select): ... here.
+	Now work by just calling _hurd_select.
+	* hurd/hurd/fd.h: Declare _hurd_select.
+
+1998-12-05  Roland McGrath  <roland@baalperazim.frob.com>
+
+	* time/strptime.c: Fix unterminated comment in last change.
+
 1998-12-05  Ulrich Drepper  <drepper@cygnus.com>
 
 	* ctype/ctype.h (_ISbit): Protect use of parameter with
@@ -13,7 +30,7 @@
 
 1998-12-04  Ulrich Drepper  <drepper@cygnus.com>
 
-	* argp/argp.h: Add __retrict.
+	* argp/argp.h: Add __restrict.
 	* dirent/dirent.h: Likewise.
 	* elf/dlfcn.h: Likewise.
 	* grp/grp.h: Likewise.
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;
+}
diff --git a/sysdeps/mach/hurd/pselect.c b/sysdeps/mach/hurd/pselect.c
new file mode 100644
index 0000000000..6c0bc7b297
--- /dev/null
+++ b/sysdeps/mach/hurd/pselect.c
@@ -0,0 +1,45 @@
+/* pselect for Hurd.
+   Copyright (C) 1998 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 <errno.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <sys/select.h>
+#include <hurd.h>
+#include <hurd/fd.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.  Additionally set the sigmask
+   SIGMASK for this call.  Returns the number of ready descriptors, or -1 for
+   errors.  */
+int
+__pselect (nfds, readfds, writefds, exceptfds, timeout, sigmask)
+     int nfds;
+     fd_set *readfds;
+     fd_set *writefds;
+     fd_set *exceptfds;
+     const struct timespec *timeout;
+     const sigset_t *sigmask;
+{
+  return _hurd_select (nfds, NULL,
+		       readfds, writefds, exceptfds, timeout, sigmask);
+}
+weak_alias (__pselect, pselect)
diff --git a/sysdeps/mach/hurd/select.c b/sysdeps/mach/hurd/select.c
index 904e1419ef..e58b542d0a 100644
--- a/sysdeps/mach/hurd/select.c
+++ b/sysdeps/mach/hurd/select.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 1991, 92, 93, 94, 95, 96, 97 Free Software Foundation, Inc.
+/* 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
@@ -17,18 +17,9 @@
    Boston, MA 02111-1307, USA.  */
 
 #include <sys/types.h>
+#include <sys/time.h>
 #include <hurd.h>
 #include <hurd/fd.h>
-#include <stdlib.h>
-#include <string.h>
-#include <assert.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 each in READFDS (if not NULL) for read
    readiness, in WRITEFDS (if not NULL) for write readiness, and in EXCEPTFDS
@@ -43,299 +34,17 @@ __select (nfds, readfds, writefds, exceptfds, timeout)
      fd_set *exceptfds;
      struct timeval *timeout;
 {
-  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_usec / 1000) :
-			   0);
-  struct
-    {
-      struct hurd_userlink ulink;
-      struct hurd_fd *cell;
-      mach_port_t io_port;
-      int type;
-      mach_port_t reply_port;
-    } d[nfds];
-
-  /* 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;
+  struct timespec ts, *to;
 
-  /* Collect the ports for interesting FDs.  */
-  firstfd = lastfd = -1;
-  for (i = 0; i < nfds; ++i)
+  if (timeout)
     {
-      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);
-	      errno = EBADF;
-	      break;
-	    }
-	  lastfd = i;
-	  if (firstfd == -1)
-	    firstfd = i;
-	}
+      to = &ts;
+      TIMEVAL_TO_TIMESPEC (timeout, to);
     }
-
-  __mutex_unlock (&_hurd_dtable_lock);
-  HURD_CRITICAL_END;
-
-  if (i < nfds)
-    return -1;
-
-  /* Send them all io_select request messages.  */
-
-  if (firstfd == -1)
-    /* But not if there were no ports to deal with at all. */
-    portset = __mach_reply_port ();
   else
-    {
-      err = 0;
-      got = 0;
-      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)
-    return __hurd_fail (err);
-
-  /* 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);
-      }
+    to = NULL;
 
-  return got;
+  return _hurd_select (nfds, NULL, readfds, writefds, exceptfds, to, NULL);
 }
 
 weak_alias (__select, select)