1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /* Copyright (c) 1988 AT&T */
28 /* All Rights Reserved */
29
30 #pragma ident "%Z%%M% %I% %E% SMI"
31
32 /*
33 * Emulation of select() system call using poll() system call.
34 *
35 * Assumptions:
36 * polling for input only is most common.
37 * polling for exceptional conditions is very rare.
38 *
39 * Note that is it not feasible to emulate all error conditions,
40 * in particular conditions that would return EFAULT are far too
41 * difficult to check for in a library routine.
42 */
43
44 #pragma weak _select = select
45
46 #include "lint.h"
47 #include <values.h>
48 #include <pthread.h>
49 #include <errno.h>
50 #include <sys/time.h>
51 #include <sys/types.h>
52 #include <sys/select.h>
53 #include <sys/poll.h>
54 #include <alloca.h>
55 #include "libc.h"
56
57 int
pselect(int nfds,fd_set * in0,fd_set * out0,fd_set * ex0,const timespec_t * tsp,const sigset_t * sigmask)58 pselect(int nfds, fd_set *in0, fd_set *out0, fd_set *ex0,
59 const timespec_t *tsp, const sigset_t *sigmask)
60 {
61 long *in, *out, *ex;
62 ulong_t m; /* bit mask */
63 int j; /* loop counter */
64 ulong_t b; /* bits to test */
65 int n, rv;
66 struct pollfd *pfd;
67 struct pollfd *p;
68 int lastj = -1;
69
70 /* "zero" is read-only, it could go in the text segment */
71 static fd_set zero = { 0 };
72
73 /*
74 * Check for invalid conditions at outset.
75 * Required for spec1170.
76 * SUSV3: We must behave as a cancellation point even if we fail early.
77 */
78 if (nfds < 0 || nfds > FD_SETSIZE) {
79 pthread_testcancel();
80 errno = EINVAL;
81 return (-1);
82 }
83 p = pfd = (struct pollfd *)alloca(nfds * sizeof (struct pollfd));
84
85 if (tsp != NULL) {
86 /* check timespec validity */
87 if (tsp->tv_nsec < 0 || tsp->tv_nsec >= NANOSEC ||
88 tsp->tv_sec < 0) {
89 pthread_testcancel();
90 errno = EINVAL;
91 return (-1);
92 }
93 }
94
95 /*
96 * If any input args are null, point them at the null array.
97 */
98 if (in0 == NULL)
99 in0 = &zero;
100 if (out0 == NULL)
101 out0 = &zero;
102 if (ex0 == NULL)
103 ex0 = &zero;
104
105 /*
106 * For each fd, if any bits are set convert them into
107 * the appropriate pollfd struct.
108 */
109 in = (long *)in0->fds_bits;
110 out = (long *)out0->fds_bits;
111 ex = (long *)ex0->fds_bits;
112 for (n = 0; n < nfds; n += NFDBITS) {
113 b = (ulong_t)(*in | *out | *ex);
114 for (j = 0, m = 1; b != 0; j++, b >>= 1, m <<= 1) {
115 if (b & 1) {
116 p->fd = n + j;
117 if (p->fd >= nfds)
118 goto done;
119 p->events = 0;
120 if (*in & m)
121 p->events |= POLLRDNORM;
122 if (*out & m)
123 p->events |= POLLWRNORM;
124 if (*ex & m)
125 p->events |= POLLRDBAND;
126 p++;
127 }
128 }
129 in++;
130 out++;
131 ex++;
132 }
133 done:
134 /*
135 * Now do the poll.
136 */
137 n = (int)(p - pfd); /* number of pollfd's */
138 do {
139 rv = _pollsys(pfd, (nfds_t)n, tsp, sigmask);
140 } while (rv < 0 && errno == EAGAIN);
141
142 if (rv < 0) /* no need to set bit masks */
143 return (rv);
144
145 if (rv == 0) {
146 /*
147 * Clear out bit masks, just in case.
148 * On the assumption that usually only
149 * one bit mask is set, use three loops.
150 */
151 if (in0 != &zero) {
152 in = (long *)in0->fds_bits;
153 for (n = 0; n < nfds; n += NFDBITS)
154 *in++ = 0;
155 }
156 if (out0 != &zero) {
157 out = (long *)out0->fds_bits;
158 for (n = 0; n < nfds; n += NFDBITS)
159 *out++ = 0;
160 }
161 if (ex0 != &zero) {
162 ex = (long *)ex0->fds_bits;
163 for (n = 0; n < nfds; n += NFDBITS)
164 *ex++ = 0;
165 }
166 return (0);
167 }
168
169 /*
170 * Check for EINVAL error case first to avoid changing any bits
171 * if we're going to return an error.
172 */
173 for (p = pfd, j = n; j-- > 0; p++) {
174 /*
175 * select will return EBADF immediately if any fd's
176 * are bad. poll will complete the poll on the
177 * rest of the fd's and include the error indication
178 * in the returned bits. This is a rare case so we
179 * accept this difference and return the error after
180 * doing more work than select would've done.
181 */
182 if (p->revents & POLLNVAL) {
183 errno = EBADF;
184 return (-1);
185 }
186 /*
187 * We would like to make POLLHUP available to select,
188 * checking to see if we have pending data to be read.
189 * BUT until we figure out how not to break Xsun's
190 * dependencies on select's existing features...
191 * This is what we _thought_ would work ... sigh!
192 */
193 /*
194 * if ((p->revents & POLLHUP) &&
195 * !(p->revents & (POLLRDNORM|POLLRDBAND))) {
196 * errno = EINTR;
197 * return (-1);
198 * }
199 */
200 }
201
202 /*
203 * Convert results of poll back into bits
204 * in the argument arrays.
205 *
206 * We assume POLLRDNORM, POLLWRNORM, and POLLRDBAND will only be set
207 * on return from poll if they were set on input, thus we don't
208 * worry about accidentally setting the corresponding bits in the
209 * zero array if the input bit masks were null.
210 *
211 * Must return number of bits set, not number of ready descriptors
212 * (as the man page says, and as poll() does).
213 */
214 rv = 0;
215 for (p = pfd; n-- > 0; p++) {
216 j = (int)(p->fd / NFDBITS);
217 /* have we moved into another word of the bit mask yet? */
218 if (j != lastj) {
219 /* clear all output bits to start with */
220 in = (long *)&in0->fds_bits[j];
221 out = (long *)&out0->fds_bits[j];
222 ex = (long *)&ex0->fds_bits[j];
223 /*
224 * In case we made "zero" read-only (e.g., with
225 * cc -R), avoid actually storing into it.
226 */
227 if (in0 != &zero)
228 *in = 0;
229 if (out0 != &zero)
230 *out = 0;
231 if (ex0 != &zero)
232 *ex = 0;
233 lastj = j;
234 }
235 if (p->revents) {
236 m = 1L << (p->fd % NFDBITS);
237 if (p->revents & POLLRDNORM) {
238 *in |= m;
239 rv++;
240 }
241 if (p->revents & POLLWRNORM) {
242 *out |= m;
243 rv++;
244 }
245 if (p->revents & POLLRDBAND) {
246 *ex |= m;
247 rv++;
248 }
249 /*
250 * Only set this bit on return if we asked about
251 * input conditions.
252 */
253 if ((p->revents & (POLLHUP|POLLERR)) &&
254 (p->events & POLLRDNORM)) {
255 if ((*in & m) == 0)
256 rv++; /* wasn't already set */
257 *in |= m;
258 }
259 /*
260 * Only set this bit on return if we asked about
261 * output conditions.
262 */
263 if ((p->revents & (POLLHUP|POLLERR)) &&
264 (p->events & POLLWRNORM)) {
265 if ((*out & m) == 0)
266 rv++; /* wasn't already set */
267 *out |= m;
268 }
269 /*
270 * Only set this bit on return if we asked about
271 * output conditions.
272 */
273 if ((p->revents & (POLLHUP|POLLERR)) &&
274 (p->events & POLLRDBAND)) {
275 if ((*ex & m) == 0)
276 rv++; /* wasn't already set */
277 *ex |= m;
278 }
279 }
280 }
281 return (rv);
282 }
283
284 int
select(int nfds,fd_set * in0,fd_set * out0,fd_set * ex0,struct timeval * tv)285 select(int nfds, fd_set *in0, fd_set *out0, fd_set *ex0, struct timeval *tv)
286 {
287 timespec_t ts;
288 timespec_t *tsp;
289
290 if (tv == NULL)
291 tsp = NULL;
292 else {
293 /* check timeval validity */
294 if (tv->tv_usec < 0 || tv->tv_usec >= MICROSEC) {
295 errno = EINVAL;
296 return (-1);
297 }
298 /*
299 * Convert timeval to timespec.
300 * To preserve compatibility with past behavior,
301 * when select was built upon poll(2), which has a
302 * minimum non-zero timeout of 1 millisecond, force
303 * a minimum non-zero timeout of 500 microseconds.
304 */
305 ts.tv_sec = tv->tv_sec;
306 ts.tv_nsec = tv->tv_usec * 1000;
307 if (ts.tv_nsec != 0 && ts.tv_nsec < 500000)
308 ts.tv_nsec = 500000;
309 tsp = &ts;
310 }
311
312 return (pselect(nfds, in0, out0, ex0, tsp, NULL));
313 }
314