# HG changeset patch # User Matt Johnston # Date 1634216115 -28800 # Node ID 90ac15aeac4372793c01bd6332bc7d5ba0d0c807 # Parent 94dc11094e263b571fbeb6d6ba90cd57a490af5a Bring back recently removed channel->flushing This resolves the "sleep 10&echo hello" case which should return immediately diff -r 94dc11094e26 -r 90ac15aeac43 channel.h --- a/channel.h Tue Oct 12 23:32:10 2021 +0800 +++ b/channel.h Thu Oct 14 20:55:15 2021 +0800 @@ -71,6 +71,9 @@ /* whether close/eof messages have been exchanged */ int sent_close, recv_close; int recv_eof, sent_eof; + /* once flushing is set, readfd will close once no more data is available + (not waiting for EOF) */ + int flushing; struct dropbear_progress_connection *conn_pending; int initconn; /* used for TCP forwarding, whether the channel has been @@ -93,9 +96,9 @@ const char *name; /* Sets up the channel */ int (*inithandler)(struct Channel*); - /* Called to check whether a channel should close, separately from the FD being closed. + /* Called to check whether a channel should close, separately from the FD being EOF. Used for noticing process exiting */ - int (*check_close)(const struct Channel*); + int (*check_close)(struct Channel*); /* Handler for ssh_msg_channel_request */ void (*reqhandler)(struct Channel*); /* Called prior to sending ssh_msg_channel_close, used for sending exit status */ diff -r 94dc11094e26 -r 90ac15aeac43 common-channel.c --- a/common-channel.c Tue Oct 12 23:32:10 2021 +0800 +++ b/common-channel.c Thu Oct 14 20:55:15 2021 +0800 @@ -285,14 +285,27 @@ /* if a type-specific check_close is defined we will only exit once that has been triggered. this is only used for a server "session" - channel, to ensure that the shell has exited (and the exit status + channel, to ensure that the shell has exited (and the exit status retrieved) before we close things up. */ - if (!channel->type->check_close + if (!channel->type->check_close || channel->sent_close || channel->type->check_close(channel)) { close_allowed = 1; } + /* In flushing mode we close FDs as soon as pipes are empty. + This is used to drain out FDs when the process exits, in the case + where the FD doesn't have EOF - "sleep 10&echo hello" case */ + if (channel->flushing) { + if (channel->readfd >= 0 && !fd_read_pending(channel->readfd)) { + close_chan_fd(channel, channel->readfd, SHUT_RD); + } + if (ERRFD_IS_READ(channel) + && channel->errfd >= 0 && !fd_read_pending(channel->errfd)) { + close_chan_fd(channel, channel->errfd, SHUT_RD); + } + } + if (channel->recv_close && !write_pending(channel) && close_allowed) { if (!channel->sent_close) { TRACE(("Sending MSG_CHANNEL_CLOSE in response to same.")) diff -r 94dc11094e26 -r 90ac15aeac43 dbutil.c --- a/dbutil.c Tue Oct 12 23:32:10 2021 +0800 +++ b/dbutil.c Thu Oct 14 20:55:15 2021 +0800 @@ -715,3 +715,22 @@ m_free(fn_dir); #endif } + +int fd_read_pending(int fd) { + fd_set fds; + struct timeval timeout; + + DROPBEAR_FD_ZERO(&fds); + FD_SET(fd, &fds); + while (1) { + timeout.tv_sec = 0; + timeout.tv_usec = 0; + if (select(fd+1, &fds, NULL, NULL, &timeout) < 0) { + if (errno == EINTR) { + continue; + } + return 0; + } + return FD_ISSET(fd, &fds); + } +} diff -r 94dc11094e26 -r 90ac15aeac43 dbutil.h --- a/dbutil.h Tue Oct 12 23:32:10 2021 +0800 +++ b/dbutil.h Thu Oct 14 20:55:15 2021 +0800 @@ -90,6 +90,8 @@ void fsync_parent_dir(const char* fn); +int fd_read_pending(int fd); + #if DROPBEAR_MSAN /* FD_ZERO seems to leave some memory uninitialized. clear it to avoid false positives */ #define DROPBEAR_FD_ZERO(fds) do { memset((fds), 0x0, sizeof(fd_set)); FD_ZERO(fds); } while(0) diff -r 94dc11094e26 -r 90ac15aeac43 svr-chansession.c --- a/svr-chansession.c Tue Oct 12 23:32:10 2021 +0800 +++ b/svr-chansession.c Thu Oct 14 20:55:15 2021 +0800 @@ -54,7 +54,7 @@ static void cleanupchansess(const struct Channel *channel); static int newchansess(struct Channel *channel); static void chansessionrequest(struct Channel *channel); -static int sesscheckclose(const struct Channel *channel); +static int sesscheckclose(struct Channel *channel); static void send_exitsignalstatus(const struct Channel *channel); static void send_msg_chansess_exitstatus(const struct Channel * channel, @@ -77,9 +77,13 @@ /* Returns whether the channel is ready to close. The child process must not be running (has never started, or has exited) */ -static int sesscheckclose(const struct Channel *channel) { +static int sesscheckclose(struct Channel *channel) { struct ChanSess *chansess = (struct ChanSess*)channel->typedata; TRACE(("sesscheckclose, pid %d, exitpid %d", chansess->pid, chansess->exit.exitpid)) + + if (chansess->exit.exitpid != -1) { + channel->flushing = 1; + } return chansess->pid == 0 || chansess->exit.exitpid != -1; }