1998-12-05 Roland McGrath <roland@baalperazim.frob.com>
[platform/upstream/glibc.git] / hurd / hurdselect.c
1 /* Guts of both `select' and `poll' for Hurd.
2    Copyright (C) 1991,92,93,94,95,96,97,98 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Library General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Library General Public License for more details.
14
15    You should have received a copy of the GNU Library General Public
16    License along with the GNU C Library; see the file COPYING.LIB.  If not,
17    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18    Boston, MA 02111-1307, USA.  */
19
20 #include <sys/types.h>
21 #include <sys/poll.h>
22 #include <hurd.h>
23 #include <hurd/fd.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <assert.h>
27 #include <stdint.h>
28
29 /* All user select types.  */
30 #define SELECT_ALL (SELECT_READ | SELECT_WRITE | SELECT_URG)
31
32 /* Used to record that a particular select rpc returned.  Must be distinct
33    from SELECT_ALL (which better not have the high bit set).  */
34 #define SELECT_RETURNED ((SELECT_ALL << 1) & ~SELECT_ALL)
35
36 /* Check the first NFDS descriptors either in POLLFDS (if nonnnull) or in
37    each of READFDS, WRITEFDS, EXCEPTFDS that is nonnull.  If TIMEOUT is not
38    NULL, time out after waiting the interval specified therein.  Returns
39    the number of ready descriptors, or -1 for errors.  */
40 int
41 _hurd_select (int nfds,
42               struct pollfd *pollfds,
43               fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
44               const struct timespec *timeout, const sigset_t *sigmask)
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_nsec / 1000000) :
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   sigset_t oset;
65
66   if (sigmask && __sigprocmask (SIG_SETMASK, sigmask, &oset))
67     return -1;
68
69   if (pollfds)
70     {
71       /* Collect interesting descriptors from the user's `pollfd' array.
72          We do a first pass that reads the user's array before taking
73          any locks.  The second pass then only touches our own stack,
74          and gets the port references.  */
75
76       for (i = 0; i < nfds; ++i)
77         if (pollfds[i].fd >= 0)
78           {
79             int type = 0;
80             if (pollfds[i].events & POLLIN)
81               type |= SELECT_READ;
82             if (pollfds[i].events & POLLOUT)
83               type |= SELECT_WRITE;
84             if (pollfds[i].events & POLLPRI)
85               type |= SELECT_URG;
86
87             d[i].io_port = pollfds[i].fd;
88             d[i].type = type;
89           }
90         else
91           d[i].type = 0;
92
93       HURD_CRITICAL_BEGIN;
94       __mutex_lock (&_hurd_dtable_lock);
95
96       for (i = 0; i < nfds; ++i)
97         if (d[i].type != 0)
98           {
99             const int fd = (int) d[i].io_port;
100
101             if (fd < _hurd_dtablesize)
102               {
103                 d[i].cell = _hurd_dtable[fd];
104                 d[i].io_port = _hurd_port_get (&d[i].cell->port, &d[i].ulink);
105                 if (d[i].io_port != MACH_PORT_NULL)
106                   continue;
107               }
108
109             /* If one descriptor is bogus, we fail completely.  */
110             while (i-- > 0)
111               if (d[i].type != 0)
112                 _hurd_port_free (&d[i].cell->port,
113                                  &d[i].ulink, d[i].io_port);
114             break;
115           }
116
117       __mutex_unlock (&_hurd_dtable_lock);
118       HURD_CRITICAL_END;
119
120       if (i < nfds)
121         {
122           if (sigmask)
123             __sigprocmask (SIG_SETMASK, &oset, NULL);
124           errno = EBADF;
125           return -1;
126         }
127
128       lastfd = i - 1;
129       firstfd = i == 0 ? lastfd : 0;
130     }
131   else
132     {
133       /* Collect interested descriptors from the user's fd_set arguments.
134          Use local copies so we can't crash from user bogosity.  */
135
136       if (readfds == NULL)
137         FD_ZERO (&rfds);
138       else
139         rfds = *readfds;
140       if (writefds == NULL)
141         FD_ZERO (&wfds);
142       else
143         wfds = *writefds;
144       if (exceptfds == NULL)
145         FD_ZERO (&xfds);
146       else
147         xfds = *exceptfds;
148
149       HURD_CRITICAL_BEGIN;
150       __mutex_lock (&_hurd_dtable_lock);
151
152       if (nfds > _hurd_dtablesize)
153         nfds = _hurd_dtablesize;
154
155       /* Collect the ports for interesting FDs.  */
156       firstfd = lastfd = -1;
157       for (i = 0; i < nfds; ++i)
158         {
159           int type = 0;
160           if (readfds != NULL && FD_ISSET (i, &rfds))
161             type |= SELECT_READ;
162           if (writefds != NULL && FD_ISSET (i, &wfds))
163             type |= SELECT_WRITE;
164           if (exceptfds != NULL && FD_ISSET (i, &xfds))
165             type |= SELECT_URG;
166           d[i].type = type;
167           if (type)
168             {
169               d[i].cell = _hurd_dtable[i];
170               d[i].io_port = _hurd_port_get (&d[i].cell->port, &d[i].ulink);
171               if (d[i].io_port == MACH_PORT_NULL)
172                 {
173                   /* If one descriptor is bogus, we fail completely.  */
174                   while (i-- > 0)
175                     _hurd_port_free (&d[i].cell->port, &d[i].ulink,
176                                      d[i].io_port);
177                   break;
178                 }
179               lastfd = i;
180               if (firstfd == -1)
181                 firstfd = i;
182             }
183         }
184
185       __mutex_unlock (&_hurd_dtable_lock);
186       HURD_CRITICAL_END;
187
188       if (i < nfds)
189         {
190           if (sigmask)
191             __sigprocmask (SIG_SETMASK, &oset, NULL);
192           errno = EBADF;
193           return -1;
194         }
195     }
196
197
198   err = 0;
199   got = 0;
200
201   /* Send them all io_select request messages.  */
202
203   if (firstfd == -1)
204     /* But not if there were no ports to deal with at all.
205        We are just a pure timeout.  */
206     portset = __mach_reply_port ();
207   else
208     {
209       portset = MACH_PORT_NULL;
210
211       for (i = firstfd; i <= lastfd; ++i)
212         if (d[i].type)
213           {
214             int type = d[i].type;
215             d[i].reply_port = __mach_reply_port ();
216             err = __io_select (d[i].io_port, d[i].reply_port,
217                                /* Poll only if there's a single descriptor.  */
218                                (firstfd == lastfd) ? to : 0,
219                                &type);
220             switch (err)
221               {
222               case MACH_RCV_TIMED_OUT:
223                 /* No immediate response.  This is normal.  */
224                 err = 0;
225                 if (firstfd == lastfd)
226                   /* When there's a single descriptor, we don't need a
227                      portset, so just pretend we have one, but really
228                      use the single reply port.  */
229                   portset = d[i].reply_port;
230                 else if (got == 0)
231                   /* We've got multiple reply ports, so we need a port set to
232                      multiplex them.  */
233                   {
234                     /* We will wait again for a reply later.  */
235                     if (portset == MACH_PORT_NULL)
236                       /* Create the portset to receive all the replies on.  */
237                       err = __mach_port_allocate (__mach_task_self (),
238                                                   MACH_PORT_RIGHT_PORT_SET,
239                                                   &portset);
240                     if (! err)
241                       /* Put this reply port in the port set.  */
242                       __mach_port_move_member (__mach_task_self (),
243                                                d[i].reply_port, portset);
244                   }
245                 break;
246
247               default:
248                 /* No other error should happen.  Callers of select
249                    don't expect to see errors, so we simulate
250                    readiness of the erring object and the next call
251                    hopefully will get the error again.  */
252                 type = SELECT_ALL;
253                 /* FALLTHROUGH */
254
255               case 0:
256                 /* We got an answer.  */
257                 if ((type & SELECT_ALL) == 0)
258                   /* Bogus answer; treat like an error, as a fake positive.  */
259                   type = SELECT_ALL;
260
261                 /* This port is already ready already.  */
262                 d[i].type &= type;
263                 d[i].type |= SELECT_RETURNED;
264                 ++got;
265                 break;
266               }
267             _hurd_port_free (&d[i].cell->port, &d[i].ulink, d[i].io_port);
268           }
269     }
270
271   /* Now wait for reply messages.  */
272   if (!err && got == 0)
273     {
274       /* Now wait for io_select_reply messages on PORT,
275          timing out as appropriate.  */
276
277       union
278         {
279           mach_msg_header_t head;
280           struct
281             {
282               mach_msg_header_t head;
283               mach_msg_type_t err_type;
284               error_t err;
285             } error;
286           struct
287             {
288               mach_msg_header_t head;
289               mach_msg_type_t err_type;
290               error_t err;
291               mach_msg_type_t result_type;
292               int result;
293             } success;
294         } msg;
295       mach_msg_option_t options = (timeout == NULL ? 0 : MACH_RCV_TIMEOUT);
296       error_t msgerr;
297       while ((msgerr = __mach_msg (&msg.head,
298                                    MACH_RCV_MSG | options,
299                                    0, sizeof msg, portset, to,
300                                    MACH_PORT_NULL)) == MACH_MSG_SUCCESS)
301         {
302           /* We got a message.  Decode it.  */
303 #define IO_SELECT_REPLY_MSGID (21012 + 100) /* XXX */
304           const mach_msg_type_t inttype =
305             { MACH_MSG_TYPE_INTEGER_T, sizeof (MACH_MSG_TYPE_INTEGER_T) * 8,
306               1, 1, 0, 0 };
307           if (msg.head.msgh_id == IO_SELECT_REPLY_MSGID &&
308               msg.head.msgh_size >= sizeof msg.error &&
309               !(msg.head.msgh_bits & MACH_MSGH_BITS_COMPLEX) &&
310               *(int *) &msg.error.err_type == *(int *) &inttype)
311             {
312               /* This is a properly formatted message so far.
313                  See if it is a success or a failure.  */
314               if (msg.error.err == EINTR &&
315                   msg.head.msgh_size == sizeof msg.error)
316                 {
317                   /* EINTR response; poll for further responses
318                      and then return quickly.  */
319                   err = EINTR;
320                   goto poll;
321                 }
322               if (msg.error.err ||
323                   msg.head.msgh_size != sizeof msg.success ||
324                   *(int *) &msg.success.result_type != *(int *) &inttype ||
325                   (msg.success.result & SELECT_ALL) == 0)
326                 {
327                   /* Error or bogus reply.  Simulate readiness.  */
328                   __mach_msg_destroy (&msg.head);
329                   msg.success.result = SELECT_ALL;
330                 }
331
332               /* Look up the respondent's reply port and record its
333                  readiness.  */
334               {
335                 int had = got;
336                 if (firstfd != -1)
337                   for (i = firstfd; i <= lastfd; ++i)
338                     if (d[i].type
339                         && d[i].reply_port == msg.head.msgh_local_port)
340                       {
341                         d[i].type &= msg.success.result;
342                         d[i].type |= SELECT_RETURNED;
343                         ++got;
344                       }
345                 assert (got > had);
346               }
347             }
348
349           if (msg.head.msgh_remote_port != MACH_PORT_NULL)
350             __mach_port_deallocate (__mach_task_self (),
351                                     msg.head.msgh_remote_port);
352
353           if (got)
354           poll:
355             {
356               /* Poll for another message.  */
357               to = 0;
358               options |= MACH_RCV_TIMEOUT;
359             }
360         }
361
362       if (err == MACH_RCV_TIMED_OUT)
363         /* This is the normal value for ERR.  We might have timed out and
364            read no messages.  Otherwise, after receiving the first message,
365            we poll for more messages.  We receive with a timeout of 0 to
366            effect a poll, so ERR is MACH_RCV_TIMED_OUT when the poll finds no
367            message waiting.  */
368         err = 0;
369
370       if (got)
371         /* At least one descriptor is known to be ready now, so we will
372            return success.  */
373         err = 0;
374     }
375
376   if (firstfd != -1)
377     for (i = firstfd; i <= lastfd; ++i)
378       if (d[i].type)
379         __mach_port_destroy (__mach_task_self (), d[i].reply_port);
380   if (firstfd == -1 || (firstfd != lastfd && portset != MACH_PORT_NULL))
381     /* Destroy PORTSET, but only if it's not actually the reply port for a
382        single descriptor (in which case it's destroyed in the previous loop;
383        not doing it here is just a bit more efficient).  */
384     __mach_port_destroy (__mach_task_self (), portset);
385
386   if (err)
387     {
388       if (sigmask)
389         __sigprocmask (SIG_SETMASK, &oset, NULL);
390       return __hurd_fail (err);
391     }
392
393   if (pollfds)
394     /* Fill in the `revents' members of the user's array.  */
395     for (i = 0; i < nfds; ++i)
396       {
397         const int type = d[i].type;
398         int_fast16_t revents = 0;
399
400         if (type & SELECT_READ)
401           revents |= POLLIN;
402         if (type & SELECT_WRITE)
403           revents |= POLLOUT;
404         if (type & SELECT_URG)
405           revents |= POLLPRI;
406
407         pollfds[i].revents = revents;
408       }
409   else
410     {
411       /* Below we recalculate GOT to include an increment for each operation
412          allowed on each fd.  */
413       got = 0;
414
415       /* Set the user bitarrays.  We only ever have to clear bits, as all
416          desired ones are initially set.  */
417       if (firstfd != -1)
418         for (i = firstfd; i <= lastfd; ++i)
419           {
420             int type = d[i].type;
421
422             if ((type & SELECT_RETURNED) == 0)
423               type = 0;
424
425             if (type & SELECT_READ)
426               got++;
427             else if (readfds)
428               FD_CLR (i, readfds);
429             if (type & SELECT_WRITE)
430               got++;
431             else if (writefds)
432               FD_CLR (i, writefds);
433             if (type & SELECT_URG)
434               got++;
435             else if (exceptfds)
436               FD_CLR (i, exceptfds);
437           }
438     }
439
440   if (sigmask && __sigprocmask (SIG_SETMASK, &oset, NULL))
441     return -1;
442
443   return got;
444 }