119261079SEd Maste /*
219261079SEd Maste * Copyright (c) 2001 Markus Friedl. All rights reserved.
319261079SEd Maste * Copyright (c) 2021 Darren Tucker (dtucker at dtucker net).
419261079SEd Maste *
519261079SEd Maste * Redistribution and use in source and binary forms, with or without
619261079SEd Maste * modification, are permitted provided that the following conditions
719261079SEd Maste * are met:
819261079SEd Maste * 1. Redistributions of source code must retain the above copyright
919261079SEd Maste * notice, this list of conditions and the following disclaimer.
1019261079SEd Maste * 2. Redistributions in binary form must reproduce the above copyright
1119261079SEd Maste * notice, this list of conditions and the following disclaimer in the
1219261079SEd Maste * documentation and/or other materials provided with the distribution.
1319261079SEd Maste *
1419261079SEd Maste * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1519261079SEd Maste * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1619261079SEd Maste * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1719261079SEd Maste * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1819261079SEd Maste * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1919261079SEd Maste * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2019261079SEd Maste * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2119261079SEd Maste * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2219261079SEd Maste * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2319261079SEd Maste * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2419261079SEd Maste */
2519261079SEd Maste
2619261079SEd Maste #include "includes.h"
2719261079SEd Maste #ifndef HAVE_PSELECT
2819261079SEd Maste
2919261079SEd Maste #include <sys/types.h>
3019261079SEd Maste #include <sys/time.h>
3119261079SEd Maste #ifdef HAVE_SYS_SELECT_H
3219261079SEd Maste # include <sys/select.h>
3319261079SEd Maste #endif
3419261079SEd Maste
3519261079SEd Maste #include <errno.h>
3619261079SEd Maste #include <fcntl.h>
3719261079SEd Maste #include <signal.h>
3819261079SEd Maste #include <stdlib.h>
3919261079SEd Maste #include <string.h>
4019261079SEd Maste #include <unistd.h>
4119261079SEd Maste
4219261079SEd Maste #include "log.h"
4319261079SEd Maste #include "misc.h" /* for set_nonblock */
4419261079SEd Maste
4519261079SEd Maste #ifndef HAVE_SIGHANDLER_T
4619261079SEd Maste typedef void (*sighandler_t)(int);
4719261079SEd Maste #endif
4819261079SEd Maste
4919261079SEd Maste static sighandler_t saved_sighandler[_NSIG];
5019261079SEd Maste
5119261079SEd Maste /*
5219261079SEd Maste * Set up the descriptors. Because they are close-on-exec, in the case
5319261079SEd Maste * where sshd's re-exec fails notify_pipe will still point to a descriptor
5419261079SEd Maste * that was closed by the exec attempt but if that descriptor has been
5519261079SEd Maste * reopened then we'll attempt to use that. Ensure that notify_pipe is
5619261079SEd Maste * outside of the range used by sshd re-exec but within NFDBITS (so we don't
5719261079SEd Maste * need to expand the fd_sets).
5819261079SEd Maste */
5919261079SEd Maste #define REEXEC_MIN_FREE_FD (STDERR_FILENO + 4)
6019261079SEd Maste static int
pselect_notify_setup_fd(int * fd)6119261079SEd Maste pselect_notify_setup_fd(int *fd)
6219261079SEd Maste {
6319261079SEd Maste int r;
6419261079SEd Maste
6519261079SEd Maste if ((r = fcntl(*fd, F_DUPFD, REEXEC_MIN_FREE_FD)) < 0 ||
6619261079SEd Maste fcntl(r, F_SETFD, FD_CLOEXEC) < 0 || r >= FD_SETSIZE)
6719261079SEd Maste return -1;
6819261079SEd Maste (void)close(*fd);
6919261079SEd Maste return (*fd = r);
7019261079SEd Maste }
7119261079SEd Maste
7219261079SEd Maste /*
7319261079SEd Maste * we write to this pipe if a SIGCHLD is caught in order to avoid
7419261079SEd Maste * the race between select() and child_terminated
7519261079SEd Maste */
7619261079SEd Maste static pid_t notify_pid;
7719261079SEd Maste static int notify_pipe[2];
7819261079SEd Maste static void
pselect_notify_setup(void)7919261079SEd Maste pselect_notify_setup(void)
8019261079SEd Maste {
8119261079SEd Maste static int initialized;
8219261079SEd Maste
8319261079SEd Maste if (initialized && notify_pid == getpid())
8419261079SEd Maste return;
8519261079SEd Maste if (notify_pid == 0)
8619261079SEd Maste debug3_f("initializing");
8719261079SEd Maste else {
8819261079SEd Maste debug3_f("pid changed, reinitializing");
8919261079SEd Maste if (notify_pipe[0] != -1)
9019261079SEd Maste close(notify_pipe[0]);
9119261079SEd Maste if (notify_pipe[1] != -1)
9219261079SEd Maste close(notify_pipe[1]);
9319261079SEd Maste }
9419261079SEd Maste if (pipe(notify_pipe) == -1) {
9519261079SEd Maste error("pipe(notify_pipe) failed %s", strerror(errno));
9619261079SEd Maste } else if (pselect_notify_setup_fd(¬ify_pipe[0]) == -1 ||
9719261079SEd Maste pselect_notify_setup_fd(¬ify_pipe[1]) == -1) {
9819261079SEd Maste error("fcntl(notify_pipe, ...) failed %s", strerror(errno));
9919261079SEd Maste close(notify_pipe[0]);
10019261079SEd Maste close(notify_pipe[1]);
10119261079SEd Maste } else {
10219261079SEd Maste set_nonblock(notify_pipe[0]);
10319261079SEd Maste set_nonblock(notify_pipe[1]);
10419261079SEd Maste notify_pid = getpid();
10519261079SEd Maste debug3_f("pid %d saved %d pipe0 %d pipe1 %d", getpid(),
10619261079SEd Maste notify_pid, notify_pipe[0], notify_pipe[1]);
10719261079SEd Maste initialized = 1;
10819261079SEd Maste return;
10919261079SEd Maste }
11019261079SEd Maste notify_pipe[0] = -1; /* read end */
11119261079SEd Maste notify_pipe[1] = -1; /* write end */
11219261079SEd Maste }
11319261079SEd Maste static void
pselect_notify_parent(void)11419261079SEd Maste pselect_notify_parent(void)
11519261079SEd Maste {
11619261079SEd Maste if (notify_pipe[1] != -1)
11719261079SEd Maste (void)write(notify_pipe[1], "", 1);
11819261079SEd Maste }
11919261079SEd Maste static void
pselect_notify_prepare(fd_set * readset)12019261079SEd Maste pselect_notify_prepare(fd_set *readset)
12119261079SEd Maste {
12219261079SEd Maste if (notify_pipe[0] != -1)
12319261079SEd Maste FD_SET(notify_pipe[0], readset);
12419261079SEd Maste }
12519261079SEd Maste static void
pselect_notify_done(fd_set * readset)12619261079SEd Maste pselect_notify_done(fd_set *readset)
12719261079SEd Maste {
12819261079SEd Maste char c;
12919261079SEd Maste
13019261079SEd Maste if (notify_pipe[0] != -1 && FD_ISSET(notify_pipe[0], readset)) {
13119261079SEd Maste while (read(notify_pipe[0], &c, 1) != -1)
13219261079SEd Maste debug2_f("reading");
13319261079SEd Maste FD_CLR(notify_pipe[0], readset);
13419261079SEd Maste }
13519261079SEd Maste }
13619261079SEd Maste
13719261079SEd Maste /*ARGSUSED*/
13819261079SEd Maste static void
pselect_sig_handler(int sig)13919261079SEd Maste pselect_sig_handler(int sig)
14019261079SEd Maste {
14119261079SEd Maste int save_errno = errno;
14219261079SEd Maste
14319261079SEd Maste pselect_notify_parent();
14419261079SEd Maste if (saved_sighandler[sig] != NULL)
14519261079SEd Maste (*saved_sighandler[sig])(sig); /* call original handler */
14619261079SEd Maste errno = save_errno;
14719261079SEd Maste }
14819261079SEd Maste
14919261079SEd Maste /*
15019261079SEd Maste * A minimal implementation of pselect(2), built on top of select(2).
15119261079SEd Maste */
15219261079SEd Maste
15319261079SEd Maste int
pselect(int nfds,fd_set * readfds,fd_set * writefds,fd_set * exceptfds,const struct timespec * timeout,const sigset_t * mask)15419261079SEd Maste pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
15519261079SEd Maste const struct timespec *timeout, const sigset_t *mask)
15619261079SEd Maste {
15719261079SEd Maste int ret, sig, saved_errno, unmasked = 0;
15819261079SEd Maste sigset_t osig;
15919261079SEd Maste struct sigaction sa, osa;
16019261079SEd Maste struct timeval tv, *tvp = NULL;
16119261079SEd Maste
16219261079SEd Maste if (timeout != NULL) {
16319261079SEd Maste tv.tv_sec = timeout->tv_sec;
16419261079SEd Maste tv.tv_usec = timeout->tv_nsec / 1000;
16519261079SEd Maste tvp = &tv;
16619261079SEd Maste }
16719261079SEd Maste if (mask == NULL) /* no signal mask, just call select */
16819261079SEd Maste return select(nfds, readfds, writefds, exceptfds, tvp);
16919261079SEd Maste
17019261079SEd Maste /* For each signal we're unmasking, install our handler if needed. */
17119261079SEd Maste for (sig = 0; sig < _NSIG; sig++) {
17219261079SEd Maste if (sig == SIGKILL || sig == SIGSTOP || sigismember(mask, sig))
17319261079SEd Maste continue;
17419261079SEd Maste if (sigaction(sig, NULL, &sa) == 0 &&
17519261079SEd Maste sa.sa_handler != SIG_IGN && sa.sa_handler != SIG_DFL) {
17619261079SEd Maste unmasked = 1;
17719261079SEd Maste if (sa.sa_handler == pselect_sig_handler)
17819261079SEd Maste continue;
17919261079SEd Maste sa.sa_handler = pselect_sig_handler;
18019261079SEd Maste if (sigaction(sig, &sa, &osa) == 0) {
18119261079SEd Maste debug3_f("installing signal handler for %s, "
18219261079SEd Maste "previous %p", strsignal(sig),
18319261079SEd Maste osa.sa_handler);
18419261079SEd Maste saved_sighandler[sig] = osa.sa_handler;
18519261079SEd Maste }
18619261079SEd Maste }
18719261079SEd Maste }
18819261079SEd Maste if (unmasked) {
18919261079SEd Maste pselect_notify_setup();
19019261079SEd Maste pselect_notify_prepare(readfds);
191*e9e8876aSEd Maste nfds = MAX(nfds, notify_pipe[0] + 1);
19219261079SEd Maste }
19319261079SEd Maste
19419261079SEd Maste /* Unmask signals, call select then restore signal mask. */
19519261079SEd Maste sigprocmask(SIG_SETMASK, mask, &osig);
19619261079SEd Maste ret = select(nfds, readfds, writefds, exceptfds, tvp);
19719261079SEd Maste saved_errno = errno;
19819261079SEd Maste sigprocmask(SIG_SETMASK, &osig, NULL);
19919261079SEd Maste
20019261079SEd Maste if (unmasked)
20119261079SEd Maste pselect_notify_done(readfds);
20219261079SEd Maste errno = saved_errno;
20319261079SEd Maste return ret;
20419261079SEd Maste }
20519261079SEd Maste #endif
206