diff options
Diffstat (limited to 'lib/vty_io.h')
-rw-r--r-- | lib/vty_io.h | 635 |
1 files changed, 447 insertions, 188 deletions
diff --git a/lib/vty_io.h b/lib/vty_io.h index 19689853..5f550837 100644 --- a/lib/vty_io.h +++ b/lib/vty_io.h @@ -25,286 +25,545 @@ #ifndef _ZEBRA_VTY_IO_H #define _ZEBRA_VTY_IO_H -#include <stdbool.h> -#include <errno.h> +//#include "zebra.h" +#include "misc.h" +//#include <errno.h> -#include "uty.h" -#include "vty.h" +#include "vty_local.h" +#include "command_local.h" +#include "vty_io_basic.h" #include "vio_fifo.h" -#include "vio_lines.h" -#include "keystroke.h" #include "thread.h" -#include "command.h" +#include "command_execute.h" #include "qstring.h" +#include "qfstring.h" +#include "list_util.h" /*============================================================================== - * Here are structures and other definitions which are shared by: + * Structures and other definitions for the top level VTY I/O. * - * vty.c -- the main vty handler - * vty_cli.c -- which handles the command line stuff - * vty_io.c -- .... - * - * The "struct vty" is used extensively across the Quagga daemons, where it - * has two functions relating to command handling as: - * - * 1) a "file handle" for output produced by commands - * - * 2) the holder of some context -- notably the current command "node" -- for - * command execution to use - * - * The bulk of "struct vty" is, therefore, private to vty.c and is factored - * out into the "struct vty_io". - * - * To reduce the size of vty.c, some groups of functions are separated into: - * - * vty_cli.c -- which looks after the keystroke by keystroke handling - * of the command line. + * There is one struct vty_io per VTY, which contains, inter alia, the vin + * and vout stacks. * + * The vin and vout stacks contain one or more struct vty_vf -- one per + * input and/or output associated with the VTY. */ +enum +{ + VTY_WATCH_DOG_INTERVAL = 5, /* interval between barks */ + + VTY_HALF_CLOSE_TIMEOUT = 120, /* timeout after half_close */ + + VTY_TIMEOUT_DEFAULT = 600, /* terminal timeout value */ +} ; + /*------------------------------------------------------------------------------ - * VTY sock structure - * - * Used for VTY_TERM and VTY_SHELL_SERV VTY types, which are attached to TCP - * and UNIX sockets, respectively. - * - * Also used for the associated listeners. + * VTY VIN and OUT types */ +enum vio_in_type /* Command input */ +{ + VIN_NONE = 0, /* not a valid input type */ -typedef int thread_action(struct thread *) ; + VIN_TERM, /* telnet terminal */ + VIN_VTYSH, /* vty_shell input */ -union sock_action -{ - qps_action* qnexus ; - thread_action* thread ; - void* anon ; + VIN_FILE, /* ordinary file input */ + VIN_PIPE, /* pipe (from child process) */ + + VIN_CONFIG, /* config file */ + + /* The VIN types >= VIN_SPECIALS do not have an associated fd. + * + * These can coexist with a VOUT which does have an associated fd. + */ + VIN_SPECIALS, /* all special from now on */ + + VIN_DEV_NULL = VIN_SPECIALS, + /* black hole input */ } ; +typedef enum vio_in_type vio_in_type_t ; -union timer_action +enum vio_out_type /* Command output */ { - qtimer_action* qnexus ; - thread_action* thread ; - void* anon ; + VOUT_NONE = 0, /* not a valid output type */ + + VOUT_TERM, /* a telnet terminal */ + VOUT_VTYSH, /* a vty_shell output pipe */ + + VOUT_FILE, /* ordinary file */ + VOUT_PIPE, /* pipe (to child process, and back again) */ + + VOUT_CONFIG, /* config file */ + + /* The VOUT types >= VOUT_SPECIALS do not have an associated fd. + * + * These can coexist with a VIN which does have an associated fd. + */ + VOUT_SPECIALS, /* all special from now on */ + + VOUT_DEV_NULL = VOUT_SPECIALS, + /* black hole output */ + + VOUT_SH_CMD, /* pipe for shell command (no actual output) */ + + VOUT_STDOUT, /* stdout */ + VOUT_STDERR, /* stderr */ +}; +typedef enum vio_out_type vio_out_type_t ; + +/*------------------------------------------------------------------------------ + * State of a vf -- has separate state for vin/vout. + */ +enum vf_state +{ + vf_closed = 0, /* the vf has not been opened, or has been + * completely closed -- there will be no vfd. */ + + vf_open, /* the vf has been opened, and any required vfd + * is open and I/O is possible. */ + + vf_end, /* for a vin: EOF has been reached or input has + * been terminated, or an error or timeout has + * been met. + * + * for a vout: output has been terminated, or an + * error or tineout has been met. + * + * The vfd may have been closed -- but in any + * case no further vfd I/O should be attempted. */ } ; +typedef enum vf_state vf_state_t ; -struct vio_sock_actions +/*------------------------------------------------------------------------------ + * vio_child structure. + * + * Lives on the vio_childer_list until collected or "curtains". + * + */ +typedef enum vio_child_await vio_child_await_t ; + +struct vio_vf ; /* Forward reference */ +typedef struct vio_vf* vio_vf ; + +typedef struct vio_child* vio_child ; + +struct vio_child { - union sock_action read ; - union sock_action write ; - union timer_action timer ; -}; + struct dl_list_pair(vio_child) list ; /* in the list of children */ + + vio_vf parent ; + + pid_t pid ; + bool collected ; /* waitpid() done */ + int report ; /* from waitpid() */ + + bool overdue ; /* patience exhausted */ + + bool awaited ; /* if child is awaited -- !vf->blocking */ + vio_timer timer ; /* limit the waiting time */ +} ; -typedef struct vio_sock* vio_sock ; -struct vio_sock +/*------------------------------------------------------------------------------ + * vty_vf -- "vty file" structure + * + * A vio_vf may be a read, write or read/write object. + * + * All I/O is via vio_vfd objects, except for VOUT_STDOUT and VOUT_STDERR. + * The vio_vfd layer hides the differences between the qpthreads an legacy + * thread environments. + * + * The VOUT_STDOUT and VOUT_STDERR are handled as direct output to the standard + * i/o file handles. In the case of a VTY_CONFIG_READ, the vin is VIN_CONFIG + * and the vout is VOUT_STDOUT, and these can share a single vty_vf. + * + * Also used for the associated listeners. + */ +struct vty_io ; /* Forward reference */ +typedef struct vty_io* vty_io ; + +struct vio_vf { - int fd ; + vty_io vio ; /* parent */ + + char* name ; /* MTYPE_VTY_NAME (if any) */ - void* info ; /* for action routines */ + /* Input side. */ - struct vio_sock_actions action ; + vio_in_type_t vin_type ; + vf_state_t vin_state ; + vio_vf vin_next ; /* list of inputs */ - bool read_open ; /* read returns 0 if not open */ - bool write_open ; /* write completes instantly if not open */ - int error_seen ; /* non-zero => failed */ + cmd_context context ; /* pushed exec->context. */ - qps_file qf ; /* when running qnexus */ + struct vty_cli* cli ; /* NULL if not a VTY_TERMINAL ! */ - struct thread *t_read; /* when running threads */ - struct thread *t_write; + vio_fifo ibuf ; /* input fifo (if required) */ - unsigned long v_timeout; /* time-out in seconds -- 0 => none */ - bool timer_running ; /* true when timer is running */ + qstring cl ; /* command line buffer */ + bool line_complete ; /* false => line in construction */ + uint line_number ; /* number of first line in cl */ + uint line_step ; /* number of real lines in cl */ - qtimer qtr; /* when running qnexus */ - struct thread *t_timer; /* when running threads */ + /* Output side. */ + vio_out_type_t vout_type ; + vf_state_t vout_state ; + vio_vf vout_next ; /* list of outputs */ + + vio_fifo obuf ; /* output fifo (if required) */ + + uint depth_mark ; /* depth of this vout */ + + /* General I/O */ + + bool blocking ; /* using blocking I/O (eg config read) */ + + vio_vfd vfd ; /* vty_io_basic "file descriptor" */ + + vty_timer_time read_timeout ; + vty_timer_time write_timeout ; + + /* Pipe extras -- child and pipe returns */ + + vio_child child ; /* state of child */ + + vf_state_t pr_state ; /* iff VOUT_PIPE/VOUT_SH_CMD */ + vio_vfd pr_vfd ; /* if pr_state != vf_closed */ + vty_timer_time pr_timeout ; /* set once closing pipe return */ + + vf_state_t ps_state ; /* stderr for all pipe types */ + vio_vfd ps_vfd ; /* if ps_state != vf_closed */ + vty_timer_time ps_timeout ; /* set once closing pipe return */ + + vio_fifo ps_buf ; /* to be moved to vio->ps_buf */ } ; -enum +enum vty_readiness /* bit significant */ { - on = true, - off = false + not_ready = 0, + read_ready = BIT(0), + write_ready = BIT(1), /* may take precedence */ +} ; +typedef enum vty_readiness vty_readiness_t ; + +/*------------------------------------------------------------------------------ + * State of a vty command loop. + */ +enum vc_state +{ + vc_stopped, /* the command loop has stopped, and will not run + * again. + * + * or, the command loop has never started. */ + + vc_waiting, /* the command loop is waiting for I/O. + * command queue command loop only */ + + vc_running, /* the command loop is running, and the vty is + * in its hands. */ } ; +typedef enum vc_state vc_state_t ; -enum vty_readiness /* bit significant */ +/*------------------------------------------------------------------------------ + * I/O and time-out error types + */ +enum vio_err_type { - not_ready = 0, - read_ready = 1, - write_ready = 2, /* takes precedence */ - now_ready = 4 + verr_none = 0, + + verr_vin = 1, + verr_vout = 2, + verr_pr = 3, /* pipe return (read) */ + verr_ps = 4, /* pipe stderr return (read) */ + + verr_mask = BIT(4) - 1, + + verr_io = BIT(4) * 0, + verr_to = BIT(4) * 1, + + verr_io_vin = verr_vin | verr_io, + verr_io_vout = verr_vout | verr_io, + verr_io_pr = verr_pr | verr_io, + verr_io_ps = verr_ps | verr_io, + + verr_to_vin = verr_vin | verr_to, + verr_to_vout = verr_vout | verr_to, + verr_to_pr = verr_pr | verr_to, + verr_to_ps = verr_ps | verr_to, } ; +typedef enum vio_err_type vio_err_type_t ; + +QFB_T(verr_mess, 200) ; /*------------------------------------------------------------------------------ * The vty_io structure + * + * The main elements of the vty_io (aka vio) object are the vin and vout stacks. + * + * The first entry in the vin/vout stacks is the "base" and is a bit special. + * This entry is at stack depth 1. Stack depth 0 is reserved for all closed, + * or about to be. + * + * The vin_depth counts the number of command inputs which have been opened + * and pushed on the stack. + * + * The vout_depth reflects the vin_depth at which the vout was opened, and + * will be: + * + * * vout_depth == vin_depth + 1 + * + * this is true after a command line such as: + * + * ...some command... > some_file + * + * When the command completes, and all output is pushed out, then the + * vout will be closed and the vout_depth reduced. + * + * * vout_depth == vin_depth + * + * this is true when a vty is set up (and the depths will be 1), and also + * if vin and a vout are opened together, as in: + * + * < some_file > some_other_file + * + * When the vin reaches eof (or fails) and is closed, then the vout_depth + * will be vin_depth + 1, which triggers the closing of the vout. + * + * * vout_depth < vin_depth + * + * This is true when one or vins have been opened and are stacked on top + * of each other. As the vins are closed, the vin_depth reduces until + * it hits the vout_depth, as above. + * + * When a vout is opened, the then current vout_depth is stored in the + * vf->depth_mark, and restored from there when the vout is closed. + * + * The vin_depth drives the closing of vouts. The vin_true_depth drives the + * closing of vins. */ +struct vty_cli ; /* forward reference -- vty_cli.h is + *not* included, because that refers + back to the vty_io ! */ -struct vty_io { - struct vty* vty ; /* the related vty */ - char *name ; /* for VTY_TERM is IP address) */ +struct vty_io /* typedef appears above */ +{ + vty vty ; /* the related vty */ /* List of all vty_io objects */ struct dl_list_pair(vty_io) vio_list ; - /* List of all vty_io that are in monitor state */ - struct dl_list_pair(vty_io) mon_list ; - - /* VTY type and sock stuff */ - enum vty_type type; - - struct vio_sock sock ; /* for VTY_TERM and VTY_SHELL_SERV */ + /* The vin/vout stacks */ - bool half_closed ; /* => on death watch list */ - bool closed ; /* => all I/O terminated - will also be half_closed */ + vio_vf vin ; /* vin stack */ + vio_vf vin_base ; + uint vin_depth ; + uint vin_true_depth ; /* less than vin_depth when closing */ - const char* close_reason ; /* message to be sent, once all other - output has completed, giving reason - for closing the VTY. */ + vio_vf vout ; /* vout stack */ + vio_vf vout_base ; + uint vout_depth ; - /* When writing configuration file */ - enum vty_type real_type ; + bool cancel ; - int file_fd ; - int file_error ; + /* Error handling */ - /*--------------------------------------------------------------------*/ - /* Command line and related state */ + vio_fifo ebuf ; /* buffer for error message */ - keystroke_stream key_stream ; + int err_depth ; /* on error, close stack to this depth */ - /* cli_drawn <=> the current prompt and user input occupy the current - * line on the screen. - * - * cli_dirty <=> the last command output did not end with a newline. - * - * If cli_drawn is true, the following are valid: + /* State * - * cli_prompt_len -- the length of the prompt part. - * (will be the "--more--" prompt in cli_more_wait) + * "blocking" is set for configuration reading VTY, so that everything is + * done with blocking I/O. * - * cli_extra_len -- the length of any ^X at the cursor position - * (for when blocked waiting for queued command) - * - * cli_echo_suppress -- the user part of the command line is suppressed - * - * NB: cli_echo_suppress is only used for password entry. + * "state" as described above. */ - bool cli_drawn ; - bool cli_dirty ; - - int cli_prompt_len ; - int cli_extra_len ; + bool blocking ; /* => all I/O is blocking. */ - bool cli_echo_suppress ; + vc_state_t state ; /* command loop state */ + cmd_return_code_t signal ; /* signal sent to command loop */ - /* "cache" for prompt -- when node or host name changes, prompt does */ - enum node_type cli_prompt_node ; - bool cli_prompt_set ; - qstring_t cli_prompt_for_node ; + char* close_reason ; /* MTYPE_TMP (if any) */ - /* State of the CLI - * - * cli_blocked -- blocked from processing keystrokes - * cmd_in_progress -- command dispatched (may be queued) - * cmd_out_enabled -- contents of the command FIFO may be written away - * cli_more_wait -- is in "--more--" wait state + /* Pipe stderr return buffer. */ - bool cli_blocked ; - bool cmd_in_progress ; - bool cmd_out_enabled ; - bool cli_more_wait ; + vio_fifo ps_buf ; - /* This is used to control command output, so that each write_ready event - * generates at most one tranche of output. + /* For ease of output, pointer to current vout->obuf + * + * Even when the vty is almost closed, there will remain a valid obuf, + * though anything sent to it under those conditions will be discarded. */ - bool cmd_out_done ; + vio_fifo obuf ; - /* This is set only if the "--more--" handling is enabled */ - bool cli_more_enabled ; - - /* Command Line(s) - * - * cli_do -- when current command being prepared is completed (by - * CR/LF or otherwise) this says what there now is to be done. - * - * cl -- current command line being prepared. - * - * clx -- current command line being executed. + /* The following is for "vty monitor". * - * NB: during command execution vty->buf is set to point at the '\0' - * terminated current command line being executed. + * With the exception of the "monitor" flag, need the LOG_MUTEX in order + * to change any of this. */ - enum cli_do cli_do ; - - qstring_t cl ; - qstring_t clx ; - - /* CLI output buffering */ - vio_fifo_t cli_obuf ; - - /* Command output buffering */ - vio_fifo_t cmd_obuf ; + struct dl_list_pair(vty_io) mon_list ; - vio_line_control cmd_lc ; + bool monitor ; /* is in monitor state */ - /* Failure count for login attempts */ - int fail; + bool mon_kick ; /* vty needs a kick */ + int maxlvl ; /* message level wish to see */ - /* History of commands */ - vector_t hist ; - int hp ; /* History lookup current point */ - int hindex; /* History insert end point */ + vio_fifo mbuf ; /* monitor output pending */ +} ; - /* Window width/height as reported by Telnet. 0 => unknown */ - int width; - int height; +/*============================================================================== + * Assertions for suitable state to close things ! + */ +Inline void +VTY_ASSERT_CAN_CLOSE(vty vty) +{ + if (vty_debug) + { + VTY_ASSERT_LOCKED() ; - /* Configure lines. */ - int lines; - bool lines_set ; /* true <=> explicitly set */ + if (!vty->vio->blocking && !vty_is_cli_thread()) + VTY_ASSERT_FAILED() ; + } ; +} ; - /* Terminal monitor. */ - bool monitor ; - bool monitor_busy ; +Inline void +VTY_ASSERT_CAN_CLOSE_VF(vio_vf vf) +{ + if (vty_debug) + { + VTY_ASSERT_LOCKED() ; - /* In configure mode. */ - bool config; + if (!vf->blocking && !vty_is_cli_thread()) + VTY_ASSERT_FAILED() ; + } ; } ; /*============================================================================== * Functions */ -extern struct vty* uty_new (enum vty_type type, int sock_fd) ; +extern int uty_out (vty_io vio, const char* format, ...) PRINTF_ATTRIBUTE(2, 3); +Inline int uty_vprintf(vty_io vio, const char *format, va_list args) ; + +Inline void uty_out_clear(vty_io vio) ; +Inline void uty_out_accept(vty_io vio) ; +Inline void uty_out_reject(vty_io vio) ; + +extern vty uty_new (vty_type_t type, node_type_t node) ; +extern void uty_close(vty_io vio) ; + +extern void uty_set_timeout(vty_io vio, vty_timer_time timeout) ; + +extern void uty_vin_new_context(vty_io vio, cmd_context context, + qpath file_here) ; +extern void uty_vin_push(vty_io vio, vio_vf vf, vio_in_type_t type, + vio_vfd_action* read_action, + vio_timer_action* read_timer_action, + usize ibuf_size) ; +extern void uty_vout_push(vty_io vio, vio_vf vf, vio_out_type_t type, + vio_vfd_action* write_action, + vio_timer_action* write_timer_action, + usize obuf_size, + bool after) ; +extern cmd_return_code_t uty_vin_pop(vty_io vio, cmd_context context, + bool final) ; +extern cmd_return_code_t uty_vout_pop(vty_io vio, bool final) ; + + +extern vio_vf uty_vf_new(vty_io vio, const char* name, int fd, vfd_type_t type, + vfd_io_type_t io_type) ; +extern void uty_vf_set_read(vio_vf vf, on_off_b on) ; +extern void uty_vf_set_read_timeout(vio_vf vf, vty_timer_time read_timeout) ; +extern void uty_vf_set_write(vio_vf vf, on_off_b on) ; +extern void uty_vf_set_write_timeout(vio_vf vf, vty_timer_time write_timeout) ; + +extern cmd_return_code_t uty_vf_error(vio_vf vf, vio_err_type_t err_type, + int err) ; +extern verr_mess_t uty_error_message(vio_vf vf, vio_err_type_t err_type, + int err, bool log) ; +extern vio_child uty_child_register(pid_t pid, vio_vf parent) ; +extern void vty_child_close_register(void) ; +extern void uty_child_awaited(vio_child child, vty_timer_time timeout) ; +extern bool uty_child_collect(vio_child child, vty_timer_time timeout, + bool final) ; +extern void uty_child_dismiss(vio_child child, bool final) ; +extern void uty_sigchld(void) ; + +extern void uty_child_signal_nexus_set(vty_io vio) ; +extern void vty_child_signal_nexus_signal(void) ; +extern void uty_child_signal_nexus_clear(vty_io vio) ; + extern void uty_open_listeners(const char *addr, unsigned short port, const char *path) ; +extern void uty_add_listener(int fd, vio_vfd_accept* accept) ; extern void uty_close_listeners(void) ; extern void uty_watch_dog_start(void) ; extern void uty_watch_dog_stop(void) ; -extern void uty_half_close (vty_io vio, const char* reason) ; -extern void uty_close (vty_io vio) ; +extern const char* uty_get_name(vty_io vio) ; -extern int uty_out (struct vty *vty, const char *format, ...) - PRINTF_ATTRIBUTE(2, 3) ; -extern int uty_vout(struct vty *vty, const char *format, va_list args) ; -extern void uty_out_clear(vty_io vio) ; -extern void uty_out_fflush(vty_io vio, FILE* file) ; +extern void uty_set_monitor(vty_io vio, bool on) ; -extern void uty_set_height(vty_io vio) ; -extern void uty_cmd_output_start(vty_io vio) ; +/*============================================================================== + * Inline Functions + */ -extern void uty_sock_set_readiness(vio_sock sock, enum vty_readiness ready) ; -extern void uty_sock_set_timer(vio_sock sock, unsigned long timeout) ; +Inline bool +uty_is_terminal(struct vty *vty) +{ + return vty->type == VTY_TERMINAL ; +} -extern int uty_read (vty_io vio, keystroke steal) ; -extern int utysh_read (vty_io vio, qstring cl, qstring buf) ; +Inline bool +uty_is_shell_server(struct vty *vty) +{ + return vty->type == VTY_SHELL_SERVER ; +} +Inline bool +uty_is_shell_client(struct vty *vty) +{ + return vty->type == VTY_SHELL_CLIENT ; +} -extern const char* uty_get_name(vty_io vio) ; +/*------------------------------------------------------------------------------ + * Command output -- append to output buffer. + */ +Inline int +uty_vprintf(vty_io vio, const char *format, va_list args) +{ + return vio_fifo_vprintf(vio->obuf, format, args) ; +} ; -extern void uty_set_monitor(vty_io vio, bool on) ; +/*------------------------------------------------------------------------------ + * Clear command output -- discard anything in the buffer, but keep markers. + */ +Inline void +uty_out_clear(vty_io vio) +{ + vio_fifo_clear(vio->obuf, false) ; +} ; + +/*------------------------------------------------------------------------------ + * Accept command output -- advance any end_mark to current put position. + */ +Inline void +uty_out_accept(vty_io vio) +{ + vio_fifo_step_end_mark(vio->obuf) ; +} ; + +/*------------------------------------------------------------------------------ + * Reject command output -- discard anything after the end_mark in the buffer, + * but keep markers. + */ +Inline void +uty_out_reject(vty_io vio) +{ + vio_fifo_back_to_end_mark(vio->obuf, true) ; +} ; #endif /* _ZEBRA_VTY_IO_H */ |