summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatt Johnston <matt@codeconstruct.com.au>2021-10-14 20:55:15 +0800
committerMatt Johnston <matt@codeconstruct.com.au>2021-10-14 20:55:15 +0800
commit4ee9231b778744baf4855cf5e1b03f8ca441a179 (patch)
treed97e4df3d7afa0207c8f036905814e5b9084ac64
parent93ca336040b1c7828b3f5de349b8f31c0b3c7f03 (diff)
downloaddropbear-4ee9231b778744baf4855cf5e1b03f8ca441a179.tar.gz
Bring back recently removed channel->flushing
This resolves the "sleep 10&echo hello" case which should return immediately
-rw-r--r--channel.h7
-rw-r--r--common-channel.c17
-rw-r--r--dbutil.c19
-rw-r--r--dbutil.h2
-rw-r--r--svr-chansession.c8
5 files changed, 47 insertions, 6 deletions
diff --git a/channel.h b/channel.h
index 6aae9ea..55e708a 100644
--- a/channel.h
+++ b/channel.h
@@ -71,6 +71,9 @@ struct Channel {
/* 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 @@ struct ChanType {
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 --git a/common-channel.c b/common-channel.c
index 5c8d9bc..047eece 100644
--- a/common-channel.c
+++ b/common-channel.c
@@ -285,14 +285,27 @@ static void check_close(struct Channel *channel) {
/* 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 --git a/dbutil.c b/dbutil.c
index f278efa..7980442 100644
--- a/dbutil.c
+++ b/dbutil.c
@@ -715,3 +715,22 @@ void fsync_parent_dir(const char* fn) {
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 --git a/dbutil.h b/dbutil.h
index 2a1c82c..39e1c35 100644
--- a/dbutil.h
+++ b/dbutil.h
@@ -90,6 +90,8 @@ char * expand_homedir_path(const char *inpath);
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 --git a/svr-chansession.c b/svr-chansession.c
index 8ae1a4b..2f90bac 100644
--- a/svr-chansession.c
+++ b/svr-chansession.c
@@ -54,7 +54,7 @@ static void closechansess(const struct Channel *channel);
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 @@ extern char** environ;
/* 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;
}