Convert the monitor process to the event subsystem.
This commit is contained in:
235
src/exec_pty.c
235
src/exec_pty.c
@@ -17,16 +17,10 @@
|
|||||||
#include <config.h>
|
#include <config.h>
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#ifdef HAVE_SYS_SYSMACROS_H
|
|
||||||
# include <sys/sysmacros.h>
|
|
||||||
#endif
|
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#ifdef HAVE_SYS_SELECT_H
|
|
||||||
# include <sys/select.h>
|
|
||||||
#endif /* HAVE_SYS_SELECT_H */
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#ifdef STDC_HEADERS
|
#ifdef STDC_HEADERS
|
||||||
# include <stdlib.h>
|
# include <stdlib.h>
|
||||||
@@ -1093,6 +1087,99 @@ handle_sigchld(int backchannel, struct command_status *cstat)
|
|||||||
debug_return_bool(alive);
|
debug_return_bool(alive);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct monitor_closure {
|
||||||
|
struct sudo_event_base *evbase;
|
||||||
|
struct sudo_event *errpipe_event;
|
||||||
|
struct sudo_event *backchannel_event;
|
||||||
|
struct sudo_event *signal_pipe_event;
|
||||||
|
struct command_status *cstat;
|
||||||
|
int backchannel;
|
||||||
|
bool alive;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
mon_signal_pipe_cb(int fd, int what, void *v)
|
||||||
|
{
|
||||||
|
struct monitor_closure *mc = v;
|
||||||
|
unsigned char signo;
|
||||||
|
ssize_t n;
|
||||||
|
debug_decl(mon_signal_pipe_cb, SUDO_DEBUG_EXEC);
|
||||||
|
|
||||||
|
n = read(fd, &signo, sizeof(signo));
|
||||||
|
if (n == -1) {
|
||||||
|
if (errno != EINTR && errno != EAGAIN) {
|
||||||
|
warning(_("error reading from signal pipe"));
|
||||||
|
sudo_ev_loopbreak(mc->evbase);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* Handle SIGCHLD specially and deliver other signals
|
||||||
|
* directly to the command.
|
||||||
|
*/
|
||||||
|
if (signo == SIGCHLD) {
|
||||||
|
mc->alive = handle_sigchld(mc->backchannel, mc->cstat);
|
||||||
|
if (!mc->alive) {
|
||||||
|
/* Remove all but the errpipe event. */
|
||||||
|
sudo_ev_del(mc->evbase, mc->backchannel_event);
|
||||||
|
sudo_ev_del(mc->evbase, mc->signal_pipe_event);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
deliver_signal(cmnd_pid, signo, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
debug_return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
mon_errpipe_cb(int fd, int what, void *v)
|
||||||
|
{
|
||||||
|
struct monitor_closure *mc = v;
|
||||||
|
ssize_t n;
|
||||||
|
debug_decl(mon_errpipe_cb, SUDO_DEBUG_EXEC);
|
||||||
|
|
||||||
|
/* read errno or EOF from command pipe */
|
||||||
|
n = read(fd, mc->cstat, sizeof(struct command_status));
|
||||||
|
if (n == -1) {
|
||||||
|
if (errno != EINTR && errno != EAGAIN) {
|
||||||
|
warning(_("error reading from pipe"));
|
||||||
|
sudo_ev_loopbreak(mc->evbase);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* Got errno or EOF, either way we are done with errpipe. */
|
||||||
|
sudo_ev_del(mc->evbase, mc->errpipe_event);
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
debug_return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
mon_backchannel_cb(int fd, int what, void *v)
|
||||||
|
{
|
||||||
|
struct monitor_closure *mc = v;
|
||||||
|
struct command_status cstmp;
|
||||||
|
ssize_t n;
|
||||||
|
debug_decl(mon_backchannel_cb, SUDO_DEBUG_EXEC);
|
||||||
|
|
||||||
|
/* read command from backchannel, should be a signal */
|
||||||
|
n = recv(fd, &cstmp, sizeof(cstmp), 0);
|
||||||
|
if (n != sizeof(cstmp)) {
|
||||||
|
if (n == -1) {
|
||||||
|
if (errno == EINTR || errno == EAGAIN)
|
||||||
|
debug_return;
|
||||||
|
warning(_("error reading from socketpair"));
|
||||||
|
} else {
|
||||||
|
/* short read or EOF, parent process died? */
|
||||||
|
}
|
||||||
|
sudo_ev_loopbreak(mc->evbase);
|
||||||
|
}
|
||||||
|
if (cstmp.type == CMD_SIGNO) {
|
||||||
|
deliver_signal(cmnd_pid, cstmp.val, true);
|
||||||
|
} else {
|
||||||
|
warningx(_("unexpected reply type on backchannel: %d"), cstmp.type);
|
||||||
|
}
|
||||||
|
debug_return;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Monitor process that creates a new session with the controlling tty,
|
* Monitor process that creates a new session with the controlling tty,
|
||||||
* resets signal handlers and forks a child to call exec_pty().
|
* resets signal handlers and forks a child to call exec_pty().
|
||||||
@@ -1104,12 +1191,10 @@ static int
|
|||||||
exec_monitor(struct command_details *details, int backchannel)
|
exec_monitor(struct command_details *details, int backchannel)
|
||||||
{
|
{
|
||||||
struct command_status cstat;
|
struct command_status cstat;
|
||||||
struct timeval tv;
|
struct sudo_event_base *evbase;
|
||||||
fd_set *fdsr;
|
struct monitor_closure mc;
|
||||||
sigaction_t sa;
|
sigaction_t sa;
|
||||||
int errpipe[2], maxfd, n;
|
int errpipe[2], n;
|
||||||
bool alive = true;
|
|
||||||
unsigned char signo;
|
|
||||||
debug_decl(exec_monitor, SUDO_DEBUG_EXEC);
|
debug_decl(exec_monitor, SUDO_DEBUG_EXEC);
|
||||||
|
|
||||||
/* Close unused fds. */
|
/* Close unused fds. */
|
||||||
@@ -1120,7 +1205,7 @@ exec_monitor(struct command_details *details, int backchannel)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* We use a pipe to atomically handle signal notification within
|
* We use a pipe to atomically handle signal notification within
|
||||||
* the select() loop.
|
* the event loop.
|
||||||
*/
|
*/
|
||||||
if (pipe_nonblock(signal_pipe) != 0)
|
if (pipe_nonblock(signal_pipe) != 0)
|
||||||
fatal(_("unable to create pipe"));
|
fatal(_("unable to create pipe"));
|
||||||
@@ -1141,7 +1226,7 @@ exec_monitor(struct command_details *details, int backchannel)
|
|||||||
/* Block all signals in mon_handler(). */
|
/* Block all signals in mon_handler(). */
|
||||||
sigfillset(&sa.sa_mask);
|
sigfillset(&sa.sa_mask);
|
||||||
|
|
||||||
/* Note: HP-UX select() will not be interrupted if SA_RESTART set */
|
/* Note: HP-UX poll() will not be interrupted if SA_RESTART is set. */
|
||||||
sa.sa_flags = SA_INTERRUPT;
|
sa.sa_flags = SA_INTERRUPT;
|
||||||
#ifdef SA_SIGINFO
|
#ifdef SA_SIGINFO
|
||||||
sa.sa_flags |= SA_SIGINFO;
|
sa.sa_flags |= SA_SIGINFO;
|
||||||
@@ -1246,93 +1331,51 @@ exec_monitor(struct command_details *details, int backchannel)
|
|||||||
} while (n == -1 && errno == EINTR);
|
} while (n == -1 && errno == EINTR);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Wait for errno on pipe, signal on backchannel or for SIGCHLD */
|
|
||||||
maxfd = MAX(MAX(errpipe[0], signal_pipe[0]), backchannel);
|
|
||||||
fdsr = ecalloc(howmany(maxfd + 1, NFDBITS), sizeof(fd_mask));
|
|
||||||
memset(&cstat, 0, sizeof(cstat));
|
|
||||||
tv.tv_sec = 0;
|
|
||||||
tv.tv_usec = 0;
|
|
||||||
for (;;) {
|
|
||||||
/* Check for signal on backchannel or errno on errpipe. */
|
|
||||||
FD_SET(backchannel, fdsr);
|
|
||||||
FD_SET(signal_pipe[0], fdsr);
|
|
||||||
if (errpipe[0] != -1)
|
|
||||||
FD_SET(errpipe[0], fdsr);
|
|
||||||
maxfd = MAX(MAX(errpipe[0], signal_pipe[0]), backchannel);
|
|
||||||
|
|
||||||
/* If command exited we just poll, there may be data on errpipe. */
|
|
||||||
n = select(maxfd + 1, fdsr, NULL, NULL, alive ? NULL : &tv);
|
|
||||||
if (n <= 0) {
|
|
||||||
if (n == 0)
|
|
||||||
goto done;
|
|
||||||
if (errno == EINTR || errno == ENOMEM)
|
|
||||||
continue;
|
|
||||||
warning("monitor: %s", _("select failed"));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FD_ISSET(signal_pipe[0], fdsr)) {
|
|
||||||
n = read(signal_pipe[0], &signo, sizeof(signo));
|
|
||||||
if (n == -1) {
|
|
||||||
if (errno == EINTR || errno == EAGAIN)
|
|
||||||
continue;
|
|
||||||
warning(_("error reading from signal pipe"));
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
/*
|
/*
|
||||||
* Handle SIGCHLD specially and deliver other signals
|
* Create new event base and register read events for the
|
||||||
* directly to the command.
|
* signal pipe, error pipe, and backchannel.
|
||||||
*/
|
*/
|
||||||
if (signo == SIGCHLD) {
|
evbase = sudo_ev_base_alloc();
|
||||||
if (!handle_sigchld(backchannel, &cstat))
|
if (evbase == NULL)
|
||||||
alive = false;
|
fatal(NULL);
|
||||||
} else {
|
|
||||||
deliver_signal(cmnd_pid, signo, false);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (errpipe[0] != -1 && FD_ISSET(errpipe[0], fdsr)) {
|
|
||||||
/* read errno or EOF from command pipe */
|
|
||||||
n = read(errpipe[0], &cstat, sizeof(cstat));
|
|
||||||
if (n == -1) {
|
|
||||||
if (errno == EINTR)
|
|
||||||
continue;
|
|
||||||
warning(_("error reading from pipe"));
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
/* Got errno or EOF, either way we are done with errpipe. */
|
|
||||||
FD_CLR(errpipe[0], fdsr);
|
|
||||||
close(errpipe[0]);
|
|
||||||
errpipe[0] = -1;
|
|
||||||
}
|
|
||||||
if (FD_ISSET(backchannel, fdsr)) {
|
|
||||||
struct command_status cstmp;
|
|
||||||
|
|
||||||
/* read command from backchannel, should be a signal */
|
memset(&cstat, 0, sizeof(cstat));
|
||||||
n = recv(backchannel, &cstmp, sizeof(cstmp), 0);
|
mc.cstat = &cstat;
|
||||||
if (n != sizeof(cstmp)) {
|
mc.evbase = evbase;
|
||||||
if (n == -1) {
|
mc.backchannel = backchannel;
|
||||||
if (errno == EINTR)
|
mc.alive = true;
|
||||||
continue;
|
|
||||||
warning(_("error reading from socketpair"));
|
|
||||||
goto done;
|
|
||||||
} else {
|
|
||||||
/* short read or EOF, parent process died? */
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (cstmp.type != CMD_SIGNO) {
|
|
||||||
warningx(_("unexpected reply type on backchannel: %d"),
|
|
||||||
cstmp.type);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
deliver_signal(cmnd_pid, cstmp.val, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
done:
|
mc.signal_pipe_event = sudo_ev_alloc(signal_pipe[0],
|
||||||
if (alive) {
|
SUDO_EV_READ|SUDO_EV_PERSIST, mon_signal_pipe_cb, &mc);
|
||||||
/* XXX An error occurred, should send an error back. */
|
if (mc.signal_pipe_event == NULL)
|
||||||
|
fatal(NULL);
|
||||||
|
if (sudo_ev_add(evbase, mc.signal_pipe_event, false) == -1)
|
||||||
|
fatal(_("unable to add event to queue"));
|
||||||
|
|
||||||
|
mc.errpipe_event = sudo_ev_alloc(errpipe[0],
|
||||||
|
SUDO_EV_READ|SUDO_EV_PERSIST, mon_errpipe_cb, &mc);
|
||||||
|
if (mc.errpipe_event == NULL)
|
||||||
|
fatal(NULL);
|
||||||
|
if (sudo_ev_add(evbase, mc.errpipe_event, false) == -1)
|
||||||
|
fatal(_("unable to add event to queue"));
|
||||||
|
|
||||||
|
mc.backchannel_event = sudo_ev_alloc(backchannel,
|
||||||
|
SUDO_EV_READ|SUDO_EV_PERSIST, mon_backchannel_cb, &mc);
|
||||||
|
if (mc.backchannel_event == NULL)
|
||||||
|
fatal(NULL);
|
||||||
|
if (sudo_ev_add(evbase, mc.backchannel_event, false) == -1)
|
||||||
|
fatal(_("unable to add event to queue"));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Wait for errno on pipe, signal on backchannel or for SIGCHLD.
|
||||||
|
* The event loop ends when the child is no longer running and
|
||||||
|
* the error pipe is closed.
|
||||||
|
*/
|
||||||
|
(void) sudo_ev_loop(evbase, 0);
|
||||||
|
if (mc.alive) {
|
||||||
|
/* XXX An error occurred, should send a message back. */
|
||||||
|
sudo_debug_printf(SUDO_DEBUG_ERROR,
|
||||||
|
"Command still running after event loop exit, sending SIGKILL");
|
||||||
kill(cmnd_pid, SIGKILL);
|
kill(cmnd_pid, SIGKILL);
|
||||||
} else {
|
} else {
|
||||||
/* Send parent status. */
|
/* Send parent status. */
|
||||||
|
Reference in New Issue
Block a user