From 80338e7b59fd90e820caab0a3e85ccea810ea5b8 Mon Sep 17 00:00:00 2001 From: djm Date: Sun, 28 Jan 2007 23:16:28 +0000 Subject: [PATCH] - (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@ --- ChangeLog | 8 +++++++- channels.c | 10 ++++++---- serverloop.c | 20 ++++++++++++++++---- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/ChangeLog b/ChangeLog index 7dd2e4ab..481bcfdd 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 9d522a6c..c68ad641 100644 --- a/channels.c +++ b/channels.c @@ -1449,10 +1449,11 @@ channel_handle_rfd(Channel *c, fd_set *readset, fd_set *writeset) 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 @@ channel_handle_efd(Channel *c, fd_set *readset, fd_set *writeset) 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 69304b5f..7e373f01 100644 --- a/serverloop.c +++ b/serverloop.c @@ -280,6 +280,7 @@ wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, int *maxfdp, 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 @@ wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, int *maxfdp, * 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 @@ wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, int *maxfdp, 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 @@ process_input(fd_set *readset) 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 @@ process_input(fd_set *readset) 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) { -- 2.11.4.GIT