summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bgpd/bgp.h14
-rw-r--r--bgpd/bgp_advertise.h4
-rw-r--r--bgpd/bgp_aspath.h5
-rw-r--r--bgpd/bgp_btoa.c10
-rw-r--r--bgpd/bgp_clist.c8
-rw-r--r--bgpd/bgp_common.c1
-rw-r--r--bgpd/bgp_common.h7
-rw-r--r--bgpd/bgp_connection.c9
-rw-r--r--bgpd/bgp_connection.h6
-rw-r--r--bgpd/bgp_debug.c38
-rw-r--r--bgpd/bgp_dump.c24
-rw-r--r--bgpd/bgp_engine.h52
-rw-r--r--bgpd/bgp_main.c89
-rw-r--r--bgpd/bgp_msg_write.h3
-rw-r--r--bgpd/bgp_notification.c2
-rw-r--r--bgpd/bgp_notification.h6
-rw-r--r--bgpd/bgp_open_state.c18
-rw-r--r--bgpd/bgp_open_state.h10
-rw-r--r--bgpd/bgp_packet.c10
-rw-r--r--bgpd/bgp_peer.c2
-rw-r--r--bgpd/bgp_peer_index.c23
-rw-r--r--bgpd/bgp_route.c10
-rw-r--r--bgpd/bgp_route_refresh.c7
-rw-r--r--bgpd/bgp_route_refresh.h12
-rw-r--r--bgpd/bgp_routemap.c20
-rw-r--r--bgpd/bgp_session.c9
-rw-r--r--bgpd/bgp_session.h10
-rw-r--r--bgpd/bgp_snmp.c38
-rw-r--r--bgpd/bgp_vty.c235
-rw-r--r--bgpd/bgpd.c3
-rw-r--r--bgpd/bgpd.h4
-rwxr-xr-xconfigure.ac2
-rw-r--r--isisd/dict.c36
-rw-r--r--isisd/dict.h2
-rw-r--r--isisd/isis_misc.c37
-rw-r--r--isisd/isis_spf.c40
-rw-r--r--isisd/topology/spgrid.c3
-rw-r--r--lib/Makefile.am60
-rw-r--r--lib/command.c4199
-rw-r--r--lib/command.h305
-rw-r--r--lib/command_common.h288
-rw-r--r--lib/command_execute.c598
-rw-r--r--lib/command_execute.h133
-rw-r--r--lib/command_local.h203
-rw-r--r--lib/command_parse.c4745
-rw-r--r--lib/command_parse.h788
-rw-r--r--lib/command_queue.c556
-rw-r--r--lib/command_queue.h8
-rw-r--r--lib/elstring.c269
-rw-r--r--lib/elstring.h297
-rw-r--r--lib/errno_names.c2
-rw-r--r--lib/getopt.c4
-rw-r--r--lib/getopt1.c4
-rw-r--r--lib/heap.c93
-rw-r--r--lib/heap.h45
-rw-r--r--lib/if.c13
-rw-r--r--lib/if.h14
-rw-r--r--lib/keychain.c59
-rw-r--r--lib/keystroke.c92
-rw-r--r--lib/keystroke.h30
-rw-r--r--lib/list_util.c17
-rw-r--r--lib/list_util.h19
-rw-r--r--lib/log.c1360
-rw-r--r--lib/log.h193
-rw-r--r--lib/log_local.h155
-rw-r--r--lib/memory.c106
-rw-r--r--lib/memory.h77
-rw-r--r--lib/memtypes.c11
-rw-r--r--lib/misc.h163
-rw-r--r--lib/miyagi.h4
-rw-r--r--lib/mqueue.c126
-rw-r--r--lib/mqueue.h100
-rw-r--r--lib/network.c248
-rw-r--r--lib/network.h7
-rw-r--r--lib/node_type.h81
-rw-r--r--lib/plist.c45
-rw-r--r--lib/plist.h2
-rw-r--r--lib/prefix.c47
-rw-r--r--lib/prefix.h5
-rw-r--r--lib/privs.c71
-rw-r--r--lib/pthread_safe.c98
-rw-r--r--lib/pthread_safe.h12
-rw-r--r--lib/qafi_safi.h2
-rw-r--r--lib/qdebug_nb.h61
-rw-r--r--lib/qfstring.c352
-rw-r--r--lib/qfstring.h155
-rw-r--r--lib/qiovec.c297
-rw-r--r--lib/qiovec.h181
-rw-r--r--lib/qlib_init.c15
-rw-r--r--lib/qlib_init.h4
-rw-r--r--lib/qpath.c1212
-rw-r--r--lib/qpath.h215
-rw-r--r--lib/qpnexus.c114
-rw-r--r--lib/qpnexus.h63
-rw-r--r--lib/qpselect.c175
-rw-r--r--lib/qpselect.h31
-rw-r--r--lib/qpthreads.c212
-rw-r--r--lib/qpthreads.h201
-rw-r--r--lib/qrand.c48
-rw-r--r--lib/qrand.h52
-rw-r--r--lib/qstring.c793
-rw-r--r--lib/qstring.h815
-rw-r--r--lib/qtime.c253
-rw-r--r--lib/qtime.h155
-rw-r--r--lib/qtimers.c34
-rw-r--r--lib/qtimers.h41
-rw-r--r--lib/regex.c51
-rw-r--r--lib/routemap.c19
-rw-r--r--lib/routemap.h2
-rw-r--r--lib/sigevent.c990
-rw-r--r--lib/sigevent.h34
-rw-r--r--lib/sockopt.c9
-rw-r--r--lib/sockunion.c1
-rw-r--r--lib/stream.c2
-rw-r--r--lib/symtab.c69
-rw-r--r--lib/symtab.h83
-rw-r--r--lib/temp.c1349
-rw-r--r--lib/thread.c10
-rw-r--r--lib/thread.h4
-rw-r--r--lib/tstring.h139
-rw-r--r--lib/uty.h239
-rw-r--r--lib/vargs.h34
-rw-r--r--lib/vector.c487
-rw-r--r--lib/vector.h172
-rw-r--r--lib/version.h.in2
-rw-r--r--lib/vio_fifo.c1687
-rw-r--r--lib/vio_fifo.h286
-rw-r--r--lib/vio_lines.c594
-rw-r--r--lib/vio_lines.h146
-rw-r--r--lib/vty.c1366
-rw-r--r--lib/vty.h181
-rw-r--r--lib/vty_cli.c3351
-rw-r--r--lib/vty_cli.h219
-rw-r--r--lib/vty_command.c1756
-rw-r--r--lib/vty_command.h67
-rw-r--r--lib/vty_common.h141
-rw-r--r--lib/vty_io.c3351
-rw-r--r--lib/vty_io.h614
-rw-r--r--lib/vty_io_basic.c1039
-rw-r--r--lib/vty_io_basic.h230
-rw-r--r--lib/vty_io_file.c1930
-rw-r--r--lib/vty_io_file.h70
-rw-r--r--lib/vty_io_shell.c369
-rw-r--r--lib/vty_io_shell.h55
-rw-r--r--lib/vty_io_term.c1549
-rw-r--r--lib/vty_io_term.h79
-rw-r--r--lib/vty_io_vsh.c369
-rw-r--r--lib/vty_io_vsh.h55
-rw-r--r--lib/vty_local.h234
-rw-r--r--lib/vty_log.c258
-rw-r--r--lib/vty_log.h41
-rw-r--r--lib/vty_pipe.c496
-rw-r--r--lib/workqueue.h8
-rw-r--r--lib/zassert.h10
-rw-r--r--lib/zconfig.h42
-rw-r--r--lib/zebra.h6
-rw-r--r--ospf6d/ospf6_lsa.c14
-rw-r--r--ripngd/ripngd.c5
-rw-r--r--tests/test-list_util.c29
-rw-r--r--tests/test-symtab.c23
-rw-r--r--tests/test-vector.c88
-rw-r--r--vtysh/vtysh.c82
-rw-r--r--vtysh/vtysh.h4
-rw-r--r--vtysh/vtysh_config.c43
-rw-r--r--vtysh/vtysh_main.c15
-rw-r--r--vtysh/vtysh_user.c25
-rw-r--r--vtysh/vtysh_user.h3
167 files changed, 33234 insertions, 13434 deletions
diff --git a/bgpd/bgp.h b/bgpd/bgp.h
index 6019b33c..0470c5ae 100644
--- a/bgpd/bgp.h
+++ b/bgpd/bgp.h
@@ -30,7 +30,7 @@
#define _GMCH_BGP_H "19-Dec-2009"
-#include <stdint.h>
+#include "misc.h"
#include "confirm.h"
/*##############################################################################
@@ -815,7 +815,8 @@ typedef U8 BGP_ATT_ORIGIN_T ; /* one byte of data ! */
VALUE(BGP_ATT_ORIGIN_L = sizeof(BGP_ATT_ORIGIN_T)) ;
-enum BGP_ATT_ORG {
+enum BGP_ATT_ORG
+{
BGP_ATT_ORG_MIN = 0,
BGP_ATT_ORG_IGP = 0, /* NLRI is interior to originating AS */
@@ -851,7 +852,8 @@ typedef U32 BGP_ATT_ASPS_AS4_T ;
/* AS Path Segment Types......................................................*/
-enum BGP_AS_SEG {
+enum BGP_AS_SEG
+{
BGP_AS_SET = 1,
BGP_AS_SEQUENCE = 2,
BGP_AS_CONFED_SEQUENCE = 3, /* RFC5065 */
@@ -862,7 +864,8 @@ enum BGP_AS_SEG {
#define AS4(h, l) (((h) << 16) + (l))
-enum BGP_ASN {
+enum BGP_ASN
+{
BGP_ASN_NULL = 0, /* Reserved */
BGP_ASN_RES1_S = 64496, /* Start of Reservation 1 (0xFBF0) */
@@ -962,7 +965,8 @@ enum
BGP_ATT_COM_MS_RES2 = 0xFFFF /* 0xFFFF_0000..0xFFFF_FFFF are reserved */
} ;
-enum BGP_COMM {
+enum BGP_COMM
+{
BGP_ATT_COM_RES1_S = 0x00000000,
BGP_ATT_COM_INTERNET = 0x00000000,
diff --git a/bgpd/bgp_advertise.h b/bgpd/bgp_advertise.h
index ca92238a..afa812f6 100644
--- a/bgpd/bgp_advertise.h
+++ b/bgpd/bgp_advertise.h
@@ -21,9 +21,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#ifndef _QUAGGA_BGP_ADVERTISE_H
#define _QUAGGA_BGP_ADVERTISE_H
-#ifndef Inline
-#define Inline static inline
-#endif
+#include "lib/misc.h"
/* BGP advertise FIFO. */
typedef struct bgp_advertise* bgp_advertise ;
diff --git a/bgpd/bgp_aspath.h b/bgpd/bgp_aspath.h
index 895379aa..c02a84aa 100644
--- a/bgpd/bgp_aspath.h
+++ b/bgpd/bgp_aspath.h
@@ -21,10 +21,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#ifndef _QUAGGA_BGP_ASPATH_H
#define _QUAGGA_BGP_ASPATH_H
-/* Macro in case there are particular compiler issues. */
-#ifndef Inline
- #define Inline static inline
-#endif
+#include "lib/misc.h"
/* AS path segment type. */
#define AS_SET 1
diff --git a/bgpd/bgp_btoa.c b/bgpd/bgp_btoa.c
index 7c708814..42cd0da2 100644
--- a/bgpd/bgp_btoa.c
+++ b/bgpd/bgp_btoa.c
@@ -18,8 +18,6 @@ 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 "zebra.h"
#include "stream.h"
#include "log.h"
@@ -91,11 +89,11 @@ attr_parse (struct stream *s, u_int16_t len)
aspath.str = aspath_make_str_count (&aspath);
printf ("ASPATH: %s\n", aspath.str);
free (aspath.str);
-
+
stream_forward (s, length);
}
break;
- case BGP_ATTR_NEXT_HOP:
+ case BGP_ATTR_NEXT_HOP:
{
struct in_addr nexthop;
nexthop.s_addr = stream_get_ipv4 (s);
@@ -144,7 +142,7 @@ main (int argc, char **argv)
perror ("fopen");
exit (1);
}
-
+
while (1)
{
stream_reset (s);
@@ -279,7 +277,7 @@ main (int argc, char **argv)
sip.s_addr = stream_get_ipv4 (s);
dip.s_addr = stream_get_ipv4 (s);
-
+
printf ("saddr: %s\n", inet_ntoa (sip));
printf ("daddr: %s\n", inet_ntoa (dip));
diff --git a/bgpd/bgp_clist.c b/bgpd/bgp_clist.c
index 1d847ee4..0df34bb0 100644
--- a/bgpd/bgp_clist.c
+++ b/bgpd/bgp_clist.c
@@ -127,7 +127,7 @@ community_list_lookup (struct community_list_handler *ch,
if (!table)
return NULL;
- return symbol_get_value(symbol_seek(table, name)) ;
+ return symbol_get_value(symbol_lookup(table, name, no_add)) ;
}
static struct community_list *
@@ -145,7 +145,7 @@ community_list_get (struct community_list_handler *ch,
if (!table)
return NULL;
- sym = symbol_find(table, name) ;
+ sym = symbol_lookup(table, name, add) ;
list = symbol_get_value(sym) ;
if (!list)
{
@@ -768,10 +768,10 @@ community_list_terminate (struct community_list_handler *ch)
{
struct community_list *list ;
- while ((list = symbol_table_ream_keep(&ch->community_list)))
+ while ((list = symbol_table_ream(&ch->community_list, keep_it)))
community_list_delete(list) ;
- while ((list = symbol_table_ream_keep(&ch->extcommunity_list)))
+ while ((list = symbol_table_ream(&ch->extcommunity_list, keep_it)))
community_list_delete(list) ;
XFREE (MTYPE_COMMUNITY_LIST_HANDLER, ch);
diff --git a/bgpd/bgp_common.c b/bgpd/bgp_common.c
index 8b2b6cea..c1ac7120 100644
--- a/bgpd/bgp_common.c
+++ b/bgpd/bgp_common.c
@@ -18,6 +18,7 @@
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
+#include "misc.h"
#include "bgpd/bgp_common.h"
#include "lib/zassert.h"
diff --git a/bgpd/bgp_common.h b/bgpd/bgp_common.h
index 0d2395ed..fb695125 100644
--- a/bgpd/bgp_common.h
+++ b/bgpd/bgp_common.h
@@ -22,18 +22,13 @@
#ifndef _QUAGGA_BGP_COMMON_H
#define _QUAGGA_BGP_COMMON_H
-#include <stdint.h>
-#include <stdbool.h>
+#include "misc.h"
#include <sys/socket.h>
#include "bgpd/bgp.h"
#include "qafi_safi.h"
#include "lib/zassert.h"
-#ifndef Inline
-#define Inline static inline
-#endif
-
/*==============================================================================
* Here are a number of "incomplete" declarations, which allow a number of
* bgpd structures to refer to each other.
diff --git a/bgpd/bgp_connection.c b/bgpd/bgp_connection.c
index 0bdd4f3d..e2916705 100644
--- a/bgpd/bgp_connection.c
+++ b/bgpd/bgp_connection.c
@@ -19,10 +19,9 @@
* Boston, MA 02111-1307, USA.
*/
-#include "bgpd/bgp_connection.h"
-
-#include <zebra.h>
+#include "misc.h"
+#include "bgpd/bgp_connection.h"
#include "bgpd/bgpd.h"
#include "bgpd/bgp_fsm.h"
#include "bgpd/bgp_engine.h"
@@ -154,7 +153,7 @@ bgp_connection_init_new(bgp_connection connection, bgp_session session,
/* Link back to session, point at its mutex and point session here */
connection->session = session ;
- connection->p_mutex = &session->mutex ;
+ connection->p_mutex = session->mutex ;
connection->lock_count = 0 ; /* no question about it */
connection->paf = AF_UNSPEC ;
@@ -685,7 +684,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_connection.h b/bgpd/bgp_connection.h
index 79270bd8..8c5e00d4 100644
--- a/bgpd/bgp_connection.h
+++ b/bgpd/bgp_connection.h
@@ -22,7 +22,7 @@
#ifndef _QUAGGA_BGP_CONNECTION_H
#define _QUAGGA_BGP_CONNECTION_H
-#include <stdbool.h>
+#include "lib/misc.h"
#include "lib/mqueue.h"
#include "lib/qpthreads.h"
@@ -40,10 +40,6 @@
#include "bgpd/bgp_notification.h"
#include "bgpd/bgp_msg_read.h"
-#ifndef Inline
-#define Inline static inline
-#endif
-
/*==============================================================================
* The BGP Finite State Machine: states and events
*
diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c
index eda146dd..83e6400a 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 AS4 actions\n"
"BGP AS4 aspath segment handling\n")
{
- if (vty_get_node(vty) == CONFIG_NODE)
+ if (vty->node == CONFIG_NODE)
DEBUG_ON (as4, AS4_SEGMENT);
else
{
@@ -429,7 +429,7 @@ DEFUN (no_debug_bgp_as4_segment,
"BGP AS4 actions\n"
"BGP AS4 aspath segment handling\n")
{
- if (vty_get_node(vty) == CONFIG_NODE)
+ if (vty->node == CONFIG_NODE)
DEBUG_OFF (as4, AS4_SEGMENT);
else
{
@@ -454,7 +454,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
{
@@ -472,7 +472,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_dump.c b/bgpd/bgp_dump.c
index d76b5699..7c36825c 100644
--- a/bgpd/bgp_dump.c
+++ b/bgpd/bgp_dump.c
@@ -21,6 +21,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#include <zebra.h>
#include "log.h"
+#include "vty.h"
#include "stream.h"
#include "sockunion.h"
#include "command.h"
@@ -28,6 +29,8 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#include "thread.h"
#include "linklist.h"
#include "bgpd/bgp_table.h"
+#include "qpath.h"
+#include "qstring.h"
#include "bgpd/bgpd.h"
#include "bgpd/bgp_route.h"
@@ -97,8 +100,8 @@ bgp_dump_open_file (struct bgp_dump *bgp_dump)
int ret;
time_t clock;
struct tm tm;
- char fullpath[MAXPATHLEN];
- char realpath[MAXPATHLEN];
+ qpath path ;
+ qstring name ;
mode_t oldumask;
time (&clock);
@@ -106,11 +109,17 @@ bgp_dump_open_file (struct bgp_dump *bgp_dump)
if (bgp_dump->filename[0] != DIRECTORY_SEP)
{
- sprintf (fullpath, "%s/%s", vty_get_cwd (), bgp_dump->filename);
- ret = strftime (realpath, MAXPATHLEN, fullpath, &tm);
+ path = vty_getcwd(NULL) ;
+ qpath_append_str(path, bgp_dump->filename) ;
}
else
- ret = strftime (realpath, MAXPATHLEN, bgp_dump->filename, &tm);
+ path = qpath_set(NULL, bgp_dump->filename) ;
+
+ name = qs_new_size(NULL, qpath_len(path) + 60) ;
+
+ ret = strftime (qs_char_nn(name), qs_len_nn(name), qpath_string(path), &tm);
+
+ qpath_free(path) ;
if (ret == 0)
{
@@ -123,11 +132,12 @@ bgp_dump_open_file (struct bgp_dump *bgp_dump)
oldumask = umask(0777 & ~LOGFILE_MASK);
- bgp_dump->fp = fopen (realpath, "w");
+ bgp_dump->fp = fopen (qs_char_nn(name), "w");
if (bgp_dump->fp == NULL)
{
- zlog_warn("bgp_dump_open_file: %s: %s", realpath, errtoa(errno, 0).str);
+ zlog_warn("bgp_dump_open_file: %s: %s", qs_char_nn(name),
+ errtoa(errno, 0).str);
umask(oldumask);
return NULL;
}
diff --git a/bgpd/bgp_engine.h b/bgpd/bgp_engine.h
index ceec1b2f..821d8127 100644
--- a/bgpd/bgp_engine.h
+++ b/bgpd/bgp_engine.h
@@ -22,23 +22,45 @@
#ifndef _QUAGGA_BGP_ENGINE_H
#define _QUAGGA_BGP_ENGINE_H
+#include "lib/misc.h"
+
#include "bgpd/bgpd.h"
#include "lib/mqueue.h"
#include "lib/qpnexus.h"
#include "lib/log.h"
-#ifndef Inline
-#define Inline static inline
+/*==============================================================================
+ * BGP_ENGINE_DEBUG setting
+ *
+ * Set to 1 if defined, but blank.
+ * Set to QDEBUG if not defined.
+ *
+ * Force to 0 if BGP_ENGINE_NO_DEBUG is defined and not zero.
+ *
+ * So: defaults to same as QDEBUG, but no matter what QDEBUG is set to:
+ *
+ * * can set BGP_ENGINE_DEBUG == 0 to turn off debug
+ * * or set BGP_ENGINE_DEBUG != 0 to turn on debug
+ * * or set BGP_ENGINE_NO_DEBUG != to force debug off
+ */
+#ifdef BGP_ENGINE_DEBUG /* If defined, make it 1 or 0 */
+# if IS_BLANK_OPTION(BGP_ENGINE_DEBUG)
+# undef BGP_ENGINE_DEBUG
+# define BGP_ENGINE_DEBUG 1
+# endif
+#else /* If not defined, follow QDEBUG */
+# define BGP_ENGINE_DEBUG QDEBUG
#endif
-enum { qdebug =
-#ifdef QDEBUG
- 1
-#else
- 0
+#ifdef BGP_ENGINE_NO_DEBUG /* Override, if defined */
+# if IS_NOT_ZERO_OPTION(BGP_ENGINE_NO_DEBUG)
+# undef BGP_ENGINE_DEBUG
+# define BGP_ENGINE_DEBUG 0
+# endif
#endif
-};
+
+enum { bgp_engine_debug = BGP_ENGINE_DEBUG } ;
/*==============================================================================
*
@@ -70,7 +92,7 @@ bgp_queue_logging(const char* name, mqueue_queue mq, struct queue_stats* stats)
++stats->count ;
- qpt_mutex_lock(&mq->mutex) ;
+ qpt_mutex_lock(mq->mutex) ;
if (mq->count > stats->max)
stats->max = mq->count ;
@@ -81,7 +103,7 @@ bgp_queue_logging(const char* name, mqueue_queue mq, struct queue_stats* stats)
if (stats->count < 1000)
{
- qpt_mutex_unlock(&mq->mutex) ;
+ qpt_mutex_unlock(mq->mutex) ;
return ;
} ;
@@ -96,7 +118,7 @@ bgp_queue_logging(const char* name, mqueue_queue mq, struct queue_stats* stats)
assert(my_count == mq->count) ;
- qpt_mutex_unlock(&mq->mutex) ;
+ qpt_mutex_unlock(mq->mutex) ;
average = stats->total * 1000 ;
average = (average / stats->count) + 5 ;
@@ -119,10 +141,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) ;
} ;
@@ -133,10 +155,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..82db783a 100644
--- a/bgpd/bgp_main.c
+++ b/bgpd/bgp_main.c
@@ -18,15 +18,15 @@ 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 <stdbool.h>
+#include "zebra.h"
+#include "misc.h"
#include "vector.h"
#include "vty.h"
#include "command.h"
#include "getopt.h"
#include "thread.h"
-#include <lib/version.h>
+#include "lib/version.h"
#include "memory.h"
#include "prefix.h"
#include "log.h"
@@ -118,13 +118,13 @@ static zebra_capabilities_t _caps_p [] =
struct zebra_privs_t bgpd_privs =
{
#if defined(QUAGGA_USER) && defined(QUAGGA_GROUP)
- .user = QUAGGA_USER,
- .group = QUAGGA_GROUP,
+ .user = QUAGGA_USER,
+ .group = QUAGGA_GROUP,
#endif
#ifdef VTY_GROUP
.vty_group = VTY_GROUP,
#endif
- .caps_p = _caps_p,
+ .caps_p = _caps_p,
.cap_num_p = sizeof(_caps_p)/sizeof(_caps_p[0]),
.cap_num_i = 0,
};
@@ -184,7 +184,7 @@ void sigusr2 (void);
/* prototypes */
static void bgp_exit (int);
-static void init_second_stage(int pthreads);
+static void init_second_stage(bool pthreads);
static void bgp_in_thread_init(void);
static void routing_start(void) ;
static void routing_finish(void) ;
@@ -206,10 +206,6 @@ static struct quagga_signal_t bgp_signals[] =
.handler = &sigusr1,
},
{
- .signal = SIGUSR2,
- .handler = &sigusr2,
- },
- {
.signal = SIGINT,
.handler = &sigint,
},
@@ -240,14 +236,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();
}
@@ -258,17 +257,6 @@ sigusr1 (void)
zlog_rotate (NULL);
}
-/* SIGUSR2 handler. */
-void
-sigusr2 (void)
-{
- /* Used to signal message queues */
- if (qpthreads_enabled)
- return;
- else
- exit(1);
-}
-
/*------------------------------------------------------------------------------
* Final exit code...
*
@@ -301,6 +289,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 +355,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");
@@ -388,8 +377,8 @@ bgp_exit (int status)
*
* 1. if it's there, invoke the command in the usual way
*
- * 2. if it's not there, invoke the command but with a NULL set of arguments,
- * which signals the "default" nature of the call.
+ * 2. if it's not there, invoke the command but with a *negative* count of
+ * arguments, which signals the "default" nature of the call.
*
* This mechanism is used so that the "threaded_cmd" is the time at which
* second stage initialisation is done. (But only once -- not on rereading
@@ -404,8 +393,8 @@ DEFUN_HID_CALL (threaded,
"threaded",
"Use pthreads\n")
{
- if (argv != NULL)
- config_threaded = 1 ; /* Explicit command => turn on threading */
+ if (argc == 0)
+ config_threaded = true ; /* Explicit command => turn on threading */
if (!done_2nd_stage_init)
init_second_stage(config_threaded) ;
@@ -421,7 +410,7 @@ DEFUN_HID_CALL (threaded,
* the message queues are available for the configuration data.
*/
static void
-init_second_stage(int pthreads)
+init_second_stage(bool pthreads)
{
assert(!done_2nd_stage_init) ;
@@ -431,13 +420,13 @@ init_second_stage(int pthreads)
bgp_peer_index_mutex_init();
/* Make nexus for main thread, always needed */
- cli_nexus = qpn_init_new(cli_nexus, 1); /* main thread */
+ cli_nexus = qpn_init_new(cli_nexus, true); /* main thread */
/* if using pthreads create additional nexus */
if (qpthreads_enabled)
{
- bgp_nexus = qpn_init_new(bgp_nexus, 0);
- routing_nexus = qpn_init_new(routing_nexus, 0);
+ bgp_nexus = qpn_init_new(bgp_nexus, false);
+ routing_nexus = qpn_init_new(routing_nexus, false);
}
else
{
@@ -495,9 +484,8 @@ main (int argc, char **argv)
/* Set umask before anything for security */
umask (0027);
-#ifdef QDEBUG
- fprintf(stderr, "%s\n", debug_banner);
-#endif
+ if (qdebug)
+ fprintf(stderr, "%s\n", debug_banner);
qlib_init_first_stage();
@@ -599,7 +587,11 @@ main (int argc, char **argv)
/* Initializations. */
srand (time (NULL));
signal_init (master, Q_SIGC(bgp_signals), bgp_signals);
- zprivs_init (&bgpd_privs);
+
+ cmd_getcwd() ; /* while have privilege */
+
+ zprivs_init (&bgpd_privs); /* lowers privileges */
+
cmd_init (1);
install_element (CONFIG_NODE, &threaded_cmd);
vty_init (master);
@@ -648,9 +640,9 @@ main (int argc, char **argv)
vty_start(vty_addr, vty_port, BGP_VTYSH_PATH);
/* Print banner. */
-#ifdef QDEBUG
- zlog_notice("%s", debug_banner);
-#endif
+ if (qdebug)
+ zlog_notice("%s", debug_banner);
+
zlog_notice ("BGPd %s%s starting: vty@%d, bgp@%s:%d",
QUAGGA_VERSION,
(qpthreads_enabled ? " pthreaded" : ""),
@@ -744,16 +736,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 +800,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_msg_write.h b/bgpd/bgp_msg_write.h
index 77bfc1f2..8867e06f 100644
--- a/bgpd/bgp_msg_write.h
+++ b/bgpd/bgp_msg_write.h
@@ -24,8 +24,7 @@
#ifndef _QUAGGA_BGP_MSG_WRITE_H
#define _QUAGGA_BGP_MSG_WRITE_H
-#include <stdint.h>
-#include <stdbool.h>
+#include "misc.h"
#include "bgpd/bgp_common.h"
#include "bgpd/bgp_connection.h"
diff --git a/bgpd/bgp_notification.c b/bgpd/bgp_notification.c
index c0690df9..64228ddb 100644
--- a/bgpd/bgp_notification.c
+++ b/bgpd/bgp_notification.c
@@ -20,8 +20,8 @@
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
+#include "misc.h"
-#include <string.h>
#include <netinet/in.h>
#include "lib/zassert.h"
diff --git a/bgpd/bgp_notification.h b/bgpd/bgp_notification.h
index 20e96063..755f431e 100644
--- a/bgpd/bgp_notification.h
+++ b/bgpd/bgp_notification.h
@@ -24,13 +24,9 @@
#ifndef _QUAGGA_BGP_NOTIFY_H
#define _QUAGGA_BGP_NOTIFY_H
-#include <stddef.h>
+#include "lib/misc.h"
#include "bgpd/bgp_common.h"
-#ifndef Inline
-#define Inline static inline
-#endif
-
/*==============================================================================
* BGP NOTIFICATION message codes.
*/
diff --git a/bgpd/bgp_open_state.c b/bgpd/bgp_open_state.c
index 9ca9617e..e1dbaeaf 100644
--- a/bgpd/bgp_open_state.c
+++ b/bgpd/bgp_open_state.c
@@ -48,7 +48,7 @@ bgp_open_state_init_new(bgp_open_state state)
else
memset(state, 0, sizeof(struct bgp_open_state)) ;
- vector_init_new(&state->unknowns, 0) ;
+ vector_init_new(state->unknowns, 0) ;
return state ;
}
@@ -66,10 +66,10 @@ bgp_open_state_free(bgp_open_state state)
if (state != NULL)
{
- while ((unknown = vector_ream_keep(&state->unknowns)) != NULL)
+ while ((unknown = vector_ream(state->unknowns, keep_it)) != NULL)
XFREE(MTYPE_TMP, unknown) ;
- while ((afi_safi = vector_ream_keep(&state->afi_safi)) != NULL)
+ while ((afi_safi = vector_ream(state->afi_safi, keep_it)) != NULL)
XFREE(MTYPE_TMP, afi_safi) ;
XFREE(MTYPE_BGP_OPEN_STATE, state) ;
@@ -222,7 +222,7 @@ bgp_open_state_unknown_add(bgp_open_state state, uint8_t code,
if (length != 0)
memcpy(unknown->value, value, length) ;
- vector_push_item(&state->unknowns, unknown) ;
+ vector_push_item(state->unknowns, unknown) ;
} ;
/*------------------------------------------------------------------------------
@@ -231,7 +231,7 @@ bgp_open_state_unknown_add(bgp_open_state state, uint8_t code,
extern int
bgp_open_state_unknown_count(bgp_open_state state)
{
- return vector_end(&state->unknowns) ;
+ return vector_end(state->unknowns) ;
} ;
/*------------------------------------------------------------------------------
@@ -240,7 +240,7 @@ bgp_open_state_unknown_count(bgp_open_state state)
extern bgp_cap_unknown
bgp_open_state_unknown_cap(bgp_open_state state, unsigned index)
{
- return vector_get_item(&state->unknowns, index) ;
+ return vector_get_item(state->unknowns, index) ;
} ;
/*==============================================================================
@@ -264,7 +264,7 @@ bgp_open_state_afi_safi_add(bgp_open_state state, iAFI_t afi, iSAFI_t safi,
afi_safi->safi = safi ;
afi_safi->cap_code = cap_code ;
- vector_push_item(&state->afi_safi, afi_safi) ;
+ vector_push_item(state->afi_safi, afi_safi) ;
return afi_safi ;
} ;
@@ -275,7 +275,7 @@ bgp_open_state_afi_safi_add(bgp_open_state state, iAFI_t afi, iSAFI_t safi,
extern int
bgp_open_state_afi_safi_count(bgp_open_state state)
{
- return vector_end(&state->afi_safi) ;
+ return vector_end(state->afi_safi) ;
} ;
/*------------------------------------------------------------------------------
@@ -284,7 +284,7 @@ bgp_open_state_afi_safi_count(bgp_open_state state)
extern bgp_cap_afi_safi
bgp_open_state_afi_safi_cap(bgp_open_state state, unsigned index)
{
- return vector_get_item(&state->afi_safi, index) ;
+ return vector_get_item(state->afi_safi, index) ;
} ;
/*==============================================================================
diff --git a/bgpd/bgp_open_state.h b/bgpd/bgp_open_state.h
index 8c30712b..e88deda0 100644
--- a/bgpd/bgp_open_state.h
+++ b/bgpd/bgp_open_state.h
@@ -22,16 +22,12 @@
#ifndef _QUAGGA_BGP_OPEN_STATE_H
#define _QUAGGA_BGP_OPEN_STATE_H
-#include <stdint.h>
+#include "misc.h"
#include "bgpd/bgp.h"
#include "bgpd/bgp_common.h"
#include "lib/vector.h"
-#ifndef Inline
-#define Inline static inline
-#endif
-
/*==============================================================================
* BGP Open State.
*
@@ -114,8 +110,8 @@ struct bgp_open_state
bool has_restarted ; /* Restart State flag */
unsigned restart_time ; /* Restart Time in seconds */
- struct vector unknowns ; /* list of bgp_cap_unknown */
- struct vector afi_safi ; /* various afi/safi capabilities */
+ vector_t unknowns ; /* list of bgp_cap_unknown */
+ vector_t afi_safi ; /* various afi/safi capabilities */
} ;
/*==============================================================================
diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c
index 2cad6ab2..ec2c0ade 100644
--- a/bgpd/bgp_packet.c
+++ b/bgpd/bgp_packet.c
@@ -777,7 +777,7 @@ bgp_route_refresh_send (struct peer *peer, afi_t afi, safi_t safi,
bgp_orf_entry orfpe = NULL;
struct prefix_list *plist = NULL;
struct orf_prefix orfp;
- vector_index i;
+ vector_index_t i;
int orf_refresh = 0;
enum prefix_list_type pe_type;
@@ -1832,7 +1832,7 @@ bgp_route_refresh_recv(bgp_peer peer, bgp_route_refresh rr)
{
afi_t afi;
safi_t safi;
- vector_index i;
+ vector_index_t i, e;
char name[BUFSIZ];
int ret;
@@ -1847,11 +1847,11 @@ bgp_route_refresh_recv(bgp_peer peer, bgp_route_refresh rr)
ret = snprintf (name, BUFSIZ, "%s.%d.%d", peer->host, afi, safi);
assert(ret < BUFSIZ);
- if (rr->entries.end > 0)
+ if ((e = bgp_orf_get_count(rr)) > 0)
{
- for (i = 0; i < rr->entries.end; ++i)
+ for (i = 0; i < e; ++i)
{
- bgp_orf_entry orfep = vector_slot(&rr->entries, i);
+ bgp_orf_entry orfep = vector_slot(rr->entries, i);
/* ignore unknown */
if (orfep->unknown)
diff --git a/bgpd/bgp_peer.c b/bgpd/bgp_peer.c
index 787420f4..8af38876 100644
--- a/bgpd/bgp_peer.c
+++ b/bgpd/bgp_peer.c
@@ -464,7 +464,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_peer_index.c b/bgpd/bgp_peer_index.c
index 518a22bc..d3c90598 100644
--- a/bgpd/bgp_peer_index.c
+++ b/bgpd/bgp_peer_index.c
@@ -18,6 +18,7 @@
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
+#include "misc.h"
#include "lib/zassert.h"
@@ -59,7 +60,7 @@
*/
static struct symbol_table bgp_peer_index ; /* lookup by 'name' */
-static struct vector bgp_peer_id_index ; /* lookup by peer-id */
+static vector_t bgp_peer_id_index ; /* lookup by peer-id */
static qpt_mutex bgp_peer_index_mutex = NULL ;
@@ -112,7 +113,7 @@ bgp_peer_index_init(void* parent)
sockunion_symbol_hash, /* "name" is an IP Address */
NULL) ; /* no value change call-back */
- vector_init_new(&bgp_peer_id_index, bgp_peer_id_unit) ;
+ vector_init_new(bgp_peer_id_index, bgp_peer_id_unit) ;
/* Initialise table entirely empty */
bgp_peer_id_table = NULL ;
@@ -149,11 +150,11 @@ bgp_peer_index_reset(void)
bgp_peer_id_table_chunk chunk ;
/* Ream out the peer id vector -- checking that all entries are empty */
- while ((entry = vector_ream_keep(&bgp_peer_id_index)) != NULL)
+ while ((entry = vector_ream(bgp_peer_id_index, keep_it)) != NULL)
passert((entry->peer == NULL) && (entry->next_free != entry)) ;
/* Discard body of symbol table -- must be empty ! */
- symbol_table_reset_keep(&bgp_peer_index) ;
+ symbol_table_reset(&bgp_peer_index, keep_it) ;
/* Discard the empty chunks of entries */
while (bgp_peer_id_table != NULL)
@@ -204,7 +205,7 @@ bgp_peer_index_register(bgp_peer peer, union sockunion* su)
entry = bgp_peer_id_free_head ;
bgp_peer_id_free_head = entry->next_free ;
- assert(vector_get_item(&bgp_peer_id_index, entry->id) == entry) ;
+ assert(vector_get_item(bgp_peer_id_index, entry->id) == entry) ;
/* Initialise the entry -- the id is already set */
entry->peer = peer ;
@@ -213,7 +214,7 @@ bgp_peer_index_register(bgp_peer peer, union sockunion* su)
peer->index_entry = entry;
/* Insert the new entry into the symbol table. */
- entry = symbol_set_value(symbol_find(&bgp_peer_index, su), entry) ;
+ entry = symbol_set_value(symbol_lookup(&bgp_peer_index, su, add), entry) ;
BGP_PEER_INDEX_UNLOCK() ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
@@ -241,7 +242,7 @@ bgp_peer_index_deregister(bgp_peer peer, union sockunion* su)
BGP_PEER_INDEX_LOCK() ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
- sym = symbol_seek(&bgp_peer_index, su) ;
+ sym = symbol_lookup(&bgp_peer_index, su, no_add) ;
passert(sym != NULL) ;
entry = symbol_delete(sym) ;
@@ -288,7 +289,7 @@ bgp_peer_index_seek_entry(union sockunion* su)
/* Only the Routing Engine can add/delete entries -- so no lock required */
- entry = symbol_get_value(symbol_seek(&bgp_peer_index, su)) ;
+ entry = symbol_get_value(symbol_lookup(&bgp_peer_index, su, no_add)) ;
if (entry != NULL)
assert((entry->peer != NULL) && (entry->next_free = entry)) ;
@@ -340,7 +341,7 @@ bgp_peer_index_seek_accept(union sockunion* su, bool* p_found)
BGP_PEER_INDEX_LOCK() ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
- entry = symbol_get_value(symbol_seek(&bgp_peer_index, su)) ;
+ entry = symbol_get_value(symbol_lookup(&bgp_peer_index, su, no_add)) ;
if (entry != NULL)
{
@@ -375,7 +376,7 @@ static void
bgp_peer_id_table_free_entry(bgp_peer_index_entry entry)
{
assert((entry != NULL) && (entry->id < bgp_peer_id_count)) ;
- assert(vector_get_item(&bgp_peer_id_index, entry->id) == entry) ;
+ assert(vector_get_item(bgp_peer_id_index, entry->id) == entry) ;
if (bgp_peer_id_free_head == NULL)
bgp_peer_id_free_head = entry ;
@@ -424,7 +425,7 @@ bgp_peer_id_table_make_ids(void)
while (id_new < bgp_peer_id_count)
{
- vector_set_item(&bgp_peer_id_index, id_new, entry) ;
+ vector_set_item(bgp_peer_id_index, id_new, entry) ;
entry->id = id_new ;
bgp_peer_id_table_free_entry(entry) ;
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index 89bdef77..b4f57419 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -5039,7 +5039,7 @@ ALIAS_DEPRECATED (bgp_network_mask_natural,
ALIAS_DEPRECATED (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"
@@ -11550,7 +11550,7 @@ DEFUN (show_ip_bgp_view_rsclient_prefix,
if (argc == 3)
peer = peer_lookup_in_view (vty, argv[0], argv[1]);
else
- peer = peer_lookup_in_view (vty, NULL, argv[0]);
+ peer = peer_lookup_in_view (vty, NULL, argv[0]);
if (! peer)
return CMD_WARNING;
@@ -11639,11 +11639,11 @@ DEFUN (show_bgp_view_ipv4_safi_rsclient_prefix,
vty_out (vty, "%% Activate the neighbor for the address family first%s",
VTY_NEWLINE);
return CMD_WARNING;
-}
+ }
if ( ! CHECK_FLAG (peer->af_flags[AFI_IP][safi],
PEER_FLAG_RSERVER_CLIENT))
-{
+ {
vty_out (vty, "%% Neighbor is not a Route-Server client%s",
VTY_NEWLINE);
return CMD_WARNING;
@@ -13463,8 +13463,10 @@ bgp_route_init (void)
*/
install_element (RESTRICTED_NODE, &show_bgp_route_cmd);
install_element (RESTRICTED_NODE, &show_bgp_ipv6_route_cmd);
+ install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_route_cmd);
install_element (RESTRICTED_NODE, &show_bgp_prefix_cmd);
install_element (RESTRICTED_NODE, &show_bgp_ipv6_prefix_cmd);
+ install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_prefix_cmd);
install_element (RESTRICTED_NODE, &show_bgp_community_cmd);
install_element (RESTRICTED_NODE, &show_bgp_ipv6_community_cmd);
install_element (RESTRICTED_NODE, &show_bgp_community2_cmd);
diff --git a/bgpd/bgp_route_refresh.c b/bgpd/bgp_route_refresh.c
index 5c2b6e5c..252183e4 100644
--- a/bgpd/bgp_route_refresh.c
+++ b/bgpd/bgp_route_refresh.c
@@ -18,6 +18,7 @@
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
+#include "misc.h"
#include <string.h>
@@ -60,7 +61,7 @@ bgp_route_refresh_new(iAFI_t afi, iSAFI_t safi, unsigned count)
rr->afi = afi ;
rr->safi = safi ;
- vector_init_new(&rr->entries, count) ;
+ vector_init_new(rr->entries, count) ;
/* rest of bgp_route_refresh zeroised -- not relevant when vector empty */
@@ -74,7 +75,7 @@ extern void
bgp_route_refresh_free(bgp_route_refresh rr)
{
bgp_orf_entry entry ;
- while((entry = vector_ream_keep(&rr->entries)) != NULL)
+ while((entry = vector_ream(rr->entries, keep_it)) != NULL)
XFREE(MTYPE_BGP_ORF_ENTRY, entry) ;
XFREE(MTYPE_BGP_ROUTE_REFRESH, rr) ;
@@ -130,7 +131,7 @@ bgp_orf_entry_new(bgp_route_refresh rr, uint8_t orf_type, bgp_form_t form,
orfe->form = form ;
orfe->unknown = (unknown_size != 0) ;
- vector_push_item(&rr->entries, orfe) ;
+ vector_push_item(rr->entries, orfe) ;
return orfe ;
} ;
diff --git a/bgpd/bgp_route_refresh.h b/bgpd/bgp_route_refresh.h
index b44ca9e0..78324ff4 100644
--- a/bgpd/bgp_route_refresh.h
+++ b/bgpd/bgp_route_refresh.h
@@ -22,16 +22,12 @@
#ifndef _QUAGGA_BGP_ROUTE_REFRESH_H
#define _QUAGGA_BGP_ROUTE_REFRESH_H
-#include <stddef.h>
+#include "lib/misc.h"
#include "bgpd/bgp_common.h"
#include "lib/prefix.h"
#include "lib/plist.h"
-#ifndef Inline
-#define Inline static inline
-#endif
-
/*==============================================================================
* Structures to hold ROUTE-REFRESH and ORF
*/
@@ -81,7 +77,7 @@ struct bgp_route_refresh
iAFI_t afi ; /* NB: Internet AFI/SAFI */
iSAFI_t safi ;
- struct vector entries ; /* empty => simple ROUTE-REFRESH */
+ vector_t entries ; /* empty => simple ROUTE-REFRESH */
bool defer ; /* otherwise: immediate */
@@ -120,13 +116,13 @@ bgp_orf_add_unknown(bgp_route_refresh rr, uint8_t orf_type, bgp_size_t length,
Inline unsigned
bgp_orf_get_count(bgp_route_refresh rr)
{
- return vector_end(&rr->entries) ;
+ return vector_end(rr->entries) ;
} ;
Inline bgp_orf_entry
bgp_orf_get_entry(bgp_route_refresh rr, unsigned index)
{
- return vector_get_item(&rr->entries, index) ;
+ return vector_get_item(rr->entries, index) ;
} ;
#endif /* _QUAGGA_BGP_ROUTE_REFRESH_H */
diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c
index e8b9ad60..042620cf 100644
--- a/bgpd/bgp_routemap.c
+++ b/bgpd/bgp_routemap.c
@@ -1460,6 +1460,13 @@ route_set_community_delete (void *rule, struct prefix *prefix,
new = community_uniq_sort (merge);
community_free (merge);
+ /* HACK: if the old community is not intern'd,
+ * we should free it here, or all references to it may be lost.
+ * Really need to clean up attribute caching sometime.
+ */
+ if (old->refcnt == 0)
+ community_free(old) ;
+
if (new->size == 0)
{
binfo->attr->community = NULL;
@@ -2973,7 +2980,7 @@ DEFUN (set_metric,
ALIAS (set_metric,
set_metric_addsub_cmd,
- "set metric <+/-metric>",
+ "set metric <-2147483647-+2147483647>",
SET_STR
"Metric value for destination routing protocol\n"
"Add or subtract metric\n")
@@ -3063,7 +3070,7 @@ ALIAS (no_set_weight,
DEFUN (set_aspath_prepend,
set_aspath_prepend_cmd,
- "set as-path prepend ." CMD_AS_RANGE,
+ "set as-path prepend .ASNs",
SET_STR
"Transform BGP AS_PATH attribute\n"
"Prepend to the as-path\n"
@@ -3101,7 +3108,7 @@ 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 .ASns",
NO_STR
SET_STR
"Transform BGP AS_PATH attribute\n"
@@ -3110,7 +3117,7 @@ ALIAS (no_set_aspath_prepend,
DEFUN (set_aspath_exclude,
set_aspath_exclude_cmd,
- "set as-path exclude ." CMD_AS_RANGE,
+ "set as-path exclude .ASNs",
SET_STR
"Transform BGP AS-path attribute\n"
"Exclude from the as-path\n"
@@ -3147,7 +3154,7 @@ 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 .ASNs",
NO_STR
SET_STR
"Transform BGP AS_PATH attribute\n"
@@ -3159,7 +3166,8 @@ DEFUN (set_community,
"set community .AA:NN",
SET_STR
"BGP community attribute\n"
- "Community number in aa:nn format or local-AS|no-advertise|no-export|internet or additive\n")
+ "Community number in aa:nn format or """
+ "local-AS|no-advertise|no-export|internet or additive\n")
{
int i;
int first = 0;
diff --git a/bgpd/bgp_session.c b/bgpd/bgp_session.c
index a734ba99..d09fe217 100644
--- a/bgpd/bgp_session.c
+++ b/bgpd/bgp_session.c
@@ -18,6 +18,7 @@
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
+#include "misc.h"
#include "bgpd/bgp_session.h"
#include "bgpd/bgp_common.h"
@@ -124,7 +125,7 @@ bgp_session_init_new(bgp_peer peer)
session = XCALLOC(MTYPE_BGP_SESSION, sizeof(struct bgp_session)) ;
- qpt_mutex_init_new(&session->mutex, qpt_mutex_recursive) ;
+ qpt_mutex_init_new(session->mutex, qpt_mutex_recursive) ;
session->peer = peer ;
bgp_peer_lock(peer) ; /* Account for the session->peer pointer */
@@ -240,7 +241,7 @@ bgp_session_delete(bgp_peer peer)
BGP_SESSION_UNLOCK(session) ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
- qpt_mutex_destroy(&session->mutex, 0) ;
+ qpt_mutex_destroy(session->mutex, 0) ;
/* Proceed to dismantle the session. */
@@ -461,7 +462,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 +518,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_session.h b/bgpd/bgp_session.h
index ccc7a28e..cc8acc19 100644
--- a/bgpd/bgp_session.h
+++ b/bgpd/bgp_session.h
@@ -22,8 +22,8 @@
#ifndef _QUAGGA_BGP_SESSION_H
#define _QUAGGA_BGP_SESSION_H
-#include <stdbool.h>
#include <zebra.h>
+#include "lib/misc.h"
#include "bgpd/bgp_common.h"
#include "bgpd/bgp_engine.h"
@@ -37,10 +37,6 @@
#include "lib/sockunion.h"
#include "lib/mqueue.h"
-#ifndef Inline
-#define Inline static inline
-#endif
-
/*==============================================================================
* BGP Session data structure.
*
@@ -308,12 +304,12 @@ MQB_ARGS_SIZE_OK(bgp_session_ttl_args) ;
inline static void BGP_SESSION_LOCK(bgp_session session)
{
- qpt_mutex_lock(&session->mutex) ;
+ qpt_mutex_lock(session->mutex) ;
} ;
inline static void BGP_SESSION_UNLOCK(bgp_session session)
{
- qpt_mutex_unlock(&session->mutex) ;
+ qpt_mutex_unlock(session->mutex) ;
} ;
/*==============================================================================
diff --git a/bgpd/bgp_snmp.c b/bgpd/bgp_snmp.c
index eaa41e6c..3f56fb25 100644
--- a/bgpd/bgp_snmp.c
+++ b/bgpd/bgp_snmp.c
@@ -44,13 +44,13 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#include "bgpd/bgp_route.h"
#include "bgpd/bgp_fsm.h"
#include "bgpd/bgp_snmp.h"
-
+
/* BGP4-MIB described in RFC1657. */
#define BGP4MIB 1,3,6,1,2,1,15
/* BGP TRAP. */
#define BGPESTABLISHED 1
-#define BGPBACKWARDTRANSITION 2
+#define BGPBACKWARDTRANSITION 2
/* BGP MIB bgpVersion. */
#define BGPVERSION 0
@@ -118,7 +118,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#define OCTET_STRING ASN_OCTET_STR
#define IPADDRESS ASN_IPADDRESS
#define GAUGE32 ASN_UNSIGNED
-
+
/* Declare static local variables for convenience. */
SNMP_LOCAL_VARIABLES
@@ -143,7 +143,7 @@ static u_char *bgp4PathAttrTable (struct variable *, oid [], size_t *,
int, size_t *, WriteMethod **);
/* static u_char *bgpTraps (); */
-struct variable bgp_variables[] =
+struct variable bgp_variables[] =
{
/* BGP version. */
{BGPVERSION, OCTET_STRING, RONLY, bgpVersion,
@@ -247,7 +247,7 @@ struct variable bgp_variables[] =
3, {6, 1, 14}},
};
-
+
static u_char *
bgpVersion (struct variable *v, oid name[], size_t *length, int exact,
size_t *var_len, WriteMethod **write_method)
@@ -343,7 +343,7 @@ bgp_peer_lookup_next (struct in_addr *src)
}
static struct peer *
-bgpPeerTable_lookup (struct variable *v, oid name[], size_t *length,
+bgpPeerTable_lookup (struct variable *v, oid name[], size_t *length,
struct in_addr *addr, int exact)
{
struct peer *peer = NULL;
@@ -364,9 +364,9 @@ bgpPeerTable_lookup (struct variable *v, oid name[], size_t *length,
{
len = *length - v->namelen;
if (len > 4) len = 4;
-
+
oid2in_addr (name + v->namelen, len, addr);
-
+
peer = bgp_peer_lookup_next (addr);
if (peer == NULL)
@@ -391,12 +391,12 @@ write_bgpPeerTable (int action, u_char *var_val,
struct peer *peer;
long intval;
size_t bigsize = SNMP_MAX_LEN;
-
- if (var_val_type != ASN_INTEGER)
+
+ if (var_val_type != ASN_INTEGER)
{
return SNMP_ERR_WRONGTYPE;
}
- if (var_val_len != sizeof (long))
+ if (var_val_len != sizeof (long))
{
return SNMP_ERR_WRONGLENGTH;
}
@@ -597,7 +597,7 @@ bgpPeerTable (struct variable *v, oid name[], size_t *length,
default:
return NULL;
break;
- }
+ }
return NULL;
}
@@ -664,7 +664,7 @@ bgp4PathAttrLookup (struct variable *v, oid name[], size_t *length,
oid2in_addr (offset, IN_ADDR_SIZE, &su.sin.sin_addr);
/* Lookup node. */
- rn = bgp_node_lookup (bgp->rib[AFI_IP][SAFI_UNICAST],
+ rn = bgp_node_lookup (bgp->rib[AFI_IP][SAFI_UNICAST],
(struct prefix *) addr);
if (rn)
{
@@ -687,7 +687,7 @@ bgp4PathAttrLookup (struct variable *v, oid name[], size_t *length,
{
if (len > IN_ADDR_SIZE)
len = IN_ADDR_SIZE;
-
+
oid2in_addr (offset, len, &addr->prefix);
offset += IN_ADDR_SIZE;
@@ -726,12 +726,12 @@ bgp4PathAttrLookup (struct variable *v, oid name[], size_t *length,
for (binfo = rn->info; binfo; binfo = binfo->info_next)
{
if (binfo->peer->su.sin.sin_family == AF_INET
- && ntohl (paddr.s_addr)
+ && ntohl (paddr.s_addr)
< ntohl (binfo->peer->su.sin.sin_addr.s_addr))
{
if (min)
{
- if (ntohl (binfo->peer->su.sin.sin_addr.s_addr)
+ if (ntohl (binfo->peer->su.sin.sin_addr.s_addr)
< ntohl (min->peer->su.sin.sin_addr.s_addr))
min = binfo;
}
@@ -749,7 +749,7 @@ bgp4PathAttrLookup (struct variable *v, oid name[], size_t *length,
offset += IN_ADDR_SIZE;
*offset = rn->p.prefixlen;
offset++;
- oid_copy_addr (offset, &min->peer->su.sin.sin_addr,
+ oid_copy_addr (offset, &min->peer->su.sin.sin_addr,
IN_ADDR_SIZE);
addr->prefix = rn->p.u.prefix4;
addr->prefixlen = rn->p.prefixlen;
@@ -773,7 +773,7 @@ bgp4PathAttrTable (struct variable *v, oid name[], size_t *length,
struct bgp *bgp;
struct bgp_info *binfo;
struct prefix_ipv4 addr;
-
+
bgp = bgp_get_default ();
if (! bgp)
return NULL;
@@ -843,7 +843,7 @@ bgp4PathAttrTable (struct variable *v, oid name[], size_t *length,
}
return NULL;
}
-
+
/* BGP Traps. */
struct trap_object bgpTrapList[] =
{
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index 735bf639..ed7e0e03 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;
}
@@ -318,12 +314,13 @@ DEFUN_DEPRECATED (neighbor_version,
}
/* "router bgp" commands. */
-DEFUN (router_bgp,
- router_bgp_cmd,
- "router bgp " CMD_AS_RANGE,
- ROUTER_STR
- BGP_STR
- AS_STR)
+DEFUN_ATTR (router_bgp,
+ router_bgp_cmd,
+ "router bgp " CMD_AS_RANGE,
+ ROUTER_STR
+ BGP_STR
+ AS_STR,
+ CMD_ATTR_NODE + BGP_NODE)
{
int ret;
as_t as;
@@ -342,9 +339,11 @@ DEFUN (router_bgp,
vty_out (vty, "Please specify 'bgp multiple-instance' first%s",
VTY_NEWLINE);
return CMD_WARNING;
+
case BGP_ERR_AS_MISMATCH:
vty_out (vty, "BGP is already running; AS is %u%s", as, VTY_NEWLINE);
return CMD_WARNING;
+
case BGP_ERR_INSTANCE_MISMATCH:
vty_out (vty, "BGP view name and AS number mismatch%s", VTY_NEWLINE);
vty_out (vty, "BGP instance is already running; AS is %u%s",
@@ -352,20 +351,21 @@ DEFUN (router_bgp,
return CMD_WARNING;
}
- vty_set_node(vty, BGP_NODE) ;
+ vty->node = BGP_NODE ;
vty->index = bgp;
return CMD_SUCCESS;
}
-ALIAS (router_bgp,
- router_bgp_view_cmd,
- "router bgp " CMD_AS_RANGE " view WORD",
- ROUTER_STR
- BGP_STR
- AS_STR
- "BGP view\n"
- "view name\n")
+ALIAS_ATTR (router_bgp,
+ router_bgp_view_cmd,
+ "router bgp " CMD_AS_RANGE " view WORD",
+ ROUTER_STR
+ BGP_STR
+ AS_STR
+ "BGP view\n"
+ "view name\n",
+ CMD_ATTR_NODE + BGP_NODE)
/* "no router bgp" commands. */
DEFUN (no_router_bgp,
@@ -601,7 +601,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 +631,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"
@@ -4012,90 +4012,118 @@ DEFUN (no_neighbor_allowas_in,
}
/* Address family configuration. */
-DEFUN (address_family_ipv4,
- address_family_ipv4_cmd,
- "address-family ipv4",
- "Enter Address Family command mode\n"
- "Address family\n")
-{
- vty_set_node(vty, BGP_IPV4_NODE) ;
+DEFUN_ATTR (address_family_ipv4,
+ address_family_ipv4_cmd,
+ "address-family ipv4",
+ "Enter Address Family command mode\n"
+ "Address family\n",
+ CMD_ATTR_NODE + BGP_IPV4_NODE)
+{
+ vty->node = BGP_IPV4_NODE ;
return CMD_SUCCESS;
}
-DEFUN (address_family_ipv4_safi,
- address_family_ipv4_safi_cmd,
- "address-family ipv4 (unicast|multicast)",
- "Enter Address Family command mode\n"
- "Address family\n"
- "Address Family modifier\n"
- "Address Family modifier\n")
+DEFUN_ATTR (address_family_ipv4_safi_unicast,
+ address_family_ipv4_safi_unicast_cmd,
+ "address-family ipv4 unicast",
+ "Enter Address Family command mode\n"
+ "Address family\n"
+ "Address Family modifier\n",
+ CMD_ATTR_NODE + BGP_IPV4_NODE)
{
- if (strncmp (argv[0], "m", 1) == 0)
- vty_set_node(vty, BGP_IPV4M_NODE) ;
- else
- vty_set_node(vty, BGP_IPV4_NODE) ;
+ vty->node = BGP_IPV4_NODE ;
+ return CMD_SUCCESS;
+}
+DEFUN_ATTR (address_family_ipv4_safi_multicast,
+ address_family_ipv4_safi_multicast_cmd,
+ "address-family ipv4 multicast",
+ "Enter Address Family command mode\n"
+ "Address family\n"
+ "Address Family modifier\n",
+ CMD_ATTR_NODE + BGP_IPV4M_NODE)
+{
+ vty->node = BGP_IPV4M_NODE ;
return CMD_SUCCESS;
}
-DEFUN (address_family_ipv6,
- address_family_ipv6_cmd,
- "address-family ipv6",
- "Enter Address Family command mode\n"
- "Address family\n")
+DEFUN_ATTR (address_family_ipv6,
+ address_family_ipv6_cmd,
+ "address-family ipv6",
+ "Enter Address Family command mode\n"
+ "Address family\n",
+ CMD_ATTR_NODE + BGP_IPV6_NODE)
{
- vty_set_node(vty, BGP_IPV6_NODE) ;
+ vty->node = BGP_IPV6_NODE ;
return CMD_SUCCESS;
}
-DEFUN (address_family_ipv6_safi,
- address_family_ipv6_safi_cmd,
- "address-family ipv6 (unicast|multicast)",
- "Enter Address Family command mode\n"
- "Address family\n"
- "Address Family modifier\n"
- "Address Family modifier\n")
+DEFUN_ATTR (address_family_ipv6_safi_unicast,
+ address_family_ipv6_safi_unicast_cmd,
+ "address-family ipv6 unicast",
+ "Enter Address Family command mode\n"
+ "Address family\n"
+ "Address Family modifier\n",
+ CMD_ATTR_NODE + BGP_IPV6_NODE)
{
- if (strncmp (argv[0], "m", 1) == 0)
- vty_set_node(vty, BGP_IPV6M_NODE) ;
- else
- vty_set_node(vty, BGP_IPV6_NODE) ;
+ vty->node = BGP_IPV6_NODE ;
+ return CMD_SUCCESS;
+}
+DEFUN_ATTR (address_family_ipv6_safi_multicast,
+ address_family_ipv6_safi_multicast_cmd,
+ "address-family ipv6 multicast",
+ "Enter Address Family command mode\n"
+ "Address family\n"
+ "Address Family modifier\n",
+ CMD_ATTR_NODE + BGP_IPV6M_NODE)
+{
+ vty->node = BGP_IPV6M_NODE ;
return CMD_SUCCESS;
}
-DEFUN (address_family_vpnv4,
- address_family_vpnv4_cmd,
- "address-family vpnv4",
- "Enter Address Family command mode\n"
- "Address family\n")
+DEFUN_ATTR (address_family_vpnv4,
+ address_family_vpnv4_cmd,
+ "address-family vpnv4",
+ "Enter Address Family command mode\n"
+ "Address family\n",
+ CMD_ATTR_NODE + BGP_VPNV4_NODE)
{
- vty_set_node(vty, BGP_VPNV4_NODE) ;
+ vty->node = BGP_VPNV4_NODE ;
return CMD_SUCCESS;
}
-ALIAS (address_family_vpnv4,
+ALIAS_ATTR (address_family_vpnv4,
address_family_vpnv4_unicast_cmd,
"address-family vpnv4 unicast",
"Enter Address Family command mode\n"
"Address family\n"
- "Address Family Modifier\n")
+ "Address Family Modifier\n",
+ CMD_ATTR_NODE + BGP_VPNV4_NODE)
-DEFUN (exit_address_family,
- exit_address_family_cmd,
- "exit-address-family",
- "Exit from Address Family configuration mode\n")
+DEFUN_ATTR (exit_address_family,
+ exit_address_family_cmd,
+ "exit-address-family",
+ "Exit from Address Family configuration mode\n",
+ CMD_ATTR_NODE + BGP_NODE)
{
- enum node_type node = vty_get_node(vty) ;
+ node_type_t node = vty->node ;
- if (node == BGP_IPV4_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);
- return CMD_SUCCESS;
-}
+ {
+ vty->node = BGP_NODE ;
+ return CMD_SUCCESS ;
+ }
+ else
+ {
+ vty_out(vty, "%% No address family to leave\n") ;
+ return CMD_WARNING ;
+ } ;
+} ;
/* BGP clear sort. */
enum clear_sort
@@ -8996,37 +9024,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 =
@@ -9744,10 +9785,12 @@ bgp_vty_init (void)
/* address-family commands. */
install_element (BGP_NODE, &address_family_ipv4_cmd);
- install_element (BGP_NODE, &address_family_ipv4_safi_cmd);
+ install_element (BGP_NODE, &address_family_ipv4_safi_unicast_cmd);
+ install_element (BGP_NODE, &address_family_ipv4_safi_multicast_cmd);
#ifdef HAVE_IPV6
install_element (BGP_NODE, &address_family_ipv6_cmd);
- install_element (BGP_NODE, &address_family_ipv6_safi_cmd);
+ install_element (BGP_NODE, &address_family_ipv6_safi_unicast_cmd);
+ install_element (BGP_NODE, &address_family_ipv6_safi_multicast_cmd);
#endif /* HAVE_IPV6 */
install_element (BGP_NODE, &address_family_vpnv4_cmd);
install_element (BGP_NODE, &address_family_vpnv4_unicast_cmd);
@@ -10516,7 +10559,7 @@ DEFUN (show_ip_community_list,
{
struct symbol_table* table;
vector extract ;
- vector_index i ;
+ vector_index_t i ;
struct symbol* sym ;
struct community_list *list;
@@ -10875,7 +10918,7 @@ DEFUN (show_ip_extcommunity_list,
{
struct symbol_table* table;
vector extract ;
- vector_index i ;
+ vector_index_t i ;
struct symbol* sym ;
struct community_list *list;
@@ -10946,7 +10989,7 @@ community_list_config_write_list(struct vty* vty, int what)
struct community_list *list;
struct community_entry *entry;
vector extract ;
- vector_index i ;
+ vector_index_t i ;
struct symbol* sym ;
int write = 0;
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index ea914022..2eee6cae 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"
@@ -4692,7 +4693,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 f6e12fb4..00dd6804 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"
@@ -453,7 +453,7 @@ bgp_wall_clock(time_t bgp_time)
/*------------------------------------------------------------------------------
* 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 596336b6..1efe342e 100755
--- a/configure.ac
+++ b/configure.ac
@@ -8,7 +8,7 @@
## $Id$
AC_PREREQ(2.53)
-AC_INIT(Quagga, 0.99.18ex14, [http://bugzilla.quagga.net])
+AC_INIT(Quagga, 0.99.18ex16p, [http://bugzilla.quagga.net])
AC_CONFIG_SRCDIR(lib/zebra.h)
AC_CONFIG_MACRO_DIR([m4])
diff --git a/isisd/dict.c b/isisd/dict.c
index 6c3e1e7f..4d2400f6 100644
--- a/isisd/dict.c
+++ b/isisd/dict.c
@@ -18,9 +18,9 @@
* $Name$
*/
+#include "zebra.h"
#include <stdlib.h>
#include <stddef.h>
-#include "zebra.h"
#include "zassert.h"
#define DICT_IMPLEMENTATION
#include "dict.h"
@@ -32,7 +32,7 @@ static const char rcsid[] = "Id: dict.c,v 1.40.2.7 2000/11/13 01:36:44 kaz";
/*
* These macros provide short convenient names for structure members,
* which are embellished with dict_ prefixes so that they are
- * properly confined to the documented namespace. It's legal for a
+ * properly confined to the documented namespace. It's legal for a
* program which uses dict to define, for instance, a macro called ``parent''.
* Such a macro would interfere with the dnode_t struct definition.
* In general, highly portable and reusable C modules which expose their
@@ -143,7 +143,7 @@ static void free_nodes(dict_t *dict, dnode_t *node, dnode_t *nil)
* dict_next() successor function, verifying that the key of each node is
* strictly lower than that of its successor, if duplicates are not allowed,
* or lower or equal if duplicates are allowed. This function is used for
- * debugging purposes.
+ * debugging purposes.
*/
static int verify_bintree(dict_t *dict)
@@ -203,7 +203,7 @@ static unsigned int verify_redblack(dnode_t *nil, dnode_t *root)
if (root->color != dnode_black)
return 0;
return height_left + 1;
- }
+ }
return 1;
}
@@ -336,7 +336,7 @@ dict_t *dict_init(dict_t *dict, dictcount_t maxcount, dict_comp_t comp)
return dict;
}
-/*
+/*
* Initialize a dictionary in the likeness of another dictionary
*/
@@ -376,7 +376,7 @@ static void dict_clear(dict_t *dict)
* debugging purposes, and should be placed in assert statements. Just because
* this function succeeds doesn't mean that the tree is not corrupt. Certain
* corruptions in the tree may simply cause undefined behavior.
- */
+ */
int dict_verify(dict_t *dict)
{
@@ -430,7 +430,7 @@ int dict_similar(const dict_t *left, const dict_t *right)
/*
* Locate a node in the dictionary having the given key.
- * If the node is not found, a null a pointer is returned (rather than
+ * If the node is not found, a null a pointer is returned (rather than
* a pointer that dictionary's nil sentinel node), otherwise a pointer to the
* located node is returned.
*/
@@ -494,9 +494,9 @@ dnode_t *dict_lower_bound(dict_t *dict, const void *key)
tentative = root;
root = root->left;
}
- }
+ }
}
-
+
return tentative;
}
@@ -526,9 +526,9 @@ dnode_t *dict_upper_bound(dict_t *dict, const void *key)
tentative = root;
root = root->right;
}
- }
+ }
}
-
+
return tentative;
}
@@ -708,10 +708,10 @@ dnode_t *dict_delete(dict_t *dict, dnode_t *delete)
child = (delete->left != nil) ? delete->left : delete->right;
- child->parent = delparent = delete->parent;
+ child->parent = delparent = delete->parent;
if (delete == delparent->left) {
- delparent->left = child;
+ delparent->left = child;
} else {
assert (delete == delparent->right);
delparent->right = child;
@@ -1035,7 +1035,7 @@ void dict_load_next(dict_load_t *load, dnode_t *newnode, const void *key)
{
dict_t *dict = load->dictptr;
dnode_t *nil = &load->nilnode;
-
+
assert (!dnode_is_in_a_dict(newnode));
assert (dict->nodecount < dict->maxcount);
@@ -1141,7 +1141,7 @@ void dict_merge(dict_t *dest, dict_t *source)
dict_load_t load;
dnode_t *leftnode = dict_first(dest), *rightnode = dict_first(source);
- assert (dict_similar(dest, source));
+ assert (dict_similar(dest, source));
if (source == dest)
return;
@@ -1174,7 +1174,7 @@ void dict_merge(dict_t *dest, dict_t *source)
leftnode = next;
continue;
}
-
+
copyright:
{
dnode_t *next = dict_next(source, rightnode);
@@ -1202,7 +1202,7 @@ typedef char input_t[256];
static int tokenize(char *string, ...)
{
- char **tokptr;
+ char **tokptr;
va_list arglist;
int tokcount = 0;
@@ -1266,7 +1266,7 @@ static void construct(dict_t *d)
dnode_t *dn;
char *tok1, *tok2, *val;
const char *key;
- char *help =
+ char *help =
"p turn prompt on\n"
"q finish construction\n"
"a <key> <val> add new entry\n";
diff --git a/isisd/dict.h b/isisd/dict.h
index 9395d1c0..e381256f 100644
--- a/isisd/dict.h
+++ b/isisd/dict.h
@@ -21,7 +21,7 @@
#ifndef DICT_H
#define DICT_H
-#include <limits.h>
+#include "misc.h"
#ifdef KAZLIB_SIDEEFFECT_DEBUG
#include "sfx.h"
#endif
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_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/isisd/topology/spgrid.c b/isisd/topology/spgrid.c
index 5197beb1..2aec9841 100644
--- a/isisd/topology/spgrid.c
+++ b/isisd/topology/spgrid.c
@@ -1,10 +1,11 @@
+#include <zebra.h>
+
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "random.c"
-#include <zebra.h>
#include "thread.h"
#include "vty.h"
diff --git a/lib/Makefile.am b/lib/Makefile.am
index e88c5998..2d41399c 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -4,20 +4,24 @@ INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib @SNMP_INCLUDES@
DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\"
lib_LTLIBRARIES = libzebra.la
-libzebra_la_LDFLAGS = -version-info 0:0:0
+libzebra_la_LDFLAGS = -version-info 0:0:0
libzebra_la_SOURCES = \
- network.c pid_output.c getopt.c getopt1.c daemon.c \
- checksum.c vector.c linklist.c vty.c command.c \
- sockunion.c prefix.c thread.c if.c memory.c buffer.c table.c hash.c \
- filter.c routemap.c distribute.c stream.c str.c log.c plist.c \
- 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_queue.c qlib_init.c pthread_safe.c list_util.c \
- vty_io.c vty_cli.c keystroke.c qstring.c vio_fifo.c vio_lines.c \
- qiovec.c qfstring.c errno_names.c
-
+ buffer.c checksum.c command.c command_execute.c \
+ command_parse.c command_queue.c daemon.c distribute.c \
+ elstring.c errno_names.c filter.c getopt.c getopt1.c \
+ hash.c heap.c if.c if_rmap.c jhash.c keychain.c keystroke.c \
+ linklist.c list_util.c log.c md5.c memory.c memtypes.c mqueue.c \
+ network.c pid_output.c plist.c pqueue.c prefix.c privs.c \
+ pthread_safe.c qfstring.c qiovec.c qlib_init.c qpath.c \
+ qpnexus.c qpselect.c qpthreads.c qrand.c qstring.c \
+ qtime.c qtimers.c routemap.c sigevent.c smux.c \
+ sockopt.c sockunion.c str.c stream.c symtab.c \
+ table.c thread.c vector.c vio_fifo.c vio_lines.c \
+ vty.c vty_cli.c vty_command.c vty_io.c vty_io_basic.c \
+ vty_io_file.c vty_io_shell.c vty_io_term.c vty_io_vsh.c \
+ vty_log.c workqueue.c zclient.c
+
BUILT_SOURCES = memtypes.h route_types.h
libzebra_la_DEPENDENCIES = @LIB_REGEX@
@@ -25,20 +29,24 @@ libzebra_la_DEPENDENCIES = @LIB_REGEX@
libzebra_la_LIBADD = @LIB_REGEX@
pkginclude_HEADERS = \
- 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 \
- 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_queue.h qlib_init.h qafi_safi.h \
- confirm.h miyagi.h pthread_safe.h list_util.h node_type.h uty.h \
- vty_io.h vty_cli.h keystroke.h qstring.h vio_fifo.h vio_lines.h \
- qiovec.h qfstring.h errno_names.h \
- route_types.h command_execute.h
-
+ command.h command_common.h command_execute.h \
+ command_local.h command_parse.h command_queue.h confirm.h \
+ distribute.h elstring.h errno_names.h filter.h getopt.h \
+ hash.h heap.h if.h if_rmap.h jhash.h keychain.h \
+ keystroke.h linklist.h list_util.h log.h log_local.h \
+ md5.h memory.h memtypes.h misc.h miyagi.h mqueue.h \
+ network.h plist.h pqueue.h prefix.h privs.h \
+ pthread_safe.h qafi_safi.h qdebug_nb.h qfstring.h qiovec.h \
+ qlib_init.h qpath.h qpnexus.h qpselect.h qpthreads.h \
+ qrand.h qstring.h qtime.h qtimers.h route_types.h \
+ routemap.h sigevent.h smux.h sockopt.h sockunion.h str.h \
+ stream.h symtab.h table.h thread.h tstring.h vargs.h \
+ vector.h version.h vio_fifo.h vio_lines.h vty.h vty_cli.h \
+ vty_command.h vty_common.h vty_io.h vty_io_basic.h \
+ vty_io_file.h vty_io_shell.h vty_io_term.h vty_io_vsh.h \
+ vty_local.h vty_log.h workqueue.h zassert.h zclient.h \
+ zconfig.h zebra.h
+
EXTRA_DIST = regex.c regex-gnu.h memtypes.awk route_types.awk route_types.txt
memtypes.h: $(srcdir)/memtypes.c $(srcdir)/memtypes.awk
diff --git a/lib/command.c b/lib/command.c
index 67a9ab04..4057bcfc 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -21,83 +21,285 @@ 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 "misc.h"
+#include "version.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 "vty_local.h"
+#include "vty_command.h"
+#include "vty_io.h"
+#include "network.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 and debug hello message.
+ */
+static const char* default_motd =
+"\n"
+"Hello, this is " QUAGGA_PROGNAME " (version " QUAGGA_VERSION ")\n"
+ QUAGGA_COPYRIGHT "\n"
+"\n" ;
+
+const char* debug_banner =
+ QUAGGA_PROGNAME " version " QUAGGA_VERSION " QDEBUG=" QDEBUG_NAME " "
+ __DATE__ " " __TIME__ ;
+
+/*==============================================================================
+ * Host information structure -- shared across command/vty
+ *
+ * Must have VTY_LOCK() or not be running multiple pthreads to access this !
+ */
+struct host host =
+{
+ /* Host name of this router. */
+ .name = NULL, /* set by cmd_init */
+ .name_set = false,
+ .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 */
-/* Command vector which includes some level of command lists. Normally
- each daemon maintains each own cmdvec. */
-vector cmdvec = NULL;
+ /* Log filename. */
+ .logfile = NULL,
-struct desc desc_cr;
-char *command_cr = NULL;
+ /* config file name of this host */
+ .config_file = NULL,
+ .config_dir = NULL,
-/* Host information structure. */
-struct host host;
+ /* Flags for services */
+ .advanced = false,
+ .encrypt = false,
-/* Standard command node structures. */
+ /* Banner configuration. */
+ .motd = NULL,
+ .motdfile = NULL,
+
+ /* Nobody has the config symbol of power */
+ .config = false,
+ .config_brand = 0,
+
+ /* 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 cmd_getcwd() */
+ .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() ;
+
+ 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 ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ uname (&info) ;
+ cmd_new_host_name(info.nodename) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * 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)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ 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) ;
+ } ;
+} ;
+
+/*==============================================================================
+ * node structures for the basic nodes.
+ *
+ * Covers everything up to and including the CONFIG_NODE.
+ */
static struct cmd_node auth_node =
{
- AUTH_NODE,
- "Password: ",
+ .node = AUTH_NODE,
+ .prompt = "Password: ",
+
+ .parent = AUTH_NODE, /* self => no parent */
+ .exit_to = NULL_NODE, /* close ! */
+ .end_to = AUTH_NODE, /* self => no end */
};
static struct cmd_node view_node =
{
- VIEW_NODE,
- "%s> ",
+ .node = VIEW_NODE,
+ .prompt = "%s> ",
+
+ .parent = VIEW_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 = RESTRICTED_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 = VIEW_NODE, /* fall back */
+ .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 = ENABLE_NODE, /* more or less */
+ .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_NAME " "
- __DATE__ " " __TIME__ ;
-#endif
+ .config_to_vtysh = true
+};
static const struct facility_map {
int facility;
@@ -105,16 +307,16 @@ static const struct facility_map {
size_t match;
} syslog_facilities[] =
{
- { LOG_KERN, "kern", 1 },
- { LOG_USER, "user", 2 },
- { LOG_MAIL, "mail", 1 },
+ { LOG_KERN, "kern", 1 },
+ { LOG_USER, "user", 2 },
+ { LOG_MAIL, "mail", 1 },
{ LOG_DAEMON, "daemon", 1 },
- { LOG_AUTH, "auth", 1 },
+ { LOG_AUTH, "auth", 1 },
{ LOG_SYSLOG, "syslog", 1 },
- { LOG_LPR, "lpr", 2 },
- { LOG_NEWS, "news", 1 },
- { LOG_UUCP, "uucp", 2 },
- { LOG_CRON, "cron", 1 },
+ { LOG_LPR, "lpr", 2 },
+ { LOG_NEWS, "news", 1 },
+ { LOG_UUCP, "uucp", 2 },
+ { LOG_CRON, "cron", 1 },
#ifdef LOG_FTP
{ LOG_FTP, "ftp", 1 },
#endif
@@ -129,6 +331,31 @@ static const struct facility_map {
{ 0, NULL, 0 },
};
+#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,
+ .items = NULL,
+ .cmdsize = 0,
+ .config = NULL,
+ .subconfig = NULL,
+ .attr = CMD_ATTR_SIMPLE,
+} ;
+#endif
+
+
static const char *
facility_name(int facility)
{
@@ -171,9 +398,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;
@@ -184,8 +414,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++)
{
@@ -203,8 +435,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);
}
@@ -220,209 +452,172 @@ cmp_desc (const void *p, const void *q)
#endif //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
-/* Install top node of command vector. */
-void
-install_node (struct cmd_node *node,
- int (*func) (struct vty *))
-{
- vector_set_index (cmdvec, node->node, node);
- node->func = func;
- node->cmd_vector = vector_init (0);
-}
-
-/* Compare two command's string. Used in sort_node (). */
-static int
-cmp_node (const struct cmd_element **a, const struct cmd_element **b)
-{
- return strcmp ((*a)->string, (*b)->string);
-}
-
-static int
-cmp_desc (const struct desc **a, const struct desc **b)
-{
- return strcmp ((*a)->cmd, (*b)->cmd);
-}
-
-/* Sort each node's command element according to command string. */
-void
-sort_node ()
-{
- unsigned int i ;
-
- for (i = 0; i < vector_length(cmdvec); i++)
- {
- struct cmd_node *cnode;
- vector cmd_vector ;
- unsigned int j;
-
- cnode = vector_get_item(cmdvec, i) ;
-
- if (cnode == NULL)
- continue ;
-
- cmd_vector = cnode->cmd_vector;
- if (cmd_vector == NULL)
- 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) ;
- } ;
- } ;
-} ;
-
/*------------------------------------------------------------------------------
- * Take string and break it into tokens.
+ * Install top node of command vector.
*
- * Discards leading and trailing white-space.
+ * Initialised as follows:
*
- * Treats lines that start with '!' or '#' (after any leading white-space)
- * as empty -- these are comment lines.
+ * .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
*
- * Tokens are non-whitespace separated by one or more white-space.
+ * .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
*
- * White-space is anything that isspace() thinks is a space. (Which in the
- * 'C' locale is ' ', '\t', '\r, '\n', '\f' and '\v'.)
+ * .config_write -- is set from parameter
*
- * Returns: NULL => empty line (after white-space trimming) or comment line.
- * otherwise: is vector containing one or more tokens.
+ * .cmd_vector -- initialised empty
+ *
+ * Default parent node:
+ *
+ * * all nodes > NODE_CONFIG have NODE_CONFIG as parent
+ * * node == NODE_CONFIG has ENABLE_NODE as parent
+ * * all nodes < NODE_CONFIG are their own parents (including ENABLE_NODE)
*
- * ....
+ * Default exit_to:
*
+ * * all nodes > NODE_CONFIG exit_to their parent
+ * * node == NODE_CONFIG exit_to ENABLE_NODE (its parent)
+ * * all nodes < NODE_CONFIG exit_to close
*
- * Note: all the tokens in the vector have at least one character, and no
- * entries are NULL.
+ * Default end_to:
+ *
+ * * all nodes >= NODE_CONFIG end_to ENABLE_NODE
+ * * all nodes < NODE_CONFIG end_to themselves
*
- * NB: it is the caller's responsibility to release the vector and its contents,
- * see cmd_free_strvec().
*/
-static vector
-cmd_make_vline(vector vline, qstring qs, const char *string)
+extern void
+install_node (struct cmd_node *node,
+ int (*config_write) (struct vty *))
{
- char *token, *tp ;
- const char *cp, *sp, *ep, *op ;
+ confirm(NULL_NODE == 0) ; /* unset value for .parent, .end_to & .exit_to */
- /* Reset any existing vline, and empty the qstring if given. */
- if (vline != NULL)
- vector_set_length(vline, 0) ;
+ if (node->parent == NULL_NODE)
+ {
+ if (node->node > CONFIG_NODE)
+ node->parent = CONFIG_NODE ;
+ else if (node->node == CONFIG_NODE)
+ node->parent = ENABLE_NODE ;
+ else
+ node->parent = node->node ;
+ } ;
- qs_clear(qs) ;
+ if (node->end_to == NULL_NODE)
+ {
+ if (node->node >= CONFIG_NODE)
+ node->end_to = ENABLE_NODE ;
+ else
+ node->end_to = node->node ;
+ } ;
- /* Strip leading and trailing white-space and deal with empty or effectively
- * empty lines -- comment lines are treated as effectively empty.
- */
- cp = string;
+ if (node->exit_to == NULL_NODE)
+ {
+ if (node->node >= CONFIG_NODE)
+ node->exit_to = node->parent ;
+ else
+ node->exit_to = NULL_NODE ;
+ } ;
- if (string == NULL)
- return NULL;
+ node->config_write = config_write ;
- while (isspace((int) *cp))
- cp++;
+ vector_init_new(node->cmd_vector, 0) ; /* embedded */
- ep = cp + strlen(cp) ;
+ vector_set_index (node_vector, node->node, node);
+} ;
- while ((ep > cp) && (isspace((int)*(ep - 1))))
- --ep ;
+/*------------------------------------------------------------------------------
+ * Return address of cmd_node -- asserts is not NULL !
+ */
+static cmd_node
+cmd_cmd_node(node_type_t node)
+{
+ cmd_node cn ;
- if ((cp == ep) || (*cp == '!') || (*cp == '#'))
- return NULL;
+ cn = vector_get_item(node_vector, node) ;
+ if (cn != NULL)
+ return cn ;
- /* Prepare return vector -- expect some reasonable number of tokens. */
- if (vline == NULL)
- vline = vector_init(10) ;
+ zabort("invalid node") ;
+} ;
- /* If writing the words to a qstring, copy the body of the original (less
- * any leading/trailing whitespace) to the qstring and '\0' terminate.
- */
- if (qs != NULL)
- {
- qs_set_n(qs, cp, ep - cp) ;
- tp = (char*)qs->body ; /* start at the beginning */
- }
- else
- tp = NULL ; /* not used, but not undefined */
+/*------------------------------------------------------------------------------
+ * Return parent node
+ */
+extern node_type_t
+cmd_node_parent(node_type_t node)
+{
+ return (cmd_cmd_node(node))->parent ;
+} ;
- op = cp ; /* original start position */
+/*------------------------------------------------------------------------------
+ * Return exit_to node
+ */
+extern node_type_t
+cmd_node_exit_to(node_type_t node)
+{
+ return (cmd_cmd_node(node))->exit_to ;
+} ;
- /* Now have string cp..ep with no leading/trailing whitespace.
- *
- * If using a qstring, a copy of that exists at tp, complete with terminating
- * '\0'. Writes '\0' terminators after each word found -- overwriting first
- * separating white-space or the '\0' at the end.
- *
- * If not using a qstring, construct a new MTYPE_STRVEC for each word.
- */
- while (cp < ep)
+/*------------------------------------------------------------------------------
+ * Return end_to 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_node (const struct cmd_command **a, const struct cmd_command **b)
+{
+ return strcmp ((*a)->string, (*b)->string);
+}
+
+/* Sort each node's command element according to command string. */
+extern void
+sort_node ()
+{
+ unsigned int i ;
+
+ for (i = 0; i < vector_length(node_vector); i++)
{
- while (isspace((int) *cp))
- cp++ ; /* skip white-space */
+ struct cmd_node *cn;
+ vector cmd_vector ;
- sp = cp ;
- while ((cp < ep) && !isspace((int) *cp))
- cp++ ; /* eat token characters */
+ cn = vector_get_item(node_vector, i) ;
- if (qs == NULL)
- {
- /* creating array of MTYPE_STRVEC */
- size_t len ;
+ if (cn == NULL)
+ continue ;
- len = cp - sp ;
- token = XMALLOC (MTYPE_STRVEC, len + 1);
- memcpy (token, sp, len);
- *(token + len) = '\0';
- }
- else
- {
- /* using qstring */
- token = tp + (sp - op) ; /* token in qstring */
- *(tp + (cp - op)) = '\0' ; /* terminate */
- } ;
+ cmd_vector = cn->cmd_vector;
+ if (cmd_vector == NULL)
+ continue ;
- vector_push_item(vline, token);
+ vector_sort(cmd_vector, (vector_sort_cmp*)cmp_node) ;
} ;
+} ;
- return vline ;
-}
-
+#if 0
/*------------------------------------------------------------------------------
- * Take string and break it into tokens.
- *
- * Discards leading and trailing white-space.
- *
- * Treats lines that start with '!' or '#' (after any leading white-space)
- * as empty -- these are comment lines.
- *
- * Tokens are non-whitespace separated by one or more white-space.
- *
- * White-space is anything that isspace() thinks is a space. (Which in the
- * 'C' locale is ' ', '\t', '\r, '\n', '\f' and '\v'.)
+ * Take string and break it into tokens -- see cmd_make_tokens().
*
* Returns: NULL => empty line (after white-space trimming) or comment line.
- * otherwise: is vector containing one or more tokens.
- *
- * Note: all the tokens in the vector have at least one character, and no
- * entries are NULL.
- *
- * NB: it is the caller's responsibility to release the vector and its contents,
- * see cmd_free_strvec().
+ * otherwise: is vector containing one or more tokens in qstrings.
*/
extern vector
cmd_make_strvec (const char *string)
{
- return cmd_make_vline(NULL, NULL, string) ;
+#if 0
+ return cmd_tokenize(NULL, string) ;
+#error sort this one out
+#endif
+ return NULL ;
} ;
/*------------------------------------------------------------------------------
@@ -431,2722 +626,569 @@ 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_free(strvec)) != 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 == '\r' || *cp == '\n' || *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;
+ struct cmd_node *cn ;
- 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);
+ cn = NULL ;
+ if (node < node_vector->limit)
+ cn = vector_get_item (node_vector, node);
- if (cnode == NULL)
+ if (cn == NULL)
{
zlog_err("Could not find prompt for node %d for", node) ;
return NULL ;
} ;
- return cnode->prompt;
+ return cn->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 cn ;
- /* cmd_init hasn't been called */
- if (!cmdvec)
- return;
+ cn = vector_get_item (node_vector, ntype);
- cnode = vector_get_item (cmdvec, ntype);
-
- if (cnode == NULL)
+ if (cn == NULL)
{
fprintf (stderr, "Command node %d doesn't exist, please check it\n",
ntype);
exit (1);
}
- vector_set (cnode->cmd_vector, cmd);
+ vector_set (cn->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";
-
-static void
-to64(char *s, long v, int n)
-{
- while (--n >= 0)
- {
- *s++ = itoa64[v&0x3f];
- v >>= 6;
- }
-}
+ "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
-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];
- gettimeofday(&tv,0);
+ extern char *crypt (const char *, const char *) ;
- to64(&salt[0], random(), 3);
- to64(&salt[3], tv.tv_usec, 3);
- salt[5] = '\0';
+ r = qt_random(*passwd) ;
- return crypt (passwd, salt);
+ 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) ;
}
/* This function write configuration of this host. */
static int
-config_write_host (struct vty *vty)
+config_write_host (vty vty)
{
+ vty_io vio ;
+
+ VTY_LOCK() ;
+
+ vio = vty->vio ;
+
if (qpthreads_enabled)
- vty_out (vty, "threaded%s", VTY_NEWLINE);
+ uty_out (vio, "threaded\n");
- if (host.name)
- vty_out (vty, "hostname %s%s", host.name, VTY_NEWLINE);
+ if (host.name_set)
+ uty_out (vio, "hostname %s\n", host.name);
- 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)
+ uty_out (vio, "password 8 %s\n", host.password);
+ else
+ uty_out (vio, "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)
+ uty_out (vio, "enable password 8 %s\n", host.enable);
+ else
+ uty_out (vio, "enable password %s\n", host.enable);
+ } ;
if (zlog_get_default_lvl(NULL) != LOG_DEBUG)
{
- vty_out (vty, "! N.B. The 'log trap' command is deprecated.%s",
- VTY_NEWLINE);
- vty_out (vty, "log trap %s%s",
- zlog_priority[zlog_get_default_lvl(NULL)], VTY_NEWLINE);
+ uty_out (vio, "! N.B. The 'log trap' command is deprecated.\n");
+ uty_out (vio, "log trap %s\n", zlog_priority[zlog_get_default_lvl(NULL)]);
}
if (host.logfile && (zlog_get_maxlvl(NULL, ZLOG_DEST_FILE) != ZLOG_DISABLED))
{
- vty_out (vty, "log file %s", host.logfile);
+ uty_out (vio, "log file %s", qpath_string(host.logfile));
if (zlog_get_maxlvl(NULL, ZLOG_DEST_FILE) != zlog_get_default_lvl(NULL))
- vty_out (vty, " %s",
+ uty_out (vio, " %s",
zlog_priority[zlog_get_maxlvl(NULL, ZLOG_DEST_FILE)]);
- vty_out (vty, "%s", VTY_NEWLINE);
+ uty_out (vio, "\n");
}
if (zlog_get_maxlvl(NULL, ZLOG_DEST_STDOUT) != ZLOG_DISABLED)
{
- vty_out (vty, "log stdout");
+ uty_out (vio, "log stdout");
if (zlog_get_maxlvl(NULL, ZLOG_DEST_STDOUT) != zlog_get_default_lvl(NULL))
- vty_out (vty, " %s",
+ uty_out (vio, " %s",
zlog_priority[zlog_get_maxlvl(NULL, ZLOG_DEST_STDOUT)]);
- vty_out (vty, "%s", VTY_NEWLINE);
+ uty_out (vio, "\n");
}
if (zlog_get_maxlvl(NULL, ZLOG_DEST_MONITOR) == ZLOG_DISABLED)
- vty_out(vty,"no log monitor%s",VTY_NEWLINE);
+ uty_out(vio,"no log monitor%s",VTY_NEWLINE);
else if (zlog_get_maxlvl(NULL, ZLOG_DEST_MONITOR) != zlog_get_default_lvl(NULL))
- vty_out(vty,"log monitor %s%s",
- zlog_priority[zlog_get_maxlvl(NULL, ZLOG_DEST_MONITOR)],VTY_NEWLINE);
+ uty_out(vio,"log monitor %s\n",
+ zlog_priority[zlog_get_maxlvl(NULL, ZLOG_DEST_MONITOR)]);
if (zlog_get_maxlvl(NULL, ZLOG_DEST_SYSLOG) != ZLOG_DISABLED)
{
- vty_out (vty, "log syslog");
+ uty_out (vio, "log syslog");
if (zlog_get_maxlvl(NULL, ZLOG_DEST_SYSLOG) != zlog_get_default_lvl(NULL))
- vty_out (vty, " %s",
+ uty_out (vio, " %s",
zlog_priority[zlog_get_maxlvl(NULL, ZLOG_DEST_SYSLOG)]);
- vty_out (vty, "%s", VTY_NEWLINE);
+ uty_out (vio, "\n");
}
if (zlog_get_facility(NULL) != LOG_DAEMON)
- vty_out (vty, "log facility %s%s",
- facility_name(zlog_get_facility(NULL)), VTY_NEWLINE);
+ uty_out (vio, "log facility %s\n", facility_name(zlog_get_facility(NULL)));
if (zlog_get_record_priority(NULL) == 1)
- vty_out (vty, "log record-priority%s", VTY_NEWLINE);
+ uty_out (vio, "log record-priority\n");
if (zlog_get_timestamp_precision(NULL) > 0)
- vty_out (vty, "log timestamp precision %d%s",
- zlog_get_timestamp_precision(NULL), VTY_NEWLINE);
+ uty_out (vio, "log timestamp precision %d\n",
+ zlog_get_timestamp_precision(NULL));
if (host.advanced)
- vty_out (vty, "service advanced-vty%s", VTY_NEWLINE);
+ uty_out (vio, "service advanced-vty\n");
if (host.encrypt)
- vty_out (vty, "service password-encryption%s", VTY_NEWLINE);
+ uty_out (vio, "service password-encryption\n");
if (host.lines >= 0)
- vty_out (vty, "service terminal-length %d%s", host.lines,
- VTY_NEWLINE);
+ uty_out (vio, "service terminal-length %d\n", host.lines);
- if (host.motdfile)
- vty_out (vty, "banner motd file %s%s", host.motdfile, VTY_NEWLINE);
+ if (host.motdfile)
+ uty_out (vio, "banner motd file %s\n", qpath_string(host.motdfile));
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;
-}
+ uty_out (vio, "no banner motd\n");
-#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;
+ VTY_UNLOCK() ;
- 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;
+ return 1;
}
-#endif
/*==============================================================================
- * Match functions.
+ * Commands and other stuff related to:
*
- * Is the given string a, possibly incomplete, value of the required kind ?
- */
-
-/* Completion match types. */
-enum match_type
-{
- no_match, /* nope */
- extend_match,
-
- ipv4_prefix_match,
- ipv4_match,
- ipv6_prefix_match,
- ipv6_match,
- range_match,
- vararg_match,
-
- partly_match, /* OK as far as it went */
- exact_match /* Syntactically complete */
-};
-
-/*------------------------------------------------------------------------------
- * Is this an IPv4 Address:
+ * * end (and ^Z) -- go to ENABLE_NODE (aka Privileged Exec) if above that,
+ * otherwise do nothing.
*
- * 999.999.999.999 -- where no part may be > 255
+ * This is installed in all nodes.
*
- * TODO: cmd_ipv4_match() seems to accept leading '.' ?
- * TODO: cmd_ipv4_match() seems to accept leading zeros ?
+ * * exit -- go to parent node, if in CONFIG_NODE or any of its
+ * sub-nodes.
*
- * Returns: no_match -- improperly formed
- * partly_match -- accepts empty string
- * exact_match -- syntactically complete
- */
-static enum match_type
-cmd_ipv4_match (const char *str)
-{
- const char *sp;
- int dots = 0, nums = 0;
- char buf[4];
-
- if (str == NULL)
- return partly_match;
-
- for (;;)
- {
- memset (buf, 0, sizeof (buf));
- sp = str;
- while (*str != '\0')
- {
- if (*str == '.')
- {
- if (dots >= 3)
- return no_match;
-
- if (*(str + 1) == '.')
- return no_match;
-
- if (*(str + 1) == '\0')
- return partly_match;
-
- dots++;
- break;
- }
- if (!isdigit ((int) *str))
- return no_match;
-
- str++;
- }
-
- if (str - sp > 3)
- return no_match;
-
- strncpy (buf, sp, str - sp);
- if (atoi (buf) > 255)
- return no_match;
-
- nums++;
-
- if (*str == '\0')
- break;
-
- str++;
- }
-
- if (nums < 4)
- return partly_match;
-
- return exact_match;
-}
-
-/*------------------------------------------------------------------------------
- * Is this an IPv4 Prefix:
+ * Parent of CONFIG_NODE is ENABLE_NODE.
*
- * 999.999.999.999/99 -- where no part may be > 255,
- * and prefix length may not be > 32
+ * For all other nodes, this is EOF (which for the
+ * terminal is "close").
*
- * TODO: cmd_ipv4_prefix_match() seems to accept leading '.' ?
- * TODO: cmd_ipv4_prefix_match() seems to accept leading zeros ?
+ * This is installed in all nodes.
*
- * Returns: no_match -- improperly formed
- * partly_match -- accepts empty string
- * exact_match -- syntactically complete
+ * * enable -- go to ENABLE_NODE, if can.
*
- * NB: partly_match is returned for anything valid before the '/', but which
- * has no '/' or no number after the '/'.
- */
-static enum match_type
-cmd_ipv4_prefix_match (const char *str)
-{
- const char *sp;
- int dots = 0;
- char buf[4];
-
- if (str == NULL)
- return partly_match;
-
- for (;;)
- {
- memset (buf, 0, sizeof (buf));
- sp = str;
- while (*str != '\0' && *str != '/')
- {
- if (*str == '.')
- {
- if (dots == 3)
- return no_match;
-
- if (*(str + 1) == '.' || *(str + 1) == '/')
- return no_match;
-
- if (*(str + 1) == '\0')
- return partly_match;
-
- dots++;
- break;
- }
-
- if (!isdigit ((int) *str))
- return no_match;
-
- str++;
- }
-
- if (str - sp > 3)
- return no_match;
-
- strncpy (buf, sp, str - sp);
- if (atoi (buf) > 255)
- return no_match;
-
- if (dots == 3)
- {
- if (*str == '/')
- {
- if (*(str + 1) == '\0')
- return partly_match;
-
- str++;
- break;
- }
- else if (*str == '\0')
- return partly_match;
- }
-
- if (*str == '\0')
- return partly_match;
-
- str++;
- }
-
- sp = str;
- while (*str != '\0')
- {
- if (!isdigit ((int) *str))
- return no_match;
-
- str++;
- }
-
- if (atoi (sp) > 32)
- return no_match;
-
- return exact_match;
-}
-
-/*------------------------------------------------------------------------------
- * Is this an IPv6 Address:
+ * This is installed in VIEW_NODE and RESTRICTED_NODE.
*
- * TODO: cmd_ipv6_match() only returns "partly_match" for empty string ?
+ * It is also installed in ENABLE_NODE (and hence is
+ * available anywhere), where it is a synonym for 'end' !
*
- * Returns: no_match -- improperly formed
- * partly_match -- accepts empty string
- * exact_match -- syntactically complete
- */
-
-#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
-
-#ifdef HAVE_IPV6
-
-static enum match_type
-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 (str == NULL)
- return partly_match;
-
- if (strspn (str, IPV6_ADDR_STR) != strlen (str))
- return no_match;
-
- /* 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);
-
- if (ret == 1)
- return exact_match;
-
- while (*str != '\0')
- {
- switch (state)
- {
- case STATE_START:
- if (*str == ':')
- {
- if (*(str + 1) != ':' && *(str + 1) != '\0')
- return no_match;
- colons--;
- state = STATE_COLON;
- }
- else
- {
- sp = str;
- state = STATE_ADDR;
- }
-
- continue;
- case STATE_COLON:
- colons++;
- if (*(str + 1) == ':')
- state = STATE_DOUBLE;
- else
- {
- sp = str + 1;
- state = STATE_ADDR;
- }
- break;
- case STATE_DOUBLE:
- if (double_colon)
- return no_match;
-
- if (*(str + 1) == ':')
- return no_match;
- else
- {
- if (*(str + 1) != '\0')
- colons++;
- sp = str + 1;
- state = STATE_ADDR;
- }
-
- double_colon++;
- nums++;
- break;
- case STATE_ADDR:
- if (*(str + 1) == ':' || *(str + 1) == '\0')
- {
- if (str - sp > 3)
- return no_match;
-
- nums++;
- state = STATE_COLON;
- }
- if (*(str + 1) == '.')
- state = STATE_DOT;
- break;
- case STATE_DOT:
- state = STATE_ADDR;
- break;
- default:
- break;
- }
-
- if (nums > 8)
- return no_match;
-
- if (colons > 7)
- return no_match;
-
- str++;
- }
-
-#if 0
- if (nums < 11)
- return partly_match;
-#endif /* 0 */
-
- return exact_match;
-}
-
-/*------------------------------------------------------------------------------
- * Is this an IPv6 Prefix:
+ * For configuration reading (VTY_CONFIG_READ) and for
+ * VTY_SHELL_SERVER, no password is required.
*
- * TODO: cmd_ipv6_prefix_match() hardly returns "partly_match" ?
- * TODO: cmd_ipv6_prefix_match() possibly accepts invalid address before '/' ?
+ * For VTY_TERMINAL, must already have authenticated
+ * once, or must be able to enter AUTH_ENABLE_NODE.
*
- * 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 '/'.
- */
-static enum match_type
-cmd_ipv6_prefix_match (const char *str)
-{
- int state = STATE_START;
- int colons = 0, nums = 0, double_colon = 0;
- int mask;
- const char *sp = NULL;
- char *endptr = NULL;
-
- if (str == NULL)
- return partly_match;
-
- if (strspn (str, IPV6_PREFIX_STR) != strlen (str))
- return no_match;
-
- while (*str != '\0' && state != STATE_MASK)
- {
- switch (state)
- {
- case STATE_START:
- if (*str == ':')
- {
- if (*(str + 1) != ':' && *(str + 1) != '\0')
- return no_match;
- colons--;
- state = STATE_COLON;
- }
- else
- {
- sp = str;
- state = STATE_ADDR;
- }
-
- 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;
- }
- break;
- case STATE_DOUBLE:
- if (double_colon)
- return no_match;
-
- if (*(str + 1) == ':')
- return no_match;
- else
- {
- if (*(str + 1) != '\0' && *(str + 1) != '/')
- colons++;
- sp = str + 1;
-
- if (*(str + 1) == '/')
- state = STATE_SLASH;
- else
- state = STATE_ADDR;
- }
-
- 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;
-
- 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 (nums > 11)
- return no_match;
-
- if (colons > 7)
- return no_match;
-
- str++;
- }
-
- if (state < STATE_MASK)
- return partly_match;
-
- mask = strtol (str, &endptr, 10);
- if (*endptr != '\0')
- return no_match;
-
- if (mask < 0 || mask > 128)
- return no_match;
-
-/* 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;
-*/
-
- return exact_match;
-}
-
-#endif /* HAVE_IPV6 */
-
-/*------------------------------------------------------------------------------
- * Is this a decimal number in the allowed range:
- *
- * Returns: 1 => OK -- *including* empty string
- * 0 => not a valid number, or not in required range
- * (or invalid range !!)
- */
-
-#define DECIMAL_STRLEN_MAX 10
-
-static int
-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;
-
- if (str == NULL)
- return 1;
-
- val = strtoul (str, &endptr, 10);
- if (*endptr != '\0')
- return 0;
-
- range++;
- p = strchr (range, '-');
- if (p == NULL)
- return 0;
- if (p - range > DECIMAL_STRLEN_MAX)
- return 0;
- strncpy (buf, range, p - range);
- buf[p - range] = '\0';
- min = strtoul (buf, &endptr, 10);
- if (*endptr != '\0')
- return 0;
-
- range = p + 1;
- p = strchr (range, '>');
- if (p == NULL)
- return 0;
- if (p - range > DECIMAL_STRLEN_MAX)
- return 0;
- strncpy (buf, range, p - range);
- buf[p - range] = '\0';
- max = strtoul (buf, &endptr, 10);
- if (*endptr != '\0')
- return 0;
-
- if (val < min || val > max)
- return 0;
-
- return 1;
-}
-
-/*==============================================================================
- * 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.
+ * * disable -- go to VIEW_NODE (aka User Exec).
*
+ * This is installed in ENABLE_NODE *only*.
*
+ * Note, however, that all ENABLE_NODE commands are
+ * available at ENABLE_NODE and above !
*/
/*------------------------------------------------------------------------------
- * Make 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)
- *
- * Returns: any of the enum match_type values:
+ * Enter CONFIG_NODE, possibly via password check.
*
- * no_match => no match of any kind
+ * If the parser established that can enter CONFIG_NODE directly, that's what
+ * happens.
*
- * 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 )
+ * If the parser established that must authenticate, then may fail here if
+ * we are not in the right state to run the authentication.
*
- * partly_match => saw partial match for a keyword
- * exact_match => saw exact match for a keyword
+ * The authentication itself may fail...
*
- * 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.
+ * NB: installed in VIEW_NODE, RESTRICTED_NODE and ENABLE_NODE.
*/
-static enum match_type
-cmd_filter_by_completion (char *command, vector cmd_v, unsigned int index)
-{
- unsigned int i;
- unsigned int k;
- enum match_type match_type;
-
- match_type = no_match;
-
- /* If command and cmd_element string does not match, remove from 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;
- int matched ;
-
- cmd_element = vector_get_item(cmd_v, i) ;
-
- /* Skip past cmd_v entries that have already been set NULL */
- 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 (match_type < vararg_match)
- match_type = vararg_match;
- matched++;
- }
- else if (CMD_RANGE (str))
- {
- if (cmd_range_match (str, command))
- {
- if (match_type < range_match)
- match_type = range_match;
-
- matched++;
- }
- }
-#ifdef HAVE_IPV6
- else if (CMD_IPV6 (str))
- {
- if (cmd_ipv6_match (command))
- {
- if (match_type < ipv6_match)
- match_type = ipv6_match;
-
- matched++;
- }
- }
- else if (CMD_IPV6_PREFIX (str))
- {
- if (cmd_ipv6_prefix_match (command))
- {
- if (match_type < ipv6_prefix_match)
- match_type = ipv6_prefix_match;
-
- matched++;
- }
- }
-#endif /* HAVE_IPV6 */
- else if (CMD_IPV4 (str))
- {
- if (cmd_ipv4_match (command))
- {
- if (match_type < ipv4_match)
- match_type = ipv4_match;
-
- matched++;
- }
- }
- else if (CMD_IPV4_PREFIX (str))
- {
- if (cmd_ipv4_prefix_match (command))
- {
- if (match_type < ipv4_prefix_match)
- match_type = ipv4_prefix_match;
- matched++;
- }
- }
- else if (CMD_OPTION (str) || CMD_VARIABLE (str))
- /* Check is this point's argument optional ? */
- {
- if (match_type < extend_match)
- match_type = extend_match;
- matched++;
- }
- else if (strncmp (command, str, strlen (command)) == 0)
- {
- if (strcmp (command, str) == 0)
- match_type = exact_match;
- else
- {
- if (match_type < partly_match)
- match_type = partly_match;
- }
- matched++;
- } ;
- } ;
-
- /* Keep cmd_v entry that has a match at this position */
- if (matched)
- vector_set_item(cmd_v, k++, cmd_element) ;
- } ;
-
- vector_set_length(cmd_v, k) ; /* discard what did not keep */
-
- return match_type;
+DEFUN_ATTR (config_terminal,
+ config_terminal_cmd,
+ "configure terminal",
+ "Configuration from vty interface\n"
+ "Configuration terminal\n",
+ CMD_ATTR_DIRECT + cmd_sp_configure)
+{
+ if (vty->exec->parsed->nnode == CONFIG_NODE)
+ return vty_cmd_config_lock(vty) ; ;
+
+ /* Otherwise, must authenticate to enter CONFIG_NODE. */
+ return vty_cmd_can_auth_enable(vty) ;
} ;
-/*------------------------------------------------------------------------------
- * Filter vector by command character with index.
- *
- * This appears to be identical to cmd_filter_by_completion(), except that
- * when matching keywords, requires an exact match.
- *
- * TODO: see if can merge cmd_filter_by_completion() & cmd_filter_by_string()
- */
-static enum match_type
-cmd_filter_by_string (char *command, vector cmd_v, unsigned int index)
-{
- unsigned int i ;
- unsigned int k ;
- enum match_type match_type;
-
- match_type = no_match;
-
- /* If command and cmd_element string do match, keep in vector */
- k = 0 ;
- for (i = 0; i < vector_length(cmd_v); i++)
- {
- unsigned int j;
- int matched ;
- const char *str;
- struct cmd_element *cmd_element;
- vector descvec;
- struct desc *desc;
-
- 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 have a match against any of the current possibilities */
- 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 (match_type < vararg_match)
- match_type = vararg_match;
- matched++;
- }
- else if (CMD_RANGE (str))
- {
- if (cmd_range_match (str, command))
- {
- if (match_type < range_match)
- match_type = range_match;
- matched++;
- }
- }
-#ifdef HAVE_IPV6
- else if (CMD_IPV6 (str))
- {
- if (cmd_ipv6_match (command) == exact_match)
- {
- if (match_type < ipv6_match)
- match_type = ipv6_match;
- matched++;
- }
- }
- else if (CMD_IPV6_PREFIX (str))
- {
- if (cmd_ipv6_prefix_match (command) == exact_match)
- {
- if (match_type < ipv6_prefix_match)
- match_type = ipv6_prefix_match;
- matched++;
- }
- }
-#endif /* HAVE_IPV6 */
- else if (CMD_IPV4 (str))
- {
- if (cmd_ipv4_match (command) == exact_match)
- {
- if (match_type < ipv4_match)
- match_type = ipv4_match;
- matched++;
- }
- }
- else if (CMD_IPV4_PREFIX (str))
- {
- if (cmd_ipv4_prefix_match (command) == exact_match)
- {
- if (match_type < ipv4_prefix_match)
- match_type = ipv4_prefix_match;
- matched++;
- }
- }
- else if (CMD_OPTION (str) || CMD_VARIABLE (str))
- {
- if (match_type < extend_match)
- match_type = extend_match;
- matched++;
- }
- else
- {
- if (strcmp (command, str) == 0)
- {
- match_type = exact_match;
- matched++;
- } ;
- } ;
- } ;
-
- /* 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 match_type;
-}
+ALIAS_ATTR (config_terminal,
+ config_enable_configure_cmd,
+ "enable configure",
+ "Turn on privileged mode command\n"
+ "Configuration terminal\n",
+ CMD_ATTR_DIRECT + cmd_sp_configure)
/*------------------------------------------------------------------------------
- * 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.
+ * Enter ENABLE_NODE, possibly via password check.
*
- * 2. for "partial match", look out for matching more than one keyword, and
- * return 1 if finds that.
+ * If the parser established that can enter ENABLE_NODE directly, that's what
+ * happens.
*
- * 3. for "range match", look out for matching more than one range, and
- * return 1 if finds that.
+ * If the parser established that must authenticate, then may fail here if
+ * we are not in the right state to run the authentication.
*
- * 4. for ipv4_prefix_match and ipv6_prefix_match, if get a "partial match",
- * return 2.
+ * The authentication itself may fail...
*
- * 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.
+ * NB: installed in VIEW_NODE, RESTRICTED_NODE and ENABLE_NODE.
*/
-static int
-is_cmd_ambiguous (char *command, vector cmd_v, int index, enum match_type type)
+DEFUN_ATTR (enable,
+ config_enable_cmd,
+ "enable",
+ "Turn on privileged mode command\n",
+ CMD_ATTR_DIRECT + cmd_sp_enable)
{
- unsigned int i;
- unsigned int k;
- int ret ;
+ if (vty->exec->parsed->nnode == ENABLE_NODE)
+ return CMD_SUCCESS ;
- 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;
- int 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++;
- 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++;
- }
- 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++;
- }
- break;
-
-#ifdef HAVE_IPV6
- case ipv6_match:
- if (CMD_IPV6 (str))
- matched++;
- break;
-
- case ipv6_prefix_match:
- if ((mt = cmd_ipv6_prefix_match (command)) != no_match)
- {
- if (mt == partly_match)
- if (ret != 1)
- ret = 2; /* There is incomplete match. */
-
- matched++;
- }
- break;
-#endif /* HAVE_IPV6 */
-
- case ipv4_match:
- if (CMD_IPV4 (str))
- matched++;
- break;
-
- case ipv4_prefix_match:
- if ((mt = cmd_ipv4_prefix_match (command)) != no_match)
- {
- if (mt == partly_match)
- if (ret != 1)
- ret = 2; /* There is incomplete match. */
-
- matched++;
- }
- break;
-
- case extend_match:
- if (CMD_OPTION (str) || CMD_VARIABLE (str))
- matched++;
- 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 ;
+ /* Otherwise, must authenticate to enter ENABLE_NODE. */
+ return vty_cmd_can_auth_enable(vty) ;
} ;
/*------------------------------------------------------------------------------
- * If src matches dst return dst string, otherwise return NULL
- *
- * Returns NULL if dst is an option, variable of vararg.
+ * disable command: end enabled state -> VIEW_NODE.
*
- * NULL or empty src are deemed to match.
+ * NB: although only installed in ENABLE_NODE, it will be implicitly available
+ * in all higher nodes -- as a quick way of crashing out to VIEW_NODE !
*/
-static const char *
-cmd_entry_function (const char *src, const char *dst)
+DEFUN_ATTR(disable,
+ config_disable_cmd,
+ "disable",
+ "Turn off privileged mode command\n",
+ CMD_ATTR_DIRECT + CMD_ATTR_NODE + VIEW_NODE)
{
- 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;
+ return CMD_SUCCESS ; /* will disable to parsed->nnode */
}
/*------------------------------------------------------------------------------
- * Check same string element existence.
- *
- * Returns: 0 => found same string in the vector
- * 1 => NOT found same string in the vector
+ * exit command: down one node level, including exit command processor.
*/
-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
-desc_unique_string (vector v, const char *str)
+DEFUN_ATTR(config_exit,
+ config_exit_cmd,
+ "exit",
+ "Exit current mode and down to previous mode\n",
+ CMD_ATTR_DIRECT + cmd_sp_exit)
{
- 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;
-}
-
-static bool
-cmd_try_do_shortcut (enum node_type node, char* first_word) {
- return (node >= MIN_DO_SHORTCUT_NODE)
- && (first_word != NULL)
- && (strcmp( "do", first_word) == 0) ? 1 : 0 ;
+ return CMD_SUCCESS ; /* will exit to parsed->nnode */
}
-/* '?' describe command support. */
-static vector
-cmd_describe_command_real (vector vline, 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 (vline) == 0)
- {
- *status = CMD_ERR_NO_MATCH;
- return NULL;
- }
- else
- index = vector_length (vline) - 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 (vline, i)))
- {
- match = cmd_filter_by_completion (command, cmd_vector, i);
-
- 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;
- }
-
- if ((ret = is_cmd_ambiguous (command, cmd_vector, i, match)) == 1)
- {
- vector_free (cmd_vector);
- vector_free (matchvec);
- *status = CMD_ERR_AMBIGUOUS;
- return NULL;
- }
- else if (ret == 2)
- {
- vector_free (cmd_vector);
- vector_free (matchvec);
- *status = 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 (vline, index);
- if (command)
- match = cmd_filter_by_completion (command, cmd_vector, index);
-
- /* 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;
-}
+/* quit is alias of exit. */
+ALIAS_ATTR (config_exit,
+ config_quit_cmd,
+ "quit",
+ "Exit current mode and down to previous mode\n",
+ CMD_ATTR_DIRECT + cmd_sp_exit) ;
/*------------------------------------------------------------------------------
- * 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.)
+ * end command: down to enable mode.
*/
-vector
-cmd_describe_command (vector vline, int node, int *status)
+DEFUN_ATTR (config_end,
+ config_end_cmd,
+ "end",
+ "End current mode and change to enable mode\n",
+ CMD_ATTR_DIRECT + cmd_sp_end)
{
- vector ret;
-
- if ( cmd_try_do_shortcut(node, vector_get_item(vline, 0) ) )
- {
- vector shifted_vline;
- unsigned int index;
-
- /* We can try it on enable node, cos' the vty is authenticated */
-
- shifted_vline = vector_init (vector_count(vline));
- /* use memcpy? */
- for (index = 1; index < vector_length (vline); index++)
- {
- vector_set_index (shifted_vline, index-1, vector_lookup(vline, index));
- }
-
- ret = cmd_describe_command_real (shifted_vline, ENABLE_NODE, status);
-
- vector_free(shifted_vline);
- return ret;
- }
-
- return cmd_describe_command_real (vline, node, status);
-}
-
-/*------------------------------------------------------------------------------
- * 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;
+ return CMD_SUCCESS ; /* will end to parsed->nnode */
}
-/*------------------------------------------------------------------------------
- * Command line completion support.
- */
-static vector
-cmd_complete_command_real (vector vline, 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 vline is empty. */
- if (vector_length (vline) == 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 (vline) - 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 (vline, 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(vline, 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 vline, int node, int *status)
-{
- vector ret;
-
- if ( cmd_try_do_shortcut(node, vector_get_item(vline, 0) ) )
- {
- vector shifted_vline;
- unsigned int index;
-
- /* We can try it on enable node, cos' the vty is authenticated */
-
- shifted_vline = vector_init (vector_count(vline));
- /* use memcpy? */
- for (index = 1; index < vector_length (vline); index++)
- {
- vector_set_index (shifted_vline, index-1, vector_lookup(vline, index));
- }
-
- ret = cmd_complete_command_real (shifted_vline, ENABLE_NODE, status);
-
- vector_free(shifted_vline);
- return ret;
- }
-
- return cmd_complete_command_real (vline, 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;
- }
-}
-
-/*------------------------------------------------------------------------------
- * Initialise a new struct cmd_parsed, allocating if required
- */
-extern cmd_parsed
-cmd_parse_init_new(cmd_parsed parsed)
-{
- if (parsed == NULL)
- parsed = XCALLOC(MTYPE_CMD_PARSED, sizeof(*parsed)) ;
- else
- memset(parsed, 0, sizeof(*parsed)) ;
-
- /* 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)
- *
- * line = zeroised qstring -- empty
- * words = zeroised qstring -- empty
- *
- * vline = zeroised vector -- empty
- *
- * so nothing else to do
- */
-
- return parsed ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Initialise a new struct cmd_parsed, allocating if required
- */
-extern cmd_parsed
-cmd_parse_reset(cmd_parsed parsed, bool free_structure)
-{
- if (parsed != NULL)
- {
- qs_reset_keep(&parsed->words) ;
- vector_reset_keep(&parsed->vline) ;
-
- if (free_structure)
- XFREE(MTYPE_CMD_PARSED, parsed) ; /* sets parsed = NULL */
- else
- cmd_parse_init_new(parsed) ;
- } ;
-
- return parsed ;
-} ;
-
-/*------------------------------------------------------------------------------
- * 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_this(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, enum cmd_parse_type type)
-{
- enum cmd_return_code ret ;
- enum cmd_return_code first_ret ;
- cmd_parsed parsed ;
-
- /* Initialise the parsed structure -- assuming no 'do' */
- if (vty->parsed == NULL)
- vty->parsed = cmd_parse_init_new(NULL) ;
- parsed = vty->parsed ;
-
- parsed->onode = parsed->cnode = vty->node ;
-
- parsed->cmd = NULL ;
- parsed->do_shortcut = 0 ;
-
- /* Parse the line into words -- set up parsed->words and parsed->vline */
- cmd_make_vline(&parsed->vline, &parsed->words, vty->buf) ;
-
- if (vector_length(&parsed->vline) == 0)
- return CMD_EMPTY ; /* NB: parsed->cmd == NULL */
-
- /* 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->cnode, vector_get_item(&parsed->vline, 0)))
- {
- parsed->cnode = ENABLE_NODE ;
- parsed->do_shortcut = 1 ;
- } ;
-
- /* Try in the current node */
- ret = cmd_parse_this(parsed, ((type & cmd_parse_strict) != 0)) ;
-
- 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_this(parsed, ((type & cmd_parse_strict) != 0)) ;
- } ;
- } ;
-
- return vty->parsed->cmd->daemon ? CMD_SUCCESS_DAEMON
- : CMD_SUCCESS ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Work function for cmd_parse_command
- *
- * Takes a parsed structure, with the:
- *
- * cnode -- node to parse in
- * vline -- the line broken into words
- * do_shortcut -- true if first word is 'do' (to be ignored)
- *
- * and parses either strictly or with command completion.
- *
- * If successful, reduces the vline structure down to the variable portions,
- * ie to the argv[] for the command function.
- *
- * 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_this(cmd_parsed parsed, bool strict)
-{
- unsigned int i ;
- unsigned int ivl ;
- unsigned index ;
- unsigned first ;
- unsigned argc ;
- vector cmd_v;
- struct cmd_element *cmd_element;
- struct cmd_element *matched_element;
- unsigned int matched_count, incomplete_count;
- enum match_type match = 0;
- int varflag;
- char *command;
-
- /* Need length of vline, discounting the first entry if required */
- first = parsed->do_shortcut ? 1 : 0 ;
-
- assert(vector_length(&parsed->vline) >= first) ;
- ivl = vector_length(&parsed->vline) - first ;
-
- /* Make copy of command elements. */
- cmd_v = vector_copy (cmd_node_vector (cmdvec, parsed->cnode));
-
- /* Look for an unambiguous result */
- for (index = 0 ; index < ivl; index++)
- {
- int ret ;
-
- command = vector_get_item(&parsed->vline, index + first) ;
- if (command == NULL)
- continue ;
-
- match = strict ? cmd_filter_by_string(command, cmd_v, index)
- : cmd_filter_by_completion(command, cmd_v, index) ;
-
- 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 ;
- } ;
-
- /* Found command -- process the arguments ready for execution */
- varflag = 0 ;
- argc = 0 ;
-
- for (index = 0; index < ivl ; index++)
- {
- int take = varflag ;
-
- if (!varflag)
- {
- vector descvec = vector_get_item (matched_element->strvec, index);
-
- if (vector_length (descvec) == 1)
- {
- struct desc *desc = vector_get_item (descvec, 0);
-
- if (CMD_VARARG (desc->cmd))
- take = varflag = 1 ;
- else
- take = (CMD_VARIABLE (desc->cmd) || CMD_OPTION (desc->cmd)) ;
- }
- else
- take = 1 ;
- }
-
- if (take)
- vector_assign_item(&parsed->vline, argc++, index + first) ;
- } ;
-
- vector_set_length(&parsed->vline, argc) ; /* set to new length */
-
- /* 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, 0) ;
- 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 ;
-} ;
-
-/*----------------------------------------------------------------------------*/
-
-/* Configration from terminal */
-DEFUN_CALL (config_terminal,
- config_terminal_cmd,
- "configure terminal",
- "Configuration from vty interface\n"
- "Configuration terminal\n")
-{
- if (vty_config_lock (vty, CONFIG_NODE))
- return CMD_SUCCESS;
-
- vty_out (vty, "VTY configuration is locked by other VTY%s", VTY_NEWLINE);
- return CMD_WARNING;
-}
-
-/* Enable command */
-DEFUN_CALL (enable,
- config_enable_cmd,
- "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_serv(vty))
- vty_set_node(vty, ENABLE_NODE);
- else
- vty_set_node(vty, AUTH_ENABLE_NODE);
-
- return CMD_SUCCESS;
-}
-
-/* Disable command */
-DEFUN_CALL (disable,
- config_disable_cmd,
- "disable",
- "Turn off privileged mode command\n")
-{
- if (vty_get_node(vty) == ENABLE_NODE)
- vty_set_node(vty, VIEW_NODE);
- return CMD_SUCCESS;
-}
-
-/* Down vty node level. */
-DEFUN_CALL (config_exit,
- config_exit_cmd,
- "exit",
- "Exit current mode and down to previous mode\n")
+/* Show version. */
+DEFUN_CALL (show_version,
+ show_version_cmd,
+ "show version",
+ SHOW_STR
+ "Displays zebra version\n")
{
- return vty_cmd_exit(vty) ;
-}
+ VTY_LOCK() ;
-/* quit is alias of exit. */
-ALIAS_CALL (config_exit,
- config_quit_cmd,
- "quit",
- "Exit current mode and down to previous mode\n")
-
-/* End of configuration. */
-DEFUN_CALL (config_end,
- config_end_cmd,
- "end",
- "End current mode and change to enable mode.")
-{
- return vty_cmd_end(vty) ;
-}
+ uty_out (vty->vio, "Quagga %s (%s).\n", QUAGGA_VERSION,
+ (host.name != NULL) ? host.name : "") ;
+ uty_out (vty->vio, "%s\n", QUAGGA_COPYRIGHT);
-/* Show version. */
-DEFUN_CALL (show_version,
- show_version_cmd,
- "show version",
- SHOW_STR
- "Displays zebra version\n")
-{
- vty_out (vty, "Quagga %s (%s).%s", QUAGGA_VERSION, host.name?host.name:"",
- VTY_NEWLINE);
- vty_out (vty, "%s%s", QUAGGA_COPYRIGHT, VTY_NEWLINE);
+ VTY_UNLOCK() ;
return CMD_SUCCESS;
}
-/* Help display function for all node. */
+/* Help display function for all node. */
DEFUN_CALL (config_help,
config_help_cmd,
"help",
"Description of the interactive help system\n")
{
vty_out (vty,
- "Quagga VTY provides advanced help feature. When you need help,%s\
-anytime at the command line please press '?'.%s\
-%s\
-If nothing matches, the help list will be empty and you must backup%s\
- until entering a '?' shows the available options.%s\
-Two styles of help are provided:%s\
-1. Full help is available when you are ready to enter a%s\
-command argument (e.g. 'show ?') and describes each possible%s\
-argument.%s\
-2. Partial help is provided when an abbreviated argument is entered%s\
- and you want to know what arguments match the input%s\
- (e.g. 'show me?'.)%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
- VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
- VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
+ "Quagga VTY provides advanced help feature. When you need help,\n"
+ "anytime at the command line please press '?'.\n"
+ "\n"
+ "If nothing matches, the help list will be empty and you must backup\n"
+ "until entering a '?' shows the available options.\n"
+ "Two styles of help are provided:\n"
+ " 1. Full help is available when you are ready to enter a\n"
+ " command argument (e.g. 'show ?') and describes each possible\n"
+ " argument.\n"
+ " 2. Partial help is provided when an abbreviated argument is entered\n"
+ " and you want to know what arguments match the input\n"
+ " (e.g. 'show me?'.)\n"
+ "\n") ;
return CMD_SUCCESS;
}
-/* Help display function for all node. */
+/* Help display function for all node. */
DEFUN_CALL (config_list,
- config_list_cmd,
- "list",
- "Print command list\n")
+ config_list_cmd,
+ "list",
+ "Print command list\n")
{
unsigned int i;
- struct cmd_node *cnode = vector_get_item (cmdvec, vty_get_node(vty));
- struct cmd_element *cmd;
-
- for (i = 0; i < vector_length (cnode->cmd_vector); i++)
- if ((cmd = vector_get_item (cnode->cmd_vector, i)) != NULL
- && !(cmd->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
- vty_out (vty, " %s%s", cmd->string,
- VTY_NEWLINE);
+ struct cmd_node *cn ;
+ struct cmd_command *cmd;
+
+ cn = vector_get_item (node_vector, vty->node);
+
+ for (i = 0; i < vector_length (cn->cmd_vector); i++)
+ if ( ((cmd = vector_get_item (cn->cmd_vector, i)) != NULL)
+ && ((cmd->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)) == 0) )
+ vty_out (vty, " %s\n", cmd->string);
+
return CMD_SUCCESS;
}
-/* Write current configuration into file. */
+/* Write current configuration into file. */
DEFUN (config_write_file,
config_write_file_cmd,
"write file",
"Write running configuration to memory, network, or terminal\n"
"Write to configuration file\n")
{
- unsigned int i;
- int fd;
- int err;
- struct cmd_node *node;
- char *config_file;
- char *config_file_tmp = NULL;
- char *config_file_sav = NULL;
- int ret = CMD_WARNING;
-
- /* Check and see if we are operating under vtysh configuration */
- if (host.config == NULL)
+ qpath path ;
+ qpath temp ;
+ qpath save ;
+ cmd_return_code_t ret, retw ;
+ unsigned int i ;
+ int fd, err ;
+ struct cmd_node *cn;
+ const char *config_name ;
+ const char *save_name ;
+ char* temp_name ;
+
+ err = 0 ; /* so far, so good */
+
+ VTY_LOCK() ;
+ path = (host.config_file != NULL) ? qpath_dup(host.config_file) : NULL ;
+ VTY_UNLOCK() ;
+
+ /* Check and see if we are operating under vtysh configuration */
+ if (path == NULL)
{
- vty_out (vty, "Can't save to configuration file, using vtysh.%s",
- VTY_NEWLINE);
+ vty_out (vty, "%% Cannot save to configuration file, using vtysh.\n");
return CMD_WARNING;
}
- /* Get filename. */
- config_file = host.config;
+ /* Set up the file names. */
+ config_name = qpath_string(path) ;
- config_file_sav =
- XMALLOC (MTYPE_TMP, strlen (config_file) + strlen (CONF_BACKUP_EXT) + 1);
- strcpy (config_file_sav, config_file);
- strcat (config_file_sav, CONF_BACKUP_EXT);
+ save = qpath_dup(path) ;
+ qpath_extend_str(save, CONF_BACKUP_EXT) ;
+ save_name = qpath_string(save) ;
+ temp = qpath_dup(path) ;
+ qpath_extend_str(temp, ".XXXXXX") ;
+ temp_name = qpath_char_string(temp) ;
- config_file_tmp = XMALLOC (MTYPE_TMP, strlen (config_file) + 8);
- sprintf (config_file_tmp, "%s.XXXXXX", config_file);
-
- /* Open file to configuration write. */
- fd = mkstemp (config_file_tmp);
+ /* Open file to configuration write. */
+ fd = mkstemp (temp_name);
if (fd < 0)
{
- vty_out (vty, "Can't open configuration file %s.%s", config_file_tmp,
- VTY_NEWLINE);
+ err = errno ;
+ vty_out (vty, "%% Can't open configuration file %s", temp_name) ;
goto finished;
}
- /* Make vty for configuration file. */
+ /* Make vty for configuration file. */
vty_open_config_write(vty, fd) ;
- /* Config file header print. */
vty_out (vty, "!\n! Zebra configuration saved from vty\n! ");
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");
- }
+ retw = CMD_SUCCESS ;
- err = vty_close_config_write(vty) ;
- close(fd) ;
+ for (i = 0; i < vector_length (node_vector); i++)
+ {
+ if ((cn = vector_get_item (node_vector, i)) && cn->config_write)
+ {
+ if ((*cn->config_write) (vty))
+ vty_out (vty, "!\n");
- if (err != 0)
+ retw = vty_cmd_out_push(vty) ; /* Push stuff so far */
+
+ if (retw != CMD_SUCCESS)
+ break ;
+ } ;
+ } ;
+
+ ret = vty_close_config_write(vty, (retw != CMD_SUCCESS)) ;
+
+ if ((ret != CMD_SUCCESS) || (retw != CMD_SUCCESS))
{
- vty_out (vty, "Failed while writing configuration file %s.%s",
- config_file_tmp, VTY_NEWLINE);
+ vty_out (vty, "%% Failed while writing configuration file %s.\n",
+ temp_name) ;
goto finished;
}
- if (unlink (config_file_sav) != 0)
+ /* Now move files around to make .sav and the real file */
+ ret = CMD_WARNING ;
+
+ if (unlink (save_name) != 0)
if (errno != ENOENT)
{
- vty_out (vty, "Can't unlink backup configuration file %s.%s",
- config_file_sav, VTY_NEWLINE);
+ err = errno ;
+ vty_out (vty, "%% Can't unlink backup configuration file %s",
+ save_name) ;
goto finished;
} ;
- if (link (config_file, config_file_sav) != 0)
+ if (link (config_name, save_name) != 0)
{
- vty_out (vty, "Can't backup old configuration file %s.%s",
- config_file_sav, VTY_NEWLINE);
+ err = errno ;
+ vty_out (vty, "%% Can't backup old configuration file %s", config_name) ;
goto finished;
} ;
sync () ;
- if (unlink (config_file) != 0)
+ if (unlink (config_name) != 0)
{
- vty_out (vty, "Can't unlink configuration file %s.%s",
- config_file, VTY_NEWLINE);
+ err = errno ;
+ vty_out (vty, "%% Can't unlink configuration file %s", config_name);
goto finished;
} ;
- if (link (config_file_tmp, config_file) != 0)
+ if (link (temp_name, config_name) != 0)
{
- vty_out (vty, "Can't save configuration file %s.%s",
- config_file, VTY_NEWLINE);
+ err = errno ;
+ vty_out (vty, "%% Can't save configuration file %s", config_name);
goto finished;
} ;
sync ();
- if (chmod (config_file, CONFIGFILE_MASK) != 0)
+ if (chmod (config_name, CONFIGFILE_MASK) != 0)
{
- vty_out (vty, "Can't chmod configuration file %s: %s (%s).\n",
- config_file, errtostr(errno, 0).str, errtoname(errno, 0).str);
+ err = errno ;
+ vty_out (vty, "%% Can't chmod configuration file %s", config_name) ;
goto finished;
}
- vty_out (vty, "Configuration saved to %s\n", config_file);
+ vty_out (vty, "Configuration saved to %s\n", config_name) ;
ret = CMD_SUCCESS;
finished:
- unlink (config_file_tmp);
- XFREE (MTYPE_TMP, config_file_tmp);
- XFREE (MTYPE_TMP, config_file_sav);
+ if (err != 0)
+ vty_out(vty, ": %s (%s).\n", errtostr(errno, 0).str,
+ errtoname(errno, 0).str) ;
+ if (fd >= 0)
+ unlink (temp_name);
+
+ qpath_free(temp) ;
+ qpath_free(save) ;
+ qpath_free(path) ;
+
return ret;
-}
+} ;
ALIAS (config_write_file,
config_write_cmd,
@@ -3166,7 +1208,7 @@ ALIAS (config_write_file,
"Copy running config to... \n"
"Copy running config to startup config (same as write file)\n")
-/* Write current configuration into the terminal. */
+/* Write current configuration into the terminal. */
DEFUN (config_write_terminal,
config_write_terminal_cmd,
"write terminal",
@@ -3174,76 +1216,71 @@ 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;
}
-/* Write current configuration into the terminal. */
+/* Write current configuration into the terminal. */
ALIAS (config_write_terminal,
show_running_config_cmd,
"show running-config",
SHOW_STR
"running configuration\n")
-/* Write startup configuration into the terminal. */
+/* Write startup configuration into the terminal. */
DEFUN (show_startup_config,
show_startup_config_cmd,
"show startup-config",
SHOW_STR
- "Contentes of startup configuration\n")
+ "Contents of startup configuration\n")
{
- char buf[BUFSIZ];
- FILE *confp;
+ cmd_return_code_t ret ;
+ qpath path ;
- confp = fopen (host.config, "r");
- if (confp == NULL)
- {
- vty_out (vty, "Can't open configuration file [%s]%s",
- host.config, VTY_NEWLINE);
- return CMD_WARNING;
- }
+ VTY_LOCK() ;
+ path = (host.config_file != NULL) ? qpath_dup(host.config_file) : NULL ;
+ VTY_UNLOCK() ;
- while (fgets (buf, BUFSIZ, confp))
+ if (path == NULL)
{
- char *cp = buf;
-
- while (*cp != '\r' && *cp != '\n' && *cp != '\0')
- cp++;
- *cp = '\0';
-
- vty_out (vty, "%s%s", buf, VTY_NEWLINE);
- }
+ vty_out (vty, "%% Cannot show configuration file, using vtysh.\n");
+ return CMD_WARNING;
+ } ;
- fclose (confp);
+ ret = vty_cat_file(vty, path, "configuration file") ;
+ qpath_free(path) ;
- return CMD_SUCCESS;
+ return ret ;
}
-/* Hostname configuration */
+/* Hostname configuration */
DEFUN_CALL (config_hostname,
hostname_cmd,
"hostname WORD",
@@ -3252,21 +1289,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,
@@ -3276,26 +1303,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)
{
@@ -3303,47 +1322,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 (host.password)
- XFREE (MTYPE_HOST, host.password);
- host.password = NULL;
+ 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.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]);
+ XFREE (MTYPE_HOST, *p_password);
- return CMD_SUCCESS;
-}
+ *p_encrypted = host.encrypt ;
+ if (*p_encrypted)
+ *p_password = XSTRDUP (MTYPE_HOST, zencrypt (argv[0]));
+ else
+ *p_password = XSTRDUP (MTYPE_HOST, argv[0]);
+ } ;
+ } ;
+
+ 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",
@@ -3352,65 +1387,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,
@@ -3426,14 +1412,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;
}
@@ -3443,24 +1427,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;
}
@@ -3471,19 +1461,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;
}
@@ -3517,6 +1501,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"
@@ -3532,10 +1525,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>]",
@@ -3544,8 +1536,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,
@@ -3556,13 +1547,17 @@ 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;
}
+/*==============================================================================
+ * Logging configuration.
+ */
+
DEFUN_CALL (config_logmsg,
config_logmsg_cmd,
"logmsg "LOG_LEVELS" .MESSAGE",
@@ -3578,8 +1573,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;
}
@@ -3712,54 +1706,34 @@ DEFUN_CALL (no_config_log_monitor,
return CMD_SUCCESS;
}
+/*------------------------------------------------------------------------------
+ * Set new logging file and level -- "log file FILENAME [LEVEL]"
+ *
+ * Note that even if fail to open the new log file, will set host.logfile.
+ *
+ * Failure here is an error.
+ */
static int
set_log_file(struct vty *vty, const char *fname, int loglevel)
{
- int ret;
- char *p = NULL;
- const char *fullpath;
-
- /* Path detection. */
- if (! IS_DIRECTORY_SEP (*fname))
- {
- char cwd[MAXPATHLEN+1];
- cwd[MAXPATHLEN] = '\0';
+ int err ;
- if (getcwd (cwd, MAXPATHLEN) == NULL)
- {
- zlog_err ("config_log_file: Unable to alloc mem!");
- return CMD_WARNING;
- }
-
- if ( (p = XMALLOC (MTYPE_TMP, strlen (cwd) + strlen (fname) + 2))
- == NULL)
- {
- zlog_err ("config_log_file: Unable to alloc mem!");
- return CMD_WARNING;
- }
- sprintf (p, "%s/%s", cwd, fname);
- fullpath = p;
- }
- else
- fullpath = fname;
-
- ret = zlog_set_file (NULL, fullpath, loglevel);
-
- if (p)
- XFREE (MTYPE_TMP, p);
+ VTY_LOCK() ;
- if (!ret)
- {
- vty_out (vty, "can't open logfile %s\n", fname);
- return CMD_WARNING;
- }
+ host.logfile = uty_cmd_path_name_complete(host.logfile, fname,
+ vty->exec->context) ;
+ err = zlog_set_file (NULL, qpath_string(host.logfile), loglevel) ;
- if (host.logfile)
- XFREE (MTYPE_HOST, host.logfile);
+ VTY_UNLOCK() ;
- host.logfile = XSTRDUP (MTYPE_HOST, fname);
+ if (err == 0)
+ return CMD_SUCCESS ;
- return CMD_SUCCESS;
+ vty_out(vty, "%% failed to open log file %s: %s (%s)\n",
+ qpath_string(host.logfile),
+ errtostr(err, 0).str,
+ errtoname(err, 0).str) ;
+ return CMD_WARNING ;
}
DEFUN_CALL (config_log_file,
@@ -3774,7 +1748,7 @@ DEFUN_CALL (config_log_file,
DEFUN_CALL (config_log_file_level,
config_log_file_level_cmd,
- "log file FILENAME "LOG_LEVELS,
+ "log file FILENAME " LOG_LEVELS,
"Logging control\n"
"Logging to file\n"
"Logging filename\n"
@@ -3784,6 +1758,7 @@ DEFUN_CALL (config_log_file_level,
if ((level = level_match(argv[1])) == ZLOG_DISABLED)
return CMD_ERR_NO_MATCH;
+
return set_log_file(vty, argv[0], level);
}
@@ -3795,13 +1770,13 @@ DEFUN_CALL (no_config_log_file,
"Cancel logging to file\n"
"Logging file name\n")
{
- zlog_reset_file (NULL);
+ VTY_LOCK() ;
- if (host.logfile)
- XFREE (MTYPE_HOST, host.logfile);
+ zlog_reset_file (NULL);
- host.logfile = NULL;
+ host.logfile = qpath_free(host.logfile) ;
+ VTY_UNLOCK() ;
return CMD_SUCCESS;
}
@@ -3989,20 +1964,43 @@ DEFUN_CALL (no_config_log_timestamp_precision,
return CMD_SUCCESS;
}
+/*==============================================================================
+ * MOTD commands and set up.
+ *
+ * Note that can set a MOTD file that does not exist at the time. A friendly
+ * message warns about this, but it is not an error. The message will not be
+ * seen while reading the configuration file -- but it is not worth stopping
+ * the configuration file reader for this !
+ */
DEFUN_CALL (banner_motd_file,
banner_motd_file_cmd,
- "banner motd file [FILE]",
+ "banner motd file FILE",
"Set banner\n"
"Banner for motd\n"
"Banner from a file\n"
"Filename\n")
{
- if (host.motdfile)
- XFREE (MTYPE_HOST, host.motdfile);
- host.motdfile = XSTRDUP (MTYPE_HOST, argv[0]);
+ int err ;
- return CMD_SUCCESS;
-}
+ VTY_LOCK() ;
+
+ host.motdfile = uty_cmd_path_name_complete(host.motdfile,
+ argv[0], vty->exec->context) ;
+ err = qpath_stat_is_file(host.motdfile) ;
+
+ if (err != 0)
+ {
+ vty_out(vty, "NB: '%s': ", qpath_string(host.motdfile)) ;
+ if (err < 0)
+ vty_out(vty, "is not a file\n") ;
+ else
+ vty_out(vty, "%s (%s)\n", errtostr(err, 0).str,
+ errtoname(err, 0).str) ;
+ } ;
+
+ VTY_UNLOCK() ;
+ return CMD_SUCCESS ;
+} ;
DEFUN_CALL (banner_motd_default,
banner_motd_default_cmd,
@@ -4011,7 +2009,11 @@ DEFUN_CALL (banner_motd_default,
"Strings for motd\n"
"Default string\n")
{
- host.motd = default_motd;
+ VTY_LOCK() ;
+
+ host.motd = default_motd ;
+
+ VTY_UNLOCK() ;
return CMD_SUCCESS;
}
@@ -4022,23 +2024,114 @@ DEFUN_CALL (no_banner_motd,
"Set banner string\n"
"Strings for motd\n")
{
- host.motd = NULL;
- if (host.motdfile)
- XFREE (MTYPE_HOST, host.motdfile);
- host.motdfile = NULL;
+ VTY_LOCK() ;
+
+ host.motd = NULL ;
+ host.motdfile = qpath_free(host.motdfile) ;
+
+ VTY_UNLOCK() ;
return CMD_SUCCESS;
}
-/* Set config filename. Called from vty.c */
-void
-host_config_set (char *filename)
+/*==============================================================================
+ * Current directory handling
+ */
+DEFUN_CALL (do_chdir,
+ chdir_cmd,
+ "chdir DIR",
+ "Set current directory\n"
+ "Directory to set\n")
+{
+ cmd_return_code_t ret ;
+ qpath path ;
+ int err ;
+
+ ret = CMD_SUCCESS ;
+
+ path = uty_cmd_path_name_complete(NULL, argv[0], vty->exec->context) ;
+ err = qpath_stat_is_directory(path) ;
+
+ if (err == 0)
+ qpath_copy(vty->exec->context->dir_cd, path) ;
+ else
+ {
+ vty_out(vty, "%% chdir %s: ", qpath_string(path)) ;
+ if (err < 0)
+ vty_out(vty, "is not a directory\n") ;
+ else
+ vty_out(vty, "%s (%s)\n", errtostr(err, 0).str,
+ errtoname(err, 0).str) ;
+ ret = CMD_WARNING ;
+ } ;
+
+ qpath_free(path) ;
+ return ret ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get cwd.
+ *
+ * This is done very early in the morning, before lowering privileges, to
+ * minimise chance of not being able to get the cwd. If cwd itself is not
+ * accessible in lowered privilege state, that will later become clear.
+ *
+ * Sets host.cwd, which is torn down in cmd_terminate().
+ *
+ */
+extern void
+cmd_getcwd(void)
{
- if (host.config)
- XFREE (MTYPE_HOST, host.config);
- host.config = XSTRDUP (MTYPE_HOST, filename);
+ host.cwd = qpath_getcwd(NULL) ;
+
+ if (host.cwd == NULL)
+ {
+ fprintf(stderr, "Cannot getcwd()\n") ;
+ exit(1) ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set host.config_file and host.config_dir.
+ */
+extern void
+cmd_host_config_set (qpath config_file)
+{
+ VTY_LOCK() ;
+
+ host.config_file = qpath_copy(host.config_file, config_file) ;
+ host.config_dir = qpath_copy(host.config_dir, config_file) ;
+
+ qpath_shave(host.config_dir) ;
+
+ VTY_UNLOCK() ;
}
-void
+/*------------------------------------------------------------------------------
+ * Set the lexical level for further command processing.
+ */
+DEFUN_CALL (lexical_level,
+ lexical_level_cmd,
+ "lexical-level <0-1>",
+ "Set lexical level\n"
+ "The required lexical level\n")
+{
+ int level ;
+
+ level = strtol(argv[0], NULL, 0) ;
+
+ vty_cmd_set_full_lex(vty, (level != 0)) ;
+
+ return CMD_SUCCESS;
+}
+
+/*==============================================================================
+ * Command handling initialisation and termination.
+ */
+
+/*------------------------------------------------------------------------------
+ * Install copy of the default commands in the given node.
+ */
+extern void
install_default (enum node_type node)
{
install_element (node, &config_exit_cmd);
@@ -4054,26 +2147,31 @@ install_default (enum node_type node)
install_element (node, &show_running_config_cmd);
}
-/* Initialize command interface. Install basic nodes and commands. */
-void
-cmd_init (int terminal)
+/*------------------------------------------------------------------------------
+ * Initialise command handling.
+ *
+ * Install basic nodes and commands. Initialise the host structure.
+ *
+ * Sets srand(time(NULL))
+ */
+extern void
+cmd_init (bool terminal)
{
- command_cr = XSTRDUP(MTYPE_STRVEC, "<cr>");
- desc_cr.cmd = command_cr;
- desc_cr.str = XSTRDUP(MTYPE_STRVEC, "");
-
- /* Allocate initial top vector of commands. */
- cmdvec = vector_init (0);
-
- /* 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;
+ srand(time(NULL)) ;
+
+ if (host.cwd == NULL) /* in case cmd_cwd() not called, yet */
+ cmd_getcwd() ;
+
+ /* Allocate initial top vector of commands. */
+ node_vector = vector_init(0);
+
+ /* Set default motd */
+ host.motd = default_motd ;
+ host.config_brand = rand() ;
+
+ /* Default host value settings are already set, see above */
+
+ cmd_get_sys_host_name() ; /* start with system name & name_gen == 1 */
/* Install top nodes. */
install_node (&view_node, NULL);
@@ -4092,30 +2190,41 @@ cmd_init (int terminal)
install_element (VIEW_NODE, &config_quit_cmd);
install_element (VIEW_NODE, &config_help_cmd);
install_element (VIEW_NODE, &config_enable_cmd);
+ install_element (VIEW_NODE, &config_enable_configure_cmd);
+ install_element (VIEW_NODE, &config_terminal_cmd);
install_element (VIEW_NODE, &config_terminal_length_cmd);
install_element (VIEW_NODE, &config_terminal_no_length_cmd);
install_element (VIEW_NODE, &show_logging_cmd);
install_element (VIEW_NODE, &echo_cmd);
+ install_element (VIEW_NODE, &chdir_cmd);
install_element (RESTRICTED_NODE, &config_list_cmd);
install_element (RESTRICTED_NODE, &config_exit_cmd);
install_element (RESTRICTED_NODE, &config_quit_cmd);
install_element (RESTRICTED_NODE, &config_help_cmd);
install_element (RESTRICTED_NODE, &config_enable_cmd);
+ install_element (RESTRICTED_NODE, &config_enable_configure_cmd);
+ install_element (RESTRICTED_NODE, &config_terminal_cmd);
install_element (RESTRICTED_NODE, &config_terminal_length_cmd);
install_element (RESTRICTED_NODE, &config_terminal_no_length_cmd);
install_element (RESTRICTED_NODE, &echo_cmd);
+ install_element (RESTRICTED_NODE, &chdir_cmd);
}
if (terminal)
{
install_default (ENABLE_NODE);
install_element (ENABLE_NODE, &config_disable_cmd);
+ install_element (ENABLE_NODE, &config_enable_cmd);
+ install_element (ENABLE_NODE, &config_enable_configure_cmd);
install_element (ENABLE_NODE, &config_terminal_cmd);
install_element (ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
}
+
install_element (ENABLE_NODE, &show_startup_config_cmd);
install_element (ENABLE_NODE, &show_version_cmd);
+ install_element (ENABLE_NODE, &lexical_level_cmd);
+ install_element (ENABLE_NODE, &chdir_cmd);
if (terminal)
{
@@ -4130,9 +2239,12 @@ cmd_init (int terminal)
install_element (CONFIG_NODE, &hostname_cmd);
install_element (CONFIG_NODE, &no_hostname_cmd);
+ install_element (CONFIG_NODE, &lexical_level_cmd);
if (terminal)
{
+ install_element (CONFIG_NODE, &echo_cmd);
+
install_element (CONFIG_NODE, &password_cmd);
install_element (CONFIG_NODE, &password_text_cmd);
install_element (CONFIG_NODE, &enable_password_cmd);
@@ -4170,97 +2282,72 @@ cmd_init (int terminal)
install_element (CONFIG_NODE, &service_terminal_length_cmd);
install_element (CONFIG_NODE, &no_service_terminal_length_cmd);
+ install_element (RESTRICTED_NODE, &show_thread_cpu_cmd);
install_element (VIEW_NODE, &show_thread_cpu_cmd);
install_element (ENABLE_NODE, &show_thread_cpu_cmd);
- install_element (RESTRICTED_NODE, &show_thread_cpu_cmd);
-
+
install_element (ENABLE_NODE, &clear_thread_cpu_cmd);
install_element (VIEW_NODE, &show_work_queues_cmd);
install_element (ENABLE_NODE, &show_work_queues_cmd);
- }
- srand(time(NULL));
-}
+ } ;
+} ;
+/*------------------------------------------------------------------------------
+ * Close down command interface.
+ *
+ * Dismantle the node_vector and all commands.
+ *
+ * Clear out the host structure.
+ */
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 ;
- if (cmdvec)
+ /* Ream out the vector of command nodes. */
+ while ((cmd_node = vector_ream(node_vector, free_it)) != NULL)
{
- for (i = 0; i < vector_length (cmdvec); i++)
+ /* Ream out the (embedded) vector of commands per node. */
+ while ((cmd = vector_ream(cmd_node->cmd_vector, keep_it)) != NULL)
{
- cmd_node = vector_get_item (cmdvec, i) ;
- if (cmd_node == NULL)
- continue ;
-
- cmd_node_v = cmd_node->cmd_vector;
+ /* Ream out the vector of items for each command.
+ *
+ * Note that each cmd is a static structure, which may appear in
+ * more than one cmd_vector -- but the "compiled" portions are
+ * dynamically allocated.
+ */
+ 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);
+ host.logfile = qpath_free(host.logfile) ;
+ host.motdfile = qpath_free(host.motdfile) ;
+ host.config_file = qpath_free(host.config_file) ;
+ host.config_dir = qpath_free(host.config_dir) ;
+ XFREE(MTYPE_HOST, host.vty_accesslist_name);
+ XFREE(MTYPE_HOST, host.vty_ipv6_accesslist_name);
+ host.cwd = qpath_free(host.cwd) ;
+} ;
diff --git a/lib/command.h b/lib/command.h
index ba7c4bb2..3ad758d5 100644
--- a/lib/command.h
+++ b/lib/command.h
@@ -23,204 +23,44 @@
#ifndef _ZEBRA_COMMAND_H
#define _ZEBRA_COMMAND_H
-#include <stdbool.h>
+#include "misc.h"
+
+#include "command_common.h"
+#include "vty.h"
-#include "node_type.h"
#include "vector.h"
#include "qstring.h"
-#ifndef Inline
-#define Inline static inline
-#endif
-
-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
-{
- /* 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_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
-} ;
-
-#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; /* Pointing out each description vector. */
- 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. */
-};
-
-/* Command parsing options */
-enum cmd_parse_type /* bit significant */
-{
- cmd_parse_completion = 0x00,
- cmd_parse_strict = 0x01,
-
- cmd_parse_do = 0x02,
- cmd_parse_tree = 0x04,
-} ;
-
-/* Parsed command */
-typedef struct cmd_parsed* cmd_parsed ;
-struct cmd_parsed
-{
- struct cmd_element *cmd ; /* NULL if empty command
- or fails to parse */
-
- enum node_type cnode ; /* node command is in */
- enum node_type onode ; /* node the parser started in */
-
- bool do_shortcut ; /* true => is "do" command */
-
- qstring_t words ; /* the words, '\0' separated */
-
- vector_t vline ; /* pointers to the words */
-} ;
-
-
-/* Command dispatch options */
-enum {
- cmd_no_queue = true,
- cmd_may_queue = false,
-} ;
-
/*------------------------------------------------------------------------------
- * 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, \
+ } ;
+
+/* Legacy name for cmd_command */
+#define cmd_element cmd_command
#define DEFUN_CMD_FUNC_DECL(funcname) \
static cmd_function funcname;
@@ -228,50 +68,52 @@ enum {
#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) \
@@ -280,44 +122,34 @@ enum {
#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"
@@ -375,26 +207,35 @@ enum {
#endif /* HAVE_IPV6 */
/* Prototypes. */
-extern void cmd_init (int);
+extern void cmd_getcwd(void) ;
+extern void cmd_init (bool);
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;
-#ifdef QDEBUG
extern const char *debug_banner ;
-#endif
#endif /* _ZEBRA_COMMAND_H */
diff --git a/lib/command_common.h b/lib/command_common.h
new file mode 100644
index 00000000..26f2909f
--- /dev/null
+++ b/lib/command_common.h
@@ -0,0 +1,288 @@
+/* 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_COMMAND_COMMON_H
+#define _ZEBRA_COMMAND_COMMON_H
+
+#include "misc.h"
+#include "vector.h"
+
+/*------------------------------------------------------------------------------
+ * These are the command levels/contexts.
+ *
+ * NB: this is the order in which configuration is written to the
+ * configuration file.
+ */
+enum node_type
+{
+ NULL_NODE = 0, /* For when need "not a node" */
+
+ AUTH_NODE, /* VTY login -> VIEW_NODE */
+ RESTRICTED_NODE, /* if no login required, may use this node */
+ VIEW_NODE, /* aka user EXEC */
+ AUTH_ENABLE_NODE, /* enable login -> ENABLE_NODE/CONFIG_NODE */
+ ENABLE_NODE, /* aka privileged EXEC */
+
+ 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, /* aka global configuration mode */
+
+ MIN_CONFIG_NODE = CONFIG_NODE,
+ /* May not change context to any node lower */
+
+ SERVICE_NODE, /* unused ! */
+ DEBUG_NODE, /* debug config write only */
+ AAA_NODE, /* unused ! */
+ KEYCHAIN_NODE, /* see: keychain.c */
+ KEYCHAIN_KEY_NODE, /* see: keychain.c -- child of KEYCHAIN_NODE */
+ INTERFACE_NODE, /* interface commands */
+ ZEBRA_NODE, /* router zebra commands */
+ TABLE_NODE, /* rtm_table config write -- see zserv.c */
+ RIP_NODE, /* router rip commands */
+ RIPNG_NODE, /* router ripng commands */
+ BGP_NODE, /* router bgp commands */
+ BGP_VPNV4_NODE, /* address-family vpnv4 -- child of BGP_NODE */
+ BGP_IPV4_NODE, /* address-family ipv4 (unicast) -- child of BGP_NODE */
+ BGP_IPV4M_NODE, /* address-family ipv4 multicast -- child of BGP_NODE */
+ BGP_IPV6_NODE, /* address-family ipv6 (unicast) -- child of BGP_NODE */
+ BGP_IPV6M_NODE, /* address-family ipv6 multicast -- child of BGP_NODE */
+ OSPF_NODE, /* router ospf commands */
+ OSPF6_NODE, /* router ospf6 commands */
+ ISIS_NODE, /* router isis commands */
+ MASC_NODE, /* unused ! RFC 2909 Multicast Address-Set Claim */
+ IRDP_NODE, /* unused ! ICMP Router Discovery Protocol */
+ IP_NODE, /* zebra_ip_config write only -- see zebra_vty.c */
+ ACCESS_NODE, /* access list config write only -- see filter.c */
+ PREFIX_NODE, /* prefix list config write only -- see plist.c */
+ ACCESS_IPV6_NODE, /* access list config write only -- see filter.c */
+ PREFIX_IPV6_NODE, /* prefix list config write only -- see plist.c */
+ AS_LIST_NODE, /* AS list config write only -- see bgp_filter.c */
+ COMMUNITY_LIST_NODE, /* Community list config write only -- see bgp_vty.c */
+ RMAP_NODE, /* route-map commands */
+ SMUX_NODE, /* SNMP config write only -- see smux.c */
+ DUMP_NODE, /* BGP dump config write only -- see bgp_dump.c */
+ FORWARDING_NODE, /* forwarding config write -- see zserv.c */
+ PROTOCOL_NODE, /* protocol config write -- see zebra_vty.c */
+ VTY_NODE, /* line vty commands */
+
+ MAX_PLUS_1_NODE,
+ MAX_NODE = MAX_PLUS_1_NODE - 1
+} ;
+typedef enum node_type node_type_t ;
+
+/*------------------------------------------------------------------------------
+ * Return values for command handling.
+ *
+ * NB: when a command is executed it may return CMD_SUCCESS, CMD_WARNING
+ * or CMD_ERROR.
+ *
+ * In all cases any output required (including any warning or error
+ * messages) must already have been output.
+ *
+ * CMD_WARNING will stop configuration reader, unless ignore warning
+ * option is set.
+ *
+ * CMD_ERROR will always stop the configuration reader.
+ *
+ * If there is no output and either CMD_WARNING or CMD_ERROR, then will
+ * output a general warning message.
+ *
+ * All other return codes are for use within the command handler.
+ */
+enum cmd_return_code
+{
+ CMD_SUCCESS = 0, /* used generally */
+
+ /* Return codes suitable for command execution functions */
+
+ CMD_WARNING = 1,
+ CMD_ERROR,
+
+ /* Return codes from the command parser */
+
+ CMD_EMPTY, /* parser: nothing to execute */
+
+ CMD_ERR_PARSING, /* parser: general parser error */
+ CMD_ERR_NO_MATCH, /* parser: command/argument not recognised */
+ CMD_ERR_AMBIGUOUS, /* parser: more than on command matches */
+ CMD_ERR_INCOMPLETE,
+
+ CMD_CLOSE, /* command: used by "exit" */
+
+
+
+ CMD_WAITING, /* I/O: waiting for more input */
+ CMD_EOF, /* I/O: nothing more to come */
+
+ CMD_HIATUS, /* Something requires attention */
+
+ CMD_IO_ERROR, /* I/O -- failed :-( */
+ CMD_IO_TIMEOUT, /* I/O -- timed out :-( */
+
+ /* For the chop ???? */
+
+
+ CMD_COMPLETE_FULL_MATCH, /* cmd_completion returns */
+ CMD_COMPLETE_MATCH,
+ CMD_COMPLETE_LIST_MATCH,
+ CMD_COMPLETE_ALREADY,
+
+
+ CMD_SUCCESS_DAEMON, /* parser: success & command is for vtysh ? */
+} ;
+
+typedef enum cmd_return_code cmd_return_code_t ;
+
+/*------------------------------------------------------------------------------
+ * Structure for each node -- root of all commands for the node.
+ *
+ * See install_node().
+ */
+struct vty ; /* Forward reference */
+
+struct cmd_node
+{
+ node_type_t node ; /* who we are */
+
+ const char* prompt ; /* prompt string for vty */
+
+ bool config_to_vtysh ; /* configuration goes to vtysh ? */
+
+ node_type_t parent ; /* parent when parsing commands */
+ node_type_t exit_to ; /* where to go on "exit" */
+ node_type_t end_to ; /* where to go on "end", "^C" or "^Z" */
+
+ int (*config_write) (struct vty*) ; /* configuration write function */
+
+ vector_t cmd_vector; /* Vector of this node's commands. */
+} ;
+
+typedef struct cmd_node cmd_node_t ;
+typedef struct cmd_node* cmd_node ;
+
+/*------------------------------------------------------------------------------
+ * Commands -- contents of the nodes' cmd_vector(s).
+ *
+ * A cmd_command is a static structure, which contains dynamic elements
+ * which are set when a command is installed. Note that is not uncommon for
+ * one cmd_command to appear in more than one node.
+ *
+ * The command attributes affect:
+ *
+ * * the parsing of the command -- in particular how the next_node is
+ * established.
+ *
+ * * whether the command is shown in help (or some forms of help if
+ * deprecated.
+ *
+ * * whether the command can be executed directly in the cli thread
+ * (avoiding having to wait for the cmd thread's attention -- this may be
+ * less useful now that all commands are treated a "priority" messages
+ * going into the cmd thread).
+ *
+ * If the command is marked CMD_ATTR_NODE, then the CMD_ATTR_MASK will
+ * extract the node_type_t that the command will set, if CMD_SUCCESS. This
+ * means that can parse commands without executing them.
+ *
+ * If the command is not marked CMD_ATTR_NODE, then the CMD_ATTR_MASK will
+ * extract the cmd_special_t value for the command -- which will be
+ * interesting if it isn't cmd_sp_simple.
+ */
+enum cmd_attr
+{
+ CMD_ATTR_SIMPLE = 0, /* bit significant */
+
+ CMD_ATTR_NODE = BIT(7), /* sets given node */
+ CMD_ATTR_MASK = CMD_ATTR_NODE - 1,
+
+ CMD_ATTR_DEPRECATED = BIT(12),
+ CMD_ATTR_HIDDEN = BIT(13), /* not shown in help */
+ CMD_ATTR_DIRECT = BIT(14), /* can run in cli thread */
+};
+typedef enum cmd_attr cmd_attr_t ;
+
+CONFIRM(CMD_ATTR_MASK >= (cmd_attr_t)MAX_NODE) ;
+
+/* Special commands, which require extra processing at parse time. */
+enum cmd_special
+{
+ cmd_sp_simple = 0,
+
+ cmd_sp_end,
+ cmd_sp_exit,
+ cmd_sp_enable,
+ cmd_sp_configure,
+
+ cmd_sp_max_plus_1,
+ cmd_sp_max = cmd_sp_max_plus_1 - 1
+} ;
+typedef enum cmd_special cmd_special_t ;
+
+CONFIRM(CMD_ATTR_MASK >= (cmd_attr_t)cmd_sp_max) ;
+
+/* Command functions and macros to define same */
+
+struct cmd_command ;
+typedef struct cmd_command* cmd_command ;
+
+typedef const char* const argv_t[] ;
+
+#define DEFUN_CMD_ARG_UNUSED __attribute__ ((unused))
+#define DEFUN_CMD_FUNCTION(name) \
+ enum cmd_return_code name (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)
+
+typedef DEFUN_CMD_FUNCTION((cmd_function)) ;
+
+/* The cmd_command structure itself */
+
+struct cmd_item ; /* Defined in command_parse.h */
+
+struct cmd_command
+{
+ 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. */
+ cmd_attr_t attr ; /* Command attributes */
+
+ vector items ; /* Vector of pointers to cmd_item(s) */
+
+ uint nt_min ; /* excluding [option](s) */
+ uint nt ; /* count of all items */
+ uint nt_max ; /* "infinite" if .vararg */
+
+ struct cmd_item* vararg ; /* if there is a vararg item */
+
+ char* r_string ;
+ char* r_doc ;
+
+//char* config ; /* Configuration string */
+//vector subconfig ; /* Sub configuration string */
+};
+
+#endif /* _ZEBRA_COMMAND_COMMON_H */
diff --git a/lib/command_execute.c b/lib/command_execute.c
new file mode 100644
index 00000000..32698d94
--- /dev/null
+++ b/lib/command_execute.c
@@ -0,0 +1,598 @@
+/* Command Line Execution
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * Recast and extended: Copyright (C) 2010 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify
+ * it 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.
+ */
+
+#include "misc.h"
+
+#include "command_local.h"
+#include "command_parse.h"
+#include "command_execute.h"
+#include "command_queue.h"
+#include "vty_common.h"
+#include "vty_command.h"
+#include "memory.h"
+
+/*==============================================================================
+ * Construct and destroy cmd_exec object.
+ *
+ *
+ */
+
+/*------------------------------------------------------------------------------
+ * Construct cmd_exec object and initialise.
+ *
+ * This is done when a command loop is entered.
+ *
+ * The initial cmd_context reflects the vty->type and initial vty->node.
+ */
+extern cmd_exec
+cmd_exec_new(vty vty)
+{
+ cmd_exec exec ;
+ cmd_context context ;
+
+ exec = XCALLOC(MTYPE_CMD_EXEC, sizeof(struct cmd_exec)) ;
+
+ /* Zeroising has set:
+ *
+ * vty = X -- set below
+ *
+ * action = all zeros
+ *
+ * context = NULL -- see below
+ *
+ * parsed = NULL -- see below
+ *
+ * password_failures = 0 -- none, yet
+ *
+ * state = exec_null
+ * locus = NULL -- not significant in exec_null
+ * ret = CMD_SUCCESS
+ *
+ * cq = NULL -- no mqb (qpthreads)
+ * -- no thread (legacy thread)
+ */
+ confirm(CMD_ACTION_ALL_ZEROS) ; /* action */
+ confirm(exec_null == 0) ; /* state */
+ confirm(CMD_SUCCESS == 0) ; /* ret */
+
+ exec->vty = vty ;
+
+ exec->parsed = cmd_parsed_new() ;
+
+ /* Initialise the context */
+ VTY_ASSERT_LOCKED() ;
+
+ exec->context = context = cmd_context_new() ;
+
+ context->node = vty->node ;
+
+ context->parse_execution = true ;
+ context->parse_only = false ;
+ context->reflect_enabled = false ;
+
+ context->onode = NULL_NODE ;
+
+ context->dir_cd = qpath_dup(host.cwd) ;
+ context->dir_home = qpath_dup(host.config_dir) ;
+
+ switch (vty->type)
+ {
+ case VTY_TERMINAL:
+ context->full_lex = true ;
+
+ context->parse_strict = false ;
+ context->parse_no_do = false ;
+ context->parse_no_tree = false ;
+
+ context->can_enable = context->node >= ENABLE_NODE ;
+ context->can_auth_enable = true ;
+
+ context->dir_here = qpath_dup(context->dir_cd) ;
+
+ break ;
+
+ case VTY_SHELL_SERVER:
+ zabort("missing VTY_SHELL_SERVER context") ;
+
+ context->full_lex = true ;
+
+ context->parse_strict = false ;
+ context->parse_no_do = false ;
+ context->parse_no_tree = false ;
+
+ context->can_enable = true ;
+ context->can_auth_enable = false ;
+
+ context->dir_here = qpath_dup(context->dir_cd) ;
+
+ break ;
+
+ case VTY_CONFIG_READ:
+ context->full_lex = false ;
+
+ context->parse_strict = true ;
+ context->parse_no_do = true ;
+ context->parse_no_tree = false ;
+
+ context->can_enable = true ;
+ context->can_auth_enable = false ;
+
+ context->dir_here = qpath_dup(context->dir_home) ;
+
+ break ;
+
+ default:
+ zabort("vty type unknown to cmd_exec_new()") ;
+ } ;
+
+ return exec ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Make a new context -- returns a completely empty context object.
+ */
+extern cmd_context
+cmd_context_new(void)
+{
+ return XCALLOC(MTYPE_CMD_EXEC, sizeof(struct cmd_context)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Save current context and update for new, child vin.
+ *
+ * - updates the dir_here if required
+ *
+ * - cannot inherit can_enable unless is ENABLE_NODE or better
+ *
+ * - cannot inherit can_auth_enable, no how
+ */
+extern cmd_context
+cmd_context_new_save(cmd_context context, qpath file_here)
+{
+ cmd_context saved ;
+
+ saved = cmd_context_new() ;
+
+ *saved = *context ; /* copy as is */
+
+ /* The saved copy of the context now owns the current paths, so now need
+ * to duplicate (or set new) paths.
+ */
+ context->dir_cd = qpath_dup(saved->dir_cd) ;
+ context->dir_home = qpath_dup(saved->dir_home) ;
+
+ if (file_here == NULL)
+ context->dir_here = qpath_dup(saved->dir_here) ;
+ else
+ {
+ context->dir_here = qpath_dup(file_here) ;
+ qpath_shave(context->dir_here) ;
+ } ;
+
+ /* The inheritance of can_enable is tricky. Will not bequeath can_enable
+ * is is not already in ENABLE_NODE or better !
+ */
+ if (context->node <= ENABLE_NODE)
+ context->can_enable = false ;
+
+ context->can_auth_enable = false ;
+
+ return saved ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Restore given context -- frees the copy restored from.
+ *
+ * Has to free the directories in the context being restored to.
+ *
+ * Returns NULL.
+ */
+extern cmd_context
+cmd_context_restore(cmd_context dst, cmd_context src)
+{
+ assert(src != NULL) ;
+
+ qpath_free(dst->dir_cd) ;
+ qpath_free(dst->dir_home) ;
+ qpath_free(dst->dir_here) ;
+
+ *dst = *src ; /* copy as is */
+
+ return cmd_context_free(src, true) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Free the given context.
+ *
+ * If the context is a copy of an existing context, then must not free the
+ * directories -- they will be freed when that existing context is freed.
+ */
+extern cmd_context
+cmd_context_free(cmd_context context, bool copy)
+{
+ if (context != NULL)
+ {
+ if (!copy)
+ {
+ qpath_free(context->dir_cd) ;
+ qpath_free(context->dir_home) ;
+ qpath_free(context->dir_here) ;
+ } ;
+
+ XFREE(MTYPE_CMD_EXEC, context) ; /* sets context = NULL */
+ } ;
+
+ return context ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Destroy cmd_exec object.
+ */
+extern cmd_exec
+cmd_exec_free(cmd_exec exec)
+{
+ if (exec != NULL)
+ {
+ exec->parsed = cmd_parsed_free(exec->parsed) ;
+ exec->context = cmd_context_free(exec->context, false) ; /* not a copy */
+
+ XFREE(MTYPE_CMD_EXEC, exec) ;
+ } ;
+
+ return NULL ;
+} ;
+
+/*==============================================================================
+ *
+ */
+/*------------------------------------------------------------------------------
+ * Parse and execute a command.
+ *
+ * The command is given by vty->buf and vty->node.
+ *
+ * Uses vty->parsed.
+ *
+ * -- use exact/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.
+ */
+#if 0
+extern enum cmd_return_code
+cmd_execute_command(struct vty *vty,
+ cmd_parse_type_t type, struct cmd_command **cmd)
+{
+ enum cmd_return_code ret ;
+
+ /* Try to parse in vty->node or, if required, ancestors thereof. */
+ ret = cmd_parse_command(vty->exec->parsed, vty->line, vty->node, type) ;
+
+ if (cmd != NULL)
+ *cmd = vty->exec->parsed->cmd ; /* for vtysh */
+
+ if (ret == CMD_SUCCESS)
+ ret = cmd_dispatch(vty, cmd_may_queue) ;
+
+ return ret ;
+} ;
+#endif
+
+/*------------------------------------------------------------------------------
+ * 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 or I/O operation
+ *
+ * when returns, the entire vin/vout stack will have been closed.
+ *
+ * If reaches EOF on the config file, returns CMD_SUCCESS. If anything else
+ * happens, will generate an error message and exits the command loop.
+ */
+extern cmd_return_code_t
+cmd_read_config(struct vty *vty, cmd_command first_cmd, bool ignore_warning)
+{
+ cmd_exec exec = vty->exec ;
+ cmd_parsed parsed = exec->parsed ;
+ cmd_return_code_t ret;
+
+ ret = CMD_SUCCESS ; /* so far, so good */
+
+ while (1)
+ {
+ /* Deal with anything which is not success !
+ */
+ if ((ret != CMD_SUCCESS) && (ret != CMD_EMPTY))
+ {
+ /* Will drop straight out of the loop if have anything other
+ * than CMD_HIATUS, CMD_EOF or CMD_CLOSE, which are all signals
+ * that some adjustment to the vin/vout stacks is required,
+ * or that we are all done here.
+ *
+ * Everything else is deemed to be an error that stops the
+ * command loop.
+ */
+ if ((ret != CMD_HIATUS) && (ret != CMD_EOF) && (ret != CMD_CLOSE))
+ break ;
+
+ ret = vty_cmd_hiatus(vty, ret) ;
+ /* for CMD_EOF & CMD_HIATUS only */
+
+ if (ret == CMD_EOF)
+ return CMD_SUCCESS ; /* eof on the config file is
+ the expected outcome ! */
+ if (ret != CMD_SUCCESS)
+ break ;
+ } ;
+
+ /* If all is well, need another command line */
+
+ ret = vty_cmd_fetch_line(vty) ; /* sets exec->action */
+ if (ret != CMD_SUCCESS)
+ continue ;
+
+ /* Parse the command line we now have */
+ assert(exec->action->to_do == cmd_do_command) ;
+
+ cmd_tokenize(parsed, exec->action->line, exec->context->full_lex) ;
+ ret = cmd_parse_command(parsed, exec->context) ;
+
+ if (ret != CMD_SUCCESS)
+ continue ;
+
+ /* Special handling before first active line. */
+ if (first_cmd != NULL)
+ {
+ if (first_cmd != parsed->cmd)
+ {
+ ret = (*first_cmd->func)(first_cmd, vty, -1, NULL) ;
+ if (ret != CMD_SUCCESS)
+ continue ;
+ } ;
+ first_cmd = NULL ;
+ } ;
+
+ /* reflection now..... */
+ if (exec->reflect)
+ {
+ ret = vty_cmd_reflect_line(vty) ;
+ if (ret != CMD_SUCCESS)
+ continue ;
+ } ;
+
+ /* Pipe work, if any */
+ if ((parsed->parts & cmd_parts_pipe) != 0)
+ {
+ ret = cmd_open_pipes(vty) ;
+ if (ret != CMD_SUCCESS)
+ continue ;
+ } ;
+
+ /* Command execution, if any */
+ if ((parsed->parts & cmd_part_command) != 0)
+ ret = cmd_execute(vty) ;
+
+ /* Deal with success (or suppressed warning). */
+ if ((ret == CMD_SUCCESS) || ((ret == CMD_WARNING) && ignore_warning))
+ ret = vty_cmd_success(vty) ;
+ } ;
+
+ /* Arrives here if:
+ *
+ * - vty_cmd_fetch_line() returns anything except CMD_SUCCESS, CMD_EOF or
+ * CMD_HIATUS -- which are not errors.
+ *
+ * - any other operation returns anything except CMD_SUCCESS
+ * (or CMD_WARNING, if they are being ignored), CMD_EOF or CMD_CLOSE.
+ *
+ * Deal with any errors -- generate suitable error messages and close back
+ * to (but excluding) vout_base.
+ *
+ * CMD_SUCCESS and CMD_EMPTY are impossible at this point -- they should
+ * have been dealt with in the loop.
+ *
+ * CMD_EOF is also impossible -- vty_cmd_fetch_line() or vty_cmd_hiatus()
+ * can return that, but that will have been dealt with.
+ *
+ * CMD_CLOSE is also impossible -- commands can return that, but that will
+ * have been dealt with.
+ *
+ * CMD_WAITING is not valid for blocking vio !
+ */
+ assert(ret != CMD_SUCCESS) ;
+ assert(ret != CMD_EMPTY) ;
+ assert(ret != CMD_EOF) ;
+ assert(ret != CMD_CLOSE) ;
+ assert(ret != CMD_WAITING) ;
+
+ vty_cmd_hiatus(vty, ret) ;
+
+ return ret ;
+} ;
+
+/*==============================================================================
+ */
+
+/*------------------------------------------------------------------------------
+ * Open in and/or out pipes
+ *
+ * * Returns:
+ *
+ * - OK -- CMD_SUCCESS
+ * - error -- CMD_ERROR, etc
+ */
+extern cmd_return_code_t
+cmd_open_pipes(vty vty)
+{
+ cmd_exec exec = vty->exec ;
+ cmd_parsed parsed = exec->parsed ;
+ cmd_return_code_t ret ;
+ vty_io vio ;
+
+ VTY_LOCK() ;
+ vio = vty->vio ;
+
+ ret = CMD_SUCCESS ;
+
+ uty_cmd_depth_mark(vio) ; /* about to push vin and/or vout */
+
+ /* Deal with any in pipe stuff */
+ if ((parsed->parts & cmd_part_in_pipe) != 0)
+ {
+ qstring args ;
+
+ args = cmd_tokens_concat(parsed, parsed->first_in_pipe,
+ parsed->num_in_pipe) ;
+
+ if ((parsed->in_pipe & cmd_pipe_file) != 0)
+ ret = uty_cmd_open_in_pipe_file(vio, exec->context, args,
+ parsed->in_pipe) ;
+ else if ((parsed->in_pipe & cmd_pipe_shell) != 0)
+ ret = uty_cmd_open_in_pipe_shell(vio, exec->context, args,
+ parsed->in_pipe) ;
+ else
+ zabort("invalid in pipe state") ;
+
+ qs_free(args) ;
+ } ;
+
+ /* Deal with any out pipe stuff */
+ if (((parsed->parts & cmd_part_out_pipe) != 0) && (ret == CMD_SUCCESS))
+ {
+ qstring args ;
+
+ args = cmd_tokens_concat(parsed, parsed->first_out_pipe,
+ parsed->num_out_pipe) ;
+
+ if ((parsed->out_pipe & cmd_pipe_file) != 0)
+ ret = uty_cmd_open_out_pipe_file(vio, exec->context, args,
+ parsed->out_pipe) ;
+ else if ((parsed->out_pipe & cmd_pipe_shell) != 0)
+ ret = uty_cmd_open_out_pipe_shell(vio, exec->context, args,
+ parsed->out_pipe) ;
+ else if ((parsed->out_pipe & cmd_pipe_dev_null) != 0)
+ ret = uty_cmd_open_out_dev_null(vio) ;
+ else
+ zabort("invalid out pipe state") ;
+
+ qs_free(args) ;
+ } ;
+
+ VTY_UNLOCK() ;
+ return ret ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Command Execution.
+ *
+ * If !parse_only, set vty->node and dispatch the command.
+ *
+ * Returns: CMD_SUCCESS -- it all want very well
+ * CMD_WARNING -- not so good: warning message sent by vty_out()
+ * CMD_ERROR -- not so good: warning message sent by vty_out()
+ * CMD_CLOSE -- close the current input
+ *
+ * NB: the distinction between CMD_WARNING and CMD_ERROR is that CMD_WARNING
+ * may be ignored when reading a configuration file.
+ *
+ * NB: no other returns are acceptable !
+ */
+extern cmd_return_code_t
+cmd_execute(vty vty)
+{
+ cmd_parsed parsed = vty->exec->parsed ;
+ cmd_context context = vty->exec->context ;
+ cmd_command cmd = parsed->cmd ;
+
+ cmd_return_code_t ret ;
+
+ vty->node = parsed->cnode ;
+
+ if (context->parse_only)
+ ret = CMD_SUCCESS ;
+ else
+ ret = (*(cmd->func))(cmd, vty, cmd_arg_vector_argc(parsed),
+ cmd_arg_vector_argv(parsed)) ;
+
+ if (ret == CMD_SUCCESS)
+ {
+ /* If the node is changed by the command, do that now and make sure
+ * that the configuration symbol of power is straight.
+ *
+ * If the new node is >= CONFIG_NODE, then MUST already have acquired
+ * the symbol of power (otherwise the command would have failed !)
+ *
+ * If the new node is < CONFIG_NODE, then we will here release the
+ * symbol of power iff we are at the vin_base !
+ *
+ * If the new node is NULL_NODE, then treat as CMD_CLOSE.
+ */
+ if (context->node != parsed->nnode)
+ {
+ context->node = parsed->nnode ;
+ vty_cmd_config_lock_check(vty, context->node) ;
+
+ if (context->node == NULL_NODE)
+ ret = CMD_CLOSE ;
+ } ;
+
+ /* The command should (no longer) change the vty->node, but if it does,
+ * it had better be to the same as what the parser expected -- for if
+ * not, that will break "parse_only" and generally cause confusion.
+ */
+ qassert((vty->node == parsed->cnode) || (vty->node == parsed->nnode)) ;
+ }
+ else
+ {
+ /* Enforce restrictions on return codes. */
+ assert((ret == CMD_WARNING) || (ret == CMD_ERROR)
+ || (ret == CMD_CLOSE)) ;
+ } ;
+
+ return ret ;
+} ;
diff --git a/lib/command_execute.h b/lib/command_execute.h
index a5a807c8..97033b6e 100644
--- a/lib/command_execute.h
+++ b/lib/command_execute.h
@@ -23,57 +23,102 @@
#ifndef _ZEBRA_COMMAND_EXECUTE_H
#define _ZEBRA_COMMAND_EXECUTE_H
-#include "command.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 (vector, int, int *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 "command_local.h"
+#include "command_parse.h"
+#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,
- vector_length(&parsed->vline),
- (const char * const*)vector_body(&parsed->vline)) ;
+ exec_null = 0, /* not started, yet */
+ exec_fetch, /* fetch command line */
+ exec_open_pipes, /* open pipes on command line */
+ exec_execute, /* execute standard command */
+ exec_special, /* execute special command */
+ exec_done_cmd, /* command has completed */
+ exec_hiatus, /* while issues are dealt with */
+ exec_stopped, /* command loop has stopped */
} ;
+typedef enum cmd_exec_state cmd_exec_state_t ;
+
+typedef struct cmd_exec* cmd_exec ;
+
+struct cmd_exec
+{
+ vty vty ; /* parent */
+
+ cmd_action_t action ; /* to do + line */
-#define cmd_parse_reset_keep(parsed) cmd_parse_reset(parsed, 0)
-#define cmd_parse_reset_free(parsed) cmd_parse_reset(parsed, 1)
+ cmd_context context ; /* how to parse/execute */
-extern void config_replace_string (struct cmd_element *, char *, ...);
+ bool out_suppress ; /* for configuration reading */
+ bool reflect ; /* actually reflect */
-/* 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 *);
+ cmd_parsed parsed ; /* parsing and its result */
-/* "<cr>" global */
-extern char *command_cr;
+ uint password_failures ; /* AUTH_NODE & AUTH_ENABLE_NODE */
+
+ 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
+ *
+ */
+
+extern cmd_exec cmd_exec_new(vty vty) ;
+extern cmd_exec cmd_exec_free(cmd_exec exec) ;
+
+extern cmd_return_code_t cmd_read_config(vty vty, cmd_command first_cmd,
+ bool ignore_warning) ;
+extern cmd_return_code_t cmd_open_pipes(vty vty) ;
+
+extern cmd_return_code_t cmd_execute(vty vty) ;
+
+extern cmd_context cmd_context_new(void) ;
+extern cmd_context cmd_context_new_save(cmd_context src, qpath file_here) ;
+extern cmd_context cmd_context_restore(cmd_context dst, cmd_context src) ;
+extern cmd_context cmd_context_free(cmd_context context, bool copy) ;
+
+
+#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_local.h b/lib/command_local.h
new file mode 100644
index 00000000..0925930d
--- /dev/null
+++ b/lib/command_local.h
@@ -0,0 +1,203 @@
+/* Command handler -- header for stuff used within command/vty
+ * 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_COMMAND_LOCAL_H
+#define _ZEBRA_COMMAND_LOCAL_H
+
+#include "command_common.h" /* First and foremost ! */
+
+#include "vty_local.h"
+#include "vector.h"
+#include "qpath.h"
+
+/*==============================================================================
+ * This is for access to some things in command.c which are not required
+ * by external users, who use command.h.
+ *
+ * This is for use within the command/vty family.
+ *
+ * Should not be used with command.h ! (Except in command.c itself.)
+ *
+ * This may duplicate things published in command.h, but also includes things
+ * which are not intended for "external" use.
+ */
+
+/*------------------------------------------------------------------------------
+ * Host configuration variable
+ */
+typedef unsigned long int name_gen_t ;
+
+struct host
+{
+ /* Host name of this router. */
+ char* name ;
+ bool name_set ; /* set by command */
+ name_gen_t name_gen ; /* incremented each time name changes */
+
+ /* Password for vty interface. */
+ char* password;
+ bool password_encrypted ;
+
+ /* Enable password */
+ char* enable;
+ bool enable_encrypted ;
+
+ /* System wide terminal lines default */
+ int lines;
+
+ /* Log filename. */
+ qpath logfile;
+
+ /* config file name of this host */
+ qpath config_file ;
+ qpath config_dir ;
+
+ /* Flags for services */
+ bool advanced;
+ bool encrypt;
+
+ /* Banner configuration. */
+ const char* motd ;
+ qpath motdfile;
+
+ /* Someone has the config symbol of power */
+ bool config ;
+ ulong config_brand ;
+
+ /* Allow vty to start without password */
+ bool no_password_check ;
+
+ /* Restrict unauthenticated logins? */
+ bool restricted_mode ;
+
+ /* vty timeout value -- see "exec timeout" command */
+ unsigned long vty_timeout_val ;
+
+ /* vty access-class command */
+ char* vty_accesslist_name ;
+
+ /* vty access-class for IPv6. */
+ char* vty_ipv6_accesslist_name ;
+
+ /* Current directory -- initialised cmd_cwd() */
+ qpath cwd ;
+} ;
+
+enum
+{
+ restricted_mode_default = false,
+} ;
+
+/* Structure declared in command.c */
+extern struct host host ;
+
+/*------------------------------------------------------------------------------
+ * Command qualifiers
+ */
+enum cmd_do
+{
+ cmd_do_nothing = 0, /* no action required */
+
+ cmd_do_command = 1, /* dispatch the current command line */
+
+ cmd_do_ctrl_c, /* received ^C */
+ cmd_do_ctrl_d, /* received ^D */
+ cmd_do_ctrl_z, /* received ^Z */
+
+ cmd_do_eof, /* hit "EOF" */
+ cmd_do_timed_out, /* terminal timed out */
+
+ cmd_do_count, /* number of different cli_do_xxx */
+
+ cmd_do_mask = 0x0F,
+
+ cmd_do_auth = 0x10,
+
+ cmd_do_keystroke = 0xFF, /* special for keystroke reader */
+} ;
+
+CONFIRM(cmd_do_count <= (cmd_do_mask + 1)) ;
+
+typedef enum cmd_do cmd_do_t ;
+
+/*------------------------------------------------------------------------------
+ * Command action -- qualifier + line
+ */
+struct cmd_action
+{
+ cmd_do_t to_do ;
+ qstring line ;
+} ;
+typedef struct cmd_action cmd_action_t[1] ;
+typedef struct cmd_action* cmd_action ;
+
+enum { CMD_ACTION_ALL_ZEROS = (cmd_do_nothing == 0) } ;
+
+/*------------------------------------------------------------------------------
+ * Vector of nodes -- defined in command.c, declared here so the parser can
+ * reach it.
+ */
+extern vector node_vector ;
+
+/*----------------------------------------------------------------------------*/
+
+#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"
+
+/*==============================================================================
+ * Functions in command.c
+ */
+
+extern const char* cmd_host_name(bool fresh) ;
+extern void cmd_host_config_set(qpath config_file);
+
+extern const char* cmd_prompt(node_type_t node) ;
+
+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) ;
+
+/*==============================================================================
+ *
+ */
+Inline void
+cmd_action_clear(cmd_action act)
+{
+ act->to_do = cmd_do_nothing ;
+ act->line = NULL ; /* not essential, but tidy */
+} ;
+
+Inline void
+cmd_action_set(cmd_action act, cmd_do_t to_do, qstring line)
+{
+ act->to_do = to_do ;
+ act->line = line ;
+} ;
+
+Inline void
+cmd_action_take(cmd_action dst, cmd_action src)
+{
+ *dst = *src ;
+ cmd_action_clear(src) ;
+} ;
+
+#endif /* _ZEBRA_COMMAND_LOCAL_H */
diff --git a/lib/command_parse.c b/lib/command_parse.c
new file mode 100644
index 00000000..ba1b217e
--- /dev/null
+++ b/lib/command_parse.c
@@ -0,0 +1,4745 @@
+/* Quagga command line parsing
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * Recast and extended: Copyright (C) 2010 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify
+ * it 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.
+ */
+
+#include "misc.h"
+#include <ctype.h>
+#include <stdio.h>
+#include <arpa/inet.h>
+
+#include "command_local.h"
+#include "command_parse.h"
+#include "memory.h"
+
+/*==============================================================================
+ * 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,
+
+ .type = item_eol,
+ .arg = false,
+
+ .range_sign_allowed = false,
+ .range_sign_required = false,
+ .range_min = 0,
+ .range_max = 0
+} ;
+
+/*------------------------------------------------------------------------------
+ * 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.
+ *
+ *
+ *
+ */
+extern void
+cmd_compile(cmd_command cmd)
+{
+ 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) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Validate a compiled item
+ *
+ * Checks that the contents of the cmd_command are consistent with the
+ * contents of the srcvec.
+ *
+ */
+extern void
+cmd_compile_check(cmd_command cmd)
+{
+ 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") ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Reject the cmd_item string or doc.
+ */
+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
+ */
+
+/*------------------------------------------------------------------------------
+ * Create a new, empty token_vector with room for a dozen arguments (initially).
+ */
+static token_vector
+cmd_token_vector_new(void)
+{
+ token_vector tv ;
+
+ tv = XCALLOC(MTYPE_CMD_PARSED, sizeof(token_vector_t)) ;
+
+ vector_init_new(tv->body, 12) ;
+
+ return tv ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Empty token_vector and release all memory if required.
+ */
+static token_vector
+cmd_token_vector_free(token_vector tv)
+{
+ if (tv != NULL)
+ {
+ cmd_token t ;
+
+ /* Give back all the token objects and release vector body */
+ while ((t = vector_ream(tv->body, keep_it)) != NULL)
+ {
+ qs_reset(t->qs, keep_it) ; /* discard body of qstring */
+ XFREE(MTYPE_TOKEN, t) ;
+ } ;
+
+ XFREE(MTYPE_CMD_PARSED, tv) ;
+ } ;
+
+ return NULL ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get string value of given token.
+ *
+ * Returns an empty (not NULL) string if token NULL or no string.
+ */
+inline static char*
+cmd_token_make_string(cmd_token t)
+{
+ if (t->term)
+ return qs_char_nn(t->qs) ;
+ else
+ {
+ t->term = true ;
+ return qs_make_string(t->qs) ;
+ }
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get i'th token from given token vector -- zero origin
+ */
+Inline cmd_token
+cmd_token_get(token_vector tv, vector_index_t i)
+{
+ return vector_get_item(tv->body, i) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set i'th token from given token vector -- zero origin
+ */
+inline static void
+cmd_token_set(token_vector tv, vector_index_t i,
+ cmd_token_type_t type, const char* p, usize len, usize tp)
+{
+ cmd_token t = cmd_token_get(tv, i) ;
+
+ if (t == NULL)
+ {
+ /* Make a brand new token object */
+ t = 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) ;
+
+ vector_set_item(tv->body, i, t) ;
+ } ;
+
+ 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) ;
+} ;
+
+
+/*------------------------------------------------------------------------------
+ * Get one or more original token values, concatenated with space between each.
+ *
+ * Returns a brand new qstring that must be discarded after use.
+ */
+extern qstring
+cmd_tokens_concat(cmd_parsed parsed, uint ti, uint nt)
+{
+ cmd_token t ;
+ qstring qs ;
+
+ assert(nt >= 2) ;
+
+ t = cmd_token_get(parsed->tokens, ++ti) ;
+ qs = qs_set_els(NULL, t->ot) ;
+
+ while (--nt >= 2)
+ {
+ t = cmd_token_get(parsed->tokens, ++ti) ;
+ qs_append_str(qs, " ") ;
+ qs_append_els(qs, t->ot) ;
+ } ;
+
+ return qs ;
+} ;
+
+/*==============================================================================
+ * Argument vector
+ */
+
+/*------------------------------------------------------------------------------
+ * Create a new, empty arg_vector with room for a dozen arguments (initially).
+ */
+static arg_vector
+cmd_arg_vector_new(void)
+{
+ arg_vector args ;
+
+ args = XCALLOC(MTYPE_CMD_PARSED, sizeof(arg_vector_t)) ;
+
+ vector_init_new(args->body, 12) ;
+
+ return args ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Empty arg_vector and release all memory if required.
+ */
+static arg_vector
+cmd_arg_vector_free(arg_vector args)
+{
+ if (args != NULL)
+ {
+ vector_reset(args->body, keep_it) ;
+ XFREE(MTYPE_CMD_PARSED, args) ;
+ } ;
+
+ return NULL ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Empty the body of the arg_vector object in cmd_parsed.
+ */
+Inline void
+cmd_arg_vector_empty(cmd_parsed parsed)
+{
+ vector_set_length(parsed->args->body, 0) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Push argument to the argument vector.
+ */
+Inline void
+cmd_arg_vector_push(cmd_parsed parsed, char* arg)
+{
+ vector_push_item(parsed->args->body, arg) ;
+} ;
+
+/*==============================================================================
+ * Parser object
+ */
+
+/*------------------------------------------------------------------------------
+ * Allocate and initialise a new cmd_parsed object
+ */
+extern cmd_parsed
+cmd_parsed_new(void)
+{
+ cmd_parsed parsed ;
+
+ parsed = XCALLOC(MTYPE_CMD_PARSED, sizeof(cmd_parsed_t)) ;
+
+ /* Zeroising the structure has set:
+ *
+ * parts = 0 -- cleared by cmd_tokenize()
+ * tok_total = 0 -- set by cmd_tokenize()
+ *
+ * elen = 0 -- set by cmd_tokenize()
+ * tsp = 0 -- set by cmd_tokenize()
+ *
+ * cmd = NULL -- no command yet
+ * cnode = 0 -- not set
+ * nnode = 0 -- not set
+ *
+ * num_tokens = 0 -- set by cmd_tokenize()
+ * tokens = NULL -- see below
+ *
+ * args = NULL -- see below
+ *
+ * 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 = NULL -- no vector of filtered commands
+ * item_v = NULL -- no vector of filtered items
+ *
+ * strongest )
+ * best_complete ) set by cmd_filter_prepare()
+ * min_strength )
+ * strict )
+ */
+ confirm(cmd_pipe_none == 0) ;
+
+ parsed->tokens = cmd_token_vector_new() ;
+ parsed->args = cmd_arg_vector_new() ;
+
+ return parsed ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Empty out and free a cmd_parsed object
+ */
+extern cmd_parsed
+cmd_parsed_free(cmd_parsed parsed)
+{
+ if (parsed != NULL)
+ {
+ parsed->tokens = cmd_token_vector_free(parsed->tokens) ;
+ parsed->args = cmd_arg_vector_free(parsed->args) ;
+
+ parsed->cmd_v = vector_reset(parsed->cmd_v, free_it) ;
+ parsed->item_v = vector_reset(parsed->item_v, free_it) ;
+
+ parsed->emess = qs_reset(parsed->emess, free_it) ;
+
+ XFREE(MTYPE_CMD_PARSED, parsed) ;
+ } ;
+
+ return NULL ;
+} ;
+
+/*==============================================================================
+ * Parsing error handling.
+ *
+ *
+ */
+static cmd_return_code_t
+cmd_set_parse_error(cmd_parsed parsed, cmd_token t, usize off,
+ const char* format, ...) PRINTF_ATTRIBUTE(4, 5) ;
+
+/*------------------------------------------------------------------------------
+ * 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_set_parse_error(cmd_parsed parsed, cmd_token t, usize off,
+ const char* format, ...)
+{
+ va_list args ;
+
+ qs_clear(parsed->emess) ;
+
+ va_start (args, format);
+ parsed->emess = qs_vprintf(parsed->emess, format, args);
+ va_end (args) ;
+
+ if (t != NULL)
+ parsed->eloc = t->tp + off ;
+ else
+ parsed->eloc = -1 ;
+
+ return CMD_ERR_PARSING ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * 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
+ */
+extern void
+cmd_get_parse_error(vio_fifo ebuf, cmd_parsed parsed, uint indent)
+{
+ if (parsed->eloc >= 0)
+ {
+ qstring here ;
+
+ here = qs_set_fill(NULL, indent + parsed->eloc, "....") ;
+ vio_fifo_put_bytes(ebuf, qs_body_nn(here), qs_len_nn(here)) ;
+ qs_reset(here, free_it) ;
+ } ;
+
+ vio_fifo_printf(ebuf, "^\n%% %s\n", qs_make_string(parsed->emess)) ;
+} ;
+
+/*==============================================================================
+ * Lexical level stuff
+ */
+
+/*------------------------------------------------------------------------------
+ * Take qstring and see if it is empty -- only whitespace and/or comment
+ */
+extern bool
+cmd_is_empty(qstring line)
+{
+ cpp_t lp ;
+
+ qs_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 bool
+cmd_pipe_reserved_char(char ch)
+{
+ return strchr("<|>%&*+-=?", ch) != NULL ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Take qstring and break it into tokens.
+ *
+ * Expects string to have been preprocessed, if required, to ensure that any
+ * unwanted control characters have been removed. This code only recognises
+ * '\t' and treats it as whitespace (so any other control characters will
+ * end up as part of a token).
+ *
+ * Ignores leading and trailing ' ' or '\t' (whitespace).
+ *
+ * Have "full_lex" flag -- to distinguish old and new.
+ *
+ * If not full_lex, we have this simple tokenization:
+ *
+ * * tokens are separated by one or more whitespace characters.
+ *
+ * * if the first non-whitespace character is '!', this is a comment line.
+ *
+ * For the "full_lex" we more or less follow the usual shell conventions,
+ * except:
+ *
+ * * can have '!' as well as '#' to start comments
+ *
+ * * may only have '|' at the start of a line. '>|' must be used for
+ * piping command output to shell command. So '|' is, effectively, a
+ * "shell command prefix".
+ *
+ * Limiting '|' in this way removes problems with regular expressions.
+ *
+ * Also allows a <| shell to use pipes !!
+ *
+ * So the "full_lex" will:
+ *
+ * * ignore anything between '....' -- as per shell convention, '\' is
+ * ignored and there is no way to include "'" in a single quoted string.
+ *
+ * * ignore anything immediately preceded by '\' -- including space
+ * (or tab converted to a space) and double quote characters.
+ *
+ * * ignore anything between "...." -- including '\"' escapes.
+ *
+ * * unbalanced "'" or '"' are treated as if eol was a "'" or '"'.
+ *
+ * Except when ignored by the rules above, the "full lex" will recognise
+ * the following:
+ *
+ * * tokens are separated by whitespace -- one ' ' or '\t' characters.
+ * Whitespace before, after or between tokens is not part of the tokens.
+ *
+ * * the character '|' is significant at the start of a line (after any
+ * leading whitespace) only. It is then the start of an out_pipe
+ * token -- so self terminates after any pipe_reserved_chars.
+ *
+ * * the characters '!', '#' are significant only at the start of a token,
+ * and then from there to end of line is comment.
+ *
+ * Note that this means that comment after a token must be separated by
+ * at least one space from that token.
+ *
+ * * the characters '<' and '>' are separators -- they terminate any
+ * preceding token. They are in_pipe or out_pipe tokens, and self
+ * terminate after any pipe_reserved_chars.
+ *
+ * The tokenization does not remove any " ' or \ characters, that is left for
+ * a later stage, where context may affect the handling.
+ *
+ * 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.
+ *
+ * NB: the elstring containing the line to be tokenized 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 void
+cmd_tokenize(cmd_parsed parsed, qstring line, bool full_lex)
+{
+ cpp_t lp ;
+ const char *cp, *tp ;
+ cmd_token_type_t total ;
+ uint nt ;
+
+ total = 0 ; /* nothing yet */
+ nt = 0 ;
+
+ qs_cpp(lp, line) ; /* NULL -> NULL */
+
+ cp = lp->p ;
+ tp = cp ;
+ while (cp < lp->e) /* process to end */
+ {
+ const char* sp ;
+ bool end ;
+ cmd_token_type_t type ;
+
+ if ((*cp == ' ') || (*cp == '\t'))
+ {
+ /* skip white-space */
+ do
+ {
+ end = (++cp == lp->e) ;
+ } while (!end && ((*cp == ' ') || (*cp == '\t'))) ;
+
+ if (end)
+ break ;
+ } ;
+
+ end = false ;
+ sp = cp ;
+ type = cmd_tok_simple ;
+ do
+ {
+ switch (*cp)
+ {
+ case '\t': /* whitespace at end of token */
+ case ' ':
+ end = true ;
+ break ;
+
+ case '\'': /* proceed to matching ' or end */
+ ++cp ;
+ if (full_lex)
+ {
+ type |= cmd_tok_sq ;
+ while (cp < lp->e)
+ {
+ if (*cp++ == '\'')
+ break ;
+ } ;
+ } ;
+ break ;
+
+ case '\\': /* step past escaped character, if any */
+ ++cp ;
+ if (full_lex)
+ {
+ type |= cmd_tok_esc ;
+ if (cp < lp->e)
+ ++cp ;
+ } ;
+ break ;
+
+ case '"': /* proceed to matching " or end... */
+ ++cp ;
+ if (full_lex)
+ {
+ type |= cmd_tok_dq ;
+ while (cp < lp->e) /* NB: do not register \ separately */
+ {
+ if (*cp++ == '"')
+ if (*(cp - 2) != '\\') /* ignore escaped " */
+ break ;
+ } ;
+ } ;
+ break ;
+
+ case '|': /* only at start of line */
+ if (full_lex && (nt == 0) && (cp == sp))
+ {
+ type = cmd_tok_out_shell ;
+ do ++cp ;
+ while ((cp < lp->e) && cmd_pipe_reserved_char(*cp)) ;
+ end = true ;
+ }
+ else
+ ++cp ;
+ break ;
+
+ case '>': /* '>' is a separator */
+ if (full_lex)
+ {
+ if (cp == sp)
+ {
+ type = cmd_tok_out_pipe ;
+ do ++cp ;
+ while ((cp < lp->e) && cmd_pipe_reserved_char(*cp)) ;
+ } ;
+ end = true ;
+ }
+ else
+ ++cp ;
+ break ;
+
+ case '<': /* '<' is a separator */
+ if (full_lex)
+ {
+ if (cp == sp)
+ {
+ type = cmd_tok_in_pipe ;
+ do ++cp ;
+ while ((cp < lp->e) && cmd_pipe_reserved_char(*cp)) ;
+ } ;
+ end = true ;
+ }
+ else
+ ++cp ;
+ break ;
+
+ case '#':
+ if (!full_lex)
+ {
+ ++cp ;
+ break ;
+ } ;
+ fall_through ; /* treat as '!'. */
+
+ case '!': /* '!' and '#' special at token start */
+ if ((cp == sp) && (full_lex || (nt == 0)))
+ {
+ type = cmd_tok_comment ;
+ cp = lp->e ;
+ }
+ else
+ ++cp ;
+ break ;
+
+ default:
+ ++cp ;
+ break ;
+ } ;
+ } while (!end && (cp < lp->e)) ;
+
+ cmd_token_set(parsed->tokens, nt, type, sp, cp - sp, sp - lp->p) ;
+ ++nt ;
+ total |= type ;
+
+ tp = cp ;
+ } ;
+
+ /* 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) ;
+} ;
+
+
+
+
+
+/*==============================================================================
+ * VTY Command Line Input Pipe
+ *
+ * Here are the mechanics which support the:
+ *
+ * < file_name
+ *
+ * <| shell_command
+ *
+ * and the:
+ *
+ * > file_name
+ * >> file_name
+ * | shell_command
+ *
+ *==============================================================================
+ * The file_name handling
+ *
+ * Two directories are supported:
+ *
+ * "home" -- being the root for configuration files
+ *
+ * "cd" -- being the root for relative filenames, in the usual way
+ *
+ * "here" -- being the directory for the enclosing "< filename"
+ * see below for more detailed semantics
+ *
+ * There are the global values:
+ *
+ * config home -- defaults to directory for configuration file.
+ * may be set by command.
+ *
+ * config cd -- defaults to cwd at start up
+ *
+ * There are the local values, within a given CLI instance (ie VTY):
+ *
+ * cli home -- set to config home when CLI instance starts, or when
+ * config home is set within the CLI.
+ *
+ * cli cd -- similarly
+ *
+ * cli here -- outside < is same as cli home, unless explicitly set
+ * - set by cli here
+ * - pushed each time executes <, and set to directory for
+ * the < filename.
+ * - pushed each time executes <| and left unchanged
+ * - popped each time exits < or <|
+ * - set to parent by "no cli here" inside <, or to
+ * default state outside <
+ *
+ * And then the filename syntax:
+ *
+ * /path -- absolute path -- in the usual way
+ *
+ * path -- path wrt "cli cd" -- in the usual way
+ *
+ * ~/path -- path in "cli home" -- so "config" stuff
+ *
+ * ~./path -- path in "here" -- so wrt to enclosing < file
+ *
+ *==============================================================================
+ * The Input Pipe Commands
+ *
+ * These are "universal commands".
+ *
+ * They are:
+ *
+ * < filename -- filename as above
+ *
+ * treat contents of given file as command lines.
+ * (That may include further < commands.)
+ *
+ * See notes on cli here for pushing/popping the here
+ * directory.
+ *
+ * TODO: Filename and quotes ??
+ *
+ * <| shell_command -- the shell command is executed "as is" by system().
+ *
+ * the stdout and stderr are collected.
+ *
+ * treats stdout as command lines.
+ * (That may include further < commands.)
+ *
+ * anything from stderr is sent to the VTY output.
+ *
+ * As far as the top level CLI is concerned, these are discrete commands.
+ * That is to say:
+ *
+ * -- except where blocked while reading the "pipe", all commands are
+ * executed one after another, in one Routing Engine operation.
+ *
+ * -- in any event, all output is gathered in the VTY buffering, and will
+ * be sent to the console (or where ever) only when the outermost command
+ * completes.
+ *
+ * There are three options associated with the output from a < operation:
+ *
+ * -- suppress command line reflect
+ *
+ * whether to suppress reflect of commands to the VTY before they are
+ * dispatched.
+ *
+ * The default is not to suppress command line reflect.
+ *
+ * -- suppress command results
+ *
+ * whether to suppress any output generated by each command.
+ *
+ * The default is not to suppress command results.
+ *
+ * -- suppress "more"
+ *
+ * whether to do "--more--", if currently applies, when finally
+ * outputting all the command results.
+ *
+ * The default is not to suppress "more".
+ *
+ * This option can only be set for the outermost < operation.
+ *
+ * Remembering that all output occurs in one go when the outermost < operation
+ * completes.
+ *
+ * The default is to show everything and implement "--more--", pretty much as
+ * if the commands had been typed in. Except that "--more--" applies to
+ * everything (including the command lines) together, rather than to the output
+ * of each command individually.
+ *
+ * These options may be changed by flags attached to the <, as follows:
+ *
+ * ! suppress command line reflect
+ *
+ * ? suppress "--more--"
+ *
+ * * suppress command output
+ *
+ * TODO: cli xxx commands for reflect and output suppression and notes ....
+ *
+ * TODO: Error handling....
+ *
+ * TODO: Closing the CLI....
+ *
+ * TODO: Time out and the CLI....
+ *
+ *
+ */
+
+
+
+
+/*------------------------------------------------------------------------------
+ * Get next cpp char & step -- unless at end of cpp.
+ */
+inline static char
+cpp_getch(cpp p)
+{
+ if (p->p < p->e)
+ return *p->p++ ;
+ else
+ return '\0' ;
+}
+
+/*------------------------------------------------------------------------------
+ * Process in-pipe token and set the required bits in the pipe type word
+ *
+ * Known tokens are: < <|
+ * Known options 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 = true ;
+
+ switch (cpp_getch(p))
+ {
+ case '<':
+ switch (cpp_getch(p))
+ {
+ default:
+ --p->p ;
+ fall_through ;
+
+ case '\0':
+ parsed->in_pipe = cmd_pipe_file ;
+ break ;
+
+ case '|':
+ parsed->in_pipe = cmd_pipe_shell ;
+ break ;
+ } ;
+ break ;
+
+ default:
+ ok = false ;
+ break ;
+ } ;
+
+ while (ok && (p->p < p->e))
+ {
+ switch (*p->p++)
+ {
+ case '+':
+ parsed->in_pipe |= cmd_pipe_reflect ;
+ break ;
+
+ default:
+ ok = false ;
+ break ;
+ } ;
+ } ;
+
+ if (ok)
+ return CMD_SUCCESS ;
+
+ return cmd_set_parse_error(parsed, t, 0, "invalid 'pipe in'") ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Process out-pipe token and set the required bits in the pipe type word
+ *
+ * Known tokens are: | > >> >| >*
+ * Known options are: none
+ */
+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 = true ;
+
+ switch (cpp_getch(p))
+ {
+ case '|':
+ parsed->out_pipe = cmd_pipe_shell | cmd_pipe_shell_only ;
+ break ;
+
+ case '>':
+ switch (cpp_getch(p))
+ {
+ default:
+ --p->p ;
+ fall_through ;
+
+ case '\0':
+ parsed->out_pipe = cmd_pipe_file ;
+ break ;
+
+ case '>':
+ parsed->out_pipe = cmd_pipe_file | cmd_pipe_append ;
+ break ;
+
+ case '|':
+ parsed->out_pipe = cmd_pipe_shell ;
+ break ;
+
+ case '*':
+ parsed->out_pipe = cmd_pipe_dev_null ;
+ break ;
+ } ;
+ break ;
+
+ default:
+ ok = false ;
+ break ;
+ } ;
+
+ while (ok && (p->p < p->e))
+ {
+ switch (*p->p++)
+ {
+ default:
+ ok = false ;
+ break ;
+ } ;
+ } ;
+
+ if (ok)
+ return CMD_SUCCESS ;
+
+ return cmd_set_parse_error(parsed, t, 0, "invalid 'pipe out'") ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * 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 "..." this 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.
+ */
+static cmd_return_code_t
+cmd_token_complete(cmd_parsed parsed, cmd_token t)
+{
+ 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 */
+
+ /* 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->p)
+ {
+ case '\t':
+ *q->p++ = ' ' ; /* '\t' -> ' ' */
+ ++p->p ;
+ break ;
+
+ case '\'':
+ ++p->p ; /* skip leading ' */
+ while (1)
+ {
+ if (p->p == p->e)
+ {
+ ret = cmd_set_parse_error(parsed, t, 0, "missing closing '") ;
+ break ;
+ } ;
+
+ if (*p->p == '\'')
+ {
+ ++p->p ; /* skip trailing ' */
+ break ; /* done '....' */
+ } ;
+
+ if (*p->p == '\t')
+ {
+ *q->p++ = ' ' ; /* '\t' -> ' ' */
+ ++p->p ;
+ }
+ else
+ *q->p++ = *p->p++ ; /* rest as is */
+ } ;
+ break ;
+
+ case '"':
+ ++p->p ; /* skip " */
+ dq = !dq ; /* switch state */
+ break ;
+
+ case '\\':
+ *q->p++ = *p->p++ ; /* copy the \ */
+ if (p->p == p->e)
+ ret = cmd_set_parse_error(parsed, t, 0, "trailing \\") ;
+ else
+ {
+ 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->p++ = ' ' ;
+ ++p->p ;
+ } ;
+ } ;
+ break ;
+
+ default:
+ *q->p++ = *p->p++ ;
+ break ;
+ } ;
+ } ;
+
+ if (dq)
+ ret = cmd_set_parse_error(parsed, t, 0, "missing closing \"") ;
+
+ 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 ;
+}
+
+/*------------------------------------------------------------------------------
+ * Tokenize the given line and work out where cursor is wrt tokens.
+ *
+ * Note that this is implicitly "full lex".
+ *
+ * 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.
+ *
+ * - if the line is blank, with zero or more spaces, the cursor is "on"
+ * the eol token.
+ *
+ * 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.
+ */
+extern bool
+cmd_token_position(cmd_parsed parsed, qstring line)
+{
+ 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 tokenize the given line */
+ cmd_tokenize(parsed, line, true) ;
+
+ /* 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_out_shell
+ | 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)) ;
+} ;
+
+/*==============================================================================
+ * 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) ;
+
+/*------------------------------------------------------------------------------
+ * 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
+ */
+static match_type_t
+cmd_ipv4_address_match(cmd_token t)
+{
+ match_strength_t ms ;
+
+ ms = cmd_ipv4_match(cmd_token_make_string(t), 0) ;
+
+ if (ms == ms_var_complete)
+ return mt_ipv4_address_complete ;
+ if (ms == ms_partial)
+ return mt_ipv4_address_partial ;
+
+ return mt_no_match ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * 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 '/'.
+ */
+static match_type_t
+cmd_ipv4_prefix_match(cmd_token t)
+{
+ 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)
+ {
+ if (*cp == '.') /* need a '.' except at start */
+ {
+ if (nums == 0)
+ return ms_no_match ;
+
+ ++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
+ return ms_no_match ; /* reject invalid number */
+ } ;
+
+ if (*cp != '\0')
+ return ms_no_match ; /* something other than digits after the '/',
+ or leading zero, or number too big */
+
+ return ms_var_complete ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * 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
+ *
+ * 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 IPv6 Prefix
+ *
+ * h:h:...[/99] -- RFC 4291 rules & prefix length must be <= 128
+ *
+ * Returns: mt_no_match -- improperly formed
+ * mt_ipv6_prefix_partial -- OK as far as it goes (or empty)
+ * mt_ipv6_prefix_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_prefix_match(cmd_token t)
+{
+ 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 ;
+
+ return mt_no_match ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * 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)
+ {
+ 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 == ':')
+ {
+ /* (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 == ':')
+ {
+ /* '::' -- counts as number, can be followed by '/' */
+
+ if (double_colon)
+ return ms_no_match ; /* at most one */
+
+ ++cp ; /* step past '::' */
+
+ double_colon = true ;
+ ++nums ; /* counts as a number */
+
+ if ((nums == 8) || (*cp == '/') || (*cp == '\0'))
+ break ; /* no more numbers */
+ }
+ else if (*cp == '\0')
+ return ms_partial ; /* accepts bare ':', inter alia */
+
+ 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) ;
+
+ /* Watch out for '.' ! */
+
+ 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 ;
+ } ;
+ } ;
+
+ /* 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 */
+
+ 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 ;
+
+ ++dp ; /* step past 'x' or 'X' */
+ base = 16 ;
+
+ if (*dp == '\0')
+ return mt_range_partial ;
+ } ;
+ }
+
+ else if (!isdigit(*dp))
+ return mt_no_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) ;
+
+/*------------------------------------------------------------------------------
+ * Prepare to filter commands in the node being parsed in.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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 !
+ *
+ * Returns: number of commands which may match to.
+ */
+static uint
+cmd_filter_prepare(cmd_parsed parsed, cmd_context context)
+{
+ vector src_v ;
+ uint ci ;
+ uint nct ;
+
+ /* 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 -- creates vector if required.
+ *
+ * Note that the cmd_v lives for as long as the parsed object, so will
+ * grow over time to accommodate what is required.
+ */
+ parsed->cmd_v = 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) && context->parse_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 = context->parse_execution ? ms_min_execute
+ : ms_min_parse ;
+ parsed->strict = context->parse_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.
+ */
+static uint
+cmd_filter(cmd_parsed parsed, uint ii, bool keep_items)
+{
+ 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 ;
+
+ /* Decide which token we are trying to match. */
+ 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) ;
+
+ /* If we are keeping the items which are matched, set the item_v
+ * empty and guess that may get one item per command -- creates the item_v
+ * vector if required..
+ *
+ * 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)
+ parsed->item_v = vector_re_init(parsed->item_v,
+ vector_length(parsed->cmd_v)) ;
+
+ /* Work down the cmd_v, attempting to match cmd_items against the cmd_token.
+ *
+ * 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.
+ */
+ c_keep = 0 ;
+ i_keep = 0 ;
+
+ for (ci = 0; ci < vector_length(parsed->cmd_v); ci++)
+ {
+ 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)
+ {
+ int ret ;
+ ret = cmd_item_filter(parsed, item, t) ;
+
+ if (ret >= 0)
+ {
+ if (ret > 0)
+ i_keep = 0 ;
+
+ if (keep_items)
+ vector_set_item(parsed->item_v, i_keep++, item) ;
+ else
+ ++i_keep ;
+ } ;
+
+ if (ret > best)
+ best = ret ;
+
+ item = item->next ;
+ } ;
+
+ /* Keep if had a match */
+ if (best >= 0)
+ {
+ if (best > 0)
+ c_keep = 0 ; /* better than all the rest */
+
+ 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 */
+
+ vector_set_length(parsed->cmd_v, c_keep) ; /* discard what did not keep */
+
+ 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 ;
+
+ /* 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_context context) ;
+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_context context) ;
+static cmd_return_code_t cmd_parse_specials(cmd_parsed parsed,
+ cmd_context context) ;
+static node_type_t cmd_auth_specials(cmd_context context, node_type_t target) ;
+
+/*------------------------------------------------------------------------------
+ * Parse a command in the given "node", or (if required) any of its ancestors.
+ *
+ * Requires command line to have been tokenized.
+ *
+ * Returns: CMD_SUCCESS => successfully parsed command, and the result is
+ * in the given parsed structure, ready for execution.
+ *
+ * - parsed->cnode may not be the incoming node.
+ *
+ * - parsed->nnode is set for when command succeeds.
+ *
+ * - parsed->parts is what was found
+ *
+ * - 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, cmd_context context)
+{
+ 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.
+ *
+ * Unless CMD_ERR_PARSING:
+ *
+ * - sort out any "do" and cut from start of command.
+ * - set parsed->cnode (from given node or according to "do")
+ * - set parsed->nnode (from given node)
+ * - 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, context) ;
+ 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) && context->parse_execution)
+ {
+ 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 allowed.
+ *
+ * Will stop moving up the tree when hits a node which is its own parent.
+ * All nodes from NODE_CONFIG up have a parent. All nodes from NODE_ENABLE
+ * down are their own parent.
+ *
+ * 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, context) ;
+
+ if (ret == CMD_SUCCESS)
+ break ;
+
+ if ((ret != CMD_ERR_NO_MATCH) || context->parse_no_tree)
+ return ret ;
+
+ 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 -- worry about parsed->nnode.
+ *
+ * Currently parsed->nnode is set to the node we came in on. If the
+ * parsed->cnode is not the same, then we have two choices:
+ *
+ * (a) parsed->cnode is ENABLE_NODE -- either because have 'do' or by tree
+ * walk -- in which case we leave parsed->nnode;
+ *
+ * (b) parsed->cnode is not ENABLE_NODE -- so this command, if successful
+ * will change context, in which cas we need to set parsed->nnode.
+ */
+ if ((parsed->nnode != parsed->cnode) && (parsed->cnode != ENABLE_NODE))
+ parsed->nnode = parsed->cnode ;
+
+ /* Worry about "special" commands and those that set the next node. */
+ if ((parsed->cmd->attr & (CMD_ATTR_NODE | CMD_ATTR_MASK)) != 0)
+ {
+ if ((parsed->cmd->attr & CMD_ATTR_NODE) != 0)
+ parsed->nnode = parsed->cmd->attr & CMD_ATTR_MASK ;
+ else
+ {
+ /* This is a "special" command, which may have some (limited)
+ * semantic restrictions.
+ *
+ * The main thing we are interested in is commands which have
+ * special effects on parsed->nnode (in particular exit and end).
+ */
+ ret = cmd_parse_specials(parsed, context) ;
+
+ if (ret != CMD_SUCCESS)
+ return ret ;
+ } ;
+ } ;
+
+ /* 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 (context->parse_execution)
+ {
+ uint ti ;
+
+ cmd_arg_vector_empty(parsed) ;
+
+ for (ti = 0; ti < parsed->num_command ; ti++)
+ {
+ bool take ;
+
+ if (ti < parsed->cmd->nt_min)
+ {
+ cmd_item item = vector_get_item (parsed->cmd->items, ti);
+
+ take = item->arg ; /* follow item */
+ }
+ else
+ take = true ; /* option or vararg token */
+
+ 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)) ;
+ } ;
+ } ;
+ } ;
+
+ /* Return appropriate form of success */
+ return parsed->cmd->daemon ? CMD_SUCCESS_DAEMON
+ : CMD_SUCCESS ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Phase 1 of command parsing -- get ready to search the command system.
+ *
+ * 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
+ *
+ * Sets parsed->cnode to the given node, or to ENABLE_NODE if have 'do' (and
+ * the original node allows it).
+ *
+ * Sets parsed->nnode to the given node.
+ *
+ * Requires line to have been tokenized -- cmd_tokenize().
+ *
+ * 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_context context)
+{
+ uint nt = parsed->num_tokens ;
+
+ /* Set command and parsing entries */
+ parsed->nnode = parsed->cnode = context->node ;
+ parsed->cmd = NULL ;
+
+ /* pick off any comment */
+ if ((parsed->tok_total & cmd_tok_comment) != 0)
+ {
+ parsed->num_comment = 1 ;
+ parsed->first_comment = --nt ; /* implicitly the last */
+ parsed->tok_total ^= cmd_tok_comment ;
+ parsed->parts |= cmd_part_comment ;
+ } ;
+
+ /* 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 (ret != CMD_SUCCESS)
+ return ret ;
+ } ;
+
+ /* 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 (!context->parse_no_do && (context->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'))
+ {
+ parsed->cnode = 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 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Phase 1b of command parsing
+ *
+ * tokenizer found at least one of:
+ *
+ * - in pipe token
+ * - out pipe token
+ * - token with quotes or escapes
+ *
+ * Deal with all of those, verifying the syntax of any pipes and completing
+ * any tokens with quotes etc.
+ *
+ * Update the "parts" and the relevant first/num values.
+ *
+ * Note that the number of tokens (nt) *excludes* any comment token.
+ *
+ * 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 ;
+
+ parts = cmd_parts_none ; /* no parts yet */
+ n = 0 ; /* no tokens in current part */
+ pn = NULL ; /* no current part */
+
+ /* Walk the tokens, establishing the start and end of:
+ *
+ * - command part )
+ * - in pipe part ) exclusive
+ * - out shell part )
+ * - out pipe part which may only follow command or in pipe
+ *
+ *
+ */
+ for (i = 0 ; i < nt ; ++i)
+ {
+ t = cmd_token_get(parsed->tokens, i) ;
+
+ /* Simple tokens can appear anywhere.
+ *
+ * If this is the first token, start a cmd_part_command.
+ */
+ 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 ;
+ } ;
+ }
+
+ /* '<' types of token can appear anywhere, except in a cmd_part_command.
+ *
+ * If this is the first token, start a cmd_part_in_pipe.
+ */
+ else if ((t->type & cmd_tok_in_pipe) != 0)
+ {
+ if ((parts & (cmd_part_command | cmd_parts_pipe)) == cmd_part_command)
+ return cmd_set_parse_error(parsed, t, 0, "unexpected 'pipe in'") ;
+
+ if (parts == cmd_parts_none)
+ {
+ ret = cmd_parse_in_pipe(parsed, t) ;
+ if (ret != CMD_SUCCESS)
+ return ret ;
+
+ parts = cmd_part_in_pipe ;
+ parsed->first_in_pipe = i ;
+ pn = &parsed->num_in_pipe ;
+ n = 0 ;
+ }
+ }
+
+ /* '|' tokens can appear anywhere.
+ *
+ * If this is the first token, start a cmd_part_out_pipe.
+ */
+ else if ((t->type & cmd_tok_out_shell) != 0)
+ {
+ if ((parts & (cmd_part_command | cmd_parts_pipe)) == cmd_part_command)
+ return cmd_set_parse_error(parsed, t, 0, "unexpected 'shell pipe'") ;
+
+ if (parts == cmd_parts_none)
+ {
+ ret = cmd_parse_out_pipe(parsed, t) ;
+ if (ret != CMD_SUCCESS)
+ return ret ;
+
+ parts = cmd_part_out_pipe ;
+ parsed->first_out_pipe = i ;
+ pn = &parsed->num_out_pipe ;
+ n = 0 ;
+ } ;
+ }
+
+ /* '>' types of token can appear anywhere, except on an empty line.
+ *
+ * If not already in cmd_part_out_pipe, start a cmd_part_out_pipe.
+ */
+ else if ((t->type & cmd_tok_out_pipe) != 0)
+ {
+ if (parts == cmd_parts_none)
+ return cmd_set_parse_error(parsed, t, 0, "unexpected 'pipe out'") ;
+
+ if ((parts & cmd_part_out_pipe) == 0)
+ {
+ 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 */
+
+ /* Now deal with any "incompleteness" */
+
+ if ((t->type & cmd_tok_incomplete) != 0)
+ {
+ ret = cmd_token_complete(parsed, t) ;
+ if (ret != CMD_SUCCESS)
+ return ret ;
+ } ;
+
+ ++n ; /* count up tokens */
+ } ;
+
+ if (pn != NULL)
+ *pn = n ; /* number in last phase */
+
+ /* If have an in-pipe or an out-pipe, worry about the number of
+ * arguments
+ */
+ if ((parts & cmd_parts_pipe) != 0)
+ {
+ 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))
+ {
+ assert(parsed->num_in_pipe > 0) ;
+
+ if (((parsed->in_pipe & cmd_pipe_file) != 0)
+ && (parsed->num_in_pipe != 2))
+ {
+ 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))
+ {
+ i = parsed->first_in_pipe ;
+ e = true ;
+ msg = "requires shell command" ;
+ } ;
+ } ;
+
+ /* 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))
+ {
+ 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 (((parsed->out_pipe & cmd_pipe_shell) != 0)
+ && (parsed->num_out_pipe < 2))
+ {
+ 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_set_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_context context)
+{
+ uint ii ;
+ uint match ;
+
+ /* Prepare to filter commands */
+
+ cmd_filter_prepare(parsed, context) ;
+
+ 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 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Perform the special handling required for the command that has parsed
+ * successfully so far.
+ *
+ * This mechanism is used to deal with commands which have a context sensitive
+ * effect on parsed->nnode.
+ *
+ * Can be used to do any other semantic checks, subject to the information
+ * being available.
+ *
+ * Returns: CMD_SUCCESS -- OK
+ * CMD_ERR_PARSING -- failed some check, see parsed->emess
+ *
+ * Dealt with here are:
+ *
+ * * "exit" and "quit"
+ *
+ * The next node depends on the current node.
+ *
+ * * "end"
+ *
+ * The next node depends on the current node.
+ *
+ * * "enable"
+ *
+ * This command is really only intended for interactive use, but need to
+ * do something with it in other contexts.
+ *
+ * Can go to ENABLE_NODE directly if can_enable is set in the context.
+ *
+ * The can_enable is set when the vty starts if it does not require any
+ * authentication to enter ENABLE_NODE (eg VTY_CONFIG_READ) or because
+ * it started in ENABLE_NODE or greater (eg VTY_TERMINAL with no password
+ * and advanced mode !).
+ *
+ * Once can_enable is set it is not unset. So getting to enable once is
+ * sufficient for a given VTY.
+ *
+ * A pipe will inherit can_enable, provided that the parent is in
+ * ENABLE_NODE or better.
+ *
+ * A pipe cannot inherit can_auth_enable -- this means that a pipe
+ * can either immediately enable, or cannot enable at all.
+ *
+ * The effect of all this is that "enable" is straightforward, except for
+ * VTY_TERMINAL. For VTY_TERMINAL:
+ *
+ * - if the VTY starts in any node >= ENABLE_NODE, then can_enable
+ * is set from the beginning !
+ *
+ * If has ever reached ENABLE_NODE, then can_enable will be set.
+ *
+ * - otherwise: when enable command is seen, must authenticate.
+ *
+ * - if there is an enable password, then must get and accept the
+ * password, which can only happen at vin_depth == vout_depth == 0
+ * -- see vty_cmd_can_auth_enable().
+ *
+ * - if there is no enable password, then is implicitly authenticated
+ * if is in VIEW_NODE.
+ *
+ * Note that will not accept enable with no password if is in
+ * RESTRICTED_NODE. Can only be in RESTRICTED_NODE if started with
+ * no password, but host.restricted_mode is set. Doesn't seem much
+ * point having a restricted_mode if you can go straight to
+ * ENABLE_NODE just because a password has not been set !
+ */
+static cmd_return_code_t
+cmd_parse_specials(cmd_parsed parsed, cmd_context context)
+{
+ cmd_return_code_t ret ;
+
+ ret = CMD_SUCCESS ;
+
+ switch (parsed->cmd->attr & CMD_ATTR_MASK)
+ {
+ case cmd_sp_simple:
+ zabort("invalid cmd_sp_simple") ;
+ break ;
+
+ case cmd_sp_end:
+ parsed->nnode = cmd_node_end_to(parsed->cnode) ;
+ break ;
+
+ case cmd_sp_exit: /* NULL_NODE <=> CMD_EOF */
+ parsed->nnode = cmd_node_exit_to(parsed->cnode) ;
+ break ;
+
+ case cmd_sp_enable:
+ parsed->nnode = cmd_auth_specials(context, ENABLE_NODE) ;
+ break ;
+
+ case cmd_sp_configure:
+ parsed->nnode = cmd_auth_specials(context, CONFIG_NODE) ;
+ break ;
+
+ default:
+ zabort("unknown cmd_sp_xxx") ;
+ break ;
+ } ;
+
+ return ret ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Deal with commands which may require AUTH_ENABLE_NODE authentication.
+ *
+ * The rule is that the parser must set what node a given command *will* change
+ * to iff it succeeds.
+ *
+ * So if we can go directly to the target node, must establish that now.
+ *
+ * If cannot go to directly, we set that should go to AUTH_ENABLE_NODE. That
+ * may fail if not in a suitable state to do that -- and issue suitable
+ * message.
+ */
+static node_type_t
+cmd_auth_specials(cmd_context context, node_type_t target)
+{
+ if (!context->can_enable)
+ {
+ /* Can enable if is already ENABLE_NODE or better (this should
+ * not be required -- but does no harm).
+ *
+ * If we are allowed to authenticate, then the authentication
+ * is trivial if there is no password set and we are in
+ * VIEW_NODE !
+ *
+ * Note that if we are in RESTRICTED_NODE, then must authenticate,
+ * which will later be refused if no password is set at the time !
+ */
+ if (context->node >= ENABLE_NODE)
+ context->can_enable = true ;
+ else if (context->can_auth_enable)
+ {
+ /* Can do a*/
+ bool no_pw ;
+
+ VTY_LOCK() ;
+ no_pw = (host.enable == NULL) ;
+ VTY_UNLOCK() ;
+
+ context->can_enable = no_pw && (context->node == VIEW_NODE) ;
+ } ;
+ } ;
+
+ context->onode = context->node ; /* see vty_cmd_auth() */
+ context->tnode = target ; /* see vty_cmd_auth() */
+
+ return context->can_enable ? target : AUTH_ENABLE_NODE ;
+} ;
+
+#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 tokenize the command line */
+ parsed = cmd_parse_init_new(&parsed_s) ;
+ tok_total = cmd_tokenize(parsed, line, node) ;
+
+
+
+
+
+
+
+ /* 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)
+ {
+ 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;
+ } ;
+
+ 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 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
+/*------------------------------------------------------------------------------
+ * 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;
+}
+
+#endif
+
+/*==============================================================================
+ * Command line help support.
+ *
+ *
+ */
+
+static bool cmd_post_last(cmd_parsed parsed) ;
+
+
+/*------------------------------------------------------------------------------
+ * Look out for the following special cases:
+ *
+ * a) before or on '#' or '!'
+ *
+ * '#' 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 '#'.
+ *
+ * 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 const char*
+cmd_help_preflight(cmd_parsed parsed)
+{
+ 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" ;
+ } ;
+
+ return NULL ; /* OK ! */
+} ;
+
+
+
+
+/*------------------------------------------------------------------------------
+ * 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, cmd_context context)
+{
+ cmd_return_code_t ret ;
+
+ /* Parse the line -- allow completion, allow do, allow backing up the tree,
+ * but do not parse for execution.
+ */
+ context->can_enable = true ;
+ context->full_lex = true ;
+ context->parse_execution = false ;
+ context->parse_no_do = false ;
+ context->parse_no_tree = false ;
+ context->parse_strict = false ;
+
+ ret = cmd_parse_command(parsed, context) ;
+
+ 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 : 0..2
+ * *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)
+ {
+ /* 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)
+ {
+ /* 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 (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 if (gap == 1)
+ {
+ *ins = last ? 0 : 1 ;
+ }
+ else
+ {
+ *mov = last ? 0 : 1 ;
+ }
+ } ;
+ }
+
+ 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 ;
+
+ /* now what do we do after the token ? */
+ assert(nt == NULL) ;
+
+ if (!last)
+ {
+ if (gap == 0)
+ *ins = 1 ; /* need space after */
+ else
+ *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 ;
+ }
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * 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) ;
+
+ return ((parsed->cti - parsed->first_command + 1) == cmd->nt_max) ;
+ } ;
+
+ return false ;
+} ;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#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 ((cmd_command = vector_get_item (cmd_v, i)) == NULL)
+ continue ;
+
+ descvec = vector_get_item (cmd_command->items, index);
+ if (descvec == NULL)
+ continue ;
+
+ 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;
+ }
+
+ /* 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 ;
+}
+#endif
+
+/*------------------------------------------------------------------------------
+ * Can the current command be completed ?
+ */
+extern vector
+cmd_complete_command (vector tokens, int node, int *status)
+{
+#if 0
+ 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);
+#endif
+
+ *status = CMD_ERR_NO_MATCH;
+ return NULL;
+} ;
+
+
+
+
+
+
diff --git a/lib/command_parse.h b/lib/command_parse.h
new file mode 100644
index 00000000..5f17d083
--- /dev/null
+++ b/lib/command_parse.h
@@ -0,0 +1,788 @@
+/* Quagga command line parsing -- header
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * Recast and extended: Copyright (C) 2010 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify
+ * it 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_COMMAND_PARSE_H
+#define _ZEBRA_COMMAND_PARSE_H
+
+#include "misc.h"
+
+#include "command_common.h"
+#include "vector.h"
+#include "vio_fifo.h"
+#include "qstring.h"
+#include "qpath.h"
+#include "elstring.h"
+#include "memory.h"
+
+#if 0
+/*==============================================================================
+ * Parsing of tokens
+ */
+
+enum cmd_token_spec
+{
+ cmd_ts_simple = 0,
+
+ cmd_ts_keyword,
+ cmd_ts_number,
+ cmd_ts_word,
+ cmd_ts_option,
+
+ cmd_ts_ipv4_addr,
+ cmd_ts_ipv4_prefix,
+
+ cmd_ts_ipv6_addr,
+ 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 ;
+} ;
+
+/*==============================================================================
+ * 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.
+ *
+ */
+enum match_type
+{
+ mt_no_match,
+
+ mt_eol_partial, /* a partial match ! */
+
+ mt_ipv6_address_partial,
+ mt_ipv6_prefix_partial,
+ mt_ipv4_address_partial,
+ mt_ipv4_prefix_partial,
+
+ 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 ;
+
+/*------------------------------------------------------------------------------
+ * 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] ;
+} ;
+
+/*==============================================================================
+ *
+ */
+
+/* Pipe types */
+enum cmd_pipe_type /* bit significant */
+{
+ cmd_pipe_none = 0,
+
+ cmd_pipe_file = BIT(0),
+ cmd_pipe_shell = BIT(1),
+ cmd_pipe_dev_null = BIT(2), /* out pipe only -- black hole */
+
+ /* For in pipes */
+ cmd_pipe_reflect = BIT(4), /* + option */
+
+ /* For out file pipes */
+ cmd_pipe_append = BIT(4), /* >> */
+
+ /* For out shell pipes */
+ cmd_pipe_shell_only = BIT(4), /* | at start of line */
+} ;
+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_eol = 0, /* all lines have one */
+
+ cmd_tok_simple = BIT( 0),
+
+ cmd_tok_sq = BIT( 4),
+ cmd_tok_dq = BIT( 5), /* '\\' within "..." are not
+ registered separately. */
+ cmd_tok_esc = BIT( 6),
+
+ cmd_tok_incomplete = (cmd_tok_sq | cmd_tok_dq | cmd_tok_esc),
+
+ cmd_tok_in_pipe = BIT( 8), /* token starting '<' */
+ cmd_tok_out_pipe = BIT( 9), /* token starting '>' */
+ cmd_tok_out_shell = BIT(10), /* token starting '|' */
+
+ cmd_tok_comment = BIT(14), /* token starting '!' or '#" */
+} ;
+typedef enum cmd_token_type cmd_token_type_t ;
+
+struct cmd_token
+{
+ cmd_token_type_t type ;
+ usize tp ; /* location of token in the line */
+
+ bool term ; /* token has been '\0' terminated */
+
+ qstring_t qs ; /* token string */
+ elstring_t ot ; /* original token in the line */
+} ;
+
+typedef struct cmd_token* cmd_token ;
+
+/*------------------------------------------------------------------------------
+ * Token vector -- a vector of token objects
+ */
+struct token_vector
+{
+ vector_t body ;
+} ;
+
+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*
+ */
+struct arg_vector
+{
+ vector_t body ;
+} ;
+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
+ *
+ * The current command line is only valid while command is being parsed and
+ * executed -- in between it is nonsense. The pointer can be overwritten at
+ * any time -- responsibility for the memory lies elsewhere.
+ *
+ * The args vector is a set of pointers to the strings in the relevant tokens.
+ * This vector is only valid while command is being parsed and executed -- in
+ * between it too is nonsense. The pointers can be overwritten or discarded at
+ * any time -- responsibility for the memory lies elsewhere.
+ *
+ * The token vectors contain the tokens for the current command being parsed
+ * and executed. After the command has completed these vectors can be
+ * emptied -- see cmd_empty_parsed_tokens() -- but the next command to be
+ * parsed will tidy up before proceeding.
+ */
+struct cmd_parsed
+{
+ cmd_parts_t parts ; /* What parts are present */
+
+ cmd_token_type_t tok_total ; /* What token types are present */
+
+ usize elen ; /* effective length (less trailing spaces) */
+ usize tsp ; /* number of trailing spaces */
+
+ uint num_tokens ; /* number of tokens parsed */
+
+ token_vector tokens ; /* vector of token objects */
+
+ /* 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 */
+ node_type_t nnode ; /* node to set if command succeeds */
+
+ arg_vector args ; /* vector of arguments */
+
+ /* NB: the following are significant only if an error is returned */
+
+ qstring emess ; /* parse error */
+ ssize eloc ; /* error location */
+
+ /* 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 */
+
+ uint first_in_pipe ;
+ uint num_in_pipe ;
+
+ uint first_do ;
+ uint num_do ;
+
+ uint first_command ;
+ uint num_command ;
+
+ uint first_out_pipe ;
+ uint num_out_pipe ;
+
+ uint first_comment ;
+ uint num_comment ;
+
+ /* The following are significant after cmd_token_position() */
+
+ 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 cmd_v ; /* working vector */
+ vector item_v ; /* working vector */
+
+ match_strength_t strongest ;
+ match_type_t best_complete ;
+
+ match_strength_t min_strength ; /* for execution */
+ bool strict ; /* for strict keyword match */
+} ;
+
+enum
+{
+ CMD_PARSED_INIT_ALL_ZEROS = (cmd_pipe_none == 0)
+} ;
+
+typedef struct cmd_parsed cmd_parsed_t[1] ;
+typedef struct cmd_parsed* cmd_parsed ;
+
+/*==============================================================================
+ * This is the stuff that defines the context in which commands are parsed,
+ * and then executed.
+ *
+ * The context lives in the cmd_exec. The CLI has a copy of the context,
+ * which it uses for the prompt and for command line help handling.
+ *
+ * Each time a new vin is pushed, the current context is copied to the current
+ * TOS (before the push).
+ *
+ * Easch time a vin is popped, the context is restored.
+ *
+ * Each time a vin or vout is pushed or popped the context the cmd_exec
+ * out_suppress and reflect flags must be updated.
+ */
+struct cmd_context
+{
+ /* The node between commands. */
+
+ node_type_t node ; /* updated on CMD_SUCCESS */
+
+ /* These properties affect the parsing of command lines. */
+
+ bool full_lex ; /* as required */
+
+ bool parse_execution ; /* parsing to execute */
+
+ bool parse_only ; /* do not execute */
+
+ bool parse_strict ; /* no incomplete keywords */
+ bool parse_no_do ; /* no 'do' commands */
+ bool parse_no_tree ; /* no tree walking */
+
+ bool can_auth_enable ; /* if required */
+ bool can_enable ; /* no (further) password needed */
+
+ /* These properties affect the execution of parsed commands. */
+
+ bool reflect_enabled ; /* per the pipe */
+
+ /* Special for AUTH_ENABLE_NODE -- going from/to */
+
+ node_type_t onode ; /* VIEW_NODE or RESTRICTED_NODE */
+ node_type_t tnode ; /* ENABLE_NODE or CONFIG_NODE */
+
+ /* The current directories. */
+
+ qpath dir_cd ; /* chdir directory */
+ qpath dir_home ; /* "~/" directory */
+ qpath dir_here ; /* "~./" directory */
+} ;
+
+typedef struct cmd_context cmd_context_t[1] ;
+typedef struct cmd_context* cmd_context ;
+
+/*==============================================================================
+ * Prototypes
+ */
+extern void cmd_compile(cmd_command cmd) ;
+extern void cmd_compile_check(cmd_command cmd) ;
+
+extern cmd_parsed cmd_parsed_new(void) ;
+extern cmd_parsed cmd_parsed_free(cmd_parsed parsed) ;
+
+extern bool cmd_is_empty(qstring line) ;
+extern void cmd_tokenize(cmd_parsed parsed, qstring line, bool full_lex) ;
+extern qstring cmd_tokens_concat(cmd_parsed parsed, uint ti, uint nt) ;
+
+extern cmd_return_code_t cmd_parse_command(cmd_parsed parsed,
+ cmd_context context) ;
+
+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, cmd_context context);
+
+extern void cmd_complete_keyword(cmd_parsed parsed,
+ int* pre, int* rep, int* ins, int* mov) ;
+
+extern void cmd_get_parse_error(vio_fifo ebuf, cmd_parsed parsed, uint indent) ;
+
+
+
+
+
+//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 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) ;
+
+/*==============================================================================
+ * Inlines
+ */
+
+/*------------------------------------------------------------------------------
+ * Get the body of the argument vector.
+ */
+Inline const char * const*
+cmd_arg_vector_argv(cmd_parsed parsed)
+{
+ return (const char* const*)vector_body(parsed->args->body) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get length of the body of the argument vector.
+ */
+Inline unsigned
+cmd_arg_vector_argc(cmd_parsed parsed)
+{
+ return vector_length(parsed->args->body) ;
+} ;
+
+#endif /* _ZEBRA_COMMAND_PARSE_H */
diff --git a/lib/command_queue.c b/lib/command_queue.c
index 5f14abae..7946d0e8 100644
--- a/lib/command_queue.c
+++ b/lib/command_queue.c
@@ -18,137 +18,537 @@
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
+#include "misc.h"
-#include <zebra.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"
-/*------------------------------------------------------------------------------
- * Form of message passed with command to be executed
+/*==============================================================================
+ * 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: fix to current state TODO
+ *
+ * 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. TODO
+ *
+ * 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: TODO
+ *
+ * - 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
+ *
+ *
*/
-struct cq_command_args
-{
- enum cmd_return_code ret ; /* return code from command */
-} ;
-MQB_ARGS_SIZE_OK(cq_command_args) ;
+
+
/*------------------------------------------------------------------------------
* 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 void cq_return(mqueue_block mqb, mqb_flag_t flag);
+static int cq_thread(struct thread* thread) ;
+static void cq_process(vty vty, cmd_exec_state_t state, cmd_return_code_t ret) ;
/*------------------------------------------------------------------------------
- * Enqueue vty and argv[] for execution in given nexus.
+ * Enqueue vty to enter the command loop -- must be exec_null
+ *
+ * Expects the vty start up process to have output some cheerful messages,
+ * which is treated as a dummy "login" command. So the loop is entered at
+ * "exec_done_cmd", which will push out the output, deal with the return code,
+ * and loop round to fetch the first command line (if required).
*/
-void
-cq_enqueue(struct vty *vty, qpn_nexus dst)
+extern void
+cq_loop_enter(vty vty, cmd_return_code_t ret)
{
- struct cq_command_args* args ;
- mqueue_block mqb ;
+ VTY_ASSERT_CLI_THREAD() ;
- assert(vty_cli_nexus) ; /* must be running qnexus-wise */
+ assert(vty->exec->state == exec_null) ;
- mqb = mqb_init_new(NULL, cq_action, vty) ;
- args = mqb_get_args(mqb) ;
+ cq_enqueue(vty, vty_cli_nexus, exec_done_cmd, ret) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Continue (resume) at hiatus -- must be exec_hiatus
+ */
+extern void
+cq_continue(vty vty, cmd_return_code_t ret)
+{
+ VTY_ASSERT_CLI_THREAD() ;
- args->ret = CMD_QUEUED ;
+ assert(vty->exec->state == exec_hiatus) ;
- mqueue_enqueue(dst->queue, mqb, 0) ;
-}
+ cq_enqueue(vty, vty_cli_nexus, exec_hiatus, ret) ;
+} ;
/*------------------------------------------------------------------------------
- * Dispatch a command from the message queue block
+ * Enqueue vty for execution in given nexus or issue thread event.
*
- * When done (or revoked/deleted) return the message, so that the sender knows
- * that the command has been dealt with (one way or another).
+ * Will execute in the current exec->state, passing in the given return
+ * code.
+ */
+static void
+cq_enqueue(vty vty, qpn_nexus dst, cmd_exec_state_t state,
+ cmd_return_code_t ret)
+{
+ cmd_exec exec = vty->exec ;
+
+ assert((dst == vty_cli_nexus) || (dst == vty_cmd_nexus)) ;
+
+ exec->locus = dst ;
+ exec->state = state ;
+ exec->ret = ret ;
+
+ if (vty_nexus)
+ {
+ mqueue_block mqb ;
+
+ if ((mqb = exec->cq.mqb) == NULL)
+ mqb = exec->cq.mqb = mqb_init_new(NULL, cq_action, vty) ;
+
+ 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) ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Deal with command message -- in the qpthreads world.
*
- * Note that if the command is revoked the return is set to CMD_QUEUED.
+ * Note that if the command is revoked.... TODO
*/
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) ;
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, vty->exec->state, vty->exec->ret) ;
+ /* do not touch vty on way out */
+
+ /* Revoke action. */
+ mqb_free(mqb) ;
+ vty->exec->cq.mqb = NULL ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Deal with command message -- in the legacy threads world.
+ *
+ * Note that if the command is revoked.... TODO
+ */
+static int
+cq_thread(struct thread* thread)
+{
+ vty vty = THREAD_ARG(thread) ;
+
+ assert(vty->exec->cq.thread == thread) ;
+
+ vty->exec->cq.thread = NULL ;
- mqb_set_action(mqb, cq_return) ;
- mqueue_enqueue(vty_cli_nexus->queue, mqb, 0) ;
+ cq_process(vty, vty->exec->state, vty->exec->ret) ;
+ /* 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.
+ *
+ * This is essentially a coroutine, where the state is in the vty, noting that:
+ *
+ * vty->exec->state is the "return address"
+ * vty->exec->ret is the "return code"
+ *
+ * The command loop runs at the mqueue level in the qpnexus world, or is
+ * driven by "events" in the legacy threads world. The two ways into the
+ * loop are:
+ *
+ * cq_loop_enter() -- called once and once only to start the loop.
*
- * The command line processing for the vty may be stalled (with read mode
- * disabled) waiting for the return from the command.
+ * cq_continue() -- called when some I/O process has completed and
+ * the loop is in hiatus, waiting for I/O.
*
- * 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.
+ * The one way out of the loop is when it hits a hiatus, which is triggered
+ * by any operation not returning CMD_SUCCESS. In hiatus, vty_cmd_hiatus()
+ * is called and given the current return code to deal with. It will
+ * return:
*
- * 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.
+ * CMD_SUCCESS => can proceed to fetch the next command
+ * CMD_WAITING => must leave the command loop, waiting for I/O
+ * CMD_EOF => close the vty and leave command loop
+ * CMD_IO_ERROR => loop back and deal with the error
+ *
+ * The cq_loop_enter() and cq_continue() operations send a message (to the
+ * cli thread, in the mult-pthreaded world), whose action routine is to call
+ * cq_process().
+ *
+ * In the multi-pthreaded world, opening and closing files MUST be done in the
+ * cli thread. Most commands MUST be executed in the cmd thread. So the
+ * command loop is passed between the threads in a number of places. (To keep
+ * life simple, switches back to the cli thread if ends up waiting for I/O.)
+ *
+ * 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.
*/
static void
-cq_return(mqueue_block mqb, mqb_flag_t flag)
+cq_process(vty vty, cmd_exec_state_t state, cmd_return_code_t ret)
{
- struct vty *vty ;
- struct cq_command_args* args ;
+ cmd_exec exec = vty->exec ;
+ cmd_parsed parsed = exec->parsed ;
+
+ /* 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_hiatus,
+ * with the current ret (which will switch to 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(state)
+ {
+ /*--------------------------------------------------------------------
+ * Should not get here in exec_null state.
+ */
+ case exec_null:
+ zabort("exec state == exec_null") ;
+ break ;
+
+ /*--------------------------------------------------------------------
+ * Need another command to execute => in_pipe !
+ *
+ * If multi-threaded: may be in either thread. If vty_cmd_fetch_line()
+ * cannot, for any reason, return a command line, will return something
+ * other than CMD_SUCCESS -- which enters the exec_hiatus.
+ */
+ case exec_fetch:
+ ret = vty_cmd_fetch_line(vty) ;
+
+ if (ret != CMD_SUCCESS)
+ break ;
+
+ if (exec->action->to_do != cmd_do_command)
+ {
+ state = exec_special ;
+ continue ;
+ } ;
+
+ fall_through ; /* with ret == CMD_SUCCESS */
+
+ /*--------------------------------------------------------------------
+ * Parse command in hand
+ *
+ * If multi-threaded: may be in either thread.
+ */
+ cmd_tokenize(parsed, exec->action->line, exec->context->full_lex) ;
+
+ ret = cmd_parse_command(parsed, exec->context) ;
+ 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 ;
+
+ state = exec_done_cmd ;
+ continue ;
+ } ;
+
+ /* reflection now */
+ if (exec->reflect)
+ {
+ ret = vty_cmd_reflect_line(vty) ;
+
+ if ((ret != CMD_SUCCESS) && (ret != CMD_WAITING))
+ break ;
+ } ;
+
+ fall_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)
+ {
+ /* 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 */
+ } ;
+
+ fall_through ; /* with ret == CMD_SUCCESS */
+
+ /*--------------------------------------------------------------------
+ * Execute command in hand (if any)
+ *
+ * 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) ;
+
+ /* Standard command handling */
+#ifdef CONSUMED_TIME_CHECK
+ {
+ RUSAGE_T before;
+ RUSAGE_T after;
+ unsigned long realtime, cputime;
+
+ GETRUSAGE(&before);
+#endif /* CONSUMED_TIME_CHECK */
+
+ 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. */
+ zlog(NULL, LOG_WARNING,
+ "SLOW COMMAND: command took %lums (cpu time %lums): %s",
+ realtime/1000, cputime/1000,
+ qs_make_string(exec->action->line)) ;
+ } ;
+#endif /* CONSUMED_TIME_CHECK */
+ } ;
- assert(vty_cli_nexus) ; /* must be running qnexus-wise */
+ fall_through ;
- vty = mqb_get_arg0(mqb) ;
- args = mqb_get_args(mqb) ;
+ /*--------------------------------------------------------------------
+ * Command has completed -- if successful, push output and loop back
+ * to fetch another command.
+ *
+ * Break if not successful, or push fails or must wait.
+ *
+ * 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_done_cmd:
+ if (ret == CMD_SUCCESS)
+ ret = vty_cmd_success(vty) ;
- /* signal end of command */
- cmd_post_command(vty, args->ret) ;
- vty_queued_result(vty, args->ret) ;
+ if ((ret != CMD_SUCCESS) && (ret != CMD_WAITING))
+ break ; /* stop */
-//if (qpthreads_enabled)
-// qpt_thread_signal(vty_cli_nexus->thread_id, SIGMQUEUE);
+ state = exec_fetch ;
+ continue ;
- mqb_free(mqb);
-}
+ /*--------------------------------------------------------------------
+ * Deal with the "special" commands
+ */
+ case exec_special:
+ if (exec->locus != vty_cli_nexus)
+ return cq_enqueue(vty, vty_cli_nexus, exec_special, ret) ;
+
+ ret = vty_cmd_special(vty) ;
+ if (ret != CMD_SUCCESS)
+ break ;
+
+ state = exec_done_cmd ;
+ continue ;
+
+ /*--------------------------------------------------------------------
+ * Hiatus state -- some return code to be dealt with !
+ */
+ case exec_hiatus:
+ if (exec->locus != vty_cli_nexus)
+ return cq_enqueue(vty, vty_cli_nexus, exec_hiatus, ret) ;
+
+ while (1)
+ {
+ /* Let vty_cmd_hiatus() deal with the return code, and adjust
+ * the stack as required.
+ */
+ ret = vty_cmd_hiatus(vty, ret) ;
+
+ if (ret == CMD_SUCCESS)
+ break ;
+
+ if (ret == CMD_WAITING)
+ {
+ exec->state = exec_hiatus ;
+ return ; /* <<< DONE, pro tem */
+ } ;
+
+ if (ret == CMD_EOF)
+ {
+ exec->state = exec_stopped ;
+ vty_cmd_loop_exit(vty) ;
+
+ return ; /* <<< DONE, permanently */
+ } ;
+
+ if (ret == CMD_IO_ERROR)
+ continue ;
+
+ zabort("invalid return from vty_cmd_hiatus()") ;
+ } ;
+
+ state = exec_fetch ;
+ continue ; /* can fetch, again */
+
+ /*--------------------------------------------------------------------
+ * Should not get here in exec_stopped state.
+ */
+ case exec_stopped:
+ zabort("exec state == exec_exit") ;
+ break ;
+
+ /*----------------------------------------------------------------------
+ * Unknown exec->state !
+ */
+ default:
+ zabort("unknown exec->state") ;
+ break ;
+ } ;
+
+ /* Have broken out of the switch() => exec_hiatus */
+ state = exec_hiatus ;
+ } ;
+} ;
/*------------------------------------------------------------------------------
- * Revoke any messages related to the given VTY -- if running qnexus-wise.
+ * Revoke any message associated with the given vty from the command queue.
*
- * Revokes in vty_cmd_nexus -- so before command is started
- * and in vty_cli_nexus -- so after command has completed
+ * This is used when a VTY_TERMINAL or a VTY_SHELL_SERVER is being closed.
*
- * Can do nothing about any command actually being executed in the
- * vty_cmd_nexus.
+ * See cq_process above for discussion of what messages there may be. At any
+ * time there is at most one message in flight.
+ *
+ * In the single-threaded world (including legacy threads), messages or events
+ * are used to enter or resume the command loop. If a message or event can
+ * be revoked, then the command loop is effectively stopped.
+ *
+ * In the multi-threaded world, messages are used to enter or resume the
+ * command loop, and to transfer it to and from the cli and cmd threads. If
+ * a message can be revoked, the command loop is effectively stopped.
+ *
+ * Note that the revoke does not affect any vty_cli_nexus messages associated
+ * with the vty_io_basic operations.
+ *
+ * Returns: true <=> have revoked a pending message/event
*/
-void
-cq_revoke(struct vty *vty)
+extern bool
+cq_revoke(vty vty)
{
- if (vty_cli_nexus)
+ int ret ;
+
+ VTY_ASSERT_CLI_THREAD_LOCKED() ;
+
+ if (vty_nexus)
{
- mqueue_revoke(vty_cmd_nexus->queue, vty) ;
- mqueue_revoke(vty_cli_nexus->queue, vty) ;
- } ;
-}
+ ret = mqueue_revoke(vty_cli_nexus->queue, vty, 1) ;
+
+ if ((ret == 0) && vty_multi_nexus)
+ ret = mqueue_revoke(vty_cmd_nexus->queue, vty, 1) ;
+ }
+ else
+ ret = thread_cancel_event(vty_master, vty) ;
+
+ /* If revoked message/event, the command loop is stopped. */
+ if (ret != 0)
+ vty->exec->state = exec_stopped ;
+
+ return (ret != 0) ;
+} ;
+
+
diff --git a/lib/command_queue.h b/lib/command_queue.h
index 804dfa1f..0f4863d6 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_loop_enter(vty vty, cmd_return_code_t ret) ;
+extern void cq_continue(vty vty, cmd_return_code_t ret) ;
+extern bool cq_revoke(vty vty) ;
#endif /* COMMAND_QUEUE_H_ */
diff --git a/lib/elstring.c b/lib/elstring.c
new file mode 100644
index 00000000..64756a64
--- /dev/null
+++ b/lib/elstring.c
@@ -0,0 +1,269 @@
+/* Length/String string handling
+ * 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.
+ */
+#include "misc.h"
+#include <ctype.h>
+
+#include "elstring.h"
+#include "memory.h"
+
+/*==============================================================================
+ * Create and free
+ */
+
+/*------------------------------------------------------------------------------
+ * Create a new elstring
+ */
+extern elstring
+els_new(void)
+{
+ return XCALLOC(MTYPE_TMP, sizeof(elstring_t)) ;
+
+ confirm(ELSTRING_INIT_ALL_ZEROS) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Initialise or create a new elstring
+ */
+extern elstring
+els_init_new(elstring els)
+{
+ if (els == NULL)
+ els = els_new() ;
+ else
+ memset(els, 0, sizeof(elstring_t)) ;
+
+ confirm(ELSTRING_INIT_ALL_ZEROS) ;
+
+ return els ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * 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.
+ */
+extern elstring
+els_free(elstring els)
+{
+ if (els != NULL)
+ XFREE(MTYPE_TMP, els) ;
+
+ return NULL ;
+} ;
+
+/*==============================================================================
+ * Various comparisons
+ */
+
+/*------------------------------------------------------------------------------
+ * Compare two elstrings -- returns the usual -ve, 0, +ve cmp result.
+ *
+ * NULL elstring is treated as empty.
+ */
+extern int
+els_cmp(elstring a, elstring b)
+{
+ const uchar* ap ;
+ const uchar* bp ;
+ ulen al, bl ;
+ ulen n ;
+
+ ap = els_body(a) ; /* NULL if a is NULL */
+ bp = els_body(b) ;
+ al = els_len(a) ; /* zero if a is NULL */
+ bl = els_len(b) ;
+
+ n = (al <= bl) ? al : bl ; /* min(al, bl) */
+
+ while (n)
+ {
+ int d = *ap++ - *bp++ ;
+ if (d != 0)
+ return d ;
+ --n ;
+ } ;
+
+ return al < bl ? -1 : (al == bl) ? 0 : +1 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Compare elstring against word.
+ *
+ * Returns: -1 => elstring and word match to end of elstring
+ * 0 => elstring and word match completely
+ * +1 => elstring and word do not match
+ *
+ * NULLs are treated as empty strings.
+ *
+ * An empty elstring will completely match an empty word.
+ * An empty elstring will partly match any non-empty word.
+ */
+extern int
+els_cmp_word(elstring a, const char* w)
+{
+ const uchar* ap, * ep ;
+ ulen al, wl ;
+
+ al = els_len(a) ; /* zero if a is NULL */
+ wl = (w != NULL) ? strlen(w) : 0 ; /* zero if w is NULL */
+
+ if (al > wl)
+ return +1 ; /* no match if longer */
+
+ if (al == 0)
+ return (wl == 0) ? 0 : -1 ; /* exact or partial if empty */
+
+ ap = els_body_nn(a) ;
+
+ /* Neither string is empty */
+
+ if (*ap != *w)
+ return +1 ; /* quick out if no match for 1st char */
+
+ ep = ap + al - 1 ;
+ while (ap < ep)
+ {
+ if (*++ap != *++w)
+ return 1 ;
+ } ;
+
+ return al == wl ? 0 : -1 ; /* full or partial match */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Compare significant parts of two qstrings -- returns the usual -ve, 0, +ve
+ * cmp result.
+ *
+ * By significant, mean excluding leading/trailing isspace() and treating
+ * multiple isspace() as single isspace().
+ *
+ * Compares the 'len' portions of the strings.
+ *
+ * NULL elstring is treated as empty.
+ */
+extern int
+els_cmp_sig(elstring a, elstring b)
+{
+ cpp_t ap ;
+ cpp_t bp ;
+
+ /* Set up pointers and dispense with leading and trailing isspace()
+ *
+ * Dummy up if NULL
+ */
+ els_cpp(ap, a) ; /* NULL if a is NULL */
+
+ while ((ap->p < ap->e) && isspace(*ap->p))
+ ++ap->p ;
+ while ((ap->p < ap->e) && isspace(*(ap->e - 1)))
+ --ap->e ;
+
+ els_cpp(bp, b) ;
+
+ while ((bp->p < bp->e) && isspace(*bp->p))
+ ++bp->p ;
+ while ((bp->p < bp->e) && isspace(*(bp->e - 1)))
+ --bp->e ;
+
+ /* Now set about finding the first difference */
+ while ((ap->p != ap->e) && (bp->p != bp->e))
+ {
+ if (isspace(*ap->p) && isspace(*bp->p))
+ {
+ do { ++ap->p ; } while (isspace(*ap->p)) ;
+ do { ++bp->p ; } while (isspace(*bp->p)) ;
+ } ;
+
+ if (*ap->p != *bp->p)
+ return (*ap->p < *bp->p) ? -1 : +1 ;
+
+ ++ap->p ;
+ ++bp->p ;
+ } ;
+
+ /* No difference before ran out of one or both */
+ if (ap->p != ap->e)
+ return +1 ;
+ else if (bp->p != bp->e)
+ return -1 ;
+ else
+ return 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Are two elstrings equal ? -- returns true if strings equal.
+ */
+extern bool
+els_equal(elstring a, elstring b)
+{
+ const uchar* ap ;
+ const uchar* bp ;
+ ulen n ;
+
+ n = els_len(b) ; /* zero if b is NULL */
+ if (n != els_len(a))
+ return false ;
+
+ ap = els_body(a) ; /* NULL if a is NULL */
+ 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.
+ */
+extern bool
+els_substring(elstring a, elstring b)
+{
+ const uchar* ap ;
+ const uchar* bp ;
+ ulen n ;
+
+ n = els_len(b) ; /* zero if b is NULL */
+ 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 ;
+} ;
+
diff --git a/lib/elstring.h b/lib/elstring.h
new file mode 100644
index 00000000..8ab1c58d
--- /dev/null
+++ b/lib/elstring.h
@@ -0,0 +1,297 @@
+/* Length/String string handling -- header
+ * 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_ELSTRING_H
+#define _ZEBRA_ELSTRING_H
+
+#include "misc.h"
+
+/*==============================================================================
+ * This is some very simple support for strings which are Length/Body
+ * objects.
+ *
+ * NB: this object knows NOTHING about memory allocation etc. The objective
+ * 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
+{
+ union
+ {
+ void* v ; /* may be NULL iff len == 0 */
+ const void* cv ;
+ } body ;
+
+ ulen len ;
+} ;
+
+typedef struct elstring elstring_t[1] ;
+typedef struct elstring* elstring ;
+
+/* Setting an elstring object to all zeros is enough to initialise it to
+ * an empty string.
+ */
+enum
+{
+ ELSTRING_INIT_ALL_ZEROS = true
+} ;
+
+/*==============================================================================
+ * 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 ;
+
+struct cpp
+{
+ 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)
+{
+ return (els != NULL) ? els_body_nn(els) : NULL ;
+} ;
+
+Inline void*
+els_body_nn(elstring els)
+{
+ return els->body.v ;
+} ;
+
+Inline ulen
+els_len(elstring els)
+{
+ return (els != NULL) ? els_len_nn(els) : 0 ;
+} ;
+
+Inline ulen
+els_len_nn(elstring els)
+{
+ return els->len ;
+} ;
+
+Inline void*
+els_end(elstring els)
+{
+ return (els != NULL) ? els_end_nn(els) : NULL ;
+} ;
+
+Inline void*
+els_end_nn(elstring els)
+{
+ return (void*)((char*)els->body.v + els->len);
+} ;
+
+Inline void
+els_pp(pp p, elstring els)
+{
+ if (els != NULL)
+ els_pp_nn(p, els) ;
+ else
+ pp_null(p) ;
+} ;
+
+Inline void
+els_pp_nn(pp p, elstring els)
+{
+ p->p = els->body.v ;
+ p->e = p->p + els->len ;
+} ;
+
+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.
+ *
+ * NB: elstring MUST NOT be NULL.
+ *
+ * NB: treats str == NULL as a zero length string.
+ *
+ * NB: sets "term" unless str == NULL.
+ */
+Inline void
+els_set_nn(elstring els, const void* str)
+{
+ els->body.cv = str ;
+ els->len = (str != NULL) ? strlen(str) : 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set elstring value from ordinary string.
+ *
+ * Creates elstring object if required (ie if els == NULL).
+ *
+ * See: els_set_nn.
+ */
+Inline elstring
+els_set(elstring els, const void* str)
+{
+ if (els == NULL)
+ els = els_new() ;
+
+ els_set_nn(els, str) ;
+
+ return els ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set elstring value from body + length.
+ *
+ * NB: sets term = false.
+ *
+ * NB: elstring MUST NOT be NULL.
+ *
+ * NB: treats str == NULL as a zero length string.
+ */
+Inline void
+els_set_n_nn(elstring els, const void* body, ulen len)
+{
+ els->body.cv = body ;
+ els->len = len ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set elstring value from body + length.
+ *
+ * Creates elstring object if required (ie if els == NULL).
+ *
+ * See els_set_n_nn.
+ */
+Inline elstring
+els_set_n(elstring els, const void* body, ulen len)
+{
+ if (els == NULL)
+ els = els_new() ;
+
+ els_set_n_nn(els, body, len) ;
+
+ return els ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Clear contents of an elstring (if any)
+ *
+ * NB: it is the callers responsibility to free the contents of the elstring.
+ * if that is required, before freeing the elstring itself.
+ */
+Inline void
+els_clear(elstring els)
+{
+ if (els != NULL)
+ {
+ els->body.v = NULL ;
+ els->len = 0 ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set elstring 'len'.
+ *
+ * NB: it is the caller's responsibility to set a valid body !!
+ *
+ * NB: elstring MUST NOT be NULL.
+ */
+Inline void
+els_set_len_nn(elstring els, ulen len)
+{
+ els->len = len ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * 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 ;
+} ;
+
+#endif /* _ZEBRA_ELSTRING_H */
diff --git a/lib/errno_names.c b/lib/errno_names.c
index 7238f1a7..3c1ff23c 100644
--- a/lib/errno_names.c
+++ b/lib/errno_names.c
@@ -18,8 +18,8 @@
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
+#include "misc.h"
-#include <stddef.h>
#include <errno.h>
#include <netdb.h>
diff --git a/lib/getopt.c b/lib/getopt.c
index ec98feac..75cf7c8b 100644
--- a/lib/getopt.c
+++ b/lib/getopt.c
@@ -30,10 +30,6 @@
# define _NO_PROTO
#endif
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
-
#include "zebra.h"
#include <stdio.h>
diff --git a/lib/getopt1.c b/lib/getopt1.c
index dc36fd0e..d5514c33 100644
--- a/lib/getopt1.c
+++ b/lib/getopt1.c
@@ -20,10 +20,6 @@
Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301,
USA. */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
#include "zebra.h"
#include "getopt.h"
diff --git a/lib/heap.c b/lib/heap.c
index 3a7ed360..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"
@@ -79,7 +77,7 @@
*/
static heap
-heap_setup(heap h, int new_vector, vector_index size, heap_cmp* cmp,
+heap_setup(heap h, int new_vector, vector_length_t size, heap_cmp* cmp,
int with_backlink, unsigned int backlink_offset) ;
/* Initialize heap -- allocating heap structure if required.
@@ -166,12 +164,13 @@ heap_re_init(heap h, unsigned int size, heap_cmp* cmp,
* *before* doing this.
*/
heap
-heap_reset(heap h, int free_structure)
+heap_reset(heap h, free_keep_b free_structure)
{
- vector_reset_keep(&h->v) ; /* vector structure is embedded in the heap */
+ vector_reset(h->v, keep_it) ; /* vector structure is embedded in the heap */
+ confirm(free_it == true) ;
if (free_structure)
- XFREE(MTYPE_VECTOR, h) ; /* sets h = NULL */
+ XFREE(MTYPE_VECTOR, h) ; /* sets h = NULL */
return h ;
} ;
@@ -189,9 +188,9 @@ heap_setup(heap h, int new_vector, unsigned int size, heap_cmp* cmp,
h->backlink_offset = backlink_offset ;
if (new_vector)
- vector_init_new(&h->v, size) ;
+ vector_init_new(h->v, size) ;
else
- vector_re_init(&h->v, size) ;
+ vector_re_init(h->v, size) ;
return h ;
} ;
@@ -220,14 +219,14 @@ heap_setup(heap h, int new_vector, unsigned int size, heap_cmp* cmp,
* NB: items are reamed out in no defined order.
*/
p_vector_item
-heap_ream(heap h, int free_structure)
+heap_ream(heap h, free_keep_b free_structure)
{
p_vector_item p_v ;
if (h == NULL)
return NULL ;
- if ((p_v = vector_ream_keep(&h->v)) == NULL)
+ if ((p_v = vector_ream(h->v, keep_it)) == NULL)
heap_reset(h, free_structure) ;
return p_v ;
@@ -247,11 +246,11 @@ heap_pop_item(heap h)
p_vector_item p_v ;
p_vector_item p_x ;
- p_v = vector_pop_item(&h->v) ; /* extract last item, if any */
- if ((p_v == NULL) || (h->v.end == 0))
+ p_v = vector_pop_item(h->v) ; /* extract last item, if any */
+ if ((p_v == NULL) || (h->v->end == 0))
return p_v ; /* done if empty or last was also first */
- p_x = h->v.p_items[0] ; /* this is what we are popping */
+ p_x = h->v->p_items[0] ; /* this is what we are popping */
heap_bubble_down(h, 0, p_v) ; /* reposition what was the last item */
/* updating any backlink */
@@ -296,13 +295,13 @@ void
heap_delete_item(heap h, p_vector_item p_v)
{
p_vector_item p_x ;
- vector_index i ;
+ vector_index_t i ;
i = heap_find_item(h, p_v) ; /* index of item to be deleted */
- p_x = vector_pop_item(&h->v) ; /* extract last item, if any */
+ p_x = vector_pop_item(h->v) ; /* extract last item, if any */
- if (i < h->v.end) /* if not deleting the last item... */
+ if (i < h->v->end) /* if not deleting the last item... */
heap_bubble(h, i, p_x) ; /* ...reinsert what was last, at the delete */
/* position, updating any backlink */
} ;
@@ -322,25 +321,25 @@ heap_delete_item(heap h, p_vector_item p_v)
void
heap_push_vector(heap h, vector v, int move_vector)
{
- vector_index i = h->v.end ;
- vector_index e ;
- vector_index n = v->end ;
+ vector_index_t i = h->v->end ;
+ vector_index_t e ;
+ vector_length_t n = v->end ;
p_vector_item p_v ;
if (move_vector)
- vector_move_append(&h->v, v) ;
+ vector_move_append(h->v, v) ;
else
- vector_copy_append(&h->v, v) ;
+ vector_copy_append(h->v, v) ;
e = i ; /* old end of the heap. */
while (n--) {
- p_v = h->v.p_items[i++] ;
+ p_v = h->v->p_items[i++] ;
if (p_v != NULL)
heap_bubble_up(h, e++, p_v) ; /* move new item into position in heap */
/* setting any backlink */
} ;
- h->v.end = e ; /* new end of heap */
+ h->v->end = e ; /* new end of heap */
} ;
/* Pop given heap to vector -- creating vector if required (v == NULL).
@@ -363,8 +362,8 @@ heap_push_vector(heap h, vector v, int move_vector)
vector
heap_pop_vector(vector v, heap h, int move_heap)
{
- vector_index n = h->v.end ;
- vector_index i ;
+ vector_length_t n = h->v->end ;
+ vector_index_t i ;
v = vector_re_init(v, n) ; /* guarantees >= 'n' items in vector */
v->end = n ;
@@ -373,7 +372,7 @@ heap_pop_vector(vector v, heap h, int move_heap)
v->p_items[i] = heap_pop_item(h) ;
if (!move_heap)
- vector_copy_here(&h->v, v) ; /* fully sorted is also heap ordered ! */
+ vector_copy_here(h->v, v) ; /* fully sorted is also heap ordered ! */
return v ;
} ;
@@ -401,11 +400,11 @@ heap_pop_vector(vector v, heap h, int move_heap)
*
* Note that this sets the backlink on the given item.
*/
-private void
-heap_bubble(heap h, vector_index i, p_vector_item p_v)
+Private void
+heap_bubble(heap h, vector_index_t i, p_vector_item p_v)
{
/* If this is < parent, we bubble upwards. */
- if ((i != 0) && (h->cmp(&p_v, &h->v.p_items[HEAP_UP(i)]) < 0))
+ if ((i != 0) && (h->cmp(&p_v, h->v->p_items[HEAP_UP(i)]) < 0))
heap_bubble_up(h, i, p_v) ;
/* Otherwise we try bubbling downwards. */
else
@@ -421,11 +420,11 @@ heap_bubble(heap h, vector_index i, p_vector_item p_v)
* v.end at all. So this can be used to work along a vector and bring
* items into heap order.
*/
-private void
-heap_bubble_up(heap h, vector_index i, p_vector_item p_v)
+Private void
+heap_bubble_up(heap h, vector_index_t i, p_vector_item p_v)
{
- p_vector_item* ha = h->v.p_items ; /* underlying array */
- vector_index ip ; /* index of parent */
+ p_vector_item* ha = h->v->p_items ; /* underlying array */
+ vector_index_t ip ; /* index of parent */
p_vector_item p_p ; /* pointer to parent item */
dassert(ha != NULL) ;
@@ -453,16 +452,16 @@ heap_bubble_up(heap h, vector_index i, p_vector_item p_v)
*
* Note that this sets the backlink on the given item.
*/
-private void
-heap_bubble_down(heap h, vector_index i, p_vector_item p_v)
+Private void
+heap_bubble_down(heap h, vector_index_t i, p_vector_item p_v)
{
- vector_index e = h->v.end ; /* end of heap */
- vector_index ic ; /* index of child */
- vector_index is ; /* index of sibling */
- p_vector_item p_c ; /* pointer to child */
- p_vector_item p_s ; /* pointer to sibling */
+ vector_length_t e = h->v->end ; /* end of heap */
+ vector_index_t ic ; /* index of child */
+ vector_index_t is ; /* index of sibling */
+ p_vector_item p_c ; /* pointer to child */
+ p_vector_item p_s ; /* pointer to sibling */
- p_vector_item* ha = h->v.p_items ; /* underlying array */
+ p_vector_item* ha = h->v->p_items ; /* underlying array */
dassert(ha != NULL) ;
while (1)
@@ -497,21 +496,21 @@ heap_bubble_down(heap h, vector_index i, p_vector_item p_v)
} ;
/* Find index of given item in the given heap. */
-private vector_index
+Private vector_index_t
heap_find_item(heap h, p_vector_item p_v)
{
- vector_index i ;
+ vector_index_t i ;
if (h->state & Heap_Has_Backlink)
i = HEAP_BACKLINK(h, p_v) ;
else
{
- for (i = 0 ; i < h->v.end ; ++i)
- if (h->v.p_items[i] == p_v)
+ for (i = 0 ; i < h->v->end ; ++i)
+ if (h->v->p_items[i] == p_v)
return i ;
} ;
- assert((i < h->v.end) && (h->v.p_items[i] == p_v)) ;
+ assert((i < h->v->end) && (h->v->p_items[i] == p_v)) ;
return i ;
} ;
diff --git a/lib/heap.h b/lib/heap.h
index bd984398..9f981c8d 100644
--- a/lib/heap.h
+++ b/lib/heap.h
@@ -17,13 +17,9 @@
#ifndef _ZEBRA_HEAP_H
#define _ZEBRA_HEAP_H
+#include "misc.h"
#include "vector.h"
-/* Macro in case there are particular compiler issues. */
-#ifndef Inline
- #define Inline static inline
-#endif
-
/*==============================================================================
* Data structures etc.
*/
@@ -34,7 +30,7 @@ enum heap_state {
Heap_Has_Backlink = 0x01, /* Set if backlink set */
} ;
-typedef vector_index heap_backlink_t ;
+typedef vector_index_t heap_backlink_t ;
typedef struct heap* heap ;
@@ -45,7 +41,7 @@ struct heap
enum heap_state state ;
unsigned int backlink_offset ;
- struct vector v ;
+ vector_t v ;
} ;
/*==============================================================================
@@ -66,17 +62,8 @@ extern heap heap_re_init(heap h, unsigned int size, heap_cmp* cmp,
#define heap_re_init_backlinked(h, size, cmp, offset) \
heap_re_init(h, size, cmp, 1, offset)
-extern heap heap_reset(heap h, int free_structure) ;
-extern p_vector_item heap_ream(heap h, int free_structure) ;
-
-/* Reset heap and free the heap structure. */
-#define heap_reset_free(h) heap_reset(h, 1)
-/* Reset heap but keep the heap structure. */
-#define heap_reset_keep(h) heap_reset(h, 0)
-/* Ream out heap and free the heap structure. */
-#define heap_ream_free(h) heap_ream(h, 1)
-/* Ream out heap but keep the heap structure. */
-#define heap_ream_keep(h) heap_ream(h, 0)
+extern heap heap_reset(heap h, free_keep_b free_structure) ;
+extern p_vector_item heap_ream(heap h, free_keep_b free_structure) ;
Inline void heap_push_item(heap h, p_vector_item p_v) ;
extern p_vector_item heap_pop_item(heap h) ;
@@ -102,20 +89,16 @@ extern vector heap_pop_vector(vector v, heap h, int move_heap) ;
* This are extern only for use in Inline and other friends
*/
-#ifndef private
- #define private extern
-#endif
-
-private void
-heap_bubble(heap h, vector_index i, p_vector_item p_v) ;
+Private void
+heap_bubble(heap h, vector_index_t i, p_vector_item p_v) ;
-private void
-heap_bubble_up(heap h, vector_index i, p_vector_item p_v) ;
+Private void
+heap_bubble_up(heap h, vector_index_t i, p_vector_item p_v) ;
-private void
-heap_bubble_down(heap h, vector_index i, p_vector_item p_v) ;
+Private void
+heap_bubble_down(heap h, vector_index_t i, p_vector_item p_v) ;
-private vector_index
+Private vector_index_t
heap_find_item(heap h, p_vector_item p_v) ;
/*==============================================================================
@@ -128,7 +111,7 @@ Inline void
heap_push_item(heap h, p_vector_item p_v)
{
dassert(p_v != NULL) ; /* no NULLs, thank you. */
- heap_bubble_up(h, vector_extend_by_1(&h->v), p_v) ;
+ heap_bubble_up(h, vector_extend_by_1(h->v), p_v) ;
} ;
/* Get copy of top heap item (does not pop).
@@ -138,7 +121,7 @@ heap_push_item(heap h, p_vector_item p_v)
Inline p_vector_item
heap_top_item(heap h)
{
- return vector_get_first_item(&h->v) ; /* if any */
+ return vector_get_first_item(h->v) ; /* if any */
} ;
/* Update heap to reflect new value of top item.
diff --git a/lib/if.c b/lib/if.c
index 048ee1fb..23d35054 100644
--- a/lib/if.c
+++ b/lib/if.c
@@ -532,11 +532,12 @@ if_sunwzebra_get (const char *name, size_t nlen)
}
#endif /* SUNOS_5 */
-DEFUN (interface,
- interface_cmd,
- "interface IFNAME",
- "Select an interface to configure\n"
- "Interface's name\n")
+DEFUN_ATTR (interface,
+ interface_cmd,
+ "interface IFNAME",
+ "Select an interface to configure\n"
+ "Interface's name\n",
+ CMD_ATTR_NODE + INTERFACE_NODE)
{
struct interface *ifp;
size_t sl;
@@ -556,7 +557,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 841ce51e..f89f488b 100644
--- a/lib/if.h
+++ b/lib/if.h
@@ -301,12 +301,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..a7929751 100644
--- a/lib/keychain.c
+++ b/lib/keychain.c
@@ -227,18 +227,19 @@ key_delete (struct keychain *keychain, struct key *key)
key_free (key);
}
-DEFUN (key_chain,
- key_chain_cmd,
- "key chain WORD",
- "Authentication key management\n"
- "Key-chain management\n"
- "Key-chain name\n")
+DEFUN_ATTR (key_chain,
+ key_chain_cmd,
+ "key chain WORD",
+ "Authentication key management\n"
+ "Key-chain management\n"
+ "Key-chain name\n",
+ CMD_ATTR_NODE + KEYCHAIN_NODE)
{
struct keychain *keychain;
keychain = keychain_get (argv[0]);
vty->index = keychain;
- vty_set_node(vty, KEYCHAIN_NODE) ;
+ vty->node = KEYCHAIN_NODE ;
return CMD_SUCCESS;
}
@@ -266,11 +267,12 @@ DEFUN (no_key_chain,
return CMD_SUCCESS;
}
-DEFUN (key,
- key_cmd,
- "key <0-2147483647>",
- "Configure a key\n"
- "Key identifier number\n")
+DEFUN_ATTR (key,
+ key_cmd,
+ "key <0-2147483647>",
+ "Configure a key\n"
+ "Key identifier number\n",
+ CMD_ATTR_NODE + KEYCHAIN_KEY_NODE)
{
struct keychain *keychain;
struct key *key;
@@ -281,17 +283,18 @@ 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;
}
-DEFUN (no_key,
- no_key_cmd,
- "no key <0-2147483647>",
- NO_STR
- "Delete a key\n"
- "Key identifier number\n")
+DEFUN_ATTR (no_key,
+ no_key_cmd,
+ "no key <0-2147483647>",
+ NO_STR
+ "Delete a key\n"
+ "Key identifier number\n",
+ CMD_ATTR_NODE + KEYCHAIN_NODE)
{
struct keychain *keychain;
struct key *key;
@@ -309,7 +312,7 @@ DEFUN (no_key,
key_delete (keychain, key);
- vty_set_node(vty, KEYCHAIN_NODE) ;
+ vty->node = KEYCHAIN_NODE ;
return CMD_SUCCESS;
}
@@ -850,16 +853,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..2b5d8128 100644
--- a/lib/keystroke.c
+++ b/lib/keystroke.c
@@ -19,8 +19,7 @@
* Boston, MA 02111-1307, USA.
*/
-#include <stdbool.h>
-#include <string.h>
+#include "misc.h"
#include "keystroke.h"
@@ -28,7 +27,6 @@
#include "memory.h"
#include "mqueue.h"
-#include "zassert.h"
/*==============================================================================
*/
@@ -262,17 +260,19 @@ enum stream_state
kst_iac_option, /* waiting for option (just seen IAC X) */
kst_iac_sub, /* waiting for IAC SE */
} ;
+typedef enum stream_state stream_state_t ;
struct keystroke_state
{
- enum stream_state state ;
- unsigned len ;
- uint8_t raw[keystroke_max_len] ;
+ stream_state_t state ;
+ uint len ;
+ uint8_t raw[keystroke_max_len] ;
} ;
+typedef struct keystroke_state keystroke_state_t ;
struct keystroke_stream
{
- vio_fifo_t fifo ; /* the keystrokes */
+ vio_fifo fifo ; /* the keystrokes */
keystroke_callback* iac_callback ;
void* iac_callback_context ;
@@ -280,14 +280,15 @@ struct keystroke_stream
uint8_t CSI ; /* CSI character value (if any) */
bool eof_met ; /* nothing more to come */
+ bool timed_out ; /* eof because timed out */
bool steal_this ; /* steal current keystroke when complete */
bool iac ; /* last character was an IAC */
- struct keystroke_state in ; /* current keystroke being collected */
+ keystroke_state_t in ; /* current keystroke being collected */
- struct keystroke_state pushed_in ;
+ keystroke_state_t pushed_in ;
/* keystroke interrupted by IAC */
} ;
@@ -301,7 +302,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) ;
@@ -317,7 +319,7 @@ static void keystroke_steal_esc(keystroke steal, keystroke_stream stream,
uint8_t u) ;
static void keystroke_steal_csi(keystroke steal, keystroke_stream stream,
uint8_t u) ;
-static void keystroke_put(keystroke_stream stream, enum keystroke_type type,
+static void keystroke_put(keystroke_stream stream, keystroke_compound_t type,
bool broken, uint8_t* bytes, int len) ;
/*==============================================================================
@@ -353,12 +355,13 @@ keystroke_stream_new(uint8_t csi_char, keystroke_callback* iac_callback,
* iac_callback = NULL -- none
* iac_callback_context = NULL -- none
*
- * eof_met = false -- no EOF yet
- * steal = false -- no stealing set
- * iac = false -- last character was not an IAC
+ * eof_met = false -- no EOF yet
+ * timed_out = false -- not timed out yet
+ * steal = false -- no stealing set
+ * iac = false -- last character was not an IAC
*
- * in.state = kst_null
- * in.len = 0 -- nothing in the buffer
+ * in.state = kst_null
+ * in.len = 0 -- nothing in the buffer
*
* pushed_in.state ) ditto
* pushed_in.len )
@@ -368,7 +371,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) ;
+ stream->fifo = vio_fifo_new(keystroke_buffer_len) ;
stream->CSI = (csi_char != '\0') ? csi_char : 0x1B ;
@@ -385,7 +388,7 @@ keystroke_stream_free(keystroke_stream stream)
{
if (stream != NULL)
{
- vio_fifo_reset_keep(&stream->fifo) ;
+ stream->fifo = vio_fifo_free(stream->fifo) ;
XFREE(MTYPE_KEY_STREAM, stream) ;
} ;
@@ -407,7 +410,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 +431,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) ;
} ;
/*------------------------------------------------------------------------------
@@ -436,14 +439,15 @@ keystroke_stream_eof(keystroke_stream stream)
*
* * discard contents of the stream, including any partial keystroke.
*
- * * set the stream "eof_met".
+ * * set the stream "eof_met" with or without timed_out
*/
extern void
-keystroke_stream_set_eof(keystroke_stream stream)
+keystroke_stream_set_eof(keystroke_stream stream, bool timed_out)
{
- vio_fifo_reset_keep(&stream->fifo) ;
+ vio_fifo_clear(stream->fifo, true) ; /* and clear marks */
stream->eof_met = true ; /* essential information */
+ stream->timed_out = timed_out ; /* variant of eof */
stream->steal_this = false ; /* keep tidy */
stream->iac = false ;
@@ -472,7 +476,7 @@ keystroke_stream_set_eof(keystroke_stream stream)
* If steal != NULL the keystroke will be set to the stolen keystroke. That
* will be type ks_null if nothing was available, and may be knull_eof.
*
- * Note that never steals broken or truncated keystrokes.
+ * Note that never steals broken or truncated keystrokes, or IAC.
*
* Passing len == 0 and ptr == NULL signals EOF to the keystroke_stream.
*
@@ -495,6 +499,7 @@ keystroke_input(keystroke_stream stream, uint8_t* ptr, size_t len,
if ((len == 0) && (ptr == NULL))
{
stream->eof_met = true ;
+ stream->timed_out = false ;
stream->steal_this = false ;
/* Loop to deal with any pending IAC and partial keystroke.
@@ -858,10 +863,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 +874,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 +889,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 +959,7 @@ keystroke_get(keystroke_stream stream, keystroke stroke)
zabort("unknown keystroke type") ;
} ;
- return 1 ;
+ return true ;
} ;
/*------------------------------------------------------------------------------
@@ -962,16 +967,17 @@ 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 ;
- stroke->value = stream->eof_met ? knull_eof : knull_not_eof ;
-
+ stroke->value = stream->eof_met ? (stream->timed_out ? knull_timed_out
+ : knull_eof)
+ : knull_not_eof ;
stroke->flags = 0 ;
stroke->len = 0 ;
- return 0 ;
+ return false ;
} ;
/*------------------------------------------------------------------------------
@@ -983,7 +989,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) ;
@@ -1012,8 +1018,8 @@ keystroke_add_raw(keystroke_stream stream, uint8_t u)
static void
keystroke_put_char(keystroke_stream stream, uint32_t u)
{
- if (u < 0x80)
- vio_fifo_put_byte(&stream->fifo, (uint8_t)u) ;
+ if (u < kf_compound)
+ vio_fifo_put_byte(stream->fifo, (uint8_t)u) ;
else
{
uint8_t buf[4] ;
@@ -1028,7 +1034,7 @@ keystroke_put_char(keystroke_stream stream, uint32_t u)
}
while (u != 0) ;
- keystroke_put(stream, ks_char, 0, p, (buf + 4) - p) ;
+ keystroke_put(stream, ks_char, false, p, (buf + 4) - p) ;
} ;
} ;
@@ -1126,12 +1132,12 @@ keystroke_clear_iac(keystroke_stream stream)
} ;
/*------------------------------------------------------------------------------
- * Store <first> <len> [<bytes>]
+ * Store <first> <len> [<bytes>] -- compound keystroke.
*
* If len == 0, bytes may be NULL
*/
static void
-keystroke_put(keystroke_stream stream, enum keystroke_type type, bool broken,
+keystroke_put(keystroke_stream stream, keystroke_compound_t type, bool broken,
uint8_t* bytes, int len)
{
if (len > keystroke_max_len)
@@ -1140,12 +1146,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 76fba15e..70aa120b 100644
--- a/lib/keystroke.h
+++ b/lib/keystroke.h
@@ -22,19 +22,13 @@
#ifndef _ZEBRA_KEYSTROKE_H
#define _ZEBRA_KEYSTROKE_H
-#include <stddef.h>
-#include <stdint.h>
-#include <stdbool.h>
+#include "misc.h"
#include <arpa/telnet.h>
#include "zassert.h"
#include "vio_fifo.h"
-#ifndef Inline
-#define Inline static inline
-#endif
-
/*==============================================================================
* Keystroke buffering
*/
@@ -53,12 +47,14 @@ enum keystroke_type
ks_type_reserved = 0x0F,
} ;
+typedef enum keystroke_type keystroke_type_t ;
CONFIRM(ks_type_count <= ks_type_reserved) ;
enum keystroke_null
{
knull_not_eof,
- knull_eof
+ knull_eof,
+ knull_timed_out,
};
enum keystroke_flags
@@ -74,22 +70,30 @@ enum keystroke_flags
kf_type_mask = 0x0F, /* extraction of type */
} ;
+typedef enum keystroke_type keystroke_flags_t ;
+
+/* The keystroke type and flags are designed so that they can be packed
+ * together as a single byte, the first of a "compound character".
+ */
+typedef uint8_t keystroke_compound_t ;
-CONFIRM(ks_type_reserved == (enum keystroke_type)kf_type_mask) ;
+CONFIRM(ks_type_reserved == (keystroke_type_t)kf_type_mask) ;
+CONFIRM((kf_compound | kf_flag_mask | kf_type_mask) <= 0xFF) ;
typedef struct keystroke* keystroke ;
typedef struct keystroke_stream* keystroke_stream ;
struct keystroke
{
- enum keystroke_type type ;
- uint8_t flags ; /* the kf_flag_mask flags */
+ keystroke_type_t type ;
+ keystroke_flags_t flags ; /* just the kf_flag_mask bits */
uint32_t value ;
unsigned len ;
uint8_t buf[keystroke_max_len] ;
} ;
+typedef struct keystroke keystroke_t[1] ;
#define keystroke_iac_callback_args void* context, keystroke stroke
typedef bool (keystroke_callback)(keystroke_iac_callback_args) ;
@@ -175,7 +179,7 @@ keystroke_stream_new(uint8_t csi_char, keystroke_callback* iac_callback,
void* iac_callback_context) ;
extern void
-keystroke_stream_set_eof(keystroke_stream stream) ;
+keystroke_stream_set_eof(keystroke_stream stream, bool timed_out) ;
extern keystroke_stream
keystroke_stream_free(keystroke_stream stream) ;
@@ -188,7 +192,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 720b8ca7..0f7e8a02 100644
--- a/lib/list_util.c
+++ b/lib/list_util.c
@@ -18,9 +18,8 @@
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
-
-#include <list_util.h>
-
+#include "misc.h"
+#include "list_util.h"
/*==============================================================================
* Single Base, Single Link
@@ -50,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 876b7b11..f6a01d93 100644
--- a/lib/list_util.h
+++ b/lib/list_util.h
@@ -22,12 +22,7 @@
#ifndef _ZEBRA_LIST_UTIL_H
#define _ZEBRA_LIST_UTIL_H
-#include <stddef.h>
-
-/* Macro in case there are particular compiler issues. */
-#ifndef Inline
- #define Inline static inline
-#endif
+#include "misc.h"
/*------------------------------------------------------------------------------
* Note that the following fell foul of "strict-aliasing":
@@ -47,7 +42,7 @@
* } ;
*
* the assignment to *p_base is, apparently, unacceptable. This works
- * perfectly well as am ordinary function. Using a GNUC extension it is
+ * perfectly well as an ordinary function. Using a GNUC extension it is
* possible to avoid the function call... hence the ugly skips.
*/
#ifdef __GNUC__
@@ -138,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
*
@@ -242,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) \
@@ -511,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 18878094..28a04dc9 100644
--- a/lib/log.c
+++ b/lib/log.c
@@ -25,14 +25,14 @@
#include <zebra.h>
#include "log.h"
-#include "vty.h"
-#include "uty.h"
+#include "log_local.h"
+#include "vty_log.h"
#include "memory.h"
-#include "command.h"
+
#ifndef SUNOS_5
#include <sys/un.h>
#endif
-/* for printstack on solaris */
+/* for printstack on solaris */
#ifdef HAVE_UCONTEXT_H
#include <ucontext.h>
#endif
@@ -40,18 +40,11 @@
#include "qfstring.h"
#include "sigevent.h"
-/* log is protected by the same mutext as vty, see comments in vty.c */
-
-/* prototypes */
-static int uzlog_reset_file (struct zlog *zl);
-static void zlog_abort (const char *mess) __attribute__ ((noreturn));
-static void vzlog (struct zlog *zl, int priority, const char *format, va_list args);
+/*------------------------------------------------------------------------------
+ * Prototypes
+ */
+static void zlog_abort (const char *mess) __attribute__ ((noreturn)) ;
static void uzlog_backtrace(int priority);
-static void uvzlog (struct zlog *zl, int priority, const char *format, va_list args);
-
-static int logfile_fd = -1; /* Used in signal handler. */
-
-struct zlog *zlog_default = NULL;
const char *zlog_proto_names[] =
{
@@ -81,23 +74,317 @@ const char *zlog_priority[] =
NULL,
};
+/*------------------------------------------------------------------------------
+ * Static variables
+ */
+struct zlog* zlog_default = NULL;
+
+struct zlog* zlog_list = NULL ;
+
+static volatile sig_atomic_t max_maxlvl = INT_MAX ;
+
+qpt_mutex_t log_mutex ;
+
+int log_lock_count = 0 ;
+int log_assert_fail = 0 ;
+
/*==============================================================================
- * Time stamp handling -- gettimeofday(), localtime() and strftime().
+ * Log locking and relationship with VTY.
*
- * Maintains a cached form of the current time (under the vty/log mutex), so
- * that can avoid multiple calls of localtime() and strftime() per second.
+ * For pthreads purposes there is a LOG_LOCK() mutex.
*
- * The value from gettimeofday() is in micro-seconds. Can provide timestamp
- * with any number of decimal digits, but at most 6 will be significant.
+ * To support interaction with the VTY:
+ *
+ * a. setting logging configuration.
+ *
+ * b. issuing log messages within the VTY
+ *
+ * the LOG_LOCK() may be collected while VTY_LOCK() -- BUT NOT vice versa !
+ * This is not too difficult, because the logging has no need to call VTY
+ * functions... except for "vty monitor" VTY, so, for that purpose:
+ *
+ * a. for each monitor VTY there is a "monitor_fifo", into which log messages
+ * are place if required. This buffer and some related flags must be
+ * handled under the LOG_LOCK().
+ *
+ * The vty_log() function takes care of this, and takes care of prompting
+ * the vty(s) to output the contents of the fifo.
+ *
+ * This will be called under LOG_LOCK(). It will NOT VTY_LOCK(). It
+ * will return promptly.
+ *
+ * b. once any logging has been handed to the VTY, the logging can forget
+ * about it.
+ *
+ * c. the interface from logging to VTY is in vty_log.h
+ *
+ * The logging system supports some logging once a signal (in particular,
+ * the hard exceptions, such as SIGSEGV) has gone off and the system is being
+ * brought to a dead stop. A separate mechanism is provided for outputting
+ * directly to any vty monitor VTY -- at a file descriptor level !
+ *
+ * The VTY may call logging functions in the same was as any other part of the
+ * system. When interacting with configuration etc, may need to acquire the
+ * LOG_LOCK, etc. The interface from VTY to logging is in log_local.h.
*/
+/*------------------------------------------------------------------------------
+ * Further initialisation for qpthreads.
+ *
+ * This is done during "second stage" initialisation, when all nexuses have
+ * been set up and the qpthread_enabled state established.
+ *
+ * Initialise mutex.
+ *
+ * NB: may be called once and once only.
+ */
+extern void
+log_init_r(void)
+{
+ qpt_mutex_init(log_mutex, qpt_mutex_recursive);
+} ;
+
+/*------------------------------------------------------------------------------
+ * Close down for qpthreads.
+ */
+extern void
+log_finish(void)
+{
+ qpt_mutex_destroy(log_mutex, 0);
+} ;
+
+/*==============================================================================
+ * The actual logging function and creation of the logging lines.
+ *
+ */
+#define TIMESTAMP_FORM "%Y/%m/%d %H:%M:%S"
+
+/* Structure used to hold line for log output -- so that need be generated
+ * just once even if output to multiple destinations.
+ *
+ * Note that the buffer length is a hard limit (including terminating "\n")
+ * Do not wish to malloc any larger buffer while logging.
+ */
+enum { logline_buffer_len = 1008 } ;
+
+struct logline {
+ size_t len ; /* length including either '\r''\n' or '\n' */
+
+ char line[logline_buffer_len]; /* buffer */
+} ;
+
+typedef struct logline logline_t[1] ;
+typedef struct logline* logline ;
+
+static void uvzlog_line(logline ll, struct zlog *zl, int priority,
+ const char *format, va_list va) ;
static void uquagga_timestamp(qf_str qfs, int timestamp_precision) ;
+/*==============================================================================
+ * The main logging function.
+ */
+extern void
+zlog (struct zlog *zl, int priority, const char *format, ...)
+{
+ logline_t ll ;
+
+ /* Decide whether any logging at all is required.
+ *
+ * NB: the max_maxlvl is established every time any change is made to
+ * logging facilities. Those changes are made under LOG_LOCK(), so
+ * only one thread will write to max_max_lvl at any time, and that
+ * will be consistent with the state of all known logging at the
+ * time.
+ *
+ * The max_maxlvl is sig_atomic_t and volatile. So, we assume that:
+ *
+ * a) writing to max_maxlvl is atomic wrt all forms of interrupt.
+ * So the variable cannot exist in a partly written state (with,
+ * say, some bytes of the old value and some of the new).
+ *
+ * b) reading max_maxlvl will either collect the state before some
+ * change to the logging levels, or after.
+ *
+ * If passes the initial test, immediately acquires the LOG_LOCK().
+ *
+ * So, if the logging facilities are being changed, then:
+ *
+ * a) if the level is about to be increased, so the current
+ * priority would pass, then that change is just too late
+ * for this particular logging operation.
+ *
+ * b) if the level is about to be reduced, will get past the
+ * initial test, but will fail the later tests, under the
+ * LOG_LOCK().
+ *
+ * NB: max_maxlvl is statically initialised to INT_MAX !
+ */
+ if (priority > max_maxlvl)
+ return ;
+
+ /* Decide where we are logging to. */
+
+ LOG_LOCK() ;
+
+ if (zl == NULL)
+ {
+ zl = zlog_default ;
+
+ if (zl == NULL)
+ {
+ /* Have to get up very early in the morning to get to here, because
+ * zlog_default should be initialised -- by openzlog() -- very early
+ * indeed.
+ *
+ * So... "log" to stderr.
+ */
+ va_list va;
+ va_start(va, format);
+ uvzlog_line(ll, zl, priority, format, va) ;
+ va_end (va);
+
+ write(fileno(stderr), ll->line, ll->len) ;
+
+ return ;
+ } ;
+ } ;
+
+ /* Step through the logging destinations and log as required. */
+
+ ll->len = 0 ; /* Nothing generated, yet */
+
+ /* Syslog output */
+ if (priority <= zl->emaxlvl[ZLOG_DEST_SYSLOG])
+ {
+ va_list va;
+ va_start(va, format);
+ vsyslog (priority|zlog_default->facility, format, va);
+ va_end(va);
+ }
+
+ /* File output. */
+ if (priority <= zl->emaxlvl[ZLOG_DEST_FILE])
+ {
+ if (ll->len == 0)
+ {
+ va_list va;
+ va_start(va, format);
+ uvzlog_line(ll, zl, priority, format, va) ;
+ va_end (va);
+ } ;
+ write(zl->file_fd, ll->line, ll->len) ;
+ }
+
+ /* stdout output. */
+ if (priority <= zl->emaxlvl[ZLOG_DEST_STDOUT])
+ {
+ if (ll->len == 0)
+ {
+ va_list va;
+ va_start(va, format);
+ uvzlog_line(ll, zl, priority, format, va) ;
+ va_end (va);
+ } ;
+ write(zl->stdout_fd, ll->line, ll->len) ;
+ }
+
+ /* Terminal monitor. */
+ if (priority <= zl->emaxlvl[ZLOG_DEST_MONITOR])
+ {
+ if (ll->len == 0)
+ {
+ va_list va;
+ va_start(va, format);
+ uvzlog_line(ll, zl, priority, format, va) ;
+ va_end (va);
+ } ;
+ vty_log(priority, ll->line, ll->len - 1) ; /* less the '\n' */
+ } ;
+
+ LOG_UNLOCK() ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Preparation of line to send to logging: file, stdout or "monitor" terminals.
+ *
+ * Line ends in '\n', but no terminating '\0'.
+ */
+static void
+uvzlog_line(logline ll, struct zlog *zl, int priority,
+ const char *format, va_list va)
+{
+ const char* q ;
+ qf_str_t qfs ;
+
+ qfs_init(qfs, ll->line, sizeof(ll->line)) ;
+ /* "<time stamp>" */
+ uquagga_timestamp(qfs, (zl != NULL) ? zl->timestamp_precision : 0) ;
+
+ qfs_append_n(qfs, " ", 1) ;
+
+ /* "<priority>: " if required */
+ if ((zl != NULL) && zl->record_priority)
+ {
+ qfs_append(qfs, zlog_priority[priority]) ;
+ qfs_append(qfs, ": ") ;
+ } ;
+
+ /* "<protocol>: " or "unknown: " */
+ if (zl != NULL)
+ q = zlog_proto_names[zl->protocol] ;
+ else
+ q = "unknown" ;
+
+ qfs_append(qfs, q) ;
+ qfs_append(qfs, ": ") ;
+
+ /* Now the log line itself (uses a *copy* of the va_list) */
+ qfs_vprintf(qfs, format, va) ;
+
+ /* Stick '\n' on the end */
+ qfs_append_n(qfs, "\n", 1) ;
+
+ /* Worry about overflow of message */
+ if (qfs_overflow(qfs) != 0)
+ qfs_term_string(qfs, "...\n", sizeof("...\n") - 1) ;
+
+ ll->len = qfs_len(qfs) ;
+} ;
+
/*------------------------------------------------------------------------------
* Fill buffer with current time, to given number of decimal digits.
*
* If given buffer is too small, provides as many characters as possible.
+
+ * Time stamp handling -- gettimeofday(), localtime() and strftime().
+ *
+ * Maintains a cached form of the current time (under the vty/log mutex), so
+ * that can avoid multiple calls of localtime() and strftime() per second.
+ *
+ * The value from gettimeofday() is in micro-seconds. Can provide timestamp
+ * with any number of decimal digits, but at most 6 will be significant.
+ *
+ * Puts a current timestamp in buf and returns the number of characters
+ * written (not including the terminating NUL). The purpose of
+ * this function is to avoid calls to localtime appearing all over the code.
+ * It caches the most recent localtime result and can therefore
+ * avoid multiple calls within the same second.
+ *
+ * The buflen MUST be > 1 and the buffer address MUST NOT be NULL.
+ *
+ * If buflen is too small, writes buflen-1 characters followed by '\0'.
+ *
+ * Time stamp is rendered in the form: %Y/%m/%d %H:%M:%S
*
+ * This has a fixed length (leading zeros are included) of 19 characters
+ * (unless this code is still in use beyond the year 9999 !)
+ *
+ * Which may be followed by "." and a number of decimal digits, usually 1..6.
+ *
+ * So the maximum time stamp is 19 + 1 + 6 = 26. Adding the trailing '\n', and
+ * rounding up for good measure -- buffer size = 32.
+
+
* Returns: number of characters in buffer, not including trailing '\0'.
*
* NB: does no rounding.
@@ -109,18 +396,21 @@ quagga_timestamp(int timestamp_precision, char *buf, size_t buflen)
{
qf_str_t qfs ;
- VTY_LOCK() ;
+ LOG_LOCK() ;
- qfs_init(&qfs, buf, buflen) ;
- uquagga_timestamp(&qfs, timestamp_precision) ;
+ qfs_init(qfs, buf, buflen) ;
+ uquagga_timestamp(qfs, timestamp_precision) ;
- VTY_UNLOCK() ;
- return qfs_len(&qfs) ;
+ LOG_UNLOCK() ;
+ return qfs_len(qfs) ;
}
/*------------------------------------------------------------------------------
* unprotected version for when mutex already held
*/
+
+// used in uvzlog_line
+
static void
uquagga_timestamp(qf_str qfs, int timestamp_precision)
{
@@ -135,7 +425,7 @@ uquagga_timestamp(qf_str qfs, int timestamp_precision)
/* would it be sufficient to use global 'recent_time' here? I fear not... */
gettimeofday(&clock, NULL);
- /* first, we update the cache if the time has changed */
+ /* first, we update the cache if the seconds time has changed */
if (cache.last != clock.tv_sec)
{
struct tm tm;
@@ -174,137 +464,54 @@ uquagga_timestamp(qf_str qfs, int timestamp_precision)
} ;
/*==============================================================================
- * va_list version of zlog
+ *
*/
-static void
-vzlog (struct zlog *zl, int priority, const char *format, va_list args)
+
+void
+_zlog_assert_failed (const char *assertion, const char *file,
+ unsigned int line, const char *function)
{
- VTY_LOCK() ;
- uvzlog(zl, priority, format, args);
- VTY_UNLOCK() ;
+ const static size_t buff_size = 1024;
+ char buff[buff_size];
+ snprintf(buff, buff_size,
+ "Assertion `%s' failed in file %s, line %u, function %s",
+ assertion, file, line, (function ? function : "?"));
+ zlog_abort(buff);
}
-/* va_list version of zlog. Unprotected assumes mutex already held*/
-static void
-uvzlog (struct zlog *zl, int priority, const char *format, va_list va)
+/* Abort with message */
+void
+_zlog_abort_mess (const char *mess, const char *file,
+ unsigned int line, const char *function)
{
- struct logline ll ; /* prepares line for output, here */
-
- VTY_ASSERT_LOCKED() ;
-
- ll.p_nl = NULL ; /* Nothing generated, yet */
-
- /* If zlog is not specified, use default one. */
- if (zl == NULL)
- zl = zlog_default ;
-
- /* When zlog_default is also NULL, use stderr for logging. */
- if (zl == NULL)
- {
- uvzlog_line(&ll, zl, priority, format, va, llt_lf) ;
- write(fileno(stderr), ll.line, ll.len) ;
- }
- else
- {
- /* Syslog output */
- if (priority <= zl->maxlvl[ZLOG_DEST_SYSLOG])
- {
- va_list ac;
- va_copy(ac, va);
- vsyslog (priority|zlog_default->facility, format, ac);
- va_end(ac);
- }
-
- /* File output. */
- if ((priority <= zl->maxlvl[ZLOG_DEST_FILE]) && zl->fp)
- {
- uvzlog_line(&ll, zl, priority, format, va, llt_lf) ;
- write(fileno(zl->fp), ll.line, ll.len) ;
- }
-
- /* stdout output. */
- if (priority <= zl->maxlvl[ZLOG_DEST_STDOUT])
- {
- uvzlog_line(&ll, zl, priority, format, va, llt_lf) ;
- write(fileno(zl->fp), ll.line, ll.len) ;
- }
-
- /* Terminal monitor. */
- if (priority <= zl->maxlvl[ZLOG_DEST_MONITOR])
- uty_log(&ll, zl, priority, format, va) ;
- }
+ const static size_t buff_size = 1024;
+ char buff[buff_size];
+ snprintf(buff, buff_size, "%s, in file %s, line %u, function %s",
+ mess, file, line, (function ? function : "?"));
+ zlog_abort(buff);
}
-/*------------------------------------------------------------------------------
- * Preparation of line to send to logging: file, stdout or "monitor" terminals.
- *
- * Takes copy of va_list before using it, so the va_list is unchanged.
- */
-extern void
-uvzlog_line(struct logline* ll, struct zlog *zl, int priority,
- const char *format, va_list va, enum ll_term term)
+/* Abort with message and errno and strerror() thereof */
+void
+_zlog_abort_errno (const char *mess, const char *file,
+ unsigned int line, const char *function)
{
- char* p ;
- const char* q ;
-
- p = ll->p_nl ;
-
- if (p != NULL)
- {
- /* we have the line -- just need to worry about the crlf state */
- if (term == ll->term)
- return ; /* exit here if all set */
- }
- else
- {
- /* must construct the line */
- qf_str_t qfs ;
- va_list vac ;
-
- qfs_init(&qfs, ll->line, sizeof(ll->line) - 2) ;
- /* leave space for '\n' or '\r''\n' */
- /* "<time stamp>" */
- uquagga_timestamp(&qfs, (zl != NULL) ? zl->timestamp_precision : 0) ;
-
- qfs_append_n(&qfs, " ", 1) ;
-
- /* "<priority>: " if required */
- if ((zl != NULL) && (zl->record_priority))
- {
- qfs_append(&qfs, zlog_priority[priority]) ;
- qfs_append(&qfs, ": ") ;
- } ;
-
- /* "<protocol>: " or "unknown: " */
- if (zl != NULL)
- q = zlog_proto_names[zl->protocol] ;
- else
- q = "unknown" ;
-
- qfs_append(&qfs, q) ;
- qfs_append(&qfs, ": ") ;
-
- /* Now the log line itself */
- va_copy(vac, va);
- qfs_vprintf(&qfs, format, vac) ;
- va_end(vac);
-
- /* Set pointer to where the '\0' is. */
- p = ll->p_nl = qfs_ptr(&qfs) ;
- } ;
-
- /* finish off with '\r''\n''\0' or '\n''\0' as required */
- if (term == llt_crlf)
- *p++ = '\r' ;
-
- if (term != llt_nul)
- *p++ = '\n' ;
-
- *p = '\0' ;
+ _zlog_abort_err(mess, errno, file, line, function);
+}
- ll->len = p - ll->line ;
- ll->term = term ;
-} ;
+/* Abort with message and given error and strerror() thereof */
+void
+_zlog_abort_err (const char *mess, int err, const char *file,
+ unsigned int line, const char *function)
+{
+ const static size_t buff_size = 1024;
+ char buff[buff_size];
+ snprintf(buff, buff_size,
+ "%s, in file %s, line %u, function %s, %s",
+ mess, file, line, (function ? function : "?"),
+ errtoa(err, 0).str);
+ zlog_abort(buff);
+}
/*============================================================================*/
@@ -445,18 +652,18 @@ open_crashlog(void)
#undef CRASHLOG_PREFIX
}
-/* Note: the goal here is to use only async-signal-safe functions. */
-void
-zlog_signal(int signo, const char *action
-#ifdef SA_SIGINFO
- , siginfo_t *siginfo, void *program_counter
-#endif
- )
+/*------------------------------------------------------------------------------
+ * Note: the goal here is to use only async-signal-safe functions.
+ */
+extern void
+zlog_signal(int signo, const char *action, siginfo_t *siginfo,
+ void *program_counter)
{
time_t now;
char buf[sizeof("DEFAULT: Received signal S at T (si_addr 0xP, PC 0xP); aborting...")+100];
char *s = buf;
char *msgstart = buf;
+ int file_fd ;
#define LOC s,buf+sizeof(buf)-s
time(&now);
@@ -490,10 +697,16 @@ zlog_signal(int signo, const char *action
/* N.B. implicit priority is most severe */
#define PRI LOG_CRIT
-#define DUMP(FD) write(FD, buf, s-buf);
+#define DUMP(FD) write(FD, buf, s - buf);
+
/* If no file logging configured, try to write to fallback log file. */
- if ((logfile_fd >= 0) || ((logfile_fd = open_crashlog()) >= 0))
- DUMP(logfile_fd)
+ file_fd = (zlog_default != NULL) ? zlog_default->file_fd : -1 ;
+ if (file_fd < 0)
+ file_fd = open_crashlog() ;
+
+ if (file_fd >= 0)
+ DUMP(file_fd)
+
if (!zlog_default)
DUMP(STDERR_FILENO)
else
@@ -503,7 +716,7 @@ zlog_signal(int signo, const char *action
/* Remove trailing '\n' for monitor and syslog */
*--s = '\0';
if (PRI <= zlog_default->maxlvl[ZLOG_DEST_MONITOR])
- vty_log_fixed(buf,s-buf);
+ vty_log_fixed(buf, s - buf);
if (PRI <= zlog_default->maxlvl[ZLOG_DEST_SYSLOG])
syslog_sigsafe(PRI|zlog_default->facility,msgstart,s-msgstart);
}
@@ -538,6 +751,7 @@ zlog_backtrace_sigsafe(int priority, void *program_counter)
int size;
char buf[100];
char *s, **bt = NULL;
+ int file_fd ;
#define LOC s,buf+sizeof(buf)-s
#ifdef HAVE_GLIBC_BACKTRACE
@@ -568,8 +782,13 @@ zlog_backtrace_sigsafe(int priority, void *program_counter)
s = num_append(LOC,size);
s = str_append(LOC," stack frames:\n");
- if ((logfile_fd >= 0) || ((logfile_fd = open_crashlog()) >= 0))
- DUMP(logfile_fd)
+ file_fd = (zlog_default != NULL) ? zlog_default->file_fd : -1 ;
+ if (file_fd < 0)
+ file_fd = open_crashlog() ;
+
+ if (file_fd >= 0)
+ DUMP(file_fd)
+
if (!zlog_default)
DUMP(STDERR_FILENO)
else
@@ -617,16 +836,16 @@ zlog_backtrace_sigsafe(int priority, void *program_counter)
void
zlog_backtrace(int priority)
{
- VTY_LOCK() ;
+ LOG_LOCK() ;
uzlog_backtrace(priority);
- VTY_UNLOCK() ;
+ LOG_UNLOCK() ;
}
static void
uzlog_backtrace(int priority)
{
#ifndef HAVE_GLIBC_BACKTRACE
- uzlog(NULL, priority, "No backtrace available on this platform.");
+ zlog(NULL, priority, "No backtrace available on this platform.");
#else
void *array[20];
int size, i;
@@ -635,161 +854,79 @@ uzlog_backtrace(int priority)
if (((size = backtrace(array,sizeof(array)/sizeof(array[0]))) <= 0) ||
((size_t)size > sizeof(array)/sizeof(array[0])))
{
- uzlog(NULL, LOG_ERR, "Cannot get backtrace, returned invalid # of frames %d "
+ zlog(NULL, LOG_ERR, "Cannot get backtrace, returned invalid # of frames %d "
"(valid range is between 1 and %lu)",
size, (unsigned long)(sizeof(array)/sizeof(array[0])));
return;
}
- uzlog(NULL, priority, "Backtrace for %d stack frames:", size);
+ zlog(NULL, priority, "Backtrace for %d stack frames:", size);
if (!(strings = backtrace_symbols(array, size)))
{
- uzlog(NULL, LOG_ERR, "Cannot get backtrace symbols (out of memory?)");
+ zlog(NULL, LOG_ERR, "Cannot get backtrace symbols (out of memory?)");
for (i = 0; i < size; i++)
- uzlog(NULL, priority, "[bt %d] %p",i,array[i]);
+ zlog(NULL, priority, "[bt %d] %p",i,array[i]);
}
else
{
for (i = 0; i < size; i++)
- uzlog(NULL, priority, "[bt %d] %s",i,strings[i]);
+ zlog(NULL, priority, "[bt %d] %s",i,strings[i]);
free(strings);
}
#endif /* HAVE_GLIBC_BACKTRACE */
}
-/* unlocked version */
-void
-uzlog (struct zlog *zl, int priority, const char *format, ...)
-{
- va_list args;
-
- va_start(args, format);
- uvzlog (zl, priority, format, args);
- va_end (args);
-}
-
-void
-zlog (struct zlog *zl, int priority, const char *format, ...)
-{
- va_list args;
-
- va_start(args, format);
- vzlog (zl, priority, format, args);
- va_end (args);
-}
-
-#define ZLOG_FUNC(FUNCNAME,PRIORITY) \
-void \
-FUNCNAME(const char *format, ...) \
-{ \
- va_list args; \
- va_start(args, format); \
- vzlog (NULL, PRIORITY, format, args); \
- va_end(args); \
-}
-
-ZLOG_FUNC(zlog_err, LOG_ERR)
-
-ZLOG_FUNC(zlog_warn, LOG_WARNING)
-
-ZLOG_FUNC(zlog_info, LOG_INFO)
-
-ZLOG_FUNC(zlog_notice, LOG_NOTICE)
-
-ZLOG_FUNC(zlog_debug, LOG_DEBUG)
-
-#undef ZLOG_FUNC
-
-#define PLOG_FUNC(FUNCNAME,PRIORITY) \
-void \
-FUNCNAME(struct zlog *zl, const char *format, ...) \
-{ \
- va_list args; \
- va_start(args, format); \
- vzlog (zl, PRIORITY, format, args); \
- va_end(args); \
-}
-
-PLOG_FUNC(plog_err, LOG_ERR)
-
-PLOG_FUNC(plog_warn, LOG_WARNING)
-
-PLOG_FUNC(plog_info, LOG_INFO)
-
-PLOG_FUNC(plog_notice, LOG_NOTICE)
-
-PLOG_FUNC(plog_debug, LOG_DEBUG)
-
-#undef PLOG_FUNC
-
-void
-_zlog_assert_failed (const char *assertion, const char *file,
- unsigned int line, const char *function)
-{
- const static size_t buff_size = 1024;
- char buff[buff_size];
- snprintf(buff, buff_size,
- "Assertion `%s' failed in file %s, line %u, function %s",
- assertion, file, line, (function ? function : "?"));
- zlog_abort(buff);
-}
-
-/* Abort with message */
-void
-_zlog_abort_mess (const char *mess, const char *file,
- unsigned int line, const char *function)
-{
- const static size_t buff_size = 1024;
- char buff[buff_size];
- snprintf(buff, buff_size, "%s, in file %s, line %u, function %s",
- mess, file, line, (function ? function : "?"));
- zlog_abort(buff);
-}
-
-/* Abort with message and errno and strerror() thereof */
-void
-_zlog_abort_errno (const char *mess, const char *file,
- unsigned int line, const char *function)
-{
- _zlog_abort_err(mess, errno, file, line, function);
-}
-
-/* Abort with message and given error and strerror() thereof */
-void
-_zlog_abort_err (const char *mess, int err, const char *file,
- unsigned int line, const char *function)
-{
- const static size_t buff_size = 1024;
- char buff[buff_size];
- snprintf(buff, buff_size,
- "%s, in file %s, line %u, function %s, %s",
- mess, file, line, (function ? function : "?"),
- errtoa(err, 0).str);
- zlog_abort(buff);
-}
+static void uzlog_set_effective_level(struct zlog* zl) ;
static void
zlog_abort (const char *mess)
{
-#if VTY_DEBUG
+#if LOG_DEBUG
/* May not be locked -- but that doesn't matter any more */
- ++vty_lock_count ;
+ ++log_lock_count ;
#endif
- /* Force fallback file logging? */
- if (zlog_default && !zlog_default->fp &&
- ((logfile_fd = open_crashlog()) >= 0) &&
- ((zlog_default->fp = fdopen(logfile_fd, "w")) != NULL))
- zlog_default->maxlvl[ZLOG_DEST_FILE] = LOG_ERR;
+ if (zlog_default != NULL)
+ {
+ if (zlog_default->file_fd < 0)
+ zlog_default->file_fd = open_crashlog() ;
+
+ if (zlog_default->file_fd >= 0)
+ {
+ zlog_default->maxlvl[ZLOG_DEST_FILE] = LOG_ERR;
+ uzlog_set_effective_level(zlog_default) ;
+ } ;
+ } ;
- uzlog(NULL, LOG_CRIT, "%s", mess);
+ zlog(NULL, LOG_CRIT, "%s", mess);
uzlog_backtrace(LOG_CRIT);
zabort_abort();
}
+/*==============================================================================
+ * Opening, closing, setting log levels etc.
+ *
+ */
+static int uzlog_set_file(struct zlog *zl, const char *filename, int level) ;
+static int uzlog_reset_file(struct zlog *zl) ;
+static void uzlog_set_effective_level(struct zlog* zl) ;
-/* Open log stream */
-struct zlog *
+/*------------------------------------------------------------------------------
+ * Get the effective zlog stream.
+ */
+static inline struct zlog* zlog_actual(struct zlog* zl)
+{
+ return zl != NULL ? zl : zlog_default ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Open logging -- create and initialise a struct zlog object.
+ *
+ * Opens a connection to syslog.
+ *
+ * This must be done very early in the morning, before any pthreading starts.
+ */
+extern struct zlog *
openzlog (const char *progname, zlog_proto_t protocol,
int syslog_flags, int syslog_facility)
{
@@ -798,29 +935,55 @@ openzlog (const char *progname, zlog_proto_t protocol,
zl = XCALLOC(MTYPE_ZLOG, sizeof (struct zlog));
- zl->ident = progname;
- zl->protocol = protocol;
- zl->facility = syslog_facility;
+ zl->ident = progname;
+ zl->protocol = protocol;
+ zl->facility = syslog_facility;
zl->syslog_options = syslog_flags;
- /* Set default logging levels. */
- for (i = 0; i < sizeof(zl->maxlvl)/sizeof(zl->maxlvl[0]); i++)
- zl->maxlvl[i] = ZLOG_DISABLED;
- zl->maxlvl[ZLOG_DEST_MONITOR] = LOG_DEBUG;
- zl->default_lvl = LOG_DEBUG;
+ /* Set default logging levels. */
+ for (i = 0 ; i < ZLOG_DEST_COUNT ; ++i)
+ zl->maxlvl[i] = ZLOG_DISABLED ;
+
+ zl->default_lvl = LOG_DEBUG ;
openlog (progname, syslog_flags, zl->facility);
+ zl->syslog = true ; /* have syslog */
+ zl->stdout_fd = fileno(stdout) ; /* assume have stdout */
+ zl->file_fd = -1 ; /* no file, yet */
+ zl->monitors = 0 ; /* no monitors, yet */
+
+ assert(zlog_list == NULL) ; /* can do this once ! */
+ zlog_list = zl ;
+
+ uzlog_set_effective_level(zl) ;
+
return zl;
-}
+} ;
-void
+/*------------------------------------------------------------------------------
+ * Close logging -- destroy struct zlog object.
+ *
+ * Closes connection to syslog and any log file.
+ */
+extern void
closezlog (struct zlog *zl)
{
+ assert((zl == zlog_list) && (zl->next == NULL)) ; /* pro tem */
+
closelog();
+ if (zl->file_fd >= 0)
+ close (zl->file_fd) ;
- if (zl->fp != NULL)
- fclose (zl->fp);
+ if (zl->filename != NULL)
+ free (zl->filename) ;
+
+ zl->syslog = false ;
+ zl->file_fd = -1 ;
+ zl->stdout_fd = -1 ;
+ zl->monitors = 0 ; /* TODO... state of VTY ?? */
+
+ uzlog_set_effective_level(zl) ;
if (zl->filename != NULL)
free (zl->filename);
@@ -828,411 +991,462 @@ closezlog (struct zlog *zl)
XFREE (MTYPE_ZLOG, zl);
}
-/* Called from command.c. */
-void
-zlog_set_level (struct zlog *zl, zlog_dest_t dest, int log_level)
+/*------------------------------------------------------------------------------
+ * Set new logging level for the given destination.
+ *
+ * Update the effective maxlvl for this zlog, and the max_maxlvl for all zlog.
+ */
+extern void
+zlog_set_level (struct zlog *zl, zlog_dest_t dest, int level)
{
- VTY_LOCK() ;
+ LOG_LOCK() ;
- if (zl == NULL)
- zl = zlog_default;
-
- if (zl != NULL)
+ if ((zl = zlog_actual(zl)) != NULL)
{
- zl->maxlvl[dest] = log_level;
+ zl->maxlvl[dest] = level ;
+ uzlog_set_effective_level(zl) ;
}
- VTY_UNLOCK() ;
+ LOG_UNLOCK() ;
}
-int
-zlog_set_file (struct zlog *zl, const char *filename, int log_level)
+/*------------------------------------------------------------------------------
+ * Set new log file: name and level.
+ *
+ * Note that this closes any existing file
+ *
+ * Returns: 0 => OK
+ * errno otherwise.
+ */
+extern int
+zlog_set_file(struct zlog *zl, const char *filename, int level)
{
- FILE *fp;
- mode_t oldumask;
- int result = 1;
+ int err ;
- VTY_LOCK() ;
+ LOG_LOCK() ;
- /* There is opend file. */
- uzlog_reset_file (zl);
+ err = uzlog_set_file(zl, filename, level) ;
- /* Set default zl. */
- if (zl == NULL)
- zl = zlog_default;
+ LOG_UNLOCK() ;
+ return err ;
+}
- if (zl != NULL)
+static int
+uzlog_set_file(struct zlog *zl, const char *filename, int level)
+{
+ int err ;
+
+ LOG_ASSERT_LOCKED() ;
+
+ err = 0 ;
+ if ((zl = zlog_actual(zl)) != NULL)
{
- /* Open file. */
+ mode_t oldumask;
+ int fd ;
+
+ /* Close any existing file */
+ uzlog_reset_file(zl);
+
+ /* Open file making damn sure we get the mode we want ! */
oldumask = umask (0777 & ~LOGFILE_MASK);
- fp = fopen (filename, "a");
- umask(oldumask);
- if (fp == NULL)
- result = 0;
+ fd = open(filename, O_WRONLY | O_APPEND | O_CREAT, LOGFILE_MASK);
+ if (fd < 0)
+ err = errno ;
else
{
- /* Set flags. */
- zl->filename = strdup (filename);
- zl->maxlvl[ZLOG_DEST_FILE] = log_level;
- zl->fp = fp;
- logfile_fd = fileno(fp);
- }
- }
+ /* Set flags. */
+ zl->filename = strdup (filename) ;
+ zl->file_fd = fd ;
+ zl->maxlvl[ZLOG_DEST_FILE] = level;
+
+ uzlog_set_effective_level(zl) ;
+ } ;
- VTY_UNLOCK() ;
- return result;
+ umask(oldumask);
+ } ;
+
+ return err ;
}
-/* Reset opend file. */
-int
-zlog_reset_file (struct zlog *zl)
+/*------------------------------------------------------------------------------
+ * Close any existing log file. Set level to ZLOG_DISABLED.
+ *
+ * Note that this closes any existing file
+ *
+ * Returns: 1
+ */
+extern int
+zlog_reset_file(struct zlog *zl)
{
- int result;
- VTY_LOCK() ;
- result = uzlog_reset_file(zl);
- VTY_UNLOCK() ;
- return result;
+ int ret ;
+ LOG_LOCK() ;
+
+ ret = uzlog_reset_file(zl) ;
+
+ LOG_UNLOCK() ;
+ return ret ;
}
static int
-uzlog_reset_file (struct zlog *zl)
- {
- if (zl == NULL)
- zl = zlog_default;
+uzlog_reset_file(struct zlog *zl)
+{
+ LOG_ASSERT_LOCKED() ;
- if (zl != NULL)
+ if ((zl = zlog_actual(zl)) != NULL)
{
- if (zl->fp)
- fclose (zl->fp);
- zl->fp = NULL;
- logfile_fd = -1;
+ if (zl->file_fd >= 0)
+ close (zl->file_fd) ;
+
+ zl->file_fd = -1 ;
zl->maxlvl[ZLOG_DEST_FILE] = ZLOG_DISABLED;
- if (zl->filename)
- free (zl->filename);
- zl->filename = NULL;
- }
+ if (zl->filename != NULL)
+ free (zl->filename) ;
+
+ zl->filename = NULL ;
+
+ uzlog_set_effective_level(zl) ;
+ } ;
return 1;
}
-/* Reopen log file. */
-int
+/*------------------------------------------------------------------------------
+ * Close and reopen log file -- TODO and the point ??
+ */
+extern int
zlog_rotate (struct zlog *zl)
{
- int level;
- int result = 1;
+ int err ;
+ char* filename ;
- VTY_LOCK() ;
+ filename = NULL ;
+ err = 0 ;
- if (zl == NULL)
- zl = zlog_default;
+ LOG_LOCK() ;
- if (zl != NULL)
+ if ((zl = zlog_actual(zl)) != NULL)
{
- if (zl->fp)
- fclose (zl->fp);
- zl->fp = NULL;
- logfile_fd = -1;
- level = zl->maxlvl[ZLOG_DEST_FILE];
- zl->maxlvl[ZLOG_DEST_FILE] = ZLOG_DISABLED;
-
- if (zl->filename)
+ if (zl->file_fd >= 0)
{
- mode_t oldumask;
- int save_errno;
-
- oldumask = umask (0777 & ~LOGFILE_MASK);
- zl->fp = fopen (zl->filename, "a");
- save_errno = errno;
- umask(oldumask);
- if (zl->fp == NULL)
- {
- /* can't call logging while locked */
- char *fname = strdup(zl->filename);
- uzlog(NULL, LOG_ERR,
- "Log rotate failed: cannot open file %s for append: %s",
- fname, errtoa(save_errno, 0).str);
- free(fname);
- result = -1;
- }
- else
- {
- logfile_fd = fileno(zl->fp);
- zl->maxlvl[ZLOG_DEST_FILE] = level;
- }
- }
- }
- VTY_UNLOCK() ;
- return result;
+ filename = zl->filename ;
+ zl->filename = NULL ; /* co-opt the name */
+
+ err = uzlog_set_file(zl, filename, zl->maxlvl[ZLOG_DEST_FILE]) ;
+ } ;
+ } ;
+
+ LOG_UNLOCK() ;
+
+ if (err != 0)
+ zlog(NULL, LOG_ERR,
+ "Log rotate failed: cannot open file %s for append: %s",
+ filename, errtoa(err, 0).str) ;
+ if (filename != NULL)
+ free(filename) ; /* discard old copy */
+
+ return (err == 0) ? 1 : -1 ;
}
-int
-zlog_get_default_lvl (struct zlog *zl)
+/*------------------------------------------------------------------------------
+ * Increment or decrement the number of monitor terminals.
+ */
+extern void
+uzlog_add_monitor(struct zlog *zl, int count)
{
- int result = LOG_DEBUG;
+ if ((zl = zlog_actual(zl)) != NULL)
+ {
+ zl->monitors += count ;
+ uzlog_set_effective_level(zl) ;
+ } ;
+}
- VTY_LOCK() ;
- if (zl == NULL)
- zl = zlog_default;
+/*------------------------------------------------------------------------------
+ * get the current default level
+ */
+extern int
+zlog_get_default_lvl (struct zlog *zl)
+{
+ int level ;
- if (zl != NULL)
- {
- result = zl->default_lvl;
- }
+ LOG_LOCK() ;
- VTY_UNLOCK() ;
- return result;
+ zl = zlog_actual(zl) ;
+ level = (zl != NULL) ? zl->default_lvl : LOG_DEBUG ;
+
+ LOG_UNLOCK() ;
+ return level ;
}
-void
+/*------------------------------------------------------------------------------
+ * set the default level
+ */
+extern void
zlog_set_default_lvl (struct zlog *zl, int level)
{
- VTY_LOCK() ;
-
- if (zl == NULL)
- zl = zlog_default;
+ LOG_LOCK() ;
- if (zl != NULL)
- {
- zl->default_lvl = level;
- }
+ if ((zl = zlog_actual(zl)) != NULL)
+ zl->default_lvl = level;
- VTY_UNLOCK() ;
+ LOG_UNLOCK() ;
}
-/* Set logging level and default for all destinations */
-void
+/*------------------------------------------------------------------------------
+ * Set default logging level and the level for any destination not ZLOG_DISABLED
+ *
+ * Note that on openzlog(), all destinations are set ZLOG_DISABLED.
+ */
+extern void
zlog_set_default_lvl_dest (struct zlog *zl, int level)
{
- int i;
+ LOG_LOCK() ;
- VTY_LOCK() ;
-
- if (zl == NULL)
- zl = zlog_default;
-
- if (zl != NULL)
+ if ((zl = zlog_actual(zl)) != NULL)
{
+ int i;
+
zl->default_lvl = level;
- for (i = 0; i < ZLOG_NUM_DESTS; i++)
+ for (i = 0; i < ZLOG_DEST_COUNT; i++)
if (zl->maxlvl[i] != ZLOG_DISABLED)
- zl->maxlvl[i] = level;
- }
+ zl->maxlvl[i] = level ;
- VTY_UNLOCK() ;
-}
+ uzlog_set_effective_level(zl) ;
+ } ;
-int
+ LOG_UNLOCK() ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get the current level for the given destination.
+ */
+extern int
zlog_get_maxlvl (struct zlog *zl, zlog_dest_t dest)
{
- int result = ZLOG_DISABLED;
+ int level ;
- VTY_LOCK() ;
+ LOG_LOCK() ;
- if (zl == NULL)
- zl = zlog_default;
-
- if (zl != NULL)
- {
- result = zl->maxlvl[dest];
- }
+ zl = zlog_actual(zl) ;
+ level = (zl != NULL) ? zl->maxlvl[dest] : ZLOG_DISABLED ;
- VTY_UNLOCK() ;
- return result;
+ LOG_UNLOCK() ;
+ return level;
}
-int
+/*------------------------------------------------------------------------------
+ * Get the current facility setting for syslog
+ */
+extern int
zlog_get_facility (struct zlog *zl)
{
- int result = LOG_DAEMON;
+ int facility ;
- VTY_LOCK() ;
+ LOG_LOCK() ;
- if (zl == NULL)
- zl = zlog_default;
-
- if (zl != NULL)
- {
- result = zl->facility;
- }
+ zl = zlog_actual(zl) ;
+ facility = (zl != NULL) ? zl->facility : LOG_DAEMON ;
- VTY_UNLOCK() ;
- return result;
-}
+ LOG_UNLOCK() ;
+ return facility ;
+} ;
-void
+/*------------------------------------------------------------------------------
+ * Set the current facility setting for syslog
+ */
+extern void
zlog_set_facility (struct zlog *zl, int facility)
{
- VTY_LOCK() ;
-
- if (zl == NULL)
- zl = zlog_default;
+ LOG_LOCK() ;
- if (zl != NULL)
- {
- zl->facility = facility;
- }
+ if ((zl = zlog_actual(zl)) != NULL)
+ zl->facility = facility ;
- VTY_UNLOCK() ;
+ LOG_UNLOCK() ;
}
-int
+/*------------------------------------------------------------------------------
+ * Get the record priority setting
+ */
+extern bool
zlog_get_record_priority (struct zlog *zl)
{
- int result = 0;
+ bool priority ;
- VTY_LOCK() ;
+ LOG_LOCK() ;
- if (zl == NULL)
- zl = zlog_default;
+ zl = zlog_actual(zl) ;
+ priority = (zl != NULL) ? zl->record_priority : false ;
- if (zl != NULL)
- {
- result = zl->record_priority;
- }
-
- VTY_UNLOCK() ;
- return result;
+ LOG_UNLOCK() ;
+ return priority ;
}
-void
-zlog_set_record_priority (struct zlog *zl, int record_priority)
+/*------------------------------------------------------------------------------
+ * Set the record priority setting
+ */
+extern void
+zlog_set_record_priority (struct zlog *zl, bool record_priority)
{
- VTY_LOCK() ;
+ LOG_LOCK() ;
- if (zl == NULL)
- zl = zlog_default;
+ if ((zl = zlog_actual(zl)) != NULL)
+ zl->record_priority = record_priority ;
- if (zl != NULL)
- {
- zl->record_priority = record_priority;
- }
- VTY_UNLOCK() ;
+ LOG_UNLOCK() ;
}
-int
+/*------------------------------------------------------------------------------
+ * Get the timestamp precision setting
+ */
+extern int
zlog_get_timestamp_precision (struct zlog *zl)
{
- int result = 0;
+ int precision = 0;
- VTY_LOCK() ;
+ LOG_LOCK() ;
- if (zl == NULL)
- zl = zlog_default;
+ zl = zlog_actual(zl) ;
+ precision = (zl != NULL) ? zl->timestamp_precision : 0 ;
- if (zl != NULL)
- {
- result = zl->timestamp_precision;
- }
- VTY_UNLOCK() ;
- return result;
+ LOG_UNLOCK() ;
+ return precision ;
}
-void
+/*------------------------------------------------------------------------------
+ * Get the timestamp precision setting
+ */
+extern void
zlog_set_timestamp_precision (struct zlog *zl, int timestamp_precision)
{
- VTY_LOCK() ;
+ LOG_LOCK() ;
- if (zl == NULL)
- zl = zlog_default;
-
- if (zl != NULL)
- {
- zl->timestamp_precision = timestamp_precision;
- }
+ if ((zl = zlog_actual(zl)) != NULL)
+ zl->timestamp_precision = timestamp_precision;
- VTY_UNLOCK() ;
+ LOG_UNLOCK() ;
}
-/* returns name of ZLOG_NONE if no zlog given and no default set */
-const char *
+/*------------------------------------------------------------------------------
+ * Get name of the protocol -- name of ZLOG_NONE if no zlog set up yet.
+ */
+extern const char *
zlog_get_proto_name (struct zlog *zl)
{
- const char * result;
- VTY_LOCK() ;
- result = uzlog_get_proto_name(zl);
- VTY_UNLOCK() ;
- return result;
+ const char* name ;
+ LOG_LOCK() ;
+
+ zl = zlog_actual(zl) ;
+ name = zlog_proto_names[(zl != NULL) ? zl->protocol : ZLOG_NONE] ;
+
+ LOG_UNLOCK() ;
+ return name ;
}
-/* unprotected version, assumes mutex held */
-const char *
-uzlog_get_proto_name (struct zlog *zl)
+/*------------------------------------------------------------------------------
+ * caller must free result
+ */
+extern char *
+zlog_get_filename (struct zlog *zl)
{
- zlog_proto_t protocol = ZLOG_NONE;
+ char* name = NULL;
- if (zl == NULL)
- zl = zlog_default;
+ LOG_LOCK() ;
- if (zl != NULL)
- {
- protocol = zl->protocol;
- }
-
- return zlog_proto_names[protocol];
+ zl = zlog_actual(zl) ;
+ name = ((zl != NULL) && (zl->filename != NULL)) ? strdup(zl->filename)
+ : NULL ;
+ LOG_UNLOCK() ;
+ return name ;
}
-/* caller must free result */
-char *
-zlog_get_filename (struct zlog *zl)
+/*------------------------------------------------------------------------------
+ * Get address of ident string
+ */
+extern const char *
+zlog_get_ident (struct zlog *zl)
{
- char * result = NULL;
+ const char* ident ;
- VTY_LOCK() ;
-
- if (zl == NULL)
- zl = zlog_default;
+ LOG_LOCK() ;
- if (zl != NULL && zl->filename != NULL)
- {
- result = strdup(zl->filename);
- }
+ zl = zlog_actual(zl) ;
+ ident = (zl != NULL) ? zl->ident : NULL ;
- VTY_UNLOCK() ;
- return result;
+ LOG_UNLOCK() ;
+ return ident ;
}
-const char *
-zlog_get_ident (struct zlog *zl)
+/*------------------------------------------------------------------------------
+ * logging to a file?
+ */
+extern bool
+zlog_is_file (struct zlog *zl)
{
- const char * result = NULL;
+ bool is_file ;
- VTY_LOCK() ;
+ LOG_LOCK() ;
- if (zl == NULL)
- zl = zlog_default;
+ zl = zlog_actual(zl) ;
+ is_file = (zl != NULL) ? (zl->file_fd >= 0) : false ;
- if (zl != NULL)
- {
- result = zl->ident;
- }
-
- VTY_UNLOCK() ;
- return result;
+ LOG_UNLOCK() ;;
+ return is_file ;
}
-/* logging to a file? */
-int
-zlog_is_file (struct zlog *zl)
+/*------------------------------------------------------------------------------
+ * Setting of emaxlvl for given destination.
+ */
+inline static void
+uzlog_set_emaxlvl(struct zlog *zl, zlog_dest_t dest, bool test)
+{
+ zl->emaxlvl[dest] = test ? zl->maxlvl[dest] : ZLOG_DISABLED ;
+};
+
+/*------------------------------------------------------------------------------
+ * Update effective logging level for the given destination, and max_maxlvl.
+ *
+ * The effective logging level takes into account whether the destination is
+ * enabled or not.
+ */
+static void
+uzlog_set_effective_level(struct zlog *zl)
{
- int result = 0;
+ int emaxlvl ;
- VTY_LOCK() ;
+ LOG_ASSERT_LOCKED() ;
- if (zl == NULL)
- zl = zlog_default;
+ /* Re-establish the emaxlvl for this logging stream. */
+ uzlog_set_emaxlvl(zl, ZLOG_DEST_SYSLOG, zl->syslog ) ;
+ uzlog_set_emaxlvl(zl, ZLOG_DEST_FILE, zl->file_fd >= 0) ;
+ uzlog_set_emaxlvl(zl, ZLOG_DEST_STDOUT, zl->stdout_fd >= 0) ;
+ uzlog_set_emaxlvl(zl, ZLOG_DEST_MONITOR, zl->monitors > 0) ;
+ confirm(ZLOG_DEST_COUNT == 4) ;
- if (zl != NULL)
+ /* Scan all known logging streams, and re-establish max_maxlvl.
+ *
+ * Do not expect there to be many of these, or that we will change them very
+ * often -- so nothing clever, here.
+ */
+ emaxlvl = ZLOG_DISABLED ;
+ zl = zlog_list ;
+ while (zl != NULL)
{
- result = (zl->fp != NULL);
- }
+ uint i ;
+ for (i = 0 ; i < ZLOG_DEST_COUNT ; ++i)
+ if (emaxlvl < zl->emaxlvl[i])
+ emaxlvl = zl->emaxlvl[i] ;
+ zl = zl->next ;
+ } ;
- VTY_UNLOCK() ;;
- return result;
-}
+ max_maxlvl = emaxlvl ;
+} ;
+
+/*==============================================================================
+ *
+ */
/* Message lookup function. */
const char *
diff --git a/lib/log.h b/lib/log.h
index dda773ea..30a81a28 100644
--- a/lib/log.h
+++ b/lib/log.h
@@ -25,7 +25,11 @@
#ifndef _ZEBRA_LOG_H
#define _ZEBRA_LOG_H
+#include "misc.h"
+#include <signal.h>
#include <syslog.h>
+#include <stdio.h>
+#include "vargs.h"
#include "pthread_safe.h"
/* Here is some guidance on logging levels to use:
@@ -59,118 +63,117 @@ typedef enum
} zlog_proto_t;
/* If maxlvl is set to ZLOG_DISABLED, then no messages will be sent
- to that logging destination. */
-#define ZLOG_DISABLED (LOG_EMERG-1)
+ * to that logging destination.
+ */
+enum { ZLOG_DISABLED = LOG_EMERG - 1 } ;
typedef enum
{
ZLOG_DEST_SYSLOG = 0,
+ ZLOG_DEST_FILE,
ZLOG_DEST_STDOUT,
ZLOG_DEST_MONITOR,
- ZLOG_DEST_FILE
+
+ ZLOG_DEST_COUNT /* Number of destinations */
} zlog_dest_t;
-#define ZLOG_NUM_DESTS (ZLOG_DEST_FILE+1)
struct zlog
{
- const char *ident; /* daemon name (first arg to openlog) */
- zlog_proto_t protocol;
- int maxlvl[ZLOG_NUM_DESTS]; /* maximum priority to send to associated
- logging destination */
- int default_lvl; /* maxlvl to use if none is specified */
- FILE *fp;
- char *filename;
- int facility; /* as per syslog facility */
- int record_priority; /* should messages logged through stdio include the
- priority of the message? */
- int syslog_options; /* 2nd arg to openlog */
- int timestamp_precision; /* # of digits of subsecond precision */
-};
+ struct zlog* next ; /* To support multiple logging streams */
+
+ const char *ident; /* daemon name (first arg to openlog) */
+ zlog_proto_t protocol ;
+
+ int maxlvl[ZLOG_DEST_COUNT]; /* maximum priority set */
+ int default_lvl; /* maxlvl to use if none is specified */
+
+ int emaxlvl[ZLOG_DEST_COUNT]; /* effective maximum priority */
+
+ bool syslog ; /* have active syslog */
+ int file_fd ; /* fd for ZLOG_DEST_FILE (if any) */
+ int stdout_fd ; /* fd for ZLOG_DEST_STDOUT */
+ int monitors ; /* count of monitors */
+
+ char *filename; /* for ZLOG_DEST_FILE */
+
+ int facility; /* as per syslog facility */
+ int syslog_options; /* 2nd arg to openlog */
-/* Message structure. */
+ int timestamp_precision; /* # of digits of subsecond precision */
+ bool record_priority; /* should messages logged through stdio
+ include the priority of the message? */
+} ;
+
+/* Message structure. */
struct message
{
int key;
const char *str;
};
-/* module initialization */
+/* module initialization */
extern void zlog_init_r(void);
extern void zlog_destroy_r(void);
-/* Default logging structure. */
+/* Default logging structure. */
extern struct zlog *zlog_default;
-/* Open zlog function */
+/* Open zlog function */
extern struct zlog *openzlog (const char *progname, zlog_proto_t protocol,
int syslog_options, int syslog_facility);
-/* Close zlog function. */
+/* 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. */
+/* Generic function for zlog. */
extern void zlog (struct zlog *zl, int priority, const char *format, ...)
- PRINTF_ATTRIBUTE(3, 4);
-/* assumed locked version for close friends */
-extern void uzlog (struct zlog *zl, int priority, const char *format, ...)
- PRINTF_ATTRIBUTE(3, 4);
-
-/* Handy zlog functions. */
-extern void zlog_err (const char *format, ...) PRINTF_ATTRIBUTE(1, 2);
-extern void zlog_warn (const char *format, ...) PRINTF_ATTRIBUTE(1, 2);
-extern void zlog_info (const char *format, ...) PRINTF_ATTRIBUTE(1, 2);
-extern void zlog_notice (const char *format, ...) PRINTF_ATTRIBUTE(1, 2);
-extern void zlog_debug (const char *format, ...) PRINTF_ATTRIBUTE(1, 2);
+ PRINTF_ATTRIBUTE(3, 4);
+
+/* Handy zlog functions. */
+#define zlog_thus(thus, ...) zlog(zlog_default, thus, __VA_ARGS__)
+#define zlog_err(...) zlog(zlog_default, LOG_ERR, __VA_ARGS__)
+#define zlog_warn(...) zlog(zlog_default, LOG_WARNING, __VA_ARGS__)
+#define zlog_info(...) zlog(zlog_default, LOG_INFO, __VA_ARGS__)
+#define zlog_notice(...) zlog(zlog_default, LOG_NOTICE, __VA_ARGS__)
+#define zlog_debug(...) zlog(zlog_default, LOG_DEBUG, __VA_ARGS__)
/* For bgpd's peer oriented log. */
-extern void plog_err (struct zlog *, const char *format, ...)
- PRINTF_ATTRIBUTE(2, 3);
-extern void plog_warn (struct zlog *, const char *format, ...)
- PRINTF_ATTRIBUTE(2, 3);
-extern void plog_info (struct zlog *, const char *format, ...)
- PRINTF_ATTRIBUTE(2, 3);
-extern void plog_notice (struct zlog *, const char *format, ...)
- PRINTF_ATTRIBUTE(2, 3);
-extern void plog_debug (struct zlog *, const char *format, ...)
- PRINTF_ATTRIBUTE(2, 3);
+#define plog_thus(zl, thus, ...) zlog(zl, thus, __VA_ARGS__)
+#define plog_err(zl, ...) zlog(zl, LOG_ERR, __VA_ARGS__)
+#define plog_warn(zl, ...) zlog(zl, LOG_WARNING, __VA_ARGS__)
+#define plog_info(zl, ...) zlog(zl, LOG_INFO, __VA_ARGS__)
+#define plog_notice(zl, ...) zlog(zl, LOG_NOTICE, __VA_ARGS__)
+#define plog_debug(zl, ...) zlog(zl, LOG_DEBUG, __VA_ARGS__)
/* Set logging level for the given destination. If the log_level
argument is ZLOG_DISABLED, then the destination is disabled.
- This function should not be used for file logging (use zlog_set_file
+ This function should not be used for file logging (use uzlog_set_file
or zlog_reset_file instead). */
-extern void zlog_set_level (struct zlog *zl, zlog_dest_t, int log_level);
+extern void zlog_set_level(struct zlog *zl, zlog_dest_t, int log_level);
/* Set logging to the given filename at the specified level. */
-extern int zlog_set_file (struct zlog *zl, const char *filename, int log_level);
+extern int zlog_set_file(struct zlog *zl, const char *filename, int log_level);
/* Disable file logging. */
-extern int zlog_reset_file (struct zlog *zl);
+extern int zlog_reset_file(struct zlog *zl);
/* Rotate log. */
extern int zlog_rotate (struct zlog *);
/* getters & setters */
-extern int zlog_get_default_lvl (struct zlog *zl);
+extern int zlog_get_default_lvl (struct zlog *zl);
extern void zlog_set_default_lvl (struct zlog *zl, int level);
extern void zlog_set_default_lvl_dest (struct zlog *zl, int level);
-extern int zlog_get_maxlvl (struct zlog *zl, zlog_dest_t dest);
-extern int zlog_get_facility (struct zlog *zl);
+extern int zlog_get_maxlvl (struct zlog *zl, zlog_dest_t dest);
+extern int zlog_get_facility (struct zlog *zl);
extern void zlog_set_facility (struct zlog *zl, int facility);
-extern int zlog_get_record_priority (struct zlog *zl);
-extern void zlog_set_record_priority (struct zlog *zl, int record_priority);
-extern int zlog_get_timestamp_precision (struct zlog *zl);
+extern bool zlog_get_record_priority (struct zlog *zl);
+extern void zlog_set_record_priority (struct zlog *zl, bool record_priority);
+extern int zlog_get_timestamp_precision (struct zlog *zl);
extern void zlog_set_timestamp_precision (struct zlog *zl, int timestamp_precision);
extern const char * zlog_get_ident (struct zlog *zl);
extern char * zlog_get_filename (struct zlog *zl);
-extern int zlog_is_file (struct zlog *zl);
+extern bool zlog_is_file (struct zlog *zl);
extern const char * zlog_get_proto_name (struct zlog *zl);
-extern const char * uzlog_get_proto_name (struct zlog *zl);
/* For hackey massage lookup and check */
#define LOOKUP(x, y) mes_lookup(x, x ## _max, y, "(no item found)")
@@ -187,11 +190,8 @@ extern const char *zlog_proto_names[];
extern const char *safe_strerror(int errnum);
/* To be called when a fatal signal is caught. */
-extern void zlog_signal(int signo, const char *action
-#ifdef SA_SIGINFO
- , siginfo_t *siginfo, void *program_counter
-#endif
- );
+extern void zlog_signal(int signo, const char *action, siginfo_t *siginfo,
+ void *program_counter) ;
/* Ring down the curtain -- turn of SIGABRT handler and abort() */
extern void zabort_abort(void) __attribute__ ((noreturn)) ;
@@ -205,63 +205,6 @@ extern void zlog_backtrace(int priority);
that is logged in addition to the current backtrace. */
extern void zlog_backtrace_sigsafe(int priority, void *program_counter);
-/* Puts a current timestamp in buf and returns the number of characters
- * written (not including the terminating NUL). The purpose of
- * this function is to avoid calls to localtime appearing all over the code.
- * It caches the most recent localtime result and can therefore
- * avoid multiple calls within the same second.
- *
- * The buflen MUST be > 1 and the buffer address MUST NOT be NULL.
- *
- * If buflen is too small, writes buflen-1 characters followed by '\0'.
- *
- * Time stamp is rendered in the form: %Y/%m/%d %H:%M:%S
- *
- * This has a fixed length (leading zeros are included) of 19 characters
- * (unless this code is still in use beyond the year 9999 !)
- *
- * Which may be followed by "." and a number of decimal digits, usually 1..6.
- *
- * So the maximum time stamp is 19 + 1 + 6 = 26. Adding the trailing '\n', and
- * rounding up for good measure -- buffer size = 32.
- */
-#define TIMESTAMP_FORM "%Y/%m/%d %H:%M:%S"
-
-enum { timestamp_buffer_len = 32 } ;
-
-extern size_t quagga_timestamp(int timestamp_precision /* # subsecond digits */,
- char *buf, size_t buflen);
-
-/* Generate line to be logged
- *
- * Structure used to hold line for log output -- so that need be generated
- * just once even if output to multiple destinations.
- *
- * Note that the buffer length is a hard limit (including terminating '\n''\0'
- * or '\r''\n''\0'). Do not wish to malloc any larger buffer while logging.
- */
-enum { logline_buffer_len = 1008 } ;
-enum ll_term
-{
- llt_nul = 0, /* NB: also length of the terminator */
- llt_lf = 1,
- llt_crlf = 2,
-} ;
-
-struct logline {
- char* p_nl ; /* address of the terminator */
-
- enum ll_term term ; /* how line is terminated */
-
- size_t len ; /* length including either '\r''\n' or '\n' */
-
- char line[logline_buffer_len]; /* buffer */
-} ;
-
-extern void
-uvzlog_line(struct logline* ll, struct zlog *zl, int priority,
- const char *format, va_list va, enum ll_term term) ;
-
/* Defines for use in command construction: */
#define LOG_LEVELS "(emergencies|alerts|critical|errors|" \
diff --git a/lib/log_local.h b/lib/log_local.h
new file mode 100644
index 00000000..8e38bf48
--- /dev/null
+++ b/lib/log_local.h
@@ -0,0 +1,155 @@
+/* Logging locking and interface presented to vty et al
+ * Copyright (C) 2011 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_LOG_LOCAL_H
+#define _ZEBRA_LOG_LOCAL_H
+
+#include "misc.h"
+
+#include "qpthreads.h"
+
+/*==============================================================================
+ * This is for access to some things in log.c which are not required
+ * by external users, who use log.h.
+ *
+ * This is for use within the log/command/vty family.
+ *
+ * Should not be used with log.h ! (Except in log.c itself.)
+ *
+ * This may duplicate things published in log.h, but also includes things
+ * which are not intended for "external" use.
+ */
+
+/*==============================================================================
+ * To make logging qpthread safe we use a single mutex.
+ *
+ * A normal mutex is used -- recursion is not expected or required.
+ *
+ * There are some "ulog" functions which assume the mutex is locked.
+ *
+ * It is assumed that while the log_mutex is owned, NO OTHER SIGNIFICANT lock
+ * will be acquired -- in particular, NOT the VTY_LOCK() !!
+ */
+
+extern qpt_mutex_t log_mutex ;
+
+/*------------------------------------------------------------------------------
+ * Sort out LOG_DEBUG.
+ *
+ * Set to 1 if defined, but blank.
+ * Set to QDEBUG if not defined.
+ *
+ * Force to 0 if LOG_NO_DEBUG is defined and not zero.
+ *
+ * So: defaults to same as QDEBUG, but no matter what QDEBUG is set to:
+ *
+ * * can set LOG_DEBUG == 0 to turn off debug
+ * * or set LOG_DEBUG != 0 to turn on debug
+ * * or set LOG_NO_DEBUG != 0 to force debug off
+ */
+
+#ifdef LOG_DEBUG /* If defined, make it 1 or 0 */
+# if IS_BLANK_OPTION(LOG_DEBUG)
+# undef LOG_DEBUG
+# define LOG_DEBUG 1
+# endif
+#else /* If not defined, follow QDEBUG */
+# define LOG_DEBUG QDEBUG
+#endif
+
+#ifdef LOG_NO_DEBUG /* Override, if defined */
+# if IS_NOT_ZERO_OPTION(LOG_NO_DEBUG)
+# undef LOG_DEBUG
+# define LOG_DEBUG 0
+# endif
+#endif
+
+enum { log_debug = LOG_DEBUG } ;
+
+/*------------------------------------------------------------------------------
+ * Locking and related functions.
+ */
+extern int log_lock_count ;
+
+Inline void
+LOG_LOCK(void) /* if is qpthreads_enabled, lock log_mutex */
+{
+ qpt_mutex_lock(log_mutex) ;
+ if (log_debug)
+ ++log_lock_count ;
+} ;
+
+Inline void
+LOG_UNLOCK(void) /* if is qpthreads_enabled, unlock log_mutex */
+{
+ if (log_debug)
+ --log_lock_count ;
+ qpt_mutex_unlock(log_mutex) ;
+} ;
+
+/* For debug (and documentation) purposes, will LOG_ASSERT_LOCKED where that
+ * is required.
+ *
+ * Note that both these checks will pass if !qpthreads_enabled. So can have
+ * code which is called before qpthreads are started up, or which will never
+ * run qpthreaded !
+ */
+
+extern int log_assert_fail ;
+
+Inline void
+LOG_ASSERT_FAILED(void)
+{
+ if (log_assert_fail == 0) ;
+ {
+ log_assert_fail = 1 ;
+ assert(0) ;
+ } ;
+} ;
+
+Inline void
+LOG_ASSERT_LOCKED(void)
+{
+ if (log_debug)
+ if ((log_lock_count == 0) && (qpthreads_enabled))
+ LOG_ASSERT_FAILED() ;
+} ;
+
+
+
+enum { timestamp_buffer_len = 32 } ;
+
+extern size_t quagga_timestamp(int timestamp_precision /* # subsecond digits */,
+ char *buf, size_t buflen);
+
+
+/*==============================================================================
+ * Functions in log.c -- used in any of the log/command/vty
+ */
+struct zlog ;
+
+extern void uzlog_add_monitor(struct zlog *zl, int count) ;
+
+extern void log_init_r(void) ;
+extern void log_finish(void);
+
+
+#endif /* _ZEBRA_LOG_LOCAL_H */
diff --git a/lib/memory.c b/lib/memory.c
index 49b20c14..ef8551cd 100644
--- a/lib/memory.c
+++ b/lib/memory.c
@@ -35,8 +35,8 @@
*/
static qpt_mutex_t memory_mutex;
-#define LOCK qpt_mutex_lock(&memory_mutex);
-#define UNLOCK qpt_mutex_unlock(&memory_mutex);
+#define LOCK qpt_mutex_lock(memory_mutex);
+#define UNLOCK qpt_mutex_unlock(memory_mutex);
static void log_memstats(int log_priority);
@@ -50,8 +50,13 @@ static const struct message mstr [] =
{ 0, NULL },
};
-/* If using the mem_tracker, include it now. */
-
+/*------------------------------------------------------------------------------
+ * Include the memory tracker, if required.
+ *
+ * Makes sure that the tracker is always part of the source -- so kept up to
+ * date -- depending on dead code removal to eliminate overhead when not
+ * used.
+ */
typedef struct mem_tracker* mem_tracker ;
struct mem_tracker
{
@@ -72,9 +77,7 @@ mem_tracker_zeroise(struct mem_tracker* mem)
memset(mem, 0, sizeof(struct mem_tracker)) ;
} ;
-#ifdef MEMORY_TRACKER
#include "mem_tracker.c"
-#endif
/*==============================================================================
* Keeping track of number of allocated objects of given type
@@ -136,9 +139,9 @@ zmalloc (enum MTYPE mtype, size_t size MEMORY_TRACKER_NAME)
else
{
mstat.mt[mtype].alloc++;
-#ifdef MEMORY_TRACKER
- mem_md_malloc(mtype, memory, size, name) ;
-#endif
+ if (memory_tracker)
+ mem_md_malloc(mtype, memory, size, MEMORY_TRACKER_NAME_ARG) ;
+
UNLOCK ;
} ;
@@ -167,9 +170,8 @@ zcalloc (enum MTYPE mtype, size_t size MEMORY_TRACKER_NAME)
else
{
mstat.mt[mtype].alloc++;
-#ifdef MEMORY_TRACKER
- mem_md_malloc(mtype, memory, size, name) ;
-#endif
+ if (memory_tracker)
+ mem_md_malloc(mtype, memory, size, MEMORY_TRACKER_NAME_ARG) ;
UNLOCK ;
} ;
@@ -206,9 +208,8 @@ zrealloc (enum MTYPE mtype, void *ptr, size_t size MEMORY_TRACKER_NAME)
{
if (ptr == NULL)
mstat.mt[mtype].alloc++;
-#ifdef MEMORY_TRACKER
- mem_md_realloc(mtype, ptr, memory, size, name) ;
-#endif
+ if (memory_tracker)
+ mem_md_realloc(mtype, ptr, memory, size, MEMORY_TRACKER_NAME_ARG) ;
UNLOCK ;
} ;
@@ -233,9 +234,8 @@ zfree (enum MTYPE mtype, void *ptr)
assert(mstat.mt[mtype].alloc > 0) ;
mstat.mt[mtype].alloc--;
-#ifdef MEMORY_TRACKER
- mem_md_free(mtype, ptr) ;
-#endif
+ if (memory_tracker)
+ mem_md_free(mtype, ptr) ;
free (ptr);
@@ -257,7 +257,7 @@ zstrdup (enum MTYPE mtype, const char *str MEMORY_TRACKER_NAME)
LOCK ;
- dup = strdup (str);
+ dup = strdup (str ? str : "");
if (dup == NULL)
{
UNLOCK ;
@@ -266,9 +266,9 @@ zstrdup (enum MTYPE mtype, const char *str MEMORY_TRACKER_NAME)
else
{
mstat.mt[mtype].alloc++;
-#ifdef MEMORY_TRACKER
- mem_md_malloc(mtype, dup, strlen(str)+1, name) ;
-#endif
+ if (memory_tracker)
+ mem_md_malloc(mtype, dup, strlen(str)+1, MEMORY_TRACKER_NAME_ARG) ;
+
UNLOCK ;
} ;
@@ -470,11 +470,12 @@ show_memory_type_vty (struct vty *vty, const char* name,
vty_out (vty, "-----------------------------%s", VTY_NEWLINE) ;
vty_out (vty, "%-30s:", name) ;
-#ifdef MEMORY_TRACKER
- show_memory_tracker_detail(vty, mt, alloc) ;
-#else
- vty_out (vty, " %10ld", alloc) ;
-#endif
+
+ if (memory_tracker)
+ show_memory_tracker_detail(vty, mt, alloc) ;
+ else
+ vty_out (vty, " %10ld", alloc) ;
+
vty_out (vty, "%s", VTY_NEWLINE);
} ;
@@ -491,15 +492,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
struct mem_type_tracker mem_tt ;
-#endif
LOCK ;
+
mst = mstat ;
-#ifdef MEMORY_TRACKER
mem_tt = mem_type_tracker ;
-#endif
+
UNLOCK ;
mem_tracker_zeroise(&mem_tot) ;
@@ -526,12 +525,11 @@ show_memory_vty (struct vty *vty, struct memory_list *m, struct mlist* ml,
else
{
alloc = mst.mt[m->index].alloc ;
-#ifdef MEMORY_TRACKER
- mt = &(mem_tt.mt[m->index]) ;
-#else
- mt = &mem_one ;
+ if (memory_tracker)
+ mt = &(mem_tt.mt[m->index]) ;
+ else
+ mt = &mem_one ;
mt->tracked_count = alloc ;
-#endif
mem_tot.malloc_count += mt->malloc_count ;
mem_tot.free_count += mt->free_count ;
@@ -607,23 +605,23 @@ DEFUN_CALL (show_memory_summary,
"Memory statistics\n"
"Summary memory statistics\n")
{
-#ifdef MEMORY_TRACKER
- show_memory_tracker_summary(vty) ;
-#else
- long alloc = 0 ;
- int mtype ;
+ if (memory_tracker)
+ show_memory_tracker_summary(vty) ;
+ else
+ {
+ long alloc = 0 ;
+ int mtype ;
# ifdef HAVE_MALLINFO
- show_memory_mallinfo (vty);
+ show_memory_mallinfo (vty);
# endif /* HAVE_MALLINFO */
- LOCK ;
- for (mtype = 1 ; mtype < MTYPE_MAX ; ++mtype)
- alloc += mstat.mt[mtype].alloc ;
- UNLOCK
- vty_out(vty, "%ld items allocated%s", alloc, VTY_NEWLINE) ;
-
-#endif /* MEMORY_TRACKER */
+ LOCK ;
+ for (mtype = 1 ; mtype < MTYPE_MAX ; ++mtype)
+ alloc += mstat.mt[mtype].alloc ;
+ UNLOCK
+ vty_out(vty, "%ld items allocated%s", alloc, VTY_NEWLINE) ;
+ } ;
return CMD_SUCCESS;
}
@@ -640,9 +638,9 @@ DEFUN_CALL (show_memory_all,
#ifdef HAVE_MALLINFO
needsep |= show_memory_mallinfo (vty);
#endif /* HAVE_MALLINFO */
-#ifdef MEMORY_TRACKER
- needsep |= show_memory_tracker_summary(vty) ;
-#endif
+
+ if (memory_tracker)
+ needsep |= show_memory_tracker_summary(vty) ;
show_memory_vty (vty, NULL, mlists, needsep);
@@ -747,14 +745,14 @@ DEFUN_CALL (show_memory_isis,
void
memory_init_r (void)
{
- qpt_mutex_init(&memory_mutex, qpt_mutex_quagga);
+ qpt_mutex_init(memory_mutex, qpt_mutex_quagga);
}
/* Finished with module */
void
memory_finish (void)
{
- qpt_mutex_destroy(&memory_mutex, 0);
+ qpt_mutex_destroy(memory_mutex, 0);
}
void
diff --git a/lib/memory.h b/lib/memory.h
index 6c95d73a..265a8226 100644
--- a/lib/memory.h
+++ b/lib/memory.h
@@ -21,25 +21,37 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#ifndef _ZEBRA_MEMORY_H
#define _ZEBRA_MEMORY_H
-#include <stddef.h>
-
-/* For pretty printing of memory allocate information. */
-struct memory_list
+#include "misc.h"
+
+/*------------------------------------------------------------------------------
+ * The file "lib/memtypes.h is created automatically (in the make) from
+ * memtypes.c, and contains a large enum which defines all the memory type
+ * names.
+ *
+ * The file memtypes.c contains a number of arrays of struct memory_list, which
+ * map memory type names to a string, for printing. Those arrays are
+ * collected together in an array of struct mlist.
+ */
+struct memory_list /* one per memory type */
{
int index;
const char *format;
};
-struct mlist {
+struct mlist { /* one per class of memory types */
struct memory_list *list;
const char *name;
};
-extern struct mlist mlists[];
+extern struct mlist mlists[]; /* all classes of memory */
#include "lib/memtypes.h"
-/* #define MEMORY_LOG */
+typedef enum MTYPE mtype_t ;
+
+/*------------------------------------------------------------------------------
+ * Option for logging memory operations.
+ */
#ifdef MEMORY_LOG
#define XMALLOC(mtype, size) \
mtype_zmalloc (__FILE__, __LINE__, (mtype), (size))
@@ -56,28 +68,61 @@ extern struct mlist mlists[];
mtype_zstrdup (__FILE__, __LINE__, (mtype), (str))
#else
-#ifdef QDEBUG
-#define MEMORY_TRACKER 1
+/*------------------------------------------------------------------------------
+ * Sort out MEMORY_TRACKER -- option to keep track of memory allocation.
+ *
+ * Set to 1 if defined, but blank.
+ * Set to QDEBUG if not defined.
+ *
+ * Force to 0 if NO_MEMORY_TRACKER is defined and not zero.
+ *
+ * So: defaults to same as QDEBUG, but no matter what QDEBUG is set to:
+ *
+ * * can set MEMORY_TRACKER == 0 to turn off debug
+ * * or set MEMORY_TRACKER != 0 to turn on debug
+ * * or set VTY_NO_DEBUG != to force debug off
+ */
+
+#ifdef MEMORY_TRACKER /* If defined, make it 1 or 0 */
+# if IS_BLANK_OPTION(MEMORY_TRACKER)
+# undef MEMORY_TRACKER
+# define MEMORY_TRACKER 1
+# endif
+#else /* If not defined, follow QDEBUG */
+# define MEMORY_TRACKER QDEBUG
+#endif
+
+#ifdef NO_MEMORY_TRACKER /* Override, if defined. */
+# if IS_NOT_ZERO_OPTION(VTY_NO_DEBUG)
+# undef MEMORY_TRACKER
+# define MEMORY_TRACKER 0
+# endif
#endif
-#ifdef MEMORY_TRACKER
-#define MEMORY_TRACKER_NAME , const char* name
-#define MEMORY_TRACKER_FUNC , __func__
+enum { memory_tracker = MEMORY_TRACKER } ;
+
+#if MEMORY_TRACKER
+#define MEMORY_TRACKER_NAME_ARG name
+#define MEMORY_TRACKER_NAME , const char* MEMORY_TRACKER_NAME_ARG
+#define MEMORY_TRACKER_FUNC , __func__
#else
+#define MEMORY_TRACKER_NAME_ARG "*dummy*"
#define MEMORY_TRACKER_NAME
#define MEMORY_TRACKER_FUNC
#endif
+/*------------------------------------------------------------------------------
+ * The macros used for all Quagga dynamic memory.
+ */
+
#define XMALLOC(mtype, size) zmalloc ((mtype), (size) \
MEMORY_TRACKER_FUNC)
#define XCALLOC(mtype, size) zcalloc ((mtype), (size) \
MEMORY_TRACKER_FUNC)
#define XREALLOC(mtype, ptr, size) zrealloc ((mtype), (ptr), (size) \
MEMORY_TRACKER_FUNC)
-#define XFREE(mtype, ptr) do { \
- zfree ((mtype), (ptr)); \
- ptr = NULL; } \
- while (0)
+#define XFREE(mtype, ptr) do { zfree ((mtype), (ptr)); \
+ ptr = NULL; } while (0)
#define XSTRDUP(mtype, str) zstrdup ((mtype), (str) \
MEMORY_TRACKER_FUNC)
diff --git a/lib/memtypes.c b/lib/memtypes.c
index 98b53209..66d65328 100644
--- a/lib/memtypes.c
+++ b/lib/memtypes.c
@@ -15,7 +15,9 @@
struct memory_list memory_list_lib[] =
{
{ MTYPE_TMP, "Temporary memory" },
- { MTYPE_STRVEC, "String vector" },
+ { MTYPE_STRING, "String (general)" },
+ { 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" },
@@ -43,17 +45,19 @@ 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_MARSHAL, "marshalled commands" },
+ { MTYPE_TOKEN, "Command token" },
{ 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" },
{ MTYPE_QSTRING, "qstring structure" },
- { MTYPE_QSTRING_BODY, "qstring body" },
+ { MTYPE_QPATH, "qpath structure" },
{ MTYPE_QIOVEC, "qiovec structure" },
{ MTYPE_QIOVEC_VEC, "qiovec iovec vector" },
{ MTYPE_IF, "Interface" },
@@ -86,7 +90,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
new file mode 100644
index 00000000..f5f9d9f6
--- /dev/null
+++ b/lib/misc.h
@@ -0,0 +1,163 @@
+/* Miscellaneous basic definitions
+ * Copyright (C) 2010 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * 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_MISC_H
+#define _ZEBRA_MISC_H
+
+/* "zconfig.h" is included at the start of this "misc.h", and at the start
+ * of "zebra.h". This ensures that we get <features.h> defined early, so
+ * that all other #includes get the same set of features.
+ */
+
+#include "zconfig.h"
+
+/* This is horrible... but for some purposes wish to turn *off* __USE_GNU.
+ *
+ * e.g: to persuade <string.h> to give POSIX version of strerror_r !!!
+ */
+#ifdef NO_USE_GNU
+# undef NO_USE_GNU
+# ifdef __USE_GNU
+# define NO_USE_GNU 1
+# undef __USE_GNU
+# endif
+#endif
+
+/* Stuff which we generally expect to have */
+
+#include <string.h>
+#include <limits.h>
+#include <unistd.h>
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "confirm.h"
+#include "zassert.h"
+
+/* Bit number to bit mask */
+#define BIT(b) (1 << b)
+
+/* Just in case there are compiler issues */
+#define Inline static inline
+
+/* For things which have to be made extern -- typically because they are
+ * used by an inline function -- but are not for public consumption.
+ */
+#define Private extern
+
+/* For use in switch/case */
+#define fall_through
+
+/* Other names of true/false */
+enum on_off
+{
+ on = true,
+ off = false
+} ;
+typedef enum on_off on_off_b ;
+
+/* Whether to add or not on lookup. */
+enum add
+{
+ add = true,
+ no_add = false
+} ;
+typedef enum add add_b ;
+
+/* Used in object "reset" functions (destructors) */
+enum free_keep
+{
+ free_it = true,
+ keep_it = false
+} ;
+typedef enum free_keep free_keep_b ;
+
+/* 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 int ssize ;
+typedef int slen ;
+
+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 ;
+
+/* Macros for sexing value of compilation options.
+ *
+ * In particular allow a blank option to be treated as true, and a zero option
+ * to be treated as false.
+ *
+ * NB: the option MUST be defined, and must be decimal numeric !!
+ */
+#define STRING_VALUE_INNER(x) #x
+#define STRING_VALUE(x) STRING_VALUE_INNER(x)
+
+#define IS_BLANK_OPTION(x) IS_BLANK_OPTION_INNER(x)
+#define IS_ZERO_OPTION(x) IS_ZERO_OPTION_INNER(x)
+#define IS_NOT_ZERO_OPTION(x) IS_NOT_OPTION_ZERO_INNER(x)
+
+#define IS_BLANK_OPTION_INNER(x) (1##x##1 == 11)
+#define IS_ZERO_OPTION_INNER(x) (1##x##1 == 101)
+#define IS_NOT_ZERO_OPTION_INNER(x) (1##x##1 != 101)
+
+/* If QDEBUG is defined, make QDEBUG_NAME and set QDEBUG
+ *
+ * Numeric value for QDEBUG: undefined => 0
+ * defined, blank => 1
+ * defined, 0 => 0
+ * defined, other => other
+ *
+ * Template for turning compilation option into a value.
+ */
+#ifdef QDEBUG
+# if IS_BLANK_OPTION(QDEBUG)
+# undef QDEBUG
+# define QDEBUG 1
+# endif
+#else
+# define QDEBUG 0
+#endif
+
+enum { qdebug = QDEBUG } ;
+
+#ifndef QDEBUG_NAME
+# define QDEBUG_NAME STRING_VALUE(QDEBUG)
+#endif
+
+#endif /* _ZEBRA_MISC_H */
diff --git a/lib/miyagi.h b/lib/miyagi.h
index 569d2da9..639b962d 100644
--- a/lib/miyagi.h
+++ b/lib/miyagi.h
@@ -17,9 +17,7 @@
#ifndef _ZEBRA_MIYAGI_H
#define _ZEBRA_MIYAGI_H
-#ifndef Inline
-#define Inline static inline
-#endif
+#include "misc.h"
/*==============================================================================
* Ghastly kludge to discard "const" from pointer
diff --git a/lib/mqueue.c b/lib/mqueue.c
index 8fa9fbd5..f816932b 100644
--- a/lib/mqueue.c
+++ b/lib/mqueue.c
@@ -18,12 +18,10 @@
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
-
-#include <string.h>
+#include "misc.h"
#include "memory.h"
#include "mqueue.h"
-#include "zassert.h"
/*==============================================================================
* These message queues are designed for inter-qpthread communication.
@@ -211,7 +209,7 @@ mqueue_init_new(mqueue_queue mq, enum mqueue_queue_type type)
memset(mq, 0, sizeof(struct mqueue_queue)) ;
if (qpt_freeze_qpthreads_enabled())
- qpt_mutex_init_new(&mq->mutex, qpt_mutex_quagga) ;
+ qpt_mutex_init_new(mq->mutex, qpt_mutex_quagga) ;
/* head, tail and tail_priority set NULL already */
/* count set zero already */
@@ -222,7 +220,7 @@ mqueue_init_new(mqueue_queue mq, enum mqueue_queue_type type)
{
case mqt_cond_unicast:
case mqt_cond_broadcast:
- qpt_cond_init_new(&mq->kick.cond.wait_here, qpt_cond_quagga) ;
+ qpt_cond_init_new(mq->kick.cond.wait_here, qpt_cond_quagga) ;
if (MQUEUE_DEFAULT_INTERVAL != 0)
{
@@ -254,7 +252,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,19 +266,22 @@ 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) ;
- qpt_mutex_destroy_keep(&mq->mutex) ;
+ qpt_mutex_destroy_keep(mq->mutex) ;
switch (mq->type)
{
case mqt_cond_unicast:
case mqt_cond_broadcast:
- qpt_cond_destroy_keep(&mq->kick.cond.wait_here) ;
+ qpt_cond_destroy_keep(mq->kick.cond.wait_here) ;
break;
case mqt_signal_unicast:
@@ -327,13 +328,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 ;
@@ -362,7 +360,7 @@ mqueue_local_reset(mqueue_local_queue lmq, int free_structure)
extern void
mqueue_set_timeout_interval(mqueue_queue mq, qtime_t interval)
{
- qpt_mutex_lock(&mq->mutex) ;
+ qpt_mutex_lock(mq->mutex) ;
dassert( (mq->type == mqt_cond_unicast) ||
(mq->type == mqt_cond_broadcast) ) ;
@@ -370,7 +368,7 @@ mqueue_set_timeout_interval(mqueue_queue mq, qtime_t interval)
mq->kick.cond.interval = interval ;
mq->kick.cond.timeout = (interval > 0) ? qt_add_monotonic(interval)
: 0 ;
- qpt_mutex_unlock(&mq->mutex) ;
+ qpt_mutex_unlock(mq->mutex) ;
} ;
/*==============================================================================
@@ -484,9 +482,12 @@ mqb_re_init(mqueue_block mqb, mqueue_action action, void* arg0)
* NB: it is the caller's responsibility to free the value of any argument that
* requires it.
*/
-extern void
+extern mqueue_block
mqb_free(mqueue_block mqb)
{
+ if (mqb == NULL)
+ return NULL ;
+
if (mqb->argv != NULL)
XFREE(MTYPE_MQUEUE_BLOCK_ARGV, mqb->argv) ;
@@ -497,6 +498,8 @@ mqb_free(mqueue_block mqb)
++mqb_free_count ;
qpt_mutex_unlock(&mqb_mutex) ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ return NULL ;
} ;
/*==============================================================================
@@ -533,12 +536,12 @@ 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) ;
- qpt_mutex_lock(&mq->mutex) ;
+ qpt_mutex_lock(mq->mutex) ;
if (mq->head == NULL)
{
@@ -588,12 +591,12 @@ mqueue_enqueue(mqueue_queue mq, mqueue_block mqb, enum mqb_rank priority)
switch (mq->type)
{
case mqt_cond_unicast:
- qpt_cond_signal(&mq->kick.cond.wait_here) ;
+ qpt_cond_signal(mq->kick.cond.wait_here) ;
--mq->waiters ;
break ;
case mqt_cond_broadcast:
- qpt_cond_broadcast(&mq->kick.cond.wait_here) ;
+ qpt_cond_broadcast(mq->kick.cond.wait_here) ;
mq->waiters = 0 ;
break ;
@@ -612,7 +615,7 @@ mqueue_enqueue(mqueue_queue mq, mqueue_block mqb, enum mqb_rank priority)
} ;
} ;
- qpt_mutex_unlock(&mq->mutex) ;
+ qpt_mutex_unlock(mq->mutex) ;
} ;
/*------------------------------------------------------------------------------
@@ -655,7 +658,7 @@ mqueue_dequeue(mqueue_queue mq, int wait, void* arg)
if (mq == NULL)
return NULL ;
- qpt_mutex_lock(&mq->mutex) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+ qpt_mutex_lock(mq->mutex) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
while (1)
{
@@ -676,13 +679,13 @@ mqueue_dequeue(mqueue_queue mq, int wait, void* arg)
case mqt_cond_unicast: /* Now wait here */
case mqt_cond_broadcast:
if ((arg == NULL) && (mq->kick.cond.interval <= 0))
- qpt_cond_wait(&mq->kick.cond.wait_here, &mq->mutex) ;
+ qpt_cond_wait(mq->kick.cond.wait_here, mq->mutex) ;
else
{
timeout_time = (arg != NULL) ? *(qtime_mono_t*)arg
: mq->kick.cond.timeout ;
- if (qpt_cond_timedwait(&mq->kick.cond.wait_here, &mq->mutex,
+ if (qpt_cond_timedwait(mq->kick.cond.wait_here, mq->mutex,
timeout_time) == 0)
{
/* Timed out -- update timeout time, if required */
@@ -742,7 +745,7 @@ mqueue_dequeue(mqueue_queue mq, int wait, void* arg)
mq->tail_priority = NULL ;
done:
- qpt_mutex_unlock(&mq->mutex) ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+ qpt_mutex_unlock(mq->mutex) ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
return mqb ;
} ;
@@ -755,35 +758,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) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+ 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 +809,30 @@ mqueue_revoke(mqueue_queue mq, void* arg0)
mq->tail_priority = prev ;
--mq->count ;
+ ++did ;
+
+ mqb_dispatch_destroy(mqb) ;
+
+ if (num == 1)
+ break ;
+ else if (num > 1)
+ --num ;
- qpt_mutex_unlock(&mq->mutex) ;
- mqb_dispatch_destroy(mqb) ;
- qpt_mutex_lock(&mq->mutex) ;
+ if (prev == NULL)
+ mqb = mq->head ;
+ else
+ mqb = prev->next ;
}
else
- prev = mqb ;
+ {
+ prev = mqb ;
+ mqb = mqb->next ;
+ } ;
} ;
- qpt_mutex_unlock(&mq->mutex) ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+ qpt_mutex_unlock(mq->mutex) ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ return did ;
} ;
/*------------------------------------------------------------------------------
@@ -827,7 +850,7 @@ mqueue_done_waiting(mqueue_queue mq, mqueue_thread_signal mtsig)
if (!qpthreads_enabled)
return 0 ;
- qpt_mutex_lock(&mq->mutex) ;
+ qpt_mutex_lock(mq->mutex) ;
dassert( (mq->type == mqt_signal_unicast) ||
(mq->type == mqt_signal_broadcast) ) ;
@@ -844,7 +867,7 @@ mqueue_done_waiting(mqueue_queue mq, mqueue_thread_signal mtsig)
if (!kicked)
mqueue_dequeue_signal(mq, mtsig) ;
- qpt_mutex_unlock(&mq->mutex) ;
+ qpt_mutex_unlock(mq->mutex) ;
return kicked ;
} ;
@@ -930,8 +953,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 a28b6606..c787e19b 100644
--- a/lib/mqueue.h
+++ b/lib/mqueue.h
@@ -22,16 +22,11 @@
#ifndef _ZEBRA_MQUEUE_H
#define _ZEBRA_MQUEUE_H
-#include <stddef.h>
-#include <stdbool.h>
+#include "misc.h"
#include "qpthreads.h"
#include "qtime.h"
-#ifndef Inline
-#define Inline static inline
-#endif
-
/*==============================================================================
* Message Queue Blocks -- mqb
*
@@ -196,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) ;
+extern void mqueue_initialise(void) ;
+extern void mqueue_finish(void) ;
-#define mqueue_local_reset_keep(lmq) mqueue_local_reset(lmq, 0)
-#define mqueue_local_reset_free(lmq) mqueue_local_reset(lmq, 1)
+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 void
-mqueue_set_timeout_interval(mqueue_queue mq, qtime_t interval) ;
+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 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) ;
+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) ;
-#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 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 mqueue_block 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_revoke(mqueue_queue mq, void* arg0) ;
+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 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/network.c b/lib/network.c
index ae67a402..6f12805a 100644
--- a/lib/network.c
+++ b/lib/network.c
@@ -20,9 +20,27 @@
* 02111-1307, USA.
*/
-#include <zebra.h>
+#include "misc.h"
+#include "qdebug_nb.h"
+
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+
#include "log.h"
#include "network.h"
+#include "memory.h"
+
+/*==============================================================================
+ * Read and write loops -- assuming blocking or not-blocking.
+ *
+ *
+ *
+ */
+
+static ssize_t read_qdebug_nb(int fd, void* buf, size_t nbyte) ;
+static ssize_t write_qdebug_nb(int fd, const void* buf, size_t nbyte) ;
/*------------------------------------------------------------------------------
* Read nbytes from fd and store into ptr -- BLOCKING
@@ -34,8 +52,8 @@
*
* NB: if applied to a NON-BLOCKING fd, may return EAGAIN or EWOULDBLOCK
*/
-int
-readn (int fd, u_char *ptr, int nbytes)
+extern int
+readn (int fd, void* buf, int nbytes)
{
int nleft;
int nread;
@@ -44,15 +62,17 @@ readn (int fd, u_char *ptr, int nbytes)
while (nleft > 0)
{
- nread = read (fd, ptr, nleft);
+ nread = read (fd, buf, nleft);
if (nread > 0)
{
nleft -= nread;
- ptr += nread;
+ buf = (char*)buf + nread;
}
+
else if (nread == 0)
break;
+
else
{
if (errno != EINTR)
@@ -73,57 +93,122 @@ readn (int fd, u_char *ptr, int nbytes)
* -1 => failed -- see errno
* -2 => EOF met immediately
*
- * NB: if asked to write zero bytes, does nothing and will return 0.
+ * NB: if asked to read zero bytes, does nothing and will return 0.
*
* Reading zero bytes is defined for all types of files, and may be used
* to probe for error state.
*/
-int
+extern int
read_nb(int fd, void* buf, size_t nbyte)
{
- size_t nleft = nbyte ;
+ size_t nleft ;
+
+// char* p = buf ;
+
+ nleft = nbyte ;
do
{
- int ret = read(fd, buf, nleft);
+ ssize_t ret ;
+
+ if (qdebug_nb)
+ ret = read_qdebug_nb(fd, buf, nleft) ;
+ else
+ ret = read(fd, buf, nleft) ;
if (ret > 0)
{
buf = (char*)buf + ret ;
nleft -= ret ;
}
+
else if (ret == 0)
{
if (nleft < nbyte)
break ; /* if read something before EOF */
- return -2 ; /* hit EOF immediately */
+// fprintf(stderr, "[read (%d) %s]\n", fd, (nbyte == 0) ? "OK" : "EOF") ;
+
+ return (nbyte == 0) ? 0 : -2 ; /* OK or EOF */
}
+
else
{
int err = errno ;
if ((err == EAGAIN) || (err == EWOULDBLOCK))
break ;
if (err != EINTR)
+// assert(0) ; // PRO TEM
return -1 ; /* failed */
} ;
} while (nleft > 0) ;
+#if 0
+{
+ int n ;
+ char buffer[100] ;
+ char* q, * e ;
+ e = buffer + 95 ;
+
+ n = nbyte - nleft ;
+
+ fprintf(stderr, "[read (%d) %d: '", fd, n) ;
+ while (n > 0)
+ {
+ q = buffer ;
+ while ((q < e) && (n > 0))
+ {
+ char ch = *p++ ;
+ --n ;
+
+ if (ch < 0x20)
+ {
+ *q++ = '\\' ;
+ switch (ch)
+ {
+ case '\n':
+ ch = 'n' ;
+ break ;
+
+ case '\r':
+ ch = 'r' ;
+ break ;
+
+ case '\t':
+ ch = 't' ;
+ break ;
+
+ default:
+ ch += '@' ;
+ break ;
+ } ;
+ } ;
+
+ *q++ = ch ;
+ } ;
+ *q++ = '\0' ;
+
+ fprintf(stderr, "%s", buffer) ;
+ }
+ fprintf(stderr, "']\n") ;
+} ;
+#endif
+
return (nbyte - nleft) ;
} ;
/*------------------------------------------------------------------------------
- * Write nbytes to fd from ptr -- BLOCKING
+ * Write nbytes to fd from buf -- BLOCKING
*
* Loops internally if gets EINTR.
*
* Returns: >= 0 -- number of bytes written
- * < 0 => error
+ * -1 => error
*
* NB: if applied to a NON-BLOCKING fd, may return EAGAIN or EWOULDBLOCK
*/
-int
-writen(int fd, const u_char *ptr, int nbytes)
+extern int
+writen(int fd, const void* buf, int nbytes)
{
int nleft;
int nwritten;
@@ -132,21 +217,23 @@ writen(int fd, const u_char *ptr, int nbytes)
while (nleft > 0)
{
- nwritten = write(fd, ptr, nleft);
+ nwritten = write(fd, buf, nleft);
- if (nwritten > 0)
+ if (nwritten >= 0)
{
+ /* write() is not expected to return 0 unless the request is 0,
+ * which in this case it isn't. So cannot happen -- and if it did,
+ * wouldn't know what else to do with it !
+ */
nleft -= nwritten;
- ptr += nwritten;
+ buf = (const char*)buf + nwritten;
}
- else if (nwritten == 0)
- break ;
else
{
if (errno != EINTR)
- return (nwritten);
- }
- }
+ return -1 ;
+ } ;
+ } ;
return nbytes - nleft;
}
@@ -163,29 +250,39 @@ writen(int fd, const u_char *ptr, int nbytes)
* Writing zero bytes is defined for "regular files", but not for anything
* else.
*/
-int
-write_nb(int fd, void* buf, size_t nbyte)
+extern int
+write_nb(int fd, const void* buf, size_t nbyte)
{
- size_t nleft = nbyte ;
+ size_t nleft ;
+
+ nleft = nbyte ;
while (nleft > 0)
{
- int ret = write(fd, buf, nleft);
+ ssize_t ret ;
- if (ret > 0)
+ if (qdebug_nb)
+ ret = write_qdebug_nb(fd, buf, nleft) ;
+ else
+ ret = write(fd, buf, nleft) ;
+
+ if (ret > 0)
{
- buf = (char*)buf + ret ;
+ buf = (const char*)buf + ret ;
nleft -= ret ;
}
else if (ret == 0)
- break ; /* not sure can happen... but
- cannot assume will go away */
+ /* write() is not expected to return 0 unless the request is 0,
+ * which in this case it isn't. So cannot happen -- but if it were
+ * to happen, this treats it as another form of "EAGAIN" !
+ */
+ break ;
else
{
- int err = errno ;
- if ((err == EAGAIN) || (err == EWOULDBLOCK))
+ if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
break ;
- if (err != EINTR)
+ if (errno != EINTR)
+// assert(0) ; // PRO TEM
return -1 ; /* failed */
} ;
} ;
@@ -194,12 +291,46 @@ write_nb(int fd, void* buf, size_t nbyte)
} ;
/*------------------------------------------------------------------------------
+ * Copy from one fd to another -- blocking.
+ *
+ * NB: if applied to a NON-BLOCKING fd, may return EAGAIN or EWOULDBLOCK
+ */
+extern int
+copyn (int dst_fd, int src_fd)
+{
+ void* buffer ;
+ int r, err ;
+
+ enum { buffer_size = 64 * 1024 } ;
+ buffer = XMALLOC(MTYPE_TMP, buffer_size) ;
+
+ do
+ {
+ r = readn(src_fd, buffer, buffer_size) ;
+ if (r > 0)
+ r = writen(dst_fd, buffer, r) ;
+ }
+ while (r > 0) ;
+
+ err = errno ;
+
+ XFREE(MTYPE_TMP, buffer) ;
+
+ errno = err ;
+ return r ;
+} ;
+
+/*==============================================================================
+ *
+ */
+
+/*------------------------------------------------------------------------------
* Set fd to non-blocking
*
* Returns: 0 => OK
* -1 => failed
*/
-int
+extern int
set_nonblocking(int fd)
{
int flags;
@@ -220,3 +351,52 @@ set_nonblocking(int fd)
}
return 0;
}
+
+/*==============================================================================
+ * Simulate read/write with tiny buffers and a lot of blocking.
+ */
+
+static qrand_seq_t rseq = QRAND_SEQ_INIT(2001) ;
+static qrand_seq_t wseq = QRAND_SEQ_INIT(3001) ;
+
+static const int blocking_errs[] = { EAGAIN, EWOULDBLOCK, EINTR } ;
+
+/*------------------------------------------------------------------------------
+ * Simulate read(), with tiny input buffer and lots of blocking.
+ */
+static ssize_t
+read_qdebug_nb(int fd, void* buf, size_t nbyte)
+{
+ if (nbyte > 0)
+ {
+ if (qrand(rseq, 3) == 0) /* 1/3 chance of blocking */
+ {
+ errno = blocking_errs[qrand(rseq, 3)] ;
+ return -1 ;
+ } ;
+
+ nbyte = qrand(rseq, (nbyte < 200) ? nbyte : 200) + 1 ;
+ } ;
+
+
+ return read(fd, buf, nbyte) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Simulate write(), with tiny input buffer and lots of blocking.
+ */
+static ssize_t
+write_qdebug_nb(int fd, const void* buf, size_t nbyte)
+{
+ assert(nbyte > 0) ;
+
+ if (qrand(wseq, 3) == 0) /* 1/3 chance of blocking */
+ {
+ errno = blocking_errs[qrand(wseq, 3)] ;
+ return -1 ;
+ } ;
+
+ nbyte = qrand(wseq, (nbyte < 200) ? nbyte : 200) + 1 ;
+
+ return write(fd, buf, nbyte) ;
+} ;
diff --git a/lib/network.h b/lib/network.h
index 72d38b52..0e626e72 100644
--- a/lib/network.h
+++ b/lib/network.h
@@ -26,8 +26,9 @@
/* Both readn and writen are deprecated and will be removed. They are not
suitable for use with non-blocking file descriptors.
*/
-extern int readn (int, u_char *, int);
-extern int writen (int, const u_char *, int);
+extern int readn (int, void*, int);
+extern int writen (int, const void*, int);
+extern int copyn (int dst_fd, int src_fd) ;
/* Set the file descriptor to use non-blocking I/O. Returns 0 for success,
-1 on error. */
@@ -35,7 +36,7 @@ extern int set_nonblocking(int fd);
/* Non-Blocking versions of read/write */
int read_nb(int fd, void* buf, size_t nbyte) ;
-int write_nb(int fd, void* buf, size_t nbyte) ;
+int write_nb(int fd, const void* buf, size_t nbyte) ;
/* Does the I/O error indicate that the operation should be retried later? */
#define ERRNO_IO_RETRY(EN) \
diff --git a/lib/node_type.h b/lib/node_type.h
deleted file mode 100644
index 7ec1107d..00000000
--- a/lib/node_type.h
+++ /dev/null
@@ -1,81 +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. */
-};
-
-#endif /* _ZEBRA_NODE_TYPE_H */
diff --git a/lib/plist.c b/lib/plist.c
index e5c71d35..c761c5c5 100644
--- a/lib/plist.c
+++ b/lib/plist.c
@@ -268,7 +268,7 @@ static void
prefix_master_reset(struct prefix_master * pm)
{
struct prefix_list* plist ;
- while ((plist = symbol_table_ream_keep(&(pm->table))))
+ while ((plist = symbol_table_ream(&(pm->table), keep_it)))
prefix_list_delete(plist) ;
pm->seqnum_flag = 1 ; /* Default is to generate sequence numbers. */
@@ -326,7 +326,8 @@ prefix_list_set_ref(prefix_list_ref* p_ref, afi_t afi, const char* name)
if (pm == NULL)
return NULL ;
- return *p_ref = symbol_set_ref(*p_ref, symbol_find(&(pm->table), name)) ;
+ return *p_ref = symbol_set_ref(*p_ref,
+ symbol_lookup(&(pm->table), name, add)) ;
} ;
/* Copy reference to prefix_list.
@@ -389,7 +390,7 @@ prefix_list_apply (struct prefix_list *plist, void *object)
struct prefix *p ;
int plen ;
struct prefix_list_entry* pe ;
- vector_index i ;
+ vector_index_t i ;
in_addr_t ip ;
#ifdef s6_addr32
@@ -575,7 +576,7 @@ prefix_list_delete (struct prefix_list* plist)
static struct prefix_list *
prefix_list_seek (struct prefix_master* pm, const char *name)
{
- return symbol_get_value(symbol_seek(&(pm->table), name)) ;
+ return symbol_get_value(symbol_lookup(&(pm->table), name, no_add)) ;
} ;
/* Lookup prefix_list by afi and name -- if afi is known, and name not NULL.
@@ -603,7 +604,7 @@ prefix_list_get (struct prefix_master* pm, const char *name, afi_t afi)
assert((pm != NULL) && (name != NULL)) ;
- sym = symbol_find(&(pm->table), name) ; /* creates if required */
+ sym = symbol_lookup(&(pm->table), name, add) ; /* creates if required */
plist = symbol_get_value(sym) ;
return plist ? plist : prefix_list_new(pm, sym, afi) ;
@@ -691,7 +692,7 @@ prefix_list_entry_ge_le_check(struct prefix_list_entry* pe, afi_t afi)
* result > 0 -- not found. index returned is of the entry with the largest
* sequence number smaller than the given one.
*/
-static vector_index
+static vector_index_t
prefix_list_entry_lookup_seq(struct prefix_list *plist, int seq, int* result)
{
return vector_bsearch(&plist->list, (vector_bsearch_cmp*)prefix_seq_cmp,
@@ -725,7 +726,7 @@ prefix_list_entry_lookup_seq(struct prefix_list *plist, int seq, int* result)
*
* Note that the cache is never empty.
*/
-static vector_index
+static vector_index_t
prefix_list_entry_lookup_val(struct prefix_list *plist,
struct prefix_list_entry* temp,
vector* cache,
@@ -753,7 +754,7 @@ prefix_list_entry_lookup_val(struct prefix_list *plist,
else
{
struct prefix_list_entry* pe ;
- vector_index i ;
+ vector_index_t i ;
*cache = NULL ; /* Not found in cache. */
*result = 0 ; /* Assume found ! */
for (VECTOR_ITEMS(&plist->list, pe, i))
@@ -781,12 +782,12 @@ prefix_list_entry_lookup_val(struct prefix_list *plist,
* -- result != 0 not found -- index is immaterial
*
* */
-static vector_index
+static vector_index_t
prefix_list_entry_lookup (struct prefix_list* plist,
struct prefix_list_entry* pe_seek, int* result)
{
struct prefix_list_entry* pe_found ;
- vector_index i ;
+ vector_index_t i ;
if (pe_seek->flags & PREFIX_SEQ)
{
@@ -840,7 +841,7 @@ prefix_list_entry_insert(struct prefix_list *plist,
{
struct prefix_list_entry* pe ;
vector cache ;
- vector_index i, ic ;
+ vector_index_t i, ic ;
int ret, retc ;
u_int32_t mask ;
int pl, sh ;
@@ -901,7 +902,7 @@ prefix_list_entry_insert(struct prefix_list *plist,
if (cache)
{
/* We need to know where the old value was. */
- vector_index io ;
+ vector_index_t io ;
int reto ;
io = vector_bsearch(cache, (vector_bsearch_cmp*)plist->cmp, pe,
&reto) ;
@@ -1005,7 +1006,7 @@ prefix_list_entry_delete (struct prefix_list *plist,
struct prefix_list_entry *pe_seek)
{
struct prefix_list_entry* pe ;
- vector_index i ;
+ vector_index_t i ;
int ret ;
i = prefix_list_entry_lookup (plist, pe_seek, &ret) ;
@@ -1155,7 +1156,7 @@ static void __attribute__ ((unused))
prefix_list_print (struct prefix_list *plist)
{
struct prefix_list_entry* pe ;
- vector_index i ;
+ vector_index_t i ;
struct vty* vty = NULL ;
if (plist == NULL)
@@ -1461,7 +1462,7 @@ vty_show_prefix_entry (struct vty *vty, struct prefix_list *plist,
if (dtype != summary_display)
{
struct prefix_list_entry* pe ;
- vector_index i ;
+ vector_index_t i ;
int with_seq = pm->seqnum_flag ;
int with_stats = (dtype == detail_display)
||(dtype == sequential_display) ;
@@ -1507,7 +1508,7 @@ vty_show_prefix_list (struct vty *vty, afi_t afi, const char *name,
else
{
vector extract ;
- vector_index i ;
+ vector_index_t i ;
struct symbol* sym ;
if (dtype == detail_display || dtype == summary_display)
@@ -1542,7 +1543,7 @@ vty_show_prefix_list_prefix (struct vty *vty, afi_t afi, const char *name,
{
struct prefix_list *plist;
struct prefix_list_entry* pe ;
- vector_index i ;
+ vector_index_t i ;
struct prefix p;
int ret;
int match;
@@ -1598,7 +1599,7 @@ vty_clear_prefix_list (struct vty *vty, afi_t afi, const char *name,
int ret;
struct prefix p;
struct prefix_list_entry* pe ;
- vector_index i ;
+ vector_index_t i ;
pm = prefix_master_get (afi);
if (pm == NULL)
@@ -2820,7 +2821,7 @@ config_write_prefix_afi (afi_t afi, struct vty *vty)
struct prefix_master *pm;
int write = 0;
vector extract ;
- vector_index i, ipe ;
+ vector_index_t i, ipe ;
struct symbol* sym ;
pm = prefix_master_get (afi);
@@ -2875,7 +2876,7 @@ prefix_bgp_orf_entry (struct stream *s, prefix_list_ref ref,
u_char init_flag, u_char permit_flag, u_char deny_flag)
{
struct prefix_list_entry *pe;
- vector_index i ;
+ vector_index_t i ;
struct prefix_list *plist = prefix_list_ref_plist(ref) ;
@@ -2900,7 +2901,7 @@ prefix_bgp_orf_entry (struct stream *s, prefix_list_ref ref,
* return 0 - no such entry
*/
int
-prefix_bgp_orf_get(struct prefix_list *plist, vector_index i,
+prefix_bgp_orf_get(struct prefix_list *plist, vector_index_t i,
struct orf_prefix *orfpe, enum prefix_list_type *pe_type)
{
struct prefix_list_entry *pe = NULL;
@@ -2991,7 +2992,7 @@ prefix_bgp_show_prefix_list (struct vty *vty, afi_t afi, char *name)
if (vty)
{
struct prefix_list_entry* pe ;
- vector_index i ;
+ vector_index_t i ;
vty_prefix_list_name_count_print(vty, plist, VTY_NEWLINE) ;
diff --git a/lib/plist.h b/lib/plist.h
index 4f383417..4abddcd0 100644
--- a/lib/plist.h
+++ b/lib/plist.h
@@ -62,7 +62,7 @@ extern const char* prefix_list_get_name(struct prefix_list* plist) ;
extern struct stream * prefix_bgp_orf_entry (struct stream *,
prefix_list_ref ref,
u_char, u_char, u_char);
-extern int prefix_bgp_orf_get(struct prefix_list *plist, vector_index i,
+extern int prefix_bgp_orf_get(struct prefix_list *plist, vector_index_t i,
struct orf_prefix *orfpe, enum prefix_list_type *pe_type);
extern int prefix_bgp_orf_set (char *, afi_t, struct orf_prefix *, int, int);
extern void prefix_bgp_orf_remove_all (char *);
diff --git a/lib/prefix.c b/lib/prefix.c
index 867c86a3..d511a535 100644
--- a/lib/prefix.c
+++ b/lib/prefix.c
@@ -27,6 +27,7 @@
#include "sockunion.h"
#include "memory.h"
#include "log.h"
+#include "tstring.h"
/* Maskbit. */
static const u_char maskbit[] = { 0x00, 0x80, 0xc0, 0xe0, 0xf0,
@@ -273,18 +274,19 @@ prefix_ipv4_free (struct prefix_ipv4 *p)
int
str2prefix_ipv4 (const char *str, struct prefix_ipv4 *p)
{
- char* pnt ;
- char* cp ;
- int ret ;
- unsigned plen ;
+ tstring_t(ipv4, 24) ;
+ char* pnt ;
+ const char* cp ;
+ int ret ;
+ unsigned plen ;
pnt = strchr (str, '/');
if (pnt == NULL)
{
/* No / => simple address */
- plen = IPV4_MAX_BITLEN;
- ret = inet_aton (str, &p->prefix);
+ plen = IPV4_MAX_BITLEN ;
+ cp = str ;
}
else
{
@@ -293,19 +295,18 @@ str2prefix_ipv4 (const char *str, struct prefix_ipv4 *p)
if (plen > IPV4_MAX_PREFIXLEN)
return 0;
- cp = XMALLOC (MTYPE_TMP, (pnt - str) + 1);
- strncpy (cp, str, pnt - str);
- *(cp + (pnt - str)) = '\0';
- ret = inet_aton (cp, &p->prefix);
- XFREE (MTYPE_TMP, cp);
- }
+ cp = tstring_set_n(ipv4, str, (pnt - str)) ;
+ } ;
+ ret = inet_aton (cp, &p->prefix);
if (ret <= 0) /* should not return < 0, but it would not be valid ! */
return 0;
p->family = AF_INET;
p->prefixlen = plen;
+ tstring_free(ipv4) ;
+
return 1 ;
}
@@ -435,10 +436,11 @@ prefix_ipv6_free (struct prefix_ipv6 *p)
int
str2prefix_ipv6 (const char *str, struct prefix_ipv6 *p)
{
- char* pnt ;
- char* cp ;
- int ret ;
- unsigned plen ;
+ tstring_t(ipv6, 64) ;
+ char* pnt ;
+ const char* cp ;
+ int ret ;
+ unsigned plen ;
pnt = strchr (str, '/');
@@ -446,7 +448,7 @@ str2prefix_ipv6 (const char *str, struct prefix_ipv6 *p)
{
/* No / => simple address */
plen = IPV6_MAX_BITLEN;
- ret = inet_pton (AF_INET6, str, &p->prefix);
+ cp = str ;
}
else
{
@@ -455,19 +457,18 @@ str2prefix_ipv6 (const char *str, struct prefix_ipv6 *p)
if (plen > IPV6_MAX_PREFIXLEN)
return 0 ;
- cp = XMALLOC (MTYPE_TMP, (pnt - str) + 1);
- strncpy (cp, str, pnt - str);
- *(cp + (pnt - str)) = '\0';
- ret = inet_pton (AF_INET6, cp, &p->prefix);
- XFREE (MTYPE_TMP, cp);
- }
+ cp = tstring_set_n(ipv6, str, (pnt - str)) ;
+ } ;
+ ret = inet_pton (AF_INET6, cp, &p->prefix);
if (ret <= 0)
return 0 ;
p->family = AF_INET6;
p->prefixlen = plen;
+ tstring_free(ipv6) ;
+
return 1 ;
}
diff --git a/lib/prefix.h b/lib/prefix.h
index d4d6adb2..bc44244c 100644
--- a/lib/prefix.h
+++ b/lib/prefix.h
@@ -23,12 +23,9 @@
#ifndef _ZEBRA_PREFIX_H
#define _ZEBRA_PREFIX_H
+#include "misc.h"
#include "sockunion.h"
-#ifndef Inline
-#define Inline static inline
-#endif
-
typedef const union sockunion* const_sockunion ;
/*
diff --git a/lib/privs.c b/lib/privs.c
index be3265ed..46dc0977 100644
--- a/lib/privs.c
+++ b/lib/privs.c
@@ -21,7 +21,7 @@
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
-#include <zebra.h>
+#include "zebra.h"
#include "log.h"
#include "privs.h"
#include "memory.h"
@@ -29,8 +29,8 @@
/* Needs to be qpthread safe */
static qpt_mutex_t privs_mutex;
-#define LOCK qpt_mutex_lock(&privs_mutex);
-#define UNLOCK qpt_mutex_unlock(&privs_mutex);
+#define LOCK qpt_mutex_lock(privs_mutex);
+#define UNLOCK qpt_mutex_unlock(privs_mutex);
#ifdef HAVE_CAPABILITIES
/* sort out some generic internal types for:
@@ -675,20 +675,28 @@ zprivs_state_null (void)
void
zprivs_init_r()
{
- qpt_mutex_init(&privs_mutex, qpt_mutex_quagga);
+ qpt_mutex_init(privs_mutex, qpt_mutex_quagga);
}
void
zprivs_finish(void)
{
- qpt_mutex_destroy(&privs_mutex, 0);
+ qpt_mutex_destroy(privs_mutex, 0);
}
+/*------------------------------------------------------------------------------
+ * Initialise privilege handling and set any running group.
+ *
+ * Returns with lowered privileges.
+ *
+ * Must be initialised before vty. Will be initialised very early in the
+ * morning, before daemonisation and therefore before any pthreads.
+ */
void
zprivs_init(struct zebra_privs_t *zprivs)
{
struct passwd *pwentry = NULL;
- struct group *grentry = NULL;
+ struct group *grentry = NULL;
if (!zprivs)
{
@@ -696,18 +704,18 @@ zprivs_init(struct zebra_privs_t *zprivs)
exit (1);
}
- LOCK
-
- /* NULL privs */
- if (! (zprivs->user || zprivs->group
- || zprivs->cap_num_p || zprivs->cap_num_i) )
+ /* NULL privs */
+ if ( (zprivs->user == NULL) && (zprivs->group == NULL)
+ && (zprivs->cap_num_p == 0)
+ && (zprivs->cap_num_i == 0) )
{
- zprivs->change = zprivs_change_null;
+ zprivs->change = zprivs_change_null;
zprivs->current_state = zprivs_state_null;
- UNLOCK
+
return;
}
+ /* Get uid for configured user (if any) */
if (zprivs->user)
{
if ( (pwentry = getpwnam (zprivs->user)) )
@@ -719,11 +727,11 @@ zprivs_init(struct zebra_privs_t *zprivs)
/* cant use log.h here as it depends on vty */
fprintf (stderr, "privs_init: could not lookup user %s\n",
zprivs->user);
- UNLOCK
exit (1);
}
}
+ /* Get gid for vty_group and add to our groups */
grentry = NULL;
if (zprivs->vty_group)
@@ -736,7 +744,6 @@ zprivs_init(struct zebra_privs_t *zprivs)
{
fprintf (stderr, "privs_init: could not setgroups, %s\n",
errtostr(errno, 0).str) ;
- UNLOCK
exit (1);
}
}
@@ -744,11 +751,11 @@ zprivs_init(struct zebra_privs_t *zprivs)
{
fprintf (stderr, "privs_init: could not lookup vty group %s\n",
zprivs->vty_group);
- UNLOCK
exit (1);
}
}
+ /* Get gid for configured group and switch to same, now. */
if (zprivs->group)
{
if ( (grentry = getgrnam (zprivs->group)) )
@@ -759,15 +766,13 @@ zprivs_init(struct zebra_privs_t *zprivs)
{
fprintf (stderr, "privs_init: could not lookup group %s\n",
zprivs->group);
- UNLOCK
exit (1);
}
- /* change group now, forever. uid we do later */
+ /* change group now, forever. uid we do later */
if ( setregid (zprivs_state.zgid, zprivs_state.zgid) )
{
fprintf (stderr, "zprivs_init: could not setregid, %s\n",
errtostr(errno, 0).str) ;
- UNLOCK
exit (1);
}
}
@@ -775,7 +780,8 @@ zprivs_init(struct zebra_privs_t *zprivs)
#ifdef HAVE_CAPABILITIES
zprivs_caps_init (zprivs);
#else /* !HAVE_CAPABILITIES */
- /* we dont have caps. we'll need to maintain rid and saved uid
+
+ /* We don't have caps. we'll need to maintain rid and saved uid
* and change euid back to saved uid (who we presume has all necessary
* privileges) whenever we are asked to raise our privileges.
*
@@ -784,20 +790,23 @@ zprivs_init(struct zebra_privs_t *zprivs)
zprivs_state.zsuid = geteuid();
if ( zprivs_state.zuid )
{
+ /* If the zuid != real uid, will set the saved uid == zuid, leaving
+ * just the two zuid to choose from -- though if the real uid is root,
+ * this makes little difference.
+ */
if ( setreuid (-1, zprivs_state.zuid) )
{
fprintf (stderr, "privs_init (uid): could not setreuid, %s\n",
errtoa(errno, 0).str);
- UNLOCK
exit (1);
}
}
- zprivs->change = zprivs_change_uid;
- zprivs->current_state = zprivs_state_uid;
+ zprivs->change = zprivs_change_uid ;
+ zprivs->current_state = zprivs_state_uid ;
+
#endif /* HAVE_CAPABILITIES */
- UNLOCK
}
void
@@ -826,9 +835,9 @@ zprivs_terminate (struct zebra_privs_t *zprivs)
}
#endif /* HAVE_LCAPS */
- zprivs->change = zprivs_change_null;
+ zprivs->change = zprivs_change_null;
zprivs->current_state = zprivs_state_null;
- zprivs_null_state = ZPRIVS_LOWERED;
+ zprivs_null_state = ZPRIVS_LOWERED;
raise_count = 0;
UNLOCK
@@ -841,12 +850,12 @@ zprivs_get_ids(struct zprivs_ids_t *ids)
LOCK
ids->uid_priv = getuid();
- (zprivs_state.zuid) ? (ids->uid_normal = zprivs_state.zuid)
- : (ids->uid_normal = -1);
- (zprivs_state.zgid) ? (ids->gid_normal = zprivs_state.zgid)
- : (ids->gid_normal = -1);
+ (zprivs_state.zuid) ? (ids->uid_normal = zprivs_state.zuid)
+ : (ids->uid_normal = -1);
+ (zprivs_state.zgid) ? (ids->gid_normal = zprivs_state.zgid)
+ : (ids->gid_normal = -1);
(zprivs_state.vtygrp) ? (ids->gid_vty = zprivs_state.vtygrp)
- : (ids->gid_vty = -1);
+ : (ids->gid_vty = -1);
UNLOCK
return;
diff --git a/lib/pthread_safe.c b/lib/pthread_safe.c
index c4ab9679..4a698216 100644
--- a/lib/pthread_safe.c
+++ b/lib/pthread_safe.c
@@ -25,6 +25,12 @@
* safe_ function is called on the same thread
*/
+/* Need "NO_USE_GNU" to turn off __USE_GNU in "misc.h" in order to get the
+ * POSIX version of strerror_r() :-(
+ */
+#define NO_USE_GNU
+
+#include "misc.h"
#include "pthread_safe.h"
#include "qpthreads.h"
#include "memory.h"
@@ -48,6 +54,8 @@ static char * thread_buff(void);
static pthread_key_t tsd_key;
static const int buff_size = 1024;
+static const char ellipsis[] = "..." ;
+
/* Module initialization, before any threads have been created */
void
safe_init_r(void)
@@ -134,7 +142,7 @@ safe_strerror(int errnum)
* to show this has happened.
*/
-static void errtox(strerror_t* st, int err, int len, int want) ;
+static void errtox(strerror_t* st, int err, uint len, uint want) ;
/*------------------------------------------------------------------------------
* Construct string to describe the given error of the form:
@@ -144,7 +152,7 @@ static void errtox(strerror_t* st, int err, int len, int want) ;
* Thread safe extension to strerror. Never returns NULL.
*/
extern strerror_t
-errtoa(int err, int len)
+errtoa(int err, uint len)
{
strerror_t st ;
@@ -159,7 +167,7 @@ errtoa(int err, int len)
* Thread-safe. Never returns NULL.
*/
extern strerror_t
-errtoname(int err, int len)
+errtoname(int err, uint len)
{
strerror_t st ;
@@ -174,7 +182,7 @@ errtoname(int err, int len)
* Thread safe replacement for strerror. Never returns NULL.
*/
extern strerror_t
-errtostr(int err, int len)
+errtostr(int err, uint len)
{
strerror_t st ;
@@ -189,21 +197,23 @@ errtostr(int err, int len)
* want == 1 -- return just name
* want == 2 -- return just the strerror()
* want == 3 -- return both, with the strerror() in single quotes.
+ *
+ * NB: this is not async-signal-safe !
*/
static void
-errtox(strerror_t* st, int err, int len, int want)
+errtox(strerror_t* st, int err, uint len, uint want)
{
qf_str_t qfs ;
const char* q ;
- int ql ;
+ uint ql ;
/* Prepare. */
int errno_saved = errno ;
- if ((len <= 0) || (len >= (int)sizeof(st->str)))
- len = sizeof(st->str) - 1 ;
- qfs_init(&qfs, st->str, len + 1) ;
+ if ((len <= 0) || (len >= sizeof(st->str)))
+ len = sizeof(st->str) ;
+ qfs_init(qfs, st->str, len) ;
q = "" ;
ql = 0 ;
@@ -214,15 +224,15 @@ errtox(strerror_t* st, int err, int len, int want)
const char* name = errno_name_lookup(err) ;
if (name != NULL)
- qfs_append(&qfs, name) ;
+ qfs_append(qfs, name) ;
else
- qfs_printf(&qfs, "ERRNO=%d", err) ;
+ qfs_printf(qfs, "ERRNO=%d", err) ;
} ;
/* name and string ? */
if (want == 3)
{
- qfs_append(&qfs, " ") ;
+ qfs_append(qfs, " ") ;
q = "'" ;
ql = 2 ;
} ;
@@ -285,22 +295,28 @@ errtox(strerror_t* st, int err, int len, int want)
else
{
qf_str_t qfs_b ;
- qfs_init(&qfs_b, buf, sizeof(buf)) ;
- qfs_printf(&qfs_b, "strerror%s(%d) returned error %d",
+ qfs_init(qfs_b, buf, sizeof(buf)) ;
+ qfs_printf(qfs_b, "strerror%s(%d) returned error %d",
qpthreads_enabled ? "_r" : "", err, ret) ;
errm = buf ;
} ;
} ;
} ;
- /* Add strerror to the result... looking out for overflow. */
- len = strlen(errm) ;
+ /* Add strerror to the result... with quotes as rquired */
+ if (ql != 0)
+ qfs_append(qfs, q) ;
- 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) ;
- /* -ve precision is ignored ! */
+ qfs_append(qfs, errm) ;
+
+ if (ql != 0)
+ qfs_append(qfs, q) ;
+
+ /* '\0' terminate -- if has overflowed, replace last few characters
+ * by "..." -- noting that sizeof("...") includes the '\0'.
+ */
+ if (qfs_term(qfs) != 0)
+ qfs_term_string(qfs, ellipsis, sizeof(ellipsis)) ;
} ;
/* Put back errno */
@@ -340,7 +356,7 @@ errtox(strerror_t* st, int err, int len, int want)
* error.
*/
-static void eaitox(strerror_t* st, int eai, int err, int len, int want) ;
+static void eaitox(strerror_t* st, int eai, int err, uint len, uint want) ;
/*------------------------------------------------------------------------------
* Construct string to describe the given EAI_XXX error of the form:
@@ -351,7 +367,7 @@ static void eaitox(strerror_t* st, int eai, int err, int len, int want) ;
* Thread safe. Never returns NULL.
*/
extern strerror_t
-eaitoa(int eai, int err, int len)
+eaitoa(int eai, int err, uint len)
{
strerror_t st ;
@@ -368,7 +384,7 @@ eaitoa(int eai, int err, int len)
* Thread-safe. Never returns NULL.
*/
extern strerror_t
-eaitoname(int eai, int err, int len)
+eaitoname(int eai, int err, uint len)
{
strerror_t st ;
@@ -385,7 +401,7 @@ eaitoname(int eai, int err, int len)
* Thread-safe. Never returns NULL.
*/
extern strerror_t
-eaitostr(int eai, int err, int len)
+eaitostr(int eai, int err, uint len)
{
strerror_t st ;
@@ -404,12 +420,12 @@ eaitostr(int eai, int err, int len)
* err != 0 => if EAI_SYSTEM, return result for errno == err instead.
*/
static void
-eaitox(strerror_t* st, int eai, int err, int len, int want)
+eaitox(strerror_t* st, int eai, int err, uint len, uint want)
{
qf_str_t qfs ;
const char* q ;
- int ql ;
+ uint ql ;
/* Look out for mapping EAI_SYSTEM */
if ((eai == EAI_SYSTEM) && (err != 0))
@@ -420,7 +436,7 @@ eaitox(strerror_t* st, int eai, int err, int len, int want)
if ((len <= 0) || (len >= (int)sizeof(st->str)))
len = sizeof(st->str) - 1 ;
- qfs_init(&qfs, st->str, len + 1) ;
+ qfs_init(qfs, st->str, len + 1) ;
q = "" ;
ql = 0 ;
@@ -431,15 +447,15 @@ eaitox(strerror_t* st, int eai, int err, int len, int want)
const char* name = eaino_name_lookup(eai) ;
if (name != NULL)
- qfs_append(&qfs, name) ;
+ qfs_append(qfs, name) ;
else
- qfs_printf(&qfs, "EAI=%d", eai) ;
+ qfs_printf(qfs, "EAI=%d", eai) ;
} ;
/* name and string ? */
if (want == 3)
{
- qfs_append(&qfs, " ") ;
+ qfs_append(qfs, " ") ;
q = "'" ;
ql = 2 ;
} ;
@@ -454,14 +470,20 @@ eaitox(strerror_t* st, int eai, int err, int len, int want)
else
eaim = gai_strerror(eai) ;
- /* Add strerror to the result... looking out for overflow. */
- len = strlen(eaim) ;
+ /* Add strerror to the result... with quotes as rquired */
+ if (ql != 0)
+ qfs_append(qfs, q) ;
- if ((len + ql) <= qfs_left(&qfs)) /* accounting for "quotes" */
- qfs_printf(&qfs, "%s%s%s", q, eaim, q) ;
- else
- qfs_printf(&qfs, "%s%.*s...%s", q, qfs_left(&qfs) - ql - 3, eaim, q) ;
- /* -ve precision is ignored ! */
+ qfs_append(qfs, eaim) ;
+
+ if (ql != 0)
+ qfs_append(qfs, q) ;
+
+ /* '\0' terminate -- if has overflowed, replace last few characters
+ * by "..." -- noting that sizeof("...") includes the '\0'.
+ */
+ if (qfs_term(qfs) != 0)
+ qfs_term_string(qfs, ellipsis, sizeof(ellipsis)) ;
} ;
/* Put back errno */
diff --git a/lib/pthread_safe.h b/lib/pthread_safe.h
index 9518f3d1..b7faba1a 100644
--- a/lib/pthread_safe.h
+++ b/lib/pthread_safe.h
@@ -35,12 +35,12 @@ extern void safe_finish(void);
extern const char * safe_strerror(int errnum);
extern const char * safe_inet_ntoa (struct in_addr in);
-extern strerror_t errtoa(int err, int len) ;
-extern strerror_t errtoname(int err, int len) ;
-extern strerror_t errtostr(int err, int len) ;
+extern strerror_t errtoa(int err, uint len) ;
+extern strerror_t errtoname(int err, uint len) ;
+extern strerror_t errtostr(int err, uint len) ;
-extern strerror_t eaitoa(int eai, int err, int len) ;
-extern strerror_t eaitoname(int eai, int err, int len) ;
-extern strerror_t eaitostr(int eai, int err, int len) ;
+extern strerror_t eaitoa(int eai, int err, uint len) ;
+extern strerror_t eaitoname(int eai, int err, uint len) ;
+extern strerror_t eaitostr(int eai, int err, uint len) ;
#endif /* PTHREAD_SAFE_H_ */
diff --git a/lib/qafi_safi.h b/lib/qafi_safi.h
index 99c86055..c93b741e 100644
--- a/lib/qafi_safi.h
+++ b/lib/qafi_safi.h
@@ -23,7 +23,7 @@
#ifndef _QUAGGA_AFI_SAFI_H
#define _QUAGGA_AFI_SAFI_H
-#include <stdint.h>
+#include "misc.h"
#include "lib/zassert.h"
/*==============================================================================
diff --git a/lib/qdebug_nb.h b/lib/qdebug_nb.h
new file mode 100644
index 00000000..6da2a120
--- /dev/null
+++ b/lib/qdebug_nb.h
@@ -0,0 +1,61 @@
+/* Debug definitions for non-blocking I/O testing
+ * Copyright (C) 2010 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * 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_QDEBUG_NB_H
+#define _ZEBRA_QDEBUG_NB_H
+
+#include "misc.h"
+#include "qrand.h"
+
+/*------------------------------------------------------------------------------
+ * Sort out QDEBUG_NB_DEBUG.
+ *
+ * Set to 1 if defined, but blank.
+ * Set to QDEBUG if not defined.
+ *
+ * Force to 0 if VTY_NO_DEBUG is defined and not zero.
+ *
+ * So: defaults to same as QDEBUG, but no matter what QDEBUG is set to:
+ *
+ * * can set QDEBUG_NB_DEBUG == 0 to turn off debug
+ * * or set QDEBUG_NB_DEBUG != 0 to turn on debug
+ * * or set QDEBUG_NB_NO_DEBUG != to force debug off
+ */
+
+#ifdef QDEBUG_NB /* If defined, make it 1 or 0 */
+# if IS_BLANK_OPTION(QDEBUG_NB)
+# undef QDEBUG_NB
+# define QDEBUG_NB 1
+# endif
+#else /* If not defined, follow QDEBUG */
+# define QDEBUG_NB QDEBUG
+#endif
+
+#ifdef QDEBUG_NB_NO /* Override, if defined */
+# if IS_NOT_ZERO_OPTION(QDEBUG_NB_NO)
+# undef QDEBUG_NB
+# define QDEBUG_NB 0
+# endif
+#endif
+
+enum { qdebug_nb = QDEBUG_NB } ;
+
+#endif /* _ZEBRA_QDEBUG_NB_H */
diff --git a/lib/qfstring.c b/lib/qfstring.c
index f0972fdf..2d5f69cc 100644
--- a/lib/qfstring.c
+++ b/lib/qfstring.c
@@ -18,58 +18,100 @@
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
+#include "misc.h"
#include "qfstring.h"
-#include "zassert.h"
-#include <stdbool.h>
/*==============================================================================
*/
/*------------------------------------------------------------------------------
- * Initialise qf_str -- to given size (which includes the '\0')
+ * Initialise qf_str -- to given size, zero offset and zero overflow.
*
- * Sets pointers and terminates an empty string with one byte reserved for the
- * terminating '\0'.
+ * Note that does not terminate the string -- that must be done separately.
*
* This operation is async-signal-safe.
*/
extern void
-qfs_init(qf_str qfs, char* str, size_t size)
+qfs_init(qf_str qfs, char* str, uint size)
{
- assert(size > 0) ;
+ qfs->str = str ;
+ qfs->ptr = str ;
+ qfs->end = str + size ;
+ qfs->offset = 0 ;
+ qfs->overflow = 0 ;
+} ;
- qfs->str = str ;
- qfs->end = str + size - 1 ;
+/*------------------------------------------------------------------------------
+ * Initialise qf_str -- to given size, with given offset and zero overflow.
+ *
+ * Note that does not terminate the string -- that must be done separately.
+ *
+ * This operation is async-signal-safe.
+ */
+extern void
+qfs_init_offset(qf_str qfs, char* str, uint size, uint offset)
+{
+ qfs->str = str ;
+ qfs->ptr = str ;
+ qfs->end = str + size ;
+ qfs->offset = offset ;
+ qfs->overflow = 0 ;
+} ;
- *str = '\0' ;
- qfs->ptr = str ;
+/*------------------------------------------------------------------------------
+ * Reset given qf_str -- with the given offset and zero overflow.
+ *
+ * Sets ptr back to the start of the string and set the given offset.
+ *
+ * This operation is async-signal-safe.
+ */
+extern void
+qfs_reset_offset(qf_str qfs, uint offset)
+{
+ qfs->ptr = qfs->str ;
+ qfs->offset = offset ;
+ qfs->overflow = 0 ;
} ;
/*------------------------------------------------------------------------------
- * Initialise qf_str which already contains string -- to given size (which
- * includes the '\0')
+ * Initialise qf_str which already contains string -- to given size with zero
+ * overflow.
*
* 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 '\0'.
+ * Sets pointers, setting the write pointer to the existing '\0'.
*
* This operation is async-signal-safe.
+ *
+ * NB: it is a mistake if the size given is less than the length of the
+ * string (excluding the trailing '\0').
*/
extern void
-qfs_init_as_is(qf_str qfs, char* str, size_t size)
+qfs_init_as_is(qf_str qfs, char* str, uint size)
{
assert(size > 0) ;
- qfs->str = str ;
- qfs->end = str + size - 1 ;
+ qfs->str = str ;
+ qfs->end = str + size ;
+ qfs->offset = 0 ;
+ qfs->overflow = 0 ;
+
+ while (*str != '\0')
+ ++str ;
- qfs->ptr = strchr(str, '\0') ;
+ qfs->ptr = str ; /* point at '\0' */
+
+ assert(qfs->ptr <= qfs->end) ;
} ;
/*------------------------------------------------------------------------------
- * Terminate string with the given string.
+ * Terminate string with the given string if given length (which may include
+ * a '\0').
+ *
+ * This is for when the qstring has overflowed, and wish to indicate that at
+ * the end -- so takes no notice of offset.
*
* If necessary, characters are discarded from the end of the string in order
* to fit in the terminating stuff.
@@ -81,34 +123,25 @@ qfs_init_as_is(qf_str qfs, char* str, size_t size)
* This operation is async-signal-safe.
*/
extern void
-qfs_term(qf_str qfs, const char* src)
+qfs_term_string(qf_str qfs, const char* src, uint n)
{
- int len ;
- int excess ;
+ uint h ;
- if ((src == NULL) || (*src == '\0'))
- {
- *qfs->ptr = '\0' ; /* should be true anyway */
- return ;
- } ;
+ h = qfs->end - qfs->ptr ; /* space available */
- len = strlen(src) ;
- excess = qfs_len(qfs) - len ;
- if (excess > 0)
+ if (h < n)
{
- if (excess <= (qfs->ptr - qfs->str))
- qfs->ptr -= excess ;
- else
+ h = qfs->end - qfs->str ; /* total space */
+ if (h < n)
{
- int want = len ;
- len = qfs->end - qfs->str ; /* take what can... */
- src += (want - len) ; /* ... from the end */
- qfs->ptr = qfs->str ;
+ src += n - h ; /* past what will not fit */
+ n = h ;
} ;
+ qfs->ptr = qfs->end - n ;
} ;
- memcpy(qfs->ptr, src, len + 1) ; /* include the '\0' */
- qfs->ptr += len ;
+ while (n--)
+ *qfs->ptr++ = *src++ ;
} ;
/*==============================================================================
@@ -120,26 +153,29 @@ qfs_term(qf_str qfs, const char* src)
*
* May append nothing at all !
*
- * This operation is async-signal-safe.
+ * This operation is async-signal-safe. Takes into account the offset, and
+ * adds up any overflow
*/
extern void
qfs_append(qf_str qfs, const char* src)
{
- int n ;
-
- if ((src == NULL) || (*src == '\0'))
+ if (src == NULL)
return ;
- n = strlen(src) ;
-
- if (n > qfs_left(qfs))
- n = qfs_left(qfs) ;
-
- if (n == 0)
- return ;
+ while (qfs->offset > 0)
+ {
+ if (*src++ == '\0')
+ return ;
+ --qfs->offset ;
+ } ;
- memcpy(qfs->ptr, src, n + 1) ;
- qfs->ptr += n ;
+ while (*src != '\0')
+ {
+ if (qfs->ptr < qfs->end)
+ *qfs->ptr++ = *src++ ;
+ else
+ ++qfs->overflow ;
+ } ;
} ;
/*------------------------------------------------------------------------------
@@ -148,43 +184,76 @@ qfs_append(qf_str qfs, const char* src)
*
* May append nothing at all !
*
- * This operation is async-signal-safe.
+ * src may be NULL iff n == 0
+ *
+ * This operation is async-signal-safe. Takes into account the offset, and
+ * adds up any overflow
*/
extern void
-qfs_append_n(qf_str qfs, const char* src, size_t n)
+qfs_append_n(qf_str qfs, const char* src, uint n)
{
- if ((int)n > qfs_left(qfs))
- n = qfs_left(qfs) ;
+ uint h ;
- if (n <= 0)
- return ;
+ if (qfs->offset > 0)
+ {
+ if (qfs->offset >= n)
+ {
+ qfs->offset -= n ;
+ return ;
+ } ;
- memcpy(qfs->ptr, src, n) ;
- qfs->ptr += n ;
+ src += qfs->offset ;
+ n -= qfs->offset ;
- *qfs->ptr = '\0' ;
+ qfs->offset = 0 ;
+ } ;
+
+ h = (qfs->end - qfs->ptr) ;
+ if (n > h)
+ {
+ qfs->overflow += n - h ;
+ n = h ;
+ } ;
+
+ while (n--)
+ *qfs->ptr++ = *src++ ;
} ;
/*------------------------------------------------------------------------------
- * Append upto 'n' copies of the given character to the qf_str
+ * Append upto 'n' copies of the given character to the qf_str.
*
* May append nothing at all !
*
- * This operation is async-signal-safe.
+ * This operation is async-signal-safe. Takes into account the offset, and
+ * adds up any overflow
*/
extern void
-qfs_append_ch_x_n(qf_str qfs, char ch, size_t n)
+qfs_append_ch_x_n(qf_str qfs, char ch, uint n)
{
- if ((int)n > qfs_left(qfs))
- n = qfs_left(qfs) ;
+ uint h ;
- if (n <= 0)
- return ;
+ if (qfs->offset > 0)
+ {
+ if (qfs->offset >= n)
+ {
+ qfs->offset -= n ;
+ return ;
+ } ;
+
+ n -= qfs->offset ;
+
+ qfs->offset = 0 ;
+ } ;
+
+ h = (qfs->end - qfs->ptr) ;
+ if (n > h)
+ {
+ qfs->overflow += n - h ;
+ n = h ;
+ } ;
while (n--)
*qfs->ptr++ = ch ;
-
- *qfs->ptr = '\0' ;
} ;
/*------------------------------------------------------------------------------
@@ -197,19 +266,13 @@ qfs_append_ch_x_n(qf_str qfs, char ch, size_t n)
*
* May append nothing at all !
*
- * This operation is async-signal-safe.
+ * This operation is async-signal-safe. Takes into account the offset, and
+ * adds up any overflow
*/
extern void
qfs_append_justified(qf_str qfs, const char* src, int width)
{
- size_t n ;
-
- if ((src == NULL) || (*src == '\0'))
- n = 0 ;
- else
- n = strlen(src) ;
-
- qfs_append_justified_n(qfs, src, n, width) ;
+ qfs_append_justified_n(qfs, src, qfs_strlen(src), width) ;
} ;
/*------------------------------------------------------------------------------
@@ -222,10 +285,11 @@ qfs_append_justified(qf_str qfs, const char* src, int width)
*
* May append nothing at all !
*
- * This operation is async-signal-safe.
+ * This operation is async-signal-safe. Takes into account the offset, and
+ * adds up any overflow
*/
extern void
-qfs_append_justified_n(qf_str qfs, const char* src, size_t n, int width)
+qfs_append_justified_n(qf_str qfs, const char* src, uint n, int width)
{
if ((int)n >= abs(width))
width = 0 ;
@@ -239,6 +303,23 @@ qfs_append_justified_n(qf_str qfs, const char* src, size_t n, int width)
qfs_append_ch_x_n(qfs, ' ', - width - n) ;
} ;
+/*------------------------------------------------------------------------------
+ * Append single character.
+ *
+ * This operation is async-signal-safe. Takes into account the offset, and
+ * adds up any overflow
+ */
+inline static void
+qfs_append_ch(qf_str qfs, char ch)
+{
+ if (qfs->offset > 0)
+ --qfs->offset ;
+ else if (qfs->ptr < qfs->end)
+ *qfs->ptr++ = ch ;
+ else
+ ++qfs->overflow ;
+} ;
+
/*==============================================================================
* Number conversion
*/
@@ -252,7 +333,8 @@ qfs_number(qf_str qfs, uintmax_t val, int sign, enum pf_flags flags,
*
* Result is appended to the given qf_str.
*
- * This operation is async-signal-safe.
+ * This operation is async-signal-safe. Takes into account the offset, and
+ * adds up any overflow
*/
extern void
qfs_signed(qf_str qfs, intmax_t s_val, enum pf_flags flags,
@@ -280,7 +362,8 @@ qfs_signed(qf_str qfs, intmax_t s_val, enum pf_flags flags,
*
* Result is appended to the given qf_str.
*
- * This operation is async-signal-safe.
+ * This operation is async-signal-safe. Takes into account the offset, and
+ * adds up any overflow
*/
extern void
qfs_unsigned(qf_str qfs, uintmax_t u_val, enum pf_flags flags,
@@ -294,7 +377,8 @@ qfs_unsigned(qf_str qfs, uintmax_t u_val, enum pf_flags flags,
*
* Result is appended to the given qf_str.
*
- * This operation is async-signal-safe.
+ * This operation is async-signal-safe. Takes into account the offset, and
+ * adds up any overflow
*/
extern void
qfs_pointer(qf_str qfs, void* p_val, enum pf_flags flags,
@@ -359,7 +443,8 @@ qfs_pointer(qf_str qfs, void* p_val, enum pf_flags flags,
* characters are to be generated -- ie no: pf_plus, pf_space, pf_zeros,
* or pf_alt (with pf_hex) -- then nothing is generated.
*
- * This operation is async-signal-safe.
+ * This operation is async-signal-safe. Takes into account the offset, and
+ * adds up any overflow
*/
static void
qfs_number(qf_str qfs, uintmax_t val, int sign, enum pf_flags flags,
@@ -438,23 +523,41 @@ qfs_number(qf_str qfs, uintmax_t val, int sign, enum pf_flags flags,
/* Set up any required sign and radix prefix */
if ((flags & pf_unsigned) || (sign == 0))
- sign_str = "" ;
+ {
+ sign_str = "" ;
+ sign_len = 0 ;
+ }
else if (sign < 0)
- sign_str = "-" ;
+ {
+ sign_str = "-" ;
+ sign_len = 1 ;
+ }
else if (flags & pf_plus)
- sign_str = "+" ;
+ {
+ sign_str = "+" ;
+ sign_len = 1 ;
+ }
else if (flags & pf_space)
- sign_str = " " ;
+ {
+ sign_str = " " ;
+ sign_len = 1 ;
+ }
else
- sign_str = "" ;
-
- sign_len = strlen(sign_str) ;
+ {
+ sign_str = "" ;
+ sign_len = 0 ;
+ } ;
- radix_str = "" ;
if ((flags & (pf_hex | pf_alt)) == (pf_hex | pf_alt))
- radix_str = (flags & pf_uc) ? "0X" : "0x" ;
-
- radix_len = strlen(radix_str) ;
+ {
+ radix_str = (flags & pf_uc) ? "0X" : "0x" ;
+ radix_len = 2 ;
+ }
+ else
+ {
+ radix_str = "" ;
+ radix_len = 0 ;
+ } ;
/* Turn off zero fill if left justify (width < 0) */
if (width < 0)
@@ -474,8 +577,7 @@ qfs_number(qf_str qfs, uintmax_t val, int sign, enum pf_flags flags,
base = (flags & pf_hex) ? 16 : 10 ;
digits = (flags & pf_uc) ? uc : lc ;
- e = p = num + sizeof(num) - 1 ;
- *p = '\0' ;
+ e = p = num + sizeof(num) ;
v = val ;
do
{
@@ -647,7 +749,7 @@ enum arg_num_type
ant_long_long, /* ll */
ant_intmax_t, /* j */
ant_size_t, /* z */
- ant_ptr_t, /* void* */
+ ant_ptr_t, /* %p */
ant_default = ant_int,
};
@@ -660,42 +762,51 @@ static enum pf_phase qfs_arg_number(qf_str qfs, va_list* p_va,
enum pf_flags flags, int width, int precision, enum arg_num_type ant) ;
/*------------------------------------------------------------------------------
- * Formatted print to qf_str -- cf printf()
+ * Formatted print to qf_str -- cf printf() -- appends to the qf_str.
*
- * This operation is async-signal-safe.
+ * This operation is async-signal-safe. Takes into account the offset, and
+ * adds up any overflow.
+ *
+ * Returns: the resulting length of the qf_str.
*/
-extern void
+extern uint
qfs_printf(qf_str qfs, const char* format, ...)
{
va_list va ;
+ uint did ;
va_start (va, format);
- qfs_vprintf(qfs, format, va);
+ did = qfs_vprintf(qfs, format, va);
va_end (va);
+
+ return did ;
} ;
/*------------------------------------------------------------------------------
- * Formatted print to qf_str -- cf vprintf()
+ * Formatted print to qf_str -- cf vprintf() -- appends to the qf_str.
*
- * This operation is async-signal-safe.
+ * This operation is async-signal-safe. Takes into account the offset, and
+ * adds up any overflow
*
* Operates on a copy of the va_list -- so the original is *unchanged*.
+ *
+ * Returns: the resulting length of the qf_str.
*/
-extern void
+extern uint
qfs_vprintf(qf_str qfs, const char *format, va_list va)
{
va_list vac ;
if (format == NULL)
- return ;
+ return qfs_len(qfs) ;
va_copy(vac, va) ;
- while ((qfs->ptr < qfs->end) && (*format != '\0'))
+ while (*format != '\0')
{
/* Have space for one byte and current format byte is not '\0' */
if (*format != '%')
- *qfs->ptr++ = *format++ ;
+ qfs_append_ch(qfs, *format++) ;
else
{
const char* start = format++ ; /* start points at the '%' ...
@@ -716,8 +827,12 @@ qfs_vprintf(qf_str qfs, const char *format, va_list va)
{
case '%': /* %% only */
if (phase == pfp_null)
- *qfs->ptr++ = '%' ;
- phase = (phase == pfp_null) ? pfp_done : pfp_failed ;
+ {
+ qfs_append_ch(qfs, '%') ;
+ phase = pfp_done ;
+ }
+ else
+ phase = pfp_failed ;
break ;
case '\'':
@@ -882,14 +997,14 @@ qfs_vprintf(qf_str qfs, const char *format, va_list va)
if (phase == pfp_failed)
{
format = start ; /* back to the start */
- *qfs->ptr++ = *format++ ; /* copy the '%' */
+ qfs_append_ch(qfs, *format++) ;
} ;
} ;
} ;
- *qfs->ptr = '\0' ;
-
va_end(vac) ;
+
+ return qfs_len(qfs) ;
} ;
/*------------------------------------------------------------------------------
@@ -910,7 +1025,8 @@ qfs_vprintf(qf_str qfs, const char *format, va_list va)
* pf_unsigned
* pf_ptr
*
- * This operation is async-signal-safe.
+ * This operation is async-signal-safe. Takes into account the offset, and
+ * adds up any overflow
*/
static enum pf_phase
qfs_arg_string(qf_str qfs, const char* src, enum pf_flags flags,
@@ -954,7 +1070,8 @@ qfs_arg_string(qf_str qfs, const char* src, enum pf_flags flags,
* pf_unsigned
* pf_ptr
*
- * This operation is async-signal-safe.
+ * This operation is async-signal-safe. Takes into account the offset, and
+ * adds up any overflow
*/
static enum pf_phase
qfs_arg_char(qf_str qfs, char ch, enum pf_flags flags, int width, int precision)
@@ -986,7 +1103,8 @@ qfs_arg_char(qf_str qfs, char ch, enum pf_flags flags, int width, int precision)
*
* and: all the number argument types.
*
- * This operation is async-signal-safe.
+ * This operation is async-signal-safe. Takes into account the offset, and
+ * adds up any overflow
*/
static enum pf_phase
qfs_arg_number(qf_str qfs, va_list* p_va, enum pf_flags flags,
diff --git a/lib/qfstring.h b/lib/qfstring.h
index 83caa13d..f6eb1807 100644
--- a/lib/qfstring.h
+++ b/lib/qfstring.h
@@ -22,44 +22,42 @@
#ifndef _ZEBRA_QFSTRING_H
#define _ZEBRA_QFSTRING_H
-#include "zebra.h"
-
-#include <stddef.h>
-#include <stdint.h>
-
-#ifndef Inline
-#define Inline static inline
-#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__ */
+#include "misc.h"
+#include "vargs.h"
/*==============================================================================
* These "qfstrings" address the issues of dealing with *fixed* length
* strings, particularly where the string handling must be async-signal-safe.
+ *
+ * Are also used to support snprintf() style printing, but to one or more
+ * fixed length buffers.
*/
-typedef struct qf_str qf_str_t ;
-typedef struct qf_str* qf_str ;
-
/* When initialised a qf_string is set:
*
- * str = start of string -- and this is never changed
- * ptr = start of string -- this is moved as stuff is appended
- * end = last possible position for terminating '\0'
- * -- and this is never changed
+ * str = start of string -- and this is never changed
+ * ptr = start of string -- this is moved as stuff is appended
+ * end = end of string -- and this is never changed
+ * offset = number of characters left to omit -- used to collect a long
+ * string in more than one section
+ * overflow = number of characters that had to leave out, because string
+ * was too short.
+ *
+ * When filling the string, will work right up to the byte just before the
+ * end. Does not terminate the string -- that must be done explicitly.
*/
struct qf_str
{
- char* str ; /* start of string */
- char* ptr ; /* current position */
- char* end ; /* end of string */
+ char* str ; /* start of string */
+ char* ptr ; /* current position */
+ char* end ; /* end of string */
+ uint offset ; /* number of chars to omit */
+ uint overflow ; /* number of excess chars */
} ;
+typedef struct qf_str qf_str_t[1] ;
+typedef struct qf_str* qf_str ;
+
/*------------------------------------------------------------------------------
* Print format flags for number printing
*/
@@ -68,21 +66,21 @@ enum pf_flags
pf_none = 0,
/* The following correspond to the "flags" */
- pf_commas = 1 << 0, /* "'" seen */
- pf_plus = 1 << 1, /* "+" seen */
- pf_space = 1 << 2, /* " " seen */
- pf_zeros = 1 << 3, /* "0" seen */
- pf_alt = 1 << 4, /* "#" seen */
+ pf_commas = BIT( 0), /* "'" seen */
+ pf_plus = BIT( 1), /* "+" seen */
+ pf_space = BIT( 2), /* " " seen */
+ pf_zeros = BIT( 3), /* "0" seen */
+ pf_alt = BIT( 4), /* "#" seen */
- pf_precision = 1 << 7, /* '.' seen */
+ pf_precision = BIT( 7), /* '.' seen */
/* The following signal how to render the value */
- pf_hex = 1 << 8, /* hex */
- pf_uc = 1 << 9, /* upper-case */
+ pf_hex = BIT( 8), /* hex */
+ pf_uc = BIT( 9), /* upper-case */
/* The following signal the type of value */
- pf_ptr = 1 << 14, /* is a pointer */
- pf_unsigned = 1 << 15, /* unsigned value */
+ pf_ptr = BIT(14), /* is a pointer */
+ pf_unsigned = BIT(15), /* unsigned value */
/* Common combination */
pf_hex_x = pf_unsigned | pf_hex,
@@ -95,22 +93,26 @@ enum pf_flags
* Functions
*/
-extern void qfs_init(qf_str qfs, char* str, size_t size) ;
-extern void qfs_init_as_is(qf_str qfs, char* str, size_t size) ;
+extern void qfs_init(qf_str qfs, char* str, uint size) ;
+extern void qfs_init_offset(qf_str qfs, char* str, uint size, uint offset) ;
+extern void qfs_reset_offset(qf_str qfs, uint offset) ;
+extern void qfs_init_as_is(qf_str qfs, char* str, uint size) ;
-extern void qfs_term(qf_str qfs, const char* src) ;
+Inline uint qfs_overflow(qf_str qfs) ;
+Inline uint qfs_term(qf_str qfs) ;
+extern void qfs_term_string(qf_str qfs, const char* src, uint n) ;
-Inline int qfs_len(qf_str qfs) ;
+Inline uint qfs_len(qf_str qfs) ;
Inline void* qfs_ptr(qf_str qfs) ;
-Inline int qfs_left(qf_str qfs) ;
+Inline uint qfs_left(qf_str qfs) ;
extern void qfs_append(qf_str qfs, const char* src) ;
-extern void qfs_append_n(qf_str qfs, const char* src, size_t n) ;
+extern void qfs_append_n(qf_str qfs, const char* src, uint n) ;
-extern void qfs_append_ch_x_n(qf_str qfs, char ch, size_t n) ;
+extern void qfs_append_ch_x_n(qf_str qfs, char ch, uint n) ;
extern void qfs_append_justified(qf_str qfs, const char* src, int width) ;
extern void qfs_append_justified_n(qf_str qfs, const char* src,
- size_t n, int width) ;
+ uint n, int width) ;
extern void qfs_signed(qf_str qfs, intmax_t s_val, enum pf_flags flags,
int width, int precision) ;
@@ -119,25 +121,27 @@ extern void qfs_unsigned(qf_str qfs, uintmax_t u_val, enum pf_flags flags,
extern void qfs_pointer(qf_str qfs, void* p_val, enum pf_flags flags,
int width, int precision) ;
-extern void qfs_printf(qf_str qfs, const char* format, ...)
+extern uint qfs_printf(qf_str qfs, const char* format, ...)
PRINTF_ATTRIBUTE(2, 3) ;
-extern void qfs_vprintf(qf_str qfs, const char *format, va_list args) ;
+extern uint qfs_vprintf(qf_str qfs, const char *format, va_list args) ;
+
+Inline uint qfs_strlen(const char* str) ;
/*==============================================================================
* The Inline functions.
*/
/*------------------------------------------------------------------------------
- * Current length of qf_str, not counting the terminating '\0'.
+ * Current length of qf_str.
*/
-Inline int
+Inline uint
qfs_len(qf_str qfs)
{
return qfs->ptr - qfs->str ;
} ;
/*------------------------------------------------------------------------------
- * Address of the terminating '\0'.
+ * Address for next byte -- assuming zero offset -- may be the end.
*/
Inline void*
qfs_ptr(qf_str qfs)
@@ -146,14 +150,63 @@ qfs_ptr(qf_str qfs)
} ;
/*------------------------------------------------------------------------------
- * Current space left in the qstr, given what has been reserved for terminating
- * '\0' and any other reservation.
+ * Current space left in the qstr -- assuming zero offset.
*/
-Inline int
+Inline uint
qfs_left(qf_str qfs)
{
return qfs->end - qfs->ptr ;
} ;
+/*------------------------------------------------------------------------------
+ * Did everything we put in the qfs, fit ?.
+ *
+ * Returns: number of chars that did *not* fit.
+ */
+Inline uint
+qfs_overflow(qf_str qfs)
+{
+ return qfs->overflow ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Insert '\0' terminator -- overwrites the last byte, if required.
+ *
+ * Assumes the qfs is not zero length !
+ *
+ * Returns: number of chars that did *not* fit (after using one for '\0').
+ *
+ * NB: does not advance pointer -- so length does not include the '\0'
+ */
+Inline uint
+qfs_term(qf_str qfs)
+{
+ if (qfs->ptr >= qfs->end)
+ {
+ assert((qfs->ptr == qfs->end) && (qfs->ptr > qfs->str)) ;
+ --qfs->ptr ;
+ ++qfs->overflow ;
+ } ;
+
+ *qfs->ptr = '\0' ;
+ return qfs->overflow ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * async-signal-safe strlen
+ */
+Inline uint
+qfs_strlen(const char* str)
+{
+ const char* s ;
+
+ s = str ;
+
+ if (s != NULL)
+ while (*s != '\0')
+ ++s ;
+
+ return s - str ;
+}
#endif /* _ZEBRA_QSTRING_H */
diff --git a/lib/qiovec.c b/lib/qiovec.c
index 546dfcb0..cc24503e 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 "misc.h"
-#include "zebra.h"
+#include <errno.h>
+#include "qdebug_nb.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
*/
@@ -40,12 +54,12 @@
* Returns: address of qiovec
*/
extern qiovec
-qiovec_init_new(qiovec viov)
+qiovec_init_new(qiovec qiov)
{
- if (viov == NULL)
- viov = XCALLOC(MTYPE_QIOVEC, sizeof(struct qiovec)) ;
+ if (qiov == NULL)
+ qiov = XCALLOC(MTYPE_QIOVEC, sizeof(struct qiovec)) ;
else
- memset(viov, 0, sizeof(struct qiovec)) ;
+ memset(qiov, 0, sizeof(struct qiovec)) ;
/* Zeroising has set:
*
@@ -60,7 +74,7 @@ qiovec_init_new(qiovec viov)
* Nothing more is required.
*/
- return viov ;
+ return qiov ;
} ;
/*------------------------------------------------------------------------------
@@ -69,80 +83,187 @@ qiovec_init_new(qiovec viov)
* Returns: address of qiovec (if any) -- NULL if structure released
*/
extern qiovec
-qiovec_reset(qiovec viov, bool free_structure)
+qiovec_reset(qiovec qiov, bool free_structure)
{
- if (viov != NULL)
+ if (qiov != NULL)
{
- if (viov->vec != NULL)
- XFREE(MTYPE_QIOVEC_VEC, viov->vec) ;
+ if (qiov->vec != NULL)
+ XFREE(MTYPE_QIOVEC_VEC, qiov->vec) ;
if (free_structure)
- XFREE(MTYPE_QIOVEC, viov) ; /* sets viov = NULL */
+ XFREE(MTYPE_QIOVEC, qiov) ; /* sets qiov = NULL */
else
- qiovec_init_new(viov) ; /* re-initialise */
+ qiovec_init_new(qiov) ; /* re-initialise */
} ;
- return viov ;
+ return qiov ;
} ;
/*------------------------------------------------------------------------------
* Clear given qiovec.
*/
extern void
-qiovec_clear(qiovec viov)
+qiovec_clear(qiovec qiov)
{
- viov->i_get = 0 ;
- viov->i_put = 0 ;
- viov->writing = 0 ;
+ qiov->i_get = 0 ;
+ qiov->i_put = 0 ;
+ qiov->writing = 0 ;
} ;
/*------------------------------------------------------------------------------
- * Push item to given qiovec
- *
- * NB: avoids pushing zero length items.
+ * Establish total length of the qiov -- add up all the item lengths.
*/
-extern void
-qiovec_push(qiovec viov, const void* base, size_t len)
+extern size_t
+qiovec_length(qiovec qiov)
{
- struct iovec* p_iov ;
+ if (qiov->i_put > qiov->i_get)
+ {
+ size_t length ;
+ uint n ;
+ qiov_item item ;
+
+ n = qiov->i_put - qiov->i_get ;
+ item = &qiov->vec[qiov->i_get] ;
- if (len == 0)
- return ;
+ length = item->len ;
+ while (--n)
+ length += (++item)->len ;
- if (viov->i_put >= viov->i_alloc)
+ return length ;
+ }
+ else
{
- size_t size ;
- assert(viov->i_put == viov->i_alloc) ;
+ assert(qiov->i_get == qiov->i_put) ;
+ qiov->i_get = qiov->i_put = 0 ;
+
+ return 0 ;
+ } ;
+} ;
- assert( ((viov->i_alloc == 0) && (viov->vec == NULL))
- || ((viov->i_alloc != 0) && (viov->vec != NULL)) ) ;
- if (viov->i_get > 200) /* keep in check */
- {
- size = (viov->i_put - viov->i_get) * sizeof(struct iovec) ;
- if (size != 0)
- memmove(viov->vec, &viov->vec[viov->i_get], size) ;
- viov->i_put -= viov->i_get ;
- viov->i_get = 0 ;
- }
- else
- {
- viov->i_alloc += 100 ; /* a sizable chunk */
+/*==============================================================================
+ * Extending and shuffling qiovec in order to support push and unshift
+ * operations.
+ *
+ * Note that if unshift becomes common, then should adjust the following.
+ * Also address the tendency to reset i_get and i_put to zero whenever they
+ * are found to be equal.
+ */
+enum
+{
+ i_get_limit = 200, /* in qiovec_extend, shuffle vector down if
+ i_get is >= i_get_limit */
- size = viov->i_alloc * sizeof(struct iovec) ;
- if (viov->vec == NULL)
- viov->vec = XMALLOC(MTYPE_QIOVEC_VEC, size) ;
- else
- viov->vec = XREALLOC(MTYPE_QIOVEC_VEC, viov->vec, size) ;
- } ;
+ i_alloc_unit = 100, /* allocate in lots of this much */
+
+ i_get_margin = 20, /* in qiovec_shuffle, if have to reorganise,
+ allow for this many unshifts. */
+
+ i_put_margin = 40, /* in qiovec_shuffle, if have to reorganise,
+ allow for this many pushes. */
+} ;
+
+static void qiovec_move_vector(qiovec qiov, uint new_i_get) ;
+static void qiovec_new_vector(qiovec qiov, uint items) ;
+
+/*------------------------------------------------------------------------------
+ * Extend -- see qiovec_push.
+ *
+ * NB: underlying vector may be reorganised, reallocated, etc. All pointers
+ * to items are now INVALID.
+ */
+Private void
+qiovec_extend(qiovec qiov)
+{
+ assert(qiov->i_put <= qiov->i_alloc) ;
+ assert(qiov->i_get <= qiov->i_put) ;
+
+ assert( ((qiov->i_alloc == 0) && (qiov->vec == NULL))
+ || ((qiov->i_alloc != 0) && (qiov->vec != NULL)) ) ;
+
+ /* Short circuit if can make space by resetting indexes */
+ if ((qiov->i_put == qiov->i_get) && (qiov->i_alloc != 0))
+ {
+ qiov->i_put = 0 ;
+ qiov->i_get = 0 ;
+
+ return ;
} ;
- p_iov = &viov->vec[viov->i_put++] ;
+ if (qiov->i_get > i_get_limit) /* keep in check */
+ qiovec_move_vector(qiov, 0) ;
+ else
+ qiovec_new_vector(qiov, i_alloc_unit) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Shuffle to allow for qiovec_unshift.
+ *
+ * Don't expect this to happen often -- but will do it if required.
+ *
+ * On exit the i_get will be > 0.
+ *
+ * NB: underlying vector may be reorganised, reallocated, etc. All pointers
+ * to items are now INVALID.
+ */
+Private void
+qiovec_shuffle(qiovec qiov)
+{
+ assert(qiov->i_put <= qiov->i_alloc) ;
+ assert(qiov->i_get <= qiov->i_put) ;
+
+ assert( ((qiov->i_alloc == 0) && (qiov->vec == NULL))
+ || ((qiov->i_alloc != 0) && (qiov->vec != NULL)) ) ;
- p_iov->iov_base = miyagi(base) ;
- p_iov->iov_len = len ;
+ if (qiov->i_get > 0)
+ return ; /* shouldn't be here, really */
+
+ /* If do not have enough spare space for both margins, allocate extra to
+ * the tune of both margins -- will leave at least the margins and at
+ * most nearly twice those margins.
+ *
+ * This will allocate a vector is there is none at all.
+ */
+ if ((qiov->i_alloc - qiov->i_put) < (i_get_margin + i_put_margin))
+ qiovec_new_vector(qiov, i_get_margin + i_put_margin) ;
+
+ /* Shuffle into place and update i_get and i_put. */
+ qiovec_move_vector(qiov, i_get_margin) ;
+
+ confirm(i_get_margin > 0) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Move contents of vector and set a new i_get (and i_put to suit).
+ */
+static void
+qiovec_move_vector(qiovec qiov, uint new_i_get)
+{
+ memmove(&qiov->vec[new_i_get], &qiov->vec[qiov->i_get],
+ (qiov->i_put - qiov->i_get) * sizeof(qiovec_t)) ;
+
+ qiov->i_put -= qiov->i_get - new_i_get ;
+ qiov->i_get = new_i_get ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Allocate or reallocate vector for given number of items.
+ */
+static void
+qiovec_new_vector(qiovec qiov, uint items)
+{
+ qiov->i_alloc += items ;
+
+ qiov->vec = XREALLOC(MTYPE_QIOVEC_VEC, qiov->vec,
+ qiov->i_alloc * sizeof(qiovec_t)) ;
} ;
+/*==============================================================================
+ * Writing qiovec and iovec
+ */
+
+static ssize_t writev_qdebug_nb(int fd, struct iovec p_iov[], int n) ;
+
/*------------------------------------------------------------------------------
* Write given qiovec -- assuming NON-BLOCKING.
*
@@ -157,24 +278,24 @@ qiovec_push(qiovec viov, const void* base, size_t len)
* -1 => failed -- see errno
*/
extern int
-qiovec_write_nb(int fd, qiovec viov)
+qiovec_write_nb(int fd, qiovec qiov)
{
int n ;
int l ;
- n = viov->i_put - viov->i_get ;
+ n = qiov->i_put - qiov->i_get ;
- l = iovec_write_nb(fd, &viov->vec[viov->i_get], n) ;
+ l = iovec_write_nb(fd, (struct iovec*)(&qiov->vec[qiov->i_get]), n) ;
if (l == 0)
{
- viov->writing = 0 ;
- viov->i_get = viov->i_put = 0 ;
+ qiov->writing = false ;
+ qiov->i_get = qiov->i_put = 0 ;
}
else
{
- viov->writing = 1 ;
- viov->i_get += (n - l) ;
+ qiov->writing = true ;
+ qiov->i_get += (n - l) ;
} ;
return l ;
@@ -209,8 +330,6 @@ qiovec_write_nb(int fd, qiovec viov)
extern int
iovec_write_nb(int fd, struct iovec p_iov[], int n)
{
- ssize_t ret ;
-
assert(n >= 0) ;
/* Skip past any leading zero length entries */
@@ -222,7 +341,12 @@ iovec_write_nb(int fd, struct iovec p_iov[], int n)
while (n > 0)
{
- ret = writev(fd, p_iov, (n < IOV_MAX ? n : IOV_MAX)) ;
+ ssize_t ret ;
+
+ if (qdebug_nb)
+ ret = writev_qdebug_nb(fd, p_iov, (n < IOV_MAX ? n : IOV_MAX)) ;
+ else
+ ret = writev (fd, p_iov, (n < IOV_MAX ? n : IOV_MAX)) ;
if (ret > 0)
{
@@ -243,6 +367,7 @@ iovec_write_nb(int fd, struct iovec p_iov[], int n)
ret = 0 ;
} ;
} ;
+
}
else if (ret == 0)
break ; /* not sure can happen... but
@@ -253,9 +378,59 @@ iovec_write_nb(int fd, struct iovec p_iov[], int n)
if ((err == EAGAIN) || (err == EWOULDBLOCK))
break ;
if (err != EINTR)
+// assert(0) ; // Pro tem
return -1 ; /* failed */
} ;
} ;
return n ;
} ;
+
+/*==============================================================================
+ * Simulation of writev() for debug purposes -- generates lots of partial
+ * writes and lots of blocking
+ *
+ */
+
+static qrand_seq_t wseq = QRAND_SEQ_INIT(4001) ;
+
+static const int blocking_errs[] = { EAGAIN, EWOULDBLOCK, EINTR } ;
+
+/*------------------------------------------------------------------------------
+ * Simulate writev() with tiny output buffers and lots of blocking.
+ */
+static ssize_t
+writev_qdebug_nb(int fd, struct iovec p_iov[], int n)
+{
+ struct iovec* q_iov ;
+ size_t len ;
+ ssize_t ret ;
+
+ assert(n > 0) ;
+
+ if (qrand(wseq, 3) == 0) /* 1/3 chance of blocking */
+ {
+ errno = blocking_errs[qrand(wseq, 3)] ;
+ return -1 ;
+ } ;
+
+ /* Process 1..n or 1..3 entries */
+ n = qrand(wseq, (n < 3) ? n : 3) ;
+ q_iov = p_iov + n ;
+ ++n ;
+
+ /* If new last entry is not zero length, write only part of
+ * it.
+ */
+ len = q_iov->iov_len ;
+ if (len > 0)
+ q_iov->iov_len = qrand(wseq, (len < 200) ? len : 200) + 1 ;
+
+ ret = writev(fd, p_iov, n) ;
+
+ q_iov->iov_len = len ; /* restore true length of entry */
+
+ return ret ;
+} ;
+
+
diff --git a/lib/qiovec.h b/lib/qiovec.h
index ee03d2f2..0cbf0364 100644
--- a/lib/qiovec.h
+++ b/lib/qiovec.h
@@ -22,33 +22,58 @@
#ifndef _ZEBRA_QIOVEC_H
#define _ZEBRA_QIOVEC_H
-#include "zebra.h"
+#include "misc.h"
+#include <sys/uio.h> /* iovec stuff */
-#include <stddef.h>
-#include <stdint.h>
-#include <stdbool.h>
+/*==============================================================================
+ * Alternative naming for struct iovec and its entries.
+ */
+struct qiov_item
+{
+ const char* base ;
+ size_t len ;
+} ;
+
+typedef struct qiov_item* qiov_item ;
+typedef struct qiov_item qiov_item_t[1] ;
+typedef struct qiov_item* qiov_vector ;
+
+union
+{
+ struct qiov_item q ;
+ struct iovec s ;
+} union_iovec ;
+
+CONFIRM(sizeof(struct qiov_item) == sizeof(struct iovec)) ;
-#ifndef Inline
-#define Inline static inline
-#endif
+CONFIRM(offsetof(struct qiov_item, base) == offsetof(struct iovec, iov_base)) ;
+CONFIRM(sizeof (union_iovec.q. base) == sizeof (union_iovec.s.iov_base)) ;
+CONFIRM(offsetof(struct qiov_item, len) == offsetof(struct iovec, iov_len)) ;
+CONFIRM(sizeof (union_iovec.q. len) == sizeof (union_iovec.s.iov_len)) ;
/*==============================================================================
* Flexible size "struct iovec"
*
* NB: a completely zero structure is a valid, empty qiovec.
*/
-typedef struct qiovec* qiovec ;
-typedef struct qiovec qiovec_t ;
struct qiovec
{
- struct iovec* vec ; /* the actual iovec array */
+ qiov_vector vec ; /* the actual iovec array */
+
+ uint i_get ; /* next entry to get */
+ uint i_put ; /* next entry to put */
+
+ uint i_alloc ; /* number of entries allocated */
bool writing ; /* started, but not finished */
+} ;
- unsigned i_get ; /* next entry to get */
- unsigned i_put ; /* next entry to put */
+typedef struct qiovec* qiovec ;
+typedef struct qiovec qiovec_t[1] ;
- unsigned i_alloc ; /* number of entries allocated */
+enum
+{
+ QIOVEC_INIT_ALL_ZEROS = true
} ;
/*==============================================================================
@@ -57,18 +82,39 @@ struct qiovec
extern qiovec qiovec_init_new(qiovec qiov) ;
extern qiovec qiovec_reset(qiovec qiov, bool free_structure) ;
-
-#define qiovec_reset_keep(qiov) qiovec_reset(qiov, 0)
-#define qiovec_reset_free(qiov) qiovec_reset(qiov, 1)
+Inline qiovec qiovec_free(qiovec qiov) ;
Inline bool qiovec_empty(qiovec qiov) ;
+extern size_t qiovec_length(qiovec qiov) ;
extern void qiovec_clear(qiovec qiov) ;
-extern void qiovec_push(qiovec qiov, const void* base, size_t len) ;
extern int qiovec_write_nb(int fd, qiovec qiov) ;
+Inline uint qiovec_count(qiovec qiov) ;
+Inline void qiovec_push_this(qiovec qiov, const void* base, size_t len) ;
+Inline void qiovec_push(qiovec qiov, qiov_item item) ;
+Inline void qiovec_pop(qiovec qiov, qiov_item item) ;
+Inline void qiovec_shift(qiovec qiov, qiov_item item) ;
+Inline void qiovec_unshift(qiovec qiov, qiov_item item) ;
+
extern int iovec_write_nb(int fd, struct iovec* p_iov, int n) ;
Inline void iovec_set(struct iovec* p_iov, const void* base, size_t len) ;
+Private void qiovec_extend(qiovec qiov) ;
+Private void qiovec_shuffle(qiovec qiov) ;
+
+/*------------------------------------------------------------------------------
+ * Free given qiovec
+ *
+ * It is the caller's responsibility to free anything that the qiovec may
+ * point to.
+ *
+ * Returns: NULL
+ */
+Inline qiovec qiovec_free(qiovec qiov)
+{
+ return qiovec_reset(qiov, free_it) ;
+} ;
+
/*------------------------------------------------------------------------------
* Is given qiov empty ?
*
@@ -82,6 +128,107 @@ qiovec_empty(qiovec qiov)
} ;
/*------------------------------------------------------------------------------
+ * How many entries in the given qiov ?
+ */
+Inline uint
+qiovec_count(qiovec qiov)
+{
+ return (qiov->i_put - qiov->i_get) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Push to qiov -- same as qiovec_push, but with item.
+ *
+ * Ignores zero length items.
+ */
+Inline void
+qiovec_push_this(qiovec qiov, const void* base, size_t len)
+{
+ qiov_item_t item ;
+
+ item->base = base ;
+ item->len = len ;
+ qiovec_push(qiov, item) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Push (copy of) item to qiov -- ie append it to the end of the vector.
+ */
+Inline void
+qiovec_push(qiovec qiov, qiov_item item)
+{
+ if (item->len != 0)
+ {
+ if (qiov->i_put >= qiov->i_alloc)
+ qiovec_extend(qiov) ;
+
+ qiov->vec[qiov->i_put++] = *item ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Pop last item from qiov -- return empty item if none.
+ */
+Inline void
+qiovec_pop(qiovec qiov, qiov_item item)
+{
+ if (qiov->i_get < qiov->i_put)
+ {
+ *item = qiov->vec[--qiov->i_put] ;
+ if (qiov->i_get == qiov->i_put)
+ qiov->i_get = qiov->i_put = 0 ;
+ }
+ else
+ {
+ assert(qiov->i_get == qiov->i_put) ;
+ item->base = NULL ;
+ item->len = 0 ;
+
+ qiov->i_get = qiov->i_put = 0 ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Shift first item off qiov -- return empty item if none.
+ */
+Inline void
+qiovec_shift(qiovec qiov, qiov_item item)
+{
+ if (qiov->i_get < qiov->i_put)
+ {
+ *item = qiov->vec[qiov->i_get++] ;
+ if (qiov->i_get == qiov->i_put)
+ qiov->i_get = qiov->i_put = 0 ;
+ }
+ else
+ {
+ assert(qiov->i_get == qiov->i_put) ;
+ item->base = NULL ;
+ item->len = 0 ;
+
+ qiov->i_get = qiov->i_put = 0 ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Unshift item onto qiov -- ie prepend it to the start of the vector.
+ */
+Inline void
+qiovec_unshift(qiovec qiov, qiov_item item)
+{
+ if (qiov->i_get == 0)
+ {
+ if (qiov->i_put == 0)
+ return qiovec_push(qiov, item) ;
+
+ qiovec_shuffle(qiov) ;
+ } ;
+
+ if (item->len != 0)
+ qiov->vec[--qiov->i_get] = *item ;
+} ;
+
+/*------------------------------------------------------------------------------
* Set a given struct iovec
*
* Gets around the fact that the standard structure does not have a const
diff --git a/lib/qlib_init.c b/lib/qlib_init.c
index c44de575..757a9e65 100644
--- a/lib/qlib_init.c
+++ b/lib/qlib_init.c
@@ -18,16 +18,18 @@
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
-
+#include "misc.h"
#include "qlib_init.h"
#include "zassert.h"
#include "memory.h"
+#include "qpnexus.h"
#include "qpthreads.h"
#include "qpselect.h"
#include "thread.h"
#include "privs.h"
#include "mqueue.h"
#include "pthread_safe.h"
+#include "log_local.h"
/*==============================================================================
* Quagga Library Initialise/Closedown
@@ -66,30 +68,33 @@
*
*/
-void
+extern void
qlib_init_first_stage(void)
{
qps_start_up() ;
}
-void
-qlib_init_second_stage(int pthreads)
+extern void
+qlib_init_second_stage(bool pthreads)
{
qpt_set_qpthreads_enabled(pthreads);
+ qpn_init() ;
memory_init_r();
thread_init_r();
+ log_init_r() ;
zprivs_init_r();
mqueue_initialise();
safe_init_r();
}
-void
+extern void
qexit(int exit_code)
{
safe_finish();
mqueue_finish();
zprivs_finish();
+ log_finish();
thread_finish();
memory_finish();
exit (exit_code);
diff --git a/lib/qlib_init.h b/lib/qlib_init.h
index 0d5fbd7e..6c6c1a01 100644
--- a/lib/qlib_init.h
+++ b/lib/qlib_init.h
@@ -22,6 +22,8 @@
#ifndef _ZEBRA_QLIB_INIT_H
#define _ZEBRA_QLIB_INIT_H
+#include "misc.h"
+
/*==============================================================================
* Quagga Library Initialise/Closedown
*
@@ -33,7 +35,7 @@
extern void qlib_init_first_stage(void) ;
-extern void qlib_init_second_stage(int pthreads) ;
+extern void qlib_init_second_stage(bool pthreads) ;
extern void qexit(int exit_code) ;
diff --git a/lib/qpath.c b/lib/qpath.c
new file mode 100644
index 00000000..28757f11
--- /dev/null
+++ b/lib/qpath.c
@@ -0,0 +1,1212 @@
+/* Some primitive path handling
+ * Copyright (C) 2010 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify
+ * it 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.
+ */
+#include "misc.h"
+#include "qpath.h"
+#include "qstring.h"
+
+#include "zassert.h"
+
+#include <unistd.h>
+#include <errno.h>
+
+/*==============================================================================
+ * Some primitive path handling, based on qstrings.
+ *
+ *
+ *==============================================================================
+ * Path Reduction
+ *
+ * As per POSIX, multiple '/' count as single '/', except for the very special
+ * case of exactly two '/' at the start of a path. (That case is referred to
+ * here as a "double root".)
+ *
+ * So this code reduces runs of '/' to single '/' -- except for the special
+ * case.
+ *
+ * This code also replaces "/./" by "/".
+ *
+ *
+ */
+
+static void qpath_reduce(qpath qp) ;
+
+/*------------------------------------------------------------------------------
+ * Initialise a brand new qpath -- allocate if required.
+ *
+ * The result is a path with a not-empty body. All qpath values may be assumed
+ * to have a body at all times.
+ *
+ * Returns: address of qpath
+ *
+ * NB: assumes initialising a new structure. If not, then caller should
+ * use qpath_reset() or qpath_clear().
+ */
+extern qpath
+qpath_init_new(qpath qp)
+{
+ if (qp == NULL)
+ qp = XCALLOC(MTYPE_QPATH, sizeof(qpath_t)) ;
+ else
+ memset(qp, 0, sizeof(qpath_t)) ;
+
+ qs_init_new(qp->path, 50) ; /* Always have a body */
+
+ return qp ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Create a new qpath if don't already have one.
+ */
+inline static qpath
+qpath_make_if_null(qpath qp)
+{
+ if (qp == NULL)
+ qp = qpath_init_new(NULL) ;
+
+ return qp ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Reset contents of given qpath, freeing the structure if required.
+ *
+ * Discards all the contents of the qpath.
+ */
+extern qpath
+qpath_reset(qpath qp, free_keep_b free_structure)
+{
+ if (qp == NULL)
+ return NULL ;
+
+ qs_reset(qp->path, keep_it) ;
+
+ if (free_structure)
+ XFREE(MTYPE_QPATH, qp) ; /* sets qp = NULL */
+ else
+ ;
+
+ return qp ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Clear down given qpath -- retaining any body, but setting it empty.
+ */
+extern qpath
+qpath_clear(qpath qp)
+{
+ if (qp == NULL)
+ return NULL ;
+
+ qs_clear(qp->path) ;
+
+ return qp ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set given qpath to copy of the given string -- allocate if required.
+ *
+ * Reduces the path (see above).
+ */
+extern qpath
+qpath_set(qpath dst, const char* src)
+{
+ return qpath_set_n(dst, src, (src != NULL) ? strlen(src) : 0) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set given qpath to copy of the given qstring -- allocate if required.
+ *
+ * Reduces the path (see above).
+ */
+extern qpath
+qpath_set_qs(qpath dst, const qstring src)
+{
+ return qpath_set_n(dst, qs_char(src), qs_len(src)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set given qpath to copy of the given string -- allocate if required.
+ *
+ * Reduces the path (see above).
+ */
+extern qpath
+qpath_set_n(qpath dst, const char* src, ulen n)
+{
+ dst = qpath_make_if_null(dst) ;
+
+ qs_set_n(dst->path, src, n) ;
+
+ qpath_reduce(dst) ;
+
+ return dst ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Copy qpath to the given qpath -- creating qpath if required.
+ * The result is an empty qpath if the given one is NULL.
+ */
+extern qpath
+qpath_copy(qpath dst, const qpath src)
+{
+ if (src != NULL)
+ return qpath_set_n(dst, qs_char_nn(src->path), qs_len_nn(src->path)) ;
+ else
+ return qpath_set_n(dst, NULL, 0) ;
+} ;
+
+/*==============================================================================
+ * Interfaces to system.
+ */
+
+/*------------------------------------------------------------------------------
+ * Get the current working directory -- creates a qpath if required.
+ *
+ * Returns: the (new) qpath if OK
+ * NULL if not OK -- any existing qpath is cleared.
+ *
+ * If fails will be because some directory on the way back to the root is
+ * not readable or searchable (!).
+ */
+extern qpath
+qpath_getcwd(qpath dst)
+{
+ qpath od ;
+
+ od = dst ;
+ dst = qpath_make_if_null(dst) ;
+
+ qs_new_size(dst->path, 50) ;
+
+ while (1)
+ {
+ void* r ;
+ usize s ;
+
+ s = qs_size_nn(dst->path) ;
+ r = getcwd(qs_char_nn(dst->path), s) ;
+
+ if (r != NULL)
+ {
+ qs_set_strlen_nn(dst->path) ;
+ return dst ; /* exit here if OK. */
+ } ;
+
+ if (errno != ERANGE)
+ break ; /* exit here on failure */
+
+ qs_new_size(dst->path, s * 2) ;
+ } ;
+
+ if (od == NULL)
+ qpath_reset(dst, free_it) ;
+ else
+ qpath_clear(dst) ;
+
+ return NULL ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set the current working directory
+ *
+ * Returns: 0 <=> OK
+ * errno otherwise
+ */
+extern int
+qpath_setcwd(qpath qp)
+{
+ return (chdir(qpath_string(qp)) == 0) ? 0 : errno ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Do "stat" for given path.
+ *
+ * Returns: 0 <=> OK
+ * errno otherwise
+ */
+extern int
+qpath_stat(qpath qp, struct stat* sbuf)
+{
+ return (stat(qpath_string(qp), sbuf) == 0) ? 0 : errno ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Is given path a file we might access -- according to stat ?
+ *
+ * Returns: -1 <=> no -- can access etc, but it's not a file
+ * 0 <=> yes
+ * errno otherwise
+ */
+extern int
+qpath_stat_is_file(qpath qp)
+{
+ struct stat sbuf[1] ;
+ int err ;
+
+ err = qpath_stat(qp, sbuf) ;
+
+ return (err == 0) ? (S_ISREG(sbuf->st_mode) ? 0 : -1)
+ : err ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Is given path a directory we might access -- according to stat ?
+ *
+ * Returns: -1 <=> no -- can access etc, but it's not a directory
+ * 0 <=> yes
+ * errno otherwise
+ */
+extern int
+qpath_stat_is_directory(qpath qp)
+{
+ struct stat sbuf[1] ;
+ int err ;
+
+ err = qpath_stat(qp, sbuf) ;
+
+ return (err == 0) ? (S_ISDIR(sbuf->st_mode) ? 0 : -1)
+ : err ;
+} ;
+
+/*==============================================================================
+ * Path editing functions
+ *
+ *
+ */
+
+/*------------------------------------------------------------------------------
+ * Shave any file part off the end of the given path.
+ *
+ * This treats anything after the last '/' of the path as being the "file part".
+ *
+ * The cases are (where 'a' is anything except '/' and 'z' is anything):
+ *
+ * 1. "" -- empty -> unchanged
+ *
+ * 2. "aaa" -- file part only -> ""
+ *
+ * 3. "/" -- root only, or -> unchanged
+ * "//" double root only -> unchanged
+ *
+ * 4. "/aaa" -- routed file -> "/"
+ * "//aaa" double routed file -> "//"
+ *
+ * 5. "zzz/" -- no file part -> unchanged
+ *
+ * 6. "zzz/aaa" -- non-empty file part -> "zzz/"
+ *
+ * Ensures that the path is "reduced" before shaving.
+ *
+ * Creates a new, empty path if qp is NULL.
+ */
+extern qpath
+qpath_shave(qpath qp)
+{
+ pp_t p ;
+
+ qp = qpath_make_if_null(qp) ;
+
+ qs_pp_nn(p, qp->path) ;
+
+ /* Track back to last '/' */
+ while ( (p->e > p->p) && (*(p->e - 1) != '/') )
+ --p->e ;
+
+ qs_set_len_nn(qp->path, p->e - p->p) ;
+
+ return qp ;
+} ;
+
+#if 0
+
+/*==============================================================================
+ * Pop the last part of the given path.
+ *
+ * The cases are (where 'a' is anything except '/' and 'z' is anything):
+ *
+ * 1. "" -- empty path
+ *
+ * - path unchanged
+ * - return empty part
+ *
+ * 2. "aaa" -- the path is a single part
+ *
+ * - part removed from path, leaves ""
+ * - return the part
+ *
+ * Note that in (1) and (2) there is no '/', in the following there is at least
+ * one '/'.
+ *
+ * 3. "/" -- root only, or
+ * "//" double root only
+ *
+ * - path unchanged
+ * - return empty part
+ *
+ * 4. "/aaa" -- one remaining part before the root,
+ * "//aaa" or one remaining part after double route
+ *
+ * - part removed from path, leaves "/" or "//"
+ * - return the part
+ *
+ * 5. "zzz/" -- empty last part
+ *
+ * - "/" removed from end of path
+ * - return empty part
+ *
+ * 6. "zzz/aaa" -- non-empty last part
+ *
+ * - part and "/" removed from end of path
+ * - return part
+ *
+ * Note that other forms of multiple '/' have been reduced, already.
+ */
+extern qpath
+qpath_pop(qpath to, qpath from)
+{
+ qpath part ;
+ qstring qs ;
+ char* sp ;
+ char* p ;
+
+ /* Get pointers to start and end of path */
+ if (qp != NULL)
+ {
+ qs = &qp->path ;
+
+ sp = qs_chars(qs) ; /* NULL if qpath is completely empty */
+ p = qs_ep_char(qs) ; /* points at trailing '\0' */
+ }
+ else
+ qs = sp = p = NULL ;
+
+ /* Deal with NULL-ness */
+ if (sp == NULL)
+ {
+ assert(p == sp) ; /* ie qs->len == 0 if qs->body == NULL */
+
+ return qpath_init_new(NULL, NULL) ;
+ } ;
+
+ /* Track back to last '/' */
+ while ( (p > sp) && (*(p - 1) != '/') )
+ --p ;
+
+ /* Construct result which is from p forwards
+ *
+ * p == NULL if the given path is completely empty.
+ */
+ part = qpath_init_new(NULL, p) ;
+
+ /* If what remains is not empty, and not just "/" or "//", remove trailing '/'
+ *
+ * If p == sp, there was no '/', so path ends up empty.
+ * If p == sp + 1, there is one '/', and that is at the front of the path
+ * If p == sp + 2, there is either "//" or "a/"
+ * If p > sp + 2, there is "aa/"
+ */
+ if (p >= (sp + 2)) /* if "//", "a/" or "aa/" etc. */
+ {
+ /* Unless is special case of "//"... */
+ if ( ! ((p == (sp + 2)) && (*sp == '/')) )
+ --p ; /* ... discard trailing '/' */
+ } ;
+
+ qs->len = (p - sp) ; /* set the new length (shorter !) */
+ *p = '\0' ; /* and terminate */
+
+ /* Return the part we hacked off */
+ return part ;
+} ;
+
+
+
+
+/*==============================================================================
+ * Shift off the first part of the given path.
+ *
+ * The cases are (where 'a' is anything except '/' and 'z' is anything):
+ *
+ * 1. "" -- empty path
+ *
+ * - path unchanged
+ * - return empty part
+ *
+ * 2. "aaa" -- the path is a single part
+ *
+ * - part removed from path, leaves ""
+ * - return the part
+ *
+ * Note that in (1) and (2) there is no '/', in the following there is at least
+ * one '/'.
+ *
+ * 3. "/" -- root only, or
+ * "//" double root
+ *
+ * - remove the "/" or "//" -- result is empty
+ * - return "/" or "//" part
+ *
+ * 4. "/azz" -- root followed by stuff, or
+ * "//zzz" double root followed by stuff.
+ *
+ * - remove the "/" or "//"
+ * - return "/" or "//" part
+ *
+ * 5. "aaa/" -- something followed by first '/' which is end of path
+ *
+ * - remove upto and including '/' -- result is empty
+ * - return upto but excluding '/'
+ *
+ * 6. "aaa/azz" -- something followed by first '/' followed by something else
+ *
+ * - remove upto and including '/'
+ * - return upto but excluding '/'
+ *
+ * Note that other forms of multiple '/' have been reduced, already.
+ */
+extern qpath
+qpath_shift(qpath qp)
+{
+ qpath part ;
+ qstring qs ;
+ char* sp ;
+ char* p ;
+ char* q ;
+
+ /* Get pointers to start and end of path */
+ if (qp != NULL)
+ {
+ qs = &qp->path ;
+ sp = qs_chars(qs) ; /* NULL if qpath is completely empty */
+ }
+ else
+ qs = sp = NULL ;
+
+ /* Deal with NULL-ness */
+ if (sp == NULL)
+ return qpath_init_new(NULL, NULL) ;
+
+ p = sp ;
+
+ /* Set p such that sp..p-1 is to be shifted off.
+ * And q such that q..end-1 is to be kept.
+ */
+ if (*sp == '/')
+ {
+ ++p ; /* single root */
+ if (*p == '/')
+ ++p ; /* double root */
+ q = p ;
+ }
+ else
+ {
+ while ((*p != '/') && (*p != '/0'))
+ ++p ; /* step to '/' or end */
+
+ if (*p == '/')
+ q = p + 1 ; /* don't keep '/' */
+ else
+ q = p ; /* keep '\0' ! */
+ } ;
+
+ /* Construct qpath for shifted off stuff */
+ part = qpath_init_new(NULL, NULL) ;
+
+ /* Hack off the shifted off stuff & '/' if required */
+
+
+ /* Return the part we hacked off */
+ return part ;
+} ;
+#endif
+
+/*==============================================================================
+ * Append, Prepend and Complete
+ */
+
+static ulen qpath_trim_home(const char* p, ulen n) ;
+
+/*------------------------------------------------------------------------------
+ * Append one path (src) onto the end of the given path (dst).
+ *
+ * If the dst path is NULL, creates a new, empty qpath to append to.
+ *
+ * The dst path is assumed to be the path to a "directory". An empty dst path
+ * is treated as "the current directory".
+ *
+ * If src path starts '/' or '~', then it is trimmed, removing leading
+ * characters up to and including '/'.
+ *
+ * If dst path is not empty, and does not end '/', then an '/' is appended
+ * before the src is appended.
+ *
+ * Note that this means:
+ *
+ * -- appending an empty path or one which is just "/", will leave the dst
+ * path ending "/" -- unless the dst path is empty.
+ *
+ * -- cannot create a rooted path by appending a path onto an empty path.
+ *
+ * -- appending a "homed" path "~..../" is assumed to be appending to the
+ * required "home".
+ *
+ * The resulting path is reduced (see above).
+ */
+extern qpath
+qpath_append(qpath dst, const qpath src)
+{
+ if (src != NULL)
+ return qpath_append_str_n(dst, qs_char_nn(src->path),
+ qs_len_nn(src->path)) ;
+ else
+ return qpath_append_str_n(dst, NULL, 0) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Append src qstring onto the end of the dst path.
+ *
+ * See above for discussion of "append" operation.
+ */
+extern qpath
+qpath_append_qs(qpath dst, const qstring src)
+{
+ return qpath_append_str_n(dst, qs_char(src), qs_len(src)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Append src string onto the end of the dst path.
+ *
+ * See above for discussion of "append" operation.
+ */
+extern qpath
+qpath_append_str(qpath dst, const char* src)
+{
+ return qpath_append_str_n(dst, src, (src != NULL) ? strlen(src) : 0) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Append src string of given length onto the end of the dst path.
+ *
+ * See above for discussion of "append" operation.
+ */
+extern qpath
+qpath_append_str_n(qpath dst, const char* src, ulen n)
+{
+ ulen l ;
+
+ dst = qpath_make_if_null(dst) ;
+
+ /* Trim the path to be appended:
+ *
+ * 1. discard from any leading '~' to the first '/' or to '\0'.
+ *
+ * 2. then discard any leading '/'
+ *
+ * 3. then establish length of result.
+ */
+ l = n ;
+ n = qpath_trim_home(src, n) ;
+ src += (l - n) ; /* step past stuff trimmed */
+
+ /* Worry about whether need to add a '/' to the path before pushing */
+ if (qs_len_nn(dst->path) != 0)
+ {
+ if (*(qs_ep_char_nn(dst->path) - 1) != '/')
+ qs_append_str_n(dst->path, "/", 1) ;
+ } ;
+
+ /* Now append the src */
+ qs_append_str_n(dst->path, src, n) ;
+
+ /* Reduce the new part of the result, and return */
+ qpath_reduce(dst) ;
+
+ return dst ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Extend given dst path by simply affixing the given src path.
+ *
+ * Does not introduce any '/' or any other stuff.
+ *
+ * The resulting path is reduced (see above).
+ */
+extern qpath
+qpath_extend(qpath dst, const qpath src)
+{
+ if (src != NULL)
+ return qpath_extend_str_n(dst, qs_char_nn(src->path),
+ qs_len_nn(src->path)) ;
+ else
+ return qpath_extend_str_n(dst, NULL, 0) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Extend given dst path by simply affixing the given src qstring.
+ */
+extern qpath
+qpath_extend_qs(qpath dst, const qstring src)
+{
+ return qpath_extend_str_n(dst, qs_char(src), qs_len(src)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Extend given dst path by simply affixing the given src string.
+ */
+extern qpath
+qpath_extend_str(qpath dst, const char* src)
+{
+ return qpath_extend_str_n(dst, src, (src != NULL) ? strlen(src) : 0) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Extend given dst path by simply affixing the given src string of the given
+ * length.
+ */
+extern qpath
+qpath_extend_str_n(qpath dst, const char* src, ulen n)
+{
+ dst = qpath_make_if_null(dst) ;
+
+ qs_append_str_n(dst->path, src, n) ;
+
+ qpath_reduce(dst) ;
+
+ return dst ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Prepend src path onto front of dst path.
+ *
+ * Like append, where the dst ends up being the dst appended to the src.
+ */
+extern qpath
+qpath_prepend(qpath dst, const qpath src)
+{
+ if (src != NULL)
+ return qpath_prepend_str_n(dst, qs_char_nn(src->path),
+ qs_len_nn(src->path)) ;
+ else
+ return qpath_prepend_str_n(dst, NULL, 0) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Prepend src qstring onto front of dst path.
+ *
+ * Like append, where the dst ends up being the dst appended to the src.
+ */
+extern qpath
+qpath_prepend_qs(qpath dst, const qstring src)
+{
+ return qpath_prepend_str_n(dst, qs_char(src), qs_len(src)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Prepend src string onto front of dst path.
+ *
+ * Like append, where the dst ends up being the dst appended to the src.
+ */
+extern qpath
+qpath_prepend_str(qpath dst, const char* src)
+{
+ return qpath_prepend_str_n(dst, src, (src != NULL) ? strlen(src) : 0) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Prepend src string of given length onto front of dst path.
+ *
+ * Like append, where the dst ends up being the dst appended to the src.
+ */
+extern qpath
+qpath_prepend_str_n(qpath dst, const char* src, ulen n)
+{
+ char* p ;
+ ulen r ;
+ bool need_slash ;
+
+ dst = qpath_make_if_null(dst) ;
+
+ /* Trim the path to be prepended to:
+ *
+ * 1. discard from any leading '~' to the first '/' (or end).
+ *
+ * 2. then discard any leading '/'
+ *
+ * 3. then establish length of any part to be replaced.
+ */
+ r = qs_len_nn(dst->path) ;
+ r -= qpath_trim_home(qs_char_nn(dst->path), r) ;
+
+ /* Worry about whether need to add a '/' to the path before pushing */
+ need_slash = (n > 0) && (*(src + n - 1) != '/') ;
+
+ /* Make room for src and possible slash in qstring */
+ qs_set_cp_nn(dst->path, 0) ;
+ qs_replace(dst->path, r, NULL, n + (need_slash ? 1 : 0)) ;
+
+ /* Now copy in the src */
+ p = qs_char_nn(dst->path) ;
+
+ if (n > 0)
+ memmove(p, src, n) ;
+
+ if (need_slash)
+ *(p + n) = '/' ;
+
+ /* Reduce the new part of the result, and return */
+ qpath_reduce(dst) ;
+
+ return dst ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Make a qpath from the given string, completing it, if required, by
+ * prepending the given directory qpath.
+ */
+extern qpath
+qpath_make(const char* src, const qpath dir)
+{
+ if (*src == '/')
+ return qpath_set(NULL, src) ;
+
+ return qpath_append_str(qpath_dup(dir), src) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * If given dst path is not rooted (does not start with '/', prepend the
+ * given src path to it. Result is reduced.
+ */
+extern qpath
+qpath_complete(qpath dst, const qpath src)
+{
+ if (src != NULL)
+ return qpath_prepend_str_n(dst, qs_char_nn(src->path),
+ qs_len_nn(src->path)) ;
+ else
+ return qpath_prepend_str_n(dst, NULL, 0) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * If given dst path is not rooted (does not start with '/', prepend the
+ * given src qstring to it. Result is reduced.
+ */
+extern qpath
+qpath_complete_qs(qpath dst, const qstring src)
+{
+ return qpath_complete_str_n(dst, qs_char(src), qs_len(src)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * If given dst path is not rooted (does not start with '/', prepend the
+ * given src string to it. Result is reduced.
+ */
+extern qpath
+qpath_complete_str(qpath dst, const char* src)
+{
+ return qpath_prepend_str_n(dst, src, (src != NULL) ? strlen(src) : 0) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * If given dst path is not rooted (does not start with '/', prepend the
+ * given src string of given length to it. Result is reduced.
+ */
+extern qpath
+qpath_complete_str_n(qpath dst, const char* src, ulen n)
+{
+ dst = qpath_make_if_null(dst) ;
+
+ if ((qs_len_nn(dst->path) == 0) || (*(qs_char_nn(dst->path)) == '/'))
+ qpath_prepend_str_n(dst, src, n) ;
+ else
+ qpath_reduce(dst) ;
+
+ return dst ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Trim leading '~' up to and including one or more '/'.
+ *
+ * Return remaining length after the trim.
+ */
+static ulen
+qpath_trim_home(const char* p, ulen n)
+{
+ if ((n > 0) && (*p == '~'))
+ {
+ do /* Step past leadin '~' to first '/' */
+ {
+ ++p ;
+ --n ;
+ } while ((n > 0) && (*p != '/')) ;
+ } ;
+
+ while ((n > 0) && (*p == '/'))
+ {
+ ++p ; /* Step past leading '/' */
+ --n ;
+ } ;
+
+ return n ;
+} ;
+
+/*==============================================================================
+ *
+ */
+
+#if 0
+
+/*------------------------------------------------------------------------------
+ * Does the given path start and end '/' ?
+ */
+extern bool
+qpath_sex(qpath qp)
+{
+ char* sp ;
+
+ if (qp == NULL)
+ return qp_empty ; /* NULL qpath is empty */
+
+ sp = qs_chars(&qp->path) ;
+
+ if ((sp == NULL) || (*sp == '\0'))
+ return qp_empty ; /* NULL body or just '\0' */
+
+ if (*sp == '~')
+ return qp_homed ;
+
+ if (*sp == '/')
+ {
+ ++sp ;
+ if (*sp == '\0')
+ return qp_root ;
+ if (*sp != '/')
+ return qp_absolute ;
+
+ ++sp ;
+ if (*sp == '\0')
+ return qp_double_root ;
+ else
+ return qp_double_absolute ;
+ } ;
+
+
+
+ qp_empty, /* nothing at all */
+ qp_relative, /* something, not starting '/' */
+ qp_root, /* "/" all on its own */
+ qp_absolute, /* something, starting with single '/' */
+ qp_homed, /* something, starting with '~' */
+ qp_double_root, /* "//" all on its own */
+ qp_double_absolute /* something, starting with "//" */
+
+
+
+ return ((qp->path).len == 1) && (*sp == '/') ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Is there anything there ?
+ */
+extern bool
+qpath_is_empty(qpath qp)
+{
+ if (qp == NULL)
+ return true ; /* NULL qpath is empty */
+
+ return (qp->path).len == 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Is it: not empty, not starting '/' (or '//') and not starting '~'
+ */
+extern bool
+qpath_is_relative(qpath qp)
+{
+ char* sp ;
+
+ if (qp == NULL)
+ return false ; /* NULL qpath is not relative */
+
+ sp = qs_chars(&qp->path) ;
+
+ return (sp != NULL) && (*sp != '/') && (*sp != '.') ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Does the given path start and end '/' ?
+ */
+extern bool
+qpath_is_root(qpath qp)
+{
+ char* sp ;
+
+ if (qp == NULL)
+ return false ; /* NULL qpath cannot be root */
+
+ sp = qs_chars(&qp->path) ;
+
+ return ((qp->path).len == 1) && (*sp == '/') ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Does the given path start '/' (and not '//') ?
+ *
+ * Note that just "/" (ie root) will return true => it is also absolute.
+ */
+extern bool
+qpath_is_absolute(qpath qp)
+{
+ char* sp ;
+
+ if (qp == NULL)
+ return false ; /* NULL qpath cannot be absolute */
+
+ sp = qs_chars(&qp->path) ;
+
+ return (sp != NULL) && (*sp == '/') && (*(sp + 1) != '/') ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Does the given path start '~'
+ */
+extern bool
+qpath_is_homed(qpath qp)
+{
+ char* sp ;
+
+ if (qp == NULL)
+ return false ; /* NULL qpath cannot be homed */
+
+ sp = qs_chars( &qp->path) ;
+
+ return (sp != NULL) && (*sp == '~') ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Does the given path start and end '/' ?
+ */
+extern bool
+qpath_is_double_root(qpath qp)
+{
+ char* sp ;
+
+ if (qp == NULL)
+ return false ; /* NULL qpath cannot be root */
+
+ sp = qs_chars(&qp->path) ;
+
+ return ((qp->path).len == 2) && (*sp == '/') && (*(sp + 1) == '/') ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Does the given path start '/' (and not '//') ?
+ *
+ * Note that just "/" (ie root) will return true => it is also absolute.
+ */
+extern bool
+qpath_is_double_absolute(qpath qp)
+{
+ char* sp ;
+
+ if (qp == NULL)
+ return false ; /* NULL qpath cannot be absolute */
+
+ sp = qs_chars(&qp->path) ;
+
+ return (sp != NULL) && (*sp == '/') && (*(sp + 1) == '/') ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Does the given path *end* '/', or is it ~aaaa with no '/' at all.
+ *
+ * Note that root and double route return directory true.
+ */
+extern bool
+qpath_is_directory(qpath qp)
+{
+ char* sp ;
+
+ if (qp == NULL)
+ return false ; /* NULL qpath cannot be directory */
+
+ sp = qs_chars(&qp->path) ;
+
+ if ((sp == NULL) || (*sp = '\0'))
+ return false ; /* Empty qpath cannot be directory */
+
+ ep = qs_ep_char(qs) ;
+
+ if (*(ep - 1) == '/')
+ return true ; /* Ends '/' */
+
+ if (*sp == '~')
+ {
+ while (*(++sp) != '/')
+ {
+ if (*sp == '\0') /* Starts '~' and no '/' found */
+ return true ;
+ } ;
+ } ;
+
+ return false ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Is the given path an atom
+ *
+ * Note that root and double route are .
+ */
+extern bool
+qpath_is_atom(qpath qp)
+{
+ char* sp ;
+
+ if (qp == NULL)
+ return false ; /* NULL qpath cannot be directory */
+
+ sp = qs_chars(&qp->path) ;
+
+ if ((sp == NULL) || (*sp = '\0'))
+ return false ; /* Empty qpath cannot be directory */
+
+ ep = qs_ep_char(qs) ;
+
+ if (*(ep - 1) == '/')
+ return true ; /* Ends '/' */
+
+ if (*sp == '~')
+ {
+ while (*(++sp) != '/')
+ {
+ if (*sp == '\0') /* Starts '~' and no '/' found */
+ return true ;
+ } ;
+ } ;
+
+ return false ;
+} ;
+
+#endif
+
+/*==============================================================================
+ *
+ *
+ *
+ */
+/*------------------------------------------------------------------------------
+ * Reduce multiple '/' to single '/' (except for exactly "//" at start).
+ *
+ * Reduce "/./" to "/".
+ */
+static void
+qpath_reduce(qpath qp)
+{
+ char* sp ;
+ char* p ;
+ char* q ;
+
+ sp = qs_make_string(qp->path) ;
+ p = sp ;
+
+ /* Deal with special case of "//" at start.
+ *
+ * If find "//x", where x is anything other than '/', step past the first
+ * '/'. Could step past both "//", but that stops it seeing "//./zzz"
+ */
+ if ((*p == '/') && (*(p + 1) == '/') && (*(p + 2) != '/'))
+ ++p ;
+
+ /* Scan to see if there is anything that needs to be fixed.
+ *
+ * Looking for "//" and "/./".
+ */
+ while (1)
+ {
+ if (*p == '\0')
+ return ; /* scanned to end */
+
+ if (*p++ != '/') /* scanning for '/' */
+ continue ;
+
+ if (*p == '/')
+ break ; /* second '/' */
+
+ if (*p != '.')
+ continue ; /* not "//" and not "/." */
+
+ if (*(p+1) == '/')
+ break ; /* found "/./" */
+ } ;
+
+ /* Rats... there is something to be fixed.
+ *
+ * *p is second '/' of "//" or '.' of "/./".
+ */
+ q = p ; /* keep the first '/' */
+
+ while (*p != '\0')
+ {
+ ++p ; /* step past '.' or second '/' */
+
+ /* Step past any number of '/' and any number of "./". */
+ while (*p != '\0')
+ {
+ while (*p == '/') /* eat any number of these */
+ ++p ;
+
+ if (*p != '.') /* done if not '.' */
+ break ;
+
+ if (*(p+1) != '/') /* done if not "./" */
+ break ;
+
+ p += 2 ; /* Step past "./" */
+ } ;
+
+ /* Here we have *p which is not '/' and not "./", so unless is '\0'
+ * there is at least one character to move across.
+ *
+ * Copying stuff, until get to '\0' or find "//" or "/./"
+ */
+ while (*p != '\0')
+ {
+ *q++ = *p ; /* copy non-'\0' */
+
+ if (*p++ != '/')
+ continue ; /* keep going if wasn't '/' */
+
+ if (*p == '/')
+ break ; /* second '/' */
+
+ if (*p != '.')
+ continue ; /* not "//" and not "/." */
+
+ if (*(p+1) == '/')
+ break ; /* found "/./" */
+ } ;
+ } ;
+
+ /* Adjust the length and terminate */
+
+ qs_set_len_nn(qp->path, q - sp) ; /* set the new length (shorter !) */
+} ;
+
+
+
+
diff --git a/lib/qpath.h b/lib/qpath.h
new file mode 100644
index 00000000..23b710a6
--- /dev/null
+++ b/lib/qpath.h
@@ -0,0 +1,215 @@
+/* Some primitive path handling -- header
+ * 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_QPATH_H
+#define _ZEBRA_QPATH_H
+
+#include "misc.h"
+#include "qstring.h"
+#include "memory.h"
+#include "sys/stat.h"
+
+/*==============================================================================
+ * For these purposes a path is "parts" separated by one more '/' characters.
+ *
+ * As per POSIX, a pair of leading "//" (not three or more leading '/') is
+ * very special.
+ *
+ * The following sexes of qpath are established after any "path reduction"
+ * has been done. Path reduction removes extraneous '/'.
+ */
+
+typedef enum qpath_sex qpath_sex_t ;
+
+enum qpath_sex
+{
+ qp_empty, /* nothing at all */
+ qp_relative, /* something, not starting '/' */
+ qp_root, /* "/" all on its own */
+ qp_absolute, /* something, starting with single '/' */
+ qp_homed, /* something, starting with '~' */
+ qp_double_root, /* "//" all on its own */
+ qp_double_absolute /* something, starting with "//" */
+} ;
+
+/* The qpath structure is largely a qstring, but one in which there is always
+ * a body, even if it only contains "\0", and the len is kept up to date.
+ */
+struct qpath
+{
+ qstring_t path ; /* Embedded */
+} ;
+
+typedef struct qpath qpath_t[1] ;
+typedef struct qpath* qpath ;
+
+/*==============================================================================
+ * Functions
+ */
+
+extern qpath qpath_init_new(qpath qp) ;
+extern qpath qpath_reset(qpath qp, free_keep_b free_structure) ;
+extern qpath qpath_clear(qpath qp) ;
+
+Inline qpath qpath_new(void) ;
+Inline qpath qpath_free(qpath qp) ;
+
+Inline const char* qpath_string(qpath qp) ;
+Inline char* qpath_char_string(qpath qp) ;
+Inline ulen qpath_len(qpath qp) ;
+Inline const qstring qpath_qs(qpath qp) ;
+
+extern qpath qpath_set(qpath dst, const char* src) ;
+extern qpath qpath_set_n(qpath dst, const char* src, ulen n) ;
+extern qpath qpath_set_qs(qpath dst, const qstring src) ;
+extern qpath qpath_copy(qpath dst, const qpath src) ;
+Inline qpath qpath_dup(const qpath qp) ;
+Inline qpath qpath_dup_str(const char* src) ;
+
+extern qpath qpath_getcwd(qpath dst) ;
+extern int qpath_setcwd(qpath dst) ;
+extern int qpath_stat(qpath qp, struct stat* stat) ;
+extern int qpath_stat_is_file(qpath qp) ;
+extern int qpath_stat_is_directory(qpath qp) ;
+
+extern qpath qpath_shave(qpath qp) ;
+
+extern qpath qpath_append(qpath dst, const qpath src) ;
+extern qpath qpath_append_qs(qpath dst, const qstring src) ;
+extern qpath qpath_append_str(qpath dst, const char* src) ;
+extern qpath qpath_append_str_n(qpath dst, const char* src, ulen n) ;
+
+extern qpath qpath_extend(qpath dst, const qpath src) ;
+extern qpath qpath_extend_qs(qpath dst, const qstring src) ;
+extern qpath qpath_extend_str(qpath dst, const char* src) ;
+extern qpath qpath_extend_str_n(qpath dst, const char* src, ulen n) ;
+
+extern qpath qpath_prepend(qpath dst, const qpath src) ;
+extern qpath qpath_prepend_qs(qpath dst, const qstring src) ;
+extern qpath qpath_prepend_str(qpath dst, const char* src) ;
+extern qpath qpath_prepend_str_n(qpath dst, const char* src, ulen n) ;
+
+extern qpath qpath_make(const char* src, const qpath dir) ;
+
+extern qpath qpath_complete(qpath dst, const qpath src) ;
+extern qpath qpath_complete_qs(qpath dst, const qstring src) ;
+extern qpath qpath_complete_str(qpath dst, const char* src) ;
+extern qpath qpath_complete_str_n(qpath dst, const char* src, ulen n) ;
+
+/*==============================================================================
+ * Inline stuff
+ */
+
+/*------------------------------------------------------------------------------
+ * Create new qpath.
+ */
+Inline qpath
+qpath_new(void)
+{
+ return qpath_init_new(NULL) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Free qpath.
+ */
+Inline qpath
+qpath_free(qpath qp)
+{
+ return qpath_reset(qp, free_it) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Duplicate qpath -- result will need to be freed.
+ */
+Inline qpath
+qpath_dup(const qpath qp)
+{
+ return qpath_copy(NULL, qp) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Duplicate string as a qpath -- result will need to be freed.
+ */
+Inline qpath
+qpath_dup_str(const char* src)
+{
+ return qpath_set(NULL, src) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get *temporary* pointer to actual path contained in the given qpath.
+ *
+ * This is *temporary* to the extent that when the qpath is changed or freed,
+ * this pointer will be INVALID -- you have been warned.
+ *
+ * This is a *const* pointer.
+ *
+ * For a NULL qpath, or an empty qpath, returns pointer to an empty string
+ * ('\0' terminated "").
+ */
+Inline const char*
+qpath_string(qpath qp)
+{
+ return (qp != NULL) ? qs_make_string(qp->path) : "" ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get *temporary* pointer to actual path contained in the given qpath.
+ *
+ * This is *temporary* to the extent that when the qpath is changed or freed,
+ * this pointer will be INVALID -- you have been warned.
+ *
+ * This is a *const* pointer.
+ *
+ * qpath may *not* be NULL qpath.
+ */
+Inline char*
+qpath_char_string(qpath qp)
+{
+ return qs_make_string(qp->path) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get length of the given qpath -- zero if NULL
+ */
+Inline ulen
+qpath_len(qpath qp)
+{
+ return (qp != NULL) ? qs_len_nn(qp->path) : 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get *temporary* pointer to qstring rendering of the given path.
+ *
+ * This is *temporary* to the extent that when the qpath is changed or freed,
+ * this pointer will be INVALID -- you have been warned.
+ *
+ * This is a *const* pointer.
+ *
+ * For a NULL qpath returns NULL qstring.
+ */
+Inline const qstring
+qpath_qs(qpath qp)
+{
+ return (qp != NULL) ? qp->path : NULL ;
+} ;
+
+#endif /* _ZEBRA_QPATH_H */
diff --git a/lib/qpnexus.c b/lib/qpnexus.c
index 3b014be2..8a805900 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"
@@ -37,18 +36,39 @@ static void qpn_in_thread_init(qpn_nexus qpn);
*/
+/*------------------------------------------------------------------------------
+ * Initialise the qpnexus handling -- to be done as soon as state of
+ * qpthreads_enabled is established.
+ */
+extern void
+qpn_init(void)
+{
+ qpt_data_create(qpn_self) ; /* thread specific data */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set the thread's qpn_self to point at its qpnexus.
+ */
+static void
+qpn_self_knowledge(qpn_nexus qpn)
+{
+ qpt_data_set_value(qpn_self, qpn) ;
+} ;
+
/*==============================================================================
* Initialisation, add hook, free etc.
*
*/
/*------------------------------------------------------------------------------
- * Initialise a nexus -- allocating it if required.
+ * Initialise a nexus -- allocating it if required.
*
- * If main_thread is set then no new thread will be created
- * when qpn_exec() is called, instead the finite state machine will be
- * run in the calling thread. The main thread will only block the
- * message queue's signal. Non-main threads will block most signals.
+ * If main_thread is set then no new thread will be created when qpn_exec() is
+ * called, instead the finite state machine will be run in the calling thread.
+ *
+ * The main thread will only block the message queue's signal.
+ *
+ * Non-main threads will block most signals.
*
* Returns the qpn_nexus.
*/
@@ -67,10 +87,13 @@ qpn_init_new(qpn_nexus qpn, bool main_thread)
qpn->start = qpn_start;
if (main_thread)
- qpn->thread_id = qpt_thread_self();
+ {
+ qpn->thread_id = qpt_thread_self();
+ qpn_self_knowledge(qpn) ;
+ } ;
return qpn;
-}
+} ;
/*------------------------------------------------------------------------------
* Add a hook function to the given nexus.
@@ -91,7 +114,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 +138,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 */
@@ -263,50 +283,38 @@ qpn_start(void* arg)
static void
qpn_in_thread_init(qpn_nexus qpn)
{
- sigset_t newmask;
+ sigset_t sigmask[1];
qpn->thread_id = qpt_thread_self();
-
+ qpn_self_knowledge(qpn) ;
+
+ /* Signal mask.
+ *
+ * The main thread blocks nothing, except SIG_INTERRUPT. So (a) all
+ * signals other than the "hard cases" are routed to the main thread, and
+ * (b) SIG_INTERRUPT is masked until it is unmasked in pselect.
+ *
+ * Other threads block everything except the hard cases and SIG_INTERRUPT.
+ */
if (qpn->main_thread)
- {
- /* Main thread, block the message queue's signal */
- sigemptyset (&newmask);
- sigaddset (&newmask, SIGMQUEUE);
- }
+ sigmakeset(sigmask, SIG_INTERRUPT, -1) ;
else
- {
- /*
- * Not main thread. Block most signals, but be careful not to
- * defer SIGTRAP because doing so breaks gdb, at least on
- * NetBSD 2.0. Avoid asking to block SIGKILL, just because
- * we shouldn't be able to do so. Avoid blocking SIGFPE,
- * SIGILL, SIGSEGV, SIGBUS as this is undefined by POSIX.
- * Don't block SIGPIPE so that is gets ignored on this thread.
- */
- sigfillset (&newmask);
- sigdelset (&newmask, SIGTRAP);
- sigdelset (&newmask, SIGKILL);
- sigdelset (&newmask, SIGPIPE);
- sigdelset (&newmask, SIGFPE);
- sigdelset (&newmask, SIGILL);
- sigdelset (&newmask, SIGSEGV);
- sigdelset (&newmask, SIGBUS);
- }
+ siginvset(sigmask, signal_get_hard_set()) ;
- if (qpthreads_enabled)
- qpt_thread_sigmask(SIG_BLOCK, &newmask, NULL);
- else
- {
- if (sigprocmask(SIG_BLOCK, &newmask, NULL) != 0)
- zabort_errno("sigprocmask failed") ;
- }
+ qpt_thread_sigmask(SIG_BLOCK, sigmask, NULL);
+
+ /* The signal mask to be used during pselect() */
+ sigcopyset(qpn->pselect_mask, sigmask) ;
+ sigdelset(qpn->pselect_mask, SIG_INTERRUPT) ;
+ qpn->pselect_signal = SIG_INTERRUPT ;
- /* Now we have thread_id and mask, prep for using message queue. */
+ /* Now we have thread_id and mask, prep for using message queue. */
if (qpn->queue != NULL)
- qpn->mts = mqueue_thread_signal_init(qpn->mts, qpn->thread_id, SIGMQUEUE);
+ qpn->mts = mqueue_thread_signal_init(qpn->mts, qpn->thread_id,
+ SIG_INTERRUPT) ;
if (qpn->selection != NULL)
- qps_set_signal(qpn->selection, SIGMQUEUE, newmask);
-}
+ qps_set_signal(qpn->selection, qpn->pselect_mask);
+} ;
/*------------------------------------------------------------------------------
* Ask the thread to terminate itself quickly and cleanly.
@@ -322,6 +330,6 @@ qpn_terminate(qpn_nexus qpn)
/* wake up any pselect */
if (qpthreads_enabled)
- qpt_thread_signal(qpn->thread_id, SIGMQUEUE);
+ qpt_thread_signal(qpn->thread_id, SIG_INTERRUPT);
} ;
}
diff --git a/lib/qpnexus.h b/lib/qpnexus.h
index 0cf5a824..1aeefc04 100644
--- a/lib/qpnexus.h
+++ b/lib/qpnexus.h
@@ -22,11 +22,11 @@
#ifndef _ZEBRA_QPNEXUS_H
#define _ZEBRA_QPNEXUS_H
-#include <stdint.h>
+#include "misc.h"
#include <time.h>
-#include <pthread.h>
#include <unistd.h>
#include <errno.h>
+#include <signal.h>
#include "zassert.h"
#include "qpthreads.h"
@@ -34,10 +34,6 @@
#include "mqueue.h"
#include "qpselect.h"
-#ifndef Inline
-#define Inline static inline
-#endif
-
/*==============================================================================
* Quagga Nexus Interface -- qpn_xxxx
*
@@ -46,15 +42,17 @@
* action routines.
*
*/
+enum
+{
+ /* maximum time in seconds to sit in a pselect */
+ MAX_PSELECT_WAIT = 10,
-/* maximum time in seconds to sit in a pselect */
-#define MAX_PSELECT_WAIT 10
-
-/* signal for message queues */
-#define SIGMQUEUE SIGUSR2
+ /* signal for message queues */
+ SIG_INTERRUPT = SIGUSR2,
-/* number of hooks per hook list */
-enum { qpn_hooks_max = 4 } ;
+ /* number of hooks per hook list */
+ qpn_hooks_max = 4,
+} ;
/*==============================================================================
* Data Structures.
@@ -74,26 +72,30 @@ typedef struct qpn_nexus* qpn_nexus ;
struct qpn_nexus
{
- /* set true to terminate the thread (eventually) */
+ /* set true to terminate the thread (eventually) */
bool terminate;
- /* true if this is the main thread */
+ /* true if this is the main thread */
bool main_thread;
- /* thread ID */
- qpt_thread_t thread_id;
+ /* thread ID */
+ qpt_thread_t thread_id;
+
+ /* Signal mask for pselect */
+ sigset_t pselect_mask[1] ;
+ int pselect_signal ;
- /* pselect handler */
+ /* pselect handler */
qps_selection selection;
- /* timer pile */
+ /* timer pile */
qtimer_pile pile;
- /* message queue */
+ /* message queue */
mqueue_queue queue;
mqueue_thread_signal mts;
- /* qpthread routine, can override */
+ /* qpthread routine, can override */
void* (*start)(void*);
/* in-thread initialise, can override. Called within the thread after all
@@ -142,17 +144,26 @@ struct qpn_nexus
struct qpn_hook_list background ;
};
+/*------------------------------------------------------------------------------
+ * Each thread that has a qpnexus uses this piece of thread specific data in
+ * order to be able to find its own nexus.
+ */
+qpt_data_t qpn_self ; /* thread specific data */
+
+Inline qpn_nexus
+qpn_find_self(void)
+{
+ return qpt_data_get_value(qpn_self) ;
+} ;
+
/*==============================================================================
* Functions
*/
-
+extern void qpn_init(void) ;
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 fd20d421..3fb80daf 100644
--- a/lib/qpselect.c
+++ b/lib/qpselect.c
@@ -18,6 +18,7 @@
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
+#include "misc.h"
#include <signal.h>
#include <string.h>
@@ -29,13 +30,38 @@
#include "memory.h"
#include "vector.h"
-enum { qdebug =
-#ifdef QDEBUG
- 1
-#else
- 0
+/*------------------------------------------------------------------------------
+ * Sort out QPSELECT_DEBUG.
+ *
+ * Set to 1 if defined, but blank.
+ * Set to QDEBUG if not defined.
+ *
+ * Force to 0 if VTY_NO_DEBUG is defined and not zero.
+ *
+ * So: defaults to same as QDEBUG, but no matter what QDEBUG is set to:
+ *
+ * * can set QPSELECT_DEBUG == 0 to turn off debug
+ * * or set QPSELECT_DEBUG != 0 to turn on debug
+ * * or set QPSELECT_NO_DEBUG != to force debug off
+ */
+
+#ifdef QPSELECT_DEBUG /* If defined, make it 1 or 0 */
+# if IS_BLANK_OPTION(QPSELECT_DEBUG)
+# undef QPSELECT_DEBUG
+# define QPSELECT_DEBUG 1
+# endif
+#else /* If not defined, follow QDEBUG */
+# define QPSELECT_DEBUG QDEBUG
#endif
-};
+
+#ifdef QPSELECT_NO_DEBUG /* Override, if defined */
+# if IS_NOT_ZERO_OPTION(QPSELECT_NO_DEBUG)
+# undef QPSELECT_DEBUG
+# define QPSELECT_DEBUG 0
+# endif
+#endif
+
+enum { qpselect_debug = QPSELECT_DEBUG } ;
/*==============================================================================
* Quagga pselect -- qps_xxxx
@@ -272,29 +298,18 @@ qps_selection_ream(qps_selection qps, int free_structure)
/*------------------------------------------------------------------------------
* Set the signal mask for the selection.
*
- * This supports the unmasking of a single signal for the duration of the
+ * This supports the unmasking of one or more signals for the duration of the
* pselect operation.
*
- * It is assumed that the set of signals generally masked by a thread is
- * essentially static. So this function is passed that set. (So the sigmask
- * argument must have the signum signal masked.)
- *
- * If the set of signals masked by the thread changes, then this function
+ * * If the set of signals masked by the thread changes, then this function
* should be called again.
*
- * Setting a signum == 0 turns OFF the use of the sigmask.
+ * Setting a sigmask == NULL turns OFF the use of the sigmask.
*/
-void
-qps_set_signal(qps_selection qps, int signum, sigset_t sigmask)
+extern void
+qps_set_signal(qps_selection qps, const sigset_t* sigmask)
{
- qps->signum = signum ;
-
- if (signum != 0)
- {
- assert(sigismember(&sigmask, signum)) ;
- sigdelset(&sigmask, signum) ;
- qps->sigmask = sigmask ;
- } ;
+ qps->sigmask = sigmask ;
} ;
/*------------------------------------------------------------------------------
@@ -319,7 +334,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 */
@@ -366,7 +381,7 @@ qps_pselect(qps_selection qps, qtime_t max_wait)
p_fds[qps_write_mnum],
p_fds[qps_error_mnum],
qtime2timespec(&ts, max_wait),
- (qps->signum != 0) ? &qps->sigmask : NULL) ;
+ qps->sigmask) ;
/* If have something, set and return the pending count. */
if (n > 0)
@@ -415,7 +430,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)
@@ -683,7 +698,7 @@ static qps_file
qps_file_lookup_fd(qps_selection qps, int fd, qps_file insert)
{
qps_file qf ;
- vector_index i ;
+ vector_index_t i ;
int ret ;
dassert((fd >= 0) && (fd < (int)FD_SETSIZE)) ;
@@ -772,9 +787,9 @@ qps_file_remove(qps_selection qps, qps_file qf)
{
qps_file qf_last ;
int ret ;
- vector_index i = vector_bsearch(&qps->files,
- (vector_bsearch_cmp*)qps_fd_cmp,
- &qf->fd, &ret) ;
+ vector_index_t i = vector_bsearch(&qps->files,
+ (vector_bsearch_cmp*)qps_fd_cmp,
+ &qf->fd, &ret) ;
if (ret == 0)
qfd = vector_delete_item(&qps->files, i) ;
else
@@ -1279,9 +1294,9 @@ qps_selection_validate(qps_selection qps)
int enabled_count[qps_mnum_count] ;
fd_full_set enabled ;
- qps_file qf ;
- int fd, n, mnum, p_mnum ;
- vector_index i ;
+ qps_file qf ;
+ int fd, n, mnum, p_mnum ;
+ vector_index_t i ;
/* 1..4) Run down the selection vector and check. */
/* Collect new enabled_count and enabled bit vectors. */
@@ -1412,3 +1427,97 @@ qps_selection_validate(qps_selection qps)
if (n != qps->pend_count)
zabort("Non-empty bit vector where tried count == 0") ;
} ;
+
+/*==============================================================================
+ * Miniature pselect -- for where want to wait for a small number of things.
+ */
+
+/*------------------------------------------------------------------------------
+ * Initialise a qps_mini and set one fd in the given mode.
+ *
+ * If the fd is < 0, sets nothing (returns empty qps_mini).
+ *
+ * Zero timeout is an indefinite wait !
+ */
+extern qps_mini
+qps_mini_set(qps_mini qm, int fd, qps_mnum_t mode, uint timeout)
+{
+ qps_super_set_zero(qm->sets, qps_mnum_count) ;
+ qm->fd_last = -1 ;
+
+ qm->interval = QTIME(timeout) ; /* convert from time in seconds */
+ qm->end_time = qt_add_monotonic(qm->interval) ;
+
+ qps_mini_add(qm, fd, mode) ;
+
+ return qm ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Add given fd in the given mode to the given qps_mini.
+ *
+ * If the fd is < 0, adds nothing (returns qps_mini unchanged).
+ */
+extern qps_mini
+qps_mini_add(qps_mini qm, int fd, qps_mnum_t mode)
+{
+ if (fd >= 0)
+ {
+ FD_SET(fd, &(qm->sets[mode].fdset)) ;
+ if (fd > qm->fd_last)
+ qm->fd_last = fd ;
+ } ;
+
+ return qm ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Wait for a qps_mini -- with given timeout, in seconds.
+ *
+ * If !signal, loops on EINTR, so will not exit until gets ready state or a
+ * timeout.
+ *
+ * If signal, will exit on EINTR. Can call this again after an EINTR return,
+ * and will continue to wait the *remainder* of the timeout interval.
+ *
+ * Returns what pselect returns: > 0 number of bits set
+ * == 0 no bits set <=> timed out
+ * < 0 EINTR (iff signal)
+ */
+extern int
+qps_mini_wait(qps_mini qm, const sigset_t* sigmask, bool signal)
+{
+ while (1)
+ {
+ struct timespec ts[1] ;
+ int n ;
+
+ n = pselect(qm->fd_last + 1,
+ &(qm->sets[qps_read_mnum].fdset),
+ &(qm->sets[qps_write_mnum].fdset),
+ &(qm->sets[qps_error_mnum].fdset),
+ (qm->interval > 0) ? qtime2timespec(ts, qm->interval)
+ : NULL,
+ sigmask) ;
+ if (n >= 0)
+ return n ;
+
+ if (errno != EINTR)
+ break ;
+
+ if (qm->interval > 0)
+ {
+ /* set new interval, in case comes back */
+ qm->interval = qm->end_time - qt_get_monotonic() ;
+ if (qm->interval < 0)
+ return 0 ;
+ } ;
+
+ if (signal)
+ return -1 ; /* signal EINTR */
+ } ;
+
+ zabort_errno("Failed in pselect") ;
+} ;
+
+
diff --git a/lib/qpselect.h b/lib/qpselect.h
index 8901ea36..37bf8680 100644
--- a/lib/qpselect.h
+++ b/lib/qpselect.h
@@ -25,14 +25,11 @@
#include <sys/select.h>
#include <errno.h>
+#include "misc.h"
#include "zassert.h"
#include "qtime.h"
#include "vector.h"
-#ifndef Inline
-#define Inline static inline
-#endif
-
/*==============================================================================
* Quagga pselect -- qps_xxxx
*
@@ -143,8 +140,7 @@ struct qps_selection
qps_mnum_t pend_mnum ; /* error/read/write mode pending (if any) */
int pend_fd ; /* fd pending (if any) */
- int signum ; /* signal that sigmask is enabling -- 0 => none */
- sigset_t sigmask ; /* sigmask to use for duration of pselect */
+ const sigset_t* sigmask ; /* sigmask to use for duration of pselect */
} ;
struct qps_file
@@ -184,7 +180,7 @@ qps_selection_ream(qps_selection qps, int free_structure) ;
#define qps_selection_ream_keep(qps) qps_selection_ream(qps, 0)
extern void
-qps_set_signal(qps_selection qps, int signum, sigset_t sigmask) ;
+qps_set_signal(qps_selection qps, const sigset_t* sigmask) ;
extern int
qps_pselect(qps_selection qps, qtime_mono_t timeout) ;
@@ -238,4 +234,25 @@ qps_set_file_info(qps_file qf, void* info)
qf->file_info = info ;
} ;
+/*==============================================================================
+ * Miniature pselect
+ *
+ */
+struct qps_mini
+{
+ fd_full_set sets ; /* bit vectors for pselect enabled stuff */
+ int fd_last ; /* highest numbered fd; -1 => none at all */
+
+ qtime_t interval ;
+ qtime_mono_t end_time ;
+} ;
+
+typedef struct qps_mini qps_mini_t[1] ;
+typedef struct qps_mini* qps_mini ;
+
+extern qps_mini qps_mini_set(qps_mini qm, int fd, qps_mnum_t mode,
+ uint timeout) ;
+extern qps_mini qps_mini_add(qps_mini qm, int fd, qps_mnum_t mode) ;
+extern int qps_mini_wait(qps_mini qm, const sigset_t* sigmask, bool signal) ;
+
#endif /* _ZEBRA_QPSELECT_H */
diff --git a/lib/qpthreads.c b/lib/qpthreads.c
index baa34d52..8909f64b 100644
--- a/lib/qpthreads.c
+++ b/lib/qpthreads.c
@@ -18,10 +18,7 @@
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
-
-/* This MUST come first... otherwise we don't get __USE_UNIX98, which is */
-/* essential if glibc is to allow pthread_mutexattr_settype() to be used. */
-#include "config.h"
+#include "misc.h"
#include <signal.h>
#include <string.h>
@@ -29,11 +26,6 @@
#include "qpthreads.h"
#include "memory.h"
-/* If this is not set, will get errors later. */
-//#ifndef __USE_UNIX98
-//#error "_USE_UNIX98 not defined"
-//#endif
-
/*==============================================================================
* Quagga Pthread Interface -- qpt_xxxx
*
@@ -251,49 +243,56 @@ enum qpthreads_enabled_state
qpt_state_set_disabled = 2,
qpt_state_set_enabled = 3,
} ;
+typedef enum qpthreads_enabled_state qpthreads_enabled_state_t ;
-static enum qpthreads_enabled_state qpthreads_enabled_state = qpt_state_unset ;
+static qpthreads_enabled_state_t qpthreads_enabled_state = qpt_state_unset ;
-uint8_t qpthreads_enabled_flag = 0 ;
-uint8_t qpthreads_thread_created_flag = 0 ;
+bool qpthreads_enabled_flag = false ;
+bool qpthreads_thread_created_flag = false ;
-/* Function to set qpthreads_enabled, one way or the other.
+/*------------------------------------------------------------------------------
+ * Function to set qpthreads_enabled, one way or the other.
*
* Returns: true <=> successful set the required state.
* false <=> it is too late to enable qpthreads :-(
*
* NB: can repeatedly set to the same state, but not change state once set.
*/
-extern int
-qpt_set_qpthreads_enabled(int how)
+extern bool
+qpt_set_qpthreads_enabled(bool want_enabled)
{
switch (qpthreads_enabled_state)
- {
- case qpt_state_unset:
- break ;
- case qpt_state_set_frozen:
- if (how != 0)
- return 0 ;
- break ;
- case qpt_state_set_disabled:
- if (how != 0)
- zabort("qpthreads_enabled is already set: cannot set enabled") ;
- break ;
- case qpt_state_set_enabled:
- if (how == 0)
- zabort("qpthreads_enabled is already set: cannot set disabled") ;
- break ;
- default:
- break ;
- }
-
- qpthreads_enabled_flag = (how != 0) ;
- qpthreads_enabled_state = (how != 0) ? qpt_state_set_enabled
- : qpt_state_set_disabled ;
- return 1 ;
+ {
+ case qpt_state_unset:
+ break ;
+
+ case qpt_state_set_frozen:
+ if (want_enabled)
+ return false ; /* too late to set the state */
+ break ;
+
+ case qpt_state_set_disabled:
+ if (want_enabled)
+ zabort("qpthreads_enabled is already set: cannot set enabled") ;
+ break ;
+
+ case qpt_state_set_enabled:
+ if (!want_enabled)
+ zabort("qpthreads_enabled is already set: cannot set disabled") ;
+ break ;
+
+ default:
+ break ;
+ } ;
+
+ qpthreads_enabled_flag = want_enabled ;
+ qpthreads_enabled_state = want_enabled ? qpt_state_set_enabled
+ : qpt_state_set_disabled ;
+ return true ;
} ;
-/* Get state of qpthreads_enabled, and freeze if not yet explictly set.
+/*------------------------------------------------------------------------------
+ * Get state of qpthreads_enabled, and freeze if not yet explictly set.
*
* Where some initialisation depends on the state of qpthreads_enabled(), this
* returns the state and freezes it if it is implicitly not enabled.
@@ -320,7 +319,8 @@ qpt_freeze_qpthreads_enabled(void)
* are NULL, then the system defaults are used.
*/
-/* Initialise a set of attributes -- setting the scheduling options.
+/*------------------------------------------------------------------------------
+ * Initialise a set of attributes -- setting the scheduling options.
*
* Options:
*
@@ -351,7 +351,7 @@ qpt_freeze_qpthreads_enabled(void)
*
* Returns the address of the qpt_thread_attr_t structure.
*/
-qpt_thread_attr_t*
+extern qpt_thread_attr_t*
qpt_thread_attr_init(qpt_thread_attr_t* attr, enum qpt_attr_options opts,
int scope, int policy, int priority)
{
@@ -420,7 +420,8 @@ qpt_thread_attr_init(qpt_thread_attr_t* attr, enum qpt_attr_options opts,
return attr ;
} ;
-/* Create Thread with given attributes (if any).
+/*------------------------------------------------------------------------------
+ * Create Thread with given attributes (if any).
*
* If no attributes are given (attr == NULL) the thread is created with system
* default attributes -- *except* that it is created joinable.
@@ -429,7 +430,7 @@ qpt_thread_attr_init(qpt_thread_attr_t* attr, enum qpt_attr_options opts,
*
* Returns the qpt_thread_t "thread id".
*/
-qpt_thread_t
+extern qpt_thread_t
qpt_thread_create(void* (*start)(void*), void* arg, qpt_thread_attr_t* attr)
{
qpt_thread_attr_t thread_attr ;
@@ -438,7 +439,7 @@ qpt_thread_create(void* (*start)(void*), void* arg, qpt_thread_attr_t* attr)
int err ;
passert(qpthreads_enabled) ;
- qpthreads_thread_created_flag = 1 ; /* and at least one thread created */
+ qpthreads_thread_created_flag = true ; /* at least one thread created */
default_attr = (attr == NULL) ;
if (default_attr)
@@ -458,7 +459,8 @@ qpt_thread_create(void* (*start)(void*), void* arg, qpt_thread_attr_t* attr)
return thread_id ;
} ;
-/* Join given thread -- do nothing if !qpthreads_enabled
+/*------------------------------------------------------------------------------
+ * Join given thread -- do nothing if !qpthreads_enabled
*
* Tolerates ESRCH (no thread known by given id).
*
@@ -490,7 +492,8 @@ qpt_thread_join(qpt_thread_t thread_id)
* Mutex initialise and destroy.
*/
-/* Initialise Mutex (allocating if required)
+/*------------------------------------------------------------------------------
+ * Initialise Mutex (allocating if required)
*
* Does nothing if !qpthreads_enabled -- but freezes the state (attempting to
* later enable qpthreads will be a FATAL error).
@@ -508,7 +511,7 @@ qpt_thread_join(qpt_thread_t thread_id)
*
* Returns the mutex -- or original mx if !qpthreads_enabled.
*/
-qpt_mutex
+extern qpt_mutex
qpt_mutex_init_new(qpt_mutex mx, enum qpt_mutex_options opts)
{
pthread_mutexattr_t mutex_attr ;
@@ -569,7 +572,8 @@ qpt_mutex_init_new(qpt_mutex mx, enum qpt_mutex_options opts)
return mx ;
} ;
-/* Destroy given mutex, and (if required) free it.
+/*------------------------------------------------------------------------------
+ * Destroy given mutex, and (if required) free it.
* -- or do nothing if !qpthreads_enabled.
*
* Returns NULL if freed the mutex, otherwise the address of same.
@@ -578,7 +582,7 @@ qpt_mutex_init_new(qpt_mutex mx, enum qpt_mutex_options opts)
* anything, so there can be nothing to release -- so does nothing, but
* returns the original mutex address (if any).
*/
-qpt_mutex
+extern qpt_mutex
qpt_mutex_destroy(qpt_mutex mx, int free_mutex)
{
int err ;
@@ -600,7 +604,8 @@ qpt_mutex_destroy(qpt_mutex mx, int free_mutex)
* Condition Variable initialise and destroy.
*/
-/* Initialise Condition Variable (allocating if required).
+/*------------------------------------------------------------------------------
+ * Initialise Condition Variable (allocating if required).
*
* Does nothing if !qpthreads_enabled -- but freezes the state (attempting to
* later enable qpthreads will be a FATAL error).
@@ -615,7 +620,7 @@ qpt_mutex_destroy(qpt_mutex mx, int free_mutex)
*
* Returns the condition variable -- or original cv id !qpthreads_enabled.
*/
-qpt_cond
+extern qpt_cond
qpt_cond_init_new(qpt_cond cv, enum qpt_cond_options opts)
{
pthread_condattr_t cond_attr ;
@@ -662,7 +667,8 @@ qpt_cond_init_new(qpt_cond cv, enum qpt_cond_options opts)
return cv ;
} ;
-/* Destroy given condition variable, and (if required) free it
+/*------------------------------------------------------------------------------
+ * Destroy given condition variable, and (if required) free it
* -- or do nothing if !qpthreads_enabled.
*
* NB: if !qpthreads_enabled qpt_cond_init_new() will not have allocated
@@ -671,7 +677,7 @@ qpt_cond_init_new(qpt_cond cv, enum qpt_cond_options opts)
*
* Returns NULL if freed the condition variable, otherwise the address of same.
*/
-qpt_cond
+extern qpt_cond
qpt_cond_destroy(qpt_cond cv, int free_cond)
{
int err ;
@@ -689,7 +695,8 @@ qpt_cond_destroy(qpt_cond cv, int free_cond)
return cv ;
} ;
-/* Wait for given condition variable or time-out
+/*------------------------------------------------------------------------------
+ * Wait for given condition variable or time-out
* -- or return immediate success if !qpthreads_enabled.
*
* Returns: wait succeeded (1 => success, 0 => timed-out).
@@ -698,8 +705,7 @@ qpt_cond_destroy(qpt_cond cv, int free_cond)
*
* Has to check the return value, so zabort_errno if not EBUSY.
*/
-
-int
+extern int
qpt_cond_timedwait(qpt_cond cv, qpt_mutex mx, qtime_mono_t timeout_time)
{
struct timespec ts ;
@@ -729,40 +735,43 @@ qpt_cond_timedwait(qpt_cond cv, qpt_mutex mx, qtime_mono_t timeout_time)
* Signal Handling.
*/
-/* Set thread signal mask -- requires qpthreads_enabled.
- *
- * Thin wrapper around pthread_sigmask.
+/*------------------------------------------------------------------------------
+ * Set thread signal mask.
*
- * zaborts if gets any error.
+ * If !qpthreads_enabled will use sigprocmask(), otherwise pthread_sigmask().
*
- * NB: it is a FATAL error to do this if !qpthreads_enabled.
+ * In fact pthread_sigmask() works for single-threaded processes, but we avoid
+ * depending on pthread library if it's not essential.
*
- * This is mostly because wish to avoid all pthreads_xxx calls when not
- * using pthreads. There is no reason not to use this in a single threaded
- * program.
+ * zaborts if gets any error.
*/
-void
+extern void
qpt_thread_sigmask(int how, const sigset_t* set, sigset_t* oset)
{
- int err ;
-
- passert(qpthreads_enabled) ;
-
if (oset != NULL)
sigemptyset(oset) ; /* to make absolutely sure */
- err = pthread_sigmask(how, set, oset) ;
- if (err != 0)
- zabort_err("pthread_sigmask failed", err) ;
+ if (qpthreads_enabled)
+ {
+ int err = pthread_sigmask(how, set, oset) ;
+ if (err != 0)
+ zabort_err("pthread_sigmask failed", err) ;
+ }
+ else
+ {
+ if (sigprocmask(how, set, oset) != 0)
+ zabort_errno("sigprocmask failed") ;
+ } ;
} ;
-/* Send given thread the given signal -- requires qpthreads_enabled (!)
+/*------------------------------------------------------------------------------
+ * Send given thread the given signal -- requires qpthreads_enabled (!)
*
* Thin wrapper around pthread_kill.
*
* zaborts if gets any error.
*/
-void
+extern void
qpt_thread_signal(qpt_thread_t thread, int signum)
{
int err ;
@@ -773,3 +782,58 @@ qpt_thread_signal(qpt_thread_t thread, int signum)
if (err != 0)
zabort_err("pthread_kill failed", err) ;
} ;
+
+/*==============================================================================
+ * Thread Specific Data Handling.
+ *
+ * When creating a key for a piece of thread specific data one could:
+ *
+ * a. arrange for all keys to be created before any threads are
+ * created -- or at least before any have a need for the key.
+ *
+ * b. use pthread_once() to protect the creation of the key.
+ *
+ * Note that there does not appear to be a way of distinguishing a key
+ * that has been created from one that has not.
+ *
+ * For !qpthreads_enabled systems, the "thread specific" data is embedded in
+ * the qpt_data object.
+ */
+
+/*------------------------------------------------------------------------------
+ * Create the given thread specific data.
+ *
+ * NB: if no value is ever set, then qpt_data_get_value() will return NULL
+ * (whether qpthreads_enabled, or not).
+ */
+extern void
+qpt_data_create(qpt_data data)
+{
+ memset(data, 0, sizeof(union qpt_data)) ;
+
+ if (qpthreads_enabled_freeze)
+ {
+ int err = pthread_key_create(&data->key, NULL) ;
+ if (err != 0)
+ zabort_err("pthread_key_create failed", err) ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Delete the given thread specific data.
+ *
+ * NB: it is the caller's responsibility to release any memory the value of
+ * the thread specific data may refer to.
+ */
+extern void
+qpt_data_delete(qpt_data data)
+{
+ if (qpthreads_enabled)
+ {
+ int err = pthread_key_delete(data->key) ;
+ if (err != 0)
+ zabort_err("pthread_key_delete failed", err) ;
+ } ;
+
+ memset(data, 0, sizeof(union qpt_data)) ;
+} ;
diff --git a/lib/qpthreads.h b/lib/qpthreads.h
index d73182ef..d05f7e34 100644
--- a/lib/qpthreads.h
+++ b/lib/qpthreads.h
@@ -22,24 +22,15 @@
#ifndef _ZEBRA_QPTHREADS_H
#define _ZEBRA_QPTHREADS_H
-#include <stdint.h>
+#include "misc.h"
#include <time.h>
#include <pthread.h>
#include <unistd.h>
#include <errno.h>
-#include <stdbool.h>
#include "zassert.h"
#include "qtime.h"
-#ifndef Inline
-#define Inline static inline
-#endif
-
-#ifndef private
-#define private extern
-#endif
-
/*==============================================================================
* Quagga Pthread Interface -- qpt_xxxx
*
@@ -61,6 +52,39 @@
#error Require _POSIX_THREADS
#endif
+/*------------------------------------------------------------------------------
+ * Sort out QPTHREADS_DEBUG.
+ *
+ * Set to 1 if defined, but blank.
+ * Set to QDEBUG if not defined.
+ *
+ * Force to 0 if QPTHREADS_NO_DEBUG is defined and not zero.
+ *
+ * So: defaults to same as QDEBUG, but no matter what QDEBUG is set to:
+ *
+ * * can set QPTHREADS_DEBUG == 0 to turn off debug
+ * * or set QPTHREADS_DEBUG != 0 to turn on debug
+ * * or set QPTHREADS_NO_DEBUG != 0 to force debug off
+ */
+
+#ifdef QPTHREADS_DEBUG /* If defined, make it 1 or 0 */
+# if IS_BLANK_OPTION(QPTHREADS_DEBUG)
+# undef QPTHREADS_DEBUG
+# define QPTHREADS_DEBUG 1
+# endif
+#else /* If not defined, follow QDEBUG */
+# define QPTHREADS_DEBUG QDEBUG
+#endif
+
+#ifdef QPTHREADS_NO_DEBUG /* Override, if defined */
+# if IS_NOT_ZERO_OPTION(QPTHREADS_NO_DEBUG)
+# undef QPTHREADS_DEBUG
+# define QPTHREADS_DEBUG 0
+# endif
+#endif
+
+enum { qpthreads_debug = QPTHREADS_DEBUG } ;
+
/*==============================================================================
* Global Switch -- this allows the library to be run WITHOUT pthreads !
*
@@ -74,42 +98,41 @@
* qpthreads_enabled_freeze -- to test and freeze unset if not yet enabled
*/
-#define qpthreads_enabled ((const uint8_t)qpthreads_enabled_flag)
+#define qpthreads_enabled ((const bool)qpthreads_enabled_flag)
#define qpthreads_enabled_freeze qpt_freeze_qpthreads_enabled()
-#define qpthreads_thread_created ((const uint8_t) \
- qpthreads_thread_created_flag)
+#define qpthreads_thread_created ((const bool)qpthreads_thread_created_flag)
/*==============================================================================
* Data types
*/
-
typedef pthread_t qpt_thread_t ;
-typedef pthread_mutex_t qpt_mutex_t ;
-typedef pthread_cond_t qpt_cond_t ;
+
+typedef pthread_mutex_t qpt_mutex_t[1] ;
+typedef pthread_mutex_t* qpt_mutex ;
+
+typedef pthread_cond_t qpt_cond_t[1] ;
+typedef pthread_cond_t* qpt_cond ;
typedef pthread_attr_t qpt_thread_attr_t ;
-typedef qpt_mutex_t* qpt_mutex ;
-typedef qpt_cond_t* qpt_cond ;
/*==============================================================================
* Thread Creation -- see qpthreads.c for further discussion.
*
* NB: it is a FATAL error to attempt these if !qpthreads_enabled.
*/
-
enum qpt_attr_options
{
qpt_attr_joinable = 0, /* the default for Quagga */
- qpt_attr_detached = 0x0001, /* otherwise will set joinable */
+ qpt_attr_detached = BIT(0), /* otherwise will set joinable */
- qpt_attr_sched_inherit = 0x0002, /* otherwise will use default */
+ qpt_attr_sched_inherit = BIT(1), /* otherwise will use default */
- qpt_attr_sched_scope = 0x0004, /* otherwise inherit/default */
- qpt_attr_sched_policy = 0x0008, /* otherwise inherit/default */
- qpt_attr_sched_priority = 0x0010, /* otherwise inherit/default */
+ qpt_attr_sched_scope = BIT(2), /* otherwise inherit/default */
+ qpt_attr_sched_policy = BIT(3), /* otherwise inherit/default */
+ qpt_attr_sched_priority = BIT(4), /* otherwise inherit/default */
} ;
#define qpt_attr_sched_explicit ( qpt_attr_sched_scope \
@@ -133,13 +156,13 @@ qpt_thread_join(qpt_thread_t thread_id) ;
/*==============================================================================
* qpthreads_enabled support -- NOT FOR PUBLIC CONSUMPTION !
*/
-private uint8_t qpthreads_enabled_flag ; /* DO NOT TOUCH THIS PLEASE */
-private uint8_t qpthreads_thread_created_flag ; /* DO NOT TOUCH THIS PLEASE */
+Private bool qpthreads_enabled_flag ; /* DO NOT TOUCH THIS PLEASE */
+Private bool qpthreads_thread_created_flag ; /* DO NOT TOUCH THIS PLEASE */
-private int
-qpt_set_qpthreads_enabled(int how) ; /* qpthreads_enabled := how */
+Private bool
+qpt_set_qpthreads_enabled(bool want_enabled) ; /* qpthreads_enabled := want */
-private int
+Private int
qpt_freeze_qpthreads_enabled(void) ; /* get and freeze qpthreads_enabled */
/*==============================================================================
@@ -174,7 +197,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
@@ -198,22 +221,25 @@ Inline bool qpt_thread_is_self(qpt_thread_t id)
enum qpt_mutex_options
{
- qpt_mutex_quagga = 0x0000, /* Quagga's default */
- qpt_mutex_normal = 0x0001,
- qpt_mutex_recursive = 0x0002,
- qpt_mutex_errorcheck = 0x0003,
- qpt_mutex_default = 0x0004, /* system default */
+ qpt_mutex_quagga = 0, /* Quagga's default */
+ qpt_mutex_normal,
+ qpt_mutex_recursive,
+ qpt_mutex_errorcheck,
+ qpt_mutex_default, /* system default */
} ;
#ifndef QPT_MUTEX_TYPE_DEFAULT
# define QPT_MUTEX_TYPE_DEFAULT PTHREAD_MUTEX_NORMAL
#endif
-#if defined(NDEBUG) && defined(NDEBUG_QPTHREADS)
-# define QPT_MUTEX_TYPE QPT_MUTEX_TYPE_DEFAULT
+enum
+{
+#if QPTHREADS_DEBUG
+ QPT_MUTEX_TYPE = PTHREAD_MUTEX_ERRORCHECK
#else
-# define QPT_MUTEX_TYPE PTHREAD_MUTEX_ERRORCHECK
+ QPT_MUTEX_TYPE = QPT_MUTEX_TYPE_DEFAULT
#endif
+} ;
extern qpt_mutex /* freezes qpthreads_enabled */
qpt_mutex_init_new(qpt_mutex mx, enum qpt_mutex_options opts) ;
@@ -313,7 +339,7 @@ qpt_mutex_lock(qpt_mutex mx)
{
if (qpthreads_enabled)
{
-#if defined(NDEBUG) && defined(NDEBUG_QPTHREADS)
+#if QPTHREADS_DEBUG
pthread_mutex_lock(mx) ;
#else
int err = pthread_mutex_lock(mx) ;
@@ -323,7 +349,8 @@ qpt_mutex_lock(qpt_mutex mx)
} ;
} ;
-/* Try to lock given mutex -- every time a winner if !qpthreads_enabled.
+/*------------------------------------------------------------------------------
+ * Try to lock given mutex -- every time a winner if !qpthreads_enabled.
*
* Returns: lock succeeded (1 => have locked, 0 => unable to lock).
*
@@ -347,23 +374,19 @@ qpt_mutex_trylock(qpt_mutex mx)
return 1 ;
} ;
-/* Unlock given mutex -- or do nothing if !qpthreads_enabled.
+/*------------------------------------------------------------------------------
+ * Unlock given mutex -- or do nothing if !qpthreads_enabled.
*
- * Unless both NCHECK_QPTHREADS and NDEBUG are defined, checks that the
- * return value is valid -- zabort_errno if it isn't.
+ * Checks that the return value is valid -- zabort_err if it isn't.
*/
Inline void
qpt_mutex_unlock(qpt_mutex mx)
{
if (qpthreads_enabled)
{
-#if defined(NDEBUG) && defined(NDEBUG_QPTHREADS)
- pthread_mutex_unlock(mx) ;
-#else
int err = pthread_mutex_unlock(mx) ;
if (err != 0)
zabort_err("pthread_mutex_unlock failed", err) ;
-#endif
} ;
} ;
@@ -371,73 +394,111 @@ qpt_mutex_unlock(qpt_mutex mx)
* Condition variable inline functions
*/
-/* Wait for given condition variable -- do nothing if !qpthreads_enabled
+/*------------------------------------------------------------------------------
+ * Wait for given condition variable -- do nothing if !qpthreads_enabled
*
- * Unless both NCHECK_QPTHREADS and NDEBUG are defined, checks that the
- * return value is valid -- zabort_errno if it isn't.
+ * Checks that the return value is valid -- zabort_err if it isn't.
*/
Inline void
qpt_cond_wait(qpt_cond cv, qpt_mutex mx)
{
if (qpthreads_enabled)
{
-#if defined(NDEBUG) && defined(NDEBUG_QPTHREADS)
- pthread_cond_wait(cv, mx) ;
-#else
int err = pthread_cond_wait(cv, mx) ;
if (err != 0)
zabort_err("pthread_cond_wait failed", err) ;
-#endif
} ;
} ;
-/* Signal given condition -- do nothing if !qpthreads_enabled
+/*------------------------------------------------------------------------------
+ * Signal given condition -- do nothing if !qpthreads_enabled
*
- * Unless both NCHECK_QPTHREADS and NDEBUG are defined, checks that the
- * return value is valid -- zabort_errno if it isn't.
+ * Checks that the return value is valid -- zabort_err if it isn't.
*/
Inline void
qpt_cond_signal(qpt_cond cv)
{
if (qpthreads_enabled)
{
-#if defined(NDEBUG) && defined(NDEBUG_QPTHREADS)
- pthread_cond_signal(cv) ;
-#else
int err = pthread_cond_signal(cv) ;
if (err != 0)
zabort_err("pthread_cond_signal failed", err) ;
-#endif
} ;
} ;
-/* Broadcast given condition -- do nothing if !qpthreads_enabled
+/*------------------------------------------------------------------------------
+ * Broadcast given condition -- do nothing if !qpthreads_enabled
*
- * Unless both NCHECK_QPTHREADS and NDEBUG are defined, checks that the
- * return value is valid -- zabort_errno if it isn't.
+ * Checks that the return value is valid -- zabort_err if it isn't.
*/
Inline void
qpt_cond_broadcast(qpt_cond cv)
{
if (qpthreads_enabled)
{
-#if defined(NDEBUG) && defined(NDEBUG_QPTHREADS)
- pthread_cond_broadcast(cv) ;
-#else
int err = pthread_cond_broadcast(cv) ;
if (err != 0)
zabort_err("pthread_cond_broadcast failed", err) ;
-#endif
} ;
} ;
/*==============================================================================
* Signal Handling.
*/
-void /* FATAL error if !qpthreads_enabled */
+extern void /* sigprocmask() if !qpthreads_enabled */
qpt_thread_sigmask(int how, const sigset_t* set, sigset_t* oset) ;
-void /* FATAL error if !qpthreads_enabled */
+extern void /* FATAL error if !qpthreads_enabled */
qpt_thread_signal(qpt_thread_t thread, int signum) ;
+/*==============================================================================
+ * Thread Specific Data Handling.
+ *
+ * Note that if !qpthreads_enabled, this maintains the data value, without
+ * using any pthread primitives.
+ *
+ * Note also that this does not support the pthread value destructor -- because
+ * cannot support that for non qpthreads_enabled (straightforwardly, anyway).
+ */
+union qpt_data
+{
+ pthread_key_t key ; /* if qpthreads_enabled */
+ void* value ; /* otherwise */
+ const void* cvalue ;
+} ;
+
+typedef union qpt_data qpt_data_t[1] ;
+typedef union qpt_data* qpt_data ;
+
+extern void qpt_data_create(qpt_data data) ;
+extern void qpt_data_delete(qpt_data data) ;
+
+/*------------------------------------------------------------------------------
+ * Set thread specific data value -- value is void*
+ */
+Inline void
+qpt_data_set_value(qpt_data data, const void* value)
+{
+ if (qpthreads_enabled)
+ {
+ int err = pthread_setspecific(data->key, value) ;
+ if (err != 0)
+ zabort_err("pthread_setspecific failed", err) ;
+ }
+ else
+ data->cvalue = value ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get thread specific data value -- value is void*
+ */
+Inline void*
+qpt_data_get_value(qpt_data data)
+{
+ if (qpthreads_enabled)
+ return pthread_getspecific(data->key) ;
+ else
+ return data->value ;
+} ;
+
#endif /* _ZEBRA_QPTHREADS_H */
diff --git a/lib/qrand.c b/lib/qrand.c
new file mode 100644
index 00000000..402bb432
--- /dev/null
+++ b/lib/qrand.c
@@ -0,0 +1,48 @@
+/* Pseudo Random Sequence
+ * Copyright (C) 2010 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify
+ * it 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.
+ */
+#include "misc.h"
+#include "qrand.h"
+
+/*==============================================================================
+ * Simple 32 bit random sequence.
+ */
+
+/*------------------------------------------------------------------------------
+ * Return next in the given sequence.
+ *
+ * Returns value in range 0..range-1, or 0..0x7FFF_FFFF if range == 0
+ *
+ * If range == 1, returns 0 every time !
+ */
+extern int
+qrand(qrand_seq seq, int range)
+{
+ uint64_t r ;
+
+ r = seq->last ^ 3141592653 ;
+ r = ((r * 2650845021) + 5) & 0xFFFFFFFF ; /* see Knuth */
+ seq->last = r ;
+
+ if (range == 0)
+ return r >> 1 ;
+ else
+ return (r % range) ;
+} ;
diff --git a/lib/qrand.h b/lib/qrand.h
new file mode 100644
index 00000000..e9a18436
--- /dev/null
+++ b/lib/qrand.h
@@ -0,0 +1,52 @@
+/* Pseudo Random Sequence -- Header
+ * Copyright (C) 2010 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify
+ * it 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_QRAND_H
+#define _ZEBRA_QRAND_H
+
+#include "misc.h"
+
+/*==============================================================================
+ * Simple 32 bit random sequence.
+ *
+ * Produces 32-bit signed integers in 0..0x7FFF_FFFF.
+ *
+ * Object of the exercise is to be able to produce repeatable sequence, so can
+ * debug !
+ */
+
+struct qrand_seq
+{
+ uint last ;
+} ;
+
+typedef struct qrand_seq* qrand_seq ;
+typedef struct qrand_seq qrand_seq_t[1] ;
+
+#define QRAND_SEQ_INIT(s) { { s } }
+
+/*==============================================================================
+ * Functions
+ */
+
+extern int qrand(qrand_seq seq, int range) ;
+
+#endif /* _ZEBRA_QRAND_H */
diff --git a/lib/qstring.c b/lib/qstring.c
index 5ca4d868..97f8e8b0 100644
--- a/lib/qstring.c
+++ b/lib/qstring.c
@@ -18,494 +18,697 @@
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
+#include "misc.h"
+#include "stdio.h"
#include "qstring.h"
-
#include "memory.h"
-#include "zassert.h"
/*==============================================================================
*/
/*------------------------------------------------------------------------------
- * Initialise qstring -- allocate if required.
+ * Create a new body or extend existing one to accommodate at least slen + 1.
*
- * If non-zero len is given, a body is allocated (for at least len + 1).
+ * Sets size to multiple of 8 (minimum 16), with at least 9 bytes free beyond
+ * the requested slen.
*
- * Returns: address of qstring
+ * Does not affect 'len' or 'cp'.
*
- * NB: assumes initialising a new structure. If not, then caller should
- * use qs_reset() or qs_clear().
+ * If 'keep_alias', ONLY sets 'b_size' & 'b_body'.
+ *
+ * If !'keep_alias', sets the elstring body & size & clears 'alias'.
*/
-extern qstring
-qs_init_new(qstring qs, size_t len)
+static inline void
+qs_new_body(qstring qs, usize slen, bool keep_alias)
{
- if (qs == NULL)
- qs = qs_new() ;
- else
- memset(qs, 0, sizeof(qstring_t)) ; /* see qs_new() */
+ qs->b_size = (slen + 0x10) & ~(usize)(0x08 - 1) ;
+ qs->b_body = XREALLOC(MTYPE_STRING, qs->b_body, qs->b_size) ;
- if (len != 0)
- return qs_make_to_length(qs, len) ;
-
- return qs ;
+ if (!keep_alias)
+ qs_set_real_body_nn(qs) ;
} ;
/*------------------------------------------------------------------------------
- * Allocate or reallocate so that string is big enough for the given length.
+ * Create a new, empty qs
*
- * Allocate qstring if required. Returns with a body with size > 0.
+ * If non-zero slen is given, a body is allocated (size = slen + 1).
+ * If zero slen is given, no body is allocated.
*
- * Allocates to 16 byte boundaries with space for '\0' beyond given length.
+ * Sets 'len' = 'cp' = 0.
*
* Returns: address of qstring
- *
- * NB: allocates new body if the size == 0.
- *
- * If the qstring is a "dummy", its contents are now copied to the new
- * real qstring body -- up to a maximum of the new length.
*/
extern qstring
-qs_make_to_length(qstring qs, size_t len)
+qs_new(usize slen)
{
- size_t size = (len + 0x10) & ~(size_t)(0x10 - 1) ;
+ qstring qs ;
- if (qs == NULL)
- qs = qs_new() ;
+ /* zeroising sets a completely empty qstring -- see qs_init_new() */
- if (size > qs->size)
- {
- if (qs->size == 0)
- {
- void* old ;
- old = qs->body ;
+ qs = XCALLOC(MTYPE_QSTRING, sizeof(qstring_t)) ;
- qs->size = size ;
- qs->body = XMALLOC(MTYPE_QSTRING_BODY, qs->size) ;
+ confirm(QSTRING_INIT_ALL_ZEROS) ;
- if ((qs->len != 0) && (old != NULL))
- memcpy(qs->body, old, (qs->len <= len) ? qs->len : len) ;
- }
- else
- {
- qs->size *= 2 ;
- if (qs->size < size)
- qs->size = size ;
- qs->body = XREALLOC(MTYPE_QSTRING_BODY, qs->body, qs->size) ;
- } ;
- };
+ if (slen != 0)
+ qs_new_body(qs, slen, false) ;
return qs ;
} ;
/*------------------------------------------------------------------------------
- * Add 'n' to the current string length, allocating or extending the body as
- * required.
+ * Create a new, empty qs with body
+ */
+extern qstring
+qs_new_with_body(usize slen)
+{
+ return qs_new(slen | 1) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Initialise qstring -- allocate if required.
*
- * Allocate qstring if required. Returns with a body with size > 0.
+ * If non-zero slen is given, a body is allocated (size = slen + 1).
+ * If zero slen is given, no body is allocated.
*
- * Allocates to 16 byte boundaries with space for '\0' beyond new length.
+ * Sets 'len' = 'cp' = 0.
*
* Returns: address of qstring
*
- * also: sets char** p_ep to point at the *end* of the new len.
- *
- * NB: allocates new body if the size == 0.
- *
- * If the qstring is a "dummy", its contents are now copied to the new
- * real qstring body -- up to a maximum of the new length.
+ * NB: assumes initialising a new structure. If not, then caller should
+ * use qs_reset() or qs_clear().
*/
extern qstring
-qs_add_len(qstring qs, size_t n, char** p_ep)
+qs_init_new(qstring qs, usize slen)
{
- size_t len ;
- len = (qs != NULL) ? qs->len + n : n ;
+ if (qs == NULL)
+ return qs_new(slen) ;
- qs = qs_make_to_length(qs, len) ;
+ memset(qs, 0, sizeof(qstring_t)) ;
- qs->len = len ;
+ confirm(QSTRING_INIT_ALL_ZEROS) ;
- *p_ep = (char*)qs->body + len ;
+ /* Zeroising has set:
+ *
+ * empty elstring -- no body, 'len' = 0
+ *
+ * size = 0 -- no elstring body
+ *
+ * cp = 0
+ *
+ * b_body = NULL -- no body buffer
+ * b_size = 0 -- no body buffer
+ *
+ * alias = false -- not an alias qstring
+ */
+
+ if (slen != 0)
+ qs_new_body(qs, slen, false) ;
return qs ;
} ;
/*------------------------------------------------------------------------------
- * Free body of qstring -- zeroise size, len and cp
+ * Reset qstring -- free body and, if required, free the structure.
*
- * Does nothing if qstring is NULL
+ * If not freeing the structure, zeroise size, len and cp -- qs_free_body()
+ *
+ * Returns: NULL if freed the structure
+ * address of structure (if any), otherwise
*
* NB: frees the body if the size != 0. So, a "dummy" qstring will not retain
* the old body.
*/
-extern void
-qs_free_body(qstring qs)
+extern qstring
+qs_reset(qstring qs, free_keep_b free_structure)
{
if (qs != NULL)
{
- if (qs->size != 0)
- XFREE(MTYPE_QSTRING_BODY, qs->body) ; /* sets qs->body = NULL */
+ if (qs->b_body != NULL)
+ XFREE(MTYPE_STRING, qs->b_body) ; /* sets qs->b_body = NULL */
+
+ if (free_structure)
+ XFREE(MTYPE_QSTRING, qs) ; /* sets qs = NULL */
+ else
+ memset(qs, 0, sizeof(qstring_t)) ; /* see qs_init_new */
- qs->size = 0 ;
- qs->len = 0 ;
- qs->cp = 0 ;
+ confirm(QSTRING_INIT_ALL_ZEROS) ;
} ;
+ return qs ;
} ;
/*------------------------------------------------------------------------------
- * Reset qstring -- free body and, if required, free the structure.
+ * Allocate or reallocate body so that is big enough for the given 'slen'.
*
- * If not freeing the structure, zeroise size, len and cp -- qs_free_body()
+ * qstring may NOT be NULL.
*
- * Returns: NULL if freed the structure
- * address of structure (if any), otherwise
+ * Returns with a body with size > 0 (even if 'slen' == 0).
*
- * NB: frees the body if the size != 0. So, a "dummy" qstring will not retain
- * the old body.
+ * Expects, but does not require, that 'slen' > 'size'.
+ *
+ * Does not change 'cp' or 'len' (even if 'len' > 'slen').
+ *
+ * If is an alias, copies min('len', 'alen', 'slen') characters to the body.
*/
-extern qstring
-qs_reset(qstring qs, int free_structure)
+Private void
+qs_make_to_size(qstring qs, usize slen, usize alen)
{
- if (qs != NULL)
+ /* 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 (qs->alias)
{
- if (qs->size != 0)
- XFREE(MTYPE_QSTRING_BODY, qs->body) ; /* sets qs->body = NULL */
+ /* alias or empty */
+ usize len = qs_len_nn(qs) ;
- if (free_structure)
- XFREE(MTYPE_QSTRING, qs) ; /* sets qs = NULL */
- else
- {
- qs->size = 0 ;
- qs->len = 0 ;
- qs->cp = 0 ;
- } ;
+ if (alen >= len)
+ alen = len ; /* keep min(alen, len) */
+
+ 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) ;
} ;
- return qs ;
} ;
/*==============================================================================
- * printf() and vprintf() type functions
+ * 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).
*/
/*------------------------------------------------------------------------------
- * Formatted print to qstring -- cf printf()
+ * Set qstring to be copy of the given string.
*
- * Allocate qstring if required.
+ * Treats src == NULL as an empty string. Otherwise src must be a '\0'
+ * terminated string.
*
- * Returns: address of qstring if OK
- * NULL if failed (unlikely though that is)
+ * Does not copy or count the '\0' terminator.
+ *
+ * See notes above.
*/
extern qstring
-qs_printf(qstring qs, const char* format, ...)
+qs_set(qstring qs, const char* src)
{
- va_list args;
+ return qs_set_n(qs, src, (src != NULL ? strlen(src) : 0)) ;
+} ;
- va_start (args, format);
- qs = qs_vprintf(qs, format, args);
- va_end (args);
+/*------------------------------------------------------------------------------
+ * Set qstring to be copy of leading 'n' bytes of given string.
+ *
+ * If n == 0, src is ignored (and may be NULL)
+ * If n > 0, src string MUST be at least 'n' bytes long.
+ *
+ * See notes above.
+ */
+extern qstring
+qs_set_n(qstring qs, const char* src, usize len)
+{
+ qs = qs_new_size(qs, len) ; /* ensures have body > len */
- return qs;
+ if (len != 0)
+ memcpy(qs_char_nn(qs), src, len) ;
+
+ qs_set_len_nn(qs, len) ;
+ qs_set_cp_nn(qs, 0) ;
+
+ return qs ;
} ;
/*------------------------------------------------------------------------------
- * Formatted print to qstring -- cf vprintf()
+ * Set qstring to be copy of given qstring contents.
*
- * Allocate qstring if required.
+ * 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.
*
- * Returns: address of qstring if OK
- * NULL if failed (unlikely though that is)
+ * See notes above -- and note that 'cp' is set to 0.
*/
extern qstring
-qs_vprintf(qstring qs, const char *format, va_list args)
+qs_set_qs(qstring qs, qstring src)
{
- va_list ac ;
- int len ;
- qstring qqs ;
+ return qs_set_n(qs, qs_body(src), qs_len(src)) ;
+} ;
- qqs = qs ;
- if (qs == NULL)
- qs = qs_new() ;
+/*------------------------------------------------------------------------------
+ * Set qstring to be copy of given elstring contents.
+ *
+ * See notes above.
+ */
+extern qstring
+qs_set_els(qstring qs, elstring src)
+{
+ return qs_set_n(qs, els_body(src), els_len(src)) ;
+} ;
- while (1)
+/*------------------------------------------------------------------------------
+ * Set qstring with given pattern to given length.
+ *
+ * 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.
+ *
+ * If the pattern is zero length, fills with spaces !
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * See notes above.
+ */
+extern qstring
+qs_set_fill_n(qstring qs, usize len, const char* src, usize flen)
+{
+ char* p ;
+ char* q ;
+ usize left ;
+
+ qs = qs_new_size(qs, len) ; /* ensures have body > len */
+
+ if (len != 0)
{
- /* 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'.
- *
- * Also note that given a zero length the string address may be NULL, and
- * the result is still the length required.
- */
- va_copy(ac, args);
- qs->len = len = vsnprintf (qs->body, qs->size, format, ac) ;
- va_end(ac);
+ 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 ;
- if (len < 0)
- break ;
+ while (left > 0)
+ {
+ if (left < flen)
+ flen = left ;
- if (len < (int)qs->size)
- return qs ;
+ memcpy(p, q, flen) ;
+ p += flen ;
+ left -= flen ;
- qs_make_to_length(qs, len) ;
+ flen += flen ;
+ } ;
} ;
- if (qqs == NULL)
- qs_reset_free(qs) ; /* discard what was allocated */
- else
- qs->len = 0 ;
+ qs_set_len_nn(qs, len) ;
+ qs_set_cp_nn(qs, 0) ;
- return NULL ;
+ return qs ;
} ;
/*==============================================================================
- * Other operations
+ * Appending to a qstring
+ *
+ * Copy the given string to the end of the given qstring (at 'len'),
+ * allocating qstring and/or extending it as required.
+ *
+ * If this is an alias, it is copied to before being appended to (even if
+ * appending nothing).
+ *
+ * Can append to NULL or empty qstring.
+ *
+ * Sets 'len' to new length.
+ * Does not affect 'cp'.
+ *
+ * Will work even if the stuff being appended is somewhere in the body of the
+ * qstring !!
+ *
+ * Returns: address of the qstring (allocated if required).
*/
/*------------------------------------------------------------------------------
- * Set qstring to be copy of the given string.
+ * Append given qstring to a qstring.
*
- * Allocates a qstring, if required.
+ * See notes above.
+ */
+extern qstring
+qs_append(qstring qs, qstring src)
+{
+ return qs_append_str_n(qs, qs_body(src), qs_len(src)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Append given string to a qstring.
*
- * Sets qs->len to the length of the string (excluding trailing '\0')
+ * Treats src == NULL as an empty string. Otherwise src must be a '\0'
+ * terminated string.
*
- * NB: if src == NULL, sets qstring to be zero length string.
+ * Does not copy or count the '\0' terminator.
*
- * Returns: address of the qstring copied to.
+ * See notes above.
+ */
+extern qstring qs_append_str(qstring qs, const char* src)
+{
+ return qs_append_str_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.
*
- * 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)
+qs_append_str_n(qstring qs, const char* src, usize n)
{
- qs = qs_set_len(qs, (src != NULL) ? strlen(src) : 0) ;
- if (qs->len != 0)
- memcpy(qs->body, src, qs->len + 1) ;
- else
- *((char*)qs->body) = '\0' ;
+ qs = qs_extend(qs, n) ; /* allocate, copy any alias, extend body,
+ set new length, etc */
+
+ if (n != 0)
+ memmove(qs_ep_char_nn(qs) - n, src, n) ;
return qs ;
} ;
/*------------------------------------------------------------------------------
- * Set qstring to be leading 'n' bytes of given string.
+ * Append given elstring to a qstring.
+ *
+ * See notes above.
+ */
+extern qstring
+qs_append_els(qstring qs, elstring src)
+{
+ return qs_append_str_n(qs, els_body(src), els_len(src)) ;
+} ;
+
+/*==============================================================================
+ * Setting of alias.
+ *
+ * Does NOT copy the given string, but sets the qstring to be a pointer to it.
+ * This means that:
*
- * Allocates qstring if required.
+ * 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.
*
- * Inserts '\0' terminator after the 'n' bytes copied.
+ * It is also the caller's responsibility to see that the original string
+ * is discarded as required (once the alias is no longer required.)
*
- * Returns: address of the qstring copied to.
+ * NB: if the qstring is changed in any way, a copy of the aliased string will
+ * be made first.
*
- * NB: src string MUST be at least 'n' bytes long.
+ * 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.
*
- * NB: src may not be NULL unless n == 0.
+ * Preserves any existing qstring body.
*
- * NB: if copying to a dummy qstring, the old body is simply discarded.
+ * 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_n(qstring qs, const char* src, size_t n)
+qs_set_alias(qstring qs, const char* src)
{
- qs = qs_set_len(qs, n) ; /* ensures have body > n */
- if (n != 0)
- memcpy(qs->body, src, n) ;
+ return qs_set_alias_n(qs, src, (src != NULL) ? strlen(src) : 0) ;
+} ;
- *((char*)qs->body + n) = '\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_new(0) ;
+
+ /* 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_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 ;
} ;
/*------------------------------------------------------------------------------
- * Append given string to a qstring.
+ * Set qstring to be an alias for the given qstring.
*
- * Allocates a qstring, if required.
+ * 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 !
*
- * Sets qs->len to the length of the result (excluding trailing '\0')
+ * If the src is an alias, then the qstring is another alias.
*
- * NB: if src == NULL, appends nothing -- but result will be '\0' terminated.
+ * 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.
*
- * Returns: address of the qstring copied to.
+ * If n == 0, src may be NULL
+ * 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_append(qstring qs, const char* src)
+extern qstring
+qs_set_alias_els(qstring qs, elstring src)
{
- size_t n ;
- char* ep ;
-
- n = (src != NULL) ? strlen(src) : 0 ;
-
- qs = qs_add_len(qs, n, &ep) ;
- ep = (char*)qs->body + qs->len ;
-
- if (n != 0)
- memcpy(ep - n, src, n + 1) ;
- else
- *ep = '\0' ;
-
- return qs ;
+ return qs_set_alias_n(qs, els_body(src), els_len(src)) ;
} ;
+/*==============================================================================
+ * Copying of qstring
+ */
+
/*------------------------------------------------------------------------------
- * Set qstring to be leading 'n' bytes of given string.
+ * Copy one qstring to another -- allocating/extending as required.
*
- * Allocates qstring if required.
- *
- * Returns: address of the qstring copied to.
+ * If both are NULL, returns NULL.
+ * Otherwise if dst is NULL, creates a new qstring.
*
- * NB: src string MUST be at least 'n' bytes long.
+ * If src is NULL it is treated as zero length, with 'cp' == 0.
*
- * NB: src may not be NULL unless n == 0.
+ * 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.
*
- * NB: if n == 0, appends nothing -- but result will be '\0' terminated.
+ * If dst is an alias, that is discarded.
*
- * NB: if copying to a dummy qstring, the old body is simply discarded.
+ * Copies the src 'cp' to the dst.
*
- * NB: if copying to a dummy qstring, the old body is simply discarded.
+ * Returns: the destination qstring (allocated if required).
*/
extern qstring
-qs_append_n(qstring qs, const char* src, size_t n)
+qs_copy(qstring dst, qstring src)
{
- char* ep ;
-
- qs = qs_add_len(qs, n, &ep) ;
+ if (src == NULL)
+ {
+ qs_clear(dst) ; /* if dst not NULL, clear it */
+ return dst ;
+ } ;
- if (n != 0)
- memcpy(ep - n, src, n) ;
+ if (src->alias)
+ dst = qs_set_alias_qs(dst, src) ;
+ else
+ dst = qs_set_qs(dst, src) ;
- *ep = '\0' ;
+ qs_set_cp_nn(dst, qs_cp_nn(src)) ; /* copy in the src cp. */
- return qs ;
+ return dst ;
} ;
-/*------------------------------------------------------------------------------
- * Copy one qstring to another
+/*==============================================================================
+ * printf() and vprintf() type functions
*
- * If both are NULL, returns NULL.
+ * Allocate and/or extend qstring as required.
*
- * Otherwise if dst is NULL, creates a new qstring.
+ * Any alias is discarded.
*
- * Sets dst: body = copy of src->len bytes of src->body -- '\0' terminated.
- * cp = src->cp
- * len = src->len
+ * Sets 'len' = length of the '\0' terminated result (less the '\0').
+ * Sets 'cp' = 0
*
- * Where a NULL src has zero cp and len.
+ * If fails and qs != NULL:
*
- * If not NULL, the destination is guaranteed to have a body, and that will be
- * '\0' terminated.
+ * Sets 'len' = 0
+ * Sets 'cp' = 0
+ * But retains any existing body.
*
- * Returns: the destination qstring
+ * Returns: address of qstring if OK
+ * NULL if failed (unlikely though that is)
+ */
+
+/*------------------------------------------------------------------------------
+ * Formatted print to qstring -- cf printf()
*
- * NB: if copying to a dummy qstring, the old body is simply discarded.
+ * See notes above.
*/
extern qstring
-qs_copy(qstring dst, qstring src)
+qs_printf(qstring qs, const char* format, ...)
{
- size_t n ;
+ va_list args;
- if (src == NULL)
- {
- if (dst == NULL)
- return dst ;
+ va_start (args, format);
+ qs = qs_vprintf(qs, format, args);
+ va_end (args);
+
+ return qs;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Formatted print to qstring -- cf vprintf()
+ *
+ * See notes above.
+ */
+extern qstring
+qs_vprintf(qstring qs, const char *format, va_list args)
+{
+ va_list ac ;
+ int slen ;
+ qstring qqs ;
- n = 0 ;
- dst->cp = 0 ;
- }
+ qqs = qs ; /* NULL => need to make qs */
+ if (qs == NULL)
+ qqs = qs_new(0) ; /* Sets size, cp & len = 0 */
else
+ qs_clear(qqs) ; /* Sets cp & len = 0, discard any alias, but
+ keep existing body */
+
+ while (1)
{
- if (dst == NULL)
- dst = qs_new() ;
+ /* 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'.
+ *
+ * Also note that given a zero length the string address may be NULL, and
+ * the result is still the length required.
+ */
+ va_copy(ac, args);
+ slen = vsnprintf(qs_body_nn(qqs), qqs->size, format, ac) ;
+ va_end(ac);
- n = src->len ;
- dst->cp = src->cp ;
- } ;
+ if (slen < 0)
+ break ; /* failed */
- qs_set_len(dst, n) ;
+ if ((usize)slen < qqs->size)
+ {
+ qs_set_len_nn(qqs, slen) ;
+ return qqs ; /* succeeded */
+ } ;
- if (n > 0)
- memcpy(dst->body, src->body, n) ;
+ qs_make_to_size(qqs, slen, 0) ; /* need space for slen */
+ } ;
- *((char*)dst->body + n) = '\0' ;
+ /* Failed... discard anything that has been allocated. */
+ if (qs == NULL)
+ qs_reset(qqs, free_it) ;
- return dst ;
+ return NULL ;
} ;
+/*==============================================================================
+ * Other operations
+ */
+
/*------------------------------------------------------------------------------
- * Compare significant parts of two qstrings.
+ * Replace 'r' bytes at 'cp' by 'n' bytes -- extending if required.
+ *
+ * May increase or decrease 'len'. but does not affect 'cp'.
*
- * By significant, mean excluding leading/trailing isspace() and treating
- * multiple isspace() as single isspace().
+ * If the given src is NULL, do not insert anything, just leave the space
+ * ready for it.
*
- * 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
+ * If 'cp' > 'len', then sets 'len' = 'cp' first -- which will introduce
+ * one or more undefined bytes.
+ *
+ * If this is a aliased qstring, a copy is made, so is no longer an alias.
*/
-extern int
-qs_cmp_sig(qstring a, qstring b)
+extern usize
+qs_replace(qstring qs, usize r, const void* src, usize n)
{
- const unsigned char* p_a ;
- const unsigned char* e_a ;
- const unsigned char* p_b ;
- const unsigned char* e_b ;
+ usize cp, len, nlen, after ;
+ const char* ap ;
+ char* np ;
- /* Set up pointers and dispense with leading and trailing isspace()
- *
- * Dummy up if NULL
- */
- if (a != NULL)
- {
- p_a = a->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 ;
- }
+ len = qs_len_nn(qs) ;
+ cp = qs_cp_nn(qs) ;
- if (b != NULL)
- {
- p_b = b->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 ;
- }
+ if ((cp + r) >= len)
+ /* Replacing up to or beyond the end of the string */
+ after = 0 ;
else
- {
- p_b = NULL ;
- e_b = NULL ;
- } ;
+ /* Replacing section short of the end of the string */
+ after = len - (cp + r) ;
- /* Now set about finding the first difference */
- while ((p_a != e_a) && (p_b != e_b))
+ 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)
{
- if (isspace(*p_a) && isspace(*p_b))
- {
- do { ++p_a ; } while (isspace(*p_a)) ;
- do { ++p_b ; } while (isspace(*p_b)) ;
- } ;
+ ap = qs_body_nn(qs) ; /* copy from the alias */
+
+ uint before = cp ;
+ if (before > len)
+ before = len ;
- if (*p_a != *p_b)
- return (*p_a < *p_b) ? -1 : +1 ;
+ if (before > 0)
+ memmove(np, ap, before) ;
- ++p_a ;
- ++p_b ;
+ qs_set_real_body_nn(qs) ;
} ;
- /* 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 ;
+ if (after > 0) /* move the after part before inserting */
+ memmove(np + cp + n, ap + cp + r, after) ;
+
+ if ((n > 0) && (src != NULL)) /* insert */
+ memmove(np + cp, src, n) ;
+
+ /* Set new 'len' */
+ qs_set_len_nn(qs, nlen) ;
+
+ return n + after ;
} ;
diff --git a/lib/qstring.h b/lib/qstring.h
index 0597eda8..b05c738b 100644
--- a/lib/qstring.h
+++ b/lib/qstring.h
@@ -22,23 +22,11 @@
#ifndef _ZEBRA_QSTRING_H
#define _ZEBRA_QSTRING_H
-#include "zebra.h"
-
-#include <stddef.h>
-#include <stdint.h>
-
+#include "misc.h"
+#include "vargs.h"
+#include "zassert.h"
#include "memory.h"
-
-#ifndef Inline
-#define Inline static inline
-#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__ */
+#include "elstring.h"
/*==============================================================================
* These "qstrings" address address the lack of a flexible length string in 'C'.
@@ -49,420 +37,673 @@
*
* 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
+{
+ elstring_t els ; /* *embedded* */
+
+ usize size ; /* of the els body */
-typedef struct qstring qstring_t ;
+ ulen cp ;
+
+ void* b_body ;
+ usize b_size ;
+
+ bool alias ;
+ char* empty ;
+} ;
+
+typedef struct qstring qstring_t[1] ;
typedef struct qstring* qstring ;
-struct qstring
+/* Setting an qstring object to all zeros is enough to initialise it to
+ * an empty string -- including the embedded elstring.
+ */
+CONFIRM(ELSTRING_INIT_ALL_ZEROS) ;
+enum
{
- union
- {
- void* body ;
- const void* const_body ;
- char* char_body ;
- unsigned char* uchar_body ;
- } ;
- size_t size ;
-
- size_t len ;
- size_t cp ;
+ QSTRING_INIT_ALL_ZEROS = true
} ;
/*------------------------------------------------------------------------------
* 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 usize qs_size(qstring qs) ;
+Inline usize qs_size_nn(qstring qs) ;
+
+Inline ulen qs_len(qstring qs) ;
+Inline ulen qs_len_nn(qstring qs) ;
+Inline void qs_set_len_nn(qstring qs, ulen len) ;
+Inline void qs_set_strlen_nn(qstring qs) ;
+
+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(qstring qs)
+{
+ return (qs != NULL) ? qs->els : NULL ;
+} ;
+
+/* Pointer to elstring of qstring (not NULL) */
+Inline elstring
+qs_els_nn(qstring qs)
+{
+ return qs->els ;
+} ;
+
+/* Copy elstring of qstring to another elstring (elstring not NULL) */
+Inline void
+qs_els_copy(elstring els, qstring qs)
+{
+ 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 !
*/
-Inline char* /* pointer to body of qstring */
-qs_chars(qstring qs)
+
+/* Start of qstring -- returns NULL if qstring is NULL, or body is. */
+Inline char*
+qs_char(qstring qs)
{
- return (char*)qs->body ;
+ return qs_body(qs) ;
} ;
-Inline unsigned char* /* pointer to body of qstring */
-qs_bytes(qstring qs)
+/* Start of qstring (not NULL)-- returns NULL if body is NULL. */
+Inline char*
+qs_char_nn(qstring qs)
{
- return (unsigned char*)qs->body ;
+ return qs_body_nn(qs) ;
} ;
-Inline char* /* pointer to given offset in qstring */
-qs_chars_at(qstring qs, size_t off)
+/* 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_chars(qs) + off ;
+ return (qs != NULL) ? qs_char_at_nn(qs, off) : NULL ;
} ;
-Inline unsigned char* /* pointer to given offset in qstring */
-qs_bytes_at(qstring qs, size_t off)
+/* Offset in qstring (not NULL) -- returns *nonsense if body is NULL */
+Inline char*
+qs_char_at_nn(qstring qs, usize off)
{
- return qs_bytes(qs) + off ;
+ return qs_char_nn(qs) + off ;
} ;
-Inline char* /* pointer to 'cp' offset in qstring */
+/* 'cp' in qstring -- returns NULL if qstring is NULL
+ * returns *nonsense if body is NULL */
+Inline char*
qs_cp_char(qstring qs)
{
- return qs_chars_at(qs, qs->cp) ;
+ return (qs != NULL) ? qs_cp_char_nn(qs) : NULL ;
} ;
-Inline unsigned char* /* pointer to 'cp' offset in qstring */
-qs_cp_byte(qstring qs)
+/* 'cp' in qstring (not NULL) -- returns *nonsense if body is NULL */
+Inline char*
+qs_cp_char_nn(qstring qs)
{
- return qs_bytes_at(qs, qs->cp) ;
+ return qs_char_at_nn(qs, qs_cp_nn(qs)) ;
} ;
-Inline char* /* pointer to 'len' offset in qstring */
+/* 'len' in qstring -- returns NULL if qstring is NULL
+ * returns *nonsense if body is NULL */
+Inline char*
qs_ep_char(qstring qs)
{
- return qs_chars_at(qs, qs->len) ;
+ return (qs != NULL) ? qs_ep_char_nn(qs) : NULL ;
} ;
-Inline unsigned char* /* pointer to 'len' offset in qstring */
-qs_ep_byte(qstring qs)
+/* 'len' in qstring (not NULL) -- returns *nonsense if body is NULL */
+Inline char*
+qs_ep_char_nn(qstring qs)
{
- return qs_bytes_at(qs, qs->len) ;
+ return qs_char_at_nn(qs, qs_len_nn(qs)) ;
} ;
-/*==============================================================================
- * Functions
+/* 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 ;
+} ;
+
+/* Start of qstring (not NULL)-- returns NULL if body is NULL. */
+Inline void*
+qs_body_nn(qstring qs)
+{
+ return els_body_nn(qs->els) ;
+} ;
+
+/* 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)
+{
+ els_set_body_nn(qs->els, body) ;
+} ;
+
+/* Size of qstring body -- zero if qstring is NULL, or is alias. */
+Inline usize
+qs_size(qstring qs)
+{
+ return (qs != NULL) ? qs_size_nn(qs) : 0 ;
+} ;
+
+/* Size of qstring (not NULL). */
+Inline usize
+qs_size_nn(qstring qs)
+{
+ return (qs->size) ;
+} ;
+
+/*----------------------------------------------------------------------------*/
+
+/* 'len' of qstring -- returns 0 if qstring is NULL */
+Inline ulen
+qs_len(qstring qs)
+{
+ return (qs != NULL) ? qs_len_nn(qs) : 0 ;
+} ;
+
+/* 'len' of qstring (not NULL) */
+Inline ulen
+qs_len_nn(qstring qs)
+{
+ return els_len_nn(qs->els) ;
+} ;
+
+/* set 'len' of qstring (not NULL) -- caller responsible for validity */
+Inline void
+qs_set_len_nn(qstring qs, ulen len)
+{
+ els_set_len_nn(qs->els, len) ;
+} ;
+
+/* set 'len' of qstring according to strlen(body) -- nothing NULL ! */
+Inline void
+qs_set_strlen_nn(qstring qs)
+{
+ els_set_len_nn(qs->els, strlen(qs_body_nn(qs))) ;
+} ;
+
+/*----------------------------------------------------------------------------*/
+
+/* 'cp' of qstring -- returns 0 if qstring is NULL */
+Inline ulen
+qs_cp(qstring qs)
+{
+ return (qs != NULL) ? qs_cp_nn(qs) : 0 ;
+} ;
+
+/* '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 ;
+} ;
+
+/* move 'cp' of qstring (not NULL) -- caller responsible for validity */
+Inline void
+qs_move_cp_nn(qstring qs, int delta)
+{
+ qs->cp += delta ;
+} ;
+
+/*----------------------------------------------------------------------------*/
+
+/* 'len' - 'cp' of qstring (not NULL) -- zero if 'len' < 'cp' */
+Inline ulen
+qs_after_cp_nn(qstring qs)
+{
+ return (qs_len_nn(qs) > qs_cp_nn(qs)) ? qs_len_nn(qs) - qs_cp_nn(qs) : 0 ;
+} ;
+
+/* 'len' - 'cp' of qstring -- zero if NULL or 'len' < 'cp' */
+Inline ulen
+qs_after_cp(qstring qs)
+{
+ return (qs != NULL) ? qs_after_cp_nn(qs) : 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Functions to fetch various pointer pairs.
*/
-extern qstring qs_init_new(qstring qs, size_t len) ;
-extern qstring qs_make_to_length(qstring qs, size_t len) ;
-extern void qs_free_body(qstring qs) ;
-extern qstring qs_reset(qstring qs, int free_structure) ;
+Inline void
+qs_pp(pp p, qstring qs)
+{
+ if (qs != NULL)
+ qs_pp_nn(p, qs) ;
+ else
+ pp_null(p) ;
+} ;
-#define qs_reset_keep(qs) qs_reset(qs, 0)
-#define qs_reset_free(qs) qs_reset(qs, 1)
+Inline void
+qs_pp_nn(pp p, qstring qs)
+{
+ els_pp_nn(p, qs->els) ;
+} ;
-Inline qstring qs_new(void) ;
-Inline qstring qs_dummy(qstring qs, const char* src, int pos) ;
+Inline void
+qs_cpp(cpp p, qstring qs)
+{
+ if (qs != NULL)
+ qs_cpp_nn(p, qs) ;
+ else
+ cpp_null(p) ;
+} ;
-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) ;
+Inline void
+qs_cpp_nn(cpp p, qstring qs)
+{
+ els_cpp_nn(p, qs->els) ;
+} ;
-extern qstring qs_set(qstring qs, const char* src) ;
-extern qstring qs_set_n(qstring qs, const char* src, size_t n) ;
+/*------------------------------------------------------------------------------
+ * Set real body -- discarding any alias.
+ *
+ * NB: does not affect 'len' or 'cp'
+ */
+Inline void qs_set_real_body_nn(qstring qs)
+{
+ qs->size = qs->b_size ;
+ qs_set_body_nn(qs, qs->b_body) ;
+ qs->alias = false ;
+} ;
-extern qstring qs_append(qstring qs, const char* src) ;
-extern qstring qs_append_n(qstring qs, const char* src, size_t n) ;
+/*==============================================================================
+ * Functions
+ */
+
+extern qstring qs_new(usize slen) ;
+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) ;
+Inline qstring qs_free(qstring qs) ;
-Inline qstring qs_need(qstring qs, size_t len) ;
-Inline qstring qs_set_len(qstring qs, size_t len) ;
-extern qstring qs_add_len(qstring qs, size_t n, char** p_ep) ;
+Inline char* qs_make_string(qstring qs) ;
+Inline const char* qs_string(qstring qs) ;
Inline void qs_clear(qstring qs) ;
-Inline size_t qs_len(qstring qs) ;
-Inline size_t qs_size(qstring qs) ;
-Inline void* qs_term(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) ;
-Inline size_t qs_insert(qstring qs, const void* src, size_t n) ;
-Inline void qs_replace(qstring qs, const void* src, size_t n) ;
-Inline size_t qs_delete(qstring qs, size_t n) ;
+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_str(qstring qs, const char* src) ;
+extern qstring qs_append_str_n(qstring qs, const char* src, usize n) ;
+extern qstring qs_append(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 int qs_cmp_sig(qstring a, qstring b) ;
+
+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_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) ;
+} ;
+
+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.
*/
/*------------------------------------------------------------------------------
- * Make a brand new, completely empty qstring
+ * Free given qstring
*/
Inline qstring
-qs_new(void)
+qs_free(qstring qs)
{
- /* Zeroising has set:
- *
- * body = NULL -- no body
- * size = 0 -- no body
- *
- * len = 0
- * cp = 0
- *
- * Nothing more to do unless initial size != 0
- */
- return XCALLOC(MTYPE_QSTRING, sizeof(qstring_t)) ;
+ return qs_reset(qs, free_it) ;
} ;
/*------------------------------------------------------------------------------
- * Construct a "dummy" qstring from the given string.
+ * Return pointer to string value -- ensure not alias and '\0' terminated.
+ *
+ * If is alias, copies that before adding '\0' terminator.
*
- * Allocates a qstring if required.
+ * Sets the '\0' terminator at the 'len' position, extending string if that
+ * is required.
*
- * This sets: body = the src
- * len = strlen(src) (0 if src is NULL)
- * cp = 0 if 'pos' is zero
- * len otherwise
- * size = 0
+ * If qs == NULL returns pointer to empty '\0' terminated string.
*
- * The zero size means that the qstring handling will not attempt to free
- * the body, nor will it write to it... Operations which require the qstring
- * to have a size will allocate a new body, and discard this one.
+ * NB: The qstring should not be changed or reset until this pointer has been
+ * discarded !
*
- * Returns: the address of the dummy qstring.
+ * NB: The value returned is not "const" BUT caller is NOT entitled to change
+ * any part of the string -- CERTAINLY nothing from the '\0' onwards !
*/
-Inline qstring
-qs_dummy(qstring qs, const char* src, int pos)
+Inline char*
+qs_make_string(qstring qs)
{
- if (qs == NULL)
- qs = qs_new() ;
+ static char empty_string[1] ;
- qs->const_body = src ;
- qs->len = (src != NULL) ? strlen(src) : 0 ;
- qs->cp = (pos == 0) ? 0 : qs->len ;
- qs->size = 0 ;
+ usize len ;
+ char* p ;
- return qs ;
-}
+ if (qs == NULL)
+ {
+ len = 0 ;
+ p = empty_string ;
+ }
+ 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 */
-/*------------------------------------------------------------------------------
- * Need space for a string of 'len' characters (plus possible '\0').
- *
- * Allocates the qstring, if required.
- *
- * Returns: address of qstring
- *
- * NB: asking for 0 bytes will cause a body to be allocated, ready for any
- * '\0' !
- *
- * NB: has no effect on 'cp' or 'len'. (Will be zero if new qstring allocated.)
- */
-Inline qstring
-qs_need(qstring qs, size_t len)
-{
- if ((qs == NULL) || (len >= qs->size))
- return qs_make_to_length(qs, len) ;
+ p = qs_char_nn(qs) ;
+ } ;
- assert(qs->body != NULL) ;
- return qs ;
+ *(p + len) = '\0' ;
+
+ return p ;
} ;
/*------------------------------------------------------------------------------
- * Set 'len' -- allocate or extend body as required.
+ * Return pointer to string value.
*
- * Allocates the qstring, if required.
+ * If possible, writes '\0' at 'len' in order to return a terminated string.
*
- * Returns: address of qstring
+ * If qs == NULL or body == NULL, or 'len' == 0 returns pointer to constant
+ * empty '\0' terminated string (ie "").
*
- * NB: setting len == 0 bytes will cause a body to be allocated, ready for any
- * '\0' !
+ * NB: if 'len' is beyond the current 'size' of the of the qstring, then
+ * does NOT write '\0' (does NOT extend the string).
*
- * NB: has no effect on 'cp' -- even if 'cp' > 'len'.
+ * NB: if string is an alias, this returns its address, whether it is
+ * terminated or not.
*
- * NB: if this is a "dummy" qstring, a copy is made of the original body.
+ * NB: In any event, the string should not be changed or reset until this
+ * pointer has been discarded !
*/
-Inline qstring
-qs_set_len(qstring qs, size_t len)
+Inline const char*
+qs_string(qstring qs)
{
- qs = qs_need(qs, len) ;
- qs->len = len ;
- return 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 ;
} ;
/*------------------------------------------------------------------------------
- * Reset contents of qstring.
+ * Clear contents of qstring -- preserves any qstring body, but sets len = 0.
*
* Does nothing if qstring is NULL
*
- * Sets 'cp' = 'len' = 0. Sets first byte of body (if any) to NULL.
+ * Sets 'cp' = 'len' = 0
*
- * For "dummy" qstring, discards the body.
+ * If is an alias qstring, discard the alias.
+ *
+ * NB: does not create a qstring body if there isn't one.
+ *
+ * NB: does not change the qstring body if there is one. (Which is used in
+ * vio_lc_write_nb().
*/
Inline void
qs_clear(qstring qs)
{
if (qs != NULL)
{
- qs->len = 0 ;
- qs->cp = 0 ;
- if (qs->size > 0)
- *((char*)qs->body) = '\0' ;
- else
- qs->body = NULL ;
+ if (qs->alias)
+ qs_set_real_body_nn(qs) ;
+ qs_set_len_nn(qs, 0) ;
+ qs_set_cp_nn(qs, 0) ;
} ;
} ;
/*------------------------------------------------------------------------------
- * Get length of qstring -- by doing strlen() -- and record it in qs->len.
+ * Ensure have space for a string of 'slen' characters (plus possible '\0'),
+ * and discard any alias.
+ *
+ * Allocate qstring if required (setting 'len' = 'cp' = 0).
+ *
+ * Returns: address of qstring -- with body that can be written upto and
+ * including 'slen' + 1.
*
- * Returns: the string length
+ * Has no effect on 'len' -- even if 'len' > 'slen'.
*
- * NB: if no body has been allocated, length = 0
+ * Has no effect on 'cp' -- even if 'cp' > 'len' or 'cp' > 'slen'.
+ *
+ * If this is a aliased qstring, the alias is discarded.
*/
-Inline size_t
-qs_len(qstring qs)
+Inline qstring
+qs_new_size(qstring qs, usize slen)
{
- return (qs != NULL) ? (qs->len = (qs->body != NULL) ? strlen(qs_chars(qs))
- : 0)
- : 0 ;
+ 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 ;
} ;
/*------------------------------------------------------------------------------
- * Get size of qstring body.
+ * Extend to 'len' + 'elen' -- allocate or extend body as required.
+ *
+ * Allocate qstring if required (setting 'cp' = 0).
+ *
+ * Returns: address of qstring -- with body that can be written up to and
+ * including 'len' + 'elen' + 1.
*
- * NB: if no body has been allocated, size == 0
- * if qstring is NULL, size == 0
+ * Has no effect on 'cp' -- even if 'cp' > 'len'.
*
- * NB: if this is a "dummy" qstring, size == 0.
+ * If this is a aliased qstring, a copy is made.
*/
-Inline size_t
-qs_size(qstring qs)
+Inline qstring
+qs_extend(qstring qs, usize elen)
{
- return (qs != NULL) ? qs->size : 0 ;
+ if (qs == NULL)
+ qs = qs_new_with_body(elen) ;
+ else
+ {
+ 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 */
+ } ;
+
+ qs_set_len_nn(qs, elen) ;
+
+ return qs ;
} ;
/*------------------------------------------------------------------------------
- * Get address of current end of qstring body -- ie byte at 'len'.
+ * Chop to given length -- will neither allocate nor extend body.
*
- * NB: allocates body if required.
+ * Does nothing if qstring is NULL.
*
- * There will be space for '\0' after 'len', so the address returned
- * is within the real body of the string.
+ * Does not change the 'len' if it is <= length to chop to.
*
- * NB: if this is a "dummy" qstring, a copy is made of the original body.
+ * NB: has no effect on 'cp' -- even if 'cp' > (new) 'len'.
*
- * NB: address of qstring may NOT be NULL.
+ * NB: if this is a aliased qstring, then it remains an aliased string, but
+ * shorter (unless no change made to the length).
*/
-Inline void*
-qs_end(qstring qs)
+Inline void
+qs_chop(qstring qs, usize clen)
{
- if (qs->len >= qs->size)
- qs_make_to_length(qs, qs->len) ; /* allows for trailing '\0' */
-
- return (char*)qs->body + qs->len ;
+ if (qs != NULL)
+ {
+ if (clen < qs_len_nn(qs))
+ qs_set_len_nn(qs, clen) ;
+ } ;
} ;
/*------------------------------------------------------------------------------
- * Set '\0' at qs->len -- allocate or extend body as required.
+ * Compare two qstrings -- returns the usual -ve, 0, +ve cmp result.
*
- * Returns address of body -- NULL if the qstring is NULL
- *
- * NB: if this is a "dummy" qstring, a copy is made of the original body.
+ * NULL qstring is treated as empty.
*/
-Inline void*
-qs_term(qstring qs)
+Inline int
+qs_cmp(qstring a, qstring b)
{
- size_t len ;
-
- if (qs == NULL)
- return NULL ;
-
- if ((len = qs->len) >= qs->size)
- qs_make_to_length(qs, len) ;
-
- *qs_chars_at(qs, len) = '\0' ;
-
- return qs->body ;
+ return els_cmp(qs_els(a), qs_els(b)) ;
} ;
/*------------------------------------------------------------------------------
- * 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 "dummy" qstring, a copy is made of the original body.
+ * Compare qstrings to given word -- see els_cmp_word
*/
-Inline size_t
-qs_insert(qstring qs, const void* src, size_t n)
+Inline int
+qs_cmp_word(qstring a, const char* w)
{
- size_t after ;
- char* p ;
-
- if (qs->len < qs->cp)
- qs->len = qs->cp ;
- after = qs->len - qs->cp ;
-
- qs_set_len(qs, qs->len + n) ; /* set len and ensure have space */
-
- p = qs_cp_char(qs) ;
- if (after > 0)
- memmove (p + n, p, after) ;
-
- if (n > 0)
- memmove(p, src, n) ;
-
- return after ;
+ return els_cmp_word(qs_els(a), w) ;
} ;
/*------------------------------------------------------------------------------
- * Replace 'n' bytes at 'cp' -- extending if required.
- *
- * May increase 'len'. but does not affect 'cp'.
- *
- * NB: qstring MUST NOT be NULL
+ * Compare significant parts of two qstrings -- returns the usual -ve, 0, +ve
+ * cmp result.
*
- * NB: if 'cp' > 'len', then sets 'len' = 'cp' first -- which will introduce
- * one or more undefined bytes.
+ * By significant, mean excluding leading/trailing isspace() and treating
+ * multiple isspace() as single isspace().
*
- * NB: the string is NOT re-terminated.
- *
- * NB: if this is a "dummy" qstring, a copy is made of the original body.
+ * NULL qstring is treated as empty.
*/
-Inline void
-qs_replace(qstring qs, const void* src, size_t n)
+Inline int
+qs_cmp_sig(qstring a, qstring b)
{
- if ((qs->len < qs->cp + n) || (qs->size == 0))
- qs_set_len(qs, qs->cp + n) ; /* set len and ensure have space */
+ return els_cmp_sig(qs_els(a), qs_els(b)) ;
+} ;
- if (n > 0)
- memmove(qs_cp_char(qs), src, n) ;
+/*------------------------------------------------------------------------------
+ * 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)) ;
} ;
/*------------------------------------------------------------------------------
- * Remove 'n' bytes at 'cp' -- extending if required.
- *
- * May change 'len'. but does not affect 'cp'.
+ * Is 'b' a leading substring of 'a' ? -- returns true if it is.
*
- * 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.
+ * If 'b' is empty it is always a leading substring.
*/
-Inline size_t
-qs_delete(qstring qs, size_t n)
+Inline bool
+qs_substring(qstring a, qstring b)
{
- size_t after ;
- char* p ;
-
- /* Watch out for "dummy" */
- if (qs->size == 0)
- qs_make_to_length(qs, qs->len) ;
-
- /* If deleting up to or beyond len, then simply set len == cp */
- if ((qs->cp + n) >= qs->len)
- {
- qs_set_len(qs, qs->cp) ; /* set len, looks after cp > len */
- return 0 ; /* nothing after */
- }
-
- /* There is at least one byte after cp (so body must exist) */
- after = qs->len - (qs->cp + n) ;
-
- if (n > 0)
- {
- p = qs_cp_char(qs) ;
- memmove (p, p + n, after) ;
-
- qs->len -= n ;
- } ;
-
- return after ;
+ return els_substring(qs_els(a), qs_els(b)) ;
} ;
-
#endif /* _ZEBRA_QSTRING_H */
diff --git a/lib/qtime.c b/lib/qtime.c
index 42b903da..d1283198 100644
--- a/lib/qtime.c
+++ b/lib/qtime.c
@@ -18,11 +18,11 @@
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
+#include "misc.h"
#include <sys/times.h>
#include <errno.h>
-#include "zassert.h"
#include "qtime.h"
/*==============================================================================
@@ -69,17 +69,40 @@
* (It appears that 60, 100, 250 and 1,000 ticks/sec. are popular options.)
*
* If sizeof(clock_t) > 4, it is assumed large enough never to wrap around.
+ * (But seems unlikely that such a system would not support CLOCK_MONOTONIC !)
*
* When clock_t is a 32-bit integer must be at least ready for wrap around.
- * There are two cases:
+ * We take the clock_t signed values and widen to 64-bit signed, so we have
+ * the current sample (this) and the previous one (last), and two cases to
+ * consider:
*
- * * +ve wrap around. new < old value, and new >= 0
+ * * +ve wrap around -- so value is 31-bit unsigned, and wraps from large
+ * +ve value to small +ve value.
*
- * step = (INT32_MAX - old + 1) + new
+ * step = this - last will be -ve
*
- * * -ve wrap around. new < old value, and new < 0 (and old > 0)
+ * 'last' will be some value ((INT32_MAX + 1) - x), and 'this' will be some
+ * (relatively) small value y. The step is x + y, we have:
*
- * step = (INT32_MAX - old + 1) - (INT32_MIN - new)
+ * step = y - ((INT32_MAX + 1) - x) = (x + y) - (INT32_MAX + 1)
+ *
+ * so we correct by adding (INT32_MAX + 1).
+ *
+ * * -ve wrap around -- so value is 32-bit signed, and wraps from a large
+ * +ve value to a very -ve value.
+ *
+ * step = this - last will be -ve
+ *
+ * 'last will' be some value (INT32_MAX + 1) - x, and 'this' will be some
+ * value (y - (INT32_MAX + 1)). The step is x + y, we have:
+ *
+ * step = (y - (INT32_MAX + 1)) - ((INT32_MAX + 1) - x)
+ * = (x + y) - 2 * (INT32_MAX + 1)
+ *
+ * so we correct by adding (INT32_MAX + 1).
+ *
+ * In both cases the wrap around gives an apparently -ve 'step', and that is
+ * corrected by adding (INT32_MAX + 1) until it goes +ve.
*
* In any event, a step > 24 hours is taken to means that something has gone
* very, very badly wrong.
@@ -97,23 +120,24 @@ CONFIRM((sizeof(clock_t) >= 4) && (sizeof(clock_t) <= 8)) ;
#ifdef GNU_LINUX
#define TIMES_TAKES_NULL 1
#else
-#undef TIMES_TAKES_NULL
+#define TIMES_TAKES_NULL 0
#endif
-static uint64_t monotonic = 0 ; /* monotonic clock in _SC_CLK_TCK's */
-static int64_t last_times_sample = 0 ; /* last value returned by times() */
+static int64_t monotonic = 0 ; /* monotonic clock in _SC_CLK_TCK's */
+static int64_t last_times_sample = 0 ; /* last value returned by times() */
-static uint64_t step_limit = 0 ; /* for sanity check */
+static int64_t step_limit = 0 ; /* for sanity check */
-static int64_t times_clk_tcks = 0 ; /* sysconf(_SC_CLK_TCK) */
-static qtime_t times_scale_q = 0 ; /* 10**9 / times_clk_tcks */
-static qtime_t times_scale_r = 0 ; /* 10**9 % times_clk_tcks */
+static int64_t times_clk_tcks = 0 ; /* sysconf(_SC_CLK_TCK) */
+static qtime_t times_scale_q = 0 ; /* 10**9 / times_clk_tcks */
+static qtime_t times_scale_r = 0 ; /* 10**9 % times_clk_tcks */
qtime_mono_t
qt_craft_monotonic(void) {
- struct tms dummy ;
- int64_t this_times_sample ;
- uint64_t step ;
+#if !TIMES_TAKES_NULL
+ struct tms dummy[1] ;
+#endif
+ clock_t this_times_sample ;
/* Set up times_scale_q & times_scale_q if not yet done. */
if (times_clk_tcks == 0) /* Is zero until it's initialized */
@@ -130,7 +154,7 @@ qt_craft_monotonic(void) {
times_scale_q = qr.quot ;
times_scale_r = qr.rem ;
- step_limit = (uint64_t)24 * 60 * 60 * times_clk_tcks ;
+ step_limit = (int64_t)24 * 60 * 60 * times_clk_tcks ;
} ;
/* No errors are defined for times(), but a return of -1 is defined */
@@ -139,51 +163,54 @@ qt_craft_monotonic(void) {
/* The following deals carefully with this -- cannot afford for the */
/* clock either to jump or to get stuck ! */
-#ifdef TIMES_TAKES_NULL
- this_times_sample = times(NULL) ; /* assume this saves effort ! */
+#if TIMES_TAKES_NULL
+# define TIMES_ARG NULL
#else
- this_times_sample = times(&dummy) ;
+# define TIMES_ARG dummy
#endif
+ this_times_sample = times(TIMES_ARG) ;
if (this_times_sample == -1) /* deal with theoretical error */
{
errno = 0 ;
- this_times_sample = times(&dummy) ;
+ this_times_sample = times(TIMES_ARG) ;
if (errno != 0)
zabort_errno("times() failed") ;
} ;
+#undef TIMES_ARG
- /* Calculate the step and verify sensible. */
- /* */
- /* Watch out for huge jumps and/or time going backwards. */
- /* For 32-bit clock_t, look out for wrap-around. */
-
- if ((sizeof(clock_t) > 4) || (this_times_sample > last_times_sample))
- /* time going backwards will appear as HUGE step forwards. */
- step = (uint64_t)(this_times_sample - last_times_sample) ;
+ /* If clock_t is large enough, treat as monotonic (!).
+ *
+ * Otherwise calculate the difference between this sample and the
+ * previous one -- the step.
+ *
+ * We do the sum in signed 64 bits, and the samples are signed 64 bits.
+ */
+ if (sizeof(clock_t) > 4)
+ monotonic = this_times_sample ;
else
{
- if (this_times_sample > 0)
- /* both samples +ve => +ve wrap around. */
- step = (uint64_t)( ((int64_t)INT32_MAX - last_times_sample + 1)
- + this_times_sample ) ;
- else
- /* this sample -ve and last sample +ve => -ve wrap round */
- /* this sample -ve and last sample -ve => time gone backwards */
- /* (which appears as a HUGE step forwards). */
- step = (uint64_t)( ((int64_t)INT32_MAX - last_times_sample + 1)
- - ((int64_t)INT32_MIN - this_times_sample) ) ;
- } ;
+ int64_t step ;
- /* TODO: better error messaging for large clock jumps. */
- if (step > step_limit)
- zabort("Sudden large monotonic clock jump") ;
+ step = this_times_sample - last_times_sample ;
- /* Advance the monotonic clock in sysconf(_SC_CLK_TCK) units. */
- monotonic += step ;
+ while (step < 0)
+ {
+ /* If times() wraps unsigned, then result needs INT32_MAX + 1
+ * adding to it to get to +ve result.
+ *
+ * If times() wraps signed, then result needs INT32_MAX + 1 adding
+ * to it *twice*.
+ */
+ step += (uint64_t)INT32_MAX + 1 ;
+ } ;
- /* Remember what we got, for next time. */
- last_times_sample = this_times_sample ;
+ if (step > step_limit)
+ zabort("Sudden large monotonic clock jump") ;
+
+ monotonic += step ;
+ last_times_sample = this_times_sample ;
+ } ;
/* Scale to qtime_t units. */
if (times_scale_r == 0)
@@ -203,3 +230,135 @@ qt_craft_mono_secs(void)
return monotonic / 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) ;
+} ;
+
+/*==============================================================================
+ * Tracking the local timezone, so can:
+ *
+ * a) rapidly convert clock_gettime(CLOCK_REALTIME, ...) times
+ *
+ * b) do that thread-safe
+ *
+ * c) do that async-signal-safe
+ *
+ * Assumptions:
+ *
+ * a) that timezones are on at most 5 minute boundaries (15 probably!)
+ *
+ * b) that DST changes are at least 60 days apart
+ *
+ * c) that DST changes occur on times which are on 5 minute boundaries
+ * (60 probably -- but this means that the DST change is on a 5 minute
+ * bounderay in local and epoch times !)
+ *
+ * Sets up and maintains a table containing 8 entries:
+ *
+ * [-3] previous - 2
+ * [-2] previous - 1
+ * [-1] previous -- previous 0-7 days
+ * [ 0] current -- current 0-7 days
+ * [+1] next -- next 0-7 days
+ * [+2] next + 1
+ * [+3] next + 2
+ * [ X] sentinal
+ *
+ * These are configured before any threads or anything else very much runs, so
+ * they are essentially static. There is a "current index", which is set to
+ * '0' to start with.
+ *
+ * Each entry comprises:
+ *
+ * * start time -- entry is valid for epoch times >= start
+ * * end time -- entry is valid for epoch times < end
+ * * offset -- add to epoch time to get local
+ *
+ * When set up the current timezone initially starts on the nearest 5 minute
+ * boundary in the past, and covers up to 7 days into the future, unless the
+ * timezone changes in that time. The timezones on either side are set
+ * similarly.
+ *
+ * At most one of these timezones may be a short one -- so this covers at least
+ * 14 days into the past and 21 into the future, and as time advances, upto
+ * 21 days into the past and down to 14 days into the future.
+ *
+ * Maximum range is 56 days -- which is within the assumed 60 days between
+ * DST changes.
+ *
+ * When time advances past the current entry the next, next + 1 and + 2 cover
+ * at least 14 days (21 if none are short entries).
+ *
+ * Every now and then (say every 5 minutes) a background process can check the
+ * current time. If that is no longer in the current entry, needs to update
+ * the table. Assuming time is moving forward: sets sentinal to be the next
+ * 0-7 days following the current last entry, and updates the "current index".
+ *
+ * BIG ASSUMPTION: that the "current index" value is written atomically, wrt
+ * to threads as well as signals.
+ *
+ * It doesn't matter if a thread or signal action code picks
+ * up an out of date "current index" value, because all the
+ * entries for the old state are still valid.
+ *
+ * No entry is changed while it is covered by the current
+ * index -3..+3.
+ *
+ * This works fine, UNLESS the clock_gettime(CLOCK_REALTIME, ...) changes
+ * dramatically -- as might happen if the operator adjusts the system clock a
+ * long way !
+ *
+ * To cope with this, a spare set of 8 entries are kept, and a new table can
+ * be built (under mutex). The worst that happens is that threads may be
+ * blocked waiting for the table to be updated.
+ *
+ * If the table is found to be out of date when a signal is bringing the
+ * system down, then the times logged will just have to use either the first
+ * or the last entry, and have done with it.
+ */
+
diff --git a/lib/qtime.h b/lib/qtime.h
index df486dfd..577c34a3 100644
--- a/lib/qtime.h
+++ b/lib/qtime.h
@@ -22,19 +22,12 @@
#ifndef _ZEBRA_QTIME_H
#define _ZEBRA_QTIME_H
-#include <stdint.h>
-#include <stdlib.h>
+#include "misc.h"
+
#include <time.h>
#include <sys/time.h>
#include <unistd.h>
-#include "zassert.h"
-#include "config.h"
-
-#ifndef Inline
-#define Inline static inline
-#endif
-
/*==============================================================================
* qtime_t -- signed 64-bit integer.
*
@@ -59,26 +52,22 @@ typedef qtime_t qtime_mono_t ; /* qtime_t value, monotonic time-base */
typedef qtime_t qtime_tod_t ; /* qtime_t value, timeofday time-base... */
/* ...just in case != CLOCK_REALTIME ! */
-/* A qtime_t second 123456789 -- nano-seconds */
-#define QTIME_SECOND 1000000000
-#define TIMESPEC_SECOND 1000000000
-#define TIMEVAL_SECOND 1000000
+/* A qtime_t second 123456789 -- nano-seconds */
+#define QTIME_SECOND ((qtime_t)1000000000)
+#define TIMESPEC_SECOND 1000000000
+#define TIMEVAL_SECOND 1000000
/* Macro to convert time in seconds to a qtime_t */
/* 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.
@@ -97,47 +86,40 @@ 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_mono_t
-qt_add_realtime(qtime_t interval) ; /* qt_get_realtime() + interval */
+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_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 qt_add_monotonic(qtime_t interval) ;
+ /* qt_get_monotonic() + interval */
Inline time_t qt_get_mono_secs(void) ;
/* clock_gettime(CLOCK_MONOTONIC, ...) */
/* OR equivalent using times() */
-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) ;
-extern time_t qt_craft_mono_secs(void) ;
-
/* 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_add_timeofday(qtime_t interval) ; /* qt_get_timeofday() + interval */
+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_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.
*/
@@ -148,7 +130,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.
*/
@@ -159,7 +142,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.
*/
@@ -176,7 +160,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.
*/
@@ -197,7 +182,12 @@ 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) ;
+Private time_t qt_craft_mono_secs(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 !
@@ -214,7 +204,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 !
@@ -225,7 +216,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)
@@ -233,7 +225,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
@@ -249,7 +242,8 @@ qt_get_monotonic(void)
#endif
} ;
-/* qt_get_monotonic() + interval
+/*------------------------------------------------------------------------------
+ * qt_get_monotonic() + interval
*/
Inline qtime_mono_t
qt_add_monotonic(qtime_t interval)
@@ -257,7 +251,8 @@ qt_add_monotonic(qtime_t interval)
return qt_get_monotonic() + interval;
} ;
-/* clock_gettime(CLOCK_MONOTONIC, ...) OR qt_craft_monotonic()
+/*------------------------------------------------------------------------------
+ * clock_gettime(CLOCK_MONOTONIC, ...) OR qt_craft_monotonic()
* -- returning time_t value
*
* Value returned is in seconds -- for coarser grain timings.
@@ -280,7 +275,8 @@ qt_get_mono_secs(void)
#endif
} ;
-/* gettimeofday(&tv, NULL) -- returning qtime_t value
+/*------------------------------------------------------------------------------
+ * gettimeofday(&tv, NULL) -- returning qtime_t value
*/
Inline qtime_tod_t
qt_get_timeofday(void)
@@ -290,7 +286,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)
@@ -298,36 +295,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 8c08a6bc..3af79d7b 100644
--- a/lib/qtimers.c
+++ b/lib/qtimers.c
@@ -18,23 +18,12 @@
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
+#include "misc.h"
-#include <stddef.h>
-#include <string.h>
-
-#include "zassert.h"
#include "qtimers.h"
#include "memory.h"
#include "heap.h"
-enum { qdebug =
-#ifdef QDEBUG
- 1
-#else
- 0
-#endif
-};
-
/*==============================================================================
* Quagga Timers -- qtimer_xxxx
*
@@ -170,7 +159,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) ;
@@ -213,11 +202,12 @@ qtimer_pile_dispatch_next(qtimer_pile qtp, qtime_mono_t upto)
* and the process MUST be run to completion.
*/
qtimer
-qtimer_pile_ream(qtimer_pile qtp, int free_structure)
+qtimer_pile_ream(qtimer_pile qtp, free_keep_b free_structure)
{
qtimer qtr ;
+ confirm(free_it == true) ;
- qtr = heap_ream_keep(&qtp->timers) ; /* ream, keeping the heap structure */
+ qtr = heap_ream(&qtp->timers, keep_it) ; /* ream, keeping the heap */
if (qtr != NULL)
qtr->active = false ; /* has been removed from pile */
else
@@ -342,7 +332,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 ;
@@ -370,7 +360,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) ;
} ;
@@ -386,7 +376,7 @@ qtimer_unset(qtimer qtr)
assert(qtp != NULL) ;
- if (qdebug)
+ if (qtimers_debug)
qtimer_pile_verify(qtp) ;
if (qtr->active)
@@ -396,7 +386,7 @@ qtimer_unset(qtimer qtr)
heap_delete_item(&qtp->timers, qtr) ;
- if (qdebug)
+ if (qtimers_debug)
qtimer_pile_verify(qtp) ;
qtr->active = false ;
@@ -413,8 +403,8 @@ qtimer_pile_verify(qtimer_pile qtp)
{
heap th = &qtp->timers ;
vector v ;
- vector_index i ;
- vector_index e ;
+ vector_index_t i ;
+ vector_length_t e ;
qtimer qtr ;
bool seen ;
@@ -429,7 +419,7 @@ qtimer_pile_verify(qtimer_pile qtp)
assert(th->state == Heap_Has_Backlink) ;
assert(th->backlink_offset == offsetof(qtimer_t, backlink)) ;
- v = &th->v ;
+ v = th->v ;
e = vector_end(v) ;
for (i = 0 ; i < e ; ++i)
{
diff --git a/lib/qtimers.h b/lib/qtimers.h
index 5beb931b..16808f1b 100644
--- a/lib/qtimers.h
+++ b/lib/qtimers.h
@@ -22,16 +22,12 @@
#ifndef _ZEBRA_QTIMERS_H
#define _ZEBRA_QTIMERS_H
-#include <stdbool.h>
+#include "misc.h"
#include "zassert.h"
#include "qtime.h"
#include "heap.h"
-#ifndef Inline
-#define Inline static inline
-#endif
-
/*==============================================================================
* Quagga Timers -- qtimer_xxxx
*
@@ -39,6 +35,39 @@
* each with an action to be executed when the timer expires.
*/
+/*------------------------------------------------------------------------------
+ * Sort out QTIMERS_DEBUG.
+ *
+ * Set to 1 if defined, but blank.
+ * Set to QDEBUG if not defined.
+ *
+ * Force to 0 if QTIMERS_NO_DEBUG is defined and not zero.
+ *
+ * So: defaults to same as QDEBUG, but no matter what QDEBUG is set to:
+ *
+ * * can set QTIMERS_DEBUG == 0 to turn off debug
+ * * or set QTIMERS_DEBUG != 0 to turn on debug
+ * * or set QTIMERS_NO_DEBUG != 0 to force debug off
+ */
+
+#ifdef QTIMERS_DEBUG /* If defined, make it 1 or 0 */
+# if IS_BLANK_OPTION(QTIMERS_DEBUG)
+# undef QTIMERS_DEBUG
+# define QTIMERS_DEBUG 1
+# endif
+#else /* If not defined, follow QDEBUG */
+# define QTIMERS_DEBUG QDEBUG
+#endif
+
+#ifdef QTIMERS_NO_DEBUG /* Override, if defined */
+# if IS_NOT_ZERO_OPTION(QTIMERS_NO_DEBUG)
+# undef QTIMERS_DEBUG
+# define QTIMERS_DEBUG 0
+# endif
+#endif
+
+enum { qtimers_debug = QTIMERS_DEBUG } ;
+
/*==============================================================================
* Data Structures.
*/
@@ -76,7 +105,7 @@ struct qtimer_pile
extern qtimer_pile qtimer_pile_init_new(qtimer_pile qtp) ;
extern bool qtimer_pile_dispatch_next(qtimer_pile qtp, qtime_mono_t upto) ;
extern qtime_t qtimer_pile_top_wait(qtimer_pile qtp, qtime_t max_wait) ;
-extern qtimer qtimer_pile_ream(qtimer_pile qtp, int free_structure) ;
+extern qtimer qtimer_pile_ream(qtimer_pile qtp, free_keep_b free_structure) ;
/* Ream out qtimer pile and free the qtimer structure. */
#define qtimer_pile_ream_free(qtp) qtimer_pile_ream(qtp, 1)
diff --git a/lib/regex.c b/lib/regex.c
index a22e03f6..425f3c8e 100644
--- a/lib/regex.c
+++ b/lib/regex.c
@@ -27,9 +27,8 @@
#undef _GNU_SOURCE
#define _GNU_SOURCE
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
+#include "misc.h"
+
#ifdef _WIN32
/* Windows does not provide unistd.h, which is required for abort() */
#include <process.h>
@@ -209,7 +208,7 @@ init_syntax_once ()
# define SYNTAX(c) re_syntax_table[c]
#endif /* not emacs */
-
+
/* Get the interface, including the syntax bits. */
#include <regex-gnu.h>
@@ -279,7 +278,7 @@ init_syntax_once ()
/* As in Harbison and Steele. */
# define SIGN_EXTEND_CHAR(c) ((((unsigned char) (c)) ^ 128) - 128)
#endif
-
+
/* Should we use malloc or alloca? If REGEX_MALLOC is not defined, we
use `alloca' instead of `malloc'. This is because using malloc in
re_search* or re_match* could cause memory leaks when C-g is used in
@@ -388,7 +387,7 @@ static int re_match_2_internal PARAMS ((struct re_pattern_buffer *bufp,
int pos,
struct re_registers *regs,
int stop));
-
+
/* These are the command codes that appear in compiled regular
expressions. Some opcodes are followed by argument bytes. A
command code can specify any interpretation whatsoever for its
@@ -527,7 +526,7 @@ typedef enum
notsyntaxspec
#endif /* emacs */
} re_opcode_t;
-
+
/* Common operations on the compiled pattern. */
/* Store NUMBER in two contiguous bytes starting at DESTINATION. */
@@ -604,7 +603,7 @@ extract_number_and_incr (destination, source)
# endif /* not EXTRACT_MACROS */
#endif /* DEBUG */
-
+
/* If DEBUG is defined, Regex prints many voluminous messages about what
it is doing (if the variable `debug' is nonzero). If linked with the
main program in `iregex.c', you can enter patterns and strings
@@ -977,7 +976,7 @@ printchar (c)
# define DEBUG_PRINT_DOUBLE_STRING(w, s1, sz1, s2, sz2)
#endif /* not DEBUG */
-
+
/* Set by `re_set_syntax' to the current regexp syntax to recognize. Can
also be assigned to arbitrarily: each pattern buffer stores its own
syntax, so it can be changed between regex compilations. */
@@ -1011,7 +1010,7 @@ re_set_syntax (syntax)
#ifdef _LIBC
weak_alias (__re_set_syntax, re_set_syntax)
#endif
-
+
/* This table gives an error message for each of the error codes listed
in regex.h. Obviously the order here has to be same as there.
POSIX doesn't require that we do anything for REG_NOERROR,
@@ -1091,7 +1090,7 @@ static const size_t re_error_msgid_idx[] =
REG_ESIZE_IDX,
REG_ERPAREN_IDX
};
-
+
/* Avoiding alloca during matching, to placate r_alloc. */
/* Define MATCH_MAY_ALLOCATE unless we need to make sure that the
@@ -1129,7 +1128,7 @@ static const size_t re_error_msgid_idx[] =
# undef MATCH_MAY_ALLOCATE
#endif
-
+
/* Failure stack declarations and macros; both re_compile_fastmap and
re_match_2 use a failure stack. These have to be macros because of
REGEX_ALLOCATE_STACK. */
@@ -1495,7 +1494,7 @@ typedef struct
} /* POP_FAILURE_POINT */
-
+
/* Structure for per-register (a.k.a. per-group) information.
Other register information, such as the
starting and ending positions (which are addresses), and the list of
@@ -1555,7 +1554,7 @@ typedef union
static char reg_unset_dummy;
#define REG_UNSET_VALUE (&reg_unset_dummy)
#define REG_UNSET(e) ((e) == REG_UNSET_VALUE)
-
+
/* Subroutine declarations and macros for regex_compile. */
static reg_errcode_t regex_compile _RE_ARGS ((const char *pattern, size_t size,
@@ -1809,7 +1808,7 @@ typedef struct
|| STREQ (string, "punct") || STREQ (string, "graph") \
|| STREQ (string, "cntrl") || STREQ (string, "blank"))
#endif
-
+
#ifndef MATCH_MAY_ALLOCATE
/* If we cannot allocate large objects within re_match_2_internal,
@@ -1857,7 +1856,7 @@ regex_grow_registers (num_regs)
}
#endif /* not MATCH_MAY_ALLOCATE */
-
+
static boolean group_in_compile_stack _RE_ARGS ((compile_stack_type
compile_stack,
regnum_t regnum));
@@ -2991,7 +2990,7 @@ regex_compile (pattern, size, syntax, bufp)
return REG_NOERROR;
} /* regex_compile */
-
+
/* Subroutines for `regex_compile'. */
/* Store OP at LOC followed by two-byte integer parameter ARG. */
@@ -3178,7 +3177,7 @@ compile_range (p_ptr, pend, translate, syntax, b)
return REG_NOERROR;
}
-
+
/* re_compile_fastmap computes a ``fastmap'' for the compiled pattern in
BUFP. A fastmap records which of the (1 << BYTEWIDTH) possible
characters can start a string that matches the pattern. This fastmap
@@ -3484,7 +3483,7 @@ re_compile_fastmap (bufp)
#ifdef _LIBC
weak_alias (__re_compile_fastmap, re_compile_fastmap)
#endif
-
+
/* Set REGS to hold NUM_REGS registers, storing them in STARTS and
ENDS. Subsequent matches using PATTERN_BUFFER and REGS will use
this memory for recording register information. STARTS and ENDS
@@ -3522,7 +3521,7 @@ re_set_registers (bufp, regs, num_regs, starts, ends)
#ifdef _LIBC
weak_alias (__re_set_registers, re_set_registers)
#endif
-
+
/* Searching routines. */
/* Like re_search_2, below, but only one string is specified, and
@@ -3704,7 +3703,7 @@ re_search_2 (bufp, string1, size1, string2, size2, startpos, range, regs, stop)
#ifdef _LIBC
weak_alias (__re_search_2, re_search_2)
#endif
-
+
/* This converts PTR, a pointer into one of the search strings `string1'
and `string2' into an offset from the beginning of that string. */
#define POINTER_TO_OFFSET(ptr) \
@@ -3783,7 +3782,7 @@ weak_alias (__re_search_2, re_search_2)
to actually save any registers when none are active. */
#define NO_HIGHEST_ACTIVE_REG (1 << BYTEWIDTH)
#define NO_LOWEST_ACTIVE_REG (NO_HIGHEST_ACTIVE_REG + 1)
-
+
/* Matching routines. */
#ifndef emacs /* Emacs never uses this. */
@@ -5248,7 +5247,7 @@ re_match_2_internal (bufp, string1, size1, string2, size2, pos, regs, stop)
return -1; /* Failure to match. */
} /* re_match_2 */
-
+
/* Subroutine definitions for re_match_2. */
@@ -5511,7 +5510,7 @@ bcmp_translate (s1, s2, len, translate)
}
return 0;
}
-
+
/* Entry points for GNU code. */
/* re_compile_pattern is the GNU regular expression compiler: it
@@ -5552,7 +5551,7 @@ re_compile_pattern (pattern, length, bufp)
#ifdef _LIBC
weak_alias (__re_compile_pattern, re_compile_pattern)
#endif
-
+
/* Entry points compatible with 4.2 BSD regex library. We don't define
them unless specifically requested. */
@@ -5623,7 +5622,7 @@ re_exec (s)
}
#endif /* _REGEX_RE_COMP */
-
+
/* POSIX.2 functions. Don't define these for Emacs. */
#ifndef emacs
diff --git a/lib/routemap.c b/lib/routemap.c
index b452530d..6de2098d 100644
--- a/lib/routemap.c
+++ b/lib/routemap.c
@@ -913,14 +913,15 @@ route_map_finish (void)
}
/* VTY related functions. */
-DEFUN (route_map,
- route_map_cmd,
- "route-map WORD (deny|permit) <1-4294967295>",
- "Create route-map or enter route-map command mode\n"
- "Route map tag\n"
- "Route map denies set operations\n"
- "Route map permits set operations\n"
- "Sequence to insert to/delete from existing route-map entry\n")
+DEFUN_ATTR (route_map,
+ route_map_cmd,
+ "route-map WORD (deny|permit) <1-4294967295>",
+ "Create route-map or enter route-map command mode\n"
+ "Route map tag\n"
+ "Route map denies set operations\n"
+ "Route map permits set operations\n"
+ "Sequence to insert to/delete from existing route-map entry\n",
+ CMD_ATTR_NODE + RMAP_NODE)
{
int permit;
unsigned long seq;
@@ -959,7 +960,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/routemap.h b/lib/routemap.h
index ac6cb999..05f037f1 100644
--- a/lib/routemap.h
+++ b/lib/routemap.h
@@ -22,7 +22,7 @@
#ifndef _ZEBRA_ROUTEMAP_H
#define _ZEBRA_ROUTEMAP_H
-#include <stdint.h>
+#include "misc.h"
/* Route map's type. */
enum route_map_type
diff --git a/lib/sigevent.c b/lib/sigevent.c
index 18fcffb0..c7c1e134 100644
--- a/lib/sigevent.c
+++ b/lib/sigevent.c
@@ -19,11 +19,19 @@
* 02111-1307, USA.
*/
-#include <zebra.h>
-#include <sigevent.h>
-#include <log.h>
-
-#ifdef SA_SIGINFO
+#include "zebra.h"
+#include "misc.h"
+#include "sigevent.h"
+#include "log.h"
+#include "vty.h"
+#include "qpnexus.h"
+#include "qpthreads.h"
+
+#include <stdarg.h>
+
+/*------------------------------------------------------------------------------
+ * Want to get some context for core and exit handlers.
+ */
#ifdef HAVE_UCONTEXT_H
#ifdef GNU_LINUX
/* get REG_EIP from ucontext.h */
@@ -33,50 +41,407 @@
#endif /* GNU_LINUX */
#include <ucontext.h>
#endif /* HAVE_UCONTEXT_H */
-#endif /* SA_SIGINFO */
+/*------------------------------------------------------------------------------
+ * Use SA_SIGINFO type handlers throughout
+ */
+#ifndef SA_SIGINFO
+#error Sorry... require SA_SIGINFO
+#endif
+
+typedef void sig_handler(int signo, siginfo_t* info, void* context) ;
+
+/*==============================================================================
+ * Signal handling for Quagga.
+ *
+ * The objectives are:
+ *
+ * 1) to handle the abnormal terminations so that they are logged, and
+ * any available information logged with them.
+ *
+ * 2) to ignore a number of signals that have no significance
+ *
+ * 3) to catch some signals such that they are treated as events in either
+ * the qpthreads or the legacy threads worlds.
+ *
+ * For the qpthreads world, these are all handled in the main thread.
+ *
+ * These may not be any of the "hard" signals or any of the signals
+ * reserved by the library.
+ *
+ * 4) to catch some signals such that they cause an "interrupt" to, e.g.
+ * pselect(), but have no other effect.
+ *
+ * SIGUSR2 is reserved for this purpose for qpthreads world
+ * (aka SIG_INTERRUPT).
+ *
+ * Signal disposition is established early in the morning, and is static from
+ * then on.
+ */
+
+/*==============================================================================
+ * Signal Sets.
+ *
+ * The following signal sets are initialised by signal_init().
+ *
+ * * hard_signals -- signals that mean that the program has misbehaved, and
+ * should exit, now -- e.g. SIGSEGV or SIGILL.
+ *
+ * In the pthreaded world, these signals are handled
+ * (or at least, not blocked) by all threads, and it is
+ * expected that they will be given to the thread which
+ * has failed.
+ *
+ * These signals are not blocked.
+ *
+ * This includes SIGKILL and SIGSTOP, which cannot be
+ * blocked, caught or ignored.
+ *
+ * * core_signals -- signals that by default are terminate + core, so this
+ * more or less the hard_signals, except:
+ *
+ * * excludes SIGKILL and SIGSTOP
+ *
+ * * includes at least one (SIGQUIT) signal that is
+ * not a hard_signal.
+ *
+ * The default action is to catch these signals, log the
+ * event and then abort() -- having turned off the
+ * SIGABRT handler !
+ *
+ * * exit_signals -- signals that by default are terminate, so this
+ * includes, e.g., SIGTERM or SIGINT.
+ *
+ * The default action is to catch these signals, log the
+ * event and then exit().
+ *
+ * * ignore_signals -- signals which, by default, we wish to ignore, so are
+ * set to SIG_IGN.
+ *
+ * * qsig_signals -- signals which are caught, and are later signalled to
+ * quagga_sigevent_process().
+ *
+ * Note that multiple signals may be seen before
+ * quagga_sigevent_process() is run, but they will only
+ * generate one event.
+ *
+ * * qsig_interrupts -- signals which are caught, but otherwise ignored,
+ * so their only function is to interrupt -- in particular
+ * to interrupt pselect().
+ *
+ * * qsig_reserved -- signal which the library reserves for itself.
+ *
+ * In the pthreads world, all signals other than the hard_signals are blocked
+ * by all threads other than the main thread.
+ *
+ * Note that we leave SIGTRAP alone -- so do not disturb debuggger(s).
+ */
+static bool signals_initialised = false ;
+
+static sigset_t hard_signals[1] ;
+static sigset_t core_signals[1] ;
+static sigset_t exit_signals[1] ;
+static sigset_t ignore_signals[1] ;
+static sigset_t qsig_signals[1] ;
+static sigset_t qsig_interrupts[1] ;
+static sigset_t qsig_reserved[1] ;
+
+static void qsig_add(int signo, qsig_event* event) ;
+static int signal_set_set(sigset_t* set, sig_handler* handler) ;
+static int signal_set(int signo, sig_handler* handler) ;
+
+static void __attribute__ ((noreturn))
+ core_handler(int signo, siginfo_t *info, void *context) ;
+static void __attribute__ ((noreturn))
+ exit_handler(int signo, siginfo_t* info, void* context) ;
+static void
+ quagga_signal_handler(int signo, siginfo_t* info, void* context) ;
+static void
+ quagga_interrupt_handler(int signo, siginfo_t* info, void* context) ;
+
+/*------------------------------------------------------------------------------
+ * The following signals are not known to POSIX (2008) or are extensions.
+ */
+#ifndef SIGEMT
+# define SIGEMT 0
+#endif
+#ifndef SIGIO
+# define SIGIO 0
+#endif
+#ifndef SIGIOT
+# define SIGIOT 0
+#endif
+#ifndef SIGPOLL
+# define SIGPOLL 0
+#endif
+#ifndef SIGPROF
+# define SIGPROF 0
+#endif
+#ifndef SIGPWR
+# define SIGPWR 0
+#endif
+#ifndef SIGSTKFLT
+# define SIGSTKFLT 0
+#endif
+#ifndef SIGSYS
+# define SIGSYS 0
+#endif
+#ifndef SIGVTALRM
+# define SIGVTALRM 0
+#endif
+#ifndef SIGWINCH
+# define SIGWINCH 0
+#endif
+#ifndef SIGXRES
+# define SIGXRES 0
+#endif
+
+/*------------------------------------------------------------------------------
+ * The signal handling below assumes that no signal that we have any interest
+ * in will have a signal number greater than the following.
+ *
+ * All the signals we are interested in are initialised in signal_init, so
+ * asserts in the code below will trap, very early on, any signal with a
+ * larger number than this.
+ *
+ * This value is used to place a very outside limit on the signal numbers that
+ * will attempt to deal with.
+ */
+enum { SIG_MAX = 128, SIG_COUNT } ;
+
+/* The value is established at signal_init() time, as the maximum signal
+ * number to consider -- established by sigaddset() on an empty set.
+ */
+int sig_max = 0 ;
+
+/*------------------------------------------------------------------------------
+ * Quagga signals descriptor struct
+ *
+ * Maps real signal numbers to "qso" ordinals... A relatively limited number
+ * of signals need to be fed into the event system, this mechanism minimises
+ * the work required to discover which signal has gone off.
+ *
+ * The qsig actions are held in a small vector in the static sigmaster
+ * structure.
+ */
+enum
+{
+ sig_null = 0, /* no real signal is this */
+ sig_min = 1, /* first real signal is at least this */
+} ;
+typedef uchar sig_num_t ; /* signal number */
+CONFIRM(SIG_MAX <= UCHAR_MAX) ;
+
+enum
+{
+ qso_null = 0, /* no qsig uses this */
+ qso_min = 1, /* first qsig is this */
+ qso_max = 10, /* only a handful are really required */
-/* master signals descriptor struct */
-struct quagga_sigevent_master_t
+ qso_count, /* number qs ordinals */
+} ;
+typedef uchar qs_ord_t ; /* qs ordinal */
+
+/*------------------------------------------------------------------------------
+ * Static structure for all known qsig_signals.
+ *
+ * Note that the qsig_interrupts are not recorded here !
+ */
+struct quagga_sigevent_master
{
+ qs_ord_t qsigc ; /* number of signals known */
+ volatile sig_atomic_t caught[qso_count] ;
+
+ qsig_event* event[qso_count] ; /* how to deal with them */
+
+ qs_ord_t map[SIG_COUNT] ; /* real signal to qs ordinal */
+
+#ifdef SIGEVENT_SCHEDULE_THREAD
struct thread *t;
+#endif
+
+} qsig_master ;
+
+/*------------------------------------------------------------------------------
+ * Initialise signals.
+ *
+ * 1. construct the signal sets discussed above.
+ *
+ * 2. set default handlers for: core_signals -- core_handler()
+ * exit_signals -- exit_handler()
+ * ignore_signals -- SIG_IGN
+ *
+ * 3. set handlers for signals used by library
+ *
+ * 4. set handlers for signals used by the daemon.
+ *
+ * This is done once, and once only, early in the morning.
+ */
+extern void
+signal_init (struct thread_master *m, int sigc,
+ struct quagga_signal_t signals[])
+{
+ int i ;
+
+ /* Set sig_max by experiment */
+ {
+ sigset_t trial[1] ;
+ int signo ;
+
+ sigemptyset(trial) ;
+ for (signo = sig_min ; signo <= SIG_COUNT ; ++signo)
+ {
+ if (sigaddset(trial, signo) < 0)
+ break ;
+ } ;
+
+ --signo ; /* last acceptable signo */
+ if ((signo < sig_min) || (signo > SIG_MAX))
+ zabort("cannot establish reasonable 'sig_max'") ;
+
+ sig_max = signo ;
+ } ;
+
+ /* Construct the standard sets of signals. */
+ sigmakeset(hard_signals, SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGKILL,
+ SIGSEGV, SIGSTOP, SIGXCPU, SIGXFSZ,
+ SIGSYS,
+ SIGEMT, SIGIOT, SIGXRES,
+ -1) ;
+
+ sigcopyset(core_signals, hard_signals) ;
+ sigaddset(core_signals, SIGQUIT) ;
+ sigdelset(core_signals, SIGKILL) ;
+ sigdelset(core_signals, SIGSTOP) ;
+
+ sigmakeset(exit_signals, SIGALRM, SIGHUP, SIGINT, SIGTERM, SIGUSR1, SIGUSR2,
+ SIGIO, SIGPOLL, SIGPROF, SIGPWR, SIGSTKFLT,
+ SIGVTALRM,
+ -1) ;
+
+ sigmakeset(ignore_signals, SIGCHLD, SIGCONT, SIGPIPE, SIGTSTP, SIGTTIN,
+ SIGTTOU, SIGURG, SIGWINCH,
+ -1) ;
+
+ /* Initialise the sig_master, qsig_signals and qsig_interrupts.
+ *
+ * Reserve all the hard_signals.
+ */
+ memset(&qsig_master, 0, sizeof(qsig_master)) ;
+
+ sigemptyset(qsig_signals) ;
+ sigemptyset(qsig_interrupts) ;
+ sigcopyset(qsig_reserved, hard_signals) ;
+
+ /* The signals used by the library.
+ *
+ * Added to qsig_signals or qsig_interrupts and to qsig_reserved.
+ */
+ qsig_add(SIGCHLD, vty_sigchld) ;
+ qsig_add(SIG_INTERRUPT, NULL) ;
+
+ /* Now collect the daemon's own signals.
+ *
+ * Added to qsig_signals or qsig_interrupts and to qsig_reserved.
+ */
+ for (i = 0 ; i < sigc ; ++i)
+ qsig_add(signals[i].signal, signals[i].handler) ;
+
+ /* In case signals with different names are the same, and to make the
+ * required reservations, let qsig_reserved take precedence over exit_signals
+ * and those take precedence over ignore_signals.
+ */
+ sigsubsets(exit_signals, qsig_reserved) ;
+ sigsubsets(ignore_signals, qsig_reserved) ;
+ sigsubsets(ignore_signals, exit_signals) ;
+
+ /* Also, remove anything from core_signals which is now qsig_signals or
+ * qsig_interrupts (possibly SIGQUIT !).
+ */
+ sigsubsets(core_signals, qsig_signals) ;
+ sigsubsets(core_signals, qsig_interrupts) ;
+
+ /* Install handlers */
+ signal_set_set(core_signals, core_handler) ;
+ signal_set_set(exit_signals, exit_handler) ;
+ signal_set_set(ignore_signals, NULL) ;
+ signal_set_set(qsig_signals, quagga_signal_handler) ;
+ signal_set_set(qsig_interrupts, quagga_interrupt_handler) ;
+
+ /* If using a timer thread to scan for signal events, start that now.
+ */
+#ifdef SIGEVENT_SCHEDULE_THREAD
+ sig_master.t =
+ thread_add_timer (m, quagga_signal_timer, &sig_master,
+ QUAGGA_SIGNAL_TIMER_INTERVAL);
+#endif /* SIGEVENT_SCHEDULE_THREAD */
- struct quagga_signal_t *signals;
- int sigc;
+ /* Signals are now initialised */
+ signals_initialised = true ;
+} ;
- volatile sig_atomic_t caught;
-} sigmaster;
+/*------------------------------------------------------------------------------
+ * Get the hard_signals set
+ */
+extern const sigset_t*
+signal_get_hard_set(void)
+{
+ assert(signals_initialised) ;
+ return hard_signals ;
+} ;
-/* Generic signal handler
- * Schedules signal event thread
+/*------------------------------------------------------------------------------
+ * Add signal to those to be caught either for quagga_sigevent_process() or
+ * to then be dropped (so called interrupt signals).
+ *
+ * Checks that signal is not amongst the reserved signals.
+ *
+ * Add signal to the reserved signals.
*/
static void
-quagga_signal_handler (int signo)
+qsig_add(int signo, qsig_event* event)
{
- int i;
- struct quagga_signal_t *sig;
+ int s ;
+
+ s = sigismember(qsig_reserved, signo) ;
+ if ((s < 0) || (signo > sig_max))
+ zabort("invalid or unknown signal number") ;
+ if (s > 0)
+ zabort("signal is reserved (or already set)") ;
- for (i = 0; i < sigmaster.sigc; i++)
+ sigaddset(qsig_reserved, signo) ;
+
+ if (event == NULL)
+ sigaddset(qsig_interrupts, signo) ;
+ else
{
- sig = &(sigmaster.signals[i]);
+ sigaddset(qsig_signals, signo) ;
- if (sig->signal == signo)
- sig->caught = 1;
- }
+ if (qsig_master.qsigc >= qso_count)
+ zabort("too many signals to be caught") ;
- sigmaster.caught = 1;
-}
+ ++qsig_master.qsigc ;
-/* check if signals have been caught and run appropriate handlers
+ qsig_master.map[signo] = qsig_master.qsigc ;
+ qsig_master.event[qsig_master.qsigc] = event ;
+
+ } ;
+} ;
+
+/*==============================================================================
+ * The event level handling of qsig_signals, delivered via qsig_master.
+ */
+
+/*------------------------------------------------------------------------------
+ * check if signals have been caught and run respective event functions
*
* Returns: 0 => nothing to do
* -1 => failed
* > 0 => done this many signals
*/
-int
+extern int
quagga_sigevent_process (void)
{
- struct quagga_signal_t *sig;
int i;
int done ;
#ifdef SIGEVENT_BLOCK_SIGNALS
@@ -100,21 +465,18 @@ quagga_sigevent_process (void)
#endif /* SIGEVENT_BLOCK_SIGNALS */
done = 0 ;
- if (sigmaster.caught > 0)
+ if (qsig_master.caught[qso_null] != 0)
{
- sigmaster.caught = 0;
- /* must not read or set sigmaster.caught after here,
+ qsig_master.caught[qso_null] = 0;
+ /* must not read or set sigmaster.caught[0] after here,
* race condition with per-sig caught flags if one does
*/
-
- for (i = 0; i < sigmaster.sigc; i++)
+ for (i = 1 ; i <= qsig_master.qsigc ; i++)
{
- sig = &(sigmaster.signals[i]);
-
- if (sig->caught > 0)
+ if (qsig_master.caught[i] != 0)
{
- sig->caught = 0;
- sig->handler ();
+ qsig_master.caught[i] = 0;
+ (qsig_master.event[i])() ;
++done ;
}
}
@@ -128,6 +490,9 @@ quagga_sigevent_process (void)
return done ;
}
+/*------------------------------------------------------------------------------
+ * Optional timer thread to poll for signals
+ */
#ifdef SIGEVENT_SCHEDULE_THREAD
/* timer thread to check signals. Shouldnt be needed */
int
@@ -144,246 +509,395 @@ quagga_signal_timer (struct thread *t)
}
#endif /* SIGEVENT_SCHEDULE_THREAD */
-/* Initialization of signal handles. */
-/* Signal wrapper. */
-static int
-signal_set (int signo)
+/*==============================================================================
+ * The signal handlers.
+ */
+static void * program_counter(void *context) ;
+
+/*------------------------------------------------------------------------------
+ * Terminate + Core
+ */
+static void __attribute__ ((noreturn))
+core_handler(int signo, siginfo_t *info, void *context)
{
- int ret;
- struct sigaction sig;
- struct sigaction osig;
-
- sig.sa_handler = &quagga_signal_handler;
- sigfillset (&sig.sa_mask);
- sig.sa_flags = 0;
- if (signo == SIGALRM) {
-#ifdef SA_INTERRUPT
- sig.sa_flags |= SA_INTERRUPT; /* SunOS */
-#endif
- } else {
-#ifdef SA_RESTART
- sig.sa_flags |= SA_RESTART;
-#endif /* SA_RESTART */
- }
-
- ret = sigaction (signo, &sig, &osig);
- if (ret < 0)
- return ret;
- else
- return 0;
+ zlog_signal(signo, "aborting...", info, program_counter(context)) ;
+ zabort_abort();
}
-#ifdef SA_SIGINFO
+/*------------------------------------------------------------------------------
+ * Terminate
+ */
+static void __attribute__ ((noreturn))
+exit_handler(int signo, siginfo_t* info, void* context)
+{
+ zlog_signal(signo, "exiting...", info, program_counter(context));
+ _exit(128+signo);
+}
+
+/*------------------------------------------------------------------------------
+ * Generic signal handler -- captures signals in the sig_master caught vector.
+ *
+ * quagga_sigevent_process() deals with the caught signals.
+ */
+static void
+quagga_signal_handler(int signo, siginfo_t* info, void* context)
+{
+ qs_ord_t qso ;
+
+ if (sigismember(qsig_signals, signo) <= 0)
+ {
+ zlog_signal(signo, "quagga_signal_handler: unknown or invalid signal",
+ info, program_counter(context)) ;
+ zabort_abort();
+ } ;
+
+ qso = qsig_master.map[signo] ;
+
+ /* Set individual caught flag before composite.
+ *
+ * This works if quagga_signal_handler() and quagga_sigevent_process()
+ * were to run at the same time -- unlikely though that may be.
+ *
+ * If quagga_sigevent_process() sees individual flag before the composite,
+ * that's fine -- worst that happens is will run through a further time
+ * and not find anything.
+ *
+ * If flags were set in the opposite order, could clear the composite
+ * and miss the individual, so lose the signal till the next time the
+ * composite is set.
+ */
+ qsig_master.caught[qso] = 1 ;
+ qsig_master.caught[qso_null] = 1 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Generic signal handler -- where signal is to be caught, and immediately
+ * dropped.
+ */
+static void
+quagga_interrupt_handler(int signo, siginfo_t* info, void* context)
+{
+ if (sigismember(qsig_interrupts, signo) <= 0)
+ {
+ zlog_signal(signo, "quagga_interrupt_handler: unknown or invalid signal",
+ info, program_counter(context)) ;
+ zabort_abort();
+ } ;
+} ;
-/* XXX This function should be enhanced to support more platforms
- (it currently works only on Linux/x86). */
+/*------------------------------------------------------------------------------
+ * Extract program counter from context.
+ *
+ * XXX This function should be enhanced to support more platforms
+ * (it currently works only on Linux/x86).
+ */
static void *
program_counter(void *context)
{
#ifdef HAVE_UCONTEXT_H
-#ifdef GNU_LINUX
-#ifdef REG_EIP
+# ifdef GNU_LINUX
+# ifdef REG_EIP
if (context)
return (void *)(((ucontext_t *)context)->uc_mcontext.gregs[REG_EIP]);
-#endif /* REG_EIP */
-#endif /* GNU_LINUX */
+# endif /* REG_EIP */
+# ifdef REG_RIP
+ if (context)
+ return (void *)(((ucontext_t *)context)->uc_mcontext.gregs[REG_RIP]);
+# endif /* REG_RIP */
+# endif /* GNU_LINUX */
#endif /* HAVE_UCONTEXT_H */
return NULL;
-}
+} ;
-#endif /* SA_SIGINFO */
+/*==============================================================================
+ * Signal clearing for abort() and fork()/vfork().
+ */
-static void __attribute__ ((noreturn))
-exit_handler(int signo
-#ifdef SA_SIGINFO
- , siginfo_t *siginfo, void *context
-#endif
- )
+/*------------------------------------------------------------------------------
+ * Set default sigaction for given signo
+ */
+static int
+sigaction_set_default(int signo)
{
- zlog_signal(signo, "exiting..."
-#ifdef SA_SIGINFO
- , siginfo, program_counter(context)
-#endif
- );
- _exit(128+signo);
-}
+ struct sigaction act[1] ;
-static void __attribute__ ((noreturn))
-core_handler(int signo
-#ifdef SA_SIGINFO
- , siginfo_t *siginfo, void *context
-#endif
- )
+ memset(act, 0, sizeof(act)) ; /* inter alia, clear sa_flags */
+ act->sa_handler = SIG_DFL ; /* return to default state */
+ sigemptyset(&act->sa_mask) ; /* no extra masking */
+
+ return sigaction(signo, act, NULL) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * When finally aborting, need to turn off the handling of SIGABRT, and need
+ * to make sure that the signal is not blocked.
+ */
+extern void
+quagga_sigabrt_no_trap(void)
{
- zlog_signal(signo, "aborting..."
-#ifdef SA_SIGINFO
- , siginfo, program_counter(context)
-#endif
- );
- zabort_abort();
-}
+ sigset_t set[1] ;
+
+ sigaction_set_default(SIGABRT) ;
+
+ sigemptyset(set) ;
+ sigaddset(set, SIGABRT) ;
+ qpt_thread_sigmask(SIG_UNBLOCK, set, NULL) ;
+ /* sigprocmask() if !qpthreads_enabled */
+} ;
-/* For the signals known to Quagga, and which are in their default state,
- * set a Quagga default handler.
+/*------------------------------------------------------------------------------
+ * Having forked, make sure that all signals are in default state and that
+ * no signals are blocked.
+ *
+ * Expects not to fail.
*/
-static void
-trap_default_signals(void)
+extern void
+quagga_signal_reset(void)
{
- static const int core_signals[] = {
- SIGQUIT,
- SIGILL,
- SIGABRT,
-#ifdef SIGEMT
- SIGEMT,
-#endif
-#ifdef SIGIOT
- SIGIOT,
-#endif
- SIGFPE,
- SIGBUS,
- SIGSEGV,
-#ifdef SIGSYS
- SIGSYS,
-#endif
-#ifdef SIGXCPU
- SIGXCPU,
-#endif
-#ifdef SIGXFSZ
- SIGXFSZ,
-#endif
- };
-
- static const int exit_signals[] = {
- SIGHUP,
- SIGINT,
- SIGALRM,
- SIGTERM,
- SIGUSR1,
- SIGUSR2,
-#ifdef SIGPOLL
- SIGPOLL,
-#endif
-#ifdef SIGVTALRM
- SIGVTALRM,
-#endif
-#ifdef SIGSTKFLT
- SIGSTKFLT,
-#endif
- };
-
- static const int ignore_signals[] = {
- SIGPIPE,
- };
-
- static const struct {
- const int *sigs;
- u_int nsigs;
- void (*handler)(int signo
-#ifdef SA_SIGINFO
- , siginfo_t *info, void *context
-#endif
- );
- } sigmap[] = {
- { core_signals, sizeof(core_signals)/sizeof(core_signals[0]), core_handler},
- { exit_signals, sizeof(exit_signals)/sizeof(exit_signals[0]), exit_handler},
- { ignore_signals, sizeof(ignore_signals)/sizeof(ignore_signals[0]), NULL},
- };
- u_int i;
-
- for (i = 0; i < sizeof(sigmap)/sizeof(sigmap[0]); i++)
+ sigset_t set[1] ;
+ int signo ;
+
+ /* Before changing the handling of any signals, mask everything and
+ * clear out any pending signals.
+ */
+ sigfillset(set) ;
+ sigprocmask(SIG_SETMASK, set, NULL) ;
+
+ while (1)
{
- u_int j;
+ sigpending(set) ;
+ if (sighasmember(set) == 0)
+ break ;
+ sigwait(set, &signo) ;
+ } ;
+
+ /* Set all signals to default handler. */
+ for (signo = sig_min ; signo <= sig_max ; ++signo)
+ {
+ if ((signo == SIGKILL) || (signo == SIGSTOP))
+ continue ;
- for (j = 0; j < sigmap[i].nsigs; j++)
- {
- struct sigaction oact;
- if (sigaction(sigmap[i].sigs[j], NULL, &oact) < 0)
- zlog_warn("Unable to get signal handler for signal %d: %s",
- sigmap[i].sigs[j], errtoa(errno, 0).str);
- else {
-#ifdef SA_SIGINFO
- if (oact.sa_flags & SA_SIGINFO)
- continue ; /* Don't set again */
+ sigaction_set_default(signo) ;
+ } ;
+
+ /* Unmask everything */
+ sigemptyset(set) ;
+ sigprocmask(SIG_SETMASK, set, NULL) ;
+} ;
+
+/*==============================================================================
+ * Functions to install signal handlers.
+ */
+
+/*------------------------------------------------------------------------------
+ * Set given handler for given set of signals. NULL handler => SIG_IGN.
+ *
+ * Returns: < 0 => failed -- value is - failing signo !
+ */
+static int
+signal_set_set(sigset_t* set, sig_handler* handler)
+{
+ int signo ;
+
+ signo = 0 ;
+ for (signo = sig_min ; signo <= sig_max ; ++signo)
+ {
+ int s ;
+ s = sigismember(set, signo) ;
+ if (s < 0)
+ break ;
+ if (s > 0)
+ if (signal_set(signo, handler) < 0)
+ return -signo ;
+ } ;
+
+ return 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set given handler for given signal. NULL handler => SIG_IGN.
+ *
+ * Returns: < 0 => failed
+ */
+#ifndef SA_INTERRUPT
+# define SA_INTERRUPT 0
#endif
- if (oact.sa_handler != SIG_DFL)
- continue ; /* Don't set again */
- }
- if ( (sigaction(sigmap[i].sigs[j], NULL, &oact) == 0) &&
- (oact.sa_handler == SIG_DFL) )
- {
- struct sigaction act;
- sigfillset (&act.sa_mask);
- if (sigmap[i].handler == NULL)
- {
- act.sa_handler = SIG_IGN;
- act.sa_flags = 0;
- }
- else
- {
-#ifdef SA_SIGINFO
- /* Request extra arguments to signal handler. */
- act.sa_sigaction = sigmap[i].handler;
- act.sa_flags = SA_SIGINFO;
-#else
- act.sa_handler = sigmap[i].handler;
- act.sa_flags = 0;
+#ifndef SA_RESTART
+# define SA_RESTART 0
#endif
- }
- if (sigaction(sigmap[i].sigs[j], &act, NULL) < 0)
- zlog_warn("Unable to set signal handler for signal %d: %s",
- sigmap[i].sigs[j], errtoa(errno, 0).str);
- }
- }
+static int
+signal_set(int signo, sig_handler* handler)
+{
+ struct sigaction act[1] ;
+
+ if (handler == NULL)
+ {
+ act->sa_handler = SIG_IGN ;
+ act->sa_flags = 0 ;
}
-}
+ else
+ {
+ act->sa_sigaction = handler ;
+ act->sa_flags = SA_SIGINFO ;
+ } ;
-void
-signal_init (struct thread_master *m, int sigc,
- struct quagga_signal_t signals[])
+ sigfillset (&act->sa_mask) ; /* mask everything */
+
+ if (signo == SIGALRM)
+ act->sa_flags |= SA_INTERRUPT ; /* want SIGALRM to interrupt */
+ else
+ act->sa_flags |= SA_RESTART ; /* all others want restart */
+
+ act->sa_flags |= SA_NOCLDSTOP ;
+
+ return sigaction (signo, act, NULL) ;
+} ;
+
+/*==============================================================================
+ * Additional signal set support.
+ */
+
+/*------------------------------------------------------------------------------
+ * Make a signal set.
+ *
+ * Takes variable list of signal number arguments:
+ *
+ * * ignores zeros
+ *
+ * * stops on first value < 0
+ */
+extern void
+sigmakeset(sigset_t* set, ...)
{
+ va_list va ;
+ int signo ;
- int i = 0;
- struct quagga_signal_t *sig;
+ va_start(va, set) ;
- /* First establish some default handlers that can be overridden by
- the application. */
- trap_default_signals();
+ sigemptyset(set) ;
+ while ((signo = va_arg(va, int)) >= 0)
+ {
+ if (signo != 0)
+ if (sigaddset(set, signo) < 0)
+ zabort("invalid signal number") ;
+ } ;
+
+ va_end(va) ;
+} ;
- while (i < sigc)
+/*------------------------------------------------------------------------------
+ * Copy a signal set.
+ */
+extern void
+sigcopyset(sigset_t* dst, const sigset_t* src)
+{
+ *dst = *src ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Add signal set 'b' into set 'a'.
+ */
+extern void
+sigaddsets(sigset_t* a, const sigset_t* b)
+{
+ int signo ;
+
+ for (signo = sig_min ; signo < SIG_MAX ; ++signo)
{
- sig = &signals[i];
- if ( signal_set (sig->signal) < 0 )
- exit (-1);
- i++;
- }
+ int s ;
+ s = sigismember(b, signo) ;
+ if (s < 0)
+ break ;
+ if (s > 0)
+ sigaddset(a, signo) ;
+ } ;
+} ;
- sigmaster.sigc = sigc;
- sigmaster.signals = signals;
+/*------------------------------------------------------------------------------
+ * Subtract signal set 'b' from set 'a'.
+ */
+extern void
+sigsubsets(sigset_t* a, const sigset_t* b)
+{
+ int signo ;
-#ifdef SIGEVENT_SCHEDULE_THREAD
- sigmaster.t =
- thread_add_timer (m, quagga_signal_timer, &sigmaster,
- QUAGGA_SIGNAL_TIMER_INTERVAL);
-#endif /* SIGEVENT_SCHEDULE_THREAD */
-}
+ for (signo = sig_min ; signo < SIG_MAX ; ++signo)
+ {
+ int s ;
+ s = sigismember(b, signo) ;
+ if (s < 0)
+ break ;
+ if (s > 0)
+ sigdelset(a, signo) ;
+ } ;
+} ;
-/* turn off trap for SIGABRT ! */
-extern void quagga_sigabrt_no_trap(void)
+/*------------------------------------------------------------------------------
+ * Make set 'a' be the inverse of set 'b'
+ */
+extern void
+siginvset(sigset_t* a, const sigset_t* b)
{
- struct sigaction new_act ;
- sigset_t set ;
+ int signo ;
- sigfillset(&set) ;
+ sigfillset(a) ;
- new_act.sa_handler = SIG_DFL ;
- new_act.sa_mask = set ;
- new_act.sa_flags = 0 ;
- sigaction(SIGABRT, &new_act, NULL) ;
+ for (signo = sig_min ; signo < SIG_MAX ; ++signo)
+ {
+ int s ;
+ s = sigismember(b, signo) ;
+ if (s < 0)
+ break ;
+ if (s > 0)
+ sigdelset(a, signo) ;
+ } ;
+} ;
- sigemptyset(&set) ;
- sigaddset(&set, SIGABRT) ;
- sigprocmask(SIG_UNBLOCK, &set, NULL) ;
+/*------------------------------------------------------------------------------
+ * See if there is any intersection between two sets.
+ *
+ * Returns: first signo of intersection -- may be more !
+ * 0 <=> none
+ */
+extern int
+sigincommon(const sigset_t* a, const sigset_t* b)
+{
+ int signo ;
+ for (signo = sig_min ; signo < SIG_MAX ; ++signo)
+ {
+ int s ;
+ s = sigismember(a, signo) ;
+ if (s < 0)
+ return 0 ;
+ if ((s > 0) && (sigismember(b, signo) > 0))
+ return signo ;
+ } ;
+
+ return 0 ;
} ;
+/*------------------------------------------------------------------------------
+ * See if there is anything in the given set.
+ *
+ * Returns: first signo found -- may be more !
+ * 0 <=> none
+ */
+extern int
+sighasmember(const sigset_t* a)
+{
+ int signo ;
+
+ for (signo = sig_min ; signo < SIG_MAX ; ++signo)
+ {
+ int s ;
+ s = sigismember(a, signo) ;
+ if (s < 0)
+ return 0 ;
+ if (s > 0)
+ return signo ;
+ } ;
+
+ return 0 ;
+} ;
diff --git a/lib/sigevent.h b/lib/sigevent.h
index 57486bc2..bc4ef945 100644
--- a/lib/sigevent.h
+++ b/lib/sigevent.h
@@ -25,32 +25,34 @@
#define _QUAGGA_SIGNAL_H
#include <thread.h>
+#include <signal.h>
#define QUAGGA_SIGNAL_TIMER_INTERVAL 2L
#define Q_SIGC(sig) (sizeof(sig)/sizeof(sig[0]))
+typedef void qsig_event(void) ;
+
struct quagga_signal_t
{
- int signal; /* signal number */
- void (*handler) (void); /* handler to call */
-
- volatile sig_atomic_t caught; /* private member */
-};
-
-/* initialise sigevent system
- * takes:
- * - pointer to valid struct thread_master
- * - number of elements in passed in signals array
- * - array of quagga_signal_t's describing signals to handle
- * and handlers to use for each signal
- */
+ int signal ; /* signal number */
+ qsig_event* handler ; /* event function */
+} ;
+
extern void signal_init (struct thread_master *m, int sigc,
- struct quagga_signal_t *signals);
+ struct quagga_signal_t *signals);
-/* check whether there are signals to handle, process any found */
extern int quagga_sigevent_process (void);
-/* turn off trap for SIGABRT ! */
+extern const sigset_t* signal_get_hard_set(void) ;
extern void quagga_sigabrt_no_trap(void) ;
+extern void quagga_signal_reset(void) ;
+
+extern void sigmakeset(sigset_t* set, ...) ;
+extern void sigcopyset(sigset_t* dst, const sigset_t* src) ;
+extern void sigaddsets(sigset_t* a, const sigset_t* b) ;
+extern void sigsubsets(sigset_t* a, const sigset_t* b) ;
+extern void siginvset(sigset_t* a, const sigset_t* b) ;
+extern int sigincommon(const sigset_t* a, const sigset_t* b) ;
+extern int sighasmember(const sigset_t* a) ;
#endif /* _QUAGGA_SIGNAL_H */
diff --git a/lib/sockopt.c b/lib/sockopt.c
index a8fae4f1..aa747429 100644
--- a/lib/sockopt.c
+++ b/lib/sockopt.c
@@ -641,8 +641,13 @@ sockopt_tcp_signature (int sock_fd, union sockunion *su, const char *password)
su2->sin6.sin6_family = AF_INET6;
/* V4Map the address */
memset (&su2->sin6.sin6_addr, 0, sizeof (struct in6_addr));
- su2->sin6.sin6_addr.s6_addr32[2] = htonl(0xffff);
- memcpy (&su2->sin6.sin6_addr.s6_addr32[3], &su->sin.sin_addr, 4);
+ su2->sin6.sin6_addr.s6_addr[10] = 0xff ;
+ su2->sin6.sin6_addr.s6_addr[11] = 0xff ;
+# ifdef s6_addr32
+ su2->sin6.sin6_addr.s6_addr32[3] = su->sin.sin_addr.s_addr ;
+# else
+ memcpy (&su2->sin6.sin6_addr.s6_addr[12], &su->sin.sin_addr, 4);
+# endif
}
# endif
}
diff --git a/lib/sockunion.c b/lib/sockunion.c
index e08a3745..deac292c 100644
--- a/lib/sockunion.c
+++ b/lib/sockunion.c
@@ -20,6 +20,7 @@
*/
#include <zebra.h>
+#include "misc.h"
#include "prefix.h"
#include "vty.h"
diff --git a/lib/stream.c b/lib/stream.c
index 1fce7103..0183ce78 100644
--- a/lib/stream.c
+++ b/lib/stream.c
@@ -20,8 +20,8 @@
* 02111-1307, USA.
*/
+#include "zebra.h"
#include <stddef.h>
-#include <zebra.h>
#include "stream.h"
#include "memory.h"
diff --git a/lib/symtab.c b/lib/symtab.c
index 57a49396..cac52962 100644
--- a/lib/symtab.c
+++ b/lib/symtab.c
@@ -212,11 +212,11 @@ symbol_base(symbol_table table, u_int32_t hash)
* any existing chain base array, symbols and symbol references are simply
* discarded -- which will leak memory and is probably a mistake.
*/
-symbol_table
+extern symbol_table
symbol_table_init_new(symbol_table table,
- void* parent,
- unsigned int base_count,
- unsigned int density,
+ void* parent,
+ uint base_count,
+ uint density,
symbol_hash_function* hash_function,
symbol_call_back_function* value_call_back)
{
@@ -240,21 +240,21 @@ symbol_table_init_new(symbol_table table,
} ;
/* Set "parent" of symbol table. */
-void
+extern void
symbol_table_set_parent(symbol_table table, void* parent)
{
table->parent = parent ;
} ;
/* Get "parent" of symbol table. */
-void*
+extern void*
symbol_table_get_parent(symbol_table table)
{
return table->parent ;
} ;
/* Set the value_call_back */
-void
+extern void
symbol_table_set_value_call_back(symbol_table table,
symbol_call_back_function* value_call_back)
{
@@ -311,8 +311,8 @@ symbol_table_setup(symbol_table table)
*
* NB: must only be done when the table is empty -- see assertion !
*/
-symbol_table
-symbol_table_reset(symbol_table table, int free_structure)
+extern symbol_table
+symbol_table_reset(symbol_table table, free_keep_b free_structure)
{
if (table== NULL)
return NULL ; /* allow for already freed table */
@@ -322,6 +322,8 @@ symbol_table_reset(symbol_table table, int free_structure)
if (table->bases)
XFREE(MTYPE_SYMBOL_BASES, table->bases);
+ confirm(free_it == true) ;
+
if (free_structure)
{
XFREE(MTYPE_VECTOR, table) ;
@@ -416,8 +418,8 @@ symbol_free(symbol sym)
* NB: it is the caller's responsibility to unset all references and release
* any that need to be released -- either before or after this operation.
*/
-void*
-symbol_table_ream(symbol_table table, int free_structure)
+extern void*
+symbol_table_ream(symbol_table table, free_keep_b free_structure)
{
void* value ;
symbol sym ;
@@ -470,8 +472,8 @@ symbol_table_ream(symbol_table table, int free_structure)
* value and no references. Where that distinction matters, it is
* necessary to do an extra lookup.
*/
-symbol
-symbol_lookup(symbol_table table, const void* name, int add)
+extern symbol
+symbol_lookup(symbol_table table, const void* name, add_b add)
{
struct symbol* this ;
struct symbol** base ;
@@ -485,7 +487,7 @@ symbol_lookup(symbol_table table, const void* name, int add)
base = symbol_base(table, hash.hash) ;
this = *base ;
- while (this)
+ while (this != NULL)
{
if ((this->hash == hash.hash)
&& (this->name_len == hash.name_len)
@@ -554,7 +556,7 @@ symbol_lookup(symbol_table table, const void* name, int add)
* NB: orphan symbols can be deleted. The effect is to free the symbol if
* possible.
*/
-void*
+extern void*
symbol_delete(symbol sym)
{
void* old_value = symbol_unset_value(sym) ;
@@ -578,7 +580,7 @@ symbol_delete(symbol sym)
static u_int32_t crc_table[] ;
/* Standard symbol string hash function. */
-void
+extern void
symbol_hash_string(symbol_hash p_hash, const char* string) {
u_int32_t h = 0 ;
const char* p = string ;
@@ -595,7 +597,7 @@ symbol_hash_string(symbol_hash p_hash, const char* string) {
} ;
/* Standard symbol byte vector hash function. */
-void
+extern void
symbol_hash_bytes(symbol_hash p_hash, const void* bytes, size_t len) {
assert(len < 0xFFFF) ;
@@ -670,8 +672,8 @@ symbol_extend_bases(symbol_table table)
/* Zeroise the reference count.*/
-symbol
-symbol_zero_ref(symbol sym, int force)
+Private symbol
+symbol_zero_ref(symbol sym, bool force)
{
assert((sym->ref_count == 1) || force) ;
@@ -807,7 +809,7 @@ symbol_ref_is_bookmark(symbol_ref ref)
} ;
/* Start walk of symbol references */
-void
+extern void
symbol_ref_walk_start(symbol sym, symbol_ref walk)
{
symbol_init_ref(walk) ; /* keeping things tidy */
@@ -817,7 +819,7 @@ symbol_ref_walk_start(symbol sym, symbol_ref walk)
} ;
/* Step walk and return the next reference (if any). */
-symbol_ref
+extern symbol_ref
symbol_ref_walk_step(symbol_ref walk)
{
symbol_ref next_ref ;
@@ -850,7 +852,7 @@ symbol_ref_walk_step(symbol_ref walk)
* NB: if the symbol is not defined and has no references or bookmarks it
* will now be freed.
*/
-void
+extern void
symbol_ref_walk_end(symbol_ref walk)
{
assert(symbol_ref_is_bookmark(walk)) ; /* must be a bookmark ! */
@@ -876,7 +878,7 @@ symbol_ref_walk_end(symbol_ref walk)
*
* Returns previous value -- which may require releasing.
*/
-void*
+extern void*
symbol_set_value(symbol sym, void* new_value)
{
void* old_value ;
@@ -912,7 +914,7 @@ symbol_set_value(symbol sym, void* new_value)
*/
/* Initialise symbol reference -- allocate if required. */
-symbol_ref
+extern symbol_ref
symbol_init_ref(symbol_ref ref)
{
if (ref == NULL)
@@ -936,7 +938,7 @@ symbol_init_ref(symbol_ref ref)
*
* if reference is not allocated, the parent and tag are unchanged.
*/
-symbol_ref
+extern symbol_ref
symbol_set_ref(symbol_ref ref, struct symbol* sym)
{
if (ref != NULL)
@@ -944,7 +946,7 @@ symbol_set_ref(symbol_ref ref, struct symbol* sym)
if (ref->sym == sym)
return ref ; /* Nothing more to do if already set to given value */
if (ref->sym != NULL)
- symbol_unset_ref_keep(ref) ;
+ symbol_unset_ref(ref, keep_it) ;
}
else
ref = symbol_init_ref(NULL) ;
@@ -967,8 +969,8 @@ symbol_set_ref(symbol_ref ref, struct symbol* sym)
*
* NB: copes if the reference is already unset, of course.
*/
-symbol_ref
-symbol_unset_ref(symbol_ref ref, int free_ref_structure)
+extern symbol_ref
+symbol_unset_ref(symbol_ref ref, free_keep_b free_ref_structure)
{
if (ref == NULL) return ref ;
@@ -979,6 +981,7 @@ symbol_unset_ref(symbol_ref ref, int free_ref_structure)
ref->sym = NULL ;
} ;
+ confirm(free_it == true) ;
if (free_ref_structure)
XFREE(MTYPE_SYMBOL_REF, ref) ; /* ref is set to NULL */
@@ -1008,7 +1011,7 @@ symbol_unset_ref(symbol_ref ref, int free_ref_structure)
* progress -- up to and including deleting it. Any other changes to
* the table must NOT be attempted.
*/
-void
+extern void
symbol_walk_start(symbol_table table, struct symbol_walker* walker)
{
walker->next = NULL ;
@@ -1016,7 +1019,7 @@ symbol_walk_start(symbol_table table, struct symbol_walker* walker)
walker->base_count = table->base_count ;
} ;
-symbol
+extern symbol
symbol_walk_next(struct symbol_walker* walker)
{
symbol this = walker->next ;
@@ -1049,9 +1052,9 @@ symbol_walk_next(struct symbol_walker* walker)
* caller's responsibility to avoid deleting any symbol whose pointer
* in the vector they expect to rely on !
*/
-vector
+extern vector
symbol_table_extract(symbol_table table,
- symbol_select_cmp* selector, const void* p_val, int most,
+ symbol_select_cmp* selector, const void* p_val, bool most,
symbol_sort_cmp* sort)
{
vector extract ;
@@ -1093,7 +1096,7 @@ symbol_table_extract(symbol_table table,
*
* This comparison treats substrings of digits as numbers, so "a10" is > "a1".
*/
-int
+extern int
symbol_mixed_name_cmp(const symbol* p_a,
const symbol* p_b)
{
diff --git a/lib/symtab.h b/lib/symtab.h
index a8a6e622..008b7853 100644
--- a/lib/symtab.h
+++ b/lib/symtab.h
@@ -22,14 +22,8 @@
#ifndef _ZEBRA_SYMTAB_H
#define _ZEBRA_SYMTAB_H
+#include "misc.h"
#include "vector.h"
-#include <stddef.h>
-#include <stdint.h>
-
-/* Macro in case there are particular compiler issues. */
-#ifndef Inline
- #define Inline static inline
-#endif
/* Maximum number of symbol table bases -- something has gone tragically wrong
* if we hit this. Assume can multiply this by 2 and get valid size_t result.
@@ -145,21 +139,18 @@ struct symbol_walker
/* Symbol Table Operations. */
-extern symbol_table
-symbol_table_init_new(symbol_table table,
- void* parent,
- unsigned bases,
- unsigned density,
- symbol_hash_function* hash_function,
- symbol_call_back_function* value_call_back) ;
-void symbol_table_set_parent(symbol_table table, void* parent) ;
-void* symbol_table_get_parent(symbol_table table) ;
-void* symbol_table_ream(symbol_table table, int free_structure) ;
-#define symbol_table_ream_free(table) symbol_table_ream(table, 1)
-#define symbol_table_ream_keep(table) symbol_table_ream(table, 0)
-symbol_table symbol_table_reset(symbol_table table, int free_structure) ;
-#define symbol_table_reset_free(table) symbol_table_reset(table, 1)
-#define symbol_table_reset_keep(table) symbol_table_reset(table, 0)
+extern symbol_table symbol_table_init_new(
+ symbol_table table,
+ void* parent,
+ uint bases,
+ uint density,
+ symbol_hash_function* hash_function,
+ symbol_call_back_function* value_call_back) ;
+extern void symbol_table_set_parent(symbol_table table, void* parent) ;
+extern void* symbol_table_get_parent(symbol_table table) ;
+extern void* symbol_table_ream(symbol_table table, free_keep_b free_structure) ;
+extern symbol_table symbol_table_reset(symbol_table table,
+ free_keep_b free_structure) ;
extern void symbol_hash_string(struct symbol_hash* p_hash, const char* string) ;
extern void symbol_hash_bytes(struct symbol_hash* p_hash, const void* bytes,
@@ -169,28 +160,31 @@ extern void symbol_table_set_value_call_back(symbol_table table,
extern void symbol_table_free(symbol_table) ;
-extern symbol symbol_lookup(symbol_table table, const void* name, int add) ;
-
-#define symbol_seek(table, name) symbol_lookup(table, name, 0)
-#define symbol_find(table, name) symbol_lookup(table, name, 1)
+extern symbol symbol_lookup(symbol_table table, const void* name, add_b add) ;
extern void* symbol_delete(symbol sym) ;
extern void* symbol_set_value(symbol sym, void* new_value) ;
-#define symbol_unset_value(sym) symbol_set_value(sym, NULL)
+Inline void*
+symbol_unset_value(symbol sym)
+{
+ return symbol_set_value(sym, NULL) ;
+} ;
-void symbol_ref_walk_start(symbol sym, symbol_ref walk) ;
-symbol_ref symbol_ref_walk_step(symbol_ref walk) ;
-void symbol_ref_walk_end(symbol_ref walk) ;
+extern void symbol_ref_walk_start(symbol sym, symbol_ref walk) ;
+extern symbol_ref symbol_ref_walk_step(symbol_ref walk) ;
+extern void symbol_ref_walk_end(symbol_ref walk) ;
-void symbol_walk_start(symbol_table table, struct symbol_walker* walker) ;
-symbol symbol_walk_next(struct symbol_walker* walker) ;
+extern void symbol_walk_start(symbol_table table, struct symbol_walker* walker);
+extern symbol symbol_walk_next(struct symbol_walker* walker) ;
typedef int symbol_select_cmp(const symbol, const void*) ;
typedef int symbol_sort_cmp(const symbol*, const symbol*) ;
-vector symbol_table_extract(symbol_table table,
- symbol_select_cmp* select, const void* p_value,
- int most, symbol_sort_cmp* sort) ;
+extern vector symbol_table_extract(symbol_table table,
+ symbol_select_cmp* select,
+ const void* p_value,
+ bool most,
+ symbol_sort_cmp* sort) ;
extern symbol_sort_cmp symbol_mixed_name_cmp ;
@@ -226,29 +220,22 @@ symbol_inc_ref(symbol sym)
return sym ;
} ;
-extern symbol symbol_zero_ref(symbol sym, int force) ;
+Private symbol symbol_zero_ref(symbol sym, bool force) ;
Inline symbol
symbol_dec_ref(symbol sym)
{
if (sym->ref_count <= 1)
- return symbol_zero_ref(sym, 0) ;
+ return symbol_zero_ref(sym, false) ;
--sym->ref_count ;
return sym ;
} ;
-extern symbol_ref
-symbol_init_ref(symbol_ref ref) ;
-
-extern symbol_ref
-symbol_set_ref(symbol_ref ref, symbol sym) ;
-
-extern symbol_ref
-symbol_unset_ref(symbol_ref ref, int free_ref_structure) ;
-
-#define symbol_unset_ref_free(ref) symbol_unset_ref(ref, 1) ;
-#define symbol_unset_ref_keep(ref) symbol_unset_ref(ref, 0) ;
+extern symbol_ref symbol_init_ref(symbol_ref ref) ;
+extern symbol_ref symbol_set_ref(symbol_ref ref, symbol sym) ;
+extern symbol_ref symbol_unset_ref(symbol_ref ref,
+ free_keep_b free_ref_structure) ;
/* Access functions -- argument is address of symbol_ref. */
/* These cope if address of symbol_ref is null, or reference is undefined. */
diff --git a/lib/temp.c b/lib/temp.c
new file mode 100644
index 00000000..759c2bb3
--- /dev/null
+++ b/lib/temp.c
@@ -0,0 +1,1349 @@
+/* Generic vector interface routine -- functions
+ * Copyright (C) 1997 Kunihiro Ishiguro
+ *
+ * 24-Nov-2009 -- extended to add a number of new operations on vectors.
+ * 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.
+ */
+
+#include <zebra.h>
+
+#include "vector.h"
+#include "memory.h"
+
+/* Vectors are implemented as a structure which points to an array of pointers
+ * to vector items. That array -- the body of the vector -- can change size,
+ * and therefore may move around in memory.
+ *
+ * The vector structure may be statically allocated, embedded in another
+ * structure, or allocated dynamically. In any case the vector operations
+ * require the address of the vector structure -- see typedef for vector.
+ *
+ * Vector items are accessed by index, fetching and storing pointers to
+ * those items. Can also push and pop items.
+ *
+ * A vector has a known (logical) end position. Everything beyond the end is
+ * defined to be NULL. When a vector is initialised it is set empty.
+ *
+ * At any given moment the vector body has a limit on the number of items it
+ * can accommodate (the physical end).
+ *
+ * A vector will grow to accommodate what is put into it. Adding items beyond
+ * the (logical) end moves it. Adding items beyond the (physical) limit causes
+ * the body to be extended to suit, and to leave some spare space for future
+ * expansion.
+ *
+ * While the vector is small (see VECTOR_LIMIT_DOUBLE_MAX) the body will grow by
+ * doubling in size. When it is larger, it grows to be multiples of
+ * VECTOR_LIMIT_DOUBLE_MAX.
+ *
+ * Deleting items reduces the (logical) end position, but does NOT release
+ * memory -- the (physical) limit is not changed.
+ *
+ * To release memory: vector_chop will release everything beyond the current
+ * end; vector_decant will create a new body of exactly the current size,
+ * releasing the old body; vector_discard will release everything beyond a
+ * given position.
+ *
+ * NB: you can set a vector item to be NULL. If you set a vector item beyond
+ * the current end, NULL items are inserted in the vector.
+ *
+ * NB: when setting a vector item it is the caller's responsibility to
+ * deallocate any pre-existing value of the item.
+ *
+ * NB: when deleting items it is also the caller's responsibility to deallocate
+ * any values that require it.
+ *
+ * Implementation Notes
+ *
+ * Everything beyond the (logical) end is implicitly NULL.
+ *
+ * Actual memory between (logical) end and (physical) limit is UNDEFINED. So
+ * when advancing the end some care has to be taken to ensure that any new
+ * items in the vector are either set to something or cleared to NULL.
+ *
+ * It would have been possible to ensure that everything between end and limit
+ * is cleared to NULL, but that is more work -- in particular it creates work
+ * when it is not always required.
+ */
+
+#define P_ITEMS_SIZE(n) SIZE(p_vector_item, n)
+
+/*==============================================================================
+ * Initialisation, allocation, reset etc.
+ */
+
+/*------------------------------------------------------------------------------
+ * Initialise a brand new vector, setting it empty.
+ *
+ * Allocates vector structure if none given -- that is, if v == NULL.
+ *
+ * If size is given as zero, no body is allocated, otherwise body of exactly
+ * the required size is allocated.
+ *
+ * NB: discards any existing vector body -- so it is the caller's responsibility
+ * to release any existing body, and any items in that body.
+ */
+extern vector
+vector_init_new(vector v, vector_length_t limit)
+{
+ if (v == NULL)
+ v = XCALLOC(MTYPE_VECTOR, sizeof(struct vector)) ;
+ else
+ memset(v, 0, sizeof(struct vector)) ;
+
+ if (limit != 0)
+ {
+ v->p_items = XMALLOC(MTYPE_VECTOR_BODY, P_ITEMS_SIZE(limit)) ;
+ v->limit = limit ;
+ } ;
+
+ return v ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Initialize vector : allocate memory and return vector.
+ * allocates body with at least 1 entry.
+ *
+ * This is a "legacy" function.
+ */
+extern vector
+vector_init (vector_length_t limit)
+{
+ return vector_init_new(NULL, limit ? limit : 1) ; /* at least 1 entry */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Basic: free the vector body and the vector structure.
+ *
+ * NB: it is the caller's responsibility to release any vector item values
+ * *before* doing this.
+ */
+void
+vector_free (vector v)
+{
+ XFREE (MTYPE_VECTOR_BODY, v->p_items);
+ XFREE (MTYPE_VECTOR, v);
+} ;
+
+/*------------------------------------------------------------------------------
+ * Re-initialise vector (or create new one), setting it empty.
+ *
+ * Allocates vector structure if none given -- that is, if v == NULL.
+ *
+ * If size is given as zero, no body is allocated, but any existing body is
+ * retained. (vector_reset() will discard body.)
+ *
+ * Otherwise ensures existing body is at least the required size, or a body
+ * of exactly the required size is allocated.
+ *
+ * NB: when re-initialising an existing vector it is the caller's responsibility
+ * to release any vector item values *before* doing this.
+ * */
+extern vector
+vector_re_init(vector v, vector_length_t)
+{
+ if ((v == NULL) || (v->p_items == NULL))
+ return vector_init_new(v, limit) ;
+
+ v->end = 0 ;
+
+ if (v->limit < size)
+ {
+ v->p_items = XREALLOC(MTYPE_VECTOR_BODY, v->p_items,
+ P_ITEMS_SIZE(limit)) ;
+ v->limit = limit ;
+ } ;
+
+ return v ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Free the vector body, and (if required) free the vector structure.
+ *
+ * Return NULL if releases vector, otherwise the address of the vector.
+ *
+ * NB: it is the caller's responsibility to release any vector item values
+ * *before* doing this.
+ */
+vector
+vector_reset(vector v, free_keep_b free_structure)
+{
+ if (v == NULL)
+ return NULL ; /* allow for already freed vector */
+
+ if (v->p_items != NULL)
+ XFREE(MTYPE_VECTOR_BODY, v->p_items) ;
+
+ if (free_structure)
+ {
+ confirm(free_it == true) ;
+ XFREE(MTYPE_VECTOR, v) ;
+ return NULL ;
+ }
+ else
+ return vector_init_new(v, 0) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set vector length to be (at least) the given fixed length.
+ *
+ * There must be a vector.
+ *
+ * Does nothing if the vector is already as long or longer than the given
+ * length.
+ *
+ * If the body is not big enough for the new length, allocates or extends to
+ * exactly the new length. Otherwise, leaves body as it is.
+ *
+ * Appends NULLs as required to extend to the required length.
+ *
+ * Note that the existing contents of the vector are preserved in all cases.
+ */
+Private void
+vector_set_new_min_length(vector v, vector_length_t len)
+{
+ assert (v != NULL) ;
+
+ if (len > v->limit)
+ {
+ if (v->p_items == NULL)
+ v->p_items = XMALLOC(MTYPE_VECTOR_BODY, P_ITEMS_SIZE(len)) ;
+ else
+ v->p_items = XREALLOC(MTYPE_VECTOR_BODY, v->p_items,
+ P_ITEMS_SIZE(len)) ;
+ v->limit = len ;
+ } ;
+
+ if (v->end < len)
+ vector_extend(v, len) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Pop item from vector, stepping past any NULLs.
+ * If vector is empty, free the body and, if required, the vector structure.
+ *
+ * Useful for emptying out and discarding a vector:
+ *
+ * while ((p_v = vector_ream_out(v, 1)))
+ * ... do what's required to release the item p_v
+ *
+ * Returns NULL if vector was empty and has now been freed as required.
+ */
+p_vector_item
+vector_ream(vector v, free_keep_b free_structure)
+{
+ p_vector_item p_v ;
+
+ if (v == NULL)
+ return NULL ;
+
+ while (v->end != 0)
+ {
+ p_v = v->p_items[--v->end] ;
+ if (p_v != NULL)
+ return p_v ; /* return non-NULL item */
+ } ;
+
+ /* vector is empty: free the body, and (if required) the vector structure. */
+ vector_reset(v, free_structure) ;
+
+ return NULL ; /* signals end */
+} ;
+
+/*==============================================================================
+ * Unset item, condensing and trimming vector.
+ *
+ * These are legacy operations.
+ */
+
+/*------------------------------------------------------------------------------
+ * Unset item at given index (ie set it NULL).
+ *
+ * Return the old value of the item.
+ *
+ * If the item at the current (logical) end of the vector is NULL, move the
+ * end backwards until finds a non-NULL item, or the vector becomes empty.
+ */
+extern p_vector_item
+vector_unset_item(vector v, vector_index_t i)
+{
+ p_vector_item was ;
+
+ if (i < v->end)
+ {
+ was = v->p_items[i] ;
+ v->p_items[i] = NULL ;
+ }
+ else if (v->end == 0)
+ return NULL ; /* avoid test for last entry NULL if is empty */
+ else
+ was = NULL ;
+
+ if (v->p_items[v->end - 1] == NULL)
+ vector_trim(v) ;
+
+ return was ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Trim any NULL entries at the current (logical) end of the vector.
+ *
+ * Returns the (new) length (end) of the vector.
+ */
+extern vector_length_t
+vector_trim(vector v)
+{
+ vector_length_t e = v->end ;
+ while ((e > 0) && (v->p_items[e - 1] == NULL))
+ --e ;
+ v->end = e ;
+ return e ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Removes any NULL entries from the given vector.
+ *
+ * Returns the (new) length (end) of the vector.
+ */
+extern vector_length_t
+vector_condense(vector v)
+{
+ vector_length_t e = 0 ;
+ vector_index_t j ;
+
+ /* Find first NULL, if any */
+ while ((e < v->end) && (v->p_items[e] != NULL))
+ ++e ;
+
+ /* Quit if no NULLs (or vector is empty) */
+ if (e == v->end)
+ return e ;
+
+ /* Shuffle any remaining non-NULL down */
+ for (j = e + 1 ; j < v->end ; ++j)
+ if (v->p_items[j] != NULL)
+ v->p_items[e++] = v->p_items[j] ;
+
+ v->end = e ;
+
+ return e ;
+} ;
+
+/*==============================================================================
+ * Inserting and deleting items.
+ */
+
+/*------------------------------------------------------------------------------
+ * Insert item in vector, before item at given position
+ * Move items and extend vector as required.
+ */
+extern void
+vector_insert_item(vector v, vector_index_t i, void* p_v)
+{
+ if ((i == v->end) && (i < v->limit))
+ ++v->end ;
+ else
+ vector_insert(v, i, 1) ;
+
+ v->p_items[i] = (p_vector_item)p_v ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Insert item in vector at given position with rider:
+ *
+ * rider < 0 -- insert before the item at the given position
+ * rider == 0 -- insert at the given position -- REPLACING any existing value
+ * rider > 0 -- insert after the item at the given position
+ *
+ * NB: when an item is replaced, it is the caller's responsibility to release
+ * any memory used by the item, if required.
+ *
+ * Move items and extend vector as required.
+ */
+extern void
+vector_insert_item_here(vector v, vector_index_t i, int rider,
+ p_vector_item p_v)
+{
+ if (rider == 0)
+ return vector_set_item(v, i, p_v) ;
+
+ if (rider > 0)
+ ++i ; /* insert before next item */
+ vector_insert_item(v, i, p_v) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Delete item from vector.
+ *
+ * Move items as required. Reduces number of items in the vector (unless
+ * the item in question is beyond the end of the vector !)
+ *
+ * Returns: the item that has just been deleted.
+ *
+ * NB: it is the caller's responsibility to release memory used by any
+ * current value of the item, if required.
+ *
+ * NB: does NOT change the size of the vector body.
+ */
+extern p_vector_item
+vector_delete_item(vector v, vector_index_t i)
+{
+ p_vector_item p_e ;
+ if (i < v->end)
+ {
+ p_e = v->p_items[i] ; /* pick up the current value */
+ if (i != (v->end - 1))
+ vector_delete(v, i, 1) ;
+ else
+ v->end = i ;
+ return p_e ;
+ }
+ else
+ return NULL ;
+} ;
+
+/*==============================================================================
+ * Moving items within vector.
+ */
+
+/*------------------------------------------------------------------------------
+ * Move item in vector from source position to destination position.
+ *
+ * Moves intervening items up or down as required.
+ *
+ * Extends vector to include the destination, if required.
+ *
+ * A source item beyond the end of the vector is implicitly NULL.
+ */
+extern void
+vector_move_item(vector v, vector_index_t i_dst, vector_index_t i_src)
+{
+ p_vector_item* pp_s ;
+ p_vector_item* pp_d ;
+ p_vector_item p_e ;
+
+ vector_length_t old_end = v->end ;
+
+ /* Worry about whether both source and destination exist. */
+ if (i_dst >= old_end)
+ {
+ vector_insert(v, i_dst, 1) ; /* ensure destination exists */
+ if (i_src >= old_end)
+ return ; /* both were beyond the end */
+ }
+ else if (i_src >= old_end)
+ {
+ i_src = old_end ; /* clamp to just beyond last */
+ vector_insert(v, i_src, 1) ; /* create empty entry */
+ } ;
+
+ if (i_dst == i_src) /* avoid work and edge case */
+ return ;
+
+ /* Both src and dst are within the vector and src != dst */
+ pp_s = &v->p_items[i_src] ; /* address of src entry */
+ pp_d = &v->p_items[i_dst] ; /* address of dst entry */
+ p_e = *pp_s ; /* pick up item to move */
+ if (i_src < i_dst)
+ memmove(pp_s, pp_s+1, P_ITEMS_SIZE(i_dst - i_src)) ;
+ else
+ memmove(pp_d+1, pp_d, P_ITEMS_SIZE(i_src - i_dst)) ;
+ *pp_d = p_e ; /* put down the item to move */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Move item in vector to given position with rider:
+ *
+ * rider < 0 -- move to before the item at the given position
+ * rider == 0 -- move to replace item at the given position
+ * rider > 0 -- insert after the item at the given position
+ *
+ * NB: it is the caller's responsibility to release the any existing value
+ * that will be replaced.
+ *
+ * Move items and extend vector as required.
+ */
+extern void
+vector_move_item_here(vector v, vector_index_t i_dst, int rider,
+ vector_index_t i_src)
+{
+ if (rider != 0)
+ {
+ if (rider > 0)
+ ++i_dst ;
+ vector_move_item(v, i_dst, i_src) ;
+ }
+ else
+ {
+ /* to replace: copy and then delete. */
+ vector_set_item(v, i_dst, vector_get_item(v, i_src)) ;
+ vector_delete_item(v, i_src) ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Reverse vector: reverse the order of items in the vector.
+ */
+extern void
+vector_reverse(vector v)
+{
+ if (v != NULL)
+ vector_part_reverse(v, 0, v->end) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Reverse portion of vector.
+ */
+extern void
+vector_part_reverse(vector v, vector_index_t i, vector_length_t n)
+{
+ vector_index_t j ;
+
+ if (v == NULL)
+ return ;
+
+ if ((i + n) > v->limit)
+ vector_extend(v, i + n) ; /* ensure portion exists */
+
+ if (n <= 1)
+ return ;
+
+ j = i + n - 1 ; /* j > i, because n > 1 */
+ do
+ {
+ p_vector_item p_i = v->p_items[i] ;
+ v->p_items[i++] = v->p_items[j] ;
+ v->p_items[j--] = p_i ;
+ } while (j > i) ;
+} ;
+
+/*==============================================================================
+ * Copying, moving and appending entire vectors.
+ */
+
+static void vector_new_limit(vector v, vector_length_t new_end) ;
+
+/*------------------------------------------------------------------------------
+ * Shallow vector copy -- copies pointers to item values, not the values.
+ *
+ * Creates a new vector.
+ *
+ * NB: creates new vector with same limit as existing one, but copies only
+ * the known items (ie up to end, not up to limit).
+ */
+vector
+vector_copy (vector v)
+{
+ vector new = vector_init_new(NULL, v->limit) ;
+
+ new->end = v->end;
+
+ if (v->limit > 0)
+ memcpy(new->p_items, v->p_items, P_ITEMS_SIZE(v->end)) ;
+
+ return new;
+}
+
+/*------------------------------------------------------------------------------
+ * Shallow vector copy -- copies pointers to item values, not the values.
+ * Creates a new vector or re-initialises an existing one.
+ *
+ * NB: creates new vector with same limit as existing one, but copies only
+ * the known items (ie up to end, not up to limit).
+ *
+ * NB: if re-initialising existing vector, it is the caller's responsibility
+ * to release any existing items if that is required.
+ *
+ * NB: if re-initialising existing vector, it is the caller's responsibility
+ * to ensure the vector structure is currently valid.
+ *
+ * NB: do NOT try copying a vector to itself !!
+ */
+vector
+vector_copy_here(vector dst, vector src)
+{
+ assert((src != NULL) && (src != dst)) ;
+
+ dst = vector_re_init(dst, src->limit) ;
+
+ dst->end = src->end;
+
+ if (src->end > 0)
+ memcpy(dst->p_items, src->p_items, P_ITEMS_SIZE(src->end)) ;
+
+ return dst ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Vector move -- moves body of vector.
+ *
+ * Creates a new vector or re-initialises an existing one.
+ * Leaves the source vector empty -- does not release the structure.
+ *
+ * NB: if re-initialising existing vector, it is the caller's responsibility
+ * to release any existing items if that is required.
+ *
+ * NB: if re-initialising existing vector, it is the caller's responsibility
+ * to ensure the vector structure is currently valid.
+ *
+ * NB: do NOT try moving a vector to itself !!
+ */
+extern vector
+vector_move_here(vector dst, vector src)
+{
+ assert((src != NULL) && (src != dst)) ;
+
+ if (dst != NULL)
+ dst = vector_reset(dst, 0) ; /* Reset to deallocate any existing body */
+ else
+ dst = vector_init_new(dst, 0) ; /* Create new structure sans body. */
+
+ *dst = *src ; /* Copy the vector structure */
+
+ vector_init_new(src, 0) ; /* Set empty, forgetting body */
+
+ return dst ;
+}
+
+/*------------------------------------------------------------------------------
+ * Shallow vector copy append -- copies pointers to item values, not the values.
+ *
+ * Appends copied pointers to the destination vector.
+ *
+ * Creates a new destination vector if required.
+ *
+ * NB: Can append to self.
+ */
+extern vector
+vector_copy_append(vector dst, vector src)
+{
+ vector_index_t new_end ;
+
+ assert(src != NULL) ;
+
+ if (dst != NULL)
+ {
+ new_end = dst->end + src->end ;
+ if (new_end > dst->limit)
+ vector_new_limit(dst, new_end) ;
+ }
+ else
+ {
+ new_end = src->end ;
+ vector_init_new(dst, new_end) ;
+ } ;
+
+ if (src->end)
+ memcpy(&dst->p_items[dst->end], src->p_items, P_ITEMS_SIZE(src->end)) ;
+
+ dst->end = new_end ; /* Done last, allows for append to self ! */
+ return dst ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Vector move append -- moves pointers to item values.
+ *
+ * Appends moved pointers to the destination vector.
+ *
+ * Creates a new destination vector if required (dst == NULL).
+ *
+ * Leaves the source vector empty -- does not release the structure.
+ *
+ * NB: do NOT try moving a vector to itself !!
+ */
+extern vector
+vector_move_append(vector dst, vector src)
+{
+ assert((src != NULL) && (src != dst)) ;
+
+ if ((dst == NULL) || (dst->end == 0))
+ return vector_move_here(dst, src) ; /* Easy way to do it if dst empty */
+
+ vector_copy_append(dst, src) ; /* Extend dst and copy src */
+ vector_init_new(src, 0) ; /* Set empty, forgetting body */
+
+ return dst ;
+} ;
+
+/*==============================================================================
+ * Portmanteau splice/extract/replace function.
+ *
+ * All take a portion of 'src' vector and:
+ *
+ * splice:
+ *
+ * a) replace a portion of the 'dst' vector by a portion of the 'src' vector
+ * copying the replaced portion of the 'dst' vector to the 'to' vector
+ * b) either: leave 'src' unchanged -- copy
+ * or: remove the stuff copied from 'src' -- move
+ *
+ * Arguments: to_copy -- true
+ * to -- vector, or NULL => allocate new vector
+ * dst -- vector
+ * i_dst -- start of portion to replace
+ * n_dst -- length of portion to replace. 0 => insertion.
+ * src -- vector, or NULL => nothing to copy/move
+ * i_src -- start of portion to copy/move
+ * n_src -- length of portion to copy/move. 0 => nothing.
+ * src_move -- true => move, otherwise copy.
+ *
+ * Returns: the (possibly new) 'to' vector
+ *
+ * NB: 'to', 'dst' and 'src' must be distinct vectors.
+ *
+ * extract:
+ *
+ * a) copy a portion of the 'src' vector to the 'to' vector
+ * c) either: leave 'src' unchanged -- copy
+ * or: remove the stuff copied from 'src' -- move
+ *
+ * Arguments: to_copy -- true
+ * to -- vector, or NULL => allocate new vector
+ * dst -- NULL
+ * i_dst -- ignored
+ * n_dst -- ignored
+ * src -- vector, or NULL => nothing to copy/move
+ * i_src -- start of portion to copy/move
+ * n_src -- length of portion to copy/move. 0 => nothing.
+ * src_move -- true => move, otherwise copy.
+ *
+ * Returns: the (possibly new) 'to' vector
+ *
+ * NB: 'to' and 'src' must be distinct vectors.
+ *
+ * replace:
+ *
+ * a) replace a portion of the 'dst' vector by a portion of the 'src' vector
+ * b) either: leave 'src' unchanged -- copy
+ * or: remove the stuff copied from 'src' -- move
+ *
+ * Arguments: to_copy -- false
+ * to -- ignored
+ * dst -- vector
+ * i_dst -- start of portion to replace
+ * n_dst -- length of portion to replace. 0 => insertion.
+ * src -- vector, or NULL => nothing to copy/move
+ * i_src -- start of portion to copy/move
+ * n_src -- length of portion to copy/move. 0 => nothing.
+ * src_move -- true => move, otherwise copy.
+ *
+ * Returns: original 'to' argument
+ *
+ * NB: 'dst' and 'src' must be distinct vectors.
+ *
+ * All copies are shallow -- pointers to item values are copied, not the values.
+ *
+ * NB: any existing contents of the 'to' vector are discarded.
+ *
+ * NB: it is the caller's responsibility to release memory allocated to any
+ * items which are discarded in these operations.
+ *
+ * NB: for splice and replace, the resulting destination vector will be at
+ * least i_dst + n_src long. (Even if is copying actual or implied NULLs
+ * from the source.)
+ *
+ * NB: where new vectors are created, they will be of exactly the required size.
+ *
+ * NB: where an existing vector is reused, it is the caller's responsibility
+ * to ensure the vector structure is currently valid (by vector_init_new()
+ * or by ensuring it is zeroized).
+ */
+extern vector
+vector_sak(int to_copy, vector to,
+ vector dst, vector_index_t i_dst, vector_length_t n_dst,
+ vector src, vector_index_t i_src, vector_length_t n_src, int src_move)
+{
+ int dst_replace ; /* true => replace portion of 'dst' */
+
+ vector_index_t new_dst_end = 0 ; /* new end of dst */
+
+ vector_length_t n_dst_nulls ; /* number of implicit NULLs to add */
+ vector_length_t n_dst_move ; /* number of items to move up or down */
+ vector_length_t n_src_real ; /* number of items to really copy */
+ vector_length_t n_src_nulls ; /* number of implicit NULLs to "copy" */
+
+ assert((to == NULL) || (dst == NULL) || (to != dst)) ;
+ assert((src == NULL) || (dst == NULL) || (src != dst)) ;
+
+ /* Worry about how much we really have in the source vector. */
+
+ n_src_real = n_src ; /* assume all real */
+ n_src_nulls = 0 ; /* so no NULLs to "copy" */
+
+ if (n_src != 0)
+ {
+ if ((src == NULL) || (i_src >= src->end))
+ n_src_real = 0 ;
+ else if ((i_src + n_src) > src->end)
+ n_src_real = src->end - i_src ;
+ n_src_nulls = n_src - n_src_real ;
+ } ;
+
+ /* If no 'dst' vector, then this is an extract. */
+
+ n_dst_move = 0 ; /* assume nothing to move */
+ n_dst_nulls = 0 ; /* assume no NULLs to add */
+
+ if (dst == NULL)
+ /* For extract: set up dst, i_dst and n_dst so that can copy to the */
+ /* 'to' vector as if from 'dst'. */
+ {
+ dst_replace = 0 ; /* no replacement operation */
+ dst = src ; /* copy from here */
+ i_dst = i_src ;
+ n_dst = n_src_real ;
+ }
+ else
+ /* Reduce n_dst to the number of actual items to be replaced. */
+ /* */
+ /* Calculate the new end of 'dst'. */
+ {
+ dst_replace = 1 ; /* have replacement to do */
+ if (i_dst >= dst->end)
+ /* If i_dst is beyond the end of 'dst', then there is nothing */
+ /* to replace (so set n_dst == 0). Will be adding n_src items */
+ /* at i_dst -- so new end must be i_dst + n_src. */
+ {
+ n_dst_nulls = i_dst - dst->end ; /* fill from end to i_dst */
+ n_dst = 0 ; /* nothing to replace */
+ new_dst_end = i_dst + n_src ; /* all beyond current end */
+ }
+ else
+ /* If i_dst + n_dst is beyond the end of 'dst', reduce n_dst to */
+ /* number of items up to the end. */
+ /* Will remove n_dst items and insert n_src, so end will move */
+ /* by n_src - n_dst. */
+ {
+ if ((i_dst + n_dst) > dst->end)
+ n_dst = dst->end - i_dst ; /* what we actually replace */
+ else if (n_dst != n_src)
+ n_dst_move = dst->end - (i_dst + n_dst) ;
+ /* what we move up or down */
+
+ new_dst_end = dst->end + n_src - n_dst ;
+ /* end depends on amount added */
+ /* & amount actually replaced */
+ } ;
+ } ;
+
+ /* Copy portion of 'dst' (or of 'src') to 'to', if required. */
+ /* */
+ /* Have arranged: n_dst -- number of items to copy, all existent */
+ /* dst -- vector to copy from -- if n_dst > 0 */
+ /* i_dst -- first item to copy -- if n_dst > 0 */
+
+ if (to_copy)
+ {
+ to = vector_re_init(to, n_dst) ; /* reinitialise or create */
+ to->end = n_dst ;
+ if (n_dst > 0)
+ memcpy(to->p_items, &dst->p_items[i_dst], P_ITEMS_SIZE(n_dst)) ;
+ } ;
+
+ /* Replace portion of 'dst' by portion of 'src', if required. */
+ /* */
+ /* Have arranged: */
+ /* */
+ /* new_dst_end -- end of dst once dust settles */
+ /* n_dst_nulls -- number of NULLs to insert at dst->end to fill up */
+ /* to i_dst (when i_dst is beyond old end.) */
+ /* n_dst_move -- number of items in dst to move up or down to */
+ /* leave n_src item hole at i_dst to fill in. */
+ /* n_src_real -- number of real src items at i_src to copy to dst */
+ /* at i_dst. */
+ /* n_src_nulls -- number of nulls to add to fill to i_dst + n_src. */
+
+ if (dst_replace)
+ {
+ if (new_dst_end > dst->limit) /* extend body if required */
+ vector_new_limit(dst, new_dst_end) ;
+
+ if (n_dst_nulls > 0)
+ memset(&dst->p_items[dst->end], 0, P_ITEMS_SIZE(n_dst_nulls)) ;
+ if (n_dst_move > 0)
+ memmove(&dst->p_items[i_dst + n_dst], &dst->p_items[i_dst + n_src],
+ P_ITEMS_SIZE(n_dst_move)) ;
+ if (n_src_real > 0)
+ memcpy(&dst->p_items[i_dst], &src->p_items[i_src],
+ P_ITEMS_SIZE(n_src_real)) ;
+ if (n_src_nulls > 0)
+ memset(&dst->p_items[i_dst + n_src_real], 0,
+ P_ITEMS_SIZE(n_src_nulls)) ;
+ dst->end = new_dst_end ;
+ } ;
+
+ /* Delete portion of 'src', if required (and have 'src' !) */
+
+ if (src_move && (n_src_real != 0))
+ vector_delete(src, i_src, n_src_real) ;
+
+ /* Done -- return 'to' vector as promised. */
+
+ return to ;
+} ;
+
+/*==============================================================================
+ * Legacy Vector Operations
+ */
+
+/*------------------------------------------------------------------------------
+ * Set value to the smallest empty slot.
+ *
+ * Returns: index of slot used.
+ */
+extern int
+vector_set (vector v, void *val)
+{
+ vector_index_t i;
+
+ i = 0 ;
+ while (1)
+ {
+ if (i == v->end)
+ {
+ i = vector_extend_by_1(v) ;
+ break ;
+ }
+
+ if (v->p_items[i] == NULL)
+ break ;
+
+ ++i ;
+ } ;
+
+ v->p_items[i] = val;
+
+ return i ;
+}
+
+/*------------------------------------------------------------------------------
+ * Set value to specified index slot.
+ *
+ * Returns: index of slot (as given)
+ */
+extern int
+vector_set_index (vector v, vector_index_t i, void *val)
+{
+ vector_ensure (v, i);
+ v->p_items[i] = val;
+ return i;
+}
+
+/*------------------------------------------------------------------------------
+ * Look up vector -- get the i'th item.
+ *
+ * Returns: the i'th item -- NULL if item is null, or i >= logical len of vector
+ */
+extern p_vector_item
+vector_lookup (vector v, vector_index_t i)
+{
+ if (i >= v->end)
+ return NULL;
+ return v->p_items[i];
+}
+
+/*------------------------------------------------------------------------------
+ * Lookup vector, ensure it -- get i'th item and ensure logical len > i.
+ *
+ * Returns: the i'th item -- NULL if item is null
+ */
+extern p_vector_item
+vector_lookup_ensure (vector v, vector_index_t i)
+{
+ vector_ensure (v, i);
+ return v->p_items[i];
+}
+
+/*------------------------------------------------------------------------------
+ * Count the number of not empty slots.
+ */
+extern vector_length_t
+vector_count (vector v)
+{
+ vector_index_t i;
+ vector_length_t count = 0;
+
+ for (i = 0; i < v->end; i++)
+ if (v->p_items[i] != NULL)
+ count++;
+
+ return count;
+}
+
+/*==============================================================================
+ * Sorting and Searching vector.
+ */
+
+/*------------------------------------------------------------------------------
+ * Sort the given vector.
+ *
+ * NB: the comparison function receives a pointer to the pointer to the
+ * vector item's value.
+ *
+ * NB: if there are NULL items in the vector, the comparison function MUST
+ * be ready for them.
+ */
+extern void
+vector_sort(vector v, vector_sort_cmp* cmp)
+{
+ if (v->end <= 1)
+ return ; /* Stop dead if 0 or 1 items */
+
+ typedef int qsort_cmp(const void*, const void*) ;
+
+ qsort(v->p_items, v->end, sizeof(p_vector_item), (qsort_cmp*)cmp) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Perform binary search on the given vector.
+ *
+ * The vector MUST be sorted in the order implied by the comparison function
+ * given. The vector need not contain unique values, but this search makes
+ * no effort to select any particular instance of a sequence of equals.
+ *
+ * Returns:
+ *
+ * result == 0: found an equal value.
+ * index returned is of first entry found which is equal to
+ * the value sought. There may be other equal values, before
+ * and/or after this one in the vector.
+ *
+ * result == +1: value not found and vector not empty.
+ * index returned is of the largest entry whose value is less
+ * than the value sought.
+ * (The value sought belongs after this point.)
+ *
+ * result == -1: value is less than everything in the vector, or the
+ * vector is empty.
+ * index returned is 0
+ *
+ * NB: The comparison function takes arguments which are:
+ *
+ * const void** pointer to pointer to value being searched for.
+ * const void** pointer to pointer to vector item to compare with
+ *
+ * The value being searched for need not be in the same form as a vector
+ * item. However, if it is then the same comparison function can be used
+ * to sort and search.
+ *
+ * NB: if there are NULL items in the vector, the comparison function MUST
+ * be ready for them.
+ */
+extern vector_index_t
+vector_bsearch(vector v, vector_bsearch_cmp* cmp, const void* p_val,
+ int* result)
+{
+ vector_index_t il, iv, ih ;
+ int c ;
+
+ if (v->end <= 1)
+ {
+ *result = (v->end == 0) ? -1 : cmp(&p_val, (const void**)&v->p_items[0]) ;
+ return 0 ; /* Stop dead if 0 or 1 items */
+ } ;
+
+ /* We have at least two items. */
+ il = 0 ;
+ ih = v->end - 1 ;
+
+ /* Pick off the edge cases: >= last and <= first. */
+ if ((c = cmp(&p_val, (const void**)&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)
+ {
+ *result = c ; /* 0 => found. -1 => val < first */
+ return il ; /* return low index. */
+ }
+
+ /* Now binary chop. We know that item[il] < val < item[ih] */
+ /* We also know that il < ih */
+ while (1)
+ {
+ iv = (il + ih) / 2 ;
+ if (iv == il) /* true if (ih == il+1) */
+ {
+ *result = +1 ;
+ 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]) ;
+ if (c == 0)
+ {
+ *result = 0 ;
+ return iv ; /* found !! */
+ }
+ if (c < 0)
+ ih = iv ; /* step down iv > il, so new ih > il */
+ else
+ il = iv ; /* step up iv < ih, so new il < ih */
+ } ;
+} ;
+
+/*==============================================================================
+ * Mechanics for adding/deleting items and managing the vector (logical) end
+ * and (physical) limit.
+ */
+
+/* Extract the LS bit of unsigned integer 'x'. */
+#define lsbit(x) ((x) & ((~(x)) + 1))
+/* Round 'x' up to a multiple of 'm' */
+#define multiple(x, m) ((((x) + (m) - 1) / (m)) * (m))
+
+/*------------------------------------------------------------------------------
+ * Set new limit to suit new end for given vector.
+ *
+ * The new limit will be at least: VECTOR_LIMIT_MIN.
+ *
+ * While the vector is relatively small, the limit is doubled until there
+ * is at least 1/8 of the new vector free.
+ *
+ * Beyond VECTOR_LIMIT_DOUBLE_MAX, however, the limit is set to the
+ * smallest multiple of VECTOR_LIMIT_DOUBLE_MAX which gives at least
+ * VECTOR_LIMIT_SLACK_MIN free entries beyond the new end.
+ *
+ * This is an attempt to balance the cost of repeated reallocations of
+ * memory against the cost of possible wasted space at the end of the
+ * vector.
+ *
+ * NB: the new_limit depends entirely on the new end position. (Current
+ * end position is ignored.)
+ *
+ * NB: the new limit may be less than the current limit, in which case the
+ * vector body is reduced in size.
+ *
+ * Except for any size set when the vector is initialised, the vector body
+ * size will be a power of 2 or a multiple of VECTOR_LIMIT_DOUBLE_MAX.
+ * (Vectors are regular in their habits, which may help the memory allocator).
+ *
+ * TODO: what to do if calculation of new_limit overflows, or calculation
+ * of P_ITEMS_SIZE will ?
+ */
+static void
+vector_new_limit(vector v, vector_index_t new_end)
+{
+ vector_length_t old_limit = v->limit ;
+ vector_length_t new_limit ;
+
+ if (new_end > ((VECTOR_LIMIT_DOUBLE_MAX * 7) / 8))
+ {
+ new_limit = multiple(new_end + VECTOR_LIMIT_SLACK_MIN,
+ VECTOR_LIMIT_DOUBLE_MAX) ;
+ }
+ else
+ {
+ /* Want the new_limit to be a power of 2. */
+ /* If the old_limit was a power of 2, start from there. */
+ /* Otherwise start from a power of 2 less than new_end: either the */
+ /* minimum value or a value mid way to VECTOR_LIMIT_DOUBLE_MAX. */
+ if ( (old_limit != 0) && (old_limit == lsbit(old_limit))
+ && (new_end >= old_limit) )
+ new_limit = old_limit ;
+ else
+ new_limit = (new_end < VECTOR_LIMIT_MID) ? VECTOR_LIMIT_MIN
+ : VECTOR_LIMIT_MID ;
+ while (new_end > ((new_limit * 7) / 8))
+ new_limit *= 2 ;
+ } ;
+
+ v->p_items =
+ XREALLOC(MTYPE_VECTOR_BODY, v->p_items, P_ITEMS_SIZE(new_limit)) ;
+
+ v->limit = new_limit ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Extend vector and set new (logical) end.
+ *
+ * Extends body if required.
+ * Ensures everything between old and new end is set NULL.
+ *
+ * NB: expects new end > old end, but copes with new end <= old end.
+ */
+extern void
+vector_extend(vector v, vector_length_t new_end)
+{
+ vector_length_t old_end = v->end ;
+
+ if (new_end > v->limit)
+ vector_new_limit(v, new_end) ;
+ v->end = new_end ;
+
+ if (new_end > old_end)
+ memset(&v->p_items[old_end], 0, P_ITEMS_SIZE(new_end - old_end)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Insert entries into vector: insert 'n' NULL entries at location 'i'.
+ *
+ * Updates end (and limit) to be at least 'i' + 'n'.
+ * (So if 'i' < end then end becomes end + 'n', else end becomes 'i' + 'n'.)
+ */
+extern void
+vector_insert(vector v, vector_index_t i, vector_length_t n)
+{
+ vector_length_t old_end, new_end ;
+ vector_length_t n_above ;
+
+ /* If i < old end, then we are inserting n NULLs, and need
+ * to shuffle at least one item up.
+ * else we are setting new end to i + n and need to NULL
+ * fill from old end to the new end.
+ */
+ old_end = v->end ;
+ if (i < old_end)
+ {
+ if (n == 0)
+ return ; /* give up now if not inserting anything */
+ n_above = old_end - i ; /* number of items to shuffle up.. >= 1 */
+ new_end = old_end + n ;
+ }
+ else
+ {
+ n_above = 0 ; /* nothing to shuffle up. */
+ new_end = i + n ;
+ i = old_end ; /* where to zeroize from */
+ n = new_end - old_end ; /* how much to zeroize */
+ } ;
+
+ /* Now we extend the body if we need to. */
+ if (new_end > v->limit)
+ vector_new_limit(v, new_end) ;
+ v->end = new_end ;
+
+ if (n_above > 0)
+ memmove(&v->p_items[i + n], &v->p_items[i], P_ITEMS_SIZE(n_above)) ;
+
+ memset(&v->p_items[i], 0, P_ITEMS_SIZE(n)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Delete items from vector: delete 'n' items at location 'i'.
+ *
+ * Does nothing if 'i' is beyond current end of vector or if 'n' == 0.
+ *
+ * Deletes from 'i' to end if less than 'n' items to the end.
+ *
+ * NB: does NOT change the size of the body.
+ *
+ * NB: it is the caller's responsibility to have released any memory allocated
+ * for the items that are being deleted.
+*/
+extern void
+vector_delete(vector v, vector_index_t i, vector_length_t n)
+{
+ vector_length_t old_end, new_end ;
+
+ old_end = v->end ;
+
+ if ((i >= old_end) || (n == 0))
+ return ;
+
+ /* If i + n < old_end, we have 1 or more items to keep and move down */
+ if ((i + n) < old_end)
+ {
+ memmove(&v->p_items[i], &v->p_items[i + n],
+ P_ITEMS_SIZE(old_end - (i + n))) ;
+ new_end = old_end - n ;
+ }
+ else
+ {
+ new_end = i ; /* We are keeping nothing above 'i' */
+ /* NB: new_end < old_end */
+ } ;
+
+ v->end = new_end ; /* account for stuff dropped */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Discard entries from vector: discard entries from location 'i' onwards.
+ *
+ * Releases memory from 'i' onwards.
+ * Releases the body altogether if this sets the vector empty ('i' == 0).
+ * Sets new end of vector iff 'i' < current end.
+ *
+ * Does nothing if 'i' is beyond current limit (physical end) of vector.
+ *
+ * NB: it is the caller's responsibility to have released any memory allocated
+ * for the items that are being discarded.
+*/
+extern void
+vector_discard(vector v, vector_index_t i)
+{
+ if (i >= v->limit)
+ return ;
+
+ if (i == 0)
+ vector_reset(v, 0) ; /* reset, without releasing the structure */
+ else
+ {
+ v->limit = i ;
+ if (i < v->end)
+ v->end = i ;
+ v->p_items = XREALLOC(MTYPE_VECTOR_BODY, v->p_items, P_ITEMS_SIZE(i)) ;
+ } ;
+} ;
+
+/* Chop vector at the current (logical) end.
+ *
+ * Releases the body altogether if the vector is currently empty.
+ */
+extern void
+vector_chop(vector v)
+{
+ vector_length_t new_limit = v->end ;
+
+ if (new_limit == 0)
+ vector_reset(v, 0) ; /* reset, without releasing the structure */
+ else if (new_limit != v->limit)
+ {
+ v->limit = new_limit ;
+ v->p_items = XREALLOC(MTYPE_VECTOR_BODY, v->p_items, P_ITEMS_SIZE(new_limit)) ;
+ } ;
+} ;
+
+/* Decant vector into a new body allocated to current logical size, and
+ * release the old body.
+ *
+ * Releases the body altogether if the vector is currently empty.
+*/
+void
+vector_decant(vector v)
+{
+ p_vector_item* p_old_body ;
+ vector_length_t new_limit = v->end ;
+
+ if (new_limit == 0)
+ vector_reset(v, 0) ; /* reset, without releasing the structure */
+ else
+ {
+ p_old_body = v->p_items ;
+
+ vector_init_new(v, new_limit) ; /* initialise with new body */
+
+ memcpy(v->p_items, p_old_body, P_ITEMS_SIZE(new_limit)) ;
+ /* copy the old body across */
+ v->end = new_limit ;
+
+ XFREE(MTYPE_VECTOR_BODY, p_old_body) ;
+ } ;
+} ;
diff --git a/lib/thread.c b/lib/thread.c
index 078a09d6..b9fd603c 100644
--- a/lib/thread.c
+++ b/lib/thread.c
@@ -44,8 +44,8 @@ static unsigned short timers_inited;
/* cpu stats needs to be qpthread safe. */
static qpt_mutex_t thread_mutex;
-#define LOCK qpt_mutex_lock(&thread_mutex);
-#define UNLOCK qpt_mutex_unlock(&thread_mutex);
+#define LOCK qpt_mutex_lock(thread_mutex);
+#define UNLOCK qpt_mutex_unlock(thread_mutex);
static struct hash *cpu_record = NULL;
/* Pointer to qtimer pile to be used, if any */
@@ -555,7 +555,7 @@ thread_master_create ()
{
#ifdef USE_MQUEUE
sigfillset (&newmask);
- sigdelset (&newmask, SIGMQUEUE);
+ sigdelset (&newmask, SIG_INTERRUPT);
#endif
if (cpu_record == NULL)
@@ -1526,14 +1526,14 @@ funcname_thread_execute (struct thread_master *m,
void
thread_init_r (void)
{
- qpt_mutex_init(&thread_mutex, qpt_mutex_quagga);
+ qpt_mutex_init(thread_mutex, qpt_mutex_quagga);
}
/* Finished with module */
void
thread_finish (void)
{
- qpt_mutex_destroy(&thread_mutex, 0);
+ qpt_mutex_destroy(thread_mutex, 0);
}
#undef USE_MQUEUE
diff --git a/lib/thread.h b/lib/thread.h
index a38fb19a..d9c58640 100644
--- a/lib/thread.h
+++ b/lib/thread.h
@@ -206,8 +206,8 @@ 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_element clear_thread_cpu_cmd;
+extern struct cmd_command show_thread_cpu_cmd;
+extern struct cmd_command clear_thread_cpu_cmd;
/* replacements for the system gettimeofday(), clock_gettime() and
* time() functions, providing support for non-decrementing clock on
diff --git a/lib/tstring.h b/lib/tstring.h
new file mode 100644
index 00000000..4dd45edb
--- /dev/null
+++ b/lib/tstring.h
@@ -0,0 +1,139 @@
+/* Temporary string handling -- header
+ * 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_TSTRING_H
+#define _ZEBRA_TSTRING_H
+
+#include "misc.h"
+#include "zassert.h"
+#include "memory.h"
+
+/*==============================================================================
+ * tstrings are allocated on the stack, but if (unexpectedly) the standard
+ * size is not enough, then they can be allocated dynamically.
+ *
+ * To declare a "tstring":
+ *
+ * tstring(foo, 64) ; // creates a "tstring" variable called "foo"
+ * // with 64 char buffer.
+ *
+ * Can then:
+ *
+ * s = tstring_set_len(foo, n) ; // ensures have buffer for n+1 chars
+ * s = tstring_set(foo, "...") ; // copies "..." (with '\0') to buffer
+ * s = tstring_set_n(foo, q, n) ; // copies n characters from q to buffer
+ * and '\0' terminates
+ *
+ * If can fit stuff in the buffer, will do so. Otherwise will allocate an
+ * MTYPE_TMP buffer to work in.
+ *
+ * And before leaving the scope of "foo" must:
+ *
+ * tstring_free(foo) ; // releases any dynamically allocated memory.
+ */
+
+struct tstring
+{
+ usize size ;
+ char* str ;
+ char* alloc ;
+} ;
+
+typedef struct tstring tstring[1] ;
+
+/* tstring(foo, 93) ; -- declare the variable "foo". */
+#define tstring_t(name, sz) \
+ char _zlxq_##name##_b[ ((sz) + 7) & 0xFFFFFFF8] ; \
+ tstring name = { { .size = ((sz) + 7) & 0xFFFFFFF8, \
+ .str = _zlxq_##name##_b, \
+ .alloc = NULL } }
+
+/*------------------------------------------------------------------------------
+ * Ensure the tstring "foo" can accomodate at least "len" characters plus the
+ * terminating '\0'.
+ *
+ * Returns: address of buffer
+ *
+ * NB: address of buffer may not be the same as returned by a previous operation
+ * on foo. Also, previous contents of foo may be lost.
+ */
+Inline char*
+tstring_set_len(struct tstring* ts, usize len)
+{
+ if (len >= ts->size)
+ {
+ ts->size = len + 1 ;
+ ts->str = ts->alloc = XREALLOC(MTYPE_TMP, ts->alloc, len + 1) ;
+ } ;
+
+ return ts->str ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Copy "len" characters from "src" to the tstring "foo", and append a
+ * terminating '\0'.
+ *
+ * The "src" address is ignored if "len" == 0 (sets "foo" to be empty string).
+ *
+ * Returns: address of buffer
+ *
+ * NB: address of buffer may not be the same as returned by a previous operation
+ * on foo. Also, previous contents of foo may be lost.
+ */
+static inline char*
+tstring_set_n(struct tstring* ts, const char* str, usize len)
+{
+ char* tss = tstring_set_len(ts, len) ;
+
+ if (len > 0)
+ memcpy(tss, str, len) ;
+ *(tss + len) = '\0' ;
+
+ return tss ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Copy the string "str" to the tstring "foo", with terminating '\0'.
+ *
+ * If "str" is NULL, sets "foo" to be an empty string.
+ *
+ * Returns: address of buffer
+ *
+ * NB: address of buffer may not be the same as returned by a previous operation
+ * on foo. Also, previous contents of foo may be lost.
+ */
+static inline char*
+tstring_set(struct tstring* ts, const char* str)
+{
+ return tstring_set_n(ts, str, (str != NULL) ? strlen(str) : 0) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * If have dynamically allocated buffer for tstring "foo", release it now.
+ */
+static inline void
+tstring_free(struct tstring* ts)
+{
+ if (ts->alloc != NULL)
+ XFREE(MTYPE_TMP, ts->alloc) ; /* sets ts->alloc NULL */
+} ;
+
+#endif /* _ZEBRA_TSTRING_H */
diff --git a/lib/uty.h b/lib/uty.h
deleted file mode 100644
index d1a8b584..00000000
--- a/lib/uty.h
+++ /dev/null
@@ -1,239 +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 <stdbool.h>
-
-#include "qpthreads.h"
-#include "qpnexus.h"
-#include "thread.h"
-#include "list_util.h"
-#include "vty.h"
-#include "node_type.h"
-
-/* Macro in case there are particular compiler issues. */
-#ifndef Inline
- #define Inline static inline
-#endif
-
-/*==============================================================================
- * 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 ;
-
-union vty_watch_dog
-{
- qtimer qnexus ; /* when running qnexus */
- struct thread* thread; /* when running threads */
- void* anon ;
-};
-
-extern union vty_watch_dog vty_watch_dog ;
-
-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)
-{
- qpt_mutex_lock(&vty_mutex) ;
- if (VTY_DEBUG)
- ++vty_lock_count ;
-} ;
-
-Inline void
-VTY_UNLOCK(void)
-{
- if (VTY_DEBUG)
- --vty_lock_count ;
- qpt_mutex_unlock(&vty_mutex) ;
-} ;
-
-/* 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 (qpthreads_enabled)
- if (!qpt_thread_is_self(vty_cli_nexus->thread_id))
- 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/vargs.h b/lib/vargs.h
new file mode 100644
index 00000000..5f0207cb
--- /dev/null
+++ b/lib/vargs.h
@@ -0,0 +1,34 @@
+/* Variable arguments definitions
+ * Copyright (C) 2010 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * 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_VARGS_H
+#define _ZEBRA_VARGS_H
+
+#include <stdarg.h>
+
+/* GCC has printf type attribute check. */
+#ifdef __GNUC__
+#define PRINTF_ATTRIBUTE(a,b) __attribute__ ((__format__ (__printf__, a, b)))
+#else
+#define PRINTF_ATTRIBUTE(a,b)
+#endif /* __GNUC__ */
+
+#endif /* _ZEBRA_VARGS_H */
diff --git a/lib/vector.c b/lib/vector.c
index 83f289fe..cd81191f 100644
--- a/lib/vector.c
+++ b/lib/vector.c
@@ -22,7 +22,7 @@
* 02111-1307, USA.
*/
-#include <zebra.h>
+#include "misc.h"
#include "vector.h"
#include "memory.h"
@@ -84,11 +84,13 @@
*/
#define P_ITEMS_SIZE(n) SIZE(p_vector_item, n)
+
/*==============================================================================
* Initialisation, allocation, reset etc.
*/
-/* Initialise a brand new vector, setting it empty.
+/*------------------------------------------------------------------------------
+ * Initialise a brand new vector, setting it empty.
*
* Allocates vector structure if none given -- that is, if v == NULL.
*
@@ -98,41 +100,50 @@
* NB: discards any existing vector body -- so it is the caller's responsibility
* to release any existing body, and any items in that body.
*/
-vector
-vector_init_new(vector v, unsigned int size)
+extern vector
+vector_init_new(vector v, vector_length_t limit)
{
if (v == NULL)
v = XCALLOC(MTYPE_VECTOR, sizeof(struct vector)) ;
else
memset(v, 0, sizeof(struct vector)) ;
- if (size != 0)
+ if (limit != 0)
{
- v->p_items = XMALLOC(MTYPE_VECTOR_BODY, P_ITEMS_SIZE(size)) ;
- v->limit = size ;
+ v->p_items = XMALLOC(MTYPE_VECTOR_BODY, P_ITEMS_SIZE(limit)) ;
+ v->limit = limit ;
} ;
return v ;
} ;
-/* Initialize vector : allocate memory and return vector.
+/*------------------------------------------------------------------------------
+ * Initialize vector : allocate memory and return vector.
* allocates body with at least 1 entry.
+ *
+ * This is a "legacy" function.
*/
-vector
-vector_init (unsigned int size)
+extern vector
+vector_init (vector_length_t limit)
{
- return vector_init_new(NULL, size ? size : 1) ; /* at least 1 entry */
+ return vector_init_new(NULL, limit ? limit : 1) ; /* at least 1 entry */
} ;
-/* Basic: free the vector body and the vector structure. */
+/*------------------------------------------------------------------------------
+ * Basic: free the vector body and the vector structure.
+ *
+ * NB: it is the caller's responsibility to release any vector item values
+ * *before* doing this.
+ */
void
vector_free (vector v)
{
XFREE (MTYPE_VECTOR_BODY, v->p_items);
XFREE (MTYPE_VECTOR, v);
-}
+} ;
-/* Re-Initialize vector (or create new one), setting it empty.
+/*------------------------------------------------------------------------------
+ * Re-initialise vector (or create new one), setting it empty.
*
* Allocates vector structure if none given -- that is, if v == NULL.
*
@@ -142,27 +153,29 @@ vector_free (vector v)
* Otherwise ensures existing body is at least the required size, or a body
* of exactly the required size is allocated.
*
- * NB: when re-initialising an existing vector it is the caller's
- * responsibility to release any vector item values *before* doing this.
+ * NB: when re-initialising an existing vector it is the caller's responsibility
+ * to release any vector item values *before* doing this.
* */
-vector
-vector_re_init(vector v, unsigned int size)
+extern vector
+vector_re_init(vector v, vector_length_t limit)
{
if ((v == NULL) || (v->p_items == NULL))
- return vector_init_new(v, size) ;
+ return vector_init_new(v, limit) ;
v->end = 0 ;
- if (v->limit < size)
+ if (v->limit < limit)
{
- v->p_items = XREALLOC(MTYPE_VECTOR_BODY, v->p_items, P_ITEMS_SIZE(size)) ;
- v->limit = size ;
+ v->p_items = XREALLOC(MTYPE_VECTOR_BODY, v->p_items,
+ P_ITEMS_SIZE(limit)) ;
+ v->limit = limit ;
} ;
return v ;
} ;
-/* Free the vector body, and free the vector structure or reset it.
+/*------------------------------------------------------------------------------
+ * Free the vector body, and (if required) free the vector structure.
*
* Return NULL if releases vector, otherwise the address of the vector.
*
@@ -170,16 +183,17 @@ vector_re_init(vector v, unsigned int size)
* *before* doing this.
*/
vector
-vector_reset(vector v, int free_structure)
+vector_reset(vector v, free_keep_b free_structure)
{
if (v == NULL)
- return NULL ; /* allow for already freed vector */
+ return NULL ; /* allow for already freed vector */
if (v->p_items != NULL)
XFREE(MTYPE_VECTOR_BODY, v->p_items) ;
if (free_structure)
{
+ confirm(free_it == true) ;
XFREE(MTYPE_VECTOR, v) ;
return NULL ;
}
@@ -187,7 +201,8 @@ vector_reset(vector v, int free_structure)
return vector_init_new(v, 0) ;
} ;
-/* Set vector length to be (at least) the given fixed length.
+/*------------------------------------------------------------------------------
+ * Set vector length to be (at least) the given fixed length.
*
* There must be a vector.
*
@@ -201,8 +216,8 @@ vector_reset(vector v, int free_structure)
*
* Note that the existing contents of the vector are preserved in all cases.
*/
-extern void
-vector_set_new_min_length(vector v, unsigned int len)
+Private void
+vector_set_new_min_length(vector v, vector_length_t len)
{
assert (v != NULL) ;
@@ -211,7 +226,8 @@ vector_set_new_min_length(vector v, unsigned int len)
if (v->p_items == NULL)
v->p_items = XMALLOC(MTYPE_VECTOR_BODY, P_ITEMS_SIZE(len)) ;
else
- v->p_items = XREALLOC(MTYPE_VECTOR_BODY, v->p_items, P_ITEMS_SIZE(len));
+ v->p_items = XREALLOC(MTYPE_VECTOR_BODY, v->p_items,
+ P_ITEMS_SIZE(len)) ;
v->limit = len ;
} ;
@@ -219,8 +235,8 @@ vector_set_new_min_length(vector v, unsigned int len)
vector_extend(v, len) ;
} ;
-/* Pop item from vector, stepping past any NULLs.
- *
+/*------------------------------------------------------------------------------
+ * Pop item from vector, stepping past any NULLs.
* If vector is empty, free the body and, if required, the vector structure.
*
* Useful for emptying out and discarding a vector:
@@ -231,7 +247,7 @@ vector_set_new_min_length(vector v, unsigned int len)
* Returns NULL if vector was empty and has now been freed as required.
*/
p_vector_item
-vector_ream(vector v, int free_structure)
+vector_ream(vector v, free_keep_b free_structure)
{
p_vector_item p_v ;
@@ -242,61 +258,23 @@ vector_ream(vector v, int free_structure)
{
p_v = v->p_items[--v->end] ;
if (p_v != NULL)
- return p_v ; /* return non-NULL item */
+ return p_v ; /* return non-NULL item */
} ;
/* vector is empty: free the body, and (if required) the vector structure. */
vector_reset(v, free_structure) ;
- return NULL ; /* signals end */
+ return NULL ; /* signals end */
} ;
/*==============================================================================
* Unset item, condensing and trimming vector.
- */
-
-/* Trim any NULL entries at the current (logical) end of the vector.
*
- * Returns the (new) end of the vector.
+ * These are legacy operations.
*/
-extern vector_index
-vector_trim(vector v)
-{
- vector_index i = v->end ;
- while ((i > 0) && (v->p_items[i - 1] == NULL))
- --i ;
- v->end = i ;
- return i ;
-} ;
-/* Removes any NULL entries from the given vector.
- *
- * Returns the (new) end of the vector.
- */
-extern vector_index vector_condense(vector v)
-{
- vector_index i = 0 ;
- vector_index j ;
-
- /* Find first NULL, if any */
- while ((i < v->end) && (v->p_items[i] != NULL))
- ++i ;
-
- /* Quit if no NULLs (or vector is empty) */
- if (i == v->end)
- return i ;
-
- /* Shuffle any remaining non-NULL down */
- for (j = i + 1 ; j < v->end ; ++j)
- if (v->p_items[j] != NULL)
- v->p_items[i++] = v->p_items[j] ;
-
- v->end = i ;
-
- return i ;
-} ;
-
-/* Unset item at given index (ie set it NULL).
+/*------------------------------------------------------------------------------
+ * Unset item at given index (ie set it NULL).
*
* Return the old value of the item.
*
@@ -304,7 +282,7 @@ extern vector_index vector_condense(vector v)
* end backwards until finds a non-NULL item, or the vector becomes empty.
*/
extern p_vector_item
-vector_unset_item(vector v, vector_index i)
+vector_unset_item(vector v, vector_index_t i)
{
p_vector_item was ;
@@ -324,15 +302,60 @@ vector_unset_item(vector v, vector_index i)
return was ;
} ;
+/*------------------------------------------------------------------------------
+ * Trim any NULL entries at the current (logical) end of the vector.
+ *
+ * Returns the (new) length (end) of the vector.
+ */
+extern vector_length_t
+vector_trim(vector v)
+{
+ vector_length_t e = v->end ;
+ while ((e > 0) && (v->p_items[e - 1] == NULL))
+ --e ;
+ v->end = e ;
+ return e ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Removes any NULL entries from the given vector.
+ *
+ * Returns the (new) length (end) of the vector.
+ */
+extern vector_length_t
+vector_condense(vector v)
+{
+ vector_length_t e = 0 ;
+ vector_index_t j ;
+
+ /* Find first NULL, if any */
+ while ((e < v->end) && (v->p_items[e] != NULL))
+ ++e ;
+
+ /* Quit if no NULLs (or vector is empty) */
+ if (e == v->end)
+ return e ;
+
+ /* Shuffle any remaining non-NULL down */
+ for (j = e + 1 ; j < v->end ; ++j)
+ if (v->p_items[j] != NULL)
+ v->p_items[e++] = v->p_items[j] ;
+
+ v->end = e ;
+
+ return e ;
+} ;
+
/*==============================================================================
* Inserting and deleting items.
*/
-/* Insert item in vector, before item at given position
+/*------------------------------------------------------------------------------
+ * Insert item in vector, before item at given position
* Move items and extend vector as required.
*/
-void
-vector_insert_item(vector v, vector_index i, void* p_v)
+extern void
+vector_insert_item(vector v, vector_index_t i, void* p_v)
{
if ((i == v->end) && (i < v->limit))
++v->end ;
@@ -342,7 +365,8 @@ vector_insert_item(vector v, vector_index i, void* p_v)
v->p_items[i] = (p_vector_item)p_v ;
} ;
-/* Insert item in vector at given position with rider:
+/*------------------------------------------------------------------------------
+ * Insert item in vector at given position with rider:
*
* rider < 0 -- insert before the item at the given position
* rider == 0 -- insert at the given position -- REPLACING any existing value
@@ -353,8 +377,8 @@ vector_insert_item(vector v, vector_index i, void* p_v)
*
* Move items and extend vector as required.
*/
-void
-vector_insert_item_here(vector v, vector_index i, int rider,
+extern void
+vector_insert_item_here(vector v, vector_index_t i, int rider,
p_vector_item p_v)
{
if (rider == 0)
@@ -365,18 +389,21 @@ vector_insert_item_here(vector v, vector_index i, int rider,
vector_insert_item(v, i, p_v) ;
} ;
-/* Delete item from vector.
+/*------------------------------------------------------------------------------
+ * Delete item from vector.
*
* Move items as required. Reduces number of items in the vector (unless
* the item in question is beyond the end of the vector !)
*
+ * Returns: the item that has just been deleted.
+ *
* NB: it is the caller's responsibility to release memory used by any
* current value of the item, if required.
*
* NB: does NOT change the size of the vector body.
*/
-p_vector_item
-vector_delete_item(vector v, vector_index i)
+extern p_vector_item
+vector_delete_item(vector v, vector_index_t i)
{
p_vector_item p_e ;
if (i < v->end)
@@ -396,19 +423,23 @@ vector_delete_item(vector v, vector_index i)
* Moving items within vector.
*/
-/* Move item in vector from source position to destination position.
+/*------------------------------------------------------------------------------
+ * Move item in vector from source position to destination position.
+ *
* Moves intervening items up or down as required.
+ *
* Extends vector to include the destination, if required.
+ *
* A source item beyond the end of the vector is implicitly NULL.
*/
-void
-vector_move_item(vector v, vector_index i_dst, vector_index i_src)
+extern void
+vector_move_item(vector v, vector_index_t i_dst, vector_index_t i_src)
{
p_vector_item* pp_s ;
p_vector_item* pp_d ;
p_vector_item p_e ;
- vector_index old_end = v->end ;
+ vector_length_t old_end = v->end ;
/* Worry about whether both source and destination exist. */
if (i_dst >= old_end)
@@ -437,7 +468,8 @@ vector_move_item(vector v, vector_index i_dst, vector_index i_src)
*pp_d = p_e ; /* put down the item to move */
} ;
-/* Move item in vector to given position with rider:
+/*------------------------------------------------------------------------------
+ * Move item in vector to given position with rider:
*
* rider < 0 -- move to before the item at the given position
* rider == 0 -- move to replace item at the given position
@@ -448,14 +480,14 @@ vector_move_item(vector v, vector_index i_dst, vector_index i_src)
*
* Move items and extend vector as required.
*/
-void
-vector_move_item_here(vector v, vector_index i_dst, int rider,
- vector_index i_src)
+extern void
+vector_move_item_here(vector v, vector_index_t i_dst, int rider,
+ vector_index_t i_src)
{
if (rider != 0)
{
if (rider > 0)
- ++i_dst ;
+ ++i_dst ;
vector_move_item(v, i_dst, i_src) ;
}
else
@@ -466,21 +498,23 @@ vector_move_item_here(vector v, vector_index i_dst, int rider,
} ;
} ;
-/* Reverse vector: reverse the order of items in the vector.
+/*------------------------------------------------------------------------------
+ * Reverse vector: reverse the order of items in the vector.
*/
-void
+extern void
vector_reverse(vector v)
{
if (v != NULL)
vector_part_reverse(v, 0, v->end) ;
} ;
-/* Reverse portion of vector.
+/*------------------------------------------------------------------------------
+ * Reverse portion of vector.
*/
-void
-vector_part_reverse(vector v, vector_index i, unsigned int n)
+extern void
+vector_part_reverse(vector v, vector_index_t i, vector_length_t n)
{
- vector_index j ;
+ vector_index_t j ;
if (v == NULL)
return ;
@@ -504,12 +538,16 @@ vector_part_reverse(vector v, vector_index i, unsigned int n)
* Copying, moving and appending entire vectors.
*/
-static void vector_new_limit(vector v, vector_index new_end) ;
+static void vector_new_limit(vector v, vector_length_t new_end) ;
-/* Shallow vector copy -- copies pointers to item values, not the values. */
-/* Creates a new vector. */
-/* NB: copies whole body, including stuff beyond (logical) end ! */
-/* TODO: is this behaviour required ? */
+/*------------------------------------------------------------------------------
+ * Shallow vector copy -- copies pointers to item values, not the values.
+ *
+ * Creates a new vector.
+ *
+ * NB: creates new vector with same limit as existing one, but copies only
+ * the known items (ie up to end, not up to limit).
+ */
vector
vector_copy (vector v)
{
@@ -518,12 +556,13 @@ vector_copy (vector v)
new->end = v->end;
if (v->limit > 0)
- memcpy(new->p_items, v->p_items, P_ITEMS_SIZE(v->limit)) ;
+ memcpy(new->p_items, v->p_items, P_ITEMS_SIZE(v->end)) ;
return new;
}
-/* Shallow vector copy -- copies pointers to item values, not the values.
+/*------------------------------------------------------------------------------
+ * Shallow vector copy -- copies pointers to item values, not the values.
* Creates a new vector or re-initialises an existing one.
*
* NB: creates new vector with same limit as existing one, but copies only
@@ -552,7 +591,9 @@ vector_copy_here(vector dst, vector src)
return dst ;
} ;
-/* Vector move -- moves body of vector.
+/*------------------------------------------------------------------------------
+ * Vector move -- moves body of vector.
+ *
* Creates a new vector or re-initialises an existing one.
* Leaves the source vector empty -- does not release the structure.
*
@@ -564,7 +605,7 @@ vector_copy_here(vector dst, vector src)
*
* NB: do NOT try moving a vector to itself !!
*/
-vector
+extern vector
vector_move_here(vector dst, vector src)
{
assert((src != NULL) && (src != dst)) ;
@@ -574,23 +615,26 @@ vector_move_here(vector dst, vector src)
else
dst = vector_init_new(dst, 0) ; /* Create new structure sans body. */
- *dst = *src ; /* Copy the vector structure */
+ *dst = *src ; /* Copy the vector structure */
- vector_init_new(src, 0) ; /* Set empty, forgetting body */
+ vector_init_new(src, 0) ; /* Set empty, forgetting body */
return dst ;
}
-/* Shallow vector copy append -- copies pointers to item values, not the values.
+/*------------------------------------------------------------------------------
+ * Shallow vector copy append -- copies pointers to item values, not the values.
+ *
* Appends copied pointers to the destination vector.
+ *
* Creates a new destination vector if required.
*
* NB: Can append to self.
*/
-vector
+extern vector
vector_copy_append(vector dst, vector src)
{
- vector_index new_end ;
+ vector_index_t new_end ;
assert(src != NULL) ;
@@ -613,14 +657,18 @@ vector_copy_append(vector dst, vector src)
return dst ;
} ;
-/* Vector move append -- moves pointers to item values.
+/*------------------------------------------------------------------------------
+ * Vector move append -- moves pointers to item values.
+ *
* Appends moved pointers to the destination vector.
+ *
* Creates a new destination vector if required (dst == NULL).
+ *
* Leaves the source vector empty -- does not release the structure.
*
* NB: do NOT try moving a vector to itself !!
*/
-vector
+extern vector
vector_move_append(vector dst, vector src)
{
assert((src != NULL) && (src != dst)) ;
@@ -629,7 +677,7 @@ vector_move_append(vector dst, vector src)
return vector_move_here(dst, src) ; /* Easy way to do it if dst empty */
vector_copy_append(dst, src) ; /* Extend dst and copy src */
- vector_init_new(src, 0) ; /* Set empty, forgetting body */
+ vector_init_new(src, 0) ; /* Set empty, forgetting body */
return dst ;
} ;
@@ -717,19 +765,19 @@ vector_move_append(vector dst, vector src)
* to ensure the vector structure is currently valid (by vector_init_new()
* or by ensuring it is zeroized).
*/
-vector
+extern vector
vector_sak(int to_copy, vector to,
- vector dst, vector_index i_dst, unsigned int n_dst,
- vector src, vector_index i_src, unsigned int n_src, int src_move)
+ vector dst, vector_index_t i_dst, vector_length_t n_dst,
+ vector src, vector_index_t i_src, vector_length_t n_src, int src_move)
{
int dst_replace ; /* true => replace portion of 'dst' */
- vector_index new_dst_end = 0 ; /* new end of dst */
+ vector_index_t new_dst_end = 0 ; /* new end of dst */
- unsigned int n_dst_nulls ; /* number of implicit NULLs to add */
- unsigned int n_dst_move ; /* number of items to move up or down */
- unsigned int n_src_real ; /* number of items to really copy */
- unsigned int n_src_nulls ; /* number of implicit NULLs to "copy" */
+ vector_length_t n_dst_nulls ; /* number of implicit NULLs to add */
+ vector_length_t n_dst_move ; /* number of items to move up or down */
+ vector_length_t n_src_real ; /* number of items to really copy */
+ vector_length_t n_src_nulls ; /* number of implicit NULLs to "copy" */
assert((to == NULL) || (dst == NULL) || (to != dst)) ;
assert((src == NULL) || (dst == NULL) || (src != dst)) ;
@@ -855,11 +903,15 @@ vector_sak(int to_copy, vector to,
* Legacy Vector Operations
*/
-/* Set value to the smallest empty slot. */
-int
+/*------------------------------------------------------------------------------
+ * Set value to the smallest empty slot.
+ *
+ * Returns: index of slot used.
+ */
+extern int
vector_set (vector v, void *val)
{
- vector_index i;
+ vector_index_t i;
i = 0 ;
while (1)
@@ -878,41 +930,55 @@ vector_set (vector v, void *val)
v->p_items[i] = val;
- return i;
+ return i ;
}
-/* Set value to specified index slot. */
-int
-vector_set_index (vector v, vector_index i, void *val)
+/*------------------------------------------------------------------------------
+ * Set value to specified index slot.
+ *
+ * Returns: index of slot (as given)
+ */
+extern int
+vector_set_index (vector v, vector_index_t i, void *val)
{
vector_ensure (v, i);
v->p_items[i] = val;
return i;
}
-/* Look up vector. */
-p_vector_item
-vector_lookup (vector v, vector_index i)
+/*------------------------------------------------------------------------------
+ * Look up vector -- get the i'th item.
+ *
+ * Returns: the i'th item -- NULL if item is null, or i >= logical len of vector
+ */
+extern p_vector_item
+vector_lookup (vector v, vector_index_t i)
{
if (i >= v->end)
return NULL;
return v->p_items[i];
}
-/* Lookup vector, ensure it. */
-p_vector_item
-vector_lookup_ensure (vector v, vector_index i)
+/*------------------------------------------------------------------------------
+ * Lookup vector, ensure it -- get i'th item and ensure logical len > i.
+ *
+ * Returns: the i'th item -- NULL if item is null
+ */
+extern p_vector_item
+vector_lookup_ensure (vector v, vector_index_t i)
{
vector_ensure (v, i);
return v->p_items[i];
}
-/* Count the number of not empty slots. */
-vector_index
+/*------------------------------------------------------------------------------
+ * Count the number of not empty slots.
+ */
+extern vector_length_t
vector_count (vector v)
{
- vector_index i;
- unsigned count = 0;
+ vector_index_t i;
+ vector_length_t count = 0;
for (i = 0; i < v->end; i++)
if (v->p_items[i] != NULL)
@@ -925,7 +991,8 @@ vector_count (vector v)
* Sorting and Searching vector.
*/
-/* Sort the given vector.
+/*------------------------------------------------------------------------------
+ * Sort the given vector.
*
* NB: the comparison function receives a pointer to the pointer to the
* vector item's value.
@@ -933,18 +1000,19 @@ vector_count (vector v)
* NB: if there are NULL items in the vector, the comparison function MUST
* be ready for them.
*/
-void
+extern void
vector_sort(vector v, vector_sort_cmp* cmp)
{
if (v->end <= 1)
- return ; /* Stop dead if 0 or 1 items */
+ return ; /* Stop dead if 0 or 1 items */
typedef int qsort_cmp(const void*, const void*) ;
qsort(v->p_items, v->end, sizeof(p_vector_item), (qsort_cmp*)cmp) ;
} ;
-/* Perform binary search on the given vector.
+/*------------------------------------------------------------------------------
+ * Perform binary search on the given vector.
*
* The vector MUST be sorted in the order implied by the comparison function
* given. The vector need not contain unique values, but this search makes
@@ -978,18 +1046,17 @@ vector_sort(vector v, vector_sort_cmp* cmp)
* NB: if there are NULL items in the vector, the comparison function MUST
* be ready for them.
*/
-vector_index
+extern vector_index_t
vector_bsearch(vector v, vector_bsearch_cmp* cmp, const void* p_val,
- int* result)
+ int* result)
{
- vector_index il, iv, ih ;
+ vector_index_t il, iv, ih ;
int c ;
if (v->end <= 1)
{
- *result = (v->end == 0) ? -1 :
- cmp(&p_val, (const void* const*)&v->p_items[0]) ;
- return 0 ; /* Stop dead if 0 or 1 items */
+ *result = (v->end == 0) ? -1 : cmp(&p_val, (const cvp*)&v->p_items[0]) ;
+ return 0 ; /* Stop dead if 0 or 1 items */
} ;
/* We have at least two items. */
@@ -997,38 +1064,38 @@ 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* const*)&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. */
+ *result = c ; /* 0 => found. +1 => val > last */
+ return ih ; /* return high index. */
} ;
- if ((c = cmp(&p_val, (const void* const*)&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. */
+ *result = c ; /* 0 => found. -1 => val < first */
+ return il ; /* return low index. */
}
- /* Now binary chop. We know that item[il] < val < item[ih] */
- /* We also know that il < ih */
+ /* Now binary chop. We know that item[il] < val < item[ih] */
+ /* We also know that il < ih */
while (1)
{
iv = (il + ih) / 2 ;
- if (iv == il) /* true if (ih == il+1) */
- {
- *result = +1 ;
- return il ; /* return il: item[il] < val < item[il+1] */
- } ;
- /* We now know that il < iv < ih */
- c = cmp(&p_val, (const void* const*)&v->p_items[iv]) ;
+ if (iv == il) /* true if (ih == il+1) */
+ {
+ *result = +1 ;
+ return il ; /* return il: item[il] < val < item[il+1] */
+ } ;
+ /* We now know that il < iv < ih */
+ c = cmp(&p_val, (const cvp*)&v->p_items[iv]) ;
if (c == 0)
- {
- *result = 0 ;
- return iv ; /* found !! */
- }
+ {
+ *result = 0 ;
+ return iv ; /* found !! */
+ }
if (c < 0)
- ih = iv ; /* step down iv > il, so new ih > il */
+ ih = iv ; /* step down iv > il, so new ih > il */
else
- il = iv ; /* step up iv < ih, so new il < ih */
+ il = iv ; /* step up iv < ih, so new il < ih */
} ;
} ;
@@ -1042,7 +1109,8 @@ vector_bsearch(vector v, vector_bsearch_cmp* cmp, const void* p_val,
/* Round 'x' up to a multiple of 'm' */
#define multiple(x, m) ((((x) + (m) - 1) / (m)) * (m))
-/* Set new limit to suit new end for given vector.
+/*------------------------------------------------------------------------------
+ * Set new limit to suit new end for given vector.
*
* The new limit will be at least: VECTOR_LIMIT_MIN.
*
@@ -1071,10 +1139,10 @@ vector_bsearch(vector v, vector_bsearch_cmp* cmp, const void* p_val,
* of P_ITEMS_SIZE will ?
*/
static void
-vector_new_limit(vector v, vector_index new_end)
+vector_new_limit(vector v, vector_index_t new_end)
{
- vector_index old_limit = v->limit ;
- vector_index new_limit ;
+ vector_length_t old_limit = v->limit ;
+ vector_length_t new_limit ;
if (new_end > ((VECTOR_LIMIT_DOUBLE_MAX * 7) / 8))
{
@@ -1103,17 +1171,18 @@ vector_new_limit(vector v, vector_index new_end)
v->limit = new_limit ;
} ;
-/* Extend vector and set new (logical) end.
+/*------------------------------------------------------------------------------
+ * Extend vector and set new (logical) end.
*
* Extends body if required.
* Ensures everything between old and new end is set NULL.
*
* NB: expects new end > old end, but copes with new end <= old end.
*/
-void
-vector_extend(vector v, vector_index new_end)
+extern void
+vector_extend(vector v, vector_length_t new_end)
{
- vector_index old_end = v->end ;
+ vector_length_t old_end = v->end ;
if (new_end > v->limit)
vector_new_limit(v, new_end) ;
@@ -1123,16 +1192,17 @@ vector_extend(vector v, vector_index new_end)
memset(&v->p_items[old_end], 0, P_ITEMS_SIZE(new_end - old_end)) ;
} ;
-/* Insert entries into vector: insert 'n' NULL entries at location 'i'.
+/*------------------------------------------------------------------------------
+ * Insert entries into vector: insert 'n' NULL entries at location 'i'.
*
* Updates end (and limit) to be at least 'i' + 'n'.
* (So if 'i' < end then end becomes end + 'n', else end becomes 'i' + 'n'.)
*/
-void
-vector_insert(vector v, vector_index i, unsigned int n)
+extern void
+vector_insert(vector v, vector_index_t i, vector_length_t n)
{
- vector_index old_end, new_end ;
- unsigned int n_above ;
+ vector_length_t old_end, new_end ;
+ vector_length_t n_above ;
/* If i < old end, then we are inserting n NULLs, and need
* to shuffle at least one item up.
@@ -1143,19 +1213,19 @@ vector_insert(vector v, vector_index i, unsigned int n)
if (i < old_end)
{
if (n == 0)
- return ; /* give up now if not inserting anything */
- n_above = old_end - i ; /* number of items to shuffle up.. >= 1 */
+ return ; /* give up now if not inserting anything */
+ n_above = old_end - i ; /* number of items to shuffle up.. >= 1 */
new_end = old_end + n ;
}
else
{
- n_above = 0 ; /* nothing to shuffle up. */
+ n_above = 0 ; /* nothing to shuffle up. */
new_end = i + n ;
i = old_end ; /* where to zeroize from */
n = new_end - old_end ; /* how much to zeroize */
} ;
- /* Now we extend the body if we need to. */
+ /* Now we extend the body if we need to. */
if (new_end > v->limit)
vector_new_limit(v, new_end) ;
v->end = new_end ;
@@ -1166,7 +1236,8 @@ vector_insert(vector v, vector_index i, unsigned int n)
memset(&v->p_items[i], 0, P_ITEMS_SIZE(n)) ;
} ;
-/* Delete items from vector: delete 'n' items at location 'i'.
+/*------------------------------------------------------------------------------
+ * Delete items from vector: delete 'n' items at location 'i'.
*
* Does nothing if 'i' is beyond current end of vector or if 'n' == 0.
*
@@ -1177,10 +1248,10 @@ vector_insert(vector v, vector_index i, unsigned int n)
* NB: it is the caller's responsibility to have released any memory allocated
* for the items that are being deleted.
*/
-void
-vector_delete(vector v, vector_index i, unsigned int n)
+extern void
+vector_delete(vector v, vector_index_t i, vector_length_t n)
{
- vector_index old_end, new_end ;
+ vector_length_t old_end, new_end ;
old_end = v->end ;
@@ -1191,7 +1262,7 @@ vector_delete(vector v, vector_index i, unsigned int n)
if ((i + n) < old_end)
{
memmove(&v->p_items[i], &v->p_items[i + n],
- P_ITEMS_SIZE(old_end - (i + n))) ;
+ P_ITEMS_SIZE(old_end - (i + n))) ;
new_end = old_end - n ;
}
else
@@ -1203,7 +1274,8 @@ vector_delete(vector v, vector_index i, unsigned int n)
v->end = new_end ; /* account for stuff dropped */
} ;
-/* Discard entries from vector: discard entries from location 'i' onwards.
+/*------------------------------------------------------------------------------
+ * Discard entries from vector: discard entries from location 'i' onwards.
*
* Releases memory from 'i' onwards.
* Releases the body altogether if this sets the vector empty ('i' == 0).
@@ -1214,8 +1286,8 @@ vector_delete(vector v, vector_index i, unsigned int n)
* NB: it is the caller's responsibility to have released any memory allocated
* for the items that are being discarded.
*/
-void
-vector_discard(vector v, vector_index i)
+extern void
+vector_discard(vector v, vector_index_t i)
{
if (i >= v->limit)
return ;
@@ -1231,34 +1303,37 @@ vector_discard(vector v, vector_index i)
} ;
} ;
-/* Chop vector at the current (logical) end.
+/*------------------------------------------------------------------------------
+ * Chop vector at the current (logical) end.
*
* Releases the body altogether if the vector is currently empty.
-*/
-void
+ */
+extern void
vector_chop(vector v)
{
- vector_index new_limit = v->end ;
+ vector_length_t new_limit = v->end ;
if (new_limit == 0)
vector_reset(v, 0) ; /* reset, without releasing the structure */
else if (new_limit != v->limit)
{
v->limit = new_limit ;
- v->p_items = XREALLOC(MTYPE_VECTOR_BODY, v->p_items, P_ITEMS_SIZE(new_limit)) ;
+ v->p_items = XREALLOC(MTYPE_VECTOR_BODY, v->p_items,
+ P_ITEMS_SIZE(new_limit)) ;
} ;
} ;
-/* Decant vector into a new body allocated to current logical size, and
+/*------------------------------------------------------------------------------
+ * Decant vector into a new body allocated to current logical size, and
* release the old body.
*
* Releases the body altogether if the vector is currently empty.
*/
-void
+extern void
vector_decant(vector v)
{
p_vector_item* p_old_body ;
- vector_index new_limit = v->end ;
+ vector_length_t new_limit = v->end ;
if (new_limit == 0)
vector_reset(v, 0) ; /* reset, without releasing the structure */
diff --git a/lib/vector.h b/lib/vector.h
index e69e628a..bae2a2f8 100644
--- a/lib/vector.h
+++ b/lib/vector.h
@@ -25,30 +25,41 @@
#ifndef _ZEBRA_VECTOR_H
#define _ZEBRA_VECTOR_H
-/* Macro in case there are particular compiler issues. */
-#ifndef Inline
- #define Inline static inline
-#endif
+#include "misc.h"
/*------------------------------------------------------------------------------
* types and struct for vector
*
* NB: an entirely zero structure represents an entirely empty vector.
- *
- * TODO: could force vector_index to be 32 bits ?
*/
typedef void* p_vector_item ;
-typedef unsigned int vector_index ;
+typedef unsigned int vector_index_t ;
+typedef unsigned int vector_length_t ;
+
+enum {
+ VECTOR_LENGTH_MAX = (vector_length_t)INT_MAX + 1
+} ;
-typedef struct vector* vector ; /* pointer to vector structure */
-typedef struct vector vector_t ; /* embedded vector structure */
struct vector
{
- p_vector_item* p_items ; /* pointer to array of vector item pointers */
- vector_index end ; /* number of "active" item entries */
- vector_index limit ; /* number of allocated item entries */
+ p_vector_item* p_items ; /* pointer to array of vector item pointers */
+ vector_length_t end ; /* number of "active" item entries */
+ vector_length_t limit ; /* number of allocated item entries */
};
+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 ;
@@ -83,7 +94,7 @@ typedef p_vector_item const* vector_body_t ;
/* To walk all items in a vector:
*
- * vector_index i ;
+ * vector_index_t i ;
* xxxxx* p_v ;
*
* for (VECTOR_ITEMS(v, p_v, i))
@@ -104,74 +115,67 @@ typedef p_vector_item const* vector_body_t ;
* Prototypes.
*/
-extern vector vector_init (unsigned int size);
-Inline void vector_ensure(vector v, vector_index i) ;
+extern vector vector_init (vector_length_t size);
+Inline void vector_ensure(vector v, vector_index_t i) ;
extern int vector_set (vector v, void *val);
-extern int vector_set_index (vector v, vector_index i, void *val);
+extern int vector_set_index (vector v, vector_index_t i, void *val);
#define vector_unset(v, i) (void)vector_unset_item(v, i)
-extern vector_index vector_count (vector v);
+extern vector_length_t vector_count (vector v);
extern void vector_free (vector v);
extern vector vector_copy (vector v);
-extern void *vector_lookup (vector, vector_index);
-extern void *vector_lookup_ensure (vector, vector_index);
+extern void *vector_lookup (vector, vector_index_t);
+extern void *vector_lookup_ensure (vector, vector_index_t);
-extern vector vector_init_new(vector v, unsigned int size) ;
-extern vector vector_re_init(vector v, unsigned int size) ;
-extern vector vector_reset(vector v, int free_structure) ;
-extern p_vector_item vector_ream(vector v, int free_structure) ;
+extern vector vector_init_new(vector v, vector_length_t size) ;
+extern vector vector_re_init(vector v, vector_length_t size) ;
+extern vector vector_reset(vector v, free_keep_b free_structure) ;
+extern p_vector_item vector_ream(vector v, free_keep_b free_structure) ;
-/* Reset vector and free the vector structure. */
-#define vector_reset_free(v) vector_reset(v, 1)
-/* Reset vector but free the heap structure. */
-#define vector_reset_keep(v) vector_reset(v, 0)
-/* Ream out vector and free the vector structure. */
-#define vector_ream_free(v) vector_ream(v, 1)
-/* Ream out vector but keep the vector structure. */
-#define vector_ream_keep(v) vector_ream(v, 0)
+Inline void vector_set_min_length(vector v, vector_length_t len) ;
+Private void vector_set_new_min_length(vector v, vector_length_t len) ;
-Inline void vector_set_min_length(vector v, unsigned int len) ;
-extern void vector_set_new_min_length(vector v, unsigned int len) ;
-
-Inline void vector_set_length(vector v, unsigned int len) ;
+Inline void vector_set_length(vector v, vector_length_t len) ;
#define vector_set_end(v, l) vector_set_length(v, l)
-Inline vector_index vector_length(vector v) ;
-#define vector_end(v) vector_length(v)
-Inline int vector_is_empty(vector v) ;
+Inline vector_length_t vector_length(vector v) ;
+#define vector_end(v) vector_length(v)
+Inline bool vector_is_empty(vector v) ;
Inline vector_body_t vector_body(vector v) ;
-Inline p_vector_item vector_get_item(vector v, vector_index i) ;
+Inline p_vector_item vector_get_item(vector v, vector_index_t i) ;
Inline p_vector_item vector_get_first_item(vector v) ;
Inline p_vector_item vector_get_last_item(vector v) ;
-Inline void vector_set_item(vector v, vector_index i, p_vector_item p_v) ;
-Inline void vector_assign_item(vector v, vector_index dst, vector_index src) ;
-extern p_vector_item vector_unset_item(vector v, vector_index i) ;
-extern vector_index vector_trim(vector v) ;
-extern vector_index vector_condense(vector v) ;
-
-extern void vector_insert_item(vector v, vector_index i, p_vector_item p_v) ;
-extern void vector_insert_item_here(vector v, vector_index i, int rider,
+Inline void vector_set_item(vector v, vector_index_t i, p_vector_item p_v) ;
+Inline void vector_assign_item(vector v, vector_index_t dst,
+ vector_index_t src) ;
+extern p_vector_item vector_unset_item(vector v, vector_index_t i) ;
+extern vector_length_t vector_trim(vector v) ;
+extern vector_length_t vector_condense(vector v) ;
+
+extern void vector_insert_item(vector v, vector_index_t i, p_vector_item p_v) ;
+extern void vector_insert_item_here(vector v, vector_index_t i, int rider,
p_vector_item p_v) ;
-extern void vector_move_item(vector v, vector_index dst, vector_index src) ;
-extern void vector_move_item_here(vector v, vector_index dst, int rider,
- vector_index src) ;
-extern p_vector_item vector_delete_item(vector v, vector_index i) ;
+extern void vector_move_item(vector v, vector_index_t dst, vector_index_t src) ;
+extern void vector_move_item_here(vector v, vector_index_t dst, int rider,
+ vector_index_t src) ;
+extern p_vector_item vector_delete_item(vector v, vector_index_t i) ;
extern void vector_reverse(vector v) ;
-extern void vector_part_reverse(vector v, vector_index i, unsigned int n) ;
+extern void vector_part_reverse(vector v, vector_index_t i, vector_length_t n) ;
Inline void vector_push_item(vector v, p_vector_item p_v) ;
Inline p_vector_item vector_pop_item(vector v) ;
+Inline void vector_unshift_item(vector v, p_vector_item p_v) ;
+Inline p_vector_item vector_shift_item(vector v) ;
-extern void vector_insert(vector v, vector_index i, unsigned int n) ;
-extern void vector_delete(vector v, vector_index i, unsigned int n) ;
+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* const* pp_val,
- const void* const* item) ;
-vector_index vector_bsearch(vector v, vector_bsearch_cmp* cmp,
+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* const* a, const void* const* 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) ;
@@ -193,15 +197,15 @@ extern vector vector_move_append(vector dst, vector src) ;
vector_sak(0, NULL, dst, i_dst, n_dst, src, i_src, n_src, 1)
extern vector vector_sak(int to_copy, vector to,
- vector dst, vector_index i_dst, unsigned int n_dst,
- vector src, vector_index i_src, unsigned int n_src, int src_move) ;
+ vector dst, vector_index_t i_dst, vector_length_t n_dst,
+ vector src, vector_index_t i_src, vector_length_t n_src, int src_move) ;
-extern void vector_discard(vector v, vector_index i) ;
+extern void vector_discard(vector v, vector_index_t i) ;
extern void vector_chop(vector v) ;
extern void vector_decant(vector v) ;
-Inline vector_index vector_extend_by_1(vector v) ;
-extern void vector_extend(vector v, vector_index new_end) ;
+Inline vector_index_t vector_extend_by_1(vector v) ;
+extern void vector_extend(vector v, vector_length_t new_end) ;
/*==============================================================================
* The inline functions:
@@ -210,10 +214,10 @@ extern void vector_extend(vector v, vector_index new_end) ;
/* Extend vector by one item at the end, which is about to be set. */
/* Returns index of new least item in the vector. */
/* NB: if left unset, the item may be UNDEFINED. */
-Inline vector_index
+Inline vector_index_t
vector_extend_by_1(vector v)
{
- vector_index i = v->end ;
+ vector_index_t i = v->end ;
if (i < v->limit)
return v->end++ ; /* simple if we have room */
@@ -226,7 +230,7 @@ vector_extend_by_1(vector v)
/* Adjusts logical and physical end of the vector as required, filling */
/* with NULLs upto any new logical end. */
Inline void
-vector_ensure(vector v, vector_index i)
+vector_ensure(vector v, vector_index_t i)
{
if (i < v->end) /* trivial if within vector */
return ;
@@ -242,7 +246,7 @@ vector_ensure(vector v, vector_index i)
/* with NULLs upto any new logical end -- does not allocate any more */
/* than is exactly necessary. */
Inline void
-vector_set_min_length(vector v, unsigned int len)
+vector_set_min_length(vector v, vector_length_t len)
{
if (len > v->end) /* will not reduce the length */
vector_set_new_min_length(v, len) ;
@@ -257,7 +261,7 @@ vector_set_min_length(vector v, unsigned int len)
/* with NULLs upto any new logical end -- does not allocate any more */
/* than is exactly necessary. */
Inline void
-vector_set_length(vector v, unsigned int len)
+vector_set_length(vector v, vector_length_t len)
{
if (len > v->end)
vector_set_new_min_length(v, len) ; /* Extend if new length greater */
@@ -266,14 +270,14 @@ vector_set_length(vector v, unsigned int len)
} ;
/* Return index of end of vector (index of last item + 1) */
-Inline vector_index
+Inline vector_length_t
vector_length(vector v)
{
return v->end ;
} ;
/* Returns whether vector is empty or not. */
-Inline int
+Inline bool
vector_is_empty(vector v)
{
return (v->end == 0) ;
@@ -296,7 +300,7 @@ vector_body(vector v)
/* Get pointer to item. Returns NULL if accessing beyond end. */
Inline p_vector_item
-vector_get_item(vector v, vector_index i)
+vector_get_item(vector v, vector_index_t i)
{
return (i < v->end) ? v->p_items[i] : NULL ;
} ;
@@ -319,7 +323,7 @@ vector_get_last_item(vector v)
/* NB: it is the caller's responsibility to release memory used by any */
/* current value of the item, if required. */
Inline void
-vector_set_item(vector v, vector_index i, void* p_v)
+vector_set_item(vector v, vector_index_t i, p_vector_item p_v)
{
vector_ensure(v, i) ;
v->p_items[i] = (p_vector_item)p_v ;
@@ -331,16 +335,16 @@ vector_set_item(vector v, vector_index i, void* p_v)
* used by the current dst item or the new (duplicated) src item.
*/
Inline void
-vector_assign_item(vector v, vector_index dst, vector_index src)
+vector_assign_item(vector v, vector_index_t dst, vector_index_t src)
{
vector_set_item(v, dst, vector_get_item(v, src)) ;
} ;
/* Push value onto vector, extending as required. */
Inline void
-vector_push_item(vector v, void* p_v)
+vector_push_item(vector v, p_vector_item p_v)
{
- vector_index i = vector_extend_by_1(v) ;
+ vector_index_t i = vector_extend_by_1(v) ;
v->p_items[i] = (p_vector_item)p_v ;
} ;
@@ -352,4 +356,22 @@ vector_pop_item(vector v)
return (v->end > 0) ? v->p_items[--v->end] : NULL ;
} ;
+/* Unshift value onto start of vector, extending as required. */
+Inline void
+vector_unshift_item(vector v, p_vector_item p_v)
+{
+ vector_insert(v, 0, 1) ;
+ v->p_items[0] = p_v ;
+} ;
+
+/* Pop value from vector. Returns NULL if vector is empty. */
+/* NB: does NOT change the size of the vector body. */
+Inline p_vector_item
+vector_shift_item(vector v)
+{
+ p_vector_item p_v = vector_get_first_item(v) ;
+ vector_delete(v, 0, 1) ;
+ return p_v ;
+} ;
+
#endif /* _ZEBRA_VECTOR_H */
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 685f33ec..65f19411 100644
--- a/lib/vio_fifo.c
+++ b/lib/vio_fifo.c
@@ -19,15 +19,14 @@
* Boston, MA 02111-1307, USA.
*/
-#include <stddef.h>
-#include <string.h>
+#include "misc.h"
+#include <stdio.h>
#include "vio_fifo.h"
+#include "qfstring.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,503 +38,536 @@
* ever needed.
*
* When releasing lumps, keeps one lump "spare", to be reused as necessary.
- * This is used in ... TODO <<<< And is released...
*
*------------------------------------------------------------------------------
* Implementation notes:
*
- * The FIFO is initialised with all pointers NULL -- so with no lumps at all.
+ * The FIFO is initialised with one lump in it (the "own_lump", which is
+ * embedded in the FIFO structure). There is always at least one lump in
+ * the FIFO.
*
- * Once a lump has been allocated there is always one lump in the FIFO.
+ * The hold_ptr allows the get_ptr to move forward, but retaining the data in
+ * the FIFO until the hold_ptr is cleared. Can move the get_ptr back to the
+ * hold_ptr to reread the data.
+ *
+ * The end_ptr allows put_ptr to move forward, but the new data cannot be got
+ * from the FIFO until the end_ptr is cleared. Can discard the new data
+ * and move the put_ptr back to the end_ptr.
+ *
+ * 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
+ * * p_start == &get_ptr => no hold mark
+ * &hold_ptr => have hold mark
+ *
+ * * put_ptr == get_ptr => FIFO empty -- unless *p_start != get_ptr.
*
- * * put_ptr == tail->end -- at all times (NULL when no lumps)
+ * * put_end == tail->end -- at all times
*
* 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
+ * * p_get_end == &get_lump->end -- when get_lump != end_lump
+ * == &end_ptr -- when get_lump == end_lump & end mark set
+ * == &put_ptr -- when get_lump == end_lump & no end mark
*
- * 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 => broken
+ * * get_ptr == *p_get_end => nothing to get -- get_lump == end_lump
+ * get_ptr < *p_get_end => data exists in the current get_lump
+ * get_ptr > *p_get_end => broken
+ *
+ * * p_end == &end_ptr -- when end mark set
+ * == &put_ptr -- when no end mark
*
* 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 < *p_get_end can get stuff without worrying about other
+ * pointers or moving between lumps etc.
+ *
+ * When get_ptr reaches *p_get_end, however, must move to the next lump,
+ * if possible, or collapse the pointers if have hit the put_ptr. Keeping
+ * the get_ptr honest in this way: (a) ensures that will always put at
+ * the beginning of a lump if possible; (b) simplifies the handling of
+ * hold_ptr et al (because get_ptr is never in the ambiguous position
+ * at the end of one lump, which is the same as the start of the next).
+ *
+ * Similarly, while put_ptr < put_end, can put stuff without worrying
+ * about other pointers or moving between lumps etc. Will leave the
+ * put_ptr at the very end of the current lump if just fills it.
+ *
+ * * the value of p_get_end depends on whether get_lump == end_lump, and
+ * then whether there is an end_ptr. But at any time, points to the end
+ * of what can be read by get_ptr without stepping between lumps etc.
+ *
+ * Note that get_ptr == p_get_end <=> is at the current end of the FIFO,
+ * because after any get operation, will advance to the next lump. If
+ * FIFO is empty after advancing the get_ptr, will reset the pointers
+ * back to the start of the then current (and only) lump.
+ *
+ * * some care must be taken to ensure that if the fifo is empty, the
+ * pointers will be at the start of one empty lump.
+ *
+ * In this context, empty means nothing between *p_start and put_ptr.
+ * Noting that *p_start is &hold_ptr or &get_ptr, depending on whether
+ * there is a hold mark or not. (If *p_start == put_ptr, there may be
+ * an end mark, but it must be end_ptr == put_ptr !)
*
- * 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.
+ * - get_ptr -- as above, if this hits *p_get_end, must:
*
- * * when advancing the put_ptr does not check for advancing the get_end.
+ * * step to the next lump, if there is one, and,
+ * unless something is held behind the get_ptr,
+ * release the lump just stepped from.
*
- * 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.
+ * * if has hit put_ptr, reset pointers -- unless there
+ * is something held behind the get_ptr.
*
- * Everywhere that the get_end is used, must check for there being one
- * lump and the possibility that put_ptr has changed.
+ * - put_ptr -- is always somewhere in the tail lump.
+ *
+ * - end_ptr -- when a new lump is added, if the end_ptr is at the
+ * end of the last lump, it is moved to the start of the
+ * new last lump (along with the put_ptr).
+ *
+ * If the end_ptr is equal to the put_ptr and the get_ptr
+ * hits it, if the pointers are reset, the end_ptr will be
+ * reset along with the put_ptr.
+ *
+ * If the put_ptr is reset back to the end_ptr, need to
+ * see if the FIFO is empty, and reset pointers if it is.
+ *
+ * - hold_ptr -- because the pointers are reset whenever the FIFO
+ * becomes empty, when hold_ptr is set, it will be at
+ * the start of an empty FIFO, or somewhere in a not-
+ * empty one. When a hold_ptr is cleared, the get_ptr
+ * may be equal to the put_ptr, and pointers must be
+ * reset.
*/
+inline static void vio_fifo_set_get_ptr(vio_fifo vff, vio_fifo_lump lump,
+ char* ptr) ;
+inline static void vio_fifo_set_get_end(vio_fifo vff) ;
+inline static void vio_fifo_release_upto(vio_fifo vff, vio_fifo_lump upto) ;
+static void vio_fifo_release_lump(vio_fifo vff, vio_fifo_lump lump) ;
-/*==============================================================================
- * Initialisation, allocation and freeing of FIFO and lumps thereof.
+/*------------------------------------------------------------------------------
+ * Test whether there is a hold mark.
*/
-
-/* Return default size, or given size rounded up to 16 byte boundary */
-static size_t
-vio_fifo_size(size_t size)
+inline static bool
+vio_fifo_have_hold_mark(vio_fifo vff)
{
- if (size == 0)
- return 4096 ;
- else
- return ((size + 16 - 1) / 16) * 16 ;
+ return vff->p_start == &vff->hold_ptr ;
} ;
-/*==============================================================================
- * Initialise VTY I/O FIFO -- allocating if required.
+/*------------------------------------------------------------------------------
+ * Test whether there is an end mark.
*/
-extern vio_fifo
-vio_fifo_init_new(vio_fifo vf, size_t size)
+inline static bool
+vio_fifo_have_end_mark(vio_fifo vff)
{
- if (vf == NULL)
- vf = XCALLOC(MTYPE_VIO_FIFO, sizeof(vio_fifo_t)) ;
- else
- memset(vf, 0, sizeof(vio_fifo_t)) ;
-
- /* Zeroising the the vio_fifo_t has set:
- *
- * lump -- 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
- *
- * 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
- *
- * spare -- NULL no spare lump
- *
- * ALSO put_ptr == get_ptr => FIFO is empty !
- */
-
- vf->size = vio_fifo_size(size) ;
-
- VIO_FIFO_DEBUG_VERIFY(vf) ;
-
- return vf ;
-}
+ return vff->p_end == &vff->end_ptr ;
+} ;
/*------------------------------------------------------------------------------
- * Free contents of given FIFO, and free FIFO structure as well, if required.
- *
- * Does nothing if given a NULL pointer -- must already have been freed !
+ * The FIFO is empty, with one lump -- reset all pointers.
*
- * If does not free the FIFO structure, resets it all empty.
+ * Preserves and hold mark or end mark -- so no need to change p_start or p_end.
*
- * Frees *all* FIFO lumps.
+ * HOWEVER: does not set p_get_end -- so if get_lump or end_lump or p_end have
+ * changed, then must also call vio_fifo_set_get_end().
*
- * See also: vio_fifo_reset_keep(vio_fifo)
- * vio_fifo_reset_free(vio_fifo)
+ * ALSO: does not set put_end -- so if the tail lumps has changed, that must
+ * be updated.
*/
-extern vio_fifo
-vio_fifo_reset(vio_fifo vf, int free_structure)
+inline static void
+vio_fifo_reset_ptrs(vio_fifo vff)
{
- vio_fifo_lump lump ;
+ char* ptr = ddl_tail(vff->base)->data ;
+
+ if (vio_fifo_debug)
+ assert(ddl_head(vff->base) == ddl_tail(vff->base)) ;
- if (vf == NULL)
- return NULL ;
+ vff->hold_ptr = ptr ;
+ vff->get_ptr = ptr ;
+ vff->end_ptr = ptr ;
+ vff->put_ptr = ptr ;
+} ;
- while (ddl_pop(&lump, vf->base, list) != NULL)
- XFREE(MTYPE_VIO_FIFO_LUMP, lump) ;
+/*------------------------------------------------------------------------------
+ * This is called *iff* get_ptr >= *p_get_end -- and preferably only after
+ * it has been adjusted forwards by at least 1 (but that is not required).
+ *
+ * If there is anything available to be got, adjust get_ptr and/or get_end
+ * in order to be able to get it -- discarding lumps as required.
+ */
+Private void
+vio_fifo_sync_get(vio_fifo vff)
+{
+ if (vio_fifo_debug)
+ assert(vff->get_ptr == *vff->p_get_end) ;
- if (vf->spare != NULL)
- XFREE(MTYPE_VIO_FIFO_LUMP, vf->spare) ;
+ if (vff->get_lump == vff->end_lump)
+ {
+ if (vio_fifo_debug)
+ assert(vff->get_ptr == *vff->p_end) ;
- if (free_structure)
- XFREE(MTYPE_VIO_FIFO, vf) ; /* sets vf = NULL */
+ /* We are in the end_lump, and there is nothing more to be read.
+ *
+ * If have reached the put_ptr, then unless there is something held
+ * behind the get_ptr, the fifo is completely empty, and pointers can
+ * be reset to the start of the end_lump (which is the only lump).
+ *
+ * p_start == &hold_ptr or &get_ptr, so can check for empty in one test.
+ */
+ if (*vff->p_start == vff->put_ptr)
+ vio_fifo_reset_ptrs(vff) ;
+ }
else
- vio_fifo_init_new(vf, vf->size) ;
+ {
+ /* Good news, can advance the get_ptr
+ *
+ * Step the get_ptr to the start of the next lump, and if no hold mark,
+ * discard any lumps which precede the new get_lump.
+ */
+ vio_fifo_lump get_lump ;
- return vf ;
+ if (vio_fifo_debug)
+ {
+ assert(vff->get_lump != vff->end_lump) ;
+ assert(vff->get_ptr == vff->get_lump->end) ;
+ } ;
+
+ get_lump = ddl_next(vff->get_lump, list) ;
+ vio_fifo_set_get_ptr(vff, get_lump, get_lump->data) ;
+
+ if (!vio_fifo_have_hold_mark(vff))
+ vio_fifo_release_upto(vff, get_lump) ;
+ } ;
} ;
/*------------------------------------------------------------------------------
- * The FIFO is empty, with one lump -- reset all pointers.
+ * Set get_lump/get_ptr and p_get_end to suit.
*/
inline static void
-vio_fifo_ptr_reset(vio_fifo vf, vio_fifo_lump lump)
+vio_fifo_set_get_ptr(vio_fifo vff, vio_fifo_lump lump, char* ptr)
{
- if (vf->rdr_lump != NULL)
- {
- assert((lump == vf->rdr_lump) && (vf->rdr_ptr == vf->get_ptr)) ;
- vf->rdr_ptr = lump->data ;
- } ;
+ vff->get_lump = lump ;
+ vff->get_ptr = lump->data ;
- /* 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 ;
+ vio_fifo_set_get_end(vff) ;
} ;
/*------------------------------------------------------------------------------
- * The FIFO is utterly empty, with ZERO lumps -- unset all pointers.
+ * Set the p_get_end depending on whether the get_lump == end_lump, or not.
+ *
+ * This must be called if the get_lump or the end_lump are changed, or if
+ * p_end changes.
*/
inline static void
-vio_fifo_ptr_unset(vio_fifo vf)
+vio_fifo_set_get_end(vio_fifo vff)
{
- assert((ddl_head(vf->base) == NULL) && (ddl_tail(vf->base) == NULL)) ;
-
- vf->one = false ;
-
- vf->put_ptr = NULL ;
- vf->put_end = NULL ;
- vf->get_ptr = NULL ;
- vf->get_end = NULL ;
+ vff->p_get_end = (vff->get_lump == vff->end_lump) ? vff->p_end
+ : &vff->get_lump->end ;
+} ;
- vf->rdr_lump = NULL ;
- vf->rdr_ptr = NULL ;
+/*------------------------------------------------------------------------------
+ * Release all lumps upto (but excluding) the given lump.
+ *
+ * NB: takes no notice of hold_ptr or anything else.
+ */
+inline static void
+vio_fifo_release_upto(vio_fifo vff, vio_fifo_lump upto)
+{
+ vio_fifo_lump lump ;
+ while (ddl_head(vff->base) != upto)
+ vio_fifo_release_lump(vff, ddl_pop(&lump, vff->base, list)) ;
} ;
+/*==============================================================================
+ * Initialisation, allocation and freeing of FIFO and lumps thereof.
+ */
+
/*------------------------------------------------------------------------------
- * Clear out contents of FIFO -- will continue to use the FIFO.
+ * Allocate and initialise a new FIFO.
*
- * Keeps one FIFO lump. (Frees everything else, including any spare.)
+ * The size given is the size for all lumps in the FIFO. 0 => default size.
+ *
+ * Size is rounded up to a 128 byte boundary.
+ *
+ * Once allocated and initialised, the FIFO contains one lump, and if it
+ * grows to more than one, will retain a spare lump once it shrinks again.
+ *
+ * Keeping a pair of lumps allows the get_ptr to lag behind the put_ptr by
+ * about a lump full, without requiring repeated memory allocation. Also,
+ * vio_fifo_write_nb() can be asked to write only lumps -- so if called
+ * regularly while putting stuff to a FIFO, will write entire lumps at once.
*/
-extern void
-vio_fifo_clear(vio_fifo vf)
+extern vio_fifo
+vio_fifo_new(ulen size)
{
- vio_fifo_lump tail ;
+ vio_fifo vff ;
+ ulen total_size ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ if (size == 0)
+ size = VIO_FIFO_DEFAULT_LUMP_SIZE ;
- assert(vf != NULL) ;
+ size = ((size + 128 - 1) / 128) * 128 ;
- tail = ddl_tail(vf->base) ;
+ if (vio_fifo_debug)
+ size = 29 ;
- if (tail != NULL)
- {
- while (ddl_head(vf->base) != tail)
- {
- vio_fifo_lump lump ;
- ddl_pop(&lump, vf->base, list) ;
- XFREE(MTYPE_VIO_FIFO_LUMP, lump) ;
- } ;
+ total_size = offsetof(struct vio_fifo, own_lump[0].data[size]) ;
- vf->rdr_lump = NULL ; /* clear rdr */
- vf->rdr_ptr = NULL ;
+ vff = XCALLOC(MTYPE_VIO_FIFO, total_size) ;
- vf->one = true ;
- vio_fifo_ptr_reset(vf, tail) ;
- }
- else
- vio_fifo_ptr_unset(vf) ;
+ /* Zeroising the the vio_fifo_t has set:
+ *
+ * base -- base pair, both pointers NULL => list is empty
+ *
+ * p_start -- X -- see vio_fifo_ptr_set()
+ *
+ * hold_ptr -- NULL -- not relevant until hold mark is set
+ *
+ * get_lump -- X )
+ * get_ptr -- X ) -- see vio_fifo_ptr_set()
+ * p_get_end -- X )
+ *
+ * end_lump -- X -- see vio_fifo_ptr_set()
+ *
+ * end_ptr -- NULL -- not relevant until hold mark is set
+ *
+ * put_ptr -- X )
+ * put_end -- X ) -- see vio_fifo_ptr_set()
+ * p_end -- X )
+ *
+ * size -- X -- set below
+ *
+ * spare -- NULL -- no spare lump
+ *
+ * own_lump -- all zeros: list -- pointers NULL, set below
+ * end -- set below
+ */
+ vff->size = size ;
+ vff->own_lump->end = vff->own_lump->data + vff->size ;
+
+ if (vio_fifo_debug)
+ assert(vff->own_lump->end == ((char*)vff + total_size)) ;
+
+ ddl_append(vff->base, vff->own_lump, list) ;
+
+ vff->p_start = &vff->get_ptr ;
- if (vf->spare != NULL)
- XFREE(MTYPE_VIO_FIFO_LUMP, vf->spare) ; /* sets vf->spare = NULL */
+ vff->get_lump = vff->own_lump ;
+ vff->get_ptr = vff->own_lump->data ;
+ vff->p_get_end = &vff->put_ptr ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ vff->end_lump = vff->own_lump ;
+
+ vff->put_ptr = vff->own_lump->data ;
+ vff->put_end = vff->own_lump->end ;
+
+ vff->p_end = &vff->put_ptr ;
+
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
+
+ return vff ;
} ;
/*------------------------------------------------------------------------------
- * See how much room there is in the FIFO.
+ * Free contents of given FIFO and the FIFO structure as well.
*
- * If no lumps have been allocated, returns the size of the lump that would
- * allocate.
- *
- * Otherwise, returns the amount of space available *without* allocating any
- * further lumps.
+ * Does nothing if given a NULL pointer -- must already have been freed !
*
- * Returns: room available as described
+ * Returns: NULL
*/
-extern size_t
-vio_fifo_room(vio_fifo vf)
+extern vio_fifo
+vio_fifo_free(vio_fifo vff)
{
- if (vf->put_ptr != NULL)
- return vf->put_end - vf->put_ptr ;
- else
- return vf->size ;
+ if (vff != NULL)
+ {
+ vio_fifo_lump lump ;
+
+ lump = vff->spare ;
+ vff->spare = NULL ;
+
+ do
+ {
+ if (lump != vff->own_lump)
+ XFREE(MTYPE_VIO_FIFO_LUMP, lump) ; /* accepts lump == NULL */
+
+ ddl_pop(&lump, vff->base, list) ;
+ }
+ while (lump != NULL) ;
+
+ XFREE(MTYPE_VIO_FIFO, vff) ;
+ } ;
+
+ return NULL ;
} ;
/*------------------------------------------------------------------------------
- * Allocate another lump for putting into.
- *
- * Call when (vf->put_ptr >= vf->put_end) -- asserts that they are equal.
+ * Clear out contents of FIFO -- will continue to use the FIFO.
*
- * Set the put_ptr/put_end pointers to point at the new lump.
+ * If required, clears any hold mark and/or end mark.
*
- * If this is the first lump allocated, set the get_ptr/get_end pointers too.
+ * Keeps one spare lump.
*
- * 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.
+ * Does nothing if there is no FIFO !
*/
extern void
-vio_fifo_lump_new(vio_fifo vf, size_t size)
+vio_fifo_clear(vio_fifo vff, bool clear_marks)
{
vio_fifo_lump lump ;
- int first_alloc ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
-
- passert(vf->put_ptr == vf->put_end) ; /* must be end of tail lump */
+ if (vff == NULL)
+ return ;
- if (vf->one)
- vf->get_end = vf->put_ptr ; /* update get_end */
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
- lump = ddl_tail(vf->base) ;
+ lump = ddl_tail(vff->base) ;
- first_alloc = (lump == NULL) ; /* extra initialisation needed */
+ vio_fifo_release_upto(vff, lump) ;
- if (first_alloc)
- assert(vf->put_ptr == NULL) ; /* must all be NULL together */
- else
- assert(vf->put_ptr == lump->end) ; /* must be end of tail lump */
+ vff->get_lump = lump ;
+ vff->end_lump = lump ;
- size = vio_fifo_size(size) ;
+ vio_fifo_reset_ptrs(vff) ;
- if ((vf->spare != NULL) && (vf->spare->size >= size))
+ if (clear_marks)
{
- lump = vf->spare ;
- vf->spare = NULL ;
- }
- else
- {
- lump = XMALLOC(MTYPE_VIO_FIFO_LUMP,
- offsetof(vio_fifo_lump_t, data[size])) ;
- lump->size = size ;
+ vff->p_start = &vff->get_ptr ;
+ vff->p_end = &vff->put_ptr ;
} ;
- lump->end = lump->data + lump->size ;
-
- ddl_append(vf->base, lump, list) ;
+ vff->p_get_end = vff->p_end ;
- 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) ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
} ;
/*------------------------------------------------------------------------------
- * 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
+ * Add a new lump to put stuff into -- work-horse for putting to the FIFO.
*
- * If releasing the only lump, the FIFO MUST be empty.
+ * Call when (vff->put_ptr >= vff->put_end) -- asserts that they are equal.
*
- * * 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 !
+ * The FIFO cannot be empty -- if it were, the pointers would have been reset,
+ * and could not be vff->put_ptr >= vff->put_end !!
*
- * If releasing the tail lump:
+ * Allocates a new lump (or reuses the spare) and updates the put_ptr.
*
- * * 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 !
+ * If the end_ptr and the put_ptr were equal, then advances that too, which
+ * ensures that the end_ptr is not ambiguous.
+
+ * If the get_ptr and the put_ptr were equal, then advances that too, which
+ * ensures that the get_ptr is not ambiguous. This can be the case if there
+ * is a hold_ptr.
*/
-static void
-vio_fifo_lump_release(vio_fifo vf, vio_fifo_lump lump)
+Private void
+vio_fifo_add_lump(vio_fifo vff)
{
- vio_fifo_lump head ;
- vio_fifo_lump tail ;
- vio_fifo_lump free ;
- bool release_head ;
- bool release_tail ;
-
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ vio_fifo_lump lump ;
- /* Prepare and check whether removing head or tail (or both) */
- head = ddl_head(vf->base) ;
- tail = ddl_tail(vf->base) ;
+ assert(vff->put_ptr == vff->put_end) ; /* must be end of tail lump */
+ assert(vff->put_ptr != *vff->p_start) ; /* cannot be empty ! */
- release_head = (lump == head) ;
- release_tail = (lump == tail) ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
- assert(release_head || release_tail) ;
+ /* If we can use the spare, do so, otherwise make a new one and
+ * add to the end of the FIFO.
+ */
+ lump = vff->spare ;
+ vff->spare = NULL ;
- /* Unless nothing ever allocated -- release the lump. */
- free = lump ; /* expect to free the lump */
- if (lump != NULL)
+ if (lump == NULL)
{
- vio_fifo_lump keep ;
-
- /* Consistency checks */
- if (release_head)
- {
- if (release_tail)
- assert(vf->get_ptr == vf->put_ptr) ;
- else
- assert(vf->get_ptr == lump->end) ;
-
- if (vf->rdr_lump == lump)
- assert(vf->rdr_ptr == vf->get_ptr) ;
- }
- else if (release_tail)
- {
- assert(vf->put_ptr == lump->data) ;
-
- if (vf->rdr_lump == lump)
- assert(vf->rdr_ptr == vf->put_ptr) ;
- } ;
-
- /* 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 */
+ ulen lump_size = offsetof(vio_fifo_lump_t, data[vff->size]) ;
- if ((keep == NULL) || (keep->size < lump->size))
- {
- keep = lump ;
- free = vf->spare ;
- } ;
+ lump = XMALLOC(MTYPE_VIO_FIFO_LUMP, lump_size) ;
+ lump->end = (char*)lump + lump_size ;
- vf->spare = keep ;
-
- head = ddl_head(vf->base) ; /* changed if released head */
- tail = ddl_tail(vf->base) ; /* changed if released tail */
+ if (vio_fifo_debug)
+ assert(lump->end == (lump->data + vff->size)) ;
} ;
- /* 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) ;
+ ddl_append(vff->base, lump, list) ;
- vio_fifo_ptr_unset(vf) ;
- }
- else
+ /* Allocated new lump on the end of FIFO.
+ *
+ * If the get_ptr == put_ptr, advance the get_ptr. If there is an end_ptr,
+ * it must be == put_ptr, and is about to advance too.
+ *
+ * If put_ptr == *p_end, advance the end_lump and the end_ptr. If there is
+ * no end_mark, then p_end == &put_ptr, and the end_lump must follow the
+ * put_ptr. If there is an end_mark, then p_end == &end_ptr, and that must
+ * follow the put_ptr if they are equal.
+ *
+ * The get_lump may or may not have been the end_lump, and that may or may
+ * not have changed. Simplest thing is to set p_get_end to what it should
+ * be now.
+ */
+ if (vff->get_ptr == vff->put_ptr)
{
- /* Have at least one lump left -- so must have had at least two ! */
- assert(!vf->one) ;
+ if (vio_fifo_debug)
+ assert(vio_fifo_have_hold_mark(vff)) ;
- vf->one = (head == tail) ; /* update */
+ vff->get_lump = lump ;
+ vff->get_ptr = lump->data ;
+ } ;
- if (release_head)
- {
- /* 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 ;
- } ;
- } ;
+ if (vff->put_ptr == *vff->p_end)
+ {
+ vff->end_lump = lump ;
+ vff->end_ptr = lump->data ; /* no effect if no end_mark */
} ;
- /* Finally, free any lump that is actually to be freed */
+ vff->put_ptr = lump->data ;
+ vff->put_end = lump->end ;
- if (free != NULL)
- XFREE(MTYPE_VIO_FIFO_LUMP, free) ;
+ vio_fifo_set_get_end(vff) ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
} ;
/*------------------------------------------------------------------------------
- * Re-allocate lump for putting into.
- *
- * Call when vf->put_ptr == start of last lump, and that lump is not big
- * enough !
- *
- * There must be at least one lump.
+ * Release the given lump, provided it is neither get_lump nor end_lump.
*
- * Updates put_ptr/put_end pointers to point at the new lump.
- *
- * Updates get_ptr/get_end pointers if required.
- *
- * Updates rdr_ptr if required.
+ * If don't have a spare lump, keep this one.
+ * If do have a spare lump, discard this one, unless it is "own_lump".
*/
static void
-vio_fifo_lump_renew(vio_fifo vf, vio_fifo_lump lump, size_t size)
+vio_fifo_release_lump(vio_fifo vff, vio_fifo_lump lump)
{
- 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(lump != NULL) ;
+ assert(lump != vff->get_lump) ;
+ assert(lump != vff->end_lump) ;
- /* Now allocate a new lump with the required size */
- vio_fifo_lump_new(vf, size) ;
-
- /* Restore the rdr_ptr, if required */
- if (rdr_set)
+ if (vff->spare == NULL)
+ vff->spare = lump ;
+ else
{
- vio_fifo_lump tail ;
-
- tail = ddl_tail(vf->base) ;
+ if (lump == vff->own_lump)
+ {
+ lump = vff->spare ; /* free the spare instead */
+ vff->spare = vff->own_lump ;
+ } ;
- vf->rdr_lump = tail ;
- vf->rdr_ptr = tail->data ;
+ XFREE(MTYPE_VIO_FIFO_LUMP, lump) ;
} ;
-
- VIO_FIFO_DEBUG_VERIFY(vf) ;
} ;
/*==============================================================================
@@ -543,623 +575,786 @@ 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, ulen 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 */
+ ulen take ;
- take = (vf->put_end - vf->put_ptr) ;
+ if (vff->put_ptr >= vff->put_end)
+ vio_fifo_add_lump(vff) ; /* 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: uses qfs_vprintf(qfs, format, va), which allows the result to be
+ * collected a section at a time, if required. With reasonable size
+ * lumps, expect to need no more than two sections, and then only
+ * occasionally.
*/
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 va)
{
- va_list ac ;
- int len ;
- int have ;
- size_t size ;
- vio_fifo_lump lump ;
+ qf_str_t qfs ;
+ ulen done ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ if (vff == NULL)
+ return 0 ;
- size = vf->size ; /* standard allocation size */
- while (1)
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
+
+ done = 0 ;
+ do
{
- /* 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 */
+ ulen did ;
- have = vf->put_end - vf->put_ptr ;
- assert(have > 0) ;
+ if (vff->put_ptr >= vff->put_end)
+ vio_fifo_add_lump(vff) ; /* traps put_ptr > put_end */
- /* 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) ;
+ qfs_init_offset(qfs, vff->put_ptr, vff->put_end - vff->put_ptr, done) ;
+
+ did = qfs_vprintf(qfs, format, va) ;
+
+ done += did ;
+ vff->put_ptr += did ;
+ }
+ while (qfs_overflow(qfs) != 0) ;
+
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
+
+ return done ;
+} ;
- if (len < have)
+/*------------------------------------------------------------------------------
+ * Read part of file into FIFO -- assuming non-blocking file
+ *
+ * Will read up to the end of the current lump, then will read as may whole
+ * lumps as are requested -- request of 0 reads up to the end of the current
+ * lump (at least 1 byte). Will stop if would block.
+ *
+ * Except where blocking intervenes, this reads in units of the lump size.
+ *
+ * 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, ulen request)
+{
+ ulen total ;
+
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
+
+ total = 0 ;
+
+ do
+ {
+ int got ;
+
+ if (vff->put_ptr >= vff->put_end)
{
- if (len < 0)
- break ; /* quit if failed */
+ vio_fifo_add_lump(vff) ; /* traps put_ptr > put_end */
- vf->put_ptr += len ;
- break ; /* done */
+ if (request > 0)
+ --request ;
} ;
- /* 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 */
+ got = read_nb(fd, vff->put_ptr, vff->put_end - vff->put_ptr) ;
- lump = ddl_tail(vf->base) ;
+ if (got <= 0)
+ {
+ if (got == -2) /* EOF met */
+ return (total > 0) ? (int)total : got ;
+ else
+ return (got == 0) ? (int)total : got ;
+ } ;
- 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 ;
- } ;
+ vff->put_ptr += got ;
+ total += got ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ } while (request > 0) ;
- return len ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
+
+ return total ;
} ;
/*==============================================================================
- * Get data from the FIFO.
+ * Copy operations -- from one FIFO to another.
*/
-static bool vio_fifo_get_next_lump(vio_fifo vf) ;
-
/*------------------------------------------------------------------------------
- * Get ready to read something out of the FIFO.
+ * 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. There must be a src FIFO.
*
- * 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(vio_fifo dst, vio_fifo src)
{
- assert(vf->rdr_lump == NULL) ;
+ vio_fifo_lump src_lump ;
+ char* src_ptr ;
- if (vf->one)
- vf->get_end = vf->put_ptr ; /* make sure have everything */
+ if (dst == NULL)
+ dst = vio_fifo_new(src->size) ;
+
+ VIO_FIFO_DEBUG_VERIFY(src) ;
+ VIO_FIFO_DEBUG_VERIFY(dst) ;
+
+ 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 ; /* end of not end_lump */
+ else
+ src_end = *src->p_end ; /* end of end_lump */
- if (vf->get_ptr >= vf->get_end)
- if (!vio_fifo_get_next_lump(vf))
- return 0 ; /* quit now if nothing there */
+ vio_fifo_put_bytes(dst, src_ptr, src_end - src_ptr) ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ if (src_lump == src->end_lump)
+ break ;
- return 1 ;
+ src_lump = ddl_next(src_lump, list) ;
+ src_ptr = src_lump->data ;
+ } ;
+
+ VIO_FIFO_DEBUG_VERIFY(dst) ;
+
+ return dst ;
} ;
/*------------------------------------------------------------------------------
- * Get upto 'n' bytes.
+ * Copy tail of src FIFO (everything from end mark to put_ptr) to dst FIFO.
*
- * Returns: number of bytes got -- may be zero.
+ * Create a dst FIFO if there isn't one. There must be a src FIFO.
+ *
+ * Appends to the dst FIFO.
+ *
+ * Does not change the src FIFO in any way.
*/
-extern size_t
-vio_fifo_get(vio_fifo vf, void* dst, size_t n)
+extern vio_fifo
+vio_fifo_copy_tail(vio_fifo dst, vio_fifo src)
{
- size_t have ;
- void* dst_in ;
+ vio_fifo_lump src_lump ;
+ char* src_ptr ;
+ vio_fifo_lump tail ;
- if (!vio_fifo_get_ready(vf))
- return 0 ; /* quit now if nothing there */
+ if (dst == NULL)
+ dst = vio_fifo_new(src->size) ;
- dst_in = dst ;
- while (n > 0)
+ VIO_FIFO_DEBUG_VERIFY(src) ;
+ VIO_FIFO_DEBUG_VERIFY(dst) ;
+
+ if (!vio_fifo_have_end_mark(src))
+ return dst ;
+
+ src_lump = src->end_lump ;
+ src_ptr = src->end_ptr ;
+ tail = ddl_tail(src->base) ;
+
+ while (1)
{
- have = vf->get_end - vf->get_ptr ;
+ char* src_end ;
- if (have > n)
- have = n ;
+ if (src_lump != tail)
+ src_end = src_lump->end ;
+ else
+ src_end = src->put_ptr ;
- memcpy(dst, vf->get_ptr, have) ;
- vf->get_ptr += have ;
- dst = (char*)dst + have ;
+ vio_fifo_put_bytes(dst, src_ptr, src_end - src_ptr) ;
- 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 */
+ if (src_lump == tail)
+ break ;
- n -= have ;
+ src_lump = ddl_next(src_lump, list) ;
+ src_ptr = src_lump->data ;
} ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ VIO_FIFO_DEBUG_VERIFY(dst) ;
- return (char*)dst - (char*)dst_in ;
+ return dst ;
} ;
-/*------------------------------------------------------------------------------
- * Get byte -- the long winded way.
+/*==============================================================================
+ * End Mark Operations.
+ *
+ * Set/Clear end mark is pretty straightforward:
+ *
+ * * if there was an end_ptr before and the put_ptr is ahead of it:
*
- * See the inline vio_fifo_get_byte().
+ * this adds one or more bytes between the get_ptr and the (new) end.
*
- * The version is used when the get_ptr is at or just before the end of the
- * current lump. Looks after all the necessary pointer updates associated with
- * hitting end of lump, or hitting end of FIFO.
+ * * if there was no end_ptr, or it is the same as the put_ptr:
*
- * Returns: 0x00..0xFF -- byte value (as an int)
- * -1 => FIFO is empty.
+ * setting/clearing the end_ptr makes no difference, because whenever
+ * the get_ptr reaches the put_ptr, the pointers are reset if they can
+ * be -- so need not worry about that here.
+ *
+ * Set the p_end and p_get_end to the new reality.
+ *
+ * The put_ptr stays in its current lump, so no need to worry about put_end.
*/
-extern int
-vio_fifo_get_next_byte(vio_fifo vf)
+/*------------------------------------------------------------------------------
+ * 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.
+ *
+ * If the put_ptr is at the end of the last lump, then the end_ptr will follow
+ * it if another lump is added to the FIFO.
+ *
+ * Set the p_end and p_get_end to the new reality.
+ */
+extern void
+vio_fifo_set_end_mark(vio_fifo vff)
{
- unsigned char u ;
-
- if (!vio_fifo_get_ready(vf))
- return -1 ; /* quit now if nothing there */
+ vff->p_end = &vff->end_ptr ;
- u = *vf->get_ptr++ ;
+ vff->end_ptr = vff->put_ptr ;
+ vff->end_lump = ddl_tail(vff->base) ;
- /* 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) ;
+ vio_fifo_set_get_end(vff) ; /* in case end_lump or p_end changed */
- return u ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
} ;
/*------------------------------------------------------------------------------
- * Get pointer to a lump of bytes.
+ * If there is an end mark, advance it to the put_ptr.
*
- * Returns: address of next byte to get, *p_have = number of bytes available
- * or: NULL => FIFO is empty, *p_have = 0
+ * If there was an end_mark before, move it (forward) to the current put_ptr,
+ * which keeps everything in between in the FIFO.
*
- * 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 there was no end mark before, do nothing.
*/
-extern void*
-vio_fifo_get_lump(vio_fifo vf, size_t* p_have)
+extern void
+vio_fifo_step_end_mark(vio_fifo vff)
{
- if (!vio_fifo_get_ready(vf))
+ if (vio_fifo_have_end_mark(vff))
{
- *p_have = 0 ;
- return NULL ;
- } ;
+ vff->end_ptr = vff->put_ptr ;
+ vff->end_lump = ddl_tail(vff->base) ;
+
+ vio_fifo_set_get_end(vff) ; /* in case end_lump changed */
- *p_have = (vf->get_end - vf->get_ptr) ;
- return vf->get_ptr ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
+ }
} ;
/*------------------------------------------------------------------------------
- * Advance FIFO to position reached.
- *
- * 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.
+ * If there is an end_ptr, clear it -- everything between end_ptr and the
+ * current put_ptr is kept in the FIFO.
*
- * The "here" argument must the the address returned by vio_fifo_get_lump()
- * plus the number of bytes taken.
+ * Set the p_end and p_get_end to the new reality.
*/
extern void
-vio_fifo_got_upto(vio_fifo vf, void* here)
+vio_fifo_clear_end_mark(vio_fifo vff)
{
- vf->get_ptr = here ;
+ vff->p_end = &vff->put_ptr ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ vff->end_lump = ddl_tail(vff->base) ;
- if (vf->get_ptr >= vf->get_end)
- vio_fifo_get_next_lump(vf) ;
+ vio_fifo_set_get_end(vff) ; /* in case end_lump or p_end changed */
+
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
} ;
/*------------------------------------------------------------------------------
- * Write contents of FIFO -- assuming non-blocking file
+ * Move put_ptr back to the end mark, if any, and discard data.
*
- * Will write all of FIFO, or upto but excluding the last lump.
+ * If there is an end_mark, keep it if required.
*
- * Returns: > 0 => blocked
- * 0 => all gone (up to last lump if !all)
- * < 0 => failed -- see errno
+ * If there is no end mark, do nothing.
*
- * Note: will work perfectly well for a non-blocking file -- which should
- * never return EAGAIN/EWOULDBLOCK, so will return from here "all gone".
+ * If there is an end mark but it is the same as the put_ptr, then need do
+ * nothing at all.
+ *
+ * Note that: if there is an end mark, then p_end == &end_ptr, so if
+ * *p_end == put_ptr, then end_ptr == put_ptr ; if there is no end mark,
+ * then p_end == &put_ptr, so *p_end == put_ptr !!
*/
-extern int
-vio_fifo_write_nb(vio_fifo vf, int fd, bool all)
+extern void
+vio_fifo_back_to_end_mark(vio_fifo vff, bool keep)
{
- char* src ;
- size_t have ;
- int done ;
-
- while ((src = vio_fifo_get_lump(vf, &have)) != NULL)
+ if (*vff->p_end != vff->put_ptr) /* test for not-empty end mark */
{
- if (!all && vf->one)
- break ; /* don't write last lump */
+ if (vio_fifo_debug)
+ assert(vio_fifo_have_end_mark(vff)) ;
- done = write_nb(fd, src, have) ;
+ if (vff->end_lump != ddl_tail(vff->base))
+ {
+ vio_fifo_lump lump ;
+ do
+ vio_fifo_release_lump(vff, ddl_crop(&lump, vff->base, list)) ;
+ while (vff->end_lump != ddl_tail(vff->base)) ;
- if (done < 0)
- return -1 ; /* failed */
+ vff->put_end = vff->end_lump->end ;
+ } ;
- vio_fifo_got_upto(vf, src + done) ;
+ if (*vff->p_start == vff->end_ptr)
+ vio_fifo_reset_ptrs(vff) ;
+ else
+ vff->put_ptr = vff->end_ptr ;
+ } ;
- if (done < (int)have)
- return 1 ; /* blocked */
+ if (!keep)
+ {
+ vff->p_end = &vff->put_ptr ;
+ vio_fifo_set_get_end(vff) ; /* in case get_lump == end_lump */
} ;
- return 0 ; /* all gone */
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
} ;
-/*------------------------------------------------------------------------------
- * Get the current rdr_end value.
- *
- * Unlike get_end, do not have a field for this, but find it each time.
+/*==============================================================================
+ * Get data from the FIFO.
*/
-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 ;
-} ;
/*------------------------------------------------------------------------------
- * 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
- *
- * If the FIFO is not empty, will return pointer to at least one byte.
+ * Get upto 'n' bytes -- steps past the bytes fetched.
*
- * Returns number of bytes to the end of the current lump. There may be
- * further lumps beyond the current one.
- *
- * 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.
+ * Returns: number of bytes got -- may be zero.
*/
-extern void*
-vio_fifo_get_rdr(vio_fifo vf, size_t* p_have)
+extern ulen
+vio_fifo_get_bytes(vio_fifo vff, void* dst, ulen n)
{
- if (!vio_fifo_get_ready(vf))
- {
- *p_have = 0 ;
- return NULL ;
- } ;
+ void* dst_start ;
- if (vf->rdr_lump == NULL) /* set up new rdr if required */
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
+
+ dst_start = dst ;
+ while (n > 0)
{
- vf->rdr_lump = ddl_head(vf->base) ;
- vf->rdr_ptr = vf->get_ptr ;
+ ulen take ;
+
+ take = *vff->p_get_end - vff->get_ptr ;
+
+ if (take > n)
+ take = n ;
+ else if (take == 0)
+ break ;
+
+ memcpy(dst, vff->get_ptr, take) ;
+ dst = (char*)dst + take ;
+
+ n -= take ;
+
+ vio_fifo_step(vff, take) ;
} ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
- *p_have = vio_fifo_rdr_end(vf) - vf->rdr_ptr ;
- return vf->rdr_ptr ;
+ return (char*)dst - (char*)dst_start ;
} ;
/*------------------------------------------------------------------------------
- * 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.
+ * Write contents of FIFO -- assuming non-blocking file
*
- * Returns number of bytes to the end of the current lump. There may be
- * further lumps beyond the current one.
+ * Will write all of FIFO up to end mark or put_ptr, or upto but excluding
+ * the end_lump.
*
- * 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().
+ * Returns: > 0 => blocked
+ * 0 => all gone (up to last lump if !all)
+ * < 0 => failed -- see errno
*
- * NB: the step may NOT exceed the last reported "have".
+ * 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_step_rdr(vio_fifo vf, size_t* p_have, size_t step)
+extern int
+vio_fifo_write_nb(vio_fifo vff, int fd, bool all)
{
- char* rdr_end ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
- assert(vf->rdr_lump != NULL) ;
+ while (1)
+ {
+ ulen have ;
+ int done ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ if ((vff->get_lump == vff->end_lump) && !all)
+ break ; /* don't write last lump */
- rdr_end = vio_fifo_rdr_end(vf) ;
- vf->rdr_ptr += step ;
+ have = vio_fifo_get(vff) ;
- if (vf->rdr_ptr >= rdr_end)
- {
- assert(vf->rdr_ptr == rdr_end) ;
+ if (have == 0)
+ break ;
- if (vf->rdr_lump != ddl_tail(vf->base))
- {
- vf->rdr_lump = ddl_next(vf->rdr_lump, list) ;
- vf->rdr_ptr = vf->rdr_lump->data ;
+ done = write_nb(fd, vio_fifo_get_ptr(vff), have) ;
- rdr_end = vio_fifo_rdr_end(vf) ;
- } ;
+ if (done < 0)
+ return -1 ; /* failed */
+
+ vio_fifo_step(vff, done) ;
+
+ if (done < (int)have)
+ return 1 ; /* blocked */
} ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
- *p_have = (rdr_end - vf->rdr_ptr) ;
- return (*p_have > 0) ? vf->rdr_ptr : NULL ;
+ return 0 ; /* all gone */
} ;
/*------------------------------------------------------------------------------
- * Move FIFO get position to the rdr position, if any.
+ * Write contents of FIFO -- assuming blocking file
+ *
+ * Will write all of FIFO up to end mark or put_ptr.
+ *
+ * Returns: 0 => all gone
+ * < 0 => failed -- see errno
*
- * This clears the rdr position, and removes all data between the current and
- * new get positions from the FIFO.
+ * 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_sync_rdr(vio_fifo vf)
+extern int
+vio_fifo_fwrite(vio_fifo vff, FILE* file)
{
- vio_fifo_lump head ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ while (1)
+ {
+ int done ;
+ ulen have ;
- if (vf->rdr_lump == NULL)
- return ;
+ have = vio_fifo_get(vff) ;
+ if (have == 0)
+ break ;
- while ((head = ddl_head(vf->base)) != vf->rdr_lump)
- {
- vf->get_ptr = vf->get_end ; /* jump to end of lump */
- vio_fifo_lump_release(vf, head) ;
- } ;
+ done = fwrite(vio_fifo_get_ptr(vff), have, 1, file) ;
- vf->get_ptr = vf->rdr_ptr ; /* jump to rdr_ptr */
+ if (done < 1)
+ return -1 ; /* failed */
- vf->rdr_lump = NULL ; /* clear the rdr */
- vf->rdr_ptr = NULL ;
+ vio_fifo_step(vff, have) ;
+ } ;
- 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(vff) ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ return 0 ; /* all gone */
} ;
/*------------------------------------------------------------------------------
- * Drop the rdr position (if any).
+ * Skip get_ptr to the current end -- which may be the current end mark.
*
- * This clears the rdr position leaving the get position and FIFO unchanged.
+ * Does not clear any hold_ptr or end_ptr.
*/
extern void
-vio_fifo_drop_rdr(vio_fifo vf)
+vio_fifo_skip_to_end(vio_fifo vff)
{
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ /* Advance directly to the current end and then synchronise get_ptr
+ */
+ vff->get_lump = vff->end_lump ;
+ vff->get_ptr = *vff->p_end ;
+ vff->p_get_end = vff->p_end ;
- vf->rdr_lump = NULL ;
- vf->rdr_ptr = NULL ;
+ vio_fifo_sync_get(vff) ;
} ;
-/*------------------------------------------------------------------------------
- * 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 !
+/*==============================================================================
+ * Hold Mark Operations.
*
- * 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.
+ * Set or clear hold_ptr.
*
- * That is done here, but may be worth updating get_end before testing
- * against get_ptr.
+ * The get_ptr is unambiguous -- so the hold_ptr is, because it is only ever
+ * set equal to the get_ptr !
*
- * Returns: true <=> at least one byte in FIFO.
+ * The put_ptr stays in its current lump, so no need to worry about put_end.
+ */
+
+/*------------------------------------------------------------------------------
+ * Set hold mark -- clearing existing one, if any.
*
- * NB: if finds that the FIFO is empty, resets the pointers to the start
- * of the last lump -- does not release the last lump.
+ * Discard all contents up to the current get_ptr (easy if no hold mark), then
+ * set hold mark at get_ptr.
*/
-static bool
-vio_fifo_get_next_lump(vio_fifo vf)
+extern void
+vio_fifo_set_hold_mark(vio_fifo vff)
{
- vio_fifo_lump head ;
+ vio_fifo_release_upto(vff, vff->get_lump) ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
- assert(vf->get_ptr == vf->get_end) ;
+ if (vff->get_ptr == vff->put_ptr)
+ vio_fifo_reset_ptrs(vff) ;
+ else
+ vff->hold_ptr = vff->get_ptr ;
- head = ddl_head(vf->base) ; /* current lump for get */
+ vff->p_start = &vff->hold_ptr ;
- /* 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)
- {
- assert( (head != NULL) && (head == ddl_tail(vf->base)) ) ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
+} ;
- if (vf->get_ptr < vf->put_ptr)
- {
- /* Had an out of date vf->get_end */
- vf->get_end = vf->put_ptr ;
+/*------------------------------------------------------------------------------
+ * Clear hold mark -- if any.
+ *
+ * Discard all contents up to the current get_ptr (easy if no hold mark), then
+ * clear hold mark (no effect if not set).
+ *
+ * Note that clearing a hold_ptr in an empty FIFO resets all the pointers. To
+ * avoid that could test for an empty hold mark (*p_start == get_ptr), but the
+ * extra step in the majority case seems worse than the extra work in the
+ * minority one.
+ */
+extern void
+vio_fifo_clear_hold_mark(vio_fifo vff)
+{
+ vio_fifo_release_upto(vff, vff->get_lump) ;
- return true ; /* FIFO not empty */
- } ;
+ if (vff->get_ptr == vff->put_ptr)
+ vio_fifo_reset_ptrs(vff) ;
- assert(vf->get_ptr == vf->put_ptr) ;
+ vff->p_start = &vff->get_ptr ;
- /* FIFO is empty -- reset pointers and exit */
- vio_fifo_ptr_reset(vf, head) ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
+} ;
- return false ; /* FIFO empty */
- } ;
+/*------------------------------------------------------------------------------
+ * If there is an hold_mark, reset get_ptr *back* to it, and leave the mark
+ * set or clear.
+ *
+ * If there is no hold mark, set one at the current position, if required.
+ *
+ * Setting the get_ptr back to the hold_ptr sets it to an unambiguous
+ * position. If the get_ptr == hold_ptr then if the FIFO is empty, the
+ * pointers will have been reset.
+ */
+extern void
+vio_fifo_back_to_hold_mark(vio_fifo vff, on_off_b set)
+{
+ if (vio_fifo_have_hold_mark(vff))
+ vio_fifo_set_get_ptr(vff, ddl_head(vff->base), vff->hold_ptr) ;
- /* Release the head and update pointers
- *
- * Deals with possibility that nothing has yet been allocated
- */
- vio_fifo_lump_release(vf, head) ;
+ vff->p_start = (set) ? &vff->hold_ptr : &vff->get_ptr ;
- return (vf->get_ptr < vf->get_end) ;
+
+ VIO_FIFO_DEBUG_VERIFY(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 ;
+ bool own_seen ;
- 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 something allocated, tail must not be NULL */
+ /* FIFO always has at least one lump. */
if (head == NULL)
+ zabort("head is NULL") ;
+ if (tail == NULL)
+ zabort("tail is NULL") ;
+
+ /* Make sure that the lump pointers all work
+ *
+ * When finished, know that head <= get_lump <= end_lump <= tail.
+ */
+ own_seen = false ;
+
+ lump = head ;
+ while (1)
+ {
+ if (lump == vff->own_lump)
+ own_seen = true ;
+
+ if (lump == vff->get_lump)
+ break ;
+
+ lump = ddl_next(lump, list) ;
+ if (lump == NULL)
+ zabort("ran out of lumps looking for get_lump") ;
+ } ;
+
+ while (lump != vff->end_lump)
+ {
+ lump = ddl_next(lump, list) ;
+
+ if (lump == NULL)
+ zabort("ran out of lumps looking for end_lump") ;
+
+ if (lump == vff->own_lump)
+ own_seen = true ;
+ } ;
+
+ while (lump != tail)
+ {
+ lump = ddl_next(lump, list) ;
+
+ if (lump == NULL)
+ zabort("ran out of lumps looking for tail") ;
+
+ if (lump == vff->own_lump)
+ own_seen = true ;
+ } ;
+
+ if (vff->spare == vff->own_lump)
+ {
+ if (own_seen)
+ zabort("own seen in FIFO, but is also spare") ;
+ }
+ else
{
- 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) )
- zabort("nothing allocated, but not all NULL") ;
- return ;
+ if (!own_seen)
+ zabort("not found own lump in the FIFO") ;
+ } ;
+
+ /* Check that the p_start, p_get_end and p_end are valid
+ */
+ if ((vff->p_start != &vff->hold_ptr) && (vff->p_start != &vff->get_ptr))
+ zabort("p_start is neither &get_ptr nor &hold_ptr") ;
+
+ if ((vff->p_end != &vff->end_ptr) && (vff->p_end != &vff->put_ptr))
+ zabort("p_end is neither &put_ptr nor &end_ptr") ;
+
+ if (vff->get_lump == vff->end_lump)
+ {
+ if (vff->p_get_end != vff->p_end)
+ zabort("p_get_end not equal to p_end and is in end_lump") ;
}
else
{
- if (tail == NULL)
- zabort("head pointer not NULL, but tail pointer is") ;
+ if (vff->p_get_end != &vff->get_lump->end)
+ zabort("p_get_end not equal to get_lump->end and is not in end_lump") ;
} ;
/* 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_ptr <= put_ptr or tail != end_lump
*/
- if ( (tail->data > vf->put_ptr)
- || (vf->put_ptr > vf->put_end)
- || (vf->put_end != tail->end) )
- zabort("put pointers outside the tail lump") ;
+ if (vio_fifo_have_hold_mark(vff))
+ {
+ if ( (head->data > vff->hold_ptr)
+ || (vff->hold_ptr > head->end) )
+ zabort("hold_ptr outside the head lump") ;
+
+ if ((vff->get_lump == head) && (vff->hold_ptr > vff->get_ptr))
+ zabort("hold_ptr greater than get_ptr") ;
+ }
+ else
+ {
+ if (vff->get_lump != head)
+ zabort("no hold_ptr, but get_lump is not head") ;
+ } ;
- if ( (head->data > vf->get_ptr)
- || (vf->get_ptr > vf->get_end)
- || (vf->get_end > head->end) )
- zabort("get pointers outside the head lump") ;
+ if ( (vff->get_lump->data > vff->get_ptr)
+ || (vff->get_ptr > *vff->p_get_end)
+ || (*vff->p_get_end > vff->get_lump->end))
+ zabort("get pointers outside the get lump") ;
- /* If head == tail, should be vf->one, etc. */
- if (head == tail)
+ if (vio_fifo_have_end_mark(vff))
{
- if (!vf->one)
- zabort("have one lump, but !vf->one") ;
+ if ( (vff->end_lump->data > vff->end_ptr)
+ || (vff->end_ptr > vff->end_lump->end) )
+ zabort("end pointer outside the end lump") ;
- if (vf->get_end > vf->put_ptr)
- zabort("get_end is greater than put_ptr when vf->one") ;
+ if ((vff->end_lump == tail) && (vff->end_ptr > vff->put_ptr))
+ zabort("end pointer greater than put 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->end_lump != tail)
+ zabort("no end_ptr, but end_lump is not tail") ;
} ;
- /* If have an rdr_lump -- make sure everything else is valid */
- if (vf->rdr_lump != NULL)
+ if ( (tail->data > vff->put_ptr)
+ || (vff->put_ptr > vff->put_end)
+ || (vff->put_end != tail->end) )
+ zabort("put pointers outside the tail lump") ;
+
+ /* Check that if get_ptr == p_get_end, that it is empty, or there is some
+ * not-empty hold or end mark.
+ *
+ * The point is to trap any failure to reset pointers or advance the get_ptr
+ * when it hits *p_get_end.
+ */
+ if (vff->get_ptr == *vff->p_get_end)
{
- lump = head ;
- while (lump != vf->rdr_lump)
+ if (*vff->p_start != vff->put_ptr)
{
- if (lump == tail)
- zabort("rdr_lump is not part of FIFO") ;
- lump = ddl_next(lump, list) ;
+ /* Not empty -- so must have a hold and/or end */
+ if (!(vio_fifo_have_hold_mark(vff) || vio_fifo_have_end_mark(vff)))
+ zabort("get_ptr is at get_end, is not empty by no marks set") ;
} ;
+ } ;
- if ( (lump->data > vf->rdr_ptr)
- || (vf->rdr_ptr > lump->end) )
- zabort("rdr_ptr outside its lump") ;
-
- if ( (lump == head) && (vf->rdr_ptr < vf->get_ptr))
- zabort("rdr_ptr is less than get_ptr in first lump") ;
-
- if ( (lump == tail) && (vf->rdr_ptr > vf->put_ptr))
- zabort("rdr_ptr is greater than put_ptr in last lump") ;
- }
- else
+ /* Check that if is empty, the pointers are reset.
+ */
+ if (*vff->p_start == vff->put_ptr)
{
- if (vf->rdr_ptr != NULL)
- zabort("rdr_ptr not NULL when rdr_lump is") ;
- }
+ if ( (tail != head)
+ || (vff->get_lump != head)
+ || (vff->end_lump != head)
+ || (vff->get_ptr != head->data)
+ || (vff->put_ptr != head->data)
+ || (vff->put_end != head->end)
+ || !( (vff->hold_ptr == NULL) || (vff->hold_ptr == head->data) )
+ || !( (vff->end_ptr == NULL) || (vff->end_ptr == head->data) )
+ )
+ zabort("pointers not valid for empty fifo") ;
+ } ;
} ;
diff --git a/lib/vio_fifo.h b/lib/vio_fifo.h
index 52f3455e..03437ae4 100644
--- a/lib/vio_fifo.h
+++ b/lib/vio_fifo.h
@@ -22,152 +22,198 @@
#ifndef _ZEBRA_VIO_FIFO_H
#define _ZEBRA_VIO_FIFO_H
-#include "zebra.h"
-#include <stdint.h>
-#include <stdbool.h>
+#include "misc.h"
+#include "vargs.h"
+#include <stdio.h>
#include "list_util.h"
-#include "zassert.h"
-
-#ifndef Inline /* in case of compiler issues */
-#define Inline static inline
-#endif
-
-#ifndef Private /* extern, but for "friends" only */
-#define Private extern
-#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__ */
/*==============================================================================
* VTY I/O FIFO -- buffering of arbitrary amounts of I/O.
*/
-#ifdef NDEBUG
-# define VIO_FIFO_DEBUG 0 /* NDEBUG override */
-#else
-# ifndef VIO_FIFO_DEBUG
-# define VIO_FIFO_DEBUG 1 /* Set to 1 to turn on debug checks */
+/*------------------------------------------------------------------------------
+ * Sort out VIO_FIFO_DEBUG.
+ *
+ * Set to 1 if defined, but blank.
+ * Set to QDEBUG if not defined.
+ *
+ * Force to 0 if VIO_FIFO_NO_DEBUG is defined and not zero.
+ *
+ * So: defaults to same as QDEBUG, but no matter what QDEBUG is set to:
+ *
+ * * can set VIO_FIFO_DEBUG == 0 to turn off debug
+ * * or set VIO_FIFO_DEBUG != 0 to turn on debug
+ * * or set VIO_FIFO_NO_DEBUG != 0 to force debug off
+ */
+
+#ifdef VIO_FIFO_DEBUG /* If defined, make it 1 or 0 */
+# if IS_BLANK_OPTION(VIO_FIFO_DEBUG)
+# undef VIO_FIFO_DEBUG
+# define VIO_FIFO_DEBUG 1
+# endif
+#else /* If not defined, follow QDEBUG */
+# define VIO_FIFO_DEBUG QDEBUG
+#endif
+
+#ifdef VIO_FIFO_NO_DEBUG /* Override, if defined */
+# if IS_NOT_ZERO_OPTION(VIO_FIFO_NO_DEBUG)
+# undef VIO_FIFO_DEBUG
+# define VIO_FIFO_DEBUG 0
# endif
#endif
+enum { vio_fifo_debug = VIO_FIFO_DEBUG } ;
+
/*==============================================================================
* Data Structures
+ *
+ * Note that the main fifo structure contains the first "lump", so the fifo
+ * always contains at least one.
*/
-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 ;
+struct vio_fifo_lump
+{
+ struct dl_list_pair(vio_fifo_lump) list ;
+
+ char* end ; /* end of this particular lump */
+ char data[] ;
+} ;
+
struct vio_fifo
{
struct dl_base_pair(vio_fifo_lump) base ;
- bool one ;
+ char** p_start ; /* -> hold_ptr/get_ptr */
- char* put_ptr ;
- char* put_end ;
+ char* hold_ptr ; /* implicitly in the head lump */
+ /* NULL <=> no hold_ptr */
+ vio_fifo_lump get_lump ; /* head lump unless "hold_ptr" */
char* get_ptr ;
- char* get_end ;
- vio_fifo_lump rdr_lump ;
- char* rdr_ptr ;
+ char** p_get_end ; /* -> lump->end/end_ptr/put_ptr */
+ char** p_end ; /* -> end_ptr/put_ptr */
+
+ vio_fifo_lump end_lump ; /* tail lump unless "end_ptr" */
+ char* end_ptr ; /* NULL <=> no end_ptr */
+
+ char* put_ptr ; /* implicitly in the tail lump */
+ char* put_end ;
+
+ ulen size ; /* set when initialised */
- size_t size ;
+ vio_fifo_lump spare ; /* may be "own_lump" */
- vio_fifo_lump spare ;
+ vio_fifo_lump_t own_lump[] ; /* embedded lump */
} ;
-struct vio_fifo_lump
-{
- struct dl_list_pair(vio_fifo_lump) list ;
+typedef struct vio_fifo vio_fifo_t[1] ; /* embedded */
+typedef struct vio_fifo* vio_fifo ;
- char* end ; /* end of this particular lump */
- size_t size ; /* size of lump when allocated */
- char data[] ;
+/* 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 }
+
/*==============================================================================
* Functions
*/
+extern vio_fifo vio_fifo_new(ulen size) ;
+extern vio_fifo vio_fifo_free(vio_fifo vff) ;
-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) ;
-
-#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 vf) ;
-Inline bool vio_fifo_empty(vio_fifo vf) ;
-extern size_t vio_fifo_room(vio_fifo vf) ;
+extern void vio_fifo_clear(vio_fifo vff, bool clear_marks) ;
+Inline bool vio_fifo_empty(vio_fifo vff) ;
+Inline bool vio_fifo_tail_empty(vio_fifo vff) ;
-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 void vio_fifo_put_bytes(vio_fifo vff, const char* src, ulen n) ;
+Inline void vio_fifo_put_byte(vio_fifo vff, 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, ulen request) ;
+
+extern ulen vio_fifo_get_bytes(vio_fifo vff, void* dst, ulen n) ;
+Inline int vio_fifo_get_byte(vio_fifo vff) ;
+Inline ulen vio_fifo_get(vio_fifo vff) ;
+Inline void* vio_fifo_get_ptr(vio_fifo vff) ;
+Inline void vio_fifo_step(vio_fifo vff, ulen step) ;
+Inline ulen vio_fifo_step_get(vio_fifo vff, ulen 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) ;
+
+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, on_off_b on) ;
/*==============================================================================
* 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)
-#else
-# define VIO_FIFO_DEBUG_VERIFY(vf)
-#endif
+#define VIO_FIFO_DEBUG_VERIFY(vff) if (vio_fifo_debug) vio_fifo_verify(vff)
/*==============================================================================
* Inline Functions
*/
+Private void vio_fifo_add_lump(vio_fifo vff) ;
+Private void vio_fifo_sync_get(vio_fifo vff) ;
+
+/*------------------------------------------------------------------------------
+ * Returns true <=> FIFO is empty -- at least: get_ptr == end_ptr (if any)
+ * or: get_ptr == put_ptr.
+ */
+Inline bool
+vio_fifo_empty(vio_fifo vff)
+{
+ return (vff == NULL) || (vff->get_ptr == *vff->p_end) ;
+} ;
+
/*------------------------------------------------------------------------------
- * Returns true <=> FIFO is empty
+ * Returns true <=> FIFO is empty beyond end_ptr (if any).
*/
Inline bool
-vio_fifo_empty(vio_fifo vf)
+vio_fifo_tail_empty(vio_fifo vff)
{
- return (vf->get_ptr == vf->put_ptr) ;
-}
+ /* if vff is NULL, treat as empty !
+ * if end_ptr is NULL, then tail is empty !
+ * else tail is empty iff end_ptr == put_ptr.
+ */
+ return (vff == NULL) || (vff->put_ptr == *vff->p_end) ;
+} ;
/*------------------------------------------------------------------------------
* 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_add_lump(vff) ; /* traps put_ptr > put_end */
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
- *vf->put_ptr++ = b ;
+ *vff->put_ptr++ = b ;
} ;
/*------------------------------------------------------------------------------
@@ -177,29 +223,73 @@ 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) ;
+ if (vff->get_ptr < *vff->p_get_end)
+ {
+ int ch ;
+ ch = (uchar)*vff->get_ptr ;
+
+ vio_fifo_step(vff, 1) ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ return ch ;
+ }
- return (unsigned char)*vf->get_ptr++ ;
+ return -1 ;
} ;
/*------------------------------------------------------------------------------
- * See if have at least one full lump.
+ * Get count of bytes available in the current lump.
*
- * This may be used with vio_fifo_write_nb(..., false) to use FIFO as a sort of
- * double buffer.
+ * There will always be at least 1 byte available in the current lump, unless
+ * the FIFO is empty, or at the end mark.
*
- * Returns: true <=> there is at least one full lump in the FIFO
- * (excluding the last lump if it happens to be full)
+ * Returns: address of bytes to get
*/
-Inline bool
-vio_fifo_full_lump(vio_fifo vf)
+Inline ulen
+vio_fifo_get(vio_fifo vff)
+{
+ return *vff->p_get_end - vff->get_ptr ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get pointer to bytes in the current lump.
+ *
+ * NB: to be called only after vio_fifo_get(), or vio_fifo_steo_get() have
+ * returned a non-zero value.
+ */
+Inline void*
+vio_fifo_get_ptr(vio_fifo vff)
+{
+ return vff->get_ptr ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Step FIFO past bytes used.
+ *
+ * Can be called after a vio_fifo_get() or vio_fifo_step_get().
+ *
+ * NB: the "step" argument MUST not exceed the "have" previously returned.
+ */
+Inline void
+vio_fifo_step(vio_fifo vff, ulen step)
+{
+ vff->get_ptr += step ;
+
+ if (vff->get_ptr >= *vff->p_get_end)
+ vio_fifo_sync_get(vff) ;
+
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * vio_fifo_step() forllowed by vio_fifo_get() !
+ */
+Inline ulen
+vio_fifo_step_get(vio_fifo vff, ulen step)
{
- return (!vf->one && (vf->put_ptr != NULL)) ;
+ vio_fifo_step(vff, step) ;
+ return vio_fifo_get(vff) ;
} ;
#endif /* _ZEBRA_VIO_FIFO_H */
diff --git a/lib/vio_lines.c b/lib/vio_lines.c
index c2e9c43c..2a6a89d1 100644
--- a/lib/vio_lines.c
+++ b/lib/vio_lines.c
@@ -19,10 +19,8 @@
* Boston, MA 02111-1307, USA.
*/
-#include <stdint.h>
-
+#include "misc.h"
#include "memory.h"
-#include "zassert.h"
#include "vio_lines.h"
#include "qiovec.h"
@@ -60,7 +58,7 @@
*
* WHAT IT DOES DO:
*
- * 1) maps bare '\n' to '\r''\n'.
+ * 1) maps bare '\n' to '\r''\n' -- if required.
*
* Swallows '\r' immediately before '\n' if present.
*
@@ -81,66 +79,84 @@
* A width of <= 0 => very large width indeed.
* A height of <= 0 => indefinite height
*
- * Pause is unset. vio_lc_append will collect an indefinite number of lines.
- *
- * Column and line position set to zero.
- *
- * Returns: address of vio_line_control
+ * Returns: address of vio_line_control
*/
extern vio_line_control
-vio_lc_init_new(vio_line_control lc, int width, int height)
+vio_lc_init_new(vio_line_control lc, int width, int height, const char* newline)
{
if (lc == NULL)
lc = XCALLOC(MTYPE_VIO_LC, sizeof(struct vio_line_control)) ;
else
- memset(lc, 0, sizeof(struct vio_line_control)) ;
+ memset(lc, 0, sizeof(vio_line_control_t)) ;
/* Zeroising has set:
*
- * pause = 0 -- no limit on the number of lines to append
- * paused = 0 -- not paused
+ * width = X -- set below.
+ * height = X -- set below
*
- * col = 0 -- at column 0
- * lines = 0 -- no lines collected, yet
+ * counter = X -- set below
*
- * iov = all 0 -- empty
- * writing = 0 -- not writing
+ * incomplete = false -- no incomplete line in hand
+ * fragments = NULL -- set below
+ * here = NULL -- set below
+ *
+ * qiov = NULL -- set below
+ *
+ * newline = zeros -- set below
*/
lc->width = width >= 0 ? width : 0 ;
lc->height = height >= 0 ? height : 0 ;
+ vio_lc_counter_reset(lc) ;
+
+ lc->fragments = qiovec_init_new(NULL) ;
+ lc->here = qs_new(120) ;
+
+ lc->qiov = qiovec_init_new(NULL) ;
+
+ lc->newline->base = newline ;
+ lc->newline->len = strlen(newline) ;
return lc ;
} ;
/*------------------------------------------------------------------------------
- * Reset vio_line_control (if any) -- release body and (if required) the
+ * Reset vio_line_control (if any) -- release body and (if required) free the
* structure.
*
* Returns: address of vio_line_control (if any) -- NULL if structure released
+ *
+ * NB: if the line control is not freed, it MUST be reinitialised by
+ * vio_lc_init_new() before it is used again !
+ *
+ * NB: it is the callers responsibility to release anything which was buffered
+ * for the line control to look after.
+ *
+ * It is also the callers responsibility to release the newline string,
+ * if required.
*/
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)
{
+ lc->fragments = qiovec_free(lc->fragments) ;
+ lc->here = qs_free(lc->here) ;
+
+ lc->qiov = qiovec_free(lc->qiov) ;
+
if (free_structure)
XFREE(MTYPE_VIO_LC, lc) ; /* sets lc = NULL */
else
- vio_lc_init_new(lc, lc->width, lc->height) ;
- /* re-initialise */
+ memset(lc, 0, sizeof(vio_line_control_t)) ;
} ;
return lc ;
} ;
/*------------------------------------------------------------------------------
- * Clear given vio_line_control.
- *
- * Sets: pause = 0
- * paused = 0
- * col = 0
- * writing = 0
+ * Clear given vio_line_control -- discard all buffered lines and reset the
+ * counter.
*
* NB: it is the callers responsibility to release anything buffered because
* it was earlier appended.
@@ -148,12 +164,17 @@ vio_lc_reset(vio_line_control lc, bool free_structure)
extern void
vio_lc_clear(vio_line_control lc)
{
- qiovec_clear(&lc->qiov) ;
+ if (lc == NULL)
+ return ;
+
+ vio_lc_counter_reset(lc) ;
+
+ lc->incomplete = false ;
+
+ qiovec_clear(lc->fragments) ;
+ qs_clear(lc->here) ;
- lc->pause = 0 ;
- lc->paused = 0 ;
- lc->col = 0 ;
- lc->writing = 0 ;
+ qiovec_clear(lc->qiov) ;
} ;
/*------------------------------------------------------------------------------
@@ -162,196 +183,404 @@ vio_lc_clear(vio_line_control lc)
* A width of <= 0 => very large width indeed.
* A height of <= 0 => indefinite height
*
- * Pause is adjusted if it is not zero, and may become zero and set paused.
+ * This may happen at almost any time when a Telnet terminal is resized.
+ * From line control perspective, can happen between calls of vio_lc_append(),
+ * vio_lc_flush() and vio_lc_write_nb().
+ *
+ * If happens while the line control has stuff buffered, then it is too late to
+ * change anything, unless was an incomplete line.
+ *
+ * Tries to sort out the counter... but is is not possible to do this perfectly.
*/
extern void
vio_lc_set_window(vio_line_control lc, int width, int height)
{
- unsigned old_height ;
+ unsigned depth ;
- old_height = lc->height ;
+ depth = lc->height ;
lc->width = width >= 0 ? width : 0 ;
lc->height = height >= 0 ? height : 0 ;
- if (lc->pause != 0)
- {
- if (lc->height > old_height)
- lc->pause += lc->height - old_height ;
- else
- {
- if (lc->pause >= (old_height - lc->height))
- lc->pause = 0 ;
- else
- lc->pause -= old_height - lc->height ;
- } ;
- lc->paused = (lc->pause == 0) ;
- } ;
+ /* If counter already exhausted, or just set indefinite height, need do
+ * nothing more.
+ */
+ if ((lc->counter <= 0) || (lc->height == 0))
+ return ;
+
+ /* Counter not already exhausted, and is setting a definite height.
+ *
+ * If no height was set before, start from scratch, now.
+ */
+ if (depth == 0)
+ return vio_lc_counter_reset(lc) ;
+
+ /* Had a definite height and setting a new one.
+ *
+ * Now calculate the depth, which is how far down the old height have
+ * got to. (The counter should be less than the old height, but if it
+ * isn't we end up with a huge depth here...)
+ */
+ depth -= lc->counter ;
+
+ /* If the new height is > depth set the counter to be the new height - depth.
+ *
+ * Otherwise, set the counter to 0, so is immediately exhausted.
+ *
+ * Cannot solve the problem of what to do if the width has changed !
+ */
+ lc->counter = (lc->height > depth) ? lc->height - depth : 0 ;
} ;
/*==============================================================================
* Appending and writing
*/
-/*------------------------------------------------------------------------------
- * Sets pause to the current height and clear paused.
- */
-extern void
-vio_lc_set_pause(vio_line_control lc)
-{
- lc->pause = lc->height ;
- lc->paused = 0 ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Put newline (if required) and account for it
- */
-static inline void
-vio_lc_newline(vio_line_control lc, bool required)
-{
- if (required)
- qiovec_push(&lc->qiov, "\r\n", 2) ;
-
- lc->col = 0 ;
- lc->line += 1 ;
- if (lc->pause != 0)
- {
- lc->pause -= 1 ;
- lc->paused = (lc->pause == 0) ;
- } ;
-} ;
+static uint vio_lc_trim(vio_line_control lc, qiov_item item, const char* e) ;
+static void vio_lc_append_line(vio_line_control lc, qiov_item item) ;
/*------------------------------------------------------------------------------
* Append a lump of output to the given line control's buffers.
*
- * Breaks the output into lines which are no longer than the lc->width.
+ * Breaks the output into lines which are no longer than the lc->width. If
+ * that is zero (unset) we use a very large width indeed.
+ *
+ * Breaks the output into a number of screen lines, limited by the line counter.
+ *
+ * Effect of line counter:
+ *
+ * * if definite height, will output only as many screen lines as it can,
+ * including none at all.
+ *
+ * * if indefinite height, when the line counter is reset it is set to some
+ * limit on the number of screen lines to buffer in the line control.
+ *
+ * Note that this is not exact, in particular will *not* stop in the
+ * middle of a complete line, just because the limit of screen lines has
+ * been exceeded.
+ *
+ * * in any case, the counter may reach exactly 0 if the number of screen
+ * lines required is exactly the number of screen lines allowed.
+ *
+ * Trims trailing whitespace from each line (but not from screen lines), before
+ * breaking up into screen lines.
+ *
+ * NB: this means that does not output anything until gets a '\n'.
+ *
+ * See vio_lc_flush().
*
* Maps '\n' to '\r''\n'.
*
- * Discards '\r' if found before '\n', and possibly at other times.
+ * Discards '\r' amongst trailing whitespace, but not otherwise.
*
* If lc->width == 0, use a very large width indeed.
*
- * If lc->pause == 0, append an indefinite number of lines
+ * Returns: number of bytes able to append to the line control.
+ *
+ * NB: the buffer presented MUST be retained until the contents of the line
+ * control's buffers have been written -- see vio_lc_write_nb().
+ *
+ * NB: the line control may buffer stuff "in hand", before it reaches the
+ * output iovec, either because:
+ *
+ * a) the current line is incomplete -- nothing will be output until
+ * a '\n' is appended, or the line control is flushed.
+ *
+ * b) the line counter was exhausted while outputting a line -- this will
+ * be output on the next call of vio_lc_append(), or when the line
+ * control is flushed.
+ *
+ * So even when the buffers that feed the line control are empty,
+ * a call of vio_lc_append() may push out some lines buffered in
+ * the line control.
*
- * NB: the buffer presented MUST be retained until the contents of the
- * line control's buffers have been written.
+ * NB: all output from the line control ends with a newline.
*
- * Returns: number of bytes able to append
+ * Incomplete lines are held in the line control until they are completed,
+ * or until the line control is flushed. When the line control is flushed,
+ * unless the incomplete line is all whitespace, a newline is appended.
*/
extern size_t
vio_lc_append(vio_line_control lc, const void* buf, size_t len)
{
- const char* p ;
- const char* end ;
-
- unsigned width ;
- unsigned pause ;
+ qiov_item_t item ;
+ const char* bp ;
+ const char* be ;
- /* Prepare local width and pause */
- if (lc->width > 0)
- width = lc->width ;
- else
- width = UINT_MAX ;
-
- if (lc->pause > 0)
- pause = 0 ;
- else
- pause = 1 ;
-
- lc->paused = 0 ;
+ /* If we have a line which was being output, but was interrupted part
+ * way through by line counter expiry... try to empty that out, first.
+ *
+ * This may be prevented by the line counter (partly or completely), or may
+ * exhaust it, which will be noticed, shortly, below.
+ */
+ if (!qiovec_empty(lc->fragments) && !lc->incomplete)
+ {
+ qiovec_shift(lc->fragments, item) ; /* want first fragment */
+ vio_lc_append_line(lc, item) ;
+ } ;
/* Append: stop when run out of data or run out of lines */
- end = (const char*)buf + len ;
- p = buf ;
+ be = (const char*)buf + len ;
+ bp = buf ;
- while ((p < end) && (lc->pause != pause))
+ /* If is counter exhausted, stop appending now.
+ *
+ * In vio_lc_append_line() updates the counter, and may set it -ve.
+ */
+ while ((bp < be) && (lc->counter > 0))
{
- const char* e ;
- bool nl ;
- int nlx ;
+ item->base = bp ; /* start of current fragment */
- nlx = 0 ; /* no line ending chars yet */
+ /* scan for '\n' -- note that we look for a complete line,
+ * before worrying about the line */
+ bp = memchr(bp, '\n', (be - bp)) ;
- /* scan for '\n'. */
- e = memchr(p, '\n', (end - p)) ;
- nl = (e != NULL) ;
- if (nl)
- ++nlx ; /* account for the '\n' */
- else
- e = end ; /* use all there is */
+ if (bp == NULL)
+ {
+ /* We do not have a '\n' -- rats */
+ item->len = be - item->base ;
+ qiovec_push(lc->fragments, item) ;
+
+ lc->incomplete = true ; /* have incomplete line in hand */
+
+ bp = be ;
+ break ; /* done */
+ } ;
- /* peel off trailing '\r'.
+ /* We have a '\n' -- hurrah !
+ *
+ * Have a complete line ready to be added to the qiov -- the
+ * current fragment and any buffered ones.
*
- * NB: if have not got a '\n', then this may discard a bare
- * '\r' -- but bare '\r' are undefined in any case.
+ * Trim off any trailing whitespace and establish the length of the
+ * last fragment of the current (complete) line. If the current
+ * fragment is all whitespace, works its way up any in hand fragments.
+ */
+ lc->incomplete = false ; /* definitely not */
+
+ item->len = vio_lc_trim(lc, item, bp) ;
+
+ ++bp ; /* step past the '\n' */
+
+ /* If have fragments in hand, then want to have the first fragment
+ * as the current fragment (currently the current fragment is the
+ * last fragment).
*/
- if ((e > p) && (*(e - 1) == '\r'))
+ if (!qiovec_empty(lc->fragments))
{
- --e ; /* strip the '\r' */
- ++nlx ; /* but account for it */
- }
+ qiovec_push(lc->fragments, item) ;
+ qiovec_shift(lc->fragments, item) ;
+ } ;
- /* have p..e characters and possibly nl to add to the output.
+ /* We are now ready to break the current line up into width
+ * sections, if required -- to the extend allowed by the counter.
+ *
+ * Have trimmed off any trailing whitespace, including back across
+ * any fragments. So:
*
- * Note that if enters the while, (e - p) > 0. So there is at least one
- * character to add. This avoids generating a spurious line ending if
- * the width has been reduced, and the next thing output is a line end.
+ * item = first fragment of the line
*/
- while ((p < e) && (lc->pause != pause))
+ vio_lc_append_line(lc, item) ;
+ } ;
+
+ /* Exhausted the available data or the line count */
+
+ return (bp - (const char*)buf) ; /* what have taken */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Flush anything which the line control has buffered.
+ *
+ * There are two possible cases:
+ *
+ * 1) had collected a complete line in vio_lc_append(), but when putting to
+ * the qiovec was stopped by the line counter.
+ *
+ * So have one or more screen lines buffered up, ready to go.
+ *
+ * 2) had collected an incomplete line in vio_lc_append().
+ *
+ * That will now be flushed as if a newline had been appended, except that
+ * if the result would be a newline *only*, then all the whitespace is
+ * discarded and nothing is output.
+ *
+ * If there is nothing to flush, or an incomplete line turns out to be
+ * entirely whitespace, return false.
+ *
+ * Otherwise we have something to flush, and will attempt to do so.
+ *
+ * Effect of line counter:
+ *
+ * * if definite height, will output only as many screen lines as it can,
+ * including none at all.
+ *
+ * * if indefinite height, will output as many screen lines as it takes,
+ * whatever the state of the line counter.
+ *
+ * * in any case, the counter may reach exactly 0 if the number of screen
+ * lines required is exactly the number of screen lines allowed.
+ *
+ * Returns: true <=> have something to flush (but counter may be exhausted)
+ * false <=> nothing to be flushed
+ */
+extern bool
+vio_lc_flush(vio_line_control lc)
+{
+ qiov_item_t item ;
+
+ if (qiovec_count(lc->fragments) == 0)
+ return false ;
+
+ /* We have something in hand.
+ *
+ * If was an incomplete line, now is the time to trim off any trailing
+ * whitespace -- more or less as if there had been a '\n' at this point.
+ */
+ if (lc->incomplete)
+ {
+ lc->incomplete = false ;
+
+ qiovec_pop(lc->fragments, item) ;
+
+ item->len = vio_lc_trim(lc, item, item->base + item->len) ;
+
+ if (item->len == 0)
{
- const char* t ;
- unsigned col ;
+ assert(qiovec_empty(lc->fragments)) ;
+ return false ; /* it was all whitespace which has all
+ been discarded. */
+ } ;
+
+ qiovec_push(lc->fragments, item) ;
+ } ;
+
+ /* Have something in hand, so now attempt to output it. */
+ qiovec_shift(lc->fragments, item) ;
+ vio_lc_append_line(lc, item) ;
+
+ return true ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Trim trailing whitespace from given fragment -- ' ', '\t' and '\r'.
+ *
+ * If required, pops fragments until finds some non-whitespace, or runs out
+ * of fragments.
+ *
+ * Returns: resulting length.
+ */
+static uint
+vio_lc_trim(vio_line_control lc, qiov_item item, const char* e)
+{
+ const char* p ;
+
+ while (1)
+ {
+ p = item->base ;
+
+ while (e > p)
+ {
+ char ch ;
+
+ ch = *(e - 1) ;
+ if ( (ch != '\r') && (ch != ' ') && (ch != '\t') )
+ return e - p ; /* <<< found non-whitespace <<< exit */
+
+ --e ;
+ } ;
- col = lc->col + (e - p) ; /* NB: e > p */
+ qiovec_pop(lc->fragments, item) ; /* pops a NULL if empty */
- if (col > width)
+ if (item->len == 0)
+ return 0 ;
+
+ e = item->base + item->len ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Append line to the output qiov -- breaking it up into width sections,
+ * inserting newlines and looking out for and updating the line counter.
+ *
+ * If definite height, may stop immediately, without outputting anything.
+ * If indefinite height, will output the current line, whatever the state of
+ * the line counter.
+ *
+ * Note that do not trim whitespace from screen lines.
+ *
+ * At this point all trailing whitespace will have been removed from the
+ * original line.
+ *
+ * Requires that have previously trimmed off any trailing whitespace, including
+ * back across any fragments. So:
+ *
+ * item = first fragment of the line -- NOT in lc->fragments.
+ *
+ * It is possible for the first fragment to be empty (empty line !).
+ */
+static void
+vio_lc_append_line(vio_line_control lc, qiov_item item)
+{
+ bool done = false ;
+
+ while ((lc->counter > 0) || (lc->height == 0))
+ {
+ uint take ;
+
+ /* Take fragments or parts thereof until we have run out of output
+ * (which sets done == true) or reach the width.
+ */
+ take = (lc->width > 0) ? lc->width : UINT_MAX ;
+ while (1)
+ {
+ if (item->len <= take)
{
- /* can use only part of what there is */
- if (width > lc->col)
- t = p + (width - lc->col) ;
- /* take to edge of screen */
+ /* Use entire fragment, and step to next (if any)
+ *
+ * Note that qiovec_push() ignores zero length items, so if
+ * this is an empty line, will push no fragments and will stop
+ * here.
+ */
+ qiovec_push(lc->qiov, item) ;
+
+ if (qiovec_empty(lc->fragments))
+ {
+ done = true ; /* signal all done */
+ break ;
+ }
else
- t = p ;
- assert(t < e) ; /* if not need to deal with nl */
+ {
+ take -= item->len ;
+ qiovec_shift(lc->fragments, item) ;
+ }
}
else
{
- /* can use all of what there is */
- if (nlx == 2) /* if have crlf, use it */
- {
- e += nlx ; /* use the crlf that's there */
- nlx = 0 ; /* used it */
- } ;
+ /* Use leading part of fragment, and reduce same */
+ qiovec_push_this(lc->qiov, item->base, take) ;
+ item->base += take ;
+ item->len -= take ;
- t = e ; /* take it all */
+ break ;
} ;
-
- assert(t >= p) ;
- if (t != p)
- qiovec_push(&lc->qiov, p, (t - p)) ;
-
- /* advance. If not taken all the line, need a crlf */
- p = t ;
-
- if (p < e)
- vio_lc_newline(lc, 1) ;
} ;
- /* If taken all of line, deal with any outstanding nl and nlx */
- if (p == e)
- {
- if (nl)
- vio_lc_newline(lc, (nlx != 0)) ;
+ qiovec_push(lc->qiov, lc->newline) ;
- p += nlx ; /* step past '\r' or '\n' */
- } ;
- } ;
+ /* Count down & exit if all done. */
+ --lc->counter ;
- /* Exhausted the available data or the line count */
- assert(p <= end) ;
+ if (done)
+ return ;
+ } ;
- return (p - (const char*)buf) ; /* what have taken */
+ /* Counter exhausted, but we are not done with the current line.
+ *
+ * unshift the current item onto the (front of) the fragments qiovec.
+ * (We know we can do this straightforwardly, because either the qiovec is
+ * empty, or what was the first item has previously been shifted off.)
+ */
+ qiovec_unshift(lc->fragments, item) ;
} ;
/*------------------------------------------------------------------------------
@@ -366,15 +595,52 @@ vio_lc_append(vio_line_control lc, const void* buf, size_t len)
* -1 => failed -- see errno
*
* Sets lc->writing if write does not complete
+ *
+ * NB: when says "all done" (or failed) the caller may release all buffered
+ * material that has been accepted by vio_lc_append() -- and not before.
*/
extern int
vio_lc_write_nb(int fd, vio_line_control lc)
{
int ret ;
- ret = qiovec_write_nb(fd, &lc->qiov) ;
+ ret = qiovec_write_nb(fd, lc->qiov) ;
+
+ if (ret <= 0)
+ {
+ /* About to promise that all buffered material previously accepted by
+ * vio_lc_append() may now be released.
+ *
+ * Unfortunately, if we have any line fragments in hand, have to buffer
+ * those locally, now.
+ *
+ * NB: if have a particularly long original line, or a particularly
+ * narrow or short screen, it is possible to pass through here
+ * more than once for the same original line.
+ *
+ * In this obscure case, the line will already be buffered in
+ * lc->here. Happily qs_clear() does not disturb the body of the
+ * qstring, and qs_append_str_n will append from within its own
+ * body !
+ */
+ if (!qiovec_empty(lc->fragments))
+ {
+ qs_set_len_nn(lc->here, 0) ; /* ready to append stuff */
+ do
+ {
+ qiov_item_t item ;
+
+ qiovec_shift(lc->fragments, item) ;
+
+ qs_append_str_n(lc->here, item->base, item->len) ;
+ }
+ while (!qiovec_empty(lc->fragments)) ;
- lc->writing = (ret > 0) ;
+ /* One fragment in hand, collected from all previous fragments
+ */
+ qiovec_push_this(lc->fragments, qs_char(lc->here), qs_len(lc->here)) ;
+ } ;
+ } ;
return ret ;
} ;
diff --git a/lib/vio_lines.h b/lib/vio_lines.h
index ffef94ec..6a3d13e9 100644
--- a/lib/vio_lines.h
+++ b/lib/vio_lines.h
@@ -22,16 +22,9 @@
#ifndef _ZEBRA_VIO_LINES_H
#define _ZEBRA_VIO_LINES_H
-#include "zebra.h"
-
-#include <stddef.h>
-#include <stdint.h>
-
+#include "misc.h"
#include "qiovec.h"
-
-#ifndef Inline
-#define Inline static inline
-#endif
+#include "qstring.h"
/*==============================================================================
*
@@ -40,52 +33,143 @@
/*------------------------------------------------------------------------------
* Line control -- collecting lines of a given width for output.
*
- * NB: a completely zero structure is a valid, clear vio_line_control.
+ * NB: need to explicitly initialise line control in order to set the required
+ * new line, the qiovec vectors and a qstring buffer !
*/
-
-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 */
- unsigned height ; /* console height -- 0 => indefinite */
+ uint width ; /* console width -- 0 => HUGE */
+ uint height ; /* console height -- 0 => indefinite */
+
+ int counter ; /* number of lines to next pause
+ <= 0 => paused. */
+
+ bool incomplete ; /* fragments in hand are an incomplete line */
+ qiovec fragments ;
+ qstring here ; /* any fragments after write */
- unsigned pause ; /* number of lines to next pause
- 0 => indefinite */
- bool paused ; /* true <=> last append stopped on pause */
+ qiovec qiov ; /* output screen lines */
- unsigned col ; /* current column position */
- unsigned line ; /* line number of last complete line */
+ qiov_item_t newline ; /* the required sequence */
+} ;
+
+typedef struct vio_line_control* vio_line_control ;
+typedef struct vio_line_control vio_line_control_t[1] ;
- struct qiovec qiov ; /* iovec control */
- bool writing ; /* write started, but not completed */
+enum
+{
+ VIO_LINE_CONTROL_INIT_ALL_ZEROS = false
} ;
/*==============================================================================
* 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) ;
+ int height,
+ const char* newline) ;
+Inline vio_line_control vio_lc_new(int width, int height,
+ const char* newline) ;
+extern vio_line_control vio_lc_reset(vio_line_control lc,
+ free_keep_b free_structure) ;
+Inline vio_line_control vio_lc_free(vio_line_control lc) ;
-#define vio_lc_reset_keep(lc) vio_lc_reset(lc, 0)
-#define vio_lc_reset_free(lc) vio_lc_reset(lc, 1)
-
-Inline bool vio_lc_empty(vio_line_control lc) ;
extern void vio_lc_clear(vio_line_control lc) ;
extern void vio_lc_set_window(vio_line_control lc, int width, int height) ;
-extern void vio_lc_set_pause(vio_line_control lc) ;
+Inline void vio_lc_counter_reset(vio_line_control lc) ;
+Inline void vio_lc_clear_pause(vio_line_control lc) ;
+
+Inline bool vio_lc_counter_is_exhausted(vio_line_control lc) ;
+Inline bool vio_lc_have_complete_line_in_hand(vio_line_control lc) ;
+Inline bool vio_lc_is_empty(vio_line_control lc) ;
+
extern size_t vio_lc_append(vio_line_control lc, const void* buf, size_t len) ;
+extern bool vio_lc_flush(vio_line_control lc) ;
extern int vio_lc_write_nb(int fd, vio_line_control lc) ;
/*------------------------------------------------------------------------------
+ * Create new line control.
+ *
+ * Returns: address of new line control.
+ */
+Inline vio_line_control
+vio_lc_new(int width, int height, const char* newline)
+{
+ return vio_lc_init_new(NULL, width, height, newline) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Free given line control (if any).
+ *
+ * It is the caller's responsibility to free anything that the line control may
+ * point to.
+ *
+ * Returns: NULL
+ */
+Inline vio_line_control
+vio_lc_free(vio_line_control lc)
+{
+ return vio_lc_reset(lc, free_it) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Counter reset.
+ */
+Inline void
+vio_lc_counter_reset(vio_line_control lc)
+{
+ lc->counter = (lc->height > 0) ? lc->height : 100 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * If no height is set, set counter to large number -- to do a tranche of
+ * output.
+ *
+ * Otherwise, if the line control is paused (or would pause as soon as any
+ * output is sent), reset the counter.
+ */
+Inline void
+vio_lc_clear_pause(vio_line_control lc)
+{
+ if ((lc->counter <= 0) || (lc->height == 0))
+ vio_lc_counter_reset(lc) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Is the given line control counter exhausted ?
+ *
+ * Any attempt to output more stuff will be prevented -- except for
+ * vio_lc_flush() which will succeed if height is indefinite.
+ */
+Inline bool
+vio_lc_counter_is_exhausted(vio_line_control lc)
+{
+ return (lc->counter <= 0) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Do we have a complete line "in hand" ?
+ *
+ * This will only be the case if have a definite height, and the line counter
+ * has expired.
+ */
+Inline bool
+vio_lc_have_complete_line_in_hand(vio_line_control lc)
+{
+ return !lc->incomplete && !qiovec_empty(lc->fragments) ;
+} ;
+
+/*------------------------------------------------------------------------------
* Is given line control empty ?
+ *
+ * Is empty if the qiov is empty and there is nothing in hand.
+ *
+ * NB: if there is something in hand, it may be complete or incomplete.
*/
Inline bool
-vio_lc_empty(vio_line_control lc)
+vio_lc_is_empty(vio_line_control lc)
{
- return qiovec_empty(&lc->qiov) ;
+ return qiovec_empty(lc->qiov) && qiovec_empty(lc->fragments) ;
} ;
#endif /* _ZEBRA_VIO_LINES_H */
diff --git a/lib/vty.c b/lib/vty.c
index 9b1e5d87..173b59e1 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,78 @@
* 02111-1307, USA.
*/
-#include "zebra.h"
-#include <stdbool.h>
+#include "misc.h"
#include "lib/version.h"
-#include "vty_io.h"
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <unistd.h>
+
#include "vty.h"
-#include "uty.h"
+#include "vty_local.h"
+#include "vty_io.h"
+#include "vty_io_file.h"
+#include "vty_command.h"
#include "vty_cli.h"
+#include "vty_log.h"
+#include "vio_fifo.h"
+#include "log_local.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"
+#include "qpath.h"
+#include "network.h"
/*==============================================================================
- * Variables etc. (see uty.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 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,43 +101,33 @@ 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.
*/
-qpn_nexus vty_cli_nexus = NULL ;
-qpn_nexus vty_cmd_nexus = NULL ;
+bool vty_nexus ; /* true <=> in the qpthreads world */
+bool vty_multi_nexus ; /* true <=> more than one qpthread */
+
+qpn_nexus vty_cli_nexus = NULL ;
+qpn_nexus vty_cmd_nexus = NULL ;
/* List of all known vio */
-vty_io vio_list_base = NULL ;
+vty_io vio_live_list = NULL ;
/* List of all vty which are in monitor state. */
-vty_io vio_monitors_base = NULL ;
+vty_io vio_monitor_list = NULL ;
/* List of all vty which are on death watch */
-vty_io vio_death_watch = NULL ;
+vty_io vio_death_watch = NULL ;
-/* Vty timeout value -- see "exec timeout" command */
-unsigned long vty_timeout_val = VTY_TIMEOUT_DEFAULT;
+/* List of child processes in our care */
+vio_child vio_childer_list = NULL ;
-/* 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 ;
-
-/* Watch-dog timer. */
-union vty_watch_dog vty_watch_dog = { NULL } ;
+/* See vty_child_signal_nexus_set() */
+qpt_mutex_t vty_child_signal_mutex ;
+qpn_nexus vty_child_signal_nexus = NULL ;
/*------------------------------------------------------------------------------
* VTYSH stuff
@@ -109,7 +141,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 *);
/*------------------------------------------------------------------------------
* Tracking the initialisation state.
@@ -143,23 +178,27 @@ 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) ;
vty_master = master_thread; /* Local pointer to the master thread */
- vty_save_cwd (); /* need cwd for config reading */
-
- vio_list_base = NULL ; /* no VTYs yet */
- vio_monitors_base = NULL ;
+ vio_live_list = NULL ; /* no VTYs yet */
vio_death_watch = NULL ;
+ vio_childer_list = NULL ;
- vty_cli_nexus = NULL ; /* not running qnexus-wise */
+ vty_nexus = false ; /* not running qnexus-wise */
+ vty_multi_nexus = false ; /* not more than one thread either */
+ vty_cli_nexus = NULL ;
vty_cmd_nexus = NULL ;
- vty_watch_dog.anon = NULL ; /* no watch dog */
+ vty_child_signal_nexus = NULL ; /* none, yet */
+
+ uty_watch_dog_init() ; /* empty watch dog */
+
+ uty_init_monitor() ;
uty_init_commands() ; /* install nodes */
@@ -190,10 +229,14 @@ vty_init_r (qpn_nexus cli, qpn_nexus cmd)
{
assert(vty_init_state == vty_init_1st_stage) ;
- vty_cli_nexus = cli ;
- vty_cmd_nexus = cmd ;
+ vty_nexus = true ;
+ vty_multi_nexus = (cli != cmd) ;
+ vty_cli_nexus = cli ;
+ vty_cmd_nexus = cmd ;
- qpt_mutex_init(&vty_mutex, qpt_mutex_recursive);
+ qpt_mutex_init(vty_mutex, qpt_mutex_recursive);
+
+ qpt_mutex_init(vty_child_signal_mutex, qpt_mutex_quagga);
vty_init_state = vty_init_2nd_stage ;
} ;
@@ -214,7 +257,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 +265,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 +295,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 +310,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 +341,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 +353,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 +375,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 +383,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,32 +433,48 @@ 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_child_close_register() ;
VTY_UNLOCK() ;
- qpt_mutex_destroy(&vty_mutex, 0);
+ qpt_mutex_destroy(vty_mutex, 0);
+ qpt_mutex_destroy(vty_child_signal_mutex, 0);
vty_init_state = vty_init_terminated ;
}
/*------------------------------------------------------------------------------
- * Reset -- final or for SIGHUP
+ * Reset -- for SIGHUP or at final curtain.
+ *
+ * For SIGHUP is called via vty_reset_because(), and is sitting in the
+ * vty_cli_nexus (if pthreaded) with the message queues still running.
+ *
+ * For final curtain will
+ *
+ *
+ * is called by vty_terminate(), by which time all qnexus
+ * have been shut down, so no message queues and no timers etc, are running.
+ *
*
* 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.
*
@@ -429,45 +490,26 @@ uty_reset (bool curtains, const char* why)
vty_io vio ;
vty_io next ;
- VTY_ASSERT_LOCKED() ;
- VTY_ASSERT_CLI_THREAD() ;
+ VTY_ASSERT_CLI_THREAD_LOCKED() ;
uty_close_listeners() ;
- next = sdl_head(vio_list_base) ;
+ next = sdl_head(vio_live_list) ;
while (next != NULL)
{
vio = next ;
next = sdl_next(vio, vio_list) ;
- if (vio->type == VTY_TERM)
- cq_revoke(vio->vty) ;
-
- if (why != NULL)
- vio->close_reason = why ;
-
- if (curtains)
- uty_close(vio) ;
- else
- uty_half_close(vio, why) ;
+ uty_close(vio, why, curtains) ;
} ;
- 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 */
@@ -484,59 +526,78 @@ 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, node_type_t node)
{
struct vty* vty ;
VTY_LOCK() ;
- vty = uty_new(type, -1) ; /* fails for VTY_TERM or VTY_SHELL_SERV */
+ vty = uty_new(type, node) ;
VTY_UNLOCK() ;
return vty ;
} ;
-/*------------------------------------------------------------------------------
- * Close the given VTY
- */
-extern void
-vty_close (struct vty *vty)
-{
- VTY_LOCK() ;
- uty_close(vty->vio) ;
- VTY_UNLOCK() ;
-}
-
/*==============================================================================
* General VTY output.
*
- * This is mostly used during command execution, to output the results of the
- * command.
+ * This is used during command execution, to output the results of commands.
*
* All these end up in uty_vout -- see vty_io.
*/
/*------------------------------------------------------------------------------
* VTY output -- cf fprintf !
+ *
+ * This is for command output, which may later be suppressed
+ *
+ * Returns: >= 0 => OK
+ * < 0 => failed (see errno)
*/
extern int
-vty_out (struct vty *vty, const char *format, ...)
+vty_out(struct vty *vty, const char *format, ...)
{
- int result;
+ int ret ;
+ va_list args ;
VTY_LOCK() ;
- va_list args;
- va_start (args, format);
- result = uty_vout(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 result;
+ return ret ;
+}
+
+/*------------------------------------------------------------------------------
+ * VTY output -- cf write
+ *
+ * Returns: >= 0 => OK
+ * < 0 => failed (see errno)
+ */
+extern int
+vty_write(struct vty *vty, const void* buf, int n)
+{
+ VTY_LOCK() ;
+
+ vio_fifo_put_bytes(vty->vio->obuf, buf, n) ;
+
+ VTY_UNLOCK() ;
+ return 0 ;
}
/*------------------------------------------------------------------------------
* VTY output -- output a given numnber of spaces
+ *
+ * This is for command output, which may be suppressed
+ *
+ * Returns: >= 0 => OK
+ * < 0 => failed (see errno)
*/
/* 1 2 3 4 */
@@ -544,14 +605,19 @@ vty_out (struct vty *vty, const char *format, ...)
const char vty_spaces_string[] = " " ;
CONFIRM(VTY_MAX_SPACES == (sizeof(vty_spaces_string) - 1)) ;
-extern void
+extern int
vty_out_indent(struct vty *vty, int indent)
{
- while (indent > 0)
+ int ret ;
+
+ ret = 0 ;
+ while ((indent > 0) && (ret >= 0))
{
- vty_out(vty, VTY_SPACES(indent)) ;
+ ret = vty_out(vty, VTY_SPACES(indent)) ;
indent -= VTY_MAX_SPACES ;
}
+
+ return ret ;
} ;
/*------------------------------------------------------------------------------
@@ -565,9 +631,9 @@ vty_time_print (struct vty *vty, int cr)
quagga_timestamp(0, buf, sizeof(buf)) ;
if (cr)
- vty_out (vty, "%s%s", buf, VTY_NEWLINE);
+ vty_out(vty, "%s\n", buf);
else
- vty_out (vty, "%s ", buf);
+ vty_out(vty, "%s ", buf);
return;
}
@@ -575,474 +641,113 @@ vty_time_print (struct vty *vty, int cr)
/*------------------------------------------------------------------------------
* Say hello to vty interface.
*/
-void
+extern void
vty_hello (struct vty *vty)
{
- VTY_LOCK() ;
+ qpath path ;
+ const char* string ;
-#ifdef QDEBUG
- uty_out (vty, "%s%s", debug_banner, VTY_NEWLINE);
-#endif
- if (host.motdfile)
- {
- FILE *f;
- char buf[4096];
-
- f = fopen (host.motdfile, "r");
- if (f)
- {
- while (fgets (buf, sizeof (buf), f))
- {
- char *s;
- /* work backwards to ignore trailing isspace() */
- for (s = buf + strlen (buf); (s > buf) && isspace ((int)*(s - 1));
- s--);
- *s = '\0';
- uty_out (vty, "%s%s", buf, VTY_NEWLINE);
- }
- fclose (f);
- }
- else
- uty_out (vty, "MOTD file %s not found%s", host.motdfile, VTY_NEWLINE);
- }
- else if (host.motd)
- uty_out (vty, "%s", host.motd);
-
- VTY_UNLOCK() ;
-}
-
-/*------------------------------------------------------------------------------
- * Clear the contents of the command output FIFO etc.
- */
-extern void
-vty_out_clear(struct vty* vty)
-{
VTY_LOCK() ;
- uty_out_clear(vty->vio) ;
- VTY_UNLOCK() ;
-} ;
-
-/*==============================================================================
- * 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(vty->vio->type == VTY_TERM) ;
-
- /* 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 */
- } ;
+ path = (host.motdfile != NULL) ? qpath_dup(host.motdfile) : NULL ;
+ string = host.motd ;
- /* Deal with the return code */
- switch (ret)
- {
- case CMD_ERR_AMBIGUOUS:
- uty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE);
- break;
+ VTY_UNLOCK() ;
- case CMD_ERR_NO_MATCH:
- uty_out (vty, "%% Unknown command.%s", VTY_NEWLINE) ;
- break;
+ if (qdebug)
+ vty_out (vty, "%s\n", debug_banner);
- case CMD_ERR_INCOMPLETE:
- uty_out (vty, "%% Command incomplete.%s", VTY_NEWLINE);
- break;
+ if (path != NULL)
+ vty_cat_file(vty, path, "motd file") ;
+ else if (string != NULL)
+ vty_out(vty, "%s", string);
- default:
- break ;
- } ;
+ /* This "\n" is a bit magic... if the motd file does not end in a "\n",
+ * then this makes sure that we start on a new line.
+ *
+ * Similarly, if the motd string doesn't end '\n', then this makes sure.
+ *
+ * This will also trim trailing space from the end of the motd message.
+ *
+ * Generally these will end in '\n', so this produces the extra blank line
+ * before the cheerful "User authentication" message, which is the most
+ * likely next line.
+ */
+ vty_out(vty, "\n") ;
- return ret ;
+ qpath_free(path) ;
} ;
/*------------------------------------------------------------------------------
- * 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
+ * "cat" file to vty
*/
-extern enum cmd_return_code
-uty_auth (struct vty *vty, const char *buf, enum cli_do cli_do)
+extern cmd_return_code_t
+vty_cat_file(vty vty, qpath path, const char* desc)
{
- char *passwd = NULL;
- enum node_type next_node = 0;
- int fail;
- char *crypt (const char *, const char *);
- enum cmd_return_code ret ;
+ int fd ;
+ void* buf ;
- vty_io vio = vty->vio ;
+ fd = uty_vfd_file_open(qpath_string(path), vfd_io_read | vfd_io_blocking) ;
- 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 (fd < 0)
{
- if (host.encrypt)
- fail = strcmp (crypt(buf, passwd), passwd);
- else
- fail = strcmp (buf, passwd);
- }
- else
- fail = 1;
+ vty_out (vty, "Cannot open %s file '%s': %s (%s)\n", desc,
+ qpath_string(path), errtostr(errno, 0).str,
+ errtoname(errno, 0).str) ;
+ return CMD_WARNING;
+ } ;
- ret = CMD_SUCCESS ;
+ enum { buffer_size = 64 * 1024 } ;
+ buf = XMALLOC(MTYPE_TMP, buffer_size) ;
- if (! fail)
- {
- vio->fail = 0;
- vty->node = next_node; /* Success ! */
- }
- else
+ while (1)
{
- vio->fail++;
- if (vio->fail >= 3)
- {
- if (vty->node == AUTH_NODE)
- {
- ret = uty_cmd_close(vty, "Bad passwords, too many failures!%s") ;
- }
- else
- {
- /* AUTH_ENABLE_NODE */
- vio->fail = 0;
- uty_out (vty, "%% Bad enable passwords, too many failures!%s",
- VTY_NEWLINE);
- vty->node = restricted_mode ? RESTRICTED_NODE : VIEW_NODE;
-
- ret = CMD_WARNING ;
- }
- }
- }
-
- return ret ;
-} ;
+ int r ;
-/*------------------------------------------------------------------------------
- * 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 ;
+ r = readn(fd, buf, buffer_size) ;
- VTY_LOCK() ;
-
- ret = CMD_SUCCESS ;
- switch (vty->node)
- {
- case VIEW_NODE:
- case ENABLE_NODE:
- case RESTRICTED_NODE:
- if (vty_shell (vty))
- exit (0);
+ if (r > 0)
+ vty_write(vty, buf, r) ; // TODO push ??
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() ;
+ {
+ if (r == 0)
+ break ;
- 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;
- }
+ // TODO error handling ....
+ } ;
+ } ;
- VTY_UNLOCK() ;
- return CMD_SUCCESS ;
-} ;
+ close(fd) ;
-/*------------------------------------------------------------------------------
- * 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 ;
+ return CMD_SUCCESS;
} ;
/*------------------------------------------------------------------------------
- * 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
+ * Clear the contents of the command output FIFO etc.
*/
-extern enum cmd_return_code
-uty_stop_input(struct vty *vty)
+extern void
+vty_out_clear(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 ;
+ VTY_LOCK() ;
+ uty_out_clear(vty->vio) ;
+ VTY_UNLOCK() ;
} ;
-/*------------------------------------------------------------------------------
- * 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.
+/*==============================================================================
+ * Deal with SIGCHLD "event".
*
- * Returns: command return code
+ * NB: if there is a nexus to be signalled, we do that *before* attempting to
+ * lock the VTY -- because in that case the VTY will be locked by that
+ * nexus !
*/
-extern enum cmd_return_code
-uty_end_config (struct vty *vty)
+extern void
+vty_sigchld(void)
{
- 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 ;
-}
+ vty_child_signal_nexus_signal() ;
-/*------------------------------------------------------------------------------
- * 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) ;
+ VTY_LOCK() ;
+ uty_sigchld() ;
+ VTY_UNLOCK() ;
} ;
/*==============================================================================
@@ -1066,18 +771,19 @@ 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 (qpath path) ;
+static void vty_read_config_file (int conf_fd, const char* name,
+ cmd_command first_cmd, bool ignore_warnings,
+ bool full_lex) ;
/*------------------------------------------------------------------------------
* 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);
}
/*------------------------------------------------------------------------------
@@ -1098,15 +804,14 @@ 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;
+ const char *name ;
+ qpath path ;
+ int conf_fd ;
/* Deal with VTYSH_ENABLED magic */
if (VTYSH_ENABLED && (config_file == NULL))
@@ -1132,7 +837,7 @@ vty_read_config_first_cmd_special(char *config_file,
{
ret = stat (integrate_default, &conf_stat);
if (ret >= 0)
- return;
+ return; /* TODO leaves host.config_file NULL */
}
} ;
@@ -1140,130 +845,115 @@ vty_read_config_first_cmd_special(char *config_file,
if (config_file == NULL)
config_file = config_default ;
- if (! IS_DIRECTORY_SEP (config_file[0]))
- {
- getcwd (cwd, sizeof(cwd)) ;
- tmp = XMALLOC (MTYPE_TMP, strlen (cwd) + strlen (config_file) + 2) ;
- sprintf (tmp, "%s/%s", cwd, config_file);
- fullpath = tmp;
- }
- else
- {
- tmp = NULL ;
- fullpath = config_file;
- } ;
+ path = qpath_make(config_file, host.cwd) ;
+ name = qpath_string(path) ;
/* try to open the configuration file */
- confp = fopen (fullpath, "r");
+ conf_fd = uty_vfd_file_open(name, vfd_io_read) ;
- if (confp == NULL)
+ if (conf_fd < 0)
{
fprintf (stderr, "%s: failed to open configuration file %s: %s\n",
- __func__, fullpath, errtostr(errno, 0).str);
+ __func__, name, errtostr(errno, 0).str) ;
- confp = vty_use_backup_config (fullpath);
- if (confp)
+ conf_fd = vty_use_backup_config (path);
+ if (conf_fd >= 0)
fprintf (stderr, "WARNING: using backup configuration file!\n");
else
{
fprintf (stderr, "can't open backup configuration file [%s%s]\n",
- fullpath, CONF_BACKUP_EXT);
+ name, CONF_BACKUP_EXT);
exit(1);
}
} ;
-#ifdef QDEBUG
- fprintf(stderr, "Reading config file: %s\n", fullpath);
-#endif
+ if (qdebug)
+ fprintf(stderr, "Reading config file: %s\n", name);
- vty_read_file (confp, first_cmd, ignore_warnings);
- fclose (confp);
+ vty_read_config_file(conf_fd, name, first_cmd, ignore_warnings, false);
- host_config_set (fullpath);
+ cmd_host_config_set(path) ;
-#ifdef QDEBUG
- fprintf(stderr, "Finished reading config file\n");
-#endif
+ qpath_free(path) ;
- if (tmp)
- XFREE (MTYPE_TMP, tmp);
+ if (qdebug)
+ fprintf(stderr, "Finished reading config file\n");
}
/*------------------------------------------------------------------------------
* Try to use a backup configuration file.
*
- * Having failed to open the file "<fullpath>", if there is a file called
- * "<fullpath>.sav" that can be opened for reading, then:
+ * Having failed to open the file "<path>", if there is a file called
+ * "<path>.sav" that can be opened for reading, then:
*
* - make a copy of that file
- * - call it "<fullpath>"
+ * - call it "<path>"
* - 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 "<path>.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 (qpath path)
{
- char *tmp_path ;
- struct stat buf;
- int ret, tmp, sav;
- int c;
- char buffer[4096] ;
-
- enum { xl = 32 } ;
- tmp_path = malloc(strlen(fullpath) + xl) ;
+ qpath temp ;
+ char* name ;
+ int sav_fd, tmp_fd, err ;
+ bool ok ;
/* construct the name "<fullname>.sav", and try to open it. */
- confirm(xl > sizeof(CONF_BACKUP_EXT)) ;
- sprintf (tmp_path, "%s%s", fullpath, CONF_BACKUP_EXT) ;
+ temp = qpath_dup(path) ;
+ qpath_extend_str(temp, CONF_BACKUP_EXT) ;
- sav = -1 ;
- if (stat (tmp_path, &buf) != -1)
- sav = open (tmp_path, O_RDONLY);
+ sav_fd = -1 ;
+ tmp_fd = -1 ;
- if (sav < 0)
- {
- free (tmp_path);
- return NULL;
- } ;
+ sav_fd = uty_vfd_file_open(qpath_string(temp),
+ vfd_io_read | vfd_io_blocking) ;
/* construct a temporary file and copy "<fullpath.sav>" to it. */
- confirm(xl > sizeof(".XXXXXX"))
- sprintf (tmp_path, "%s%s", fullpath, ".XXXXXX") ;
+ qpath_extend_str(temp, ".XXXXXX") ;
+ name = qpath_char_string(temp) ;
- /* Open file to configuration write. */
- tmp = mkstemp (tmp_path);
- if (tmp < 0)
- {
- free (tmp_path);
- close(sav);
- return NULL;
- }
+ if (sav_fd >= 0)
+ tmp_fd = mkstemp(name) ;
+
+ ok = ((sav_fd >= 0) && (tmp_fd >= 0)) ;
+
+ if (ok)
+ ok = (copyn(tmp_fd, sav_fd) == 0) ;
- while((c = read (sav, buffer, sizeof(buffer))) > 0)
- write (tmp, buffer, c);
+ err = errno ;
- close (sav);
- close (tmp);
+ if (tmp_fd >= 0)
+ close(tmp_fd) ;
+ if (sav_fd >= 0)
+ close(sav_fd) ;
- /* Make sure that have the required file status */
- if (chmod(tmp_path, CONFIGFILE_MASK) != 0)
+ /* If now OK, then have copied the .sav to the temporary file. */
+
+ if (ok)
{
- unlink (tmp_path);
- free (tmp_path);
- return NULL;
- }
+ /* Make sure that have the required file status */
+ ok = chmod(name, CONFIGFILE_MASK) == 0 ;
- /* Make <fullpath> be a name for the new file just created. */
- ret = link (tmp_path, fullpath) ;
+ /* Finally, make a link with the original name */
+ if (ok)
+ ok = link(name, qpath_string(path)) == 0 ;
- /* Discard the temporary, now */
- unlink (tmp_path) ;
- free (tmp_path) ;
+ err = errno ;
+ } ;
+
+ if (tmp_fd >= 0) /* if made a temporary, done with it now */
+ unlink(name) ;
+
+ qpath_free(temp) ; /* done with the qpath */
- /* If link was successful, try to open -- otherwise, failed. */
- return (ret == 0) ? fopen (fullpath, "r") : NULL ;
+ if (ok)
+ return uty_vfd_file_open(qpath_string(path), vfd_io_read) ;
+
+ errno = err ;
+ return -1 ;
} ;
/*------------------------------------------------------------------------------
@@ -1287,134 +977,65 @@ 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_config_file (int fd, const char* name, cmd_command first_cmd,
+ bool ignore_warnings, bool full_lex)
{
- enum cmd_return_code ret ;
- struct vty *vty ;
-
- /* 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) ;
-
- /* 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;
+ cmd_return_code_t ret ;
+ vty vty ;
- case CMD_ERROR:
- fprintf (stderr, "%% Error...\n");
- break;
+ vty = vty_config_read_open(fd, name, full_lex) ;
- case CMD_ERR_AMBIGUOUS:
- fprintf (stderr, "%% Ambiguous command.\n");
- break;
+ vty_cmd_loop_prepare(vty) ;
- case CMD_ERR_NO_MATCH:
- fprintf (stderr, "%% There is no such command.\n");
- break;
+ zlog_info("Started reading configuration: %s", name) ;
- case CMD_ERR_INCOMPLETE:
- fprintf (stderr, "%% Incomplete command.\n");
- break;
+ ret = cmd_read_config(vty, first_cmd, ignore_warnings) ;
- default:
- fprintf(stderr, "%% (unknown cause %d)\n", ret) ;
- break ;
- } ;
+ zlog_info("Finished reading configuration%s",
+ (ret == CMD_SUCCESS) ? "." : " -- FAILED") ;
- uty_out_fflush(vty->vio, stderr) ; /* flush command output buffer */
+ vty_cmd_loop_exit(vty) ;
- exit (1);
- } ;
-
- uty_close(vty->vio) ;
- VTY_UNLOCK() ;
+ if (ret != CMD_SUCCESS)
+ exit(1) ;
} ;
-/*==============================================================================
- * Configuration node/state handling
- *
- * At most one VTY may hold the configuration symbol of power at any time.
- */
-
/*------------------------------------------------------------------------------
- * Attempt to gain the configuration symbol of power
- *
- * If succeeds, set the given node.
+ * Push the given fd as the VOUT_CONFIG.
*
- * Returns: true <=> now own the symbol of power.
+ * Note that this is a "blocking" vf, so can open and close in the cmd thread.
*/
-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 == 0)
- {
- vty->vio->config = 1 ;
- vty_config = 1 ;
- } ;
-
- result = vty->vio->config;
+ vio = vty->vio ;
- if (result)
- vty->node = node ;
+ vf = uty_vf_new(vio, "config write", fd, vfd_file,
+ vfd_io_read | vfd_io_blocking) ;
+ uty_vout_push(vio, vf, VOUT_CONFIG, NULL, NULL, 32 * 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 pop the VOUT_CONFIG.
*/
-extern void
-vty_config_unlock (struct vty *vty, enum node_type node)
+extern cmd_return_code_t
+vty_close_config_write(struct vty* vty, bool final)
{
+ cmd_return_code_t ret ;
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 == 1) && (vty->vio->config == 1))
- {
- vty->vio->config = 0;
- vty_config = 0;
- }
+ ret = uty_vout_pop(vty->vio, final) ;
- assert(node <= MAX_NON_CONFIG_NODE) ;
- vty->node = node ;
+ VTY_UNLOCK() ;
+
+ return ret ;
} ;
/*==============================================================================
@@ -1431,24 +1052,26 @@ DEFUN_CALL (config_who,
VTY_LOCK() ;
- vio = vio_list_base ;
- while (vio != NULL) /* TODO: show only VTY_TERM ??? */
+ vio = vio_live_list ; /* once locked */
+
+ while (vio != NULL) /* TODO: show only VTY_TERM ??? */
{
- uty_out (vty, "%svty[%d] connected from %s.%s",
- vio->config ? "*" : " ",
- i, uty_get_name(vio), VTY_NEWLINE);
+ 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;
}
/* Move to vty configuration mode. */
-DEFUN_CALL (line_vty,
- line_vty_cmd,
- "line vty",
- "Configure a terminal line\n"
- "Virtual terminal\n")
+DEFUN_ATTR (line_vty,
+ line_vty_cmd,
+ "line vty",
+ "Configure a terminal line\n"
+ "Virtual terminal\n",
+ CMD_ATTR_DIRECT + CMD_ATTR_NODE + VTY_NODE)
{
VTY_LOCK() ;
vty->node = VTY_NODE;
@@ -1456,7 +1079,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)
{
@@ -1474,10 +1101,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 (vty_term(vty) || vty_shell_serv(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;
@@ -1520,10 +1146,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;
@@ -1537,23 +1162,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) ))
{
- uty_out (vty, "Access-class is not currently applied to vty%s",
- VTY_NEWLINE);
- 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
@@ -1566,10 +1193,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;
@@ -1584,26 +1211,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) ))
{
- uty_out (vty, "IPv6 access-class is not currently applied to vty%s",
- VTY_NEWLINE);
- 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 */
@@ -1614,7 +1240,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;
}
@@ -1626,7 +1252,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;
}
@@ -1638,7 +1264,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;
}
@@ -1650,7 +1276,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;
}
@@ -1662,7 +1288,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;
}
@@ -1675,7 +1301,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;
}
@@ -1718,26 +1344,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_out (vty, " %s%s", line->char_body, VTY_NEWLINE);
-
- index++;
- }
+ if (vty->type == VTY_TERMINAL)
+ uty_cli_hist_show(vty->vio->vin->cli) ;
VTY_UNLOCK() ;
return CMD_SUCCESS;
@@ -1751,35 +1361,40 @@ DEFUN_CALL (show_history,
static int
vty_config_write (struct vty *vty)
{
- vty_out (vty, "line vty%s", VTY_NEWLINE);
+ vty_io vio ;
+
+ VTY_LOCK() ; /* while accessing the host.xxx */
+
+ vio = vty->vio ;
- if (vty_accesslist_name)
- vty_out (vty, " access-class %s%s",
- vty_accesslist_name, VTY_NEWLINE);
+ uty_out (vio, "line vty\n");
- if (vty_ipv6_accesslist_name)
- vty_out (vty, " ipv6 access-class %s%s",
- vty_ipv6_accesslist_name, VTY_NEWLINE);
+ if (host.vty_accesslist_name)
+ uty_out (vio, " access-class %s\n", host.vty_accesslist_name);
+
+ if (host.vty_ipv6_accesslist_name)
+ uty_out (vio, " 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%s",
- vty_timeout_val / 60,
- vty_timeout_val % 60, VTY_NEWLINE);
+ if (host.vty_timeout_val != VTY_TIMEOUT_DEFAULT)
+ uty_out (vio, " exec-timeout %ld %ld\n", host.vty_timeout_val / 60,
+ host.vty_timeout_val % 60);
/* login */
- if (no_password_check)
- vty_out (vty, " no login%s", VTY_NEWLINE);
+ if (host.no_password_check)
+ uty_out (vio, " 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%s", VTY_NEWLINE);
+ uty_out (vio, " no anonymous restricted\n");
else
- vty_out (vty, " anonymous restricted%s", VTY_NEWLINE);
+ uty_out (vio, " anonymous restricted\n");
}
- vty_out (vty, "!%s", VTY_NEWLINE);
+ uty_out (vio, "!\n");
+
+ VTY_UNLOCK() ;
return CMD_SUCCESS;
}
@@ -1789,96 +1404,65 @@ vty_config_write (struct vty *vty)
*/
/*------------------------------------------------------------------------------
- * Save cwd
- *
- * This is done early in the morning so that any future operations on files
- * can use the original cwd if required.
+ * Get cwd as at start-up. Never changed -- so no locking required.
*/
-static void
-vty_save_cwd (void)
+extern qpath
+vty_getcwd (qpath qp)
{
- char cwd[MAXPATHLEN];
- char *c;
+ VTY_LOCK() ;
- c = getcwd (cwd, MAXPATHLEN);
+ qp = qpath_copy(qp, host.cwd) ;
- if (!c)
- {
- chdir (SYSCONFDIR);
- getcwd (cwd, MAXPATHLEN);
- }
-
- vty_cwd = XSTRDUP(MTYPE_TMP, cwd) ;
-} ;
+ VTY_UNLOCK() ;
-/*------------------------------------------------------------------------------
- * Get cwd as at start-up. Never changed -- so no locking required.
- */
-char *
-vty_get_cwd ()
-{
- return vty_cwd;
+return qp ;
}
/*==============================================================================
- * Access functions for VTY values, where locking is or might be required.
+ * Access functions for vio values, where locking is or might be required.
*/
+#if 0
-bool
-vty_shell (struct vty *vty)
+static bool
+vty_is_terminal(struct vty *vty)
{
bool result;
VTY_LOCK() ;
- result = (vty->vio->type == VTY_SHELL) ;
+ result = uty_is_terminal(vty) ;
VTY_UNLOCK() ;
return result;
}
-bool
-vty_term(struct vty *vty)
+static bool
+vty_is_shell_server (struct vty *vty)
{
bool result;
VTY_LOCK() ;
- result = (vty->vio->type == VTY_TERM);
+ result = uty_is_shell_server(vty) ;
VTY_UNLOCK() ;
return result;
}
-bool
-vty_shell_serv (struct vty *vty)
+static bool
+vty_is_shell_client (struct vty *vty)
{
bool result;
VTY_LOCK() ;
- result = (vty->vio->type == VTY_SHELL_SERV);
- VTY_UNLOCK() ;
- return result;
-}
-
-enum node_type
-vty_get_node(struct vty *vty)
-{
- int result;
- VTY_LOCK() ;
- result = vty->node;
+ result = uty_is_shell_client(vty) ;
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 126218ab..2a13ba6e 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
@@ -24,7 +24,11 @@
#ifndef _ZEBRA_VTY_H
#define _ZEBRA_VTY_H
-#include <stdbool.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,141 +39,30 @@
#include "list_util.h"
#include "vector.h"
#include "qstring.h"
-#include "node_type.h"
-
-/* Macro in case there are particular compiler issues. */
-#ifndef Inline
- #define Inline static inline
-#endif
+#include "qpath.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.
+ * These are definitions and functions for things which are required outside
+ * the vty and command family.
*/
-#ifdef VTYSH
-# define VTYSH_DEFINED 1
-#else
-# define VTYSH_DEFINED 0
-#endif
-
-enum { VTYSH_ENABLED = VTYSH_DEFINED } ;
-
-#undef VTYSH_DEFINED
-
-/*==============================================================================
- * VTY Types
- */
-enum vty_type
-{
- VTY_NONE = 0, /* no type at all */
-
- VTY_TERM, /* a telnet terminal -- input and output */
- VTY_SHELL_SERV, /* a vty_shell slave -- input and output */
-
- VTY_CONFIG_READ, /* reading config file -- output is to buffer
- -- no input */
-
- VTY_CONFIG_WRITE, /* writing config file -- output is to file
- -- no input */
-
- VTY_STDOUT, /* general output -- output is to stdout
- -- no input */
-
- VTY_STDERR, /* general output -- output is to stderr
- -- no input */
-
- VTY_SHELL, /* vty in vtysh -- output is to stdout */
-} ;
-
-/*==============================================================================
- * VTY struct.
- */
-
-typedef struct vty_io* vty_io ; /* private to vty.c */
-
-struct cmd_parsed ; /* in case vty.h expanded before command.h */
-
-struct vty
-{
- /*----------------------------------------------------------------------
- * 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 ;
-
- /* String which is newline... read only -- no locking */
- const char* newline ;
-
- /*----------------------------------------------------------------------------
- * 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 ;
-
- /*----------------------------------------------------------------------
- * The following are used inside vty.c only.
- */
-
- /* Pointer to related vty_io structure */
- vty_io vio ;
-};
-
-/*------------------------------------------------------------------------------
- * Can now include this
- */
-
-#include "command.h"
/*------------------------------------------------------------------------------
*
*/
-#define VTY_BUFSIZ 512
-#define VTY_MAXHIST 51
+enum { VTY_HIST_COUNT = 55 } ;
/* Integrated configuration file. */
#define INTEGRATE_DEFAULT_CONFIG "Quagga.conf"
-/* Small macro to determine newline is newline only or linefeed needed. */
-#define VTY_NEWLINE (((vty) != NULL) ? (vty)->newline : "\n")
+/* Conversion of "\n" to "\r\n" is done at output time. */
+#define VTY_NEWLINE "\n"
+#define VTY_NL "\n"
/* For indenting, mostly. */
extern const char vty_spaces_string[] ;
enum { VTY_MAX_SPACES = 40 } ;
#define VTY_SPACES(n) (vty_spaces_string + ((n) < VTY_MAX_SPACES \
? VTY_MAX_SPACES - (n) : 0))
-
-/* Default time out value */
-#define VTY_TIMEOUT_DEFAULT 600
-
/* Vty read buffer size. */
#define VTY_READ_BUFSIZ 512
@@ -182,18 +75,11 @@ 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 { \
char *endptr = NULL; \
- (V) = strtoul ((STR), &endptr, 10); \
+ (V) = strtoul ((STR), &endptr, 0); \
if (*endptr != '\0' || (V) == ULONG_MAX) \
{ \
vty_out (vty, "%% Invalid %s value%s", NAME, VTY_NEWLINE); \
@@ -217,7 +103,7 @@ do { \
VTY_GET_INTEGER_RANGE(NAME,V,STR,0U,UINT32_MAX)
#define VTY_GET_IPV4_ADDRESS(NAME,V,STR) \
-do { \
+do { \
int retv; \
retv = inet_aton ((STR), &(V)); \
if (!retv) \
@@ -228,7 +114,7 @@ do {
} while (0)
#define VTY_GET_IPV4_PREFIX(NAME,V,STR) \
-do { \
+do { \
int retv; \
retv = str2prefix_ipv4 ((STR), &(V)); \
if (retv <= 0) \
@@ -252,33 +138,32 @@ extern void vty_start(const char *addr, unsigned short port, const char *path) ;
#define vty_serv_sock(addr, port, path) vty_start(addr, port, path)
extern void vty_restart(const char *addr, unsigned short port,
const char *path) ;
-extern struct vty* vty_open(enum vty_type type) ;
-extern void vty_close(struct vty *);
-
-extern void vty_init_vtysh (void);
extern void vty_terminate (void);
extern void vty_reset (void);
extern void vty_reset_because(const char* why) ;
extern int vty_out (struct vty *, const char *, ...) PRINTF_ATTRIBUTE(2, 3);
-extern void vty_out_indent(struct vty *vty, int indent) ;
+extern int vty_write(struct vty *vty, const void* buf, int n) ;
+extern int vty_out_indent(struct vty *vty, int indent) ;
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_sigchld(void) ;
-extern void vty_time_print (struct vty *, int);
+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 char *vty_get_cwd (void);
+extern qpath vty_getcwd(qpath qp);
-extern bool vty_shell (struct vty *);
-extern bool vty_term (struct vty *);
-extern bool vty_shell_serv (struct vty *);
-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);
+/*------------------------------------------------------------------------------
+ * vtysh
+ */
+extern void vty_hello (vty vty);
+extern struct vty* vty_open(enum vty_type type, node_type_t node) ;
+extern void vty_close_final(vty vty);
+extern void vty_init_vtysh (void);
#endif /* _ZEBRA_VTY_H */
diff --git a/lib/vty_cli.c b/lib/vty_cli.c
index b4791365..311d950c 100644
--- a/lib/vty_cli.c
+++ b/lib/vty_cli.c
@@ -20,103 +20,365 @@
* 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 "keystroke.h"
-#include "command.h"
+#include "command_common.h"
+#include "command_parse.h"
#include "command_execute.h"
#include "command_queue.h"
#include "memory.h"
+/*------------------------------------------------------------------------------
+ * Essential stuff.
+ */
+#define TELNET_NEWLINE "\r\n"
+static const char* telnet_newline = TELNET_NEWLINE ;
+ const char* uty_cli_newline = TELNET_NEWLINE ;
+
/*==============================================================================
- * Host name handling
+ * Construct and destroy CLI object
+ *
*
- * 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.
*
- * Static variables -- under the VTY_LOCK !
*/
+static bool uty_cli_iac_callback(keystroke_iac_callback_args) ;
+static void uty_cli_update_more(vty_cli cli) ;
+
+static void uty_cli_cancel(vty_cli cli) ;
+
+/*------------------------------------------------------------------------------
+ * Construct and initialise a new CLI object -- never embedded.
+ *
+ * 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. When the command loop is entered,
+ * will find its way to uty_cli_want_command().
+ *
+ * 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 = NULL -- set up when first used.
+ * hp = 0 -- hp == h_now => in the present ...
+ * h_now = 0 -- ... see uty_cli_hist_add()
+ * h_repeat = false -- ... 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
+ *
+ * tilde_prompt = false -- tilde prompt is not drawn
+ * tilde_enabled = false -- set below if ! multi-threaded
+ *
+ * prompt_len = 0 -- not drawn, in any case
+ * extra_len = 0 -- not drawn, in any case
+ *
+ * prompt_node = NULL_NODE -- so not set !
+ * prompt_gen = 0 -- not a generation number
+ * prompt_for_node = NULL -- not set, yet
+ *
+ * dispatched = false -- see below
+ * in_progress = false -- see below
+ * blocked = false -- see below
+ * paused = false
+ *
+ * out_active = false
+ * flush = false
+ *
+ * more_wait = false
+ * more_enter = false
+ *
+ * more_enabled = false -- not in "--More--" state
+ *
+ * pause_timer = NULL -- set below if multi-threaded
+ *
+ * context = NULL -- see below
+ * context_auth = false -- set by uty_cli_want_command()
+ *
+ * parsed = NULL -- see below
+ * to_do = cmd_do_nothing
+ * cl = NULL qstring -- set below
+ * clo = NULL qstring -- set below
+ * clx = NULL qstring -- set below
+ * dispatch = all zeros -- nothing to dispatch
+ *
+ * cbuf = NULL -- see below
+ *
+ * olc = NULL -- see below
+ */
+ confirm(NULL_NODE == 0) ; /* prompt_node & node */
+ confirm(cmd_do_nothing == 0) ; /* to_do */
+ confirm(CMD_ACTION_ALL_ZEROS) ; /* dispatch */
+
+ cli->vf = vf ;
+
+ /* Allocate and initialise a keystroke stream TODO: CSI ?? */
+ cli->key_stream = keystroke_stream_new('\0', uty_cli_iac_callback, cli) ;
+
+ /* Set up cl and clx qstrings and the command line output fifo */
+ cli->cl = qs_new(120) ; /* reasonable line length */
+ cli->cls = qs_new(120) ;
+ cli->clx = qs_new(120) ;
+
+ cli->cbuf = vio_fifo_new(1000) ;
+
+ cli->olc = vio_lc_new(0 , 0, telnet_newline) ;
+
+ /* Make an empty context object and an empty parsed object */
+ cli->context = cmd_context_new() ;
+ cli->parsed = cmd_parsed_new() ;
+
+ /* 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. */
-static char* vty_host_name = NULL ;
-int vty_host_name_set = 0 ;
+ /* Enable "~ " prompt and pause timer if multi-threaded */
+ if (vty_nexus)
+ {
+ cli->pause_timer = qtimer_init_new(NULL, vty_cli_nexus->pile,
+ vty_term_pause_timeout, cli) ;
+ cli->tilde_enabled = vty_multi_nexus ;
+ } ;
+
+ /* Ready to be started -- paused, out_active, flush & more_wait are false.
+ *
+ * Is started by the first call of uty_cli_want_command(), which (inter alia)
+ * set the cli->context so CLI knows how to prompt etc.
+ */
+ cli->dispatched = true ;
+ cli->in_progress = true ;
+ cli->blocked = true ;
-static void uty_new_host_name(const char* name) ;
+ return cli ;
+} ;
/*------------------------------------------------------------------------------
- * Update vty_host_name as per "hostname" or "no hostname" command
+ * 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 dispatched and not final, keeps the cbuf, the olc and clx if dispatched.
+ * 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 void
-uty_set_host_name(const char* name)
+extern vty_cli
+uty_cli_close(vty_cli cli, bool final)
{
- VTY_ASSERT_LOCKED() ;
+ if (cli == NULL)
+ return NULL ;
- vty_host_name_set = (name != NULL) ;
+ VTY_ASSERT_CLI_THREAD_LOCKED() ;
- 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_cancel(cli) ; /* " ^C\r\n" unless believed blank */
+
+ cli->more_wait = false ; /* exit more_wait (if was in it) */
+ cli->more_enter = false ; /* and so cannot be this */
+
+ cli->blocked = true ; /* do not renter the CLI */
+ cli->out_active = true ; /* if there is any output, it can go */
+ cli->flush = true ; /* all of it can go */
+
+ /* Ream out the history. */
+ {
+ qstring line ;
+ while ((line = vector_ream(cli->hist, free_it)) != NULL)
+ qs_reset(line, free_it) ;
+ cli->hist = NULL ;
+ } ;
+
+ /* Empty the keystroke handling. */
+ cli->key_stream = keystroke_stream_free(cli->key_stream) ;
+
+ /* Can discard active command line if not dispatched TODO ?? */
+ if (!cli->dispatched || final)
+ cli->clx = qs_reset(cli->clx, free_it) ;
+
+ /* Can discard context and parsed objects */
+ cli->context = cmd_context_free(cli->context, true) ; /* its a copy */
+ cli->parsed = cmd_parsed_free(cli->parsed) ;
+
+ /* Discard any pause_timer, and suppress */
+ cli->pause_timer = qtimer_free(cli->pause_timer) ;
+ cli->paused = false ;
+ cli->tilde_enabled = false ;
+
+ /* If final, free the CLI object. */
+ if (final)
+ {
+ cli->prompt_for_node = qs_free(cli->prompt_for_node) ;
+ cli->cl = qs_free(cli->cl) ;
+ cli->cls = qs_free(cli->cls) ;
+
+ cli->cbuf = vio_fifo_free(cli->cbuf) ;
+ cli->olc = vio_lc_free(cli->olc) ;
+
+ 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 ;
+ cli->lines = lines ;
+ cli->lines_set = explicit ;
- VTY_ASSERT_LOCKED() ;
-
- 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 ;
+ bool on ;
- VTY_ASSERT_LOCKED() ;
-
- 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 ;
} ;
/*==============================================================================
@@ -131,32 +393,68 @@ 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 inside the CLI code. [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.
+ * dispatched -- a command line has been dispatched, and is waiting for
+ * (dsp) command loop to fetch it.
+ *
+ * in_progress -- a command has been taken by the command loop, and is still
+ * (inp) running -- or at least no further command has been
+ * fetched by the command loop.
+ *
+ * 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
+ * (bkd) dispatched.
+ *
+ * or: the CLI has been closed.
+ *
+ * out_active -- the command output FIFO is being emptied.
+ * (oa)
+ * This is set when output is pushed, and cleared when
+ * everything is written away and flush is set. When it
+ * is set, any current command line is wiped.
*
- * cmd_in_progress -- a command has been dispatched and has not yet
- * completed (may have been queued).
+ * Note that what this flag does is prevent the CLI from
+ * running until the output completes, and in particular
+ * prevents it from writing anything to the CLI buffer.
*
- * cmd_out_enabled -- the command FIFO is may be emptied.
+ * flush -- is set when the CLI is ready for the next command (so
+ * (fsh) when in_progress is cleared) to cause any incomplete
+ * command output to be flushed, and to signal that
+ * out_active should be cleared when all output is complete.
*
- * This is set when a command completes, and cleared when
- * everything is written away.
+ * more_wait -- is in "--more--" wait state. => out_active !
+ * (mwt)
+ * The "--more--" CLI uses the CLI output buffer to draw
+ * and undraw the "--more--" prompt. The buffer will
+ * otherwise be empty because is out_active.
*
- * cli_more_wait -- is in "--more--" wait state
+ * more_enter -- is in the process of entering the "--more--" wait state,
+ * (men) waiting to write the "--more--" prompt and prepare the
+ * keystroke input.
*
* The following are the valid combinations:
*
- * blkd : cip : o_en : m_wt :
- * -----:------:------:------:--------------------------------------------
- * 0 : 0 : 0 : 0 : collecting a new command
- * 0 : 1 : 0 : 0 : command dispatched
- * 1 : 1 : 0 : 0 : waiting for (queued) command to complete
- * 1 : 0 : 1 : 0 : waiting for command output to complete
- * 1 : 0 : 0 : 1 : waiting for "--more--" to be written away
- * 0 : 0 : 0 : 1 : waiting for "--more--" response
- * 1 : 1 : 1 : 0 : waiting for command to complete, after the
- * CLI has been closed
+ * dsp:inp:bkd: oa:fsh:mwt:men:
+ * ---:---:---:---:---:---:---:-----------------------------------------
+ * 0 : 0 : 0 : 0 : 0 : 0 : 0 : collecting a new command
+ * 1 : 0 : 0 : 0 : 0 : 0 : 0 : waiting for command to be fetched
+ * 1 : 1 : 0 : X : 0 : 0 : 0 : command fetched and running
+ * 1 : 1 : 1 : X : 0 : 0 : 0 : waiting for command to complete
+ * 0 : 0 : 0 : 1 : 1 : 0 : 0 : waiting for command output to finish
+ * 1 : 1 : X : 1 : X : 1 : 1 : waiting for "--more--" to start
+ * 1 : 1 : X : 1 : X : 1 : 0 : waiting for "--more--" response
+ * 1 : 1 : 1 : 1 : 1 : 0 : 0 : waiting for command to complete,
+ * after the CLI has been closed
*
* There are two output FIFOs:
*
@@ -165,9 +463,19 @@ 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, or if it pushes the output
+ * explicitly. Note that where the CLI level command is a pipe-in, the first
+ * output will set out_active, and that will persist until returns to the CLI
+ * level. Between commands in the pipe, the output will be pushed, so will
+ * output stuff more or less as it is generated.
+ *
+ * It is expected that each command's output will end with a newline. However,
+ * the line control imposes some discipline, and holds on to incomplete lines
+ * until a newline arrives, or the output if flushed. -- so that when the
+ * CLI is kicked, the cursor will be at the start of an empty line.
+ *
*
* 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
@@ -177,44 +485,20 @@ uty_new_host_name(const char* name)
* re-entered again on write_ready/read_ready -- so does one command line at
* a time, yielding the processor after each one.
*
- * Note that select/pselect treat a socket which is at "EOF", or has seen an
- * error, or has been half closed, etc. as readable and writable. This means
- * that the CLI will continue to move forward even after the socket is no
- * longer delivering any data.
- *
*------------------------------------------------------------------------------
* The "--more--" handling.
*
* This is largely buried in the output handling.
*
- * While cmd_in_progress is true cmd_out_enabled will be false. When the
- * command completes:
- *
- * * cmd_in_progress is cleared
- *
- * * cmd_out_enabled is set
- *
- * * cli_blocked will be set
- *
- * * the line_control structure is reset
- *
- * * the output process is kicked off by setting write on
+ * When command 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.)
- *
- * When all the output has completed the CLI will be kicked, which will see
- * that the output buffer is now empty, and it can proceed.
- *
- * It is expected that the output will end with a newline -- so that when the
- * CLI is kicked, the cursor will be at the start of an empty line.
- *
- * This mechanism means that the command output FIFO only ever contains the
- * output from the last command executed.
+ * main CLI. (See the more_wait and more_enter flags and their handling.)
*
* If the user decides to abandon output at the "--more--" prompt, then the
- * contents of the command output FIFO are discarded.
+ * then contents of the command output FIFO are discarded.
*
*------------------------------------------------------------------------------
* The qpthreads/qpnexus extension.
@@ -262,104 +546,17 @@ uty_new_host_name(const char* name)
* The CLI
*/
-#define CONTROL(X) ((X) - '@')
-
-static void uty_cli_cmd_complete(vty_io vio, enum cmd_return_code ret) ;
-static enum vty_readiness uty_cli_standard(vty_io vio) ;
-static enum vty_readiness uty_cli_more_wait(vty_io vio) ;
-static void uty_cli_draw(vty_io vio) ;
-static void uty_cli_draw_this(vty_io vio, enum node_type node) ;
-static void uty_cli_wipe(vty_io vio, int len) ;
-
-static void uty_will_echo (vty_io vio) ;
-static void uty_will_suppress_go_ahead (vty_io vio) ;
-static void uty_dont_linemode (vty_io vio) ;
-static void uty_do_window_size (vty_io vio) ;
-static void uty_dont_lflow_ahead (vty_io vio) ;
-
-/*------------------------------------------------------------------------------
- * Initialise CLI.
- *
- * It is assumed that the following have been initialised, empty or zero:
- *
- * cli_prompt_for_node
- * cl
- * clx
- * cli_vbuf
- * cli_obuf
- *
- * cli_drawn
- * cli_dirty
- *
- * cli_prompt_set
- *
- * cli_blocked
- * cmd_in_progress
- * cmd_out_enabled
- * cli_wait_more
- *
- * cli_more_enabled
- *
- * Sets the CLI such that there is apparently a command in progress, so that
- * further initialisation (in particular hello messages and the like) is
- * treated as a "start up command".
- *
- * Sends a suitable set of Telnet commands to start the process.
- */
-extern void
-uty_cli_init(vty_io vio)
-{
- assert(vio->type == VTY_TERM) ;
-
- vio->cmd_in_progress = 1 ;
- vio->cli_blocked = 1 ;
-
- vio->cli_do = cli_do_nothing ;
-
- /* Setting up terminal. */
- uty_will_echo (vio);
- uty_will_suppress_go_ahead (vio);
- uty_dont_linemode (vio);
- uty_do_window_size (vio);
- if (0)
- uty_dont_lflow_ahead (vio) ;
-} ;
+#define CONTROL(X) ((X) & 0x1F)
-/*------------------------------------------------------------------------------
- * 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 cmd_do_t uty_cli_auth(vty_cli cli) ;
+static enum vty_readiness uty_cli_more_wait(vty_cli cli) ;
+static void uty_cli_draw(vty_cli cli) ;
/*------------------------------------------------------------------------------
- * 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->type == VTY_TERM) ;
-
- cq_revoke(vio->vty) ;
-
- vio->cli_blocked = 1 ; /* don't attempt any more */
- vio->cmd_out_enabled = 1 ; /* allow output to clear */
-} ;
-
-/*------------------------------------------------------------------------------
- * CLI for VTY_TERM
+ * Called by uty_term_ready(), so driven by read/write ready.
*
* Do nothing at all if half closed.
*
@@ -369,29 +566,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->type == VTY_TERM) ;
- if (vio->half_closed)
- return not_ready ; /* Nothing more if half closed */
+ if (cli->vf->vin_state == vf_closed)
+ return not_ready ; /* Nothing more from CLI if closed */
/* 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) ;
+static void uty_cli_response(vty_cli cli, cmd_do_t cmd_do) ;
+
+static bool 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) ;
+
+static void uty_cli_pause_start(vty_cli cli) ;
/*------------------------------------------------------------------------------
* Standard CLI for VTY_TERM -- if not blocked, runs until:
@@ -407,283 +608,302 @@ 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)
{
+ bool need_prompt ;
+
VTY_ASSERT_LOCKED() ;
- assert(vio->type == VTY_TERM) ;
- /* cli_blocked is set when is waiting for a command, or its output to
- * complete -- unless either of those has happened, is still blocked.
+ 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 (cli->blocked || cli->out_active || cli->paused || cli->mon_active)
+ return not_ready ;
- if (vio->cmd_in_progress)
- {
- assert(!vio->cmd_out_enabled) ;
- return not_ready ;
- } ;
-
- if (!vio_fifo_empty(&vio->cmd_obuf))
- return not_ready ;
-
- vio->cli_blocked = 0 ;
- vio->cmd_out_enabled = 0 ;
- } ;
-
- /* If there is nothing pending, then can run the CLI until there is
- * something to do, or runs out of input.
+ /* Make sure that the command line is drawn.
*
- * If there is something to do, that is because a previous command has
- * now completed, which may have wiped the pending command or changed
- * the required prompt.
+ * If there is nothing pending, then can run the CLI until there is
+ * something to do, or runs out of input.
*/
- if (vio->cli_do == cli_do_nothing)
- vio->cli_do = uty_cli_process(vio, vio->vty->node) ;
- else
- uty_cli_draw_this(vio, vio->vty->node) ;
+ if (!cli->drawn)
+ uty_cli_draw(cli) ;
+
+ if (cli->to_do == cmd_do_nothing)
+ cli->to_do = cli->auth_context ? uty_cli_auth(cli)
+ : uty_cli_process(cli) ;
- /* If have something to do, do it. */
- if (vio->cli_do != cli_do_nothing)
+ /* If have something to do, do it if we can. */
+ need_prompt = false ;
+
+ 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->dispatched)
+ cli->blocked = true ; /* waiting for previous */
else
- vio->cli_blocked = 1 ;
+ need_prompt = uty_cli_dispatch(cli) ; /* dispatch latest */
} ;
- /* Use write_ready as a proxy for read_ready on the keystroke stream.
+ /* If blocked, must wait for some other event to continue in CLI.
+ *
+ * Note that will be blocked if have just dispatched a command, and is
+ * "tilde_enabled" -- which will be true if single threaded, and may be
+ * set for other reasons.
*
- * Also, if the command line is not drawn, then return write_ready, so
- * that
+ * If the keystroke stream is not empty, use write_ready as a proxy for
+ * CLI ready -- no point doing anything until any buffered.
*
- * Note that if has just gone cli_blocked, still returns ready. This is
- * defensive: at worst will generate one unnecessary read_ready/write_ready
- * event.
+ * If command prompt has been redrawn, need to kick writer to deal with
+ * that -- will reenter to then process any keystrokes.
*/
- if (keystroke_stream_empty(vio->key_stream))
- return read_ready ;
- else
+ assert(!cli->paused) ;
+
+ if (cli->blocked)
+ return not_ready ;
+
+ if (!keystroke_stream_empty(cli->key_stream) || need_prompt)
return write_ready ;
+
+ /* The keystroke stream is empty, but CLI is not blocked.
+ *
+ * If a command is dispatched, and output is not active, and the command
+ * line is not drawn, then go to paused state, so that will delay output
+ * of the prompt slightly (or until a keystroke arrives, or the current
+ * command completes, or something else happens).
+ *
+ * Note that if a command has been dispatched, and is !tilde_enabled, then
+ * will now be blocked.
+ */
+ if (cli->dispatched && !cli->out_active && !cli->drawn)
+ uty_cli_pause_start(cli) ;
+
+ return read_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 cli->cl_do = and cli->cl to be a follow-on command.
*
- * Can set vio->cl_do = and vio->cl to be a follow-on command.
+ * Returns: true <=> nothing, in fact, to do: prompt has been redrawn.
*/
static bool
-uty_cli_dispatch(vty_io vio)
+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->dispatched && !cli->in_progress
+ && !cli->blocked && !cli->out_active) ;
+ qassert(cli->context->node == vio->vty->exec->context->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->auth_context)
+ to_do_now |= cmd_do_auth ;
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:
+ case cmd_do_timed_out:
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 */
+ cmd_action_set(cli->dispatch, to_do_now, cli->clx) ;
+ cli->dispatched = true ;
+
+ uty_cmd_signal(vio, CMD_SUCCESS) ;
+
+ cli->blocked = (to_do_now != cmd_do_command) || !cli->tilde_enabled ;
}
else
{
- uty_cli_cmd_complete(vio, ret) ;
- return true ; /* command complete */
+ cmd_action_clear(cli->dispatch) ;
+ cli->dispatched = false ;
+
+ uty_cli_draw(cli) ;
} ;
+
+ return !cli->dispatched ;
} ;
/*------------------------------------------------------------------------------
- * Queued command has completed.
+ * Check if command is empty, if not add to history
*
- * Note that sets write on whether there is anything in the output buffer
- * or not... write_ready will kick read_ready.
+ * Returns: cmd_do_nothing -- empty command line
+ * cmd_do_command -- command ready to be executed (added to history)
*/
-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 ;
+ if (cmd_is_empty(cli->clx))
+ return cmd_do_nothing ; /* easy when nothing to do ! */
- VTY_LOCK() ;
+ /* Add not empty command to history */
+ uty_cli_hist_add(cli, cli->clx) ;
- vio = vty->vio ;
+ return cmd_do_command ;
+} ;
- if (!vio->closed)
- {
- uty_cli_wipe(vio, 0) ; /* wipe any partly constructed line */
+/*------------------------------------------------------------------------------
+ * Want another command line from the CLI.
+ *
+ * If in_progress this <=> any previous command has completed, and output may
+ * be active writing the results away. Will:
+ *
+ * * clear cli->dispatched
+ * * clear cli->in_progress
+ * * clear cli->blocked
+ *
+ * May be in more_wait state -- so avoids touching that.
+ *
+ * If not in_progress, then if dispatched, that is a new command ready to pass
+ * to the command loop -- which we do here, and set cli->in_progress.
+ */
+extern cmd_return_code_t
+uty_cli_want_command(vty_cli cli, cmd_action action, cmd_context context)
+{
+ VTY_ASSERT_LOCKED() ;
- /* Do the command completion actions that were deferred because the
- * command was queued.
+ if (cli->in_progress)
+ {
+ /* Previous command has completed
*
- * Return of CMD_QUEUED => command was revoked before being executed.
- * However interesting that might be... frankly don't care.
+ * Make sure state reflects the fact that we are now waiting for a
+ * command.
*/
- uty_cli_cmd_complete(vio, ret) ;
+ cli->dispatched = false ; /* no longer have a dispatched command */
+ cli->in_progress = false ; /* command complete */
+ cli->blocked = false ; /* releases possibly blocked command */
+ cli->paused = false ; /* override paused state */
+
+ *cli->context = *context ; /* make sure is up to date */
+ cli->auth_context = ( (cli->context->node == AUTH_NODE)
+ || (cli->context->node == AUTH_ENABLE_NODE) ) ;
- /* Kick the socket -- to write away any outstanding output, and
- * re-enter the CLI when that's done.
+ /* If the output is owned by command output, then set flush flag, so
+ * that when buffers empty, the output will be released.
+ *
+ * If the output side is not owned by command output, wipe any temporary
+ * prompt.
+ *
+ * In any case, kick write_ready to ensure output clears and prompt is
+ * written and so on.
*/
- uty_sock_set_readiness(&vio->sock, write_ready) ;
+ if (cli->out_active)
+ cli->flush = true ;
+ else
+ uty_cli_draw(cli) ;
+
+ uty_term_set_readiness(cli->vf, write_ready) ;
}
- else
+ else if (cli->dispatched)
{
- /* If the VTY is closed, the only reason it still exists is because
- * there was cmd_in_progress.
+ /* New command has been dispatched -- can now pass that to the
+ * command loop -- setting it in_progress.
*/
- vio->cmd_in_progress = 0 ;
+ assert(cli->dispatch->to_do != cmd_do_nothing) ;
+ cmd_action_take(action, cli->dispatch) ;
- uty_close(vio) ; /* Final close */
+ cli->in_progress = true ;
} ;
- VTY_UNLOCK() ;
-}
+ return cli->in_progress ? CMD_SUCCESS : CMD_WAITING ;
+} ;
/*------------------------------------------------------------------------------
- * Command has completed, so:
- *
- * * clear cmd_in_progress
- * * set cmd_out_enabled -- so any output can now proceed
- * * set cli_blocked -- waiting for output to complete
- * * and prepare the line control for output
- *
- * If the return is CMD_CLOSE, then also now does the required half close.
- *
- * Note that apart from CMD_CLOSE, don't really care what the return was. Any
- * diagnostics or other action must be dealt with elsewhere (as part of the
- * command execution.
- *
- * Note that everything proceeds as if there is some output. So after every
- * command goes through at least one write_ready event.
- *
- * This ensures some multiplexing at the command level.
- *
- * It also means that the decision about whether there is anything to output
- * is left to the output code.
+ * Start pause timer and set paused.
*/
static void
-uty_cli_cmd_complete(vty_io vio, enum cmd_return_code ret)
+uty_cli_pause_start(vty_cli cli)
{
- VTY_ASSERT_LOCKED() ;
-
- assert(vio->cmd_in_progress && !vio->cmd_out_enabled) ;
-
- if (ret == CMD_CLOSE)
- uty_half_close(vio, NULL) ;
-
- vio->cmd_in_progress = 0 ; /* command complete */
- vio->cmd_out_enabled = 1 ; /* enable the output */
- vio->cli_blocked = 1 ; /* now blocked waiting for output */
-
- vio->vty->buf = NULL ; /* finished with command line */
-
- uty_cmd_output_start(vio) ; /* reset line control (belt & braces) */
+ qtimer_set(cli->pause_timer, qt_add_monotonic(QTIME(0.2)), NULL) ;
+ cli->paused = true ;
} ;
/*==============================================================================
* 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 and more_enter flags.
*
* The first stage of handling "--more--" is to suck the input dry, so that
* (as far as is reasonably possible) does not steal a keystroke as the
* "--more--" response which was typed before the prompt was issued.
*
- * The cli_blocked flag indicates that the CLI is in this first stage.
+ * The more_enter flag indicates that the CLI is in this first stage.
*/
/*------------------------------------------------------------------------------
@@ -692,53 +912,14 @@ 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)
-{
- VTY_ASSERT_LOCKED() ;
-
- assert(vio->cli_blocked && vio->cmd_out_enabled && !vio->cli_more_wait) ;
-
- uty_cli_wipe(vio, 0) ; /* make absolutely sure that command line is
- wiped before change the CLI state */
-
- vio->cmd_out_enabled = 0 ; /* stop output pro tem */
- vio->cli_more_wait = 1 ; /* new state */
-
- uty_cli_draw(vio) ; /* draw the "--more--" */
-} ;
-
-/*------------------------------------------------------------------------------
- * Exit the "--more--" CLI.
- *
- * Wipes the "--more--" prompt.
- *
- * This is used when the user responds to the prompt.
- *
- * It is also used when the vty is "half-closed". In this case, it is (just)
- * possible that the '--more--' prompt is yet to be completely written away,
- * so:
- *
- * * assert that is either: !vio->cli_blocked (most of the time it will)
- * or: !vio_fifo_empty(&vio->cli_obuf)
- *
- * * note that can wipe the prompt even though it hasn't been fully
- * written away yet. (The effect is to append the wipe action to the
- * cli_obuf !)
- */
-extern void
-uty_cli_exit_more_wait(vty_io vio)
+uty_cli_enter_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->out_active && !cli->more_wait && !cli->drawn) ;
- uty_cli_wipe(vio, 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 = true ; /* new state */
+ cli->more_enter = true ; /* drawing the "--more--" etc. */
} ;
/*------------------------------------------------------------------------------
@@ -751,87 +932,184 @@ uty_cli_exit_more_wait(vty_io vio)
*
* EOF on input causes immediate exit from cli_more_state.
*
- * Returns: read_ready -- waiting to steal a keystroke
- * now_ready -- just left cli_more_wait
- * not_ready -- otherwise
+ * Returns: read_ready -- waiting to steal a keystroke
+ * write_ready -- waiting to draw or undraw the "--more--" prompt
+ * 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 ;
+ keystroke_t steal ;
+ bool cancel ;
VTY_ASSERT_LOCKED() ;
+ assert(cli->more_wait) ; /* must be in more_wait state ! */
+
+ if (cli->paused || cli->mon_active)
+ return not_ready ;
+
/* Deal with the first stage of "--more--" */
- if (vio->cli_blocked)
+ if (cli->more_enter)
{
int get ;
+ uty_cli_draw_if_required(cli) ; /* draw the "--more--" */
+
/* 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_enter = 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) ;
+
+ return read_ready ;
} ;
- /* Go through the "--more--" process, unless no longer write_open (!) */
- if (vio->sock.write_open)
+ /* Go through the "--more--" process, unless closing */
+ /* The read fetches a reasonable lump from the I/O -- so if there
+ * is a complete keystroke available, expect to get it.
+ *
+ * If no complete keystroke available to steal, returns ks_null.
+ *
+ * If has hit EOF or timeout (or error etc), returns knull_eof.
+ */
+ uty_term_read(cli->vf, steal) ;
+
+ /* If nothing stolen, make sure prompt is drawn and wait for more
+ * input.
+ *
+ * If anything at all has been stolen, then continue or cancel.
+ */
+ cancel = false ;
+ switch(steal->type)
{
- /* The read fetches a reasonable lump from the I/O -- so if there
- * is a complete keystroke available, expect to get it.
- *
- * If no complete keystroke available to steal, returns ks_null.
- *
- * If has hit EOF (or error etc), returns knull_eof.
- */
- uty_read(vio, &steal) ;
+ case ks_null:
+ switch(steal->value)
+ {
+ case knull_not_eof:
+ // TODO need to refresh "--more--" in case of monitor ??
+ return read_ready; /* <<< exit: no keystroke */
+ break ;
- /* If nothing stolen, make sure prompt is drawn and wait for more
- * input.
- */
- if ((steal.type == ks_null) && (steal.value != knull_eof))
- {
- if (uty_cli_draw_if_required(vio)) /* "--more--" if req. */
- return write_ready ;
- else
- return read_ready ;
- } ;
+ case knull_eof:
+ case knull_timed_out:
+ cancel = true ;
+ break ;
- /* Stolen a keystroke -- a (very) few terminate all output */
- if (steal.type == ks_char)
- {
- switch (steal.value)
+ default:
+ break ;
+ } ;
+ break ;
+
+ case ks_char:
+ switch (steal->value)
{
case CONTROL('C'):
case 'q':
case 'Q':
- uty_out_clear(vio) ;
+ cancel = true ;
break;
- default: /* everything else, thrown away */
+ default:
break ;
} ;
- } ;
+ break ;
+
+ default:
+ break ;
} ;
/* End of "--more--" process
*
- * Wipe out the prompt and update state.
+ * Wipe out the prompt (unless "cancel") and update state.
*
* Return write_ready to tidy up the screen and, unless cleared, write
* some more.
*/
- uty_cli_exit_more_wait(vio) ;
+ if (cancel)
+ {
+ uty_out_clear(cli->vf->vio) ;
+ vio_lc_clear(cli->olc) ; /* clear & reset counter */
+ uty_cli_cancel(cli) ;
+ }
+ else
+ {
+ vio_lc_counter_reset(cli->olc) ;
+ uty_cli_wipe(cli, 0) ;
+ } ;
+
+ cli->more_wait = false ; /* exit more_wait */
+ cli->more_enter = false ; /* tidy */
+
+ return write_ready ;
+} ;
+
+/*==============================================================================
+ * Monitor output.
+ *
+ * To prepare for monitor output, wipe as much as is necessary for the
+ * monitor line to appear correctly.
+ *
+ * After monitor output, may need to do two things:
+ *
+ * * if the output was incomplete, place the rump in the CLI buffer,
+ * so that:
+ *
+ * a. don't mess up the console with partial lines
+ *
+ * b. don't lose part of a message
+ *
+ * c. act as a brake on further monitor output -- cannot do any more
+ * until the last, part, line is dealt with.
+ *
+ * * restore the command line, unless it is empty !
+ */
+
+ /*-----------------------------------------------------------------------------
+ * Prepare for new monitor output line.
+ *
+ * Wipe any existing command line.
+ */
+extern void
+uty_cli_pre_monitor(vty_cli cli)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ uty_cli_wipe(cli, 0) ;
+
+ cli->mon_active = true ; /* block cli & enable empty of fifo */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Recover from monitor line output.
+ *
+ * If monitor line failed to complete, append the rump to the CLI buffer.
+ *
+ * If have a non-empty command line, or is cli_more_wait, redraw the command
+ * line.
+ *
+ * Returns: 0 => rump was empty and no command line stuff written
+ * > 0 => rump not empty or some command line stuff written
+ */
+extern void
+uty_cli_post_monitor(vty_cli cli)
+{
+ VTY_ASSERT_LOCKED() ;
- return now_ready ;
+ uty_cli_pause_start(cli) ; /* do not draw prompt immediately */
+
+ cli->more_enter = cli->more_wait ;
+ /* revert to more_enter state */
+
+ cli->mon_active = false ; /* unblock cli & turn off output */
} ;
/*==============================================================================
@@ -839,27 +1117,22 @@ 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
+ * keyboard then may need to have intermediate buffering.
*
- * The "cli_echo" functions discard the output if vio->cli_echo_suppress != 0.
- * This is used while passwords are entered and to allow command line changes
- * to be made while the line is not visible.
+ * No actual I/O takes place here-- all "output" is to cli->cbuf
*/
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,
@@ -867,86 +1140,54 @@ 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 = "................................" ;
+static cli_rep telnet_stars = "********************************" ;
-static const char* telnet_newline = "\r\n" ;
+CONFIRM(sizeof(telnet_spaces) == (sizeof(cli_rep_char) + 1)) ;
+CONFIRM(sizeof(telnet_dots) == (sizeof(cli_rep_char) + 1)) ;
-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, ...)
{
- VTY_ASSERT_LOCKED() ;
+ va_list args ;
- if (vio->sock.write_open)
- {
- va_list args ;
-
- va_start (args, format);
- vio_fifo_vprintf(&vio->cli_obuf, format, args) ;
- va_end(args);
- } ;
-} ;
-
-/*------------------------------------------------------------------------------
- * CLI VTY output -- echo user input
- *
- * Do nothing if echo suppressed (eg in AUTH_NODE) or not write_open
- */
-static void
-uty_cli_echo(vty_io vio, const char *this, size_t len)
-{
VTY_ASSERT_LOCKED() ;
- if (vio->cli_echo_suppress || !vio->sock.write_open)
- return ;
-
- uty_cli_write(vio, this, len) ;
-}
+ va_start (args, format);
+ vio_fifo_vprintf(cli->cbuf, format, args) ;
+ va_end(args);
+} ;
/*------------------------------------------------------------------------------
- * CLI VTY output -- echo 'n' characters using a cli_rep_char string
- *
- * Do nothing if echo suppressed (eg in AUTH_NODE)
+ * Completely empty the cli command buffer
*/
-static void
-uty_cli_echo_n(vty_io vio, cli_rep_char chars, int n)
+extern void
+uty_cli_out_clear(vty_cli cli)
{
- VTY_ASSERT_LOCKED() ;
-
- if (vio->cli_echo_suppress || !vio->sock.write_open)
- return ;
-
- uty_cli_write_n(vio, chars, n) ;
-}
+ vio_fifo_clear(cli->cbuf, true) ;
+} ;
/*------------------------------------------------------------------------------
* 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 ;
@@ -955,7 +1196,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 ;
} ;
} ;
@@ -966,32 +1207,35 @@ 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 ;
} ;
/*==============================================================================
- * Standard Messages
+ * Prompts and responses
*/
+static void uty_cli_goto_end_if_drawn(vty_cli cli) ;
/*------------------------------------------------------------------------------
* Send newline to the console.
*
* 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_goto_end_if_drawn(cli) ;
+
+ uty_cli_write(cli, telnet_newline, 2) ;
+ cli->drawn = false ;
+ cli->dirty = false ;
} ;
/*------------------------------------------------------------------------------
@@ -1001,23 +1245,59 @@ 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
+ * If the command line is drawn, then show that it has been cancelled.
+ *
+ * If the command line is dirty, then cancel it and start new line.
+ *
+ * Sets: cli_drawn = false
+ * cli_dirty = false
+ */
+static void
+uty_cli_cancel(vty_cli cli)
+{
+ if (cli->drawn || cli->dirty)
+ {
+ uty_cli_goto_end_if_drawn(cli) ;
+ uty_cli_write_s(cli, " ^C" TELNET_NEWLINE) ;
+ } ;
+
+ cli->drawn = false ;
+ cli->dirty = false ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * If the command line is drawn and the cursor is not at the end of the line,
+ * move the physical cursor to the end of the line.
+ *
+ * Assumes about to issue newline.
+ */
+static void
+uty_cli_goto_end_if_drawn(vty_cli cli)
+{
+ ulen after ;
+
+ if (cli->drawn && ( (after = qs_after_cp_nn(cli->cl)) != 0 ))
+ uty_cli_write(cli, qs_cp_char_nn(cli->cl), after) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * 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.
@@ -1025,113 +1305,106 @@ 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] = "^*",
+ [cmd_do_timed_out] = "^!"
},
{ /* 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] = "^*",
+ [cmd_do_timed_out] = "^!"
}
} ;
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))
- return ;
+ assert((to_do != cmd_do_nothing) && cli->drawn
+ && (qs_cp_nn(cli->cl) == qs_len_nn(cli->cl))) ;
- 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->dispatched ? 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->dispatched)
{
- 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.
+ * Current prompt length
*/
-
-static void
-uty_cli_out_CMD_ERR_AMBIGUOUS(vty_io vio)
-{
- uty_cli_write_s(vio, "% " MSG_CMD_ERR_AMBIGUOUS ".") ;
- uty_cli_out_newline(vio) ;
-} ;
-
-static void
-uty_cli_out_CMD_ERR_NO_MATCH(vty_io vio)
+extern ulen
+uty_cli_prompt_len(vty_cli cli)
{
- uty_cli_write_s(vio, "% " MSG_CMD_ERR_NO_MATCH ".") ;
- uty_cli_out_newline(vio) ;
+ return cli->prompt_len ;
} ;
-/*==============================================================================
- * Command line draw and wipe
- */
-
/*------------------------------------------------------------------------------
* 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->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 ;
} ;
/*------------------------------------------------------------------------------
@@ -1141,28 +1414,17 @@ 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 ;
} ;
/*------------------------------------------------------------------------------
- * Draw prompt etc for the current vty node.
- *
- * See uty_cli_draw_this()
- */
-static void
-uty_cli_draw(vty_io vio)
-{
- uty_cli_draw_this(vio, vio->vty->node) ;
-} ;
-
-/*------------------------------------------------------------------------------
* Draw prompt and entire command line, leaving current position where it
* should be.
*
@@ -1172,50 +1434,49 @@ 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
+ * * if is more_wait, draw the "--more--" prompt
*
- * * if is cmd_in_progress, draw the vestigial prompt.
+ * * if is dispatched, draw the vestigial prompt.
*
* By the time the current command completes, the node may have changed, so
* the current prompt may be invalid.
*
- * Sets: cli_drawn = true
- * cli_dirty = false
- * cli_prompt_len = length of prompt used
- * cli_extra_len = 0
- * cli_echo_suppress = (AUTH_NODE or AUTH_ENABLE_NODE)
+ * Sets: cli->drawn = true
+ * cli->dirty = false
+ * cli->prompt_len = length of prompt used
+ * cli->extra_len = 0
*/
static void
-uty_cli_draw_this(vty_io vio, enum node_type node)
+uty_cli_draw(vty_cli cli)
{
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->dispatched)
{
/* 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
{
@@ -1226,162 +1487,92 @@ 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))
+ node_type_t node = cli->context->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);
+ cli->prompt_for_node =
+ 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 ;
- 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) ;
- } ;
-} ;
-
-/*==============================================================================
- * Monitor output.
- *
- * To prepare for monitor output, wipe as much as is necessary for the
- * monitor line to appear correctly.
- *
- * After monitor output, may need to do two things:
- *
- * * if the output was incomplete, place the rump in the CLI buffer,
- * so that:
- *
- * a. don't mess up the console with partial lines
- *
- * b. don't lose part of a message
- *
- * c. act as a brake on further monitor output -- cannot do any more
- * until the last, part, line is dealt with.
- *
- * * restore the command line, unless it is empty !
- */
-
- /*-----------------------------------------------------------------------------
- * Prepare for new monitor output line.
- *
- * Wipe any existing command line.
- */
-extern void
-uty_cli_pre_monitor(vty_io vio, size_t len)
-{
- VTY_ASSERT_LOCKED() ;
-
- uty_cli_wipe(vio, len) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Recover from monitor line output.
- *
- * If monitor line failed to complete, append the rump to the CLI buffer.
- *
- * If have a non-empty command line, or is cli_more_wait, redraw the command
- * line.
- *
- * Returns: 0 => rump was empty and no command line stuff written
- * > 0 => rump not empty or some command line stuff written
- */
-extern int
-uty_cli_post_monitor(vty_io vio, const char* buf, size_t len)
-{
- VTY_ASSERT_LOCKED() ;
-
- if (len != 0)
- uty_cli_write(vio, buf, len) ;
+ if (cli->auth_context)
+ uty_cli_write_n(cli, telnet_stars, l_len) ;
+ else
+ uty_cli_write(cli, qs_char(cli->cl), l_len) ;
- if (vio->cli_more_wait || (vio->cl.len != 0))
- {
- uty_cli_draw(vio) ;
- ++len ;
+ if (qs_cp_nn(cli->cl) < l_len)
+ uty_cli_write_n(cli, telnet_backspaces, l_len - qs_cp_nn(cli->cl)) ;
} ;
-
- return len ;
} ;
/*==============================================================================
* Command line processing loop
*/
+static cmd_do_t uty_cli_get_keystroke(vty_cli cli, keystroke stroke) ;
+static void uty_cli_update_line(vty_cli cli, uint rc) ;
-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 void uty_cli_insert (qstring cl, const char* chars, int n) ;
+static int uty_cli_forwards(qstring cl, int n) ;
+static int uty_cli_backwards(qstring cl, int n) ;
+static void uty_cli_del_forwards(qstring cl, int n) ;
+static void uty_cli_del_backwards(qstring cl, int n) ;
+static void uty_cli_bol(qstring cl) ;
+static void uty_cli_eol(qstring cl) ;
+static int uty_cli_word_forwards_delta(qstring cl) ;
+static void uty_cli_word_forwards(qstring cl) ;
+static int uty_cli_word_backwards_delta(qstring cl) ;
+static void uty_cli_word_backwards(qstring cl) ;
+static void uty_cli_del_word_forwards(qstring cl) ;
+static void uty_cli_del_word_backwards(qstring cl) ;
+static void uty_cli_del_to_eol(qstring cl) ;
+static void uty_cli_clear_line(qstring cl) ;
+static void uty_cli_transpose_chars(qstring cl) ;
+static void uty_cli_complete_command (vty_cli cli) ;
+static void uty_cli_describe_command (vty_cli cli) ;
-/*------------------------------------------------------------------------------
- * Fetch next keystroke, reading from the file if required.
- */
-static inline bool
-uty_cli_get_keystroke(vty_io vio, keystroke stroke)
+enum hist_step
{
- if (keystroke_get(vio->key_stream, stroke))
- return 1 ;
+ hist_previous = -1,
+ hist_next = +1,
+};
- uty_read(vio, NULL) ; /* not stealing */
-
- return keystroke_get(vio->key_stream, stroke) ;
-} ;
+static void uty_cli_hist_use(vty_cli cli, enum hist_step) ;
/*------------------------------------------------------------------------------
- * 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.
+ * Expects the command line to have been drawn.
*
* Process keystrokes until run out of stuff to do, or have a "command line"
* that must now be executed.
@@ -1389,153 +1580,138 @@ 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
- *
- * When returns the cl is '\0' terminated.
+ * Returns: cmd_do_xxxx
*/
-static enum cli_do
-uty_cli_process(vty_io vio, enum node_type node)
+static cmd_do_t
+uty_cli_process(vty_cli cli)
{
- struct keystroke stroke ;
- uint8_t u ;
- int auth ;
- enum cli_do ret ;
+ keystroke_t stroke ;
+ uint8_t u ;
+ cmd_do_t to_do ;
- auth = (node == AUTH_NODE || node == AUTH_ENABLE_NODE) ;
+ qs_copy(cli->cls, cli->cl) ;
+
+ assert(cli->drawn) ;
/* Now process as much as possible of what there is */
- ret = cli_do_nothing ;
- while (1)
+ do
{
- if (!vio->cli_drawn)
- uty_cli_draw_this(vio, node) ;
-
- if (!uty_cli_get_keystroke(vio, &stroke))
- {
- ret = (stroke.value == knull_eof) ? cli_do_eof : cli_do_nothing ;
- break ;
- } ;
+ to_do = uty_cli_get_keystroke(cli, stroke) ;
- if (stroke.flags != 0)
- {
- /* TODO: deal with broken keystrokes */
- continue ;
- } ;
+ if (to_do != cmd_do_keystroke)
+ break ;
- switch (stroke.type)
+ switch (stroke->type)
{
/* Straightforward character -----------------------------------*/
/* Note: only interested in 8-bit characters ! */
case ks_char:
- u = (uint8_t)stroke.value ;
+ u = (uint8_t)stroke->value ;
- switch (stroke.value)
+ switch (stroke->value)
{
case CONTROL('A'):
- uty_cli_bol (vio);
+ uty_cli_bol(cli->cl) ;
break;
case CONTROL('B'):
- uty_cli_backwards(vio, 1);
+ uty_cli_backwards(cli->cl, 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 ..................*/
- else
- uty_cli_del_forwards(vio, 1);
+ uty_cli_del_forwards(cli->cl, 1);
break;
case CONTROL('E'):
- uty_cli_eol (vio);
+ uty_cli_eol(cli->cl);
break;
case CONTROL('F'):
- uty_cli_forwards(vio, 1);
+ uty_cli_forwards(cli->cl, 1);
break;
case CONTROL('H'):
case 0x7f:
- uty_cli_del_backwards(vio, 1);
+ uty_cli_del_backwards(cli->cl, 1);
break;
case CONTROL('K'):
- uty_cli_del_to_eol (vio);
+ uty_cli_del_to_eol(cli->cl);
break;
case CONTROL('N'):
- uty_cli_next_line (vio);
+ uty_cli_hist_use(cli, hist_next) ;
break;
case CONTROL('P'):
- uty_cli_previous_line (vio);
+ uty_cli_hist_use(cli, hist_previous) ;
break;
case CONTROL('T'):
- uty_cli_transpose_chars (vio);
+ uty_cli_transpose_chars(cli->cl) ;
break;
case CONTROL('U'):
- uty_cli_clear_line(vio);
+ uty_cli_clear_line(cli->cl) ;
break;
case CONTROL('W'):
- uty_cli_del_word_backwards (vio);
+ uty_cli_del_word_backwards(cli->cl) ;
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 ;
+ if (cmd_token_position(cli->parsed, cli->cl))
+ uty_cli_insert (cli->cl, " ", 1) ;
else
- uty_cli_complete_command (vio, node);
+ uty_cli_complete_command(cli);
break;
case '?':
- if (auth)
- uty_cli_insert (vio, (char*)&u, 1);
+ if (cmd_token_position(cli->parsed, cli->cl))
+ uty_cli_insert(cli->cl, (char*)&u, 1) ;
else
- uty_cli_describe_command (vio, node);
+ uty_cli_describe_command(cli);
break;
default:
- if ((stroke.value >= 0x20) && (stroke.value < 0x7F))
- uty_cli_insert (vio, (char*)&u, 1) ;
+ if ((stroke->value >= 0x20) && (stroke->value < 0x7F))
+ uty_cli_insert(cli->cl, (char*)&u, 1) ;
break;
}
break ;
/* ESC X -------------------------------------------------------------*/
case ks_esc:
- switch (stroke.value)
+ switch (stroke->value)
{
case 'b':
- uty_cli_word_backwards (vio);
+ uty_cli_word_backwards(cli->cl);
break;
case 'f':
- uty_cli_word_forwards (vio);
+ uty_cli_word_forwards(cli->cl);
break;
case 'd':
- uty_cli_del_word_forwards (vio);
+ uty_cli_del_word_forwards(cli->cl);
break;
case CONTROL('H'):
case 0x7f:
- uty_cli_del_word_backwards (vio);
+ uty_cli_del_word_backwards(cli->cl);
break;
default:
@@ -1543,36 +1719,39 @@ uty_cli_process(vty_io vio, enum node_type node)
} ;
break ;
- /* ESC [ X -----------------------------------------------------------*/
+ /* ESC [ ... ---------------------------------------------------------*/
case ks_csi:
- if (stroke.len != 0)
- break ; /* only recognise 3 byte sequences */
-
- switch (stroke.value)
- {
- case ('A'):
- uty_cli_previous_line (vio);
- break;
-
- case ('B'):
- uty_cli_next_line (vio);
- break;
-
- case ('C'):
- uty_cli_forwards(vio, 1);
- break;
-
- case ('D'):
- uty_cli_backwards(vio, 1);
- break;
- default:
- break ;
- } ;
- break ;
-
- /* Telnet Command ----------------------------------------------------*/
- case ks_iac:
- uty_telnet_command(vio, &stroke, false) ;
+ if (stroke->len == 0)
+ {
+ /* ESC [ X */
+ switch (stroke->value)
+ {
+ case ('A'): /* up arrow */
+ uty_cli_hist_use(cli, hist_previous) ;
+ break;
+
+ case ('B'): /* down arrow */
+ uty_cli_hist_use(cli, hist_next) ;
+ break;
+
+ case ('C'): /* right arrow */
+ uty_cli_forwards(cli->cl, 1) ;
+ break;
+
+ case ('D'): /* left arrow */
+ uty_cli_backwards(cli->cl, 1) ;
+ break;
+
+ default:
+ break ;
+ } ;
+ }
+ else if (stroke->len == 1)
+ {
+ /* ESC [ 3 ~ only ! */
+ if ((stroke->value == '~') && (stroke->buf[0] == '3'))
+ uty_cli_del_forwards(cli->cl, 1) ;
+ } ;
break ;
/* Unknown -----------------------------------------------------------*/
@@ -1580,214 +1759,400 @@ uty_cli_process(vty_io vio, enum node_type node)
zabort("unknown keystroke type") ;
} ;
- /* After each keystroke..... */
-
- if (ret != cli_do_nothing)
- {
- uty_cli_eol (vio) ; /* go to the end of the line */
- break ; /* stop processing */
- } ;
- } ;
+ }
+ while (to_do == cmd_do_keystroke) ;
/* Tidy up and return where got to. */
- qs_term(&vio->cl) ; /* add '\0' */
+ if (to_do != cmd_do_nothing)
+ uty_cli_eol(cli->cl) ; /* go to the end of the line */
- return ret ;
-} ;
+ uty_cli_update_line(cli, qs_cp_nn(cli->cl)) ;
-/*==============================================================================
- * Command line operations
- */
+ return to_do ;
+} ;
/*------------------------------------------------------------------------------
- * Insert 'n' characters at current position in the command line
+ * Update the command line to reflect the difference between old line and the
+ * new line.
*
- * Returns number of characters inserted -- ie 'n'
+ * Leave the screen cursor at the given required cursor position.
*/
-static int
-uty_cli_insert (vty_io vio, const char* chars, int n)
+static void
+uty_cli_update_line(vty_cli cli, uint rc)
{
- int after ;
+ const char* np ; /* new line */
+ const char* sp ; /* screen line */
- VTY_ASSERT_LOCKED() ;
+ ulen nl ; /* new length */
+ ulen sl ; /* screen length */
- assert((vio->cl.cp <= vio->cl.len) && (n >= 0)) ;
+ ulen sc ; /* screen cursor */
- if (n <= 0)
- return n ; /* avoid trouble */
+ ulen s, l ;
- after = qs_insert(&vio->cl, chars, n) ;
+ assert(cli->drawn) ;
- uty_cli_echo(vio, qs_cp_char(&vio->cl), after + n) ;
+ np = qs_char_nn(cli->cl) ;
+ nl = qs_len_nn(cli->cl) ;
- if (after != 0)
- uty_cli_echo_n(vio, telnet_backspaces, after) ;
+ sp = qs_char_nn(cli->cls) ;
+ sl = qs_len_nn(cli->cls) ;
+ sc = qs_cp_nn(cli->cls) ;
- vio->cl.cp += n ;
+ /* Find how many characters are the same. */
+ l = (nl <= sl) ? nl : sl ;
+ s = 0 ;
+ while ((s < l) && (*(np + s) == *(sp + s)))
+ ++s ;
- return n ;
+ /* If the screen and new are different lengths, or the strings are not
+ * the same, then need to draw stuff to correct what is on the screen.
+ * That will leave the screen cursor at the end of the new line, after
+ * any spaces required to wipe out excess characters.
+ *
+ * Note that sc is the current cursor position on the screen, and we keep
+ * that up to date as we draw stuff.
+ */
+ if ((nl != sl) || (s != nl))
+ {
+ /* Move back if the screen cursor is beyond the same section */
+ if (sc > s)
+ {
+ uty_cli_write_n(cli, telnet_backspaces, sc - s) ;
+ sc = s ;
+ } ;
+
+ /* Write from cursor to the end of the new line. */
+ uty_cli_write(cli, np + sc, nl - sc) ;
+ sc = nl ;
+
+ /* If the old line was longer, need to wipe out old stuff */
+ if (sl > nl)
+ {
+ uty_cli_write_n(cli, telnet_spaces, sl - nl) ;
+ sc = sl ;
+ } ;
+ } ;
+
+ /* Now move cursor to the required cursor position */
+ if (sc > rc)
+ uty_cli_write_n(cli, telnet_backspaces, sc - rc) ;
+
+ if (sc < rc) /* => lines unchanged, but cursor moved */
+ uty_cli_write(cli, np + sc, rc - sc) ;
} ;
/*------------------------------------------------------------------------------
- * Overstrike 'n' characters at current position in the command line
+ * For password: process keystrokes until run out of input, or get something
+ * to cmd_do.
*
- * Move current position forwards.
+ * Similar to uty_cli_auth, except accepts a limited number of keystrokes.
*
- * Returns number of characters inserted -- ie 'n'
+ * Returns: cmd_do_xxxx
*/
-static int
-uty_cli_overwrite (vty_io vio, char* chars, int n)
+static cmd_do_t
+uty_cli_auth(vty_cli cli)
{
- VTY_ASSERT_LOCKED() ;
+ keystroke_t stroke ;
+ uint8_t u ;
+ cmd_do_t to_do ;
+ int olen, nlen ;
- assert((vio->cl.cp <= vio->cl.len) && (n >= 0)) ;
+ /* For auth command lines, cursor is always at the end */
+ assert(qs_cp_nn(cli->cl) == qs_len_nn(cli->cl)) ;
- if (n > 0)
+ olen = qs_len_nn(cli->cl) ;
+
+ /* Now process as much as possible of what there is */
+ do
{
- qs_replace(&vio->cl, chars, n) ;
- uty_cli_echo(vio, chars, n) ;
+ to_do = uty_cli_get_keystroke(cli, stroke) ;
- vio->cl.cp += n ;
- } ;
+ if (to_do != cmd_do_keystroke)
+ break ;
- return n ;
-}
+ switch (stroke->type)
+ {
+ /* Straightforward character ---------------------------------*/
+ /* Note: only interested in 8-bit characters ! */
+ case ks_char:
+ u = (uint8_t)stroke->value ;
+
+ switch (stroke->value)
+ {
+ case CONTROL('C'):
+ to_do = cmd_do_ctrl_c ; /* Exit on ^C */
+ break ;
+
+ case CONTROL('D'):
+ to_do = cmd_do_ctrl_d ; /* Exit on ^D */
+ break;
+
+ case CONTROL('H'):
+ case 0x7F:
+ uty_cli_del_backwards(cli->cl, 1);
+ break;
+
+ case CONTROL('U'):
+ case CONTROL('W'):
+ uty_cli_clear_line(cli->cl);
+ break;
+
+ case CONTROL('Z'):
+ to_do = cmd_do_ctrl_z ; /* Exit on ^Z */
+ break;
+
+ case '\n':
+ case '\r':
+ to_do = cmd_do_command ; /* Exit on CR or LF */
+ break ;
+
+ default:
+ if ((stroke->value >= 0x20) && (stroke->value < 0x7F))
+ uty_cli_insert(cli->cl, (char*)&u, 1) ;
+ break;
+ }
+ break ;
+
+ /* ESC X -----------------------------------------------------*/
+ case ks_esc:
+ switch (stroke->value)
+ {
+ case CONTROL('H'):
+ case 0x7f:
+ uty_cli_clear_line(cli->cl);
+ break;
+
+ default:
+ break;
+ } ;
+ break ;
+
+ /* ESC [ ... ---------------------------------------------------*/
+ case ks_csi:
+ break ;
+
+ /* Unknown -----------------------------------------------------*/
+ default:
+ zabort("unknown keystroke type") ;
+ } ;
+ }
+ while (to_do == cmd_do_keystroke) ;
+
+ /* Tidy up and return where got to. */
+
+ nlen = qs_len_nn(cli->cl) ;
+
+ if (nlen < olen)
+ uty_cli_out_wipe_n(cli, nlen - olen) ;
+ if (nlen > olen)
+ uty_cli_write_n(cli, telnet_stars, nlen - olen) ;
+
+ return to_do ;
+} ;
/*------------------------------------------------------------------------------
- * Insert a word into vty interface with overwrite mode.
+ * Fetch next keystroke, reading from the file if required.
*
- * NB: Assumes result will then be the end of the line.
+ * Returns: cmd_do_t
+ */
+static cmd_do_t
+uty_cli_get_keystroke(vty_cli cli, keystroke stroke)
+{
+ while (1)
+ {
+ if (keystroke_get(cli->key_stream, stroke))
+ {
+ if (stroke->flags != 0)
+ {
+ /* TODO: deal with broken keystrokes */
+ }
+
+ if (stroke->type != ks_iac)
+ return cmd_do_keystroke ; /* have a keystroke */
+
+ /* Deal with telnet command, so invisible to upper level */
+ uty_telnet_command(cli->vf, stroke, false) ;
+ }
+ else
+ {
+ int get ;
+
+ assert(stroke->type == ks_null) ;
+
+ switch (stroke->value)
+ {
+ case knull_not_eof:
+ break ;
+
+ case knull_eof:
+ return cmd_do_eof ;
+
+ case knull_timed_out:
+ return cmd_do_timed_out ;
+
+ default:
+ zabort("unknown knull_xxx") ;
+ break ;
+ } ;
+
+ get = uty_term_read(cli->vf, NULL) ; /* sets eof in key_stream
+ if hit eof or error */
+ if (get <= 0)
+ return cmd_do_nothing ;
+ } ;
+ } ;
+} ;
+
+/*==============================================================================
+ * Command line operations
+ */
+
+/*------------------------------------------------------------------------------
+ * Insert 'n' characters at current position in the command line, leaving
+ * cursor after the inserted characters.
*
- * Returns number of characters inserted -- ie length of string
+ * NB: assumes line will be updated by uty_cli_update_line()
*/
-static int
-uty_cli_word_overwrite (vty_io vio, char *str)
+static void
+uty_cli_insert(qstring cl, const char* chars, int n)
{
- int n ;
- VTY_ASSERT_LOCKED() ;
+ assert((qs_cp_nn(cl) <= qs_len_nn(cl)) && (n >= 0)) ;
- n = uty_cli_overwrite(vio, str, strlen(str)) ;
+ if (n > 0)
+ {
+ qs_insert(cl, chars, n) ;
+ qs_move_cp_nn(cl, n) ;
+ } ;
+} ;
- vio->cl.len = vio->cl.cp ;
+/*------------------------------------------------------------------------------
+ * Replace 'm' characters at the current position, by 'n' characters and leave
+ * cursor at the end of the inserted characters.
+ *
+ * NB: assumes line will be updated by uty_cli_update_line()
+ */
+static void
+uty_cli_replace(qstring cl, int m, const char* chars, int n)
+{
+ assert((qs_cp_nn(cl) <= qs_len_nn(cl)) && (n >= 0) && (m >= 0)) ;
- return n ;
-}
+ qs_replace(cl, m, chars, n) ;
+
+ qs_move_cp_nn(cl, n) ;
+} ;
/*------------------------------------------------------------------------------
* Forward 'n' characters -- stop at end of line.
*
* Returns number of characters actually moved
+ *
+ * NB: assumes line will be updated by uty_cli_update_line()
*/
static int
-uty_cli_forwards(vty_io vio, int n)
+uty_cli_forwards(qstring cl, int n)
{
int have ;
- VTY_ASSERT_LOCKED() ;
- have = vio->cl.len - vio->cl.cp ;
+ have = qs_after_cp_nn(cl) ;
if (have < n)
n = have ;
assert(n >= 0) ;
- if (n > 0)
- {
- uty_cli_echo(vio, qs_cp_char(&vio->cl), n) ;
- vio->cl.cp += n ;
- } ;
+ qs_move_cp_nn(cl, n) ;
return n ;
-}
+} ;
/*------------------------------------------------------------------------------
* Backwards 'n' characters -- stop at start of line.
*
* Returns number of characters actually moved
+ *
+ * NB: assumes line will be updated by uty_cli_update_line()
*/
static int
-uty_cli_backwards(vty_io vio, int n)
+uty_cli_backwards(qstring cl, int n)
{
- VTY_ASSERT_LOCKED() ;
-
- if ((int)vio->cl.cp < n)
- n = vio->cl.cp ;
+ if ((int)qs_cp_nn(cl) < n)
+ n = qs_cp_nn(cl) ;
assert(n >= 0) ;
- if (n > 0)
- {
- uty_cli_echo_n(vio, telnet_backspaces, n) ;
- vio->cl.cp -= n ;
- } ;
+ qs_move_cp_nn(cl, -n) ;
return n ;
-}
+} ;
+
+/*------------------------------------------------------------------------------
+ * Move forwards (if n > 0) or backwards (if n < 0) -- stop at start or end of
+ * line.
+ *
+ * NB: assumes line will be updated by uty_cli_update_line()
+ */
+static void
+uty_cli_move(qstring cl, int n)
+{
+ if (n < 0)
+ uty_cli_backwards(cl, -n) ;
+
+ if (n > 0)
+ uty_cli_forwards(cl, +n) ;
+} ;
/*------------------------------------------------------------------------------
* Delete 'n' characters -- forwards -- stop at end of line.
*
- * Returns number of characters actually deleted.
+ * NB: assumes line will be updated by uty_cli_update_line()
*/
-static int
-uty_cli_del_forwards(vty_io vio, int n)
+static void
+uty_cli_del_forwards(qstring cl, int n)
{
- int after ;
int have ;
- VTY_ASSERT_LOCKED() ;
-
- have = vio->cl.len - vio->cl.cp ;
+ have = qs_after_cp_nn(cl) ;
if (have < n)
n = have ; /* cannot delete more than have */
assert(n >= 0) ;
- if (n <= 0)
- return 0 ;
-
- after = qs_delete(&vio->cl, n) ;
-
- if (after > 0)
- uty_cli_echo(vio, qs_cp_char(&vio->cl), after) ;
-
- uty_cli_echo_n(vio, telnet_spaces, n) ;
- uty_cli_echo_n(vio, telnet_backspaces, after + n) ;
-
- return n ;
+ if (n > 0)
+ qs_delete(cl, n) ;
}
/*------------------------------------------------------------------------------
* Delete 'n' characters before the point.
*
- * Returns number of characters actually deleted.
+ * NB: assumes line will be updated by uty_cli_update_line()
*/
-static int
-uty_cli_del_backwards(vty_io vio, int n)
+static void
+uty_cli_del_backwards(qstring cl, int n)
{
- return uty_cli_del_forwards(vio, uty_cli_backwards(vio, n)) ;
+ uty_cli_del_forwards(cl, uty_cli_backwards(cl, n)) ;
}
/*------------------------------------------------------------------------------
* Move to the beginning of the line.
*
- * Returns number of characters moved over.
+ * NB: assumes line will be updated by uty_cli_update_line()
*/
-static int
-uty_cli_bol (vty_io vio)
+static void
+uty_cli_bol(qstring cl)
{
- return uty_cli_backwards(vio, vio->cl.cp) ;
+ qs_set_cp_nn(cl, 0) ;
} ;
/*------------------------------------------------------------------------------
* Move to the end of the line.
*
- * Returns number of characters moved over
+ * NB: assumes line will be updated by uty_cli_update_line()
*/
-static int
-uty_cli_eol (vty_io vio)
+static void
+uty_cli_eol(qstring cl)
{
- return uty_cli_forwards(vio, vio->cl.len - vio->cl.cp) ;
+ qs_set_cp_nn(cl, qs_len_nn(cl)) ;
} ;
/*------------------------------------------------------------------------------
@@ -1798,25 +2163,23 @@ 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(qstring cl)
{
char* cp ;
char* tp ;
char* ep ;
- VTY_ASSERT_LOCKED() ; ;
+ cp = qs_cp_char(cl) ;
+ ep = qs_ep_char(cl) ;
- assert(vio->cl.cp <= vio->cl.len) ;
-
- cp = qs_cp_char(&vio->cl) ;
- ep = qs_ep_char(&vio->cl) ;
+ assert(cp <= ep) ;
tp = cp ;
- while ((tp < ep) && (*tp != ' '))
+ while ((tp < ep) && (*tp != ' ')) /* step over spaces */
++tp ;
- while ((tp < ep) && (*tp == ' '))
+ while ((tp < ep) && (*tp == ' ')) /* step to space */
++tp ;
return tp - cp ;
@@ -1826,11 +2189,13 @@ uty_cli_word_forwards_delta(vty_io vio)
* Forward word -- move to start of next word.
*
* Moves past any non-spaces, then past any spaces.
+ *
+ * NB: assumes line will be updated by uty_cli_update_line()
*/
-static int
-uty_cli_word_forwards(vty_io vio)
+static void
+uty_cli_word_forwards(qstring cl)
{
- return uty_cli_forwards(vio, uty_cli_word_forwards_delta(vio)) ;
+ uty_cli_forwards(cl, uty_cli_word_forwards_delta(cl)) ;
} ;
/*------------------------------------------------------------------------------
@@ -1842,24 +2207,21 @@ 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(qstring cl)
{
char* cp ;
char* tp ;
char* sp ;
- VTY_ASSERT_LOCKED() ; ;
+ assert(qs_cp_nn(cl) <= qs_len_nn(cl)) ;
- assert(vio->cl.cp <= vio->cl.len) ;
-
- cp = qs_cp_char(&vio->cl) ;
- sp = qs_chars(&vio->cl) ;
+ cp = qs_cp_char(cl) ;
+ sp = qs_char(cl) ;
tp = cp ;
- if (eat_spaces)
- while ((tp > sp) && (*(tp - 1) == ' '))
- --tp ;
+ while ((tp > sp) && (*(tp - 1) == ' '))
+ --tp ;
while ((tp > sp) && (*(tp - 1) != ' '))
--tp ;
@@ -1868,30 +2230,17 @@ uty_cli_word_backwards_delta(vty_io vio, int eat_spaces)
} ;
/*------------------------------------------------------------------------------
- * Backward word, but not trailing spaces.
- *
- * Move back until next (backwards) character is space or start of line.
- *
- * Returns number of characters stepped over.
- */
-static int
-uty_cli_word_backwards_pure (vty_io vio)
-{
- return uty_cli_backwards(vio, uty_cli_word_backwards_delta(vio, 0)) ;
-} ;
-
-/*------------------------------------------------------------------------------
* Backward word -- move to start of previous word.
*
* Moves past any spaces, then move back until next (backwards) character is
* space or start of line.
*
- * Returns number of characters stepped over.
+ * NB: assumes line will be updated by uty_cli_update_line()
*/
-static int
-uty_cli_word_backwards (vty_io vio)
+static void
+uty_cli_word_backwards(qstring cl)
{
- return uty_cli_backwards(vio, uty_cli_word_backwards_delta(vio, 1)) ;
+ uty_cli_backwards(cl, uty_cli_word_backwards_delta(cl)) ;
} ;
/*------------------------------------------------------------------------------
@@ -1899,12 +2248,12 @@ uty_cli_word_backwards (vty_io vio)
*
* Deletes any leading spaces, then deletes upto next space or end of line.
*
- * Returns number of characters deleted.
+ * NB: assumes line will be updated by uty_cli_update_line()
*/
-static int
-uty_cli_del_word_forwards(vty_io vio)
+static void
+uty_cli_del_word_forwards(qstring cl)
{
- return uty_cli_del_forwards(vio, uty_cli_word_forwards_delta(vio)) ;
+ uty_cli_del_forwards(cl, uty_cli_word_forwards_delta(cl)) ;
}
/*------------------------------------------------------------------------------
@@ -1912,99 +2261,148 @@ uty_cli_del_word_forwards(vty_io vio)
*
* Deletes any trailing spaces, then deletes upto next space or start of line.
*
- * Returns number of characters deleted.
+ * NB: assumes line will be updated by uty_cli_update_line()
*/
-static int
-uty_cli_del_word_backwards(vty_io vio)
+static void
+uty_cli_del_word_backwards(qstring cl)
{
- return uty_cli_del_backwards(vio, uty_cli_word_backwards_delta(vio, 1)) ;
+ uty_cli_del_backwards(cl, uty_cli_word_backwards_delta(cl)) ;
} ;
/*------------------------------------------------------------------------------
* Kill rest of line from current point.
*
- * Returns number of characters deleted.
+ * NB: assumes line will be updated by uty_cli_update_line()
*/
-static int
-uty_cli_del_to_eol (vty_io vio)
+static void
+uty_cli_del_to_eol(qstring cl)
{
- return uty_cli_del_forwards(vio, vio->cl.len - vio->cl.cp) ;
+ qs_set_len_nn(cl, qs_cp_nn(cl)) ;
} ;
/*------------------------------------------------------------------------------
* Kill line from the beginning.
*
- * Returns number of characters deleted.
+ * NB: assumes line will be updated by uty_cli_update_line()
*/
-static int
-uty_cli_clear_line(vty_io vio)
+static void
+uty_cli_clear_line(qstring cl)
{
- uty_cli_bol(vio) ;
- return uty_cli_del_to_eol(vio) ;
+ qs_set_cp_nn(cl, 0) ;
+ qs_set_len_nn(cl, 0) ;
} ;
/*------------------------------------------------------------------------------
* Transpose chars before or at the point.
*
- * Return number of characters affected.
+ * NB: assumes line will be updated by uty_cli_update_line()
*/
-static int
-uty_cli_transpose_chars(vty_io vio)
+static void
+uty_cli_transpose_chars(qstring cl)
{
- char chars[2] ;
+ char ch ;
char* cp ;
- VTY_ASSERT_LOCKED() ;
-
/* Give up if < 2 characters or at start of line. */
- if ((vio->cl.len < 2) || (vio->cl.cp < 1))
- return 0 ;
+ if ((qs_len_nn(cl) < 2) || (qs_cp_nn(cl) < 1))
+ return ;
- /* Move back to first of characters to exchange */
- if (vio->cl.cp == vio->cl.len)
- uty_cli_backwards(vio, 2) ;
- else
- uty_cli_backwards(vio, 1) ;
+ /* If we are not at the end, step past the second character */
+ if (qs_after_cp_nn(cl) == 0)
+ qs_move_cp_nn(cl, 1) ;
- /* Pick up in the new order */
- cp = qs_cp_char(&vio->cl) ;
- chars[1] = *cp++ ;
- chars[0] = *cp ;
+ /* Get address of first character */
+ cp = qs_cp_char(cl) - 2 ;
- /* And overwrite */
- return uty_cli_overwrite(vio, chars, 2) ;
+ /* swap characters */
+ ch = *(cp + 1) ;
+ *(cp + 1) = *cp ;
+ *cp = ch ;
} ;
/*==============================================================================
* Command line history handling
+ *
+ * cli->hist is vector of qstrings (created on demand)
+ * 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, which is the
+ * same as the oldest thing we can remember !).
+ *
+ * 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.
*/
+static inline uint
+hp_next(uint hp)
+{
+ return (hp != (VTY_HIST_COUNT - 1)) ? hp + 1 : 0 ;
+} ;
+
+static inline uint
+hp_prev(uint hp)
+{
+ return (hp != 0) ? hp - 1 : VTY_HIST_COUNT - 1 ;
+} ;
+
+static inline uint
+hp_step(uint hp, enum hist_step step)
+{
+ if (step == hist_previous)
+ return hp_prev(hp) ;
+
+ if (step == hist_next)
+ return hp_next(hp) ;
+
+ zabort("invalid hist_step") ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Create cli->hist.
+ */
+static void
+uty_cli_hist_make(vty_cli cli)
+{
+ cli->hist = vector_init_new(cli->hist, VTY_HIST_COUNT) ;
+ vector_set_min_length(cli->hist, VTY_HIST_COUNT) ;
+} ;
+
/*------------------------------------------------------------------------------
* 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 h_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) ;
-
- /* find the previous command line in the history */
- prev_index = vio->hindex - 1 ;
- if (prev_index < 0)
- prev_index = VTY_MAXHIST - 1 ;
-
- prev_line = vector_get_item(&vio->hist, prev_index) ;
+ if (cli->hist == NULL)
+ uty_cli_hist_make(cli) ; /* create if required */
/* If the previous line is NULL, that means the history is empty.
*
@@ -2012,23 +2410,38 @@ 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 ;
- else
- prev_line = vector_get_item(&vio->hist, vio->hindex) ;
+ h_prev = hp_prev(cli->h_now) ;
- /* Now replace the hindex entry */
- vector_set_item(&vio->hist, vio->hindex, qs_copy(prev_line, &line)) ;
+ hist_line = vector_get_item(cli->hist, h_prev) ;
- /* Advance to the near future and reset the history pointer */
- vio->hindex++;
- if (vio->hindex == VTY_MAXHIST)
- vio->hindex = 0;
+ if ((hist_line == NULL) || (qs_cmp_sig(hist_line, clx) == 0))
+ {
+ cli->h_now = h_prev ;
+ cli->h_repeat = true ; /* latest history is a repeat */
+ }
+ else
+ {
+ hist_line = vector_get_item(cli->hist, cli->h_now) ;
+ cli->h_repeat = false ; /* latest history is novel */
+ } ;
+
+ /* 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) ;
- vio->hp = vio->hindex;
+ /* Advance history */
+ cli->hp = cli->h_now = hp_next(cli->h_now) ;
} ;
/*------------------------------------------------------------------------------
@@ -2036,236 +2449,336 @@ 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)
+ *
+ * NB: assumes line will be updated by uty_cli_update_line()
*/
static void
-uty_cli_history_use(vty_io vio, int step)
+uty_cli_hist_use(vty_cli cli, enum hist_step step)
{
- int index ;
- unsigned old_len ;
- unsigned after ;
- unsigned back ;
- qstring hist ;
-
- VTY_ASSERT_LOCKED() ;
+ uint hp ;
+ qstring hist_line ;
assert((step == +1) || (step == -1)) ;
- index = vio->hp ;
+ if (cli->hist == NULL)
+ uty_cli_hist_make(cli) ; /* create if required */
+
+ hp = cli->hp ;
- /* Special case of being at the insertion point */
- if (index == vio->hindex)
+ /* Special case of being at the insertion point (the present)
+ *
+ * Cannot step forwards from 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. So if return to the present,
+ * the cursor returns to its current position. (When lines are added to
+ * the history, the cursor is at the end of the line.)
+ */
+ 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.
- */
- 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 ;
+ /* Advance or retreat and get history line. */
+ hp = hp_step(hp, step) ;
- 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 (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;
+ /* Update the history pointer and copy history line to current line. */
+ cli->hp = hp ;
+ qs_copy(cli->cl, hist_line) ;
+} ;
- /* Move back to the start of the current line */
- uty_cli_bol(vio) ;
+/*------------------------------------------------------------------------------
+ * Show the contents of the history
+ */
+extern void
+uty_cli_hist_show(vty_cli cli)
+{
+ uint hp ;
+ uint h_end ;
- /* Get previous line from history buffer and echo that */
- old_len = vio->cl.len ;
- qs_copy(&vio->cl, hist) ;
+ VTY_ASSERT_LOCKED() ;
- /* Sort out wiping out any excess and setting the cursor position */
- if (old_len > vio->cl.len)
- after = old_len - vio->cl.len ;
- else
- after = 0 ;
+ if (cli->hist == NULL)
+ return ; /* if no history */
- back = after ;
- if (vio->cl.len > vio->cl.cp)
- back += (vio->cl.len - vio->cl.cp) ;
+ /* We start with the oldest thing we can remember, which means that
+ * we start by stepping "forwards" from "now".
+ *
+ * Until the history buffer fills, there will be a number of NULL entries
+ * between "now" and the oldest thing in the history.
+ *
+ * We do not show the "now" entry, which is not part of history.
+ *
+ * We do not show the entry before "now", because that is the current
+ * executing command, unless that was a repeat of the command before !
+ */
+ hp = cli->h_now ;
- if (vio->cl.len > 0)
- uty_cli_echo(vio, vio->cl.body, vio->cl.len) ;
+ h_end = cli->h_repeat ? hp : hp_prev(hp) ;
- if (after > 0)
- uty_cli_echo_n(vio, telnet_spaces, after) ;
+ while (1)
+ {
+ qstring line ;
- if (back > 0)
- uty_cli_echo_n(vio, telnet_backspaces, back) ;
+ hp = hp_next(hp) ;
- return ;
-} ;
+ if (hp == h_end)
+ break ; /* reached end of history */
-/*------------------------------------------------------------------------------
- * Use next history line, if any.
- */
-static void
-uty_cli_next_line(vty_io vio)
-{
- uty_cli_history_use(vio, +1) ;
-}
+ line = vector_get_item(cli->hist, hp) ;
-/*------------------------------------------------------------------------------
- * Use previous history line, if any.
- */
-static void
-uty_cli_previous_line (vty_io vio)
-{
- uty_cli_history_use(vio, -1) ;
-}
+ if (line != NULL)
+ uty_out(cli->vf->vio, " %s\n", qs_make_string(line));
+ } ;
+} ;
/*==============================================================================
* 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 uint uty_cli_help_parse(vty_cli cli) ;
+
+static void uty_cli_complete_keyword(vty_cli cli, const char* keyword) ;
+static void uty_cli_complete_list(vty_cli cli, vector item_v) ;
-static vector uty_cli_cmd_prepare(vty_io vio, int help) ;
+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) ;
+static uint uty_cli_width_to_use(vty_cli cli) ;
+
+static void uty_cli_help_message(vty_cli cli, const char* msg) ;
+static void uty_cli_help_newline(vty_cli cli) ;
+static void uty_cli_help_finish(vty_cli cli) ;
/*------------------------------------------------------------------------------
* Command completion
+ *
+ * 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.
+ *
+ * This is called from inside "uty_cli_process()".
+ *
+ * NB: assumes line will be updated by uty_cli_update_line()
*/
static void
-uty_cli_complete_command (vty_io vio, enum node_type node)
+uty_cli_complete_command (vty_cli cli)
{
- unsigned i ;
- int ret ;
- int len ;
- int n ;
- vector matched ;
- vector vline ;
+ uint n_items ;
+ cmd_parsed parsed ;
+ cmd_item item ;
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);
+ parsed = cli->parsed ;
- /* Show the result. */
- switch (ret)
+ /* Establish what items may be present at the current token position. */
+ n_items = uty_cli_help_parse(cli) ;
+
+ if (n_items > 1) /* render list of alternatives */
+ uty_cli_complete_list(cli, parsed->item_v) ;
+ else if (n_items == 1)
{
- case CMD_ERR_AMBIGUOUS:
- uty_cli_out_newline(vio) ; /* clears cli_drawn */
- uty_cli_out_CMD_ERR_AMBIGUOUS(vio) ;
- break ;
+ /* One possible item -- one or more possible commands */
+ item = vector_get_item(parsed->item_v, 0) ;
- case CMD_ERR_NO_MATCH:
- uty_cli_out_newline(vio) ; /* clears cli_drawn */
- uty_cli_out_CMD_ERR_NO_MATCH(vio) ;
- break ;
+ switch (item->type)
+ {
+ case item_null:
+ zabort("invalid item_null") ;
- 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 item_eol:
- 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 item_option_word:
- 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 ;
- } ;
+ case item_vararg:
- n = vio->width ;
- if (n == 0)
- n = 60 ;
- n = n / (len + 2) ;
- if (n == 0)
- n = 1 ;
+ case item_word:
- 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) ;
+ case item_ipv6_prefix:
+ case item_ipv6_address:
+ case item_ipv4_prefix:
+ case item_ipv4_address:
- break;
+ case item_range:
+ uty_cli_describe_list(cli, parsed->item_v) ;
+ break ;
- case CMD_COMPLETE_ALREADY:
- default:
- break;
+ case item_keyword:
+ uty_cli_complete_keyword(cli, item->str) ;
+ break ;
+
+ default:
+ zabort("unknown item type") ;
+ } ;
} ;
- cmd_free_strvec(matched);
+ /* If necessary, redraw the command line */
+ uty_cli_help_finish(cli) ;
} ;
/*------------------------------------------------------------------------------
* Command Description
*/
static void
-uty_cli_describe_command (vty_io vio, enum node_type node)
+uty_cli_describe_command (vty_cli cli)
{
- int ret;
- vector vline;
- vector describe;
+ uint n_items ;
VTY_ASSERT_LOCKED() ;
- /* Try and match the tokenised command line */
- vline = uty_cli_cmd_prepare(vio, 1) ;
- describe = cmd_describe_command (vline, node, &ret);
- cmd_free_strvec (vline);
+ /* Establish what items may be present at the current token position. */
+ n_items = uty_cli_help_parse(cli) ;
- uty_cli_out_newline(vio); /* clears cli_drawn */
+ if (n_items > 0) /* render list of possibilities */
+ uty_cli_describe_list(cli, cli->parsed->item_v) ;
- /* Deal with result. */
- switch (ret)
+ /* If necessary, redraw the command line */
+ uty_cli_help_finish(cli) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * 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)
+{
+ const char* msg ;
+ cmd_return_code_t ret ;
+ uint n_items ;
+
+ /* The preflight checks avoid getting into trouble doing command completion
+ * on a line with comment
+ */
+ msg = cmd_help_preflight(cli->parsed) ;
+ if (msg != NULL)
{
- case CMD_ERR_AMBIGUOUS:
- uty_cli_out_CMD_ERR_AMBIGUOUS(vio) ;
- break ;
+ uty_cli_help_message(cli, msg) ;
+ return 0 ;
+ } ;
+
+ /* Now see what the cmd_completion can come up with. */
+ ret = cmd_completion(cli->parsed, cli->context) ;
+
+ if (ret == CMD_ERR_PARSING)
+ {
+ if (cli->parsed->eloc >= 0)
+ {
+ uint eloc = cli->prompt_len + cli->parsed->eloc ;
+
+ uty_cli_help_newline(cli) ; /* clears cli_drawn etc. */
+ uty_cli_write_n(cli, telnet_dots, eloc) ;
+ uty_cli_write_s(cli, "^") ;
+ } ;
- case CMD_ERR_NO_MATCH:
- uty_cli_out_CMD_ERR_NO_MATCH(vio) ;
- break ;
+ uty_cli_help_message(cli, qs_make_string(cli->parsed->emess)) ;
- default:
- uty_cli_describe_show(vio, describe) ;
- break ;
+ return 0 ;
} ;
- if (describe != NULL)
- vector_free (describe);
-}
+ /* Will now have 0, 1 or more items which match at the current
+ * cursor token.
+ */
+ n_items = vector_length(cli->parsed->item_v) ;
+
+ if (n_items == 0)
+ uty_cli_help_message(cli, "command not recognised") ;
+
+ return n_items ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Can complete a keyword.
+ */
+static void
+uty_cli_complete_keyword(vty_cli cli, const char* keyword)
+{
+ int pre, rep, ins, mov ;
+
+ cmd_complete_keyword(cli->parsed, &pre, &rep, &ins, &mov) ;
+
+ uty_cli_move(cli->cl, pre) ; /* move to start of token */
+ uty_cli_replace(cli->cl, rep, keyword, strlen(keyword)) ;
+
+ assert(ins <= 2) ;
+ if (ins > 0)
+ uty_cli_insert(cli->cl, " ", ins) ;
+
+ uty_cli_move(cli->cl, mov) ;
+
+ return ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * 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_cli_complete_list(vty_cli cli, vector item_v)
+{
+ uint i, len, n ;
+
+ len = 6 ;
+ for (i = 0 ; i < vector_length(item_v) ; ++i)
+ {
+ cmd_item item ;
+ uint sl ;
+
+ item = vector_get_item(item_v, i) ;
+
+ sl = strlen(item->str) ;
+ if (len < sl)
+ len = sl ;
+ } ;
+
+ n = uty_cli_width_to_use(cli) / (len + 2) ;
+
+ 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_help_newline(cli) ;
+} ;
/*------------------------------------------------------------------------------
* Show the command description.
@@ -2281,426 +2794,170 @@ uty_cli_describe_command (vty_io vio, enum node_type node)
* word description ..................................
* .............text
*
- * If one of the options is '<cr>', that is always shown last.
+ * NB: the items will have been sorted before we get here. Inter alia, that
+ * ensures that any <cr> is shown last.
*/
static void
-uty_cli_describe_show(vty_io vio, vector describe)
+uty_cli_describe_list(vty_cli cli, vector item_v)
{
- unsigned int i, cmd_width, desc_width;
- struct desc *desc, *desc_cr ;
+ uint i, str_width, doc_width, width ;
/* Get width of the longest "word" */
- cmd_width = 0;
- for (i = 0; i < vector_active (describe); i++)
- if ((desc = vector_slot (describe, i)) != NULL)
- {
- unsigned int len;
-
- if (desc->cmd[0] == '\0')
- continue;
-
- len = strlen (desc->cmd);
- if (desc->cmd[0] == '.')
- len--;
-
- if (cmd_width < len)
- cmd_width = len;
- }
+ str_width = 0;
+ for (i = 0 ; i < vector_length(item_v) ; ++i)
+ {
+ cmd_item item ;
+ uint len ;
- /* Set width of description string. */
- desc_width = vio->width - (cmd_width + 6);
+ item = vector_get_item(item_v, i) ;
- /* Print out description. */
- desc_cr = NULL ; /* put <cr> last if it appears */
+ len = strlen(item->str) ;
+ if (item->str[0] == '.')
+ len--;
- for (i = 0; i < vector_active (describe); i++)
- if ((desc = vector_slot (describe, i)) != NULL)
- {
- if (desc->cmd[0] == '\0')
- continue;
+ if (len > str_width)
+ str_width = len ;
+ } ;
- if (strcmp (desc->cmd, command_cr) == 0)
- {
- desc_cr = desc;
- continue;
- }
+ /* 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) ;
- uty_cli_describe_fold (vio, cmd_width, desc_width, desc);
- }
+ if (width > ((str_width + 6) + 20))
+ doc_width = width - (str_width + 6) ;
+ else
+ doc_width = 0 ;
- if (desc_cr != NULL)
- uty_cli_describe_fold (vio, cmd_width, desc_width, desc_cr);
-} ;
+ /* Print out description. */
+ for (i = 0 ; i < vector_length(item_v) ; ++i)
+ {
+ cmd_item item ;
+ const char* str, * dp, * ep ;
-/*------------------------------------------------------------------------------
- * Show one word and the description, folding the description as required.
- */
-static void
-uty_cli_describe_fold (vty_io vio, int cmd_width,
- unsigned int desc_width, struct desc *desc)
-{
- char *buf;
- const char *cmd, *p;
- int pos;
+ item = vector_get_item(item_v, i) ;
- VTY_ASSERT_LOCKED() ;
+ str = item->str[0] == '.' ? item->str + 1 : item->str;
+ dp = item->doc ;
+ ep = dp + strlen(dp) ;
- cmd = desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd;
- p = desc->str ;
+ /* If have a sensible description width */
+ if (doc_width > 20)
+ {
+ while ((ep - dp) > doc_width)
+ {
+ const char* np ;
- /* If have a sensible description width */
- if (desc_width > 20)
- {
- buf = XCALLOC (MTYPE_TMP, strlen (desc->str) + 1);
+ np = dp + doc_width ; /* target next position */
- while (strlen (p) > desc_width)
- {
- /* move back to first space */
- for (pos = desc_width; pos > 0; pos--)
- if (*(p + pos) == ' ')
- break;
+ while ((np > dp) && (*np != ' '))
+ --np ; /* seek back to ' ' */
- /* if did not find a space, break at width */
- if (pos == 0)
- pos = desc_width ;
+ if (np == dp) /* if no space... */
+ np = dp + doc_width ; /* ...force break */
- strncpy (buf, p, pos);
- buf[pos] = '\0';
- uty_cli_describe_line(vio, cmd_width, cmd, buf) ;
+ uty_cli_describe_line(cli, str_width, str, dp, np - dp) ;
- cmd = ""; /* for 2nd and subsequent lines */
+ str = ""; /* for 2nd and subsequent lines */
- p += pos ; /* step past what just wrote */
- while (*p == ' ')
- ++p ; /* skip spaces */
+ dp = np ; /* step past what just wrote */
+ while (*dp == ' ')
+ ++dp ; /* skip spaces */
+ } ;
} ;
- XFREE (MTYPE_TMP, buf);
+ uty_cli_describe_line(cli, str_width, str, dp, ep - dp) ;
} ;
- uty_cli_describe_line(vio, cmd_width, cmd, p) ;
+ uty_cli_help_newline(cli) ;
} ;
/*------------------------------------------------------------------------------
* Show one description line.
*/
static void
-uty_cli_describe_line(vty_io vio, int cmd_width, const char* cmd,
- const char* str)
-{
- if (str != NULL)
- uty_cli_out (vio, " %-*s %s", cmd_width, cmd, str) ;
- else
- uty_cli_out (vio, " %-s", cmd) ;
- uty_cli_out_newline(vio) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Prepare "vline" token array for command handler.
- *
- * For "help" (command completion/description), if the command line is empty,
- * or ends in ' ', adds an empty token to the end of the token array.
- */
-static vector
-uty_cli_cmd_prepare(vty_io vio, int help)
+uty_cli_describe_line(vty_cli cli, uint str_width, const char* str,
+ const char* doc, uint len)
{
- vector vline ;
+ if ((*str == '\0') && (len == 0))
+ return ; /* quit if nothing to say */
- vline = cmd_make_strvec(qs_term(&vio->cl)) ;
+ uty_cli_help_newline(cli) ;
- /* Note that if there is a vector of tokens, then there is at least one
- * token, so can guarantee that vio->cl.len >= 1 !
- */
- if (help)
- if ((vline == NULL) || isspace(*qs_chars_at(&vio->cl, vio->cl.len - 1)))
- vline = cmd_add_to_strvec(vline, "") ;
-
- return vline ;
-} ;
-
-/*==============================================================================
- * VTY telnet stuff
- */
-
-#define TELNET_OPTION_DEBUG 0 /* 0 to turn off */
-
-static const char* telnet_commands[256] =
-{
- [tn_IAC ] = "IAC",
- [tn_DONT ] = "DONT",
- [tn_DO ] = "DO",
- [tn_WONT ] = "WONT",
- [tn_WILL ] = "WILL",
- [tn_SB ] = "SB",
- [tn_GA ] = "GA",
- [tn_EL ] = "EL",
- [tn_EC ] = "EC",
- [tn_AYT ] = "AYT",
- [tn_AO ] = "AO",
- [tn_IP ] = "IP",
- [tn_BREAK] = "BREAK",
- [tn_DM ] = "DM",
- [tn_NOP ] = "NOP",
- [tn_SE ] = "SE",
- [tn_EOR ] = "EOR",
- [tn_ABORT] = "ABORT",
- [tn_SUSP ] = "SUSP",
- [tn_EOF ] = "EOF",
-} ;
-
-static const char* telnet_options[256] =
-{
- [to_BINARY] = "BINARY", /* 8-bit data path */
- [to_ECHO] = "ECHO", /* echo */
- [to_RCP] = "RCP", /* prepare to reconnect */
- [to_SGA] = "SGA", /* suppress go ahead */
- [to_NAMS] = "NAMS", /* approximate message size */
- [to_STATUS] = "STATUS", /* give status */
- [to_TM] = "TM", /* timing mark */
- [to_RCTE] = "RCTE", /* remote controlled tx and echo */
- [to_NAOL] = "NAOL", /* neg. about output line width */
- [to_NAOP] = "NAOP", /* neg. about output page size */
- [to_NAOCRD] = "NAOCRD", /* neg. about CR disposition */
- [to_NAOHTS] = "NAOHTS", /* neg. about horizontal tabstops */
- [to_NAOHTD] = "NAOHTD", /* neg. about horizontal tab disp. */
- [to_NAOFFD] = "NAOFFD", /* neg. about formfeed disposition */
- [to_NAOVTS] = "NAOVTS", /* neg. about vertical tab stops */
- [to_NAOVTD] = "NAOVTD", /* neg. about vertical tab disp. */
- [to_NAOLFD] = "NAOLFD", /* neg. about output LF disposition */
- [to_XASCII] = "XASCII", /* extended ascii character set */
- [to_LOGOUT] = "LOGOUT", /* force logout */
- [to_BM] = "BM", /* byte macro */
- [to_DET] = "DET", /* data entry terminal */
- [to_SUPDUP] = "SUPDUP", /* supdup protocol */
- [to_SUPDUPOUTPUT] = "SUPDUPOUTPUT",/* supdup output */
- [to_SNDLOC] = "SNDLOC", /* send location */
- [to_TTYPE] = "TTYPE", /* terminal type */
- [to_EOR] = "EOR", /* end or record */
- [to_TUID] = "TUID", /* TACACS user identification */
- [to_OUTMRK] = "OUTMRK", /* output marking */
- [to_TTYLOC] = "TTYLOC", /* terminal location number */
- [to_3270REGIME] = "3270REGIME", /* 3270 regime */
- [to_X3PAD] = "X3PAD", /* X.3 PAD */
- [to_NAWS] = "NAWS", /* window size */
- [to_TSPEED] = "TSPEED", /* terminal speed */
- [to_LFLOW] = "LFLOW", /* remote flow control */
- [to_LINEMODE] = "LINEMODE", /* Linemode option */
- [to_XDISPLOC] = "XDISPLOC", /* X Display Location */
- [to_OLD_ENVIRON] = "OLD_ENVIRON", /* Old - Environment variables */
- [to_AUTHENTICATION] = "AUTHENTICATION", /* Authenticate */
- [to_ENCRYPT] = "ENCRYPT", /* Encryption option */
- [to_NEW_ENVIRON] = "NEW_ENVIRON", /* New - Environment variables */
- [to_EXOPL] = "EXOPL", /* extended-options-list */
-} ;
-
-/*------------------------------------------------------------------------------
- * For debug. Put string or value as decimal.
- */
-static void
-uty_cli_out_dec(vty_io vio, const char* str, unsigned char u)
-{
- if (str != NULL)
- uty_cli_out(vio, "%s ", str) ;
+ if (len == 0)
+ uty_cli_out(cli, " %s", str) ; /* left justify */
else
- uty_cli_out(vio, "%d ", (int)u) ;
+ {
+ uty_cli_out(cli, " %-*s ", str_width, str) ;
+ uty_cli_write(cli, doc, len) ;
+ } ;
} ;
/*------------------------------------------------------------------------------
- * For debug. Put string or value as hex.
+ * Return the actual or assumed console width.
+ *
+ * If we know the width we use it. Otherwise just assume something reasonable.
*/
-static void
-uty_cli_out_hex(vty_io vio, const char* str, unsigned char u)
+static uint
+uty_cli_width_to_use(vty_cli cli)
{
- if (str != NULL)
- uty_cli_out(vio, "%s ", str) ;
- else
- uty_cli_out(vio, "0x%02x ", (unsigned)u) ;
+ return (cli->width == 0) ? 60 : cli->width ;
} ;
/*------------------------------------------------------------------------------
- * Send telnet: "WILL TELOPT_ECHO"
- */
-static void
-uty_will_echo (vty_io vio)
-{
- unsigned char cmd[] = { tn_IAC, tn_WILL, to_ECHO };
- VTY_ASSERT_LOCKED() ;
- uty_cli_write (vio, (char*)cmd, (int)sizeof(cmd));
-}
-
-/*------------------------------------------------------------------------------
- * Send telnet: "suppress Go-Ahead"
- */
-static void
-uty_will_suppress_go_ahead (vty_io vio)
-{
- unsigned char cmd[] = { tn_IAC, tn_WILL, to_SGA };
- VTY_ASSERT_LOCKED() ;
- uty_cli_write (vio, (char*)cmd, (int)sizeof(cmd));
-}
-
-/*------------------------------------------------------------------------------
- * Send telnet: "don't use linemode"
+ * Move to new line, issue message and leave on new line.
+ *
+ * Deals with udating the command line if we are currently on it.
*/
static void
-uty_dont_linemode (vty_io vio)
+uty_cli_help_message(vty_cli cli, const char* msg)
{
- unsigned char cmd[] = { tn_IAC, tn_DONT, to_LINEMODE };
- VTY_ASSERT_LOCKED() ;
- uty_cli_write (vio, (char*)cmd, (int)sizeof(cmd));
-}
+ uty_cli_help_newline(cli) ; /* clears cli->drawn etc. */
+ uty_cli_write_s(cli, "% ") ;
+ uty_cli_write_s(cli, msg) ;
+ uty_cli_write(cli, telnet_newline, 2) ;
+} ;
/*------------------------------------------------------------------------------
- * Send telnet: "Use window size"
+ * If the command line is drawn, make sure it is up to date, leaving cursor
+ * at the end of the line, and then issue newline.
+ *
+ * Clears cli->drawn and cli->dirty.
*/
static void
-uty_do_window_size (vty_io vio)
+uty_cli_help_newline(vty_cli cli)
{
- unsigned char cmd[] = { tn_IAC, tn_DO, to_NAWS };
- VTY_ASSERT_LOCKED() ;
- uty_cli_write (vio, (char*)cmd, (int)sizeof(cmd));
-}
+ if (cli->drawn)
+ uty_cli_update_line(cli, qs_len_nn(cli->cl)) ;
-/*------------------------------------------------------------------------------
- * 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));
-}
+ uty_cli_write(cli, telnet_newline, 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) ;
+ cli->drawn = false ;
+ cli->dirty = false ;
} ;
/*------------------------------------------------------------------------------
- * Process incoming Telnet Option(s)
+ * If the command line help has "undrawn" the command line, then redraw it now
+ * and make a new copy to cli->cls.
*
- * 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.
+ * Sets cli->drawn
*/
-static bool
-uty_telnet_command(vty_io vio, keystroke stroke, bool callback)
+static void
+uty_cli_help_finish(vty_cli cli)
{
- uint8_t* p ;
- uint8_t o ;
- int left ;
- bool dealt_with ;
-
- /* Echo to the other end if required */
- if (TELNET_OPTION_DEBUG)
+ if (!cli->drawn)
{
- uty_cli_wipe(vio, 0) ;
-
- p = stroke->buf ;
- left = stroke->len ;
-
- uty_cli_out_hex(vio, telnet_commands[tn_IAC], tn_IAC) ;
-
- if (left-- > 0)
- uty_cli_out_dec(vio, telnet_commands[*p], *p) ;
- ++p ;
-
- if (left-- > 0)
- uty_cli_out_dec(vio, telnet_options[*p], *p) ;
- ++p ;
-
- if (left > 0)
- {
- while(left-- > 0)
- uty_cli_out_hex(vio, NULL, *p++) ;
-
- if (stroke->flags & kf_truncated)
- uty_cli_out(vio, "... ") ;
-
- if (!(stroke->flags & kf_broken))
- {
- uty_cli_out_hex(vio, telnet_commands[tn_IAC], tn_IAC) ;
- uty_cli_out_hex(vio, telnet_commands[tn_SE], tn_SE) ;
- }
- } ;
-
- if (stroke->flags & kf_broken)
- uty_cli_out (vio, "BROKEN") ;
-
- uty_cli_out (vio, "\r\n") ;
+ uty_cli_draw(cli) ;
+ qs_copy(cli->cls, cli->cl) ;
} ;
-
- /* Process the telnet command */
- dealt_with = false ;
-
- if (stroke->flags != 0)
- return dealt_with ; /* go no further if broken */
-
- p = stroke->buf ;
- left = stroke->len ;
-
- passert(left >= 1) ; /* must be if not broken ! */
- passert(stroke->value == *p) ; /* or something is wrong */
-
- ++p ; /* step past X of IAC X */
- --left ;
-
- /* Decode the one command that is interesting -- "NAWS" */
- switch (stroke->value)
- {
- case tn_SB:
- passert(left > 0) ; /* or parser failed */
-
- o = *p++ ; /* the option byte */
- --left ;
- switch(o)
- {
- case to_NAWS:
- if (left != 4)
- {
- uzlog(NULL, LOG_WARNING,
- "RFC 1073 violation detected: telnet NAWS option "
- "should send %d characters, but we received %d",
- (3 + 4 + 2), (3 + left + 2)) ;
- }
- else
- {
- vio->width = *p++ << 8 ;
- vio->width += *p++ ;
- vio->height = *p++ << 8 ;
- vio->height += *p ;
-
- if (TELNET_OPTION_DEBUG)
- uty_cli_out(vio, "TELNET NAWS window size received: "
- "width %d, height %d%s",
- vio->width, vio->height, telnet_newline) ;
- uty_set_height(vio) ;
-
- dealt_with = true ;
- } ;
- break ;
-
- default: /* no other IAC SB <option> */
- break ;
- } ;
- break ;
-
- default: /* no other IAC X */
- break ;
- } ;
-
- return dealt_with ;
} ;
diff --git a/lib/vty_cli.h b/lib/vty_cli.h
index f532616a..78ccc900 100644
--- a/lib/vty_cli.h
+++ b/lib/vty_cli.h
@@ -24,26 +24,219 @@
#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 "qtimers.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 hist ; /* embedded */
+ uint hp ; /* current place in history */
+ uint h_now ; /* the present moment */
+ bool h_repeat ; /* latest entry is repeat */
+
+ /* 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.
+ *
+ * This flag <=> the CLI "owns" the screen. This flag
+ * must be cleared -- by wiping the command line -- before
+ * any other output can use the screen.
+ *
+ * In particular, must be cleared before setting
+ * out_active -- see below.
+ *
+ * dirty <=> the last command output did not end with a newline.
+ *
+ * tilde_enabled <=> do not do the "~ " one command line ahead.
+ *
+ * If drawn is true, the following are valid:
+ *
+ * tilde_prompt -- the prompt is the "~ "
+ *
+ * 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 ;
+
+ bool tilde_prompt ;
+ bool tilde_enabled ;
+
+ 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 prompt_for_node ;
+
+ /* State of the CLI
+ *
+ * dispatched -- command dispatched by CLI
+ * in_progress -- command taken by the command loop
+ * blocked -- blocked until current command completes
+ * paused -- command dispatched and nothing else happened
+ *
+ * mon_active -- there is stuff in the logging monitor buffer
+ *
+ * out_active -- contents of the obuf FIFO are being written away
+ * though may be blocked in more_wait
+ *
+ * This flag <=> that the command output "owns" the screen.
+ *
+ * While this flag is set, the CLI may not write to the
+ * screen.
+ *
+ * flush -- this flag => out_active.
+ *
+ * When the CLI is ready to read the next CLI command, it
+ * must wait for all command output to complete. This
+ * flag is set, so that (a) any final but incomplete
+ * line of command output will be flushed, and (b) to
+ * signal that out_active must be cleared when all output
+ * has completed.
+ *
+ * more_wait -- is in "--more--" wait state
+ * more_enter -- more_wait and waiting for "--more--" prompt to be
+ * written away and keystrokes to be consumed.
+ */
+ bool dispatched ;
+ bool in_progress ;
+ bool blocked ;
+ bool paused ;
-extern enum vty_readiness uty_cli(vty_io vio) ;
-extern keystroke_callback uty_cli_iac_callback ;
+ bool mon_active ;
+ bool out_active ;
+ bool flush ;
-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) ;
+ bool more_wait ;
+ bool more_enter ;
-extern void uty_free_host_name(void) ;
-extern void uty_check_host_name(void) ;
+ /* This is set only if the "--more--" handling is enabled */
+ bool more_enabled ;
-extern bool uty_cli_draw_if_required(vty_io vio) ;
+ /* Timer for paused state -- multi-threaded only */
+ qtimer pause_timer ;
-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) ;
+ /* Command Line(s)
+ *
+ * context -- the node etc. that the CLI is in. This may be some way behind
+ * the VTY, but is updated when the CLI level command completes.
+ *
+ * Note that this is a copy of the state of exec->context when
+ * uty_want_command() was last called.
+ *
+ * auth_context -- true <=> context->node is AUTH_NODE or AUTH_ENABLE_NODE
+ *
+ * parsed -- the parsed object used to parse command for cli help
+ *
+ * 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.
+ * cls -- current command line on the screen
+ *
+ * clx -- current command line being executed.
+ *
+ * dispatch -- the last action dispatched.
+ *
+ * NB: during command execution vty->buf is set to point at the '\0'
+ * terminated current command line being executed.
+ */
+ cmd_context context ;
+ bool auth_context ;
+
+ cmd_parsed parsed ;
+
+ cmd_do_t to_do ;
+ qstring cl ;
+ qstring cls ;
+
+ qstring clx ;
+
+ cmd_action_t dispatch ;
+
+ /* CLI line buffering and line control */
+ vio_fifo cbuf ;
+ vio_line_control olc ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Functions
+ */
+extern vty_cli uty_cli_new(vio_vf vf) ;
+extern vty_cli uty_cli_close(vty_cli cli, bool final) ;
+
+extern void uty_cli_hist_show(vty_cli cli) ;
+extern ulen uty_cli_prompt_len(vty_cli cli) ;
+
+extern vty_readiness_t uty_cli(vty_cli cli) ;
+
+extern cmd_return_code_t uty_cli_want_command(vty_cli cli, cmd_action action,
+ cmd_context context) ;
+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_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_cli cli) ;
+
+extern void uty_cli_pre_monitor(vty_cli cli) ;
+extern void uty_cli_post_monitor(vty_cli cli) ;
+
+/*------------------------------------------------------------------------------
+ * Pro tem -- "\r\n" string
+ */
+extern const char* uty_cli_newline ;
#endif /* _ZEBRA_VTY_CLI_H */
diff --git a/lib/vty_command.c b/lib/vty_command.c
new file mode 100644
index 00000000..9aebc50f
--- /dev/null
+++ b/lib/vty_command.c
@@ -0,0 +1,1756 @@
+/* VTY for command execution
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * Revisions: Copyright (C) 2010 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * 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.
+ */
+
+#include "misc.h"
+#include "stdio.h"
+
+#include "vty_command.h"
+#include "command_local.h"
+#include "command_queue.h"
+#include "command_execute.h"
+#include "command_parse.h"
+#include "vty_local.h"
+#include "vty_io.h"
+#include "vty_cli.h"
+#include "vio_fifo.h"
+#include "vty_io_file.h"
+#include "vty_io_term.h"
+#include "list_util.h"
+#include "qstring.h"
+
+/*==============================================================================
+ * Variables etc.
+ */
+
+/*------------------------------------------------------------------------------
+ * Prototypes
+ */
+
+static void uty_cmd_loop_prepare(vty_io vio) ;
+static cmd_return_code_t uty_cmd_hiatus(vty_io vio, cmd_return_code_t ret) ;
+static cmd_return_code_t vty_cmd_auth(vty vty, node_type_t* p_next_node) ;
+static uint uty_show_error_context(vio_fifo ebuf, vio_vf vf) ;
+static uint uty_cmd_failed(vty_io vio, cmd_return_code_t ret) ;
+static void uty_cmd_prepare(vty_io vio) ;
+static void uty_cmd_config_lock(vty vty) ;
+static void uty_cmd_config_lock_check(struct vty *vty, node_type_t node) ;
+
+/*==============================================================================
+ * There are two command loops -- cmd_read_config() and cq_process(). These
+ * functions support those command loops: TODO update !!
+ *
+ * * uty_cmd_prepare()
+ *
+ * After opening or closing a vin/vout object, the state of the execution
+ * context must be set to reflect the current top of the vin/vout stack.
+ *
+ * * uty_cmd_dispatch_line()
+ *
+ * This is for command lines which come from "push" sources, eg the
+ * Telnet VTY CLI or the VTYSH.
+ *
+ * Hands command line over to the vty_cli_nexus message queue.
+ *
+ * NB: from this point onwards, the vio is vio->cmd_running ! TODO
+ *
+ * * vty_cmd_fetch_line()
+ *
+ * This is for command lines which are "pulled" by one of the command
+ * execution loops, eg files and pipes. If not in cmd_read_config())
+ * can return CMD_WAITING.
+ *
+ * * vty_cmd_open_in_pipe_file()
+ * * vty_cmd_open_in_pipe_shell()
+ * * vty_cmd_open_out_pipe_file()
+ * * vty_cmd_open_out_pipe_shell()
+ *
+ * These do the I/O side of the required opens, pushing the new vty_vf
+ * onto the vin/vout stack.
+ *
+ * * vty_cmd_success()
+ *
+ * When a command returns success, this is called either to push all
+ * buffered output to the current vout, or to discard all buffered
+ * output (which is what cmd_read_config() does).
+ *
+ * If required, pops any vout(s).
+ *
+ *
+ */
+
+/*------------------------------------------------------------------------------
+ * Prepare to enter the config read command loop.
+ *
+ * Initialise exec object, and copy required settings from the current vin
+ * and vout.
+ */
+extern void
+vty_cmd_loop_prepare(vty vty)
+{
+ VTY_LOCK() ;
+
+ assert(vty->type == VTY_CONFIG_READ) ;
+
+ uty_cmd_loop_prepare(vty->vio) ; /* by vty->type & vty->node */
+ VTY_UNLOCK() ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Enter the command_queue command loop.
+ */
+extern void
+uty_cmd_loop_enter(vty_io vio)
+{
+ VTY_ASSERT_CLI_THREAD_LOCKED() ;
+
+ assert(vio->vty->type == VTY_TERMINAL) ;
+
+ uty_cmd_loop_prepare(vio) ; /* by vty->type & vty->node */
+
+ cq_loop_enter(vio->vty, vio->vty->node != NULL_NODE ? CMD_SUCCESS
+ : CMD_CLOSE) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Prepare to enter a command loop.
+ *
+ * Initialise cmd_exec object, and its cmd_context -- given vty->type and
+ * vty->node.
+ */
+static void
+uty_cmd_loop_prepare(vty_io vio)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ assert(vio->vty->exec == NULL) ;
+ assert(vio->state == vc_null) ;
+
+ vio->vty->exec = cmd_exec_new(vio->vty) ;
+ vio->state = vc_running ;
+
+ if (vio->vty->node > MAX_NON_CONFIG_NODE)
+ uty_cmd_config_lock(vio->vty) ; /* TODO cannot fail !? */
+
+ uty_cmd_prepare(vio) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Signal to the command loop that some I/O has completed -- successfully, or
+ * with some I/O error (including time out).
+ *
+ * If the vio is in vc_waiting state -- so will be exec_hiatus -- send message
+ * so that command loop continues.
+ *
+ * Otherwise, if is vc_running and have CMD_IO_ERROR, set the vc_io_error_trap,
+ * so that the next interaction with the vty (other than output) will signal
+ * a pending error.
+ *
+ * Accepts:
+ *
+ * CMD_SUCCESS -- if vc_waiting, passed in.
+ * otherwise, ignored
+ *
+ * CMD_WAITING -- ignored
+ *
+ * CMD_IO_ERROR -- if vc_waiting, passed in
+ * if vc_running, set vc_io_error_trap
+ *
+ * CMD_CLOSE -- if vc_waiting, passed in
+ * if vc_running, set vc_close_trap
+ * if vc_io_error_trap, set vc_close_trap
+ */
+extern void
+uty_cmd_signal(vty_io vio, cmd_return_code_t ret)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if (ret == CMD_WAITING)
+ return ;
+
+ assert((ret == CMD_SUCCESS) || (ret == CMD_IO_ERROR) || (ret == CMD_CLOSE)) ;
+
+ switch (vio->state)
+ {
+ case vc_null:
+ zabort("invalid vc_null") ;
+ break ;
+
+ case vc_running: /* ignore CMD_SUCCESS */
+ if (ret == CMD_IO_ERROR)
+ vio->state = vc_io_error_trap ;
+ if (ret == CMD_CLOSE)
+ vio->state = vc_close_trap ;
+ break ;
+
+ case vc_waiting: /* pass in the return code continue */
+ vio->state = vc_running ;
+ cq_continue(vio->vty, ret) ;
+ break ;
+
+ case vc_io_error_trap: /* ignore CMD_SUCCESS or duplicate */
+ if (ret == CMD_CLOSE) /* CMD_IO_ERROR. */
+ vio->state = vc_close_trap ;
+ break ;
+
+ case vc_close_trap: /* ignore CMD_SUCCESS, CMD_IO_ERROR, */
+ case vc_stopped: /* and duplicate/too late CMD_CLOSE. */
+ break ;
+
+ case vc_closed:
+ zabort("invalid vc_closed") ;
+ break ;
+
+ default:
+ zabort("unknown vio->state") ;
+ break ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Reset the command loop.
+ *
+ * This is used by uty_close() to try to shut down the command loop.
+ *
+ * Does nothing if already closed or never got going.
+ *
+ * "curtains" means that the program is being terminated, so no message or
+ * event handling is running any more, and all threads other than the main
+ * thread have stopped. This means that whatever the state of the command
+ * loop, we can terminate it now. Revokes any outstanding messages/events,
+ * in order to tidy up.
+ *
+ * If not curtains, then push a CMD_CLOSE into the command loop, to bring it
+ * to a shuddering halt.
+ *
+ * Will leave the vio->state:
+ *
+ * vc_running -- a CMD_CLOSE has been passed in (was vc_waiting).
+ * Will not be the case if "curtains".
+ *
+ * vc_close_trap -- command loop is running, but will stop as soon as it
+ * sees this trap.
+ * Will not be the case if "curtains".
+ *
+ * vc_stopped -- command loop is stopped
+ * Will be the case if "curtains" (unless vc_null or
+ * vc_closed).
+ * Otherwise will only be the case if the loop had already
+ * stopped.
+ *
+ * vc_null -- never been kissed
+ * vc_closed -- was already closed
+ *
+ * No other states are possible.
+ */
+extern void
+uty_cmd_loop_close(vty_io vio, bool curtains)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if ((vio->state == vc_null) || (vio->state == vc_closed))
+ return ;
+
+ if (curtains)
+ {
+ if (!vio->blocking)
+ cq_revoke(vio->vty) ; /* collect any outstanding message */
+
+ vio->state = vc_stopped ;
+ }
+ else
+ uty_cmd_signal(vio, CMD_CLOSE) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Exit command loop.
+ *
+ * Final close of the VTY, giving a reason, if required.
+ */
+extern void
+vty_cmd_loop_exit(vty vty)
+{
+ VTY_LOCK() ;
+
+ VTY_ASSERT_CAN_CLOSE(vty) ;
+
+ /* Make sure no longer holding the config symbol of power */
+ uty_cmd_config_lock_check(vty, NULL_NODE) ;
+
+ /* Can now close the vty */
+ uty_close(vty->vio, NULL, false) ; /* not curtains */
+
+ VTY_UNLOCK() ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Handle a "special" command -- anything not cmd_do_command.
+ *
+ * These "commands" are related to VTY_TERMINAL CLI only.
+ */
+extern cmd_return_code_t
+vty_cmd_special(vty vty)
+{
+ cmd_return_code_t ret ;
+ cmd_do_t to_do ;
+ node_type_t next_node ;
+
+ ret = CMD_SUCCESS ;
+
+ to_do = vty->exec->action->to_do ;
+
+ /* Note that the next node handling is special here... we establish
+ * the next node explicitly here -- there is no parse operation to preset
+ * what CMD_SUCCESS next node will be.
+ */
+
+ vty->node = vty->exec->context->node ; /* as per all commands */
+ next_node = vty->exec->context->node ; /* by default. */
+
+ switch (to_do & cmd_do_mask)
+ {
+ case cmd_do_nothing:
+ break ;
+
+ case cmd_do_eof:
+ if (vty->type == VTY_TERMINAL)
+ vty_out(vty, "%% Terminal closed\n") ;
+
+ ret = CMD_CLOSE ;
+ break ;
+
+ case cmd_do_timed_out:
+ if (vty->type == VTY_TERMINAL)
+ vty_out(vty, "%% Terminal timed out\n") ;
+
+ ret = CMD_CLOSE ;
+ break ;
+
+ case cmd_do_command:
+ if ((to_do & cmd_do_auth) != 0)
+ ret = vty_cmd_auth(vty, &next_node) ;
+ else
+ zabort("invalid cmd_do_command") ;
+ break ;
+
+ case cmd_do_ctrl_d:
+ if ((to_do & cmd_do_auth) != 0)
+ next_node = cmd_node_exit_to(vty->node) ;
+ else
+ zabort("invalid cmd_do_ctrl_d") ;
+ break ;
+
+ case cmd_do_ctrl_c:
+ case cmd_do_ctrl_z:
+ next_node = cmd_node_end_to(vty->node) ;
+ break ;
+
+ default:
+ zabort("unknown or invalid cmd_do") ;
+ } ;
+
+ /* Now worry about changing node */
+ if (ret == CMD_CLOSE)
+ next_node = NULL_NODE ;
+ else if (next_node == NULL_NODE)
+ ret = CMD_CLOSE ;
+
+ if (next_node != vty->exec->context->node)
+ {
+ vty->exec->context->node = next_node ;
+ vty_cmd_config_lock_check(vty, next_node) ;
+ } ;
+ return ret ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Check that can enter AUTH_ENABLE_NODE.
+ *
+ * Must be: VTY_TERMINAL
+ *
+ * and: no pipes, in or out -- so talking directly to terminal
+ *
+ * and: be VIEW_NODE if there is no enable password.
+ *
+ * Note that "can_enable" <=> vin_depth == 1 and VTY_TERMINAL (or other VTY
+ * that can authenticate). But it may not => vout_depth == 0.
+ */
+extern cmd_return_code_t
+vty_cmd_can_auth_enable(vty vty)
+{
+ cmd_return_code_t ret ;
+
+ VTY_LOCK() ;
+
+ assert(vty->exec->parsed->nnode == AUTH_ENABLE_NODE) ;
+ assert((vty->exec->context->onode == VIEW_NODE) ||
+ (vty->exec->context->onode == RESTRICTED_NODE)) ;
+
+ ret = CMD_WARNING ;
+
+ if (vty->type != VTY_TERMINAL)
+ uty_out(vty->vio, "%% Wrong VTY type (%d) for 'enable'", vty->type) ;
+ else if ((vty->exec->context->onode != VIEW_NODE)
+ && (host.enable == NULL))
+ uty_out(vty->vio, "%% cannot enable because there is no enable password") ;
+ else if ((vty->vio->vin_depth != 1) || (vty->vio->vout_depth != 1))
+ uty_out(vty->vio,
+ "%% cannot authenticate for 'enable' in a pipe command") ;
+ else
+ {
+ assert(vty->exec->context->can_auth_enable) ;
+ ret = CMD_SUCCESS ;
+ } ;
+
+ VTY_UNLOCK() ;
+
+ return ret ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Authentication of vty
+ *
+ * Note that if the AUTH_NODE password fails too many times, the vty is
+ * closed.
+ *
+ * Quagga authentication is a touch complicated. The following appear to be
+ * the rules:
+ *
+ * 1. host.no_password_check -- set by "no login" command
+ *
+ * Means that a VTY_TERMINAL can start without going through any
+ * password check -- including when no host.password is configured.
+ *
+ * Note that this does not affect the authentication for enable, except
+ * at startup of a VTY_TERMINAL...
+ *
+ * When a VTY_TERMINAL starts:
+ *
+ * * if host.restricted_mode -> RESTRICTED_NODE
+ *
+ * * else if host.advanced -> ENABLE_NODE
+ *
+ * This is whether or not an enable password exists.
+ *
+ * * otherwise -> VIEW_NODE
+ *
+ * So being in RESTRICTED_NODE <=> was host.no_password_check &&
+ * host.restricted_mode when the VTY_TERMINAL was started.
+ *
+ * 2. host.restricted_mode -- set by "anonymous restricted"
+ *
+ * Significant only at VTY_TERMINAL start, and only if no_password_check.
+ *
+ * NB: if the enable password is NULL, there is not much point in
+ * RESTRICTED_NODE, since ENABLE_NODE is but one command away.
+ *
+ * NB: that behaviour is is modified here... if is in RESTRICTED_MODE,
+ * will not authenticate AUTH_ENABLE_NODE if there is no enable
+ * password.
+ *
+ * Note that the check is left to this point, so that is completed
+ * atomically. Elsewhere, will refuse to enter ENABLE_NODE from
+ * RESTRICTED_NODE if no enable password. By the time we get here
+ * it is (just) possible that the situation has changed.
+ *
+ * 3. host.advanced -- set by "service advanced-vty"
+ *
+ * Significant iff there is no enable password, when it sets ENABLE_NODE
+ * as the start up node (if no_password_check) or post AUTH_NODE node.
+ *
+ * 4. host.password -- set by "password xxx"
+ *
+ * Unless no_password_check, if there is no password, you cannot start
+ * a vty.
+ *
+ * 5. host.enable -- set by "enable password xxx"
+ *
+ * If this is set, then must authenticate against it for vty to reach
+ * ENABLE_NODE.
+ *
+ * If it is not set, then can enter ENABLE_NODE at any time.
+ *
+ * If AUTH_ENABLE_NODE fails, falls back to the node we came from -- which has
+ * been planted in the context for this purpose. (If host.restricted_mode has
+ * changed since the vty started, could argue this should change where should
+ * fall back to... but that seems unnecessarily complicated.)
+ *
+ * Returns: CMD_SUCCESS -- OK, one way or another
+ * CMD_WARNING -- with error message sent to output
+ * CMD_CLOSE -- too many password failures
+ */
+static cmd_return_code_t
+vty_cmd_auth(vty vty, node_type_t* p_next_node)
+{
+ char *crypt (const char *, const char *);
+
+ char* passwd = NULL ;
+ bool encrypted = false ;
+ bool enable = false ;
+ bool advanced ;
+ bool pass ;
+ cmd_return_code_t ret ;
+ cmd_exec exec ;
+ cmd_context context ;
+
+ exec = vty->exec ;
+ context = exec->context ;
+
+ /* Select the password we need to check against. */
+ passwd = NULL ;
+ encrypted = false ;
+ enable = false ;
+ advanced = false ;
+
+ pass = false ;
+
+ VTY_LOCK() ; /* while access host.xxx */
+
+ switch (vty->node)
+ {
+ case AUTH_NODE:
+ passwd = host.password ;
+ encrypted = host.password_encrypted ;
+ enable = false ;
+
+ context->onode = NULL_NODE ; /* started from nowhere */
+
+ if (host.advanced && (host.enable == NULL))
+ {
+ context->tnode = ENABLE_NODE ;
+ advanced = true ;
+ }
+ else
+ {
+ context->tnode = VIEW_NODE ;
+ advanced = false ;
+ } ;
+ break ;
+
+ case AUTH_ENABLE_NODE:
+ passwd = host.enable ;
+ encrypted = host.enable_encrypted ;
+ enable = true ;
+ advanced = false ;
+
+ assert((context->onode == VIEW_NODE) ||
+ (context->onode == RESTRICTED_NODE)) ;
+ break ;
+
+ default:
+ zabort("unknown vty->node") ;
+ break ;
+ } ;
+
+ VTY_UNLOCK() ;
+
+ /* Check against selected password (if any) */
+ if (passwd == NULL)
+ {
+ /* Here we reject any attempt to AUTH_NODE against an empty password.
+ *
+ * Otherwise, is dealing with the (largely) theoretical case of
+ * This fails any attempt to AUTH_ENABLE against an empty password
+ * if was in RESTRICTED_NODE.
+ *
+ * This passes the theoretically possible case of enable in VIEW_NODE,
+ * when there was an enable password set when the enable command was
+ * executed, but it has since been unset !
+ */
+ pass = context->onode == VIEW_NODE ;
+ }
+ else
+ {
+ char* candidate = qs_make_string(exec->action->line) ;
+
+ if (encrypted)
+ candidate = crypt(candidate, passwd) ;
+
+ pass = (strcmp(candidate, passwd) == 0) ;
+ } ;
+
+ /* Now worry about the result */
+ ret = CMD_SUCCESS ; /* so far, so good */
+
+ if (pass)
+ {
+ *p_next_node = context->tnode ;
+
+ if (enable || advanced)
+ context->can_enable = true ;
+
+ if (*p_next_node == CONFIG_NODE)
+ {
+ ret = vty_cmd_config_lock(vty) ;
+ if (ret == CMD_WARNING)
+ *p_next_node = ENABLE_NODE ;
+ } ;
+
+ exec->password_failures = 0 ; /* forgive any failures */
+ }
+ else
+ {
+ bool no_more = false ;
+
+ if (passwd == NULL)
+ {
+ /* Cannot possibly authenticate ! */
+ no_more = true ;
+ vty_out(vty, "%% No password is set, cannot authenticate!\n") ;
+ }
+ else
+ {
+ exec->password_failures++ ;
+
+ if (exec->password_failures >= 3)
+ {
+ no_more = true ;
+ vty_out(vty, "%% Bad passwords, too many failures!\n") ;
+
+ exec->password_failures = 0 ; /* allow further attempts */
+ } ;
+ } ;
+
+ if (no_more)
+ {
+ if (!enable)
+ {
+ *p_next_node = NULL_NODE ;
+ ret = CMD_CLOSE ;
+ }
+ else
+ {
+ *p_next_node = context->onode ;
+ ret = CMD_WARNING ;
+ } ;
+ } ;
+ } ;
+
+ return ret ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Fetch the next command line to be executed.
+ *
+ * Returns: CMD_SUCCESS => OK -- have a line ready to be processed.
+ *
+ * vty->exec->line points at the line
+ * vty->exec->to_do says what to do with it
+ *
+ * or: CMD_WAITING => OK -- but waiting for command line to arrive
+ * <=> non-blocking
+ *
+ * or: CMD_EOF => OK -- but nothing to fetch from the current vin
+ *
+ * Need to close the current vin and pop vin/vout
+ * as necessary.
+ *
+ * or: CMD_HIATUS => OK -- but need to close one or more vin/vout
+ * to adjust stack.
+ *
+ * or: any other return code from the current vin when it attempts to
+ * fetch another command line -- including I/O error or timeout.
+ *
+ * NB: can be called from any thread -- because does no closing of files or
+ * anything other than read/write.
+ *
+ * TODO -- dealing with states other than vf_open !!
+ */
+extern cmd_return_code_t
+vty_cmd_fetch_line(vty vty)
+{
+ cmd_return_code_t ret ;
+ vty_io vio ;
+ vio_vf vf ;
+ cmd_exec exec ;
+
+ VTY_LOCK() ;
+
+ vio = vty->vio ; /* once locked */
+ exec = vty->exec ;
+
+ cmd_action_clear(exec->action) ; /* tidy */
+
+ vf = vio->vin ;
+
+ ret = CMD_SUCCESS ; /* assume all is well */
+
+ /* Worry about all the things that stop us from being able to fetch the
+ * next command line.
+ */
+ if ( (vty->vio->state != vc_running)
+ || (vio->vin_depth < vio->vout->depth_mark)
+ || (vio->vin_depth > vio->real_depth) )
+ ret = CMD_HIATUS ;
+ else
+ {
+ switch (vf->vin_state)
+ {
+ case vf_open:
+ case vf_eof:
+ case vf_timed_out:
+ switch (vf->vin_type)
+ {
+ case VIN_NONE:
+ zabort("invalid VIN_NONE") ;
+ break ;
+
+ case VIN_TERM:
+ ret = uty_term_fetch_command_line(vf, exec->action,
+ exec->context) ;
+ break ;
+
+ case VIN_VTYSH:
+ zabort("invalid VIN_VTYSH") ;
+ break ;
+
+ case VIN_DEV_NULL:
+ ret = CMD_EOF ;
+ break ;
+
+ case VIN_FILE:
+ case VIN_CONFIG:
+ ret = uty_file_fetch_command_line(vf, exec->action) ;
+ break ;
+
+ case VIN_PIPE:
+ ret = uty_pipe_fetch_command_line(vf, exec->action) ;
+ break ;
+
+ default:
+ zabort("unknown vin_type") ;
+ } ;
+ break ;
+
+ case vf_closed: /* TODO treat closed as EOF ? */
+ ret = CMD_EOF ;
+ break ;
+
+ case vf_error:
+ ret = CMD_IO_ERROR ;
+ break ;
+
+ case vf_closing:
+ assert(!vf->blocking) ;
+ ret = CMD_WAITING ;
+ break ;
+
+ default:
+ zabort("invalid vf->vin_state") ;
+ break ;
+ } ;
+ } ;
+
+ VTY_UNLOCK() ;
+
+ return ret ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Deal with return code at the "exec_hiatus" point in the command loop.
+ *
+ * The command_queue command loop runs until something happens that it
+ * cannot immediately deal with, at which point it enters "exec_hiatus", and
+ * this function is called. The command loop will deal with CMD_SUCCESS and
+ * CMD_EMPTY, but otherwise this function must deal with:
+ *
+ * CMD_HIATUS -- something requires attention, eg:
+ *
+ * - the vout_depth > vin_depth, so the vout needs to
+ * be closed and popped.
+ *
+ * - the vio->state needs to be checked.
+ *
+ * CMD_EOF -- from vty_cmd_fetch_line() => current vin has hit eof,
+ * and must be closed and popped.
+ *
+ * CMD_CLOSE -- from a command return (or otherwise) => must close
+ * and pop the current vin (same as CMD_EOF, really).
+ *
+ * CMD_WAITING -- from vty_cmd_fetch_line() or elsewhere => must go to
+ * vc_waiting and the command loop MUST exit.
+ *
+ * CMD_SUCCESS -- see below
+ *
+ * CMD_EMPTY -- should not appear, but is treated as CMD_SUCCESS
+ *
+ * anything else -- treated as a command or I/O or other error.
+ *
+ * The handling of errors depends on the type of error:
+ *
+ * * command errors will cause all levels of the stack other than vin_base
+ * and vout_base to be closed, and a suitable error message output to the
+ * vout_base.
+ *
+ * Inputs are closed without dealing with any further input and discarding
+ * any buffered input.
+ *
+ * Pending output will be pushed out, and pipe return stuff will be sucked
+ * in and blown out, until the return signals EOF.
+ *
+ * * I/O errors will cause all levels of TODO ... depending on error location ??
+ *
+ * I/O errors also cause all closes to be "final", so pending output is
+ * attempted -- but will be abandoned if would block. Also, any further
+ * I/O errors will be discarded.
+ *
+ * This function will return:
+ *
+ * CMD_SUCCESS => OK -- can try and fetch a command line again.
+ *
+ * state == vc_running
+ *
+ * CMD_WAITING => OK -- but waiting for input to arrive or for something
+ * to be completely closed. => non-blocking
+ *
+ * state == vc_waiting
+ *
+ * CMD_EOF => OK -- but nothing more to fetch, close the vty and exit
+ * command loop.
+ * <=> the vin_base is now closed.
+ *
+ * state == vc_stopped
+ *
+ * CMD_IO_ERROR => some error has occurred while closing stuff.
+ *
+ * state == vc_io_error_trap
+ *
+ * And nothing else.
+ *
+ * The CMD_IO_ERROR is returned so that errors are not hidden inside here.
+ * At some point vty_cmd_hiatus() must be called again to deal with the
+ * error.
+ *
+ * When the command loop has gone vc_waiting, the I/O side of things can wake
+ * it up by uty_cmd_signal(), which passes in a return code. When the
+ * command loop runs it will call this function to handle the new return code.
+ * If CMD_SUCCESS is passed in, will continue trying to adjust the vin/vout
+ * stacks.
+ *
+ * The configuration reader command loop also uses vty_cmd_hiatus() to handle
+ * all return codes. However, it will exit the command loop at the slightest
+ * hint of trouble.
+ *
+ * All this TODO (after discussion of error handling)
+
+ * NB: can be called from any thread if !blocking, otherwise MUST be cli thread.
+ */
+extern cmd_return_code_t
+vty_cmd_hiatus(vty vty, cmd_return_code_t ret)
+{
+ VTY_LOCK() ;
+ VTY_ASSERT_CAN_CLOSE(vty) ;
+
+ ret = uty_cmd_hiatus(vty->vio, ret) ;
+
+ VTY_UNLOCK() ;
+ return ret ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Inside of vty_cmd_hiatus() -- can return at any time.
+ */
+static cmd_return_code_t
+uty_cmd_hiatus(vty_io vio, cmd_return_code_t ret)
+{
+ /* (0) worry about state of the vio.
+ *
+ * We expect it to generally be be vc_running or vc_waiting, otherwise:
+ *
+ * vc_io_error_trap => an I/O error has been posted asynchronously
+ *
+ * set state to vc_running and return code to
+ * the pending CMD_IO_ERROR.
+ *
+ * vc_close_trap => the vty has been reset asynchronously
+ *
+ * set state to vc_running and return code to
+ * the pending CMD_CLOSE.
+ *
+ * vc_stopped )
+ * vc_closed ) invalid -- cannot be here in this state
+ * vc_null )
+ */
+ switch (vio->state)
+ {
+ case vc_null:
+ case vc_stopped:
+ case vc_closed:
+ zabort("invalid vc_xxxx") ;
+ break ;
+
+ case vc_running:
+ case vc_waiting:
+ break ;
+
+ case vc_io_error_trap:
+ ret = CMD_IO_ERROR ; /* convert pending IO error */
+ break ;
+
+ case vc_close_trap:
+ ret = CMD_CLOSE ; /* convert pending close */
+ break ;
+
+ default:
+ zabort("unknown vio->state") ;
+ break ;
+ } ;
+
+ vio->state = vc_running ; /* running in hiatus */
+
+ /* (1) Handle the return code.
+ *
+ * Deal here with the return codes that signify success, or signify
+ * success but some vin and/or vout need to be closed.
+ *
+ * Call uty_cmd_failed() to deal with return codes that signify some
+ * sort of failure. A failure generally means closing all the way to
+ * the vin_/vout_base, or possibly completely.
+ *
+ * Note that CMD_WAITING is immediately returned !
+ */
+ switch (ret)
+ {
+ case CMD_SUCCESS:
+ case CMD_EMPTY:
+ case CMD_HIATUS:
+ break ;
+
+ case CMD_WAITING:
+ assert(!vio->blocking) ;
+ vio->state = vc_waiting ;
+ return ret ; /* <<< exit here on CMD_WAITING */
+
+ case CMD_EOF:
+ case CMD_CLOSE:
+ uty_out_accept(vio) ; /* accept any buffered remarks. */
+ assert(vio->real_depth > 0) ;
+ --vio->real_depth ;
+ break ;
+
+ default:
+ /* If not any of the above, must be an error of some kind:
+ *
+ * * set real_depth to close all pipes and files.
+ *
+ * Depending on the type of vin_base/vout_base and the type of
+ * error, may or may not leave the bas I/O open.
+ *
+ * * create error messages in the vout_base.
+ */
+ vio->real_depth = uty_cmd_failed(vio, ret) ;
+ break ;
+ } ;
+
+ ret = CMD_SUCCESS ; /* OK, so far */
+
+ /* (2) Do we need to close one or more vin, or are we waiting for one to
+ * close ?
+ *
+ * The close will immediately close the input, and discard anything
+ * which has been buffered. The only difficulty with closing inputs
+ * is VIN_PIPE, where the "return" input (from the child stderr) may
+ * not yet have finished.
+ *
+ * For blocking vio, close operations will either complete or fail.
+ *
+ * For non-blocking vio, close operations may return CMD_WAITING
+ * (eg: VIN_PIPE where the child stderr is not yet at EOF, or the
+ * child completion status has not yet been collected). Where a
+ * close operation does not complete, the vf is marked vf_closing,
+ * and the stack stays at its current level.
+ *
+ * For hard errors will do "final" close, which immediately closes the
+ * input (and any pipe return) discarding any buffered input. Any errors
+ * that occur in the process are discarded.
+ */
+ while ((vio->vin_depth > vio->real_depth) && (ret == CMD_SUCCESS))
+ ret = uty_vin_pop(vio, vio->err_hard, vio->vty->exec->context) ;
+
+ /* (3) And, do we need to close one or more vout, or are we waiting for
+ * one to close ? If so:
+ *
+ * Any output after the current end_mark is discarded. Note that we
+ * do not actually close the vout_base.
+ *
+ * In all cases we push any remaining output and collect any remaining
+ * pipe return, and collect child termination condition.
+ *
+ * For blocking vio, close operations will either complete or fail.
+ *
+ * For non-blocking vio, close operations may return CMD_WAITING
+ * (eg: where the output buffers have not yet been written away).
+ * Where a close operation does not complete, the vf is marked
+ * vf_closing, and the stack stays at its current level.
+ *
+ * For hard errors will attempt to write away anything which is,
+ * pending, but will stop if would block and on any error, and close
+ * the output -- discarding any remaining output and any errors.
+ *
+ * NB: when we reach the vout_base, turn off the hard error -- so
+ * never "final" close the vout TODO error handling.
+ *
+ * Also: if we are at, or we reach, the vout_base, and there is an error
+ * message in hand, now is the time to move that to the obuf and
+ * push it.
+ */
+ while (ret == CMD_SUCCESS)
+ {
+ assert(vio->vout->depth_mark >= vio->vout_depth) ;
+
+ if (vio->vout_depth == 1)
+ {
+ assert(vio->vout->depth_mark == 1) ;
+
+ vio->err_hard = false ;
+
+ if (vio->ebuf != NULL)
+ {
+ vio_fifo_copy(vio->obuf, vio->ebuf) ;
+ vio->ebuf = vio_fifo_free(vio->ebuf) ;
+
+ ret = uty_cmd_out_push(vio->vout, vio->err_hard) ;
+
+ if (ret != CMD_SUCCESS)
+ break ;
+ } ;
+ } ;
+
+ if (vio->vout_depth == 0)
+ assert(vio->vout->depth_mark == 0) ;
+
+ if (vio->vin_depth < vio->vout->depth_mark)
+ ret = uty_vout_pop(vio, vio->err_hard) ;
+ else
+ break ;
+ } ;
+
+ /* (4) Quit now if not successful on stack adjustment
+ */
+ if (ret != CMD_SUCCESS)
+ {
+ if (ret == CMD_WAITING)
+ {
+ assert(!vio->blocking) ;
+ vio->state = vc_waiting ;
+ return ret ; /* <<< exit here on CMD_WAITING */
+ } ;
+
+ if (ret == CMD_IO_ERROR)
+ {
+ vio->state = vc_io_error_trap ;
+ return ret ;
+ } ;
+
+ zabort("invalid return code") ;
+ } ;
+
+ /* (5) Having dealt with closing of files and adjustment of stack, may
+ * now be EOF on the vin_base.
+ */
+ if (vio->real_depth == 0)
+ {
+ vio->state = vc_stopped ;
+ return CMD_EOF ;
+ } ;
+
+ /* (6) All ready now to continue processing commands.
+ */
+ uty_cmd_prepare(vio) ; /* update vty->exec state */
+
+ return CMD_SUCCESS ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * When entering command loop, or after opening or closing a vin/vout object,
+ * update the vty->exec context.
+ *
+ * Output to the vout_base is suppressed for reading of configuration files.
+ *
+ * Reflection of the command line depends on the current context, and on the
+ * state of output suppression.
+ */
+static void
+uty_cmd_prepare(vty_io vio)
+{
+ cmd_exec exec = vio->vty->exec ;
+
+ exec->out_suppress = (vio->vty->type == VTY_CONFIG_READ) &&
+ (vio->vout_depth == 1) ;
+ exec->reflect = exec->context->reflect_enabled && !exec->out_suppress ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set the full_lex flag for further commands, and for any further pipes.
+ */
+extern void
+vty_cmd_set_full_lex(vty vty, bool full_lex)
+{
+ VTY_LOCK() ;
+
+ vty->exec->context->full_lex = full_lex ;
+
+ VTY_UNLOCK() ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Reflect the command line to the current vio->obuf.
+ *
+ * Advances the end_mark past the reflected line, so that output (in particular
+ * error stuff) is separate.
+ *
+ * NB: pushes the output, so that if the command takes a long time to process,
+ * it is visible while it proceeds.
+ */
+extern cmd_return_code_t
+vty_cmd_reflect_line(vty vty)
+{
+ cmd_return_code_t ret ;
+
+ VTY_LOCK() ;
+
+ if (vty->vio->state != vc_running)
+ ret = CMD_HIATUS ;
+ else
+ {
+ vio_fifo obuf ;
+ qstring line ;
+
+ obuf = vty->vio->obuf ; /* once locked */
+ line = vty->exec->action->line ;
+
+ vio_fifo_put_bytes(obuf, qs_char_nn(line), qs_len_nn(line)) ;
+ vio_fifo_put_byte(obuf, '\n') ;
+
+ ret = uty_cmd_out_push(vty->vio->vout, false) ; /* not final */
+ } ;
+
+ VTY_UNLOCK() ;
+
+ return ret ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set the vio->depth_mark -- about to push vin and/or vout
+ */
+extern void
+uty_cmd_depth_mark(vty_io vio)
+{
+ vio->depth_mark = vio->vin_depth ;
+}
+
+/*------------------------------------------------------------------------------
+ * Open the given file as an in pipe, if possible.
+ *
+ * Puts error messages to vty if fails.
+ *
+ * NB: saves the current context to the current vin, before opening and pushing
+ * the new one.
+ */
+extern cmd_return_code_t
+uty_cmd_open_in_pipe_file(vty_io vio, cmd_context context,
+ qstring name, cmd_pipe_type_t type)
+{
+ cmd_return_code_t ret ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ if (vio->state != vc_running)
+ ret = CMD_HIATUS ;
+ else
+ {
+ ret = uty_file_read_open(vio, name, context) ;
+
+ if (ret == CMD_SUCCESS)
+ {
+ context->reflect_enabled = (type & cmd_pipe_reflect) != 0 ;
+ context->parse_strict = true ;
+
+ uty_cmd_prepare(vio) ;
+ } ;
+ } ;
+
+ return ret ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Run the given shell command as an in pipe, if possible.
+ *
+ * Puts error messages to vty if fails.
+ *
+ * NB: saves the current context to the current vin, before opening and pushing
+ * the new one.
+ */
+extern cmd_return_code_t
+uty_cmd_open_in_pipe_shell(vty_io vio, cmd_context context, qstring command,
+ cmd_pipe_type_t type)
+{
+ cmd_return_code_t ret ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ if (vio->state != vc_running)
+ ret = CMD_HIATUS ;
+ else
+ {
+ ret = uty_pipe_read_open(vio, command, context) ;
+
+ if (ret == CMD_SUCCESS)
+ {
+ context->reflect_enabled = (type & cmd_pipe_reflect) != 0 ;
+ context->parse_strict = true ;
+
+ uty_cmd_prepare(vio) ;
+ } ;
+ } ;
+
+ return ret ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Open the given file as an out pipe, if possible.
+ *
+ * Puts error messages to vty if fails.
+ */
+extern cmd_return_code_t
+uty_cmd_open_out_pipe_file(vty_io vio, cmd_context context, qstring name,
+ cmd_pipe_type_t type)
+{
+ cmd_return_code_t ret ;
+
+ if (vio->state != vc_running)
+ ret = CMD_HIATUS ;
+ else
+ {
+ ret = uty_file_write_open(vio, name,
+ ((type & cmd_pipe_append) != 0), context) ;
+ if (ret == CMD_SUCCESS)
+ uty_cmd_prepare(vio) ;
+ } ;
+
+ return ret ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Open the given shell command as an out pipe, if possible.
+ *
+ * Puts error messages to vty if fails.
+ */
+extern cmd_return_code_t
+uty_cmd_open_out_pipe_shell(vty_io vio, cmd_context context, qstring command,
+ cmd_pipe_type_t type)
+{
+ cmd_return_code_t ret ;
+
+ if (vio->state != vc_running)
+ ret = CMD_HIATUS ;
+ else
+ {
+ ret = uty_pipe_write_open(vio, command,
+ ((type & cmd_pipe_shell_only) !=0)) ;
+
+ if (ret == CMD_SUCCESS)
+ uty_cmd_prepare(vio) ;
+ } ;
+
+ return ret ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Open "/dev/null" as an out pipe, if possible.
+ *
+ * Puts error messages to vty if fails.
+ */
+extern cmd_return_code_t
+uty_cmd_open_out_dev_null(vty_io vio)
+{
+ cmd_return_code_t ret ;
+ vio_vf vf ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ if (vio->state != vc_running)
+ ret = CMD_HIATUS ;
+ else
+ {
+ vf = uty_vf_new(vio, "dev_null", -1, vfd_none, vfd_io_none) ;
+ uty_vout_push(vio, vf, VOUT_DEV_NULL, NULL, NULL, 0) ;
+
+ uty_cmd_prepare(vio) ;
+
+ ret = CMD_SUCCESS ;
+ } ;
+
+ return ret ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Complete the given file name, if not rooted.
+ *
+ * Returns: given or new qpath (if given was NULL)
+ */
+extern qpath
+uty_cmd_path_name_complete(qpath dst, const char* name, cmd_context context)
+{
+ if (*name == '/')
+ return qpath_set(dst, name) ; /* done if is rooted */
+
+ if (*name != '~')
+ dst = qpath_copy(dst, context->dir_cd) ;
+ else if ((*(name + 1) == '/') || (*(name + 1) == '\0'))
+ dst = qpath_copy(dst, context->dir_home) ;
+ else if ((*(name + 1) == '.') &&
+ ( (*(name + 2) == '/') || (*(name + 2) == '\0')) )
+ dst = qpath_copy(dst, context->dir_here) ;
+ else
+ return qpath_set(dst, name) ; /* no idea... probably an error */
+
+ return qpath_append_str(dst, name) ; /* create the full path */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Command has completed successfully.
+ *
+ * An output generated by the command is now pushed unless exec->out_suppress,
+ * or discarded.
+ */
+extern cmd_return_code_t
+vty_cmd_success(vty vty)
+{
+ cmd_return_code_t ret ;
+ vty_io vio ;
+
+ VTY_LOCK() ;
+ vio = vty->vio ; /* once locked */
+
+ ret = CMD_SUCCESS ;
+
+ if (vio->state != vc_running)
+ ret = CMD_HIATUS ;
+ else
+ {
+ if (!vio_fifo_tail_empty(vio->obuf))
+ {
+ if (!vty->exec->out_suppress)
+ ret = uty_cmd_out_push(vio->vout, false) ; /* not final */
+ else
+ vio_fifo_back_to_end_mark(vio->obuf, true) ; /* keep end mark */
+ } ;
+ } ;
+
+ VTY_UNLOCK() ;
+
+ return ret ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * If there is anything after the end_mark, push it to be written, now.
+ *
+ * See uty_cmd_out_push() below.
+ */
+extern cmd_return_code_t
+vty_cmd_out_push(vty vty)
+{
+ cmd_return_code_t ret ;
+
+ VTY_LOCK() ;
+
+ if (vty->vio->state != vc_running)
+ ret = CMD_HIATUS ;
+ else
+ ret = uty_cmd_out_push(vty->vio->vout, false) ; /* not final */
+
+ VTY_UNLOCK() ;
+
+ return ret ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * If there is anything after the end_mark, push it to be written, now.
+ *
+ * For most outputs we kick the I/O side so that the pselect processing deals
+ * with the required write. For VOUT_STDERR and VOUT_STDOUT, output goes
+ * directly to standard I/O.
+ *
+ * Advances the end_mark past the stuff pushed.
+ *
+ * NB: takes no notice of vf->out_suppress, which applies only to buffered
+ * output present when successfully complete a command -- vty_cmd_success().
+ *
+ * TODO: worry about closing state !
+ * TODO: need a vf level one of these.
+ */
+extern cmd_return_code_t
+uty_cmd_out_push(vio_vf vf, bool final) /* TODO: write errors & blocking */
+{
+ cmd_return_code_t ret ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ vio_fifo_step_end_mark(vf->obuf) ; /* advance the end mark */
+
+ ret = CMD_SUCCESS ;
+
+ switch (vf->vout_state)
+ {
+ case vf_open:
+ case vf_closing:
+ switch (vf->vout_type)
+ {
+ case VOUT_NONE:
+ zabort("invalid vout_none") ;
+ break ;
+
+ case VOUT_TERM:
+ ret = uty_term_out_push(vf, final) ;
+ break ;
+
+ case VOUT_VTYSH:
+ /* Kick the writer */
+ break ;
+
+ case VOUT_FILE:
+ ret = uty_file_out_push(vf, final) ;
+ break ;
+
+ case VOUT_PIPE:
+ ret = uty_pipe_out_push(vf, final) ;
+ break ;
+
+ case VOUT_CONFIG:
+ ret = uty_file_out_push(vf, final) ; /* treat as file */
+ break ;
+
+ case VOUT_DEV_NULL:
+ case VOUT_SHELL_ONLY:
+ vio_fifo_clear(vf->obuf, false) ; /* keep end mark */
+ break ;
+
+ case VOUT_STDOUT:
+ vio_fifo_fwrite(vf->obuf, stdout) ; // TODO errors
+ break ;
+
+ case VOUT_STDERR:
+ vio_fifo_fwrite(vf->obuf, stderr) ; // TODO errors
+ break ;
+
+ default:
+ zabort("unknown vout_type") ;
+ } ;
+ break ;
+
+ case vf_closed:
+ case vf_timed_out:
+ break ; /* immediate success ! */
+
+ case vf_eof:
+ zabort("vf->vout cannot be vf_eof") ;
+ break ;
+
+ case vf_error:
+ ret = CMD_IO_ERROR ;
+ break ;
+
+ default:
+ zabort("unknown vf->vout_state") ;
+ break ;
+ } ;
+
+ return ret ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Dealing with error of some kind.
+ *
+ * The current vio->obuf will have an end_mark. After the end_mark will be
+ * any output generated since the start of the current command (or any out_push
+ * since then). For command errors, that output is expected to be error
+ * messages associated with the error.
+ *
+ * A new "ebuf" fifo is created, and the location of the error is written to
+ * that fifo. Once the contents of the "ebuf" are output, the command line in
+ * which the error occurred should be the last thing output.
+ *
+ * Any other error message is then appended to the ebuf.
+ *
+ * For command errors, the tail of the vio->obuf (stuff after the end_mark) is
+ * moved to the ebuf.
+ *
+ * Deals with:
+ *
+ * CMD_WARNING = TODO
+ * CMD_ERROR =>
+ *
+ * CMD_ERR_PARSING, parser: general parser error
+ * CMD_ERR_NO_MATCH, parser: command/argument not recognised
+ * CMD_ERR_AMBIGUOUS, parser: more than on command matches
+ * CMD_ERR_INCOMPLETE,
+ *
+ * CMD_IO_ERROR I/O -- failed :-(
+ * CMD_IO_TIMEOUT I/O -- timed out :-(
+ *
+ * In any event, need to separate out any output from the failing command,
+ * so that can report error location and type, before showing the error
+ * message(s).
+ *
+ * NB: does not expect to see all the possible CMD_XXX return codes (see
+ * below), but treats all as a form of error !
+ */
+static uint
+uty_cmd_failed(vty_io vio, cmd_return_code_t ret)
+{
+ ulen indent ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ /* Process the vin stack to generate the error location(s) */
+ if (vio->ebuf != NULL)
+ vio_fifo_clear(vio->ebuf, true) ;
+ else
+ vio->ebuf = vio_fifo_new(1000) ;
+
+ indent = uty_show_error_context(vio->ebuf, vio->vin) ;
+
+ /* Now any additional error message if required */
+ switch (ret)
+ {
+ case CMD_WARNING:
+ if (vio_fifo_tail_empty(vio->obuf))
+ vio_fifo_printf(vio->ebuf, "%% WARNING: non-specific warning\n") ;
+ break ;
+
+ case CMD_ERROR:
+ if (vio_fifo_tail_empty(vio->obuf))
+ vio_fifo_printf(vio->ebuf, "%% ERROR: non-specific error\n") ;
+ break ;
+
+ case CMD_ERR_PARSING:
+ cmd_get_parse_error(vio->ebuf, vio->vty->exec->parsed, indent) ;
+ break ;
+
+ case CMD_ERR_NO_MATCH:
+ vio_fifo_printf(vio->ebuf, "%% Unknown command.\n") ;
+ break;
+
+ case CMD_ERR_AMBIGUOUS:
+ vio_fifo_printf(vio->ebuf, "%% Ambiguous command.\n");
+ break;
+
+ case CMD_ERR_INCOMPLETE:
+ vio_fifo_printf(vio->ebuf, "%% Command incomplete.\n");
+ break;
+
+ case CMD_IO_ERROR:
+ break ;
+
+ case CMD_IO_TIMEOUT:
+ break ;
+
+ default:
+ vio_fifo_printf(vio->ebuf, "%% Unexpected return code (%d).\n", (int)ret);
+ break ;
+ } ;
+
+ /* Now stick the obuf tail onto the end of the ebuf & discard the tail of
+ * the obuf.
+ */
+ vio_fifo_copy_tail(vio->ebuf, vio->obuf) ;
+ vio_fifo_back_to_end_mark(vio->obuf, true) ;
+
+ /* Decide what stack level to close back to. */
+ return (vio->vty->type == VTY_TERMINAL) ? 1 : 0 ; // TODO I/O error ??
+} ;
+
+/*------------------------------------------------------------------------------
+ * In the given fifo, construct message giving the context in which an error
+ * has occurred.
+ *
+ * For file and pipe input (including config file), it is assumed that no
+ * command line has been reflected, so the context is given as:
+ *
+ * % on line 99 of xxxx:
+ * <the command line -- noting small indent>
+ *
+ * For interactive input, if the stack depth is 1, then it is assumed that the
+ * command line is the last complete line output. Otherwise the context is
+ * given as:
+ *
+ * % in command line:
+ * <the command line -- noting small indent>
+ *
+ * The context starts with level 1 of the vin stack, and ends with the current
+ * level.
+ *
+ * Returns: "eloc" -- start of command line at the current level.
+ */
+static uint
+uty_show_error_context(vio_fifo ebuf, vio_vf vf)
+{
+ vio_vf vf_next ;
+ uint indent ;
+
+ /* Recurse until hit end of the vin stack */
+ vf_next = ssl_next(vf, vin_next) ;
+
+ if (vf_next != NULL)
+ uty_show_error_context(ebuf, vf_next) ;
+ else
+ assert(vf == vf->vio->vin_base) ;
+
+ /* On the way back, add the error location for each vin entry
+ * and establish the location of the start of the command line as shown.
+ */
+ indent = 0 ;
+
+ switch (vf->vin_type)
+ {
+ case VIN_NONE:
+ zabort("invalid VIN_NONE") ;
+ break ;
+
+ case VIN_TERM:
+ indent = uty_term_show_error_context(vf, ebuf, vf->vio->vin_depth) ;
+ break ;
+
+ case VIN_VTYSH:
+// eloc = uty_term_show_error_context(vf, ebuf, vf->vio->vin_depth) ;
+ break ;
+
+ case VIN_FILE:
+ case VIN_PIPE:
+ case VIN_CONFIG:
+ vio_fifo_printf(ebuf, "%% on line %d of %s:\n",
+ vf->line_number, vf->name) ;
+ vio_fifo_printf(ebuf, " %s\n", qs_make_string(vf->cl)) ;
+ indent = 2 ;
+ break ;
+
+ case VIN_DEV_NULL:
+ break ;
+
+ default:
+ zabort("unknown vin_type") ;
+ } ;
+
+ return indent ;
+} ;
+
+/*==============================================================================
+ * Configuration node/state handling
+ *
+ * At most one VTY may hold the configuration symbol of power at any time.
+ */
+
+/*------------------------------------------------------------------------------
+ * Attempt to gain the configuration symbol of power -- may already own it !
+ *
+ * Returns: true <=> now own the symbol of power (or already did).
+ */
+extern cmd_return_code_t
+vty_cmd_config_lock (vty vty)
+{
+ VTY_LOCK() ;
+ uty_cmd_config_lock(vty) ;
+ VTY_UNLOCK() ;
+
+ if (vty->config)
+ return CMD_SUCCESS ;
+
+ vty_out (vty, "VTY configuration is locked by other VTY\n") ;
+ return CMD_WARNING ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Attempt to gain the configuration symbol of power -- may already own it !
+ *
+ * Returns: true <=> now own the symbol of power (or already did).
+ */
+static void
+uty_cmd_config_lock (vty vty)
+{
+ if (!host.config) /* If nobody owns the lock... */
+ {
+ host.config = true ; /* ...rope it... */
+ vty->config = true ;
+
+ do
+ ++host.config_brand ; /* ...update the brand... */
+ while (host.config_brand == 0) ;
+
+ vty->config_brand = host.config_brand ; /* ...brand it. */
+ }
+ else /* Somebody owns the lock... */
+ {
+ if (vty->config) /* ...if we think it is us, check brand */
+ assert(host.config_brand == vty->config_brand) ;
+ } ;
+}
+
+/*------------------------------------------------------------------------------
+ * Check that given node and ownership of configuration symbol of power...
+ * ...see below.
+ */
+extern void
+vty_cmd_config_lock_check(vty vty, node_type_t node)
+{
+ VTY_LOCK() ;
+ uty_cmd_config_lock_check(vty, node) ;
+ VTY_UNLOCK() ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Check that given node and ownership of configuration symbol of power
+ * are mutually consistent.
+ *
+ * If node > MAX_NON_CONFIG_NODE, must own the symbol of power.
+ *
+ * If node <= MAX_NON_CONFIG_NODE, will release symbol of power, if own it,
+ * PROVIDED is at vin_depth <= 1 !!
+ */
+static void
+uty_cmd_config_lock_check(vty vty, node_type_t node)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if (vty->config)
+ {
+ /* We think we own it, so we better had */
+ assert(host.config) ;
+ assert(host.config_brand == vty->config_brand) ;
+
+ /* If no longer need it, release */
+ if ((node <= MAX_NON_CONFIG_NODE) && (vty->vio->vin_depth <= 1))
+ {
+ host.config = false ;
+ vty->config = false ;
+ } ;
+ }
+ else
+ {
+ /* We don't think we own it, so we had better not */
+ if (host.config)
+ assert(host.config_brand != vty->config_brand) ;
+
+ /* Also, node had better not require that we do. */
+ assert(node <= MAX_NON_CONFIG_NODE) ;
+ } ;
+} ;
diff --git a/lib/vty_command.h b/lib/vty_command.h
new file mode 100644
index 00000000..125b9a03
--- /dev/null
+++ b/lib/vty_command.h
@@ -0,0 +1,67 @@
+/* VTY for command execution
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * Revisions: Copyright (C) 2010 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * 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_VTY_COMMAND_H
+#define _ZEBRA_VTY_COMMAND_H
+
+#include "misc.h"
+
+#include "command_local.h"
+#include "vty_local.h"
+#include "vty_io.h"
+
+extern void vty_cmd_loop_prepare(vty vty) ;
+extern void uty_cmd_loop_enter(vty_io vio) ;
+extern void uty_cmd_signal(vty_io vio, cmd_return_code_t ret) ;
+extern void uty_cmd_loop_close(vty_io vio, bool curtains) ;
+extern void vty_cmd_loop_exit(vty vty) ;
+
+extern cmd_return_code_t vty_cmd_fetch_line(vty vty) ;
+extern cmd_return_code_t vty_cmd_hiatus(vty vty, cmd_return_code_t ret) ;
+extern cmd_return_code_t vty_cmd_special(vty vty) ;
+extern cmd_return_code_t vty_cmd_can_auth_enable(vty vty) ;
+extern cmd_return_code_t vty_cmd_reflect_line(vty vty) ;
+extern cmd_return_code_t vty_cmd_out_push(vty vty) ;
+extern cmd_return_code_t uty_cmd_out_push(vio_vf vf, bool final) ;
+
+extern void vty_cmd_set_full_lex(vty vty, bool full_lex) ;
+
+extern void uty_cmd_depth_mark(vty_io vio) ;
+extern cmd_return_code_t uty_cmd_open_in_pipe_file(vty_io vio,
+ cmd_context context, qstring name, cmd_pipe_type_t type) ;
+extern cmd_return_code_t uty_cmd_open_in_pipe_shell(vty_io vio,
+ cmd_context context, qstring command, cmd_pipe_type_t type) ;
+extern cmd_return_code_t uty_cmd_open_out_pipe_file(vty_io vio,
+ cmd_context context, qstring name, cmd_pipe_type_t type) ;
+extern cmd_return_code_t uty_cmd_open_out_dev_null(vty_io vio) ;
+extern cmd_return_code_t uty_cmd_open_out_pipe_shell(vty_io vio,
+ cmd_context context, qstring command, cmd_pipe_type_t type) ;
+extern qpath uty_cmd_path_name_complete(qpath dst, const char* name,
+ cmd_context context) ;
+
+extern cmd_return_code_t vty_cmd_success(vty vty) ;
+
+extern cmd_return_code_t vty_cmd_config_lock (vty vty) ;
+extern void vty_cmd_config_lock_check(struct vty *vty, node_type_t node) ;
+
+#endif /* _ZEBRA_VTY_COMMAND_H */
diff --git a/lib/vty_common.h b/lib/vty_common.h
new file mode 100644
index 00000000..04429d47
--- /dev/null
+++ b/lib/vty_common.h
@@ -0,0 +1,141 @@
+/* VTY top level
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * Revisions: Copyright (C) 2010 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * 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_VTY_COMMON_H
+#define _ZEBRA_VTY_COMMON_H
+
+#include "misc.h"
+#include "qstring.h"
+#include "command_common.h"
+
+/*==============================================================================
+ * These are things required by:
+ *
+ * vty.h -- which is used by all "external" code.
+ *
+ * command_local.h -- which is used by all "internal" code.
+ *
+ * This allows some things not to be published to "external" code.
+ */
+
+/*==============================================================================
+ * VTY Types and the VTY structure.
+ *
+ * The "struct vty" is used extensively across the Quagga daemons, where it
+ * has two functions relating to command handling as:
+ *
+ * 1) a "handle" for output produced by commands
+ *
+ * 2) the holder of some context -- notably the current command "node" -- for
+ * command execution to use
+ *
+ * The bulk of "struct vty" is, therefore, private to vty.c et al and is
+ * factored out into the "struct vty_io" -- opaque to users of the struct vty.
+ *
+ * There is also context used when parsing and executing commands which is
+ * private to command.c et al, and is factored out into the "struct cmd_exec"
+ * -- also opaque to users of the struct vty.
+ */
+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 ;
+
+/* Most of the contents of the vty structure live in two opaque structures,
+ * which are forward referenced here.
+ */
+struct vty_io ;
+struct cmd_execution ;
+
+/* All command execution functions take a vty argument, and this is it.
+ */
+typedef struct vty* vty ;
+struct vty
+{
+ vty_type_t type ;
+
+ /*----------------------------------------------------------------------
+ * The following are the context in which commands are executed.
+ *
+ * While a command has the vty in its hands, it can access and change these
+ * because they are not touched by the CLI thread until the command has
+ * completed.
+ */
+
+ /* Node status of this vty. */
+ node_type_t 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 (owner of the symbol of power) */
+ bool config ; /* we own */
+ ulong config_brand ; /* and this is our mark */
+
+ /*----------------------------------------------------------------------------
+ * The current cmd_exec environment -- used in command_execute.c et al
+ *
+ * This is accessed freely by the command handling code because there is
+ * only one thread of execution per vty -- though for some vty types (notably
+ * VTY_TERMINAL) that may be in the vty_cli_thread or in the vty_cmd_thread
+ * at different times.
+ *
+ * Where information from the vio is required, the VTY_LOCK is acquired.
+ */
+ struct cmd_exec* exec ; /* one per vty */
+
+ /*----------------------------------------------------------------------
+ * The following is used inside vty.c etc only -- under VTY_LOCK.
+ *
+ * The lock is required because the vty_cli_thread may be doing I/O and
+ * other stuff at the same time as the vty_cmd_thread is doing I/O, or at the
+ * same time as other vty are being serviced.
+ *
+ * Could have one lock per vty -- but would then need a lock for the common
+ * parts of the cli thread, so one lock keeps things relatively simple.
+ */
+ struct vty_io* vio ; /* one per vty */
+} ;
+
+#endif /* _ZEBRA_VTY_COMMON_H */
diff --git a/lib/vty_io.c b/lib/vty_io.c
index 2eadc2d1..96df3850 100644
--- a/lib/vty_io.c
+++ b/lib/vty_io.c
@@ -1,4 +1,4 @@
-/* VTY IO Functions
+/* VTY IO Functions -- top level of VTY IO hierarchy
* Copyright (C) 1997, 98 Kunihiro Ishiguro
*
* Revisions: Copyright (C) 2010 Chris Hall (GMCH), Highwayman
@@ -20,14 +20,19 @@
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
-
-#include "zebra.h"
+#include "misc.h"
#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"
@@ -40,148 +45,37 @@
#include <arpa/telnet.h>
#include <sys/un.h> /* for VTYSH */
#include <sys/socket.h>
+#include <wait.h>
#define VTYSH_DEBUG 0
/*==============================================================================
- * VTY Command Output -- base functions
- *
- * During command processing the output sent here is held until the command
- * completes.
+ * Basic output to VTY.
*/
-static int uty_config_write(vty_io vio, bool all) ;
-
/*------------------------------------------------------------------------------
- * VTY output function -- cf fprintf
+ * VTY output -- cf fprintf ! Same as vty_out, less the VTY_LOCK().
*
- * 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
+ * This is for command output, which may later be suppressed
*
* 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)
+uty_out(vty_io vio, const char *format, ...)
{
- vty_io vio ;
- int ret ;
+ int ret ;
+ va_list args ;
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") ;
- } ;
+ va_start (args, format) ;
+ ret = uty_vprintf(vio, format, args) ;
+ va_end (args) ;
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) ;
-} ;
-
/*==============================================================================
* The watch dog.
*
@@ -193,2552 +87,1805 @@ uty_out_fflush(vty_io vio, FILE* file)
* * the death watch list
*/
-enum { vty_watch_dog_interval = 5 } ;
+/* Watch-dog timer. */
+static vio_timer_t vty_watch_dog ;
-static void vty_watch_dog_qnexus(qtimer qtr, void* timer_info, qtime_t when) ;
-static int vty_watch_dog_thread(struct thread *thread) ;
+static vty_timer_time uty_watch_dog_bark(vio_timer timer, void* info) ;
+static void uty_death_watch_scan(bool final) ;
-static void uty_watch_dog_bark(void) ;
-static bool uty_death_watch_scan(void) ;
+static vty_io uty_dispose(vty_io vio) ;
/*------------------------------------------------------------------------------
- * Start watch dog -- the first time a VTY is created.
+ * Initialise watch dog -- at start-up time.
*/
extern void
-uty_watch_dog_start()
+uty_watch_dog_init(void)
{
- if (vty_cli_nexus)
- vty_watch_dog.qnexus = qtimer_init_new(NULL, vty_cli_nexus->pile,
- NULL, NULL) ;
+ vio_timer_init_new(vty_watch_dog, NULL, NULL) ; /* empty */
+} ;
- uty_watch_dog_bark() ; /* start up by barking the first time */
-}
+/*------------------------------------------------------------------------------
+ * Start watch dog -- before a VTY is created.
+ */
+extern void
+uty_watch_dog_start(void)
+{
+ vio_timer_init_new(vty_watch_dog, uty_watch_dog_bark, NULL) ;
+ vio_timer_set(vty_watch_dog, VTY_WATCH_DOG_INTERVAL) ;
+} ;
/*------------------------------------------------------------------------------
* Stop watch dog timer -- at close down.
*
* Final run along the death-watch
- *
*/
extern void
uty_watch_dog_stop(void)
{
- if (vty_watch_dog.anon != NULL)
- {
- if (vty_cli_nexus)
- qtimer_free(vty_watch_dog.qnexus) ;
- else
- thread_cancel(vty_watch_dog.thread) ;
- } ;
-
- uty_death_watch_scan() ; /* scan the death-watch list */
+ vio_timer_reset(vty_watch_dog, keep_it) ;
+ uty_death_watch_scan(true) ; /* scan the death-watch list */
}
/*------------------------------------------------------------------------------
- * qnexus watch dog action
- */
-static void
-vty_watch_dog_qnexus(qtimer qtr, void* timer_info, qtime_t when)
-{
- VTY_LOCK() ;
-
- uty_watch_dog_bark() ;
-
- VTY_UNLOCK() ;
-} ;
-
-/*------------------------------------------------------------------------------
- * thread watch dog action
+ * Watch dog vio_timer action
*/
-static int
-vty_watch_dog_thread(struct thread *thread)
+static vty_timer_time
+uty_watch_dog_bark(vio_timer timer, void* info)
{
- VTY_LOCK() ;
+ cmd_host_name(true) ; /* check for host name change */
- vty_watch_dog.thread = NULL ;
- uty_watch_dog_bark() ;
+ uty_death_watch_scan(false) ; /* scan the death-watch list */
- VTY_UNLOCK() ;
- return 0 ;
+ return VTY_WATCH_DOG_INTERVAL ;
} ;
/*------------------------------------------------------------------------------
- * Watch dog action
+ * Process the death watch list -- anything on the list can be disposed of.
+ *
+ * At curtains...
*/
static void
-uty_watch_dog_bark(void)
+uty_death_watch_scan(bool final)
{
- uty_check_host_name() ; /* check for host name change */
+ VTY_ASSERT_CLI_THREAD() ;
- uty_death_watch_scan() ; /* scan the death-watch list */
+ /* Dispose of anything on the death watch list. */
- /* Set timer to go off again later */
- if (vty_cli_nexus)
- qtimer_set(vty_watch_dog.qnexus,
- qt_add_monotonic(QTIME(vty_watch_dog_interval)),
- vty_watch_dog_qnexus) ;
- else
+ while (vio_death_watch != NULL)
{
- if (vty_watch_dog.thread != NULL)
- thread_cancel (vty_watch_dog.thread);
- vty_watch_dog.thread = thread_add_timer (vty_master,
- vty_watch_dog_thread, NULL, vty_watch_dog_interval) ;
- } ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Scan the death watch list.
- *
- * A vty may finally be freed if it is closed and there is no command in
- * progress.
- */
-static bool
-uty_death_watch_scan(void)
-{
- vty_io vio ;
- vty_io next ;
+ vty vty ;
+ vty_io vio ;
- next = vio_death_watch ;
- while (next != NULL)
- {
- vio = next ;
- next = sdl_next(vio, vio_list) ;
+ vio = vio_death_watch ;
+ vio_death_watch = sdl_next(vio, vio_list) ; /* take off death watch */
- if (vio->closed && !vio->cmd_in_progress)
- {
- uty_close(vio) ; /* closes again to ensure that all buffers
- are released. */
+ vty = vio->vty ;
- sdl_del(vio_death_watch, vio, vio_list) ;
+ vty->vio = uty_dispose(vty->vio) ;
+ assert(vty->exec == NULL) ;
- XFREE(MTYPE_VTY, vio->vty) ;
- XFREE(MTYPE_VTY, vio) ;
- } ;
+ XFREE(MTYPE_VTY, vty) ;
} ;
-
- return (vio_death_watch == NULL) ;
} ;
/*==============================================================================
* 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) ;
+static void uty_vout_close_reason(vio_vf vf, const char* reason) ;
+static cmd_return_code_t uty_vf_read_close(vio_vf vf, bool final) ;
+static cmd_return_code_t 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
+ * Allocate new vty structure, including empty vty_io and empty execution
+ * structures.
+ *
+ * Caller must complete the initialisation of the vty_io, which means:
*
- * Allocates and initialises basic vty and vty_io structures, setting the
- * given type.
+ * * constructing a suitable vio_vf and doing uty_vin_push() to set the
+ * vin_base.
*
- * Note that where is not setting up a vty_sock, this *may* be called from
- * any thread.
+ * All vty_io MUST have a vin_base, even if it is /dev/null.
*
- * NB: may not create a VTY_CONFIG_WRITE type vty directly
+ * * constructing a suitable vio_vf and doing uty_vout_push() to set the
+ * vout_base and the vio->obuf.
*
- * see: vty_open_config_write() and vty_close_config_write()
+ * All vty_io MUST have a vout_base, even if it is /dev/null.
*
- * 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 !)
+ * * setting vio->cli, if required
*
- * the sock_fd is ignored for everything else.
+ * * etc.
*
- * Returns: new vty
+ * No "exec" is allocated. That is done when the command loop is entered.
*/
-extern struct vty *
-uty_new(enum vty_type type, int sock_fd)
+extern vty
+uty_new(vty_type_t type, node_type_t node)
{
- struct vty *vty ;
- struct vty_io* vio ;
+ vty vty ;
+ 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
+ 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 = X -- set to actual node, below
*
- * 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 owner of configuration symbol of power
+ * config_brand = 0 -- none, yet
*
- * 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--"
+ * exec = NULL -- execution state set up when required
+ * vio = X -- set below
+ */
+ confirm(NULL_NODE == 0) ;
+ confirm(QSTRING_INIT_ALL_ZEROS) ;
+
+ vty->type = type ;
+ vty->node = node ;
+
+ 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
+ * real_depth = 0 -- nothing stacked, yet
*
- * fail = 0 -- no login failures yet
+ * vout = NULL -- empty output stack
+ * vout_base = NULL -- empty output stack
+ * vout_depth = 0 -- no stacked vout's, yet
*
- * hist = empty vector
- * hp = 0 -- at the beginning
- * hindex = 0 -- the beginning
+ * depth_mark = 0 -- no stacked vin/vout, yet
*
- * width = 0 -- unknown console width
- * height = 0 -- unknown console height
+ * err_hard = false -- no error at all, yet
+ * ebuf = NULL -- no error at all, yet
*
- * lines = 0 -- no limit
- * lines_set = 0 -- no explicit setting
+ * vio_list = NULLs -- not on the vio_list, yet
+ * mon_list = NULLs -- not on the monitors list
*
- * monitor = 0 -- not a monitor
- * monitor_busy = 0 -- not a busy monitor
+ * blocking = X -- set below: false unless VTY_CONFIG_READ
*
- * 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:
+ * state = vc_null -- not started vty command loop
*
- * 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 (type == VTY_TERM)
- vty->newline = "\n" ; /* line control looks after "\r\n" */
- else
- vty->newline = "\n" ;
-
- /* Initialise the vio_sock, */
- uty_sock_init_new(&vio->sock, sock_fd, vio) ;
-
- /* Make sure all buffers etc. are initialised clean and empty.
+ * close_reason = NULL -- none set
*
- * Note that no buffers are actually allocated at this stage.
+ * obuf = NULL -- no output buffer, yet
*/
- qs_init_new(&vio->cli_prompt_for_node, 0) ;
+ confirm(vc_null == 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 */
+ vty->vio = vio ;
+ vio->vty = vty ;
- vio_fifo_init_new(&vio->cmd_obuf, 8 * 1024) ;
+ vio->blocking = (type == VTY_CONFIG_READ) ;
/* Place on list of known vio/vty */
- sdl_push(vio_list_base, vio, vio_list) ;
+ sdl_push(vio_live_list, vio, vio_list) ;
return vty;
} ;
/*------------------------------------------------------------------------------
- * Create new vty of type VTY_TERM -- ie attached to a telnet session.
+ * Add a new vf to the vio->vin stack, and set read stuff.
+ *
+ * Sets the vf->vin_type and set vf->read_open.
+ *
+ * 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.
*
- * Returns: new vty
+ * Sets the read ready action and the read timer timeout action.
+ *
+ * NB: is usually called from the cli thread, but may be called from the cmd
+ * thread for vf which is blocking !
+ *
+ * NB: a uty_cmd_prepare() is required before command processing can continue.
*/
-static struct vty *
-uty_new_term(int sock_fd, union sockunion *su)
+extern void
+uty_vin_push(vty_io vio, vio_vf vf, vio_in_type_t type,
+ vio_vfd_action* read_action,
+ vio_timer_action* read_timer_action,
+ usize ibuf_size)
{
- struct vty *vty ;
- vty_io vio ;
- enum vty_readiness ready ;
-
- VTY_ASSERT_LOCKED() ;
- VTY_ASSERT_CLI_THREAD() ;
+ vf->vin_type = type ;
+ vf->vin_state = vf_open ;
- /* Allocate new vty structure and set up default values. */
- vty = uty_new (VTY_TERM, sock_fd) ;
- vio = vty->vio ;
+ assert(type != VIN_NONE) ;
- /* 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
+ if ((type < VIN_SPECIALS) && (!vf->blocking))
{
- vio->sock.action.read.thread = vty_read_thread ;
- vio->sock.action.write.thread = vty_write_thread ;
- vio->sock.action.timer.thread = vty_timer_thread ;
+ vio_vfd_set_read_action(vf->vfd, read_action) ;
+ vio_vfd_set_read_timeout_action(vf->vfd, read_timer_action) ;
} ;
- /* The text form of the address identifies the VTY */
- vio->name = sockunion_su2str (su, MTYPE_VTY_NAME);
+ ssl_push(vio->vin, vf, vin_next) ;
+ vio->real_depth = ++vio->vin_depth ;
- /* Set the initial node */
- if (no_password_check)
+ if (vio->vin_base == NULL)
{
- 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) ;
+ assert(vio->vin_depth == 1) ;
+ vio->vin_base = vf ;
+ } ;
- /* 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
+ if (ibuf_size != 0)
{
- /* 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);
+ vf->ibuf = vio_fifo_new(ibuf_size) ;
+ vf->cl = qs_new(150) ;
+ vf->line_complete = true ;
+ vf->line_step = 1 ;
} ;
-
- /* Now start the CLI and set a suitable state of readiness */
- ready = uty_cli_start(vio) ;
- uty_sock_set_readiness(&vio->sock, ready) ;
-
- return vty;
} ;
/*------------------------------------------------------------------------------
- * Create new vty of type VTY_SHELL_SERV -- ie attached to a vtysh session.
+ * Save the given context in the current top of the vin stack.
+ *
+ * This is done when a new pipe is opened, so that:
+ *
+ * a) saves the current context in the current vin (the new pipe has
+ * not yet been pushed) so that uty_vin_pop() can restore this context
+ * after closing the then top of the stack.
+ *
+ * b) can update context for about to be run vin, eg:
+ *
+ * - dir_here -- if required
*
- * Returns: new vty
+ * - can_enable
+ *
+ * So the top of the vin stack does not contain the current context, that is
+ * in the vty->exec !
*/
-static struct vty *
-uty_new_shell_serv(int sock_fd)
+extern void
+uty_vin_new_context(vty_io vio, cmd_context context, qpath file_here)
{
- struct vty *vty ;
- vty_io vio ;
+ assert(vio->vin->context == NULL) ;
+ vio->vin->context = cmd_context_new_save(context, file_here) ;
+} ;
+/*------------------------------------------------------------------------------
+ * 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.
+ *
+ * Initialises an output buffer and sets an end_mark.
+ *
+ * The depth_mark is set to the current vio->depth_mark + 1. This is the
+ * vin_depth below which the vout should be closed. Before a command line
+ * is fetched (and hence after the previous command line has completed) the
+ * vout->depth_mark is checked. If it is > the current vin_depth, then
+ * the vout is closed before a command line can be fetched.
+ *
+ * NB: is usually called from the cli thread, but may be called from the cmd
+ * thread for vf which is blocking !
+ *
+ * NB: VOUT_DEV_NULL, VOUT_STDOUT and VOUT_STDERR are special.
+ *
+ * The write_action and the write_timer_action are ignored.
+ *
+ * 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.
+ *
+ * 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.
+ */
+extern void
+uty_vout_push(vty_io vio, vio_vf vf, vio_out_type_t type,
+ vio_vfd_action* write_action,
+ vio_timer_action* write_timer_action,
+ usize obuf_size)
+{
VTY_ASSERT_LOCKED() ;
- /* Allocate new vty structure and set up default values. */
- vty = uty_new (VTY_SHELL_SERV, sock_fd) ;
- vio = vty->vio ;
+ vf->vout_type = type ;
+ vf->vout_state = vf_open ;
- /* Set the action functions */
- if (vty_cli_nexus)
+ assert(type != VOUT_NONE) ;
+
+ if ((type < VOUT_SPECIALS) && (!vf->blocking))
{
- vio->sock.action.read.qnexus = vtysh_read_qnexus ;
- vio->sock.action.write.qnexus = vty_write_qnexus ;
- vio->sock.action.timer.qnexus = NULL ;
- }
- else
+ 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) ;
+ ++vio->vout_depth ;
+
+ if (vio->vout_base == NULL)
{
- vio->sock.action.read.thread = vtysh_read_thread ;
- vio->sock.action.write.thread = vty_write_thread ;
- vio->sock.action.timer.thread = NULL ;
+ assert(vio->vout_depth == 1) ;
+ vio->vout_base = vf ;
} ;
- vty->node = VIEW_NODE;
+ vf->obuf = vio_fifo_new(obuf_size) ;
+ vio_fifo_set_end_mark(vf->obuf) ;
- /* Kick start the CLI etc. */
- uty_sock_set_readiness(&vio->sock, write_ready) ;
+ vf->depth_mark = vio->depth_mark + 1 ;
- return vty;
+ vio->obuf = vf->obuf ;
} ;
/*------------------------------------------------------------------------------
- * Set/Clear "monitor" state:
+ * Set timeout value.
*
- * set: if VTY_TERM and not already "monitor" (and write_open !)
- * clear: if is "monitor"
+ * 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_monitor(vty_io vio, bool on)
+uty_set_timeout(vty_io vio, vty_timer_time timeout)
{
+ vio_in_type_t vt ;
+
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) ;
- }
+ vt = vio->vin_base->vin_type ;
+
+ if ((vt == VIN_TERM) || (vt == VIN_VTYSH))
+ uty_vf_set_read_timeout(vio->vin_base, timeout) ;
} ;
/*------------------------------------------------------------------------------
- * 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 : "?" ;
} ;
/*------------------------------------------------------------------------------
- * Closing down VTY for reading.
+ * Close VTY -- final.
+ *
+ * Two forms: "curtains" and "not-curtains".
+ *
+ * At "curtains" the system is being terminated, and all message and event
+ * handling has stopped. Can assume that a vty is not in any command loop,
+ * so can be killed off here and now.
+ *
+ * At "not-curtains" messages and event handling is still running. It is
+ * possible that a vty is running a command in the cmd_thread, so cannot
+ * completely close things down -- must leave enough for the command loop
+ * to continue to work, up to the point that the closing of the vty is
+ * detected.
+ *
+ * For everything that is closed, uses "final" close, which stops things
+ * instantly -- will not block, or set any read/write ready, or continue the
+ * command loop, or take any notice of errors.
+ *
+ * This close is called by:
+ *
+ * * uty_reset() -- SIGHUP -- !curtains
+ *
+ * Will close "final" everything except vout_base.
*
- * For VTY_TERM (must be in CLI thread):
+ * If the command loop is not already stopped, it is signalled to stop
+ * as soon as possible. When it does it will return here via
+ * vty_cmd_loop_exit().
*
- * * shut the socket for reading
- * * discard all buffered input, setting it to "EOF"
- * * turns off any monitor status !
- * * drop down to RESTRICTED_NODE
+ * If command loop has already stopped, then will proceed to complete
+ * close -- see below.
*
- * For VTY_SHELL_SERV (must be in CLI thread):
+ * * uty_reset() -- SIGTERM -- curtains
*
- * * shut the socket for reading
- * * discard all buffered input
- * * drop down to RESTRICTED_NODE
+ * Will close "final" everything except vout_base.
*
- * In all cases:
+ * The command loop will be set stopped, and will proceed to complete
+ * close -- see below.
*
- * * place on death watch
- * * set the vty half_closed
- * * sets the reason for closing (if any given)
+ * * vty_cmd_loop_exit() -- when the command loop has stopped -- !curtains
*
- * For VTY_TERM and VTY_SHELL_SERV, when the output side has emptied out all
- * the buffers, the VTY is closed.
+ * The command loop may have stopped in response to a vc_close_trap,
+ * which means that have been here before, and the vty is pretty much
+ * closed already.
*
- * 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.)
+ * The command loop may have stopped:
+ *
+ * - because has reached the end of the vin_base input
+ * - vin_base has timed out
+ * - there was a command error, on non-interactive vty
+ * - there was an I/O error
+ *
+ * In all these cases, the stack will already have been closed final or
+ * otherwise, except for vout_base, and all output will have been pushed.
+ *
+ * vout_base will have been closed, but not "final", so will be sitting
+ * in vf_closing state.
+ *
+ * So the vio is ready for complete close.
+ *
+ * For complete close any remaining vf are closed final, and the close reason
+ * is output to the vout_base, if any and if possible.
+ *
+ * The vty is then closed and placed on death watch to be finally reaped.
*/
extern void
-uty_half_close (vty_io vio, const char* reason)
+uty_close(vty_io vio, const char* reason, bool curtains)
{
- VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CAN_CLOSE(vio->vty) ;
- if (vio->half_closed)
- return ;
+ /* Stamp on any monitor output instantly. */
+ uty_set_monitor(vio, off) ;
- if (reason != NULL)
- vio->close_reason = reason ;
+ /* Save the close reason for later, unless one is already set. */
+ if ((reason != NULL) && (vio->close_reason == NULL))
+ vio->close_reason = XSTRDUP(MTYPE_TMP, reason) ;
- /* Do the file side of things
+ /* Close the command loop -- if not already stopped (or closed !)
*
- * Note that half closing the file sets a new timeout, sets read off
- * and write on.
+ * If command loop is not already stopped, the if "curtains" will stop it
+ * instantly, otherwise will signal to the command loop to close, soonest.
*/
- uty_sock_half_close(&vio->sock) ;
- uty_set_monitor(vio, 0) ;
+ uty_cmd_loop_close(vio, curtains) ;
- /* 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.
+ /* Close all vin including the vin_base.
*
- * If is sitting on a "--more--" prompt, then exit the wait_more CLI.
+ * Note that the vin_base is closed, but is still on the vin stack.
*/
- vio->cli_more_enabled = 0 ;
+ do
+ uty_vin_pop(vio, true, NULL) ; /* final close, discard context */
+ while (vio->vin != vio->vin_base) ;
- if (vio->cli_more_wait)
- uty_cli_exit_more_wait(vio) ;
+ /* Close all the vout excluding the vout_base. */
+ while (vio->vout != vio->vout_base)
+ uty_vout_pop(vio, true) ; /* final close */
- /* 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 loop is still running, this is as far as can go.
*
- * If command is in progress, then this process will start when it
- * completes.
+ * The command loop will hit the vc_close_trap or execute CMD_CLOSE, and
+ * that will cause this function to be called again, in vc_stopped state.
+ *
+ * Save the close_reason for later.
*/
- 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) ;
-
- /* Move to the death watch list */
- sdl_del(vio_list_base, vio, vio_list) ;
- sdl_push(vio_death_watch, vio, vio_list) ;
+ if ((vio->state == vc_running) || (vio->state == vc_close_trap))
+ return ;
- vio->half_closed = 1 ;
-} ;
+ /* If the vout_base is not closed, try to output the close reason,
+ * if any.
+ */
+ if ((vio->close_reason != NULL) && (vio->vout_base->vout_state != vf_closed))
+ uty_vout_close_reason(vio->vout_base, vio->close_reason) ;
-/*------------------------------------------------------------------------------
- * 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() ;
+ /* Now final close the vout_base.
+ *
+ * Note that the vout_base will be closed, but on the vout stack with an
+ * empty obuf... just in case TODO ?
+ */
+ uty_vout_pop(vio, true) ; /* final close */
- /* 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) ;
+ assert(vio->obuf == vio->vout_base->obuf) ;
+ vio_fifo_clear(vio->obuf, true) ; /* and discard any marks */
- /* If not already closed, close. */
- if (!vio->closed)
+ /* All should now be very quiet indeed. */
+ if (vty_debug)
{
- 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 */
+ assert(vio->vin == vio->vin_base) ;
+ assert(vio->vin_depth == 0) ;
+ assert(vio->real_depth == 0) ;
- uty_sock_close(&vio->sock) ;
+ assert(vio->vout == vio->vout_base) ;
+ assert(vio->vout_depth == 0) ;
+ assert(vio->vin->vin_state == vf_closed) ;
+ assert(vio->vout->vout_state == vf_closed) ;
} ;
- /* 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_free_body(&vio->cli_prompt_for_node) ;
- qs_free_body(&vio->cl) ;
+ /* Can dispose of these now -- leave vin/vout for final disposition */
+ vio->vty->exec = cmd_exec_free(vio->vty->exec) ;
+ vio->ebuf = vio_fifo_free(vio->ebuf) ;
- {
- qstring line ;
- while ((line = vector_ream_keep(&vio->hist)) != 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.
+ /* Command loop is not running, so can place on death watch for final
+ * disposition.
*/
- if (!vio->cmd_in_progress)
+ if (vio->state == vc_stopped)
{
- qs_free_body(&vio->clx) ;
- 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)
-{
- vty_io vio ;
-
- VTY_LOCK() ;
-
- vio = vty->vio ;
-
- assert((vio->type != VTY_CONFIG_WRITE) && (vio->type != VTY_NONE)) ;
+ vio->state = vc_closed ;
- vio->real_type = vio->type ;
-
- vio->type = VTY_CONFIG_WRITE ;
- vio->file_fd = fd ;
- vio->file_error = 0 ;
+ sdl_del(vio_live_list, vio, vio_list) ;
+ sdl_push(vio_death_watch, vio, vio_list) ;
+ } ;
- VTY_UNLOCK() ;
+ assert(vio->state == vc_closed) ; /* thank you and good night */
} ;
/*------------------------------------------------------------------------------
- * Write away configuration file stuff -- all or just the full lump(s).
+ * Dispose unwanted vty.
*
- * Returns: > 0 => blocked
- * 0 => all gone (up to last lump if !all)
- * < 0 => failed -- see vio->file_error
+ * Called from deathwatch -- must already be removed from deathwatch list.
*/
-static int
-uty_config_write(vty_io vio, bool all)
+static vty_io
+uty_dispose(vty_io vio)
{
- int ret ;
-
VTY_ASSERT_LOCKED() ;
- if (vio->file_error == 0)
- {
- ret = vio_fifo_write_nb(&vio->cmd_obuf, vio->file_fd, all) ;
-
- if (ret < 0)
- vio->file_error = errno ;
- }
- else
- ret = -1 ;
-
- return ret ;
-} ;
+ assert(vio->state == vc_closed) ;
-/*------------------------------------------------------------------------------
- * 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 ;
+ /* Stop pointing at vout_base obuf */
+ vio->obuf = NULL ;
- VTY_LOCK() ;
+ /* Clear out vout and vin (may be the same) */
+ assert(vio->vin == vio->vin_base) ;
+ vio->vin_base = uty_vf_free(vio->vin_base) ;
- vio = vty->vio ;
+ assert(vio->vout == vio->vout_base) ;
+ if (vio->vout != vio->vin)
+ vio->vout_base = uty_vf_free(vio->vout_base) ;
- assert((vio->type == VTY_CONFIG_WRITE) && (vio->real_type != VTY_NONE)) ;
+ vio->vin = NULL ;
+ vio->vout = NULL ;
- uty_config_write(vio, true) ; /* write all that is left */
+ /* Remainder of contents of the vio */
+ vio->ebuf = vio_fifo_free(vio->ebuf) ;
- err = vio->file_error ;
+ /* Really cannot be a monitor any more ! */
+ assert(!vio->monitor) ;
+ vio->mbuf = vio_fifo_free(vio->mbuf) ;
- vio->type = vio->real_type ;
- vio->file_fd = -1 ;
- vio->file_error = 0 ;
+ XFREE(MTYPE_VTY, vio) ;
- VTY_UNLOCK() ;
-
- return err ;
+ return NULL ;
} ;
-/*==============================================================================
- * vio_sock level operations
- */
-
/*------------------------------------------------------------------------------
- * Initialise a new vio_sock structure.
+ * Close top of the vin stack and pop when done -- see uty_vf_read_close().
*
- * Requires that: the vio_sock structure is not currently in use.
+ * If succeeds in closing, unless is vin_base, pops the vin stack and if this
+ * is read-only will free the vio_vf and all its contents. (So if this is
+ * vin_base, it is left on the stack, but vf_closed/vf_closing.)
*
- * if fd >= 0 then: sock is open and ready read and write
- * otherwise: sock is not open
+ * If the given context is not NULL, having popped the vin stack, the context
+ * in the new top of stack is restored to the given context.
*
- * there are no errors, yet.
+ * On final close, will completely close the input, even if errors occur (and
+ * no errors are posted).
*
- * Sets timeout to no timeout at all -- timeout is optional.
+ * Returns: CMD_SUCCESS -- input completely closed
+ * CMD_WAITING -- waiting for input to close <=> not-blocking
+ * (not if final)
+ * CMD_IO_ERROR -- error or timeout
*
- * NB: MUST be in the CLI thread if the fd is >= 0 !
+ * NB: a uty_cmd_prepare() is required before command processing can continue.
*/
-static void
-uty_sock_init_new(vio_sock sock, int fd, void* info)
+extern cmd_return_code_t
+uty_vin_pop(vty_io vio, bool final, cmd_context context)
{
- VTY_ASSERT_LOCKED() ;
-
- if (fd >= 0)
- VTY_ASSERT_CLI_THREAD() ;
-
- memset(sock, 0, sizeof(struct vio_sock)) ;
+ cmd_return_code_t ret ;
- /* 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.
- *
- * 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.
- */
-static void
-uty_sock_restart_timer(vio_sock sock)
-{
VTY_ASSERT_LOCKED() ;
- VTY_ASSERT_CLI_THREAD() ;
- if (sock->v_timeout != 0)
+ ret = uty_vf_read_close(vio->vin, final) ;
+
+ if ((ret == CMD_SUCCESS) || final)
{
- assert(sock->action.timer.anon != NULL) ;
+ assert(vio->vin->vin_state == vf_closed) ;
- if (vty_cli_nexus)
+ if (vio->vin_depth > 1)
{
- 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) ;
- } ;
+ vio_vf vf ;
- sock->timer_running = 1 ;
- }
- else if (sock->timer_running)
- {
- if (vty_cli_nexus)
- {
- if (sock->qtr != NULL)
- qtimer_unset(sock->qtr) ;
+ vf = ssl_pop(&vf, vio->vin, vin_next) ;
+ --vio->vin_depth ;
+
+ if (vf->vout_state == vf_closed)
+ uty_vf_free(vf) ;
}
else
{
- if (sock->t_timer != NULL)
- thread_cancel (sock->t_timer) ;
+ assert(vio->vin == vio->vin_base) ;
+ vio->vin_depth = 0 ; /* may already have been closed */
} ;
- sock->timer_running = 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 (vio->real_depth > vio->vin_depth)
+ vio->real_depth = vio->vin_depth ;
- if (sock->fd < 0)
- return 0 ;
-
- if (on)
- {
- assert(sock->action.read.anon != NULL) ;
-
- 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) ;
-
- 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 (vio->vin->context != NULL)
{
- if (sock->t_read != NULL)
- thread_cancel (sock->t_read) ;
+ if (context != NULL)
+ vio->vin->context = cmd_context_restore(context, vio->vin->context);
+ else
+ vio->vin->context = cmd_context_free(vio->vin->context, false) ;
+ /* Not a copy */
} ;
} ;
- return on ;
+ return ret ;
} ;
/*------------------------------------------------------------------------------
- * Set write on/off
+ * Close top of the vout stack and pop when done -- see uty_vf_write_close().
+ *
+ * If is vout_base, does not completely close, unless is "final" -- if not
+ * final, CMD_SUCCESS means that the output buffers are empty and the
+ * vout_depth has been reduced, but the vio_vf is still writeable, held in
+ * vf_closing state.
+ *
+ * Unless is vout_base, pops the vout stack. If this is write-only will free
+ * the vio_vf and all its contents.
+ *
+ * If this is vout_base, does not actually close the vfd and does not close
+ * the vout side of the vf. This leaves an active vio->obuf (inter alia.)
+ *
+ * Before closing, discard anything after end_mark, then push any outstanding
+ * output.
*
- * Returns: the on/off state set
+ * 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 (except for vout_base).
+ *
+ * Returns: CMD_SUCCESS -- input completely closed
+ * CMD_WAITING -- waiting for input to close <=> not-blocking
+ * CMD_IO_ERROR -- error or timeout
+ *
+ * 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().
+ *
+ * However, ensures that vio->obuf is up to date !
*/
-static bool
-uty_sock_set_write(vio_sock sock, bool on)
+extern cmd_return_code_t
+uty_vout_pop(vty_io vio, bool final)
{
+ cmd_return_code_t ret ;
+
VTY_ASSERT_LOCKED() ;
- VTY_ASSERT_CLI_THREAD() ;
- if (sock->fd < 0)
- return 0 ;
+ ret = uty_vf_write_close(vio->vout, final) ;
- if (on)
+ if ((ret == CMD_SUCCESS) || final)
{
- assert(sock->action.write.anon != NULL) ;
-
- if (vty_cli_nexus)
- qps_enable_mode(sock->qf, qps_write_mnum, sock->action.write.qnexus) ;
- else
+ if (vio->vout_depth > 1)
{
- if (sock->t_write != NULL)
- thread_cancel(sock->t_write) ;
+ vio_vf vf ;
- sock->t_write = thread_add_write(vty_master,
- sock->action.write.thread, sock->info, sock->fd) ;
- } ;
- }
- else
- {
- if (vty_cli_nexus)
- qps_disable_modes(sock->qf, qps_write_mbit) ;
+ vf = ssl_pop(&vf, vio->vout, vout_next) ;
+ --vio->vout_depth ;
+
+ uty_vf_free(vf) ;
+ }
else
{
- if (sock->t_write != NULL)
- thread_cancel (sock->t_write) ;
- } ;
- } ;
+ assert(vio->vout == vio->vout_base) ;
+ if (final)
+ assert(vio->vout->vout_state == vf_closed) ;
- 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() ;
+ vio->vout_depth = 0 ; /* may already have been closed */
- uty_sock_set_read(sock, (ready == read_ready)) ;
- uty_sock_set_write(sock, (ready >= write_ready)) ;
-} ;
+ assert(vio->vin_depth == 0) ;
+ vio->vout->depth_mark = 0 ; /* align with the end stop */
+ } ;
-/*------------------------------------------------------------------------------
- * Set a new timer value.
- */
-extern void
-uty_sock_set_timer(vio_sock sock, unsigned long timeout)
-{
- VTY_ASSERT_LOCKED() ;
- VTY_ASSERT_CLI_THREAD() ;
+ vio->obuf = vio->vout->obuf ;
+ } ;
- sock->v_timeout = timeout ;
- if (sock->timer_running)
- uty_sock_restart_timer(sock) ;
+ return ret ;
} ;
/*------------------------------------------------------------------------------
- * Close given vty sock for reading.
+ * Try to output the close reason to the vout_base.
*
- * 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.
+ * This is the final act for the vout_base. Will attempt to output unless
+ * the thing is completely closed.
*
- * Structure is cleared of everything except the last error !
+ * Should by now not be any buffered output -- but if there is, that is
+ * discarded before the close reason is output.
*
- * NB: if there is a socket, MUST be in the CLI thread
+ * Any actual output may be done when the vout_base is finally closed.
*/
static void
-uty_sock_close(vio_sock sock)
+uty_vout_close_reason(vio_vf vf, const char* reason)
{
- 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 ;
- } ;
+ if ((vf->vout_state == vf_closed) || (reason == NULL) || (*reason == '\0'))
+ return ;
- sock->fd = -1 ;
+ vio_fifo_clear(vf->obuf, true) ; /* clear any markers, too */
- if (sock->t_read != NULL)
- thread_cancel(sock->t_write) ;
- if (sock->t_write != NULL)
- thread_cancel(sock->t_write) ;
+ if (vf->vout_type < VOUT_SPECIALS)
+ assert(vf->vfd != NULL) ; /* make sure */
- sock->t_read = NULL ;
- sock->t_write = NULL ;
+ switch(vf->vout_type)
+ {
+ case VOUT_NONE:
+ zabort("invalid VOUT_NONE") ;
+ break ;
- sock->info = NULL ;
- sock->action.read.anon = NULL ;
- sock->action.write.anon = NULL ;
- sock->action.timer.anon = NULL ;
+ case VOUT_TERM:
+ uty_term_close_reason(vf, reason) ;
+ break ;
- if (sock->qtr != NULL)
- qtimer_free(sock->qtr) ;
- if (sock->t_timer != NULL)
- thread_cancel(sock->t_timer) ;
+ case VOUT_VTYSH:
+ break ;
- sock->v_timeout = 0 ;
- sock->qtr = NULL ;
- sock->t_timer = NULL ;
-} ;
+ case VOUT_FILE:
+ case VOUT_PIPE:
+ case VOUT_SHELL_ONLY:
+ break ;
-/*------------------------------------------------------------------------------
- * 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() ;
+ case VOUT_CONFIG:
+ break ;
- /* can no longer be a monitor ! *before* any logging ! */
- uty_set_monitor(vio, 0) ;
+ case VOUT_DEV_NULL:
+ break ;
- /* 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()") ;
- } ;
+ case VOUT_STDOUT:
+ fprintf(stdout, "%% %s\n", reason) ;
+ break ;
- 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) ;
- } ;
+ case VOUT_STDERR:
+ fprintf(stderr, "%% %s\n", reason) ;
+ break ;
- return -1 ;
+ default:
+ zabort("unknown VOUT type") ;
+ } ;
} ;
/*==============================================================================
- * 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.
+ * vio_vf level operations
+ */
+
+/*------------------------------------------------------------------------------
+ * Create and initialise a new vio_vf structure.
*
- * If this write enters the "--more--" state, then will have tried to
- * write away the prompt.
+ * There are no errors, yet.
*
- * 2. invokes the CLI
+ * This leaves most things unset/NULL/false. Caller will need to:
*
- * Which will do either the standard CLI stuff or the special "--more--"
- * stuff.
+ * - uty_vin_push() and/or uty_vout_push()
*
- * 3. attempts to write any output there now is.
+ * - once those are done, the following optional items remain to be set
+ * if they are required:
*
- * If the CLI generated new output, as much as possible is written away
- * now.
+ * read_timeout -- default = 0 => no timeout
+ * write_timeout -- default = 0 => no timeout
*
- * If this write enters the "--more--" state, then it returns now_ready,
- * if the prompt was written away, which loops back to the CLI.
+ * - for pipes the child state needs to be set, and for out pipes the return
+ * state needs to be set in this vio_vf (the master) and in the next
+ * vout (the slave).
*
- * Note that this is arranging:
+ * 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.
*
- * a. to write away the "--more--" prompt as soon as the tranche of output to
- * which it refers, completes
+ * A VTY_STDOUT or a VTY_STDERR (output only, to the standard I/O) can
+ * be set up without an fd.
*
- * b. to enter the cli_more_wait CLI for the first time immediately after the
- * "--more--" prompt is written away.
+ * 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. The required VOUT_STDERR will be set by
+ * uty_vout_push().
*
- * The loop limits itself to one trache of command output each time.
+ * NB: if the parent vio is blocking, then the vf will be blocking, and so
+ * will any vfd. An individual vf may be set blocking by setting
+ * vfd_io_blocking in the io_type.
*
- * Resets the timer because something happened.
+ * The vty stuff opens all files, pipes etc. non-blocking. A non-blocking
+ * vf simulates blocking by local pselect. As far as the vfd level is
+ * concerned, once the file, pipe etc. is open, there is no difference
+ * between blocking and non-blocking except that non-blocking vfd are not
+ * allowed to set read/write ready.
*/
-static void
-uty_ready(vty_io vio)
+extern vio_vf
+uty_vf_new(vty_io vio, const char* name, int fd, vfd_type_t type,
+ vfd_io_type_t io_type)
{
- enum vty_readiness ready ;
+ vio_vf vf ;
VTY_ASSERT_LOCKED() ;
- vio->cmd_out_done = 0 ; /* not done any command output yet */
+ vf = XCALLOC (MTYPE_VTY, sizeof(struct vio_vf)) ;
- 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) ;
+ /* Zeroising the structure has set:
+ *
+ * vio = X -- set below
+ * name = X -- set below
+ *
+ * vin_type = VIN_NONE -- see uty_vin_push()
+ * vin_state = vf_closed -- see uty_vin_push()
+ * vin_next = NULL -- see uty_vin_push()
+ *
+ * context = NULL -- see uty_vin_new_context()
+ *
+ * cli = NULL -- no CLI, yet
+ *
+ * ibuf = NULL -- none, yet -- see uty_vin_push()
+ * cl = NULL -- none, yet -- see uty_vin_push()
+ * line_complete = false -- see uty_vin_push()
+ * line_number = 0 -- nothing yet
+ * line_step = 0 -- see uty_vin_push()
+ *
+ * vout_type = VOUT_NONE -- see uty_vout_push()
+ * vout_state = vf_closed -- see uty_vout_push()
+ * vout_next = NULL -- see uty_vout_push()
+ *
+ * pr_master = NULL -- none -- see uty_pipe_write_open()
+ *
+ * obuf = NULL -- none -- see uty_vout_push()
+ *
+ * depth_mark = 0 -- see uty_vout_push()
+ *
+ * vfd = NULL -- no vfd, yet
+ *
+ * blocking = X -- see below
+ * closing = false -- not on the closing list, yet.
+ *
+ * error_seen = 0 -- no error seen, yet
+ *
+ * read_timeout = 0 -- none
+ * write_timeout = 0 -- none
+ *
+ * child = 0 -- none )
+ * terminated = false -- not ) -- see uty_pipe_read/write_open()
+ * term_status = X -- none )
+ *
+ * pr_state = vf_closed -- no pipe return vfd
+ * -- see uty_pipe_read/write_open()
+ * pr_vfd = NULL -- no vfd -- see uty_pipe_read/write_open()
+ *
+ * pr_slave = NULL -- none -- see uty_pipe_read/write_open()
+ *
+ * pr_only = false -- see uty_pipe_read/write_open()
+ */
+ confirm((VIN_NONE == 0) && (VOUT_NONE == 0)) ;
+ confirm(vf_closed == 0) ;
+ confirm(QSTRING_INIT_ALL_ZEROS) ;
+
+ if (vio->blocking)
+ io_type |= vfd_io_blocking ; /* inherit blocking state */
+
+ vf->vio = vio ;
+ vf->blocking = (io_type & vfd_io_blocking) != 0 ;
- uty_sock_set_readiness(&vio->sock, ready) ;
- uty_sock_restart_timer(&vio->sock) ;
+ if (name != NULL)
+ vf->name = XSTRDUP(MTYPE_VTY_NAME, name) ;
+
+ if (fd >= 0)
+ vf->vfd = vio_vfd_new(fd, type, io_type, vf) ;
+
+ return vf ;
} ;
-/*==============================================================================
- * Reading from VTY_TERM.
+/*------------------------------------------------------------------------------
+ * Close the read side of the given vio_vf -- if can.
*
- * The select/pselect call-back ends up in uty_read_ready().
+ * 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.
*
- * Note that uty_write_ready() also calls uty_read_ready, in order to kick the
- * current CLI.
- */
-
-/*------------------------------------------------------------------------------
- * Callback -- qnexus: ready to read -> kicking CLI
+ * For not-final close, if there is any other related I/O, then waits until
+ * that has completed. For example, with a VIN_PIPE:
+ *
+ * * waits for any return input to complete, and pushes it to the relevant
+ * vout.
+ *
+ * * waits to collect the child, and its termination state.
+ *
+ * This means that a not-final close may return errors. For not-blocking vf
+ * may return waiting state. For blocking vf, may block and may later return
+ * timeout error.
+ *
+ * For a final close, if there is any related I/O then will attempt to complete
+ * it -- but will give up if would block. I/O errors on final close are
+ * ignored. A final close may be called in terminating state, so does not
+ * do any "vty_cmd_signal".
+ *
+ * Returns: CMD_SUCCESS -- is all closed
+ * CMD_WAITING -- cannot close at the moment <=> non-blocking
+ * (not if "final")
+ * CMD_IO_ERROR -- something went wrong
+ *
+ * NB: on "final" close returns CMD_SUCCESS no matter what happened, and all
+ * input will have been closed down, and the vf closed.
*/
-static void
-vty_read_qnexus(qps_file qf, void* file_info)
+static cmd_return_code_t
+uty_vf_read_close(vio_vf vf, bool final)
{
- vty_io vio = file_info;
+ cmd_return_code_t ret ;
- VTY_LOCK() ;
+ VTY_ASSERT_CAN_CLOSE_VF(vf) ;
- assert((vio->sock.fd == qf->fd) && (vio == vio->sock.info)) ;
+ ret = CMD_SUCCESS ;
- uty_ready(vio) ;
+ if (vf->vin_state == vf_closed)
+ return ret ; /* quit if already closed */
- VTY_UNLOCK() ;
-}
+ vf->vin_state = vf_closing ; /* TODO wipes out error etc ? */
-/*------------------------------------------------------------------------------
- * Callback -- threads: ready to read -> kicking CLI
- */
-static int
-vty_read_thread(struct thread *thread)
-{
- vty_io vio = THREAD_ARG (thread);
+ /* 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) ;
- VTY_LOCK() ;
+ /* Now the vin_type specific clean up. */
+ switch(vf->vin_type)
+ {
+ case VIN_NONE:
+ zabort("invalid VIN_NONE") ;
+ break ;
- assert(vio->sock.fd == THREAD_FD (thread) && (vio == vio->sock.info)) ;
+ case VIN_TERM:
+ ret = uty_term_read_close(vf, final) ;
+ break ;
- vio->sock.t_read = NULL ; /* implicitly */
- uty_ready(vio);
+ case VIN_VTYSH:
+ zabort("tba VIN_VTYSH") ;
+ break ;
- VTY_UNLOCK() ;
- return 0 ;
-}
+ case VIN_CONFIG:
+ ret = uty_config_read_close(vf, final) ;
+ break ;
-/*------------------------------------------------------------------------------
- * 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 ;
+ case VIN_FILE:
+ ret = uty_file_read_close(vf, final) ;
+ break ;
- if (!vio->sock.read_open)
- return -1 ; /* at EOF if not open */
+ case VIN_PIPE:
+ ret = uty_pipe_read_close(vf, final) ;
+ break ;
- 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") ;
+ case VIN_DEV_NULL:
+ ret = CMD_SUCCESS ;
+ break ;
- vio->sock.read_open = 0 ;
- keystroke_input(vio->key_stream, NULL, 0, steal) ;
+ default:
+ zabort("unknown VIN type") ;
+ } ;
- get = -1 ;
+ if ((ret == CMD_SUCCESS) || final)
+ {
+ vf->vin_state = vf_closed ;
+ assert(vf->pr_state == vf_closed) ;
} ;
- return get ;
+ return ret ;
} ;
-/*==============================================================================
- * The write sock action for VTY_TERM type VTY
+/*------------------------------------------------------------------------------
+ * Close the write side of the given vio_vf, if can.
*
- * There are two sets of buffering:
+ * Discards anything beyond the current end_mark, and clears the end_mark.
*
- * cli -- command line -- which reflects the status of the command line
+ * Pushes any outstanding output, and if is pipe will attempt to collect
+ * the child. On not-final close, if cannot complete everything, will return
+ * CMD_WAITING for non-blocking, or block (and may time out). On final close,
+ * will do as much as possible without blocking, and will then close even if
+ * there is outstanding output or child has not been collected.
*
- * cmd -- command output -- which is written to the file only while
- * cmd_out_enabled.
+ * For not-final close, for example, with a VOUT_PIPE:
*
- * The cli output takes precedence.
+ * * waits for any return input to complete, and pushes it to the relevant
+ * vout.
*
- * 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.
+ * * waits to collect the child, and its termination state.
*
- * If not cmd_in_progress, clears cli_blocked if both FIFOs are, or become,
- * empty.
+ * This means that a not-final close may return errors.
*
- * Note that if !write_open, or becomes !write_open, then the FIFOs are empty
- * and all output instantly successful.
+ * For a final close, if there is any related I/O then will attempt to complete
+ * it -- but will give up if would block. I/O errors on final close are
+ * ignored. A final close may be called in terminating state, so does not
+ * do any "vty_cmd_signal".
*
- * Sets write on if prevented from writing everything available for output
- * by write() threatening to block.
+ * If this is the vout_base, unless "final", does NOT actually close the vf or
+ * the vfd -- so the vout_base will still work -- but is marked vf_closing !
+ * The effect is, essentially, to try to empty out any buffers, but not to
+ * do anything that would prevent further output. This is used so that a
+ * command loop can close the vout_base in the usual way, waiting until all
+ * output is flushed, but when uty_close() is finally called, it can output
+ * any close reason there is to hand.
*
- * Returns: write_ready if should now set write on
- * now_ready if should loop back and try again
- * not_ready otherwise
+ * Returns: CMD_SUCCESS -- is all closed
+ * CMD_WAITING -- cannot close at the moment <=> non-blocking
+ * (not if "final")
+ * CMD_IO_ERROR -- something went wrong
+ *
+ * NB: on "final" all output will have been closed down, and the vfd closed,
+ * no matter what the return value says.
+ *
+ * NB: must not have open vins at this or a higher level in the stack.
+ *
+ * NB: does not at this stage discard the obuf.
*/
-static enum vty_readiness
-uty_write(vty_io vio)
+static cmd_return_code_t
+uty_vf_write_close(vio_vf vf, bool final)
{
- int ret ;
+ cmd_return_code_t ret ;
+ bool base ;
- VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CAN_CLOSE_VF(vf) ;
- 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 ;
- }
+ ret = CMD_SUCCESS ;
- /* Next: empty out the cli output */
- ret = vio_fifo_write_nb(&vio->cli_obuf, vio->sock.fd, true) ;
- if (ret != 0)
- break ;
+ if (vf->vout_state == vf_closed)
+ return ret ; /* quit if already closed */
- /* Finished now if not allowed to progress the command stuff */
- if (!vio->cmd_out_enabled)
- return not_ready ; /* done all can do */
+ vf->vout_state = vf_closing ; /* TODO wipes out error etc ? */
- /* Last: if there is something in the command buffer, do that */
- if (!vio_fifo_empty(&vio->cmd_obuf))
- {
- if (vio->cmd_out_done)
- break ; /* ...but not if done once */
-
- vio->cmd_out_done = 1 ; /* done this once */
+ base = (vf == vf->vio->vout_base) ;
- assert(!vio->cli_more_wait) ;
+ /* Must close vin before closing vout at the same level.
+ *
+ * Note that cannot currently be a pipe return slave, because if was
+ * slave to a VOUT_PIPE/VOUT_SHELL_ONLY that vout must have been closed
+ * already, and if was slave to a VIN_PIPE, then that too will have been
+ * closed already (because of the above).
+ */
+ assert( (vf->vio->vin_depth < vf->vio->vout->depth_mark)
+ || (vf->vio->vout_depth ==0) ) ;
+ assert(vf->pr_master == NULL) ;
- 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) ;
+ /* If there is anything in the obuf beyond the end_mark, then it is
+ * assumed to be surplus to requirements, and we clear the end_mark.
+ */
+ vio_fifo_back_to_end_mark(vf->obuf, false) ;
- /* 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 ;
- } ;
+ /* The vout_type specific close functions will attempt to write
+ * everything away.
+ *
+ * If "final", will only keep going until blocks -- at which point will
+ * bring everything to a shuddering halt.
+ */
+ switch(vf->vout_type)
+ {
+ case VOUT_NONE:
+ zabort("invalid VOUT_NONE") ;
+ break ;
- if (ret != 0)
- break ;
- }
+ case VOUT_TERM:
+ ret = uty_term_write_close(vf, final, base) ;
+ break ;
- /* Exciting stuff: there is nothing left to output...
- *
- * ... watch out for half closed state.
- */
- if (vio->half_closed)
- {
- if (vio->close_reason != NULL)
- {
- vio->cmd_in_progress = 1 ; /* TODO: not use vty_out ? */
+ case VOUT_VTYSH:
+ break ;
- 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) ;
+ case VOUT_FILE:
+ ret = uty_file_write_close(vf, final, base) ;
+ break ;
- vio->cmd_in_progress = 0 ;
+ case VOUT_PIPE:
+ case VOUT_SHELL_ONLY:
+ ret = uty_pipe_write_close(vf, final, base,
+ vf->vout_type == VOUT_SHELL_ONLY) ;
+ break ;
- vio->close_reason = NULL ; /* MUST discard now... */
- continue ; /* ... and write away */
- } ;
+ case VOUT_CONFIG:
+ ret = uty_file_write_close(vf, final, base) ; /* treat as file */
+ break ;
- if (!vio->closed) /* avoid recursion */
- uty_close(vio) ;
+ case VOUT_DEV_NULL:
+ case VOUT_STDOUT:
+ case VOUT_STDERR:
+ ret = CMD_SUCCESS ;
+ break ;
- return not_ready ; /* it's all over */
- } ;
+ default:
+ zabort("unknown VOUT type") ;
+ } ;
- /* For VTY_TERM: if the command line is not drawn, now is a good
- * time to do that.
- */
- if (vio->type == VTY_TERM)
- if (uty_cli_draw_if_required(vio))
- continue ; /* do that now. */
+ assert(vf->vin_state == vf_closed) ;
- /* There really is nothing left to output */
- return not_ready ;
- } ;
+ if (((ret == CMD_SUCCESS) && !base) || final)
+ {
- /* Arrives here if there is more to do, or failed (or was !write_open) */
+ assert(vf->vio->obuf == vf->obuf) ;
+ assert(vio_fifo_empty(vf->obuf)) ;
- 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") ;
+ if (vf->vout_type < VOUT_SPECIALS)
+ vf->vfd = vio_vfd_close(vf->vfd) ;
+ else
+ assert(vf->vfd == NULL) ;
- vio->sock.write_open = 0 ; /* crash close write */
+ vf->vout_state = vf_closed ;
} ;
- /* 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 */
+ return ret ;
} ;
/*------------------------------------------------------------------------------
- * Write as much as possible -- for "monitor" output.
- *
- * Outputs only:
+ * Free the given vio_vf structure and all its contents.
*
- * a. outstanding line control stuff.
+ * Expects the vfd to already have been closed, but will close it if not.
*
- * b. contents of CLI buffer
+ * Expects any cli to be closed, but will close it if not.
*
- * 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)
+ * Assumes has been removed from any and all lists !
*/
-static int
-uty_write_monitor(vty_io vio)
+static vio_vf
+uty_vf_free(vio_vf vf)
{
- VTY_ASSERT_LOCKED() ;
+ assert((vf->vin_state == vf_closed) && (vf->vout_state == vf_closed)
+ && (vf->pr_state == vf_closed)) ;
- if (!vio->sock.write_open)
- return -1 ;
+ XFREE(MTYPE_VTY_NAME, vf->name) ;
- if (vio->cmd_lc != NULL)
- {
- int ret ;
- ret = uty_write_lc(vio, &vio->cmd_obuf, vio->cmd_lc) ;
+ assert(vf->cli == NULL) ;
- if (ret != 0)
- return ret ;
- } ;
+ vf->ibuf = vio_fifo_free(vf->ibuf) ;
+ vf->cl = qs_reset(vf->cl, free_it) ;
+ vf->obuf = vio_fifo_free(vf->obuf) ;
- return vio_fifo_write_nb(&vio->cli_obuf, vio->sock.fd, true) ;
+ vf->context = cmd_context_free(vf->context, false) ; /* not a copy */
+
+ vf->vfd = vio_vfd_close(vf->vfd) ; /* for completeness */
+ vf->pr_vfd = vio_vfd_close(vf->pr_vfd) ; /* for completeness */
+
+ XFREE(MTYPE_VTY, vf) ;
+
+ return NULL ;
} ;
+
+
+
/*------------------------------------------------------------------------------
- * 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).
+ * Dealing with an I/O error on VTY socket
*
- * NB: expects that the sock is write_open
+ * If this is the first error for this VTY, produce suitable log message.
*
- * Returns: > 0 => blocked or completed one tranche
- * 0 => all gone
- * < 0 => failed
+ * If is a "monitor", turn that off, *before* issuing log message.
*/
-static int
-uty_write_fifo_lc(vty_io vio, vio_fifo vf, vio_line_control lc)
+extern int
+uty_vf_error(vio_vf vf, const char* what, int err)
{
- int ret ;
- char* src ;
- size_t have ;
+ vty_io vio = vf->vio ;
- /* 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 */
+ VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
- src = vio_fifo_get_rdr(vf, &have) ;
+ /* can no longer be a monitor ! *before* any logging ! */
+ uty_set_monitor(vio, off) ;
- while ((src != NULL) && (!lc->paused))
+ /* if this is the first error, log it */
+ if (vf->error_seen == 0)
{
- size_t take ;
- take = vio_lc_append(lc, src, have) ;
- src = vio_fifo_step_rdr(vf, &have, take) ;
- } ;
+ const char* type = "?" ; /* TODO */
- vio->cli_dirty = (lc->col != 0) ;
- /* Write the contents of the line control */
- ret = uty_write_lc(vio, vf, lc) ;
+ vf->error_seen = err ;
+ zlog(NULL, LOG_WARNING, "%s: %s failed on fd %d: %s",
+ type, what, vio_vfd_fd(vf->vfd), errtoa(err, 0).str) ;
+ } ;
- if (ret < 0)
- return ret ; /* give up now if failed. */
+ return -1 ;
+} ;
- 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).
+ * Set required read ready state. Applies the current read timeout.
*
- * NB: expects that the sock is write_open
+ * Forces off if: vf->vin_state != vf_open
*
- * NB: does nothing other than write() and buffer management.
+ * Does nothing if: vf->vin_state == vf_closed
+ * or: vf->vfd == NULL
*
- * Returns: > 0 => blocked
- * 0 => all gone
- * < 0 => failed
+ * NB: must NOT be a "blocking" vf
*/
-static int
-uty_write_lc(vty_io vio, vio_fifo vf, vio_line_control lc)
+extern void
+uty_vf_set_read(vio_vf vf, on_off_b how)
{
- int ret ;
-
- ret = vio_lc_write_nb(vio->sock.fd, lc) ;
-
- if (ret <= 0)
- vio_fifo_sync_rdr(vf) ; /* finished with FIFO contents */
+ if (vf->vin_state != vf_open)
+ how = off ;
- return ret ;
+ if (vf->vin_state != vf_closed)
+ vio_vfd_set_read(vf->vfd, how, vf->read_timeout) ;
} ;
/*------------------------------------------------------------------------------
- * Start command output -- clears down the line control.
+ * Set required read ready timeout -- if already read on, restart it.
*
- * Requires that that current line is empty -- restarts the line control
- * on the basis that is at column 0.
+ * If this is a blocking vf, will set the timeout value, but since can never
+ * be "read on", will never attempt to restart any timer !
*/
extern void
-uty_cmd_output_start(vty_io vio)
+uty_vf_set_read_timeout(vio_vf vf, vty_timer_time read_timeout)
{
- if (vio->cmd_lc != NULL)
- vio_lc_clear(vio->cmd_lc) ;
+ vf->read_timeout = read_timeout ;
+
+ if (!vf->blocking)
+ uty_vf_set_read(vf, on) ;
} ;
/*------------------------------------------------------------------------------
- * Set the effective height for line control (if any)
+ * Set required write ready state. Applies the current write timeout.
*
- * If using line_control, may enable the "--more--" output handling.
+ * Forces off if: vf->vout_state != vf_open
*
- * If not, want some limit on the amount of stuff output at a time.
+ * Does nothing if: vf->vout_state == vf_closed
+ * or: vf->vfd == NULL
*
- * Sets the line control window width and height.
- * Sets cli_more_enabled if "--more--" is enabled.
+ * NB: must NOT be a "blocking" vf
*/
extern void
-uty_set_height(vty_io vio)
+uty_vf_set_write(vio_vf vf, on_off_b how)
{
- bool on ;
-
- on = 0 ; /* default state */
-
- if ((vio->cmd_lc != NULL) && !vio->half_closed)
- {
- int height ;
-
- height = 0 ; /* default state */
-
- 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 ;
- } ;
-
- if (height > 0)
- on = 1 ; /* have a defined height */
- else
- height = 200 ; /* but no "--more--" */
+ if ((vf->vout_state != vf_open) && (vf->vout_state != vf_closing))
+ how = off ;
- vio_lc_set_window(vio->cmd_lc, vio->width, height) ;
- } ;
-
- vio->cli_more_enabled = on ;
+ if (vf->vout_state != vf_closed)
+ vio_vfd_set_write(vf->vfd, how, vf->write_timeout) ;
} ;
-/*==============================================================================
- * Timer for VTY_TERM (and VTY_SHELL_SERV).
- */
-
/*------------------------------------------------------------------------------
- * Timer has expired.
- *
- * If half_closed, then this is curtains -- have waited long enough !
+ * Set required write ready timeout -- if already write on, restart it.
*
- * 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.
+ * If this is a blocking vf, will set the timeout value, but since can never
+ * be "write on", will never attempt to restart any timer !
*/
-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)
+extern void
+uty_vf_set_write_timeout(vio_vf vf, vty_timer_time write_timeout)
{
- vty_io vio = THREAD_ARG (thread);
+ vf->write_timeout = write_timeout ;
- VTY_LOCK() ;
-
- vio->sock.t_timer = NULL ; /* implicitly */
-
- uty_timer_expired(vio) ;
-
- VTY_UNLOCK() ;
- return 0;
-}
+ if (!vf->blocking)
+ uty_vf_set_write(vf, on) ;
+} ;
/*==============================================================================
- * VTY Listener(s)
+ * Child care.
*
- * 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.
+ * Management of vio_child objects and the vio_childer_list.
+ *
+ * When child is registered it is placed on the vio_childer_list. It remains
+ * there until it is "collected", that is until a waitpid() has returned a
+ * "report" for the child.
+ *
+ * When a parent waits for a child to be collected, it may be in one of three
+ * states:
+ *
+ * TODO
+ *
+ * When a child is collected, or becomes overdue, the parent is signalled (if
+ * required) and the child->awaited state is cleared.
+ *
+ * When a parent "dismisses" a child, if it has not yet been collected it is
+ * "smacked" -- kill(SIGTERM) -- but kept on the register until it is
+ * collected. When a child which has been collected is dismissed it is freed.
+ *
+ * At "curtains" the register may contain children which have been dismissed,
+ * but not yet collected. Should NOT contain any children with living parents.
+ * All children remaining on the register are smacked, and all with no living
+ * parents are freed. (This could leave children on the register, but avoids
+ * the possibility of a dangling reference from a parent.)
*/
-
-#if defined(HAVE_IPV6) && !defined(NRL)
-# define VTY_USE_ADDRINFO 1
-#else
-# define VTY_USE_ADDRINFO 0
-#endif
+static void uty_child_collected(vio_child child, int report) ;
+static vty_timer_time vty_child_overdue(vio_timer timer, void* action_info) ;
+static void uty_child_signal_parent(vio_child child) ;
+static vio_child uty_child_free(vio_child child) ;
/*------------------------------------------------------------------------------
- * Open VTY listener(s)
+ * Set vty_child_signal_nexus() -- if required.
*
- * addr -- address ) to listen for VTY_TERM connections
- * port -- port )
- * path -- path for VTYSH connections -- if VTYSH_ENABLED
+ * Note that this is set/cleared under VTY_LOCK() and its own mutex. This
+ * allows it to be read under its own mutex and/or VTY_LOCK().
*/
extern void
-uty_open_listeners(const char *addr, unsigned short port, const char *path)
+uty_child_signal_nexus_set(vty_io vio)
{
VTY_ASSERT_LOCKED() ;
- /* If port is set to 0, do not listen on TCP/IP at all! */
- if (port)
- {
- int n ;
+ assert(vio->blocking) ;
- if (VTY_USE_ADDRINFO)
- n = uty_serv_sock_addrinfo(addr, port);
- else
- n = uty_serv_sock(addr, port);
+ if (!vty_is_cli_thread())
+ {
+ qpt_mutex_lock(vty_child_signal_mutex) ;
- if (n == 0)
- uzlog(NULL, LOG_ERR, "could not open any VTY listeners") ;
- }
+ vty_child_signal_nexus = qpn_find_self() ;
- /* If want to listen for vtysh, set up listener now */
- if (VTYSH_ENABLED && (path != NULL))
- uty_serv_vtysh(path) ;
+ qpt_mutex_unlock(vty_child_signal_mutex) ;
+ } ;
} ;
/*------------------------------------------------------------------------------
- * Close VTY listener
- *
- * addr -- address ) to listen for VTY_TERM connections
- * port -- port )
- * path -- path for VTYSH connections -- if VTYSH_ENABLED
+ * If there is a nexus to signal, clear the indicator and signal the
+ * associated thread.
*/
extern void
-uty_close_listeners(void)
+vty_child_signal_nexus_signal(void)
{
- vty_listener listener ;
+ qpt_mutex_lock(vty_child_signal_mutex) ;
- VTY_ASSERT_LOCKED() ;
+ if (vty_child_signal_nexus != NULL)
+ qpt_thread_signal(vty_child_signal_nexus->thread_id,
+ vty_child_signal_nexus->pselect_signal) ;
- while ((listener = ssl_pop(&listener, vty_listeners_list, next)) != NULL)
- {
- uty_sock_close(&listener->sock) ; /* no ceremony, no flowers */
- XFREE(MTYPE_VTY, listener) ;
- } ;
-} ;
+ qpt_mutex_unlock(vty_child_signal_mutex) ;
+}
/*------------------------------------------------------------------------------
- * Open listener(s) for VTY_TERM -- using getaddrinfo().
+ * Set vty_child_signal_nexus() -- if required.
*
- * Returns: number of listeners successfully opened.
+ * Note that this is set/cleared under VTY_LOCK() and its own mutex. This
+ * allows it to be read under its own mutex and/or VTY_LOCK().
*/
-static int
-uty_serv_sock_addrinfo (const char *hostname, unsigned short port)
+extern void
+uty_child_signal_nexus_clear(vty_io vio)
{
-#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);
+ assert(vio->blocking) ;
- if (ret != 0)
+ if (!vty_is_cli_thread())
{
- 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;
+ qpt_mutex_lock(vty_child_signal_mutex) ;
- n = 0 ;
- do
- {
- if ((ainfo->ai_family != AF_INET) && (ainfo->ai_family != AF_INET6))
- continue;
+ vty_child_signal_nexus = NULL ;
- 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 */
-}
+ qpt_mutex_unlock(vty_child_signal_mutex) ;
+ } ;
+} ;
/*------------------------------------------------------------------------------
- * Open listener(s) for VTY_TERM -- not using getaddrinfo() !
+ * New child.
*
- * Returns: number of listeners successfully opened.
+ * NB: must register child promptly (under VTY_LOCK, at same time as fork)
+ * in order to avoid missing the SIG_CHLD !
*/
-static int
-uty_serv_sock(const char* addr, unsigned short port)
+extern vio_child
+uty_child_register(pid_t pid, vio_vf parent)
{
- int ret;
- int n ;
- union sockunion su_addr ;
- struct sockaddr* sa ;
+ vio_child child ;
VTY_ASSERT_LOCKED() ;
- n = 0 ; /* nothing opened yet */
+ child = XCALLOC(MTYPE_VTY, sizeof(struct vio_child)) ;
- /* 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);
- } ;
+ /* Zeroising has set:
+ *
+ * list -- NULLs -- added to vio_childer_list, below
+ *
+ * parent -- NULL -- parent vf set, below
+ *
+ * pid -- X -- child pid set below
+ * collected -- false -- not yet collected
+ * report -- X -- not relevant until collected
+ *
+ * awaited -- false -- not waiting for child
+ * overdue -- false -- child not overdue
+ * timer -- NULL -- no waiting timer set
+ */
- /* 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
+ child->parent = parent ;
+ child->pid = pid ;
- /* If not used the address... something wrong */
- if (sa != NULL)
- uzlog(NULL, LOG_ERR, "could not use address %s, to listen for VTY", addr);
+ sdl_push(vio_childer_list, child, list) ;
- /* Done */
- return n ;
-}
+ return child ;
+} ;
/*------------------------------------------------------------------------------
- * Open a VTY_TERM listener socket.
+ * Set waiting for child to be collected.
*
- * 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'
+ * This is for !vf->blocking: set timer and leave, waiting for SIGCHLD event.
*/
-static int
-uty_serv_sock_open(sa_family_t family, int type, int protocol,
- struct sockaddr* sa, unsigned short port)
+extern void
+uty_child_awaited(vio_child child, vty_timer_time timeout)
{
- union sockunion su ;
- int sock ;
- int ret ;
+ VTY_ASSERT_CLI_THREAD_LOCKED() ;
- 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 (ret >= 0)
- ret = sockunion_listen (sock, 3);
+ assert(child->parent != NULL) ;
+ assert(!child->parent->blocking) ;
- if (ret < 0)
- {
- close (sock);
- return -1 ;
- }
+ child->awaited = true ;
- /* Socket is open -- set VTY Term listener going */
- uty_serv_start_listener(sock, VTY_TERM) ;
+ if (child->timer == NULL)
+ child->timer = vio_timer_init_new(NULL, vty_child_overdue, child) ;
- /* Return OK and signal whether used address or not */
- return (sa != NULL) ? 1 : 0 ;
+ vio_timer_set(child->timer, timeout) ;
} ;
/*------------------------------------------------------------------------------
- * Open a VTY_SHEL_SERV listener socket (UNIX domain).
+ * See if parent can collect child -- directly.
+ *
+ * This is for vf->blocking,
+ *
+ * If can, will collect now -- marking collected and taking off the
+ * vio_childer_list.
+ *
+ * If cannot immediately collect, if final mark overdue and return.
*
- * Returns: < 0 => failed
- * >= 0 => OK
+ * Otherwise wait for up to timeout seconds for a suitable SIGCHLD or related
+ * wake-up signal.
+ *
+ * NB: this blocks with the VTY_LOCK() in its hands !! But this is only
+ * required for configuration file reading... and timeout is limited.
+ *
+ * Returns: true <=> collected
+ * false => timed out or final
*/
-static int
-uty_serv_vtysh(const char *path)
+extern bool
+uty_child_collect(vio_child child, vty_timer_time timeout, bool final)
{
- int ret;
- int sock, sa_len, path_len ;
- struct sockaddr_un sa_un ;
- mode_t old_mask;
- struct zprivs_ids_t ids;
+ bool first ;
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 ;
- } ;
+ assert(child->parent != NULL) ;
+ assert(child->parent->blocking) ;
+ assert(child->timer == NULL) ;
- /* First of all, unlink existing socket */
- unlink (path);
+ assert(child->pid > 0) ;
- /* Make UNIX domain socket. */
- sock = socket (AF_UNIX, SOCK_STREAM, 0);
- if (sock < 0)
+ first = true ;
+ while (1)
{
- uzlog(NULL, LOG_ERR, "Cannot create unix stream socket: %s",
- errtoa(errno, 0).str) ;
- return -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);
+ pid_t pid ;
+ int report ;
- sa_len = SUN_LEN(&sa_un) ;
-
-#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
- sa_un.sun_len = sa_len ;
-#endif
+ qps_mini_t qm ;
+ sigset_t* sig_mask = NULL ;
- old_mask = umask (0007);
+ if (child->collected)
+ return true ; /* have collected */
- 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);
+ pid = waitpid(child->pid, &report, WNOHANG) ;
- if (ret >= 0)
- ret = set_nonblocking(sock);
-
- if (ret >= 0)
- {
- ret = listen (sock, 5);
- if (ret < 0)
- uzlog(NULL, LOG_ERR, "listen(fd %d) failed: %s", sock,
- errtoa(errno, 0).str) ;
- } ;
+ if (pid == child->pid)
+ {
+ /* Collected the child */
+ uty_child_collected(child, report) ;
- zprivs_get_ids(&ids);
+ return true ; /* have collected */
+ } ;
- 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) ;
- }
+ if (pid != 0)
+ {
+ int err = 0 ;
- umask (old_mask);
+ /* With the exception of EINTR, all other returns are, essentially,
+ * impossible...
+ *
+ * (1) ECHLD means that the given pid is not a child...
+ * ...which is impossible if not collected -- but treat as
+ * "overdue".
+ *
+ * (2) only other known error is EINVAL -- invalid options...
+ * absolutely impossible.
+ *
+ * (3) some pid other than the given child is an invalid response !
+ */
+ if (pid < 0)
+ {
+ if (errno == EINTR)
+ continue ;
- /* Give up now if failed along the way */
- if (ret < 0)
- {
- close (sock) ;
- return -1 ;
- } ;
+ err = errno ;
- /* Socket is open -- set VTY Term listener going */
- uty_serv_start_listener(sock, VTY_SHELL_SERV) ;
+ zlog_err("waitpid(%d) returned %s", child->pid,
+ errtoa(err, 0).str) ;
+ }
+ else
+ zlog_err("waitpid(%d) returned pid=%d", child->pid, pid) ;
- return 0 ;
-} ;
+ if (err != ECHILD)
+ zabort("impossible return from waitpid()") ;
-/*------------------------------------------------------------------------------
- * Socket is open -- set a VTY listener going
- *
- * Note that the vyt_listener structure is passed to the accept action function.
- */
-static void
-uty_serv_start_listener(int fd, enum vty_type type)
-{
- vty_listener listener ;
+ final = true ; /* treat ECHLD as last straw ! */
+ } ;
- listener = XCALLOC(MTYPE_VTY, sizeof (struct vty_listener));
+ /* Waiting for child. */
+ if (final)
+ {
+ child->overdue = true ;
+ return false ; /* overdue */
+ } ;
- ssl_push(vty_listeners_list, listener, next) ;
- uty_sock_init_new(&listener->sock, fd, listener) ;
+ /* Need to wait -- if this is the first time through, prepare for
+ * that.
+ */
+ if (first)
+ {
+ qps_mini_set(qm, -1, 0, 6) ;
- listener->type = type ;
+ if (vty_is_cli_thread())
+ sig_mask = NULL ;
+ else
+ sig_mask = vty_child_signal_nexus->pselect_mask ;
- if (vty_cli_nexus)
- listener->sock.action.read.qnexus = vty_accept_qnexus ;
- else
- listener->sock.action.read.thread = vty_accept_thread ;
+ first = false ;
+ } ;
- uty_sock_set_read(&listener->sock, on) ;
+ /* Wait on pselect. */
+ if (qps_mini_wait(qm, sig_mask, true) == 0)
+ final = true ; /* timed out => now final */
+ } ;
} ;
/*------------------------------------------------------------------------------
- * Accept action for the thread world -- create and dispatch VTY
+ * Dismiss child -- if not collected, smack but leave to be collected in
+ * due course (or swept up at "curtains").
*/
-static int
-vty_accept_thread(struct thread *thread)
+extern vio_child
+uty_child_dismiss(vio_child child, bool final)
{
- vty_listener listener = THREAD_ARG(thread) ;
- int result ;
+ VTY_ASSERT_LOCKED() ;
- VTY_LOCK() ;
+ if (child != NULL)
+ {
+ if (!child->collected)
+ {
+ assert(child->pid > 0) ;
- result = uty_accept(listener, THREAD_FD(thread));
+ kill(child->pid, SIGKILL) ; /* hasten the end */
- uty_sock_set_read(&listener->sock, on) ;
+ if (final)
+ {
+ assert(child->parent == NULL) ;
+ sdl_del(vio_childer_list, child, list) ;
+ child->collected = true ; /* forceably */
+ } ;
- VTY_UNLOCK() ;
- return result ;
-} ;
+ child->overdue = true ; /* too late for parent */
+ } ;
-/*------------------------------------------------------------------------------
- * Accept action for the qnexus world -- create and dispatch VTY
- */
-static void
-vty_accept_qnexus(qps_file qf, void* listener)
-{
- VTY_LOCK() ;
+ child->parent = NULL ; /* orphan from now on */
+ child->awaited = false ; /* nobody waiting */
- uty_accept(listener, qf->fd);
+ if (child->collected)
+ uty_child_free(child) ;
+ } ;
- VTY_UNLOCK() ;
-}
+ return NULL ;
+} ;
/*------------------------------------------------------------------------------
- * Accept action -- create and dispatch VTY_TERM or VTY_SHELL_SERV
+ * At "curtains" -- empty out anything left in the child register.
+ *
+ * The only children that can be left are dismissed children that have yet to
+ * be collected.
*/
-static int
-uty_accept(vty_listener listener, int listen_sock)
+extern void
+vty_child_close_register(void)
{
- 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") ;
- } ;
+ while (vio_childer_list != NULL)
+ uty_child_dismiss(vio_childer_list, true) ; /* final */
} ;
/*------------------------------------------------------------------------------
- * Accept action -- create and dispatch VTY_TERM
+ * See whether any children are ready for collection, and check each one
+ * against the register.
+ *
+ * Perform waitpid( , , WNOHANG) until no child is returned, and process
+ * each one against the register.
+ *
+ * The is done when a SIGCHLD is route through the event mechanism.
+ *
+ * If another SIGCHLD occurs while this is being done, that will later cause
+ * another call of this function -- at worst it may find that the child in
+ * question has already been collected.
+ *
+ * This is also done when about to block waiting for a child.
+ *
+ * Set any children that can be collected, collected and signal to any parents
+ * that their children are now ready.
*/
-static int
-uty_accept_term(vty_listener listener)
+extern void
+uty_sigchld(void)
{
- 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) ;
+ VTY_ASSERT_CLI_THREAD_LOCKED() ;
- 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)
+ while (1)
{
- /* 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 */
+ vio_child child ;
+ pid_t pid ;
+ int report ;
- prefix_free (p);
+ pid = waitpid(-1, &report, WNOHANG) ;
- 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) ;
+ if (pid == 0)
+ break ;
- /* All set -- create the VTY_TERM */
- uty_new_term(sock_fd, &su);
+ if (pid < 0)
+ {
+ if (errno == EINTR)
+ continue ; /* loop on "Interrupted" */
- /* Log new VTY */
- uzlog (NULL, LOG_INFO, "Vty connection from %s (fd %d)", sutoa(&su).str,
- sock_fd) ;
+ if (errno != ECHILD) /* returns ECHLD if no children */
+ zlog_err("waitpid(-1) returned %s", errtoa(errno, 0).str) ;
- return 0;
-}
+ break ;
+ } ;
-/*------------------------------------------------------------------------------
- * 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 ;
+ child = vio_childer_list ;
+ while (1)
+ {
+ if (child == NULL)
+ {
+ zlog_err("waitpid(-1) returned pid %d, which is not registered",
+ pid) ;
+ break ;
+ } ;
- VTY_ASSERT_LOCKED() ;
+ if (child->pid == pid)
+ {
+ /* Have collected child.
+ *
+ * Remove from the vio_childer_list, set collected flag.
+ *
+ * We can leave any timer object -- if it goes off it will be
+ * ignored, because the child is no longer awaited. Timer will
+ * be discarded when the child is dismissed/freed.
+ *
+ * If no parent, can free child object now.
+ */
+ uty_child_collected(child, report) ;
- client_len = sizeof(client);
- memset (&client, 0, client_len);
+ if (child->parent == NULL)
+ uty_child_free(child) ;
+ else if (child->awaited)
+ uty_child_signal_parent(child) ;
- sock_fd = accept(listener->sock.fd, (struct sockaddr *) &client,
- (socklen_t *) &client_len) ;
+ break ;
+ } ;
- 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 ;
+ child = sdl_next(child, list) ;
+ } ;
} ;
-
- /* 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"
+ * Set the child collected and set the report.
*
- * 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.
+ * Remove from the vio_childer_list -- is now either back in the hands of the
+ * parent, or ready to be freed.
*/
static void
-utysh_read_ready(vty_io vio)
+uty_child_collected(vio_child child, int report)
{
- uty_sock_set_read(&vio->sock, off) ;
+ assert(!child->collected) ; /* can only collect once */
- /* 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.
- */
+ sdl_del(vio_childer_list, child, list) ;
+
+ child->collected = true ;
+ child->report = report ;
} ;
/*------------------------------------------------------------------------------
- * Callback -- qnexus: ready to read -> kicking the "SHELL_SERV CLI"
+ * Set child as overdue -- vio_timer action routine.
+ *
+ * NB: the timer may go off after the child has been collected, but before the
+ * parent has got round to stopping the timer.
*/
-static void
-vtysh_read_qnexus(qps_file qf, void* file_info)
+static vty_timer_time
+vty_child_overdue(vio_timer timer, void* action_info)
{
- vty_io vio = file_info;
+ vio_child child ;
VTY_LOCK() ;
- assert((vio->sock.fd == qf->fd) && (vio == vio->sock.info)) ;
+ child = action_info ;
+ assert(timer == child->timer) ;
- utysh_read_ready(vio) ;
+ if (child->awaited)
+ {
+ child->overdue = true ;
+ uty_child_signal_parent(child) ;
+ } ;
VTY_UNLOCK() ;
-}
+
+ return 0 ; /* stop timer */
+} ;
/*------------------------------------------------------------------------------
- * Callback -- threads: ready to read -> kicking the "SHELL_SERV CLI"
+ * Signal that child is ready -- collected or overdue.
+ *
+ * Must be "awaited" -- so not "blocking"
*/
-static int
-vtysh_read_thread(struct thread *thread)
+static void
+uty_child_signal_parent(vio_child child)
{
- vty_io vio = THREAD_ARG (thread);
-
- VTY_LOCK() ;
-
- assert(vio->sock.fd == THREAD_FD (thread) && (vio == vio->sock.info)) ;
+ assert(child->awaited && (child->parent != NULL)) ;
- vio->sock.t_read = NULL ; /* implicitly */
- utysh_read_ready(vio);
+ assert(!child->parent->vio->blocking) ;
- VTY_UNLOCK() ;
- return 0 ;
-}
+ child->awaited = false ;
+ uty_cmd_signal(child->parent->vio, CMD_SUCCESS) ;
+} ;
/*------------------------------------------------------------------------------
- * 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)
+ * Free the child -- caller must ensure that any parent has disowned the child,
+ * and that it is collected (so not on the vio_childer_list).
*/
-extern int
-utysh_read (vty_io vio, qstring cl, qstring buf)
+static vio_child
+uty_child_free(vio_child child)
{
- int get ;
- char* cp ;
- char* ep ;
- size_t have ;
-
- while (1)
+ if (child != NULL)
{
- /* 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' */
+ assert(child->collected && (child->parent == NULL)) ;
- 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 <<<<<<<<<<<<< */
- } ;
+ child->timer = vio_timer_reset(child->timer, free_it) ;
+ XFREE(MTYPE_VTY, child) ; /* sets child = NULL */
} ;
+
+ return child ;
} ;
/*==============================================================================
- * 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 ??
+ * VTY Listener(s)
*
- * 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.
+ * Have listeners for VTY_TERMINAL and VTY_SHELL_SERVER types of VTY.
*/
+/* List of listeners so can tidy up. */
+static vio_listener vty_listeners_list = NULL ;
+
/*------------------------------------------------------------------------------
- * Output logging information to all vty which are set to "monitor".
+ * Open VTY listener(s) for VTY_TERMINAL and VTY_SHELL_SERVER.
+ *
+ * addr -- address ) to listen for VTY_TERMINAL connections
+ * port -- port )
+ * path -- path for VTY_SHELL_SERVER connections -- if VTYSH_ENABLED
*/
extern void
-uty_log(struct logline* ll, struct zlog *zl, int priority,
- const char *format, va_list va)
+uty_open_listeners(const char *addr, unsigned short port, const char *path)
{
- 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 */
+ /* If port is set to 0, do not listen for VTY_TERMINAL at all! */
+ if (port)
+ uty_term_open_listeners(addr, port) ;
- uty_cli_pre_monitor(vio, ll->len - 2) ; /* claim the console */
+#if 0
+ /* If want to listen for vtysh, set up listener now */
+ if (VTYSH_ENABLED && (path != NULL))
+ uty_serv_vtysh(path) ;
+#endif
+} ;
- 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) ;
- } ;
- } ;
+/*------------------------------------------------------------------------------
+ * Create listener and set it ready to accept.
+ *
+ * Adds to list of listeners for close down.
+ */
+extern void
+uty_add_listener(int fd, vio_vfd_accept* accept_action)
+{
+ vio_listener vl ;
- if (ret != 0)
- /* need to prod */ ;
+ VTY_ASSERT_LOCKED() ;
- if (ret >= 0)
- vio->monitor_busy = 0 ;
- } ;
+ vl = vio_listener_new(fd, accept_action) ;
- vio = sdl_next(vio, mon_list) ;
- } ;
+ ssl_push(vty_listeners_list, vl, next) ;
} ;
/*------------------------------------------------------------------------------
- * Async-signal-safe version of vty_log for fixed strings.
- *
- * This is last gasp operation.
+ * Close all VTY listeners
*/
-void
-vty_log_fixed (const char *buf, size_t len)
+extern void
+uty_close_listeners(void)
{
- vty_io vio ;
+ vio_listener listener ;
- /* 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) ;
+ VTY_ASSERT_LOCKED() ;
- vio = sdl_next(vio, mon_list) ;
+ while ((listener = ssl_pop(&listener, vty_listeners_list, next)) != NULL)
+ {
+ vio_listener_close(listener) ; /* no ceremony, no flowers */
} ;
} ;
diff --git a/lib/vty_io.h b/lib/vty_io.h
index 19689853..24790526 100644
--- a/lib/vty_io.h
+++ b/lib/vty_io.h
@@ -25,286 +25,518 @@
#ifndef _ZEBRA_VTY_IO_H
#define _ZEBRA_VTY_IO_H
-#include <stdbool.h>
-#include <errno.h>
+//#include "zebra.h"
+#include "misc.h"
+//#include <errno.h>
-#include "uty.h"
-#include "vty.h"
+#include "vty_local.h"
+#include "command_local.h"
+#include "vty_io_basic.h"
#include "vio_fifo.h"
-#include "vio_lines.h"
-#include "keystroke.h"
#include "thread.h"
-#include "command.h"
+#include "command_execute.h"
#include "qstring.h"
+#include "list_util.h"
/*==============================================================================
- * Here are structures and other definitions which are shared by:
+ * Structures and other definitions for the top level VTY I/O.
*
- * vty.c -- the main vty handler
- * vty_cli.c -- which handles the command line stuff
- * vty_io.c -- ....
- *
- * The "struct vty" is used extensively across the Quagga daemons, where it
- * has two functions relating to command handling as:
- *
- * 1) a "file handle" for output produced by commands
- *
- * 2) the holder of some context -- notably the current command "node" -- for
- * command execution to use
- *
- * The bulk of "struct vty" is, therefore, private to vty.c and is factored
- * out into the "struct vty_io".
- *
- * To reduce the size of vty.c, some groups of functions are separated into:
- *
- * vty_cli.c -- which looks after the keystroke by keystroke handling
- * of the command line.
+ * There is one struct vty_io per VTY, which contains, inter alia, the vin
+ * and vout stacks.
*
+ * The vin and vout stacks contain one or more struct vty_vf -- one per
+ * input and/or output associated with the VTY.
*/
-/*------------------------------------------------------------------------------
- * VTY sock structure
- *
- * Used for VTY_TERM and VTY_SHELL_SERV VTY types, which are attached to TCP
- * and UNIX sockets, respectively.
- *
- * Also used for the associated listeners.
- */
+enum
+{
+ VTY_WATCH_DOG_INTERVAL = 5, /* interval between barks */
-typedef int thread_action(struct thread *) ;
+ VTY_HALF_CLOSE_TIMEOUT = 120, /* timeout after half_close */
-union sock_action
-{
- qps_action* qnexus ;
- thread_action* thread ;
- void* anon ;
+ VTY_TIMEOUT_DEFAULT = 600, /* terminal timeout value */
} ;
-union timer_action
+/*------------------------------------------------------------------------------
+ * VTY VIN and OUT types
+ */
+enum vio_in_type /* Command input */
{
- qtimer_action* qnexus ;
- thread_action* thread ;
- void* anon ;
+ VIN_NONE = 0, /* not a valid input type */
+
+ VIN_TERM, /* telnet terminal */
+ VIN_VTYSH, /* vty_shell input */
+
+ VIN_FILE, /* ordinary file input */
+ VIN_PIPE, /* pipe (from child process) */
+
+ 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 ;
-struct vio_sock_actions
+enum vio_out_type /* Command output */
{
- union sock_action read ;
- union sock_action write ;
- union timer_action timer ;
+ VOUT_NONE = 0, /* not a valid output type */
+
+ VOUT_TERM, /* a telnet terminal */
+ VOUT_VTYSH, /* a vty_shell output pipe */
+
+ VOUT_FILE, /* ordinary file */
+ VOUT_PIPE, /* pipe (to child process, and back again) */
+
+ VOUT_CONFIG, /* config file */
+
+ /* The VOUT types >= VOUT_SPECIALS do not have an associated fd.
+ *
+ * These can coexist with a VIN which does have an associated fd.
+ */
+ VOUT_SPECIALS, /* all special from now on */
+
+ VOUT_DEV_NULL = VOUT_SPECIALS,
+ /* black hole output */
+
+ VOUT_SHELL_ONLY, /* pipe (but only back from child process */
+
+ VOUT_STDOUT, /* stdout */
+ VOUT_STDERR, /* stderr */
};
+typedef enum vio_out_type vio_out_type_t ;
-typedef struct vio_sock* vio_sock ;
-struct vio_sock
+/*------------------------------------------------------------------------------
+ * State of a vf -- has separate state for vin/vout.
+ */
+enum vf_state
{
- int fd ;
+ vf_closed = 0, /* the vf has not been opened, or has been
+ * completely closed -- there will be no vfd. */
+
+ vf_open, /* the vf has been opened, and any required vfd
+ * is open and I/O is possible. */
+
+ vf_eof, /* for a vin: end of file has been reached or
+ * input has been terminated. The vfd may have
+ * been closed -- but in any case no further
+ * I/O should be attempted, and any buffered
+ * input will be discarded. */
+
+ vf_closing, /* for a vout: open, but in process of closing.
+ * May still output stuff. */
+
+ vf_error, /* an I/O error has been reported.
+ * The vfd may or may not have been closed and
+ * I/O may or may not be possible TODO */
+
+ vf_timed_out, /* a timout has been reported.
+ * The vfd may or may not have been closed and
+ * I/O may or may not be possible TODO */
+} ;
+typedef enum vf_state vf_state_t ;
- void* info ; /* for action routines */
+/*------------------------------------------------------------------------------
+ * vio_child structure.
+ *
+ * Lives on the vio_childer_list until collected or "curtains".
+ *
+ */
+typedef enum vio_child_await vio_child_await_t ;
- struct vio_sock_actions action ;
+struct vio_vf ; /* Forward reference */
+typedef struct vio_vf* vio_vf ;
- bool read_open ; /* read returns 0 if not open */
- bool write_open ; /* write completes instantly if not open */
- int error_seen ; /* non-zero => failed */
+typedef struct vio_child* vio_child ;
- qps_file qf ; /* when running qnexus */
+struct vio_child
+{
+ struct dl_list_pair(vio_child) list ; /* in the list of children */
- struct thread *t_read; /* when running threads */
- struct thread *t_write;
+ vio_vf parent ;
- unsigned long v_timeout; /* time-out in seconds -- 0 => none */
- bool timer_running ; /* true when timer is running */
+ pid_t pid ;
+ bool collected ; /* waitpid() done */
+ int report ; /* from waitpid() */
- qtimer qtr; /* when running qnexus */
- struct thread *t_timer; /* when running threads */
+ bool overdue ; /* patience exhausted */
+ bool awaited ; /* if child is awaited -- !vf->blocking */
+ vio_timer timer ; /* limit the waiting time */
} ;
-enum
+/*------------------------------------------------------------------------------
+ * vty_vf -- "vty file" structure
+ *
+ * A vio_vf may be a read, write or read/write object.
+ *
+ * All I/O is via vio_vfd objects, except for VOUT_STDOUT and VOUT_STDERR.
+ * The vio_vfd layer hides the differences between the qpthreads an legacy
+ * thread environments.
+ *
+ * The VOUT_STDOUT and VOUT_STDERR are handled as direct output to the standard
+ * i/o file handles. In the case of a VTY_CONFIG_READ, the vin is VIN_CONFIG
+ * and the vout is VOUT_STDOUT, and these can share a single vty_vf.
+ *
+ * Also used for the associated listeners.
+ */
+struct vty_io ; /* Forward reference */
+typedef struct vty_io* vty_io ;
+
+struct vio_vf
{
- on = true,
- off = false
+ 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 */
+
+ cmd_context context ; /* pushed exec->context. */
+
+ struct vty_cli* cli ; /* NULL if not a VTY_TERMINAL ! */
+
+ vio_fifo ibuf ; /* input fifo (if required) */
+
+ qstring cl ; /* command line buffer */
+ bool line_complete ; /* false => line in construction */
+ uint line_number ; /* number of first line in cl */
+ uint line_step ; /* number of real lines in cl */
+
+ /* Output side. */
+
+ vio_out_type_t vout_type ;
+ vf_state_t vout_state ;
+ vio_vf vout_next ; /* list of outputs */
+
+ vio_vf pr_master ; /* receiving return stuff from there */
+
+ vio_fifo obuf ; /* output fifo (if required) */
+
+ uint depth_mark ; /* depth of this vout */
+
+ /* I/O */
+
+ vio_vfd vfd ; /* vty_io_basic "file descriptor" */
+
+ bool blocking ; /* using blocking I/O (config read) */
+
+ int error_seen ; /* non-zero => failed */
+
+ vty_timer_time read_timeout ;
+ vty_timer_time write_timeout ;
+
+ /* Pipe extras */
+
+ vio_child child ; /* state of child */
+
+ vf_state_t pr_state ; /* iff pipe */
+ vio_vfd pr_vfd ; /* if pr_state == vf_open */
+
+ vio_vf pr_slave ; /* sending return stuff to there */
+
+ bool pr_only ; /* no vfd */
} ;
-enum vty_readiness /* bit significant */
+enum vty_readiness /* bit significant */
{
- not_ready = 0,
- read_ready = 1,
- write_ready = 2, /* takes precedence */
- now_ready = 4
+ not_ready = 0,
+ read_ready = BIT(0),
+ write_ready = BIT(1), /* may take precedence */
} ;
+typedef enum vty_readiness vty_readiness_t ;
/*------------------------------------------------------------------------------
- * The vty_io structure
+ * State of a vty command loop.
*/
+enum vc_state
+{
+ vc_null = 0, /* the command loop has not yet been entered. */
-struct vty_io {
- struct vty* vty ; /* the related vty */
- char *name ; /* for VTY_TERM is IP address) */
+ vc_running, /* the command loop is running, and the vty is
+ * in its hands. */
- /* List of all vty_io objects */
- struct dl_list_pair(vty_io) vio_list ;
+ vc_io_error_trap, /* the command loop is running, but an I/O
+ * error has been posted. */
- /* List of all vty_io that are in monitor state */
- struct dl_list_pair(vty_io) mon_list ;
+ vc_close_trap, /* the command loop is running, but the vty is
+ * reset and must be closed */
- /* VTY type and sock stuff */
- enum vty_type type;
+ vc_waiting, /* the command loop is waiting for I/O. */
- struct vio_sock sock ; /* for VTY_TERM and VTY_SHELL_SERV */
+ vc_stopped, /* the command loop has stopped, the vty is due
+ to be closed (loop cannot run again) */
- bool half_closed ; /* => on death watch list */
- bool closed ; /* => all I/O terminated
- will also be half_closed */
-
- const char* close_reason ; /* message to be sent, once all other
- output has completed, giving reason
- for closing the VTY. */
+ vc_closed, /* the command loop has finished and the vty
+ * has been closed. */
+} ;
+typedef enum vc_state vc_state_t ;
- /* When writing configuration file */
- enum vty_type real_type ;
+/*------------------------------------------------------------------------------
+ * 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) TODO
+ * 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". TODO
+ *
+ * - 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 ! */
- int file_fd ;
- int file_error ;
+struct vty_io /* typedef appears above */
+{
+ vty vty ; /* the related vty */
- /*--------------------------------------------------------------------*/
- /* Command line and related state */
+ vio_vf vin ; /* vin stack */
+ vio_vf vin_base ;
+ uint vin_depth ;
- keystroke_stream key_stream ;
+ uint real_depth ; /* less than vin_depth when closing */
- /* cli_drawn <=> the current prompt and user input occupy the current
- * line on the screen.
- *
- * cli_dirty <=> the last command output did not end with a newline.
- *
- * If cli_drawn is true, the following are valid:
- *
- * cli_prompt_len -- the length of the prompt part.
- * (will be the "--more--" prompt in cli_more_wait)
- *
- * cli_extra_len -- the length of any ^X at the cursor position
- * (for when blocked waiting for queued command)
- *
- * cli_echo_suppress -- the user part of the command line is suppressed
- *
- * NB: cli_echo_suppress is only used for password entry.
- */
- bool cli_drawn ;
- bool cli_dirty ;
+ vio_vf vout ; /* vout stack */
+ vio_vf vout_base ;
+ uint vout_depth ;
- int cli_prompt_len ;
- int cli_extra_len ;
+ uint depth_mark ; /* vin_depth before pipes opened */
- bool cli_echo_suppress ;
+ /* Error handling */
+ bool err_hard ; /* eg I/O error */
+ vio_fifo ebuf ; /* buffer for error message */
- /* "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 ;
+ /* List of all vty_io objects */
+ struct dl_list_pair(vty_io) vio_list ;
- /* State of the CLI
+ /* State
+ *
+ * "blocking" is set for configuration reading VTY, so that everything is
+ * done with blocking I/O.
*
- * 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
+ * "state" as described above.
*/
- bool cli_blocked ;
- bool cmd_in_progress ;
- bool cmd_out_enabled ;
- bool cli_more_wait ;
+ bool blocking ; /* => all I/O is blocking. */
- /* This is used to control command output, so that each write_ready event
- * generates at most one tranche of output.
- */
- bool cmd_out_done ;
+ vc_state_t state ;
- /* This is set only if the "--more--" handling is enabled */
- bool cli_more_enabled ;
+ char* close_reason ; /* MTYPE_TMP (if any) */
- /* Command Line(s)
- *
- * cli_do -- when current command being prepared is completed (by
- * CR/LF or otherwise) this says what there now is to be done.
- *
- * cl -- current command line being prepared.
+ /* For ease of output, pointer to current vout->obuf
*
- * 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.
+ * Even when the vty is almost closed, there will remain a valid obuf,
+ * though anything sent to it under those conditions will be discarded.
*/
- enum cli_do cli_do ;
-
- qstring_t cl ;
- qstring_t clx ;
+ vio_fifo obuf ;
- /* CLI output buffering */
- vio_fifo_t cli_obuf ;
-
- /* Command output buffering */
- vio_fifo_t cmd_obuf ;
+ /* The following is for "vty monitor".
+ *
+ * With the exception of the "monitor" flag, need the LOG_MUTEX in order
+ * to change any of this.
+ */
+ bool monitor ; /* is in monitor state */
- vio_line_control cmd_lc ;
+ bool mon_kick ; /* vty needs a kick */
+ int maxlvl ; /* message level wish to see */
- /* Failure count for login attempts */
- int fail;
+ vio_fifo mbuf ; /* monitor output pending */
- /* History of commands */
- vector_t hist ;
- int hp ; /* History lookup current point */
- int hindex; /* History insert end point */
+ /* List of all vty_io that are in monitor state */
+ struct dl_list_pair(vty_io) mon_list ;
+} ;
- /* Window width/height as reported by Telnet. 0 => unknown */
- int width;
- int height;
+/*==============================================================================
+ * Assertions for suitable state to close things !
+ */
+Inline void
+VTY_ASSERT_CAN_CLOSE(vty vty)
+{
+ if (vty_debug)
+ {
+ VTY_ASSERT_LOCKED() ;
- /* Configure lines. */
- int lines;
- bool lines_set ; /* true <=> explicitly set */
+ if (!vty->vio->blocking && !vty_is_cli_thread())
+ VTY_ASSERT_FAILED() ;
+ } ;
+} ;
- /* Terminal monitor. */
- bool monitor ;
- bool monitor_busy ;
+Inline void
+VTY_ASSERT_CAN_CLOSE_VF(vio_vf vf)
+{
+ if (vty_debug)
+ {
+ VTY_ASSERT_LOCKED() ;
- /* In configure mode. */
- bool config;
+ if (!vf->blocking && !vty_is_cli_thread())
+ VTY_ASSERT_FAILED() ;
+ } ;
} ;
/*==============================================================================
* Functions
*/
-extern struct vty* uty_new (enum vty_type type, int sock_fd) ;
+extern int uty_out (vty_io vio, const char* format, ...) PRINTF_ATTRIBUTE(2, 3);
+Inline int uty_vprintf(vty_io vio, const char *format, va_list args) ;
+
+Inline void uty_out_clear(vty_io vio) ;
+Inline void uty_out_accept(vty_io vio) ;
+Inline void uty_out_reject(vty_io vio) ;
+
+extern vty uty_new (vty_type_t type, node_type_t node) ;
+extern void uty_close(vty_io vio, const char* reason, bool curtains) ;
+
+extern void uty_set_timeout(vty_io vio, vty_timer_time timeout) ;
+
+extern void uty_vin_new_context(vty_io vio, cmd_context context,
+ qpath file_here) ;
+extern void uty_vin_push(vty_io vio, vio_vf vf, vio_in_type_t type,
+ vio_vfd_action* read_action,
+ vio_timer_action* read_timer_action,
+ usize ibuf_size) ;
+extern void uty_vout_push(vty_io vio, vio_vf vf, vio_out_type_t type,
+ vio_vfd_action* write_action,
+ vio_timer_action* write_timer_action,
+ usize obuf_size) ;
+extern cmd_return_code_t uty_vin_pop(vty_io vio, bool final,
+ cmd_context context) ;
+extern cmd_return_code_t uty_vout_pop(vty_io vio, bool final) ;
+
+
+extern vio_vf uty_vf_new(vty_io vio, const char* name, int fd, vfd_type_t type,
+ vfd_io_type_t io_type) ;
+extern void uty_vf_set_read(vio_vf vf, on_off_b on) ;
+extern void uty_vf_set_read_timeout(vio_vf vf, vty_timer_time read_timeout) ;
+extern void uty_vf_set_write(vio_vf vf, on_off_b on) ;
+extern void uty_vf_set_write_timeout(vio_vf vf, vty_timer_time write_timeout) ;
+extern int uty_vf_error(vio_vf vf, const char* what, int err) ;
+
+extern vio_child uty_child_register(pid_t pid, vio_vf parent) ;
+extern void vty_child_close_register(void) ;
+extern void uty_child_awaited(vio_child child, vty_timer_time timeout) ;
+extern bool uty_child_collect(vio_child child, vty_timer_time timeout,
+ bool final) ;
+extern vio_child uty_child_dismiss(vio_child child, bool final) ;
+extern void uty_sigchld(void) ;
+
+extern void uty_child_signal_nexus_set(vty_io vio) ;
+extern void vty_child_signal_nexus_signal(void) ;
+extern void uty_child_signal_nexus_clear(vty_io vio) ;
+
extern void uty_open_listeners(const char *addr, unsigned short port,
const char *path) ;
+extern void uty_add_listener(int fd, vio_vfd_accept* accept) ;
extern void uty_close_listeners(void) ;
+extern void uty_watch_dog_init(void) ;
extern void uty_watch_dog_start(void) ;
extern void uty_watch_dog_stop(void) ;
-extern void uty_half_close (vty_io vio, const char* reason) ;
-extern void uty_close (vty_io vio) ;
+extern const char* uty_get_name(vty_io vio) ;
-extern int uty_out (struct vty *vty, const char *format, ...)
- PRINTF_ATTRIBUTE(2, 3) ;
-extern int uty_vout(struct vty *vty, const char *format, va_list args) ;
-extern void uty_out_clear(vty_io vio) ;
-extern void uty_out_fflush(vty_io vio, FILE* file) ;
+extern void uty_set_monitor(vty_io vio, bool on) ;
-extern void uty_set_height(vty_io vio) ;
-extern void uty_cmd_output_start(vty_io vio) ;
+/*==============================================================================
+ * Inline Functions
+ */
-extern void uty_sock_set_readiness(vio_sock sock, enum vty_readiness ready) ;
-extern void uty_sock_set_timer(vio_sock sock, unsigned long timeout) ;
+Inline bool
+uty_is_terminal(struct vty *vty)
+{
+ return vty->type == VTY_TERMINAL ;
+}
-extern int uty_read (vty_io vio, keystroke steal) ;
-extern int utysh_read (vty_io vio, qstring cl, qstring buf) ;
+Inline bool
+uty_is_shell_server(struct vty *vty)
+{
+ return vty->type == VTY_SHELL_SERVER ;
+}
+Inline bool
+uty_is_shell_client(struct vty *vty)
+{
+ return vty->type == VTY_SHELL_CLIENT ;
+}
-extern const char* uty_get_name(vty_io vio) ;
+/*------------------------------------------------------------------------------
+ * Command output -- append to output buffer.
+ */
+Inline int
+uty_vprintf(vty_io vio, const char *format, va_list args)
+{
+ return vio_fifo_vprintf(vio->obuf, format, args) ;
+} ;
-extern void uty_set_monitor(vty_io vio, bool on) ;
+/*------------------------------------------------------------------------------
+ * Clear command output -- discard anything in the buffer, but keep markers.
+ */
+Inline void
+uty_out_clear(vty_io vio)
+{
+ vio_fifo_clear(vio->obuf, false) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Accept command output -- advance any end_mark to current put position.
+ */
+Inline void
+uty_out_accept(vty_io vio)
+{
+ vio_fifo_step_end_mark(vio->obuf) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Reject command output -- discard anything after the end_mark in the buffer,
+ * but keep markers.
+ */
+Inline void
+uty_out_reject(vty_io vio)
+{
+ vio_fifo_back_to_end_mark(vio->obuf, true) ;
+} ;
#endif /* _ZEBRA_VTY_IO_H */
diff --git a/lib/vty_io_basic.c b/lib/vty_io_basic.c
new file mode 100644
index 00000000..1d40c210
--- /dev/null
+++ b/lib/vty_io_basic.c
@@ -0,0 +1,1039 @@
+/* VTY IO Basic Functions -- bottom level of VTY IO hierarchy
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * Revisions: Copyright (C) 2010 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * 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.
+ */
+#include "misc.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) ; /* TODO umask etc ? */
+} ;
+
+/*==============================================================================
+ * Base level I/O and Timer handling....
+ *
+ * This is separated out so that the differences between running in a qpnexus
+ * and an old thread environment are encapsulated here.
+ */
+
+static void vio_vfd_mqb_kick(vio_vfd vfd) ;
+static void vio_vfd_mqb_free(vio_vfd vfd) ;
+
+/*==============================================================================
+ * File Descriptor handling
+ *
+ * Provides read/write ready handling in consistent manner -- so don't care
+ * whether is qpnexus or old thread environment.
+ *
+ * NB: in all cases, when a read/write event goes off, the read/write readiness
+ * is cleared and any read/write timer is stopped.
+ *
+ * In the qpnexus world, there is a small complication... the qpselect stuff
+ * for all vty lives in the cli thread, so there is a mechanism here to allow
+ * for messages from other threads to implement the necessary qpselect things,
+ * see above.
+ */
+
+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 timer) ;
+
+Inline void
+vio_vfd_do_read_action(vio_vfd vfd)
+{
+ if (vfd->read_action != NULL)
+ vfd->read_action(vfd, vfd->action_info) ;
+}
+
+Inline void
+vio_vfd_do_write_action(vio_vfd vfd)
+{
+ if (vfd->write_action != NULL)
+ vfd->write_action(vfd, vfd->action_info) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Create a new vfd structure.
+ *
+ * Note that sets the same action info for read, write, read timeout and
+ * write timeout.
+ *
+ * Sets the blocking_vf state, which will disallow any attempt to set read/write
+ * ready/timeout -- but enable open/close when not in cli thread.
+ */
+extern vio_vfd
+vio_vfd_new(int fd, vfd_type_t type, vfd_io_type_t io_type, void* action_info)
+{
+ vio_vfd vfd ;
+
+ vfd = XCALLOC(MTYPE_VTY, sizeof(struct vio_vfd)) ;
+
+ /* Has set:
+ *
+ * fd -- X -- see below
+ * active -- X -- see below
+ *
+ * type -- X -- see below
+ * io_type -- X -- see below
+ *
+ * blocking_vf -- X -- see below
+ *
+ * action_info -- NULL -- set below if !blocking_vf
+ *
+ * read_action -- NULL
+ * write_action -- NULL
+ *
+ * read_timer -- NULL -- set below if !blocking_vf
+ * write_timer -- NULL -- set below if !blocking_vf
+ *
+ * f.qf -- NULL -- set below if !blocking_vf
+ *
+ * f.thread.read -- NULL
+ * f.thread.write -- NULL
+ *
+ * queued -- false
+ *
+ * read_req -- all zeros -- none set
+ * write_req -- all zeros -- none set
+ *
+ * mqb -- NULL -- none, yet
+ */
+ confirm(VIO_TIMER_INIT_ZERO) ;
+
+ vfd->fd = fd ;
+ vfd->type = type ;
+ vfd->io_type = io_type ;
+
+ vfd->blocking_vf = (io_type & vfd_io_blocking) != 0 ;
+
+ if (!vfd->blocking_vf)
+ {
+ VTY_ASSERT_CLI_THREAD() ;
+
+ if (vty_nexus)
+ {
+ vfd->f.qf = qps_file_init_new(NULL, NULL) ;
+ qps_add_file(vty_cli_nexus->selection, vfd->f.qf, vfd->fd, vfd) ;
+ } ;
+
+ vfd->read_timer = vio_timer_init_new(NULL, NULL, NULL) ;
+ vfd->write_timer = vio_timer_init_new(NULL, NULL, NULL) ;
+
+ vio_vfd_set_action_info(vfd, action_info) ;
+ } ;
+
+ return vfd ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set the read action field for the given vio_vfd.
+ */
+extern void
+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_vfd.
+ */
+extern void
+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_vfd.
+ */
+extern void
+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_vfd.
+ */
+extern void
+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_vfd read/write action.
+ */
+extern void
+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_vfd (if any), then kick it.
+ */
+extern void
+vio_vfd_kick_read_action(vio_vfd vfd)
+{
+ if ((vfd != NULL) && (vfd->read_action != NULL))
+ vio_vfd_do_read_action(vfd) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * If there is a write action set for the give vio_vfd (if any), then kick it.
+ */
+extern void
+vio_vfd_kick_write_action(vio_vfd vfd)
+{
+ if ((vfd != NULL) && (vfd->write_action != NULL))
+ vio_vfd_do_read_action(vfd) ;
+} ;
+#endif
+
+/*------------------------------------------------------------------------------
+ * Close the read side of the given vfd (if any).
+ *
+ * If the vfd is a socket, then does a shutdown of the read side.
+ *
+ * If the vfd is not a socket and is read (only) closes the vfd.
+ *
+ * In any case, turns off any read ready and read ready timeout.
+ *
+ * Returns original vfd, or NULL if it has been closed.
+ *
+ * NB: if this is not a "blocking_vf", then MUST be in the cli thread.
+ */
+extern vio_vfd
+vio_vfd_read_close(vio_vfd vfd)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if (vfd == NULL)
+ return NULL ;
+
+ if (!vfd->blocking_vf)
+ VTY_ASSERT_CLI_THREAD() ;
+
+ if ((vfd->io_type & vfd_io_read) != 0)
+ {
+ if ((vfd->io_type & vfd_io_write) != 0)
+ {
+ /* read & write, so really half-close if can */
+ if (vfd->fd >= 0)
+ {
+ if (vfd->type == vfd_socket)
+ 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_vfd_close(vfd) ;
+ } ;
+ } ;
+
+ return vfd ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Close the given vfd (if any).
+ *
+ * If there is an fd, close it. Stops any read/write waiting and releases all
+ * memory.
+ *
+ * NB: if this is not a "blocking_vf", then MUST be in the cli thread.
+ * Inter alia, this guarantees that cannot be in the middle of a read/write
+ * ready/timeout operation -- so the file can be closed down, and
+ * any pending ready/timeout will be swept away.
+ */
+extern vio_vfd
+vio_vfd_close(vio_vfd vfd)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if (vfd == NULL)
+ return NULL ;
+
+ if (!vfd->blocking_vf)
+ VTY_ASSERT_CLI_THREAD() ;
+
+ /* Close the underlying fd, if any */
+
+ if (vfd->fd >= 0)
+ close(vfd->fd) ; /* ignores errors TODO */
+
+ /* Clear out the vfd then free it */
+
+ 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_vfd_mqb_free(vfd) ;
+ }
+ else
+ {
+ if (vfd->f.thread.read != NULL)
+ {
+ assert(vfd->fd == THREAD_FD(vfd->f.thread.read)) ;
+ thread_cancel(vfd->f.thread.read) ;
+ vfd->f.thread.read = NULL ;
+ } ;
+
+ if (vfd->f.thread.write != NULL)
+ {
+ assert(vfd->fd == THREAD_FD(vfd->f.thread.write)) ;
+ thread_cancel(vfd->f.thread.write) ;
+ vfd->f.thread.write = NULL ;
+ } ;
+
+ assert(vfd->mqb == NULL) ;
+ } ;
+
+ vfd->read_timer = vio_timer_reset(vfd->read_timer, free_it) ;
+ vfd->write_timer = vio_timer_reset(vfd->write_timer, free_it) ;
+
+ XFREE(MTYPE_VTY, vfd) ;
+
+ return NULL ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set or unset read ready state on given vio_vfd (if any) if it is active.
+ *
+ * Do nothing if vfd NULL, fd < 0 or not a read type of fd.
+ *
+ * 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.
+ *
+ * NB: must NOT be a "blocking_vf" !!
+ */
+extern on_off_b
+vio_vfd_set_read(vio_vfd vfd, on_off_b on, vty_timer_time timeout)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if ((vfd == NULL) || (vfd->fd < 0) || ((vfd->io_type & vfd_io_read) == 0))
+ return off ;
+
+ assert(!vfd->blocking_vf) ;
+
+ if (vty_is_cli_thread())
+ {
+ /* In the cli thread (effectively) so do things directly. */
+
+ vfd->read_req.set = false ; /* doing or overriding request */
+
+ if (on)
+ {
+ assert(vfd->read_action != NULL) ;
+
+ if (vty_nexus)
+ qps_enable_mode(vfd->f.qf, qps_read_mnum,
+ vio_vfd_qps_read_action) ;
+ else
+ {
+ if (vfd->f.thread.read == NULL)
+ vfd->f.thread.read = thread_add_read(vty_master,
+ vio_vfd_thread_read_action, vfd, vfd->fd) ;
+ } ;
+
+ vio_timer_set(vfd->read_timer, timeout) ;
+ }
+ else
+ {
+ if (vty_nexus)
+ qps_disable_modes(vfd->f.qf, qps_read_mbit) ;
+ else
+ {
+ if (vfd->f.thread.read != NULL)
+ thread_cancel (vfd->f.thread.read) ;
+ } ;
+
+ vio_timer_unset(vfd->read_timer) ;
+ } ;
+ }
+ else
+ {
+ /* In other threads, must send message to cli thread */
+ vfd->read_req.set = true ;
+ vfd->read_req.on = on ;
+ vfd->read_req.timeout = timeout ;
+
+ vio_timer_squelch(vfd->read_timer) ;
+
+ vio_vfd_mqb_kick(vfd) ;
+ } ;
+
+ return on ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set or unset write ready state on given vio_vfd (if any) if it is active.
+ *
+ * Do nothing if vfd NULL, fd < 0 or not a write type of fd.
+ *
+ * If setting write_on, starts any write timeout timer.
+ * If setting write off, stops any write 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.
+ *
+ * NB: must NOT be a "blocking_vf" !!
+ */
+extern on_off_b
+vio_vfd_set_write(vio_vfd vfd, on_off_b on, vty_timer_time timeout)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if ((vfd == NULL) || (vfd->fd < 0) || ((vfd->io_type & vfd_io_write) == 0))
+ return off ;
+
+ assert(!vfd->blocking_vf) ;
+
+ if (vty_is_cli_thread())
+ {
+ /* In the cli thread (effectively) so do things directly. */
+
+ vfd->write_req.set = false ; /* doing or overriding request */
+
+ if (on)
+ {
+ assert(vfd->write_action != NULL) ;
+
+ if (vty_nexus)
+ qps_enable_mode(vfd->f.qf, qps_write_mnum,
+ vio_vfd_qps_write_action) ;
+ else
+ {
+ if (vfd->f.thread.write == NULL)
+ vfd->f.thread.write = thread_add_write(vty_master,
+ vio_vfd_thread_write_action, vfd, vfd->fd) ;
+ } ;
+
+ vio_timer_set(vfd->write_timer, timeout) ;
+ }
+ else
+ {
+ if (vty_nexus)
+ qps_disable_modes(vfd->f.qf, qps_write_mbit) ;
+ else
+ {
+ if (vfd->f.thread.write != NULL)
+ thread_cancel (vfd->f.thread.write) ;
+ } ;
+
+ vio_timer_unset(vfd->write_timer) ;
+ } ;
+ }
+ else
+ {
+ /* In other threads, must send message to cli thread */
+ vfd->write_req.set = true ;
+ vfd->write_req.on = on ;
+ vfd->write_req.timeout = timeout ;
+
+ vio_timer_squelch(vfd->write_timer) ;
+
+ vio_vfd_mqb_kick(vfd) ;
+ } ;
+
+ return on ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Callback -- qnexus: ready to read
+ *
+ * Clears read ready state and unsets any read timer.
+ */
+static void
+vio_vfd_qps_read_action(qps_file qf, void* file_info)
+{
+ vio_vfd vfd ;
+
+ VTY_LOCK() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ 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_vfd_do_read_action(vfd) ;
+
+ VTY_UNLOCK() ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Callback -- thread: ready to read
+ *
+ * Clears read ready state and unsets any read timer.
+ *
+ * NB: if !vfd->active, then has been closed in another thread, but close
+ * message is yet to be procesed.
+ */
+static int
+vio_vfd_thread_read_action(struct thread *thread)
+{
+ vio_vfd vfd ;
+
+ VTY_LOCK() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ vfd = THREAD_ARG(thread);
+
+ assert(vfd->fd == THREAD_FD(thread)) ;
+
+ vfd->f.thread.read = NULL ; /* implicitly */
+ vio_timer_unset(vfd->read_timer) ;
+
+ vio_vfd_do_read_action(vfd) ;
+
+ VTY_UNLOCK() ;
+ return 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Callback -- qnexus: ready to write
+ *
+ * Clears write ready state and unsets any write timer.
+ *
+ * NB: if !vfd->active, then has been closed in another thread, but close
+ * message is yet to be procesed.
+ */
+static void
+vio_vfd_qps_write_action(qps_file qf, void* file_info)
+{
+ vio_vfd vfd = file_info ;
+
+ VTY_LOCK() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ 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_vfd_do_write_action(vfd) ;
+
+ VTY_UNLOCK() ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Callback -- thread: ready to write
+ *
+ * Clears write ready state and unsets any write timer.
+ *
+ * NB: if !vfd->active, then has been closed in another thread, but close
+ * message is yet to be procesed.
+ */
+static int
+vio_vfd_thread_write_action(struct thread *thread)
+{
+ vio_vfd vfd = THREAD_ARG (thread);
+
+ VTY_LOCK() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ assert(vfd->fd == THREAD_FD(thread)) ;
+ vio_timer_unset(vfd->write_timer) ;
+
+ vfd->f.thread.write = NULL ; /* implicitly */
+
+ vio_vfd_do_write_action(vfd) ;
+
+ VTY_UNLOCK() ;
+ return 0 ;
+} ;
+
+/*==============================================================================
+ * Message handling, so that other threads can signal for output to be
+ * dispatched !
+ *
+ * There is one message block per vfd. It is only every touched under the
+ * vty_mutex.
+ *
+ * Once it is dispatched it is marked 'active'. Can still be changed, but no
+ * 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 close,
+ * which it will do when it is dequeued and actioned.
+ */
+
+static void vio_vfd_mqb_action(mqueue_block mqb, mqb_flag_t flag) ;
+
+/*------------------------------------------------------------------------------
+ * Dispatch mqb, if not already active
+ */
+static void
+vio_vfd_mqb_kick(vio_vfd vfd)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if (!vfd->queued)
+ {
+ vfd->queued = true ;
+
+ if (vfd->mqb == NULL)
+ vfd->mqb = mqb_init_new(NULL, vio_vfd_mqb_action, vfd) ;
+
+ mqueue_enqueue(vty_cli_nexus->queue, vfd->mqb, mqb_priority) ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Free mqb, if exists.
+ */
+static void
+vio_vfd_mqb_free(vio_vfd vfd)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if (vfd->queued)
+ mqb_set_arg0(vfd->mqb, NULL) ; /* mqb will suicide */
+ else
+ mqb_free(vfd->mqb) ; /* kill now (if any) */
+
+ vfd->queued = false ;
+ vfd->mqb = NULL ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Action routine for the read/write on/off setting message.
+ *
+ * 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_vfd_mqb_action(mqueue_block mqb, mqb_flag_t flag)
+{
+ vio_vfd vfd ;
+
+ VTY_LOCK() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ vfd = mqb_get_arg0(mqb) ;
+
+ if (vfd != NULL)
+ {
+ assert(mqb == vfd->mqb) ;
+ vfd->queued = false ;
+ } ;
+
+ if ((flag != mqb_destroy) && (vfd != NULL))
+ {
+ if (vfd->read_req.set)
+ vio_vfd_set_read(vfd, vfd->read_req.on, vfd->read_req.timeout) ;
+ if (vfd->write_req.set)
+ vio_vfd_set_write(vfd, vfd->write_req.on, vfd->write_req.timeout) ;
+ }
+ else
+ {
+ mqb_free(mqb) ; /* Suicide */
+ if (vfd != NULL)
+ vfd->mqb = NULL ; /* make sure vfd knows */
+ } ;
+
+ VTY_UNLOCK() ;
+} ;
+
+/*==============================================================================
+ * Listener Handling
+ *
+ *
+ */
+
+static void vio_accept(vio_vfd vfd, void* info) ;
+
+/*------------------------------------------------------------------------------
+ * Create a new listener object for the newly opened listener socket.
+ *
+ * Sets the accept action that will be called, and passed the fd of the listener
+ * socket, when the listen socket goes 'read ready'.
+ *
+ * Returns address of newly created listener structure.
+ */
+extern vio_listener
+vio_listener_new(int fd, vio_vfd_accept* accept_action)
+{
+ vio_listener listener ;
+
+ VTY_ASSERT_CLI_THREAD_LOCKED() ;
+
+ listener = XCALLOC(MTYPE_VTY, sizeof(struct vio_listener)) ;
+ /* sets the next pointer to NULL */
+
+ listener->vfd = vio_vfd_new(fd, vfd_listener, vfd_io_read, listener) ;
+
+ listener->accept_action = accept_action ;
+
+ vio_vfd_set_read_action(listener->vfd, vio_accept) ;
+ vio_vfd_set_read(listener->vfd, on, 0) ;
+
+ return listener ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Close listener and free listener structure.
+ * Stops any read waiting and releases all memory.
+ *
+ * NB: assumes that the structure has been removed from any list.
+ */
+extern void
+vio_listener_close(vio_listener listener)
+{
+ VTY_ASSERT_CLI_THREAD_LOCKED() ;
+
+ vio_vfd_close(listener->vfd) ;
+ XFREE(MTYPE_VTY, listener) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Accept action -- this is the read_action from the listener vfd.
+ *
+ * info points at the listener object.
+ */
+static void
+vio_accept(vio_vfd vfd, void* info)
+{
+ vio_listener listener ;
+
+ VTY_ASSERT_CLI_THREAD_LOCKED() ;
+
+ listener = info ;
+ assert(vfd == listener->vfd) ;
+
+ vio_vfd_set_read(listener->vfd, on, 0) ;
+
+ listener->accept_action(vfd->fd) ;
+} ;
+
+/*==============================================================================
+ * Timer Handling
+ *
+ * Provides timer primitives that work either in qnexus environment or in
+ * a thread environment. Timer times are seconds.
+ *
+ * The main difference is that thread environment timers are 'one-shot', set up
+ * for one timing run and then destroyed.
+ */
+
+static void vio_timer_qtr_action(qtimer qtr, void* timer_info, qtime_t when) ;
+static int vio_timer_thread_action(struct thread *thread) ;
+
+/*------------------------------------------------------------------------------
+ * Allocate and/or initialise vio_timer structure.
+ */
+extern vio_timer
+vio_timer_init_new(vio_timer timer, vio_timer_action* action, void* action_info)
+{
+ if (timer == NULL)
+ timer = XCALLOC(MTYPE_VTY, sizeof(vio_timer_t)) ;
+ else
+ memset(timer, 0, sizeof(vio_timer_t)) ;
+
+ confirm(VIO_TIMER_INIT_ZERO) ;
+
+ /* active -- 0, false
+ * squelch -- 0, false
+ * t -- NULL, no qtr and no thread
+ */
+
+ timer->action = action ;
+ timer->action_info = action_info ;
+
+ return timer ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set the action field for the given timer.
+ */
+extern void
+vio_timer_set_action(vio_timer timer, vio_timer_action* action)
+{
+ if (timer != NULL)
+ timer->action = action ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set the info field for the given timer.
+ */
+extern void
+vio_timer_set_info(vio_timer timer, void* action_info)
+{
+ if (timer != NULL)
+ timer->action_info = action_info ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Squelch the given timer -- if it goes off, do not call the action routine,
+ * and leave the rimer inactive.
+ *
+ * Used when doing read/write ready from not-cli thread.
+ */
+static void
+vio_timer_squelch(vio_timer timer)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if (timer != NULL)
+ timer->squelch = true ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Reset vio_timer structure. Stops any timer and releases all memory.
+ */
+extern vio_timer
+vio_timer_reset(vio_timer timer, free_keep_b free_structure)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if (timer != NULL)
+ {
+ VTY_ASSERT_CLI_THREAD() ;
+
+ if (timer->t.anon != NULL)
+ {
+ if (vty_nexus)
+ qtimer_free(timer->t.qtr) ;
+ else
+ thread_cancel(timer->t.thread) ;
+
+ timer->t.anon = NULL ;
+ } ;
+
+ timer->active = false ;
+ timer->squelch = false ;
+
+ if (free_structure)
+ XFREE(MTYPE_VTY, timer) ; /* sets timer = NULL */
+ } ;
+
+ return timer ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set vio_timer going, with the given time (in seconds).
+ *
+ * If timer is running, set to new time.
+ *
+ * If the time == 0, stop any current timer, do not restart.
+ */
+extern void
+vio_timer_set(vio_timer timer, vty_timer_time time)
+{
+ VTY_ASSERT_CLI_THREAD_LOCKED() ;
+
+ if (time == 0)
+ {
+ vio_timer_unset(timer) ;
+ return ;
+ } ;
+
+ assert(timer->action != NULL) ;
+
+ if (vty_nexus)
+ {
+ if (timer->t.qtr == NULL) /* allocate qtr if required */
+ timer->t.qtr = qtimer_init_new(NULL, vty_cli_nexus->pile,
+ vio_timer_qtr_action, timer) ;
+ qtimer_set(timer->t.qtr, qt_add_monotonic(QTIME(time)), NULL) ;
+ }
+ else
+ {
+ if (timer->t.thread != NULL)
+ thread_cancel(timer->t.thread) ;
+ timer->t.thread = thread_add_timer(vty_master,
+ vio_timer_thread_action, timer, time) ;
+ } ;
+
+ timer->active = true ;
+ timer->squelch = false ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Stop vio_timer, if any.
+ */
+extern void
+vio_timer_unset(vio_timer timer)
+{
+ VTY_ASSERT_CLI_THREAD_LOCKED() ;
+
+ if (timer->active)
+ {
+ if (vty_nexus)
+ {
+ assert(timer->t.qtr != NULL) ;
+ qtimer_unset(timer->t.qtr) ;
+ }
+ else
+ {
+ assert(timer->t.thread != NULL) ;
+ thread_cancel(timer->t.thread) ;
+ timer->t.thread = NULL ;
+ } ;
+
+ timer->active = false ;
+ } ;
+
+ timer->squelch = false ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Callback -- qnexus: deal with timer timeout.
+ */
+static void
+vio_timer_qtr_action(qtimer qtr, void* timer_info, qtime_t when)
+{
+ vio_timer timer ;
+ vty_timer_time time ;
+
+ VTY_LOCK() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ timer = timer_info ;
+
+ if ((timer->action != NULL) && (!timer->squelch))
+ {
+ time = timer->action(timer, timer->action_info) ;
+ if (time != 0)
+ vio_timer_set(timer, time) ;
+ else
+ timer->active = false ;
+ }
+ else
+ {
+ timer->squelch = false ;
+ timer->active = false ;
+ } ;
+
+ VTY_UNLOCK() ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Callback -- thread: deal with timer timeout.
+ */
+static int
+vio_timer_thread_action(struct thread *thread)
+{
+ vio_timer timer ;
+ vty_timer_time time ;
+
+ VTY_LOCK() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ timer = THREAD_ARG(thread) ;
+ timer->t.thread = NULL ; /* implicitly */
+
+ if ((timer->action != NULL) && (!timer->squelch))
+ {
+ time = timer->action(timer, timer->action_info) ;
+ if (time != 0)
+ vio_timer_set(timer, time) ;
+ else
+ timer->active = false ;
+ }
+ else
+ {
+ timer->squelch = false ;
+ timer->active = false ;
+ } ;
+
+ VTY_UNLOCK() ;
+ return 0;
+} ;
diff --git a/lib/vty_io_basic.h b/lib/vty_io_basic.h
new file mode 100644
index 00000000..9d174fdd
--- /dev/null
+++ b/lib/vty_io_basic.h
@@ -0,0 +1,230 @@
+/* VTY IO Structure and Functions -- header
+ * Virtual terminal [aka TeletYpe] interface routine.
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * Revisions: Copyright (C) 2010 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * 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_VTY_IO_BASIC_H
+#define _ZEBRA_VTY_IO_BASIC_H
+
+#include "misc.h"
+
+//#include "vty_local.h"
+
+#include "qpselect.h"
+#include "thread.h"
+#include "mqueue.h"
+
+/*==============================================================================
+ * Here are structures and other definitions which are shared by all the
+ * VTY I/O hierarchy, providing the basic read/write ready functions and
+ * timer functions.
+ *
+ * This is separated out so that the differences between running in a qpnexus
+ * and an old thread environment are encapsulated here.
+ */
+
+enum vfd_type
+{
+ vfd_none = 0,
+ vfd_socket,
+ vfd_file,
+ vfd_pipe,
+ vfd_listener,
+} ;
+typedef enum vfd_type vfd_type_t ;
+
+enum vfd_io_type /* NB: *bit*significant* */
+{
+ vfd_io_none = 0,
+
+ 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 ;
+typedef vty_timer_time vio_timer_action(vio_timer timer, void* action_info) ;
+
+struct vio_timer
+{
+ vio_timer_action* action ; /* who do we call */
+ void* action_info ;
+
+ bool active ;
+ bool squelch ; /* used when message pending */
+
+ union {
+ qtimer qtr ; /* when running qnexus */
+ struct thread* thread ; /* when running threads */
+ void* anon ;
+ } t ;
+} ;
+typedef struct vio_timer vio_timer_t[1] ;
+
+enum { VIO_TIMER_INIT_ZERO = true } ;
+
+/*------------------------------------------------------------------------------
+ * File descriptors -- looks after ready to read and/or write.
+ *
+ * Implemented as qps_file or as read/write thread, depending on the
+ * environment.
+ */
+typedef struct vio_vfd* vio_vfd ;
+
+typedef void vio_vfd_action(vio_vfd vfd, void* action_info) ;
+
+typedef struct
+{
+ bool set ;
+ on_off_b on ;
+ vty_timer_time timeout ;
+} vfd_request_t ;
+
+struct vio_vfd
+{
+ int fd ;
+
+ vfd_type_t type ; /* used for half-close */
+ vfd_io_type_t io_type ; /* read, write, read/write */
+
+ /* The rest of the vfd is to do with managing read/write ready and
+ * read/write timeouts.
+ *
+ * At the vf level we manage "blocking" vfs, which do not and must not
+ * use read/write ready or read/write timeouts. When the vfd is created
+ * this is passed, so that mistakes can be spotted !
+ *
+ * Non-blocking vfs may only be created and closed in the cli thread (or
+ * when running in the legacy threads environment). This is because can
+ * only dismantle qps_file structure while in the owning thread.
+ */
+ bool blocking_vf ; /* Reject read/write ready etc. */
+
+ void* action_info ; /* for all action and time-out */
+
+ vio_vfd_action* read_action ;
+ vio_vfd_action* write_action ;
+
+ vio_timer read_timer ;
+ vio_timer write_timer ;
+
+ union
+ {
+ qps_file qf ; /* when running qnexus */
+
+ struct /* when running threads */
+ {
+ struct thread* read ;
+ struct thread* write ;
+ } thread ;
+ } f ;
+
+ /* To support remote setting clearing of read/write ready and read/write
+ * timeout -- by remote we mean anything such action not in the cli thread.
+ *
+ * This is required because the qpselect and qtimer stuff assumes that those
+ * structures are private to the thread whose qnexus they belong to !
+ *
+ * This stuff is only required when running multi-pthreaded.
+ */
+ bool queued ; /* message is on queue */
+
+ vfd_request_t read_req ;
+ vfd_request_t write_req ;
+
+ mqueue_block mqb ; /* message if any */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Listeners
+ */
+
+typedef struct vio_listener* vio_listener ;
+
+typedef void vio_vfd_accept(int fd) ;
+
+struct vio_listener
+{
+ vio_listener next ; /* ssl type list */
+ vio_vfd vfd ;
+ vio_vfd_accept* accept_action ;
+};
+
+/*==============================================================================
+ * Functions
+ */
+
+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_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_vfd_set_write_timeout_action(vio_vfd vfd,
+ vio_timer_action* action) ;
+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_b vio_vfd_set_write(vio_vfd vfd, on_off_b on,
+ vty_timer_time timeout) ;
+Inline int vio_vfd_fd(vio_vfd vfd) ;
+
+extern vio_listener vio_listener_new(int fd, vio_vfd_accept* accept) ;
+extern void vio_listener_close(vio_listener listener) ;
+
+extern vio_timer vio_timer_init_new(vio_timer timer, vio_timer_action* action,
+ void* action_info) ;
+extern vio_timer vio_timer_reset(vio_timer timer, free_keep_b free_structure) ;
+extern void vio_timer_set_action(vio_timer timer, vio_timer_action* action) ;
+extern void vio_timer_set_info(vio_timer timer, void* action_info) ;
+extern void vio_timer_set(vio_timer timer, vty_timer_time time) ;
+extern void vio_timer_unset(vio_timer timer) ;
+
+/*==============================================================================
+ * Inline Functions
+ */
+
+/*------------------------------------------------------------------------------
+ * Return the fd from a vio_vfd structure
+ */
+Inline int
+vio_vfd_fd(vio_vfd vfd)
+{
+ return vfd->fd ;
+} ;
+
+#endif /* _ZEBRA_VTY_IO_BASIC_H */
diff --git a/lib/vty_io_file.c b/lib/vty_io_file.c
new file mode 100644
index 00000000..01a92a2d
--- /dev/null
+++ b/lib/vty_io_file.c
@@ -0,0 +1,1930 @@
+/* VTY I/O for Files
+ *
+ * Copyright (C) 2010 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * 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.
+ */
+#include "misc.h"
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <errno.h>
+
+#include "command_local.h"
+
+#include "vty_io.h"
+#include "vty_io_file.h"
+#include "vty_io_basic.h"
+#include "vty_command.h"
+#include "network.h"
+#include "sigevent.h"
+
+/*==============================================================================
+ * VTY Configuration file I/O -- VIN_CONFIG and VOUT_CONFIG.
+ *
+ *
+ *
+ */
+
+/*------------------------------------------------------------------------------
+ * Set up VTY on which to read configuration file -- using already open fd.
+ *
+ * Sets TODO
+ *
+ * NB: sets up a blocking vio -- so the vin_base and vout_base will "block"
+ * (using local pselect() as required), as will any further vin/vout
+ * opened for this vio.
+ */
+extern vty
+vty_config_read_open(int fd, const char* name, bool full_lex)
+{
+ vty vty ;
+ vty_io vio ;
+ vio_vf vf ;
+
+ VTY_LOCK() ;
+
+ /* Create the vty and its vio.
+ *
+ * NB: VTY_CONFIG_READ type vty is set vio->blocking.
+ */
+ vty = uty_new(VTY_CONFIG_READ, CONFIG_NODE) ;
+
+ vio = vty->vio ;
+
+ /* Create the vin_base/vout_base vf
+ *
+ * NB: don't need to specify vfd_io_blocking, because the vio is set blocking.
+ */
+ vf = uty_vf_new(vio, name, fd, vfd_file, vfd_io_read) ;
+
+ uty_vin_push( vio, vf, VIN_CONFIG, NULL, NULL, 64 * 1024) ;
+ uty_vout_push(vio, vf, VOUT_STDERR, NULL, NULL, 4 * 1024) ;
+
+ /* Deal with the possibility that while reading the configuration file, may
+ * use a pipe, and therefore may block waiting to collect a child process.
+ *
+ * Before there is any chance of a SIGCHLD being raised for a child of the
+ * configuration file, invoke the magic required for SIGCHLD to wake up
+ * a pselect() while waiting to collect child.
+ */
+ uty_child_signal_nexus_set(vio) ;
+
+ VTY_UNLOCK() ;
+
+ return vty ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Tidy up after config input file has been closed.
+ *
+ * There is now no further possibility that will block waiting for child to be
+ * collected.
+ *
+ * Nothing further required -- input comes to a halt.
+ */
+extern cmd_return_code_t
+uty_config_read_close(vio_vf vf, bool final)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ uty_child_signal_nexus_clear(vf->vio) ;
+
+ return CMD_SUCCESS ;
+} ;
+
+/*==============================================================================
+ * VTY File I/O -- VIN_FILE and VOUT_FILE
+ *
+ * This is for input and output of configuration files and piped stuff.
+ *
+ * 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. The actual I/O is non-blocking, the "blocking"
+ * I/O is manufactured using a mini-pselect, so can time-out file writing
+ * before too long.
+ */
+static void uty_file_read_ready(vio_vfd vfd, void* action_info) ;
+static vty_timer_time uty_file_read_timeout(vio_timer timer,
+ void* action_info) ;
+
+static void uty_file_write_ready(vio_vfd vfd, void* action_info) ;
+static vty_timer_time uty_file_write_timeout(vio_timer timer,
+ void* action_info) ;
+
+static cmd_return_code_t uty_fifo_command_line(vio_vf vf, cmd_action action) ;
+
+/*------------------------------------------------------------------------------
+ * Open file for input, to be read as commands.
+ *
+ * If could not open, issues message to the vio.
+ *
+ * If opens OK, save the current context in the current vin (before pushing
+ * the new vin).
+ *
+ * Returns: CMD_SUCCESS -- all set to go
+ * CMD_WARNING -- failed to open
+ *
+ * If "blocking" vf, this can be called from any thread, otherwise must be the
+ * cli thread -- see uty_vfd_new().
+ */
+extern cmd_return_code_t
+uty_file_read_open(vty_io vio, qstring name, cmd_context context)
+{
+ cmd_return_code_t ret ;
+ qpath path ;
+ const char* pns ;
+ int fd ;
+ vio_vf vf ;
+ vfd_io_type_t iot ;
+
+ VTY_ASSERT_CLI_THREAD_LOCKED() ;
+
+ assert(vio->vin != NULL) ; /* Not expected to be vin_base */
+
+ /* Now is the time to complete the name, if required. */
+
+ path = uty_cmd_path_name_complete(NULL, qs_string(name), context) ;
+ pns = qpath_string(path) ;
+
+ /* Do the basic file open. */
+ iot = vfd_io_read ;
+
+ fd = uty_vfd_file_open(pns, iot) ;
+
+ if (fd < 0)
+ {
+ uty_out(vio, "%% Could not open input file %s\n", pns) ;
+
+ ret = CMD_WARNING ; /* TODO add errno to message ? */
+ }
+ else
+ {
+ /* We have a file, so now save context and update dir "here" */
+ uty_vin_new_context(vio, context, path) ;
+
+ /* OK -- now push the new input onto the vin_stack. */
+ vf = uty_vf_new(vio, pns, fd, vfd_file, iot) ;
+ uty_vin_push(vio, vf, VIN_FILE, uty_file_read_ready,
+ uty_file_read_timeout, 16 * 1024) ;
+ uty_vf_set_read_timeout(vf, 30) ;
+
+ ret = CMD_SUCCESS ;
+ } ;
+
+ qpath_free(path) ;
+ return ret ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Open file for output.
+ *
+ * If could not open, issues message to the vio.
+ *
+ * Returns: CMD_SUCCESS -- all set to go
+ * CMD_WARNING -- failed to open
+ *
+ * If "blocking" vf, this can be called from any thread, otherwise must be the
+ * cli thread -- see uty_vfd_new().
+ */
+extern cmd_return_code_t
+uty_file_write_open(vty_io vio, qstring name, bool append, cmd_context context)
+{
+ cmd_return_code_t ret ;
+ qpath path ;
+ const char* pns ;
+ int fd ;
+ vio_vf vf ;
+ vfd_io_type_t iot ;
+
+ VTY_ASSERT_CLI_THREAD_LOCKED() ;
+
+ /* Now is the time to complete the name, if required. */
+
+ path = uty_cmd_path_name_complete(NULL, qs_string(name), context) ;
+ pns = qpath_string(path) ;
+
+ /* Do the basic file open. */
+ iot = vfd_io_write | (append ? vfd_io_append : 0) ;
+
+ fd = uty_vfd_file_open(pns, iot) ;
+
+ if (fd < 0)
+ {
+ uty_out(vio, "%% Could not open output file %s\n", pns) ;
+
+ ret = CMD_WARNING ; /* TODO add errno to message ? */
+ }
+ else
+ {
+ /* OK -- now push the new output onto the vout_stack. */
+ vf = uty_vf_new(vio, pns, fd, vfd_file, iot) ;
+ uty_vout_push(vio, vf, VOUT_FILE, uty_file_write_ready,
+ uty_file_write_timeout, 16 * 1024) ;
+ ret = CMD_SUCCESS ;
+ } ;
+
+ qpath_free(path) ;
+ return ret ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Command line fetch from a file or pipe.
+ *
+ * Returns: CMD_SUCCESS -- have another command line ready to go
+ * CMD_WAITING -- would not wait for input <=> non-blocking
+ * CMD_EOF -- ran into EOF
+ * CMD_IO_ERROR -- ran into an I/O error
+ * CMD_IO_TIMEOUT -- could wait no longer <=> blocking
+ *
+ * In "non-blocking" state, on CMD_WAITING sets read ready, which will
+ * vty_cmd_signal() the command loop to come round again.
+ *
+ * In "blocking" state, will not return until have something, or there is
+ * nothing to be had, or times out.
+ *
+ * This can be called in any thread.
+ *
+ * NB: the vin_state will become vf_eof the first time that CMD_EOF is
+ * returned.
+ */
+extern cmd_return_code_t
+uty_file_fetch_command_line(vio_vf vf, cmd_action action)
+{
+ VTY_ASSERT_LOCKED() ; /* In any thread */
+
+ assert((vf->vin_type == VIN_FILE) || (vf->vin_type == VIN_CONFIG)) ;
+ assert((vf->vin_state == vf_open) || (vf->vin_state == vf_eof)) ;
+
+ while (1)
+ {
+ cmd_return_code_t ret ;
+
+ /* Try to complete line straight from the buffer.
+ *
+ * If buffer is empty and have seen eof on the input, signal CMD_EOF.
+ */
+ ret = uty_fifo_command_line(vf, action) ;
+
+ if (ret != CMD_WAITING)
+ return ret ;
+
+ /* Could not complete line and exhausted contents of fifo */
+ while (1)
+ {
+ qps_mini_t qm ;
+ int get ;
+
+ get = vio_fifo_read_nb(vf->ibuf, vio_vfd_fd(vf->vfd), 1) ;
+
+ if (get > 0)
+ break ; /* loop back to uty_fifo_command_line() */
+
+ if (get == -1)
+ return CMD_IO_ERROR ; /* register error */
+
+ if (get == -2)
+ {
+ /* Hit end of file set the vf into vf_eof.
+ *
+ * NB: does not know has hit eof until tries to read and nothing
+ * is returned.
+ *
+ * Loop back so that uty_fifo_command_line() can reconsider, now
+ * that we know that there is no more data to come -- it may
+ * signal CMD_SUCCESS (non-empty final line) or CMD_EOF.
+ */
+ assert(vio_fifo_empty(vf->ibuf)) ;
+
+ vf->vin_state = vf_eof ;
+
+ break ; /* loop back to uty_fifo_command_line() */
+ } ;
+
+ /* Would block -- for non-blocking return CMD_WAITING, for
+ * blocking we block here with a timeout, and when there is more
+ * to read, loop back to
+ */
+ assert(get == 0) ;
+
+ if (!vf->blocking)
+ {
+ uty_vf_set_read(vf, on) ;
+ return CMD_WAITING ;
+ } ;
+
+ /* Implement blocking I/O, with timeout */
+ qps_mini_set(qm, vio_vfd_fd(vf->vfd), qps_read_mnum,
+ vf->read_timeout) ;
+ if (qps_mini_wait(qm, NULL, false) == 0)
+ return CMD_IO_TIMEOUT ; /* TODO vf_timeout ?? */
+
+ /* Loop back to vio_fifo_read_nb() */
+ }
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Try to complete a command line for the given vf, from the current input
+ * fifo -- performs NO I/O
+ *
+ * Returns: CMD_SUCCESS -- have a command line.
+ * CMD_EOF -- there is no more
+ * CMD_WAITING -- waiting for more
+ *
+ * If vf->vin_state == vf_eof, will return CMD_SUCCESS if have last part line
+ * in hand. Otherwise will return CMD_EOF, any number of times.
+ */
+static cmd_return_code_t
+uty_fifo_command_line(vio_vf vf, cmd_action action)
+{
+ VTY_ASSERT_LOCKED() ; /* In any thread */
+
+ if (vf->line_complete)
+ {
+ vio_fifo_set_hold_mark(vf->ibuf) ; /* advance hold */
+
+ vf->line_complete = false ;
+ vf->line_number += vf->line_step ;
+
+ qs_set_len_nn(vf->cl, 0) ;
+ vf->line_step = 0 ;
+ } ;
+
+ while (1)
+ {
+ char* s, * p, * e ;
+ ulen have ;
+ ulen len ;
+ bool eol ;
+
+ /* Get what we can from the fifo */
+ have = vio_fifo_get(vf->ibuf) ;
+
+ /* If fifo is empty, may be last line before eof, eof or waiting */
+ if (have == 0)
+ {
+ if (vf->vin_state == vf_eof)
+ {
+ if (qs_len_nn(vf->cl) > 0)
+ break ; /* have non-empty last line */
+ else
+ return CMD_EOF ;
+ } ;
+
+ return CMD_WAITING ;
+ } ;
+
+ assert(vf->vin_state != vf_eof) ; /* not empty => not eof */
+
+ /* 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 = vio_fifo_get_ptr(vf->ibuf) ;
+ e = s + have ; /* have != 0 */
+
+ eol = false ;
+ while (p < e)
+ {
+ if (*p++ < 0x20)
+ {
+ if (*(p-1) != '\n')
+ {
+ *(p-1) = ' ' ; /* everything other than '\n' */
+ continue ;
+ } ;
+
+ ++vf->line_step ; /* got a '\n' */
+
+ eol = true ;
+ break ;
+ } ;
+ } ;
+
+ /* Step past what have just consumed -- we have a hold_mark, so
+ * stuff is still in the fifo.
+ */
+ vio_fifo_step(vf->ibuf, p - s) ;
+
+ /* If not found '\n', then we have a line fragment that needs to be
+ * appended to any previous line fragments.
+ *
+ * Loops back to try to get some more form the fifo.
+ */
+ if (!eol)
+ {
+ qs_append_str_n(vf->cl, s, p - s) ;
+ continue ;
+ } ;
+
+ /* Have an eol. Step back across the '\n' and any trailing spaces
+ * we have in hand.
+ */
+ do --p ; while ((p > s) && (*(p-1) == ' ')) ;
+
+ /* If we have nothing so far, set alias to point at what we have in
+ * the fifo. Otherwise, append to what we have.
+ *
+ * End up with: s = start of entire line, so far.
+ * p = end of entire line so far.
+ */
+ len = p - s ; /* length to add */
+ if (qs_len_nn(vf->cl) == 0)
+ qs_set_alias_n(vf->cl, s, len) ;
+ else
+ {
+ if (len != 0)
+ qs_append_str_n(vf->cl, s, len) ;
+
+ s = qs_char_nn(vf->cl) ;
+ p = s + qs_len_nn(vf->cl) ;
+
+ 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) == ' ')) ;
+
+ qs_set_len_nn(vf->cl, p - s) ;
+ } ;
+ } ;
+
+ /* Now worry about we have a trailing '\'. */
+
+ if ((p == s) || (*(p-1) != '\\'))
+ break ; /* no \ => no continuation => success */
+
+ /* 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'
+ */
+ e = p ;
+ do --p ; while ((p > s) && (*(p-1) == '\\')) ;
+
+ if (((e - p) & 1) == 0)
+ break ; /* even => no continuation => success */
+
+ qs_set_len_nn(vf->cl, p - s - 1) ; /* strip odd '\' */
+
+ continue ; /* loop back to fetch more */
+ } ;
+
+ /* Success: have a line in hand */
+
+ vf->line_complete = true ;
+
+ action->to_do = cmd_do_command ;
+ action->line = vf->cl ;
+
+ return CMD_SUCCESS ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * File or pipe is ready to read -- this is the call-back planted in the
+ * vf->vfd.
+ *
+ * The command_queue processing can continue.
+ *
+ * Note that the read_ready state and any time out are automatically
+ * disabled when they go off.
+ */
+static void
+uty_file_read_ready(vio_vfd vfd, void* action_info)
+{
+ vio_vf vf ;
+
+ VTY_LOCK() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ vf = action_info ;
+ assert(vf->vfd == vfd) ;
+
+ uty_cmd_signal(vf->vio, CMD_SUCCESS) ;
+
+ VTY_UNLOCK() ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * File or pipe has timed out, waiting to read -- this is the call-back planted
+ * in the vf->vfd.
+ *
+ * ????
+ */
+static vty_timer_time
+uty_file_read_timeout(vio_timer timer, void* action_info)
+{
+ vio_vf vf ;
+
+ VTY_LOCK() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ vf = action_info ;
+ assert(vf->vfd->write_timer == timer) ;
+
+//cq_continue(vf->vio->vty) ; TODO
+
+ VTY_UNLOCK() ;
+
+ return 0 ; /* Do not restart timer */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Command output push to a file.
+ *
+ * Until the file becomes vf_closing, will not write the end_lump of the fifo,
+ * so all output is in units of the fifo size -- which should be "chunky".
+ *
+ * Although it is unlikely to happen, if blocking, will block if cannot
+ * completely write away what is required, or enable write ready.
+ *
+ * If final, will write as much as possible, but not block and will ignore
+ * any errors -- but the return code will be an error return code.
+ *
+ * Returns: CMD_SUCCESS -- written everything
+ * CMD_WAITING -- could not write everything (<=> non-blocking)
+ * CMD_IO_ERROR -- ran into an I/O error
+ * CMD_IO_TIMEOUT -- not going to wait any longer (<=> blocking)
+ *
+ * In "non-blocking" state, on CMD_WAITING the caller will have to take steps
+ * to come back later.
+ *
+ * In "blocking" state, will not return until have written everything there is,
+ * away, or cannot continue.
+ *
+ * This can be called in any thread.
+ */
+extern cmd_return_code_t
+uty_file_out_push(vio_vf vf, bool final)
+{
+ bool all ;
+
+ VTY_ASSERT_LOCKED() ; /* In any thread */
+ assert((vf->vout_state == vf_open) || (vf->vout_state == vf_closing)) ;
+ assert((vf->vout_type == VOUT_FILE) || (vf->vout_type == VOUT_CONFIG)) ;
+
+ all = (vf->vout_state == vf_closing) ;
+ /* empty buffers if closing */
+ while (1)
+ {
+ qps_mini_t qm ;
+ int n ;
+
+ n = vio_fifo_write_nb(vf->obuf, vio_vfd_fd(vf->vfd), all || final) ;
+
+ if (n < 0)
+ return CMD_IO_ERROR ; /* TODO */
+
+ if ((n == 0) || final) /* all gone (or as much as can go) */
+ return CMD_SUCCESS ;
+
+ /* Cannot write everything away without waiting */
+ if (!vf->blocking)
+ {
+ uty_vf_set_write(vf, on) ;
+ return CMD_WAITING ;
+ } ;
+
+ /* Implement blocking I/O, with timeout */
+ qps_mini_set(qm, vio_vfd_fd(vf->vfd), qps_write_mnum, vf->write_timeout) ;
+ if (qps_mini_wait(qm, NULL, false) == 0)
+ return CMD_IO_TIMEOUT ;
+
+ /* Loop back to vio_fifo_write_nb() */
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * File or pipe is ready to write -- this is the call-back planted in the
+ * vf->vfd.
+ *
+ * Note that the write_ready state and any time out are automatically
+ * disabled when they go off.
+ *
+ * Since we have gone to all the trouble of going through pselect, might as
+ * well write everything away. This works while the file is being closed, too.
+ */
+static void
+uty_file_write_ready(vio_vfd vfd, void* action_info)
+{
+ cmd_return_code_t ret ;
+ vio_vf vf ;
+
+ VTY_LOCK() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ vf = action_info ;
+ assert(vf->vfd == vfd) ;
+
+ ret = uty_file_out_push(vf, false) ; /* re-enable write ready if required */
+
+ if (ret != CMD_WAITING)
+ uty_cmd_signal(vf->vio, ret) ;
+
+ VTY_UNLOCK() ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * File or pipe is ready to write -- this is the call-back planted in the
+ * vf->vfd.
+ *
+ * Note that the read_ready state and any time out are automatically
+ * disabled when they go off.
+ */
+static vty_timer_time
+uty_file_write_timeout(vio_timer timer, void* action_info)
+{
+ vio_vf vf ;
+
+ VTY_LOCK() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ vf = action_info ;
+ assert(vf->vfd->write_timer == timer) ;
+
+//uty_file_out_push(vf) ; // TODO ???????
+
+ VTY_UNLOCK() ;
+
+ return 0 ; /* Do not restart timer */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Tidy up after input file has been closed.
+ *
+ * Nothing further required -- input comes to a halt.
+ */
+extern cmd_return_code_t
+uty_file_read_close(vio_vf vf, bool final)
+{
+ return CMD_SUCCESS ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Flush output buffer and close.
+ *
+ * See uty_file_out_push()
+ *
+ * Returns: CMD_SUCCESS -- buffers are empty, or final
+ * CMD_WAITING -- waiting for I/O to complete
+ * CMD_xxx TODO
+ */
+extern cmd_return_code_t
+uty_file_write_close(vio_vf vf, bool final, bool base)
+{
+ VTY_ASSERT_CLI_THREAD_LOCKED() ;
+ assert(vf->vout_state == vf_closing) ;
+ assert((vf->vout_type == VOUT_FILE) || (vf->vout_type == VOUT_CONFIG)) ;
+
+ return uty_file_out_push(vf, final) ;
+} ;
+
+/*==============================================================================
+ * Shell pipe stuff
+ *
+ */
+typedef int pipe_pair_t[2] ;
+typedef int* pipe_pair ;
+
+enum pipe_half
+{
+ in_fd = 0,
+ out_fd = 1,
+} ;
+typedef enum pipe_half pipe_half_t ;
+
+CONFIRM(STDIN_FILENO == 0) ;
+CONFIRM(STDOUT_FILENO == 1) ;
+CONFIRM(STDERR_FILENO == 2) ;
+
+enum
+{
+ stdin_fd = STDIN_FILENO,
+ stdout_fd = STDOUT_FILENO,
+ stderr_fd = STDERR_FILENO,
+
+ stds = 3
+} ;
+
+enum pipe_id
+{
+ in_pipe,
+ out_pipe,
+ ret_pipe,
+
+ pipe_count
+} ;
+typedef enum pipe_id pipe_id_t ;
+
+typedef pipe_pair_t pipe_set[pipe_count] ;
+
+static pid_t uty_pipe_fork(vty_io vio, const char* cmd_str, pipe_set pipes,
+ pipe_id_t type) ;
+static bool uty_pipe_pair(vty_io vio, const char* cmd_str,
+ const char* what, pipe_set pipes,
+ pipe_id_t id,
+ pipe_half_t half) ;
+static bool uty_pipe_fork_fail(vty_io vio, const char* cmd_str,
+ const char* what, pipe_set pipes,
+ pipe_pair pair,
+ const char* action) ;
+static void uty_pipe_close_half_pipe(pipe_pair pair, pipe_half_t half) ;
+static void uty_pipe_open_complete(vio_vf vf, pid_t pid, int ret_fd,
+ vio_vf slave) ;
+static bool uty_pipe_exec_prepare(vty_io vio, pipe_set pipes) ;
+
+static cmd_return_code_t uty_pipe_return_close(vio_vf vf, bool final) ;
+static cmd_return_code_t uty_pipe_return_empty(vio_vf vf, bool final) ;
+static cmd_return_code_t uty_pipe_collect_child(vio_vf vf, bool final) ;
+static cmd_return_code_t uty_pipe_release_slave(vio_vf vf, bool final) ;
+
+static cmd_return_code_t uty_pipe_shovel(vio_vf vf, bool final, bool closing) ;
+
+static void uty_pipe_read_ready(vio_vfd vfd, void* action_info) ;
+static vty_timer_time uty_pipe_read_timeout(vio_timer timer,
+ void* action_info) ;
+static void uty_pipe_return_ready(vio_vfd vfd, void* action_info) ;
+static vty_timer_time uty_pipe_return_timeout(vio_timer timer,
+ void* action_info) ;
+static void uty_pipe_write_ready(vio_vfd vfd, void* action_info) ;
+static vty_timer_time uty_pipe_write_timeout(vio_timer timer,
+ void* action_info) ;
+
+/*------------------------------------------------------------------------------
+ * Open pipe whose child's stdout is going to be read and executed as commands.
+ *
+ * The child's stderr is read, separately, and that is sent to the current
+ * vout.
+ *
+ * If could not open, issues message to the vio.
+ *
+ * The new vin has two vio_fd's, the standard one to read commands, and the
+ * other (the pr_vfd) to collect any stderr output (the "return") from the
+ * child, which is sent to the current vout.
+ *
+ * The current vout is made the "slave" of the new vin "master". For the
+ * return the pr_vd reads into the "slave" obuf.
+ *
+ * There are three tricky bits:
+ *
+ * - while reading from the main vfd, also reads from the return vfd.
+ *
+ * Up to eof on the main vfd, reads from the return vfd whenever tries
+ * to fetch a command. Once a command is fetched, will not shovel anything
+ * further into the slave obuf until the next command is fetched. This
+ * means that return stuff is output *between* commands.
+ *
+ * - in "blocking" state, will not block on the return unless blocks on the
+ * main vfd, or the main vfd is at eof.
+ *
+ * in "non-blocking" state, will set read ready on the main vfd, until that
+ * reaches eof, in which case sets read ready on the return vfd (if that
+ * has not yet reached eof).
+ *
+ * - the close operation will not succeed until the return vfd reaches EOF,
+ * or is a "final" close.
+ *
+ * If could not open, issues message to the vio.
+ *
+ * Returns: CMD_SUCCESS -- all set to go
+ * CMD_WARNING -- failed to open
+ */
+extern cmd_return_code_t
+uty_pipe_read_open(vty_io vio, qstring command, cmd_context context)
+{
+ pipe_set pipes ;
+ const char* cmd_str ;
+ vio_vf vf ;
+ pid_t child ;
+
+ VTY_ASSERT_CLI_THREAD_LOCKED() ;
+
+ assert(vio->vin != NULL) ; /* Not expected to be vin_base */
+
+ cmd_str = qs_make_string(command) ;
+
+ /* Fork it */
+ child = uty_pipe_fork(vio, cmd_str, pipes, in_pipe) ;
+
+ if (child < 0)
+ return CMD_WARNING ;
+
+ /* We have a pipe, so now save context */
+ uty_vin_new_context(vio, context, NULL) ;
+
+ /* OK -- now push the new input onto the vin_stack. */
+ vf = uty_vf_new(vio, cmd_str, pipes[in_pipe][in_fd], vfd_pipe, vfd_io_read) ;
+ uty_vin_push(vio, vf, VIN_PIPE, uty_pipe_read_ready,
+ uty_pipe_read_timeout, 4 * 1024) ;
+
+ /* And the err_pair is set as the return from the child */
+ uty_pipe_open_complete(vf, child, pipes[ret_pipe][in_fd], vio->vout) ;
+
+ return CMD_SUCCESS ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Open pipe which is going to be written to (or not, if shell_only), and
+ * what the pipe returns will be output to the current vout.
+ *
+ * The new vout becomes the "master", and has two vio_vfd's, one for output to
+ * the pipe (this is NULL if shell_only) and the other for the pipe return.
+ * The pipe return reads directly into the "slave" obuf.
+ *
+ * There are three tricky bits:
+ *
+ * - while writing to the output vfd, also reads from the return vfd.
+ *
+ * This is to avoid any possibility that the child process blocks because
+ * it cannot write to the return pipe.
+ *
+ * - when closing the master pipe, it must remain open up to the time it
+ * gets eof on the return pipe.
+ *
+ * This means that the all return output is collected before any further
+ * output to the slave can be done.
+ *
+ * - in "blocking" state, reads from the return only when writes to the
+ * pipe, and when closing the pipe.
+ *
+ * in "non-blocking" state, reads from the return and pushes to the slave
+ * under pselect... so sucks and blows at its own rate.
+ *
+ * Returns: CMD_SUCCESS -- all set to go
+ * CMD_WARNING -- failed to open -- message sent to vio.
+ */
+extern cmd_return_code_t
+uty_pipe_write_open(vty_io vio, qstring command, bool shell_only)
+{
+ pipe_set pipes ;
+ const char* cmd_str ;
+ pid_t child ;
+ vio_vf vf ;
+
+ VTY_ASSERT_CLI_THREAD_LOCKED() ;
+
+ cmd_str = qs_make_string(command) ;
+
+ /* Do the basic file open. */
+ child = uty_pipe_fork(vio, cmd_str, pipes, shell_only ? ret_pipe : out_pipe) ;
+ if (child < 0)
+ return CMD_WARNING ;
+
+ /* OK -- now push the new output onto the vout_stack. */
+
+ if (shell_only)
+ {
+ vf = uty_vf_new(vio, cmd_str, -1, vfd_none, vfd_io_none) ;
+ uty_vout_push(vio, vf, VOUT_SHELL_ONLY, NULL, NULL, 0) ;
+ }
+ else
+ {
+ vf = uty_vf_new(vio, cmd_str, pipes[out_pipe][out_fd],
+ vfd_pipe, vfd_io_write) ;
+ uty_vout_push(vio, vf, VOUT_PIPE, uty_pipe_write_ready,
+ uty_pipe_write_timeout, 4 * 1024) ;
+ } ;
+
+ /* Record the child pid and set up the pr_vf and enslave vout_next */
+
+ uty_pipe_open_complete(vf, child, pipes[ret_pipe][in_fd], vf->vout_next) ;
+
+ vf->pr_only = shell_only ;
+
+ /* If not blocking start up the return pipe reader. */
+
+ if (!vf->blocking)
+ vio_vfd_set_read(vf->pr_vfd, on, 0) ; /* TODO timeout */
+
+ return CMD_SUCCESS ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Complete the opening of a pipe.
+ *
+ * All pipes have a return. For in_pipes this is the child's stderr, for
+ * out_pipes this is the child's combined stdout/stderr.
+ *
+ * VIN_PIPE sucks the return in between reading command lines.
+ *
+ * VOUT_PIPE/VOUT_SHELL_ONLY suck the return at the same time as writing to
+ * the child.
+ *
+ * Sets pr_only false.
+ */
+static void
+uty_pipe_open_complete(vio_vf vf, pid_t pid, int ret_fd, vio_vf slave)
+{
+ vfd_io_type_t iot ;
+
+ vf->child = uty_child_register(pid, vf) ;
+
+ /* And configure the pipe return vfd. */
+ iot = vfd_io_read | (vf->blocking ? vfd_io_blocking : 0) ;
+
+ vf->pr_vfd = vio_vfd_new(ret_fd, vfd_pipe, iot, vf) ;
+ vf->pr_state = vf_open ;
+
+ vf->pr_only = false ;
+
+ if (!vf->blocking)
+ vio_vfd_set_read_action(vf->pr_vfd, uty_pipe_return_ready) ;
+
+ vio_vfd_set_read_timeout_action(vf->pr_vfd, uty_pipe_return_timeout) ;
+
+ /* Configure master/slave relationship. */
+ slave->pr_master = vf ;
+ vf->pr_slave = slave ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Fork fork fork
+ *
+ * Set up pipes according to type of fork:
+ *
+ * in_pipe -- input from child's stdout as main fd
+ * input from child's stderr as return fd
+ *
+ * out_pipe -- output to child's stdin as main fd
+ * input from child's stderr & stdout as return fd
+ *
+ * ret_pipe -- nothing for main fd
+ * input from child's stderr & stdout as return fd
+ *
+ * vfork to create child process and then:
+ *
+ * -- in parent close the unused part(s) of the pair(s).
+ *
+ * -- in child, close all unused fds, and move relevant part(s) of pair(s)
+ * to stdin, stdout and stderr, sort out signal, set pwd then exec sh -c.
+ */
+static pid_t
+uty_pipe_fork(vty_io vio, const char* cmd_str, pipe_set pipes, pipe_id_t type)
+{
+ pid_t child ;
+ int id ;
+
+ /* Set pipes empty */
+ for (id = 0 ; id < pipe_count ; ++id)
+ {
+ pipes[id][in_fd] = -1 ;
+ pipes[id][out_fd] = -1 ;
+ } ;
+
+ /* Open as many pipes as are required. */
+ if (type == in_pipe)
+ if (!uty_pipe_pair(vio, cmd_str, "input pipe", pipes, in_pipe, in_fd))
+ return -1 ;
+
+ if (type == out_pipe)
+ if (!uty_pipe_pair(vio, cmd_str, "output pipe", pipes, out_pipe, out_fd))
+ return -1 ;
+
+ if (!uty_pipe_pair(vio, cmd_str, "return pipe", pipes, ret_pipe, in_fd))
+ return -1 ;
+
+ /* Off to the races */
+
+ child = vfork() ;
+
+ if (child == 0) /* In child */
+ {
+
+ /* Prepare all file descriptors and then execute */
+ if (uty_pipe_exec_prepare(vio, pipes))
+ execl("/bin/bash", "bash", "-c", cmd_str, NULL) ; /* does not return */
+ else
+ exit(0x80 | errno) ;
+ }
+ else if (child > 0) /* In parent -- success */
+ {
+ /* close the pipe fds we do not need */
+ uty_pipe_close_half_pipe(pipes[in_pipe], out_fd) ;
+ uty_pipe_close_half_pipe(pipes[out_pipe], in_fd) ;
+ uty_pipe_close_half_pipe(pipes[ret_pipe], out_fd) ;
+ }
+ else if (child < 0) /* In parent -- failed */
+ uty_pipe_fork_fail(vio, cmd_str, "vfork", pipes, NULL, "child") ;
+
+ return child ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Open a pipe pair -- generate suitable error message if failed and close
+ * any early pipes that have been opened.
+ */
+static bool
+uty_pipe_pair(vty_io vio, const char* cmd_str,
+ const char* what, pipe_set pipes,
+ pipe_id_t id,
+ pipe_half_t half)
+{
+ pipe_pair pair ;
+
+ pair = pipes[id] ;
+
+ if (pipe(pair) < 0)
+ return uty_pipe_fork_fail(vio, cmd_str, what, pipes, pair, "open") ;
+
+ if (set_nonblocking(pair[half]) < 0)
+ return uty_pipe_fork_fail(vio, cmd_str, what, pipes, pair,
+ "set non-blocking") ;
+
+ return true ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Open a pipe pair -- generate suitable error message if failed and close
+ * any early pipes that have been opened.
+ */
+static bool
+uty_pipe_fork_fail(vty_io vio, const char* cmd_str,
+ const char* what, pipe_set pipes,
+ pipe_pair pair,
+ const char* action)
+{
+ int err = errno ;
+ int id ;
+
+ /* Close anything that has been opened */
+ for (id = 0 ; id < pipe_count ; ++id)
+ {
+ if (pipes[id] == pair)
+ continue ; /* ignore if just failed to open */
+
+ uty_pipe_close_half_pipe(pipes[id], in_fd) ;
+ uty_pipe_close_half_pipe(pipes[id], out_fd) ;
+ } ;
+
+ uty_out(vio, "%% Failed to %s %s for %s\n", action, what, cmd_str) ;
+
+ errno = err ;
+
+ return false ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Close half of a pipe pair, if it is open.
+ */
+static void
+uty_pipe_close_half_pipe(pipe_pair pair, pipe_half_t half)
+{
+ if (pair[half] >= 0)
+ {
+ close(pair[half]) ;
+ pair[half] = -1 ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * In the child process... prepare to exec the shell.
+ *
+ * Discard all fd's except for the those required for the shell command.
+ *
+ * Arrange for the pipe fd's to be stdin/stdout/stderr as required. The pairs
+ * are named wrt to the parent process:
+ *
+ * in_pipe -- if present, is: stdout for the child
+ * out_pipe -- if present, is: stdin for the child
+ * ret_pipe -- if present, is: stderr for the child
+ * and: stdout for the child, if no in_pipe
+ *
+ * Set current directory.
+ *
+ * Reset all signals to default state and unmask everything.
+ */
+static bool
+uty_pipe_exec_prepare(vty_io vio, pipe_set pipes)
+{
+ int std[stds] ;
+ int fd ;
+
+ /* Assign fds to child's std[] */
+
+ std[stdin_fd] = pipes[out_pipe][in_fd] ; /* stdin for child */
+ std[stdout_fd] = pipes[in_pipe][out_fd] ; /* stdout for child */
+ std[stderr_fd] = pipes[ret_pipe][out_fd] ; /* stderr for child */
+
+ /* Mark everything to be closed on exec */
+ for (fd = 0 ; fd <= 1024 ; ++fd) /* TODO -- max_fd */
+ {
+ int fd_flags ;
+ fd_flags = fcntl(fd, F_GETFD, 0) ;
+ if (fd_flags >= 0)
+ fcntl(fd, F_SETFD, fd_flags | FD_CLOEXEC) ;
+ } ;
+
+ /* Now dup anything of what we keep to ensure is above 2, making sure that
+ * the dup remains FD_CLOEXEC.
+ *
+ * This is highly unlikely, so no real extra work. It simplifies the next
+ * step which moves fds to the required position, and clears the FD_CLOEXEC
+ * flag on the duplicate.
+ */
+ for (fd = 0 ; fd < stds ; ++fd)
+ {
+ if ((std[fd] >= 0) && (std[fd] < stds))
+ if ((std[fd] = fcntl(std[fd], F_DUPFD, stds)) < 0)
+ return false ;
+ } ;
+
+ /* Now dup2 to the required location -- destination is NOT FD_CLOEXEC.
+ */
+ for (fd = 0 ; fd < stds ; ++fd)
+ {
+ if (std[fd] >= 0)
+ if (dup2(std[fd], fd) != fd)
+ return false ;
+ } ;
+
+ /* Finally (for fds) if we don't have a stdout for the child,
+ * dup stderr to stdout.
+ */
+ if ((std[stdout_fd] < 0) && (std[stderr_fd] >= 0))
+ if (dup2(stderr_fd, stdout_fd) != stdout_fd)
+ return false ;
+
+ /* Clear down all the signals magic. */
+ quagga_signal_reset() ;
+
+ /* Set the working directory */
+
+ if (qpath_setcwd(vio->vty->exec->context->dir_cd) != 0)
+ return false ;
+
+ return true ; /* coming, ready or not */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Command line fetch from a pipe and (for blocking) shovel return into
+ * slave and push.
+ *
+ * In "non-blocking" state, on CMD_WAITING may be waiting for read ready on
+ * this vf, or (indirectly) write ready on the slave.
+ *
+ * In "blocking" state, will not return until have something, or there is
+ * nothing to be had.
+ *
+ * Returns: CMD_SUCCESS -- have another command line ready to go
+ * CMD_WAITING -- would not wait for input <=> non-blocking
+ * CMD_EOF -- ran into EOF -- on input AND return
+ * CMD_IO_ERROR -- ran into an I/O error
+ * CMD_IO_TIMEOUT -- could wait no longer <=> blocking
+ *
+ * If returns CMD_EOF will have vf->vin_state == vf_eof. Further, will not
+ * have vf_eof until returns CMD_EOF (the first time).
+ *
+ * This can be called in any thread.
+ *
+ * NB: once this has signalled CMD_EOF (on the main input) the close must deal
+ * with sucking up any remaining return.
+ */
+extern cmd_return_code_t
+uty_pipe_fetch_command_line(vio_vf vf, cmd_action action)
+{
+ VTY_ASSERT_LOCKED() ; /* In any thread */
+
+ assert(vf->vin_type == VIN_PIPE) ;
+ assert((vf->vin_state == vf_open) || (vf->vin_state == vf_eof)) ;
+
+ /* TODO police the state of the two vfs ? */
+
+ while (1) /* so blocking stuff can loop round */
+ {
+ cmd_return_code_t ret ;
+ int get ;
+ qps_mini_t qm ;
+
+ /* Try to complete line straight from the buffer.
+ *
+ * If buffer is empty and have seen eof on the input, signal CMD_EOF.
+ */
+ ret = uty_fifo_command_line(vf, action) ;
+
+ if ((ret == CMD_SUCCESS) || (ret == CMD_EOF))
+ return ret ;
+
+ /* Worry about the return.
+ *
+ * If the child is outputting to both stderr and stdout, we have no
+ * way of telling the order the data was sent in -- but we treat
+ * stderr as a higher priority !
+ *
+ * Expect only CMD_SUCCESS or CMD_WAITING (non-blocking), CMD_IO_ERROR
+ * or CMD_IO_TIMEOUT (blocking).
+ */
+ if (vf->blocking && (vf->pr_state == vf_open))
+ {
+ ret = uty_pipe_shovel(vf, false, false) ; /* not final or closing */
+
+ if ((ret != CMD_SUCCESS) && (ret != CMD_EOF))
+ return ret ; /* cannot continue */
+ } ;
+
+ /* Need more from the main input. */
+
+ get = vio_fifo_read_nb(vf->ibuf, vio_vfd_fd(vf->vfd), 100) ;
+
+ if (get > 0)
+ continue ; /* loop back */
+
+ if (get == -1)
+ return CMD_IO_ERROR ; /* register error */
+
+ if (get == -2) /* EOF met immediately */
+ {
+ vf->vin_state = vf_eof ;
+ continue ; /* loop back -- deals with possible
+ final line and the return. */
+ } ;
+
+ assert (get == 0) ;
+
+ /* We get here if main input is not yet at eof. */
+ assert ((vf->vin_state == vf_open) || (vf->pr_state == vf_open)) ;
+
+ if (!vf->blocking)
+ {
+ uty_vf_set_read(vf, on) ;
+ return CMD_WAITING ;
+ } ;
+
+ /* Implement blocking I/O, with timeout */
+ qps_mini_set(qm, (vf->vin_state == vf_open) ? vio_vfd_fd(vf->vfd)
+ : -1,
+ qps_read_mnum, vf->read_timeout) ;
+ if (vf->pr_state == vf_open)
+ qps_mini_add(qm, vio_vfd_fd(vf->pr_vfd), qps_read_mnum) ;
+
+ if (qps_mini_wait(qm, NULL, false) == 0)
+ return CMD_IO_TIMEOUT ;
+
+ continue ; /* loop back */
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Command output push to a pipe and (for blocking) shovel return into
+ * slave and push.
+ *
+ * Does not push to the pipe while there is return input to be read. For
+ * blocking I/O may block in the slave output. For non-blocking I/O, will
+ * return if would block in the slave output.
+ *
+ * Until the file becomes vf_closing, will not write the end_lump of the fifo,
+ * so all output is in units of the fifo size -- which should be "chunky".
+ *
+ * Although it is unlikely to happen, if blocking, will block if cannot
+ * completely write away what is required, or enable write ready.
+ *
+ * If final, will do final shovel from return to slave and also attempt to
+ * empty any output buffer -- will not wait or block, and ignores errors.
+ *
+ * Returns: CMD_SUCCESS -- written everything
+ * CMD_WAITING -- could not write everything (<=> non-blocking)
+ * CMD_IO_ERROR -- ran into an I/O error
+ * CMD_IO_TIMEOUT -- not going to wait any longer (<=> blocking)
+ *
+ * In "non-blocking" state, on CMD_WAITING the slave output will put the
+ * master return pr_vfd into read ready state when it sees write ready, or will
+ * here set the pr_vfd into read ready state.
+ *
+ * In "blocking" state, will not return until have written everything there is,
+ * away, or cannot continue.
+ *
+ * This can be called in any thread.
+ */
+extern cmd_return_code_t
+uty_pipe_out_push(vio_vf vf, bool final)
+{
+ VTY_ASSERT_LOCKED() ; /* In any thread */
+ assert((vf->vout_state == vf_open) || (vf->vout_state == vf_closing)) ;
+ assert((vf->vout_type == VOUT_PIPE) || (vf->vout_type == VOUT_SHELL_ONLY)) ;
+
+ /* If blocking, see if there is anything in the return, and shovel.
+ *
+ * For non-blocking, the pselect process looks after this.
+ */
+ if (vf->blocking && (vf->pr_state == vf_open))
+ {
+ cmd_return_code_t ret ;
+
+ ret = uty_pipe_shovel(vf, final, false) ; /* not closing */
+
+ if ((ret != CMD_SUCCESS) && (ret != CMD_EOF) && !final)
+ return ret ;
+ } ;
+
+ /* Now write away everything we can -- nothing if pr_only.
+ */
+ if (!vf->pr_only)
+ {
+ bool all ;
+
+ all = (vf->vout_state == vf_closing) ;
+
+ while (1)
+ {
+ qps_mini_t qm ;
+ int put ;
+
+ put = vio_fifo_write_nb(vf->obuf, vio_vfd_fd(vf->vfd), all) ;
+
+ if (put < 0)
+ return CMD_IO_ERROR ; /* TODO */
+
+ if ((put == 0) || final) /* all gone or final */
+ break ;
+
+ /* Cannot write everything away without waiting */
+ if (!vf->blocking)
+ {
+ uty_vf_set_write(vf, on) ;
+ return CMD_WAITING ;
+ } ;
+
+ /* Implement blocking I/O, with timeout */
+ qps_mini_set(qm, vio_vfd_fd(vf->vfd), qps_write_mnum,
+ vf->write_timeout) ;
+ if (qps_mini_wait(qm, NULL, false) == 0)
+ return CMD_IO_TIMEOUT ;
+
+ /* Loop back to vio_fifo_write_nb() */
+ } ;
+ } ;
+
+ return CMD_SUCCESS ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Shovel from return to slave, pushing the slave as we go.
+ *
+ * For VIN_PIPE: this is called between command line reads, when a command
+ * line read needs more input. Wants to suck the return dry before proceeding
+ * to read any more command lines. Is also called when the vin is closed.
+ *
+ * For VOUT_PIPE: for blocking vfs this is called each time the output is
+ * pushed -- to keep the process moving. And when the vout is closed, to
+ * complete the process. For non-blocking vfs, TODO
+ *
+ * For VOUT_SHELL_ONLY: same as VOUT_PIPE.
+ *
+ * For blocking, keeps going until can read no more from the return. The slave
+ * output may block, and could time out.
+ *
+ * With non-blocking, reads from the return and pushes at the slave. If the
+ * slave obuf is not emptied by the push, the slave will set the master
+ * read ready, when the slave goes write-ready.
+ *
+ * For "final" does not attempt to read anything from the return, but sets it
+ * vf_eof.
+ *
+ * For "closing" (if not "final"), if blocking will block on the return, until
+ * get eof (or time out).
+ *
+ * Returns: CMD_SUCCESS -- shovelled everything, but not hit EOF
+ * if "closing" <=> non-blocking
+ * CMD_EOF -- shovelled everything and is now at EOF
+ * CMD_WAITING -- waiting for slave write-ready <=> non-blocking
+ * CMD_IO_ERROR -- ran into an I/O error
+ * CMD_IO_TIMEOUT -- could wait no longer <=> blocking
+ *
+ * This can be called in any thread.
+ */
+static cmd_return_code_t
+uty_pipe_shovel(vio_vf vf, bool final, bool closing)
+{
+ vio_vf slave ;
+
+ VTY_ASSERT_LOCKED() ; /* In any thread */
+
+ // TODO other pr_state ??? vf_closed/vf_closing/vf_error ??
+
+ /* Suck and blow -- may block in uty_cmd_out_push()
+ *
+ * Note that we do not push the slave if there is nothing new from the
+ * return -- there shouldn't be anything pending in the slave obuf.
+ */
+ slave = vf->pr_slave ;
+ assert(vf == slave->pr_master) ;
+
+ while (!final && (vf->pr_state == vf_open))
+ {
+ cmd_return_code_t ret ;
+ int get ;
+
+ get = vio_fifo_read_nb(slave->obuf, vio_vfd_fd(vf->pr_vfd), 10) ;
+
+ if (get == 0)
+ {
+ qps_mini_t qm ;
+
+ if (!closing || !vf->blocking)
+ return CMD_SUCCESS ; /* quit if now dry */
+
+ /* Implement blocking I/O, with timeout */
+ qps_mini_set(qm, vio_vfd_fd(vf->pr_vfd), qps_read_mnum,
+ vf->read_timeout) ;
+ if (qps_mini_wait(qm, NULL, false) == 0)
+ return CMD_IO_TIMEOUT ;
+ }
+
+ else if (get >= 0)
+ {
+ ret = uty_cmd_out_push(slave, final) ; /* may block etc. */
+
+ if (ret != CMD_SUCCESS)
+ return ret ;
+ }
+
+ else if (get == -1)
+ return CMD_IO_ERROR ; /* register error TODO */
+
+ else
+ {
+ assert (get == -2) ; /* eof on the return */
+ break ;
+ } ;
+ } ;
+
+ vf->pr_state = vf_eof ; /* Set EOF TODO: willy nilly ? */
+
+ return CMD_EOF ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Pipe is ready to read -- this is the call-back planted in the vf->vfd, of
+ * type VIN_PIPE.
+ *
+ * Can restart the command_queue.
+ *
+ * Note that the read_ready state and any time out are automatically
+ * disabled when they go off.
+ */
+static void
+uty_pipe_read_ready(vio_vfd vfd, void* action_info)
+{
+ vio_vf vf ;
+
+ VTY_LOCK() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ vf = action_info ;
+ assert(vf->vfd == vfd) ;
+
+ uty_cmd_signal(vf->vio, CMD_SUCCESS) ;
+
+ VTY_UNLOCK() ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Pipe has timed out, waiting to read -- this is the call-back planted in the
+ * vf->vfd, of type VIN_PIPE.
+ *
+ * ????
+ */
+static vty_timer_time
+uty_pipe_read_timeout(vio_timer timer, void* action_info)
+{
+ vio_vf vf ;
+
+ VTY_LOCK() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ vf = action_info ;
+ assert(vf->vfd->read_timer == timer) ;
+
+// TODO .... signal time out
+
+ VTY_UNLOCK() ;
+
+ return 0 ; /* Do not restart timer */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Pipe return is ready to read -- this is the call-back planted in the
+ * vf->pr_vfd:
+ *
+ * VIN_PIPE: used only when closing the vin, and are waiting for
+ * the return to be drained.
+ *
+ * VOUT_PIPE: used to continually shovel from the return to the
+ * slave -- including while closing.
+ *
+ * VOUT_SHELL_ONLY: used to continually shovel from the return to the
+ * slave -- which happens while closing.
+ *
+ * If all is well, and more return input is expected, re-enables read ready.
+ *
+ * If uty_pipe_shovel() returns CMD_WAITING, then is waiting for the slave
+ * output buffer to clear. The slave will kick uty_pipe_return_slave_ready().
+ *
+ * For all other returns, kick the command loop, which if it is waiting is TODO
+ */
+static void
+uty_pipe_return_ready(vio_vfd vfd, void* action_info)
+{
+ cmd_return_code_t ret ;
+ vio_vf vf ;
+
+ VTY_LOCK() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ vf = action_info ;
+ assert(vf->pr_vfd == vfd) ;
+
+ ret = uty_pipe_shovel(vf, false, false) ; /* not final or closing */
+ /* TODO -- errors !! */
+ if (ret == CMD_SUCCESS)
+ vio_vfd_set_read(vf->pr_vfd, on, 0) ; /* expect more */
+ else if (ret != CMD_WAITING)
+ uty_cmd_signal(vf->vio, (ret == CMD_EOF) ? CMD_SUCCESS
+ : ret) ; /* in case TODO */
+
+ VTY_UNLOCK() ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Pipe return slave is ready to write.
+ *
+ * This is called by the pipe return slave, to prompt the master when the slave
+ * has been waiting to be able to write away its output buffer.
+ *
+ * This will set read ready on the return, to keep the process of shovelling
+ * from return to slave going.
+ */
+extern void
+uty_pipe_return_slave_ready(vio_vf slave)
+{
+ vio_vf vf ;
+
+ vf = slave->pr_master ;
+ assert(vf->pr_slave == slave) ;
+
+ vio_vfd_set_read(vf->pr_vfd, on, 0) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Pipe return has timed out -- this is the call-back planted in the
+ * vf->pr_vfd of either a VIN_PIPE or a VOUT_PIPE ????
+ *
+ * ????
+ */
+static vty_timer_time
+uty_pipe_return_timeout(vio_timer timer, void* action_info)
+{
+ vio_vf vf ;
+
+ VTY_LOCK() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ vf = action_info ;
+ assert(vf->pr_vfd->read_timer == timer) ;
+
+//cq_continue(vf->vio->vty) ; TODO
+
+ VTY_UNLOCK() ;
+
+ return 0 ; /* Do not restart timer */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Pipe output is ready to write -- this is the call-back planted in the
+ * vf->vfd of a VOUT_PIPE
+ *
+ * Note that the write_ready state and any time out are automatically
+ * disabled when they go off.
+ */
+static void
+uty_pipe_write_ready(vio_vfd vfd, void* action_info)
+{
+ cmd_return_code_t ret ;
+ vio_vf vf ;
+
+ VTY_LOCK() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ vf = action_info ;
+ assert(vf->vfd == vfd) ;
+
+ ret = uty_pipe_out_push(vf, false) ; /* not final */
+
+ if (ret != CMD_WAITING)
+ uty_cmd_signal(vf->vio, ret) ;
+
+ if ((ret == CMD_SUCCESS) && (vf->pr_master != NULL))
+ uty_pipe_return_slave_ready(vf) ;
+
+ VTY_UNLOCK() ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Pipe output has timed out -- this is the call-back planted in the
+ * vf->vfd of a VOUT_PIPE
+ *
+ * ????
+ */
+static vty_timer_time
+uty_pipe_write_timeout(vio_timer timer, void* action_info)
+{
+ vio_vf vf ;
+
+ VTY_LOCK() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ vf = action_info ;
+ assert(vf->vfd->write_timer == timer) ;
+
+// TODO
+
+ VTY_UNLOCK() ;
+
+ return 0 ; /* Do not restart timer */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Tidy up after input pipe has been read closed.
+ *
+ * Nothing needs to be done with the main input, but do need to close the
+ * return, collect the child and release the slave.
+ */
+extern cmd_return_code_t
+uty_pipe_read_close(vio_vf vf, bool final)
+{
+ return uty_pipe_return_close(vf, final) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Close VOUT_PIPE or VOUT_SHELL_ONLY.
+ *
+ * For not-final, wish to flush output buffer. If final will attempt to empty
+ * the output buffer -- but will not wait or block, and ignores errors.
+ *
+ * Must then close the return, collect the child and release the slave.
+ *
+ * Returns: CMD_SUCCESS -- pushed what there was, or final
+ * CMD_WAITING -- would have blocked <=> non-blocking
+ * CMD_IO_ERROR -- ran into an I/O error
+ * CMD_IO_TIMEOUT -- could wait no longer <=> blocking
+ */
+extern cmd_return_code_t
+uty_pipe_write_close(vio_vf vf, bool final, bool base, bool shell_only)
+{
+ cmd_return_code_t ret ;
+
+ VTY_ASSERT_CAN_CLOSE_VF(vf) ;
+
+ /* If the main vfd is still there, keep pushing.
+ *
+ * Close it if manage to empty out buffers (or final) -- signals end to the
+ * child process.
+ */
+ if (!vf->pr_only)
+ {
+ ret = uty_pipe_out_push(vf, final) ;
+
+ if ((ret != CMD_SUCCESS) && !final)
+ return ret ;
+
+ /* Now need to close the output vfd to signal to the child that we
+ * are done.
+ *
+ * Sets pr_only so that if does go CMD_WAITING, then future call of
+ * uty_pipe_out_push() will not attempt any I/O ! vf->vfd is set NULL,
+ * which will be ignored by any future vio_vfd_close().
+ */
+ vf->vfd = vio_vfd_close(vf->vfd) ;
+ vf->pr_only = true ; /* only the return is left */
+ } ;
+
+ /* If the return is still open, try to empty and close that. */
+ return uty_pipe_return_close(vf, final) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Close the return on a pipe.
+ *
+ * If not final, may need to drain the return.
+ *
+ * Need to collect the child to complete the process. Once the child is
+ * collected (or have given up waiting, will send a message to the slave if
+ * not completed with return code == 0.
+ *
+ * Once any message has been pushed to the slave, can release the slave
+ * and the close process will be complete.
+ *
+ * If final, then will not block and will ignore errors. Return code reflects
+ * the last operation.
+ *
+ * Returns: CMD_SUCCESS -- closed and child collected
+ * CMD_WAITING
+ * CMD_IO_ERROR
+ * CMD_IO_TIMEOUT
+ * CMD_xxxx -- child error(s)
+ */
+static cmd_return_code_t
+uty_pipe_return_close(vio_vf vf, bool final)
+{
+ cmd_return_code_t ret ;
+
+ VTY_ASSERT_CAN_CLOSE_VF(vf) ;
+
+ /* If the return is still open, try to empty and close that. */
+ if (vf->pr_state != vf_closed)
+ {
+ ret = uty_pipe_return_empty(vf, final) ;
+
+ if (!final)
+ {
+ if (ret == CMD_SUCCESS)
+ {
+ vio_vfd_set_read(vf->pr_vfd, on, 0) ; /* TODO timeout */
+ return CMD_WAITING ;
+ } ;
+
+ if (ret != CMD_EOF)
+ return ret ;
+ } ;
+ } ;
+
+ /* If not already collected, collect the child. */
+ if (vf->child != NULL)
+ {
+ ret = uty_pipe_collect_child(vf, final) ;
+
+ if ((ret != CMD_SUCCESS) && !final)
+ return ret ;
+ } ;
+
+ /* Finally, release the slave */
+ return uty_pipe_release_slave(vf, final) ;
+}
+
+/*------------------------------------------------------------------------------
+ * If the return is still open, shovel from return to slave.
+ *
+ * If final will not block or wait, and ignores errors -- return code reflects
+ * the last operation.
+ *
+ * If blocking, will block until return hits EOF or timeout.
+ *
+ * Returns: CMD_SUCCESS -- shovelled what there was, may be more to come
+ * <=> non-blocking
+ * CMD_EOF -- shovelled what there was, is now *closed*
+ * CMD_WAITING -- waiting for slave write-ready <=> non-blocking
+ * CMD_IO_ERROR -- ran into an I/O error
+ * CMD_IO_TIMEOUT -- could wait no longer <=> blocking
+ */
+static cmd_return_code_t
+uty_pipe_return_empty(vio_vf vf, bool final)
+{
+ cmd_return_code_t ret ;
+
+ // worry about pr_state ??
+
+ ret = uty_pipe_shovel(vf, final, true) ; /* closing ! */
+
+ if ((ret == CMD_EOF) || final)
+ {
+ vf->pr_vfd = vio_vfd_close(vf->pr_vfd) ;
+ vf->pr_state = vf_closed ;
+ } ;
+
+ return ret ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Collect child.
+ *
+ * When does collect, or times out, will dismiss the child and set vf->child
+ * to NULL -- which indicates that the child is done with.
+ *
+ * Returns: CMD_SUCCESS -- child dismissed, vf->child == NULL
+ * CMD_WAITING -- waiting to collect child <=> non-blocking
+ *
+ */
+static cmd_return_code_t
+uty_pipe_collect_child(vio_vf vf, bool final)
+{
+ vio_vf slave ;
+
+ assert(vf->child != NULL) ;
+
+ /* Collect -- blocking or not blocking */
+ if (!vf->child->collected && !vf->child->overdue)
+ {
+ /* If we are !blocking and !final, leave the child collection up to
+ * the SIGCHLD system.
+ */
+ if (!vf->blocking && !final)
+ {
+ uty_child_awaited(vf->child, 6) ;
+ return CMD_WAITING ;
+ } ;
+
+ /* If we are blocking or final, try to collect the child here and
+ * now -- if not final may block here.
+ */
+ uty_child_collect(vf->child, 6, final) ;
+ } ;
+
+ /* If child is overdue, or did not terminate cleanly, write message to
+ * slave... which we then have to push...
+ */
+ slave = vf->pr_slave ;
+ assert((slave != NULL) && (vf == slave->pr_master)) ;
+
+ if (!vf->child->collected)
+ {
+ vio_fifo_printf(slave->obuf, "%% child process still running\n") ;
+ }
+ else if (WIFEXITED(vf->child->report))
+ {
+ int status = WEXITSTATUS(vf->child->report) ;
+ if (status != 0)
+ vio_fifo_printf(slave->obuf,
+ "%% child process ended normally, but with status = %d\n",
+ status) ;
+ }
+ else if (WIFSIGNALED(vf->child->report))
+ {
+ int signal = WTERMSIG(vf->child->report) ;
+ vio_fifo_printf(slave->obuf,
+ "%% child process terminated by signal = %d\n", signal) ;
+ }
+ else
+ {
+ vio_fifo_printf(slave->obuf,
+ "%% child process ended in unknown state = %d\n",
+ vf->child->report) ;
+ } ;
+
+ /* Can now dismiss the child. */
+ vf->child = uty_child_dismiss(vf->child, false) ; /* not final */
+
+ return CMD_SUCCESS ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Push output to slave a final time -- to shift any final message about state
+ * of child on closing.
+ *
+ * Then release slave.
+ */
+static cmd_return_code_t
+uty_pipe_release_slave(vio_vf vf, bool final)
+{
+ cmd_return_code_t ret ;
+ vio_vf slave ;
+
+ ret = CMD_SUCCESS ;
+
+ slave = vf->pr_slave ;
+ if (slave != NULL)
+ {
+ assert(vf == slave->pr_master) ;
+
+ ret = uty_cmd_out_push(slave, final) ; /* may block etc. */
+
+ if ((ret != CMD_SUCCESS) && !final)
+ return ret ;
+
+ slave->pr_master = NULL ; /* release slave */
+ vf->pr_slave = NULL ;
+ } ;
+
+ return ret ;
+} ;
+
+/*==============================================================================
+ * stdout and stderr
+ *
+ *
+ */
+
diff --git a/lib/vty_io_file.h b/lib/vty_io_file.h
new file mode 100644
index 00000000..5bb5a607
--- /dev/null
+++ b/lib/vty_io_file.h
@@ -0,0 +1,70 @@
+/* VTY I/O for Files -- Header
+ *
+ * Copyright (C) 2010 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * 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_VTY_IO_FILE_H
+#define _ZEBRA_VTY_IO_FILE_H
+
+#include "misc.h"
+
+#include "vty_io.h"
+#include "command_parse.h"
+
+/*==============================================================================
+ * Here are structures and other definitions which are shared by:
+ *
+ * vty_io.c -- the main VTY I/O stuff
+ *
+ * for I/O to files
+ */
+
+/*==============================================================================
+ * Functions
+ */
+extern vty vty_config_read_open(int fd, const char* name, bool full_lex) ;
+extern cmd_return_code_t uty_config_read_close(vio_vf vf, bool final) ;
+
+extern cmd_return_code_t uty_file_read_open(vty_io vio, qstring name,
+ cmd_context context) ;
+extern cmd_return_code_t uty_file_write_open(vty_io vio, qstring name,
+ bool append, cmd_context context) ;
+
+extern cmd_return_code_t uty_file_fetch_command_line(vio_vf vf,
+ cmd_action action) ;
+extern cmd_return_code_t uty_file_out_push(vio_vf vf, bool final) ;
+
+extern cmd_return_code_t uty_file_read_close(vio_vf vf, bool final) ;
+extern cmd_return_code_t uty_file_write_close(vio_vf vf, bool final, bool base) ;
+
+
+extern cmd_return_code_t uty_pipe_read_open(vty_io vio, qstring command,
+ cmd_context context) ;
+extern cmd_return_code_t uty_pipe_write_open(vty_io vio, qstring command,
+ bool shell_only) ;
+extern cmd_return_code_t uty_pipe_fetch_command_line(vio_vf vf,
+ cmd_action action) ;
+extern cmd_return_code_t uty_pipe_out_push(vio_vf vf, bool final) ;
+extern void uty_pipe_return_slave_ready(vio_vf slave) ;
+extern cmd_return_code_t uty_pipe_read_close(vio_vf vf, bool final) ;
+extern cmd_return_code_t uty_pipe_write_close(vio_vf vf, bool final, bool base,
+ bool shell_only) ;
+
+#endif
diff --git a/lib/vty_io_shell.c b/lib/vty_io_shell.c
new file mode 100644
index 00000000..0b3cb5c4
--- /dev/null
+++ b/lib/vty_io_shell.c
@@ -0,0 +1,369 @@
+/* VTY IO SHELL -- VTY Shell I/O
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * Revisions: Copyright (C) 2010 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * 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.
+ */
+
+#include "zebra.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
+
+#if 0
+
+/*------------------------------------------------------------------------------
+ * 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 ;
+
+ 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_nexus)
+ {
+ 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;
+
+ /* Kick start the CLI etc. */
+ uty_sock_set_readiness(&vio->sock, write_ready) ;
+
+ return vty;
+} ;
+
+
+/*------------------------------------------------------------------------------
+ * 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))
+ {
+ zlog(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)
+ {
+ zlog(NULL, LOG_ERR, "Cannot create unix stream socket: %s",
+ errtoa(errno, 0).str) ;
+ return -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);
+
+ sa_len = SUN_LEN(&sa_un) ;
+
+#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
+ sa_un.sun_len = sa_len ;
+#endif
+
+ old_mask = umask (0007);
+
+ ret = bind (sock, (struct sockaddr *) &sa_un, sa_len) ;
+ if (ret < 0)
+ zlog(NULL, LOG_ERR, "Cannot bind path %s: %s", path, errtoa(errno, 0).str);
+
+ if (ret >= 0)
+ ret = set_nonblocking(sock);
+
+ if (ret >= 0)
+ {
+ ret = listen (sock, 5);
+ if (ret < 0)
+ zlog(NULL, LOG_ERR, "listen(fd %d) failed: %s", sock,
+ errtoa(errno, 0).str) ;
+ } ;
+
+ zprivs_get_ids(&ids);
+
+ if (ids.gid_vty > 0)
+ {
+ /* set group of socket */
+ if ( chown (path, -1, ids.gid_vty) )
+ zlog (NULL, LOG_ERR, "uty_serv_vtysh: could chown socket, %s",
+ errtoa(errno, 0).str) ;
+ }
+
+ umask (old_mask);
+
+ /* 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 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * 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)
+ {
+ zlog (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 */
+ zlog (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.
+ */
+} ;
+
+/*------------------------------------------------------------------------------
+ * 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 <<<<<<<<<<<<< */
+ } ;
+ } ;
+} ;
+
+#endif
diff --git a/lib/vty_io_shell.h b/lib/vty_io_shell.h
new file mode 100644
index 00000000..87cd92d0
--- /dev/null
+++ b/lib/vty_io_shell.h
@@ -0,0 +1,55 @@
+/* VTY IO SHELL -- VTY Shell I/O -- header
+ * Virtual terminal [aka TeletYpe] interface routine.
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * Revisions: Copyright (C) 2010 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * 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_VTY_IO_SHELL_H
+#define _ZEBRA_VTY_IO_SHELL_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:
+ *
+ * vty_io.c -- the main VTY I/O stuff
+ *
+ * for I/O for VTY Shell Server.
+ */
+
+/*==============================================================================
+ * Functions
+ */
+
+extern int uty_vprintf_shell(vty_io vio, const char *format, va_list args) ;
+
+#endif /* _ZEBRA_VTY_IO_SHELL_H */
diff --git a/lib/vty_io_term.c b/lib/vty_io_term.c
new file mode 100644
index 00000000..0a1efc72
--- /dev/null
+++ b/lib/vty_io_term.c
@@ -0,0 +1,1549 @@
+/* VTY IO TERM -- Telnet Terminal I/O
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * Revisions: Copyright (C) 2010 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * 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.
+ */
+#include "misc.h"
+
+#include "vty_local.h"
+#include "vty_io.h"
+#include "vty_io_term.h"
+#include "vty_io_file.h"
+#include "vty_cli.h"
+#include "vty_command.h"
+#include "vio_fifo.h"
+
+#include "log_local.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/socket.h>
+#include <errno.h>
+
+/*==============================================================================
+ * 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.
+ *
+ *
+ *
+ *
+ */
+
+
+
+
+
+
+/*==============================================================================
+ * 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
+
+/*==============================================================================
+ * Opening and closing VTY_TERMINAL type
+ */
+
+typedef enum {
+ utw_error = 0,
+ utw_done = BIT(0), /* all possible is done */
+ utw_stopped = BIT(1),
+ utw_blocked = BIT(2), /* I/O blocked */
+ utw_paused = utw_blocked | utw_stopped,
+} utw_ret_t ;
+
+static void uty_term_read_ready(vio_vfd vfd, void* action_info) ;
+static void uty_term_write_ready(vio_vfd vfd, void* action_info) ;
+static vty_timer_time uty_term_read_timeout(vio_timer timer,
+ void* action_info) ;
+static vty_timer_time uty_term_write_timeout(vio_timer timer,
+ void* action_info) ;
+static utw_ret_t uty_term_write(vio_vf vf) ;
+
+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) ;
+
+/*------------------------------------------------------------------------------
+ * 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_open(int sock_fd, union sockunion *su)
+{
+ node_type_t node ;
+ vty vty ;
+ vty_io vio ;
+ vio_vf vf ;
+
+ VTY_ASSERT_CLI_THREAD_LOCKED() ;
+
+ /* The initial vty->node will be authentication, unless the host does not
+ * require that, in which case it may be a number of other things.
+ *
+ * Note that setting NULL_NODE at this point will cause the terminal to be
+ * closed very quickly, after issuing suitable message.
+ */
+ node = (host.password != NULL) ? AUTH_NODE : NULL_NODE ;
+
+ if (host.no_password_check)
+ {
+ if (host.restricted_mode)
+ node = RESTRICTED_NODE;
+ else if (host.advanced && (host.enable == NULL))
+ node = ENABLE_NODE;
+ else
+ node = VIEW_NODE;
+ } ;
+
+ /* Allocate new vty structure and set up default values.
+ *
+ * This completes the initialisation of the vty object, except that the
+ * execution and vio objects are largely empty.
+ */
+ vty = uty_new(VTY_TERMINAL, node) ;
+ vio = vty->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
+ *
+ * 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) ;
+
+ uty_vin_push( vio, vf, VIN_TERM, uty_term_read_ready,
+ uty_term_read_timeout,
+ 0) ; /* no ibuf required */
+ uty_vout_push(vio, vf, VOUT_TERM, uty_term_write_ready,
+ uty_term_write_timeout,
+ 4096) ; /* obuf is required */
+
+ vf->read_timeout = host.vty_timeout_val ; /* current EXEC timeout */
+
+ /* Set up the CLI object & initialise */
+ vf->cli = uty_cli_new(vf) ;
+
+ /* 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 about to authenticate, issue friendly message.
+ *
+ * If cannot authenticate, issue an error message.
+ */
+ if (vty->node == AUTH_NODE)
+ vty_out(vty, "User Access Verification\n") ;
+ else if (vty->node == NULL_NODE)
+ vty_out(vty, "%% Cannot continue because no password is set\n") ;
+
+ /* Enter the command loop. */
+ uty_cmd_loop_enter(vio) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Command line fetch from a VTY_TERMINAL.
+ *
+ * Fetching a command line <=> the previous command has completed.
+ *
+ * Returns: CMD_SUCCESS -- have another command line ready to go
+ * CMD_WAITING -- would not wait for input <=> non-blocking
+ * CMD_EOF -- ??????
+ *
+ * This can be called in any thread.
+ *
+ * NB: this does not signal CMD_EOF TODO ????
+ */
+extern cmd_return_code_t
+uty_term_fetch_command_line(vio_vf vf, cmd_action action, cmd_context context)
+{
+ return uty_cli_want_command(vf->cli, action, context) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Showing error context for the VTY_TERMINAL command line.
+ *
+ * If the stack is at level == 1, then the last full line displayed will be
+ * the line in which the error occurred (unless have monitor output, which
+ * there is little we can do about). So there is no further output required.
+ * The command line is indented by the current prompt.
+ *
+ * If the stack is at level > 1, then may or may not have had output separating
+ * the command line from the current position, so we output the command line
+ * to provide context.
+ *
+ * Returns: indent position of command line
+ */
+extern uint
+uty_term_show_error_context(vio_vf vf, vio_fifo ebuf, uint depth)
+{
+ if (depth == 1)
+ return uty_cli_prompt_len(vf->cli) ;
+
+ vio_fifo_printf(ebuf, "%% in command line:\n") ;
+ vio_fifo_printf(ebuf, " %s\n", qs_make_string(vf->cli->clx)) ;
+
+ return 2 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Push output to the terminal.
+ *
+ * Returns: CMD_SUCCESS -- all buffers are empty, or final
+ * CMD_WAITING -- all buffers are not empty
+ * CMD_IO_ERROR -- failed -- final or not.
+ *
+ * This can be called in any thread. If "final" will not turn on any
+ * read/write ready stuff.
+ */
+extern cmd_return_code_t
+uty_term_out_push(vio_vf vf, bool final)
+{
+ vty_cli cli = vf->cli ;
+ utw_ret_t done ;
+
+ /* If have something in the obuf that needs to be written, then if not already
+ * out_active, make sure the command line is clear, and set out_active.
+ */
+ if (!cli->out_active && !vio_fifo_empty(vf->obuf))
+ {
+ uty_cli_wipe(cli, 0) ;
+ cli->out_active = true ;
+ vio_lc_counter_reset(cli->olc) ;
+ } ;
+
+ /* Give the terminal writing a shove.
+ *
+ * If final, keep pushing while succeeds in writing without blocking.
+ */
+ if (final)
+ cli->flush = cli->out_active ; /* make sure empty everything */
+
+ do
+ {
+ if (final)
+ vio_lc_counter_reset(cli->olc) ;
+
+ done = uty_term_write(vf) ;
+ }
+ while (final && (done == utw_paused)) ;
+
+ if (!final)
+ {
+ if (done == utw_error)
+ return CMD_IO_ERROR ; /* TODO */
+
+ if ((done & utw_blocked) != 0)
+ {
+ confirm((utw_paused & utw_blocked) != 0) ;
+
+ uty_term_set_readiness(vf, write_ready) ;
+ return CMD_WAITING ;
+ } ;
+ } ;
+
+ return CMD_SUCCESS ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * The read side of the vfd has been closed. Close down CLI as far as
+ * possible, given that output may be continuing.
+ *
+ * Expects to be called once only for the VTY_TERMINAL.
+ *
+ * There is no difference between final and not-final close in this case.
+ *
+ * Note that this is only closed when the VTY_TERMINAL is forcibly closed, or
+ * when the user quits.
+ *
+ * Returns: CMD_SUCCESS -- all is quiet.
+ */
+extern cmd_return_code_t
+uty_term_read_close(vio_vf vf, bool final)
+{
+ vty_io vio ;
+
+ /* Get the vio and ensure that we are all straight */
+ vio = vf->vio ;
+ assert((vio->vin == vio->vin_base) && (vio->vin == vf)) ;
+ assert(vio->vin->vin_state == vf_closing) ;
+
+ /*
+ */
+ uty_set_monitor(vio, 0) ;
+
+ /* Close the CLI as far as possible, leaving output side intact.
+ *
+ * Can generate some final output, which will be dealt with as the output
+ * side is closed.
+ */
+ uty_cli_close(vf->cli, false) ;
+
+ /* Log closing of VTY_TERMINAL
+ */
+ assert(vio->vty->type == VTY_TERMINAL) ;
+ zlog (NULL, LOG_INFO, "Vty connection (fd %d) close", vio_vfd_fd(vf->vfd)) ;
+
+ return CMD_SUCCESS ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Try to output the close reason to the given VOUT_TERM.
+ *
+ * If there is anything pending to be output, discard it, first. The obuf has
+ * already been cleared.
+ *
+ * This will be pushed out when the VOUT_TERM is finally closed.
+ */
+extern void
+uty_term_close_reason(vio_vf vf, const char* reason)
+{
+ vio_lc_clear(vf->cli->olc) ;
+ vio_fifo_clear(vf->cli->cbuf, true) ;
+
+ uty_cli_out(vf->cli, "%% %s%s", reason, uty_cli_newline) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Close the writing side of VTY_TERMINAL.
+ *
+ * Assumes that the read side has been closed already, and so this is the last
+ * thing to be closed.
+ *
+ * Kicks the output side:
+ *
+ * if final, will push as much as possible until would block.
+ *
+ * if not final, will push another tranche and let the uty_term_ready() keep
+ * pushing until buffers empty and can uty_cmd_signal().
+ *
+ * Returns: CMD_SUCCESS => all written (or final) and CLI closed.
+ * CMD_WAITING => write ready running to empty the buffers
+ * CMD_IO_ERROR =>
+ * others TODO
+ */
+extern cmd_return_code_t
+uty_term_write_close(vio_vf vf, bool final, bool base)
+{
+ cmd_return_code_t ret ;
+ vty_io vio ;
+
+ /* Get the vio and ensure that we are all straight
+ *
+ * Can only be the vout_base and must also be the vin_base, and the vin_base
+ * must now be closed.
+ */
+ vio = vf->vio ;
+ assert((vio->vout == vio->vout_base) && (vio->vout == vf)) ;
+ assert((vio->vin == vio->vin_base) && (vio->vin->vin_state == vf_closed)) ;
+
+ ret = uty_term_out_push(vf, final) ;
+
+ if (final)
+ vf->cli = uty_cli_close(vf->cli, final) ;
+
+ return ret ;
+} ;
+
+/*==============================================================================
+ * Action routines for VIN_TERM/VOUT_TERM type vin/vout objects
+ */
+
+/*==============================================================================
+ * Readiness and the VIN_TERM type vin.
+ *
+ * For TERM stuff the driving force is write ready. This is used to prompt the
+ * VOUT_TERM when there is outstanding output (obviously), but also if there
+ * is buffered input in the keystroke stream.
+ *
+ * The VIN_TERM uses read ready only when it doesn't set write ready. Does
+ * not set both at once.
+ */
+
+static void uty_term_ready(vio_vf vf) ;
+
+/*------------------------------------------------------------------------------
+ * 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() ;
+
+ if ((ready & write_ready) != 0)
+ uty_vf_set_write(vf, on) ;
+ else if ((ready & read_ready) != 0)
+ uty_vf_set_read(vf, on) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Terminal read ready
+ */
+static void
+uty_term_read_ready(vio_vfd vfd, void* action_info)
+{
+ vio_vf vf = action_info ;
+
+ assert(vfd == vf->vfd) ;
+
+ vf->cli->paused = false ; /* read ready clears paused */
+ uty_term_ready(vf) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Terminal write ready
+ */
+static void
+uty_term_write_ready(vio_vfd vfd, void* action_info)
+{
+ vio_vf vf = action_info ;
+
+ assert(vfd == vf->vfd) ;
+
+ uty_term_ready(vf) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Terminal, something is ready -- read, write or no longer paused.
+ *
+ * 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_term_ready(vio_vf vf)
+{
+ vty_readiness_t ready ;
+ utw_ret_t done ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ /* Start by trying to write away any outstanding stuff, and then another
+ * tranche of any outstanding output.
+ */
+ ready = not_ready ;
+
+ if (!vf->cli->more_enabled)
+ vio_lc_counter_reset(vf->cli->olc) ; /* do one tranche */
+
+ done = uty_term_write(vf) ;
+
+ while (done != utw_error)
+ {
+ utw_ret_t done_before ;
+ done_before = done ;
+
+ /* Kick the CLI, which may advance either because there is more input,
+ * or because some output has now completed, or for any other reason.
+ *
+ * This may return write_ready, which is a proxy for CLI ready, and
+ * MUST be honoured, even (especially) if the output buffers are empty.
+ */
+ ready = uty_cli(vf->cli) ;
+
+ /* Now try to write away any new output which may have been generated
+ * by the CLI.
+ */
+ done = uty_term_write(vf) ;
+
+ if (done == done_before)
+ break ; /* quit if no change in response */
+ } ;
+
+ if (done == utw_error)
+ ; /* TODO !! */
+
+ if ((done & utw_blocked) != 0)
+ {
+ confirm((utw_paused & utw_blocked) != 0) ;
+ ready |= write_ready ;
+ } ;
+
+ if ((done != utw_blocked) && (done != utw_error))
+ {
+ /* Since is not output blocked, tell master that is now ready. */
+ if (vf->pr_master != NULL)
+ uty_pipe_return_slave_ready(vf) ;
+ } ;
+
+ uty_term_set_readiness(vf, ready) ;
+
+ /* Signal the command loop if out_active and the buffers empty out.
+ */
+ if (done == utw_done)
+ uty_cmd_signal(vf->vio, CMD_SUCCESS) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Read timer has expired.
+ *
+ * If closing, 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 vty_timer_time
+uty_term_read_timeout(vio_timer timer, void* action_info)
+{
+ vio_vf vf = action_info ;
+
+ assert(timer == vf->vfd->read_timer) ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ vf->vin_state = vf_timed_out ;
+ keystroke_stream_set_eof(vf->cli->key_stream, true) ; /* timed out */
+
+ vf->cli->paused = false ;
+
+ uty_term_ready(vf) ;
+
+ return 0 ;
+ } ;
+
+/*------------------------------------------------------------------------------
+ * 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
+ *
+ * Otherwise, half close the VTY and leave it to the death-watch to sweep up.
+ */
+static vty_timer_time
+uty_term_write_timeout(vio_timer timer, void* action_info)
+{
+ vio_vf vf = action_info ;
+
+ assert(timer == vf->vfd->read_timer) ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ vf->cli->paused = false ;
+
+//uty_close(vio, true, qs_set(NULL, "Timed out")) ; TODO
+
+ return 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Timeout on the cli->paused timer -- clear paused and treat as CLI ready.
+ */
+extern void
+vty_term_pause_timeout(qtimer qtr, void* timer_info, qtime_mono_t when)
+{
+ vty_cli cli ;
+
+ VTY_LOCK() ;
+
+ cli = timer_info ;
+ assert(cli->pause_timer == qtr) ;
+
+ if (cli->paused)
+ {
+ cli->paused = false ;
+ uty_term_ready(cli->vf) ;
+ } ;
+
+ VTY_UNLOCK() ;
+} ;
+
+/*==============================================================================
+ * Reading from VTY_TERMINAL.
+ *
+ * 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
+ * > 0 => read at least one byte
+ * -1 => EOF (or not open, or failed, or timed out, ...)
+ */
+extern int
+uty_term_read(vio_vf vf, keystroke steal)
+{
+ unsigned char buf[500] ;
+ int get ;
+
+ if (vf->vin_state != vf_open)
+ return -1 ; /* at EOF if not open */
+
+ get = read_nb(vio_vfd_fd(vf->vfd), buf, sizeof(buf)) ;
+ if (get >= 0)
+ keystroke_input(vf->cli->key_stream, buf, get, steal) ;
+ else if (get < 0)
+ {
+ if (get == -1)
+ uty_vf_error(vf, "read", errno) ;
+
+ keystroke_input(vf->cli->key_stream, NULL, 0, steal) ;
+ /* Tell keystroke stream that EOF met */
+ get = -1 ;
+ } ;
+
+ return get ;
+} ;
+
+/*==============================================================================
+ * Writing to VOUT_TERM -- driven by ready state.
+ *
+ * There are two sets of buffering:
+ *
+ * cli->cbuf -- command line -- reflects the status of the command line
+ *
+ * vf->obuf -- command output -- which is written to the file only while
+ * out_active, and not blocked in more_wait.
+ *
+ * The cli output takes precedence.
+ *
+ * Output of command stuff is subject to line_control, and may go through the
+ * "--more--" mechanism.
+ */
+
+static utw_ret_t uty_term_write_lc(vio_line_control lc, vio_vf vf,
+ vio_fifo vff) ;
+
+/*------------------------------------------------------------------------------
+ * Have some (more) monitor output to send to the vty.
+ *
+ * Make sure the command line is clear, then claim screen for monitor output
+ * and attempt to empty out buffers.
+ */
+extern void
+uty_term_mon_write(vio_vf vf)
+{
+ utw_ret_t done ;
+
+ uty_cli_pre_monitor(vf->cli) ; /* make sure in a fit state */
+
+ done = uty_term_write(vf) ; /* TODO -- errors !! */
+
+ if ((done & utw_blocked) != 0)
+ {
+ confirm((utw_paused & utw_blocked) != 0) ;
+
+ uty_term_set_readiness(vf, write_ready) ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Write as much as possible of what there is.
+ *
+ * Move to more_wait if required. TODO
+ *
+ * If is cli->flush, then when all buffers are emptied out, clears itself and
+ * the out_active flag.
+ *
+ * Returns:
+ *
+ * utw_error -- I/O error -- see errno (utw_error = -1)
+ *
+ * utw_blocked -- write blocked -- some write operation would block
+ *
+ * utw_paused -- have written as much as line control allows in one go.
+ *
+ * NB: this does NOT mean is now in more_wait, it means that
+ * to write more it is necessary to clear the line
+ * control pause state.
+ *
+ * utw_stopped -- have done as much as can do -- no more output is possible
+ * until some external event changes things.
+ *
+ * This implies that any pending output has completed, in
+ * particular the line control iovec and the cli->cbuf are
+ * both empty.
+ *
+ * This state includes:
+ *
+ * * !out_active -- if there is something in the vf->obuf,
+ * we are not yet ready to output it.
+ *
+ * * more_wait -- waiting for user
+ *
+ * utw_done -- have written everything can find, unless more output
+ * arrives, there is no more to do.
+ *
+ * If was cli->flush the all output really has gone, as
+ * well as any incomplete line. Also the out_active and
+ * the flush flags will have been cleared.
+ *
+ * If was not cli->flush, the out_active state persists,
+ * and there may be an incomplete line still pending.
+ */
+static utw_ret_t
+uty_term_write(vio_vf vf)
+{
+ vty_cli cli = vf->cli ;
+ utw_ret_t ret ;
+ int did ;
+ ulen have, take ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ /* If the vout is neither vf_open, not vf_closing, discard all buffered
+ * output, and return all done.
+ */
+ if ((vf->vout_state != vf_open) && (vf->vout_state != vf_closing))
+ {
+ vio_fifo_clear(vf->obuf, false) ;
+ vio_fifo_clear(cli->cbuf, false) ;
+ vio_lc_clear(cli->olc) ;
+
+ cli->out_active = false ;
+ cli->flush = false ;
+
+ cli->more_wait = false ;
+ cli->more_enter = false ;
+
+ return utw_done ;
+ } ;
+
+ /* Any outstanding line control output takes precedence */
+ ret = uty_term_write_lc(cli->olc, vf, vf->obuf) ;
+ if (ret != utw_done)
+ return ret ; /* utw_blocked or utw_error */
+
+ /* Next: empty out the cli output */
+ did = vio_fifo_write_nb(cli->cbuf, vio_vfd_fd(vf->vfd), true) ;
+ if (did != 0)
+ return (did < 0) ? utw_error : utw_blocked ;
+
+ /* Next: if there is monitor output to deal with, deal with it.
+ *
+ * Note that the mon_active flag is set under VTY_LOCK(), so do not
+ * need to LOG_LOCK() to discover whether there is anything to do.
+ *
+ * But the vio->mbuf is filled under LOG_LOCK(), so need to write it
+ * under the same.
+ */
+ if (cli->mon_active)
+ {
+ LOG_LOCK() ;
+
+ did = vio_fifo_write_nb(vf->vio->mbuf, vio_vfd_fd(vf->vfd), true) ;
+
+ LOG_UNLOCK() ;
+
+ if (did != 0)
+ return (did < 0) ? utw_error : utw_blocked ;
+
+ uty_cli_post_monitor(vf->cli) ;
+ } ;
+
+ /* If not out_active, or in more_wait, then we are stopped, waiting for some
+ * external event to move things on.
+ */
+ if (cli->flush)
+ assert(cli->out_active) ; /* makes no sense, otherwise */
+
+ if (!cli->out_active || cli->more_wait)
+ return utw_stopped ;
+
+ /* Push the output fifo and any complete line fragments that may be buffered
+ * in hand in the line control -- this will stop if the line counter becomes
+ * exhausted.
+ *
+ * Note that this arranges for vio_lc_append() to be called at least once,
+ * even if the fifo is empty -- this deals with any parts of a complete
+ * line that may be held in the line control due to counter exhaustion.
+ *
+ * If the fifo is or becomes empty, then if is cli->flush, flush out any
+ * incomplete line which may be held in the line control -- in effect,
+ * cli->flush is a phantom '\n' at the end of the output fifo !
+ */
+ vio_fifo_set_hold_mark(vf->obuf) ; /* released in uty_term_write_lc() */
+
+ have = vio_fifo_get(vf->obuf) ;
+ while (1)
+ {
+ take = vio_lc_append(cli->olc, vio_fifo_get_ptr(vf->obuf), have) ;
+
+ if (take == 0)
+ break ;
+
+ have = vio_fifo_step_get(vf->obuf, take) ;
+
+ if (have == 0)
+ break ;
+ } ;
+
+ if ((have == 0) && (cli->flush))
+ vio_lc_flush(cli->olc) ;
+
+ ret = uty_term_write_lc(cli->olc, vf, vf->obuf) ;
+ if (ret != utw_done)
+ return ret ; /* utw_blocked or utw_error */
+
+ /* If arrive here, then:
+ *
+ * * no output is blocked and no errors have occurred.
+ *
+ * * the cli->cbuf is empty.
+ *
+ * * the line control iovec buffer is empty.
+ *
+ * If the fifo is not empty or there is a some part of a complete line in
+ * hand, then the line counter must be exhausted.
+ */
+ if ((have != 0) || vio_lc_have_complete_line_in_hand(cli->olc))
+ {
+ assert(vio_lc_counter_is_exhausted(cli->olc)) ;
+
+ if (cli->more_enabled)
+ {
+ uty_cli_enter_more_wait(cli) ;
+ return utw_stopped ;
+ }
+ else
+ return utw_paused ; /* artificial block */
+ } ;
+
+ /* Exciting stuff: there is nothing left to output...
+ *
+ * ...with the sole possible exception of an incomplete line buffered
+ * in the line control, which can do nothing about until there is more
+ * to be output, or the output is flushed...
+ *
+ * ...if not cli->flush, we are stopped, waiting for something else to
+ * happen.
+ */
+ assert(!cli->more_wait && !cli->more_enter) ;
+
+ if (cli->flush)
+ {
+ /* Even more exciting: is cli->flush !
+ *
+ * This means that any incomplete line must have been flushed, above.
+ * So all buffers MUST be empty.
+ */
+ assert(vio_fifo_empty(vf->obuf) && vio_lc_is_empty(cli->olc)) ;
+
+ cli->out_active = false ;
+ cli->flush = false ;
+ } ;
+
+ return utw_done ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Write contents of line control iovec buffer (if any).
+ *
+ * NB: expects that the vf is write_open
+ *
+ * NB: does nothing other than write() and buffer management.
+ *
+ * Returns: utw_blocked => blocked
+ * utw_done => all gone (may still have stuff "in hand")
+ * utw_error => failed
+ */
+static utw_ret_t
+uty_term_write_lc(vio_line_control lc, vio_vf vf, vio_fifo vff)
+{
+ int did ;
+
+ did = vio_lc_write_nb(vio_vfd_fd(vf->vfd), lc) ;
+
+ if (did > 0)
+ return utw_blocked ;
+
+ vio_fifo_clear_hold_mark(vff) ; /* finished with FIFO contents */
+
+ return (did == 0) ? utw_done : utw_error ;
+} ;
+
+/*==============================================================================
+ * VTY Listener(s) for VTY_TERMINAL
+ */
+
+/* Prototypes for listener stuff */
+
+static int uty_term_listen_addrinfo(const char *addr, unsigned short port) ;
+static int uty_term_listen_simple(const char *addr, unsigned short port) ;
+static int uty_term_listen_open(sa_family_t family, int type, int protocol,
+ struct sockaddr* sa, unsigned short port) ;
+static void uty_term_accept(int sock_listen) ;
+
+/*------------------------------------------------------------------------------
+ * Open listener(s) for VTY_TERMINAL -- using getaddrinfo() for preference.
+ */
+extern void
+uty_term_open_listeners(const char *addr, unsigned short port)
+{
+ int n ;
+
+ if (VTY_USE_ADDRINFO)
+ n = uty_term_listen_addrinfo(addr, port);
+ else
+ n = uty_term_listen_simple(addr, port);
+
+ if (n == 0)
+ zlog(NULL, LOG_ERR, "could not open any VTY_TERMINAL listeners") ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Open listener(s) for VTY_TERMINAL -- using getaddrinfo()
+ *
+ * Returns: number of listeners successfully opened.
+ */
+static int
+uty_term_listen_addrinfo(const char *addr, 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 (addr, 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_term_listen_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_term_listen_simple(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
+ zlog(NULL, LOG_ERR, "bad address %s, cannot listen for VTY", addr);
+ } ;
+
+ /* Try for AF_INET */
+ ret = uty_term_listen_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_term_listen_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)
+ zlog(NULL, LOG_ERR, "could not use address %s, to listen for VTY", addr);
+
+ /* Done */
+ return n ;
+}
+
+/*------------------------------------------------------------------------------
+ * Open a VTY_TERMINAL 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_term_listen_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 (ret >= 0)
+ ret = sockunion_listen (sock, 3);
+
+ if (ret < 0)
+ {
+ close (sock);
+ return -1 ;
+ }
+
+ /* Socket is open -- set VTY_TERMINAL listener going */
+ uty_add_listener(sock, uty_term_accept) ;
+
+ /* Return OK and signal whether used address or not */
+ return (sa != NULL) ? 1 : 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Accept action -- create and dispatch VTY_TERMINAL
+ */
+static void
+uty_term_accept(int sock_listen)
+{
+ 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 (sock_listen, &su);
+
+ if (sock_fd < 0)
+ {
+ if (sock_fd == -1)
+ zlog (NULL, LOG_WARNING, "can't accept vty socket : %s",
+ errtoa(errno, 0).str) ;
+ return ;
+ }
+
+ /* Really MUST have non-blocking */
+ ret = set_nonblocking(sock_fd) ; /* issues WARNING if fails */
+ if (ret < 0)
+ {
+ close(sock_fd) ;
+ return ;
+ } ;
+
+ /* New socket is open... worry about access lists */
+ p = sockunion2hostprefix (&su);
+ ret = 0 ; /* so far, so good */
+
+ if ((p->family == AF_INET) && host.vty_accesslist_name)
+ {
+ /* VTY's accesslist apply. */
+ struct access_list* acl ;
+
+ 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) && host.vty_ipv6_accesslist_name)
+ {
+ /* VTY's ipv6 accesslist apply. */
+ struct access_list* acl ;
+
+ if ((acl = access_list_lookup (AFI_IP6, host.vty_ipv6_accesslist_name))
+ && (access_list_apply (acl, p) == FILTER_DENY))
+ ret = -1 ;
+ }
+#endif /* HAVE_IPV6 */
+
+ prefix_free (p);
+
+ if (ret != 0)
+ {
+ zlog (NULL, LOG_INFO, "Vty connection refused from %s", sutoa(&su).str) ;
+ close (sock_fd);
+ return ;
+ } ;
+
+ /* Final options (optional) */
+ on = 1 ;
+ ret = setsockopt (sock_fd, IPPROTO_TCP, TCP_NODELAY,
+ (void*)&on, sizeof (on));
+ if (ret < 0)
+ zlog (NULL, LOG_INFO, "can't set sockopt to socket %d: %s",
+ sock_fd, errtoa(errno, 0).str) ;
+
+ /* All set -- create the VTY_TERMINAL and set it going */
+ uty_term_open(sock_fd, &su);
+
+ /* Log new VTY */
+ zlog (NULL, LOG_INFO, "Vty connection from %s (fd %d)", sutoa(&su).str,
+ sock_fd) ;
+
+ return ;
+} ;
+
+/*==============================================================================
+ * 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)
+ {
+ zlog(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 ;
+} ;
+
diff --git a/lib/vty_io_term.h b/lib/vty_io_term.h
new file mode 100644
index 00000000..d8999b98
--- /dev/null
+++ b/lib/vty_io_term.h
@@ -0,0 +1,79 @@
+/* VTY IO TERM -- Telnet Terminal I/O -- header
+ * Virtual terminal [aka TeletYpe] interface routine.
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * Revisions: Copyright (C) 2010 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * 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_VTY_IO_TERM_H
+#define _ZEBRA_VTY_IO_TERM_H
+
+#include "misc.h"
+#include <errno.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_local.h"
+#include "qstring.h"
+#include "qtimers.h"
+
+/*==============================================================================
+ * Here are structures and other definitions which are shared by:
+ *
+ * vty_io.c -- the main VTY I/O stuff
+ *
+ * for I/O to Telnet Terminal.
+ */
+
+/*==============================================================================
+ * Functions
+ */
+
+extern void uty_term_new(vty_io vio, int sock_fd) ;
+
+extern cmd_return_code_t uty_term_fetch_command_line(vio_vf vf,
+ cmd_action action, cmd_context context) ;
+extern cmd_return_code_t uty_term_out_push(vio_vf vf, bool final) ;
+extern uint uty_term_show_error_context(vio_vf vf, vio_fifo ebuf, uint depth) ;
+extern cmd_return_code_t uty_term_read_close(vio_vf vf, bool final) ;
+extern void uty_term_close_reason(vio_vf vf, const char* reason) ;
+extern cmd_return_code_t uty_term_write_close(vio_vf vf, bool final, bool base);
+
+extern int uty_term_read(vio_vf vf, keystroke steal) ;
+extern void uty_term_set_readiness(vio_vf vf, vty_readiness_t ready) ;
+
+extern qtimer_action vty_term_pause_timeout ;
+
+extern void uty_term_mon_write(vio_vf vf) ;
+
+extern bool uty_telnet_command(vio_vf, keystroke stroke, bool callback) ;
+
+
+extern void uty_term_open_listeners(const char *addr, unsigned short port) ;
+
+
+#endif /* _ZEBRA_VTY_IO_TERM_H */
diff --git a/lib/vty_io_vsh.c b/lib/vty_io_vsh.c
new file mode 100644
index 00000000..0b3cb5c4
--- /dev/null
+++ b/lib/vty_io_vsh.c
@@ -0,0 +1,369 @@
+/* VTY IO SHELL -- VTY Shell I/O
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * Revisions: Copyright (C) 2010 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * 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.
+ */
+
+#include "zebra.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
+
+#if 0
+
+/*------------------------------------------------------------------------------
+ * 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 ;
+
+ 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_nexus)
+ {
+ 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;
+
+ /* Kick start the CLI etc. */
+ uty_sock_set_readiness(&vio->sock, write_ready) ;
+
+ return vty;
+} ;
+
+
+/*------------------------------------------------------------------------------
+ * 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))
+ {
+ zlog(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)
+ {
+ zlog(NULL, LOG_ERR, "Cannot create unix stream socket: %s",
+ errtoa(errno, 0).str) ;
+ return -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);
+
+ sa_len = SUN_LEN(&sa_un) ;
+
+#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
+ sa_un.sun_len = sa_len ;
+#endif
+
+ old_mask = umask (0007);
+
+ ret = bind (sock, (struct sockaddr *) &sa_un, sa_len) ;
+ if (ret < 0)
+ zlog(NULL, LOG_ERR, "Cannot bind path %s: %s", path, errtoa(errno, 0).str);
+
+ if (ret >= 0)
+ ret = set_nonblocking(sock);
+
+ if (ret >= 0)
+ {
+ ret = listen (sock, 5);
+ if (ret < 0)
+ zlog(NULL, LOG_ERR, "listen(fd %d) failed: %s", sock,
+ errtoa(errno, 0).str) ;
+ } ;
+
+ zprivs_get_ids(&ids);
+
+ if (ids.gid_vty > 0)
+ {
+ /* set group of socket */
+ if ( chown (path, -1, ids.gid_vty) )
+ zlog (NULL, LOG_ERR, "uty_serv_vtysh: could chown socket, %s",
+ errtoa(errno, 0).str) ;
+ }
+
+ umask (old_mask);
+
+ /* 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 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * 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)
+ {
+ zlog (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 */
+ zlog (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.
+ */
+} ;
+
+/*------------------------------------------------------------------------------
+ * 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 <<<<<<<<<<<<< */
+ } ;
+ } ;
+} ;
+
+#endif
diff --git a/lib/vty_io_vsh.h b/lib/vty_io_vsh.h
new file mode 100644
index 00000000..87cd92d0
--- /dev/null
+++ b/lib/vty_io_vsh.h
@@ -0,0 +1,55 @@
+/* VTY IO SHELL -- VTY Shell I/O -- header
+ * Virtual terminal [aka TeletYpe] interface routine.
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * Revisions: Copyright (C) 2010 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * 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_VTY_IO_SHELL_H
+#define _ZEBRA_VTY_IO_SHELL_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:
+ *
+ * vty_io.c -- the main VTY I/O stuff
+ *
+ * for I/O for VTY Shell Server.
+ */
+
+/*==============================================================================
+ * Functions
+ */
+
+extern int uty_vprintf_shell(vty_io vio, const char *format, va_list args) ;
+
+#endif /* _ZEBRA_VTY_IO_SHELL_H */
diff --git a/lib/vty_local.h b/lib/vty_local.h
new file mode 100644
index 00000000..ff389baf
--- /dev/null
+++ b/lib/vty_local.h
@@ -0,0 +1,234 @@
+/* VTY top level
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * Revisions: Copyright (C) 2010 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * 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_VTY_LOCAL_H
+#define _ZEBRA_VTY_LOCAL_H
+
+#include "misc.h"
+#include "vty_common.h" /* First and foremost */
+#include "command_common.h"
+
+//#include "log_local.h" /* NB: */
+
+#include "vargs.h"
+
+#include "qpthreads.h"
+#include "qpnexus.h"
+#include "thread.h"
+#include "qpath.h"
+
+/*==============================================================================
+ * This is for access to some things in vty.c which are not required
+ * by external users, who use vty.h.
+ *
+ * This is for use within the command/vty family.
+ *
+ * Should not be used with vty.h ! (Except in vty.c itself.)
+ *
+ * This may duplicate things published in vty.h, but also includes things
+ * which are not intended for "external" use.
+ */
+
+/*==============================================================================
+ * 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
+
+/*==============================================================================
+ * Variables in vty.c -- used in any of the family
+ */
+extern struct vty_io* vio_live_list ;
+extern struct vty_io* vio_monitor_list ;
+extern struct vty_io* vio_death_watch ;
+
+extern struct vio_child* vio_childer_list ;
+
+extern struct thread_master* vty_master ;
+
+extern bool vty_nexus ;
+extern bool vty_multi_nexus ;
+
+extern qpn_nexus vty_cli_nexus ;
+extern qpn_nexus vty_cmd_nexus ;
+
+extern qpt_mutex_t vty_child_signal_mutex ;
+extern qpn_nexus vty_child_signal_nexus ;
+
+/*==============================================================================
+ * To make vty qpthread safe we use a single mutex.
+ *
+ * A recursive mutex is used, which 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.
+ *
+ * vty also interacts with logging functions. Note that where it needs to
+ * LOG_LOCK() and VTY_LOCK() it will acquire the VTY_LOCK() *first*.
+ */
+
+extern qpt_mutex_t vty_mutex ;
+
+/*------------------------------------------------------------------------------
+ * Sort out VTY_DEBUG.
+ *
+ * Set to 1 if defined, but blank.
+ * Set to QDEBUG if not defined.
+ *
+ * Force to 0 if VTY_NO_DEBUG is defined and not zero.
+ *
+ * So: defaults to same as QDEBUG, but no matter what QDEBUG is set to:
+ *
+ * * can set VTY_DEBUG == 0 to turn off debug
+ * * or set VTY_DEBUG != 0 to turn on debug
+ * * or set VTY_NO_DEBUG != 0 to force debug off
+ */
+
+#ifdef VTY_DEBUG /* If defined, make it 1 or 0 */
+# if IS_BLANK_OPTION(VTY_DEBUG)
+# undef VTY_DEBUG
+# define VTY_DEBUG 1
+# endif
+#else /* If not defined, follow QDEBUG */
+# define VTY_DEBUG QDEBUG
+#endif
+
+#ifdef VTY_NO_DEBUG /* Override, if defined */
+# if IS_NOT_ZERO_OPTION(VTY_NO_DEBUG)
+# undef VTY_DEBUG
+# define VTY_DEBUG 0
+# endif
+#endif
+
+enum { vty_debug = VTY_DEBUG } ;
+
+/*------------------------------------------------------------------------------
+ * Locking and related functions.
+ */
+extern int vty_lock_count ;
+
+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) ;
+} ;
+
+Inline bool /* true => running with more than one pthread */
+vty_is_multi_pthreaded(void)
+{
+ return !qpthreads_enabled && (vty_cli_nexus != vty_cmd_nexus) ;
+} ;
+
+/* 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.
+ *
+ * Note that both these checks will pass if !qpthreads_enabled. So can have
+ * code which is called before qpthreads are started up, or which will never
+ * run qpthreaded !
+ */
+
+extern int vty_assert_fail ;
+
+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_debug)
+ if ((vty_lock_count == 0) && (qpthreads_enabled))
+ VTY_ASSERT_FAILED() ;
+} ;
+
+Inline void
+VTY_ASSERT_CLI_THREAD(void)
+{
+ if (vty_debug)
+ if (!vty_is_cli_thread())
+ VTY_ASSERT_FAILED() ;
+} ;
+
+Inline void
+VTY_ASSERT_CLI_THREAD_LOCKED(void)
+{
+ if (vty_debug)
+ {
+ VTY_ASSERT_CLI_THREAD() ;
+ VTY_ASSERT_LOCKED() ;
+ } ;
+} ;
+
+/*==============================================================================
+ * Functions in vty.c -- used in any of the family
+ */
+extern void vty_hello (vty vty);
+
+extern int vty_out (vty vty, const char* format, ...) PRINTF_ATTRIBUTE(2, 3);
+extern int vty_write(struct vty *vty, const void* buf, int n) ;
+extern int vty_out_indent(vty vty, int indent) ;
+extern void vty_out_clear(vty vty) ;
+extern cmd_return_code_t vty_cat_file(vty vty, qpath path, const char* desc) ;
+
+extern void vty_time_print (struct vty *, int);
+
+extern void vty_set_lines(vty vty, int lines);
+
+extern void vty_open_config_write(vty vty, int fd) ;
+extern cmd_return_code_t vty_close_config_write(struct vty* vty, bool final) ;
+
+#endif /* _ZEBRA_VTY_LOCAL_H */
diff --git a/lib/vty_log.c b/lib/vty_log.c
new file mode 100644
index 00000000..080f86a3
--- /dev/null
+++ b/lib/vty_log.c
@@ -0,0 +1,258 @@
+/* VTY interface to logging
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * Revisions: Copyright (C) 2010 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * 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.
+ */
+
+#include "misc.h"
+
+#include "vty_log.h"
+#include "vty_local.h"
+#include "vty_io_term.h"
+#include "log_local.h"
+#include "list_util.h"
+#include "vio_fifo.h"
+#include "mqueue.h"
+
+/*==============================================================================
+ * This supports the "vty monitor" facility -- which reflects logging
+ * information to one or more VTY_TERMINAL vty.
+ *
+ * There are a number of issues:
+ *
+ * a) output of logging information should not be held up any longer than
+ * is absolutely necessary.
+ *
+ * b) console may be busy doing other things, so logging information needs
+ * to be buffered.
+ *
+ * c) zlog() et al, hold the LOG_LOCK(), which is at a lower level than the
+ * VTY_LOCK().
+ *
+ * d) may have one or more monitor vty, possibly at different levels of
+ * message.
+ *
+ * e) must avoid logging error messages for given vty on that vty !
+ *
+ *
+ *
+ *
+ */
+static vio_fifo monitor_buffer = NULL ;
+static uint monitor_count = 0 ;
+
+static bool mon_kicked = false ;
+static mqueue_block mon_mqb = NULL ;
+
+static void vty_mon_action(mqueue_block mqb, mqb_flag_t flag) ;
+
+/*------------------------------------------------------------------------------
+ * Initialise the vty monitor facility.
+ *
+ */
+extern void
+uty_init_monitor(void)
+{
+ LOG_LOCK() ;
+
+ vio_monitor_list = NULL ;
+ monitor_buffer = NULL ;
+
+ mon_kicked = false ;
+ mon_mqb = mqb_init_new(NULL, vty_mon_action, &mon_mqb) ;
+
+ LOG_UNLOCK() ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * 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)
+{
+ int level ;
+ int count ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ level = 0 ;
+ count = 0 ;
+
+ LOG_LOCK() ;
+
+ if (on && !vio->monitor)
+ {
+ if (vio->vty->type == VTY_TERMINAL)
+ {
+ vio->monitor = true ;
+
+ vio->maxlvl = INT_MAX ; /* pro tem TODO */
+ vio->mon_kick = false ;
+
+ if (vio->mbuf == NULL)
+ vio->mbuf = vio_fifo_new(8 * 1024) ;
+
+ sdl_push(vio_monitor_list, vio, mon_list) ;
+
+ count = +1 ;
+ } ;
+ }
+ else if (!on && vio->monitor)
+ {
+ vio->monitor = false ;
+
+ vio->maxlvl = INT_MAX ; /* pro tem TODO */
+ vio->mon_kick = false ;
+
+ sdl_del(vio_monitor_list, vio, mon_list) ;
+ count = -1 ;
+ }
+
+ if (count != 0)
+ uzlog_add_monitor(NULL, count) ;
+
+ monitor_count += count ;
+
+ LOG_UNLOCK() ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Put logging message to all suitable monitors.
+ *
+ * All we can do here is to shovel stuff into buffers and then kick the VTY
+ * to do something. If running multi-nexus, then the kick takes the form of
+ * a message sent to the cli nexus, otherwise can call the message action
+ * function here and now.
+ *
+ * NB: expect incoming line to NOT include '\n' or any other line ending.
+ */
+extern void
+vty_log(int priority, const char* line, uint len)
+{
+ vty_io vio ;
+ bool kick ;
+
+ LOG_ASSERT_LOCKED() ;
+
+ vio = vio_monitor_list ;
+ kick = false ;
+ while (vio != NULL)
+ {
+ if (priority <= vio->maxlvl)
+ {
+ vio_fifo_put_bytes(vio->mbuf, line, len) ;
+ vio_fifo_put_bytes(vio->mbuf, "\r\n", 2) ;
+
+ vio->mon_kick = kick = true ;
+ }
+ else
+ vio->mon_kick = false ;
+
+ vio = sdl_next(vio, mon_list) ;
+ } ;
+
+ if (kick)
+ {
+ if (vty_multi_nexus)
+ {
+ if (!mon_kicked)
+ {
+ mon_kicked = true ;
+ mqueue_enqueue(vty_cli_nexus->queue, mon_mqb, mqb_ordinary) ;
+ } ;
+ }
+ else
+ vty_mon_action(NULL, mqb_action) ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Put logging message to all suitable monitors.
+ *
+ * All we can do here is to shovel stuff into buffers and then boot the VTY
+ * to do something.
+ *
+ * NB: expect incoming line to NOT include '\n' or any other line ending.
+ */
+static void
+vty_mon_action(mqueue_block mqb, mqb_flag_t flag)
+{
+ VTY_LOCK() ;
+ LOG_LOCK() ; /* IN THIS ORDER !!! */
+
+ mon_kicked = false ; /* If anything else happens, need to kick again */
+
+ if (flag == mqb_action)
+ {
+ vty_io vio ;
+
+ vio = vio_monitor_list ;
+ while (vio != NULL)
+ {
+ assert(vio->vout_base->vout_type == VOUT_TERM) ;
+
+ if (vio->mon_kick)
+ {
+ vio->mon_kick = false ;
+ uty_term_mon_write(vio->vout_base) ;
+ } ;
+
+ vio = sdl_next(vio, mon_list) ;
+ } ;
+ }
+ else
+ mqb_free(mqb) ; /* Suicide */
+
+ LOG_UNLOCK() ;
+ VTY_UNLOCK() ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Async-signal-safe version of vty_log for fixed strings.
+ *
+ * This is last gasp operation.
+ */
+extern void
+vty_log_fixed (const char *buf, uint len)
+{
+ vty_io vio ;
+
+ /* Write to all known "monitor" vty
+ *
+ * Forget all the niceties -- about to die in any case.
+ */
+ vio = sdl_head(vio_monitor_list) ;
+ while (vio != NULL)
+ {
+ 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_log.h b/lib/vty_log.h
new file mode 100644
index 00000000..4a0614a6
--- /dev/null
+++ b/lib/vty_log.h
@@ -0,0 +1,41 @@
+/* VTY interface to logging -- header
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * Revisions: Copyright (C) 2010 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * 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_VTY_LOG_H
+#define _ZEBRA_VTY_LOG_H
+
+#include "misc.h"
+#include "vty_io.h"
+
+/*==============================================================================
+ */
+
+extern void uty_init_monitor(void) ;
+extern void uty_terminate_monitor(void) ;
+
+extern void uty_set_monitor(vty_io vio, bool on) ;
+
+extern void vty_log(int priority, const char* line, uint len) ;
+extern void vty_log_fixed(const char* buf, uint len) ;
+
+#endif /* _ZEBRA_VTY_LOG_H */
diff --git a/lib/vty_pipe.c b/lib/vty_pipe.c
new file mode 100644
index 00000000..ad86e30c
--- /dev/null
+++ b/lib/vty_pipe.c
@@ -0,0 +1,496 @@
+/* VTY Command Line Pipe Handling
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * Revisions: Copyright (C) 2010 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * 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.
+ */
+
+#include <zebra.h>
+
+#include "vty.h"
+#include "vty_cli.h"
+#include "vty_command.h"
+#include "vty_io.h"
+#include "vio_lines.h"
+
+#include "command_execute.h"
+#include "command_queue.h"
+
+#include "memory.h"
+
+/*==============================================================================
+ * VTY Command Line Input Pipe
+ *
+ * Here are the mechanics which support the:
+ *
+ * < file_name
+ *
+ * <| shell_command
+ *
+ * and the:
+ *
+ * > file_name
+ * >> file_name
+ * | shell_command
+ *
+ *==============================================================================
+ * The file_name handling
+ *
+ * Two directories are supported:
+ *
+ * "home" -- being the root for configuration files
+ *
+ * "cd" -- being the root for relative filenames, in the usual way
+ *
+ * "here" -- being the directory for the enclosing "< filename"
+ * see below for more detailed semantics
+ *
+ * There are the global values:
+ *
+ * config home -- defaults to directory for configuration file.
+ * may be set by command.
+ *
+ * config cd -- defaults to cwd at start up
+ *
+ * There are the local values, within a given CLI instance (ie VTY):
+ *
+ * cli home -- set to config home when CLI instance starts, or when
+ * config home is set within the CLI.
+ *
+ * cli cd -- similarly
+ *
+ * cli here -- outside < is same as cli home, unless explicitly set
+ * - set by cli here
+ * - pushed each time executes <, and set to directory for
+ * the < filename.
+ * - pushed each time executes <| and left unchanged
+ * - popped each time exits < or <|
+ * - set to parent by "no cli here" inside <, or to
+ * default state outside <
+ *
+ * And then the filename syntax:
+ *
+ * /path -- absolute path -- in the usual way
+ *
+ * path -- path wrt "cli cd" -- in the usual way
+ *
+ * ~/path -- path in "cli home" -- so "config" stuff
+ *
+ * ~./path -- path in "here" -- so wrt to enclosing < file
+ *
+ *==============================================================================
+ * The Input Pipe Commands
+ *
+ * These are "universal commands".
+ *
+ * They are:
+ *
+ * < filename -- filename as above
+ *
+ * treat contents of given file as command lines.
+ * (That may include further < commands.)
+ *
+ * See notes on cli here for pushing/popping the here
+ * directory.
+ *
+ * TODO: Filename and quotes ??
+ *
+ * <| shell_command -- the shell command is executed "as is" by system().
+ *
+ * the stdout and stderr are collected.
+ *
+ * treats stdout as command lines.
+ * (That may include further < commands.)
+ *
+ * anything from stderr is sent to the VTY output.
+ *
+ * As far as the top level CLI is concerned, these are discrete commands.
+ * That is to say:
+ *
+ * -- except where blocked while reading the "pipe", all commands are
+ * executed one after another, in one Routing Engine operation.
+ *
+ * -- in any event, all output is gathered in the VTY buffering, and will
+ * be sent to the console (or where ever) only when the outermost command
+ * completes.
+ *
+ * There are three options associated with the output from a < operation:
+ *
+ * -- suppress command line echo
+ *
+ * whether to suppress echo of commands to the VTY before they are
+ * dispatched.
+ *
+ * The default is not to suppress command line echo.
+ *
+ * -- suppress command results
+ *
+ * whether to suppress any output generated by each command.
+ *
+ * The default is not to suppress command results.
+ *
+ * -- suppress "more"
+ *
+ * whether to do "--more--", if currently applies, when finally
+ * outputting all the command results.
+ *
+ * The default is not to suppress "more".
+ *
+ * This option can only be set for the outermost < operation.
+ *
+ * Remembering that all output occurs in one go when the outermost < operation
+ * completes.
+ *
+ * The default is to show everything and implement "--more--", pretty much as
+ * if the commands had been typed in. Except that "--more--" applies to
+ * everything (including the command lines) together, rather than to the output
+ * of each command individually.
+ *
+ * These options may be changed by flags attached to the <, as follows:
+ *
+ * ! suppress command line echo
+ *
+ * ? suppress "--more--"
+ *
+ * * suppress command output
+ *
+ * TODO: cli xxx commands for echo and output suppression and notes ....
+ *
+ * TODO: Error handling....
+ *
+ * TODO: Closing the CLI....
+ *
+ * TODO: Time out and the CLI....
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ */
+#if 0
+/*==============================================================================
+ *
+ */
+
+#include "misc.h"
+
+#include "command_local.h"
+
+#include "vty_io_pipe.h"
+#include "vty_io_file.h"
+#include "vty_io_basic.h"
+
+/*==============================================================================
+ * VTY File Output
+ *
+ * This is for input and output of configuration files and piped stuff.
+ *
+ * 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.
+ */
+
+
+
+/*==============================================================================
+ * Prototypes.
+ */
+
+/*------------------------------------------------------------------------------
+ *
+ *
+ */
+extern cmd_return_code_t
+uty_file_read_open(vty_io vio, qstring name, bool reflect)
+{
+ const char* name_str ;
+ int fd ;
+ vio_vf vf ;
+ vfd_io_type_t iot ;
+
+ name_str = qs_make_string(name) ;
+
+ iot = vfd_io_read | vfd_io_blocking ; /* TODO blocking */
+
+ /* Do the basic file open. */
+ fd = uty_vfd_file_open(name_str, iot) ;
+
+ if (fd < 0)
+ {
+ uty_out(vio, "%% Could not open input file %s\n", name_str) ;
+
+ return CMD_WARNING ;
+ }
+
+ /* OK -- now push the new input onto the vin_stack. */
+ vf = uty_vf_new(vio, name_str, fd, vfd_file, iot) ;
+ uty_vin_push(vio, vf, VIN_FILE, NULL, NULL, 16 * 1024) ;
+
+ vf->parse_type = cmd_parse_strict ;
+ vf->reflect_enabled = reflect ;
+
+ return CMD_SUCCESS ;
+} ;
+
+/*------------------------------------------------------------------------------
+ *
+ *
+ */
+extern cmd_return_code_t
+uty_file_write_open(vty_io vio, qstring name, bool append)
+{
+ const char* name_str ;
+ int fd ;
+ vio_vf vf ;
+ vfd_io_type_t iot ;
+
+ iot = vfd_io_write | vfd_io_blocking ; /* TODO blocking */
+
+ if (append)
+ iot |= vfd_io_append ;
+
+ name_str = qs_make_string(name) ;
+
+ /* Do the basic file open. */
+ fd = uty_vfd_file_open(name_str, iot) ;
+
+ if (fd < 0)
+ {
+ uty_out(vio, "%% Could not open output file %s\n", name_str) ;
+
+ return CMD_WARNING ;
+ }
+
+ /* OK -- now push the new input onto the vin_stack. */
+ vf = uty_vf_new(vio, name_str, fd, vfd_file, iot) ;
+ uty_vout_push(vio, vf, VOUT_FILE, NULL, NULL, 16 * 1024) ;
+
+ return CMD_SUCCESS ;
+} ;
+
+/*==============================================================================
+ * Command line fetch from a file or pipe.
+ *
+ * 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
+ */
+extern cmd_return_code_t
+uty_file_fetch_command_line(vio_vf vf, qstring* line)
+{
+ assert(vf->vin_state == vf_open) ;
+
+ if (vf->line_complete)
+ {
+ vio_fifo_set_hold_mark(vf->ibuf) ; /* advance hold */
+
+ vf->line_complete = false ;
+ vf->line_number += vf->line_step ;
+
+ qs_set_len_nn(vf->cl, 0) ;
+ vf->line_step = 0 ;
+ } ;
+
+ while (1)
+ {
+ char* s, * p, * q, * e ;
+ size_t have ;
+ ulen len ;
+
+ s = vio_fifo_get(vf->ibuf, &have) ;
+
+ /* If nothing in hand, try and get some more.
+ *
+ * Either exits or loops back to set s & have.
+ */
+ if (have == 0)
+ {
+ int get ;
+
+ get = vio_fifo_read_nb(vf->ibuf, vio_vfd_fd(vf->vfd), 100) ;
+
+ if (get > 0)
+ continue ; /* loop back */
+
+ if (get == 0)
+ return CMD_WAITING ; /* need to set read ready ! */
+
+ if (get == -1)
+ ; /* register error */
+
+ if (get == -2)
+ return (qs_len_nn(vf->cl) > 0) ? CMD_SUCCESS : CMD_EOF ;
+ } ;
+
+ /* 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 ;
+
+ while (p < e)
+ {
+ if (*p++ < 0x20)
+ {
+ if (*(p-1) != '\n')
+ {
+ *(p-1) = ' ' ; /* everything other than '\n' */
+ continue ;
+ } ;
+
+ ++vf->line_step ; /* got a '\n' */
+
+ q = p ; /* point just past '\n' */
+ do --q ; while ((q > s) && (*(q-1) == ' ')) ;
+ /* discard trailing "spaces" */
+ break ;
+ } ;
+ } ;
+
+ /* Step past what have just consumed -- we have a hold_mark, so
+ * stuff is still in the fifo.
+ */
+ vio_fifo_step(vf->ibuf, p - s) ;
+
+ /* If not found '\n', then we have a line fragment that needs to be
+ * appended to any previous line fragments.
+ *
+ * Loops back to try to get some more form the fifo.
+ */
+ if (q == NULL)
+ {
+ qs_append_str_n(vf->cl, s, p - s) ;
+ continue ;
+ } ;
+
+ /* If we have nothing so far, set alias to point at what we have in
+ * the fifo. Otherwise, append to what we have.
+ *
+ * End up with: s = start of entire line, so far.
+ * p = end of entire line so far.
+ */
+ len = q - s ; /* length to add */
+ if (qs_len_nn(vf->cl) == 0)
+ {
+ qs_set_alias_n(vf->cl, s, len) ;
+ p = q ;
+ }
+ else
+ {
+ if (len != 0)
+ qs_append_str_n(vf->cl, s, len) ;
+
+ s = qs_char_nn(vf->cl) ;
+ p = s + qs_len_nn(vf->cl) ;
+
+ 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) == ' ')) ;
+
+ qs_set_len_nn(vf->cl, p - s) ;
+ } ;
+ } ;
+
+ /* Now worry about we have a trailing '\'. */
+
+ if ((p == s) || (*(p-1) != '\\'))
+ break ; /* no \ => no continuation => success */
+
+ /* Have a trailing '\'.
+ *
+ * If there are an odd number of '\', strip the last one and loop
+ * round to collect the continuation.
+ *
+ * If there are an even number of '\', then this is not a continuation.
+ *
+ * Note that this rule deals with the case of the continuation line
+ * being empty... e.g. ....\\\ n n -- where n is '\n'
+ */
+ q = p ;
+ do --q ; while ((q > s) && (*(q-1) == '\\')) ;
+
+ if (((p - q) & 1) == 0)
+ break ; /* even => no continuation => success */
+
+ qs_set_len_nn(vf->cl, p - s - 1) ; /* strip odd '\' */
+
+ continue ; /* loop back to fetch more */
+ } ;
+
+ /* Success have a line in hand */
+
+ vf->line_complete = true ;
+ *line = vf->cl ;
+
+ return CMD_SUCCESS ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Command output push to a file or pipe.
+ *
+ * Returns: CMD_SUCCESS -- done
+ * CMD_IO_ERROR -- ran into an I/O error
+ */
+extern cmd_return_code_t
+uty_file_out_push(vio_vf vf)
+{
+ assert(vf->vout_state == vf_open) ;
+
+ if (vio_fifo_write_nb(vf->obuf, vio_vfd_fd(vf->vfd), false) >= 0)
+ return CMD_SUCCESS ;
+ else
+ return CMD_IO_ERROR ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Tidy up after input file has been closed
+ */
+extern void
+uty_file_read_close(vio_vf vf)
+{
+ return ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Flush output buffer and close.
+ *
+ * Returns: true <=> buffer (now) empty
+ */
+extern bool
+uty_file_write_close(vio_vf vf, bool final)
+{
+ return vio_fifo_write_nb(vf->obuf, vio_vfd_fd(vf->vfd), true) == 0 ;
+} ;
+#endif
diff --git a/lib/workqueue.h b/lib/workqueue.h
index 369cd871..46f27734 100644
--- a/lib/workqueue.h
+++ b/lib/workqueue.h
@@ -24,12 +24,10 @@
#ifndef _QUAGGA_WORK_QUEUE_H
#define _QUAGGA_WORK_QUEUE_H
-#ifndef Inline
-#define Inline static inline
-#endif
+#include "misc.h"
/* Hold time for the initial schedule of a queue run, in millisec */
-enum { WORK_QUEUE_DEFAULT_HOLD = 50 } ;
+enum { WORK_QUEUE_DEFAULT_HOLD = 50 } ;
/* action value, for use by item processor and item error handlers */
typedef enum
@@ -163,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/zassert.h b/lib/zassert.h
index 8ca2203f..84e2c819 100644
--- a/lib/zassert.h
+++ b/lib/zassert.h
@@ -5,6 +5,7 @@
#ifndef _QUAGGA_ASSERT_H
#define _QUAGGA_ASSERT_H
+#include "misc.h"
#include "confirm.h"
extern void _zlog_assert_failed (const char *assertion, const char *file,
@@ -33,7 +34,7 @@ extern void _zlog_abort_err (const char *mess, int err, const char *file,
#define zassert(EX) ((void)((EX) ? 0 : \
(_zlog_assert_failed(#EX, __FILE__, __LINE__, \
- __ASSERT_FUNCTION), 0)))
+ __ASSERT_FUNCTION), 0)))
/* Implicitly *permanent* assert() -- irrespective of NDEBUG */
#undef assert
@@ -44,11 +45,14 @@ extern void _zlog_abort_err (const char *mess, int err, const char *file,
/* NDEBUG time assert() */
#ifndef NDEBUG
-#define dassert(EX) zassert(EX)
+# define dassert(EX) zassert(EX)
#else
-#define dassert(EX)
+# define dassert(EX)
#endif
+/* Assert iff QDEBUG */
+#define qassert(EX) if (qdebug) zassert(EX)
+
/* Abort with message */
#define zabort(MS) _zlog_abort_mess(MS, __FILE__, __LINE__, __ASSERT_FUNCTION)
diff --git a/lib/zconfig.h b/lib/zconfig.h
new file mode 100644
index 00000000..f1bd6f78
--- /dev/null
+++ b/lib/zconfig.h
@@ -0,0 +1,42 @@
+/* Zebra common header.
+ Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 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_CONFIG_H
+#define _ZEBRA_CONFIG_H
+
+#ifdef HAVE_CONFIG_H
+
+#include "config.h"
+
+#undef _THREAD_SAFE
+#define _THREAD_SAFE 1
+
+#undef _REENTRANT
+#define _REENTRANT 1
+
+#ifdef _FEATURES_H
+#error Features defined
+#endif
+
+#include <features.h>
+
+#endif /* HAVE_CONFIG_H */
+
+#endif /* _ZEBRA_CONFIG_H */
diff --git a/lib/zebra.h b/lib/zebra.h
index 799cfc3d..031fe85c 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
@@ -37,6 +35,7 @@ typedef unsigned char u_int8_t;
typedef int socklen_t;
#endif /* HAVE_SOCKLEN_T */
+#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
@@ -44,7 +43,6 @@ typedef int socklen_t;
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
-#include <string.h>
#include <pwd.h>
#include <grp.h>
#ifdef HAVE_STROPTS_H
diff --git a/ospf6d/ospf6_lsa.c b/ospf6d/ospf6_lsa.c
index 588b9462..9643e04d 100644
--- a/ospf6d/ospf6_lsa.c
+++ b/ospf6d/ospf6_lsa.c
@@ -14,9 +14,9 @@
* 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.
+ * 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>
@@ -573,7 +573,7 @@ ospf6_lsa_unlock (struct ospf6_lsa *lsa)
ospf6_lsa_delete (lsa);
}
-
+
/* ospf6 lsa expiry */
int
ospf6_lsa_expire (struct thread *thread)
@@ -660,7 +660,7 @@ ospf6_lsa_refresh (struct thread *thread)
return 0;
}
-
+
/* enhanced Fletcher checksum algorithm, RFC1008 7.2 */
#define MODX 4102
@@ -717,12 +717,12 @@ ospf6_lsa_terminate (void)
{
vector_free (ospf6_lsa_handler_vector);
}
-
+
static char *
ospf6_lsa_handler_name (struct ospf6_lsa_handler *h)
{
static char buf[64];
- unsigned int i;
+ unsigned int i;
unsigned int size = strlen (h->name);
if (!strcmp(h->name, "Unknown") &&
diff --git a/ripngd/ripngd.c b/ripngd/ripngd.c
index d534b2b6..6a7bbc54 100644
--- a/ripngd/ripngd.c
+++ b/ripngd/ripngd.c
@@ -233,10 +233,7 @@ ripng_recv_packet (int sock, u_char *buf, int bufsize,
struct msghdr msg;
struct iovec iov;
struct cmsghdr *cmsgptr;
- struct in6_addr dst ;
-
- dst.s6_addr[0] = 0xFF ; /* calm down compiler: also do nothing
- if does not find a 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 */
diff --git a/tests/test-list_util.c b/tests/test-list_util.c
index fc81a562..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>
@@ -20,10 +21,10 @@ static void test_ddl(void);
} while (0)
static void
-test_assert_fail(const char* true, const char* message, const char* func,
+test_assert_fail(const char* truth, const char* message, const char* func,
int line)
{
- printf("*** %s %d: (%s) not true: %s\n", func, line, true, message) ;
+ printf("*** %s %d: (%s) not true: %s\n", func, line, truth, message) ;
} ;
@@ -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-symtab.c b/tests/test-symtab.c
index ed83e607..a4b0bcf2 100644
--- a/tests/test-symtab.c
+++ b/tests/test-symtab.c
@@ -56,24 +56,25 @@ test_symbol_table_init_new(void)
assert_true(table != NULL, "table == NULL");
/* expect to not find */
- sym = symbol_lookup(table, name, 0);
+ sym = symbol_lookup(table, name, no_add);
assert_true(sym == NULL, "sym != NULL");
/* add */
- sym = symbol_lookup(table, name, 1);
+ sym = symbol_lookup(table, name, add);
symbol_set_value(sym, value);
assert_true(sym != NULL, "sym == NULL");
- assert_true(strcmp(symbol_get_name(sym), name) == 0, "strcmp(symbol_get_name(sym), name) != 0");
+ assert_true(strcmp(symbol_get_name(sym), name) == 0,
+ "strcmp(symbol_get_name(sym), name) != 0");
/* find */
- sym2 = symbol_lookup(table, name, 0);
+ sym2 = symbol_lookup(table, name, no_add);
assert_true(sym == sym2, "sym != sym2");
assert_true(symbol_get_value(sym) == value, "symbol_get_value(sym) != value");
old_value = symbol_delete(sym);
assert_true(value == old_value, "value != old_value");
- while ((old_value = symbol_table_ream(table, 1)) != NULL)
+ while ((old_value = symbol_table_ream(table, keep_it)) != NULL)
{
}
@@ -98,7 +99,7 @@ test_symbol_table_lookup(void)
for (i = 0; i < len; ++i)
{
sprintf(buf, "%d-name", i);
- sym = symbol_lookup(table, buf, 1);
+ sym = symbol_lookup(table, buf, add);
assert_true(sym != NULL, "add: sym == NULL");
assert_true(strcmp(symbol_get_name(sym), buf) == 0,
"strcmp(symbol_get_name(sym), buf) != 0");
@@ -114,7 +115,7 @@ test_symbol_table_lookup(void)
for (i = 0; i < len; ++i)
{
sprintf(buf, "%d-name", i);
- sym = symbol_lookup(table, buf, 0);
+ sym = symbol_lookup(table, buf, no_add);
assert_true(sym != NULL, "find: sym == NULL");
assert_true(strcmp(symbol_get_name(sym), buf) == 0,
"strcmp(symbol_get_name(sym), buf) != 0");
@@ -162,12 +163,12 @@ test_call_back(void)
/* add */
symbol_table_set_value_call_back(table, call_back_function_set);
- sym = symbol_lookup(table, name, 1);
+ sym = symbol_lookup(table, name, add);
symbol_set_value(sym, value);
/* change */
symbol_table_set_value_call_back(table, call_back_function_change);
- sym = symbol_lookup(table, name, 1);
+ sym = symbol_lookup(table, name, add);
symbol_set_value(sym, new_value);
/* delete */
@@ -216,7 +217,7 @@ test_ref(void)
table = symbol_table_init_new(table, NULL, 0, 0, NULL, NULL);
/* add */
- sym = symbol_lookup(table, name, 1);
+ sym = symbol_lookup(table, name, add);
symbol_set_value(sym, value);
/* create references, in reverse order so that walk in order */
@@ -272,7 +273,7 @@ test_ref_heavy(void)
table = symbol_table_init_new(table, NULL, 0, 0, NULL, NULL);
/* add */
- sym = symbol_lookup(table, name, 1);
+ sym = symbol_lookup(table, name, add);
symbol_set_value(sym, value);
/* create references, in reverse order so that walk in order */
diff --git a/tests/test-vector.c b/tests/test-vector.c
index f3b314db..7d2121cc 100644
--- a/tests/test-vector.c
+++ b/tests/test-vector.c
@@ -424,12 +424,12 @@ void
do_test_insert(const int rider)
{
vector v = NULL;
- const vector_index len = 100;
- const vector_index ins = 50;
- vector_index i;
+ const vector_length_t len = 100;
+ const vector_index_t ins = 50;
+ vector_index_t i;
char buf[10];
- vector_index check_end = len + 1;
- vector_index check_ins = ins;
+ vector_length_t check_end = len + 1;
+ vector_index_t check_ins = ins;
int check_shift = 1;
switch(rider)
@@ -563,9 +563,9 @@ test_vector_bsearch(void)
const int len = 2000;
char buf[20];
char target[20];
- vector_index target_index = 0;
+ vector_index_t target_index = 0;
int result;
- vector_index index;
+ vector_index_t index;
printf("test_vector_bsearch\n");
@@ -614,14 +614,14 @@ void
do_test_move_item_here(const int rider)
{
vector v = NULL;
- const vector_index len = 100;
- const vector_index ins = 50;
- const vector_index src = 70;
- vector_index i;
+ const vector_length_t len = 100;
+ const vector_index_t ins = 50;
+ const vector_index_t src = 70;
+ vector_index_t i;
char buf[10];
- vector_index check_dest = 0;
- vector_index check_src = 0;
- vector_index check_end = 0;
+ vector_index_t check_dest = 0;
+ vector_index_t check_src = 0;
+ vector_index_t check_end = 0;
int check_shift = 0;
p_vector_item dest_item = NULL;
@@ -711,10 +711,10 @@ void
test_vector_part_reverse(void)
{
vector v = NULL;
- const vector_index len = 100;
- const vector_index rstart = 50;
- const vector_index rstop = 70;
- vector_index i;
+ const vector_length_t len = 100;
+ const vector_index_t rstart = 50;
+ const vector_index_t rstop = 70;
+ vector_index_t i;
char buf[10];
printf("test_vector_part_reverse\n");
@@ -770,8 +770,8 @@ test_vector_copy_here(void)
{
vector v1 = NULL;
vector v2 = NULL;
- vector_index i;
- const vector_index len = 100;
+ vector_index_t i;
+ const vector_length_t len = 100;
char buf[10];
printf("test_vector_copy_here\n");
@@ -809,8 +809,8 @@ test_vector_move_here(void)
{
vector v1 = NULL;
vector v2 = NULL;
- vector_index i;
- const vector_index len = 100;
+ vector_index_t i;
+ const vector_length_t len = 100;
char buf[10];
printf("test_vector_move_here\n");
@@ -851,8 +851,8 @@ test_vector_copy_append(void)
{
vector v1 = NULL;
vector v2 = NULL;
- vector_index i;
- const vector_index len = 100;
+ vector_index_t i;
+ const vector_length_t len = 100;
char buf[10];
printf("test_vector_copy_append\n");
@@ -900,8 +900,8 @@ test_vector_move_append(void)
{
vector v1 = NULL;
vector v2 = NULL;
- vector_index i;
- const vector_index len = 100;
+ vector_index_t i;
+ const vector_length_t len = 100;
char buf[10];
printf("test_vector_move_append\n");
@@ -949,10 +949,10 @@ void
test_vector_insert(void)
{
vector v = NULL;
- vector_index i;
- const vector_index len = 100;
- const vector_index istart = 50;
- const vector_index istop = 70;
+ vector_index_t i;
+ const vector_length_t len = 100;
+ const vector_index_t istart = 50;
+ const vector_index_t istop = 70;
char buf[10];
printf("test_vector_insert\n");
@@ -1004,10 +1004,10 @@ void
test_vector_delete(void)
{
vector v = NULL;
- vector_index i;
- const vector_index len = 100;
- const vector_index dstart = 50;
- const vector_index dstop = 70;
+ vector_index_t i;
+ const vector_length_t len = 100;
+ const vector_index_t dstart = 50;
+ const vector_index_t dstop = 70;
char buf[10];
printf("test_vector_delete\n");
@@ -1060,9 +1060,9 @@ void
test_vector_discard(void)
{
vector v = NULL;
- vector_index i;
- const vector_index len = 100;
- const vector_index dstart = 50;
+ vector_index_t i;
+ const vector_length_t len = 100;
+ const vector_index_t dstart = 50;
char buf[10];
printf("test_vector_discard\n");
@@ -1110,12 +1110,12 @@ test_vector_sak(void)
vector v1 = NULL;
vector v2 = NULL;
vector v3 = NULL;
- vector_index i;
- const vector_index len = 100;
- const vector_index sstart = 60;
- const vector_index sstop = 70;
- const vector_index dstart = 40;
- const vector_index dstop = 50;
+ vector_index_t i;
+ const vector_length_t len = 100;
+ const vector_index_t sstart = 60;
+ const vector_index_t sstop = 70;
+ const vector_index_t dstart = 40;
+ const vector_index_t dstop = 50;
char buf[10];
printf("test_vector_sak\n");
@@ -1132,7 +1132,7 @@ test_vector_sak(void)
}
v1 = vector_sak(1, v1, v2, dstart, dstop - dstart,
- v3, sstart, sstop - sstart, 0);
+ v3, sstart, sstop - sstart, 0);
assert_true(v1 != NULL, "v1 == NULL");
assert_true(vector_end(v1) == (dstop - dstart),
diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c
index 64c1b549..a1f65190 100644
--- a/vtysh/vtysh.c
+++ b/vtysh/vtysh.c
@@ -30,6 +30,7 @@
#include <readline/readline.h>
#include <readline/history.h>
+#include "command_execute.h"
#include "command.h"
#include "memory.h"
#include "vtysh/vtysh.h"
@@ -37,7 +38,7 @@
#include "bgpd/bgp_vty.h"
/* Struct VTY. */
-struct vty *vty;
+static struct vty* vtysh_vty;
/* VTY shell pager name. */
char *vtysh_pager_name = NULL;
@@ -51,13 +52,13 @@ struct vtysh_client
const char *path;
} vtysh_client[] =
{
- { .fd = -1, .name = "zebra", .flag = VTYSH_ZEBRA, .path = ZEBRA_VTYSH_PATH},
- { .fd = -1, .name = "ripd", .flag = VTYSH_RIPD, .path = RIP_VTYSH_PATH},
+ { .fd = -1, .name = "zebra", .flag = VTYSH_ZEBRA, .path = ZEBRA_VTYSH_PATH},
+ { .fd = -1, .name = "ripd", .flag = VTYSH_RIPD, .path = RIP_VTYSH_PATH},
{ .fd = -1, .name = "ripngd", .flag = VTYSH_RIPNGD, .path = RIPNG_VTYSH_PATH},
- { .fd = -1, .name = "ospfd", .flag = VTYSH_OSPFD, .path = OSPF_VTYSH_PATH},
+ { .fd = -1, .name = "ospfd", .flag = VTYSH_OSPFD, .path = OSPF_VTYSH_PATH},
{ .fd = -1, .name = "ospf6d", .flag = VTYSH_OSPF6D, .path = OSPF6_VTYSH_PATH},
- { .fd = -1, .name = "bgpd", .flag = VTYSH_BGPD, .path = BGP_VTYSH_PATH},
- { .fd = -1, .name = "isisd", .flag = VTYSH_ISISD, .path = ISIS_VTYSH_PATH},
+ { .fd = -1, .name = "bgpd", .flag = VTYSH_BGPD, .path = BGP_VTYSH_PATH},
+ { .fd = -1, .name = "isisd", .flag = VTYSH_ISISD, .path = ISIS_VTYSH_PATH},
};
#define VTYSH_INDEX_MAX (sizeof(vtysh_client)/sizeof(vtysh_client[0]))
@@ -250,7 +251,7 @@ vtysh_client_execute (struct vtysh_client *vclient, const char *line, FILE *fp)
}
}
-void
+static void
vtysh_exit_ripd_only (void)
{
if (ripd_client)
@@ -277,34 +278,34 @@ 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;
int saved_ret, saved_node;
/* TODO: how well does vtysh_execute_func work ?? -- esp. qpthreads_enabled */
- vty->buf = line ;
+ vtysh_vty->buf = line ;
- saved_ret = ret = cmd_execute_command (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 ??? */
- saved_node = vty->node;
+ saved_node = vtysh_vty->node;
/* If command doesn't succeeded in current node, try to walk up in node tree.
- * Changing vty->node is enough to try it just out without actual walkup in
- * the vtysh. */
+ * Changing vtysh_vty->node is enough to try it just out without actual
+ * walkup in the vtysh. */
while (ret != CMD_SUCCESS && ret != CMD_SUCCESS_DAEMON && ret != CMD_WARNING
- && vty->node > CONFIG_NODE)
+ && vtysh_vty->node > CONFIG_NODE)
{
- vty->node = node_parent(vty->node);
- ret = cmd_execute_command (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++;
}
- vty->node = saved_node;
+ vtysh_vty->node = saved_node;
/* If command succeeded in any other node than current (tried > 0) we have
* to move into node in the vtysh where it succeeded. */
@@ -338,7 +339,7 @@ vtysh_execute_func (const char *line, int pager)
switch (ret)
{
case CMD_WARNING:
- if (vty->type == VTY_FILE)
+ if (vtysh_vty->type == VTY_FILE)
fprintf (stdout,"Warning...\n");
break;
case CMD_ERR_AMBIGUOUS:
@@ -382,16 +383,16 @@ vtysh_execute_func (const char *line, int pager)
{
line = "end";
- vty->buf = line ;
+ vtysh_vty->buf = line ;
- ret = cmd_execute_command (vty, cmd_parse_completion, &cmd);
+ ret = cmd_execute_command (vtysh_vty, cmd_parse_standard, &cmd);
if (ret != CMD_SUCCESS_DAEMON)
break;
}
else
if (cmd->func)
{
- (*cmd->func) (cmd, vty, 0, NULL);
+ (*cmd->func) (cmd, vtysh_vty, 0, NULL);
break;
}
}
@@ -410,7 +411,7 @@ vtysh_execute_func (const char *line, int pager)
break;
if (cmd->func)
- (*cmd->func) (cmd, vty, 0, NULL);
+ (*cmd->func) (cmd, vtysh_vty, 0, NULL);
}
}
if (pager && vtysh_pager_name && fp && closepager)
@@ -441,12 +442,12 @@ 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 ?? */
- while (fgets (vty->buf, VTY_BUFSIZ, fp))
+ while (fgets (vty->buf, 2000, fp))
{
/* Execute configuration command : this is strict match. */
ret = cmd_execute_command(vty, cmd_parse_strict, &cmd);
@@ -524,7 +525,7 @@ vtysh_config_from_file (struct vty *vty, FILE *fp)
}
/* We don't care about the point of the cursor when '?' is typed. */
-int
+static int
vtysh_rl_describe (void)
{
int ret;
@@ -546,7 +547,7 @@ vtysh_rl_describe (void)
if (rl_end && isspace ((int) rl_line_buffer[rl_end - 1]))
vector_set (vline, '\0');
- describe = cmd_describe_command (vline, vty->node, &ret);
+ describe = cmd_describe_command (vline, vtysh_vty->node, &ret);
fprintf (stdout,"\n");
@@ -591,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);
@@ -626,7 +627,7 @@ command_generator (const char *text, int state)
{
index = 0;
- if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
+ if (vtysh_vty->node == AUTH_NODE || vtysh_vty->node == AUTH_ENABLE_NODE)
return NULL;
vline = cmd_make_strvec (rl_line_buffer);
@@ -636,7 +637,7 @@ command_generator (const char *text, int state)
if (rl_end && isspace ((int) rl_line_buffer[rl_end - 1]))
vector_set (vline, '\0');
- matched = cmd_complete_command (vline, vty->node, &complete_status);
+ matched = cmd_complete_command (vline, vtysh_vty->node, &complete_status);
}
if (matched && matched[index])
@@ -671,7 +672,7 @@ vtysh_completion (char *text, int start, int end)
vector vline;
char **matched = NULL;
- if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
+ if (vtysh_vty->node == AUTH_NODE || vtysh_vty->node == AUTH_ENABLE_NODE)
return NULL;
vline = cmd_make_strvec (rl_line_buffer);
@@ -682,7 +683,7 @@ vtysh_completion (char *text, int start, int end)
if (rl_end && isspace ((int) rl_line_buffer[rl_end - 1]))
vector_set (vline, '\0');
- matched = cmd_complete_command (vline, vty, &ret);
+ matched = cmd_complete_command (vline, vtysh_vty, &ret);
cmd_free_strvec (vline);
@@ -791,17 +792,17 @@ static struct cmd_node keychain_key_node =
extern struct cmd_node vty_node;
/* When '^Z' is received from vty, move down to the enable mode. */
-int
+static int
vtysh_end (void)
{
- switch (vty->node)
+ switch (vtysh_vty->node)
{
case VIEW_NODE:
case ENABLE_NODE:
/* Nothing to do. */
break;
default:
- vty->node = ENABLE_NODE;
+ vtysh_vty->node = ENABLE_NODE;
break;
}
return CMD_SUCCESS;
@@ -2196,17 +2197,17 @@ vtysh_prompt (void)
hostname = names.nodename;
}
- snprintf (buf, sizeof buf, cmd_prompt (vty->node), hostname);
+ snprintf (buf, sizeof buf, cmd_prompt (vtysh_vty->node), hostname);
return buf;
}
-void
+struct vty*
vtysh_init_vty (void)
{
- /* Make vty structure. */
- vty = vty_open(VTY_SHELL);
- vty->node = VIEW_NODE;
+ /* Make vtysh_vty structure. */
+ vtysh_vty = vty_open(VTY_SHELL);
+ vtysh_vty->node = VIEW_NODE;
/* Initialize commands. */
cmd_init (0);
@@ -2437,4 +2438,5 @@ vtysh_init_vty (void)
install_element (CONFIG_NODE, &vtysh_enable_password_text_cmd);
install_element (CONFIG_NODE, &no_vtysh_enable_password_cmd);
+ return vtysh_vty ;
}
diff --git a/vtysh/vtysh.h b/vtysh/vtysh.h
index e711d593..820e5506 100644
--- a/vtysh/vtysh.h
+++ b/vtysh/vtysh.h
@@ -36,7 +36,7 @@
/* vtysh local configuration file. */
#define VTYSH_DEFAULT_CONFIG "vtysh.conf"
-void vtysh_init_vty (void);
+struct vty* vtysh_init_vty (void);
void vtysh_init_cmd (void);
extern int vtysh_connect_all (const char *optional_daemon_name);
void vtysh_readline_init (void);
@@ -64,6 +64,4 @@ void vtysh_pager_init (void);
/* Child process execution flag. */
extern int execute_flag;
-extern struct vty *vty;
-
#endif /* VTYSH_H */
diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c
index 15938f43..aed3a91c 100644
--- a/vtysh/vtysh_config.c
+++ b/vtysh/vtysh_config.c
@@ -47,19 +47,19 @@ struct config
struct list *config_top;
-int
-line_cmp (char *c1, char *c2)
+static int
+line_cmp (const char *c1, const char *c2)
{
return strcmp (c1, c2);
}
-void
+static void
line_del (char *line)
{
XFREE (MTYPE_VTYSH_CONFIG_LINE, line);
}
-struct config *
+static struct config *
config_new ()
{
struct config *config;
@@ -67,13 +67,13 @@ config_new ()
return config;
}
-int
+static int
config_cmp (struct config *c1, struct config *c2)
{
return strcmp (c1->name, c2->name);
}
-void
+static void
config_del (struct config* config)
{
list_delete (config->line);
@@ -82,7 +82,7 @@ config_del (struct config* config)
XFREE (MTYPE_VTYSH_CONFIG, config);
}
-struct config *
+static struct config *
config_get (int index, const char *line)
{
struct config *config;
@@ -121,13 +121,13 @@ config_get (int index, const char *line)
return config;
}
-void
+static void
config_add_line (struct list *config, const char *line)
{
listnode_add (config, XSTRDUP (MTYPE_VTYSH_CONFIG_LINE, line));
}
-void
+static void
config_add_line_uniq (struct list *config, const char *line)
{
struct listnode *node, *nnode;
@@ -141,7 +141,7 @@ config_add_line_uniq (struct list *config, const char *line)
listnode_add_sort (config, XSTRDUP (MTYPE_VTYSH_CONFIG_LINE, line));
}
-void
+static void
vtysh_config_parse_line (const char *line)
{
char 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
@@ -365,7 +372,7 @@ vtysh_read_file (FILE *confp)
vtysh_execute_no_pager ("end");
vtysh_execute_no_pager ("disable");
- vty_close (vty);
+ vty_close_final(vty);
if (ret != CMD_SUCCESS)
{
@@ -396,7 +403,7 @@ vtysh_read_config (char *config_default_dir)
vtysh_read_file (confp);
fclose (confp);
- host_config_set (config_default_dir);
+ cmd_host_config_set (config_default_dir);
return (0);
}
diff --git a/vtysh/vtysh_main.c b/vtysh/vtysh_main.c
index 4a315a5c..c576d3e0 100644
--- a/vtysh/vtysh_main.c
+++ b/vtysh/vtysh_main.c
@@ -63,7 +63,7 @@ struct thread_master *master;
FILE *logfile;
/* SIGTSTP handler. This function care user's ^Z input. */
-void
+static void
sigtstp (int sig)
{
/* Execute "end" command. */
@@ -84,7 +84,7 @@ sigtstp (int sig)
}
/* SIGINT handler. This function care user's ^Z input. */
-void
+static void
sigint (int sig)
{
/* Check this process is not child process. */
@@ -98,7 +98,7 @@ sigint (int sig)
/* Signale wrapper for vtysh. We don't use sigevent because
* vtysh doesn't use threads. TODO */
-RETSIGTYPE *
+static RETSIGTYPE *
vtysh_signal_set (int signo, void (*func)(int))
{
int ret;
@@ -121,7 +121,7 @@ vtysh_signal_set (int signo, void (*func)(int))
}
/* Initialization of signal handles. */
-void
+static void
vtysh_signal_init ()
{
vtysh_signal_set (SIGINT, sigint);
@@ -168,7 +168,7 @@ struct option longopts[] =
};
/* Read a string, and return a pointer to it. Returns NULL on EOF. */
-char *
+static char *
vtysh_rl_gets ()
{
HIST_ENTRY *last;
@@ -202,7 +202,7 @@ static void log_it(const char *line)
{
time_t t = time(NULL);
struct tm *tmp = localtime(&t);
- char *user = getenv("USER") ? : "boot";
+ const char *user = getenv("USER") ? : "boot";
char tod[64];
strftime(tod, sizeof tod, "%Y%m%d-%H:%M.%S", tmp);
@@ -214,6 +214,7 @@ static void log_it(const char *line)
int
main (int argc, char **argv, char **env)
{
+ struct vty* vty ;
char *p;
int opt;
int dryrun = 0;
@@ -292,7 +293,7 @@ main (int argc, char **argv, char **env)
vtysh_signal_init ();
/* Make vty structure and register commands. */
- vtysh_init_vty ();
+ vty = vtysh_init_vty ();
vtysh_init_cmd ();
vtysh_user_init ();
vtysh_config_init ();
diff --git a/vtysh/vtysh_user.c b/vtysh/vtysh_user.c
index 58676c10..5dd50bb7 100644
--- a/vtysh/vtysh_user.c
+++ b/vtysh/vtysh_user.c
@@ -21,6 +21,7 @@
#include <zebra.h>
#include <lib/version.h>
+#include <vtysh_user.h>
#include <pwd.h>
@@ -98,19 +99,21 @@ struct vtysh_user
struct list *userlist;
-struct vtysh_user *
+static struct vtysh_user *
user_new ()
{
return XCALLOC (0, sizeof (struct vtysh_user));
}
-void
+static void user_free (struct vtysh_user *user) __attribute__((unused)) ;
+
+static void
user_free (struct vtysh_user *user)
{
XFREE (0, user);
}
-struct vtysh_user *
+static struct vtysh_user *
user_lookup (const char *name)
{
struct listnode *node, *nnode;
@@ -124,8 +127,10 @@ user_lookup (const char *name)
return NULL;
}
-void
-user_config_write ()
+static void user_config_write (void) __attribute__((unused)) ;
+
+static void
+user_config_write (void)
{
struct listnode *node, *nnode;
struct vtysh_user *user;
@@ -137,7 +142,7 @@ user_config_write ()
}
}
-struct vtysh_user *
+static struct vtysh_user *
user_get (const char *name)
{
struct vtysh_user *user;
@@ -165,8 +170,8 @@ DEFUN (username_nopassword,
return CMD_SUCCESS;
}
-int
-vtysh_auth ()
+extern int
+vtysh_auth(void)
{
struct vtysh_user *user;
struct passwd *passwd;
@@ -186,8 +191,8 @@ vtysh_auth ()
return 0;
}
-void
-vtysh_user_init ()
+extern void
+vtysh_user_init(void)
{
userlist = list_new ();
install_element (CONFIG_NODE, &username_nopassword_cmd);
diff --git a/vtysh/vtysh_user.h b/vtysh/vtysh_user.h
index 8d0a4cf6..c02cfede 100644
--- a/vtysh/vtysh_user.h
+++ b/vtysh/vtysh_user.h
@@ -22,6 +22,7 @@
#ifndef _VTYSH_USER_H
#define _VTYSH_USER_H
-int vtysh_auth ();
+extern int vtysh_auth(void) ;
+extern void vtysh_user_init(void) ;
#endif /* _VTYSH_USER_H */