1 /* This file is part of the Sofia-SIP package.
2 
3    Copyright (C) 2005 Nokia Corporation.
4 
5    Contact: Pekka Pessi <pekka.pessi@nokia.com>
6 
7    This file is originally from GNU C library.
8 
9    Copyright (C) 1994,1996,1997,1998,1999,2001,2002
10    Free Software Foundation, Inc.
11 
12    This library is free software; you can redistribute it and/or
13    modify it under the terms of the GNU Lesser General Public
14    License as published by the Free Software Foundation; either
15    version 2.1 of the License, or (at your option) any later version.
16 
17    This library is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20    Lesser General Public License for more details.
21 
22    You should have received a copy of the GNU Lesser General Public
23    License along with the GNU C Library; if not, write to the Free
24    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
25    02111-1307 USA.  */
26 
27 #include "config.h"
28 
29 #if HAVE_SELECT
30 
31 #if HAVE_SYS_SELECT_H
32 #include <sys/select.h>
33 #endif
34 
35 #include "sofia-sip/su.h"
36 
37 #if HAVE_ALLOCA_H
38 #include <alloca.h>
39 #endif
40 
41 #if HAVE_SYS_TIME_H
42 #include <sys/time.h>
43 #endif
44 
45 #include <string.h>
46 
47 #include "sofia-sip/su_wait.h"
48 
49 #undef NBBY
50 #undef NFDBITS
51 #undef FDSETSIZE
52 #undef roundup
53 
54 #define	NBBY  8					/* bits in a byte */
55 #define NFDBITS	(sizeof(long) * NBBY)		/* bits per mask */
56 
57 #define FDSETSIZE(n) (((n) + NFDBITS - 1) / NFDBITS * (NFDBITS / NBBY))
58 #define roundup(n, x) (((n) + (x) - 1) / (x) * (x))
59 
60 /* Emulated poll() using select().
61 
62 This is used by su_wait().
63 
64 Poll the file descriptors described by the NFDS structures starting at
65 FDS.  If TIMEOUT is nonzero and not -1, allow TIMEOUT milliseconds for
66 an event to occur; if TIMEOUT is -1, block until an event occurs.
67 Returns the number of file descriptors with events, zero if timed out,
68 or -1 for errors.  */
69 
poll(struct pollfd * fds,nfds_t nfds,int timeout)70 int poll(struct pollfd *fds, nfds_t nfds, int timeout)
71 {
72   struct timeval tv;
73   struct pollfd *f;
74   int ready;
75   int maxfd = 0;
76 
77 #if HAVE_ALLOCA_H
78   static int max_fd_size;
79   int bytes;
80   fd_set *rset, *wset, *xset;
81 
82   if (!max_fd_size)
83     max_fd_size = getdtablesize ();
84 
85   bytes = FDSETSIZE (max_fd_size);
86 
87   rset = alloca (bytes);
88   wset = alloca (bytes);
89   xset = alloca (bytes);
90 
91   /* We can't call FD_ZERO, since FD_ZERO only works with sets
92      of exactly __FD_SETSIZE size.  */
93   memset (rset, 0, bytes);
94   memset (wset, 0, bytes);
95   memset (xset, 0, bytes);
96 #else
97   fd_set rset[1], wset[1], xset[1];
98 
99   FD_ZERO(rset);
100   FD_ZERO(wset);
101   FD_ZERO(xset);
102 #endif
103 
104   for (f = fds; f < &fds[nfds]; ++f)
105     {
106       f->revents = 0;
107       if (f->fd >= 0)
108 	{
109 #if HAVE_ALLOCA_H
110 	  if (f->fd >= max_fd_size)
111 	    {
112 	      /* The user provides a file descriptor number which is higher
113 		 than the maximum we got from the `getdtablesize' call.
114 		 Maybe this is ok so enlarge the arrays.  */
115 	      fd_set *nrset, *nwset, *nxset;
116 	      int nbytes;
117 
118 	      max_fd_size = roundup (f->fd, NFDBITS);
119 	      nbytes = FDSETSIZE (max_fd_size);
120 
121 	      nrset = alloca (nbytes);
122 	      nwset = alloca (nbytes);
123 	      nxset = alloca (nbytes);
124 
125 	      memset ((char *) nrset + bytes, 0, nbytes - bytes);
126 	      memset ((char *) nwset + bytes, 0, nbytes - bytes);
127 	      memset ((char *) nxset + bytes, 0, nbytes - bytes);
128 
129 	      rset = memcpy (nrset, rset, bytes);
130 	      wset = memcpy (nwset, wset, bytes);
131 	      xset = memcpy (nxset, xset, bytes);
132 
133 	      bytes = nbytes;
134 	    }
135 #else
136 	  if (f->fd >= FD_SETSIZE) {
137 	    errno = EBADF;
138 	    return -1;
139 	  }
140 #endif /* HAVE_ALLOCA_H */
141 
142 	  if (f->events & POLLIN)
143 	    FD_SET (f->fd, rset);
144 	  if (f->events & POLLOUT)
145 	    FD_SET (f->fd, wset);
146 	  if (f->events & POLLPRI)
147 	    FD_SET (f->fd, xset);
148 	  if (f->fd > maxfd && (f->events & (POLLIN|POLLOUT|POLLPRI)))
149 	    maxfd = f->fd;
150 	}
151     }
152 
153   tv.tv_sec = timeout / 1000;
154   tv.tv_usec = (timeout % 1000) * 1000;
155 
156   while (1)
157     {
158       ready = select (maxfd + 1, rset, wset, xset,
159 		      timeout == -1 ? NULL : &tv);
160 
161       /* It might be that one or more of the file descriptors is invalid.
162 	 We now try to find and mark them and then try again.  */
163       if (ready == -1 && errno == EBADF)
164 	{
165 	  struct timeval sngl_tv;
166 #if HAVE_ALLOCA_H
167 	  fd_set *sngl_rset = alloca (bytes);
168 	  fd_set *sngl_wset = alloca (bytes);
169 	  fd_set *sngl_xset = alloca (bytes);
170 
171 	  /* Clear the original set.  */
172 	  memset (rset, 0, bytes);
173 	  memset (wset, 0, bytes);
174 	  memset (xset, 0, bytes);
175 #else
176 	  fd_set sngl_rset[1];
177 	  fd_set sngl_wset[1];
178 	  fd_set sngl_xset[1];
179 
180 	  FD_ZERO(rset);
181 	  FD_ZERO(wset);
182 	  FD_ZERO(xset);
183 #endif
184 
185 	  /* This means we don't wait for input.  */
186 	  sngl_tv.tv_sec = 0;
187 	  sngl_tv.tv_usec = 0;
188 
189 	  maxfd = -1;
190 
191 	  /* Reset the return value.  */
192 	  ready = 0;
193 
194 	  for (f = fds; f < &fds[nfds]; ++f)
195 	    if (f->fd != -1 && (f->events & (POLLIN|POLLOUT|POLLPRI))
196 		&& (f->revents & POLLNVAL) == 0)
197 	      {
198 		int n;
199 
200 #if HAVE_ALLOCA_H
201 		memset (sngl_rset, 0, bytes);
202 		memset (sngl_wset, 0, bytes);
203 		memset (sngl_xset, 0, bytes);
204 #else
205 		FD_ZERO(rset);
206 		FD_ZERO(wset);
207 		FD_ZERO(xset);
208 #endif
209 
210 		if (f->events & POLLIN)
211 		  FD_SET (f->fd, sngl_rset);
212 		if (f->events & POLLOUT)
213 		  FD_SET (f->fd, sngl_wset);
214 		if (f->events & POLLPRI)
215 		  FD_SET (f->fd, sngl_xset);
216 
217 		n = select (f->fd + 1, sngl_rset, sngl_wset, sngl_xset,
218 			    &sngl_tv);
219 		if (n != -1)
220 		  {
221 		    /* This descriptor is ok.  */
222 		    if (f->events & POLLIN)
223 		      FD_SET (f->fd, rset);
224 		    if (f->events & POLLOUT)
225 		      FD_SET (f->fd, wset);
226 		    if (f->events & POLLPRI)
227 		      FD_SET (f->fd, xset);
228 		    if (f->fd > maxfd)
229 		      maxfd = f->fd;
230 		    if (n > 0)
231 		      /* Count it as being available.  */
232 		      ++ready;
233 		  }
234 		else if (errno == EBADF)
235 		  f->revents |= POLLNVAL;
236 	      }
237 	  /* Try again.  */
238 	  continue;
239 	}
240 
241       break;
242     }
243 
244   if (ready > 0)
245     for (f = fds; f < &fds[nfds]; ++f)
246       {
247 	if (f->fd >= 0)
248 	  {
249 	    if (FD_ISSET (f->fd, rset))
250 	      f->revents |= POLLIN;
251 	    if (FD_ISSET (f->fd, wset))
252 	      f->revents |= POLLOUT;
253 	    if (FD_ISSET (f->fd, xset))
254 	      f->revents |= POLLPRI;
255 	  }
256       }
257 
258   return ready;
259 }
260 
261 #endif
262