From: Todd C. Miller Date: Mon, 27 Aug 2012 15:22:33 +0000 (-0400) Subject: Pass on SIGTSTP to the command if it was sent by a user process X-Git-Tag: SUDO_1_8_6^2~13 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=37269d662a53e048837dee13af2af24e6eac9c1e;p=sudo Pass on SIGTSTP to the command if it was sent by a user process (not the kernel or the terminal) when we are not I/O logging and set the default SIGTSTP handler when we re-send the signal to ourself, restoring our handler after we resume. --- diff --git a/src/exec.c b/src/exec.c index cb795511a..0fce4c947 100644 --- a/src/exec.c +++ b/src/exec.c @@ -98,8 +98,17 @@ static int fork_cmnd(struct command_details *details, int sv[2]) ppgrp = getpgrp(); /* parent's process group */ /* - * XXX - should only send SIGCONT when command pgrp != parent pgrp - * OR we were suspended by a user (not kernel or tty) signal. + * Handle suspend/restore of sudo and the command. + * In most cases, the command will be in the same process group as + * sudo and job control will "just work". However, if the command + * changes its process group ID and does not change it back (or is + * kill by SIGSTOP which is not catchable), we need to resume the + * command manually. Also, if SIGTSTP is sent directly to sudo, + * we need to suspend the command, and then suspend ourself, restoring + * the default SIGTSTP handler temporarily. + * + * XXX - currently we send SIGCONT upon resume in some cases where + * we don't need to (e.g. command pgrp == parent pgrp). */ zero_bytes(&sa, sizeof(sa)); sigemptyset(&sa.sa_mask); @@ -111,6 +120,10 @@ static int fork_cmnd(struct command_details *details, int sv[2]) sa.sa_handler = handler; #endif sigaction(SIGCONT, &sa, NULL); +#ifdef SA_SIGINFO + sa.sa_sigaction = handler_user_only; +#endif + sigaction(SIGTSTP, &sa, NULL); /* * The policy plugin's session init must be run before we fork @@ -548,16 +561,26 @@ handle_signals(int sv[2], pid_t child, int log_io, struct command_status *cstat) * the child upon resume, potentially stopping sudo * with SIGTTOU while the command continues to run. */ + sigaction_t sa, osa; pid_t saved_pgrp = (pid_t)-1; + int signo = WSTOPSIG(status); int fd = open(_PATH_TTY, O_RDWR|O_NOCTTY, 0); if (fd != -1) { if ((saved_pgrp = tcgetpgrp(fd)) == ppgrp) saved_pgrp = -1; } - if (kill(getpid(), WSTOPSIG(status)) != 0) { + if (signo == SIGTSTP) { + zero_bytes(&sa, sizeof(sa)); + sigemptyset(&sa.sa_mask); + sa.sa_handler = SIG_DFL; + sigaction(SIGTSTP, &sa, NULL); + } + if (kill(getpid(), signo) != 0) { warning("kill(%d, SIG%s)", (int)getpid(), - strsigname(WSTOPSIG(status))); + strsigname(signo)); } + if (signo == SIGTSTP) + sigaction(SIGTSTP, &osa, NULL); if (fd != -1) { /* * Restore command's process group if different.