904e1419efc20b5a56f1d7f633e6fbd1ddd4953b
[platform/upstream/glibc.git] / sysdeps / mach / hurd / select.c
1 /* Copyright (C) 1991, 92, 93, 94, 95, 96, 97 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 not,
16    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17    Boston, MA 02111-1307, USA.  */
18
19 #include <sys/types.h>
20 #include <hurd.h>
21 #include <hurd/fd.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <assert.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 __select (nfds, readfds, writefds, exceptfds, timeout)
40      int nfds;
41      fd_set *readfds;
42      fd_set *writefds;
43      fd_set *exceptfds;
44      struct timeval *timeout;
45 {
46   int i;
47   mach_port_t portset;
48   int got;
49   error_t err;
50   fd_set rfds, wfds, xfds;
51   int firstfd, lastfd;
52   mach_msg_timeout_t to = (timeout != NULL ?
53                            (timeout->tv_sec * 1000 +
54                             timeout->tv_usec / 1000) :
55                            0);
56   struct
57     {
58       struct hurd_userlink ulink;
59       struct hurd_fd *cell;
60       mach_port_t io_port;
61       int type;
62       mach_port_t reply_port;
63     } d[nfds];
64
65   /* Use local copies so we can't crash from user bogosity.  */
66   if (readfds == NULL)
67     FD_ZERO (&rfds);
68   else
69     rfds = *readfds;
70   if (writefds == NULL)
71     FD_ZERO (&wfds);
72   else
73     wfds = *writefds;
74   if (exceptfds == NULL)
75     FD_ZERO (&xfds);
76   else
77     xfds = *exceptfds;
78
79   HURD_CRITICAL_BEGIN;
80   __mutex_lock (&_hurd_dtable_lock);
81
82   if (nfds > _hurd_dtablesize)
83     nfds = _hurd_dtablesize;
84
85   /* Collect the ports for interesting FDs.  */
86   firstfd = lastfd = -1;
87   for (i = 0; i < nfds; ++i)
88     {
89       int type = 0;
90       if (readfds != NULL && FD_ISSET (i, &rfds))
91         type |= SELECT_READ;
92       if (writefds != NULL && FD_ISSET (i, &wfds))
93         type |= SELECT_WRITE;
94       if (exceptfds != NULL && FD_ISSET (i, &xfds))
95         type |= SELECT_URG;
96       d[i].type = type;
97       if (type)
98         {
99           d[i].cell = _hurd_dtable[i];
100           d[i].io_port = _hurd_port_get (&d[i].cell->port, &d[i].ulink);
101           if (d[i].io_port == MACH_PORT_NULL)
102             {
103               /* If one descriptor is bogus, we fail completely.  */
104               while (i-- > 0)
105                 _hurd_port_free (&d[i].cell->port, &d[i].ulink, d[i].io_port);
106               errno = EBADF;
107               break;
108             }
109           lastfd = i;
110           if (firstfd == -1)
111             firstfd = i;
112         }
113     }
114
115   __mutex_unlock (&_hurd_dtable_lock);
116   HURD_CRITICAL_END;
117
118   if (i < nfds)
119     return -1;
120
121   /* Send them all io_select request messages.  */
122
123   if (firstfd == -1)
124     /* But not if there were no ports to deal with at all. */
125     portset = __mach_reply_port ();
126   else
127     {
128       err = 0;
129       got = 0;
130       portset = MACH_PORT_NULL;
131
132       for (i = firstfd; i <= lastfd; ++i)
133         if (d[i].type)
134           {
135             int type = d[i].type;
136             d[i].reply_port = __mach_reply_port ();
137             err = __io_select (d[i].io_port, d[i].reply_port,
138                                /* Poll only if there's a single descriptor.  */
139                                (firstfd == lastfd) ? to : 0,
140                                &type);
141             switch (err)
142               {
143               case MACH_RCV_TIMED_OUT:
144                 /* No immediate response.  This is normal.  */
145                 err = 0;
146                 if (firstfd == lastfd)
147                   /* When there's a single descriptor, we don't need a
148                      portset, so just pretend we have one, but really
149                      use the single reply port.  */
150                   portset = d[i].reply_port;
151                 else if (got == 0)
152                   /* We've got multiple reply ports, so we need a port set to
153                      multiplex them.  */
154                   {
155                     /* We will wait again for a reply later.  */
156                     if (portset == MACH_PORT_NULL)
157                       /* Create the portset to receive all the replies on.  */
158                       err = __mach_port_allocate (__mach_task_self (),
159                                                   MACH_PORT_RIGHT_PORT_SET,
160                                                   &portset);
161                     if (! err)
162                       /* Put this reply port in the port set.  */
163                       __mach_port_move_member (__mach_task_self (),
164                                                d[i].reply_port, portset);
165                   }
166                 break;
167
168               default:
169                 /* No other error should happen.  Callers of select
170                    don't expect to see errors, so we simulate
171                    readiness of the erring object and the next call
172                    hopefully will get the error again.  */
173                 type = SELECT_ALL;
174                 /* FALLTHROUGH */
175
176               case 0:
177                 /* We got an answer.  */
178                 if ((type & SELECT_ALL) == 0)
179                   /* Bogus answer; treat like an error, as a fake positive.  */
180                   type = SELECT_ALL;
181
182                 /* This port is already ready already.  */
183                 d[i].type &= type;
184                 d[i].type |= SELECT_RETURNED;
185                 ++got;
186                 break;
187               }
188             _hurd_port_free (&d[i].cell->port, &d[i].ulink, d[i].io_port);
189           }
190     }
191
192   /* Now wait for reply messages.  */
193   if (!err && got == 0)
194     {
195       /* Now wait for io_select_reply messages on PORT,
196          timing out as appropriate.  */
197
198       union
199         {
200           mach_msg_header_t head;
201           struct
202             {
203               mach_msg_header_t head;
204               mach_msg_type_t err_type;
205               error_t err;
206             } error;
207           struct
208             {
209               mach_msg_header_t head;
210               mach_msg_type_t err_type;
211               error_t err;
212               mach_msg_type_t result_type;
213               int result;
214             } success;
215         } msg;
216       mach_msg_option_t options = (timeout == NULL ? 0 : MACH_RCV_TIMEOUT);
217       error_t msgerr;
218       while ((msgerr = __mach_msg (&msg.head,
219                                    MACH_RCV_MSG | options,
220                                    0, sizeof msg, portset, to,
221                                    MACH_PORT_NULL)) == MACH_MSG_SUCCESS)
222         {
223           /* We got a message.  Decode it.  */
224 #define IO_SELECT_REPLY_MSGID (21012 + 100) /* XXX */
225           const mach_msg_type_t inttype =
226             { MACH_MSG_TYPE_INTEGER_T, sizeof (MACH_MSG_TYPE_INTEGER_T) * 8,
227               1, 1, 0, 0 };
228           if (msg.head.msgh_id == IO_SELECT_REPLY_MSGID &&
229               msg.head.msgh_size >= sizeof msg.error &&
230               !(msg.head.msgh_bits & MACH_MSGH_BITS_COMPLEX) &&
231               *(int *) &msg.error.err_type == *(int *) &inttype)
232             {
233               /* This is a properly formatted message so far.
234                  See if it is a success or a failure.  */
235               if (msg.error.err == EINTR &&
236                   msg.head.msgh_size == sizeof msg.error)
237                 {
238                   /* EINTR response; poll for further responses
239                      and then return quickly.  */
240                   err = EINTR;
241                   goto poll;
242                 }
243               if (msg.error.err ||
244                   msg.head.msgh_size != sizeof msg.success ||
245                   *(int *) &msg.success.result_type != *(int *) &inttype ||
246                   (msg.success.result & SELECT_ALL) == 0)
247                 {
248                   /* Error or bogus reply.  Simulate readiness.  */
249                   __mach_msg_destroy (&msg.head);
250                   msg.success.result = SELECT_ALL;
251                 }
252
253               /* Look up the respondent's reply port and record its
254                  readiness.  */
255               {
256                 int had = got;
257                 if (firstfd != -1)
258                   for (i = firstfd; i <= lastfd; ++i)
259                     if (d[i].type
260                         && d[i].reply_port == msg.head.msgh_local_port)
261                       {
262                         d[i].type &= msg.success.result;
263                         d[i].type |= SELECT_RETURNED;
264                         ++got;
265                       }
266                 assert (got > had);
267               }
268             }
269
270           if (msg.head.msgh_remote_port != MACH_PORT_NULL)
271             __mach_port_deallocate (__mach_task_self (),
272                                     msg.head.msgh_remote_port);
273
274           if (got)
275           poll:
276             {
277               /* Poll for another message.  */
278               to = 0;
279               options |= MACH_RCV_TIMEOUT;
280             }
281         }
282
283       if (err == MACH_RCV_TIMED_OUT)
284         /* This is the normal value for ERR.  We might have timed out and
285            read no messages.  Otherwise, after receiving the first message,
286            we poll for more messages.  We receive with a timeout of 0 to
287            effect a poll, so ERR is MACH_RCV_TIMED_OUT when the poll finds no
288            message waiting.  */
289         err = 0;
290
291       if (got)
292         /* At least one descriptor is known to be ready now, so we will
293            return success.  */
294         err = 0;
295     }
296
297   if (firstfd != -1)
298     for (i = firstfd; i <= lastfd; ++i)
299       if (d[i].type)
300         __mach_port_destroy (__mach_task_self (), d[i].reply_port);
301   if (firstfd == -1 || (firstfd != lastfd && portset != MACH_PORT_NULL))
302     /* Destroy PORTSET, but only if it's not actually the reply port for a
303        single descriptor (in which case it's destroyed in the previous loop;
304        not doing it here is just a bit more efficient).  */
305     __mach_port_destroy (__mach_task_self (), portset);
306
307   if (err)
308     return __hurd_fail (err);
309
310   /* Below we recalculate GOT to include an increment for each operation
311      allowed on each fd.  */
312   got = 0;
313
314   /* Set the user bitarrays.  We only ever have to clear bits, as all desired
315      ones are initially set.  */
316   if (firstfd != -1)
317     for (i = firstfd; i <= lastfd; ++i)
318       {
319         int type = d[i].type;
320
321         if ((type & SELECT_RETURNED) == 0)
322           type = 0;
323
324         if (type & SELECT_READ)
325           got++;
326         else if (readfds)
327           FD_CLR (i, readfds);
328         if (type & SELECT_WRITE)
329           got++;
330         else if (writefds)
331           FD_CLR (i, writefds);
332         if (type & SELECT_URG)
333           got++;
334         else if (exceptfds)
335           FD_CLR (i, exceptfds);
336       }
337
338   return got;
339 }
340
341 weak_alias (__select, select)