summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Hall <chris.hall@highwayman.com>2011-02-13 23:11:45 +0000
committerChris Hall <chris.hall@highwayman.com>2011-02-13 23:11:45 +0000
commit5cae7eea451f2b7d65b5892e2c1dafc70f8b836e (patch)
tree0fbd9679e9ae28e7d061b5bdda08756077415ecb
parent64be6d766a65dc0749d17f5023d714678e9c96a6 (diff)
downloadquagga-5cae7eea451f2b7d65b5892e2c1dafc70f8b836e.tar.bz2
quagga-5cae7eea451f2b7d65b5892e2c1dafc70f8b836e.tar.xz
Second tranche of updates for pipework branch.
modified: bgpd/bgp_connection.c modified: bgpd/bgp_debug.c modified: bgpd/bgp_engine.h modified: bgpd/bgp_main.c modified: bgpd/bgp_packet.c modified: bgpd/bgp_peer.c modified: bgpd/bgp_route.c modified: bgpd/bgp_routemap.c modified: bgpd/bgp_session.c modified: bgpd/bgp_vty.c modified: bgpd/bgpd.c modified: bgpd/bgpd.h modified: configure.ac modified: isisd/dict.h modified: isisd/isis_misc.c modified: isisd/isis_routemap.c modified: isisd/isis_spf.c modified: lib/Makefile.am modified: lib/command.c modified: lib/command.h modified: lib/command_execute.h modified: lib/command_parse.c modified: lib/command_parse.h modified: lib/command_queue.c modified: lib/command_queue.h modified: lib/elstring.h modified: lib/heap.c modified: lib/if.c modified: lib/if.h modified: lib/keychain.c modified: lib/keystroke.c modified: lib/keystroke.h modified: lib/list_util.c modified: lib/list_util.h modified: lib/log.c modified: lib/log.h modified: lib/memory.c modified: lib/memory.h modified: lib/memtypes.c modified: lib/misc.h modified: lib/mqueue.c modified: lib/mqueue.h deleted: lib/node_type.h modified: lib/pthread_safe.c modified: lib/qfstring.c modified: lib/qiovec.c modified: lib/qiovec.h modified: lib/qpath.c modified: lib/qpnexus.c modified: lib/qpnexus.h modified: lib/qpselect.c modified: lib/qpthreads.h modified: lib/qstring.c modified: lib/qstring.h modified: lib/qtime.c modified: lib/qtime.h modified: lib/qtimers.c modified: lib/qtimers.h modified: lib/routemap.c modified: lib/symtab.h modified: lib/thread.h deleted: lib/uty.h modified: lib/vector.c modified: lib/vector.h modified: lib/version.h.in modified: lib/vio_fifo.c modified: lib/vio_fifo.h modified: lib/vio_lines.c modified: lib/vio_lines.h modified: lib/vty.c modified: lib/vty.h modified: lib/vty_cli.c modified: lib/vty_cli.h modified: lib/vty_io.c modified: lib/vty_io.h modified: lib/vty_io_basic.c modified: lib/vty_io_basic.h modified: lib/vty_io_file.c modified: lib/vty_io_file.h modified: lib/vty_io_shell.c modified: lib/vty_io_term.c modified: lib/vty_io_term.h modified: lib/vty_local.h modified: lib/vty_pipe.c modified: lib/workqueue.h modified: lib/zebra.h modified: ospf6d/ospf6_lsa.c modified: ripngd/ripngd.c modified: tests/test-list_util.c modified: tests/test-vector.c modified: vtysh/vtysh.c modified: vtysh/vtysh_config.c
-rw-r--r--bgpd/bgp_connection.c3
-rw-r--r--bgpd/bgp_debug.c38
-rw-r--r--bgpd/bgp_engine.h30
-rw-r--r--bgpd/bgp_main.c27
-rw-r--r--bgpd/bgp_packet.c5
-rw-r--r--bgpd/bgp_peer.c2
-rw-r--r--bgpd/bgp_route.c2
-rw-r--r--bgpd/bgp_routemap.c18
-rw-r--r--bgpd/bgp_session.c4
-rw-r--r--bgpd/bgp_vty.c77
-rw-r--r--bgpd/bgpd.c3
-rw-r--r--bgpd/bgpd.h4
-rwxr-xr-xconfigure.ac2
-rw-r--r--isisd/dict.h1
-rw-r--r--isisd/isis_misc.c37
-rw-r--r--isisd/isis_routemap.c21
-rw-r--r--isisd/isis_spf.c40
-rw-r--r--lib/Makefile.am19
-rw-r--r--lib/command.c2919
-rw-r--r--lib/command.h263
-rw-r--r--lib/command_execute.h135
-rw-r--r--lib/command_parse.c4260
-rw-r--r--lib/command_parse.h806
-rw-r--r--lib/command_queue.c580
-rw-r--r--lib/command_queue.h8
-rw-r--r--lib/elstring.h259
-rw-r--r--lib/heap.c4
-rw-r--r--lib/if.c2
-rw-r--r--lib/if.h14
-rw-r--r--lib/keychain.c22
-rw-r--r--lib/keystroke.c41
-rw-r--r--lib/keystroke.h6
-rw-r--r--lib/list_util.c12
-rw-r--r--lib/list_util.h10
-rw-r--r--lib/log.c3
-rw-r--r--lib/log.h8
-rw-r--r--lib/memory.c26
-rw-r--r--lib/memory.h16
-rw-r--r--lib/memtypes.c7
-rw-r--r--lib/misc.h28
-rw-r--r--lib/mqueue.c79
-rw-r--r--lib/mqueue.h101
-rw-r--r--lib/node_type.h82
-rw-r--r--lib/pthread_safe.c3
-rw-r--r--lib/qfstring.c20
-rw-r--r--lib/qiovec.c18
-rw-r--r--lib/qiovec.h3
-rw-r--r--lib/qpath.c6
-rw-r--r--lib/qpnexus.c14
-rw-r--r--lib/qpnexus.h5
-rw-r--r--lib/qpselect.c25
-rw-r--r--lib/qpthreads.h24
-rw-r--r--lib/qstring.c861
-rw-r--r--lib/qstring.h592
-rw-r--r--lib/qtime.c48
-rw-r--r--lib/qtime.h137
-rw-r--r--lib/qtimers.c18
-rw-r--r--lib/qtimers.h16
-rw-r--r--lib/routemap.c2
-rw-r--r--lib/symtab.h2
-rw-r--r--lib/thread.h2
-rw-r--r--lib/uty.h232
-rw-r--r--lib/vector.c10
-rw-r--r--lib/vector.h14
-rw-r--r--lib/version.h.in2
-rw-r--r--lib/vio_fifo.c1823
-rw-r--r--lib/vio_fifo.h196
-rw-r--r--lib/vio_lines.c2
-rw-r--r--lib/vio_lines.h18
-rw-r--r--lib/vty.c1078
-rw-r--r--lib/vty.h152
-rw-r--r--lib/vty_cli.c2542
-rw-r--r--lib/vty_cli.h166
-rw-r--r--lib/vty_io.c1196
-rw-r--r--lib/vty_io.h485
-rw-r--r--lib/vty_io_basic.c387
-rw-r--r--lib/vty_io_basic.h65
-rw-r--r--lib/vty_io_file.c2650
-rw-r--r--lib/vty_io_file.h25
-rw-r--r--lib/vty_io_shell.c5
-rw-r--r--lib/vty_io_term.c987
-rw-r--r--lib/vty_io_term.h15
-rw-r--r--lib/vty_local.h25
-rw-r--r--lib/vty_pipe.c59
-rw-r--r--lib/workqueue.h2
-rw-r--r--lib/zebra.h4
-rw-r--r--ospf6d/ospf6_lsa.c8
-rw-r--r--ripngd/ripngd.c178
-rw-r--r--tests/test-list_util.c25
-rw-r--r--tests/test-vector.c4
-rw-r--r--vtysh/vtysh.c18
-rw-r--r--vtysh/vtysh_config.c19
92 files changed, 12405 insertions, 11807 deletions
diff --git a/bgpd/bgp_connection.c b/bgpd/bgp_connection.c
index 0bdd4f3d..151d3309 100644
--- a/bgpd/bgp_connection.c
+++ b/bgpd/bgp_connection.c
@@ -22,6 +22,7 @@
#include "bgpd/bgp_connection.h"
#include <zebra.h>
+#include "misc.h"
#include "bgpd/bgpd.h"
#include "bgpd/bgp_fsm.h"
@@ -685,7 +686,7 @@ bgp_connection_stop(bgp_connection connection, bool stop_writer)
connection->notification_pending = 0 ;
/* Empty out the pending queue and remove from connection queue */
- mqueue_local_reset_keep(&connection->pending_queue) ;
+ mqueue_local_reset(&connection->pending_queue, keep_it) ;
bgp_connection_queue_del(connection) ;
/* If required: set write buffer *unwritable* (and empty). */
diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c
index c4cf9cf6..f7494f19 100644
--- a/bgpd/bgp_debug.c
+++ b/bgpd/bgp_debug.c
@@ -367,7 +367,7 @@ DEFUN (debug_bgp_as4,
BGP_STR
"BGP AS4 actions\n")
{
- if (vty_get_node(vty) == CONFIG_NODE)
+ if (vty->node == CONFIG_NODE)
DEBUG_ON (as4, AS4);
else
{
@@ -385,7 +385,7 @@ DEFUN (no_debug_bgp_as4,
BGP_STR
"BGP AS4 actions\n")
{
- if (vty_get_node(vty) == CONFIG_NODE)
+ if (vty->node == CONFIG_NODE)
DEBUG_OFF (as4, AS4);
else
{
@@ -410,7 +410,7 @@ DEFUN (debug_bgp_as4_segment,
BGP_STR
"BGP AS4 aspath segment handling\n")
{
- if (vty_get_node(vty) == CONFIG_NODE)
+ if (vty->node == CONFIG_NODE)
DEBUG_ON (as4, AS4_SEGMENT);
else
{
@@ -428,7 +428,7 @@ DEFUN (no_debug_bgp_as4_segment,
BGP_STR
"BGP AS4 aspath segment handling\n")
{
- if (vty_get_node(vty) == CONFIG_NODE)
+ if (vty->node == CONFIG_NODE)
DEBUG_OFF (as4, AS4_SEGMENT);
else
{
@@ -453,7 +453,7 @@ DEFUN (debug_bgp_fsm,
BGP_STR
"BGP Finite State Machine\n")
{
- if (vty_get_node(vty) == CONFIG_NODE)
+ if (vty->node == CONFIG_NODE)
DEBUG_ON (fsm, FSM);
else
{
@@ -471,7 +471,7 @@ DEFUN (no_debug_bgp_fsm,
BGP_STR
"Finite State Machine\n")
{
- if (vty_get_node(vty) == CONFIG_NODE)
+ if (vty->node == CONFIG_NODE)
DEBUG_OFF (fsm, FSM);
else
{
@@ -496,7 +496,7 @@ DEFUN (debug_bgp_events,
BGP_STR
"BGP events\n")
{
- if (vty_get_node(vty) == CONFIG_NODE)
+ if (vty->node == CONFIG_NODE)
DEBUG_ON (events, EVENTS);
else
{
@@ -514,7 +514,7 @@ DEFUN (no_debug_bgp_events,
BGP_STR
"BGP events\n")
{
- if (vty_get_node(vty) == CONFIG_NODE)
+ if (vty->node == CONFIG_NODE)
DEBUG_OFF (events, EVENTS);
else
{
@@ -538,7 +538,7 @@ DEFUN (debug_bgp_filter,
BGP_STR
"BGP filters\n")
{
- if (vty_get_node(vty) == CONFIG_NODE)
+ if (vty->node == CONFIG_NODE)
DEBUG_ON (filter, FILTER);
else
{
@@ -556,7 +556,7 @@ DEFUN (no_debug_bgp_filter,
BGP_STR
"BGP filters\n")
{
- if (vty_get_node(vty) == CONFIG_NODE)
+ if (vty->node == CONFIG_NODE)
DEBUG_OFF (filter, FILTER);
else
{
@@ -580,7 +580,7 @@ DEFUN (debug_bgp_keepalive,
BGP_STR
"BGP keepalives\n")
{
- if (vty_get_node(vty) == CONFIG_NODE)
+ if (vty->node == CONFIG_NODE)
DEBUG_ON (keepalive, KEEPALIVE);
else
{
@@ -598,7 +598,7 @@ DEFUN (no_debug_bgp_keepalive,
BGP_STR
"BGP keepalives\n")
{
- if (vty_get_node(vty) == CONFIG_NODE)
+ if (vty->node == CONFIG_NODE)
DEBUG_OFF (keepalive, KEEPALIVE);
else
{
@@ -622,7 +622,7 @@ DEFUN (debug_bgp_update,
BGP_STR
"BGP updates\n")
{
- if (vty_get_node(vty) == CONFIG_NODE)
+ if (vty->node == CONFIG_NODE)
{
DEBUG_ON (update, UPDATE_IN);
DEBUG_ON (update, UPDATE_OUT);
@@ -645,7 +645,7 @@ DEFUN (debug_bgp_update_direct,
"Inbound updates\n"
"Outbound updates\n")
{
- if (vty_get_node(vty) == CONFIG_NODE)
+ if (vty->node == CONFIG_NODE)
{
if (strncmp ("i", argv[0], 1) == 0)
{
@@ -684,7 +684,7 @@ DEFUN (no_debug_bgp_update,
BGP_STR
"BGP updates\n")
{
- if (vty_get_node(vty) == CONFIG_NODE)
+ if (vty->node == CONFIG_NODE)
{
DEBUG_OFF (update, UPDATE_IN);
DEBUG_OFF (update, UPDATE_OUT);
@@ -711,7 +711,7 @@ DEFUN (debug_bgp_normal,
DEBUG_STR
BGP_STR)
{
- if (vty_get_node(vty) == CONFIG_NODE)
+ if (vty->node == CONFIG_NODE)
DEBUG_ON (normal, NORMAL);
else
{
@@ -728,7 +728,7 @@ DEFUN (no_debug_bgp_normal,
DEBUG_STR
BGP_STR)
{
- if (vty_get_node(vty) == CONFIG_NODE)
+ if (vty->node == CONFIG_NODE)
DEBUG_OFF (normal, NORMAL);
else
{
@@ -751,7 +751,7 @@ DEFUN (debug_bgp_zebra,
BGP_STR
"BGP Zebra messages\n")
{
- if (vty_get_node(vty) == CONFIG_NODE)
+ if (vty->node == CONFIG_NODE)
DEBUG_ON (zebra, ZEBRA);
else
{
@@ -769,7 +769,7 @@ DEFUN (no_debug_bgp_zebra,
BGP_STR
"BGP Zebra messages\n")
{
- if (vty_get_node(vty) == CONFIG_NODE)
+ if (vty->node == CONFIG_NODE)
DEBUG_OFF (zebra, ZEBRA);
else
{
diff --git a/bgpd/bgp_engine.h b/bgpd/bgp_engine.h
index 7e3cceec..bedfc18e 100644
--- a/bgpd/bgp_engine.h
+++ b/bgpd/bgp_engine.h
@@ -30,13 +30,25 @@
#include "lib/qpnexus.h"
#include "lib/log.h"
-enum { qdebug =
-#ifdef QDEBUG
- 1
+/*==============================================================================
+ * DEBUG setting
+ */
+
+#ifdef BGP_ENGINE_DEBUG /* Can be forced from outside */
+# if BGP_ENGINE_DEBUG
+# define BGP_ENGINE_DEBUG 1 /* Force 1 or 0 */
#else
- 0
+# define BGP_ENGINE_DEBUG 0
+# endif
+#else
+# ifdef QDEBUG
+# define BGP_ENGINE_DEBUG 1 /* Follow QDEBUG */
+#else
+# define BGP_ENGINE_DEBUG 0
+# endif
#endif
-};
+
+enum { bgp_engine_debug = BGP_ENGINE_DEBUG } ;
/*==============================================================================
*
@@ -117,10 +129,10 @@ bgp_queue_logging(const char* name, mqueue_queue mq, struct queue_stats* stats)
/* Send given message to the BGP Engine -- priority/ordinary
*/
Inline void
-bgp_to_bgp_engine(mqueue_block mqb, enum mqb_rank priority)
+bgp_to_bgp_engine(mqueue_block mqb, mqb_rank_b priority)
{
mqueue_enqueue(bgp_nexus->queue, mqb, priority) ;
- if (qdebug)
+ if (bgp_engine_debug)
bgp_queue_logging("BGP Engine", bgp_nexus->queue, &bgp_engine_queue_stats) ;
} ;
@@ -131,10 +143,10 @@ bgp_to_bgp_engine(mqueue_block mqb, enum mqb_rank priority)
/* Send given message to the Routing Engine -- priority/ordinary
*/
Inline void
-bgp_to_routing_engine(mqueue_block mqb, enum mqb_rank priority)
+bgp_to_routing_engine(mqueue_block mqb, mqb_rank_b priority)
{
mqueue_enqueue(routing_nexus->queue, mqb, priority) ;
- if (qdebug)
+ if (bgp_engine_debug)
bgp_queue_logging("Routing Engine", routing_nexus->queue,
&routing_engine_queue_stats) ;
} ;
diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c
index 60b66533..6bbc8197 100644
--- a/bgpd/bgp_main.c
+++ b/bgpd/bgp_main.c
@@ -240,14 +240,17 @@ sighup (void)
}
-/* SIGINT handler. */
+/* SIGINT and SIGTERM handler. */
void
sigint (void)
{
zlog_notice ("Terminating on signal");
+ vty_reset_because("Terminating");
+
/* tell the routing engine to send notifies to peers and wait
- * for all sessions to be disabled */
+ * for all sessions to be disabled, then terminate.
+ */
sigterm_enqueue();
}
@@ -301,6 +304,7 @@ bgp_exit (int status)
/* reverse bgp_zebra_init/if_init */
if (retain_mode)
if_add_hook (IF_DELETE_HOOK, NULL);
+
for (ALL_LIST_ELEMENTS (iflist, node, nnode, ifp))
{
struct listnode *c_node, *c_nnode;
@@ -366,10 +370,10 @@ bgp_exit (int status)
if (qpthreads_enabled)
{
- qpn_reset_free(routing_nexus);
- qpn_reset_free(bgp_nexus);
+ qpn_reset(routing_nexus, free_it);
+ qpn_reset(bgp_nexus, free_it);
} ;
- cli_nexus = qpn_reset_free(cli_nexus);
+ cli_nexus = qpn_reset(cli_nexus, free_it);
if (CONF_BGP_DEBUG (normal, NORMAL))
log_memstats_stderr ("bgpd");
@@ -744,16 +748,17 @@ sighup_action(mqueue_block mqb, mqb_flag_t flag)
{
zlog_info ("bgpd restarting!");
- bgp_terminate (0, 0); /* send notifies */
+ bgp_terminate (false, false); /* send notifies */
bgp_reset ();
- /* Reload config file. */
- vty_read_config (config_file, config_default);
+ /* Reload config file -- no special first command, now */
+ vty_read_config_first_cmd_special(config_file, config_default,
+ NULL, config_ignore_warnings) ;
- /* Create VTY's socket */
+ /* Create VTY's socket */
vty_restart(vty_addr, vty_port, BGP_VTYSH_PATH);
- /* Try to return to normal operation. */
+ /* Try to return to normal operation. */
}
mqb_free(mqb);
@@ -807,7 +812,7 @@ sigterm_action(mqueue_block mqb, mqb_flag_t flag)
*/
program_terminating = true ;
- bgp_terminate(1, retain_mode);
+ bgp_terminate(true, retain_mode);
qpn_add_hook_function(&routing_nexus->foreground,
program_terminate_if_all_peers_deleted) ;
diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c
index 9a5aaf30..54a47b06 100644
--- a/bgpd/bgp_packet.c
+++ b/bgpd/bgp_packet.c
@@ -173,9 +173,10 @@ bgp_update_packet (struct peer *peer, afi_t afi, safi_t safi)
if (rn->prn)
prd = (struct prefix_rd *) &rn->prn->p;
- if (binfo && binfo->extra)
+ if (binfo)
{
- tag = binfo->extra->tag;
+ if (binfo->extra)
+ tag = binfo->extra->tag;
from = binfo->peer;
}
diff --git a/bgpd/bgp_peer.c b/bgpd/bgp_peer.c
index 3a1eb71f..59c9a8da 100644
--- a/bgpd/bgp_peer.c
+++ b/bgpd/bgp_peer.c
@@ -466,7 +466,7 @@ bgp_session_has_disabled(bgp_session session)
session->state = bgp_session_sDisabled ;
/* Immediately discard any other messages for this session. */
- mqueue_revoke(routing_nexus->queue, session) ;
+ mqueue_revoke(routing_nexus->queue, session, 0) ;
/* If the session is marked "delete_me", do that.
*
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index 4d69badb..c9281ca9 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -5033,7 +5033,7 @@ DEFUN (bgp_network_mask_natural_backdoor,
ALIAS (bgp_network_mask_natural_backdoor,
bgp_network_mask_natural_backdoor_ttl_cmd,
- "network A.B.C.D backdoor pathlimit (1-255>",
+ "network A.B.C.D backdoor pathlimit <1-255>",
"Specify a network to announce via BGP\n"
"Network number\n"
"Specify a BGP backdoor route\n"
diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c
index f40d057d..ebc37e0d 100644
--- a/bgpd/bgp_routemap.c
+++ b/bgpd/bgp_routemap.c
@@ -3065,7 +3065,7 @@ DEFUN (set_metric,
ALIAS (set_metric,
set_metric_addsub_cmd,
- "set metric <+/-metric>",
+ "set metric <+/-32b>",
SET_STR
"Metric value for destination routing protocol\n"
"Add or subtract metric\n")
@@ -3155,11 +3155,11 @@ ALIAS (no_set_weight,
DEFUN (set_aspath_prepend,
set_aspath_prepend_cmd,
- "set as-path prepend ." CMD_AS_RANGE,
+ "set as-path prepend .ASs",
SET_STR
"Transform BGP AS_PATH attribute\n"
"Prepend to the as-path\n"
- "AS number\n")
+ "AS number(s)\n")
{
int ret;
char *str;
@@ -3193,20 +3193,20 @@ DEFUN (no_set_aspath_prepend,
ALIAS (no_set_aspath_prepend,
no_set_aspath_prepend_val_cmd,
- "no set as-path prepend ." CMD_AS_RANGE,
+ "no set as-path prepend .ASs",
NO_STR
SET_STR
"Transform BGP AS_PATH attribute\n"
"Prepend to the as-path\n"
- "AS number\n")
+ "AS number(s)\n")
DEFUN (set_aspath_exclude,
set_aspath_exclude_cmd,
- "set as-path exclude ." CMD_AS_RANGE,
+ "set as-path exclude .ASs",
SET_STR
"Transform BGP AS-path attribute\n"
"Exclude from the as-path\n"
- "AS number\n")
+ "AS number(s)\n")
{
int ret;
char *str;
@@ -3239,12 +3239,12 @@ DEFUN (no_set_aspath_exclude,
ALIAS (no_set_aspath_exclude,
no_set_aspath_exclude_val_cmd,
- "no set as-path exclude ." CMD_AS_RANGE,
+ "no set as-path exclude .ASs",
NO_STR
SET_STR
"Transform BGP AS_PATH attribute\n"
"Exclude from the as-path\n"
- "AS number\n")
+ "AS number(s)\n")
DEFUN (set_community,
set_community_cmd,
diff --git a/bgpd/bgp_session.c b/bgpd/bgp_session.c
index a734ba99..681075da 100644
--- a/bgpd/bgp_session.c
+++ b/bgpd/bgp_session.c
@@ -461,7 +461,7 @@ bgp_session_disable(bgp_peer peer, bgp_notify notification)
/* Can revoke whatever may be queued already. Will revoke again when the
* disable is acknowledged to finally clear the session out of the queue.
*/
- mqueue_revoke(routing_nexus->queue, session) ;
+ mqueue_revoke(routing_nexus->queue, session, 0) ;
/* Now change to limping state */
session->state = bgp_session_sLimping;
@@ -517,7 +517,7 @@ bgp_session_do_disable(mqueue_block mqb, mqb_flag_t flag)
if (flag == mqb_action)
{
/* Immediately discard any other messages for this session. */
- mqueue_revoke(bgp_nexus->queue, session) ;
+ mqueue_revoke(bgp_nexus->queue, session, 0) ;
/* Get the FSM to send any notification and close connections */
bgp_fsm_disable_session(session, args->notification) ;
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index 23d39b85..9195b9f3 100644
--- a/bgpd/bgp_vty.c
+++ b/bgpd/bgp_vty.c
@@ -57,9 +57,7 @@ extern struct in_addr router_id_zebra;
afi_t
bgp_node_afi (struct vty *vty)
{
- enum node_type node = vty_get_node(vty) ;
-
- if (node == BGP_IPV6_NODE || node == BGP_IPV6M_NODE)
+ if (vty->node == BGP_IPV6_NODE || vty->node == BGP_IPV6M_NODE)
return AFI_IP6;
return AFI_IP;
}
@@ -69,11 +67,9 @@ bgp_node_afi (struct vty *vty)
safi_t
bgp_node_safi (struct vty *vty)
{
- enum node_type node = vty_get_node(vty) ;
-
- if (node == BGP_VPNV4_NODE)
+ if (vty->node == BGP_VPNV4_NODE)
return SAFI_MPLS_VPN;
- if (node == BGP_IPV4M_NODE || node == BGP_IPV6M_NODE)
+ if (vty->node == BGP_IPV4M_NODE || vty->node == BGP_IPV6M_NODE)
return SAFI_MULTICAST;
return SAFI_UNICAST;
}
@@ -352,7 +348,7 @@ DEFUN (router_bgp,
return CMD_WARNING;
}
- vty_set_node(vty, BGP_NODE) ;
+ vty->node = BGP_NODE ;
vty->index = bgp;
return CMD_SUCCESS;
@@ -601,7 +597,7 @@ ALIAS (no_bgp_confederation_identifier,
DEFUN (bgp_confederation_peers,
bgp_confederation_peers_cmd,
- "bgp confederation peers ." CMD_AS_RANGE,
+ "bgp confederation peers .ASs",
"BGP specific commands\n"
"AS confederation parameters\n"
"Peer ASs in BGP confederation\n"
@@ -631,7 +627,7 @@ DEFUN (bgp_confederation_peers,
DEFUN (no_bgp_confederation_peers,
no_bgp_confederation_peers_cmd,
- "no bgp confederation peers ." CMD_AS_RANGE,
+ "no bgp confederation peers .ASs",
NO_STR
"BGP specific commands\n"
"AS confederation parameters\n"
@@ -4018,7 +4014,7 @@ DEFUN (address_family_ipv4,
"Enter Address Family command mode\n"
"Address family\n")
{
- vty_set_node(vty, BGP_IPV4_NODE) ;
+ vty->node = BGP_IPV4_NODE ;
return CMD_SUCCESS;
}
@@ -4031,9 +4027,9 @@ DEFUN (address_family_ipv4_safi,
"Address Family modifier\n")
{
if (strncmp (argv[0], "m", 1) == 0)
- vty_set_node(vty, BGP_IPV4M_NODE) ;
+ vty->node = BGP_IPV4M_NODE ;
else
- vty_set_node(vty, BGP_IPV4_NODE) ;
+ vty->node = BGP_IPV4_NODE ;
return CMD_SUCCESS;
}
@@ -4044,7 +4040,7 @@ DEFUN (address_family_ipv6,
"Enter Address Family command mode\n"
"Address family\n")
{
- vty_set_node(vty, BGP_IPV6_NODE) ;
+ vty->node = BGP_IPV6_NODE ;
return CMD_SUCCESS;
}
@@ -4057,9 +4053,9 @@ DEFUN (address_family_ipv6_safi,
"Address Family modifier\n")
{
if (strncmp (argv[0], "m", 1) == 0)
- vty_set_node(vty, BGP_IPV6M_NODE) ;
+ vty->node = BGP_IPV6M_NODE ;
else
- vty_set_node(vty, BGP_IPV6_NODE) ;
+ vty->node = BGP_IPV6_NODE ;
return CMD_SUCCESS;
}
@@ -4070,7 +4066,7 @@ DEFUN (address_family_vpnv4,
"Enter Address Family command mode\n"
"Address family\n")
{
- vty_set_node(vty, BGP_VPNV4_NODE) ;
+ vty->node = BGP_VPNV4_NODE ;
return CMD_SUCCESS;
}
@@ -4086,14 +4082,14 @@ DEFUN (exit_address_family,
"exit-address-family",
"Exit from Address Family configuration mode\n")
{
- enum node_type node = vty_get_node(vty) ;
+ node_type_t node = vty->node ;
if (node == BGP_IPV4_NODE
|| node == BGP_IPV4M_NODE
|| node == BGP_VPNV4_NODE
|| node == BGP_IPV6_NODE
|| node == BGP_IPV6M_NODE)
- vty_set_node(vty, BGP_NODE);
+ vty->node = BGP_NODE ;
return CMD_SUCCESS;
}
@@ -8869,37 +8865,50 @@ bgp_config_write_redistribute (struct vty *vty, struct bgp *bgp, afi_t afi,
/* BGP node structure. */
static struct cmd_node bgp_node =
{
- BGP_NODE,
- "%s(config-router)# ",
- 1,
+ .node = BGP_NODE,
+ .prompt = "%s(config-router)# ",
+
+ .config_to_vtysh = true,
};
static struct cmd_node bgp_ipv4_unicast_node =
{
- BGP_IPV4_NODE,
- "%s(config-router-af)# ",
- 1,
+ .node = BGP_IPV4_NODE,
+ .prompt = "%s(config-router-af)# ",
+
+ .parent = BGP_NODE,
+
+ .config_to_vtysh = true,
};
static struct cmd_node bgp_ipv4_multicast_node =
{
- BGP_IPV4M_NODE,
- "%s(config-router-af)# ",
- 1,
+ .node = BGP_IPV4M_NODE,
+ .prompt = "%s(config-router-af)# ",
+
+ .parent = BGP_NODE,
+
+ .config_to_vtysh = true,
};
static struct cmd_node bgp_ipv6_unicast_node =
{
- BGP_IPV6_NODE,
- "%s(config-router-af)# ",
- 1,
+ .node = BGP_IPV6_NODE,
+ .prompt = "%s(config-router-af)# ",
+
+ .parent = BGP_NODE,
+
+ .config_to_vtysh = true,
};
static struct cmd_node bgp_ipv6_multicast_node =
{
- BGP_IPV6M_NODE,
- "%s(config-router-af)# ",
- 1,
+ .node = BGP_IPV6M_NODE,
+ .prompt = "%s(config-router-af)# ",
+
+ .parent = BGP_NODE,
+
+ .config_to_vtysh = true,
};
static struct cmd_node bgp_vpnv4_node =
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index 79aad2cb..c2d69bd2 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -19,6 +19,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA. */
#include <zebra.h>
+#include "misc.h"
#include "prefix.h"
#include "thread.h"
@@ -4691,7 +4692,7 @@ bgp_init (void)
*
*/
void
-bgp_terminate (int terminating, int retain_mode)
+bgp_terminate (bool terminating, bool retain_mode)
{
struct bgp *bgp;
struct peer *peer;
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index ee8efa1b..12a01450 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -21,7 +21,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#ifndef _QUAGGA_BGPD_H
#define _QUAGGA_BGPD_H
-#include "stdbool.h"
+#include "misc.h"
#include "bgpd/bgp_common.h"
#include "bgpd/bgp_notification.h"
@@ -410,7 +410,7 @@ extern qpn_nexus bgp_nexus;
extern qpn_nexus routing_nexus;
/* Prototypes. */
-extern void bgp_terminate (int, int);
+extern void bgp_terminate (bool, bool);
extern void bgp_reset (void);
extern void bgp_zclient_reset (void); /* See bgp_zebra ! */
diff --git a/configure.ac b/configure.ac
index 61afe05d..4f100d94 100755
--- a/configure.ac
+++ b/configure.ac
@@ -8,7 +8,7 @@
## $Id$
AC_PREREQ(2.53)
-AC_INIT(Quagga, 0.99.15ex09, [http://bugzilla.quagga.net])
+AC_INIT(Quagga, 0.99.15ex10p, [http://bugzilla.quagga.net])
AC_CONFIG_SRCDIR(lib/zebra.h)
AC_CONFIG_MACRO_DIR([m4])
diff --git a/isisd/dict.h b/isisd/dict.h
index 9395d1c0..8cab985e 100644
--- a/isisd/dict.h
+++ b/isisd/dict.h
@@ -21,6 +21,7 @@
#ifndef DICT_H
#define DICT_H
+#include "zconfig.h"
#include <limits.h>
#ifdef KAZLIB_SIDEEFFECT_DEBUG
#include "sfx.h"
diff --git a/isisd/isis_misc.c b/isisd/isis_misc.c
index fd910835..d45d7aa0 100644
--- a/isisd/isis_misc.c
+++ b/isisd/isis_misc.c
@@ -3,21 +3,21 @@
* Miscellanous routines
*
* Copyright (C) 2001,2002 Sampo Saaristo
- * Tampere University of Technology
+ * Tampere University of Technology
* Institute of Communications Engineering
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public Licenseas published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public Licenseas published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
- * This program is distributed in the hope that it will be useful,but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * This program is distributed in the hope that it will be useful,but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
@@ -96,7 +96,7 @@ isonet_print (u_char * from, int len)
/*
* Returns 0 on error, length of buff on ok
- * extract dot from the dotted str, and insert all the number in a buff
+ * extract dot from the dotted str, and insert all the number in a buff
*/
int
dotformat2buff (u_char * buff, const u_char * dotted)
@@ -464,7 +464,7 @@ isis_jitter (unsigned long timer, unsigned long jitter)
if (timer == 1)
return timer;
- /*
+ /*
* randomizing just the percent value provides
* no good random numbers - hence the spread
* to RANDOM_SPREAD (100000), which is ok as
@@ -496,16 +496,5 @@ newprefix2inaddr (u_char * prefix_start, u_char prefix_masklen)
const char *
unix_hostname (void)
{
- static struct utsname names;
- const char *hostname;
- extern struct host host;
-
- hostname = host.name;
- if (!hostname)
- {
- uname (&names);
- hostname = names.nodename;
- }
-
- return hostname;
-}
+ return cmd_host_name(true) ; /* if required, get fresh system hostname */
+} ;
diff --git a/isisd/isis_routemap.c b/isisd/isis_routemap.c
index cff0fa3f..fcc05264 100644
--- a/isisd/isis_routemap.c
+++ b/isisd/isis_routemap.c
@@ -2,22 +2,22 @@
* IS-IS Rout(e)ing protocol - isis_routemap.c
*
* Copyright (C) 2001,2002 Sampo Saaristo
- * Tampere University of Technology
+ * Tampere University of Technology
* Institute of Communications Engineering
*
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public Licenseas published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public Licenseas published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
- * This program is distributed in the hope that it will be useful,but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * This program is distributed in the hope that it will be useful,but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <zebra.h>
@@ -68,8 +68,7 @@ isis_route_map_upd (const char *name)
for (i = 0; i <= ZEBRA_ROUTE_MAX; i++)
{
if (isis->rmap[i].name)
- isis->rmap[i].map = isis->rmap[i].map =
- route_map_lookup_by_name (isis->rmap[i].name);
+ isis->rmap[i].map = route_map_lookup_by_name (isis->rmap[i].name);
else
isis->rmap[i].map = NULL;
}
diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c
index 5d7e9da4..c4433e49 100644
--- a/isisd/isis_spf.c
+++ b/isisd/isis_spf.c
@@ -3,21 +3,21 @@
* The SPT algorithm
*
* Copyright (C) 2001,2002 Sampo Saaristo
- * Tampere University of Technology
+ * Tampere University of Technology
* Institute of Communications Engineering
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public Licenseas published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public Licenseas published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
- * This program is distributed in the hope that it will be useful,but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * This program is distributed in the hope that it will be useful,but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
@@ -64,7 +64,7 @@ remove_excess_adjs (struct list *adjs)
struct isis_adjacency *adj, *candidate = NULL;
int comp;
- for (ALL_LIST_ELEMENTS_RO (adjs, node, adj))
+ for (ALL_LIST_ELEMENTS_RO (adjs, node, adj))
{
if (excess == NULL)
excess = node;
@@ -229,7 +229,7 @@ isis_spftree_del (struct isis_spftree *spftree)
return;
}
-#endif
+#endif
void
spftree_area_init (struct isis_area *area)
@@ -241,7 +241,7 @@ spftree_area_init (struct isis_area *area)
area->spftree6[0] = isis_spftree_new ();
#endif
- /* thread_add_timer (master, isis_run_spf_l1, area,
+ /* thread_add_timer (master, isis_run_spf_l1, area,
isis_jitter (PERIODIC_SPF_INTERVAL, 10)); */
}
@@ -251,7 +251,7 @@ spftree_area_init (struct isis_area *area)
#ifdef HAVE_IPV6
area->spftree6[1] = isis_spftree_new ();
#endif
- /* thread_add_timer (master, isis_run_spf_l2, area,
+ /* thread_add_timer (master, isis_run_spf_l2, area,
isis_jitter (PERIODIC_SPF_INTERVAL, 10)); */
}
@@ -416,7 +416,7 @@ isis_spf_add2tent (struct isis_spftree *spftree, enum vertextype vtype,
listnode_add (spftree->tents, vertex);
return vertex;
}
-
+
/* XXX: This cant use the standard ALL_LIST_ELEMENT macro */
for (node = listhead (spftree->tents); node; node = listnextnode (node))
{
@@ -613,7 +613,7 @@ lspfragloop:
if (family == AF_INET && lsp->tlv_data.ipv4_int_reachs)
{
prefix.family = AF_INET;
- for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.ipv4_int_reachs,
+ for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.ipv4_int_reachs,
node, ipreach))
{
dist = cost + ipreach->metrics.metric_default;
@@ -658,7 +658,7 @@ lspfragloop:
if (family == AF_INET6 && lsp->tlv_data.ipv6_reachs)
{
prefix.family = AF_INET6;
- for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.ipv6_reachs,
+ for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.ipv6_reachs,
node, ip6reach))
{
dist = cost + ip6reach->metric;
@@ -789,7 +789,7 @@ isis_spf_preload_tent (struct isis_spftree *spftree,
if (family == AF_INET6 && !circuit->ipv6_router)
continue;
#endif /* HAVE_IPV6 */
- /*
+ /*
* Add IP(v6) addresses of this circuit
*/
if (family == AF_INET)
@@ -872,7 +872,7 @@ isis_spf_preload_tent (struct isis_spftree *spftree,
}
list_delete (adj_list);
/*
- * Add the pseudonode
+ * Add the pseudonode
*/
if (level == 1)
memcpy (lsp_id, circuit->u.bc.l1_desig_is, ISIS_SYS_ID_LEN + 1);
@@ -1272,7 +1272,7 @@ isis_spf_schedule6 (struct isis_area *area, int level)
spftree->pending = 1;
return retval;
}
-
+
THREAD_TIMER_OFF (spftree->t_spf);
if (diff < MINIMUM_SPF_INTERVAL)
@@ -1323,7 +1323,7 @@ isis_print_paths (struct vty *vty, struct list *paths)
continue;
if (memcmp (vertex->N.id, isis->sysid, ISIS_SYS_ID_LEN) == 0)
{
- vty_out (vty, "%s --%s", host.name?host.name:"",
+ vty_out (vty, "%s --%s", cmd_host_name(true),
VTY_NEWLINE);
}
else
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 39b6b03e..cc8fbd3a 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -14,9 +14,10 @@ libzebra_la_SOURCES = \
zclient.c sockopt.c smux.c md5.c if_rmap.c keychain.c privs.c \
sigevent.c pqueue.c jhash.c memtypes.c workqueue.c symtab.c heap.c \
qtime.c qpthreads.c mqueue.c qpselect.c qtimers.c qpnexus.c \
- command_parse.c command_queue.c qlib_init.c pthread_safe.c list_util.c \
- vty_io.c vty_io_file.c vty_io_shell.c vty_io_term.c vty_cli.c \
- vty_io_basic.c keystroke.c qstring.c vio_fifo.c vio_lines.c \
+ command_parse.c command_execute.c command_queue.c vty_command.c \
+ vty_io.c vty_io_file.c vty_io_shell.c vty_io_vsh.c vty_io_term.c vty_cli.c \
+ vty_io_basic.c keystroke.c qstring.c elstring.c vio_fifo.c vio_lines.c \
+ qlib_init.c pthread_safe.c list_util.c \
qiovec.c qfstring.c errno_names.c
BUILT_SOURCES = memtypes.h route_types.h
@@ -26,19 +27,19 @@ libzebra_la_DEPENDENCIES = @LIB_REGEX@
libzebra_la_LIBADD = @LIB_REGEX@
pkginclude_HEADERS = \
- buffer.h checksum.h command.h filter.h getopt.h hash.h \
+ zebra.h zconfig.h buffer.h checksum.h command.h filter.h getopt.h hash.h \
if.h linklist.h log.h \
memory.h network.h prefix.h routemap.h distribute.h sockunion.h \
- str.h stream.h table.h thread.h vector.h version.h vty.h zebra.h \
+ str.h stream.h table.h thread.h vector.h version.h vty.h \
plist.h zclient.h sockopt.h smux.h md5.h if_rmap.h keychain.h \
privs.h sigevent.h pqueue.h jhash.h zassert.h memtypes.h \
workqueue.h route_types.h symtab.h heap.h \
qtime.h qpthreads.h mqueue.h qpselect.h qtimers.h qpnexus.h \
- command_parse.h command_queue.h qlib_init.h qafi_safi.h \
+ command_parse.h command_queue.h vty_command.h qlib_init.h qafi_safi.h \
confirm.h misc.h vargs.h miyagi.h pthread_safe.h list_util.h \
- tstring.h node_type.h uty.h \
- vty_io.h vty_io_file.h vty_io_shell.h vty_io_term.h vty_cli.h \
- vty_io_basic.h keystroke.h qstring.h vio_fifo.h vio_lines.h \
+ tstring.h command_common.h command_local.h vty_common.h vty_local.h \
+ vty_io.h vty_io_file.h vty_io_shell.h vty_io_vsh.h vty_io_term.h vty_cli.h \
+ vty_io_basic.h keystroke.h qstring.h elstring.h vio_fifo.h vio_lines.h \
qiovec.h qfstring.h errno_names.h \
route_types.h command_execute.h
diff --git a/lib/command.c b/lib/command.c
index 3c4f056a..adea80e4 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -21,86 +21,279 @@ along with GNU Zebra; see the file COPYING. If not, write to the
Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
-#include <zebra.h>
+#include "zconfig.h"
+#include "version.h"
+#include "misc.h"
+
+#include <ctype.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
#include "memory.h"
#include "log.h"
-#include <lib/version.h>
#include "thread.h"
#include "vector.h"
-#include "vty.h"
-#include "uty.h"
#include "qstring.h"
+#include "qtime.h"
+#include "workqueue.h"
#include "command.h"
+#include "command_local.h"
+#include "command_parse.h"
#include "command_execute.h"
-#include "workqueue.h"
#include "command_queue.h"
-#include "command_parse.h"
+#include "vty_local.h"
+#include "vty_command.h"
+#include "vty_io.h"
+
+/* Vector of cmd_node, one for each known node, built during daemon
+ * initialisation.
+ *
+ * Declared extern in command_local.h, so it can get at it.
+ */
+vector node_vector = NULL ;
+
+/*==============================================================================
+ * Default motd string.
+ */
+#define DEFAULT_MOTD \
+"\n" \
+"Hello, this is " QUAGGA_PROGNAME " (version " QUAGGA_VERSION ")\n" \
+ QUAGGA_COPYRIGHT "\n" \
+"\n"
+
+#ifdef QDEBUG
+const char *debug_banner =
+ QUAGGA_PROGNAME " version " QUAGGA_VERSION " QDEBUG=" QDEBUG " "
+ __DATE__ " " __TIME__;
+#endif
+
+/*==============================================================================
+ * Host information structure -- shared across command/vty
+ */
+struct host host =
+{
+ /* Host name of this router. */
+ .name_set = false,
+ .name = NULL, /* set by cmd_init */
+ .name_gen = 0, /* set by cmd_init */
+
+ /* Password for vty interface. */
+ .password = NULL,
+ .password_encrypted = false,
+
+ /* Enable password */
+ .enable = NULL,
+ .enable_encrypted = false,
+
+ /* System wide terminal lines. */
+ .lines = -1, /* unset */
+
+ /* Log filename. */
+ .logfile = NULL,
+
+ /* config file name of this host */
+ .config_file = NULL,
+
+ /* Flags for services */
+ .advanced = false,
+ .encrypt = false,
+
+ /* Banner configuration. */
+ .motd = DEFAULT_MOTD,
+ .motdfile = NULL,
+
+ /* allow VTY to start without password */
+ .no_password_check = false,
+
+ /* if VTY starts without password, use RESTRICTED_NODE */
+ .restricted_mode = false,
+
+ /* Vty timeout value -- see "exec timeout" command */
+ .vty_timeout_val = VTY_TIMEOUT_DEFAULT,
+
+ /* Vty access-class command */
+ .vty_accesslist_name = NULL,
+
+ /* Vty access-class for IPv6. */
+ .vty_ipv6_accesslist_name = NULL,
+
+ /* Current directory -- initialised in vty_init() */
+ .vty_cwd = NULL,
+} ;
+
+/*==============================================================================
+ * host.name handling.
+ *
+ * If the host.name is set explicitly by command then host.name_set is true,
+ * and things are simple.
+ *
+ * Otherwise, need to ask the system. Does that once at start up, and if the
+ * host.name is unset by command -- so there should always be a valid host.name.
+ *
+ * However, it is conceivable that the host name changes (!). So, when asking
+ * for cmd_host_name(), can ask for the system to be asked afresh (if the name
+ * is not explicitly set).
+ *
+ * The VTY watch-dog timer refreshes the host.name on a regular basis,
+ * cmd_check_host_name(), so need not ask for a fresh host.name, unless wish
+ * to guarantee to be absolutely up to date.
+ *
+ * The VTY prompts need the current host name, but that is debounced using the
+ * host.name_gen value. host.name_gen is incremented each time the host.name
+ * actually changes. It is thought unlikely that this will ever wrap round,
+ * but it is guaranteed not to be zero.
+ *
+ * The fact that the host.name can change is reflected in the need to hold
+ * the VTY_LOCK while have the host.name in hand.
+ */
+
+static void cmd_get_sys_host_name(void) ;
+static void cmd_new_host_name(const char* name) ;
+
+/*------------------------------------------------------------------------------
+ * Get the host name: (a) from an explicit host name command
+ * or: (b) from the last time the system was asked.
+ *
+ * Note that the system is asked regularly by the watch dog.
+ *
+ * NB: needs to be VTY_LOCK() *or* not running qpthreads.
+ */
+extern const char*
+cmd_host_name(bool fresh)
+{
+ VTY_ASSERT_LOCKED() ;
-/* Command vector which includes some level of command lists. Normally
- each daemon maintains each own cmdvec. */
-vector cmdvec = NULL;
+ if (!host.name_set && fresh)
+ cmd_get_sys_host_name() ;
+
+ return host.name ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set (or unset) the host name from an explicit host name command.
+ *
+ * If unsets, immediately asks the system for the system host name (which may
+ * be the same !).
+ */
+static cmd_return_code_t
+cmd_set_host_name(const char* name)
+{
+ VTY_LOCK() ;
+
+ host.name_set = (name != NULL) ;
+ if (host.name_set)
+ cmd_new_host_name(name) ;
+ else
+ cmd_get_sys_host_name() ;
+
+ VTY_UNLOCK() ;
+ return CMD_SUCCESS ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get the host name from the system and set host.name to that.
+ *
+ * NB: result will not be NULL, whatever happens will get at least an empty
+ * '\0' terminated string.
+ *
+ * NB: called early in the morning to initialise host.name and to set
+ * host.name_gen != 0.
+ */
+static void
+cmd_get_sys_host_name(void)
+{
+ struct utsname info ;
-struct desc desc_cr;
-char *command_cr = NULL;
+ VTY_ASSERT_LOCKED() ;
-/* Host information structure. */
-struct host host;
+ uname (&info) ;
+ cmd_new_host_name(info.nodename) ;
+} ;
-/* Store of qstrings, used for parsing. */
-token_vector_t spare_token_strings ;
+/*------------------------------------------------------------------------------
+ * Set host.name to (possibly) new value.
+ *
+ * Does nothing if new name == old name, otherwise increments name_gen.
+ */
+static void
+cmd_new_host_name(const char* name)
+{
+ if ((host.name == NULL) || (strcmp(host.name, name) != 0))
+ {
+ XFREE(MTYPE_HOST, host.name) ;
+ host.name = XSTRDUP(MTYPE_HOST, name) ;
+ do ++host.name_gen ; while (host.name_gen == 0) ;
+ } ;
+} ;
-/* Standard command node structures. */
+/*==============================================================================
+ * node structures for the basic nodes.
+ *
+ * Covers everything up to and including the CONFIG_NODE.
+ */
static struct cmd_node auth_node =
{
- .node = AUTH_NODE,
- "Password: ",
+ .node = AUTH_NODE,
+ .prompt = "Password: ",
+
+ .parent = AUTH_NODE, /* self => no parent */
+ .exit_to = AUTH_NODE, /* self => no exit */
+ .end_to = AUTH_NODE, /* self => no end */
};
static struct cmd_node view_node =
{
- VIEW_NODE,
- "%s> ",
+ .node = VIEW_NODE,
+ .prompt = "%s> ",
+
+ .parent = AUTH_NODE, /* self => no parent */
+ .exit_to = NULL_NODE, /* close ! */
+ .end_to = VIEW_NODE, /* self => no end */
};
static struct cmd_node restricted_node =
{
- RESTRICTED_NODE,
- "%s$ ",
+ .node = RESTRICTED_NODE,
+ .prompt = "%s$ ",
+
+ .parent = AUTH_NODE, /* self => no parent */
+ .exit_to = NULL_NODE, /* close ! */
+ .end_to = RESTRICTED_NODE, /* self => no end */
};
static struct cmd_node auth_enable_node =
{
- AUTH_ENABLE_NODE,
- "Password: ",
+ .node = AUTH_ENABLE_NODE,
+ .prompt = "Password: ",
+
+ .parent = AUTH_ENABLE_NODE, /* self => no parent */
+ .exit_to = NULL_NODE, /* close ! */
+ .end_to = AUTH_ENABLE_NODE, /* self => no end */
};
static struct cmd_node enable_node =
{
- ENABLE_NODE,
- "%s# ",
+ .node = ENABLE_NODE,
+ .prompt = "%s# ",
+
+ .parent = ENABLE_NODE, /* self => no parent */
+ .exit_to = NULL_NODE, /* close ! */
+ .end_to = ENABLE_NODE, /* self => no end */
};
static struct cmd_node config_node =
{
- CONFIG_NODE,
- "%s(config)# ",
- 1
-};
+ .node = CONFIG_NODE,
+ .prompt = "%s(config)# ",
-/* Default motd string. */
-static const char *default_motd =
-"\r\n\
-Hello, this is " QUAGGA_PROGNAME " (version " QUAGGA_VERSION ").\r\n\
-" QUAGGA_COPYRIGHT "\r\n\
-\r\n";
+ .parent = CONFIG_NODE, /* self => no parent */
+ .exit_to = ENABLE_NODE, /* exit == end for CONFIG_NODE */
+ .end_to = ENABLE_NODE, /* standard end action */
-#ifdef QDEBUG
-const char *debug_banner =
- QUAGGA_PROGNAME " version " QUAGGA_VERSION " QDEBUG=" QDEBUG " "
- __DATE__ " " __TIME__;
-#endif
+ .config_to_vtysh = true
+};
static const struct facility_map {
int facility;
@@ -132,18 +325,29 @@ static const struct facility_map {
{ 0, NULL, 0 },
};
-static struct cmd_element cmd_pipe =
+#if 0
+static enum cmd_return_code
+cmd_pipe_func(cmd_command self DEFUN_CMD_ARG_UNUSED,
+ struct vty* vty DEFUN_CMD_ARG_UNUSED,
+ int argc DEFUN_CMD_ARG_UNUSED,
+ argv_t argv DEFUN_CMD_ARG_UNUSED)
+{
+ return CMD_SUCCESS ;
+} ;
+
+static struct cmd_command cmd_pipe_element =
{
.string = "< or <|", /* Dummy */
.func = cmd_pipe_func,
.doc = "Pipe input to command processor",
.daemon = 0,
- .strvec = NULL,
+ .items = NULL,
.cmdsize = 0,
.config = NULL,
.subconfig = NULL,
.attr = CMD_ATTR_SIMPLE,
} ;
+#endif
static const char *
@@ -188,9 +392,12 @@ print_version (const char *progname)
}
-/* Utility function to concatenate argv argument into a single string
- with inserting ' ' character between each argument. */
-char *
+/*------------------------------------------------------------------------------
+ * Concatenate argv argument into a single string, inserting ' ' between each
+ * argument.
+ *
+ */
+extern char *
argv_concat (const char* const* argv, int argc, int shift)
{
int i;
@@ -201,8 +408,10 @@ argv_concat (const char* const* argv, int argc, int shift)
len = 0;
for (i = shift; i < argc; i++)
len += strlen(argv[i])+1;
+
if (!len)
return NULL;
+
p = str = XMALLOC(MTYPE_TMP, len);
for (i = shift; i < argc; i++)
{
@@ -220,8 +429,8 @@ argv_concat (const char* const* argv, int argc, int shift)
static int
cmp_node (const void *p, const void *q)
{
- const struct cmd_element *a = *(struct cmd_element * const *)p;
- const struct cmd_element *b = *(struct cmd_element * const *)q;
+ const struct cmd_command *a = *(struct cmd_command * const *)p;
+ const struct cmd_command *b = *(struct cmd_command * const *)q;
return strcmp (a->string, b->string);
}
@@ -237,42 +446,146 @@ cmp_desc (const void *p, const void *q)
#endif //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
-/* Install top node of command vector. */
-void
+/*------------------------------------------------------------------------------
+ * Install top node of command vector.
+ *
+ * Initialised as follows:
+ *
+ * .node -- must be set before node is installed
+ * .prompt -- must be set before node is installed
+ * .config_to_vtysh -- must be set true/false before node is installed
+ *
+ * .parent -- may be set, or if 0 (node_null) will be set to default
+ * .exit_to -- may be set, or if 0 (node_null) will be set to default
+ * .end_to -- may be set, or if 0 (node_null) will be set to default
+ *
+ * .config_write -- is set from parameter
+ *
+ * .cmd_vector -- initialised empty
+ *
+ * Default parent node:
+ *
+ * * all nodes >= NODE_CONFIG have NODE_CONFIG as a parent
+ * * all nodes < NODE_CONFIG are their own parents
+ *
+ * Default exit_to:
+ *
+ * * all nodes > NODE_CONFIG exit_to their parent
+ * * node == NODE_CONFIG exit_to ENABLE_NODE (same as end_to !)
+ * * all nodes < NODE_CONFIG exit_to close
+ *
+ * Default end_to:
+ *
+ * * all nodes >= NODE_CONFIG end_to ENABLE_NODE
+ * * all nodes < NODE_CONFIG end_to themselves
+ *
+ */
+extern void
install_node (struct cmd_node *node,
- int (*func) (struct vty *))
+ int (*config_write) (struct vty *))
{
- vector_set_index (cmdvec, node->node, node);
- node->func = func;
- node->cmd_vector = vector_init (0);
-}
+ confirm(NULL_NODE == 0) ; /* unset value for .parent, .end_to & .exit_to */
-/* Compare two command's string. Used in sort_node (). */
-static int
-cmp_node (const struct cmd_element **a, const struct cmd_element **b)
+ if (node->parent == NULL_NODE)
+ {
+ if (node->node >= CONFIG_NODE)
+ node->parent = CONFIG_NODE ;
+ else
+ node->parent = node->node ;
+ } ;
+
+ if (node->end_to == NULL_NODE)
+ {
+ if (node->node >= CONFIG_NODE)
+ node->end_to = ENABLE_NODE ;
+ else
+ node->end_to = node->node ;
+ } ;
+
+ if (node->exit_to == NULL_NODE)
+ {
+ if (node->node > CONFIG_NODE)
+ node->exit_to = node->parent ;
+ else if (node->node == CONFIG_NODE)
+ node->exit_to = ENABLE_NODE ;
+ else
+ node->exit_to = NULL_NODE ;
+ } ;
+
+ node->config_write = config_write ;
+
+ vector_init_new(node->cmd_vector, 0) ; /* embedded */
+
+ vector_set_index (node_vector, node->node, node);
+} ;
+
+/*------------------------------------------------------------------------------
+ * Return address of cmd_node -- asserts is not NULL !
+ */
+static cmd_node
+cmd_cmd_node(node_type_t node)
{
- return strcmp ((*a)->string, (*b)->string);
-}
+ cmd_node cn ;
+
+ cn = vector_get_item(node_vector, node) ;
+ if (cn != NULL)
+ return cn ;
+
+ zabort("invalid node") ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Return parent node
+ */
+extern node_type_t
+cmd_node_parent(node_type_t node)
+{
+ return (cmd_cmd_node(node))->parent ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Return exit_to node
+ */
+extern node_type_t
+cmd_node_exit_to(node_type_t node)
+{
+ return (cmd_cmd_node(node))->exit_to ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Return parent node
+ */
+extern node_type_t
+cmd_node_end_to(node_type_t node)
+{
+ return (cmd_cmd_node(node))->end_to ;
+} ;
+
+
+/*------------------------------------------------------------------------------
+ * Sorting of all node cmd_vectors.
+ */
+
+/* Compare two command's string. Used in sort_node (). */
static int
-cmp_desc (const struct desc **a, const struct desc **b)
+cmp_node (const struct cmd_command **a, const struct cmd_command **b)
{
- return strcmp ((*a)->cmd, (*b)->cmd);
+ return strcmp ((*a)->string, (*b)->string);
}
/* Sort each node's command element according to command string. */
-void
+extern void
sort_node ()
{
unsigned int i ;
- for (i = 0; i < vector_length(cmdvec); i++)
+ for (i = 0; i < vector_length(node_vector); i++)
{
struct cmd_node *cnode;
vector cmd_vector ;
- unsigned int j;
- cnode = vector_get_item(cmdvec, i) ;
+ cnode = vector_get_item(node_vector, i) ;
if (cnode == NULL)
continue ;
@@ -282,25 +595,10 @@ sort_node ()
continue ;
vector_sort(cmd_vector, (vector_sort_cmp*)cmp_node) ;
-
- for (j = 0; j < vector_length(cmd_vector); j++)
- {
- struct cmd_element *cmd_element ;
- vector descvec ;
-
- cmd_element = vector_get_item (cmd_vector, j);
- if (cmd_element == NULL)
- continue ;
-
- descvec = vector_get_last_item(cmd_element->strvec) ;
- if (descvec == NULL)
- continue ;
-
- vector_sort(descvec, (vector_sort_cmp*)cmp_desc) ;
- } ;
} ;
} ;
+#if 0
/*------------------------------------------------------------------------------
* Take string and break it into tokens -- see cmd_make_tokens().
*
@@ -310,8 +608,11 @@ sort_node ()
extern vector
cmd_make_strvec (const char *string)
{
+#if 0
return cmd_tokenise(NULL, string) ;
#error sort this one out
+#endif
+ return NULL ;
} ;
/*------------------------------------------------------------------------------
@@ -320,204 +621,47 @@ cmd_make_strvec (const char *string)
* Create vector if required.
*/
extern vector
-cmd_add_to_strvec (vector strvec, const char* str)
+cmd_add_to_strvec (vector items, const char* str)
{
- if (strvec == NULL)
- strvec = vector_init(1) ;
+ if (items == NULL)
+ items = vector_init(1) ;
- vector_push_item(strvec, XSTRDUP(MTYPE_STRVEC, str));
+ vector_push_item(items, XSTRDUP(MTYPE_STRVEC, str));
- return strvec ;
+ return items ;
} ;
/*------------------------------------------------------------------------------
* Free allocated string vector (if any) and all its contents.
*
- * Note that this is perfectly happy with strvec == NULL.
+ * Note that this is perfectly happy with items == NULL.
*/
extern void
-cmd_free_strvec (vector strvec)
+cmd_free_strvec (vector items)
{
char *cp;
- /* Note that vector_ream_free() returns NULL if strvec == NULL */
- while((cp = vector_ream(strvec, free_it)) != NULL)
+ /* Note that vector_ream_free() returns NULL if items == NULL */
+ while((cp = vector_ream(items, free_it)) != NULL)
XFREE (MTYPE_STRVEC, cp);
} ;
-/*----------------------------------------------------------------------------*/
-
-/* Fetch next description. Used in cmd_make_descvec(). */
-static char *
-cmd_desc_str (const char **string)
-{
- const char *cp, *start;
- char *token;
- int strlen;
-
- cp = *string;
-
- if (cp == NULL)
- return NULL;
-
- /* Skip white spaces. */
- while (isspace ((int) *cp) && *cp != '\0')
- cp++;
-
- /* Return if there is only white spaces */
- if (*cp == '\0')
- return NULL;
-
- start = cp;
-
- while (!(*cp == '\r' || *cp == '\n') && *cp != '\0')
- cp++;
-
- strlen = cp - start;
- token = XMALLOC (MTYPE_STRVEC, strlen + 1);
- memcpy (token, start, strlen);
- *(token + strlen) = '\0';
-
- *string = cp;
-
- return token;
-}
-
-/* New string vector. */
-static vector
-cmd_make_descvec (const char *string, const char *descstr)
-{
- int multiple = 0;
- const char *sp;
- char *token;
- int len;
- const char *cp;
- const char *dp;
- vector allvec;
- vector strvec = NULL;
- struct desc *desc;
-
- cp = string;
- dp = descstr;
-
- if (cp == NULL)
- return NULL;
-
- allvec = vector_init (0);
-
- while (1)
- {
- while (isspace ((int) *cp) && *cp != '\0')
- cp++;
-
- if (*cp == '(')
- {
- multiple = 1;
- cp++;
- }
- if (*cp == ')')
- {
- multiple = 0;
- cp++;
- }
- if (*cp == '|')
- {
- if (! multiple)
- {
- fprintf (stderr, "Command parse error!: %s\n", string);
- exit (1);
- }
- cp++;
- }
-
- while (isspace ((int) *cp) && *cp != '\0')
- cp++;
-
- if (*cp == '(')
- {
- multiple = 1;
- cp++;
- }
-
- if (*cp == '\0')
- return allvec;
-
- sp = cp;
-
- while (! (isspace ((int) *cp) || *cp == ')' || *cp == '|') && *cp != '\0')
- cp++;
-
- len = cp - sp;
-
- token = XMALLOC (MTYPE_STRVEC, len + 1);
- memcpy (token, sp, len);
- *(token + len) = '\0';
-
- desc = XCALLOC (MTYPE_DESC, sizeof (struct desc));
- desc->cmd = token;
- desc->str = cmd_desc_str (&dp);
-
- if (multiple)
- {
- if (multiple == 1)
- {
- strvec = vector_init (0);
- vector_set (allvec, strvec);
- }
- multiple++;
- }
- else
- {
- strvec = vector_init (0);
- vector_set (allvec, strvec);
- }
- vector_set (strvec, desc);
- }
-}
-
-/* Count mandantory string vector size. This is to determine inputed
- command has enough command length. */
-static int
-cmd_cmdsize (vector strvec)
-{
- unsigned int i;
- int size = 0;
-
- for (i = 0; i < vector_length(strvec); i++)
- {
- vector descvec;
-
- descvec = vector_get_item (strvec, i) ;
- if (descvec == NULL)
- continue ;
-
- if (vector_length(descvec) == 1)
- {
- struct desc *desc;
-
- desc = vector_get_item(descvec, 0) ;
- if (desc != NULL)
- if (desc->cmd == NULL || CMD_OPTION (desc->cmd))
- break ;
- }
- size++;
- } ;
-
- return size;
-}
+#endif
-/* Return prompt character of specified node. */
-const char *
-cmd_prompt (enum node_type node)
+/*------------------------------------------------------------------------------
+ * Return prompt string for the specified node.
+ */
+extern const char *
+cmd_prompt(node_type_t node)
{
struct cmd_node *cnode;
- assert(cmdvec != NULL) ;
- assert(cmdvec->p_items != NULL) ;
+ assert(node_vector != NULL) ;
+ assert(node_vector->p_items != NULL) ;
cnode = NULL ;
- if (node < cmdvec->limit)
- cnode = vector_get_item (cmdvec, node);
+ if (node < node_vector->limit)
+ cnode = vector_get_item (node_vector, node);
if (cnode == NULL)
{
@@ -528,17 +672,16 @@ cmd_prompt (enum node_type node)
return cnode->prompt;
}
-/* Install a command into a node. */
-void
-install_element (enum node_type ntype, struct cmd_element *cmd)
+/*------------------------------------------------------------------------------
+ * Install a command into a node.
+ *
+ */
+extern void
+install_element(node_type_t ntype, cmd_command cmd)
{
- struct cmd_node *cnode;
+ cmd_node cnode;
- /* cmd_init hasn't been called */
- if (!cmdvec)
- return;
-
- cnode = vector_get_item (cmdvec, ntype);
+ cnode = vector_get_item (node_vector, ntype);
if (cnode == NULL)
{
@@ -549,65 +692,75 @@ install_element (enum node_type ntype, struct cmd_element *cmd)
vector_set (cnode->cmd_vector, cmd);
- if (cmd->strvec == NULL)
- cmd->strvec = cmd_make_descvec (cmd->string, cmd->doc);
+ /* A cmd_command may appear in a number of cmd_vectors, but the cmd->items
+ * etc. need only be set up once.
+ *
+ * It is assumed that once a cmd_command has been installed it will never be
+ * changed !
+ *
+ * Need now to "compile" the command if not already compiled.
+ */
+ if (cmd->items == NULL)
+ cmd_compile(cmd);
- cmd->cmdsize = cmd_cmdsize (cmd->strvec);
-}
+ /* Post compilation check for reasonable cmd_command ! */
+ cmd_compile_check(cmd) ;
+} ;
+/*==============================================================================
+ * Password encryption
+ */
static const unsigned char itoa64[] =
-"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+ "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
-static void
-to64(char *s, long v, int n)
-{
- while (--n >= 0)
- {
- *s++ = itoa64[v&0x3f];
- v >>= 6;
- }
-}
-
-static char *
+/* Uses the usual crypt() function.
+ *
+ * Note that crypt() is not thread safe !
+ */
+static const char *
zencrypt (const char *passwd)
{
- char salt[6];
- struct timeval tv;
- char *crypt (const char *, const char *);
+ uint32_t r ;
+ char salt[3];
+
+ extern char *crypt (const char *, const char *) ;
- gettimeofday(&tv,0);
+ r = qt_random(*passwd) ;
- to64(&salt[0], random(), 3);
- to64(&salt[3], tv.tv_usec, 3);
- salt[5] = '\0';
+ salt[0] = itoa64[(r >> (32 - 5)) & 0x3F] ; /* ms 5 */
+ salt[1] = itoa64[(r >> (32 - 10)) & 0x3F] ; /* next ms 5 */
+ salt[2] = '\0';
- return crypt (passwd, salt);
+ return crypt(passwd, salt) ;
}
/* This function write configuration of this host. */
static int
config_write_host (struct vty *vty)
{
+ VTY_LOCK() ;
+
if (qpthreads_enabled)
vty_out (vty, "threaded%s", VTY_NEWLINE);
- if (host.name)
+ if (host.name_set)
vty_out (vty, "hostname %s%s", host.name, VTY_NEWLINE);
- if (host.encrypt)
+ if (host.password != NULL)
{
- if (host.password_encrypt)
- vty_out (vty, "password 8 %s%s", host.password_encrypt, VTY_NEWLINE);
- if (host.enable_encrypt)
- vty_out (vty, "enable password 8 %s%s", host.enable_encrypt, VTY_NEWLINE);
- }
- else
+ if (host.password_encrypted)
+ vty_out (vty, "password 8 %s\n", host.password);
+ else
+ vty_out (vty, "password %s\n", host.password);
+ } ;
+
+ if (host.enable != NULL)
{
- if (host.password)
- vty_out (vty, "password %s%s", host.password, VTY_NEWLINE);
- if (host.enable)
- vty_out (vty, "enable password %s%s", host.enable, VTY_NEWLINE);
- }
+ if (host.enable_encrypted)
+ vty_out (vty, "enable password 8 %s\n", host.enable);
+ else
+ vty_out (vty, "enable password %s\n", host.enable);
+ } ;
if (zlog_get_default_lvl(NULL) != LOG_DEBUG)
{
@@ -671,1674 +824,16 @@ config_write_host (struct vty *vty)
vty_out (vty, "service terminal-length %d%s", host.lines,
VTY_NEWLINE);
- if (host.motdfile)
+ if (host.motdfile)
vty_out (vty, "banner motd file %s%s", host.motdfile, VTY_NEWLINE);
else if (! host.motd)
vty_out (vty, "no banner motd%s", VTY_NEWLINE);
- return 1;
-}
-
-/* Utility function for getting command vector. */
-static vector
-cmd_node_vector (vector v, enum node_type ntype)
-{
- struct cmd_node *cnode = vector_get_item (v, ntype);
- return cnode->cmd_vector;
-}
-
-#if 0
-/* Filter command vector by symbol. This function is not actually used;
- * should it be deleted? */
-static int
-cmd_filter_by_symbol (char *command, char *symbol)
-{
- int i, lim;
-
- if (strcmp (symbol, "IPV4_ADDRESS") == 0)
- {
- i = 0;
- lim = strlen (command);
- while (i < lim)
- {
- if (! (isdigit ((int) command[i]) || command[i] == '.' || command[i] == '/'))
- return 1;
- i++;
- }
- return 0;
- }
- if (strcmp (symbol, "STRING") == 0)
- {
- i = 0;
- lim = strlen (command);
- while (i < lim)
- {
- if (! (isalpha ((int) command[i]) || command[i] == '_' || command[i] == '-'))
- return 1;
- i++;
- }
- return 0;
- }
- if (strcmp (symbol, "IFNAME") == 0)
- {
- i = 0;
- lim = strlen (command);
- while (i < lim)
- {
- if (! isalnum ((int) command[i]))
- return 1;
- i++;
- }
- return 0;
- }
- return 0;
-}
-#endif
-
-/*==============================================================================
- * Command "filtering".
- *
- * The command parsing process starts with a (shallow) copy of the cmd_vector
- * entry for the current "node".
- *
- * So cmd_v contains pointers to struct cmd_element values. When match fails,
- * the pointer is set NULL -- so parsing is a process of reducing the cmd_v
- * down to just the entries that match.
- *
- * Each cmd_element has a vector "strvec", which contains an entry for each
- * "token" position. That entry is a vector containing the possible values at
- * that position.
- *
- *
- */
-
-/*------------------------------------------------------------------------------
- * Make strict or completion match and return match type flag.
- *
- * Takes: command -- address of candidate token
- * cmd_v -- vector of commands that is being reduced/filtered
- * index -- index of token (position in line -- 0 == first)
- * min_match -- any_match => allow partial matching
- * exact_match => must match completely
- *
- * Returns: any of the enum match_type values:
- *
- * no_match => no match of any kind
- *
- * extend_match => saw an optional token
- * ipv4_prefix_match )
- * ipv4_match )
- * ipv6_prefix_match ) saw full or partial match for this
- * ipv6_match )
- * range_match )
- * vararg_match )
- *
- * partly_match => saw partial match for a keyword
- * exact_match => saw exact match for a keyword
- *
- * Note that these return values are in ascending order of preference. So,
- * if there are multiple possibilities at this position, will return the one
- * furthest down this list.
- */
-static enum match_type
-cmd_filter(const char *command, vector cmd_v, unsigned int index,
- match_type_t min_match)
-{
- unsigned int i;
- unsigned int k;
- enum match_type best_match;
- size_t c_len ;
-
- best_match = no_match ;
- c_len = strlen(command) ;
-
- /* If command and cmd_element string do match, keep in vector */
- k = 0 ;
- for (i = 0; i < vector_length (cmd_v); i++)
- {
- const char *str;
- struct cmd_element *cmd_element;
- vector descvec;
- struct desc *desc;
- unsigned int j;
- bool matched ;
-
- cmd_element = vector_get_item(cmd_v, i) ;
-
- /* Skip past NULL cmd_v entries (just in case) */
- if (cmd_element == NULL)
- continue ;
-
- /* Discard cmd_v entry that has no token at the current position */
- descvec = vector_get_item(cmd_element->strvec, index) ;
- if (descvec == NULL)
- continue ;
-
- /* See if get any sort of match at current position */
- matched = 0 ;
- for (j = 0; j < vector_length (descvec); j++)
- {
- desc = vector_get_item(descvec, j) ;
- if (desc == NULL)
- continue ;
-
- str = desc->cmd;
-
- if (CMD_VARARG (str))
- {
- if (best_match < vararg_match)
- best_match = vararg_match;
- matched = true ;
- }
- else if (CMD_RANGE (str))
- {
- if (cmd_range_match (str, command))
- {
- if (best_match < range_match)
- best_match = range_match;
- matched = true ;
- }
- }
-#ifdef HAVE_IPV6
- else if (CMD_IPV6 (str))
- {
- if (cmd_ipv6_match (command) >= min_match)
- {
- if (best_match < ipv6_match)
- best_match = ipv6_match;
- matched = true ;
- }
- }
- else if (CMD_IPV6_PREFIX (str))
- {
- if (cmd_ipv6_prefix_match (command) >= min_match)
- {
- if (best_match < ipv6_prefix_match)
- best_match = ipv6_prefix_match;
- matched = true ;
- }
- }
-#endif /* HAVE_IPV6 */
- else if (CMD_IPV4 (str))
- {
- if (cmd_ipv4_match (command) >= min_match)
- {
- if (best_match < ipv4_match)
- best_match = ipv4_match;
- matched = true ;
- }
- }
- else if (CMD_IPV4_PREFIX (str))
- {
- if (cmd_ipv4_prefix_match (command) >= min_match)
- {
- if (best_match < ipv4_prefix_match)
- best_match = ipv4_prefix_match;
- matched = true ;
- }
- }
- else if (CMD_OPTION (str) || CMD_VARIABLE (str))
- {
- if (best_match < extend_match)
- best_match = extend_match;
- matched = true ;
- }
- else
- {
- if (strcmp (command, str) == 0)
- {
- best_match = exact_match ;
- matched = true ;
- }
- else if (min_match <= partly_match)
- {
- if (strncmp (command, str, c_len) == 0)
- {
- if (best_match < partly_match)
- best_match = partly_match ;
- matched = true ;
- } ;
- } ;
- } ;
- } ;
-
- /* Keep cmd_element if have a match */
- if (matched)
- vector_set_item(cmd_v, k++, cmd_element) ;
- } ;
-
- vector_set_length(cmd_v, k) ; /* discard what did not keep */
-
- return best_match;
-} ;
-
-/*------------------------------------------------------------------------------
- * Check for ambiguous match
- *
- * Given the best that cmd_filter_by_completion() or cmd_filter_by_string()
- * found, check as follows:
- *
- * 1. discard all commands for which do not have the type of match selected.
- *
- * See above for the ranking of matches.
- *
- * 2. for "partial match", look out for matching more than one keyword, and
- * return 1 if finds that.
- *
- * 3. for "range match", look out for matching more than one range, and
- * return 1 if finds that.
- *
- * 4. for ipv4_prefix_match and ipv6_prefix_match, if get a "partial match",
- * return 2.
- *
- * This appears to catch things which are supposed to be prefixes, but
- * do not have a '/' or do not have any digits after the '/'.
- *
- * Takes: command -- address of candidate token
- * cmd_v -- vector of commands that is being reduced/filtered
- * index -- index of token (position in line -- 0 == first)
- * type -- as returned by cmd_filter_by_completion()
- * or cmd_filter_by_string()
- *
- * Returns: 0 => not ambiguous
- * 1 => ambiguous -- the candidate token matches more than one
- * keyword, or the candidate number matches more
- * than one number range.
- * 2 => partial match for ipv4_prefix or ipv6_prefix
- * (missing '/' or no digits after '/').
- *
- * NB: it is assumed that cannot find both 1 and 2 states. But in any case,
- * returns 1 in preference.
- */
-static int
-is_cmd_ambiguous (const char *command, vector cmd_v, int index,
- enum match_type type)
-{
- unsigned int i;
- unsigned int k;
- int ret ;
-
- ret = 0 ; /* all's well so far */
- k = 0 ; /* nothing kept, yet */
-
- for (i = 0; i < vector_length (cmd_v); i++)
- {
- unsigned int j;
- struct cmd_element *cmd_element;
- const char *str_matched ;
- vector descvec;
- struct desc *desc;
- bool matched ;
- enum match_type mt ;
-
- cmd_element = vector_get_item (cmd_v, i) ;
-
- /* Skip past NULL cmd_v entries (just in case) */
- if (cmd_element == NULL)
- continue ;
-
- /* The cmd_v entry MUST have a token at the current position */
- descvec = vector_get_item (cmd_element->strvec, index) ;
- assert(descvec != NULL) ;
-
- /* See if have a match against any of the current possibilities
- *
- * str_matched is set the first time get a partial string match,
- * or the first time get a number range match.
- *
- * If get a second partial string match or number range match, then
- * unless
- */
- str_matched = NULL ;
- matched = 0;
- for (j = 0; j < vector_length (descvec); j++)
- {
- enum match_type ret;
- const char *str ;
-
- desc = vector_get_item (descvec, j) ;
- if (desc == NULL)
- continue ;
-
- str = desc->cmd;
-
- switch (type)
- {
- case exact_match:
- if (!(CMD_OPTION (str) || CMD_VARIABLE (str))
- && strcmp (command, str) == 0)
- matched = true ;
- break;
-
- case partly_match:
- if (!(CMD_OPTION (str) || CMD_VARIABLE (str))
- && strncmp (command, str, strlen (command)) == 0)
- {
- if (str_matched && (strcmp (str_matched, str) != 0))
- ret = 1; /* There is ambiguous match. */
- else
- str_matched = str;
- matched = true ;
- }
- break;
-
- case range_match:
- if (cmd_range_match (str, command))
- {
- if (str_matched && strcmp (str_matched, str) != 0)
- ret = 1;
- else
- str_matched = str;
- matched = true ;
- }
- break;
-
-#ifdef HAVE_IPV6
- case ipv6_match:
- if (CMD_IPV6 (str))
- matched = true ;
- break;
-
- case ipv6_prefix_match:
- if ((mt = cmd_ipv6_prefix_match (command)) != no_match)
- {
- if ((mt == partly_match) && (ret != 1))
- ret = 2; /* There is incomplete match. */
- matched = true ;
- }
- break;
-#endif /* HAVE_IPV6 */
-
- case ipv4_match:
- if (CMD_IPV4 (str))
- matched = true ;
- break;
-
- case ipv4_prefix_match:
- if ((mt = cmd_ipv4_prefix_match (command)) != no_match)
- {
- if ((mt == partly_match) && (ret != 1))
- ret = 2; /* There is incomplete match. */
- matched = true ;
- }
- break;
-
- case extend_match:
- if (CMD_OPTION (str) || CMD_VARIABLE (str))
- matched = true ;
- break;
-
- case no_match:
- default:
- break;
- } ;
- } ;
-
- /* Keep cmd_element if have a match */
- if (matched)
- vector_set_item(cmd_v, k++, cmd_element) ;
- } ;
-
- vector_set_length(cmd_v, k) ; /* discard what did not keep */
-
- return ret ;
-} ;
-
-/*------------------------------------------------------------------------------
- * If src matches dst return dst string, otherwise return NULL
- *
- * Returns NULL if dst is an option, variable of vararg.
- *
- * NULL or empty src are deemed to match.
- */
-static const char *
-cmd_entry_function (const char *src, const char *dst)
-{
- if (CMD_OPTION (dst) || CMD_VARIABLE (dst) || CMD_VARARG (dst))
- return NULL;
-
- if ((src == NULL) || (*src == '\0'))
- return dst;
-
- if (strncmp (src, dst, strlen (src)) == 0)
- return dst;
-
- return NULL;
-}
-
-/* If src matches dst return dst string, otherwise return NULL */
-/* This version will return the dst string always if it is
- CMD_VARIABLE for '?' key processing */
-static const char *
-cmd_entry_function_desc (const char *src, const char *dst)
-{
- if (CMD_VARARG (dst))
- return dst;
-
- if (CMD_RANGE (dst))
- {
- if (cmd_range_match (dst, src))
- return dst;
- else
- return NULL;
- }
-
-#ifdef HAVE_IPV6
- if (CMD_IPV6 (dst))
- {
- if (cmd_ipv6_match (src))
- return dst;
- else
- return NULL;
- }
-
- if (CMD_IPV6_PREFIX (dst))
- {
- if (cmd_ipv6_prefix_match (src))
- return dst;
- else
- return NULL;
- }
-#endif /* HAVE_IPV6 */
-
- if (CMD_IPV4 (dst))
- {
- if (cmd_ipv4_match (src))
- return dst;
- else
- return NULL;
- }
-
- if (CMD_IPV4_PREFIX (dst))
- {
- if (cmd_ipv4_prefix_match (src))
- return dst;
- else
- return NULL;
- }
-
- /* Optional or variable commands always match on '?' */
- if (CMD_OPTION (dst) || CMD_VARIABLE (dst))
- return dst;
-
- /* In case of 'command \t', given src is NULL string. */
- if (src == NULL)
- return dst;
-
- if (strncmp (src, dst, strlen (src)) == 0)
- return dst;
- else
- return NULL;
-}
-
-/*------------------------------------------------------------------------------
- * Check same string element existence.
- *
- * Returns: 0 => found same string in the vector
- * 1 => NOT found same string in the vector
- */
-static bool
-cmd_unique_string (vector v, const char *str)
-{
- unsigned int i;
- char *match;
+ VTY_UNLOCK() ;
- for (i = 0; i < vector_length (v); i++)
- if ((match = vector_get_item (v, i)) != NULL)
- if (strcmp (match, str) == 0)
- return 0;
return 1;
}
-/* Compare string to description vector. If there is same string
- return 1 else return 0. */
-static bool
-desc_unique_string (vector v, const char *str)
-{
- unsigned int i;
- struct desc *desc;
-
- for (i = 0; i < vector_length (v); i++)
- if ((desc = vector_get_item (v, i)) != NULL)
- if (strcmp (desc->cmd, str) == 0)
- return 1;
- return 0;
-}
-
-/*------------------------------------------------------------------------------
- * Special parsing for leading 'do', if current mode allows it.
- *
- * If finds a valid "do", sets current node and do_shortcut flag, and discards
- * the "do" token.
- *
- * Returns: true <=> dealt with the "do"
- * false => no do, or no do allowed.
- */
-static bool
-cmd_try_do_shortcut(cmd_parsed parsed)
-{
- const char* ts ;
-
- if (parsed->cnode < MIN_DO_SHORTCUT_NODE)
- return false ;
-
- ts = cmd_token_string(cmd_token_get(&parsed->tokens, 0)) ;
- if (strcmp("do", ts) != 0)
- return false ;
-
- parsed->cnode = ENABLE_NODE ;
- parsed->do_shortcut = true ;
- cmd_token_discard(cmd_token_shift(&parsed->tokens)) ;
-
- return true ;
-} ;
-
-/*==============================================================================
- * '?' describe command support.
- */
-
-/*------------------------------------------------------------------------------
- * Get description of current (partial) command
- *
- * Returns: NULL => no description available
- *
- * status set to CMD_ERR_NO_MATCH or CMD_ERR_AMBIGUOUS
- *
- * or: address of vector of "struct desc" values available.
- *
- * NB: when a vector is returned it is the caller's responsibility to
- * vector_free() it. (The contents are all effectively const, so do not
- * themselves need to be freed.)
- */
-extern vector
-cmd_describe_command (const char* line, node_type_t node,
- cmd_return_code_t* status)
-{
- vector ret ;
- struct cmd_parsed parsed_s ;
- cmd_parsed parsed ;
- cmd_token_type_t tok_total ;
-
- /* Set up a parser object and tokenise the command line */
- parsed = cmd_parse_init_new(&parsed_s) ;
- tok_total = cmd_tokenise(parsed, line, node) ;
-
-
-
-
-
-
-
- /* Stop immediately if line is empty apart from comment */
- if ((tok_total & ~cmd_tok_comment) == cmd_tok_null)
- return CMD_EMPTY ; /* NB: parsed->cmd == NULL */
-
- /* Level 1 parsing
- *
- * Strip quotes and escapes from all the tokens.
- */
- if (tok_total != cmd_tok_simple)
- {
- ret = cmd_parse_phase_one(parsed) ;
- if (ret != CMD_SUCCESS)
- return ret ;
- } ;
-
- /* If allowed to 'do', see if there.
- *
- * 'do' forces command to be parsed in ENABLE_NODE (if allowed)
- */
- if (type & cmd_parse_do)
- cmd_try_do_shortcut(parsed) ;
-
-
-
-
- return cmd_describe_command_real (tokens, node, status);
-
-
-
- static vector
- cmd_describe_command_real (vector tokens, int node, int *status)
- {
- unsigned int i;
- vector cmd_vector;
- #define INIT_MATCHVEC_SIZE 10
- vector matchvec;
- struct cmd_element *cmd_element;
- unsigned int index;
- int ret;
- enum match_type match;
- char *command;
-
- /* Set index. */
- if (vector_length (tokens) == 0)
- {
- *status = CMD_ERR_NO_MATCH;
- return NULL;
- }
- else
- index = vector_length (tokens) - 1;
-
- /* Make copy vector of current node's command vector. */
- cmd_vector = vector_copy (cmd_node_vector (cmdvec, node));
-
- /* Prepare match vector */
- matchvec = vector_init (INIT_MATCHVEC_SIZE);
-
- /* Filter commands. */
- /* Only words precedes current word will be checked in this loop. */
- for (i = 0; i < index; i++)
- if ((command = vector_get_item (tokens, i)))
- {
- match = cmd_filter(command, cmd_vector, i, any_match) ;
-
- if (match == vararg_match)
- {
- struct cmd_element *cmd_element;
- vector descvec;
- unsigned int j, k;
-
- for (j = 0; j < vector_length (cmd_vector); j++)
- if ((cmd_element = vector_get_item (cmd_vector, j)) != NULL
- && (vector_length (cmd_element->strvec)))
- {
- descvec = vector_get_item (cmd_element->strvec,
- vector_length (cmd_element->strvec) - 1);
- for (k = 0; k < vector_length (descvec); k++)
- {
- struct desc *desc = vector_get_item (descvec, k);
- vector_set (matchvec, desc);
- }
- }
-
- vector_set (matchvec, &desc_cr);
- vector_free (cmd_vector);
-
- return matchvec;
- } ;
-
- ret = is_cmd_ambiguous (command, cmd_vector, i, match) ;
- if (ret != 0)
- {
- vector_free (cmd_vector);
- vector_free (matchvec);
- *status = (ret == 1) ? CMD_ERR_AMBIGUOUS
- : CMD_ERR_NO_MATCH ;
- return NULL ;
- } ;
- }
-
- /* Prepare match vector */
- /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */
-
- /* Make sure that cmd_vector is filtered based on current word */
- command = vector_get_item (tokens, index);
- if (command)
- match = cmd_filter(command, cmd_vector, index, any_match);
-
- /* Make description vector. */
- for (i = 0; i < vector_length (cmd_vector); i++)
- {
- vector strvec ;
-
- cmd_element = vector_get_item (cmd_vector, i) ;
- if (cmd_element == NULL)
- continue ;
-
- /* Ignore cmd_element if no tokens at index position.
- *
- * Deal with special case of possible <cr> completion.
- */
- strvec = cmd_element->strvec;
- if (index >= vector_length (strvec))
- {
- if (command == NULL && index == vector_length (strvec))
- {
- if (!desc_unique_string (matchvec, command_cr))
- vector_push_item(matchvec, &desc_cr);
- }
- continue ;
- } ;
-
- /* Check if command is completed. */
- unsigned int j;
- vector descvec = vector_get_item (strvec, index);
- struct desc *desc;
-
- for (j = 0; j < vector_length (descvec); j++)
- if ((desc = vector_get_item (descvec, j)))
- {
- const char *string;
-
- string = cmd_entry_function_desc (command, desc->cmd);
- if (string)
- {
- /* Uniqueness check */
- if (!desc_unique_string (matchvec, string))
- vector_push_item(matchvec, desc);
- }
- } ;
- } ;
-
- vector_free (cmd_vector);
-
- if (vector_length(matchvec) == 0)
- {
- vector_free (matchvec);
- *status = CMD_ERR_NO_MATCH;
- return NULL;
- }
-
- *status = CMD_SUCCESS;
- return matchvec;
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- cmd_parse_reset(parsed, false) ;
-
- return
-} ;
-
-/*------------------------------------------------------------------------------
- * Check LCD of matched command.
- *
- * Scan list of matched keywords, and by comparing them pair-wise, find the
- * longest common leading substring.
- *
- * Returns: 0 if zero or one matched keywords
- * length of longest common leading substring, otherwise.
- */
-static int
-cmd_lcd (vector matchvec)
-{
- int n ;
- int i ;
- int lcd ;
- char *sp, *sq, *ss ;
-
- n = vector_end(matchvec) ;
- if (n < 2)
- return 0 ;
-
- ss = vector_get_item(matchvec, 0) ;
- lcd = strlen(ss) ;
-
- for (i = 1 ; i < n ; i++)
- {
- sq = ss ;
- ss = vector_get_item(matchvec, i) ;
- sp = ss ;
-
- while ((*sp == *sq) && (*sp != '\0'))
- {
- ++sp ;
- ++sq ;
- } ;
-
- if (lcd > (sp - ss))
- lcd = (sp - ss) ;
- }
- return lcd;
-}
-
-/*------------------------------------------------------------------------------
- * Command line completion support.
- */
-static vector
-cmd_complete_command_real (vector tokens, int node, int *status)
-{
- unsigned int i;
- unsigned int ivl ;
- unsigned int last_ivl ;
- vector cmd_v ;
-#define INIT_MATCHVEC_SIZE 10
- vector matchvec;
- struct cmd_element *cmd_element;
- unsigned int index;
- struct desc *desc;
- vector descvec;
- char *token;
- int n ;
-
- /* Stop immediately if the tokens is empty. */
- if (vector_length (tokens) == 0)
- {
- *status = CMD_ERR_NO_MATCH;
- return NULL;
- }
-
- /* Take (shallow) copy of cmdvec for given node. */
- cmd_v = vector_copy (cmd_node_vector (cmdvec, node));
-
- /* First, filter upto, but excluding last token */
- last_ivl = vector_length (tokens) - 1;
-
- for (ivl = 0; ivl < last_ivl; ivl++)
- {
- enum match_type match;
- int ret;
-
- /* TODO: does this test make any sense ? */
- if ((token = vector_get_item (tokens, ivl)) == NULL)
- continue ;
-
- /* First try completion match, return best kind of match */
- index = ivl ;
- match = cmd_filter_by_completion (token, cmd_v, index) ;
-
- /* Eliminate all but the selected kind of match */
- ret = is_cmd_ambiguous (token, cmd_v, index, match) ;
-
- if (ret == 1)
- {
- /* ret == 1 => either token matches more than one keyword
- * or token matches more than one number range
- */
- vector_free (cmd_v);
- *status = CMD_ERR_AMBIGUOUS;
- return NULL;
- }
-#if 0
- /* For command completion purposes do not appear to care about
- * incomplete ipv4 or ipv6 prefixes (missing '/' or digits after).
- */
- else if (ret == 2)
- {
- vector_free (cmd_v);
- *status = CMD_ERR_NO_MATCH;
- return NULL;
- }
-#endif
- }
-
- /* Prepare match vector. */
- matchvec = vector_init (INIT_MATCHVEC_SIZE);
-
- /* Now we got into completion */
- index = last_ivl ;
- token = vector_get_item(tokens, last_ivl) ; /* is now the last token */
-
- for (i = 0; i < vector_length (cmd_v); i++)
- {
- unsigned int j;
- const char *string;
-
- if ((cmd_element = vector_get_item (cmd_v, i)) == NULL)
- continue ;
-
- descvec = vector_get_item (cmd_element->strvec, index);
- if (descvec == NULL)
- continue ;
-
- for (j = 0; j < vector_length (descvec); j++)
- {
- desc = vector_get_item (descvec, j) ;
- if (desc == NULL)
- continue ;
-
- string = cmd_entry_function(token, desc->cmd) ;
- if ((string != NULL) && cmd_unique_string(matchvec, string))
- cmd_add_to_strvec (matchvec, string) ;
- } ;
- } ;
-
- n = vector_length(matchvec) ; /* number of entries in the matchvec */
-
- /* We don't need cmd_v any more. */
- vector_free (cmd_v);
-
- /* No matched command */
- if (n == 0)
- {
- vector_free (matchvec);
-
- /* In case of 'command \t' pattern. Do you need '?' command at
- the end of the line. */
- if (*token == '\0')
- *status = CMD_COMPLETE_ALREADY;
- else
- *status = CMD_ERR_NO_MATCH;
- return NULL;
- }
-
- /* Only one matched */
- if (n == 1)
- {
- *status = CMD_COMPLETE_FULL_MATCH;
- return matchvec ;
- }
-
- /* Check LCD of matched strings. */
- if (token != NULL)
- {
- unsigned lcd = cmd_lcd (matchvec) ;
-
- if (lcd != 0)
- {
- if (strlen(token) < lcd)
- {
- char *lcdstr;
-
- lcdstr = XMALLOC (MTYPE_STRVEC, lcd + 1);
- memcpy (lcdstr, vector_get_item(matchvec, 0), lcd) ;
- lcdstr[lcd] = '\0';
-
- cmd_free_strvec(matchvec) ; /* discard the match vector */
-
- matchvec = vector_init (1);
- vector_push_item(matchvec, lcdstr) ;
-
- *status = CMD_COMPLETE_MATCH;
- return matchvec ;
- }
- }
- }
-
- *status = CMD_COMPLETE_LIST_MATCH;
- return matchvec ;
-}
-
-/*------------------------------------------------------------------------------
- * Can the current command be completed ?
- */
-extern vector
-cmd_complete_command (vector tokens, int node, int *status)
-{
- vector ret;
-
- if ( cmd_try_do_shortcut(node, vector_get_item(tokens, 0) ) )
- {
- vector shifted_tokens;
- unsigned int index;
-
- /* We can try it on enable node, cos' the vty is authenticated */
-
- shifted_tokens = vector_init (vector_count(tokens));
- /* use memcpy? */
- for (index = 1; index < vector_length (tokens); index++)
- {
- vector_set_index (shifted_tokens, index-1,
- vector_lookup(tokens, index)) ;
- }
-
- ret = cmd_complete_command_real (shifted_tokens, ENABLE_NODE, status);
-
- vector_free(shifted_tokens);
- return ret;
- }
-
- return cmd_complete_command_real (tokens, node, status);
-}
-
-/*------------------------------------------------------------------------------
- * Return parent node
- *
- * All nodes > CONFIG_NODE are descended from CONFIG_NODE
- */
-enum node_type
-node_parent ( enum node_type node )
-{
- assert (node > CONFIG_NODE);
-
- switch (node)
- {
- case BGP_VPNV4_NODE:
- case BGP_IPV4_NODE:
- case BGP_IPV4M_NODE:
- case BGP_IPV6_NODE:
- case BGP_IPV6M_NODE:
- return BGP_NODE;
-
- case KEYCHAIN_KEY_NODE:
- return KEYCHAIN_NODE;
-
- default:
- return CONFIG_NODE;
- } ;
-} ;
-
-/*==============================================================================
- * Parsing of command lines
- */
-
-/*------------------------------------------------------------------------------
- * Parse a command in the given "node", if possible, ready for execution.
- *
- * If 'strict': use cmd_filter_by_string()
- * otherwise: use cmd_filter_by_completion()
- *
- * If 'do': see if there is a 'do' at the front and proceed accordingly.
- *
- * If 'tree': move up the node tree to find command if not found in the
- * current node.
- */
-
-static enum cmd_return_code cmd_parse_phase_one(cmd_parsed parsed) ;
-static enum cmd_return_code cmd_parse_phase_two(struct cmd_parsed* parsed,
- bool strict) ;
-
-/*------------------------------------------------------------------------------
- * Parse a command in the given "node", or (if required) any of its ancestors.
- *
- * Returns: CMD_SUCCESS => successfully parsed command, and the result is
- * in the given parsed structure, ready for execution.
- *
- * NB: parsed->cnode may have changed.
- *
- * NB: parsed->cmd->daemon => daemon
- *
- * CMD_EMPTY => empty or comment line
- *
- * NB: parsed->cmd == NULL
- *
- * CMD_SUCCESS_DAEMON => parsed successfully. Something for vtysh ??
- *
- * CMD_ERR_NO_MATCH )
- * CMD_ERR_AMBIGUOUS ) failed to parse
- * CMD_ERR_INCOMPLETE )
- *
- * NB: if has failed to parse in the current node
- * and in any ancestor nodes, returns the error
- * from the attempt to parse in the current node
- * (parsed->cnode which is returned unchanged).
- *
- * NB: the command line MUST be preserved until the parsed command is no
- * longer required -- no copy is made.
- *
- * NB: expects to have free run of everything in the vty structure (except
- * the contents of the vty_io sub-structure) until the command completes.
- *
- * See elsewhere for description of parsed structure.
- */
-extern enum cmd_return_code
-cmd_parse_command(struct vty* vty, cmd_parse_type_t type)
-{
- enum cmd_return_code ret ;
- enum cmd_return_code first_ret ;
- cmd_parsed parsed ;
- cmd_token_type_t tok_total ;
- bool varflag ;
- unsigned int i, ivl ;
-
- /* Initialise the parsed structure -- assuming no 'do' */
- if (vty->parsed == NULL)
- parsed = vty->parsed = cmd_parse_init_new(NULL) ;
- else
- {
- parsed = vty->parsed ;
-
- parsed->cmd = NULL ;
- parsed->do_shortcut = false ;
-
- if (parsed->pipes != cmd_pipe_none)
- {
- parsed->pipes = cmd_pipe_none ;
- cmd_empty_parsed_tokens(parsed) ;
- } ;
- } ;
-
- /* Parse the line into tokens, set parsed->line, ->cnode & ->onode */
- tok_total = cmd_tokenise(parsed, vty->buf, vty->node) ;
-
- /* Stop immediately if line is empty apart from comment */
- if ((tok_total & ~cmd_tok_comment) == cmd_tok_null)
- return CMD_EMPTY ; /* NB: parsed->cmd == NULL */
-
- /* Level 1 parsing
- *
- * Strip quotes and escapes from all the tokens.
- */
- if (tok_total != cmd_tok_simple)
- {
- ret = cmd_parse_phase_one(parsed) ;
- if (ret != CMD_SUCCESS)
- return ret ;
- } ;
-
- /* If allowed to 'do', see if there.
- *
- * 'do' forces command to be parsed in ENABLE_NODE (if allowed)
- */
- if (type & cmd_parse_do)
- cmd_try_do_shortcut(parsed) ;
-
- /* Level 2 parsing
- * Try in the current node
- */
- ret = cmd_parse_phase_two(parsed, type) ;
-
- if (ret != CMD_SUCCESS)
- {
- if (((type & cmd_parse_tree) == 0) || parsed->do_shortcut)
- return ret ; /* done if not allowed to walk tree
- or just tried to parse a 'do' */
-
- /* Try in parent node(s) */
- first_ret = ret ;
-
- while (ret != CMD_SUCCESS)
- {
- if (parsed->cnode <= CONFIG_NODE)
- {
- parsed->cnode = parsed->onode ; /* restore node state */
- return first_ret ; /* return original result */
- } ;
-
- parsed->cnode = node_parent(parsed->cnode) ;
- ret = cmd_parse_phase_two(parsed, type) ;
- } ;
- } ;
-
- /* Parsed successfully -- construct the arg_vector */
-
- varflag = false ;
- ivl = vector_length(parsed->cmd->strvec) ;
-
- cmd_arg_vector_empty(parsed) ;
- for (i = 0; i < ivl ; i++)
- {
- bool take = varflag ;
-
- if (!varflag)
- {
- vector descvec = vector_get_item (parsed->cmd->strvec, i);
-
- if (vector_length (descvec) == 1)
- {
- struct desc *desc = vector_get_item (descvec, 0);
-
- if (CMD_VARARG (desc->cmd))
- take = varflag = true ;
- else
- take = (CMD_VARIABLE (desc->cmd) || CMD_OPTION (desc->cmd)) ;
- }
- else
- take = true ;
- }
-
- if (take)
- cmd_arg_vector_push(parsed,
- cmd_token_value(cmd_token_get(&parsed->tokens, i))) ;
- } ;
-
- /* Return appropriate form of success */
- return parsed->cmd->daemon ? CMD_SUCCESS_DAEMON
- : CMD_SUCCESS ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Phase 1 of command parsing
- *
- * * At start of line look for:
- *
- * * '<' -- pipe-in command of some sort
- *
- * Sets the pipe type and the read_pipe_tokens -- all tokens up to
- * '>', '|', '!' or '#'..
- *
- * Scan for '>', '|', '!' or '#'
- *
- * * '>' or '|' -- pipe-out command of some sort
- *
- * Collect type of pipe, and then all tokens up to '!' or '#'
- *
- *
- *
- * * '!', '#', comment -- discards
- *
- * Returns: CMD_SUCCESS -- parsed successfully
- * CMD_ERR_NO_MATCH )
- * CMD_ERR_AMBIGUOUS ) failed to parse
- * CMD_ERR_INCOMPLETE )
- */
-static enum cmd_return_code
-cmd_parse_phase_one(cmd_parsed parsed)
-{
-
-
-
-
-
-
-} ;
-
-/*------------------------------------------------------------------------------
- * Phase 2 of command parsing
- *
- * Takes a parsed structure, with the:
- *
- * cnode -- node to parse in
- * tokens -- the line broken into words
- * do_shortcut -- true if first word is 'do' (to be ignored)
- *
- * and parses either strictly or with command completion.
- *
- * Returns: CMD_SUCCESS -- parsed successfully
- * CMD_ERR_NO_MATCH )
- * CMD_ERR_AMBIGUOUS ) failed to parse
- * CMD_ERR_INCOMPLETE )
- */
-static enum cmd_return_code
-cmd_parse_phase_two(cmd_parsed parsed, cmd_parse_type_t type)
-{
- unsigned int i ;
- unsigned int ivl ;
- unsigned index ;
- vector cmd_v;
- struct cmd_element *cmd_element;
- struct cmd_element *matched_element;
- unsigned int matched_count, incomplete_count;
- enum match_type match ;
- enum match_type filter_level ;
- const char *command;
-
- /* Need number of tokens */
- ivl = cmd_token_count(&parsed->tokens) ;
-
- /* Make copy of command elements. */
- cmd_v = vector_copy (cmd_node_vector (cmdvec, parsed->cnode));
-
- /* Look for an unambiguous result */
- filter_level = (type & cmd_parse_strict) ? exact_match : any_match ;
- match = no_match ; /* in case of emptiness */
- for (index = 0 ; index < ivl; index++)
- {
- int ret ;
-
- command = cmd_token_string(cmd_token_get(&parsed->tokens, index)) ;
-
- match = cmd_filter(command, cmd_v, index, filter_level) ;
-
- if (match == vararg_match)
- break;
-
- ret = is_cmd_ambiguous (command, cmd_v, index, match);
-
- if (ret != 0)
- {
- assert((ret == 1) || (ret == 2)) ;
- vector_free (cmd_v);
- return (ret == 1) ? CMD_ERR_AMBIGUOUS : CMD_ERR_NO_MATCH ;
- }
- } ;
-
- /* Check matched count. */
- matched_element = NULL;
- matched_count = 0;
- incomplete_count = 0;
-
- for (i = 0; i < vector_length(cmd_v); i++)
- {
- cmd_element = vector_get_item(cmd_v, i) ;
- if (cmd_element == NULL)
- continue ;
-
- if (match == vararg_match || index >= cmd_element->cmdsize)
- {
- matched_element = cmd_element;
-#if 0
- printf ("DEBUG: %s\n", cmd_element->string);
-#endif
- matched_count++;
- }
- else
- {
- incomplete_count++;
- }
- } ;
-
- /* Finished with cmd_v. */
- vector_free (cmd_v);
-
- /* To execute command, matched_count must be 1. */
- if (matched_count != 1)
- {
- if (matched_count == 0)
- return (incomplete_count) ? CMD_ERR_INCOMPLETE : CMD_ERR_NO_MATCH ;
- else
- return CMD_ERR_AMBIGUOUS ;
- } ;
-
- /* Everything checks out... ready to execute command */
- parsed->cmd = matched_element ;
-
- return CMD_SUCCESS ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Dispatch a parsed command.
- *
- * Returns: command return code. NB: may be CMD_QUEUED (unless no_queue).
- *
- * NB: expects to have free run of everything in the vty structure (except
- * the contents of the vty_io sub-structure) until the command completes.
- */
-extern enum cmd_return_code
-cmd_dispatch(struct vty* vty, bool no_queue)
-{
- cmd_parsed parsed = vty->parsed ;
- enum cmd_return_code ret ;
-
- if (parsed->cmd == NULL)
- return CMD_SUCCESS ; /* NULL commands are easy */
-
- vty->node = parsed->cnode ;
-
- if (no_queue || !vty_cli_nexus)
- {
- ret = cmd_dispatch_call(vty) ;
- cmd_post_command(vty, ret) ;
- }
- else
- {
- /* Don't do it now, but send to bgp qpthread */
- if (parsed->cmd->attr & CMD_ATTR_CALL)
- cq_enqueue(vty, vty_cli_nexus) ;
- else
- cq_enqueue(vty, vty_cmd_nexus) ;
-
- ret = CMD_QUEUED ;
- } ;
-
- return ret ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Tidy up after executing command.
- *
- * This is separated out so that can be called when queued command completes.
- *
- * If have just processed a "do" shortcut command, and it has not set the
- * vty->node to something other than ENABLE_NODE, then restore to the original
- * state.
- *
- * Arguments: ret = CMD_XXXX -- NB: CMD_QUEUED => command revoked
- */
-extern void
-cmd_post_command(struct vty* vty, int ret)
-{
- if (vty->parsed->do_shortcut)
- {
- if (vty->node == ENABLE_NODE)
- vty->node = vty->parsed->onode ;
- } ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Parse and execute a command.
- *
- * The command is given by vty->buf and vty->node.
- *
- * Uses vty->parsed.
- *
- * -- use strict/completion parsing, as required.
- *
- * -- parse in current node and in ancestors, as required
- *
- * If does not find in any ancestor, return error from current node.
- *
- * -- implement the "do" shortcut, as required
- *
- * If qpthreads_enabled, then may queue the command rather than execute it
- * here.
- *
- * The vty->node may be changed during the execution of the command, and may
- * be returned changed once the command has completed.
- *
- * NB: expects to have free run of everything in the vty structure (except
- * the contents of the vty_io sub-structure) until the command completes.
- */
-extern enum cmd_return_code
-cmd_execute_command(struct vty *vty,
- enum cmd_parse_type type, struct cmd_element **cmd)
-{
- enum cmd_return_code ret ;
-
- /* Try to parse in vty->node or, if required, ancestors thereof. */
- ret = cmd_parse_command(vty, type) ;
-
- if (cmd != NULL)
- *cmd = vty->parsed->cmd ; /* for vtysh */
-
- if (ret == CMD_SUCCESS)
- ret = cmd_dispatch(vty, cmd_may_queue) ;
- else if (ret == CMD_EMPTY)
- ret = CMD_SUCCESS ;
-
- return ret ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Read configuration from file.
- *
- * In the qpthreads world this assumes that it is running with the vty
- * locked, and that all commands are to be executed directly.
- *
- * If the 'first_cmd' argument is not NULL it is the address of the first
- * command that is expected to appear. If the first command is not this, then
- * the 'first_cmd' is called with argv == NULL (and argc == 0) to signal the
- * command is being invoked by default.
- *
- * Command processing continues while CMD_SUCCESS is returned by the command
- * parser and command execution.
- *
- * If 'ignore_warning' is set, then any CMD_WARNING returned by command
- * execution is converted to CMD_SUCCESS. Note that any CMD_WARNING returned
- * by command parsing (or in execution of any default 'first_cmd').
- *
- * Returns: cmd_return_code for last command
- * vty->buf is last line processed
- * vty->lineno is number of last line processed (1 is first)
- *
- * If the file is empty, will return CMD_SUCCESS.
- *
- * Never returns CMD_EMPTY -- that counts as CMD_SUCCESS.
- *
- * If
- *
- * If return code is not CMD_SUCCESS, the the output buffering contains the
- * output from the last command attempted.
- */
-extern enum cmd_return_code
-config_from_file (struct vty *vty, FILE *fp, struct cmd_element* first_cmd,
- qstring buf, bool ignore_warning)
-{
- enum cmd_return_code ret;
-
- vty->buf = buf->body ;
- vty->lineno = 0 ;
-
- ret = CMD_SUCCESS ; /* in case file is empty */
- vty_out_clear(vty) ;
-
- while (fgets (buf->body, buf->size, fp))
- {
- ++vty->lineno ;
-
- /* Execute configuration command : this is strict match */
- ret = cmd_parse_command(vty, cmd_parse_strict + cmd_parse_tree) ;
-
- if (ret == CMD_EMPTY)
- continue ; /* skip empty/comment */
-
- if (ret != CMD_SUCCESS)
- break ; /* stop on *any* parsing issue */
-
- /* special handling before of first command */
- if (first_cmd != NULL)
- {
- if (first_cmd != vty->parsed->cmd)
- {
- ret = (*first_cmd->func)(first_cmd, vty, 0, NULL) ;
- if (ret != CMD_SUCCESS)
- break ; /* stop on *any* issue with "default" */
- } ;
- first_cmd = NULL ;
- }
-
- /* Standard command handling */
- ret = cmd_dispatch(vty, cmd_no_queue) ;
-
- if (ret != CMD_SUCCESS)
- {
- /* Ignore CMD_WARNING if required
- *
- * Ignore CMD_CLOSE at all times.
- */
- if ( ((ret == CMD_WARNING) && ignore_warning)
- || (ret == CMD_CLOSE) )
- ret = CMD_SUCCESS ; /* in case at EOF */
- else
- break ; /* stop */
- } ;
-
- vty_out_clear(vty) ;
- } ;
-
- if (ret == CMD_EMPTY)
- ret = CMD_SUCCESS ; /* OK if end on empty line */
-
- return ret ;
-} ;
-
-/*==============================================================================
- */
-
-static cmd_return_code_t cmd_fetch_command(struct vty* vty) ;
-
-/*------------------------------------------------------------------------------
- * Command Loop
- *
- * Read and dispatch commands until can no longer do so for whatever reason:
- *
- * - reached end of command stream -- CMD_CLOSE
- * - encounter error of some kind -- CMD_WARNING, CMD_ERROR, etc
- * - waiting for input to arrive -- CMD_WAIT_INPUT
- * - waiting for command to complete -- CMD_QUEUED
- * - waiting for output to complete -- ??
- *
- */
-extern cmd_return_code_t
-cmd_command_loop(struct vty *vty, struct cmd_element* first_cmd,
- qstring buf, bool ignore_warning)
-{
- cmd_return_code_t ret ;
-
- vty->buf = buf->body ;
- vty->lineno = 0 ;
-
-
- /*
- *
- */
-
- vty_out_clear(vty) ;
-
- while (1) {
-
- /* Fetch a command line */
-
- ret = cmd_fetch_command(vty) ;
-
- if (ret != CMD_SUCCESS)
- break ;
-
- ++vty->lineno ;
-
- /* Parse the command line we now have */
-
- ret = cmd_parse_command(vty, cmd_parse_strict + cmd_parse_tree) ;
-
- if (ret == CMD_EMPTY)
- continue ; /* skip empty/comment */
-
- if (ret != CMD_SUCCESS)
- break ; /* stop on *any* parsing issue */
-
- /* special handling before of first command */
- if (first_cmd != NULL)
- {
- if (first_cmd != vty->parsed->cmd)
- {
- ret = (*first_cmd->func)(first_cmd, vty, 0, NULL) ;
- if (ret != CMD_SUCCESS)
- break ; /* stop on *any* issue with "default" */
- } ;
- first_cmd = NULL ;
- } ;
-
- /* Reflect command line if required */
-
- /* Standard command handling */
-
- ret = cmd_dispatch(vty, cmd_no_queue) ;
-
- if (ret == CMD_QUEUED)
- break ;
-
- /* Output Handling..... */
-
-
- /* Return code handling.... */
-
- if (ret != CMD_SUCCESS)
- {
- if ((ret == CMD_WARNING) && !ignore_warning)
- break ;
- if (ret != CMD_CLOSE)
- break ;
- } ;
-
- vty_out_clear(vty) ;
- } ;
-
- return ret ;
-} ;
-
-
-/*------------------------------------------------------------------------------
- * Fetch the next command.
- *
- *
- *
- *
- */
-static cmd_return_code_t
-cmd_fetch_command(struct vty* vty)
-{
-
-
-
-
-} ;
-
-
-
-
-
-
-
-
-
-
-
/*============================================================================*/
/*----------------------------------------------------------------------------*/
@@ -2364,11 +859,10 @@ DEFUN_CALL (enable,
"Turn on privileged mode command\n")
{
/* If enable password is NULL, change to ENABLE_NODE */
- if ((host.enable == NULL && host.enable_encrypt == NULL) ||
- vty_shell_server(vty))
- vty_set_node(vty, ENABLE_NODE);
+ if ((host.enable == NULL) || (vty->type == VTY_SHELL_SERVER))
+ vty->node = ENABLE_NODE ;
else
- vty_set_node(vty, AUTH_ENABLE_NODE);
+ vty->node = AUTH_ENABLE_NODE ;
return CMD_SUCCESS;
}
@@ -2379,8 +873,8 @@ DEFUN_CALL (disable,
"disable",
"Turn off privileged mode command\n")
{
- if (vty_get_node(vty) == ENABLE_NODE)
- vty_set_node(vty, VIEW_NODE);
+ if (vty->node == ENABLE_NODE)
+ vty->node = VIEW_NODE;
return CMD_SUCCESS;
}
@@ -2390,7 +884,7 @@ DEFUN_CALL (config_exit,
"exit",
"Exit current mode and down to previous mode\n")
{
- return vty_cmd_exit(vty) ;
+ return cmd_exit(vty) ;
}
/* quit is alias of exit. */
@@ -2403,9 +897,9 @@ ALIAS_CALL (config_exit,
DEFUN_CALL (config_end,
config_end_cmd,
"end",
- "End current mode and change to enable mode.")
+ "End current mode and change to enable mode\n")
{
- return vty_cmd_end(vty) ;
+ return cmd_end(vty) ;
}
/* Show version. */
@@ -2453,8 +947,8 @@ DEFUN_CALL (config_list,
"Print command list\n")
{
unsigned int i;
- struct cmd_node *cnode = vector_get_item (cmdvec, vty_get_node(vty));
- struct cmd_element *cmd;
+ struct cmd_node *cnode = vector_get_item (node_vector, vty->node);
+ struct cmd_command *cmd;
for (i = 0; i < vector_length (cnode->cmd_vector); i++)
if ((cmd = vector_get_item (cnode->cmd_vector, i)) != NULL
@@ -2481,7 +975,7 @@ DEFUN (config_write_file,
int ret = CMD_WARNING;
/* Check and see if we are operating under vtysh configuration */
- if (host.config == NULL)
+ if (host.config_file == NULL)
{
vty_out (vty, "Can't save to configuration file, using vtysh.%s",
VTY_NEWLINE);
@@ -2489,7 +983,7 @@ DEFUN (config_write_file,
}
/* Get filename. */
- config_file = host.config;
+ config_file = host.config_file;
config_file_sav =
XMALLOC (MTYPE_TMP, strlen (config_file) + strlen (CONF_BACKUP_EXT) + 1);
@@ -2504,8 +998,7 @@ DEFUN (config_write_file,
fd = mkstemp (config_file_tmp);
if (fd < 0)
{
- vty_out (vty, "Can't open configuration file %s.%s", config_file_tmp,
- VTY_NEWLINE);
+ vty_out (vty, "Can't open configuration file %s.\n", config_file_tmp) ;
goto finished;
}
@@ -2517,15 +1010,18 @@ DEFUN (config_write_file,
vty_time_print (vty, 1);
vty_out (vty, "!\n");
- for (i = 0; i < vector_length (cmdvec); i++)
- if ((node = vector_get_item (cmdvec, i)) && node->func)
- {
- if ((*node->func) (vty))
- vty_out (vty, "!\n");
- }
+ for (i = 0; i < vector_length (node_vector); i++)
+ {
+ if ((node = vector_get_item (node_vector, i)) && node->config_write)
+ {
+ if ((*node->config_write) (vty))
+ vty_out (vty, "!\n");
+
+ vty_cmd_out_push(vty) ; /* Push stuff so far */
+ }
+ } ;
err = vty_close_config_write(vty) ;
- close(fd) ;
if (err != 0)
{
@@ -2611,31 +1107,34 @@ DEFUN (config_write_terminal,
"Write to terminal\n")
{
unsigned int i;
- struct cmd_node *node;
+ bool vtysh_config ;
- if (vty_shell_serv(vty))
- {
- for (i = 0; i < vector_length (cmdvec); i++)
- if ((node = vector_get_item (cmdvec, i)) && node->func && node->vtysh)
- {
- if ((*node->func) (vty))
- vty_out (vty, "!%s", VTY_NEWLINE);
- }
- }
- else
+ vtysh_config = (vty->type == VTY_SHELL_SERVER) ;
+
+ if (!vtysh_config)
+ vty_out (vty, "\n"
+ "Current configuration:\n"
+ "!\n") ;
+
+ for (i = 0 ; i < vector_length(node_vector) ; i++)
{
- vty_out (vty, "%sCurrent configuration:%s", VTY_NEWLINE,
- VTY_NEWLINE);
- vty_out (vty, "!%s", VTY_NEWLINE);
-
- for (i = 0; i < vector_length (cmdvec); i++)
- if ((node = vector_get_item (cmdvec, i)) && node->func)
- {
- if ((*node->func) (vty))
- vty_out (vty, "!%s", VTY_NEWLINE);
- }
- vty_out (vty, "end%s",VTY_NEWLINE);
- }
+ cmd_node node ;
+
+ node = vector_get_item(node_vector, i) ;
+
+ if (node == NULL)
+ continue ;
+
+ if (vtysh_config && !node->config_to_vtysh)
+ continue ;
+
+ if ((*node->config_write != NULL) && ((*node->config_write)(vty) != 0))
+ vty_out (vty, "!\n") ;
+ } ;
+
+ if (!vtysh_config)
+ vty_out (vty, "end\n") ;
+
return CMD_SUCCESS;
}
@@ -2656,11 +1155,11 @@ DEFUN (show_startup_config,
char buf[BUFSIZ];
FILE *confp;
- confp = fopen (host.config, "r");
+ confp = fopen (host.config_file, "r");
if (confp == NULL)
{
vty_out (vty, "Can't open configuration file [%s]%s",
- host.config, VTY_NEWLINE);
+ host.config_file, VTY_NEWLINE);
return CMD_WARNING;
}
@@ -2689,21 +1188,11 @@ DEFUN_CALL (config_hostname,
{
if (!isalpha((int) *argv[0]))
{
- vty_out (vty, "Please specify string starting with alphabet%s", VTY_NEWLINE);
+ vty_out (vty, "Please specify string starting with alphabet\n");
return CMD_WARNING;
}
- VTY_LOCK() ;
-
- if (host.name)
- XFREE (MTYPE_HOST, host.name);
-
- host.name = XSTRDUP (MTYPE_HOST, argv[0]);
- uty_set_host_name(host.name) ;
-
- VTY_UNLOCK() ;
-
- return CMD_SUCCESS;
+ return cmd_set_host_name(argv[0]) ;
}
DEFUN_CALL (config_no_hostname,
@@ -2713,26 +1202,18 @@ DEFUN_CALL (config_no_hostname,
"Reset system's network name\n"
"Host name of this router\n")
{
- VTY_LOCK() ;
-
- if (host.name)
- XFREE (MTYPE_HOST, host.name);
- host.name = NULL;
- uty_set_host_name(host.name) ;
-
- VTY_UNLOCK() ;
-
- return CMD_SUCCESS;
+ return cmd_set_host_name(NULL) ;
}
-/* VTY interface password set. */
-DEFUN_CALL (config_password, password_cmd,
- "password (8|) WORD",
- "Assign the terminal connection password\n"
- "Specifies a HIDDEN password will follow\n"
- "dummy string \n"
- "The HIDDEN line password string\n")
+/*------------------------------------------------------------------------------
+ * Password setting function -- common for password and enable password.
+ */
+static cmd_return_code_t
+do_set_password(vty vty, int argc, argv_t argv, char** p_password,
+ bool* p_encrypted)
{
+ cmd_return_code_t ret ;
+
/* Argument check. */
if (argc == 0)
{
@@ -2740,47 +1221,63 @@ DEFUN_CALL (config_password, password_cmd,
return CMD_WARNING;
}
+ VTY_LOCK() ;
+ ret = CMD_SUCCESS ;
+
if (argc == 2)
{
+ /* Encrypted password argument */
+
if (*argv[0] == '8')
- {
- if (host.password)
- XFREE (MTYPE_HOST, host.password);
- host.password = NULL;
- if (host.password_encrypt)
- XFREE (MTYPE_HOST, host.password_encrypt);
- host.password_encrypt = XSTRDUP (MTYPE_HOST, argv[1]);
- return CMD_SUCCESS;
- }
+ {
+ XFREE(MTYPE_HOST, *p_password);
+ *p_encrypted = true ;
+ *p_password = XSTRDUP(MTYPE_HOST, argv[1]);
+ }
else
- {
- vty_out (vty, "Unknown encryption type.%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
+ {
+ vty_out(vty, "Unknown encryption type.\n");
+ ret = CMD_WARNING;
+ }
}
-
- if (!isalnum ((int) *argv[0]))
+ else
{
- vty_out (vty,
- "Please specify string starting with alphanumeric%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
+ /* Plaintext password argument */
+
+ if (!isalnum ((int) *argv[0]))
+ {
+ vty_out(vty, "Please specify string starting with alphanumeric\n");
+ ret = CMD_WARNING ;
+ }
+ else
+ {
+ /* If host.encrypt, only keeps the encrypted password. */
- if (host.password)
- XFREE (MTYPE_HOST, host.password);
- host.password = NULL;
+ XFREE (MTYPE_HOST, *p_password);
- if (host.encrypt)
- {
- if (host.password_encrypt)
- XFREE (MTYPE_HOST, host.password_encrypt);
- host.password_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (argv[0]));
- }
- else
- host.password = XSTRDUP (MTYPE_HOST, argv[0]);
+ *p_encrypted = host.encrypt ;
+ if (*p_encrypted)
+ *p_password = XSTRDUP (MTYPE_HOST, zencrypt (argv[0]));
+ else
+ *p_password = XSTRDUP (MTYPE_HOST, argv[0]);
+ } ;
+ } ;
- return CMD_SUCCESS;
-}
+ VTY_UNLOCK() ;
+ return ret ;
+} ;
+
+/* VTY interface password set. */
+DEFUN_CALL (config_password, password_cmd,
+ "password (8) WORD",
+ "Assign the terminal connection password\n"
+ "Specifies a HIDDEN password will follow\n"
+ "dummy string \n"
+ "The HIDDEN line password string\n")
+{
+ return do_set_password(vty, argc, argv, &host.password,
+ &host.password_encrypted) ;
+} ;
ALIAS_CALL (config_password, password_text_cmd,
"password LINE",
@@ -2789,65 +1286,16 @@ ALIAS_CALL (config_password, password_text_cmd,
/* VTY enable password set. */
DEFUN_CALL (config_enable_password, enable_password_cmd,
- "enable password (8|) WORD",
+ "enable password (8) WORD",
"Modify enable password parameters\n"
"Assign the privileged level password\n"
"Specifies a HIDDEN password will follow\n"
"dummy string \n"
"The HIDDEN 'enable' password string\n")
{
- /* Argument check. */
- if (argc == 0)
- {
- vty_out (vty, "Please specify password.%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- /* Crypt type is specified. */
- if (argc == 2)
- {
- if (*argv[0] == '8')
- {
- if (host.enable)
- XFREE (MTYPE_HOST, host.enable);
- host.enable = NULL;
-
- if (host.enable_encrypt)
- XFREE (MTYPE_HOST, host.enable_encrypt);
- host.enable_encrypt = XSTRDUP (MTYPE_HOST, argv[1]);
-
- return CMD_SUCCESS;
- }
- else
- {
- vty_out (vty, "Unknown encryption type.%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
- }
-
- if (!isalnum ((int) *argv[0]))
- {
- vty_out (vty,
- "Please specify string starting with alphanumeric%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- if (host.enable)
- XFREE (MTYPE_HOST, host.enable);
- host.enable = NULL;
-
- /* Plain password input. */
- if (host.encrypt)
- {
- if (host.enable_encrypt)
- XFREE (MTYPE_HOST, host.enable_encrypt);
- host.enable_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (argv[0]));
- }
- else
- host.enable = XSTRDUP (MTYPE_HOST, argv[0]);
-
- return CMD_SUCCESS;
-}
+ return do_set_password(vty, argc, argv, &host.enable,
+ &host.enable_encrypted) ;
+} ;
ALIAS_CALL (config_enable_password,
enable_password_text_cmd,
@@ -2863,14 +1311,12 @@ DEFUN_CALL (no_config_enable_password, no_enable_password_cmd,
"Modify enable password parameters\n"
"Assign the privileged level password\n")
{
- if (host.enable)
- XFREE (MTYPE_HOST, host.enable);
- host.enable = NULL;
+ VTY_LOCK() ;
- if (host.enable_encrypt)
- XFREE (MTYPE_HOST, host.enable_encrypt);
- host.enable_encrypt = NULL;
+ host.enable_encrypted = false ;
+ XFREE (MTYPE_HOST, host.enable);
+ VTY_UNLOCK() ;
return CMD_SUCCESS;
}
@@ -2880,24 +1326,30 @@ DEFUN_CALL (service_password_encrypt,
"Set up miscellaneous service\n"
"Enable encrypted passwords\n")
{
- if (host.encrypt)
- return CMD_SUCCESS;
+ VTY_LOCK() ;
- host.encrypt = 1;
+ host.encrypt = true ;
- if (host.password)
+ /* If we have an unencrypted password in hand, convert that now.
+ * If not, retain any already encrypted password.
+ */
+ if (!host.password_encrypted && (host.password != NULL))
{
- if (host.password_encrypt)
- XFREE (MTYPE_HOST, host.password_encrypt);
- host.password_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (host.password));
- }
- if (host.enable)
+ char* plain = host.password ;
+ host.password = XSTRDUP(MTYPE_HOST, zencrypt (plain)) ;
+ XFREE(MTYPE_HOST, plain) ;
+ host.password_encrypted = true ;
+ } ;
+
+ if (!host.enable_encrypted && (host.enable != NULL))
{
- if (host.enable_encrypt)
- XFREE (MTYPE_HOST, host.enable_encrypt);
- host.enable_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (host.enable));
- }
+ char* plain = host.enable ;
+ host.enable = XSTRDUP(MTYPE_HOST, zencrypt (plain)) ;
+ XFREE(MTYPE_HOST, plain) ;
+ host.enable_encrypted = true ;
+ } ;
+ VTY_UNLOCK() ;
return CMD_SUCCESS;
}
@@ -2908,19 +1360,13 @@ DEFUN_CALL (no_service_password_encrypt,
"Set up miscellaneous service\n"
"Enable encrypted passwords\n")
{
- if (! host.encrypt)
- return CMD_SUCCESS;
-
- host.encrypt = 0;
+ VTY_LOCK() ;
- if (host.password_encrypt)
- XFREE (MTYPE_HOST, host.password_encrypt);
- host.password_encrypt = NULL;
+ /* Keep any existing passwords, encrypted or not. */
- if (host.enable_encrypt)
- XFREE (MTYPE_HOST, host.enable_encrypt);
- host.enable_encrypt = NULL;
+ host.encrypt = false ;
+ VTY_UNLOCK() ;
return CMD_SUCCESS;
}
@@ -2954,6 +1400,15 @@ DEFUN_CALL (config_terminal_no_length, config_terminal_no_length_cmd,
return CMD_SUCCESS;
}
+static cmd_return_code_t
+set_host_lines(int lines)
+{
+ VTY_LOCK() ;
+ host.lines = lines ;
+ VTY_UNLOCK() ;
+ return CMD_SUCCESS ;
+} ;
+
DEFUN_CALL (service_terminal_length, service_terminal_length_cmd,
"service terminal-length <0-512>",
"Set up miscellaneous service\n"
@@ -2969,10 +1424,9 @@ DEFUN_CALL (service_terminal_length, service_terminal_length_cmd,
vty_out (vty, "length is malformed%s", VTY_NEWLINE);
return CMD_WARNING;
}
- host.lines = lines;
- return CMD_SUCCESS;
-}
+ return set_host_lines(lines) ;
+} ;
DEFUN_CALL (no_service_terminal_length, no_service_terminal_length_cmd,
"no service terminal-length [<0-512>]",
@@ -2981,8 +1435,7 @@ DEFUN_CALL (no_service_terminal_length, no_service_terminal_length_cmd,
"System wide terminal length configuration\n"
"Number of lines of VTY (0 means no line control)\n")
{
- host.lines = -1;
- return CMD_SUCCESS;
+ return set_host_lines(-1) ;
}
DEFUN_HID_CALL (do_echo,
@@ -2993,10 +1446,10 @@ DEFUN_HID_CALL (do_echo,
{
char *message;
- vty_out (vty, "%s%s", ((message = argv_concat(argv, argc, 0)) ? message : ""),
- VTY_NEWLINE);
- if (message)
- XFREE(MTYPE_TMP, message);
+ message = argv_concat(argv, argc, 0) ;
+ vty_out (vty, "%s\n", message ? message : "") ;
+ XFREE(MTYPE_TMP, message);
+
return CMD_SUCCESS;
}
@@ -3015,8 +1468,7 @@ DEFUN_CALL (config_logmsg,
message = argv_concat(argv, argc, 1);
zlog(NULL, level, "%s", (message ? message : ""));
- if (message)
- XFREE(MTYPE_TMP, message);
+ XFREE(MTYPE_TMP, message);
return CMD_SUCCESS;
}
@@ -3436,6 +1888,7 @@ DEFUN_CALL (banner_motd_file,
{
if (host.motdfile)
XFREE (MTYPE_HOST, host.motdfile);
+
host.motdfile = XSTRDUP (MTYPE_HOST, argv[0]);
return CMD_SUCCESS;
@@ -3448,7 +1901,7 @@ DEFUN_CALL (banner_motd_default,
"Strings for motd\n"
"Default string\n")
{
- host.motd = default_motd;
+ host.motd = DEFAULT_MOTD ;
return CMD_SUCCESS;
}
@@ -3467,12 +1920,12 @@ DEFUN_CALL (no_banner_motd,
}
/* Set config filename. Called from vty.c */
-void
-host_config_set (char *filename)
+extern void
+host_config_set (const char* file_name)
{
- if (host.config)
- XFREE (MTYPE_HOST, host.config);
- host.config = XSTRDUP (MTYPE_HOST, filename);
+ if (host.config_file)
+ XFREE (MTYPE_HOST, host.config_file);
+ host.config_file = XSTRDUP (MTYPE_HOST, file_name);
}
void
@@ -3495,25 +1948,12 @@ install_default (enum node_type node)
void
cmd_init (int terminal)
{
- command_cr = XSTRDUP(MTYPE_STRVEC, "<cr>");
- desc_cr.cmd = command_cr;
- desc_cr.str = XSTRDUP(MTYPE_STRVEC, "");
+ /* Allocate initial top vector of commands. */
+ node_vector = vector_init(0);
- /* Allocate initial top vector of commands. */
- cmdvec = vector_init(0);
+ /* Default host value settings are already set, see above */
- /* Allocate vector of spare qstrings for tokens */
- cmd_spare_tokens_init() ;
-
- /* Default host value settings. */
- host.name = NULL;
- host.password = NULL;
- host.enable = NULL;
- host.logfile = NULL;
- host.config = NULL;
- host.lines = -1;
- host.motd = default_motd;
- host.motdfile = NULL;
+ cmd_get_sys_host_name() ; /* start with system name & name_gen == 1 */
/* Install top nodes. */
install_node (&view_node, NULL);
@@ -3622,85 +2062,46 @@ cmd_init (int terminal)
void
cmd_terminate ()
{
- unsigned int i, j, k, l;
- struct cmd_node *cmd_node;
- struct cmd_element *cmd_element;
- struct desc *desc;
- vector cmd_node_v, cmd_element_v, desc_v;
+ cmd_node cmd_node;
+ cmd_command cmd ;
- cmd_spare_tokens_free() ;
-
- if (cmdvec)
+ while ((cmd_node = vector_ream(node_vector, free_it)) != NULL)
{
- for (i = 0; i < vector_length (cmdvec); i++)
+ while ((cmd = vector_ream(cmd_node->cmd_vector, free_it)) != NULL)
{
- cmd_node = vector_get_item (cmdvec, i) ;
- if (cmd_node == NULL)
- continue ;
-
- cmd_node_v = cmd_node->cmd_vector;
+ /* Note that each cmd is a static structure, which may appear in
+ * more than one cmd_vector.
+ */
+ cmd_item next_item ;
- for (j = 0; j < vector_length (cmd_node_v); j++)
+ while ((next_item = vector_ream(cmd->items, free_it)) != NULL)
{
- cmd_element = vector_get_item (cmd_node_v, j) ;
- if (cmd_element == NULL)
- continue ;
-
- cmd_element_v = cmd_element->strvec ;
- if (cmd_element_v == NULL)
- continue ;
-
- for (k = 0; k < vector_length (cmd_element_v); k++)
+ do
{
- desc_v = vector_get_item (cmd_element_v, k) ;
- if (desc_v == NULL)
- continue ;
-
- for (l = 0; l < vector_length (desc_v); l++)
- {
- desc = vector_get_item (desc_v, l) ;
- if (desc == NULL)
- continue ;
-
- if (desc->cmd)
- XFREE (MTYPE_STRVEC, desc->cmd);
- if (desc->str)
- XFREE (MTYPE_STRVEC, desc->str);
-
- XFREE (MTYPE_DESC, desc);
- } ;
- vector_free (desc_v);
- } ;
-
- cmd_element->strvec = NULL;
- vector_free (cmd_element_v);
+ cmd_item item ;
+ item = next_item ;
+ next_item = item->next ;
+ XFREE(MTYPE_CMD_ITEM, item);
+ }
+ while (next_item != NULL) ;
} ;
- vector_free (cmd_node_v);
- } ;
+ cmd->items = NULL ; /* gone */
- vector_free (cmdvec);
- cmdvec = NULL;
+ XFREE (MTYPE_CMD_STRING, cmd->r_string) ; /* sets NULL */
+ XFREE (MTYPE_CMD_STRING, cmd->r_doc) ;
+ } ;
}
- if (command_cr)
- XFREE(MTYPE_STRVEC, command_cr);
- if (desc_cr.str)
- XFREE(MTYPE_STRVEC, desc_cr.str);
- if (host.name)
- XFREE (MTYPE_HOST, host.name);
- if (host.password)
- XFREE (MTYPE_HOST, host.password);
- if (host.password_encrypt)
- XFREE (MTYPE_HOST, host.password_encrypt);
- if (host.enable)
- XFREE (MTYPE_HOST, host.enable);
- if (host.enable_encrypt)
- XFREE (MTYPE_HOST, host.enable_encrypt);
- if (host.logfile)
- XFREE (MTYPE_HOST, host.logfile);
- if (host.motdfile)
- XFREE (MTYPE_HOST, host.motdfile);
- if (host.config)
- XFREE (MTYPE_HOST, host.config);
-}
+ node_vector = NULL ;
+
+ XFREE(MTYPE_HOST, host.name);
+ XFREE(MTYPE_HOST, host.password);
+ XFREE(MTYPE_HOST, host.enable);
+ XFREE(MTYPE_HOST, host.logfile);
+ XFREE(MTYPE_HOST, host.motdfile);
+ XFREE(MTYPE_HOST, host.config_file);
+ XFREE(MTYPE_HOST, host.vty_accesslist_name);
+ XFREE(MTYPE_HOST, host.vty_ipv6_accesslist_name);
+ XFREE(MTYPE_HOST, host.vty_cwd);
+} ;
diff --git a/lib/command.h b/lib/command.h
index fe4f0a56..89644ce7 100644
--- a/lib/command.h
+++ b/lib/command.h
@@ -25,168 +25,39 @@
#include "misc.h"
-#include "node_type.h"
+#include "command_common.h"
+#include "vty.h"
+
#include "vector.h"
#include "qstring.h"
-struct vty ; /* in case command.h expanded first */
-
-/* Host configuration variable */
-struct host
-{
- /* Host name of this router. */
- char *name;
-
- /* Password for vty interface. */
- char *password;
- char *password_encrypt;
-
- /* Enable password */
- char *enable;
- char *enable_encrypt;
-
- /* System wide terminal lines. */
- int lines;
-
- /* Log filename. */
- char *logfile;
-
- /* config file name of this host */
- char *config;
-
- /* Flags for services */
- int advanced;
- int encrypt;
-
- /* Banner configuration. */
- const char *motd;
- char *motdfile;
-};
-
-/* Node which has some commands and prompt string and configuration
- function pointer . */
-struct cmd_node
-{
- /* Node index. */
- enum node_type node;
-
- /* Prompt character at vty interface. */
- const char *prompt;
-
- /* Is this node's configuration goes to vtysh ? */
- int vtysh;
-
- /* Node's configuration write function */
- int (*func) (struct vty *);
-
- /* Vector of this node's command list. */
- vector cmd_vector;
-};
-
-enum
-{
- CMD_ATTR_SIMPLE = 0x00,
- /* bit significant */
- CMD_ATTR_DEPRECATED = 0x01,
- CMD_ATTR_HIDDEN = 0x02,
- CMD_ATTR_CALL = 0x04,
-};
-
-/* Return values for command handling.
- *
- * NB: when a command is executed it may return CMD_SUCCESS or CMD_WARNING.
- *
- * In both cases any output required (including any warning or error
- * messages) must already have been output.
- *
- * All other return codes are for use within the command handler.
+/*==============================================================================
+ * This contains everything required for command handling from outside the
+ * library.
*/
-enum cmd_return_code
-{
- CMD_SUCCESS = 0,
- CMD_WARNING = 1,
- CMD_ERROR,
-
- CMD_EMPTY,
- CMD_SUCCESS_DAEMON,
-
- CMD_WAIT_INPUT,
- CMD_CLOSE,
- CMD_QUEUED,
-
- CMD_ERR_NO_MATCH,
- CMD_ERR_AMBIGUOUS,
- CMD_ERR_INCOMPLETE,
-
- CMD_COMPLETE_FULL_MATCH,
- CMD_COMPLETE_MATCH,
- CMD_COMPLETE_LIST_MATCH,
- CMD_COMPLETE_ALREADY
-} ;
-
-typedef enum cmd_return_code cmd_return_code_t ;
-
-#define MSG_CMD_ERR_AMBIGUOUS "Ambiguous command"
-#define MSG_CMD_ERR_NO_MATCH "Unrecognised command"
-#define MSG_CMD_ERR_NO_MATCH_old "There is no matched command"
-
-/* Structure of command element. */
-
-struct cmd_element ;
-typedef struct cmd_element* cmd_element ;
-
-typedef const char* const argv_t[] ;
-
-#define DEFUN_CMD_ARG_UNUSED __attribute__ ((unused))
-#define DEFUN_CMD_FUNCTION(name) \
- enum cmd_return_code name (cmd_element self DEFUN_CMD_ARG_UNUSED, \
- struct vty* vty DEFUN_CMD_ARG_UNUSED, \
- int argc DEFUN_CMD_ARG_UNUSED, \
- argv_t argv DEFUN_CMD_ARG_UNUSED)
-
-typedef DEFUN_CMD_FUNCTION((cmd_function)) ;
-
-struct cmd_element
-{
- const char* string ; /* Command specification by string. */
- cmd_function* func ;
- const char* doc ; /* Documentation of this command. */
- int daemon ; /* Daemon to which this command belong. */
- vector strvec ; /* Vector of vectors of struct desc */
- unsigned int cmdsize ; /* Command index count. */
- char* config ; /* Configuration string */
- vector subconfig ; /* Sub configuration string */
- u_char attr ; /* Command attributes */
-};
-
-/* Command description structure. */
-struct desc
-{
- char* cmd; /* Command string. */
- char* str; /* Command's description. */
-};
-
/*------------------------------------------------------------------------------
- * Can now include these
+ * Turn off these macros when using cpp with extract.pl
*/
-
-#include "vty.h"
-#include "uty.h"
-
-/*----------------------------------------------------------------------------*/
-/* Turn off these macros when uisng cpp with extract.pl */
#ifndef VTYSH_EXTRACT_PL
-/* helper defines for end-user DEFUN* macros */
-#define DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attrs, dnum) \
- struct cmd_element cmdname = \
+/* helper defines for end-user DEFUN* macros */
+#define DEFUN_CMD_COMMAND(funcname, cmdname, cmdstr, helpstr, attrs, dnum) \
+ struct cmd_command cmdname = \
{ \
- .string = cmdstr, \
- .func = funcname, \
- .doc = helpstr, \
- .attr = attrs, \
- .daemon = dnum, \
- };
+ .string = cmdstr, \
+ .func = funcname, \
+ .doc = helpstr, \
+ .attr = attrs, \
+ .daemon = dnum, \
+ \
+ .items = NULL, \
+ .nt_min = 0, \
+ .nt = 0, \
+ .nt_max = 0, \
+ .vararg = NULL, \
+ .r_string = NULL, \
+ .r_doc = NULL, \
+ } ;
#define DEFUN_CMD_FUNC_DECL(funcname) \
static cmd_function funcname;
@@ -194,50 +65,52 @@ struct desc
#define DEFUN_CMD_FUNC_TEXT(funcname) \
static DEFUN_CMD_FUNCTION(funcname)
-/* DEFUN for vty command interafce. Little bit hacky ;-). */
+/* DEFUN for vty command interface. Little bit hacky ;-). */
#define DEFUN(funcname, cmdname, cmdstr, helpstr) \
DEFUN_CMD_FUNC_DECL(funcname) \
- DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) \
+ DEFUN_CMD_COMMAND(funcname, cmdname, cmdstr, helpstr, 0, 0) \
DEFUN_CMD_FUNC_TEXT(funcname)
#define DEFUN_ATTR(funcname, cmdname, cmdstr, helpstr, attr) \
DEFUN_CMD_FUNC_DECL(funcname) \
- DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, 0) \
+ DEFUN_CMD_COMMAND(funcname, cmdname, cmdstr, helpstr, attr, 0) \
DEFUN_CMD_FUNC_TEXT(funcname)
+#define DEFUN_CALL(funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_ATTR (funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DIRECT)
+
#define DEFUN_HIDDEN(funcname, cmdname, cmdstr, helpstr) \
DEFUN_ATTR (funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN)
#define DEFUN_HID_CALL(funcname, cmdname, cmdstr, helpstr) \
- DEFUN_ATTR (funcname, cmdname, cmdstr, helpstr, (CMD_ATTR_CALL | CMD_ATTR_HIDDEN))
+ DEFUN_ATTR (funcname, cmdname, cmdstr, helpstr, \
+ (CMD_ATTR_DIRECT | CMD_ATTR_HIDDEN))
#define DEFUN_DEPRECATED(funcname, cmdname, cmdstr, helpstr) \
DEFUN_ATTR (funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED)
#define DEFUN_DEP_CALL(funcname, cmdname, cmdstr, helpstr) \
- DEFUN_ATTR (funcname, cmdname, cmdstr, helpstr, (CMD_ATTR_CALL | CMD_ATTR_DEPRECATED))
-
-#define DEFUN_CALL(funcname, cmdname, cmdstr, helpstr) \
- DEFUN_ATTR (funcname, cmdname, cmdstr, helpstr, CMD_ATTR_CALL)
+ DEFUN_ATTR (funcname, cmdname, cmdstr, helpstr, \
+ (CMD_ATTR_DIRECT | CMD_ATTR_DEPRECATED))
-/* DEFUN_NOSH for commands that vtysh should ignore */
+/* DEFUN_NOSH for commands that vtysh should ignore */
#define DEFUN_NOSH(funcname, cmdname, cmdstr, helpstr) \
DEFUN(funcname, cmdname, cmdstr, helpstr)
-/* DEFSH for vtysh. */
+/* DEFSH for vtysh. */
#define DEFSH(daemon, cmdname, cmdstr, helpstr) \
- DEFUN_CMD_ELEMENT(NULL, cmdname, cmdstr, helpstr, 0, daemon)
+ DEFUN_CMD_COMMAND(NULL, cmdname, cmdstr, helpstr, 0, daemon)
-/* DEFUN + DEFSH */
+/* DEFUN + DEFSH */
#define DEFUNSH(daemon, funcname, cmdname, cmdstr, helpstr) \
DEFUN_CMD_FUNC_DECL(funcname) \
- DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, daemon) \
+ DEFUN_CMD_COMMAND(funcname, cmdname, cmdstr, helpstr, 0, daemon) \
DEFUN_CMD_FUNC_TEXT(funcname)
-/* DEFUN + DEFSH with attributes */
+/* DEFUN + DEFSH with attributes */
#define DEFUNSH_ATTR(daemon, funcname, cmdname, cmdstr, helpstr, attr) \
DEFUN_CMD_FUNC_DECL(funcname) \
- DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, daemon) \
+ DEFUN_CMD_COMMAND(funcname, cmdname, cmdstr, helpstr, attr, daemon) \
DEFUN_CMD_FUNC_TEXT(funcname)
#define DEFUNSH_HIDDEN(daemon, funcname, cmdname, cmdstr, helpstr) \
@@ -246,44 +119,34 @@ struct desc
#define DEFUNSH_DEPRECATED(daemon, funcname, cmdname, cmdstr, helpstr) \
DEFUNSH_ATTR (daemon, funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED)
-/* ALIAS macro which define existing command's alias. */
+/* ALIAS macro which define existing command's alias. */
#define ALIAS(funcname, cmdname, cmdstr, helpstr) \
- DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0)
+ DEFUN_CMD_COMMAND(funcname, cmdname, cmdstr, helpstr, 0, 0)
#define ALIAS_ATTR(funcname, cmdname, cmdstr, helpstr, attr) \
- DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, 0)
+ DEFUN_CMD_COMMAND(funcname, cmdname, cmdstr, helpstr, attr, 0)
+
+#define ALIAS_CALL(funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_CMD_COMMAND(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DIRECT, 0)
#define ALIAS_HIDDEN(funcname, cmdname, cmdstr, helpstr) \
- DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN, 0)
+ DEFUN_CMD_COMMAND(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN, 0)
#define ALIAS_DEPRECATED(funcname, cmdname, cmdstr, helpstr) \
- DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED, 0)
-
-#define ALIAS_CALL(funcname, cmdname, cmdstr, helpstr) \
- DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_CALL, 0)
+ DEFUN_CMD_COMMAND(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED, 0)
#define ALIAS_SH(daemon, funcname, cmdname, cmdstr, helpstr) \
- DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, daemon)
+ DEFUN_CMD_COMMAND(funcname, cmdname, cmdstr, helpstr, 0, daemon)
#define ALIAS_SH_HIDDEN(daemon, funcname, cmdname, cmdstr, helpstr) \
- DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN, daemon)
+ DEFUN_CMD_COMMAND(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN, daemon)
#define ALIAS_SH_DEPRECATED(daemon, funcname, cmdname, cmdstr, helpstr) \
- DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED, daemon)
+ DEFUN_CMD_COMMAND(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED,\
+ daemon)
#endif /* VTYSH_EXTRACT_PL */
-/* Some macroes */
-#define CMD_OPTION(S) ((S[0]) == '[')
-#define CMD_VARIABLE(S) (((S[0]) >= 'A' && (S[0]) <= 'Z') || ((S[0]) == '<'))
-#define CMD_VARARG(S) ((S[0]) == '.')
-#define CMD_RANGE(S) ((S[0] == '<'))
-
-#define CMD_IPV4(S) ((strcmp ((S), "A.B.C.D") == 0))
-#define CMD_IPV4_PREFIX(S) ((strcmp ((S), "A.B.C.D/M") == 0))
-#define CMD_IPV6(S) ((strcmp ((S), "X:X::X:X") == 0))
-#define CMD_IPV6_PREFIX(S) ((strcmp ((S), "X:X::X:X/M") == 0))
-
/* Common descriptions. */
#define SHOW_STR "Show running system information\n"
#define IP_STR "IP information\n"
@@ -347,17 +210,29 @@ extern void cmd_terminate (void);
extern void print_version (const char *);
extern void install_node (struct cmd_node *, int (*) (struct vty *));
-extern void install_default (enum node_type);
-extern void install_element (enum node_type, struct cmd_element *);
+extern void install_default (node_type_t);
+extern void install_element (node_type_t, struct cmd_command *);
extern void sort_node (void);
+extern const char* cmd_host_name(bool fresh) ;
+
+extern node_type_t cmd_node_parent(node_type_t node) ;
+extern node_type_t cmd_node_exit_to(node_type_t node) ;
+extern node_type_t cmd_node_end_to(node_type_t node) ;
+
/* Concatenates argv[shift] through argv[argc-1] into a single NUL-terminated
string with a space between each element (allocated using
XMALLOC(MTYPE_TMP)). Returns NULL if shift >= argc. */
extern char *argv_concat (const char* const* argv, int argc, int shift);
-/* struct host global, ick */
-extern struct host host;
+/* Export typical functions. */
+extern struct cmd_command config_end_cmd;
+extern struct cmd_command config_exit_cmd;
+extern struct cmd_command config_quit_cmd;
+extern struct cmd_command config_help_cmd;
+extern struct cmd_command config_list_cmd;
+extern char *host_config_file (void);
+extern void host_config_set (const char *);
#ifdef QDEBUG
extern const char *debug_banner ;
diff --git a/lib/command_execute.h b/lib/command_execute.h
index 6a7e2e4b..c1a2bdd0 100644
--- a/lib/command_execute.h
+++ b/lib/command_execute.h
@@ -23,59 +23,104 @@
#ifndef _ZEBRA_COMMAND_EXECUTE_H
#define _ZEBRA_COMMAND_EXECUTE_H
-#include "command.h"
+#include "command_local.h"
#include "command_parse.h"
-#include "node_type.h"
-
-extern vector cmd_make_strvec (const char *);
-extern vector cmd_add_to_strvec (vector v, const char* str) ;
-extern void cmd_free_strvec (vector);
-extern vector cmd_describe_command (const char* line, node_type_t node,
- cmd_return_code_t* status) ;
-extern vector cmd_complete_command (vector, int, int *status);
-extern const char *cmd_prompt (enum node_type);
-extern enum cmd_return_code
-config_from_file (struct vty* vty, FILE *fp, struct cmd_element* first_cmd,
- qstring buf, bool stop_on_warning) ;
-extern enum node_type node_parent (enum node_type);
-extern enum cmd_return_code cmd_execute_command (struct vty *vty,
- enum cmd_parse_type type, struct cmd_element **cmd) ;
-extern enum cmd_return_code cmd_execute_command_strict (struct vty *vty,
- enum cmd_parse_type type, struct cmd_element **cmd) ;
-
-extern cmd_parsed cmd_parse_init_new(cmd_parsed parsed) ;
-extern cmd_parsed cmd_parse_reset(cmd_parsed parsed, bool free_structure) ;
-extern enum cmd_return_code cmd_parse_command(struct vty* vty,
- enum cmd_parse_type type) ;
-extern enum cmd_return_code cmd_dispatch(struct vty* vty, bool no_queue) ;
-
-Inline enum cmd_return_code
-cmd_dispatch_call(struct vty* vty)
+#include "vty_common.h"
+#include "qstring.h"
+#include "mqueue.h"
+#include "qpnexus.h"
+#include "thread.h"
+
+/*==============================================================================
+ * This is stuff which is used to parse and then execute commands.
+ */
+
+/* State of the execution loop
+ */
+enum cmd_exec_state
{
- cmd_parsed parsed = vty->parsed ;
- return (*(parsed->cmd->func))(parsed->cmd, vty, cmd_arg_vector_argc(parsed),
- cmd_arg_vector_argv(parsed)) ;
+ exec_null = 0,
+
+ exec_special, /* not a simple command */
+
+ exec_fetch,
+ exec_parse,
+ exec_open_pipes,
+ exec_execute,
+ exec_success,
+ exec_complete,
} ;
+typedef enum cmd_exec_state cmd_exec_state_t ;
+
+typedef struct cmd_exec* cmd_exec ;
+
+struct cmd_exec
+{
+ vty vty ; /* parent */
-#define cmd_parse_reset_keep(parsed) cmd_parse_reset(parsed, 0)
-#define cmd_parse_reset_free(parsed) cmd_parse_reset(parsed, 1)
+ qstring line ; /* pointer to qstring in vf */
+ cmd_do_t to_do ; /* for cli driven stuff */
-extern void config_replace_string (struct cmd_element *, char *, ...);
+ cmd_parse_type_t parse_type ; /* how should parse */
+ bool out_enabled ; /* as required */
+ bool reflect_enabled ; /* as required */
+
+ cmd_parsed_t parsed ; /* embedded */
+
+ cmd_exec_state_t state ; /* for cq_process */
+ qpn_nexus locus ; /* for cq_process */
+
+ cmd_return_code_t ret ; /* for cq_process */
+
+ union
+ {
+ mqueue_block mqb ; /* for cq_process */
+ struct thread* thread ;
+ } cq ;
+} ;
+
+/*==============================================================================
+ * Functions
+ *
+ */
-/* Export typical functions. */
-extern struct cmd_element config_end_cmd;
-extern struct cmd_element config_exit_cmd;
-extern struct cmd_element config_quit_cmd;
-extern struct cmd_element config_help_cmd;
-extern struct cmd_element config_list_cmd;
-extern char *host_config_file (void);
-extern void host_config_set (char *);
+extern cmd_exec cmd_exec_new(vty vty) ;
+extern cmd_exec cmd_exec_free(cmd_exec exec) ;
-/* "<cr>" global */
-extern char *command_cr;
+extern cmd_return_code_t cmd_read_config(vty vty, cmd_command first_cmd,
+ bool ignore_warning) ;
+extern cmd_return_code_t cmd_end(vty vty) ;
+extern cmd_return_code_t cmd_exit(vty vty) ;
+
+extern cmd_return_code_t cmd_open_pipes(vty vty) ;
+
+extern cmd_return_code_t cmd_execute(vty vty) ;
+
+
+
+
+
+
+#if 0
+
+extern enum cmd_return_code cmd_execute_command (vty vty,
+ cmd_parse_type_t type, struct cmd_command **cmd) ;
+extern enum cmd_return_code cmd_execute_command_strict (vty vty,
+ enum cmd_parse_type type, struct cmd_command **cmd) ;
+
+
+extern void config_replace_string (cmd_command, char *, ...);
-#ifdef QDEBUG
-extern const char *debug_banner;
#endif
+/*==============================================================================
+ * Inlines
+ */
+
+Inline bool
+cmd_is_direct(cmd_parsed parsed)
+{
+ return ((parsed->cmd->attr & CMD_ATTR_DIRECT) != 0) ;
+} ;
+
#endif /* _ZEBRA_COMMAND_EXECUTE_H */
diff --git a/lib/command_parse.c b/lib/command_parse.c
index d8960b8d..c64ee3ba 100644
--- a/lib/command_parse.c
+++ b/lib/command_parse.c
@@ -1,4 +1,4 @@
-/* Quagga command line parsing -- header
+/* Quagga command line parsing
* Copyright (C) 1997, 98 Kunihiro Ishiguro
*
* Recast and extended: Copyright (C) 2010 Chris Hall (GMCH), Highwayman
@@ -21,58 +21,1155 @@
* Boston, MA 02111-1307, USA.
*/
-#include <zebra.h>
+#include "misc.h"
+#include <ctype.h>
+#include <stdio.h>
+#include <arpa/inet.h>
+#include "command_local.h"
#include "command_parse.h"
#include "memory.h"
/*==============================================================================
- * Token handling
+ * Command Description objects.
+ *
+ */
+static void cmd_fail_item(cmd_command cmd, const char* msg) ;
+static char* cmd_item_brackets(cmd_command cmd, char* cp) ;
+static cmd_item cmd_make_item(cmd_command cmd, char* cp, char* dp) ;
+static void cmd_make_item_inner(cmd_command cmd, cmd_item n, char* cp) ;
+static char* cmd_make_item_numeric(cmd_command cmd, cmd_item n, char* cp) ;
+static long cmd_make_item_number(cmd_command cmd, cmd_item n, char** p_cp) ;
+static int cmd_cmp_item(const cmd_item* a, const cmd_item* b) ;
+static int cmd_cmp_range_items(const cmd_item a, const cmd_item b) ;
+static bool cmd_item_is_option(cmd_item_type_t it) ;
+static bool cmd_item_is_vararg(cmd_item_type_t it) ;
+
+/*------------------------------------------------------------------------------
+ * Dummy eol_item
*/
+static struct cmd_item eol_item =
+{
+ .str = "<cr>",
+ .doc = "",
+
+ .next = NULL,
-/* Store of qstrings, used for parsing. */
-token_vector_t spare_tokens ;
+ .type = item_eol,
+ .arg = false,
-static char cmd_token_escape(char e) ;
+ .range_sign_allowed = false,
+ .range_sign_required = false,
+ .range_min = 0,
+ .range_max = 0
+} ;
/*------------------------------------------------------------------------------
- * Initialise a brand new token vector -- empty.
+ * Parse cmd_command string and doc to create the items for the cmd_command,
+ * and fill in:
+ *
+ * cmd->items -- vector of cmd_item(s).
+ *
+ * Where a given item may have more than one possible
+ * value, thet are arranged as a list.
+ *
+ * cmd->nt_min -- count of items up to first [option]
+ * where (...) counts as 1
+ * and .vararg counts as 1
+ *
+ * Is the minimum number of tokens required to match to
+ * this command.
+ *
+ * cmd->nt -- count of all items
+ * where (...) counts as 1
+ * and [option] counts as 1
+ * and .vararg counts as 1
+ *
+ * cmd->nt_max -- count of all items as nt_var,
+ * except .vararg forces to UINT_MAX
+ *
+ * Is the maximum number of tokens which can be matched
+ * to this command.
+ *
+ * cmd->r_string -- copy of cmd->string, chopped up and referred to by
+ * the cmd_items.
+ *
+ * cmd->d_string -- copy of the cmd->doc, chopped up and referred to by
+ * the cmd_items.
+ *
+ * Note that the cmd_items point into the r_string and the d_string, and
+ * do not have further copies of their fragments of the original.
+ *
+ * Note that the t_string and d_string have all extraneous spaces, tabs and
+ * control characters removed.
+ *
+ * Stops dead if not valid !
+ *
+ * Accepts: items separated by one or more spaces. Early on in the process,
+ * will discard spaces within a bracketed item (and checks for balanced
+ * brackets). Rejects any control character other than '\t', which is
+ * converted to ' '. Multiple spaces are reduced to single spaces.
+ *
+ * - single item is one of:
+ *
+ * - keyword -- anything starting a-z or 0-9, followed by any
+ * alphanumeric, '-', '_' or ':'.
+ * or *
+ * - <0-9> -- decimal range
+ * - WORD -- anything at least starting A-Z, followed by any
+ * alphanumeric, '-', '_' or ':'.
+ * - A.B.C.D -- ipv4 address
+ * - A.B.C.D/M -- ipv4 prefix
+ * - X:X::X:X -- ipv6 address
+ * - X:X::X:X/M -- ipv6 prefix
+ * - .vararg -- anything starting '.', followed by any alphanumeric,
+ * '-', '_' or ':'.
+ * - [item] -- optional item any of the above TODO
+ *
+ * - multiple item is: '(' item '|' item .... ')'
+ *
+ * where spaces around items are discarded. The items may be any of the
+ * above, except:
+ *
+ * - must all be different types of item, or for keywords and ranges,
+ * different values.
+ *
+ * - cannot have [item]
+ *
+ * - cannot have .var
+ *
+ * may have a single item -- whose value is sent out as an argument.
+ *
+ * An [item] may only be followed by other [item](s). An [item] matches a
+ * token or end of line.
+ *
+ * A .vararg must be the last item. A .vararg matches one or more tokens.
+ *
+ *
+ *
*/
-static inline void
-cmd_token_vector_init(token_vector tokens)
+extern void
+cmd_compile(cmd_command cmd)
{
- vector_init_new(tokens->body, 0) ;
+ vector multvec ;
+ char* cp ;
+ char* qp ;
+ char* dp ;
+ bool opt ;
+ bool vararg ;
+
+ /* Initialise the compiled version of the command */
+
+ assert((cmd->r_doc == NULL) && (cmd->r_string == NULL)) ;
+
+ cmd->items = vector_init_new(NULL, 10) ; /* plenty ! */
+ cmd->nt_min = 0 ;
+ cmd->nt = 0 ;
+ cmd->nt_max = 0 ;
+ cmd->vararg = NULL ;
+ cmd->r_doc = XSTRDUP(MTYPE_CMD_STRING, cmd->doc) ; /* NULL => "" */
+ cmd->r_string = XSTRDUP(MTYPE_CMD_STRING, cmd->string) ;
+
+ /* Simplify the command line string by replacing TABs by spaces, and barfing
+ * on control characters. Strip leading and trailing spaces and any spaces
+ * between brackets... checking for matching brackets.
+ */
+ cp = cmd->r_string ;
+ while (*cp != '\0')
+ {
+ if (!iscntrl(*cp))
+ ++cp ;
+ else if (*cp == '\t')
+ *cp++ = ' ' ;
+ else
+ cmd_fail_item(cmd, "improper control character in string") ;
+ } ;
+
+ cp = cmd->r_string ;
+ while (*cp == ' ')
+ ++cp ;
+
+ qp = cmd->r_string ;
+ while (*cp != '\0')
+ {
+ if (*cp != ' ')
+ {
+ if ((*cp == '(') || (*cp == '[') || (*cp == '<') || (*cp == '{'))
+ {
+ /* Check for balanced brackets and remove any spaces between.
+ *
+ * Checks for enclosed brackets being balanced as well.
+ *
+ * Leaves cp pointing at the trailing bracket.
+ */
+ char* sp = cp ;
+ cp = cmd_item_brackets(cmd, cp) ;
+ while (sp < cp)
+ {
+ if (*sp != ' ')
+ *qp++ = *sp++ ;
+ else
+ ++sp ;
+ } ;
+ } ;
+ }
+ else
+ {
+ while (*(cp + 1) == ' ')
+ ++cp ;
+ if (*(cp + 1) == '\0')
+ break ;
+ } ;
+
+ *qp++ = *cp++ ;
+ } ;
+
+ *qp++ = '\0' ; /* terminate reduced string */
+
+ /* Simplify the documentation string by replacing TABs by spaces, and barfing
+ * on control characters other than '\n'.
+ *
+ * Strips leading spaces and any spaces before or after '\n'.
+ */
+
+ qp = dp = cmd->r_doc ;
+ while (*dp != '\0')
+ {
+ /* Strip leading */
+ while (*dp == ' ')
+ ++dp ;
+
+ /* Eat documentation section. */
+ while ((*dp != '\n') && (*dp != '\0'))
+ {
+ if (!iscntrl(*dp))
+ *qp++ = *dp++ ;
+ else if (*dp == '\t')
+ {
+ *qp++ = ' ' ;
+ ++dp ;
+ }
+ else
+ cmd_fail_item(cmd, "improper control character in documentation") ;
+ } ;
+
+ /* Get here with *dp == '\n' or '\0'
+ *
+ * Strip trailing spaces (any before '\n' or '\0'
+ */
+ while ((qp != cmd->r_doc) && (*(qp - 1) == ' '))
+ --qp ;
+
+ /* copy '\n', if required. */
+ if (*dp == '\n')
+ *qp++ = *dp++ ;
+ } ;
+
+ *qp++ = '\0' ; /* terminate reduced string */
+
+ /* Processing loop */
+
+ cp = cmd->r_string ;
+ dp = cmd->r_doc ;
+
+ opt = false ;
+ vararg = false ;
+
+ multvec = NULL ;
+
+ while (*cp != '\0')
+ {
+ uint multiple ;
+
+ /* Deal with single or multiple item. */
+ multiple = 0 ;
+ do
+ {
+ cmd_item n ;
+ char* c_sp ;
+ char* d_sp ;
+
+ /* step to the next documentation section */
+
+ d_sp = dp ; /* start of documentation */
+
+ while (*dp != '\0')
+ {
+ if (*dp == '\n')
+ {
+ *dp++ = '\0' ;
+ break ;
+ } ;
+ ++dp ;
+ } ;
+
+ /* Deal with '(' if we have one. */
+
+ if (*cp == '(') /* change up to multiple */
+ {
+ if (multiple != 0)
+ cmd_fail_item(cmd, "unexpected '('") ;
+
+ multiple = 1 ; /* seen '(' */
+ ++cp ; /* step past it */
+
+ multvec = vector_re_init(multvec, 10) ; /* plenty ! */
+ } ;
+
+ /* Find end of current item & '\0' terminate it. */
+ c_sp = cp ;
+ while (1)
+ {
+ if (*cp == '|') /* eat '|' */
+ {
+ if ((c_sp == cp) || (multiple < 1))
+ cmd_fail_item(cmd, "unexpected '|'") ;
+ *cp++ = '\0' ;
+ break ;
+ } ;
+
+ if (*cp == ')') /* eat ')' */
+ {
+ if ((c_sp == cp) || (multiple < 1))
+ cmd_fail_item(cmd, "unexpected ')'") ;
+ *cp++ = '\0' ;
+ multiple = 2 ;
+
+ if ((*cp != ' ') && (*cp != '\0'))
+ cmd_fail_item(cmd, "expect ' ' or nothing after ')'") ;
+ } ;
+
+ if (*cp == ' ')
+ {
+ *cp++ = '\0' ;
+ break ;
+ } ;
+
+ if (*cp == '\0')
+ break ;
+
+ ++cp ;
+ } ;
+
+ /* Create the next item and push */
+
+ n = cmd_make_item(cmd, c_sp, d_sp) ;
+
+ if (multiple == 0)
+ vector_push_item(cmd->items, n) ;
+ else
+ vector_push_item(multvec, n) ;
+
+ /* Extra checks for multiple item. */
+ if (multiple > 0)
+ {
+ n->arg = true ; /* always */
+
+ if (cmd_item_is_option(n->type))
+ cmd_fail_item(cmd, "cannot have [option] inside (..)") ;
+
+ /* could lift this restriction, but need to check that
+ * do not have a WORD|.VAR together, because that is tautologous.
+ */
+
+ if (cmd_item_is_vararg(n->type))
+ cmd_fail_item(cmd, "cannot have .vararg inside (..)") ;
+ } ;
+
+ /* Check optional item state -- can only be trailing */
+ if (cmd_item_is_option(n->type))
+ opt = true ;
+ else if (opt)
+ cmd_fail_item(cmd, "can only have [option] after [option]") ;
+
+ /* Check vararg item state -- can only be trailing */
+ if (vararg)
+ cmd_fail_item(cmd, "cannot have anything after .vararg") ;
+ else if (cmd_item_is_vararg(n->type))
+ {
+ vararg = true ;
+ cmd->vararg = n ; /* remember for parsing */
+ } ;
+
+ } while (multiple == 1) ;
+
+ /* count the item */
+ if (!opt)
+ ++cmd->nt_min ;
+ ++cmd->nt ;
+ if (!vararg)
+ ++cmd->nt_max ;
+ else
+ cmd->nt_max = UINT_MAX ;
+
+ /* Complete the multiple item.
+ *
+ * Sort the items so that are always used and presented in the same
+ * order. Check that the items are unique.
+ *
+ * We must have at least one item.
+ */
+ if (multiple == 2)
+ {
+ cmd_item n, p ;
+ uint i ;
+
+ assert(vector_length(multvec) >= 1) ;
+
+ vector_sort(multvec, (vector_sort_cmp*)cmd_cmp_item) ;
+
+ n = vector_get_item(multvec, 0) ;
+ vector_push_item(cmd->items, n) ;
+
+ for (i = 1 ; i < vector_length(multvec) ; ++i)
+ {
+ p = n ;
+ n = vector_get_item(multvec, i) ;
+
+ p->next = n ;
+ n->next = NULL ;
+
+ if (p->type == n->type)
+ {
+ bool repeat ;
+
+ if (n->type == item_keyword)
+ repeat = strcmp(n->str, p->str) == 0 ;
+ else if (n->type == item_range)
+ repeat = cmd_cmp_range_items(n, p) == 0 ;
+ else
+ repeat = true ;
+
+ if (repeat)
+ cmd_fail_item(cmd, "repeated items in (...)") ;
+ } ;
+ } ;
+ }
+ else
+ assert(multiple == 0) ;
+ } ;
+
+ vector_reset(multvec, free_it) ;
+
+ /* Reduce the vector to the minimum size required */
+ vector_decant(cmd->items) ;
} ;
/*------------------------------------------------------------------------------
- * Initialise empty spare tokens vector
+ * Validate a compiled item
+ *
+ * Checks that the contents of the cmd_command are consistent with the
+ * contents of the srcvec.
+ *
*/
extern void
-cmd_spare_tokens_init(void)
+cmd_compile_check(cmd_command cmd)
{
- cmd_token_vector_init(spare_tokens) ;
+ bool ok ;
+
+ uint nt_min = 0 ;
+ uint nt = 0 ;
+ uint nt_max = 0 ;
+ cmd_item vararg = NULL ;
+
+ ok = true ;
+
+ /* Require the following to be set to something */
+ if ( (cmd->string == NULL)
+ || (cmd->func == NULL)
+ || (cmd->items == NULL)
+ || (cmd->r_string == NULL)
+ || (cmd->r_doc == NULL) )
+ ok = false ;
+
+ /* cmd->nt must match the vector_length and be non-zero. */
+ ok = ok && ((nt = vector_length(cmd->items)) == cmd->nt) ;
+ ok = ok && (nt != 0) ;
+
+ /* Walk the vector of items, and check that those are OK. */
+ if (ok)
+ {
+ uint ii = 0 ;
+ bool opt = false ;
+
+ for (ii = 0 ; ok && (ii < nt) ; ++ii)
+ {
+ cmd_item item ;
+ cmd_item first_item ;
+
+ item = vector_get_item(cmd->items, ii) ;
+ if (item == NULL)
+ {
+ ok = false ;
+ break ;
+ } ;
+
+ if (vararg != NULL) /* nothing after vararg */
+ {
+ ok = false ;
+ break ;
+ } ;
+
+ first_item = item ;
+ while (ok && (item != NULL))
+ {
+ /* If this is an option, may only be a single item
+ *
+ * Otherwise, after an option must all be options.
+ */
+ if (cmd_item_is_option(item->type))
+ {
+ /* option must be a single item. */
+ opt = true ;
+ if ((item != first_item) || (item->next != NULL))
+ {
+ ok = false ;
+ break ;
+ } ;
+ }
+ else if (opt)
+ {
+ /* once we have an option, must all be options */
+ ok = false ;
+ break ;
+ } ;
+
+ /* If this is a vararg, must be the last of this item.
+ *
+ * Note that allow for [.varg] and (...., .varg) -- the second
+ * should be sorted to the back !
+ */
+ if (cmd_item_is_vararg(item->type))
+ {
+ /* vararg must be last item & only vararg */
+ if ((item->next != NULL) || (vararg != NULL))
+ {
+ ok = false ;
+ break ;
+ } ;
+
+ vararg = item ;
+ } ;
+
+
+ /* If there is a next, this and the next MUST be arg */
+ if (item->next != NULL)
+ {
+ if (!((item->arg) && (item->next->arg)))
+ {
+ ok = false ;
+ break ;
+ } ;
+ } ;
+
+ item = item->next ;
+ } ;
+
+ /* Advance the nt_min and nt_max as required. */
+ if (!opt)
+ ++nt_min ;
+
+ if (vararg == NULL)
+ ++nt_max ;
+ else
+ nt_max = UINT_MAX ;
+ } ;
+ } ;
+
+ /* Final checks */
+
+ ok = ok && (cmd->nt_min == nt_min)
+ && (cmd->nt == nt)
+ && (cmd->nt_max == nt_max)
+ && (cmd->vararg == vararg) ;
+
+ if (!ok)
+ cmd_fail_item(cmd, "some compile error") ;
} ;
/*------------------------------------------------------------------------------
- * Empty out the spare_tokens vector and release all memory
+ * Reject the cmd_item string or doc.
*/
-extern void
-cmd_spare_tokens_free(void)
+static void
+cmd_fail_item(cmd_command cmd, const char* msg)
+{
+ fprintf (stderr, "Command parse error!: %s\n", msg) ;
+ fprintf (stderr, " in command: '%s'\n", cmd->string) ;
+ exit(2) ;
+}
+
+/*------------------------------------------------------------------------------
+ * Advance to matching bracket -- fail if not found. Recurse as required.
+ *
+ * Returns: address of matching bracket.
+ */
+static char*
+cmd_item_brackets(cmd_command cmd, char* cp)
+{
+ char seek ;
+
+ switch (*cp)
+ {
+ case '(':
+ seek = ')' ;
+ break ;
+
+ case '[':
+ seek = ']' ;
+ break ;
+
+ case '<':
+ seek = '>' ;
+ break ;
+
+ case '{':
+ seek = '}' ;
+ break ;
+
+ default:
+ return cp ;
+ } ;
+
+ do
+ {
+ ++cp ;
+
+ if (*cp == seek)
+ return cp ;
+ else if ((*cp == '(') || (*cp == '[') || (*cp == '<') || (*cp == '{'))
+ cp = cmd_item_brackets(cmd, cp) ;
+ }
+ while (*cp != '\0') ;
+
+ cmd_fail_item(cmd, "unbalanced brackets of some sort") ;
+
+ return cp ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Make descriptor for current item.
+ *
+ * cp points at start of '\0' terminated item, which has no spaces and no
+ * control characters in or around it. Also, if there are brackets, they are
+ * balanced.
+ *
+ * Returns: new descriptor, filled in as required.
+ */
+
+static cmd_item
+cmd_make_item(cmd_command cmd, char* cp, char* dp)
+{
+ char* inner ;
+ cmd_item n ;
+
+ n = XCALLOC(MTYPE_CMD_ITEM, sizeof(struct cmd_item)) ;
+
+ /* Zeroising has set:
+ *
+ * * cmd = NULL -- set below
+ * * doc = NULL -- set below
+ *
+ * * next = NULL -- set elsewhere if multiple.
+ *
+ * * type = item_null
+ * * arg = false -- set if required, below
+ *
+ * * range_sign_allowed )
+ * * range_sign_required ) -- set elsewhere if required
+ * * range_min )
+ * * range_max )
+ */
+ confirm(item_null == 0) ;
+
+ n->str = cp ;
+ n->doc = dp ;
+
+ /* Worry about option state */
+ inner = NULL ;
+ if (*cp == '[')
+ {
+ n->arg = true ; /* always true for option */
+
+ inner = XSTRDUP(MTYPE_TMP, cp) ;
+ cp = inner + 1 ; /* strip leading '[' */
+ *(cp + strlen(cp) - 1) = '\0' ; /* strip trailing ']' */
+
+ if (*cp == '\0')
+ cmd_fail_item(cmd, "empty [option]") ;
+ } ;
+
+ /* Deal with the inner item */
+ cmd_make_item_inner(cmd, n, cp) ;
+
+ /* Worry about the option state, again. */
+ if (inner != NULL)
+ {
+ XFREE(MTYPE_TMP, inner) ;
+
+ if (n->type == item_vararg)
+ cmd_fail_item(cmd, "cannot have [.vararg]") ;
+
+ n->type = item_option_word ; /* TODO other option types ? */
+ } ;
+
+ /* return newly minted cmd_item item */
+ assert(n->type != item_null) ;
+
+ return n ;
+}
+
+/*------------------------------------------------------------------------------
+ * Make inner part of cmd_item -- so can have [...] anything (in principle).
+ *
+ * Require '\0' terminated inner part.
+ */
+static void
+cmd_make_item_inner(cmd_command cmd, cmd_item n, char* cp)
+{
+ bool eat_name_chars ; /* alphanumeric + '-', '_', '.' and ':' */
+
+ eat_name_chars = false ;
+
+ if (islower(*cp) /* 'a'..'z' */
+ || isdigit(*cp)) /* '0'..'9' */
+ {
+ /* item_keyword -- lowercase alpha numeric + '_' and '-' */
+ n->type = item_keyword ;
+ eat_name_chars = true ;
+ }
+ else if (*cp == '*') /* '*' */
+ {
+ /* special item_keyword '*' */
+ n->type = item_keyword ;
+ ++cp ;
+ }
+ else if (isupper(*cp)) /* 'A'..'Z' */
+ {
+ n->arg = true ;
+
+ /* WORD or other variable */
+ if (strcmp(cp, "A.B.C.D") == 0)
+ n->type = item_ipv4_address ;
+ else if (strcmp(cp, "A.B.C.D/M") == 0)
+ n->type = item_ipv4_prefix ;
+ else if (strcmp(cp, "X:X::X:X") == 0)
+ n->type = item_ipv6_address ;
+ else if (strcmp(cp, "X:X::X:X/M") == 0)
+ n->type = item_ipv6_prefix ;
+ else
+ {
+ n->type = item_word ;
+ eat_name_chars = true ;
+ } ;
+
+ if (n->type != item_word)
+ cp += strlen(cp) ; /* step past "A.B.C.D" et al */
+ }
+ else if (*cp == '.') /* '.' */
+ {
+ n->arg = true ;
+ n->type = item_vararg ;
+ eat_name_chars = true ;
+ }
+ else if (*cp == '<') /* '<' */
+ {
+ n->arg = true ;
+
+ cp = cmd_make_item_numeric(cmd, n, ++cp) ;
+
+ if (*cp != '>')
+ cmd_fail_item(cmd, "badly formed <...>") ;
+ else
+ ++cp ;
+ }
+ else if (*cp == '\0')
+ cmd_fail_item(cmd, "cannot have an empty item") ;
+
+ if (eat_name_chars)
+ {
+ do ++cp ; while ( isalnum(*cp)
+ || (*cp == '-')
+ || (*cp == '_')
+ || (*cp == ':')
+ || (*cp == '.') ) ;
+
+ } ;
+
+ if (*cp != '\0')
+ cmd_fail_item(cmd, "invalid item") ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Make <...> types
+ *
+ * Require '\0' terminated <...>, pointing after the '<'.
+ *
+ * Assumes the '>' is present.
+ *
+ * Returns: where processed up to -- pointing at '>' iff OK.
+ *
+ * Supports ranges:
+ *
+ * 9-10 => unsigned values in 32b range -- NO sign
+ *
+ * +9-10 => unsigned value, where '+' is optional
+ * 9-+10 => unsigned value, where '+' is *required*
+ * +9-+10 => same as above
+ *
+ * -9-10 => signed value, where '+' is optional
+ * -9-+10 => signed value, where '+' is *required*
+ *
+ * -9--8 => signed value, where '-' is required (!)
+ *
+ *
+ * +/-9 => -9-+9 -- sign is required.
+ *
+ * In place of decimal number can use 9b -- giving 2^9 - 1.
+ */
+static char*
+cmd_make_item_numeric(cmd_command cmd, cmd_item n, char* cp)
+{
+ if (isdigit(*cp) || (*cp == '+') || (*cp == '-'))
+ {
+ long m ;
+ bool pm ;
+
+ confirm((LONG_MAX > item_max_number) && (LONG_MIN < -item_max_number)) ;
+
+ n->type = item_range ;
+ n->range_sign_allowed = false ;
+ n->range_sign_required = false ;
+
+ if (strncmp(cp, "+/-", 3) == 0)
+ {
+ pm = true ;
+ n->range_sign_required = true ;
+ cp += 2 ; /* step to '-' to get -ve range_min. */
+ }
+ else
+ {
+ pm = false ;
+ n->range_sign_allowed = (*cp == '+') ;
+ } ;
+
+ m = cmd_make_item_number(cmd, n, &cp) ;
+ n->range_min = m ;
+
+ if (pm)
+ m = -m ; /* for range_max */
+
+ else if (*cp == '-')
+ {
+ ++cp ; /* past the '-' */
+
+ n->range_sign_required = (*cp == '+') ;
+
+ m = cmd_make_item_number(cmd, n, &cp) ;
+ }
+
+ else
+ cmd_fail_item(cmd, "badly formed <0-1>") ;
+
+ n->range_max = m ;
+
+ if (n->range_min > n->range_max)
+ cmd_fail_item(cmd, "badly formed <0-1> min > max !") ;
+
+ if ((n->range_sign_required) || (n->range_min < 0))
+ n->range_sign_allowed = true ; /* allowed if required ! */
+ } ;
+
+ return cp ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get signed or unsigned value -- process the '9b' form.
+ *
+ */
+static long
+cmd_make_item_number(cmd_command cmd, cmd_item n, char** p_cp)
+{
+ long m ;
+ char* cp ;
+
+ cp = *p_cp ;
+ m = strtol(cp, p_cp, 10) ;
+
+ if ((*p_cp == cp) || (m > item_max_number))
+ cmd_fail_item(cmd, "badly formed or out of range number in <...>") ;
+
+ if (**p_cp == 'b')
+ {
+ long s ;
+
+ ++(*p_cp) ; /* step past 'b' */
+ s = m ;
+ m = labs(m) ;
+ if ((m == 0) || (m > 32))
+ cmd_fail_item(cmd, "out of range number in 9b form in <...>") ;
+
+ m = ((long)1 << m) - 1 ;
+ if (s < 0)
+ m = -m ;
+ } ;
+
+ return m ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Compare cmd_item items
+ *
+ * Note that command types sort with the larger type value before the smaller.
+ */
+static int
+cmd_cmp_item(const cmd_item* a, const cmd_item* b)
+{
+ if ((*a)->type != (*b)->type)
+ return ((*a)->type > (*b)->type) ? -1 : +1 ; /* descending order */
+ else
+ {
+ if ((*a)->type == item_range)
+ return cmd_cmp_range_items(*a, *b) ;
+ else
+ return strcmp ((*a)->str, (*b)->str);
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Compare cmd_item item_range items
+ */
+static int
+cmd_cmp_range_items(const cmd_item a, const cmd_item b)
+{
+ int as, bs ;
+
+ if (a->range_min != b->range_min)
+ return (a->range_min < b->range_min) ? -1 : +1 ;
+
+ if (a->range_max != b->range_max)
+ return (a->range_max < b->range_max) ? -1 : +1 ;
+
+ as = a->range_sign_required ? 2 : (a->range_sign_allowed ? 1 : 0) ;
+ bs = b->range_sign_required ? 2 : (b->range_sign_allowed ? 1 : 0) ;
+
+ if (as != bs)
+ return (as < bs) ? -1 : +1 ;
+
+ return 0 ;
+} ;
+
+/*==============================================================================
+ * Token objects
+ */
+
+/*------------------------------------------------------------------------------
+ * Make a brand new token object
+ */
+Private cmd_token
+cmd_token_new(void)
+{
+ return XCALLOC(MTYPE_TOKEN, sizeof(struct cmd_token)) ;
+
+ /* Zeroising the new structure sets:
+ *
+ * type = 0 -- cmd_tok_eol
+ * qs = zeroised qstring -- empty string
+ * complete = 0 -- false
+ *
+ * tp = 0
+ * lp = zeroised elstring -- empty string
+ */
+ confirm(cmd_tok_eol == 0) ;
+ confirm(QSTRING_INIT_ALL_ZEROS) ;
+ confirm(ELSTRING_INIT_ALL_ZEROS) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Empty token object and free it.
+ */
+static void
+cmd_token_free(cmd_token t)
+{
+ qs_reset(t->qs, keep_it) ; /* discard body of qstring */
+ XFREE(MTYPE_TOKEN, t) ;
+} ;
+
+/*==============================================================================
+ * Parser object
+ */
+
+/*------------------------------------------------------------------------------
+ * Initialise a new cmd_parsed object, allocating if required
+ */
+extern cmd_parsed
+cmd_parsed_init_new(cmd_parsed parsed)
{
- token tok ;
+ if (parsed == NULL)
+ parsed = XCALLOC(MTYPE_CMD_PARSED, sizeof(*parsed)) ;
+ else
+ memset(parsed, 0, sizeof(*parsed)) ;
+
+ /* Zeroising the structure has set:
+ *
+ * parts = 0 -- cleared by cmd_tokenise()
+ * tok_total = 0 -- set by cmd_tokenise()
+ *
+ * elen = 0 -- set by cmd_tokenise()
+ * tsp = 0 -- set by cmd_tokenise()
+ *
+ * cmd = NULL -- no command yet
+ * cnode = 0 -- not set
+ *
+ * num_tokens = 0 -- set by cmd_tokenise()
+ * tokens = all zeros -- empty token vector
+ *
+ * args = all zeros -- empty vector of arguments
+ *
+ * emess = NULL -- no error yet
+ * eloc = 0 -- no error location
+ *
+ * in_pipe = cmd_pipe_none
+ * out_pipe = cmd_pipe_none
+ *
+ * first_in_pipe )
+ * num_in_pipe )
+ * first_do )
+ * num_do )
+ * first_command ) none
+ * num_command )
+ * first_out_pipe )
+ * num_out_pipe )
+ * first_comment )
+ * num_comment )
+ *
+ * cti ) set by cmd_token_position()
+ * rp )
+ *
+ * cmd_v = all zeros -- empty vector of filtered commands
+ * item_v = all zeros -- empty vector of filtered items
+ *
+ * strongest )
+ * best_complete ) set by cmd_filter_prepare()
+ * min_strength )
+ * strict )
+ */
+ confirm(cmd_pipe_none == 0) ;
+ confirm(TOKEN_VECTOR_INIT_ALL_ZEROS) ;
+ confirm(ARG_VECTOR_INIT_ALL_ZEROS) ;
- while ((tok = vector_ream(spare_tokens->body, keep_it)) != NULL)
- qs_reset(tok->qs, keep_it) ;
+ return parsed ;
} ;
/*------------------------------------------------------------------------------
- * Take string and break it into tokens.
+ * Empty out and (if required) free a cmd_parsed object
+ */
+extern cmd_parsed
+cmd_parsed_reset(cmd_parsed parsed, free_keep_b free_structure)
+{
+ if (parsed != NULL)
+ {
+ cmd_token t ;
+
+ /* Give back all the token objects and release vector body */
+ while ((t = vector_ream(parsed->tokens->body, keep_it)) != NULL)
+ cmd_token_free(t) ;
+
+ vector_reset(parsed->args->body, keep_it) ; /* embedded */
+ vector_reset(parsed->cmd_v, keep_it) ; /* embedded */
+ vector_reset(parsed->item_v, keep_it) ; /* embedded */
+
+ if (free_structure)
+ XFREE(MTYPE_CMD_PARSED, parsed) ; /* sets parsed = NULL */
+ else
+ cmd_parsed_init_new(parsed) ;
+ } ;
+
+ return parsed ;
+} ;
+
+/*==============================================================================
+ * Parsing error handling.
+ *
+ *
+ */
+
+/*------------------------------------------------------------------------------
+ * Register a parsing error.
+ *
+ * Takes token in which parsing error was detected, and an offset from the
+ * start of that, for the location of the error. If the offset is not zero,
+ * it must be an offset in the original token (!).
+ *
+ * The message is a simple constant string (!).
+ *
+ * The message will be output as: ..........^ pointing to the location
+ * followed by: % <mess>\n
+ *
+ * (The mess does not need to include the '%' or the '\n'.)
+ *
+ * Sets: parsed->emess
+ * parsed->eloc
+ *
+ * Returns: CMD_ERR_PARSING -- which MUST only be returned if p
+ */
+static cmd_return_code_t
+cmd_parse_error(cmd_parsed parsed, cmd_token t, usize off, const char* mess)
+{
+ parsed->emess = mess ;
+ parsed->eloc = t->tp + off ;
+
+ return CMD_ERR_PARSING ;
+} ;
+
+/*==============================================================================
+ * Lexical level stuff
+ */
+
+/*------------------------------------------------------------------------------
+ * Take elstring and see if it is empty -- only whitespace and/or comment
+ */
+extern bool
+cmd_is_empty(elstring line)
+{
+ cpp_t lp ;
+
+ els_cpp(lp, line) ; /* NULL -> NULL */
+
+ while (lp->p < lp->e)
+ {
+ if ((*lp->p == ' ') || (*lp->p == '\t'))
+ ++lp->p ;
+ else
+ return ((*lp->p == '!') || (*lp->p == '#')) ;
+ } ;
+
+ return true ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Reserved characters in a pipe token.
+ *
+ * The characters reserved allow for a large number of possible options to
+ * be attached to the pipe token.
+ *
+ * Wish to allow the pipe token to be followed by file name or command name
+ * without requiring whitespace separation, also do not want to intrude into
+ * quoted or escaped stuff. So limit the characters that are reserved.
+ */
+static inline bool
+cmd_pipe_reserved_char(char ch)
+{
+ return strchr("<|>%&*+-=?", ch) != NULL ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Take elstring and break it into tokens.
*
* Discards leading and trailing ' ' or '\t'.
*
* Expects string to have been preprocessed, if required, to ensure that any
* unwanted control characters have been removed. This code only recognises
- * '\t'.
+ * '\t' and treats it as whitespace.
*
* Anything between '....' is ignored by the tokenizer. NB: this follows the
* shell convention, so '\' is also ignored and there is no way to include "'"
@@ -90,60 +1187,88 @@ cmd_spare_tokens_free(void)
* * tokens are separated by whitespace -- one ' ' or '\t' characters
* The whitespace is discarded.
*
- * * tokens are separated by "separators", which start with any of:
+ * * tokens which start with any of:
*
- * '!', '#', '<', '>' and '|'
+ * '!', '#', '<' and '>'
*
- * which may be followed by one or more characters to form a separator
- * token.
+ * terminate themselves, as follows:
*
* - from '!' or '#' to end of line is a comment token.
*
- * - '<' opt and '<|' opt are separators, where opt is any combination
- * of '+', '*' and '-'.
+ * - '<' followed by pipe_reserved_chars is a token (in_pipe)
*
- * - '>', '>>' and '|' are separators.
+ * - '>' followed by pipe_reserved_chars is a token (out_pipe).
*
- * NB: the tokenization mimics the standard shell which makes the piping stuff
- * straightforward. It's also well known. Apart from the "'" rule, it
- * also seems fine !
+ * See above for pipe_reserved_
*
- * NB: any control characters other than those spotted by isspace() are accepted
- * as part of the current token !
+ * NB: this means that for '!', '#', '<' and '>' to be significant, they
+ * MUST be preceeded by whitespace (or start of line). This ever so
+ * slightly reduces the impact of the new lexical conventions.
*
- * The tokens returned contain all the original characters of the line, except
- * for the removal of '\t' between tokens.
+ * NB: the tokenization roughly mimics the (POSIX) standard shell. The
+ * differences are:
*
- * Returns: the types of all tokens or'd together.
- * returns cmd_tok_null if the line is empty (apart from ' ' and '\t')
- * or if the pointer was NULL.
+ * '|' is *not* a pipe ('>|' is), because '|' is a character in the
+ * regex repertoire.
+ *
+ * '<' and '>' do not terminate a token -- so are only significant
+ * at the start of a token.
+ *
+ * '!' is not a comment for the shell.
+ *
+ * The requirement for whitespace (or start of line) before '#' is
+ * consistent with the shell.
+ *
+ * The handling of '...' follows the standard shell.
+ *
+ * The tokenization does not remove any " ' or \ characters, that is left
+ * for a later stage, where context may affect the handling.
+ *
+ * NB: any control characters other than '\t' are accepted as part of the
+ * current token !
+ *
+ * The tokens returned contain all the original characters of the line, except
+ * for the removal of ' ' and '\t' between tokens and at the end of the line.
*
* Note: all the tokens in the vector have at least one character, and no
* entries are NULL.
*
* NB: it is the callers responsibility to release the token objects in due
* course.
+ *
+ * NB: the elstring containing the line to be tokenised MUST NOT change
+ * until the parsed object is finished with.
+ *
+ * Returns: the types of all tokens or'd together.
+ * returns cmd_tok_null if the line is empty (apart from ' ' and '\t')
+ * or if the elstring was or contained NULL.
+ *
+ * Initialises the parsed object, ready for further parsing:
+ *
+ * Sets: parsed->parts = cmd_parts_none
+ * parsed->num_tokens )
+ * parsed->elen ) per the results
+ * parsed->tsp )
+ * parsed->tokens )
+ *
+ * Note that the num_tokens does not include the cmd_tok_eol on the end.
*/
-extern cmd_token_type_t
-cmd_tokenise(cmd_parsed parsed, const char *line, node_type_t node)
+extern void
+cmd_tokenise(cmd_parsed parsed, qstring line)
{
- const char *cp, *ep ;
+ cpp_t lp ;
+ const char *cp, *tp ;
cmd_token_type_t total ;
+ uint nt ;
- cmd_empty_token_vector(parsed->tokens) ; /* Empty the token vector */
-
- parsed->line = line ;
- parsed->onode = parsed->cnode = node ;
-
- total = cmd_tok_null ; /* nothing yet */
+ total = 0 ; /* nothing yet */
+ nt = 0 ;
- if (line == NULL) /* tolerate NULL */
- return total ;
+ qs_cpp(lp, line) ; /* NULL -> NULL */
- cp = line ;
- ep = cp + strlen(cp) ;
-
- while (cp < ep) /* process to end */
+ cp = lp->p ;
+ tp = cp ;
+ while (cp < lp->e) /* process to end */
{
const char* sp ;
bool end ;
@@ -152,18 +1277,17 @@ cmd_tokenise(cmd_parsed parsed, const char *line, node_type_t node)
if ((*cp == ' ') || (*cp == '\t'))
{
/* skip white-space */
- do { ++cp ; } while ((*cp == ' ') || (*cp == '\t')) ;
-
- if (cp == ep)
+ do
{
- if (total != cmd_tok_null)
- total |= cmd_tok_trailing ;
- break ;
- } ;
+ end = (++cp == lp->e) ;
+ } while (!end && ((*cp == ' ') || (*cp == '\t'))) ;
+
+ if (end)
+ break ;
} ;
- sp = cp ;
end = false ;
+ sp = cp ;
type = cmd_tok_simple ;
do
{
@@ -174,10 +1298,10 @@ cmd_tokenise(cmd_parsed parsed, const char *line, node_type_t node)
end = true ;
break ;
- case '\'': /* proceed to matching '\'' or end */
- type |= cmd_tok_sq ;
+ case '\'': /* proceed to matching ' or end */
++cp ;
- while (cp < ep)
+ type |= cmd_tok_sq ;
+ while (cp < lp->e)
{
if (*cp++ == '\'')
break ;
@@ -185,719 +1309,2785 @@ cmd_tokenise(cmd_parsed parsed, const char *line, node_type_t node)
break ;
case '\\': /* step past escaped character, if any */
- type |= cmd_tok_esc ;
++cp ;
- if (cp < ep)
+ type |= cmd_tok_esc ;
+ if (cp < lp->e)
++cp ;
break ;
- case '"': /* proceed to matching '"' or end... */
- type |= cmd_tok_dq ;
+ case '"': /* proceed to matching " or end... */
++cp ;
- while (cp < ep) /* NB: do not register '\\' separately */
+ type |= cmd_tok_dq ;
+ while (cp < lp->e) /* NB: do not register \ separately */
{
if (*cp++ == '"')
- if (*(cp - 2) != '\\') /* ignore escaped '"' */
+ if (*(cp - 2) != '\\') /* ignore escaped " */
break ;
} ;
break ;
- case '>': /* '>' or '>>' separators. */
- end = true ;
- if (cp == sp) /* if at start of token */
+ case '>': /* '>' special at start */
+ end = (cp == sp) ;
+ ++cp ;
+ if (end) /* if special */
{
- type = cmd_tok_pipe_out ;
- ++cp ;
- if ((cp < ep) && (*cp == '>'))
+ type = cmd_tok_out_pipe ;
+ while ((cp < lp->e) && cmd_pipe_reserved_char(*cp))
++cp ;
} ;
break ;
- case '|': /* '|' separator. */
- end = true ;
- if (cp == sp)
- type = cmd_tok_pipe_out ;
- ++cp ;
- break ;
-
- case '<': /* '<' or '<|' separators. */
- end = true ;
- if (cp == sp)
+ case '<': /* '<' special at start */
+ end = (cp == sp) ;
+ ++cp ;
+ if (end) /* if special */
{
- type = cmd_tok_pipe_in ;
- ++cp ;
- if ((cp < ep) && (*cp == '|'))
- ++cp ;
- if ( (cp < ep) &&
- ((*cp == '+') || (*cp == '-') || (*cp == '*')) )
+ type = cmd_tok_in_pipe ;
+ while ((cp < lp->e) && cmd_pipe_reserved_char(*cp))
++cp ;
} ;
break ;
- case '!': /* '!' and '#' separators. */
+ case '!': /* '!' and '#' special at start */
case '#':
- end = true ;
- if (cp == sp)
+ if ((cp == sp) && (nt == 0))
{
+ end = true ;
type = cmd_tok_comment ;
- cp = ep ;
- } ;
+ cp = lp->e ;
+ }
+ else
+ ++cp ;
break ;
default:
++cp ;
break ;
} ;
- } while (!end && (cp < ep)) ;
+ } while (!end && (cp < lp->e)) ;
- cmd_token_push(parsed->tokens,
- cmd_token_new(type, sp, cp - sp, sp - line)) ;
+ cmd_token_set(parsed->tokens, nt, type, sp, cp - sp, sp - lp->p) ;
+ ++nt ;
+ total |= type ;
+
+ tp = cp ;
} ;
- return total ;
+ /* When we get here, tp points just after last character of last token,
+ * or at start of line if the line is blank (other than whitespace).
+ *
+ * If line is empty (apart from whitespace):
+ *
+ * - the effective length is zero.
+ * - the trailing space count is the number of (leading) spaces.
+ *
+ * If line is not empty:
+ *
+ * - the start position of the first token is the number of leading spaces.
+ * - the trailing space count is the number of spaces after the last
+ * token. (If the last token is comment, this will be zero.)
+ */
+ parsed->parts = cmd_parts_none ;
+ parsed->tok_total = total ;
+ parsed->num_tokens = nt ;
+ parsed->elen = tp - lp->p ;
+ parsed->tsp = lp->e - tp ;
+
+ /* Append an empty end of line token. */
+ cmd_token_set(parsed->tokens, parsed->num_tokens, cmd_tok_eol,
+ lp->e, 0, lp->e - lp->p) ;
} ;
/*------------------------------------------------------------------------------
- * Process token to remove quotes and escapes (if any).
+ * Process in-pipe token and set the required bits in the pipe type word
*
- * Returns: true <=> OK
- * false => invalid escape or incomplete quotes
+ * Known tokens are: < <| <+ <|+
+ */
+static cmd_return_code_t
+cmd_parse_in_pipe(cmd_parsed parsed, cmd_token t)
+{
+ cpp_t p ;
+ bool ok ;
+
+ els_cpp(p, t->ot) ;
+
+ ok = ((p->p < p->e) && (*p->p++ == '<')) ;
+
+ if (ok)
+ {
+ /* First character after '<' may qualify the type of the pipe */
+ parsed->in_pipe = cmd_pipe_file ;
+
+ if (p->p < p->e)
+ {
+ switch (*p->p++)
+ {
+ case '|':
+ parsed->in_pipe = cmd_pipe_shell ;
+ break ;
+
+ default:
+ --p->p ; /* put back */
+ break ;
+ }
+ } ;
+
+ /* Deal with option characters */
+ while (ok && (p->p < p->e))
+ {
+ /* Eat option character, if recognise it. */
+ switch (*p->p++)
+ {
+ case '+': /* reflect command lines */
+ parsed->in_pipe |= cmd_pipe_reflect ;
+ break ;
+
+ default:
+ --p->p ;
+ ok = false ;
+ break ;
+ } ;
+ } ;
+ } ;
+
+ if (!ok)
+ return cmd_parse_error(parsed, t, 0, "invalid 'pipe in'") ;
+
+ return CMD_SUCCESS ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Process out-pipe token and set the required bits in the pipe type word
+ *
+ * Known tokens are: > >> >| >*
+ */
+static cmd_return_code_t
+cmd_parse_out_pipe(cmd_parsed parsed, cmd_token t)
+{
+ cpp_t p ;
+ bool ok ;
+
+ els_cpp(p, t->ot) ;
+
+ ok = ((p->p < p->e) && (*p->p++ == '>')) ;
+
+ if (ok)
+ {
+ /* First character after '>' may qualify the type of the pipe */
+ parsed->out_pipe = cmd_pipe_file ;
+
+ if (p->p < p->e)
+ {
+ switch (*p->p++)
+ {
+ case '>':
+ parsed->out_pipe |= cmd_pipe_append ;
+ break ;
+
+ case '|':
+ parsed->out_pipe = cmd_pipe_shell ;
+ break ;
+
+ case '*':
+ parsed->out_pipe = cmd_pipe_dev_null ;
+ break ;
+
+ default:
+ --p->p ; /* put back */
+ break ;
+ }
+ } ;
+
+ /* Could now have options, but presently do not */
+ if (p->p < p->e)
+ {
+ ok = false ;
+ } ;
+ } ;
+
+ if (!ok)
+ return cmd_parse_error(parsed, t, 0, "invalid 'pipe out'") ;
+
+ return CMD_SUCCESS ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * If token is incomplete make a copy of it and '\0' terminate.
+ *
+ * If if contains quotes or escapes process those down.
+ *
+ * For Quagga purposes, the following is done:
+ *
+ * inside '...': all characters stand for themselves, except '\t' -> ' ',
+ * and, of course, the terminating '.
+ *
+ * This is just like the shell.
+ *
+ * inside "...": all characters stand for themselves, except '\t' -> ' ',
+ * and, of course, the terminating ", plus the following
+ * \x escapes are processed:
+ *
+ * \" -> " -- so can have " in "...."
+ * \\ -> \ -- so can have \ in "...."
+ * \$ -> $ -- so can have $ in "...."
+ *
+ * outside quotes: all characters stand for themselves, except:
+ *
+ * \sp -> sp -- so can escape the odd space (cf shell)
+ * \tab -> sp -- ditto of tab, but map to space
+ * \? -> ? )
+ * \' -> ' )
+ * \" -> " ) so can escape the odd meta character
+ * \< -> < ) where required or to taste.
+ * \> -> > )
+ * \! -> ! )
+ * \# -> \# )
+ *
+ * NB: with the exception of $ inside of "..." the carefully avoids the
+ * regex meta characters: .*+?^$_ and (|)[-]
+ * and the regex escaped forms of those and the back reference \1 etc.
+ *
+ * $ in "..." is reserved for future use as a variable or other
+ * substitution start.
+ *
+ * Returns: CMD_SUCCESS <=> OK
+ * CMD_ERR_PARSE <=> invalid escape or incomplete quotes
*
* NB: if fails, returns token completed as far as possible.
*/
-extern bool
-cmd_token_do_complete(token t)
+static cmd_return_code_t
+cmd_token_complete(cmd_parsed parsed, cmd_token t)
{
- char *s, *p, *q ;
- char ch ;
- bool ok = true ;
- bool dq = false ;
+ cpp_t p ;
+ pp_t q ;
+ bool dq ;
+ cmd_return_code_t ret ;
+
+ if ((t->type & cmd_tok_incomplete) == 0)
+ return CMD_SUCCESS ; /* Quick out if nothing to do */
- p = s = cmd_token_value(t) ;
- q = p ;
- while (*p != '\0')
+ /* To process quotes etc works from the elstring that points to
+ * the original line, to the qstring.
+ *
+ * For quotes and escapes, the result is always no longer than the
+ * original.
+ */
+ els_cpp_nn(p, t->ot) ; /* original token */
+
+ qs_new_size(t->qs, p->e - p->p) ; /* discard alias & set qs to be
+ big enough for original */
+ qs_pp_nn(q, t->qs) ; /* where to complete token to */
+
+ ret = CMD_SUCCESS ;
+ dq = false ;
+ while (p->p < p->e)
{
- switch (*p)
+ switch (*p->p)
{
+ case '\t':
+ *q->p++ = ' ' ; /* '\t' -> ' ' */
+ ++p->p ;
+ break ;
+
case '\'':
- ++p ; /* skip leading '\'' */
+ ++p->p ; /* skip leading ' */
while (1)
{
- if (*p == '\0')
+ if (p->p == p->e)
{
- ok = false ; /* broken '...' */
+ ret = cmd_parse_error(parsed, t, 0, "missing closing '") ;
break ;
} ;
- if (*p == '\'')
+ if (*p->p == '\'')
{
- ++p ; /* skip trailing '\'' */
+ ++p->p ; /* skip trailing ' */
break ; /* done '....' */
} ;
- *q++ = *p++ ;
+ if (*p->p == '\t')
+ {
+ *q->p++ = ' ' ; /* '\t' -> ' ' */
+ ++p->p ;
+ }
+ else
+ *q->p++ = *p->p++ ; /* rest as is */
} ;
break ;
case '"':
- ++p ; /* skip '"' */
- dq = !dq ;
+ ++p->p ; /* skip " */
+ dq = !dq ; /* switch state */
break ;
case '\\':
- ++p ; /* step past '\\' */
- ch = cmd_token_escape(*p) ;
- if (ch == '\0')
+ *q->p++ = *p->p++ ; /* copy the \ */
+ if (p->p == p->e)
+ ret = cmd_parse_error(parsed, t, 0, "trailing \\") ;
+ else
{
- ok = false ;
- ch = *p ;
- if (ch == '\0')
- ch = '\\' ; /* \ at end is kept */
+ if (dq)
+ {
+ /* inside "...": \", \$ and \\ only */
+ if ((*p->p == '"') || (*p->p == '$') || (*p->p == '\\'))
+ --q->p ; /* strip the \ */
+ }
+ else
+ {
+ /* outside quotes: \sp \tab \< \> \! \# */
+ if ( (*p->p == '\t') || (*p->p == ' ') ||
+ (*p->p == '\'') || (*p->p == '"') ||
+ (*p->p == '<') || (*p->p == '>') ||
+ (*p->p == '!') || (*p->p == '#') )
+ --q->p ; /* strip the \ */
+ } ;
+
+ if (*p->p != '\t')
+ *q->p++ = *p->p++ ;
else
- *q++ = '\\' ; /* otherwise keep \x */
+ {
+ *q->p++ = ' ' ;
+ ++p->p ;
+ } ;
} ;
- *q++ = ch ;
- break ;
+ break ;
default:
- *q++ = *p++ ;
+ *q->p++ = *p->p++ ;
+ break ;
} ;
} ;
- qs_term_here(t->qs, q) ;
+ if (dq)
+ ret = cmd_parse_error(parsed, t, 0, "missing closing \"") ;
- return ok && !dq ;
+ if (ret == CMD_SUCCESS)
+ {
+ *q->p = '\0' ; /* '\0' terminate */
+ qs_set_len_nn(t->qs, q->p - qs_char_nn(t->qs)) ;
+ t->term = true ;
+ }
+ else
+ {
+ qs_set_alias_els(t->qs, t->ot) ;
+ } ;
+
+ return ret ;
}
/*------------------------------------------------------------------------------
- * Return escaped value of \e.
+ * Tokenise the given line and work out where cursor is wrt tokens.
+ *
+ * Looks for first token whose end is at or beyond the cursor position. Note
+ * that:
+ *
+ * * where the cursor is just after the last character of a token, it is at
+ * the end of that token.
+ *
+ * * where there is more than one space between tokens, and the cursor is
+ * after the first space, then it is deemed to be "on" the second token.
+ *
+ * - if there are spaces at the start of the line and the cursor is on
+ * one of those spaces, the it is "on" the first token.
*
- * Everything except '0'..'9', 'A'..'Z', 'a'..'z' can be escaped -- these are
- * reserved for future actual escapes !
+ * - if the line is blank, with zero or more spaces, the cursor is "on"
+ * the eol token.
*
- * Returns '\0' if e == '\0' or if \e is an invalid escape.
+ * Note that where a line ends in a comment, there are no trailing spaces.
+ *
+ * Returns: true <=> "in a special place"
+ *
+ * Is "in a special place" if the cursor is:
+ *
+ * a. in a quoted string of any type
+ * b. in an escape (so immediately after a '\')
+ * c. after the '!' or '#' on a line which consists only of a comment
+ * d. after the first '<' or '>' of the first pipe token on the line
+ *
+ * If NOT "in a special place", will set:
+ *
+ * * parsed->cti -- cursor token index
+ * * parsed->rp -- cursor position relative to the start of token
+ *
+ * Note that the cursor token may be a comment or pipe token, or the eol token
+ * at the end of the line.
*/
-static char
-cmd_token_escape(char e)
+extern bool
+cmd_token_position(cmd_parsed parsed, qstring line)
{
- if ((e < '0') || (e > 'z'))
- return e ;
- return isalpha(e) ? '\0' : e ;
+ cmd_token t ;
+ uint ti ;
+ uint cp, ep ;
+
+ cpp_t p ;
+ const char* q ;
+ const char* e ;
+ bool sq, dq, bs ;
+
+ /* Re-initialise parsed object and tokenise the given line */
+ cmd_tokenise(parsed, line) ;
+
+ /* Get the cursor position */
+ cp = qs_cp_nn(line) ;
+
+ /* Look for the last token whose end is <= cp
+ *
+ * Will position on last, "eol" token -- which is not counted in
+ * parsed->num_tokens -- if is beyond the last real token on the line.
+ */
+ t = NULL ;
+ ti = 0 ;
+ while (1)
+ {
+ t = cmd_token_get(parsed->tokens, ti) ;
+
+ if (cp > t->tp)
+ {
+ /* As soon as we are past '<', '>', '!' or '#' -- return "special" */
+ if ((t->type & ( cmd_tok_in_pipe
+ | cmd_tok_out_pipe | cmd_tok_comment) ) != 0)
+ return true ;
+ } ;
+
+ ep = t->tp + els_len_nn(t->ot) ;
+
+ if ((cp <= ep) || (ti == parsed->num_tokens))
+ break ; /* stop when found token (or at eol) */
+
+ ++ti ;
+ } ;
+
+ parsed->cti = ti ;
+ parsed->ctl = els_len_nn(t->ot) ;
+ parsed->rp = (int)cp - (int)t->tp ;
+
+ /* Arrive with t = token in which cp belongs
+ *
+ * If the token is incomplete, then need to check for "ins" -- unless is
+ * already "ins".
+ */
+ if ((t->type & cmd_tok_incomplete) == 0)
+ return false ;
+
+ /* Scan to see if in '...' or "..." or after '\'. */
+
+ els_cpp_nn(p, t->ot) ; /* original token */
+
+ q = p->p + (cp - t->tp) ; /* position interested in */
+ assert(q > p->p) ;
+
+ dq = false ;
+ sq = false ;
+ bs = false ;
+
+ e = (q <= p->e) ? q : p->e ; /* stop at q or end of token */
+
+ while (p->p < e)
+ {
+ switch (*p->p)
+ {
+ case '\'':
+ if (!dq && !bs) /* ignore ' inside "..." & after \ */
+ sq = !sq ;
+ break ;
+
+ case '\"': /* ignore " inside '...' & after \ */
+ if (!sq && !bs)
+ dq = !dq ;
+ break ;
+
+ case '\\':
+ if (!sq) /* ignore \ inside '...' */
+ bs = !bs ; /* cope with \\ */
+ break ;
+
+ default:
+ break ;
+ } ;
+
+ ++p->p ;
+ } ;
+
+ /* Have scanned to but excluding current character or end of token, whichever
+ * came first.
+ *
+ * If in '...' or "...", then is "ins"
+ * If is immediately after \, then is "ins".
+ */
+ return sq || dq || (bs && (p->e <= q)) ;
} ;
/*==============================================================================
- * Parser object
+ * Match functions.
+ *
+ * Is the given string a, possibly incomplete, value of the required kind ?
*/
+static match_strength_t cmd_ipv4_match(const char* cp, uint prefix) ;
+static match_strength_t cmd_prefix_match(const char* cp, uint prefix) ;
+
/*------------------------------------------------------------------------------
- * Initialise a new cmd_parsed object, allocating if required
+ * Is this an IPv4 Address
+ *
+ * 999.999.999.999 -- each 0..255, no leading zeros, decimal only.
+ *
+ * Returns: mt_no_match -- improperly formed
+ * mt_ipv4_address_partial -- OK as far as it goes (or empty)
+ * mt_ipv4_address_complete -- syntactically complete
*/
-extern cmd_parsed
-cmd_parse_init_new(cmd_parsed parsed)
+static match_type_t
+cmd_ipv4_address_match(cmd_token t)
{
- if (parsed == NULL)
- parsed = XCALLOC(MTYPE_CMD_PARSED, sizeof(*parsed)) ;
- else
- memset(parsed, 0, sizeof(*parsed)) ;
+ match_strength_t ms ;
- /* Zeroising the structure has set:
- *
- * cmd = NULL -- no command parsed, yet
- * cnode -- no node set, yet
- *
- * do_shortcut -- false
- * onode -- not material (do_shortcut is false)
- *
- * pipes = 0 -- cmd_pipe_none
- */
- confirm(cmd_pipe_none == 0) ;
+ ms = cmd_ipv4_match(cmd_token_make_string(t), 0) ;
- cmd_token_vector_init(parsed->tokens) ;
- cmd_token_vector_init(parsed->read_pipe_tokens) ;
- cmd_token_vector_init(parsed->write_pipe_tokens) ;
- cmd_arg_vector_init(parsed) ;
+ if (ms == ms_var_complete)
+ return mt_ipv4_address_complete ;
+ if (ms == ms_partial)
+ return mt_ipv4_address_partial ;
- return parsed ;
+ return mt_no_match ;
} ;
/*------------------------------------------------------------------------------
- * Empty out and (if required) free a cmd_parsed object
+ * Is this an IPv4 Prefix
+ *
+ * 999.999.999.999/99 -- each 0..255, no leading zeros, decimal only.
+ * and prefix length must be <= 32
+ *
+ * Returns: mt_no_match -- improperly formed
+ * mt_ipv4_prefix_partial -- OK as far as it goes (or empty)
+ * mt_ipv4_prefix_complete -- syntactically complete
+ *
+ * NB: partly_match is returned for anything valid before the '/', but which
+ * has no '/' or no number after the '/'.
*/
-extern cmd_parsed
-cmd_parse_reset(cmd_parsed parsed, bool free_structure)
+static match_type_t
+cmd_ipv4_prefix_match(cmd_token t)
{
- if (parsed != NULL)
+ match_strength_t ms ;
+
+ ms = cmd_ipv4_match(cmd_token_make_string(t), 32) ;
+
+ if (ms == ms_var_complete)
+ return mt_ipv4_prefix_complete ;
+ if (ms == ms_partial)
+ return mt_ipv4_prefix_partial ;
+
+ return mt_no_match ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Is this an IPv4 Address or Prefix:
+ *
+ * 999.999.999.999[/99] -- each 0..255, no leading zeros, decimal only.
+ * and prefix length must be <= n
+ *
+ * Returns: ms_no_match -- improperly formed
+ * ms_partial -- OK as far as it goes (or empty)
+ * ms_var_complete -- syntactically complete
+ *
+ * NB: partly_match is returned for anything valid before the '/', but which
+ * has no '/' or no number after the '/'.
+ */
+static match_strength_t
+cmd_ipv4_match(const char* cp, uint prefix)
+{
+ uint nums ;
+
+ for (nums = 0 ; nums < 4 ; ++nums)
{
- cmd_empty_parsed_tokens(parsed) ; /* give back tokens */
- cmd_arg_vector_free(parsed) ; /* give back vector body */
+ if (*cp == '.') /* need a '.' except at start */
+ {
+ if (nums == 0)
+ return ms_no_match ;
- if (free_structure)
- XFREE(MTYPE_CMD_PARSED, parsed) ; /* sets parsed = NULL */
+ ++cp ; /* step past '.' */
+ }
+ else
+ {
+ if (nums != 0)
+ return (*cp == '\0') ? ms_partial : ms_no_match ;
+ } ;
+
+ /* Collect a decimal number 0..255, no leading zeros.
+ *
+ * Rejects anything other than digits -- including '/' and '.'.
+ *
+ * Accepts '\0' as partial -- which accepts empty strings.
+ */
+ if (*cp == '0')
+ {
+ ++cp ;
+ if (isdigit(*cp))
+ return ms_no_match ; /* reject leading zeros */
+ }
+ else
+ {
+ char* ep ;
+
+ if (isdigit(*cp))
+ {
+ if (strtoul(cp, &ep, 10) <= 255)
+ cp = ep ;
+ else
+ return ms_no_match ; /* reject invalid number */
+ }
+ else
+ return (*cp == '\0') ? ms_partial : ms_no_match ;
+ } ;
+ } ;
+
+ /* Arrive here with 4 numbers */
+
+ if (prefix == 0)
+ return (*cp == '\0') ? ms_var_complete : ms_no_match ;
+ else
+ return cmd_prefix_match(cp, prefix) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Is this a Prefix:
+ *
+ * /99 no leading zeros, decimal only, value must be <= n.
+ *
+ * Arrives here with *cp pointing at where there should be a '/'.
+ *
+ * Returns: ms_no_match -- improperly formed
+ * ms_partial -- OK as far as it goes (or empty).
+ * ms_var_complete -- syntactically complete
+ */
+static match_strength_t
+cmd_prefix_match(const char* cp, uint prefix)
+{
+ if (*cp != '/')
+ return (*cp == '\0') ? ms_partial : ms_no_match ;
+
+ /* OK have '/' and a prefix is now expected. */
+
+ ++cp ; /* step past '/' */
+
+ if (*cp == '\0')
+ return ms_partial ; /* if nothing after '/' */
+
+ if (*cp == '0')
+ ++cp ;
+ else
+ {
+ char* ep ;
+ if (isdigit(*cp) && (strtoul(cp, &ep, 10) <= prefix))
+ cp = ep ;
else
- cmd_parse_init_new(parsed) ;
+ return ms_no_match ; /* reject invalid number */
} ;
- return parsed ;
+ if (*cp != '\0')
+ return ms_no_match ; /* something other than digits after the '/',
+ or leading zero, or number too big */
+
+ return ms_var_complete ;
} ;
-/*==============================================================================
- * Match functions.
+/*------------------------------------------------------------------------------
+ * IPv6 Address and Prefix matching.
+ */
+
+#ifdef HAVE_IPV6
+
+static match_strength_t cmd_ipv6_match(const char* cp, uint prefix) ;
+
+
+
+/*------------------------------------------------------------------------------
+ * Is this an IPv6 Address
*
- * Is the given string a, possibly incomplete, value of the required kind ?
+ * h:h:... -- RFC 4291 rules & prefix length must be <= n
+ *
+ * Returns: mt_no_match -- improperly formed
+ * mt_ipv6_address_partial -- OK as far as it goes (or empty)
+ * mt_ipv6_address_complete -- syntactically complete
+ *
+ * NB: partly_match is returned for anything valid before the '/', but which
+ * has no '/' or no number after the '/'.
*/
+static match_type_t
+cmd_ipv6_address_match (cmd_token t)
+{
+ match_strength_t ms ;
+
+ ms = cmd_ipv6_match(cmd_token_make_string(t), 0) ;
+ if (ms == ms_var_complete)
+ return mt_ipv6_address_complete ;
+ if (ms == ms_partial)
+ return mt_ipv6_address_partial ;
+
+ return mt_no_match ;
+} ;
/*------------------------------------------------------------------------------
- * Is this an IPv4 Address:
+ * Is this an IPv6 Prefix
*
- * 999.999.999.999 -- where no part may be > 255
+ * h:h:...[/99] -- RFC 4291 rules & prefix length must be <= 128
*
- * TODO: cmd_ipv4_match() seems to accept leading '.' ?
- * TODO: cmd_ipv4_match() seems to accept leading zeros ?
+ * Returns: mt_no_match -- improperly formed
+ * mt_ipv6_prefix_partial -- OK as far as it goes (or empty)
+ * mt_ipv6_prefix_complete -- syntactically complete
*
- * Returns: no_match -- improperly formed
- * partly_match -- accepts empty string
- * exact_match -- syntactically complete
+ * NB: partly_match is returned for anything valid before the '/', but which
+ * has no '/' or no number after the '/'.
*/
-extern match_type_t
-cmd_ipv4_match (const char *str)
+static match_type_t
+cmd_ipv6_prefix_match(cmd_token t)
{
- const char *sp;
- int dots = 0, nums = 0;
- char buf[4];
+ match_strength_t ms ;
+
+ ms = cmd_ipv6_match(cmd_token_make_string(t), 128) ;
+
+ if (ms == ms_var_complete)
+ return mt_ipv6_prefix_complete ;
+ if (ms == ms_partial)
+ return mt_ipv6_prefix_partial ;
- if (str == NULL)
- return partly_match;
+ return mt_no_match ;
+} ;
- for (;;)
+/*------------------------------------------------------------------------------
+ * Is this an IPv6 Address or Prefix:
+ *
+ * h:h:...[/99] -- RFC 4291 rules & prefix length must be <= n
+ *
+ * Returns: ms_no_match -- improperly formed
+ * ms_partial -- OK as far as it goes (or empty)
+ * ms_var_complete -- syntactically complete
+ *
+ * NB: partly_match is returned for anything valid before the '/', but which
+ * has no '/' or no number after the '/'.
+ */
+static match_strength_t
+cmd_ipv6_match(const char* cp, uint prefix)
+{
+ bool double_colon ;
+ uint nums ;
+
+ double_colon = false ;
+ nums = 0 ;
+
+ /* At start, the first time around the loop... */
+
+ for (nums = 0 ; nums < 8 ; ++nums)
{
- memset (buf, 0, sizeof (buf));
- sp = str;
- while (*str != '\0')
+ const char* sp, * ep ;
+
+ /* After number (nums != 0), or at start.
+ *
+ * Deal with (a) ':', '::' and '::/'.
+ * (b) '/' -- valid only if had '::'.
+ * (c) '\0' -- partial unless have had '::'
+ * (d) if not at start, must have one of the above.
+ */
+ if (*cp == ':')
{
- if (*str == '.')
+ /* (a) ':', '::' and '::/'.
+ *
+ * At start can accept '::', but not ':' (unless '\0' follows).
+ *
+ * If not at start, accept ':' and accept '::' if not already seen.
+ *
+ * After '::' can have the full complement of numbers, or '/' or
+ * '\0' which bring the number part to an end.
+ *
+ * After ':' we accept '\0' but explicitly reject '/' (and we
+ * reject a ':' at the start if not followed by '\0').
+ */
+ ++cp ; /* step past ':' */
+
+ if (*cp == ':')
{
- if (dots >= 3)
- return no_match;
+ /* '::' -- counts as number, can be followed by '/' */
- if (*(str + 1) == '.')
- return no_match;
+ if (double_colon)
+ return ms_no_match ; /* at most one */
- if (*(str + 1) == '\0')
- return partly_match;
+ ++cp ; /* step past '::' */
- dots++;
- break;
+ double_colon = true ;
+ ++nums ; /* counts as a number */
+
+ if ((nums == 8) || (*cp == '/') || (*cp == '\0'))
+ break ; /* no more numbers */
}
- if (!isdigit ((int) *str))
- return no_match;
+ else if (*cp == '\0')
+ return ms_partial ; /* accepts bare ':', inter alia */
- str++;
+ else if ((*cp == '/') || (nums == 0))
+ return ms_no_match ;
+ }
+ else if (*cp == '/')
+ {
+ /* (b) '/' -- valid only if had '::'. */
+ if (double_colon)
+ break ;
+ else
+ return ms_no_match ;
+ }
+ else if (*cp == '\0')
+ {
+ /* (c) '\0' -- partial unless have had '::' */
+ if (double_colon)
+ break ;
+ else
+ return ms_partial ; /* accept empty string, inter alia */
}
+ else if (nums != 0)
+ /* (d) if not at start, must have one of the above. */
+ return ms_no_match ;
+
+ assert(*cp != '\0') ;
+
+ /* Is now at start, or after ':' and is not '\0'.
+ *
+ * Require 1..4 hex digits -- will also accept 1..3 decimals !
+ *
+ * Rejects anything else, including '/' at this stage.
+ */
+ sp = cp ;
+ ep = cp + 4 ;
+ do
+ {
+ if (((*cp >= '0') && (*cp <= '9')) || ((*cp >= 'A') && (*cp <= 'F'))
+ || ((*cp >= 'a') && (*cp <= 'f')))
+ ++cp ;
+ else
+ {
+ if (cp > sp)
+ break ;
+ else
+ return ms_no_match ; /* no digits */
+ }
+ }
+ while (cp < ep) ;
- if (str - sp > 3)
- return no_match;
+ /* Watch out for '.' ! */
- strncpy (buf, sp, str - sp);
- if (atoi (buf) > 255)
- return no_match;
+ if (*cp == '.')
+ {
+ /* Can have IPv4 trailing part, if that would account for the
+ * last two number parts of the IPv6.
+ *
+ * Note that a '.' after something which is not simple decimal
+ * 0..255 will be rejected by cmd_ipv4_match().
+ *
+ * Note also that we pass through the prefix requirement.
+ */
+ if ((nums == 6) || (double_colon && (nums < 6)))
+ return cmd_ipv4_match(sp, prefix) ;
+ else
+ return ms_no_match ;
+ } ;
+ } ;
- nums++;
+ /* Arrives here either because nums == 8, or met '/' or '\0' after '::
+ *
+ * So only get here if have a valid end of the digits part of the IPv6
+ */
+
+ assert((nums == 8) || double_colon) ;
+
+ if (prefix == 0)
+ return (*cp == '\0') ? ms_var_complete : ms_no_match ;
+ else
+ return cmd_prefix_match(cp, prefix) ;
+} ;
+
+
+#endif /* HAVE_IPV6 */
+
+/*------------------------------------------------------------------------------
+ * Is this a decimal number in the allowed range:
+ *
+ * Returns: mt_no_match -- improperly formed or empty
+ * mt_range_partial -- OK as far as it went (or empty string)
+ * mt_range_complete -- syntactically complete
+ */
+static match_type_t
+cmd_range_match (cmd_item item, cmd_token t)
+{
+ const char* cp, * dp ;
+ char *ep ;
+ int base ;
+ long val;
+
+ confirm((LONG_MAX > item_max_number) && (LONG_MIN < -item_max_number)) ;
+
+ cp = cmd_token_make_string(t) ;
+
+ /* Worry about any sign */
+
+ dp = cp ;
+
+ if ((*cp == '-') || (*cp == '+'))
+ {
+ if (!item->range_sign_allowed)
+ return mt_no_match ; /* reject '-' or '+' */
+
+ ++dp ; /* step to digit */
+ }
+ else
+ {
+ if (item->range_sign_required)
+ return mt_no_match ;
+ } ;
+
+ /* Worry about leading digits and hex, and no digits at all */
+
+ base = 10 ; /* by default. */
+
+ if (*dp == '\0')
+ return mt_range_partial ; /* accepts empty string, inter alia */
- if (*str == '\0')
- break;
+ else if (*dp == '0')
+ {
+ ++dp ; /* step past zero */
+
+ if (*dp != '\0')
+ {
+ /* No leading zeros and no stinking octal -- but allow hex */
+ if ((*dp != 'x') && (*dp != 'X'))
+ return mt_no_match ;
- str++;
+ ++dp ; /* step past 'x' or 'X' */
+ base = 16 ;
+
+ if (*dp == '\0')
+ return mt_range_partial ;
+ } ;
}
- if (nums < 4)
- return partly_match;
+ else if (!isdigit(*dp))
+ return mt_no_match ;
- return exact_match;
-}
+ /* The string starts with digit, possibly preceded by sign, and possibly
+ * an 'x' or 'X' with at least 1 further character.
+ */
+ val = strtol(cp, &ep, base) ;
+ if (*ep != '\0')
+ return mt_no_match ;
+
+ /* Is the result in range ? */
+
+ if (val >= item->range_min && val <= item->range_max)
+ return mt_range_complete ; /* on the money */
+
+ /* Want to return mt_range_partial iff adding digits might make
+ * an in range value.
+ *
+ * If val is < 0, then adding digits makes it smaller.
+ * If val is == 0, not allowed to add digits.
+ * If val is > 0, then adding digits makes it bigger.
+ */
+ if (val < item->range_min)
+ {
+ /* Is less than minimum, so partial match if can get bigger. */
+ return (val > 0) ? mt_range_partial : mt_no_match ;
+ }
+ else
+ {
+ /* Is more than maximum, so partial match if can get smaller. */
+ return (val < 0) ? mt_range_partial : mt_no_match ;
+ } ;
+} ;
+
+/*==============================================================================
+ * Command "filtering".
+ *
+ * The command parsing process starts with a (shallow) copy of the cmd_vector
+ * entry for the current "node".
+ *
+ * So cmd_v contains pointers to struct cmd_command values. When match fails,
+ * the pointer is set NULL -- so parsing is a process of reducing the cmd_v
+ * down to just the entries that match.
+ *
+ * Each cmd_command has a vector "items", which contains an entry for each
+ * "token" position. That entry is a vector containing the possible values at
+ * that position.
+ */
+
+static int cmd_item_filter(cmd_parsed parsed, cmd_item item, cmd_token t) ;
/*------------------------------------------------------------------------------
- * Is this an IPv4 Prefix:
+ * Prepare to filter commands in the node being parsed in.
*
- * 999.999.999.999/99 -- where no part may be > 255,
- * and prefix length may not be > 32
+ * The execute option turns off all partial matching -- so will not match, say,
+ * 222 as a possible IP address ! This means that the result of the filter
+ * operation will be executable command(s), only.
*
- * TODO: cmd_ipv4_prefix_match() seems to accept leading '.' ?
- * TODO: cmd_ipv4_prefix_match() seems to accept leading zeros ?
+ * The execute option also pre-filters the command vector to discard all
+ * commands which are too short or too long to match the current line.
*
- * Returns: no_match -- improperly formed
- * partly_match -- accepts empty string
- * exact_match -- syntactically complete
+ * The strict option turns off partial matching of keywords, so only complete
+ * keywords will do. This is used for the configuration file, so that new
+ * commands can be added !
*
- * NB: partly_match is returned for anything valid before the '/', but which
- * has no '/' or no number after the '/'.
+ * Returns: number of commands which may match to.
+ */
+static uint
+cmd_filter_prepare(cmd_parsed parsed, cmd_parse_type_t type)
+{
+ vector src_v ;
+ uint ci ;
+ uint nct ;
+
+ bool execution = (type & cmd_parse_execution) != 0 ;
+ bool strict = (type & cmd_parse_strict) != 0 ;
+
+ /* get commands for the current node */
+ src_v = ((cmd_node)vector_get_item(node_vector, parsed->cnode))
+ ->cmd_vector ;
+
+ assert(src_v != NULL) ; /* invalid parsed->cnode ?? */
+
+ /* empty the working commands vector, making sure big enough for the current
+ * node's commands.
+ *
+ * Note that the cmd_v lives for as long as the parsed object, so will
+ * grow over time to accommodate what is required.
+ */
+ vector_re_init(parsed->cmd_v, vector_length(src_v)) ;
+
+ /* Filter out commands which are too short.
+ *
+ * For execution, can filter out commands which are too long.
+ */
+ nct = parsed->num_command ;
+
+ for (ci = 0 ; ci < vector_length(src_v) ; ++ci)
+ {
+ cmd_command cmd ;
+
+ cmd = vector_get_item(src_v, ci) ;
+
+ if ( cmd->nt_max < nct)
+ continue ; /* ignore if too short */
+
+ if ((cmd->nt_min > nct) && execution)
+ continue ; /* ignore if too long */
+
+ vector_push_item(parsed->cmd_v, cmd) ;
+ } ;
+
+ /* Other preparation for filtering
+ *
+ * The min_strength is set according to whether is for execution. The
+ * strongest match is set from that. This means that any match which does
+ * not meet the minimum criterion is automatically excluded.
+ */
+
+ parsed->min_strength = execution ? ms_min_execute : ms_min_parse ;
+ parsed->strict = strict ;
+
+ parsed->strongest = parsed->min_strength ;
+ parsed->best_complete = mt_no_match ;
+
+ return vector_length(parsed->cmd_v) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Filter set commands by matching items against the given token.
+ *
+ * Takes: parsed -- cmd_parsed object, previously prepared by
+ * cmd_prepare_fiter().
+ * ii -- item index (0 == first)
+ * keep_items -- how to filter
+ *
+ * The item index is the index wrt the start of the commands being matched,
+ * not the index of the token in the command line.
+ *
+ * Keywords must match strictly or partly, depending on the filtering state
+ * in the parsed object.
+ *
+ * Variables must match completely if filtering for execution, otherwise may
+ * match partially.
+ *
+ * Returns: the number of items matched.
+ *
+ * NB: when filtering for execution, will not accept any partial matches for
+ * variables (but may accept partial matches for keywords). So, once the
+ * last token has been filtered against, the number of items matched gives
+ * the number of unambiguous commands, with complete values, that the line
+ * matches.
*/
-extern match_type_t
-cmd_ipv4_prefix_match (const char *str)
+static uint
+cmd_filter(cmd_parsed parsed, uint ii, bool keep_items)
{
- const char *sp;
- int dots = 0;
- char buf[4];
+ uint ci ; /* command index -- in cmd_v */
+ uint ti ; /* token index in command line */
+ uint c_keep ;
+ uint i_keep ;
+ cmd_token t ;
+
+ /* Reset the filtering state */
+
+ parsed->strongest = parsed->min_strength ;
+ parsed->best_complete = mt_no_match ;
+
+ /* If we are keeping the items which are matched, set the item_v
+ * empty and guess that may get one item per command.
+ *
+ * Note that the item_v lives for as long as the parsed object, so will
+ * grow over time to accommodate what is required.
+ */
+ if (keep_items)
+ vector_re_init(parsed->item_v, vector_length(parsed->cmd_v)) ;
- if (str == NULL)
- return partly_match;
+ /* Work down the cmd_v, attempting to match cmd_items against cmd_tokens.
+ *
+ * Keep in the cmd_v the commands for which we get a match.
+ *
+ * At the same time, if required, keep in the item_v all the items which have
+ * matched.
+ */
+ if (ii < parsed->num_command)
+ ti = parsed->first_command + ii ; /* map to token index */
+ else
+ {
+ /* special case of filtering against the empty token at the end of
+ * the command line -- this is for command line help stuff.
+ *
+ * Note that there may be out_pipe or even comment tokens in between,
+ * so we here set the ti to the trailing eol token.
+ */
+ assert(ii == parsed->num_command) ;
+ ti = parsed->num_tokens ;
+ }
+ t = cmd_token_get(parsed->tokens, ti) ;
- for (;;)
+ c_keep = 0 ;
+ i_keep = 0 ;
+
+ if (keep_items)
+ vector_re_init(parsed->item_v, vector_length(parsed->cmd_v)) ;
+
+ for (ci = 0; ci < vector_length(parsed->cmd_v); ci++)
{
- memset (buf, 0, sizeof (buf));
- sp = str;
- while (*str != '\0' && *str != '/')
+ cmd_command cmd ;
+ cmd_item item ;
+ int best ;
+
+ cmd = vector_get_item(parsed->cmd_v, ci) ;
+
+ if (ii < cmd->nt)
+ item = vector_get_item(cmd->items, ii) ;
+
+ else if (ii == cmd->nt_max)
+ item = &eol_item ; /* match at end of line */
+
+ else if (ii > cmd->nt_max)
+ continue ; /* discard commands we are beyond, now */
+
+ else /* cmd->nt < cmd->nt_max <=> vararg. */
+ {
+ /* It is elsewhere arranged that a vararg is always the last
+ * item for a given command.
+ *
+ * We cope with all tokens from the first vararg onwards by matching
+ * them all against the vararg. Inter alia this allows for a
+ * vararg to check each argument in turn -- iff feel like doing
+ * that.
+ */
+ item = cmd->vararg ;
+
+ /* Must have something and must now only check against the varag.
+ * (Not anything else in the (...) if vararg was in one !)
+ */
+ assert((item != NULL) && (item->next == NULL)) ;
+ } ;
+
+ /* See if get any sort of match at current position */
+ best = -1 ;
+ while (item != NULL)
{
- if (*str == '.')
+ int ret ;
+ ret = cmd_item_filter(parsed, item, t) ;
+
+ if (ret >= 0)
{
- if (dots == 3)
- return no_match;
+ if (ret > 0)
+ i_keep = 0 ;
- if (*(str + 1) == '.' || *(str + 1) == '/')
- return no_match;
+ if (keep_items)
+ vector_set_item(parsed->item_v, i_keep++, item) ;
+ else
+ ++i_keep ;
+ } ;
- if (*(str + 1) == '\0')
- return partly_match;
+ if (ret > best)
+ best = ret ;
- dots++;
- break;
- }
+ item = item->next ;
+ } ;
- if (!isdigit ((int) *str))
- return no_match;
+ /* Keep if had a match */
+ if (best >= 0)
+ {
+ if (best > 0)
+ c_keep = 0 ; /* better than all the rest */
- str++;
- }
+ vector_set_item(parsed->cmd_v, c_keep++, cmd) ;
+ } ;
+ } ;
+
+ if (keep_items)
+ vector_set_length(parsed->item_v, i_keep) ; /* discard what did not keep */
- if (str - sp > 3)
- return no_match;
+ vector_set_length(parsed->cmd_v, c_keep) ; /* discard what did not keep */
- strncpy (buf, sp, str - sp);
- if (atoi (buf) > 255)
- return no_match;
+ return i_keep ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Filter given item, in given parsed state, attempting to match given token.
+ *
+ * Update the parsed state if get a better match.
+ *
+ * Returns: < 0 => is not as good )
+ * == 0 => equally good ) compared to best match to date.
+ * > 0 => better than )
+ *
+ * NB: the matching functions will partially match an empty string.
+ *
+ * The ms_anything types of item, will match an empty string.
+ */
+static int
+cmd_item_filter(cmd_parsed parsed, cmd_item item, cmd_token t)
+{
+ match_strength_t ms ;
+ match_type_t mt ;
+ int cw ;
- if (dots == 3)
+ /* Ignore item if the best we can hope for is not as strong as what we
+ * have already.
+ */
+ mt = item_best_match(item->type) ;
+ ms = match_match_strength(mt) ;
+
+ if ((mt < parsed->best_complete) || (ms < parsed->strongest))
+ return -1 ; /* cannot even be as good */
+
+ /* Bang the rocks together to get match type
+ *
+ * TODO: do we need mt_xxx_partial etc ?
+ */
+ if (t->type == cmd_tok_eol)
+ {
+// mt = (item->type == item_eol) ? mt_eol : mt_eol_partial ;
+ mt = mt_eol_partial ;
+ }
+ else
+ {
+ switch (item->type)
+ {
+ case item_null:
+ zabort("invalid item_null") ;
+ break ;
+
+ case item_eol:
+ break ;
+
+ case item_keyword:
+ cw = els_cmp_word(t->ot, item->str) ;
+
+ if (cw > 0) /* nope */
+ mt = mt_no_match ;
+ else if (cw == 0) /* exact match */
+ mt = mt_keyword_complete ;
+ else /* partial match */
+ mt = parsed->strict ? mt_no_match
+ : mt_keyword_incomplete ;
+ break ;
+
+ case item_range:
+ mt = cmd_range_match(item, t) ;
+ break ;
+
+ case item_ipv4_address:
+ mt = cmd_ipv4_address_match(t) ;
+ break ;
+
+ case item_ipv4_prefix:
+ mt = cmd_ipv4_prefix_match(t) ;
+ break ;
+
+ case item_ipv6_address:
+ #ifdef HAVE_IPV6
+ mt = cmd_ipv6_address_match(t) ;
+ #endif /* HAVE_IPV6 */
+ break ;
+
+ case item_ipv6_prefix:
+ #ifdef HAVE_IPV6
+ mt = cmd_ipv6_prefix_match(t) ;
+ #endif /* HAVE_IPV6 */
+ break ;
+
+ case item_word:
+ mt = mt_word_match ;
+ break ;
+
+ case item_vararg:
+ mt = mt_vararg_match ;
+ break ;
+
+ case item_option_word:
+ mt = mt_option_word_match ;
+ break ;
+
+ default:
+ zabort("unknown item type") ;
+ } ;
+ } ;
+
+ /* Easy if did not match at all */
+ if (mt == mt_no_match)
+ return -1 ;
+
+ /* Is what we got worse, as good or better ?
+ *
+ * Update parsed to suit and return the news.
+ *
+ * Note that parsed->best_complete will be ms_no_match until parsed->strongest
+ * is set to ms_var_complete.
+ */
+ ms = match_match_strength(mt) ;
+
+ if (ms < parsed->strongest)
+ return -1 ;
+
+ if (ms == parsed->strongest)
+ return 0 ;
+
+ parsed->strongest = ms ;
+ return +1 ;
+} ;
+
+/*==============================================================================
+ * Parsing of command lines
+ */
+
+/*------------------------------------------------------------------------------
+ * Parse a command in the given "node", if possible, ready for execution.
+ *
+ * If 'exact': use cmd_filter_by_string()
+ * otherwise: use cmd_filter_by_completion()
+ *
+ * If 'do': see if there is a 'do' at the front and proceed accordingly.
+ *
+ * If 'tree': move up the node tree to find command if not found in the
+ * current node.
+ */
+
+static cmd_return_code_t cmd_parse_phase_one(cmd_parsed parsed,
+ cmd_parse_type_t type, node_type_t node) ;
+static cmd_return_code_t cmd_parse_phase_one_b(cmd_parsed parsed, uint nt) ;
+static cmd_return_code_t cmd_parse_phase_two(cmd_parsed parsed,
+ cmd_parse_type_t type) ;
+
+/*------------------------------------------------------------------------------
+ * Parse a command in the given "node", or (if required) any of its ancestors.
+ *
+ * Requires command line to have been tokenised.
+ *
+ * Returns: CMD_SUCCESS => successfully parsed command, and the result is
+ * in the given parsed structure, ready for execution.
+ *
+ * NB: parsed->cnode may not be the incoming node.
+ *
+ * NB: parsed->parts is what was found
+ *
+ * NB: parsed->cmd->daemon => daemon
+ *
+ * CMD_EMPTY => line is empty, except perhaps for comment
+ * (iff parsing for execution)
+ *
+ * CMD_ERR_INCOMPLETE => "do" and nothing more
+ * (iff parsing for execution)
+ *
+ * CMD_SUCCESS_DAEMON => parsed successfully. Something for vtysh ??
+ *
+ * CMD_ERR_NO_MATCH => could find nothing to match the command
+ * line.
+ *
+ * CMD_ERR_AMBIGUOUS => found 2 or more possible matches.
+ * Or, if not parsing for execution, there
+ * were no command tokens.
+ *
+ * CMD_ERR_PARSING => something wrong ! See parsed->emess.
+ *
+ * NB: unless cmd_parse_no_tree, may have tried any ancestor nodes. Returns
+ * with parsed->cnode with node last tried.
+ *
+ * NB: unless cmd_parse_no_do, will have taken a leading "do", and pushed the
+ * parsed->cnode to ENABLE_NODE (if in MIN_DO_SHORTCUT_NODE or higher).
+ *
+ * NB: the command line MUST be preserved until the parsed command is no
+ * longer required -- no copy is made.
+ *
+ * NB: expects to have free run of everything in the vty structure (except
+ * the contents of the vty_io sub-structure) until the command completes.
+ *
+ * See elsewhere for description of parsed structure.
+ */
+extern cmd_return_code_t
+cmd_parse_command(cmd_parsed parsed, node_type_t node, cmd_parse_type_t type)
+{
+ cmd_return_code_t ret ;
+
+ /* Level 1 parsing
+ *
+ * Break down the tokens into:
+ *
+ * 1) in-pipe or command (with or without "do")
+ * 2) possible out-pipe
+ * 3) possible comment
+ *
+ * Complete any tokens which contain quotes and/or escapes.
+ *
+ * If there is a command then:
+ *
+ * - sort out any "do" and cut from start of command.
+ * - set parsed->cnode (from given node or according to "do")
+ * - set parsed->cmd = NULL
+ * - empty the argv vector
+ *
+ * Note that cmd_parse_phase_one only returns CMD_SUCCESS or CMD_ERR_PARSING.
+ */
+ ret = cmd_parse_phase_one(parsed, type, node) ;
+ if (ret != CMD_SUCCESS)
+ {
+ assert(ret == CMD_ERR_PARSING) ; /* no other error at this point */
+ return ret ;
+ } ;
+
+ /* If no command tokens, and is parsing for execution, then we are done...
+ * but watch out for bare "do"
+ */
+ if (((parsed->parts & cmd_part_command) == 0) &&
+ ((type & cmd_parse_execution) != 0))
+ {
+ if ((parsed->parts & ~cmd_part_comment) == cmd_parts_none)
+ return CMD_EMPTY ; /* accept empty */
+
+ if ((parsed->parts & cmd_part_do) != 0)
+ return CMD_ERR_INCOMPLETE ; /* reject "do" alone */
+
+ return CMD_SUCCESS ; /* accept pipes */
+ } ;
+
+ /* Level 2 parsing
+ *
+ * Try in the current node and then in parent nodes, if can.
+ *
+ * Cannot move up the node tree if is already at CONFIG_NODE or below.
+ * Note that "do" pushes us to the ENABLE_NODE -- which is below the
+ * CONFIG_NODE.
+ *
+ * Note that when not parsing for execution, may get here with no command
+ * tokens at all -- in which case cmd_parse_phase_two() will return
+ * CMD_ERR_AMBIGUOUS.
+ *
+ * Note that cmd_parse_phase_two only returns CMD_SUCCESS, CMD_ERR_NO_MATCH
+ * or CMD_ERR_AMBIGUOUS.
+ */
+ while (1)
+ {
+ node_type_t pnode ;
+
+ ret = cmd_parse_phase_two(parsed, type) ;
+
+ if (ret == CMD_SUCCESS)
+ break ;
+
+ if (ret != CMD_ERR_NO_MATCH)
+ return ret ;
+
+ confirm(ENABLE_NODE < CONFIG_NODE) ;
+
+ if (((type & cmd_parse_no_tree) != 0) || (parsed->cnode <= CONFIG_NODE))
+ return CMD_ERR_NO_MATCH ;
+
+ pnode = cmd_node_parent(parsed->cnode) ;
+
+ if (pnode == parsed->cnode)
+ return CMD_ERR_NO_MATCH ; /* done if no parent node. */
+
+ parsed->cnode = pnode ;
+ } ;
+
+ /* Parsed successfully.
+ *
+ * If for execution, fill the arg_vector
+ *
+ * The arg_vector is an array of pointers to '\0' terminated strings, which
+ * are pointers to the relevant tokens' qstring bodies.
+ */
+
+ if ((type & cmd_parse_execution) != 0)
+ {
+ uint ti ;
+
+ cmd_arg_vector_empty(parsed) ;
+
+ for (ti = 0; ti < parsed->num_command ; ti++)
{
- if (*str == '/')
+ bool take ;
+
+ if (ti < parsed->cmd->nt_min)
{
- if (*(str + 1) == '\0')
- return partly_match;
+ cmd_item item = vector_get_item (parsed->cmd->items, ti);
- str++;
- break;
+ take = item->arg ; /* follow item */
}
- else if (*str == '\0')
- return partly_match;
- }
+ else
+ take = true ; /* option or vararg token */
- if (*str == '\0')
- return partly_match;
+ if (take)
+ {
+ cmd_token t ;
+ t = cmd_token_get(parsed->tokens, parsed->first_command + ti) ;
+ cmd_arg_vector_push(parsed, cmd_token_make_string(t)) ;
+ } ;
+ } ;
+ } ;
- str++;
- }
+ /* Return appropriate form of success */
+ return parsed->cmd->daemon ? CMD_SUCCESS_DAEMON
+ : CMD_SUCCESS ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Phase 1 of command parsing
+ *
+ * Scan the tokens to break them up into the sections:
+ *
+ * - in-pipe -- '<' etc up to '>' or comment
+ * - do -- leading 'do' on the command (empty if in-pipe)
+ * - command -- actual command (empty if in-pipe)
+ * - out-pipe -- '>' etc up to comment
+ * - comment -- '!' or '#' onwards
+ *
+ * Requires line to have been tokenised -- cmd_tokenise().
+ *
+ * Returns: CMD_SUCCESS -- all is well
+ * CMD_ERR_PARSING -- parsing error -- malformed or misplaced pipe
+ * -- malformed quotes/escapes
+ */
+static cmd_return_code_t
+cmd_parse_phase_one(cmd_parsed parsed, cmd_parse_type_t type, node_type_t node)
+{
+ uint nt = parsed->num_tokens ;
- sp = str;
- while (*str != '\0')
+ /* Set command and parsing entries */
+ parsed->cnode = node ;
+ parsed->cmd = NULL ;
+
+ /* pick off any comment */
+ if ((parsed->tok_total & cmd_tok_comment) != 0)
{
- if (!isdigit ((int) *str))
- return no_match;
+ parsed->num_comment = 1 ;
+ parsed->first_comment = --nt ; /* implicitly the last */
+ parsed->tok_total ^= cmd_tok_comment ;
+ parsed->parts |= cmd_part_comment ;
+ } ;
- str++;
+ /* If this is not a simple line need to do some extra work:
+ *
+ * - identify and check any pipe items
+ * -
+ */
+ if (parsed->tok_total == cmd_tok_simple)
+ {
+ /* All tokens are simple and there is at least one */
+ parsed->first_command = 0 ;
+ parsed->num_command = nt ;
+ parsed->parts |= cmd_part_command ;
}
+ else if (parsed->tok_total != 0)
+ {
+ /* There is at least one not-simple cmd_token. */
+ cmd_return_code_t ret ;
+ ret = cmd_parse_phase_one_b(parsed, nt) ;
- if (atoi (sp) > 32)
- return no_match;
+ if (ret != CMD_SUCCESS)
+ return ret ;
+ } ;
- return exact_match;
-}
+ /* If have a have a 'do' at the front, account for it */
+ if ((parsed->parts & cmd_part_command) != 0)
+ {
+ /* Have a command -- worry about "do" if allowed */
+ if (((type & cmd_parse_no_do) == 0) && (node >= MIN_DO_SHORTCUT_NODE))
+ {
+ cmd_token t ;
+ t = cmd_token_get(parsed->tokens, parsed->first_command) ;
+ if (els_len_nn(t->ot) == 2)
+ {
+ const char* p = els_body_nn(t->ot) ;
+ if ((*p == 'd') && (*(p+1) == 'o'))
+ {
+ node = ENABLE_NODE ; /* change to this node */
+
+ parsed->num_do = 1 ;
+ parsed->first_do = parsed->first_command ;
+ --parsed->num_command ;
+ ++parsed->first_command ;
+ parsed->parts |= cmd_part_do ;
+ /* If have *only* the "do", we don't have a command */
+ if (parsed->num_command == 0)
+ parsed->parts ^= cmd_part_command ;
+ } ;
+ } ;
+ } ;
+ } ;
+
+ return CMD_SUCCESS ;
+} ;
/*------------------------------------------------------------------------------
- * Is this an IPv6 Address:
+ * Phase 1b of command parsing
+ *
+ * Tokeniser found at least one of:
+ *
+ * - in pipe token
+ * - out pipe token
+ * - token with quotes or escapes
*
- * TODO: cmd_ipv6_match() only returns "partly_match" for empty string ?
+ * Deal with all of those, verifying the syntax of any pipes and completing
+ * any tokens with quotes etc.
*
- * Returns: no_match -- improperly formed
- * partly_match -- accepts empty string
- * exact_match -- syntactically complete
+ * Update the "parts" and the relevant first/num values.
+ *
+ * Returns: CMD_SUCCESS -- all is well
+ * CMD_ERR_PARSING -- parsing error -- malformed or misplaced pipe
+ * -- malformed quotes/escapes
*/
+static cmd_return_code_t
+cmd_parse_phase_one_b(cmd_parsed parsed, uint nt)
+{
+ cmd_return_code_t ret ;
+ cmd_token t ;
+ cmd_parts_t parts ;
+ uint i, n ;
+ uint* pn ;
-#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
-#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
-#define STATE_START 1
-#define STATE_COLON 2
-#define STATE_DOUBLE 3
-#define STATE_ADDR 4
-#define STATE_DOT 5
-#define STATE_SLASH 6
-#define STATE_MASK 7
+ parts = cmd_parts_none ; /* no parts yet */
+ n = 0 ; /* no tokens in current part */
+ pn = NULL ; /* no current part */
-#ifdef HAVE_IPV6
+ for (i = 0 ; i < nt ; ++i)
+ {
+ t = cmd_token_get(parsed->tokens, i) ;
-extern match_type_t
-cmd_ipv6_match (const char *str)
-{
- int state = STATE_START;
- int colons = 0, nums = 0, double_colon = 0;
- const char *sp = NULL;
- struct sockaddr_in6 sin6_dummy;
- int ret;
+ if ((t->type & cmd_tok_simple) != 0)
+ {
+ if (parts == cmd_parts_none)
+ {
+ parts = cmd_part_command ;
+ parsed->first_command = i ;
+ pn = &parsed->num_command ;
+ n = 0 ;
+ } ;
+ } ;
- if (str == NULL)
- return partly_match;
+ if ((t->type & cmd_tok_in_pipe) != 0)
+ {
+ if (parts != cmd_parts_none)
+ return cmd_parse_error(parsed, t, 0, "unexpected 'pipe in'") ;
- if (strspn (str, IPV6_ADDR_STR) != strlen (str))
- return no_match;
+ ret = cmd_parse_in_pipe(parsed, t) ;
+ if (ret != CMD_SUCCESS)
+ return ret ;
- /* use inet_pton that has a better support,
- * for example inet_pton can support the automatic addresses:
- * ::1.2.3.4
- */
- ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
+ parts = cmd_part_in_pipe ;
+ parsed->first_in_pipe = i ;
+ pn = &parsed->num_in_pipe ;
+ n = 0 ;
+ } ;
+
+ if ((t->type & cmd_tok_out_pipe) != 0)
+ {
+ if ((parts == cmd_parts_none) || ((parts & cmd_part_out_pipe) != 0))
+ return cmd_parse_error(parsed, t, 0, "unexpected 'pipe out'") ;
+
+ ret = cmd_parse_out_pipe(parsed, t) ;
+ if (ret != CMD_SUCCESS)
+ return ret ;
+
+ *pn = n ; /* set number of in-pipe/cmd */
+
+ parts |= cmd_part_out_pipe ;
+ parsed->first_out_pipe = i ;
+ pn = &parsed->num_out_pipe ;
+ n = 0 ;
+ } ;
+
+ assert(parts != cmd_parts_none) ; /* dealt with all token types */
+
+ if ((t->type & cmd_tok_incomplete) != 0)
+ {
+ ret = cmd_token_complete(parsed, t) ;
+ if (ret != CMD_SUCCESS)
+ return ret ;
+ } ;
+
+ ++n ; /* count up tokens */
+ } ;
- if (ret == 1)
- return exact_match;
+ if (pn != NULL)
+ *pn = n ; /* number in last phase */
- while (*str != '\0')
+ /* If have an in-pipe or an out-pipe, worry about the number of
+ * arguments
+ */
+ if ((parts & cmd_parts_pipe) != 0)
{
- switch (state)
+ const char* msg = NULL ;
+ uint i ;
+ bool e ;
+
+ /* If there is an in_pipe part, check number of arguments */
+ if ((msg == NULL) && ((parts & cmd_part_in_pipe) != 0))
{
- case STATE_START:
- if (*str == ':')
+ assert(parsed->num_in_pipe > 0) ;
+
+ if (((parsed->in_pipe & cmd_pipe_file) != 0)
+ && (parsed->num_in_pipe != 2))
{
- if (*(str + 1) != ':' && *(str + 1) != '\0')
- return no_match;
- colons--;
- state = STATE_COLON;
- }
- else
+ if (parsed->num_in_pipe == 1)
+ {
+ i = parsed->first_in_pipe ;
+ e = true ;
+ msg = "requires file" ;
+ }
+ else
+ {
+ i = parsed->first_in_pipe + 2 ;
+ e = false ;
+ msg = "expects file only" ;
+ } ;
+ } ;
+
+ if (((parsed->in_pipe & cmd_pipe_shell) != 0)
+ && (parsed->num_in_pipe < 2))
{
- sp = str;
- state = STATE_ADDR;
- }
+ i = parsed->first_in_pipe ;
+ e = true ;
+ msg = "requires shell command" ;
+ } ;
+ } ;
- continue;
- case STATE_COLON:
- colons++;
- if (*(str + 1) == ':')
- state = STATE_DOUBLE;
- else
+ /* If there is an out_pipe part, check the number of arguments */
+ if ((msg == NULL) && ((parts & cmd_part_out_pipe) != 0))
+ {
+ assert(parsed->num_out_pipe > 0) ;
+
+ if (((parsed->out_pipe & cmd_pipe_file) != 0)
+ && (parsed->num_out_pipe != 2))
{
- sp = str + 1;
- state = STATE_ADDR;
- }
- break;
- case STATE_DOUBLE:
- if (double_colon)
- return no_match;
+ if (parsed->num_out_pipe == 1)
+ {
+ i = parsed->first_out_pipe ;
+ e = true ;
+ msg = "requires file" ;
+ }
+ else
+ {
+ i = parsed->first_out_pipe + 2 ;
+ e = false ;
+ msg = "expects file only" ;
+ } ;
+ } ;
- if (*(str + 1) == ':')
- return no_match;
- else
+ if (((parsed->out_pipe & cmd_pipe_shell) != 0)
+ && (parsed->num_out_pipe < 2))
{
- if (*(str + 1) != '\0')
- colons++;
- sp = str + 1;
- state = STATE_ADDR;
- }
+ assert(parsed->num_out_pipe > 0) ;
+
+ i = parsed->first_out_pipe ;
+ e = true ;
+ msg = "requires shell command" ;
+ } ;
+ } ;
+
+ if (msg != NULL)
+ {
+ t = cmd_token_get(parsed->tokens, i) ;
+ return cmd_parse_error(parsed, t, e ? els_len_nn(t->ot) : 0, msg) ;
+ } ;
+ } ;
+
+ /* It's OK -- so record the parts found and return success */
+ parsed->parts |= parts ;
+
+ return CMD_SUCCESS ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Phase 2 of command parsing -- parsing for execution.
+ *
+ * Assumes phase 1 completed successfully.
+ *
+ * Note that if parsed->num_command == 0, will have constructed a cmd_v, with
+ * all possible commands in it (depending on cmd_parse_execution).
+ *
+ * Returns: CMD_SUCCESS -- parsed successfully
+ * CMD_ERR_NO_MATCH -- could find nothing that matches
+ * CMD_ERR_AMBIGUOUS -- found more than one match
+ * or parsed->num_command == 0
+ */
+static cmd_return_code_t
+cmd_parse_phase_two(cmd_parsed parsed, cmd_parse_type_t type)
+{
+ uint ii ;
+ uint match ;
+
+ /* Prepare to filter commands */
+
+ cmd_filter_prepare(parsed, type) ;
+
+ match = 2 ; /* in case parsed->num_command == 0 ! */
+
+ for (ii = 0 ; ii < parsed->num_command ; ii++)
+ match = cmd_filter(parsed, ii, false) ;
+
+ /* Should end up with one command to execute. */
+ if (match == 0)
+ return CMD_ERR_NO_MATCH ;
+
+ if (match > 1)
+ return CMD_ERR_AMBIGUOUS ;
+
+ parsed->cmd = vector_get_item(parsed->cmd_v, 0) ;
+
+ return CMD_SUCCESS ;
+} ;
+
+#if 0
+/*------------------------------------------------------------------------------
+ * If src matches dst return dst string, otherwise return NULL
+ *
+ * Returns NULL if dst is an option, variable of vararg.
+ *
+ * NULL or empty src are deemed to match.
+ */
+static const char *
+cmd_entry_function (const char *src, const char *dst)
+{
+ if (CMD_OPTION (dst) || CMD_VARIABLE (dst) || CMD_VARARG (dst))
+ return NULL;
+
+ if ((src == NULL) || (*src == '\0'))
+ return dst;
+
+ if (strncmp (src, dst, strlen (src)) == 0)
+ return dst;
+
+ return NULL;
+}
+
+/* If src matches dst return dst string, otherwise return NULL */
+/* This version will return the dst string always if it is
+ CMD_VARIABLE for '?' key processing */
+static const char *
+cmd_entry_function_item (const char *src, const char *dst)
+{
+ if (CMD_VARARG (dst))
+ return dst;
+
+ if (CMD_RANGE (dst))
+ {
+ if (cmd_range_match (dst, src))
+ return dst;
+ else
+ return NULL;
+ }
+
+#ifdef HAVE_IPV6
+ if (CMD_IPV6 (dst))
+ {
+ if (cmd_ipv6_match (src))
+ return dst;
+ else
+ return NULL;
+ }
+
+ if (CMD_IPV6_PREFIX (dst))
+ {
+ if (cmd_ipv6_prefix_match (src))
+ return dst;
+ else
+ return NULL;
+ }
+#endif /* HAVE_IPV6 */
+
+ if (CMD_IPV4 (dst))
+ {
+ if (cmd_ipv4_match (src))
+ return dst;
+ else
+ return NULL;
+ }
+
+ if (CMD_IPV4_PREFIX (dst))
+ {
+ if (cmd_ipv4_prefix_match (src))
+ return dst;
+ else
+ return NULL;
+ }
+
+ /* Optional or variable commands always match on '?' */
+ if (CMD_OPTION (dst) || CMD_VARIABLE (dst))
+ return dst;
+
+ /* In case of 'command \t', given src is NULL string. */
+ if (src == NULL)
+ return dst;
+
+ if (strncmp (src, dst, strlen (src)) == 0)
+ return dst;
+ else
+ return NULL;
+}
+
+/*------------------------------------------------------------------------------
+ * Check same string element existence.
+ *
+ * Returns: 0 => found same string in the vector
+ * 1 => NOT found same string in the vector
+ */
+static bool
+cmd_unique_string (vector v, const char *str)
+{
+ unsigned int i;
+ char *match;
+
+ for (i = 0; i < vector_length (v); i++)
+ if ((match = vector_get_item (v, i)) != NULL)
+ if (strcmp (match, str) == 0)
+ return 0;
+ return 1;
+}
+
+/* Compare string to description vector. If there is same string
+ return 1 else return 0. */
+static bool
+item_unique_string (vector v, const char *str)
+{
+ unsigned int i;
+ struct cmd_item *item;
+
+ for (i = 0; i < vector_length (v); i++)
+ if ((item = vector_get_item (v, i)) != NULL)
+ if (strcmp (item->cmd, str) == 0)
+ return 1;
+ return 0;
+}
+#endif
+/*==============================================================================
+ * '?' describe command support.
+ */
+
+/*------------------------------------------------------------------------------
+ * Get description of current (partial) command
+ *
+ * Returns: NULL => no description available
+ *
+ * status set to CMD_ERR_NO_MATCH or CMD_ERR_AMBIGUOUS
+ *
+ * or: address of vector of "struct cmd_item" values available.
+ *
+ * NB: when a vector is returned it is the caller's responsibility to
+ * vector_free() it. (The contents are all effectively const, so do not
+ * themselves need to be freed.)
+ */
+extern vector
+cmd_describe_command (const char* line, node_type_t node,
+ cmd_return_code_t* status)
+{
+#if 0
+ vector ret ;
+ struct cmd_parsed parsed_s ;
+ cmd_parsed parsed ;
+ cmd_token_type_t tok_total ;
+
+ /* Set up a parser object and tokenise the command line */
+ parsed = cmd_parse_init_new(&parsed_s) ;
+ tok_total = cmd_tokenise(parsed, line, node) ;
+
- double_colon++;
- nums++;
- break;
- case STATE_ADDR:
- if (*(str + 1) == ':' || *(str + 1) == '\0')
+
+
+
+
+
+ /* Level 1 parsing
+ *
+ * Strip quotes and escapes from all the tokens.
+ */
+ if (tok_total != cmd_tok_simple)
+ {
+ ret = cmd_parse_phase_one(parsed) ;
+ if (ret != CMD_SUCCESS)
+ return ret ;
+ } ;
+
+ /* If allowed to 'do', see if there.
+ *
+ * 'do' forces command to be parsed in ENABLE_NODE (if allowed)
+ */
+ if ((type & cmd_parse_no_do) == 0)
+ cmd_try_do_shortcut(parsed) ;
+
+
+
+
+ return cmd_describe_command_real (tokens, node, status);
+
+
+
+ static vector
+ cmd_describe_command_real (vector tokens, int node, int *status)
+ {
+ unsigned int i;
+ vector cmd_vector;
+ #define INIT_MATCHVEC_SIZE 10
+ vector matchvec;
+ struct cmd_command *cmd_command;
+ unsigned int index;
+ int ret;
+ enum match_type match;
+ char *command;
+
+ /* Set index. */
+ if (vector_length (tokens) == 0)
+ {
+ *status = CMD_ERR_NO_MATCH;
+ return NULL;
+ }
+ else
+ index = vector_length (tokens) - 1;
+
+ /* Make copy vector of current node's command vector. */
+ cmd_vector = vector_copy (cmd_node_vector (cmdvec, node));
+
+ /* Prepare match vector */
+ matchvec = vector_init (INIT_MATCHVEC_SIZE);
+
+ /* Filter commands. */
+ /* Only words precedes current word will be checked in this loop. */
+ for (i = 0; i < index; i++)
+ if ((command = vector_get_item (tokens, i)))
+ {
+ match = cmd_filter(command, cmd_vector, i, any_match) ;
+
+ if (match == vararg_match)
{
- if (str - sp > 3)
- return no_match;
+ struct cmd_command *cmd_command;
+ vector descvec;
+ unsigned int j, k;
+
+ for (j = 0; j < vector_length (cmd_vector); j++)
+ if ((cmd_command = vector_get_item (cmd_vector, j)) != NULL
+ && (vector_length (cmd_command->items)))
+ {
+ descvec = vector_get_item (cmd_command->items,
+ vector_length (cmd_command->items) - 1);
+ for (k = 0; k < vector_length (descvec); k++)
+ {
+ struct cmd_item *item = vector_get_item (descvec, k);
+ vector_set (matchvec, item);
+ }
+ }
+
+ vector_set (matchvec, &item_cr);
+ vector_free (cmd_vector);
+
+ return matchvec;
+ } ;
- nums++;
- state = STATE_COLON;
- }
- if (*(str + 1) == '.')
- state = STATE_DOT;
- break;
- case STATE_DOT:
- state = STATE_ADDR;
- break;
- default:
- break;
+ ret = is_cmd_ambiguous (command, cmd_vector, i, match) ;
+ if (ret != 0)
+ {
+ vector_free (cmd_vector);
+ vector_free (matchvec);
+ *status = (ret == 1) ? CMD_ERR_AMBIGUOUS
+ : CMD_ERR_NO_MATCH ;
+ return NULL ;
+ } ;
}
- if (nums > 8)
- return no_match;
+ /* Prepare match vector */
+ /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */
- if (colons > 7)
- return no_match;
+ /* Make sure that cmd_vector is filtered based on current word */
+ command = vector_get_item (tokens, index);
+ if (command)
+ match = cmd_filter(command, cmd_vector, index, any_match);
- str++;
- }
+ /* Make description vector. */
+ for (i = 0; i < vector_length (cmd_vector); i++)
+ {
+ vector items ;
+
+ cmd_command = vector_get_item (cmd_vector, i) ;
+ if (cmd_command == NULL)
+ continue ;
+
+ /* Ignore cmd_command if no tokens at index position.
+ *
+ * Deal with special case of possible <cr> completion.
+ */
+ items = cmd_command->items;
+ if (index >= vector_length (items))
+ {
+ if (command == NULL && index == vector_length (items))
+ {
+ if (!item_unique_string (matchvec, cr_string))
+ vector_push_item(matchvec, &item_cr);
+ }
+ continue ;
+ } ;
+
+ /* Check if command is completed. */
+ unsigned int j;
+ vector descvec = vector_get_item (items, index);
+ struct cmd_item *item;
+
+ for (j = 0; j < vector_length (descvec); j++)
+ if ((item = vector_get_item (descvec, j)))
+ {
+ const char *string;
+
+ string = cmd_entry_function_desc (command, item->cmd);
+ if (string)
+ {
+ /* Uniqueness check */
+ if (!item_unique_string (matchvec, string))
+ vector_push_item(matchvec, item);
+ }
+ } ;
+ } ;
+
+ vector_free (cmd_vector);
+
+ if (vector_length(matchvec) == 0)
+ {
+ vector_free (matchvec);
+ *status = CMD_ERR_NO_MATCH;
+ return NULL;
+ }
+
+ *status = CMD_SUCCESS;
+ return matchvec;
+ }
+
+ cmd_parse_reset(parsed, false) ;
+
+ return
+#endif
+
+ return NULL ;
+} ;
#if 0
- if (nums < 11)
- return partly_match;
-#endif /* 0 */
+/*------------------------------------------------------------------------------
+ * Check LCD of matched command.
+ *
+ * Scan list of matched keywords, and by comparing them pair-wise, find the
+ * longest common leading substring.
+ *
+ * Returns: 0 if zero or one matched keywords
+ * length of longest common leading substring, otherwise.
+ */
+static int
+cmd_lcd (vector matchvec)
+{
+ int n ;
+ int i ;
+ int lcd ;
+ char *sp, *sq, *ss ;
- return exact_match;
+ n = vector_end(matchvec) ;
+ if (n < 2)
+ return 0 ;
+
+ ss = vector_get_item(matchvec, 0) ;
+ lcd = strlen(ss) ;
+
+ for (i = 1 ; i < n ; i++)
+ {
+ sq = ss ;
+ ss = vector_get_item(matchvec, i) ;
+ sp = ss ;
+
+ while ((*sp == *sq) && (*sp != '\0'))
+ {
+ ++sp ;
+ ++sq ;
+ } ;
+
+ if (lcd > (sp - ss))
+ lcd = (sp - ss) ;
+ }
+ return lcd;
}
+#endif
+
+/*==============================================================================
+ * Command line help support.
+ *
+ *
+ */
+
+static bool cmd_post_last(cmd_parsed parsed) ;
+
+
/*------------------------------------------------------------------------------
- * Is this an IPv6 Prefix:
+ * Look out for the following special cases:
*
- * TODO: cmd_ipv6_prefix_match() hardly returns "partly_match" ?
- * TODO: cmd_ipv6_prefix_match() possibly accepts invalid address before '/' ?
+ * a) before or on '#' or '!'
*
- * Returns: no_match -- improperly formed
- * partly_match -- accepts empty string
- * exact_match -- syntactically complete
+ * '#' and '!' are only recognised as the start of a comment at the
+ * start of a line... So can insert something in front, and what
+ * currently looks like a comment, suddenly needs to be reconsidered
+ * as tokens... the first of which happens to start with '!' or '#'.
*
- * NB: partly_match is returned for anything valid before the '/', but which
- * has no '/' or no number after the '/'.
+ * To save work, and leave open the possibility of trailing comments,
+ * we treat this case as an "error".
+ *
+ * b) before or on '<'
+ *
+ * currently, pipe-in must be the first token on the command line.
+ *
+ * So if there is anything prior to the '<', treat that as an error.
+ * If is just spaces before the '<', say that nothing may be placed
+ * before the '<' (cf nothing may be placed before '!' or '#').
+ *
+ * in these cases no help is available -- return message explaining.
+ *
+ * NB: assumes that have already done cmd_token_position(), and therefore that
+ * if there is a '#' or '<' that cannot be positioned *after* it, or
+ * indeed that can be positioned after a '>'.
*/
-extern match_type_t
-cmd_ipv6_prefix_match (const char *str)
+extern const char*
+cmd_help_preflight(cmd_parsed parsed)
{
- int state = STATE_START;
- int colons = 0, nums = 0, double_colon = 0;
- int mask;
- const char *sp = NULL;
- char *endptr = NULL;
+ if ((parsed->tok_total & cmd_tok_comment) != 0)
+ {
+ return "cannot have a command on a comment line";
+ } ;
+
+ if ((parsed->tok_total & cmd_tok_in_pipe) != 0)
+ {
+ return "cannot have a command and an '<' pipe together" ;
+ } ;
- if (str == NULL)
- return partly_match;
+ return NULL ; /* OK ! */
+} ;
- if (strspn (str, IPV6_PREFIX_STR) != strlen (str))
- return no_match;
- while (*str != '\0' && state != STATE_MASK)
+
+
+/*------------------------------------------------------------------------------
+ * See if current command line can be completed, and if so, how.
+ *
+ * NB: must already have done cmd_token_position().
+ *
+ * Must not be called if cmd_token_position() reported a "special".
+ *
+ * Returns: CMD_ERR_PARSING -- the parser could not make sense of the line.
+ *
+ */
+extern cmd_return_code_t
+cmd_completion(cmd_parsed parsed, node_type_t node)
+{
+ cmd_return_code_t ret ;
+
+ /* Parse the line -- allow completion, allow do, allow backing up the tree,
+ * but do not parse for execution.
+ */
+ ret = cmd_parse_command(parsed, node, cmd_parse_standard) ;
+
+ if (ret == CMD_ERR_PARSING)
+ return ret ; /* nothing more possible */
+
+ /* Expect now to have a cmd_v set with the result of the filtering,
+ * with 0, 1 or more possible commands in it.
+ *
+ * Now establish what the alternatives are at the current token position.
+ *
+ * We do this by filtering again, on the current token position, and
+ * asking the filter to collect all the possible items. Note that if the
+ * current token position is beyond the last actual command token, then
+ * will be filtering on the empty eol token -- which we arrange to match
+ * anything, including eol -- which gives the dummy item_cr of type
+ * item_eol.
+ */
+ assert(parsed->cti >= parsed->first_command) ;
+ assert(parsed->cti <= parsed->first_command + parsed->num_command) ;
+
+ cmd_filter(parsed, parsed->cti - parsed->first_command, true) ;
+
+ /* Reduce the list of items available at the current position to
+ * eliminate duplicates and have the possibilities sorted by type and
+ * by value.
+ */
+ if (vector_length(parsed->item_v) > 1)
+ {
+ cmd_item prev ;
+ uint ii ;
+ uint i_keep ;
+
+ vector_sort(parsed->item_v, (vector_sort_cmp*)cmd_cmp_item) ;
+
+ i_keep = 1 ;
+
+ prev = vector_get_item(parsed->item_v, 0) ;
+ for (ii = 1 ; ii < vector_length(parsed->item_v) ; ++ii)
+ {
+ cmd_item item ;
+
+ item = vector_get_item(parsed->item_v, ii) ;
+ if (cmd_cmp_item(&item, &prev) != 0)
+ {
+ vector_set_item(parsed->item_v, i_keep++, item) ;
+ prev = item ;
+ } ;
+ } ;
+
+ vector_set_length(parsed->item_v, i_keep) ;
+ /* discard what did not keep */
+ } ;
+
+ return CMD_SUCCESS ;
+} ;
+
+
+
+
+/*------------------------------------------------------------------------------
+ * How to insert a newly completed keyword ?
+ *
+ * There are a number of cases:
+ *
+ * 1) the cti is for the token to be completed.
+ *
+ * May be positioned one or more spaces in front of the keyword.
+ *
+ * Will replace any leading spaces and the keyword by the new keyword.
+ *
+ * 2) the cti is for the empty eol token
+ *
+ * Will replace any leading spaces to the eol keyword, by the new
+ * keyword.
+ *
+ * 3) the cti is for a '>' token, or any other !cmd_tok_simple.
+ *
+ * If cti is not exactly on the token, then insert the keyword exactly
+ * where it is -- no spaces need to be removed or added.
+ *
+ * If the cti is on the token, insert the keyword and one space.
+ *
+ * The second issue is where to leave the cursor... if the command is now
+ * complete, wish to leave the cursor at the end of the newly completed
+ * keyword. Otherwise:
+ *
+ * 1) if the next token is a cmd_tok_simple, move to it.
+ *
+ * 2) otherwise if there is already a space after the newly completed
+ * keyword, move past it.
+ *
+ * Otherwise, insert a space.
+ *
+ * Returns: n = number of characters to move the cursor before
+ * starting to replace characters (may be -ve)
+ * *rep = number of characters to replace
+ * *ins = number of spaces to insert
+ * *mov = number of spaces to move afterwards (may be -ve)
+ */
+extern void
+cmd_complete_keyword(cmd_parsed parsed, int* pre, int* rep, int* ins, int* mov)
+{
+ cmd_token t, nt ;
+ bool last ;
+ int gap ;
+
+ /* Is this the last token possible on this command line ? */
+ last = cmd_post_last(parsed) ;
+
+ /* Get the token we are operating and the following one (if any).
+ *
+ * Calculate gap between end of token to be replaced and start of next,
+ * if any.
+ *
+ * gap == 0 => is at eol or on !cmd_tok_simple.
+ */
+ t = cmd_token_get(parsed->tokens, parsed->cti) ;
+ if (parsed->cti < parsed->num_tokens)
+ {
+ nt = cmd_token_get(parsed->tokens, parsed->cti + 1) ;
+ gap = nt->tp - (t->tp + els_len_nn(t->ot)) ;
+ }
+ else
+ {
+ nt = NULL ;
+ gap = (parsed->rp < 0) ? -parsed->rp : 0 ;
+ } ;
+
+ /* Now work out what we need to do. */
+
+ *pre = 0 ; /* preset values */
+ *rep = 0 ;
+ *ins = 0 ;
+ *mov = 0 ;
+
+ if ((t->type & cmd_tok_simple) != 0)
{
- switch (state)
+ /* Replacing an existing simple token ------------------------------
+ *
+ * Move to start of token and replace it.
+ */
+ *pre = -parsed->rp ;
+ *rep = parsed->ctl ;
+
+ /* now what do we do after the token ? */
+ assert(nt != NULL) ;
+
+ if ((nt->type & cmd_tok_simple) != 0)
{
- case STATE_START:
- if (*str == ':')
+ /* Next token is simple -- step to it */
+ assert(!last) ;
+
+ *mov = gap ;
+ }
+ else if (nt->type == cmd_tok_eol)
+ {
+ /* Next token is eol
+ *
+ * gap is the number of spaces there will be after the
+ * newly replaced token.
+ */
+ if (!last)
{
- if (*(str + 1) != ':' && *(str + 1) != '\0')
- return no_match;
- colons--;
- state = STATE_COLON;
+ if (gap == 0)
+ *ins = 1 ; /* need a trailing space */
+ else
+ *mov = 1 ; /* step over existing space */
+ } ;
+ }
+ else
+ {
+ /* Next token is something special.
+ *
+ * gap is the number of spaces there will be after the
+ * newly replaced token.
+ */
+ if (gap == 0)
+ {
+ *ins = last ? 1 : 2 ;
+ *mov = -1 ;
}
- else
+ else if (gap == 1)
{
- sp = str;
- state = STATE_ADDR;
+ *ins = last ? 0 : 1 ;
}
-
- continue;
- case STATE_COLON:
- colons++;
- if (*(str + 1) == '/')
- return no_match;
- else if (*(str + 1) == ':')
- state = STATE_DOUBLE;
else
{
- sp = str + 1;
- state = STATE_ADDR;
+ *mov = last ? 0 : 1 ;
}
- break;
- case STATE_DOUBLE:
- if (double_colon)
- return no_match;
+ } ;
+ }
+
+ else if (t->type == cmd_tok_eol)
+ {
+ /* Inserting at or before eol --------------------------------------
+ *
+ * Insert exactly where we are -- *pre == 0
+ */
+
+ /* If last, replace any spaces between cursor and end of line,
+ * otherwise, keep one of them.
+ */
+ *rep = last ? gap : (gap > 0) ? gap - 1 : gap ;
- if (*(str + 1) == ':')
- return no_match;
+ /* now what do we do after the token ? */
+ assert(nt == NULL) ;
+
+ if (!last)
+ {
+ if (gap == 0)
+ *ins = 1 ; /* need space after */
else
- {
- if (*(str + 1) != '\0' && *(str + 1) != '/')
- colons++;
- sp = str + 1;
+ *mov = 1 ; /* step over one */
+ } ;
+ }
+ else
+ {
+ /* Inserting at or before something special ------------------------
+ *
+ * Insert exactly where we are -- *pre == 0
+ * replace nothing -- *rep == 0
+ */
+ if (gap == 0)
+ {
+ *ins = last ? 1 : 2 ;
+ *mov = -1 ;
+ }
+ else if (gap == 1)
+ {
+ *ins = last ? 0 : 1 ;
+ }
+ else
+ {
+ *mov = last ? 0 : 1 ;
+ }
+ } ;
+} ;
- if (*(str + 1) == '/')
- state = STATE_SLASH;
- else
- state = STATE_ADDR;
- }
+/*------------------------------------------------------------------------------
+ * Do we have just one command left, and have we just filtered on the last
+ * possible command item & token ?
+ *
+ * If so, then there is no point filtering any further, and there is nothing
+ * more that could be added to this command line.
+ */
+static bool
+cmd_post_last(cmd_parsed parsed)
+{
+ if (vector_length(parsed->cmd_v) == 1)
+ {
+ cmd_command cmd ;
+ cmd = vector_get_item(parsed->cmd_v, 0) ;
- double_colon++;
- nums += 1;
- break;
- case STATE_ADDR:
- if (*(str + 1) == ':' || *(str + 1) == '.'
- || *(str + 1) == '\0' || *(str + 1) == '/')
- {
- if (str - sp > 3)
- return no_match;
+ return ((parsed->cti - parsed->first_command + 1) == cmd->nt_max) ;
+ } ;
+
+ return false ;
+} ;
- for (; sp <= str; sp++)
- if (*sp == '/')
- return no_match;
- nums++;
- if (*(str + 1) == ':')
- state = STATE_COLON;
- else if (*(str + 1) == '.')
- state = STATE_DOT;
- else if (*(str + 1) == '/')
- state = STATE_SLASH;
- }
- break;
- case STATE_DOT:
- state = STATE_ADDR;
- break;
- case STATE_SLASH:
- if (*(str + 1) == '\0')
- return partly_match;
-
- state = STATE_MASK;
- break;
- default:
- break;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#if 0
+static vector
+cmd_complete_command_real (vector tokens, int node, int *status)
+{
+ unsigned int i;
+ unsigned int ivl ;
+ unsigned int last_ivl ;
+ vector cmd_v ;
+#define INIT_MATCHVEC_SIZE 10
+ vector matchvec;
+ struct cmd_command *cmd_command;
+ unsigned int index;
+ struct cmd_item *cmd_item;
+ vector descvec;
+ char *token;
+ int n ;
+
+ /* Stop immediately if the tokens is empty. */
+ if (vector_length (tokens) == 0)
+ {
+ *status = CMD_ERR_NO_MATCH;
+ return NULL;
+ }
+
+ /* Take (shallow) copy of cmdvec for given node. */
+ cmd_v = vector_copy (cmd_node_vector (cmdvec, node));
+
+ /* First, filter upto, but excluding last token */
+ last_ivl = vector_length (tokens) - 1;
+
+ for (ivl = 0; ivl < last_ivl; ivl++)
+ {
+ enum match_type match;
+ int ret;
+
+ /* TODO: does this test make any sense ? */
+ if ((token = vector_get_item (tokens, ivl)) == NULL)
+ continue ;
+
+ /* First try completion match, return best kind of match */
+ index = ivl ;
+ match = cmd_filter_by_completion (token, cmd_v, index) ;
+
+ /* Eliminate all but the selected kind of match */
+ ret = is_cmd_ambiguous (token, cmd_v, index, match) ;
+
+ if (ret == 1)
+ {
+ /* ret == 1 => either token matches more than one keyword
+ * or token matches more than one number range
+ */
+ vector_free (cmd_v);
+ *status = CMD_ERR_AMBIGUOUS;
+ return NULL;
}
+#if 0
+ /* For command completion purposes do not appear to care about
+ * incomplete ipv4 or ipv6 prefixes (missing '/' or digits after).
+ */
+ else if (ret == 2)
+ {
+ vector_free (cmd_v);
+ *status = CMD_ERR_NO_MATCH;
+ return NULL;
+ }
+#endif
+ }
+
+ /* Prepare match vector. */
+ matchvec = vector_init (INIT_MATCHVEC_SIZE);
+
+ /* Now we got into completion */
+ index = last_ivl ;
+ token = vector_get_item(tokens, last_ivl) ; /* is now the last token */
+
+ for (i = 0; i < vector_length (cmd_v); i++)
+ {
+ unsigned int j;
+ const char *string;
- if (nums > 11)
- return no_match;
+ if ((cmd_command = vector_get_item (cmd_v, i)) == NULL)
+ continue ;
- if (colons > 7)
- return no_match;
+ descvec = vector_get_item (cmd_command->items, index);
+ if (descvec == NULL)
+ continue ;
- str++;
+ for (j = 0; j < vector_length (descvec); j++)
+ {
+ item = vector_get_item (descvec, j) ;
+ if (item == NULL)
+ continue ;
+
+ string = cmd_entry_function(token, item->str) ;
+ if ((string != NULL) && cmd_unique_string(matchvec, string))
+ cmd_add_to_strvec (matchvec, string) ;
+ } ;
+ } ;
+
+ n = vector_length(matchvec) ; /* number of entries in the matchvec */
+
+ /* We don't need cmd_v any more. */
+ vector_free (cmd_v);
+
+ /* No matched command */
+ if (n == 0)
+ {
+ vector_free (matchvec);
+
+ /* In case of 'command \t' pattern. Do you need '?' command at
+ the end of the line. */
+ if (*token == '\0')
+ *status = CMD_COMPLETE_ALREADY;
+ else
+ *status = CMD_ERR_NO_MATCH;
+ return NULL;
}
- if (state < STATE_MASK)
- return partly_match;
+ /* Only one matched */
+ if (n == 1)
+ {
+ *status = CMD_COMPLETE_FULL_MATCH;
+ return matchvec ;
+ }
- mask = strtol (str, &endptr, 10);
- if (*endptr != '\0')
- return no_match;
+ /* Check LCD of matched strings. */
+ if (token != NULL)
+ {
+ unsigned lcd = cmd_lcd (matchvec) ;
+
+ if (lcd != 0)
+ {
+ if (strlen(token) < lcd)
+ {
+ char *lcdstr;
- if (mask < 0 || mask > 128)
- return no_match;
+ lcdstr = XMALLOC (MTYPE_STRVEC, lcd + 1);
+ memcpy (lcdstr, vector_get_item(matchvec, 0), lcd) ;
+ lcdstr[lcd] = '\0';
-/* I don't know why mask < 13 makes command match partly.
- Forgive me to make this comments. I Want to set static default route
- because of lack of function to originate default in ospf6d; sorry
- yasu
- if (mask < 13)
- return partly_match;
-*/
+ cmd_free_strvec(matchvec) ; /* discard the match vector */
- return exact_match;
-}
+ matchvec = vector_init (1);
+ vector_push_item(matchvec, lcdstr) ;
-#endif /* HAVE_IPV6 */
+ *status = CMD_COMPLETE_MATCH;
+ return matchvec ;
+ }
+ }
+ }
+
+ *status = CMD_COMPLETE_LIST_MATCH;
+ return matchvec ;
+}
+#endif
/*------------------------------------------------------------------------------
- * Is this a decimal number in the allowed range:
- *
- * Returns: true <=> OK -- *including* empty string
- * false => not a valid number, or not in required range
- * (or invalid range !!)
+ * Can the current command be completed ?
*/
+extern vector
+cmd_complete_command (vector tokens, int node, int *status)
+{
+#if 0
+ vector ret;
-#define DECIMAL_STRLEN_MAX 10
+ if ( cmd_try_do_shortcut(node, vector_get_item(tokens, 0) ) )
+ {
+ vector shifted_tokens;
+ unsigned int index;
-extern bool
-cmd_range_match (const char *range, const char *str)
-{
- char *p;
- char buf[DECIMAL_STRLEN_MAX + 1];
- char *endptr = NULL;
- unsigned long min, max, val;
+ /* We can try it on enable node, cos' the vty is authenticated */
- if (str == NULL)
- return true ;
+ shifted_tokens = vector_init (vector_count(tokens));
+ /* use memcpy? */
+ for (index = 1; index < vector_length (tokens); index++)
+ {
+ vector_set_index (shifted_tokens, index-1,
+ vector_lookup(tokens, index)) ;
+ }
- val = strtoul (str, &endptr, 10);
- if (*endptr != '\0')
- return false ;
+ ret = cmd_complete_command_real (shifted_tokens, ENABLE_NODE, status);
+
+ vector_free(shifted_tokens);
+ return ret;
+ }
+
+ return cmd_complete_command_real (tokens, node, status);
+#endif
+
+ *status = CMD_ERR_NO_MATCH;
+ return NULL;
+} ;
- range++;
- p = strchr (range, '-');
- if (p == NULL)
- return false ;
- if (p - range > DECIMAL_STRLEN_MAX)
- return false ;
- strncpy (buf, range, p - range);
- buf[p - range] = '\0';
- min = strtoul (buf, &endptr, 10);
- if (*endptr != '\0')
- return false ;
- range = p + 1;
- p = strchr (range, '>');
- if (p == NULL)
- return false ;
- if (p - range > DECIMAL_STRLEN_MAX)
- return false ;
- strncpy (buf, range, p - range);
- buf[p - range] = '\0';
- max = strtoul (buf, &endptr, 10);
- if (*endptr != '\0')
- return false ;
- if (val < min || val > max)
- return false ;
- return true ;
-}
diff --git a/lib/command_parse.h b/lib/command_parse.h
index ab91e7f4..789fe601 100644
--- a/lib/command_parse.h
+++ b/lib/command_parse.h
@@ -24,13 +24,15 @@
#ifndef _ZEBRA_COMMAND_PARSE_H
#define _ZEBRA_COMMAND_PARSE_H
-#include <zebra.h>
+#include "zconfig.h" /* HAVE_IPV6 */
#include "misc.h"
-#include "node_type.h"
+#include "command_common.h"
#include "vector.h"
#include "qstring.h"
+#include "elstring.h"
+#if 0
/*==============================================================================
* Parsing of tokens
*/
@@ -51,42 +53,397 @@ enum cmd_token_spec
cmd_ts_ipv6_prefix,
cmd_ts_vararg,
+} ;
+typedef enum cmd_token_spec cmd_token_spec_t ;
+#endif
+
+/*==============================================================================
+ * Sexing of token types in command descriptions
+ */
+#define CMD_OPTION(S) ((S[0]) == '[')
+#define CMD_VARIABLE(S) (((S[0]) >= 'A' && (S[0]) <= 'Z') || ((S[0]) == '<'))
+#define CMD_VARARG(S) ((S[0]) == '.')
+#define CMD_RANGE(S) ((S[0] == '<'))
+
+#define CMD_IPV4(S) ((strcmp ((S), "A.B.C.D") == 0))
+#define CMD_IPV4_PREFIX(S) ((strcmp ((S), "A.B.C.D/M") == 0))
+#define CMD_IPV6(S) ((strcmp ((S), "X:X::X:X") == 0))
+#define CMD_IPV6_PREFIX(S) ((strcmp ((S), "X:X::X:X/M") == 0))
+
+/*==============================================================================
+ * Command Items.
+ *
+ * A command is compiled into a vector of lists of command items -- the
+ * cmd_command->items (so called).
+ *
+ */
+
+/* Command item types
+ *
+ * NB: the command items sort according to this -- highest first.
+ *
+ * NB: this is in the same order as the match_type -- and should remain so.
+ * See match_type below for further discussion of the hierarchy.
+ */
+enum cmd_item_type
+{
+ item_null,
+
+ item_eol,
+ item_option_word,
+ item_vararg, /* rest of the line */
+ item_word,
+ item_ipv6_prefix,
+ item_ipv6_address,
+ item_ipv4_prefix,
+ item_ipv4_address,
+
+ item_range,
+
+ item_keyword,
+
+ item_type_count, /* number of types */
+} ;
+typedef enum cmd_item_type cmd_item_type_t ;
+
+enum {
+ item_max_number = 0xFFFFFFFF /* can be +/- this */
+} ;
+
+CONFIRM(LONG_MAX >= item_max_number) ;
+
+/*------------------------------------------------------------------------------
+ * Sex cmd_item_type and return whether it is an "option" type, or not.
+ *
+ * NB: tolerates item_null -- others may well not.
+ */
+Inline bool
+cmd_item_is_option(cmd_item_type_t itt)
+{
+ const static bool is_option[item_type_count] =
+ {
+ [item_null] = false,
+
+ [item_eol] = false,
+
+ [item_option_word] = true,
+
+ [item_vararg] = false,
+ [item_word] = false,
+
+ [item_ipv6_prefix] = false,
+ [item_ipv6_address] = false,
+ [item_ipv4_prefix] = false,
+ [item_ipv4_address] = false,
+
+ [item_range] = false,
+
+ [item_keyword] = false,
+ } ;
+
+ assert((itt >= 0) && (itt < item_type_count)) ;
+
+ return is_option[itt] ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Sex cmd_item_type and return whether it is an "vararg" type, or not.
+ *
+ * NB: tolerates item_null -- others may well not.
+ */
+Inline bool
+cmd_item_is_vararg(cmd_item_type_t itt)
+{
+ const static bool is_vararg[item_type_count] =
+ {
+ [item_null] = false,
+
+ [item_eol] = false,
+
+ [item_option_word] = false,
+
+ [item_vararg] = true,
+ [item_word] = false,
+
+ [item_ipv6_prefix] = false,
+ [item_ipv6_address] = false,
+ [item_ipv4_prefix] = false,
+ [item_ipv4_address] = false,
+
+ [item_range] = false,
+
+ [item_keyword] = false,
+ } ;
+
+ assert((itt >= 0) && (itt < item_type_count)) ;
+
+ return is_vararg[itt] ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * The command item structure.
+ */
+typedef struct cmd_item* cmd_item ;
+struct cmd_item
+{
+ const char* str ; /* in r_string -- original string form */
+ const char* doc ; /* in r_doc -- description text */
+
+ cmd_item next ; /* Next possibility (if any) */
+
+ cmd_item_type_t type ;
+ bool arg ; /* include in argv */
+
+ /* For item_range values */
+ bool range_sign_allowed ;
+ bool range_sign_required ;
+ long range_min ;
+ long range_max ;
} ;
-typedef enum cmd_token_spec cmd_token_spec_t ;
/*==============================================================================
- * Completion match types.
+ * Match strengths types and filter settings.
+ *
+ * When matching a token, may have a number of competing items, possibly of
+ * different types.
+ *
+ * For execution a token must match completely -- or, for keyword items,
+ * match partially at most one possible keyword.
+ */
+
+/* Match strength
+ *
+ * When matching a token against an item, the following are the possible
+ * results.
+ */
+enum match_strength
+{
+ ms_no_match = 0, /* match failed: token is definitely NOT
+ * the item in question.
+ */
+
+ ms_min_parse = ms_no_match + 1,
+ /* for parsing must match somehow ! */
+
+ ms_partial, /* match OK up to the end of the token, but
+ * more is required to complete it.
+ * This is used by the variable matches.
+ */
+
+ ms_min_execute = ms_partial + 1,
+ /* for execution must be at least this */
+
+ ms_anything, /* matches because will match anything.
+ * This is used for WORD and such like items.
+ */
+
+ ms_kwd_incomplete, /* keyword match OK, but not complete.
+ */
+
+ ms_var_complete, /* match succeeded: token is a complete and
+ * valid instance of the item in question
+ */
+
+ ms_kwd_complete /* match succeeded: token is a complete and
+ * valid instance of the item in question
+ */
+} ;
+typedef enum match_strength match_strength_t ;
+
+
+/* The match type indicates what has been matched and the strength of the
+ * match.
+ *
+ * NB: the order of these is significant, higher numbered match types are
+ * preferred over lower numbered ones.
+ *
+ * NB: this is in the same order as the cmd_item_type -- and should remain so.
+ *
+ * During the command filtering, when a token and an item match, and the
+ * match type is better than the match type to date, then all previous matches
+ * are discarded.
+ *
+ * The hierarchy means that, for example, '22' will match an IPv4 prefix
+ * partially, but that will be forgotten if it later matches a WORD, and that
+ * will be forgotten if it later matches a number range.
+ *
+ * The IPv6 items have a lower priority so that a simple decimal will prefer
+ * the simpler IPv4 address form. As soon as there is an 'a'..'f' or a ':',
+ * the IPv4 will no longer match.
+ *
+ * The partial keyword match is preferred over a partial anything else, but
+ * cannot mask a complete value !
+ *
+ * The relative ranking of: mt_option_word_match, mt_vararg_match, and
+ * mt_word_match, is more or less arbitrary -- if these are ever candidates
+ * at the same time, the commands are ambiguous.
*
- * NB: the order of these is significant -- in particular as confirmed below.
*/
enum match_type
{
- no_match = 0, /* nope */
- any_match = 1,
+ mt_no_match,
- extend_match = any_match,
+ mt_eol_partial, /* a partial match ! */
- ipv4_prefix_match,
- ipv4_match,
- ipv6_prefix_match,
- ipv6_match,
- range_match,
- vararg_match,
+ mt_ipv6_address_partial,
+ mt_ipv6_prefix_partial,
+ mt_ipv4_address_partial,
+ mt_ipv4_prefix_partial,
- partly_match, /* OK as far as it went */
- exact_match, /* Syntactically complete -- greatest match */
+ mt_range_partial,
+
+ mt_eol, /* an ms_anything match */
+
+ mt_option_word_match, /* anything can match a [WORD] */
+
+ mt_vararg_match, /* anything can match a .vararg */
+ mt_word_match, /* anything can match a WORD */
+
+ mt_keyword_incomplete,
+
+ mt_ipv6_prefix_complete,
+ mt_ipv6_address_complete,
+ mt_ipv4_prefix_complete,
+ mt_ipv4_address_complete,
+
+ mt_range_complete,
+
+ mt_keyword_complete,
match_type_count /* Number of match types */
} ;
typedef enum match_type match_type_t ;
-CONFIRM(no_match == false) ;
-CONFIRM(extend_match == (no_match + 1)) ;
-CONFIRM(partly_match == (exact_match - 1)) ;
-CONFIRM(exact_match == (match_type_count - 1)) ;
+/*------------------------------------------------------------------------------
+ * Map match_type -> cmd_item_type
+ *
+ * From a match type extract the type of item which has been match, partially
+ * or completely.
+ */
+Inline cmd_item_type_t
+match_item_type(match_type_t mt)
+{
+ static cmd_item_type_t match_item_type[match_type_count] =
+ {
+ [mt_no_match] = item_null,
+
+ [mt_eol_partial] = item_eol,
+
+ [mt_ipv4_prefix_partial] = item_ipv4_prefix,
+ [mt_ipv4_address_partial] = item_ipv4_address,
+ [mt_ipv6_prefix_partial] = item_ipv6_prefix,
+ [mt_ipv6_address_partial] = item_ipv6_address,
+
+ [mt_range_partial] = item_range,
+
+ [mt_eol] = item_eol,
+
+ [mt_option_word_match] = item_option_word,
+
+ [mt_vararg_match] = item_vararg,
+ [mt_word_match] = item_word,
+
+ [mt_keyword_incomplete] = item_keyword,
+
+ [mt_ipv6_prefix_complete] = item_ipv6_prefix,
+ [mt_ipv6_address_complete] = item_ipv6_address,
+ [mt_ipv4_prefix_complete] = item_ipv4_prefix,
+ [mt_ipv4_address_complete] = item_ipv4_address,
+
+ [mt_range_complete] = item_range,
+
+ [mt_keyword_complete] = item_keyword,
+ } ;
+
+ assert((mt >= 0) && (mt < match_type_count)) ;
+
+ return match_item_type[mt] ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Map match_type -> match_strength.
+ *
+ * From a match type extract the strength of the match.
+ */
+Inline match_strength_t
+match_match_strength(match_type_t mt)
+{
+ const static match_strength_t match_match_strength[match_type_count] =
+ {
+ [mt_no_match] = ms_no_match,
+
+ [mt_eol_partial] = ms_partial,
+
+ [mt_ipv6_prefix_partial] = ms_partial,
+ [mt_ipv6_address_partial] = ms_partial,
+ [mt_ipv4_prefix_partial] = ms_partial,
+ [mt_ipv4_address_partial] = ms_partial,
+
+ [mt_range_partial] = ms_partial,
+
+ [mt_eol] = ms_anything,
+
+ [mt_option_word_match] = ms_anything,
+
+ [mt_vararg_match] = ms_anything,
+ [mt_word_match] = ms_anything,
+
+ [mt_keyword_incomplete] = ms_kwd_incomplete,
+
+ [mt_ipv6_prefix_complete] = ms_var_complete,
+ [mt_ipv6_address_complete] = ms_var_complete,
+ [mt_ipv4_prefix_complete] = ms_var_complete,
+ [mt_ipv4_address_complete] = ms_var_complete,
+
+ [mt_range_complete] = ms_var_complete,
+
+ [mt_keyword_complete] = ms_kwd_complete,
+ } ;
+
+ assert((mt >= 0) && (mt < match_type_count)) ;
+
+ return match_match_strength[mt] ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Map cmd_item_type -> best possible match type.
+ *
+ * This gives the most optimistic outcome of an attempt to match to the given
+ * type of item.
+ *
+ * NB: tolerates item_null -- others may not
+ */
+Inline match_type_t
+item_best_match(cmd_item_type_t it)
+{
+ const static match_type_t item_best_match[item_type_count] =
+ {
+ [item_null] = mt_no_match,
+
+ [item_eol] = mt_eol,
+
+ [item_option_word] = mt_option_word_match,
+
+ [item_vararg] = mt_vararg_match,
+ [item_word] = mt_word_match,
+
+ [item_ipv6_prefix] = mt_ipv6_prefix_complete,
+ [item_ipv6_address] = mt_ipv6_address_complete,
+ [item_ipv4_prefix] = mt_ipv4_prefix_complete,
+ [item_ipv4_address] = mt_ipv4_address_complete,
+
+ [item_range] = mt_range_complete,
+
+ [item_keyword] = mt_keyword_complete,
+ } ;
+
+ assert((it >= 0) && (it < item_type_count)) ;
+
+ return item_best_match[it] ;
+} ;
/*==============================================================================
*
@@ -95,11 +452,13 @@ CONFIRM(exact_match == (match_type_count - 1)) ;
/* Command parsing options */
enum cmd_parse_type /* bit significant */
{
- cmd_parse_completion = 0,
+ cmd_parse_standard = 0, /* accept short command parts */
+
cmd_parse_strict = BIT(0),
+ cmd_parse_no_do = BIT(1),
+ cmd_parse_no_tree = BIT(2),
- cmd_parse_do = BIT(1),
- cmd_parse_tree = BIT(2),
+ cmd_parse_execution = BIT(3), /* wish to execute command */
} ;
typedef enum cmd_parse_type cmd_parse_type_t ;
@@ -108,28 +467,44 @@ enum cmd_pipe_type /* bit significant */
{
cmd_pipe_none = 0,
- cmd_pipe_in_file = BIT(0),
- cmd_pipe_in_shell = BIT(1),
+ cmd_pipe_file = BIT(0),
+ cmd_pipe_shell = BIT(1),
+ cmd_pipe_dev_null = BIT(2), /* out pipe only -- black hole */
- cmd_pipe_reflect = BIT(4),
- cmd_pipe_output = BIT(5),
- cmd_pipe_more = BIT(6),
+ /* For in pipes */
+ cmd_pipe_reflect = BIT(4), /* + option */
- cmd_pipe_out_file = BIT( 8),
- cmd_pipe_out_file_append = BIT( 9),
- cmd_pipe_out_shell = BIT(10),
+ /* For out file pipes */
+ cmd_pipe_append = BIT(4), /* >> */
} ;
typedef enum cmd_pipe_type cmd_pipe_type_t ;
+/* Parsed parts */
+enum cmd_parts /* bit significant */
+{
+ cmd_parts_none = 0,
+
+ cmd_part_do = BIT(0),
+ cmd_part_command = BIT(1),
+
+ cmd_part_in_pipe = BIT(2),
+ cmd_part_out_pipe = BIT(3),
+
+ cmd_parts_pipe = (cmd_part_in_pipe | cmd_part_out_pipe),
+
+ cmd_part_comment = BIT(4),
+} ;
+typedef enum cmd_parts cmd_parts_t ;
+
+
/*------------------------------------------------------------------------------
* Token object -- a qstring and some other properties.
*/
enum cmd_token_type /* *bit* significant */
{
- cmd_tok_null = 0, /* used for empty lines */
+ cmd_tok_eol = 0, /* all lines have one */
cmd_tok_simple = BIT( 0),
- cmd_tok_trailing = BIT( 1),
cmd_tok_sq = BIT( 8),
cmd_tok_dq = BIT( 9), /* '\\' within "..." are not
@@ -138,20 +513,24 @@ enum cmd_token_type /* *bit* significant */
cmd_tok_incomplete = (cmd_tok_sq | cmd_tok_dq | cmd_tok_esc),
- cmd_tok_pipe_in = BIT(12),
- cmd_tok_pipe_out = BIT(13),
- cmd_tok_comment = BIT(14),
+ cmd_tok_in_pipe = BIT(12), /* token starting '<' */
+ cmd_tok_out_pipe = BIT(13), /* token starting '>' */
+ cmd_tok_comment = BIT(14), /* token starting '!' or '#" */
} ;
typedef enum cmd_token_type cmd_token_type_t ;
-struct token
+struct cmd_token
{
cmd_token_type_t type ;
- qstring_t qs ;
- size_t tp ;
+ qstring_t qs ; /* token string */
+
+ bool term ; /* token has been '\0' terminated */
+
+ usize tp ; /* location of token in the line */
+ elstring_t ot ; /* original token in the line */
} ;
-typedef struct token token_t[1] ;
-typedef struct token* token ;
+
+typedef struct cmd_token* cmd_token ;
/*------------------------------------------------------------------------------
* Token vector -- a vector of token objects
@@ -164,6 +543,10 @@ struct token_vector
typedef struct token_vector token_vector_t[1] ;
typedef struct token_vector* token_vector ;
+enum {
+ TOKEN_VECTOR_INIT_ALL_ZEROS = VECTOR_INIT_ALL_ZEROS
+} ;
+
/*------------------------------------------------------------------------------
* Argument vector -- a vector of const char*
*/
@@ -174,6 +557,10 @@ struct arg_vector
typedef struct arg_vector arg_vector_t[1] ;
typedef struct arg_vector* arg_vector ;
+enum {
+ ARG_VECTOR_INIT_ALL_ZEROS = VECTOR_INIT_ALL_ZEROS
+} ;
+
/*------------------------------------------------------------------------------
* Parsed command line
*
@@ -191,252 +578,209 @@ typedef struct arg_vector* arg_vector ;
* emptied -- see cmd_empty_parsed_tokens() -- but the next command to be
* parsed will tidy up before proceeding.
*/
-typedef struct cmd_parsed* cmd_parsed ;
struct cmd_parsed
{
- struct cmd_element *cmd ; /* NULL if empty command
- or fails to parse */
+ cmd_parts_t parts ; /* What parts are present */
- const char* line ; /* the current line */
+ cmd_token_type_t tok_total ; /* What token types are present */
- enum node_type cnode ; /* node command is in */
- enum node_type onode ; /* node the parser started in */
+ usize elen ; /* effective length (less trailing spaces) */
+ usize tsp ; /* number of trailing spaces */
- bool do_shortcut ; /* true => is "do" command */
+ uint num_tokens ; /* number of tokens parsed */
- token_vector_t tokens ; /* vector of token objects */
- arg_vector_t args ; /* vector of arguments */
+ token_vector_t tokens ; /* vector of token objects */
- cmd_pipe_type_t pipes ; /* if any */
+ /* NB: the following are significant only if there is a command part
+ * or a do part
+ */
+ struct cmd_command *cmd ; /* NULL if empty command
+ or fails to parse */
+ node_type_t cnode ; /* node command is in */
- token_vector_t read_pipe_tokens ;
- token_vector_t write_pipe_tokens ;
-} ;
+ arg_vector_t args ; /* vector of arguments -- embedded */
-/* Command dispatch options */
-enum {
- cmd_no_queue = true,
- cmd_may_queue = false,
-} ;
+ /* NB: the following are significant only if an error is returned */
-/*------------------------------------------------------------------------------
- * Vector of spare token objects -- declared here to allow inlines, defined
- * in command_parse.c
- */
-extern token_vector_t spare_tokens ;
+ const char* emess ; /* parse error */
+ usize eloc ; /* error location */
-/*==============================================================================
- * Prototypes
- */
-extern void cmd_spare_tokens_init(void) ;
-extern void cmd_spare_tokens_free(void) ;
-
-extern cmd_parsed cmd_parse_init_new(cmd_parsed parsed) ;
-extern cmd_parsed cmd_parse_reset(cmd_parsed parsed, bool free_structure) ;
-
-extern cmd_token_type_t cmd_tokenise(cmd_parsed parsed, const char *line,
- node_type_t node) ;
-Inline const char* cmd_token_string(token t) ;
-Inline int cmd_token_count(token_vector tv) ;
-Inline token cmd_token_get(token_vector tv, vector_index_t i) ;
-Inline token cmd_token_pop(token_vector tv) ;
-Inline void cmd_token_push(token_vector tv, token t) ;
-Inline token cmd_token_shift(token_vector tv) ;
-Inline void cmd_token_unshift(token_vector tv, token t) ;
-Inline token cmd_token_make(void) ;
-Inline token cmd_token_new(cmd_token_type_t type, const char* p,
- size_t len, size_t tp) ;
-
-Inline void cmd_empty_token_vector(token_vector tv) ;
-Inline void cmd_empty_parsed_tokens(cmd_parsed parsed) ;
-
-Inline bool cmd_token_complete(token t) ;
-extern bool cmd_token_do_complete(token t) ;
-
-extern match_type_t cmd_ipv4_match (const char *str) ;
-extern match_type_t cmd_ipv4_prefix_match (const char *str) ;
-#if HAVE_IPV6
-extern match_type_t cmd_ipv6_match (const char *str) ;
-extern match_type_t cmd_ipv6_prefix_match (const char *str) ;
-#endif
-extern bool cmd_range_match (const char *range, const char *str) ;
+ /* NB: the following are significant only if respective part is
+ * present.
+ */
+ cmd_pipe_type_t in_pipe ; /* if any */
+ cmd_pipe_type_t out_pipe ; /* if any */
-/*==============================================================================
- * Inline Functions
- */
+ uint first_in_pipe ;
+ uint num_in_pipe ;
-/*------------------------------------------------------------------------------
- * Get pointer to token value.
- *
- * Returns NULL if token NULL or no string.
- */
-Inline char*
-cmd_token_value(token t)
-{
- return (t == NULL) ? NULL : qs_chars(t->qs) ;
-} ;
+ uint first_do ;
+ uint num_do ;
-/*------------------------------------------------------------------------------
- * Get string value of given token.
- *
- * Returns an empty (not NULL) string if token NULL or no string.
- */
-Inline const char*
-cmd_token_string(token t)
-{
- const char* s = cmd_token_value(t) ;
- return (s == NULL) ? "" : s ;
-} ;
+ uint first_command ;
+ uint num_command ;
-/*------------------------------------------------------------------------------
- * Get number of tokens in the given token vector
- */
-Inline int
-cmd_token_count(token_vector tv)
-{
- return vector_length(tv->body) ;
-} ;
+ uint first_out_pipe ;
+ uint num_out_pipe ;
-/*------------------------------------------------------------------------------
- * Get i'th token from given token vector -- zero origin
- */
-Inline token
-cmd_token_get(token_vector tv, vector_index_t i)
-{
- return vector_get_item(tv->body, i) ;
-} ;
+ uint first_comment ;
+ uint num_comment ;
-/*------------------------------------------------------------------------------
- * Pop token from end of given token vector -- if any.
- */
-Inline token
-cmd_token_pop(token_vector tv)
-{
- return vector_pop_item(tv->body) ;
-} ;
+ /* The following are significant after cmd_token_position() */
-/*------------------------------------------------------------------------------
- * Push token onto end of given token vector.
- */
-Inline void
-cmd_token_push(token_vector tv, token t)
-{
- vector_push_item(tv->body, t) ;
+ uint cti ; /* cursor token index -- may be eol token */
+ uint ctl ; /* cursor token length -- 0 <=> eol token */
+ int rp ; /* cursor relative to start of cursor token */
+
+ /* The following are used while filtering commands */
+
+ vector_t cmd_v ; /* working vector -- embedded */
+ vector_t item_v ; /* working vector -- embedded */
+
+ match_strength_t strongest ;
+ match_type_t best_complete ;
+
+ match_strength_t min_strength ; /* for execution */
+ bool strict ; /* for strict keyword match */
} ;
-/*------------------------------------------------------------------------------
- * Shift first token off front of given token vector -- if any.
- */
-Inline token
-cmd_token_shift(token_vector tv)
+enum
{
- return vector_shift_item(tv->body) ;
+ CMD_PARSED_INIT_ALL_ZEROS = (cmd_pipe_none == 0)
+ && TOKEN_VECTOR_INIT_ALL_ZEROS
+ && ARG_VECTOR_INIT_ALL_ZEROS
} ;
-/*------------------------------------------------------------------------------
- * Unshift token onto front of given token vector.
- */
-Inline void
-cmd_token_unshift(token_vector tv, token t)
+typedef struct cmd_parsed cmd_parsed_t[1] ;
+typedef struct cmd_parsed* cmd_parsed ;
+
+/* Command dispatch options */
+enum cmd_queue_b
{
- vector_unshift_item(tv->body, t) ;
+ cmd_no_queue = false,
+ cmd_queue_it = true,
} ;
+typedef enum cmd_queue_b cmd_queue_b ;
-/*------------------------------------------------------------------------------
- * Make a brand new token object
+/*==============================================================================
+ * Prototypes
*/
-Inline token
-cmd_token_make(void)
-{
- return XCALLOC(MTYPE_TOKEN, sizeof(struct token)) ;
+extern void cmd_compile(cmd_command cmd) ;
+extern void cmd_compile_check(cmd_command cmd) ;
- /* Zeroising the new structure sets:
- *
- * type = 0 -- cmd_tok_null
- * qs = zeroised qstring -- empty string
- */
- confirm(cmd_tok_null == 0) ;
-} ;
+extern cmd_parsed cmd_parsed_init_new(cmd_parsed parsed) ;
+extern cmd_parsed cmd_parsed_reset(cmd_parsed parsed,
+ free_keep_b free_structure) ;
+extern bool cmd_is_empty(elstring line) ;
-/*------------------------------------------------------------------------------
- * Create new 'cmd_tok_simple' token from given characters + length
- */
-Inline token
-cmd_token_new(cmd_token_type_t type, const char* p, size_t len, size_t tp)
-{
- token t ;
+extern cmd_return_code_t cmd_parse_command(cmd_parsed parsed,
+ node_type_t node, cmd_parse_type_t type) ;
- t = cmd_token_pop(spare_tokens) ;
- if (t == NULL)
- t = cmd_token_make() ;
+extern bool cmd_token_position(cmd_parsed parsed, qstring line) ;
+extern const char* cmd_help_preflight(cmd_parsed parsed) ;
+extern cmd_return_code_t cmd_completion(cmd_parsed parsed, node_type_t node) ;
- t->type = type ;
- qs_set_n(&t->qs, p, len) ;
- t->tp = tp ;
+extern void cmd_complete_keyword(cmd_parsed parsed,
+ int* pre, int* rep, int* ins, int* mov) ;
- return t ;
-} ;
-/*------------------------------------------------------------------------------
- * Discard given token -- give back to spare tokens list
- */
-Inline void
-cmd_token_discard(token t)
-{
- if (t != NULL)
- vector_push_item(spare_tokens->body, t) ;
-} ;
-/*------------------------------------------------------------------------------
- * Release contents of token vector -- move to the spare_token_strings.
+
+
+
+//extern vector cmd_make_strvec (const char *);
+//extern vector cmd_add_to_strvec (vector v, const char* str) ;
+//extern void cmd_free_strvec (vector);
+extern vector cmd_describe_command (const char* line, node_type_t node,
+ cmd_return_code_t* status) ;
+extern vector cmd_complete_command (vector, int, int *status);
+
+
+
+extern void cmd_tokenise(cmd_parsed parsed, qstring line) ;
+//extern cmd_return_code_t cmd_parse_error(cmd_parsed parsed, cmd_token t,
+// usize off, const char* mess) ;
+
+//Inline const char* cmd_token_string(cmd_token t) ;
+//Inline char* cmd_token_make_string(cmd_token t) ;
+//Inline int cmd_token_count(token_vector tv) ;
+//Inline cmd_token cmd_token_get(token_vector tv, vector_index_t i) ;
+//Inline void cmd_token_set(token_vector tv, vector_index_t i,
+// cmd_token_type_t type, const char* p, usize len, usize tp) ;
+
+//extern cmd_return_code_t cmd_token_complete(cmd_parsed parsed, cmd_token t) ;
+//Inline const char* cmd_token_string(cmd_token t) ;
+//extern cmd_return_code_t cmd_parse_in_pipe(cmd_parsed parsed, cmd_token t) ;
+//extern cmd_return_code_t cmd_parse_out_pipe(cmd_parsed parsed, cmd_token t) ;
+
+//extern match_type_t cmd_ipv4_match (const char *str) ;
+//extern match_type_t cmd_ipv4_prefix_match (const char *str) ;
+#if HAVE_IPV6
+//extern match_type_t cmd_ipv6_match (const char *str) ;
+//extern match_type_t cmd_ipv6_prefix_match (const char *str) ;
+#endif
+//extern bool cmd_range_match (const char *range, const char *str) ;
+
+/*==============================================================================
+ * Inline Functions
*/
-Inline void
-cmd_empty_token_vector(token_vector tv)
-{
- if (cmd_token_count(tv) != 0)
- vector_move_append(spare_tokens->body, tv->body) ;
-} ;
+
+Private cmd_token cmd_token_new(void) ;
/*------------------------------------------------------------------------------
- * Release contents of all token vectors in given parsed object.
+ * Get pointer to token value.
+ *
+ * Returns NULL if token NULL or no string.
*/
-Inline void
-cmd_empty_parsed_tokens(cmd_parsed parsed)
+Inline char*
+cmd_token_value(cmd_token t)
{
- cmd_empty_token_vector(parsed->tokens) ;
- cmd_empty_token_vector(parsed->read_pipe_tokens) ;
- cmd_empty_token_vector(parsed->write_pipe_tokens) ;
+ return (t == NULL) ? NULL : qs_char_nn(t->qs) ;
} ;
/*------------------------------------------------------------------------------
- * If token is incomplete (contains quotes or escapes) process those down.
+ * Get string value of given token.
*
- * Returns: true <=> OK
- * false => invalid escape
+ * Returns an empty (not NULL) string if token NULL or no string.
*/
-Inline bool
-cmd_token_complete(token t)
+Inline char*
+cmd_token_make_string(cmd_token t)
{
- return ((t->type & cmd_tok_incomplete) == 0) ? true
- : cmd_token_do_complete(t) ;
+ if (t->term)
+ return qs_char_nn(t->qs) ;
+ else
+ {
+ t->term = true ;
+ return qs_make_string(t->qs) ;
+ }
} ;
/*------------------------------------------------------------------------------
- * Initialise arg_vector object in cmd_parsed.
+ * Get i'th token from given token vector -- zero origin
*/
-Inline void
-cmd_arg_vector_init(cmd_parsed parsed)
+Inline cmd_token
+cmd_token_get(token_vector tv, vector_index_t i)
{
- vector_init_new(parsed->args->body, 0) ;
+ return vector_get_item(tv->body, i) ;
} ;
/*------------------------------------------------------------------------------
- * Free the body of the arg_vector object in cmd_parsed.
+ * Set i'th token from given token vector -- zero origin
*/
Inline void
-cmd_arg_vector_free(cmd_parsed parsed)
+cmd_token_set(token_vector tv, vector_index_t i,
+ cmd_token_type_t type, const char* p, usize len, usize tp)
{
- vector_reset(parsed->args->body, keep_it) ;
+ cmd_token t = cmd_token_get(tv, i) ;
+
+ if (t == NULL)
+ vector_set_item(tv->body, i, (t = cmd_token_new())) ;
+
+ t->type = type ;
+ t->term = false ;
+ t->tp = tp ;
+ qs_set_alias_n(t->qs, p, len) ;
+ qs_els_copy_nn(t->ot, t->qs) ;
} ;
/*------------------------------------------------------------------------------
diff --git a/lib/command_queue.c b/lib/command_queue.c
index 5f14abae..0a09d950 100644
--- a/lib/command_queue.c
+++ b/lib/command_queue.c
@@ -19,136 +19,564 @@
* Boston, MA 02111-1307, USA.
*/
-#include <zebra.h>
+#include "zconfig.h" /* for CONSUMED_TIME_CHECK */
+#include "misc.h"
-#include "mqueue.h"
#include "qpnexus.h"
-#include "memory.h"
+#include "mqueue.h"
+#include "thread.h"
+
#include "command_queue.h"
#include "command_execute.h"
-#include "vty.h"
-#include "uty.h"
-#include "vector.h"
-#include "qstring.h"
+#include "vty_command.h"
+#include "vty_io.h"
+#include "log.h"
+
+/*==============================================================================
+ * This command loop processes commands for VTY_TERMINAL and VTY_SHELL_SERVER.
+ *
+ * Commands appear from those sources, driven by socket I/O. Once a command
+ * line is ready it is passed out of the I/O pselect/select driven code
+ * as a message, and enters the command loop.
+ *
+ * If pipes are involved that is all dealt with in the command loop, which
+ * runs until the vin/vout stacks return to 0 -- the VTY_TERMINAL and
+ * VTY_SHELL_SERVER.
+ *
+ * There are further issues:
+ *
+ * 1) in the qpthreads world, commands are parsed in the CLI thread, but most
+ * are executed in the Routing thread. So parsed commands are passed, by
+ * message between threads.
+ *
+ * 2) input is expected to be non-blocking -- so the command loop will
+ * exit if a command line cannot be delivered immediately, and will be
+ * returned to later.
+ *
+ * 3) while a VTY is in the command loop it is marked vio->cmd_running.
+ *
+ * While that is true, the vty and the vty->exec are in the hands
+ * of the command loop. The vty->vio is always accessed under VTY_LOCK().
+ *
+ * 4) opening pipes is done in the CLI thread, in case of any possible
+ * blocking.
+ *
+ * 5) all output is to fifo buffers -- when output is pushed the CLI side
+ * is kicked to manage all output via pselect/select.
+ *
+ * In vty_io_basic() it is possible to set read/write ready and associated
+ * timeouts while running in the CMD thread, but that too depends on the
+ * passing of messages to the CLI thread.
+ *
+ * The smooth running of the command handling depends on the continued
+ * running of the CLI thread.
+ *
+ * To close a VTY must (eventually) arrange for vio->cmd_running to be cleared.
+ * While a vty is vio->cmd_running, it must be in one of these states:
+ *
+ * - on the vty_cli_nexus queue (or the combined queue)
+ * - executing in the vty_cli_nexus (or the single "both" nexus)
+ * - on the vty_cmd_nexus queue (if different)
+ * - executing in the vty_cmd_nexus (if different)
+ *
+ * Or, in the legacy threads world:
+ *
+ * - on the event queue
+ * - executing
+ *
+ * Where there is only one pthread (and in the legacy threads world) things are
+ * easy.
+ *
+ *
+ * This revoke runs in the CLI thread, and will catch the message if it is
+ * on either queue, and vty_revoke() will deal with it -- still in the CLI
+ * thread.
+ *
+ * The command cannot be running in the CLI thread, since that is where we
+ * are !
+ *
+ * That leaves the command running in the CMD thread. That will run to
+ * completion... the VTY may be closed in the meantime, which will shut down
+ * the reading side, so the command loop will come to a halt quite quickly.
+ * Note, however, that the smooth running of this process requires the CLI
+ * thread and its messages to be
+ *
+ *
+ */
+
+
+
/*------------------------------------------------------------------------------
- * Form of message passed with command to be executed
+ * Prototypes
*/
+static void cq_enqueue(struct vty *vty, qpn_nexus dst, cmd_exec_state_t state,
+ cmd_return_code_t ret) ;
+static void cq_action(mqueue_block mqb, mqb_flag_t flag);
+static int cq_thread(struct thread* thread) ;
+static void cq_process(vty vty) ;
-struct cq_command_args
+/*------------------------------------------------------------------------------
+ * Enqueue vty for parse and execution of a command.
+ *
+ * Sets the vio->cmd_running flag, which will be cleared only when the
+ * command is completed (including any nested pipes etc.) or when the vty
+ * is blocked on input or it is revoked.
+ *
+ * Note that from now on, exec->ret reflects the state of the return
+ * code when the vty was last enqueued.
+ *
+ * While vio_cmd_running is set, the CLI side MUST NOT fiddle with the main
+ * part of the VTY or with the vty->exec state.
+ */
+extern void
+cq_dispatch(vty vty, cmd_do_t to_do, qstring line)
{
- enum cmd_return_code ret ; /* return code from command */
+ VTY_ASSERT_CLI_THREAD() ;
+
+ vty->exec->to_do = to_do ;
+ vty->exec->line = line ;
+ vty->vio->cmd_running = true ;
+ cq_enqueue(vty, vty_cli_nexus,
+ (to_do == cmd_do_command) ? exec_parse
+ : exec_special, CMD_SUCCESS) ;
} ;
-MQB_ARGS_SIZE_OK(cq_command_args) ;
/*------------------------------------------------------------------------------
- * Prototypes
+ * Enqueue vty for fetching a command line from an in_pipe which has just
+ * received more input.
+ *
+ * Sets the vio->cmd_running flag, which will be cleared only when the
+ * command is completed (including any nested pipes etc.) or when the vty
+ * is blocked on input or it is revoked.
+ *
+ * While vio_cmd_running is set, the CLI side MUST NOT fiddle with the main
+ * part of the VTY or with the vty->exec state.
*/
-static void cq_action(mqueue_block mqb, mqb_flag_t flag);
-static void cq_return(mqueue_block mqb, mqb_flag_t flag);
+extern void
+cq_go_fetch(vty vty)
+{
+ VTY_ASSERT_CLI_THREAD() ;
+
+ vty->vio->cmd_running = true ;
+ cq_enqueue(vty, vty_cli_nexus, exec_fetch, CMD_SUCCESS) ;
+} ;
/*------------------------------------------------------------------------------
- * Enqueue vty and argv[] for execution in given nexus.
+ * Enqueue vty for execution in given nexus or issue thread event.
+ *
+ * Note that preserves the return code state.
*/
-void
-cq_enqueue(struct vty *vty, qpn_nexus dst)
+static void
+cq_enqueue(vty vty, qpn_nexus dst, cmd_exec_state_t state,
+ cmd_return_code_t ret)
{
- struct cq_command_args* args ;
- mqueue_block mqb ;
+ cmd_exec exec = vty->exec ;
+
+ assert(vty->vio->cmd_running) ;
+ assert((dst == vty_cli_nexus) || (dst == vty_cmd_nexus)) ;
- assert(vty_cli_nexus) ; /* must be running qnexus-wise */
+ exec->locus = dst ;
+ exec->state = state ;
+ exec->ret = ret ;
- mqb = mqb_init_new(NULL, cq_action, vty) ;
- args = mqb_get_args(mqb) ;
+ if (vty_nexus)
+ {
+ mqueue_block mqb ;
- args->ret = CMD_QUEUED ;
+ if ((mqb = exec->cq.mqb) == NULL)
+ mqb = exec->cq.mqb = mqb_init_new(NULL, cq_action, vty) ;
- mqueue_enqueue(dst->queue, mqb, 0) ;
-}
+ mqueue_enqueue(dst->queue, mqb, (dst == vty_cmd_nexus) ? mqb_priority
+ : mqb_ordinary) ;
+ }
+ else
+ {
+ assert(vty_cli_nexus == vty_cmd_nexus) ;
+ exec->cq.thread = thread_add_event(vty_master, cq_thread, vty, 0) ;
+ } ;
+} ;
/*------------------------------------------------------------------------------
- * Dispatch a command from the message queue block
+ * Deal with command message -- in the qpthreads world.
*
- * When done (or revoked/deleted) return the message, so that the sender knows
- * that the command has been dealt with (one way or another).
- *
- * Note that if the command is revoked the return is set to CMD_QUEUED.
+ * Note that if the command is revoked....
*/
static void
cq_action(mqueue_block mqb, mqb_flag_t flag)
{
- struct vty *vty;
- struct cq_command_args* args ;
+ vty vty;
- assert(vty_cli_nexus) ; /* must be running qnexus-wise */
+ assert(vty_nexus) ; /* must be running qnexus-wise */
vty = mqb_get_arg0(mqb);
- args = mqb_get_args(mqb) ;
+
+ assert(vty->exec->cq.mqb == mqb) ;
+ assert(vty->vio->cmd_running) ;
if (flag == mqb_action)
- {
- args->ret = cmd_dispatch_call(vty) ;
- assert(args->ret != CMD_QUEUED) ; /* avoid confusion ! */
- }
- else
- args->ret = CMD_QUEUED ;
+ return cq_process(vty) ; /* do not touch vty on way out */
+
+ mqb_free(mqb) ;
+ vty->exec->cq.mqb = NULL ;
+} ;
- mqb_set_action(mqb, cq_return) ;
- mqueue_enqueue(vty_cli_nexus->queue, mqb, 0) ;
+/*------------------------------------------------------------------------------
+ * Deal with command message -- in the legacy threads world.
+ *
+ * Note that if the command is revoked....
+ */
+static int
+cq_thread(struct thread* thread)
+{
+ vty vty = THREAD_ARG(thread) ;
+
+ assert(vty->exec->cq.thread == thread) ;
+ assert(vty->vio->cmd_running) ;
+
+ vty->exec->cq.thread = NULL ;
+
+ cq_process(vty) ; /* do not touch vty on way out */
+
+ return 0 ;
} ;
/*------------------------------------------------------------------------------
- * Accept return from command which has completed.
+ * Process command(s) queued from VTY_TERMINAL or from VTY_SHELL_SERVER.
*
- * The command line processing for the vty may be stalled (with read mode
- * disabled) waiting for the return from the command.
+ * To get into the process loop, or to get back to it when more input has
+ * arrived a message is sent:
*
- * Do not care whether the message is being revoked or not... the command
- * has completed and that must be signalled to the CLI. Any pending output
- * is released.
+ * cli -> cli -- exec_parse -- when a command line has been gathered
+ * from input and is ready to be processed.
*
- * The command itself may have been revoked before it was executed. That
- * makes no difference either... the output buffers will simply be empty.
- * However, the return code is CMD_QUEUED, to signal the fact that the command
- * was never executed.
+ * cli -> cli -- exec_fetch -- when was waiting for more input from an
+ * in_pipe of some sort.
+ *
+ * In the single-pthread world, where the vty_cli_nexus is the same as the
+ * vty_cmd_nexus (either because not running qpthreaded, or because are
+ * running in the legacy threads world), things are reasonably straightforward.
+ * The process runs to completion or until an in_pipe would block, and the
+ * above are the only messages in the system.
+ *
+ * In the multi-pthread world, things are more complicated... The above
+ * messages are sent, and in addition the following are sent back and forth to
+ * transfer between threads in the following states:
+ *
+ * cmd -> cli -- exec_open_pipes
+ * cli -> cmd -- exec_execute
+ * cmd -> cli -- exec_complete
+ *
+ * Note that this means that in the multi-pthread world, only one sort of
+ * message is sent to the vty_cmd_nexus.
+ *
+ * The vty_io_basic stuff allows the multi-pthread vty_cmd_nexus to set read
+ * and/or write ready state -- which may generate messages to the vty_cli_nexus.
+ *
+ * NB: if blocks in exec_fetch, then vty_cmd_fetch_line() will have cleared
+ * vio->cmd_running -- so on return from cq_process the vty MAY HAVE BEEN
+ * DELETED.
*/
static void
-cq_return(mqueue_block mqb, mqb_flag_t flag)
+cq_process(vty vty)
{
- struct vty *vty ;
- struct cq_command_args* args ;
+ cmd_exec exec = vty->exec ;
+ cmd_parsed parsed = exec->parsed ;
+ cmd_return_code_t ret = exec->ret ;
+
+ /* Have switch wrapped in a while(1) so that can change state by setting
+ * exec->state and doing "continue".
+ *
+ * Breaking out of the switch forces the exec state to exec_complete,
+ * with the current ret, in the CLI thread.
+ *
+ * The exec locus is either vty_cli_nexus or vty_cmd_nexus. If these
+ * are equal (including both NULL, in the legacy threads world), then is
+ * running single threaded -- otherwise is running multi-threaded.
+ */
+ while (1)
+ {
+ switch(exec->state)
+ {
+ /*--------------------------------------------------------------------
+ * Should not get here in exec_null state.
+ */
+ case exec_null:
+ zabort("exec->state == exec_null") ;
+ break ;
+
+ /*--------------------------------------------------------------------
+ * Deal with the "spacial" commands
+ */
+ case exec_special:
+ ret = vty_cmd_special(vty) ;
+ if (ret != CMD_SUCCESS)
+ break ;
+
+ exec->state = exec_fetch ;
+ /* continue by falling through */
+
+ /*--------------------------------------------------------------------
+ * Need another command to execute => in_pipe !
+ *
+ * Note that at vin_depth == 0 this will return CMD_EOF, and will
+ * drop out of the loop exec_complete.
+ *
+ * Will also receive CMD_EOF if the VTY has been closed.
+ *
+ * If multi-threaded: may be in either thread:
+ *
+ * vty_cmd_fetch_line() may set read and/or write ready -- so in
+ * vty_cmd_nexus may generate message to vty_cli_nexus.
+ */
+ case exec_fetch:
+ ret = vty_cmd_fetch_line(vty) ;
+
+ if (ret != CMD_SUCCESS)
+ {
+ /* If is CMD_WAITING, then the vty_cmd_fetch_line() will
+ * have prepared for command to be re-queued when there is more
+ * to be read. NB: vio->cmd_running has been cleared, so
+ * vty MAY HAVE BEEN DELETED !
+ *
+ * If is CMD_EOF then is "closing" or reached EOF on top-most
+ * pipe.
+ */
+ if (ret == CMD_WAITING)
+ return ; /* <<<< DONE, pro tem */
+
+ break ; /* => exec_complete */
+ } ;
+
+ if (exec->to_do != cmd_do_command)
+ {
+ exec->state = exec_special ;
+ continue ;
+ } ;
+
+ exec->state = exec_parse ;
+ /* continue by falling through */
+
+ /*--------------------------------------------------------------------
+ * Parse command in hand
+ *
+ * If multi-threaded: may be in either thread:
+ *
+ * vty_cmd_reflect_line() may set read and/or write ready -- so in
+ * vty_cmd_nexus may generate message to vty_cli_nexus.
+ */
+ case exec_parse:
+ cmd_tokenise(parsed, exec->line) ;
+ ret = cmd_parse_command(parsed, vty->node,
+ exec->parse_type | cmd_parse_execution) ;
+ if (ret != CMD_SUCCESS)
+ {
+ if (ret != CMD_EMPTY)
+ break ; /* stop on *any* parsing issue */
+
+ /* Empty lines from in_pipes we simply ignore.
+ *
+ * Don't expect to see them otherwise, but if we do then need
+ * to complete the command execution process.
+ */
+ ret = CMD_SUCCESS ;
+
+ exec->state = exec_success ;
+ continue ;
+ } ;
+
+ /* reflection now -- output always succeeds */
+ if (exec->reflect_enabled)
+ vty_cmd_reflect_line(vty) ;
+
+ /* continue by falling through */
+
+ /*--------------------------------------------------------------------
+ * Pipe work if any
+ *
+ * Will receive CMD_EOF if the VTY has been closed.
+ *
+ * If multi-threaded: must be in vty_cli_nexus to proceed -- so may
+ * generate message to transfer to vty_cli_nexus.
+ */
+ case exec_open_pipes:
+ if ((parsed->parts & cmd_parts_pipe) != 0)
+ {
+ exec->state = exec_open_pipes ;
+
+ /* If running pthreaded, do open pipes in vty_cli_nexus */
+ if (exec->locus != vty_cli_nexus)
+ return cq_enqueue(vty, vty_cli_nexus, exec_open_pipes, ret) ;
+
+ /* Now in vty_cli_nexus */
+ ret = cmd_open_pipes(vty) ;
+ if (ret != CMD_SUCCESS)
+ break ; /* quit if open fails */
+ } ;
- assert(vty_cli_nexus) ; /* must be running qnexus-wise */
+ exec->state = exec_execute ;
+ /* continue by falling through */
- vty = mqb_get_arg0(mqb) ;
- args = mqb_get_args(mqb) ;
+ /*--------------------------------------------------------------------
+ * Execute command in hand
+ *
+ * If multi-threaded: some commands can run in either thread, most must
+ * run in the vty_cmd_nexus -- so may generate message to transfer to
+ * the vty_cmd_nexus.
+ */
+ case exec_execute:
+ if ((parsed->parts & cmd_part_command) != 0)
+ {
+ /* If running pthreaded, do most commands in vty_cmd_nexus */
+ if ((exec->locus != vty_cmd_nexus) &&
+ (!cmd_is_direct(parsed)))
+ return cq_enqueue(vty, vty_cmd_nexus, exec_execute, ret) ;
- /* signal end of command */
- cmd_post_command(vty, args->ret) ;
- vty_queued_result(vty, args->ret) ;
+ /* Standard command handling */
+#ifdef CONSUMED_TIME_CHECK
+ {
+ RUSAGE_T before;
+ RUSAGE_T after;
+ unsigned long realtime, cputime;
-//if (qpthreads_enabled)
-// qpt_thread_signal(vty_cli_nexus->thread_id, SIGMQUEUE);
+ GETRUSAGE(&before);
+#endif /* CONSUMED_TIME_CHECK */
- mqb_free(mqb);
-}
+ ret = cmd_execute(vty) ;
+
+#ifdef CONSUMED_TIME_CHECK
+ GETRUSAGE(&after);
+ realtime = thread_consumed_time(&after, &before, &cputime) ;
+ if (realtime > 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,
+ qs_make_string(exec->line)) ;
+ } ;
+#endif /* CONSUMED_TIME_CHECK */
+
+ if (ret != CMD_SUCCESS)
+ break ; /* stop */
+ } ;
+
+ exec->state = exec_success ;
+ /* continue by falling through */
+
+ /*--------------------------------------------------------------------
+ * Command has completed successfully -- so push the output.
+ *
+ * If the vout_depth > vin_depth, pops the vout's -- deals with single
+ * command lines with a pipe output.
+ *
+ * Output cannot block, so this always succeeds.
+ *
+ * Then loop back to fetch another command line, if can.
+ *
+ * If multi-threaded: may be in either thread:
+ *
+ * vty_cmd_success() may set write ready -- so in vty_cmd_nexus may
+ * generate message to vty_cli_nexus.
+ */
+ case exec_success:
+ assert(ret == CMD_SUCCESS) ;
+
+ vty_cmd_success(vty) ;
+
+ exec->state = exec_fetch ;
+ continue ;
+
+ /*--------------------------------------------------------------------
+ * End of the command loop !
+ *
+ * If multi-threaded: must return to the vty_cli_nexus.
+ */
+ case exec_complete:
+ if (exec->locus != vty_cli_nexus)
+ break ; /* Will send back to the cli */
+
+ /* Now in the vty_cli_nexus */
+
+ exec->state = exec_null ; /* all done ! */
+
+ vty_cmd_loop_exit(vty, ret) ; /* clears vio->cmd_running */
+
+ return ; /* <<<< Finally finished ! */
+
+ /*----------------------------------------------------------------------
+ * Unknown exec->state !
+ */
+ default:
+ zabort("unknown exec->state") ;
+ break ;
+ } ;
+
+ /* Have broken out of the switch(). This means that for good or ill,
+ * the command is complete. If we are not in the vty_cli_nexus, need to
+ * send back to the vty_cli_nexus for handling.
+ *
+ * At all times we treat CMD_EOF and CMD_SUCCESS.
+ *
+ * Otherwise, can continue in exec_complete state.
+ */
+ if (ret == CMD_EOF)
+ ret = CMD_SUCCESS ;
+
+ if (exec->locus != vty_cli_nexus)
+ return cq_enqueue(vty, vty_cli_nexus, exec_complete, ret) ;
+
+ exec->state = exec_complete ;
+ } ;
+} ;
/*------------------------------------------------------------------------------
- * Revoke any messages related to the given VTY -- if running qnexus-wise.
+ * Revoke any message associated with the given vty from the command queue.
+ *
+ * This is used when a VTY_TERMINAL or a VTY_SHELL_SERVER is being closed.
+ *
+ * See cq_process above for discussion of what messages there may be. At any
+ * time there is at most one message in flight.
+ *
+ * If we find a message in flight, then we vty_cmd_loop_exit() to bring things
+ * to a stop tidily.
*
- * Revokes in vty_cmd_nexus -- so before command is started
- * and in vty_cli_nexus -- so after command has completed
+ * In the single-threaded world, expect that a command, once started will run
+ * to conclusion, or until blocked on an in_pipe. So after this revoke the
+ * vty should not be vio->cmd_running.
*
- * Can do nothing about any command actually being executed in the
- * vty_cmd_nexus.
+ * In the multi-threaded world, this revoke will catch any vty which is on
+ * either the vty_cli_nexus or vty_cmd_nexus queues, and force completion.
+ * After this revoke, vio->cmd_running will be true iff the command is
+ * currently being executed in the vty_cmd_nexus -- we expect that to run to
+ * conclusion or block on an in_pipe, shortly, which will be collected when
+ * the vty_cli_nexus message queue is next processed.
+ *
+ * Note that the revoke does not affect any vty_cli_nexus messages associated
+ * with the vty_io_basic operations.
*/
-void
-cq_revoke(struct vty *vty)
+extern void
+cq_revoke(vty vty)
{
- if (vty_cli_nexus)
+ int ret ;
+
+ VTY_ASSERT_CLI_THREAD() ;
+
+ if (vty_nexus)
{
- mqueue_revoke(vty_cmd_nexus->queue, vty) ;
- mqueue_revoke(vty_cli_nexus->queue, vty) ;
+ ret = mqueue_revoke(vty_cmd_nexus->queue, vty, 1) ;
+
+ if ((ret == 0) && (vty_cli_nexus != vty_cmd_nexus))
+ ret = mqueue_revoke(vty_cli_nexus->queue, vty, 1) ;
+ }
+ else
+ ret = thread_cancel_event(vty_master, vty) ;
+
+ if (ret != 0)
+ {
+ vty->exec->state = exec_null ;
+ vty_cmd_loop_exit(vty, vty->exec->ret) ;
} ;
-}
+} ;
+
+
diff --git a/lib/command_queue.h b/lib/command_queue.h
index 804dfa1f..0966bd68 100644
--- a/lib/command_queue.h
+++ b/lib/command_queue.h
@@ -22,10 +22,12 @@
#ifndef COMMAND_QUEUE_H_
#define COMMAND_QUEUE_H_
-#include "command.h"
+#include "vty_local.h"
+#include "command_execute.h"
#include "qpnexus.h"
-extern void cq_enqueue(struct vty *vty, qpn_nexus dst) ;
-extern void cq_revoke(struct vty* vty) ;
+extern void cq_dispatch(vty vty, cmd_do_t to_do, qstring line) ;
+extern void cq_go_fetch(vty vty) ;
+extern void cq_revoke(vty vty) ;
#endif /* COMMAND_QUEUE_H_ */
diff --git a/lib/elstring.h b/lib/elstring.h
index 051e5962..8ab1c58d 100644
--- a/lib/elstring.h
+++ b/lib/elstring.h
@@ -23,8 +23,6 @@
#define _ZEBRA_ELSTRING_H
#include "misc.h"
-#include "zassert.h"
-#include "memory.h"
/*==============================================================================
* This is some very simple support for strings which are Length/Body
@@ -34,8 +32,8 @@
* is the simplest possible encapsulation of strings which are NOT '\0'
* terminated.
*
- *
- *
+ * NB: this object knows NOTHING about whether there is a '\0' beyond the
+ * 'len' of the string.
*/
struct elstring
{
@@ -46,7 +44,6 @@ struct elstring
} body ;
ulen len ;
- bool term ; /* true <=> body is '\0' terminated */
} ;
typedef struct elstring elstring_t[1] ;
@@ -60,15 +57,48 @@ enum
ELSTRING_INIT_ALL_ZEROS = true
} ;
-/*------------------------------------------------------------------------------
- * Various forms of body -- NB:
+/*==============================================================================
+ * Pointer pair and unsigned pointer pair and const versions.
*/
+struct pp
+{
+ char* p ;
+ char* e ;
+} ;
+typedef struct pp pp_t[1] ;
+typedef struct pp* pp ;
-Inline void*
-els_body_nn(elstring els)
+struct cpp
{
- return els->body.v ;
+ const char* p ;
+ const char* e ;
} ;
+typedef struct cpp cpp_t[1] ;
+typedef struct cpp* cpp ;
+
+/*==============================================================================
+ * NULLs for all types of pp and for els
+ */
+Inline void pp_null(pp p) { p->p = p->e = NULL ; } ;
+Inline void cpp_null(cpp p) { p->p = p->e = NULL ; } ;
+
+Inline void els_null(elstring els) { els->body.v = NULL ; els->len = 0 ; } ;
+
+/*==============================================================================
+ * Access functions.
+ */
+
+Inline void* els_body(elstring els) ;
+Inline void* els_body_nn(elstring els) ;
+Inline ulen els_len(elstring els) ;
+Inline ulen els_len_nn(elstring els) ;
+Inline void* els_end(elstring els) ;
+Inline void* els_end_nn(elstring els) ;
+
+Inline void els_pp(pp p, elstring els) ;
+Inline void els_pp_nn(pp p, elstring els) ;
+Inline void els_cpp(cpp p, elstring els) ;
+Inline void els_cpp_nn(cpp p, elstring els) ;
Inline void*
els_body(elstring els)
@@ -76,10 +106,10 @@ els_body(elstring els)
return (els != NULL) ? els_body_nn(els) : NULL ;
} ;
-Inline ulen
-els_len_nn(elstring els)
+Inline void*
+els_body_nn(elstring els)
{
- return els->len ;
+ return els->body.v ;
} ;
Inline ulen
@@ -88,38 +118,70 @@ els_len(elstring els)
return (els != NULL) ? els_len_nn(els) : 0 ;
} ;
-Inline bool
-els_term_nn(elstring els)
+Inline ulen
+els_len_nn(elstring els)
{
- return els->term ;
+ return els->len ;
} ;
-Inline bool
-els_term(elstring els)
+Inline void*
+els_end(elstring els)
{
- return (els != NULL) ? els_term_nn(els) : false ;
+ return (els != NULL) ? els_end_nn(els) : NULL ;
} ;
-/*==============================================================================
- * All so simple that everything is implemented as Inline
- */
+Inline void*
+els_end_nn(elstring els)
+{
+ return (void*)((char*)els->body.v + els->len);
+} ;
-/*------------------------------------------------------------------------------
- * Initialise or create a new elstring
- */
-Inline elstring
-els_init_new(elstring els)
+Inline void
+els_pp(pp p, elstring els)
{
- if (els == NULL)
- els = XCALLOC(MTYPE_TMP, sizeof(elstring_t)) ;
+ if (els != NULL)
+ els_pp_nn(p, els) ;
else
- memset(els, 0, sizeof(elstring_t)) ;
+ pp_null(p) ;
+} ;
- confirm(ELSTRING_INIT_ALL_ZEROS) ;
+Inline void
+els_pp_nn(pp p, elstring els)
+{
+ p->p = els->body.v ;
+ p->e = p->p + els->len ;
+} ;
- return els ;
+Inline void
+els_cpp(cpp p, elstring els)
+{
+ if (els != NULL)
+ els_cpp_nn(p, els) ;
+ else
+ cpp_null(p) ;
+} ;
+
+Inline void
+els_cpp_nn(cpp p, elstring els)
+{
+ p->p = els->body.cv ;
+ p->e = p->p + els->len ;
} ;
+/*==============================================================================
+ * All so simple that most is implemented as Inline
+ */
+
+extern elstring els_init_new(elstring els) ;
+extern elstring els_new(void) ;
+extern elstring els_free(elstring els) ;
+
+extern int els_cmp(elstring a, elstring b) ;
+extern int els_cmp_word(elstring a, const char* w) ;
+extern int els_cmp_sig(elstring a, elstring b) ;
+extern bool els_equal(elstring a, elstring b) ;
+extern bool els_substring(elstring a, elstring b) ;
+
/*------------------------------------------------------------------------------
* Set elstring value from ordinary string.
*
@@ -134,7 +196,6 @@ els_set_nn(elstring els, const void* str)
{
els->body.cv = str ;
els->len = (str != NULL) ? strlen(str) : 0 ;
- els->term = (str != NULL) ;
} ;
/*------------------------------------------------------------------------------
@@ -148,7 +209,7 @@ Inline elstring
els_set(elstring els, const void* str)
{
if (els == NULL)
- els = XCALLOC(MTYPE_TMP, sizeof(elstring_t)) ;
+ els = els_new() ;
els_set_nn(els, str) ;
@@ -169,7 +230,6 @@ els_set_n_nn(elstring els, const void* body, ulen len)
{
els->body.cv = body ;
els->len = len ;
- els->term = false ;
} ;
/*------------------------------------------------------------------------------
@@ -183,7 +243,7 @@ Inline elstring
els_set_n(elstring els, const void* body, ulen len)
{
if (els == NULL)
- els = XCALLOC(MTYPE_TMP, sizeof(elstring_t)) ;
+ els = els_new() ;
els_set_n_nn(els, body, len) ;
@@ -203,31 +263,13 @@ els_clear(elstring els)
{
els->body.v = NULL ;
els->len = 0 ;
- els->term = false ;
} ;
} ;
/*------------------------------------------------------------------------------
- * Release dynamically allocated elstring.
- *
- * Returns NULL.
- *
- * NB: it is the callers responsibility to free the contents of the elstring.
- * if that is required, before freeing the elstring itself.
- */
-Inline elstring
-els_free(elstring els)
-{
- if (els != NULL)
- XFREE(MTYPE_TMP, els) ;
-
- return NULL ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Set elstring length. And set term false.
+ * Set elstring 'len'.
*
- * NB: it is the caller's responsibility to set a valid length !!
+ * NB: it is the caller's responsibility to set a valid body !!
*
* NB: elstring MUST NOT be NULL.
*/
@@ -235,120 +277,21 @@ Inline void
els_set_len_nn(elstring els, ulen len)
{
els->len = len ;
- els->term = false ;
} ;
/*------------------------------------------------------------------------------
- * Set elstring body. And set term false.
+ * Set elstring body.
*
* NB: it is the caller's responsibility to set a valid body !!
*
+ * NB: it is the caller's responsibility to set a valid 'len'.
+ *
* NB: elstring MUST NOT be NULL.
*/
Inline void
els_set_body_nn(elstring els, const void* body)
{
els->body.cv = body ;
- els->term = false ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Set elstring terminated.
- *
- * NB: it is the caller's responsibility to set a valid body !!
- *
- * NB: elstring MUST NOT be NULL.
- */
-Inline void
-els_set_term_nn(elstring els, bool term)
-{
- els->term = term ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Compare two elstrings -- returns the usual -ve, 0, +ve cmp result.
- */
-Inline int
-els_cmp(elstring a, elstring b)
-{
- const uchar* ap ;
- const uchar* bp ;
- ulen al, bl ;
- ulen n ;
-
- ap = els_body(a) ;
- bp = els_body(b) ;
- al = els_len(a) ;
- bl = els_len(b) ;
-
- n = (al <= bl) ? al : bl ;
-
- while (n)
- {
- int d = *ap++ - *bp++ ;
- if (d != 0)
- return d ;
- --n ;
- } ;
-
- return al < bl ? -1 : (al == bl) ? 0 : +1 ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Are two elstrings equal ? -- returns true if strings equal.
- */
-Inline bool
-els_equal(elstring a, elstring b)
-{
- const uchar* ap ;
- const uchar* bp ;
- ulen n ;
-
- n = els_len(b) ;
- if (n != els_len(a))
- return false ;
-
- ap = els_body(a) ;
- bp = els_body(b) ;
-
- while (n)
- {
- if (*ap++ != *bp++)
- return false ;
- --n ;
- } ;
-
- return true ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Is 'b' a leading substring of 'a' ? -- returns true if it is.
- *
- * If 'b' is empty it is always a leading substring.
- */
-Inline int
-els_substring(elstring a, elstring b)
-{
- const uchar* ap ;
- const uchar* bp ;
- ulen n ;
-
- n = els_len(b) ;
- if (n > els_len(a))
- return false ;
-
- ap = els_body(a) ;
- bp = els_body(b) ;
-
- while (n)
- {
- if (*ap++ != *bp++)
- return false ;
- --n ;
- } ;
-
- return true ;
} ;
#endif /* _ZEBRA_ELSTRING_H */
-
diff --git a/lib/heap.c b/lib/heap.c
index 7821a9c9..708e88d0 100644
--- a/lib/heap.c
+++ b/lib/heap.c
@@ -19,9 +19,7 @@
* 02111-1307, USA.
*/
-#include <zebra.h>
-
-#include "zassert.h"
+#include "misc.h"
#include "heap.h"
#include "memory.h"
diff --git a/lib/if.c b/lib/if.c
index b1d0d49a..66a8bfef 100644
--- a/lib/if.c
+++ b/lib/if.c
@@ -556,7 +556,7 @@ DEFUN (interface,
#endif /* SUNOS_5 */
vty->index = ifp;
- vty_set_node(vty, INTERFACE_NODE) ;
+ vty->node = INTERFACE_NODE ;
return CMD_SUCCESS;
}
diff --git a/lib/if.h b/lib/if.h
index c99ab81b..b4b83cd5 100644
--- a/lib/if.h
+++ b/lib/if.h
@@ -300,12 +300,12 @@ extern char *if_indextoname (unsigned int, char *);
/* Exported variables. */
extern struct list *iflist;
-extern struct cmd_element interface_desc_cmd;
-extern struct cmd_element no_interface_desc_cmd;
-extern struct cmd_element interface_cmd;
-extern struct cmd_element no_interface_cmd;
-extern struct cmd_element interface_pseudo_cmd;
-extern struct cmd_element no_interface_pseudo_cmd;
-extern struct cmd_element show_address_cmd;
+extern struct cmd_command interface_desc_cmd;
+extern struct cmd_command no_interface_desc_cmd;
+extern struct cmd_command interface_cmd;
+extern struct cmd_command no_interface_cmd;
+extern struct cmd_command interface_pseudo_cmd;
+extern struct cmd_command no_interface_pseudo_cmd;
+extern struct cmd_command show_address_cmd;
#endif /* _ZEBRA_IF_H */
diff --git a/lib/keychain.c b/lib/keychain.c
index 2f8a0b77..57ccf7cc 100644
--- a/lib/keychain.c
+++ b/lib/keychain.c
@@ -238,7 +238,7 @@ DEFUN (key_chain,
keychain = keychain_get (argv[0]);
vty->index = keychain;
- vty_set_node(vty, KEYCHAIN_NODE) ;
+ vty->node = KEYCHAIN_NODE ;
return CMD_SUCCESS;
}
@@ -281,7 +281,7 @@ DEFUN (key,
VTY_GET_INTEGER ("key identifier", index, argv[0]);
key = key_get (keychain, index);
vty->index_sub = key;
- vty_set_node(vty, KEYCHAIN_KEY_NODE) ;
+ vty->node = KEYCHAIN_KEY_NODE ;
return CMD_SUCCESS;
}
@@ -309,7 +309,7 @@ DEFUN (no_key,
key_delete (keychain, key);
- vty_set_node(vty, KEYCHAIN_NODE) ;
+ vty->node = KEYCHAIN_NODE ;
return CMD_SUCCESS;
}
@@ -850,16 +850,20 @@ DEFUN (send_lifetime_duration_month_day,
static struct cmd_node keychain_node =
{
- KEYCHAIN_NODE,
- "%s(config-keychain)# ",
- 1
+ .node = KEYCHAIN_NODE,
+ .prompt ="%s(config-keychain)# ",
+
+ .config_to_vtysh = true
};
static struct cmd_node keychain_key_node =
{
- KEYCHAIN_KEY_NODE,
- "%s(config-keychain-key)# ",
- 1
+ .node = KEYCHAIN_KEY_NODE,
+ .prompt = "%s(config-keychain-key)# ",
+
+ .parent = KEYCHAIN_NODE,
+
+ .config_to_vtysh = true
};
static int
diff --git a/lib/keystroke.c b/lib/keystroke.c
index c6eb811e..13b79e39 100644
--- a/lib/keystroke.c
+++ b/lib/keystroke.c
@@ -272,7 +272,7 @@ struct keystroke_state
struct keystroke_stream
{
- vio_fifo_t fifo ; /* the keystrokes */
+ vio_fifo_t fifo ; /* the keystrokes -- embedded */
keystroke_callback* iac_callback ;
void* iac_callback_context ;
@@ -301,7 +301,8 @@ enum { keystroke_buffer_len = 2000 } ; /* should be plenty ! */
static void keystroke_in_push(keystroke_stream stream, uint8_t u) ;
static void keystroke_in_pop(keystroke_stream stream) ;
-inline static int keystroke_set_null(keystroke_stream stream, keystroke stroke);
+inline static bool keystroke_set_null(keystroke_stream stream,
+ keystroke stroke) ;
inline static uint8_t keystroke_get_byte(keystroke_stream stream) ;
inline static void keystroke_add_raw(keystroke_stream stream, uint8_t u) ;
static void keystroke_put_char(keystroke_stream stream, uint32_t u) ;
@@ -368,7 +369,7 @@ keystroke_stream_new(uint8_t csi_char, keystroke_callback* iac_callback,
stream->iac_callback = iac_callback ;
stream->iac_callback_context = iac_callback_context ;
- vio_fifo_init_new(&stream->fifo, keystroke_buffer_len) ;
+ vio_fifo_init_new(stream->fifo, keystroke_buffer_len) ;
stream->CSI = (csi_char != '\0') ? csi_char : 0x1B ;
@@ -385,7 +386,7 @@ keystroke_stream_free(keystroke_stream stream)
{
if (stream != NULL)
{
- vio_fifo_reset_keep(&stream->fifo) ;
+ vio_fifo_reset(stream->fifo, keep_it) ;
XFREE(MTYPE_KEY_STREAM, stream) ;
} ;
@@ -407,7 +408,7 @@ keystroke_stream_free(keystroke_stream stream)
extern bool
keystroke_stream_empty(keystroke_stream stream)
{
- return (stream == NULL) || vio_fifo_empty(&stream->fifo) ;
+ return (stream == NULL) || vio_fifo_empty(stream->fifo) ;
} ;
/*------------------------------------------------------------------------------
@@ -428,7 +429,7 @@ keystroke_stream_eof(keystroke_stream stream)
* is converted to a broken keystroke and placed in the stream.
* (So eof_met => no partial keystroke.)
*/
- return (stream == NULL) || (vio_fifo_empty(&stream->fifo) && stream->eof_met);
+ return (stream == NULL) || (vio_fifo_empty(stream->fifo) && stream->eof_met);
} ;
/*------------------------------------------------------------------------------
@@ -441,7 +442,7 @@ keystroke_stream_eof(keystroke_stream stream)
extern void
keystroke_stream_set_eof(keystroke_stream stream)
{
- vio_fifo_reset_keep(&stream->fifo) ;
+ vio_fifo_reset(stream->fifo, keep_it) ;
stream->eof_met = true ; /* essential information */
@@ -858,10 +859,10 @@ keystroke_in_pop(keystroke_stream stream)
/*==============================================================================
* Fetch next keystroke from keystroke stream
*
- * Returns: 1 => have a stroke type != ks_null
- * 0 => stroke type is ks_null (may be EOF).
+ * Returns: true => have a stroke type != ks_null
+ * false => stroke type is ks_null (may be EOF).
*/
-extern int
+extern bool
keystroke_get(keystroke_stream stream, keystroke stroke)
{
int b ;
@@ -869,7 +870,7 @@ keystroke_get(keystroke_stream stream, keystroke stroke)
uint8_t* e ;
/* Get first byte and deal with FIFO empty response */
- b = vio_fifo_get_byte(&stream->fifo) ;
+ b = vio_fifo_get_byte(stream->fifo) ;
if (b < 0)
return keystroke_set_null(stream, stroke) ;
@@ -884,7 +885,7 @@ keystroke_get(keystroke_stream stream, keystroke stroke)
stroke->len = 1 ;
stroke->buf[0] = b ;
- return 1 ;
+ return true ;
} ;
/* Sex the compound keystroke */
@@ -954,7 +955,7 @@ keystroke_get(keystroke_stream stream, keystroke stroke)
zabort("unknown keystroke type") ;
} ;
- return 1 ;
+ return true ;
} ;
/*------------------------------------------------------------------------------
@@ -962,7 +963,7 @@ keystroke_get(keystroke_stream stream, keystroke stroke)
*
* Returns: 0
*/
-inline static int
+inline static bool
keystroke_set_null(keystroke_stream stream, keystroke stroke)
{
stroke->type = ks_null ;
@@ -971,7 +972,7 @@ keystroke_set_null(keystroke_stream stream, keystroke stroke)
stroke->flags = 0 ;
stroke->len = 0 ;
- return 0 ;
+ return false ;
} ;
/*------------------------------------------------------------------------------
@@ -983,7 +984,7 @@ keystroke_set_null(keystroke_stream stream, keystroke stroke)
inline static uint8_t
keystroke_get_byte(keystroke_stream stream)
{
- int b = vio_fifo_get_byte(&stream->fifo) ;
+ int b = vio_fifo_get_byte(stream->fifo) ;
passert(b >= 0) ;
@@ -1013,7 +1014,7 @@ static void
keystroke_put_char(keystroke_stream stream, uint32_t u)
{
if (u < 0x80)
- vio_fifo_put_byte(&stream->fifo, (uint8_t)u) ;
+ vio_fifo_put_byte(stream->fifo, (uint8_t)u) ;
else
{
uint8_t buf[4] ;
@@ -1140,12 +1141,12 @@ keystroke_put(keystroke_stream stream, enum keystroke_type type, bool broken,
type |= kf_truncated ;
} ;
- vio_fifo_put_byte(&stream->fifo,
+ vio_fifo_put_byte(stream->fifo,
kf_compound | (broken ? kf_broken : 0) | type) ;
- vio_fifo_put_byte(&stream->fifo, len) ;
+ vio_fifo_put_byte(stream->fifo, len) ;
if (len > 0)
- vio_fifo_put(&stream->fifo, (void*)bytes, len) ;
+ vio_fifo_put_bytes(stream->fifo, (void*)bytes, len) ;
} ;
/*------------------------------------------------------------------------------
diff --git a/lib/keystroke.h b/lib/keystroke.h
index 10494df0..67c6cd0f 100644
--- a/lib/keystroke.h
+++ b/lib/keystroke.h
@@ -47,6 +47,7 @@ enum keystroke_type
ks_type_reserved = 0x0F,
} ;
+typedef enum keystroke_type keystroke_type ;
CONFIRM(ks_type_count <= ks_type_reserved) ;
enum keystroke_null
@@ -68,8 +69,9 @@ enum keystroke_flags
kf_type_mask = 0x0F, /* extraction of type */
} ;
+typedef enum keystroke_type keystroke_flags ;
-CONFIRM(ks_type_reserved == kf_type_mask) ;
+CONFIRM(ks_type_reserved == (keystroke_type)kf_type_mask) ;
typedef struct keystroke* keystroke ;
typedef struct keystroke_stream* keystroke_stream ;
@@ -182,7 +184,7 @@ keystroke_stream_eof(keystroke_stream stream) ;
extern void
keystroke_input(keystroke_stream stream, uint8_t* ptr, size_t len,
keystroke steal) ;
-extern int
+extern bool
keystroke_get(keystroke_stream stream, keystroke stroke) ;
#endif /* _ZEBRA_KEYSTROKE_H */
diff --git a/lib/list_util.c b/lib/list_util.c
index 8ce01c03..f73244a4 100644
--- a/lib/list_util.c
+++ b/lib/list_util.c
@@ -49,28 +49,28 @@
*
* Note again the cast to (void**).
*
- * Returns: 0 => OK -- removed item from list (OR item == NULL)
- * -1 => item not found on list
+ * Returns: true => removed item from list
+ * false => item not found on list (or item == NULL)
*/
-extern int
+extern bool
ssl_del_func(void* p_this, void* item, size_t link_offset)
{
void* this ;
if (item == NULL)
- return 0 ;
+ return false ;
while ((this = *(void**)p_this) != item)
{
if (this == NULL)
- return -1 ;
+ return false ;
p_this = _sl_p_next(this, link_offset) ;
} ;
*(void**)p_this = _sl_next(item, link_offset) ;
- return 0 ;
+ return true ;
} ;
/*==============================================================================
diff --git a/lib/list_util.h b/lib/list_util.h
index bd796779..f6a01d93 100644
--- a/lib/list_util.h
+++ b/lib/list_util.h
@@ -133,10 +133,10 @@ struct dl_void_base_pair base_pair(void*) ;
*
* ssl_del(base, item, next) -- delete from list
*
- * Treat as function returning int. Does nothing if the item is NULL.
+ * Treat as function returning bool. Does nothing if the item is NULL.
*
- * Returns: 0 => OK -- removed item from list (OR item == NULL)
- * -1 => item not found on list
+ * Returns: true => removed item from list
+ * false => item not found on list (or item was NULL)
*
* ssl_del_head(base, next) -- delete head of list
*
@@ -237,7 +237,7 @@ struct dl_void_base_pair base_pair(void*) ;
(base) = item ; \
} while (0)
-extern int ssl_del_func(void* p_this, void* obj, size_t link_offset)
+extern bool ssl_del_func(void* p_this, void* obj, size_t link_offset)
__attribute__((noinline)) ;
#define ssl_del(base, item, next) \
@@ -506,7 +506,7 @@ extern int ssl_del_func(void* p_this, void* obj, size_t link_offset)
*
* Treat as function returning void*.
*
- * Returns old head in dst and as return from "function".
+ * Returns old tail in dst and as return from "function".
*
* Returns NULL and sets dst == NULL if list is empty.
*
diff --git a/lib/log.c b/lib/log.c
index 72f967f6..4da1e5e8 100644
--- a/lib/log.c
+++ b/lib/log.c
@@ -26,9 +26,8 @@
#include "log.h"
#include "vty.h"
-#include "uty.h"
#include "memory.h"
-#include "command.h"
+#include "command_local.h"
#ifndef SUNOS_5
#include <sys/un.h>
#endif
diff --git a/lib/log.h b/lib/log.h
index 256d09a3..6f4c8265 100644
--- a/lib/log.h
+++ b/lib/log.h
@@ -26,6 +26,7 @@
#define _ZEBRA_LOG_H
#include <syslog.h>
+#include <stdio.h>
#include "vargs.h"
#include "pthread_safe.h"
@@ -109,13 +110,6 @@ extern struct zlog *openzlog (const char *progname, zlog_proto_t protocol,
/* Close zlog function. */
extern void closezlog (struct zlog *zl);
-/* GCC have printf type attribute check. */
-#ifdef __GNUC__
-#define PRINTF_ATTRIBUTE(a,b) __attribute__ ((__format__ (__printf__, a, b)))
-#else
-#define PRINTF_ATTRIBUTE(a,b)
-#endif /* __GNUC__ */
-
/* Generic function for zlog. */
extern void zlog (struct zlog *zl, int priority, const char *format, ...)
PRINTF_ATTRIBUTE(3, 4);
diff --git a/lib/memory.c b/lib/memory.c
index c13404d2..e15492c9 100644
--- a/lib/memory.c
+++ b/lib/memory.c
@@ -72,7 +72,7 @@ mem_tracker_zeroise(struct mem_tracker* mem)
memset(mem, 0, sizeof(struct mem_tracker)) ;
} ;
-#ifdef MEMORY_TRACKER
+#if MEMORY_TRACKER
#include "mem_tracker.c"
#endif
@@ -130,7 +130,7 @@ zmalloc (enum MTYPE mtype, size_t size MEMORY_TRACKER_NAME)
else
{
mstat.mt[mtype].alloc++;
-#ifdef MEMORY_TRACKER
+#if MEMORY_TRACKER
mem_md_malloc(mtype, memory, size, name) ;
#endif
UNLOCK ;
@@ -159,7 +159,7 @@ zcalloc (enum MTYPE mtype, size_t size MEMORY_TRACKER_NAME)
else
{
mstat.mt[mtype].alloc++;
-#ifdef MEMORY_TRACKER
+#if MEMORY_TRACKER
mem_md_malloc(mtype, memory, size, name) ;
#endif
UNLOCK ;
@@ -188,7 +188,7 @@ zrealloc (enum MTYPE mtype, void *ptr, size_t size MEMORY_TRACKER_NAME)
{
if (ptr == NULL)
mstat.mt[mtype].alloc++;
-#ifdef MEMORY_TRACKER
+#if MEMORY_TRACKER
mem_md_realloc(mtype, ptr, memory, size, name) ;
#endif
UNLOCK ;
@@ -210,7 +210,7 @@ zfree (enum MTYPE mtype, void *ptr)
assert(mstat.mt[mtype].alloc > 0) ;
mstat.mt[mtype].alloc--;
-#ifdef MEMORY_TRACKER
+#if MEMORY_TRACKER
mem_md_free(mtype, ptr) ;
#endif
@@ -230,7 +230,7 @@ zstrdup (enum MTYPE mtype, const char *str MEMORY_TRACKER_NAME)
LOCK ;
- dup = strdup (str);
+ dup = strdup (str ? str : "");
if (dup == NULL)
{
UNLOCK ;
@@ -239,7 +239,7 @@ zstrdup (enum MTYPE mtype, const char *str MEMORY_TRACKER_NAME)
else
{
mstat.mt[mtype].alloc++;
-#ifdef MEMORY_TRACKER
+#if MEMORY_TRACKER
mem_md_malloc(mtype, dup, strlen(str)+1, name) ;
#endif
UNLOCK ;
@@ -443,7 +443,7 @@ show_memory_type_vty (struct vty *vty, const char* name,
vty_out (vty, "-----------------------------%s", VTY_NEWLINE) ;
vty_out (vty, "%-30s:", name) ;
-#ifdef MEMORY_TRACKER
+#if MEMORY_TRACKER
show_memory_tracker_detail(vty, mt, alloc) ;
#else
vty_out (vty, " %10ld", alloc) ;
@@ -464,13 +464,13 @@ show_memory_vty (struct vty *vty, struct memory_list *m, struct mlist* ml,
struct mem_tracker mem_one ;
struct mem_tracker* mt ;
-#ifdef MEMORY_TRACKER
+#if MEMORY_TRACKER
struct mem_type_tracker mem_tt ;
#endif
LOCK ;
mst = mstat ;
-#ifdef MEMORY_TRACKER
+#if MEMORY_TRACKER
mem_tt = mem_type_tracker ;
#endif
UNLOCK ;
@@ -499,7 +499,7 @@ show_memory_vty (struct vty *vty, struct memory_list *m, struct mlist* ml,
else
{
alloc = mst.mt[m->index].alloc ;
-#ifdef MEMORY_TRACKER
+#if MEMORY_TRACKER
mt = &(mem_tt.mt[m->index]) ;
#else
mt = &mem_one ;
@@ -580,7 +580,7 @@ DEFUN_CALL (show_memory_summary,
"Memory statistics\n"
"Summary memory statistics\n")
{
-#ifdef MEMORY_TRACKER
+#if MEMORY_TRACKER
show_memory_tracker_summary(vty) ;
#else
long alloc = 0 ;
@@ -613,7 +613,7 @@ DEFUN_CALL (show_memory_all,
#ifdef HAVE_MALLINFO
needsep |= show_memory_mallinfo (vty);
#endif /* HAVE_MALLINFO */
-#ifdef MEMORY_TRACKER
+#if MEMORY_TRACKER
needsep |= show_memory_tracker_summary(vty) ;
#endif
diff --git a/lib/memory.h b/lib/memory.h
index 6cc95a5b..4fccb079 100644
--- a/lib/memory.h
+++ b/lib/memory.h
@@ -58,11 +58,21 @@ typedef enum MTYPE mtype_t ;
mtype_zstrdup (__FILE__, __LINE__, (mtype), (str))
#else
-#ifdef QDEBUG
-#define MEMORY_TRACKER 1
+#ifdef MEMORY_TRACKER /* Can be forced from outside */
+# if MEMORY_TRACKER
+# define MEMORY_TRACKER 1 /* Force 1 or 0 */
+#else
+# define MEMORY_TRACKER 0
+# endif
+#else
+# ifdef QDEBUG
+# define MEMORY_TRACKER 1 /* Follow QDEBUG */
+#else
+# define MEMORY_TRACKER 0
+# endif
#endif
-#ifdef MEMORY_TRACKER
+#if MEMORY_TRACKER
#define MEMORY_TRACKER_NAME , const char* name
#define MEMORY_TRACKER_FUNC , __func__
#else
diff --git a/lib/memtypes.c b/lib/memtypes.c
index e5a591d6..7e73e4c1 100644
--- a/lib/memtypes.c
+++ b/lib/memtypes.c
@@ -16,7 +16,8 @@ struct memory_list memory_list_lib[] =
{
{ MTYPE_TMP, "Temporary memory" },
{ MTYPE_STRING, "String (general)" },
- { MTYPE_STRVEC, "String vector" },
+ { MTYPE_CMD_ITEM, "Command item" },
+ { MTYPE_CMD_STRING, "Command string" },
{ MTYPE_VECTOR, "Vector structure" },
{ MTYPE_VECTOR_BODY, "Vector body" },
{ MTYPE_SYMBOL_TABLE, "Symbol Table structure" },
@@ -44,13 +45,14 @@ struct memory_list memory_list_lib[] =
{ MTYPE_QPN_NEXUS, "qtn nexus" },
{ MTYPE_TSD, "Thread specific data" },
{ MTYPE_VTY, "VTY" },
+ { MTYPE_CMD_EXEC, "Command exec context" },
{ MTYPE_CMD_PARSED, "Parsed command" },
{ MTYPE_TOKEN, "Command token" },
- { MTYPE_MARSHAL, "marshalled commands" },
{ MTYPE_VTY_OUT_BUF, "VTY output buffer" },
{ MTYPE_VTY_HIST, "VTY history" },
{ MTYPE_VTY_NAME, "VTY name" },
{ MTYPE_KEY_STREAM, "Keystroke Stream" },
+ { MTYPE_VTY_CLI, "VTY CLI" },
{ MTYPE_VIO_FIFO, "VTY IO FIFO" },
{ MTYPE_VIO_FIFO_LUMP, "VTY IO FIFO Lump" },
{ MTYPE_VIO_LC, "VTY IO Line Control" },
@@ -87,7 +89,6 @@ struct memory_list memory_list_lib[] =
{ MTYPE_ROUTE_MAP_RULE, "Route map rule" },
{ MTYPE_ROUTE_MAP_RULE_STR, "Route map rule str" },
{ MTYPE_ROUTE_MAP_COMPILED, "Route map compiled" },
- { MTYPE_DESC, "Command desc" },
{ MTYPE_KEY, "Key" },
{ MTYPE_KEYCHAIN, "Key chain" },
{ MTYPE_IF_RMAP, "Interface route map" },
diff --git a/lib/misc.h b/lib/misc.h
index 9437b617..1bd0680b 100644
--- a/lib/misc.h
+++ b/lib/misc.h
@@ -22,13 +22,16 @@
#ifndef _ZEBRA_MISC_H
#define _ZEBRA_MISC_H
+#include "zconfig.h"
+
/* Stuff which we generally expect to have */
-#include <stddef.h>
-#include <stdint.h>
#include <limits.h>
+#include <string.h>
+
#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
#include <stdlib.h>
-#include <string.h>
#include "zassert.h"
@@ -43,6 +46,9 @@
*/
#define Private extern
+/* For use in switch/case */
+#define fall_through
+
/* Other names of true/false */
enum on_off
{
@@ -67,14 +73,26 @@ enum free_keep
} ;
typedef enum free_keep free_keep_b ;
-/* We really want to be able to assume that an int is at least 32 bits */
-CONFIRM(UINT_MAX >= 0xFFFFFFFF) ;
+/* We really want to be able to assume that an int is at least 32 bits
+ * and that a long is at least 64 bits !
+ */
+CONFIRM(UINT_MAX >= 0xFFFFFFFF) ;
+CONFIRM(ULONG_MAX >= 0xFFFFFFFFFFFFFFFF) ;
/* Some useful shorthand */
typedef unsigned char byte ;
typedef unsigned char uchar ;
+
typedef unsigned int uint ;
typedef unsigned int usize ;
typedef unsigned int ulen ;
+typedef unsigned long ulong ;
+
+/* cvp == const void* -- ptr to constant void
+ * cvp* == void * const* -- ptr to ptr to constant void
+ * const cvp* == void const* const* -- ptr to constant ptr to constant void
+ */
+typedef const void* cvp ;
+
#endif /* _ZEBRA_MISC_H */
diff --git a/lib/mqueue.c b/lib/mqueue.c
index 8fa9fbd5..c7037a64 100644
--- a/lib/mqueue.c
+++ b/lib/mqueue.c
@@ -254,7 +254,7 @@ mqueue_init_new(mqueue_queue mq, enum mqueue_queue_type type)
extern void
mqueue_empty(mqueue_queue mq)
{
- mqueue_revoke(mq, NULL) ;
+ mqueue_revoke(mq, NULL, 0) ;
assert((mq->head == NULL) && (mq->count == 0)) ;
} ;
@@ -268,8 +268,11 @@ mqueue_empty(mqueue_queue mq)
* NB: there MUST NOT be ANY waiters !
*/
extern mqueue_queue
-mqueue_reset(mqueue_queue mq, int free_structure)
+mqueue_reset(mqueue_queue mq, free_keep_b free_structure)
{
+ if (mq == NULL)
+ return NULL ;
+
mqueue_empty(mq) ;
passert(mq->waiters == 0) ;
@@ -327,13 +330,10 @@ mqueue_local_init_new(mqueue_local_queue lmq)
*
* Dequeues entries and dispatches them "mqb_destroy", to empty the queue.
*
- * See: mqueue_local_reset_keep(lmq)
- * mqueue_local_reset_free(lmq)
- *
* Returns address of Local Message Queue
*/
extern mqueue_local_queue
-mqueue_local_reset(mqueue_local_queue lmq, int free_structure)
+mqueue_local_reset(mqueue_local_queue lmq, free_keep_b free_structure)
{
mqueue_block mqb ;
@@ -533,7 +533,7 @@ static void mqueue_dequeue_signal(mqueue_queue mq, mqueue_thread_signal mtsig) ;
* never be any waiters... so no kicking is ever done.
*/
extern void
-mqueue_enqueue(mqueue_queue mq, mqueue_block mqb, enum mqb_rank priority)
+mqueue_enqueue(mqueue_queue mq, mqueue_block mqb, mqb_rank_b priority)
{
if (mq == NULL)
return mqb_dispatch_destroy(mqb) ;
@@ -755,35 +755,41 @@ done:
*
* Revokes by calling mqb_dispatch_destroy().
*
- * During a revoke() operation more items may be enqueued, but no other mqueue
- * operations may be performed. Enqueued items may promptly be revoked, except
- * for priority items if the revoke operation has already moved past the last
- * priority item.
+ * NB: for safety, holds the queue locked for the duration of the revoke
+ * operation.
+ *
+ * If the destroy code can handle it, this means that can revoke stuff
+ * from one thread even though it is usually only dequeued by another.
+ *
+ * The danger is that if queues get very long, and many revokes happen,
+ * may (a) spend a lot of time scanning the message queue, which stops
+ * other threads as soon as they try to enqueue anything, and (b) if this
+ * happens a lot, could end up in an O(n^2) thing scanning the message
+ * queue once for each revoked object type.
*
* If mq is NULL, does nothing.
+ *
+ * If num > 0, stops after revoking that many messages.
+ *
+ * Returns: number of messages revoked.
*/
-extern void
-mqueue_revoke(mqueue_queue mq, void* arg0)
+extern int
+mqueue_revoke(mqueue_queue mq, void* arg0, int num)
{
mqueue_block mqb ;
mqueue_block prev ;
+ int did ;
if (mq == NULL)
- return ;
+ return 0 ;
qpt_mutex_lock(&mq->mutex) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+ did = 0 ;
prev = NULL ;
- while (1)
+ mqb = mq->head ;
+ while (mqb != NULL)
{
- if (prev == NULL)
- mqb = mq->head ;
- else
- mqb = prev->next ;
-
- if (mqb == NULL)
- break ;
-
if ((arg0 == NULL) || (arg0 == mqb->arg0))
{
assert(mq->count > 0) ;
@@ -800,16 +806,30 @@ mqueue_revoke(mqueue_queue mq, void* arg0)
mq->tail_priority = prev ;
--mq->count ;
+ ++did ;
+
+ mqb_dispatch_destroy(mqb) ;
- qpt_mutex_unlock(&mq->mutex) ;
- mqb_dispatch_destroy(mqb) ;
- qpt_mutex_lock(&mq->mutex) ;
+ if (num == 1)
+ break ;
+ else if (num > 1)
+ --num ;
+
+ if (prev == NULL)
+ mqb = mq->head ;
+ else
+ mqb = prev->next ;
}
else
- prev = mqb ;
+ {
+ prev = mqb ;
+ mqb = mqb->next ;
+ } ;
} ;
qpt_mutex_unlock(&mq->mutex) ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ return did ;
} ;
/*------------------------------------------------------------------------------
@@ -930,8 +950,11 @@ mqueue_thread_signal_init(mqueue_thread_signal mqt, qpt_thread_t thread,
* Otherwise zeroises the structure, and returns address of same.
*/
extern mqueue_thread_signal
-mqueue_thread_signal_reset(mqueue_thread_signal mqt, int free_structure)
+mqueue_thread_signal_reset(mqueue_thread_signal mqt, free_keep_b free_structure)
{
+ if (mqt == NULL)
+ return NULL ;
+
passert(mqt->prev == NULL) ;
if (free_structure)
diff --git a/lib/mqueue.h b/lib/mqueue.h
index 68dceb15..f33af564 100644
--- a/lib/mqueue.h
+++ b/lib/mqueue.h
@@ -191,83 +191,48 @@ struct mqueue_local_queue
* Functions
*/
-extern void
-mqueue_initialise(void) ;
-
-extern void
-mqueue_finish(void) ;
-
-extern mqueue_queue
-mqueue_init_new(mqueue_queue mq, enum mqueue_queue_type type) ;
-
-extern void
-mqueue_empty(mqueue_queue mq) ;
-
-extern mqueue_queue
-mqueue_reset(mqueue_queue mq, int free_structure) ;
-
-#define mqueue_reset_keep(mq) mqueue_reset(mq, 0)
-#define mqueue_reset_free(mq) mqueue_reset(mq, 1)
-
-extern mqueue_local_queue
-mqueue_local_init_new(mqueue_local_queue lmq) ;
-
-extern mqueue_local_queue
-mqueue_local_reset(mqueue_local_queue lmq, int free_structure) ;
-
-#define mqueue_local_reset_keep(lmq) mqueue_local_reset(lmq, 0)
-#define mqueue_local_reset_free(lmq) mqueue_local_reset(lmq, 1)
-
-extern void
-mqueue_set_timeout_interval(mqueue_queue mq, qtime_t interval) ;
-
-extern mqueue_thread_signal
-mqueue_thread_signal_init(mqueue_thread_signal mqt, qpt_thread_t thread,
- int signum) ;
-mqueue_thread_signal
-mqueue_thread_signal_reset(mqueue_thread_signal mqt, int free_structure) ;
-
-#define mqueue_thread_signal_reset_keep(mqt) mqueue_thread_signal_reset(mqt, 0)
-#define mqueue_thread_signal_reset_free(mqt) mqueue_thread_signal_reset(mqt, 1)
-
-extern mqueue_block
-mqb_init_new(mqueue_block mqb, mqueue_action action, void* arg0) ;
-
-extern mqueue_block
-mqb_re_init(mqueue_block mqb, mqueue_action action, void* arg0) ;
-
-extern void
-mqb_free(mqueue_block mqb) ;
+extern void mqueue_initialise(void) ;
+extern void mqueue_finish(void) ;
+
+extern mqueue_queue mqueue_init_new(mqueue_queue mq,
+ enum mqueue_queue_type type) ;
+extern void mqueue_empty(mqueue_queue mq) ;
+extern mqueue_queue mqueue_reset(mqueue_queue mq, free_keep_b free_structure) ;
+
+extern mqueue_local_queue mqueue_local_init_new(mqueue_local_queue lmq) ;
+extern mqueue_local_queue mqueue_local_reset(mqueue_local_queue lmq,
+ free_keep_b free_structure) ;
+
+extern void mqueue_set_timeout_interval(mqueue_queue mq, qtime_t interval) ;
+extern mqueue_thread_signal mqueue_thread_signal_init(mqueue_thread_signal mqt,
+ qpt_thread_t thread, int signum) ;
+mqueue_thread_signal mqueue_thread_signal_reset(mqueue_thread_signal mqt,
+ free_keep_b free_structure) ;
+
+extern mqueue_block mqb_init_new(mqueue_block mqb, mqueue_action action,
+ void* arg0) ;
+extern mqueue_block mqb_re_init(mqueue_block mqb, mqueue_action action,
+ void* arg0) ;
+extern void mqb_free(mqueue_block mqb) ;
enum mqb_rank
{
mqb_priority = true,
mqb_ordinary = false
} ;
+typedef enum mqb_rank mqb_rank_b ;
-extern void
-mqueue_enqueue(mqueue_queue mq, mqueue_block mqb, enum mqb_rank priority) ;
-
-extern mqueue_block
-mqueue_dequeue(mqueue_queue mq, int wait, void* arg) ;
+extern void mqueue_enqueue(mqueue_queue mq, mqueue_block mqb,
+ mqb_rank_b priority) ;
+extern mqueue_block mqueue_dequeue(mqueue_queue mq, int wait, void* arg) ;
+extern int mqueue_revoke(mqueue_queue mq, void* arg0, int num) ;
-extern void
-mqueue_revoke(mqueue_queue mq, void* arg0) ;
-
-extern int
-mqueue_done_waiting(mqueue_queue mq, mqueue_thread_signal mtsig) ;
-
-extern void
-mqueue_local_enqueue(mqueue_local_queue lmq, mqueue_block mqb) ;
-
-extern void
-mqueue_local_enqueue_head(mqueue_local_queue lmq, mqueue_block mqb) ;
-
-Inline mqueue_block
-mqueue_local_head(mqueue_local_queue lmq) ;
+extern int mqueue_done_waiting(mqueue_queue mq, mqueue_thread_signal mtsig) ;
-extern mqueue_block
-mqueue_local_dequeue(mqueue_local_queue lmq) ;
+extern void mqueue_local_enqueue(mqueue_local_queue lmq, mqueue_block mqb) ;
+extern void mqueue_local_enqueue_head(mqueue_local_queue lmq, mqueue_block mqb) ;
+Inline mqueue_block mqueue_local_head(mqueue_local_queue lmq) ;
+extern mqueue_block mqueue_local_dequeue(mqueue_local_queue lmq) ;
/*==============================================================================
* Access functions for mqueue_block fields -- mqb_set_xxx/mqb_get_xxx
diff --git a/lib/node_type.h b/lib/node_type.h
deleted file mode 100644
index b1f26187..00000000
--- a/lib/node_type.h
+++ /dev/null
@@ -1,82 +0,0 @@
-/* Command handler node_type stuff -- header
- * Copyright (C) 1997, 98 Kunihiro Ishiguro
- *
- * This file is part of GNU Zebra.
- *
- * GNU Zebra is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * GNU Zebra is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with GNU Zebra; see the file COPYING. If not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#ifndef _ZEBRA_NODE_TYPE_H
-#define _ZEBRA_NODE_TYPE_H
-
-/* There are some command levels which called from command node. */
-enum node_type
-{
- AUTH_NODE, /* Authentication mode of vty interface. */
- RESTRICTED_NODE, /* Restricted view mode */
- VIEW_NODE, /* View node. Default mode of vty interface. */
- AUTH_ENABLE_NODE, /* Authentication mode for change enable. */
- ENABLE_NODE, /* Enable node. */
-
- MIN_DO_SHORTCUT_NODE = ENABLE_NODE,
- /* May not "do xxx" at any node lower */
- MAX_NON_CONFIG_NODE = ENABLE_NODE,
- /* May not be higher than this without owning
- * the configuration symbol of power */
-
- CONFIG_NODE, /* Config node. Default mode of config file. */
-
- MIN_CONTEXT_NODE = CONFIG_NODE,
- /* May not change context to any node lower */
-
- SERVICE_NODE, /* Service node. */
- DEBUG_NODE, /* Debug node. */
- AAA_NODE, /* AAA node. */
- KEYCHAIN_NODE, /* Key-chain node. */
- KEYCHAIN_KEY_NODE, /* Key-chain key node. */
- INTERFACE_NODE, /* Interface mode node. */
- ZEBRA_NODE, /* zebra connection node. */
- TABLE_NODE, /* rtm_table selection node. */
- RIP_NODE, /* RIP protocol mode node. */
- RIPNG_NODE, /* RIPng protocol mode node. */
- BGP_NODE, /* BGP protocol mode which includes BGP4+ */
- BGP_VPNV4_NODE, /* BGP MPLS-VPN PE exchange. */
- BGP_IPV4_NODE, /* BGP IPv4 unicast address family. */
- BGP_IPV4M_NODE, /* BGP IPv4 multicast address family. */
- BGP_IPV6_NODE, /* BGP IPv6 address family */
- BGP_IPV6M_NODE, /* BGP IPv6 multicast address family. */
- OSPF_NODE, /* OSPF protocol mode */
- OSPF6_NODE, /* OSPF protocol for IPv6 mode */
- ISIS_NODE, /* ISIS protocol mode */
- MASC_NODE, /* MASC for multicast. */
- IRDP_NODE, /* ICMP Router Discovery Protocol mode. */
- IP_NODE, /* Static ip route node. */
- ACCESS_NODE, /* Access list node. */
- PREFIX_NODE, /* Prefix list node. */
- ACCESS_IPV6_NODE, /* Access list node. */
- PREFIX_IPV6_NODE, /* Prefix list node. */
- AS_LIST_NODE, /* AS list node. */
- COMMUNITY_LIST_NODE, /* Community list node. */
- RMAP_NODE, /* Route map node. */
- SMUX_NODE, /* SNMP configuration node. */
- DUMP_NODE, /* Packet dump node. */
- FORWARDING_NODE, /* IP forwarding node. */
- PROTOCOL_NODE, /* protocol filtering node */
- VTY_NODE, /* Vty node. */
-} ;
-typedef enum node_type node_type_t ;
-
-#endif /* _ZEBRA_NODE_TYPE_H */
diff --git a/lib/pthread_safe.c b/lib/pthread_safe.c
index c4ab9679..e2c7cdc0 100644
--- a/lib/pthread_safe.c
+++ b/lib/pthread_safe.c
@@ -299,7 +299,8 @@ errtox(strerror_t* st, int err, int len, int want)
if ((len + ql) <= qfs_left(&qfs)) /* accounting for "quotes" */
qfs_printf(&qfs, "%s%s%s", q, errm, q) ;
else
- qfs_printf(&qfs, "%s%.*s...%s", q, qfs_left(&qfs) - ql - 3, errm, q) ;
+ qfs_printf(&qfs, "%s%.*s...%s",
+ q, (int)(qfs_left(&qfs) - ql - 3), errm, q) ;
/* -ve precision is ignored ! */
} ;
diff --git a/lib/qfstring.c b/lib/qfstring.c
index 3db3fedf..f40f1417 100644
--- a/lib/qfstring.c
+++ b/lib/qfstring.c
@@ -26,10 +26,10 @@
*/
/*------------------------------------------------------------------------------
- * Initialise qf_str -- to given size (which includes the '\n')
+ * Initialise qf_str -- to given size (which includes the '\0')
*
* Sets pointers and terminates an empty string with one byte reserved for the
- * terminating '\n'.
+ * terminating '\0'.
*
* This operation is async-signal-safe.
*/
@@ -47,12 +47,12 @@ qfs_init(qf_str qfs, char* str, size_t size)
/*------------------------------------------------------------------------------
* Initialise qf_str which already contains string -- to given size (which
- * includes the '\n')
+ * includes the '\0')
*
* This may be used to prepare for appending to a buffer which already contains
* something.
*
- * Sets pointers, setting the write pointer to the existing terminating '\n'.
+ * Sets pointers, setting the write pointer to the existing terminating '\0'.
*
* This operation is async-signal-safe.
*/
@@ -331,7 +331,7 @@ qfs_pointer(qf_str qfs, void* p_val, enum pf_flags flags,
*
* If the precision is < 0 it is ignored (unless pf_hex, see below).
*
- * If the precision is 0 it is ignored unless bf_precision is set.
+ * If the precision is 0 it is ignored unless pf_precision is set.
*
* Precedence issues:
*
@@ -776,7 +776,7 @@ qfs_vprintf(qf_str qfs, const char *format, va_list args)
if (precision < 0)
{
precision = 0 ;
- flags &= ~pf_precision ;
+ flags &= ~pf_precision ; /* completely ignore */
} ;
}
else
@@ -885,7 +885,7 @@ qfs_vprintf(qf_str qfs, const char *format, va_list args)
* %s handler -- tolerates NULL pointer
*
* Accepts: width
- * precision
+ * precision -- ignored if < 0
* pf_precision -- explicit precision
*
* Rejects: pf_commas -- "'" seen
@@ -913,6 +913,12 @@ qfs_arg_string(qf_str qfs, va_list args, enum pf_flags flags,
if (flags != (flags & pf_precision))
return pfp_failed ;
+ if (precision < 0) /* make sure */
+ {
+ precision = 0 ;
+ flags &= ~pf_precision ;
+ } ;
+
len = (src != NULL) ? strlen(src) : 0 ;
if (((precision > 0) || (flags & pf_precision)) && (len > precision))
len = precision ;
diff --git a/lib/qiovec.c b/lib/qiovec.c
index 546dfcb0..38ea0dbb 100644
--- a/lib/qiovec.c
+++ b/lib/qiovec.c
@@ -18,16 +18,30 @@
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
+#include "zconfig.h" /* Otherwise IOV_MAX is not defined ! */
-#include "zebra.h"
+#include <errno.h>
+#include "misc.h"
#include "memory.h"
-#include "zassert.h"
#include "miyagi.h"
#include "qiovec.h"
/*==============================================================================
+ * IOV_MAX is defined by POSIX to appear in <limits.h>.
+ *
+ * This does not appear unless "zconfig.h" is included, first...
+ * ...but although it compiles OK, Eclipse cannot see where the value has
+ * come from.
+ */
+#ifdef IOV_MAX /* Stops Eclipse whinging */
+#if IOV_MAX < 64 /* check for a reasonable value */
+#error IOV_MAX < 64
+#endif
+#endif
+
+/*==============================================================================
* Initialise, allocate and reset qiovec
*/
diff --git a/lib/qiovec.h b/lib/qiovec.h
index f1498146..230198f3 100644
--- a/lib/qiovec.h
+++ b/lib/qiovec.h
@@ -22,9 +22,8 @@
#ifndef _ZEBRA_QIOVEC_H
#define _ZEBRA_QIOVEC_H
-#include "zebra.h"
-
#include "misc.h"
+#include <sys/uio.h> /* iovec stuff */
/*==============================================================================
* Flexible size "struct iovec"
diff --git a/lib/qpath.c b/lib/qpath.c
index f6157b89..07c294de 100644
--- a/lib/qpath.c
+++ b/lib/qpath.c
@@ -68,7 +68,7 @@ qpath_init_new(qpath qp, const char* path)
/* Worry about fields other than the path */
- qs_init_new(&qp->path, 0) ;
+ qs_init_new(qp->path, 0) ;
if (path != NULL)
qpath_set(qp, path) ;
@@ -119,7 +119,7 @@ qpath_set(qpath qp, const char* path)
if (path != NULL)
qs_set(&qp->path, path) ;
else
- qs_clear(&qp->path) ;
+ qs_clear(qp->path) ;
/* Worry about fields other than the path */
} ;
@@ -521,7 +521,7 @@ qpath_push_str(qpath qp, const char* path)
len = 0 ;
/* Worry about whether need to add a '/' to the path before pushing */
- sp = qs_chars(qs) ;
+ sp = qs_char(qs) ;
ep = qs_ep_char(qs) ; /* points at trailing '\0' */
if (sp == NULL)
diff --git a/lib/qpnexus.c b/lib/qpnexus.c
index 3b014be2..001d5049 100644
--- a/lib/qpnexus.c
+++ b/lib/qpnexus.c
@@ -18,9 +18,8 @@
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
-
-#include <zebra.h>
-#include <stdbool.h>
+#include "misc.h"
+#include <signal.h>
#include "qpnexus.h"
#include "memory.h"
@@ -91,7 +90,7 @@ qpn_add_hook_function(qpn_hook_list list, void* hook)
* object is empty or otherwise out of action.
*/
extern qpn_nexus
-qpn_reset(qpn_nexus qpn, bool free_structure)
+qpn_reset(qpn_nexus qpn, free_keep_b free_structure)
{
qps_file qf;
qtimer qtr;
@@ -115,11 +114,8 @@ qpn_reset(qpn_nexus qpn, bool free_structure)
qpn->selection = NULL ;
}
- if (qpn->queue != NULL)
- qpn->queue = mqueue_reset(qpn->queue, 1);
-
- if (qpn->mts != NULL)
- qpn->mts = mqueue_thread_signal_reset(qpn->mts, 1);
+ qpn->queue = mqueue_reset(qpn->queue, free_it);
+ qpn->mts = mqueue_thread_signal_reset(qpn->mts, free_it);
if (free_structure)
XFREE(MTYPE_QPN_NEXUS, qpn) ; /* sets qpn = NULL */
diff --git a/lib/qpnexus.h b/lib/qpnexus.h
index 03b52876..a6c8bf44 100644
--- a/lib/qpnexus.h
+++ b/lib/qpnexus.h
@@ -146,9 +146,6 @@ extern qpn_nexus qpn_init_new(qpn_nexus qpn, bool main_thread);
extern void qpn_add_hook_function(qpn_hook_list list, void* hook) ;
extern void qpn_exec(qpn_nexus qpn);
extern void qpn_terminate(qpn_nexus qpn);
-extern qpn_nexus qpn_reset(qpn_nexus qpn, bool free_structure);
-
-#define qpn_reset_free(qpn) qpn_reset(qpn, 1)
-#define qpn_reset_keep(qpn) qpn_reset(qpn, 0)
+extern qpn_nexus qpn_reset(qpn_nexus qpn, free_keep_b free_structure);
#endif /* _ZEBRA_QPNEXUS_H */
diff --git a/lib/qpselect.c b/lib/qpselect.c
index c15f112e..bcd2e2c9 100644
--- a/lib/qpselect.c
+++ b/lib/qpselect.c
@@ -29,13 +29,24 @@
#include "memory.h"
#include "vector.h"
-enum { qdebug =
-#ifdef QDEBUG
- 1
+/*------------------------------------------------------------------------------
+ * Sort out any debug setting
+ */
+#ifdef QPSELECT_DEBUG /* Can be forced from outside */
+# if QPSELECT_DEBUG
+# define QPSELECT_DEBUG 1 /* Force 1 or 0 */
+#else
+# define QPSELECT_DEBUG 0
+# endif
#else
- 0
+# ifdef QDEBUG
+# define QPSELECT_DEBUG 1 /* Follow QDEBUG */
+#else
+# define QPSELECT_DEBUG 0
+# endif
#endif
-};
+
+enum { qpselect_debug = QPSELECT_DEBUG } ;
/*==============================================================================
* Quagga pselect -- qps_xxxx
@@ -319,7 +330,7 @@ qps_pselect(qps_selection qps, qtime_t max_wait)
fd_set* p_fds[qps_mnum_count] ;
int n ;
- if (qdebug)
+ if (qpselect_debug)
qps_selection_validate(qps) ;
/* If there is stuff still pending, tidy up by zeroising the result */
@@ -415,7 +426,7 @@ qps_dispatch_next(qps_selection qps)
qps_file qf ;
qps_mnum_t mnum ;
- if (qdebug)
+ if (qpselect_debug)
qps_selection_validate(qps) ;
if (qps->pend_count == 0)
diff --git a/lib/qpthreads.h b/lib/qpthreads.h
index 959194ad..da6b2319 100644
--- a/lib/qpthreads.h
+++ b/lib/qpthreads.h
@@ -52,6 +52,22 @@
#error Require _POSIX_THREADS
#endif
+#ifdef QPTHREADS_DEBUG /* Can be forced from outside */
+# if QPTHREADS_DEBUG
+# define QPTHREADS_DEBUG 1 /* Force 1 or 0 */
+#else
+# define QPTHREADS_DEBUG 0
+# endif
+#else
+# ifdef QDEBUG
+# define QPTHREADS_DEBUG 1 /* Follow QDEBUG */
+#else
+# define QPTHREADS_DEBUG 0
+# endif
+#endif
+
+enum { qpthreads_debug = QPTHREADS_DEBUG } ;
+
/*==============================================================================
* Global Switch -- this allows the library to be run WITHOUT pthreads !
*
@@ -165,7 +181,7 @@ Inline bool qpt_thread_is_self(qpt_thread_t id)
*
* Quagga's default mutex type is:
*
- * * PTHREAD_MUTEX_ERRORCHECK unless NDEBUG && NDEBUG_QPTHREADS
+ * * PTHREAD_MUTEX_ERRORCHECK if QPTHREADS_DEBUG
* * QPT_MUTEX_TYPE_DEFAULT
*
* QPT_MUTEX_TYPE_DEFAULT may be set elsewhere. If it is not set then it is
@@ -200,10 +216,10 @@ enum qpt_mutex_options
# define QPT_MUTEX_TYPE_DEFAULT PTHREAD_MUTEX_NORMAL
#endif
-#if defined(NDEBUG) && defined(NDEBUG_QPTHREADS)
-# define QPT_MUTEX_TYPE QPT_MUTEX_TYPE_DEFAULT
-#else
+#if QPTHREADS_DEBUG
# define QPT_MUTEX_TYPE PTHREAD_MUTEX_ERRORCHECK
+#else
+# define QPT_MUTEX_TYPE QPT_MUTEX_TYPE_DEFAULT
#endif
extern qpt_mutex /* freezes qpthreads_enabled */
diff --git a/lib/qstring.c b/lib/qstring.c
index c7e5b81c..19f74e8c 100644
--- a/lib/qstring.c
+++ b/lib/qstring.c
@@ -19,13 +19,10 @@
* Boston, MA 02111-1307, USA.
*/
-#include <stdio.h>
-#include <ctype.h>
+#include "stdio.h"
#include "qstring.h"
-
#include "memory.h"
-#include "zassert.h"
/*==============================================================================
*/
@@ -38,15 +35,53 @@ qs_new(void)
{
/* zeroising sets a completely empty qstring -- see qs_init_new() */
return XCALLOC(MTYPE_QSTRING, sizeof(qstring_t)) ;
+
+ confirm(QSTRING_INIT_ALL_ZEROS) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Create a new body or extend existing one to accommodate at least slen + 1.
+ *
+ * Sets size to multiple of 8 (minimum 16), with at least 9 bytes free beyond
+ * the requested slen.
+ *
+ * Does not affect 'len' or 'cp'.
+ *
+ * If 'keep_alias', ONLY sets 'b_size' & 'b_body'.
+ *
+ * If !'keep_alias', sets the elstring body & size & clears 'alias'.
+ */
+static inline void
+qs_new_body(qstring qs, usize slen, bool keep_alias)
+{
+ qs->b_size = (slen + 0x10) & ~(usize)(0x08 - 1) ;
+ qs->b_body = XREALLOC(MTYPE_STRING, qs->b_body, qs->b_size) ;
+
+ if (!keep_alias)
+ qs_set_real_body_nn(qs) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Create a new, empty qs with body
+ */
+extern qstring
+qs_new_with_body(usize slen)
+{
+ qstring qs ;
+
+ qs = qs_new() ;
+ qs_new_body(qs, slen, false) ;
+
+ return qs ;
} ;
/*------------------------------------------------------------------------------
* Initialise qstring -- allocate if required.
*
- * If non-zero slen is given, a body is allocated (for at least slen + 1).
+ * If non-zero slen is given, a body is allocated (size = slen + 1).
* If zero slen is given, no body is allocated.
*
- * Sets qs->len = qs->cp = 0. '\0' terminates body if allocates one.
+ * Sets 'len' = 'cp' = 0.
*
* Returns: address of qstring
*
@@ -65,18 +100,20 @@ qs_init_new(qstring qs, usize slen)
/* Zeroising has set:
*
- * body = NULL -- no body
- * size = 0 -- no body
+ * empty elstring -- no body, 'len' = 0
+ *
+ * size = 0 -- no elstring body
*
- * len = 0
* cp = 0
*
- * b_body = NULL -- no body buffer
- * b_size = 0 -- no body buffer
+ * b_body = NULL -- no body buffer
+ * b_size = 0 -- no body buffer
+ *
+ * alias = false -- not an alias qstring
*/
if (slen != 0)
- qs_make_to_size(qs, slen, false) ;
+ qs_new_body(qs, slen, false) ;
return qs ;
} ;
@@ -104,90 +141,75 @@ qs_reset(qstring qs, free_keep_b free_structure)
XFREE(MTYPE_QSTRING, qs) ; /* sets qs = NULL */
else
memset(qs, 0, sizeof(qstring_t)) ; /* see qs_init_new */
+
+ confirm(QSTRING_INIT_ALL_ZEROS) ;
} ;
return qs ;
} ;
/*------------------------------------------------------------------------------
- * Allocate or reallocate body so that is big enough for the given "slen".
+ * Allocate or reallocate body so that is big enough for the given 'slen'.
*
- * If the qstring is currently an alias, copies all of the alias to a new
- * body -- so always returns a non-alias qstring.
+ * qstring may NOT be NULL.
*
- * Returns with a body with size > 0. Allocates to 16 byte boundaries with
- * space for '\0' beyond given length.
+ * Returns with a body with size > 0 (even if 'slen' == 0).
*
- * Does NOT affect qs->len or qs->cp. Does NOT re-terminate.
+ * Expects, but does not require, that 'slen' > 'size'.
*
- * NB: will allocate a new body even if the slen == 0.
+ * Does not change 'cp' or 'len' (even if 'len' > 'slen').
*
- * NB: always copies all of any aliased string (even if the slen == 0).
- *
- * NB: sets terminated false
+ * If is an alias, copies min('len', 'alen', 'slen') characters to the body.
*/
Private void
-qs_make_to_size(qstring qs, usize slen, bool keep)
+qs_make_to_size(qstring qs, usize slen, usize alen)
{
- usize size ;
- usize alen ;
+ /* If body needs to be extended, do that now */
+ if (slen >= qs->b_size)
+ qs_new_body(qs, slen, (qs->alias && (alen != 0))) ;
+ /* keeps alias if required */
/* Worry about alias. If we keep it, we keep all of it. */
- if (keep && (qs->size == 0))
+ if (qs->alias)
{
- alen = qs_len_nn(qs) ; /* alias stuff to keep */
- if (slen <= alen)
- slen = alen + 1 ; /* making sure can do that. */
- }
- else
- alen = 0 ; /* no alias stuff to keep */
+ /* alias or empty */
+ usize len = qs_len_nn(qs) ;
- /* Calculate the new size -- multiple of 16, >= 16. */
- size = (slen + 0x10) & ~(usize)(0x10 - 1) ;
- dassert(size != 0) ;
+ if (alen >= len)
+ alen = len ; /* keep min(alen, len) */
- /* If that requires the body to be extended, do that now */
- if (size > qs->b_size)
- {
- /* Need to allocate or extend the buffer.
- *
- * If current size is not zero, extend by doubling size or making at
- * least the multiple of 16 calculated for new len.
- */
- qs->b_size *= 2 ;
- if (qs->b_size < size)
- qs->b_size = size ;
- qs->b_body = XREALLOC(MTYPE_STRING, qs->b_body, qs->b_size) ;
- } ;
-
- /* If this is a non-empty alias, copy all or part of it. */
- if (alen != 0)
- memcpy(qs->b_body, qs_body_nn(qs), alen) ;
-
- /* Update body and size, and no longer known to be terminated */
- qs_set_body_nn(qs, qs->b_body) ;
- qs->size = qs->b_size ;
+ if (alen > slen)
+ alen = slen ; /* keep only to new len. */
+ if (alen != 0)
+ memcpy(qs->b_body, qs_body_nn(qs), alen) ;
+ qs_set_real_body_nn(qs) ;
+ } ;
} ;
/*==============================================================================
* Setting value of qstring
+ *
+ * Copy the given string to the qstring, allocating qstring and/or extending
+ * it as required.
+ *
+ * Any alias is simply discarded.
+ *
+ * Sets 'len' to new length
+ * Sets 'cp' = 0
+ *
+ * Returns: address of the qstring (allocated if required).
*/
/*------------------------------------------------------------------------------
* Set qstring to be copy of the given string.
*
- * Allocate qstring if required (setting qs->len = qs->cp = 0).
- *
- * Allocates a body and copies src to it, adding '\0'. Treats src == NULL as
- * an empty string.
- *
- * Sets qs->len to the length of the string (excluding trailing '\0').
- * Sets qs->cp == 0.
+ * Treats src == NULL as an empty string. Otherwise src must be a '\0'
+ * terminated string.
*
- * Returns: address of the qstring copied to.
+ * Does not copy or count the '\0' terminator.
*
- * NB: if copying to a dummy qstring, the old body is simply discarded.
+ * See notes above.
*/
extern qstring
qs_set(qstring qs, const char* src)
@@ -196,157 +218,202 @@ qs_set(qstring qs, const char* src)
} ;
/*------------------------------------------------------------------------------
- * Set qstring to be leading 'n' bytes of given string.
- *
- * Allocate qstring if required (setting qs->len = qs->cp = 0).
- *
- * Allocates a body and copies 'n' bytes from src to it, adding '\0'. The
- * src pointer is ignored if n == 0.
+ * Set qstring to be copy of leading 'n' bytes of given string.
*
- * Sets qs->len to the length of the string (excluding trailing '\0').
- * Sets qs->cp == 0.
+ * If n == 0, src is ignored (and may be NULL)
+ * If n > 0, src string MUST be at least 'n' bytes long.
*
- * Returns: address of the qstring copied to.
- *
- * NB: if n == 0, src may be NULL
- *
- * NB: if n > 0, src string MUST be at least 'n' bytes long.
- *
- * NB: if copying to a dummy qstring, the old body is simply discarded.
+ * See notes above.
*/
extern qstring
qs_set_n(qstring qs, const char* src, usize len)
{
- char* p ;
-
- qs = qs_new_len(qs, len) ; /* ensures have body > n */
-
- p = qs_char_nn(qs) ;
+ qs = qs_new_size(qs, len) ; /* ensures have body > len */
if (len != 0)
- memcpy(p, src, len) ;
-
- *(p + len) = '\0' ;
+ memcpy(qs_char_nn(qs), src, len) ;
- qs_set_term_nn(qs, true) ;
- qs->cp = 0 ;
+ qs_set_len_nn(qs, len) ;
+ qs_set_cp_nn(qs, 0) ;
return qs ;
} ;
/*------------------------------------------------------------------------------
- * Append given string to a qstring -- adding at qs->len position.
- *
- * Allocate qstring if required (setting qs->len = qs->cp = 0).
- *
- * Allocates or extends the body and copies bytes from src to it, adding '\0'.
- * Treats src == NULL as an empty string.
- *
- * Sets qs->len to the length of the result (excluding trailing '\0')
- * Does not change qs->cp.
+ * Set qstring to be copy of given qstring contents.
*
- * Returns: address of the qstring appended to.
+ * If the given qstring is an alias, then the contents of the alias are copied
+ * (so the result is not an alias). See qs_copy() for the alternative.
*
- * NB: if appending to a dummy qstring, the old body is copied first.
+ * See notes above -- and note that 'cp' is set to 0.
*/
-extern qstring qs_append(qstring qs, const char* src)
+extern qstring
+qs_set_qs(qstring qs, qstring src)
{
- return qs_append_n(qs, src, (src != NULL) ? strlen(src) : 0) ;
+ return qs_set_n(qs, qs_body(src), qs_len(src)) ;
} ;
/*------------------------------------------------------------------------------
- * Append leading 'n' bytes of given string to a qstring -- adding at qs->len
- * position.
- *
- * Allocate qstring if required (setting qs->len = qs->cp = 0).
+ * Set qstring to be copy of given elstring contents.
*
- * Allocates or extends the body and copies 'n' bytes from src to it,
- * adding '\0'. The src pointer is ignored if n == 0.
+ * See notes above.
+ */
+extern qstring
+qs_set_els(qstring qs, elstring src)
+{
+ return qs_set_n(qs, els_body(src), els_len(src)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set qstring with given pattern to given length.
*
- * Sets qs->len to the length of the result (excluding trailing '\0')
- * Does not change qs->cp.
+ * Repeats the given pattern as many times as necessary to get to the given
+ * length -- using a final partial piece of the pattern as required.
*
- * Returns: address of the qstring appended to.
+ * If the pattern is zero length, fills with spaces !
*
- * NB: if n == 0, src may be NULL
+ * See notes above.
+ */
+extern qstring
+qs_set_fill(qstring qs, usize len, const char* src)
+{
+ return qs_set_fill_n(qs, len, src, (src != NULL ? strlen(src) : 0)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set qstring with given pattern to given length.
*
- * NB: if n > 0, src string MUST be at least 'n' bytes long.
+ * Repeats the given pattern as many times as necessary to get to the given
+ * length -- using a final partial piece of the pattern as required.
*
- * NB: if appending to a dummy qstring, the old body is copied first.
+ * See notes above.
*/
extern qstring
-qs_append_n(qstring qs, const char* src, usize n)
+qs_set_fill_n(qstring qs, usize len, const char* src, usize flen)
{
- char* ep ;
+ char* p ;
+ char* q ;
+ usize left ;
- qs = qs_add_len(qs, n, &ep) ;
+ qs = qs_new_size(qs, len) ; /* ensures have body > len */
- if (n != 0)
- memcpy(ep, src, n) ;
+ if (len != 0)
+ {
+ if (flen == 0)
+ {
+ src = " " ;
+ flen = strlen(src) ;
+ } ;
+
+ if (len < flen)
+ flen = len ;
+
+ q = p = qs_char_nn(qs) ;
+ memcpy(p, src, flen) ;
+ p += flen ;
+ left = len - flen ;
+
+ while (left > 0)
+ {
+ if (left < flen)
+ flen = left ;
+
+ memcpy(p, q, flen) ;
+ p += flen ;
+ left -= flen ;
+
+ flen += flen ;
+ } ;
+ } ;
+
+ qs_set_len_nn(qs, len) ;
+ qs_set_cp_nn(qs, 0) ;
return qs ;
} ;
-/*------------------------------------------------------------------------------
- * Copy one qstring to another
+/*==============================================================================
+ * Appending to a qstring
*
- * If both are NULL, returns NULL.
+ * Copy the given string to the end of the given qstring (at 'len'),
+ * allocating qstring and/or extending it as required.
*
- * Otherwise if dst is NULL, creates a new qstring.
+ * If this is an alias, it is copied to before being appended to (even if
+ * appending nothing).
*
- * Sets dst: body = copy of src->len bytes of src->body -- '\0' terminated.
- * cp = src->cp
- * len = src->len
+ * Can append to NULL or empty qstring.
*
- * Where a NULL src has zero cp and len.
+ * Sets 'len' to new length.
+ * Does not affect 'cp'.
+ *
+ * Returns: address of the qstring (allocated if required).
+ */
+
+/*------------------------------------------------------------------------------
+ * Append given string to a qstring.
*
- * If not NULL, the destination is guaranteed to have a body, and that will be
- * '\0' terminated.
+ * Treats src == NULL as an empty string. Otherwise src must be a '\0'
+ * terminated string.
*
- * Returns: the destination qstring
+ * Does not copy or count the '\0' terminator.
*
- * NB: if copying to a dummy qstring, the old body is simply discarded.
+ * See notes above.
+ */
+extern qstring qs_append(qstring qs, const char* src)
+{
+ return qs_append_n(qs, src, (src != NULL) ? strlen(src) : 0) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Append leading 'n' bytes of given string to a qstring.
+ *
+ * If n == 0, src may be NULL
+ * If n > 0, src string MUST be at least 'n' bytes long.
+ *
+ * See notes above.
*/
extern qstring
-qs_copy(qstring dst, qstring src)
+qs_append_n(qstring qs, const char* src, usize n)
{
- usize n ;
+ qs = qs_extend(qs, n) ; /* allocate, copy any alias, extend body,
+ set new length, etc */
- if (src == NULL)
- {
- if (dst == NULL)
- return dst ;
+ if (n != 0)
+ memcpy(qs_ep_char_nn(qs) - n, src, n) ;
- n = 0 ;
- dst->cp = 0 ;
- }
- else
- {
- if (dst == NULL)
- dst = qs_new() ;
+ return qs ;
+} ;
- n = src->len ;
- dst->cp = src->cp ;
- } ;
+/*------------------------------------------------------------------------------
+ * Append given qstring to a qstring.
+ *
+ * See notes above.
+ */
+extern qstring
+qs_append_qs(qstring qs, qstring src)
+{
+ return qs_append_n(qs, qs_body(src), qs_len(src)) ;
+} ;
- qs_set_len(dst, n) ; /* TODO: Copies alias !! */
+/*------------------------------------------------------------------------------
+ * Append given elstring to a qstring.
+ *
+ * See notes above.
+ */
+extern qstring
+qs_append_els(qstring qs, elstring src)
+{
+ return qs_append_n(qs, els_body(src), els_len(src)) ;
+} ;
- if (n > 0)
- memcpy(dst->s.body, src->s.body, n) ;
- *(dst->s.char_body + n) = '\0' ;
- return dst ;
-} ;
-/*------------------------------------------------------------------------------
- * Construct a qstring which is an alias for the given string.
- *
- * Allocates a qstring if required.
- *
- * Given string must be '\0' terminated.
+/*==============================================================================
+ * Setting of alias.
*
* Does NOT copy the given string, but sets the qstring to be a pointer to it.
+ * This means that:
*
* NB: it is the caller's responsibility to ensure that the original string
* stays put for however long the qstring is an alias for it.
@@ -361,150 +428,147 @@ qs_copy(qstring dst, qstring src)
* use, the qstring must not be released, so that the alias is not
* released.
*
- * Returns: the address of the qstring.
+ * Preserves any existing qstring body.
+ *
+ * Returns: address of the qstring (allocated if required).
+ */
+
+/*------------------------------------------------------------------------------
+ * Set qstring to be an alias for the given string.
+ *
+ * Treats src == NULL as an empty string. Otherwise src must be a '\0'
+ * terminated string.
+ *
+ * Does not count the '\0' terminator.
+ *
+ * See notes above.
*/
extern qstring
qs_set_alias(qstring qs, const char* src)
{
+ return qs_set_alias_n(qs, src, (src != NULL) ? strlen(src) : 0) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set qstring to be an alias for the leading 'n' bytes of given string.
+ *
+ * If n == 0, src may be NULL
+ * If n > 0, src string MUST be at least 'n' bytes long.
+ *
+ * See notes above.
+ */
+extern qstring
+qs_set_alias_n(qstring qs, const char* src, usize n)
+{
if (qs == NULL)
- qs = qs_init_new(NULL, 0) ;
+ qs = qs_new() ;
/* Make the alias. Note that any existing b_body and b_size are preserved,
* so that any current body can be reused at a later date.
*/
- qs->s.const_body = (src != NULL) ? src : "" ;
- qs->len = strlen(src) ;
- qs->cp = 0 ;
- qs->size = 0 ; /* <=> this is an alias ! */
- qs->terminated = true ;
+ qs_set_body_nn(qs, (n != 0) ? src : "") ;
+ qs_set_len_nn(qs, n) ;
+ qs_set_cp_nn(qs, 0) ;
+ qs->alias = true ;
+ qs->size = 0 ; /* => empty or alias */
return qs ;
} ;
/*------------------------------------------------------------------------------
- * Construct a qstring which is an alias for the 'n' given characters.
- *
- * Allocates a qstring if required.
- *
- * Given characters are assumed not to be '\0' terminated.
+ * Set qstring to be an alias for the given qstring.
*
- * Does NOT copy the given characters, but sets the qstring to be a pointer to
- * them.
+ * If the src is not an alias, then the qstring is an alias for the body of
+ * src -- so must be careful not to disturb that !
*
- * NB: it is the caller's responsibility to ensure that the original characters
- * stays put for however long the qstring is an alias for them.
+ * If the src is an alias, then the qstring is another alias.
*
- * It is also the caller's responsibility to see that the original
- * characters are discarded as required (once the alias is no longer
- * required.)
- *
- * NB: if the qstring is changed in any way, a copy of the aliased characters
- * will be made first.
+ * See notes above.
+ */
+extern qstring
+qs_set_alias_qs(qstring qs, qstring src)
+{
+ return qs_set_alias_n(qs, qs_body(src), qs_len(src)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Construct a qstring which is an alias for the given elstring.
*
- * NB: if a pointer to the body of the qstring is taken, then while that is in
- * use, the qstring must not be released, so that the alias is not
- * released.
+ * If n == 0, src may be NULL
+ * If n > 0, src string MUST be at least 'n' bytes long.
*
- * Returns: the address of the qstring.
+ * See notes above.
*/
extern qstring
-qs_set_alias_n(qstring qs, const char* src, usize len)
+qs_set_alias_els(qstring qs, elstring src)
{
- if (qs == NULL)
- qs = qs_init_new(NULL, 0) ;
-
- if (len == 0)
- src = "" ;
- else
- assert(src != NULL) ;
-
- /* Make the alias. Note that any existing b_body and b_size are preserved,
- * so that any current body can be reused at a later date.
- */
- qs->s.const_body = src ;
- qs->len = len ;
- qs->cp = 0 ;
- qs->size = 0 ; /* <=> this is an alias ! */
- qs->terminated = false ;
-
- return qs ;
+ return qs_set_alias_n(qs, els_body(src), els_len(src)) ;
} ;
-
-
-
-
-
-
-
-
-
-
-
-
-
+/*==============================================================================
+ * Copying of qstring
+ */
/*------------------------------------------------------------------------------
- * Add 'n' to the current string length, allocating or extending the body.
+ * Copy one qstring to another -- allocating/extending as required.
*
- * Allocate qstring if required (setting qs->len = qs->cp = 0).
+ * If both are NULL, returns NULL.
+ * Otherwise if dst is NULL, creates a new qstring.
*
- * Returns with a body with size > 0. Allocates to 16 byte boundaries with
- * space for '\0' beyond given length.
+ * If src is NULL it is treated as zero length, with 'cp' == 0.
*
- * Does NOT affect qs->cp.
- * Does set the new qs->len -- qs->len += n
- * Does NOT reterminate.
+ * If src is not an alias, a copy is made to dst.
+ * If src is an alias, dst becomes another alias for the same thing.
*
- * Returns: address of qstring
+ * If dst is an alias, that is discarded.
*
- * also: sets char** p_ep to point at the *end* of the old len.
+ * Copies the src 'cp' to the dst.
*
- * NB: will allocate a new body even if the new len == 0.
- *
- * NB: always copies all of any aliased string (even if the slen == 0).
+ * Returns: the destination qstring (allocated if required).
*/
extern qstring
-qs_add_len(qstring qs, usize n, char** p_ep)
+qs_copy(qstring dst, qstring src)
{
- usize slen ;
- usize len ;
-
- len = qs_len(qs) ;
- slen = len + n ;
+ if (src == NULL)
+ {
+ qs_clear(dst) ; /* if dst not NULL, clear it */
+ return dst ;
+ } ;
- /* Set the new length -- creating if required.
- *
- * Will always return with a body and no longer an alias (if was one).
- */
- qs = qs_set_len(qs, slen) ;
+ if (src->alias)
+ dst = qs_set_alias_qs(dst, src) ;
+ else
+ dst = qs_set_qs(dst, src) ;
- /* Set pointer to old end (len) position. */
- *p_ep = ((char*)qs_body_nn(qs)) + len ;
+ qs_set_cp_nn(dst, qs_cp_nn(src)) ; /* copy in the src cp. */
- return qs ;
+ return dst ;
} ;
/*==============================================================================
* printf() and vprintf() type functions
- */
-
-/*------------------------------------------------------------------------------
- * Formatted print to qstring -- cf printf()
*
- * Allocate qstring if required (setting qs->len = qs->cp = 0).
+ * Allocate and/or extend qstring as required.
*
- * If OK:
+ * Any alias is discarded.
*
- * Sets qs->len to the length of the null terminated result.
- * Does NOT affect qs->cp.
+ * Sets 'len' = length of the '\0' terminated result (less the '\0').
+ * Sets 'cp' = 0
*
- * If fails:
+ * If fails and qs != NULL:
*
- * Sets qs->len = qs->cp = 0 and terminates to zero length.
+ * Sets 'len' = 0
+ * Sets 'cp' = 0
+ * But retains any existing body.
*
* Returns: address of qstring if OK
- * NULL if failed (unlikely though that is) -- qstring set empty.
+ * NULL if failed (unlikely though that is)
+ */
+
+/*------------------------------------------------------------------------------
+ * Formatted print to qstring -- cf printf()
+ *
+ * See notes above.
*/
extern qstring
qs_printf(qstring qs, const char* format, ...)
@@ -521,19 +585,7 @@ qs_printf(qstring qs, const char* format, ...)
/*------------------------------------------------------------------------------
* Formatted print to qstring -- cf vprintf()
*
- * Allocate qstring if required (setting qs->len = qs->cp = 0).
- *
- * If OK:
- *
- * Sets qs->len to the length of the null terminated result.
- * Does NOT affect qs->cp.
- *
- * If fails:
- *
- * Sets qs->len = qs->cp = 0 and terminates to zero length.
- *
- * Returns: address of qstring if OK
- * NULL if failed (unlikely though that is)
+ * See notes above.
*/
extern qstring
qs_vprintf(qstring qs, const char *format, va_list args)
@@ -542,11 +594,12 @@ qs_vprintf(qstring qs, const char *format, va_list args)
int slen ;
qstring qqs ;
- qqs = qs ;
+ qqs = qs ; /* NULL => need to make qs */
if (qs == NULL)
- qs = qs_new() ; /* sets size == 0 */
+ qqs = qs_new() ; /* Sets size, cp & len = 0 */
else
- qs_set_len_nn(qs, 0) ; /* Forget current contents */
+ qs_clear(qqs) ; /* Sets cp & len = 0, discard any alias, but
+ keep existing body */
while (1)
{
@@ -558,25 +611,24 @@ qs_vprintf(qstring qs, const char *format, va_list args)
* the result is still the length required.
*/
va_copy(ac, args);
- slen = vsnprintf (qs_body_nn(qs), qs->size, format, ac) ;
+ slen = vsnprintf(qs_body_nn(qqs), qqs->size, format, ac) ;
va_end(ac);
if (slen < 0)
- break ; /* Quit if failed */
+ break ; /* failed */
- if ((usize)slen < qs->size)
+ if ((usize)slen < qqs->size)
{
- qs_set_len_nn(qs, slen) ;
- return qs ; /* Exit if succeeded */
+ qs_set_len_nn(qqs, slen) ;
+ return qqs ; /* succeeded */
} ;
- qs_make_to_size(qs, slen) ; /* Extend body to required len */
+ qs_make_to_size(qqs, slen, 0) ; /* need space for slen */
} ;
- if (qqs == NULL)
- qs_reset(qs, free_it) ; /* discard what was allocated */
- else
- qs_clear(qs) ;
+ /* Failed... discard anything that has been allocated. */
+ if (qs == NULL)
+ qs_reset(qqs, free_it) ;
return NULL ;
} ;
@@ -586,233 +638,64 @@ qs_vprintf(qstring qs, const char *format, va_list args)
*/
/*------------------------------------------------------------------------------
- * Compare significant parts of two qstrings.
+ * Replace 'r' bytes at 'cp' by 'n' bytes -- extending if required.
*
- * By significant, mean excluding leading/trailing isspace() and treating
- * multiple isspace() as single isspace().
+ * May increase or decrease 'len'. but does not affect 'cp'.
*
- * Compares the 'len' portions of the strings.
+ * Returns: number of bytes beyond 'cp' that now exist.
*
- * If either is NULL, it is deemed to be an empty string.
+ * qstring MUST NOT be NULL
*
- * Returns: -1 => a < b
- * 0 => a == b
- * +1 => a > b
- */
-extern int
-qs_cmp_sig(qstring a, qstring b)
-{
- const unsigned char* p_a ;
- const unsigned char* e_a ;
- const unsigned char* p_b ;
- const unsigned char* e_b ;
-
- /* Set up pointers and dispense with leading and trailing isspace()
- *
- * Dummy up if NULL
- */
- if (a != NULL)
- {
- p_a = a->s.uchar_body ;
- e_a = p_a + a->len ;
-
- while ((p_a < e_a) && isspace(*p_a))
- ++p_a ;
- while ((p_a < e_a) && isspace(*(e_a - 1)))
- --e_a ;
- }
- else
- {
- p_a = NULL ;
- e_a = NULL ;
- }
-
- if (b != NULL)
- {
- p_b = b->s.uchar_body ;
- e_b = p_b + b->len ;
-
- while ((p_b < e_b) && isspace(*p_b))
- ++p_b ;
- while ((p_b < e_b) && isspace(*(e_b - 1)))
- --e_b ;
- }
- else
- {
- p_b = NULL ;
- e_b = NULL ;
- } ;
-
- /* Now set about finding the first difference */
- while ((p_a != e_a) && (p_b != e_b))
- {
- if (isspace(*p_a) && isspace(*p_b))
- {
- do { ++p_a ; } while (isspace(*p_a)) ;
- do { ++p_b ; } while (isspace(*p_b)) ;
- } ;
-
- if (*p_a != *p_b)
- return (*p_a < *p_b) ? -1 : +1 ;
-
- ++p_a ;
- ++p_b ;
- } ;
-
- /* No difference before ran out of one or both */
- if (p_a != e_a)
- return +1 ;
- else if (p_b != e_b)
- return -1 ;
- else
- return 0 ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Insert 'n' bytes at 'cp' -- moves anything cp..len up.
- *
- * Increases 'len'. but does not affect 'cp'.
- *
- * Returns: number of bytes beyond 'cp' that were moved before insert.
- *
- * NB: qstring MUST NOT be NULL
- *
- * NB: if 'cp' > 'len', then sets 'len' = 'cp' first -- which will introduce
- * one or more undefined bytes.
- *
- * NB: the string is NOT re-terminated.
- *
- * NB: if this is a aliased qstring, a copy is made of the original body.
- */
-extern usize
-qs_insert(qstring qs, const void* src, usize n)
-{
- usize after ;
- usize len ;
- char* p ;
-
- qs->terminated = false ; /* NB: require qs != NULL ! */
-
- len = qs_len_nn(qs) ;
- if (len < qs->cp) /* make len = max(len, cp) ! */
- len = qs->cp ;
-
- after = len - qs->cp ;
-
- qs_set_len(qs, len + n) ; /* set len and ensure have space
- Makes copy of any aliased string. */
- p = qs_cp_char(qs) ;
- if (after > 0)
- memmove (p + n, p, after) ;
-
- if (n > 0)
- memmove(p, src, n) ;
-
- return after ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Replace 'n' bytes at 'cp' -- extending if required.
- *
- * May increase 'len'. but does not affect 'cp'.
+ * If 'cp' > 'len', then sets 'len' = 'cp' first -- which will introduce
+ * one or more undefined bytes.
*
- * NB: qstring MUST NOT be NULL
- *
- * NB: if 'cp' > 'len', then sets 'len' = 'cp' first -- which will introduce
- * one or more undefined bytes.
- *
- * NB: the string is NOT re-terminated.
- *
- * NB: if this is a aliased qstring, a copy is made of the original body.
- */
-extern void
-qs_replace(qstring qs, const void* src, usize n)
-{
- usize len ;
-
- qs->terminated = false ; /* NB: require qs != NULL ! */
-
- len = qs_len_nn(qs) ;
- if (len < (qs->cp + n)) /* make len = max(len, cp + n) */
- len = qs->cp + n ;
-
- qs_set_len(qs, len) ; /* set len and ensure have space.
- Makes copy of any aliased string. */
-
- if (n > 0)
- memmove(qs_cp_char(qs), src, n) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Remove 'n' bytes at 'cp' -- extending if required.
- *
- * May change 'len'. but does not affect 'cp'.
- *
- * Returns: number of bytes beyond 'cp' that were moved before insert.
- *
- * NB: qstring MUST NOT be NULL
- *
- * NB: if 'cp' > 'len', then sets 'len' = 'cp' first -- which will introduce
- * one or more undefined bytes.
- *
- * NB: the string is NOT re-terminated.
- *
- * NB: if this is a aliased qstring, a copy is made of the original body.
+ * If this is a aliased qstring, a copy is made, so is no longer an alias.
*/
extern usize
-qs_delete(qstring qs, usize n)
+qs_replace(qstring qs, usize r, const void* src, usize n)
{
- usize after ;
- char* p ;
- usize len ;
+ usize cp, len, nlen, after ;
+ const char* ap ;
+ char* np ;
- qs->terminated = false ; /* NB: require qs != NULL ! */
+ len = qs_len_nn(qs) ;
+ cp = qs_cp_nn(qs) ;
- len = qs_len_nn(qs) ;
-
- /* If deleting to or beyond 'len', force len to cp */
- if ((qs->cp + n) >= len)
- {
- len = qs->cp ;
- qs_set_len_nn(qs, len) ; /* truncate now, so that if this is an
- aliased string, only copy what is
- going to be kept. */
- after = 0 ; /* nothing to move */
- }
+ if ((cp + r) >= len)
+ /* Replacing up to or beyond the end of the string */
+ after = 0 ;
else
- after = len - (qs->cp + n) ;
+ /* Replacing section short of the end of the string */
+ after = len - (cp + r) ;
- qs_set_len(qs, len) ; /* set len and ensure have space.
- Makes copy of any aliased string. */
+ nlen = cp + n + after ;
+ if (nlen >= qs->b_size)
+ qs_new_body(qs, nlen, qs->alias) ; /* keeping any alias */
+ ap = np = qs->b_body ;
+ if (qs->alias)
+ {
+ ap = qs_body_nn(qs) ; /* copy from the alias */
- /* Watch out for "dummy" */
- if (qs->size == 0)
- qs_make_to_size(qs, len) ;
+ uint before = cp ;
+ if (before > len)
+ before = len ;
- /* If deleting up to or beyond len, then simply set len == cp
- * note that this may reduce or increase len !
- */
- if ((qs->cp + n) >= len)
- {
- if (qs->cp < len)
- qs_set_len_nn(qs, qs->cp) ; /* discard stuff after qs->cp */
+ if (before > 0)
+ memmove(np, ap, before) ;
- qs_set_len(qs, qs->cp) ; /* set len */
- return 0 ; /* nothing after */
- }
+ qs_set_real_body_nn(qs) ;
+ } ;
- /* There is at least one byte after cp (so body must exist) */
- after = len - (qs->cp + n) ;
+ if (after > 0) /* move the after part before inserting */
+ memmove(np + cp + n, ap + cp + r, after) ;
- if (n > 0)
- {
- p = qs_cp_char(qs) ;
- memmove (p, p + n, after) ;
+ if (n > 0) /* insert */
+ memmove(np + cp, src, n) ;
- qs_set_len_nn(qs, len - n) ;
- } ;
+ /* Set new 'len' */
+ qs_set_len_nn(qs, nlen) ;
- return after ;
+ return n + after ;
} ;
diff --git a/lib/qstring.h b/lib/qstring.h
index 5b1d4932..48124a2d 100644
--- a/lib/qstring.h
+++ b/lib/qstring.h
@@ -38,8 +38,21 @@
* The caller does, however, have to explicitly release the contents of a
* qstring when it is done with.
*
- *
- *
+ * The mechanics of a qstring work on a length/pointer basis. The body of a
+ * qstring is not guaranteed to be '\0' terminated, but when memory is
+ * allocated provision is always made for a terminator beyond the 'len'.
+ * The qs_string() function will add a '\0' at the 'len' position.
+ *
+ * The body of a qstring is allocated and extended automatically as the length
+ * or the size is set. The address of the body can, therefore, change -- so
+ * should be careful when handling pointers to the body. The qstring handling
+ * tends to hold on to any body that has been allocated -- only qs_reset() will
+ * free the body.
+ *
+ * The qstring supports an "alias" state. A qstring can be set to be an
+ * "alias" for another length/pointer string, and at some later date that
+ * can be copied to the qstring body -- to be changed or for any other
+ * reason.
*/
struct qstring
{
@@ -47,10 +60,12 @@ struct qstring
usize size ; /* of the els body */
- usize cp ;
+ ulen cp ;
- usize b_size ;
void* b_body ;
+ usize b_size ;
+
+ bool alias ;
} ;
typedef struct qstring qstring_t[1] ;
@@ -68,111 +83,295 @@ enum
/*------------------------------------------------------------------------------
* Access functions for body of qstring -- to take care of casting pointers
*
- * NB: if the body has not yet been allocated, these functions will return
- * NULL or NULL + the offset.
+ * There are generally two versions of each function:
+ *
+ * xxxx_nn -- where the argument may NOT be NULL
+ *
+ * xxxx -- where a NULL or zero value is returned if the argument
+ * is NULL
+ *
+ * NB: the various 'cp', 'len' and 'at' functions do not guarantee that these
+ * positions exist within the current body of the string.
+ *
*/
+
+Inline elstring qs_els(qstring qs) ;
+Inline elstring qs_els_nn(qstring qs) ;
+Inline void qs_els_copy(elstring els, qstring qs) ;
+Inline void qs_els_copy_nn(elstring els, qstring qs) ;
+
+Inline char* qs_char(qstring qs) ;
+Inline char* qs_char_nn(qstring qs) ;
+
+Inline char* qs_cp_char(qstring qs) ;
+Inline char* qs_cp_char_nn(qstring qs) ;
+
+Inline char* qs_ep_char(qstring qs) ;
+Inline char* qs_ep_char_nn(qstring qs) ;
+
+Inline char* qs_char_at(qstring qs, usize off) ;
+Inline char* qs_char_at_nn(qstring qs, usize off) ;
+
+Inline void* qs_body(qstring qs) ;
+Inline void* qs_body_nn(qstring qs) ;
+Inline void qs_set_body_nn(qstring qs, const void* body) ;
+
+Inline ulen qs_len(qstring qs) ;
+Inline ulen qs_len_nn(qstring qs) ;
+Inline void qs_set_len_nn(qstring qs, ulen len) ;
+
+Inline ulen qs_cp(qstring qs) ;
+Inline ulen qs_cp_nn(qstring qs) ;
+Inline void qs_set_cp_nn(qstring qs, usize cp) ;
+Inline void qs_move_cp_nn(qstring qs, int delta) ;
+
+Inline ulen qs_after_cp(qstring qs) ;
+Inline ulen qs_after_cp_nn(qstring qs) ;
+
+Inline void qs_pp(pp p, qstring qs) ;
+Inline void qs_pp_nn(pp p, qstring qs) ;
+
+Inline void qs_cpp(cpp p, qstring qs) ;
+Inline void qs_cpp_nn(cpp p, qstring qs) ;
+
+/*------------------------------------------------------------------------------
+ * Functions to fetch the elstring body of the qstring.
+ */
+
+/* Pointer to elstring of qstring -- returns NULL if qstring is NULL */
Inline elstring
-qs_els_nn(qstring qs)
+qs_els(qstring qs)
{
- return qs->els ;
+ return (qs != NULL) ? qs->els : NULL ;
} ;
+/* Pointer to elstring of qstring (not NULL) */
Inline elstring
-qs_els(qstring qs)
+qs_els_nn(qstring qs)
{
return qs->els ;
} ;
-Inline void*
-qs_body_nn(qstring qs) /* pointer to body of qstring (not NULL) */
+/* Copy elstring of qstring to another elstring (elstring not NULL) */
+Inline void
+qs_els_copy(elstring els, qstring qs)
{
- return els_body_nn(qs->els) ;
+ if (qs != NULL)
+ qs_els_copy_nn(els, qs) ;
+ else
+ els_null(els) ;
+} ;
+
+/* Copy elstring of qstring to another elstring (neither NULL) */
+Inline void
+qs_els_copy_nn(elstring els, qstring qs)
+{
+ *els = *(qs->els) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Functions to fetch pointers to or into the string body.
+ *
+ * NB: these pointers must be treated with care -- and change to the string
+ * may invalidate the pointer. Where the qstring is an alias, the pointer
+ * returned is the alias -- which could disappear !
+ */
+
+/* Start of qstring -- returns NULL if qstring is NULL, or body is. */
+Inline char*
+qs_char(qstring qs)
+{
+ return qs_body(qs) ;
} ;
-Inline void* /* pointer to body of qstring */
+/* Start of qstring (not NULL)-- returns NULL if body is NULL. */
+Inline char*
+qs_char_nn(qstring qs)
+{
+ return qs_body_nn(qs) ;
+} ;
+
+/* Offset in qstring -- returns NULL if qstring is NULL
+ * returns *nonsense if body is NULL */
+Inline char*
+qs_char_at(qstring qs, usize off)
+{
+ return (qs != NULL) ? qs_char_at_nn(qs, off) : NULL ;
+} ;
+
+/* Offset in qstring (not NULL) -- returns *nonsense if body is NULL */
+Inline char*
+qs_char_at_nn(qstring qs, usize off)
+{
+ return qs_char_nn(qs) + off ;
+} ;
+
+/* 'cp' in qstring -- returns NULL if qstring is NULL
+ * returns *nonsense if body is NULL */
+Inline char*
+qs_cp_char(qstring qs)
+{
+ return (qs != NULL) ? qs_cp_char_nn(qs) : NULL ;
+} ;
+
+/* 'cp' in qstring (not NULL) -- returns *nonsense if body is NULL */
+Inline char*
+qs_cp_char_nn(qstring qs)
+{
+ return qs_char_at_nn(qs, qs_cp_nn(qs)) ;
+} ;
+
+/* 'len' in qstring -- returns NULL if qstring is NULL
+ * returns *nonsense if body is NULL */
+Inline char*
+qs_ep_char(qstring qs)
+{
+ return (qs != NULL) ? qs_ep_char_nn(qs) : NULL ;
+} ;
+
+/* 'len' in qstring (not NULL) -- returns *nonsense if body is NULL */
+Inline char*
+qs_ep_char_nn(qstring qs)
+{
+ return qs_char_at_nn(qs, qs_len_nn(qs)) ;
+} ;
+
+/* Start of qstring -- returns NULL if qstring is NULL, or body is. */
+Inline void*
qs_body(qstring qs)
{
return (qs != NULL) ? qs_body_nn(qs) : NULL ;
} ;
-Inline void /* set pointer to body of qstring (not NULL) */
-qs_set_body_nn(qstring qs, const void* body)
+/* Start of qstring (not NULL)-- returns NULL if body is NULL. */
+Inline void*
+qs_body_nn(qstring qs)
{
- els_set_body_nn(qs->els, body) ; /* sets term = fase */
+ return els_body_nn(qs->els) ;
} ;
-Inline ulen /* length of qstring (not NULL) */
-qs_len_nn(qstring qs)
+/* Set new body of qstring (not NULL).
+ *
+ * Caller must ensure that 'size', 'len' & 'cp' are all valid ! */
+Inline void
+qs_set_body_nn(qstring qs, const void* body)
{
- return els_len_nn(qs->els) ;
+ els_set_body_nn(qs->els, body) ;
} ;
-Inline ulen /* length of qstring */
+/*----------------------------------------------------------------------------*/
+
+/* 'len' of qstring -- returns 0 if qstring is NULL */
+Inline ulen
qs_len(qstring qs)
{
return (qs != NULL) ? qs_len_nn(qs) : 0 ;
} ;
-Inline void /* set length of qstring (not NULL) */
-qs_do_set_len_nn(qstring qs, ulen len)
+/* 'len' of qstring (not NULL) */
+Inline ulen
+qs_len_nn(qstring qs)
{
- els_set_len_nn(qs->els, len) ; /* sets term = false */
+ return els_len_nn(qs->els) ;
} ;
-Inline ulen /* cp of qstring (not NULL) */
-qs_cp_nn(qstring qs)
+/* set 'len' of qstring (not NULL) -- caller responsible for validity */
+Inline void
+qs_set_len_nn(qstring qs, ulen len)
{
- return qs->cp ;
+ els_set_len_nn(qs->els, len) ;
} ;
-Inline ulen /* cp of qstring */
+/*----------------------------------------------------------------------------*/
+
+/* 'cp' of qstring -- returns 0 if qstring is NULL */
+Inline ulen
qs_cp(qstring qs)
{
return (qs != NULL) ? qs_cp_nn(qs) : 0 ;
} ;
-Inline void /* set cp of qstring (not NULL) */
-qs_do_set_cp_nn(qstring qs, ulen cp)
+/* 'cp' of qstring (not NULL) */
+Inline ulen
+qs_cp_nn(qstring qs)
+{
+ return qs->cp ;
+} ;
+
+/* set 'cp' of qstring (not NULL) -- caller responsible for validity */
+Inline void
+qs_set_cp_nn(qstring qs, usize cp)
{
qs->cp = cp ;
} ;
-Inline char* /* pointer to given offset in qstring */
-qs_char_at_nn(qstring qs, usize off)
+/* move 'cp' of qstring (not NULL) -- caller responsible for validity */
+Inline void
+qs_move_cp_nn(qstring qs, int delta)
{
- char* p ;
- p = qs_body_nn(qs) ;
- return (p != NULL) ? p + off : NULL ;
+ qs->cp += delta ;
} ;
-Inline char* /* pointer to given offset in qstring */
-qs_char_at(qstring qs, usize off)
+/*----------------------------------------------------------------------------*/
+
+/* 'len' - 'cp' of qstring (not NULL) -- zero if 'len' < 'cp' */
+Inline ulen
+qs_after_cp_nn(qstring qs)
{
- return (qs != NULL) ? qs_char_at_nn(qs, off) : NULL ;
+ return (qs_len_nn(qs) > qs_cp_nn(qs)) ? qs_len_nn(qs) - qs_cp_nn(qs) : 0 ;
} ;
-Inline char* /* pointer to 'cp' offset in qstring */
-qs_cp_char(qstring qs)
+/* 'len' - 'cp' of qstring -- zero if NULL or 'len' < 'cp' */
+Inline ulen
+qs_after_cp(qstring qs)
{
- return (qs != NULL) ? qs_char_at_nn(qs, qs_cp_nn(qs)) : NULL ;
+ return (qs != NULL) ? qs_after_cp_nn(qs) : 0 ;
} ;
-Inline char* /* pointer to 'len' offset in qstring */
-qs_ep_char(qstring qs)
+/*------------------------------------------------------------------------------
+ * Functions to fetch various pointer pairs.
+ */
+
+Inline void
+qs_pp(pp p, qstring qs)
+{
+ if (qs != NULL)
+ qs_pp_nn(p, qs) ;
+ else
+ pp_null(p) ;
+} ;
+
+Inline void
+qs_pp_nn(pp p, qstring qs)
{
- return (qs != NULL) ? qs_char_at_nn(qs, qs_len_nn(qs)) : NULL ;
+ els_pp_nn(p, qs->els) ;
+} ;
+
+Inline void
+qs_cpp(cpp p, qstring qs)
+{
+ if (qs != NULL)
+ qs_cpp_nn(p, qs) ;
+ else
+ cpp_null(p) ;
} ;
-Inline bool /* whether qstring is known to be terminated */
-qs_term_nn(qstring qs)
+Inline void
+qs_cpp_nn(cpp p, qstring qs)
{
- return els_term_nn(qs->els) ;
+ els_cpp_nn(p, qs->els) ;
} ;
-Inline void /* set qstring is known to be terminated */
-qs_set_term_nn(qstring qs, bool how)
+/*------------------------------------------------------------------------------
+ * Set real body -- discarding any alias.
+ *
+ * NB: does not affect 'len' or 'cp'
+ */
+Inline void qs_set_real_body_nn(qstring qs)
{
- return els_set_term_nn(qs->els, how) ;
+ qs->size = qs->b_size ;
+ qs_set_body_nn(qs, qs->b_body) ;
+ qs->alias = false ;
} ;
/*==============================================================================
@@ -180,42 +379,140 @@ qs_set_term_nn(qstring qs, bool how)
*/
extern qstring qs_new(void) ;
+extern qstring qs_new_with_body(usize slen) ;
extern qstring qs_init_new(qstring qs, usize len) ;
extern qstring qs_reset(qstring qs, free_keep_b free_structure) ;
-Private void qs_make_to_size(qstring qs, usize len, free_keep_b free) ;
+Inline char* qs_make_string(qstring qs) ;
+Inline const char* qs_string(qstring qs) ;
+Inline void qs_clear(qstring qs) ;
+Inline qstring qs_new_size(qstring qs, usize slen) ;
+Inline qstring qs_extend(qstring qs, usize elen) ;
+Inline void qs_chop(qstring qs, usize clen) ;
+
+Private void qs_make_to_size(qstring qs, usize len, usize alen) ;
extern qstring qs_set(qstring qs, const char* src) ;
extern qstring qs_set_n(qstring qs, const char* src, usize n) ;
+extern qstring qs_set_qs(qstring qs, qstring src) ;
+extern qstring qs_set_els(qstring qs, elstring src) ;
+extern qstring qs_set_fill(qstring qs, usize len, const char* src) ;
+extern qstring qs_set_fill_n(qstring qs, usize len, const char* src,
+ usize flen) ;
extern qstring qs_append(qstring qs, const char* src) ;
extern qstring qs_append_n(qstring qs, const char* src, usize n) ;
-
-extern qstring qs_copy(qstring dst, qstring src) ;
+extern qstring qs_append_qs(qstring qs, qstring src) ;
+extern qstring qs_append_els(qstring qs, elstring src) ;
extern qstring qs_set_alias(qstring qs, const char* src) ;
extern qstring qs_set_alias_n(qstring qs, const char* src, usize len) ;
+extern qstring qs_set_alias_qs(qstring qs, qstring src) ;
+extern qstring qs_set_alias_els(qstring qs, elstring src) ;
+
+extern qstring qs_copy(qstring dst, qstring src) ;
extern qstring qs_printf(qstring qs, const char* format, ...)
PRINTF_ATTRIBUTE(2, 3) ;
extern qstring qs_vprintf(qstring qs, const char *format, va_list args) ;
-extern usize qs_insert(qstring qs, const void* src, usize n) ;
-extern void qs_replace(qstring qs, const void* src, usize n) ;
-extern usize qs_delete(qstring qs, usize n) ;
+extern usize qs_replace(qstring qs, usize r, const void* src, usize n) ;
+Inline usize qs_insert(qstring qs, const void* src, usize n)
+{
+ return qs_replace(qs, 0, src, n) ;
+} ;
+Inline usize qs_delete(qstring qs, usize n)
+{
+ return qs_replace(qs, n, NULL, 0) ;
+} ;
-extern int qs_cmp_sig(qstring a, qstring b) ;
+Inline int qs_cmp(qstring a, qstring b) ;
+Inline int qs_cmp_word(qstring a, const char* w) ;
+Inline int qs_cmp_sig(qstring a, qstring b) ;
+Inline bool qs_equal(qstring a, qstring b) ;
+Inline bool qs_substring(qstring a, qstring b) ;
/*==============================================================================
* The Inline functions.
*/
/*------------------------------------------------------------------------------
+ * Return pointer to string value -- ensure not alias and '\0' terminated.
+ *
+ * If is alias, copies that before adding '\0' terminator.
+ *
+ * Sets the '\0' terminator at the 'len' position, extending string if that
+ * is required.
+ *
+ * If qs == NULL or 'len' == 0 returns pointer to constant empty '\0'
+ * terminated string (ie "").
+ *
+ * NB: The qstring should not be changed or reset until this pointer has been
+ * discarded !
+ */
+Inline char*
+qs_make_string(qstring qs)
+{
+ usize len ;
+ char* p ;
+
+ if (qs == NULL)
+ {
+ len = 0 ;
+ qs = qs_new_with_body(len) ;
+ }
+ else
+ {
+ len = qs_len_nn(qs) ;
+ if (len >= qs->size) /* for alias, qs_size == 0 */
+ qs_make_to_size(qs, len, len) ; /* extend and/or copy any alias */
+ } ;
+
+ p = qs_char_nn(qs) ;
+ *(p + len) = '\0' ;
+
+ return p ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Return pointer to string value.
+ *
+ * If possible, writes '\0' at 'len' in order to return a terminated string.
+ *
+ * If qs == NULL or body == NULL, or 'len' == 0 returns pointer to constant
+ * empty '\0' terminated string (ie "").
+ *
+ * NB: if 'len' is beyond the current 'size' of the of the qstring, then
+ * does NOT write '\0' (does NOT extend the string).
+ *
+ * NB: if string is an alias, this returns its address, whether it is
+ * terminated or not.
+ *
+ * NB: In any event, the string should not be changed or reset until this
+ * pointer has been discarded !
+ */
+Inline const char*
+qs_string(qstring qs)
+{
+ char* p ;
+ usize len ;
+
+ if ( (qs == NULL) || ((len = qs_len_nn(qs) ) == 0)
+ || ((p = qs_char_nn(qs)) == NULL) )
+ return "" ;
+
+ if (len < qs->size) /* for alias, qs_size == 0 */
+ *(p + len) = '\0' ;
+
+ return p ;
+} ;
+
+/*------------------------------------------------------------------------------
* Clear contents of qstring -- preserves any qstring body, but sets len = 0.
*
* Does nothing if qstring is NULL
*
- * Sets 'cp' = 'len' = 0.
+ * Sets 'cp' = 'len' = 0
*
* If is an alias qstring, discard the alias.
*
@@ -226,68 +523,64 @@ qs_clear(qstring qs)
{
if (qs != NULL)
{
- qs_do_set_len_nn(qs, 0) ; /* sets term == false */
- if (qs->size == 0)
- {
- qs_set_body_nn(qs, qs->b_body) ;
- qs->size = qs->b_size ;
- } ;
- qs->cp = 0 ;
+ if (qs->alias)
+ qs_set_real_body_nn(qs) ;
+ qs_set_len_nn(qs, 0) ;
+ qs_set_cp_nn(qs, 0) ;
} ;
} ;
/*------------------------------------------------------------------------------
- * Need space for a string of 'slen' characters (plus possible '\0').
+ * Ensure have space for a string of 'slen' characters (plus possible '\0'),
+ * and discard any alias.
*
- * Allocate qstring if required (setting qs->len = qs->cp = 0).
+ * Allocate qstring if required (setting 'len' = 'cp' = 0).
*
* Returns: address of qstring -- with body that can be written upto and
* including 'slen' + 1.
*
- * NB: has no effect on 'len' -- even if 'len' > 'slen'.
+ * Has no effect on 'len' -- even if 'len' > 'slen'.
*
- * NB: has no effect on 'cp' -- even if 'cp' > 'len' or 'cp' > 'slen'.
+ * Has no effect on 'cp' -- even if 'cp' > 'len' or 'cp' > 'slen'.
*
- * NB: if this is a aliased qstring, the alias is discarded and term = false.
+ * If this is a aliased qstring, the alias is discarded.
*/
Inline qstring
-qs_need(qstring qs, usize slen)
+qs_new_size(qstring qs, usize slen)
{
- if (qs == NULL)
- qs = qs_init_new(NULL, slen) ; /* Make the qstring if required */
- else
- if (slen >= qs->size) /* for alias qs->size == 0 ! */
- qs_make_to_size(qs, slen, free_it) ;
+ if (qs == NULL)
+ qs = qs_new_with_body(slen) ;
+ else if (slen >= qs->size) /* for alias qs->size == 0 ! */
+ qs_make_to_size(qs, slen, 0) ; /* extend and/or make body */
return qs ;
} ;
/*------------------------------------------------------------------------------
- * Set 'len' -- allocate or extend body as required.
+ * Extend to 'len' + 'elen' -- allocate or extend body as required.
*
- * Allocate qstring if required (setting qs->len = qs->cp = 0).
+ * Allocate qstring if required (setting 'cp' = 0).
*
- * Returns: address of qstring -- with body that can be written upto and
- * including 'len' + 1.
+ * Returns: address of qstring -- with body that can be written up to and
+ * including 'len' + 'elen' + 1.
*
- * Sets 'cp' to the (new) 'len' if 'cp' > 'len'.
+ * Has no effect on 'cp' -- even if 'cp' > 'len'.
*
- * NB: if this is a aliased qstring, a copy is made of all of the original body,
- * even if that is longer than the required 'slen'. (And term = false.)
+ * If this is a aliased qstring, a copy is made.
*/
Inline qstring
-qs_set_len(qstring qs, usize len)
+qs_extend(qstring qs, usize elen)
{
if (qs == NULL)
- qs = qs_init_new(NULL, len) ; /* Make the qstring if required */
+ qs = qs_new_with_body(elen) ;
else
- if (len >= qs->size) /* for alias qs->size == 0 ! */
- qs_make_to_size(qs, len, keep_it) ;
-
- qs_do_set_len_nn(qs, len) ;
+ {
+ elen = qs_len_nn(qs) + elen ;
+ if (elen >= qs->size) /* for alias qs->size == 0 ! */
+ qs_make_to_size(qs, elen, elen) ; /* extend and/or copy any alias */
+ } ;
- if (qs->cp > len)
- qs->cp = len ;
+ qs_set_len_nn(qs, elen) ;
return qs ;
} ;
@@ -299,125 +592,74 @@ qs_set_len(qstring qs, usize len)
*
* Does not change the 'len' if it is <= length to chop to.
*
- * Sets 'cp' to the (new) 'len' if 'cp' > 'len'.
+ * NB: has no effect on 'cp' -- even if 'cp' > (new) 'len'.
*
* NB: if this is a aliased qstring, then it remains an aliased string, but
- * shorter and term = false (unless no change made to the length).
+ * shorter (unless no change made to the length).
*/
Inline void
qs_chop(qstring qs, usize clen)
{
if (qs != NULL)
{
- usize len = qs_len_nn(qs) ;
- if (len > clen)
- qs_do_set_len_nn(qs, (len = clen)) ; /* sets term = false */
- if (qs->cp > len)
- qs->cp = len ;
+ if (clen < qs_len_nn(qs))
+ qs_set_len_nn(qs, clen) ;
} ;
} ;
/*------------------------------------------------------------------------------
- * Set 'cp' -- allocate or extend body as required.
- *
- * Allocates the qstring, if required.
- *
- * Returns: address of qstring
+ * Compare two qstrings -- returns the usual -ve, 0, +ve cmp result.
*
- * NB: if there was no body, allocates a body for the string, even if 'cp' == 0.
- *
- * NB: if new 'cp' > 'len', extends body (or allocates one), and sets 'len' to
- * 'cp'. If this is an alias qstring, a copy of the string is made.
+ * NULL qstring is treated as empty.
*/
-Inline qstring
-qs_set_cp(qstring qs, usize cp)
+Inline int
+qs_cmp(qstring a, qstring b)
{
- if (qs == NULL)
- qs = qs_new(qs) ;
-
- if (qs->size <= cp)
-
-
- if ((qs == NULL) || (cp >(cp > qs_len_nn(qs)))
- qs = qs_set_len(qs, cp) ;
- qs->cp = cp ;
- return qs ;
+ return els_cmp(qs_els(a), qs_els(b)) ;
} ;
/*------------------------------------------------------------------------------
- * If not "term": set '\0' at qs->len -- extending body as required.
- *
- * Does NOT affect qs->cp or qs->len.
- *
- * Returns address of body -- NULL if the qstring is NULL
- *
- * NB: if this is an alias, and it is not terminated, make a copy before adding
- * terminating '\0'.
+ * Compare qstrings to given word -- see els_cmp_word
*/
-Inline void
-qs_terminate_nn(qstring qs)
+Inline int
+qs_cmp_word(qstring a, const char* w)
{
- if (!qs_term_nn(qs))
- {
- usize len ;
-
- len = qs_len_nn(qs) ;
-
- if (len >= qs->size) /* alias has size == 0 */
- qs_make_to_size(qs, len) ; /* make sure can insert '\0' */
-
- *qs_char_at_nn(qs, len) = '\0' ;
-
- qs_set_term_nn(qs, true) ;
- } ;
+ return els_cmp_word(qs_els(a), w) ;
} ;
/*------------------------------------------------------------------------------
- * Return pointer to '\0' terminated string value.
+ * Compare significant parts of two qstrings -- returns the usual -ve, 0, +ve
+ * cmp result.
*
- * If qs is NULL or body is NULL returns pointer to constant empty '\0'
- * terminated string.
+ * By significant, mean excluding leading/trailing isspace() and treating
+ * multiple isspace() as single isspace().
*
- * If string is terminated, return address of string, which may be an alias
- * address.
- *
- * Otherwise, makes sure that there is a '\0' at the qs->len position, making
- * a copy of any aliased string if required, and returns address of result.
- *
- * NB: value returned may be the address of the qstring body, or the address of
- * an aliased string. In any event, the string should not be changed or
- * reset until this pointer has been discarded !
+ * NULL qstring is treated as empty.
*/
-Inline const char*
-qs_string(qstring qs)
+Inline int
+qs_cmp_sig(qstring a, qstring b)
{
- if ((qs == NULL) || (qs_len_nn(qs) == 0))
- return "" ;
-
- qs_terminate_nn(qs) ;
+ return els_cmp_sig(qs_els(a), qs_els(b)) ;
+} ;
- return qs_body_nn(qs) ;
+/*------------------------------------------------------------------------------
+ * Are two qstrings equal ? -- returns true if they are.
+ */
+Inline bool
+qs_equal(qstring a, qstring b)
+{
+ return els_equal(qs_els(a), qs_els(b)) ;
} ;
/*------------------------------------------------------------------------------
- * Assuming the given address is within the size of the given qstring,
- * set qs->len and insert '\0' terminator there.
- *
- * Does NOT affect qs->cp.
- *
- * NB: must NOT be a NULL qs.
+ * Is 'b' a leading substring of 'a' ? -- returns true if it is.
*
- * NB: must NOT be an aliased qstring.
- *
- * NB: must NOT have a NULL body.
+ * If 'b' is empty it is always a leading substring.
*/
-Inline void
-qs_term_here(qstring qs, char* here)
+Inline bool
+qs_substring(qstring a, qstring b)
{
- assert((here >= qs->s.char_body) && (here < (qs->s.char_body + qs->size))) ;
-
- qs->len = (here - qs->s.char_body) ;
- *here = '\0' ;
+ return els_substring(qs_els(a), qs_els(b)) ;
} ;
#endif /* _ZEBRA_QSTRING_H */
diff --git a/lib/qtime.c b/lib/qtime.c
index 3ec34a72..92e67e27 100644
--- a/lib/qtime.c
+++ b/lib/qtime.c
@@ -192,3 +192,51 @@ qt_craft_monotonic(void) {
return (monotonic * times_scale_q) +
((monotonic * times_scale_r) / times_clk_tcks) ;
} ;
+
+/*==============================================================================
+ * A simple minded random number generator.
+ *
+ * Designed to be reasonably unpredictable... particularly the ms bits !
+ */
+
+static inline uint32_t
+qt_rand(uint64_t q, uint64_t s)
+{
+ /* Takes q ^ s and reduces to 32 bits by xoring ms and ls halves
+ * then uses Knuth recommended linear congruent to randomise that, so that
+ * most of the original bits affect the result.
+ *
+ * Note that linear congruent tends to be "more random" in the ms bits.
+ */
+ q ^= s ;
+ q = (q ^ (q >> 32)) & 0xFFFFFFFF ;
+ return ((q * 2650845021) + 5) & 0xFFFFFFFF ;
+} ;
+
+extern uint32_t
+qt_random(uint32_t seed)
+{
+ uint32_t x, y ;
+ uint64_t t ;
+
+ t = qt_get_realtime() ;
+
+ /* Set x by munging the time, the address of x, the current contents of x,
+ * and the "seed". (Munge the seed a bit for good measure.)
+ */
+ x = qt_rand(t ^ (uint64_t)x ^ (uint64_t)&x, seed ^ 3141592653) ;
+ /* munge the address and the contents with the seed */
+
+ /* Set y by munging the time, the address of y, the current contents of y,
+ * and the "seed". (Munge the seed a bit for good measure.)
+ */
+ y = qt_rand(t ^ (uint64_t)y ^ (uint64_t)&y, seed ^ 3562951413) ;
+ /* munge the current real time with the seed */
+
+ /* Return x and y munged together.
+ *
+ * Note that we swap the halves of y before munging, in order to spread
+ * the "more random" part of y down to the ls end of the result.
+ */
+ return x ^ ((y >> 16) & 0xFFFF) ^ ((y & 0xFFFF) << 16) ;
+} ;
diff --git a/lib/qtime.h b/lib/qtime.h
index 38e9ac1a..94d468a8 100644
--- a/lib/qtime.h
+++ b/lib/qtime.h
@@ -23,14 +23,11 @@
#define _ZEBRA_QTIME_H
#include "misc.h"
-#include <stdlib.h>
+
#include <time.h>
#include <sys/time.h>
#include <unistd.h>
-#include "zassert.h"
-#include "config.h"
-
/*==============================================================================
* qtime_t -- signed 64-bit integer.
*
@@ -64,17 +61,13 @@ typedef qtime_t qtime_tod_t ; /* qtime_t value, timeofday time-base... */
/* Note that the time to convert may be a float. */
#define QTIME(s) ((qtime_t)((s) * (qtime_t)QTIME_SECOND))
-Inline qtime_t
-timespec2qtime(struct timespec* p_ts) ;
-
-Inline qtime_t
-timeval2qtime(struct timeval* p_tv) ;
-
-Inline struct timespec*
-qtime2timespec(struct timespec* p_ts, qtime_t qt) ;
-
-Inline struct timeval*
-qtime2timeval(struct timeval* p_tv, qtime_t qt) ;
+/*==============================================================================
+ * Conversion functions
+ */
+Inline qtime_t timespec2qtime(struct timespec* p_ts) ;
+Inline qtime_t timeval2qtime(struct timeval* p_tv) ;
+Inline struct timespec* qtime2timespec(struct timespec* p_ts, qtime_t qt) ;
+Inline struct timeval* qtime2timeval(struct timeval* p_tv, qtime_t qt) ;
/*==============================================================================
* Clocks.
@@ -93,44 +86,37 @@ qtime2timeval(struct timeval* p_tv, qtime_t qt) ;
* a manufactured equivalent using times() -- see qt_craft_monotonic().
*/
-Inline qtime_real_t
-qt_get_realtime(void) ; /* clock_gettime(CLOCK_REALTIME, ...) */
+Inline qtime_real_t qt_get_realtime(void) ;
+ /* clock_gettime(CLOCK_REALTIME, ...) */
+Inline qtime_mono_t qt_add_realtime(qtime_t interval) ;
+ /* qt_get_realtime() + interval */
-Inline qtime_mono_t
-qt_add_realtime(qtime_t interval) ; /* qt_get_realtime() + interval */
-
-Inline qtime_mono_t
-qt_get_monotonic(void) ; /* clock_gettime(CLOCK_MONOTONIC, ...) */
+Inline qtime_mono_t qt_get_monotonic(void) ;
+ /* clock_gettime(CLOCK_MONOTONIC, ...) */
/* OR equivalent using times() */
-Inline qtime_mono_t
-qt_add_monotonic(qtime_t interval) ; /* qt_get_monotonic() + interval */
-
-Inline qtime_mono_t /* monotonic time from CLOCK_REALTIME */
-qt_realtime2monotonic(qtime_real_t realtime) ;
-Inline qtime_real_t /* CLOCK_REALTIME from monotonic time */
-qt_monotonic2realtime(qtime_mono_t monotonic) ;
-
-/* Function to manufacture a monotonic clock. */
-extern qtime_mono_t
-qt_craft_monotonic(void) ;
+Inline qtime_mono_t qt_add_monotonic(qtime_t interval) ;
+ /* qt_get_monotonic() + interval */
/* These are provided just in case gettimeofday() != CLOCK_REALTIME */
-Inline qtime_tod_t
-qt_get_timeofday(void) ; /* gettimeofday(&tv, NULL) */
+Inline qtime_tod_t qt_get_timeofday(void) ;
+ /* gettimeofday(&tv, NULL) */
+Inline qtime_tod_t qt_add_timeofday(qtime_t interval) ;
+ /* qt_get_timeofday() + interval */
-Inline qtime_tod_t
-qt_add_timeofday(qtime_t interval) ; /* qt_get_timeofday() + interval */
-
-Inline qtime_mono_t /* monotonic time from timeofday */
-qt_timeofday2monotonic(qtime_tod_t timeofday) ;
-Inline qtime_tod_t /* timeofday from monotonic time */
-qt_monotonic2timeofday(qtime_mono_t monotonic) ;
+/*==============================================================================
+ * Primitive random number generation.
+ *
+ * Uses time and other stuff to produce something which is not particularly
+ * predictable.
+ */
+extern uint32_t qt_random(uint32_t seed) ;
/*==============================================================================
* Inline conversion functions
*/
-/* Convert timespec to qtime_t
+/*------------------------------------------------------------------------------
+ * Convert timespec to qtime_t
*
* Returns qtime_t value.
*/
@@ -141,7 +127,8 @@ timespec2qtime(struct timespec* p_ts)
confirm(QTIME_SECOND == TIMESPEC_SECOND) ;
} ;
-/* Convert timeval to qtime_t
+/*------------------------------------------------------------------------------
+ * Convert timeval to qtime_t
*
* Returns qtime_t value.
*/
@@ -152,7 +139,8 @@ timeval2qtime(struct timeval* p_tv)
confirm(QTIME_SECOND == TIMEVAL_SECOND * 1000) ;
} ;
-/* Convert qtime_t to timespec
+/*------------------------------------------------------------------------------
+ * Convert qtime_t to timespec
*
* Takes address of struct timespec and returns that address.
*/
@@ -169,7 +157,8 @@ qtime2timespec(struct timespec* p_ts, qtime_t qt)
return p_ts ;
} ;
-/* Convert timespec to qtime_t
+/*------------------------------------------------------------------------------
+ * Convert timespec to qtime_t
*
* Takes address of struct timespec and returns that address.
*/
@@ -190,7 +179,11 @@ qtime2timeval(struct timeval* p_tv, qtime_t qt)
* Inline Clock Functions.
*/
-/* Read given clock & return a qtime_t value.
+/* Function to manufacture a monotonic clock. */
+Private qtime_mono_t qt_craft_monotonic(void) ;
+
+/*------------------------------------------------------------------------------
+ * Read given clock & return a qtime_t value.
*
* While possibility of error is essentially theoretical, must treat it as a
* FATAL error -- cannot continue with broken time value !
@@ -207,7 +200,8 @@ qt_clock_gettime(clockid_t clock_id)
return timespec2qtime(&ts) ;
} ;
-/* clock_gettime(CLOCK_REALTIME, ...) -- returning qtime_t value
+/*------------------------------------------------------------------------------
+ * clock_gettime(CLOCK_REALTIME, ...) -- returning qtime_t value
*
* While possibility of error is essentially theoretical, must treat it as a
* FATAL error -- cannot continue with broken time value !
@@ -218,7 +212,8 @@ qt_get_realtime(void)
return qt_clock_gettime(CLOCK_REALTIME) ;
} ;
-/* qt_get_realtime() + interval
+/*------------------------------------------------------------------------------
+ * qt_get_realtime() + interval
*/
Inline qtime_real_t
qt_add_realtime(qtime_t interval)
@@ -226,7 +221,8 @@ qt_add_realtime(qtime_t interval)
return qt_get_realtime() + interval;
} ;
-/* clock_gettime(CLOCK_MONOTONIC, ...) OR qt_craft_monotonic()
+/*------------------------------------------------------------------------------
+ * clock_gettime(CLOCK_MONOTONIC, ...) OR qt_craft_monotonic()
* -- returning qtime_t value
*
* While possibility of error is essentially theoretical, must treat it as a
@@ -242,7 +238,8 @@ qt_get_monotonic(void)
#endif
} ;
-/* qt_get_monotonic() + interval
+/*------------------------------------------------------------------------------
+ * qt_get_monotonic() + interval
*/
Inline qtime_mono_t
qt_add_monotonic(qtime_t interval)
@@ -250,7 +247,8 @@ qt_add_monotonic(qtime_t interval)
return qt_get_monotonic() + interval;
} ;
-/* gettimeofday(&tv, NULL) -- returning qtime_t value
+/*------------------------------------------------------------------------------
+ * gettimeofday(&tv, NULL) -- returning qtime_t value
*/
Inline qtime_tod_t
qt_get_timeofday(void)
@@ -260,7 +258,8 @@ qt_get_timeofday(void)
return timeval2qtime(&tv) ;
}
-/* qt_get_timeofday() + interval
+/*------------------------------------------------------------------------------
+ * qt_get_timeofday() + interval
*/
Inline qtime_tod_t
qt_add_timeofday(qtime_t interval)
@@ -268,36 +267,4 @@ qt_add_timeofday(qtime_t interval)
return qt_get_timeofday() + interval;
} ;
-/*==============================================================================
- * Conversion between realtime/timeofday and monotonic
- */
-
-/* Convert a CLOCK_REALTIME time to our local monotonic time. */
-Inline qtime_mono_t
-qt_realtime2monotonic(qtime_real_t realtime)
-{
- return qt_get_monotonic() + (realtime - qt_get_realtime()) ;
-} ;
-
-/* Convert a local monotonic time to CLOCK_REALTIME time. */
-Inline qtime_real_t
-qt_monotonic2realtime(qtime_mono_t monotonic)
-{
- return qt_get_realtime() + (monotonic - qt_get_monotonic()) ;
-} ;
-
-/* Convert a gettimeofday() time to our local monotonic time. */
-Inline qtime_mono_t
-qt_timeofday2monotonic(qtime_tod_t timeofday)
-{
- return qt_get_monotonic() + (timeofday - qt_get_timeofday()) ;
-} ;
-
-/* Convert a local monotonic time to gettimeofday() time. */
-Inline qtime_tod_t
-qt_monotonic2timeofday(qtime_mono_t monotonic)
-{
- return qt_get_timeofday() + (monotonic - qt_get_monotonic()) ;
-} ;
-
#endif /* _ZEBRA_QTIME_H */
diff --git a/lib/qtimers.c b/lib/qtimers.c
index 5c0f1518..6ccb7f7d 100644
--- a/lib/qtimers.c
+++ b/lib/qtimers.c
@@ -27,14 +27,6 @@
#include "memory.h"
#include "heap.h"
-enum { qdebug =
-#ifdef QDEBUG
- 1
-#else
- 0
-#endif
-};
-
/*==============================================================================
* Quagga Timers -- qtimer_xxxx
*
@@ -170,7 +162,7 @@ qtimer_pile_dispatch_next(qtimer_pile qtp, qtime_mono_t upto)
{
qtimer qtr ;
- if (qdebug)
+ if (qtimers_debug)
qtimer_pile_verify(qtp) ;
qtr = heap_top_item(&qtp->timers) ;
@@ -343,7 +335,7 @@ qtimer_set(qtimer qtr, qtime_mono_t when, qtimer_action* action)
qtp = qtr->pile ;
assert(qtp != NULL) ;
- if (qdebug)
+ if (qtimers_debug)
qtimer_pile_verify(qtp) ;
qtr->time = when ;
@@ -371,7 +363,7 @@ qtimer_set(qtimer qtr, qtime_mono_t when, qtimer_action* action)
else
assert(qtr->action != NULL) ;
- if (qdebug)
+ if (qtimers_debug)
qtimer_pile_verify(qtp) ;
} ;
@@ -387,7 +379,7 @@ qtimer_unset(qtimer qtr)
assert(qtp != NULL) ;
- if (qdebug)
+ if (qtimers_debug)
qtimer_pile_verify(qtp) ;
if (qtr->active)
@@ -397,7 +389,7 @@ qtimer_unset(qtimer qtr)
heap_delete_item(&qtp->timers, qtr) ;
- if (qdebug)
+ if (qtimers_debug)
qtimer_pile_verify(qtp) ;
qtr->active = false ;
diff --git a/lib/qtimers.h b/lib/qtimers.h
index 8534a789..2af81819 100644
--- a/lib/qtimers.h
+++ b/lib/qtimers.h
@@ -35,6 +35,22 @@
* each with an action to be executed when the timer expires.
*/
+#ifdef QTIMERS_DEBUG /* Can be forced from outside */
+# if QTIMERS_DEBUG
+# define QTIMERS_DEBUG 1 /* Force 1 or 0 */
+#else
+# define QTIMERS_DEBUG 0
+# endif
+#else
+# ifdef QDEBUG
+# define QTIMERS_DEBUG 1 /* Follow QDEBUG */
+#else
+# define QTIMERS_DEBUG 0
+# endif
+#endif
+
+enum { qtimers_debug = QTIMERS_DEBUG } ;
+
/*==============================================================================
* Data Structures.
*/
diff --git a/lib/routemap.c b/lib/routemap.c
index b452530d..d716e0b5 100644
--- a/lib/routemap.c
+++ b/lib/routemap.c
@@ -959,7 +959,7 @@ DEFUN (route_map,
index = route_map_index_get (map, permit, seq);
vty->index = index;
- vty_set_node(vty, RMAP_NODE) ;
+ vty->node = RMAP_NODE ;
return CMD_SUCCESS;
}
diff --git a/lib/symtab.h b/lib/symtab.h
index 4e33390e..008b7853 100644
--- a/lib/symtab.h
+++ b/lib/symtab.h
@@ -168,7 +168,7 @@ extern void* symbol_set_value(symbol sym, void* new_value) ;
Inline void*
symbol_unset_value(symbol sym)
{
- symbol_set_value(sym, NULL) ;
+ return symbol_set_value(sym, NULL) ;
} ;
extern void symbol_ref_walk_start(symbol sym, symbol_ref walk) ;
diff --git a/lib/thread.h b/lib/thread.h
index fa021486..6f74876d 100644
--- a/lib/thread.h
+++ b/lib/thread.h
@@ -206,7 +206,7 @@ extern int thread_should_yield (struct thread *);
/* Internal libzebra exports */
extern void thread_getrusage (RUSAGE_T *);
-extern struct cmd_element show_thread_cpu_cmd;
+extern struct cmd_command show_thread_cpu_cmd;
/* replacements for the system gettimeofday(), clock_gettime() and
* time() functions, providing support for non-decrementing clock on
diff --git a/lib/uty.h b/lib/uty.h
deleted file mode 100644
index cc759673..00000000
--- a/lib/uty.h
+++ /dev/null
@@ -1,232 +0,0 @@
-/* VTY internal stuff -- header
- * Copyright (C) 1997 Kunihiro Ishiguro
- *
- * Copyright (C) 2009 Chris Hall (GMCH), Highwayman
- *
- * This file is part of GNU Zebra.
- *
- * GNU Zebra is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * GNU Zebra is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with GNU Zebra; see the file COPYING. If not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#ifndef _ZEBRA_UTY_H
-#define _ZEBRA_UTY_H
-
-#include "misc.h"
-#include "vargs.h"
-
-#include "qpthreads.h"
-#include "qpnexus.h"
-#include "thread.h"
-#include "list_util.h"
-#include "vty.h"
-#include "vty_io_basic.h"
-#include "node_type.h"
-
-/*==============================================================================
- * This is stuff which is used by the close family of:
- *
- * vty
- * command
- * command_queue
- * log
- *
- * and which is not for use elsewhere.
- *
- * There are also:
- *
- * vty_io
- * vty_cli
- *
- * Which are the "immediate family" of vty:
- *
- * * *nothing* in their ".h" is for use anywhere except the immediate family.
- *
- * * things for use within the rest of the family are published here.
- */
-
-/*==============================================================================
- * Variables in vty.c -- used in any of the family
- */
-extern vty_io vio_list_base ;
-extern vty_io vio_monitors_base ;
-extern vty_io vio_death_watch ;
-
-extern struct thread_master* vty_master ;
-
-extern unsigned long vty_timeout_val ;
-
-extern bool vty_config ;
-
-extern bool no_password_check ;
-extern const bool restricted_mode_default ;
-extern bool restricted_mode ;
-
-char *vty_accesslist_name ;
-char *vty_ipv6_accesslist_name ;
-
-extern qpn_nexus vty_cli_nexus ;
-extern qpn_nexus vty_cmd_nexus ;
-
-/*==============================================================================
- * To make vty qpthread safe we use a single mutex.
- *
- * 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.
- *
- * A recursive mutex is used. This simplifies the calling from log to vty and
- * back again. It also allows for the vty internals to call each other.
- *
- * There are some "uty" functions which assume the mutex is locked.
- *
- * vty is closely bound to the command handling -- the main vty structure
- * contains the context in which commands are parsed and executed.
- */
-
-extern qpt_mutex_t vty_mutex ;
-
-#ifdef NDEBUG
-# define VTY_DEBUG 0 /* NDEBUG override */
-#else
-# ifndef VTY_DEBUG
-# define VTY_DEBUG 1 /* Set to 1 to turn on debug checks */
-# endif
-#endif
-
-#if VTY_DEBUG
-
-extern int vty_lock_count ;
-extern int vty_assert_fail ;
-
-#endif
-
-Inline void
-VTY_LOCK(void) /* if is qpthreads_enabled, lock vty_mutex */
-{
- qpt_mutex_lock(&vty_mutex) ;
- if (VTY_DEBUG)
- ++vty_lock_count ;
-} ;
-
-Inline void
-VTY_UNLOCK(void) /* if is qpthreads_enabled, unlock vty_mutex */
-{
- if (VTY_DEBUG)
- --vty_lock_count ;
- qpt_mutex_unlock(&vty_mutex) ;
-} ;
-
-Inline bool /* true => is (effectively) cli thread */
-vty_is_cli_thread(void)
-{
- return !qpthreads_enabled || qpt_thread_is_self(vty_cli_nexus->thread_id) ;
-} ;
-
-/* For debug (and documentation) purposes, will VTY_ASSERT_LOCKED where that
- * is required.
- *
- * In some cases, need also to be running in the CLI thread as well.
- */
-#if VTY_DEBUG
-
-Inline void
-VTY_ASSERT_FAILED(void)
-{
- if (vty_assert_fail == 0) ;
- {
- vty_assert_fail = 1 ;
- assert(0) ;
- } ;
-} ;
-
-Inline void
-VTY_ASSERT_LOCKED(void)
-{
- if (vty_lock_count == 0)
- VTY_ASSERT_FAILED() ;
-} ;
-
-Inline void
-VTY_ASSERT_CLI_THREAD(void)
-{
- if (!vty_is_cli_thread())
- VTY_ASSERT_FAILED() ;
-} ;
-
-#else
-
-#define VTY_ASSERT_LOCKED()
-#define VTY_ASSERT_CLI_THREAD()
-
-#endif
-
-/*==============================================================================
- * Shared definitions
- */
-
-enum cli_do
-{
- cli_do_nothing = 0, /* no action required */
-
- cli_do_command, /* dispatch the current command line */
- cli_do_ctrl_c, /* received ^c */
- cli_do_ctrl_d, /* received ^d on empty line */
- cli_do_ctrl_z, /* received ^z */
-
- cli_do_eof, /* hit "EOF" */
-
- cli_do_count /* number of different cli_do_xxx */
-} ;
-
-/*==============================================================================
- * Functions in vty.c -- used in any of the family
- */
-extern enum cmd_return_code uty_command(struct vty *vty) ;
-extern enum cmd_return_code uty_auth (struct vty *vty, const char *buf,
- enum cli_do cli_do) ;
-extern enum cmd_return_code vty_cmd_exit(struct vty* vty) ;
-extern enum cmd_return_code vty_cmd_end(struct vty* vty) ;
-extern enum cmd_return_code uty_cmd_close(struct vty *vty, const char* reason) ;
-extern enum cmd_return_code uty_stop_input(struct vty *vty) ;
-extern enum cmd_return_code uty_end_config (struct vty *vty) ;
-extern enum cmd_return_code uty_down_level (struct vty *vty) ;
-
-extern bool vty_config_lock (struct vty *, enum node_type node);
-extern void vty_config_unlock (struct vty *, enum node_type node);
-extern void uty_config_unlock (struct vty *vty, enum node_type node) ;
-
-/*==============================================================================
- * Functions in vty_cli -- used outside the immediate vty family
- */
-extern void vty_queued_result(struct vty* vty, enum cmd_return_code ret);
-extern void uty_set_host_name(const char* name) ;
-
-/*==============================================================================
- * Functions in vty_io -- used outside the immediate vty family
- */
-extern void vty_open_config_write(struct vty* vty, int fd) ;
-extern int vty_close_config_write(struct vty*) ;
-
-extern void vty_log_fixed (const char *buf, size_t len);
-
-extern void uty_log (struct logline* ll, struct zlog *zl, int priority,
- const char *format, va_list va);
-
-/*==============================================================================
- * Functions in command.c
- */
-extern void cmd_post_command(struct vty* vty, int ret) ;
-
-#endif /* _ZEBRA_UTY_H */
diff --git a/lib/vector.c b/lib/vector.c
index 6df8d409..15371930 100644
--- a/lib/vector.c
+++ b/lib/vector.c
@@ -22,7 +22,7 @@
* 02111-1307, USA.
*/
-#include <zebra.h>
+//#include <zebra.h>
#include "vector.h"
#include "memory.h"
@@ -1055,7 +1055,7 @@ vector_bsearch(vector v, vector_bsearch_cmp* cmp, const void* p_val,
if (v->end <= 1)
{
- *result = (v->end == 0) ? -1 : cmp(&p_val, (const void**)&v->p_items[0]) ;
+ *result = (v->end == 0) ? -1 : cmp(&p_val, (const cvp*)&v->p_items[0]) ;
return 0 ; /* Stop dead if 0 or 1 items */
} ;
@@ -1064,12 +1064,12 @@ vector_bsearch(vector v, vector_bsearch_cmp* cmp, const void* p_val,
ih = v->end - 1 ;
/* Pick off the edge cases: >= last and <= first. */
- if ((c = cmp(&p_val, (const void**)&v->p_items[ih])) >= 0)
+ if ((c = cmp(&p_val, (const cvp*)&v->p_items[ih])) >= 0)
{
*result = c ; /* 0 => found. +1 => val > last */
return ih ; /* return high index. */
} ;
- if ((c = cmp(&p_val, (const void**)&v->p_items[il])) <= 0)
+ if ((c = cmp(&p_val, (const cvp*)&v->p_items[il])) <= 0)
{
*result = c ; /* 0 => found. -1 => val < first */
return il ; /* return low index. */
@@ -1086,7 +1086,7 @@ vector_bsearch(vector v, vector_bsearch_cmp* cmp, const void* p_val,
return il ; /* return il: item[il] < val < item[il+1] */
} ;
/* We now know that il < iv < ih */
- c = cmp(&p_val, (const void**)&v->p_items[iv]) ;
+ c = cmp(&p_val, (const cvp*)&v->p_items[iv]) ;
if (c == 0)
{
*result = 0 ;
diff --git a/lib/vector.h b/lib/vector.h
index 58b9e415..bae2a2f8 100644
--- a/lib/vector.h
+++ b/lib/vector.h
@@ -50,6 +50,16 @@ struct vector
typedef struct vector vector_t[1] ; /* embedded vector structure */
typedef struct vector* vector ; /* pointer to vector structure */
+/* Setting a vector object to all zeros is enough to initialise it to
+ * an empty vector.
+ */
+enum
+{
+ VECTOR_INIT_ALL_ZEROS = true
+} ;
+
+#define VECTOR_INIT_EMPTY { .p_items = NULL, .end = 0, .limit = 0 }
+
/* Under very controlled circumstances, may access the vector body */
typedef p_vector_item const* vector_body_t ;
@@ -162,10 +172,10 @@ Inline p_vector_item vector_shift_item(vector v) ;
extern void vector_insert(vector v, vector_index_t i, vector_length_t n) ;
extern void vector_delete(vector v, vector_index_t i, vector_length_t n) ;
-typedef int vector_bsearch_cmp(const void** pp_val, const void** item) ;
+typedef int vector_bsearch_cmp(const cvp* pp_val, const cvp* item) ;
vector_index_t vector_bsearch(vector v, vector_bsearch_cmp* cmp,
const void* p_val, int* result) ;
-typedef int vector_sort_cmp(const void** a, const void** b) ;
+typedef int vector_sort_cmp(const cvp* a, const cvp* b) ;
void vector_sort(vector v, vector_sort_cmp* cmp) ;
extern vector vector_copy_here(vector dst, vector src) ;
diff --git a/lib/version.h.in b/lib/version.h.in
index 429474d1..ded43944 100644
--- a/lib/version.h.in
+++ b/lib/version.h.in
@@ -34,6 +34,8 @@
#define QUAGGA_COPYRIGHT "Copyright 1996-2005 Kunihiro Ishiguro, et al."
+#include <sys/types.h>
+
pid_t pid_output (const char *);
#ifndef HAVE_DAEMON
diff --git a/lib/vio_fifo.c b/lib/vio_fifo.c
index 10c4a343..e6365861 100644
--- a/lib/vio_fifo.c
+++ b/lib/vio_fifo.c
@@ -20,14 +20,13 @@
*/
#include "misc.h"
+#include <stdio.h>
#include <string.h>
#include "vio_fifo.h"
#include "network.h"
-#include "list_util.h"
#include "memory.h"
-#include "zassert.h"
/*==============================================================================
* VTY I/O FIFO manages an arbitrary length byte-wise FIFO buffer.
@@ -39,7 +38,6 @@
* ever needed.
*
* When releasing lumps, keeps one lump "spare", to be reused as necessary.
- * This is used in ... TODO <<<< And is released...
*
*------------------------------------------------------------------------------
* Implementation notes:
@@ -48,97 +46,218 @@
*
* Once a lump has been allocated there is always one lump in the FIFO.
*
+ * The hold_mark allows the get_ptr to move forward, but retaining the data in
+ * the FIFO until the hold_mark is cleared. Can move the get_ptr back to the
+ * hold_mark to reread the data.
+ *
+ * The end_mark allows put_ptr to move forward, but the new data cannot be got
+ * from the FIFO until the end_mark is cleared. Can discard the new data
+ * and move the put_ptr back to the end_mark.
+ *
+ * There are four lumps of interest:
+ *
+ * * head -- where the hold_mark is, if there is one.
+ *
+ * * get_lump -- where the get_ptr is.
+ * Same as head when no hold_mark.
+ *
+ * * end_lump -- where the end_mark is, if there is one.
+ * Same as tail when no end mark.
+ *
+ * * tail -- where the put_ptr is.
+ *
+ * Some or all of those may be the same, depending on how big the FIFO is.
+ *
* The following are expected to be true:
*
- * * put_ptr == get_ptr => FIFO empty
+ * * set <=> at least one lump in the FIFO
+ *
+ * * as_one <=> set && get_lump == tail && !end_mark
+ * => get_end moves with put_ptr
*
- * * put_ptr == tail->end -- at all times (NULL when no lumps)
+ * * hold_mark => there is a hold mark & hold_ptr is valid
+ * & head lump contains mark
+ * !hold_mark => get_lump == head & hold_ptr == NULL
+ *
+ * * end_mark => there is an end_mark & end_end is valid
+ * & end_lump contains mark
+ * !end_mark => end_lump == tail & end_ptr == NULL
+ *
+ * * put_ptr == get_ptr => FIFO empty -- unless hold_mark and
+ * hold_ptr != get_ptr.
+ *
+ * * put_end == tail->end -- at all times (NULL when no lumps)
*
* put_ptr >= tail->data ) otherwise something is broken
* put_ptr <= tail->end )
*
- * * get_ptr == head->end -- when there is more than one lump
- * get_ptr <= put_ptr -- when there is only one lump
+ * * get_end == get_lump->end -- when get_lump != end_lump
+ * == end_end -- when get_lump == end_lump & end_mark set
+ * <= put_ptr -- when get_lump == end_lump & no end_mark
+ *
+ * See note below on get_end and as_one flag.
*
- * get_ptr >= head->data ) otherwise something is broken
- * get_ptr <= head->end )
+ * get_ptr >= get_lump->data ) otherwise something is broken
+ * get_ptr <= get_lump->end )
*
* * put_ptr == put_end => tail lump is full
* put_ptr < put_end => space exists in the tail lump
* put_ptr > put_end => broken
*
- * * get_ptr == get_end => head lump is empty
- * BUT if there is only one lump, make sure that
- * get_end == put_ptr.
- * get_ptr < get_end => data exists in the head lump
+ * * get_ptr == get_end => nothing to get from current get_lump
+ * BUT if as_one, make sure that get_end == put_ptr
+ * get_ptr < get_end => data exists in the current get_lump
* get_ptr > get_end => broken
*
* Note that:
*
- * * when the get_ptr reaches the put_ptr the pointers are reset to the
- * start of the one and only lump.
+ * * while get_ptr <= get_end can get stuff without worrying about other
+ * pointers or moving between lumps etc. It is permissible to leave
+ * get_ptr == get_end -- this will be tidied up on the next get operation,
+ * or any other time vio_fifo_sync_get() is called. Leaving get_ptr in
+ * that state delays the discard of the now empty lump, but has no other
+ * real downside.
+ *
+ * Similarly, while put_ptr <= put_end, can put stuff without worrying
+ * about other pointers or moving between lumps etc.
+ *
+ * When getting, if get_ptr == get_end, or require more data than is
+ * immediately available, need to use vio_fifo_sync_get(), to do that.
+ *
+ * * the value of get_end depends on whether get_lump == end_lump, and then
+ * whether there is an end_mark.
+ *
+ * When get_lump == end_lump && !end_mark, then get_end may be out of date
+ * because get_ptr has been advanced. This is dealt with by
+ * vio_fifo_sync_get(), which uses the as_one flag to signal that it should
+ * set get_end = put_ptr and then (re)check for anything to get.
*
- * Everywhere that the get_ptr is moved, must check for meeting the
- * put_ptr and reset pointers. At the same time, when reaches the end of
- * a lump, gets rid of it.
+ * The as_one flag is there to save a little work. Making sure that the
+ * get_end is up to date can be done often when getting from the FIFO. So
+ * the maintenance of the flag should be worth the effort.
*
- * * when advancing the put_ptr does not check for advancing the get_end.
+ * * some care must be taken to ensure that are not fooled by the
+ * ambiguity of a pointer to the end of one lump and a pointer to the
+ * start of the next -- these are really equal, but they don't look as if
+ * they are !
*
- * The one exception to this, is that when the put_ptr advances to a new
- * block, if there was one lump, sets the get_end to the end of that block.
+ * - put_ptr -- if at the end of the last lump there is no next lump !
*
- * Everywhere that the get_end is used, must check for there being one
- * lump and the possibility that put_ptr has changed.
+ * When a new lump is added, the put_ptr advances to the
+ * start of the new last lump.
+ *
+ * - end_end -- when set from the put_ptr, this can be at the end of
+ * the last lump, but as above, there is no next lump.
+ *
+ * When a new lump is added, if the end_end is at the
+ * end of the last lump, it is moved to the start of the
+ * new last lump (along with the out_ptr).
+ *
+ * So... end_end will never be ambiguous.
+ *
+ * - get_ptr -- this can be ambiguous.
+ *
+ * When getting bytes, if the segment get_ptr..get_end
+ * is sufficient, then nothing else is required.
+ *
+ * Otherwise, vio_fifo_sync_get() will sort things out,
+ * including resetting all pointers if the FIFO has been
+ * emptied.
+ *
+ * - hold_ptr -- when set from get_ptr this could be at the end of the
+ * first lump -- but when vio_fifo_sync_get() is called,
+ * that will be spotted and sorted out.
+ *
+ * If get_ptr is set from an ambiguous hold_ptr, that is
+ * also taken care of by the next vio_fifo_sync_get().
+ *
+ * When doing things with hold_ptr, does a number of
+ * vio_fifo_sync_get() operations, so the hold_ptr should
+ * not, in practice, be ambiguous.
+ *
+ * * Before the first lump is allocated the FIFO appears empty (of course)
+ * but may have hold_mark and/or end_mark set, and these work as expected.
*/
/*==============================================================================
* Initialisation, allocation and freeing of FIFO and lumps thereof.
*/
-/* Return default size, or given size rounded up to 16 byte boundary */
+/*------------------------------------------------------------------------------
+ * Return default size, or given size rounded up to 128 byte boundary
+ */
static size_t
vio_fifo_size(size_t size)
{
+#if VIO_FIFO_DEBUG
+#warning VIO_FIFO_DEBUG and 29 byte lumps !
+ return 29 ;
+#else
if (size == 0)
return 4096 ;
else
- return ((size + 16 - 1) / 16) * 16 ;
+ return ((size + 128 - 1) / 128) * 128 ;
+#endif
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set and return true end of lump.
+ *
+ * End of lump can be set short of true end when putting stuff, if wish to
+ * move to next lump early (eg if not enough room left in current lump).
+ *
+ * When reusing a lump, need to restore the true lump end.
+ */
+static inline char*
+vio_fifo_true_lump_size(vio_fifo_lump lump)
+{
+ return lump->end = lump->data + lump->size ;
} ;
/*==============================================================================
* Initialise VTY I/O FIFO -- allocating if required.
*/
extern vio_fifo
-vio_fifo_init_new(vio_fifo vf, size_t size)
+vio_fifo_init_new(vio_fifo vff, size_t size)
{
- if (vf == NULL)
- vf = XCALLOC(MTYPE_VIO_FIFO, sizeof(vio_fifo_t)) ;
+ if (vff == NULL)
+ vff = XCALLOC(MTYPE_VIO_FIFO, sizeof(vio_fifo_t)) ;
else
- memset(vf, 0, sizeof(vio_fifo_t)) ;
+ memset(vff, 0, sizeof(vio_fifo_t)) ;
/* Zeroising the the vio_fifo_t has set:
*
- * lump -- base pair, both pointers NULL => list is empty
+ * base -- base pair, both pointers NULL => list is empty
*
- * put_ptr -- NULL ) no lump to put anything into
- * put_end -- NULL ) put_ptr == put_end => no room in current lump
+ * set -- false -- nothing set, yet.
+ * as_one -- false -- get_lump != tail or end_mark
+ * hold_mark -- false -- no hold mark
+ * end_mark -- false -- no end mark
+ *
+ * hold_ptr -- NULL no hold_mark
*
+ * get_lump -- NULL )
* get_ptr -- NULL ) no lump to get anything from
* get_end -- NULL ) get_ptr -- get_end => nothing left in current lump
*
- * rdr_lump -- NULL ) no rdr_lump
- * rdr_ptr -- NULL
+ * end_lump -- NULL no lump at end of what can get
+ * end_end -- NULL no end_mark
*
- * spare -- NULL no spare lump
+ * put_ptr -- NULL ) no lump to put anything into
+ * put_end -- NULL ) put_ptr == put_end => no room in current lump
*
- * ALSO put_ptr == get_ptr => FIFO is empty !
+ * size -- 0 no size set for lumps (yet)
+ *
+ * spare -- NULL no spare lump
*/
+ confirm(VIO_FIFO_INIT_ALL_ZEROS) ;
- vf->size = vio_fifo_size(size) ;
+ if (size != 0)
+ vff->size = vio_fifo_size(size) ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
- return vf ;
+ return vff ;
}
/*------------------------------------------------------------------------------
@@ -146,7 +265,8 @@ vio_fifo_init_new(vio_fifo vf, size_t size)
*
* Does nothing if given a NULL pointer -- must already have been freed !
*
- * If does not free the FIFO structure, resets it all empty.
+ * If does not free the FIFO structure, resets it all empty, keeping the
+ * current size setting.
*
* Frees *all* FIFO lumps.
*
@@ -154,104 +274,289 @@ vio_fifo_init_new(vio_fifo vf, size_t size)
* vio_fifo_reset_free(vio_fifo)
*/
extern vio_fifo
-vio_fifo_reset(vio_fifo vf, int free_structure)
+vio_fifo_reset(vio_fifo vff, free_keep_b free_structure)
{
vio_fifo_lump lump ;
- if (vf == NULL)
+ if (vff == NULL)
return NULL ;
- while (ddl_pop(&lump, vf->base, list) != NULL)
+ while (ddl_pop(&lump, vff->base, list) != NULL)
XFREE(MTYPE_VIO_FIFO_LUMP, lump) ;
- if (vf->spare != NULL)
- XFREE(MTYPE_VIO_FIFO_LUMP, vf->spare) ;
+ if (vff->spare != NULL)
+ XFREE(MTYPE_VIO_FIFO_LUMP, vff->spare) ;
+
+ confirm(free_it == true) ;
if (free_structure)
- XFREE(MTYPE_VIO_FIFO, vf) ; /* sets vf = NULL */
+ XFREE(MTYPE_VIO_FIFO, vff) ; /* sets vff = NULL */
else
- vio_fifo_init_new(vf, vf->size) ;
+ vio_fifo_init_new(vff, vff->size) ;
- return vf ;
+ return vff ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * The FIFO has one lump -- set all pointers.
+ *
+ * Preserves end_mark -- setting to new put_ptr position
+ * Preserves hold_mark -- setting to new put_ptr position
+ *
+ * Sets as_one if no end_mark.
+ */
+inline static void
+vio_fifo_ptr_set(vio_fifo vff, vio_fifo_lump lump)
+{
+ vff->as_one = !vff->end_mark ;
+
+ vff->put_ptr = lump->data ;
+ vff->put_end = vio_fifo_true_lump_size(lump) ;
+
+ vff->hold_ptr = vff->hold_mark ? vff->put_ptr : NULL ;
+
+ vff->get_lump = lump ;
+ vff->get_ptr = vff->put_ptr ;
+ vff->get_end = vff->put_ptr ;
+
+ vff->end_lump = lump ;
+ vff->end_end = vff->end_mark ? vff->put_ptr : NULL ;
} ;
/*------------------------------------------------------------------------------
* The FIFO is empty, with one lump -- reset all pointers.
+ *
+ * Preserves end_mark -- setting to new put_ptr position
+ * Preserves hold_mark -- setting to new put_ptr position
+ *
+ * Sets as_one if no end_mark.
*/
inline static void
-vio_fifo_ptr_reset(vio_fifo vf, vio_fifo_lump lump)
+vio_fifo_ptr_reset(vio_fifo vff, vio_fifo_lump lump)
{
- if (vf->rdr_lump != NULL)
- {
- assert((lump == vf->rdr_lump) && (vf->rdr_ptr == vf->get_ptr)) ;
- vf->rdr_ptr = lump->data ;
- } ;
+ assert(vff->set) ;
+ assert(lump == ddl_tail(vff->base)) ; /* must be tail */
+ assert(lump == ddl_head(vff->base)) ; /* and must be head */
+
+ vio_fifo_ptr_set(vff, lump) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Synchronise get_end and put_ptr (if required)
+ *
+ * When in the same lump, the get_end and the put_ptr should be the same. But
+ * maintaining that every time something is put into the buffer is a pain, so
+ * we allow the put_ptr to get ahead, and re-synchronise when get_ptr hits
+ * get_end, or any other time we need the pointers to be straight.
+ *
+ * The as_one flag takes a little maintenance... it means:
+ *
+ * (get_lump == tail) && set && !end_mark
+ *
+ * If get_ptr is at the end of what can be got from the current lump, advances
+ * to the next lump if possible, releasing anything that can be released.
+ *
+ * If the FIFO is empty (with one lump), will crash all pointers down to start
+ * of current lump. The FIFO is empty if get_ptr == put_ptr, and there are no
+ * bytes held before the get_ptr.
+ *
+ * NB: object of the hold_mark is to allow users of the FIFO to store pointers
+ * to sections of the FIFO returned by vio_fifo_get_lump() and stepped
+ * over by vio_fifo_got_upto(). Note that where there is nothing to
+ * read vio_fifo_get_lump() returns NULL -- so do not have to preserve
+ * the hold_ptr when hold_ptr == get_ptr (== end_end) == put_ptr -- that
+ * is, if hold_ptr == get_ptr, we can move the hold_ptr around with the
+ * get_ptr.
+ *
+ * Can reach empty state without realising it, because the "get" stuff is not
+ * required to check every time a byte is read -- in fact, it is not necessary
+ * to check until cannot get anything because get_ptr == get_end.
+ *
+ * NB: with an end_mark there is the ambiguous position at the end of a
+ * lump -- which is the same as the start of the next lump, if any.
+ *
+ * Elsewhere we advance the end_end if it was at the very end of the FIFO
+ * and we advance the put_ptr to a new lump. But we cope here in any
+ * case.
+ *
+ * Returns: true <=> there is something in the (now) current get_lump.
+ * false <=> there is nothing else to be got (though if there is an
+ * end_mark, there may be stuff beyond that).
+ */
+static bool vio_fifo_sync_get_next(vio_fifo vff) ;
+static void vio_fifo_release_head(vio_fifo vff, vio_fifo_lump lump) ;
+
+inline static bool
+vio_fifo_sync_get(vio_fifo vff)
+{
+ if (vff->as_one) /* false if !vff->set */
+ vff->get_end = vff->put_ptr ;
- /* Note that sets the lump->end to the true lump->end */
- vf->get_ptr = vf->get_end = vf->put_ptr = lump->data ;
- vf->put_end = lump->end = lump->data + lump->size ;
+ if (vff->get_ptr < vff->get_end) /* both NULL if !vff->set */
+ return true ; /* have at least one byte */
+
+ return vio_fifo_sync_get_next(vff) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * See if fifo really is or is not empty (get_ptr == end_end or == put_ptr).
+ *
+ * Used by vio_fifo_empty(). Called iff get_ptr >= get_end !
+ *
+ * So need to vio_fifo_sync_get() and test again against end_end & put_ptr.
+ */
+Private bool
+vio_fifo_do_empty(vio_fifo vff)
+{
+ return ! vio_fifo_sync_get(vff) ;
} ;
/*------------------------------------------------------------------------------
- * The FIFO is utterly empty, with ZERO lumps -- unset all pointers.
+ * Set a new get_lump/get_ptr/get_end set. And set as_one to suit.
*/
inline static void
-vio_fifo_ptr_unset(vio_fifo vf)
+vio_fifo_set_get_ptr(vio_fifo vff, void* ptr, vio_fifo_lump lump)
{
- assert((ddl_head(vf->base) == NULL) && (ddl_tail(vf->base) == NULL)) ;
+ vff->get_lump = lump ;
+ vff->get_ptr = ptr ;
- vf->one = false ;
+ if (lump != vff->end_lump)
+ {
+ vff->get_end = lump->end ;
+ vff->as_one = false ;
+ }
+ else if (vff->end_mark)
+ {
+ vff->get_end = vff->end_end ;
+ vff->as_one = false ;
+ }
+ else
+ {
+ vff->get_end = vff->put_ptr ;
+ vff->as_one = true ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Move on to next lump to get stuff from.
+ *
+ * For use by vio_fifo_sync_get() *ONLY*.
+ *
+ * Asserts that (vff->get_ptr == vff->get_end) and assumes that if as_one, then
+ * get_end == put_ptr !
+ *
+ * Returns: true <=> at least one byte in FIFO available to get.
+ */
+static bool
+vio_fifo_sync_get_next(vio_fifo vff)
+{
+ bool hold_empty ;
- vf->put_ptr = NULL ;
- vf->put_end = NULL ;
- vf->get_ptr = NULL ;
- vf->get_end = NULL ;
+ assert(vff->get_ptr == vff->get_end) ;
- vf->rdr_lump = NULL ;
- vf->rdr_ptr = NULL ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
+
+ if (!vff->set)
+ return false ; /* quit before get into any trouble */
+
+ /* Worry about whether there is anything held.
+ *
+ * NB: we know that at this point the get_ptr == get_end, so is at the end
+ * of the current get_lump. The hold_ptr cannot be ahead of get_ptr,
+ * so the test hold_ptr == get_ptr is sufficient to detect that there
+ * is nothing, in fact, held.
+ */
+ hold_empty = !vff->hold_mark || (vff->hold_ptr == vff->get_ptr) ;
+
+ /* If we are not at the end_lump, step forward, discarding current
+ * lump unless hold_mark.
+ */
+ if (vff->get_lump != vff->end_lump)
+ {
+ vio_fifo_lump next ;
+ next = ddl_next(vff->get_lump, list) ;
+
+ vio_fifo_set_get_ptr(vff, next->data, next) ;
+
+ if (hold_empty)
+ {
+ vio_fifo_release_head(vff, next) ;
+
+ /* If there is a hold_mark, then had hold_ptr == get_ptr
+ * and this is where we keep hold_ptr & get_ptr in sync.
+ */
+ if (vff->hold_mark)
+ vff->hold_ptr = vff->get_ptr ;
+ } ;
+
+ /* Return the get state now */
+ if (vff->get_ptr < vff->get_end)
+ return true ;
+ } ;
+
+ /* Still have get_ptr == get_end => nothing more to get.
+ *
+ * Check now for empty FIFO, and reset all pointers if that is the case.
+ */
+ if ((vff->get_ptr == vff->put_ptr) && hold_empty)
+ {
+ if (vff->hold_mark)
+ assert(vff->hold_ptr == vff->get_ptr) ;
+
+ if (vff->end_mark)
+ assert(vff->end_end == vff->put_ptr) ;
+
+ vio_fifo_ptr_reset(vff, vff->end_lump) ;
+ }
+
+ return false ; /* nothing to get */
} ;
/*------------------------------------------------------------------------------
* Clear out contents of FIFO -- will continue to use the FIFO.
*
+ * If required, clears any hold mark and/or end mark.
+ *
* Keeps one FIFO lump. (Frees everything else, including any spare.)
*
* Does nothing if there is no FIFO !
*/
extern void
-vio_fifo_clear(vio_fifo vf)
+vio_fifo_clear(vio_fifo vff, bool clear_marks)
{
- vio_fifo_lump tail ;
-
- if (vf == NULL)
+ if (vff == NULL)
return ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
- tail = ddl_tail(vf->base) ;
+ vff->as_one = vff->set ;
+ if (clear_marks)
+ {
+ vff->hold_mark = false ; /* Discard marks */
+ vff->end_mark = false ;
+ } ;
- if (tail != NULL)
+ if (vff->set)
{
- while (ddl_head(vf->base) != tail)
+ vio_fifo_lump lump ;
+
+ while (1)
{
- vio_fifo_lump lump ;
- ddl_pop(&lump, vf->base, list) ;
+ lump = ddl_head(vff->base) ;
+ if (lump == ddl_tail(vff->base))
+ break ;
+
+ ddl_pop(&lump, vff->base, list) ;
XFREE(MTYPE_VIO_FIFO_LUMP, lump) ;
} ;
- vf->rdr_lump = NULL ; /* clear rdr */
- vf->rdr_ptr = NULL ;
-
- vf->one = true ;
- vio_fifo_ptr_reset(vf, tail) ;
- }
- else
- vio_fifo_ptr_unset(vf) ;
+ vio_fifo_ptr_reset(vff, lump) ;
+ } ;
- if (vf->spare != NULL)
- XFREE(MTYPE_VIO_FIFO_LUMP, vf->spare) ; /* sets vf->spare = NULL */
+ if (vff->spare != NULL)
+ XFREE(MTYPE_VIO_FIFO_LUMP, vff->spare) ; /* sets vff->spare = NULL */
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
} ;
/*------------------------------------------------------------------------------
@@ -266,279 +571,226 @@ vio_fifo_clear(vio_fifo vf)
* Returns: room available as described
*/
extern size_t
-vio_fifo_room(vio_fifo vf)
+vio_fifo_room(vio_fifo vff)
{
- if (vf->put_ptr != NULL)
- return vf->put_end - vf->put_ptr ;
+ if (vff->set)
+ return vff->put_end - vff->put_ptr ;
else
- return vf->size ;
+ return vff->size ;
} ;
/*------------------------------------------------------------------------------
- * Allocate another lump for putting into.
+ * Need a new lump to put stuff into.
*
- * Call when (vf->put_ptr >= vf->put_end) -- asserts that they are equal.
+ * Call when (vff->put_ptr >= vff->put_end) -- asserts that they are equal.
*
- * Set the put_ptr/put_end pointers to point at the new lump.
+ * If the FIFO is, in fact, empty but with at least one lump, then does not
+ * allocate anything more, but releases all lumps but the last lump and then
+ * resets all pointers to the start of that lump.
*
- * If this is the first lump allocated, set the get_ptr/get_end pointers too.
+ * Otherwise, allocates a new lump (or reuses the spare) to the requested size,
+ * and updates all pointers as required. (Allocates to vff->size if
+ * requested size is zero.)
*
- * If have just filled the first lump on the list, update the get_end pointer
- * to reflect the fact that the out lump is now full.
+ * NB: if there is an end_mark, makes sure that it advances with the put_ptr
+ * if currently end_end == the put_ptr.
*/
-extern void
-vio_fifo_lump_new(vio_fifo vf, size_t size)
+Private void
+vio_fifo_lump_new(vio_fifo vff, size_t size)
{
vio_fifo_lump lump ;
- int first_alloc ;
+ size_t std_size ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ passert(vff->put_ptr == vff->put_end) ; /* must be end of tail lump
+ (or both NULL) */
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
- passert(vf->put_ptr == vf->put_end) ; /* must be end of tail lump */
+ /* First, make sure that the get side is synchronised, which may advance
+ * various pointers, release lumps and possibly reset now empty FIFO.
+ *
+ * If synchronising the get side does not yield space, then the FIFO is
+ * either not set or it has something in it (including held stuff).
+ */
+ vio_fifo_sync_get(vff) ;
- if (vf->one)
- vf->get_end = vf->put_ptr ; /* update get_end */
+ if (vff->put_ptr < vff->put_end)
+ return ; /* Done if have created space */
- lump = ddl_tail(vf->base) ;
+ /* If we can use the spare, do so, otherwise make it to size */
+ lump = vff->spare ;
+ vff->spare = NULL ;
- first_alloc = (lump == NULL) ; /* extra initialisation needed */
+ std_size = vio_fifo_size(vff->size) ; /* normalised standard */
- if (first_alloc)
- assert(vf->put_ptr == NULL) ; /* must all be NULL together */
+ if (size <= std_size)
+ size = std_size ; /* use standard as a minimum */
else
- assert(vf->put_ptr == lump->end) ; /* must be end of tail lump */
+#if VIO_FIFO_DEBUG
+ size |= 3 ; /* most of the time a little bigger */
+#else
+ size = vio_fifo_size(size) ; /* normalise requested size */
+#endif
- size = vio_fifo_size(size) ;
-
- if ((vf->spare != NULL) && (vf->spare->size >= size))
- {
- lump = vf->spare ;
- vf->spare = NULL ;
- }
- else
+ if ((lump == NULL) || (lump->size < size))
{
- lump = XMALLOC(MTYPE_VIO_FIFO_LUMP,
- offsetof(vio_fifo_lump_t, data[size])) ;
+ /* If there was no spare, lump == NULL and XREALLOC == XMALLOC.
+ * If there was a spare that was too small, better extend that than
+ * keep a sub-standard spare.
+ */
+ lump = XREALLOC(MTYPE_VIO_FIFO_LUMP, lump,
+ offsetof(vio_fifo_lump_t, data[size])) ;
lump->size = size ;
+ lump->end = lump->data + size ;
} ;
- lump->end = lump->data + lump->size ;
-
- ddl_append(vf->base, lump, list) ;
-
- vf->one = first_alloc ;
-
- vf->put_ptr = lump->data ;
- vf->put_end = lump->end ;
-
- if (first_alloc)
- {
- vf->get_ptr = vf->put_ptr ; /* get_ptr == put_ptr => empty */
- vf->get_end = vf->put_ptr ;
- } ;
-
- VIO_FIFO_DEBUG_VERIFY(vf) ;
-} ;
+ ddl_append(vff->base, lump, list) ;
-/*------------------------------------------------------------------------------
- * Release lump, head or tail (or both) and update pointers.
- *
- * Note that this does release the lump if it is the only lump.
- *
- * Do nothing if nothing is yet allocated.
- *
- * If releasing the only lump in the FIFO, resets all pointers to NULL.
- *
- * If releasing the head lump:
- *
- * * the lump MUST be finished with -- so vf->get_ptr must be at the end
- *
- * If releasing the only lump, the FIFO MUST be empty.
- *
- * * if the lump is the current vf->rdr_lump, the reader must be at the
- * end too -- ie it must be the same as the vf->get_ptr !
- *
- * If releasing the tail lump:
- *
- * * the lump MUST be empty
- *
- * If releasing the only lump, the FIFO MUST be empty.
- *
- * * if the lump is the current vf->rdr_lump, the reader must be at the
- * end too -- ie it must be the same as the vf->get_ptr !
- */
-static void
-vio_fifo_lump_release(vio_fifo vf, vio_fifo_lump lump)
-{
- vio_fifo_lump head ;
- vio_fifo_lump tail ;
- vio_fifo_lump free ;
- bool release_head ;
- bool release_tail ;
-
- VIO_FIFO_DEBUG_VERIFY(vf) ;
-
- /* Prepare and check whether removing head or tail (or both) */
- head = ddl_head(vf->base) ;
- tail = ddl_tail(vf->base) ;
-
- release_head = (lump == head) ;
- release_tail = (lump == tail) ;
-
- assert(release_head || release_tail) ;
-
- /* Unless nothing ever allocated -- release the lump. */
- free = lump ; /* expect to free the lump */
- if (lump != NULL)
+ /* If not just allocated the first lump, set the put_ptr and normalise any
+ * end_mark or update end_lump.
+ *
+ * If is first block to be allocated, set all pointers, taking into account
+ * any end_mark and/or hold_mark.
+ */
+ if (vff->set)
{
- vio_fifo_lump keep ;
+ /* Allocated new lump on the end of a not-empty fifo.
+ *
+ * Have to watch out for cases where the get_ptr and/or end_end are
+ * equal to the put_ptr, which is about to move to the start of a new
+ * lump, so as to avoid ambiguity !
+ */
- /* Consistency checks */
- if (release_head)
+ if (vff->get_ptr == vff->put_ptr)
{
- if (release_tail)
- assert(vf->get_ptr == vf->put_ptr) ;
- else
- assert(vf->get_ptr == lump->end) ;
+ /* If the fifo is empty, the vio_fifo_sync_get() above will have
+ * spotted it. So can only get here iff there is something held in
+ * the fifo behind the get_ptr.
+ *
+ * If was as_one, is still as_one.
+ *
+ * If was not as_one then must have end_mark with end_end == put_ptr,
+ * which will be dealt with below.
+ */
+ assert(vff->hold_mark && (vff->get_ptr != vff->hold_ptr)) ;
- if (vf->rdr_lump == lump)
- assert(vf->rdr_ptr == vf->get_ptr) ;
+ vff->get_ptr = lump->data ;
+ vff->get_end = lump->data ;
+ vff->get_lump = lump ;
}
- else if (release_tail)
+ else
{
- assert(vf->put_ptr == lump->data) ;
-
- if (vf->rdr_lump == lump)
- assert(vf->rdr_ptr == vf->put_ptr) ;
+ /* If were as_one, then will no longer be, because put_ptr is about
+ * to advance to the start of the new lump.
+ */
+ vff->as_one = false ;
} ;
- /* Remove lump from FIFO and decide whether to keep as spare, or
- * which of spare and this to free.
- */
- ddl_del(vf->base, lump, list) ;
-
- keep = vf->spare ; /* expect to keep current spare */
+ if (vff->end_mark)
+ {
+ assert(!vff->as_one) ;
- if ((keep == NULL) || (keep->size < lump->size))
+ /* The end_end follows the put_ptr iff they are equal
+ *
+ * If the get_ptr also equals the put_ptr, it has already advanced.
+ */
+ if (vff->end_end == vff->put_ptr)
+ {
+ vff->end_end = lump->data ;
+ vff->end_lump = lump ;
+ } ;
+ }
+ else
{
- keep = lump ;
- free = vf->spare ;
+ /* No end_mark => end_lump simply follows the put_ptr. */
+ vff->end_lump = lump ;
} ;
- vf->spare = keep ;
-
- head = ddl_head(vf->base) ; /* changed if released head */
- tail = ddl_tail(vf->base) ; /* changed if released tail */
+ vff->put_ptr = lump->data ;
+ vff->put_end = vio_fifo_true_lump_size(lump) ;
+ }
+ else
+ {
+ /* Allocated lump for previously empty fifo -- set all pointers to the
+ * start of the lump, except for put_end.
+ */
+ vff->set = true ;
+ vio_fifo_ptr_set(vff, lump) ;
} ;
- /* Now update pointers... depending on what was released and what have
- * left.
- */
- if (head == NULL)
- {
- /* Deal with FIFO that now has no lumps or had none to start with */
- if (lump != NULL)
- assert(vf->one) ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
+} ;
- vio_fifo_ptr_unset(vf) ;
- }
- else
+/*------------------------------------------------------------------------------
+ * Release the given lump, provided it is neither get_lump nor end_lump.
+ *
+ * If don't have a spare lump, keep this one. Otherwise, keep larger of
+ * this and current spare.
+ */
+static void
+vio_fifo_release_lump(vio_fifo vff, vio_fifo_lump lump)
+{
+ assert(lump != NULL) ;
+ assert(lump != vff->get_lump) ;
+ assert(lump != vff->end_lump) ;
+
+ if (vff->spare != NULL)
{
- /* Have at least one lump left -- so must have had at least two ! */
- assert(!vf->one) ;
+ vio_fifo_lump free ;
- vf->one = (head == tail) ; /* update */
+ free = vff->spare ;
- if (release_head)
+ if (free->size > lump->size)
{
- /* Released the head.
- *
- * Update the vf->get_ptr and the vf->get_end.
- */
- vf->get_ptr = head->data ;
- if (vf->one)
- vf->get_end = vf->put_ptr ;
- else
- vf->get_end = head->end ;
-
- /* Update vf->rdr_ptr and vf->rdr_lump. */
- if (vf->rdr_lump == lump)
- {
- vf->rdr_lump = head ;
- vf->rdr_ptr = head->data ;
- } ;
- }
- else
- {
- /* Released the tail.
- * Update the vf->put_ptr and vf->put_end
- */
- vf->put_ptr = vf->put_end = tail->end ;
-
- /* Update vf->rdr_ptr and vf->rdr_lump. */
- if (vf->rdr_lump == lump)
- {
- vf->rdr_lump = tail ;
- vf->rdr_ptr = tail->end ;
- } ;
+ free = lump ;
+ lump = vff->spare ;
} ;
- } ;
-
- /* Finally, free any lump that is actually to be freed */
- if (free != NULL)
- XFREE(MTYPE_VIO_FIFO_LUMP, free) ;
+ XFREE(MTYPE_VIO_FIFO_LUMP, free) ;
+ } ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ vff->spare = lump ;
} ;
/*------------------------------------------------------------------------------
- * Re-allocate lump for putting into.
- *
- * Call when vf->put_ptr == start of last lump, and that lump is not big
- * enough !
+ * Release lumps from head up to, but not including, given lump.
*
- * There must be at least one lump.
+ * NB: must be "set" and must not attempt to release the get_lump or the
+ * end_lump.
*
- * Updates put_ptr/put_end pointers to point at the new lump.
+ * So MUST advance at least the get_lump before calling this.
*
- * Updates get_ptr/get_end pointers if required.
- *
- * Updates rdr_ptr if required.
+ * It is the caller's responsibility to update get and/or hold pointers.
*/
static void
-vio_fifo_lump_renew(vio_fifo vf, vio_fifo_lump lump, size_t size)
+vio_fifo_release_head(vio_fifo vff, vio_fifo_lump upto)
{
- bool rdr_set ;
-
- VIO_FIFO_DEBUG_VERIFY(vf) ;
-
- /* FIFO may not be completely empty.
- * This must be the last lump.
- * The last lump must be empty.
- */
- assert((lump != NULL) && (lump == ddl_tail(vf->base))) ;
-
- /* Remove the last, *empty* lump, and update all pointers to suit. */
- rdr_set = (vf->rdr_lump == lump) ;
-
- vio_fifo_lump_release(vf, lump) ;
+ assert(vff->set) ;
- /* Now allocate a new lump with the required size */
- vio_fifo_lump_new(vf, size) ;
-
- /* Restore the rdr_ptr, if required */
- if (rdr_set)
+ while (upto != ddl_head(vff->base))
{
- vio_fifo_lump tail ;
+ vio_fifo_lump lump ;
+ vio_fifo_release_lump(vff, ddl_pop(&lump, vff->base, list)) ;
+ } ;
+} ;
- tail = ddl_tail(vf->base) ;
+/*------------------------------------------------------------------------------
+ * Release lumps from tail back to, but not including, given lump.
+ *
+ * NB: must be "set" and must not attempt to release the get_lump or the
+ * end_lump.
+ *
+ * It is the caller's responsibility to update end and/or put pointers.
+ */
+static void
+vio_fifo_release_tail(vio_fifo vff, vio_fifo_lump backto)
+{
+ assert(vff->set) ;
- vf->rdr_lump = tail ;
- vf->rdr_ptr = tail->data ;
+ while (backto != ddl_tail(vff->base))
+ {
+ vio_fifo_lump lump ;
+ vio_fifo_release_lump(vff, ddl_crop(&lump, vff->base, list)) ;
} ;
-
- VIO_FIFO_DEBUG_VERIFY(vf) ;
} ;
/*==============================================================================
@@ -546,196 +798,461 @@ vio_fifo_lump_renew(vio_fifo vf, vio_fifo_lump lump, size_t size)
*/
/*------------------------------------------------------------------------------
- * Store 'n' bytes -- allocate new lump if current is exhausted.
+ * Put 'n' bytes -- allocating as required.
*/
extern void
-vio_fifo_put(vio_fifo vf, const char* src, size_t n)
+vio_fifo_put_bytes(vio_fifo vff, const char* src, size_t n)
{
- size_t take ;
-
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
while (n > 0)
{
- if (vf->put_ptr >= vf->put_end)
- vio_fifo_lump_new(vf, vf->size) ; /* traps put_ptr > put_end */
+ size_t take ;
- take = (vf->put_end - vf->put_ptr) ;
+ if (vff->put_ptr >= vff->put_end)
+ vio_fifo_lump_new(vff, 0) ; /* traps put_ptr > put_end */
+
+ take = (vff->put_end - vff->put_ptr) ;
if (take > n)
take = n ;
- memcpy(vf->put_ptr, src, take) ;
- vf->put_ptr += take ;
+ memcpy(vff->put_ptr, src, take) ;
+ vff->put_ptr += take ;
src += take ;
n -= take ;
} ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
} ;
/*------------------------------------------------------------------------------
- * Formatted print to fifo -- cf printf()
+ * Formatted print to FIFO -- cf printf()
*
* Returns: >= 0 -- number of bytes written
* < 0 -- failed (unlikely though that is)
*/
extern int
-vio_fifo_printf(vio_fifo vf, const char* format, ...)
+vio_fifo_printf(vio_fifo vff, const char* format, ...)
{
va_list args;
int len ;
va_start (args, format);
- len = vio_fifo_vprintf(vf, format, args);
+ len = vio_fifo_vprintf(vff, format, args);
va_end (args);
return len;
} ;
/*------------------------------------------------------------------------------
- * Formatted print to fifo -- cf vprintf()
+ * Formatted print to FIFO -- cf vprintf()
+ *
+ * Does nothing if vff is NULL !
*
* Returns: >= 0 -- number of bytes written
* < 0 -- failed (unlikely though that is)
+ *
+ * NB: does not extend an existing lump in order to make things fit, but
+ * splits the result across two lumps. This ensures that at all times
+ * pointers into existing lumps are stable -- so pointer returned by
+ * vio_fifo_get_lump(), for example, cannot be upset !
*/
extern int
-vio_fifo_vprintf(vio_fifo vf, const char *format, va_list args)
+vio_fifo_vprintf(vio_fifo vff, const char *format, va_list args)
{
va_list ac ;
int len ;
int have ;
- size_t size ;
- vio_fifo_lump lump ;
+ int had ;
+ int need ;
+ char* last ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ if (vff == NULL)
+ return 0 ;
- size = vf->size ; /* standard allocation size */
- while (1)
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
+
+ /* First the simple way.
+ *
+ * Note that vsnprintf() returns the length of what it would like to
+ * have produced, if it had the space. That length does not include
+ * the trailing '\0'. In the meantime, it has output as much as it
+ * can, with a trailing '\0'.
+ */
+ assert(vff->put_ptr <= vff->put_end) ;
+
+ have = vff->put_end - vff->put_ptr ; /* what can do in current lump
+ if any. */
+ va_copy(ac, args) ;
+ len = vsnprintf(vff->put_ptr, have, format, ac) ;
+ va_end(ac) ;
+
+ if ((len < have) || (len == 0)) /* OK, or failed ! */
{
- /* Find what space is left in the tail lump, and allocate a new,
- * empty lump if required.
- */
- if (vf->put_ptr >= vf->put_end)
- vio_fifo_lump_new(vf, size) ; /* traps put_ptr > put_end */
+ if (len > 0)
+ vff->put_ptr += len ; /* advance put_ptr as required */
- have = vf->put_end - vf->put_ptr ;
- assert(have > 0) ;
+ return len ;
+ } ;
- /* Note that vsnprintf() returns the length of what it would like to
- * have produced, if it had the space. That length does not include
- * the trailing '\0'.
- */
- va_copy(ac, args) ;
- len = vsnprintf(vf->put_ptr, have, format, ac) ;
- va_end(ac) ;
+ /* Now know that we need len + 1 bytes in all to complete the task, and
+ * that it has written have - 1 bytes to the existing lump (if any).
+ *
+ * Also, len > 0.
+ *
+ * Allocate a new lump in which we can write the entire result, even if
+ * that is a non-standard size.
+ */
+ need = len + 1 ; /* need includes the '\0' */
+ had = have ;
- if (len < have)
- {
- if (len < 0)
- break ; /* quit if failed */
+ if (had > 0)
+ vff->put_ptr += had ; /* step to the end */
+ last = vff->put_ptr ; /* point at end of lump (NULL if none) */
- vf->put_ptr += len ;
- break ; /* done */
- } ;
+ vio_fifo_lump_new(vff, need) ;/* new lump to do it all */
- /* Not able to complete the operation in the current buffer.
- *
- * If the required space (len + 1) is greater than the standard
- * allocation, then need to increase the allocation for the next lump.
- *
- * If the current lump is empty, need to renew it with a fresh lump of
- * the now known required size.
- *
- * If the current lump is not empty, need to cut the end off and then
- * allocate a fresh lump (of the standard or now known required size).
- */
- if (len >= (int)size)
- size = len + 1 ; /* need a non-standard size */
+ have = vff->put_end - vff->put_ptr ;
+ assert(have >= need) ; /* have >= 2 */
- lump = ddl_tail(vf->base) ;
+ /* We really expect to get the same result a second time ! */
+ va_copy(ac, args) ;
+ len = vsnprintf(vff->put_ptr, have, format, ac) ;
+ va_end(ac) ;
- if (vf->put_ptr == lump->data)
- /* Need to replace the last, empty, lump with another empty lump, but
- * big enough.
- */
- vio_fifo_lump_renew(vf, lump, size) ;
- else
- /* Need to cut this lump short, and allocate new lump at top of loop.
- */
- lump->end = vf->put_end = vf->put_ptr ;
+ /* Since have >= what previously said it needed, things have gone
+ * badly wrong if the new len is >= have.
+ *
+ * Also, things have gone badly wrong if new len is < what previously
+ * had, which was then not enough !
+ *
+ * Also, things have gone badly wrong if new len == 0, because previously
+ * it was > 0 !
+ */
+ if ((len >= have) || (len < had) || (len == 0))
+ return (len < 0) ? len : -1 ;
+
+ /* Move result around if required -- len >= had */
+ have = len ;
+ if (had > 0)
+ {
+ char* frag ;
+ frag = vff->put_ptr + had ; /* first character to keep */
+ *(last - 1) = *(frag - 1) ; /* replace the '\0' */
+
+ have -= had ; /* amount to keep */
+ if (have > 0)
+ memmove(vff->put_ptr, frag, have) ;
} ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ /* Advance the put_ptr past what we have in the new lump. */
+ vff->put_ptr += have ;
+
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
return len ;
} ;
+/*------------------------------------------------------------------------------
+ * Read part of file into FIFO -- assuming non-blocking file
+ *
+ * Will read up to the end of the lump which meets, or exceeds the number of
+ * bytes requested, or until would block.
+ *
+ * Returns: 0..n -- number of bytes read
+ * -1 => failed -- see errno
+ * -2 => EOF met immediately
+ *
+ * Note: will work perfectly well for a non-blocking file -- which should
+ * never return EAGAIN/EWOULDBLOCK, so will return from here with
+ * something, error or EOF.
+ */
+extern int
+vio_fifo_read_nb(vio_fifo vff, int fd, size_t request)
+{
+ size_t total ;
+
+ total = 0 ;
+
+ do
+ {
+ int got ;
+
+ if (vff->put_ptr >= vff->put_end)
+ vio_fifo_lump_new(vff, 0) ; /* traps put_ptr > put_end */
+
+ got = read_nb(fd, vff->put_ptr, vff->put_end - vff->put_ptr) ;
+
+ if (got <= 0)
+ {
+ if (got == -2) /* EOF met */
+ return (total > 0) ? (int)total : got ;
+ else
+ return (got == 0) ? (int)total : got ;
+ } ;
+
+ vff->put_ptr += got ;
+ total += got ;
+
+ } while (total < request) ;
+
+ return total ;
+} ;
+
/*==============================================================================
- * Get data from the FIFO.
+ * Copy operations -- from one fifo to another.
*/
-static bool vio_fifo_get_next_lump(vio_fifo vf) ;
+/*------------------------------------------------------------------------------
+ * Copy src fifo (everything from get_ptr to end_mark or put_ptr) to dst fifo.
+ *
+ * Create a dst fifo if there isn't one.
+ *
+ * Appends to the dst fifo.
+ *
+ * Does not change the src fifo in any way.
+ */
+extern vio_fifo
+vio_fifo_copy(vio_fifo dst, vio_fifo src)
+{
+ if (dst == NULL)
+ dst = vio_fifo_init_new(dst, 0) ;
+
+ if ((src != 0) && (src->set))
+ {
+ vio_fifo_lump src_lump ;
+ char* src_ptr ;
+
+ if (src->get_ptr >= src->get_end)
+ vio_fifo_sync_get(src) ;
+
+ src_lump = src->get_lump ;
+ src_ptr = src->get_ptr ;
+
+ while (1)
+ {
+ char* src_end ;
+
+ if (src_lump != src->end_lump)
+ src_end = src_lump->end ;
+ else
+ src_end = (src->end_mark) ? src->end_end : src->put_ptr ;
+
+ vio_fifo_put_bytes(dst, src_ptr, src_end - src_ptr) ;
+
+ if (src_lump == src->end_lump)
+ break ;
+
+ src_lump = ddl_next(src_lump, list) ;
+ src_ptr = src_lump->data ;
+ } ;
+ } ;
+
+ return dst ;
+} ;
/*------------------------------------------------------------------------------
- * Get ready to read something out of the FIFO.
+ * Copy tail of src fifo (everything from end_mark to put_ptr) to dst fifo.
+ *
+ * Create a dst fifo if there isn't one.
*
- * Makes sure vf->get_end is up to date (if required) and if the FIFO is not
- * empty, makes sure vf->get_ptr points at the next byte to be read.
+ * Appends to the dst fifo.
*
- * Returns: true <=> there is something in the FIFO.
+ * Does not change the src fifo in any way.
*/
-static inline bool
-vio_fifo_get_ready(vio_fifo vf)
+extern vio_fifo
+vio_fifo_copy_tail(vio_fifo dst, vio_fifo src)
{
- assert(vf->rdr_lump == NULL) ;
+ if (dst == NULL)
+ dst = vio_fifo_init_new(dst, 0) ;
- if (vf->one)
- vf->get_end = vf->put_ptr ; /* make sure have everything */
+ if ((src != 0) && (src->end_mark) && (src->set))
+ {
+ vio_fifo_lump src_lump ;
+ char* src_ptr ;
+ vio_fifo_lump tail ;
- if (vf->get_ptr >= vf->get_end)
- if (!vio_fifo_get_next_lump(vf))
- return 0 ; /* quit now if nothing there */
+ src_lump = src->end_lump ;
+ src_ptr = src->end_end ;
+ tail = ddl_tail(src->base) ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ while (1)
+ {
+ char* src_end ;
- return 1 ;
+ if (src_lump != tail)
+ src_end = src_lump->end ;
+ else
+ src_end = src->put_ptr ;
+
+ vio_fifo_put_bytes(dst, src_ptr, src_end - src_ptr) ;
+
+ if (src_lump == tail)
+ break ;
+
+ src_lump = ddl_next(src_lump, list) ;
+ src_ptr = src_lump->data ;
+ } ;
+ } ;
+
+ return dst ;
} ;
+/*==============================================================================
+ * End Mark Operations.
+ */
+
+/*------------------------------------------------------------------------------
+ * Set end_mark at the current put position.
+ *
+ * If there was an end_mark before, move it (forward) to the current put_ptr,
+ * which keeps everything in between in the FIFO.
+ *
+ * Set the get_end to the new reality.
+ */
+extern void
+vio_fifo_set_end_mark(vio_fifo vff)
+{
+ if (vff->set)
+ {
+ vio_fifo_sync_get(vff) ; /* in case is currently empty */
+
+ vff->end_lump = ddl_tail(vff->base) ;
+ vff->end_end = vff->put_ptr ;
+
+ vff->get_end = (vff->get_lump == vff->end_lump) ? vff->end_end
+ : vff->get_lump->end ;
+ } ;
+
+ vff->as_one = false ; /* not as_one with end_mark */
+ vff->end_mark = true ;
+
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * If there is an end mark, advance it to the put_ptr.
+ *
+ * If there was an end_mark before, move it (forward) to the current put_ptr,
+ * which keeps everything in between in the FIFO.
+ *
+ * If there was no end_mark before, do nothing.
+ *
+ * Set the get_end to the new reality.
+ */
+extern void
+vio_fifo_step_end_mark(vio_fifo vff)
+{
+ if (vff->end_mark)
+ vio_fifo_set_end_mark(vff) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * If there is an end_mark, clear it -- everything between end mark and
+ * current put_ptr is kept in the FIFO.
+ *
+ * Set the get_end to the new reality.
+ */
+extern void
+vio_fifo_clear_end_mark(vio_fifo vff)
+{
+ if (vff->end_mark)
+ {
+ vff->end_mark = false ;
+
+ if (vff->set)
+ {
+ vff->end_lump = ddl_tail(vff->base) ;
+ vff->end_end = NULL ;
+
+ vff->as_one = (vff->get_lump == vff->end_lump) ;
+ /* since now no end_mark */
+ if (!vff->as_one)
+ vff->get_end = vff->get_lump->end ;
+ /* would have been end_end */
+
+ vio_fifo_sync_get(vff) ; /* sets get_end if as_one and
+ tidies up if now empty (!) */
+ } ;
+ } ;
+
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Move put_ptr back to the end mark, if any, and discard data.
+ *
+ * If there is an end_mark, keep it if required.
+ *
+ * If there is no end mark, do nothing.
+ */
+extern void
+vio_fifo_back_to_end_mark(vio_fifo vff, bool keep)
+{
+ if (vff->end_mark)
+ {
+ if (vff->set)
+ {
+ vio_fifo_release_tail(vff, vff->end_lump) ;
+
+ vff->put_ptr = vff->end_end ;
+ vff->put_end = vio_fifo_true_lump_size(vff->end_lump) ;
+
+ /* If retaining the existing end_mark, we retain the end_end and
+ * the current as_one (false).
+ *
+ * Otherwise...
+ */
+ if (!keep)
+ {
+ vff->end_mark = false ;
+ vff->end_end = NULL ;
+ vff->as_one = (vff->get_lump == vff->end_lump) ;
+ } ;
+
+ vio_fifo_sync_get(vff) ; /* in case now empty */
+
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
+ }
+ else
+ vff->end_mark = keep ;
+ } ;
+} ;
+
+/*==============================================================================
+ * Get data from the FIFO.
+ */
+
/*------------------------------------------------------------------------------
* Get upto 'n' bytes.
*
* Returns: number of bytes got -- may be zero.
*/
extern size_t
-vio_fifo_get(vio_fifo vf, void* dst, size_t n)
+vio_fifo_get_bytes(vio_fifo vff, void* dst, size_t n)
{
size_t have ;
void* dst_in ;
- if (!vio_fifo_get_ready(vf))
- return 0 ; /* quit now if nothing there */
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
dst_in = dst ;
- while (n > 0)
+ while (vio_fifo_sync_get(vff) && (n > 0))
{
- have = vf->get_end - vf->get_ptr ;
+ have = vff->get_end - vff->get_ptr ;
if (have > n)
have = n ;
- memcpy(dst, vf->get_ptr, have) ;
- vf->get_ptr += have ;
+ memcpy(dst, vff->get_ptr, have) ;
+ vff->get_ptr += have ;
dst = (char*)dst + have ;
- if (vf->get_ptr >= vf->get_end) /* deal with exhausted lump */
- if (!vio_fifo_get_next_lump(vf))
- break ; /* quit if nothing more to come */
-
n -= have ;
} ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
return (char*)dst - (char*)dst_in ;
} ;
@@ -752,76 +1269,84 @@ vio_fifo_get(vio_fifo vf, void* dst, size_t n)
* Returns: 0x00..0xFF -- byte value (as an int)
* -1 => FIFO is empty.
*/
-
-extern int
-vio_fifo_get_next_byte(vio_fifo vf)
+Private int
+vio_fifo_get_next_byte(vio_fifo vff)
{
- unsigned char u ;
-
- if (!vio_fifo_get_ready(vf))
- return -1 ; /* quit now if nothing there */
-
- u = *vf->get_ptr++ ;
-
- /* As soon as reach the end want either to discard empty lump, or reset
- * the pointers.
- */
- if (vf->get_ptr >= vf->get_end) /* deal with exhausted lump */
- vio_fifo_get_next_lump(vf) ;
-
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ if (vio_fifo_sync_get(vff))
+ return (uchar)*vff->get_ptr++ ;
- return u ;
+ return -1 ;
} ;
/*------------------------------------------------------------------------------
- * Get pointer to a lump of bytes.
+ * Get pointer to as many bytes as are available in the current lump (or next
+ * lump if nothing available in the current).
*
* Returns: address of next byte to get, *p_have = number of bytes available
* or: NULL => FIFO is empty, *p_have = 0
*
- * If the FIFO is not empty, will return pointer to at least one byte.
- *
- * Returns number of bytes to the end of the current lump. There may be
- * further lumps beyond the current one.
+ * If the FIFO is not empty and not at the end_mark, will return pointer to at
+ * least one byte. There may be more bytes to get in further lumps.
*/
extern void*
-vio_fifo_get_lump(vio_fifo vf, size_t* p_have)
+vio_fifo_get(vio_fifo vff, size_t* p_have)
{
- if (!vio_fifo_get_ready(vf))
+ if (vio_fifo_sync_get(vff))
{
- *p_have = 0 ;
- return NULL ;
+ *p_have = (vff->get_end - vff->get_ptr) ;
+ return vff->get_ptr ;
} ;
- *p_have = (vf->get_end - vf->get_ptr) ;
- return vf->get_ptr ;
+ *p_have = 0 ;
+ return NULL ;
} ;
/*------------------------------------------------------------------------------
- * Advance FIFO to position reached.
+ * Step FIFO past bytes used.
*
- * Having done vio_fifo_get_lump(), can take any number of bytes (up to the
- * number that "have"), then call this function to advance the pointers.
+ * Can be called after a vio_fifo_get() or vio_fifo_step_get().
*
- * The "here" argument must the the address returned by vio_fifo_get_lump()
- * plus the number of bytes taken.
+ * NB: the "step" argument MUST not exceed the "have" previously returned.
*/
extern void
-vio_fifo_got_upto(vio_fifo vf, void* here)
+vio_fifo_step(vio_fifo vff, size_t step)
+{
+ vff->get_ptr += step ;
+ vio_fifo_sync_get(vff) ; /* ensure up to date with that */
+
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Step FIFO past bytes used, the get pointer to as many bytes as are available
+ * in the current lump (or next lump if nothing available in the current).
+ *
+ * Same as vio_fifo_step() followed by vio_fifo_get().
+ *
+ * Can be called after a vio_fifo_get() or vio_fifo_step_get().
+ *
+ * NB: the "step" argument MUST not exceed the "have" previously returned.
+ */
+extern void*
+vio_fifo_step_get(vio_fifo vff, size_t* p_have, size_t step)
{
- vf->get_ptr = here ;
+ vff->get_ptr += step ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ if (vio_fifo_sync_get(vff))
+ {
+ *p_have = (vff->get_end - vff->get_ptr) ;
+ return vff->get_ptr ;
+ } ;
- if (vf->get_ptr >= vf->get_end)
- vio_fifo_get_next_lump(vf) ;
+ *p_have = 0 ;
+ return NULL ;
} ;
/*------------------------------------------------------------------------------
* Write contents of FIFO -- assuming non-blocking file
*
- * Will write all of FIFO, or upto but excluding the last lump.
+ * Will write all of FIFO up to end mark or put_ptr, or upto but excluding
+ * the last lump.
*
* Returns: > 0 => blocked
* 0 => all gone (up to last lump if !all)
@@ -831,338 +1356,314 @@ vio_fifo_got_upto(vio_fifo vf, void* here)
* never return EAGAIN/EWOULDBLOCK, so will return from here "all gone".
*/
extern int
-vio_fifo_write_nb(vio_fifo vf, int fd, bool all)
+vio_fifo_write_nb(vio_fifo vff, int fd, bool all)
{
char* src ;
size_t have ;
- int done ;
- while ((src = vio_fifo_get_lump(vf, &have)) != NULL)
+ while ((src = vio_fifo_get(vff, &have)) != NULL)
{
- if (!all && vf->one)
- break ; /* don't write last lump */
+ int done ;
+
+ if ((vff->get_lump == vff->end_lump) && !all)
+ break ; /* don't write last lump */
done = write_nb(fd, src, have) ;
if (done < 0)
- return -1 ; /* failed */
+ return -1 ; /* failed */
- vio_fifo_got_upto(vf, src + done) ;
+ vio_fifo_step(vff, done) ;
if (done < (int)have)
- return 1 ; /* blocked */
+ return 1 ; /* blocked */
} ;
- return 0 ; /* all gone */
-} ;
-
-/*------------------------------------------------------------------------------
- * Get the current rdr_end value.
- *
- * Unlike get_end, do not have a field for this, but find it each time.
- */
-inline static char*
-vio_fifo_rdr_end(vio_fifo vf)
-{
- if (vf->rdr_lump == ddl_tail(vf->base))
- return vf->put_ptr ;
- else
- return vf->rdr_lump->end ;
+ return 0 ; /* all gone */
} ;
/*------------------------------------------------------------------------------
- * Get the current rdr position -- sets it up if not currently set.
- *
- * Returns: address of next byte to get, *p_have = number of bytes available
- * or: NULL => FIFO is empty, *p_have = 0
+ * Write contents of FIFO -- assuming blocking file
*
- * If the FIFO is not empty, will return pointer to at least one byte.
+ * Will write all of FIFO up to end mark or put_ptr.
*
- * Returns number of bytes to the end of the current lump. There may be
- * further lumps beyond the current one.
+ * Returns: 0 => all gone
+ * < 0 => failed -- see errno
*
- * NB: unless returns FIFO is empty, it is a mistake to now do any "get"
- * operation other than vio_fifo_step_rdr(), until do vio_fifo_sync_rdr()
- * or vio_fifo_drop_rdr.
+ * Note: will work perfectly well for a non-blocking file -- which should
+ * never return EAGAIN/EWOULDBLOCK, so will return from here "all gone".
*/
-extern void*
-vio_fifo_get_rdr(vio_fifo vf, size_t* p_have)
+extern int
+vio_fifo_fwrite(vio_fifo vff, FILE* file)
{
- if (!vio_fifo_get_ready(vf))
- {
- *p_have = 0 ;
- return NULL ;
- } ;
+ char* src ;
+ size_t have ;
- if (vf->rdr_lump == NULL) /* set up new rdr if required */
+ while ((src = vio_fifo_get(vff, &have)) != NULL)
{
- vf->rdr_lump = ddl_head(vf->base) ;
- vf->rdr_ptr = vf->get_ptr ;
- } ;
+ size_t done ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ done = fwrite(src, have, 1, file) ;
- *p_have = vio_fifo_rdr_end(vf) - vf->rdr_ptr ;
- return vf->rdr_ptr ;
+ if (done < 1)
+ return -1 ; /* failed */
+
+ vio_fifo_step(vff, have) ;
+ } ;
+
+ return 0 ; /* all gone */
} ;
/*------------------------------------------------------------------------------
- * Step the rdr forward by the given number of bytes.
- *
- * Returns: address of next byte to get, *p_have = number of bytes available
- * or: NULL => FIFO is empty, *p_have = 0
- *
- * If the FIFO is not empty, will return pointer to at least one byte.
+ * Skip get_ptr to the current end -- which may be the current end_mark.
*
- * Returns number of bytes to the end of the current lump. There may be
- * further lumps beyond the current one.
- *
- * NB: this does not change the get pointers, so all the data being stepped
- * over is preserved in the FIFO, until vio_fifo_sync_rdr().
- *
- * NB: the step may NOT exceed the last reported "have".
+ * Does not clear any hold_mark or end_mark.
*/
-extern void*
-vio_fifo_step_rdr(vio_fifo vf, size_t* p_have, size_t step)
+extern void
+vio_fifo_skip_to_end(vio_fifo vff)
{
- char* rdr_end ;
+ vio_fifo_sync_get(vff) ; /* ensure all straight */
- assert(vf->rdr_lump != NULL) ;
+ /* Setting the get_ptr to the start of the end_lump does the bulk
+ * of the work -- then just skip to the get_end which that sets.
+ */
+ vio_fifo_set_get_ptr(vff, vff->end_lump->data, vff->end_lump) ;
+ vff->get_ptr = vff->get_end ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ vio_fifo_sync_get(vff) ; /* crunch */
+} ;
- rdr_end = vio_fifo_rdr_end(vf) ;
- vf->rdr_ptr += step ;
+/*==============================================================================
+ * Hold Mark Operations.
+ */
- if (vf->rdr_ptr >= rdr_end)
+/*------------------------------------------------------------------------------
+ * If there is a hold_mark, clear it -- discard all contents up to the
+ * current get_ptr.
+ *
+ * Set hold_mark at the current get_ptr.
+ */
+extern void
+vio_fifo_set_hold_mark(vio_fifo vff)
+{
+ if (vff->set)
{
- assert(vf->rdr_ptr == rdr_end) ;
-
- if (vf->rdr_lump != ddl_tail(vf->base))
- {
- vf->rdr_lump = ddl_next(vf->rdr_lump, list) ;
- vf->rdr_ptr = vf->rdr_lump->data ;
+ if (vff->hold_mark)
+ vio_fifo_clear_hold_mark(vff) ; /* clear existing mark & sync */
+ else
+ vio_fifo_sync_get(vff) ; /* ensure all straight */
- rdr_end = vio_fifo_rdr_end(vf) ;
- } ;
+ vff->hold_ptr = vff->get_ptr ;
} ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ vff->hold_mark = true ;
- *p_have = (rdr_end - vf->rdr_ptr) ;
- return (*p_have > 0) ? vf->rdr_ptr : NULL ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
} ;
/*------------------------------------------------------------------------------
- * Move FIFO get position to the rdr position, if any.
+ * If there is a hold_mark, clear it -- discard all contents up to the
+ * current get_ptr.
*
- * This clears the rdr position, and removes all data between the current and
- * new get positions from the FIFO.
+ * The get_ptr is synchronised.
*/
extern void
-vio_fifo_sync_rdr(vio_fifo vf)
+vio_fifo_clear_hold_mark(vio_fifo vff)
{
- vio_fifo_lump head ;
-
- VIO_FIFO_DEBUG_VERIFY(vf) ;
-
- if (vf->rdr_lump == NULL)
- return ;
+ /* Make sure all is up to date, and in particular that the get_ptr
+ * is not sitting at the end of a lump when there is a following lump.
+ */
+ vio_fifo_sync_get(vff) ;
- while ((head = ddl_head(vf->base)) != vf->rdr_lump)
+ if ((vff->hold_mark) && (vff->set))
{
- vf->get_ptr = vf->get_end ; /* jump to end of lump */
- vio_fifo_lump_release(vf, head) ;
- } ;
+ /* Release everything upto but not including the current get_lump.
+ *
+ * This has no effect on the get_ptr etc. so they remain straight.
+ */
+ vio_fifo_release_head(vff, vff->get_lump) ;
- vf->get_ptr = vf->rdr_ptr ; /* jump to rdr_ptr */
+ vff->hold_ptr = NULL ;
+ } ;
- vf->rdr_lump = NULL ; /* clear the rdr */
- vf->rdr_ptr = NULL ;
+ vff->hold_mark = false ;
- if (vf->one)
- {
- if (vf->put_ptr == vf->get_ptr) /* reset pointers if FIFO empty */
- vio_fifo_ptr_reset(vf, head) ;
- else
- vf->get_end = vf->put_ptr ;
- }
- else
- vf->get_end = head->end ;
-
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
} ;
/*------------------------------------------------------------------------------
- * Drop the rdr position (if any).
+ * If there is an hold_mark, reset get_ptr *back* to it.
*
- * This clears the rdr position leaving the get position and FIFO unchanged.
+ * Leave hold mark set or clear.
*/
extern void
-vio_fifo_drop_rdr(vio_fifo vf)
-{
- VIO_FIFO_DEBUG_VERIFY(vf) ;
-
- vf->rdr_lump = NULL ;
- vf->rdr_ptr = NULL ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Move on to next lump to get stuff from.
- *
- * Advance pointers etc. so that have at least one byte available, unless
- * the FIFO is entirely empty.
- *
- * This should be called if (vf->get_ptr >= vf->get_end) -- asserts that
- * these are equal !
- *
- * NB: when there is only one block, it may be that get_end is out of date,
- * and should be advanced to the current put_ptr position.
- *
- * That is done here, but may be worth updating get_end before testing
- * against get_ptr.
- *
- * Returns: true <=> at least one byte in FIFO.
- *
- * NB: if finds that the FIFO is empty, resets the pointers to the start
- * of the last lump -- does not release the last lump.
- */
-static bool
-vio_fifo_get_next_lump(vio_fifo vf)
+vio_fifo_back_to_hold_mark(vio_fifo vff, bool mark)
{
- vio_fifo_lump head ;
-
- VIO_FIFO_DEBUG_VERIFY(vf) ;
- assert(vf->get_ptr == vf->get_end) ;
-
- head = ddl_head(vf->base) ; /* current lump for get */
-
- /* Deal with the simple case of one lump, first.
- *
- * To save work when putting data into the FIFO (particularly when putting
- * a byte at a time) does not keep the vf->get_end up to date (when there is
- * only one lump).
- *
- * If the FIFO is empty, reset pointers and return empty.
- */
- if (vf->one)
+ if (vff->hold_mark)
{
- assert( (head != NULL) && (head == ddl_tail(vf->base)) ) ;
-
- if (vf->get_ptr < vf->put_ptr)
+ if (vff->set)
{
- /* Had an out of date vf->get_end */
- vf->get_end = vf->put_ptr ;
+ vio_fifo_set_get_ptr(vff, vff->hold_ptr, ddl_head(vff->base)) ;
+ /* Set back to hold position */
- return true ; /* FIFO not empty */
- } ;
-
- assert(vf->get_ptr == vf->put_ptr) ;
-
- /* FIFO is empty -- reset pointers and exit */
- vio_fifo_ptr_reset(vf, head) ;
-
- return false ; /* FIFO empty */
- } ;
+ vff->end_mark = mark ; /* new state */
+ if (!mark)
+ vff->hold_ptr = NULL ; /* clear if required */
- /* Release the head and update pointers
- *
- * Deals with possibility that nothing has yet been allocated
- */
- vio_fifo_lump_release(vf, head) ;
+ vio_fifo_sync_get(vff) ; /* to be absolutely sure ! */
+ } ;
- return (vf->get_ptr < vf->get_end) ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
+ }
+ else if (mark)
+ vio_fifo_set_end_mark(vff) ;
} ;
/*==============================================================================
* For debug purposes -- verify the state of the given FIFO
*/
Private void
-vio_fifo_verify(vio_fifo vf)
+vio_fifo_verify(vio_fifo vff)
{
vio_fifo_lump head ;
vio_fifo_lump lump ;
vio_fifo_lump tail ;
- head = ddl_head(vf->base) ;
- tail = ddl_tail(vf->base) ;
+ head = ddl_head(vff->base) ;
+ tail = ddl_tail(vff->base) ;
- /* If nothing allocated, should all be NULL & !vf->one */
+ /* If nothing allocated, should all be NULL & !vff->set */
/* If something allocated, tail must not be NULL */
if (head == NULL)
{
if ( (tail != NULL)
- || (vf->put_ptr != NULL)
- || (vf->put_end != NULL)
- || (vf->get_ptr != NULL)
- || (vf->rdr_lump != NULL)
- || (vf->rdr_ptr != NULL)
- || (vf->one) )
+ || (vff->set)
+ || (vff->as_one)
+ || (vff->hold_ptr != NULL)
+ || (vff->get_lump != NULL)
+ || (vff->get_ptr != NULL)
+ || (vff->get_end != NULL)
+ || (vff->end_lump != NULL)
+ || (vff->end_end != NULL)
+ || (vff->put_ptr != NULL)
+ || (vff->put_end != NULL) )
zabort("nothing allocated, but not all NULL") ;
return ;
}
else
{
if (tail == NULL)
- zabort("head pointer not NULL, but tail pointer is") ;
+ zabort("head not NULL, but tail is") ;
} ;
- /* Check that all the pointers are within respective lumps
+ /* Must now be set ! */
+ if (!vff->set)
+ zabort("head not NULL, but set is false") ;
+
+ /* Make sure that the lump pointers all work
*
- * Know that put_end is always tail->end, but get_end need not be.
+ * When finished, know that head <= get_lump <= end_lump <= tail.
*/
- if ( (tail->data > vf->put_ptr)
- || (vf->put_ptr > vf->put_end)
- || (vf->put_end != tail->end) )
- zabort("put pointers outside the tail lump") ;
+ lump = head ;
+ while (lump != vff->get_lump)
+ {
+ lump = ddl_next(lump, list) ;
+ if (lump == NULL)
+ zabort("ran out of lumps looking for get_lump") ;
+ } ;
- if ( (head->data > vf->get_ptr)
- || (vf->get_ptr > vf->get_end)
- || (vf->get_end > head->end) )
- zabort("get pointers outside the head lump") ;
+ while (lump != vff->end_lump)
+ {
+ lump = ddl_next(lump, list) ;
+ if (lump == NULL)
+ zabort("ran out of lumps looking for end_lump") ;
+ } ;
- /* If head == tail, should be vf->one, etc. */
- if (head == tail)
+ while (lump != tail)
{
- if (!vf->one)
- zabort("have one lump, but !vf->one") ;
+ lump = ddl_next(lump, list) ;
+ if (lump == NULL)
+ zabort("ran out of lumps looking for tail") ;
+ } ;
- if (vf->get_end > vf->put_ptr)
- zabort("get_end is greater than put_ptr when vf->one") ;
+ /* Check that all the pointers are within respective lumps
+ *
+ * Know that put_end is always tail->end, but get_end need not be.
+ *
+ * When finished, know that:
+ *
+ * - get_lump == head if !hold_mark
+ * - end_lump == tail if !end_mark
+ * - that all pointers are within their respective lumps
+ * - all ptr are <= their respective ends
+ * - if hold_mark: hold_ptr <= get_ptr or head != get_lump
+ * - if end_mark: end_end <= put_ptr or tail != end_lump
+ */
+ if (vff->hold_mark)
+ {
+ if ( (head->data > vff->hold_ptr)
+ || (vff->hold_ptr > head->end) )
+ zabort("hold pointer outside the head lump") ;
+
+ if ((vff->get_lump == head) && (vff->hold_ptr > vff->get_ptr))
+ zabort("hold pointer greater than get pointer") ;
}
else
{
- if (vf->one)
- zabort("have two or more lumps, but vf->one is true") ;
-
- if (vf->get_end != head->end)
- zabort("get_end is not head->end when !vf->one") ;
+ if (vff->hold_ptr != NULL)
+ zabort("no hold_mark, but hold pointer not NULL") ;
+ if (vff->get_lump != head)
+ zabort("no hold_mark, but get_lump is not head") ;
} ;
- /* If have an rdr_lump -- make sure everything else is valid */
- if (vf->rdr_lump != NULL)
+ if ( (vff->get_lump->data > vff->get_ptr)
+ || (vff->get_ptr > vff->get_end)
+ || (vff->get_end > vff->get_lump->end))
+ zabort("get pointers outside the get lump") ;
+
+ if (vff->end_mark)
{
- lump = head ;
- while (lump != vf->rdr_lump)
- {
- if (lump == tail)
- zabort("rdr_lump is not part of FIFO") ;
- lump = ddl_next(lump, list) ;
- } ;
+ if ( (vff->end_lump->data > vff->end_end)
+ || (vff->end_end > vff->end_lump->end) )
+ zabort("end pointer outside the end lump") ;
- if ( (lump->data > vf->rdr_ptr)
- || (vf->rdr_ptr > lump->end) )
- zabort("rdr_ptr outside its lump") ;
+ if ((vff->end_lump == tail) && (vff->end_end > vff->put_ptr))
+ zabort("end pointer greater than put pointer") ;
+ }
+ else
+ {
+ if (vff->end_end != NULL)
+ zabort("no end_mark, but end end not NULL") ;
+ if (vff->end_lump != tail)
+ zabort("no end_mark, but end_lump is not tail") ;
+ } ;
- if ( (lump == head) && (vf->rdr_ptr < vf->get_ptr))
- zabort("rdr_ptr is less than get_ptr in first lump") ;
+ if ( (tail->data > vff->put_ptr)
+ || (vff->put_ptr > vff->put_end)
+ || (vff->put_end != tail->end) )
+ zabort("put pointers outside the tail lump") ;
- if ( (lump == tail) && (vf->rdr_ptr > vf->put_ptr))
- zabort("rdr_ptr is greater than put_ptr in last lump") ;
+ /* The as_one state & get_end
+ */
+ if (vff->get_lump != vff->end_lump)
+ {
+ if (vff->as_one)
+ zabort("get_lump != end_lump, but as_one true") ;
+ if (vff->get_end != vff->get_lump->end)
+ zabort("get_lump != end_lump, but get_end != get_lump->end") ;
}
- else
+ else if (vff->end_mark)
{
- if (vf->rdr_ptr != NULL)
- zabort("rdr_ptr not NULL when rdr_lump is") ;
+ if (vff->as_one)
+ zabort("end_mark true, but as_one also true") ;
+ if (vff->get_end != vff->end_end)
+ zabort("get_lump == end_lump and end_mark, but get_end != end_end") ;
}
+ else
+ {
+ if (!vff->as_one)
+ zabort("get_lump == end_lump and !end_mark, but as_one not true") ;
+ if (vff->get_end > vff->put_ptr)
+ zabort("is as_one, but get_end > put_ptr") ;
+ } ;
} ;
diff --git a/lib/vio_fifo.h b/lib/vio_fifo.h
index 08af4590..1b7b05d3 100644
--- a/lib/vio_fifo.h
+++ b/lib/vio_fifo.h
@@ -22,37 +22,35 @@
#ifndef _ZEBRA_VIO_FIFO_H
#define _ZEBRA_VIO_FIFO_H
-#include "zebra.h"
#include "misc.h"
+#include "vargs.h"
+#include <stdio.h>
#include "list_util.h"
-#include "zassert.h"
-
-/* GCC have printf type attribute check. */
-#ifdef __GNUC__
-#define PRINTF_ATTRIBUTE(a,b) __attribute__ ((__format__ (__printf__, a, b)))
-#else
-#define PRINTF_ATTRIBUTE(a,b)
-#endif /* __GNUC__ */
/*==============================================================================
* VTY I/O FIFO -- buffering of arbitrary amounts of I/O.
*/
-#ifdef NDEBUG
-# define VIO_FIFO_DEBUG 0 /* NDEBUG override */
+#ifdef VIO_FIFO_DEBUG /* Can be forced from outside */
+# if VIO_FIFO_DEBUG
+# define VIO_FIFO_DEBUG 1 /* Force 1 or 0 */
+#else
+# define VIO_FIFO_DEBUG 0
+# endif
+#else
+# ifdef QDEBUG
+# define VIO_FIFO_DEBUG 1 /* Follow QDEBUG */
#else
-# ifndef VIO_FIFO_DEBUG
-# define VIO_FIFO_DEBUG 1 /* Set to 1 to turn on debug checks */
+# define VIO_FIFO_DEBUG 0
# endif
#endif
+enum { vio_fifo_debug = VIO_FIFO_DEBUG } ;
+
/*==============================================================================
* Data Structures
*/
-typedef struct vio_fifo vio_fifo_t ;
-typedef struct vio_fifo* vio_fifo ;
-
typedef struct vio_fifo_lump vio_fifo_lump_t ;
typedef struct vio_fifo_lump* vio_fifo_lump ;
@@ -60,22 +58,46 @@ struct vio_fifo
{
struct dl_base_pair(vio_fifo_lump) base ;
- bool one ;
+ bool set ; /* have at least one lump */
- char* put_ptr ;
- char* put_end ;
+ bool as_one ; /* get_lump == tail && !end_mark
+ => get_end may not be up to date */
+ bool hold_mark ; /* hold stuff while getting */
+ bool end_mark ; /* do not get beyond end */
+
+ char* hold_ptr ; /* implicitly in the head lump */
+ /* used only if "hold_mark" */
+
+ vio_fifo_lump get_lump ; /* head lump unless "hold_mark" */
char* get_ptr ;
char* get_end ;
- vio_fifo_lump rdr_lump ;
- char* rdr_ptr ;
+ vio_fifo_lump end_lump ; /* tail lump unless "end_mark" */
+ char* end_end ; /* used only if "end_mark" */
+
+ char* put_ptr ; /* implicitly in the tail lump */
+ char* put_end ;
size_t size ;
vio_fifo_lump spare ;
} ;
+typedef struct vio_fifo vio_fifo_t[1] ; /* embedded */
+typedef struct vio_fifo* vio_fifo ;
+
+/* Setting a FIFO object to all zeros is enough to initialise it to an
+ * empty FIFO (with default lump sizes).
+ */
+enum
+{
+ VIO_FIFO_INIT_ALL_ZEROS = true,
+ VIO_FIFO_DEFAULT_LUMP_SIZE = 4 * 1024
+} ;
+
+#define VIO_FIFO_INIT_EMPTY { 0 }
+
struct vio_fifo_lump
{
struct dl_list_pair(vio_fifo_lump) list ;
@@ -89,76 +111,114 @@ struct vio_fifo_lump
* Functions
*/
-extern vio_fifo vio_fifo_init_new(vio_fifo vf, size_t size) ;
-extern vio_fifo vio_fifo_reset(vio_fifo vf, int free_structure) ;
+extern vio_fifo vio_fifo_init_new(vio_fifo vff, size_t size) ;
+extern vio_fifo vio_fifo_reset(vio_fifo vff, free_keep_b free_structure) ;
-#define vio_fifo_reset_keep(vf) vio_fifo_reset(vf, 0)
-#define vio_fifo_reset_free(vf) vio_fifo_reset(vf, 1)
+extern void vio_fifo_clear(vio_fifo vff, bool clear_marks) ;
+Inline bool vio_fifo_empty(vio_fifo vff) ;
+extern size_t vio_fifo_room(vio_fifo vff) ;
-extern void vio_fifo_clear(vio_fifo vf) ;
-Inline bool vio_fifo_empty(vio_fifo vf) ;
-extern size_t vio_fifo_room(vio_fifo vf) ;
+extern void vio_fifo_put_bytes(vio_fifo vff, const char* src, size_t n) ;
+Inline void vio_fifo_put_byte(vio_fifo vff, char b) ;
-extern void vio_fifo_put(vio_fifo vf, const char* src, size_t n) ;
-Inline void vio_fifo_put_byte(vio_fifo vf, char b) ;
-
-extern int vio_fifo_printf(vio_fifo vf, const char* format, ...)
+extern int vio_fifo_printf(vio_fifo vff, const char* format, ...)
PRINTF_ATTRIBUTE(2, 3) ;
-extern int vio_fifo_vprintf(vio_fifo vf, const char *format, va_list args) ;
-
-extern size_t vio_fifo_get(vio_fifo vf, void* dst, size_t n) ;
-Inline int vio_fifo_get_byte(vio_fifo vf) ;
-extern void* vio_fifo_get_lump(vio_fifo vf, size_t* have) ;
-extern void vio_fifo_got_upto(vio_fifo vf, void* here) ;
-
-Inline bool vio_fifo_full_lump(vio_fifo vf) ;
-extern int vio_fifo_write_nb(vio_fifo vf, int fd, bool all) ;
-
-extern void* vio_fifo_get_rdr(vio_fifo vf, size_t* have) ;
-extern void* vio_fifo_step_rdr(vio_fifo vf, size_t* have, size_t step) ;
-extern void vio_fifo_sync_rdr(vio_fifo vf) ;
-extern void vio_fifo_drop_rdr(vio_fifo vf) ;
-
-Private void vio_fifo_lump_new(vio_fifo vf, size_t size) ;
-Private int vio_fifo_get_next_byte(vio_fifo vf) ;
+extern int vio_fifo_vprintf(vio_fifo vff, const char *format, va_list args) ;
+extern int vio_fifo_read_nb(vio_fifo vff, int fd, size_t request) ;
+
+extern size_t vio_fifo_get_bytes(vio_fifo vff, void* dst, size_t n) ;
+Inline int vio_fifo_get_byte(vio_fifo vff) ;
+extern void* vio_fifo_get(vio_fifo vff, size_t* have) ;
+extern void vio_fifo_step(vio_fifo vff, size_t step) ;
+extern void* vio_fifo_step_get(vio_fifo vff, size_t* p_have, size_t step) ;
+
+extern vio_fifo vio_fifo_copy(vio_fifo dst, vio_fifo src) ;
+extern vio_fifo vio_fifo_copy_tail(vio_fifo dst, vio_fifo src) ;
+
+Inline bool vio_fifo_full_lump(vio_fifo vff) ;
+extern int vio_fifo_write_nb(vio_fifo vff, int fd, bool all) ;
+extern int vio_fifo_fwrite(vio_fifo vff, FILE* file) ;
+
+extern void vio_fifo_skip_to_end(vio_fifo vff) ;
+extern void vio_fifo_set_end_mark(vio_fifo vff) ;
+extern void vio_fifo_step_end_mark(vio_fifo vff) ;
+extern void vio_fifo_clear_end_mark(vio_fifo vff) ;
+extern void vio_fifo_back_to_end_mark(vio_fifo vff, bool keep) ;
+extern void vio_fifo_set_hold_mark(vio_fifo vff) ;
+extern void vio_fifo_clear_hold_mark(vio_fifo vff) ;
+extern void vio_fifo_back_to_hold_mark(vio_fifo vff, bool keep) ;
/*==============================================================================
* Debug -- verification function
*/
-Private void vio_fifo_verify(vio_fifo vf) ;
+Private void vio_fifo_verify(vio_fifo vff) ;
#if VIO_FIFO_DEBUG
-# define VIO_FIFO_DEBUG_VERIFY(vf) vio_fifo_verify(vf)
+# define VIO_FIFO_DEBUG_VERIFY(vff) vio_fifo_verify(vff)
#else
-# define VIO_FIFO_DEBUG_VERIFY(vf)
+# define VIO_FIFO_DEBUG_VERIFY(vff)
#endif
/*==============================================================================
* Inline Functions
*/
+Private void vio_fifo_lump_new(vio_fifo vff, size_t size) ;
+Private int vio_fifo_get_next_byte(vio_fifo vff) ;
+Private bool vio_fifo_do_empty(vio_fifo vff) ;
+
/*------------------------------------------------------------------------------
- * Returns true <=> FIFO is empty
+ * Returns true <=> FIFO is empty -- at least: get_ptr == end_end (if any)
+ * or: get_ptr == put_ptr.
*/
Inline bool
-vio_fifo_empty(vio_fifo vf)
+vio_fifo_empty(vio_fifo vff)
{
- return (vf->get_ptr == vf->put_ptr) ;
-}
+ /* if vff is NULL, treat as empty !
+ * if !set, then all pointers should be NULL (so get_ptr == put_ptr)
+ * if !end_mark, then vff->end_end will be NULL (so get_ptr != end_end,
+ * unless !set)
+ * Is definitely empty if any of the following is true.
+ */
+ if ((vff == NULL) || (vff->get_ptr == vff->put_ptr)
+ || (vff->get_ptr == vff->end_end))
+ return true ;
+
+ /* If get_ptr < get_end is NOT empty */
+ if (vff->get_ptr < vff->get_end)
+ return false ;
+
+ /* See if can advance get_ptr, and if so whether is then empty. */
+ return vio_fifo_do_empty(vff) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Returns true <=> FIFO is empty beyond end_end (if any).
+ */
+Inline bool
+vio_fifo_empty_tail(vio_fifo vff)
+{
+ /* if vff is NULL, treat as empty !
+ * if !set, then all pointers should be NULL (so end_end == put_ptr)
+ * if !end_mark, then tail is empty !
+ * else tail is empty iff end_end == put_ptr.
+ */
+ return (vff == NULL) || !vff->end_mark || (vff->end_end == vff->put_ptr) ;
+} ;
/*------------------------------------------------------------------------------
* Put one byte to the FIFO
*/
Inline void
-vio_fifo_put_byte(vio_fifo vf, char b)
+vio_fifo_put_byte(vio_fifo vff, char b)
{
- if (vf->put_ptr >= vf->put_end)
- vio_fifo_lump_new(vf, vf->size) ; /* traps put_ptr > put_end */
+ if (vff->put_ptr >= vff->put_end)
+ vio_fifo_lump_new(vff, 0) ; /* traps put_ptr > put_end */
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
- *vf->put_ptr++ = b ;
+ *vff->put_ptr++ = b ;
} ;
/*------------------------------------------------------------------------------
@@ -168,14 +228,12 @@ vio_fifo_put_byte(vio_fifo vf, char b)
* -1 => FIFO is empty.
*/
Inline int
-vio_fifo_get_byte(vio_fifo vf)
+vio_fifo_get_byte(vio_fifo vff)
{
- if (vf->get_end <= (vf->get_ptr + 1))
- return vio_fifo_get_next_byte(vf) ;
-
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ if (vff->get_ptr < vff->get_end)
+ return (uchar)*vff->get_ptr++ ;
- return (unsigned char)*vf->get_ptr++ ;
+ return vio_fifo_get_next_byte(vff) ;
} ;
/*------------------------------------------------------------------------------
@@ -188,9 +246,9 @@ vio_fifo_get_byte(vio_fifo vf)
* (excluding the last lump if it happens to be full)
*/
Inline bool
-vio_fifo_full_lump(vio_fifo vf)
+vio_fifo_full_lump(vio_fifo vff)
{
- return (!vf->one && (vf->put_ptr != NULL)) ;
+ return (ddl_head(vff->base) != ddl_tail(vff->base)) ;
} ;
#endif /* _ZEBRA_VIO_FIFO_H */
diff --git a/lib/vio_lines.c b/lib/vio_lines.c
index 2ac874d1..a9268fd5 100644
--- a/lib/vio_lines.c
+++ b/lib/vio_lines.c
@@ -119,7 +119,7 @@ vio_lc_init_new(vio_line_control lc, int width, int height)
* Returns: address of vio_line_control (if any) -- NULL if structure released
*/
extern vio_line_control
-vio_lc_reset(vio_line_control lc, bool free_structure)
+vio_lc_reset(vio_line_control lc, free_keep_b free_structure)
{
if (lc != NULL)
{
diff --git a/lib/vio_lines.h b/lib/vio_lines.h
index 7097fe9c..dd5545cc 100644
--- a/lib/vio_lines.h
+++ b/lib/vio_lines.h
@@ -22,7 +22,6 @@
#ifndef _ZEBRA_VIO_LINES_H
#define _ZEBRA_VIO_LINES_H
-#include "zebra.h"
#include "misc.h"
#include "qiovec.h"
@@ -35,9 +34,6 @@
*
* NB: a completely zero structure is a valid, clear vio_line_control.
*/
-
-typedef struct vio_line_control* vio_line_control ;
-typedef struct vio_line_control vio_line_control_t ;
struct vio_line_control
{
unsigned width ; /* console width -- 0 => HUGE */
@@ -54,15 +50,21 @@ struct vio_line_control
bool writing ; /* write started, but not completed */
} ;
+typedef struct vio_line_control* vio_line_control ;
+typedef struct vio_line_control vio_line_control_t[1] ;
+
+enum
+{
+ VIO_LINE_CONTROL_INIT_ALL_ZEROS = true
+} ;
+
/*==============================================================================
* Functions
*/
extern vio_line_control vio_lc_init_new(vio_line_control lc, int width,
int height) ;
-extern vio_line_control vio_lc_reset(vio_line_control lc, bool free_structure) ;
-
-#define vio_lc_reset_keep(lc) vio_lc_reset(lc, 0)
-#define vio_lc_reset_free(lc) vio_lc_reset(lc, 1)
+extern vio_line_control vio_lc_reset(vio_line_control lc,
+ free_keep_b free_structure) ;
Inline bool vio_lc_empty(vio_line_control lc) ;
extern void vio_lc_clear(vio_line_control lc) ;
diff --git a/lib/vty.c b/lib/vty.c
index 3c1e62b7..da94b992 100644
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -1,4 +1,4 @@
-/* VTY top level
+/* VTY external interface
* Copyright (C) 1997, 98 Kunihiro Ishiguro
*
* Revisions: Copyright (C) 2010 Chris Hall (GMCH), Highwayman
@@ -21,36 +21,74 @@
* 02111-1307, USA.
*/
-#include "zebra.h"
+#include "zconfig.h"
#include "misc.h"
#include "lib/version.h"
-#include "vty_io.h"
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <ctype.h>
+
#include "vty.h"
-#include "uty.h"
+#include "vty_local.h"
+#include "vty_io.h"
+#include "vty_command.h"
#include "vty_cli.h"
+#include "vio_fifo.h"
#include "list_util.h"
#include "command.h"
-#include "command_queue.h"
+#include "command_local.h"
#include "command_execute.h"
+#include "command_parse.h"
#include "memory.h"
#include "log.h"
#include "mqueue.h"
+#include "qstring.h"
+
+/*==============================================================================
+ * The vty family comprises:
+ *
+ * vty -- level visible from outside the vty/command/log family
+ * and within those families.
+ *
+ * vty_common.h -- definitions ...
+ * vty_local.h
+ *
+ * vty_io -- top level of the vio handling
+ *
+ * vty_command -- functions called by the command family
+ * vty_log -- functions called by the log family
+ *
+ * vty_cli -- terminal command line handling
+ * vty_io_term -- terminal (telnet) I/O
+ * vty_io_vsh -- vtysh I/O
+ * vty_io_file -- file I/O
+ * vty_io_shell -- system shell I/O
+ *
+ * vty_io_basic -- common low level I/O handling
+ * encapsulates the differences between qpselect and legacy
+ * thread/select worlds.
+ *
+ * vio_lines -- for terminal: handles width, CRLF, line counting etc.
+ * vio_fifo --
+ * qiovec
+ *
+ */
+
/*==============================================================================
- * Variables etc. (see uty.h)
+ * Variables etc. (see vty_local.h)
*/
/* The mutex and related debug counters */
qpt_mutex_t vty_mutex ;
-#if VTY_DEBUG
-
int vty_lock_count = 0 ;
-int vty_assert_fail = 0 ;
+#if VTY_DEBUG
+int vty_assert_fail = 0 ;
#endif
/* For thread handling -- initialised in vty_init */
@@ -59,7 +97,14 @@ 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.
+ *
+ * If not in the qpthreads world, vty_cli_nexus == vty_cmd_nexus == NULL.
+ *
+ * If in the qpthreads world these vty_cli_nexus == vty_cmd_nexus if not
+ * actually running pthreaded.
*/
+bool vty_nexus ; /* true <=> in the qpthreads world */
+
qpn_nexus vty_cli_nexus = NULL ;
qpn_nexus vty_cmd_nexus = NULL ;
@@ -72,28 +117,6 @@ 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;
-
-/* Vty access-class for IPv6. */
-char *vty_ipv6_accesslist_name = NULL;
-
-/* Current directory -- initialised in vty_init() */
-static char *vty_cwd = NULL;
-
-/* Configure lock -- only one vty may be in CONFIG_NODE or above ! */
-bool vty_config = 0 ;
-
-/* Login password check override. */
-bool no_password_check = 0;
-
-/* Restrict unauthenticated logins? */
-const bool restricted_mode_default = 0 ;
- bool restricted_mode = 0 ;
-
/*------------------------------------------------------------------------------
* VTYSH stuff
*/
@@ -107,9 +130,10 @@ char integrate_default[] = SYSCONFDIR INTEGRATE_DEFAULT_CONFIG ;
static void uty_reset (bool final, const char* why) ;
static void uty_init_commands (void) ;
static void vty_save_cwd (void) ;
-static bool vty_terminal (struct vty *);
-static bool vty_shell_server (struct vty *);
-static bool vty_shell_client (struct vty *);
+
+//static bool vty_terminal (struct vty *);
+//static bool vty_shell_server (struct vty *);
+//static bool vty_shell_client (struct vty *);
/*------------------------------------------------------------------------------
* Tracking the initialisation state.
@@ -143,8 +167,8 @@ static enum vty_init_state vty_init_state ;
extern void
vty_init (struct thread_master *master_thread)
{
- VTY_LOCK() ; /* Does nothing if !qpthreads_enabled */
VTY_ASSERT_CLI_THREAD() ; /* True if !qpthreads_enabled */
+ VTY_LOCK() ; /* Does nothing if !qpthreads_enabled */
assert(vty_init_state == vty_init_pending) ;
@@ -156,7 +180,8 @@ vty_init (struct thread_master *master_thread)
vio_monitors_base = NULL ;
vio_death_watch = NULL ;
- vty_cli_nexus = NULL ; /* not running qnexus-wise */
+ vty_nexus = false ; /* not running qnexus-wise */
+ vty_cli_nexus = NULL ;
vty_cmd_nexus = NULL ;
uty_watch_dog_init() ; /* empty watch dog */
@@ -190,6 +215,7 @@ vty_init_r (qpn_nexus cli, qpn_nexus cmd)
{
assert(vty_init_state == vty_init_1st_stage) ;
+ vty_nexus = true ;
vty_cli_nexus = cli ;
vty_cmd_nexus = cmd ;
@@ -214,7 +240,7 @@ vty_init_vtysh (void)
/*------------------------------------------------------------------------------
* Start the VTY going.
*
- * This starts the listeners for VTY_TERM and VTY_SHELL_SERV.
+ * This starts the listeners for VTY_TERMINAL and VTY_SHELL_SERVER.
*
* Also starts the watch dog.
*
@@ -222,14 +248,12 @@ vty_init_vtysh (void)
* any threads are started -- so is, implicitly, in the CLI thread.
*
* NB: may be called once and once only.
- *
- * NB: MUST be in the CLI thread (if any).
*/
extern void
vty_start(const char *addr, unsigned short port, const char *path)
{
- VTY_LOCK() ;
VTY_ASSERT_CLI_THREAD() ;
+ VTY_LOCK() ;
assert( (vty_init_state == vty_init_1st_stage)
|| (vty_init_state == vty_init_2nd_stage) ) ;
@@ -254,10 +278,11 @@ vty_reset()
/*------------------------------------------------------------------------------
* Reset all VTY status
*
- * This is done in response to SIGHUP -- and runs in the CLI thread.
+ * This is done in response to SIGHUP/SIGINT/SIGTERM -- and runs in the
+ * CLI thread (if there is one).
*
- * Half closes all VTY, leaving the death watch to tidy up once all output
- * and any command in progress have completed.
+ * Closes all VTY, leaving the death watch to tidy up once all output and any
+ * command in progress have completed.
*
* Closes all listening sockets.
*
@@ -268,21 +293,24 @@ vty_reset()
extern void
vty_reset_because(const char* why)
{
- VTY_LOCK() ;
VTY_ASSERT_CLI_THREAD() ;
+ VTY_LOCK() ;
- assert(vty_init_state == vty_init_started) ;
+ if (vty_init_state != vty_init_reset)
+ {
+ assert(vty_init_state == vty_init_started) ;
- uty_reset(0, why) ; /* not final ! */
+ uty_reset(false, why) ; /* not final ! */
- vty_init_state = vty_init_reset ;
+ vty_init_state = vty_init_reset ;
+ } ;
VTY_UNLOCK() ;
}
/*------------------------------------------------------------------------------
* Restart the VTY, following a vty_reset().
*
- * This starts the listeners for VTY_TERM and VTY_SHELL_SERV, again.
+ * This starts the listeners for VTY_TERMINAL and VTY_SHELL_SERVER, again.
*
* NB: may be called once, and once only, *after* a vty_reset().
*
@@ -296,7 +324,7 @@ struct vty_restart_args
} ;
MQB_ARGS_SIZE_OK(vty_restart_args) ;
-static void uty_restart_action(mqueue_block mqb, mqb_flag_t flag) ;
+static void vty_restart_action(mqueue_block mqb, mqb_flag_t flag) ;
static void uty_restart(const char *addr, unsigned short port,
const char *path) ;
extern void
@@ -308,14 +336,14 @@ vty_restart(const char *addr, unsigned short port, const char *path)
*
* Otherwise, construct and dispatch message to do a uty_restart.
*/
- if (!vty_cli_nexus)
+ if (!vty_nexus)
uty_restart(addr, port, path) ;
else
{
mqueue_block mqb ;
struct vty_restart_args* args ;
- mqb = mqb_init_new(NULL, uty_restart_action, vty_cli_nexus) ;
+ mqb = mqb_init_new(NULL, vty_restart_action, vty_cli_nexus) ;
args = mqb_get_args(mqb) ;
if (addr != NULL)
@@ -330,7 +358,7 @@ vty_restart(const char *addr, unsigned short port, const char *path)
else
args->path = NULL ;
- mqueue_enqueue(vty_cli_nexus->queue, mqb, 0) ;
+ mqueue_enqueue(vty_cli_nexus->queue, mqb, mqb_priority) ;
} ;
VTY_UNLOCK() ;
@@ -338,7 +366,7 @@ vty_restart(const char *addr, unsigned short port, const char *path)
/* Deal with the uty_restart message */
static void
-uty_restart_action(mqueue_block mqb, mqb_flag_t flag)
+vty_restart_action(mqueue_block mqb, mqb_flag_t flag)
{
struct vty_restart_args* args ;
args = mqb_get_args(mqb) ;
@@ -388,17 +416,18 @@ uty_restart(const char *addr, unsigned short port, const char *path)
extern void
vty_terminate (void)
{
+ VTY_ASSERT_CLI_THREAD() ;
+
if ( (vty_init_state == vty_init_pending)
|| (vty_init_state == vty_init_terminated) )
- return ; /* nothing to do ! */
+ return ; /* nothing to do ! */
VTY_LOCK() ;
- VTY_ASSERT_CLI_THREAD() ;
assert( (vty_init_state > vty_init_pending)
&& (vty_init_state < vty_init_terminated) ) ;
- uty_reset(1, "Shut down") ; /* final reset */
+ uty_reset(true, "Shut down") ; /* final reset */
VTY_UNLOCK() ;
@@ -408,12 +437,14 @@ vty_terminate (void)
}
/*------------------------------------------------------------------------------
- * Reset -- final or for SIGHUP
+ * Reset -- final curtain or for SIGHUP
*
* Closes listeners.
*
- * Closes (final) or half closes (SIGHUP) all VTY, and revokes any outstanding
- * commands.
+ * Revokes any outstanding commands and close (SIGHUP) or close_final
+ * (curtains) all VTY.
+ *
+ *
*
* Resets the vty timeout and access lists.
*
@@ -440,34 +471,22 @@ uty_reset (bool curtains, const char* why)
vio = next ;
next = sdl_next(vio, vio_list) ;
- if (uty_is_terminal(vio->vty))
- cq_revoke(vio->vty) ;
-
- if (curtains)
- uty_close_final(vio, why) ;
- else
- uty_close(vio, why) ;
+ uty_close(vio, curtains, qs_set(NULL, why)) ;
} ;
- vty_timeout_val = VTY_TIMEOUT_DEFAULT;
-
- if (vty_accesslist_name)
- {
- XFREE(MTYPE_VTY, vty_accesslist_name);
- vty_accesslist_name = NULL;
- }
+ host.vty_timeout_val = VTY_TIMEOUT_DEFAULT;
- if (vty_ipv6_accesslist_name)
- {
- XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
- vty_ipv6_accesslist_name = NULL;
- }
+ XFREE(MTYPE_HOST, host.vty_accesslist_name) ;
+ /* sets host.vty_accesslist_name = NULL */
- if (curtains && vty_cwd)
- XFREE (MTYPE_TMP, vty_cwd);
+ XFREE(MTYPE_HOST, host.vty_ipv6_accesslist_name);
+ /* sets host.vty_ipv6_accesslist_name = NULL */
if (curtains)
- uty_watch_dog_stop() ; /* and final death watch run */
+ {
+ XFREE (MTYPE_HOST, host.vty_cwd);
+ uty_watch_dog_stop() ; /* and final death watch run */
+ } ;
} ;
/*==============================================================================
@@ -481,15 +500,17 @@ uty_reset (bool curtains, const char* why)
/*------------------------------------------------------------------------------
* Create a new VTY of the given type
*
- * The type may NOT be: VTY_TERM or VTY_SHELL_SERV
+ * The type may NOT be: VTY_TERMINAL or VTY_SHELL_SERVER
+ *
+ * Appears only to be used by vtysh !! TODO ????
*/
-extern struct vty *
-vty_open(enum vty_type type)
+extern vty
+vty_open(vty_type_t type)
{
struct vty* vty ;
VTY_LOCK() ;
- vty = uty_new(type, -1) ; /* fails for VTY_TERM or VTY_SHELL_SERV */
+ vty = uty_new(type) ;
VTY_UNLOCK() ;
return vty ;
@@ -498,12 +519,16 @@ vty_open(enum vty_type type)
/*------------------------------------------------------------------------------
* Close the given VTY
*/
-extern void
-vty_close (struct vty *vty)
+extern bool
+vty_close(vty vty, bool final, qstring reason)
{
+ bool closed ;
+
VTY_LOCK() ;
- uty_close(vty->vio) ;
+ closed = uty_close(vty->vio, final, reason) ;
VTY_UNLOCK() ;
+
+ return closed ;
}
/*==============================================================================
@@ -517,59 +542,22 @@ vty_close (struct vty *vty)
/*------------------------------------------------------------------------------
* VTY output -- cf fprintf !
*
- * This is for command output, which may be suppressed
- *
- * Returns: >= 0 => OK
- * < 0 => failed (see errno)
- */
-extern int
-vty_out (struct vty *vty, const char *format, ...)
-{
- int ret ;
- va_list args ;
-
- VTY_LOCK() ;
-
- if (vty->output_enabled)
- {
- va_start (args, format) ;
- ret = uty_vprintf(vty, format, args) ;
- va_end (args) ;
- }
- else
- ret = 0 ;
-
- VTY_UNLOCK() ;
- return ret ;
-}
-
-/*------------------------------------------------------------------------------
- * VTY output error message -- cf fprintf !
- *
- * If command has not yet been reflected, do that first.
+ * This is for command output, which may later be suppressed
*
* Returns: >= 0 => OK
* < 0 => failed (see errno)
*/
extern int
-vty_out_error (struct vty *vty, const char *format, ...)
+vty_out(struct vty *vty, const char *format, ...)
{
int ret ;
va_list args ;
VTY_LOCK() ;
- if (!vty->reflected)
- ret = uty_reflect(vty) ;
- else
- ret = 0 ;
-
- if (ret == 0)
- {
- va_start (args, format) ;
- ret = uty_vprintf(vty, format, args) ;
- va_end (args) ;
- } ;
+ va_start (args, format) ;
+ ret = vio_fifo_vprintf(vty->vio->obuf, format, args) ;
+ va_end (args) ;
VTY_UNLOCK() ;
return ret ;
@@ -631,7 +619,7 @@ vty_hello (struct vty *vty)
VTY_LOCK() ;
#ifdef QDEBUG
- uty_out (vty, "%s\n", debug_banner);
+ vty_out (vty, "%s\n", debug_banner);
#endif
if (host.motdfile)
{
@@ -648,15 +636,15 @@ vty_hello (struct vty *vty)
for (s = buf + strlen (buf); (s > buf) && isspace ((int)*(s - 1));
s--);
*s = '\0';
- uty_output (vty, "%s\n", buf);
+ vty_out(vty, "%s\n", buf);
}
fclose (f);
}
else
- uty_output (vty, "MOTD file %s not found\n", host.motdfile);
+ vty_out(vty, "MOTD file %s not found\n", host.motdfile);
}
else if (host.motd)
- uty_output (vty, "%s", host.motd);
+ vty_out(vty, "%s", host.motd);
VTY_UNLOCK() ;
}
@@ -665,7 +653,7 @@ vty_hello (struct vty *vty)
* Clear the contents of the command output FIFO etc.
*/
extern void
-vty_out_clear(struct vty* vty)
+vty_out_clear(vty vty)
{
VTY_LOCK() ;
uty_out_clear(vty->vio) ;
@@ -673,429 +661,6 @@ vty_out_clear(struct vty* vty)
} ;
/*==============================================================================
- * Command Execution
- */
-
-/*------------------------------------------------------------------------------
- * Execute command -- adding to history is not empty or just comment
- *
- * This is for VTY_TERM type VTY.
- *
- * Outputs diagnostics if fails to parse.
- *
- * Returns: command return code
- */
-extern enum cmd_return_code
-uty_command(struct vty *vty)
-{
- enum cmd_return_code ret;
-
- VTY_ASSERT_LOCKED() ;
- VTY_ASSERT_CLI_THREAD() ;
-
- assert(uty_is_terminal(vty)) ;
-
- /* Parse the command and add to history (if not empty) */
- ret = cmd_parse_command(vty,
- cmd_parse_completion + cmd_parse_do + cmd_parse_tree) ;
- if (ret != CMD_EMPTY)
- uty_cli_hist_add (vty->vio, vty->buf) ;
-
- /* If parsed and not empty, dispatch */
- if (ret == CMD_SUCCESS)
- {
-#ifdef CONSUMED_TIME_CHECK
- RUSAGE_T before;
- RUSAGE_T after;
- unsigned long realtime, cputime;
-
- GETRUSAGE(&before);
-#endif /* CONSUMED_TIME_CHECK */
-
- ret = cmd_dispatch(vty, cmd_may_queue) ;
-
-#ifdef CONSUMED_TIME_CHECK
- GETRUSAGE(&after);
- if ((realtime = thread_consumed_time(&after, &before, &cputime)) >
- CONSUMED_TIME_CHECK)
- /* Warn about CPU hog that must be fixed. */
- uzlog(NULL, LOG_WARNING,
- "SLOW COMMAND: command took %lums (cpu time %lums): %s",
- realtime/1000, cputime/1000, vty->buf) ;
-#endif /* CONSUMED_TIME_CHECK */
- } ;
-
- /* Deal with the return code */
- switch (ret)
- {
- case CMD_ERR_AMBIGUOUS:
- vty_out_error(vty, "%% Ambiguous command.\n");
- break;
-
- case CMD_ERR_NO_MATCH:
- vty_out_error(vty, "%% Unknown command.\n") ;
- break;
-
- case CMD_ERR_INCOMPLETE:
- vty_out_error(vty, "%% Command incomplete.\n");
- break;
-
- default:
- break ;
- } ;
-
- return ret ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Authentication of vty
- *
- * During AUTH_NODE and AUTH_ENABLE_NODE, when a command line is dispatched by
- * any means this function is called.
- *
- * Note that if the AUTH_NODE password fails too many times, the terminal is
- * closed.
- *
- * Returns: command return code
- */
-extern enum cmd_return_code
-uty_auth (struct vty *vty, const char *buf, enum cli_do cli_do)
-{
- char *passwd = NULL;
- enum node_type next_node = 0;
- int fail;
- char *crypt (const char *, const char *);
- enum cmd_return_code ret ;
-
- vty_io vio = vty->vio ;
-
- VTY_ASSERT_LOCKED() ;
- VTY_ASSERT_CLI_THREAD() ;
-
- /* What to do ?
- *
- * In fact, all the exotic command terminators simply discard any input
- * and return.
- */
- switch (cli_do)
- {
- case cli_do_nothing:
- case cli_do_ctrl_c:
- case cli_do_ctrl_z:
- return CMD_SUCCESS ;
-
- case cli_do_command:
- break ;
-
- case cli_do_ctrl_d:
- case cli_do_eof:
- return uty_cmd_close(vty, "End") ;
-
- default:
- zabort("unknown or invalid cli_do") ;
- } ;
-
- /* Ordinary command dispatch -- see if password is OK. */
- switch (vty->node)
- {
- case AUTH_NODE:
- if (host.encrypt)
- passwd = host.password_encrypt;
- else
- passwd = host.password;
- if (host.advanced)
- next_node = host.enable ? VIEW_NODE : ENABLE_NODE;
- else
- next_node = VIEW_NODE;
- break;
-
- case AUTH_ENABLE_NODE:
- if (host.encrypt)
- passwd = host.enable_encrypt;
- else
- passwd = host.enable;
- next_node = ENABLE_NODE;
- break;
-
- default:
- zabort("unknown node type") ;
- }
-
- if (passwd)
- {
- if (host.encrypt)
- fail = strcmp (crypt(buf, passwd), passwd);
- else
- fail = strcmp (buf, passwd);
- }
- else
- fail = 1;
-
- ret = CMD_SUCCESS ;
-
- if (! fail)
- {
- vio->fail = 0;
- vty->node = next_node; /* Success ! */
- }
- else
- {
- vio->fail++;
- if (vio->fail >= 3)
- {
- if (vty->node == AUTH_NODE)
- {
- ret = uty_cmd_close(vty, "Bad passwords, too many failures!") ;
- }
- else
- {
- /* AUTH_ENABLE_NODE */
- vio->fail = 0;
- vty_out_error(vty,
- "%% Bad enable passwords, too many failures!\n") ;
- vty->node = restricted_mode ? RESTRICTED_NODE : VIEW_NODE;
-
- ret = CMD_WARNING ;
- }
- }
- }
-
- return ret ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Command line "exit" command -- aka "quit"
- *
- * Falls back one NODE level.
- *
- * Returns: command return code
- */
-extern enum cmd_return_code
-vty_cmd_exit(struct vty* vty)
-{
- enum cmd_return_code ret ;
-
- VTY_LOCK() ;
-
- ret = CMD_SUCCESS ;
- switch (vty->node)
- {
- case VIEW_NODE:
- case ENABLE_NODE:
- case RESTRICTED_NODE:
- if (uty_shell_client(vty))
- exit (0);
- else
- ret = uty_cmd_close(vty, "Exit") ;
- break;
- case CONFIG_NODE:
- uty_config_unlock (vty, ENABLE_NODE);
- break;
- case INTERFACE_NODE:
- case ZEBRA_NODE:
- case BGP_NODE:
- case RIP_NODE:
- case RIPNG_NODE:
- case OSPF_NODE:
- case OSPF6_NODE:
- case ISIS_NODE:
- case KEYCHAIN_NODE:
- case MASC_NODE:
- case RMAP_NODE:
- case VTY_NODE:
- vty->node = CONFIG_NODE ;
- break;
- case BGP_VPNV4_NODE:
- case BGP_IPV4_NODE:
- case BGP_IPV4M_NODE:
- case BGP_IPV6_NODE:
- case BGP_IPV6M_NODE:
- vty->node = BGP_NODE ;
- break;
- case KEYCHAIN_KEY_NODE:
- vty->node = KEYCHAIN_NODE ;
- break;
- default:
- break;
- }
-
- VTY_UNLOCK() ;
- return ret ;
-}
-
-/*------------------------------------------------------------------------------
- * Command line "end" command
- *
- * Falls back to ENABLE_NODE.
- *
- * Returns: command return code
- */
-extern enum cmd_return_code
-vty_cmd_end(struct vty* vty)
-{
- VTY_LOCK() ;
-
- switch (vty->node)
- {
- case VIEW_NODE:
- case ENABLE_NODE:
- case RESTRICTED_NODE:
- /* Nothing to do. */
- break;
- case CONFIG_NODE:
- case INTERFACE_NODE:
- case ZEBRA_NODE:
- case RIP_NODE:
- case RIPNG_NODE:
- case BGP_NODE:
- case BGP_VPNV4_NODE:
- case BGP_IPV4_NODE:
- case BGP_IPV4M_NODE:
- case BGP_IPV6_NODE:
- case BGP_IPV6M_NODE:
- case RMAP_NODE:
- case OSPF_NODE:
- case OSPF6_NODE:
- case ISIS_NODE:
- case KEYCHAIN_NODE:
- case KEYCHAIN_KEY_NODE:
- case MASC_NODE:
- case VTY_NODE:
- uty_config_unlock (vty, ENABLE_NODE);
- break;
- default:
- break;
- }
-
- VTY_UNLOCK() ;
- return CMD_SUCCESS ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Result of command is to close the input.
- *
- * Posts the reason for the close.
- *
- * Returns: CMD_CLOSE
- */
-extern enum cmd_return_code
-uty_cmd_close(struct vty *vty, const char* reason)
-{
- vty->vio->close_reason = reason ;
- return CMD_CLOSE ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Command line ^C action.
- *
- * Ignores contents of command line (including not adding to history).
- *
- * Fall back to ENABLE_NODE if in any one of a number of nodes.
- *
- * Resets the history pointer.
- *
- * Returns: command return code
- */
-extern enum cmd_return_code
-uty_stop_input(struct vty *vty)
-{
- vty_io vio = vty->vio ;
-
- VTY_ASSERT_LOCKED() ;
-
- switch (vty->node)
- {
- 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;
- }
-
- /* Set history pointer to the latest one. */
- vio->hp = vio->hindex;
-
- return CMD_SUCCESS ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Command ^Z action.
- *
- * Ignores contents of command line (including not adding to history).
- *
- * Fall back to ENABLE_NODE if in any one of a number of nodes.
- *
- * Returns: command return code
- */
-extern enum cmd_return_code
-uty_end_config (struct vty *vty)
-{
- VTY_ASSERT_LOCKED() ;
-
- switch (vty->node)
- {
- case VIEW_NODE:
- case ENABLE_NODE:
- case RESTRICTED_NODE:
- /* Nothing to do. */
- break;
- case CONFIG_NODE:
- case INTERFACE_NODE:
- case ZEBRA_NODE:
- case RIP_NODE:
- case RIPNG_NODE:
- case BGP_NODE:
- case BGP_VPNV4_NODE:
- case BGP_IPV4_NODE:
- case BGP_IPV4M_NODE:
- case BGP_IPV6_NODE:
- case BGP_IPV6M_NODE:
- case RMAP_NODE:
- case OSPF_NODE:
- case OSPF6_NODE:
- case ISIS_NODE:
- case KEYCHAIN_NODE:
- case KEYCHAIN_KEY_NODE:
- case MASC_NODE:
- case VTY_NODE:
- uty_config_unlock (vty, ENABLE_NODE) ;
- break;
- default:
- /* Unknown node, we have to ignore it. */
- break;
- }
-
- return CMD_SUCCESS ;
-}
-
-/*------------------------------------------------------------------------------
- * Command ^D action -- when nothing else on command line.
- *
- * Same as "exit" command.
- *
- * Returns: command return code
- */
-extern enum cmd_return_code
-uty_down_level (struct vty *vty)
-{
- return vty_cmd_exit(vty) ;
-} ;
-
-/*==============================================================================
* Reading of configuration file
*
* The reading of the configuration file occurs at two times:
@@ -1116,18 +681,18 @@ uty_down_level (struct vty *vty)
* run directly in the thread -- no commands are queued.
*/
-static FILE * vty_use_backup_config (char *fullpath) ;
-static void vty_read_file (FILE *confp, struct cmd_element* first_cmd,
- bool ignore_warnings) ;
+static int vty_use_backup_config (const char *fullpath) ;
+static void vty_read_file (int conf_fd, const char* name,
+ cmd_command first_cmd, bool ignore_warnings) ;
/*------------------------------------------------------------------------------
* Read the given configuration file.
*/
extern void
-vty_read_config (char *config_file,
- char *config_default)
+vty_read_config (const char *config_file,
+ const char *config_default)
{
- vty_read_config_first_cmd_special(config_file, config_default, NULL, 1);
+ vty_read_config_first_cmd_special(config_file, config_default, NULL, true);
}
/*------------------------------------------------------------------------------
@@ -1148,15 +713,15 @@ vty_read_config (char *config_file,
* command.
*/
extern void
-vty_read_config_first_cmd_special(char *config_file,
- char *config_default,
- struct cmd_element* first_cmd,
+vty_read_config_first_cmd_special(const char *config_file,
+ const char *config_default,
+ cmd_command first_cmd,
bool ignore_warnings)
{
- char cwd[MAXPATHLEN];
- FILE *confp = NULL;
- char *fullpath;
- char *tmp = NULL;
+ char cwd[MAXPATHLEN] ;
+ int conf_fd ;
+ const char *fullpath ;
+ char *tmp = NULL ;
/* Deal with VTYSH_ENABLED magic */
if (VTYSH_ENABLED && (config_file == NULL))
@@ -1204,15 +769,15 @@ vty_read_config_first_cmd_special(char *config_file,
} ;
/* try to open the configuration file */
- confp = fopen (fullpath, "r");
+ conf_fd = uty_vfd_file_open(fullpath, vfd_io_read | vfd_io_blocking) ;
- if (confp == NULL)
+ if (conf_fd < 0)
{
fprintf (stderr, "%s: failed to open configuration file %s: %s\n",
__func__, fullpath, errtostr(errno, 0).str);
- confp = vty_use_backup_config (fullpath);
- if (confp)
+ conf_fd = vty_use_backup_config (fullpath);
+ if (conf_fd >= 0)
fprintf (stderr, "WARNING: using backup configuration file!\n");
else
{
@@ -1226,8 +791,7 @@ vty_read_config_first_cmd_special(char *config_file,
fprintf(stderr, "Reading config file: %s\n", fullpath);
#endif
- vty_read_file (confp, first_cmd, ignore_warnings);
- fclose (confp);
+ vty_read_file(conf_fd, fullpath, first_cmd, ignore_warnings);
host_config_set (fullpath);
@@ -1249,17 +813,17 @@ vty_read_config_first_cmd_special(char *config_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.
+ * Returns: < 0 => no "<fullpath>.sav", or failed doing any of the above
+ * >= 0 otherwise, fd file.
*/
-static FILE *
-vty_use_backup_config (char *fullpath)
+static int
+vty_use_backup_config (const char *fullpath)
{
- char *tmp_path ;
+ char *tmp_path ;
struct stat buf;
- int ret, tmp, sav;
- int c;
- char buffer[4096] ;
+ int ret, tmp, sav;
+ int c, err;
+ char* buffer ;
enum { xl = 32 } ;
tmp_path = malloc(strlen(fullpath) + xl) ;
@@ -1268,14 +832,17 @@ vty_use_backup_config (char *fullpath)
confirm(xl > sizeof(CONF_BACKUP_EXT)) ;
sprintf (tmp_path, "%s%s", fullpath, CONF_BACKUP_EXT) ;
- sav = -1 ;
if (stat (tmp_path, &buf) != -1)
- sav = open (tmp_path, O_RDONLY);
+ sav = uty_vfd_file_open(tmp_path, vfd_io_read | vfd_io_blocking) ;
+ else
+ sav = -1 ;
if (sav < 0)
{
- free (tmp_path);
- return NULL;
+ err = errno ; /* making sure */
+ free (tmp_path) ;
+ errno = err ;
+ return sav ;
} ;
/* construct a temporary file and copy "<fullpath.sav>" to it. */
@@ -1286,34 +853,60 @@ vty_use_backup_config (char *fullpath)
tmp = mkstemp (tmp_path);
if (tmp < 0)
{
+ err = errno ;
free (tmp_path);
close(sav);
- return NULL;
+ errno = err ;
+ return tmp;
}
- while((c = read (sav, buffer, sizeof(buffer))) > 0)
- write (tmp, buffer, c);
+ enum { buffer_size = 64 * 1024 } ;
+ buffer = malloc(buffer_size) ;
+
+ c = 1 ;
+ while (c > 0)
+ {
+ c = read(sav, buffer, buffer_size) ;
+ if (c > 0)
+ {
+ if (write(tmp, buffer, c) < c)
+ c = -1 ;
+ } ;
+ } ;
+ err = errno ;
+
+ free(buffer) ;
+ close(sav) ;
+ close(tmp) ;
- close (sav);
- close (tmp);
+ if (c < 0)
+ {
+ errno = err ;
+ return c ;
+ } ;
/* Make sure that have the required file status */
if (chmod(tmp_path, CONFIGFILE_MASK) != 0)
{
+ err = errno ;
unlink (tmp_path);
free (tmp_path);
- return NULL;
- }
+ errno = err ;
+ return -1 ;
+ } ;
/* Make <fullpath> be a name for the new file just created. */
ret = link (tmp_path, fullpath) ;
/* Discard the temporary, now */
+ err = errno ;
unlink (tmp_path) ;
free (tmp_path) ;
+ errno = err ;
/* If link was successful, try to open -- otherwise, failed. */
- return (ret == 0) ? fopen (fullpath, "r") : NULL ;
+ return (ret == 0) ? uty_vfd_file_open(fullpath, vfd_io_read | vfd_io_blocking)
+ : -1 ;
} ;
/*------------------------------------------------------------------------------
@@ -1337,68 +930,49 @@ vty_use_backup_config (char *fullpath)
* so all commands are executed directly.
*/
static void
-vty_read_file (FILE *confp, struct cmd_element* first_cmd, bool ignore_warnings)
+vty_read_file (int conf_fd, const char* name,
+ cmd_command first_cmd, bool ignore_warnings)
{
- enum cmd_return_code ret ;
- struct vty *vty ;
+ cmd_return_code_t ret ;
+ vty vty ;
+ vty_io vio ;
+ vio_vf vf ;
+
+ VTY_LOCK() ;
/* Set up configuration file reader VTY -- which buffers all output */
vty = vty_open(VTY_CONFIG_READ);
vty->node = CONFIG_NODE;
- /* 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) ;
+ vio = vty->vio ;
- /* Execute configuration file */
- ret = config_from_file (vty, confp, first_cmd, &vty->vio->clx,
- ignore_warnings) ;
-
- VTY_LOCK() ;
-
- if (ret != CMD_SUCCESS)
- {
- fprintf (stderr, "%% while processing line %u of the configuration:\n"
- "%s", vty->lineno, vty->buf) ;
-
- switch (ret)
- {
- case CMD_WARNING:
- fprintf (stderr, "%% Warning...\n");
- break;
+ vf = uty_vf_new(vio, name, conf_fd, vfd_file, vfd_io_read | vfd_io_blocking) ;
- case CMD_ERROR:
- fprintf (stderr, "%% Error...\n");
- break;
+ uty_vin_open( vio, vf, VIN_CONFIG, NULL, NULL, 64 * 1024) ;
+ uty_vout_open(vio, vf, VOUT_STDERR, NULL, NULL, 4 * 1024) ;
- case CMD_ERR_AMBIGUOUS:
- fprintf (stderr, "%% Ambiguous command.\n");
- break;
+ vio->vin->parse_type = cmd_parse_strict | cmd_parse_no_do ;
- case CMD_ERR_NO_MATCH:
- fprintf (stderr, "%% There is no such command.\n");
- break;
+ /* When we get here the VTY is set up and all ready to go. */
+ uty_cmd_prepare(vio) ;
- case CMD_ERR_INCOMPLETE:
- fprintf (stderr, "%% Incomplete command.\n");
- break;
+ VTY_UNLOCK() ;
- default:
- fprintf(stderr, "%% (unknown cause %d)\n", ret) ;
- break ;
- } ;
+ /* Execute configuration file */
+ ret = cmd_read_config(vty, first_cmd, ignore_warnings) ;
- uty_out_fflush(vty->vio, stderr) ; /* flush command output buffer */
+ ret = vty_cmd_loop_exit(vty, ret) ;
+ if (ret != CMD_SUCCESS)
+ {
+ vty_close(vty, true, qs_set(NULL, "Error in configuration file.")) ;
exit (1);
} ;
- uty_close(vty->vio) ;
- VTY_UNLOCK() ;
+ vty_close(vty, false, NULL) ;
} ;
+#if 0
/*------------------------------------------------------------------------------
* Flush the contents of the command output FIFO to the given file.
*
@@ -1414,84 +988,59 @@ uty_out_fflush(vty_io vio, FILE* file)
fflush(file) ;
- while ((src = vio_fifo_get_lump(&vio->cmd_obuf, &have)) != NULL)
+ src = vio_fifo_get_lump(&vio->cmd_obuf, &have) ;
+ while (src != NULL)
{
fwrite(src, 1, have, file) ;
- vio_fifo_got_upto(&vio->cmd_obuf, src + have) ;
+ src = vio_fifo_step_get_lump(&vio->cmd_obuf, &have, have) ;
} ;
fflush(file) ;
} ;
-
-
-
+#endif
/*==============================================================================
- * Configuration node/state handling
- *
- * At most one VTY may hold the configuration symbol of power at any time.
+ * For writing configuration file by command, temporarily redirect output to
+ * an actual file.
*/
/*------------------------------------------------------------------------------
- * Attempt to gain the configuration symbol of power
- *
- * If succeeds, set the given node.
- *
- * Returns: true <=> now own the symbol of power.
+ * Set the given fd as the VTY_FILE output.
*/
-extern bool
-vty_config_lock (struct vty *vty, enum node_type node)
+extern void
+vty_open_config_write(vty vty, int fd)
{
- bool result;
+ vty_io vio ;
+ vio_vf vf ;
VTY_LOCK() ;
- if (!vty_config)
- {
- vty->config = true ;
- vty_config = true ;
- } ;
+ vio = vty->vio ;
- result = vty->config;
+ vf = uty_vf_new(vio, "config write", fd, vfd_file, vfd_io_read) ;
- if (result)
- vty->node = node ;
+ uty_vout_open(vio, vf, VOUT_FILE, NULL, NULL, 16 * 1024) ;
VTY_UNLOCK() ;
-
- return result;
-}
+} ;
/*------------------------------------------------------------------------------
- * Give back the configuration symbol of power -- if own it.
- *
- * Set the given node -- which must be <= MAX_NON_CONFIG_NODE
+ * Write away any pending stuff, and return the VTY to normal.
*/
-extern void
-vty_config_unlock (struct vty *vty, enum node_type node)
+extern int
+vty_close_config_write(struct vty* vty)
{
+// vty_io vio ;
+// int err ;
+
VTY_LOCK() ;
- uty_config_unlock(vty, node);
- VTY_UNLOCK() ;
-}
-/*------------------------------------------------------------------------------
- * 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)
-{
- VTY_ASSERT_LOCKED() ;
- if (vty_config && vty->config)
- {
- vty->config = false ;
- vty_config = false ;
- }
+ uty_vout_close(vty->vio, false) ;
+
+ VTY_UNLOCK() ;
- assert(node <= MAX_NON_CONFIG_NODE) ;
- vty->node = node ;
+// return err ;
+ return 0 ;
} ;
/*==============================================================================
@@ -1508,13 +1057,15 @@ DEFUN_CALL (config_who,
VTY_LOCK() ;
- vio = vio_list_base ;
+ vio = vio_list_base ; /* once locked */
+
while (vio != NULL) /* TODO: show only VTY_TERM ??? */
{
- uty_output (vty, "%svty[%d] connected from %s.\n",
+ vty_out(vty, "%svty[%d] connected from %s.\n",
vio->vty->config ? "*" : " ", i, uty_get_name(vio));
vio = sdl_next(vio, vio_list) ;
} ;
+
VTY_UNLOCK() ;
return CMD_SUCCESS;
}
@@ -1532,7 +1083,11 @@ DEFUN_CALL (line_vty,
return CMD_SUCCESS;
}
-/* Set time out value. */
+/*------------------------------------------------------------------------------
+ * Set time out value.
+ *
+ * Affects this and any future VTY_TERMINAL or VTY_SHELL_SERVER type VTY.
+ */
static int
exec_timeout (struct vty *vty, const char *min_str, const char *sec_str)
{
@@ -1550,10 +1105,9 @@ exec_timeout (struct vty *vty, const char *min_str, const char *sec_str)
if (sec_str)
timeout += strtol (sec_str, NULL, 10);
- vty_timeout_val = timeout;
+ host.vty_timeout_val = timeout;
- if (uty_is_terminal(vty) || uty_is_shell_server(vty))
- uty_sock_set_timer(&vty->vio->sock, timeout) ;
+ uty_set_timeout(vty->vio, timeout) ; /* update own timeout, if required */
VTY_UNLOCK() ;
return CMD_SUCCESS;
@@ -1596,10 +1150,9 @@ DEFUN_CALL (vty_access_class,
{
VTY_LOCK() ;
- if (vty_accesslist_name)
- XFREE(MTYPE_VTY, vty_accesslist_name);
+ XFREE(MTYPE_HOST, host.vty_accesslist_name);
- vty_accesslist_name = XSTRDUP(MTYPE_VTY, argv[0]);
+ host.vty_accesslist_name = XSTRDUP(MTYPE_HOST, argv[0]);
VTY_UNLOCK() ;
return CMD_SUCCESS;
@@ -1613,22 +1166,25 @@ DEFUN_CALL (no_vty_access_class,
"Filter connections based on an IP access list\n"
"IP access list\n")
{
- int result = CMD_SUCCESS;
+ cmd_return_code_t ret ;
VTY_LOCK() ;
- if (! vty_accesslist_name || (argc && strcmp(vty_accesslist_name, argv[0])))
+
+ if ((argc == 0) || ( (host.vty_accesslist_name != NULL) &&
+ (strcmp(host.vty_accesslist_name, argv[0]) == 0) ))
{
- vty_out_error(vty, "Access-class is not currently applied to vty\n");
- result = CMD_WARNING;
+ XFREE(MTYPE_HOST, host.vty_accesslist_name);
+ /* sets host.vty_accesslist_name = NULL */
+ ret = CMD_SUCCESS ;
}
else
{
- XFREE(MTYPE_VTY, vty_accesslist_name);
- vty_accesslist_name = NULL;
- }
+ vty_out(vty, "Access-class is not currently applied to vty\n") ;
+ ret = CMD_WARNING;
+ } ;
VTY_UNLOCK() ;
- return result;
+ return ret;
}
#ifdef HAVE_IPV6
@@ -1641,10 +1197,10 @@ DEFUN_CALL (vty_ipv6_access_class,
"IPv6 access list\n")
{
VTY_LOCK() ;
- if (vty_ipv6_accesslist_name)
- XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
- vty_ipv6_accesslist_name = XSTRDUP(MTYPE_VTY, argv[0]);
+ XFREE(MTYPE_HOST, host.vty_ipv6_accesslist_name);
+
+ host.vty_ipv6_accesslist_name = XSTRDUP(MTYPE_HOST, argv[0]);
VTY_UNLOCK() ;
return CMD_SUCCESS;
@@ -1659,25 +1215,25 @@ DEFUN_CALL (no_vty_ipv6_access_class,
"Filter connections based on an IP access list\n"
"IPv6 access list\n")
{
- int result = CMD_SUCCESS;
+ cmd_return_code_t ret ;
VTY_LOCK() ;
- if (! vty_ipv6_accesslist_name ||
- (argc && strcmp(vty_ipv6_accesslist_name, argv[0])))
+ if ((argc == 0) || ( (host.vty_ipv6_accesslist_name != NULL) &&
+ (strcmp(host.vty_ipv6_accesslist_name, argv[0]) == 0) ))
{
- vty_out_error(vty, "IPv6 access-class is not currently applied to vty\n") ;
- result = CMD_WARNING;
+ XFREE(MTYPE_HOST, host.vty_ipv6_accesslist_name);
+ /* sets host.vty_ipv6_accesslist_name = NULL */
+ ret = CMD_SUCCESS ;
}
else
{
- XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
-
- vty_ipv6_accesslist_name = NULL;
- }
+ vty_out(vty, "IPv6 access-class is not currently applied to vty\n") ;
+ ret = CMD_WARNING;
+ } ;
VTY_UNLOCK() ;
- return CMD_SUCCESS;
+ return ret;
}
#endif /* HAVE_IPV6 */
@@ -1688,7 +1244,7 @@ DEFUN_CALL (vty_login,
"Enable password checking\n")
{
VTY_LOCK() ;
- no_password_check = 0;
+ host.no_password_check = false ;
VTY_UNLOCK() ;
return CMD_SUCCESS;
}
@@ -1700,7 +1256,7 @@ DEFUN_CALL (no_vty_login,
"Enable password checking\n")
{
VTY_LOCK() ;
- no_password_check = 1;
+ host.no_password_check = true ;
VTY_UNLOCK() ;
return CMD_SUCCESS;
}
@@ -1712,7 +1268,7 @@ DEFUN_CALL (vty_restricted_mode,
"Restrict view commands available in anonymous, unauthenticated vty\n")
{
VTY_LOCK() ;
- restricted_mode = 1;
+ host.restricted_mode = true;
VTY_UNLOCK() ;
return CMD_SUCCESS;
}
@@ -1724,7 +1280,7 @@ DEFUN_CALL (vty_no_restricted_mode,
"Enable password checking\n")
{
VTY_LOCK() ;
- restricted_mode = 0;
+ host.restricted_mode = false;
VTY_UNLOCK() ;
return CMD_SUCCESS;
}
@@ -1736,7 +1292,7 @@ DEFUN_CALL (service_advanced_vty,
"Enable advanced mode vty interface\n")
{
VTY_LOCK() ;
- host.advanced = 1;
+ host.advanced = true;
VTY_UNLOCK() ;
return CMD_SUCCESS;
}
@@ -1749,7 +1305,7 @@ DEFUN_CALL (no_service_advanced_vty,
"Enable advanced mode vty interface\n")
{
VTY_LOCK() ;
- host.advanced = 0;
+ host.advanced = false;
VTY_UNLOCK() ;
return CMD_SUCCESS;
}
@@ -1792,26 +1348,10 @@ DEFUN_CALL (show_history,
SHOW_STR
"Display the session command history\n")
{
- int index;
-
VTY_LOCK() ;
- for (index = vty->vio->hindex + 1; index != vty->vio->hindex;)
- {
- qstring line ;
-
- if (index == VTY_MAXHIST)
- {
- index = 0;
- continue;
- }
-
- line = vector_get_item(vty->vio->hist, index) ;
- if (line != NULL)
- uty_output (vty, " %s\n", line->char_body);
-
- index++;
- }
+ if (vty->type == VTY_TERMINAL)
+ uty_cli_hist_show(vty->vio->vin->cli) ;
VTY_UNLOCK() ;
return CMD_SUCCESS;
@@ -1827,22 +1367,22 @@ vty_config_write (struct vty *vty)
{
vty_out (vty, "line vty\n");
- if (vty_accesslist_name)
- vty_out (vty, " access-class %s\n", vty_accesslist_name);
+ if (host.vty_accesslist_name)
+ vty_out (vty, " access-class %s\n", host.vty_accesslist_name);
- if (vty_ipv6_accesslist_name)
- vty_out (vty, " ipv6 access-class %s\n", vty_ipv6_accesslist_name);
+ if (host.vty_ipv6_accesslist_name)
+ vty_out (vty, " ipv6 access-class %s\n", host.vty_ipv6_accesslist_name);
/* exec-timeout */
- if (vty_timeout_val != VTY_TIMEOUT_DEFAULT)
- vty_out (vty, " exec-timeout %ld %ld\n", vty_timeout_val / 60,
- vty_timeout_val % 60);
+ if (host.vty_timeout_val != VTY_TIMEOUT_DEFAULT)
+ vty_out (vty, " exec-timeout %ld %ld\n", host.vty_timeout_val / 60,
+ host.vty_timeout_val % 60);
/* login */
- if (no_password_check)
+ if (host.no_password_check)
vty_out (vty, " no login\n");
- if (restricted_mode != restricted_mode_default)
+ if (host.restricted_mode != restricted_mode_default)
{
if (restricted_mode_default)
vty_out (vty, " no anonymous restricted\n");
@@ -1879,7 +1419,7 @@ vty_save_cwd (void)
getcwd (cwd, MAXPATHLEN);
}
- vty_cwd = XSTRDUP(MTYPE_TMP, cwd) ;
+ host.vty_cwd = XSTRDUP(MTYPE_HOST, cwd) ;
} ;
/*------------------------------------------------------------------------------
@@ -1888,12 +1428,13 @@ vty_save_cwd (void)
char *
vty_get_cwd ()
{
- return vty_cwd;
+ return host.vty_cwd;
}
/*==============================================================================
* Access functions for VTY values, where locking is or might be required.
*/
+#if 0
static bool
vty_is_terminal(struct vty *vty)
@@ -1925,31 +1466,16 @@ vty_is_shell_client (struct vty *vty)
return result;
}
-enum node_type
-vty_get_node(struct vty *vty)
-{
- int result;
- VTY_LOCK() ;
- result = vty->node;
- VTY_UNLOCK() ;
- return result;
-}
-
-void
-vty_set_node(struct vty *vty, enum node_type node)
-{
- VTY_LOCK() ;
- vty->node = node;
- VTY_UNLOCK() ;
-}
+#endif
void
vty_set_lines(struct vty *vty, int lines)
{
VTY_LOCK() ;
- vty->vio->lines = lines;
- vty->vio->lines_set = 1 ;
- uty_set_height(vty->vio) ;
+
+ if (vty->type == VTY_TERMINAL)
+ uty_cli_set_lines(vty->vio->vin->cli, lines, true) ;
+
VTY_UNLOCK() ;
}
diff --git a/lib/vty.h b/lib/vty.h
index b8621588..99b08d96 100644
--- a/lib/vty.h
+++ b/lib/vty.h
@@ -1,4 +1,4 @@
-/* VTY top level
+/* VTY external interface -- header
* Copyright (C) 1997, 98 Kunihiro Ishiguro
*
* Revisions: Copyright (C) 2010 Chris Hall (GMCH), Highwayman
@@ -25,6 +25,10 @@
#define _ZEBRA_VTY_H
#include "misc.h"
+#include "vargs.h"
+
+#include "command_common.h" /* NB: *not* command.h */
+#include "vty_common.h" /* struct vty & VTY types */
#include "thread.h"
#include "log.h"
@@ -35,128 +39,12 @@
#include "list_util.h"
#include "vector.h"
#include "qstring.h"
-#include "node_type.h"
-
-/*==============================================================================
- * The VTYSH uses a unix socket to talk to the daemon.
- *
- * The ability to respond to a connection from VTYSH appears to be a *compile*
- * time option. In the interests of keeping the code up to date, the VTYSH
- * option is turned into a testable constant.
- */
-#ifdef VTYSH
- enum { VTYSH_ENABLED = true } ;
-#else
- enum { VTYSH_ENABLED = false } ;
-#endif
-
-/*==============================================================================
- * VTY Types
- */
-enum vty_type /* Command output */
-{
- VTY_TERMINAL, /* a telnet terminal server */
- VTY_SHELL_SERVER, /* a vty_shell server */
-
- VTY_SHELL_CLIENT, /* a vty_shell client */
-
- VTY_CONFIG_READ, /* configuration file reader */
-
- VTY_STDOUT, /* stdout */
- VTY_STDERR, /* stderr */
-} ;
-typedef enum vty_type vty_type_t ;
-
-/*==============================================================================
- *
- *
- *
- *
- */
-
-typedef unsigned long vty_timer_time ; /* Time out time in seconds */
-
-enum
-{
- VTY_WATCH_DOG_INTERVAL = 5, /* interval between barks */
-
- VTY_HALF_CLOSE_TIMEOUT = 120, /* timeout after half_close */
-
- VTY_TIMEOUT_DEFAULT = 600, /* terminal timeout value */
-} ;
/*==============================================================================
- * VTY struct.
- */
-
-typedef struct vty_io* vty_io ; /* private to vty.c */
-
-struct cmd_parsed ; /* in case vty.h expanded before command.h */
-
-typedef struct vty* vty ;
-struct vty
-{
- vty_type_t type ;
-
- /*----------------------------------------------------------------------
- * The following are the context in which commands are executed.
- */
-
- /* Node status of this vty
- *
- * NB: when using qpthreads should lock VTY to access this -- so use
- * vty_get_node() and vty_set_node().
- */
- enum node_type node ;
-
- /* For current referencing point of interface, route-map, access-list
- * etc...
- *
- * NB: this value is private to the command execution, which is assumed
- * to all be in the one thread... so no lock required.
- */
- void *index ;
-
- /* For multiple level index treatment such as key chain and key.
- *
- * NB: this value is private to the command execution, which is assumed
- * to all be in the one thread... so no lock required.
- */
- void *index_sub ;
-
- /* In configure mode. */
- bool config;
-
- /*----------------------------------------------------------------------------
- * The current command line.
- *
- * These are set when a command is parsed and dispatched.
- *
- * They are not touched until the command completes -- so may be read while
- * the command is being parsed and executed.
- */
- const char* buf ;
- struct cmd_parsed* parsed ;
- unsigned lineno ;
-
- bool output_enabled ;
- bool reflect_enabled ;
- bool more_enabled ;
-
- bool reflected ;
-
- /*----------------------------------------------------------------------
- * The following are used inside vty.c only.
- */
- vty_io vio ; /* one vio object per vty */
-} ;
-
-/*------------------------------------------------------------------------------
- * Can now include this
+ * These are definitions and functions for things which are required outside
+ * the vty and command family.
*/
-#include "command.h"
-
/*------------------------------------------------------------------------------
*
*/
@@ -187,13 +75,6 @@ enum { VTY_MAX_SPACES = 40 } ;
#define IS_DIRECTORY_SEP(c) ((c) == DIRECTORY_SEP)
#endif
-/* GCC have printf type attribute check. */
-#ifdef __GNUC__
-#define PRINTF_ATTRIBUTE(a,b) __attribute__ ((__format__ (__printf__, a, b)))
-#else
-#define PRINTF_ATTRIBUTE(a,b)
-#endif /* __GNUC__ */
-
/* Utility macros to convert VTY argument to unsigned long or integer. */
#define VTY_GET_LONG(NAME,V,STR) \
do { \
@@ -267,22 +148,23 @@ extern void vty_reset_because(const char* why) ;
extern int vty_out (struct vty *, const char *, ...) PRINTF_ATTRIBUTE(2, 3);
extern int vty_out_indent(struct vty *vty, int indent) ;
-extern int vty_out_error (struct vty *vty, const char *format, ...)
- PRINTF_ATTRIBUTE(2, 3);
extern void vty_out_clear(struct vty *vty) ;
-extern void vty_read_config (char *config_file, char *config_default);
-extern void vty_read_config_first_cmd_special(
- char *config_file, char *config_default,
- struct cmd_element* first_cmd, bool ignore_warnings) ;
+
+
+
+
+extern void vty_read_config (const char* config_file,
+ const char* config_default);
+extern void vty_read_config_first_cmd_special(const char* config_file,
+ const char* config_default,
+ cmd_command first_cmd,
+ bool ignore_warnings) ;
extern void vty_time_print (struct vty *, int);
extern char *vty_get_cwd (void);
extern void vty_hello (struct vty *);
-extern enum node_type vty_get_node(struct vty *);
-extern void vty_set_node(struct vty *, enum node_type);
-extern void vty_set_lines(struct vty *, int);
#endif /* _ZEBRA_VTY_H */
diff --git a/lib/vty_cli.c b/lib/vty_cli.c
index 17351f58..42d78349 100644
--- a/lib/vty_cli.c
+++ b/lib/vty_cli.c
@@ -20,17 +20,20 @@
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
+#include "misc.h"
+#include <ctype.h>
-#include <zebra.h>
-
-#include "keystroke.h"
-#include "vty.h"
-#include "uty.h"
+#include "vty_local.h"
+#include "vty_command.h"
#include "vty_cli.h"
+#include "vty_io_term.h"
#include "vty_io.h"
#include "vio_lines.h"
+#include "vio_fifo.h"
-#include "command.h"
+#include "keystroke.h"
+
+#include "command_common.h"
#include "command_parse.h"
#include "command_execute.h"
#include "command_queue.h"
@@ -38,86 +41,323 @@
#include "memory.h"
/*==============================================================================
- * Host name handling
+ * Construct and destroy CLI object
+ *
+ *
+ *
+ */
+static bool uty_cli_iac_callback(keystroke_iac_callback_args) ;
+static void uty_cli_update_more(vty_cli cli) ;
+
+/*------------------------------------------------------------------------------
+ * Construct and initialise a new CLI object -- never embedded.
*
- * The host name is used in the command line prompt. The name used is either
- * the name set by "hostname" command, or the current machine host name.
+ * The CLI is started up in the state it is in waiting for a command to
+ * complete. This means that all start-up messages etc. are handled as the
+ * output from an implied start command. uty_cli_start() is called when the
+ * start-up messages etc. are complete.
*
- * Static variables -- under the VTY_LOCK !
+ * Note that the vty_io_term stuff sends out a bunch of telnet commands, which
+ * will be buffered in the CLI buffer. That will be output, before the
+ * start-up messages etc. on uty_cli_start().
*/
+extern vty_cli
+uty_cli_new(vio_vf vf)
+{
+ vty_cli cli ;
+
+ cli = XCALLOC(MTYPE_VTY_CLI, sizeof(struct vty_cli)) ;
+
+ /* Zeroising has initialised:
+ *
+ * vf = NULL -- set below
+ *
+ * hist = empty vector (embedded) -- set up when first used.
+ * hp = 0 -- hp == h_now => in the present ...
+ * h_now = 0 -- ... see uty_cli_hist_add()
+ *
+ * width = 0 -- unknown width ) Telnet window size
+ * height = 0 -- unknown height )
+ *
+ * lines = 0 -- set below
+ * lines_set = false -- set below
+ *
+ * monitor = false -- not a "monitor"
+ * monitor_busy = false -- so not busy either
+ *
+ * v_timeout = ???
+ *
+ * key_stream = X -- set below
+ *
+ * drawn = false
+ * dirty = false
+ *
+ * prompt_len = 0 -- not drawn, in any case
+ * extra_len = 0 -- not drawn, in any case
+ *
+ * echo_suppress = false
+ *
+ * prompt_node = NULL_NODE -- so not set !
+ * prompt_gen = 0 -- not a generation number
+ * prompt_for_node = empty qstring (embedded)
+ *
+ * password_fail = 0 -- so far, so good.
+ *
+ * blocked = false -- see below
+ * in_progress = false -- see below
+ * out_active = false
+ *
+ * more_wait = false
+ * more_active = false
+ *
+ * out_done = false -- not that it matters
+ *
+ * more_enabled = false -- not in "--More--" state
+ *
+ * node = NULL_NODE -- set in vty_cli_start()
+ * to_do = cmd_do_nothing
+ *
+ * cl = NULL qstring -- set below
+ * clx = NULL qstring -- set below
+ *
+ * parsed = all zeros -- empty parsed object
+ * cbuf = empty fifo (embedded)
+ *
+ * olc = empty line control (embedded)
+ */
+ confirm(VECTOR_INIT_ALL_ZEROS) ; /* hist */
+ confirm(QSTRING_INIT_ALL_ZEROS) ; /* prompt_for_node */
+ confirm(NULL_NODE == 0) ; /* prompt_node & node */
+ confirm(cmd_do_nothing == 0) ; /* to_do */
+ confirm(VIO_FIFO_INIT_ALL_ZEROS) ; /* cbuf */
+ confirm(VIO_LINE_CONTROL_INIT_ALL_ZEROS) ; /* olc */
+ confirm(CMD_PARSED_INIT_ALL_ZEROS) ; /* parsed */
+
+ cli->vf = vf ;
+
+ /* Allocate and initialise a keystroke stream TODO: CSI ?? */
+ cli->key_stream = keystroke_stream_new('\0', uty_cli_iac_callback, cli) ;
-static char* vty_host_name = NULL ;
-int vty_host_name_set = 0 ;
+ /* Set up cl and clx qstrings and the command line output fifo */
+ cli->cl = qs_init_new(NULL, 120) ; /* reasonable line length */
+ cli->clx = qs_init_new(NULL, 120) ;
-static void uty_new_host_name(const char* name) ;
+ vio_fifo_init_new(cli->cbuf, 500) ; /* just for the CLI stuff */
+
+ /* Use global 'lines' setting, as default -- but 'lines_set' false. */
+ uty_cli_set_lines(cli, host.lines, false) ;
+ uty_cli_update_more(cli) ; /* and update the olc etc to suit. */
+
+ /* Ready to be started -- out_active & more_wait are false. */
+ cli->blocked = true ;
+ cli->in_progress = true ;
+
+ return cli ;
+} ;
/*------------------------------------------------------------------------------
- * Update vty_host_name as per "hostname" or "no hostname" command
+ * Start the CLI.
+ *
+ * The implied start "command" is complete and everything is ready to start
+ * the CLI.
+ *
+ * Note that before releasing any pending output, calls
+ *
+ * Returns: write_ready -- so the first event is a write event, to flush
+ * any output to date.
*/
extern void
-uty_set_host_name(const char* name)
+uty_cli_start(vty_cli cli, node_type_t node)
+{
+ uty_cli_done_command(cli, node) ; /* implied push output */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Close CLI object, if any -- may be called more than once.
+ *
+ * Shuts down and discards anything to do with the input.
+ *
+ * Revokes anything that can be revoked, which may allow output to proceed
+ * and the vty to clear itself down.
+ *
+ * If in_progress and not final, keeps the cbuf, the olc and clx if in_progress.
+ * If not final, keeps the cbuf and the olc.
+ *
+ * Destroys self if final.
+ *
+ * Expects other code to:
+ *
+ * - close down command loop and empty the vin/vout stacks.
+ *
+ * - turn of any monitor status.
+ *
+ * - half close the telnet connection.
+ */
+extern vty_cli
+uty_cli_close(vty_cli cli, bool final)
{
+ if (cli == NULL)
+ return NULL ;
+
VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
- vty_host_name_set = (name != NULL) ;
+ /* Revoke any command that is in flight. */
+ cq_revoke(cli->vf->vio->vty) ;
- if (vty_host_name_set)
- uty_new_host_name(name) ;
- else
- uty_check_host_name() ;
+ /* Bring as much of the command handling to a stop as possible, and
+ * turn off "---more--" etc. so that output can proceed.
+ */
+ cli->more_enabled = false ;
+ cli->lines = 0 ; /* so will not be re-enabled */
+
+ uty_cli_wipe(cli, 0) ; /* wipe anything the CLI has on screen */
+
+ cli->more_wait = false ; /* exit more_wait (if was in it) */
+ cli->more_active = false ; /* and so cannot be this */
+
+ cli->out_active = true ; /* if there is any output, it can go */
+
+ /* Ream out the history. */
+ {
+ qstring line ;
+ while ((line = vector_ream(cli->hist, keep_it)) != NULL)
+ qs_reset(line, free_it) ;
+ } ;
+
+ /* Empty the keystroke handling. */
+ cli->key_stream = keystroke_stream_free(cli->key_stream) ;
+
+ /* Can discard active command line if not in_progress. */
+ if (!cli->in_progress || final)
+ cli->clx = qs_reset(cli->clx, free_it) ;
+
+ /* Can discard parsed object. */
+ cmd_parsed_reset(cli->parsed, keep_it) ;
+
+ /* If final, free the CLI object. */
+ if (final)
+ {
+ qs_reset(cli->prompt_for_node, keep_it) ;
+ cli->cl = qs_reset(cli->cl, free_it) ;
+
+ vio_fifo_reset(cli->cbuf, keep_it) ; /* embedded fifo */
+ vio_lc_reset(cli->olc, keep_it) ; /* embedded line control */
+
+ XFREE(MTYPE_VTY_CLI, cli) ; /* sets cli = NULL */
+ } ;
+
+ return cli ;
} ;
/*------------------------------------------------------------------------------
- * If vty_host_name is set, free it.
+ * The keystroke iac callback function.
+ *
+ * This deals with IAC sequences that should be dealt with as soon as they
+ * are read -- not stored in the keystroke stream for later processing.
*/
-extern void
-uty_free_host_name(void)
+static bool
+uty_cli_iac_callback(keystroke_iac_callback_args)
{
- if (vty_host_name != NULL)
- XFREE (MTYPE_HOST, vty_host_name) ; /* sets vty_host_name = NULL */
+ return uty_telnet_command(((vty_cli)context)->vf, stroke, true) ;
} ;
/*------------------------------------------------------------------------------
- * If the host name is not set by command, see if the actual host name has
- * changed, and if so change it.
+ * Set the cli->lines, explicitly or implicitly, and update the "--more--"
+ * state to suit.
*
- * This is done periodically in case the actual host name changes !
+ * lines < 0 => use whatever telnet has said, or no "--more--"
+ * lines = 0 => no "--more--"
+ * lines > 0 => use
*/
extern void
-uty_check_host_name(void)
+uty_cli_set_lines(vty_cli cli, int lines, bool explicit)
{
- struct utsname names ;
-
- VTY_ASSERT_LOCKED() ;
+ cli->lines = lines ;
+ cli->lines_set = explicit ;
- if (vty_host_name_set)
- return ; /* nothing to do if set by command */
+ uty_cli_update_more(cli) ;
+} ;
- uname (&names) ;
+/*------------------------------------------------------------------------------
+ * Set the cli->lines, explicitly or implicitly, and update the "--more--"
+ * state to suit.
+ *
+ * lines < 0 => use whatever telnet has said, or no "--more--"
+ * lines = 0 => no "--more--"
+ * lines > 0 =>
+ */
+extern void
+uty_cli_set_window(vty_cli cli, int width, int height)
+{
+ cli->width = width ;
+ cli->height = height ;
- if ((vty_host_name == NULL) || (strcmp(vty_host_name, names.nodename) != 0))
- uty_new_host_name(names.nodename) ;
+ uty_cli_update_more(cli) ;
} ;
/*------------------------------------------------------------------------------
- * Set new vty_host_name and run along list of VTYs to mark the change.
+ * Update the "--more--" state, after changing one or more of:
+ *
+ * cli->lines
+ * cli->lines_set
+ * cli->width
+ * cli->height
+ *
+ * Set the effective height for line control, if required, which may enable the
+ * "--more--" output handling.
+ *
+ * If not, want some limit on the amount of stuff output at a time.
+ *
+ * Sets the line control window width and height.
+ * Sets cli_more_enabled if "--more--" is enabled.
*/
static void
-uty_new_host_name(const char* name)
+uty_cli_update_more(vty_cli cli)
{
- vty_io vio ;
-
- VTY_ASSERT_LOCKED() ;
+ bool on ;
- uty_free_host_name() ;
- vty_host_name = XSTRDUP(MTYPE_HOST, name) ;
+ on = false ; /* default state */
- vio = vio_list_base ;
- while (vio != NULL)
+ if (cli->vf->vout_state == vf_open)
{
- vio->cli_prompt_set = 0 ;
- vio = sdl_next(vio, vio_list) ;
+ int height ;
+
+ height = 0 ; /* default state: no "--more--" */
+
+ if ((cli->width) != 0)
+ {
+ /* If window size is known, use lines or given height */
+ if (cli->lines >= 0)
+ height = cli->lines ;
+ else
+ {
+ /* Window height, leaving one line from previous "page"
+ * and one line for the "--more--" -- if at all possible
+ */
+ height = cli->height - 2 ;
+ if (height < 1)
+ height = 1 ;
+ } ;
+ }
+ else
+ {
+ /* If window size not known, use lines if that has been set
+ * explicitly for this terminal.
+ */
+ if (cli->lines_set)
+ height = cli->lines ;
+ } ;
+
+ if (height > 0)
+ on = true ; /* have a defined height */
+ else
+ height = 200 ; /* but no "--more--" */
+
+ vio_lc_set_window(cli->olc, cli->width, height) ;
} ;
+
+ cli->more_enabled = on ;
} ;
/*==============================================================================
@@ -132,31 +372,49 @@ uty_new_host_name(const char* name)
* -- setting read or write on, means enabling the file for select/pselect to
* consider it for read_ready or write_ready, respectively.
*
+ * All command actions are dispatched via the command queue -- commands are
+ * not executed in the cli. [For legacy threads the event queue is used. If
+ * that is too slow, a priority event queue will have to be invented.]
+ *
* State of the CLI:
*
- * cli_blocked -- the CLI is unable to process any further keystrokes.
+ * in_progress -- a command has been dispatched and has not yet completed.
+ *
+ * The command may be a in_pipe, so there may be many commands
+ * to be completed before the CLI level command is.
+ *
+ * or: the CLI has been closed.
+ *
+ * blocked -- is in_progress and a further command is now ready to be
+ * dispatched.
+ *
+ * or: the CLI has been closed.
+ *
+ * out_active -- the command output FIFO is being emptied.
*
- * cmd_in_progress -- a command has been dispatched and has not yet
- * completed (may have been queued).
+ * This is set when a command completes, and cleared when
+ * everything is written away.
*
- * cmd_out_enabled -- the command FIFO is may be emptied.
+ * Note that in this context a command is an individual
+ * command -- where in_progress may cover any number of
+ * command if the CLI level command is an in_pipe.
*
- * This is set when a command completes, and cleared when
- * everything is written away.
+ * more_wait -- is in "--more--" wait state
*
- * cli_more_wait -- is in "--more--" wait state
+ * more_active -- is in "--more--" wait state, and the "--more--" prompt
+ * has not been written away, yet.
*
* The following are the valid combinations:
*
- * blkd : cip : o_en : m_wt :
- * -----:------:------:------:--------------------------------------------
- * 0 : 0 : 0 : 0 : collecting a new command
- * 0 : 1 : 0 : 0 : command dispatched
- * 1 : 1 : 0 : 0 : waiting for (queued) command to complete
- * 1 : 0 : 1 : 0 : waiting for command output to complete
- * 1 : 0 : 0 : 1 : waiting for "--more--" to be written away
- * 0 : 0 : 0 : 1 : waiting for "--more--" response
- * 1 : 1 : 1 : 0 : waiting for command to complete, after the
+ * in_p: blkd: o_ac: m_wt: m_ac:
+ * ----:-----:-----:-----:-----:-----------------------------------------
+ * 0 : 0 : 0 : 0 : 0 : collecting a new command
+ * 0 : 0 : 1 : 0 : 0 : waiting for command output to finish
+ * 1 : 0 : X : 0 : 0 : command dispatched
+ * 1 : 1 : X : 0 : 0 : waiting for command to complete
+ * X : X : 0 : 1 : 1 : waiting for "--more--" to be written away
+ * X : X : 0 : 1 : 0 : waiting for "--more--" response
+ * 1 : 1 : 1 : 0 : 0 : waiting for command to complete, after the
* CLI has been closed
*
* There are two output FIFOs:
@@ -166,9 +424,9 @@ uty_new_host_name(const char* name)
* 2. for output generated by commands -- vty_out and friends.
*
* The CLI FIFO is emptied whenever possible, in preference to the command
- * FIFO. The command FIFO is emptied when cmd_out_enabled. While
- * cmd_in_progress is also !cmd_out_enabled -- so that all the output from a
- * given command is collected together before being sent to the file.
+ * FIFO. The command FIFO is emptied when out_active. While a command is
+ * in_progress all its output is collected in the command output buffer,
+ * to be written away when the command completes.
*
* Note that only sets read on when the keystroke stream is empty and has not
* yet hit eof. The CLI process is driven mostly by write_ready -- which
@@ -188,32 +446,20 @@ uty_new_host_name(const char* name)
*
* This is largely buried in the output handling.
*
- * While cmd_in_progress is true cmd_out_enabled will be false. When the
- * command completes:
- *
- * * cmd_in_progress is cleared
- *
- * * cmd_out_enabled is set
- *
- * * cli_blocked will be set
- *
- * * the line_control structure is reset
- *
- * * the output process is kicked off by setting write on
+ * When a command completes and its output is pushed to written away,
+ * out_active will be set (and any command line will be wiped). The output
+ * process is then kicked.
*
* The output process used the line_control structure to manage the output, and
* occasionally enter the trivial "--more--" CLI. This is invisible to the
- * main CLI. (See the cli_more_wait flag and its handling.)
+ * main CLI. (See the more_wait and more_active flags and their handling.)
*
- * When all the output has completed the CLI will be kicked, which will see
- * that the output buffer is now empty, and it can proceed.
+ * When all the output has completed the out_active flag is cleared and the CLI
+ * will be kicked.
*
* It is expected that the output will end with a newline -- so that when the
* CLI is kicked, the cursor will be at the start of an empty line.
*
- * This mechanism means that the command output FIFO only ever contains the
- * output from the last command executed.
- *
* If the user decides to abandon output at the "--more--" prompt, then the
* contents of the command output FIFO are discarded.
*
@@ -265,102 +511,15 @@ uty_new_host_name(const char* name)
#define CONTROL(X) ((X) & 0x1F)
-static void uty_cli_cmd_complete(vty_io vio, enum cmd_return_code ret) ;
-static enum vty_readiness uty_cli_standard(vty_io vio) ;
-static enum vty_readiness uty_cli_more_wait(vty_io vio) ;
-static void uty_cli_draw(vty_io vio) ;
-static void uty_cli_draw_this(vty_io vio, enum node_type node) ;
-static void uty_cli_wipe(vty_io vio, int len) ;
-
-static void uty_will_echo (vty_io vio) ;
-static void uty_will_suppress_go_ahead (vty_io vio) ;
-static void uty_dont_linemode (vty_io vio) ;
-static void uty_do_window_size (vty_io vio) ;
-static void uty_dont_lflow_ahead (vty_io vio) ;
-
-/*------------------------------------------------------------------------------
- * Initialise CLI.
- *
- * It is assumed that the following have been initialised, empty or zero:
- *
- * cli_prompt_for_node
- * cl
- * clx
- * cli_vbuf
- * cli_obuf
- *
- * cli_drawn
- * cli_dirty
- *
- * cli_prompt_set
- *
- * cli_blocked
- * cmd_in_progress
- * cmd_out_enabled
- * cli_wait_more
- *
- * cli_more_enabled
- *
- * Sets the CLI such that there is apparently a command in progress, so that
- * further initialisation (in particular hello messages and the like) is
- * treated as a "start up command".
- *
- * Sends a suitable set of Telnet commands to start the process.
- */
-extern void
-uty_cli_init(vty_io vio)
-{
- assert(vio->vty->type == VTY_TERMINAL) ;
-
- vio->cmd_in_progress = 1 ;
- vio->cli_blocked = 1 ;
-
- vio->cli_do = cli_do_nothing ;
-
- /* Setting up terminal. */
- uty_will_echo (vio);
- uty_will_suppress_go_ahead (vio);
- uty_dont_linemode (vio);
- uty_do_window_size (vio);
- if (0)
- uty_dont_lflow_ahead (vio) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Start the CLI.
- *
- * All start-up operations are complete -- so the "command" is now complete.
- *
- * Returns: write_ready -- so the first event is a write event, to flush
- * any output to date.
- */
-extern enum vty_readiness
-uty_cli_start(vty_io vio)
-{
- uty_cli_cmd_complete(vio, CMD_SUCCESS) ;
- return write_ready ;
-} ;
+static enum vty_readiness uty_cli_standard(vty_cli cli) ;
+static enum vty_readiness uty_cli_more_wait(vty_cli cli) ;
+static void uty_cli_draw(vty_cli cli) ;
+static void uty_cli_draw_this(vty_cli cli, node_type_t node) ;
/*------------------------------------------------------------------------------
- * Close the CLI
+ * CLI for VTY_TERMINAL
*
- * Note that if any command is revoked, then will clear cmd_in_progress and
- * set cmd_out_enabled -- so any output can now clear.
- */
-extern void
-uty_cli_close(vty_io vio)
-{
- VTY_ASSERT_LOCKED() ;
- assert(vio->vty->type == VTY_TERMINAL) ;
-
- cq_revoke(vio->vty) ;
-
- vio->cli_blocked = 1 ; /* don't attempt any more */
- vio->cmd_out_enabled = 1 ; /* allow output to clear */
-} ;
-
-/*------------------------------------------------------------------------------
- * CLI for VTY_TERM
+ * Called by uty_term_ready(), so driven by read/write ready.
*
* Do nothing at all if half closed.
*
@@ -370,29 +529,33 @@ uty_cli_close(vty_io vio)
* NB: on return, requires that an attempt is made to write away anything that
* may be ready for that.
*/
-extern enum vty_readiness
-uty_cli(vty_io vio)
+extern vty_readiness_t
+uty_cli(vty_cli cli)
{
VTY_ASSERT_LOCKED() ;
- assert(vio->vty_type == VTY_TERM) ;
- if (vio->half_closed)
- return not_ready ; /* Nothing more if half closed */
+ if (cli->vf->vin_state != vf_open)
+ return not_ready ; /* Nothing more from CLI if closing */
/* Standard or "--more--" CLI ? */
- if (vio->cli_more_wait)
- return uty_cli_more_wait(vio) ;
+ if (cli->more_wait)
+ return uty_cli_more_wait(cli) ;
else
- return uty_cli_standard(vio) ;
+ return uty_cli_standard(cli) ;
} ;
/*==============================================================================
* The Standard CLI
*/
-static enum cli_do uty_cli_process(vty_io vio, enum node_type node) ;
-static void uty_cli_response(vty_io vio, enum cli_do cli_do) ;
-static bool uty_cli_dispatch(vty_io vio) ;
+static cmd_do_t uty_cli_process(vty_cli cli, node_type_t node) ;
+static void uty_cli_response(vty_cli cli, cmd_do_t cmd_do) ;
+
+
+static void uty_cli_dispatch(vty_cli cli) ;
+static cmd_do_t uty_cli_command(vty_cli cli) ;
+static void uty_cli_hist_add (vty_cli cli, qstring clx) ;
+
/*------------------------------------------------------------------------------
* Standard CLI for VTY_TERM -- if not blocked, runs until:
@@ -408,34 +571,21 @@ static bool uty_cli_dispatch(vty_io vio) ;
* write_ready if there is anything in the keystroke stream
* read_ready otherwise
*/
-static enum vty_readiness
-uty_cli_standard(vty_io vio)
+static vty_readiness_t
+uty_cli_standard(vty_cli cli)
{
VTY_ASSERT_LOCKED() ;
- assert(vio->vty_type == VTY_TERM) ;
- /* cli_blocked is set when is waiting for a command, or its output to
- * complete -- unless either of those has happened, is still blocked.
+ assert(!cli->more_wait) ; /* cannot be here in more_wait state ! */
+
+ /* cli_blocked is set when is waiting for a command, or some output to
+ * complete.
*
* NB: in both these cases, assumes that other forces are at work to
* keep things moving.
*/
- if (vio->cli_blocked)
- {
- assert(vio->cmd_in_progress || vio->cmd_out_enabled) ;
-
- if (vio->cmd_in_progress)
- {
- assert(!vio->cmd_out_enabled) ;
- return not_ready ;
- } ;
-
- if (!vio_fifo_empty(&vio->cmd_obuf))
- return not_ready ;
-
- vio->cli_blocked = 0 ;
- vio->cmd_out_enabled = 0 ;
- } ;
+ if (cli->blocked || cli->out_active)
+ return not_ready ;
/* If there is nothing pending, then can run the CLI until there is
* something to do, or runs out of input.
@@ -444,26 +594,26 @@ uty_cli_standard(vty_io vio)
* now completed, which may have wiped the pending command or changed
* the required prompt.
*/
- if (vio->cli_do == cli_do_nothing)
- vio->cli_do = uty_cli_process(vio, vio->vty->node) ;
+ if (cli->to_do == cmd_do_nothing)
+ cli->to_do = uty_cli_process(cli, cli->node) ;
else
- uty_cli_draw_this(vio, vio->vty->node) ;
+ uty_cli_draw_this(cli, cli->node) ;
- /* If have something to do, do it. */
- if (vio->cli_do != cli_do_nothing)
+ /* If have something to do, do it if we can. */
+ if (cli->to_do != cmd_do_nothing)
{
- /* Reflect immediate response */
- uty_cli_response(vio, vio->cli_do) ;
+ /* Reflect immediate response */
+ uty_cli_response(cli, cli->to_do) ;
/* If command not already in progress, dispatch this one, which may
* set the CLI blocked.
*
* Otherwise is now blocked until queued command completes.
*/
- if (!vio->cmd_in_progress)
- vio->cli_blocked = uty_cli_dispatch(vio) ;
+ if (cli->in_progress)
+ cli->blocked = true ; /* blocked waiting for previous */
else
- vio->cli_blocked = 1 ;
+ uty_cli_dispatch(cli) ; /* can dispatch the latest */
} ;
/* Use write_ready as a proxy for read_ready on the keystroke stream.
@@ -475,166 +625,164 @@ uty_cli_standard(vty_io vio)
* defensive: at worst will generate one unnecessary read_ready/write_ready
* event.
*/
- if (keystroke_stream_empty(vio->key_stream))
+ if (keystroke_stream_empty(cli->key_stream))
return read_ready ;
else
return write_ready ;
} ;
/*------------------------------------------------------------------------------
- * Dispatch the current vio->cli_do -- queueing it if necessary.
+ * Dispatch the current cli->to_do -- queueing it if necessary.
*
* Requires that are NOT blocked and NO command is queued.
*
* Expects to be on new blank line, and when returns will be on new, blank
* line.
*
- * Returns: true <=> command completed and output is pending
- * false => command has been queued and is now in progress
+ * Generally sets cli->to_do = cmd_do_nothing and clears cli->cl to empty.
*
- * Generally sets vio->cl_do = cli_do_nothing and clears vio->cl to empty.
- *
- * Can set vio->cl_do = and vio->cl to be a follow-on command.
+ * Can set cli->cl_do = and cli->cl to be a follow-on command.
*/
-static bool
-uty_cli_dispatch(vty_io vio)
+static void
+uty_cli_dispatch(vty_cli cli)
{
- qstring_t tmp ;
- enum cli_do cli_do ;
- enum cmd_return_code ret ;
+ qstring tmp ;
+ cmd_do_t to_do_now ;
- struct vty* vty = vio->vty ;
+ vty_io vio = cli->vf->vio ;
VTY_ASSERT_LOCKED() ;
- assert(!vio->cli_blocked && !vio->cmd_in_progress) ;
- /* Set vio->clx to the command about to execute.
+ /* About to dispatch a command, so must be in the following state. */
+ assert(!cli->in_progress && !cli->out_active && !cli->blocked) ;
+ assert(cli->node == vio->vty->node) ;
+
+ /* Set cli->clx to the command about to execute & pick up cli->to_do.
*
- * Clear vio->cl and vio->cl_do.
+ * Clear cli->cl and cli->to_do.
*/
- vio->cmd_in_progress = 1 ; /* => vty->buf is valid */
- vio->cmd_out_enabled = 0 ; /* => collect all output */
-
- tmp = vio->clx ; /* swap clx and cl */
- vio->clx = vio->cl ;
- vio->cl = tmp ;
+ tmp = cli->clx ; /* swap clx and cl */
+ cli->clx = cli->cl ;
+ cli->cl = tmp ;
- qs_term(&vio->clx) ; /* ensure string is terminated */
- vty->buf = qs_chars(&vio->clx) ; /* terminated command line */
- cli_do = vio->cli_do ; /* current operation */
+ to_do_now = cli->to_do ; /* current operation */
- vio->cli_do = cli_do_nothing ; /* clear */
- qs_clear(&vio->cl) ; /* set cl empty (with '\0') */
+ cli->to_do = cmd_do_nothing ; /* clear */
+ qs_clear(cli->cl) ; /* set cl empty */
- /* Reset the command output FIFO and line_control */
- assert(vio_fifo_empty(&vio->cmd_obuf)) ;
- uty_out_clear(vio) ; /* clears FIFO and line control */
+ /* Reset the command output FIFO and line_control TODO */
+//uty_out_clear(cli->vio) ; /* clears FIFO and line control */
/* Dispatch command */
- if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
- {
- /* AUTH_NODE and AUTH_ENABLE_NODE are unique */
- ret = uty_auth(vty, vty->buf, cli_do) ;
- }
+ if ((cli->node == AUTH_NODE) && (to_do_now != cmd_do_nothing))
+ to_do_now |= cmd_do_auth ;
+ else if ((cli->node == AUTH_ENABLE_NODE) && (to_do_now != cmd_do_nothing))
+ to_do_now |= cmd_do_auth_enable ;
else
{
/* All other nodes... */
- switch (cli_do)
+ switch (to_do_now)
{
- case cli_do_nothing:
- ret = CMD_SUCCESS ;
+ case cmd_do_nothing:
+ case cmd_do_ctrl_c:
+ case cmd_do_eof:
break ;
- case cli_do_command:
- ret = uty_command(vty) ;
+ case cmd_do_command:
+ to_do_now = uty_cli_command(cli) ;
break ;
- case cli_do_ctrl_c:
- ret = uty_stop_input(vty) ;
+ case cmd_do_ctrl_d:
+ zabort("invalid cmd_do_ctrl_d") ;
break ;
- case cli_do_ctrl_d:
- ret = uty_down_level(vty) ;
- break ;
+ case cmd_do_ctrl_z:
+ to_do_now = uty_cli_command(cli) ;
- case cli_do_ctrl_z:
- ret = uty_command(vty) ;
- if (ret == CMD_QUEUED)
- vio->cli_do = cli_do_ctrl_z ; /* defer the ^Z action */
+ if (to_do_now == cmd_do_nothing)
+ to_do_now = cmd_do_ctrl_z ;
else
- ret = uty_end_config(vty) ; /* do the ^Z now */
- break ;
+ cli->to_do = cmd_do_ctrl_z ; /* defer the ^Z */
- case cli_do_eof:
- ret = uty_cmd_close(vio->vty, "End") ;
break ;
default:
- zabort("unknown cli_do_xxx value") ;
+ zabort("unknown cmd_do_xxx value") ;
} ;
} ;
- if (ret == CMD_QUEUED)
+ cli->hp = cli->h_now ; /* in any event, back to the present */
+
+ if (to_do_now != cmd_do_nothing)
{
- uty_cli_draw(vio) ; /* draw the prompt */
- return false ; /* command not complete */
+ cli->in_progress = true ;
+ uty_cmd_dispatch(vio, to_do_now, cli->clx) ;
}
else
- {
- uty_cli_cmd_complete(vio, ret) ;
- return true ; /* command complete */
- } ;
+ uty_cli_draw(cli) ;
} ;
/*------------------------------------------------------------------------------
- * Queued command has completed.
+ * Enqueue command -- adding to history -- if is not empty or just comment
*
- * Note that sets write on whether there is anything in the output buffer
- * or not... write_ready will kick read_ready.
+ * This is for VTY_TERM type VTY.
+ *
+ * Returns: CMD_SUCCESS => empty command line (or just comment)
+ * CMD_WAITING => enqueued for parse and execute
*/
-extern void
-vty_queued_result(struct vty *vty, enum cmd_return_code ret)
+static cmd_do_t
+uty_cli_command(vty_cli cli)
{
- vty_io vio ;
+ VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
- VTY_LOCK() ;
+ if (cmd_is_empty(qs_els_nn(cli->clx)))
+ return cmd_do_nothing ; /* easy when nothing to do ! */
- vio = vty->tos ;
+ /* Add not empty command to history */
+ uty_cli_hist_add(cli, cli->clx) ;
- if (!vio->closed)
- {
- uty_cli_wipe(vio, 0) ; /* wipe any partly constructed line */
+ return cmd_do_command ;
+} ;
- /* Do the command completion actions that were deferred because the
- * command was queued.
- *
- * Return of CMD_QUEUED => command was revoked before being executed.
- * However interesting that might be... frankly don't care.
- */
- uty_cli_cmd_complete(vio, ret) ;
+/*------------------------------------------------------------------------------
+ * A command has completed, and there is (or may be) some output to now
+ * write away.
+ */
+extern void
+uty_cli_out_push(vty_cli cli)
+{
+ VTY_ASSERT_LOCKED() ;
- /* Kick the socket -- to write away any outstanding output, and
- * re-enter the CLI when that's done.
- */
- uty_sock_set_readiness(&vio->sock, write_ready) ;
- }
- else
+ if (cli->more_wait)
+ return ; /* can do nothing */
+
+ if (vio_fifo_empty(cli->vf->obuf))
{
- /* If the VTY is closed, the only reason it still exists is because
- * there was cmd_in_progress.
- */
- vio->cmd_in_progress = 0 ; /* death watch will apply coup de grace */
+ assert(!cli->out_active ) ;
+ return ; /* need do nothing if is empty */
} ;
- VTY_UNLOCK() ;
-}
+ uty_cli_wipe(cli, 0) ; /* wipe any partly constructed line */
+
+ cli->out_active = true ; /* enable the output */
+
+ uty_term_set_readiness(cli->vf, write_ready) ;
+ /* kick the write side into action */
+} ;
/*------------------------------------------------------------------------------
+ * Queued command has completed.
+ *
+ * Note that sets write on whether there is anything in the output buffer
+ * or not... write_ready will kick read_ready.
+
+
* Command has completed, so:
*
* * clear cmd_in_progress
- * * set cmd_out_enabled -- so any output can now proceed
- * * set cli_blocked -- waiting for output to complete
+ * * set cmd_out_active -- so any output can now proceed
+ * * set cli_blocked -- waiting for output to complete
* * and prepare the line control for output
*
* If the return is CMD_CLOSE, then also now does the required half close.
@@ -650,33 +798,153 @@ vty_queued_result(struct vty *vty, enum cmd_return_code ret)
*
* It also means that the decision about whether there is anything to output
* is left to the output code.
+
+
+
*/
-static void
-uty_cli_cmd_complete(vty_io vio, enum cmd_return_code ret)
+extern void
+uty_cli_done_command(vty_cli cli, node_type_t node)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ uty_cli_out_push(cli) ; /* just in case */
+
+ cli->in_progress = false ; /* command complete */
+ cli->blocked = false ; /* releases possibly blocked command */
+ cli->node = node ; /* and now in this node */
+
+ /* Reach all the way back, and get the now current node. */
+ cli->node = cli->vf->vio->vty->node ;
+
+ uty_term_set_readiness(cli->vf, write_ready) ;
+ /* kick the write side into action */
+} ;
+
+/*==============================================================================
+ * All commands are dispatched to command_queue.
+ *
+ * Some commands are VLI specific... and end up back here.
+ */
+
+/*------------------------------------------------------------------------------
+ * Authentication of vty
+ *
+ * Note that if the AUTH_NODE password fails too many times, the terminal is
+ * closed.
+ *
+ * Returns: CMD_SUCCESS -- OK, one way or another
+ * CMD_WARNING -- with error message sent to output
+ * CMD_CLOSE -- too many password failures
+ */
+extern cmd_return_code_t
+uty_cli_auth(vty_cli cli)
{
+ char *crypt (const char *, const char *);
+
+ char* passwd = NULL ;
+ bool encrypted = false ;
+ node_type_t next_node = 0 ;
+ cmd_return_code_t ret ;
+ vty_io vio ;
+ cmd_exec exec ;
+
VTY_ASSERT_LOCKED() ;
- assert(vio->cmd_in_progress && !vio->cmd_out_enabled) ;
+ vio = cli->vf->vio ;
+ exec = vio->vty->exec ;
+
+ /* Deal with the exotic terminators. */
+ switch (exec->to_do & cmd_do_mask)
+ {
+ case cmd_do_nothing:
+ case cmd_do_ctrl_c:
+ case cmd_do_ctrl_z:
+ return CMD_SUCCESS ;
+
+ case cmd_do_command:
+ break ;
+
+ case cmd_do_ctrl_d:
+ case cmd_do_eof:
+ return uty_cmd_close(vio, "End") ;
+
+ default:
+ zabort("unknown or invalid cmd_do") ;
+ } ;
+
+ /* Ordinary command dispatch -- see if password is OK.
+ *
+ * Select the password we need to check against.
+ */
+ if ((exec->to_do & ~cmd_do_mask) == cmd_do_auth)
+ {
+ passwd = host.password ;
+ encrypted = host.password_encrypted ;
+
+ if (host.advanced)
+ next_node = host.enable ? VIEW_NODE : ENABLE_NODE;
+ else
+ next_node = VIEW_NODE;
+ }
+ else if ((exec->to_do & ~cmd_do_mask) == cmd_do_auth_enable)
+ {
+ passwd = host.enable ;
+ encrypted = host.enable_encrypted ;
+
+ next_node = ENABLE_NODE;
+ }
+ else
+ zabort("unknown to_do_value") ;
+
+ /* Check against selected password (if any) */
+ if (passwd != NULL)
+ {
+ char* candidate = qs_make_string(exec->line) ;
+
+ if (encrypted)
+ candidate = crypt(candidate, passwd) ;
+
+ if (strcmp(candidate, passwd) == 0)
+ {
+ cli->password_fail = 0 ; /* forgive any recent failures */
+ vio->vty->node = next_node;
+
+ return CMD_SUCCESS ; /* <<< SUCCESS <<<<<<<< */
+ } ;
+ } ;
- if (ret == CMD_CLOSE)
- uty_close(vio, NULL) ;
+ /* Password failed -- or none set ! */
+ cli->password_fail++ ;
- vio->cmd_in_progress = 0 ; /* command complete */
- vio->cmd_out_enabled = 1 ; /* enable the output */
- vio->cli_blocked = 1 ; /* now blocked waiting for output */
+ ret = CMD_SUCCESS ; /* OK so far */
- vio->vty->buf = NULL ; /* finished with command line */
+ if (cli->password_fail >= 3)
+ {
+ if ((exec->to_do & ~cmd_do_mask) == cmd_do_auth)
+ {
+ ret = uty_cmd_close(vio, "Bad passwords, too many failures!") ;
+ }
+ else
+ {
+ /* AUTH_ENABLE_NODE */
+ cli->password_fail = 0 ; /* allow further attempts */
+ uty_out(vio, "%% Bad enable passwords, too many failures!\n") ;
+ vio->vty->node = host.restricted_mode ? RESTRICTED_NODE
+ : VIEW_NODE ;
+ ret = CMD_WARNING ;
+ } ;
+ } ;
- uty_cmd_output_start(vio) ; /* reset line control (belt & braces) */
+ return ret ;
} ;
/*==============================================================================
* The "--more--" CLI
*
- * While command output is being cleared from its FIFO, the CLI is cli_blocked.
+ * While command output is being cleared from its FIFO, the CLI is blocked.
*
* When the output side signals that "--more--" is required, it sets the
- * cli_more_wait flag and clears the cmd_out_enabled flag.
+ * more_wait flag and clears the out_active flag.
*
* The first stage of handling "--more--" is to suck the input dry, so that
* (as far as is reasonably possible) does not steal a keystroke as the
@@ -691,19 +959,20 @@ uty_cli_cmd_complete(vty_io vio, enum cmd_return_code ret)
* Outputs the new prompt line.
*/
extern void
-uty_cli_enter_more_wait(vty_io vio)
+uty_cli_enter_more_wait(vty_cli cli)
{
VTY_ASSERT_LOCKED() ;
- assert(vio->cli_blocked && vio->cmd_out_enabled && !vio->cli_more_wait) ;
+ assert(cli->out_active && !cli->more_wait) ;
- uty_cli_wipe(vio, 0) ; /* make absolutely sure that command line is
+ uty_cli_wipe(cli, 0) ; /* make absolutely sure that command line is
wiped before change the CLI state */
- vio->cmd_out_enabled = 0 ; /* stop output pro tem */
- vio->cli_more_wait = 1 ; /* new state */
+ cli->out_active = false ; /* stop output pro tem */
+ cli->more_wait = true ; /* new state */
+ cli->more_active = true ; /* drawing the "--more--" */
- uty_cli_draw(vio) ; /* draw the "--more--" */
+ uty_cli_draw(cli) ; /* draw the "--more--" */
} ;
/*------------------------------------------------------------------------------
@@ -717,27 +986,26 @@ uty_cli_enter_more_wait(vty_io vio)
* possible that the '--more--' prompt is yet to be completely written away,
* so:
*
- * * assert that is either: !vio->cli_blocked (most of the time it will)
- * or: !vio_fifo_empty(&vio->cli_obuf)
+ * * assert that is either: !cli->blocked (most of the time it will)
+ * or: !vio_fifo_empty(cli->cbuf)
*
* * note that can wipe the prompt even though it hasn't been fully
* written away yet. (The effect is to append the wipe action to the
* cli_obuf !)
*/
extern void
-uty_cli_exit_more_wait(vty_io vio)
+uty_cli_exit_more_wait(vty_cli cli)
{
VTY_ASSERT_LOCKED() ;
- assert( (!vio->cli_blocked || !vio_fifo_empty(&vio->cli_obuf))
- && !vio->cmd_out_enabled && vio->cli_more_wait) ;
+ assert(cli->more_wait) ;
- uty_cli_wipe(vio, 0) ; /* wipe the prompt ('--more--')
+ uty_cli_wipe(cli, 0) ; /* wipe the prompt ('--more--')
before changing the CLI state */
- vio->cli_blocked = 1 ; /* back to blocked waiting for output */
- vio->cli_more_wait = 0 ; /* exit more_wait */
- vio->cmd_out_enabled = 1 ; /* re-enable output */
+ cli->more_wait = false ; /* exit more_wait */
+ cli->more_active = false ; /* tidy */
+ cli->out_active = true ; /* re-enable output */
} ;
/*------------------------------------------------------------------------------
@@ -754,35 +1022,37 @@ uty_cli_exit_more_wait(vty_io vio)
* now_ready -- just left cli_more_wait
* not_ready -- otherwise
*/
-static enum vty_readiness
-uty_cli_more_wait(vty_io vio)
+static vty_readiness_t
+uty_cli_more_wait(vty_cli cli)
{
struct keystroke steal ;
VTY_ASSERT_LOCKED() ;
+ assert(cli->more_wait) ; /* must be in more_wait state ! */
+
/* Deal with the first stage of "--more--" */
- if (vio->cli_blocked)
+ if (cli->more_active)
{
int get ;
/* If the CLI buffer is not yet empty, then is waiting for the
* initial prompt to clear, so nothing to be done here.
*/
- if (!vio_fifo_empty(&vio->cli_obuf))
- return not_ready ;
+ if (!vio_fifo_empty(cli->cbuf))
+ return write_ready ;
- vio->cli_blocked = 0 ;
+ cli->more_active = false ;
/* empty the input buffer into the keystroke stream */
do
{
- get = uty_read(vio, NULL) ;
+ get = uty_term_read(cli->vf, NULL) ;
} while (get > 0) ;
} ;
- /* Go through the "--more--" process, unless no longer write_open (!) */
- if (vio->sock.write_open)
+ /* Go through the "--more--" process, unless closing */
+ if (cli->vf->vout_state == vf_open)
{
/* The read fetches a reasonable lump from the I/O -- so if there
* is a complete keystroke available, expect to get it.
@@ -791,15 +1061,18 @@ uty_cli_more_wait(vty_io vio)
*
* If has hit EOF (or error etc), returns knull_eof.
*/
- uty_read(vio, &steal) ;
+ uty_term_read(cli->vf, &steal) ;
/* If nothing stolen, make sure prompt is drawn and wait for more
* input.
*/
if ((steal.type == ks_null) && (steal.value != knull_eof))
{
- if (uty_cli_draw_if_required(vio)) /* "--more--" if req. */
- return write_ready ;
+ if (uty_cli_draw_if_required(cli)) /* "--more--" if req. */
+ {
+ cli->more_active = true ; /* written "--more--" again */
+ return write_ready ;
+ }
else
return read_ready ;
} ;
@@ -812,7 +1085,7 @@ uty_cli_more_wait(vty_io vio)
case CONTROL('C'):
case 'q':
case 'Q':
- uty_out_clear(vio) ;
+ uty_out_clear(cli->vf->vio) ;
break;
default: /* everything else, thrown away */
@@ -828,7 +1101,7 @@ uty_cli_more_wait(vty_io vio)
* Return write_ready to tidy up the screen and, unless cleared, write
* some more.
*/
- uty_cli_exit_more_wait(vio) ;
+ uty_cli_exit_more_wait(cli) ;
return now_ready ;
} ;
@@ -838,18 +1111,17 @@ uty_cli_more_wait(vty_io vio)
*
* This is buffered separately from the general (command) VTY output above.
*
- * Has a dedicated buffer in the struct vty, which is flushed regularly during
- * command processing.
+ * Has a dedicated buffer in the struct vty_cli, which is flushed regularly
+ * during command processing.
*
* It is expected that can flush straight to the file, since this is running at
* CLI speed. However, if the CLI is being driven by something other than a
* keyboard, or "monitor" output has filled the buffers, then may need to
* have intermediate buffering.
*
- * No actual I/O takes place here-- all "output" is to vio->cli_obuf
- * and/or vio->cli_ex_obuf
+ * No actual I/O takes place here-- all "output" is to cli->cbuf
*
- * The "cli_echo" functions discard the output if vio->cli_echo_suppress != 0.
+ * The "cli_echo" functions discard the output if cli->echo_suppress.
* This is used while passwords are entered and to allow command line changes
* to be made while the line is not visible.
*/
@@ -857,8 +1129,9 @@ uty_cli_more_wait(vty_io vio)
enum { cli_rep_count = 32 } ;
typedef const char cli_rep_char[cli_rep_count] ;
+typedef const char cli_rep[] ;
-static const char telnet_backspaces[] =
+static cli_rep telnet_backspaces =
{ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
@@ -866,35 +1139,39 @@ static const char telnet_backspaces[] =
} ;
CONFIRM(sizeof(telnet_backspaces) == sizeof(cli_rep_char)) ;
-static const char telnet_spaces[] =
- { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
- ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
- ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
- ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '
- } ;
-CONFIRM(sizeof(telnet_spaces) == sizeof(cli_rep_char)) ;
+/* 12345678901234567890123456789012 */
+static cli_rep telnet_spaces = " " ;
+static cli_rep telnet_dots = "................................" ;
+
+CONFIRM(sizeof(telnet_spaces) == (sizeof(cli_rep_char) + 1)) ;
+CONFIRM(sizeof(telnet_dots) == (sizeof(cli_rep_char) + 1)) ;
static const char* telnet_newline = "\r\n" ;
-static void uty_cli_write(vty_io vio, const char *this, int len) ;
-static void uty_cli_write_n(vty_io vio, cli_rep_char chars, int n) ;
+static void uty_cli_write_n(vty_cli cli, cli_rep_char chars, int n) ;
/*------------------------------------------------------------------------------
* CLI VTY output -- cf fprintf()
*/
-static void
-uty_cli_out(vty_io vio, const char *format, ...)
+extern void
+uty_cli_out(vty_cli cli, const char *format, ...)
{
+ va_list args ;
+
VTY_ASSERT_LOCKED() ;
- if (vio->sock.write_open)
- {
- va_list args ;
+ va_start (args, format);
+ vio_fifo_vprintf(cli->cbuf, format, args) ;
+ va_end(args);
+} ;
- va_start (args, format);
- vio_fifo_vprintf(&vio->cli_obuf, format, args) ;
- va_end(args);
- } ;
+/*------------------------------------------------------------------------------
+ * Completely empty the cli command buffer
+ */
+extern void
+uty_cli_out_clear(vty_cli cli)
+{
+ vio_fifo_clear(cli->cbuf, true) ;
} ;
/*------------------------------------------------------------------------------
@@ -903,14 +1180,12 @@ uty_cli_out(vty_io vio, const char *format, ...)
* Do nothing if echo suppressed (eg in AUTH_NODE) or not write_open
*/
static void
-uty_cli_echo(vty_io vio, const char *this, size_t len)
+uty_cli_echo(vty_cli cli, const char *this, size_t len)
{
VTY_ASSERT_LOCKED() ;
- if (vio->cli_echo_suppress || !vio->sock.write_open)
- return ;
-
- uty_cli_write(vio, this, len) ;
+ if (!cli->echo_suppress)
+ uty_cli_write(cli, this, len) ;
}
/*------------------------------------------------------------------------------
@@ -919,33 +1194,30 @@ uty_cli_echo(vty_io vio, const char *this, size_t len)
* Do nothing if echo suppressed (eg in AUTH_NODE)
*/
static void
-uty_cli_echo_n(vty_io vio, cli_rep_char chars, int n)
+uty_cli_echo_n(vty_cli cli, cli_rep_char chars, int n)
{
VTY_ASSERT_LOCKED() ;
- if (vio->cli_echo_suppress || !vio->sock.write_open)
- return ;
-
- uty_cli_write_n(vio, chars, n) ;
+ if (!cli->echo_suppress)
+ uty_cli_write_n(cli, chars, n) ;
}
/*------------------------------------------------------------------------------
* CLI VTY output -- cf write()
*/
-inline static void
-uty_cli_write(vty_io vio, const char *this, int len)
+extern void
+uty_cli_write(vty_cli cli, const char *this, int len)
{
VTY_ASSERT_LOCKED() ;
- if (vio->sock.write_open)
- vio_fifo_put(&vio->cli_obuf, this, len) ;
+ vio_fifo_put_bytes(cli->cbuf, this, len) ;
} ;
/*------------------------------------------------------------------------------
* CLI VTY output -- write 'n' characters using a cli_rep_char string
*/
static void
-uty_cli_write_n(vty_io vio, cli_rep_char chars, int n)
+uty_cli_write_n(vty_cli cli, cli_rep_char chars, int n)
{
int len ;
@@ -954,7 +1226,7 @@ uty_cli_write_n(vty_io vio, cli_rep_char chars, int n)
{
if (n < len)
len = n ;
- uty_cli_write(vio, chars, len) ;
+ uty_cli_write(cli, chars, len) ;
n -= len ;
} ;
} ;
@@ -965,13 +1237,13 @@ uty_cli_write_n(vty_io vio, cli_rep_char chars, int n)
* Returns length of string.
*/
static int
-uty_cli_write_s(vty_io vio, const char *str)
+uty_cli_write_s(vty_cli cli, const char *str)
{
int len ;
len = strlen(str) ;
if (len != 0)
- uty_cli_write(vio, str, len) ;
+ uty_cli_write(cli, str, len) ;
return len ;
} ;
@@ -985,12 +1257,12 @@ uty_cli_write_s(vty_io vio, const char *str)
*
* Clears the cli_drawn and the cli_dirty flags.
*/
-static void
-uty_cli_out_newline(vty_io vio)
+extern void
+uty_cli_out_newline(vty_cli cli)
{
- uty_cli_write(vio, telnet_newline, 2) ;
- vio->cli_drawn = 0 ;
- vio->cli_dirty = 0 ;
+ uty_cli_write(cli, telnet_newline, 2) ;
+ cli->drawn = false ;
+ cli->dirty = false ;
} ;
/*------------------------------------------------------------------------------
@@ -1000,23 +1272,23 @@ uty_cli_out_newline(vty_io vio)
* 'n' > 0, wipes characters forwards, leaving cursor where it is
*/
static void
-uty_cli_out_wipe_n(vty_io vio, int n)
+uty_cli_out_wipe_n(vty_cli cli, int n)
{
if (n < 0)
{
n = abs(n) ;
- uty_cli_write_n(vio, telnet_backspaces, n);
+ uty_cli_write_n(cli, telnet_backspaces, n);
} ;
if (n > 0)
{
- uty_cli_write_n(vio, telnet_spaces, n) ;
- uty_cli_write_n(vio, telnet_backspaces, n) ;
+ uty_cli_write_n(cli, telnet_spaces, n) ;
+ uty_cli_write_n(cli, telnet_backspaces, n) ;
} ;
} ;
/*------------------------------------------------------------------------------
- * Send response to the given cli_do
+ * Send response to the given cmd_do
*
* If no command is in progress, then will send newline to signal that the
* command is about to be dispatched.
@@ -1024,113 +1296,126 @@ uty_cli_out_wipe_n(vty_io vio, int n)
* If command is in progress, then leaves cursor on '^' to signal that is now
* waiting for previous command to complete.
*/
-static const char* cli_response [2][cli_do_count] =
+static const char* cli_response [2][cmd_do_count] =
{
{ /* when not waiting for previous command to complete */
- [cli_do_command] = "",
- [cli_do_ctrl_c] = "^C",
- [cli_do_ctrl_d] = "^D",
- [cli_do_ctrl_z] = "^Z",
- [cli_do_eof] = "^*"
+ [cmd_do_command] = "",
+ [cmd_do_ctrl_c] = "^C",
+ [cmd_do_ctrl_d] = "^D",
+ [cmd_do_ctrl_z] = "^Z",
+ [cmd_do_eof] = "^*"
},
{ /* when waiting for a previous command to complete */
- [cli_do_command] = "^",
- [cli_do_ctrl_c] = "^C",
- [cli_do_ctrl_d] = "^D",
- [cli_do_ctrl_z] = "^Z",
- [cli_do_eof] = "^*"
+ [cmd_do_command] = "^",
+ [cmd_do_ctrl_c] = "^C",
+ [cmd_do_ctrl_d] = "^D",
+ [cmd_do_ctrl_z] = "^Z",
+ [cmd_do_eof] = "^*"
}
} ;
static void
-uty_cli_response(vty_io vio, enum cli_do cli_do)
+uty_cli_response(vty_cli cli, cmd_do_t to_do)
{
const char* str ;
int len ;
- if ((cli_do == cli_do_nothing) || (vio->half_closed))
+ if ((to_do == cmd_do_nothing) || (cli->vf->vin_state != vf_open))
return ;
- str = (cli_do < cli_do_count)
- ? cli_response[vio->cmd_in_progress ? 1 : 0][cli_do] : NULL ;
+ str = (to_do < cmd_do_count)
+ ? cli_response[cli->in_progress ? 1 : 0][to_do] : NULL ;
assert(str != NULL) ;
- len = uty_cli_write_s(vio, str) ;
+ len = uty_cli_write_s(cli, str) ;
- if (vio->cmd_in_progress)
+ if (cli->in_progress)
{
- vio->cli_extra_len = len ;
- uty_cli_write_n(vio, telnet_backspaces, len) ;
+ cli->extra_len = len ;
+ uty_cli_write_n(cli, telnet_backspaces, len) ;
}
else
{
- uty_cli_out_newline(vio) ;
+ uty_cli_out_newline(cli) ;
} ;
} ;
/*------------------------------------------------------------------------------
* Send various messages with trailing newline.
*/
-
+#if 0
static void
-uty_cli_out_CMD_ERR_AMBIGUOUS(vty_io vio)
+uty_cli_out_CMD_ERR_AMBIGUOUS(vty_cli cli)
{
- uty_cli_write_s(vio, "% " MSG_CMD_ERR_AMBIGUOUS ".") ;
- uty_cli_out_newline(vio) ;
+ uty_cli_write_s(cli, "% " MSG_CMD_ERR_AMBIGUOUS ".") ;
+ uty_cli_out_newline(cli) ;
} ;
static void
-uty_cli_out_CMD_ERR_NO_MATCH(vty_io vio)
+uty_cli_out_CMD_ERR_NO_MATCH(vty_cli cli)
{
- uty_cli_write_s(vio, "% " MSG_CMD_ERR_NO_MATCH ".") ;
- uty_cli_out_newline(vio) ;
+ uty_cli_write_s(cli, "% " MSG_CMD_ERR_NO_MATCH ".") ;
+ uty_cli_out_newline(cli) ;
} ;
-
+#endif
/*==============================================================================
* Command line draw and wipe
*/
/*------------------------------------------------------------------------------
+ * Current prompt length
+ */
+extern ulen
+uty_cli_prompt_len(vty_cli cli)
+{
+ return cli->prompt_len ;
+} ;
+
+/*------------------------------------------------------------------------------
* Wipe the current console line -- if any.
+ *
+ * If it is known that some characters will immediately be written, then
+ * passing the number of them as "len" will reduce the number of characters
+ * that have to be sent.
*/
-static void
-uty_cli_wipe(vty_io vio, int len)
+extern void
+uty_cli_wipe(vty_cli cli, int len)
{
int a ;
int b ;
- if (!vio->cli_drawn)
+ if (!cli->drawn)
return ; /* quit if already wiped */
- assert(vio->cl.cp <= vio->cl.len) ;
+ assert(qs_cp_nn(cli->cl) <= qs_len_nn(cli->cl)) ;
/* Establish how much ahead and how much behind the cursor */
- a = vio->cli_extra_len ;
- b = vio->cli_prompt_len ;
+ a = cli->extra_len ;
+ b = cli->prompt_len ;
- if (!vio->cli_echo_suppress && !vio->cli_more_wait)
+ if (!cli->echo_suppress && !cli->more_wait)
{
- a += vio->cl.len - vio->cl.cp ;
- b += vio->cl.cp ;
+ a += qs_len_nn(cli->cl) - qs_cp_nn(cli->cl) ;
+ b += qs_cp_nn(cli->cl) ;
} ;
/* Wipe anything ahead of the current position and ahead of new len */
if ((a + b) > len)
- uty_cli_out_wipe_n(vio, +a) ;
+ uty_cli_out_wipe_n(cli, +a) ;
/* Wipe anything behind current position, but ahead of new len */
if (b > len)
{
- uty_cli_out_wipe_n(vio, -(b - len)) ;
+ uty_cli_out_wipe_n(cli, -(b - len)) ;
b = len ; /* moved the cursor back */
} ;
/* Back to the beginning of the line */
- uty_cli_write_n(vio, telnet_backspaces, b) ;
+ uty_cli_write_n(cli, telnet_backspaces, b) ;
/* Nothing there any more */
- vio->cli_drawn = 0 ;
- vio->cli_dirty = 0 ;
+ cli->drawn = false ;
+ cli->dirty = false ;
} ;
/*------------------------------------------------------------------------------
@@ -1140,12 +1425,12 @@ uty_cli_wipe(vty_io vio, int len)
* See uty_cli_draw().
*/
extern bool
-uty_cli_draw_if_required(vty_io vio)
+uty_cli_draw_if_required(vty_cli cli)
{
- if (vio->cli_drawn)
+ if (cli->drawn)
return false ;
- uty_cli_draw(vio) ;
+ uty_cli_draw(cli) ;
return true ;
} ;
@@ -1156,9 +1441,9 @@ uty_cli_draw_if_required(vty_io vio)
* See uty_cli_draw_this()
*/
static void
-uty_cli_draw(vty_io vio)
+uty_cli_draw(vty_cli cli)
{
- uty_cli_draw_this(vio, vio->vty->node) ;
+ uty_cli_draw_this(cli, cli->node) ;
} ;
/*------------------------------------------------------------------------------
@@ -1171,7 +1456,7 @@ uty_cli_draw(vty_io vio)
*
* Draws prompt according to the given 'node', except:
*
- * * if is half_closed, draw nothing -- wipes the current line
+ * * if is closing, draw nothing -- wipes the current line
*
* * if is cli_more_wait, draw the "--more--" prompt
*
@@ -1187,34 +1472,34 @@ uty_cli_draw(vty_io vio)
* cli_echo_suppress = (AUTH_NODE or AUTH_ENABLE_NODE)
*/
static void
-uty_cli_draw_this(vty_io vio, enum node_type node)
+uty_cli_draw_this(vty_cli cli, enum node_type node)
{
const char* prompt ;
size_t l_len ;
int p_len ;
- if (vio->cli_dirty)
- uty_cli_out_newline(vio) ; /* clears cli_dirty and cli_drawn */
+ if (cli->dirty)
+ uty_cli_out_newline(cli) ; /* clears cli_dirty and cli_drawn */
/* Sort out what the prompt is. */
- if (vio->half_closed)
+ if (cli->vf->vin_state != vf_open)
{
prompt = "" ;
p_len = 0 ;
l_len = 0 ;
}
- else if (vio->cli_more_wait)
+ else if (cli->more_wait)
{
prompt = "--more--" ;
p_len = strlen(prompt) ;
l_len = 0 ;
}
- else if (vio->cmd_in_progress)
+ else if (cli->in_progress)
{
/* If there is a queued command, the prompt is a minimal affair. */
prompt = "~ " ;
p_len = strlen(prompt) ;
- l_len = vio->cl.len ;
+ l_len = qs_len_nn(cli->cl) ;
}
else
{
@@ -1225,49 +1510,46 @@ uty_cli_draw_this(vty_io vio, enum node_type node)
*
* If the host name changes, the cli_prompt_set flag is cleared.
*/
- if (!vio->cli_prompt_set || (node != vio->cli_prompt_node))
+ if ((node != cli->prompt_node) || (host.name_gen != cli->prompt_gen))
{
const char* prompt ;
- if (vty_host_name == NULL)
- uty_check_host_name() ; /* should never be required */
-
prompt = cmd_prompt(node) ;
if (prompt == NULL)
{
- zlog_err("vty %s has node %d", uty_get_name(vio), node) ;
+ zlog_err("vty %s has node %d", uty_get_name(cli->vf->vio), node) ;
prompt = "%s ???: " ;
} ;
- qs_printf(&vio->cli_prompt_for_node, prompt, vty_host_name);
+ qs_printf(cli->prompt_for_node, prompt, host.name);
- vio->cli_prompt_node = node ;
- vio->cli_prompt_set = 1 ;
+ cli->prompt_node = node ;
+ cli->prompt_gen = host.name_gen ;
} ;
- prompt = vio->cli_prompt_for_node.body ;
- p_len = vio->cli_prompt_for_node.len ;
- l_len = vio->cl.len ;
+ prompt = qs_char(cli->prompt_for_node) ;
+ p_len = qs_len(cli->prompt_for_node) ;
+ l_len = qs_len_nn(cli->cl) ;
} ;
/* Now, if line is currently drawn, time to wipe it */
- if (vio->cli_drawn)
- uty_cli_wipe(vio, p_len + l_len) ;
+ if (cli->drawn)
+ uty_cli_wipe(cli, p_len + l_len) ;
/* Set about writing the prompt and the line */
- vio->cli_drawn = 1 ;
- vio->cli_extra_len = 0 ;
- vio->cli_echo_suppress = (node == AUTH_NODE || node == AUTH_ENABLE_NODE) ;
+ cli->drawn = true ;
+ cli->extra_len = false ;
+ cli->echo_suppress = (node == AUTH_NODE || node == AUTH_ENABLE_NODE) ;
- vio->cli_prompt_len = p_len ;
+ cli->prompt_len = p_len ;
- uty_cli_write(vio, prompt, p_len) ;
+ uty_cli_write(cli, prompt, p_len) ;
if (l_len != 0)
{
- uty_cli_write(vio, qs_chars(&vio->cl), l_len) ;
- if (vio->cl.cp < l_len)
- uty_cli_write_n(vio, telnet_backspaces, l_len - vio->cl.cp) ;
+ uty_cli_write(cli, qs_char(cli->cl), l_len) ;
+ if (qs_cp_nn(cli->cl) < l_len)
+ uty_cli_write_n(cli, telnet_backspaces, l_len - qs_cp_nn(cli->cl)) ;
} ;
} ;
@@ -1298,11 +1580,11 @@ uty_cli_draw_this(vty_io vio, enum node_type node)
* Wipe any existing command line.
*/
extern void
-uty_cli_pre_monitor(vty_io vio, size_t len)
+uty_cli_pre_monitor(vty_cli cli, size_t len)
{
VTY_ASSERT_LOCKED() ;
- uty_cli_wipe(vio, len) ;
+ uty_cli_wipe(cli, len) ;
} ;
/*------------------------------------------------------------------------------
@@ -1317,16 +1599,16 @@ uty_cli_pre_monitor(vty_io vio, size_t len)
* > 0 => rump not empty or some command line stuff written
*/
extern int
-uty_cli_post_monitor(vty_io vio, const char* buf, size_t len)
+uty_cli_post_monitor(vty_cli cli, const char* buf, size_t len)
{
VTY_ASSERT_LOCKED() ;
if (len != 0)
- uty_cli_write(vio, buf, len) ;
+ uty_cli_write(cli, buf, len) ;
- if (vio->cli_more_wait || (vio->cl.len != 0))
+ if (cli->more_wait || (qs_len_nn(cli->cl) != 0))
{
- uty_cli_draw(vio) ;
+ uty_cli_draw(cli) ;
++len ;
} ;
@@ -1337,48 +1619,52 @@ uty_cli_post_monitor(vty_io vio, const char* buf, size_t len)
* Command line processing loop
*/
-static bool uty_telnet_command(vty_io vio, keystroke stroke, bool callback) ;
-static int uty_cli_insert (vty_io vio, const char* chars, int n) ;
-static int uty_cli_overwrite (vty_io vio, char* chars, int n) ;
-static int uty_cli_word_overwrite (vty_io vio, char *str) ;
-static int uty_cli_forwards(vty_io vio, int n) ;
-static int uty_cli_backwards(vty_io vio, int n) ;
-static int uty_cli_del_forwards(vty_io vio, int n) ;
-static int uty_cli_del_backwards(vty_io vio, int n) ;
-static int uty_cli_bol (vty_io vio) ;
-static int uty_cli_eol (vty_io vio) ;
-static int uty_cli_word_forwards_delta(vty_io vio) ;
-static int uty_cli_word_forwards(vty_io vio) ;
-static int uty_cli_word_backwards_delta(vty_io vio, int eat_spaces) ;
-static int uty_cli_word_backwards_pure (vty_io vio) ;
-static int uty_cli_word_backwards (vty_io vio) ;
-static int uty_cli_del_word_forwards(vty_io vio) ;
-static int uty_cli_del_word_backwards(vty_io vio) ;
-static int uty_cli_del_to_eol (vty_io vio) ;
-static int uty_cli_clear_line(vty_io vio) ;
-static int uty_cli_transpose_chars(vty_io vio) ;
-static void uty_cli_history_use(vty_io vio, int step) ;
-static void uty_cli_next_line(vty_io vio) ;
-static void uty_cli_previous_line (vty_io vio) ;
-static void uty_cli_complete_command (vty_io vio, enum node_type node) ;
-static void uty_cli_describe_command (vty_io vio, enum node_type node) ;
+static int uty_cli_insert (vty_cli cli, const char* chars, int n) ;
+static int uty_cli_overwrite (vty_cli cli, char* chars, int n) ;
+//static int uty_cli_word_overwrite (vty_cli cli, char *str) ;
+static int uty_cli_forwards(vty_cli cli, int n) ;
+static int uty_cli_backwards(vty_cli cli, int n) ;
+static int uty_cli_del_forwards(vty_cli cli, int n) ;
+static int uty_cli_del_backwards(vty_cli cli, int n) ;
+static int uty_cli_bol (vty_cli cli) ;
+static int uty_cli_eol (vty_cli cli) ;
+static int uty_cli_word_forwards_delta(vty_cli cli) ;
+static int uty_cli_word_forwards(vty_cli cli) ;
+static int uty_cli_word_backwards_delta(vty_cli cli, int eat_spaces) ;
+//static int uty_cli_word_backwards_pure (vty_cli cli) ;
+static int uty_cli_word_backwards (vty_cli cli) ;
+static int uty_cli_del_word_forwards(vty_cli cli) ;
+static int uty_cli_del_word_backwards(vty_cli cli) ;
+static int uty_cli_del_to_eol (vty_cli cli) ;
+static int uty_cli_clear_line(vty_cli cli) ;
+static int uty_cli_transpose_chars(vty_cli cli) ;
+static void uty_cli_hist_use(vty_cli cli, int step) ;
+static void uty_cli_hist_next(vty_cli cli) ;
+static void uty_cli_hist_previous(vty_cli cli) ;
+static void uty_cli_complete_command (vty_cli cli, node_type_t node) ;
+static void uty_cli_describe_command (vty_cli cli, node_type_t node) ;
/*------------------------------------------------------------------------------
* Fetch next keystroke, reading from the file if required.
*/
-static inline bool
-uty_cli_get_keystroke(vty_io vio, keystroke stroke)
+static bool
+uty_cli_get_keystroke(vty_cli cli, keystroke stroke)
{
- if (keystroke_get(vio->key_stream, stroke))
- return 1 ;
+ int got ;
+
+ do
+ {
+ if (keystroke_get(cli->key_stream, stroke))
+ return true ;
- uty_read(vio, NULL) ; /* not stealing */
+ got = uty_term_read(cli->vf, NULL) ; /* not stealing */
+ } while (got > 0) ;
- return keystroke_get(vio->key_stream, stroke) ;
+ return false ;
} ;
/*------------------------------------------------------------------------------
- * Process keystrokes until run out of input, or get something to cli_do.
+ * Process keystrokes until run out of input, or get something to cmd_do.
*
* If required, draw the prompt and command line.
*
@@ -1388,30 +1674,30 @@ uty_cli_get_keystroke(vty_io vio, keystroke stroke)
* Processes the contents of the keystroke stream. If exhausts that, will set
* ready to read and return. (To give some "sharing".)
*
- * Returns: cli_do_xxxx
+ * Returns: cmd_do_xxxx
*
* When returns the cl is '\0' terminated.
*/
-static enum cli_do
-uty_cli_process(vty_io vio, enum node_type node)
+static cmd_do_t
+uty_cli_process(vty_cli cli, node_type_t node)
{
- struct keystroke stroke ;
- uint8_t u ;
- int auth ;
- enum cli_do ret ;
+ struct keystroke stroke ;
+ uint8_t u ;
+ bool auth ;
+ cmd_do_t to_do ;
auth = (node == AUTH_NODE || node == AUTH_ENABLE_NODE) ;
/* Now process as much as possible of what there is */
- ret = cli_do_nothing ;
- while (ret == cli_do_nothing)
+ to_do = cmd_do_nothing ;
+ while (to_do == cmd_do_nothing)
{
- if (!vio->cli_drawn)
- uty_cli_draw_this(vio, node) ;
+ if (!cli->drawn)
+ uty_cli_draw_this(cli, node) ;
- if (!uty_cli_get_keystroke(vio, &stroke))
+ if (!uty_cli_get_keystroke(cli, &stroke))
{
- ret = (stroke.value == knull_eof) ? cli_do_eof : cli_do_nothing ;
+ to_do = (stroke.value == knull_eof) ? cmd_do_eof : cmd_do_nothing ;
break ;
} ;
@@ -1431,87 +1717,91 @@ uty_cli_process(vty_io vio, enum node_type node)
switch (stroke.value)
{
case CONTROL('A'):
- uty_cli_bol (vio);
+ uty_cli_bol (cli);
break;
case CONTROL('B'):
- uty_cli_backwards(vio, 1);
+ uty_cli_backwards(cli, 1);
break;
case CONTROL('C'):
- ret = cli_do_ctrl_c ; /* Exit on ^C ..................*/
+ to_do = cmd_do_ctrl_c ; /* Exit on ^C ..................*/
break ;
case CONTROL('D'):
- if (vio->cl.len == 0) /* if at start of empty line */
- ret = cli_do_ctrl_d ; /* Exit on ^D ..................*/
+ if (auth)
+ to_do = cmd_do_ctrl_d ; /* Exit on ^D ..................*/
else
- uty_cli_del_forwards(vio, 1);
+ uty_cli_del_forwards(cli, 1);
break;
case CONTROL('E'):
- uty_cli_eol (vio);
+ uty_cli_eol (cli);
break;
case CONTROL('F'):
- uty_cli_forwards(vio, 1);
+ uty_cli_forwards(cli, 1);
break;
case CONTROL('H'):
case 0x7f:
- uty_cli_del_backwards(vio, 1);
+ uty_cli_del_backwards(cli, 1);
break;
case CONTROL('K'):
- uty_cli_del_to_eol (vio);
+ uty_cli_del_to_eol (cli);
break;
case CONTROL('N'):
- uty_cli_next_line (vio);
+ uty_cli_hist_next (cli);
break;
case CONTROL('P'):
- uty_cli_previous_line (vio);
+ uty_cli_hist_previous(cli);
break;
case CONTROL('T'):
- uty_cli_transpose_chars (vio);
+ uty_cli_transpose_chars (cli);
break;
case CONTROL('U'):
- uty_cli_clear_line(vio);
+ uty_cli_clear_line(cli);
break;
case CONTROL('W'):
- uty_cli_del_word_backwards (vio);
+ uty_cli_del_word_backwards (cli);
break;
case CONTROL('Z'):
- ret = cli_do_ctrl_z ; /* Exit on ^Z ..................*/
+ to_do = cmd_do_ctrl_z ; /* Exit on ^Z ..................*/
break;
case '\n':
case '\r':
- ret = cli_do_command ; /* Exit on CR or LF.............*/
+ to_do = cmd_do_command ; /* Exit on CR or LF.............*/
break ;
case '\t':
if (auth)
- break ;
+ uty_cli_insert (cli, " ", 1) ;
+ else if (cmd_token_position(cli->parsed, cli->cl))
+ uty_cli_insert (cli, " ", 1) ;
else
- uty_cli_complete_command (vio, node);
+ uty_cli_complete_command (cli, node);
break;
case '?':
- if (auth)
- uty_cli_insert (vio, (char*)&u, 1);
+ if (auth)
+ uty_cli_insert (cli, (char*)&u, 1) ;
+ else if (cmd_token_position(cli->parsed, cli->cl))
+ uty_cli_insert (cli, (char*)&u, 1) ;
else
- uty_cli_describe_command (vio, node);
+ uty_cli_describe_command (cli, node);
break;
default:
if ((stroke.value >= 0x20) && (stroke.value < 0x7F))
- uty_cli_insert (vio, (char*)&u, 1) ;
+ uty_cli_insert (cli, (char*)&u, 1) ;
break;
}
break ;
@@ -1521,20 +1811,20 @@ uty_cli_process(vty_io vio, enum node_type node)
switch (stroke.value)
{
case 'b':
- uty_cli_word_backwards (vio);
+ uty_cli_word_backwards (cli);
break;
case 'f':
- uty_cli_word_forwards (vio);
+ uty_cli_word_forwards (cli);
break;
case 'd':
- uty_cli_del_word_forwards (vio);
+ uty_cli_del_word_forwards (cli);
break;
case CONTROL('H'):
case 0x7f:
- uty_cli_del_word_backwards (vio);
+ uty_cli_del_word_backwards (cli);
break;
default:
@@ -1549,20 +1839,20 @@ uty_cli_process(vty_io vio, enum node_type node)
switch (stroke.value)
{
- case ('A'):
- uty_cli_previous_line (vio);
+ case ('A'): /* up arrow */
+ uty_cli_hist_previous(cli);
break;
- case ('B'):
- uty_cli_next_line (vio);
+ case ('B'): /* down arrow */
+ uty_cli_hist_next (cli);
break;
- case ('C'):
- uty_cli_forwards(vio, 1);
+ case ('C'): /* right arrow */
+ uty_cli_forwards(cli, 1);
break;
- case ('D'):
- uty_cli_backwards(vio, 1);
+ case ('D'): /* left arrow */
+ uty_cli_backwards(cli, 1);
break;
default:
@@ -1572,7 +1862,7 @@ uty_cli_process(vty_io vio, enum node_type node)
/* Telnet Command ----------------------------------------------------*/
case ks_iac:
- uty_telnet_command(vio, &stroke, false) ;
+ uty_telnet_command(cli->vf, &stroke, false) ;
break ;
/* Unknown -----------------------------------------------------------*/
@@ -1584,12 +1874,10 @@ uty_cli_process(vty_io vio, enum node_type node)
/* Tidy up and return where got to. */
- if (ret != cli_do_nothing)
- uty_cli_eol (vio) ; /* go to the end of the line */
-
- qs_term(&vio->cl) ; /* add '\0' */
+ if (to_do != cmd_do_nothing)
+ uty_cli_eol (cli) ; /* go to the end of the line */
- return ret ;
+ return to_do ;
} ;
/*==============================================================================
@@ -1602,25 +1890,25 @@ uty_cli_process(vty_io vio, enum node_type node)
* Returns number of characters inserted -- ie 'n'
*/
static int
-uty_cli_insert (vty_io vio, const char* chars, int n)
+uty_cli_insert (vty_cli cli, const char* chars, int n)
{
int after ;
VTY_ASSERT_LOCKED() ;
- assert((vio->cl.cp <= vio->cl.len) && (n >= 0)) ;
+ assert((qs_cp_nn(cli->cl) <= qs_len_nn(cli->cl)) && (n >= 0)) ;
if (n <= 0)
return n ; /* avoid trouble */
- after = qs_insert(&vio->cl, chars, n) ;
+ after = qs_insert(cli->cl, chars, n) ;
- uty_cli_echo(vio, qs_cp_char(&vio->cl), after + n) ;
+ uty_cli_echo(cli, qs_cp_char(cli->cl), after) ;
- if (after != 0)
- uty_cli_echo_n(vio, telnet_backspaces, after) ;
+ if ((after - n) != 0)
+ uty_cli_echo_n(cli, telnet_backspaces, after - n) ;
- vio->cl.cp += n ;
+ qs_move_cp_nn(cli->cl, n) ;
return n ;
} ;
@@ -1633,24 +1921,58 @@ uty_cli_insert (vty_io vio, const char* chars, int n)
* Returns number of characters inserted -- ie 'n'
*/
static int
-uty_cli_overwrite (vty_io vio, char* chars, int n)
+uty_cli_overwrite (vty_cli cli, char* chars, int n)
{
VTY_ASSERT_LOCKED() ;
- assert((vio->cl.cp <= vio->cl.len) && (n >= 0)) ;
+ assert((qs_cp_nn(cli->cl) <= qs_len_nn(cli->cl)) && (n >= 0)) ;
if (n > 0)
{
- qs_replace(&vio->cl, chars, n) ;
- uty_cli_echo(vio, chars, n) ;
+ qs_replace(cli->cl, n, chars, n) ;
+ uty_cli_echo(cli, chars, n) ;
- vio->cl.cp += n ;
+ qs_move_cp_nn(cli->cl, n) ;
} ;
return n ;
}
/*------------------------------------------------------------------------------
+ * Replace 'm' characters at the current position, by 'n' characters and leave
+ * cursor at the end of the inserted characters.
+ *
+ * Returns number of characters inserted -- ie 'n'
+ */
+static int
+uty_cli_replace(vty_cli cli, int m, const char* chars, int n)
+{
+ int a, b ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ assert((qs_cp_nn(cli->cl) <= qs_len_nn(cli->cl)) && (n >= 0) && (m >= 0)) ;
+
+ b = qs_len_nn(cli->cl) - qs_cp_nn(cli->cl) ;
+ a = qs_replace(cli->cl, m, chars, n) ;
+
+ uty_cli_echo(cli, qs_cp_char(cli->cl), a) ;
+
+ if (a < b)
+ uty_cli_echo_n(cli, telnet_spaces, b - a) ;
+ else
+ b = a ;
+
+ if (b > n)
+ uty_cli_echo_n(cli, telnet_backspaces, b - n) ;
+
+ qs_move_cp_nn(cli->cl, n) ;
+
+ return n ;
+} ;
+
+#if 0
+/*------------------------------------------------------------------------------
* Insert a word into vty interface with overwrite mode.
*
* NB: Assumes result will then be the end of the line.
@@ -1658,17 +1980,18 @@ uty_cli_overwrite (vty_io vio, char* chars, int n)
* Returns number of characters inserted -- ie length of string
*/
static int
-uty_cli_word_overwrite (vty_io vio, char *str)
+uty_cli_word_overwrite (vty_cli cli, char *str)
{
int n ;
VTY_ASSERT_LOCKED() ;
- n = uty_cli_overwrite(vio, str, strlen(str)) ;
+ n = uty_cli_overwrite(cli, str, strlen(str)) ;
- vio->cl.len = vio->cl.cp ;
+ qs_set_len_nn(cli->cl, qs_cp_nn(cli->cl)) ;
return n ;
}
+#endif
/*------------------------------------------------------------------------------
* Forward 'n' characters -- stop at end of line.
@@ -1676,12 +1999,12 @@ uty_cli_word_overwrite (vty_io vio, char *str)
* Returns number of characters actually moved
*/
static int
-uty_cli_forwards(vty_io vio, int n)
+uty_cli_forwards(vty_cli cli, int n)
{
int have ;
VTY_ASSERT_LOCKED() ;
- have = vio->cl.len - vio->cl.cp ;
+ have = qs_after_cp_nn(cli->cl) ;
if (have < n)
n = have ;
@@ -1689,12 +2012,12 @@ uty_cli_forwards(vty_io vio, int n)
if (n > 0)
{
- uty_cli_echo(vio, qs_cp_char(&vio->cl), n) ;
- vio->cl.cp += n ;
+ uty_cli_echo(cli, qs_cp_char(cli->cl), n) ;
+ qs_move_cp_nn(cli->cl, n) ;
} ;
return n ;
-}
+} ;
/*------------------------------------------------------------------------------
* Backwards 'n' characters -- stop at start of line.
@@ -1702,23 +2025,43 @@ uty_cli_forwards(vty_io vio, int n)
* Returns number of characters actually moved
*/
static int
-uty_cli_backwards(vty_io vio, int n)
+uty_cli_backwards(vty_cli cli, int n)
{
VTY_ASSERT_LOCKED() ;
- if ((int)vio->cl.cp < n)
- n = vio->cl.cp ;
+ if ((int)qs_cp_nn(cli->cl) < n)
+ n = qs_cp_nn(cli->cl) ;
assert(n >= 0) ;
if (n > 0)
{
- uty_cli_echo_n(vio, telnet_backspaces, n) ;
- vio->cl.cp -= n ;
+ uty_cli_echo_n(cli, telnet_backspaces, n) ;
+ qs_move_cp_nn(cli->cl, -n) ;
} ;
return n ;
-}
+} ;
+
+/*------------------------------------------------------------------------------
+ * Move forwards (if n > 0) or backwards (if n < 0) -- stop at start or end of
+ * line.
+ *
+ * Returns number of characters actually moved -- signed
+ */
+static int
+uty_cli_move(vty_cli cli, int n)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if (n < 0)
+ return - uty_cli_backwards(cli, -n) ;
+
+ if (n > 0)
+ return + uty_cli_forwards(cli, +n) ;
+
+ return 0 ;
+} ;
/*------------------------------------------------------------------------------
* Delete 'n' characters -- forwards -- stop at end of line.
@@ -1726,14 +2069,14 @@ uty_cli_backwards(vty_io vio, int n)
* Returns number of characters actually deleted.
*/
static int
-uty_cli_del_forwards(vty_io vio, int n)
+uty_cli_del_forwards(vty_cli cli, int n)
{
int after ;
int have ;
VTY_ASSERT_LOCKED() ;
- have = vio->cl.len - vio->cl.cp ;
+ have = qs_after_cp_nn(cli->cl) ;
if (have < n)
n = have ; /* cannot delete more than have */
@@ -1742,13 +2085,13 @@ uty_cli_del_forwards(vty_io vio, int n)
if (n <= 0)
return 0 ;
- after = qs_delete(&vio->cl, n) ;
+ after = qs_delete(cli->cl, n) ;
if (after > 0)
- uty_cli_echo(vio, qs_cp_char(&vio->cl), after) ;
+ uty_cli_echo(cli, qs_cp_char(cli->cl), after) ;
- uty_cli_echo_n(vio, telnet_spaces, n) ;
- uty_cli_echo_n(vio, telnet_backspaces, after + n) ;
+ uty_cli_echo_n(cli, telnet_spaces, n) ;
+ uty_cli_echo_n(cli, telnet_backspaces, after + n) ;
return n ;
}
@@ -1759,9 +2102,9 @@ uty_cli_del_forwards(vty_io vio, int n)
* Returns number of characters actually deleted.
*/
static int
-uty_cli_del_backwards(vty_io vio, int n)
+uty_cli_del_backwards(vty_cli cli, int n)
{
- return uty_cli_del_forwards(vio, uty_cli_backwards(vio, n)) ;
+ return uty_cli_del_forwards(cli, uty_cli_backwards(cli, n)) ;
}
/*------------------------------------------------------------------------------
@@ -1770,9 +2113,9 @@ uty_cli_del_backwards(vty_io vio, int n)
* Returns number of characters moved over.
*/
static int
-uty_cli_bol (vty_io vio)
+uty_cli_bol (vty_cli cli)
{
- return uty_cli_backwards(vio, vio->cl.cp) ;
+ return uty_cli_backwards(cli, qs_cp_nn(cli->cl)) ;
} ;
/*------------------------------------------------------------------------------
@@ -1781,9 +2124,9 @@ uty_cli_bol (vty_io vio)
* Returns number of characters moved over
*/
static int
-uty_cli_eol (vty_io vio)
+uty_cli_eol (vty_cli cli)
{
- return uty_cli_forwards(vio, vio->cl.len - vio->cl.cp) ;
+ return uty_cli_forwards(cli, qs_after_cp_nn(cli->cl)) ;
} ;
/*------------------------------------------------------------------------------
@@ -1794,7 +2137,7 @@ uty_cli_eol (vty_io vio)
* Steps over non-space characters and then any spaces.
*/
static int
-uty_cli_word_forwards_delta(vty_io vio)
+uty_cli_word_forwards_delta(vty_cli cli)
{
char* cp ;
char* tp ;
@@ -1802,10 +2145,10 @@ uty_cli_word_forwards_delta(vty_io vio)
VTY_ASSERT_LOCKED() ; ;
- assert(vio->cl.cp <= vio->cl.len) ;
+ assert(qs_cp_nn(cli->cl) <= qs_len_nn(cli->cl)) ;
- cp = qs_cp_char(&vio->cl) ;
- ep = qs_ep_char(&vio->cl) ;
+ cp = qs_cp_char(cli->cl) ;
+ ep = qs_ep_char(cli->cl) ;
tp = cp ;
@@ -1824,9 +2167,9 @@ uty_cli_word_forwards_delta(vty_io vio)
* Moves past any non-spaces, then past any spaces.
*/
static int
-uty_cli_word_forwards(vty_io vio)
+uty_cli_word_forwards(vty_cli cli)
{
- return uty_cli_forwards(vio, uty_cli_word_forwards_delta(vio)) ;
+ return uty_cli_forwards(cli, uty_cli_word_forwards_delta(cli)) ;
} ;
/*------------------------------------------------------------------------------
@@ -1838,7 +2181,7 @@ uty_cli_word_forwards(vty_io vio)
* Steps back until next (backwards) character is space, or hits start of line.
*/
static int
-uty_cli_word_backwards_delta(vty_io vio, int eat_spaces)
+uty_cli_word_backwards_delta(vty_cli cli, int eat_spaces)
{
char* cp ;
char* tp ;
@@ -1846,10 +2189,10 @@ uty_cli_word_backwards_delta(vty_io vio, int eat_spaces)
VTY_ASSERT_LOCKED() ; ;
- assert(vio->cl.cp <= vio->cl.len) ;
+ assert(qs_cp_nn(cli->cl) <= qs_len_nn(cli->cl)) ;
- cp = qs_cp_char(&vio->cl) ;
- sp = qs_chars(&vio->cl) ;
+ cp = qs_cp_char(cli->cl) ;
+ sp = qs_char(cli->cl) ;
tp = cp ;
@@ -1863,6 +2206,7 @@ uty_cli_word_backwards_delta(vty_io vio, int eat_spaces)
return cp - tp ;
} ;
+#if 0
/*------------------------------------------------------------------------------
* Backward word, but not trailing spaces.
*
@@ -1871,10 +2215,11 @@ uty_cli_word_backwards_delta(vty_io vio, int eat_spaces)
* Returns number of characters stepped over.
*/
static int
-uty_cli_word_backwards_pure (vty_io vio)
+uty_cli_word_backwards_pure (vty_cli cli)
{
- return uty_cli_backwards(vio, uty_cli_word_backwards_delta(vio, 0)) ;
+ return uty_cli_backwards(cli, uty_cli_word_backwards_delta(cli, 0)) ;
} ;
+#endif
/*------------------------------------------------------------------------------
* Backward word -- move to start of previous word.
@@ -1885,9 +2230,9 @@ uty_cli_word_backwards_pure (vty_io vio)
* Returns number of characters stepped over.
*/
static int
-uty_cli_word_backwards (vty_io vio)
+uty_cli_word_backwards (vty_cli cli)
{
- return uty_cli_backwards(vio, uty_cli_word_backwards_delta(vio, 1)) ;
+ return uty_cli_backwards(cli, uty_cli_word_backwards_delta(cli, 1)) ;
} ;
/*------------------------------------------------------------------------------
@@ -1898,9 +2243,9 @@ uty_cli_word_backwards (vty_io vio)
* Returns number of characters deleted.
*/
static int
-uty_cli_del_word_forwards(vty_io vio)
+uty_cli_del_word_forwards(vty_cli cli)
{
- return uty_cli_del_forwards(vio, uty_cli_word_forwards_delta(vio)) ;
+ return uty_cli_del_forwards(cli, uty_cli_word_forwards_delta(cli)) ;
}
/*------------------------------------------------------------------------------
@@ -1911,9 +2256,9 @@ uty_cli_del_word_forwards(vty_io vio)
* Returns number of characters deleted.
*/
static int
-uty_cli_del_word_backwards(vty_io vio)
+uty_cli_del_word_backwards(vty_cli cli)
{
- return uty_cli_del_backwards(vio, uty_cli_word_backwards_delta(vio, 1)) ;
+ return uty_cli_del_backwards(cli, uty_cli_word_backwards_delta(cli, 1)) ;
} ;
/*------------------------------------------------------------------------------
@@ -1922,9 +2267,9 @@ uty_cli_del_word_backwards(vty_io vio)
* Returns number of characters deleted.
*/
static int
-uty_cli_del_to_eol (vty_io vio)
+uty_cli_del_to_eol (vty_cli cli)
{
- return uty_cli_del_forwards(vio, vio->cl.len - vio->cl.cp) ;
+ return uty_cli_del_forwards(cli, qs_after_cp_nn(cli->cl)) ;
} ;
/*------------------------------------------------------------------------------
@@ -1933,10 +2278,10 @@ uty_cli_del_to_eol (vty_io vio)
* Returns number of characters deleted.
*/
static int
-uty_cli_clear_line(vty_io vio)
+uty_cli_clear_line(vty_cli cli)
{
- uty_cli_bol(vio) ;
- return uty_cli_del_to_eol(vio) ;
+ uty_cli_bol(cli) ;
+ return uty_cli_del_to_eol(cli) ;
} ;
/*------------------------------------------------------------------------------
@@ -1945,7 +2290,7 @@ uty_cli_clear_line(vty_io vio)
* Return number of characters affected.
*/
static int
-uty_cli_transpose_chars(vty_io vio)
+uty_cli_transpose_chars(vty_cli cli)
{
char chars[2] ;
char* cp ;
@@ -1953,54 +2298,79 @@ uty_cli_transpose_chars(vty_io vio)
VTY_ASSERT_LOCKED() ;
/* Give up if < 2 characters or at start of line. */
- if ((vio->cl.len < 2) || (vio->cl.cp < 1))
+ if ((qs_len_nn(cli->cl) < 2) || (qs_cp_nn(cli->cl) < 1))
return 0 ;
/* Move back to first of characters to exchange */
- if (vio->cl.cp == vio->cl.len)
- uty_cli_backwards(vio, 2) ;
+ if (qs_cp_nn(cli->cl) == qs_len_nn(cli->cl))
+ uty_cli_backwards(cli, 2) ;
else
- uty_cli_backwards(vio, 1) ;
+ uty_cli_backwards(cli, 1) ;
/* Pick up in the new order */
- cp = qs_cp_char(&vio->cl) ;
+ cp = qs_cp_char(cli->cl) ;
chars[1] = *cp++ ;
chars[0] = *cp ;
/* And overwrite */
- return uty_cli_overwrite(vio, chars, 2) ;
+ return uty_cli_overwrite(cli, chars, 2) ;
} ;
/*==============================================================================
* Command line history handling
+ *
+ * cli->hist is vector of qstrings
+ * cli->h_now is index of the present time
+ * cli->hp is index of most recent line read back
+ *
+ * cli->hist is initialised empty, with h_now == hp == 0.
+ *
+ * On first use it is set to VTY_MAX_HIST entries, and its size never changes.
+ * Before VTY_MAX_HIST lines have been inserted, a NULL entry signals the end
+ * of history (to date).
+ *
+ * h_now is incremented after a line is inserted (and wraps round). So
+ * stepping +1 moves towards the present (down) and -1 moves towards the past
+ * (up).
+ *
+ * hp == h_now means we are in the present.
+ *
+ * Cannot step forwards from hp == h_now (into the future !).
+ *
+ * Before stepping backwards from hp == hp_now, sets hp_now to be a copy of
+ * the current line (complete with cp), so can return to the present.
+ *
+ * Cannot step backwards to hp == hp_now -- that would be to wrap round from
+ * the ancient past to the now time.
+ *
+ * When storing a line in the history, replaces the last line stored if that
+ * is the same as the new line, apart from whitespace.
*/
/*------------------------------------------------------------------------------
* Add given command line to the history buffer.
*
* This is inserting the vty->buf line into the history.
+ *
+ * Resets hp == h_now.
*/
-extern void
-uty_cli_hist_add (vty_io vio, const char* cmd_line)
+static void
+uty_cli_hist_add (vty_cli cli, qstring clx)
{
- qstring prev_line ;
- qstring_t line ;
- int prev_index ;
+ qstring hist_line ;
+ int prev ;
VTY_ASSERT_LOCKED() ;
- /* Construct a dummy qstring for the given command line */
- qs_dummy(&line, cmd_line, 1) ; /* set cursor to the end */
-
/* make sure have a suitable history vector */
- vector_set_min_length(vio->hist, VTY_MAXHIST) ;
+ vector_set_min_length(cli->hist, VTY_MAXHIST) ;
- /* find the previous command line in the history */
- prev_index = vio->hindex - 1 ;
- if (prev_index < 0)
- prev_index = VTY_MAXHIST - 1 ;
+ /* get the previous command line */
+ prev = cli->h_now - 1 ;
+ if (prev < 0)
+ prev = VTY_MAXHIST - 1 ;
- prev_line = vector_get_item(vio->hist, prev_index) ;
+ hist_line = vector_get_item(cli->hist, prev) ;
/* If the previous line is NULL, that means the history is empty.
*
@@ -2008,23 +2378,32 @@ uty_cli_hist_add (vty_io vio, const char* cmd_line)
* replace it with the current line -- so that the latest whitespace
* version is saved.
*
- * Either way, replace the the previous line entry by moving hindex
- * back !
+ * In both those cases, replace the the previous line entry by moving
+ * h_now back to it -- leaving hist_line pointing at it.
+ *
+ * Otherwise, leave cli->h_now and point hist_line at the most ancient
+ * line in history.
*/
- if ((prev_line == NULL) || (qs_cmp_sig(prev_line, &line) == 0))
- vio->hindex = prev_index ;
+ if ((hist_line == NULL) || (qs_cmp_sig(hist_line, clx) == 0))
+ cli->h_now = prev ;
else
- prev_line = vector_get_item(vio->hist, vio->hindex) ;
+ hist_line = vector_get_item(cli->hist, cli->h_now) ;
- /* Now replace the hindex entry */
- vector_set_item(vio->hist, vio->hindex, qs_copy(prev_line, &line)) ;
+ /* Now replace the h_now entry
+ *
+ * Note that the line inserted in the history has it's 'cp' set to the end of
+ * the line -- so that it is there when it comes back out again.
+ */
+ hist_line = qs_copy(hist_line, clx) ;
+ qs_set_cp_nn(hist_line, qs_len_nn(hist_line)) ;
+ vector_set_item(cli->hist, cli->h_now, hist_line) ;
/* Advance to the near future and reset the history pointer */
- vio->hindex++;
- if (vio->hindex == VTY_MAXHIST)
- vio->hindex = 0;
+ cli->h_now++;
+ if (cli->h_now == VTY_MAXHIST)
+ cli->h_now = 0;
- vio->hp = vio->hindex;
+ cli->hp = cli->h_now;
} ;
/*------------------------------------------------------------------------------
@@ -2032,668 +2411,503 @@ uty_cli_hist_add (vty_io vio, const char* cmd_line)
*
* This function is called from vty_next_line and vty_previous_line.
*
- * Step +1 is towards the present
- * -1 is into the past
+ * Step -1 is into the past (up)
+ * +1 is towards the present (down)
*/
static void
-uty_cli_history_use(vty_io vio, int step)
+uty_cli_hist_use(vty_cli cli, int step)
{
- int index ;
- unsigned old_len ;
- unsigned after ;
- unsigned back ;
- qstring hist ;
+ int hp ;
+ ulen old_len ;
+ ulen new_len ;
+ ulen after ;
+ ulen back ;
+ qstring hist_line ;
VTY_ASSERT_LOCKED() ;
assert((step == +1) || (step == -1)) ;
- index = vio->hp ;
+ hp = cli->hp ;
/* Special case of being at the insertion point */
- if (index == vio->hindex)
+ if (hp == cli->h_now)
{
if (step > 0)
return ; /* already in the present */
/* before stepping back from the present, take a copy of the
* current command line -- so can get back to it.
+ *
+ * Note that the 'cp' is stored with the line. If return to here
+ * and later enter the line it will replace this.
*/
- hist = vector_get_item(vio->hist, vio->hindex) ;
- vector_set_item(vio->hist, vio->hindex, qs_copy(hist, &vio->cl)) ;
+ hist_line = vector_get_item(cli->hist, cli->h_now) ;
+ vector_set_item(cli->hist, cli->h_now, qs_copy(hist_line, cli->cl)) ;
} ;
/* Advance or retreat */
- index += step ;
- if (index < 0)
- index = VTY_MAXHIST - 1 ;
- else if (index >= VTY_MAXHIST)
- index = 0 ;
+ hp += step ;
+ if (hp < 0)
+ hp = VTY_MAXHIST - 1 ;
+ else if (hp >= VTY_MAXHIST)
+ hp = 0 ;
- hist = vector_get_item(vio->hist, index) ;
+ hist_line = vector_get_item(cli->hist, hp) ;
- /* If moving backwards in time, may not move back to the insertion
+ /* If moving backwards in time, may not move back to the h_now
* point (that would be wrapping round to the present) and may not
* move back to a NULL entry (that would be going back before '.').
+ *
+ * If moving forwards in time, may return to the present, with
+ * hp == cli->h_now.
*/
if (step < 0)
- if ((hist == NULL) || (index == vio->hindex))
+ if ((hist_line == NULL) || (hp == cli->h_now))
return ;
- /* Now, if arrived at the insertion point, this is returning to the
- * present, which is fine.
- */
- vio->hp = index;
+ cli->hp = hp ;
/* Move back to the start of the current line */
- uty_cli_bol(vio) ;
+ uty_cli_bol(cli) ;
/* Get previous line from history buffer and echo that */
- old_len = vio->cl.len ;
- qs_copy(&vio->cl, hist) ;
+ old_len = qs_len_nn(cli->cl) ;
+ qs_copy(cli->cl, hist_line) ;
+ new_len = qs_len_nn(cli->cl) ;
/* Sort out wiping out any excess and setting the cursor position */
- if (old_len > vio->cl.len)
- after = old_len - vio->cl.len ;
+ if (old_len > new_len)
+ after = old_len - new_len ;
else
after = 0 ;
- back = after ;
- if (vio->cl.len > vio->cl.cp)
- back += (vio->cl.len - vio->cl.cp) ;
+ /* Return cursor to stored 'cp' -- which will be end of line unless
+ * this is the copy of the original current line stored above.
+ */
+ back = after + qs_after_cp_nn(cli->cl) ;
- if (vio->cl.len > 0)
- uty_cli_echo(vio, vio->cl.body, vio->cl.len) ;
+ if (new_len > 0)
+ uty_cli_echo(cli, qs_char_nn(cli->cl), new_len) ;
if (after > 0)
- uty_cli_echo_n(vio, telnet_spaces, after) ;
+ uty_cli_echo_n(cli, telnet_spaces, after) ;
if (back > 0)
- uty_cli_echo_n(vio, telnet_backspaces, back) ;
+ uty_cli_echo_n(cli, telnet_backspaces, back) ;
return ;
} ;
/*------------------------------------------------------------------------------
- * Use next history line, if any.
+ * Use previous history line, if any (up arrow).
*/
static void
-uty_cli_next_line(vty_io vio)
+uty_cli_hist_previous(vty_cli cli)
{
- uty_cli_history_use(vio, +1) ;
+ uty_cli_hist_use(cli, -1) ;
}
/*------------------------------------------------------------------------------
- * Use previous history line, if any.
+ * Use next history line, if any (down arrow).
*/
static void
-uty_cli_previous_line (vty_io vio)
+uty_cli_hist_next(vty_cli cli)
{
- uty_cli_history_use(vio, -1) ;
+ uty_cli_hist_use(cli, +1) ;
}
-/*==============================================================================
- * Command Completion and Command Description
- *
- */
-static void uty_cli_describe_show(vty_io vio, vector describe) ;
-static void uty_cli_describe_fold (vty_io vio, int cmd_width,
- unsigned int desc_width, struct desc *desc) ;
-static void uty_cli_describe_line(vty_io vio, int cmd_width, const char* cmd,
- const char* str) ;
-
-static vector uty_cli_cmd_prepare(vty_io vio, int help) ;
-
/*------------------------------------------------------------------------------
- * Command completion
+ * Show the contents of the history
*/
-static void
-uty_cli_complete_command (vty_io vio, enum node_type node)
+extern void
+uty_cli_hist_show(vty_cli cli)
{
- unsigned i ;
- int ret ;
- int len ;
- int n ;
- vector matched ;
- vector vline ;
+ int hp ;
VTY_ASSERT_LOCKED() ;
- /* Try and match the tokenised command line */
- vline = uty_cli_cmd_prepare(vio, 1) ;
- matched = cmd_complete_command (vline, node, &ret);
- cmd_free_strvec (vline);
+ hp = cli->h_now ;
- /* Show the result. */
- switch (ret)
+ while(1)
{
- case CMD_ERR_AMBIGUOUS:
- uty_cli_out_newline(vio) ; /* clears cli_drawn */
- uty_cli_out_CMD_ERR_AMBIGUOUS(vio) ;
- break ;
-
- case CMD_ERR_NO_MATCH:
- uty_cli_out_newline(vio) ; /* clears cli_drawn */
- uty_cli_out_CMD_ERR_NO_MATCH(vio) ;
- break ;
-
- case CMD_COMPLETE_FULL_MATCH:
- uty_cli_eol (vio) ;
- uty_cli_word_backwards_pure (vio);
- uty_cli_word_overwrite (vio, vector_get_item(matched, 0));
- uty_cli_insert(vio, " ", 1);
- break ;
-
- case CMD_COMPLETE_MATCH:
- uty_cli_eol (vio) ;
- uty_cli_word_backwards_pure (vio);
- uty_cli_word_overwrite (vio, vector_get_item(matched, 0));
- break ;
-
- case CMD_COMPLETE_LIST_MATCH:
- len = 6 ;
- for (i = 0; i < vector_end(matched); i++)
- {
- int sl = strlen((char*)vector_get_item(matched, i)) ;
- if (len < sl)
- len = sl ;
- } ;
+ qstring line ;
- n = vio->width ;
- if (n == 0)
- n = 60 ;
- n = n / (len + 2) ;
- if (n == 0)
- n = 1 ;
+ ++hp ;
+ if (hp == VTY_MAXHIST)
+ hp = 0 ;
- for (i = 0; i < vector_end(matched); i++)
- {
- if ((i % n) == 0)
- uty_cli_out_newline(vio) ; /* clears cli_drawn */
- uty_cli_out(vio, "%-*s ", len, (char*)vector_get_item(matched, i));
- }
- uty_cli_out_newline(vio) ;
+ if (hp == cli->h_now)
+ break ; /* wrapped round to "now" */
- break;
+ line = vector_get_item(cli->hist, hp) ;
- case CMD_COMPLETE_ALREADY:
- default:
- break;
- } ;
+ if (line == NULL)
+ break ; /* reached limit of history so far */
- cmd_free_strvec(matched);
+ uty_out(cli->vf->vio, " %s\n", qs_string(line));
+ }
} ;
-/*------------------------------------------------------------------------------
- * Command Description
+/*==============================================================================
+ * Command Completion and Command Description
+ *
*/
-static void
-uty_cli_describe_command (vty_io vio, node_type_t node)
-{
- cmd_return_code_t ret ;
- vector describe ;
-
- VTY_ASSERT_LOCKED() ;
-
- /* Try and match the tokenised command line */
- describe = cmd_describe_command (qs_term(&vio->cl), node, &ret);
-
- uty_cli_out_newline(vio); /* clears cli_drawn */
-
- /* Deal with result. */
- switch (ret)
- {
- case CMD_ERR_AMBIGUOUS:
- uty_cli_out_CMD_ERR_AMBIGUOUS(vio) ;
- break ;
+static uint uty_cli_help_parse(vty_cli cli, node_type_t node) ;
+static void uty_cli_out_message(vty_cli cli, const char* msg) ;
- case CMD_ERR_NO_MATCH:
- uty_cli_out_CMD_ERR_NO_MATCH(vio) ;
- break ;
+static void uty_cli_complete_keyword(vty_cli cli, const char* keyword) ;
+static void uty_cli_complete_list(vty_cli cli, vector item_v) ;
- default:
- uty_cli_describe_show(vio, describe) ;
- break ;
- } ;
+static void uty_cli_describe_list(vty_cli cli, vector item_v) ;
+static void uty_cli_describe_line(vty_cli cli, uint str_width, const char* str,
+ const char* doc, uint len) ;
- if (describe != NULL)
- vector_free (describe);
-}
+static uint uty_cli_width_to_use(vty_cli cli) ;
/*------------------------------------------------------------------------------
- * Show the command description.
- *
- * Generates lines of the form:
- *
- * word description text
- *
- * Where the word field is adjusted to suit the longest word, and the
- * description text is wrapped if required (if the width of the console is
- * known) so that get:
+ * Command completion
*
- * word description ..................................
- * .............text
+ * Requires that cmd_token_position() has been called to tokenise the line and
+ * establish which token the cursor is in. Must NOT call this if the cursor
+ * is in a "special" place.
*
- * If one of the options is '<cr>', that is always shown last.
*/
static void
-uty_cli_describe_show(vty_io vio, vector describe)
+uty_cli_complete_command (vty_cli cli, node_type_t node)
{
- unsigned int i, cmd_width, desc_width;
- struct desc *desc, *desc_cr ;
+ uint n_items ;
+ cmd_parsed parsed ;
+ cmd_item item ;
- /* Get width of the longest "word" */
- cmd_width = 0;
- for (i = 0; i < vector_active (describe); i++)
- if ((desc = vector_slot (describe, i)) != NULL)
- {
- unsigned int len;
+ VTY_ASSERT_LOCKED() ;
+
+ parsed = cli->parsed ;
- if (desc->cmd[0] == '\0')
- continue;
+ /* Establish what items may be present at the current token position. */
+ n_items = uty_cli_help_parse(cli, node) ;
- len = strlen (desc->cmd);
- if (desc->cmd[0] == '.')
- len--;
+ if (n_items == 0) /* quit if nothing to consider */
+ return ;
- if (cmd_width < len)
- cmd_width = len;
- }
+ if (n_items > 1) /* render list of alternatives */
+ return uty_cli_complete_list(cli, parsed->item_v) ;
- /* Set width of description string. */
- desc_width = vio->width - (cmd_width + 6);
+ /* One possible item -- one or more possible commands */
+ item = vector_get_item(parsed->item_v, 0) ;
- /* Print out description. */
- desc_cr = NULL ; /* put <cr> last if it appears */
+ switch (item->type)
+ {
+ case item_null:
+ zabort("invalid item_null") ;
- for (i = 0; i < vector_active (describe); i++)
- if ((desc = vector_slot (describe, i)) != NULL)
- {
- if (desc->cmd[0] == '\0')
- continue;
+ case item_eol:
- if (strcmp (desc->cmd, command_cr) == 0)
- {
- desc_cr = desc;
- continue;
- }
+ case item_option_word:
+
+ case item_vararg:
+
+ case item_word:
+
+ case item_ipv6_prefix:
+ case item_ipv6_address:
+ case item_ipv4_prefix:
+ case item_ipv4_address:
+
+ case item_range:
+ return uty_cli_describe_list(cli, parsed->item_v) ;
- uty_cli_describe_fold (vio, cmd_width, desc_width, desc);
- }
+ case item_keyword:
+ return uty_cli_complete_keyword(cli, item->str) ;
- if (desc_cr != NULL)
- uty_cli_describe_fold (vio, cmd_width, desc_width, desc_cr);
+ default:
+ zabort("unknown item type") ;
+ } ;
} ;
/*------------------------------------------------------------------------------
- * Show one word and the description, folding the description as required.
+ * Command Description
*/
static void
-uty_cli_describe_fold (vty_io vio, int cmd_width,
- unsigned int desc_width, struct desc *desc)
+uty_cli_describe_command (vty_cli cli, node_type_t node)
{
- char *buf;
- const char *cmd, *p;
- int pos;
+ uint n_items ;
VTY_ASSERT_LOCKED() ;
- cmd = desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd;
- p = desc->str ;
+ /* Establish what items may be present at the current token position. */
+ n_items = uty_cli_help_parse(cli, node) ;
- /* If have a sensible description width */
- if (desc_width > 20)
- {
- buf = XCALLOC (MTYPE_TMP, strlen (desc->str) + 1);
+ if (n_items > 0) /* render list of possibilities */
+ uty_cli_describe_list(cli, cli->parsed->item_v) ;
+} ;
- while (strlen (p) > desc_width)
- {
- /* move back to first space */
- for (pos = desc_width; pos > 0; pos--)
- if (*(p + pos) == ' ')
- break;
+/*------------------------------------------------------------------------------
+ * Parse for command completion and command description.
+ *
+ * Requires that cmd_token_position() has been called to tokenise the line and
+ * establish which token the cursor is in. Must NOT call this if the cursor
+ * is in a "special" place.
+ *
+ * Deal with all cases which yield no items at all.
+ *
+ * Returns: number of items to consider.
+ */
+static uint
+uty_cli_help_parse(vty_cli cli, node_type_t node)
+{
+ const char* msg ;
+ cmd_return_code_t ret ;
+ uint n_items ;
- /* if did not find a space, break at width */
- if (pos == 0)
- pos = desc_width ;
+ /* The preflight checks avoid getting into trouble doing command completion
+ * on a line with comment
+ */
+ msg = cmd_help_preflight(cli->parsed) ;
+ if (msg != NULL)
+ {
+ uty_cli_out_message(cli, msg) ;
+ return 0 ;
+ } ;
- strncpy (buf, p, pos);
- buf[pos] = '\0';
- uty_cli_describe_line(vio, cmd_width, cmd, buf) ;
+ /* Now see what the cmd_completion can come up with. */
+ ret = cmd_completion(cli->parsed, node) ;
- cmd = ""; /* for 2nd and subsequent lines */
+ if (ret == CMD_ERR_PARSING)
+ {
+ uint eloc = cli->prompt_len + cli->parsed->eloc ;
- p += pos ; /* step past what just wrote */
- while (*p == ' ')
- ++p ; /* skip spaces */
- } ;
+ uty_cli_out_newline(cli) ; /* clears cli_drawn */
+ uty_cli_write_n(cli, telnet_dots, eloc) ;
+ uty_cli_write_s(cli, "^") ;
- XFREE (MTYPE_TMP, buf);
+ uty_cli_out_message(cli, cli->parsed->emess) ;
+
+ return 0 ;
} ;
- uty_cli_describe_line(vio, cmd_width, cmd, p) ;
-} ;
+ /* Will now have 0, 1 or more items which match at the current
+ * cursor token.
+ */
+ n_items = vector_length(cli->parsed->item_v) ;
-/*------------------------------------------------------------------------------
- * Show one description line.
- */
-static void
-uty_cli_describe_line(vty_io vio, int cmd_width, const char* cmd,
- const char* str)
-{
- if (str != NULL)
- uty_cli_out (vio, " %-*s %s", cmd_width, cmd, str) ;
- else
- uty_cli_out (vio, " %-s", cmd) ;
- uty_cli_out_newline(vio) ;
+ if (n_items == 0)
+ uty_cli_out_message(cli, "command not recognised") ;
+
+ return n_items ;
} ;
-/*------------------------------------------------------------------------------
- * Prepare "vline" token array for command handler.
- *
- * For "help" (command completion/description), if the command line is empty,
- * or ends in ' ', adds an empty token to the end of the token array.
- */
-static vector
-uty_cli_cmd_prepare(vty_io vio, int help)
-{
- vector vline ;
- vline = cmd_make_strvec(qs_term(&vio->cl)) ;
- /* Note that if there is a vector of tokens, then there is at least one
- * token, so can guarantee that vio->cl.len >= 1 !
- */
- if (help)
- if ((vline == NULL) || isspace(*qs_chars_at(&vio->cl, vio->cl.len - 1)))
- vline = cmd_add_to_strvec(vline, "") ;
- return vline ;
-} ;
-/*==============================================================================
- * VTY telnet stuff
- */
-
-#define TELNET_OPTION_DEBUG 0 /* 0 to turn off */
-
-static const char* telnet_commands[256] =
-{
- [tn_IAC ] = "IAC",
- [tn_DONT ] = "DONT",
- [tn_DO ] = "DO",
- [tn_WONT ] = "WONT",
- [tn_WILL ] = "WILL",
- [tn_SB ] = "SB",
- [tn_GA ] = "GA",
- [tn_EL ] = "EL",
- [tn_EC ] = "EC",
- [tn_AYT ] = "AYT",
- [tn_AO ] = "AO",
- [tn_IP ] = "IP",
- [tn_BREAK] = "BREAK",
- [tn_DM ] = "DM",
- [tn_NOP ] = "NOP",
- [tn_SE ] = "SE",
- [tn_EOR ] = "EOR",
- [tn_ABORT] = "ABORT",
- [tn_SUSP ] = "SUSP",
- [tn_EOF ] = "EOF",
-} ;
-
-static const char* telnet_options[256] =
-{
- [to_BINARY] = "BINARY", /* 8-bit data path */
- [to_ECHO] = "ECHO", /* echo */
- [to_RCP] = "RCP", /* prepare to reconnect */
- [to_SGA] = "SGA", /* suppress go ahead */
- [to_NAMS] = "NAMS", /* approximate message size */
- [to_STATUS] = "STATUS", /* give status */
- [to_TM] = "TM", /* timing mark */
- [to_RCTE] = "RCTE", /* remote controlled tx and echo */
- [to_NAOL] = "NAOL", /* neg. about output line width */
- [to_NAOP] = "NAOP", /* neg. about output page size */
- [to_NAOCRD] = "NAOCRD", /* neg. about CR disposition */
- [to_NAOHTS] = "NAOHTS", /* neg. about horizontal tabstops */
- [to_NAOHTD] = "NAOHTD", /* neg. about horizontal tab disp. */
- [to_NAOFFD] = "NAOFFD", /* neg. about formfeed disposition */
- [to_NAOVTS] = "NAOVTS", /* neg. about vertical tab stops */
- [to_NAOVTD] = "NAOVTD", /* neg. about vertical tab disp. */
- [to_NAOLFD] = "NAOLFD", /* neg. about output LF disposition */
- [to_XASCII] = "XASCII", /* extended ascii character set */
- [to_LOGOUT] = "LOGOUT", /* force logout */
- [to_BM] = "BM", /* byte macro */
- [to_DET] = "DET", /* data entry terminal */
- [to_SUPDUP] = "SUPDUP", /* supdup protocol */
- [to_SUPDUPOUTPUT] = "SUPDUPOUTPUT",/* supdup output */
- [to_SNDLOC] = "SNDLOC", /* send location */
- [to_TTYPE] = "TTYPE", /* terminal type */
- [to_EOR] = "EOR", /* end or record */
- [to_TUID] = "TUID", /* TACACS user identification */
- [to_OUTMRK] = "OUTMRK", /* output marking */
- [to_TTYLOC] = "TTYLOC", /* terminal location number */
- [to_3270REGIME] = "3270REGIME", /* 3270 regime */
- [to_X3PAD] = "X3PAD", /* X.3 PAD */
- [to_NAWS] = "NAWS", /* window size */
- [to_TSPEED] = "TSPEED", /* terminal speed */
- [to_LFLOW] = "LFLOW", /* remote flow control */
- [to_LINEMODE] = "LINEMODE", /* Linemode option */
- [to_XDISPLOC] = "XDISPLOC", /* X Display Location */
- [to_OLD_ENVIRON] = "OLD_ENVIRON", /* Old - Environment variables */
- [to_AUTHENTICATION] = "AUTHENTICATION", /* Authenticate */
- [to_ENCRYPT] = "ENCRYPT", /* Encryption option */
- [to_NEW_ENVIRON] = "NEW_ENVIRON", /* New - Environment variables */
- [to_EXOPL] = "EXOPL", /* extended-options-list */
-} ;
-
-/*------------------------------------------------------------------------------
- * For debug. Put string or value as decimal.
- */
+
+
+
static void
-uty_cli_out_dec(vty_io vio, const char* str, unsigned char u)
+uty_cli_out_message(vty_cli cli, const char* msg)
{
- if (str != NULL)
- uty_cli_out(vio, "%s ", str) ;
- else
- uty_cli_out(vio, "%d ", (int)u) ;
+ uty_cli_out_newline(cli) ; /* clears cli_drawn */
+ uty_cli_write_s(cli, "% ") ;
+ uty_cli_write_s(cli, msg) ;
+ uty_cli_out_newline(cli) ;
} ;
-/*------------------------------------------------------------------------------
- * For debug. Put string or value as hex.
- */
+
+
static void
-uty_cli_out_hex(vty_io vio, const char* str, unsigned char u)
+uty_cli_complete_keyword(vty_cli cli, const char* keyword)
{
- if (str != NULL)
- uty_cli_out(vio, "%s ", str) ;
- else
- uty_cli_out(vio, "0x%02x ", (unsigned)u) ;
+ int pre, rep, ins, mov ;
+
+ cmd_complete_keyword(cli->parsed, &pre, &rep, &ins, &mov) ;
+
+ uty_cli_move(cli, pre) ; /* move to start of token */
+ uty_cli_replace(cli, rep, keyword, strlen(keyword)) ;
+
+ if (ins > 0)
+ uty_cli_insert(cli, " ", ins) ;
+
+ uty_cli_move(cli, mov) ;
+
+ return ;
} ;
/*------------------------------------------------------------------------------
- * Send telnet: "WILL TELOPT_ECHO"
+ * Show the command completions -- usually more than one.
+ *
+ * Generates a table of possible items, with fixed width entries, depending
+ * on the longest option.
+ *
+ * NB: the items will have been sorted before we get here. Inter alia, that
+ * ensures that any <cr> is shown last.
*/
static void
-uty_will_echo (vty_io vio)
+uty_cli_complete_list(vty_cli cli, vector item_v)
{
- unsigned char cmd[] = { tn_IAC, tn_WILL, to_ECHO };
- VTY_ASSERT_LOCKED() ;
- uty_cli_write (vio, (char*)cmd, (int)sizeof(cmd));
-}
+ uint i, len, n ;
-/*------------------------------------------------------------------------------
- * Send telnet: "suppress Go-Ahead"
- */
-static void
-uty_will_suppress_go_ahead (vty_io vio)
-{
- unsigned char cmd[] = { tn_IAC, tn_WILL, to_SGA };
- VTY_ASSERT_LOCKED() ;
- uty_cli_write (vio, (char*)cmd, (int)sizeof(cmd));
-}
+ len = 6 ;
+ for (i = 0 ; i < vector_length(item_v) ; ++i)
+ {
+ cmd_item item ;
+ uint sl ;
-/*------------------------------------------------------------------------------
- * Send telnet: "don't use linemode"
- */
-static void
-uty_dont_linemode (vty_io vio)
-{
- unsigned char cmd[] = { tn_IAC, tn_DONT, to_LINEMODE };
- VTY_ASSERT_LOCKED() ;
- uty_cli_write (vio, (char*)cmd, (int)sizeof(cmd));
-}
+ item = vector_get_item(item_v, i) ;
-/*------------------------------------------------------------------------------
- * Send telnet: "Use window size"
- */
-static void
-uty_do_window_size (vty_io vio)
-{
- unsigned char cmd[] = { tn_IAC, tn_DO, to_NAWS };
- VTY_ASSERT_LOCKED() ;
- uty_cli_write (vio, (char*)cmd, (int)sizeof(cmd));
-}
+ sl = strlen(item->str) ;
+ if (len < sl)
+ len = sl ;
+ } ;
-/*------------------------------------------------------------------------------
- * Send telnet: "don't use lflow" -- not currently used
- */
-static void
-uty_dont_lflow_ahead (vty_io vio)
-{
- unsigned char cmd[] = { tn_IAC, tn_DONT, to_LFLOW };
- VTY_ASSERT_LOCKED() ;
- uty_cli_write (vio, (char*)cmd, (int)sizeof(cmd));
-}
+ n = uty_cli_width_to_use(cli) / (len + 2) ;
-/*------------------------------------------------------------------------------
- * The keystroke iac callback function.
- *
- * This deals with IAC sequences that should be dealt with as soon as they
- * are read -- not stored in the keystroke stream for later processing.
- */
-extern bool
-uty_cli_iac_callback(keystroke_iac_callback_args)
-{
- return uty_telnet_command((vty_io)context, stroke, true) ;
+ if (n == 0)
+ n = 1 ;
+
+ for (i = 0 ; i < vector_length(item_v) ; ++i)
+ {
+ cmd_item item ;
+ item = vector_get_item(item_v, i) ;
+
+ if ((i % n) == 0)
+ uty_cli_out_newline(cli) ; /* clears cli_drawn */
+
+ uty_cli_out(cli, "%-*s ", len, item->str) ;
+ }
+ uty_cli_out_newline(cli) ;
} ;
/*------------------------------------------------------------------------------
- * Process incoming Telnet Option(s)
+ * Show the command description.
+ *
+ * Generates lines of the form:
*
- * May be called during keystroke iac callback, or when processing CLI
- * keystrokes.
+ * word description text
*
- * In particular: get telnet window size.
+ * Where the word field is adjusted to suit the longest word, and the
+ * description text is wrapped if required (if the width of the console is
+ * known) so that get:
*
- * Returns: true <=> dealt with, for:
+ * word description ..................................
+ * .............text
*
- * * telnet window size.
+ * NB: the items will have been sorted before we get here. Inter alia, that
+ * ensures that any <cr> is shown last.
*/
-static bool
-uty_telnet_command(vty_io vio, keystroke stroke, bool callback)
+static void
+uty_cli_describe_list(vty_cli cli, vector item_v)
{
- uint8_t* p ;
- uint8_t o ;
- int left ;
- bool dealt_with ;
+ uint i, str_width, doc_width, width ;
- /* Echo to the other end if required */
- if (TELNET_OPTION_DEBUG)
+ /* Get width of the longest "word" */
+ str_width = 0;
+ for (i = 0 ; i < vector_length(item_v) ; ++i)
{
- uty_cli_wipe(vio, 0) ;
+ cmd_item item ;
+ uint len ;
+
+ item = vector_get_item(item_v, i) ;
- p = stroke->buf ;
- left = stroke->len ;
+ len = strlen(item->str) ;
+ if (item->str[0] == '.')
+ len--;
- uty_cli_out_hex(vio, telnet_commands[tn_IAC], tn_IAC) ;
+ if (len > str_width)
+ str_width = len ;
+ } ;
- if (left-- > 0)
- uty_cli_out_dec(vio, telnet_commands[*p], *p) ;
- ++p ;
+ /* Set width of description string.
+ *
+ * Format is:
+ *
+ * __wo.....rd__description...
+ * ...continues
+ *
+ * The width of the word part has been established above as the width of the
+ * widest word.
+ *
+ * There are two spaces on either side of the word, so we here calculate the
+ * width of the description part
+ */
+ width = uty_cli_width_to_use(cli) ;
- if (left-- > 0)
- uty_cli_out_dec(vio, telnet_options[*p], *p) ;
- ++p ;
+ if (width > ((str_width + 6) + 20))
+ doc_width = width - (str_width + 6) ;
+ else
+ doc_width = 0 ;
- if (left > 0)
- {
- while(left-- > 0)
- uty_cli_out_hex(vio, NULL, *p++) ;
+ /* Print out description. */
+ for (i = 0 ; i < vector_length(item_v) ; ++i)
+ {
+ cmd_item item ;
+ const char* str, * dp, * ep ;
+
+ item = vector_get_item(item_v, i) ;
- if (stroke->flags & kf_truncated)
- uty_cli_out(vio, "... ") ;
+ str = item->str[0] == '.' ? item->str + 1 : item->str;
+ dp = item->doc ;
+ ep = dp + strlen(dp) ;
- if (!(stroke->flags & kf_broken))
+ /* If have a sensible description width */
+ if (doc_width > 20)
+ {
+ while ((ep - dp) > doc_width)
{
- uty_cli_out_hex(vio, telnet_commands[tn_IAC], tn_IAC) ;
- uty_cli_out_hex(vio, telnet_commands[tn_SE], tn_SE) ;
- }
- } ;
+ const char* np ;
- if (stroke->flags & kf_broken)
- uty_cli_out (vio, "BROKEN") ;
+ np = dp + doc_width ; /* target next position */
- uty_cli_out (vio, "\r\n") ;
- } ;
+ while ((np > dp) && (*np != ' '))
+ --np ; /* seek back to ' ' */
- /* Process the telnet command */
- dealt_with = false ;
+ if (np == dp) /* if no space... */
+ np = dp + doc_width ; /* ...force break */
- if (stroke->flags != 0)
- return dealt_with ; /* go no further if broken */
+ uty_cli_describe_line(cli, str_width, str, dp, np - dp) ;
- p = stroke->buf ;
- left = stroke->len ;
+ str = ""; /* for 2nd and subsequent lines */
- passert(left >= 1) ; /* must be if not broken ! */
- passert(stroke->value == *p) ; /* or something is wrong */
+ dp = np ; /* step past what just wrote */
+ while (*dp == ' ')
+ ++dp ; /* skip spaces */
+ } ;
+ } ;
- ++p ; /* step past X of IAC X */
- --left ;
+ uty_cli_describe_line(cli, str_width, str, dp, ep - dp) ;
+ } ;
- /* Decode the one command that is interesting -- "NAWS" */
- switch (stroke->value)
- {
- case tn_SB:
- passert(left > 0) ; /* or parser failed */
+ uty_cli_out_newline(cli) ;
+} ;
- o = *p++ ; /* the option byte */
- --left ;
- switch(o)
- {
- case to_NAWS:
- if (left != 4)
- {
- uzlog(NULL, LOG_WARNING,
- "RFC 1073 violation detected: telnet NAWS option "
- "should send %d characters, but we received %d",
- (3 + 4 + 2), (3 + left + 2)) ;
- }
- else
- {
- vio->width = *p++ << 8 ;
- vio->width += *p++ ;
- vio->height = *p++ << 8 ;
- vio->height += *p ;
-
- if (TELNET_OPTION_DEBUG)
- uty_cli_out(vio, "TELNET NAWS window size received: "
- "width %d, height %d%s",
- vio->width, vio->height, telnet_newline) ;
- uty_set_height(vio) ;
-
- dealt_with = true ;
- } ;
- break ;
+/*------------------------------------------------------------------------------
+ * Show one description line.
+ */
+static void
+uty_cli_describe_line(vty_cli cli, uint str_width, const char* str,
+ const char* doc, uint len)
+{
+ if ((*str == '\0') && (len == 0))
+ return ; /* quit if nothing to say */
- default: /* no other IAC SB <option> */
- break ;
- } ;
- break ;
+ uty_cli_out_newline(cli) ;
- default: /* no other IAC X */
- break ;
- } ;
+ if (len == 0)
+ uty_cli_out(cli, " %s", str) ; /* left justify */
+ else
+ {
+ uty_cli_out(cli, " %-*s ", str_width, str) ;
+ uty_cli_write(cli, doc, len) ;
+ } ;
+} ;
- return dealt_with ;
+/*------------------------------------------------------------------------------
+ * Return the actual or assumed console width.
+ *
+ * If we know the width we use it. Otherwise just assume something reasonable.
+ */
+static uint
+uty_cli_width_to_use(vty_cli cli)
+{
+ return (cli->width == 0) ? 60 : cli->width ;
} ;
diff --git a/lib/vty_cli.h b/lib/vty_cli.h
index f532616a..94e502f0 100644
--- a/lib/vty_cli.h
+++ b/lib/vty_cli.h
@@ -24,26 +24,166 @@
#ifndef _ZEBRA_VTY_CLI_H
#define _ZEBRA_VTY_CLI_H
+#include "misc.h"
+#include "vargs.h"
+
+#include "command_local.h"
#include "vty_io.h"
+#include "vty_io_basic.h"
+#include "vio_fifo.h"
+#include "vio_lines.h"
+#include "qstring.h"
#include "keystroke.h"
-extern void uty_cli_init(vty_io vio) ;
-extern enum vty_readiness uty_cli_start(vty_io vio) ;
-extern void uty_cli_close(vty_io vio) ;
+/*------------------------------------------------------------------------------
+ * The vty_cli structure pointed to by the vty_io structure.
+ */
+typedef struct vty_cli* vty_cli ;
+
+struct vty_cli
+{
+ vio_vf vf ; /* parent */
+
+ /* History of commands */
+ vector_t hist ; /* embedded */
+ int hp ; /* current place in history */
+ int h_now ; /* the present moment */
+
+ /* Window width/height as reported by Telnet. 0 => unknown */
+ int width;
+ int height;
+
+ /* Configure lines. */
+ int lines;
+ bool lines_set ; /* true <=> explicitly set */
+
+ /* Terminal monitor. */
+ bool monitor ;
+ bool monitor_busy ;
+
+ /* Terminal timeout in seconds -- 0 => none */
+ vty_timer_time v_timeout ;
+
+ /* The incoming stuff */
+ keystroke_stream key_stream ;
+
+ /* drawn <=> the current prompt and user input occupy the current
+ * line on the screen.
+ *
+ * dirty <=> the last command output did not end with a newline.
+ *
+ * If drawn is true, the following are valid:
+ *
+ * prompt_len -- the length of the prompt part.
+ * (will be the "--more--" prompt in cli_more_wait)
+ *
+ * extra_len -- the length of any ^X at the cursor position
+ * (for when blocked waiting for queued command)
+ *
+ * echo_suppress -- the user part of the command line is suppressed
+ *
+ * NB: echo_suppress is only used for password entry.
+ */
+ bool drawn ;
+ bool dirty ;
+
+ int prompt_len ;
+ int extra_len ;
+
+ bool echo_suppress ;
+
+ /* "cache" for prompt -- when node or host name changes, prompt does */
+ node_type_t prompt_node ;
+ name_gen_t prompt_gen ;
+ qstring_t prompt_for_node ;
+
+ /* password failure count -- main login or enable login. */
+ int password_fail ;
+
+ /* State of the CLI
+ *
+ * in_progress -- command dispatched
+ * blocked -- blocked until current command completes
+ * out_active -- contents of the command FIFO are being written away
+ *
+ * more_wait -- is in "--more--" wait state
+ * more_active -- more_wait and waiting for "--more--" prompt to be
+ * written away.
+ */
+ bool in_progress ;
+ bool blocked ;
+ bool out_active ;
+
+ bool more_wait ;
+ bool more_active ;
+
+ /* This is used to control command output, so that each write_ready event
+ * generates at most one tranche of output.
+ */
+ bool out_done ;
+
+ /* This is set only if the "--more--" handling is enabled */
+ bool more_enabled ;
+
+ /* Command Line(s)
+ *
+ * node -- the node that the CLI is in. This may be some way behind
+ * the VTY, but is updated when the CLI level command completes.
+ *
+ * to_do -- when current command being prepared is completed (by
+ * CR/LF or otherwise) this says what there now is to be done.
+ *
+ * cl -- current command line being prepared.
+ *
+ * clx -- current command line being executed.
+ *
+ * NB: during command execution vty->buf is set to point at the '\0'
+ * terminated current command line being executed.
+ */
+ node_type_t node ;
+
+ cmd_do_t to_do ;
+
+ qstring cl ;
+ qstring clx ;
+
+ cmd_parsed_t parsed ; /* embedded */
+
+ /* CLI line buffering */
+ vio_fifo_t cbuf ; /* embedded */
+
+ /* CLI line control for command output & "--more--" stuff */
+ vio_line_control_t olc ; /* embedded */
+} ;
+
+extern vty_cli uty_cli_new(vio_vf vf) ;
+extern void uty_cli_start(vty_cli cli, node_type_t node) ;
+
+extern vty_cli uty_cli_close(vty_cli cli, bool final) ;
+
+extern cmd_return_code_t uty_cli_auth(vty_cli) ;
+extern void uty_cli_hist_show(vty_cli cli) ;
+extern ulen uty_cli_prompt_len(vty_cli cli) ;
-extern enum vty_readiness uty_cli(vty_io vio) ;
-extern keystroke_callback uty_cli_iac_callback ;
+extern vty_readiness_t uty_cli(vty_cli cli) ;
+extern void uty_cli_out_push(vty_cli cli) ;
+extern void uty_cli_done_command(vty_cli cli, node_type_t node) ;
-extern void uty_cli_hist_add (vty_io vio, const char* cmd_line) ;
-extern void uty_cli_enter_more_wait(vty_io vio) ;
-extern void uty_cli_exit_more_wait(vty_io vio) ;
+extern void uty_cli_out(vty_cli cli, const char *format, ...)
+ PRINTF_ATTRIBUTE(2, 3) ;
+extern void uty_cli_out_newline(vty_cli cli) ;
+extern void uty_cli_out_clear(vty_cli cli) ;
+extern void uty_cli_write(vty_cli cli, const char *this, int len) ;
+extern void uty_cli_wipe(vty_cli cli, int len) ;
-extern void uty_free_host_name(void) ;
-extern void uty_check_host_name(void) ;
+extern void uty_cli_set_lines(vty_cli cli, int lines, bool explicit) ;
+extern void uty_cli_set_window(vty_cli cli, int width, int height) ;
+extern void uty_cli_enter_more_wait(vty_cli cli) ;
+extern void uty_cli_exit_more_wait(vty_cli cli) ;
-extern bool uty_cli_draw_if_required(vty_io vio) ;
+extern bool uty_cli_draw_if_required(vty_cli cli) ;
-extern void uty_cli_pre_monitor(vty_io vio, size_t len) ;
-extern int uty_cli_post_monitor(vty_io vio, const char* buf, size_t len) ;
+extern void uty_cli_pre_monitor(vty_cli cli, size_t len) ;
+extern int uty_cli_post_monitor(vty_cli cli, const char* buf, size_t len) ;
#endif /* _ZEBRA_VTY_CLI_H */
diff --git a/lib/vty_io.c b/lib/vty_io.c
index c116e6b9..4b50c526 100644
--- a/lib/vty_io.c
+++ b/lib/vty_io.c
@@ -24,10 +24,14 @@
#include "vty.h"
#include "vty_io.h"
#include "vty_io_term.h"
+#include "vty_io_file.h"
#include "vty_cli.h"
+#include "vty_command.h"
#include "qstring.h"
#include "keystroke.h"
#include "list_util.h"
+#include "command_parse.h"
+#include "command_execute.h"
#include "memory.h"
@@ -45,20 +49,18 @@
/*==============================================================================
* Basic output to VTY.
- *
- *
*/
/*------------------------------------------------------------------------------
- * UTY output function -- cf fprintf
+ * VTY output -- cf fprintf ! Same as vty_out, less the VTY_LOCK().
*
- * NB: this is NOT suppressed by ! vty->output_enabled
+ * This is for command output, which may later be suppressed
*
* Returns: >= 0 => OK
* < 0 => failed (see errno)
*/
extern int
-uty_output(struct vty *vty, const char *format, ...)
+uty_out(vty_io vio, const char *format, ...)
{
int ret ;
va_list args ;
@@ -66,122 +68,10 @@ uty_output(struct vty *vty, const char *format, ...)
VTY_ASSERT_LOCKED() ;
va_start (args, format) ;
- ret = uty_vprintf(vty, format, args) ;
+ ret = uty_vprintf(vio, format, args) ;
va_end (args) ;
return ret ;
-}
-
-/*------------------------------------------------------------------------------
- * UTY reflect command line, if not already reflected
- *
- * NB: this is NOT suppressed by ! vty->output_enabled
- *
- * Returns: >= 0 => OK
- * < 0 => failed (see errno)
- */
-extern int
-uty_reflect(struct vty *vty)
-{
- int ret ;
-
- if (!vty->reflected)
- ret = uty_output(vty, "%s\n", vty->buf) ;
- else
- ret = 0 ;
-
- vty->reflected = true ;
-
- return ret ;
-} ;
-
-/*------------------------------------------------------------------------------
- * VTY output function -- cf vfprintf
- *
- * Returns: >= 0 => OK
- * < 0 => failed (see errno)
- *
- * NB: for VTY_TERM and for VTY_SHELL_SERV -- this is command output:
- *
- * * MAY NOT do any command output if !cmd_enabled
- *
- * * first, the life of a vty is not guaranteed unless cmd_in_progress,
- * so should not attempt to use a vty anywhere other than command
- * execution.
- *
- * * second, cmd_out_enabled is false most of the time, and is only
- * set true when a command completes, and it is time to write away
- * the results.
- *
- * * all output is placed in the vio->cmd_obuf. When the command completes,
- * the contents of the cmd_obuf will be written away -- subject to line
- * control.
- *
- * * output is discarded if the vty is no longer write_open
- */
-extern int
-uty_vprintf(struct vty *vty, const char *format, va_list args)
-{
- vio_vf vf ;
- int ret ;
-
- VTY_ASSERT_LOCKED() ;
-
- vf = vty->vio->vout ;
-
- switch (vf->vout_type)
- {
- case VOUT_NONE:
- ret = 0 ;
- break ;
-
- case VOUT_TERM:
- ret = uty_term_vprintf(vf, format, args) ;
- break ;
-
- case VOUT_SHELL:
- ret = uty_shell_vprintf(vf, format, args) ;
- break ;
-
- case VOUT_FILE:
- ret = uty_file_vprintf(vf, format, args) ;
- break ;
-
- case VOUT_PIPE:
- ret = uty_pipe_vprintf(vf, format, args) ;
- break ;
-
- case VOUT_STDOUT:
- ret = uty_stdout_vprintf(vf, format, args) ;
- break ;
-
- case VOUT_STDERR:
- ret = uty_stderr_vprintf(vf, format, args) ;
- break ;
-
- default:
- zabort("impossible VTY Output type") ;
- } ;
-
- return ret ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Clear the contents of the command output FIFO etc.
- *
- * NB: does not change any of the cli_blocked/cmd_in_progress/cli_wait_more/etc
- * flags -- competent parties must deal with those
- */
-extern void
-uty_out_clear(vty_io vio)
-{
- VTY_ASSERT_LOCKED() ;
-
- if (vio->vout != NULL)
- {
- vio_fifo_clear(vio->vout->obuf) ;
- vio_lc_clear(vio->vout->olc) ;
- } ;
} ;
/*==============================================================================
@@ -238,7 +128,7 @@ uty_watch_dog_stop(void)
static vty_timer_time
uty_watch_dog_bark(vio_timer_t* timer, void* info)
{
- uty_check_host_name() ; /* check for host name change */
+ cmd_host_name(true) ; /* check for host name change */
uty_death_watch_scan(false) ; /* scan the death-watch list */
@@ -248,11 +138,12 @@ uty_watch_dog_bark(vio_timer_t* timer, void* info)
/*------------------------------------------------------------------------------
* Scan the death watch list.
*
- * A vty may finally be freed if it is closed and there is no command in
- * progress.
+ * A vty can finally be freed if it is closed and there is no command running.
+ *
+ * At curtains the command running state is forced off.
*/
static bool
-uty_death_watch_scan(bool final)
+uty_death_watch_scan(bool curtains)
{
vty_io vio ;
vty_io next ;
@@ -263,21 +154,19 @@ uty_death_watch_scan(bool final)
vio = next ;
next = sdl_next(vio, vio_list) ;
- if (final && !vio->closed)
-
-
- if (vio->closed)
+ /* If this is curtains, override cmd_running ! */
+ if (curtains)
+ vio->cmd_running = false ;
-
- if (vio->closed && !vio->cmd_in_progress)
+ if (uty_close(vio, curtains, NULL))
{
- uty_close(vio) ; /* closes again to ensure that all buffers
- are released. */
+ vty vty = vio->vty ;
sdl_del(vio_death_watch, vio, vio_list) ;
- XFREE(MTYPE_VTY, vio->vty) ;
- XFREE(MTYPE_VTY, vio) ;
+ cmd_exec_free(vty->exec) ;
+ XFREE(MTYPE_VTY, vty->vio) ;
+ XFREE(MTYPE_VTY, vty) ;
} ;
} ;
@@ -288,189 +177,128 @@ uty_death_watch_scan(bool final)
* Prototypes.
*/
-static void uty_vf_half_close(vio_vf vf) ;
-static void uty_vf_close(vio_vf vf) ;
+static void uty_vf_read_close(vio_vf vf) ;
+static bool uty_vf_write_close(vio_vf vf, bool final) ;
+static vio_vf uty_vf_free(vio_vf vf) ;
/*==============================================================================
* Creation and destruction of VTY objects
*/
/*------------------------------------------------------------------------------
- * Allocate new vty struct
- *
- * VTY_TERMINAL a telnet terminal server
- *
- * Must be in cli thread.
- *
- * Requires fd = socket for telnet terminal.
- *
- * VTY_SHELL_SERVER a vty_shell server
- *
- * Must be in cli thread.
+ * Allocate new vty structure, including empty vty_io and empty execution
+ * structures.
*
- * Requires fd = socket for talking to the VTY_SHELL_CLIENT
+ * Caller must complete the initialisation of the vty_io, which means:
*
- * VTY_SHELL_CLIENT a vty_shell client
+ * * constructing a suitable vio_vf and doing uty_vin_open() to set the
+ * vin_base.
*
- * VTY_CONFIG_READ configuration file reader
+ * All vty_io MUST have a vin_base, even if it is /dev/null.
*
- * Requires fd = file descriptor for reading config file
+ * * constructing a suitable vio_vf and doing uty_vout_open() to set the
+ * vout_base.
*
- * VTY_STDOUT stdout
- * VTY_STDERR stderr
+ * All vty_io MUST have a vout_base, even if it is /dev/null.
*
+ * * setting the vio->obuf to the vout_base obuf... TODO ???
*
+ * * setting vio->cli, if required.
*
- *
- *
- *
- *
- *
- * Allocates and initialises basic vty and vty_io structures, setting the
- * given type.
- *
- * Note that where is not setting up a vty_file, this *may* be called from
- * any thread.
- *
- * Returns: new vty
+ * Caller must also set the initial vty->node, if any.
*/
-extern struct vty *
-uty_new(enum vty_type type, int fd)
+extern vty
+uty_new(vty_type_t type)
{
vty vty ;
vty_io vio ;
+ bool execution ;
+
VTY_ASSERT_LOCKED() ;
/* Basic allocation */
- vty = XCALLOC (MTYPE_VTY, sizeof (struct vty)) ;
- vio = XCALLOC (MTYPE_VTY, sizeof (struct vty_io)) ;
-
- vty->vio = vio ;
- vio->vty = vty ;
-
- /* Zeroising the vty_io structure has set:
- *
- * name = NULL -- no name, yet
- *
- * vio_list both pointers NULL
- * mon_list both pointers NULL
- *
- * half_closed = 0 -- NOT half closed (important !)
- * closed = 0 -- NOT closed (important !)
- * close_reason = NULL -- no reason, yet
+ vty = XCALLOC(MTYPE_VTY, sizeof(struct vty)) ;
+ /* Zeroising the vty structure has set:
*
- * real_type = 0 -- not material
- * file_fd = 0 -- not material
- * file_error = 0 -- not material
+ * type = X -- set to actual type, below
*
- * key_stream = NULL -- no key stream (always empty, at EOF)
+ * node = NULL_NODE -- set to something sensible elsewhere
*
- * cli_drawn = 0 -- not drawn
- * cli_dirty = 0 -- not dirty
- * cli_prompt_len = 0 )
- * cli_extra_len = 0 ) not material
- * cli_echo_suppress = 0 )
+ * index = NULL -- nothing, yet
+ * index_sub = NULL -- nothing, yet
*
- * cli_prompt_node = 0 -- not material
- * cli_prompt_set = 0 -- so prompt needs to be constructed
+ * config = false -- not in configure mode
*
- * cli_blocked = 0 -- not blocked
- * cmd_in_progress = 0 -- no command in progress
- * cmd_out_enabled = 0 -- command output is disabled
- * cli_wait_more = 0 -- not waiting for response to "--more--"
+ * execution = X -- set below
+ * vio = X -- set below
+ */
+ confirm(NULL_NODE == 0) ;
+ confirm(QSTRING_INIT_ALL_ZEROS) ;
+
+ vty->type = type ;
+
+ vio = XCALLOC(MTYPE_VTY, sizeof(struct vty_io)) ;
+
+ /* Zeroising the vty_io structure has set:
*
- * cli_more_enabled = 0 -- not enabled for "--more--"
+ * vty = X -- set to point to parent vty, below
*
- * cmd_out_done = 0 -- not material
+ * name = NULL -- no name, yet TODO ???
*
- * cli_do = 0 == cli_do_nothing
+ * vin = NULL -- empty input stack
+ * vin_base = NULL -- empty input stack
+ * vin_depth = 0 -- no stacked vin's, yet
*
- * cmd_lc = NULL -- no line control
+ * vout = NULL -- empty output stack
+ * vout_base = NULL -- empty output stack
+ * vout_depth = 0 -- no stacked vout's, yet
*
- * fail = 0 -- no login failures yet
+ * vout_closing = NULL -- nothing closing yet
*
- * hist = empty vector
- * hp = 0 -- at the beginning
- * hindex = 0 -- the beginning
+ * vio_list = NULLs -- not on the vio_list, yet
+ * mon_list = NULLs -- not on the monitors list
*
- * width = 0 -- unknown console width
- * height = 0 -- unknown console height
+ * blocking = X -- set below: false unless VTY_CONFIG_READ
+ * cmd_running = false -- no commands running, yet
*
- * lines = 0 -- no limit
- * lines_set = 0 -- no explicit setting
+ * state = X -- set vf_open, below.
*
- * monitor = 0 -- not a monitor
- * monitor_busy = 0 -- not a busy monitor
+ * close_reason = NULL -- not closed, yet
*
- * config = 0 -- not holder of "config" mode
+ * obuf = NULL -- no output buffer, yet
*/
- confirm(cli_do_nothing == 0) ;
- confirm(AUTH_NODE == 0) ; /* default node type */
-
- vty->type = type ;
-
+ vty->vio = vio ;
+ vio->vty = vty ;
+ vio->blocking = (type == VTY_CONFIG_READ) ;
+ vio->state = vf_open ;
+ /* Create and initialise the command execution environment (if any) */
+ execution = true ;
- /* Zeroising the vty structure has set:
- *
- * node = 0 TODO: something better for node value ????
- * buf = NULL -- no command line, yet
- * parsed = NULL -- no parsed command, yet
- * lineno = 0 -- nothing read, yet
- * index = NULL -- nothing, yet
- * index_sub = NULL -- nothing, yet
- */
-
- /* If this is a VTY_TERM or a VTY_SHELL, place */
- switch (type)
+ switch(type)
{
- case VTY_TERMINAL: /* Require fd -- Telnet session */
- VTY_ASSERT_CLI_THREAD() ;
- assert(fd >= 0) ;
-
- uty_term_new(vio, fd) ;
- break ;
-
- case VTY_SHELL_SERVER: /* Require fd -- Unix socket */
- VTY_ASSERT_CLI_THREAD() ;
- assert(fd >= 0) ;
-
-
- break ;
-
- case VTY_CONFIG_READ: /* Require fd -- file to read */
- assert(fd >= 0) ;
+ case VTY_TERMINAL:
+ case VTY_SHELL_SERVER:
+ case VTY_SHELL_CLIENT:
+ case VTY_CONFIG_READ:
+ execution = true ;
break ;
case VTY_STDOUT:
case VTY_STDERR:
- case VTY_SHELL_CLIENT:
- fd = -1 ; /* No fd -- output to stdout/stderr */
+ execution = false ;
break ;
default:
- zabort("unknown VTY type") ;
+ zabort("unknown vty type") ;
} ;
-
-
-
- /* Make sure all buffers etc. are initialised clean and empty.
- *
- * Note that no buffers are actually allocated at this stage.
- */
- qs_init_new(&vio->cli_prompt_for_node, 0) ;
-
- qs_init_new(&vio->cl, 0) ;
- qs_init_new(&vio->clx, 0) ;
-
- vio_fifo_init_new(&vio->cli_obuf, 2 * 1024) ; /* allocate in 2K lumps */
-
- vio_fifo_init_new(&vio->cmd_obuf, 8 * 1024) ;
+ if (execution)
+ vty->exec = cmd_exec_new(vty) ;
/* Place on list of known vio/vty */
sdl_push(vio_list_base, vio, vio_list) ;
@@ -483,73 +311,127 @@ uty_new(enum vty_type type, int fd)
*
* Sets the vf->vin_type and set vf->read_open.
*
- * Sets the read ready action and the read timer timeout action.
+ * Initialises an input buffer if required, and sets line_complete and
+ * line_step so that first attempt to fetch a line will give line 1.
*
- * NB: may add a VIN_NONE *only* as the first vin item.
+ * Sets the read ready action and the read timer timeout action.
*
- * Can have a write only VTY_XXX object, which requires a vin entry so that
- * do not have to everywhere deal with NULL vio_vf pointers.
+ * NB: a uty_cmd_prepare() is required before command processing can continue.
*
- * But for subsequent write only vio_vf objects, must not add to the vin
- * stack.
+ * That is not done here because the vin state may not be complete (in
+ * particular the vin->parse_type and vin->reflect_enabled !).
*/
extern void
-uty_vin_add(vty_io vio, vio_vf vf, vio_in_type_t type,
- vio_fd_action* read_action, vio_timer_action* read_timer_action)
+uty_vin_open(vty_io vio, vio_vf vf, vio_in_type_t type,
+ vio_vfd_action* read_action,
+ vio_timer_action* read_timer_action,
+ usize ibuf_size)
{
vf->vin_type = type ;
- vf->read_open = true ;
+ vf->vin_state = vf_open ;
- vio_fd_set_read_action(vf->vfd, read_action) ;
- vio_fd_set_read_timeout_action(vf->vfd, read_timer_action) ;
+ if (type < VIN_SPECIALS)
+ {
+ vio_vfd_set_read_action(vf->vfd, read_action) ;
+ vio_vfd_set_read_timeout_action(vf->vfd, read_timer_action) ;
+ } ;
ssl_push(vio->vin, vf, vin_next) ;
if (vio->vin_base == NULL)
- vio->vin_base = vf ;
+ {
+ assert(vio->vin_depth == 0) ;
+ vio->vin_base = vf ;
+ }
else
- assert(type != VIN_NONE) ;
+ {
+ assert(type != VIN_NONE) ;
+ ++vio->vin_depth ;
+ } ;
+
+ if (ibuf_size != 0)
+ {
+ vf->ibuf = vio_fifo_init_new(NULL, ibuf_size) ;
+
+ vf->line_complete = true ;
+ vf->line_step = 1 ;
+ } ;
} ;
/*------------------------------------------------------------------------------
- * Add a new vf to the vio->vout stack, and set write stuff.
+ * Push a new vf to the vio->vout stack, and set write stuff.
*
* Sets the vf->vout_type and set vf->write_open.
*
* Sets the write ready action and the write timer timeout action.
*
- * NB: may add a VOUT_NONE *only* as the first vout item.
+ * Initialises an output buffer and sets an end_mark.
+ *
+ * NB: VOUT_DEV_NULL, VOUT_STDOUT and VOUT_STDERR are special.
+ *
+ * The write_action and the write_timer_action are ignored.
*
- * Can have a read only VTY_XXX object, which requires a vout entry so that
- * do not have to everywhere deal with NULL vio_vf pointers.
+ * All actual I/O to these outputs is direct, blocking and via standard
+ * I/O -- except VOUT_DEV_NULL where all I/O is discarded.
*
- * But for subsequent read only vio_vf objects, must not add to the vout
- * stack.
+ * NB: all outputs are set up with an obuf, so all output is collected, even
+ * if it is later to be discarded.
+ *
+ * NB: a uty_cmd_prepare() is required before command processing can continue.
+ *
+ * That is not done here because the vout state may not be complete (in
+ * particular the vout->out_enabled !).
*/
extern void
-uty_vout_add(vty_io vio, vio_vf vf, vio_out_type_t type,
- vio_fd_action* write_action, vio_timer_action* write_timer_action)
+uty_vout_open(vty_io vio, vio_vf vf, vio_out_type_t type,
+ vio_vfd_action* write_action,
+ vio_timer_action* write_timer_action,
+ usize obuf_size)
{
vf->vout_type = type ;
- vf->write_open = true ;
+ vf->vout_state = vf_open ;
- vio_fd_set_write_action(vf->vfd, write_action) ;
- vio_fd_set_write_timeout_action(vf->vfd, write_timer_action) ;
+ if (type < VOUT_SPECIALS)
+ {
+ vio_vfd_set_write_action(vf->vfd, write_action) ;
+ vio_vfd_set_write_timeout_action(vf->vfd, write_timer_action) ;
+ } ;
ssl_push(vio->vout, vf, vout_next) ;
if (vio->vout_base == NULL)
- vio->vout_base = vf ;
+ {
+ assert(vio->vout_depth == 0) ;
+ vio->vout_base = vf ;
+ }
else
- assert(type != VOUT_NONE) ;
-} ;
-
-
-
+ {
+ assert(type != VOUT_NONE) ;
+ ++vio->vout_depth ;
+ } ;
+ vf->obuf = vio_fifo_init_new(NULL, obuf_size) ;
+ vio_fifo_set_end_mark(vf->obuf) ;
+ vio->obuf = vio->vout->obuf ;
+} ;
+/*------------------------------------------------------------------------------
+ * Set timeout value.
+ *
+ * This is only ever called when a command (eg exec-timeout) sets a new
+ * time out value -- which applies only to VIN_TERM and VTY_VTYSH.
+ */
+extern void
+uty_set_timeout(vty_io vio, vty_timer_time timeout)
+{
+ vio_in_type_t vt ;
+ VTY_ASSERT_LOCKED() ;
+ vt = vio->vin_base->vin_type ;
+ if ((vt == VIN_TERM) || (vt == VIN_VTYSH))
+ uty_vf_set_read_timeout(vio->vin_base, timeout) ;
+} ;
/*------------------------------------------------------------------------------
* Set/Clear "monitor" state:
@@ -561,10 +443,10 @@ extern void
uty_set_monitor(vty_io vio, bool on)
{
VTY_ASSERT_LOCKED() ;
-
+#if 0
if (on && !vio->monitor)
{
- if ((vio->vty->type == VTY_TERMINAL) && !vio->half_closed)
+ if ((vio->vty->type == VTY_TERMINAL) && !vio->closing)
{
vio->monitor = 1 ;
sdl_push(vio_monitors_base, vio, mon_list) ;
@@ -575,267 +457,278 @@ uty_set_monitor(vty_io vio, bool on)
vio->monitor = 0 ;
sdl_del(vio_monitors_base, vio, mon_list) ;
}
+#endif
} ;
/*------------------------------------------------------------------------------
- * Return "name" of VTY
+ * Return "name" of VTY.
*
- * For VTY_TERM this is the IP address of the far end of the telnet connection.
+ * The name of the base vin, or (failing that) the base vout.
*/
extern const char*
uty_get_name(vty_io vio)
{
- return (vio->name != NULL) ? vio->name : "?" ;
+ const char* name ;
+
+ name = vio->vin_base->name ;
+ if (name == NULL)
+ name = vio->vout_base->name ;
+
+ return (name != NULL) ? name : "?" ;
} ;
/*------------------------------------------------------------------------------
- * Close all the readers.
+ * Close all vin excluding the vin_base.
*
- * Half-close everything on the vin stack. Empties the vin stack down to the
- * base entry. Discards any read-only vio_vf (except for last vin entry).
+ * This is done on close and when there is a command error.
+ */
+extern void
+uty_vin_close_stack(vty_io vio)
+{
+ while (vio->vin != vio->vin_base)
+ uty_vin_close(vio) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Close all vout except for the vout_base.
*
- * VIO is placed on death watch, and will stay there until:
+ * This is done on close and when there is a command error.
*
- * * outstanding commands complete.
+ * Should only be "final" on a call from the watch-dog !
+ */
+extern void
+uty_vout_close_stack(vty_io vio, bool final)
+{
+ while (vio->vout != vio->vout_base)
+ uty_vout_close(vio, final) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Close VTY -- may be called any number of times !
*
- * * outstanding output completes, or times out, or program terminates.
+ * If no reason for the close has already been set, sets the given reason.
+ * The close reason is reported at the end of the output to the vout_base.
*
- * For VTY_TERMINAL (must be in CLI thread):
+ * Once a VTY has been closed, it will provide no further input. However, until
+ * "final" close, it will continue to output anything which is pending at the
+ * time of the close, which includes: (a) anything in the output buffer,
+ * (b) any further output from a then running command and (c) anything that an
+ * out_pipe may be in the process of returning.
*
- * * shut the socket for reading
- * * discard all buffered input, setting it to "EOF"
- * * turns off any monitor status !
- * * drop down to RESTRICTED_NODE
+ * The main complication is that in a multi-threaded world there may be a
+ * vio->cmd_running. The close shuts down everything on the input stack, so
+ * any active command loop will come to a halt as soon as the current command
+ * does complete. The output stack is preserved, but will be closed on
+ * completion of the command.
*
- * For VTY_SHELL_SERVER (must be in CLI thread):
+ * Closing a VTY places it on the death-watch list. Once any pending output
+ * has been dealt with and any cmd_running has completed, then the death-watch
+ * will apply the coup de grace.
*
- * * shut the socket for reading
- * * discard all buffered input
- * * drop down to RESTRICTED_NODE
+ * Getting the death-watch to finally close the VTY also allows the vty to be
+ * closed from somewhere deep (e.g. when there is an I/O error) without
+ * destroying the VTY and upsetting code that might not realise the VTY has
+ * vanished.
*
- * If no reason for the close has already been set, sets the given reason.
+ * The close sequence is:
+ *
+ * 1. if a close reason has not already been set, set any given one.
+ *
+ * 2. if not already closing -- ie this is the first call of uty_close()
+ *
+ * a. transfer to death-watch list & set closing.
+ *
+ * b. turn off any monitor state
+ *
+ * c. close down the input/read side completely.
+ *
+ * Empties the vin stack down to vin_base, closing all input.
+ *
+ * Close the vin_base. The vin_base is left !read_open.
+ *
+ * All read-only vio_vf (except the vin_base) are freed.
+ *
+ * Note that everything is read closed before anything is write
+ * closed.
+ *
+ * A vio_vf is read closed once and only once.
+ *
+ * The vin_base is closed only by this function. Until the final
+ * uty_close, there is always at least the vin_base, even if it is
+ * read_closed.
+ *
+ * 3. try to close everything on the vout_closing list.
+ *
+ * even if cmd_running
*
- * If already half-closed, does nothing else.
*
- * Returns true <=> first half close.
*/
-static bool
-uty_do_half_close(vty_io vio, const char* reason)
+extern bool
+uty_close(vty_io vio, bool final, qstring reason)
{
- vio_vf vf ;
+ vio_vf vf_next ;
VTY_ASSERT_LOCKED() ;
- if ((vio->close_reason == NULL) && (reason != NULL))
- vio->close_reason = XSTRDUP(MTYPE_TMP, reason) ;
+ /* quit if already closed */
+ if (vio->state == vf_closed)
+ return true ;
- if (vio->half_closed)
- return false ;
+ /* Set the close reason, if not already set. */
+ if (reason != NULL)
+ {
+ if (vio->close_reason == NULL)
+ vio->close_reason = reason ;
+ else
+ qs_reset(reason, free_it) ;
+ } ;
- /* Half close everything on the vin stack.
- *
- * Leave stack with just the base entry (closed for read).
+ /* If not already closing, set closing and transfer to the death watch
+ * list -- turn off any "monitor" status immediately.
*/
- while (1)
+ if (vio->state == vf_open)
{
- vf = vio->vin ; /* Current first on list */
+ vio->state = vf_closing ;
- uty_vf_half_close(vf) ; /* fd level half close etc. */
+ /* Transfer to the death-watch */
+ sdl_del(vio_list_base, vio, vio_list) ;
+ sdl_push(vio_death_watch, vio, vio_list) ;
- switch(vf->vin_type) /* tidy up each type */
- {
- case VIN_NONE:
- break ;
+ /* TODO turn off any "monitor" IMMEDIATELY. */
- case VIN_TERM:
- uty_term_half_close(vf) ;
- uty_cli_close(vio) ; /* tell the CLI to stop */
- break ;
+ /* Flush the vin stack.
+ *
+ * Where possible this will revoke commands bring command processing to
+ * as sudden a halt as possible -- though may still be processing
+ * commands.
+ */
+ uty_vin_close_stack(vio) ;
+ assert(vio->vin == vio->vin_base) ;
+ uty_vf_read_close(vio->vin) ;
+ } ;
+
+ /* If there is anything on the vout_closing list, give it a shove in case
+ * that manages to close anything -- which it will do if "final".
+ */
+ vf_next = vio->vout_closing ;
+ while (vf_next != NULL)
+ {
+ vio_vf vf = vf_next ;
+ vf_next = ssl_next(vf, vout_next) ;
- case VIN_SHELL:
- break ;
+ uty_vf_write_close(vf, final) ; /* removes from vout_closing if
+ is now completely closed. */
+ } ;
- case VIN_FILE:
- break ;
+ /* If is vio->cmd_running, this is as far as we can go this time. */
+ if (vio->cmd_running)
+ return false ;
- case VIN_PIPE:
- break ;
+ /* Make sure no longer holding the config symbol of power */
+ uty_config_unlock(vio->vty, NULL_NODE) ;
- case VIN_CONFIG:
- break ;
+ /* Flush the vout stack.
+ *
+ * If cannot completely close a vf, places it on the vout_closing list,
+ * except for the vout_base.
+ */
+ uty_vout_close_stack(vio, final) ;
+ assert(vio->vout == vio->vout_base) ;
+ uty_vf_write_close(vio->vout, final) ;
- default:
- zabort("unknown VIN type") ;
- } ;
+ /* See if have now successfully closed everything. */
+ if ((vio->vout_closing != NULL) || (vio->vout_base->vout_state != vf_closed))
+ return false ; /* something is still open */
- /* Finished if half closed the last on the list */
- if (vf == vio->vin_base)
- break ;
+ /* Empty everything out of the vio and return the good news.
+ *
+ * NB: retains vio->vty & the vio->vio_list for death-watch.
+ */
+ assert((vio->vin == vio->vin_base) && (vio->vin_depth == 0)) ;
- /* Hack off head of list. If it is read-only, close & free. */
- ssl_del_head(vio->vin, vin_next) ;
+ if (vio->vin != vio->vout)
+ vio->vin = uty_vf_free(vio->vin) ;
+ else
+ vio->vin = NULL ;
+ vio->vin_base = NULL ;
- if (vf->vout_type == VOUT_NONE)
- uty_vf_close(vf) ;
- } ;
+ assert((vio->vout == vio->vout_base) && (vio->vout_depth == 0)) ;
- /* Make sure no longer holding the config symbol of power */
- uty_config_unlock(vio->vty, RESTRICTED_NODE) ;
+ vio->vout = uty_vf_free(vio->vout) ;
- /* Move to the death watch list */
- sdl_del(vio_list_base, vio, vio_list) ;
- sdl_push(vio_death_watch, vio, vio_list) ;
+ vio->close_reason = qs_reset(vio->close_reason, free_it) ;
- vio->half_closed = true ;
+ vio->obuf = NULL ;
+ vio->state = vf_closed ;
return true ;
} ;
/*------------------------------------------------------------------------------
- * Close VTY.
- *
+ * Pop and close the top of the vin stack -- MUST NOT be vin_base.
*
+ * Read closes the vio_vfd -- if the vio_vfd was read-only, this will fully
+ * close it, and the vio_vfd will have been freed.
*
- * If no reason for the close has already been set, sets the given reason.
- *
- * If not already half-closed, close all readers as described above, and then
- * kick everything on the vout stack and apply VTY_HALF_CLOSE_TIMEOUT.
+ * If this is read-only will free the vio_vf and all its contents.
*
+ * NB: a uty_cmd_prepare() is required before command processing can continue.
*
+ * That is not done here because this may be called from outside the
+ * command loop -- in particular by uty_close().
*/
extern void
-uty_close (vty_io vio, const char* reason)
+uty_vin_close(vty_io vio)
{
- if (uty_do_half_close(vio, reason))
- {
- vio_vf vf ;
+ vio_vf vf ;
- /* Run down the vout stack, and write-ready enable everything with
- * the half close timeout set.
- *
- * This will have the effect of kicking all output.
- */
- vf = vio->vout ;
- while (vf != NULL)
- {
- vf->write_timeout = 0 ;
- uty_vf_set_write(vf, on) ;
+ vf = vio->vin ;
- vf = ssl_next(vf, vout_next) ;
- } ;
- } ;
+ assert(vf != vio->vin_base) ;
+ assert(vio->vin_depth > 0) ;
+
+ ssl_del_head(vio->vin, vin_next) ;
+ --vio->vin_depth ;
+
+ uty_vf_read_close(vf) ;
} ;
/*------------------------------------------------------------------------------
- * Closing down VTY.
- *
- * Shuts down everything and discards all buffers etc. etc.
+ * Pop and close the top of the vout stack -- MUST NOT be vout_base.
*
- * If cmd_in_progress, cannot complete the process -- but sets the closed
- * flag.
+ * Moves the vio_vf to the vout_closing list.
*
- * Can call vty_close() any number of times.
+ * Before closing, push any outstanding output.
*
- * The vty structure is placed on death watch, which will finally free the
- * structure once no longer cmd_in_progress.
+ * Unless "final", the close is soft, that is, if there is any output still
+ * outstanding does not actually close the vout.
*
+ * If there is no outstanding output (or if final) will completely close the
+ * vf and free it.
*
- * If no reason for the close has already been set, sets the given reason.
+ * NB: a uty_cmd_prepare() is required before command processing can continue.
*
+ * That is not done here because this may be called from outside the
+ * command loop -- in particular by uty_close().
*/
extern void
-uty_close_final(vty_io vio, const char* reason)
+uty_vout_close(vty_io vio, bool final)
{
- VTY_ASSERT_LOCKED() ;
-
- /* If not already closed, close. */
- uty_do_half_close(vio, reason) ; /* set reason if required, and
- make sure is half closed. */
- if (!vio->closed)
- {
- vio_vf vf ;
-
- /* Empty the vin stack */
- assert(vio->vin != NULL) ;
- assert(vio->vin == vio->vin_base) ;
- if (vio->vin != vio->vout_base)
- uty_vf_close(vio->vin) ;
- vio->vin_base = vio->vin = NULL ;
-
- /* Now kick everything in the vout stack, in case can get stuff
- * written away. And then close.
- */
- while (ssl_pop(&vf, vio->vout, vout_next) != NULL)
- {
- uty_vf_set_write(vf, off) ; /* stop any write ready... */
- vf->closing = true ; /* ...permanently. */
-
- switch(vf->vout_type) /* tidy up each type */
- {
- case VOUT_NONE:
- break ;
-
- case VOUT_TERM:
- break ;
-
- case VOUT_SHELL:
- break ;
-
- case VOUT_FILE:
- break ;
-
- case VOUT_PIPE:
- break ;
-
- case VOUT_STDOUT:
- break ;
-
- case VOUT_STDERR:
- break ;
-
- default:
- zabort("unknown VOUT type") ;
- } ;
-
- uty_vf_close(vf) ; /* close and free */
- } ;
+ vio_vf vf ;
- vio->vout_base = NULL ; /* stack is empty */
- vio->closed = true ; /* now closed */
- } ;
+ vf = vio->vout ;
- /* Nothing more should happen, so can now release almost everything,
- * the exceptions being the things that are related to a cmd_in_progress.
- *
- * All writing to buffers is suppressed, and as the sock has been closed,
- * there will be no more read_ready or write_ready events.
- */
- if (vio->name != NULL)
- XFREE(MTYPE_VTY_NAME, vio->name) ;
+ assert(vf != vio->vout_base) ;
+ assert(vio->vout_depth > 0) ;
- vio->key_stream = keystroke_stream_free(vio->key_stream) ;
+ ssl_del_head(vio->vout, vout_next) ;
+ --vio->vout_depth ;
- qs_reset(&vio->cli_prompt_for_node, keep_it) ;
- qs_reset(&vio->cl, keep_it) ;
+ ssl_push(vio->vout_closing, vf, vout_next) ;
- {
- qstring line ;
- while ((line = vector_ream(vio->hist, keep_it)) != NULL)
- qs_reset_free(line) ;
- } ;
+ vio->obuf = vio->vout->obuf ;
- /* The final stage cannot be completed if cmd_in_progress.
- *
- * The clx is pointed at by vty->buf -- containing the current command.
- *
- * Once everything is released, can take the vty off death watch, and
- * release the vio and the vty.
- */
- if (!vio->cmd_in_progress)
- {
- qs_reset(&vio->clx, keep_it) ;
- vio->vty->buf = NULL ;
- } ;
+ uty_vf_write_close(vf, final) ;
} ;
/*==============================================================================
@@ -847,96 +740,294 @@ uty_close_final(vty_io vio, const char* reason)
*
* There are no errors, yet.
*
- * This leaves most things unset/NULL/false. Caller will want to set:
+ * This leaves most things unset/NULL/false. Caller will need to:
+ *
+ * - uty_vin_open() and/or uty_vout_open()
+ *
+ * - once those are done, the following optional items remain to be set
+ * if they are required:
+ *
+ * read_timeout -- default = 0 => no timeout
+ * write_timeout -- default = 0 => no timeout
+ *
+ * parse_type -- default = cmd_parse_standard
+ * reflect_enabled -- default = false
+ * out_enabled -- default = true iff vfd_io_write
+ *
+ * If vio->blocking, adds vfd_io_blocking to the io_type.
*
+ * NB: if there is no fd for this vio_vf, it should be set to -1, and the
+ * type (recommend vfd_none) and io_type are ignored -- except for
+ * vfd_io_write and the out_enabled state.
*
+ * A VTY_STDOUT or a VTY_STDERR (output only, to the standard I/O) can
+ * be set up without an fd, and with/without out_enabled.
*
- * Sets timeout to no timeout at all -- timeout is optional.
+ * A VTY_CONFIG_READ can be set up with the fd of the input file, but
+ * MUST be type = vfd_file and io_type = vfd_io_read -- because the fd
+ * is for input only. This will set out_enabled false (as required).
+ * The required VOUT_STDERR will be set by uty_vout_open().
*/
extern vio_vf
-uty_vf_new(vty_io vio, int fd, vfd_type_t type, vfd_io_type_t io_type)
+uty_vf_new(vty_io vio, const char* name, int fd, vfd_type_t type,
+ vfd_io_type_t io_type)
{
vio_vf vf ;
VTY_ASSERT_LOCKED() ;
+ if (vio->blocking)
+ io_type |= vfd_io_blocking ;
+
vf = XCALLOC (MTYPE_VTY, sizeof(struct vio_vf)) ;
/* Zeroising the structure has set:
*
- * vin_type = 0 -- VIN_NONE
- * vin_next = NULL -- not on a vin list, yet
+ * vio = X -- set below
+ * name = X -- set below
+ *
+ * vin_type = VIN_NONE -- see uty_vin_open()
+ * vin_state = vf_closed -- see uty_vin_open()
+ * vin_next = NULL -- see uty_vin_open()
+ *
+ * cli = NULL -- no CLI, yet
+ *
+ * ibuf = NULL -- none, yet -- see uty_vin_open()
+ *
+ * cl = zeros -- empty qstring (embedded)
+ * line_complete = false -- see uty_vout_open()
+ * line_number = 0 -- nothing yet
+ * line_step = 0 -- see uty_vout_open()
+ *
+ * parse_type = cmd_parse_standard
+ * reflect_enabled = false
*
- * vout_type = NULL -- VOUT_NONE
- * vout_next = NULL -- not on a vout list, yet
+ * vout_type = VOUT_NONE -- see uty_vout_open()
+ * vout_state = vf_closed -- see uty_vout_open()
+ * vout_next = NULL -- see uty_vout_open()
*
- * obuf = NULL -- none, yet
- * olc = NULL -- none, yet
+ * obuf = NULL -- none, yet -- see uty_vout_open()
*
- * blocking = false
- * closing = false
+ * out_enabled = X -- set below: true iff vfd_io_write
*
- * read_open = false
- * write_open = false
- * error_seen = 0 -- no error seen, yet
+ * vfd = NULL -- no vfd, yet
*
- * read_timeout = 0 -- none
- * write_timeout = 0 -- none
+ * blocking = false -- default is non-blocking I/O
+ * closing = false -- definitely not
+ *
+ * error_seen = 0 -- no error seen, yet
+ *
+ * read_on = false
+ * write_on = false
+ *
+ * read_timeout = 0 -- none
+ * write_timeout = 0 -- none
*/
confirm((VIN_NONE == 0) && (VOUT_NONE == 0)) ;
+ confirm(vf_closed == 0) ;
+ confirm(QSTRING_INIT_ALL_ZEROS) ;
+ confirm(cmd_parse_standard == 0) ;
vf->vio = vio ;
- vf->vfd = vio_fd_new(fd, type, io_type, vf) ;
+
+ if (name != NULL)
+ vf->name = XSTRDUP(MTYPE_VTY_NAME, name) ;
+
+ if (fd >= 0)
+ vf->vfd = vio_vfd_new(fd, type, io_type, vf) ;
+
+ vf->out_enabled = ((io_type & vfd_io_write) != 0) ;
return vf ;
} ;
/*------------------------------------------------------------------------------
- * Half close a vio_vf.
+ * Close the read side of the given vio_vf.
+ *
+ * Read closes the vio_vfd -- if the vio_vfd was read-only, this will fully
+ * close it, and the vio_vfd will have been freed.
+ *
+ * If this is read-only (ie vout_type == VOUT_NONE) will free the vio_vf as
+ * well as the vio_vfd -- unless this is the vin_base.
*
- * Half closes the vio_fd and shuts down all reading. If the vio_fd was
- * read-only, the half-close will fully close it, and the vio_fd will have
- * been freed.
+ * Note that read_close is effective immediately, there is no delay as there is
+ * with write_close -- so no need for a "final" option.
*/
static void
-uty_vf_half_close(vio_vf vf)
+uty_vf_read_close(vio_vf vf)
{
- /* the vfd level half close will close completely if is read only. */
- vf->vfd = vio_fd_half_close(vf->vfd) ;
+ assert(vf->vin_state != vf_closed) ;
+
+ /* Do the vfd level read close and mark the vf no longer read_open */
+ if (vf->vin_type < VIN_SPECIALS)
+ vf->vfd = vio_vfd_read_close(vf->vfd) ;
+
+ vf->vin_state = vf_closed ;
+ vf->read_on = off ;
+
+ /* Now the vin_type specific clean up. */
+ switch(vf->vin_type)
+ {
+ case VIN_NONE:
+ zabort("invalid VIN_NONE") ;
+ break ;
+
+ case VIN_TERM:
+ uty_term_read_close(vf) ;
+ break ;
- vf->read_open = false ; /* no more read operations */
- vf->read_on = off ; /* of course */
+ case VIN_VTYSH:
+ break ;
- if (vf->vfd == NULL) /* check really was read-only */
- assert(!vf->write_open && !vf->read_on) ;
+ case VIN_FILE:
+ uty_file_read_close(vf) ;
+ break ;
+
+ case VIN_PIPE:
+ break ;
+
+ case VIN_CONFIG:
+ break ;
+
+ case VIN_DEV_NULL:
+ break ;
+
+ default:
+ zabort("unknown VIN type") ;
+ } ;
+
+ if ((vf->vout_type == VOUT_NONE) && (vf != vf->vio->vin_base))
+ uty_vf_free(vf) ;
} ;
/*------------------------------------------------------------------------------
- * Close given vio_vf and free the vio_vf structure and all its contents.
+ * Close the write side of the given vio_vf, if can.
+ *
+ * Pushes any outstanding output -- non-blocking if not a blocking vty.
*
- * Assumes has been removed from vio->vin and vio->vout !
+ * The vio_vf MUST either be on the vout_closing list or be vout_base.
+ * The vio_vf MUST be write_open and MUST NOT be read_open.
+ *
+ * If output is empty, or if final, close the vf, then if it is on the
+ * vout_closing list, remove and free it.
+ *
+ * Returns whether output was empty or not.
*/
-static void
-uty_vf_close(vio_vf vf)
+static bool
+uty_vf_write_close(vio_vf vf, bool final)
+{
+ bool empty ;
+
+ assert((vf->vout_state != vf_closed) && (vf->vin_state == vf_closed)) ;
+
+ /* Worry about remaining return input from vout pipe TODO */
+
+ /* Worry about appending the close reason to the vout_base TODO */
+
+ /* Now the vout_type specific clean up. */
+ empty = false ;
+
+ switch(vf->vout_type)
+ {
+ case VOUT_NONE:
+ zabort("invalid VOUT_NONE") ;
+ break ;
+
+ case VOUT_TERM:
+ empty = uty_term_write_close(vf, final) ;
+ break ;
+
+ case VOUT_VTYSH:
+ break ;
+
+ case VOUT_FILE:
+ empty = uty_file_write_close(vf, final) ;
+ break ;
+
+ case VOUT_PIPE:
+ break ;
+
+ case VOUT_CONFIG:
+ break ;
+
+ case VOUT_DEV_NULL:
+ case VOUT_STDOUT:
+ case VOUT_STDERR:
+ empty = true ;
+ break ;
+
+ default:
+ zabort("unknown VOUT type") ;
+ } ;
+
+ if (empty || final)
+ {
+ /* Do the vfd level close and mark the vf no longer write_open */
+ if (vf->vout_type < VOUT_SPECIALS)
+ vf->vfd = vio_vfd_close(vf->vfd) ;
+ else
+ assert(vf->vfd == NULL) ;
+
+ vf->vout_state = vf_closed ;
+ vf->write_on = off ;
+
+ if (ssl_del(vf->vio->vout_closing, vf, vout_next))
+ uty_vf_free(vf) ;
+ else
+ assert(vf == vf->vio->vout_base) ;
+ } ;
+
+ return empty ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Free the given vio_vf structure and all its contents.
+ *
+ * Expects the vfd to already have been closed, but will close it if not.
+ *
+ * Expects any cli to be closed, but will close it if not.
+ *
+ * Assumes has been removed from any and all lists !
+ */
+static vio_vf
+uty_vf_free(vio_vf vf)
{
- vf->vfd = vio_fd_close(vf->vfd) ;
+ XFREE(MTYPE_VTY_NAME, vf->name) ;
+
+ vf->ibuf = vio_fifo_reset(vf->ibuf, free_it) ;
+ vf->obuf = vio_fifo_reset(vf->obuf, free_it) ;
+
+ qs_reset(vf->cl, keep_it) ;
- vf->obuf = vio_fifo_reset_free(vf->obuf) ;
- vf->olc = vio_lc_reset_free(vf->olc) ;
+ vf->cli = uty_cli_close(vf->cli, true) ;
+ vf->vfd = vio_vfd_close(vf->vfd) ; /* for completeness */
XFREE(MTYPE_VTY, vf) ;
+
+ return NULL ;
} ;
/*------------------------------------------------------------------------------
+ *
+ *
+ */
+
+
+
+
+
+/*------------------------------------------------------------------------------
* Dealing with an I/O error on VTY socket
*
* If this is the first error for this VTY, produce suitable log message.
*
* If is a "monitor", turn that off, *before* issuing log message.
*/
-static int
-uty_vf_error(vty_io vio, const char* what)
+extern int
+uty_vf_error(vio_vf vf, const char* what, int err)
{
+ vty_io vio = vf->vio ;
+
VTY_ASSERT_LOCKED() ;
VTY_ASSERT_CLI_THREAD() ;
@@ -944,24 +1035,14 @@ uty_vf_error(vty_io vio, const char* what)
uty_set_monitor(vio, 0) ;
/* if this is the first error, log it */
- if (vio->sock.error_seen == 0)
+ if (vf->error_seen == 0)
{
- const char* type ;
- switch (vio->vty_type)
- {
- case VTY_TERM:
- type = "VTY Terminal" ;
- break ;
- case VTY_SHELL:
- type = "VTY Shell Server" ;
- break ;
- default:
- zabort("unknown VTY type for uty_file_error()") ;
- } ;
-
- vio->sock.error_seen = errno ;
+ const char* type = "?" ; /* TODO */
+
+
+ vf->error_seen = err ;
uzlog(NULL, LOG_WARNING, "%s: %s failed on fd %d: %s",
- type, what, vio->sock.fd, errtoa(vio->sock.error_seen, 0).str) ;
+ type, what, vio_vfd_fd(vf->vfd), errtoa(err, 0).str) ;
} ;
return -1 ;
@@ -970,33 +1051,34 @@ uty_vf_error(vty_io vio, const char* what)
/*------------------------------------------------------------------------------
* Set required read ready state. Applies the current read timeout.
*
- * Forces off if: vf->closing
+ * Forces off if: vf->vin_state != vf_open
*
- * Does nothing if: !vf->read_open
+ * Does nothing if: vf->vin_state == vf_closed
* or: vf->vfd == NULL
+ *
+ * Returns new state of vf->read_on
*/
-extern on_off_t
-uty_vf_set_read(vio_vf vf, on_off_t how)
+extern on_off_b
+uty_vf_set_read(vio_vf vf, on_off_b how)
{
- if (vf->closing)
+ if (vf->vin_state != vf_open)
how = off ;
- return vf->read_on = vf->read_open
- ? vio_fd_set_read(vf->vfd, how, vf->read_timeout)
+
+ return vf->read_on = (vf->vin_state != vf_closed)
+ ? vio_vfd_set_read(vf->vfd, how, vf->read_timeout)
: off ;
} ;
/*------------------------------------------------------------------------------
* Set required read ready timeout -- if already read_on, restart it.
*
- * Forces read ready off if: vf->closing
- *
- * Does nothing if: !vf->read_open
- * or: vf->vfd == NULL
+ * Returns new state of vf->read_on
*/
-extern on_off_t
+extern on_off_b
uty_vf_set_read_timeout(vio_vf vf, vty_timer_time read_timeout)
{
vf->read_timeout = read_timeout ;
+
return vf->read_on ? uty_vf_set_read(vf, on)
: off ;
} ;
@@ -1004,34 +1086,34 @@ uty_vf_set_read_timeout(vio_vf vf, vty_timer_time read_timeout)
/*------------------------------------------------------------------------------
* Set required write ready state. Applies the current write timeout.
*
- * Forces off if: vf->closing
+ * Forces off if: vf->vout_state != vf_open
*
- * Does nothing if: !vf->write_open
+ * Does nothing if: vf->vout_state == vf_closed
* or: vf->vfd == NULL
+ *
+ * Returns new state of vf->write_on
*/
-extern on_off_t
-uty_vf_set_write(vio_vf vf, on_off_t how)
+extern on_off_b
+uty_vf_set_write(vio_vf vf, on_off_b how)
{
- if (vf->closing)
+ if (vf->vout_state != vf_open)
how = off ;
- return vf->write_on = vf->write_open
- ? vio_fd_set_write(vf->vfd, how, vf->write_timeout)
+ return vf->write_on = (vf->vout_state != vf_closed)
+ ? vio_vfd_set_write(vf->vfd, how, vf->write_timeout)
: off ;
} ;
/*------------------------------------------------------------------------------
* Set required write ready timeout -- if already write_on, restart it.
*
- * Forces write ready off if: vf->closing
- *
- * Does nothing if: !vf->write_open
- * or: vf->vfd == NULL
+ * Returns new state of vf->write_on
*/
-extern on_off_t
+extern on_off_b
uty_vf_set_write_timeout(vio_vf vf, vty_timer_time write_timeout)
{
vf->write_timeout = write_timeout ;
+
return vf->write_on ? uty_vf_set_write(vf, on)
: off ;
} ;
@@ -1061,9 +1143,11 @@ uty_open_listeners(const char *addr, unsigned short port, const char *path)
if (port)
uty_term_open_listeners(addr, port) ;
+#if 0
/* If want to listen for vtysh, set up listener now */
if (VTYSH_ENABLED && (path != NULL))
uty_serv_vtysh(path) ;
+#endif
} ;
/*------------------------------------------------------------------------------
@@ -1072,7 +1156,7 @@ uty_open_listeners(const char *addr, unsigned short port, const char *path)
* Adds to list of listeners for close down.
*/
extern void
-uty_add_listener(int fd, vio_fd_accept* accept_action)
+uty_add_listener(int fd, vio_vfd_accept* accept_action)
{
vio_listener vl ;
diff --git a/lib/vty_io.h b/lib/vty_io.h
index ecd7a451..5db12ca2 100644
--- a/lib/vty_io.h
+++ b/lib/vty_io.h
@@ -25,145 +25,251 @@
#ifndef _ZEBRA_VTY_IO_H
#define _ZEBRA_VTY_IO_H
-#include "zebra.h"
+//#include "zebra.h"
#include "misc.h"
+//#include <errno.h>
-#include <errno.h>
-
+#include "vty_local.h"
+#include "command_local.h"
#include "vty_io_basic.h"
-#include "uty.h"
-#include "vty.h"
#include "vio_fifo.h"
-#include "vio_lines.h"
-#include "keystroke.h"
#include "thread.h"
-#include "command.h"
+#include "command_execute.h"
#include "qstring.h"
/*==============================================================================
- * Here are structures and other definitions which are shared by:
- *
- * vty.c -- the main vty handler
- * vty_cli.c -- which handles the command line stuff
- * vty_io.c -- ....
- *
- * The "struct vty" is used extensively across the Quagga daemons, where it
- * has two functions relating to command handling as:
- *
- * 1) a "file handle" for output produced by commands
- *
- * 2) the holder of some context -- notably the current command "node" -- for
- * command execution to use
+ * Structures and other definitions for the top level VTY I/O.
*
- * The bulk of "struct vty" is, therefore, private to vty.c and is factored
- * out into the "struct vty_io".
- *
- * To reduce the size of vty.c, some groups of functions are separated into:
- *
- * vty_cli.c -- which looks after the keystroke by keystroke handling
- * of the command line.
+ * There is one struct vty_io per VTY, which contains, inter alia, the vin
+ * and vout stacks.
*
+ * The vin and vout stacks contain one or more struct vty_vf -- one per
+ * input and/or output associated with the VTY.
*/
-/*==============================================================================
- * VTY CLI and OUT types
+enum
+{
+ VTY_WATCH_DOG_INTERVAL = 5, /* interval between barks */
+
+ VTY_HALF_CLOSE_TIMEOUT = 120, /* timeout after half_close */
+
+ VTY_TIMEOUT_DEFAULT = 600, /* terminal timeout value */
+} ;
+
+/*------------------------------------------------------------------------------
+ * VTY VIN and OUT types
*/
enum vio_in_type /* Command input */
{
- VIN_NONE = 0, /* no input at all */
+ VIN_NONE = 0, /* not a valid input type */
VIN_TERM, /* telnet terminal */
- VIN_SHELL, /* vty_shell input */
+ VIN_VTYSH, /* vty_shell input */
VIN_FILE, /* ordinary file input */
VIN_PIPE, /* pipe (from child process) */
- VIN_CONFIG, /* config file ?? */
+ VIN_CONFIG, /* config file */
+
+ VIN_PIPE_RETURN, /* */
+
+ /* The VIN types >= VIN_SPECIALS do not have an associated fd.
+ *
+ * These can coexist with a VOUT which does have an associated fd.
+ */
+ VIN_SPECIALS, /* all special from now on */
+
+ VIN_DEV_NULL = VIN_SPECIALS,
+ /* black hole input */
} ;
typedef enum vio_in_type vio_in_type_t ;
enum vio_out_type /* Command output */
{
- VOUT_NONE = 0, /* no output at all */
+ VOUT_NONE = 0, /* not a valid output type */
VOUT_TERM, /* a telnet terminal */
- VOUT_SHELL, /* a vty_shell output pipe */
+ VOUT_VTYSH, /* a vty_shell output pipe */
VOUT_FILE, /* ordinary file */
VOUT_PIPE, /* pipe (to child process) */
+ VOUT_CONFIG, /* config file ?? */
+
+ /* The VOUT types >= VOUT_SPECIALS do not have an associated fd.
+ *
+ * These can coexist with a VIN which does have an associated fd.
+ */
+ VOUT_SPECIALS, /* all special from now on */
+
+ VOUT_DEV_NULL = VOUT_SPECIALS,
+ /* black hole output */
+
VOUT_STDOUT, /* stdout */
VOUT_STDERR, /* stderr */
};
typedef enum vio_out_type vio_out_type_t ;
/*------------------------------------------------------------------------------
- * VIO file structure
+ * State of a vf or of the entire vio.
+ *
+ * For a vf: I/O is possible iff vf_open -- has separate state for vin/vout.
+ *
+ * For a vio: used to manage the closing process.
+ */
+enum vf_state
+{
+ vf_closed = 0, /* not active -- may be discarded */
+
+ vf_open = 1, /* open for business */
+
+ vf_eof = 2, /* open, but at eof */
+ vf_error = 3, /* open, but error reported */
+
+ vf_closing = 4, /* open, but in process of closing */
+} ;
+typedef enum vf_state vf_state_t ;
+
+/*------------------------------------------------------------------------------
+ * vty_vf -- "vty file" structure
*
- * All I/O is non-blocking for all sources and sinks of VIO stuff.
+ * A vio_vf may be a read, write or read/write object.
+ *
+ * All I/O is via vio_vfd objects, except for VOUT_STDOUT and VOUT_STDERR.
+ * The vio_vfd layer hides the differences between the qpthreads an legacy
+ * thread environments.
+ *
+ * The VOUT_STDOUT and VOUT_STDERR are handled as direct output to the standard
+ * i/o file handles. In the case of a VTY_CONFIG_READ, the vin is VIN_CONFIG
+ * and the vout is VOUT_STDOUT, and these can share a single vty_vf.
*
* Also used for the associated listeners.
*/
+struct vty_io ; /* Forward reference */
+typedef struct vty_io* vty_io ;
+
typedef struct vio_vf* vio_vf ;
struct vio_vf
{
- vty_io vio ; /* parent */
+ vty_io vio ; /* parent */
+
+ char* name ; /* MTYPE_VTY_NAME (if any) */
+
+ /* Input side. */
+
+ vio_in_type_t vin_type ;
+ vf_state_t vin_state ;
+ vio_vf vin_next ; /* list of inputs */
+
+
+ struct vty_cli* cli ; /* NULL if not a VTY_TERMINAL ! */
+
+ vio_fifo ibuf ; /* input fifo (if required) */
- vio_in_type_t vin_type ;
- vio_vf vin_next ; /* list of inputs */
+ qstring_t cl ; /* command line buffer */
+ bool line_complete ; /* false => line in construction */
+ uint line_number ; /* number of first line in cl */
+ uint line_step ; /* number of real lines in cl */
- vio_out_type_t vout_type ;
- vio_vf vout_next ; /* list of outputs */
+ cmd_parse_type_t parse_type ; /* iff vin_type != VIN_NONE */
+ bool reflect_enabled ; /* false if vin_type == VIN_NONE */
- vio_fifo obuf ; /* pointer to fifo */
- vio_line_control olc ; /* pointer to lc */
+ /* Output side. */
- vio_fd vfd ;
+ vio_out_type_t vout_type ;
+ vf_state_t vout_state ;
+ vio_vf vout_next ; /* list of outputs */
- bool blocking ; /* using blocking reads */
- bool closing ; /* suppress read/write ready */
+ vio_fifo obuf ; /* output fifo (if required) */
- bool read_open ; /* reads returns 0 if not */
- bool write_open ; /* writes complete instantly if not */
- int error_seen ; /* non-zero => failed */
+ bool out_enabled ; /* false if vout_type == VOUT_NONE */
- on_off_b read_on ;
- on_off_b write_on ;
+ /* I/O */
+
+ vio_vfd vfd ; /* vty_io_basic "file descriptor" */
+
+ bool blocking ; /* using blocking I/O (config read) */
+
+ int error_seen ; /* non-zero => failed */
+
+ on_off_b read_on ;
+ on_off_b write_on ;
vty_timer_time read_timeout ;
vty_timer_time write_timeout ;
+
} ;
-enum vty_readiness /* bit significant */
+enum vty_readiness /* bit significant */
{
not_ready = 0,
- read_ready = 1,
- write_ready = 2, /* takes precedence */
- now_ready = 4
+ read_ready = BIT(0),
+ write_ready = BIT(1), /* may take precedence */
+ now_ready = BIT(2)
} ;
+typedef enum vty_readiness vty_readiness_t ;
/*------------------------------------------------------------------------------
* The vty_io structure
*
+ * The main elements of the vty_io object are the vin and vout stacks.
+ *
+ * One of the challenges is managing the closing of VTY objects. First,
+ * cannot close and free a VTY object which is in the hands of a command
+ * function, or which is queued to or from a command function. Second,
+ * do not wish to completely close an output until have given it a chance
+ * to clear any buffered output.
+ *
+ *
*
+ * "cmd_running" means that the VTY is in hands of (or has been passed to)
+ * a command loop -- the VTY cannot be fully closed until that is no
+ * longer the case.
*
+ * "blocking" is set for configuration reading VTY, so that everything is
+ * done with blocking I/O.
*
+ * "closing" means that the vty has been closed (!), but a command
+ * and or output may still be active:
*
+ * - if is a socket, will have shut_down the read side (half-closed)
+ *
+ * - any further attempts to read will give "eof"
+ *
+ * - there may be a command in execution -- see "cmd_running".
+ *
+ * - further writing will be honoured.
+ *
+ * - the write side may still be active, attempting to empty out any
+ * pending output.
+ *
+ * "closed" means the vty has been fully and finally closed.
+ *
+ * - any further attempts to write will be ignored, but return instant
+ * success.
+ *
+ * - the file/socket has been fully closed.
+ *
+ * - the VTY and all attached structures can be reaped by the death_watch.
*/
+struct vty_cli ; /* forward reference -- vty_cli.h is
+ *not* included, because that refers
+ back to the vty_io ! */
-struct vty_io
+struct vty_io /* typedef appears above */
{
- struct vty* vty ; /* the related vty */
- char *name ; /* for VTY_TERM is IP address) */
+ vty vty ; /* the related vty */
- /* vin stack */
- vio_vf vin ;
+ vio_vf vin ; /* vin stack */
vio_vf vin_base ;
+ uint vin_depth ;
- /* vout stack */
- vio_vf vout ;
+ vio_vf vout ; /* vout stack */
vio_vf vout_base ;
+ uint vout_depth ;
+
+ vio_vf vout_closing ; /* vout closing list */
/* List of all vty_io objects */
struct dl_list_pair(vty_io) vio_list ;
@@ -171,193 +277,102 @@ struct vty_io
/* List of all vty_io that are in monitor state */
struct dl_list_pair(vty_io) mon_list ;
- /* VTY state */
-
- bool half_closed ; /* => on death watch list until closed */
- bool closed ; /* => all I/O terminated
- will also be half_closed */
-
- char* close_reason ; /* message to be sent, once all other
- output has completed, giving reason
- for closing the VTY. */
-
-
-
-
-
- /* When writing configuration file */
- enum vty_type real_type ;
-
- int file_fd ;
- int file_error ;
-
- /* Failure count for login attempts */
- int fail;
-
- /* History of commands */
- vector_t hist ;
- int hp ; /* History lookup current point */
- int hindex; /* History insert end point */
-
- /* Window width/height as reported by Telnet. 0 => unknown */
- int width;
- int height;
-
- /* Configure lines. */
- int lines;
- bool lines_set ; /* true <=> explicitly set */
-
- /* Terminal monitor. */
- bool monitor ;
- bool monitor_busy ;
-
- /* Terminal timeout in seconds -- 0 => none */
- vty_timer_time v_timeout ;
-
- /*-------------------------------------------------------------------------
- * CLI_TERM stuff.
- */
-
- keystroke_stream key_stream ;
-
- /* cli_drawn <=> the current prompt and user input occupy the current
- * line on the screen.
+ /* VTY state
*
- * cli_dirty <=> the last command output did not end with a newline.
+ * "blocking" is set for configuration reading VTY, so that everything is
+ * done with blocking I/O.
*
- * If cli_drawn is true, the following are valid:
+ * "half_closed" means that the vty has been closed (!), but a command
+ * and or output may still be active:
*
- * cli_prompt_len -- the length of the prompt part.
- * (will be the "--more--" prompt in cli_more_wait)
+ * - if is a socket, will have shut_down the read side (half-closed)
*
- * cli_extra_len -- the length of any ^X at the cursor position
- * (for when blocked waiting for queued command)
+ * - any further attempts to read will give "eof"
*
- * cli_echo_suppress -- the user part of the command line is suppressed
+ * - there may be a command in execution -- see "cmd_running".
*
- * NB: cli_echo_suppress is only used for password entry.
- */
- bool cli_drawn ;
- bool cli_dirty ;
-
- int cli_prompt_len ;
- int cli_extra_len ;
-
- bool cli_echo_suppress ;
-
- /* "cache" for prompt -- when node or host name changes, prompt does */
- enum node_type cli_prompt_node ;
- bool cli_prompt_set ;
- qstring_t cli_prompt_for_node ;
-
- /* State of the CLI
+ * - further writing will be honoured.
*
- * cli_blocked -- blocked from processing keystrokes
- * cmd_in_progress -- command dispatched (may be queued)
- * cmd_out_enabled -- contents of the command FIFO may be written away
- * cli_more_wait -- is in "--more--" wait state
- */
- bool cli_blocked ;
- bool cmd_in_progress ;
- bool cmd_out_enabled ;
- bool cli_more_wait ;
-
- /* This is used to control command output, so that each write_ready event
- * generates at most one tranche of output.
- */
- bool cmd_out_done ;
-
- /* This is set only if the "--more--" handling is enabled */
- bool cli_more_enabled ;
-
- /* Command Line(s)
+ * - the write side may still be active, attempting to empty out any
+ * pending output.
+ *
+ * "cmd_running" means that the VTY is in hands of (or has been passed to)
+ * a command loop -- the VTY cannot be fully closed until that is no
+ * longer the case.
*
- * cli_do -- when current command being prepared is completed (by
- * CR/LF or otherwise) this says what there now is to be done.
+ * "closed" means the vty has been fully and finally closed.
*
- * cl -- current command line being prepared.
+ * - any further attempts to write will be ignored, but return instant
+ * success.
*
- * clx -- current command line being executed.
+ * - the file/socket has been fully closed.
*
- * NB: during command execution vty->buf is set to point at the '\0'
- * terminated current command line being executed.
+ * - the VTY and all attached structures can be reaped by the death_watch.
*/
- enum cli_do cli_do ;
+ bool blocking ; /* => all I/O is blocking. */
- qstring_t cl ;
- qstring_t clx ;
+ bool cmd_running ; /* => cannot be fully closed */
- /* CLI output buffering */
- vio_fifo_t cli_obuf ;
+ vf_state_t state ;
-} ;
+ qstring close_reason ; /* message to be sent, once all other
+ output has completed, giving reason
+ for closing the VTY. */
-/*==============================================================================
- * If possible, will use getaddrinfo() to find all the things to listen on.
- */
-enum {
-#if defined(HAVE_IPV6) && !defined(NRL)
- VTY_USE_ADDRINFO = 1,
-#else
- VTY_USE_ADDRINFO = 0,
-#endif
+ /* For ease of output, pointer to current vout->obuf */
+ vio_fifo obuf ; /* NULL => no vout ! */
} ;
/*==============================================================================
* Functions
*/
-extern vty uty_new (vty_type_t type, int sock_fd) ;
-extern void uty_close (vty_io vio, const char* reason) ;
-extern void uty_close_final(vty_io vio, const char* reason) ;
+extern int uty_out (vty_io vio, const char* format, ...) PRINTF_ATTRIBUTE(2, 3);
+Inline int uty_vprintf(vty_io vio, const char *format, va_list args) ;
+
+Inline void uty_out_clear(vty_io vio) ;
+Inline void uty_out_accept(vty_io vio) ;
+Inline void uty_out_reject(vty_io vio) ;
+extern vty uty_new (vty_type_t type) ;
+extern bool uty_close(vty_io vio, bool final, qstring reason) ;
-extern void uty_vin_add(vty_io vio, vio_vf vf, vio_in_type_t type,
- vio_fd_action* read_action, vio_timer_action* read_timer_action) ;
-extern void uty_vout_add(vty_io vio, vio_vf vf, vio_out_type_t type,
- vio_fd_action* write_action, vio_timer_action* write_timer_action) ;
+extern void uty_vin_close(vty_io vio) ;
+extern void uty_vout_close(vty_io vio, bool final) ;
+extern void uty_vin_close_stack(vty_io vio) ;
+extern void uty_vout_close_stack(vty_io vio, bool final) ;
+extern void uty_set_timeout(vty_io vio, vty_timer_time timeout) ;
+extern void uty_vin_open(vty_io vio, vio_vf vf, vio_in_type_t type,
+ vio_vfd_action* read_action,
+ vio_timer_action* read_timer_action,
+ usize ibuf_size) ;
+extern void uty_vout_open(vty_io vio, vio_vf vf, vio_out_type_t type,
+ vio_vfd_action* write_action,
+ vio_timer_action* write_timer_action,
+ usize obuf_size) ;
-extern vio_vf uty_vf_new(vty_io vio, int fd, vfd_type_t type,
+extern vio_vf uty_vf_new(vty_io vio, const char* name, int fd, vfd_type_t type,
vfd_io_type_t io_type) ;
-Inline int uty_vf_fd(vio_vf vf) ;
-extern on_off_t uty_vf_set_read(vio_vf vf, on_off_t on) ;
-extern on_off_t uty_vf_set_read_timeout(vio_vf vf,
+extern on_off_b uty_vf_set_read(vio_vf vf, on_off_b on) ;
+extern on_off_b uty_vf_set_read_timeout(vio_vf vf,
vty_timer_time read_timeout) ;
-extern on_off_t uty_vf_set_write(vio_vf vf, on_off_t on) ;
-extern on_off_t uty_vf_set_write_timeout(vio_vf vf,
+extern on_off_b uty_vf_set_write(vio_vf vf, on_off_b on) ;
+extern on_off_b uty_vf_set_write_timeout(vio_vf vf,
vty_timer_time write_timeout) ;
-
+extern int uty_vf_error(vio_vf vf, const char* what, int err) ;
extern void uty_open_listeners(const char *addr, unsigned short port,
const char *path) ;
-extern void uty_add_listener(int fd, vio_fd_accept* accept) ;
+extern void uty_add_listener(int fd, vio_vfd_accept* accept) ;
extern void uty_close_listeners(void) ;
extern void uty_watch_dog_init(void) ;
extern void uty_watch_dog_start(void) ;
extern void uty_watch_dog_stop(void) ;
-
-
-extern int uty_output (struct vty *vty, const char *format, ...)
- PRINTF_ATTRIBUTE(2, 3) ;
-extern int uty_vprintf(struct vty *vty, const char *format, va_list args) ;
-extern int uty_reflect(struct vty *vty) ;
-extern void uty_out_clear(vty_io vio) ;
-extern void uty_out_fflush(vty_io vio, FILE* file) ;
-
-extern void uty_set_height(vty_io vio) ;
-extern void uty_cmd_output_start(vty_io vio) ;
-
-extern void uty_file_set_readiness(vio_vf vf, enum vty_readiness ready) ;
-extern void uty_file_set_timer(vio_vf vf, unsigned long timeout) ;
-
-extern int uty_read (vty_io vio, keystroke steal) ;
-extern int utysh_read (vty_io vio, qstring cl, qstring buf) ;
-
extern const char* uty_get_name(vty_io vio) ;
extern void uty_set_monitor(vty_io vio, bool on) ;
@@ -366,19 +381,6 @@ extern void uty_set_monitor(vty_io vio, bool on) ;
* Inline Functions
*/
-/*------------------------------------------------------------------------------
- * Return the fd from a vio_fd structure
- */
-Inline int
-uty_vf_fd(vio_vf vf)
-{
- return vio_fd_fd(vf->vfd) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Return the fd from a vio_fd structure
- */
-
Inline bool
uty_is_terminal(struct vty *vty)
{
@@ -397,4 +399,41 @@ uty_is_shell_client(struct vty *vty)
return vty->type == VTY_SHELL_CLIENT ;
}
+/*------------------------------------------------------------------------------
+ * Command output -- append to output buffer.
+ */
+Inline int
+uty_vprintf(vty_io vio, const char *format, va_list args)
+{
+ return vio_fifo_vprintf(vio->obuf, format, args) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Clear command output -- discard anything in the buffer, but keep markers.
+ */
+Inline void
+uty_out_clear(vty_io vio)
+{
+ vio_fifo_clear(vio->obuf, false) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Accept command output -- advance any end_mark to current put position.
+ */
+Inline void
+uty_out_accept(vty_io vio)
+{
+ vio_fifo_step_end_mark(vio->obuf) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Reject command output -- discard anything after the end_mark in the buffer,
+ * but keep markers.
+ */
+Inline void
+uty_out_reject(vty_io vio)
+{
+ vio_fifo_back_to_end_mark(vio->obuf, true) ;
+} ;
+
#endif /* _ZEBRA_VTY_IO_H */
diff --git a/lib/vty_io_basic.c b/lib/vty_io_basic.c
index 3bead51f..85ea2587 100644
--- a/lib/vty_io_basic.c
+++ b/lib/vty_io_basic.c
@@ -21,9 +21,67 @@
* 02111-1307, USA.
*/
-#include "zebra.h"
-
#include "vty_io_basic.h"
+#include "vty_local.h"
+
+#include "memory.h"
+
+#include <sys/socket.h>
+#include <fcntl.h>
+
+/*==============================================================================
+ * Base level open operations -- files and pipes
+ *
+ */
+
+/*==============================================================================
+ * Try to open the given file for the given type of I/O.
+ *
+ * vfd_io_write => create if does not exist (mode 0600)
+ *
+ * vfd_io_append => set O_APPEND
+ * otherwise => truncate file if it does exist
+ *
+ * vfd_io_read => fail if does not exist
+ *
+ * vfd_io_read_write => create if does not exist (mode 0600)
+ *
+ * vfd_io_append => set O_APPEND
+ * otherwise => leave file as is
+ *
+ * vfd_io_blocking => do not open O_NONBLOCK
+ *
+ * (if none of the above, treat as vfd_io_read)
+ *
+ * Returns: if >= 0 -- the fd of the open file
+ * < 0 -- failed to open file -- see errno
+ */
+extern int
+uty_vfd_file_open(const char* name, vfd_io_type_t io_type)
+{
+ int oflag ;
+
+ oflag = 0 ;
+ if ((io_type & vfd_io_read_write) == vfd_io_read_write)
+ oflag = O_RDWR | O_CREAT ;
+ else if ((io_type & vfd_io_write) != 0)
+ oflag = O_WRONLY | O_CREAT ;
+ else
+ oflag = O_RDONLY ;
+
+ if ((io_type & vfd_io_write) != 0)
+ {
+ if ((io_type & vfd_io_append) != 0)
+ oflag |= O_APPEND ;
+ else if ((io_type & vfd_io_read) == 0)
+ oflag |= O_TRUNC ;
+ } ;
+
+ if ((io_type & vfd_io_blocking) == 0)
+ oflag |= O_NONBLOCK ;
+
+ return open(name, oflag, S_IRUSR | S_IWUSR) ;
+} ;
/*==============================================================================
* Base level I/O and Timer handling....
@@ -34,25 +92,23 @@
struct vio_io_set_args /* to CLI thread */
{
- bool active ; /* set when queued, cleared when dequeued */
- bool die ; /* set when is queued and vio_fd is closed */
- bool close ; /* close and free the vio_fd and mqb */
+ bool active ; /* set when queued, cleared when dequeued */
+ bool close ; /* close and free the vio_vfd and mqb */
bool readable ; /* set when read state to be changed */
- on_off_t read_on ; /* what to change read to */
+ on_off_b read_on ; /* what to change read to */
vty_timer_time read_timeout ;
/* what to set the timeout to, if any */
bool writable ; /* set when write state to be changed */
- on_off_t write_on ; /* what to change write to */
+ on_off_b write_on ; /* what to change write to */
vty_timer_time write_timeout ;
/* what to set the timeout to, if any */
} ;
MQB_ARGS_SIZE_OK(vio_io_set_args) ;
-static void vio_fd_mqb_dispatch(vio_fd vfd) ;
-static void vio_fd_mqb_free(vio_fd vfd) ;
-static struct vio_io_set_args* vio_fd_mqb_args(vio_fd vfd) ;
+static void vio_vfd_mqb_dispatch(vio_vfd vfd) ;
+static struct vio_io_set_args* vio_vfd_mqb_args(vio_vfd vfd) ;
/*==============================================================================
* File Descriptor handling
@@ -69,22 +125,22 @@ static struct vio_io_set_args* vio_fd_mqb_args(vio_fd vfd) ;
* see above.
*/
-static void vio_fd_qps_read_action(qps_file qf, void* file_info) ;
-static void vio_fd_qps_write_action(qps_file qf, void* file_info) ;
-static int vio_fd_thread_read_action(struct thread *thread) ;
-static int vio_fd_thread_write_action(struct thread *thread) ;
+static void vio_vfd_qps_read_action(qps_file qf, void* file_info) ;
+static void vio_vfd_qps_write_action(qps_file qf, void* file_info) ;
+static int vio_vfd_thread_read_action(struct thread *thread) ;
+static int vio_vfd_thread_write_action(struct thread *thread) ;
static void vio_timer_squelch(vio_timer_t* timer) ;
Inline void
-vio_fd_do_read_action(vio_fd vfd)
+vio_vfd_do_read_action(vio_vfd vfd)
{
if (vfd->active)
vfd->read_action(vfd, vfd->action_info) ;
}
Inline void
-vio_fd_do_write_action(vio_fd vfd)
+vio_vfd_do_write_action(vio_vfd vfd)
{
if (vfd->active)
vfd->write_action(vfd, vfd->action_info) ;
@@ -93,12 +149,12 @@ vio_fd_do_write_action(vio_fd vfd)
/*------------------------------------------------------------------------------
* Create a new vfd structure.
*/
-extern vio_fd
-vio_fd_new(int fd, vfd_type_t type, vfd_io_type_t io_type, void* action_info)
+extern vio_vfd
+vio_vfd_new(int fd, vfd_type_t type, vfd_io_type_t io_type, void* action_info)
{
- vio_fd vfd ;
+ vio_vfd vfd ;
- vfd = XCALLOC(MTYPE_VTY, sizeof(struct vio_fd)) ;
+ vfd = XCALLOC(MTYPE_VTY, sizeof(struct vio_vfd)) ;
/* Has set:
*
@@ -114,12 +170,12 @@ vio_fd_new(int fd, vfd_type_t type, vfd_io_type_t io_type, void* action_info)
* mqb -- NULL
*/
- vio_fd_set_fd(vfd, fd, type, io_type) ;
+ vio_vfd_set_fd(vfd, fd, type, io_type) ;
vio_timer_init(&vfd->read_timer, NULL, NULL) ;
vio_timer_init(&vfd->write_timer, NULL, NULL) ;
- vio_fd_set_action_info(vfd, action_info) ;
+ vio_vfd_set_action_info(vfd, action_info) ;
return vfd ;
} ;
@@ -127,13 +183,13 @@ vio_fd_new(int fd, vfd_type_t type, vfd_io_type_t io_type, void* action_info)
/*------------------------------------------------------------------------------
* If vfd was not fully set up when created, set it up now.
*
- * To close an active vfd, use vio_fd_close() !
+ * To close an active vfd, use vio_vfd_close() !
*
* NB: for use when vfd has been created, but the fd was not known at that
* time -- ie the vfd is NOT active.
*/
extern void
-vio_fd_set_fd(vio_fd vfd, int fd, vfd_type_t type, vfd_io_type_t io_type)
+vio_vfd_set_fd(vio_vfd vfd, int fd, vfd_type_t type, vfd_io_type_t io_type)
{
assert(!vfd->active) ;
@@ -144,74 +200,76 @@ vio_fd_set_fd(vio_fd vfd, int fd, vfd_type_t type, vfd_io_type_t io_type)
} ;
/*------------------------------------------------------------------------------
- * Set the read action field for the given vio_fd.
+ * Set the read action field for the given vio_vfd.
*/
extern void
-vio_fd_set_read_action(vio_fd vfd, vio_fd_action* action)
+vio_vfd_set_read_action(vio_vfd vfd, vio_vfd_action* action)
{
vfd->read_action = action ;
} ;
/*------------------------------------------------------------------------------
- * Set the write action field for the given vio_fd.
+ * Set the write action field for the given vio_vfd.
*/
extern void
-vio_fd_set_write_action(vio_fd vfd, vio_fd_action* action)
+vio_vfd_set_write_action(vio_vfd vfd, vio_vfd_action* action)
{
vfd->write_action = action ;
} ;
/*------------------------------------------------------------------------------
- * Set the read action field for the given vio_fd.
+ * Set the read action field for the given vio_vfd.
*/
extern void
-vio_fd_set_read_timeout_action(vio_fd vfd, vio_timer_action* action)
+vio_vfd_set_read_timeout_action(vio_vfd vfd, vio_timer_action* action)
{
vio_timer_set_action(&vfd->read_timer, action) ;
} ;
/*------------------------------------------------------------------------------
- * Set the write action field for the given vio_fd.
+ * Set the write action field for the given vio_vfd.
*/
extern void
-vio_fd_set_write_timeout_action(vio_fd vfd, vio_timer_action* action)
+vio_vfd_set_write_timeout_action(vio_vfd vfd, vio_timer_action* action)
{
vio_timer_set_action(&vfd->write_timer, action) ;
} ;
/*------------------------------------------------------------------------------
- * Set the action_info field for the given vio_fd read/write action.
+ * Set the action_info field for the given vio_vfd read/write action.
*/
extern void
-vio_fd_set_action_info(vio_fd vfd, void* action_info)
+vio_vfd_set_action_info(vio_vfd vfd, void* action_info)
{
vfd->action_info = action_info ;
vio_timer_set_info(&vfd->read_timer, action_info) ;
vio_timer_set_info(&vfd->write_timer, action_info) ;
} ;
+#if 0
/*------------------------------------------------------------------------------
- * If there is a read action set for the give vio_fd (if any), then kick it.
+ * If there is a read action set for the give vio_vfd (if any), then kick it.
*/
extern void
-vio_fd_do_read_action(vio_fd vfd)
+vio_vfd_kick_read_action(vio_vfd vfd)
{
if ((vfd != NULL) && (vfd->read_action != NULL))
- vio_fd_do_read_action(vfd) ;
+ vio_vfd_do_read_action(vfd) ;
} ;
/*------------------------------------------------------------------------------
- * If there is a write action set for the give vio_fd (if any), then kick it.
+ * If there is a write action set for the give vio_vfd (if any), then kick it.
*/
extern void
-vio_fd_do_write_action(vio_fd vfd)
+vio_vfd_kick_write_action(vio_vfd vfd)
{
if ((vfd != NULL) && (vfd->write_action != NULL))
- vio_fd_do_read_action(vfd) ;
+ vio_vfd_do_read_action(vfd) ;
} ;
+#endif
/*------------------------------------------------------------------------------
- * Half close the given vfd (if any).
+ * Close the read side of the given vfd (if any).
*
* If the vfd is a socket, then does a shutdown of the read side.
*
@@ -221,8 +279,8 @@ vio_fd_do_write_action(vio_fd vfd)
*
* Returns original vfd, or NULL if it has been closed.
*/
-extern vio_fd
-vio_fd_half_close(vio_fd vfd)
+extern vio_vfd
+vio_vfd_read_close(vio_vfd vfd)
{
VTY_ASSERT_LOCKED() ;
@@ -239,14 +297,14 @@ vio_fd_half_close(vio_fd vfd)
{
/* read & write, so really half-close if can */
if (vfd->type == vfd_socket)
- shutdown(vfd->fd, SHUT_RD) ;
- vio_fd_set_read(vfd, off, 0) ;
+ shutdown(vfd->fd, SHUT_RD) ; /* ignore errors TODO */
+ vio_vfd_set_read(vfd, off, 0) ;
vfd->io_type ^= vfd_io_read ; /* now write only ! */
}
else
{
/* read only, so fully close */
- vfd = vio_fd_close(vfd) ;
+ vfd = vio_vfd_close(vfd) ;
} ;
} ;
}
@@ -259,32 +317,31 @@ vio_fd_half_close(vio_fd vfd)
/*------------------------------------------------------------------------------
* If there is an fd, close it.
*
- * Stops any read/write waiting and releases all memory.
- *
- * NB: this can done from any thread, but if not done from the CLI thread,
- * and there is a qf, a message must be sent to the CLI thread to actually
- * implement: which passes the vio_fd to the CLI thread for later
- * close and destruction.
- *
- * The actual close has to be delayed, so that cannot open another fd
- * and bang into a still active qps_file !
- *
- * The message looks after freeing the vio_fd, the qps_file and the mqb.
+ * Stops any read/write waiting and releases all memory, including the vfd
+ * itself if required..
*/
static void
-vio_fd_do_close(vio_fd vfd)
+vio_vfd_do_close(vio_vfd vfd, free_keep_b free)
{
+ if (vfd == NULL)
+ return ;
+
VTY_ASSERT_LOCKED() ;
VTY_ASSERT_CLI_THREAD() ;
- if (vty_cli_nexus)
+ if (vty_nexus)
{
if (vfd->f.qf != NULL)
{
assert(vfd->fd == qps_file_fd(vfd->f.qf)) ;
vfd->f.qf = qps_file_free(vfd->f.qf) ;
} ;
- vio_fd_mqb_free(vfd) ;
+
+ if (vfd->mqb != NULL)
+ {
+ mqb_free(vfd->mqb) ;
+ vfd->mqb = NULL ;
+ } ;
}
else
{
@@ -306,12 +363,13 @@ vio_fd_do_close(vio_fd vfd)
} ;
if (vfd->fd >= 0)
- close(vfd->fd) ;
+ close(vfd->fd) ; /* ignores errors TODO */
vio_timer_reset(&vfd->read_timer) ;
vio_timer_reset(&vfd->write_timer) ;
- XFREE(MTYPE_VTY, vfd) ;
+ if (free == free_it)
+ XFREE(MTYPE_VTY, vfd) ;
} ;
/*------------------------------------------------------------------------------
@@ -322,16 +380,16 @@ vio_fd_do_close(vio_fd vfd)
*
* NB: this can done from any thread, but if not done from the CLI thread,
* and there is a qf, a message must be sent to the CLI thread to actually
- * implement: which passes the vio_fd to the CLI thread for later
+ * implement: which passes the vio_vfd to the CLI thread for later
* close and destruction.
*
* The actual close has to be delayed, so that cannot open another fd
* and bang into a still active qps_file !
*
- * The message looks after freeing the vio_fd, the qps_file and the mqb.
+ * The message looks after freeing the vio_vfd, the qps_file and the mqb.
*/
-extern vio_fd
-vio_fd_close(vio_fd vfd)
+extern vio_vfd
+vio_vfd_close(vio_vfd vfd)
{
VTY_ASSERT_LOCKED() ;
@@ -340,9 +398,9 @@ vio_fd_close(vio_fd vfd)
if (vfd->fd < 0)
{
- /* closing an inactive vio_fd -- make sure all is quiet */
+ /* closing an inactive vio_vfd -- make sure all is quiet */
assert(!vfd->active) ;
- if (vty_cli_nexus)
+ if (vty_nexus)
{
assert(vfd->f.qf == NULL) ;
}
@@ -355,19 +413,18 @@ vio_fd_close(vio_fd vfd)
}
else
{
- /* closing an active vio_fd */
+ /* closing an active vio_vfd */
if (vty_is_cli_thread())
{
/* In cli thread, so close directly */
- vio_fd_do_close(vfd) ;
+ vio_vfd_do_close(vfd, free_it) ;
}
else
{
/* Rats... have to send message to cli thread to close */
- struct vio_io_set_args* args = vio_fd_mqb_args(vfd) ;
+ struct vio_io_set_args* args = vio_vfd_mqb_args(vfd) ;
args->close = true ;
- args->die = true ;
/* in case something goes ready before the close message
* is processed, squelch.
@@ -381,7 +438,7 @@ vio_fd_close(vio_fd vfd)
vio_timer_squelch(&vfd->write_timer) ;
assert(vfd == mqb_get_arg0(vfd->mqb)) ;
- vio_fd_mqb_dispatch(vfd) ;
+ vio_vfd_mqb_dispatch(vfd) ;
} ;
} ;
@@ -389,16 +446,16 @@ vio_fd_close(vio_fd vfd)
} ;
/*------------------------------------------------------------------------------
- * Set or unset read ready state on given vio_fd (if any) if it is active.
+ * Set or unset read ready state on given vio_vfd (if any) if it is active.
*
- * If setting read_on, starts any read timeout timer.
+ * If setting read_on, starts any read timeout timer (or stops it if 0).
* If setting read off, stops any read timeout timer.
*
* NB: this can done from any thread, but if not done from the CLI thread,
* a message must be sent to the CLI thread to actually implement.
*/
-extern on_off_t
-vio_fd_set_read(vio_fd vfd, on_off_t on, vty_timer_time timeout)
+extern on_off_b
+vio_vfd_set_read(vio_vfd vfd, on_off_b on, vty_timer_time timeout)
{
struct vio_io_set_args* args ;
@@ -422,7 +479,7 @@ vio_fd_set_read(vio_fd vfd, on_off_t on, vty_timer_time timeout)
{
assert(vfd->read_action != NULL) ;
- if (vty_cli_nexus)
+ if (vty_nexus)
{
if (vfd->f.qf == NULL)
{
@@ -431,20 +488,20 @@ vio_fd_set_read(vio_fd vfd, on_off_t on, vty_timer_time timeout)
vfd->fd, vfd) ;
} ;
qps_enable_mode(vfd->f.qf, qps_read_mnum,
- vio_fd_qps_read_action) ;
+ vio_vfd_qps_read_action) ;
}
else
{
if (vfd->f.thread.read == NULL)
vfd->f.thread.read = thread_add_read(vty_master,
- vio_fd_thread_read_action, vfd, vfd->fd) ;
+ vio_vfd_thread_read_action, vfd, vfd->fd) ;
} ;
vio_timer_set(&vfd->read_timer, timeout) ;
}
else
{
- if (vty_cli_nexus)
+ if (vty_nexus)
{
if (vfd->f.qf != NULL)
qps_disable_modes(vfd->f.qf, qps_read_mbit) ;
@@ -462,19 +519,19 @@ vio_fd_set_read(vio_fd vfd, on_off_t on, vty_timer_time timeout)
{
/* In other threads, must send message to cli thread */
- args = vio_fd_mqb_args(vfd) ;
+ args = vio_vfd_mqb_args(vfd) ;
args->readable = true ;
args->read_on = on ;
args->read_timeout = timeout ;
vio_timer_squelch(&vfd->read_timer) ;
- vio_fd_mqb_dispatch(vfd) ;
+ vio_vfd_mqb_dispatch(vfd) ;
} ;
return on ;
} ;
/*------------------------------------------------------------------------------
- * Set or unset write ready state on given vio_fd (if any) if it is active.
+ * Set or unset write ready state on given vio_vfd (if any) if it is active.
*
* If setting write_on, starts any write timeout timer.
* If setting write off, stops any write timeout timer.
@@ -482,8 +539,8 @@ vio_fd_set_read(vio_fd vfd, on_off_t on, vty_timer_time timeout)
* NB: this can done from any thread, but if not done from the CLI thread,
* a message must be sent to the CLI thread to actually implement.
*/
-extern on_off_t
-vio_fd_set_write(vio_fd vfd, on_off_t on, vty_timer_time timeout)
+extern on_off_b
+vio_vfd_set_write(vio_vfd vfd, on_off_b on, vty_timer_time timeout)
{
struct vio_io_set_args* args ;
@@ -507,7 +564,7 @@ vio_fd_set_write(vio_fd vfd, on_off_t on, vty_timer_time timeout)
{
assert(vfd->write_action != NULL) ;
- if (vty_cli_nexus)
+ if (vty_nexus)
{
if (vfd->f.qf == NULL)
{
@@ -516,20 +573,20 @@ vio_fd_set_write(vio_fd vfd, on_off_t on, vty_timer_time timeout)
vfd->fd, vfd) ;
} ;
qps_enable_mode(vfd->f.qf, qps_write_mnum,
- vio_fd_qps_write_action) ;
+ vio_vfd_qps_write_action) ;
}
else
{
if (vfd->f.thread.write == NULL)
vfd->f.thread.write = thread_add_write(vty_master,
- vio_fd_thread_write_action, vfd, vfd->fd) ;
+ vio_vfd_thread_write_action, vfd, vfd->fd) ;
} ;
vio_timer_set(&vfd->write_timer, timeout) ;
}
else
{
- if (vty_cli_nexus)
+ if (vty_nexus)
{
if (vfd->f.qf != NULL)
qps_disable_modes(vfd->f.qf, qps_write_mbit) ;
@@ -547,12 +604,12 @@ vio_fd_set_write(vio_fd vfd, on_off_t on, vty_timer_time timeout)
{
/* In other threads, must send message to cli thread */
- args = vio_fd_mqb_args(vfd) ;
+ args = vio_vfd_mqb_args(vfd) ;
args->writable = true ;
args->write_on = on ;
args->write_timeout = timeout ;
vio_timer_squelch(&vfd->write_timer) ;
- vio_fd_mqb_dispatch(vfd) ;
+ vio_vfd_mqb_dispatch(vfd) ;
} ;
return on ;
@@ -567,19 +624,21 @@ vio_fd_set_write(vio_fd vfd, on_off_t on, vty_timer_time timeout)
* message is yet to be procesed.
*/
static void
-vio_fd_qps_read_action(qps_file qf, void* file_info)
+vio_vfd_qps_read_action(qps_file qf, void* file_info)
{
- vio_fd vfd = file_info ;
+ vio_vfd vfd ;
- VTY_LOCK() ;
VTY_ASSERT_CLI_THREAD() ;
+ VTY_LOCK() ;
+
+ vfd = file_info ;
assert((vfd->fd == qf->fd) && (vfd->f.qf == qf)) ;
qps_disable_modes(vfd->f.qf, qps_read_mbit) ;
vio_timer_unset(&vfd->read_timer) ;
- vio_fd_do_read_action(vfd) ;
+ vio_vfd_do_read_action(vfd) ;
VTY_UNLOCK() ;
} ;
@@ -593,19 +652,21 @@ vio_fd_qps_read_action(qps_file qf, void* file_info)
* message is yet to be procesed.
*/
static int
-vio_fd_thread_read_action(struct thread *thread)
+vio_vfd_thread_read_action(struct thread *thread)
{
- vio_fd vfd = THREAD_ARG (thread);
+ vio_vfd vfd ;
- VTY_LOCK() ;
VTY_ASSERT_CLI_THREAD() ;
+ VTY_LOCK() ;
+
+ vfd = THREAD_ARG(thread);
assert(vfd->fd == THREAD_FD(thread)) ;
vfd->f.thread.read = NULL ; /* implicitly */
vio_timer_unset(&vfd->read_timer) ;
- vio_fd_do_read_action(vfd) ;
+ vio_vfd_do_read_action(vfd) ;
VTY_UNLOCK() ;
return 0 ;
@@ -620,19 +681,19 @@ vio_fd_thread_read_action(struct thread *thread)
* message is yet to be procesed.
*/
static void
-vio_fd_qps_write_action(qps_file qf, void* file_info)
+vio_vfd_qps_write_action(qps_file qf, void* file_info)
{
- vio_fd vfd = file_info ;
+ vio_vfd vfd = file_info ;
- VTY_LOCK() ;
VTY_ASSERT_CLI_THREAD() ;
+ VTY_LOCK() ;
assert((vfd->fd == qf->fd) && (vfd->f.qf == qf)) ;
qps_disable_modes(vfd->f.qf, qps_write_mbit) ;
vio_timer_unset(&vfd->write_timer) ;
- vio_fd_do_write_action(vfd) ;
+ vio_vfd_do_write_action(vfd) ;
VTY_UNLOCK() ;
} ;
@@ -646,19 +707,19 @@ vio_fd_qps_write_action(qps_file qf, void* file_info)
* message is yet to be procesed.
*/
static int
-vio_fd_thread_write_action(struct thread *thread)
+vio_vfd_thread_write_action(struct thread *thread)
{
- vio_fd vfd = THREAD_ARG (thread);
+ vio_vfd vfd = THREAD_ARG (thread);
- VTY_LOCK() ;
VTY_ASSERT_CLI_THREAD() ;
+ VTY_LOCK() ;
assert(vfd->fd == THREAD_FD(thread)) ;
vio_timer_unset(&vfd->write_timer) ;
vfd->f.thread.write = NULL ; /* implicitly */
- vio_fd_do_write_action(vfd) ;
+ vio_vfd_do_write_action(vfd) ;
VTY_UNLOCK() ;
return 0 ;
@@ -675,57 +736,31 @@ vio_fd_thread_write_action(struct thread *thread)
* further dispatch is required. When it has been dequeued and processed,
* it is marked inactive.
*
- * If the vfd is closed while the message is active, it is marked to die,
+ * If the vfd is closed while the message is active, it is marked to close,
* which it will do when it is dequeued and actioned.
*/
-static void vio_fd_set_action(mqueue_block mqb, mqb_flag_t flag) ;
+static void vio_vfd_set_action(mqueue_block mqb, mqb_flag_t flag) ;
/*------------------------------------------------------------------------------
* Get mqb for the given vfd -- make one if required.
*/
static struct vio_io_set_args*
-vio_fd_mqb_args(vio_fd vfd)
+vio_vfd_mqb_args(vio_vfd vfd)
{
VTY_ASSERT_LOCKED() ;
if (vfd->mqb == NULL)
- vfd->mqb = mqb_init_new(NULL, vio_fd_set_action, vfd) ;
+ vfd->mqb = mqb_init_new(NULL, vio_vfd_set_action, vfd) ;
return mqb_get_args(vfd->mqb) ;
} ;
/*------------------------------------------------------------------------------
- * Free mqb for the given vfd -- if any.
- */
-static void
-vio_fd_mqb_free(vio_fd vfd)
-{
- VTY_ASSERT_LOCKED() ;
-
- if (vfd->mqb != NULL)
- {
- struct vio_io_set_args* args = mqb_get_args(vfd->mqb) ;
-
- if (args->active)
- {
- args->die = true ;
- mqb_set_arg0(vfd->mqb, NULL) ;
- }
- else
- {
- mqb_free(vfd->mqb) ;
- } ;
-
- vfd->mqb = NULL ;
- } ;
-} ;
-
-/*------------------------------------------------------------------------------
* Dispatch mqb, if not already active
*/
static void
-vio_fd_mqb_dispatch(vio_fd vfd)
+vio_vfd_mqb_dispatch(vio_vfd vfd)
{
struct vio_io_set_args* args = mqb_get_args(vfd->mqb) ;
@@ -741,33 +776,43 @@ vio_fd_mqb_dispatch(vio_fd vfd)
/*------------------------------------------------------------------------------
* Action routine for the read/write on/off setting message.
*
- * If the mqb is marked to die, then it and any qps_file it points to have been
- * cut loose, and now is the time to close the fd and release the qps_file,
- * along with releasing the mqb.
+ * If the mqb is marked to close, then it and any qps_file it points to have
+ * been cut loose, and now is the time to close the fd and release the
+ * qps_file, along with releasing the mqb.
+ *
+ * If the mqb is being revoked
*/
static void
-vio_fd_set_action(mqueue_block mqb, mqb_flag_t flag)
+vio_vfd_set_action(mqueue_block mqb, mqb_flag_t flag)
{
- struct vio_io_set_args* args = mqb_get_args(mqb) ;
- vio_fd vfd = mqb_get_arg0(mqb) ;
+ struct vio_io_set_args* args ;
+ vio_vfd vfd ;
- VTY_LOCK() ;
VTY_ASSERT_CLI_THREAD() ;
+ VTY_LOCK() ;
+
+ args = mqb_get_args(mqb) ;
+ vfd = mqb_get_arg0(mqb) ;
args->active = false ;
- if ((flag != mqb_destroy) && (!args->die))
+ if ((flag != mqb_destroy) && (!args->close))
{
if (args->readable)
- vio_fd_set_read(vfd, args->read_on, args->read_timeout) ;
+ vio_vfd_set_read(vfd, args->read_on, args->read_timeout) ;
if (args->writable)
- vio_fd_set_write(vfd, args->write_on, args->write_timeout) ;
+ vio_vfd_set_write(vfd, args->write_on, args->write_timeout) ;
}
else
{
- if (args->close)
- vio_fd_do_close(vfd) ;
- mqb_free(mqb) ;
+ /* Revoke and/or close.
+ *
+ * If close can free the vfd.
+ *
+ * If just revoke (should not happen), then cannot free the vfd, because
+ * somewhere there can be a dangling reference.
+ */
+ vio_vfd_do_close(vfd, args->close) ; /* frees the mqb */
} ;
VTY_UNLOCK() ;
@@ -779,7 +824,7 @@ vio_fd_set_action(mqueue_block mqb, mqb_flag_t flag)
*
*/
-static void vio_accept(vio_fd vfd, void* info) ;
+static void vio_accept(vio_vfd vfd, void* info) ;
/*------------------------------------------------------------------------------
* Create a new listener object for the newly opened listener socket.
@@ -790,7 +835,7 @@ static void vio_accept(vio_fd vfd, void* info) ;
* Returns address of newly created listener structure.
*/
extern vio_listener
-vio_listener_new(int fd, vio_fd_accept* accept_action)
+vio_listener_new(int fd, vio_vfd_accept* accept_action)
{
vio_listener listener ;
@@ -800,12 +845,12 @@ vio_listener_new(int fd, vio_fd_accept* accept_action)
listener = XCALLOC(MTYPE_VTY, sizeof(struct vio_listener)) ;
/* sets the next pointer to NULL */
- listener->vfd = vio_fd_new(fd, vfd_listener, vfd_io_read, listener) ;
+ listener->vfd = vio_vfd_new(fd, vfd_listener, vfd_io_read, listener) ;
listener->accept_action = accept_action ;
- vio_fd_set_read_action(listener->vfd, vio_accept) ;
- vio_fd_set_read(listener->vfd, on, 0) ;
+ vio_vfd_set_read_action(listener->vfd, vio_accept) ;
+ vio_vfd_set_read(listener->vfd, on, 0) ;
return listener ;
} ;
@@ -822,7 +867,7 @@ vio_listener_close(vio_listener listener)
VTY_ASSERT_LOCKED() ;
VTY_ASSERT_CLI_THREAD() ;
- vio_fd_close(listener->vfd) ;
+ vio_vfd_close(listener->vfd) ;
XFREE(MTYPE_VTY, listener) ;
} ;
@@ -832,7 +877,7 @@ vio_listener_close(vio_listener listener)
* info points at the listener object.
*/
static void
-vio_accept(vio_fd vfd, void* info)
+vio_accept(vio_vfd vfd, void* info)
{
vio_listener listener ;
@@ -842,6 +887,8 @@ vio_accept(vio_fd vfd, void* info)
listener = info ;
assert(vfd == listener->vfd) ;
+ vio_vfd_set_read(listener->vfd, on, 0) ;
+
listener->accept_action(vfd->fd) ;
} ;
@@ -902,7 +949,6 @@ static void
vio_timer_squelch(vio_timer_t* timer)
{
VTY_ASSERT_LOCKED() ;
- VTY_ASSERT_CLI_THREAD() ;
timer->squelch = true ;
} ;
@@ -920,7 +966,7 @@ vio_timer_reset(vio_timer_t* timer)
if (timer->t.anon != NULL)
{
- if (vty_cli_nexus)
+ if (vty_nexus)
qtimer_free(timer->t.qtr) ;
else
thread_cancel(timer->t.thread) ;
@@ -953,7 +999,7 @@ vio_timer_set(vio_timer_t* timer, vty_timer_time time)
assert(timer->action != NULL) ;
- if (vty_cli_nexus)
+ if (vty_nexus)
{
if (timer->t.qtr == NULL) /* allocate qtr if required */
timer->t.qtr = qtimer_init_new(NULL, vty_cli_nexus->pile,
@@ -983,7 +1029,7 @@ vio_timer_unset(vio_timer_t* timer)
if (timer->active)
{
- if (vty_cli_nexus)
+ if (vty_nexus)
{
assert(timer->t.qtr != NULL) ;
qtimer_unset(timer->t.qtr) ;
@@ -1007,13 +1053,15 @@ vio_timer_unset(vio_timer_t* timer)
static void
vio_timer_qtr_action(qtimer qtr, void* timer_info, qtime_t when)
{
- vio_timer_t* timer = timer_info ;
+ vio_timer_t* timer ;
vty_timer_time time ;
- VTY_LOCK() ;
VTY_ASSERT_CLI_THREAD() ;
+ VTY_LOCK() ;
+
+ timer = timer_info ;
- if (!timer->squelch) /* do nothing if squelched */
+ if ((timer->action != NULL) && (!timer->squelch))
{
time = timer->action(timer, timer->action_info) ;
if (time != 0)
@@ -1036,15 +1084,16 @@ vio_timer_qtr_action(qtimer qtr, void* timer_info, qtime_t when)
static int
vio_timer_thread_action(struct thread *thread)
{
- vio_timer_t* timer = THREAD_ARG (thread);
+ vio_timer_t* timer ;
vty_timer_time time ;
- VTY_LOCK() ;
VTY_ASSERT_CLI_THREAD() ;
+ VTY_LOCK() ;
+ timer = THREAD_ARG(thread) ;
timer->t.thread = NULL ; /* implicitly */
- if (!timer->squelch) /* do nothing if squelched */
+ if ((timer->action != NULL) && (!timer->squelch))
{
time = timer->action(timer, timer->action_info) ;
if (time != 0)
diff --git a/lib/vty_io_basic.h b/lib/vty_io_basic.h
index a2b1cbb3..00ff923b 100644
--- a/lib/vty_io_basic.h
+++ b/lib/vty_io_basic.h
@@ -27,7 +27,7 @@
#include "misc.h"
-#include "vty.h"
+//#include "vty_local.h"
#include "qpselect.h"
#include "thread.h"
@@ -55,15 +55,23 @@ typedef enum vfd_type vfd_type_t ;
enum vfd_io_type /* NB: *bit*significant* */
{
vfd_io_none = 0,
- vfd_io_read = 1,
- vfd_io_write = 2,
- vfd_io_read_write = 3,
+
+ vfd_io_read = BIT(0),
+ vfd_io_write = BIT(1),
+ vfd_io_read_write = vfd_io_read | vfd_io_write,
+
+ vfd_io_append = BIT(2),
+ vfd_io_blocking = BIT(3),
} ;
typedef enum vfd_io_type vfd_io_type_t ;
/*------------------------------------------------------------------------------
* Timers -- implemented as qtimer or thread timer, depending on environment.
+ *
+ * Timer action function returns a new value for the timer; 0 => off.
*/
+typedef unsigned long vty_timer_time ; /* Time out time in seconds */
+
typedef struct vio_timer vio_timer_t ;
typedef vty_timer_time vio_timer_action(vio_timer_t* timer, void* action_info) ;
@@ -88,10 +96,11 @@ struct vio_timer
* Implemented as qps_file or as read/write thread, depending on the
* environment.
*/
-typedef struct vio_fd* vio_fd ;
-typedef void vio_fd_action(vio_fd vfd, void* action_info) ;
+typedef struct vio_vfd* vio_vfd ;
+
+typedef void vio_vfd_action(vio_vfd vfd, void* action_info) ;
-struct vio_fd
+struct vio_vfd
{
int fd ;
bool active ; /* used for close message */
@@ -99,8 +108,8 @@ struct vio_fd
vfd_type_t type ; /* used for half-close */
vfd_io_type_t io_type ; /* read, write, read/write */
- vio_fd_action* read_action ;
- vio_fd_action* write_action ;
+ vio_vfd_action* read_action ;
+ vio_vfd_action* write_action ;
vio_timer_t read_timer ;
vio_timer_t write_timer ;
@@ -127,39 +136,41 @@ struct vio_fd
typedef struct vio_listener* vio_listener ;
-typedef void vio_fd_accept(int fd) ;
+typedef void vio_vfd_accept(int fd) ;
struct vio_listener
{
vio_listener next ; /* ssl type list */
- vio_fd vfd ;
- vio_fd_accept* accept_action ;
+ vio_vfd vfd ;
+ vio_vfd_accept* accept_action ;
};
/*==============================================================================
* Functions
*/
-extern vio_fd vio_fd_new(int fd, vfd_type_t type,
+extern int uty_vfd_file_open(const char* name, vfd_io_type_t io_type) ;
+
+extern vio_vfd vio_vfd_new(int fd, vfd_type_t type,
vfd_io_type_t io_type, void* action_info) ;
-extern void vio_fd_set_fd(vio_fd vfd, int fd, vfd_type_t type,
+extern void vio_vfd_set_fd(vio_vfd vfd, int fd, vfd_type_t type,
vfd_io_type_t io_type) ;
-extern void vio_fd_set_read_action(vio_fd vfd, vio_fd_action* action) ;
-extern void vio_fd_set_write_action(vio_fd vfd, vio_fd_action* action) ;
-extern void vio_fd_set_read_timeout_action(vio_fd vfd,
+extern void vio_vfd_set_read_action(vio_vfd vfd, vio_vfd_action* action) ;
+extern void vio_vfd_set_write_action(vio_vfd vfd, vio_vfd_action* action) ;
+extern void vio_vfd_set_read_timeout_action(vio_vfd vfd,
vio_timer_action* action) ;
-extern void vio_fd_set_write_timeout_action(vio_fd vfd,
+extern void vio_vfd_set_write_timeout_action(vio_vfd vfd,
vio_timer_action* action) ;
-extern void vio_fd_set_action_info(vio_fd vfd, void* action_info) ;
-extern vio_fd vio_fd_half_close(vio_fd vfd) ;
-extern vio_fd vio_fd_close(vio_fd vfd) ;
-extern on_off_t vio_fd_set_read(vio_fd vfd, on_off_t on,
+extern void vio_vfd_set_action_info(vio_vfd vfd, void* action_info) ;
+extern vio_vfd vio_vfd_read_close(vio_vfd vfd) ;
+extern vio_vfd vio_vfd_close(vio_vfd vfd) ;
+extern on_off_b vio_vfd_set_read(vio_vfd vfd, on_off_b on,
vty_timer_time timeout) ;
-extern on_off_t vio_fd_set_write(vio_fd vfd, on_off_t on,
+extern on_off_b vio_vfd_set_write(vio_vfd vfd, on_off_b on,
vty_timer_time timeout) ;
-Inline int vio_fd_fd(vio_fd vfd) ;
+Inline int vio_vfd_fd(vio_vfd vfd) ;
-extern vio_listener vio_listener_new(int fd, vio_fd_accept* accept) ;
+extern vio_listener vio_listener_new(int fd, vio_vfd_accept* accept) ;
extern void vio_listener_close(vio_listener listener) ;
extern void vio_timer_init(vio_timer_t* timer, vio_timer_action* action,
@@ -175,10 +186,10 @@ extern void vio_timer_unset(vio_timer_t* timer) ;
*/
/*------------------------------------------------------------------------------
- * Return the fd from a vio_fd structure
+ * Return the fd from a vio_vfd structure
*/
Inline int
-vio_fd_fd(vio_fd vfd)
+vio_vfd_fd(vio_vfd vfd)
{
return vfd->fd ;
} ;
diff --git a/lib/vty_io_file.c b/lib/vty_io_file.c
index ed2c35e8..2d17276a 100644
--- a/lib/vty_io_file.c
+++ b/lib/vty_io_file.c
@@ -1,7 +1,6 @@
-/* VTY Log Functions
- * Copyright (C) 1997, 98 Kunihiro Ishiguro
+/* VTY I/O for Files
*
- * Revisions: Copyright (C) 2010 Chris Hall (GMCH), Highwayman
+ * Copyright (C) 2010 Chris Hall (GMCH), Highwayman
*
* This file is part of GNU Zebra.
*
@@ -20,2589 +19,304 @@
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
+#include "misc.h"
-#include "zebra.h"
+#include "command_local.h"
-#include "vty.h"
-#include "vty_io.h"
-#include "vty_cli.h"
-#include "qstring.h"
-#include "keystroke.h"
-
-#include "memory.h"
-
-#include "prefix.h"
-#include "filter.h"
-#include "privs.h"
-#include "sockunion.h"
-#include "network.h"
-
-#include <arpa/telnet.h>
-#include <sys/un.h> /* for VTYSH */
-#include <sys/socket.h>
-
-#define VTYSH_DEBUG 0
+#include "vty_io_file.h"
+#include "vty_io_basic.h"
/*==============================================================================
- * VTY Log Output -- provides a "vty" interface to logging.
+ * VTY File Output
*
- * Once a VTY_LOG object is created, can be used as an (output only) vty.
+ * This is for input and output of configuration files and piped stuff.
*
- * All output is sent to the logging system,
- *
- *
- * During command processing the output sent here is held until the command
- * completes.
+ * When reading the configuration (and piped stuff in the configuration) I/O
+ * is blocking... nothing else can run while this is going on. Otherwise,
+ * all I/O is non-blocking.
*/
-static int uty_config_write(vty_io vio, bool all) ;
-
-/*------------------------------------------------------------------------------
- * VTY output function -- cf fprintf
- *
- * Returns: >= 0 => OK
- * < 0 => failed (see errno)
- */
-extern int
-uty_out (struct vty *vty, const char *format, ...)
-{
- int result;
- VTY_ASSERT_LOCKED() ;
- va_list args;
- va_start (args, format);
- result = uty_vout(vty, format, args);
- va_end (args);
- return result;
-}
-
-/*------------------------------------------------------------------------------
- * VTY output function -- cf vfprintf
- *
- * Returns: >= 0 => OK
- * < 0 => failed (see errno)
- *
- * NB: for VTY_TERM and for VTY_SHELL_SERV -- this is command output:
- *
- * * MAY NOT do any command output if !cmd_enabled
- *
- * * first, the life of a vty is not guaranteed unless cmd_in_progress,
- * so should not attempt to use a vty anywhere other than command
- * execution.
- *
- * * second, cmd_out_enabled is false most of the time, and is only
- * set true when a command completes, and it is time to write away
- * the results.
- *
- * * all output is placed in the vio->cmd_obuf. When the command completes,
- * the contents of the cmd_obuf will be written away -- subject to line
- * control.
- *
- * * output is discarded if the vty is no longer write_open
- */
-extern int
-uty_vout(struct vty *vty, const char *format, va_list args)
-{
- vty_io vio ;
- int ret ;
-
- VTY_ASSERT_LOCKED() ;
-
- vio = vty->vio ;
-
- switch (vio->type)
- {
- case VTY_STDOUT:
- case VTY_SHELL:
- ret = vprintf (format, args) ;
- break ;
-
- case VTY_STDERR:
- ret = vfprintf (stderr, format, args) ;
- break ;
-
- case VTY_CONFIG_WRITE:
- ret = vio_fifo_vprintf(&vio->cmd_obuf, format, args) ;
- if ((ret > 0) && vio_fifo_full_lump(&vio->cmd_obuf))
- ret = uty_config_write(vio, false) ;
- break ;
-
- case VTY_TERM:
- case VTY_SHELL_SERV:
- assert(vio->cmd_in_progress) ;
-
- if (!vio->sock.write_open)
- return 0 ; /* discard output if not open ! */
- /* fall through.... */
-
- case VTY_CONFIG_READ:
- ret = vio_fifo_vprintf(&vio->cmd_obuf, format, args) ;
- break ;
-
- default:
- zabort("impossible VTY type") ;
- } ;
-
- return ret ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Clear the contents of the command output FIFO etc.
- *
- * NB: does not change any of the cli_blocked/cmd_in_progress/cli_wait_more/etc
- * flags -- competent parties must deal with those
- */
-extern void
-uty_out_clear(vty_io vio)
-{
- VTY_ASSERT_LOCKED() ;
-
- vio_fifo_clear(&vio->cmd_obuf) ;
-
- if (vio->cmd_lc != NULL)
- vio_lc_clear(vio->cmd_lc) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Flush the contents of the command output FIFO to the given file.
- *
- * Takes no notice of any errors !
- */
-extern void
-uty_out_fflush(vty_io vio, FILE* file)
-{
- char* src ;
- size_t have ;
-
- VTY_ASSERT_LOCKED() ;
-
- fflush(file) ;
-
- while ((src = vio_fifo_get_lump(&vio->cmd_obuf, &have)) != NULL)
- {
- fwrite(src, 1, have, file) ;
- vio_fifo_got_upto(&vio->cmd_obuf, src + have) ;
- } ;
-
- fflush(file) ;
-} ;
/*==============================================================================
* Prototypes.
*/
-static void uty_sock_init_new(vio_sock sock, int fd, void* info) ;
-static void uty_sock_half_close(vio_sock sock) ;
-static void uty_sock_close(vio_sock sock) ;
-
-static void vty_read_qnexus (qps_file qf, void* file_info) ;
-static void vty_write_qnexus (qps_file qf, void* file_info) ;
-static void vty_timer_qnexus (qtimer qtr, void* timer_info, qtime_t when) ;
-
-static int vty_read_thread (struct thread *thread) ;
-static int vty_write_thread (struct thread *thread) ;
-static int vty_timer_thread (struct thread *thread) ;
-
-static void vtysh_read_qnexus (qps_file qf, void* file_info) ;
-static int vtysh_read_thread (struct thread *thread) ;
-
-static enum vty_readiness uty_write(vty_io vio) ;
-
-/*==============================================================================
- * Creation and destruction of VTY objects
- */
/*------------------------------------------------------------------------------
- * Allocate new vty struct
- *
- * Allocates and initialises basic vty and vty_io structures, setting the
- * given type.
- *
- * Note that where is not setting up a vty_sock, this *may* be called from
- * any thread.
*
- * NB: may not create a VTY_CONFIG_WRITE type vty directly
- *
- * see: vty_open_config_write() and vty_close_config_write()
- *
- * NB: the sock_fd *must* be valid for VTY_TERM and VTY_SHELL_SERV.
- * (So MUST be in the CLI thread to set those up !)
- *
- * the sock_fd is ignored for everything else.
- *
- * Returns: new vty
- */
-extern struct vty *
-uty_new(enum vty_type type, int sock_fd)
-{
- struct vty *vty ;
- struct vty_io* vio ;
-
- VTY_ASSERT_LOCKED() ;
-
- /* If this is a VTY_TERM or a VTY_SHELL, place */
- switch (type)
- {
- case VTY_TERM: /* Require fd -- Telnet session */
- case VTY_SHELL_SERV: /* Require fd -- Unix socket */
- assert(sock_fd >= 0) ;
- break ;
-
- case VTY_CONFIG_WRITE:
- zabort("may not make a new VTY_CONFIG_WRITE VTY") ;
- break ;
-
- case VTY_CONFIG_READ:
- case VTY_STDOUT:
- case VTY_STDERR:
- case VTY_SHELL:
- sock_fd = -1 ; /* No fd -- output to stdout/stderr */
- break ;
-
- default:
- zabort("unknown VTY type") ;
- } ;
-
- /* Basic allocation */
- vty = XCALLOC (MTYPE_VTY, sizeof (struct vty));
- vio = XCALLOC (MTYPE_VTY, sizeof (struct vty_io)) ;
-
- vty->vio = vio ;
- vio->vty = vty ;
-
- /* Zeroising the vty_io structure has set:
- *
- * name = NULL -- no name, yet
- *
- * vio_list both pointers NULL
- * mon_list both pointers NULL
- *
- * half_closed = 0 -- NOT half closed (important !)
- * closed = 0 -- NOT closed (important !)
- * close_reason = NULL -- no reason, yet
- *
- * real_type = 0 -- not material
- * file_fd = 0 -- not material
- * file_error = 0 -- not material
- *
- * key_stream = NULL -- no key stream (always empty, at EOF)
- *
- * cli_drawn = 0 -- not drawn
- * cli_dirty = 0 -- not dirty
- * cli_prompt_len = 0 )
- * cli_extra_len = 0 ) not material
- * cli_echo_suppress = 0 )
- *
- * cli_prompt_node = 0 -- not material
- * cli_prompt_set = 0 -- so prompt needs to be constructed
- *
- * cli_blocked = 0 -- not blocked
- * cmd_in_progress = 0 -- no command in progress
- * cmd_out_enabled = 0 -- command output is disabled
- * cli_wait_more = 0 -- not waiting for response to "--more--"
- *
- * cli_more_enabled = 0 -- not enabled for "--more--"
- *
- * cmd_out_done = 0 -- not material
- *
- * cli_do = 0 == cli_do_nothing
- *
- * cmd_lc = NULL -- no line control
- *
- * fail = 0 -- no login failures yet
- *
- * hist = empty vector
- * hp = 0 -- at the beginning
- * hindex = 0 -- the beginning
- *
- * width = 0 -- unknown console width
- * height = 0 -- unknown console height
- *
- * lines = 0 -- no limit
- * lines_set = 0 -- no explicit setting
- *
- * monitor = 0 -- not a monitor
- * monitor_busy = 0 -- not a busy monitor
- *
- * config = 0 -- not holder of "config" mode
- */
- confirm(cli_do_nothing == 0) ;
- confirm(AUTH_NODE == 0) ; /* default node type */
-
- vio->type = type ;
-
- /* Zeroising the vty structure has set:
- *
- * node = 0 TODO: something better for node value ????
- * buf = NULL -- no command line, yet
- * parsed = NULL -- no parsed command, yet
- * lineno = 0 -- nothing read, yet
- * index = NULL -- nothing, yet
- * index_sub = NULL -- nothing, yet
- */
-
- /* Initialise the vio_sock, */
- uty_sock_init_new(&vio->sock, sock_fd, vio) ;
-
- /* Make sure all buffers etc. are initialised clean and empty.
- *
- * Note that no buffers are actually allocated at this stage.
- */
- qs_init_new(&vio->cli_prompt_for_node, 0) ;
-
- qs_init_new(&vio->cl, 0) ;
- qs_init_new(&vio->clx, 0) ;
-
- vio_fifo_init_new(&vio->cli_obuf, 2 * 1024) ; /* allocate in 2K lumps */
-
- vio_fifo_init_new(&vio->cmd_obuf, 8 * 1024) ;
-
- /* Place on list of known vio/vty */
- sdl_push(vio_list_base, vio, vio_list) ;
-
- return vty;
-} ;
-
-/*------------------------------------------------------------------------------
- * Create new vty of type VTY_TERM -- ie attached to a telnet session.
*
- * Returns: new vty
*/
-static struct vty *
-uty_new_term(int sock_fd, union sockunion *su)
+extern cmd_return_code_t
+uty_file_read_open(vty_io vio, qstring name, bool reflect)
{
- struct vty *vty ;
- vty_io vio ;
- enum vty_readiness ready ;
-
- VTY_ASSERT_LOCKED() ;
- VTY_ASSERT_CLI_THREAD() ;
-
- /* Allocate new vty structure and set up default values. */
- vty = uty_new (VTY_TERM, sock_fd) ;
- vio = vty->vio ;
-
- /* Allocate and initialise a keystroke stream TODO: CSI ?? */
- vio->key_stream = keystroke_stream_new('\0', uty_cli_iac_callback, vio) ;
-
- /* Set the socket action functions */
- if (vty_cli_nexus)
- {
- vio->sock.action.read.qnexus = vty_read_qnexus ;
- vio->sock.action.write.qnexus = vty_write_qnexus ;
- vio->sock.action.timer.qnexus = vty_timer_qnexus ;
- }
- else
- {
- vio->sock.action.read.thread = vty_read_thread ;
- vio->sock.action.write.thread = vty_write_thread ;
- vio->sock.action.timer.thread = vty_timer_thread ;
- } ;
-
- /* The text form of the address identifies the VTY */
- vio->name = sockunion_su2str (su, MTYPE_VTY_NAME);
-
- /* Set the initial node */
- if (no_password_check)
- {
- if (restricted_mode)
- vty->node = RESTRICTED_NODE;
- else if (host.advanced)
- vty->node = ENABLE_NODE;
- else
- vty->node = VIEW_NODE;
- }
- else
- vty->node = AUTH_NODE;
-
- /* Pick up current timeout setting */
- vio->sock.v_timeout = vty_timeout_val;
-
- /* Use global 'lines' setting, as default. May be -1 => unset */
- vio->lines = host.lines ;
-
- /* For VTY_TERM use vio_line_control for '\n' and "--more--" */
- vio->cmd_lc = vio_lc_init_new(NULL, 0, 0) ;
- uty_set_height(vio) ; /* set initial state */
-
- /* Initialise the CLI, ready for start-up messages etc. */
- uty_cli_init(vio) ;
-
- /* Reject connection if password isn't set, and not "no password" */
- if ((host.password == NULL) && (host.password_encrypt == NULL)
- && ! no_password_check)
- {
- uty_half_close (vio, "Vty password is not set.");
- vty = NULL;
- }
- else
- {
- /* Say hello to the world. */
- vty_hello (vty);
-
- if (! no_password_check)
- uty_out (vty, "%sUser Access Verification%s%s", VTY_NEWLINE,
- VTY_NEWLINE, VTY_NEWLINE);
- } ;
+ const char* name_str ;
+ int fd ;
+ vio_vf vf ;
+ vfd_io_type_t iot ;
- /* Now start the CLI and set a suitable state of readiness */
- ready = uty_cli_start(vio) ;
- uty_sock_set_readiness(&vio->sock, ready) ;
+ name_str = qs_make_string(name) ;
- return vty;
-} ;
+ iot = vfd_io_read | vfd_io_blocking ; /* TODO blocking */
-/*------------------------------------------------------------------------------
- * Create new vty of type VTY_SHELL_SERV -- ie attached to a vtysh session.
- *
- * Returns: new vty
- */
-static struct vty *
-uty_new_shell_serv(int sock_fd)
-{
- struct vty *vty ;
- vty_io vio ;
+ /* Do the basic file open. */
+ fd = uty_vfd_file_open(name_str, iot) ;
- VTY_ASSERT_LOCKED() ;
-
- /* Allocate new vty structure and set up default values. */
- vty = uty_new (VTY_SHELL_SERV, sock_fd) ;
- vio = vty->vio ;
-
- /* Set the action functions */
- if (vty_cli_nexus)
+ if (fd < 0)
{
- vio->sock.action.read.qnexus = vtysh_read_qnexus ;
- vio->sock.action.write.qnexus = vty_write_qnexus ;
- vio->sock.action.timer.qnexus = NULL ;
- }
- else
- {
- vio->sock.action.read.thread = vtysh_read_thread ;
- vio->sock.action.write.thread = vty_write_thread ;
- vio->sock.action.timer.thread = NULL ;
- } ;
-
- vty->node = VIEW_NODE;
+ uty_out(vio, "%% Could not open input file %s\n", name_str) ;
- /* Kick start the CLI etc. */
- uty_sock_set_readiness(&vio->sock, write_ready) ;
-
- return vty;
-} ;
-
-/*------------------------------------------------------------------------------
- * Set/Clear "monitor" state:
- *
- * set: if VTY_TERM and not already "monitor" (and write_open !)
- * clear: if is "monitor"
- */
-extern void
-uty_set_monitor(vty_io vio, bool on)
-{
- VTY_ASSERT_LOCKED() ;
-
- if (on && !vio->monitor)
- {
- if ((vio->type == VTY_TERM) && vio->sock.write_open)
- {
- vio->monitor = 1 ;
- sdl_push(vio_monitors_base, vio, mon_list) ;
- } ;
- }
- else if (!on && vio->monitor)
- {
- vio->monitor = 0 ;
- sdl_del(vio_monitors_base, vio, mon_list) ;
+ return CMD_WARNING ;
}
-} ;
-
-/*------------------------------------------------------------------------------
- * Return "name" of VTY
- *
- * For VTY_TERM this is the IP address of the far end of the telnet connection.
- */
-extern const char*
-uty_get_name(vty_io vio)
-{
- return (vio->name != NULL) ? vio->name : "?" ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Closing down VTY for reading.
- *
- * For VTY_TERM (must be in CLI thread):
- *
- * * shut the socket for reading
- * * discard all buffered input, setting it to "EOF"
- * * turns off any monitor status !
- * * drop down to RESTRICTED_NODE
- *
- * For VTY_SHELL_SERV (must be in CLI thread):
- *
- * * shut the socket for reading
- * * discard all buffered input
- * * drop down to RESTRICTED_NODE
- *
- * In all cases:
- *
- * * place on death watch
- * * set the vty half_closed
- * * sets the reason for closing (if any given)
- *
- * For VTY_TERM and VTY_SHELL_SERV, when the output side has emptied out all
- * the buffers, the VTY is closed.
- *
- * May already have set the vio->close_reason, or can set it now. (Passing a
- * NULL reason has no effect on any existing posted reason.)
- */
-extern void
-uty_half_close (vty_io vio, const char* reason)
-{
- VTY_ASSERT_LOCKED() ;
-
- if (vio->half_closed)
- return ;
-
- if (reason != NULL)
- vio->close_reason = reason ;
-
- /* Do the file side of things
- *
- * Note that half closing the file sets a new timeout, sets read off
- * and write on.
- */
- uty_sock_half_close(&vio->sock) ;
- uty_set_monitor(vio, 0) ;
-
- /* Discard everything in the keystroke stream and force it to EOF */
- if (vio->key_stream != NULL)
- keystroke_stream_set_eof(vio->key_stream) ;
-
- /* Turn off "--more--" so that all output clears without interruption.
- *
- * If is sitting on a "--more--" prompt, then exit the wait_more CLI.
- */
- vio->cli_more_enabled = 0 ;
-
- if (vio->cli_more_wait)
- uty_cli_exit_more_wait(vio) ;
-
- /* If a command is not in progress, enable output, which will clear
- * the output buffer if there is anything there, plus any close reason,
- * and then close.
- *
- * If command is in progress, then this process will start when it
- * completes.
- */
- if (!vio->cmd_in_progress)
- vio->cmd_out_enabled = 1 ;
-
- /* Make sure no longer holding the config symbol of power */
- uty_config_unlock(vio->vty, RESTRICTED_NODE) ;
- /* Log closing of VTY_TERM */
- if (vio->type == VTY_TERM)
- uzlog (NULL, LOG_INFO, "Vty connection (fd %d) close", vio->sock.fd) ;
+ /* OK -- now push the new input onto the vin_stack. */
+ vf = uty_vf_new(vio, name_str, fd, vfd_file, iot) ;
+ uty_vin_open(vio, vf, VIN_FILE, NULL, NULL, 16 * 1024) ;
- /* Move to the death watch list */
- sdl_del(vio_list_base, vio, vio_list) ;
- sdl_push(vio_death_watch, vio, vio_list) ;
+ vf->parse_type = cmd_parse_strict ;
+ vf->reflect_enabled = reflect ;
- vio->half_closed = 1 ;
+ return CMD_SUCCESS ;
} ;
/*------------------------------------------------------------------------------
- * Closing down VTY.
*
- * Shuts down everything and discards all buffers etc. etc.
*
- * If cmd_in_progress, cannot complete the process -- but sets the closed
- * flag.
- *
- * Can call vty_close() any number of times.
- *
- * The vty structure is placed on death watch, which will finally free the
- * structure once no longer cmd_in_progress.
- */
-extern void
-uty_close (vty_io vio)
-{
- VTY_ASSERT_LOCKED() ;
-
- /* Empty all the output buffers */
- vio_fifo_reset_keep(&vio->cli_obuf) ;
- vio_fifo_reset_keep(&vio->cmd_obuf) ;
- vio->cmd_lc = vio_lc_reset_free(vio->cmd_lc) ;
-
- /* If not already closed, close. */
- if (!vio->closed)
- {
- uty_half_close(vio, NULL) ; /* place on death watch -- if not
- already done */
- if (vio->type == VTY_TERM)
- uty_cli_close(vio) ; /* tell the CLI to stop */
-
- vio->closed = 1 ; /* now closed (stop uty_write()
- from recursing) */
-
- if (vio->sock.write_open)
- uty_write(vio) ; /* last gasp attempt */
-
- uty_sock_close(&vio->sock) ;
-
- } ;
-
- /* Nothing more should happen, so can now release almost everything,
- * the exceptions being the things that are related to a cmd_in_progress.
- *
- * All writing to buffers is suppressed, and as the sock has been closed,
- * there will be no more read_ready or write_ready events.
- */
- if (vio->name != NULL)
- XFREE(MTYPE_VTY_NAME, vio->name) ;
-
- vio->key_stream = keystroke_stream_free(vio->key_stream) ;
-
- qs_reset(&vio->cli_prompt_for_node, keep_it) ;
- qs_reset(&vio->cl, keep_it) ;
-
- {
- qstring line ;
- while ((line = vector_ream(vio->hist, keep_it)) != NULL)
- qs_reset_free(line) ;
- } ;
-
- /* The final stage cannot be completed if cmd_in_progress.
- *
- * The clx is pointed at by vty->buf -- containing the current command.
- *
- * Once everything is released, can take the vty off death watch, and
- * release the vio and the vty.
- */
- if (!vio->cmd_in_progress)
- {
- qs_reset(&vio->clx, keep_it) ;
- vio->vty->buf = NULL ;
- } ;
-} ;
-
-/*==============================================================================
- * For writing configuration file by command, temporarily redirect output to
- * an actual file.
*/
-
-/*------------------------------------------------------------------------------
- * Set the given fd as the VTY_FILE output.
- */
-extern void
-vty_open_config_write(struct vty* vty, int fd)
+extern cmd_return_code_t
+uty_file_write_open(vty_io vio, qstring name, bool append)
{
- vty_io vio ;
-
- VTY_LOCK() ;
+ const char* name_str ;
+ int fd ;
+ vio_vf vf ;
+ vfd_io_type_t iot ;
- vio = vty->vio ;
+ iot = vfd_io_write | vfd_io_blocking ; /* TODO blocking */
- assert((vio->type != VTY_CONFIG_WRITE) && (vio->type != VTY_NONE)) ;
+ if (append)
+ iot |= vfd_io_append ;
- vio->real_type = vio->type ;
+ name_str = qs_make_string(name) ;
- vio->type = VTY_CONFIG_WRITE ;
- vio->file_fd = fd ;
- vio->file_error = 0 ;
+ /* Do the basic file open. */
+ fd = uty_vfd_file_open(name_str, iot) ;
- VTY_UNLOCK() ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Write away configuration file stuff -- all or just the full lump(s).
- *
- * Returns: > 0 => blocked
- * 0 => all gone (up to last lump if !all)
- * < 0 => failed -- see vio->file_error
- */
-static int
-uty_config_write(vty_io vio, bool all)
-{
- int ret ;
-
- VTY_ASSERT_LOCKED() ;
-
- if (vio->file_error == 0)
+ if (fd < 0)
{
- ret = vio_fifo_write_nb(&vio->cmd_obuf, vio->file_fd, all) ;
+ uty_out(vio, "%% Could not open output file %s\n", name_str) ;
- if (ret < 0)
- vio->file_error = errno ;
+ return CMD_WARNING ;
}
- else
- ret = -1 ;
-
- return ret ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Write away any pending stuff, and return the VTY to normal.
- */
-extern int
-vty_close_config_write(struct vty* vty)
-{
- vty_io vio ;
- int err ;
-
- VTY_LOCK() ;
-
- vio = vty->vio ;
-
- assert((vio->type == VTY_CONFIG_WRITE) && (vio->real_type != VTY_NONE)) ;
- uty_config_write(vio, true) ; /* write all that is left */
+ /* OK -- now push the new input onto the vin_stack. */
+ vf = uty_vf_new(vio, name_str, fd, vfd_file, iot) ;
+ uty_vout_open(vio, vf, VOUT_FILE, NULL, NULL, 16 * 1024) ;
- err = vio->file_error ;
+ vf->out_enabled = true ;
- vio->type = vio->real_type ;
- vio->file_fd = -1 ;
- vio->file_error = 0 ;
-
- VTY_UNLOCK() ;
-
- return err ;
+ return CMD_SUCCESS ;
} ;
/*==============================================================================
- * vio_sock level operations
- */
-
-/*------------------------------------------------------------------------------
- * Initialise a new vio_sock structure.
- *
- * Requires that: the vio_sock structure is not currently in use.
- *
- * if fd >= 0 then: sock is open and ready read and write
- * otherwise: sock is not open
- *
- * there are no errors, yet.
- *
- * Sets timeout to no timeout at all -- timeout is optional.
- *
- * NB: MUST be in the CLI thread if the fd is >= 0 !
- */
-static void
-uty_sock_init_new(vio_sock sock, int fd, void* info)
-{
- VTY_ASSERT_LOCKED() ;
-
- if (fd >= 0)
- VTY_ASSERT_CLI_THREAD() ;
-
- memset(sock, 0, sizeof(struct vio_sock)) ;
-
- /* Zeroising the structure has set:
- *
- * action = all the actions set NULL
- *
- * error_seen = 0 -- no error, yet
- *
- * qf = NULL -- no qfile, yet
- * t_read = NULL ) no threads, yet
- * t_write = NULL )
- *
- * v_timeout = 0 -- no timeout set
- * timer_runing = 0 -- not running, yet
- * t_timer = NULL -- no timer thread, yet
- * qtr = NULL -- no qtimer, yet
- */
- sock->fd = fd ;
- sock->info = info ;
-
- sock->read_open = (fd >= 0) ;
- sock->write_open = (fd >= 0) ;
-
- if ((fd >= 0) && vty_cli_nexus)
- {
- sock->qf = qps_file_init_new(NULL, NULL);
- qps_add_file(vty_cli_nexus->selection, sock->qf, sock->fd, sock->info);
- } ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Restart the timer.
+ * Command line fetch from a file or pipe.
*
- * If a timeout time is set, then start or restart the timer with that value.
- *
- * If no timeout time is set, and the timer is running, unset it.
+ * Returns: CMD_SUCCESS -- have another command line ready to go
+ * CMD_WAITING -- do not have a command line at the moment
+ * CMD_EOF -- ran into EOF
+ * CMD_IO_ERROR -- ran into an I/O error
*/
-static void
-uty_sock_restart_timer(vio_sock sock)
+extern cmd_return_code_t
+uty_file_fetch_command_line(vio_vf vf, qstring* line)
{
- VTY_ASSERT_LOCKED() ;
- VTY_ASSERT_CLI_THREAD() ;
+ assert(vf->vin_state == vf_open) ;
- if (sock->v_timeout != 0)
+ if (vf->line_complete)
{
- assert(sock->action.timer.anon != NULL) ;
+ vio_fifo_set_hold_mark(vf->ibuf) ; /* advance hold */
- if (vty_cli_nexus)
- {
- if (sock->qtr == NULL) /* allocate qtr if required */
- sock->qtr = qtimer_init_new(NULL, vty_cli_nexus->pile,
- NULL, sock->info) ;
- qtimer_set(sock->qtr, qt_add_monotonic(QTIME(sock->v_timeout)),
- sock->action.timer.qnexus) ;
- }
- else
- {
- if (sock->t_timer != NULL)
- thread_cancel (sock->t_timer);
- sock->t_timer = thread_add_timer (vty_master,
- sock->action.timer.thread, sock->info, sock->v_timeout) ;
- } ;
-
- sock->timer_running = 1 ;
- }
- else if (sock->timer_running)
- {
- if (vty_cli_nexus)
- {
- if (sock->qtr != NULL)
- qtimer_unset(sock->qtr) ;
- }
- else
- {
- if (sock->t_timer != NULL)
- thread_cancel (sock->t_timer) ;
- } ;
+ vf->line_complete = false ;
+ vf->line_number += vf->line_step ;
- sock->timer_running = 0 ;
+ qs_set_len_nn(vf->cl, 0) ;
+ vf->line_step = 0 ;
} ;
-} ;
-/*------------------------------------------------------------------------------
- * Set read on/off
- *
- * Returns: the on/off state set
- */
-static bool
-uty_sock_set_read(vio_sock sock, bool on)
-{
- VTY_ASSERT_LOCKED() ;
- VTY_ASSERT_CLI_THREAD() ;
-
- if (sock->fd < 0)
- return 0 ;
-
- if (on)
+ while (1)
{
- assert(sock->action.read.anon != NULL) ;
+ char* s, * p, * q, * e ;
+ size_t have ;
+ ulen len ;
- if (vty_cli_nexus)
- qps_enable_mode(sock->qf, qps_read_mnum, sock->action.read.qnexus) ;
- else
- {
- if (sock->t_read != NULL)
- thread_cancel(sock->t_read) ;
+ s = vio_fifo_get(vf->ibuf, &have) ;
- sock->t_read = thread_add_read(vty_master,
- sock->action.read.thread, sock->info, sock->fd) ;
- } ;
- }
- else
- {
- if (vty_cli_nexus)
- qps_disable_modes(sock->qf, qps_read_mbit) ;
- else
+ /* If nothing in hand, try and get some more.
+ *
+ * Either exits or loops back to set s & have.
+ */
+ if (have == 0)
{
- if (sock->t_read != NULL)
- thread_cancel (sock->t_read) ;
- } ;
- } ;
-
- return on ;
-} ;
+ int get ;
-/*------------------------------------------------------------------------------
- * Set write on/off
- *
- * Returns: the on/off state set
- */
-static bool
-uty_sock_set_write(vio_sock sock, bool on)
-{
- VTY_ASSERT_LOCKED() ;
- VTY_ASSERT_CLI_THREAD() ;
+ get = vio_fifo_read_nb(vf->ibuf, vio_vfd_fd(vf->vfd), 100) ;
- if (sock->fd < 0)
- return 0 ;
+ if (get > 0)
+ continue ; /* loop back */
- if (on)
- {
- assert(sock->action.write.anon != NULL) ;
+ if (get == 0)
+ return CMD_WAITING ; /* need to set read ready ! */
- if (vty_cli_nexus)
- qps_enable_mode(sock->qf, qps_write_mnum, sock->action.write.qnexus) ;
- else
- {
- if (sock->t_write != NULL)
- thread_cancel(sock->t_write) ;
+ if (get == -1)
+ ; /* register error */
- sock->t_write = thread_add_write(vty_master,
- sock->action.write.thread, sock->info, sock->fd) ;
+ if (get == -2)
+ return (qs_len_nn(vf->cl) > 0) ? CMD_SUCCESS : CMD_EOF ;
} ;
- }
- else
- {
- if (vty_cli_nexus)
- qps_disable_modes(sock->qf, qps_write_mbit) ;
- else
- {
- if (sock->t_write != NULL)
- thread_cancel (sock->t_write) ;
- } ;
- } ;
-
- return on ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Set read/write readiness -- for VTY_TERM
- *
- * Note that for VTY_TERM, set only one of read or write, and sets write for
- * preference.
- */
-extern void
-uty_sock_set_readiness(vio_sock sock, enum vty_readiness ready)
-{
- VTY_ASSERT_LOCKED() ;
- VTY_ASSERT_CLI_THREAD() ;
-
- uty_sock_set_read(sock, (ready == read_ready)) ;
- uty_sock_set_write(sock, (ready >= write_ready)) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Set a new timer value.
- */
-extern void
-uty_sock_set_timer(vio_sock sock, unsigned long timeout)
-{
- VTY_ASSERT_LOCKED() ;
- VTY_ASSERT_CLI_THREAD() ;
-
- sock->v_timeout = timeout ;
- if (sock->timer_running)
- uty_sock_restart_timer(sock) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Close given vty sock for reading.
- *
- * Sets timer to timeout for clearing any pending output.
- *
- * NB: if there is a socket, MUST be in the CLI thread
- */
-static void
-uty_sock_half_close(vio_sock sock)
-{
- VTY_ASSERT_LOCKED() ;
-
- sock->read_open = 0 ; /* make sure */
-
- if (sock->fd < 0)
- return ; /* nothing more if no socket */
-
- VTY_ASSERT_CLI_THREAD() ;
-
- shutdown(sock->fd, SHUT_RD) ; /* actual half close */
-
- uty_sock_set_read(sock, off) ;
- uty_sock_set_write(sock, on) ;
- sock->v_timeout = 30 ; /* for output to clear */
- uty_sock_restart_timer(sock) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Close given vio_sock, completely -- shut down any timer.
- *
- * Structure is cleared of everything except the last error !
- *
- * NB: if there is a socket, MUST be in the CLI thread
- */
-static void
-uty_sock_close(vio_sock sock)
-{
- VTY_ASSERT_LOCKED() ;
-
- sock->read_open = 0 ; /* make sure */
- sock->write_open = 0 ;
-
- if (sock->fd < 0)
- {
- assert( (sock->qf == NULL)
- && (sock->qtr == NULL)
- && (sock->t_read == NULL)
- && (sock->t_write == NULL)
- && (sock->t_timer == NULL) ) ;
- return ; /* no more to be done here */
- } ;
-
- VTY_ASSERT_CLI_THREAD() ;
- close(sock->fd) ;
-
- if (vty_cli_nexus)
- {
- assert((sock->qf != NULL) && (sock->fd == qps_file_fd(sock->qf))) ;
- qps_remove_file(sock->qf) ;
- qps_file_free(sock->qf) ;
- sock->qf = NULL ;
- } ;
-
- sock->fd = -1 ;
-
- if (sock->t_read != NULL)
- thread_cancel(sock->t_write) ;
- if (sock->t_write != NULL)
- thread_cancel(sock->t_write) ;
-
- sock->t_read = NULL ;
- sock->t_write = NULL ;
-
- sock->info = NULL ;
- sock->action.read.anon = NULL ;
- sock->action.write.anon = NULL ;
- sock->action.timer.anon = NULL ;
-
- if (sock->qtr != NULL)
- qtimer_free(sock->qtr) ;
- if (sock->t_timer != NULL)
- thread_cancel(sock->t_timer) ;
-
- sock->v_timeout = 0 ;
- sock->qtr = NULL ;
- sock->t_timer = NULL ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Dealing with an I/O error on VTY socket
- *
- * If this is the first error for this VTY, produce suitable log message.
- *
- * If is a "monitor", turn that off, *before* issuing log message.
- */
-static int
-uty_sock_error(vty_io vio, const char* what)
-{
- VTY_ASSERT_LOCKED() ;
- VTY_ASSERT_CLI_THREAD() ;
-
- /* can no longer be a monitor ! *before* any logging ! */
- uty_set_monitor(vio, 0) ;
-
- /* if this is the first error, log it */
- if (vio->sock.error_seen == 0)
- {
- const char* type ;
- switch (vio->type)
- {
- case VTY_TERM:
- type = "VTY Terminal" ;
- break ;
- case VTY_SHELL_SERV:
- type = "VTY Shell Server" ;
- break ;
- default:
- zabort("unknown VTY type for uty_sock_error()") ;
- } ;
-
- vio->sock.error_seen = errno ;
- uzlog(NULL, LOG_WARNING, "%s: %s failed on fd %d: %s",
- type, what, vio->sock.fd, errtoa(vio->sock.error_seen, 0).str) ;
- } ;
-
- return -1 ;
-} ;
-
-/*==============================================================================
- * Readiness and the VTY_TERM type VTY.
- *
- * For VTY_TERM the driving force is write ready. This is used to prompt the
- * VTY_TERM when there is outstanding output (obviously), but also if there
- * is buffered input in the keystroke stream.
- *
- * The VTY_TERM uses read ready only when it doesn't set write ready. Does
- * not set both at once.
- *
- * So there is only one, common, uty_ready function, which:
- *
- * 1. attempts to clear any output it can.
- *
- * The state of the output affects the CLI, so must always do this before
- * before invoking the CLI.
- *
- * If this write enters the "--more--" state, then will have tried to
- * write away the prompt.
- *
- * 2. invokes the CLI
- *
- * Which will do either the standard CLI stuff or the special "--more--"
- * stuff.
- *
- * 3. attempts to write any output there now is.
- *
- * If the CLI generated new output, as much as possible is written away
- * now.
- *
- * If this write enters the "--more--" state, then it returns now_ready,
- * if the prompt was written away, which loops back to the CLI.
- *
- * Note that this is arranging:
- *
- * a. to write away the "--more--" prompt as soon as the tranche of output to
- * which it refers, completes
- *
- * b. to enter the cli_more_wait CLI for the first time immediately after the
- * "--more--" prompt is written away.
- *
- * The loop limits itself to one trache of command output each time.
- *
- * Resets the timer because something happened.
- */
-static void
-uty_ready(vty_io vio)
-{
- enum vty_readiness ready ;
-
- VTY_ASSERT_LOCKED() ;
-
- vio->cmd_out_done = 0 ; /* not done any command output yet */
-
- uty_write(vio) ; /* try to clear outstanding stuff */
- do
- {
- ready = uty_cli(vio) ; /* do any CLI work... */
- ready |= uty_write(vio) ; /* ...and any output that generates */
- } while (ready >= now_ready) ;
-
- uty_sock_set_readiness(&vio->sock, ready) ;
- uty_sock_restart_timer(&vio->sock) ;
-} ;
-
-/*==============================================================================
- * Reading from VTY_TERM.
- *
- * The select/pselect call-back ends up in uty_read_ready().
- *
- * Note that uty_write_ready() also calls uty_read_ready, in order to kick the
- * current CLI.
- */
-
-/*------------------------------------------------------------------------------
- * Callback -- qnexus: ready to read -> kicking CLI
- */
-static void
-vty_read_qnexus(qps_file qf, void* file_info)
-{
- vty_io vio = file_info;
-
- VTY_LOCK() ;
-
- assert((vio->sock.fd == qf->fd) && (vio == vio->sock.info)) ;
-
- uty_ready(vio) ;
-
- VTY_UNLOCK() ;
-}
-
-/*------------------------------------------------------------------------------
- * Callback -- threads: ready to read -> kicking CLI
- */
-static int
-vty_read_thread(struct thread *thread)
-{
- vty_io vio = THREAD_ARG (thread);
-
- VTY_LOCK() ;
-
- assert(vio->sock.fd == THREAD_FD (thread) && (vio == vio->sock.info)) ;
- vio->sock.t_read = NULL ; /* implicitly */
- uty_ready(vio);
-
- VTY_UNLOCK() ;
- return 0 ;
-}
-
-/*------------------------------------------------------------------------------
- * Read a lump of bytes and shovel into the keystroke stream
- *
- * Steal keystroke if required -- see keystroke_input()
- *
- * Returns: 0 => nothing available
- * > 0 => read at least one byte
- * -1 => EOF (or not open, or failed)
- */
-extern int
-uty_read (vty_io vio, keystroke steal)
-{
- unsigned char buf[500] ;
- int get ;
-
- if (!vio->sock.read_open)
- return -1 ; /* at EOF if not open */
-
- get = read_nb(vio->sock.fd, buf, sizeof(buf)) ;
- if (get >= 0)
- keystroke_input(vio->key_stream, buf, get, steal) ;
- else if (get < 0)
- {
- if (get == -1)
- uty_sock_error(vio, "read") ;
-
- vio->sock.read_open = 0 ;
- keystroke_input(vio->key_stream, NULL, 0, steal) ;
-
- get = -1 ;
- } ;
-
- return get ;
-} ;
-
-/*==============================================================================
- * The write sock action for VTY_TERM type VTY
- *
- * There are two sets of buffering:
- *
- * cli -- command line -- which reflects the status of the command line
- *
- * cmd -- command output -- which is written to the file only while
- * cmd_out_enabled.
- *
- * The cli output takes precedence.
- *
- * Output of command stuff is subject to line_control, and may go through the
- * "--more--" mechanism.
- */
-
-static int uty_write_lc(vty_io vio, vio_fifo vf, vio_line_control lc) ;
-static int uty_write_fifo_lc(vty_io vio, vio_fifo vf, vio_line_control lc) ;
-
-/*------------------------------------------------------------------------------
- * Callback -- qnexus: ready to write -> try to empty buffers
- */
-static void
-vty_write_qnexus(qps_file qf, void* file_info)
-{
- vty_io vio = file_info ;
-
- VTY_LOCK() ;
-
- assert((vio->sock.fd == qf->fd) && (vio == vio->sock.info)) ;
-
- uty_ready(vio) ;
-
- VTY_UNLOCK() ;
-}
-
-/*------------------------------------------------------------------------------
- * Callback -- thread: ready to write -> try to empty buffers
- */
-static int
-vty_write_thread(struct thread *thread)
-{
- vty_io vio = THREAD_ARG (thread);
-
- VTY_LOCK() ;
-
- assert(vio->sock.fd == THREAD_FD (thread) && (vio == vio->sock.info)) ;
-
- vio->sock.t_write = NULL; /* implicitly */
- uty_ready(vio) ;
-
- VTY_UNLOCK() ;
- return 0 ;
-}
-
-/*------------------------------------------------------------------------------
- * Write as much as possible of what there is.
- *
- * If not cmd_in_progress, clears cli_blocked if both FIFOs are, or become,
- * empty.
- *
- * Note that if !write_open, or becomes !write_open, then the FIFOs are empty
- * and all output instantly successful.
- *
- * Sets write on if prevented from writing everything available for output
- * by write() threatening to block.
- *
- * Returns: write_ready if should now set write on
- * now_ready if should loop back and try again
- * not_ready otherwise
- */
-static enum vty_readiness
-uty_write(vty_io vio)
-{
- int ret ;
-
- VTY_ASSERT_LOCKED() ;
-
- ret = -1 ;
- while (vio->sock.write_open)
- {
- /* Any outstanding line control output takes precedence */
- if (vio->cmd_lc != NULL)
- {
- ret = uty_write_lc(vio, &vio->cmd_obuf, vio->cmd_lc) ;
- if (ret != 0)
- break ;
- }
-
- /* Next: empty out the cli output */
- ret = vio_fifo_write_nb(&vio->cli_obuf, vio->sock.fd, true) ;
- if (ret != 0)
- break ;
-
- /* Finished now if not allowed to progress the command stuff */
- if (!vio->cmd_out_enabled)
- return not_ready ; /* done all can do */
+ /* Try to find a '\n' -- converting all other control chars to ' '
+ *
+ * When we find '\n' step back across any trailing ' ' (which includes
+ * any control chars before the '\n').
+ *
+ * This means that we cope with "\r\n" line terminators. But not anything
+ * more exotic.
+ */
+ p = s ;
+ e = s + have ; /* have != 0 */
+ q = NULL ;
- /* Last: if there is something in the command buffer, do that */
- if (!vio_fifo_empty(&vio->cmd_obuf))
+ while (p < e)
{
- if (vio->cmd_out_done)
- break ; /* ...but not if done once */
-
- vio->cmd_out_done = 1 ; /* done this once */
-
- assert(!vio->cli_more_wait) ;
+ if (*p++ < 0x20)
+ {
+ if (*(p-1) != '\n')
+ {
+ *(p-1) = ' ' ; /* everything other than '\n' */
+ continue ;
+ } ;
- if (vio->cmd_lc != NULL)
- ret = uty_write_fifo_lc(vio, &vio->cmd_obuf, vio->cmd_lc) ;
- else
- ret = vio_fifo_write_nb(&vio->cmd_obuf, vio->sock.fd, true) ;
+ ++vf->line_step ; /* got a '\n' */
- /* If moved into "--more--" state@
- *
- * * the "--more--" prompt is ready to be written, so do that now
- *
- * * if that completes, then want to run the CLI *now* to perform the
- * first stage of the "--more--" process.
- */
- if (vio->cli_more_wait)
- {
- ret = vio_fifo_write_nb(&vio->cli_obuf, vio->sock.fd, true) ;
- if (ret == 0)
- return now_ready ;
+ q = p ; /* point just past '\n' */
+ do --q ; while ((q > s) && (*(q-1) == ' ')) ;
+ /* discard trailing "spaces" */
+ break ;
} ;
+ } ;
- if (ret != 0)
- break ;
- }
+ /* Step past what have just consumed -- we have a hold_mark, so
+ * stuff is still in the fifo.
+ */
+ vio_fifo_step(vf->ibuf, p - s) ;
- /* Exciting stuff: there is nothing left to output...
+ /* If not found '\n', then we have a line fragment that needs to be
+ * appended to any previous line fragments.
*
- * ... watch out for half closed state.
+ * Loops back to try to get some more form the fifo.
*/
- if (vio->half_closed)
+ if (q == NULL)
{
- if (vio->close_reason != NULL)
- {
- vio->cmd_in_progress = 1 ; /* TODO: not use vty_out ? */
-
- struct vty* vty = vio->vty ;
- if (vio->cli_drawn || vio->cli_dirty)
- vty_out(vty, VTY_NEWLINE) ;
- vty_out(vty, "%% %s%s", vio->close_reason, VTY_NEWLINE) ;
-
- vio->cmd_in_progress = 0 ;
-
- vio->close_reason = NULL ; /* MUST discard now... */
- continue ; /* ... and write away */
- } ;
-
- if (!vio->closed) /* avoid recursion */
- uty_close(vio) ;
-
- return not_ready ; /* it's all over */
+ qs_append_n(vf->cl, s, p - s) ;
+ continue ;
} ;
- /* For VTY_TERM: if the command line is not drawn, now is a good
- * time to do that.
+ /* If we have nothing so far, set alias to point at what we have in
+ * the fifo. Otherwise, append to what we have.
+ *
+ * End up with: s = start of entire line, so far.
+ * p = end of entire line so far.
*/
- if (vio->type == VTY_TERM)
- if (uty_cli_draw_if_required(vio))
- continue ; /* do that now. */
-
- /* There really is nothing left to output */
- return not_ready ;
- } ;
-
- /* Arrives here if there is more to do, or failed (or was !write_open) */
-
- if (ret >= 0)
- return write_ready ;
-
- /* If is write_open, then report the error
- *
- * If still read_open, let the reader pick up and report the error, when it
- * has finished anything it has buffered.
- */
- if (vio->sock.write_open)
- {
- if (!vio->sock.read_open)
- uty_sock_error(vio, "write") ;
-
- vio->sock.write_open = 0 ; /* crash close write */
- } ;
-
- /* For whatever reason, is no longer write_open -- clear all buffers.
- */
- vio_fifo_clear(&vio->cli_obuf) ; /* throw away cli stuff */
- uty_out_clear(vio) ; /* throw away cmd stuff */
-
- vio->close_reason = NULL ; /* too late for this */
-
- return not_ready ; /* NB: NOT blocked by I/O */
-} ;
-
-/*------------------------------------------------------------------------------
- * Write as much as possible -- for "monitor" output.
- *
- * Outputs only:
- *
- * a. outstanding line control stuff.
- *
- * b. contents of CLI buffer
- *
- * And:
- *
- * a. does not report any errors.
- *
- * b. does not change anything except the state of the buffers.
- *
- * In particular, for the qpthreaded world, does not attempt to change
- * the state of the qfile or any other "thread private" structures.
- *
- * Returns: > 0 => blocked
- * 0 => all gone
- * < 0 => failed (or !write_open)
- */
-static int
-uty_write_monitor(vty_io vio)
-{
- VTY_ASSERT_LOCKED() ;
-
- if (!vio->sock.write_open)
- return -1 ;
-
- if (vio->cmd_lc != NULL)
- {
- int ret ;
- ret = uty_write_lc(vio, &vio->cmd_obuf, vio->cmd_lc) ;
-
- if (ret != 0)
- return ret ;
- } ;
-
- return vio_fifo_write_nb(&vio->cli_obuf, vio->sock.fd, true) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Write the given FIFO to output -- subject to possible line control.
- *
- * Note that even if no "--more--" is set, will have set some height, so
- * that does not attempt to empty the FIFO completely all in one go.
- *
- * If the line control becomes "paused", it is time to enter "--more--" state
- * -- unless the FIFO is empty (or "--more--" is not enabled).
- *
- * NB: expects that the sock is write_open
- *
- * Returns: > 0 => blocked or completed one tranche
- * 0 => all gone
- * < 0 => failed
- */
-static int
-uty_write_fifo_lc(vty_io vio, vio_fifo vf, vio_line_control lc)
-{
- int ret ;
- char* src ;
- size_t have ;
-
- /* Collect another line_control height's worth of output.
- *
- * Expect the line control to be empty at this point, but it does not have
- * to be.
- */
- vio_lc_set_pause(lc) ; /* clears lc->paused */
-
- src = vio_fifo_get_rdr(vf, &have) ;
-
- while ((src != NULL) && (!lc->paused))
- {
- size_t take ;
- take = vio_lc_append(lc, src, have) ;
- src = vio_fifo_step_rdr(vf, &have, take) ;
- } ;
-
- vio->cli_dirty = (lc->col != 0) ;
-
- /* Write the contents of the line control */
- ret = uty_write_lc(vio, vf, lc) ;
-
- if (ret < 0)
- return ret ; /* give up now if failed. */
-
- if ((ret == 0) && vio_fifo_empty(vf))
- return 0 ; /* FIFO and line control empty */
-
- /* If should now do "--more--", now is the time to prepare for that.
- *
- * Entering more state issues a new prompt in the CLI buffer, which can
- * be written once line control write completes.
- *
- * The "--more--" cli will not do anything until the CLI buffer has
- * cleared.
- */
- if (lc->paused && vio->cli_more_enabled)
- uty_cli_enter_more_wait(vio) ;
-
- return 1 ; /* FIFO or line control, not empty */
-} ;
-
-/*------------------------------------------------------------------------------
- * Write contents of line control (if any).
- *
- * NB: expects that the sock is write_open
- *
- * NB: does nothing other than write() and buffer management.
- *
- * Returns: > 0 => blocked
- * 0 => all gone
- * < 0 => failed
- */
-static int
-uty_write_lc(vty_io vio, vio_fifo vf, vio_line_control lc)
-{
- int ret ;
-
- ret = vio_lc_write_nb(vio->sock.fd, lc) ;
-
- if (ret <= 0)
- vio_fifo_sync_rdr(vf) ; /* finished with FIFO contents */
-
- return ret ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Start command output -- clears down the line control.
- *
- * Requires that that current line is empty -- restarts the line control
- * on the basis that is at column 0.
- */
-extern void
-uty_cmd_output_start(vty_io vio)
-{
- if (vio->cmd_lc != NULL)
- vio_lc_clear(vio->cmd_lc) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Set the effective height for line control (if any)
- *
- * If using line_control, may enable the "--more--" output handling.
- *
- * If not, want some limit on the amount of stuff output at a time.
- *
- * Sets the line control window width and height.
- * Sets cli_more_enabled if "--more--" is enabled.
- */
-extern void
-uty_set_height(vty_io vio)
-{
- bool on ;
-
- on = 0 ; /* default state */
-
- if ((vio->cmd_lc != NULL) && !vio->half_closed)
- {
- int height ;
-
- height = 0 ; /* default state */
-
- if ((vio->width) != 0)
+ len = q - s ; /* length to add */
+ if (qs_len_nn(vf->cl) == 0)
{
- /* If window size is known, use lines or given height */
- if (vio->lines >= 0)
- height = vio->lines ;
- else
- {
- /* Window height, leaving one line from previous "page"
- * and one line for the "--more--" -- if at all possible
- */
- height = vio->height - 2 ;
- if (height < 1)
- height = 1 ;
- } ;
+ qs_set_alias_n(vf->cl, s, len) ;
+ p = q ;
}
else
{
- /* If window size not known, use lines if that has been set
- * explicitly for this terminal.
- */
- if (vio->lines_set)
- height = vio->lines ;
- } ;
-
- if (height > 0)
- on = 1 ; /* have a defined height */
- else
- height = 200 ; /* but no "--more--" */
-
- vio_lc_set_window(vio->cmd_lc, vio->width, height) ;
- } ;
-
- vio->cli_more_enabled = on ;
-} ;
-
-/*==============================================================================
- * Timer for VTY_TERM (and VTY_SHELL_SERV).
- */
-
-/*------------------------------------------------------------------------------
- * Timer has expired.
- *
- * If half_closed, then this is curtains -- have waited long enough !
- *
- * Otherwise, half close the VTY and leave it to the death-watch to sweep up.
- */
-static void
-uty_timer_expired (vty_io vio)
-{
- VTY_ASSERT_LOCKED() ;
-
- if (vio->half_closed)
- return uty_close(vio) ; /* curtains */
-
- uty_half_close(vio, "Timed out") ; /* bring input side to a halt */
- } ;
-
-/*------------------------------------------------------------------------------
- * Callback -- qnexus: deal with timer timeout.
- */
-static void
-vty_timer_qnexus (qtimer qtr, void* timer_info, qtime_t when)
-{
- vty_io vio = timer_info ;
-
- VTY_LOCK() ;
-
- uty_timer_expired(vio);
-
- VTY_UNLOCK() ;
-}
-
-/*------------------------------------------------------------------------------
- * Callback -- thread: deal with timer timeout.
- */
-static int
-vty_timer_thread (struct thread *thread)
-{
- vty_io vio = THREAD_ARG (thread);
-
- VTY_LOCK() ;
-
- vio->sock.t_timer = NULL ; /* implicitly */
-
- uty_timer_expired(vio) ;
-
- VTY_UNLOCK() ;
- return 0;
-}
-
-/*==============================================================================
- * VTY Listener(s)
- *
- * Have listeners for VTY_TERM and VTY_SHELL_SERV types of VTY.
- */
-
-typedef struct vty_listener* vty_listener ;
-
-struct vty_listener
-{
- vty_listener next ; /* ssl type list */
-
- enum vty_type type ;
-
- struct vio_sock sock ;
-};
-
-/* List of listeners so can tidy up. */
-static vty_listener vty_listeners_list = NULL ;
-
-/* Prototypes for listener stuff */
-static int uty_serv_sock_addrinfo (const char *hostname, unsigned short port) ;
-static int uty_serv_sock(const char* addr, unsigned short port) ;
-static int uty_serv_sock_open(sa_family_t family, int type, int protocol,
- struct sockaddr* sa, unsigned short port) ;
-static int uty_serv_vtysh(const char *path) ;
-static int vty_accept_thread(struct thread *thread) ;
-static void vty_accept_qnexus(qps_file qf, void* listener) ;
-static int uty_accept(vty_listener listener, int listen_sock) ;
-static int uty_accept_term(vty_listener listener) ;
-static int uty_accept_shell_serv (vty_listener listener) ;
-
-static void uty_serv_start_listener(int fd, enum vty_type type) ;
-
-/*------------------------------------------------------------------------------
- * If possible, will use getaddrinfo() to find all the things to listen on.
- */
-
-#if defined(HAVE_IPV6) && !defined(NRL)
-# define VTY_USE_ADDRINFO 1
-#else
-# define VTY_USE_ADDRINFO 0
-#endif
-
-/*------------------------------------------------------------------------------
- * Open VTY listener(s)
- *
- * addr -- address ) to listen for VTY_TERM connections
- * port -- port )
- * path -- path for VTYSH connections -- if VTYSH_ENABLED
- */
-extern void
-uty_open_listeners(const char *addr, unsigned short port, const char *path)
-{
- VTY_ASSERT_LOCKED() ;
-
- /* If port is set to 0, do not listen on TCP/IP at all! */
- if (port)
- {
- int n ;
-
- if (VTY_USE_ADDRINFO)
- n = uty_serv_sock_addrinfo(addr, port);
- else
- n = uty_serv_sock(addr, port);
-
- if (n == 0)
- uzlog(NULL, LOG_ERR, "could not open any VTY listeners") ;
- }
-
- /* If want to listen for vtysh, set up listener now */
- if (VTYSH_ENABLED && (path != NULL))
- uty_serv_vtysh(path) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Close VTY listener
- *
- * addr -- address ) to listen for VTY_TERM connections
- * port -- port )
- * path -- path for VTYSH connections -- if VTYSH_ENABLED
- */
-extern void
-uty_close_listeners(void)
-{
- vty_listener listener ;
-
- VTY_ASSERT_LOCKED() ;
-
- while ((listener = ssl_pop(&listener, vty_listeners_list, next)) != NULL)
- {
- uty_sock_close(&listener->sock) ; /* no ceremony, no flowers */
- XFREE(MTYPE_VTY, listener) ;
- } ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Open listener(s) for VTY_TERM -- using getaddrinfo().
- *
- * Returns: number of listeners successfully opened.
- */
-static int
-uty_serv_sock_addrinfo (const char *hostname, unsigned short port)
-{
-#if VTY_USE_ADDRINFO
-
-# ifndef HAVE_IPV6
-# error Using getaddrinfo() but HAVE_IPV6 is not defined ??
-# endif
-
- int ret;
- int n ;
- struct addrinfo req;
- struct addrinfo *ainfo;
- struct addrinfo *ainfo_save;
- char port_str[16];
-
- VTY_ASSERT_LOCKED() ;
-
- /* Want to listen, TCP-wise, on all available address families, on the
- * given port.
- */
- memset (&req, 0, sizeof (struct addrinfo));
- req.ai_flags = AI_PASSIVE;
- req.ai_family = AF_UNSPEC;
- req.ai_socktype = SOCK_STREAM;
- snprintf(port_str, sizeof(port_str), "%d", port);
-
- ret = getaddrinfo (hostname, port_str, &req, &ainfo);
-
- if (ret != 0)
- {
- fprintf (stderr, "getaddrinfo failed: %s\n", eaitoa(ret, errno, 0).str);
- exit (1);
- }
-
- /* Open up sockets on all AF_INET and AF_INET6 addresses */
- ainfo_save = ainfo;
-
- n = 0 ;
- do
- {
- if ((ainfo->ai_family != AF_INET) && (ainfo->ai_family != AF_INET6))
- continue;
-
- assert(ainfo->ai_family == ainfo->ai_addr->sa_family) ;
-
- ret = uty_serv_sock_open(ainfo->ai_family, ainfo->ai_socktype,
- ainfo->ai_protocol, ainfo->ai_addr, port) ;
- if (ret >= 0)
- ++n ;
- }
- while ((ainfo = ainfo->ai_next) != NULL);
-
- freeaddrinfo (ainfo_save);
-
- return n ;
-
-#else
- zabort("uty_serv_sock_addrinfo not implemented") ;
-#endif /* VTY_USE_ADDRINFO */
-}
-
-/*------------------------------------------------------------------------------
- * Open listener(s) for VTY_TERM -- not using getaddrinfo() !
- *
- * Returns: number of listeners successfully opened.
- */
-static int
-uty_serv_sock(const char* addr, unsigned short port)
-{
- int ret;
- int n ;
- union sockunion su_addr ;
- struct sockaddr* sa ;
-
- VTY_ASSERT_LOCKED() ;
-
- n = 0 ; /* nothing opened yet */
-
- /* If have an address, see what kind and whether valid */
- sa = NULL ;
-
- if (addr != NULL)
- {
- ret = str2sockunion (addr, &su_addr) ;
- if (ret == 0)
- sa = &su_addr.sa ;
- else
- uzlog(NULL, LOG_ERR, "bad address %s, cannot listen for VTY", addr);
- } ;
-
- /* Try for AF_INET */
- ret = uty_serv_sock_open(AF_INET, SOCK_STREAM, 0, sa, port) ;
- if (ret >= 0)
- ++n ; /* opened socket */
- if (ret == 1)
- sa = NULL ; /* used the address */
-
-#if HAVE_IPV6
- /* Try for AF_INET6 */
- ret = uty_serv_sock_open(AF_INET6, SOCK_STREAM, 0, sa, port) ;
- if (ret >= 0)
- ++n ; /* opened socket */
- if (ret == 1)
- sa = NULL ; /* used the address */
-#endif
-
- /* If not used the address... something wrong */
- if (sa != NULL)
- uzlog(NULL, LOG_ERR, "could not use address %s, to listen for VTY", addr);
-
- /* Done */
- return n ;
-}
-
-/*------------------------------------------------------------------------------
- * Open a VTY_TERM listener socket.
- *
- * The sockaddr 'sa' may be NULL or of a different address family, in which
- * case "any" address is used.
- *
- * If the sockaddr 'sa' is used, only the address portion is used.
- *
- * Returns: < 0 => failed
- * == 0 => OK -- did not use the sockaddr 'sa'.
- * > 1 => OK -- and did use the sockaddr 'sa'
- */
-static int
-uty_serv_sock_open(sa_family_t family, int type, int protocol,
- struct sockaddr* sa, unsigned short port)
-{
- union sockunion su ;
- int sock ;
- int ret ;
-
- VTY_ASSERT_LOCKED() ;
-
- /* Is there an address and is it for this family ? */
- if ((sa != NULL) || (sa->sa_family == family))
- /* Set up sockunion containing required family and address */
- sockunion_new_sockaddr(&su, sa) ;
- else
- {
- /* no address or wrong family -- set up empty sockunion of
- * required family */
- sockunion_init_new(&su, family) ;
- sa = NULL ;
- } ;
-
- /* Open the socket and set its properties */
- sock = sockunion_socket(family, type, protocol) ;
- if (sock < 0)
- return -1 ;
-
- ret = sockopt_reuseaddr (sock);
-
- if (ret >= 0)
- ret = sockopt_reuseport (sock);
-
- if (ret >= 0)
- ret = set_nonblocking(sock);
-
-#if defined(HAVE_IPV6) && defined(IPV6_V6ONLY)
- /* Want only IPV6 on ipv6 socket (not mapped addresses)
- *
- * This distinguishes 0.0.0.0 from :: -- without this, bind() will reject the
- * attempt to bind to :: after binding to 0.0.0.0.
- */
- if ((ret >= 0) && (sa->sa_family == AF_INET6))
- {
- int on = 1;
- ret = setsockopt (sock, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&on, sizeof(on));
- }
-#endif
-
- if (ret >= 0)
- ret = sockunion_bind (sock, &su, port, sa) ;
+ if (len != 0)
+ qs_append_n(vf->cl, s, len) ;
- if (ret >= 0)
- ret = sockunion_listen (sock, 3);
+ s = qs_char_nn(vf->cl) ;
+ p = s + qs_len_nn(vf->cl) ;
- if (ret < 0)
- {
- close (sock);
- return -1 ;
- }
-
- /* Socket is open -- set VTY Term listener going */
- uty_serv_start_listener(sock, VTY_TERM) ;
-
- /* Return OK and signal whether used address or not */
- return (sa != NULL) ? 1 : 0 ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Open a VTY_SHEL_SERV listener socket (UNIX domain).
- *
- * Returns: < 0 => failed
- * >= 0 => OK
- */
-static int
-uty_serv_vtysh(const char *path)
-{
- int ret;
- int sock, sa_len, path_len ;
- struct sockaddr_un sa_un ;
- mode_t old_mask;
- struct zprivs_ids_t ids;
-
- VTY_ASSERT_LOCKED() ;
-
- /* worry about the path length */
- path_len = strlen(path) + 1 ;
- if (path_len >= (int)sizeof(sa_un.sun_path))
- {
- uzlog(NULL, LOG_ERR, "path too long for unix stream socket: '%s'", path);
- return -1 ;
- } ;
-
- /* First of all, unlink existing socket */
- unlink (path);
-
- /* Make UNIX domain socket. */
- sock = socket (AF_UNIX, SOCK_STREAM, 0);
- if (sock < 0)
- {
- uzlog(NULL, LOG_ERR, "Cannot create unix stream socket: %s",
- errtoa(errno, 0).str) ;
- return -1 ;
- }
+ if ((len == 0) && (p > s) && (*(p-1) == ' '))
+ {
+ /* Have an empty end of line section, and the last character
+ * of what we have so far is ' ', so need now to trim trailing
+ * spaces off the stored stuff.
+ */
+ do --p ; while ((p > s) && (*(p-1) == ' ')) ;
- /* Bind to the required path */
- memset (&sa_un, 0, sizeof(sa_un));
- sa_un.sun_family = AF_UNIX;
- strncpy (sa_un.sun_path, path, sizeof(sa_un.sun_path) - 1);
+ qs_set_len_nn(vf->cl, p - s) ;
+ } ;
+ } ;
- sa_len = SUN_LEN(&sa_un) ;
+ /* Now worry about we have a trailing '\'. */
-#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
- sa_un.sun_len = sa_len ;
-#endif
+ if ((p == s) || (*(p-1) != '\\'))
+ break ; /* no \ => no continuation => success */
- old_mask = umask (0007);
+ /* Have a trailing '\'.
+ *
+ * If there are an odd number of '\', strip the last one and loop
+ * round to collect the continuation.
+ *
+ * If there are an even number of '\', then this is not a continuation.
+ *
+ * Note that this rule deals with the case of the continuation line
+ * being empty... e.g. ....\\\ n n -- where n is '\n'
+ */
+ q = p ;
+ do --q ; while ((q > s) && (*(q-1) == '\\')) ;
- ret = bind (sock, (struct sockaddr *) &sa_un, sa_len) ;
- if (ret < 0)
- uzlog(NULL, LOG_ERR, "Cannot bind path %s: %s", path, errtoa(errno, 0).str);
+ if (((p - q) & 1) == 0)
+ break ; /* even => no continuation => success */
- if (ret >= 0)
- ret = set_nonblocking(sock);
+ qs_set_len_nn(vf->cl, p - s - 1) ; /* strip odd '\' */
- if (ret >= 0)
- {
- ret = listen (sock, 5);
- if (ret < 0)
- uzlog(NULL, LOG_ERR, "listen(fd %d) failed: %s", sock,
- errtoa(errno, 0).str) ;
+ continue ; /* loop back to fetch more */
} ;
- zprivs_get_ids(&ids);
-
- if (ids.gid_vty > 0)
- {
- /* set group of socket */
- if ( chown (path, -1, ids.gid_vty) )
- uzlog (NULL, LOG_ERR, "uty_serv_vtysh: could chown socket, %s",
- errtoa(errno, 0).str) ;
- }
+ /* Success have a line in hand */
- umask (old_mask);
+ vf->line_complete = true ;
+ *line = vf->cl ;
- /* Give up now if failed along the way */
- if (ret < 0)
- {
- close (sock) ;
- return -1 ;
- } ;
-
- /* Socket is open -- set VTY Term listener going */
- uty_serv_start_listener(sock, VTY_SHELL_SERV) ;
-
- return 0 ;
+ return CMD_SUCCESS ;
} ;
/*------------------------------------------------------------------------------
- * Socket is open -- set a VTY listener going
+ * Command output push to a file or pipe.
*
- * Note that the vyt_listener structure is passed to the accept action function.
+ * Returns: CMD_SUCCESS -- done
+ * CMD_IO_ERROR -- ran into an I/O error
*/
-static void
-uty_serv_start_listener(int fd, enum vty_type type)
+extern cmd_return_code_t
+uty_file_out_push(vio_vf vf)
{
- vty_listener listener ;
-
- listener = XCALLOC(MTYPE_VTY, sizeof (struct vty_listener));
+ assert(vf->vout_state == vf_open) ;
- ssl_push(vty_listeners_list, listener, next) ;
- uty_sock_init_new(&listener->sock, fd, listener) ;
-
- listener->type = type ;
-
- if (vty_cli_nexus)
- listener->sock.action.read.qnexus = vty_accept_qnexus ;
+ if (vio_fifo_write_nb(vf->obuf, vio_vfd_fd(vf->vfd), false) >= 0)
+ return CMD_SUCCESS ;
else
- listener->sock.action.read.thread = vty_accept_thread ;
-
- uty_sock_set_read(&listener->sock, on) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Accept action for the thread world -- create and dispatch VTY
- */
-static int
-vty_accept_thread(struct thread *thread)
-{
- vty_listener listener = THREAD_ARG(thread) ;
- int result ;
-
- VTY_LOCK() ;
-
- result = uty_accept(listener, THREAD_FD(thread));
-
- uty_sock_set_read(&listener->sock, on) ;
-
- VTY_UNLOCK() ;
- return result ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Accept action for the qnexus world -- create and dispatch VTY
- */
-static void
-vty_accept_qnexus(qps_file qf, void* listener)
-{
- VTY_LOCK() ;
-
- uty_accept(listener, qf->fd);
-
- VTY_UNLOCK() ;
-}
-
-/*------------------------------------------------------------------------------
- * Accept action -- create and dispatch VTY_TERM or VTY_SHELL_SERV
- */
-static int
-uty_accept(vty_listener listener, int listen_sock)
-{
- VTY_ASSERT_LOCKED() ;
-
- assert(listener->sock.fd == listen_sock) ;
-
- switch (listener->type)
- {
- case VTY_TERM:
- return uty_accept_term(listener) ;
-
- case VTY_SHELL_SERV:
- return uty_accept_shell_serv(listener) ;
-
- default:
- zabort("unknown vty type") ;
- } ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Accept action -- create and dispatch VTY_TERM
- */
-static int
-uty_accept_term(vty_listener listener)
-{
- int sock_fd;
- union sockunion su;
- int ret;
- unsigned int on;
- struct prefix *p ;
-
- VTY_ASSERT_LOCKED() ;
-
- /* We can handle IPv4 or IPv6 socket. */
- sockunion_init_new(&su, AF_UNSPEC) ;
-
- sock_fd = sockunion_accept (listener->sock.fd, &su);
-
- if (sock_fd < 0)
- {
- if (sock_fd == -1)
- uzlog (NULL, LOG_WARNING, "can't accept vty socket : %s",
- errtoa(errno, 0).str) ;
- return -1;
- }
-
- /* Really MUST have non-blocking */
- ret = set_nonblocking(sock_fd) ; /* issues WARNING if fails */
- if (ret < 0)
- {
- close(sock_fd) ;
- return -1 ;
- } ;
-
- /* New socket is open... worry about access lists */
- p = sockunion2hostprefix (&su);
- ret = 0 ; /* so far, so good */
-
- if ((p->family == AF_INET) && vty_accesslist_name)
- {
- /* VTY's accesslist apply. */
- struct access_list* acl ;
-
- if ((acl = access_list_lookup (AFI_IP, vty_accesslist_name)) &&
- (access_list_apply (acl, p) == FILTER_DENY))
- ret = -1 ;
- }
-
-#ifdef HAVE_IPV6
- if ((p->family == AF_INET6) && vty_ipv6_accesslist_name)
- {
- /* VTY's ipv6 accesslist apply. */
- struct access_list* acl ;
-
- if ((acl = access_list_lookup (AFI_IP6, vty_ipv6_accesslist_name)) &&
- (access_list_apply (acl, p) == FILTER_DENY))
- ret = -1 ;
- }
-#endif /* HAVE_IPV6 */
-
- prefix_free (p);
-
- if (ret != 0)
- {
- uzlog (NULL, LOG_INFO, "Vty connection refused from %s", sutoa(&su).str) ;
- close (sock_fd);
- return 0;
- } ;
-
- /* Final options (optional) */
- on = 1 ;
- ret = setsockopt (sock_fd, IPPROTO_TCP, TCP_NODELAY,
- (void*)&on, sizeof (on));
- if (ret < 0)
- uzlog (NULL, LOG_INFO, "can't set sockopt to socket %d: %s",
- sock_fd, errtoa(errno, 0).str) ;
-
- /* All set -- create the VTY_TERM */
- uty_new_term(sock_fd, &su);
-
- /* Log new VTY */
- uzlog (NULL, LOG_INFO, "Vty connection from %s (fd %d)", sutoa(&su).str,
- sock_fd) ;
-
- return 0;
-}
-
-/*------------------------------------------------------------------------------
- * Accept action -- create and dispatch VTY_SHELL_SERV
- */
-static int
-uty_accept_shell_serv (vty_listener listener)
-{
- int sock_fd ;
- int ret ;
- int client_len ;
- struct sockaddr_un client ;
-
- VTY_ASSERT_LOCKED() ;
-
- client_len = sizeof(client);
- memset (&client, 0, client_len);
-
- sock_fd = accept(listener->sock.fd, (struct sockaddr *) &client,
- (socklen_t *) &client_len) ;
-
- if (sock_fd < 0)
- {
- uzlog (NULL, LOG_WARNING, "can't accept vty shell socket : %s",
- errtoa(errno, 0).str) ;
- return -1;
- }
-
- /* Really MUST have non-blocking */
- ret = set_nonblocking(sock_fd) ; /* issues WARNING if fails */
- if (ret < 0)
- {
- close(sock_fd) ;
- return -1 ;
- } ;
-
- /* All set -- create the VTY_SHELL_SERV */
- if (VTYSH_DEBUG)
- printf ("VTY shell accept\n");
-
- uty_new_shell_serv(sock_fd) ;
-
- /* Log new VTY */
- uzlog (NULL, LOG_INFO, "Vty shell connection (fd %d)", sock_fd);
- return 0;
-}
-
-/*==============================================================================
- * Reading from the VTY_SHELL_SERV type sock.
- *
- * The select/pselect call-back ends up in utysh_read_ready().
- */
-
-/*------------------------------------------------------------------------------
- * Ready to read -> kicking the "SHELL_SERV CLI"
- *
- * End up here when there is something ready to be read.
- *
- * Will also end up here if an error has occurred, the other end has closed,
- * this end has half closed, etc. This fact is used to kick the CLI even when
- * there is no data to be read.
- *
- * Note that nothing is actually read here -- reading is done in the CLI itself,
- * if required.
- *
- * The CLI decides whether to re-enable read, or enable write, or both.
- */
-static void
-utysh_read_ready(vty_io vio)
-{
- uty_sock_set_read(&vio->sock, off) ;
-
- /* TODO: need minimal "CLI" for VTY_SHELL_SERV
- * NB: when output from command is flushed out, must append the
- * following four bytes: '\0' '\0' '\0' <ret>
- * Where <ret> is the command return code.
- */
+ return CMD_IO_ERROR ;
} ;
/*------------------------------------------------------------------------------
- * Callback -- qnexus: ready to read -> kicking the "SHELL_SERV CLI"
- */
-static void
-vtysh_read_qnexus(qps_file qf, void* file_info)
-{
- vty_io vio = file_info;
-
- VTY_LOCK() ;
-
- assert((vio->sock.fd == qf->fd) && (vio == vio->sock.info)) ;
-
- utysh_read_ready(vio) ;
-
- VTY_UNLOCK() ;
-}
-
-/*------------------------------------------------------------------------------
- * Callback -- threads: ready to read -> kicking the "SHELL_SERV CLI"
- */
-static int
-vtysh_read_thread(struct thread *thread)
-{
- vty_io vio = THREAD_ARG (thread);
-
- VTY_LOCK() ;
-
- assert(vio->sock.fd == THREAD_FD (thread) && (vio == vio->sock.info)) ;
-
- vio->sock.t_read = NULL ; /* implicitly */
- utysh_read_ready(vio);
-
- VTY_UNLOCK() ;
- return 0 ;
-}
-
-/*------------------------------------------------------------------------------
- * Read a lump of bytes and shovel into the command line buffer
- *
- * Lines coming in are terminated by '\0'.
- *
- * Assumes that the incoming command line is empty or otherwise incomplete.
- *
- * Moves stuff from the "buf" qstring and appends to "cl" qstring, stopping
- * when get '\0' or empties the "buf".
- *
- * When empties "buf", reads a lump from the sock.
- *
- * Returns: 0 => command line is incomplete
- * 1 => have a complete command line
- * -1 => EOF (or not open, or failed)
- */
-extern int
-utysh_read (vty_io vio, qstring cl, qstring buf)
-{
- int get ;
- char* cp ;
- char* ep ;
- size_t have ;
-
- while (1)
- {
- /* process what there is in the buffer */
- if (buf->len > buf->cp)
- {
- cp = qs_cp_char(buf) ;
- ep = qs_ep_char(buf) ;
- have = ep - cp ;
-
- ep = memchr(cp, '\0', have) ;
- if (ep != NULL)
- have = ep - cp ; /* have upto, but excluding '\0' */
-
- if (have > 0) /* take what have */
- {
- qs_insert(cl, cp, have) ;
- cl->cp += have ;
- buf->cp += have ;
- } ;
-
- if (ep != NULL) /* if found '\0' */
- {
- qs_term(cl) ; /* '\0' terminate */
- ++buf->cp ; /* step past it */
- return 1 ; /* have a complete line <<<<<<<<<<<<< */
- }
- } ;
-
- /* buffer is empty -- try and get some more stuff */
- assert(buf->len == buf->cp) ;
-
- if (!vio->sock.read_open)
- return -1 ; /* at EOF if not open <<<<<<<<<<<<< */
-
- qs_need(buf, 500) ; /* need a reasonable lump */
- qs_clear(buf) ; /* set cp = len = 0 */
-
- get = read_nb(vio->sock.fd, buf->body, buf->size) ;
- if (get > 0)
- buf->len = get ;
- else if (get == 0)
- return 0 ; /* have an incomplete line <<<<<<<<<<<< */
- else
- {
- if (get == -1)
- uty_sock_error(vio, "read") ;
-
- vio->sock.read_open = 0 ;
-
- return -1 ; /* at EOF or failed <<<<<<<<<<<<< */
- } ;
- } ;
-} ;
-
-/*==============================================================================
- * Output to vty which are set to "monitor".
- *
- * This is VERY TRICKY.
- *
- * If not running qpthreaded, then the objective is to get the message away
- * immediately -- do not wish it to be delayed in any way by the thread
- * system.
- *
- * So proceed as follows:
- *
- * a. wipe command line -- which adds output to the CLI buffer
- *
- * b. write the CLI buffer to the sock and any outstanding line control.
- *
- * c. write the monitor output.
- *
- * If that does not complete, put the tail end to the CLI buffer.
- *
- * d. restore any command line -- which adds output to the CLI buffer
- *
- * e. write the CLI buffer to the sock
- *
- * If that all succeeds, nothing has changed as far as the VTY stuff is
- * concerned -- except that possibly some CLI output was sent before it got
- * round to it.
- *
- * Note that step (b) will deal with any output hanging around from an
- * earlier step (e). If cannot complete that, then does not add fuel to the
- * fire -- but the message will be discarded.
- *
- * If that fails, or does not complete, then can set write on, to signal that
- * there is some output in the CLI buffer that needs to be sent, or some
- * error to be dealt with.
- *
- * The output should be tidy.
- *
- * To cut down the clutter, step (d) is performed only if the command line
- * is not empty (or if in cli_more_wait). Once a the user has started to enter
- * a command, the prompt and the command will remain visible.
- *
- * When logging an I/O error for a vty that happens to be a monitor, the
- * monitor-ness has already been turned off. The monitor output code does not
- * attempt to log any errors, sets write on so that the error will be picked
- * up that way.
- *
- * However, in the event of an assertion failure, it is possible that an
- * assertion will fail inside the monitor output. The monitor_busy flag
- * prevents disaster. It is also left set if I/O fails in monitor output, so
- * will not try to use the monitor again.
- *
- * Note that an assertion which is false for all vty monitors will recurse
- * through all the monitors, setting each one busy, in turn !
- *
-
-
- * TODO: sort out write on in the qpthreads world ??
- *
- * The problem is that the qpselect structure is designed to be accessed ONLY
- * within the thread to which it belongs. This makes it impossible for the
- * monitor output to set/clear read/write on the vty sock... so some way
- * around this is required.
- */
-
-/*------------------------------------------------------------------------------
- * Output logging information to all vty which are set to "monitor".
+ * Tidy up after input file has been closed
*/
extern void
-uty_log(struct logline* ll, struct zlog *zl, int priority,
- const char *format, va_list va)
+uty_file_read_close(vio_vf vf)
{
- vty_io vio ;
-
- VTY_ASSERT_LOCKED() ;
-
- vio = sdl_head(vio_monitors_base) ;
-
- if (vio == NULL)
- return ; /* go no further if no "monitor" vtys */
-
- /* Prepare line for output. */
- uvzlog_line(ll, zl, priority, format, va, llt_crlf) ; /* with crlf */
-
- /* write to all known "monitor" vty
- *
- */
- while (vio != NULL)
- {
- if (!vio->monitor_busy)
- {
- int ret ;
-
- vio->monitor_busy = 1 ; /* close the door */
-
- uty_cli_pre_monitor(vio, ll->len - 2) ; /* claim the console */
-
- ret = uty_write_monitor(vio) ;
- if (ret == 0)
- {
- ret = write_nb(vio->sock.fd, ll->line, ll->len) ;
-
- if (ret >= 0)
- {
- ret = uty_cli_post_monitor(vio, ll->line + ret,
- ll->len - ret) ;
- if (ret > 0)
- ret = uty_write_monitor(vio) ;
- } ;
- } ;
-
- if (ret != 0)
- /* need to prod */ ;
-
- if (ret >= 0)
- vio->monitor_busy = 0 ;
- } ;
-
- vio = sdl_next(vio, mon_list) ;
- } ;
+ return ;
} ;
/*------------------------------------------------------------------------------
- * Async-signal-safe version of vty_log for fixed strings.
+ * Flush output buffer and close.
*
- * This is last gasp operation.
+ * Returns: true <=> buffer (now) empty
*/
-void
-vty_log_fixed (const char *buf, size_t len)
+extern bool
+uty_file_write_close(vio_vf vf, bool final)
{
- vty_io vio ;
-
- /* Write to all known "monitor" vty
- *
- * Forget all the niceties -- about to die in any case.
- */
- vio = sdl_head(vio_monitors_base) ;
- while (vio != NULL)
- {
- write(vio->sock.fd, buf, len) ;
- write(vio->sock.fd, "\r\n", 2) ;
-
- vio = sdl_next(vio, mon_list) ;
- } ;
+ return vio_fifo_write_nb(vf->obuf, vio_vfd_fd(vf->vfd), true) == 0 ;
} ;
diff --git a/lib/vty_io_file.h b/lib/vty_io_file.h
index b4a79f52..ab852e08 100644
--- a/lib/vty_io_file.h
+++ b/lib/vty_io_file.h
@@ -1,8 +1,6 @@
-/* VTY IO FILE -- File I/O -- header
- * Virtual terminal [aka TeletYpe] interface routine.
- * Copyright (C) 1997, 98 Kunihiro Ishiguro
+/* VTY I/O for Files -- Header
*
- * Revisions: Copyright (C) 2010 Chris Hall (GMCH), Highwayman
+ * Copyright (C) 2010 Chris Hall (GMCH), Highwayman
*
* This file is part of GNU Zebra.
*
@@ -26,17 +24,8 @@
#define _ZEBRA_VTY_IO_FILE_H
#include "misc.h"
-#include <errno.h>
-#include "uty.h"
-#include "vty.h"
#include "vty_io.h"
-#include "vio_fifo.h"
-#include "vio_lines.h"
-#include "keystroke.h"
-#include "thread.h"
-#include "command.h"
-#include "qstring.h"
/*==============================================================================
* Here are structures and other definitions which are shared by:
@@ -50,7 +39,15 @@
* Functions
*/
-extern int uty_vprintf_file(vty_io vio, const char *format, va_list args) ;
+extern cmd_return_code_t uty_file_read_open(vty_io vio, qstring name,
+ bool reflect) ;
+extern cmd_return_code_t uty_file_write_open(vty_io vio, qstring name,
+ bool append) ;
+extern cmd_return_code_t uty_file_fetch_command_line(vio_vf vf, qstring* line) ;
+extern cmd_return_code_t uty_file_out_push(vio_vf vf) ;
+
+extern void uty_file_read_close(vio_vf vf) ;
+extern bool uty_file_write_close(vio_vf vf, bool final) ;
#endif /* _ZEBRA_VTY_IO_FILE_H */
diff --git a/lib/vty_io_shell.c b/lib/vty_io_shell.c
index c56cc2d2..7b24b871 100644
--- a/lib/vty_io_shell.c
+++ b/lib/vty_io_shell.c
@@ -43,6 +43,7 @@
#define VTYSH_DEBUG 0
+#if 0
/*------------------------------------------------------------------------------
* Create new vty of type VTY_SHELL_SERV -- ie attached to a vtysh session.
@@ -62,7 +63,7 @@ uty_new_shell_serv(int sock_fd)
vio = vty->vio ;
/* Set the action functions */
- if (vty_cli_nexus)
+ if (vty_nexus)
{
vio->sock.action.read.qnexus = vtysh_read_qnexus ;
vio->sock.action.write.qnexus = vty_write_qnexus ;
@@ -364,3 +365,5 @@ utysh_read (vty_io vio, qstring cl, qstring buf)
} ;
} ;
} ;
+
+#endif
diff --git a/lib/vty_io_term.c b/lib/vty_io_term.c
index 2053dcc5..0f8e834d 100644
--- a/lib/vty_io_term.c
+++ b/lib/vty_io_term.c
@@ -20,11 +20,15 @@
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
+#include "zconfig.h"
+#include "misc.h"
-#include "zebra.h"
-
+#include "vty_local.h"
+#include "vty_io.h"
#include "vty_io_term.h"
#include "vty_cli.h"
+#include "vty_command.h"
+#include "vio_fifo.h"
#include "qstring.h"
#include "keystroke.h"
@@ -38,149 +42,159 @@
#include "network.h"
#include <arpa/telnet.h>
-#include <sys/un.h> /* for VTYSH */
#include <sys/socket.h>
+#include <errno.h>
-#define VTYSH_DEBUG 0
-
-/*------------------------------------------------------------------------------
- * Create new vty of type VTY_TERMINAL -- ie attached to a telnet session.
+/*==============================================================================
+ * The I/O side of Telnet VTY_TERMINAL. The CLI side is vty_cli.c.
+ *
+ * A VTY_TERMINAL comes into being when a telnet connection is accepted, and
+ * is closed either on command, or on timeout, or when the daemon is reset
+ * or terminated.
+ *
+ *
+ *
*
- * This is called by the accept action for the VTY_TERMINAL listener.
*/
-static void
-uty_term_accept_new(int sock_fd, union sockunion *su)
-{
- struct vty *vty ;
- vty_io vio ;
- enum vty_readiness ready ;
- VTY_ASSERT_LOCKED() ;
- VTY_ASSERT_CLI_THREAD() ;
- /* Allocate new vty structure and set up default values. */
- vty = uty_new(VTY_TERMINAL, sock_fd) ;
- vio = vty->vio ;
- /* The text form of the address identifies the VTY */
- vty->vio->name = sockunion_su2str (su, MTYPE_VTY_NAME);
- /* Set the initial node */
- if (no_password_check)
- {
- if (restricted_mode)
- vty->node = RESTRICTED_NODE;
- else if (host.advanced)
- vty->node = ENABLE_NODE;
- else
- vty->node = VIEW_NODE;
- }
- else
- vty->node = AUTH_NODE;
- /* Pick up current timeout setting */
- vio->sock.v_timeout = vty_timeout_val;
- /* Use global 'lines' setting, as default. May be -1 => unset */
- vio->lines = host.lines ;
-
- /* For VTY_TERM use vio_line_control for '\n' and "--more--" */
- vio->cmd_lc = vio_lc_init_new(NULL, 0, 0) ;
- uty_set_height(vio) ; /* set initial state */
-
- /* Initialise the CLI, ready for start-up messages etc. */
- uty_cli_init(vio) ;
+/*==============================================================================
+ * If possible, will use getaddrinfo() to find all the things to listen on.
+ */
+#if defined(HAVE_IPV6) && !defined(NRL)
+# define VTY_USE_ADDRINFO 1
+#else
+# define VTY_USE_ADDRINFO 0
+#endif
- /* Reject connection if password isn't set, and not "no password" */
- if ((host.password == NULL) && (host.password_encrypt == NULL)
- && ! no_password_check)
- {
- uty_close(vio, "Vty password is not set.");
- vty = NULL;
- }
- else
- {
- /* Say hello to the world. */
- vty_hello (vty);
+/*==============================================================================
+ * Opening and closing VTY_TERMINAL type
+ */
- if (! no_password_check)
- uty_output (vty, "\nUser Access Verification\n\n");
- } ;
+static void uty_term_ready(vio_vfd vfd, void* action_info) ;
+static vty_timer_time uty_term_read_timeout(vio_timer_t* timer,
+ void* action_info) ;
+static vty_timer_time uty_term_write_timeout(vio_timer_t* timer,
+ void* action_info) ;
+static vty_readiness_t uty_term_write(vio_vf vf) ;
- /* Now start the CLI and set a suitable state of readiness */
- ready = uty_cli_start(vio) ;
- uty_sock_set_readiness(&vio->sock, ready) ;
-} ;
+static void uty_term_will_echo(vty_cli cli) ;
+static void uty_term_will_suppress_go_ahead(vty_cli cli) ;
+static void uty_term_dont_linemode(vty_cli cli) ;
+static void uty_term_do_window_size(vty_cli cli) ;
+static void uty_term_dont_lflow_ahead(vty_cli cli) ;
/*------------------------------------------------------------------------------
- * Construct vio_vf structure for new VTY_TERMINAL type VTY, and add same.
- *
+ * Create new vty of type VTY_TERMINAL -- ie attached to a telnet session.
*
+ * This is called by the accept action for the VTY_TERMINAL listener.
*/
-
static void
-uty_term_new(vty_io vio, int sock_fd)
+uty_term_open(int sock_fd, union sockunion *su)
{
- vio_vf vf ;
-
- enum vty_readiness ready ;
+ vty vty ;
+ vty_io vio ;
+ vio_vf vf ;
VTY_ASSERT_LOCKED() ;
VTY_ASSERT_CLI_THREAD() ;
- /* May only be the first vio_vf ! */
- assert((vio->vin_base == NULL) && (vio->vout_base == NULL)) ;
-
- /* Construct and add to the vio */
- vf = uty_vf_new(vio, sock_fd, vfd_socket, vfd_io_read_write) ;
+ /* Allocate new vty structure and set up default values. */
+ vty = uty_new(VTY_TERMINAL) ;
+ vio = vty->vio ;
- uty_vin_add(vio, vf, VIN_TERM, uty_term_ready, uty_term_read_timeout) ;
- uty_vout_add(vio, vf, VIN_TERM, uty_term_ready, uty_term_write_timeout) ;
+ /* The initial vty->node depends on a number of vty type things, so
+ * we set that now.
+ *
+ * This completes the initialisation of the vty object, except that the
+ * execution and vio objects are largely empty.
+ */
+ if (host.no_password_check)
+ {
+ if (host.restricted_mode)
+ vio->vty->node = RESTRICTED_NODE;
+ else if (host.advanced)
+ vio->vty->node = ENABLE_NODE;
+ else
+ vio->vty->node = VIEW_NODE;
+ }
+ else
+ vio->vty->node = AUTH_NODE;
- /* Allocate and initialise a keystroke stream TODO: CSI ?? */
- vio->key_stream = keystroke_stream_new('\0', uty_cli_iac_callback, vio) ;
+ /* Complete the initialisation of the vty_io object.
+ *
+ * Note that the defaults for:
+ *
+ * - read_timeout -- default = 0 => no timeout
+ * - write_timeout -- default = 0 => no timeout
+ *
+ * - parse_type -- default = cmd_parse_standard
+ * - reflect_enabled -- default = false
+ * - out_enabled -- default = true iff vfd_io_write
+ *
+ * Are OK, except that we want the read_timeout set to the current EXEC
+ * timeout value.
+ *
+ * The text form of the address identifies the VTY.
+ */
+ vf = uty_vf_new(vio, sutoa(su).str, sock_fd, vfd_socket, vfd_io_read_write) ;
- /* Pick up current timeout setting */
- vf->read_timeout = vty_timeout_val;
+ uty_vin_open( vio, vf, VIN_TERM, uty_term_ready,
+ uty_term_read_timeout,
+ 0) ; /* no ibuf required */
+ uty_vout_open(vio, vf, VOUT_TERM, uty_term_ready,
+ uty_term_write_timeout,
+ 4096) ; /* obuf is required */
- /* Use global 'lines' setting, as default. May be -1 => unset */
- vio->lines = host.lines ;
+ vf->read_timeout = host.vty_timeout_val ; /* current EXEC timeout */
- /* For VTY_TERM use vio_line_control for '\n' and "--more--" */
- vf->olc = vio_lc_init_new(NULL, 0, 0) ;
- uty_set_height(vio) ; /* set initial state */
+ /* Set up the CLI object & initialise */
+ vf->cli = uty_cli_new(vf) ;
- /* Initialise the CLI, ready for start-up messages etc. */
- uty_cli_init(vio) ;
+ /* When we get here the VTY is set up and all ready to go. */
+ uty_cmd_prepare(vio) ;
- /* Reject connection if password isn't set, and not "no password" */
- if ((host.password == NULL) && (host.password_encrypt == NULL)
- && ! no_password_check)
- {
- uty_close(vio, "Vty password is not set.");
- vty = NULL;
- }
- else
+ /* Issue Telnet commands/escapes to be a good telnet citizen -- not much
+ * real negotiating going on -- just a statement of intentions !
+ */
+ uty_term_will_echo (vf->cli);
+ uty_term_will_suppress_go_ahead (vf->cli);
+ uty_term_dont_linemode (vf->cli);
+ uty_term_do_window_size (vf->cli);
+ if (0)
+ uty_term_dont_lflow_ahead (vf->cli) ;
+
+ /* Say hello */
+ vty_hello(vty);
+
+ /* If need password, issue prompt or give up if no password to check
+ * against !
+ */
+ if (vty->node == AUTH_NODE)
{
- /* Say hello to the world. */
- vty_hello (vty);
-
- if (! no_password_check)
- uty_output (vty, "\nUser Access Verification\n\n");
+ if (host.password != NULL)
+ vty_out(vty, "\nUser Access Verification\n\n");
+ else
+ uty_close(vio, false, qs_set(NULL, "vty password is not set."));
} ;
- /* Now start the CLI and set a suitable state of readiness */
- ready = uty_cli_start(vio) ;
- uty_sock_set_readiness(&vio->sock, ready) ;
+ /* Push the output to date and start the CLI */
+ uty_cmd_out_push(vio) ;
+ uty_cli_start(vf->cli, vty->node) ;
} ;
-
-
/*------------------------------------------------------------------------------
+ * Close the reading side of VTY_TERMINAL, and close down CLI as far as
+ * possible, given that output may be continuing.
*
+ * Expects to be called once only for the VTY_TERMINAL.
*/
extern void
-uty_term_half_close(vio_vf vf)
+uty_term_read_close(vio_vf vf)
{
vty_io vio ;
@@ -193,93 +207,60 @@ uty_term_half_close(vio_vf vf)
* Note that half closing the file sets a new timeout, sets read off
* and write on.
*/
-
-
-
uty_set_monitor(vio, 0) ;
- /* Discard everything in the keystroke stream and force it to EOF */
- if (vio->key_stream != NULL)
- keystroke_stream_set_eof(vio->key_stream) ;
-
- /* Turn off "--more--" so that all output clears without interruption.
- *
- * If is sitting on a "--more--" prompt, then exit the wait_more CLI.
- */
- vio->cli_more_enabled = 0 ;
-
- if (vio->cli_more_wait)
- uty_cli_exit_more_wait(vio) ;
-
- /* If a command is not in progress, enable output, which will clear
- * the output buffer if there is anything there, plus any close reason,
- * and then close.
- *
- * If command is in progress, then this process will start when it
- * completes.
- */
- if (!vio->cmd_in_progress)
- vio->cmd_out_enabled = 1 ;
+ uty_cli_close(vf->cli, false) ;
/* Log closing of VTY_TERM */
assert(vio->vty->type == VTY_TERMINAL) ;
- uzlog (NULL, LOG_INFO, "Vty connection (fd %d) close", uty_vf_fd(vf)) ;
+ uzlog (NULL, LOG_INFO, "Vty connection (fd %d) close", vio_vfd_fd(vf->vfd)) ;
} ;
/*------------------------------------------------------------------------------
- * vprintf to VTY_TERM
- *
- * All output goes to output fifo until command completes.
+ * Close the writing side of VTY_TERMINAL.
*
- * NB: MUST be cmd_in_progress
+ * Pushes any buffered stuff to output and
*
- * Discards output if the socket is not open for whatever reason.
*/
-extern int
-uty_term_vprintf(vio_vf vf, const char *format, va_list args)
+extern bool
+uty_term_write_close(vio_vf vf, bool final)
{
- VTY_ASSERT_LOCKED() ;
-
- if (!vf->write_open)
- return 0 ; /* discard output if not open ! */
+ vty_io vio ;
+ vty_readiness_t ready ;
- assert(vf->vio->cmd_in_progress) ;
+ /* Get the vio and ensure that we are all straight */
+ vio = vf->vio ;
+ assert((vio->vin == vio->vin_base) && (vio->vin == vf)) ;
- return vio_fifo_vprintf(vf->obuf, format, args) ;
-} ;
+ /* Do the file side of things
+ *
+ * Note that half closing the file sets a new timeout, sets read off
+ * and write on.
+ */
+ uty_set_monitor(vio, 0) ;
-/*------------------------------------------------------------------------------
- * Set/Clear "monitor" state:
- *
- * set: if VTY_TERM and not already "monitor" (and write_open !)
- * clear: if is "monitor"
- */
-extern void
-uty_set_monitor(vty_io vio, bool on)
-{
- VTY_ASSERT_LOCKED() ;
+ vf->cli->out_active = true ; /* force the issue */
- if (on && !vio->monitor)
- {
- if ((vio->type == VTY_TERM) && vio->sock.write_open)
- {
- vio->monitor = 1 ;
- sdl_push(vio_monitors_base, vio, mon_list) ;
- } ;
- }
- else if (!on && vio->monitor)
+ do
{
- vio->monitor = 0 ;
- sdl_del(vio_monitors_base, vio, mon_list) ;
- }
+ vf->cli->out_done = false ;
+ ready = uty_term_write(vf) ;
+ } while ((ready != write_ready) && vf->cli->out_active) ;
+
+ final = final || !vf->cli->out_active ;
+
+ if (!final)
+ uty_term_set_readiness(vf, ready) ;
+
+ vf->cli = uty_cli_close(vf->cli, final) ;
+
+ return final ;
} ;
/*==============================================================================
* Action routines for VIN_TERM/VOUT_TERM type vin/vout objects
*/
-static enum vty_readiness uty_term_write(vio_vf vf) ;
-
/*==============================================================================
* Readiness and the VIN_TERM type vin.
*
@@ -289,7 +270,23 @@ static enum vty_readiness uty_term_write(vio_vf vf) ;
*
* The VIN_TERM uses read ready only when it doesn't set write ready. Does
* not set both at once.
+ */
+
+/*------------------------------------------------------------------------------
+ * Set read/write readiness -- for VIN_TERM/VOUT_TERM
*
+ * Note that sets only one of read or write, and sets write for preference.
+ */
+extern void
+uty_term_set_readiness(vio_vf vf, vty_readiness_t ready)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ uty_vf_set_read(vf, (ready == read_ready)) ;
+ uty_vf_set_write(vf, (ready >= write_ready)) ;
+} ;
+
+/*------------------------------------------------------------------------------
* So there is only one, common, uty_term_ready function, which:
*
* 1. attempts to clear any output it can.
@@ -326,39 +323,41 @@ static enum vty_readiness uty_term_write(vio_vf vf) ;
* Resets the timer because something happened.
*/
static void
-uty_term_ready(vio_fd vfd, void* action_info)
+uty_term_ready(vio_vfd vfd, void* action_info)
{
- enum vty_readiness ready ;
+ vty_readiness_t ready ;
vio_vf vf = action_info ;
- vty_io vio = vf->vio ;
- VTY_ASSERT_LOCKED() ;
+ assert(vfd == vf->vfd) ;
- vio->cmd_out_done = 0 ; /* not done any command output yet */
+ VTY_ASSERT_LOCKED() ;
uty_term_write(vf) ; /* try to clear outstanding stuff */
do
{
- ready = uty_cli(vio) ; /* do any CLI work... */
+ ready = uty_cli(vf->cli) ; /* do any CLI work... */
ready |= uty_term_write(vf) ; /* ...and any output that generates */
} while (ready >= now_ready) ;
- uty_file_set_readiness(vf, ready) ;
+ uty_term_set_readiness(vf, ready) ;
} ;
/*==============================================================================
- * Reading from VTY_TERM.
- *
- * The select/pselect call-back ends up in uty_read_ready().
+ * Reading from VTY_TERMINAL.
*
- * Note that uty_write_ready() also calls uty_read_ready, in order to kick the
- * current CLI.
+ * The select/pselect call-back ends up in uty_term_ready().
*/
/*------------------------------------------------------------------------------
* Read a lump of bytes and shovel into the keystroke stream
*
+ * This function is called from the vty_cli to top up the keystroke buffer,
+ * or in the stealing of a keystroke to end "--more--" state.
+ *
+ * NB: need not be in the term_ready path. Indeed, when the VTY_TERMINAL is
+ * initialised, this is called to suck up any telnet preamble.
+ *
* Steal keystroke if required -- see keystroke_input()
*
* Returns: 0 => nothing available
@@ -366,25 +365,24 @@ uty_term_ready(vio_fd vfd, void* action_info)
* -1 => EOF (or not open, or failed)
*/
extern int
-uty_read (vty_io vio, keystroke steal)
+uty_term_read(vio_vf vf, keystroke steal)
{
unsigned char buf[500] ;
int get ;
- if (!vio->sock.read_open)
- return -1 ; /* at EOF if not open */
+ if (vf->vin_state != vf_open)
+ return -1 ; /* at EOF if not open */
- get = read_nb(vio->sock.fd, buf, sizeof(buf)) ;
+ get = read_nb(vio_vfd_fd(vf->vfd), buf, sizeof(buf)) ;
if (get >= 0)
- keystroke_input(vio->key_stream, buf, get, steal) ;
+ keystroke_input(vf->cli->key_stream, buf, get, steal) ;
else if (get < 0)
{
if (get == -1)
- uty_file_error(vio, "read") ;
-
- vio->sock.read_open = 0 ;
- keystroke_input(vio->key_stream, NULL, 0, steal) ;
+ uty_vf_error(vf, "read", errno) ;
+ keystroke_input(vf->cli->key_stream, NULL, 0, steal) ;
+ /* Tell keystroke stream that EOF met */
get = -1 ;
} ;
@@ -396,10 +394,10 @@ uty_read (vty_io vio, keystroke steal)
*
* There are two sets of buffering:
*
- * cli -- command line -- which reflects the status of the command line
+ * cli->cbuf -- command line -- reflects the status of the command line
*
- * cmd -- command output -- which is written to the file only while
- * cmd_out_enabled.
+ * vf->obuf -- command output -- which is written to the file only while
+ * out_active.
*
* The cli output takes precedence.
*
@@ -407,8 +405,8 @@ uty_read (vty_io vio, keystroke steal)
* "--more--" mechanism.
*/
-static int uty_write_lc(vio_vf vf, vio_fifo vfifo, vio_line_control lc) ;
-static int uty_write_fifo_lc(vio_vf vf, vio_fifo vfifo, vio_line_control lc) ;
+static int uty_write_lc(vio_vf vf, vio_fifo vff, vio_line_control lc) ;
+static int uty_write_fifo_lc(vio_vf vf, vio_fifo vff, vio_line_control lc) ;
/*------------------------------------------------------------------------------
* Write as much as possible of what there is.
@@ -426,83 +424,79 @@ static int uty_write_fifo_lc(vio_vf vf, vio_fifo vfifo, vio_line_control lc) ;
* now_ready if should loop back and try again
* not_ready otherwise
*/
-static enum vty_readiness
+static vty_readiness_t
uty_term_write(vio_vf vf)
{
- vty_io vio = vf->vio ;
+ vty_cli cli = vf->cli ;
int ret ;
VTY_ASSERT_LOCKED() ;
ret = -1 ;
- while (vf->write_open)
+ while (vf->vout_state == vf_open)
{
/* Any outstanding line control output takes precedence */
- if (vf->olc != NULL)
- {
- ret = uty_write_lc(vf, vf->obuf, vf->olc) ;
- if (ret != 0)
- break ;
- }
+ ret = uty_write_lc(vf, vf->obuf, cli->olc) ;
+ if (ret != 0)
+ break ;
/* Next: empty out the cli output */
- ret = vio_fifo_write_nb(&vf->cli_obuf, vio->sock.fd, true) ;
+ ret = vio_fifo_write_nb(cli->cbuf, vio_vfd_fd(vf->vfd), true) ;
if (ret != 0)
break ;
/* Finished now if not allowed to progress the command stuff */
- if (!vio->cmd_out_enabled)
+ if (!cli->out_active)
return not_ready ; /* done all can do */
- /* Last: if there is something in the command buffer, do that */
+ /* If there is something in the command buffer, do that */
if (!vio_fifo_empty(vf->obuf))
{
- if (vio->cmd_out_done)
+#if 0
+ if (cli->out_done)
break ; /* ...but not if done once */
- vio->cmd_out_done = 1 ; /* done this once */
-
- assert(!vio->cli_more_wait) ;
+ cli->out_done = true ; /* done this once */
+#endif
+ assert(!cli->more_wait) ;
- if (vio->cmd_lc != NULL)
- ret = uty_write_fifo_lc(vf, vf->obuf, vf->olc) ;
- else
- ret = vio_fifo_write_nb(vf->obuf, vio->sock.fd, true) ;
-
- /* If moved into "--more--" state@
- *
- * * the "--more--" prompt is ready to be written, so do that now
- *
- * * if that completes, then want to run the CLI *now* to perform the
- * first stage of the "--more--" process.
- */
- if (vio->cli_more_wait)
+ ret = uty_write_fifo_lc(vf, vf->obuf, cli->olc) ;
+ if (ret != 0)
{
- ret = vio_fifo_write_nb(vf->obuf, vio->sock.fd, true) ;
- if (ret == 0)
- return now_ready ;
- } ;
+ if (ret < 0)
+ break ; /* failed */
+
+ if (!cli->more_wait)
+ return write_ready ; /* done a tranche */
+
+ /* Moved into "--more--" state
+ *
+ * * the "--more--" prompt is ready to be written, so do that
+ * now
+ *
+ * * if that completes, then want to run the CLI *now* to
+ * perform the first stage of the "--more--" process.
+ */
+ ret = vio_fifo_write_nb(cli->cbuf, vio_vfd_fd(vf->vfd), true) ;
+ if (ret != 0)
+ break ;
- if (ret != 0)
- break ;
- }
+ return now_ready ;
+ } ;
+ } ;
/* Exciting stuff: there is nothing left to output...
*
* ... watch out for half closed state.
*/
- if (vio->half_closed)
+#if 0
+ if (vio->closing)
{
if (vio->close_reason != NULL)
{
- vio->cmd_in_progress = 1 ; /* TODO: not use vty_out ? */
-
- struct vty* vty = vio->vty ;
- if (vio->cli_drawn || vio->cli_dirty)
- vty_out(vty, VTY_NEWLINE) ;
- vty_out(vty, "%% %s%s", vio->close_reason, VTY_NEWLINE) ;
-
- vio->cmd_in_progress = 0 ;
+ if (cli->drawn || cli->dirty)
+ uty_out(vio, "\n") ;
+ uty_out(vio, "%% %s\n", vio->close_reason) ;
vio->close_reason = NULL ; /* MUST discard now... */
continue ; /* ... and write away */
@@ -513,42 +507,39 @@ uty_term_write(vio_vf vf)
return not_ready ; /* it's all over */
} ;
+#endif
- /* For VTY_TERM: if the command line is not drawn, now is a good
- * time to do that.
- */
- if (vio->vty_type == VTY_TERM)
- if (uty_cli_draw_if_required(vio))
- continue ; /* do that now. */
+ if (uty_cli_draw_if_required(cli))
+ continue ; /* do that now. */
/* There really is nothing left to output */
+ cli->out_active = false ;
+
return not_ready ;
} ;
/* Arrives here if there is more to do, or failed (or was !write_open) */
- if (ret >= 0)
+ if (ret > 0)
return write_ready ;
+ if (ret == 0) /* just in case */
+ return not_ready ;
+
/* If is write_open, then report the error
*
* If still read_open, let the reader pick up and report the error, when it
* has finished anything it has buffered.
*/
- if (vf->write_open)
- {
- if (!vf->read_open)
- uty_file_error(vio, "write") ;
-
- vf->write_open = false ; /* crash close write */
- } ;
+ if (vf->vout_state == vf_open)
+ uty_vf_error(vf, "write", errno) ;
/* For whatever reason, is no longer write_open -- clear all buffers.
*/
- vio_fifo_clear(vf->obuf) ; /* throw away cli stuff */
- uty_out_clear(vio) ; /* throw away cmd stuff */
+ vio_fifo_clear(vf->obuf, true) ; /* throw away cli stuff */
+ uty_cli_out_clear(cli) ; /* throw away cmd stuff */
- vio->close_reason = NULL ; /* too late for this */
+ cli->out_active = false ;
return not_ready ; /* NB: NOT blocked by I/O */
} ;
@@ -575,6 +566,7 @@ uty_term_write(vio_vf vf)
* 0 => all gone
* < 0 => failed (or !write_open)
*/
+#if 0
static int
uty_write_monitor(vio_vf vf)
{
@@ -592,11 +584,12 @@ uty_write_monitor(vio_vf vf)
return ret ;
} ;
- return vio_fifo_write_nb(vf->obuf, uty_vf_fd(vf), true) ;
+ return vio_fifo_write_nb(vf->obuf, vio_vfd_fd(vf), true) ;
} ;
+#endif
/*------------------------------------------------------------------------------
- * Write the given FIFO to output -- subject to possible line control.
+ * Write the given FIFO to output -- subject to line control.
*
* Note that even if no "--more--" is set, will have set some height, so
* that does not attempt to empty the FIFO completely all in one go.
@@ -606,16 +599,19 @@ uty_write_monitor(vio_vf vf)
*
* NB: expects that the sock is write_open
*
- * Returns: > 0 => blocked or completed one tranche
+ * Returns: > 0 => blocked or completed one tranche (more to go)
* 0 => all gone
* < 0 => failed
*/
static int
-uty_write_fifo_lc(vio_vf vf, vio_fifo vfifo, vio_line_control lc)
+uty_write_fifo_lc(vio_vf vf, vio_fifo vff, vio_line_control lc)
{
int ret ;
char* src ;
size_t have ;
+ vty_cli cli ;
+
+ cli = vf->cli ;
/* Collect another line_control height's worth of output.
*
@@ -624,24 +620,29 @@ uty_write_fifo_lc(vio_vf vf, vio_fifo vfifo, vio_line_control lc)
*/
vio_lc_set_pause(lc) ; /* clears lc->paused */
- src = vio_fifo_get_rdr(vfifo, &have) ;
+ vio_fifo_set_hold_mark(vff) ;
+ src = vio_fifo_get(vff, &have) ;
while ((src != NULL) && (!lc->paused))
{
size_t take ;
+
+ if (src == NULL)
+ break ;
+
take = vio_lc_append(lc, src, have) ;
- src = vio_fifo_step_rdr(vfifo, &have, take) ;
+ src = vio_fifo_step_get(vff, &have, take) ;
} ;
- vf->vio->cli_dirty = (lc->col != 0) ;
+ cli->dirty = (lc->col != 0) ;
/* Write the contents of the line control */
- ret = uty_write_lc(vf, vfifo, lc) ;
+ ret = uty_write_lc(vf, vff, lc) ;
if (ret < 0)
return ret ; /* give up now if failed. */
- if ((ret == 0) && vio_fifo_empty(vfifo))
+ if ((ret == 0) && vio_fifo_empty(vff))
return 0 ; /* FIFO and line control empty */
/* If should now do "--more--", now is the time to prepare for that.
@@ -652,8 +653,8 @@ uty_write_fifo_lc(vio_vf vf, vio_fifo vfifo, vio_line_control lc)
* The "--more--" cli will not do anything until the CLI buffer has
* cleared.
*/
- if (lc->paused && vf->vio->cli_more_enabled)
- uty_cli_enter_more_wait(vf->vio) ;
+ if (lc->paused && cli->more_enabled)
+ uty_cli_enter_more_wait(cli) ;
return 1 ; /* FIFO or line control, not empty */
} ;
@@ -670,18 +671,21 @@ uty_write_fifo_lc(vio_vf vf, vio_fifo vfifo, vio_line_control lc)
* < 0 => failed
*/
static int
-uty_write_lc(vio_vf vf, vio_fifo vfifo, vio_line_control lc)
+uty_write_lc(vio_vf vf, vio_fifo vff, vio_line_control lc)
{
int ret ;
- ret = vio_lc_write_nb(uty_vf_fd(vf), lc) ;
+ ret = vio_lc_write_nb(vio_vfd_fd(vf->vfd), lc) ;
if (ret <= 0)
- vio_fifo_sync_rdr(vfifo) ; /* finished with FIFO contents */
+ vio_fifo_clear_hold_mark(vff) ; /* finished with FIFO contents */
return ret ;
} ;
+
+#if 0
+
/*------------------------------------------------------------------------------
* Start command output -- clears down the line control.
*
@@ -693,137 +697,60 @@ uty_cmd_output_start(vio_vf vf)
{
if (vf->olc != NULL)
vio_lc_clear(vf->olc) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Set the effective height for line control (if any)
- *
- * If using line_control, may enable the "--more--" output handling.
- *
- * If not, want some limit on the amount of stuff output at a time.
- *
- * Sets the line control window width and height.
- * Sets cli_more_enabled if "--more--" is enabled.
- */
-extern void
-uty_set_height(vio_vf vf)
-{
- vty_io vio = vf->vio ;
- bool on ;
-
- on = 0 ; /* default state */
- if ((vf->olc != NULL) && !vio->half_closed)
- {
- int height ;
-
- height = 0 ; /* default state */
+ vio_fifo_set_hold_mark(vf->obuf) ; /* mark to keep until all gone */
+} ;
- if ((vio->width) != 0)
- {
- /* If window size is known, use lines or given height */
- if (vio->lines >= 0)
- height = vio->lines ;
- else
- {
- /* Window height, leaving one line from previous "page"
- * and one line for the "--more--" -- if at all possible
- */
- height = vio->height - 2 ;
- if (height < 1)
- height = 1 ;
- } ;
- }
- else
- {
- /* If window size not known, use lines if that has been set
- * explicitly for this terminal.
- */
- if (vio->lines_set)
- height = vio->lines ;
- } ;
+#endif
- if (height > 0)
- on = 1 ; /* have a defined height */
- else
- height = 200 ; /* but no "--more--" */
- vio_lc_set_window(vio->cmd_lc, vio->width, height) ;
- } ;
- vio->cli_more_enabled = on ;
-} ;
/*==============================================================================
- * Timer for VTY_TERM (and VTY_SHELL_SERV).
+ * Timer actions for VTY_TERMINAL
*/
/*------------------------------------------------------------------------------
- * Timer has expired.
+ * Read timer has expired.
+ *
+ * If closing, then this is curtains -- have waited long enough !
*
- * If half_closed, then this is curtains -- have waited long enough !
+ * TODO .... sort out the VTY_TERMINAL time-out & death-watch timeout
*
* Otherwise, half close the VTY and leave it to the death-watch to sweep up.
*/
-static void
-uty_timer_expired (vty_io vio)
+static vty_timer_time
+uty_term_read_timeout(vio_timer_t* timer, void* action_info)
{
+ vty_io vio = action_info ;
+
VTY_ASSERT_LOCKED() ;
- if (vio->half_closed)
- return uty_close(vio) ; /* curtains */
+ uty_close(vio, true, qs_set(NULL, "Timed out")) ;
- uty_close(vio, "Timed out") ; /* bring input side to a halt */
+ return 0 ;
} ;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
/*------------------------------------------------------------------------------
- * Set read/write readiness -- for VTY_TERM
+ * Write timer has expired.
+ *
+ * If closing, then this is curtains -- have waited long enough !
+ *
+ * TODO .... sort out the VTY_TERMINAL time-out & death-watch timeout
*
- * Note that for VTY_TERM, set only one of read or write, and sets write for
- * preference.
+ * Otherwise, half close the VTY and leave it to the death-watch to sweep up.
*/
-extern void
-uty_file_set_readiness(vio_vf vf, enum vty_readiness ready)
+static vty_timer_time
+uty_term_write_timeout(vio_timer_t* timer, void* action_info)
{
- VTY_ASSERT_LOCKED() ;
- VTY_ASSERT_CLI_THREAD() ;
-
- vio_fd_set_read(vf, (ready == read_ready)) ;
- vio_fd_set_write(vf, (ready >= write_ready)) ;
-} ;
+ vty_io vio = action_info ;
-/*------------------------------------------------------------------------------
- * Set a new timer value.
- */
-extern void
-uty_file_set_timer(vio_vf vf, unsigned long timeout)
-{
VTY_ASSERT_LOCKED() ;
- VTY_ASSERT_CLI_THREAD() ;
-
- vf->v_timeout = timeout ;
-} ;
-
-
-
+ uty_close(vio, true, qs_set(NULL, "Timed out")) ;
+ return 0 ;
+} ;
/*==============================================================================
* VTY Listener(s) for VTY_TERMINAL
@@ -1055,7 +982,7 @@ uty_term_listen_open(sa_family_t family, int type, int protocol,
} ;
/*------------------------------------------------------------------------------
- * Accept action -- create and dispatch VTY_TERM
+ * Accept action -- create and dispatch VTY_TERMINAL
*/
static void
uty_term_accept(int sock_listen)
@@ -1093,24 +1020,24 @@ uty_term_accept(int sock_listen)
p = sockunion2hostprefix (&su);
ret = 0 ; /* so far, so good */
- if ((p->family == AF_INET) && vty_accesslist_name)
+ if ((p->family == AF_INET) && host.vty_accesslist_name)
{
/* VTY's accesslist apply. */
struct access_list* acl ;
- if ((acl = access_list_lookup (AFI_IP, vty_accesslist_name)) &&
- (access_list_apply (acl, p) == FILTER_DENY))
+ if ((acl = access_list_lookup (AFI_IP, host.vty_accesslist_name))
+ && (access_list_apply (acl, p) == FILTER_DENY))
ret = -1 ;
}
#ifdef HAVE_IPV6
- if ((p->family == AF_INET6) && vty_ipv6_accesslist_name)
+ if ((p->family == AF_INET6) && host.vty_ipv6_accesslist_name)
{
/* VTY's ipv6 accesslist apply. */
struct access_list* acl ;
- if ((acl = access_list_lookup (AFI_IP6, vty_ipv6_accesslist_name)) &&
- (access_list_apply (acl, p) == FILTER_DENY))
+ if ((acl = access_list_lookup (AFI_IP6, host.vty_ipv6_accesslist_name))
+ && (access_list_apply (acl, p) == FILTER_DENY))
ret = -1 ;
}
#endif /* HAVE_IPV6 */
@@ -1132,8 +1059,8 @@ uty_term_accept(int sock_listen)
uzlog (NULL, LOG_INFO, "can't set sockopt to socket %d: %s",
sock_fd, errtoa(errno, 0).str) ;
- /* All set -- create the VTY_TERM */
- uty_term_accept_new(sock_fd, &su);
+ /* All set -- create the VTY_TERMINAL and set it going */
+ uty_term_open(sock_fd, &su);
/* Log new VTY */
uzlog (NULL, LOG_INFO, "Vty connection from %s (fd %d)", sutoa(&su).str,
@@ -1143,6 +1070,289 @@ uty_term_accept(int sock_listen)
} ;
/*==============================================================================
+ * VTY telnet stuff
+ *
+ * Note that all telnet commands (escapes) and any debug stuff is treated as
+ * CLI output.
+ */
+
+#define TELNET_OPTION_DEBUG 0 /* 0 to turn off */
+
+static const char* telnet_commands[256] =
+{
+ [tn_IAC ] = "IAC",
+ [tn_DONT ] = "DONT",
+ [tn_DO ] = "DO",
+ [tn_WONT ] = "WONT",
+ [tn_WILL ] = "WILL",
+ [tn_SB ] = "SB",
+ [tn_GA ] = "GA",
+ [tn_EL ] = "EL",
+ [tn_EC ] = "EC",
+ [tn_AYT ] = "AYT",
+ [tn_AO ] = "AO",
+ [tn_IP ] = "IP",
+ [tn_BREAK] = "BREAK",
+ [tn_DM ] = "DM",
+ [tn_NOP ] = "NOP",
+ [tn_SE ] = "SE",
+ [tn_EOR ] = "EOR",
+ [tn_ABORT] = "ABORT",
+ [tn_SUSP ] = "SUSP",
+ [tn_EOF ] = "EOF",
+} ;
+
+static const char* telnet_options[256] =
+{
+ [to_BINARY] = "BINARY", /* 8-bit data path */
+ [to_ECHO] = "ECHO", /* echo */
+ [to_RCP] = "RCP", /* prepare to reconnect */
+ [to_SGA] = "SGA", /* suppress go ahead */
+ [to_NAMS] = "NAMS", /* approximate message size */
+ [to_STATUS] = "STATUS", /* give status */
+ [to_TM] = "TM", /* timing mark */
+ [to_RCTE] = "RCTE", /* remote controlled tx and echo */
+ [to_NAOL] = "NAOL", /* neg. about output line width */
+ [to_NAOP] = "NAOP", /* neg. about output page size */
+ [to_NAOCRD] = "NAOCRD", /* neg. about CR disposition */
+ [to_NAOHTS] = "NAOHTS", /* neg. about horizontal tabstops */
+ [to_NAOHTD] = "NAOHTD", /* neg. about horizontal tab disp. */
+ [to_NAOFFD] = "NAOFFD", /* neg. about formfeed disposition */
+ [to_NAOVTS] = "NAOVTS", /* neg. about vertical tab stops */
+ [to_NAOVTD] = "NAOVTD", /* neg. about vertical tab disp. */
+ [to_NAOLFD] = "NAOLFD", /* neg. about output LF disposition */
+ [to_XASCII] = "XASCII", /* extended ascii character set */
+ [to_LOGOUT] = "LOGOUT", /* force logout */
+ [to_BM] = "BM", /* byte macro */
+ [to_DET] = "DET", /* data entry terminal */
+ [to_SUPDUP] = "SUPDUP", /* supdup protocol */
+ [to_SUPDUPOUTPUT] = "SUPDUPOUTPUT",/* supdup output */
+ [to_SNDLOC] = "SNDLOC", /* send location */
+ [to_TTYPE] = "TTYPE", /* terminal type */
+ [to_EOR] = "EOR", /* end or record */
+ [to_TUID] = "TUID", /* TACACS user identification */
+ [to_OUTMRK] = "OUTMRK", /* output marking */
+ [to_TTYLOC] = "TTYLOC", /* terminal location number */
+ [to_3270REGIME] = "3270REGIME", /* 3270 regime */
+ [to_X3PAD] = "X3PAD", /* X.3 PAD */
+ [to_NAWS] = "NAWS", /* window size */
+ [to_TSPEED] = "TSPEED", /* terminal speed */
+ [to_LFLOW] = "LFLOW", /* remote flow control */
+ [to_LINEMODE] = "LINEMODE", /* Linemode option */
+ [to_XDISPLOC] = "XDISPLOC", /* X Display Location */
+ [to_OLD_ENVIRON] = "OLD_ENVIRON", /* Old - Environment variables */
+ [to_AUTHENTICATION] = "AUTHENTICATION", /* Authenticate */
+ [to_ENCRYPT] = "ENCRYPT", /* Encryption option */
+ [to_NEW_ENVIRON] = "NEW_ENVIRON", /* New - Environment variables */
+ [to_EXOPL] = "EXOPL", /* extended-options-list */
+} ;
+
+/*------------------------------------------------------------------------------
+ * For debug. Put string or value as decimal.
+ */
+static void
+uty_cli_out_dec(vty_cli cli, const char* str, unsigned char u)
+{
+ if (str != NULL)
+ uty_cli_out(cli, "%s ", str) ;
+ else
+ uty_cli_out(cli, "%d ", (int)u) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * For debug. Put string or value as hex.
+ */
+static void
+uty_cli_out_hex(vty_cli cli, const char* str, unsigned char u)
+{
+ if (str != NULL)
+ uty_cli_out(cli, "%s ", str) ;
+ else
+ uty_cli_out(cli, "0x%02x ", (unsigned)u) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Send telnet: "WILL TELOPT_ECHO"
+ */
+static void
+uty_term_will_echo (vty_cli cli)
+{
+ unsigned char cmd[] = { tn_IAC, tn_WILL, to_ECHO };
+ VTY_ASSERT_LOCKED() ;
+ uty_cli_write (cli, (char*)cmd, (int)sizeof(cmd));
+}
+
+/*------------------------------------------------------------------------------
+ * Send telnet: "suppress Go-Ahead"
+ */
+static void
+uty_term_will_suppress_go_ahead (vty_cli cli)
+{
+ unsigned char cmd[] = { tn_IAC, tn_WILL, to_SGA };
+ VTY_ASSERT_LOCKED() ;
+ uty_cli_write (cli, (char*)cmd, (int)sizeof(cmd));
+}
+
+/*------------------------------------------------------------------------------
+ * Send telnet: "don't use linemode"
+ */
+static void
+uty_term_dont_linemode (vty_cli cli)
+{
+ unsigned char cmd[] = { tn_IAC, tn_DONT, to_LINEMODE };
+ VTY_ASSERT_LOCKED() ;
+ uty_cli_write (cli, (char*)cmd, (int)sizeof(cmd));
+}
+
+/*------------------------------------------------------------------------------
+ * Send telnet: "Use window size"
+ */
+static void
+uty_term_do_window_size (vty_cli cli)
+{
+ unsigned char cmd[] = { tn_IAC, tn_DO, to_NAWS };
+ VTY_ASSERT_LOCKED() ;
+ uty_cli_write (cli, (char*)cmd, (int)sizeof(cmd));
+}
+
+/*------------------------------------------------------------------------------
+ * Send telnet: "don't use lflow" -- not currently used
+ */
+static void
+uty_term_dont_lflow_ahead (vty_cli cli)
+{
+ unsigned char cmd[] = { tn_IAC, tn_DONT, to_LFLOW };
+ VTY_ASSERT_LOCKED() ;
+ uty_cli_write (cli, (char*)cmd, (int)sizeof(cmd));
+}
+
+/*------------------------------------------------------------------------------
+ * Process incoming Telnet Option(s)
+ *
+ * May be called during keystroke iac callback, or when processing CLI
+ * keystrokes.
+ *
+ * In particular: get telnet window size.
+ *
+ * Returns: true <=> dealt with, for:
+ *
+ * * telnet window size.
+ */
+extern bool
+uty_telnet_command(vio_vf vf, keystroke stroke, bool callback)
+{
+ uint8_t* p ;
+ uint8_t o ;
+ int left ;
+ bool dealt_with ;
+
+ vty_cli cli = vf->cli ;
+
+ /* Echo to the other end if required */
+ if (TELNET_OPTION_DEBUG)
+ {
+ uty_cli_wipe(cli, 0) ;
+
+ p = stroke->buf ;
+ left = stroke->len ;
+
+ uty_cli_out_hex(cli, telnet_commands[tn_IAC], tn_IAC) ;
+
+ if (left-- > 0)
+ uty_cli_out_dec(cli, telnet_commands[*p], *p) ;
+ ++p ;
+
+ if (left-- > 0)
+ uty_cli_out_dec(cli, telnet_options[*p], *p) ;
+ ++p ;
+
+ if (left > 0)
+ {
+ while(left-- > 0)
+ uty_cli_out_hex(cli, NULL, *p++) ;
+
+ if (stroke->flags & kf_truncated)
+ uty_cli_out(cli, "... ") ;
+
+ if (!(stroke->flags & kf_broken))
+ {
+ uty_cli_out_hex(cli, telnet_commands[tn_IAC], tn_IAC) ;
+ uty_cli_out_hex(cli, telnet_commands[tn_SE], tn_SE) ;
+ }
+ } ;
+
+ if (stroke->flags & kf_broken)
+ uty_cli_out (cli, "BROKEN") ;
+
+ uty_cli_out_newline(cli) ;
+ } ;
+
+ /* Process the telnet command */
+ dealt_with = false ;
+
+ if (stroke->flags != 0)
+ return dealt_with ; /* go no further if broken */
+
+ p = stroke->buf ;
+ left = stroke->len ;
+
+ passert(left >= 1) ; /* must be if not broken ! */
+ passert(stroke->value == *p) ; /* or something is wrong */
+
+ ++p ; /* step past X of IAC X */
+ --left ;
+
+ /* Decode the one command that is interesting -- "NAWS" */
+ switch (stroke->value)
+ {
+ case tn_SB:
+ passert(left > 0) ; /* or parser failed */
+
+ o = *p++ ; /* the option byte */
+ --left ;
+ switch(o)
+ {
+ case to_NAWS:
+ if (left != 4)
+ {
+ uzlog(NULL, LOG_WARNING,
+ "RFC 1073 violation detected: telnet NAWS option "
+ "should send %d characters, but we received %d",
+ (3 + 4 + 2), (3 + left + 2)) ;
+ }
+ else
+ {
+ int width, height ;
+
+ width = *p++ << 8 ; width += *p++ ;
+ height = *p++ << 8 ; height += *p ;
+
+ if (TELNET_OPTION_DEBUG)
+ {
+ uty_cli_out(cli, "TELNET NAWS window size received: "
+ "width %d, height %d", width, height) ;
+ uty_cli_out_newline(cli) ;
+ } ;
+
+ uty_cli_set_window(cli, width, height) ;
+
+ dealt_with = true ;
+ } ;
+ break ;
+
+ default: /* no other IAC SB <option> */
+ break ;
+ } ;
+ break ;
+
+ default: /* no other IAC X */
+ break ;
+ } ;
+
+ return dealt_with ;
+} ;
+
+/*==============================================================================
* Output to vty which are set to "monitor".
*
* This is VERY TRICKY.
@@ -1230,6 +1440,7 @@ uty_log(struct logline* ll, struct zlog *zl, int priority,
*/
while (vio != NULL)
{
+#if 0
if (!vio->monitor_busy)
{
int ret ;
@@ -1241,7 +1452,7 @@ uty_log(struct logline* ll, struct zlog *zl, int priority,
ret = uty_write_monitor(vio) ;
if (ret == 0)
{
- ret = write_nb(uty_vf_fd(vf), ll->line, ll->len) ;
+ ret = write_nb(vio_vfd_fd(vf->vfd), ll->line, ll->len) ;
if (ret >= 0)
{
@@ -1258,7 +1469,7 @@ uty_log(struct logline* ll, struct zlog *zl, int priority,
if (ret >= 0)
vio->monitor_busy = 0 ;
} ;
-
+#endif
vio = sdl_next(vio, mon_list) ;
} ;
} ;
@@ -1280,8 +1491,8 @@ vty_log_fixed (const char *buf, size_t len)
vio = sdl_head(vio_monitors_base) ;
while (vio != NULL)
{
- write(uty_vf_fd(vf), buf, len) ;
- write(uty_vf_fd(vf), "\r\n", 2) ;
+ write(vio_vfd_fd(vio->vout_base->vfd), buf, len) ;
+ write(vio_vfd_fd(vio->vout_base->vfd), "\r\n", 2) ;
vio = sdl_next(vio, mon_list) ;
} ;
diff --git a/lib/vty_io_term.h b/lib/vty_io_term.h
index ef975a71..f1823a92 100644
--- a/lib/vty_io_term.h
+++ b/lib/vty_io_term.h
@@ -28,16 +28,16 @@
#include "misc.h"
#include <errno.h>
-#include "uty.h"
#include "vty.h"
#include "vty_io_basic.h"
#include "vty_io.h"
+#include "vty_cli.h"
#include "vio_fifo.h"
#include "vio_lines.h"
#include "keystroke.h"
#include "thread.h"
-#include "command.h"
+#include "command_local.h"
#include "qstring.h"
/*==============================================================================
@@ -54,8 +54,15 @@
extern void uty_term_new(vty_io vio, int sock_fd) ;
-extern void uty_term_half_close(vio_vf vf) ;
-extern int uty_term_vprintf(vio_vf vf, const char *format, va_list args) ;
+extern void uty_term_read_close(vio_vf vf) ;
+extern bool uty_term_write_close(vio_vf vf, bool final) ;
+
+extern int uty_term_read(vio_vf vf, keystroke steal) ;
+extern void uty_term_set_readiness(vio_vf vf, vty_readiness_t ready) ;
+
+
+
+extern bool uty_telnet_command(vio_vf, keystroke stroke, bool callback) ;
extern void uty_term_open_listeners(const char *addr, unsigned short port) ;
diff --git a/lib/vty_local.h b/lib/vty_local.h
index 2767f9d3..97e5786c 100644
--- a/lib/vty_local.h
+++ b/lib/vty_local.h
@@ -89,33 +89,36 @@ extern qpn_nexus vty_cmd_nexus ;
extern qpt_mutex_t vty_mutex ;
-#ifdef NDEBUG
-# define VTY_DEBUG 0 /* NDEBUG override */
+#ifdef VTY_DEBUG /* Can be forced from outside */
+# if VTY_DEBUG
+# define VTY_DEBUG 1 /* Force 1 or 0 */
#else
-# ifndef VTY_DEBUG
-# define VTY_DEBUG 1 /* Set to 1 to turn on debug checks */
+# define VTY_DEBUG 0
+# endif
+#else
+# ifdef QDEBUG
+# define VTY_DEBUG 1 /* Follow QDEBUG */
+#else
+# define VTY_DEBUG 0
# endif
#endif
-#if VTY_DEBUG
+enum { vty_debug = VTY_DEBUG } ;
extern int vty_lock_count ;
-extern int vty_assert_fail ;
-
-#endif
Inline void
VTY_LOCK(void) /* if is qpthreads_enabled, lock vty_mutex */
{
qpt_mutex_lock(&vty_mutex) ;
- if (VTY_DEBUG)
+ if (vty_debug)
++vty_lock_count ;
} ;
Inline void
VTY_UNLOCK(void) /* if is qpthreads_enabled, unlock vty_mutex */
{
- if (VTY_DEBUG)
+ if (vty_debug)
--vty_lock_count ;
qpt_mutex_unlock(&vty_mutex) ;
} ;
@@ -137,6 +140,8 @@ vty_is_cli_thread(void)
*/
#if VTY_DEBUG
+extern int vty_assert_fail ;
+
Inline void
VTY_ASSERT_FAILED(void)
{
diff --git a/lib/vty_pipe.c b/lib/vty_pipe.c
index d4f1c9ba..3276085c 100644
--- a/lib/vty_pipe.c
+++ b/lib/vty_pipe.c
@@ -24,12 +24,11 @@
#include <zebra.h>
#include "vty.h"
-#include "uty.h"
#include "vty_cli.h"
+#include "vty_command.h"
#include "vty_io.h"
#include "vio_lines.h"
-#include "command.h"
#include "command_execute.h"
#include "command_queue.h"
@@ -194,7 +193,7 @@
*
*/
-
+#if 0
@@ -684,7 +683,7 @@ uty_cli_dispatch(vty_io vio)
cli_do = vio->cli_do ; /* current operation */
vio->cli_do = cli_do_nothing ; /* clear */
- qs_clear(&vio->cl) ; /* set cl empty (with '\0') */
+ qs_clear(vio->cl) ; /* set cl empty (with '\0') */
/* Reset the command output FIFO and line_control */
assert(vio_fifo_empty(&vio->cmd_obuf)) ;
@@ -706,7 +705,7 @@ uty_cli_dispatch(vty_io vio)
break ;
case cli_do_command:
- ret = uty_command(vty) ;
+ ret = uty_dispatch_command(vty) ;
break ;
case cli_do_ctrl_c:
@@ -1401,7 +1400,7 @@ uty_cli_draw_this(vty_io vio, enum node_type node)
prompt = "%s ???: " ;
} ;
- qs_printf(&vio->cli_prompt_for_node, prompt, vty_host_name);
+ qs_printf(vio->cli_prompt_for_node, prompt, vty_host_name);
vio->cli_prompt_node = node ;
vio->cli_prompt_set = 1 ;
@@ -1778,12 +1777,12 @@ uty_cli_insert (vty_io vio, const char* chars, int n)
if (n <= 0)
return n ; /* avoid trouble */
- after = qs_insert(&vio->cl, chars, n) ;
+ after = qs_insert(vio->cl, chars, n) ;
- uty_cli_echo(vio, qs_cp_char(&vio->cl), after + n) ;
+ uty_cli_echo(vio, qs_cp_char(vio->cl), after) ;
- if (after != 0)
- uty_cli_echo_n(vio, telnet_backspaces, after) ;
+ if ((after - n) != 0)
+ uty_cli_echo_n(vio, telnet_backspaces, after - n) ;
vio->cl.cp += n ;
@@ -1806,7 +1805,7 @@ uty_cli_overwrite (vty_io vio, char* chars, int n)
if (n > 0)
{
- qs_replace(&vio->cl, chars, n) ;
+ qs_replace(vio->cl, n, chars, n) ;
uty_cli_echo(vio, chars, n) ;
vio->cl.cp += n ;
@@ -1854,7 +1853,7 @@ uty_cli_forwards(vty_io vio, int n)
if (n > 0)
{
- uty_cli_echo(vio, qs_cp_char(&vio->cl), n) ;
+ uty_cli_echo(vio, qs_cp_char(vio->cl), n) ;
vio->cl.cp += n ;
} ;
@@ -1907,10 +1906,10 @@ uty_cli_del_forwards(vty_io vio, int n)
if (n <= 0)
return 0 ;
- after = qs_delete(&vio->cl, n) ;
+ after = qs_delete(vio->cl, n) ;
if (after > 0)
- uty_cli_echo(vio, qs_cp_char(&vio->cl), after) ;
+ uty_cli_echo(vio, qs_cp_char(vio->cl), after) ;
uty_cli_echo_n(vio, telnet_spaces, n) ;
uty_cli_echo_n(vio, telnet_backspaces, after + n) ;
@@ -1969,8 +1968,8 @@ uty_cli_word_forwards_delta(vty_io vio)
assert(vio->cl.cp <= vio->cl.len) ;
- cp = qs_cp_char(&vio->cl) ;
- ep = qs_ep_char(&vio->cl) ;
+ cp = qs_cp_char(vio->cl) ;
+ ep = qs_ep_char(vio->cl) ;
tp = cp ;
@@ -2013,8 +2012,8 @@ uty_cli_word_backwards_delta(vty_io vio, int eat_spaces)
assert(vio->cl.cp <= vio->cl.len) ;
- cp = qs_cp_char(&vio->cl) ;
- sp = qs_chars(&vio->cl) ;
+ cp = qs_cp_char(vio->cl) ;
+ sp = qs_chars(vio->cl) ;
tp = cp ;
@@ -2128,7 +2127,7 @@ uty_cli_transpose_chars(vty_io vio)
uty_cli_backwards(vio, 1) ;
/* Pick up in the new order */
- cp = qs_cp_char(&vio->cl) ;
+ cp = qs_cp_char(vio->cl) ;
chars[1] = *cp++ ;
chars[0] = *cp ;
@@ -2176,13 +2175,13 @@ uty_cli_hist_add (vty_io vio, const char* cmd_line)
* Either way, replace the the previous line entry by moving hindex
* back !
*/
- if ((prev_line == NULL) || (qs_cmp_sig(prev_line, &line) == 0))
+ if ((prev_line == NULL) || (qs_cmp_sig(prev_line, line) == 0))
vio->hindex = prev_index ;
else
prev_line = vector_get_item(vio->hist, vio->hindex) ;
/* Now replace the hindex entry */
- vector_set_item(vio->hist, vio->hindex, qs_copy(prev_line, &line)) ;
+ vector_set_item(vio->hist, vio->hindex, qs_copy(prev_line, line)) ;
/* Advance to the near future and reset the history pointer */
vio->hindex++;
@@ -2225,7 +2224,7 @@ uty_cli_history_use(vty_io vio, int step)
* current command line -- so can get back to it.
*/
hist = vector_get_item(&vio->hist, vio->hindex) ;
- vector_set_item(vio->hist, vio->hindex, qs_copy(hist, &vio->cl)) ;
+ vector_set_item(vio->hist, vio->hindex, qs_copy(hist, vio->cl)) ;
} ;
/* Advance or retreat */
@@ -2255,7 +2254,7 @@ uty_cli_history_use(vty_io vio, int step)
/* Get previous line from history buffer and echo that */
old_len = vio->cl.len ;
- qs_copy(&vio->cl, hist) ;
+ qs_copy(vio->cl, hist) ;
/* Sort out wiping out any excess and setting the cursor position */
if (old_len > vio->cl.len)
@@ -2448,7 +2447,7 @@ static void
uty_cli_describe_show(vty_io vio, vector describe)
{
unsigned int i, cmd_width, desc_width;
- struct desc *desc, *desc_cr ;
+ struct desc *desc, *cr_item ;
/* Get width of the longest "word" */
cmd_width = 0;
@@ -2472,7 +2471,7 @@ uty_cli_describe_show(vty_io vio, vector describe)
desc_width = vio->width - (cmd_width + 6);
/* Print out description. */
- desc_cr = NULL ; /* put <cr> last if it appears */
+ cr_item = NULL ; /* put <cr> last if it appears */
for (i = 0; i < vector_active (describe); i++)
if ((desc = vector_slot (describe, i)) != NULL)
@@ -2480,17 +2479,17 @@ uty_cli_describe_show(vty_io vio, vector describe)
if (desc->cmd[0] == '\0')
continue;
- if (strcmp (desc->cmd, command_cr) == 0)
+ if (strcmp (desc->cmd, cr_string) == 0)
{
- desc_cr = desc;
+ cr_item = desc;
continue;
}
uty_cli_describe_fold (vio, cmd_width, desc_width, desc);
}
- if (desc_cr != NULL)
- uty_cli_describe_fold (vio, cmd_width, desc_width, desc_cr);
+ if (cr_item != NULL)
+ uty_cli_describe_fold (vio, cmd_width, desc_width, cr_item);
} ;
/*------------------------------------------------------------------------------
@@ -2865,3 +2864,5 @@ uty_telnet_command(vty_io vio, keystroke stroke, bool callback)
return dealt_with ;
} ;
+
+#endif
diff --git a/lib/workqueue.h b/lib/workqueue.h
index 34e68cd8..d9f32a04 100644
--- a/lib/workqueue.h
+++ b/lib/workqueue.h
@@ -161,7 +161,7 @@ extern void work_queue_unplug (struct work_queue *wq);
/* Helpers, exported for thread.c and command.c */
extern int work_queue_run (struct thread *);
-extern struct cmd_element show_work_queues_cmd;
+extern struct cmd_command show_work_queues_cmd;
/*==============================================================================
* The Inline functions
diff --git a/lib/zebra.h b/lib/zebra.h
index 799cfc3d..52e5d571 100644
--- a/lib/zebra.h
+++ b/lib/zebra.h
@@ -21,9 +21,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#ifndef _ZEBRA_H
#define _ZEBRA_H
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif /* HAVE_CONFIG_H */
+#include "zconfig.h"
#ifdef SUNOS_5
#define _XPG4_2
diff --git a/ospf6d/ospf6_lsa.c b/ospf6d/ospf6_lsa.c
index c1db3741..0b571bf4 100644
--- a/ospf6d/ospf6_lsa.c
+++ b/ospf6d/ospf6_lsa.c
@@ -867,10 +867,10 @@ DEFUN (no_debug_ospf6_lsa_type,
return CMD_SUCCESS;
}
-struct cmd_element debug_ospf6_lsa_type_cmd;
-struct cmd_element debug_ospf6_lsa_type_detail_cmd;
-struct cmd_element no_debug_ospf6_lsa_type_cmd;
-struct cmd_element no_debug_ospf6_lsa_type_detail_cmd;
+struct cmd_command debug_ospf6_lsa_type_cmd;
+struct cmd_command debug_ospf6_lsa_type_detail_cmd;
+struct cmd_command no_debug_ospf6_lsa_type_cmd;
+struct cmd_command no_debug_ospf6_lsa_type_detail_cmd;
void
install_element_ospf6_debug_lsa (void)
diff --git a/ripngd/ripngd.c b/ripngd/ripngd.c
index cd4e0586..6a7bbc54 100644
--- a/ripngd/ripngd.c
+++ b/ripngd/ripngd.c
@@ -16,7 +16,7 @@
* You should have received a copy of the GNU General Public License
* along with GNU Zebra; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
- * 02111-1307, USA.
+ * 02111-1307, USA.
*/
#include <zebra.h>
@@ -97,9 +97,9 @@ ripng_info_free (struct ripng_info *rinfo)
{
XFREE (MTYPE_RIPNG_ROUTE, rinfo);
}
-
+
/* Create ripng socket. */
-static int
+static int
ripng_make_socket (void)
{
int ret;
@@ -138,7 +138,7 @@ ripng_make_socket (void)
if (ripngd_privs.change (ZPRIVS_RAISE))
zlog_err ("ripng_make_socket: could not raise privs");
-
+
ret = bind (sock, (struct sockaddr *) &ripaddr, sizeof (ripaddr));
if (ret < 0)
{
@@ -154,7 +154,7 @@ ripng_make_socket (void)
/* Send RIPng packet. */
int
-ripng_send_packet (caddr_t buf, int bufsize, struct sockaddr_in6 *to,
+ripng_send_packet (caddr_t buf, int bufsize, struct sockaddr_in6 *to,
struct interface *ifp)
{
int ret;
@@ -214,7 +214,7 @@ ripng_send_packet (caddr_t buf, int bufsize, struct sockaddr_in6 *to,
if (ret < 0) {
if (to)
- zlog_err ("RIPng send fail on %s to %s: %s", ifp->name,
+ zlog_err ("RIPng send fail on %s to %s: %s", ifp->name,
inet6_ntoa (to->sin6_addr), safe_strerror (errno));
else
zlog_err ("RIPng send fail on %s: %s", ifp->name, safe_strerror (errno));
@@ -226,14 +226,14 @@ ripng_send_packet (caddr_t buf, int bufsize, struct sockaddr_in6 *to,
/* Receive UDP RIPng packet from socket. */
static int
ripng_recv_packet (int sock, u_char *buf, int bufsize,
- struct sockaddr_in6 *from, unsigned int *ifindex,
+ struct sockaddr_in6 *from, unsigned int *ifindex,
int *hoplimit)
{
int ret;
struct msghdr msg;
struct iovec iov;
struct cmsghdr *cmsgptr;
- struct in6_addr dst;
+ struct in6_addr dst = IN6ADDR_ANY_INIT ;
/* Ancillary data. This store cmsghdr and in6_pktinfo. But at this
point I can't determine size of cmsghdr */
@@ -255,14 +255,14 @@ ripng_recv_packet (int sock, u_char *buf, int bufsize,
return ret;
for (cmsgptr = ZCMSG_FIRSTHDR(&msg); cmsgptr != NULL;
- cmsgptr = CMSG_NXTHDR(&msg, cmsgptr))
+ cmsgptr = CMSG_NXTHDR(&msg, cmsgptr))
{
/* I want interface index which this packet comes from. */
if (cmsgptr->cmsg_level == IPPROTO_IPV6 &&
- cmsgptr->cmsg_type == IPV6_PKTINFO)
+ cmsgptr->cmsg_type == IPV6_PKTINFO)
{
struct in6_pktinfo *ptr;
-
+
ptr = (struct in6_pktinfo *) CMSG_DATA (cmsgptr);
*ifindex = ptr->ipi6_ifindex;
dst = ptr->ipi6_addr;
@@ -305,7 +305,7 @@ ripng_packet_dump (struct ripng_packet *packet, int size, const char *sndrcv)
command_str = "unknown";
/* Dump packet header. */
- zlog_debug ("%s %s version %d packet size %d",
+ zlog_debug ("%s %s version %d packet size %d",
sndrcv, command_str, packet->version, size);
/* Dump each routing table entry. */
@@ -316,8 +316,8 @@ ripng_packet_dump (struct ripng_packet *packet, int size, const char *sndrcv)
if (rte->metric == RIPNG_METRIC_NEXTHOP)
zlog_debug (" nexthop %s/%d", inet6_ntoa (rte->addr), rte->prefixlen);
else
- zlog_debug (" %s/%d metric %d tag %d",
- inet6_ntoa (rte->addr), rte->prefixlen,
+ zlog_debug (" %s/%d metric %d tag %d",
+ inet6_ntoa (rte->addr), rte->prefixlen,
rte->metric, ntohs (rte->tag));
}
}
@@ -335,7 +335,7 @@ ripng_nexthop_rte (struct rte *rte,
zlog_debug ("RIPng nexthop RTE address %s tag %d prefixlen %d",
inet6_ntoa (rte->addr), ntohs (rte->tag), rte->prefixlen);
- /* RFC2080 2.1.1 Next Hop:
+ /* RFC2080 2.1.1 Next Hop:
The route tag and prefix length in the next hop RTE must be
set to zero on sending and ignored on receiption. */
if (ntohs (rte->tag) != 0)
@@ -413,7 +413,7 @@ ripng_garbage_collect (struct thread *t)
/* Off timeout timer. */
RIPNG_TIMER_OFF (rinfo->t_timeout);
-
+
/* Get route_node pointer. */
rp = rinfo->rp;
@@ -441,7 +441,7 @@ ripng_timeout (struct thread *t)
rp = rinfo->rp;
/* - The garbage-collection timer is set for 120 seconds. */
- RIPNG_TIMER_ON (rinfo->t_garbage_collect, ripng_garbage_collect,
+ RIPNG_TIMER_ON (rinfo->t_garbage_collect, ripng_garbage_collect,
ripng->garbage_time);
/* Delete this route from the kernel. */
@@ -485,7 +485,7 @@ ripng_incoming_filter (struct prefix_ipv6 *p, struct ripng_interface *ri)
/* Input distribute-list filtering. */
if (ri->list[RIPNG_FILTER_IN])
{
- if (access_list_apply (ri->list[RIPNG_FILTER_IN],
+ if (access_list_apply (ri->list[RIPNG_FILTER_IN],
(struct prefix *) p) == FILTER_DENY)
{
if (IS_RIPNG_DEBUG_PACKET)
@@ -496,7 +496,7 @@ ripng_incoming_filter (struct prefix_ipv6 *p, struct ripng_interface *ri)
}
if (ri->prefix[RIPNG_FILTER_IN])
{
- if (prefix_list_apply (ri->prefix[RIPNG_FILTER_IN],
+ if (prefix_list_apply (ri->prefix[RIPNG_FILTER_IN],
(struct prefix *) p) == PREFIX_DENY)
{
if (IS_RIPNG_DEBUG_PACKET)
@@ -513,7 +513,7 @@ ripng_incoming_filter (struct prefix_ipv6 *p, struct ripng_interface *ri)
if (dist->list[DISTRIBUTE_IN])
{
alist = access_list_lookup (AFI_IP6, dist->list[DISTRIBUTE_IN]);
-
+
if (alist)
{
if (access_list_apply (alist,
@@ -529,7 +529,7 @@ ripng_incoming_filter (struct prefix_ipv6 *p, struct ripng_interface *ri)
if (dist->prefix[DISTRIBUTE_IN])
{
plist = prefix_list_lookup (AFI_IP6, dist->prefix[DISTRIBUTE_IN]);
-
+
if (plist)
{
if (prefix_list_apply (plist,
@@ -583,7 +583,7 @@ ripng_outgoing_filter (struct prefix_ipv6 *p, struct ripng_interface *ri)
if (dist->list[DISTRIBUTE_OUT])
{
alist = access_list_lookup (AFI_IP6, dist->list[DISTRIBUTE_OUT]);
-
+
if (alist)
{
if (access_list_apply (alist,
@@ -599,7 +599,7 @@ ripng_outgoing_filter (struct prefix_ipv6 *p, struct ripng_interface *ri)
if (dist->prefix[DISTRIBUTE_OUT])
{
plist = prefix_list_lookup (AFI_IP6, dist->prefix[DISTRIBUTE_OUT]);
-
+
if (plist)
{
if (prefix_list_apply (plist,
@@ -669,7 +669,7 @@ ripng_route_process (struct rte *rte, struct sockaddr_in6 *from,
newinfo.metric_out = rte->metric; /* XXX */
newinfo.tag = ntohs(rte->tag); /* XXX */
- ret = route_map_apply (ri->routemap[RIPNG_FILTER_IN],
+ ret = route_map_apply (ri->routemap[RIPNG_FILTER_IN],
(struct prefix *)&p, RMAP_RIPNG, &newinfo);
if (ret == RMAP_DENYMATCH)
@@ -707,7 +707,7 @@ ripng_route_process (struct rte *rte, struct sockaddr_in6 *from,
* arrived. If the result is greater than infinity, use infinity
* (RFC2453 Sec. 3.9.2)
**/
-
+
/* Zebra ripngd can handle offset-list in. */
ret = ripng_offset_list_apply_in (&p, ifp, &rte->metric);
@@ -755,7 +755,7 @@ ripng_route_process (struct rte *rte, struct sockaddr_in6 *from,
if (rte->metric != RIPNG_METRIC_INFINITY)
{
rinfo = ripng_info_new ();
-
+
/* - Setting the destination prefix and length to those in
the RTE. */
rp->info = rinfo;
@@ -799,12 +799,12 @@ ripng_route_process (struct rte *rte, struct sockaddr_in6 *from,
else
{
rinfo = rp->info;
-
+
/* If there is an existing route, compare the next hop address
to the address of the router from which the datagram came.
If this datagram is from the same router as the existing
route, reinitialize the timeout. */
- same = (IN6_ARE_ADDR_EQUAL (&rinfo->from, &from->sin6_addr)
+ same = (IN6_ARE_ADDR_EQUAL (&rinfo->from, &from->sin6_addr)
&& (rinfo->ifindex == ifp->ifindex));
if (same)
@@ -881,7 +881,7 @@ ripng_route_process (struct rte *rte, struct sockaddr_in6 *from,
if (oldmetric != RIPNG_METRIC_INFINITY)
{
/* - The garbage-collection timer is set for 120 seconds. */
- RIPNG_TIMER_ON (rinfo->t_garbage_collect,
+ RIPNG_TIMER_ON (rinfo->t_garbage_collect,
ripng_garbage_collect, ripng->garbage_time);
RIPNG_TIMER_OFF (rinfo->t_timeout);
@@ -914,7 +914,7 @@ ripng_route_process (struct rte *rte, struct sockaddr_in6 *from,
/* Add redistributed route to RIPng table. */
void
-ripng_redistribute_add (int type, int sub_type, struct prefix_ipv6 *p,
+ripng_redistribute_add (int type, int sub_type, struct prefix_ipv6 *p,
unsigned int ifindex, struct in6_addr *nexthop)
{
struct route_node *rp;
@@ -959,7 +959,7 @@ ripng_redistribute_add (int type, int sub_type, struct prefix_ipv6 *p,
return;
}
}
-
+
RIPNG_TIMER_OFF (rinfo->t_timeout);
RIPNG_TIMER_OFF (rinfo->t_garbage_collect);
@@ -984,10 +984,10 @@ ripng_redistribute_add (int type, int sub_type, struct prefix_ipv6 *p,
rinfo->ifindex = ifindex;
rinfo->metric = 1;
rinfo->rp = rp;
-
+
if (nexthop && IN6_IS_ADDR_LINKLOCAL(nexthop))
rinfo->nexthop = *nexthop;
-
+
rinfo->flags |= RIPNG_RTF_FIB;
rp->info = rinfo;
@@ -1012,7 +1012,7 @@ ripng_redistribute_add (int type, int sub_type, struct prefix_ipv6 *p,
/* Delete redistributed route to RIPng table. */
void
-ripng_redistribute_delete (int type, int sub_type, struct prefix_ipv6 *p,
+ripng_redistribute_delete (int type, int sub_type, struct prefix_ipv6 *p,
unsigned int ifindex)
{
struct route_node *rp;
@@ -1039,13 +1039,13 @@ ripng_redistribute_delete (int type, int sub_type, struct prefix_ipv6 *p,
rinfo = rp->info;
if (rinfo != NULL
- && rinfo->type == type
- && rinfo->sub_type == sub_type
+ && rinfo->type == type
+ && rinfo->sub_type == sub_type
&& rinfo->ifindex == ifindex)
{
/* Perform poisoned reverse. */
rinfo->metric = RIPNG_METRIC_INFINITY;
- RIPNG_TIMER_ON (rinfo->t_garbage_collect,
+ RIPNG_TIMER_ON (rinfo->t_garbage_collect,
ripng_garbage_collect, ripng->garbage_time);
RIPNG_TIMER_OFF (rinfo->t_timeout);
@@ -1053,7 +1053,7 @@ ripng_redistribute_delete (int type, int sub_type, struct prefix_ipv6 *p,
ripng_aggregate_decrement (rp, rinfo);
rinfo->flags |= RIPNG_RTF_CHANGED;
-
+
if (IS_RIPNG_DEBUG_EVENT)
zlog_debug ("Poisone %s/%d on the interface %s with an infinity metric [delete]",
inet6_ntoa(p->prefix), p->prefixlen,
@@ -1073,7 +1073,7 @@ ripng_redistribute_withdraw (int type)
if (!ripng)
return;
-
+
for (rp = route_top (ripng->table); rp; rp = route_next (rp))
if ((rinfo = rp->info) != NULL)
{
@@ -1082,7 +1082,7 @@ ripng_redistribute_withdraw (int type)
{
/* Perform poisoned reverse. */
rinfo->metric = RIPNG_METRIC_INFINITY;
- RIPNG_TIMER_ON (rinfo->t_garbage_collect,
+ RIPNG_TIMER_ON (rinfo->t_garbage_collect,
ripng_garbage_collect, ripng->garbage_time);
RIPNG_TIMER_OFF (rinfo->t_timeout);
@@ -1106,7 +1106,7 @@ ripng_redistribute_withdraw (int type)
/* RIP routing information. */
static void
-ripng_response_process (struct ripng_packet *packet, int size,
+ripng_response_process (struct ripng_packet *packet, int size,
struct sockaddr_in6 *from, struct interface *ifp,
int hoplimit)
{
@@ -1162,7 +1162,7 @@ ripng_response_process (struct ripng_packet *packet, int size,
/* Update RIPng peer. */
ripng_peer_update (from, packet->version);
-
+
/* Reset nexthop. */
memset (&nexthop, 0, sizeof (struct ripng_nexthop));
nexthop.flag = RIPNG_NEXTHOP_UNSPEC;
@@ -1170,7 +1170,7 @@ ripng_response_process (struct ripng_packet *packet, int size,
/* Set RTE pointer. */
rte = packet->rte;
- for (lim = ((caddr_t) packet) + size; (caddr_t) rte < lim; rte++)
+ for (lim = ((caddr_t) packet) + size; (caddr_t) rte < lim; rte++)
{
/* First of all, we have to check this RTE is next hop RTE or
not. Next hop RTE is completely different with normal RTE so
@@ -1239,7 +1239,7 @@ ripng_response_process (struct ripng_packet *packet, int size,
/* Response to request message. */
static void
-ripng_request_process (struct ripng_packet *packet,int size,
+ripng_request_process (struct ripng_packet *packet,int size,
struct sockaddr_in6 *from, struct interface *ifp)
{
caddr_t lim;
@@ -1283,7 +1283,7 @@ ripng_request_process (struct ripng_packet *packet,int size,
IN6_IS_ADDR_UNSPECIFIED (&rte->addr) &&
rte->prefixlen == 0 &&
rte->metric == RIPNG_METRIC_INFINITY)
- {
+ {
/* All route with split horizon */
ripng_output_process (ifp, from, ripng_all_route);
}
@@ -1306,7 +1306,7 @@ ripng_request_process (struct ripng_packet *packet,int size,
p.prefix = rte->addr;
p.prefixlen = rte->prefixlen;
apply_mask_ipv6 (&p);
-
+
rp = route_node_lookup (ripng->table, (struct prefix *) &p);
if (rp)
@@ -1349,10 +1349,10 @@ ripng_read (struct thread *thread)
ripng_event (RIPNG_READ, sock);
/* Read RIPng packet. */
- len = ripng_recv_packet (sock, STREAM_DATA (ripng->ibuf),
+ len = ripng_recv_packet (sock, STREAM_DATA (ripng->ibuf),
STREAM_SIZE (ripng->ibuf), &from, &ifindex,
&hoplimit);
- if (len < 0)
+ if (len < 0)
{
zlog_warn ("RIPng recvfrom failed: %s.", safe_strerror (errno));
return len;
@@ -1374,7 +1374,7 @@ ripng_read (struct thread *thread)
/* RIPng packet received. */
if (IS_RIPNG_DEBUG_EVENT)
zlog_debug ("RIPng packet received from %s port %d on %s",
- inet6_ntoa (from.sin6_addr), ntohs (from.sin6_port),
+ inet6_ntoa (from.sin6_addr), ntohs (from.sin6_port),
ifp ? ifp->name : "unknown");
/* Logging before packet checking. */
@@ -1389,9 +1389,9 @@ ripng_read (struct thread *thread)
}
/* Packet version mismatch checking. */
- if (packet->version != ripng->version)
+ if (packet->version != ripng->version)
{
- zlog_warn ("RIPng packet version %d doesn't fit to my version %d",
+ zlog_warn ("RIPng packet version %d doesn't fit to my version %d",
packet->version, ripng->version);
ripng_peer_bad_packet (&from);
return 0;
@@ -1463,7 +1463,7 @@ ripng_update (struct thread *t)
if (ri->ri_send == RIPNG_SEND_OFF)
{
if (IS_RIPNG_DEBUG_EVENT)
- zlog (NULL, LOG_DEBUG,
+ zlog (NULL, LOG_DEBUG,
"[Event] RIPng send to if %d is suppressed by config",
ifp->ifindex);
continue;
@@ -1500,7 +1500,7 @@ ripng_triggered_interval (struct thread *t)
ripng_triggered_update (t);
}
return 0;
-}
+}
/* Execute triggered update. */
int
@@ -1555,7 +1555,7 @@ ripng_triggered_update (struct thread *t)
update is triggered when the timer expires. */
interval = (random () % 5) + 1;
- ripng->t_triggered_interval =
+ ripng->t_triggered_interval =
thread_add_timer (master, ripng_triggered_interval, NULL, interval);
return 0;
@@ -1613,9 +1613,9 @@ ripng_output_process (struct interface *ifp, struct sockaddr_in6 *to,
/* Get RIPng interface. */
ri = ifp->info;
-
+
ripng_rte_list = ripng_rte_new();
-
+
for (rp = route_top (ripng->table); rp; rp = route_next (rp))
{
if ((rinfo = rp->info) != NULL && rinfo->suppress == 0)
@@ -1667,8 +1667,8 @@ ripng_output_process (struct interface *ifp, struct sockaddr_in6 *to,
{
int ret;
- ret = route_map_apply (ri->routemap[RIPNG_FILTER_OUT],
- (struct prefix *) p, RMAP_RIPNG,
+ ret = route_map_apply (ri->routemap[RIPNG_FILTER_OUT],
+ (struct prefix *) p, RMAP_RIPNG,
rinfo);
if (ret == RMAP_DENYMATCH)
@@ -1726,7 +1726,7 @@ ripng_output_process (struct interface *ifp, struct sockaddr_in6 *to,
if (rinfo->metric_out > RIPNG_METRIC_INFINITY)
rinfo->metric_out = RIPNG_METRIC_INFINITY;
- /* Perform split-horizon with poisoned reverse
+ /* Perform split-horizon with poisoned reverse
* for RIPng routes.
**/
if (ri->split_horizon == RIPNG_SPLIT_HORIZON_POISONED_REVERSE) {
@@ -1740,8 +1740,8 @@ ripng_output_process (struct interface *ifp, struct sockaddr_in6 *to,
}
/* Process the aggregated RTE entry */
- if ((aggregate = rp->aggregate) != NULL &&
- aggregate->count > 0 &&
+ if ((aggregate = rp->aggregate) != NULL &&
+ aggregate->count > 0 &&
aggregate->suppress == 0)
{
/* If no route-map are applied, the RTE will be these following
@@ -1772,8 +1772,8 @@ ripng_output_process (struct interface *ifp, struct sockaddr_in6 *to,
newinfo.tag = aggregate->tag;
newinfo.tag_out = aggregate->tag_out;
- ret = route_map_apply (ri->routemap[RIPNG_FILTER_OUT],
- (struct prefix *) p, RMAP_RIPNG,
+ ret = route_map_apply (ri->routemap[RIPNG_FILTER_OUT],
+ (struct prefix *) p, RMAP_RIPNG,
&newinfo);
if (ret == RMAP_DENYMATCH)
@@ -1833,7 +1833,7 @@ ripng_create (void)
ripng->timeout_time = RIPNG_TIMEOUT_TIMER_DEFAULT;
ripng->garbage_time = RIPNG_GARBAGE_TIMER_DEFAULT;
ripng->default_metric = RIPNG_DEFAULT_METRIC_DEFAULT;
-
+
/* Make buffer. */
ripng->ibuf = stream_new (RIPNG_MAX_PACKET_SIZE * 5);
ripng->obuf = stream_new (RIPNG_MAX_PACKET_SIZE);
@@ -1842,7 +1842,7 @@ ripng_create (void)
ripng->table = route_table_init ();
ripng->route = route_table_init ();
ripng->aggregate = route_table_init ();
-
+
/* Make socket. */
ripng->sock = ripng_make_socket ();
if (ripng->sock < 0)
@@ -1879,11 +1879,11 @@ ripng_request (struct interface *ifp)
rte = ripng_packet.rte;
rte->metric = RIPNG_METRIC_INFINITY;
- return ripng_send_packet ((caddr_t) &ripng_packet, sizeof (ripng_packet),
+ return ripng_send_packet ((caddr_t) &ripng_packet, sizeof (ripng_packet),
NULL, ifp);
}
-
+
static int
ripng_update_jitter (int time)
{
@@ -1910,22 +1910,22 @@ ripng_event (enum ripng_event event, int sock)
/* Update timer jitter. */
jitter = ripng_update_jitter (ripng->update_time);
- ripng->t_update =
- thread_add_timer (master, ripng_update, NULL,
+ ripng->t_update =
+ thread_add_timer (master, ripng_update, NULL,
sock ? 2 : ripng->update_time + jitter);
break;
case RIPNG_TRIGGERED_UPDATE:
if (ripng->t_triggered_interval)
ripng->trigger = 1;
else if (! ripng->t_triggered_update)
- ripng->t_triggered_update =
+ ripng->t_triggered_update =
thread_add_event (master, ripng_triggered_update, NULL, 0);
break;
default:
break;
}
}
-
+
/* Print out routes update time. */
static void
@@ -1936,7 +1936,7 @@ ripng_vty_out_uptime (struct vty *vty, struct ripng_info *rinfo)
#define TIME_BUF 25
char timebuf [TIME_BUF];
struct thread *thread;
-
+
if ((thread = rinfo->t_timeout) != NULL)
{
clock = thread_timer_remain_second (thread);
@@ -1983,7 +1983,7 @@ ripng_route_subtype_print (struct ripng_info *rinfo)
strcat(str, "?");
break;
}
-
+
return str;
}
@@ -2003,7 +2003,7 @@ DEFUN (show_ipv6_ripng,
if (! ripng)
return CMD_SUCCESS;
- /* Header of display. */
+ /* Header of display. */
vty_out (vty, "Codes: R - RIPng, C - connected, S - Static, O - OSPF, B - BGP%s"
"Sub-codes:%s"
" (n) - normal, (s) - static, (d) - default, (r) - redistribute,%s"
@@ -2011,7 +2011,7 @@ DEFUN (show_ipv6_ripng,
" Network Next Hop Via Metric Tag Time%s",
VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
-
+
for (rp = route_top (ripng->table); rp; rp = route_next (rp))
{
if ((aggregate = rp->aggregate) != NULL)
@@ -2023,7 +2023,7 @@ DEFUN (show_ipv6_ripng,
aggregate->count, aggregate->suppress,
inet6_ntoa (p->prefix), p->prefixlen);
#else
- len = vty_out (vty, "R(a) %s/%d ",
+ len = vty_out (vty, "R(a) %s/%d ",
inet6_ntoa (p->prefix), p->prefixlen);
#endif /* DEBUG */
vty_out (vty, "%s", VTY_NEWLINE);
@@ -2060,7 +2060,7 @@ DEFUN (show_ipv6_ripng,
len = vty_out (vty, "%*s", len, " ");
/* from */
- if ((rinfo->type == ZEBRA_ROUTE_RIPNG) &&
+ if ((rinfo->type == ZEBRA_ROUTE_RIPNG) &&
(rinfo->sub_type == RIPNG_ROUTE_RTE))
{
len = vty_out (vty, "%s", ifindex2ifname(rinfo->ifindex));
@@ -2078,7 +2078,7 @@ DEFUN (show_ipv6_ripng,
rinfo->metric, rinfo->tag);
/* time */
- if ((rinfo->type == ZEBRA_ROUTE_RIPNG) &&
+ if ((rinfo->type == ZEBRA_ROUTE_RIPNG) &&
(rinfo->sub_type == RIPNG_ROUTE_RTE))
{
/* RTE from remote RIP routers */
@@ -2141,7 +2141,7 @@ DEFUN (show_ipv6_ripng_status,
for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp))
{
struct ripng_interface *ri;
-
+
ri = ifp->info;
if (ri->enable_network || ri->enable_interface)
@@ -2161,7 +2161,7 @@ DEFUN (show_ipv6_ripng_status,
vty_out (vty, " Gateway BadPackets BadRoutes Distance Last Update%s", VTY_NEWLINE);
ripng_peer_display (vty);
- return CMD_SUCCESS;
+ return CMD_SUCCESS;
}
DEFUN (router_ripng,
@@ -2490,14 +2490,14 @@ DEFUN (ripng_timers,
vty_out (vty, "update timer value error%s", VTY_NEWLINE);
return CMD_WARNING;
}
-
+
timeout = strtoul (argv[1], &endptr, 10);
if (timeout == ULONG_MAX || *endptr != '\0')
{
vty_out (vty, "timeout timer value error%s", VTY_NEWLINE);
return CMD_WARNING;
}
-
+
garbage = strtoul (argv[2], &endptr, 10);
if (garbage == ULONG_MAX || *endptr != '\0')
{
@@ -2554,7 +2554,7 @@ DEFUN (show_ipv6_protocols, show_ipv6_protocols_cmd,
return CMD_SUCCESS;
vty_out (vty, "Routing Protocol is \"ripng\"%s", VTY_NEWLINE);
-
+
vty_out (vty, "Sending updates every %ld seconds, next due in %d seconds%s",
ripng->update_time, 0,
VTY_NEWLINE);
@@ -2637,13 +2637,13 @@ ripng_config_write (struct vty *vty)
/* RIP offset-list configuration. */
config_write_ripng_offset_list (vty);
-
+
/* RIPng aggregate routes. */
for (rp = route_top (ripng->aggregate); rp; rp = route_next (rp))
if (rp->info != NULL)
- vty_out (vty, " aggregate-address %s/%d%s",
+ vty_out (vty, " aggregate-address %s/%d%s",
inet6_ntoa (rp->p.u.prefix6),
- rp->p.prefixlen,
+ rp->p.prefixlen,
VTY_NEWLINE);
@@ -2782,7 +2782,7 @@ ripng_distribute_update_all_wrapper (struct access_list *notused)
{
ripng_distribute_update_all(NULL);
}
-
+
/* delete all the added ripng routes. */
void
ripng_clean()
@@ -2933,10 +2933,10 @@ ripng_routemap_update_redistribute (void)
if (ripng)
{
- for (i = 0; i < ZEBRA_ROUTE_MAX; i++)
+ for (i = 0; i < ZEBRA_ROUTE_MAX; i++)
{
if (ripng->route_map[i].name)
- ripng->route_map[i].map =
+ ripng->route_map[i].map =
route_map_lookup_by_name (ripng->route_map[i].name);
}
}
diff --git a/tests/test-list_util.c b/tests/test-list_util.c
index a3c6ad59..c4e88502 100644
--- a/tests/test-list_util.c
+++ b/tests/test-list_util.c
@@ -1,4 +1,5 @@
#include <zebra.h>
+#include "misc.h"
#include <list_util.h>
#include <string.h>
@@ -253,12 +254,12 @@ test_ssl(void)
this = base ;
first = base->next ;
ret = ssl_del(base, this, next) ;
- test_assert(ret == 0, "ssl_del did not return 0") ;
+ test_assert(ret == true, "ssl_del did not return true") ;
test_assert(first == base, "ssl_del of first item failed") ;
this = other->base ;
ret = ssl_del(other->base, this, other_next) ;
- test_assert(ret == 0, "ssl_del did not return 0") ;
+ test_assert(ret == true, "ssl_del did not return true") ;
test_assert(first == other->base, "ssl_del of first item failed") ;
printf("\n") ;
@@ -271,9 +272,9 @@ test_ssl(void)
*/
printf(" Deleting arbitrary items") ;
ret = ssl_del(base, del, next) ;
- test_assert(ret == 0, "ssl_del did not return 0") ;
+ test_assert(ret == true, "ssl_del did not return true") ;
ret = ssl_del(ssl_parent.base, other_del, other_next) ;
- test_assert(ret == 0, "ssl_del did not return 0") ;
+ test_assert(ret == true, "ssl_del did not return true") ;
printf("\n") ;
/* Deletion of items from arbitrary place in list
@@ -282,9 +283,9 @@ test_ssl(void)
*/
printf(" Deleting non-existant items") ;
ret = ssl_del(base, &dummy, next) ;
- test_assert(ret == -1, "ssl_del did not return -1") ;
+ test_assert(ret == false, "ssl_del did not return false") ;
ret = ssl_del(other->base, &dummy, other_next) ;
- test_assert(ret == -1, "ssl_del did not return -1") ;
+ test_assert(ret == false, "ssl_del did not return false") ;
printf("\n") ;
/* Deletion of NULL items
@@ -296,10 +297,10 @@ test_ssl(void)
this = NULL ;
ret = ssl_del(base, this, next) ;
- test_assert(ret == 0, "ssl_del did not return 0") ;
+ test_assert(ret == false, "ssl_del did not return false") ;
ret = ssl_del(ssl_parent.base, this, other_next) ;
- test_assert(ret == 0, "ssl_del did not return 0") ;
+ test_assert(ret == false, "ssl_del did not return false") ;
printf("\n") ;
@@ -567,21 +568,21 @@ test_ssl(void)
test_assert(other->base == NULL, "ssl_del of first and only item failed") ;
ret = ssl_del(base, del, next) ;
- test_assert(ret == -1, "ssl_del did not return -1") ;
+ test_assert(ret == false, "ssl_del did not return false") ;
test_assert(base == NULL, "ssl_del on empty list") ;
ret = ssl_del(other->base, other_del, other_next) ;
- test_assert(ret == -1, "ssl_del did not return -1") ;
+ test_assert(ret == false, "ssl_del did not return false") ;
test_assert(other->base == NULL, "ssl_del on empty list") ;
this = NULL ;
ret = ssl_del(base, this, next) ;
- test_assert(ret == 0, "ssl_del did not return 0") ;
+ test_assert(ret == false, "ssl_del did not return false") ;
test_assert(base == NULL, "ssl_del on empty list") ;
ret = ssl_del(other->base, this, other_next) ;
- test_assert(ret == 0, "ssl_del did not return 0") ;
+ test_assert(ret == false, "ssl_del did not return false") ;
test_assert(other->base == NULL, "ssl_del on empty list") ;
printf("\n") ;
diff --git a/tests/test-vector.c b/tests/test-vector.c
index a73e5637..e95e3c58 100644
--- a/tests/test-vector.c
+++ b/tests/test-vector.c
@@ -25,7 +25,7 @@ void test_vector_insert_item(void);
void test_vector_insert_item_here(void);
void test_vector_delete_item(void);
void do_test_insert(const int rider);
-int sort_cmp(const void** a, const void** b);
+int sort_cmp(void const* const* a, void const* const* b);
void test_vector_sort(void);
void test_vector_bsearch(void);
void test_vector_move_item_here(void);
@@ -550,7 +550,7 @@ test_vector_sort(void)
}
int
-sort_cmp(const void** a, const void** b)
+sort_cmp(void const* const* a, void const* const* b)
{
return strcmp(*a, *b);
}
diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c
index 3692a953..17f6a994 100644
--- a/vtysh/vtysh.c
+++ b/vtysh/vtysh.c
@@ -30,8 +30,8 @@
#include <readline/readline.h>
#include <readline/history.h>
-#include "command.h"
#include "command_execute.h"
+#include "command.h"
#include "memory.h"
#include "vtysh/vtysh.h"
#include "log.h"
@@ -278,7 +278,7 @@ vtysh_execute_func (const char *line, int pager)
{
int ret, cmd_stat;
u_int i;
- struct cmd_element *cmd;
+ struct cmd_command *cmd;
FILE *fp = NULL;
int closepager = 0;
int tried = 0;
@@ -287,7 +287,7 @@ vtysh_execute_func (const char *line, int pager)
/* TODO: how well does vtysh_execute_func work ?? -- esp. qpthreads_enabled */
vtysh_vty->buf = line ;
- saved_ret = ret = cmd_execute_command (vtysh_vty, cmd_parse_completion, &cmd);
+ saved_ret = ret = cmd_execute_command (vtysh_vty, cmd_parse_standard, &cmd);
if ((ret == CMD_SUCCESS) && (cmd == NULL))
return ret ; /* quit if nothing to do ??? */
@@ -300,8 +300,8 @@ vtysh_execute_func (const char *line, int pager)
while (ret != CMD_SUCCESS && ret != CMD_SUCCESS_DAEMON && ret != CMD_WARNING
&& vtysh_vty->node > CONFIG_NODE)
{
- vtysh_vty->node = node_parent(vtysh_vty->node);
- ret = cmd_execute_command (vtysh_vty, cmd_parse_completion, &cmd);
+ vtysh_vty->node = cmd_node_parent(vtysh_vty->node);
+ ret = cmd_execute_command (vtysh_vty, cmd_parse_standard, &cmd);
tried++;
}
@@ -385,7 +385,7 @@ vtysh_execute_func (const char *line, int pager)
vtysh_vty->buf = line ;
- ret = cmd_execute_command (vtysh_vty, cmd_parse_completion, &cmd);
+ ret = cmd_execute_command (vtysh_vty, cmd_parse_standard, &cmd);
if (ret != CMD_SUCCESS_DAEMON)
break;
}
@@ -442,7 +442,7 @@ int
vtysh_config_from_file (struct vty *vty, FILE *fp)
{
int ret;
- struct cmd_element *cmd;
+ struct cmd_command *cmd;
/* TODO: (1) allocate buffer for vty->buf (2) what about CMD_QUEUED ?? */
@@ -592,14 +592,14 @@ vtysh_rl_describe (void)
if (desc->cmd[0] == '\0')
continue;
- if (! desc->str)
+ if (! desc->doc)
fprintf (stdout," %-s\n",
desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd);
else
fprintf (stdout," %-*s %s\n",
width,
desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
- desc->str);
+ desc->doc);
}
cmd_free_strvec (vline);
diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c
index dfdc020a..c20c2b2c 100644
--- a/vtysh/vtysh_config.c
+++ b/vtysh/vtysh_config.c
@@ -285,12 +285,19 @@ vtysh_config_parse (char *line)
/* Macro to check delimiter is needed between each configuration line
* or not. */
-#define NO_DELIMITER(I) \
- ((I) == ACCESS_NODE || (I) == PREFIX_NODE || (I) == IP_NODE \
- || (I) == AS_LIST_NODE || (I) == COMMUNITY_LIST_NODE || \
- (I) == ACCESS_IPV6_NODE || (I) == PREFIX_IPV6_NODE \
- || (I) == SERVICE_NODE || (I) == FORWARDING_NODE || (I) == DEBUG_NODE \
- || (I) == AAA_NODE)
+#define NO_DELIMITER(I) ( \
+ (I) == ACCESS_NODE || \
+ (I) == PREFIX_NODE || \
+ (I) == IP_NODE || \
+ (I) == AS_LIST_NODE || \
+ (I) == COMMUNITY_LIST_NODE || \
+ (I) == ACCESS_IPV6_NODE || \
+ (I) == PREFIX_IPV6_NODE || \
+ (I) == SERVICE_NODE || \
+ (I) == FORWARDING_NODE || \
+ (I) == DEBUG_NODE || \
+ (I) == AAA_NODE || \
+ 0 )
/* Display configuration to file pointer. */
void