build, vtysh: extract vtysh commands from .xref

Rather than running selected source files through the preprocessor and a
bunch of perl regex'ing to get the list of all DEFUNs, use the data
collected in frr.xref.

This not only eliminates issues we've been having with preprocessor
failures due to nonexistent header files, but is also much faster.
Where extract.pl would take 5s, this now finishes in 0.2s.  And since
this is a non-parallelizable build step towards the end of the build
(dependent on a lot of other things being done already), the speedup is
actually noticeable.

Also files containing CLI no longer need to be listed in `vtysh_scan`
since the .xref data covers everything.  `#ifndef VTYSH_EXTRACT_PL`
checks are equally obsolete.

Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
This commit is contained in:
David Lamparter 2022-10-04 18:44:36 +02:00
parent 695f387ed8
commit 89cb86aeb0
93 changed files with 423 additions and 692 deletions

View File

@ -144,7 +144,6 @@ pkginclude_HEADERS =
nodist_pkginclude_HEADERS =
dist_yangmodels_DATA =
man_MANS =
vtysh_scan =
vtysh_daemons =
clippy_scan =
@ -226,6 +225,7 @@ EXTRA_DIST += \
python/makefile.py \
python/tiabwarfo.py \
python/xrelfo.py \
python/xref2vtysh.py \
python/test_xrelfo.py \
python/runtests.py \
\

View File

@ -4,11 +4,6 @@
if BABELD
sbin_PROGRAMS += babeld/babeld
vtysh_scan += \
babeld/babel_interface.c \
babeld/babel_zebra.c \
babeld/babeld.c \
# end
vtysh_daemons += babeld
endif

View File

@ -26,9 +26,7 @@
#include "lib/log.h"
#include "lib/northbound_cli.h"
#ifndef VTYSH_EXTRACT_PL
#include "bfdd/bfdd_cli_clippy.c"
#endif /* VTYSH_EXTRACT_PL */
#include "bfd.h"
#include "bfdd_nb.h"

View File

@ -28,9 +28,7 @@
#include "bfd.h"
#ifndef VTYSH_EXTRACT_PL
#include "bfdd/bfdd_vty_clippy.c"
#endif
/*
* Commands help string definitions.

View File

@ -5,8 +5,6 @@
if BFDD
noinst_LIBRARIES += bfdd/libbfd.a
sbin_PROGRAMS += bfdd/bfdd
vtysh_scan += bfdd/bfdd_vty.c
vtysh_scan += bfdd/bfdd_cli.c
vtysh_daemons += bfdd
man8 += $(MANBUILD)/frr-bfdd.8
endif

View File

@ -2033,9 +2033,7 @@ static const struct cmd_variable_handler bmp_targets_var_handlers[] = {
#define BMP_STR "BGP Monitoring Protocol\n"
#ifndef VTYSH_EXTRACT_PL
#include "bgpd/bgp_bmp_clippy.c"
#endif
DEFPY_NOSH(bmp_targets_main,
bmp_targets_cmd,

View File

@ -1410,9 +1410,7 @@ DEFUN (no_debug_bgp_update_direct_peer,
return CMD_SUCCESS;
}
#ifndef VTYSH_EXTRACT_PL
#include "bgpd/bgp_debug_clippy.c"
#endif
DEFPY (debug_bgp_update_prefix_afi_safi,
debug_bgp_update_prefix_afi_safi_cmd,

View File

@ -3336,9 +3336,7 @@ static void write_vni_config(struct vty *vty, struct bgpevpn *vpn)
}
}
#ifndef VTYSH_EXTRACT_PL
#include "bgpd/bgp_evpn_vty_clippy.c"
#endif
DEFPY(bgp_evpn_flood_control,
bgp_evpn_flood_control_cmd,

View File

@ -39,9 +39,7 @@
#define BGP_LABELPOOL_ENABLE_TESTS 0
#ifndef VTYSH_EXTRACT_PL
#include "bgpd/bgp_labelpool_clippy.c"
#endif
/*

View File

@ -90,9 +90,7 @@
#include "bgpd/bgp_flowspec_util.h"
#include "bgpd/bgp_pbr.h"
#ifndef VTYSH_EXTRACT_PL
#include "bgpd/bgp_route_clippy.c"
#endif
DEFINE_HOOK(bgp_snmp_update_stats,
(struct bgp_node *rn, struct bgp_path_info *pi, bool added),

View File

@ -74,9 +74,7 @@
#include "bgpd/rfapi/bgp_rfapi_cfg.h"
#endif
#ifndef VTYSH_EXTRACT_PL
#include "bgpd/bgp_routemap_clippy.c"
#endif
/* Memo of route-map commands.

View File

@ -52,16 +52,12 @@
#include "lib/network.h"
#include "lib/thread.h"
#ifndef VTYSH_EXTRACT_PL
#include "rtrlib/rtrlib.h"
#endif
#include "hook.h"
#include "libfrr.h"
#include "lib/version.h"
#ifndef VTYSH_EXTRACT_PL
#include "bgpd/bgp_rpki_clippy.c"
#endif
DEFINE_MTYPE_STATIC(BGPD, BGP_RPKI_CACHE, "BGP RPKI Cache server");
DEFINE_MTYPE_STATIC(BGPD, BGP_RPKI_CACHE_GROUP, "BGP RPKI Cache server group");

View File

@ -1316,9 +1316,7 @@ void bgp_clear_soft_in(struct bgp *bgp, afi_t afi, safi_t safi)
bgp_clear(NULL, bgp, afi, safi, clear_all, BGP_CLEAR_SOFT_IN, NULL);
}
#ifndef VTYSH_EXTRACT_PL
#include "bgpd/bgp_vty_clippy.c"
#endif
DEFUN_HIDDEN (bgp_local_mac,
bgp_local_mac_cmd,

View File

@ -6,36 +6,9 @@ if BGPD
noinst_LIBRARIES += bgpd/libbgp.a
sbin_PROGRAMS += bgpd/bgpd
noinst_PROGRAMS += bgpd/bgp_btoa
vtysh_scan += \
bgpd/bgp_bfd.c \
bgpd/bgp_debug.c \
bgpd/bgp_dump.c \
bgpd/bgp_evpn_mh.c \
bgpd/bgp_evpn_vty.c \
bgpd/bgp_filter.c \
bgpd/bgp_labelpool.c \
bgpd/bgp_mplsvpn.c \
bgpd/bgp_nexthop.c \
bgpd/bgp_route.c \
bgpd/bgp_routemap.c \
bgpd/bgp_vty.c \
bgpd/bgp_flowspec_vty.c \
# end
# can be loaded as DSO - always include for vtysh
vtysh_scan += bgpd/bgp_rpki.c
vtysh_scan += bgpd/bgp_bmp.c
vtysh_daemons += bgpd
if ENABLE_BGP_VNC
vtysh_scan += \
bgpd/rfapi/bgp_rfapi_cfg.c \
bgpd/rfapi/rfapi.c \
bgpd/rfapi/rfapi_vty.c \
bgpd/rfapi/vnc_debug.c \
# end
endif
if SNMP
module_LTLIBRARIES += bgpd/bgpd_snmp.la
endif

View File

@ -2747,7 +2747,6 @@ AC_CONFIG_FILES([
pkgsrc/ripd.sh pkgsrc/ripngd.sh pkgsrc/zebra.sh
pkgsrc/eigrpd.sh])
AC_CONFIG_FILES([vtysh/extract.pl], [chmod +x vtysh/extract.pl])
AC_CONFIG_FILES([tools/frr], [chmod +x tools/frr])
AC_CONFIG_FILES([tools/watchfrr.sh], [chmod +x tools/watchfrr.sh])
AC_CONFIG_FILES([tools/frrinit.sh], [chmod +x tools/frrinit.sh])

View File

@ -453,9 +453,7 @@ all DEFPY statements**:
/* GPL header */
#include ...
...
#ifndef VTYSH_EXTRACT_PL
#include "daemon/filename_clippy.c"
#endif
DEFPY(...)
DEFPY(...)

View File

@ -43,9 +43,14 @@ simplifying the output. This is discussed in :ref:`vtysh-configuration`.
Command Extraction
------------------
When VTYSH is built, a Perl script named :file:`extract.pl` searches the FRR
codebase looking for ``DEFUN``'s. It extracts these ``DEFUN``'s, transforms
them into ``DEFSH``'s and appends them to ``vtysh_cmd.c``. Each ``DEFSH``
To build ``vtysh``, the :file:`python/xref2vtysh.py` script scans through the
:file:`frr.xref` file created earlier in the build process. This file contains
a list of all ``DEFUN`` and ``install_element`` sites in the code, generated
directly from the binaries (and therefore matching exactly what is really
available.)
This list is collated and transformed into ``DEFSH`` (and ``install_element``)
statements, output to ``vtysh_cmd.c``. Each ``DEFSH``
contains the name of the command plus ``_vtysh``, as well as a flag that
indicates which daemons the command was found in. When the command is executed
in VTYSH, this flag is inspected to determine which daemons to send the command
@ -55,6 +60,12 @@ avoiding spurious errors from daemons that don't have the command defined.
The extraction script contains lots of hardcoded knowledge about what sources
to look at and what flags to use for certain commands.
.. note::
The ``vtysh_scan`` Makefile variable and ``#ifndef VTYSH_EXTRACT_PL``
checks in source files are no longer used. Remove them when rebasing older
changes.
.. _vtysh-special-defuns:
Special DEFUNs
@ -69,7 +80,7 @@ several VTYSH-specific ``DEFUN`` variants that each serve different purposes.
simply forwarded to the daemons indicated in the daemon flag.
``DEFUN_NOSH``
Used by daemons. Has the same expansion as a ``DEFUN``, but ``extract.pl``
Used by daemons. Has the same expansion as a ``DEFUN``, but ``xref2vtysh.py``
will skip these definitions when extracting commands. This is typically used
when VTYSH must take some special action upon receiving the command, and the
programmer therefore needs to write VTYSH's copy of the command manually

View File

@ -31,9 +31,7 @@
#include "eigrp_zebra.h"
#include "eigrp_cli.h"
#ifndef VTYSH_EXTRACT_PL
#include "eigrpd/eigrp_cli_clippy.c"
#endif /* VTYSH_EXTRACT_PL */
/*
* XPath: /frr-eigrpd:eigrpd/instance

View File

@ -55,9 +55,7 @@
#include "eigrpd/eigrp_dump.h"
#include "eigrpd/eigrp_const.h"
#ifndef VTYSH_EXTRACT_PL
#include "eigrpd/eigrp_vty_clippy.c"
#endif
static void eigrp_vty_display_prefix_entry(struct vty *vty, struct eigrp *eigrp,
struct eigrp_prefix_descriptor *pe,

View File

@ -4,12 +4,6 @@
if EIGRPD
sbin_PROGRAMS += eigrpd/eigrpd
vtysh_scan += \
eigrpd/eigrp_cli.c \
eigrpd/eigrp_dump.c \
eigrpd/eigrp_vty.c \
# end
# eigrpd/eigrp_routemap.c
vtysh_daemons += eigrpd
man8 += $(MANBUILD)/frr-eigrpd.8
endif

View File

@ -37,9 +37,7 @@
#include "isisd/isis_circuit.h"
#include "isisd/isis_csm.h"
#ifndef VTYSH_EXTRACT_PL
#include "isisd/isis_cli_clippy.c"
#endif
#ifndef FABRICD

View File

@ -5,16 +5,6 @@
if ISISD
noinst_LIBRARIES += isisd/libisis.a
sbin_PROGRAMS += isisd/isisd
vtysh_scan += \
isisd/isis_cli.c \
isisd/isis_ldp_sync.c \
isisd/isis_redist.c \
isisd/isis_spf.c \
isisd/isis_te.c \
isisd/isis_sr.c \
isisd/isis_vty_fabricd.c \
isisd/isisd.c \
# end
vtysh_daemons += isisd
if SNMP
module_LTLIBRARIES += isisd/isisd_snmp.la
@ -25,18 +15,6 @@ endif
if FABRICD
noinst_LIBRARIES += isisd/libfabric.a
sbin_PROGRAMS += isisd/fabricd
if !ISISD
vtysh_scan += \
isisd/isis_cli.c \
isisd/isis_ldp_sync.c \
isisd/isis_redist.c \
isisd/isis_spf.c \
isisd/isis_te.c \
isisd/isis_sr.c \
isisd/isis_vty_fabricd.c \
isisd/isisd.c \
# end
endif
vtysh_daemons += fabricd
endif

View File

@ -25,9 +25,7 @@
#include "ldpd/ldpd.h"
#include "ldpd/ldp_vty.h"
#ifndef VTYSH_EXTRACT_PL
#include "ldpd/ldp_vty_cmds_clippy.c"
#endif
DEFPY_NOSH(ldp_mpls_ldp,
ldp_mpls_ldp_cmd,

View File

@ -5,7 +5,6 @@
if LDPD
noinst_LIBRARIES += ldpd/libldp.a
sbin_PROGRAMS += ldpd/ldpd
vtysh_scan += ldpd/ldp_vty_cmds.c
vtysh_daemons += ldpd
man8 += $(MANBUILD)/frr-ldpd.8
endif

View File

@ -251,9 +251,6 @@ struct cmd_node {
/* Argc max counts. */
#define CMD_ARGC_MAX 256
/* Turn off these macros when using 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) \
static const struct cmd_element cmdname = { \
@ -370,8 +367,6 @@ struct cmd_node {
#define ALIAS_YANG(funcname, cmdname, cmdstr, helpstr) \
ALIAS_ATTR(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_YANG)
#endif /* VTYSH_EXTRACT_PL */
/* Some macroes */
/*
@ -511,7 +506,6 @@ struct xref_install_element {
enum node_type node_type;
};
#ifndef VTYSH_EXTRACT_PL
#define install_element(node_type_, cmd_element_) do { \
static const struct xref_install_element _xref \
__attribute__((used)) = { \
@ -523,7 +517,6 @@ struct xref_install_element {
XREF_LINK(_xref.xref); \
_install_element(node_type_, cmd_element_); \
} while (0)
#endif
extern void _install_element(enum node_type, const struct cmd_element *);

View File

@ -31,9 +31,7 @@
#include "lib/plist_int.h"
#include "lib/printfrr.h"
#ifndef VTYSH_EXTRACT_PL
#include "lib/filter_cli_clippy.c"
#endif /* VTYSH_EXTRACT_PL */
#define ACCESS_LIST_STR "Access list entry\n"
#define ACCESS_LIST_ZEBRA_STR "Access list name\n"

View File

@ -35,9 +35,7 @@
#include "buffer.h"
#include "log.h"
#include "northbound_cli.h"
#ifndef VTYSH_EXTRACT_PL
#include "lib/if_clippy.c"
#endif
DEFINE_MTYPE_STATIC(LIB, IF, "Interface");
DEFINE_MTYPE_STATIC(LIB, CONNECTED, "Connected");

View File

@ -29,9 +29,7 @@
#include "lib/printfrr.h"
#include "lib/systemd.h"
#ifndef VTYSH_EXTRACT_PL
#include "lib/log_vty_clippy.c"
#endif
#define ZLOG_MAXLVL(a, b) MAX(a, b)

View File

@ -28,9 +28,7 @@
#include <command.h>
#include <jhash.h>
#ifndef VTYSH_EXTRACT_PL
#include "lib/nexthop_group_clippy.c"
#endif
DEFINE_MTYPE_STATIC(LIB, NEXTHOP_GROUP, "Nexthop Group");

View File

@ -32,9 +32,7 @@
#include "northbound.h"
#include "northbound_cli.h"
#include "northbound_db.h"
#ifndef VTYSH_EXTRACT_PL
#include "lib/northbound_cli_clippy.c"
#endif
struct debug nb_dbg_cbs_config = {0, "Northbound callbacks: configuration"};
struct debug nb_dbg_cbs_state = {0, "Northbound callbacks: state"};

View File

@ -1193,9 +1193,7 @@ static int vty_clear_prefix_list(struct vty *vty, afi_t afi, const char *name,
return CMD_SUCCESS;
}
#ifndef VTYSH_EXTRACT_PL
#include "lib/plist_clippy.c"
#endif
DEFPY (show_ip_prefix_list,
show_ip_prefix_list_cmd,

View File

@ -26,9 +26,7 @@
#include "lib/northbound_cli.h"
#include "lib/routemap.h"
#ifndef VTYSH_EXTRACT_PL
#include "lib/routemap_cli_clippy.c"
#endif /* VTYSH_EXTRACT_PL */
#define ROUTE_MAP_CMD_STR \
"Create route-map or enter route-map command mode\n" \

View File

@ -139,27 +139,6 @@ nodist_lib_libfrr_la_SOURCES = \
yang/frr-module-translator.yang.c \
# end
vtysh_scan += \
lib/distribute.c \
lib/filter.c \
lib/filter_cli.c \
lib/if.c \
lib/if_rmap.c \
lib/keychain.c \
lib/lib_vty.c \
lib/log_vty.c \
lib/nexthop_group.c \
lib/plist.c \
lib/routemap.c \
lib/routemap_cli.c \
lib/spf_backoff.c \
lib/thread.c \
lib/vrf.c \
lib/vty.c \
# end
# can be loaded as DSO - always include for vtysh
vtysh_scan += lib/agentx.c
if SQLITE3
lib_libfrr_la_LIBADD += $(SQLITE3_LIBS)
lib_libfrr_la_SOURCES += lib/db.c
@ -347,7 +326,6 @@ lib_libfrrsnmp_la_SOURCES = \
if CARES
lib_LTLIBRARIES += lib/libfrrcares.la
pkginclude_HEADERS += lib/resolver.h
vtysh_scan += lib/resolver.c
endif
lib_libfrrcares_la_CFLAGS = $(AM_CFLAGS) $(CARES_CFLAGS)
@ -478,13 +456,18 @@ SUFFIXES += .xref
# dependencies added in python/makefile.py
frr.xref:
$(AM_V_XRELFO) $(CLIPPY) $(top_srcdir)/python/xrelfo.py -o $@ $^
$(AM_V_XRELFO) $(CLIPPY) $(top_srcdir)/python/xrelfo.py -o $@ -c vtysh/vtysh_cmd.c $^
all-am: frr.xref
clean-xref:
-rm -rf $(xrefs) frr.xref
clean-local: clean-xref
CLEANFILES += vtysh/vtysh_cmd.c
vtysh/vtysh_cmd.c: frr.xref
@test -f $@ || rm -f frr.xref || true
@test -f $@ || $(MAKE) $(AM_MAKEFLAGS) frr.xref
## automake's "ylwrap" is a great piece of GNU software... not.
.l.c:
$(AM_V_LEX)$(am__skiplex) $(LEXCOMPILE) $<

View File

@ -102,9 +102,7 @@ unsigned long cputime_threshold = CONSUMED_TIME_CHECK;
unsigned long walltime_threshold = CONSUMED_TIME_CHECK;
/* CLI start ---------------------------------------------------------------- */
#ifndef VTYSH_EXTRACT_PL
#include "lib/thread_clippy.c"
#endif
static unsigned int cpu_record_hash_key(const struct cpu_thread_history *a)
{

View File

@ -53,9 +53,7 @@
#include <arpa/telnet.h>
#include <termios.h>
#ifndef VTYSH_EXTRACT_PL
#include "lib/vty_clippy.c"
#endif
DEFINE_MTYPE_STATIC(LIB, VTY, "VTY");
DEFINE_MTYPE_STATIC(LIB, VTY_SERV, "VTY server");

View File

@ -202,9 +202,9 @@
#endif /* HAVE_GLIBC_BACKTRACE */
/* Local includes: */
#if !(defined(__GNUC__) || defined(VTYSH_EXTRACT_PL))
#if !defined(__GNUC__)
#define __attribute__(x)
#endif /* !__GNUC__ || VTYSH_EXTRACT_PL */
#endif /* !__GNUC__ */
#include <assert.h>

View File

@ -158,9 +158,7 @@ static int reconf_clear_dst(struct zlog_cfg_5424_user *cfg, struct vty *vty)
return reconf_dst(cfg, vty);
}
#ifndef VTYSH_EXTRACT_PL
#include "lib/zlog_5424_cli_clippy.c"
#endif
DEFPY_NOSH(log_5424_target,
log_5424_target_cmd,

View File

@ -4,7 +4,6 @@
if NHRPD
sbin_PROGRAMS += nhrpd/nhrpd
vtysh_scan += nhrpd/nhrp_vty.c
vtysh_daemons += nhrpd
man8 += $(MANBUILD)/frr-nhrpd.8
endif

View File

@ -47,9 +47,7 @@
#include "ospf6d.h"
#include "lib/json.h"
#include "ospf6_nssa.h"
#ifndef VTYSH_EXTRACT_PL
#include "ospf6d/ospf6_area_clippy.c"
#endif
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_AREA, "OSPF6 area");
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_PLISTNAME, "Prefix list name");

View File

@ -65,9 +65,7 @@ static void ospf6_asbr_redistribute_set(struct ospf6 *ospf6, int type);
static void ospf6_asbr_redistribute_unset(struct ospf6 *ospf6,
struct ospf6_redist *red, int type);
#ifndef VTYSH_EXTRACT_PL
#include "ospf6d/ospf6_asbr_clippy.c"
#endif
unsigned char conf_debug_ospf6_asbr = 0;

View File

@ -42,9 +42,7 @@
#include "ospf6d/ospf6_intra.h"
#include "ospf6d/ospf6_spf.h"
#include "ospf6d/ospf6_gr.h"
#ifndef VTYSH_EXTRACT_PL
#include "ospf6d/ospf6_gr_clippy.c"
#endif
static void ospf6_gr_nvm_delete(struct ospf6 *ospf6);

View File

@ -49,9 +49,7 @@
#include "ospf6d.h"
#include "ospf6_gr.h"
#include "lib/json.h"
#ifndef VTYSH_EXTRACT_PL
#include "ospf6d/ospf6_gr_helper_clippy.c"
#endif
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_GR_HELPER, "OSPF6 Graceful restart helper");

View File

@ -46,9 +46,7 @@
#include "ospf6_flood.h"
#include "ospf6d.h"
#ifndef VTYSH_EXTRACT_PL
#include "ospf6d/ospf6_lsa_clippy.c"
#endif
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_LSA, "OSPF6 LSA");
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_LSA_HEADER, "OSPF6 LSA header");

View File

@ -49,9 +49,7 @@
#include "ospf6_asbr.h"
#include "ospf6d.h"
#include "ospf6_nssa.h"
#ifndef VTYSH_EXTRACT_PL
#include "ospf6d/ospf6_nssa_clippy.c"
#endif
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_LSA, "OSPF6 LSA");
unsigned char config_debug_ospf6_nssa = 0;

View File

@ -37,9 +37,7 @@
#include "ospf6_interface.h"
#include "ospf6d.h"
#include "ospf6_zebra.h"
#ifndef VTYSH_EXTRACT_PL
#include "ospf6d/ospf6_route_clippy.c"
#endif
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_ROUTE, "OSPF6 route");
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_ROUTE_TABLE, "OSPF6 route table");

View File

@ -65,9 +65,7 @@ FRR_CFG_DEFAULT_BOOL(OSPF6_LOG_ADJACENCY_CHANGES,
{ .val_bool = false },
);
#ifndef VTYSH_EXTRACT_PL
#include "ospf6d/ospf6_top_clippy.c"
#endif
/* global ospf6d variable */
static struct ospf6_master ospf6_master;

View File

@ -5,27 +5,6 @@
if OSPF6D
noinst_LIBRARIES += ospf6d/libospf6.a
sbin_PROGRAMS += ospf6d/ospf6d
vtysh_scan += \
ospf6d/ospf6_nssa.c \
ospf6d/ospf6_abr.c \
ospf6d/ospf6_asbr.c \
ospf6d/ospf6_area.c \
ospf6d/ospf6_bfd.c \
ospf6d/ospf6_flood.c \
ospf6d/ospf6_gr.c \
ospf6d/ospf6_gr_helper.c \
ospf6d/ospf6_interface.c \
ospf6d/ospf6_intra.c \
ospf6d/ospf6_lsa.c \
ospf6d/ospf6_message.c \
ospf6d/ospf6_neighbor.c \
ospf6d/ospf6_route.c \
ospf6d/ospf6_spf.c \
ospf6d/ospf6_top.c \
ospf6d/ospf6_zebra.c \
ospf6d/ospf6d.c \
ospf6d/ospf6_auth_trailer.c \
# end
vtysh_daemons += ospf6d
if SNMP
module_LTLIBRARIES += ospf6d/ospf6d_snmp.la

View File

@ -42,9 +42,7 @@
#include "ospfd/ospf_dump.h"
#include "ospfd/ospf_packet.h"
#include "ospfd/ospf_network.h"
#ifndef VTYSH_EXTRACT_PL
#include "ospfd/ospf_dump_clippy.c"
#endif
/* Configuration debug option variables. */
unsigned long conf_debug_ospf_packet[5] = {0, 0, 0, 0, 0};

View File

@ -44,9 +44,7 @@
#include "ospfd/ospf_gr.h"
#include "ospfd/ospf_errors.h"
#include "ospfd/ospf_dump.h"
#ifndef VTYSH_EXTRACT_PL
#include "ospfd/ospf_gr_clippy.c"
#endif
static void ospf_gr_nvm_delete(struct ospf *ospf);

View File

@ -751,9 +751,7 @@ void ospf_ldp_sync_if_write_config(struct vty *vty,
/*
* LDP-SYNC commands.
*/
#ifndef VTYSH_EXTRACT_PL
#include "ospfd/ospf_ldp_sync_clippy.c"
#endif
DEFPY (ospf_mpls_ldp_sync,
ospf_mpls_ldp_sync_cmd,

View File

@ -185,9 +185,7 @@ static void ospf_show_vrf_name(struct ospf *ospf, struct vty *vty,
}
}
#ifndef VTYSH_EXTRACT_PL
#include "ospfd/ospf_vty_clippy.c"
#endif
DEFUN_NOSH (router_ospf,
router_ospf_cmd,

View File

@ -5,18 +5,6 @@
if OSPFD
noinst_LIBRARIES += ospfd/libfrrospf.a
sbin_PROGRAMS += ospfd/ospfd
vtysh_scan += \
ospfd/ospf_bfd.c \
ospfd/ospf_dump.c \
ospfd/ospf_gr.c \
ospfd/ospf_ldp_sync.c \
ospfd/ospf_opaque.c \
ospfd/ospf_ri.c \
ospfd/ospf_routemap.c \
ospfd/ospf_te.c \
ospfd/ospf_sr.c \
ospfd/ospf_vty.c \
# end
vtysh_daemons += ospfd
if SNMP
module_LTLIBRARIES += ospfd/ospfd_snmp.la

View File

@ -31,9 +31,7 @@
#include "pathd/pathd.h"
#include "pathd/path_nb.h"
#ifndef VTYSH_EXTRACT_PL
#include "pathd/path_cli_clippy.c"
#endif
#include "pathd/path_ted.h"
#define XPATH_MAXATTRSIZE 64

View File

@ -40,9 +40,7 @@
#include "pathd/path_pcep_lib.h"
#include "pathd/path_pcep_pcc.h"
#ifndef VTYSH_EXTRACT_PL
#include "pathd/path_pcep_cli_clippy.c"
#endif
#define DEFAULT_PCE_PRECEDENCE 255
#define DEFAULT_PCC_MSD 4

View File

@ -29,9 +29,7 @@
#include "pathd/path_errors.h"
#include "pathd/path_ted.h"
#ifndef VTYSH_EXTRACT_PL
#include "pathd/path_ted_clippy.c"
#endif
static struct ls_ted *path_ted_create_ted(void);
static void path_ted_register_vty(void);

View File

@ -5,16 +5,11 @@
if PATHD
noinst_LIBRARIES += pathd/libpath.a
sbin_PROGRAMS += pathd/pathd
vtysh_scan += \
pathd/path_cli.c \
pathd/path_ted.c \
#end
vtysh_daemons += pathd
# TODO add man page
#man8 += $(MANBUILD)/pathd.8
if PATHD_PCEP
vtysh_scan += pathd/path_pcep_cli.c
module_LTLIBRARIES += pathd/pathd_pcep.la
endif

View File

@ -23,9 +23,7 @@
#include "command.h"
#include "vector.h"
#ifndef VTYSH_EXTRACT_PL
#include "pbrd/pbr_debug_clippy.c"
#endif
#include "pbrd/pbr_debug.h"
struct debug pbr_dbg_map = {0, "PBR map"};

View File

@ -36,9 +36,7 @@
#include "pbrd/pbr_zebra.h"
#include "pbrd/pbr_vty.h"
#include "pbrd/pbr_debug.h"
#ifndef VTYSH_EXTRACT_PL
#include "pbrd/pbr_vty_clippy.c"
#endif
DEFUN_NOSH(pbr_map, pbr_map_cmd, "pbr-map PBRMAP seq (1-700)",
"Create pbr-map or enter pbr-map command mode\n"

View File

@ -5,10 +5,6 @@
if PBRD
noinst_LIBRARIES += pbrd/libpbr.a
sbin_PROGRAMS += pbrd/pbrd
vtysh_scan += \
pbrd/pbr_vty.c \
pbrd/pbr_debug.c \
# end
vtysh_daemons += pbrd
man8 += $(MANBUILD)/frr-pbrd.8
endif

View File

@ -45,9 +45,7 @@
#include "pim_zebra.h"
#include "pim_instance.h"
#ifndef VTYSH_EXTRACT_PL
#include "pimd/pim6_cmd_clippy.c"
#endif
static struct cmd_node debug_node = {
.name = "debug",

View File

@ -2319,9 +2319,7 @@ void gm_ifp_update(struct interface *ifp)
#include "lib/command.h"
#ifndef VTYSH_EXTRACT_PL
#include "pimd/pim6_mld_clippy.c"
#endif
static struct vrf *gm_cmd_vrf_lookup(struct vty *vty, const char *vrf_str,
int *err)

View File

@ -70,9 +70,7 @@
#include "pim_addr.h"
#include "pim_cmd_common.h"
#ifndef VTYSH_EXTRACT_PL
#include "pimd/pim_cmd_clippy.c"
#endif
static struct cmd_node debug_node = {
.name = "debug",

View File

@ -39,10 +39,8 @@
#if defined(HAVE_LINUX_MROUTE_H)
#include <linux/mroute.h>
#else
#ifndef VTYSH_EXTRACT_PL
#include "linux/mroute.h"
#endif
#endif
typedef struct vifctl pim_vifctl;
typedef struct igmpmsg kernmsg;
@ -86,10 +84,8 @@ typedef struct sioc_sg_req pim_sioc_sg_req;
#if defined(HAVE_LINUX_MROUTE6_H)
#include <linux/mroute6.h>
#else
#ifndef VTYSH_EXTRACT_PL
#include "linux/mroute6.h"
#endif
#endif
#ifndef MRT_INIT
#define MRT_BASE MRT6_BASE

View File

@ -6,11 +6,6 @@ if PIMD
sbin_PROGRAMS += pimd/pimd
bin_PROGRAMS += pimd/mtracebis
noinst_PROGRAMS += pimd/test_igmpv3_join
vtysh_scan += \
pimd/pim_cmd.c \
pimd/pim6_cmd.c \
pimd/pim6_mld.c \
#end
vtysh_daemons += pimd
vtysh_daemons += pim6d
man8 += $(MANBUILD)/frr-pimd.8

View File

@ -1,72 +0,0 @@
#!/usr/bin/env python3
#
# Quick demo program that checks whether files define commands that aren't
# in vtysh. Execute after building.
#
# This is free and unencumbered software released into the public domain.
#
# Anyone is free to copy, modify, publish, use, compile, sell, or
# distribute this software, either in source code form or as a compiled
# binary, for any purpose, commercial or non-commercial, and by any
# means.
#
# In jurisdictions that recognize copyright laws, the author or authors
# of this software dedicate any and all copyright interest in the
# software to the public domain. We make this dedication for the benefit
# of the public at large and to the detriment of our heirs and
# successors. We intend this dedication to be an overt act of
# relinquishment in perpetuity of all present and future rights to this
# software under copyright law.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
#
# For more information, please refer to <http://unlicense.org/>
import os
import json
import subprocess
os.chdir(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
with open("frr.xref", "r") as fd:
data = json.load(fd)
vtysh_scan, _ = subprocess.Popen(
["make", "var-vtysh_scan"], stdout=subprocess.PIPE
).communicate()
vtysh_scan = set(vtysh_scan.decode("US-ASCII").split())
check = set()
vtysh = {}
for cmd, defs in data["cli"].items():
for binary, clidef in defs.items():
if clidef["defun"]["file"].startswith("vtysh/"):
vtysh[clidef["string"]] = clidef
for cmd, defs in data["cli"].items():
for binary, clidef in defs.items():
if clidef["defun"]["file"].startswith("vtysh/"):
continue
if clidef["defun"]["file"] not in vtysh_scan:
vtysh_def = vtysh.get(clidef["string"])
if vtysh_def is not None:
print(
"\033[33m%s defines %s, has a custom define in vtysh %s\033[m"
% (clidef["defun"]["file"], cmd, vtysh_def["defun"]["file"])
)
else:
print(
"\033[31m%s defines %s, not in vtysh_scan\033[m"
% (clidef["defun"]["file"], cmd)
)
check.add(clidef["defun"]["file"])
print("\nfiles to check:\n\t" + " ".join(sorted(check)))

386
python/xref2vtysh.py Normal file
View File

@ -0,0 +1,386 @@
# FRR xref vtysh command extraction
#
# Copyright (C) 2022 David Lamparter for NetDEF, Inc.
#
# This program 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 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
# more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; see the file COPYING; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
Generate vtysh_cmd.c from frr .xref file(s).
This can run either standalone or as part of xrelfo. The latter saves a
non-negligible amount of time (0.5s on average systems, more on e.g. slow ARMs)
since serializing and deserializing JSON is a significant bottleneck in this.
"""
import sys
import os
import re
import pathlib
import argparse
from collections import defaultdict
import difflib
import typing
from typing import (
Dict,
List,
)
import json
try:
import ujson as json # type: ignore
except ImportError:
pass
frr_top_src = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# vtysh needs to know which daemon(s) to send commands to. For lib/, this is
# not quite obvious...
daemon_flags = {
"lib/agentx.c": "VTYSH_ISISD|VTYSH_RIPD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ZEBRA",
"lib/filter.c": "VTYSH_ACL",
"lib/filter_cli.c": "VTYSH_ACL",
"lib/if.c": "VTYSH_INTERFACE",
"lib/keychain.c": "VTYSH_RIPD|VTYSH_EIGRPD|VTYSH_OSPF6D",
"lib/lib_vty.c": "VTYSH_ALL",
"lib/log_vty.c": "VTYSH_ALL",
"lib/nexthop_group.c": "VTYSH_NH_GROUP",
"lib/resolver.c": "VTYSH_NHRPD|VTYSH_BGPD",
"lib/routemap.c": "VTYSH_RMAP",
"lib/routemap_cli.c": "VTYSH_RMAP",
"lib/spf_backoff.c": "VTYSH_ISISD",
"lib/thread.c": "VTYSH_ALL",
"lib/vrf.c": "VTYSH_VRF",
"lib/vty.c": "VTYSH_ALL",
}
vtysh_cmd_head = """/* autogenerated file, DO NOT EDIT! */
#include <zebra.h>
#include "command.h"
#include "linklist.h"
#include "vtysh/vtysh.h"
"""
if sys.stderr.isatty():
_fmt_red = "\033[31m"
_fmt_green = "\033[32m"
_fmt_clear = "\033[m"
else:
_fmt_red = _fmt_green = _fmt_clear = ""
def c_escape(text: str) -> str:
"""
Escape string for output into C source code.
Handles only what's needed here. CLI strings and help text don't contain
weird special characters.
"""
return text.replace("\\", "\\\\").replace('"', '\\"').replace("\n", "\\n")
class NodeDict(defaultdict):
"""
CLI node ID (integer) -> dict of commands in that node.
"""
nodenames: Dict[int, str] = {}
def __init__(self):
super().__init__(dict)
def items_named(self):
for k, v in self.items():
yield self.nodename(k), v
@classmethod
def nodename(cls, nodeid: int) -> str:
return cls.nodenames.get(nodeid, str(nodeid))
@classmethod
def load_nodenames(cls):
with open(os.path.join(frr_top_src, "lib", "command.h"), "r") as fd:
command_h = fd.read()
nodes = re.search(r"enum\s+node_type\s+\{(.*?)\}", command_h, re.S)
if nodes is None:
raise RuntimeError(
"regex failed to match on lib/command.h (to get CLI node names)"
)
text = nodes.group(1)
text = re.sub(r"/\*.*?\*/", "", text, flags=re.S)
text = re.sub(r"//.*?$", "", text, flags=re.M)
text = text.replace(",", " ")
text = text.split()
for i, name in enumerate(text):
cls.nodenames[i] = name
class CommandEntry:
"""
CLI command definition.
- one DEFUN creates at most one of these, even if the same command is
installed in multiple CLI nodes (e.g. BGP address-family nodes)
- for each CLI node, commands with the same CLI string are merged. This
is *almost* irrelevant - ospfd & ospf6d define some identical commands
in the route-map node. Those must be merged for things to work
correctly.
"""
all_defs: List["CommandEntry"] = []
warn_counter = 0
def __init__(self, origin, name, spec):
self.origin = origin
self.name = name
self._spec = spec
self._registered = False
self.cmd = spec["string"]
self._cmd_normalized = self.normalize_cmd(self.cmd)
self.hidden = "hidden" in spec.get("attrs", [])
self.daemons = self._get_daemons()
self.doclines = self._spec["doc"].splitlines(keepends=True)
if not self.doclines[-1].endswith("\n"):
self.warn_loc("docstring does not end with \\n")
def warn_loc(self, wtext, nodename=None):
"""
Print warning with parseable (compiler style) location
Matching the way compilers emit file/lineno means editors/IDE can
identify / jump to the error location.
"""
if nodename:
prefix = ": [%s] %s:" % (nodename, self.name)
else:
prefix = ": %s:" % (self.name,)
for line in wtext.rstrip("\n").split("\n"):
sys.stderr.write(
"%s:%d%s %s\n"
% (
self._spec["defun"]["file"],
self._spec["defun"]["line"],
prefix,
line,
)
)
prefix = "- "
CommandEntry.warn_counter += 1
def _get_daemons(self):
path = pathlib.Path(self.origin)
if path.name == "vtysh":
return {}
defun_file = os.path.relpath(self._spec["defun"]["file"], frr_top_src)
defun_path = pathlib.Path(defun_file)
if defun_path.parts[0] != "lib":
if "." not in path.name:
# daemons don't have dots in their filename
return {"VTYSH_" + path.name.upper()}
# loadable modules - use directory name to determine daemon
return {"VTYSH_" + path.parts[-2].upper()}
if defun_file in daemon_flags:
return {daemon_flags[defun_file]}
v6_cmd = "ipv6" in self.name
if defun_file == "lib/plist.c":
if v6_cmd:
return {
"VTYSH_RIPNGD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ZEBRA|VTYSH_PIM6D|VTYSH_BABELD|VTYSH_ISISD|VTYSH_FABRICD"
}
else:
return {
"VTYSH_RIPD|VTYSH_OSPFD|VTYSH_BGPD|VTYSH_ZEBRA|VTYSH_PIMD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_ISISD|VTYSH_FABRICD"
}
if defun_file == "lib/if_rmap.c":
if v6_cmd:
return {"VTYSH_RIPNGD"}
else:
return {"VTYSH_RIPD"}
return {}
def __repr__(self):
return f"<CommandEntry {self.name}: {self.cmd!r}>"
def register(self):
"""Track DEFUNs so each is only output once."""
if not self._registered:
self.all_defs.append(self)
self._registered = True
return self
def merge(self, other, nodename):
if self._cmd_normalized != other._cmd_normalized:
self.warn_loc(
f"command definition mismatch, first definied as:\n{self.cmd!r}",
nodename=nodename,
)
other.warn_loc(f"later defined as:\n{other.cmd!r}", nodename=nodename)
if self._spec["doc"] != other._spec["doc"]:
self.warn_loc(
f"help string mismatch, first defined here (-)", nodename=nodename
)
other.warn_loc(
f"later defined here (+)\nnote: both commands define {self.cmd!r} in same node ({nodename})",
nodename=nodename,
)
d = difflib.Differ()
for diffline in d.compare(self.doclines, other.doclines):
if diffline.startswith(" "):
continue
if diffline.startswith("+ "):
diffline = _fmt_green + diffline
elif diffline.startswith("- "):
diffline = _fmt_red + diffline
sys.stderr.write("\t" + diffline.rstrip("\n") + _fmt_clear + "\n")
if self.hidden != other.hidden:
self.warn_loc(
f"hidden flag mismatch, first {self.hidden!r} here", nodename=nodename
)
other.warn_loc(
f"later {other.hidden!r} here (+)\nnote: both commands define {self.cmd!r} in same node ({nodename})",
nodename=nodename,
)
# ensure name is deterministic regardless of input DEFUN order
self.name = min([self.name, other.name], key=lambda i: (len(i), i))
self.daemons.update(other.daemons)
def get_def(self):
doc = "\n".join(['\t"%s"' % c_escape(line) for line in self.doclines])
defsh = "DEFSH_HIDDEN" if self.hidden else "DEFSH"
# make daemon list deterministic
daemons = set()
for daemon in self.daemons:
daemons.update(daemon.split("|"))
daemon_str = "|".join(sorted(daemons))
return f"""
{defsh} ({daemon_str}, {self.name}_vtysh,
\t"{c_escape(self.cmd)}",
{doc})
"""
# accept slightly different command definitions that result in the same command
re_collapse_ws = re.compile(r"\s+")
re_remove_varnames = re.compile(r"\$[a-z][a-z0-9_]*")
@classmethod
def normalize_cmd(cls, cmd):
cmd = cmd.strip()
cmd = cls.re_collapse_ws.sub(" ", cmd)
cmd = cls.re_remove_varnames.sub("", cmd)
return cmd
@classmethod
def process(cls, nodes, name, origin, spec):
if "nosh" in spec.get("attrs", []):
return
if origin == "vtysh/vtysh":
return
if origin == "isisd/fabricd":
# dirty workaround :(
name = "fabricd_" + name
entry = cls(origin, name, spec)
if not entry.daemons:
return
for nodedata in spec.get("nodes", []):
node = nodes[nodedata["node"]]
if entry._cmd_normalized not in node:
node[entry._cmd_normalized] = entry.register()
else:
node[entry._cmd_normalized].merge(
entry, nodes.nodename(nodedata["node"])
)
@classmethod
def load(cls, xref):
nodes = NodeDict()
for cmd_name, origins in xref.get("cli", {}).items():
for origin, spec in origins.items():
CommandEntry.process(nodes, cmd_name, origin, spec)
return nodes
@classmethod
def output_defs(cls, ofd):
for entry in sorted(cls.all_defs, key=lambda i: i.name):
ofd.write(entry.get_def())
@classmethod
def output_install(cls, ofd, nodes):
ofd.write("\nvoid vtysh_init_cmd(void)\n{\n")
for name, items in sorted(nodes.items_named()):
for item in sorted(items.values(), key=lambda i: i.name):
ofd.write(f"\tinstall_element({name}, &{item.name}_vtysh);\n")
ofd.write("}\n")
@classmethod
def run(cls, xref, ofd):
ofd.write(vtysh_cmd_head)
NodeDict.load_nodenames()
nodes = cls.load(xref)
cls.output_defs(ofd)
cls.output_install(ofd, nodes)
def main():
argp = argparse.ArgumentParser(description="FRR xref to vtysh defs")
argp.add_argument(
"xreffile", metavar="XREFFILE", type=str, help=".xref file to read"
)
argp.add_argument("-Werror", action="store_const", const=True)
args = argp.parse_args()
with open(args.xreffile, "r") as fd:
data = json.load(fd)
CommandEntry.run(data, sys.stdout)
if args.Werror and CommandEntry.warn_counter:
sys.exit(1)
if __name__ == "__main__":
main()

View File

@ -37,6 +37,7 @@ from clippy.uidhash import uidhash
from clippy.elf import *
from clippy import frr_top_src, CmdAttr
from tiabwarfo import FieldApplicator
from xref2vtysh import CommandEntry
try:
with open(os.path.join(frr_top_src, 'python', 'xrefstructs.json'), 'r') as fd:
@ -366,6 +367,7 @@ def main():
argp = argparse.ArgumentParser(description = 'FRR xref ELF extractor')
argp.add_argument('-o', dest='output', type=str, help='write JSON output')
argp.add_argument('--out-by-file', type=str, help='write by-file JSON output')
argp.add_argument('-c', dest='vtysh_cmds', type=str, help='write vtysh_cmd.c')
argp.add_argument('-Wlog-format', action='store_const', const=True)
argp.add_argument('-Wlog-args', action='store_const', const=True)
argp.add_argument('-Werror', action='store_const', const=True)
@ -435,5 +437,13 @@ def _main(args):
json.dump(outbyfile, fd, indent=2, sort_keys=True, **json_dump_args)
os.rename(args.out_by_file + '.tmp', args.out_by_file)
if args.vtysh_cmds:
with open(args.vtysh_cmds + '.tmp', 'w') as fd:
CommandEntry.run(out, fd)
os.rename(args.vtysh_cmds + '.tmp', args.vtysh_cmds)
if args.Werror and CommandEntry.warn_counter:
sys.exit(1)
if __name__ == '__main__':
main()

View File

@ -30,9 +30,7 @@
#include "ripd/ripd.h"
#include "ripd/rip_nb.h"
#ifndef VTYSH_EXTRACT_PL
#include "ripd/rip_cli_clippy.c"
#endif
/*
* XPath: /frr-ripd:ripd/instance

View File

@ -4,11 +4,6 @@
if RIPD
sbin_PROGRAMS += ripd/ripd
vtysh_scan += \
ripd/rip_cli.c \
ripd/rip_debug.c \
ripd/ripd.c \
# end
vtysh_daemons += ripd
if SNMP

View File

@ -30,9 +30,7 @@
#include "ripngd/ripngd.h"
#include "ripngd/ripng_nb.h"
#ifndef VTYSH_EXTRACT_PL
#include "ripngd/ripng_cli_clippy.c"
#endif
/*
* XPath: /frr-ripngd:ripngd/instance

View File

@ -4,11 +4,6 @@
if RIPNGD
sbin_PROGRAMS += ripngd/ripngd
vtysh_scan += \
ripngd/ripng_cli.c \
ripngd/ripng_debug.c \
ripngd/ripngd.c \
# end
vtysh_daemons += ripngd
man8 += $(MANBUILD)/frr-ripngd.8
endif

View File

@ -37,9 +37,7 @@
#include "sharpd/sharp_zebra.h"
#include "sharpd/sharp_nht.h"
#include "sharpd/sharp_vty.h"
#ifndef VTYSH_EXTRACT_PL
#include "sharpd/sharp_vty_clippy.c"
#endif
DEFINE_MTYPE_STATIC(SHARPD, SRV6_LOCATOR, "SRv6 Locator");

View File

@ -5,7 +5,6 @@
if SHARPD
noinst_LIBRARIES += sharpd/libsharp.a
sbin_PROGRAMS += sharpd/sharpd
vtysh_scan += sharpd/sharp_vty.c
vtysh_daemons += sharpd
man8 += $(MANBUILD)/frr-sharpd.8
endif

View File

@ -36,9 +36,7 @@
#include "static_vty.h"
#include "static_routes.h"
#include "static_debug.h"
#ifndef VTYSH_EXTRACT_PL
#include "staticd/static_vty_clippy.c"
#endif
#include "static_nb.h"
#define STATICD_STR "Static route daemon\n"

View File

@ -5,7 +5,6 @@
if STATICD
noinst_LIBRARIES += staticd/libstatic.a
sbin_PROGRAMS += staticd/staticd
vtysh_scan += staticd/static_vty.c
vtysh_daemons += staticd
man8 += $(MANBUILD)/frr-staticd.8
endif

View File

@ -4,7 +4,6 @@
if VRRPD
sbin_PROGRAMS += vrrpd/vrrpd
vtysh_scan += vrrpd/vrrp_vty.c
vtysh_daemons += vrrpd
man8 += $(MANBUILD)/frr-vrrpd.8
endif

View File

@ -33,9 +33,7 @@
#include "vrrp_debug.h"
#include "vrrp_vty.h"
#include "vrrp_zebra.h"
#ifndef VTYSH_EXTRACT_PL
#include "vrrpd/vrrp_vty_clippy.c"
#endif
#define VRRP_STR "Virtual Router Redundancy Protocol\n"

4
vtysh/.gitignore vendored
View File

@ -1,4 +1,6 @@
vtysh
vtysh_cmd.c
extract.pl
vtysh_daemons.h
# does not exist anymore - remove 2023-10-04 or so
extract.pl

View File

@ -1,282 +0,0 @@
#! @PERL@
##
## @configure_input@
##
## Virtual terminal interface shell command extractor.
## Copyright (C) 2000 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.
##
use Getopt::Long;
print <<EOF;
#include <zebra.h>
#include "command.h"
#include "linklist.h"
#include "vtysh/vtysh.h"
EOF
my $cli_stomp = 0;
sub scan_file {
my ( $file, $fabricd) = @_;
$cppadd = $fabricd ? "-DFABRICD=1" : "";
$command_line = "@CPP@ -P -std=gnu11 -DHAVE_CONFIG_H -DVTYSH_EXTRACT_PL -Ivtysh/@top_builddir@ -Ivtysh/@top_srcdir@ -Ivtysh/@top_srcdir@/lib -Ivtysh/@top_builddir@/lib -Ivtysh/@top_srcdir@/bgpd -Ivtysh/@top_srcdir@/bgpd/rfapi @LUA_INCLUDE@ @CPPFLAGS@ @LIBYANG_CFLAGS@ $cppadd $file |";
open (FH, $command_line)
|| die "Open to the pipeline failed: $!\n\nCommand Issued:\n$command_line";
local $/; undef $/;
$line = <FH>;
if (!close (FH)) {
die "File: $file failed to compile:\n$!\nwhen extracting cli from it please inspect\n"
}
# ?: makes a group non-capturing
@defun = ($line =~ /((?:DEFUN|DEFUN_HIDDEN|DEFUN_YANG|ALIAS|ALIAS_HIDDEN|ALIAS_YANG|DEFPY|DEFPY_HIDDEN|DEFPY_YANG)\s*\(.+?\));?\s?\s?\n/sg);
@install = ($line =~ /install_element\s*\(\s*[0-9A-Z_]+,\s*&[^;]*;\s*\n/sg);
# DEFUN process
foreach (@defun) {
# $_ will contain the entire string including the DEFUN, ALIAS, etc.
# We need to extract the DEFUN/ALIAS from everything in ()s.
# The /s at the end tells the regex to allow . to match newlines.
$_ =~ /^(.*?)\s*\((.*)\)$/s;
my (@defun_array);
$defun_or_alias = $1;
@defun_array = split (/,/, $2);
if ($defun_or_alias =~ /_HIDDEN/) {
$hidden = 1;
} else {
$hidden = 0;
}
$defun_array[0] = '';
# Actual input command string.
$str = "$defun_array[2]";
$str =~ s/^\s+//g;
$str =~ s/\s+$//g;
# Get VTY command structure. This is needed for searching
# install_element() command.
$cmd = "$defun_array[1]";
$cmd =~ s/^\s+//g;
$cmd =~ s/\s+$//g;
if ($fabricd) {
$cmd = "fabricd_" . $cmd;
}
# $protocol is VTYSH_PROTO format for redirection of user input
if ($file =~ /lib\/keychain\.c$/) {
$protocol = "VTYSH_RIPD|VTYSH_EIGRPD|VTYSH_OSPF6D";
}
elsif ($file =~ /lib\/routemap\.c$/ || $file =~ /lib\/routemap_cli\.c$/) {
$protocol = "VTYSH_RMAP";
}
elsif ($file =~ /lib\/vrf\.c$/) {
$protocol = "VTYSH_VRF";
}
elsif ($file =~ /lib\/if\.c$/) {
$protocol = "VTYSH_INTERFACE";
}
elsif ($file =~ /lib\/(filter|filter_cli)\.c$/) {
$protocol = "VTYSH_ACL";
}
elsif ($file =~ /lib\/(lib|log)_vty\.c$/) {
$protocol = "VTYSH_ALL";
}
elsif ($file =~ /lib\/agentx\.c$/) {
$protocol = "VTYSH_ISISD|VTYSH_RIPD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ZEBRA";
}
elsif ($file =~ /lib\/nexthop_group\.c$/) {
$protocol = "VTYSH_NH_GROUP";
}
elsif ($file =~ /lib\/plist\.c$/) {
if ($defun_array[1] =~ m/ipv6/) {
$protocol = "VTYSH_RIPNGD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ZEBRA|VTYSH_PIM6D|VTYSH_BABELD|VTYSH_ISISD|VTYSH_FABRICD";
} else {
$protocol = "VTYSH_RIPD|VTYSH_OSPFD|VTYSH_BGPD|VTYSH_ZEBRA|VTYSH_PIMD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_ISISD|VTYSH_FABRICD";
}
}
elsif ($file =~ /lib\/if_rmap\.c$/) {
if ($defun_array[1] =~ m/ipv6/) {
$protocol = "VTYSH_RIPNGD";
} else {
$protocol = "VTYSH_RIPD";
}
}
elsif ($file =~ /lib\/resolver\.c$/) {
$protocol = "VTYSH_NHRPD|VTYSH_BGPD";
}
elsif ($file =~ /lib\/spf_backoff\.c$/) {
$protocol = "VTYSH_ISISD";
}
elsif ($file =~ /lib\/(vty|thread)\.c$/) {
$protocol = "VTYSH_ALL";
}
elsif ($file =~ /librfp\/.*\.c$/ || $file =~ /rfapi\/.*\.c$/) {
$protocol = "VTYSH_BGPD";
}
elsif ($fabricd) {
$protocol = "VTYSH_FABRICD";
}
elsif ($file =~ /pimd\/pim6_.*\.c$/) {
$protocol = "VTYSH_PIM6D";
}
else {
($protocol) = ($file =~ /^(?:.*\/)?([a-z0-9]+)\/[a-zA-Z0-9_\-]+\.c$/);
$protocol = "VTYSH_" . uc $protocol;
}
# Append _vtysh to structure then build DEFUN again
$defun_array[1] = $cmd . "_vtysh";
$defun_body = join (", ", @defun_array);
# $cmd -> $str hash for lookup
if (exists($cmd2str{$cmd})) {
warn "Duplicate CLI Function: $cmd\n";
warn "\tFrom cli: $cmd2str{$cmd} to New cli: $str\n";
warn "\tOriginal Protocol: $cmd2proto{$cmd} to New Protocol: $protocol\n";
$cli_stomp++;
}
$cmd2str{$cmd} = $str;
$cmd2defun{$cmd} = $defun_body;
$cmd2proto{$cmd} = $protocol;
$cmd2hidden{$cmd} = $hidden;
}
# install_element() process
foreach (@install) {
my (@element_array);
@element_array = split (/,/);
# Install node
$enode = $element_array[0];
$enode =~ s/^\s+//g;
$enode =~ s/\s+$//g;
($enode) = ($enode =~ /([0-9A-Z_]+)$/);
# VTY command structure.
($ecmd) = ($element_array[1] =~ /&([^\)]+)/);
$ecmd =~ s/^\s+//g;
$ecmd =~ s/\s+$//g;
if ($fabricd) {
$ecmd = "fabricd_" . $ecmd;
}
# Register $ecmd
if (defined ($cmd2str{$ecmd})) {
my ($key);
$key = $enode . "," . $cmd2str{$ecmd};
$ocmd{$key} = $ecmd;
$odefun{$key} = $cmd2defun{$ecmd};
if ($cmd2hidden{$ecmd}) {
$defsh{$key} = "DEFSH_HIDDEN"
} else {
$defsh{$key} = "DEFSH"
}
push (@{$oproto{$key}}, $cmd2proto{$ecmd});
}
}
}
my $have_isisd = 0;
my $have_fabricd = 0;
GetOptions('have-isisd' => \$have_isisd, 'have-fabricd' => \$have_fabricd);
foreach (@ARGV) {
if (/(^|\/)isisd\//) {
# We scan all the IS-IS files twice, once for isisd,
# once for fabricd. Exceptions are made for the files
# that are not shared between the two.
if (/isis_vty_isisd.c/) {
if ( $have_isisd ) {
scan_file($_, 0);
}
} elsif (/isis_vty_fabricd.c/) {
if ( $have_fabricd ) {
scan_file($_, 1);
}
} else {
if ( $have_isisd ) {
scan_file($_, 0);
}
if ( $have_fabricd ) {
scan_file($_, 1);
}
}
} else {
scan_file($_, 0);
}
}
# When we have cli commands that map to the same function name, we
# can introduce subtle bugs due to code not being called when
# we think it is.
#
# If extract.pl fails with a error message and you've been
# modifying the cli, then go back and fix your code to
# not have cli command function collisions.
# please fix your code before submittal
if ($cli_stomp) {
warn "There are $cli_stomp command line stomps\n";
}
# Check finaly alive $cmd;
foreach (keys %odefun) {
my ($node, $str) = (split (/,/));
my ($cmd) = $ocmd{$_};
$live{$cmd} = $_;
}
# Output DEFSH
foreach (sort keys %live) {
my ($proto);
my ($key);
$key = $live{$_};
$proto = join ("|", @{$oproto{$key}});
printf "$defsh{$key} ($proto$odefun{$key})\n\n";
}
# Output install_element
print <<EOF;
void vtysh_init_cmd(void)
{
EOF
foreach (sort keys %odefun) {
my ($node, $str) = (split (/,/));
$cmd = $ocmd{$_};
$cmd =~ s/_cmd$/_cmd_vtysh/;
printf " install_element ($node, &$cmd);\n";
}
print <<EOF
}
EOF

View File

@ -20,7 +20,6 @@ vtysh_vtysh_SOURCES = \
nodist_vtysh_vtysh_SOURCES = \
vtysh/vtysh_cmd.c \
# end
CLEANFILES += vtysh/vtysh_cmd.c
noinst_HEADERS += \
vtysh/vtysh.h \
@ -39,23 +38,3 @@ $(vtysh_vtysh_OBJECTS): vtysh/vtysh_daemons.h
CLEANFILES += vtysh/vtysh_daemons.h
vtysh/vtysh_daemons.h:
$(PERL) $(top_srcdir)/vtysh/daemons.pl $(vtysh_daemons) > vtysh/vtysh_daemons.h
AM_V_EXTRACT = $(am__v_EXTRACT_$(V))
am__v_EXTRACT_ = $(am__v_EXTRACT_$(AM_DEFAULT_VERBOSITY))
am__v_EXTRACT_0 = @echo " EXTRACT " $@;
am__v_EXTRACT_1 =
if ISISD
HAVE_ISISD = --have-isisd
else
HAVE_ISISD =
endif
if FABRICD
HAVE_FABRICD = --have-fabricd
else
HAVE_FABRICD =
endif
vtysh/vtysh_cmd.c: vtysh/extract.pl $(vtysh_scan)
$(AM_V_EXTRACT) $^ $(HAVE_ISISD) $(HAVE_FABRICD) > vtysh/vtysh_cmd.c

View File

@ -4,7 +4,6 @@
if WATCHFRR
sbin_PROGRAMS += watchfrr/watchfrr
vtysh_scan += watchfrr/watchfrr_vty.c
man8 += $(MANBUILD)/frr-watchfrr.8
endif

View File

@ -153,9 +153,7 @@ DEFUN_NOSH (show_logging,
return CMD_SUCCESS;
}
#ifndef VTYSH_EXTRACT_PL
#include "watchfrr/watchfrr_vty_clippy.c"
#endif
DEFPY (watchfrr_ignore_daemon,
watchfrr_ignore_daemon_cmd,

View File

@ -23,9 +23,7 @@
#include "command.h"
#include "debug.h"
#ifndef VTYSH_EXTRACT_PL
#include "zebra/debug_clippy.c"
#endif
/* For debug statement. */
unsigned long zebra_debug_event;

View File

@ -23,9 +23,7 @@
#include "lib/json.h"
#include "zebra/dpdk/zebra_dplane_dpdk.h"
#ifndef VTYSH_EXTRACT_PL
#include "zebra/dpdk/zebra_dplane_dpdk_vty_clippy.c"
#endif
#define ZD_STR "Zebra dataplane information\n"
#define ZD_DPDK_STR "DPDK offload information\n"

View File

@ -2603,9 +2603,7 @@ static void interface_update_stats(void)
#endif /* HAVE_NET_RT_IFLIST */
}
#ifndef VTYSH_EXTRACT_PL
#include "zebra/interface_clippy.c"
#endif
/* Show all interfaces to vty. */
DEFPY(show_interface, show_interface_cmd,
"show interface vrf NAME$vrf_name [brief$brief] [json$uj]",

View File

@ -51,9 +51,7 @@ static uint32_t interfaces_configured_for_ra_from_bgp;
#if defined(HAVE_RTADV)
#ifndef VTYSH_EXTRACT_PL
#include "zebra/rtadv_clippy.c"
#endif
DEFINE_MTYPE_STATIC(ZEBRA, RTADV_PREFIX, "Router Advertisement Prefix");
DEFINE_MTYPE_STATIC(ZEBRA, ADV_IF, "Advertised Interface");

View File

@ -4,29 +4,6 @@
if ZEBRA
sbin_PROGRAMS += zebra/zebra
vtysh_scan += \
zebra/debug.c \
zebra/interface.c \
zebra/router-id.c \
zebra/rtadv.c \
zebra/zebra_gr.c \
zebra/zebra_mlag_vty.c \
zebra/zebra_evpn_mh.c \
zebra/zebra_mpls_vty.c \
zebra/zebra_srv6_vty.c \
zebra/zebra_ptm.c \
zebra/zebra_pw.c \
zebra/zebra_routemap.c \
zebra/zebra_vty.c \
zebra/zserv.c \
zebra/zebra_vrf.c \
zebra/dpdk/zebra_dplane_dpdk_vty.c \
# end
# can be loaded as DSO - always include for vtysh
vtysh_scan += zebra/irdp_interface.c
vtysh_scan += zebra/zebra_fpm.c
vtysh_daemons += zebra
if IRDP
@ -255,8 +232,6 @@ module_LTLIBRARIES += zebra/dplane_fpm_nl.la
zebra_dplane_fpm_nl_la_SOURCES = zebra/dplane_fpm_nl.c
zebra_dplane_fpm_nl_la_LDFLAGS = $(MODULE_LDFLAGS)
zebra_dplane_fpm_nl_la_LIBADD =
vtysh_scan += zebra/dplane_fpm_nl.c
endif
if NETLINK_DEBUG

View File

@ -3252,9 +3252,7 @@ int zebra_evpn_mh_if_write(struct vty *vty, struct interface *ifp)
return 0;
}
#ifndef VTYSH_EXTRACT_PL
#include "zebra/zebra_evpn_mh_clippy.c"
#endif
/* CLI for setting an ES in bypass mode */
DEFPY_HIDDEN(zebra_evpn_es_bypass, zebra_evpn_es_bypass_cmd,
"[no] evpn mh bypass",

View File

@ -29,9 +29,7 @@
#include "debug.h"
#include "zapi_msg.h"
#ifndef VTYSH_EXTRACT_PL
#include "zebra/zebra_mlag_vty_clippy.c"
#endif
DEFUN_HIDDEN (show_mlag,
show_mlag_cmd,

View File

@ -40,9 +40,7 @@
#include "zebra/zebra_rnh.h"
#include "zebra/zebra_routemap.h"
#ifndef VTYSH_EXTRACT_PL
#include "zebra/zebra_routemap_clippy.c"
#endif
static uint32_t zebra_rmap_update_timer = ZEBRA_RMAP_DEFAULT_UPDATE_TIMER;
static struct thread *zebra_t_rmap_update = NULL;

View File

@ -40,9 +40,7 @@
#include "zebra/zebra_routemap.h"
#include "zebra/zebra_dplane.h"
#ifndef VTYSH_EXTRACT_PL
#include "zebra/zebra_srv6_vty_clippy.c"
#endif
static int zebra_sr_config(struct vty *vty);

View File

@ -44,9 +44,7 @@
#include "zebra/zebra_vxlan.h"
#include "zebra/zebra_netns_notify.h"
#include "zebra/zebra_routemap.h"
#ifndef VTYSH_EXTRACT_PL
#include "zebra/zebra_vrf_clippy.c"
#endif
#include "zebra/table_manager.h"
static void zebra_vrf_table_create(struct zebra_vrf *zvrf, afi_t afi,

View File

@ -46,9 +46,7 @@
#include "lib/route_opaque.h"
#include "zebra/zebra_vxlan.h"
#include "zebra/zebra_evpn_mh.h"
#ifndef VTYSH_EXTRACT_PL
#include "zebra/zebra_vty_clippy.c"
#endif
#include "zebra/zserv.h"
#include "zebra/router-id.h"
#include "zebra/ipforward.h"