Wire up SIGALRM handler
Set close on exec flag for child side of the socketpair Fix signal handling when not doing I/O logging
This commit is contained in:
114
src/script.c
114
src/script.c
@@ -111,6 +111,7 @@ static void script_run(const char *path, char *argv[], char *envp[], int);
|
|||||||
static void sigchild(int s);
|
static void sigchild(int s);
|
||||||
static void sigwinch(int s);
|
static void sigwinch(int s);
|
||||||
static void sync_winsize(int src, int dst);
|
static void sync_winsize(int src, int dst);
|
||||||
|
static void deliver_signal(pid_t pid, int signo);
|
||||||
|
|
||||||
/* sudo.c */
|
/* sudo.c */
|
||||||
extern struct plugin_container_list io_plugins;
|
extern struct plugin_container_list io_plugins;
|
||||||
@@ -339,6 +340,10 @@ script_execve(struct command_details *details, char *argv[], char *envp[],
|
|||||||
sa.sa_handler = sigchild;
|
sa.sa_handler = sigchild;
|
||||||
sigaction(SIGCHLD, &sa, NULL);
|
sigaction(SIGCHLD, &sa, NULL);
|
||||||
|
|
||||||
|
/* Catch SIGALRM for command timeout */
|
||||||
|
sa.sa_handler = handler;
|
||||||
|
sigaction(SIGALRM, &sa, NULL);
|
||||||
|
|
||||||
/* Ignore SIGPIPE from other end of socketpair. */
|
/* Ignore SIGPIPE from other end of socketpair. */
|
||||||
sa.sa_handler = SIG_IGN;
|
sa.sa_handler = SIG_IGN;
|
||||||
sigaction(SIGPIPE, &sa, NULL);
|
sigaction(SIGPIPE, &sa, NULL);
|
||||||
@@ -392,6 +397,10 @@ script_execve(struct command_details *details, char *argv[], char *envp[],
|
|||||||
error(1, "Can't set terminal to raw mode");
|
error(1, "Can't set terminal to raw mode");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Set command timeout if specified. */
|
||||||
|
if (ISSET(details->flags, CD_SET_TIMEOUT))
|
||||||
|
alarm(details->timeout);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Child will run the command in the pty, parent will pass data
|
* Child will run the command in the pty, parent will pass data
|
||||||
* to and from pty.
|
* to and from pty.
|
||||||
@@ -404,6 +413,7 @@ script_execve(struct command_details *details, char *argv[], char *envp[],
|
|||||||
case 0:
|
case 0:
|
||||||
/* child */
|
/* child */
|
||||||
close(sv[0]);
|
close(sv[0]);
|
||||||
|
fcntl(sv[1], F_SETFD, FD_CLOEXEC);
|
||||||
if (exec_setup(details) == 0) {
|
if (exec_setup(details) == 0) {
|
||||||
/* headed for execve() */
|
/* headed for execve() */
|
||||||
if (log_io)
|
if (log_io)
|
||||||
@@ -459,7 +469,7 @@ script_execve(struct command_details *details, char *argv[], char *envp[],
|
|||||||
zero_bytes(&input, sizeof(input));
|
zero_bytes(&input, sizeof(input));
|
||||||
zero_bytes(&output, sizeof(output));
|
zero_bytes(&output, sizeof(output));
|
||||||
for (;;) {
|
for (;;) {
|
||||||
/* XXX - racey */
|
/* XXX - racey */
|
||||||
if (!relaysig && recvsig != SIGCHLD) {
|
if (!relaysig && recvsig != SIGCHLD) {
|
||||||
relaysig = recvsig;
|
relaysig = recvsig;
|
||||||
recvsig = 0;
|
recvsig = 0;
|
||||||
@@ -491,8 +501,14 @@ script_execve(struct command_details *details, char *argv[], char *envp[],
|
|||||||
FD_SET(script_fds[SFD_MASTER], fdsw);
|
FD_SET(script_fds[SFD_MASTER], fdsw);
|
||||||
}
|
}
|
||||||
FD_SET(sv[0], fdsr);
|
FD_SET(sv[0], fdsr);
|
||||||
if (relaysig)
|
if (relaysig) {
|
||||||
FD_SET(sv[0], fdsw);
|
if (log_io) {
|
||||||
|
FD_SET(sv[0], fdsw);
|
||||||
|
} else {
|
||||||
|
/* nothing listening on sv[0], send directly */
|
||||||
|
deliver_signal(child, relaysig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
nready = select(maxfd + 1, fdsr, fdsw, NULL, NULL);
|
nready = select(maxfd + 1, fdsr, fdsw, NULL, NULL);
|
||||||
if (nready == -1) {
|
if (nready == -1) {
|
||||||
@@ -506,7 +522,7 @@ script_execve(struct command_details *details, char *argv[], char *envp[],
|
|||||||
if (n == -1) {
|
if (n == -1) {
|
||||||
if (errno == EINTR)
|
if (errno == EINTR)
|
||||||
continue;
|
continue;
|
||||||
if (errno != EAGAIN) {
|
if (log_io && errno != EAGAIN) {
|
||||||
/* Did the other end of the pipe go away? */
|
/* Did the other end of the pipe go away? */
|
||||||
cstat->type = CMD_ERRNO;
|
cstat->type = CMD_ERRNO;
|
||||||
cstat->val = errno;
|
cstat->val = errno;
|
||||||
@@ -629,14 +645,61 @@ script_execve(struct command_details *details, char *argv[], char *envp[],
|
|||||||
return cstat->type == CMD_ERRNO ? -1 : 0;
|
return cstat->type == CMD_ERRNO ? -1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
deliver_signal(pid_t pid, int signo)
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
|
||||||
|
/* Handle signal from parent. */
|
||||||
|
sudo_debug(8, "signal %d from parent", signo);
|
||||||
|
switch (signo) {
|
||||||
|
case SIGKILL:
|
||||||
|
_exit(1); /* XXX */
|
||||||
|
/* NOTREACHED */
|
||||||
|
case SIGHUP:
|
||||||
|
case SIGTERM:
|
||||||
|
case SIGINT:
|
||||||
|
case SIGQUIT:
|
||||||
|
case SIGTSTP:
|
||||||
|
/* relay signal to child */
|
||||||
|
killpg(pid, signo);
|
||||||
|
break;
|
||||||
|
case SIGALRM:
|
||||||
|
/* command time out, kill child with increasing urgency */
|
||||||
|
killpg(pid, SIGHUP);
|
||||||
|
sleep(1);
|
||||||
|
killpg(pid, SIGTERM);
|
||||||
|
sleep(1);
|
||||||
|
killpg(pid, SIGKILL);
|
||||||
|
break;
|
||||||
|
case SIGUSR1:
|
||||||
|
/* foreground process, grant it controlling tty. */
|
||||||
|
do {
|
||||||
|
status = tcsetpgrp(script_fds[SFD_SLAVE], pid);
|
||||||
|
} while (status == -1 && errno == EINTR);
|
||||||
|
killpg(pid, SIGCONT);
|
||||||
|
break;
|
||||||
|
case SIGUSR2:
|
||||||
|
/* background process, I take controlling tty. */
|
||||||
|
do {
|
||||||
|
status = tcsetpgrp(script_fds[SFD_SLAVE], getpid());
|
||||||
|
} while (status == -1 && errno == EINTR);
|
||||||
|
killpg(pid, SIGCONT);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
warningx("unexpected signal from child: %d", signo);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
script_child(const char *path, char *argv[], char *envp[], int backchannel, int rbac)
|
script_child(const char *path, char *argv[], char *envp[], int backchannel, int rbac)
|
||||||
{
|
{
|
||||||
struct command_status cstat;
|
struct command_status cstat;
|
||||||
fd_set *fdsr;
|
fd_set *fdsr;
|
||||||
sigaction_t sa;
|
sigaction_t sa;
|
||||||
pid_t pid, self = getpid();
|
pid_t pid;
|
||||||
int n, signo, status;
|
int n, status;
|
||||||
|
|
||||||
recvsig = 0;
|
recvsig = 0;
|
||||||
|
|
||||||
@@ -649,8 +712,8 @@ script_child(const char *path, char *argv[], char *envp[], int backchannel, int
|
|||||||
sigemptyset(&sa.sa_mask);
|
sigemptyset(&sa.sa_mask);
|
||||||
sa.sa_flags = SA_RESTART;
|
sa.sa_flags = SA_RESTART;
|
||||||
sa.sa_handler = SIG_DFL;
|
sa.sa_handler = SIG_DFL;
|
||||||
sigaction(SIGCONT, &sa, NULL);
|
|
||||||
sigaction(SIGWINCH, &sa, NULL);
|
sigaction(SIGWINCH, &sa, NULL);
|
||||||
|
sigaction(SIGALRM, &sa, NULL);
|
||||||
|
|
||||||
/* Ignore any SIGTT{IN,OU} or SIGPIPE we get. */
|
/* Ignore any SIGTT{IN,OU} or SIGPIPE we get. */
|
||||||
sa.sa_handler = SIG_IGN;
|
sa.sa_handler = SIG_IGN;
|
||||||
@@ -774,7 +837,7 @@ script_child(const char *path, char *argv[], char *envp[], int backchannel, int
|
|||||||
error(1, "select failed");
|
error(1, "select failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* read child status */
|
/* read command from backchannel, should be a signal */
|
||||||
n = recv(backchannel, &cstat, sizeof(cstat), 0);
|
n = recv(backchannel, &cstat, sizeof(cstat), 0);
|
||||||
if (n == -1) {
|
if (n == -1) {
|
||||||
if (errno == EINTR)
|
if (errno == EINTR)
|
||||||
@@ -786,40 +849,7 @@ script_child(const char *path, char *argv[], char *envp[], int backchannel, int
|
|||||||
warningx("unexpected reply type on backchannel: %d", cstat.type);
|
warningx("unexpected reply type on backchannel: %d", cstat.type);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
signo = cstat.val;
|
deliver_signal(child, cstat.val);
|
||||||
|
|
||||||
/* Handle signal from parent. */
|
|
||||||
sudo_debug(8, "signal %d from parent", signo);
|
|
||||||
switch (signo) {
|
|
||||||
case SIGKILL:
|
|
||||||
_exit(1); /* XXX */
|
|
||||||
/* NOTREACHED */
|
|
||||||
case SIGHUP:
|
|
||||||
case SIGTERM:
|
|
||||||
case SIGINT:
|
|
||||||
case SIGQUIT:
|
|
||||||
case SIGTSTP:
|
|
||||||
/* relay signal to child */
|
|
||||||
killpg(child, signo);
|
|
||||||
break;
|
|
||||||
case SIGUSR1:
|
|
||||||
/* foreground process, grant it controlling tty. */
|
|
||||||
do {
|
|
||||||
status = tcsetpgrp(script_fds[SFD_SLAVE], child);
|
|
||||||
} while (status == -1 && errno == EINTR);
|
|
||||||
killpg(child, SIGCONT);
|
|
||||||
break;
|
|
||||||
case SIGUSR2:
|
|
||||||
/* background process, I take controlling tty. */
|
|
||||||
do {
|
|
||||||
status = tcsetpgrp(script_fds[SFD_SLAVE], self);
|
|
||||||
} while (status == -1 && errno == EINTR);
|
|
||||||
killpg(child, SIGCONT);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
warningx("unexpected signal from child: %d", signo);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_exit(1); /* XXX */
|
_exit(1); /* XXX */
|
||||||
|
@@ -689,8 +689,6 @@ exec_setup(struct command_details *details)
|
|||||||
}
|
}
|
||||||
if (ISSET(details->flags, CD_SET_UMASK))
|
if (ISSET(details->flags, CD_SET_UMASK))
|
||||||
(void) umask(details->umask);
|
(void) umask(details->umask);
|
||||||
if (ISSET(details->flags, CD_SET_TIMEOUT))
|
|
||||||
alarm(details->timeout);
|
|
||||||
if (details->chroot) {
|
if (details->chroot) {
|
||||||
if (chroot(details->chroot) != 0 || chdir("/") != 0) {
|
if (chroot(details->chroot) != 0 || chdir("/") != 0) {
|
||||||
warning("unable to change root to %s", details->chroot);
|
warning("unable to change root to %s", details->chroot);
|
||||||
|
Reference in New Issue
Block a user