Shells typically change their process group when they start up so
that they can implement job control. Most well-behaved shells change the pgrp back to its original value before suspending so we must not try to restore in that case, lest we race with the child upon resume, potentially stopping sudo with SIGTTOU while the command continues to run. Some shells, such as pdksh, just suspend the shell by sending SIGSTOP to themselves without restoring the pgrp. In this case we need to change the pgrp back for them. Should fix bug #568
This commit is contained in:
24
src/exec.c
24
src/exec.c
@@ -73,6 +73,7 @@ struct sigforward {
|
|||||||
};
|
};
|
||||||
TQ_DECLARE(sigforward)
|
TQ_DECLARE(sigforward)
|
||||||
static struct sigforward_list sigfwd_list;
|
static struct sigforward_list sigfwd_list;
|
||||||
|
static pid_t ppgrp = -1;
|
||||||
|
|
||||||
volatile pid_t cmnd_pid = -1;
|
volatile pid_t cmnd_pid = -1;
|
||||||
|
|
||||||
@@ -94,6 +95,12 @@ static int fork_cmnd(struct command_details *details, int sv[2])
|
|||||||
sigaction_t sa;
|
sigaction_t sa;
|
||||||
debug_decl(fork_cmnd, SUDO_DEBUG_EXEC)
|
debug_decl(fork_cmnd, SUDO_DEBUG_EXEC)
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
zero_bytes(&sa, sizeof(sa));
|
zero_bytes(&sa, sizeof(sa));
|
||||||
sigemptyset(&sa.sa_mask);
|
sigemptyset(&sa.sa_mask);
|
||||||
sa.sa_flags = SA_INTERRUPT; /* do not restart syscalls */
|
sa.sa_flags = SA_INTERRUPT; /* do not restart syscalls */
|
||||||
@@ -534,17 +541,28 @@ handle_signals(int sv[2], pid_t child, int log_io, struct command_status *cstat)
|
|||||||
if (WIFSTOPPED(status)) {
|
if (WIFSTOPPED(status)) {
|
||||||
/*
|
/*
|
||||||
* Save the controlling terminal's process group
|
* Save the controlling terminal's process group
|
||||||
* so we can restore it after we resume.
|
* so we can restore it after we resume, if needed.
|
||||||
|
* Most well-behaved shells change the pgrp back to
|
||||||
|
* its original value before suspending so we must
|
||||||
|
* not try to restore in that case, lest we race with
|
||||||
|
* the child upon resume, potentially stopping sudo
|
||||||
|
* with SIGTTOU while the command continues to run.
|
||||||
*/
|
*/
|
||||||
pid_t saved_pgrp = (pid_t)-1;
|
pid_t saved_pgrp = (pid_t)-1;
|
||||||
int fd = open(_PATH_TTY, O_RDWR|O_NOCTTY, 0);
|
int fd = open(_PATH_TTY, O_RDWR|O_NOCTTY, 0);
|
||||||
if (fd != -1)
|
if (fd != -1) {
|
||||||
saved_pgrp = tcgetpgrp(fd);
|
if ((saved_pgrp = tcgetpgrp(fd)) == ppgrp)
|
||||||
|
saved_pgrp = -1;
|
||||||
|
}
|
||||||
if (kill(getpid(), WSTOPSIG(status)) != 0) {
|
if (kill(getpid(), WSTOPSIG(status)) != 0) {
|
||||||
warning("kill(%d, SIG%s)", (int)getpid(),
|
warning("kill(%d, SIG%s)", (int)getpid(),
|
||||||
strsigname(WSTOPSIG(status)));
|
strsigname(WSTOPSIG(status)));
|
||||||
}
|
}
|
||||||
if (fd != -1) {
|
if (fd != -1) {
|
||||||
|
/*
|
||||||
|
* Restore command's process group if different.
|
||||||
|
* Otherwise, we cannot resume some shells.
|
||||||
|
*/
|
||||||
if (saved_pgrp != (pid_t)-1)
|
if (saved_pgrp != (pid_t)-1)
|
||||||
(void)tcsetpgrp(fd, saved_pgrp);
|
(void)tcsetpgrp(fd, saved_pgrp);
|
||||||
close(fd);
|
close(fd);
|
||||||
|
Reference in New Issue
Block a user