diff options
author | Chris Hall <GMCH@hestia.halldom.com> | 2010-03-16 01:35:19 +0000 |
---|---|---|
committer | Chris Hall <GMCH@hestia.halldom.com> | 2010-03-16 01:35:19 +0000 |
commit | d87a9d74eab06082ea49313083ffa0aa41f666f9 (patch) | |
tree | 7c6f7ae0be39683b7c90ea298454ec28d49406cb /lib/vty.c | |
parent | 05fb7fd0421b395c089bb08dd0e0d78d3746b8cf (diff) | |
download | quagga-d87a9d74eab06082ea49313083ffa0aa41f666f9.tar.bz2 quagga-d87a9d74eab06082ea49313083ffa0aa41f666f9.tar.xz |
Major update
bgpd/bgp_advertise.c
bgpd/bgp_advertise.h
The adj_in and adj_out objects are now put on a list based
on the peer to whom the route belongs.
The adj_in and adj_out objects also now point to the bgp_node
which they are routes for.
This substantially reduces the work needed to shut down a peer.
bgpd/bgp_damp.c
Changes to adj_in and adj_out forced small change to macros
used in bgp_damp.c to manage its lists.
bgpd/bgp_debug.c
Replaced direct access to vty->node by the required vty_get_node().
bgpd/bgp_dump.c
Changes to the names of fields in bgp_info structures.
bgpd/bgp_engine.h
Modified the debug and trace functions.
bgpd/bgp_fsm.c
Make use of sockunion2str() consistent with common usage.
Improved some documentation.
bgpd/bgp_main.c
Use the newly extended qpn_add_hook_function() facility.
bgpd/bgp_mplsvpn.c
Changes to the names of fields in bgp_info structures.
bgpd/bgp_msg_read.c
Bug fix: correct handling of capability code length.
Improvement: better casting in calculation of message length.
bgpd/bgp_msg_write.c
Bug fix: correct byte ordering of bgp_id in open message.
bgpd/bgp_network.c
Bug fix: correct handling of incoming connections.
Takes advantage of improvements in sockunion.c.
bgpd/bgp_nexthop.c
Changes to the names of fields in bgp_info structures.
bgpd/bgp_open_state.c
Remove mistaken #include of memtypes.h
bgpd/bgp_packet.c
Improvements to handling of withdrawing routes for peers.
bgpd/bgp_peer.c
Tidying up the state of peers as they are enabled and disabled.
Improvements to handling of withdrawing routes for peers.
bgpd/bgp_peer.h
Adding list bases for lists of routes originated by the peer.
bgpd/bgp_peer_index.c
Bug fix: correct freeing of peer indexes.
bgpd/bgp_route.c
Implement lists of bgp_info based in the owning peer. Adjust
for name changes to bgp_info fields.
Reimplemented all the clearing functions to use the lists of
items that belong to the peer -- rather than searching route
tables for stuff to withdraw.
Changed work queue handling for added/changed routes, so
that queues run through existing items, rather than having
queues of auxiliary items -- lower memory overhead.
bgpd/bgp_route.h
Added fields to bgp_info to allow all bgp_info originated by
each peer to live on lists based in the peer. And changed the
name of existing fields to avoid confusion.
bgpd/bgp_routemap.c
Removing redundant code and fixing a memory leak.
bgpd/bgp_table.h
Based work queue for added/changed routes directly in the
table, rather than having auxiliary structures.
bgpd/bgp_vty.c
Use vty_get_node() and vty_set_node() rather than direct
access to the vty field.
bgpd/bgpd.c
Implement changes to route clearing.
bgpd/bgpd.h
Changes to work queue handling.
lib/buffer.c
Changes to allow embedded buffer structures.
lib/buffer.h
Moved struct buffer here so that could have embedded buffer
structurs.
lib/command.c
Substantial tidy up and document exercise.
Restructured the top level command processing and finding
of descriptions and command completion.
Removal of unpleasant messing around with the insides of
vector structures.
Movement of some command actions to vty.c.
Uses uty.h to pick up the "private" functions from vty.c
et al.
lib/command.h
Moved the "node" values to node_type.h, so that can use an
enum node_type in places where cannot include command.h.
lib/command_queue.c
Updated to cope with the called command changing the node
value.
Improved handling of revoked commands, so the the command
line handler does not get stuck waiting for a command to
complete which has been revoked !
lib/command_queue.h
Improved message format.
lib/if.c
Use vty_set_node().
lib/keychain.c
Use vty_set_node().
new lib/keystroke.c
new lib/keystroke.h
New code to implement a keystroke FIFO.
This moves some complexity out of the command handler.
The handling of mixtures of escapes and Telnet IACs is
tightened up.
It would be possible to extend this to, say, UTF-8.
Regularises the "stealing" of keystrokes for the "--more--"
output handling... which was a bit hit and miss.
new lib/list_util.c
new lib/list_util.h
New code to implement various forms of linked list,
where the list pointers are embedded in structures.
lib/log.c
Changed the handling of log messages, so that all types
of log output (except syslog) use the same message buffer
scheme, and the message is constructed once and once
only.
Changes to the handling of VTY_LOCK() etc.
Uses uty.h to pick up the "private" functions from vty.c
et al.
lib/log.h
Changes to the buffering of log messages.
new lib/mem_tracker.c
New code to track memory allocation/deallocation, for
debug purposes.
lib/memory.c
lib/memory.h
Updated to allow the use of the mem_tracker.
lib/memtypes.awk
Made the memtypes into a named enum MTYPE.
lib/memtypes.c
Various new memory types.
lib/mqueue.c
lib/mqueue.h
Add mqueue_finish function for close-down.
lib/network.c
lib/network.h
Added non-blocking read_nb() and write_nb().
new lib/node_type.h
As above.
lib/plist.c
Remove vty_puts() which wasn't a good idea.
lib/qlib_init.c
Added qps_init() to first stage and mqueue_finish to
finish.
lib/qpnexus.c
lib/qpnexus.h
More flexible hooks for in_thread_init and in_thread_final.
lib/qpselect.c
lib/qpselect.h
Added qps_start_up() to build the required maps once and
for all.
Added qdebug to control the debug checks and validation.
Improved validation and test functions.
new lib/qstring.c
new lib/qstring.h
New code for limited flexible string handling.
lib/qtimers.c
Added qdebug to control the debug checks and validation.
lib/routemap.c
Use vty_set_node().
lib/sockunion.c
lib/sockunion.h
Tidied up and regularised the handling of sin_len and
sin6_len.
Created common function for setting port into socket.
Created common function for initialisation/allocation of
new sockunion.
Reduced various functions by using common sub-functions.
Rationalised some code.
Added sockunion_listen() and sockunion_new_sockaddr().
Renamed sockunion_new() to sockunion_new_prefix().
Improved some logging messages.
Added documentation.
new lib/uty.h
Functions etc. used only by vty/command/log/vty_io and
vty_cli.
lib/vector.c
lib/vector.h
Added vector_t type.
Removed VECTOR_INDEX, vector_only_wrapper_free() and
vector_only_index_free() -- following improvement of
code in command.c.
Added vector_set_min_length(), vector_set_new_min_length()
and vector_length() functions.
new lib/vio_fifo.c
new lib/vio_fifo.h
New code to manage simple FIFO of indefinite length.
lib/vty.c
lib/vty.h
Reworked. Broken into vty.c, vty_io.c and vty_cli.c.
new lib/vty_cli.c
new lib/vty_cli.h
CLI handling parts of the vty family.
new lib/vty_io.c
new lib/vty_io.h
I/O parts of the vty family.
lib/workqueue.h
Introduced tyedefs for the various call-back entries.
new tests/test-list_util.c
Tests for the list-util stuff.
vtysh/vtysh.c
Small change to interface for cmd_execute_command()
Diffstat (limited to 'lib/vty.c')
-rw-r--r-- | lib/vty.c | 3837 |
1 files changed, 879 insertions, 2958 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 @@ -20,327 +21,348 @@ * 02111-1307, USA. */ -#include <zebra.h> -#include "miyagi.h" +#include "zebra.h" +#include <stdbool.h> + +#include "vty_io.h" +#include "vty.h" +#include "uty.h" +#include "vty_cli.h" + +#include "list_util.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.h" -#include "privs.h" -#include "network.h" - -#include <arpa/telnet.h> -#include "qpthreads.h" -#include "qpnexus.h" - -/* Needs to be qpthread safe */ -qpt_mutex_t vty_mutex; -#ifdef NDEBUG -#define LOCK qpt_mutex_lock(&vty_mutex); -#define UNLOCK qpt_mutex_unlock(&vty_mutex); -#else -int vty_lock_count = 0; -int vty_lock_asserted = 0; -#define LOCK qpt_mutex_lock(&vty_mutex);++vty_lock_count; -#define UNLOCK --vty_lock_count;qpt_mutex_unlock(&vty_mutex); -#define ASSERTLOCKED if(vty_lock_count==0 && !vty_lock_asserted){vty_lock_asserted=1;assert(0);} + +/*============================================================================== + * Variables etc. + */ + +/*------------------------------------------------------------------------------ + * Static and Global (see uty.h) Variables + */ + +/* The mutex and related debug counters */ +qpt_mutex_t vty_mutex ; + +#if VTY_DEBUG + +int vty_lock_count = 0 ; +int vty_lock_assert_fail = 0 ; + #endif -/* - * To make vty qpthread safe we use a single mutex. In general external - * routines have explicit locks, static routines assume that they are being - * called with the mutex already locked. There are a few exceptions, e.g. - * callbacks where static routines are being invoked from outside the module. - * - * There are a few cases where both external and static versions of a - * routine exist. The former for use outside, the latter for use inside - * the module (and lock). In these cases the internal static versions - * starts uty_. This is not strictly necessary as we are using a recursive - * mutex but it avoids unnecessary recursive calls. The recursive mutex - * is used so that we can call zlog and friends from anywhere. - * - * vty and log recurse through each other, so the same mutex is used - * for both, i.e. they are treated as being part of the same monitor. +/* For thread handling -- initialised in vty_init */ +struct thread_master* vty_master = NULL ; + +/* 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 ; -/* Vty events */ -enum event -{ - VTY_SERV, - VTY_READ, - VTY_WRITE, - VTY_TIMEOUT_RESET, -#ifdef VTYSH - VTYSH_SERV, - VTYSH_READ, - VTYSH_WRITE -#endif /* VTYSH */ -}; +/* List of all known vio */ +vty_io vio_list_base = NULL ; + +/* List of all vty which are in monitor state. */ +vty_io vio_monitors_base = NULL ; + +/* List of all vty which are on death watch */ +vty_io vio_death_watch = NULL ; + +/* Vty timeout value -- see "exec timeout" command */ +unsigned long vty_timeout_val = VTY_TIMEOUT_DEFAULT; + +/* Vty access-class command */ +char *vty_accesslist_name = NULL; -/* Prototypes */ -static int uty_out (struct vty *vty, const char *format, ...); -static int uty_vout(struct vty *vty, const char *format, va_list args); -static void vty_event (enum event, int, struct vty *); -static void uty_hello (struct vty *vty); -static void uty_close (struct vty *vty); -static int uty_config_unlock (struct vty *vty); -static int uty_shell (struct vty *vty); -static int uty_read (struct vty *vty, int vty_sock); -static int uty_flush (struct vty *vty, int vty_sock); -static void vty_event_t (enum event event, int sock, struct vty *vty); -static void vty_event_r (enum event event, int sock, struct vty *vty); -static int uty_accept (int accept_sock); -static int uty_timeout (struct vty *vty); -static void vty_timeout_r (qtimer qtr, void* timer_info, qtime_t when); -static void vty_read_r (qps_file qf, void* file_info); -static void vty_flush_r (qps_file qf, void* file_info); -void uty_reset (void); - -/* Extern host structure from command.c */ -extern struct host host; - -/* Vector which store each vty structure. */ -static vector vtyvec; - -/* Vty timeout value. */ -static unsigned long vty_timeout_val = VTY_TIMEOUT_DEFAULT; - -/* Vty access-class command */ -static char *vty_accesslist_name = NULL; - -/* Vty access-calss for IPv6. */ -static char *vty_ipv6_accesslist_name = NULL; - -/* VTY server thread. */ -static vector Vvty_serv_thread; - -/* Current directory. */ +/* Vty access-class for IPv6. */ +char *vty_ipv6_accesslist_name = NULL; + +/* Current directory -- initialised in vty_init() */ static char *vty_cwd = NULL; -/* Configure lock. */ -static int vty_config; +/* Configure lock -- only one vty may be in CONFIG_NODE or above ! */ +bool vty_config = 0 ; -/* Login password check. */ -static int no_password_check = 0; +/* Login password check override. */ +bool no_password_check = 0; -/* Restrict unauthenticated logins? */ -static const u_char restricted_mode_default = 0; -static u_char restricted_mode = 0; +/* Restrict unauthenticated logins? */ +const bool restricted_mode_default = 0 ; + bool restricted_mode = 0 ; -/* Integrated configuration file path */ -char integrate_default[] = SYSCONFDIR INTEGRATE_DEFAULT_CONFIG; +/* Watch-dog timer. */ +union vty_watch_dog vty_watch_dog = { NULL } ; -/* Master of the threads. */ -static struct thread_master *master = NULL; -static qpn_nexus cli_nexus = NULL; -static qpn_nexus routing_nexus = NULL; +/*------------------------------------------------------------------------------ + * VTYSH stuff + */ -/* VTY standard output function. vty == NULL or VTY_SHELL => stdout */ -int -vty_out (struct vty *vty, const char *format, ...) -{ - int result; +/* Integrated configuration file path -- for VTYSH */ +char integrate_default[] = SYSCONFDIR INTEGRATE_DEFAULT_CONFIG ; - LOCK - va_list args; - va_start (args, format); - result = uty_vout(vty, format, args); - va_end (args); - UNLOCK - return result; -} +/*------------------------------------------------------------------------------ + * Prototypes + */ +static void uty_reset (bool final) ; +static void uty_init_commands (void) ; +static void vty_save_cwd (void) ; -/* internal VTY standard output function. vty == NULL or VTY_SHELL => stdout */ -static int -uty_out (struct vty *vty, const char *format, ...) -{ - int result; - ASSERTLOCKED - va_list args; - va_start (args, format); - result = uty_vout(vty, format, args); - va_end (args); - return result; -} +/*============================================================================== + * Public Interface + */ -/* internal VTY standard output function. vty == NULL or VTY_SHELL => stdout */ -static int -uty_vout(struct vty *vty, const char *format, va_list args) +/*------------------------------------------------------------------------------ + * Initialise vty handling (threads and pthreads) + * + * Install vty's own commands like `who' command. + */ +extern void +vty_init (struct thread_master *master_thread) { - int len = 0; - int size = 1024; - char buf[1024]; - char *p = NULL; - va_list ac; + VTY_LOCK() ; - ASSERTLOCKED + vty_master = master_thread; /* Local pointer to the master thread */ - if (uty_shell (vty)) - { - vprintf (format, args); - } - else - { - /* Try to write to initial buffer. */ - va_copy(ac, args); - len = vsnprintf (buf, sizeof buf, format, ac); - va_end(ac); + vty_save_cwd (); /* need cwd for config reading */ - /* Initial buffer is not enough. */ - if (len < 0 || len >= size) - { - while (1) - { - if (len > -1) - size = len + 1; - else - size = size * 2; + vio_list_base = NULL ; /* no VTYs yet */ + vio_monitors_base = NULL ; + vio_death_watch = NULL ; - p = XREALLOC (MTYPE_VTY_OUT_BUF, p, size); - if (! p) - return -1; + vty_cli_nexus = NULL ; /* not running qnexus-wise */ + vty_cmd_nexus = NULL ; - va_copy(ac, args); - len = vsnprintf (p, size, format, ac); - va_end(ac); + vty_watch_dog.anon = NULL ; /* no watch dog */ - if (len > -1 && len < size) - break; - } - } + uty_init_commands() ; /* install nodes */ - /* When initial buffer is enough to store all output. */ - if (! p) - p = buf; + VTY_UNLOCK() ; +} - /* Pointer p must point out buffer. */ - buffer_put (vty->obuf, (u_char *) p, len); +/*------------------------------------------------------------------------------ + * Further initialisation for qpthreads. + * + * This is done during "second stage" initialisation, when all nexuses have + * been set up and the qpthread_enabled state established. + * + * Need to know where the CLI nexus and the Routeing Engine nexus are. + * + * Initialise mutex. + */ +extern void +vty_init_r (qpn_nexus cli, qpn_nexus cmd) +{ + vty_cli_nexus = cli ; + vty_cmd_nexus = cmd ; - /* If p is not different with buf, it is allocated buffer. */ - if (p != buf) - XFREE (MTYPE_VTY_OUT_BUF, p); - } + qpt_mutex_init(&vty_mutex, qpt_mutex_recursive); +} ; - return len; -} +/*------------------------------------------------------------------------------ + * Initialise the listeners for VTY_TERM and VTY_SHELL_SERV VTY + * + * This is done after the configuration file has been read. + */ +extern void +vty_serv_sock(const char *addr, unsigned short port, const char *path) +{ + VTY_LOCK() ; + uty_open_listeners(addr, port, path) ; + VTY_UNLOCK() ; +} ; -int -vty_puts(struct vty *vty, const char* str) +/*------------------------------------------------------------------------------ + * Initialisation for vtysh application. + * + * TODO: work out what this needs to do ! (If anything.) + */ +extern void +vty_init_vtysh (void) { - return vty_out(vty, "%s", str) ; -} + VTY_LOCK() ; -int -vty_out_newline(struct vty *vty) + VTY_UNLOCK() ; +} ; + +/*------------------------------------------------------------------------------ + * Create a new VTY of the given type + */ +extern struct vty * +vty_new (int fd, enum vty_type type) +{ + struct vty* vty ; + + VTY_LOCK() ; + vty = uty_new(fd, type); + VTY_UNLOCK() ; + + return vty ; +} ; + +/*------------------------------------------------------------------------------ + * Close the given VTY completely + */ +extern void +vty_close (struct vty *vty) { - return vty_out(vty, "%s", VTY_NEWLINE) ; + VTY_LOCK() ; + uty_close(vty->vio); + VTY_UNLOCK() ; } -/* 123456789012345678901234 */ -const char* vty_spaces_string = " " ; +/*------------------------------------------------------------------------------ + * Reset all VTY status + * + * This is done just before the configuration file is re-read (SIGHUP). + * + * Half closes all VTY, leaving the death watch to tidy up once all output has + * completed. + * + * NB: old code discarded all output and hard closed all the VTY... + * + * TODO: ...SIGHUP while a command is queued ? + * + * Closes all listening sockets. + */ +extern void +vty_reset(void) +{ + VTY_LOCK() ; + uty_reset(0) ; /* not final ! */ + VTY_UNLOCK() ; +} -int -vty_out_indent(struct vty *vty, int indent) +/*------------------------------------------------------------------------------ + * System shut-down + * + * Reset all known vty and release all memory. + */ +extern void +vty_terminate (void) { - return vty_puts(vty, VTY_SPACES(indent)) ; + VTY_LOCK() ; + uty_reset(1) ; /* final reset */ + VTY_UNLOCK() ; + + qpt_mutex_destroy(&vty_mutex, 0); } -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) +/*------------------------------------------------------------------------------ + * Reset -- final or for SIGHUP + */ +static void +uty_reset (bool curtains) { - int ret; - int len; - char buf[1024]; + vty_io vio ; + + VTY_ASSERT_LOCKED() ; - ASSERTLOCKED + uty_close_listeners() ; - if (!ctl->already_rendered) + while ((vio = sdl_pop(&vio, vio_list_base, vio_list)) != NULL) { - ctl->len = uquagga_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; + uty_half_close(vio) ; /* TODO: reason for close */ - if (((ret = vsnprintf(buf+len, sizeof(buf)-len, format, va)) < 0) || - ((size_t)((len += ret)+2) > sizeof(buf))) - return -1; + if (curtains) + uty_full_close(vio) ; + } ; - buf[len++] = '\r'; - buf[len++] = '\n'; + vty_timeout_val = VTY_TIMEOUT_DEFAULT; - if (write(vty->fd, buf, len) < 0) + if (vty_accesslist_name) { - 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 */ - uzlog(NULL, LOG_WARNING, "%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; + XFREE(MTYPE_VTY, vty_accesslist_name); + vty_accesslist_name = NULL; } - return 0; -} -/* Output current time to the vty. */ -void -vty_time_print (struct vty *vty, int cr) + if (vty_ipv6_accesslist_name) + { + XFREE(MTYPE_VTY, vty_ipv6_accesslist_name); + vty_ipv6_accesslist_name = NULL; + } + + if (curtains && vty_cwd) + XFREE (MTYPE_TMP, vty_cwd); +} ; + +/*============================================================================== + * 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, ...) { - char buf [25]; + int result; - if (quagga_timestamp(0, buf, sizeof(buf)) == 0) + VTY_LOCK() ; + va_list args; + va_start (args, format); + result = uty_vout(vty, format, args); + va_end (args); + VTY_UNLOCK() ; + return result; +} + +/*------------------------------------------------------------------------------ + * VTY output -- output a given numnber of spaces + */ + +/* 1 2 3 4 */ +/* 1234567890123456789012345678901234567890 */ +const char vty_spaces_string[] = " " ; +CONFIRM(VTY_MAX_SPACES == (sizeof(vty_spaces_string) - 1)) ; + +extern int +vty_out_indent(struct vty *vty, int indent) +{ + while (indent > VTY_MAX_SPACES) { - zlog (NULL, LOG_INFO, "quagga_timestamp error"); - return; + int ret = vty_out(vty, VTY_SPACES(indent)) ; + if (ret < 0) + return ret ; + indent -= VTY_MAX_SPACES ; } + return vty_out(vty, VTY_SPACES(indent)) ; +} ; + +/*------------------------------------------------------------------------------ + * VTY output -- output the current time in standard form, to the second. + */ +extern void +vty_time_print (struct vty *vty, int cr) +{ + char buf [timestamp_buffer_len]; + + quagga_timestamp(0, buf, sizeof(buf)) ; + if (cr) - vty_out (vty, "%s\n", buf); + vty_out (vty, "%s%s", buf, VTY_NEWLINE); else vty_out (vty, "%s ", buf); return; } -/* Say hello to vty interface. */ +/*------------------------------------------------------------------------------ + * Say hello to vty interface. + */ void vty_hello (struct vty *vty) { - LOCK - uty_hello(vty); - UNLOCK -} + VTY_LOCK() ; -static void -uty_hello (struct vty *vty) -{ - ASSERTLOCKED #ifdef QDEBUG uty_out (vty, "%s%s", debug_banner, VTY_NEWLINE); #endif @@ -351,126 +373,147 @@ uty_hello (struct vty *vty) 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'; - uty_out (vty, "%s%s", buf, VTY_NEWLINE); - } - fclose (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); + uty_out (vty, "MOTD file %s not found%s", host.motdfile, VTY_NEWLINE); } else if (host.motd) uty_out (vty, "%s", host.motd); + + VTY_UNLOCK() ; } -/* Put out prompt and wait input from user. */ -static void -vty_prompt (struct vty *vty) +/*============================================================================== + * Command Execution + */ + +/*------------------------------------------------------------------------------ + * Execute command, adding it to the history if not empty or comment + * + * Outputs diagnostics if fails to parse. + * + * Returns: CMD_xxxx result. + */ +extern int +uty_command(struct vty *vty, const char *buf) { - struct utsname names; - const char*hostname; + int ret; + vector vline; + const char *protocolname; - ASSERTLOCKED + VTY_ASSERT_LOCKED() ; - if (vty->type == VTY_TERM) - { - hostname = host.name; - if (!hostname) - { - uname (&names); - hostname = names.nodename; - } - uty_out (vty, cmd_prompt (vty->node), hostname); - } -} + /* Split readline string up into the vector */ + vline = cmd_make_strvec (buf); -/* Send WILL TELOPT_ECHO to remote server. */ -static void -vty_will_echo (struct vty *vty) -{ - unsigned char cmd[] = { IAC, WILL, TELOPT_ECHO, '\0' }; - ASSERTLOCKED - uty_out (vty, "%s", cmd); -} + if (vline == NULL) + return CMD_SUCCESS; /* quit if empty or comment */ -/* Make suppress Go-Ahead telnet option. */ -static void -vty_will_suppress_go_ahead (struct vty *vty) -{ - unsigned char cmd[] = { IAC, WILL, TELOPT_SGA, '\0' }; - ASSERTLOCKED - uty_out (vty, "%s", cmd); -} + uty_cli_hist_add (vty->vio, buf) ; -/* Make don't use linemode over telnet. */ -static void -vty_dont_linemode (struct vty *vty) -{ - unsigned char cmd[] = { IAC, DONT, TELOPT_LINEMODE, '\0' }; - ASSERTLOCKED - uty_out (vty, "%s", cmd); -} +#ifdef CONSUMED_TIME_CHECK + { + RUSAGE_T before; + RUSAGE_T after; + unsigned long realtime, cputime; -/* Use window size. */ -static void -vty_do_window_size (struct vty *vty) -{ - unsigned char cmd[] = { IAC, DO, TELOPT_NAWS, '\0' }; - ASSERTLOCKED - uty_out (vty, "%s", cmd); -} + GETRUSAGE(&before); +#endif /* CONSUMED_TIME_CHECK */ -#if 0 /* Currently not used. */ -/* Make don't use lflow vty interface. */ -static void -vty_dont_lflow_ahead (struct vty *vty) -{ - unsigned char cmd[] = { IAC, DONT, TELOPT_LFLOW, '\0' }; - ASSERTLOCKED - uty_out (vty, "%s", cmd); -} -#endif /* 0 */ +//VTY_UNLOCK() ; + ret = cmd_execute_command (vline, vty, NULL, vty_cmd_nexus, vty_cli_nexus, 0); +//VTY_LOCK() ; -/* Allocate new vty struct. */ -struct vty * -vty_new (int fd, int type) -{ - struct vty *vty = XCALLOC (MTYPE_VTY, sizeof (struct vty)); + /* Get the name of the protocol if any */ + protocolname = uzlog_get_proto_name(NULL); - vty->obuf = buffer_new(0); /* Use default buffer size. */ - vty->buf = XCALLOC (MTYPE_VTY, VTY_BUFSIZ); - vty->max = VTY_BUFSIZ; - vty->fd = fd; - vty->type = type; +#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, buf); + } +#endif /* CONSUMED_TIME_CHECK */ - if (cli_nexus) - { - vty->qf = qps_file_init_new(vty->qf, NULL); - qps_add_file(cli_nexus->selection, vty->qf, vty->fd, vty); - vty->qtr = qtimer_init_new(vty->qtr, cli_nexus->pile, vty_timeout_r, vty); - } + if (ret != CMD_SUCCESS) + switch (ret) + { + case CMD_WARNING: + if (vty->vio->type == VTY_FILE) + uty_out (vty, "Warning...%s", VTY_NEWLINE); + break; + case CMD_ERR_AMBIGUOUS: + uty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE); + break; + case CMD_ERR_NO_MATCH: + uty_out (vty, "%% [%s] Unknown command: %s%s", protocolname, buf, VTY_NEWLINE); + break; + case CMD_ERR_INCOMPLETE: + uty_out (vty, "%% Command incomplete.%s", VTY_NEWLINE); + break; + } + cmd_free_strvec (vline); - return vty; + return ret; } -/* Authentication of vty */ -static void -vty_auth (struct vty *vty, char *buf) +/*------------------------------------------------------------------------------ + * 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: 0 <=> not queued. + */ +extern int +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 *); - ASSERTLOCKED + vty_io vio = vty->vio ; + + VTY_ASSERT_LOCKED() ; + + /* 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_d: + case cli_do_ctrl_z: + return 0 ; + + case cli_do_command: + break ; + + default: + zabort("unknown or invalid cli_do") ; + } ; + /* Ordinary command dispatch -- see if password is OK. */ switch (vty->node) { case AUTH_NODE: @@ -504,368 +547,102 @@ vty_auth (struct vty *vty, char *buf) if (! fail) { - vty->fail = 0; + vio->fail = 0; vty->node = next_node; /* Success ! */ } else { - vty->fail++; - if (vty->fail >= 3) + vio->fail++; + if (vio->fail >= 3) { if (vty->node == AUTH_NODE) { - uty_out (vty, "%% Bad passwords, too many failures!%s", VTY_NEWLINE); - vty->status = VTY_CLOSE; + uty_out (vty, "%% Bad passwords, too many failures!%s", + VTY_NEWLINE); + uty_half_close(vio) ; } else { /* AUTH_ENABLE_NODE */ - vty->fail = 0; - uty_out (vty, "%% Bad enable passwords, too many failures!%s", VTY_NEWLINE); + vio->fail = 0; + uty_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) -{ - int ret; - vector vline; - const char *protocolname; - - ASSERTLOCKED - - /* Split readline string up into the vector */ - vline = cmd_make_strvec (buf); - - if (vline == NULL) - return CMD_SUCCESS; - -#ifdef CONSUMED_TIME_CHECK - { - RUSAGE_T before; - RUSAGE_T after; - unsigned long realtime, cputime; - - GETRUSAGE(&before); -#endif /* CONSUMED_TIME_CHECK */ - - UNLOCK - ret = cmd_execute_command (vline, vty, NULL, routing_nexus, 0); - LOCK - - /* Get the name of the protocol if any */ - protocolname = uzlog_get_proto_name(NULL); - -#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, buf); - } -#endif /* CONSUMED_TIME_CHECK */ - - if (ret != CMD_SUCCESS) - switch (ret) - { - case CMD_WARNING: - if (vty->type == VTY_FILE) - uty_out (vty, "Warning...%s", VTY_NEWLINE); - break; - case CMD_ERR_AMBIGUOUS: - uty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE); - break; - case CMD_ERR_NO_MATCH: - uty_out (vty, "%% [%s] Unknown command: %s%s", protocolname, buf, VTY_NEWLINE); - break; - case CMD_ERR_INCOMPLETE: - uty_out (vty, "%% Command incomplete.%s", VTY_NEWLINE); - break; - } - cmd_free_strvec (vline); - - return ret; -} - -/* queued command has completed */ -void -vty_queued_result(struct vty *vty, int result) -{ - LOCK - - vty_prompt(vty); - - /* Wake up */ - if (cli_nexus) - { - vty_event (VTY_WRITE, vty->fd, vty); - if (qpthreads_enabled) - qpt_thread_signal(cli_nexus->thread_id, SIGMQUEUE); - } - - UNLOCK -} - -static const char telnet_backward_char = 0x08; -static const char telnet_space_char = ' '; - -/* Basic function to write buffer to vty. */ -static void -vty_write (struct vty *vty, const char *buf, size_t nbytes) -{ - ASSERTLOCKED - 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); -} - -/* Ensure length of input buffer. Is buffer is short, double it. */ -static void -vty_ensure (struct vty *vty, int length) -{ - ASSERTLOCKED - 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) -{ - int i; - int length; - - ASSERTLOCKED - - 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; - vty_write (vty, &vty->buf[vty->cp], length + 1); - for (i = 0; i < length; i++) - vty_write (vty, &telnet_backward_char, 1); + return 0 ; +} ; - vty->cp++; - vty->length++; -} - -/* Self insert character 'c' in overwrite mode. */ -static void -vty_self_insert_overwrite (struct vty *vty, char c) -{ - ASSERTLOCKED - vty_ensure (vty, vty->length + 1); - vty->buf[vty->cp++] = c; - - if (vty->cp > vty->length) - vty->length++; - - if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE)) - return; - - vty_write (vty, &c, 1); -} - -/* Insert a word into vty interface with overwrite mode. */ -static void -vty_insert_word_overwrite (struct vty *vty, char *str) +/*------------------------------------------------------------------------------ + * Command line "exit" command -- aka "quit" + * + * Falls back one NODE level. + * + * Returns: 0 <=> not queued. + */ +extern int +vty_cmd_exit(struct vty* vty) { + VTY_LOCK() ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ - int len = strlen (str); - - ASSERTLOCKED - - vty_write (vty, str, len); - strcpy (&vty->buf[vty->cp], str); - vty->cp += len; - vty->length = vty->cp; -} - -/* Forward character. */ -static void -vty_forward_char (struct vty *vty) -{ - ASSERTLOCKED - if (vty->cp < vty->length) - { - vty_write (vty, &vty->buf[vty->cp], 1); - vty->cp++; - } -} - -/* Backward character. */ -static void -vty_backward_char (struct vty *vty) -{ - ASSERTLOCKED - if (vty->cp > 0) + switch (vty->node) { - vty->cp--; - vty_write (vty, &telnet_backward_char, 1); + case VIEW_NODE: + case ENABLE_NODE: + case RESTRICTED_NODE: + if (vty_shell (vty)) + exit (0); +// else +// vty_set_status(vty, VTY_CLOSE); + 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; } -} - -/* Move to the beginning of the line. */ -static void -vty_beginning_of_line (struct vty *vty) -{ - ASSERTLOCKED - while (vty->cp) - vty_backward_char (vty); -} - -/* Move to the end of the line. */ -static void -vty_end_of_line (struct vty *vty) -{ - ASSERTLOCKED - while (vty->cp < vty->length) - vty_forward_char (vty); -} - -static void vty_kill_line_from_beginning (struct vty *); -static void vty_redraw_line (struct vty *); -/* Print command line history. This function is called from - vty_next_line and vty_previous_line. */ -static void -vty_history_print (struct vty *vty) -{ - int length; - - ASSERTLOCKED - - vty_kill_line_from_beginning (vty); - - /* 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); -} - -/* Show next command line history. */ -static void -vty_next_line (struct vty *vty) -{ - int try_index; - - ASSERTLOCKED - - if (vty->hp == vty->hindex) - return; - - /* Try is there history exist or not. */ - try_index = vty->hp; - if (try_index == (VTY_MAXHIST - 1)) - try_index = 0; - else - try_index++; - - /* If there is not history return. */ - if (vty->hist[try_index] == NULL) - return; - else - vty->hp = try_index; - - vty_history_print (vty); + VTY_UNLOCK() ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + return 0 ; } -/* Show previous command line history. */ -static void -vty_previous_line (struct vty *vty) -{ - int try_index; - - ASSERTLOCKED - - try_index = vty->hp; - if (try_index == 0) - try_index = VTY_MAXHIST - 1; - else - try_index--; - - if (vty->hist[try_index] == NULL) - return; - else - vty->hp = try_index; - - vty_history_print (vty); -} - -/* This function redraw all of the command line character. */ -static void -vty_redraw_line (struct vty *vty) -{ - ASSERTLOCKED - vty_write (vty, vty->buf, vty->length); - vty->cp = vty->length; -} - -/* Forward word. */ -static void -vty_forward_word (struct vty *vty) -{ - ASSERTLOCKED - 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); -} - -/* Backward word without skipping training space. */ -static void -vty_backward_pure_word (struct vty *vty) -{ - ASSERTLOCKED - while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ') - vty_backward_char (vty); -} - -/* Backward word. */ -static void -vty_backward_word (struct vty *vty) -{ - ASSERTLOCKED - while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ') - vty_backward_char (vty); - - while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ') - vty_backward_char (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) -{ - ASSERTLOCKED - uty_out (vty, "%s", VTY_NEWLINE); - (*config_exit_cmd.func)(NULL, vty, 0, NULL); - vty_prompt (vty); - vty->cp = 0; -} - -/* When '^Z' is received from vty, move down to the enable mode. */ -static void -vty_end_config (struct vty *vty) +/*------------------------------------------------------------------------------ + * Command line "end" command + * + * Falls back to ENABLE_NODE. + * + * Returns: 0 <=> not queued. + */ +extern int +vty_cmd_end(struct vty* vty) { - ASSERTLOCKED - uty_out (vty, "%s", VTY_NEWLINE); + VTY_LOCK() ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ switch (vty->node) { @@ -893,406 +670,76 @@ vty_end_config (struct vty *vty) case KEYCHAIN_KEY_NODE: case MASC_NODE: case VTY_NODE: - uty_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); - vty->cp = 0; -} - -/* Delete a charcter at the current point. */ -static void -vty_delete_char (struct vty *vty) -{ - int i; - int size; - - ASSERTLOCKED - - if (vty->length == 0) - { - vty_down_level (vty); - return; - } - - if (vty->cp == vty->length) - return; /* completion need here? */ - - size = vty->length - vty->cp; - - 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; - - vty_write (vty, &vty->buf[vty->cp], size - 1); - vty_write (vty, &telnet_space_char, 1); - - for (i = 0; i < size; i++) - vty_write (vty, &telnet_backward_char, 1); -} - -/* Delete a character before the point. */ -static void -vty_delete_backward_char (struct vty *vty) -{ - ASSERTLOCKED - if (vty->cp == 0) - return; - - vty_backward_char (vty); - vty_delete_char (vty); -} - -/* Kill rest of line from current point. */ -static void -vty_kill_line (struct vty *vty) -{ - int i; - int size; - - ASSERTLOCKED - - size = vty->length - vty->cp; - - if (size == 0) - return; - - 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); - - memset (&vty->buf[vty->cp], 0, size); - vty->length = vty->cp; -} - -/* Kill line from the beginning. */ -static void -vty_kill_line_from_beginning (struct vty *vty) -{ - ASSERTLOCKED - vty_beginning_of_line (vty); - vty_kill_line (vty); -} - -/* Delete a word before the point. */ -static void -vty_forward_kill_word (struct vty *vty) -{ - ASSERTLOCKED - 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); -} - -/* Delete a word before the point. */ -static void -vty_backward_kill_word (struct vty *vty) -{ - ASSERTLOCKED - 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); -} - -/* Transpose chars before or at the point. */ -static void -vty_transpose_chars (struct vty *vty) -{ - char c1, c2; - - ASSERTLOCKED - - /* If length is short or point is near by the beginning of line then - return. */ - if (vty->length < 2 || vty->cp < 1) - return; - - /* In case of point is located at the end of the line. */ - if (vty->cp == vty->length) - { - c1 = vty->buf[vty->cp - 1]; - c2 = vty->buf[vty->cp - 2]; - - vty_backward_char (vty); - vty_backward_char (vty); - vty_self_insert_overwrite (vty, c1); - vty_self_insert_overwrite (vty, c2); - } - else - { - c1 = vty->buf[vty->cp]; - c2 = vty->buf[vty->cp - 1]; - - vty_backward_char (vty); - vty_self_insert_overwrite (vty, c1); - vty_self_insert_overwrite (vty, c2); - } -} + VTY_UNLOCK() ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + return 0 ; +} ; -/* Do completion at vty interface. */ -static void -vty_complete_command (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: 0 <=> not queued. + */ +extern int +uty_stop_input(struct vty *vty) { + vty_io vio = vty->vio ; - int i; - int ret; - char **matched = NULL; - vector vline; - - ASSERTLOCKED - - if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE) - return; - - vline = cmd_make_strvec (vty->buf); - if (vline == NULL) - return; - - /* In case of 'help \t'. */ - if (isspace ((int) vty->buf[vty->length - 1])) - vector_set (vline, '\0'); - - matched = cmd_complete_command (vline, vty->node, &ret); + VTY_ASSERT_LOCKED() ; - cmd_free_strvec (vline); - - uty_out (vty, "%s", VTY_NEWLINE); - switch (ret) + switch (vty->node) { - case CMD_ERR_AMBIGUOUS: - uty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE); - vty_prompt (vty); - vty_redraw_line (vty); - break; - case CMD_ERR_NO_MATCH: - /* vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE); */ - vty_prompt (vty); - vty_redraw_line (vty); - 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)) - uty_out (vty, "%s", VTY_NEWLINE); - uty_out (vty, "%-10s ", matched[i]); - XFREE (MTYPE_TMP, matched[i]); - } - uty_out (vty, "%s", VTY_NEWLINE); - - vty_prompt (vty); - vty_redraw_line (vty); - break; - case CMD_ERR_NOTHING_TODO: - vty_prompt (vty); - vty_redraw_line (vty); + 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: + /* Unknown node, we have to ignore it. */ 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; - - ASSERTLOCKED - - cmd = desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd; - - if (desc_width <= 0) - { - uty_out (vty, " %-*s %s%s", cmd_width, cmd, desc->str, VTY_NEWLINE); - return; - } - - buf = XCALLOC (MTYPE_TMP, strlen (desc->str) + 1); - - for (p = desc->str; strlen (p) > desc_width; p += pos + 1) - { - for (pos = desc_width; pos > 0; pos--) - if (*(p + pos) == ' ') - break; - - if (pos == 0) - break; - - strncpy (buf, p, pos); - buf[pos] = '\0'; - uty_out (vty, " %-*s %s%s", cmd_width, cmd, buf, VTY_NEWLINE); - - cmd = ""; - } - - uty_out (vty, " %-*s %s%s", cmd_width, cmd, p, VTY_NEWLINE); - - XFREE (MTYPE_TMP, buf); -} - -/* 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; - - ASSERTLOCKED - - vline = cmd_make_strvec (vty->buf); - - /* In case of '> ?'. */ - if (vline == NULL) - { - vline = vector_init (1); - vector_set (vline, '\0'); - } - else - if (isspace ((int) vty->buf[vty->length - 1])) - vector_set (vline, '\0'); - - describe = cmd_describe_command (vline, vty->node, &ret); - - uty_out (vty, "%s", VTY_NEWLINE); - - /* Ambiguous error. */ - switch (ret) - { - case CMD_ERR_AMBIGUOUS: - uty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE); - goto out; - break; - case CMD_ERR_NO_MATCH: - uty_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; - - if (desc->cmd[0] == '\0') - continue; - - len = strlen (desc->cmd); - if (desc->cmd[0] == '.') - len--; - - if (width < len) - width = len; - } - - /* Get width of description string. */ - desc_width = vty->width - (width + 6); - /* 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) - uty_out (vty, " %-s%s", - desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, - VTY_NEWLINE); - else if (desc_width >= strlen (desc->str)) - uty_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 - uty_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)) - { - if (!desc->str) - uty_out (vty, " %-s%s", - desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, - VTY_NEWLINE); - else if (desc_width >= strlen (desc->str)) - uty_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); - } - -out: - cmd_free_strvec (vline); - if (describe) - vector_free (describe); - - vty_prompt (vty); - vty_redraw_line (vty); -} + /* Set history pointer to the latest one. */ + vio->hp = vio->hindex; -static void -vty_clear_buf (struct vty *vty) -{ - ASSERTLOCKED - memset (vty->buf, 0, vty->max); -} + return 0 ; +} ; -/* ^C stop current input and do not add command line to the history. */ -static void -vty_stop_input (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: 0 <=> not queued. + */ +extern int +uty_end_config (struct vty *vty) { - ASSERTLOCKED - vty->cp = vty->length = 0; - vty_clear_buf (vty); - uty_out (vty, "%s", VTY_NEWLINE); + VTY_ASSERT_LOCKED() ; switch (vty->node) { @@ -1307,6 +754,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: @@ -1315,1354 +767,258 @@ vty_stop_input (struct vty *vty) case KEYCHAIN_KEY_NODE: case MASC_NODE: case VTY_NODE: - uty_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; + return 0 ; } -/* Add current command line to the history buffer. */ -static void -vty_hist_add (struct vty *vty) +/*------------------------------------------------------------------------------ + * Command ^D action -- when nothing else on command line. + * + * Same as "exit" command. + * + * Returns: 0 <=> not queued. + */ +extern int +uty_down_level (struct vty *vty) { - int index; - - ASSERTLOCKED + return vty_cmd_exit(vty) ; +} ; - 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); - - /* History index rotation. */ - vty->hindex++; - if (vty->hindex == VTY_MAXHIST) - vty->hindex = 0; - - vty->hp = vty->hindex; -} +/*============================================================================== + * Reading of configuration file + */ -/* #define TELNET_OPTION_DEBUG */ +static FILE * vty_use_backup_config (char *fullpath) ; +static void vty_read_file (FILE *confp, void (*after_first_cmd)(void)) ; -/* Get telnet window size. */ -static int -vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes) +/*------------------------------------------------------------------------------ + * Read the given configuration file. + */ +extern void +vty_read_config (char *config_file, + char *config_default) { -#ifdef TELNET_OPTION_DEBUG - int i; - - ASSERTLOCKED - - for (i = 0; i < nbytes; i++) - { - switch (buf[i]) - { - case IAC: - uty_out (vty, "IAC "); - break; - case WILL: - uty_out (vty, "WILL "); - break; - case WONT: - uty_out (vty, "WONT "); - break; - case DO: - uty_out (vty, "DO "); - break; - case DONT: - uty_out (vty, "DONT "); - break; - case SB: - uty_out (vty, "SB "); - break; - case SE: - uty_out (vty, "SE "); - break; - case TELOPT_ECHO: - uty_out (vty, "TELOPT_ECHO %s", VTY_NEWLINE); - break; - case TELOPT_SGA: - uty_out (vty, "TELOPT_SGA %s", VTY_NEWLINE); - break; - case TELOPT_NAWS: - uty_out (vty, "TELOPT_NAWS %s", VTY_NEWLINE); - break; - default: - uty_out (vty, "%x ", buf[i]); - break; - } - } - uty_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) - uzlog(NULL, LOG_WARNING, "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) - uzlog(NULL, LOG_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 - uty_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; + vty_read_config_first_cmd_special(config_file, config_default, NULL); } -/* Execute current command line. */ -static int -vty_execute (struct vty *vty) +/*------------------------------------------------------------------------------ + * 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, + void (*after_first_cmd)(void)) { - int ret; - - ret = CMD_SUCCESS; - - switch (vty->node) - { - case AUTH_NODE: - case AUTH_ENABLE_NODE: - vty_auth (vty, vty->buf); - break; - default: - ret = vty_command (vty, vty->buf); - if (vty->type == VTY_TERM) - vty_hist_add (vty); - break; - } - - /* Clear command line buffer. */ - vty->cp = vty->length = 0; - vty_clear_buf (vty); - - if (vty->status != VTY_CLOSE && ret != CMD_QUEUED) - vty_prompt (vty); - - return ret; -} - -#define CONTROL(X) ((X) - '@') -#define VTY_NORMAL 0 -#define VTY_PRE_ESCAPE 1 -#define VTY_ESCAPE 2 + char cwd[MAXPATHLEN]; + FILE *confp = NULL; + char *fullpath; + char *tmp = NULL; -/* Escape character command map. */ -static void -vty_escape_map (unsigned char c, struct vty *vty) -{ - ASSERTLOCKED - switch (c) + /* Deal with VTYSH_ENABLED magic */ + if (VTYSH_ENABLED && (config_file == NULL)) { - case ('A'): - vty_previous_line (vty); - break; - case ('B'): - vty_next_line (vty); - break; - case ('C'): - vty_forward_char (vty); - break; - case ('D'): - vty_backward_char (vty); - break; - default: - break; - } - - /* Go back to normal mode. */ - vty->escape = VTY_NORMAL; -} - -/* Quit print out to the buffer. */ -static void -vty_buffer_reset (struct vty *vty) -{ - ASSERTLOCKED - buffer_reset (vty->obuf); - vty_prompt (vty); - vty_redraw_line (vty); -} - -/* Callback: qpthreads., Read data via vty socket. */ -static void -vty_read_r (qps_file qf, void* file_info) -{ - int vty_sock = qf->fd; - struct vty *vty = (struct vty *)file_info; - - LOCK - - /* is this necessary? */ - qps_disable_modes(qf, qps_read_mbit); - uty_read(vty, vty_sock); - - UNLOCK -} - -/* Callback: threads. Read data via vty socket. */ -static int -vty_read (struct thread *thread) -{ - int vty_sock = THREAD_FD (thread); - struct vty *vty = THREAD_ARG (thread); - int result ; - - LOCK + int ret; + struct stat conf_stat; - vty->t_read = NULL; - result = uty_read(vty, vty_sock); + /* !!!!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 + */ - UNLOCK - return result; -} + /* Stat for vtysh Zebra.conf, if found startup and wait for + * boot configuration + */ -static int -uty_read (struct vty *vty, int vty_sock) -{ - int i; - int nbytes; - unsigned char buf[VTY_READ_BUFSIZ]; + if ( strstr(config_default, "vtysh") == NULL) + { + ret = stat (integrate_default, &conf_stat); + if (ret >= 0) + return; + } + } ; - /* 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 */ - uzlog(NULL, LOG_WARNING, "%s: read error on vty client fd %d, closing: %s", - __func__, vty->fd, safe_strerror(errno)); - } - buffer_reset(vty->obuf); - vty->status = VTY_CLOSE; - } + /* Use default if necessary, and deal with constructing full path */ + if (config_file == NULL) + config_file = config_default ; - for (i = 0; i < nbytes; i++) + if (! IS_DIRECTORY_SEP (config_file[0])) { - 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; - } - - 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; - } - - 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': - uty_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; - } + getcwd (cwd, sizeof(cwd)) ; + tmp = XMALLOC (MTYPE_TMP, strlen (cwd) + strlen (config_file) + 2) ; + sprintf (tmp, "%s/%s", cwd, config_file); + fullpath = tmp; } - - /* Check status. */ - if (vty->status == VTY_CLOSE) - uty_close (vty); else { - vty_event (VTY_WRITE, vty_sock, vty); - vty_event (VTY_READ, vty_sock, vty); - } + tmp = NULL ; + fullpath = config_file; + } ; - return 0; -} - -/* Callback: qpthreads. Flush buffer to the vty. */ -static void -vty_flush_r (qps_file qf, void* file_info) -{ - int vty_sock = qf->fd; - struct vty *vty = (struct vty *)file_info; - - LOCK - - qps_disable_modes(qf, qps_write_mbit); + /* try to open the configuration file */ + confp = fopen (fullpath, "r"); - /* Temporary disable read thread. */ - if ((vty->lines == 0)) + if (confp == NULL) { - qps_disable_modes(qf, qps_read_mbit); - } - - uty_flush(vty, vty_sock); - - UNLOCK -} - -/* Callback: threads. Flush buffer to the vty. */ -static int -vty_flush (struct thread *thread) -{ - int vty_sock = THREAD_FD (thread); - struct vty *vty = THREAD_ARG (thread); - int result; - - LOCK - vty->t_write = NULL; - - /* Temporary disable read thread. */ - if ((vty->lines == 0) && vty->t_read) - { - thread_cancel (vty->t_read); - vty->t_read = NULL; - } - result = uty_flush(vty, vty_sock); - - UNLOCK - return result; -} - -static int -uty_flush (struct vty *vty, int vty_sock) -{ - int erase; - buffer_status_t flushrc; - - /* Function execution continue. */ - erase = ((vty->status == VTY_MORE || vty->status == VTY_MORELINE)); - - /* 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 */ - uzlog(NULL, LOG_WARNING, "buffer_flush failed on vty client fd %d, closing", - vty->fd); - buffer_reset(vty->obuf); - uty_close(vty); - break; - case BUFFER_EMPTY: - if (vty->status == VTY_CLOSE) - uty_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; - } - - return 0; -} - -/* Create new vty structure. */ -static struct vty * -vty_create (int vty_sock, union sockunion *su) -{ - struct vty *vty; + fprintf (stderr, "%s: failed to open configuration file %s: %s\n", + __func__, fullpath, safe_strerror (errno)); - ASSERTLOCKED - - /* Allocate new vty structure and set up default values. */ - vty = vty_new (vty_sock, VTY_TERM); - vty->address = sockunion_su2str (su); - if (no_password_check) - { - if (restricted_mode) - vty->node = RESTRICTED_NODE; - else if (host.advanced) - vty->node = ENABLE_NODE; + confp = vty_use_backup_config (fullpath); + if (confp) + fprintf (stderr, "WARNING: using backup configuration file!\n"); else - vty->node = VIEW_NODE; - } - 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) - { - uty_out (vty, "Vty password is not set.%s", VTY_NEWLINE); - vty->status = VTY_CLOSE; - uty_close (vty); - return NULL; - } - } - - /* Say hello to the world. */ - uty_hello (vty); - if (! no_password_check) - uty_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; -} - -/* Callback: qpthreads. Accept connection from the network. */ -static void -vty_accept_r (qps_file qf, void* file_info) -{ - LOCK - - int accept_sock = qf->fd; - uty_accept(accept_sock); - - UNLOCK -} - -/* Callback: threads. Accept connection from the network. */ -static int -vty_accept (struct thread *thread) -{ - int result; - - LOCK - - int accept_sock = THREAD_FD (thread); - result = uty_accept(accept_sock); - - UNLOCK - return result; -} - -static int -uty_accept (int accept_sock) -{ - int vty_sock; - struct vty *vty; - union sockunion su; - int ret; - unsigned int on; - struct prefix *p = NULL; - struct access_list *acl = NULL; - char *bufp; - - ASSERTLOCKED - - /* We continue hearing vty socket. */ - vty_event (VTY_SERV, accept_sock, NULL); - - memset (&su, 0, sizeof (union sockunion)); - - /* We can handle IPv4 or IPv6 socket. */ - vty_sock = sockunion_accept (accept_sock, &su); - if (vty_sock < 0) - { - uzlog (NULL, LOG_WARNING, "can't accept vty socket : %s", safe_strerror (errno)); - return -1; - } - set_nonblocking(vty_sock); - - 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; - uzlog (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; - } - } - -#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; - uzlog (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); - - on = 1; - ret = setsockopt (vty_sock, IPPROTO_TCP, TCP_NODELAY, - (char *) &on, sizeof (on)); - if (ret < 0) - uzlog (NULL, LOG_INFO, "can't set sockopt to vty_sock : %s", - safe_strerror (errno)); - - uzlog (NULL, LOG_INFO, "Vty connection from %s", - (bufp = sockunion_su2str (&su))); - if (bufp) - XFREE (MTYPE_TMP, bufp); - - vty = vty_create (vty_sock, &su); - - return 0; -} - -#if defined(HAVE_IPV6) && !defined(NRL) -static void -vty_serv_sock_addrinfo (const char *hostname, unsigned short port) -{ - int ret; - struct addrinfo req; - struct addrinfo *ainfo; - struct addrinfo *ainfo_save; - int sock; - char port_str[BUFSIZ]; - - ASSERTLOCKED - - 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'; - - ret = getaddrinfo (hostname, port_str, &req, &ainfo); - - if (ret != 0) - { - fprintf (stderr, "getaddrinfo failed: %s\n", gai_strerror (ret)); - exit (1); - } - - ainfo_save = ainfo; - - do - { - 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); - - /* set non-blocking */ - ret = set_nonblocking(sock); - if (ret < 0) { - close (sock); /* Avoid sd leak. */ - continue; + fprintf (stderr, "can't open backup configuration file [%s%s]\n", + fullpath, CONF_BACKUP_EXT); + exit(1); } + } ; - 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 */ - -#if defined(HAVE_IPV6) && defined(NRL) || !defined(HAVE_IPV6) -/* 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; - - ASSERTLOCKED - - 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; +#ifdef QDEBUG + fprintf(stderr, "Reading config file: %s\n", fullpath); #endif - } - - if(naddr) - switch(inet_pton(family,addr,naddr)) - { - case -1: - uzlog(NULL, LOG_ERR, "bad address %s",addr); - naddr=NULL; - break; - case 0: - uzlog(NULL, LOG_ERR, "error translating address %s: %s",addr,safe_strerror(errno)); - naddr=NULL; - } - - /* Make new socket. */ - accept_sock = sockunion_stream_socket (&su); - if (accept_sock < 0) - return; - - /* This is server, so reuse address. */ - sockopt_reuseaddr (accept_sock); - sockopt_reuseport (accept_sock); - - /* set non-blocking */ - ret = set_nonblocking(accept_sock); - if (ret < 0) - { - close (accept_sock); /* Avoid sd leak. */ - return; - } - - /* Bind socket to universal address and given port. */ - ret = sockunion_bind (accept_sock, &su, port, naddr); - if (ret < 0) - { - uzlog(NULL, LOG_WARNING, "can't bind socket"); - close (accept_sock); /* Avoid sd leak. */ - return; - } - - /* Listen socket under queue 3. */ - ret = listen (accept_sock, 3); - if (ret < 0) - { - uzlog (NULL, LOG_WARNING, "can't listen socket"); - close (accept_sock); /* Avoid sd leak. */ - return; - } - - /* Add vty server event. */ - vty_event (VTY_SERV, accept_sock, NULL); -} -#endif /* defined(HAVE_IPV6) && defined(NRL) || !defined(HAVE_IPV6) */ - -#ifdef VTYSH -/* For sockaddr_un. */ -#include <sys/un.h> - -/* VTY shell UNIX domain socket. */ -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; - - ASSERTLOCKED - - /* 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) - { - uzlog(NULL, LOG_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) - { - uzlog(NULL, LOG_ERR, "Cannot bind path %s: %s", path, safe_strerror(errno)); - close (sock); /* Avoid sd leak. */ - return; - } - - ret = listen (sock, 5); - if (ret < 0) - { - uzlog(NULL, LOG_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) ) - { - uzlog (NULL, LOG_ERR, "vty_serv_un: could chown socket, %s", - safe_strerror (errno) ); - } - } - - vty_event (VTYSH_SERV, sock, NULL); -} - -/* #define VTYSH_DEBUG 1 */ - -/* Callback: qpthreads. Accept connection */ -void int -vtysh_accept_r (qps_file qf, void* file_info) -{ - int accept_sock = qf->fd; - LOCK - utysh_accept (accept_sock); - UNLOCK -} - -/* Callback: threads. Accept connection */ -static int -vtysh_accept (struct thread *thread) -{ - int accept_sock = THREAD_FD (thread); - LOCK - result = utysh_accept (accept_sock); - UNLOCK - return result; -} - -static int -utysh_accept (int accept_sock) -{ - int sock; - int client_len; - struct sockaddr_un client; - struct vty *vty; - - ASSERTLOCKED - - 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) - { - uzlog (NULL, LOG_WARNING, "can't accept vty socket : %s", safe_strerror (errno)); - return -1; - } - - if (set_nonblocking(sock) < 0) - { - uzlog (NULL, LOG_WARNING, "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; - - vty_event (VTYSH_READ, sock, vty); - - return 0; -} - -static int -vtysh_flush(struct vty *vty) -{ - ASSERTLOCKED - - 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 */ - uzlog(NULL, LOG_WARNING, "%s: write error to fd %d, closing", __func__, vty->fd); - buffer_reset(vty->obuf); - uty_close(vty); - return -1; - break; - case BUFFER_EMPTY: - break; - } - return 0; -} - -/* Callback: qpthreads., Read data via vty socket. */ -static void -vtysh_read_r (qps_file qf, void* file_info) -{ - int vty_sock = qf->fd; - struct vty *vty = (struct vty *)file_info; + vty_read_file (confp, after_first_cmd); + fclose (confp); - LOCK + host_config_set (fullpath); - /* is this necessary? */ - qps_disable_modes(qf, qps_read_mbit); - utysh_read(vty, vty_soc); +#ifdef QDEBUG + fprintf(stderr, "Finished reading config file\n"); +#endif - UNLOCK + if (tmp) + XFREE (MTYPE_TMP, tmp); } -/* Callback: threads. Read data via vty socket. */ -static int -vtysh_read (struct thread *thread) +/*------------------------------------------------------------------------------ + * 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 vty_sock = THREAD_FD (thread); - struct vty *vty = THREAD_ARG (thread); - int result; - - LOCK - - vty->t_read = NULL; - result = uty_read(vty, vty_soc); - - UNLOCK - return result; -} + char *tmp_path ; + struct stat buf; + int ret, tmp, sav; + int c; + char buffer[4096] ; -static int -utysh_read (struct vty *vty, int sock) -{ - int ret; - int nbytes; - unsigned char buf[VTY_READ_BUFSIZ]; - unsigned char *p; - u_char header[4] = {0, 0, 0, 0}; + enum { xl = 32 } ; + tmp_path = malloc(strlen(fullpath) + xl) ; - 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 */ - uzlog(NULL, LOG_WARNING, "%s: read failed on vtysh client fd %d, closing: %s", - __func__, sock, safe_strerror(errno)); - } - buffer_reset(vty->obuf); - uty_close (vty); -#ifdef VTYSH_DEBUG - printf ("close vtysh\n"); -#endif /* VTYSH_DEBUG */ - return 0; - } + /* 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) ; -#ifdef VTYSH_DEBUG - printf ("line: %.*s\n", nbytes, buf); -#endif /* VTYSH_DEBUG */ + sav = -1 ; + if (stat (tmp_path, &buf) != -1) + sav = open (tmp_path, O_RDONLY); - for (p = buf; p < buf+nbytes; p++) + if (sav < 0) { - 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; - } - } - - vty_event (VTYSH_READ, sock, vty); - - return 0; -} - -/* Callback: qpthraeds. Write */ -static void -vtysh_write_r (qps_file qf, void* file_info) -{ - struct vty *vty = (struct vty *)file_info; - - LOCK - - qps_disable_modes(qf, qps_write_mbit); - vtysh_flush(vty); - - UNLOCK -} - -//* Callback: thraeds. Write */ -static int -vtysh_write (struct thread *thread) -{ - struct vty *vty = THREAD_ARG (thread); - - LOCK - - vty->t_write = NULL; - vtysh_flush(vty); - - UNLOCK - return 0; -} + free (tmp_path); + return NULL; + } ; -#endif /* VTYSH */ + /* construct a temporary file and copy "<fullpath.sav>" to it. */ + confirm(xl > sizeof(".XXXXXX")) + sprintf (tmp_path, "%s%s", fullpath, ".XXXXXX") ; -/* Determine address family to bind. */ -void -vty_serv_sock (const char *addr, unsigned short port, const char *path) -{ - LOCK - - /* If port is set to 0, do not listen on TCP/IP at all! */ - if (port) + /* Open file to configuration write. */ + tmp = mkstemp (tmp_path); + if (tmp < 0) { - -#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 */ + free (tmp_path); + close(sav); + return NULL; } -#ifdef VTYSH - vty_serv_un (path); -#endif /* VTYSH */ - - UNLOCK -} + while((c = read (sav, buffer, sizeof(buffer))) > 0) + write (tmp, buffer, c); -/* 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) -{ - LOCK - uty_close(vty); - UNLOCK -} + close (sav); + close (tmp); -static void -uty_close (struct vty *vty) -{ - int i; - - ASSERTLOCKED - - /* 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); - if (vty->qf) - { - qps_remove_file(vty->qf); - qps_file_free(vty->qf); - vty->qf = NULL; - } - if (vty->qtr) + /* Make sure that have the required file status */ + if (chmod(tmp_path, CONFIGFILE_MASK) != 0) { - qtimer_free(vty->qtr); - vty->qtr = NULL; + unlink (tmp_path); + free (tmp_path); + return NULL; } - /* 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. */ - uty_config_unlock (vty); - - /* OK free vty. */ - XFREE (MTYPE_VTY, vty); -} - -/* Callback: qpthreads. When time out occur output message then close connection. */ -static void -vty_timeout_r (qtimer qtr, void* timer_info, qtime_t when) -{ - struct vty *vty = (struct vty *)timer_info; - LOCK - qtimer_unset(qtr); - uty_timeout(vty); - UNLOCK -} - -/* Callback: threads. When time out occur output message then close connection. */ -static int -vty_timeout (struct thread *thread) -{ - int result; - struct vty *vty = THREAD_ARG (thread); - LOCK - vty->t_timeout = NULL; - result = uty_timeout(vty); - UNLOCK - return result; -} - -static int -uty_timeout (struct vty *vty) -{ - vty->v_timeout = 0; - - /* Clear buffer*/ - buffer_reset (vty->obuf); - uty_out (vty, "%sVty connection is timed out.%s", VTY_NEWLINE, VTY_NEWLINE); + /* Make <fullpath> be a name for the new file just created. */ + ret = link (tmp_path, fullpath) ; - /* Close connection. */ - vty->status = VTY_CLOSE; - uty_close (vty); + /* Discard the temporary, now */ + unlink (tmp_path) ; + free (tmp_path) ; - return 0; -} + /* If link was successful, try to open -- otherwise, failed. */ + return (ret == 0) ? fopen (fullpath, "r") : NULL ; +} ; -/* Read up configuration file from file_name. */ +/*------------------------------------------------------------------------------ + * 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. + */ static void vty_read_file (FILE *confp, void (*after_first_cmd)(void)) { int ret; struct vty *vty; + /* TODO: sort out what VTY Type should use for reading config file */ vty = vty_new (0, VTY_TERM); /* stdout */ vty->node = CONFIG_NODE; - /* Execute configuration file */ - ret = config_from_file (vty, confp, after_first_cmd); + /* 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) ; + + /* Execute configuration file */ + ret = config_from_file (vty, confp, after_first_cmd, &vty->vio->clx) ; - LOCK + VTY_LOCK() ; if ( !((ret == CMD_SUCCESS) || (ret == CMD_ERR_NOTHING_TODO)) ) { @@ -2675,219 +1031,13 @@ vty_read_file (FILE *confp, void (*after_first_cmd)(void)) fprintf (stderr, "There is no such command.\n"); break; } - fprintf (stderr, "Error occured during reading below line.\n%s\n", - vty->buf); - uty_close (vty); - exit (1); - } - - uty_close (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; -} - -/* Read up configuration file from file_name. */ -void -vty_read_config (char *config_file, - char *config_default_dir) -{ - vty_read_config_first_cmd_special(config_file, config_default_dir, NULL); -} - -/* Read up configuration file from file_name. - * callback after first command */ -void -vty_read_config_first_cmd_special(char *config_file, - char *config_default_dir, void (*after_first_cmd)(void)) -{ - char cwd[MAXPATHLEN]; - FILE *confp = NULL; - char *fullpath; - char *tmp = NULL; - - /* 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; - - 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 - { -#ifdef VTYSH - int ret; - struct stat conf_stat; - - /* !!!!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 - */ + fprintf (stderr, "Error occurred while processing:\n%s\n", vty->buf); - 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; + exit (1); } - vty_read_file (confp, after_first_cmd); - - fclose (confp); - -#ifdef QDEBUG - fprintf(stderr, "Reading config file: %s\n", fullpath); -#endif - host_config_set (fullpath); -#ifdef QDEBUG - fprintf(stderr, "Finished reading config file\n"); -#endif - - 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; - - ASSERTLOCKED - - if (!vtyvec) - return; - - for (i = 0; i < vector_active (vtyvec); i++) - if (((vty = vector_slot (vtyvec, i)) != NULL) && vty->monitor) - { - va_list ac; - va_copy(ac, va); - vty_log_out (vty, level, proto_str, format, ctl, ac); - va_end(ac); - } + uty_half_close (vty->vio); + VTY_UNLOCK() ; } #ifdef QDEBUG @@ -2898,238 +1048,120 @@ vty_goodbye (void) unsigned int i; struct vty *vty; - LOCK + VTY_LOCK() ; if (vtyvec) { for (i = 0; i < vector_active (vtyvec); i++) { - if (((vty = vector_slot (vtyvec, i)) != NULL) && vty->type == VTY_TERM) + if (((vty = vector_slot (vtyvec, i)) != NULL) && vty->vio->type == VTY_TERM) { - uty_out(vty, QUAGGA_PROGNAME " is shutting down%s", VTY_NEWLINE); + uty_cout(vty, QUAGGA_PROGNAME " is shutting down%s", VTY_NEWLINE); /* Wake up */ - if (cli_nexus) - vty_event (VTY_WRITE, vty->fd, vty); + if (vty_cli_nexus) + vty_event (VTY_WRITE, vty->vio->fd, vty); } } if (qpthreads_enabled) - qpt_thread_signal(cli_nexus->thread_id, SIGMQUEUE); + qpt_thread_signal(vty_cli_nexus->thread_id, SIGMQUEUE); } - UNLOCK + VTY_UNLOCK() ; } #endif -/* 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]; - - /* vty may not have been initialised */ - if (!vtyvec) - return; +/*============================================================================== + * Configuration node/state handling + * + * At most one VTY may hold the configuration symbol of power at any time. + */ - iov[0].iov_base = miyagi(buf) ; - iov[0].iov_len = len; - iov[1].iov_base = miyagi("\r\n") ; - iov[1].iov_len = 2; +/*------------------------------------------------------------------------------ + * 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) +{ + bool result; - 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); - } -} + VTY_LOCK() ; -int -vty_config_lock (struct vty *vty) -{ - int result; - LOCK if (vty_config == 0) { - vty->config = 1; - vty_config = 1; - } - result = vty->config; - UNLOCK - return result; -} + vty->vio->config = 1 ; + vty_config = 1 ; + } ; + + result = vty->vio->config; + + if (result) + vty->node = node ; + + VTY_UNLOCK() ; -int -vty_config_unlock (struct vty *vty) -{ - int result; - LOCK - result = uty_config_unlock(vty); - UNLOCK return result; } -static int -uty_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 +vty_config_unlock (struct vty *vty, enum node_type node) { - ASSERTLOCKED - if (vty_config == 1 && vty->config == 1) - { - vty->config = 0; - vty_config = 0; - } - return vty->config; + VTY_LOCK() ; + uty_config_unlock(vty, node); + VTY_UNLOCK() ; } -static void -vty_event (enum event event, int sock, 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 (cli_nexus) - vty_event_r(event, sock, vty); - else - vty_event_t(event, sock, vty); -} - -/* thread event setter */ -static void -vty_event_t (enum event event, int sock, struct vty *vty) - { - struct thread *vty_serv_thread; - - ASSERTLOCKED - - switch (event) + VTY_ASSERT_LOCKED() ; + if ((vty_config == 1) && (vty->vio->config == 1)) { - 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); - - /* 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; + vty->vio->config = 0; + vty_config = 0; } -} - -/* qpthreads event setter */ -static void -vty_event_r (enum event event, int sock, struct vty *vty) - { - qps_file accept_file = NULL; - - ASSERTLOCKED - - switch (event) - { - case VTY_SERV: - accept_file = vector_get_item(Vvty_serv_thread, sock); - if (accept_file == NULL) - { - accept_file = qps_file_init_new(accept_file, NULL); - qps_add_file(cli_nexus->selection, accept_file, sock, NULL); - vector_set_index(Vvty_serv_thread, sock, accept_file); - } - qps_enable_mode(accept_file, qps_read_mnum, vty_accept_r) ; - break; -#ifdef VTYSH - case VTYSH_SERV: - accept_file = vector_get_item(Vvty_serv_thread, sock); - if (accept_file == NULL) - { - accept_file = qps_file_init_new(accept_file, NULL); - qps_add_file(master, accept_file, sock, NULL); - vector_set_index(Vvty_serv_thread, sock, accept_file); - } - qps_enable_mode(accept_file, qps_read_mnum, vtysh_accept_r) ; - break; - case VTYSH_READ: - qps_enable_mode(vty->file, qps_read_mnum, vtysh_read_r) ; - break; - case VTYSH_WRITE: - qps_enable_mode(vty->file, qps_write_mnum, vtysh_write_r) ; - break; -#endif /* VTYSH */ - case VTY_READ: - qps_enable_mode(vty->qf, qps_read_mnum, vty_read_r) ; - - /* Time out treatment. */ - if (vty->v_timeout) - { - qtimer_set(vty->qtr, qt_add_monotonic(QTIME(vty->v_timeout)), NULL) ; - } - break; - case VTY_WRITE: - qps_enable_mode(vty->qf, qps_write_mnum, vty_flush_r) ; - break; - case VTY_TIMEOUT_RESET: - if (vty->qtr == NULL) - break; - if (vty->v_timeout) - { - qtimer_set(vty->qtr, qt_add_monotonic(QTIME(vty->v_timeout)), NULL) ; - } - else - { - qtimer_unset(vty->qtr); - } - break; - } -} + assert(node <= MAX_NON_CONFIG_NODE) ; + vty->node = node ; +} ; +/*============================================================================== + * 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 ; - LOCK - for (i = 0; i < vector_active (vtyvec); i++) - if ((v = vector_slot (vtyvec, i)) != NULL) + VTY_LOCK() ; + + vio = vio_list_base ; + while (vio != NULL) /* TODO: show only VTY_TERM ??? */ + { uty_out (vty, "%svty[%d] connected from %s.%s", - v->config ? "*" : " ", - i, v->address, VTY_NEWLINE); - UNLOCK + vio->config ? "*" : " ", + i, uty_get_name(vio), VTY_NEWLINE); + vio = sdl_next(vio, vio_list) ; + } ; + VTY_UNLOCK() ; return CMD_SUCCESS; } @@ -3140,9 +1172,9 @@ DEFUN_CALL (line_vty, "Configure a terminal line\n" "Virtual terminal\n") { - LOCK + VTY_LOCK() ; vty->node = VTY_NODE; - UNLOCK + VTY_UNLOCK() ; return CMD_SUCCESS; } @@ -3152,7 +1184,7 @@ exec_timeout (struct vty *vty, const char *min_str, const char *sec_str) { unsigned long timeout = 0; - LOCK + VTY_LOCK() ; /* min_str and sec_str are already checked by parser. So it must be all digit string. */ @@ -3165,10 +1197,10 @@ 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); + vty->vio->file.v_timeout = timeout; +// vty_event (VTY_TIMEOUT_RESET, 0, vty); - UNLOCK + VTY_UNLOCK() ; return CMD_SUCCESS; } @@ -3207,14 +1239,14 @@ DEFUN_CALL (vty_access_class, "Filter connections based on an IP access list\n" "IP access list\n") { - LOCK + VTY_LOCK() ; if (vty_accesslist_name) XFREE(MTYPE_VTY, vty_accesslist_name); vty_accesslist_name = XSTRDUP(MTYPE_VTY, argv[0]); - UNLOCK + VTY_UNLOCK() ; return CMD_SUCCESS; } @@ -3228,7 +1260,7 @@ DEFUN_CALL (no_vty_access_class, { int result = CMD_SUCCESS; - LOCK + VTY_LOCK() ; if (! vty_accesslist_name || (argc && strcmp(vty_accesslist_name, argv[0]))) { uty_out (vty, "Access-class is not currently applied to vty%s", @@ -3241,7 +1273,7 @@ DEFUN_CALL (no_vty_access_class, vty_accesslist_name = NULL; } - UNLOCK + VTY_UNLOCK() ; return result; } @@ -3254,13 +1286,13 @@ DEFUN_CALL (vty_ipv6_access_class, "Filter connections based on an IP access list\n" "IPv6 access list\n") { - LOCK + VTY_LOCK() ; if (vty_ipv6_accesslist_name) XFREE(MTYPE_VTY, vty_ipv6_accesslist_name); vty_ipv6_accesslist_name = XSTRDUP(MTYPE_VTY, argv[0]); - UNLOCK + VTY_UNLOCK() ; return CMD_SUCCESS; } @@ -3275,7 +1307,7 @@ DEFUN_CALL (no_vty_ipv6_access_class, { int result = CMD_SUCCESS; - LOCK + VTY_LOCK() ; if (! vty_ipv6_accesslist_name || (argc && strcmp(vty_ipv6_accesslist_name, argv[0]))) @@ -3291,7 +1323,7 @@ DEFUN_CALL (no_vty_ipv6_access_class, vty_ipv6_accesslist_name = NULL; } - UNLOCK + VTY_UNLOCK() ; return CMD_SUCCESS; } #endif /* HAVE_IPV6 */ @@ -3302,9 +1334,9 @@ DEFUN_CALL (vty_login, "login", "Enable password checking\n") { - LOCK + VTY_LOCK() ; no_password_check = 0; - UNLOCK + VTY_UNLOCK() ; return CMD_SUCCESS; } @@ -3314,9 +1346,9 @@ DEFUN_CALL (no_vty_login, NO_STR "Enable password checking\n") { - LOCK + VTY_LOCK() ; no_password_check = 1; - UNLOCK + VTY_UNLOCK() ; return CMD_SUCCESS; } @@ -3326,9 +1358,9 @@ DEFUN_CALL (vty_restricted_mode, "anonymous restricted", "Restrict view commands available in anonymous, unauthenticated vty\n") { - LOCK + VTY_LOCK() ; restricted_mode = 1; - UNLOCK + VTY_UNLOCK() ; return CMD_SUCCESS; } @@ -3338,9 +1370,9 @@ DEFUN_CALL (vty_no_restricted_mode, NO_STR "Enable password checking\n") { - LOCK + VTY_LOCK() ; restricted_mode = 0; - UNLOCK + VTY_UNLOCK() ; return CMD_SUCCESS; } @@ -3350,9 +1382,9 @@ DEFUN_CALL (service_advanced_vty, "Set up miscellaneous service\n" "Enable advanced mode vty interface\n") { - LOCK + VTY_LOCK() ; host.advanced = 1; - UNLOCK + VTY_UNLOCK() ; return CMD_SUCCESS; } @@ -3363,9 +1395,9 @@ DEFUN_CALL (no_service_advanced_vty, "Set up miscellaneous service\n" "Enable advanced mode vty interface\n") { - LOCK + VTY_LOCK() ; host.advanced = 0; - UNLOCK + VTY_UNLOCK() ; return CMD_SUCCESS; } @@ -3375,9 +1407,9 @@ DEFUN_CALL (terminal_monitor, "Set terminal line parameters\n" "Copy debug output to the current terminal line\n") { - LOCK - vty->monitor = 1; - UNLOCK + VTY_LOCK() ; + uty_set_monitor(vty->vio, true); + VTY_UNLOCK() ; return CMD_SUCCESS; } @@ -3388,9 +1420,9 @@ DEFUN_CALL (terminal_no_monitor, NO_STR "Copy debug output to the current terminal line\n") { - LOCK - vty->monitor = 0; - UNLOCK + VTY_LOCK() ; + uty_set_monitor(vty->vio, false); + VTY_UNLOCK() ; return CMD_SUCCESS; } @@ -3409,27 +1441,34 @@ DEFUN_CALL (show_history, { int index; - LOCK + VTY_LOCK() ; - for (index = vty->hindex + 1; index != vty->hindex;) + for (index = vty->vio->hindex + 1; index != vty->vio->hindex;) { + const char* line ; + if (index == VTY_MAXHIST) { index = 0; continue; } - if (vty->hist[index] != NULL) - uty_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, VTY_NEWLINE); index++; } - UNLOCK + VTY_UNLOCK() ; return CMD_SUCCESS; } -/* Display current configuration. */ +/*============================================================================== + * Output the current configuration + * + * Returns: CMD_SUCCESS + */ static int vty_config_write (struct vty *vty) { @@ -3466,76 +1505,16 @@ vty_config_write (struct vty *vty) return CMD_SUCCESS; } -struct cmd_node vty_node = -{ - VTY_NODE, - "%s(config-line)# ", - 1, -}; - -/* Reset all VTY status. */ -void -vty_reset () -{ - LOCK - uty_reset(); - UNLOCK -} - -void -uty_reset () -{ - unsigned int i; - struct vty *vty; - struct thread *vty_serv_thread; - qps_file qf; - - for (i = 0; i < vector_active (vtyvec); i++) - if ((vty = vector_slot (vtyvec, i)) != NULL) - { - buffer_reset (vty->obuf); - vty->status = VTY_CLOSE; - uty_close (vty); - } - - if (cli_nexus) - { - for (i = 0; i < vector_active (Vvty_serv_thread); i++) - if ((qf = vector_slot (Vvty_serv_thread, i)) != NULL) - { - qps_remove_file(qf); - qps_file_free(qf); - vector_slot (Vvty_serv_thread, i) = NULL; - close (i); - } - } - else - { - assert(master); - 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) { @@ -3550,140 +1529,107 @@ 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; } +/*============================================================================== + * Access functions for VTY values, where locking is or might be required. + */ + int vty_shell (struct vty *vty) { - LOCK + VTY_LOCK() ; int result; - result = uty_shell (vty); - UNLOCK + result = (vty->vio->type == VTY_SHELL) ? 1 : 0 ; + VTY_UNLOCK() ; return result; } -static int -uty_shell (struct vty *vty) -{ - return ((vty == NULL) || (vty->type == VTY_SHELL)) ? 1 : 0; -} - int vty_shell_serv (struct vty *vty) { - LOCK + VTY_LOCK() ; int result; - result = ((vty->type == VTY_SHELL_SERV) ? 1 : 0); - UNLOCK + result = ((vty->vio->type == VTY_SHELL_SERV) ? 1 : 0); + VTY_UNLOCK() ; return result; } -void -vty_init_vtysh () -{ - LOCK - vtyvec = vector_init (0); - UNLOCK -} - int vty_get_node(struct vty *vty) { int result; - LOCK + VTY_LOCK() ; result = vty->node; - UNLOCK + VTY_UNLOCK() ; return result; } void vty_set_node(struct vty *vty, int node) { - LOCK + VTY_LOCK() ; vty->node = node; - UNLOCK + VTY_UNLOCK() ; } int vty_get_type(struct vty *vty) { int result; - LOCK - result = vty->type; - UNLOCK + VTY_LOCK() ; + result = vty->vio->type; + VTY_UNLOCK() ; return result; } int -vty_get_status(struct vty *vty) -{ - int result; - LOCK - result = vty->status; - UNLOCK - return result; -} - -void -vty_set_status(struct vty *vty, int status) -{ - LOCK - vty->status = status; - UNLOCK -} - -int vty_get_lines(struct vty *vty) { int result; - LOCK - result = vty->lines; - UNLOCK + VTY_LOCK() ; + result = vty->vio->lines; + VTY_UNLOCK() ; return result; } void vty_set_lines(struct vty *vty, int lines) { - LOCK - vty->lines = lines; - UNLOCK + VTY_LOCK() ; + vty->vio->lines = lines; + VTY_UNLOCK() ; } -/* qpthreads: Install vty's own commands like `who' command. */ -void -vty_init_r (qpn_nexus cli_n, qpn_nexus routing_n) -{ - cli_nexus = cli_n; - routing_nexus = routing_n; - qpt_mutex_init(&vty_mutex, qpt_mutex_recursive); -} +/*============================================================================== + * The VTY command nodes + */ -/* threads: Install vty's own commands like `who' command. */ -void -vty_init (struct thread_master *master_thread) +struct cmd_node vty_node = { - LOCK - - /* For further configuration read, preserve current directory. */ - vty_save_cwd (); - - vtyvec = vector_init (0); - - master = master_thread; + VTY_NODE, + "%s(config-line)# ", + 1, +}; - /* Initilize server thread vector. */ - Vvty_serv_thread = vector_init (0); +/*------------------------------------------------------------------------------ + * 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 (RESTRICTED_NODE, &config_who_cmd); @@ -3714,29 +1660,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 */ - - UNLOCK -} - -void -vty_terminate (void) -{ - LOCK - - if (vty_cwd) - XFREE (MTYPE_TMP, vty_cwd); - - if (vtyvec && Vvty_serv_thread) - { - uty_reset (); - vector_free (vtyvec); - vector_free (Vvty_serv_thread); - } - UNLOCK - - qpt_mutex_destroy(&vty_mutex, 0); -} - -#undef LOCK -#undef UNLOCK -#undef ASSERTLOCKED +} ; |