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