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