diff options
Diffstat (limited to 'lib/vty_pipe.c')
-rw-r--r-- | lib/vty_pipe.c | 2742 |
1 files changed, 185 insertions, 2557 deletions
diff --git a/lib/vty_pipe.c b/lib/vty_pipe.c index 3276085c..ad86e30c 100644 --- a/lib/vty_pipe.c +++ b/lib/vty_pipe.c @@ -188,2681 +188,309 @@ * * */ - -/*============================================================================== - * - */ - #if 0 - - - - - - - - - - - -static char* vty_host_name = NULL ; -int vty_host_name_set = 0 ; - -static void uty_new_host_name(const char* name) ; - -/*------------------------------------------------------------------------------ - * Update vty_host_name as per "hostname" or "no hostname" command - */ -extern void -uty_set_host_name(const char* name) -{ - VTY_ASSERT_LOCKED() ; - - vty_host_name_set = (name != NULL) ; - - if (vty_host_name_set) - uty_new_host_name(name) ; - else - uty_check_host_name() ; -} ; - -/*------------------------------------------------------------------------------ - * If vty_host_name is set, free it. - */ -extern void -uty_free_host_name(void) -{ - if (vty_host_name != NULL) - XFREE (MTYPE_HOST, vty_host_name) ; /* sets vty_host_name = NULL */ -} ; - -/*------------------------------------------------------------------------------ - * If the host name is not set by command, see if the actual host name has - * changed, and if so change it. - * - * This is done periodically in case the actual host name changes ! - */ -extern void -uty_check_host_name(void) -{ - struct utsname names ; - - VTY_ASSERT_LOCKED() ; - - if (vty_host_name_set) - return ; /* nothing to do if set by command */ - - uname (&names) ; - - if ((vty_host_name == NULL) || (strcmp(vty_host_name, names.nodename) != 0)) - uty_new_host_name(names.nodename) ; -} ; - -/*------------------------------------------------------------------------------ - * Set new vty_host_name and run along list of VTYs to mark the change. - */ -static void -uty_new_host_name(const char* name) -{ - vty_io vio ; - - VTY_ASSERT_LOCKED() ; - - uty_free_host_name() ; - vty_host_name = XSTRDUP(MTYPE_HOST, name) ; - - vio = vio_list_base ; - while (vio != NULL) - { - vio->cli_prompt_set = 0 ; - vio = sdl_next(vio, vio_list) ; - } ; -} ; - /*============================================================================== - * General mechanism for command execution. - * - * Command execution is driven by select/pselect -- which means that the - * processing of commands is multiplexed with all other activity. In the - * following: - * - * -- read_ready and write_ready are events signalled by select/pselect - * - * -- setting read or write on, means enabling the file for select/pselect to - * consider it for read_ready or write_ready, respectively. - * - * State of the CLI: - * - * cli_blocked -- the CLI is unable to process any further keystrokes. - * - * cmd_in_progress -- a command has been dispatched and has not yet - * completed (may have been queued). - * - * cmd_out_enabled -- the command FIFO is may be emptied. - * - * This is set when a command completes, and cleared when - * everything is written away. - * - * cli_more_wait -- is in "--more--" wait state - * - * The following are the valid combinations: - * - * 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 - * - * 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 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 - * invokes read_ready. - * - * Note also that after each command dispatch the CLI processor exits, to be - * re-entered again on write_ready/read_ready -- so does one command line at - * a time, yielding the processor after each one. - * - * Note that select/pselect treat a socket which is at "EOF", or has seen an - * error, or has been half closed, etc. as readable and writable. This means - * that the CLI will continue to move forward even after the socket is no - * longer delivering any data. - * - *------------------------------------------------------------------------------ - * The "--more--" handling. - * - * This is largely buried in the output handling. - * - * 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 - * - * * the output process is kicked off by setting write on - * - * 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 cli_more_wait flag and its handling.) - * - * 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. - * - * This mechanism means that the command output FIFO only ever contains the - * output from the last command executed. - * - * If the user decides to abandon output at the "--more--" prompt, then the - * contents of the command output FIFO are discarded. - * - *------------------------------------------------------------------------------ - * The qpthreads/qpnexus extension. - * - * When running in qnexus mode, many commands are not executed directly in the - * CLI, but are queued for execution by the main "routeing" nexus. - * - * The parsing of commands is context sensitive. The context depends may - * change during the execution of a command. So... it is not possible to - * dispatch a command until the previous one has completed. - * - * In qnexus mode, when a command is queued, the CLI does not go cli_blocked, - * even if some output has already been generated. This allows a further - * command to be entered while the previous one is executed. However, if the - * command is dispatched before the previous one completes, then the cli will - * block. - * - * While the previous command is executing, the current command line has a - * minimal prompt -- to show that the context is not known. When the previous - * command completes, the command line is redrawn in the new context. - * - *------------------------------------------------------------------------------ - * Command line drawn state. - * - * When the cli_drawn flag is set, the current console line contains the - * current prompt and the user input to date. The cursor is positioned where - * the user last placed it. - * - * The command line can be "wiped" -- see uty_cli_wipe() -- which removes all - * output and prompt, and leaves the console at the start of an empty line - * where the command line used to be. - * - * On entry to the CLI, it will draw the command line again if it has been - * wiped. - * - * This mechanism is used to support the partial command line that may be - * entered while a queued command executes. - * - * It is also used for the command help/completion system. - * - * It is also used to support the "monitor" output. - */ - -/*============================================================================== - * 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. - * - * It is assumed that the following have been initialised, empty or zero: * - * cli_prompt_for_node - * cl - * clx - * cli_vbuf - * cli_obuf - * - * cli_drawn - * cli_dirty - * - * cli_prompt_set - * - * 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) -{ - VTY_ASSERT_LOCKED() ; - assert(vio->type == VTY_TERM) ; - - 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) ; +#include "misc.h" - if (vio->half_closed) - return not_ready ; /* Nothing more if half closed */ +#include "command_local.h" - /* Standard or "--more--" CLI ? */ - if (vio->cli_more_wait) - return uty_cli_more_wait(vio) ; - else - return uty_cli_standard(vio) ; -} ; +#include "vty_io_pipe.h" +#include "vty_io_file.h" +#include "vty_io_basic.h" /*============================================================================== - * The Standard CLI - */ - -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 bool uty_cli_dispatch(vty_io vio) ; - -/*------------------------------------------------------------------------------ - * Standard CLI for VTY_TERM -- if not blocked, runs until: - * - * * runs out of keystrokes - * * executes a command - * - * 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 - */ -static enum vty_readiness -uty_cli_standard(vty_io vio) -{ - VTY_ASSERT_LOCKED() ; - assert(vio->type == VTY_TERM) ; - - /* cli_blocked is set when is waiting for a command, or its output to - * complete -- unless either of those has happened, is still blocked. - * - * NB: in both these cases, assumes that other forces are at work to - * keep things moving. - */ - if (vio->cli_blocked) - { - assert(vio->cmd_in_progress || vio->cmd_out_enabled) ; - - 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) - 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) - { - /* Reflect immediate response */ - uty_cli_response(vio, vio->cli_do) ; - - /* If command not already in progress, dispatch this one, which may - * set the CLI blocked. - * - * Otherwise is now blocked until queued command completes. - */ - if (!vio->cmd_in_progress) - vio->cli_blocked = uty_cli_dispatch(vio) ; - else - vio->cli_blocked = 1 ; - } ; - - /* Use write_ready as a proxy for read_ready on the keystroke stream. - * - * Also, if the command line is not drawn, then return write_ready, so - * that - * - * 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. - */ - if (keystroke_stream_empty(vio->key_stream)) - return read_ready ; - else - return write_ready ; -} ; - -/*------------------------------------------------------------------------------ - * Dispatch the current vio->cli_do -- queueing it if necessary. - * - * Requires that are NOT blocked and NO command is queued. - * - * Expects to be on new blank line, and when returns will be on new, blank - * line. - * - * 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 bool -uty_cli_dispatch(vty_io vio) -{ - qstring_t tmp ; - enum cli_do cli_do ; - enum cmd_return_code ret ; - - struct vty* vty = vio->vty ; - - VTY_ASSERT_LOCKED() ; - assert(!vio->cli_blocked && !vio->cmd_in_progress) ; - - /* Set vio->clx to the command about to execute. - * - * Clear vio->cl and vio->cl_do. - */ - 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 ; - vio->cl = tmp ; - - qs_term(&vio->clx) ; /* ensure string is terminated */ - vty->buf = qs_chars(&vio->clx) ; /* terminated command line */ - cli_do = vio->cli_do ; /* current operation */ - - vio->cli_do = cli_do_nothing ; /* clear */ - qs_clear(vio->cl) ; /* set cl empty (with '\0') */ - - /* Reset the command output FIFO and line_control */ - assert(vio_fifo_empty(&vio->cmd_obuf)) ; - 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 */ - ret = uty_auth(vty, vty->buf, cli_do) ; - } - else - { - /* All other nodes... */ - switch (cli_do) - { - case cli_do_nothing: - ret = CMD_SUCCESS ; - break ; - - case cli_do_command: - ret = uty_dispatch_command(vty) ; - break ; - - case cli_do_ctrl_c: - ret = uty_stop_input(vty) ; - break ; - - case cli_do_ctrl_d: - ret = uty_down_level(vty) ; - break ; - - case cli_do_ctrl_z: - ret = uty_command(vty) ; - if (ret == CMD_QUEUED) - vio->cli_do = cli_do_ctrl_z ; /* defer the ^Z action */ - else - ret = uty_end_config(vty) ; /* do the ^Z now */ - break ; - - case cli_do_eof: - ret = uty_cmd_close(vio->vty, "End") ; - break ; - - default: - zabort("unknown cli_do_xxx value") ; - } ; - } ; - - if (ret == CMD_QUEUED) - { - uty_cli_draw(vio) ; /* draw the prompt */ - return false ; /* command not complete */ - } - else - { - uty_cli_cmd_complete(vio, ret) ; - return true ; /* command complete */ - } ; -} ; - -/*------------------------------------------------------------------------------ - * Queued command has completed. - * - * Note that sets write on whether there is anything in the output buffer - * or not... write_ready will kick read_ready. - */ -extern void -vty_queued_result(struct vty *vty, enum cmd_return_code ret) -{ - vty_io vio ; - - VTY_LOCK() ; - - vio = vty->vio ; - - if (!vio->closed) - { - uty_cli_wipe(vio, 0) ; /* wipe any partly constructed line */ - - /* 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) ; - - /* 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 */ - } ; - - 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. + * VTY File Output * - * This ensures some multiplexing at the command level. + * This is for input and output of configuration files and piped stuff. * - * It also means that the decision about whether there is anything to output - * is left to the output code. + * When reading the configuration (and piped stuff in the configuration) I/O + * is blocking... nothing else can run while this is going on. Otherwise, + * all I/O is non-blocking. */ -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. + * Prototypes. */ -extern void -uty_cli_enter_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--" */ -} ; /*------------------------------------------------------------------------------ - * Exit the "--more--" CLI. * - * Wipes the "--more--" prompt. * - * This is used when the user responds to the prompt. - * - * It is also used when the vty is "half-closed". In this case, it is (just) - * possible that the '--more--' prompt is yet to be completely written away, - * so: - * - * * assert that is either: !vio->cli_blocked (most of the time it will) - * or: !vio_fifo_empty(&vio->cli_obuf) - * - * * note that can wipe the prompt even though it hasn't been fully - * written away yet. (The effect is to append the wipe action to the - * cli_obuf !) */ -extern void -uty_cli_exit_more_wait(vty_io vio) +extern cmd_return_code_t +uty_file_read_open(vty_io vio, qstring name, bool reflect) { - VTY_ASSERT_LOCKED() ; + const char* name_str ; + int fd ; + vio_vf vf ; + vfd_io_type_t iot ; - assert( (!vio->cli_blocked || !vio_fifo_empty(&vio->cli_obuf)) - && !vio->cmd_out_enabled && vio->cli_more_wait) ; + name_str = qs_make_string(name) ; - uty_cli_wipe(vio, 0) ; /* wipe the prompt ('--more--') - before changing the CLI state */ + iot = vfd_io_read | vfd_io_blocking ; /* TODO blocking */ - 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 */ -} ; + /* Do the basic file open. */ + fd = uty_vfd_file_open(name_str, iot) ; -/*------------------------------------------------------------------------------ - * 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) + if (fd < 0) { - 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) ; - } ; + uty_out(vio, "%% Could not open input file %s\n", name_str) ; - /* 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_exit_more_wait(vio) ; - - return now_ready ; -} ; - -/*============================================================================== - * CLI VTY output - * - * This is buffered separately from the general (command) VTY output above. - * - * Has a dedicated buffer in the struct vty, which is flushed regularly during - * command processing. - * - * It is expected that can flush straight to the file, since this is running at - * CLI speed. However, if the CLI is being driven by something other than a - * keyboard, or "monitor" output has filled the buffers, then may need to - * have intermediate buffering. - * - * No actual I/O takes place here-- all "output" is to vio->cli_obuf - * and/or vio->cli_ex_obuf - * - * The "cli_echo" functions discard the output if vio->cli_echo_suppress != 0. - * This is used while passwords are entered and to allow command line changes - * to be made while the line is not visible. - */ - -enum { cli_rep_count = 32 } ; - -typedef const char cli_rep_char[cli_rep_count] ; - -static const char telnet_backspaces[] = - { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 - } ; -CONFIRM(sizeof(telnet_backspaces) == sizeof(cli_rep_char)) ; - -static const char telnet_spaces[] = - { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', - ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', - ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', - ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' - } ; -CONFIRM(sizeof(telnet_spaces) == sizeof(cli_rep_char)) ; + return CMD_WARNING ; + } -static const char* telnet_newline = "\r\n" ; + /* OK -- now push the new input onto the vin_stack. */ + vf = uty_vf_new(vio, name_str, fd, vfd_file, iot) ; + uty_vin_push(vio, vf, VIN_FILE, NULL, NULL, 16 * 1024) ; -static void uty_cli_write(vty_io vio, const char *this, int len) ; -static void uty_cli_write_n(vty_io vio, cli_rep_char chars, int n) ; + vf->parse_type = cmd_parse_strict ; + vf->reflect_enabled = reflect ; -/*------------------------------------------------------------------------------ - * CLI VTY output -- cf fprintf() - */ -static void -uty_cli_out(vty_io vio, const char *format, ...) -{ - VTY_ASSERT_LOCKED() ; - - if (vio->sock.write_open) - { - va_list args ; - - va_start (args, format); - vio_fifo_vprintf(&vio->cli_obuf, format, args) ; - va_end(args); - } ; + return CMD_SUCCESS ; } ; /*------------------------------------------------------------------------------ - * CLI VTY output -- echo user input * - * 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) -{ - VTY_ASSERT_LOCKED() ; - - if (vio->cli_echo_suppress || !vio->sock.write_open) - return ; - - uty_cli_write(vio, this, len) ; -} - -/*------------------------------------------------------------------------------ - * CLI VTY output -- echo 'n' characters using a cli_rep_char string * - * Do nothing if echo suppressed (eg in AUTH_NODE) */ -static void -uty_cli_echo_n(vty_io vio, cli_rep_char chars, int n) +extern cmd_return_code_t +uty_file_write_open(vty_io vio, qstring name, bool append) { - VTY_ASSERT_LOCKED() ; + const char* name_str ; + int fd ; + vio_vf vf ; + vfd_io_type_t iot ; - if (vio->cli_echo_suppress || !vio->sock.write_open) - return ; + iot = vfd_io_write | vfd_io_blocking ; /* TODO blocking */ - uty_cli_write_n(vio, chars, n) ; -} + if (append) + iot |= vfd_io_append ; -/*------------------------------------------------------------------------------ - * CLI VTY output -- cf write() - */ -inline static void -uty_cli_write(vty_io vio, const char *this, int len) -{ - VTY_ASSERT_LOCKED() ; + name_str = qs_make_string(name) ; - if (vio->sock.write_open) - vio_fifo_put(&vio->cli_obuf, this, len) ; -} ; + /* Do the basic file open. */ + fd = uty_vfd_file_open(name_str, iot) ; -/*------------------------------------------------------------------------------ - * CLI VTY output -- write 'n' characters using a cli_rep_char string - */ -static void -uty_cli_write_n(vty_io vio, cli_rep_char chars, int n) -{ - int len ; - - len = sizeof(cli_rep_char) ; - while (n > 0) + if (fd < 0) { - if (n < len) - len = n ; - uty_cli_write(vio, chars, len) ; - n -= len ; - } ; -} ; + uty_out(vio, "%% Could not open output file %s\n", name_str) ; -/*------------------------------------------------------------------------------ - * CLI VTY output -- write given string - * - * Returns length of string. - */ -static int -uty_cli_write_s(vty_io vio, const char *str) -{ - int len ; + return CMD_WARNING ; + } - len = strlen(str) ; - if (len != 0) - uty_cli_write(vio, str, len) ; + /* OK -- now push the new input onto the vin_stack. */ + vf = uty_vf_new(vio, name_str, fd, vfd_file, iot) ; + uty_vout_push(vio, vf, VOUT_FILE, NULL, NULL, 16 * 1024) ; - return len ; + return CMD_SUCCESS ; } ; /*============================================================================== - * Standard Messages - */ - -/*------------------------------------------------------------------------------ - * Send newline to the console. + * Command line fetch from a file or pipe. * - * Clears the cli_drawn and the cli_dirty flags. + * Returns: CMD_SUCCESS -- have another command line ready to go + * CMD_WAITING -- do not have a command line at the moment + * CMD_EOF -- ran into EOF + * CMD_IO_ERROR -- ran into an I/O error */ -static void -uty_cli_out_newline(vty_io vio) +extern cmd_return_code_t +uty_file_fetch_command_line(vio_vf vf, qstring* line) { - uty_cli_write(vio, telnet_newline, 2) ; - vio->cli_drawn = 0 ; - vio->cli_dirty = 0 ; -} ; + assert(vf->vin_state == vf_open) ; -/*------------------------------------------------------------------------------ - * Wipe 'n' characters. - * - * If 'n' < 0, wipes characters backwards and moves cursor back. - * 'n' > 0, wipes characters forwards, leaving cursor where it is - */ -static void -uty_cli_out_wipe_n(vty_io vio, int n) -{ - if (n < 0) + if (vf->line_complete) { - n = abs(n) ; - uty_cli_write_n(vio, telnet_backspaces, n); - } ; - - if (n > 0) - { - uty_cli_write_n(vio, telnet_spaces, n) ; - uty_cli_write_n(vio, telnet_backspaces, n) ; - } ; -} ; - -/*------------------------------------------------------------------------------ - * Send response to the given cli_do - * - * If no command is in progress, then will send newline to signal that the - * command is about to be dispatched. - * - * If command is in progress, then leaves cursor on '^' to signal that is now - * waiting for previous command to complete. - */ -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_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_eof] = "^*" - } -} ; - -static void -uty_cli_response(vty_io vio, enum cli_do cli_do) -{ - const char* str ; - int len ; - - if ((cli_do == cli_do_nothing) || (vio->half_closed)) - return ; + vio_fifo_set_hold_mark(vf->ibuf) ; /* advance hold */ - str = (cli_do < cli_do_count) - ? cli_response[vio->cmd_in_progress ? 1 : 0][cli_do] : NULL ; - assert(str != NULL) ; + vf->line_complete = false ; + vf->line_number += vf->line_step ; - len = uty_cli_write_s(vio, str) ; - - if (vio->cmd_in_progress) - { - vio->cli_extra_len = len ; - uty_cli_write_n(vio, telnet_backspaces, len) ; - } - else - { - uty_cli_out_newline(vio) ; + qs_set_len_nn(vf->cl, 0) ; + vf->line_step = 0 ; } ; -} ; - -/*------------------------------------------------------------------------------ - * Send various messages with trailing newline. - */ -static void -uty_cli_out_CMD_ERR_AMBIGUOUS(vty_io vio) -{ - uty_cli_write_s(vio, "% " MSG_CMD_ERR_AMBIGUOUS ".") ; - uty_cli_out_newline(vio) ; -} ; - -static void -uty_cli_out_CMD_ERR_NO_MATCH(vty_io vio) -{ - uty_cli_write_s(vio, "% " MSG_CMD_ERR_NO_MATCH ".") ; - uty_cli_out_newline(vio) ; -} ; - -/*============================================================================== - * Command line draw and wipe - */ - -/*------------------------------------------------------------------------------ - * Wipe the current console line -- if any. - */ -static void -uty_cli_wipe(vty_io vio, int len) -{ - int a ; - int b ; - - if (!vio->cli_drawn) - return ; /* quit if already wiped */ - - assert(vio->cl.cp <= vio->cl.len) ; - - /* 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) - { - a += vio->cl.len - vio->cl.cp ; - b += vio->cl.cp ; - } ; - - /* Wipe anything ahead of the current position and ahead of new len */ - if ((a + b) > len) - uty_cli_out_wipe_n(vio, +a) ; - - /* Wipe anything behind current position, but ahead of new len */ - if (b > len) + while (1) { - uty_cli_out_wipe_n(vio, -(b - len)) ; - b = len ; /* moved the cursor back */ - } ; + char* s, * p, * q, * e ; + size_t have ; + ulen len ; - /* Back to the beginning of the line */ - uty_cli_write_n(vio, telnet_backspaces, b) ; + s = vio_fifo_get(vf->ibuf, &have) ; - /* Nothing there any more */ - vio->cli_drawn = 0 ; - vio->cli_dirty = 0 ; -} ; + /* If nothing in hand, try and get some more. + * + * Either exits or loops back to set s & have. + */ + if (have == 0) + { + int get ; -/*------------------------------------------------------------------------------ - * 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 ; + get = vio_fifo_read_nb(vf->ibuf, vio_vfd_fd(vf->vfd), 100) ; - uty_cli_draw(vio) ; + if (get > 0) + continue ; /* loop back */ - return true ; -} ; + if (get == 0) + return CMD_WAITING ; /* need to set read ready ! */ -/*------------------------------------------------------------------------------ - * 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) ; -} ; + if (get == -1) + ; /* register error */ -/*------------------------------------------------------------------------------ - * Draw prompt and entire command line, leaving current position where it - * should be. - * - * If command line is currently drawn, this wipes and redraws. - * - * Otherwise, assumes is positioned at start of an empty line. - * - * 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_this(vty_io vio, enum node_type node) -{ - const char* prompt ; - size_t l_len ; - int p_len ; - - if (vio->cli_dirty) - uty_cli_out_newline(vio) ; /* clears cli_dirty and cli_drawn */ + if (get == -2) + return (qs_len_nn(vf->cl) > 0) ? CMD_SUCCESS : CMD_EOF ; + } ; - /* Sort out what the prompt is. */ - 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 = "~ " ; - p_len = strlen(prompt) ; - l_len = vio->cl.len ; - } - else - { - /* The prompt depends on the node, and is expected to include the - * host name. + /* Try to find a '\n' -- converting all other control chars to ' ' * - * Caches the prompt so doesn't re-evaluate it every time. + * When we find '\n' step back across any trailing ' ' (which includes + * any control chars before the '\n'). * - * If the host name changes, the cli_prompt_set flag is cleared. + * This means that we cope with "\r\n" line terminators. But not anything + * more exotic. */ - if (!vio->cli_prompt_set || (node != vio->cli_prompt_node)) - { - const char* prompt ; - - if (vty_host_name == NULL) - uty_check_host_name() ; /* should never be required */ + p = s ; + e = s + have ; /* have != 0 */ + q = NULL ; - prompt = cmd_prompt(node) ; - if (prompt == NULL) + while (p < e) + { + if (*p++ < 0x20) { - zlog_err("vty %s has node %d", uty_get_name(vio), node) ; - prompt = "%s ???: " ; - } ; + if (*(p-1) != '\n') + { + *(p-1) = ' ' ; /* everything other than '\n' */ + continue ; + } ; - qs_printf(vio->cli_prompt_for_node, prompt, vty_host_name); + ++vf->line_step ; /* got a '\n' */ - vio->cli_prompt_node = node ; - vio->cli_prompt_set = 1 ; + q = p ; /* point just past '\n' */ + do --q ; while ((q > s) && (*(q-1) == ' ')) ; + /* discard trailing "spaces" */ + break ; + } ; } ; - prompt = vio->cli_prompt_for_node.body ; - p_len = vio->cli_prompt_for_node.len ; - l_len = vio->cl.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 (l_len != 0) - { - 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) ; - } ; -} ; - -/*============================================================================== - * 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 ! - */ - - /*----------------------------------------------------------------------------- - * 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) ; -} ; - -/*------------------------------------------------------------------------------ - * 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) ; -static int uty_cli_forwards(vty_io vio, int n) ; -static int uty_cli_backwards(vty_io vio, int n) ; -static int uty_cli_del_forwards(vty_io vio, int n) ; -static int uty_cli_del_backwards(vty_io vio, int n) ; -static int uty_cli_bol (vty_io vio) ; -static int uty_cli_eol (vty_io vio) ; -static int uty_cli_word_forwards_delta(vty_io vio) ; -static int uty_cli_word_forwards(vty_io vio) ; -static int uty_cli_word_backwards_delta(vty_io vio, int eat_spaces) ; -static int uty_cli_word_backwards_pure (vty_io vio) ; -static int uty_cli_word_backwards (vty_io vio) ; -static int uty_cli_del_word_forwards(vty_io vio) ; -static int uty_cli_del_word_backwards(vty_io vio) ; -static int uty_cli_del_to_eol (vty_io vio) ; -static int uty_cli_clear_line(vty_io vio) ; -static int uty_cli_transpose_chars(vty_io vio) ; -static void uty_cli_history_use(vty_io vio, int step) ; -static void uty_cli_next_line(vty_io vio) ; -static void uty_cli_previous_line (vty_io vio) ; -static void uty_cli_complete_command (vty_io vio, enum node_type node) ; -static void uty_cli_describe_command (vty_io vio, enum node_type node) ; - -/*------------------------------------------------------------------------------ - * Fetch next keystroke, reading from the file if required. - */ -static inline bool -uty_cli_get_keystroke(vty_io vio, keystroke stroke) -{ - if (keystroke_get(vio->key_stream, stroke)) - return 1 ; - - uty_read(vio, NULL) ; /* not stealing */ - - return keystroke_get(vio->key_stream, stroke) ; -} ; - -/*------------------------------------------------------------------------------ - * Process keystrokes until run out of input, or get something to cli_do. - * - * If required, draw the prompt and command line. - * - * Process keystrokes until run out of stuff to do, or have a "command line" - * that must now be executed. - * - * Processes the contents of the keystroke stream. If exhausts that, will set - * ready to read and return. (To give some "sharing".) - * - * Returns: cli_do_xxxx - * - * When returns the cl is '\0' terminated. - */ -static enum cli_do -uty_cli_process(vty_io vio, enum node_type node) -{ - struct keystroke stroke ; - uint8_t u ; - int auth ; - enum cli_do ret ; - - auth = (node == AUTH_NODE || node == AUTH_ENABLE_NODE) ; - - /* Now process as much as possible of what there is */ - ret = cli_do_nothing ; - while (1) - { - if (!vio->cli_drawn) - uty_cli_draw_this(vio, node) ; - - if (!uty_cli_get_keystroke(vio, &stroke)) - { - ret = (stroke.value == knull_eof) ? cli_do_eof : cli_do_nothing ; - break ; - } ; + /* Step past what have just consumed -- we have a hold_mark, so + * stuff is still in the fifo. + */ + vio_fifo_step(vf->ibuf, p - s) ; - if (stroke.flags != 0) + /* If not found '\n', then we have a line fragment that needs to be + * appended to any previous line fragments. + * + * Loops back to try to get some more form the fifo. + */ + if (q == NULL) { - /* TODO: deal with broken keystrokes */ + qs_append_str_n(vf->cl, s, p - s) ; continue ; } ; - switch (stroke.type) - { - /* Straightforward character -----------------------------------*/ - /* Note: only interested in 8-bit characters ! */ - case ks_char: - u = (uint8_t)stroke.value ; - - switch (stroke.value) - { - case CONTROL('A'): - uty_cli_bol (vio); - break; - - case CONTROL('B'): - uty_cli_backwards(vio, 1); - break; - - case CONTROL('C'): - ret = cli_do_ctrl_c ; /* Exit on ^C ..................*/ - break ; - - case CONTROL('D'): - if (vio->cl.len == 0) /* if at start of empty line */ - ret = cli_do_ctrl_d ; /* Exit on ^D ..................*/ - else - uty_cli_del_forwards(vio, 1); - break; - - case CONTROL('E'): - uty_cli_eol (vio); - break; - - case CONTROL('F'): - uty_cli_forwards(vio, 1); - break; - - case CONTROL('H'): - case 0x7f: - uty_cli_del_backwards(vio, 1); - break; - - case CONTROL('K'): - uty_cli_del_to_eol (vio); - break; - - case CONTROL('N'): - uty_cli_next_line (vio); - break; - - case CONTROL('P'): - uty_cli_previous_line (vio); - break; - - case CONTROL('T'): - uty_cli_transpose_chars (vio); - break; - - case CONTROL('U'): - uty_cli_clear_line(vio); - break; - - case CONTROL('W'): - uty_cli_del_word_backwards (vio); - break; - - case CONTROL('Z'): - ret = cli_do_ctrl_z ; /* Exit on ^Z ..................*/ - break; - - case '\n': - case '\r': - ret = cli_do_command ; /* Exit on CR or LF.............*/ - break ; - - case '\t': - if (auth) - break ; - else - uty_cli_complete_command (vio, node); - break; - - case '?': - if (auth) - uty_cli_insert (vio, (char*)&u, 1); - else - uty_cli_describe_command (vio, node); - break; - - default: - if ((stroke.value >= 0x20) && (stroke.value < 0x7F)) - uty_cli_insert (vio, (char*)&u, 1) ; - break; - } - break ; - - /* ESC X -------------------------------------------------------------*/ - case ks_esc: - switch (stroke.value) - { - case 'b': - uty_cli_word_backwards (vio); - break; - - case 'f': - uty_cli_word_forwards (vio); - break; - - case 'd': - uty_cli_del_word_forwards (vio); - break; - - case CONTROL('H'): - case 0x7f: - uty_cli_del_word_backwards (vio); - break; - - default: - break; - } ; - break ; - - /* ESC [ X -----------------------------------------------------------*/ - case ks_csi: - if (stroke.len != 0) - break ; /* only recognise 3 byte sequences */ - - switch (stroke.value) - { - case ('A'): - uty_cli_previous_line (vio); - break; - - case ('B'): - uty_cli_next_line (vio); - break; - - case ('C'): - uty_cli_forwards(vio, 1); - break; - - case ('D'): - uty_cli_backwards(vio, 1); - break; - default: - break ; - } ; - break ; - - /* Telnet Command ----------------------------------------------------*/ - case ks_iac: - uty_telnet_command(vio, &stroke, false) ; - break ; - - /* Unknown -----------------------------------------------------------*/ - default: - zabort("unknown keystroke type") ; - } ; - - /* After each keystroke..... */ - - if (ret != cli_do_nothing) + /* If we have nothing so far, set alias to point at what we have in + * the fifo. Otherwise, append to what we have. + * + * End up with: s = start of entire line, so far. + * p = end of entire line so far. + */ + len = q - s ; /* length to add */ + if (qs_len_nn(vf->cl) == 0) { - uty_cli_eol (vio) ; /* go to the end of the line */ - break ; /* stop processing */ - } ; - } ; - - /* Tidy up and return where got to. */ - - qs_term(&vio->cl) ; /* add '\0' */ - - return ret ; -} ; - -/*============================================================================== - * Command line operations - */ - -/*------------------------------------------------------------------------------ - * Insert 'n' characters at current position in the command line - * - * Returns number of characters inserted -- ie 'n' - */ -static int -uty_cli_insert (vty_io vio, const char* chars, int n) -{ - int after ; - - VTY_ASSERT_LOCKED() ; - - assert((vio->cl.cp <= vio->cl.len) && (n >= 0)) ; - - if (n <= 0) - return n ; /* avoid trouble */ - - after = qs_insert(vio->cl, chars, n) ; - - uty_cli_echo(vio, qs_cp_char(vio->cl), after) ; - - if ((after - n) != 0) - uty_cli_echo_n(vio, telnet_backspaces, after - n) ; - - vio->cl.cp += n ; - - return n ; -} ; - -/*------------------------------------------------------------------------------ - * Overstrike 'n' characters at current position in the command line - * - * Move current position forwards. - * - * Returns number of characters inserted -- ie 'n' - */ -static int -uty_cli_overwrite (vty_io vio, char* chars, int n) -{ - VTY_ASSERT_LOCKED() ; - - assert((vio->cl.cp <= vio->cl.len) && (n >= 0)) ; - - if (n > 0) - { - qs_replace(vio->cl, n, chars, n) ; - uty_cli_echo(vio, chars, n) ; - - vio->cl.cp += n ; - } ; - - return n ; -} - -/*------------------------------------------------------------------------------ - * Insert a word into vty interface with overwrite mode. - * - * NB: Assumes result will then be the end of the line. - * - * Returns number of characters inserted -- ie length of string - */ -static int -uty_cli_word_overwrite (vty_io vio, char *str) -{ - int n ; - VTY_ASSERT_LOCKED() ; - - n = uty_cli_overwrite(vio, str, strlen(str)) ; - - vio->cl.len = vio->cl.cp ; - - return n ; -} + qs_set_alias_n(vf->cl, s, len) ; + p = q ; + } + else + { + if (len != 0) + qs_append_str_n(vf->cl, s, len) ; -/*------------------------------------------------------------------------------ - * Forward 'n' characters -- stop at end of line. - * - * Returns number of characters actually moved - */ -static int -uty_cli_forwards(vty_io vio, int n) -{ - int have ; - VTY_ASSERT_LOCKED() ; + s = qs_char_nn(vf->cl) ; + p = s + qs_len_nn(vf->cl) ; - have = vio->cl.len - vio->cl.cp ; - if (have < n) - n = have ; + if ((len == 0) && (p > s) && (*(p-1) == ' ')) + { + /* Have an empty end of line section, and the last character + * of what we have so far is ' ', so need now to trim trailing + * spaces off the stored stuff. + */ + do --p ; while ((p > s) && (*(p-1) == ' ')) ; - assert(n >= 0) ; + qs_set_len_nn(vf->cl, p - s) ; + } ; + } ; - if (n > 0) - { - uty_cli_echo(vio, qs_cp_char(vio->cl), n) ; - vio->cl.cp += n ; - } ; + /* Now worry about we have a trailing '\'. */ - return n ; -} + if ((p == s) || (*(p-1) != '\\')) + break ; /* no \ => no continuation => success */ -/*------------------------------------------------------------------------------ - * Backwards 'n' characters -- stop at start of line. - * - * Returns number of characters actually moved - */ -static int -uty_cli_backwards(vty_io vio, int n) -{ - VTY_ASSERT_LOCKED() ; + /* Have a trailing '\'. + * + * If there are an odd number of '\', strip the last one and loop + * round to collect the continuation. + * + * If there are an even number of '\', then this is not a continuation. + * + * Note that this rule deals with the case of the continuation line + * being empty... e.g. ....\\\ n n -- where n is '\n' + */ + q = p ; + do --q ; while ((q > s) && (*(q-1) == '\\')) ; - if ((int)vio->cl.cp < n) - n = vio->cl.cp ; + if (((p - q) & 1) == 0) + break ; /* even => no continuation => success */ - assert(n >= 0) ; + qs_set_len_nn(vf->cl, p - s - 1) ; /* strip odd '\' */ - if (n > 0) - { - uty_cli_echo_n(vio, telnet_backspaces, n) ; - vio->cl.cp -= n ; + continue ; /* loop back to fetch more */ } ; - return n ; -} - -/*------------------------------------------------------------------------------ - * Delete 'n' characters -- forwards -- stop at end of line. - * - * Returns number of characters actually deleted. - */ -static int -uty_cli_del_forwards(vty_io vio, int n) -{ - int after ; - int have ; - - VTY_ASSERT_LOCKED() ; + /* Success have a line in hand */ - have = vio->cl.len - vio->cl.cp ; - if (have < n) - n = have ; /* cannot delete more than have */ + vf->line_complete = true ; + *line = vf->cl ; - assert(n >= 0) ; - - if (n <= 0) - return 0 ; - - after = qs_delete(vio->cl, n) ; - - if (after > 0) - uty_cli_echo(vio, qs_cp_char(vio->cl), after) ; - - uty_cli_echo_n(vio, telnet_spaces, n) ; - uty_cli_echo_n(vio, telnet_backspaces, after + n) ; - - return n ; -} - -/*------------------------------------------------------------------------------ - * Delete 'n' characters before the point. - * - * Returns number of characters actually deleted. - */ -static int -uty_cli_del_backwards(vty_io vio, int n) -{ - return uty_cli_del_forwards(vio, uty_cli_backwards(vio, n)) ; -} - -/*------------------------------------------------------------------------------ - * Move to the beginning of the line. - * - * Returns number of characters moved over. - */ -static int -uty_cli_bol (vty_io vio) -{ - return uty_cli_backwards(vio, vio->cl.cp) ; + return CMD_SUCCESS ; } ; /*------------------------------------------------------------------------------ - * Move to the end of the line. + * Command output push to a file or pipe. * - * Returns number of characters moved over + * Returns: CMD_SUCCESS -- done + * CMD_IO_ERROR -- ran into an I/O error */ -static int -uty_cli_eol (vty_io vio) +extern cmd_return_code_t +uty_file_out_push(vio_vf vf) { - return uty_cli_forwards(vio, vio->cl.len - vio->cl.cp) ; -} ; + assert(vf->vout_state == vf_open) ; -/*------------------------------------------------------------------------------ - * Forward word delta -- distance to start of next word. - * - * Return number of characters to step over to reach next word. - * - * Steps over non-space characters and then any spaces. - */ -static int -uty_cli_word_forwards_delta(vty_io vio) -{ - char* cp ; - char* tp ; - char* ep ; - - VTY_ASSERT_LOCKED() ; ; - - assert(vio->cl.cp <= vio->cl.len) ; - - cp = qs_cp_char(vio->cl) ; - ep = qs_ep_char(vio->cl) ; - - tp = cp ; - - while ((tp < ep) && (*tp != ' ')) - ++tp ; - - while ((tp < ep) && (*tp == ' ')) - ++tp ; - - return tp - cp ; -} ; - -/*------------------------------------------------------------------------------ - * Forward word -- move to start of next word. - * - * Moves past any non-spaces, then past any spaces. - */ -static int -uty_cli_word_forwards(vty_io vio) -{ - return uty_cli_forwards(vio, uty_cli_word_forwards_delta(vio)) ; -} ; - -/*------------------------------------------------------------------------------ - * Backward word delta -- distance to start of next word, back. - * - * Return number of characters to step over to reach next word. - * - * If "eat_spaces", starts by stepping over spaces. - * Steps back until next (backwards) character is space, or hits start of line. - */ -static int -uty_cli_word_backwards_delta(vty_io vio, int eat_spaces) -{ - char* cp ; - char* tp ; - char* sp ; - - VTY_ASSERT_LOCKED() ; ; - - assert(vio->cl.cp <= vio->cl.len) ; - - cp = qs_cp_char(vio->cl) ; - sp = qs_chars(vio->cl) ; - - tp = cp ; - - if (eat_spaces) - while ((tp > sp) && (*(tp - 1) == ' ')) - --tp ; - - while ((tp > sp) && (*(tp - 1) != ' ')) - --tp ; - - return cp - tp ; -} ; - -/*------------------------------------------------------------------------------ - * Backward word, but not trailing spaces. - * - * Move back until next (backwards) character is space or start of line. - * - * Returns number of characters stepped over. - */ -static int -uty_cli_word_backwards_pure (vty_io vio) -{ - return uty_cli_backwards(vio, uty_cli_word_backwards_delta(vio, 0)) ; -} ; - -/*------------------------------------------------------------------------------ - * Backward word -- move to start of previous word. - * - * Moves past any spaces, then move back until next (backwards) character is - * space or start of line. - * - * Returns number of characters stepped over. - */ -static int -uty_cli_word_backwards (vty_io vio) -{ - return uty_cli_backwards(vio, uty_cli_word_backwards_delta(vio, 1)) ; -} ; - -/*------------------------------------------------------------------------------ - * Delete to end of word -- forwards. - * - * Deletes any leading spaces, then deletes upto next space or end of line. - * - * Returns number of characters deleted. - */ -static int -uty_cli_del_word_forwards(vty_io vio) -{ - return uty_cli_del_forwards(vio, uty_cli_word_forwards_delta(vio)) ; -} - -/*------------------------------------------------------------------------------ - * Delete to start of word -- backwards. - * - * Deletes any trailing spaces, then deletes upto next space or start of line. - * - * Returns number of characters deleted. - */ -static int -uty_cli_del_word_backwards(vty_io vio) -{ - return uty_cli_del_backwards(vio, uty_cli_word_backwards_delta(vio, 1)) ; -} ; - -/*------------------------------------------------------------------------------ - * Kill rest of line from current point. - * - * Returns number of characters deleted. - */ -static int -uty_cli_del_to_eol (vty_io vio) -{ - return uty_cli_del_forwards(vio, vio->cl.len - vio->cl.cp) ; -} ; - -/*------------------------------------------------------------------------------ - * Kill line from the beginning. - * - * Returns number of characters deleted. - */ -static int -uty_cli_clear_line(vty_io vio) -{ - uty_cli_bol(vio) ; - return uty_cli_del_to_eol(vio) ; -} ; - -/*------------------------------------------------------------------------------ - * Transpose chars before or at the point. - * - * Return number of characters affected. - */ -static int -uty_cli_transpose_chars(vty_io vio) -{ - char chars[2] ; - char* cp ; - - VTY_ASSERT_LOCKED() ; - - /* Give up if < 2 characters or at start of line. */ - if ((vio->cl.len < 2) || (vio->cl.cp < 1)) - return 0 ; - - /* Move back to first of characters to exchange */ - if (vio->cl.cp == vio->cl.len) - uty_cli_backwards(vio, 2) ; + if (vio_fifo_write_nb(vf->obuf, vio_vfd_fd(vf->vfd), false) >= 0) + return CMD_SUCCESS ; else - uty_cli_backwards(vio, 1) ; - - /* Pick up in the new order */ - cp = qs_cp_char(vio->cl) ; - chars[1] = *cp++ ; - chars[0] = *cp ; - - /* And overwrite */ - return uty_cli_overwrite(vio, chars, 2) ; + return CMD_IO_ERROR ; } ; -/*============================================================================== - * Command line history handling - */ - /*------------------------------------------------------------------------------ - * Add given command line to the history buffer. - * - * This is inserting the vty->buf line into the history. + * Tidy up after input file has been closed */ extern void -uty_cli_hist_add (vty_io vio, const char* cmd_line) -{ - 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) ; - - /* find the previous command line in the history */ - prev_index = vio->hindex - 1 ; - if (prev_index < 0) - prev_index = VTY_MAXHIST - 1 ; - - prev_line = vector_get_item(vio->hist, prev_index) ; - - /* 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) ; - - /* Now replace the hindex entry */ - vector_set_item(vio->hist, vio->hindex, qs_copy(prev_line, 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; -} ; - -/*------------------------------------------------------------------------------ - * 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) +uty_file_read_close(vio_vf vf) { - int index ; - unsigned old_len ; - unsigned after ; - unsigned back ; - qstring hist ; - - VTY_ASSERT_LOCKED() ; - - assert((step == +1) || (step == -1)) ; - - index = vio->hp ; - - /* 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) - index = 0 ; - - hist = vector_get_item(vio->hist, index) ; - - /* 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 */ - uty_cli_bol(vio) ; - - /* Get previous line from history buffer and echo that */ - old_len = vio->cl.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 ; - - 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 (back > 0) - uty_cli_echo_n(vio, telnet_backspaces, back) ; - return ; } ; /*------------------------------------------------------------------------------ - * Use next history line, if any. - */ -static void -uty_cli_next_line(vty_io vio) -{ - uty_cli_history_use(vio, +1) ; -} - -/*------------------------------------------------------------------------------ - * Use previous history line, if any. - */ -static void -uty_cli_previous_line (vty_io vio) -{ - uty_cli_history_use(vio, -1) ; -} - -/*============================================================================== - * Command Completion and Command Description + * Flush output buffer and close. * - */ -static void uty_cli_describe_show(vty_io vio, vector describe) ; -static void uty_cli_describe_fold (vty_io vio, int cmd_width, - unsigned int desc_width, struct desc *desc) ; -static void uty_cli_describe_line(vty_io vio, int cmd_width, const char* cmd, - const char* str) ; - -static vector uty_cli_cmd_prepare(vty_io vio, int help) ; - -/*------------------------------------------------------------------------------ - * Command completion - */ -static void -uty_cli_complete_command (vty_io vio, enum node_type node) -{ - unsigned i ; - int ret ; - int len ; - int n ; - vector matched ; - vector vline ; - - VTY_ASSERT_LOCKED() ; - - /* Try and match the tokenised command line */ - vline = uty_cli_cmd_prepare(vio, 1) ; - matched = cmd_complete_command (vline, node, &ret); - cmd_free_strvec (vline); - - /* Show the result. */ - switch (ret) - { - case CMD_ERR_AMBIGUOUS: - uty_cli_out_newline(vio) ; /* clears cli_drawn */ - uty_cli_out_CMD_ERR_AMBIGUOUS(vio) ; - break ; - - case CMD_ERR_NO_MATCH: - uty_cli_out_newline(vio) ; /* clears cli_drawn */ - uty_cli_out_CMD_ERR_NO_MATCH(vio) ; - break ; - - case CMD_COMPLETE_FULL_MATCH: - uty_cli_eol (vio) ; - uty_cli_word_backwards_pure (vio); - uty_cli_word_overwrite (vio, vector_get_item(matched, 0)); - uty_cli_insert(vio, " ", 1); - break ; - - case CMD_COMPLETE_MATCH: - uty_cli_eol (vio) ; - uty_cli_word_backwards_pure (vio); - uty_cli_word_overwrite (vio, vector_get_item(matched, 0)); - break ; - - case CMD_COMPLETE_LIST_MATCH: - len = 6 ; - for (i = 0; i < vector_end(matched); i++) - { - 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, "%-*s ", len, (char*)vector_get_item(matched, i)); - } - uty_cli_out_newline(vio) ; - - break; - - case CMD_COMPLETE_ALREADY: - default: - break; - } ; - - cmd_free_strvec(matched); -} ; - -/*------------------------------------------------------------------------------ - * Command Description - */ -static void -uty_cli_describe_command (vty_io vio, enum node_type node) -{ - int ret; - vector vline; - vector describe; - - VTY_ASSERT_LOCKED() ; - - /* Try and match the tokenised command line */ - vline = uty_cli_cmd_prepare(vio, 1) ; - describe = cmd_describe_command (vline, node, &ret); - cmd_free_strvec (vline); - - uty_cli_out_newline(vio); /* clears cli_drawn */ - - /* Deal with result. */ - switch (ret) - { - case CMD_ERR_AMBIGUOUS: - uty_cli_out_CMD_ERR_AMBIGUOUS(vio) ; - break ; - - case CMD_ERR_NO_MATCH: - uty_cli_out_CMD_ERR_NO_MATCH(vio) ; - break ; - - default: - uty_cli_describe_show(vio, describe) ; - break ; - } ; - - if (describe != NULL) - vector_free (describe); -} - -/*------------------------------------------------------------------------------ - * Show the command description. - * - * Generates lines of the form: - * - * word description text - * - * Where the word field is adjusted to suit the longest word, and the - * description text is wrapped if required (if the width of the console is - * known) so that get: - * - * word description .................................. - * .............text - * - * If one of the options is '<cr>', that is always shown last. - */ -static void -uty_cli_describe_show(vty_io vio, vector describe) -{ - unsigned int i, cmd_width, desc_width; - struct desc *desc, *cr_item ; - - /* Get width of the longest "word" */ - cmd_width = 0; - for (i = 0; i < vector_active (describe); i++) - if ((desc = vector_slot (describe, i)) != NULL) - { - unsigned int len; - - if (desc->cmd[0] == '\0') - continue; - - len = strlen (desc->cmd); - if (desc->cmd[0] == '.') - len--; - - if (cmd_width < len) - cmd_width = len; - } - - /* Set width of description string. */ - desc_width = vio->width - (cmd_width + 6); - - /* Print out description. */ - cr_item = NULL ; /* put <cr> last if it appears */ - - for (i = 0; i < vector_active (describe); i++) - if ((desc = vector_slot (describe, i)) != NULL) - { - if (desc->cmd[0] == '\0') - continue; - - if (strcmp (desc->cmd, cr_string) == 0) - { - cr_item = desc; - continue; - } - - uty_cli_describe_fold (vio, cmd_width, desc_width, desc); - } - - if (cr_item != NULL) - uty_cli_describe_fold (vio, cmd_width, desc_width, cr_item); -} ; - -/*------------------------------------------------------------------------------ - * Show one word and the description, folding the description as required. - */ -static void -uty_cli_describe_fold (vty_io vio, int cmd_width, - unsigned int desc_width, struct desc *desc) -{ - char *buf; - const char *cmd, *p; - int pos; - - VTY_ASSERT_LOCKED() ; - - cmd = desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd; - p = desc->str ; - - /* If have a sensible description width */ - if (desc_width > 20) - { - buf = XCALLOC (MTYPE_TMP, strlen (desc->str) + 1); - - while (strlen (p) > desc_width) - { - /* move back to first space */ - for (pos = desc_width; pos > 0; pos--) - if (*(p + pos) == ' ') - break; - - /* if did not find a space, break at width */ - if (pos == 0) - pos = desc_width ; - - strncpy (buf, p, pos); - buf[pos] = '\0'; - uty_cli_describe_line(vio, cmd_width, cmd, buf) ; - - cmd = ""; /* for 2nd and subsequent lines */ - - p += pos ; /* step past what just wrote */ - while (*p == ' ') - ++p ; /* skip spaces */ - } ; - - XFREE (MTYPE_TMP, buf); - } ; - - uty_cli_describe_line(vio, cmd_width, cmd, p) ; -} ; - -/*------------------------------------------------------------------------------ - * Show one description line. - */ -static void -uty_cli_describe_line(vty_io vio, int cmd_width, const char* cmd, - const char* str) -{ - if (str != NULL) - uty_cli_out (vio, " %-*s %s", cmd_width, cmd, str) ; - else - uty_cli_out (vio, " %-s", cmd) ; - uty_cli_out_newline(vio) ; -} ; - -/*------------------------------------------------------------------------------ - * Prepare "vline" token array for command handler. - * - * For "help" (command completion/description), if the command line is empty, - * or ends in ' ', adds an empty token to the end of the token array. - */ -static vector -uty_cli_cmd_prepare(vty_io vio, int help) -{ - vector vline ; - - vline = cmd_make_strvec(qs_term(&vio->cl)) ; - - /* Note that if there is a vector of tokens, then there is at least one - * token, so can guarantee that vio->cl.len >= 1 ! - */ - if (help) - if ((vline == NULL) || isspace(*qs_chars_at(&vio->cl, vio->cl.len - 1))) - vline = cmd_add_to_strvec(vline, "") ; - - return vline ; -} ; - -/*============================================================================== - * VTY telnet stuff - */ - -#define TELNET_OPTION_DEBUG 0 /* 0 to turn off */ - -static const char* telnet_commands[256] = -{ - [tn_IAC ] = "IAC", - [tn_DONT ] = "DONT", - [tn_DO ] = "DO", - [tn_WONT ] = "WONT", - [tn_WILL ] = "WILL", - [tn_SB ] = "SB", - [tn_GA ] = "GA", - [tn_EL ] = "EL", - [tn_EC ] = "EC", - [tn_AYT ] = "AYT", - [tn_AO ] = "AO", - [tn_IP ] = "IP", - [tn_BREAK] = "BREAK", - [tn_DM ] = "DM", - [tn_NOP ] = "NOP", - [tn_SE ] = "SE", - [tn_EOR ] = "EOR", - [tn_ABORT] = "ABORT", - [tn_SUSP ] = "SUSP", - [tn_EOF ] = "EOF", -} ; - -static const char* telnet_options[256] = -{ - [to_BINARY] = "BINARY", /* 8-bit data path */ - [to_ECHO] = "ECHO", /* echo */ - [to_RCP] = "RCP", /* prepare to reconnect */ - [to_SGA] = "SGA", /* suppress go ahead */ - [to_NAMS] = "NAMS", /* approximate message size */ - [to_STATUS] = "STATUS", /* give status */ - [to_TM] = "TM", /* timing mark */ - [to_RCTE] = "RCTE", /* remote controlled tx and echo */ - [to_NAOL] = "NAOL", /* neg. about output line width */ - [to_NAOP] = "NAOP", /* neg. about output page size */ - [to_NAOCRD] = "NAOCRD", /* neg. about CR disposition */ - [to_NAOHTS] = "NAOHTS", /* neg. about horizontal tabstops */ - [to_NAOHTD] = "NAOHTD", /* neg. about horizontal tab disp. */ - [to_NAOFFD] = "NAOFFD", /* neg. about formfeed disposition */ - [to_NAOVTS] = "NAOVTS", /* neg. about vertical tab stops */ - [to_NAOVTD] = "NAOVTD", /* neg. about vertical tab disp. */ - [to_NAOLFD] = "NAOLFD", /* neg. about output LF disposition */ - [to_XASCII] = "XASCII", /* extended ascii character set */ - [to_LOGOUT] = "LOGOUT", /* force logout */ - [to_BM] = "BM", /* byte macro */ - [to_DET] = "DET", /* data entry terminal */ - [to_SUPDUP] = "SUPDUP", /* supdup protocol */ - [to_SUPDUPOUTPUT] = "SUPDUPOUTPUT",/* supdup output */ - [to_SNDLOC] = "SNDLOC", /* send location */ - [to_TTYPE] = "TTYPE", /* terminal type */ - [to_EOR] = "EOR", /* end or record */ - [to_TUID] = "TUID", /* TACACS user identification */ - [to_OUTMRK] = "OUTMRK", /* output marking */ - [to_TTYLOC] = "TTYLOC", /* terminal location number */ - [to_3270REGIME] = "3270REGIME", /* 3270 regime */ - [to_X3PAD] = "X3PAD", /* X.3 PAD */ - [to_NAWS] = "NAWS", /* window size */ - [to_TSPEED] = "TSPEED", /* terminal speed */ - [to_LFLOW] = "LFLOW", /* remote flow control */ - [to_LINEMODE] = "LINEMODE", /* Linemode option */ - [to_XDISPLOC] = "XDISPLOC", /* X Display Location */ - [to_OLD_ENVIRON] = "OLD_ENVIRON", /* Old - Environment variables */ - [to_AUTHENTICATION] = "AUTHENTICATION", /* Authenticate */ - [to_ENCRYPT] = "ENCRYPT", /* Encryption option */ - [to_NEW_ENVIRON] = "NEW_ENVIRON", /* New - Environment variables */ - [to_EXOPL] = "EXOPL", /* extended-options-list */ -} ; - -/*------------------------------------------------------------------------------ - * For debug. Put string or value as decimal. - */ -static void -uty_cli_out_dec(vty_io vio, const char* str, unsigned char u) -{ - if (str != NULL) - uty_cli_out(vio, "%s ", str) ; - else - uty_cli_out(vio, "%d ", (int)u) ; -} ; - -/*------------------------------------------------------------------------------ - * For debug. Put string or value as hex. - */ -static void -uty_cli_out_hex(vty_io vio, const char* str, unsigned char u) -{ - if (str != NULL) - uty_cli_out(vio, "%s ", str) ; - else - uty_cli_out(vio, "0x%02x ", (unsigned)u) ; -} ; - -/*------------------------------------------------------------------------------ - * Send telnet: "WILL TELOPT_ECHO" - */ -static void -uty_will_echo (vty_io vio) -{ - unsigned char cmd[] = { tn_IAC, tn_WILL, to_ECHO }; - VTY_ASSERT_LOCKED() ; - uty_cli_write (vio, (char*)cmd, (int)sizeof(cmd)); -} - -/*------------------------------------------------------------------------------ - * Send telnet: "suppress Go-Ahead" - */ -static void -uty_will_suppress_go_ahead (vty_io vio) -{ - unsigned char cmd[] = { tn_IAC, tn_WILL, to_SGA }; - VTY_ASSERT_LOCKED() ; - uty_cli_write (vio, (char*)cmd, (int)sizeof(cmd)); -} - -/*------------------------------------------------------------------------------ - * Send telnet: "don't use linemode" - */ -static void -uty_dont_linemode (vty_io vio) -{ - unsigned char cmd[] = { tn_IAC, tn_DONT, to_LINEMODE }; - VTY_ASSERT_LOCKED() ; - uty_cli_write (vio, (char*)cmd, (int)sizeof(cmd)); -} - -/*------------------------------------------------------------------------------ - * Send telnet: "Use window size" - */ -static void -uty_do_window_size (vty_io vio) -{ - unsigned char cmd[] = { tn_IAC, tn_DO, to_NAWS }; - VTY_ASSERT_LOCKED() ; - uty_cli_write (vio, (char*)cmd, (int)sizeof(cmd)); -} - -/*------------------------------------------------------------------------------ - * Send telnet: "don't use lflow" -- not currently used - */ -static void -uty_dont_lflow_ahead (vty_io vio) -{ - unsigned char cmd[] = { tn_IAC, tn_DONT, to_LFLOW }; - VTY_ASSERT_LOCKED() ; - uty_cli_write (vio, (char*)cmd, (int)sizeof(cmd)); -} - -/*------------------------------------------------------------------------------ - * 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. + * Returns: true <=> buffer (now) empty */ extern bool -uty_cli_iac_callback(keystroke_iac_callback_args) +uty_file_write_close(vio_vf vf, bool final) { - return uty_telnet_command((vty_io)context, stroke, true) ; + return vio_fifo_write_nb(vf->obuf, vio_vfd_fd(vf->vfd), true) == 0 ; } ; - -/*------------------------------------------------------------------------------ - * 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 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 ; - - uty_cli_out_hex(vio, telnet_commands[tn_IAC], tn_IAC) ; - - if (left-- > 0) - uty_cli_out_dec(vio, telnet_commands[*p], *p) ; - ++p ; - - if (left-- > 0) - uty_cli_out_dec(vio, telnet_options[*p], *p) ; - ++p ; - - if (left > 0) - { - while(left-- > 0) - uty_cli_out_hex(vio, NULL, *p++) ; - - if (stroke->flags & kf_truncated) - uty_cli_out(vio, "... ") ; - - if (!(stroke->flags & kf_broken)) - { - uty_cli_out_hex(vio, telnet_commands[tn_IAC], tn_IAC) ; - uty_cli_out_hex(vio, telnet_commands[tn_SE], tn_SE) ; - } - } ; - - 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 dealt_with ; /* go no further if broken */ - - p = stroke->buf ; - left = stroke->len ; - - passert(left >= 1) ; /* must be if not broken ! */ - passert(stroke->value == *p) ; /* or something is wrong */ - - ++p ; /* step past X of IAC X */ - --left ; - - /* Decode the one command that is interesting -- "NAWS" */ - switch (stroke->value) - { - case tn_SB: - passert(left > 0) ; /* or parser failed */ - - o = *p++ ; /* the option byte */ - --left ; - switch(o) - { - case to_NAWS: - if (left != 4) - { - uzlog(NULL, LOG_WARNING, - "RFC 1073 violation detected: telnet NAWS option " - "should send %d characters, but we received %d", - (3 + 4 + 2), (3 + left + 2)) ; - } - else - { - vio->width = *p++ << 8 ; - vio->width += *p++ ; - vio->height = *p++ << 8 ; - vio->height += *p ; - - if (TELNET_OPTION_DEBUG) - 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 ; - - default: /* no other IAC SB <option> */ - break ; - } ; - break ; - - default: /* no other IAC X */ - break ; - } ; - - return dealt_with ; -} ; - #endif |