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 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 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 }