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.
4 Copyright (C) 2004 Free Software Foundation, Inc.
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)
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.
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. */
20 /* written by Mark D. Baushke */
23 * Observed on Solaris 7:
24 * If /dev/null is in the readfds set, it will never be marked as
25 * ready by the OS. In the case of a /dev/null fd being the only fd
26 * in the select set and timeout == NULL, the select will hang.
27 * If /dev/null is in the exceptfds set, it will not be set on
28 * return from select().
32 #endif /* HAVE_CONFIG_H */
34 /* The rpl_select function calls the real select. */
39 #include <sys/types.h>
43 #if defined(STDC_HEADERS) || defined(HAVE_STRING_H)
50 #endif /* HAVE_UNISTD_H */
55 __RCSID("$MirOS: src/gnu/usr.bin/cvs/lib/sunos57-select.c,v 1.5 2010/09/19 19:42:59 tg Exp $");
57 static struct stat devnull;
58 static int devnull_set = -1;
60 rpl_select (int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
61 struct timeval *timeout)
65 /* Argument checking */
66 if (nfds < 1 || nfds > FD_SETSIZE)
72 /* Perform the initial stat on /dev/null */
73 if (devnull_set == -1)
74 devnull_set = stat ("/dev/null", &devnull);
80 fd_set null_rfds, null_wfds;
81 bool altered = false; /* Whether we have altered the caller's args.
87 for (fd = 0; fd < nfds; fd++)
89 /* Check the callers bits for interesting fds */
90 bool isread = (readfds && FD_ISSET (fd, readfds));
91 bool isexcept = (exceptfds && FD_ISSET (fd, exceptfds));
92 bool iswrite = (writefds && FD_ISSET (fd, writefds));
94 /* Check interesting fds against /dev/null */
95 if (isread || iswrite || isexcept)
99 /* Equivalent to /dev/null ? */
100 if (fstat (fd, &sb) >= 0
101 && sb.st_dev == devnull.st_dev
102 && sb.st_ino == devnull.st_ino
103 && sb.st_mode == devnull.st_mode
104 && sb.st_uid == devnull.st_uid
105 && sb.st_gid == devnull.st_gid
106 && sb.st_size == devnull.st_size
107 && sb.st_blocks == devnull.st_blocks
108 && sb.st_blksize == devnull.st_blksize)
110 /* Save the interesting bits for later use. */
113 FD_SET (fd, &null_rfds);
114 FD_CLR (fd, readfds);
118 /* Pass exception bits through.
120 * At the moment, we only know that this bug
121 * exists in Solaris 7 and so this file should
122 * only be compiled on Solaris 7. Since Solaris 7
123 * never returns ready for exceptions on
124 * /dev/null, we probably could assume this too,
125 * but since Solaris 9 is known to always return
126 * ready for exceptions on /dev/null, pass this
127 * through in case any other systems turn out to
128 * do the same. Besides, this will cause the
129 * timeout to be processed as it would have been
132 maxfd = MAX (maxfd, fd);
135 /* We know of no bugs involving selecting /dev/null
136 * writefds, but we also know that /dev/null is always
137 * ready for write. Therefore, since we have already
138 * performed all the necessary processing, avoid calling
139 * the system select for this case.
141 FD_SET (fd, &null_wfds);
142 FD_CLR (fd, writefds);
147 /* A non-/dev/null fd is present. */
148 maxfd = MAX (maxfd, fd);
154 /* we need to call select, one way or another. */
157 /* We already have some ready bits set, so timeout immediately
158 * if no bits are set.
160 struct timeval ztime;
163 ret = select (maxfd + 1, readfds, writefds, exceptfds, &ztime);
166 /* Timeout. Zero the sets since the system select might
178 /* No /dev/null fds. Call select just as the user specified. */
179 ret = select (maxfd + 1, readfds, writefds, exceptfds, timeout);
183 * Borrowed from the Solaris 7 man page for select(3c):
185 * On successful completion, the objects pointed to by the
186 * readfds, writefds, and exceptfds arguments are modified to
187 * indicate which file descriptors are ready for reading,
188 * ready for writing, or have an error condition pending,
189 * respectively. For each file descriptor less than nfds, the
190 * corresponding bit will be set on successful completion if
191 * it was set on input and the associated condition is true
192 * for that file descriptor.
194 * On failure, the objects pointed to by the readfds,
195 * writefds, and exceptfds arguments are not modified. If the
196 * timeout interval expires without the specified condition
197 * being true for any of the specified file descriptors, the
198 * objects pointed to by the readfs, writefs, and errorfds
199 * arguments have all bits set to 0.
201 * On successful completion, select() returns the total number
202 * of bits set in the bit masks. Otherwise, -1 is returned,
203 * and errno is set to indicate the error.
206 /* Fix up the fd sets for any changes we may have made. */
209 /* Tell the caller that nothing is blocking the /dev/null fds */
210 for (fd = 0; fd < nfds; fd++)
212 /* If ret < 0, then we still need to restore the fd sets. */
213 if (FD_ISSET (fd, &null_rfds))
215 FD_SET (fd, readfds);
219 if (FD_ISSET (fd, &null_wfds))
221 FD_SET (fd, writefds);
229 ret = select (nfds, readfds, writefds, exceptfds, timeout);