1 /* 2 * Copyright (c) 2001 Markus Friedl. All rights reserved. 3 * Copyright (c) 2021 Darren Tucker (dtucker at dtucker net). 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #include "includes.h" 27 #ifndef HAVE_PSELECT 28 29 #include <sys/types.h> 30 #include <sys/time.h> 31 #ifdef HAVE_SYS_SELECT_H 32 # include <sys/select.h> 33 #endif 34 35 #include <errno.h> 36 #include <fcntl.h> 37 #include <signal.h> 38 #include <stdlib.h> 39 #include <string.h> 40 #include <unistd.h> 41 42 #include "log.h" 43 44 #ifndef HAVE_SIGHANDLER_T 45 typedef void (*sighandler_t)(int); 46 #endif 47 48 static sighandler_t saved_sighandler[_NSIG]; 49 static int notify_pipe[2]; /* 0 = read end, 1 = write end */ 50 51 /* 52 * Because the debugging for this is so noisy, we only output on the first 53 * call, and suppress it thereafter. 54 */ 55 static int suppress_debug; 56 57 static void 58 pselect_set_nonblock(int fd) 59 { 60 int val; 61 62 if ((val = fcntl(fd, F_GETFL)) == -1 || 63 fcntl(fd, F_SETFL, val|O_NONBLOCK) == -1) 64 error_f("fcntl: %s", strerror(errno)); 65 } 66 67 /* 68 * we write to this pipe if a SIGCHLD is caught in order to avoid 69 * the race between select() and child_terminated. 70 */ 71 static int 72 pselect_notify_setup(void) 73 { 74 if (pipe(notify_pipe) == -1) { 75 error("pipe(notify_pipe) failed %s", strerror(errno)); 76 notify_pipe[0] = notify_pipe[1] = -1; 77 return -1; 78 } 79 pselect_set_nonblock(notify_pipe[0]); 80 pselect_set_nonblock(notify_pipe[1]); 81 if (!suppress_debug) 82 debug3_f("pipe0 %d pipe1 %d", notify_pipe[0], notify_pipe[1]); 83 return 0; 84 } 85 static void 86 pselect_notify_parent(void) 87 { 88 if (notify_pipe[1] != -1) 89 (void)write(notify_pipe[1], "", 1); 90 } 91 static void 92 pselect_notify_prepare(fd_set *readset) 93 { 94 if (notify_pipe[0] != -1) 95 FD_SET(notify_pipe[0], readset); 96 } 97 static void 98 pselect_notify_done(fd_set *readset) 99 { 100 char c; 101 102 if (notify_pipe[0] != -1 && FD_ISSET(notify_pipe[0], readset)) { 103 while (read(notify_pipe[0], &c, 1) != -1) 104 debug2_f("reading"); 105 FD_CLR(notify_pipe[0], readset); 106 } 107 (void)close(notify_pipe[0]); 108 (void)close(notify_pipe[1]); 109 } 110 111 /*ARGSUSED*/ 112 static void 113 pselect_sig_handler(int sig) 114 { 115 int save_errno = errno; 116 117 pselect_notify_parent(); 118 if (saved_sighandler[sig] != NULL) 119 (*saved_sighandler[sig])(sig); /* call original handler */ 120 errno = save_errno; 121 } 122 123 /* 124 * A minimal implementation of pselect(2), built on top of select(2). 125 */ 126 127 int 128 pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, 129 const struct timespec *timeout, const sigset_t *mask) 130 { 131 int ret, sig, saved_errno, unmasked = 0; 132 sigset_t osig; 133 struct sigaction sa, osa; 134 struct timeval tv, *tvp = NULL; 135 136 if (timeout != NULL) { 137 tv.tv_sec = timeout->tv_sec; 138 tv.tv_usec = timeout->tv_nsec / 1000; 139 tvp = &tv; 140 } 141 if (mask == NULL) /* no signal mask, just call select */ 142 return select(nfds, readfds, writefds, exceptfds, tvp); 143 144 /* For each signal unmasked, save old handler and install ours. */ 145 for (sig = 0; sig < _NSIG; sig++) { 146 saved_sighandler[sig] = NULL; 147 if (sig == SIGKILL || sig == SIGSTOP || sigismember(mask, sig)) 148 continue; 149 if (sigaction(sig, NULL, &sa) == 0 && 150 sa.sa_handler != SIG_IGN && sa.sa_handler != SIG_DFL) { 151 unmasked = 1; 152 sa.sa_handler = pselect_sig_handler; 153 if (sigaction(sig, &sa, &osa) == 0) { 154 if (!suppress_debug) 155 debug3_f("installed signal handler for" 156 " %s, previous 0x%p", 157 strsignal(sig), osa.sa_handler); 158 saved_sighandler[sig] = osa.sa_handler; 159 } 160 } 161 } 162 if (unmasked) { 163 if ((ret = pselect_notify_setup()) == -1) { 164 saved_errno = ENOMEM; 165 goto out; 166 } 167 pselect_notify_prepare(readfds); 168 nfds = MAX(nfds, notify_pipe[0] + 1); 169 } 170 171 /* Unmask signals, call select then restore signal mask. */ 172 sigprocmask(SIG_SETMASK, mask, &osig); 173 ret = select(nfds, readfds, writefds, exceptfds, tvp); 174 saved_errno = errno; 175 sigprocmask(SIG_SETMASK, &osig, NULL); 176 177 if (unmasked) 178 pselect_notify_done(readfds); 179 180 out: 181 /* Restore signal handlers. */ 182 for (sig = 0; sig < _NSIG; sig++) { 183 if (saved_sighandler[sig] == NULL) 184 continue; 185 if (sigaction(sig, NULL, &sa) == 0) { 186 sa.sa_handler = saved_sighandler[sig]; 187 if (sigaction(sig, &sa, NULL) == 0) { 188 if (!suppress_debug) 189 debug3_f("restored signal handler for " 190 "%s", strsignal(sig)); 191 } else { 192 error_f("failed to restore signal handler for " 193 "%s: %s", strsignal(sig), strerror(errno)); 194 } 195 } 196 } 197 suppress_debug = 1; 198 errno = saved_errno; 199 return ret; 200 } 201 #endif 202