]> granicus.if.org Git - sudo/commitdiff
Pass on SIGTSTP to the command if it was sent by a user process
authorTodd C. Miller <Todd.Miller@courtesan.com>
Mon, 27 Aug 2012 15:22:33 +0000 (11:22 -0400)
committerTodd C. Miller <Todd.Miller@courtesan.com>
Mon, 27 Aug 2012 15:22:33 +0000 (11:22 -0400)
(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.

src/exec.c

index cb795511ad6c0fc4b16df841c5944a76db89d092..0fce4c94730fa845c190e4cb3e1ed6bf68532fae 100644 (file)
@@ -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.