Use a socketpair to pass signals from parent to child. Child will

now pass command status change info back via the socketpair.  This
allows the parent to distinguish between signals it has been sent
directly and signals the command has received.  It also means the
parent can once again print the signal notifications to the tty so
all writes to the pty master occur in the parent.  The command is
now always started in background mode with tty signals handled
by the parent.
This commit is contained in:
Todd C. Miller
2009-11-15 21:42:17 +00:00
parent 871b3fb125
commit dcf6602daa
6 changed files with 334 additions and 211 deletions

506
script.c
View File

@@ -18,6 +18,7 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/param.h> #include <sys/param.h>
#include <sys/socket.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/time.h> #include <sys/time.h>
#include <sys/wait.h> #include <sys/wait.h>
@@ -77,12 +78,16 @@
__unused static const char rcsid[] = "$Sudo$"; __unused static const char rcsid[] = "$Sudo$";
#endif /* lint */ #endif /* lint */
#define SFD_MASTER 0 #define SFD_MASTER 0
#define SFD_SLAVE 1 #define SFD_SLAVE 1
#define SFD_LOG 2 #define SFD_LOG 2
#define SFD_OUTPUT 3 #define SFD_OUTPUT 3
#define SFD_TIMING 4 #define SFD_TIMING 4
#define SFD_USERTTY 5 #define SFD_USERTTY 5
#define TERM_COOKED 0
#define TERM_CBREAK 1
#define TERM_RAW 2
struct script_buf { struct script_buf {
int len; /* buffer length (how much read in) */ int len; /* buffer length (how much read in) */
@@ -94,7 +99,9 @@ static int script_fds[6];
static sig_atomic_t alive = 1; static sig_atomic_t alive = 1;
static sig_atomic_t recvsig = 0; static sig_atomic_t recvsig = 0;
static sig_atomic_t ttymode = TERM_COOKED;
static sig_atomic_t foreground = 0; static sig_atomic_t foreground = 0;
static sig_atomic_t tty_initialized = 0;
static sigset_t ttyblock; static sigset_t ttyblock;
@@ -103,16 +110,17 @@ static int child_status;
static char slavename[PATH_MAX]; static char slavename[PATH_MAX];
static void script_child __P((char *path, char *argv[], int, int)); static int suspend_parent __P((int signo, struct script_buf *output,
static void script_run __P((char *path, char *argv[], int)); struct timeval *then, struct timeval *now, void *ofile, void *tfile));
static void sync_winsize __P((int src, int dst));
static void handler __P((int s));
static void sigchild __P((int s));
static void sigcont __P((int s));
static void sigrelay __P((int s));
static void sigwinch __P((int s));
static void flush_output __P((struct script_buf *output, struct timeval *then, static void flush_output __P((struct script_buf *output, struct timeval *then,
struct timeval *now, void *ofile, void *tfile)); struct timeval *now, void *ofile, void *tfile));
static void handler __P((int s));
static void script_child __P((char *path, char *argv[], int, int));
static void script_run __P((char *path, char *argv[], int));
static void sigchild __P((int s));
static void sigcont __P((int s));
static void sigwinch __P((int s));
static void sync_winsize __P((int src, int dst));
extern int get_pty __P((int *master, int *slave, char *name, size_t namesz)); extern int get_pty __P((int *master, int *slave, char *name, size_t namesz));
@@ -250,11 +258,6 @@ script_setup()
slavename, sizeof(slavename))) slavename, sizeof(slavename)))
log_error(USE_ERRNO, "Can't get pty"); log_error(USE_ERRNO, "Can't get pty");
/* Copy terminal attrs from user tty -> pty slave. */
if (!term_copy(script_fds[SFD_USERTTY], script_fds[SFD_SLAVE], 0))
log_error(USE_ERRNO, "Can't copy terminal attributes");
sync_winsize(script_fds[SFD_USERTTY], script_fds[SFD_SLAVE]);
/* /*
* Build a path containing the session id split into two-digit subdirs, * Build a path containing the session id split into two-digit subdirs,
* so ID 000001 becomes /var/log/sudo-session/00/00/01. * so ID 000001 becomes /var/log/sudo-session/00/00/01.
@@ -349,6 +352,90 @@ log_output(buf, n, then, now, ofile, tfile)
sigprocmask(SIG_SETMASK, &omask, NULL); sigprocmask(SIG_SETMASK, &omask, NULL);
} }
/*
* Suspend sudo if the underlying command is suspended.
* Returns SIGUSR1 if the child should be resume in foreground else SIGUSR2.
*/
static int
suspend_parent(signo, output, then, now, ofile, tfile)
int signo;
struct script_buf *output;
struct timeval *then;
struct timeval *now;
void *ofile;
void *tfile;
{
sigaction_t sa, osa;
int n, oldmode = ttymode, rval = 0;
switch (signo) {
case SIGTTOU:
case SIGTTIN:
/*
* If we are the foreground process, just resume the child.
* Otherwise, re-send the signal with the handler disabled.
*/
if (foreground) {
if (ttymode != TERM_RAW) {
do {
n = term_raw(script_fds[SFD_USERTTY], 1, 0);
} while (!n && errno == EINTR);
ttymode = TERM_RAW;
}
rval = SIGUSR1; /* resume child in foreground */
break;
}
ttymode = TERM_RAW;
/* FALLTHROUGH */
case SIGTSTP:
/* Flush any remaining output to master tty. */
flush_output(output, then, now, ofile, tfile);
/* Restore original tty mode before suspending. */
if (oldmode != TERM_COOKED) {
do {
n = term_restore(script_fds[SFD_USERTTY], 0);
} while (!n && errno == EINTR);
}
/* Suspend self and continue child when we resume. */
sa.sa_handler = SIG_DFL;
sigaction(signo, &sa, &osa);
#ifdef SCRIPT_DEBUG
warningx("kill parent %d", signo);
#endif
kill(parent, signo);
/*
* Only modify term if we are foreground process and either
* the old tty mode was not cooked or child got SIGTT{IN,OU}
*/
#ifdef SCRIPT_DEBUG
warningx("parent is in %sground, ttymode %d -> %d",
foreground ? "fore" : "back", oldmode, ttymode);
#endif
if (ttymode != TERM_COOKED) {
if (foreground) {
/* Set raw/cbreak mode. */
do {
n = term_raw(script_fds[SFD_USERTTY], 1,
ttymode == TERM_CBREAK);
} while (!n && errno == EINTR);
} else {
/* background process, no access to tty. */
ttymode = TERM_COOKED;
}
}
sigaction(signo, &osa, NULL);
rval = ttymode == TERM_RAW ? SIGUSR1 : SIGUSR2;
break;
}
return(rval);
}
/* /*
* This is a little bit tricky due to how POSIX job control works and * This is a little bit tricky due to how POSIX job control works and
* we fact that we have two different controlling terminals to deal with. * we fact that we have two different controlling terminals to deal with.
@@ -368,10 +455,11 @@ script_execv(path, argv)
char *path; char *path;
char *argv[]; char *argv[];
{ {
sigaction_t sa, osa; sigaction_t sa;
struct script_buf input, output; struct script_buf input, output;
struct timeval now, then; struct timeval now, then;
int n, nready, exitcode = 1; int n, nready, exitcode = 1;
int relaysig, sv[2];
fd_set *fdsr, *fdsw; fd_set *fdsr, *fdsw;
FILE *idfile; FILE *idfile;
#ifdef HAVE_ZLIB #ifdef HAVE_ZLIB
@@ -411,34 +499,53 @@ script_execv(path, argv)
sa.sa_flags = SA_RESTART; sa.sa_flags = SA_RESTART;
sa.sa_handler = sigwinch; sa.sa_handler = sigwinch;
sigaction(SIGWINCH, &sa, NULL); sigaction(SIGWINCH, &sa, NULL);
/* XXX - now get command status via sv (still need to detect child death) */
sa.sa_handler = sigchild; sa.sa_handler = sigchild;
sigaction(SIGCHLD, &sa, NULL); sigaction(SIGCHLD, &sa, NULL);
/* Ignore SIGPIPE from other end of socketpair. */
sa.sa_handler = SIG_IGN;
sigaction(SIGPIPE, &sa, NULL);
/* To update foreground/background state. */ /* To update foreground/background state. */
sa.sa_handler = sigcont; sa.sa_handler = sigcont;
sigaction(SIGCONT, &sa, NULL); sigaction(SIGCONT, &sa, NULL);
/* Relay SIG{HUP,TERM} from parent to child. */ /* Signals to relay from parent to child. */
sa.sa_handler = sigrelay; sa.sa_flags = 0; /* do not restart syscalls for these */
sa.sa_handler = handler;
sigaction(SIGHUP, &sa, NULL); sigaction(SIGHUP, &sa, NULL);
sigaction(SIGTERM, &sa, NULL); sigaction(SIGTERM, &sa, NULL);
sigaction(SIGINT, &sa, NULL); sigaction(SIGINT, &sa, NULL);
sigaction(SIGQUIT, &sa, NULL); sigaction(SIGQUIT, &sa, NULL);
/* Handler for tty-based signals */
sa.sa_flags = 0; /* do not restart syscalls for these three */
sa.sa_handler = handler;
sigaction(SIGTSTP, &sa, NULL); sigaction(SIGTSTP, &sa, NULL);
#if 0 /* XXX - keep? */
sigaction(SIGTTIN, &sa, NULL); sigaction(SIGTTIN, &sa, NULL);
sigaction(SIGTTOU, &sa, NULL); sigaction(SIGTTOU, &sa, NULL);
#endif
/*
* We communicate with the child over a bi-directional pipe.
* Parent sends signal info to child and child sends back wait status.
*/
if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) != 0)
log_error(USE_ERRNO, "cannot create sockets");
/* Only set tty to raw mode if we are the foreground process. */
if (foreground) { if (foreground) {
/* Copy terminal attrs from user tty -> pty slave. */
if (term_copy(script_fds[SFD_USERTTY], script_fds[SFD_SLAVE], 0)) {
tty_initialized = 1;
sync_winsize(script_fds[SFD_USERTTY], script_fds[SFD_SLAVE]);
}
/* Start out in cbreak mode. */
do { do {
n = term_raw(script_fds[SFD_USERTTY], 1); n = term_raw(script_fds[SFD_USERTTY], 1, 1);
} while (!n && errno == EINTR); } while (!n && errno == EINTR);
if (!n) if (!n)
log_error(USE_ERRNO, "Can't set terminal to raw mode"); log_error(USE_ERRNO, "Can't set terminal to raw mode");
ttymode = TERM_CBREAK;
} }
/* /*
@@ -451,14 +558,12 @@ script_execv(path, argv)
log_error(USE_ERRNO, "fork"); log_error(USE_ERRNO, "fork");
break; break;
case 0: case 0:
script_child(path, argv, foreground, rbac_enabled); close(sv[0]);
script_child(path, argv, sv[1], rbac_enabled);
/* NOTREACHED */ /* NOTREACHED */
break; break;
} }
close(sv[1]);
/* Do not need slave handle in parent */
close(script_fds[SFD_SLAVE]);
script_fds[SFD_SLAVE] = -1;
if ((idfile = fdopen(script_fds[SFD_LOG], "w")) == NULL) if ((idfile = fdopen(script_fds[SFD_LOG], "w")) == NULL)
log_error(USE_ERRNO, "fdopen"); log_error(USE_ERRNO, "fdopen");
@@ -505,21 +610,26 @@ script_execv(path, argv)
* and pass output from master to stdout and ofile. Note that * and pass output from master to stdout and ofile. Note that
* we've set things up such that master is > 3 (see sudo.c). * we've set things up such that master is > 3 (see sudo.c).
*/ */
fdsr = (fd_set *)emalloc2(howmany(script_fds[SFD_MASTER] + 1, NFDBITS), fdsr = (fd_set *)emalloc2(howmany(sv[0] + 1, NFDBITS), sizeof(fd_mask));
sizeof(fd_mask)); fdsw = (fd_set *)emalloc2(howmany(sv[0] + 1, NFDBITS), sizeof(fd_mask));
fdsw = (fd_set *)emalloc2(howmany(script_fds[SFD_MASTER] + 1, NFDBITS),
sizeof(fd_mask));
zero_bytes(&input, sizeof(input)); zero_bytes(&input, sizeof(input));
zero_bytes(&output, sizeof(output)); zero_bytes(&output, sizeof(output));
while (alive) { while (alive) {
/* XXX */
if (!relaysig && recvsig != SIGCHLD) {
relaysig = recvsig;
recvsig = 0;
}
if (input.off == input.len) if (input.off == input.len)
input.off = input.len = 0; input.off = input.len = 0;
if (output.off == output.len) if (output.off == output.len)
output.off = output.len = 0; output.off = output.len = 0;
zero_bytes(fdsw, howmany(script_fds[SFD_MASTER] + 1, NFDBITS) * sizeof(fd_mask)); zero_bytes(fdsw, howmany(sv[0] + 1, NFDBITS) * sizeof(fd_mask));
zero_bytes(fdsr, howmany(script_fds[SFD_MASTER] + 1, NFDBITS) * sizeof(fd_mask)); zero_bytes(fdsr, howmany(sv[0] + 1, NFDBITS) * sizeof(fd_mask));
if (input.len != sizeof(input.buf))
if (ttymode == TERM_RAW && input.len != sizeof(input.buf))
FD_SET(script_fds[SFD_USERTTY], fdsr); FD_SET(script_fds[SFD_USERTTY], fdsr);
if (output.len != sizeof(output.buf)) if (output.len != sizeof(output.buf))
FD_SET(script_fds[SFD_MASTER], fdsr); FD_SET(script_fds[SFD_MASTER], fdsr);
@@ -527,68 +637,58 @@ script_execv(path, argv)
FD_SET(STDOUT_FILENO, fdsw); FD_SET(STDOUT_FILENO, fdsw);
if (input.len > input.off) if (input.len > input.off)
FD_SET(script_fds[SFD_MASTER], fdsw); FD_SET(script_fds[SFD_MASTER], fdsw);
FD_SET(sv[0], fdsr);
if (relaysig)
FD_SET(sv[0], fdsw);
switch (recvsig) { nready = select(sv[0] + 1, fdsr, fdsw, NULL, NULL);
case SIGTTOU:
case SIGTTIN:
/*
* If we are the foreground process, just resume the child.
* Otherwise, re-send the signal with the handler disabled.
*/
if (foreground) {
recvsig = 0;
/* Set tty to raw mode and tell child it is in foregound. */
do {
n = term_raw(script_fds[SFD_USERTTY], 1);
} while (!n && errno == EINTR);
kill(child, SIGUSR1);
break;
}
/* FALLTHROUGH */
case SIGTSTP:
/* Flush any remaining output to master tty. */
flush_output(&output, &then, &now, ofile, tfile);
/* Restore tty mode */
do {
n = term_restore(script_fds[SFD_USERTTY]);
} while (!n && errno == EINTR);
/* Suspend self and continue child when we resume. */
sa.sa_handler = SIG_DFL;
sigaction(recvsig, &sa, &osa);
suspend:
kill(parent, recvsig);
/*
* If we were suspended due to tty I/O and sudo is still in
* the background, re-suspend.
*/
if (!foreground && (recvsig == SIGTTOU || recvsig == SIGTTIN))
goto suspend;
sigaction(recvsig, &osa, NULL);
recvsig = 0;
if (foreground) {
/* Set tty to raw mode and tell child it is in foregound. */
do {
n = term_raw(script_fds[SFD_USERTTY], 1);
} while (!n && errno == EINTR);
kill(child, SIGUSR1);
} else {
/* Tell child it is in the background. */
kill(child, SIGUSR2);
}
break;
default:
break;
}
nready = select(script_fds[SFD_MASTER] + 1, fdsr, fdsw, NULL, NULL);
if (nready == -1) { if (nready == -1) {
if (errno == EINTR) if (errno == EINTR)
continue; continue;
log_error(USE_ERRNO, "select failed"); log_error(USE_ERRNO, "select failed");
} }
if (FD_ISSET(sv[0], fdsr)) {
/* read child status */
n = read(sv[0], &child_status, sizeof(child_status));
if (n == -1) {
if (errno == EINTR)
continue;
if (errno != EAGAIN)
break;
} else if (n != sizeof(child_status)) {
break; /* EOF? */
}
if (WIFSTOPPED(child_status)) {
/* Suspend parent and tell child how to resume on return. */
#ifdef SCRIPT_DEBUG
warningx("child stopped, suspending parent");
#endif
relaysig = suspend_parent(WSTOPSIG(child_status),
&output, &then, &now, ofile, tfile);
/* XXX - write relaysig immediately? */
continue;
} else {
/* Child exited or was killed, either way we are done. */
if (WIFEXITED(child_status))
exitcode = WEXITSTATUS(child_status);
else if (WIFSIGNALED(child_status))
exitcode = WTERMSIG(child_status) | 128;
break;
}
}
if (FD_ISSET(sv[0], fdsw)) {
/* XXX - we rely on child to be suspended before we suspend us */
n = write(sv[0], &relaysig, sizeof(relaysig));
relaysig = 0;
if (n == -1) {
if (errno == EINTR)
continue;
if (errno != EAGAIN)
break;
} else if (n != sizeof(relaysig)) {
break; /* should not happen */
}
}
if (FD_ISSET(script_fds[SFD_USERTTY], fdsr)) { if (FD_ISSET(script_fds[SFD_USERTTY], fdsr)) {
n = read(script_fds[SFD_USERTTY], input.buf + input.len, n = read(script_fds[SFD_USERTTY], input.buf + input.len,
sizeof(input.buf) - input.len); sizeof(input.buf) - input.len);
@@ -663,31 +763,42 @@ script_execv(path, argv)
fclose(tfile); fclose(tfile);
#endif #endif
#ifdef HAVE_STRSIGNAL
if (WIFSIGNALED(child_status)) {
int signo = WTERMSIG(child_status);
if (signo && signo != SIGINT && signo != SIGPIPE) {
char *reason = strsignal(signo);
write(STDOUT_FILENO, reason, strlen(reason));
if (WCOREDUMP(child_status))
write(STDOUT_FILENO, " (core dumped)", 14);
write(STDOUT_FILENO, "\n", 1);
}
}
#endif
do { do {
n = term_restore(script_fds[SFD_USERTTY]); n = term_restore(script_fds[SFD_USERTTY], 0);
} while (!n && errno == EINTR); } while (!n && errno == EINTR);
if (WIFEXITED(child_status))
exitcode = WEXITSTATUS(child_status);
else if (WIFSIGNALED(child_status))
exitcode = WTERMSIG(child_status) | 128;
exit(exitcode); exit(exitcode);
} }
void void
script_child(path, argv, foreground, rbac_enabled) script_child(path, argv, backchannel, rbac_enabled)
char *path; char *path;
char *argv[]; char *argv[];
int foreground; int backchannel;
int rbac_enabled; int rbac_enabled;
{ {
sigaction_t sa; sigaction_t sa;
pid_t pid, self = getpid(); pid_t pid, self = getpid();
int signo, status, exitcode = 1; int nread, signo, status;
#ifndef TIOCSCTTY #ifndef TIOCSCTTY
int n; int n;
#endif #endif
recvsig = 0;
/* Close unused fds. */ /* Close unused fds. */
close(script_fds[SFD_MASTER]); close(script_fds[SFD_MASTER]);
close(script_fds[SFD_LOG]); close(script_fds[SFD_LOG]);
@@ -700,26 +811,19 @@ script_child(path, argv, foreground, rbac_enabled)
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(SIGCHLD, &sa, NULL);
sigaction(SIGCONT, &sa, NULL); sigaction(SIGCONT, &sa, NULL);
sigaction(SIGWINCH, &sa, NULL); sigaction(SIGWINCH, &sa, NULL);
/* Ignore SIGTT{IN,OU} if we get any. */ /* Ignore any SIGTT{IN,OU} or SIGPIPE we get. */
/* XXX - how can we since the pgrp is orphaned? */
sa.sa_handler = SIG_IGN; sa.sa_handler = SIG_IGN;
sigaction(SIGPIPE, &sa, NULL);
sigaction(SIGTTIN, &sa, NULL); sigaction(SIGTTIN, &sa, NULL);
sigaction(SIGTTOU, &sa, NULL); sigaction(SIGTTOU, &sa, NULL);
/* /* We want SIGCHLD to interrupt us. */
* Handle signals from parent and pass to child.
* We convert SIGUSR[12] to SIGCONT after special handling.
*/
sa.sa_flags = 0; /* do not restart syscalls for these signals. */ sa.sa_flags = 0; /* do not restart syscalls for these signals. */
sa.sa_handler = handler; sa.sa_handler = handler;
sigaction(SIGHUP, &sa, NULL); sigaction(SIGCHLD, &sa, NULL);
sigaction(SIGTERM, &sa, NULL);
sigaction(SIGUSR1, &sa, NULL);
sigaction(SIGUSR2, &sa, NULL);
/* /*
* Start a new session with the parent as the session leader * Start a new session with the parent as the session leader
@@ -759,10 +863,19 @@ script_child(path, argv, foreground, rbac_enabled)
_exit(1); _exit(1);
} }
if (child == 0) { if (child == 0) {
/* Reset signal handlers. */
sa.sa_flags = SA_RESTART; sa.sa_flags = SA_RESTART;
sa.sa_handler = SIG_DFL; sa.sa_handler = SIG_DFL;
sigaction(SIGHUP, &sa, NULL);
sigaction(SIGTERM, &sa, NULL);
sigaction(SIGINT, &sa, NULL);
sigaction(SIGQUIT, &sa, NULL);
sigaction(SIGTSTP, &sa, NULL);
sigaction(SIGTTIN, &sa, NULL); sigaction(SIGTTIN, &sa, NULL);
sigaction(SIGTTOU, &sa, NULL); sigaction(SIGTTOU, &sa, NULL);
sigaction(SIGUSR1, &sa, NULL);
sigaction(SIGUSR2, &sa, NULL);
/* setup tty and exec command */ /* setup tty and exec command */
script_run(path, argv, rbac_enabled); script_run(path, argv, rbac_enabled);
warning("unable to execute %s", path); warning("unable to execute %s", path);
@@ -770,84 +883,87 @@ script_child(path, argv, foreground, rbac_enabled)
} }
/* /*
* Put child in its own process group and make it the foreground * Put child in its own process group. We always start the command
* process of the session if sudo itself is run in the foreground. * in the background until it needs to be the foreground process.
*/ */
setpgid(child, child); setpgid(child, child);
if (foreground) {
do {
status = tcsetpgrp(script_fds[SFD_SLAVE], child);
} while (status == -1 && errno == EINTR);
if (status == -1)
warning("tcsetpgrp");
}
/* Wait for signal to arrive or for child to stop/exit. */ /* Wait for signal on backchannel or for SIGCHLD */
for (;;) { for (;;) {
while ((signo = recvsig)) { /* Read child status, assumes recvsig can only be SIGCHLD */
while (recvsig) {
recvsig = 0; recvsig = 0;
switch (signo) { /* read child status and relay to parent */
case SIGHUP: do {
case SIGTERM: pid = waitpid(child, &status, WUNTRACED|WNOHANG);
/* signal child; we'll get its status when we continue. */ } while (pid == -1 && errno == EINTR);
killpg(child, signo); if (pid == child) {
break; #ifdef SCRIPT_DEBUG
case SIGUSR1: if (WIFSTOPPED(status))
/* foreground process, grant it controlling tty. */ warningx("command stopped, signal %d", WSTOPSIG(status));
do { else if (WIFSIGNALED(status))
status = tcsetpgrp(script_fds[SFD_SLAVE], child); warningx("command killed, signal %d", WTERMSIG(status));
} while (status == -1 && errno == EINTR); else
/* FALLTHROUGH */ warningx("command exited?");
case SIGUSR2:
status = tcgetpgrp(script_fds[SFD_SLAVE]);
killpg(child, SIGCONT);
break;
}
}
pid = waitpid(child, &status, WUNTRACED);
if (pid != child) {
if (pid == -1 && errno == EINTR)
continue;
/* either no child or a fatal error, just exit */
break;
}
/* If child exited or was killed we are done. */
if (!WIFSTOPPED(status))
break;
/*
* Child was stopped by signal, signal parent and wait for SIGUSR[12].
*/
signo = WSTOPSIG(status);
/* Take back controlling tty while child is suspended. */
do {
status = tcsetpgrp(script_fds[SFD_SLAVE], self);
} while (status == -1 && errno == EINTR);
kill(parent, signo);
}
/* Command is no longer running. */
if (pid == child) {
if (WIFEXITED(status)) {
exitcode = WEXITSTATUS(status);
} else if (WIFSIGNALED(status)) {
#ifdef HAVE_STRSIGNAL
signo = WTERMSIG(status);
if (signo != SIGINT && signo != SIGPIPE) {
char *reason = strsignal(signo);
write(script_fds[SFD_MASTER], reason, strlen(reason));
if (WCOREDUMP(status))
write(script_fds[SFD_MASTER], " (core dumped)", 14);
write(script_fds[SFD_MASTER], "\n", 1);
}
#endif #endif
exitcode = WTERMSIG(status) | 128; if (write(backchannel, &status, sizeof(status)) != sizeof(status))
break; /* XXX - error, kill child and exit */
#ifdef SCRIPT_DEBUG
warningx("sent signo to parent");
#endif
if (!WIFSTOPPED(status)) {
_exit(1); /* child dead */
}
}
}
nread = read(backchannel, &signo, sizeof(signo));
if (nread == -1) {
if (errno != EINTR)
break; /* XXX - error, kill child and exit */
continue;
}
if (nread != sizeof(signo)) {
/* EOF? */
break;
}
/* Handle signal from parent. */
#ifdef SCRIPT_DEBUG
warningx("signal %d from parent", signo);
#endif
switch (signo) {
case SIGKILL:
_exit(1);
/* 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:
/* XXX - warn? */
break;
} }
} }
_exit(exitcode); _exit(1);
} }
static void static void
@@ -945,11 +1061,18 @@ sigcont(s)
/* Did we get continued in the foreground or background? */ /* Did we get continued in the foreground or background? */
foreground = tcgetpgrp(script_fds[SFD_USERTTY]) == parent; foreground = tcgetpgrp(script_fds[SFD_USERTTY]) == parent;
if (foreground && !tty_initialized) {
if (term_copy(script_fds[SFD_USERTTY], script_fds[SFD_SLAVE], 0)) {
tty_initialized = 1;
sync_winsize(script_fds[SFD_USERTTY], script_fds[SFD_SLAVE]);
}
}
errno = serrno; errno = serrno;
} }
/* /*
* Signal handler for child * Handler for SIGCHLD in parent
*/ */
static void static void
sigchild(s) sigchild(s)
@@ -982,19 +1105,8 @@ handler(s)
} }
/* /*
* Handler for SIG{HUP,TERM} in parent, relays to child. * Handler for SIGWINCH in parent
*/ */
static void
sigrelay(s)
int s;
{
int serrno = errno;
kill(child, s);
errno = serrno;
}
static void static void
sigwinch(s) sigwinch(s)
int s; int s;

2
sudo.c
View File

@@ -1470,7 +1470,7 @@ cleanup(gotsignal)
} }
#ifdef _PATH_SUDO_TRANSCRIPT #ifdef _PATH_SUDO_TRANSCRIPT
if (def_transcript) if (def_transcript)
term_restore(STDIN_FILENO); term_restore(STDIN_FILENO, 0);
#endif #endif
} }

4
sudo.h
View File

@@ -275,8 +275,8 @@ void script_setup __P((void));
int term_cbreak __P((int)); int term_cbreak __P((int));
int term_copy __P((int, int, int)); int term_copy __P((int, int, int));
int term_noecho __P((int)); int term_noecho __P((int));
int term_raw __P((int, int)); int term_raw __P((int, int, int));
int term_restore __P((int)); int term_restore __P((int, int));
char *get_timestr __P((time_t, int)); char *get_timestr __P((time_t, int));
YY_DECL; YY_DECL;

View File

@@ -155,7 +155,7 @@ static int stack_top;
extern time_t get_date __P((char *)); extern time_t get_date __P((char *));
extern char *get_timestr __P((time_t, int)); extern char *get_timestr __P((time_t, int));
extern int term_cbreak __P((int)); extern int term_cbreak __P((int));
extern int term_restore __P((int)); extern int term_restore __P((int, int));
extern void zero_bytes __P((volatile void *, size_t)); extern void zero_bytes __P((volatile void *, size_t));
void cleanup __P((int)); void cleanup __P((int));
@@ -351,7 +351,7 @@ main(argc, argv)
} while (nread); } while (nread);
} }
} }
term_restore(STDOUT_FILENO); term_restore(STDOUT_FILENO, 0);
exit(0); exit(0);
} }
@@ -813,7 +813,7 @@ void
cleanup(signo) cleanup(signo)
int signo; int signo;
{ {
term_restore(STDOUT_FILENO); term_restore(STDOUT_FILENO, 0);
if (signo) if (signo)
kill(getpid(), signo); kill(getpid(), signo);
} }

25
term.c
View File

@@ -86,6 +86,8 @@ __unused static const char rcsid[] = "$Sudo$";
# define tcsetattr(f, a, t) ioctl(f, a, t) # define tcsetattr(f, a, t) ioctl(f, a, t)
# undef TCSAFLUSH # undef TCSAFLUSH
# define TCSAFLUSH TCSETAF # define TCSAFLUSH TCSETAF
# undef TCSADRAIN
# define TCSADRAIN TCSETAW
# else /* SGTTY */ # else /* SGTTY */
# undef termios # undef termios
# define termios sgttyb # define termios sgttyb
@@ -94,6 +96,8 @@ __unused static const char rcsid[] = "$Sudo$";
# define tcsetattr(f, a, t) ioctl(f, a, t) # define tcsetattr(f, a, t) ioctl(f, a, t)
# undef TCSAFLUSH # undef TCSAFLUSH
# define TCSAFLUSH TIOCSETP # define TCSAFLUSH TIOCSETP
# undef TCSADRAIN
# define TCSADRAIN TIOCSETN
# endif /* HAVE_TERMIO_H */ # endif /* HAVE_TERMIO_H */
#endif /* HAVE_TERMIOS_H */ #endif /* HAVE_TERMIOS_H */
@@ -105,11 +109,14 @@ int term_erase;
int term_kill; int term_kill;
int int
term_restore(fd) term_restore(fd, flush)
int fd; int fd;
int flush;
{ {
if (changed) { if (changed) {
if (tcsetattr(fd, TCSAFLUSH|TCSASOFT, &oterm) != 0) int flags = TCSASOFT;
flags |= flush ? TCSAFLUSH : TCSADRAIN;
if (tcsetattr(fd, flags, &oterm) != 0)
return(0); return(0);
changed = 0; changed = 0;
} }
@@ -127,7 +134,7 @@ term_noecho(fd)
#ifdef VSTATUS #ifdef VSTATUS
term.c_cc[VSTATUS] = _POSIX_VDISABLE; term.c_cc[VSTATUS] = _POSIX_VDISABLE;
#endif #endif
if (tcsetattr(fd, TCSAFLUSH|TCSASOFT, &term) == 0) { if (tcsetattr(fd, TCSADRAIN|TCSASOFT, &term) == 0) {
changed = 1; changed = 1;
return(1); return(1);
} }
@@ -137,9 +144,10 @@ term_noecho(fd)
#if defined(HAVE_TERMIOS_H) || defined(HAVE_TERMIO_H) #if defined(HAVE_TERMIOS_H) || defined(HAVE_TERMIO_H)
int int
term_raw(fd, onlcr) term_raw(fd, onlcr, isig)
int fd; int fd;
int onlcr; int onlcr;
int isig;
{ {
struct termios term; struct termios term;
@@ -154,9 +162,11 @@ term_raw(fd, onlcr)
else else
term.c_oflag &= ~OPOST; term.c_oflag &= ~OPOST;
term.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); term.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
if (isig)
term.c_lflag |= ISIG;
term.c_cc[VMIN] = 1; term.c_cc[VMIN] = 1;
term.c_cc[VTIME] = 0; term.c_cc[VTIME] = 0;
if (tcsetattr(fd, TCSAFLUSH|TCSASOFT, &term) == 0) { if (tcsetattr(fd, TCSADRAIN|TCSASOFT, &term) == 0) {
changed = 1; changed = 1;
return(1); return(1);
} }
@@ -178,7 +188,7 @@ term_cbreak(fd)
#ifdef VSTATUS #ifdef VSTATUS
term.c_cc[VSTATUS] = _POSIX_VDISABLE; term.c_cc[VSTATUS] = _POSIX_VDISABLE;
#endif #endif
if (tcsetattr(fd, TCSAFLUSH|TCSASOFT, &term) == 0) { if (tcsetattr(fd, TCSADRAIN|TCSASOFT, &term) == 0) {
term_erase = term.c_cc[VERASE]; term_erase = term.c_cc[VERASE];
term_kill = term.c_cc[VKILL]; term_kill = term.c_cc[VKILL];
changed = 1; changed = 1;
@@ -200,7 +210,8 @@ term_copy(src, dst, onlcr)
/* Do not convert line endings from NL to NLCR. */ /* Do not convert line endings from NL to NLCR. */
if (!onlcr) if (!onlcr)
CLR(tt.c_oflag, ONLCR); CLR(tt.c_oflag, ONLCR);
if (tcsetattr(dst, TCSAFLUSH|TCSASOFT, &tt) != 0) /* XXX - add TCSANOW compat define */
if (tcsetattr(dst, TCSANOW|TCSASOFT, &tt) != 0)
return(0); return(0);
return(1); return(1);
} }

View File

@@ -136,7 +136,7 @@ restart:
} }
/* Restore old tty settings and signals. */ /* Restore old tty settings and signals. */
term_restore(input); term_restore(input, 1);
(void) sigaction(SIGALRM, &savealrm, NULL); (void) sigaction(SIGALRM, &savealrm, NULL);
(void) sigaction(SIGINT, &saveint, NULL); (void) sigaction(SIGINT, &saveint, NULL);
(void) sigaction(SIGHUP, &savehup, NULL); (void) sigaction(SIGHUP, &savehup, NULL);