58818a78d9a0629e084b775a881947391691aaff
[kopensolaris-gnu/glibc.git] / sysdeps / mach / hurd / select.c
1 /* Copyright (C) 1991, 1992, 1993, 1994, 1995, 1996 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public License as
6 published by the Free Software Foundation; either version 2 of the
7 License, or (at your option) any later version.
8
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public
15 License along with the GNU C Library; see the file COPYING.LIB.  If
16 not, write to the Free Software Foundation, Inc., 675 Mass Ave,
17 Cambridge, MA 02139, USA.  */
18
19 #include <ansidecl.h>
20 #include <sys/types.h>
21 #include <hurd.h>
22 #include <hurd/fd.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 /* All user select types.  */
27 #define SELECT_ALL (SELECT_READ | SELECT_WRITE | SELECT_URG)
28
29 /* Used to record that a particular select rpc returned. Must be distinct
30    from SELECT_ALL (which better not have the high bit set).  */
31 #define SELECT_RETURNED ((SELECT_ALL << 1) & ~SELECT_ALL)
32
33 /* Check the first NFDS descriptors each in READFDS (if not NULL) for read
34    readiness, in WRITEFDS (if not NULL) for write readiness, and in EXCEPTFDS
35    (if not NULL) for exceptional conditions.  If TIMEOUT is not NULL, time out
36    after waiting the interval specified therein.  Returns the number of ready
37    descriptors, or -1 for errors.  */
38 int
39 DEFUN(__select, (nfds, readfds, writefds, exceptfds, timeout),
40       int nfds AND fd_set *readfds AND fd_set *writefds AND
41       fd_set *exceptfds AND struct timeval *timeout)
42 {
43   int i;
44   mach_port_t port;
45   int got;
46   int *types;
47   struct hurd_userlink *ulink;
48   mach_port_t *ports;
49   struct hurd_fd **cells;
50   error_t err;
51   fd_set rfds, wfds, xfds;
52   int firstfd, lastfd;
53   mach_msg_timeout_t to = (timeout != NULL ?
54                            (timeout->tv_sec * 1000 +
55                             timeout->tv_usec / 1000) :
56                            0);
57
58   /* Use local copies so we can't crash from user bogosity.  */
59   if (readfds == NULL)
60     FD_ZERO (&rfds);
61   else
62     rfds = *readfds;
63   if (writefds == NULL)
64     FD_ZERO (&wfds);
65   else
66     wfds = *writefds;
67   if (exceptfds == NULL)
68     FD_ZERO (&xfds);
69   else
70     xfds = *exceptfds;
71
72   HURD_CRITICAL_BEGIN;
73   __mutex_lock (&_hurd_dtable_lock);
74
75   if (nfds > _hurd_dtablesize)
76     nfds = _hurd_dtablesize;
77
78   /* Collect the ports for interesting FDs.  */
79   cells = __alloca (nfds * sizeof (*cells));
80   ports = __alloca (nfds * sizeof (*ports));
81   types = __alloca (nfds * sizeof (*types));
82   ulink = __alloca (nfds * sizeof (*ulink));
83   firstfd = lastfd = -1;
84   for (i = 0; i < nfds; ++i)
85     {
86       int type = 0;
87       if (readfds != NULL && FD_ISSET (i, &rfds))
88         type |= SELECT_READ;
89       if (writefds != NULL && FD_ISSET (i, &wfds))
90         type |= SELECT_WRITE;
91       if (exceptfds != NULL && FD_ISSET (i, &xfds))
92         type |= SELECT_URG;
93       types[i] = type;
94       if (type)
95         {
96           cells[i] = _hurd_dtable[i];
97           ports[i] = _hurd_port_get (&cells[i]->port, &ulink[i]);
98           if (ports[i] == MACH_PORT_NULL)
99             {
100               /* If one descriptor is bogus, we fail completely.  */
101               while (i-- > 0)
102                 _hurd_port_free (&cells[i]->port, &ulink[i], ports[i]);
103               errno = EBADF;
104               break;
105             }
106           lastfd = i;
107           if (firstfd == -1)
108             firstfd = i;
109         }
110     }
111
112   __mutex_unlock (&_hurd_dtable_lock);
113   HURD_CRITICAL_END;
114
115   if (i < nfds)
116     return -1;
117
118   /* Get a port to receive the io_select_reply messages on.  */
119   port = __mach_reply_port ();
120
121   /* Send them all io_select request messages.  */
122   got = 0;
123   err = 0;
124   for (i = firstfd; i <= lastfd; ++i)
125     if (types[i])
126       {
127         if (!err)
128           {
129             int tag = i;
130             int type = types[i];
131             err = __io_select (ports[i], port,
132                                /* Poll for each but the last.  */
133                                (i == lastfd && got == 0) ? to : 0,
134                                &type, &tag);
135             switch (err)
136               {
137               case MACH_RCV_TIMED_OUT:
138                 /* No immediate response.  This is normal.  */
139                 err = 0;
140                 break;
141
142               case 0:
143                 /* We got an answer.  This is not necessarily the answer to
144                    the query we sent just now.  It may correspond to any
145                    prior query which timed out before its answer arrived.  */
146                 if (tag < 0 || tag > i || (type & SELECT_ALL) == 0)
147                   /* This is not a proper answer to any query we have yet
148                      made.  */
149                   err = EGRATUITOUS;
150                 else
151                   {
152                     /* Some port is ready.  TAG tells us which.  */
153                     types[tag] &= type;
154                     types[tag] |= SELECT_RETURNED;
155                     ++got;
156                   }
157                 break;
158
159               default:
160                 /* Any other error kills us.
161                    But we must continue to loop to free the ports.  */
162                 break;
163               }
164           }
165         _hurd_port_free (&cells[i]->port, &ulink[i], ports[i]);
166       }
167
168   /* Now wait for reply messages.  */
169   if (!err && got == 0 && port != MACH_PORT_NULL)
170     {
171       /* Now wait for io_select_reply messages on PORT,
172          timing out as appropriate.  */
173
174       union
175         {
176           mach_msg_header_t head;
177           struct
178             {
179               mach_msg_header_t head;
180               mach_msg_type_t err_type;
181               error_t err;
182             } error;
183           struct
184             {
185               mach_msg_header_t head;
186               mach_msg_type_t err_type;
187               error_t err;
188               mach_msg_type_t result_type;
189               int result;
190               mach_msg_type_t tag_type;
191               int tag;
192             } success;
193         } msg;
194       mach_msg_option_t options = (timeout == NULL ? 0 : MACH_RCV_TIMEOUT);
195       error_t msgerr;
196       while ((msgerr = __mach_msg (&msg.head,
197                                    MACH_RCV_MSG | options,
198                                    0, sizeof msg, port, to,
199                                    MACH_PORT_NULL)) == MACH_MSG_SUCCESS)
200         {
201           /* We got a message.  Decode it.  */
202 #define IO_SELECT_REPLY_MSGID (21012 + 100) /* XXX */
203           const mach_msg_type_t inttype =
204             { MACH_MSG_TYPE_INTEGER_32, 32, 1, 1, 0, 0 };
205           if (msg.head.msgh_id == IO_SELECT_REPLY_MSGID &&
206               msg.head.msgh_size >= sizeof msg.error &&
207               !(msg.head.msgh_bits & MACH_MSGH_BITS_COMPLEX) &&
208               *(int *) &msg.error.err_type == *(int *) &inttype)
209             {
210               /* This is a properly formatted message so far.
211                  See if it is a success or a failure.  */
212               if (msg.error.err)
213                 {
214                   err = msg.error.err;
215                   if (msg.head.msgh_size != sizeof msg.error)
216                     __mach_msg_destroy (&msg);
217                 }
218               else if (msg.head.msgh_size != sizeof msg.success ||
219                        *(int *) &msg.success.tag_type != *(int *) &inttype ||
220                        *(int *) &msg.success.result_type != *(int *) &inttype)
221                 __mach_msg_destroy (&msg);
222               else if ((msg.success.result & SELECT_ALL) == 0 ||
223                        msg.success.tag < firstfd || msg.success.tag > lastfd)
224                 err = EGRATUITOUS;
225               else
226                 {
227                   /* This is a winning io_select_reply message!
228                      Record the readiness it indicates and send a reply.  */
229                   types[msg.success.tag] &= msg.success.result;
230                   types[msg.success.tag] |= SELECT_RETURNED;
231                   ++got;
232                 }
233             }
234
235           if (msg.head.msgh_remote_port != MACH_PORT_NULL)
236             __mach_port_deallocate (__mach_task_self (),
237                                     msg.head.msgh_remote_port);
238
239           if (got || err == EINTR)
240             {
241               /* Poll for another message.  */
242               to = 0;
243               options |= MACH_RCV_TIMEOUT;
244             }
245         }
246
247     if (err == MACH_RCV_TIMED_OUT)
248       /* This is the normal value for ERR.  We might have timed out and
249          read no messages.  Otherwise, after receiving the first message,
250          we poll for more messages.  We receive with a timeout of 0 to
251          effect a poll, so ERR is MACH_RCV_TIMED_OUT when the poll finds no
252          message waiting.  */
253       err = 0;
254
255       if (got && err == EINTR)
256         /* Some calls were interrupted, but at least one descriptor
257            is known to be ready now, so we will return success.  */
258         err = 0;
259     }
260
261   if (port != MACH_PORT_NULL)
262     /* We must destroy the port if we made some select requests
263        that might send notification on that port after we no longer care.
264        If the port were reused, that notification could confuse the next
265        select call to use the port.  The notification might be valid,
266        but the descriptor may have changed to a different server.  */
267     __mach_port_destroy (__mach_task_self (), port);
268
269   if (timeout && got == 0 && err == MACH_RCV_TIMED_OUT)
270     /* No io_select call returned success immediately, and the last call
271        blocked for our full timeout period and then timed out.  So the
272        multiplex times out too.  */
273     return 0;
274
275   if (err)
276     return __hurd_fail (err);
277
278   /* Below we recalculate GOT to include an increment for each operation
279      allowed on each fd.  */
280   got = 0;
281
282   /* Set the user bitarrays.  We only ever have to clear bits, as all desired
283      ones are initially set.  */
284   for (i = 0; i < nfds; ++i)
285     {
286       int type = types[i];
287
288       if ((type & SELECT_RETURNED) == 0)
289         type = 0;
290
291       if (readfds != NULL && (type & SELECT_READ) == 0)
292         FD_CLR (i, readfds);
293       else
294         got++;
295       if (writefds != NULL && (type & SELECT_WRITE) == 0)
296         FD_CLR (i, writefds);
297       else
298         got++;
299       if (exceptfds != NULL && (type & SELECT_URG) == 0)
300         FD_CLR (i, exceptfds);
301       else
302         got++;
303     }
304
305   return got;
306 }
307
308 weak_alias (__select, select)