diff options
Diffstat (limited to 'lib/vty.c')
-rw-r--r-- | lib/vty.c | 3732 |
1 files changed, 1388 insertions, 2344 deletions
@@ -1,7 +1,8 @@ -/* - * Virtual terminal [aka TeletYpe] interface routine. +/* VTY top level * Copyright (C) 1997, 98 Kunihiro Ishiguro * + * Revisions: Copyright (C) 2010 Chris Hall (GMCH), Highwayman + * * This file is part of GNU Zebra. * * GNU Zebra is free software; you can redistribute it and/or modify it @@ -17,1083 +18,872 @@ * You should have received a copy of the GNU General Public License * along with GNU Zebra; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. + * 02111-1307, USA. */ -#include <zebra.h> +#include "zebra.h" +#include <stdbool.h> +#include "lib/version.h" -#include "linklist.h" -#include "thread.h" -#include "buffer.h" -#include <lib/version.h> -#include "command.h" -#include "sockunion.h" -#include "memory.h" -#include "str.h" -#include "log.h" -#include "prefix.h" -#include "filter.h" +#include "vty_io.h" #include "vty.h" -#include "privs.h" -#include "network.h" - -#include <arpa/telnet.h> - -/* Vty events */ -enum event -{ - VTY_SERV, - VTY_READ, - VTY_WRITE, - VTY_TIMEOUT_RESET, -#ifdef VTYSH - VTYSH_SERV, - VTYSH_READ, - VTYSH_WRITE -#endif /* VTYSH */ -}; +#include "uty.h" +#include "vty_cli.h" -static void vty_event (enum event, int, struct vty *); +#include "list_util.h" -/* Extern host structure from command.c */ -extern struct host host; - -/* Vector which store each vty structure. */ -static vector vtyvec; +#include "command.h" +#include "command_queue.h" +#include "command_execute.h" +#include "memory.h" +#include "log.h" +#include "mqueue.h" -/* Vty timeout value. */ -static unsigned long vty_timeout_val = VTY_TIMEOUT_DEFAULT; +/*============================================================================== + * Variables etc. (see uty.h) + */ -/* Vty access-class command */ -static char *vty_accesslist_name = NULL; +/* The mutex and related debug counters */ +qpt_mutex_t vty_mutex ; -/* Vty access-calss for IPv6. */ -static char *vty_ipv6_accesslist_name = NULL; +#if VTY_DEBUG -/* VTY server thread. */ -vector Vvty_serv_thread; +int vty_lock_count = 0 ; +int vty_assert_fail = 0 ; -/* Current directory. */ -char *vty_cwd = NULL; +#endif -/* Configure lock. */ -static int vty_config; +/* For thread handling -- initialised in vty_init */ +struct thread_master* vty_master = NULL ; -/* Login password check. */ -static int no_password_check = 0; +/* In the qpthreads world, have nexus for the CLI and one for the Routeing + * Engine. Some commands are processed directly in the CLI, most have to + * be sent to the Routeing Engine. + */ +qpn_nexus vty_cli_nexus = NULL ; +qpn_nexus vty_cmd_nexus = NULL ; -/* Restrict unauthenticated logins? */ -static const u_char restricted_mode_default = 0; -static u_char restricted_mode = 0; +/* List of all known vio */ +vty_io vio_list_base = NULL ; -/* Integrated configuration file path */ -char integrate_default[] = SYSCONFDIR INTEGRATE_DEFAULT_CONFIG; +/* List of all vty which are in monitor state. */ +vty_io vio_monitors_base = NULL ; - -/* VTY standard output function. */ -int -vty_out (struct vty *vty, const char *format, ...) -{ - va_list args; - int len = 0; - int size = 1024; - char buf[1024]; - char *p = NULL; +/* List of all vty which are on death watch */ +vty_io vio_death_watch = NULL ; - if (vty_shell (vty)) - { - va_start (args, format); - vprintf (format, args); - va_end (args); - } - else - { - /* Try to write to initial buffer. */ - va_start (args, format); - len = vsnprintf (buf, sizeof buf, format, args); - va_end (args); +/* Vty timeout value -- see "exec timeout" command */ +unsigned long vty_timeout_val = VTY_TIMEOUT_DEFAULT; - /* Initial buffer is not enough. */ - if (len < 0 || len >= size) - { - while (1) - { - if (len > -1) - size = len + 1; - else - size = size * 2; +/* Vty access-class command */ +char *vty_accesslist_name = NULL; - p = XREALLOC (MTYPE_VTY_OUT_BUF, p, size); - if (! p) - return -1; +/* Vty access-class for IPv6. */ +char *vty_ipv6_accesslist_name = NULL; - va_start (args, format); - len = vsnprintf (p, size, format, args); - va_end (args); +/* Current directory -- initialised in vty_init() */ +static char *vty_cwd = NULL; - if (len > -1 && len < size) - break; - } - } +/* Configure lock -- only one vty may be in CONFIG_NODE or above ! */ +bool vty_config = 0 ; - /* When initial buffer is enough to store all output. */ - if (! p) - p = buf; +/* Login password check override. */ +bool no_password_check = 0; - /* Pointer p must point out buffer. */ - buffer_put (vty->obuf, (u_char *) p, len); +/* Restrict unauthenticated logins? */ +const bool restricted_mode_default = 0 ; + bool restricted_mode = 0 ; - /* If p is not different with buf, it is allocated buffer. */ - if (p != buf) - XFREE (MTYPE_VTY_OUT_BUF, p); - } +/* Watch-dog timer. */ +union vty_watch_dog vty_watch_dog = { NULL } ; - return len; -} +/*------------------------------------------------------------------------------ + * VTYSH stuff + */ -static int -vty_log_out (struct vty *vty, const char *level, const char *proto_str, - const char *format, struct timestamp_control *ctl, va_list va) -{ - int ret; - int len; - char buf[1024]; +/* Integrated configuration file path -- for VTYSH */ +char integrate_default[] = SYSCONFDIR INTEGRATE_DEFAULT_CONFIG ; - if (!ctl->already_rendered) - { - ctl->len = quagga_timestamp(ctl->precision, ctl->buf, sizeof(ctl->buf)); - ctl->already_rendered = 1; - } - if (ctl->len+1 >= sizeof(buf)) - return -1; - memcpy(buf, ctl->buf, len = ctl->len); - buf[len++] = ' '; - buf[len] = '\0'; - - if (level) - ret = snprintf(buf+len, sizeof(buf)-len, "%s: %s: ", level, proto_str); - else - ret = snprintf(buf+len, sizeof(buf)-len, "%s: ", proto_str); - if ((ret < 0) || ((size_t)(len += ret) >= sizeof(buf))) - return -1; +/*------------------------------------------------------------------------------ + * Prototypes + */ +static void uty_reset (bool final, const char* why) ; +static void uty_init_commands (void) ; +static void vty_save_cwd (void) ; - if (((ret = vsnprintf(buf+len, sizeof(buf)-len, format, va)) < 0) || - ((size_t)((len += ret)+2) > sizeof(buf))) - return -1; +/*------------------------------------------------------------------------------ + * Tracking the initialisation state. + */ +enum vty_init_state +{ + vty_init_pending = 0, /* first and lowest numbered state */ + vty_init_1st_stage, + vty_init_2nd_stage, + vty_init_started, + vty_init_reset, + vty_init_terminated /* final and highest numbered state */ +}; - buf[len++] = '\r'; - buf[len++] = '\n'; +static enum vty_init_state vty_init_state ; - if (write(vty->fd, buf, len) < 0) - { - if (ERRNO_IO_RETRY(errno)) - /* Kernel buffer is full, probably too much debugging output, so just - drop the data and ignore. */ - return -1; - /* Fatal I/O error. */ - vty->monitor = 0; /* disable monitoring to avoid infinite recursion */ - zlog_warn("%s: write failed to vty client fd %d, closing: %s", - __func__, vty->fd, safe_strerror(errno)); - buffer_reset(vty->obuf); - /* cannot call vty_close, because a parent routine may still try - to access the vty struct */ - vty->status = VTY_CLOSE; - shutdown(vty->fd, SHUT_RDWR); - return -1; - } - return 0; -} +/*============================================================================== + * Public Interface + */ -/* Output current time to the vty. */ -void -vty_time_print (struct vty *vty, int cr) +/*------------------------------------------------------------------------------ + * Initialise vty handling (threads and pthreads) + * + * Install vty's own commands like `who' command. + * + * This runs before any pthreads or nexus stuff starts -- so is, implicitly, + * in the CLI thread. + * + * NB: may be called once and once only. + */ +extern void +vty_init (struct thread_master *master_thread) { - char buf [25]; - - if (quagga_timestamp(0, buf, sizeof(buf)) == 0) - { - zlog (NULL, LOG_INFO, "quagga_timestamp error"); - return; - } - if (cr) - vty_out (vty, "%s\n", buf); - else - vty_out (vty, "%s ", buf); + VTY_LOCK() ; /* Does nothing if !qpthreads_enabled */ + VTY_ASSERT_CLI_THREAD() ; /* True if !qpthreads_enabled */ - return; -} + assert(vty_init_state == vty_init_pending) ; -/* Say hello to vty interface. */ -void -vty_hello (struct vty *vty) -{ - if (host.motdfile) - { - FILE *f; - char buf[4096]; + vty_master = master_thread; /* Local pointer to the master thread */ - f = fopen (host.motdfile, "r"); - if (f) - { - while (fgets (buf, sizeof (buf), f)) - { - char *s; - /* work backwards to ignore trailling isspace() */ - for (s = buf + strlen (buf); (s > buf) && isspace ((int)*(s - 1)); - s--); - *s = '\0'; - vty_out (vty, "%s%s", buf, VTY_NEWLINE); - } - fclose (f); - } - else - vty_out (vty, "MOTD file not found%s", VTY_NEWLINE); - } - else if (host.motd) - vty_out (vty, "%s", host.motd); -} + vty_save_cwd (); /* need cwd for config reading */ -/* Put out prompt and wait input from user. */ -static void -vty_prompt (struct vty *vty) -{ - struct utsname names; - const char*hostname; + vio_list_base = NULL ; /* no VTYs yet */ + vio_monitors_base = NULL ; + vio_death_watch = NULL ; - if (vty->type == VTY_TERM) - { - hostname = host.name; - if (!hostname) - { - uname (&names); - hostname = names.nodename; - } - vty_out (vty, cmd_prompt (vty->node), hostname); - } -} + vty_cli_nexus = NULL ; /* not running qnexus-wise */ + vty_cmd_nexus = NULL ; -/* Send WILL TELOPT_ECHO to remote server. */ -static void -vty_will_echo (struct vty *vty) -{ - unsigned char cmd[] = { IAC, WILL, TELOPT_ECHO, '\0' }; - vty_out (vty, "%s", cmd); -} + vty_watch_dog.anon = NULL ; /* no watch dog */ -/* Make suppress Go-Ahead telnet option. */ -static void -vty_will_suppress_go_ahead (struct vty *vty) -{ - unsigned char cmd[] = { IAC, WILL, TELOPT_SGA, '\0' }; - vty_out (vty, "%s", cmd); -} + uty_init_commands() ; /* install nodes */ -/* Make don't use linemode over telnet. */ -static void -vty_dont_linemode (struct vty *vty) -{ - unsigned char cmd[] = { IAC, DONT, TELOPT_LINEMODE, '\0' }; - vty_out (vty, "%s", cmd); -} + vty_init_state = vty_init_1st_stage ; -/* Use window size. */ -static void -vty_do_window_size (struct vty *vty) -{ - unsigned char cmd[] = { IAC, DO, TELOPT_NAWS, '\0' }; - vty_out (vty, "%s", cmd); + VTY_UNLOCK() ; } -#if 0 /* Currently not used. */ -/* Make don't use lflow vty interface. */ -static void -vty_dont_lflow_ahead (struct vty *vty) +/*------------------------------------------------------------------------------ + * Further initialisation for qpthreads. + * + * This is done during "second stage" initialisation, when all nexuses have + * been set up and the qpthread_enabled state established. + * + * This is before any threads have been started, so is, implicitly, in the + * CLI thread. + * + * Need to know where the CLI nexus and the Routeing Engine nexus are. + * + * Initialise mutex. + * + * Cannot lock or assert in CLI thread while initialising those things ! + * + * NB: may be called once and once only. + */ +extern void +vty_init_r (qpn_nexus cli, qpn_nexus cmd) { - unsigned char cmd[] = { IAC, DONT, TELOPT_LFLOW, '\0' }; - vty_out (vty, "%s", cmd); -} -#endif /* 0 */ + assert(vty_init_state == vty_init_1st_stage) ; -/* Allocate new vty struct. */ -struct vty * -vty_new () -{ - struct vty *new = XCALLOC (MTYPE_VTY, sizeof (struct vty)); + vty_cli_nexus = cli ; + vty_cmd_nexus = cmd ; - new->obuf = buffer_new(0); /* Use default buffer size. */ - new->buf = XCALLOC (MTYPE_VTY, VTY_BUFSIZ); - new->max = VTY_BUFSIZ; + qpt_mutex_init(&vty_mutex, qpt_mutex_recursive); - return new; -} + vty_init_state = vty_init_2nd_stage ; +} ; -/* Authentication of vty */ -static void -vty_auth (struct vty *vty, char *buf) +/*------------------------------------------------------------------------------ + * Initialisation for vtysh application. + * + * TODO: work out what this needs to do ! (If anything.) + */ +extern void +vty_init_vtysh (void) { - char *passwd = NULL; - enum node_type next_node = 0; - int fail; - char *crypt (const char *, const char *); + VTY_LOCK() ; - switch (vty->node) - { - case AUTH_NODE: - if (host.encrypt) - passwd = host.password_encrypt; - else - passwd = host.password; - if (host.advanced) - next_node = host.enable ? VIEW_NODE : ENABLE_NODE; - else - next_node = VIEW_NODE; - break; - case AUTH_ENABLE_NODE: - if (host.encrypt) - passwd = host.enable_encrypt; - else - passwd = host.enable; - next_node = ENABLE_NODE; - break; - } + VTY_UNLOCK() ; +} ; - if (passwd) - { - if (host.encrypt) - fail = strcmp (crypt(buf, passwd), passwd); - else - fail = strcmp (buf, passwd); - } - else - fail = 1; - - if (! fail) - { - vty->fail = 0; - vty->node = next_node; /* Success ! */ - } - else - { - vty->fail++; - if (vty->fail >= 3) - { - if (vty->node == AUTH_NODE) - { - vty_out (vty, "%% Bad passwords, too many failures!%s", VTY_NEWLINE); - vty->status = VTY_CLOSE; - } - else - { - /* AUTH_ENABLE_NODE */ - vty->fail = 0; - vty_out (vty, "%% Bad enable passwords, too many failures!%s", VTY_NEWLINE); - vty->node = restricted_mode ? RESTRICTED_NODE : VIEW_NODE; - } - } - } -} - -/* Command execution over the vty interface. */ -static int -vty_command (struct vty *vty, char *buf) +/*------------------------------------------------------------------------------ + * Start the VTY going. + * + * This starts the listeners for VTY_TERM and VTY_SHELL_SERV. + * + * Also starts the watch dog. + * + * This is run during early morning start, after any daemonisation, but before + * any threads are started -- so is, implicitly, in the CLI thread. + * + * NB: may be called once and once only. + * + * NB: MUST be in the CLI thread (if any). + */ +extern void +vty_start(const char *addr, unsigned short port, const char *path) { - int ret; - vector vline; - const char *protocolname; - - /* Split readline string up into the vector */ - vline = cmd_make_strvec (buf); - - if (vline == NULL) - return CMD_SUCCESS; + VTY_LOCK() ; + VTY_ASSERT_CLI_THREAD() ; -#ifdef CONSUMED_TIME_CHECK - { - RUSAGE_T before; - RUSAGE_T after; - unsigned long realtime, cputime; + assert( (vty_init_state == vty_init_1st_stage) + || (vty_init_state == vty_init_2nd_stage) ) ; - GETRUSAGE(&before); -#endif /* CONSUMED_TIME_CHECK */ + uty_watch_dog_start() ; - ret = cmd_execute_command (vline, vty, NULL, 0); + uty_open_listeners(addr, port, path) ; - /* Get the name of the protocol if any */ - if (zlog_default) - protocolname = zlog_proto_names[zlog_default->protocol]; - else - protocolname = zlog_proto_names[ZLOG_NONE]; - -#ifdef CONSUMED_TIME_CHECK - GETRUSAGE(&after); - if ((realtime = thread_consumed_time(&after, &before, &cputime)) > - CONSUMED_TIME_CHECK) - /* Warn about CPU hog that must be fixed. */ - zlog_warn("SLOW COMMAND: command took %lums (cpu time %lums): %s", - realtime/1000, cputime/1000, buf); - } -#endif /* CONSUMED_TIME_CHECK */ - - if (ret != CMD_SUCCESS) - switch (ret) - { - case CMD_WARNING: - if (vty->type == VTY_FILE) - vty_out (vty, "Warning...%s", VTY_NEWLINE); - break; - case CMD_ERR_AMBIGUOUS: - vty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE); - break; - case CMD_ERR_NO_MATCH: - vty_out (vty, "%% [%s] Unknown command: %s%s", protocolname, buf, VTY_NEWLINE); - break; - case CMD_ERR_INCOMPLETE: - vty_out (vty, "%% Command incomplete.%s", VTY_NEWLINE); - break; - } - cmd_free_strvec (vline); - - return ret; -} - -static const char telnet_backward_char = 0x08; -static const char telnet_space_char = ' '; + vty_init_state = vty_init_started ; + VTY_UNLOCK() ; +} ; -/* Basic function to write buffer to vty. */ -static void -vty_write (struct vty *vty, const char *buf, size_t nbytes) +/*------------------------------------------------------------------------------ + * Reset all VTY status for reasons unknown -- probably SIGHUP + */ +extern void +vty_reset() { - if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE)) - return; - - /* Should we do buffering here ? And make vty_flush (vty) ? */ - buffer_put (vty->obuf, buf, nbytes); + vty_reset_because("Reset") ; } -/* Ensure length of input buffer. Is buffer is short, double it. */ -static void -vty_ensure (struct vty *vty, int length) -{ - if (vty->max <= length) - { - vty->max *= 2; - vty->buf = XREALLOC (MTYPE_VTY, vty->buf, vty->max); - } -} - -/* Basic function to insert character into vty. */ -static void -vty_self_insert (struct vty *vty, char c) +/*------------------------------------------------------------------------------ + * Reset all VTY status + * + * This is done in response to SIGHUP -- and runs in the CLI thread. + * + * Half closes all VTY, leaving the death watch to tidy up once all output + * and any command in progress have completed. + * + * Closes all listening sockets. + * + * TODO: revoke ? + * + * NB: old code discarded all output and hard closed all the VTY... + */ +extern void +vty_reset_because(const char* why) { - int i; - int length; + VTY_LOCK() ; + VTY_ASSERT_CLI_THREAD() ; - vty_ensure (vty, vty->length + 1); - length = vty->length - vty->cp; - memmove (&vty->buf[vty->cp + 1], &vty->buf[vty->cp], length); - vty->buf[vty->cp] = c; + assert(vty_init_state == vty_init_started) ; - vty_write (vty, &vty->buf[vty->cp], length + 1); - for (i = 0; i < length; i++) - vty_write (vty, &telnet_backward_char, 1); + uty_reset(0, why) ; /* not final ! */ - vty->cp++; - vty->length++; + vty_init_state = vty_init_reset ; + VTY_UNLOCK() ; } -/* Self insert character 'c' in overwrite mode. */ -static void -vty_self_insert_overwrite (struct vty *vty, char c) -{ - vty_ensure (vty, vty->length + 1); - vty->buf[vty->cp++] = c; +/*------------------------------------------------------------------------------ + * Restart the VTY, following a vty_reset(). + * + * This starts the listeners for VTY_TERM and VTY_SHELL_SERV, again. + * + * NB: may be called once, and once only, *after* a vty_reset(). + * + * NB: need not be in the CLI thread (if any). + */ +struct vty_restart_args +{ + char* addr ; + unsigned short port ; + char* path ; +} ; +MQB_ARGS_SIZE_OK(vty_restart_args) ; + +static void uty_restart_action(mqueue_block mqb, mqb_flag_t flag) ; +static void uty_restart(const char *addr, unsigned short port, + const char *path) ; +extern void +vty_restart(const char *addr, unsigned short port, const char *path) +{ + VTY_LOCK() ; + + /* If not running qnexus-wise, call uty_restart directly. + * + * Otherwise, construct and dispatch message to do a uty_restart. + */ + if (!vty_cli_nexus) + uty_restart(addr, port, path) ; + else + { + mqueue_block mqb ; + struct vty_restart_args* args ; - if (vty->cp > vty->length) - vty->length++; + mqb = mqb_init_new(NULL, uty_restart_action, vty_cli_nexus) ; + args = mqb_get_args(mqb) ; - if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE)) - return; + if (addr != NULL) + args->addr = XSTRDUP(MTYPE_TMP, addr) ; + else + args->addr = NULL ; - vty_write (vty, &c, 1); -} + args->port = port ; -/* Insert a word into vty interface with overwrite mode. */ -static void -vty_insert_word_overwrite (struct vty *vty, char *str) -{ - int len = strlen (str); - vty_write (vty, str, len); - strcpy (&vty->buf[vty->cp], str); - vty->cp += len; - vty->length = vty->cp; -} + if (path != NULL) + args->path = XSTRDUP(MTYPE_TMP, path) ; + else + args->path = NULL ; -/* Forward character. */ -static void -vty_forward_char (struct vty *vty) -{ - if (vty->cp < vty->length) - { - vty_write (vty, &vty->buf[vty->cp], 1); - vty->cp++; - } -} + mqueue_enqueue(vty_cli_nexus->queue, mqb, 0) ; + } ; + + VTY_UNLOCK() ; +} ; -/* Backward character. */ +/* Deal with the uty_restart message */ static void -vty_backward_char (struct vty *vty) +uty_restart_action(mqueue_block mqb, mqb_flag_t flag) { - if (vty->cp > 0) + struct vty_restart_args* args ; + args = mqb_get_args(mqb) ; + + if (flag == mqb_action) { - vty->cp--; - vty_write (vty, &telnet_backward_char, 1); - } -} + VTY_LOCK() ; -/* Move to the beginning of the line. */ -static void -vty_beginning_of_line (struct vty *vty) -{ - while (vty->cp) - vty_backward_char (vty); -} + uty_restart(args->addr, args->port, args->path) ; -/* Move to the end of the line. */ -static void -vty_end_of_line (struct vty *vty) -{ - while (vty->cp < vty->length) - vty_forward_char (vty); -} + VTY_UNLOCK() ; + } ; -static void vty_kill_line_from_beginning (struct vty *); -static void vty_redraw_line (struct vty *); + if (args->addr != NULL) + XFREE(MTYPE_TMP, args->addr) ; + if (args->path != NULL) + XFREE(MTYPE_TMP, args->path) ; +} ; -/* Print command line history. This function is called from - vty_next_line and vty_previous_line. */ +/* Do the actual restart */ static void -vty_history_print (struct vty *vty) +uty_restart(const char *addr, unsigned short port, const char *path) { - int length; + VTY_ASSERT_LOCKED() ; + assert(vty_init_state == vty_init_reset) ; - vty_kill_line_from_beginning (vty); + uty_open_listeners(addr, port, path) ; - /* Get previous line from history buffer */ - length = strlen (vty->hist[vty->hp]); - memcpy (vty->buf, vty->hist[vty->hp], length); - vty->cp = vty->length = length; - - /* Redraw current line */ - vty_redraw_line (vty); -} + vty_init_state = vty_init_started ; +} ; -/* Show next command line history. */ -static void -vty_next_line (struct vty *vty) +/*------------------------------------------------------------------------------ + * System shut-down + * + * In the pthreads world, all threads other than the main (CLI) thread have + * been joined -- so this is, implicitly, in the CLI thread. + * + * Close all known vty and release all memory -- discard all pending output. + * + * NB: this may be done in any initialisation state. + * + * Note that all the locking stuff does nothing if not qpthreads_enabled, so + * these may be done in any state of initialisation. (It is assumed that the + * switch into qpthreads_enabled is an atomic action... so all second stage + * initialisation completes together.) + */ +extern void +vty_terminate (void) { - int try_index; + if ( (vty_init_state == vty_init_pending) + || (vty_init_state == vty_init_terminated) ) + return ; /* nothing to do ! */ - if (vty->hp == vty->hindex) - return; + VTY_LOCK() ; + VTY_ASSERT_CLI_THREAD() ; - /* Try is there history exist or not. */ - try_index = vty->hp; - if (try_index == (VTY_MAXHIST - 1)) - try_index = 0; - else - try_index++; + assert( (vty_init_state > vty_init_pending) + && (vty_init_state < vty_init_terminated) ) ; - /* If there is not history return. */ - if (vty->hist[try_index] == NULL) - return; - else - vty->hp = try_index; - - vty_history_print (vty); -} - -/* Show previous command line history. */ -static void -vty_previous_line (struct vty *vty) -{ - int try_index; + uty_reset(1, "Shut down") ; /* final reset */ - try_index = vty->hp; - if (try_index == 0) - try_index = VTY_MAXHIST - 1; - else - try_index--; + VTY_UNLOCK() ; - if (vty->hist[try_index] == NULL) - return; - else - vty->hp = try_index; + qpt_mutex_destroy(&vty_mutex, 0); - vty_history_print (vty); + vty_init_state = vty_init_terminated ; } -/* This function redraw all of the command line character. */ +/*------------------------------------------------------------------------------ + * Reset -- final or for SIGHUP + * + * Closes listeners. + * + * Closes (final) or half closes (SIGHUP) all VTY, and revokes any outstanding + * commands. + * + * Resets the vty timeout and access lists. + * + * When reach final reset it should not be possible for there to be any + * commands still in progress. If there are, they are simply left on the + * death-watch list... there is no pressing need to do anything more radical, + * and the presence of anything on the death watch is grounds for some debug + * activity ! + */ static void -vty_redraw_line (struct vty *vty) +uty_reset (bool curtains, const char* why) { - vty_write (vty, vty->buf, vty->length); - vty->cp = vty->length; -} + vty_io vio ; + vty_io next ; -/* Forward word. */ -static void -vty_forward_word (struct vty *vty) -{ - while (vty->cp != vty->length && vty->buf[vty->cp] != ' ') - vty_forward_char (vty); - - while (vty->cp != vty->length && vty->buf[vty->cp] == ' ') - vty_forward_char (vty); -} + VTY_ASSERT_LOCKED() ; + VTY_ASSERT_CLI_THREAD() ; -/* Backward word without skipping training space. */ -static void -vty_backward_pure_word (struct vty *vty) -{ - while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ') - vty_backward_char (vty); -} + uty_close_listeners() ; -/* Backward word. */ -static void -vty_backward_word (struct vty *vty) -{ - while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ') - vty_backward_char (vty); + next = sdl_head(vio_list_base) ; + while (next != NULL) + { + vio = next ; + next = sdl_next(vio, vio_list) ; - while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ') - vty_backward_char (vty); -} + if (vio->type == VTY_TERM) + cq_revoke(vio->vty) ; -/* When '^D' is typed at the beginning of the line we move to the down - level. */ -static void -vty_down_level (struct vty *vty) -{ - vty_out (vty, "%s", VTY_NEWLINE); - (*config_exit_cmd.func)(NULL, vty, 0, NULL); - vty_prompt (vty); - vty->cp = 0; -} + if (why != NULL) + vio->close_reason = why ; -/* When '^Z' is received from vty, move down to the enable mode. */ -static void -vty_end_config (struct vty *vty) -{ - vty_out (vty, "%s", VTY_NEWLINE); + if (curtains) + uty_close(vio) ; + else + uty_half_close(vio, why) ; + } ; - switch (vty->node) + vty_timeout_val = VTY_TIMEOUT_DEFAULT; + + if (vty_accesslist_name) { - case VIEW_NODE: - case ENABLE_NODE: - case RESTRICTED_NODE: - /* Nothing to do. */ - break; - case CONFIG_NODE: - case INTERFACE_NODE: - case ZEBRA_NODE: - case RIP_NODE: - case RIPNG_NODE: - case BGP_NODE: - case BGP_VPNV4_NODE: - case BGP_IPV4_NODE: - case BGP_IPV4M_NODE: - case BGP_IPV6_NODE: - case BGP_IPV6M_NODE: - case RMAP_NODE: - case OSPF_NODE: - case OSPF6_NODE: - case ISIS_NODE: - case KEYCHAIN_NODE: - case KEYCHAIN_KEY_NODE: - case MASC_NODE: - case VTY_NODE: - vty_config_unlock (vty); - vty->node = ENABLE_NODE; - break; - default: - /* Unknown node, we have to ignore it. */ - break; + XFREE(MTYPE_VTY, vty_accesslist_name); + vty_accesslist_name = NULL; } - vty_prompt (vty); - vty->cp = 0; -} - -/* Delete a charcter at the current point. */ -static void -vty_delete_char (struct vty *vty) -{ - int i; - int size; - - if (vty->length == 0) + if (vty_ipv6_accesslist_name) { - vty_down_level (vty); - return; + XFREE(MTYPE_VTY, vty_ipv6_accesslist_name); + vty_ipv6_accesslist_name = NULL; } - if (vty->cp == vty->length) - return; /* completion need here? */ + if (curtains && vty_cwd) + XFREE (MTYPE_TMP, vty_cwd); + + if (curtains) + uty_watch_dog_stop() ; /* and final death watch run */ +} ; - size = vty->length - vty->cp; +/*============================================================================== + * Opening and closing VTY. + * + * VTY without a socket may be opened and closed at will. + * + * TODO: sort out the relationship between the non-socket VTY and vty_reset() + */ - vty->length--; - memmove (&vty->buf[vty->cp], &vty->buf[vty->cp + 1], size - 1); - vty->buf[vty->length] = '\0'; - - if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE) - return; +/*------------------------------------------------------------------------------ + * Create a new VTY of the given type + * + * The type may NOT be: VTY_TERM or VTY_SHELL_SERV + */ +extern struct vty * +vty_open(enum vty_type type) +{ + struct vty* vty ; - vty_write (vty, &vty->buf[vty->cp], size - 1); - vty_write (vty, &telnet_space_char, 1); + VTY_LOCK() ; + vty = uty_new(type, -1) ; /* fails for VTY_TERM or VTY_SHELL_SERV */ + VTY_UNLOCK() ; - for (i = 0; i < size; i++) - vty_write (vty, &telnet_backward_char, 1); -} + return vty ; +} ; -/* Delete a character before the point. */ -static void -vty_delete_backward_char (struct vty *vty) +/*------------------------------------------------------------------------------ + * Close the given VTY + */ +extern void +vty_close (struct vty *vty) { - if (vty->cp == 0) - return; - - vty_backward_char (vty); - vty_delete_char (vty); + VTY_LOCK() ; + uty_close(vty->vio) ; + VTY_UNLOCK() ; } -/* Kill rest of line from current point. */ -static void -vty_kill_line (struct vty *vty) +/*============================================================================== + * General VTY output. + * + * This is mostly used during command execution, to output the results of the + * command. + * + * All these end up in uty_vout -- see vty_io. + */ + +/*------------------------------------------------------------------------------ + * VTY output -- cf fprintf ! + */ +extern int +vty_out (struct vty *vty, const char *format, ...) { - int i; - int size; + int result; - size = vty->length - vty->cp; - - if (size == 0) - return; + VTY_LOCK() ; + va_list args; + va_start (args, format); + result = uty_vout(vty, format, args); + va_end (args); + VTY_UNLOCK() ; + return result; +} - for (i = 0; i < size; i++) - vty_write (vty, &telnet_space_char, 1); - for (i = 0; i < size; i++) - vty_write (vty, &telnet_backward_char, 1); +/*------------------------------------------------------------------------------ + * VTY output -- output a given numnber of spaces + */ - memset (&vty->buf[vty->cp], 0, size); - vty->length = vty->cp; -} +/* 1 2 3 4 */ +/* 1234567890123456789012345678901234567890 */ +const char vty_spaces_string[] = " " ; +CONFIRM(VTY_MAX_SPACES == (sizeof(vty_spaces_string) - 1)) ; -/* Kill line from the beginning. */ -static void -vty_kill_line_from_beginning (struct vty *vty) +extern void +vty_out_indent(struct vty *vty, int indent) { - vty_beginning_of_line (vty); - vty_kill_line (vty); -} + while (indent > 0) + { + vty_out(vty, VTY_SPACES(indent)) ; + indent -= VTY_MAX_SPACES ; + } +} ; -/* Delete a word before the point. */ -static void -vty_forward_kill_word (struct vty *vty) +/*------------------------------------------------------------------------------ + * VTY output -- output the current time in standard form, to the second. + */ +extern void +vty_time_print (struct vty *vty, int cr) { - while (vty->cp != vty->length && vty->buf[vty->cp] == ' ') - vty_delete_char (vty); - while (vty->cp != vty->length && vty->buf[vty->cp] != ' ') - vty_delete_char (vty); -} + char buf [timestamp_buffer_len]; -/* Delete a word before the point. */ -static void -vty_backward_kill_word (struct vty *vty) -{ - while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ') - vty_delete_backward_char (vty); - while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ') - vty_delete_backward_char (vty); + quagga_timestamp(0, buf, sizeof(buf)) ; + + if (cr) + vty_out (vty, "%s%s", buf, VTY_NEWLINE); + else + vty_out (vty, "%s ", buf); + + return; } -/* Transpose chars before or at the point. */ -static void -vty_transpose_chars (struct vty *vty) +/*------------------------------------------------------------------------------ + * Say hello to vty interface. + */ +void +vty_hello (struct vty *vty) { - char c1, c2; - - /* If length is short or point is near by the beginning of line then - return. */ - if (vty->length < 2 || vty->cp < 1) - return; + VTY_LOCK() ; - /* In case of point is located at the end of the line. */ - if (vty->cp == vty->length) +#ifdef QDEBUG + uty_out (vty, "%s%s", debug_banner, VTY_NEWLINE); +#endif + if (host.motdfile) { - c1 = vty->buf[vty->cp - 1]; - c2 = vty->buf[vty->cp - 2]; + FILE *f; + char buf[4096]; - vty_backward_char (vty); - vty_backward_char (vty); - vty_self_insert_overwrite (vty, c1); - vty_self_insert_overwrite (vty, c2); + f = fopen (host.motdfile, "r"); + if (f) + { + while (fgets (buf, sizeof (buf), f)) + { + char *s; + /* work backwards to ignore trailing isspace() */ + for (s = buf + strlen (buf); (s > buf) && isspace ((int)*(s - 1)); + s--); + *s = '\0'; + uty_out (vty, "%s%s", buf, VTY_NEWLINE); + } + fclose (f); + } + else + uty_out (vty, "MOTD file %s not found%s", host.motdfile, VTY_NEWLINE); } - else - { - c1 = vty->buf[vty->cp]; - c2 = vty->buf[vty->cp - 1]; + else if (host.motd) + uty_out (vty, "%s", host.motd); - vty_backward_char (vty); - vty_self_insert_overwrite (vty, c1); - vty_self_insert_overwrite (vty, c2); - } + VTY_UNLOCK() ; } -/* Do completion at vty interface. */ -static void -vty_complete_command (struct vty *vty) +/*------------------------------------------------------------------------------ + * Clear the contents of the command output FIFO etc. + */ +extern void +vty_out_clear(struct vty* vty) { - int i; - int ret; - char **matched = NULL; - vector vline; + VTY_LOCK() ; + uty_out_clear(vty->vio) ; + VTY_UNLOCK() ; +} ; - if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE) - return; +/*============================================================================== + * Command Execution + */ + +/*------------------------------------------------------------------------------ + * Execute command -- adding to history is not empty or just comment + * + * This is for VTY_TERM type VTY. + * + * Outputs diagnostics if fails to parse. + * + * Returns: command return code + */ +extern enum cmd_return_code +uty_command(struct vty *vty) +{ + enum cmd_return_code ret; - vline = cmd_make_strvec (vty->buf); - if (vline == NULL) - return; + VTY_ASSERT_LOCKED() ; + VTY_ASSERT_CLI_THREAD() ; - /* In case of 'help \t'. */ - if (isspace ((int) vty->buf[vty->length - 1])) - vector_set (vline, '\0'); + assert(vty->vio->type == VTY_TERM) ; - matched = cmd_complete_command (vline, vty, &ret); - - cmd_free_strvec (vline); + /* Parse the command and add to history (if not empty) */ + ret = cmd_parse_command(vty, + cmd_parse_completion + cmd_parse_do + cmd_parse_tree) ; + if (ret != CMD_EMPTY) + uty_cli_hist_add (vty->vio, vty->buf) ; - vty_out (vty, "%s", VTY_NEWLINE); - switch (ret) + /* If parsed and not empty, dispatch */ + if (ret == CMD_SUCCESS) { +#ifdef CONSUMED_TIME_CHECK + RUSAGE_T before; + RUSAGE_T after; + unsigned long realtime, cputime; + + GETRUSAGE(&before); +#endif /* CONSUMED_TIME_CHECK */ + + ret = cmd_dispatch(vty, cmd_may_queue) ; + +#ifdef CONSUMED_TIME_CHECK + GETRUSAGE(&after); + if ((realtime = thread_consumed_time(&after, &before, &cputime)) > + CONSUMED_TIME_CHECK) + /* Warn about CPU hog that must be fixed. */ + uzlog(NULL, LOG_WARNING, + "SLOW COMMAND: command took %lums (cpu time %lums): %s", + realtime/1000, cputime/1000, vty->buf) ; +#endif /* CONSUMED_TIME_CHECK */ + } ; + + /* Deal with the return code */ + switch (ret) + { case CMD_ERR_AMBIGUOUS: - vty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE); - vty_prompt (vty); - vty_redraw_line (vty); + uty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE); break; + case CMD_ERR_NO_MATCH: - /* vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE); */ - vty_prompt (vty); - vty_redraw_line (vty); + uty_out (vty, "%% Unknown command.%s", VTY_NEWLINE) ; break; - case CMD_COMPLETE_FULL_MATCH: - vty_prompt (vty); - vty_redraw_line (vty); - vty_backward_pure_word (vty); - vty_insert_word_overwrite (vty, matched[0]); - vty_self_insert (vty, ' '); - XFREE (MTYPE_TMP, matched[0]); - break; - case CMD_COMPLETE_MATCH: - vty_prompt (vty); - vty_redraw_line (vty); - vty_backward_pure_word (vty); - vty_insert_word_overwrite (vty, matched[0]); - XFREE (MTYPE_TMP, matched[0]); - vector_only_index_free (matched); - return; - break; - case CMD_COMPLETE_LIST_MATCH: - for (i = 0; matched[i] != NULL; i++) - { - if (i != 0 && ((i % 6) == 0)) - vty_out (vty, "%s", VTY_NEWLINE); - vty_out (vty, "%-10s ", matched[i]); - XFREE (MTYPE_TMP, matched[i]); - } - vty_out (vty, "%s", VTY_NEWLINE); - vty_prompt (vty); - vty_redraw_line (vty); + case CMD_ERR_INCOMPLETE: + uty_out (vty, "%% Command incomplete.%s", VTY_NEWLINE); break; - case CMD_ERR_NOTHING_TODO: - vty_prompt (vty); - vty_redraw_line (vty); - break; - default: - break; - } - if (matched) - vector_only_index_free (matched); -} -static void -vty_describe_fold (struct vty *vty, int cmd_width, - unsigned int desc_width, struct desc *desc) -{ - char *buf; - const char *cmd, *p; - int pos; + default: + break ; + } ; - cmd = desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd; + return ret ; +} ; - if (desc_width <= 0) - { - vty_out (vty, " %-*s %s%s", cmd_width, cmd, desc->str, VTY_NEWLINE); - return; - } +/*------------------------------------------------------------------------------ + * Authentication of vty + * + * During AUTH_NODE and AUTH_ENABLE_NODE, when a command line is dispatched by + * any means this function is called. + * + * Note that if the AUTH_NODE password fails too many times, the terminal is + * closed. + * + * Returns: command return code + */ +extern enum cmd_return_code +uty_auth (struct vty *vty, const char *buf, enum cli_do cli_do) +{ + char *passwd = NULL; + enum node_type next_node = 0; + int fail; + char *crypt (const char *, const char *); + enum cmd_return_code ret ; - buf = XCALLOC (MTYPE_TMP, strlen (desc->str) + 1); + vty_io vio = vty->vio ; - for (p = desc->str; strlen (p) > desc_width; p += pos + 1) - { - for (pos = desc_width; pos > 0; pos--) - if (*(p + pos) == ' ') - break; + VTY_ASSERT_LOCKED() ; + VTY_ASSERT_CLI_THREAD() ; - if (pos == 0) - break; + /* What to do ? + * + * In fact, all the exotic command terminators simply discard any input + * and return. + */ + switch (cli_do) + { + case cli_do_nothing: + case cli_do_ctrl_c: + case cli_do_ctrl_z: + return CMD_SUCCESS ; - strncpy (buf, p, pos); - buf[pos] = '\0'; - vty_out (vty, " %-*s %s%s", cmd_width, cmd, buf, VTY_NEWLINE); + case cli_do_command: + break ; - cmd = ""; - } + case cli_do_ctrl_d: + case cli_do_eof: + return uty_cmd_close(vty, "End") ; - vty_out (vty, " %-*s %s%s", cmd_width, cmd, p, VTY_NEWLINE); + default: + zabort("unknown or invalid cli_do") ; + } ; - XFREE (MTYPE_TMP, buf); -} + /* Ordinary command dispatch -- see if password is OK. */ + switch (vty->node) + { + case AUTH_NODE: + if (host.encrypt) + passwd = host.password_encrypt; + else + passwd = host.password; + if (host.advanced) + next_node = host.enable ? VIEW_NODE : ENABLE_NODE; + else + next_node = VIEW_NODE; + break; -/* Describe matched command function. */ -static void -vty_describe_command (struct vty *vty) -{ - int ret; - vector vline; - vector describe; - unsigned int i, width, desc_width; - struct desc *desc, *desc_cr = NULL; + case AUTH_ENABLE_NODE: + if (host.encrypt) + passwd = host.enable_encrypt; + else + passwd = host.enable; + next_node = ENABLE_NODE; + break; - vline = cmd_make_strvec (vty->buf); + default: + zabort("unknown node type") ; + } - /* In case of '> ?'. */ - if (vline == NULL) + if (passwd) { - vline = vector_init (1); - vector_set (vline, '\0'); + if (host.encrypt) + fail = strcmp (crypt(buf, passwd), passwd); + else + fail = strcmp (buf, passwd); } - else - if (isspace ((int) vty->buf[vty->length - 1])) - vector_set (vline, '\0'); - - describe = cmd_describe_command (vline, vty, &ret); + else + fail = 1; - vty_out (vty, "%s", VTY_NEWLINE); + ret = CMD_SUCCESS ; - /* Ambiguous error. */ - switch (ret) + if (! fail) { - case CMD_ERR_AMBIGUOUS: - vty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE); - goto out; - break; - case CMD_ERR_NO_MATCH: - vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE); - goto out; - break; - } - - /* Get width of command string. */ - width = 0; - for (i = 0; i < vector_active (describe); i++) - if ((desc = vector_slot (describe, i)) != NULL) - { - unsigned int len; + vio->fail = 0; + vty->node = next_node; /* Success ! */ + } + else + { + vio->fail++; + if (vio->fail >= 3) + { + if (vty->node == AUTH_NODE) + { + ret = uty_cmd_close(vty, "Bad passwords, too many failures!%s") ; + } + else + { + /* AUTH_ENABLE_NODE */ + vio->fail = 0; + uty_out (vty, "%% Bad enable passwords, too many failures!%s", + VTY_NEWLINE); + vty->node = restricted_mode ? RESTRICTED_NODE : VIEW_NODE; - if (desc->cmd[0] == '\0') - continue; + ret = CMD_WARNING ; + } + } + } - len = strlen (desc->cmd); - if (desc->cmd[0] == '.') - len--; + return ret ; +} ; - if (width < len) - width = len; - } +/*------------------------------------------------------------------------------ + * Command line "exit" command -- aka "quit" + * + * Falls back one NODE level. + * + * Returns: command return code + */ +extern enum cmd_return_code +vty_cmd_exit(struct vty* vty) +{ + enum cmd_return_code ret ; - /* Get width of description string. */ - desc_width = vty->width - (width + 6); + VTY_LOCK() ; - /* Print out description. */ - 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, command_cr) == 0) - { - desc_cr = desc; - continue; - } - - if (!desc->str) - vty_out (vty, " %-s%s", - desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, - VTY_NEWLINE); - else if (desc_width >= strlen (desc->str)) - vty_out (vty, " %-*s %s%s", width, - desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, - desc->str, VTY_NEWLINE); - else - vty_describe_fold (vty, width, desc_width, desc); - -#if 0 - vty_out (vty, " %-*s %s%s", width - desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, - desc->str ? desc->str : "", VTY_NEWLINE); -#endif /* 0 */ - } - - if ((desc = desc_cr)) + ret = CMD_SUCCESS ; + switch (vty->node) { - if (!desc->str) - vty_out (vty, " %-s%s", - desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, - VTY_NEWLINE); - else if (desc_width >= strlen (desc->str)) - vty_out (vty, " %-*s %s%s", width, - desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, - desc->str, VTY_NEWLINE); + case VIEW_NODE: + case ENABLE_NODE: + case RESTRICTED_NODE: + if (vty_shell (vty)) + exit (0); else - vty_describe_fold (vty, width, desc_width, desc); + ret = uty_cmd_close(vty, "Exit") ; + break; + case CONFIG_NODE: + uty_config_unlock (vty, ENABLE_NODE); + break; + case INTERFACE_NODE: + case ZEBRA_NODE: + case BGP_NODE: + case RIP_NODE: + case RIPNG_NODE: + case OSPF_NODE: + case OSPF6_NODE: + case ISIS_NODE: + case KEYCHAIN_NODE: + case MASC_NODE: + case RMAP_NODE: + case VTY_NODE: + vty->node = CONFIG_NODE ; + break; + case BGP_VPNV4_NODE: + case BGP_IPV4_NODE: + case BGP_IPV4M_NODE: + case BGP_IPV6_NODE: + case BGP_IPV6M_NODE: + vty->node = BGP_NODE ; + break; + case KEYCHAIN_KEY_NODE: + vty->node = KEYCHAIN_NODE ; + break; + default: + break; } -out: - cmd_free_strvec (vline); - if (describe) - vector_free (describe); - - vty_prompt (vty); - vty_redraw_line (vty); + VTY_UNLOCK() ; + return ret ; } -static void -vty_clear_buf (struct vty *vty) -{ - memset (vty->buf, 0, vty->max); -} - -/* ^C stop current input and do not add command line to the history. */ -static void -vty_stop_input (struct vty *vty) +/*------------------------------------------------------------------------------ + * Command line "end" command + * + * Falls back to ENABLE_NODE. + * + * Returns: command return code + */ +extern enum cmd_return_code +vty_cmd_end(struct vty* vty) { - vty->cp = vty->length = 0; - vty_clear_buf (vty); - vty_out (vty, "%s", VTY_NEWLINE); + VTY_LOCK() ; switch (vty->node) { @@ -1108,6 +898,11 @@ vty_stop_input (struct vty *vty) case RIP_NODE: case RIPNG_NODE: case BGP_NODE: + case BGP_VPNV4_NODE: + case BGP_IPV4_NODE: + case BGP_IPV4M_NODE: + case BGP_IPV6_NODE: + case BGP_IPV6M_NODE: case RMAP_NODE: case OSPF_NODE: case OSPF6_NODE: @@ -1116,1465 +911,557 @@ vty_stop_input (struct vty *vty) case KEYCHAIN_KEY_NODE: case MASC_NODE: case VTY_NODE: - vty_config_unlock (vty); - vty->node = ENABLE_NODE; + uty_config_unlock (vty, ENABLE_NODE); break; default: - /* Unknown node, we have to ignore it. */ break; } - vty_prompt (vty); - - /* Set history pointer to the latest one. */ - vty->hp = vty->hindex; -} - -/* Add current command line to the history buffer. */ -static void -vty_hist_add (struct vty *vty) -{ - int index; - - if (vty->length == 0) - return; - - index = vty->hindex ? vty->hindex - 1 : VTY_MAXHIST - 1; - - /* Ignore the same string as previous one. */ - if (vty->hist[index]) - if (strcmp (vty->buf, vty->hist[index]) == 0) - { - vty->hp = vty->hindex; - return; - } - /* Insert history entry. */ - if (vty->hist[vty->hindex]) - XFREE (MTYPE_VTY_HIST, vty->hist[vty->hindex]); - vty->hist[vty->hindex] = XSTRDUP (MTYPE_VTY_HIST, vty->buf); + VTY_UNLOCK() ; + return CMD_SUCCESS ; +} ; - /* History index rotation. */ - vty->hindex++; - if (vty->hindex == VTY_MAXHIST) - vty->hindex = 0; - - vty->hp = vty->hindex; -} - -/* #define TELNET_OPTION_DEBUG */ - -/* Get telnet window size. */ -static int -vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes) +/*------------------------------------------------------------------------------ + * Result of command is to close the input. + * + * Posts the reason for the close. + * + * Returns: CMD_CLOSE + */ +extern enum cmd_return_code +uty_cmd_close(struct vty *vty, const char* reason) { -#ifdef TELNET_OPTION_DEBUG - int i; + vty->vio->close_reason = reason ; + return CMD_CLOSE ; +} ; - for (i = 0; i < nbytes; i++) - { - switch (buf[i]) - { - case IAC: - vty_out (vty, "IAC "); - break; - case WILL: - vty_out (vty, "WILL "); - break; - case WONT: - vty_out (vty, "WONT "); - break; - case DO: - vty_out (vty, "DO "); - break; - case DONT: - vty_out (vty, "DONT "); - break; - case SB: - vty_out (vty, "SB "); - break; - case SE: - vty_out (vty, "SE "); - break; - case TELOPT_ECHO: - vty_out (vty, "TELOPT_ECHO %s", VTY_NEWLINE); - break; - case TELOPT_SGA: - vty_out (vty, "TELOPT_SGA %s", VTY_NEWLINE); - break; - case TELOPT_NAWS: - vty_out (vty, "TELOPT_NAWS %s", VTY_NEWLINE); - break; - default: - vty_out (vty, "%x ", buf[i]); - break; - } - } - vty_out (vty, "%s", VTY_NEWLINE); - -#endif /* TELNET_OPTION_DEBUG */ - - switch (buf[0]) - { - case SB: - vty->sb_len = 0; - vty->iac_sb_in_progress = 1; - return 0; - break; - case SE: - { - if (!vty->iac_sb_in_progress) - return 0; - - if ((vty->sb_len == 0) || (vty->sb_buf[0] == '\0')) - { - vty->iac_sb_in_progress = 0; - return 0; - } - switch (vty->sb_buf[0]) - { - case TELOPT_NAWS: - if (vty->sb_len != TELNET_NAWS_SB_LEN) - zlog_warn("RFC 1073 violation detected: telnet NAWS option " - "should send %d characters, but we received %lu", - TELNET_NAWS_SB_LEN, (u_long)vty->sb_len); - else if (sizeof(vty->sb_buf) < TELNET_NAWS_SB_LEN) - zlog_err("Bug detected: sizeof(vty->sb_buf) %lu < %d, " - "too small to handle the telnet NAWS option", - (u_long)sizeof(vty->sb_buf), TELNET_NAWS_SB_LEN); - else - { - vty->width = ((vty->sb_buf[1] << 8)|vty->sb_buf[2]); - vty->height = ((vty->sb_buf[3] << 8)|vty->sb_buf[4]); -#ifdef TELNET_OPTION_DEBUG - vty_out(vty, "TELNET NAWS window size negotiation completed: " - "width %d, height %d%s", - vty->width, vty->height, VTY_NEWLINE); -#endif - } - break; - } - vty->iac_sb_in_progress = 0; - return 0; - break; - } - default: - break; - } - return 1; -} - -/* Execute current command line. */ -static int -vty_execute (struct vty *vty) +/*------------------------------------------------------------------------------ + * Command line ^C action. + * + * Ignores contents of command line (including not adding to history). + * + * Fall back to ENABLE_NODE if in any one of a number of nodes. + * + * Resets the history pointer. + * + * Returns: command return code + */ +extern enum cmd_return_code +uty_stop_input(struct vty *vty) { - int ret; + vty_io vio = vty->vio ; - ret = CMD_SUCCESS; + VTY_ASSERT_LOCKED() ; switch (vty->node) { - case AUTH_NODE: - case AUTH_ENABLE_NODE: - vty_auth (vty, vty->buf); + case CONFIG_NODE: + case INTERFACE_NODE: + case ZEBRA_NODE: + case RIP_NODE: + case RIPNG_NODE: + case BGP_NODE: + case RMAP_NODE: + case OSPF_NODE: + case OSPF6_NODE: + case ISIS_NODE: + case KEYCHAIN_NODE: + case KEYCHAIN_KEY_NODE: + case MASC_NODE: + case VTY_NODE: + uty_config_unlock (vty, ENABLE_NODE) ; break; default: - ret = vty_command (vty, vty->buf); - if (vty->type == VTY_TERM) - vty_hist_add (vty); + /* Unknown node, we have to ignore it. */ break; } - /* Clear command line buffer. */ - vty->cp = vty->length = 0; - vty_clear_buf (vty); - - if (vty->status != VTY_CLOSE ) - vty_prompt (vty); - - return ret; -} + /* Set history pointer to the latest one. */ + vio->hp = vio->hindex; -#define CONTROL(X) ((X) - '@') -#define VTY_NORMAL 0 -#define VTY_PRE_ESCAPE 1 -#define VTY_ESCAPE 2 + return CMD_SUCCESS ; +} ; -/* Escape character command map. */ -static void -vty_escape_map (unsigned char c, struct vty *vty) +/*------------------------------------------------------------------------------ + * Command ^Z action. + * + * Ignores contents of command line (including not adding to history). + * + * Fall back to ENABLE_NODE if in any one of a number of nodes. + * + * Returns: command return code + */ +extern enum cmd_return_code +uty_end_config (struct vty *vty) { - switch (c) + VTY_ASSERT_LOCKED() ; + + switch (vty->node) { - case ('A'): - vty_previous_line (vty); - break; - case ('B'): - vty_next_line (vty); - break; - case ('C'): - vty_forward_char (vty); + case VIEW_NODE: + case ENABLE_NODE: + case RESTRICTED_NODE: + /* Nothing to do. */ break; - case ('D'): - vty_backward_char (vty); + case CONFIG_NODE: + case INTERFACE_NODE: + case ZEBRA_NODE: + case RIP_NODE: + case RIPNG_NODE: + case BGP_NODE: + case BGP_VPNV4_NODE: + case BGP_IPV4_NODE: + case BGP_IPV4M_NODE: + case BGP_IPV6_NODE: + case BGP_IPV6M_NODE: + case RMAP_NODE: + case OSPF_NODE: + case OSPF6_NODE: + case ISIS_NODE: + case KEYCHAIN_NODE: + case KEYCHAIN_KEY_NODE: + case MASC_NODE: + case VTY_NODE: + uty_config_unlock (vty, ENABLE_NODE) ; break; default: + /* Unknown node, we have to ignore it. */ break; } - /* Go back to normal mode. */ - vty->escape = VTY_NORMAL; + return CMD_SUCCESS ; } -/* Quit print out to the buffer. */ -static void -vty_buffer_reset (struct vty *vty) -{ - buffer_reset (vty->obuf); - vty_prompt (vty); - vty_redraw_line (vty); -} - -/* Read data via vty socket. */ -static int -vty_read (struct thread *thread) +/*------------------------------------------------------------------------------ + * Command ^D action -- when nothing else on command line. + * + * Same as "exit" command. + * + * Returns: command return code + */ +extern enum cmd_return_code +uty_down_level (struct vty *vty) { - int i; - int nbytes; - unsigned char buf[VTY_READ_BUFSIZ]; - - int vty_sock = THREAD_FD (thread); - struct vty *vty = THREAD_ARG (thread); - vty->t_read = NULL; - - /* Read raw data from socket */ - if ((nbytes = read (vty->fd, buf, VTY_READ_BUFSIZ)) <= 0) - { - if (nbytes < 0) - { - if (ERRNO_IO_RETRY(errno)) - { - vty_event (VTY_READ, vty_sock, vty); - return 0; - } - vty->monitor = 0; /* disable monitoring to avoid infinite recursion */ - zlog_warn("%s: read error on vty client fd %d, closing: %s", - __func__, vty->fd, safe_strerror(errno)); - } - buffer_reset(vty->obuf); - vty->status = VTY_CLOSE; - } - - for (i = 0; i < nbytes; i++) - { - if (buf[i] == IAC) - { - if (!vty->iac) - { - vty->iac = 1; - continue; - } - else - { - vty->iac = 0; - } - } - - if (vty->iac_sb_in_progress && !vty->iac) - { - if (vty->sb_len < sizeof(vty->sb_buf)) - vty->sb_buf[vty->sb_len] = buf[i]; - vty->sb_len++; - continue; - } + return vty_cmd_exit(vty) ; +} ; - if (vty->iac) - { - /* In case of telnet command */ - int ret = 0; - ret = vty_telnet_option (vty, buf + i, nbytes - i); - vty->iac = 0; - i += ret; - continue; - } - - - if (vty->status == VTY_MORE) - { - switch (buf[i]) - { - case CONTROL('C'): - case 'q': - case 'Q': - vty_buffer_reset (vty); - break; -#if 0 /* More line does not work for "show ip bgp". */ - case '\n': - case '\r': - vty->status = VTY_MORELINE; - break; -#endif - default: - break; - } - continue; - } - - /* Escape character. */ - if (vty->escape == VTY_ESCAPE) - { - vty_escape_map (buf[i], vty); - continue; - } - - /* Pre-escape status. */ - if (vty->escape == VTY_PRE_ESCAPE) - { - switch (buf[i]) - { - case '[': - vty->escape = VTY_ESCAPE; - break; - case 'b': - vty_backward_word (vty); - vty->escape = VTY_NORMAL; - break; - case 'f': - vty_forward_word (vty); - vty->escape = VTY_NORMAL; - break; - case 'd': - vty_forward_kill_word (vty); - vty->escape = VTY_NORMAL; - break; - case CONTROL('H'): - case 0x7f: - vty_backward_kill_word (vty); - vty->escape = VTY_NORMAL; - break; - default: - vty->escape = VTY_NORMAL; - break; - } - continue; - } +/*============================================================================== + * Reading of configuration file + * + * The reading of the configuration file occurs at two times: + * + * 1. early in the morning, before daemonisation, and before any threads + * or nexuses have been set up. + * + * In the qpthreads world, this means that it is running in the main (CLI) + * and only thread and nexus. + * + * 2. at SIGHUP time. + * + * In the qpthreads world, this is running in whatever thread is executing + * commands. + * + * Sets up a VTY_CONFIG_READ in which to execute commands. This has no CLI + * and no socket. All output is buffered in the cmd_obuf. All commands are + * run directly in the thread -- no commands are queued. + */ - switch (buf[i]) - { - case CONTROL('A'): - vty_beginning_of_line (vty); - break; - case CONTROL('B'): - vty_backward_char (vty); - break; - case CONTROL('C'): - vty_stop_input (vty); - break; - case CONTROL('D'): - vty_delete_char (vty); - break; - case CONTROL('E'): - vty_end_of_line (vty); - break; - case CONTROL('F'): - vty_forward_char (vty); - break; - case CONTROL('H'): - case 0x7f: - vty_delete_backward_char (vty); - break; - case CONTROL('K'): - vty_kill_line (vty); - break; - case CONTROL('N'): - vty_next_line (vty); - break; - case CONTROL('P'): - vty_previous_line (vty); - break; - case CONTROL('T'): - vty_transpose_chars (vty); - break; - case CONTROL('U'): - vty_kill_line_from_beginning (vty); - break; - case CONTROL('W'): - vty_backward_kill_word (vty); - break; - case CONTROL('Z'): - vty_end_config (vty); - break; - case '\n': - case '\r': - vty_out (vty, "%s", VTY_NEWLINE); - vty_execute (vty); - break; - case '\t': - vty_complete_command (vty); - break; - case '?': - if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE) - vty_self_insert (vty, buf[i]); - else - vty_describe_command (vty); - break; - case '\033': - if (i + 1 < nbytes && buf[i + 1] == '[') - { - vty->escape = VTY_ESCAPE; - i++; - } - else - vty->escape = VTY_PRE_ESCAPE; - break; - default: - if (buf[i] > 31 && buf[i] < 127) - vty_self_insert (vty, buf[i]); - break; - } - } +static FILE * vty_use_backup_config (char *fullpath) ; +static void vty_read_file (FILE *confp, struct cmd_element* first_cmd, + bool ignore_warnings) ; - /* Check status. */ - if (vty->status == VTY_CLOSE) - vty_close (vty); - else - { - vty_event (VTY_WRITE, vty_sock, vty); - vty_event (VTY_READ, vty_sock, vty); - } - return 0; +/*------------------------------------------------------------------------------ + * Read the given configuration file. + */ +extern void +vty_read_config (char *config_file, + char *config_default) +{ + vty_read_config_first_cmd_special(config_file, config_default, NULL, 1); } -/* Flush buffer to the vty. */ -static int -vty_flush (struct thread *thread) +/*------------------------------------------------------------------------------ + * Read the given configuration file. + * + * The config_file (-f argument) is used if specified. + * + * If config_file is NULL, use the config_default. + * + * If using the config_default, if VTYSH_ENABLED, look for "vtysh" in the name. + * If find "vtysh" and find the "integrate_default" file, then do nothing + * now -- expect vtysh to connect in due course and provide the configuration. + * + * The config_file or config_default may be relative file names. + * + * May have a function to call after the first actual command is processed. + * This mechanism supports the setting of qpthreads-ness by configuration file + * command. + */ +extern void +vty_read_config_first_cmd_special(char *config_file, + char *config_default, + struct cmd_element* first_cmd, + bool ignore_warnings) { - int erase; - buffer_status_t flushrc; - int vty_sock = THREAD_FD (thread); - struct vty *vty = THREAD_ARG (thread); - - vty->t_write = NULL; + char cwd[MAXPATHLEN]; + FILE *confp = NULL; + char *fullpath; + char *tmp = NULL; - /* Tempolary disable read thread. */ - if ((vty->lines == 0) && vty->t_read) + /* Deal with VTYSH_ENABLED magic */ + if (VTYSH_ENABLED && (config_file == NULL)) { - thread_cancel (vty->t_read); - vty->t_read = NULL; - } + int ret; + struct stat conf_stat; - /* Function execution continue. */ - erase = ((vty->status == VTY_MORE || vty->status == VTY_MORELINE)); + /* !!!!PLEASE LEAVE!!!! + * This is NEEDED for use with vtysh -b, or else you can get + * a real configuration food fight with a lot garbage in the + * merged configuration file it creates coming from the per + * daemon configuration files. This also allows the daemons + * to start if there default configuration file is not + * present or ignore them, as needed when using vtysh -b to + * configure the daemons at boot - MAG + */ - /* N.B. if width is 0, that means we don't know the window size. */ - if ((vty->lines == 0) || (vty->width == 0)) - flushrc = buffer_flush_available(vty->obuf, vty->fd); - else if (vty->status == VTY_MORELINE) - flushrc = buffer_flush_window(vty->obuf, vty->fd, vty->width, - 1, erase, 0); - else - flushrc = buffer_flush_window(vty->obuf, vty->fd, vty->width, - vty->lines >= 0 ? vty->lines : - vty->height, - erase, 0); - switch (flushrc) - { - case BUFFER_ERROR: - vty->monitor = 0; /* disable monitoring to avoid infinite recursion */ - zlog_warn("buffer_flush failed on vty client fd %d, closing", - vty->fd); - buffer_reset(vty->obuf); - vty_close(vty); - return 0; - case BUFFER_EMPTY: - if (vty->status == VTY_CLOSE) - vty_close (vty); - else - { - vty->status = VTY_NORMAL; - if (vty->lines == 0) - vty_event (VTY_READ, vty_sock, vty); - } - break; - case BUFFER_PENDING: - /* There is more data waiting to be written. */ - vty->status = VTY_MORE; - if (vty->lines == 0) - vty_event (VTY_WRITE, vty_sock, vty); - break; - } + /* Stat for vtysh Zebra.conf, if found startup and wait for + * boot configuration + */ - return 0; -} + if ( strstr(config_default, "vtysh") == NULL) + { + ret = stat (integrate_default, &conf_stat); + if (ret >= 0) + return; + } + } ; -/* Create new vty structure. */ -static struct vty * -vty_create (int vty_sock, union sockunion *su) -{ - struct vty *vty; + /* Use default if necessary, and deal with constructing full path */ + if (config_file == NULL) + config_file = config_default ; - /* Allocate new vty structure and set up default values. */ - vty = vty_new (); - vty->fd = vty_sock; - vty->type = VTY_TERM; - vty->address = sockunion_su2str (su); - if (no_password_check) + if (! IS_DIRECTORY_SEP (config_file[0])) { - if (restricted_mode) - vty->node = RESTRICTED_NODE; - else if (host.advanced) - vty->node = ENABLE_NODE; - else - vty->node = VIEW_NODE; + getcwd (cwd, sizeof(cwd)) ; + tmp = XMALLOC (MTYPE_TMP, strlen (cwd) + strlen (config_file) + 2) ; + sprintf (tmp, "%s/%s", cwd, config_file); + fullpath = tmp; } else - vty->node = AUTH_NODE; - vty->fail = 0; - vty->cp = 0; - vty_clear_buf (vty); - vty->length = 0; - memset (vty->hist, 0, sizeof (vty->hist)); - vty->hp = 0; - vty->hindex = 0; - vector_set_index (vtyvec, vty_sock, vty); - vty->status = VTY_NORMAL; - vty->v_timeout = vty_timeout_val; - if (host.lines >= 0) - vty->lines = host.lines; - else - vty->lines = -1; - vty->iac = 0; - vty->iac_sb_in_progress = 0; - vty->sb_len = 0; - - if (! no_password_check) { - /* Vty is not available if password isn't set. */ - if (host.password == NULL && host.password_encrypt == NULL) - { - vty_out (vty, "Vty password is not set.%s", VTY_NEWLINE); - vty->status = VTY_CLOSE; - vty_close (vty); - return NULL; - } - } - - /* Say hello to the world. */ - vty_hello (vty); - if (! no_password_check) - vty_out (vty, "%sUser Access Verification%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); - - /* Setting up terminal. */ - vty_will_echo (vty); - vty_will_suppress_go_ahead (vty); - - vty_dont_linemode (vty); - vty_do_window_size (vty); - /* vty_dont_lflow_ahead (vty); */ - - vty_prompt (vty); - - /* Add read/write thread. */ - vty_event (VTY_WRITE, vty_sock, vty); - vty_event (VTY_READ, vty_sock, vty); - - return vty; -} - -/* Accept connection from the network. */ -static int -vty_accept (struct thread *thread) -{ - int vty_sock; - struct vty *vty; - union sockunion su; - int ret; - unsigned int on; - int accept_sock; - struct prefix *p = NULL; - struct access_list *acl = NULL; - char *bufp; - - accept_sock = THREAD_FD (thread); + tmp = NULL ; + fullpath = config_file; + } ; - /* We continue hearing vty socket. */ - vty_event (VTY_SERV, accept_sock, NULL); + /* try to open the configuration file */ + confp = fopen (fullpath, "r"); - memset (&su, 0, sizeof (union sockunion)); - - /* We can handle IPv4 or IPv6 socket. */ - vty_sock = sockunion_accept (accept_sock, &su); - if (vty_sock < 0) + if (confp == NULL) { - zlog_warn ("can't accept vty socket : %s", safe_strerror (errno)); - return -1; - } - set_nonblocking(vty_sock); + fprintf (stderr, "%s: failed to open configuration file %s: %s\n", + __func__, fullpath, errtostr(errno, 0).str); - p = sockunion2hostprefix (&su); - - /* VTY's accesslist apply. */ - if (p->family == AF_INET && vty_accesslist_name) - { - if ((acl = access_list_lookup (AFI_IP, vty_accesslist_name)) && - (access_list_apply (acl, p) == FILTER_DENY)) - { - char *buf; - zlog (NULL, LOG_INFO, "Vty connection refused from %s", - (buf = sockunion_su2str (&su))); - free (buf); - close (vty_sock); - - /* continue accepting connections */ - vty_event (VTY_SERV, accept_sock, NULL); - - prefix_free (p); - - return 0; - } - } + confp = vty_use_backup_config (fullpath); + if (confp) + fprintf (stderr, "WARNING: using backup configuration file!\n"); + else + { + fprintf (stderr, "can't open backup configuration file [%s%s]\n", + fullpath, CONF_BACKUP_EXT); + exit(1); + } + } ; -#ifdef HAVE_IPV6 - /* VTY's ipv6 accesslist apply. */ - if (p->family == AF_INET6 && vty_ipv6_accesslist_name) - { - if ((acl = access_list_lookup (AFI_IP6, vty_ipv6_accesslist_name)) && - (access_list_apply (acl, p) == FILTER_DENY)) - { - char *buf; - zlog (NULL, LOG_INFO, "Vty connection refused from %s", - (buf = sockunion_su2str (&su))); - free (buf); - close (vty_sock); - - /* continue accepting connections */ - vty_event (VTY_SERV, accept_sock, NULL); - - prefix_free (p); - - return 0; - } - } -#endif /* HAVE_IPV6 */ - - prefix_free (p); +#ifdef QDEBUG + fprintf(stderr, "Reading config file: %s\n", fullpath); +#endif - on = 1; - ret = setsockopt (vty_sock, IPPROTO_TCP, TCP_NODELAY, - (char *) &on, sizeof (on)); - if (ret < 0) - zlog (NULL, LOG_INFO, "can't set sockopt to vty_sock : %s", - safe_strerror (errno)); + vty_read_file (confp, first_cmd, ignore_warnings); + fclose (confp); - zlog (NULL, LOG_INFO, "Vty connection from %s", - (bufp = sockunion_su2str (&su))); - if (bufp) - XFREE (MTYPE_TMP, bufp); + host_config_set (fullpath); - vty = vty_create (vty_sock, &su); +#ifdef QDEBUG + fprintf(stderr, "Finished reading config file\n"); +#endif - return 0; + if (tmp) + XFREE (MTYPE_TMP, tmp); } -#if defined(HAVE_IPV6) && !defined(NRL) -static void -vty_serv_sock_addrinfo (const char *hostname, unsigned short port) +/*------------------------------------------------------------------------------ + * Try to use a backup configuration file. + * + * Having failed to open the file "<fullpath>", if there is a file called + * "<fullpath>.sav" that can be opened for reading, then: + * + * - make a copy of that file + * - call it "<fullpath>" + * - return an open FILE + * + * Returns: NULL => no "<fullpath>.sav", or faild doing any of the above + * otherwise, returns FILE* for open file. + */ +static FILE * +vty_use_backup_config (char *fullpath) { - int ret; - struct addrinfo req; - struct addrinfo *ainfo; - struct addrinfo *ainfo_save; - int sock; - char port_str[BUFSIZ]; + char *tmp_path ; + struct stat buf; + int ret, tmp, sav; + int c; + char buffer[4096] ; - memset (&req, 0, sizeof (struct addrinfo)); - req.ai_flags = AI_PASSIVE; - req.ai_family = AF_UNSPEC; - req.ai_socktype = SOCK_STREAM; - sprintf (port_str, "%d", port); - port_str[sizeof (port_str) - 1] = '\0'; + enum { xl = 32 } ; + tmp_path = malloc(strlen(fullpath) + xl) ; - ret = getaddrinfo (hostname, port_str, &req, &ainfo); + /* construct the name "<fullname>.sav", and try to open it. */ + confirm(xl > sizeof(CONF_BACKUP_EXT)) ; + sprintf (tmp_path, "%s%s", fullpath, CONF_BACKUP_EXT) ; - if (ret != 0) - { - fprintf (stderr, "getaddrinfo failed: %s\n", gai_strerror (ret)); - exit (1); - } - - ainfo_save = ainfo; + sav = -1 ; + if (stat (tmp_path, &buf) != -1) + sav = open (tmp_path, O_RDONLY); - do + if (sav < 0) { - if (ainfo->ai_family != AF_INET -#ifdef HAVE_IPV6 - && ainfo->ai_family != AF_INET6 -#endif /* HAVE_IPV6 */ - ) - continue; - - sock = socket (ainfo->ai_family, ainfo->ai_socktype, ainfo->ai_protocol); - if (sock < 0) - continue; - - sockopt_reuseaddr (sock); - sockopt_reuseport (sock); - - ret = bind (sock, ainfo->ai_addr, ainfo->ai_addrlen); - if (ret < 0) - { - close (sock); /* Avoid sd leak. */ - continue; - } - - ret = listen (sock, 3); - if (ret < 0) - { - close (sock); /* Avoid sd leak. */ - continue; - } - - vty_event (VTY_SERV, sock, NULL); - } - while ((ainfo = ainfo->ai_next) != NULL); - - freeaddrinfo (ainfo_save); -} -#endif /* HAVE_IPV6 && ! NRL */ + free (tmp_path); + return NULL; + } ; -/* Make vty server socket. */ -static void -vty_serv_sock_family (const char* addr, unsigned short port, int family) -{ - int ret; - union sockunion su; - int accept_sock; - void* naddr=NULL; + /* construct a temporary file and copy "<fullpath.sav>" to it. */ + confirm(xl > sizeof(".XXXXXX")) + sprintf (tmp_path, "%s%s", fullpath, ".XXXXXX") ; - memset (&su, 0, sizeof (union sockunion)); - su.sa.sa_family = family; - if(addr) - switch(family) - { - case AF_INET: - naddr=&su.sin.sin_addr; -#ifdef HAVE_IPV6 - case AF_INET6: - naddr=&su.sin6.sin6_addr; -#endif - } - - if(naddr) - switch(inet_pton(family,addr,naddr)) + /* Open file to configuration write. */ + tmp = mkstemp (tmp_path); + if (tmp < 0) { - case -1: - zlog_err("bad address %s",addr); - naddr=NULL; - break; - case 0: - zlog_err("error translating address %s: %s",addr,safe_strerror(errno)); - naddr=NULL; + free (tmp_path); + close(sav); + return NULL; } - /* Make new socket. */ - accept_sock = sockunion_stream_socket (&su); - if (accept_sock < 0) - return; + while((c = read (sav, buffer, sizeof(buffer))) > 0) + write (tmp, buffer, c); - /* This is server, so reuse address. */ - sockopt_reuseaddr (accept_sock); - sockopt_reuseport (accept_sock); + close (sav); + close (tmp); - /* Bind socket to universal address and given port. */ - ret = sockunion_bind (accept_sock, &su, port, naddr); - if (ret < 0) + /* Make sure that have the required file status */ + if (chmod(tmp_path, CONFIGFILE_MASK) != 0) { - zlog_warn("can't bind socket"); - close (accept_sock); /* Avoid sd leak. */ - return; + unlink (tmp_path); + free (tmp_path); + return NULL; } - /* Listen socket under queue 3. */ - ret = listen (accept_sock, 3); - if (ret < 0) - { - zlog (NULL, LOG_WARNING, "can't listen socket"); - close (accept_sock); /* Avoid sd leak. */ - return; - } + /* Make <fullpath> be a name for the new file just created. */ + ret = link (tmp_path, fullpath) ; - /* Add vty server event. */ - vty_event (VTY_SERV, accept_sock, NULL); -} + /* Discard the temporary, now */ + unlink (tmp_path) ; + free (tmp_path) ; -#ifdef VTYSH -/* For sockaddr_un. */ -#include <sys/un.h> + /* If link was successful, try to open -- otherwise, failed. */ + return (ret == 0) ? fopen (fullpath, "r") : NULL ; +} ; -/* VTY shell UNIX domain socket. */ +/*------------------------------------------------------------------------------ + * Read the given configuration file. + * + * May have a function to call after the first actual command is processed. + * This mechanism supports the setting of qpthreads-ness by configuration file + * command. + * + * In the qpthreads world: + * + * * when the configuration is first read, this runs in the CLI thread + * (the main and only thread). + * + * * when the configuration is reread, this runs in the command processor + * thread. + * + * All consoles are shut down, so there can be no interference from that + * quarter. + * + * so all commands are executed directly. + */ static void -vty_serv_un (const char *path) -{ - int ret; - int sock, len; - struct sockaddr_un serv; - mode_t old_mask; - struct zprivs_ids_t ids; - - /* First of all, unlink existing socket */ - unlink (path); - - /* Set umask */ - old_mask = umask (0007); - - /* Make UNIX domain socket. */ - sock = socket (AF_UNIX, SOCK_STREAM, 0); - if (sock < 0) - { - zlog_err("Cannot create unix stream socket: %s", safe_strerror(errno)); - return; - } - - /* Make server socket. */ - memset (&serv, 0, sizeof (struct sockaddr_un)); - serv.sun_family = AF_UNIX; - strncpy (serv.sun_path, path, strlen (path)); -#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN - len = serv.sun_len = SUN_LEN(&serv); -#else - len = sizeof (serv.sun_family) + strlen (serv.sun_path); -#endif /* HAVE_STRUCT_SOCKADDR_UN_SUN_LEN */ - - ret = bind (sock, (struct sockaddr *) &serv, len); - if (ret < 0) - { - zlog_err("Cannot bind path %s: %s", path, safe_strerror(errno)); - close (sock); /* Avoid sd leak. */ - return; - } - - ret = listen (sock, 5); - if (ret < 0) - { - zlog_err("listen(fd %d) failed: %s", sock, safe_strerror(errno)); - close (sock); /* Avoid sd leak. */ - return; - } - - umask (old_mask); - - zprivs_get_ids(&ids); - - if (ids.gid_vty > 0) - { - /* set group of socket */ - if ( chown (path, -1, ids.gid_vty) ) - { - zlog_err ("vty_serv_un: could chown socket, %s", - safe_strerror (errno) ); - } - } - - vty_event (VTYSH_SERV, sock, NULL); -} - -/* #define VTYSH_DEBUG 1 */ - -static int -vtysh_accept (struct thread *thread) +vty_read_file (FILE *confp, struct cmd_element* first_cmd, bool ignore_warnings) { - int accept_sock; - int sock; - int client_len; - struct sockaddr_un client; - struct vty *vty; - - accept_sock = THREAD_FD (thread); - - vty_event (VTYSH_SERV, accept_sock, NULL); - - memset (&client, 0, sizeof (struct sockaddr_un)); - client_len = sizeof (struct sockaddr_un); - - sock = accept (accept_sock, (struct sockaddr *) &client, - (socklen_t *) &client_len); - - if (sock < 0) - { - zlog_warn ("can't accept vty socket : %s", safe_strerror (errno)); - return -1; - } - - if (set_nonblocking(sock) < 0) - { - zlog_warn ("vtysh_accept: could not set vty socket %d to non-blocking," - " %s, closing", sock, safe_strerror (errno)); - close (sock); - return -1; - } - -#ifdef VTYSH_DEBUG - printf ("VTY shell accept\n"); -#endif /* VTYSH_DEBUG */ - - vty = vty_new (); - vty->fd = sock; - vty->type = VTY_SHELL_SERV; - vty->node = VIEW_NODE; + enum cmd_return_code ret ; + struct vty *vty ; + qtime_t taking ; - vty_event (VTYSH_READ, sock, vty); - - return 0; -} - -static int -vtysh_flush(struct vty *vty) -{ - switch (buffer_flush_available(vty->obuf, vty->fd)) - { - case BUFFER_PENDING: - vty_event(VTYSH_WRITE, vty->fd, vty); - break; - case BUFFER_ERROR: - vty->monitor = 0; /* disable monitoring to avoid infinite recursion */ - zlog_warn("%s: write error to fd %d, closing", __func__, vty->fd); - buffer_reset(vty->obuf); - vty_close(vty); - return -1; - break; - case BUFFER_EMPTY: - break; - } - return 0; -} - -static int -vtysh_read (struct thread *thread) -{ - int ret; - int sock; - int nbytes; - struct vty *vty; - unsigned char buf[VTY_READ_BUFSIZ]; - unsigned char *p; - u_char header[4] = {0, 0, 0, 0}; - - sock = THREAD_FD (thread); - vty = THREAD_ARG (thread); - vty->t_read = NULL; - - if ((nbytes = read (sock, buf, VTY_READ_BUFSIZ)) <= 0) - { - if (nbytes < 0) - { - if (ERRNO_IO_RETRY(errno)) - { - vty_event (VTYSH_READ, sock, vty); - return 0; - } - vty->monitor = 0; /* disable monitoring to avoid infinite recursion */ - zlog_warn("%s: read failed on vtysh client fd %d, closing: %s", - __func__, sock, safe_strerror(errno)); - } - buffer_reset(vty->obuf); - vty_close (vty); -#ifdef VTYSH_DEBUG - printf ("close vtysh\n"); -#endif /* VTYSH_DEBUG */ - return 0; - } - -#ifdef VTYSH_DEBUG - printf ("line: %.*s\n", nbytes, buf); -#endif /* VTYSH_DEBUG */ + /* Set up configuration file reader VTY -- which buffers all output */ + vty = vty_open(VTY_CONFIG_READ); + vty->node = CONFIG_NODE; - for (p = buf; p < buf+nbytes; p++) - { - vty_ensure(vty, vty->length+1); - vty->buf[vty->length++] = *p; - if (*p == '\0') - { - /* Pass this line to parser. */ - ret = vty_execute (vty); - /* Note that vty_execute clears the command buffer and resets - vty->length to 0. */ - - /* Return result. */ -#ifdef VTYSH_DEBUG - printf ("result: %d\n", ret); - printf ("vtysh node: %d\n", vty->node); -#endif /* VTYSH_DEBUG */ - - header[3] = ret; - buffer_put(vty->obuf, header, 4); - - if (!vty->t_write && (vtysh_flush(vty) < 0)) - /* Try to flush results; exit if a write error occurs. */ - return 0; - } - } + /* Make sure we have a suitable buffer, and set vty->buf to point at + * it -- same like other command execution. + */ + qs_need(&vty->vio->clx, VTY_BUFSIZ) ; + vty->buf = qs_chars(&vty->vio->clx) ; - vty_event (VTYSH_READ, sock, vty); + taking = qt_get_monotonic() ; - return 0; -} + /* Execute configuration file */ + ret = config_from_file (vty, confp, first_cmd, &vty->vio->clx, + ignore_warnings) ; -static int -vtysh_write (struct thread *thread) -{ - struct vty *vty = THREAD_ARG (thread); + taking = (qt_get_monotonic() - taking) / (QTIME_SECOND / 1000) ; - vty->t_write = NULL; - vtysh_flush(vty); - return 0; -} + zlog_info("Finished reading configuration in %d.%dsecs%s", + (int)(taking / 1000), (int)(taking % 1000), + (ret == CMD_SUCCESS) ? "." : " -- FAILED") ; -#endif /* VTYSH */ + VTY_LOCK() ; -/* Determine address family to bind. */ -void -vty_serv_sock (const char *addr, unsigned short port, const char *path) -{ - /* If port is set to 0, do not listen on TCP/IP at all! */ - if (port) + if (ret != CMD_SUCCESS) { + fprintf (stderr, "%% while processing line %u of the configuration:\n" + "%s", vty->lineno, vty->buf) ; -#ifdef HAVE_IPV6 -#ifdef NRL - vty_serv_sock_family (addr, port, AF_INET); - vty_serv_sock_family (addr, port, AF_INET6); -#else /* ! NRL */ - vty_serv_sock_addrinfo (addr, port); -#endif /* NRL*/ -#else /* ! HAVE_IPV6 */ - vty_serv_sock_family (addr,port, AF_INET); -#endif /* HAVE_IPV6 */ - } - -#ifdef VTYSH - vty_serv_un (path); -#endif /* VTYSH */ -} - -/* Close vty interface. Warning: call this only from functions that - will be careful not to access the vty afterwards (since it has - now been freed). This is safest from top-level functions (called - directly by the thread dispatcher). */ -void -vty_close (struct vty *vty) -{ - int i; - - /* Cancel threads.*/ - if (vty->t_read) - thread_cancel (vty->t_read); - if (vty->t_write) - thread_cancel (vty->t_write); - if (vty->t_timeout) - thread_cancel (vty->t_timeout); - - /* Flush buffer. */ - buffer_flush_all (vty->obuf, vty->fd); - - /* Free input buffer. */ - buffer_free (vty->obuf); - - /* Free command history. */ - for (i = 0; i < VTY_MAXHIST; i++) - if (vty->hist[i]) - XFREE (MTYPE_VTY_HIST, vty->hist[i]); - - /* Unset vector. */ - vector_unset (vtyvec, vty->fd); - - /* Close socket. */ - if (vty->fd > 0) - close (vty->fd); - - if (vty->address) - XFREE (MTYPE_TMP, vty->address); - if (vty->buf) - XFREE (MTYPE_VTY, vty->buf); - - /* Check configure. */ - vty_config_unlock (vty); - - /* OK free vty. */ - XFREE (MTYPE_VTY, vty); -} - -/* When time out occur output message then close connection. */ -static int -vty_timeout (struct thread *thread) -{ - struct vty *vty; + switch (ret) + { + case CMD_WARNING: + fprintf (stderr, "%% Warning...\n"); + break; - vty = THREAD_ARG (thread); - vty->t_timeout = NULL; - vty->v_timeout = 0; + case CMD_ERROR: + fprintf (stderr, "%% Error...\n"); + break; - /* Clear buffer*/ - buffer_reset (vty->obuf); - vty_out (vty, "%sVty connection is timed out.%s", VTY_NEWLINE, VTY_NEWLINE); + case CMD_ERR_AMBIGUOUS: + fprintf (stderr, "%% Ambiguous command.\n"); + break; - /* Close connection. */ - vty->status = VTY_CLOSE; - vty_close (vty); + case CMD_ERR_NO_MATCH: + fprintf (stderr, "%% There is no such command.\n"); + break; - return 0; -} + case CMD_ERR_INCOMPLETE: + fprintf (stderr, "%% Incomplete command.\n"); + break; -/* Read up configuration file from file_name. */ -static void -vty_read_file (FILE *confp) -{ - int ret; - struct vty *vty; + default: + fprintf(stderr, "%% (unknown cause %d)\n", ret) ; + break ; + } ; - vty = vty_new (); - vty->fd = 0; /* stdout */ - vty->type = VTY_TERM; - vty->node = CONFIG_NODE; - - /* Execute configuration file */ - ret = config_from_file (vty, confp); + uty_out_fflush(vty->vio, stderr) ; /* flush command output buffer */ - if ( !((ret == CMD_SUCCESS) || (ret == CMD_ERR_NOTHING_TODO)) ) - { - switch (ret) - { - case CMD_ERR_AMBIGUOUS: - fprintf (stderr, "Ambiguous command.\n"); - break; - case CMD_ERR_NO_MATCH: - fprintf (stderr, "There is no such command.\n"); - break; - } - fprintf (stderr, "Error occured during reading below line.\n%s\n", - vty->buf); - vty_close (vty); exit (1); - } + } ; - vty_close (vty); -} + uty_close(vty->vio) ; + VTY_UNLOCK() ; +} ; -static FILE * -vty_use_backup_config (char *fullpath) -{ - char *fullpath_sav, *fullpath_tmp; - FILE *ret = NULL; - struct stat buf; - int tmp, sav; - int c; - char buffer[512]; - - fullpath_sav = malloc (strlen (fullpath) + strlen (CONF_BACKUP_EXT) + 1); - strcpy (fullpath_sav, fullpath); - strcat (fullpath_sav, CONF_BACKUP_EXT); - if (stat (fullpath_sav, &buf) == -1) - { - free (fullpath_sav); - return NULL; - } - - fullpath_tmp = malloc (strlen (fullpath) + 8); - sprintf (fullpath_tmp, "%s.XXXXXX", fullpath); - - /* Open file to configuration write. */ - tmp = mkstemp (fullpath_tmp); - if (tmp < 0) - { - free (fullpath_sav); - free (fullpath_tmp); - return NULL; - } - - sav = open (fullpath_sav, O_RDONLY); - if (sav < 0) - { - unlink (fullpath_tmp); - free (fullpath_sav); - free (fullpath_tmp); - return NULL; - } - - while((c = read (sav, buffer, 512)) > 0) - write (tmp, buffer, c); - - close (sav); - close (tmp); - - if (chmod(fullpath_tmp, CONFIGFILE_MASK) != 0) - { - unlink (fullpath_tmp); - free (fullpath_sav); - free (fullpath_tmp); - return NULL; - } - - if (link (fullpath_tmp, fullpath) == 0) - ret = fopen (fullpath, "r"); - - unlink (fullpath_tmp); - - free (fullpath_sav); - free (fullpath_tmp); - return ret; -} +/*============================================================================== + * Configuration node/state handling + * + * At most one VTY may hold the configuration symbol of power at any time. + */ -/* Read up configuration file from file_name. */ -void -vty_read_config (char *config_file, - char *config_default_dir) +/*------------------------------------------------------------------------------ + * Attempt to gain the configuration symbol of power + * + * If succeeds, set the given node. + * + * Returns: true <=> now own the symbol of power. + */ +extern bool +vty_config_lock (struct vty *vty, enum node_type node) { - char cwd[MAXPATHLEN]; - FILE *confp = NULL; - char *fullpath; - char *tmp = NULL; + bool result; - /* If -f flag specified. */ - if (config_file != NULL) - { - if (! IS_DIRECTORY_SEP (config_file[0])) - { - getcwd (cwd, MAXPATHLEN); - tmp = XMALLOC (MTYPE_TMP, - strlen (cwd) + strlen (config_file) + 2); - sprintf (tmp, "%s/%s", cwd, config_file); - fullpath = tmp; - } - else - fullpath = config_file; + VTY_LOCK() ; - confp = fopen (fullpath, "r"); - - if (confp == NULL) - { - fprintf (stderr, "%s: failed to open configuration file %s: %s\n", - __func__, fullpath, safe_strerror (errno)); - - confp = vty_use_backup_config (fullpath); - if (confp) - fprintf (stderr, "WARNING: using backup configuration file!\n"); - else - { - fprintf (stderr, "can't open configuration file [%s]\n", - config_file); - exit(1); - } - } - } - else + if (vty_config == 0) { -#ifdef VTYSH - int ret; - struct stat conf_stat; + vty->vio->config = 1 ; + vty_config = 1 ; + } ; - /* !!!!PLEASE LEAVE!!!! - * This is NEEDED for use with vtysh -b, or else you can get - * a real configuration food fight with a lot garbage in the - * merged configuration file it creates coming from the per - * daemon configuration files. This also allows the daemons - * to start if there default configuration file is not - * present or ignore them, as needed when using vtysh -b to - * configure the daemons at boot - MAG - */ - - /* Stat for vtysh Zebra.conf, if found startup and wait for - * boot configuration - */ - - if ( strstr(config_default_dir, "vtysh") == NULL) - { - ret = stat (integrate_default, &conf_stat); - if (ret >= 0) - return; - } -#endif /* VTYSH */ - - confp = fopen (config_default_dir, "r"); - if (confp == NULL) - { - fprintf (stderr, "%s: failed to open configuration file %s: %s\n", - __func__, config_default_dir, safe_strerror (errno)); - - confp = vty_use_backup_config (config_default_dir); - if (confp) - { - fprintf (stderr, "WARNING: using backup configuration file!\n"); - fullpath = config_default_dir; - } - else - { - fprintf (stderr, "can't open configuration file [%s]\n", - config_default_dir); - exit (1); - } - } - else - fullpath = config_default_dir; - } - - vty_read_file (confp); - - fclose (confp); - - host_config_set (fullpath); - - if (tmp) - XFREE (MTYPE_TMP, fullpath); -} - -/* Small utility function which output log to the VTY. */ -void -vty_log (const char *level, const char *proto_str, - const char *format, struct timestamp_control *ctl, va_list va) -{ - unsigned int i; - struct vty *vty; - - if (!vtyvec) - return; - - for (i = 0; i < vector_active (vtyvec); i++) - if ((vty = vector_slot (vtyvec, i)) != NULL) - if (vty->monitor) - { - va_list ac; - va_copy(ac, va); - vty_log_out (vty, level, proto_str, format, ctl, ac); - va_end(ac); - } -} + result = vty->vio->config; -/* Async-signal-safe version of vty_log for fixed strings. */ -void -vty_log_fixed (const char *buf, size_t len) -{ - unsigned int i; - struct iovec iov[2]; + if (result) + vty->node = node ; - /* vty may not have been initialised */ - if (!vtyvec) - return; - - iov[0].iov_base = (void *)buf; - iov[0].iov_len = len; - iov[1].iov_base = (void *)"\r\n"; - iov[1].iov_len = 2; + VTY_UNLOCK() ; - for (i = 0; i < vector_active (vtyvec); i++) - { - struct vty *vty; - if (((vty = vector_slot (vtyvec, i)) != NULL) && vty->monitor) - /* N.B. We don't care about the return code, since process is - most likely just about to die anyway. */ - writev(vty->fd, iov, 2); - } + return result; } -int -vty_config_lock (struct vty *vty) +/*------------------------------------------------------------------------------ + * Give back the configuration symbol of power -- if own it. + * + * Set the given node -- which must be <= MAX_NON_CONFIG_NODE + */ +extern void +vty_config_unlock (struct vty *vty, enum node_type node) { - if (vty_config == 0) - { - vty->config = 1; - vty_config = 1; - } - return vty->config; + VTY_LOCK() ; + uty_config_unlock(vty, node); + VTY_UNLOCK() ; } -int -vty_config_unlock (struct vty *vty) +/*------------------------------------------------------------------------------ + * Give back the configuration symbol of power -- if own it. + * + * Set the given node -- which must be <= MAX_NON_CONFIG_NODE + */ +extern void +uty_config_unlock (struct vty *vty, enum node_type node) { - if (vty_config == 1 && vty->config == 1) + VTY_ASSERT_LOCKED() ; + if ((vty_config == 1) && (vty->vio->config == 1)) { - vty->config = 0; - vty_config = 0; + vty->vio->config = 0; + vty_config = 0; } - return vty->config; -} - -/* Master of the threads. */ -static struct thread_master *master; -static void -vty_event (enum event event, int sock, struct vty *vty) -{ - struct thread *vty_serv_thread; - - switch (event) - { - case VTY_SERV: - vty_serv_thread = thread_add_read (master, vty_accept, vty, sock); - vector_set_index (Vvty_serv_thread, sock, vty_serv_thread); - break; -#ifdef VTYSH - case VTYSH_SERV: - thread_add_read (master, vtysh_accept, vty, sock); - break; - case VTYSH_READ: - vty->t_read = thread_add_read (master, vtysh_read, vty, sock); - break; - case VTYSH_WRITE: - vty->t_write = thread_add_write (master, vtysh_write, vty, sock); - break; -#endif /* VTYSH */ - case VTY_READ: - vty->t_read = thread_add_read (master, vty_read, vty, sock); + assert(node <= MAX_NON_CONFIG_NODE) ; + vty->node = node ; +} ; - /* Time out treatment. */ - if (vty->v_timeout) - { - if (vty->t_timeout) - thread_cancel (vty->t_timeout); - vty->t_timeout = - thread_add_timer (master, vty_timeout, vty, vty->v_timeout); - } - break; - case VTY_WRITE: - if (! vty->t_write) - vty->t_write = thread_add_write (master, vty_flush, vty, sock); - break; - case VTY_TIMEOUT_RESET: - if (vty->t_timeout) - { - thread_cancel (vty->t_timeout); - vty->t_timeout = NULL; - } - if (vty->v_timeout) - { - vty->t_timeout = - thread_add_timer (master, vty_timeout, vty, vty->v_timeout); - } - break; - } -} - -DEFUN (config_who, +/*============================================================================== + * Commands + * + */ +DEFUN_CALL (config_who, config_who_cmd, "who", "Display who is on vty\n") { - unsigned int i; - struct vty *v; + unsigned int i = 0; + vty_io vio ; - for (i = 0; i < vector_active (vtyvec); i++) - if ((v = vector_slot (vtyvec, i)) != NULL) - vty_out (vty, "%svty[%d] connected from %s.%s", - v->config ? "*" : " ", - i, v->address, VTY_NEWLINE); + VTY_LOCK() ; + + vio = vio_list_base ; + while (vio != NULL) /* TODO: show only VTY_TERM ??? */ + { + uty_out (vty, "%svty[%d] connected from %s.%s", + vio->config ? "*" : " ", + i, uty_get_name(vio), VTY_NEWLINE); + vio = sdl_next(vio, vio_list) ; + } ; + VTY_UNLOCK() ; return CMD_SUCCESS; } /* Move to vty configuration mode. */ -DEFUN (line_vty, +DEFUN_CALL (line_vty, line_vty_cmd, "line vty", "Configure a terminal line\n" "Virtual terminal\n") { + VTY_LOCK() ; vty->node = VTY_NODE; + VTY_UNLOCK() ; return CMD_SUCCESS; } @@ -2584,6 +1471,8 @@ exec_timeout (struct vty *vty, const char *min_str, const char *sec_str) { unsigned long timeout = 0; + VTY_LOCK() ; + /* min_str and sec_str are already checked by parser. So it must be all digit string. */ if (min_str) @@ -2595,14 +1484,15 @@ exec_timeout (struct vty *vty, const char *min_str, const char *sec_str) timeout += strtol (sec_str, NULL, 10); vty_timeout_val = timeout; - vty->v_timeout = timeout; - vty_event (VTY_TIMEOUT_RESET, 0, vty); + if (vty_term(vty) || vty_shell_serv(vty)) + uty_sock_set_timer(&vty->vio->sock, timeout) ; + VTY_UNLOCK() ; return CMD_SUCCESS; } -DEFUN (exec_timeout_min, +DEFUN_CALL (exec_timeout_min, exec_timeout_min_cmd, "exec-timeout <0-35791>", "Set timeout value\n" @@ -2611,7 +1501,7 @@ DEFUN (exec_timeout_min, return exec_timeout (vty, argv[0], NULL); } -DEFUN (exec_timeout_sec, +DEFUN_CALL (exec_timeout_sec, exec_timeout_sec_cmd, "exec-timeout <0-35791> <0-2147483>", "Set the EXEC timeout\n" @@ -2621,7 +1511,7 @@ DEFUN (exec_timeout_sec, return exec_timeout (vty, argv[0], argv[1]); } -DEFUN (no_exec_timeout, +DEFUN_CALL (no_exec_timeout, no_exec_timeout_cmd, "no exec-timeout", NO_STR @@ -2631,61 +1521,71 @@ DEFUN (no_exec_timeout, } /* Set vty access class. */ -DEFUN (vty_access_class, +DEFUN_CALL (vty_access_class, vty_access_class_cmd, "access-class WORD", "Filter connections based on an IP access list\n" "IP access list\n") { + VTY_LOCK() ; + if (vty_accesslist_name) XFREE(MTYPE_VTY, vty_accesslist_name); vty_accesslist_name = XSTRDUP(MTYPE_VTY, argv[0]); + VTY_UNLOCK() ; return CMD_SUCCESS; } /* Clear vty access class. */ -DEFUN (no_vty_access_class, +DEFUN_CALL (no_vty_access_class, no_vty_access_class_cmd, "no access-class [WORD]", NO_STR "Filter connections based on an IP access list\n" "IP access list\n") { + int result = CMD_SUCCESS; + + VTY_LOCK() ; if (! vty_accesslist_name || (argc && strcmp(vty_accesslist_name, argv[0]))) { - vty_out (vty, "Access-class is not currently applied to vty%s", + uty_out (vty, "Access-class is not currently applied to vty%s", VTY_NEWLINE); - return CMD_WARNING; + result = CMD_WARNING; + } + else + { + XFREE(MTYPE_VTY, vty_accesslist_name); + vty_accesslist_name = NULL; } - XFREE(MTYPE_VTY, vty_accesslist_name); - - vty_accesslist_name = NULL; - - return CMD_SUCCESS; + VTY_UNLOCK() ; + return result; } #ifdef HAVE_IPV6 /* Set vty access class. */ -DEFUN (vty_ipv6_access_class, +DEFUN_CALL (vty_ipv6_access_class, vty_ipv6_access_class_cmd, "ipv6 access-class WORD", IPV6_STR "Filter connections based on an IP access list\n" "IPv6 access list\n") { + VTY_LOCK() ; if (vty_ipv6_accesslist_name) XFREE(MTYPE_VTY, vty_ipv6_accesslist_name); vty_ipv6_accesslist_name = XSTRDUP(MTYPE_VTY, argv[0]); + VTY_UNLOCK() ; return CMD_SUCCESS; } /* Clear vty access class. */ -DEFUN (no_vty_ipv6_access_class, +DEFUN_CALL (no_vty_ipv6_access_class, no_vty_ipv6_access_class_cmd, "no ipv6 access-class [WORD]", NO_STR @@ -2693,112 +1593,135 @@ DEFUN (no_vty_ipv6_access_class, "Filter connections based on an IP access list\n" "IPv6 access list\n") { + int result = CMD_SUCCESS; + + VTY_LOCK() ; + if (! vty_ipv6_accesslist_name || (argc && strcmp(vty_ipv6_accesslist_name, argv[0]))) { - vty_out (vty, "IPv6 access-class is not currently applied to vty%s", + uty_out (vty, "IPv6 access-class is not currently applied to vty%s", VTY_NEWLINE); - return CMD_WARNING; + result = CMD_WARNING; } + else + { + XFREE(MTYPE_VTY, vty_ipv6_accesslist_name); - XFREE(MTYPE_VTY, vty_ipv6_accesslist_name); - - vty_ipv6_accesslist_name = NULL; + vty_ipv6_accesslist_name = NULL; + } + VTY_UNLOCK() ; return CMD_SUCCESS; } #endif /* HAVE_IPV6 */ /* vty login. */ -DEFUN (vty_login, +DEFUN_CALL (vty_login, vty_login_cmd, "login", "Enable password checking\n") { + VTY_LOCK() ; no_password_check = 0; + VTY_UNLOCK() ; return CMD_SUCCESS; } -DEFUN (no_vty_login, +DEFUN_CALL (no_vty_login, no_vty_login_cmd, "no login", NO_STR "Enable password checking\n") { + VTY_LOCK() ; no_password_check = 1; + VTY_UNLOCK() ; return CMD_SUCCESS; } /* initial mode. */ -DEFUN (vty_restricted_mode, +DEFUN_CALL (vty_restricted_mode, vty_restricted_mode_cmd, "anonymous restricted", "Restrict view commands available in anonymous, unauthenticated vty\n") { + VTY_LOCK() ; restricted_mode = 1; + VTY_UNLOCK() ; return CMD_SUCCESS; } -DEFUN (vty_no_restricted_mode, +DEFUN_CALL (vty_no_restricted_mode, vty_no_restricted_mode_cmd, "no anonymous restricted", NO_STR "Enable password checking\n") { + VTY_LOCK() ; restricted_mode = 0; + VTY_UNLOCK() ; return CMD_SUCCESS; } -DEFUN (service_advanced_vty, +DEFUN_CALL (service_advanced_vty, service_advanced_vty_cmd, "service advanced-vty", "Set up miscellaneous service\n" "Enable advanced mode vty interface\n") { + VTY_LOCK() ; host.advanced = 1; + VTY_UNLOCK() ; return CMD_SUCCESS; } -DEFUN (no_service_advanced_vty, +DEFUN_CALL (no_service_advanced_vty, no_service_advanced_vty_cmd, "no service advanced-vty", NO_STR "Set up miscellaneous service\n" "Enable advanced mode vty interface\n") { + VTY_LOCK() ; host.advanced = 0; + VTY_UNLOCK() ; return CMD_SUCCESS; } -DEFUN (terminal_monitor, +DEFUN_CALL (terminal_monitor, terminal_monitor_cmd, "terminal monitor", "Set terminal line parameters\n" "Copy debug output to the current terminal line\n") { - vty->monitor = 1; + VTY_LOCK() ; + uty_set_monitor(vty->vio, true); + VTY_UNLOCK() ; return CMD_SUCCESS; } -DEFUN (terminal_no_monitor, +DEFUN_CALL (terminal_no_monitor, terminal_no_monitor_cmd, "terminal no monitor", "Set terminal line parameters\n" NO_STR "Copy debug output to the current terminal line\n") { - vty->monitor = 0; + VTY_LOCK() ; + uty_set_monitor(vty->vio, false); + VTY_UNLOCK() ; return CMD_SUCCESS; } -ALIAS (terminal_no_monitor, +ALIAS_CALL (terminal_no_monitor, no_terminal_monitor_cmd, "no terminal monitor", NO_STR "Set terminal line parameters\n" "Copy debug output to the current terminal line\n") -DEFUN (show_history, +DEFUN_CALL (show_history, show_history_cmd, "show history", SHOW_STR @@ -2806,24 +1729,34 @@ DEFUN (show_history, { int index; - for (index = vty->hindex + 1; index != vty->hindex;) + VTY_LOCK() ; + + for (index = vty->vio->hindex + 1; index != vty->vio->hindex;) { + qstring line ; + if (index == VTY_MAXHIST) { index = 0; continue; } - if (vty->hist[index] != NULL) - vty_out (vty, " %s%s", vty->hist[index], VTY_NEWLINE); + line = vector_get_item(&vty->vio->hist, index) ; + if (line != NULL) + uty_out (vty, " %s%s", line->char_body, VTY_NEWLINE); index++; } + VTY_UNLOCK() ; return CMD_SUCCESS; } -/* Display current configuration. */ +/*============================================================================== + * Output the current configuration + * + * Returns: CMD_SUCCESS + */ static int vty_config_write (struct vty *vty) { @@ -2839,14 +1772,14 @@ vty_config_write (struct vty *vty) /* exec-timeout */ if (vty_timeout_val != VTY_TIMEOUT_DEFAULT) - vty_out (vty, " exec-timeout %ld %ld%s", + vty_out (vty, " exec-timeout %ld %ld%s", vty_timeout_val / 60, vty_timeout_val % 60, VTY_NEWLINE); /* login */ if (no_password_check) vty_out (vty, " no login%s", VTY_NEWLINE); - + if (restricted_mode != restricted_mode_default) { if (restricted_mode_default) @@ -2854,58 +1787,22 @@ vty_config_write (struct vty *vty) else vty_out (vty, " anonymous restricted%s", VTY_NEWLINE); } - + vty_out (vty, "!%s", VTY_NEWLINE); return CMD_SUCCESS; } -struct cmd_node vty_node = -{ - VTY_NODE, - "%s(config-line)# ", - 1, -}; - -/* Reset all VTY status. */ -void -vty_reset () -{ - unsigned int i; - struct vty *vty; - struct thread *vty_serv_thread; - - for (i = 0; i < vector_active (vtyvec); i++) - if ((vty = vector_slot (vtyvec, i)) != NULL) - { - buffer_reset (vty->obuf); - vty->status = VTY_CLOSE; - vty_close (vty); - } - - for (i = 0; i < vector_active (Vvty_serv_thread); i++) - if ((vty_serv_thread = vector_slot (Vvty_serv_thread, i)) != NULL) - { - thread_cancel (vty_serv_thread); - vector_slot (Vvty_serv_thread, i) = NULL; - close (i); - } - - vty_timeout_val = VTY_TIMEOUT_DEFAULT; - - if (vty_accesslist_name) - { - XFREE(MTYPE_VTY, vty_accesslist_name); - vty_accesslist_name = NULL; - } - - if (vty_ipv6_accesslist_name) - { - XFREE(MTYPE_VTY, vty_ipv6_accesslist_name); - vty_ipv6_accesslist_name = NULL; - } -} +/*============================================================================== + * The cwd at start-up. + */ +/*------------------------------------------------------------------------------ + * Save cwd + * + * This is done early in the morning so that any future operations on files + * can use the original cwd if required. + */ static void vty_save_cwd (void) { @@ -2920,51 +1817,212 @@ vty_save_cwd (void) getcwd (cwd, MAXPATHLEN); } - vty_cwd = XMALLOC (MTYPE_TMP, strlen (cwd) + 1); - strcpy (vty_cwd, cwd); -} + vty_cwd = XSTRDUP(MTYPE_TMP, cwd) ; +} ; +/*------------------------------------------------------------------------------ + * Get cwd as at start-up. Never changed -- so no locking required. + */ char * vty_get_cwd () { return vty_cwd; } -int +/*============================================================================== + * Access functions for VTY values, where locking is or might be required. + */ + +bool vty_shell (struct vty *vty) { - return vty->type == VTY_SHELL ? 1 : 0; + bool result; + VTY_LOCK() ; + result = (vty->vio->type == VTY_SHELL) ; + VTY_UNLOCK() ; + return result; +} + +bool +vty_term(struct vty *vty) +{ + bool result; + VTY_LOCK() ; + result = (vty->vio->type == VTY_TERM); + VTY_UNLOCK() ; + return result; } -int +bool vty_shell_serv (struct vty *vty) { - return vty->type == VTY_SHELL_SERV ? 1 : 0; + bool result; + VTY_LOCK() ; + result = (vty->vio->type == VTY_SHELL_SERV); + VTY_UNLOCK() ; + return result; +} + +enum node_type +vty_get_node(struct vty *vty) +{ + int result; + VTY_LOCK() ; + result = vty->node; + VTY_UNLOCK() ; + return result; } void -vty_init_vtysh () +vty_set_node(struct vty *vty, enum node_type node) { - vtyvec = vector_init (VECTOR_MIN_SIZE); + VTY_LOCK() ; + vty->node = node; + VTY_UNLOCK() ; } -/* Install vty's own commands like `who' command. */ void -vty_init (struct thread_master *master_thread) +vty_set_lines(struct vty *vty, int lines) { - /* For further configuration read, preserve current directory. */ - vty_save_cwd (); + VTY_LOCK() ; + vty->vio->lines = lines; + vty->vio->lines_set = 1 ; + uty_set_height(vty->vio) ; + VTY_UNLOCK() ; +} - vtyvec = vector_init (VECTOR_MIN_SIZE); +/*============================================================================== + * + */ +const char* wordlist[] = + { + "Lorem", + "ipsum", + "dolor", + "magna", + "vita", + "brevis", + "Aliquot", + "in", + "tempura", + "mores", + "ad", + "Astronomica", + "per", + "impedimenta", + "quod", + "et", + "sed", + "semper", + "ut", + "Elisium", + "est", + }; + + +DEFUN (delay_secs, + delay_secs_cmd, + "delay <0-600> secs <0-10000> lines", + "Delay for a number of seconds and spit out a number of lines\n" + "Delay time\n" + "Delay time units\n" + "How much to output\n" + "Output units\n") +{ + unsigned long delay ; + unsigned long lines ; + + unsigned long unit ; + + delay = strtol(argv[0], NULL, 10) ; + lines = strtol(argv[1], NULL, 10) ; + + vty_out(vty, "delay %d secs %d lines\n", (int)delay, (int)lines) ; + + unit = (lines * 100) / delay ; + + while (delay--) + { + char buf[200] ; + char* e ; + int n ; + int w = sizeof(wordlist) / sizeof(char*) ; + + sleep(1) ; + + n = ((rand() % (unit + 1)) + (unit / 2)) / 100 ; + + if ((n > (int)lines) || (delay == 0)) + n = lines ; + + lines -= n ; + + while (n--) + { + char* q ; + const char* p ; + int a ; + + q = buf ; + e = buf + (rand() % 120) + 30 ; + + if ((rand() % 6) == 0) + e = buf ; + + a = (rand() % 4) == 1 ; + while (q < e) + { + int s ; + s = 0 ; + if (a == 1) + s = (rand() % 5) + 1 ; + else if (a > 1) + s = 1 ; + + while (s--) + *q++ = ' ' ; + + p = wordlist[rand() % w] ; + while (*p != '\0') + *q++ = *p++ ; + + a = (rand() % 4) + 1 ; + } ; + + *q++ = '\n' ; + *q++ = '\0' ; - master = master_thread; + vty_out(vty, buf) ; + } ; + } ; - /* Initilize server thread vector. */ - Vvty_serv_thread = vector_init (VECTOR_MIN_SIZE); + return CMD_SUCCESS; +} + +/*============================================================================== + * The VTY command nodes + */ + +struct cmd_node vty_node = +{ + VTY_NODE, + "%s(config-line)# ", + 1, +}; + +/*------------------------------------------------------------------------------ + * Install vty's own commands like `who' command. + */ +static void +uty_init_commands (void) +{ + VTY_ASSERT_LOCKED() ; - /* Install bgp top node. */ install_node (&vty_node, vty_config_write); + install_element (VIEW_NODE, &delay_secs_cmd); + install_element (ENABLE_NODE, &delay_secs_cmd); + install_element (RESTRICTED_NODE, &config_who_cmd); install_element (RESTRICTED_NODE, &show_history_cmd); install_element (VIEW_NODE, &config_who_cmd); @@ -2993,18 +2051,4 @@ vty_init (struct thread_master *master_thread) install_element (VTY_NODE, &vty_ipv6_access_class_cmd); install_element (VTY_NODE, &no_vty_ipv6_access_class_cmd); #endif /* HAVE_IPV6 */ -} - -void -vty_terminate (void) -{ - if (vty_cwd) - XFREE (MTYPE_TMP, vty_cwd); - - if (vtyvec && Vvty_serv_thread) - { - vty_reset (); - vector_free (vtyvec); - vector_free (Vvty_serv_thread); - } -} +} ; |