summary refs log tree commit diff
path: root/sysdeps/mach/hurd/select.c
diff options
context:
space:
mode:
Diffstat (limited to 'sysdeps/mach/hurd/select.c')
-rw-r--r--sysdeps/mach/hurd/select.c275
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)