From 03e3ca08dbcfcfaff664d3d0d5a41c9cf8a82215 Mon Sep 17 00:00:00 2001
From: "Todd C. Miller" <Todd.Miller@courtesan.com>
Date: Sun, 11 Oct 2009 13:25:23 +0000
Subject: [PATCH] Handle SIGTTOU and remove some debugging.

---
 script.c | 112 +++++++++++++++++++++++--------------------------------
 1 file changed, 47 insertions(+), 65 deletions(-)

diff --git a/script.c b/script.c
index b6dffe5e5..d8b79024b 100644
--- a/script.c
+++ b/script.c
@@ -100,8 +100,6 @@ static int get_pty __P((int *master, int *slave));
 
 /*
  * TODO: run monitor as root?
- *       make bg work (does not generate SIGTTOU)
- *       handle SIGTTIN/SIGTTOU better (race?)
  */
 
 struct script_buf {
@@ -239,9 +237,8 @@ script_setup()
 	log_error(USE_ERRNO, "Can't get pty");
 
     /* Copy terminal attrs from stdin -> pty slave. */
-    if (!term_copy(STDIN_FILENO, script_fds[SFD_SLAVE], 0)) {
+    if (!term_copy(STDIN_FILENO, script_fds[SFD_SLAVE], 0))
 	log_error(USE_ERRNO, "Can't copy terminal attributes");
-    }
     sync_winsize(STDIN_FILENO, script_fds[SFD_SLAVE]);
 
     if (!term_raw(STDIN_FILENO, 1))
@@ -342,7 +339,7 @@ script_execv(path, argv)
 {
     sigaction_t sa, saveint, savehup, saveterm;
     sigaction_t savequit, savetstp, savettin, savettou;
-    int status;
+    int status, exitcode = 1;
     pid_t child, pid;
 
     parent = getpid(); /* so child can pass signals back to us */
@@ -393,49 +390,57 @@ script_execv(path, argv)
     /* Wait for signal from child or for child to exit. */
     for (;;) {
 	pid = waitpid(child, &status, 0);
-	if (pid == -1 && errno == EINTR) {
-	    /* Restore old tty settings and signal handler. */
-	    term_restore(STDIN_FILENO);
-	    switch (signo) {
-	    case SIGINT:
-		(void) sigaction(SIGINT, &saveint, NULL);
-		break;
-	    case SIGHUP:
-		(void) sigaction(SIGHUP, &savehup, NULL);
-		break;
-	    case SIGQUIT:
-		(void) sigaction(SIGQUIT, &savequit, NULL);
-		break;
-	    case SIGTERM:
-		(void) sigaction(SIGTERM, &saveterm, NULL);
-		break;
-	    case SIGTSTP:
-		(void) sigaction(SIGTSTP, &savetstp, NULL);
-		break;
-	    case SIGTTIN:
-		(void) sigaction(SIGTTIN, &savettin, NULL);
-		break;
-	    case SIGTTOU:
-		(void) sigaction(SIGTTOU, &savettou, NULL);
-		break;
-	    }
-	    kill(parent, signo);
-	    /* Reinstall signal handler, reset raw mode and continue child */
-	    (void) sigaction(signo, &sa, NULL);
-	    term_raw(STDIN_FILENO, 1);
-	    killpg(child, SIGCONT);
-	} else {
+	if (pid != -1 || errno != EINTR) {
+	    /* headed for exit */
 	    if (pid == child) {
 		if (WIFEXITED(status))
-		    exit(WEXITSTATUS(status));
+		    exitcode = WEXITSTATUS(status);
 		if (WIFSIGNALED(status))
-		    exit(128 | WTERMSIG(status));
+		    exitcode = WTERMSIG(status) | 128;
 	    }
 	    break;
 	}
+
+	/* Restore old tty settings and signal handler. */
+	term_restore(STDIN_FILENO);
+      check_sig:
+	switch (signo) {
+	case SIGINT:
+	    (void) sigaction(SIGINT, &saveint, NULL);
+	    break;
+	case SIGHUP:
+	    (void) sigaction(SIGHUP, &savehup, NULL);
+	    break;
+	case SIGQUIT:
+	    (void) sigaction(SIGQUIT, &savequit, NULL);
+	    break;
+	case SIGTERM:
+	    (void) sigaction(SIGTERM, &saveterm, NULL);
+	    break;
+	case SIGTSTP:
+	    (void) sigaction(SIGTSTP, &savetstp, NULL);
+	    break;
+	case SIGTTIN:
+	    (void) sigaction(SIGTTIN, &savettin, NULL);
+	    break;
+	case SIGTTOU:
+	    (void) sigaction(SIGTTOU, &savettou, NULL);
+	    break;
+	default:
+	    /* should not happen */
+	    continue;
+	}
+	kill(parent, signo); /* re-send signal with handler disabled */
+	/* Reinstall signal handler, reset raw mode and continue child */
+	(void) sigaction(signo, &sa, NULL);
+	if (!term_raw(STDIN_FILENO, 1) && errno == EINTR)
+	    goto check_sig;
+	killpg(child, SIGCONT);
     }
 
-    exit(0);
+    term_restore(STDIN_FILENO);
+
+    exit(exitcode);
 }
 
 void
@@ -468,7 +473,6 @@ script_child(path, argv)
      * Create new session, with the slave as controlling terminal and
      * point std{in,out,err} to it.
      */
-    warningx("before: pid %d, pgrp %d, sid %d", getpid(), getpgrp(), getsid(0)); /* XXX */
 #ifdef HAVE_SETSID
     if (setsid() == -1)
 	log_error(USE_ERRNO, "setsid");
@@ -479,7 +483,6 @@ script_child(path, argv)
     if (ioctl(script_fds[SFD_SLAVE], TIOCSCTTY, NULL) != 0)
 	log_error(USE_ERRNO, "unable to set controlling tty");
 #endif
-    warningx("after: pid %d, pgrp %d, sid %d", getpid(), getpgrp(), getsid(0)); /* XXX */
 
     if ((idfile = fdopen(script_fds[SFD_LOG], "w")) == NULL)
 	log_error(USE_ERRNO, "fdopen");
@@ -674,12 +677,10 @@ script_child(path, argv)
 	} while (output.len > output.off);
     }
 
-    term_restore(STDIN_FILENO);
-
     if (WIFEXITED(grandchild_status))
 	exit(WEXITSTATUS(grandchild_status));
     if (WIFSIGNALED(grandchild_status))
-	exit(128 | WTERMSIG(grandchild_status));
+	exit(WTERMSIG(grandchild_status) | 128);
     exit(1);
 }
 
@@ -689,8 +690,6 @@ script_grandchild(path, argv, rbac_enabled)
     char *argv[];
     int rbac_enabled;
 {
-    warningx("grandchild: pid %d, pgrp %d, sid %d, tcpgrp %d", getpid(), getpgrp(), getsid(0), tcgetpgrp(script_fds[SFD_SLAVE])); /* XXX */
-
     dup2(script_fds[SFD_SLAVE], STDIN_FILENO);
     dup2(script_fds[SFD_SLAVE], STDOUT_FILENO);
     dup2(script_fds[SFD_SLAVE], STDERR_FILENO);
@@ -774,26 +773,9 @@ sigrelay(signo)
 {
     int serrno = errno;
 
-    /* XXX */
-    switch (signo) {
-    case SIGTSTP:
-	write(STDERR_FILENO, "SIGTSTP caught\n", 15);
-	break;
-    case SIGTTIN:
-	write(STDERR_FILENO, "SIGTTIN caught\n", 15);
-	break;
-    case SIGTTOU:
-	write(STDERR_FILENO, "SIGTTOU caught\n", 15);
-	break;
-    case SIGQUIT:
-	write(STDERR_FILENO, "SIGQUIT caught\n", 15);
-	break;
-    default:
-	write(STDERR_FILENO, "SIG???? caught\n", 15);
-	break;
-    }
     /* Relay signal back to parent for its tty. */
     kill(parent, signo);
+
     /* Suspend self and command, parent will continue us when it is time. */
     killpg(getpid(), SIGSTOP);
 
-- 
2.40.0