- (djm) [channels.c serverloop.c] Fix so-called "hang on exit" (bz #52)
   when closing a tty session when a background process still holds tty
   fds open. Great detective work and patch by Marc Aurele La France,
   slightly tweaked by me; ok dtucker@
diff --git a/ChangeLog b/ChangeLog
index 7dd2e4a..481bcfd 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+20070128
+ - (djm) [channels.c serverloop.c] Fix so-called "hang on exit" (bz #52)
+   when closing a tty session when a background process still holds tty
+   fds open. Great detective work and patch by Marc Aurele La France,
+   slightly tweaked by me; ok dtucker@
+
 20070123
  - (dtucker) [openbsd-compat/bsd-snprintf.c] Static declarations for public
    library interfaces aren't very helpful. Fix up the DOPR_OUTCH macro
@@ -2686,4 +2692,4 @@
    OpenServer 6 and add osr5bigcrypt support so when someone migrates
    passwords between UnixWare and OpenServer they will still work. OK dtucker@
 
-$Id: ChangeLog,v 1.4608 2007/01/23 13:07:29 dtucker Exp $
+$Id: ChangeLog,v 1.4609 2007/01/28 23:16:28 djm Exp $
diff --git a/channels.c b/channels.c
index 9d522a6..c68ad64 100644
--- a/channels.c
+++ b/channels.c
@@ -1449,10 +1449,11 @@
 	int len;
 
 	if (c->rfd != -1 &&
-	    FD_ISSET(c->rfd, readset)) {
+	    (c->detach_close || FD_ISSET(c->rfd, readset))) {
 		errno = 0;
 		len = read(c->rfd, buf, sizeof(buf));
-		if (len < 0 && (errno == EINTR || errno == EAGAIN))
+		if (len < 0 && (errno == EINTR ||
+		    (errno == EAGAIN && !(c->isatty && c->detach_close))))
 			return 1;
 #ifndef PTY_ZEROREAD
 		if (len <= 0) {
@@ -1604,11 +1605,12 @@
 				c->local_consumed += len;
 			}
 		} else if (c->extended_usage == CHAN_EXTENDED_READ &&
-		    FD_ISSET(c->efd, readset)) {
+		    (c->detach_close || FD_ISSET(c->efd, readset))) {
 			len = read(c->efd, buf, sizeof(buf));
 			debug2("channel %d: read %d from efd %d",
 			    c->self, len, c->efd);
-			if (len < 0 && (errno == EINTR || errno == EAGAIN))
+			if (len < 0 && (errno == EINTR ||
+			    (errno == EAGAIN && !c->detach_close)))
 				return 1;
 			if (len <= 0) {
 				debug2("channel %d: closing read-efd %d",
diff --git a/serverloop.c b/serverloop.c
index 69304b5..7e373f0 100644
--- a/serverloop.c
+++ b/serverloop.c
@@ -280,6 +280,7 @@
 	struct timeval tv, *tvp;
 	int ret;
 	int client_alive_scheduled = 0;
+	int program_alive_scheduled = 0;
 
 	/*
 	 * if using client_alive, set the max timeout accordingly,
@@ -317,6 +318,7 @@
 		 * the client, try to get some more data from the program.
 		 */
 		if (packet_not_very_much_data_to_write()) {
+			program_alive_scheduled = child_terminated;
 			if (!fdout_eof)
 				FD_SET(fdout, *readsetp);
 			if (!fderr_eof)
@@ -362,8 +364,16 @@
 		memset(*writesetp, 0, *nallocp);
 		if (errno != EINTR)
 			error("select: %.100s", strerror(errno));
-	} else if (ret == 0 && client_alive_scheduled)
-		client_alive_check();
+	} else {
+		if (ret == 0 && client_alive_scheduled)
+			client_alive_check();
+		if (!compat20 && program_alive_scheduled && fdin_is_tty) {
+			if (!fdout_eof)
+				FD_SET(fdout, *readsetp);
+			if (!fderr_eof)
+				FD_SET(fderr, *readsetp);
+		}
+	}
 
 	notify_done(*readsetp);
 }
@@ -407,7 +417,8 @@
 	if (!fdout_eof && FD_ISSET(fdout, readset)) {
 		errno = 0;
 		len = read(fdout, buf, sizeof(buf));
-		if (len < 0 && (errno == EINTR || errno == EAGAIN)) {
+		if (len < 0 && (errno == EINTR ||
+		    (errno == EAGAIN && !child_terminated))) {
 			/* do nothing */
 #ifndef PTY_ZEROREAD
 		} else if (len <= 0) {
@@ -425,7 +436,8 @@
 	if (!fderr_eof && FD_ISSET(fderr, readset)) {
 		errno = 0;
 		len = read(fderr, buf, sizeof(buf));
-		if (len < 0 && (errno == EINTR || errno == EAGAIN)) {
+		if (len < 0 && (errno == EINTR ||
+		    (errno == EAGAIN && !child_terminated))) {
 			/* do nothing */
 #ifndef PTY_ZEROREAD
 		} else if (len <= 0) {