/* Emulation for poll(2)
Contributed by Paolo Bonzini.
- Copyright 2001, 2002, 2003 Free Software Foundation, Inc.
+ Copyright 2001, 2002, 2003, 2006, 2007 Free Software Foundation, Inc.
This file is part of gnulib.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation,
- Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
-#ifdef HAVE_CONFIG_H
#include "config.h"
-#endif
#include <sys/types.h>
#include "poll.h"
#include <sys/select.h>
#include <unistd.h>
-#if TIME_WITH_SYS_TIME
-# include <sys/time.h>
-# include <time.h>
-#else
-# if HAVE_SYS_TIME_H
-# include <sys/time.h>
-# else
-# include <time.h>
-# endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_SYS_FILIO_H
+#include <sys/filio.h>
#endif
+#include <sys/time.h>
+#include <time.h>
+
#ifndef INFTIM
#define INFTIM (-1)
#endif
#define EOVERFLOW EINVAL
#endif
+/* BeOS does not have MSG_PEEK. */
+#ifndef MSG_PEEK
+#define MSG_PEEK 0
+#endif
+
int
poll (pfd, nfd, timeout)
struct pollfd *pfd;
{
fd_set rfds, wfds, efds;
struct timeval tv, *ptv;
- int maxfd, rc, happened;
+ int maxfd, rc;
nfds_t i;
- char data[64];
#ifdef _SC_OPEN_MAX
if (nfd > sysconf (_SC_OPEN_MAX))
| POLLWRNORM | POLLWRBAND)))
{
maxfd = pfd[i].fd;
+
+ /* Windows use a linear array of sockets (of size FD_SETSIZE). The
+ descriptor value is not used to address the array. */
+#if defined __CYGWIN__ || (!defined _WIN32 && !defined __WIN32__)
if (maxfd > FD_SETSIZE)
{
errno = EOVERFLOW;
return -1;
}
+#endif
}
}
/* examine fd sets */
rc = select (maxfd + 1, &rfds, &wfds, &efds, ptv);
+ if (rc < 0)
+ return rc;
/* establish results */
- if (rc > 0)
- {
- rc = 0;
- for (i = 0; i < nfd; i++)
- {
- pfd[i].revents = 0;
- if (pfd[i].fd < 0)
- continue;
-
- happened = 0;
- if (FD_ISSET (pfd[i].fd, &rfds))
- {
- /* support for POLLHUP. An hung up descriptor does not
- increase the return value! */
- if (recv (pfd[i].fd, data, 64, MSG_PEEK) == -1)
- {
- if (errno == ESHUTDOWN || errno == ECONNRESET
- || errno == ECONNABORTED || errno == ENETRESET)
- pfd[i].revents |= POLLHUP;
- }
- else
- happened |= POLLIN | POLLRDNORM;
- }
-
- if (FD_ISSET (pfd[i].fd, &wfds))
- happened |= POLLOUT | POLLWRNORM | POLLWRBAND;
-
- if (FD_ISSET (pfd[i].fd, &efds))
- happened |= POLLPRI | POLLRDBAND;
-
- pfd[i].revents |= pfd[i].events & happened;
- rc += (happened > 0);
- }
- }
+ rc = 0;
+ for (i = 0; i < nfd; i++)
+ if (pfd[i].fd < 0)
+ pfd[i].revents = 0;
+ else
+ {
+ int happened = 0, sought = pfd[i].events;
+ if (FD_ISSET (pfd[i].fd, &rfds))
+ {
+ int r;
+
+#if defined __MACH__ && defined __APPLE__
+ /* There is a bug in Mac OS X that causes it to ignore MSG_PEEK
+ for some kinds of descriptors. Detect if this descriptor is a
+ connected socket, a server socket, or something else using a
+ 0-byte recv, and use ioctl(2) to detect POLLHUP. */
+ r = recv (pfd[i].fd, NULL, 0, MSG_PEEK);
+ if (r == 0 || errno == ENOTSOCK)
+ ioctl(pfd[i].fd, FIONREAD, &r);
+#else
+ char data[64];
+ r = recv (pfd[i].fd, data, sizeof (data), MSG_PEEK);
+#endif
+ if (r == 0)
+ happened |= POLLHUP;
+
+ /* If the event happened on an unconnected server socket,
+ that's fine. */
+ else if (r > 0 || ( /* (r == -1) && */ errno == ENOTCONN))
+ happened |= (POLLIN | POLLRDNORM) & sought;
+
+ /* Distinguish hung-up sockets from other errors. */
+ else if (errno == ESHUTDOWN || errno == ECONNRESET
+ || errno == ECONNABORTED || errno == ENETRESET)
+ happened |= POLLHUP;
+
+ else
+ happened |= POLLERR;
+ }
+
+ if (FD_ISSET (pfd[i].fd, &wfds))
+ happened |= (POLLOUT | POLLWRNORM | POLLWRBAND) & sought;
+
+ if (FD_ISSET (pfd[i].fd, &efds))
+ happened |= (POLLPRI | POLLRDBAND) & sought;
+
+ if (happened)
+ {
+ pfd[i].revents = happened;
+ rc++;
+ }
+ }
return rc;
}