*** empty log message ***
[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    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 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include <sys/types.h>
27 #include "poll.h"
28 #include <errno.h>
29 #include <limits.h>
30 #include <sys/socket.h>
31 #include <sys/select.h>
32 #include <unistd.h>
33
34 #if TIME_WITH_SYS_TIME
35 # include <sys/time.h>
36 # include <time.h>
37 #else
38 # if HAVE_SYS_TIME_H
39 #  include <sys/time.h>
40 # else
41 #  include <time.h>
42 # endif
43 #endif
44
45 #ifndef INFTIM
46 #define INFTIM (-1)
47 #endif
48
49 #ifndef EOVERFLOW
50 #define EOVERFLOW EINVAL
51 #endif
52
53 int
54 poll (pfd, nfd, timeout)
55      struct pollfd *pfd;
56      nfds_t nfd;
57      int timeout;
58 {
59   fd_set rfds, wfds, efds;
60   struct timeval tv, *ptv;
61   int maxfd, rc, happened;
62   nfds_t i;
63   char data[64];
64
65 #ifdef _SC_OPEN_MAX
66   if (nfd > sysconf (_SC_OPEN_MAX))
67     {
68       errno = EINVAL;
69       return -1;
70     }
71 #else /* !_SC_OPEN_MAX */
72 #ifdef OPEN_MAX
73   if (nfd > OPEN_MAX)
74     {
75       errno = EINVAL;
76       return -1;
77     }
78 #endif /* OPEN_MAX -- else, no check is needed */
79 #endif /* !_SC_OPEN_MAX */
80
81   /* EFAULT is not necessary to implement, but let's do it in the
82      simplest case. */
83   if (!pfd)
84     {
85       errno = EFAULT;
86       return -1;
87     }
88
89   /* convert timeout number into a timeval structure */
90   ptv = &tv;
91   if (timeout >= 0)
92     {
93       /* return immediately or after timeout */
94       ptv->tv_sec = timeout / 1000;
95       ptv->tv_usec = (timeout % 1000) * 1000;
96     }
97   else if (timeout == INFTIM)
98     /* wait forever */
99     ptv = NULL;
100   else
101     {
102       errno = EINVAL;
103       return -1;
104     }
105
106   /* create fd sets and determine max fd */
107   maxfd = -1;
108   FD_ZERO (&rfds);
109   FD_ZERO (&wfds);
110   FD_ZERO (&efds);
111   for (i = 0; i < nfd; i++)
112     {
113       if (pfd[i].fd < 0)
114         continue;
115
116       if (pfd[i].events & (POLLIN | POLLRDNORM))
117         FD_SET (pfd[i].fd, &rfds);
118
119       /* see select(2): "the only exceptional condition detectable
120          is out-of-band data received on a socket", hence we push
121          POLLWRBAND events onto wfds instead of efds. */
122       if (pfd[i].events & (POLLOUT | POLLWRNORM | POLLWRBAND))
123         FD_SET (pfd[i].fd, &wfds);
124       if (pfd[i].events & (POLLPRI | POLLRDBAND))
125         FD_SET (pfd[i].fd, &efds);
126       if (pfd[i].fd >= maxfd
127           && (pfd[i].events & (POLLIN | POLLOUT | POLLPRI
128                                | POLLRDNORM | POLLRDBAND
129                                | POLLWRNORM | POLLWRBAND)))
130         {
131           maxfd = pfd[i].fd;
132           if (maxfd > FD_SETSIZE)
133             {
134               errno = EOVERFLOW;
135               return -1;
136             }
137         }
138     }
139
140   /* examine fd sets */
141   rc = select (maxfd + 1, &rfds, &wfds, &efds, ptv);
142
143   /* establish results */
144   if (rc > 0)
145     {
146       rc = 0;
147       for (i = 0; i < nfd; i++)
148         {
149           pfd[i].revents = 0;
150           if (pfd[i].fd < 0)
151             continue;
152
153           happened = 0;
154           if (FD_ISSET (pfd[i].fd, &rfds))
155             {
156               /* support for POLLHUP.  An hung up descriptor does not
157                  increase the return value! */
158               if (recv (pfd[i].fd, data, 64, MSG_PEEK) == -1)
159                 {
160                   if (errno == ESHUTDOWN || errno == ECONNRESET
161                       || errno == ECONNABORTED || errno == ENETRESET)
162                     pfd[i].revents |= POLLHUP;
163                 }
164               else
165                 happened |= POLLIN | POLLRDNORM;
166             }
167
168           if (FD_ISSET (pfd[i].fd, &wfds))
169             happened |= POLLOUT | POLLWRNORM | POLLWRBAND;
170
171           if (FD_ISSET (pfd[i].fd, &efds))
172             happened |= POLLPRI | POLLRDBAND;
173
174           pfd[i].revents |= pfd[i].events & happened;
175           rc += (happened > 0);
176         }
177     }
178
179   return rc;
180 }