2007-01-03 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, 2007 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;
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   if (rc < 0)
147     return rc;
148
149   /* establish results */
150   rc = 0;
151   for (i = 0; i < nfd; i++)
152     if (pfd[i].fd < 0)
153       pfd[i].revents = 0;
154     else
155       {
156         int happened = 0, sought = pfd[i].events;
157         if (FD_ISSET (pfd[i].fd, &rfds))
158           {
159             int r;
160             
161 #if defined __MACH__ && defined __APPLE__
162             /* There is a bug in Mac OS X that causes it to ignore MSG_PEEK
163                for some kinds of descriptors.  Detect if this descriptor is a
164                connected socket, a server socket, or something else using a
165                0-byte recv, and use ioctl(2) to detect POLLHUP.  */
166             r = recv (pfd[i].fd, NULL, 0, MSG_PEEK);
167             if (r == 0 || errno == ENOTSOCK)
168               ioctl(pfd[i].fd, FIONREAD, &r);
169 #else
170             char data[64];
171             r = recv (pfd[i].fd, data, sizeof (data), MSG_PEEK);
172 #endif
173             if (r == 0)
174               happened |= POLLHUP;
175             
176             /* If the event happened on an unconnected server socket,
177                that's fine. */
178             else if (r > 0 || ( /* (r == -1) && */ errno == ENOTCONN))
179               happened |= (POLLIN | POLLRDNORM) & sought;
180             
181             /* Distinguish hung-up sockets from other errors.  */
182             else if (errno == ESHUTDOWN || errno == ECONNRESET
183                      || errno == ECONNABORTED || errno == ENETRESET)
184               happened |= POLLHUP;
185             
186             else
187               happened |= POLLERR;
188           }
189         
190         if (FD_ISSET (pfd[i].fd, &wfds))
191           happened |= (POLLOUT | POLLWRNORM | POLLWRBAND) & sought;
192         
193         if (FD_ISSET (pfd[i].fd, &efds))
194           happened |= (POLLPRI | POLLRDBAND) & sought;
195         
196         if (happened)
197           {
198             pfd[i].revents = happened;
199             rc++;
200           }
201       }
202
203   return rc;
204 }