Prefer <config.h> over "config.h". See autoconf doc for explanation.
[gnulib.git] / lib / poll.c
1 /* Emulation for poll(2)
2    Contributed by Paolo Bonzini.
3
4    Copyright 2001, 2002, 2003, 2006, 2007, 2008 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 #include <sys/time.h>
40 #include <time.h>
41
42 #ifndef INFTIM
43 #define INFTIM (-1)
44 #endif
45
46 #ifndef EOVERFLOW
47 #define EOVERFLOW EINVAL
48 #endif
49
50 /* BeOS does not have MSG_PEEK.  */
51 #ifndef MSG_PEEK
52 #define MSG_PEEK 0
53 #endif
54
55 int
56 poll (pfd, nfd, timeout)
57      struct pollfd *pfd;
58      nfds_t nfd;
59      int timeout;
60 {
61   fd_set rfds, wfds, efds;
62   struct timeval tv, *ptv;
63   int maxfd, rc;
64   nfds_t i;
65
66 #ifdef _SC_OPEN_MAX
67   if (nfd > sysconf (_SC_OPEN_MAX))
68     {
69       errno = EINVAL;
70       return -1;
71     }
72 #else /* !_SC_OPEN_MAX */
73 #ifdef OPEN_MAX
74   if (nfd > OPEN_MAX)
75     {
76       errno = EINVAL;
77       return -1;
78     }
79 #endif /* OPEN_MAX -- else, no check is needed */
80 #endif /* !_SC_OPEN_MAX */
81
82   /* EFAULT is not necessary to implement, but let's do it in the
83      simplest case. */
84   if (!pfd)
85     {
86       errno = EFAULT;
87       return -1;
88     }
89
90   /* convert timeout number into a timeval structure */
91   ptv = &tv;
92   if (timeout >= 0)
93     {
94       /* return immediately or after timeout */
95       ptv->tv_sec = timeout / 1000;
96       ptv->tv_usec = (timeout % 1000) * 1000;
97     }
98   else if (timeout == INFTIM)
99     /* wait forever */
100     ptv = NULL;
101   else
102     {
103       errno = EINVAL;
104       return -1;
105     }
106
107   /* create fd sets and determine max fd */
108   maxfd = -1;
109   FD_ZERO (&rfds);
110   FD_ZERO (&wfds);
111   FD_ZERO (&efds);
112   for (i = 0; i < nfd; i++)
113     {
114       if (pfd[i].fd < 0)
115         continue;
116
117       if (pfd[i].events & (POLLIN | POLLRDNORM))
118         FD_SET (pfd[i].fd, &rfds);
119
120       /* see select(2): "the only exceptional condition detectable
121          is out-of-band data received on a socket", hence we push
122          POLLWRBAND events onto wfds instead of efds. */
123       if (pfd[i].events & (POLLOUT | POLLWRNORM | POLLWRBAND))
124         FD_SET (pfd[i].fd, &wfds);
125       if (pfd[i].events & (POLLPRI | POLLRDBAND))
126         FD_SET (pfd[i].fd, &efds);
127       if (pfd[i].fd >= maxfd
128           && (pfd[i].events & (POLLIN | POLLOUT | POLLPRI
129                                | POLLRDNORM | POLLRDBAND
130                                | POLLWRNORM | POLLWRBAND)))
131         {
132           maxfd = pfd[i].fd;
133
134           /* Windows use a linear array of sockets (of size FD_SETSIZE). The
135              descriptor value is not used to address the array.  */
136 #if defined __CYGWIN__ || (!defined _WIN32 && !defined __WIN32__)
137           if (maxfd > FD_SETSIZE)
138             {
139               errno = EOVERFLOW;
140               return -1;
141             }
142 #endif
143         }
144     }
145
146   /* examine fd sets */
147   rc = select (maxfd + 1, &rfds, &wfds, &efds, ptv);
148   if (rc < 0)
149     return rc;
150
151   /* establish results */
152   rc = 0;
153   for (i = 0; i < nfd; i++)
154     if (pfd[i].fd < 0)
155       pfd[i].revents = 0;
156     else
157       {
158         int happened = 0, sought = pfd[i].events;
159         if (FD_ISSET (pfd[i].fd, &rfds))
160           {
161             int r;
162
163 #if defined __MACH__ && defined __APPLE__
164             /* There is a bug in Mac OS X that causes it to ignore MSG_PEEK
165                for some kinds of descriptors.  Detect if this descriptor is a
166                connected socket, a server socket, or something else using a
167                0-byte recv, and use ioctl(2) to detect POLLHUP.  */
168             r = recv (pfd[i].fd, NULL, 0, MSG_PEEK);
169             if (r == 0 || errno == ENOTSOCK)
170               ioctl(pfd[i].fd, FIONREAD, &r);
171 #else
172             char data[64];
173             r = recv (pfd[i].fd, data, sizeof (data), MSG_PEEK);
174 #endif
175             if (r == 0)
176               happened |= POLLHUP;
177
178             /* If the event happened on an unconnected server socket,
179                that's fine. */
180             else if (r > 0 || ( /* (r == -1) && */ errno == ENOTCONN))
181               happened |= (POLLIN | POLLRDNORM) & sought;
182
183             /* Distinguish hung-up sockets from other errors.  */
184             else if (errno == ESHUTDOWN || errno == ECONNRESET
185                      || errno == ECONNABORTED || errno == ENETRESET)
186               happened |= POLLHUP;
187
188             else
189               happened |= POLLERR;
190           }
191
192         if (FD_ISSET (pfd[i].fd, &wfds))
193           happened |= (POLLOUT | POLLWRNORM | POLLWRBAND) & sought;
194
195         if (FD_ISSET (pfd[i].fd, &efds))
196           happened |= (POLLPRI | POLLRDBAND) & sought;
197
198         if (happened)
199           {
200             pfd[i].revents = happened;
201             rc++;
202           }
203       }
204
205   return rc;
206 }