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