/* Work around the bug in Solaris 7 whereby a fd that is opened on /dev/null will cause select/poll to hang when given a NULL timeout. Copyright (C) 2004 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* written by Mark D. Baushke */ /* * Observed on Solaris 7: * If /dev/null is in the readfds set, it will never be marked as * ready by the OS. In the case of a /dev/null fd being the only fd * in the select set and timeout == NULL, the select will hang. * If /dev/null is in the exceptfds set, it will not be set on * return from select(). */ #ifdef HAVE_CONFIG_H # include #endif /* HAVE_CONFIG_H */ #include #include #include #include #ifdef HAVE_UNISTD_H # include #endif /* HAVE_UNISTD_H */ #include "xtime.h" /* The rpl_select function calls the real select. */ #undef select static struct stat devnull; static int devnull_set = -1; int rpl_select (int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) { int ret = 0; /* Argument checking */ if (nfds < 1 || nfds > FD_SETSIZE) { errno = EINVAL; return -1; } /* Perform the initial stat on /dev/null */ if (devnull_set == -1) devnull_set = stat ("/dev/null", &devnull); if (devnull_set >= 0) { int fd; fd_set null_rfds, null_efds; int altered = 0; /* have the callers args have been modified? */ int count = 0; /* Count of /dev/null fds in all sets. */ int goodfd = 0; /* A non-/dev/null fd means call select() */ FD_ZERO (&null_rfds); FD_ZERO (&null_efds); for (fd = 0; fd < nfds; fd++) { /* Check the callers bits for interesting fds */ int isread = (readfds && FD_ISSET (fd, readfds)); int iswrite = (writefds && FD_ISSET (fd, writefds)); int isexcept = (exceptfds && FD_ISSET (fd, exceptfds)); /* Check interesting fds against /dev/null */ if (isread || iswrite || isexcept) { struct stat sb; /* Equivalent to /dev/null ? */ if (fstat (fd, &sb) >= 0 && sb.st_dev == devnull.st_dev && sb.st_ino == devnull.st_ino && sb.st_mode == devnull.st_mode && sb.st_uid == devnull.st_uid && sb.st_gid == devnull.st_gid && sb.st_size == devnull.st_size && sb.st_blocks == devnull.st_blocks && sb.st_blksize == devnull.st_blksize) { /* Save the interesting bits for later use. */ if (readfds != NULL && isread) { FD_SET (fd, &null_rfds); FD_CLR (fd, readfds); count++; altered = 1; /* We have made changes to args. */ } if (writefds != NULL && iswrite) goodfd = 1; if (exceptfds != NULL && isexcept) { FD_SET (fd, &null_efds); FD_CLR (fd, exceptfds); count++; altered = 1; /* We have made changes to args. */ } } else goodfd = 1; /* A non-/dev/null fd is present. */ } } /* Call select() if there is anything left after alterations. * Note: We may end up waiting for the entire duration of the * timeout even though the /dev/null fds might be ready right * now. Those are the breaks if we ever want to see any of the * other fds be ready. */ if (goodfd) ret = select (nfds, readfds, writefds, exceptfds, timeout); /* Fix up the fd sets for any changes we may have made. */ if (altered) { /* Add to the number of ready fds in all sets. * If ret < 0, then we still need to restore the fd sets, * but we should make sure that we do not add any /dev/null * fds to the count or things could get confused. */ if (ret >= 0) ret += count; /* Tell the caller that nothing is blocking the /dev/null fds */ for (fd = 0; fd < nfds; fd++) { if (readfds != NULL && FD_ISSET (fd, &null_rfds)) FD_SET (fd, readfds); if (exceptfds != NULL && FD_ISSET (fd, &null_efds)) FD_SET (fd, exceptfds); } } } else ret = select (nfds, readfds, writefds, exceptfds, timeout); return ret; }