mirror of
https://github.com/clearml/dropbear
synced 2025-06-14 10:18:53 +00:00
Improve behaviour when flushing out after a process has exited.
--HG-- branch : channel-fix extra : convert_revision : e73ee8f7ae404a9355685c30828a0ad4524031bc
This commit is contained in:
parent
cda7af7ca2
commit
f5ad5c1553
@ -84,6 +84,8 @@ struct Channel {
|
|||||||
for this channel (and are awaiting a confirmation
|
for this channel (and are awaiting a confirmation
|
||||||
or failure). */
|
or failure). */
|
||||||
|
|
||||||
|
int flushing;
|
||||||
|
|
||||||
const struct ChanType* type;
|
const struct ChanType* type;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -148,6 +148,7 @@ struct Channel* newchannel(unsigned int remotechan,
|
|||||||
newchan->errfd = FD_CLOSED; /* this isn't always set to start with */
|
newchan->errfd = FD_CLOSED; /* this isn't always set to start with */
|
||||||
newchan->initconn = 0;
|
newchan->initconn = 0;
|
||||||
newchan->await_open = 0;
|
newchan->await_open = 0;
|
||||||
|
newchan->flushing = 0;
|
||||||
|
|
||||||
newchan->writebuf = cbuf_new(RECV_MAXWINDOW);
|
newchan->writebuf = cbuf_new(RECV_MAXWINDOW);
|
||||||
newchan->extrabuf = NULL; /* The user code can set it up */
|
newchan->extrabuf = NULL; /* The user code can set it up */
|
||||||
@ -203,12 +204,14 @@ void channelio(fd_set *readfds, fd_set *writefds) {
|
|||||||
|
|
||||||
/* read data and send it over the wire */
|
/* read data and send it over the wire */
|
||||||
if (channel->readfd >= 0 && FD_ISSET(channel->readfd, readfds)) {
|
if (channel->readfd >= 0 && FD_ISSET(channel->readfd, readfds)) {
|
||||||
|
TRACE(("send normal readfd"))
|
||||||
send_msg_channel_data(channel, 0);
|
send_msg_channel_data(channel, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* read stderr data and send it over the wire */
|
/* read stderr data and send it over the wire */
|
||||||
if (ERRFD_IS_READ(channel) &&
|
if (ERRFD_IS_READ(channel) && channel->errfd >= 0
|
||||||
channel->errfd >= 0 && FD_ISSET(channel->errfd, readfds)) {
|
&& FD_ISSET(channel->errfd, readfds)) {
|
||||||
|
TRACE(("send normal errfd"))
|
||||||
send_msg_channel_data(channel, 1);
|
send_msg_channel_data(channel, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -265,8 +268,14 @@ static void check_close(struct Channel *channel) {
|
|||||||
cbuf_getused(channel->writebuf),
|
cbuf_getused(channel->writebuf),
|
||||||
channel->extrabuf ? cbuf_getused(channel->extrabuf) : 0))
|
channel->extrabuf ? cbuf_getused(channel->extrabuf) : 0))
|
||||||
|
|
||||||
|
if (!channel->flushing && channel->type->check_close
|
||||||
|
&& channel->type->check_close(channel))
|
||||||
|
{
|
||||||
|
channel->flushing = 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (channel->recv_close && !write_pending(channel)) {
|
if (channel->recv_close && !write_pending(channel)) {
|
||||||
if (! channel->sent_close) {
|
if (!channel->sent_close) {
|
||||||
TRACE(("Sending MSG_CHANNEL_CLOSE in response to same."))
|
TRACE(("Sending MSG_CHANNEL_CLOSE in response to same."))
|
||||||
send_msg_channel_close(channel);
|
send_msg_channel_close(channel);
|
||||||
}
|
}
|
||||||
@ -278,6 +287,22 @@ static void check_close(struct Channel *channel) {
|
|||||||
close_chan_fd(channel, channel->writefd, SHUT_WR);
|
close_chan_fd(channel, channel->writefd, SHUT_WR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Special handling for flushing read data after an exit. We
|
||||||
|
read regardless of whether the select FD was set,
|
||||||
|
and if there isn't data available, the channel will get closed. */
|
||||||
|
if (channel->flushing) {
|
||||||
|
TRACE(("might send data, flushing"))
|
||||||
|
if (channel->readfd >= 0 && channel->transwindow > 0) {
|
||||||
|
TRACE(("send data readfd"))
|
||||||
|
send_msg_channel_data(channel, 0);
|
||||||
|
}
|
||||||
|
if (ERRFD_IS_READ(channel) && channel->readfd >= 0
|
||||||
|
&& channel->transwindow > 0) {
|
||||||
|
TRACE(("send data errfd"))
|
||||||
|
send_msg_channel_data(channel, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* If we're not going to send any more data, send EOF */
|
/* If we're not going to send any more data, send EOF */
|
||||||
if (!channel->sent_eof
|
if (!channel->sent_eof
|
||||||
&& channel->readfd == FD_CLOSED
|
&& channel->readfd == FD_CLOSED
|
||||||
@ -287,15 +312,13 @@ static void check_close(struct Channel *channel) {
|
|||||||
|
|
||||||
/* And if we can't receive any more data from them either, close up */
|
/* And if we can't receive any more data from them either, close up */
|
||||||
if (!channel->sent_close
|
if (!channel->sent_close
|
||||||
&& channel->writefd == FD_CLOSED
|
|
||||||
&& channel->readfd == FD_CLOSED
|
&& channel->readfd == FD_CLOSED
|
||||||
&& channel->errfd == FD_CLOSED) {
|
&& !write_pending(channel)) {
|
||||||
|
TRACE(("sending close, readfd is closed"))
|
||||||
send_msg_channel_close(channel);
|
send_msg_channel_close(channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Check whether a deferred (EINPROGRESS) connect() was successful, and
|
/* Check whether a deferred (EINPROGRESS) connect() was successful, and
|
||||||
* if so, set up the channel properly. Otherwise, the channel is cleaned up, so
|
* if so, set up the channel properly. Otherwise, the channel is cleaned up, so
|
||||||
* it is important that the channel reference isn't used after a call to this
|
* it is important that the channel reference isn't used after a call to this
|
||||||
@ -341,6 +364,9 @@ static void send_msg_channel_close(struct Channel *channel) {
|
|||||||
|
|
||||||
channel->sent_eof = 1;
|
channel->sent_eof = 1;
|
||||||
channel->sent_close = 1;
|
channel->sent_close = 1;
|
||||||
|
close_chan_fd(channel, channel->readfd, SHUT_RD);
|
||||||
|
close_chan_fd(channel, channel->errfd, SHUT_RDWR);
|
||||||
|
close_chan_fd(channel, channel->writefd, SHUT_WR);
|
||||||
TRACE(("leave send_msg_channel_close"))
|
TRACE(("leave send_msg_channel_close"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -556,6 +582,7 @@ static void send_msg_channel_data(struct Channel *channel, int isextended) {
|
|||||||
|
|
||||||
CHECKCLEARTOWRITE();
|
CHECKCLEARTOWRITE();
|
||||||
|
|
||||||
|
TRACE(("enter send_msg_channel_data"))
|
||||||
dropbear_assert(!channel->sent_close);
|
dropbear_assert(!channel->sent_close);
|
||||||
|
|
||||||
if (isextended) {
|
if (isextended) {
|
||||||
@ -591,6 +618,9 @@ static void send_msg_channel_data(struct Channel *channel, int isextended) {
|
|||||||
len = read(fd, buf_getwriteptr(ses.writepayload, maxlen), maxlen);
|
len = read(fd, buf_getwriteptr(ses.writepayload, maxlen), maxlen);
|
||||||
if (len <= 0) {
|
if (len <= 0) {
|
||||||
if (len == 0 || errno != EINTR) {
|
if (len == 0 || errno != EINTR) {
|
||||||
|
/* This will also get hit in the case of EAGAIN. The only
|
||||||
|
time we expect to receive EAGAIN is when we're flushing a FD,
|
||||||
|
in which case it can be treated the same as EOF */
|
||||||
close_chan_fd(channel, fd, SHUT_RD);
|
close_chan_fd(channel, fd, SHUT_RD);
|
||||||
}
|
}
|
||||||
ses.writepayload->len = ses.writepayload->pos = 0;
|
ses.writepayload->len = ses.writepayload->pos = 0;
|
||||||
@ -606,6 +636,14 @@ static void send_msg_channel_data(struct Channel *channel, int isextended) {
|
|||||||
channel->transwindow -= len;
|
channel->transwindow -= len;
|
||||||
|
|
||||||
encrypt_packet();
|
encrypt_packet();
|
||||||
|
|
||||||
|
/* If we receive less data than we requested when flushing, we've
|
||||||
|
reached the equivalent of EOF */
|
||||||
|
if (channel->flushing && len < maxlen)
|
||||||
|
{
|
||||||
|
TRACE(("closing from channel, flushing out."))
|
||||||
|
close_chan_fd(channel, fd, SHUT_RD);
|
||||||
|
}
|
||||||
TRACE(("leave send_msg_channel_data"))
|
TRACE(("leave send_msg_channel_data"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,6 +61,12 @@ void common_session_init(int sock, char* remotehost) {
|
|||||||
|
|
||||||
ses.connecttimeout = 0;
|
ses.connecttimeout = 0;
|
||||||
|
|
||||||
|
if (pipe(ses.signal_pipe) < 0) {
|
||||||
|
dropbear_exit("signal pipe failed");
|
||||||
|
}
|
||||||
|
setnonblocking(ses.signal_pipe[0]);
|
||||||
|
setnonblocking(ses.signal_pipe[1]);
|
||||||
|
|
||||||
kexfirstinitialise(); /* initialise the kex state */
|
kexfirstinitialise(); /* initialise the kex state */
|
||||||
|
|
||||||
ses.writepayload = buf_new(MAX_TRANS_PAYLOAD_LEN);
|
ses.writepayload = buf_new(MAX_TRANS_PAYLOAD_LEN);
|
||||||
@ -108,7 +114,6 @@ void common_session_init(int sock, char* remotehost) {
|
|||||||
|
|
||||||
ses.allowprivport = 0;
|
ses.allowprivport = 0;
|
||||||
|
|
||||||
|
|
||||||
TRACE(("leave session_init"))
|
TRACE(("leave session_init"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,6 +137,10 @@ void session_loop(void(*loophandler)()) {
|
|||||||
FD_SET(ses.sock, &writefd);
|
FD_SET(ses.sock, &writefd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* We get woken up when signal handlers write to this pipe.
|
||||||
|
SIGCHLD in svr-chansession is the only one currently. */
|
||||||
|
FD_SET(ses.signal_pipe[0], &readfd);
|
||||||
|
|
||||||
/* set up for channels which require reading/writing */
|
/* set up for channels which require reading/writing */
|
||||||
if (ses.dataallowed) {
|
if (ses.dataallowed) {
|
||||||
@ -155,6 +164,14 @@ void session_loop(void(*loophandler)()) {
|
|||||||
FD_ZERO(&writefd);
|
FD_ZERO(&writefd);
|
||||||
FD_ZERO(&readfd);
|
FD_ZERO(&readfd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* We'll just empty out the pipe if required. We don't do
|
||||||
|
any thing with the data, since the pipe's purpose is purely to
|
||||||
|
wake up the select() above. */
|
||||||
|
if (FD_ISSET(ses.signal_pipe[0], &readfd)) {
|
||||||
|
char x;
|
||||||
|
while (read(ses.signal_pipe[0], &x, 1) > 0) {}
|
||||||
|
}
|
||||||
|
|
||||||
/* check for auth timeout, rekeying required etc */
|
/* check for auth timeout, rekeying required etc */
|
||||||
checktimeouts();
|
checktimeouts();
|
||||||
|
@ -123,7 +123,8 @@ struct sshsession {
|
|||||||
|
|
||||||
unsigned char lastpacket; /* What the last received packet type was */
|
unsigned char lastpacket; /* What the last received packet type was */
|
||||||
|
|
||||||
|
int signal_pipe[2]; /* stores endpoints of a self-pipe used for
|
||||||
|
race-free signal handling */
|
||||||
|
|
||||||
/* KEX/encryption related */
|
/* KEX/encryption related */
|
||||||
struct KEXState kexstate;
|
struct KEXState kexstate;
|
||||||
|
@ -120,9 +120,21 @@ static void sesssigchild_handler(int UNUSED(dummy)) {
|
|||||||
/* we use this to determine how pid exited */
|
/* we use this to determine how pid exited */
|
||||||
exit->exitsignal = -1;
|
exit->exitsignal = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Make sure that the main select() loop wakes up */
|
||||||
|
while (1) {
|
||||||
|
/* EAGAIN means the pipe's full, so don't need to write anything */
|
||||||
|
/* isserver is just a random byte to write */
|
||||||
|
if (write(ses.signal_pipe[1], &ses.isserver, 1) == 1 || errno == EAGAIN) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (errno == EINTR) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
dropbear_exit("error writing signal pipe");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
sa_chld.sa_handler = sesssigchild_handler;
|
sa_chld.sa_handler = sesssigchild_handler;
|
||||||
sa_chld.sa_flags = SA_NOCLDSTOP;
|
sa_chld.sa_flags = SA_NOCLDSTOP;
|
||||||
sigaction(SIGCHLD, &sa_chld, NULL);
|
sigaction(SIGCHLD, &sa_chld, NULL);
|
||||||
@ -244,16 +256,17 @@ static void closechansess(struct Channel *channel) {
|
|||||||
unsigned int i;
|
unsigned int i;
|
||||||
struct logininfo *li;
|
struct logininfo *li;
|
||||||
|
|
||||||
|
TRACE(("enter closechansess"))
|
||||||
|
|
||||||
chansess = (struct ChanSess*)channel->typedata;
|
chansess = (struct ChanSess*)channel->typedata;
|
||||||
|
|
||||||
send_exitsignalstatus(channel);
|
|
||||||
|
|
||||||
TRACE(("enter closechansess"))
|
|
||||||
if (chansess == NULL) {
|
if (chansess == NULL) {
|
||||||
TRACE(("leave closechansess: chansess == NULL"))
|
TRACE(("leave closechansess: chansess == NULL"))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
send_exitsignalstatus(channel);
|
||||||
|
|
||||||
m_free(chansess->cmd);
|
m_free(chansess->cmd);
|
||||||
m_free(chansess->term);
|
m_free(chansess->term);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user