changeset 1835:90ac15aeac43

Bring back recently removed channel->flushing This resolves the "sleep 10&echo hello" case which should return immediately
author Matt Johnston <matt@codeconstruct.com.au>
date Thu, 14 Oct 2021 20:55:15 +0800
parents 94dc11094e26
children 06c7ddbb9dd6
files channel.h common-channel.c dbutil.c dbutil.h svr-chansession.c
diffstat 5 files changed, 47 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- 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 */
--- 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."))
--- 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);
+	}
+}
--- 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)
--- 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;
 }