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>
45 #endif /* HAVE_UNISTD_H */
50 static struct stat devnull
;
51 static int devnull_set
= -1;
53 rpl_select (int nfds
, fd_set
*readfds
, fd_set
*writefds
, fd_set
*exceptfds
,
54 struct timeval
*timeout
)
58 /* Argument checking */
59 if (nfds
< 1 || nfds
> FD_SETSIZE
)
65 /* Perform the initial stat on /dev/null */
66 if (devnull_set
== -1)
67 devnull_set
= stat ("/dev/null", &devnull
);
73 fd_set null_rfds
, null_wfds
;
74 bool altered
= false; /* Whether we have altered the caller's args.
80 for (fd
= 0; fd
< nfds
; fd
++)
82 /* Check the callers bits for interesting fds */
83 bool isread
= (readfds
&& FD_ISSET (fd
, readfds
));
84 bool isexcept
= (exceptfds
&& FD_ISSET (fd
, exceptfds
));
85 bool iswrite
= (writefds
&& FD_ISSET (fd
, writefds
));
87 /* Check interesting fds against /dev/null */
88 if (isread
|| iswrite
|| isexcept
)
92 /* Equivalent to /dev/null ? */
93 if (fstat (fd
, &sb
) >= 0
94 && sb
.st_dev
== devnull
.st_dev
95 && sb
.st_ino
== devnull
.st_ino
96 && sb
.st_mode
== devnull
.st_mode
97 && sb
.st_uid
== devnull
.st_uid
98 && sb
.st_gid
== devnull
.st_gid
99 && sb
.st_size
== devnull
.st_size
100 && sb
.st_blocks
== devnull
.st_blocks
101 && sb
.st_blksize
== devnull
.st_blksize
)
103 /* Save the interesting bits for later use. */
106 FD_SET (fd
, &null_rfds
);
107 FD_CLR (fd
, readfds
);
111 /* Pass exception bits through.
113 * At the moment, we only know that this bug
114 * exists in Solaris 7 and so this file should
115 * only be compiled on Solaris 7. Since Solaris 7
116 * never returns ready for exceptions on
117 * /dev/null, we probably could assume this too,
118 * but since Solaris 9 is known to always return
119 * ready for exceptions on /dev/null, pass this
120 * through in case any other systems turn out to
121 * do the same. Besides, this will cause the
122 * timeout to be processed as it would have been
125 maxfd
= MAX (maxfd
, fd
);
128 /* We know of no bugs involving selecting /dev/null
129 * writefds, but we also know that /dev/null is always
130 * ready for write. Therefore, since we have already
131 * performed all the necessary processing, avoid calling
132 * the system select for this case.
134 FD_SET (fd
, &null_wfds
);
135 FD_CLR (fd
, writefds
);
140 /* A non-/dev/null fd is present. */
141 maxfd
= MAX (maxfd
, fd
);
147 /* we need to call select, one way or another. */
150 /* We already have some ready bits set, so timeout immediately
151 * if no bits are set.
153 struct timeval ztime
;
156 ret
= select (maxfd
+ 1, readfds
, writefds
, exceptfds
, &ztime
);
159 /* Timeout. Zero the sets since the system select might
171 /* No /dev/null fds. Call select just as the user specified. */
172 ret
= select (maxfd
+ 1, readfds
, writefds
, exceptfds
, timeout
);
176 * Borrowed from the Solaris 7 man page for select(3c):
178 * On successful completion, the objects pointed to by the
179 * readfds, writefds, and exceptfds arguments are modified to
180 * indicate which file descriptors are ready for reading,
181 * ready for writing, or have an error condition pending,
182 * respectively. For each file descriptor less than nfds, the
183 * corresponding bit will be set on successful completion if
184 * it was set on input and the associated condition is true
185 * for that file descriptor.
187 * On failure, the objects pointed to by the readfds,
188 * writefds, and exceptfds arguments are not modified. If the
189 * timeout interval expires without the specified condition
190 * being true for any of the specified file descriptors, the
191 * objects pointed to by the readfs, writefs, and errorfds
192 * arguments have all bits set to 0.
194 * On successful completion, select() returns the total number
195 * of bits set in the bit masks. Otherwise, -1 is returned,
196 * and errno is set to indicate the error.
199 /* Fix up the fd sets for any changes we may have made. */
202 /* Tell the caller that nothing is blocking the /dev/null fds */
203 for (fd
= 0; fd
< nfds
; fd
++)
205 /* If ret < 0, then we still need to restore the fd sets. */
206 if (FD_ISSET (fd
, &null_rfds
))
208 FD_SET (fd
, readfds
);
212 if (FD_ISSET (fd
, &null_wfds
))
214 FD_SET (fd
, writefds
);
222 ret
= select (nfds
, readfds
, writefds
, exceptfds
, timeout
);