diff options
Diffstat (limited to 'lib/vty_cli.c')
-rw-r--r-- | lib/vty_cli.c | 1187 |
1 files changed, 775 insertions, 412 deletions
diff --git a/lib/vty_cli.c b/lib/vty_cli.c index de9bb53c..53d64716 100644 --- a/lib/vty_cli.c +++ b/lib/vty_cli.c @@ -26,10 +26,12 @@ #include "keystroke.h" #include "vty.h" #include "uty.h" -#include "vty_io.h" #include "vty_cli.h" +#include "vty_io.h" +#include "vio_lines.h" #include "command.h" +#include "command_queue.h" #include "memory.h" @@ -130,60 +132,41 @@ uty_new_host_name(const char* name) * * State of the CLI: * - * cli_blocked -- a command has been dispatched, and CLI is now waiting - * for it and/or its output to complete. - * - * cmd_in_progress -- a command has been dispatched (and may have been - * queued). - * - * If not blocked: can continue in the CLI until another - * command is ready to be executed. - * - * There are two output FIFOs: - * - * 1. for the CLI itself -- uty_cli_out and friends - * - * 2. for output generated by commands -- vty_out and friends. - * - * The CLI FIFO is emptied whenever possible, in preference to the command - * FIFO. The command FIFO is only emptied when !cmd_in_progress -- this means - * that all the output from a given command is collected together before being - * sent to the file. - * - * The CLI starts with cli_blocked and !cmd_in_progress, with the socket set - * write on. The CLI progresses as follows: - * - * * on read_ready, if cli_blocked: do nothing. + * cli_blocked -- the CLI is unable to process any further keystrokes. * - * * on write_ready: + * cmd_in_progress -- a command has been dispatched and has not yet + * completed (may have been queued). * - * - empty out the CLI buffer + * cmd_out_enabled -- the command FIFO is may be emptied. * - * - if ! cmd_in_progress: + * This is set when a command completes, and cleared when + * everything is written away. * - * * empty out the command buffer + * cli_more_wait -- is in "--more--" wait state * - * * if the command buffer is empty, clear cli_blocked (if was set) + * The following are the valid combinations: * - * - generate a read_ready event unless write() would block. + * blkd : cip : o_en : m_wt : + * -----:------:------:------:-------------------------------------------- + * 0 : 0 : 0 : 0 : collecting a new command + * 0 : 1 : 0 : 0 : command dispatched + * 1 : 1 : 0 : 0 : waiting for (queued) command to complete + * 1 : 0 : 1 : 0 : waiting for command output to complete + * 1 : 0 : 0 : 1 : waiting for "--more--" to be written away + * 0 : 0 : 0 : 1 : waiting for "--more--" response + * 1 : 1 : 1 : 0 : waiting for command to complete, after the + * CLI has been closed * - * So the CLI is kicked once all available output completes. - * - * * on read_ready, if !cli_blocked: - * - * - process keystrokes into command line under construction. - * - * - when required, dispatch the command: - * - * * set cmd_in_progress - * - * * execute the command -- which may generate output + * There are two output FIFOs: * - * * clear cmd_in_progress + * 1. for the CLI itself -- uty_cli_out and friends * - * * set cli_blocked + * 2. for output generated by commands -- vty_out and friends. * - * - set write on (or read on) as required. + * The CLI FIFO is emptied whenever possible, in preference to the command + * FIFO. The command FIFO is emptied when cmd_out_enabled. While + * cmd_in_progress is also !cmd_out_enabled -- so that all the output from a + * given command is collected together before being sent to the file. * * Note that only sets read on when the keystroke stream is empty and has not * yet hit eof. The CLI process is driven mostly by write_ready -- which @@ -203,13 +186,13 @@ uty_new_host_name(const char* name) * * This is largely buried in the output handling. * - * While cmd_in_progress is true, all output to the command output FIFO is held - * there -- no actual write() is performed. - * - * When the command completes: + * While cmd_in_progress is true cmd_out_enabled will be false. When the + * command completes: * * * cmd_in_progress is cleared * + * * cmd_out_enabled is set + * * * cli_blocked will be set * * * the line_control structure is reset @@ -218,10 +201,10 @@ uty_new_host_name(const char* name) * * The output process used the line_control structure to manage the output, and * occasionally enter the trivial "--more--" CLI. This is invisible to the - * main CLI. (See the cmd_wait_more flag and its handling.) + * main CLI. (See the cli_more_wait flag and its handling.) * - * When all the output has completed, cli_blocked is cleared and the CLI will - * be kicked. + * When all the output has completed the CLI will be kicked, which will see + * that the output buffer is now empty, and it can proceed. * * It is expected that the output will end with a newline -- so that when the * CLI is kicked, the cursor will be at the start of an empty line. @@ -275,70 +258,191 @@ uty_new_host_name(const char* name) */ /*============================================================================== - * Command Line Interface + * The CLI + */ + +#define CONTROL(X) ((X) - '@') + +static void uty_cli_cmd_complete(vty_io vio, enum cmd_return_code ret) ; +static enum vty_readiness uty_cli_standard(vty_io vio) ; +static enum vty_readiness uty_cli_more_wait(vty_io vio) ; +static void uty_cli_draw(vty_io vio) ; +static void uty_cli_draw_this(vty_io vio, enum node_type node) ; +static void uty_cli_wipe(vty_io vio, int len) ; + +static void uty_will_echo (vty_io vio) ; +static void uty_will_suppress_go_ahead (vty_io vio) ; +static void uty_dont_linemode (vty_io vio) ; +static void uty_do_window_size (vty_io vio) ; +static void uty_dont_lflow_ahead (vty_io vio) ; + +/*------------------------------------------------------------------------------ + * Initialise CLI. * - * State of the CLI: + * It is assumed that the following have been initialised, empty or zero: + * + * cli_prompt_for_node + * cl + * clx + * cli_vbuf + * cli_obuf * - * cli_blocked -- a command has been dispatched, and now waiting - * for it and/or its output to complete. + * cli_drawn + * cli_dirty * - * cmd_in_progress -- a command has been dispatched (and may have been - * queued). + * cli_prompt_set * - * Can continue in the CLI until another command is - * ready to be executed. + * cli_blocked + * cmd_in_progress + * cmd_out_enabled + * cli_wait_more + * + * cli_more_enabled + * + * Sets the CLI such that there is apparently a command in progress, so that + * further initialisation (in particular hello messages and the like) is + * treated as a "start up command". + * + * Sends a suitable set of Telnet commands to start the process. + */ +extern void +uty_cli_init(vty_io vio) +{ + assert(vio->type == VTY_TERM) ; + + vio->cmd_in_progress = 1 ; + vio->cli_blocked = 1 ; + + vio->cli_do = cli_do_nothing ; + + /* Setting up terminal. */ + uty_will_echo (vio); + uty_will_suppress_go_ahead (vio); + uty_dont_linemode (vio); + uty_do_window_size (vio); + if (0) + uty_dont_lflow_ahead (vio) ; +} ; + +/*------------------------------------------------------------------------------ + * Start the CLI. + * + * All start-up operations are complete -- so the "command" is now complete. + * + * Returns: write_ready -- so the first event is a write event, to flush + * any output to date. + */ +extern enum vty_readiness +uty_cli_start(vty_io vio) +{ + uty_cli_cmd_complete(vio, CMD_SUCCESS) ; + return write_ready ; +} ; + +/*------------------------------------------------------------------------------ + * Close the CLI + * + * Note that if any command is revoked, then will clear cmd_in_progress and + * set cmd_out_enabled -- so any output can now clear. + */ +extern void +uty_cli_close(vty_io vio) +{ + cq_revoke(vio->vty) ; + + vio->cli_blocked = 1 ; /* don't attempt any more */ + vio->cmd_out_enabled = 1 ; /* allow output to clear */ +} ; + +/*------------------------------------------------------------------------------ + * CLI for VTY_TERM + * + * Do nothing at all if half closed. + * + * Otherwise do: standard CLI + * or: "--more--" CLI + * + * NB: on return, requires that an attempt is made to write away anything that + * may be ready for that. + */ +extern enum vty_readiness +uty_cli(vty_io vio) +{ + VTY_ASSERT_LOCKED() ; + assert(vio->type == VTY_TERM) ; + + if (vio->half_closed) + return not_ready ; /* Nothing more if half closed */ + + /* Standard or "--more--" CLI ? */ + if (vio->cli_more_wait) + return uty_cli_more_wait(vio) ; + else + return uty_cli_standard(vio) ; +} ; + +/*============================================================================== + * The Standard CLI */ -static void uty_cli_draw(vty_io vio, enum node_type node) ; static enum cli_do uty_cli_process(vty_io vio, enum node_type node) ; static void uty_cli_response(vty_io vio, enum cli_do cli_do) ; -static int uty_cli_dispatch(vty_io vio) ; +static bool uty_cli_dispatch(vty_io vio) ; /*------------------------------------------------------------------------------ - * Run the CLI until: + * Standard CLI for VTY_TERM -- if not blocked, runs until: * - * * becomes blocked * * runs out of keystrokes * * executes a command * - * When exits will (in general): - * - * * set write on if there is anything to be written or have something - * in the keystroke stream to be processed. - * - * * set read on if does not set write on, if not yet hit EOF on the - * keystroke stream. - * * Note that this executes at most one command each time it is called. This * is to allow for a modicum of sharing of the system. For real keyboard input * this will make no difference at all ! + * + * Returns: not_ready blocked and was blocked when entered + * write_ready if there is anything in the keystroke stream + * read_ready otherwise */ -extern void -uty_cli(vty_io vio) +static enum vty_readiness +uty_cli_standard(vty_io vio) { - bool won ; - VTY_ASSERT_LOCKED() ; + assert(vio->type == VTY_TERM) ; /* cli_blocked is set when is waiting for a command, or its output to - * complete. + * complete -- unless either of those has happened, is still blocked. * - * There is no good reason to arrive here in that state, and nothing to be - * done if that happens. + * NB: in both these cases, assumes that other forces are at work to + * keep things moving. */ if (vio->cli_blocked) - return ; + { + assert(vio->cmd_in_progress || vio->cmd_out_enabled) ; - /* If there is something to do, that is because a previous command has - * now completed, which may have wiped the pending command. - * - * If there is nothing pending, then can run the CLI until there is + if (vio->cmd_in_progress) + { + assert(!vio->cmd_out_enabled) ; + return not_ready ; + } ; + + if (!vio_fifo_empty(&vio->cmd_obuf)) + return not_ready ; + + vio->cli_blocked = 0 ; + vio->cmd_out_enabled = 0 ; + } ; + + /* If there is nothing pending, then can run the CLI until there is * something to do, or runs out of input. + * + * If there is something to do, that is because a previous command has + * now completed, which may have wiped the pending command or changed + * the required prompt. */ - if (vio->cli_do != cli_do_nothing) - uty_cli_draw(vio, vio->vty->node) ; - else + if (vio->cli_do == cli_do_nothing) vio->cli_do = uty_cli_process(vio, vio->vty->node) ; + else + uty_cli_draw_this(vio, vio->vty->node) ; /* If have something to do, do it. */ if (vio->cli_do != cli_do_nothing) @@ -357,73 +461,21 @@ uty_cli(vty_io vio) vio->cli_blocked = 1 ; } ; - /* If there is anything in the CLI output FIFO, must set write on to clear - * it. + /* Use write_ready as a proxy for read_ready on the keystroke stream. * - * If there is anything in the command output FIFO *and* is !cmd_in_progress, - * must set write on to clear it. + * Also, if the command line is not drawn, then return write_ready, so + * that * - * Otherwise: if cmd_in_progress, then the command buffer is not to be - * output yet, and the command completion will kick things into action -- so - * nothing further is required here. - * - * Otherwise: if there is anything in the command output buffer, must set - * write on to clear it. - * - * Otherwise: if there is something in the keystroke stream, then must set - * write on... the write_ready acts as a proxy for keystroke stream is ready. - * - * Otherwise: set read on -- so if further input arrives, will get on it. + * Note that if has just gone cli_blocked, still returns ready. This is + * defensive: at worst will generate one unnecessary read_ready/write_ready + * event. */ - do - { - won = 1 ; /* generally the case */ - - /* If have some CLI output -- do that */ - if (!vio_fifo_empty(&vio->cli_obuf)) - break ; - - /* If is blocked with command in progress -- do nothing. - * - * This state <=> that there is a queued command, and the CLI is now - * blocked until the command completes. When it does, things will - * progress. - */ - if (vio->cli_blocked && vio->cmd_in_progress) - { - won = 0 ; - break ; - } ; - - /* If have some command output which is not held because there is - * a command in progress -- do that. - */ - if (!vio_fifo_empty(&vio->cmd_obuf) && !vio->cmd_in_progress) - break ; - - /* If the keystroke buffer is not empty then write_ready is used as a - * proxy for keystroke ready. - */ - if (!keystroke_stream_empty(vio->key_stream)) - break ; - - /* Finally... if not at keystroke EOF, set read on */ - won = 0 ; - - if (!keystroke_stream_eof(vio->key_stream)) - uty_file_set_read(&vio->file, on) ; - - } while (0) ; - - if (won) - uty_file_set_write(&vio->file, on) ; + if (keystroke_stream_empty(vio->key_stream)) + return read_ready ; + else + return write_ready ; } ; -/* TODO: dealing with EOF and timeout and closing and such - * - * uty_cout (vty, "%sVty connection is timed out.%s", VTY_NEWLINE, VTY_NEWLINE); - * - */ /*------------------------------------------------------------------------------ * Dispatch the current vio->cli_do -- queueing it if necessary. * @@ -432,19 +484,19 @@ uty_cli(vty_io vio) * Expects to be on new blank line, and when returns will be on new, blank * line. * - * Returns: true <=> output is pending and command completed - * false => no output pending or command was queued + * Returns: true <=> command completed and output is pending + * false => command has been queued and is now in progress * * Generally sets vio->cl_do = cli_do_nothing and clears vio->cl to empty. * * Can set vio->cl_do = and vio->cl to be a follow-on command. */ -static int +static bool uty_cli_dispatch(vty_io vio) { qstring_t tmp ; enum cli_do cli_do ; - int queued ; + enum cmd_return_code ret ; struct vty* vty = vio->vty ; @@ -455,7 +507,8 @@ uty_cli_dispatch(vty_io vio) * * Clear vio->cl and vio->cl_do. */ - vio->cmd_in_progress = 1 ; /* => vty->buf is valid */ + vio->cmd_in_progress = 1 ; /* => vty->buf is valid */ + vio->cmd_out_enabled = 0 ; /* => collect all output */ tmp = vio->clx ; /* swap clx and cl */ vio->clx = vio->cl ; @@ -466,18 +519,17 @@ uty_cli_dispatch(vty_io vio) cli_do = vio->cli_do ; /* current operation */ vio->cli_do = cli_do_nothing ; /* clear */ - qs_set_empty(&vio->cl) ; /* set cl empty (with '\0') */ + qs_clear(&vio->cl) ; /* set cl empty (with '\0') */ /* Reset the command output FIFO and line_control */ - assert(vio_fifo_empty(&vio->cmd_obuf)) ; - /* TODO: reset line_control */ + uty_out_clear(vio) ; /* clears FIFO and line control */ /* Dispatch command */ if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE)) { /* AUTH_NODE and AUTH_ENABLE_NODE are unique */ - queued = uty_auth(vty, vty->buf, cli_do) ; + ret = uty_auth(vty, vty->buf, cli_do) ; } else { @@ -488,23 +540,27 @@ uty_cli_dispatch(vty_io vio) break ; case cli_do_command: - queued = uty_command(vty, vty->buf) ; + ret = uty_command(vty) ; break ; case cli_do_ctrl_c: - queued = uty_stop_input(vty) ; + ret = uty_stop_input(vty) ; break ; case cli_do_ctrl_d: - queued = uty_down_level(vty) ; + ret = uty_down_level(vty) ; break ; case cli_do_ctrl_z: - queued = uty_command(vty, vty->buf) ; - if (queued) - vio->cli_do = cli_do_ctrl_z ; + ret = uty_command(vty) ; + if (ret == CMD_QUEUED) + vio->cli_do = cli_do_ctrl_z ; /* defer the ^Z action */ else - queued = uty_end_config(vty) ; + ret = uty_end_config(vty) ; /* do the ^Z now */ + break ; + + case cli_do_eof: + ret = uty_cmd_close(vio->vty, "End") ; break ; default: @@ -512,13 +568,16 @@ uty_cli_dispatch(vty_io vio) } ; } ; - if (!queued) + if (ret == CMD_QUEUED) + { + uty_cli_draw(vio) ; /* draw the prompt */ + return 0 ; /* command not complete */ + } + else { - vio->cmd_in_progress = 0 ; /* command complete */ - vty->buf = NULL ; /* finished with command line */ + uty_cli_cmd_complete(vio, ret) ; + return 1 ; /* command complete */ } ; - - return ! (vio->cmd_in_progress || vio_fifo_empty(&vio->cmd_obuf)) ; } ; /*------------------------------------------------------------------------------ @@ -528,7 +587,7 @@ uty_cli_dispatch(vty_io vio) * or not... write_ready will kick read_ready. */ extern void -vty_queued_result(struct vty *vty, int result, int action) +vty_queued_result(struct vty *vty, enum cmd_return_code ret) { vty_io vio ; @@ -536,20 +595,210 @@ vty_queued_result(struct vty *vty, int result, int action) vio = vty->vio ; - vio->cmd_in_progress = 0 ; /* command complete */ - vty->buf = NULL ; /* finished with command line */ + if (!vio->closed) + { + uty_cli_wipe(vio, 0) ; /* wipe any partly constructed line */ - vio->cli_blocked = !vio_fifo_empty(&vio->cmd_obuf) ; - /* blocked if output is now pending */ + /* Do the command completion actions that were deferred because the + * command was queued. + * + * Return of CMD_QUEUED => command was revoked before being executed. + * However interesting that might be... frankly don't care. + */ + uty_cli_cmd_complete(vio, ret) ; - uty_cli_wipe(vio) ; /* wipe any partly constructed line */ + /* Kick the socket -- to write away any outstanding output, and + * re-enter the CLI when that's done. + */ + uty_sock_set_readiness(&vio->sock, write_ready) ; + } + else + { + /* If the VTY is closed, the only reason it still exists is because + * there was cmd_in_progress. + */ + vio->cmd_in_progress = 0 ; + + uty_close(vio) ; /* Final close */ + } ; - uty_file_set_write(&vty->vio->file, on) ; - /* flush any output -- which will do a - * read_ready when all finished */ VTY_UNLOCK() ; } +/*------------------------------------------------------------------------------ + * Command has completed, so: + * + * * clear cmd_in_progress + * * set cmd_out_enabled -- so any output can now proceed + * * set cli_blocked -- waiting for output to complete + * * and prepare the line control for output + * + * If the return is CMD_CLOSE, then also now does the required half close. + * + * Note that apart from CMD_CLOSE, don't really care what the return was. Any + * diagnostics or other action must be dealt with elsewhere (as part of the + * command execution. + * + * Note that everything proceeds as if there is some output. So after every + * command goes through at least one write_ready event. + * + * This ensures some multiplexing at the command level. + * + * It also means that the decision about whether there is anything to output + * is left to the output code. + */ +static void +uty_cli_cmd_complete(vty_io vio, enum cmd_return_code ret) +{ + VTY_ASSERT_LOCKED() ; + + assert(vio->cmd_in_progress && !vio->cmd_out_enabled) ; + + if (ret == CMD_CLOSE) + uty_half_close(vio, NULL) ; + + vio->cmd_in_progress = 0 ; /* command complete */ + vio->cmd_out_enabled = 1 ; /* enable the output */ + vio->cli_blocked = 1 ; /* now blocked waiting for output */ + + vio->vty->buf = NULL ; /* finished with command line */ + + uty_cmd_output_start(vio) ; /* reset line control (belt & braces) */ +} ; + +/*============================================================================== + * The "--more--" CLI + * + * While command output is being cleared from its FIFO, the CLI is cli_blocked. + * + * When the output side signals that "--more--" is required, it sets the + * cli_more_wait flag and clears the cmd_out_enabled flag. + * + * The first stage of handling "--more--" is to suck the input dry, so that + * (as far as is reasonably possible) does not steal a keystroke as the + * "--more--" response which was typed before the prompt was issued. + * + * The cli_blocked flag indicates that the CLI is in this first stage. + */ + +/*------------------------------------------------------------------------------ + * Change the CLI to the "--more--" CLI. + * + * Outputs the new prompt line. + */ +extern void +uty_cli_go_more_wait(vty_io vio) +{ + VTY_ASSERT_LOCKED() ; + + assert(vio->cli_blocked && vio->cmd_out_enabled && !vio->cli_more_wait) ; + + uty_cli_wipe(vio, 0) ; /* make absolutely sure that command line is + wiped before change the CLI state */ + + vio->cmd_out_enabled = 0 ; /* stop output pro tem */ + vio->cli_more_wait = 1 ; /* new state */ + + uty_cli_draw(vio) ; /* draw the "--more--" */ +} ; + +/*------------------------------------------------------------------------------ + * Handle the "--more--" state. + * + * Deals with the first stage if cli_blocked. + * + * Tries to steal a keystroke, and when succeeds wipes the "--more--" + * prompt and exits cli_more_wait -- and may cancel all outstanding output. + * + * EOF on input causes immediate exit from cli_more_state. + * + * Returns: read_ready -- waiting to steal a keystroke + * now_ready -- just left cli_more_wait + * not_ready -- otherwise + */ +static enum vty_readiness +uty_cli_more_wait(vty_io vio) +{ + struct keystroke steal ; + + VTY_ASSERT_LOCKED() ; + + /* Deal with the first stage of "--more--" */ + if (vio->cli_blocked) + { + int get ; + + /* If the CLI buffer is not yet empty, then is waiting for the + * initial prompt to clear, so nothing to be done here. + */ + if (!vio_fifo_empty(&vio->cli_obuf)) + return not_ready ; + + vio->cli_blocked = 0 ; + + /* empty the input buffer into the keystroke stream */ + do + { + get = uty_read(vio, NULL) ; + } while (get > 0) ; + } ; + + /* Go through the "--more--" process, unless no longer write_open (!) */ + if (vio->sock.write_open) + { + /* The read fetches a reasonable lump from the I/O -- so if there + * is a complete keystroke available, expect to get it. + * + * If no complete keystroke available to steal, returns ks_null. + * + * If has hit EOF (or error etc), returns knull_eof. + */ + uty_read(vio, &steal) ; + + /* If nothing stolen, make sure prompt is drawn and wait for more + * input. + */ + if ((steal.type == ks_null) && (steal.value != knull_eof)) + { + if (uty_cli_draw_if_required(vio)) /* "--more--" if req. */ + return write_ready ; + else + return read_ready ; + } ; + + /* Stolen a keystroke -- a (very) few terminate all output */ + if (steal.type == ks_char) + { + switch (steal.value) + { + case CONTROL('C'): + case 'q': + case 'Q': + uty_out_clear(vio) ; + break; + + default: /* everything else, thrown away */ + break ; + } ; + } ; + } ; + + /* End of "--more--" process + * + * Wipe out the prompt and update state. + * + * Return write_ready to tidy up the screen and, unless cleared, write + * some more. + */ + uty_cli_wipe(vio, 0) ; + + vio->cli_blocked = 1 ; /* back to blocked waiting for output */ + vio->cli_more_wait = 0 ; /* exit more_wait */ + vio->cmd_out_enabled = 1 ; /* re-enable output */ + + return now_ready ; +} ; + /*============================================================================== * CLI VTY output * @@ -600,21 +849,17 @@ static void uty_cli_write_n(vty_io vio, cli_rep_char chars, int n) ; * CLI VTY output -- cf fprintf() */ static void -uty_cli_out (vty_io vio, const char *format, ...) +uty_cli_out(vty_io vio, const char *format, ...) { VTY_ASSERT_LOCKED() ; - if (vio->file.write_open) + if (vio->sock.write_open) { - va_list args; - int len ; + va_list args ; va_start (args, format); - len = qs_vprintf(&vio->cli_vbuf, format, args) ; + vio_fifo_vprintf(&vio->cli_obuf, format, args) ; va_end(args); - - if (len > 0) - uty_cli_write(vio, qs_chars(&vio->cli_vbuf), len) ; } ; } ; @@ -624,11 +869,11 @@ uty_cli_out (vty_io vio, const char *format, ...) * Do nothing if echo suppressed (eg in AUTH_NODE) or not write_open */ static void -uty_cli_echo (vty_io vio, const char *this, size_t len) +uty_cli_echo(vty_io vio, const char *this, size_t len) { VTY_ASSERT_LOCKED() ; - if (vio->cli_echo_suppress || !vio->file.write_open) + if (vio->cli_echo_suppress || !vio->sock.write_open) return ; uty_cli_write(vio, this, len) ; @@ -644,7 +889,7 @@ uty_cli_echo_n(vty_io vio, cli_rep_char chars, int n) { VTY_ASSERT_LOCKED() ; - if (vio->cli_echo_suppress || !vio->file.write_open) + if (vio->cli_echo_suppress || !vio->sock.write_open) return ; uty_cli_write_n(vio, chars, n) ; @@ -658,7 +903,7 @@ uty_cli_write(vty_io vio, const char *this, int len) { VTY_ASSERT_LOCKED() ; - if (vio->file.write_open) + if (vio->sock.write_open) vio_fifo_put(&vio->cli_obuf, this, len) ; } ; @@ -704,13 +949,14 @@ uty_cli_write_s(vty_io vio, const char *str) /*------------------------------------------------------------------------------ * Send newline to the console. * - * Clears the cli_drawn flag. + * Clears the cli_drawn and the cli_dirty flags. */ static void uty_cli_out_newline(vty_io vio) { uty_cli_write(vio, telnet_newline, 2) ; vio->cli_drawn = 0 ; + vio->cli_dirty = 0 ; } ; /*------------------------------------------------------------------------------ @@ -747,16 +993,18 @@ uty_cli_out_wipe_n(vty_io vio, int n) static const char* cli_response [2][cli_do_count] = { { /* when not waiting for previous command to complete */ - [cli_do_command] = "", - [cli_do_ctrl_c] = "^C", - [cli_do_ctrl_d] = "^D", - [cli_do_ctrl_z] = "^Z", + [cli_do_command] = "", + [cli_do_ctrl_c] = "^C", + [cli_do_ctrl_d] = "^D", + [cli_do_ctrl_z] = "^Z", + [cli_do_eof] = "^*" }, { /* when waiting for a previous command to complete */ - [cli_do_command] = "^", - [cli_do_ctrl_c] = "^C", - [cli_do_ctrl_d] = "^D", - [cli_do_ctrl_z] = "^Z", + [cli_do_command] = "^", + [cli_do_ctrl_c] = "^C", + [cli_do_ctrl_d] = "^D", + [cli_do_ctrl_z] = "^Z", + [cli_do_eof] = "^*" } } ; @@ -766,7 +1014,7 @@ uty_cli_response(vty_io vio, enum cli_do cli_do) const char* str ; int len ; - if (cli_do == cli_do_nothing) + if ((cli_do == cli_do_nothing) || (vio->half_closed)) return ; str = (cli_do < cli_do_count) @@ -811,36 +1059,72 @@ uty_cli_out_CMD_ERR_NO_MATCH(vty_io vio) /*------------------------------------------------------------------------------ * Wipe the current console line -- if any. */ -extern void -uty_cli_wipe(vty_io vio) +static void +uty_cli_wipe(vty_io vio, int len) { int a ; int b ; - if (!vio->cli_drawn == 0) + if (!vio->cli_drawn) return ; /* quit if already wiped */ assert(vio->cl.cp <= vio->cl.len) ; - /* deal with echo suppression first */ - if (vio->cli_echo_suppress) - { - b = 0 ; - a = 0 ; - } - else + /* Establish how much ahead and how much behind the cursor */ + a = vio->cli_extra_len ; + b = vio->cli_prompt_len ; + + if (!vio->cli_echo_suppress && !vio->cli_more_wait) { - b = vio->cl.cp ; /* behind cursor */ - a = vio->cl.len - b ; /* ahead of cursor */ - } + a += vio->cl.len - vio->cl.cp ; + b += vio->cl.cp ; + } ; - /* Stuff ahead of the current position */ - uty_cli_out_wipe_n(vio, a + vio->cli_extra_len) ; + /* Stuff ahead of the current position if any ahead of new len */ + if ((a + b) > len) + uty_cli_out_wipe_n(vio, +a) ; - /* Stuff behind the current position */ - uty_cli_out_wipe_n(vio, vio->cli_prompt_len + b) ; + /* Stuff behind current position, but ahead of new len */ + if (b > len) + { + uty_cli_out_wipe_n(vio, -(b - len)) ; + b = len ; /* moved the cursor back */ + } ; + + /* Back to the beginning of the line */ + uty_cli_write_n(vio, telnet_backspaces, b) ; + /* Nothing there any more */ vio->cli_drawn = 0 ; + vio->cli_dirty = 0 ; +} ; + +/*------------------------------------------------------------------------------ + * If not currently drawn, draw prompt etc according to the current state + * and node. + * + * See uty_cli_draw(). + */ +extern bool +uty_cli_draw_if_required(vty_io vio) +{ + if (vio->cli_drawn) + return false ; + + uty_cli_draw(vio) ; + + return true ; +} ; + +/*------------------------------------------------------------------------------ + * Draw prompt etc for the current vty node. + * + * See uty_cli_draw_this() + */ +static void +uty_cli_draw(vty_io vio) +{ + uty_cli_draw_this(vio, vio->vty->node) ; } ; /*------------------------------------------------------------------------------ @@ -851,34 +1135,52 @@ uty_cli_wipe(vty_io vio) * * Otherwise, assumes is positioned at start of an empty line. * - * If there is a command queued, uses a dummy prompt -- because by the time - * the command is executed, the node may have changed, so the current prompt - * may be invalid. + * Draws prompt according to the given 'node', except: + * + * * if is half_closed, draw nothing -- wipes the current line + * + * * if is cli_more_wait, draw the "--more--" prompt + * + * * if is cmd_in_progress, draw the vestigial prompt. + * + * By the time the current command completes, the node may have changed, so + * the current prompt may be invalid. * * Sets: cli_drawn = true + * cli_dirty = false * cli_prompt_len = length of prompt used * cli_extra_len = 0 * cli_echo_suppress = (AUTH_NODE or AUTH_ENABLE_NODE) */ static void -uty_cli_draw(vty_io vio, enum node_type node) +uty_cli_draw_this(vty_io vio, enum node_type node) { const char* prompt ; + size_t l_len ; + int p_len ; - if (vio->cli_drawn) - uty_cli_wipe(vio) ; - - vio->cli_drawn = 1 ; - vio->cli_extra_len = 0 ; - vio->cli_echo_suppress = (node == AUTH_NODE || node == AUTH_ENABLE_NODE) ; + if (vio->cli_dirty) + uty_cli_out_newline(vio) ; /* clears cli_dirty and cli_drawn */ /* Sort out what the prompt is. */ - - if (vio->cmd_in_progress) + if (vio->half_closed) + { + prompt = "" ; + p_len = 0 ; + l_len = 0 ; + } + else if (vio->cli_more_wait) + { + prompt = "--more--" ; + p_len = strlen(prompt) ; + l_len = 0 ; + } + else if (vio->cmd_in_progress) { /* If there is a queued command, the prompt is a minimal affair. */ prompt = "~ " ; - vio->cli_prompt_len = strlen(prompt) ; + p_len = strlen(prompt) ; + l_len = vio->cl.len ; } else { @@ -909,27 +1211,99 @@ uty_cli_draw(vty_io vio, enum node_type node) vio->cli_prompt_set = 1 ; } ; - prompt = qs_chars(&vio->cli_prompt_for_node) ; - vio->cli_prompt_len = vio->cli_prompt_for_node.len ; + prompt = vio->cli_prompt_for_node.body ; + p_len = vio->cli_prompt_for_node.len ; + l_len = vio->cl.len ; } ; - uty_cli_write(vio, prompt, vio->cli_prompt_len) ; + /* Now, if line is currently drawn, time to wipe it */ + if (vio->cli_drawn) + uty_cli_wipe(vio, p_len + l_len) ; + + /* Set about writing the prompt and the line */ + vio->cli_drawn = 1 ; + vio->cli_extra_len = 0 ; + vio->cli_echo_suppress = (node == AUTH_NODE || node == AUTH_ENABLE_NODE) ; + + vio->cli_prompt_len = p_len ; + + uty_cli_write(vio, prompt, p_len) ; - if ((vio->cl.len != 0) && !vio->cli_echo_suppress) + if (l_len != 0) { - uty_cli_write(vio, qs_chars(&vio->cl), vio->cl.len) ; - if (vio->cl.cp < vio->cl.len) - uty_cli_write_n(vio, telnet_backspaces, vio->cl.len - vio->cl.cp) ; + uty_cli_write(vio, qs_chars(&vio->cl), l_len) ; + if (vio->cl.cp < l_len) + uty_cli_write_n(vio, telnet_backspaces, l_len - vio->cl.cp) ; } ; } ; /*============================================================================== - * Command line processing loop + * Monitor output. + * + * To prepare for monitor output, wipe as much as is necessary for the + * monitor line to appear correctly. + * + * After monitor output, may need to do two things: + * + * * if the output was incomplete, place the rump in the CLI buffer, + * so that: + * + * a. don't mess up the console with partial lines + * + * b. don't lose part of a message + * + * c. act as a brake on further monitor output -- cannot do any more + * until the last, part, line is dealt with. + * + * * restore the command line, unless it is empty ! */ -#define CONTROL(X) ((X) - '@') + /*----------------------------------------------------------------------------- + * Prepare for new monitor output line. + * + * Wipe any existing command line. + */ +extern void +uty_cli_pre_monitor(vty_io vio, size_t len) +{ + VTY_ASSERT_LOCKED() ; + + uty_cli_wipe(vio, len) ; +} ; -static void uty_telnet_command(vty_io vio, keystroke stroke) ; +/*------------------------------------------------------------------------------ + * Recover from monitor line output. + * + * If monitor line failed to complete, append the rump to the CLI buffer. + * + * If have a non-empty command line, or is cli_more_wait, redraw the command + * line. + * + * Returns: 0 => rump was empty and no command line stuff written + * > 0 => rump not empty or some command line stuff written + */ +extern int +uty_cli_post_monitor(vty_io vio, const char* buf, size_t len) +{ + VTY_ASSERT_LOCKED() ; + + if (len != 0) + uty_cli_write(vio, buf, len) ; + + if (vio->cli_more_wait || (vio->cl.len != 0)) + { + uty_cli_draw(vio) ; + ++len ; + } ; + + return len ; +} ; + +/*============================================================================== + * Command line processing loop + */ + +static bool uty_telnet_command(vty_io vio, keystroke stroke, bool callback) ; static int uty_cli_insert (vty_io vio, const char* chars, int n) ; static int uty_cli_overwrite (vty_io vio, char* chars, int n) ; static int uty_cli_word_overwrite (vty_io vio, char *str) ; @@ -999,10 +1373,13 @@ uty_cli_process(vty_io vio, enum node_type node) while (1) { if (!vio->cli_drawn) - uty_cli_draw(vio, node) ; + uty_cli_draw_this(vio, node) ; if (!uty_cli_get_keystroke(vio, &stroke)) - break ; + { + ret = (stroke.value == knull_eof) ? cli_do_eof : cli_do_nothing ; + break ; + } ; if (stroke.flags != 0) { @@ -1160,10 +1537,10 @@ uty_cli_process(vty_io vio, enum node_type node) /* Telnet Command ----------------------------------------------------*/ case ks_iac: - uty_telnet_command(vio, &stroke) ; + uty_telnet_command(vio, &stroke, false) ; break ; - /* Single byte escape ------------------------------------------------*/ + /* Unknown -----------------------------------------------------------*/ default: zabort("unknown keystroke type") ; } ; @@ -1185,95 +1562,6 @@ uty_cli_process(vty_io vio, enum node_type node) } ; /*============================================================================== - * Support for the "--More--" handling - */ - -#define MSG_MORE_PROMPT "--more--" - -/*------------------------------------------------------------------------------ - * Output the "--more--" prompt and enter waiting for more state. - * - * As enters the waiting state, empties all the available input into the - * keystroke FIFO... so doesn't treat anything that arrived before the - * prompt was issued as a response to the prompt ! - * - * Enables read -- now waiting for response to --more-- - * - * NB: it is the caller's responsibility to clear the "--more--" prompt from - * the CLI buffer. - */ -extern void -uty_cli_want_more(vty_io vio) -{ - int get ; - - /* Empty the input buffers into the keystroke FIFO */ - do - { - get = uty_read(vio, NULL) ; - } while (get > 0) ; - - /* Output the prompt and update the state */ - uty_cli_write_s(vio, MSG_MORE_PROMPT) ; - - vio->cmd_wait_more = 1 ; - - uty_file_set_read(&vio->file, on) ; -} ; - -/*------------------------------------------------------------------------------ - * Waiting for response to "--more--" prompt. - * - * Steal the next available keystroke and process it. Error and EOF appear as - * a null keystroke. - * - * Wipes the prompt and exits cli_wait_more state when has stolen keystroke. - * - * Enables write -- need to clear the prompt from the cli buffers and may - * now continue command output, or signal it's done. - */ -extern void -uty_cli_wait_more(vty_io vio) -{ - struct keystroke steal ; - - /* The read fetches a reasonable lump from the I/O -- so if there is a - * complete keystroke available, expect to get it. - * - * If no complete keystroke available to steal, returns ks_null. - * - * If has hit EOF (or error etc), returns knull_eof. - */ - uty_read(vio, &steal) ; - - if ((steal.type == ks_null) || (steal.value != knull_eof)) - return ; - - /* Stolen a keystroke -- a (very) few terminate all output */ - if (steal.type == ks_char) - { - switch (steal.value) - { - case CONTROL('C'): - case 'q': - case 'Q': - uty_out_discard(vio) ; - break; - - default: /* evrything else, thrown away */ - break ; - } ; - } ; - - /* Wipe out the prompt and update state. */ - uty_cli_out_wipe_n(vio, - (int)strlen(MSG_MORE_PROMPT)) ; - - vio->cmd_wait_more = 0 ; - - uty_file_set_write(&vio->file, on) ; -} ; - -/*============================================================================== * Command line operations */ @@ -1289,7 +1577,7 @@ uty_cli_insert (vty_io vio, const char* chars, int n) VTY_ASSERT_LOCKED() ; - assert((vio->cl.cp <= vio->cl.len)&& (n >= 0)) ; + assert((vio->cl.cp <= vio->cl.len) && (n >= 0)) ; if (n <= 0) return n ; /* avoid trouble */ @@ -1359,12 +1647,12 @@ uty_cli_word_overwrite (vty_io vio, char *str) static int uty_cli_forwards(vty_io vio, int n) { - int c ; + int have ; VTY_ASSERT_LOCKED() ; - c = vio->cl.len - vio->cl.cp ; - if (n > c) - n = c ; + have = vio->cl.len - vio->cl.cp ; + if (have < n) + n = have ; assert(n >= 0) ; @@ -1385,17 +1673,18 @@ uty_cli_forwards(vty_io vio, int n) static int uty_cli_backwards(vty_io vio, int n) { - int c ; VTY_ASSERT_LOCKED() ; - c = vio->cl.len - vio->cl.cp ; - if (n > c) - n = c ; + if ((int)vio->cl.cp < n) + n = vio->cl.cp ; assert(n >= 0) ; - uty_cli_echo_n(vio, telnet_backspaces, n) ; - vio->cl.cp -= n ; + if (n > 0) + { + uty_cli_echo_n(vio, telnet_backspaces, n) ; + vio->cl.cp -= n ; + } ; return n ; } @@ -1409,10 +1698,15 @@ static int uty_cli_del_forwards(vty_io vio, int n) { int after ; + int have ; VTY_ASSERT_LOCKED() ; - assert((vio->cl.len - vio->cl.cp) && (n >= 0)) ; + have = vio->cl.len - vio->cl.cp ; + if (have < n) + n = have ; /* cannot delete more than have */ + + assert(n >= 0) ; if (n <= 0) return 0 ; @@ -1425,8 +1719,6 @@ uty_cli_del_forwards(vty_io vio, int n) uty_cli_echo_n(vio, telnet_spaces, n) ; uty_cli_echo_n(vio, telnet_backspaces, after + n) ; - vio->cl.len -= n ; - return n ; } @@ -1660,31 +1952,18 @@ uty_cli_transpose_chars(vty_io vio) extern void uty_cli_hist_add (vty_io vio, const char* cmd_line) { - char* prev_line ; - char* line ; - char* e ; + qstring prev_line ; + qstring_t line ; int prev_index ; VTY_ASSERT_LOCKED() ; + /* Construct a dummy qstring for the given command line */ + qs_dummy(&line, cmd_line, 1) ; /* set cursor to the end */ + /* make sure have a suitable history vector */ vector_set_min_length(&vio->hist, VTY_MAXHIST) ; - /* trim leading and trailing spaces before considering for history */ - - while (*cmd_line == ' ') - ++cmd_line ; /* hack off leading spaces */ - - if (*cmd_line == '\0') - return ; /* ignore empty lines */ - - line = XSTRDUP(MTYPE_VTY_HIST, cmd_line) ; - - e = line + strlen(line) ; - while (*(e - 1) == ' ') - --e ; - *e = '\0' ; /* hack off any trailing spaces */ - /* find the previous command line in the history */ prev_index = vio->hindex - 1 ; if (prev_index < 0) @@ -1692,24 +1971,27 @@ uty_cli_hist_add (vty_io vio, const char* cmd_line) prev_line = vector_get_item(&vio->hist, prev_index) ; - /* Insert unless same as previous line */ - if ((prev_line == NULL) || (strcmp(line, prev_line) != 0)) - { - /* Insert history entry. */ - if (prev_line != NULL) - XFREE (MTYPE_VTY_HIST, prev_line) ; + /* If the previous line is NULL, that means the history is empty. + * + * If the previous line is essentially the same as the current line, + * replace it with the current line -- so that the latest whitespace + * version is saved. + * + * Either way, replace the the previous line entry by moving hindex + * back ! + */ + if ((prev_line == NULL) || (qs_cmp_sig(prev_line, &line) == 0)) + vio->hindex = prev_index ; + else + prev_line = vector_get_item(&vio->hist, vio->hindex) ; - vector_set_item(&vio->hist, vio->hindex, line) ; + /* Now replace the hindex entry */ + vector_set_item(&vio->hist, vio->hindex, qs_copy(prev_line, &line)) ; - /* History index rotation. */ - vio->hindex++; - if (vio->hindex == VTY_MAXHIST) - vio->hindex = 0; - } - else - { - XFREE(MTYPE_VTY_HIST, line) ; - } ; + /* Advance to the near future and reset the history pointer */ + vio->hindex++; + if (vio->hindex == VTY_MAXHIST) + vio->hindex = 0; vio->hp = vio->hindex; } ; @@ -1718,36 +2000,58 @@ uty_cli_hist_add (vty_io vio, const char* cmd_line) * Replace command line by current history. * * This function is called from vty_next_line and vty_previous_line. + * + * Step +1 is towards the present + * -1 is into the past */ static void uty_cli_history_use(vty_io vio, int step) { int index ; - unsigned new_len ; unsigned old_len ; - char* hist ; + unsigned after ; + unsigned back ; + qstring hist ; VTY_ASSERT_LOCKED() ; - /* See if have anything usable */ + assert((step == +1) || (step == -1)) ; + index = vio->hp ; - if ((step > 0) && (index == vio->hindex)) - return ; /* cannot step forward past the insertion point */ + /* Special case of being at the insertion point */ + if (index == vio->hindex) + { + if (step > 0) + return ; /* already in the present */ + /* before stepping back from the present, take a copy of the + * current command line -- so can get back to it. + */ + hist = vector_get_item(&vio->hist, vio->hindex) ; + vector_set_item(&vio->hist, vio->hindex, qs_copy(hist, &vio->cl)) ; + } ; + + /* Advance or retreat */ index += step ; if (index < 0) index = VTY_MAXHIST - 1 ; - else if (index >= VTY_MAXHIST) ; + else if (index >= VTY_MAXHIST) index = 0 ; - if ((step < 0) && (index == vio->hindex)) - return ; /* cannot step back to the insertion point */ - hist = vector_get_item(&vio->hist, index) ; - if (hist == NULL) - return ; /* cannot step to unused entry */ + /* If moving backwards in time, may not move back to the insertion + * point (that would be wrapping round to the present) and may not + * move back to a NULL entry (that would be going back before '.'). + */ + if (step < 0) + if ((hist == NULL) || (index == vio->hindex)) + return ; + + /* Now, if arrived at the insertion point, this is returning to the + * present, which is fine. + */ vio->hp = index; /* Move back to the start of the current line */ @@ -1755,13 +2059,26 @@ uty_cli_history_use(vty_io vio, int step) /* Get previous line from history buffer and echo that */ old_len = vio->cl.len ; - new_len = qs_set(&vio->cl, hist) ; - vio->cl.cp = new_len ; + qs_copy(&vio->cl, hist) ; + + /* Sort out wiping out any excess and setting the cursor position */ + if (old_len > vio->cl.len) + after = old_len - vio->cl.len ; + else + after = 0 ; - uty_cli_echo(vio, hist, new_len) ; + back = after ; + if (vio->cl.len > vio->cl.cp) + back += (vio->cl.len - vio->cl.cp) ; + + if (vio->cl.len > 0) + uty_cli_echo(vio, vio->cl.body, vio->cl.len) ; + + if (after > 0) + uty_cli_echo_n(vio, telnet_spaces, after) ; - if (old_len < new_len) - uty_cli_del_to_eol(vio) ; + if (back > 0) + uty_cli_echo_n(vio, telnet_backspaces, back) ; return ; } ; @@ -1804,6 +2121,8 @@ uty_cli_complete_command (vty_io vio, enum node_type node) { unsigned i ; int ret ; + int len ; + int n ; vector matched ; vector vline ; @@ -1841,16 +2160,32 @@ uty_cli_complete_command (vty_io vio, enum node_type node) break ; case CMD_COMPLETE_LIST_MATCH: + len = 6 ; for (i = 0; i < vector_end(matched); i++) { - if ((i % 6) == 0) + int sl = strlen((char*)vector_get_item(matched, i)) ; + if (len < sl) + len = sl ; + } ; + + n = vio->width ; + if (n == 0) + n = 60 ; + n = n / (len + 2) ; + if (n == 0) + n = 1 ; + + for (i = 0; i < vector_end(matched); i++) + { + if ((i % n) == 0) uty_cli_out_newline(vio) ; /* clears cli_drawn */ - uty_cli_out (vio, "%-10s ", (char*)vector_get_item(matched, i)); + uty_cli_out(vio, "%-*s ", len, (char*)vector_get_item(matched, i)); } + uty_cli_out_newline(vio) ; break; - case CMD_ERR_NOTHING_TODO: + case CMD_COMPLETE_ALREADY: default: break; } ; @@ -2052,7 +2387,7 @@ uty_cli_cmd_prepare(vty_io vio, int help) * VTY telnet stuff */ -#define TELNET_OPTION_DEBUG 1 /* 0 to turn off */ +#define TELNET_OPTION_DEBUG 0 /* 0 to turn off */ static const char* telnet_commands[256] = { @@ -2150,7 +2485,7 @@ uty_cli_out_hex(vty_io vio, const char* str, unsigned char u) /*------------------------------------------------------------------------------ * Send telnet: "WILL TELOPT_ECHO" */ -extern void +static void uty_will_echo (vty_io vio) { unsigned char cmd[] = { tn_IAC, tn_WILL, to_ECHO }; @@ -2161,7 +2496,7 @@ uty_will_echo (vty_io vio) /*------------------------------------------------------------------------------ * Send telnet: "suppress Go-Ahead" */ -extern void +static void uty_will_suppress_go_ahead (vty_io vio) { unsigned char cmd[] = { tn_IAC, tn_WILL, to_SGA }; @@ -2172,7 +2507,7 @@ uty_will_suppress_go_ahead (vty_io vio) /*------------------------------------------------------------------------------ * Send telnet: "don't use linemode" */ -extern void +static void uty_dont_linemode (vty_io vio) { unsigned char cmd[] = { tn_IAC, tn_DONT, to_LINEMODE }; @@ -2183,7 +2518,7 @@ uty_dont_linemode (vty_io vio) /*------------------------------------------------------------------------------ * Send telnet: "Use window size" */ -extern void +static void uty_do_window_size (vty_io vio) { unsigned char cmd[] = { tn_IAC, tn_DO, to_NAWS }; @@ -2194,7 +2529,7 @@ uty_do_window_size (vty_io vio) /*------------------------------------------------------------------------------ * Send telnet: "don't use lflow" -- not currently used */ -extern void +static void uty_dont_lflow_ahead (vty_io vio) { unsigned char cmd[] = { tn_IAC, tn_DONT, to_LFLOW }; @@ -2203,20 +2538,42 @@ uty_dont_lflow_ahead (vty_io vio) } /*------------------------------------------------------------------------------ + * The keystroke iac callback function. + * + * This deals with IAC sequences that should be dealt with as soon as they + * are read -- not stored in the keystroke stream for later processing. + */ +extern bool +uty_cli_iac_callback(keystroke_iac_callback_args) +{ + return uty_telnet_command((vty_io)context, stroke, true) ; +} ; + +/*------------------------------------------------------------------------------ * Process incoming Telnet Option(s) * + * May be called during keystroke iac callback, or when processing CLI + * keystrokes. + * * In particular: get telnet window size. + * + * Returns: true <=> dealt with, for: + * + * * telnet window size. */ -static void -uty_telnet_command(vty_io vio, keystroke stroke) +static bool +uty_telnet_command(vty_io vio, keystroke stroke, bool callback) { uint8_t* p ; uint8_t o ; int left ; + bool dealt_with ; /* Echo to the other end if required */ if (TELNET_OPTION_DEBUG) { + uty_cli_wipe(vio, 0) ; + p = stroke->buf ; left = stroke->len ; @@ -2245,16 +2602,17 @@ uty_telnet_command(vty_io vio, keystroke stroke) } } ; - if (!(stroke->flags & kf_broken)) + if (stroke->flags & kf_broken) uty_cli_out (vio, "BROKEN") ; uty_cli_out (vio, "\r\n") ; - } ; /* Process the telnet command */ + dealt_with = false ; + if (stroke->flags != 0) - return ; /* go no further if broken */ + return dealt_with ; /* go no further if broken */ p = stroke->buf ; left = stroke->len ; @@ -2294,6 +2652,9 @@ uty_telnet_command(vty_io vio, keystroke stroke) uty_cli_out(vio, "TELNET NAWS window size received: " "width %d, height %d%s", vio->width, vio->height, telnet_newline) ; + uty_set_height(vio) ; + + dealt_with = true ; } ; break ; @@ -2305,4 +2666,6 @@ uty_telnet_command(vty_io vio, keystroke stroke) default: /* no other IAC X */ break ; } ; + + return dealt_with ; } ; |