1 /* Work around the bug in Solaris 7 whereby a fd that is opened on 2 /dev/null will cause select/poll to hang when given a NULL timeout. 3 4 Copyright (C) 2004 Free Software Foundation, Inc. 5 6 This program is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 2, or (at your option) 9 any later version. 10 11 This program is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with this program; if not, write to the Free Software Foundation, 18 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ 19 #include <sys/cdefs.h> 20 __RCSID("$NetBSD: sunos57-select.c,v 1.2 2016/05/17 14:00:09 christos Exp $"); 21 22 23 /* written by Mark D. Baushke */ 24 25 /* 26 * Observed on Solaris 7: 27 * If /dev/null is in the readfds set, it will never be marked as 28 * ready by the OS. In the case of a /dev/null fd being the only fd 29 * in the select set and timeout == NULL, the select will hang. 30 * If /dev/null is in the exceptfds set, it will not be set on 31 * return from select(). 32 */ 33 #ifdef HAVE_CONFIG_H 34 # include <config.h> 35 #endif /* HAVE_CONFIG_H */ 36 37 /* The rpl_select function calls the real select. */ 38 #undef select 39 40 #include <stdbool.h> 41 #include <stdio.h> 42 #include <sys/types.h> 43 #include <sys/stat.h> 44 #include <errno.h> 45 46 #ifdef HAVE_UNISTD_H 47 # include <unistd.h> 48 #endif /* HAVE_UNISTD_H */ 49 50 #include "minmax.h" 51 #include "xtime.h" 52 53 static struct stat devnull; 54 static int devnull_set = -1; 55 int 56 rpl_select (int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, 57 struct timeval *timeout) 58 { 59 int ret = 0; 60 61 /* Argument checking */ 62 if (nfds < 1 || nfds > FD_SETSIZE) 63 { 64 errno = EINVAL; 65 return -1; 66 } 67 68 /* Perform the initial stat on /dev/null */ 69 if (devnull_set == -1) 70 devnull_set = stat ("/dev/null", &devnull); 71 72 if (devnull_set >= 0) 73 { 74 int fd; 75 int maxfd = -1; 76 fd_set null_rfds, null_wfds; 77 bool altered = false; /* Whether we have altered the caller's args. 78 */ 79 80 FD_ZERO (&null_rfds); 81 FD_ZERO (&null_wfds); 82 83 for (fd = 0; fd < nfds; fd++) 84 { 85 /* Check the callers bits for interesting fds */ 86 bool isread = (readfds && FD_ISSET (fd, readfds)); 87 bool isexcept = (exceptfds && FD_ISSET (fd, exceptfds)); 88 bool iswrite = (writefds && FD_ISSET (fd, writefds)); 89 90 /* Check interesting fds against /dev/null */ 91 if (isread || iswrite || isexcept) 92 { 93 struct stat sb; 94 95 /* Equivalent to /dev/null ? */ 96 if (fstat (fd, &sb) >= 0 97 && sb.st_dev == devnull.st_dev 98 && sb.st_ino == devnull.st_ino 99 && sb.st_mode == devnull.st_mode 100 && sb.st_uid == devnull.st_uid 101 && sb.st_gid == devnull.st_gid 102 && sb.st_size == devnull.st_size 103 && sb.st_blocks == devnull.st_blocks 104 && sb.st_blksize == devnull.st_blksize) 105 { 106 /* Save the interesting bits for later use. */ 107 if (isread) 108 { 109 FD_SET (fd, &null_rfds); 110 FD_CLR (fd, readfds); 111 altered = true; 112 } 113 if (isexcept) 114 /* Pass exception bits through. 115 * 116 * At the moment, we only know that this bug 117 * exists in Solaris 7 and so this file should 118 * only be compiled on Solaris 7. Since Solaris 7 119 * never returns ready for exceptions on 120 * /dev/null, we probably could assume this too, 121 * but since Solaris 9 is known to always return 122 * ready for exceptions on /dev/null, pass this 123 * through in case any other systems turn out to 124 * do the same. Besides, this will cause the 125 * timeout to be processed as it would have been 126 * otherwise. 127 */ 128 maxfd = MAX (maxfd, fd); 129 if (iswrite) 130 { 131 /* We know of no bugs involving selecting /dev/null 132 * writefds, but we also know that /dev/null is always 133 * ready for write. Therefore, since we have already 134 * performed all the necessary processing, avoid calling 135 * the system select for this case. 136 */ 137 FD_SET (fd, &null_wfds); 138 FD_CLR (fd, writefds); 139 altered = true; 140 } 141 } 142 else 143 /* A non-/dev/null fd is present. */ 144 maxfd = MAX (maxfd, fd); 145 } 146 } 147 148 if (maxfd >= 0) 149 { 150 /* we need to call select, one way or another. */ 151 if (altered) 152 { 153 /* We already have some ready bits set, so timeout immediately 154 * if no bits are set. 155 */ 156 struct timeval ztime; 157 ztime.tv_sec = 0; 158 ztime.tv_usec = 0; 159 ret = select (maxfd + 1, readfds, writefds, exceptfds, &ztime); 160 if (ret == 0) 161 { 162 /* Timeout. Zero the sets since the system select might 163 * not have. 164 */ 165 if (readfds) 166 FD_ZERO (readfds); 167 if (exceptfds) 168 FD_ZERO (exceptfds); 169 if (writefds) 170 FD_ZERO (writefds); 171 } 172 } 173 else 174 /* No /dev/null fds. Call select just as the user specified. */ 175 ret = select (maxfd + 1, readfds, writefds, exceptfds, timeout); 176 } 177 178 /* 179 * Borrowed from the Solaris 7 man page for select(3c): 180 * 181 * On successful completion, the objects pointed to by the 182 * readfds, writefds, and exceptfds arguments are modified to 183 * indicate which file descriptors are ready for reading, 184 * ready for writing, or have an error condition pending, 185 * respectively. For each file descriptor less than nfds, the 186 * corresponding bit will be set on successful completion if 187 * it was set on input and the associated condition is true 188 * for that file descriptor. 189 * 190 * On failure, the objects pointed to by the readfds, 191 * writefds, and exceptfds arguments are not modified. If the 192 * timeout interval expires without the specified condition 193 * being true for any of the specified file descriptors, the 194 * objects pointed to by the readfs, writefs, and errorfds 195 * arguments have all bits set to 0. 196 * 197 * On successful completion, select() returns the total number 198 * of bits set in the bit masks. Otherwise, -1 is returned, 199 * and errno is set to indicate the error. 200 */ 201 202 /* Fix up the fd sets for any changes we may have made. */ 203 if (altered) 204 { 205 /* Tell the caller that nothing is blocking the /dev/null fds */ 206 for (fd = 0; fd < nfds; fd++) 207 { 208 /* If ret < 0, then we still need to restore the fd sets. */ 209 if (FD_ISSET (fd, &null_rfds)) 210 { 211 FD_SET (fd, readfds); 212 if (ret >= 0) 213 ret++; 214 } 215 if (FD_ISSET (fd, &null_wfds)) 216 { 217 FD_SET (fd, writefds); 218 if (ret >= 0) 219 ret++; 220 } 221 } 222 } 223 } 224 else 225 ret = select (nfds, readfds, writefds, exceptfds, timeout); 226 227 return ret; 228 } 229