Merge pull request #12196 from opensourcerouting/xref-vtysh

*: rewrite `extract.pl` using `xref` infra
This commit is contained in:
Donald Sharp 2022-11-03 08:54:09 -04:00 committed by GitHub
commit d7cde18c63
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
103 changed files with 951 additions and 997 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1415,9 +1415,7 @@ DEFUN (no_debug_bgp_update_direct_peer,
return CMD_SUCCESS; return CMD_SUCCESS;
} }
#ifndef VTYSH_EXTRACT_PL
#include "bgpd/bgp_debug_clippy.c" #include "bgpd/bgp_debug_clippy.c"
#endif
DEFPY (debug_bgp_update_prefix_afi_safi, DEFPY (debug_bgp_update_prefix_afi_safi,
debug_bgp_update_prefix_afi_safi_cmd, 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" #include "bgpd/bgp_evpn_vty_clippy.c"
#endif
DEFPY(bgp_evpn_flood_control, DEFPY(bgp_evpn_flood_control,
bgp_evpn_flood_control_cmd, bgp_evpn_flood_control_cmd,

View File

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

View File

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

View File

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

View File

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

View File

@ -1308,9 +1308,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); bgp_clear(NULL, bgp, afi, safi, clear_all, BGP_CLEAR_SOFT_IN, NULL);
} }
#ifndef VTYSH_EXTRACT_PL
#include "bgpd/bgp_vty_clippy.c" #include "bgpd/bgp_vty_clippy.c"
#endif
DEFUN_HIDDEN (bgp_local_mac, DEFUN_HIDDEN (bgp_local_mac,
bgp_local_mac_cmd, bgp_local_mac_cmd,

View File

@ -6,36 +6,9 @@ if BGPD
noinst_LIBRARIES += bgpd/libbgp.a noinst_LIBRARIES += bgpd/libbgp.a
sbin_PROGRAMS += bgpd/bgpd sbin_PROGRAMS += bgpd/bgpd
noinst_PROGRAMS += bgpd/bgp_btoa 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 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 if SNMP
module_LTLIBRARIES += bgpd/bgpd_snmp.la module_LTLIBRARIES += bgpd/bgpd_snmp.la
endif endif

View File

@ -2747,7 +2747,6 @@ AC_CONFIG_FILES([
pkgsrc/ripd.sh pkgsrc/ripngd.sh pkgsrc/zebra.sh pkgsrc/ripd.sh pkgsrc/ripngd.sh pkgsrc/zebra.sh
pkgsrc/eigrpd.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/frr], [chmod +x tools/frr])
AC_CONFIG_FILES([tools/watchfrr.sh], [chmod +x tools/watchfrr.sh]) AC_CONFIG_FILES([tools/watchfrr.sh], [chmod +x tools/watchfrr.sh])
AC_CONFIG_FILES([tools/frrinit.sh], [chmod +x tools/frrinit.sh]) AC_CONFIG_FILES([tools/frrinit.sh], [chmod +x tools/frrinit.sh])

View File

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

View File

@ -43,9 +43,14 @@ simplifying the output. This is discussed in :ref:`vtysh-configuration`.
Command Extraction Command Extraction
------------------ ------------------
When VTYSH is built, a Perl script named :file:`extract.pl` searches the FRR To build ``vtysh``, the :file:`python/xref2vtysh.py` script scans through the
codebase looking for ``DEFUN``'s. It extracts these ``DEFUN``'s, transforms :file:`frr.xref` file created earlier in the build process. This file contains
them into ``DEFSH``'s and appends them to ``vtysh_cmd.c``. Each ``DEFSH`` 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 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 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 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 The extraction script contains lots of hardcoded knowledge about what sources
to look at and what flags to use for certain commands. 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: .. _vtysh-special-defuns:
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. simply forwarded to the daemons indicated in the daemon flag.
``DEFUN_NOSH`` ``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 will skip these definitions when extracting commands. This is typically used
when VTYSH must take some special action upon receiving the command, and the 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 programmer therefore needs to write VTYSH's copy of the command manually

View File

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

View File

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

View File

@ -4,12 +4,6 @@
if EIGRPD if EIGRPD
sbin_PROGRAMS += eigrpd/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 vtysh_daemons += eigrpd
man8 += $(MANBUILD)/frr-eigrpd.8 man8 += $(MANBUILD)/frr-eigrpd.8
endif endif

View File

@ -28,6 +28,13 @@ am__v_PROTOC_1 =
SUFFIXES += .pb.h .pb.cc .grpc.pb.cc SUFFIXES += .pb.h .pb.cc .grpc.pb.cc
grpc/frr-northbound.grpc.pb.h: grpc/frr-northbound.grpc.pb.cc
@test -f $@ || rm -f $< || true
@test -f $@ || $(MAKE) $(AM_MAKEFLAGS) $<
grpc/frr-northbound.pb.h: grpc/frr-northbound.pb.cc
@test -f $@ || rm -f $< || true
@test -f $@ || $(MAKE) $(AM_MAKEFLAGS) $<
.proto.pb.cc: .proto.pb.cc:
$(AM_V_PROTOC)$(PROTOC) -I$(top_srcdir) --cpp_out=$(top_builddir) $^ $(AM_V_PROTOC)$(PROTOC) -I$(top_srcdir) --cpp_out=$(top_builddir) $^
.proto.grpc.pb.cc: .proto.grpc.pb.cc:

View File

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

View File

@ -1057,8 +1057,9 @@ static void show_node(struct vty *vty, struct isis_area *area, int level)
} }
DEFUN(show_sr_node, show_sr_node_cmd, DEFUN(show_sr_node, show_sr_node_cmd,
"show isis segment-routing node", "show " PROTO_NAME " segment-routing node",
SHOW_STR PROTO_HELP SHOW_STR
PROTO_HELP
"Segment-Routing\n" "Segment-Routing\n"
"Segment-Routing node\n") "Segment-Routing node\n")
{ {

View File

@ -5,16 +5,6 @@
if ISISD if ISISD
noinst_LIBRARIES += isisd/libisis.a noinst_LIBRARIES += isisd/libisis.a
sbin_PROGRAMS += isisd/isisd 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 vtysh_daemons += isisd
if SNMP if SNMP
module_LTLIBRARIES += isisd/isisd_snmp.la module_LTLIBRARIES += isisd/isisd_snmp.la
@ -25,18 +15,6 @@ endif
if FABRICD if FABRICD
noinst_LIBRARIES += isisd/libfabric.a noinst_LIBRARIES += isisd/libfabric.a
sbin_PROGRAMS += isisd/fabricd 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 vtysh_daemons += fabricd
endif endif

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -32,9 +32,7 @@
#include "northbound.h" #include "northbound.h"
#include "northbound_cli.h" #include "northbound_cli.h"
#include "northbound_db.h" #include "northbound_db.h"
#ifndef VTYSH_EXTRACT_PL
#include "lib/northbound_cli_clippy.c" #include "lib/northbound_cli_clippy.c"
#endif
struct debug nb_dbg_cbs_config = {0, "Northbound callbacks: configuration"}; struct debug nb_dbg_cbs_config = {0, "Northbound callbacks: configuration"};
struct debug nb_dbg_cbs_state = {0, "Northbound callbacks: state"}; 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; return CMD_SUCCESS;
} }
#ifndef VTYSH_EXTRACT_PL
#include "lib/plist_clippy.c" #include "lib/plist_clippy.c"
#endif
DEFPY (show_ip_prefix_list, DEFPY (show_ip_prefix_list,
show_ip_prefix_list_cmd, show_ip_prefix_list_cmd,

View File

@ -26,9 +26,7 @@
#include "lib/northbound_cli.h" #include "lib/northbound_cli.h"
#include "lib/routemap.h" #include "lib/routemap.h"
#ifndef VTYSH_EXTRACT_PL
#include "lib/routemap_cli_clippy.c" #include "lib/routemap_cli_clippy.c"
#endif /* VTYSH_EXTRACT_PL */
#define ROUTE_MAP_CMD_STR \ #define ROUTE_MAP_CMD_STR \
"Create route-map or enter route-map command mode\n" \ "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 \ yang/frr-module-translator.yang.c \
# end # 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 if SQLITE3
lib_libfrr_la_LIBADD += $(SQLITE3_LIBS) lib_libfrr_la_LIBADD += $(SQLITE3_LIBS)
lib_libfrr_la_SOURCES += lib/db.c lib_libfrr_la_SOURCES += lib/db.c
@ -347,7 +326,6 @@ lib_libfrrsnmp_la_SOURCES = \
if CARES if CARES
lib_LTLIBRARIES += lib/libfrrcares.la lib_LTLIBRARIES += lib/libfrrcares.la
pkginclude_HEADERS += lib/resolver.h pkginclude_HEADERS += lib/resolver.h
vtysh_scan += lib/resolver.c
endif endif
lib_libfrrcares_la_CFLAGS = $(AM_CFLAGS) $(CARES_CFLAGS) lib_libfrrcares_la_CFLAGS = $(AM_CFLAGS) $(CARES_CFLAGS)
@ -478,13 +456,18 @@ SUFFIXES += .xref
# dependencies added in python/makefile.py # dependencies added in python/makefile.py
frr.xref: 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 all-am: frr.xref
clean-xref: clean-xref:
-rm -rf $(xrefs) frr.xref -rm -rf $(xrefs) frr.xref
clean-local: clean-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. ## automake's "ylwrap" is a great piece of GNU software... not.
.l.c: .l.c:
$(AM_V_LEX)$(am__skiplex) $(LEXCOMPILE) $< $(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; unsigned long walltime_threshold = CONSUMED_TIME_CHECK;
/* CLI start ---------------------------------------------------------------- */ /* CLI start ---------------------------------------------------------------- */
#ifndef VTYSH_EXTRACT_PL
#include "lib/thread_clippy.c" #include "lib/thread_clippy.c"
#endif
static unsigned int cpu_record_hash_key(const struct cpu_thread_history *a) static unsigned int cpu_record_hash_key(const struct cpu_thread_history *a)
{ {

View File

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

View File

@ -202,9 +202,9 @@
#endif /* HAVE_GLIBC_BACKTRACE */ #endif /* HAVE_GLIBC_BACKTRACE */
/* Local includes: */ /* Local includes: */
#if !(defined(__GNUC__) || defined(VTYSH_EXTRACT_PL)) #if !defined(__GNUC__)
#define __attribute__(x) #define __attribute__(x)
#endif /* !__GNUC__ || VTYSH_EXTRACT_PL */ #endif /* !__GNUC__ */
#include <assert.h> #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); return reconf_dst(cfg, vty);
} }
#ifndef VTYSH_EXTRACT_PL
#include "lib/zlog_5424_cli_clippy.c" #include "lib/zlog_5424_cli_clippy.c"
#endif
DEFPY_NOSH(log_5424_target, DEFPY_NOSH(log_5424_target,
log_5424_target_cmd, log_5424_target_cmd,

View File

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

View File

@ -49,9 +49,7 @@
#include "ospf6d.h" #include "ospf6d.h"
#include "lib/json.h" #include "lib/json.h"
#include "ospf6_nssa.h" #include "ospf6_nssa.h"
#ifndef VTYSH_EXTRACT_PL
#include "ospf6d/ospf6_area_clippy.c" #include "ospf6d/ospf6_area_clippy.c"
#endif
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_AREA, "OSPF6 area"); DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_AREA, "OSPF6 area");
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_PLISTNAME, "Prefix list name"); 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, static void ospf6_asbr_redistribute_unset(struct ospf6 *ospf6,
struct ospf6_redist *red, int type); struct ospf6_redist *red, int type);
#ifndef VTYSH_EXTRACT_PL
#include "ospf6d/ospf6_asbr_clippy.c" #include "ospf6d/ospf6_asbr_clippy.c"
#endif
unsigned char conf_debug_ospf6_asbr = 0; unsigned char conf_debug_ospf6_asbr = 0;
@ -2206,10 +2204,10 @@ static const struct route_map_rule_cmd ospf6_routemap_rule_set_tag_cmd = {
/* add "set metric-type" */ /* add "set metric-type" */
DEFUN_YANG (ospf6_routemap_set_metric_type, ospf6_routemap_set_metric_type_cmd, DEFUN_YANG (ospf6_routemap_set_metric_type, ospf6_routemap_set_metric_type_cmd,
"set metric-type <type-1|type-2>", "set metric-type <type-1|type-2>",
"Set value\n" SET_STR
"Type of metric\n" "Type of metric for destination routing protocol\n"
"OSPF6 external type 1 metric\n" "OSPF[6] external type 1 metric\n"
"OSPF6 external type 2 metric\n") "OSPF[6] external type 2 metric\n")
{ {
char *ext = argv[2]->text; char *ext = argv[2]->text;
@ -2228,10 +2226,10 @@ DEFUN_YANG (ospf6_routemap_set_metric_type, ospf6_routemap_set_metric_type_cmd,
DEFUN_YANG (ospf6_routemap_no_set_metric_type, ospf6_routemap_no_set_metric_type_cmd, DEFUN_YANG (ospf6_routemap_no_set_metric_type, ospf6_routemap_no_set_metric_type_cmd,
"no set metric-type [<type-1|type-2>]", "no set metric-type [<type-1|type-2>]",
NO_STR NO_STR
"Set value\n" SET_STR
"Type of metric\n" "Type of metric for destination routing protocol\n"
"OSPF6 external type 1 metric\n" "OSPF[6] external type 1 metric\n"
"OSPF6 external type 2 metric\n") "OSPF[6] external type 2 metric\n")
{ {
const char *xpath = const char *xpath =
"./set-action[action='frr-ospf-route-map:metric-type']"; "./set-action[action='frr-ospf-route-map:metric-type']";

View File

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

View File

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

View File

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

View File

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

View File

@ -37,9 +37,7 @@
#include "ospf6_interface.h" #include "ospf6_interface.h"
#include "ospf6d.h" #include "ospf6d.h"
#include "ospf6_zebra.h" #include "ospf6_zebra.h"
#ifndef VTYSH_EXTRACT_PL
#include "ospf6d/ospf6_route_clippy.c" #include "ospf6d/ospf6_route_clippy.c"
#endif
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_ROUTE, "OSPF6 route"); DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_ROUTE, "OSPF6 route");
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_ROUTE_TABLE, "OSPF6 route table"); 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 }, { .val_bool = false },
); );
#ifndef VTYSH_EXTRACT_PL
#include "ospf6d/ospf6_top_clippy.c" #include "ospf6d/ospf6_top_clippy.c"
#endif
/* global ospf6d variable */ /* global ospf6d variable */
static struct ospf6_master ospf6_master; static struct ospf6_master ospf6_master;

View File

@ -5,27 +5,6 @@
if OSPF6D if OSPF6D
noinst_LIBRARIES += ospf6d/libospf6.a noinst_LIBRARIES += ospf6d/libospf6.a
sbin_PROGRAMS += ospf6d/ospf6d 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 vtysh_daemons += ospf6d
if SNMP if SNMP
module_LTLIBRARIES += ospf6d/ospf6d_snmp.la module_LTLIBRARIES += ospf6d/ospf6d_snmp.la

View File

@ -42,9 +42,7 @@
#include "ospfd/ospf_dump.h" #include "ospfd/ospf_dump.h"
#include "ospfd/ospf_packet.h" #include "ospfd/ospf_packet.h"
#include "ospfd/ospf_network.h" #include "ospfd/ospf_network.h"
#ifndef VTYSH_EXTRACT_PL
#include "ospfd/ospf_dump_clippy.c" #include "ospfd/ospf_dump_clippy.c"
#endif
/* Configuration debug option variables. */ /* Configuration debug option variables. */
unsigned long conf_debug_ospf_packet[5] = {0, 0, 0, 0, 0}; 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_gr.h"
#include "ospfd/ospf_errors.h" #include "ospfd/ospf_errors.h"
#include "ospfd/ospf_dump.h" #include "ospfd/ospf_dump.h"
#ifndef VTYSH_EXTRACT_PL
#include "ospfd/ospf_gr_clippy.c" #include "ospfd/ospf_gr_clippy.c"
#endif
static void ospf_gr_nvm_delete(struct ospf *ospf); 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. * LDP-SYNC commands.
*/ */
#ifndef VTYSH_EXTRACT_PL
#include "ospfd/ospf_ldp_sync_clippy.c" #include "ospfd/ospf_ldp_sync_clippy.c"
#endif
DEFPY (ospf_mpls_ldp_sync, DEFPY (ospf_mpls_ldp_sync,
ospf_mpls_ldp_sync_cmd, 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" #include "ospfd/ospf_vty_clippy.c"
#endif
DEFUN_NOSH (router_ospf, DEFUN_NOSH (router_ospf,
router_ospf_cmd, router_ospf_cmd,

View File

@ -5,18 +5,6 @@
if OSPFD if OSPFD
noinst_LIBRARIES += ospfd/libfrrospf.a noinst_LIBRARIES += ospfd/libfrrospf.a
sbin_PROGRAMS += ospfd/ospfd 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 vtysh_daemons += ospfd
if SNMP if SNMP
module_LTLIBRARIES += ospfd/ospfd_snmp.la module_LTLIBRARIES += ospfd/ospfd_snmp.la

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -6,11 +6,6 @@ if PIMD
sbin_PROGRAMS += pimd/pimd sbin_PROGRAMS += pimd/pimd
bin_PROGRAMS += pimd/mtracebis bin_PROGRAMS += pimd/mtracebis
noinst_PROGRAMS += pimd/test_igmpv3_join 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 += pimd
vtysh_daemons += pim6d vtysh_daemons += pim6d
man8 += $(MANBUILD)/frr-pimd.8 man8 += $(MANBUILD)/frr-pimd.8

View File

@ -321,15 +321,31 @@ extra_info = {
"lsp_processq_complete", "lsp_processq_complete",
], ],
# zebra - main WQ # zebra - main WQ
("mq_add_handler", "work_queue_add"): ["meta_queue_process",], ("mq_add_handler", "work_queue_add"): [
("meta_queue_process", "work_queue_add"): ["meta_queue_process",], "meta_queue_process",
],
("meta_queue_process", "work_queue_add"): [
"meta_queue_process",
],
# bgpd - label pool WQ # bgpd - label pool WQ
("bgp_lp_get", "work_queue_add"): ["lp_cbq_docallback",], ("bgp_lp_get", "work_queue_add"): [
("bgp_lp_event_chunk", "work_queue_add"): ["lp_cbq_docallback",], "lp_cbq_docallback",
("bgp_lp_event_zebra_up", "work_queue_add"): ["lp_cbq_docallback",], ],
("bgp_lp_event_chunk", "work_queue_add"): [
"lp_cbq_docallback",
],
("bgp_lp_event_zebra_up", "work_queue_add"): [
"lp_cbq_docallback",
],
# bgpd - main WQ # bgpd - main WQ
("bgp_process", "work_queue_add"): ["bgp_process_wq", "bgp_processq_del",], ("bgp_process", "work_queue_add"): [
("bgp_add_eoiu_mark", "work_queue_add"): ["bgp_process_wq", "bgp_processq_del",], "bgp_process_wq",
"bgp_processq_del",
],
("bgp_add_eoiu_mark", "work_queue_add"): [
"bgp_process_wq",
"bgp_processq_del",
],
# clear node WQ # clear node WQ
("bgp_clear_route_table", "work_queue_add"): [ ("bgp_clear_route_table", "work_queue_add"): [
"bgp_clear_route_node", "bgp_clear_route_node",
@ -337,7 +353,9 @@ extra_info = {
"bgp_clear_node_complete", "bgp_clear_node_complete",
], ],
# rfapi WQs # rfapi WQs
("rfapi_close", "work_queue_add"): ["rfapi_deferred_close_workfunc",], ("rfapi_close", "work_queue_add"): [
"rfapi_deferred_close_workfunc",
],
("rfapiRibUpdatePendingNode", "work_queue_add"): [ ("rfapiRibUpdatePendingNode", "work_queue_add"): [
"rfapiRibDoQueuedCallback", "rfapiRibDoQueuedCallback",
"rfapiRibQueueItemDelete", "rfapiRibQueueItemDelete",

View File

@ -36,7 +36,10 @@ from _clippy import (
) )
frr_top_src = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) frr_top_src = os.path.dirname(
os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
)
def graph_iterate(graph): def graph_iterate(graph):
"""iterator yielding all nodes of a graph """iterator yielding all nodes of a graph

View File

@ -16,7 +16,7 @@
# with this program; see the file COPYING; if not, write to the Free Software # 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 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
''' """
Wrapping layer and additional utility around _clippy.ELFFile. Wrapping layer and additional utility around _clippy.ELFFile.
Essentially, the C bits have the low-level ELF access bits that should be Essentially, the C bits have the low-level ELF access bits that should be
@ -28,7 +28,7 @@ across architecture, word size and even endianness boundaries. Both the C
module (through GElf_*) and this code (cf. struct.unpack format mangling module (through GElf_*) and this code (cf. struct.unpack format mangling
in ELFDissectStruct) will take appropriate measures to flip and resize in ELFDissectStruct) will take appropriate measures to flip and resize
fields as needed. fields as needed.
''' """
import struct import struct
from collections import OrderedDict from collections import OrderedDict
@ -40,16 +40,18 @@ from _clippy import ELFFile, ELFAccessError
# data access # data access
# #
class ELFNull(object): class ELFNull(object):
''' """
NULL pointer, returned instead of ELFData NULL pointer, returned instead of ELFData
''' """
def __init__(self): def __init__(self):
self.symname = None self.symname = None
self._dstsect = None self._dstsect = None
def __repr__(self): def __repr__(self):
return '<ptr: NULL>' return "<ptr: NULL>"
def __hash__(self): def __hash__(self):
return hash(None) return hash(None)
@ -57,33 +59,37 @@ class ELFNull(object):
def get_string(self): def get_string(self):
return None return None
class ELFUnresolved(object): class ELFUnresolved(object):
''' """
Reference to an unresolved external symbol, returned instead of ELFData Reference to an unresolved external symbol, returned instead of ELFData
:param symname: name of the referenced symbol :param symname: name of the referenced symbol
:param addend: offset added to the symbol, normally zero :param addend: offset added to the symbol, normally zero
''' """
def __init__(self, symname, addend): def __init__(self, symname, addend):
self.addend = addend self.addend = addend
self.symname = symname self.symname = symname
self._dstsect = None self._dstsect = None
def __repr__(self): def __repr__(self):
return '<unresolved: %s+%d>' % (self.symname, self.addend) return "<unresolved: %s+%d>" % (self.symname, self.addend)
def __hash__(self): def __hash__(self):
return hash((self.symname, self.addend)) return hash((self.symname, self.addend))
class ELFData(object): class ELFData(object):
''' """
Actual data somewhere in the ELF file. Actual data somewhere in the ELF file.
:type dstsect: ELFSubset :type dstsect: ELFSubset
:param dstsect: container data area (section or entire file) :param dstsect: container data area (section or entire file)
:param dstoffs: byte offset into dstsect :param dstoffs: byte offset into dstsect
:param dstlen: byte size of object, or None if unknown, open-ended or string :param dstlen: byte size of object, or None if unknown, open-ended or string
''' """
def __init__(self, dstsect, dstoffs, dstlen): def __init__(self, dstsect, dstoffs, dstlen):
self._dstsect = dstsect self._dstsect = dstsect
self._dstoffs = dstoffs self._dstoffs = dstoffs
@ -91,62 +97,78 @@ class ELFData(object):
self.symname = None self.symname = None
def __repr__(self): def __repr__(self):
return '<ptr: %s+0x%05x/%d>' % (self._dstsect.name, self._dstoffs, self._dstlen or -1) return "<ptr: %s+0x%05x/%d>" % (
self._dstsect.name,
self._dstoffs,
self._dstlen or -1,
)
def __hash__(self): def __hash__(self):
return hash((self._dstsect, self._dstoffs)) return hash((self._dstsect, self._dstoffs))
def get_string(self): def get_string(self):
''' """
Interpret as C string / null terminated UTF-8 and get the actual text. Interpret as C string / null terminated UTF-8 and get the actual text.
''' """
try: try:
return self._dstsect[self._dstoffs:str].decode('UTF-8') return self._dstsect[self._dstoffs : str].decode("UTF-8")
except: except:
import pdb; pdb.set_trace() import pdb
pdb.set_trace()
def get_data(self, reflen): def get_data(self, reflen):
''' """
Interpret as some structure (and check vs. expected length) Interpret as some structure (and check vs. expected length)
:param reflen: expected size of the object, compared against actual :param reflen: expected size of the object, compared against actual
size (which is only known in rare cases, mostly when directly size (which is only known in rare cases, mostly when directly
accessing a symbol since symbols have their destination object accessing a symbol since symbols have their destination object
size recorded) size recorded)
''' """
if self._dstlen is not None and self._dstlen != reflen: if self._dstlen is not None and self._dstlen != reflen:
raise ValueError('symbol size mismatch (got %d, expected %d)' % (self._dstlen, reflen)) raise ValueError(
return self._dstsect[self._dstoffs:self._dstoffs+reflen] "symbol size mismatch (got %d, expected %d)" % (self._dstlen, reflen)
)
return self._dstsect[self._dstoffs : self._dstoffs + reflen]
def offset(self, offs, within_symbol=False): def offset(self, offs, within_symbol=False):
''' """
Get another ELFData at an offset Get another ELFData at an offset
:param offs: byte offset, can be negative (e.g. in container_of) :param offs: byte offset, can be negative (e.g. in container_of)
:param within_symbol: retain length information :param within_symbol: retain length information
''' """
if self._dstlen is None or not within_symbol: if self._dstlen is None or not within_symbol:
return ELFData(self._dstsect, self._dstoffs + offs, None) return ELFData(self._dstsect, self._dstoffs + offs, None)
else: else:
return ELFData(self._dstsect, self._dstoffs + offs, self._dstlen - offs) return ELFData(self._dstsect, self._dstoffs + offs, self._dstlen - offs)
# #
# dissection data items # dissection data items
# #
class ELFDissectData(object): class ELFDissectData(object):
''' """
Common bits for ELFDissectStruct and ELFDissectUnion Common bits for ELFDissectStruct and ELFDissectUnion
''' """
def __init__(self):
self._data = None
self.elfclass = None
def __len__(self): def __len__(self):
''' """
Used for boolean evaluation, e.g. "if struct: ..." Used for boolean evaluation, e.g. "if struct: ..."
''' """
return not (isinstance(self._data, ELFNull) or isinstance(self._data, ELFUnresolved)) return not (
isinstance(self._data, ELFNull) or isinstance(self._data, ELFUnresolved)
)
def container_of(self, parent, fieldname): def container_of(self, parent, fieldname):
''' """
Assume this struct is embedded in a larger struct and get at the larger Assume this struct is embedded in a larger struct and get at the larger
Python ``self.container_of(a, b)`` = C ``container_of(self, a, b)`` Python ``self.container_of(a, b)`` = C ``container_of(self, a, b)``
@ -154,25 +176,26 @@ class ELFDissectData(object):
:param parent: class (not instance) of the larger struct :param parent: class (not instance) of the larger struct
:param fieldname: fieldname that refers back to this :param fieldname: fieldname that refers back to this
:returns: instance of parent, with fieldname set to this object :returns: instance of parent, with fieldname set to this object
''' """
offset = 0 offset = 0
if not hasattr(parent, '_efields'): if not hasattr(parent, "_efields"):
parent._setup_efields() parent._setup_efields()
for field in parent._efields[self.elfclass]: for field in parent._efields[self.elfclass]:
if field[0] == fieldname: if field[0] == fieldname:
break break
spec = field[1] spec = field[1]
if spec == 'P': if spec == "P":
spec = 'I' if self.elfclass == 32 else 'Q' spec = "I" if self.elfclass == 32 else "Q"
offset += struct.calcsize(spec) offset += struct.calcsize(spec)
else: else:
raise AttributeError('%r not found in %r.fields' % (fieldname, parent)) raise AttributeError("%r not found in %r.fields" % (fieldname, parent))
return parent(self._data.offset(-offset), replace={fieldname: self})
return parent(self._data.offset(-offset), replace = {fieldname: self})
class ELFDissectStruct(ELFDissectData): class ELFDissectStruct(ELFDissectData):
''' """
Decode and provide access to a struct somewhere in the ELF file Decode and provide access to a struct somewhere in the ELF file
Handles pointers and strings somewhat nicely. Create a subclass for each Handles pointers and strings somewhat nicely. Create a subclass for each
@ -205,30 +228,31 @@ class ELFDissectStruct(ELFDissectData):
.. attribute:: fieldrename .. attribute:: fieldrename
Dictionary to rename fields, useful if fields comes from tiabwarfo.py. Dictionary to rename fields, useful if fields comes from tiabwarfo.py.
''' """
class Pointer(object): class Pointer(object):
''' """
Quick wrapper for pointers to further structs Quick wrapper for pointers to further structs
This is just here to avoid going into infinite loops when loading This is just here to avoid going into infinite loops when loading
structs that have pointers to each other (e.g. struct xref <--> structs that have pointers to each other (e.g. struct xref <-->
struct xrefdata.) The pointer destination is only instantiated when struct xrefdata.) The pointer destination is only instantiated when
actually accessed. actually accessed.
''' """
def __init__(self, cls, ptr): def __init__(self, cls, ptr):
self.cls = cls self.cls = cls
self.ptr = ptr self.ptr = ptr
def __repr__(self): def __repr__(self):
return '<Pointer:%s %r>' % (self.cls.__name__, self.ptr) return "<Pointer:%s %r>" % (self.cls.__name__, self.ptr)
def __call__(self): def __call__(self):
if isinstance(self.ptr, ELFNull): if isinstance(self.ptr, ELFNull):
return None return None
return self.cls(self.ptr) return self.cls(self.ptr)
def __new__(cls, dataptr, parent = None, replace = None): def __new__(cls, dataptr, parent=None, replace=None):
if dataptr._dstsect is None: if dataptr._dstsect is None:
return super().__new__(cls) return super().__new__(cls)
@ -239,19 +263,19 @@ class ELFDissectStruct(ELFDissectData):
dataptr._dstsect._pointers[(cls, dataptr)] = obj dataptr._dstsect._pointers[(cls, dataptr)] = obj
return obj return obj
replacements = 'lLnN' replacements = "lLnN"
@classmethod @classmethod
def _preproc_structspec(cls, elfclass, spec): def _preproc_structspec(cls, elfclass, spec):
elfbits = elfclass elfbits = elfclass
if hasattr(spec, 'calcsize'): if hasattr(spec, "calcsize"):
spec = '%ds' % (spec.calcsize(elfclass),) spec = "%ds" % (spec.calcsize(elfclass),)
if elfbits == 32: if elfbits == 32:
repl = ['i', 'I'] repl = ["i", "I"]
else: else:
repl = ['q', 'Q'] repl = ["q", "Q"]
for c in cls.replacements: for c in cls.replacements:
spec = spec.replace(c, repl[int(c.isupper())]) spec = spec.replace(c, repl[int(c.isupper())])
return spec return spec
@ -269,8 +293,8 @@ class ELFDissectStruct(ELFDissectData):
size += struct.calcsize(newf[1]) size += struct.calcsize(newf[1])
cls._esize[elfclass] = size cls._esize[elfclass] = size
def __init__(self, dataptr, parent = None, replace = None): def __init__(self, dataptr, parent=None, replace=None):
if not hasattr(self.__class__, '_efields'): if not hasattr(self.__class__, "_efields"):
self._setup_efields() self._setup_efields()
self._fdata = None self._fdata = None
@ -290,12 +314,12 @@ class ELFDissectStruct(ELFDissectData):
# need to correlate output from struct.unpack with extra metadata # need to correlate output from struct.unpack with extra metadata
# about the particular fields, so note down byte offsets (in locs) # about the particular fields, so note down byte offsets (in locs)
# and tuple indices of pointers (in ptrs) # and tuple indices of pointers (in ptrs)
pspec = '' pspec = ""
locs = {} locs = {}
ptrs = set() ptrs = set()
for idx, spec in enumerate(pspecl): for idx, spec in enumerate(pspecl):
if spec == 'P': if spec == "P":
ptrs.add(idx) ptrs.add(idx)
spec = self._elfsect.ptrtype spec = self._elfsect.ptrtype
@ -326,7 +350,9 @@ class ELFDissectStruct(ELFDissectData):
self._fdata[name] = replace[name] self._fdata[name] = replace[name]
continue continue
if isinstance(self.fields[i][1], type) and issubclass(self.fields[i][1], ELFDissectData): if isinstance(self.fields[i][1], type) and issubclass(
self.fields[i][1], ELFDissectData
):
dataobj = self.fields[i][1](dataptr.offset(locs[i]), self) dataobj = self.fields[i][1](dataptr.offset(locs[i]), self)
self._fdata[name] = dataobj self._fdata[name] = dataobj
continue continue
@ -353,35 +379,41 @@ class ELFDissectStruct(ELFDissectData):
def __repr__(self): def __repr__(self):
if not isinstance(self._data, ELFData): if not isinstance(self._data, ELFData):
return '<%s: %r>' % (self.__class__.__name__, self._data) return "<%s: %r>" % (self.__class__.__name__, self._data)
return '<%s: %s>' % (self.__class__.__name__, return "<%s: %s>" % (
', '.join(['%s=%r' % t for t in self._fdata.items()])) self.__class__.__name__,
", ".join(["%s=%r" % t for t in self._fdata.items()]),
)
@classmethod @classmethod
def calcsize(cls, elfclass): def calcsize(cls, elfclass):
''' """
Sum up byte size of this struct Sum up byte size of this struct
Wraps struct.calcsize with some extra features. Wraps struct.calcsize with some extra features.
''' """
if not hasattr(cls, '_efields'): if not hasattr(cls, "_efields"):
cls._setup_efields() cls._setup_efields()
pspec = ''.join([f[1] for f in cls._efields[elfclass]]) pspec = "".join([f[1] for f in cls._efields[elfclass]])
ptrtype = 'I' if elfclass == 32 else 'Q' ptrtype = "I" if elfclass == 32 else "Q"
pspec = pspec.replace('P', ptrtype) pspec = pspec.replace("P", ptrtype)
return struct.calcsize(pspec) return struct.calcsize(pspec)
class ELFDissectUnion(ELFDissectData): class ELFDissectUnion(ELFDissectData):
''' """
Decode multiple structs in the same place. Decode multiple structs in the same place.
Not currently used (and hence not tested.) Worked at some point but not Not currently used (and hence not tested.) Worked at some point but not
needed anymore and may be borked now. Remove this comment when using. needed anymore and may be borked now. Remove this comment when using.
''' """
def __init__(self, dataptr, parent = None):
members = {}
def __init__(self, dataptr, parent=None):
self._dataptr = dataptr self._dataptr = dataptr
self._parent = parent self._parent = parent
self.members = [] self.members = []
@ -391,31 +423,44 @@ class ELFDissectUnion(ELFDissectData):
setattr(self, name, item) setattr(self, name, item)
def __repr__(self): def __repr__(self):
return '<%s: %s>' % (self.__class__.__name__, ', '.join([repr(i) for i in self.members])) return "<%s: %s>" % (
self.__class__.__name__,
", ".join([repr(i) for i in self.members]),
)
@classmethod @classmethod
def calcsize(cls, elfclass): def calcsize(cls, elfclass):
return max([member.calcsize(elfclass) for name, member in cls.members]) return max([member.calcsize(elfclass) for name, member in cls.members])
# #
# wrappers for spans of ELF data # wrappers for spans of ELF data
# #
class ELFSubset(object): class ELFSubset(object):
''' """
Common abstract base for section-level and file-level access. Common abstract base for section-level and file-level access.
''' """
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.name = None
self._obj = None
self._elffile = None
self.ptrtype = None
self.endian = None
self._pointers = WeakValueDictionary() self._pointers = WeakValueDictionary()
def _wrap_data(self, data, dstsect):
raise NotImplementedError()
def __hash__(self): def __hash__(self):
return hash(self.name) return hash(self.name)
def __getitem__(self, k): def __getitem__(self, k):
''' """
Read data from slice Read data from slice
Subscript **must** be a slice; a simple index will not return a byte Subscript **must** be a slice; a simple index will not return a byte
@ -425,22 +470,22 @@ class ELFSubset(object):
- `this[123:456]` - extract specific range - `this[123:456]` - extract specific range
- `this[123:str]` - extract until null byte. The slice stop value is - `this[123:str]` - extract until null byte. The slice stop value is
the `str` type (or, technically, `unicode`.) the `str` type (or, technically, `unicode`.)
''' """
return self._obj[k] return self._obj[k]
def getreloc(self, offset): def getreloc(self, offset):
''' """
Check for a relocation record at the specified offset. Check for a relocation record at the specified offset.
''' """
return self._obj.getreloc(offset) return self._obj.getreloc(offset)
def iter_data(self, scls, slice_ = slice(None)): def iter_data(self, scls, slice_=slice(None)):
''' """
Assume an array of structs present at a particular slice and decode Assume an array of structs present at a particular slice and decode
:param scls: ELFDissectData subclass for the struct :param scls: ELFDissectData subclass for the struct
:param slice_: optional range specification :param slice_: optional range specification
''' """
size = scls.calcsize(self._elffile.elfclass) size = scls.calcsize(self._elffile.elfclass)
offset = slice_.start or 0 offset = slice_.start or 0
@ -453,7 +498,7 @@ class ELFSubset(object):
offset += size offset += size
def pointer(self, offset): def pointer(self, offset):
''' """
Try to dereference a pointer value Try to dereference a pointer value
This checks whether there's a relocation at the given offset and This checks whether there's a relocation at the given offset and
@ -463,10 +508,12 @@ class ELFSubset(object):
:param offset: byte offset from beginning of section, :param offset: byte offset from beginning of section,
or virtual address in file or virtual address in file
:returns: ELFData wrapping pointed-to object :returns: ELFData wrapping pointed-to object
''' """
ptrsize = struct.calcsize(self.ptrtype) ptrsize = struct.calcsize(self.ptrtype)
data = struct.unpack(self.endian + self.ptrtype, self[offset:offset + ptrsize])[0] data = struct.unpack(
self.endian + self.ptrtype, self[offset : offset + ptrsize]
)[0]
reloc = self.getreloc(offset) reloc = self.getreloc(offset)
dstsect = None dstsect = None
@ -497,14 +544,15 @@ class ELFSubset(object):
# wrap_data is different between file & section # wrap_data is different between file & section
return self._wrap_data(data, dstsect) return self._wrap_data(data, dstsect)
class ELFDissectSection(ELFSubset): class ELFDissectSection(ELFSubset):
''' """
Access the contents of an ELF section like ``.text`` or ``.data`` Access the contents of an ELF section like ``.text`` or ``.data``
:param elfwrap: ELFDissectFile wrapper for the file :param elfwrap: ELFDissectFile wrapper for the file
:param idx: section index in section header table :param idx: section index in section header table
:param section: section object from C module :param section: section object from C module
''' """
def __init__(self, elfwrap, idx, section): def __init__(self, elfwrap, idx, section):
super().__init__() super().__init__()
@ -524,8 +572,9 @@ class ELFDissectSection(ELFSubset):
dstsect = self._elfwrap.get_section(dstsect.idx) dstsect = self._elfwrap.get_section(dstsect.idx)
return ELFData(dstsect, offs, None) return ELFData(dstsect, offs, None)
class ELFDissectFile(ELFSubset): class ELFDissectFile(ELFSubset):
''' """
Access the contents of an ELF file. Access the contents of an ELF file.
Note that offsets for array subscript and relocation/pointer access are Note that offsets for array subscript and relocation/pointer access are
@ -537,7 +586,7 @@ class ELFDissectFile(ELFSubset):
address like 0x400000 on x86. address like 0x400000 on x86.
:param filename: ELF file to open :param filename: ELF file to open
''' """
def __init__(self, filename): def __init__(self, filename):
super().__init__() super().__init__()
@ -546,8 +595,8 @@ class ELFDissectFile(ELFSubset):
self._elffile = self._obj = ELFFile(filename) self._elffile = self._obj = ELFFile(filename)
self._sections = {} self._sections = {}
self.ptrtype = 'I' if self._elffile.elfclass == 32 else 'Q' self.ptrtype = "I" if self._elffile.elfclass == 32 else "Q"
self.endian = '>' if self._elffile.bigendian else '<' self.endian = ">" if self._elffile.bigendian else "<"
@property @property
def _elfwrap(self): def _elfwrap(self):
@ -557,9 +606,9 @@ class ELFDissectFile(ELFSubset):
return ELFData(self, data, None) return ELFData(self, data, None)
def get_section(self, secname): def get_section(self, secname):
''' """
Look up section by name or index Look up section by name or index
''' """
if isinstance(secname, int): if isinstance(secname, int):
sh_idx = secname sh_idx = secname
section = self._elffile.get_section_idx(secname) section = self._elffile.get_section_idx(secname)

View File

@ -19,13 +19,14 @@
import struct import struct
from hashlib import sha256 from hashlib import sha256
def bititer(data, bits, startbit = True):
''' def bititer(data, bits, startbit=True):
"""
just iterate the individual bits out from a bytes object just iterate the individual bits out from a bytes object
if startbit is True, an '1' bit is inserted at the very beginning if startbit is True, an '1' bit is inserted at the very beginning
goes <bits> at a time, starts at LSB. goes <bits> at a time, starts at LSB.
''' """
bitavail, v = 0, 0 bitavail, v = 0, 0
if startbit and len(data) > 0: if startbit and len(data) > 0:
v = data.pop(0) v = data.pop(0)
@ -41,31 +42,33 @@ def bititer(data, bits, startbit = True):
bitavail -= bits bitavail -= bits
v >>= bits v >>= bits
def base32c(data): def base32c(data):
''' """
Crockford base32 with extra dashes Crockford base32 with extra dashes
''' """
chs = "0123456789ABCDEFGHJKMNPQRSTVWXYZ" chs = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"
o = '' o = ""
if type(data) == str: if type(data) == str:
data = [ord(v) for v in data] data = [ord(v) for v in data]
else: else:
data = list(data) data = list(data)
for i, bits in enumerate(bititer(data, 5)): for i, bits in enumerate(bititer(data, 5)):
if i == 5: if i == 5:
o = o + '-' o = o + "-"
elif i == 10: elif i == 10:
break break
o = o + chs[bits] o = o + chs[bits]
return o return o
def uidhash(filename, hashstr, hashu32a, hashu32b):
'''
xref Unique ID hash used in FRRouting
'''
filename = '/'.join(filename.rsplit('/')[-2:])
hdata = filename.encode('UTF-8') + hashstr.encode('UTF-8') def uidhash(filename, hashstr, hashu32a, hashu32b):
hdata += struct.pack('>II', hashu32a, hashu32b) """
xref Unique ID hash used in FRRouting
"""
filename = "/".join(filename.rsplit("/")[-2:])
hdata = filename.encode("UTF-8") + hashstr.encode("UTF-8")
hdata += struct.pack(">II", hashu32a, hashu32b)
i = sha256(hdata).digest() i = sha256(hdata).digest()
return base32c(i) return base32c(i)

View File

@ -161,7 +161,15 @@ for clippy_file in clippy_scan:
# combine daemon .xref files into frr.xref # combine daemon .xref files into frr.xref
out_lines.append("") out_lines.append("")
xref_targets = [ xref_targets = [
target for target in xref_targets if target not in ["tools/ssd", "vtysh/vtysh"] target
for target in xref_targets
if target
not in [
"bgpd/rfp-example/rfptest/rfptest",
"pimd/mtracebis",
"tools/ssd",
"vtysh/vtysh",
]
] ]
out_lines.append( out_lines.append(
"xrefs = %s" % (" ".join(["%s.xref" % target for target in xref_targets])) "xrefs = %s" % (" ".join(["%s.xref" % target for target in xref_targets]))

View File

@ -5,9 +5,11 @@ import os
try: try:
import _clippy import _clippy
except ImportError: except ImportError:
sys.stderr.write('''these tests need to be run with the _clippy C extension sys.stderr.write(
"""these tests need to be run with the _clippy C extension
module available. Try running "clippy runtests.py ...". module available. Try running "clippy runtests.py ...".
''') """
)
sys.exit(1) sys.exit(1)
os.chdir(os.path.dirname(os.path.abspath(__file__))) os.chdir(os.path.dirname(os.path.abspath(__file__)))

View File

@ -22,20 +22,21 @@ import pytest
from pprint import pprint from pprint import pprint
root = os.path.dirname(os.path.dirname(__file__)) root = os.path.dirname(os.path.dirname(__file__))
sys.path.append(os.path.join(root, 'python')) sys.path.append(os.path.join(root, "python"))
import xrelfo import xrelfo
from clippy import elf, uidhash from clippy import elf, uidhash
def test_uidhash(): def test_uidhash():
assert uidhash.uidhash("lib/test_xref.c", "logging call", 3, 0) \ assert uidhash.uidhash("lib/test_xref.c", "logging call", 3, 0) == "H7KJB-67TBH"
== 'H7KJB-67TBH'
def test_xrelfo_other(): def test_xrelfo_other():
for data in [ for data in [
elf.ELFNull(), elf.ELFNull(),
elf.ELFUnresolved('somesym', 0), elf.ELFUnresolved("somesym", 0),
]: ]:
dissect = xrelfo.XrefPtr(data) dissect = xrelfo.XrefPtr(data)
print(repr(dissect)) print(repr(dissect))
@ -43,9 +44,10 @@ def test_xrelfo_other():
with pytest.raises(AttributeError): with pytest.raises(AttributeError):
dissect.xref dissect.xref
def test_xrelfo_obj(): def test_xrelfo_obj():
xrelfo_ = xrelfo.Xrelfo() xrelfo_ = xrelfo.Xrelfo()
edf = xrelfo_.load_elf(os.path.join(root, 'lib/.libs/zclient.o'), 'zclient.lo') edf = xrelfo_.load_elf(os.path.join(root, "lib/.libs/zclient.o"), "zclient.lo")
xrefs = xrelfo_._xrefs xrefs = xrelfo_._xrefs
with pytest.raises(elf.ELFAccessError): with pytest.raises(elf.ELFAccessError):
@ -54,12 +56,13 @@ def test_xrelfo_obj():
pprint(xrefs[0]) pprint(xrefs[0])
pprint(xrefs[0]._data) pprint(xrefs[0]._data)
def test_xrelfo_bin(): def test_xrelfo_bin():
xrelfo_ = xrelfo.Xrelfo() xrelfo_ = xrelfo.Xrelfo()
edf = xrelfo_.load_elf(os.path.join(root, 'lib/.libs/libfrr.so'), 'libfrr.la') edf = xrelfo_.load_elf(os.path.join(root, "lib/.libs/libfrr.so"), "libfrr.la")
xrefs = xrelfo_._xrefs xrefs = xrelfo_._xrefs
assert edf[0:4] == b'\x7fELF' assert edf[0:4] == b"\x7fELF"
pprint(xrefs[0]) pprint(xrefs[0])
pprint(xrefs[0]._data) pprint(xrefs[0]._data)

View File

@ -23,10 +23,19 @@ import re
import argparse import argparse
import json import json
structs = ['xref', 'xref_logmsg', 'xref_threadsched', 'xref_install_element', 'xrefdata', 'xrefdata_logmsg', 'cmd_element'] structs = [
"xref",
"xref_logmsg",
"xref_threadsched",
"xref_install_element",
"xrefdata",
"xrefdata_logmsg",
"cmd_element",
]
def extract(filename='lib/.libs/libfrr.so'):
''' def extract(filename="lib/.libs/libfrr.so"):
"""
Convert output from "pahole" to JSON. Convert output from "pahole" to JSON.
Example pahole output: Example pahole output:
@ -41,26 +50,30 @@ def extract(filename='lib/.libs/libfrr.so'):
/* size: 32, cachelines: 1, members: 5 */ /* size: 32, cachelines: 1, members: 5 */
/* last cacheline: 32 bytes */ /* last cacheline: 32 bytes */
}; };
''' """
pahole = subprocess.check_output(['pahole', '-C', ','.join(structs), filename]).decode('UTF-8') pahole = subprocess.check_output(
["pahole", "-C", ",".join(structs), filename]
).decode("UTF-8")
struct_re = re.compile(r'^struct ([^ ]+) \{([^\}]+)};', flags=re.M | re.S) struct_re = re.compile(r"^struct ([^ ]+) \{([^\}]+)};", flags=re.M | re.S)
field_re = re.compile(r'^\s*(?P<type>[^;\(]+)\s+(?P<name>[^;\[\]]+)(?:\[(?P<array>\d+)\])?;\s*\/\*(?P<comment>.*)\*\/\s*$') field_re = re.compile(
comment_re = re.compile(r'^\s*\/\*.*\*\/\s*$') r"^\s*(?P<type>[^;\(]+)\s+(?P<name>[^;\[\]]+)(?:\[(?P<array>\d+)\])?;\s*\/\*(?P<comment>.*)\*\/\s*$"
)
comment_re = re.compile(r"^\s*\/\*.*\*\/\s*$")
pastructs = struct_re.findall(pahole) pastructs = struct_re.findall(pahole)
out = {} out = {}
for sname, data in pastructs: for sname, data in pastructs:
this = out.setdefault(sname, {}) this = out.setdefault(sname, {})
fields = this.setdefault('fields', []) fields = this.setdefault("fields", [])
lines = data.strip().splitlines() lines = data.strip().splitlines()
next_offs = 0 next_offs = 0
for line in lines: for line in lines:
if line.strip() == '': if line.strip() == "":
continue continue
m = comment_re.match(line) m = comment_re.match(line)
if m is not None: if m is not None:
@ -68,51 +81,55 @@ def extract(filename='lib/.libs/libfrr.so'):
m = field_re.match(line) m = field_re.match(line)
if m is not None: if m is not None:
offs, size = m.group('comment').strip().split() offs, size = m.group("comment").strip().split()
offs = int(offs) offs = int(offs)
size = int(size) size = int(size)
typ_ = m.group('type').strip() typ_ = m.group("type").strip()
name = m.group('name') name = m.group("name")
if name.startswith('(*'): if name.startswith("(*"):
# function pointer # function pointer
typ_ = typ_ + ' *' typ_ = typ_ + " *"
name = name[2:].split(')')[0] name = name[2:].split(")")[0]
data = { data = {
'name': name, "name": name,
'type': typ_, "type": typ_,
# 'offset': offs, # 'offset': offs,
# 'size': size, # 'size': size,
} }
if m.group('array'): if m.group("array"):
data['array'] = int(m.group('array')) data["array"] = int(m.group("array"))
fields.append(data) fields.append(data)
if offs != next_offs: if offs != next_offs:
raise ValueError('%d padding bytes before struct %s.%s' % (offs - next_offs, sname, name)) raise ValueError(
"%d padding bytes before struct %s.%s"
% (offs - next_offs, sname, name)
)
next_offs = offs + size next_offs = offs + size
continue continue
raise ValueError('cannot process line: %s' % line) raise ValueError("cannot process line: %s" % line)
return out return out
class FieldApplicator(object): class FieldApplicator(object):
''' """
Fill ELFDissectStruct fields list from pahole/JSON Fill ELFDissectStruct fields list from pahole/JSON
Uses the JSON file created by the above code to fill in the struct fields Uses the JSON file created by the above code to fill in the struct fields
in subclasses of ELFDissectStruct. in subclasses of ELFDissectStruct.
''' """
# only what we really need. add more as needed. # only what we really need. add more as needed.
packtypes = { packtypes = {
'int': 'i', "int": "i",
'uint8_t': 'B', "uint8_t": "B",
'uint16_t': 'H', "uint16_t": "H",
'uint32_t': 'I', "uint32_t": "I",
'char': 's', "char": "s",
} }
def __init__(self, data): def __init__(self, data):
@ -126,60 +143,65 @@ class FieldApplicator(object):
def resolve(self, cls): def resolve(self, cls):
out = [] out = []
#offset = 0 # offset = 0
fieldrename = getattr(cls, "fieldrename", {})
fieldrename = getattr(cls, 'fieldrename', {})
def mkname(n): def mkname(n):
return (fieldrename.get(n, n),) return (fieldrename.get(n, n),)
for field in self.data[cls.struct]['fields']: for field in self.data[cls.struct]["fields"]:
typs = field['type'].split() typs = field["type"].split()
typs = [i for i in typs if i not in ['const']] typs = [i for i in typs if i not in ["const"]]
# this will break reuse of xrefstructs.json across 32bit & 64bit # this will break reuse of xrefstructs.json across 32bit & 64bit
# platforms # platforms
#if field['offset'] != offset: # if field['offset'] != offset:
# assert offset < field['offset'] # assert offset < field['offset']
# out.append(('_pad', '%ds' % (field['offset'] - offset,))) # out.append(('_pad', '%ds' % (field['offset'] - offset,)))
# pretty hacky C types handling, but covers what we need # pretty hacky C types handling, but covers what we need
ptrlevel = 0 ptrlevel = 0
while typs[-1] == '*': while typs[-1] == "*":
typs.pop(-1) typs.pop(-1)
ptrlevel += 1 ptrlevel += 1
if ptrlevel > 0: if ptrlevel > 0:
packtype = ('P', None) packtype = ("P", None)
if ptrlevel == 1: if ptrlevel == 1:
if typs[0] == 'char': if typs[0] == "char":
packtype = ('P', str) packtype = ("P", str)
elif typs[0] == 'struct' and typs[1] in self.clsmap: elif typs[0] == "struct" and typs[1] in self.clsmap:
packtype = ('P', self.clsmap[typs[1]]) packtype = ("P", self.clsmap[typs[1]])
elif typs[0] == 'enum': elif typs[0] == "enum":
packtype = ('I',) packtype = ("I",)
elif typs[0] in self.packtypes: elif typs[0] in self.packtypes:
packtype = (self.packtypes[typs[0]],) packtype = (self.packtypes[typs[0]],)
elif typs[0] == 'struct': elif typs[0] == "struct":
if typs[1] in self.clsmap: if typs[1] in self.clsmap:
packtype = (self.clsmap[typs[1]],) packtype = (self.clsmap[typs[1]],)
else: else:
raise ValueError('embedded struct %s not in extracted data' % (typs[1],)) raise ValueError(
"embedded struct %s not in extracted data" % (typs[1],)
)
else: else:
raise ValueError('cannot decode field %s in struct %s (%s)' % ( raise ValueError(
cls.struct, field['name'], field['type'])) "cannot decode field %s in struct %s (%s)"
% (cls.struct, field["name"], field["type"])
)
if 'array' in field and typs[0] == 'char': if "array" in field and typs[0] == "char":
packtype = ('%ds' % field['array'],) packtype = ("%ds" % field["array"],)
out.append(mkname(field['name']) + packtype) out.append(mkname(field["name"]) + packtype)
elif 'array' in field: elif "array" in field:
for i in range(0, field['array']): for i in range(0, field["array"]):
out.append(mkname('%s_%d' % (field['name'], i)) + packtype) out.append(mkname("%s_%d" % (field["name"], i)) + packtype)
else: else:
out.append(mkname(field['name']) + packtype) out.append(mkname(field["name"]) + packtype)
#offset = field['offset'] + field['size'] # offset = field['offset'] + field['size']
cls.fields = out cls.fields = out
@ -187,16 +209,30 @@ class FieldApplicator(object):
for cls in self.classes: for cls in self.classes:
self.resolve(cls) self.resolve(cls)
def main(): def main():
argp = argparse.ArgumentParser(description = 'FRR DWARF structure extractor') argp = argparse.ArgumentParser(description="FRR DWARF structure extractor")
argp.add_argument('-o', dest='output', type=str, help='write JSON output', default='python/xrefstructs.json') argp.add_argument(
argp.add_argument('-i', dest='input', type=str, help='ELF file to read', default='lib/.libs/libfrr.so') "-o",
dest="output",
type=str,
help="write JSON output",
default="python/xrefstructs.json",
)
argp.add_argument(
"-i",
dest="input",
type=str,
help="ELF file to read",
default="lib/.libs/libfrr.so",
)
args = argp.parse_args() args = argp.parse_args()
out = extract(args.input) out = extract(args.input)
with open(args.output + '.tmp', 'w') as fd: with open(args.output + ".tmp", "w") as fd:
json.dump(out, fd, indent=2, sort_keys=True) json.dump(out, fd, indent=2, sort_keys=True)
os.rename(args.output + '.tmp', args.output) os.rename(args.output + ".tmp", args.output)
if __name__ == '__main__':
if __name__ == "__main__":
main() main()

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)))

389
python/xref2vtysh.py Normal file
View File

@ -0,0 +1,389 @@
# 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 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 "<CommandEntry %s: %r>" % (self.name, self.cmd)
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(
"command definition mismatch, first definied as:\n%r" % (self.cmd,),
nodename=nodename,
)
other.warn_loc("later defined as:\n%r" % (other.cmd,), nodename=nodename)
if self._spec["doc"] != other._spec["doc"]:
self.warn_loc(
"help string mismatch, first defined here (-)", nodename=nodename
)
other.warn_loc(
"later defined here (+)\nnote: both commands define %r in same node (%s)"
% (self.cmd, 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(
"hidden flag mismatch, first %r here" % (self.hidden,),
nodename=nodename,
)
other.warn_loc(
"later %r here (+)\nnote: both commands define %r in same node (%s)"
% (other.hidden, self.cmd, 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 """
%s (%s, %s_vtysh,
\t"%s",
%s)
""" % (
defsh,
daemon_str,
self.name,
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("\tinstall_element(%s, &%s_vtysh);\n" % (name, item.name))
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,15 +37,18 @@ from clippy.uidhash import uidhash
from clippy.elf import * from clippy.elf import *
from clippy import frr_top_src, CmdAttr from clippy import frr_top_src, CmdAttr
from tiabwarfo import FieldApplicator from tiabwarfo import FieldApplicator
from xref2vtysh import CommandEntry
try: try:
with open(os.path.join(frr_top_src, 'python', 'xrefstructs.json'), 'r') as fd: with open(os.path.join(frr_top_src, "python", "xrefstructs.json"), "r") as fd:
xrefstructs = json.load(fd) xrefstructs = json.load(fd)
except FileNotFoundError: except FileNotFoundError:
sys.stderr.write(''' sys.stderr.write(
"""
The "xrefstructs.json" file (created by running tiabwarfo.py with the pahole The "xrefstructs.json" file (created by running tiabwarfo.py with the pahole
tool available) could not be found. It should be included with the sources. tool available) could not be found. It should be included with the sources.
''') """
)
sys.exit(1) sys.exit(1)
# constants, need to be kept in sync manually... # constants, need to be kept in sync manually...
@ -57,7 +60,7 @@ XREFT_INSTALL_ELEMENT = 0x301
# LOG_* # LOG_*
priovals = {} priovals = {}
prios = ['0', '1', '2', 'E', 'W', 'N', 'I', 'D'] prios = ["0", "1", "2", "E", "W", "N", "I", "D"]
class XrelfoJson(object): class XrelfoJson(object):
@ -70,9 +73,10 @@ class XrelfoJson(object):
def to_dict(self, refs): def to_dict(self, refs):
pass pass
class Xref(ELFDissectStruct, XrelfoJson): class Xref(ELFDissectStruct, XrelfoJson):
struct = 'xref' struct = "xref"
fieldrename = {'type': 'typ'} fieldrename = {"type": "typ"}
containers = {} containers = {}
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@ -85,7 +89,7 @@ class Xref(ELFDissectStruct, XrelfoJson):
def container(self): def container(self):
if self._container is None: if self._container is None:
if self.typ in self.containers: if self.typ in self.containers:
self._container = self.container_of(self.containers[self.typ], 'xref') self._container = self.container_of(self.containers[self.typ], "xref")
return self._container return self._container
def check(self, *args, **kwargs): def check(self, *args, **kwargs):
@ -94,10 +98,10 @@ class Xref(ELFDissectStruct, XrelfoJson):
class Xrefdata(ELFDissectStruct): class Xrefdata(ELFDissectStruct):
struct = 'xrefdata' struct = "xrefdata"
# uid is all zeroes in the data loaded from ELF # uid is all zeroes in the data loaded from ELF
fieldrename = {'uid': '_uid'} fieldrename = {"uid": "_uid"}
def ref_from(self, xref, typ): def ref_from(self, xref, typ):
self.xref = xref self.xref = xref
@ -108,38 +112,84 @@ class Xrefdata(ELFDissectStruct):
return None return None
return uidhash(self.xref.file, self.hashstr, self.hashu32_0, self.hashu32_1) return uidhash(self.xref.file, self.hashstr, self.hashu32_0, self.hashu32_1)
class XrefPtr(ELFDissectStruct): class XrefPtr(ELFDissectStruct):
fields = [ fields = [
('xref', 'P', Xref), ("xref", "P", Xref),
] ]
class XrefThreadSched(ELFDissectStruct, XrelfoJson): class XrefThreadSched(ELFDissectStruct, XrelfoJson):
struct = 'xref_threadsched' struct = "xref_threadsched"
Xref.containers[XREFT_THREADSCHED] = XrefThreadSched Xref.containers[XREFT_THREADSCHED] = XrefThreadSched
class XrefLogmsg(ELFDissectStruct, XrelfoJson): class XrefLogmsg(ELFDissectStruct, XrelfoJson):
struct = 'xref_logmsg' struct = "xref_logmsg"
def _warn_fmt(self, text): def _warn_fmt(self, text):
lines = text.split('\n') lines = text.split("\n")
yield ((self.xref.file, self.xref.line), '%s:%d: %s (in %s())%s\n' % (self.xref.file, self.xref.line, lines[0], self.xref.func, ''.join(['\n' + l for l in lines[1:]]))) yield (
(self.xref.file, self.xref.line),
"%s:%d: %s (in %s())%s\n"
% (
self.xref.file,
self.xref.line,
lines[0],
self.xref.func,
"".join(["\n" + l for l in lines[1:]]),
),
)
fmt_regexes = [ fmt_regexes = [
(re.compile(r'([\n\t]+)'), 'error: log message contains tab or newline'), (re.compile(r"([\n\t]+)"), "error: log message contains tab or newline"),
# (re.compile(r'^(\s+)'), 'warning: log message starts with whitespace'), # (re.compile(r'^(\s+)'), 'warning: log message starts with whitespace'),
(re.compile(r'^((?:warn(?:ing)?|error):\s*)', re.I), 'warning: log message starts with severity'), (
re.compile(r"^((?:warn(?:ing)?|error):\s*)", re.I),
"warning: log message starts with severity",
),
] ]
arg_regexes = [ arg_regexes = [
# the (?<![\?:] ) avoids warning for x ? inet_ntop(...) : "(bla)" # the (?<![\?:] ) avoids warning for x ? inet_ntop(...) : "(bla)"
(re.compile(r'((?<![\?:] )inet_ntop\s*\(\s*(?:[AP]F_INET|2)\s*,)'), 'cleanup: replace inet_ntop(AF_INET, ...) with %pI4', lambda s: True), (
(re.compile(r'((?<![\?:] )inet_ntop\s*\(\s*(?:[AP]F_INET6|10)\s*,)'), 'cleanup: replace inet_ntop(AF_INET6, ...) with %pI6', lambda s: True), re.compile(r"((?<![\?:] )inet_ntop\s*\(\s*(?:[AP]F_INET|2)\s*,)"),
(re.compile(r'((?<![\?:] )inet_ntoa)'), 'cleanup: replace inet_ntoa(...) with %pI4', lambda s: True), "cleanup: replace inet_ntop(AF_INET, ...) with %pI4",
(re.compile(r'((?<![\?:] )ipaddr2str)'), 'cleanup: replace ipaddr2str(...) with %pIA', lambda s: True), lambda s: True,
(re.compile(r'((?<![\?:] )prefix2str)'), 'cleanup: replace prefix2str(...) with %pFX', lambda s: True), ),
(re.compile(r'((?<![\?:] )prefix_mac2str)'), 'cleanup: replace prefix_mac2str(...) with %pEA', lambda s: True), (
(re.compile(r'((?<![\?:] )sockunion2str)'), 'cleanup: replace sockunion2str(...) with %pSU', lambda s: True), re.compile(r"((?<![\?:] )inet_ntop\s*\(\s*(?:[AP]F_INET6|10)\s*,)"),
"cleanup: replace inet_ntop(AF_INET6, ...) with %pI6",
# (re.compile(r'^(\s*__(?:func|FUNCTION|PRETTY_FUNCTION)__\s*)'), 'error: debug message starts with __func__', lambda s: (s.priority & 7 == 7) ), lambda s: True,
),
(
# string split-up here is to not trigger "inet_ntoa forbidden"
re.compile(r"((?<![\?:] )inet_" + r"ntoa)"),
"cleanup: replace inet_" + "ntoa(...) with %pI4",
lambda s: True,
),
(
re.compile(r"((?<![\?:] )ipaddr2str)"),
"cleanup: replace ipaddr2str(...) with %pIA",
lambda s: True,
),
(
re.compile(r"((?<![\?:] )prefix2str)"),
"cleanup: replace prefix2str(...) with %pFX",
lambda s: True,
),
(
re.compile(r"((?<![\?:] )prefix_mac2str)"),
"cleanup: replace prefix_mac2str(...) with %pEA",
lambda s: True,
),
(
re.compile(r"((?<![\?:] )sockunion2str)"),
"cleanup: replace sockunion2str(...) with %pSU",
lambda s: True,
),
# (re.compile(r'^(\s*__(?:func|FUNCTION|PRETTY_FUNCTION)__\s*)'), 'error: debug message starts with __func__', lambda s: (s.priority & 7 == 7) ),
] ]
def check(self, wopt): def check(self, wopt):
@ -149,11 +199,11 @@ class XrefLogmsg(ELFDissectStruct, XrelfoJson):
out = [] out = []
for i, text in enumerate(items): for i, text in enumerate(items):
if (i % 2) == 1: if (i % 2) == 1:
out.append('\033[41;37;1m%s\033[m' % repr(text)[1:-1]) out.append("\033[41;37;1m%s\033[m" % repr(text)[1:-1])
else: else:
out.append(repr(text)[1:-1]) out.append(repr(text)[1:-1])
excerpt = ''.join(out) excerpt = "".join(out)
else: else:
excerpt = repr(itext)[1:-1] excerpt = repr(itext)[1:-1]
return excerpt return excerpt
@ -174,70 +224,99 @@ class XrefLogmsg(ELFDissectStruct, XrelfoJson):
continue continue
excerpt = fmt_msg(rex, self.args) excerpt = fmt_msg(rex, self.args)
yield from self._warn_fmt('%s:\n\t"%s",\n\t%s' % (msg, repr(self.fmtstring)[1:-1], excerpt)) yield from self._warn_fmt(
'%s:\n\t"%s",\n\t%s' % (msg, repr(self.fmtstring)[1:-1], excerpt)
)
def dump(self): def dump(self):
print('%-60s %s%s %-25s [EC %d] %s' % ( print(
'%s:%d %s()' % (self.xref.file, self.xref.line, self.xref.func), "%-60s %s%s %-25s [EC %d] %s"
prios[self.priority & 7], % (
priovals.get(self.priority & 0x30, ' '), "%s:%d %s()" % (self.xref.file, self.xref.line, self.xref.func),
self.xref.xrefdata.uid, self.ec, self.fmtstring)) prios[self.priority & 7],
priovals.get(self.priority & 0x30, " "),
self.xref.xrefdata.uid,
self.ec,
self.fmtstring,
)
)
def to_dict(self, xrelfo): def to_dict(self, xrelfo):
jsobj = dict([(i, getattr(self.xref, i)) for i in ['file', 'line', 'func']]) jsobj = dict([(i, getattr(self.xref, i)) for i in ["file", "line", "func"]])
if self.ec != 0: if self.ec != 0:
jsobj['ec'] = self.ec jsobj["ec"] = self.ec
jsobj['fmtstring'] = self.fmtstring jsobj["fmtstring"] = self.fmtstring
jsobj['args'] = self.args jsobj["args"] = self.args
jsobj['priority'] = self.priority & 7 jsobj["priority"] = self.priority & 7
jsobj['type'] = 'logmsg' jsobj["type"] = "logmsg"
jsobj['binary'] = self._elfsect._elfwrap.orig_filename jsobj["binary"] = self._elfsect._elfwrap.orig_filename
if self.priority & 0x10: if self.priority & 0x10:
jsobj.setdefault('flags', []).append('errno') jsobj.setdefault("flags", []).append("errno")
if self.priority & 0x20: if self.priority & 0x20:
jsobj.setdefault('flags', []).append('getaddrinfo') jsobj.setdefault("flags", []).append("getaddrinfo")
xrelfo["refs"].setdefault(self.xref.xrefdata.uid, []).append(jsobj)
xrelfo['refs'].setdefault(self.xref.xrefdata.uid, []).append(jsobj)
Xref.containers[XREFT_LOGMSG] = XrefLogmsg Xref.containers[XREFT_LOGMSG] = XrefLogmsg
class CmdElement(ELFDissectStruct, XrelfoJson): class CmdElement(ELFDissectStruct, XrelfoJson):
struct = 'cmd_element' struct = "cmd_element"
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
def to_dict(self, xrelfo): def to_dict(self, xrelfo):
jsobj = xrelfo['cli'].setdefault(self.name, {}).setdefault(self._elfsect._elfwrap.orig_filename, {}) jsobj = (
xrelfo["cli"]
.setdefault(self.name, {})
.setdefault(self._elfsect._elfwrap.orig_filename, {})
)
jsobj.update({ jsobj.update(
'string': self.string, {
'doc': self.doc, "string": self.string,
}) "doc": self.doc,
}
)
if self.attr: if self.attr:
jsobj['attr'] = attr = self.attr jsobj["attr"] = attr = self.attr
for attrname in CmdAttr.__members__: for attrname in CmdAttr.__members__:
val = CmdAttr[attrname] val = CmdAttr[attrname]
if attr & val: if attr & val:
jsobj.setdefault('attrs', []).append(attrname.lower()) jsobj.setdefault("attrs", []).append(attrname.lower())
attr &= ~val attr &= ~val
jsobj['defun'] = dict([(i, getattr(self.xref, i)) for i in ['file', 'line', 'func']]) jsobj["defun"] = dict(
[(i, getattr(self.xref, i)) for i in ["file", "line", "func"]]
)
Xref.containers[XREFT_DEFUN] = CmdElement Xref.containers[XREFT_DEFUN] = CmdElement
class XrefInstallElement(ELFDissectStruct, XrelfoJson): class XrefInstallElement(ELFDissectStruct, XrelfoJson):
struct = 'xref_install_element' struct = "xref_install_element"
def to_dict(self, xrelfo): def to_dict(self, xrelfo):
jsobj = xrelfo['cli'].setdefault(self.cmd_element.name, {}).setdefault(self._elfsect._elfwrap.orig_filename, {}) jsobj = (
nodes = jsobj.setdefault('nodes', []) xrelfo["cli"]
.setdefault(self.cmd_element.name, {})
.setdefault(self._elfsect._elfwrap.orig_filename, {})
)
nodes = jsobj.setdefault("nodes", [])
nodes.append(
{
"node": self.node_type,
"install": dict(
[(i, getattr(self.xref, i)) for i in ["file", "line", "func"]]
),
}
)
nodes.append({
'node': self.node_type,
'install': dict([(i, getattr(self.xref, i)) for i in ['file', 'line', 'func']]),
})
Xref.containers[XREFT_INSTALL_ELEMENT] = XrefInstallElement Xref.containers[XREFT_INSTALL_ELEMENT] = XrefInstallElement
@ -254,86 +333,90 @@ fieldapply()
class Xrelfo(dict): class Xrelfo(dict):
def __init__(self): def __init__(self):
super().__init__({ super().__init__(
'refs': {}, {
'cli': {}, "refs": {},
}) "cli": {},
}
)
self._xrefs = [] self._xrefs = []
def load_file(self, filename): def load_file(self, filename):
orig_filename = filename orig_filename = filename
if filename.endswith('.la') or filename.endswith('.lo'): if filename.endswith(".la") or filename.endswith(".lo"):
with open(filename, 'r') as fd: with open(filename, "r") as fd:
for line in fd: for line in fd:
line = line.strip() line = line.strip()
if line.startswith('#') or line == '' or '=' not in line: if line.startswith("#") or line == "" or "=" not in line:
continue continue
var, val = line.split('=', 1) var, val = line.split("=", 1)
if var not in ['library_names', 'pic_object']: if var not in ["library_names", "pic_object"]:
continue continue
if val.startswith("'") or val.startswith('"'): if val.startswith("'") or val.startswith('"'):
val = val[1:-1] val = val[1:-1]
if var == 'pic_object': if var == "pic_object":
filename = os.path.join(os.path.dirname(filename), val) filename = os.path.join(os.path.dirname(filename), val)
break break
val = val.strip().split()[0] val = val.strip().split()[0]
filename = os.path.join(os.path.dirname(filename), '.libs', val) filename = os.path.join(os.path.dirname(filename), ".libs", val)
break break
else: else:
raise ValueError('could not process libtool file "%s"' % orig_filename) raise ValueError(
'could not process libtool file "%s"' % orig_filename
)
while True: while True:
with open(filename, 'rb') as fd: with open(filename, "rb") as fd:
hdr = fd.read(4) hdr = fd.read(4)
if hdr == b'\x7fELF': if hdr == b"\x7fELF":
self.load_elf(filename, orig_filename) self.load_elf(filename, orig_filename)
return return
if hdr[:2] == b'#!': if hdr[:2] == b"#!":
path, name = os.path.split(filename) path, name = os.path.split(filename)
filename = os.path.join(path, '.libs', name) filename = os.path.join(path, ".libs", name)
continue continue
if hdr[:1] == b'{': if hdr[:1] == b"{":
with open(filename, 'r') as fd: with open(filename, "r") as fd:
self.load_json(fd) self.load_json(fd)
return return
raise ValueError('cannot determine file type for %s' % (filename)) raise ValueError("cannot determine file type for %s" % (filename))
def load_elf(self, filename, orig_filename): def load_elf(self, filename, orig_filename):
edf = ELFDissectFile(filename) edf = ELFDissectFile(filename)
edf.orig_filename = orig_filename edf.orig_filename = orig_filename
note = edf._elffile.find_note('FRRouting', 'XREF') note = edf._elffile.find_note("FRRouting", "XREF")
if note is not None: if note is not None:
endian = '>' if edf._elffile.bigendian else '<' endian = ">" if edf._elffile.bigendian else "<"
mem = edf._elffile[note] mem = edf._elffile[note]
if edf._elffile.elfclass == 64: if edf._elffile.elfclass == 64:
start, end = struct.unpack(endian + 'QQ', mem) start, end = struct.unpack(endian + "QQ", mem)
start += note.start start += note.start
end += note.start + 8 end += note.start + 8
else: else:
start, end = struct.unpack(endian + 'II', mem) start, end = struct.unpack(endian + "II", mem)
start += note.start start += note.start
end += note.start + 4 end += note.start + 4
ptrs = edf.iter_data(XrefPtr, slice(start, end)) ptrs = edf.iter_data(XrefPtr, slice(start, end))
else: else:
xrefarray = edf.get_section('xref_array') xrefarray = edf.get_section("xref_array")
if xrefarray is None: if xrefarray is None:
raise ValueError('file has neither xref note nor xref_array section') raise ValueError("file has neither xref note nor xref_array section")
ptrs = xrefarray.iter_data(XrefPtr) ptrs = xrefarray.iter_data(XrefPtr)
for ptr in ptrs: for ptr in ptrs:
if ptr.xref is None: if ptr.xref is None:
print('NULL xref') print("NULL xref")
continue continue
self._xrefs.append(ptr.xref) self._xrefs.append(ptr.xref)
@ -346,15 +429,15 @@ class Xrelfo(dict):
def load_json(self, fd): def load_json(self, fd):
data = json.load(fd) data = json.load(fd)
for uid, items in data['refs'].items(): for uid, items in data["refs"].items():
myitems = self['refs'].setdefault(uid, []) myitems = self["refs"].setdefault(uid, [])
for item in items: for item in items:
if item in myitems: if item in myitems:
continue continue
myitems.append(item) myitems.append(item)
for cmd, items in data['cli'].items(): for cmd, items in data["cli"].items():
self['cli'].setdefault(cmd, {}).update(items) self["cli"].setdefault(cmd, {}).update(items)
return data return data
@ -362,23 +445,33 @@ class Xrelfo(dict):
for xref in self._xrefs: for xref in self._xrefs:
yield from xref.check(checks) yield from xref.check(checks)
def main(): def main():
argp = argparse.ArgumentParser(description = 'FRR xref ELF extractor') argp = argparse.ArgumentParser(description="FRR xref ELF extractor")
argp.add_argument('-o', dest='output', type=str, help='write JSON output') 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("--out-by-file", type=str, help="write by-file JSON output")
argp.add_argument('-Wlog-format', action='store_const', const=True) argp.add_argument("-c", dest="vtysh_cmds", type=str, help="write vtysh_cmd.c")
argp.add_argument('-Wlog-args', action='store_const', const=True) argp.add_argument("-Wlog-format", action="store_const", const=True)
argp.add_argument('-Werror', action='store_const', const=True) argp.add_argument("-Wlog-args", action="store_const", const=True)
argp.add_argument('--profile', action='store_const', const=True) argp.add_argument("-Werror", action="store_const", const=True)
argp.add_argument('binaries', metavar='BINARY', nargs='+', type=str, help='files to read (ELF files or libtool objects)') argp.add_argument("--profile", action="store_const", const=True)
argp.add_argument(
"binaries",
metavar="BINARY",
nargs="+",
type=str,
help="files to read (ELF files or libtool objects)",
)
args = argp.parse_args() args = argp.parse_args()
if args.profile: if args.profile:
import cProfile import cProfile
cProfile.runctx('_main(args)', globals(), {'args': args}, sort='cumtime')
cProfile.runctx("_main(args)", globals(), {"args": args}, sort="cumtime")
else: else:
_main(args) _main(args)
def _main(args): def _main(args):
errors = 0 errors = 0
xrelfo = Xrelfo() xrelfo = Xrelfo()
@ -388,52 +481,59 @@ def _main(args):
xrelfo.load_file(fn) xrelfo.load_file(fn)
except: except:
errors += 1 errors += 1
sys.stderr.write('while processing %s:\n' % (fn)) sys.stderr.write("while processing %s:\n" % (fn))
traceback.print_exc() traceback.print_exc()
for option in dir(args): for option in dir(args):
if option.startswith('W') and option != 'Werror': if option.startswith("W") and option != "Werror":
checks = sorted(xrelfo.check(args)) checks = sorted(xrelfo.check(args))
sys.stderr.write(''.join([c[-1] for c in checks])) sys.stderr.write("".join([c[-1] for c in checks]))
if args.Werror and len(checks) > 0: if args.Werror and len(checks) > 0:
errors += 1 errors += 1
break break
refs = xrelfo["refs"]
refs = xrelfo['refs']
counts = {} counts = {}
for k, v in refs.items(): for k, v in refs.items():
strs = set([i['fmtstring'] for i in v]) strs = set([i["fmtstring"] for i in v])
if len(strs) != 1: if len(strs) != 1:
print('\033[31;1m%s\033[m' % k) print("\033[31;1m%s\033[m" % k)
counts[k] = len(v) counts[k] = len(v)
out = xrelfo out = xrelfo
outbyfile = {} outbyfile = {}
for uid, locs in refs.items(): for uid, locs in refs.items():
for loc in locs: for loc in locs:
filearray = outbyfile.setdefault(loc['file'], []) filearray = outbyfile.setdefault(loc["file"], [])
loc = dict(loc) loc = dict(loc)
del loc['file'] del loc["file"]
filearray.append(loc) filearray.append(loc)
for k in outbyfile.keys(): for k in outbyfile.keys():
outbyfile[k] = sorted(outbyfile[k], key=lambda x: x['line']) outbyfile[k] = sorted(outbyfile[k], key=lambda x: x["line"])
if errors: if errors:
sys.exit(1) sys.exit(1)
if args.output: if args.output:
with open(args.output + '.tmp', 'w') as fd: with open(args.output + ".tmp", "w") as fd:
json.dump(out, fd, indent=2, sort_keys=True, **json_dump_args) json.dump(out, fd, indent=2, sort_keys=True, **json_dump_args)
os.rename(args.output + '.tmp', args.output) os.rename(args.output + ".tmp", args.output)
if args.out_by_file: if args.out_by_file:
with open(args.out_by_file + '.tmp', 'w') as fd: with open(args.out_by_file + ".tmp", "w") as fd:
json.dump(outbyfile, fd, indent=2, sort_keys=True, **json_dump_args) json.dump(outbyfile, fd, indent=2, sort_keys=True, **json_dump_args)
os.rename(args.out_by_file + '.tmp', args.out_by_file) os.rename(args.out_by_file + ".tmp", args.out_by_file)
if __name__ == '__main__': 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() main()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

4
vtysh/.gitignore vendored
View File

@ -1,4 +1,6 @@
vtysh vtysh
vtysh_cmd.c vtysh_cmd.c
extract.pl
vtysh_daemons.h 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 = \ nodist_vtysh_vtysh_SOURCES = \
vtysh/vtysh_cmd.c \ vtysh/vtysh_cmd.c \
# end # end
CLEANFILES += vtysh/vtysh_cmd.c
noinst_HEADERS += \ noinst_HEADERS += \
vtysh/vtysh.h \ vtysh/vtysh.h \
@ -39,23 +38,3 @@ $(vtysh_vtysh_OBJECTS): vtysh/vtysh_daemons.h
CLEANFILES += vtysh/vtysh_daemons.h CLEANFILES += vtysh/vtysh_daemons.h
vtysh/vtysh_daemons.h: vtysh/vtysh_daemons.h:
$(PERL) $(top_srcdir)/vtysh/daemons.pl $(vtysh_daemons) > 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 if WATCHFRR
sbin_PROGRAMS += watchfrr/watchfrr sbin_PROGRAMS += watchfrr/watchfrr
vtysh_scan += watchfrr/watchfrr_vty.c
man8 += $(MANBUILD)/frr-watchfrr.8 man8 += $(MANBUILD)/frr-watchfrr.8
endif endif

View File

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

View File

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

View File

@ -23,9 +23,7 @@
#include "lib/json.h" #include "lib/json.h"
#include "zebra/dpdk/zebra_dplane_dpdk.h" #include "zebra/dpdk/zebra_dplane_dpdk.h"
#ifndef VTYSH_EXTRACT_PL
#include "zebra/dpdk/zebra_dplane_dpdk_vty_clippy.c" #include "zebra/dpdk/zebra_dplane_dpdk_vty_clippy.c"
#endif
#define ZD_STR "Zebra dataplane information\n" #define ZD_STR "Zebra dataplane information\n"
#define ZD_DPDK_STR "DPDK offload 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 */ #endif /* HAVE_NET_RT_IFLIST */
} }
#ifndef VTYSH_EXTRACT_PL
#include "zebra/interface_clippy.c" #include "zebra/interface_clippy.c"
#endif
/* Show all interfaces to vty. */ /* Show all interfaces to vty. */
DEFPY(show_interface, show_interface_cmd, DEFPY(show_interface, show_interface_cmd,
"show interface vrf NAME$vrf_name [brief$brief] [json$uj]", "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) #if defined(HAVE_RTADV)
#ifndef VTYSH_EXTRACT_PL
#include "zebra/rtadv_clippy.c" #include "zebra/rtadv_clippy.c"
#endif
DEFINE_MTYPE_STATIC(ZEBRA, RTADV_PREFIX, "Router Advertisement Prefix"); DEFINE_MTYPE_STATIC(ZEBRA, RTADV_PREFIX, "Router Advertisement Prefix");
DEFINE_MTYPE_STATIC(ZEBRA, ADV_IF, "Advertised Interface"); DEFINE_MTYPE_STATIC(ZEBRA, ADV_IF, "Advertised Interface");

View File

@ -4,29 +4,6 @@
if ZEBRA if ZEBRA
sbin_PROGRAMS += zebra/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 vtysh_daemons += zebra
if IRDP 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_SOURCES = zebra/dplane_fpm_nl.c
zebra_dplane_fpm_nl_la_LDFLAGS = $(MODULE_LDFLAGS) zebra_dplane_fpm_nl_la_LDFLAGS = $(MODULE_LDFLAGS)
zebra_dplane_fpm_nl_la_LIBADD = zebra_dplane_fpm_nl_la_LIBADD =
vtysh_scan += zebra/dplane_fpm_nl.c
endif endif
if NETLINK_DEBUG if NETLINK_DEBUG

View File

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

View File

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

View File

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

Some files were not shown because too many files have changed in this diff Show More