/* * Copyright (c) 2001 Markus Friedl. All rights reserved. * Copyright (c) 2021 Darren Tucker (dtucker at dtucker net). * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #ifndef HAVE_PSELECT #include #include #ifdef HAVE_SYS_SELECT_H # include #endif #include #include #include #include #include #include #include "log.h" #ifndef HAVE_SIGHANDLER_T typedef void (*sighandler_t)(int); #endif static sighandler_t saved_sighandler[_NSIG]; static int notify_pipe[2]; /* 0 = read end, 1 = write end */ /* * Because the debugging for this is so noisy, we only output on the first * call, and suppress it thereafter. */ static int suppress_debug; static void pselect_set_nonblock(int fd) { int val; if ((val = fcntl(fd, F_GETFL)) == -1 || fcntl(fd, F_SETFL, val|O_NONBLOCK) == -1) error_f("fcntl: %s", strerror(errno)); } /* * we write to this pipe if a SIGCHLD is caught in order to avoid * the race between select() and child_terminated. */ static int pselect_notify_setup(void) { if (pipe(notify_pipe) == -1) { error("pipe(notify_pipe) failed %s", strerror(errno)); notify_pipe[0] = notify_pipe[1] = -1; return -1; } pselect_set_nonblock(notify_pipe[0]); pselect_set_nonblock(notify_pipe[1]); if (!suppress_debug) debug3_f("pipe0 %d pipe1 %d", notify_pipe[0], notify_pipe[1]); return 0; } static void pselect_notify_parent(void) { if (notify_pipe[1] != -1) (void)write(notify_pipe[1], "", 1); } static void pselect_notify_prepare(fd_set *readset) { if (notify_pipe[0] != -1) FD_SET(notify_pipe[0], readset); } static void pselect_notify_done(fd_set *readset) { char c; if (notify_pipe[0] != -1 && FD_ISSET(notify_pipe[0], readset)) { while (read(notify_pipe[0], &c, 1) != -1) debug2_f("reading"); FD_CLR(notify_pipe[0], readset); } (void)close(notify_pipe[0]); (void)close(notify_pipe[1]); } /*ARGSUSED*/ static void pselect_sig_handler(int sig) { int save_errno = errno; pselect_notify_parent(); if (saved_sighandler[sig] != NULL) (*saved_sighandler[sig])(sig); /* call original handler */ errno = save_errno; } /* * A minimal implementation of pselect(2), built on top of select(2). */ int pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timespec *timeout, const sigset_t *mask) { int ret, sig, saved_errno, unmasked = 0; sigset_t osig; struct sigaction sa, osa; struct timeval tv, *tvp = NULL; if (timeout != NULL) { tv.tv_sec = timeout->tv_sec; tv.tv_usec = timeout->tv_nsec / 1000; tvp = &tv; } if (mask == NULL) /* no signal mask, just call select */ return select(nfds, readfds, writefds, exceptfds, tvp); /* For each signal unmasked, save old handler and install ours. */ for (sig = 0; sig < _NSIG; sig++) { saved_sighandler[sig] = NULL; if (sig == SIGKILL || sig == SIGSTOP || sigismember(mask, sig)) continue; if (sigaction(sig, NULL, &sa) == 0 && sa.sa_handler != SIG_IGN && sa.sa_handler != SIG_DFL) { unmasked = 1; sa.sa_handler = pselect_sig_handler; if (sigaction(sig, &sa, &osa) == 0) { if (!suppress_debug) debug3_f("installed signal handler for" " %s, previous 0x%p", strsignal(sig), osa.sa_handler); saved_sighandler[sig] = osa.sa_handler; } } } if (unmasked) { if ((ret = pselect_notify_setup()) == -1) { saved_errno = ENOMEM; goto out; } pselect_notify_prepare(readfds); nfds = MAX(nfds, notify_pipe[0] + 1); } /* Unmask signals, call select then restore signal mask. */ sigprocmask(SIG_SETMASK, mask, &osig); ret = select(nfds, readfds, writefds, exceptfds, tvp); saved_errno = errno; sigprocmask(SIG_SETMASK, &osig, NULL); if (unmasked) pselect_notify_done(readfds); out: /* Restore signal handlers. */ for (sig = 0; sig < _NSIG; sig++) { if (saved_sighandler[sig] == NULL) continue; if (sigaction(sig, NULL, &sa) == 0) { sa.sa_handler = saved_sighandler[sig]; if (sigaction(sig, &sa, NULL) == 0) { if (!suppress_debug) debug3_f("restored signal handler for " "%s", strsignal(sig)); } else { error_f("failed to restore signal handler for " "%s: %s", strsignal(sig), strerror(errno)); } } } suppress_debug = 1; errno = saved_errno; return ret; } #endif