From f8c4f9afe24d9e918367dfd4a9a2a2a42ad36fc0 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Sat, 23 Mar 2013 23:16:06 +0800 Subject: Add ~. and ~^Z handling to exit/suspend dbclient --- channel.h | 4 +++- cli-chansession.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- common-channel.c | 13 +++++++++++- session.h | 3 +++ sysoptions.h | 2 ++ 5 files changed, 81 insertions(+), 4 deletions(-) diff --git a/channel.h b/channel.h index 950d4b3..7146697 100644 --- a/channel.h +++ b/channel.h @@ -83,8 +83,10 @@ struct Channel { int flushing; - const struct ChanType* type; + /* Used by client chansession to handle ~ escaping, NULL ignored otherwise */ + void (*read_mangler)(struct Channel*, unsigned char* bytes, int *len); + const struct ChanType* type; }; struct ChanType { diff --git a/cli-chansession.c b/cli-chansession.c index 126c32f..65d1f4f 100644 --- a/cli-chansession.c +++ b/cli-chansession.c @@ -38,9 +38,10 @@ static void cli_closechansess(struct Channel *channel); static int cli_initchansess(struct Channel *channel); static void cli_chansessreq(struct Channel *channel); - static void send_chansess_pty_req(struct Channel *channel); static void send_chansess_shell_req(struct Channel *channel); +static void cli_escape_handler(struct Channel *channel, unsigned char* buf, int *len); + static void cli_tty_setup(); @@ -374,7 +375,9 @@ static int cli_initchansess(struct Channel *channel) { if (cli_opts.wantpty) { cli_tty_setup(); - } + channel->read_mangler = cli_escape_handler; + cli_ses.last_char = '\r'; + } return 0; /* Success */ } @@ -429,3 +432,59 @@ void cli_send_chansess_request() { TRACE(("leave cli_send_chansess_request")) } + +// returns 1 if the character should be consumed, 0 to pass through +static int +do_escape(unsigned char c) { + switch (c) { + case '.': + dropbear_exit("Terminated"); + return 1; + break; + case 0x1a: + // ctrl-z + cli_tty_cleanup(); + kill(getpid(), SIGTSTP); + // after continuation + cli_tty_setup(); + cli_ses.winchange = 1; + return 1; + break; + } + return 0; +} + +static +void cli_escape_handler(struct Channel *channel, unsigned char* buf, int *len) { + char c; + int skip_char = 0; + + // only handle escape characters if they are read one at a time. simplifies + // the code and avoids nasty people putting ~. at the start of a line to paste + if (*len != 1) { + cli_ses.last_char = 0x0; + return; + } + + c = buf[0]; + + if (cli_ses.last_char == DROPBEAR_ESCAPE_CHAR) { + skip_char = do_escape(c); + cli_ses.last_char = 0x0; + } else { + if (c == DROPBEAR_ESCAPE_CHAR) { + if (cli_ses.last_char == '\r') { + cli_ses.last_char = DROPBEAR_ESCAPE_CHAR; + skip_char = 1; + } else { + cli_ses.last_char = 0x0; + } + } else { + cli_ses.last_char = c; + } + } + + if (skip_char) { + *len = 0; + } +} diff --git a/common-channel.c b/common-channel.c index 9328a2e..05b9d11 100644 --- a/common-channel.c +++ b/common-channel.c @@ -643,6 +643,7 @@ static void send_msg_channel_data(struct Channel *channel, int isextended) { /* read the data */ len = read(fd, buf_getwriteptr(ses.writepayload, maxlen), maxlen); + if (len <= 0) { if (len == 0 || errno != EINTR) { /* This will also get hit in the case of EAGAIN. The only @@ -650,12 +651,22 @@ static void send_msg_channel_data(struct Channel *channel, int isextended) { in which case it can be treated the same as EOF */ close_chan_fd(channel, fd, SHUT_RD); } - ses.writepayload->len = ses.writepayload->pos = 0; + buf_setpos(ses.writepayload, 0); + buf_setlen(ses.writepayload, 0); TRACE(("leave send_msg_channel_data: len %d read err %d or EOF for fd %d", len, errno, fd)) return; } + if (channel->read_mangler) { + channel->read_mangler(channel, buf_getwriteptr(ses.writepayload, len), &len); + if (len == 0) { + buf_setpos(ses.writepayload, 0); + buf_setlen(ses.writepayload, 0); + return; + } + } + TRACE(("send_msg_channel_data: len %d fd %d", len, fd)) buf_incrwritepos(ses.writepayload, len); /* ... real size here */ diff --git a/session.h b/session.h index 158e209..0719e34 100644 --- a/session.h +++ b/session.h @@ -259,6 +259,9 @@ struct clientsession { int stderrcopy; int stderrflags; + /* for escape char handling */ + int last_char; + int winchange; /* Set to 1 when a windowchange signal happens */ int lastauthtype; /* either AUTH_TYPE_PUBKEY or AUTH_TYPE_PASSWORD, diff --git a/sysoptions.h b/sysoptions.h index 2d93e7b..8c591ea 100644 --- a/sysoptions.h +++ b/sysoptions.h @@ -54,6 +54,8 @@ #define _PATH_CP "/bin/cp" +#define DROPBEAR_ESCAPE_CHAR '~' + /* success/failure defines */ #define DROPBEAR_SUCCESS 0 #define DROPBEAR_FAILURE -1 -- cgit v1.2.1