mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-04-28 21:20:48 +00:00
Merge pull request #12196 from opensourcerouting/xref-vtysh
*: rewrite `extract.pl` using `xref` infra
This commit is contained in:
commit
d7cde18c63
@ -144,7 +144,6 @@ pkginclude_HEADERS =
|
||||
nodist_pkginclude_HEADERS =
|
||||
dist_yangmodels_DATA =
|
||||
man_MANS =
|
||||
vtysh_scan =
|
||||
vtysh_daemons =
|
||||
clippy_scan =
|
||||
|
||||
@ -226,6 +225,7 @@ EXTRA_DIST += \
|
||||
python/makefile.py \
|
||||
python/tiabwarfo.py \
|
||||
python/xrelfo.py \
|
||||
python/xref2vtysh.py \
|
||||
python/test_xrelfo.py \
|
||||
python/runtests.py \
|
||||
\
|
||||
|
@ -4,11 +4,6 @@
|
||||
|
||||
if BABELD
|
||||
sbin_PROGRAMS += babeld/babeld
|
||||
vtysh_scan += \
|
||||
babeld/babel_interface.c \
|
||||
babeld/babel_zebra.c \
|
||||
babeld/babeld.c \
|
||||
# end
|
||||
vtysh_daemons += babeld
|
||||
endif
|
||||
|
||||
|
@ -26,9 +26,7 @@
|
||||
#include "lib/log.h"
|
||||
#include "lib/northbound_cli.h"
|
||||
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "bfdd/bfdd_cli_clippy.c"
|
||||
#endif /* VTYSH_EXTRACT_PL */
|
||||
|
||||
#include "bfd.h"
|
||||
#include "bfdd_nb.h"
|
||||
|
@ -28,9 +28,7 @@
|
||||
|
||||
#include "bfd.h"
|
||||
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "bfdd/bfdd_vty_clippy.c"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Commands help string definitions.
|
||||
|
@ -5,8 +5,6 @@
|
||||
if BFDD
|
||||
noinst_LIBRARIES += bfdd/libbfd.a
|
||||
sbin_PROGRAMS += bfdd/bfdd
|
||||
vtysh_scan += bfdd/bfdd_vty.c
|
||||
vtysh_scan += bfdd/bfdd_cli.c
|
||||
vtysh_daemons += bfdd
|
||||
man8 += $(MANBUILD)/frr-bfdd.8
|
||||
endif
|
||||
|
@ -2034,9 +2034,7 @@ static const struct cmd_variable_handler bmp_targets_var_handlers[] = {
|
||||
|
||||
#define BMP_STR "BGP Monitoring Protocol\n"
|
||||
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "bgpd/bgp_bmp_clippy.c"
|
||||
#endif
|
||||
|
||||
DEFPY_NOSH(bmp_targets_main,
|
||||
bmp_targets_cmd,
|
||||
|
@ -1415,9 +1415,7 @@ DEFUN (no_debug_bgp_update_direct_peer,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "bgpd/bgp_debug_clippy.c"
|
||||
#endif
|
||||
|
||||
DEFPY (debug_bgp_update_prefix_afi_safi,
|
||||
debug_bgp_update_prefix_afi_safi_cmd,
|
||||
|
@ -3336,9 +3336,7 @@ static void write_vni_config(struct vty *vty, struct bgpevpn *vpn)
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "bgpd/bgp_evpn_vty_clippy.c"
|
||||
#endif
|
||||
|
||||
DEFPY(bgp_evpn_flood_control,
|
||||
bgp_evpn_flood_control_cmd,
|
||||
|
@ -39,9 +39,7 @@
|
||||
|
||||
#define BGP_LABELPOOL_ENABLE_TESTS 0
|
||||
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "bgpd/bgp_labelpool_clippy.c"
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
|
@ -90,9 +90,7 @@
|
||||
#include "bgpd/bgp_flowspec_util.h"
|
||||
#include "bgpd/bgp_pbr.h"
|
||||
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "bgpd/bgp_route_clippy.c"
|
||||
#endif
|
||||
|
||||
DEFINE_HOOK(bgp_snmp_update_stats,
|
||||
(struct bgp_node *rn, struct bgp_path_info *pi, bool added),
|
||||
|
@ -74,9 +74,7 @@
|
||||
#include "bgpd/rfapi/bgp_rfapi_cfg.h"
|
||||
#endif
|
||||
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "bgpd/bgp_routemap_clippy.c"
|
||||
#endif
|
||||
|
||||
/* Memo of route-map commands.
|
||||
|
||||
|
@ -52,16 +52,12 @@
|
||||
|
||||
#include "lib/network.h"
|
||||
#include "lib/thread.h"
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "rtrlib/rtrlib.h"
|
||||
#endif
|
||||
#include "hook.h"
|
||||
#include "libfrr.h"
|
||||
#include "lib/version.h"
|
||||
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "bgpd/bgp_rpki_clippy.c"
|
||||
#endif
|
||||
|
||||
DEFINE_MTYPE_STATIC(BGPD, BGP_RPKI_CACHE, "BGP RPKI Cache server");
|
||||
DEFINE_MTYPE_STATIC(BGPD, BGP_RPKI_CACHE_GROUP, "BGP RPKI Cache server group");
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "bgpd/bgp_vty_clippy.c"
|
||||
#endif
|
||||
|
||||
DEFUN_HIDDEN (bgp_local_mac,
|
||||
bgp_local_mac_cmd,
|
||||
|
@ -6,36 +6,9 @@ if BGPD
|
||||
noinst_LIBRARIES += bgpd/libbgp.a
|
||||
sbin_PROGRAMS += bgpd/bgpd
|
||||
noinst_PROGRAMS += bgpd/bgp_btoa
|
||||
vtysh_scan += \
|
||||
bgpd/bgp_bfd.c \
|
||||
bgpd/bgp_debug.c \
|
||||
bgpd/bgp_dump.c \
|
||||
bgpd/bgp_evpn_mh.c \
|
||||
bgpd/bgp_evpn_vty.c \
|
||||
bgpd/bgp_filter.c \
|
||||
bgpd/bgp_labelpool.c \
|
||||
bgpd/bgp_mplsvpn.c \
|
||||
bgpd/bgp_nexthop.c \
|
||||
bgpd/bgp_route.c \
|
||||
bgpd/bgp_routemap.c \
|
||||
bgpd/bgp_vty.c \
|
||||
bgpd/bgp_flowspec_vty.c \
|
||||
# end
|
||||
|
||||
# can be loaded as DSO - always include for vtysh
|
||||
vtysh_scan += bgpd/bgp_rpki.c
|
||||
vtysh_scan += bgpd/bgp_bmp.c
|
||||
|
||||
vtysh_daemons += bgpd
|
||||
|
||||
if ENABLE_BGP_VNC
|
||||
vtysh_scan += \
|
||||
bgpd/rfapi/bgp_rfapi_cfg.c \
|
||||
bgpd/rfapi/rfapi.c \
|
||||
bgpd/rfapi/rfapi_vty.c \
|
||||
bgpd/rfapi/vnc_debug.c \
|
||||
# end
|
||||
endif
|
||||
if SNMP
|
||||
module_LTLIBRARIES += bgpd/bgpd_snmp.la
|
||||
endif
|
||||
|
@ -2747,7 +2747,6 @@ AC_CONFIG_FILES([
|
||||
pkgsrc/ripd.sh pkgsrc/ripngd.sh pkgsrc/zebra.sh
|
||||
pkgsrc/eigrpd.sh])
|
||||
|
||||
AC_CONFIG_FILES([vtysh/extract.pl], [chmod +x vtysh/extract.pl])
|
||||
AC_CONFIG_FILES([tools/frr], [chmod +x tools/frr])
|
||||
AC_CONFIG_FILES([tools/watchfrr.sh], [chmod +x tools/watchfrr.sh])
|
||||
AC_CONFIG_FILES([tools/frrinit.sh], [chmod +x tools/frrinit.sh])
|
||||
|
@ -453,9 +453,7 @@ all DEFPY statements**:
|
||||
/* GPL header */
|
||||
#include ...
|
||||
...
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "daemon/filename_clippy.c"
|
||||
#endif
|
||||
|
||||
DEFPY(...)
|
||||
DEFPY(...)
|
||||
|
@ -43,9 +43,14 @@ simplifying the output. This is discussed in :ref:`vtysh-configuration`.
|
||||
Command Extraction
|
||||
------------------
|
||||
|
||||
When VTYSH is built, a Perl script named :file:`extract.pl` searches the FRR
|
||||
codebase looking for ``DEFUN``'s. It extracts these ``DEFUN``'s, transforms
|
||||
them into ``DEFSH``'s and appends them to ``vtysh_cmd.c``. Each ``DEFSH``
|
||||
To build ``vtysh``, the :file:`python/xref2vtysh.py` script scans through the
|
||||
:file:`frr.xref` file created earlier in the build process. This file contains
|
||||
a list of all ``DEFUN`` and ``install_element`` sites in the code, generated
|
||||
directly from the binaries (and therefore matching exactly what is really
|
||||
available.)
|
||||
|
||||
This list is collated and transformed into ``DEFSH`` (and ``install_element``)
|
||||
statements, output to ``vtysh_cmd.c``. Each ``DEFSH``
|
||||
contains the name of the command plus ``_vtysh``, as well as a flag that
|
||||
indicates which daemons the command was found in. When the command is executed
|
||||
in VTYSH, this flag is inspected to determine which daemons to send the command
|
||||
@ -55,6 +60,12 @@ avoiding spurious errors from daemons that don't have the command defined.
|
||||
The extraction script contains lots of hardcoded knowledge about what sources
|
||||
to look at and what flags to use for certain commands.
|
||||
|
||||
.. note::
|
||||
|
||||
The ``vtysh_scan`` Makefile variable and ``#ifndef VTYSH_EXTRACT_PL``
|
||||
checks in source files are no longer used. Remove them when rebasing older
|
||||
changes.
|
||||
|
||||
.. _vtysh-special-defuns:
|
||||
|
||||
Special DEFUNs
|
||||
@ -69,7 +80,7 @@ several VTYSH-specific ``DEFUN`` variants that each serve different purposes.
|
||||
simply forwarded to the daemons indicated in the daemon flag.
|
||||
|
||||
``DEFUN_NOSH``
|
||||
Used by daemons. Has the same expansion as a ``DEFUN``, but ``extract.pl``
|
||||
Used by daemons. Has the same expansion as a ``DEFUN``, but ``xref2vtysh.py``
|
||||
will skip these definitions when extracting commands. This is typically used
|
||||
when VTYSH must take some special action upon receiving the command, and the
|
||||
programmer therefore needs to write VTYSH's copy of the command manually
|
||||
|
@ -31,9 +31,7 @@
|
||||
#include "eigrp_zebra.h"
|
||||
#include "eigrp_cli.h"
|
||||
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "eigrpd/eigrp_cli_clippy.c"
|
||||
#endif /* VTYSH_EXTRACT_PL */
|
||||
|
||||
/*
|
||||
* XPath: /frr-eigrpd:eigrpd/instance
|
||||
|
@ -55,9 +55,7 @@
|
||||
#include "eigrpd/eigrp_dump.h"
|
||||
#include "eigrpd/eigrp_const.h"
|
||||
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "eigrpd/eigrp_vty_clippy.c"
|
||||
#endif
|
||||
|
||||
static void eigrp_vty_display_prefix_entry(struct vty *vty, struct eigrp *eigrp,
|
||||
struct eigrp_prefix_descriptor *pe,
|
||||
|
@ -4,12 +4,6 @@
|
||||
|
||||
if EIGRPD
|
||||
sbin_PROGRAMS += eigrpd/eigrpd
|
||||
vtysh_scan += \
|
||||
eigrpd/eigrp_cli.c \
|
||||
eigrpd/eigrp_dump.c \
|
||||
eigrpd/eigrp_vty.c \
|
||||
# end
|
||||
# eigrpd/eigrp_routemap.c
|
||||
vtysh_daemons += eigrpd
|
||||
man8 += $(MANBUILD)/frr-eigrpd.8
|
||||
endif
|
||||
|
@ -28,6 +28,13 @@ am__v_PROTOC_1 =
|
||||
|
||||
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:
|
||||
$(AM_V_PROTOC)$(PROTOC) -I$(top_srcdir) --cpp_out=$(top_builddir) $^
|
||||
.proto.grpc.pb.cc:
|
||||
|
@ -37,9 +37,7 @@
|
||||
#include "isisd/isis_circuit.h"
|
||||
#include "isisd/isis_csm.h"
|
||||
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "isisd/isis_cli_clippy.c"
|
||||
#endif
|
||||
|
||||
#ifndef FABRICD
|
||||
|
||||
|
@ -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,
|
||||
"show isis segment-routing node",
|
||||
SHOW_STR PROTO_HELP
|
||||
"show " PROTO_NAME " segment-routing node",
|
||||
SHOW_STR
|
||||
PROTO_HELP
|
||||
"Segment-Routing\n"
|
||||
"Segment-Routing node\n")
|
||||
{
|
||||
|
@ -5,16 +5,6 @@
|
||||
if ISISD
|
||||
noinst_LIBRARIES += isisd/libisis.a
|
||||
sbin_PROGRAMS += isisd/isisd
|
||||
vtysh_scan += \
|
||||
isisd/isis_cli.c \
|
||||
isisd/isis_ldp_sync.c \
|
||||
isisd/isis_redist.c \
|
||||
isisd/isis_spf.c \
|
||||
isisd/isis_te.c \
|
||||
isisd/isis_sr.c \
|
||||
isisd/isis_vty_fabricd.c \
|
||||
isisd/isisd.c \
|
||||
# end
|
||||
vtysh_daemons += isisd
|
||||
if SNMP
|
||||
module_LTLIBRARIES += isisd/isisd_snmp.la
|
||||
@ -25,18 +15,6 @@ endif
|
||||
if FABRICD
|
||||
noinst_LIBRARIES += isisd/libfabric.a
|
||||
sbin_PROGRAMS += isisd/fabricd
|
||||
if !ISISD
|
||||
vtysh_scan += \
|
||||
isisd/isis_cli.c \
|
||||
isisd/isis_ldp_sync.c \
|
||||
isisd/isis_redist.c \
|
||||
isisd/isis_spf.c \
|
||||
isisd/isis_te.c \
|
||||
isisd/isis_sr.c \
|
||||
isisd/isis_vty_fabricd.c \
|
||||
isisd/isisd.c \
|
||||
# end
|
||||
endif
|
||||
vtysh_daemons += fabricd
|
||||
endif
|
||||
|
||||
|
@ -25,9 +25,7 @@
|
||||
|
||||
#include "ldpd/ldpd.h"
|
||||
#include "ldpd/ldp_vty.h"
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "ldpd/ldp_vty_cmds_clippy.c"
|
||||
#endif
|
||||
|
||||
DEFPY_NOSH(ldp_mpls_ldp,
|
||||
ldp_mpls_ldp_cmd,
|
||||
|
@ -5,7 +5,6 @@
|
||||
if LDPD
|
||||
noinst_LIBRARIES += ldpd/libldp.a
|
||||
sbin_PROGRAMS += ldpd/ldpd
|
||||
vtysh_scan += ldpd/ldp_vty_cmds.c
|
||||
vtysh_daemons += ldpd
|
||||
man8 += $(MANBUILD)/frr-ldpd.8
|
||||
endif
|
||||
|
@ -251,9 +251,6 @@ struct cmd_node {
|
||||
/* Argc max counts. */
|
||||
#define CMD_ARGC_MAX 256
|
||||
|
||||
/* Turn off these macros when using cpp with extract.pl */
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
|
||||
/* helper defines for end-user DEFUN* macros */
|
||||
#define DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attrs, dnum) \
|
||||
static const struct cmd_element cmdname = { \
|
||||
@ -370,8 +367,6 @@ struct cmd_node {
|
||||
#define ALIAS_YANG(funcname, cmdname, cmdstr, helpstr) \
|
||||
ALIAS_ATTR(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_YANG)
|
||||
|
||||
#endif /* VTYSH_EXTRACT_PL */
|
||||
|
||||
/* Some macroes */
|
||||
|
||||
/*
|
||||
@ -511,7 +506,6 @@ struct xref_install_element {
|
||||
enum node_type node_type;
|
||||
};
|
||||
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#define install_element(node_type_, cmd_element_) do { \
|
||||
static const struct xref_install_element _xref \
|
||||
__attribute__((used)) = { \
|
||||
@ -523,7 +517,6 @@ struct xref_install_element {
|
||||
XREF_LINK(_xref.xref); \
|
||||
_install_element(node_type_, cmd_element_); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
extern void _install_element(enum node_type, const struct cmd_element *);
|
||||
|
||||
|
@ -31,9 +31,7 @@
|
||||
#include "lib/plist_int.h"
|
||||
#include "lib/printfrr.h"
|
||||
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "lib/filter_cli_clippy.c"
|
||||
#endif /* VTYSH_EXTRACT_PL */
|
||||
|
||||
#define ACCESS_LIST_STR "Access list entry\n"
|
||||
#define ACCESS_LIST_ZEBRA_STR "Access list name\n"
|
||||
|
2
lib/if.c
2
lib/if.c
@ -35,9 +35,7 @@
|
||||
#include "buffer.h"
|
||||
#include "log.h"
|
||||
#include "northbound_cli.h"
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "lib/if_clippy.c"
|
||||
#endif
|
||||
|
||||
DEFINE_MTYPE_STATIC(LIB, IF, "Interface");
|
||||
DEFINE_MTYPE_STATIC(LIB, CONNECTED, "Connected");
|
||||
|
@ -29,9 +29,7 @@
|
||||
#include "lib/printfrr.h"
|
||||
#include "lib/systemd.h"
|
||||
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "lib/log_vty_clippy.c"
|
||||
#endif
|
||||
|
||||
#define ZLOG_MAXLVL(a, b) MAX(a, b)
|
||||
|
||||
|
@ -28,9 +28,7 @@
|
||||
#include <command.h>
|
||||
#include <jhash.h>
|
||||
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "lib/nexthop_group_clippy.c"
|
||||
#endif
|
||||
|
||||
DEFINE_MTYPE_STATIC(LIB, NEXTHOP_GROUP, "Nexthop Group");
|
||||
|
||||
|
@ -32,9 +32,7 @@
|
||||
#include "northbound.h"
|
||||
#include "northbound_cli.h"
|
||||
#include "northbound_db.h"
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "lib/northbound_cli_clippy.c"
|
||||
#endif
|
||||
|
||||
struct debug nb_dbg_cbs_config = {0, "Northbound callbacks: configuration"};
|
||||
struct debug nb_dbg_cbs_state = {0, "Northbound callbacks: state"};
|
||||
|
@ -1193,9 +1193,7 @@ static int vty_clear_prefix_list(struct vty *vty, afi_t afi, const char *name,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "lib/plist_clippy.c"
|
||||
#endif
|
||||
|
||||
DEFPY (show_ip_prefix_list,
|
||||
show_ip_prefix_list_cmd,
|
||||
|
@ -26,9 +26,7 @@
|
||||
#include "lib/northbound_cli.h"
|
||||
#include "lib/routemap.h"
|
||||
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "lib/routemap_cli_clippy.c"
|
||||
#endif /* VTYSH_EXTRACT_PL */
|
||||
|
||||
#define ROUTE_MAP_CMD_STR \
|
||||
"Create route-map or enter route-map command mode\n" \
|
||||
|
@ -139,27 +139,6 @@ nodist_lib_libfrr_la_SOURCES = \
|
||||
yang/frr-module-translator.yang.c \
|
||||
# end
|
||||
|
||||
vtysh_scan += \
|
||||
lib/distribute.c \
|
||||
lib/filter.c \
|
||||
lib/filter_cli.c \
|
||||
lib/if.c \
|
||||
lib/if_rmap.c \
|
||||
lib/keychain.c \
|
||||
lib/lib_vty.c \
|
||||
lib/log_vty.c \
|
||||
lib/nexthop_group.c \
|
||||
lib/plist.c \
|
||||
lib/routemap.c \
|
||||
lib/routemap_cli.c \
|
||||
lib/spf_backoff.c \
|
||||
lib/thread.c \
|
||||
lib/vrf.c \
|
||||
lib/vty.c \
|
||||
# end
|
||||
# can be loaded as DSO - always include for vtysh
|
||||
vtysh_scan += lib/agentx.c
|
||||
|
||||
if SQLITE3
|
||||
lib_libfrr_la_LIBADD += $(SQLITE3_LIBS)
|
||||
lib_libfrr_la_SOURCES += lib/db.c
|
||||
@ -347,7 +326,6 @@ lib_libfrrsnmp_la_SOURCES = \
|
||||
if CARES
|
||||
lib_LTLIBRARIES += lib/libfrrcares.la
|
||||
pkginclude_HEADERS += lib/resolver.h
|
||||
vtysh_scan += lib/resolver.c
|
||||
endif
|
||||
|
||||
lib_libfrrcares_la_CFLAGS = $(AM_CFLAGS) $(CARES_CFLAGS)
|
||||
@ -478,13 +456,18 @@ SUFFIXES += .xref
|
||||
|
||||
# dependencies added in python/makefile.py
|
||||
frr.xref:
|
||||
$(AM_V_XRELFO) $(CLIPPY) $(top_srcdir)/python/xrelfo.py -o $@ $^
|
||||
$(AM_V_XRELFO) $(CLIPPY) $(top_srcdir)/python/xrelfo.py -o $@ -c vtysh/vtysh_cmd.c $^
|
||||
all-am: frr.xref
|
||||
|
||||
clean-xref:
|
||||
-rm -rf $(xrefs) frr.xref
|
||||
clean-local: clean-xref
|
||||
|
||||
CLEANFILES += vtysh/vtysh_cmd.c
|
||||
vtysh/vtysh_cmd.c: frr.xref
|
||||
@test -f $@ || rm -f frr.xref || true
|
||||
@test -f $@ || $(MAKE) $(AM_MAKEFLAGS) frr.xref
|
||||
|
||||
## automake's "ylwrap" is a great piece of GNU software... not.
|
||||
.l.c:
|
||||
$(AM_V_LEX)$(am__skiplex) $(LEXCOMPILE) $<
|
||||
|
@ -102,9 +102,7 @@ unsigned long cputime_threshold = CONSUMED_TIME_CHECK;
|
||||
unsigned long walltime_threshold = CONSUMED_TIME_CHECK;
|
||||
|
||||
/* CLI start ---------------------------------------------------------------- */
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "lib/thread_clippy.c"
|
||||
#endif
|
||||
|
||||
static unsigned int cpu_record_hash_key(const struct cpu_thread_history *a)
|
||||
{
|
||||
|
@ -53,9 +53,7 @@
|
||||
#include <arpa/telnet.h>
|
||||
#include <termios.h>
|
||||
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "lib/vty_clippy.c"
|
||||
#endif
|
||||
|
||||
DEFINE_MTYPE_STATIC(LIB, VTY, "VTY");
|
||||
DEFINE_MTYPE_STATIC(LIB, VTY_SERV, "VTY server");
|
||||
|
@ -202,9 +202,9 @@
|
||||
#endif /* HAVE_GLIBC_BACKTRACE */
|
||||
|
||||
/* Local includes: */
|
||||
#if !(defined(__GNUC__) || defined(VTYSH_EXTRACT_PL))
|
||||
#if !defined(__GNUC__)
|
||||
#define __attribute__(x)
|
||||
#endif /* !__GNUC__ || VTYSH_EXTRACT_PL */
|
||||
#endif /* !__GNUC__ */
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
|
@ -158,9 +158,7 @@ static int reconf_clear_dst(struct zlog_cfg_5424_user *cfg, struct vty *vty)
|
||||
return reconf_dst(cfg, vty);
|
||||
}
|
||||
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "lib/zlog_5424_cli_clippy.c"
|
||||
#endif
|
||||
|
||||
DEFPY_NOSH(log_5424_target,
|
||||
log_5424_target_cmd,
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
if NHRPD
|
||||
sbin_PROGRAMS += nhrpd/nhrpd
|
||||
vtysh_scan += nhrpd/nhrp_vty.c
|
||||
vtysh_daemons += nhrpd
|
||||
man8 += $(MANBUILD)/frr-nhrpd.8
|
||||
endif
|
||||
|
@ -49,9 +49,7 @@
|
||||
#include "ospf6d.h"
|
||||
#include "lib/json.h"
|
||||
#include "ospf6_nssa.h"
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "ospf6d/ospf6_area_clippy.c"
|
||||
#endif
|
||||
|
||||
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_AREA, "OSPF6 area");
|
||||
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_PLISTNAME, "Prefix list name");
|
||||
|
@ -65,9 +65,7 @@ static void ospf6_asbr_redistribute_set(struct ospf6 *ospf6, int type);
|
||||
static void ospf6_asbr_redistribute_unset(struct ospf6 *ospf6,
|
||||
struct ospf6_redist *red, int type);
|
||||
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "ospf6d/ospf6_asbr_clippy.c"
|
||||
#endif
|
||||
|
||||
unsigned char conf_debug_ospf6_asbr = 0;
|
||||
|
||||
@ -2206,10 +2204,10 @@ static const struct route_map_rule_cmd ospf6_routemap_rule_set_tag_cmd = {
|
||||
/* add "set metric-type" */
|
||||
DEFUN_YANG (ospf6_routemap_set_metric_type, ospf6_routemap_set_metric_type_cmd,
|
||||
"set metric-type <type-1|type-2>",
|
||||
"Set value\n"
|
||||
"Type of metric\n"
|
||||
"OSPF6 external type 1 metric\n"
|
||||
"OSPF6 external type 2 metric\n")
|
||||
SET_STR
|
||||
"Type of metric for destination routing protocol\n"
|
||||
"OSPF[6] external type 1 metric\n"
|
||||
"OSPF[6] external type 2 metric\n")
|
||||
{
|
||||
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,
|
||||
"no set metric-type [<type-1|type-2>]",
|
||||
NO_STR
|
||||
"Set value\n"
|
||||
"Type of metric\n"
|
||||
"OSPF6 external type 1 metric\n"
|
||||
"OSPF6 external type 2 metric\n")
|
||||
SET_STR
|
||||
"Type of metric for destination routing protocol\n"
|
||||
"OSPF[6] external type 1 metric\n"
|
||||
"OSPF[6] external type 2 metric\n")
|
||||
{
|
||||
const char *xpath =
|
||||
"./set-action[action='frr-ospf-route-map:metric-type']";
|
||||
|
@ -42,9 +42,7 @@
|
||||
#include "ospf6d/ospf6_intra.h"
|
||||
#include "ospf6d/ospf6_spf.h"
|
||||
#include "ospf6d/ospf6_gr.h"
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "ospf6d/ospf6_gr_clippy.c"
|
||||
#endif
|
||||
|
||||
static void ospf6_gr_nvm_delete(struct ospf6 *ospf6);
|
||||
|
||||
|
@ -49,9 +49,7 @@
|
||||
#include "ospf6d.h"
|
||||
#include "ospf6_gr.h"
|
||||
#include "lib/json.h"
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "ospf6d/ospf6_gr_helper_clippy.c"
|
||||
#endif
|
||||
|
||||
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_GR_HELPER, "OSPF6 Graceful restart helper");
|
||||
|
||||
|
@ -46,9 +46,7 @@
|
||||
#include "ospf6_flood.h"
|
||||
#include "ospf6d.h"
|
||||
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "ospf6d/ospf6_lsa_clippy.c"
|
||||
#endif
|
||||
|
||||
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_LSA, "OSPF6 LSA");
|
||||
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_LSA_HEADER, "OSPF6 LSA header");
|
||||
|
@ -49,9 +49,7 @@
|
||||
#include "ospf6_asbr.h"
|
||||
#include "ospf6d.h"
|
||||
#include "ospf6_nssa.h"
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "ospf6d/ospf6_nssa_clippy.c"
|
||||
#endif
|
||||
|
||||
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_LSA, "OSPF6 LSA");
|
||||
unsigned char config_debug_ospf6_nssa = 0;
|
||||
|
@ -37,9 +37,7 @@
|
||||
#include "ospf6_interface.h"
|
||||
#include "ospf6d.h"
|
||||
#include "ospf6_zebra.h"
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "ospf6d/ospf6_route_clippy.c"
|
||||
#endif
|
||||
|
||||
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_ROUTE, "OSPF6 route");
|
||||
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_ROUTE_TABLE, "OSPF6 route table");
|
||||
|
@ -65,9 +65,7 @@ FRR_CFG_DEFAULT_BOOL(OSPF6_LOG_ADJACENCY_CHANGES,
|
||||
{ .val_bool = false },
|
||||
);
|
||||
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "ospf6d/ospf6_top_clippy.c"
|
||||
#endif
|
||||
|
||||
/* global ospf6d variable */
|
||||
static struct ospf6_master ospf6_master;
|
||||
|
@ -5,27 +5,6 @@
|
||||
if OSPF6D
|
||||
noinst_LIBRARIES += ospf6d/libospf6.a
|
||||
sbin_PROGRAMS += ospf6d/ospf6d
|
||||
vtysh_scan += \
|
||||
ospf6d/ospf6_nssa.c \
|
||||
ospf6d/ospf6_abr.c \
|
||||
ospf6d/ospf6_asbr.c \
|
||||
ospf6d/ospf6_area.c \
|
||||
ospf6d/ospf6_bfd.c \
|
||||
ospf6d/ospf6_flood.c \
|
||||
ospf6d/ospf6_gr.c \
|
||||
ospf6d/ospf6_gr_helper.c \
|
||||
ospf6d/ospf6_interface.c \
|
||||
ospf6d/ospf6_intra.c \
|
||||
ospf6d/ospf6_lsa.c \
|
||||
ospf6d/ospf6_message.c \
|
||||
ospf6d/ospf6_neighbor.c \
|
||||
ospf6d/ospf6_route.c \
|
||||
ospf6d/ospf6_spf.c \
|
||||
ospf6d/ospf6_top.c \
|
||||
ospf6d/ospf6_zebra.c \
|
||||
ospf6d/ospf6d.c \
|
||||
ospf6d/ospf6_auth_trailer.c \
|
||||
# end
|
||||
vtysh_daemons += ospf6d
|
||||
if SNMP
|
||||
module_LTLIBRARIES += ospf6d/ospf6d_snmp.la
|
||||
|
@ -42,9 +42,7 @@
|
||||
#include "ospfd/ospf_dump.h"
|
||||
#include "ospfd/ospf_packet.h"
|
||||
#include "ospfd/ospf_network.h"
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "ospfd/ospf_dump_clippy.c"
|
||||
#endif
|
||||
|
||||
/* Configuration debug option variables. */
|
||||
unsigned long conf_debug_ospf_packet[5] = {0, 0, 0, 0, 0};
|
||||
|
@ -44,9 +44,7 @@
|
||||
#include "ospfd/ospf_gr.h"
|
||||
#include "ospfd/ospf_errors.h"
|
||||
#include "ospfd/ospf_dump.h"
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "ospfd/ospf_gr_clippy.c"
|
||||
#endif
|
||||
|
||||
static void ospf_gr_nvm_delete(struct ospf *ospf);
|
||||
|
||||
|
@ -751,9 +751,7 @@ void ospf_ldp_sync_if_write_config(struct vty *vty,
|
||||
/*
|
||||
* LDP-SYNC commands.
|
||||
*/
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "ospfd/ospf_ldp_sync_clippy.c"
|
||||
#endif
|
||||
|
||||
DEFPY (ospf_mpls_ldp_sync,
|
||||
ospf_mpls_ldp_sync_cmd,
|
||||
|
@ -185,9 +185,7 @@ static void ospf_show_vrf_name(struct ospf *ospf, struct vty *vty,
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "ospfd/ospf_vty_clippy.c"
|
||||
#endif
|
||||
|
||||
DEFUN_NOSH (router_ospf,
|
||||
router_ospf_cmd,
|
||||
|
@ -5,18 +5,6 @@
|
||||
if OSPFD
|
||||
noinst_LIBRARIES += ospfd/libfrrospf.a
|
||||
sbin_PROGRAMS += ospfd/ospfd
|
||||
vtysh_scan += \
|
||||
ospfd/ospf_bfd.c \
|
||||
ospfd/ospf_dump.c \
|
||||
ospfd/ospf_gr.c \
|
||||
ospfd/ospf_ldp_sync.c \
|
||||
ospfd/ospf_opaque.c \
|
||||
ospfd/ospf_ri.c \
|
||||
ospfd/ospf_routemap.c \
|
||||
ospfd/ospf_te.c \
|
||||
ospfd/ospf_sr.c \
|
||||
ospfd/ospf_vty.c \
|
||||
# end
|
||||
vtysh_daemons += ospfd
|
||||
if SNMP
|
||||
module_LTLIBRARIES += ospfd/ospfd_snmp.la
|
||||
|
@ -31,9 +31,7 @@
|
||||
|
||||
#include "pathd/pathd.h"
|
||||
#include "pathd/path_nb.h"
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "pathd/path_cli_clippy.c"
|
||||
#endif
|
||||
#include "pathd/path_ted.h"
|
||||
|
||||
#define XPATH_MAXATTRSIZE 64
|
||||
|
@ -40,9 +40,7 @@
|
||||
#include "pathd/path_pcep_lib.h"
|
||||
#include "pathd/path_pcep_pcc.h"
|
||||
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "pathd/path_pcep_cli_clippy.c"
|
||||
#endif
|
||||
|
||||
#define DEFAULT_PCE_PRECEDENCE 255
|
||||
#define DEFAULT_PCC_MSD 4
|
||||
|
@ -29,9 +29,7 @@
|
||||
#include "pathd/path_errors.h"
|
||||
#include "pathd/path_ted.h"
|
||||
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "pathd/path_ted_clippy.c"
|
||||
#endif
|
||||
|
||||
static struct ls_ted *path_ted_create_ted(void);
|
||||
static void path_ted_register_vty(void);
|
||||
|
@ -5,16 +5,11 @@
|
||||
if PATHD
|
||||
noinst_LIBRARIES += pathd/libpath.a
|
||||
sbin_PROGRAMS += pathd/pathd
|
||||
vtysh_scan += \
|
||||
pathd/path_cli.c \
|
||||
pathd/path_ted.c \
|
||||
#end
|
||||
vtysh_daemons += pathd
|
||||
# TODO add man page
|
||||
#man8 += $(MANBUILD)/pathd.8
|
||||
|
||||
if PATHD_PCEP
|
||||
vtysh_scan += pathd/path_pcep_cli.c
|
||||
module_LTLIBRARIES += pathd/pathd_pcep.la
|
||||
endif
|
||||
|
||||
|
@ -23,9 +23,7 @@
|
||||
#include "command.h"
|
||||
#include "vector.h"
|
||||
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "pbrd/pbr_debug_clippy.c"
|
||||
#endif
|
||||
#include "pbrd/pbr_debug.h"
|
||||
|
||||
struct debug pbr_dbg_map = {0, "PBR map"};
|
||||
|
@ -36,9 +36,7 @@
|
||||
#include "pbrd/pbr_zebra.h"
|
||||
#include "pbrd/pbr_vty.h"
|
||||
#include "pbrd/pbr_debug.h"
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "pbrd/pbr_vty_clippy.c"
|
||||
#endif
|
||||
|
||||
DEFUN_NOSH(pbr_map, pbr_map_cmd, "pbr-map PBRMAP seq (1-700)",
|
||||
"Create pbr-map or enter pbr-map command mode\n"
|
||||
|
@ -5,10 +5,6 @@
|
||||
if PBRD
|
||||
noinst_LIBRARIES += pbrd/libpbr.a
|
||||
sbin_PROGRAMS += pbrd/pbrd
|
||||
vtysh_scan += \
|
||||
pbrd/pbr_vty.c \
|
||||
pbrd/pbr_debug.c \
|
||||
# end
|
||||
vtysh_daemons += pbrd
|
||||
man8 += $(MANBUILD)/frr-pbrd.8
|
||||
endif
|
||||
|
@ -45,9 +45,7 @@
|
||||
#include "pim_zebra.h"
|
||||
#include "pim_instance.h"
|
||||
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "pimd/pim6_cmd_clippy.c"
|
||||
#endif
|
||||
|
||||
static struct cmd_node debug_node = {
|
||||
.name = "debug",
|
||||
|
@ -2319,9 +2319,7 @@ void gm_ifp_update(struct interface *ifp)
|
||||
|
||||
#include "lib/command.h"
|
||||
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "pimd/pim6_mld_clippy.c"
|
||||
#endif
|
||||
|
||||
static struct vrf *gm_cmd_vrf_lookup(struct vty *vty, const char *vrf_str,
|
||||
int *err)
|
||||
|
@ -70,9 +70,7 @@
|
||||
#include "pim_addr.h"
|
||||
#include "pim_cmd_common.h"
|
||||
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "pimd/pim_cmd_clippy.c"
|
||||
#endif
|
||||
|
||||
static struct cmd_node debug_node = {
|
||||
.name = "debug",
|
||||
|
@ -39,10 +39,8 @@
|
||||
#if defined(HAVE_LINUX_MROUTE_H)
|
||||
#include <linux/mroute.h>
|
||||
#else
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "linux/mroute.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
typedef struct vifctl pim_vifctl;
|
||||
typedef struct igmpmsg kernmsg;
|
||||
@ -86,10 +84,8 @@ typedef struct sioc_sg_req pim_sioc_sg_req;
|
||||
#if defined(HAVE_LINUX_MROUTE6_H)
|
||||
#include <linux/mroute6.h>
|
||||
#else
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "linux/mroute6.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef MRT_INIT
|
||||
#define MRT_BASE MRT6_BASE
|
||||
|
@ -6,11 +6,6 @@ if PIMD
|
||||
sbin_PROGRAMS += pimd/pimd
|
||||
bin_PROGRAMS += pimd/mtracebis
|
||||
noinst_PROGRAMS += pimd/test_igmpv3_join
|
||||
vtysh_scan += \
|
||||
pimd/pim_cmd.c \
|
||||
pimd/pim6_cmd.c \
|
||||
pimd/pim6_mld.c \
|
||||
#end
|
||||
vtysh_daemons += pimd
|
||||
vtysh_daemons += pim6d
|
||||
man8 += $(MANBUILD)/frr-pimd.8
|
||||
|
@ -321,15 +321,31 @@ extra_info = {
|
||||
"lsp_processq_complete",
|
||||
],
|
||||
# zebra - main WQ
|
||||
("mq_add_handler", "work_queue_add"): ["meta_queue_process",],
|
||||
("meta_queue_process", "work_queue_add"): ["meta_queue_process",],
|
||||
("mq_add_handler", "work_queue_add"): [
|
||||
"meta_queue_process",
|
||||
],
|
||||
("meta_queue_process", "work_queue_add"): [
|
||||
"meta_queue_process",
|
||||
],
|
||||
# bgpd - label pool WQ
|
||||
("bgp_lp_get", "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",],
|
||||
("bgp_lp_get", "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
|
||||
("bgp_process", "work_queue_add"): ["bgp_process_wq", "bgp_processq_del",],
|
||||
("bgp_add_eoiu_mark", "work_queue_add"): ["bgp_process_wq", "bgp_processq_del",],
|
||||
("bgp_process", "work_queue_add"): [
|
||||
"bgp_process_wq",
|
||||
"bgp_processq_del",
|
||||
],
|
||||
("bgp_add_eoiu_mark", "work_queue_add"): [
|
||||
"bgp_process_wq",
|
||||
"bgp_processq_del",
|
||||
],
|
||||
# clear node WQ
|
||||
("bgp_clear_route_table", "work_queue_add"): [
|
||||
"bgp_clear_route_node",
|
||||
@ -337,7 +353,9 @@ extra_info = {
|
||||
"bgp_clear_node_complete",
|
||||
],
|
||||
# rfapi WQs
|
||||
("rfapi_close", "work_queue_add"): ["rfapi_deferred_close_workfunc",],
|
||||
("rfapi_close", "work_queue_add"): [
|
||||
"rfapi_deferred_close_workfunc",
|
||||
],
|
||||
("rfapiRibUpdatePendingNode", "work_queue_add"): [
|
||||
"rfapiRibDoQueuedCallback",
|
||||
"rfapiRibQueueItemDelete",
|
||||
|
@ -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):
|
||||
"""iterator yielding all nodes of a graph
|
||||
|
@ -16,7 +16,7 @@
|
||||
# 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
|
||||
|
||||
'''
|
||||
"""
|
||||
Wrapping layer and additional utility around _clippy.ELFFile.
|
||||
|
||||
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
|
||||
in ELFDissectStruct) will take appropriate measures to flip and resize
|
||||
fields as needed.
|
||||
'''
|
||||
"""
|
||||
|
||||
import struct
|
||||
from collections import OrderedDict
|
||||
@ -40,16 +40,18 @@ from _clippy import ELFFile, ELFAccessError
|
||||
# data access
|
||||
#
|
||||
|
||||
|
||||
class ELFNull(object):
|
||||
'''
|
||||
"""
|
||||
NULL pointer, returned instead of ELFData
|
||||
'''
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.symname = None
|
||||
self._dstsect = None
|
||||
|
||||
def __repr__(self):
|
||||
return '<ptr: NULL>'
|
||||
return "<ptr: NULL>"
|
||||
|
||||
def __hash__(self):
|
||||
return hash(None)
|
||||
@ -57,33 +59,37 @@ class ELFNull(object):
|
||||
def get_string(self):
|
||||
return None
|
||||
|
||||
|
||||
class ELFUnresolved(object):
|
||||
'''
|
||||
"""
|
||||
Reference to an unresolved external symbol, returned instead of ELFData
|
||||
|
||||
:param symname: name of the referenced symbol
|
||||
:param addend: offset added to the symbol, normally zero
|
||||
'''
|
||||
"""
|
||||
|
||||
def __init__(self, symname, addend):
|
||||
self.addend = addend
|
||||
self.symname = symname
|
||||
self._dstsect = None
|
||||
|
||||
def __repr__(self):
|
||||
return '<unresolved: %s+%d>' % (self.symname, self.addend)
|
||||
return "<unresolved: %s+%d>" % (self.symname, self.addend)
|
||||
|
||||
def __hash__(self):
|
||||
return hash((self.symname, self.addend))
|
||||
|
||||
|
||||
class ELFData(object):
|
||||
'''
|
||||
"""
|
||||
Actual data somewhere in the ELF file.
|
||||
|
||||
:type dstsect: ELFSubset
|
||||
:param dstsect: container data area (section or entire file)
|
||||
:param dstoffs: byte offset into dstsect
|
||||
:param dstlen: byte size of object, or None if unknown, open-ended or string
|
||||
'''
|
||||
"""
|
||||
|
||||
def __init__(self, dstsect, dstoffs, dstlen):
|
||||
self._dstsect = dstsect
|
||||
self._dstoffs = dstoffs
|
||||
@ -91,62 +97,78 @@ class ELFData(object):
|
||||
self.symname = None
|
||||
|
||||
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):
|
||||
return hash((self._dstsect, self._dstoffs))
|
||||
|
||||
def get_string(self):
|
||||
'''
|
||||
"""
|
||||
Interpret as C string / null terminated UTF-8 and get the actual text.
|
||||
'''
|
||||
"""
|
||||
try:
|
||||
return self._dstsect[self._dstoffs:str].decode('UTF-8')
|
||||
return self._dstsect[self._dstoffs : str].decode("UTF-8")
|
||||
except:
|
||||
import pdb; pdb.set_trace()
|
||||
import pdb
|
||||
|
||||
pdb.set_trace()
|
||||
|
||||
def get_data(self, reflen):
|
||||
'''
|
||||
"""
|
||||
Interpret as some structure (and check vs. expected length)
|
||||
|
||||
:param reflen: expected size of the object, compared against actual
|
||||
size (which is only known in rare cases, mostly when directly
|
||||
accessing a symbol since symbols have their destination object
|
||||
size recorded)
|
||||
'''
|
||||
"""
|
||||
if self._dstlen is not None and self._dstlen != reflen:
|
||||
raise ValueError('symbol size mismatch (got %d, expected %d)' % (self._dstlen, reflen))
|
||||
return self._dstsect[self._dstoffs:self._dstoffs+reflen]
|
||||
raise ValueError(
|
||||
"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):
|
||||
'''
|
||||
"""
|
||||
Get another ELFData at an offset
|
||||
|
||||
:param offs: byte offset, can be negative (e.g. in container_of)
|
||||
:param within_symbol: retain length information
|
||||
'''
|
||||
"""
|
||||
if self._dstlen is None or not within_symbol:
|
||||
return ELFData(self._dstsect, self._dstoffs + offs, None)
|
||||
else:
|
||||
return ELFData(self._dstsect, self._dstoffs + offs, self._dstlen - offs)
|
||||
|
||||
|
||||
#
|
||||
# dissection data items
|
||||
#
|
||||
|
||||
|
||||
class ELFDissectData(object):
|
||||
'''
|
||||
"""
|
||||
Common bits for ELFDissectStruct and ELFDissectUnion
|
||||
'''
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self._data = None
|
||||
self.elfclass = None
|
||||
|
||||
def __len__(self):
|
||||
'''
|
||||
"""
|
||||
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):
|
||||
'''
|
||||
"""
|
||||
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)``
|
||||
@ -154,25 +176,26 @@ class ELFDissectData(object):
|
||||
:param parent: class (not instance) of the larger struct
|
||||
:param fieldname: fieldname that refers back to this
|
||||
:returns: instance of parent, with fieldname set to this object
|
||||
'''
|
||||
"""
|
||||
offset = 0
|
||||
if not hasattr(parent, '_efields'):
|
||||
if not hasattr(parent, "_efields"):
|
||||
parent._setup_efields()
|
||||
|
||||
for field in parent._efields[self.elfclass]:
|
||||
if field[0] == fieldname:
|
||||
break
|
||||
spec = field[1]
|
||||
if spec == 'P':
|
||||
spec = 'I' if self.elfclass == 32 else 'Q'
|
||||
if spec == "P":
|
||||
spec = "I" if self.elfclass == 32 else "Q"
|
||||
offset += struct.calcsize(spec)
|
||||
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):
|
||||
'''
|
||||
"""
|
||||
Decode and provide access to a struct somewhere in the ELF file
|
||||
|
||||
Handles pointers and strings somewhat nicely. Create a subclass for each
|
||||
@ -205,30 +228,31 @@ class ELFDissectStruct(ELFDissectData):
|
||||
.. attribute:: fieldrename
|
||||
|
||||
Dictionary to rename fields, useful if fields comes from tiabwarfo.py.
|
||||
'''
|
||||
"""
|
||||
|
||||
class Pointer(object):
|
||||
'''
|
||||
"""
|
||||
Quick wrapper for pointers to further structs
|
||||
|
||||
This is just here to avoid going into infinite loops when loading
|
||||
structs that have pointers to each other (e.g. struct xref <-->
|
||||
struct xrefdata.) The pointer destination is only instantiated when
|
||||
actually accessed.
|
||||
'''
|
||||
"""
|
||||
|
||||
def __init__(self, cls, ptr):
|
||||
self.cls = cls
|
||||
self.ptr = ptr
|
||||
|
||||
def __repr__(self):
|
||||
return '<Pointer:%s %r>' % (self.cls.__name__, self.ptr)
|
||||
return "<Pointer:%s %r>" % (self.cls.__name__, self.ptr)
|
||||
|
||||
def __call__(self):
|
||||
if isinstance(self.ptr, ELFNull):
|
||||
return None
|
||||
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:
|
||||
return super().__new__(cls)
|
||||
|
||||
@ -239,19 +263,19 @@ class ELFDissectStruct(ELFDissectData):
|
||||
dataptr._dstsect._pointers[(cls, dataptr)] = obj
|
||||
return obj
|
||||
|
||||
replacements = 'lLnN'
|
||||
replacements = "lLnN"
|
||||
|
||||
@classmethod
|
||||
def _preproc_structspec(cls, elfclass, spec):
|
||||
elfbits = elfclass
|
||||
|
||||
if hasattr(spec, 'calcsize'):
|
||||
spec = '%ds' % (spec.calcsize(elfclass),)
|
||||
if hasattr(spec, "calcsize"):
|
||||
spec = "%ds" % (spec.calcsize(elfclass),)
|
||||
|
||||
if elfbits == 32:
|
||||
repl = ['i', 'I']
|
||||
repl = ["i", "I"]
|
||||
else:
|
||||
repl = ['q', 'Q']
|
||||
repl = ["q", "Q"]
|
||||
for c in cls.replacements:
|
||||
spec = spec.replace(c, repl[int(c.isupper())])
|
||||
return spec
|
||||
@ -269,8 +293,8 @@ class ELFDissectStruct(ELFDissectData):
|
||||
size += struct.calcsize(newf[1])
|
||||
cls._esize[elfclass] = size
|
||||
|
||||
def __init__(self, dataptr, parent = None, replace = None):
|
||||
if not hasattr(self.__class__, '_efields'):
|
||||
def __init__(self, dataptr, parent=None, replace=None):
|
||||
if not hasattr(self.__class__, "_efields"):
|
||||
self._setup_efields()
|
||||
|
||||
self._fdata = None
|
||||
@ -290,12 +314,12 @@ class ELFDissectStruct(ELFDissectData):
|
||||
# need to correlate output from struct.unpack with extra metadata
|
||||
# about the particular fields, so note down byte offsets (in locs)
|
||||
# and tuple indices of pointers (in ptrs)
|
||||
pspec = ''
|
||||
pspec = ""
|
||||
locs = {}
|
||||
ptrs = set()
|
||||
|
||||
for idx, spec in enumerate(pspecl):
|
||||
if spec == 'P':
|
||||
if spec == "P":
|
||||
ptrs.add(idx)
|
||||
spec = self._elfsect.ptrtype
|
||||
|
||||
@ -326,7 +350,9 @@ class ELFDissectStruct(ELFDissectData):
|
||||
self._fdata[name] = replace[name]
|
||||
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)
|
||||
self._fdata[name] = dataobj
|
||||
continue
|
||||
@ -353,35 +379,41 @@ class ELFDissectStruct(ELFDissectData):
|
||||
|
||||
def __repr__(self):
|
||||
if not isinstance(self._data, ELFData):
|
||||
return '<%s: %r>' % (self.__class__.__name__, self._data)
|
||||
return '<%s: %s>' % (self.__class__.__name__,
|
||||
', '.join(['%s=%r' % t for t in self._fdata.items()]))
|
||||
return "<%s: %r>" % (self.__class__.__name__, self._data)
|
||||
return "<%s: %s>" % (
|
||||
self.__class__.__name__,
|
||||
", ".join(["%s=%r" % t for t in self._fdata.items()]),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def calcsize(cls, elfclass):
|
||||
'''
|
||||
"""
|
||||
Sum up byte size of this struct
|
||||
|
||||
Wraps struct.calcsize with some extra features.
|
||||
'''
|
||||
if not hasattr(cls, '_efields'):
|
||||
"""
|
||||
if not hasattr(cls, "_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'
|
||||
pspec = pspec.replace('P', ptrtype)
|
||||
ptrtype = "I" if elfclass == 32 else "Q"
|
||||
pspec = pspec.replace("P", ptrtype)
|
||||
|
||||
return struct.calcsize(pspec)
|
||||
|
||||
|
||||
class ELFDissectUnion(ELFDissectData):
|
||||
'''
|
||||
"""
|
||||
Decode multiple structs in the same place.
|
||||
|
||||
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.
|
||||
'''
|
||||
def __init__(self, dataptr, parent = None):
|
||||
"""
|
||||
|
||||
members = {}
|
||||
|
||||
def __init__(self, dataptr, parent=None):
|
||||
self._dataptr = dataptr
|
||||
self._parent = parent
|
||||
self.members = []
|
||||
@ -391,31 +423,44 @@ class ELFDissectUnion(ELFDissectData):
|
||||
setattr(self, name, item)
|
||||
|
||||
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
|
||||
def calcsize(cls, elfclass):
|
||||
return max([member.calcsize(elfclass) for name, member in cls.members])
|
||||
|
||||
|
||||
#
|
||||
# wrappers for spans of ELF data
|
||||
#
|
||||
|
||||
|
||||
class ELFSubset(object):
|
||||
'''
|
||||
"""
|
||||
Common abstract base for section-level and file-level access.
|
||||
'''
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
self.name = None
|
||||
self._obj = None
|
||||
self._elffile = None
|
||||
self.ptrtype = None
|
||||
self.endian = None
|
||||
self._pointers = WeakValueDictionary()
|
||||
|
||||
def _wrap_data(self, data, dstsect):
|
||||
raise NotImplementedError()
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.name)
|
||||
|
||||
def __getitem__(self, k):
|
||||
'''
|
||||
"""
|
||||
Read data from slice
|
||||
|
||||
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:str]` - extract until null byte. The slice stop value is
|
||||
the `str` type (or, technically, `unicode`.)
|
||||
'''
|
||||
"""
|
||||
return self._obj[k]
|
||||
|
||||
def getreloc(self, offset):
|
||||
'''
|
||||
"""
|
||||
Check for a relocation record at the specified 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
|
||||
|
||||
:param scls: ELFDissectData subclass for the struct
|
||||
:param slice_: optional range specification
|
||||
'''
|
||||
"""
|
||||
size = scls.calcsize(self._elffile.elfclass)
|
||||
|
||||
offset = slice_.start or 0
|
||||
@ -453,7 +498,7 @@ class ELFSubset(object):
|
||||
offset += size
|
||||
|
||||
def pointer(self, offset):
|
||||
'''
|
||||
"""
|
||||
Try to dereference a pointer value
|
||||
|
||||
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,
|
||||
or virtual address in file
|
||||
:returns: ELFData wrapping pointed-to object
|
||||
'''
|
||||
"""
|
||||
|
||||
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)
|
||||
dstsect = None
|
||||
@ -497,14 +544,15 @@ class ELFSubset(object):
|
||||
# wrap_data is different between file & section
|
||||
return self._wrap_data(data, dstsect)
|
||||
|
||||
|
||||
class ELFDissectSection(ELFSubset):
|
||||
'''
|
||||
"""
|
||||
Access the contents of an ELF section like ``.text`` or ``.data``
|
||||
|
||||
:param elfwrap: ELFDissectFile wrapper for the file
|
||||
:param idx: section index in section header table
|
||||
:param section: section object from C module
|
||||
'''
|
||||
"""
|
||||
|
||||
def __init__(self, elfwrap, idx, section):
|
||||
super().__init__()
|
||||
@ -524,8 +572,9 @@ class ELFDissectSection(ELFSubset):
|
||||
dstsect = self._elfwrap.get_section(dstsect.idx)
|
||||
return ELFData(dstsect, offs, None)
|
||||
|
||||
|
||||
class ELFDissectFile(ELFSubset):
|
||||
'''
|
||||
"""
|
||||
Access the contents of an ELF file.
|
||||
|
||||
Note that offsets for array subscript and relocation/pointer access are
|
||||
@ -537,7 +586,7 @@ class ELFDissectFile(ELFSubset):
|
||||
address like 0x400000 on x86.
|
||||
|
||||
:param filename: ELF file to open
|
||||
'''
|
||||
"""
|
||||
|
||||
def __init__(self, filename):
|
||||
super().__init__()
|
||||
@ -546,8 +595,8 @@ class ELFDissectFile(ELFSubset):
|
||||
self._elffile = self._obj = ELFFile(filename)
|
||||
self._sections = {}
|
||||
|
||||
self.ptrtype = 'I' if self._elffile.elfclass == 32 else 'Q'
|
||||
self.endian = '>' if self._elffile.bigendian else '<'
|
||||
self.ptrtype = "I" if self._elffile.elfclass == 32 else "Q"
|
||||
self.endian = ">" if self._elffile.bigendian else "<"
|
||||
|
||||
@property
|
||||
def _elfwrap(self):
|
||||
@ -557,9 +606,9 @@ class ELFDissectFile(ELFSubset):
|
||||
return ELFData(self, data, None)
|
||||
|
||||
def get_section(self, secname):
|
||||
'''
|
||||
"""
|
||||
Look up section by name or index
|
||||
'''
|
||||
"""
|
||||
if isinstance(secname, int):
|
||||
sh_idx = secname
|
||||
section = self._elffile.get_section_idx(secname)
|
||||
|
@ -19,13 +19,14 @@
|
||||
import struct
|
||||
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
|
||||
|
||||
if startbit is True, an '1' bit is inserted at the very beginning
|
||||
goes <bits> at a time, starts at LSB.
|
||||
'''
|
||||
"""
|
||||
bitavail, v = 0, 0
|
||||
if startbit and len(data) > 0:
|
||||
v = data.pop(0)
|
||||
@ -41,31 +42,33 @@ def bititer(data, bits, startbit = True):
|
||||
bitavail -= bits
|
||||
v >>= bits
|
||||
|
||||
|
||||
def base32c(data):
|
||||
'''
|
||||
"""
|
||||
Crockford base32 with extra dashes
|
||||
'''
|
||||
"""
|
||||
chs = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"
|
||||
o = ''
|
||||
o = ""
|
||||
if type(data) == str:
|
||||
data = [ord(v) for v in data]
|
||||
else:
|
||||
data = list(data)
|
||||
for i, bits in enumerate(bititer(data, 5)):
|
||||
if i == 5:
|
||||
o = o + '-'
|
||||
o = o + "-"
|
||||
elif i == 10:
|
||||
break
|
||||
o = o + chs[bits]
|
||||
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')
|
||||
hdata += struct.pack('>II', hashu32a, hashu32b)
|
||||
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")
|
||||
hdata += struct.pack(">II", hashu32a, hashu32b)
|
||||
i = sha256(hdata).digest()
|
||||
return base32c(i)
|
||||
|
@ -161,7 +161,15 @@ for clippy_file in clippy_scan:
|
||||
# combine daemon .xref files into frr.xref
|
||||
out_lines.append("")
|
||||
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(
|
||||
"xrefs = %s" % (" ".join(["%s.xref" % target for target in xref_targets]))
|
||||
|
@ -5,9 +5,11 @@ import os
|
||||
try:
|
||||
import _clippy
|
||||
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 ...".
|
||||
''')
|
||||
"""
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
os.chdir(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
@ -22,20 +22,21 @@ import pytest
|
||||
from pprint import pprint
|
||||
|
||||
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
|
||||
from clippy import elf, uidhash
|
||||
|
||||
|
||||
def test_uidhash():
|
||||
assert uidhash.uidhash("lib/test_xref.c", "logging call", 3, 0) \
|
||||
== 'H7KJB-67TBH'
|
||||
assert uidhash.uidhash("lib/test_xref.c", "logging call", 3, 0) == "H7KJB-67TBH"
|
||||
|
||||
|
||||
def test_xrelfo_other():
|
||||
for data in [
|
||||
elf.ELFNull(),
|
||||
elf.ELFUnresolved('somesym', 0),
|
||||
]:
|
||||
elf.ELFNull(),
|
||||
elf.ELFUnresolved("somesym", 0),
|
||||
]:
|
||||
|
||||
dissect = xrelfo.XrefPtr(data)
|
||||
print(repr(dissect))
|
||||
@ -43,9 +44,10 @@ def test_xrelfo_other():
|
||||
with pytest.raises(AttributeError):
|
||||
dissect.xref
|
||||
|
||||
|
||||
def test_xrelfo_obj():
|
||||
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
|
||||
|
||||
with pytest.raises(elf.ELFAccessError):
|
||||
@ -54,12 +56,13 @@ def test_xrelfo_obj():
|
||||
pprint(xrefs[0])
|
||||
pprint(xrefs[0]._data)
|
||||
|
||||
|
||||
def test_xrelfo_bin():
|
||||
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
|
||||
|
||||
assert edf[0:4] == b'\x7fELF'
|
||||
assert edf[0:4] == b"\x7fELF"
|
||||
|
||||
pprint(xrefs[0])
|
||||
pprint(xrefs[0]._data)
|
||||
|
@ -23,10 +23,19 @@ import re
|
||||
import argparse
|
||||
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.
|
||||
|
||||
Example pahole output:
|
||||
@ -41,26 +50,30 @@ def extract(filename='lib/.libs/libfrr.so'):
|
||||
/* size: 32, cachelines: 1, members: 5 */
|
||||
/* 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)
|
||||
field_re = re.compile(r'^\s*(?P<type>[^;\(]+)\s+(?P<name>[^;\[\]]+)(?:\[(?P<array>\d+)\])?;\s*\/\*(?P<comment>.*)\*\/\s*$')
|
||||
comment_re = re.compile(r'^\s*\/\*.*\*\/\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*$"
|
||||
)
|
||||
comment_re = re.compile(r"^\s*\/\*.*\*\/\s*$")
|
||||
|
||||
pastructs = struct_re.findall(pahole)
|
||||
out = {}
|
||||
|
||||
for sname, data in pastructs:
|
||||
this = out.setdefault(sname, {})
|
||||
fields = this.setdefault('fields', [])
|
||||
fields = this.setdefault("fields", [])
|
||||
|
||||
lines = data.strip().splitlines()
|
||||
|
||||
next_offs = 0
|
||||
|
||||
for line in lines:
|
||||
if line.strip() == '':
|
||||
if line.strip() == "":
|
||||
continue
|
||||
m = comment_re.match(line)
|
||||
if m is not None:
|
||||
@ -68,51 +81,55 @@ def extract(filename='lib/.libs/libfrr.so'):
|
||||
|
||||
m = field_re.match(line)
|
||||
if m is not None:
|
||||
offs, size = m.group('comment').strip().split()
|
||||
offs, size = m.group("comment").strip().split()
|
||||
offs = int(offs)
|
||||
size = int(size)
|
||||
typ_ = m.group('type').strip()
|
||||
name = m.group('name')
|
||||
typ_ = m.group("type").strip()
|
||||
name = m.group("name")
|
||||
|
||||
if name.startswith('(*'):
|
||||
if name.startswith("(*"):
|
||||
# function pointer
|
||||
typ_ = typ_ + ' *'
|
||||
name = name[2:].split(')')[0]
|
||||
typ_ = typ_ + " *"
|
||||
name = name[2:].split(")")[0]
|
||||
|
||||
data = {
|
||||
'name': name,
|
||||
'type': typ_,
|
||||
# 'offset': offs,
|
||||
# 'size': size,
|
||||
"name": name,
|
||||
"type": typ_,
|
||||
# 'offset': offs,
|
||||
# 'size': size,
|
||||
}
|
||||
if m.group('array'):
|
||||
data['array'] = int(m.group('array'))
|
||||
if m.group("array"):
|
||||
data["array"] = int(m.group("array"))
|
||||
|
||||
fields.append(data)
|
||||
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
|
||||
continue
|
||||
|
||||
raise ValueError('cannot process line: %s' % line)
|
||||
raise ValueError("cannot process line: %s" % line)
|
||||
|
||||
return out
|
||||
|
||||
|
||||
class FieldApplicator(object):
|
||||
'''
|
||||
"""
|
||||
Fill ELFDissectStruct fields list from pahole/JSON
|
||||
|
||||
Uses the JSON file created by the above code to fill in the struct fields
|
||||
in subclasses of ELFDissectStruct.
|
||||
'''
|
||||
"""
|
||||
|
||||
# only what we really need. add more as needed.
|
||||
packtypes = {
|
||||
'int': 'i',
|
||||
'uint8_t': 'B',
|
||||
'uint16_t': 'H',
|
||||
'uint32_t': 'I',
|
||||
'char': 's',
|
||||
"int": "i",
|
||||
"uint8_t": "B",
|
||||
"uint16_t": "H",
|
||||
"uint32_t": "I",
|
||||
"char": "s",
|
||||
}
|
||||
|
||||
def __init__(self, data):
|
||||
@ -126,60 +143,65 @@ class FieldApplicator(object):
|
||||
|
||||
def resolve(self, cls):
|
||||
out = []
|
||||
#offset = 0
|
||||
# offset = 0
|
||||
|
||||
fieldrename = getattr(cls, "fieldrename", {})
|
||||
|
||||
fieldrename = getattr(cls, 'fieldrename', {})
|
||||
def mkname(n):
|
||||
return (fieldrename.get(n, n),)
|
||||
|
||||
for field in self.data[cls.struct]['fields']:
|
||||
typs = field['type'].split()
|
||||
typs = [i for i in typs if i not in ['const']]
|
||||
for field in self.data[cls.struct]["fields"]:
|
||||
typs = field["type"].split()
|
||||
typs = [i for i in typs if i not in ["const"]]
|
||||
|
||||
# this will break reuse of xrefstructs.json across 32bit & 64bit
|
||||
# platforms
|
||||
|
||||
#if field['offset'] != offset:
|
||||
# if field['offset'] != offset:
|
||||
# assert offset < field['offset']
|
||||
# out.append(('_pad', '%ds' % (field['offset'] - offset,)))
|
||||
|
||||
# pretty hacky C types handling, but covers what we need
|
||||
|
||||
ptrlevel = 0
|
||||
while typs[-1] == '*':
|
||||
while typs[-1] == "*":
|
||||
typs.pop(-1)
|
||||
ptrlevel += 1
|
||||
|
||||
if ptrlevel > 0:
|
||||
packtype = ('P', None)
|
||||
packtype = ("P", None)
|
||||
if ptrlevel == 1:
|
||||
if typs[0] == 'char':
|
||||
packtype = ('P', str)
|
||||
elif typs[0] == 'struct' and typs[1] in self.clsmap:
|
||||
packtype = ('P', self.clsmap[typs[1]])
|
||||
elif typs[0] == 'enum':
|
||||
packtype = ('I',)
|
||||
if typs[0] == "char":
|
||||
packtype = ("P", str)
|
||||
elif typs[0] == "struct" and typs[1] in self.clsmap:
|
||||
packtype = ("P", self.clsmap[typs[1]])
|
||||
elif typs[0] == "enum":
|
||||
packtype = ("I",)
|
||||
elif typs[0] in self.packtypes:
|
||||
packtype = (self.packtypes[typs[0]],)
|
||||
elif typs[0] == 'struct':
|
||||
elif typs[0] == "struct":
|
||||
if typs[1] in self.clsmap:
|
||||
packtype = (self.clsmap[typs[1]],)
|
||||
else:
|
||||
raise ValueError('embedded struct %s not in extracted data' % (typs[1],))
|
||||
raise ValueError(
|
||||
"embedded struct %s not in extracted data" % (typs[1],)
|
||||
)
|
||||
else:
|
||||
raise ValueError('cannot decode field %s in struct %s (%s)' % (
|
||||
cls.struct, field['name'], field['type']))
|
||||
raise ValueError(
|
||||
"cannot decode field %s in struct %s (%s)"
|
||||
% (cls.struct, field["name"], field["type"])
|
||||
)
|
||||
|
||||
if 'array' in field and typs[0] == 'char':
|
||||
packtype = ('%ds' % field['array'],)
|
||||
out.append(mkname(field['name']) + packtype)
|
||||
elif 'array' in field:
|
||||
for i in range(0, field['array']):
|
||||
out.append(mkname('%s_%d' % (field['name'], i)) + packtype)
|
||||
if "array" in field and typs[0] == "char":
|
||||
packtype = ("%ds" % field["array"],)
|
||||
out.append(mkname(field["name"]) + packtype)
|
||||
elif "array" in field:
|
||||
for i in range(0, field["array"]):
|
||||
out.append(mkname("%s_%d" % (field["name"], i)) + packtype)
|
||||
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
|
||||
|
||||
@ -187,16 +209,30 @@ class FieldApplicator(object):
|
||||
for cls in self.classes:
|
||||
self.resolve(cls)
|
||||
|
||||
|
||||
def main():
|
||||
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('-i', dest='input', type=str, help='ELF file to read', default='lib/.libs/libfrr.so')
|
||||
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(
|
||||
"-i",
|
||||
dest="input",
|
||||
type=str,
|
||||
help="ELF file to read",
|
||||
default="lib/.libs/libfrr.so",
|
||||
)
|
||||
args = argp.parse_args()
|
||||
|
||||
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)
|
||||
os.rename(args.output + '.tmp', args.output)
|
||||
os.rename(args.output + ".tmp", args.output)
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
@ -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
389
python/xref2vtysh.py
Normal 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()
|
330
python/xrelfo.py
330
python/xrelfo.py
@ -37,15 +37,18 @@ from clippy.uidhash import uidhash
|
||||
from clippy.elf import *
|
||||
from clippy import frr_top_src, CmdAttr
|
||||
from tiabwarfo import FieldApplicator
|
||||
from xref2vtysh import CommandEntry
|
||||
|
||||
try:
|
||||
with open(os.path.join(frr_top_src, 'python', 'xrefstructs.json'), 'r') as fd:
|
||||
with open(os.path.join(frr_top_src, "python", "xrefstructs.json"), "r") as fd:
|
||||
xrefstructs = json.load(fd)
|
||||
except FileNotFoundError:
|
||||
sys.stderr.write('''
|
||||
sys.stderr.write(
|
||||
"""
|
||||
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.
|
||||
''')
|
||||
"""
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
# constants, need to be kept in sync manually...
|
||||
@ -57,7 +60,7 @@ XREFT_INSTALL_ELEMENT = 0x301
|
||||
|
||||
# LOG_*
|
||||
priovals = {}
|
||||
prios = ['0', '1', '2', 'E', 'W', 'N', 'I', 'D']
|
||||
prios = ["0", "1", "2", "E", "W", "N", "I", "D"]
|
||||
|
||||
|
||||
class XrelfoJson(object):
|
||||
@ -70,9 +73,10 @@ class XrelfoJson(object):
|
||||
def to_dict(self, refs):
|
||||
pass
|
||||
|
||||
|
||||
class Xref(ELFDissectStruct, XrelfoJson):
|
||||
struct = 'xref'
|
||||
fieldrename = {'type': 'typ'}
|
||||
struct = "xref"
|
||||
fieldrename = {"type": "typ"}
|
||||
containers = {}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@ -85,7 +89,7 @@ class Xref(ELFDissectStruct, XrelfoJson):
|
||||
def container(self):
|
||||
if self._container is None:
|
||||
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
|
||||
|
||||
def check(self, *args, **kwargs):
|
||||
@ -94,10 +98,10 @@ class Xref(ELFDissectStruct, XrelfoJson):
|
||||
|
||||
|
||||
class Xrefdata(ELFDissectStruct):
|
||||
struct = 'xrefdata'
|
||||
struct = "xrefdata"
|
||||
|
||||
# uid is all zeroes in the data loaded from ELF
|
||||
fieldrename = {'uid': '_uid'}
|
||||
fieldrename = {"uid": "_uid"}
|
||||
|
||||
def ref_from(self, xref, typ):
|
||||
self.xref = xref
|
||||
@ -108,38 +112,84 @@ class Xrefdata(ELFDissectStruct):
|
||||
return None
|
||||
return uidhash(self.xref.file, self.hashstr, self.hashu32_0, self.hashu32_1)
|
||||
|
||||
|
||||
class XrefPtr(ELFDissectStruct):
|
||||
fields = [
|
||||
('xref', 'P', Xref),
|
||||
("xref", "P", Xref),
|
||||
]
|
||||
|
||||
|
||||
class XrefThreadSched(ELFDissectStruct, XrelfoJson):
|
||||
struct = 'xref_threadsched'
|
||||
struct = "xref_threadsched"
|
||||
|
||||
|
||||
Xref.containers[XREFT_THREADSCHED] = XrefThreadSched
|
||||
|
||||
|
||||
class XrefLogmsg(ELFDissectStruct, XrelfoJson):
|
||||
struct = 'xref_logmsg'
|
||||
struct = "xref_logmsg"
|
||||
|
||||
def _warn_fmt(self, text):
|
||||
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:]])))
|
||||
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:]]),
|
||||
),
|
||||
)
|
||||
|
||||
fmt_regexes = [
|
||||
(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'^((?:warn(?:ing)?|error):\s*)', re.I), 'warning: log message starts with severity'),
|
||||
(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"^((?:warn(?:ing)?|error):\s*)", re.I),
|
||||
"warning: log message starts with severity",
|
||||
),
|
||||
]
|
||||
arg_regexes = [
|
||||
# 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_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) ),
|
||||
# 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,
|
||||
),
|
||||
(
|
||||
# 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):
|
||||
@ -149,11 +199,11 @@ class XrefLogmsg(ELFDissectStruct, XrelfoJson):
|
||||
out = []
|
||||
for i, text in enumerate(items):
|
||||
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:
|
||||
out.append(repr(text)[1:-1])
|
||||
|
||||
excerpt = ''.join(out)
|
||||
excerpt = "".join(out)
|
||||
else:
|
||||
excerpt = repr(itext)[1:-1]
|
||||
return excerpt
|
||||
@ -174,70 +224,99 @@ class XrefLogmsg(ELFDissectStruct, XrelfoJson):
|
||||
continue
|
||||
|
||||
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):
|
||||
print('%-60s %s%s %-25s [EC %d] %s' % (
|
||||
'%s:%d %s()' % (self.xref.file, self.xref.line, self.xref.func),
|
||||
prios[self.priority & 7],
|
||||
priovals.get(self.priority & 0x30, ' '),
|
||||
self.xref.xrefdata.uid, self.ec, self.fmtstring))
|
||||
print(
|
||||
"%-60s %s%s %-25s [EC %d] %s"
|
||||
% (
|
||||
"%s:%d %s()" % (self.xref.file, self.xref.line, self.xref.func),
|
||||
prios[self.priority & 7],
|
||||
priovals.get(self.priority & 0x30, " "),
|
||||
self.xref.xrefdata.uid,
|
||||
self.ec,
|
||||
self.fmtstring,
|
||||
)
|
||||
)
|
||||
|
||||
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:
|
||||
jsobj['ec'] = self.ec
|
||||
jsobj['fmtstring'] = self.fmtstring
|
||||
jsobj['args'] = self.args
|
||||
jsobj['priority'] = self.priority & 7
|
||||
jsobj['type'] = 'logmsg'
|
||||
jsobj['binary'] = self._elfsect._elfwrap.orig_filename
|
||||
jsobj["ec"] = self.ec
|
||||
jsobj["fmtstring"] = self.fmtstring
|
||||
jsobj["args"] = self.args
|
||||
jsobj["priority"] = self.priority & 7
|
||||
jsobj["type"] = "logmsg"
|
||||
jsobj["binary"] = self._elfsect._elfwrap.orig_filename
|
||||
|
||||
if self.priority & 0x10:
|
||||
jsobj.setdefault('flags', []).append('errno')
|
||||
jsobj.setdefault("flags", []).append("errno")
|
||||
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
|
||||
|
||||
|
||||
class CmdElement(ELFDissectStruct, XrelfoJson):
|
||||
struct = 'cmd_element'
|
||||
struct = "cmd_element"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
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({
|
||||
'string': self.string,
|
||||
'doc': self.doc,
|
||||
})
|
||||
jsobj.update(
|
||||
{
|
||||
"string": self.string,
|
||||
"doc": self.doc,
|
||||
}
|
||||
)
|
||||
if self.attr:
|
||||
jsobj['attr'] = attr = self.attr
|
||||
jsobj["attr"] = attr = self.attr
|
||||
for attrname in CmdAttr.__members__:
|
||||
val = CmdAttr[attrname]
|
||||
if attr & val:
|
||||
jsobj.setdefault('attrs', []).append(attrname.lower())
|
||||
jsobj.setdefault("attrs", []).append(attrname.lower())
|
||||
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
|
||||
|
||||
|
||||
class XrefInstallElement(ELFDissectStruct, XrelfoJson):
|
||||
struct = 'xref_install_element'
|
||||
struct = "xref_install_element"
|
||||
|
||||
def to_dict(self, xrelfo):
|
||||
jsobj = xrelfo['cli'].setdefault(self.cmd_element.name, {}).setdefault(self._elfsect._elfwrap.orig_filename, {})
|
||||
nodes = jsobj.setdefault('nodes', [])
|
||||
jsobj = (
|
||||
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
|
||||
|
||||
@ -254,86 +333,90 @@ fieldapply()
|
||||
|
||||
class Xrelfo(dict):
|
||||
def __init__(self):
|
||||
super().__init__({
|
||||
'refs': {},
|
||||
'cli': {},
|
||||
})
|
||||
super().__init__(
|
||||
{
|
||||
"refs": {},
|
||||
"cli": {},
|
||||
}
|
||||
)
|
||||
self._xrefs = []
|
||||
|
||||
def load_file(self, filename):
|
||||
orig_filename = filename
|
||||
if filename.endswith('.la') or filename.endswith('.lo'):
|
||||
with open(filename, 'r') as fd:
|
||||
if filename.endswith(".la") or filename.endswith(".lo"):
|
||||
with open(filename, "r") as fd:
|
||||
for line in fd:
|
||||
line = line.strip()
|
||||
if line.startswith('#') or line == '' or '=' not in line:
|
||||
if line.startswith("#") or line == "" or "=" not in line:
|
||||
continue
|
||||
|
||||
var, val = line.split('=', 1)
|
||||
if var not in ['library_names', 'pic_object']:
|
||||
var, val = line.split("=", 1)
|
||||
if var not in ["library_names", "pic_object"]:
|
||||
continue
|
||||
if val.startswith("'") or val.startswith('"'):
|
||||
val = val[1:-1]
|
||||
|
||||
if var == 'pic_object':
|
||||
if var == "pic_object":
|
||||
filename = os.path.join(os.path.dirname(filename), val)
|
||||
break
|
||||
|
||||
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
|
||||
else:
|
||||
raise ValueError('could not process libtool file "%s"' % orig_filename)
|
||||
raise ValueError(
|
||||
'could not process libtool file "%s"' % orig_filename
|
||||
)
|
||||
|
||||
while True:
|
||||
with open(filename, 'rb') as fd:
|
||||
with open(filename, "rb") as fd:
|
||||
hdr = fd.read(4)
|
||||
|
||||
if hdr == b'\x7fELF':
|
||||
if hdr == b"\x7fELF":
|
||||
self.load_elf(filename, orig_filename)
|
||||
return
|
||||
|
||||
if hdr[:2] == b'#!':
|
||||
if hdr[:2] == b"#!":
|
||||
path, name = os.path.split(filename)
|
||||
filename = os.path.join(path, '.libs', name)
|
||||
filename = os.path.join(path, ".libs", name)
|
||||
continue
|
||||
|
||||
if hdr[:1] == b'{':
|
||||
with open(filename, 'r') as fd:
|
||||
if hdr[:1] == b"{":
|
||||
with open(filename, "r") as fd:
|
||||
self.load_json(fd)
|
||||
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):
|
||||
edf = ELFDissectFile(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:
|
||||
endian = '>' if edf._elffile.bigendian else '<'
|
||||
endian = ">" if edf._elffile.bigendian else "<"
|
||||
mem = edf._elffile[note]
|
||||
if edf._elffile.elfclass == 64:
|
||||
start, end = struct.unpack(endian + 'QQ', mem)
|
||||
start, end = struct.unpack(endian + "QQ", mem)
|
||||
start += note.start
|
||||
end += note.start + 8
|
||||
else:
|
||||
start, end = struct.unpack(endian + 'II', mem)
|
||||
start, end = struct.unpack(endian + "II", mem)
|
||||
start += note.start
|
||||
end += note.start + 4
|
||||
|
||||
ptrs = edf.iter_data(XrefPtr, slice(start, end))
|
||||
|
||||
else:
|
||||
xrefarray = edf.get_section('xref_array')
|
||||
xrefarray = edf.get_section("xref_array")
|
||||
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)
|
||||
|
||||
for ptr in ptrs:
|
||||
if ptr.xref is None:
|
||||
print('NULL xref')
|
||||
print("NULL xref")
|
||||
continue
|
||||
self._xrefs.append(ptr.xref)
|
||||
|
||||
@ -346,15 +429,15 @@ class Xrelfo(dict):
|
||||
|
||||
def load_json(self, fd):
|
||||
data = json.load(fd)
|
||||
for uid, items in data['refs'].items():
|
||||
myitems = self['refs'].setdefault(uid, [])
|
||||
for uid, items in data["refs"].items():
|
||||
myitems = self["refs"].setdefault(uid, [])
|
||||
for item in items:
|
||||
if item in myitems:
|
||||
continue
|
||||
myitems.append(item)
|
||||
|
||||
for cmd, items in data['cli'].items():
|
||||
self['cli'].setdefault(cmd, {}).update(items)
|
||||
for cmd, items in data["cli"].items():
|
||||
self["cli"].setdefault(cmd, {}).update(items)
|
||||
|
||||
return data
|
||||
|
||||
@ -362,23 +445,33 @@ class Xrelfo(dict):
|
||||
for xref in self._xrefs:
|
||||
yield from xref.check(checks)
|
||||
|
||||
|
||||
def main():
|
||||
argp = argparse.ArgumentParser(description = 'FRR xref ELF extractor')
|
||||
argp.add_argument('-o', dest='output', type=str, help='write JSON output')
|
||||
argp.add_argument('--out-by-file', type=str, help='write by-file JSON output')
|
||||
argp.add_argument('-Wlog-format', action='store_const', const=True)
|
||||
argp.add_argument('-Wlog-args', action='store_const', const=True)
|
||||
argp.add_argument('-Werror', action='store_const', const=True)
|
||||
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)')
|
||||
argp = argparse.ArgumentParser(description="FRR xref ELF extractor")
|
||||
argp.add_argument("-o", dest="output", type=str, help="write JSON output")
|
||||
argp.add_argument("--out-by-file", type=str, help="write by-file JSON output")
|
||||
argp.add_argument("-c", dest="vtysh_cmds", type=str, help="write vtysh_cmd.c")
|
||||
argp.add_argument("-Wlog-format", action="store_const", const=True)
|
||||
argp.add_argument("-Wlog-args", action="store_const", const=True)
|
||||
argp.add_argument("-Werror", action="store_const", const=True)
|
||||
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()
|
||||
|
||||
if args.profile:
|
||||
import cProfile
|
||||
cProfile.runctx('_main(args)', globals(), {'args': args}, sort='cumtime')
|
||||
|
||||
cProfile.runctx("_main(args)", globals(), {"args": args}, sort="cumtime")
|
||||
else:
|
||||
_main(args)
|
||||
|
||||
|
||||
def _main(args):
|
||||
errors = 0
|
||||
xrelfo = Xrelfo()
|
||||
@ -388,52 +481,59 @@ def _main(args):
|
||||
xrelfo.load_file(fn)
|
||||
except:
|
||||
errors += 1
|
||||
sys.stderr.write('while processing %s:\n' % (fn))
|
||||
sys.stderr.write("while processing %s:\n" % (fn))
|
||||
traceback.print_exc()
|
||||
|
||||
for option in dir(args):
|
||||
if option.startswith('W') and option != 'Werror':
|
||||
if option.startswith("W") and option != "Werror":
|
||||
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:
|
||||
errors += 1
|
||||
break
|
||||
|
||||
|
||||
refs = xrelfo['refs']
|
||||
refs = xrelfo["refs"]
|
||||
|
||||
counts = {}
|
||||
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:
|
||||
print('\033[31;1m%s\033[m' % k)
|
||||
print("\033[31;1m%s\033[m" % k)
|
||||
counts[k] = len(v)
|
||||
|
||||
out = xrelfo
|
||||
outbyfile = {}
|
||||
for uid, locs in refs.items():
|
||||
for loc in locs:
|
||||
filearray = outbyfile.setdefault(loc['file'], [])
|
||||
filearray = outbyfile.setdefault(loc["file"], [])
|
||||
loc = dict(loc)
|
||||
del loc['file']
|
||||
del loc["file"]
|
||||
filearray.append(loc)
|
||||
|
||||
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:
|
||||
sys.exit(1)
|
||||
|
||||
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)
|
||||
os.rename(args.output + '.tmp', args.output)
|
||||
os.rename(args.output + ".tmp", args.output)
|
||||
|
||||
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)
|
||||
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()
|
||||
|
@ -30,9 +30,7 @@
|
||||
|
||||
#include "ripd/ripd.h"
|
||||
#include "ripd/rip_nb.h"
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "ripd/rip_cli_clippy.c"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* XPath: /frr-ripd:ripd/instance
|
||||
|
@ -4,11 +4,6 @@
|
||||
|
||||
if RIPD
|
||||
sbin_PROGRAMS += ripd/ripd
|
||||
vtysh_scan += \
|
||||
ripd/rip_cli.c \
|
||||
ripd/rip_debug.c \
|
||||
ripd/ripd.c \
|
||||
# end
|
||||
vtysh_daemons += ripd
|
||||
|
||||
if SNMP
|
||||
|
@ -30,9 +30,7 @@
|
||||
|
||||
#include "ripngd/ripngd.h"
|
||||
#include "ripngd/ripng_nb.h"
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "ripngd/ripng_cli_clippy.c"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* XPath: /frr-ripngd:ripngd/instance
|
||||
|
@ -4,11 +4,6 @@
|
||||
|
||||
if RIPNGD
|
||||
sbin_PROGRAMS += ripngd/ripngd
|
||||
vtysh_scan += \
|
||||
ripngd/ripng_cli.c \
|
||||
ripngd/ripng_debug.c \
|
||||
ripngd/ripngd.c \
|
||||
# end
|
||||
vtysh_daemons += ripngd
|
||||
man8 += $(MANBUILD)/frr-ripngd.8
|
||||
endif
|
||||
|
@ -37,9 +37,7 @@
|
||||
#include "sharpd/sharp_zebra.h"
|
||||
#include "sharpd/sharp_nht.h"
|
||||
#include "sharpd/sharp_vty.h"
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "sharpd/sharp_vty_clippy.c"
|
||||
#endif
|
||||
|
||||
DEFINE_MTYPE_STATIC(SHARPD, SRV6_LOCATOR, "SRv6 Locator");
|
||||
|
||||
|
@ -5,7 +5,6 @@
|
||||
if SHARPD
|
||||
noinst_LIBRARIES += sharpd/libsharp.a
|
||||
sbin_PROGRAMS += sharpd/sharpd
|
||||
vtysh_scan += sharpd/sharp_vty.c
|
||||
vtysh_daemons += sharpd
|
||||
man8 += $(MANBUILD)/frr-sharpd.8
|
||||
endif
|
||||
|
@ -36,9 +36,7 @@
|
||||
#include "static_vty.h"
|
||||
#include "static_routes.h"
|
||||
#include "static_debug.h"
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "staticd/static_vty_clippy.c"
|
||||
#endif
|
||||
#include "static_nb.h"
|
||||
|
||||
#define STATICD_STR "Static route daemon\n"
|
||||
|
@ -5,7 +5,6 @@
|
||||
if STATICD
|
||||
noinst_LIBRARIES += staticd/libstatic.a
|
||||
sbin_PROGRAMS += staticd/staticd
|
||||
vtysh_scan += staticd/static_vty.c
|
||||
vtysh_daemons += staticd
|
||||
man8 += $(MANBUILD)/frr-staticd.8
|
||||
endif
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
if VRRPD
|
||||
sbin_PROGRAMS += vrrpd/vrrpd
|
||||
vtysh_scan += vrrpd/vrrp_vty.c
|
||||
vtysh_daemons += vrrpd
|
||||
man8 += $(MANBUILD)/frr-vrrpd.8
|
||||
endif
|
||||
|
@ -33,9 +33,7 @@
|
||||
#include "vrrp_debug.h"
|
||||
#include "vrrp_vty.h"
|
||||
#include "vrrp_zebra.h"
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "vrrpd/vrrp_vty_clippy.c"
|
||||
#endif
|
||||
|
||||
|
||||
#define VRRP_STR "Virtual Router Redundancy Protocol\n"
|
||||
|
4
vtysh/.gitignore
vendored
4
vtysh/.gitignore
vendored
@ -1,4 +1,6 @@
|
||||
vtysh
|
||||
vtysh_cmd.c
|
||||
extract.pl
|
||||
vtysh_daemons.h
|
||||
|
||||
# does not exist anymore - remove 2023-10-04 or so
|
||||
extract.pl
|
||||
|
@ -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
|
@ -20,7 +20,6 @@ vtysh_vtysh_SOURCES = \
|
||||
nodist_vtysh_vtysh_SOURCES = \
|
||||
vtysh/vtysh_cmd.c \
|
||||
# end
|
||||
CLEANFILES += vtysh/vtysh_cmd.c
|
||||
|
||||
noinst_HEADERS += \
|
||||
vtysh/vtysh.h \
|
||||
@ -39,23 +38,3 @@ $(vtysh_vtysh_OBJECTS): vtysh/vtysh_daemons.h
|
||||
CLEANFILES += vtysh/vtysh_daemons.h
|
||||
vtysh/vtysh_daemons.h:
|
||||
$(PERL) $(top_srcdir)/vtysh/daemons.pl $(vtysh_daemons) > vtysh/vtysh_daemons.h
|
||||
|
||||
AM_V_EXTRACT = $(am__v_EXTRACT_$(V))
|
||||
am__v_EXTRACT_ = $(am__v_EXTRACT_$(AM_DEFAULT_VERBOSITY))
|
||||
am__v_EXTRACT_0 = @echo " EXTRACT " $@;
|
||||
am__v_EXTRACT_1 =
|
||||
|
||||
if ISISD
|
||||
HAVE_ISISD = --have-isisd
|
||||
else
|
||||
HAVE_ISISD =
|
||||
endif
|
||||
|
||||
if FABRICD
|
||||
HAVE_FABRICD = --have-fabricd
|
||||
else
|
||||
HAVE_FABRICD =
|
||||
endif
|
||||
|
||||
vtysh/vtysh_cmd.c: vtysh/extract.pl $(vtysh_scan)
|
||||
$(AM_V_EXTRACT) $^ $(HAVE_ISISD) $(HAVE_FABRICD) > vtysh/vtysh_cmd.c
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
if WATCHFRR
|
||||
sbin_PROGRAMS += watchfrr/watchfrr
|
||||
vtysh_scan += watchfrr/watchfrr_vty.c
|
||||
man8 += $(MANBUILD)/frr-watchfrr.8
|
||||
endif
|
||||
|
||||
|
@ -153,9 +153,7 @@ DEFUN_NOSH (show_logging,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "watchfrr/watchfrr_vty_clippy.c"
|
||||
#endif
|
||||
|
||||
DEFPY (watchfrr_ignore_daemon,
|
||||
watchfrr_ignore_daemon_cmd,
|
||||
|
@ -23,9 +23,7 @@
|
||||
#include "command.h"
|
||||
#include "debug.h"
|
||||
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "zebra/debug_clippy.c"
|
||||
#endif
|
||||
|
||||
/* For debug statement. */
|
||||
unsigned long zebra_debug_event;
|
||||
|
@ -23,9 +23,7 @@
|
||||
#include "lib/json.h"
|
||||
#include "zebra/dpdk/zebra_dplane_dpdk.h"
|
||||
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "zebra/dpdk/zebra_dplane_dpdk_vty_clippy.c"
|
||||
#endif
|
||||
|
||||
#define ZD_STR "Zebra dataplane information\n"
|
||||
#define ZD_DPDK_STR "DPDK offload information\n"
|
||||
|
@ -2603,9 +2603,7 @@ static void interface_update_stats(void)
|
||||
#endif /* HAVE_NET_RT_IFLIST */
|
||||
}
|
||||
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "zebra/interface_clippy.c"
|
||||
#endif
|
||||
/* Show all interfaces to vty. */
|
||||
DEFPY(show_interface, show_interface_cmd,
|
||||
"show interface vrf NAME$vrf_name [brief$brief] [json$uj]",
|
||||
|
@ -51,9 +51,7 @@ static uint32_t interfaces_configured_for_ra_from_bgp;
|
||||
|
||||
#if defined(HAVE_RTADV)
|
||||
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "zebra/rtadv_clippy.c"
|
||||
#endif
|
||||
|
||||
DEFINE_MTYPE_STATIC(ZEBRA, RTADV_PREFIX, "Router Advertisement Prefix");
|
||||
DEFINE_MTYPE_STATIC(ZEBRA, ADV_IF, "Advertised Interface");
|
||||
|
@ -4,29 +4,6 @@
|
||||
|
||||
if ZEBRA
|
||||
sbin_PROGRAMS += zebra/zebra
|
||||
vtysh_scan += \
|
||||
zebra/debug.c \
|
||||
zebra/interface.c \
|
||||
zebra/router-id.c \
|
||||
zebra/rtadv.c \
|
||||
zebra/zebra_gr.c \
|
||||
zebra/zebra_mlag_vty.c \
|
||||
zebra/zebra_evpn_mh.c \
|
||||
zebra/zebra_mpls_vty.c \
|
||||
zebra/zebra_srv6_vty.c \
|
||||
zebra/zebra_ptm.c \
|
||||
zebra/zebra_pw.c \
|
||||
zebra/zebra_routemap.c \
|
||||
zebra/zebra_vty.c \
|
||||
zebra/zserv.c \
|
||||
zebra/zebra_vrf.c \
|
||||
zebra/dpdk/zebra_dplane_dpdk_vty.c \
|
||||
# end
|
||||
|
||||
# can be loaded as DSO - always include for vtysh
|
||||
vtysh_scan += zebra/irdp_interface.c
|
||||
vtysh_scan += zebra/zebra_fpm.c
|
||||
|
||||
vtysh_daemons += zebra
|
||||
|
||||
if IRDP
|
||||
@ -255,8 +232,6 @@ module_LTLIBRARIES += zebra/dplane_fpm_nl.la
|
||||
zebra_dplane_fpm_nl_la_SOURCES = zebra/dplane_fpm_nl.c
|
||||
zebra_dplane_fpm_nl_la_LDFLAGS = $(MODULE_LDFLAGS)
|
||||
zebra_dplane_fpm_nl_la_LIBADD =
|
||||
|
||||
vtysh_scan += zebra/dplane_fpm_nl.c
|
||||
endif
|
||||
|
||||
if NETLINK_DEBUG
|
||||
|
@ -3252,9 +3252,7 @@ int zebra_evpn_mh_if_write(struct vty *vty, struct interface *ifp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "zebra/zebra_evpn_mh_clippy.c"
|
||||
#endif
|
||||
/* CLI for setting an ES in bypass mode */
|
||||
DEFPY_HIDDEN(zebra_evpn_es_bypass, zebra_evpn_es_bypass_cmd,
|
||||
"[no] evpn mh bypass",
|
||||
|
@ -29,9 +29,7 @@
|
||||
#include "debug.h"
|
||||
#include "zapi_msg.h"
|
||||
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "zebra/zebra_mlag_vty_clippy.c"
|
||||
#endif
|
||||
|
||||
DEFUN_HIDDEN (show_mlag,
|
||||
show_mlag_cmd,
|
||||
|
@ -40,9 +40,7 @@
|
||||
#include "zebra/zebra_rnh.h"
|
||||
#include "zebra/zebra_routemap.h"
|
||||
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "zebra/zebra_routemap_clippy.c"
|
||||
#endif
|
||||
|
||||
static uint32_t zebra_rmap_update_timer = ZEBRA_RMAP_DEFAULT_UPDATE_TIMER;
|
||||
static struct thread *zebra_t_rmap_update = NULL;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user