Bring back recently removed channel->flushing

This resolves the "sleep 10&echo hello" case which should
return immediately
This commit is contained in:
Matt Johnston 2021-10-14 20:55:15 +08:00
parent 043b0fbd1b
commit a7ef149463
5 changed files with 47 additions and 6 deletions

View File

@ -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 */

View File

@ -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."))

View File

@ -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);
}
}

View File

@ -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)

View File

@ -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;
}