2006-09-28 Paolo Bonzini <bonzini@gnu.org>
[gnulib.git] / lib / poll.c
1 /* Emulation for poll(2)
2    Contributed by Paolo Bonzini.
3
4    Copyright 2001, 2002, 2003, 2006 Free Software Foundation, Inc.
5
6    This file is part of gnulib.
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2, or (at your option)
11    any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License along
19    with this program; if not, write to the Free Software Foundation,
20    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
21
22 #include "config.h"
23
24 #include <sys/types.h>
25 #include "poll.h"
26 #include <errno.h>
27 #include <limits.h>
28 #include <sys/socket.h>
29 #include <sys/select.h>
30 #include <unistd.h>
31
32 #ifdef HAVE_SYS_IOCTL_H
33 #include <sys/ioctl.h>
34 #endif
35 #ifdef HAVE_SYS_FILIO_H
36 #include <sys/filio.h>
37 #endif
38
39 #if TIME_WITH_SYS_TIME
40 # include <sys/time.h>
41 # include <time.h>
42 #else
43 # if HAVE_SYS_TIME_H
44 #  include <sys/time.h>
45 # else
46 #  include <time.h>
47 # endif
48 #endif
49
50 #ifndef INFTIM
51 #define INFTIM (-1)
52 #endif
53
54 #ifndef EOVERFLOW
55 #define EOVERFLOW EINVAL
56 #endif
57
58 int
59 poll (pfd, nfd, timeout)
60      struct pollfd *pfd;
61      nfds_t nfd;
62      int timeout;
63 {
64   fd_set rfds, wfds, efds;
65   struct timeval tv, *ptv;
66   int maxfd, rc, happened;
67   nfds_t i;
68
69 #ifdef _SC_OPEN_MAX
70   if (nfd > sysconf (_SC_OPEN_MAX))
71     {
72       errno = EINVAL;
73       return -1;
74     }
75 #else /* !_SC_OPEN_MAX */
76 #ifdef OPEN_MAX
77   if (nfd > OPEN_MAX)
78     {
79       errno = EINVAL;
80       return -1;
81     }
82 #endif /* OPEN_MAX -- else, no check is needed */
83 #endif /* !_SC_OPEN_MAX */
84
85   /* EFAULT is not necessary to implement, but let's do it in the
86      simplest case. */
87   if (!pfd)
88     {
89       errno = EFAULT;
90       return -1;
91     }
92
93   /* convert timeout number into a timeval structure */
94   ptv = &tv;
95   if (timeout >= 0)
96     {
97       /* return immediately or after timeout */
98       ptv->tv_sec = timeout / 1000;
99       ptv->tv_usec = (timeout % 1000) * 1000;
100     }
101   else if (timeout == INFTIM)
102     /* wait forever */
103     ptv = NULL;
104   else
105     {
106       errno = EINVAL;
107       return -1;
108     }
109
110   /* create fd sets and determine max fd */
111   maxfd = -1;
112   FD_ZERO (&rfds);
113   FD_ZERO (&wfds);
114   FD_ZERO (&efds);
115   for (i = 0; i < nfd; i++)
116     {
117       if (pfd[i].fd < 0)
118         continue;
119
120       if (pfd[i].events & (POLLIN | POLLRDNORM))
121         FD_SET (pfd[i].fd, &rfds);
122
123       /* see select(2): "the only exceptional condition detectable
124          is out-of-band data received on a socket", hence we push
125          POLLWRBAND events onto wfds instead of efds. */
126       if (pfd[i].events & (POLLOUT | POLLWRNORM | POLLWRBAND))
127         FD_SET (pfd[i].fd, &wfds);
128       if (pfd[i].events & (POLLPRI | POLLRDBAND))
129         FD_SET (pfd[i].fd, &efds);
130       if (pfd[i].fd >= maxfd
131           && (pfd[i].events & (POLLIN | POLLOUT | POLLPRI
132                                | POLLRDNORM | POLLRDBAND
133                                | POLLWRNORM | POLLWRBAND)))
134         {
135           maxfd = pfd[i].fd;
136           if (maxfd > FD_SETSIZE)
137             {
138               errno = EOVERFLOW;
139               return -1;
140             }
141         }
142     }
143
144   /* examine fd sets */
145   rc = select (maxfd + 1, &rfds, &wfds, &efds, ptv);
146
147   /* establish results */
148   if (rc > 0)
149     {
150       rc = 0;
151       for (i = 0; i < nfd; i++)
152         {
153           pfd[i].revents = 0;
154           if (pfd[i].fd < 0)
155             continue;
156
157           happened = 0;
158           if (FD_ISSET (pfd[i].fd, &rfds))
159             {
160               int r;
161               long avail = -1;
162               /* support for POLLHUP.  */
163 #if defined __MACH__ && defined __APPLE__
164               /* There is a bug in Mac OS X that causes it to ignore MSG_PEEK for
165                  some kinds of descriptors.  Use FIONREAD to emulate POLLHUP.
166                  It is still not completely POSIX compliant (it does not fully
167                  work on TTYs), but at least it does not delete data!  For other
168                  platforms, we still use MSG_PEEK because it was proved to be
169                  reliable, and I a leery of changing it.  */
170               do
171                 r = ioctl (pfd[i].fd, FIONREAD, &avail);
172               while (r == -1 && (errno == EAGAIN || errno == EINTR));
173               if (avail < 0)
174                 avail = 0;
175 #else
176               char data[64];
177               r = recv (pfd[i].fd, data, 64, MSG_PEEK);
178               if (r == -1)
179                 {
180                   avail = (errno == ESHUTDOWN || errno == ECONNRESET ||
181                            errno == ECONNABORTED || errno == ENETRESET) ? 0 : -1;
182                   errno = 0;
183                 }
184               else
185                 avail = r;
186 #endif
187
188               /* An hung up descriptor does not increase the return value! */
189               if (avail == 0)
190                 pfd[i].revents |= POLLHUP;
191               else if (avail == -1)
192                 pfd[i].revents |= POLLERR;
193               else
194                 happened |= POLLIN | POLLRDNORM;
195             }
196
197           if (FD_ISSET (pfd[i].fd, &wfds))
198             happened |= POLLOUT | POLLWRNORM | POLLWRBAND;
199
200           if (FD_ISSET (pfd[i].fd, &efds))
201             happened |= POLLPRI | POLLRDBAND;
202
203           pfd[i].revents |= pfd[i].events & happened;
204           rc += (happened > 0);
205         }
206     }
207
208   return rc;
209 }