New module poll, from 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 Free Software Foundation, Inc.
5
6    This file is part of gnulib.
7
8    gnulib is free software; you can redistribute it and/or modify it
9    under the terms of the GNU Lesser General Public License as published
10    by the Free Software Foundation; either version 2.1, or (at your option)
11    any later version.
12
13    gnulib is distributed in the hope that it will be useful, but WITHOUT
14    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
15    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
16    License for more details.
17
18    You should have received a copy of the GNU Lesser General Public License
19    along with gnulib; see the file COPYING.LIB.  If not, write to the Free
20    Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
21    USA.  
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #include <sys/types.h>
29 #include "poll.h"
30 #include <errno.h>
31 #include <limits.h>
32 #include <sys/socket.h>
33 #include <sys/select.h>
34 #include <unistd.h>
35
36 #if TIME_WITH_SYS_TIME
37 # include <sys/time.h>
38 # include <time.h>
39 #else
40 # if HAVE_SYS_TIME_H
41 #  include <sys/time.h>
42 # else
43 #  include <time.h>
44 # endif
45 #endif
46
47 #ifndef INFTIM
48 #define INFTIM (-1)
49 #endif
50
51 #ifndef EOVERFLOW
52 #define EOVERFLOW EINVAL
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, happened;
64   nfds_t i;
65   char data[64];
66
67 #ifdef _SC_OPEN_MAX
68   if (nfd > sysconf (_SC_OPEN_MAX))
69     {
70       errno = EINVAL;
71       return -1;
72     }
73 #else /* !_SC_OPEN_MAX */
74 #ifdef OPEN_MAX
75   if (nfd > OPEN_MAX)
76     {
77       errno = EINVAL;
78       return -1;
79     }
80 #endif /* OPEN_MAX -- else, no check is needed */
81 #endif /* !_SC_OPEN_MAX */
82
83   /* EFAULT is not necessary to implement, but let's do it in the
84      simplest case. */
85   if (!pfd)
86     {
87       errno = EFAULT;
88       return -1;
89     }
90
91   /* convert timeout number into a timeval structure */
92   ptv = &tv;
93   if (timeout >= 0)
94     {
95       /* return immediately or after timeout */
96       ptv->tv_sec = timeout / 1000;
97       ptv->tv_usec = (timeout % 1000) * 1000;
98     }
99   else if (timeout == INFTIM)
100     /* wait forever */
101     ptv = NULL;
102   else
103     {
104       errno = EINVAL;
105       return -1;
106     }
107
108   /* create fd sets and determine max fd */
109   maxfd = -1;
110   FD_ZERO (&rfds);
111   FD_ZERO (&wfds);
112   FD_ZERO (&efds);
113   for (i = 0; i < nfd; i++)
114     {
115       if (pfd[i].fd < 0)
116         continue;
117
118       if (pfd[i].events & (POLLIN | POLLRDNORM))
119         FD_SET (pfd[i].fd, &rfds);
120
121       /* see select(2): "the only exceptional condition detectable
122          is out-of-band data received on a socket", hence we push
123          POLLWRBAND events onto wfds instead of efds. */
124       if (pfd[i].events & (POLLOUT | POLLWRNORM | POLLWRBAND))
125         FD_SET (pfd[i].fd, &wfds);
126       if (pfd[i].events & (POLLPRI | POLLRDBAND))
127         FD_SET (pfd[i].fd, &efds);
128       if (pfd[i].fd >= maxfd
129           && (pfd[i].events & (POLLIN | POLLOUT | POLLPRI
130                                | POLLRDNORM | POLLRDBAND
131                                | POLLWRNORM | POLLWRBAND)))
132         {
133           maxfd = pfd[i].fd;
134           if (maxfd > FD_SETSIZE)
135             {
136               errno = EOVERFLOW;
137               return -1;
138             }
139         }
140     }
141
142   /* examine fd sets */
143   rc = select (maxfd + 1, &rfds, &wfds, &efds, ptv);
144
145   /* establish results */
146   if (rc > 0)
147     {
148       rc = 0;
149       for (i = 0; i < nfd; i++)
150         {
151           pfd[i].revents = 0;
152           if (pfd[i].fd < 0)
153             continue;
154
155           happened = 0;
156           if (FD_ISSET (pfd[i].fd, &rfds))
157             {
158               /* support for POLLHUP.  An hung up descriptor does not
159                  increase the return value! */
160               if (recv (pfd[i].fd, data, 64, MSG_PEEK) == -1)
161                 {
162                   if (errno == ESHUTDOWN || errno == ECONNRESET
163                       || errno == ECONNABORTED || errno == ENETRESET)
164                     pfd[i].revents |= POLLHUP;
165                 }
166               else
167                 happened |= POLLIN | POLLRDNORM;
168             }
169
170           if (FD_ISSET (pfd[i].fd, &wfds))
171             happened |= POLLOUT | POLLWRNORM | POLLWRBAND;
172
173           if (FD_ISSET (pfd[i].fd, &efds))
174             happened |= POLLPRI | POLLRDBAND;
175
176           pfd[i].revents |= pfd[i].events & happened;
177           rc += (happened > 0);
178         }
179     }
180
181   return rc;
182 }