summaryrefslogtreecommitdiffstats
path: root/doc/developer/cli.rst
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 09:53:30 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 09:53:30 +0000
commit2c7cac91ed6e7db0f6937923d2b57f97dbdbc337 (patch)
treec05dc0f8e6aa3accc84e3e5cffc933ed94941383 /doc/developer/cli.rst
parentInitial commit. (diff)
downloadfrr-2c7cac91ed6e7db0f6937923d2b57f97dbdbc337.tar.xz
frr-2c7cac91ed6e7db0f6937923d2b57f97dbdbc337.zip
Adding upstream version 8.4.4.upstream/8.4.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--doc/developer/cli.rst999
1 files changed, 999 insertions, 0 deletions
diff --git a/doc/developer/cli.rst b/doc/developer/cli.rst
new file mode 100644
index 0000000..ff6c4f6
--- /dev/null
+++ b/doc/developer/cli.rst
@@ -0,0 +1,999 @@
+.. _command-line-interface:
+
+Command Line Interface
+======================
+
+FRR features a flexible modal command line interface. Often when adding new
+features or modifying existing code it is necessary to create or modify CLI
+commands. FRR has a powerful internal CLI system that does most of the heavy
+lifting for you.
+
+Modes
+-----
+FRR's CLI is organized by modes. Each mode is associated with some set of
+functionality, e.g. EVPN, or some underlying object such as an interface. Each
+mode contains a set of commands that control the associated functionality or
+object. Users move between the modes by entering a command, which is usually
+different for each source and destination mode.
+
+A summary of the modes is given in the following figure.
+
+.. graphviz:: ../figures/nodes.dot
+
+.. seealso:: :ref:`cli-data-structures`
+
+Walkup
+^^^^^^
+FRR exhibits, for historical reasons, a peculiar behavior called 'walkup'.
+Suppose a user is in ``OSPF_NODE``, which contains only OSPF-specific commands,
+and enters the following command: ::
+
+ ip route 192.168.100.0/24 10.0.2.2
+
+This command is not defined in ``OSPF_NODE``, so the matcher will fail to match
+the command in that node. The matcher will then check "parent" nodes of
+``OSPF_NODE``. In this case the direct parent of ``OSPF_NODE`` is
+``CONFIG_NODE``, so the current node switches to ``CONFIG_NODE`` and the command
+is tried in that node. Since static route commands are defined in
+``CONFIG_NODE`` the command succeeds. The procedure of attempting to execute
+unmatched commands by sequentially "walking up" to parent nodes only happens in
+children (direct and indirect) below ``CONFIG_NODE`` and stops at
+``CONFIG_NODE``.
+
+Unfortunately, the internal representation of the various modes is not actually
+a graph. Instead, there is an array. The parent-child relationships are not
+explicitly defined in any datastructure but instead are hard-coded into the
+specific commands that switch nodes. For walkup, there is a function that takes
+a node and returns the parent of the node. This interface causes all manner of
+insidious problems, even for experienced developers, and needs to be fixed at
+some point in the future.
+
+Deprecation of old style of commands
+------------------------------------
+
+There are currently 2 styles of defining commands within a FRR source file.
+``DEFUN`` and ``DEFPY``. ``DEFPY`` should be used for all new commands that
+a developer is writing. This is because it allows for much better handling
+of command line arguments as well as ensuring that input is correct. ``DEFUN``
+is listed here for historical reasons as well as for ensuring that existing
+code can be understood by new developers.
+
+Defining Commands
+-----------------
+All definitions for the CLI system are exposed in ``lib/command.h``. In this
+header there are a set of macros used to define commands. These macros are
+collectively referred to as "DEFUNs", because of their syntax:
+
+::
+
+ DEFUN(command_name,
+ command_name_cmd,
+ "example command FOO...",
+ "Examples\n"
+ "CLI command\n"
+ "Argument\n")
+ {
+ // ...command handler...
+ }
+
+DEFUNs generally take four arguments which are expanded into the appropriate
+constructs for hooking into the CLI. In order these are:
+
+- **Function name** - the name of the handler function for the command
+- **Command name** - the identifier of the ``struct cmd_element`` for the
+ command. By convention this should be the function name with ``_cmd``
+ appended.
+- **Command definition** - an expression in FRR's CLI grammar that defines the
+ form of the command and its arguments, if any
+- **Doc string** - a newline-delimited string that documents each element in
+ the command definition
+
+In the above example, ``command_name`` is the function name,
+``command_name_cmd`` is the command name, ``"example..."`` is the definition and
+the last argument is the doc string. The block following the macro is the body
+of the handler function, details on which are presented later in this section.
+
+In order to make the command show up to the user it must be installed into the
+CLI graph. To do this, call:
+
+``install_element(NODE, &command_name_cmd);``
+
+This will install the command into the specified CLI node. Usually these calls
+are grouped together in a CLI initialization function for a set of commands, and
+the DEFUNs themselves are grouped into the same source file to avoid cluttering
+the codebase. The names of these files follow the form ``*_vty.[ch]`` by
+convention. Please do not scatter individual CLI commands in the middle of
+source files; instead expose the necessary functions in a header and place the
+command definition in a ``*_vty.[ch]`` file.
+
+Definition Grammar
+^^^^^^^^^^^^^^^^^^
+FRR uses its own grammar for defining CLI commands. The grammar draws from
+syntax commonly seen in \*nix manpages and should be fairly intuitive. The
+parser is implemented in Bison and the lexer in Flex. These may be found in
+``lib/command_parse.y`` and ``lib/command_lex.l``, respectively.
+
+ **ProTip**: if you define a new command and find that the parser is
+ throwing syntax or other errors, the parser is the last place you want
+ to look. Bison is very stable and if it detects a syntax error, 99% of
+ the time it will be a syntax error in your definition.
+
+The formal grammar in BNF is given below. This is the grammar implemented in the
+Bison parser. At runtime, the Bison parser reads all of the CLI strings and
+builds a combined directed graph that is used to match and interpret user input.
+
+Human-friendly explanations of how to use this grammar are given a bit later in
+this section alongside information on the :ref:`cli-data-structures` constructed
+by the parser.
+
+.. productionlist::
+ command: `cmd_token_seq`
+ : `cmd_token_seq` `placeholder_token` "..."
+ cmd_token_seq: *empty*
+ : `cmd_token_seq` `cmd_token`
+ cmd_token: `simple_token`
+ : `selector`
+ simple_token: `literal_token`
+ : `placeholder_token`
+ literal_token: WORD `varname_token`
+ varname_token: "$" WORD
+ placeholder_token: `placeholder_token_real` `varname_token`
+ placeholder_token_real: IPV4
+ : IPV4_PREFIX
+ : IPV6
+ : IPV6_PREFIX
+ : VARIABLE
+ : RANGE
+ : MAC
+ : MAC_PREFIX
+ selector: "<" `selector_seq_seq` ">" `varname_token`
+ : "{" `selector_seq_seq` "}" `varname_token`
+ : "[" `selector_seq_seq` "]" `varname_token`
+ : "![" `selector_seq_seq` "]" `varname_token`
+ selector_seq_seq: `selector_seq_seq` "|" `selector_token_seq`
+ : `selector_token_seq`
+ selector_token_seq: `selector_token_seq` `selector_token`
+ : `selector_token`
+ selector_token: `selector`
+ : `simple_token`
+
+Tokens
+^^^^^^
+The various capitalized tokens in the BNF above are in fact themselves
+placeholders, but not defined as such in the formal grammar; the grammar
+provides the structure, and the tokens are actually more like a type system for
+the strings you write in your CLI definitions. A CLI definition string is broken
+apart and each piece is assigned a type by the lexer based on a set of regular
+expressions. The parser uses the type information to verify the string and
+determine the structure of the CLI graph; additional metadata (such as the raw
+text of each token) is encoded into the graph as it is constructed by the
+parser, but this is merely a dumb copy job.
+
+Here is a brief summary of the various token types along with examples.
+
++-----------------+-------------------+-------------------------------------------------------------+
+| Token type | Syntax | Description |
++=================+===================+=============================================================+
+| ``WORD`` | ``show ip bgp`` | Matches itself. In the given example every token is a WORD. |
++-----------------+-------------------+-------------------------------------------------------------+
+| ``IPV4`` | ``A.B.C.D`` | Matches an IPv4 address. |
++-----------------+-------------------+-------------------------------------------------------------+
+| ``IPV6`` | ``X:X::X:X`` | Matches an IPv6 address. |
++-----------------+-------------------+-------------------------------------------------------------+
+| ``IPV4_PREFIX`` | ``A.B.C.D/M`` | Matches an IPv4 prefix in CIDR notation. |
++-----------------+-------------------+-------------------------------------------------------------+
+| ``IPV6_PREFIX`` | ``X:X::X:X/M`` | Matches an IPv6 prefix in CIDR notation. |
++-----------------+-------------------+-------------------------------------------------------------+
+| ``MAC`` | ``X:X:X:X:X:X`` | Matches a 48-bit mac address. |
++-----------------+-------------------+-------------------------------------------------------------+
+| ``MAC_PREFIX`` | ``X:X:X:X:X:X/M`` | Matches a 48-bit mac address with a mask. |
++-----------------+-------------------+-------------------------------------------------------------+
+| ``VARIABLE`` | ``FOOBAR`` | Matches anything. |
++-----------------+-------------------+-------------------------------------------------------------+
+| ``RANGE`` | ``(X-Y)`` | Matches numbers in the range X..Y inclusive. |
++-----------------+-------------------+-------------------------------------------------------------+
+
+When presented with user input, the parser will search over all defined
+commands in the current context to find a match. It is aware of the various
+types of user input and has a ranking system to help disambiguate commands. For
+instance, suppose the following commands are defined in the user's current
+context:
+
+::
+
+ example command FOO
+ example command (22-49)
+ example command A.B.C.D/X
+
+The following table demonstrates the matcher's choice for a selection of
+possible user input.
+
++---------------------------------+---------------------------+--------------------------------------------------------------------------------------------------------------+
+| Input | Matched command | Reason |
++=================================+===========================+==============================================================================================================+
+| ``example command eLi7eH4xx0r`` | example command FOO | ``eLi7eH4xx0r`` is not an integer or IPv4 prefix, |
+| | | but FOO is a variable and matches all input. |
++---------------------------------+---------------------------+--------------------------------------------------------------------------------------------------------------+
+| ``example command 42`` | example command (22-49) | ``42`` is not an IPv4 prefix. It does match both |
+| | | ``(22-49)`` and ``FOO``, but RANGE tokens are more specific and have a higher priority than VARIABLE tokens. |
++---------------------------------+---------------------------+--------------------------------------------------------------------------------------------------------------+
+| ``example command 10.3.3.0/24`` | example command A.B.C.D/X | The user entered an IPv4 prefix, which is best matched by the last command. |
++---------------------------------+---------------------------+--------------------------------------------------------------------------------------------------------------+
+
+Rules
+^^^^^
+There are also constructs which allow optional tokens, mutual exclusion,
+one-or-more selection and repetition.
+
+- ``<angle|brackets>`` -- Contain sequences of tokens separated by pipes and
+ provide mutual exclusion. User input matches at most one option.
+- ``[square brackets]`` -- Contains sequences of tokens that can be omitted.
+ ``[<a|b>]`` can be shortened to ``[a|b]``.
+- ``![exclamation square brackets]`` -- same as ``[square brackets]``, but
+ only allow skipping the contents if the command input starts with ``no``.
+ (For cases where the positive command needs a parameter, but the parameter
+ is optional for the negative case.)
+- ``{curly|braces}`` -- similar to angle brackets, but instead of mutual
+ exclusion, curly braces indicate that one or more of the pipe-separated
+ sequences may be provided in any order.
+- ``VARIADICS...`` -- Any token which accepts input (anything except WORD)
+ which occurs as the last token of a line may be followed by an ellipsis,
+ which indicates that input matching the token may be repeated an unlimited
+ number of times.
+- ``$name`` -- Specify a variable name for the preceding token. See
+ "Variable Names" below.
+
+Some general notes:
+
+- Options are allowed at the beginning of the command. The developer is
+ entreated to use these extremely sparingly. They are most useful for
+ implementing the 'no' form of configuration commands. Please think carefully
+ before using them for anything else. There is usually a better solution, even
+ if it is just separating out the command definition into separate ones.
+- The developer should judiciously apply separation of concerns when defining
+ commands. CLI definitions for two unrelated or vaguely related commands or
+ configuration items should be defined in separate commands. Clarity is
+ preferred over LOC (within reason).
+- The maximum number of space-separated tokens that can be entered is
+ presently limited to 256. Please keep this limit in mind when
+ implementing new CLI.
+
+Variable Names
+^^^^^^^^^^^^^^
+The parser tries to fill the "varname" field on each token. This can happen
+either manually or automatically. Manual specifications work by appending
+``$name`` after the input specifier:
+
+::
+
+ foo bar$cmd WORD$name A.B.C.D$ip
+
+Note that you can also assign variable names to fixed input tokens, this can be
+useful if multiple commands share code. You can also use "$name" after a
+multiple-choice option:
+
+::
+
+ foo bar <A.B.C.D|X:X::X:X>$addr [optionA|optionB]$mode
+
+The variable name is in this case assigned to the last token in each of the
+branches.
+
+Automatic assignment of variable names works by applying the following rules:
+
+- manual names always have priority
+- a ``[no]`` at the beginning receives ``no`` as varname on the ``no`` token
+- ``VARIABLE`` tokens whose text is not ``WORD`` or ``NAME`` receive a cleaned
+ lowercase version of the token text as varname, e.g. ``ROUTE-MAP`` becomes
+ ``route_map``.
+- other variable tokens (i.e. everything except "fixed") receive the text of
+ the preceding fixed token as varname, if one can be found. E.g.
+ ``ip route A.B.C.D/M INTERFACE`` assigns "route" to the ``A.B.C.D/M`` token.
+
+These rules should make it possible to avoid manual varname assignment in 90% of
+the cases.
+
+Doc Strings
+^^^^^^^^^^^
+Each token in a command definition should be documented with a brief doc string
+that informs a user of the meaning and/or purpose of the subsequent command
+tree. These strings are provided as the last parameter to DEFUN macros,
+concatenated together and separated by an escaped newline (``\n``). These are
+best explained by example.
+
+::
+
+ DEFUN (config_terminal,
+ config_terminal_cmd,
+ "configure terminal",
+ "Configuration from vty interface\n"
+ "Configuration terminal\n")
+
+The last parameter is split into two lines for readability. Two newline
+delimited doc strings are present, one for each token in the command. The second
+string documents the functionality of the ``terminal`` command in the
+``configure`` subtree.
+
+Note that the first string, for ``configure`` does not contain documentation for
+'terminal'. This is because the CLI is best envisioned as a tree, with tokens
+defining branches. An imaginary ``start`` token is the root of every command in
+a CLI node. Each subsequent written token descends into a subtree, so the
+documentation for that token ideally summarizes all the functionality contained
+in the subtree.
+
+A consequence of this structure is that the developer must be careful to use the
+same doc strings when defining multiple commands that are part of the same tree.
+Commands which share prefixes must share the same doc strings for those
+prefixes. On startup the parser will generate warnings if it notices
+inconsistent doc strings. Behavior is undefined; the same token may show up
+twice in completions, with different doc strings, or it may show up once with a
+random doc string. Parser warnings should be heeded and fixed to avoid confusing
+users.
+
+The number of doc strings provided must be equal to the amount of tokens present
+in the command definition, read left to right, ignoring any special constructs.
+
+In the examples below, each arrowed token needs a doc string.
+
+::
+
+ "show ip bgp"
+ ^ ^ ^
+
+ "command <foo|bar> [example]"
+ ^ ^ ^ ^
+
+DEFPY
+^^^^^
+``DEFPY(...)`` is an enhanced version of ``DEFUN()`` which is preprocessed by
+:file:`python/clidef.py`. The python script parses the command definition
+string, extracts variable names and types, and generates a C wrapper function
+that parses the variables and passes them on. This means that in the CLI
+function body, you will receive additional parameters with appropriate types.
+
+This is best explained by an example. Invoking ``DEFPY`` like this:
+
+.. code-block:: c
+
+ DEFPY(func, func_cmd, "[no] foo bar A.B.C.D (0-99)$num", "...help...")
+
+defines the handler function like this:
+
+.. code-block:: c
+
+ func(self, vty, argc, argv, /* standard CLI arguments */
+ const char *no, /* unparsed "no" */
+ struct in_addr bar, /* parsed IP address */
+ const char *bar_str, /* unparsed IP address */
+ long num, /* parsed num */
+ const char *num_str) /* unparsed num */
+
+Note that as documented in the previous section, ``bar`` is automatically
+applied as variable name for ``A.B.C.D``. The Python script then detects this as
+an IP address argument and generates code to parse it into a ``struct in_addr``,
+passing it in ``bar``. The raw value is passed in ``bar_str``. The range/number
+argument works in the same way with the explicitly given variable name.
+
+Type rules
+""""""""""
+
++----------------------------+--------------------------------+--------------------------+
+| Token(s) | Type | Value if omitted by user |
++============================+================================+==========================+
+| ``A.B.C.D`` | ``struct in_addr`` | ``0.0.0.0`` |
++----------------------------+--------------------------------+--------------------------+
+| ``X:X::X:X`` | ``struct in6_addr`` | ``::`` |
++----------------------------+--------------------------------+--------------------------+
+| ``A.B.C.D + X:X::X:X`` | ``const union sockunion *`` | ``NULL`` |
++----------------------------+--------------------------------+--------------------------+
+| ``A.B.C.D/M`` | ``const struct prefix_ipv4 *`` | ``all-zeroes struct`` |
++----------------------------+--------------------------------+--------------------------+
+| ``X:X::X:X/M`` | ``const struct prefix_ipv6 *`` | ``all-zeroes struct`` |
++----------------------------+--------------------------------+--------------------------+
+| ``A.B.C.D/M + X:X::X:X/M`` | ``const struct prefix *`` | ``all-zeroes struct`` |
++----------------------------+--------------------------------+--------------------------+
+| ``(0-9)`` | ``long`` | ``0`` |
++----------------------------+--------------------------------+--------------------------+
+| ``VARIABLE`` | ``const char *`` | ``NULL`` |
++----------------------------+--------------------------------+--------------------------+
+| ``word`` | ``const char *`` | ``NULL`` |
++----------------------------+--------------------------------+--------------------------+
+| *all other* | ``const char *`` | ``NULL`` |
++----------------------------+--------------------------------+--------------------------+
+
+Note the following details:
+
+- Not all parameters are pointers, some are passed as values.
+- When the type is not ``const char *``, there will be an extra ``_str``
+ argument with type ``const char *``.
+- You can give a variable name not only to ``VARIABLE`` tokens but also to
+ ``word`` tokens (e.g. constant words). This is useful if some parts of a
+ command are optional. The type will be ``const char *``.
+- ``[no]`` will be passed as ``const char *no``.
+- Most pointers will be ``NULL`` when the argument is optional and the
+ user did not supply it. As noted in the table above, some prefix
+ struct type arguments are passed as pointers to all-zeroes structs,
+ not as ``NULL`` pointers.
+- If a parameter is not a pointer, but is optional and the user didn't use it,
+ the default value will be passed. Check the ``_str`` argument if you need to
+ determine whether the parameter was omitted.
+- If the definition contains multiple parameters with the same variable name,
+ they will be collapsed into a single function parameter. The python code will
+ detect if the types are compatible (i.e. IPv4 + IPv6 variants) and choose a
+ corresponding C type.
+- The standard DEFUN parameters (``self, vty, argc, argv``) are still present
+ and can be used. A DEFUN can simply be **edited into a DEFPY without further
+ changes and it will still work**; this allows easy forward migration.
+- A file may contain both ``DEFUN`` and ``DEFPY`` statements.
+
+Getting a parameter dump
+""""""""""""""""""""""""
+The clidef.py script can be called to get a list of DEFUNs/DEFPYs with the
+parameter name/type list:
+
+::
+
+ lib/clippy python/clidef.py --all-defun --show lib/plist.c > /dev/null
+
+The generated code is printed to stdout, the info dump to stderr. The
+``--all-defun`` argument will make it process DEFUN blocks as well as DEFPYs,
+which is useful prior to converting some DEFUNs. **The dump does not list the
+``_str`` arguments** to keep the output shorter.
+
+Note that the ``clidef.py`` script cannot be run with python directly, it needs
+to be run with *clippy* since the latter makes the CLI parser available.
+
+Include & Makefile requirements
+"""""""""""""""""""""""""""""""
+A source file that uses DEFPY needs to include the ``*_clippy.c`` file **before
+all DEFPY statements**:
+
+.. code-block:: c
+
+ /* GPL header */
+ #include ...
+ ...
+ #ifndef VTYSH_EXTRACT_PL
+ #include "daemon/filename_clippy.c"
+ #endif
+
+ DEFPY(...)
+ DEFPY(...)
+
+ install_element(...)
+
+This dependency needs to be marked in ``Makefile.am`` or ``subdir.am``: (there
+is no ordering requirement)
+
+.. code-block:: make
+
+ # ...
+
+ # if linked into a LTLIBRARY (.la/.so):
+ filename.lo: filename_clippy.c
+
+ # if linked into an executable or static library (.a):
+ filename.o: filename_clippy.c
+
+Handlers
+^^^^^^^^
+The block that follows a CLI definition is executed when a user enters input
+that matches the definition. Its function signature looks like this:
+
+.. code-block:: c
+
+ int (*func) (const struct cmd_element *, struct vty *, int, struct cmd_token *[]);
+
+The first argument is the command definition struct. The last argument is an
+ordered array of tokens that correspond to the path taken through the graph, and
+the argument just prior to that is the length of the array.
+
+The arrangement of the token array has changed from Quagga's CLI implementation.
+In the old system, missing arguments were padded with ``NULL`` so that the same
+parts of a command would show up at the same indices regardless of what was
+entered. The new system does not perform such padding and therefore it is
+generally *incorrect* to assume consistent indices in this array. As a simple
+example:
+
+Command definition:
+
+::
+
+ command [foo] <bar|baz>
+
+User enters:
+
+::
+
+ command foo bar
+
+Array:
+
+::
+
+ [0] -> command
+ [1] -> foo
+ [2] -> bar
+
+User enters:
+
+::
+
+ command baz
+
+Array:
+
+::
+
+ [0] -> command
+ [1] -> baz
+
+
+.. _cli-data-structures:
+
+Data Structures
+---------------
+On startup, the CLI parser sequentially parses each command string definition
+and constructs a directed graph with each token forming a node. This graph is
+the basis of the entire CLI system. It is used to match user input in order to
+generate command completions and match commands to functions.
+
+There is one graph per CLI node (not the same as a graph node in the CLI graph).
+The CLI node struct keeps a reference to its graph (see :file:`lib/command.h`).
+
+While most of the graph maintains the form of a tree, special constructs
+outlined in the Rules section introduce some quirks. ``<>``, ``[]`` and ``{}``
+form self-contained 'subgraphs'. Each subgraph is a tree except that all of the
+'leaves' actually share a child node. This helps with minimizing graph size and
+debugging.
+
+As a working example, here is the graph of the following command: ::
+
+ show [ip] bgp neighbors [<A.B.C.D|X:X::X:X|WORD>] [json]
+
+.. figure:: ../figures/cligraph.png
+ :align: center
+
+ Graph of example CLI command
+
+
+``FORK`` and ``JOIN`` nodes are plumbing nodes that don't correspond to user
+input. They're necessary in order to deduplicate these constructs where
+applicable.
+
+Options follow the same form, except that there is an edge from the ``FORK``
+node to the ``JOIN`` node. Since all of the subgraphs in the example command are
+optional, all of them have this edge.
+
+Keywords follow the same form, except that there is an edge from ``JOIN`` to
+``FORK``. Because of this the CLI graph cannot be called acyclic. There is
+special logic in the input matching code that keeps a stack of paths already
+taken through the node in order to disallow following the same path more than
+once.
+
+Variadics are a bit special; they have an edge back to themselves, which allows
+repeating the same input indefinitely.
+
+The leaves of the graph are nodes that have no out edges. These nodes are
+special; their data section does not contain a token, as most nodes do, or
+``NULL``, as in ``FORK``/``JOIN`` nodes, but instead has a pointer to a
+``cmd_element``. All paths through the graph that terminate on a leaf are
+guaranteed to be defined by that command. When a user enters a complete command,
+the command matcher tokenizes the input and executes a DFS on the CLI graph. If
+it is simultaneously able to exhaust all input (one input token per graph node),
+and then find exactly one leaf connected to the last node it reaches, then the
+input has matched the corresponding command and the command is executed. If it
+finds more than one node, then the command is ambiguous (more on this in
+deduplication). If it cannot exhaust all input, the command is unknown. If it
+exhausts all input but does not find an edge node, the command is incomplete.
+
+The parser uses an incremental strategy to build the CLI graph for a node. Each
+command is parsed into its own graph, and then this graph is merged into the
+overall graph. During this merge step, the parser makes a best-effort attempt to
+remove duplicate nodes. If it finds a node in the overall graph that is equal to
+a node in the corresponding position in the command graph, it will intelligently
+merge the properties from the node in the command graph into the
+already-existing node. Subgraphs are also checked for isomorphism and merged
+where possible. The definition of whether two nodes are 'equal' is based on the
+equality of some set of token properties; read the parser source for the most
+up-to-date definition of equality.
+
+When the parser is unable to deduplicate some complicated constructs, this can
+result in two identical paths through separate parts of the graph. If this
+occurs and the user enters input that matches these paths, they will receive an
+'ambiguous command' error and will be unable to execute the command. Most of the
+time the parser can detect and warn about duplicate commands, but it will not
+always be able to do this. Hence care should be taken before defining a new
+command to ensure it is not defined elsewhere.
+
+struct cmd\_token
+^^^^^^^^^^^^^^^^^
+
+.. code-block:: c
+
+ /* Command token struct. */
+ struct cmd_token
+ {
+ enum cmd_token_type type; // token type
+ uint8_t attr; // token attributes
+ bool allowrepeat; // matcher can match token repetitively?
+
+ char *text; // token text
+ char *desc; // token description
+ long long min, max; // for ranges
+ char *arg; // user input that matches this token
+ char *varname; // variable name
+ };
+
+This struct is used in the CLI graph to match input against. It is also used to
+pass user input to command handler functions, as it is frequently useful for
+handlers to have access to that information. When a command is matched, the
+sequence of ``cmd_tokens`` that form the matching path are duplicated and placed
+in order into ``*argv[]``. Before this happens the ``->arg`` field is set to
+point at the snippet of user input that matched it.
+
+For most nontrivial commands the handler function will need to determine which
+of the possible matching inputs was entered. Previously this was done by looking
+at the first few characters of input. This is now considered an anti-pattern and
+should be avoided. Instead, the ``->type`` or ``->text`` fields for this logic.
+The ``->type`` field can be used when the possible inputs differ in type. When
+the possible types are the same, use the ``->text`` field. This field has the
+full text of the corresponding token in the definition string and using it makes
+for much more readable code. An example is helpful.
+
+Command definition:
+
+::
+
+ command <(1-10)|foo|BAR>
+
+In this example, the user may enter any one of:
+- an integer between 1 and 10
+- "foo"
+- anything at all
+
+If the user enters "command f", then:
+
+::
+
+ argv[1]->type == WORD_TKN
+ argv[1]->arg == "f"
+ argv[1]->text == "foo"
+
+Range tokens have some special treatment; a token with ``->type == RANGE_TKN``
+will have the ``->min`` and ``->max`` fields set to the bounding values of the
+range.
+
+struct cmd\_element
+^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: c
+
+ struct cmd_node {
+ /* Node index. */
+ enum node_type node;
+
+ /* Prompt character at vty interface. */
+ const char *prompt;
+
+ /* Is this node's configuration goes to vtysh ? */
+ int vtysh;
+
+ /* Node's configuration write function */
+ int (*func)(struct vty *);
+
+ /* Node's command graph */
+ struct graph *cmdgraph;
+
+ /* Vector of this node's command list. */
+ vector cmd_vector;
+
+ /* Hashed index of command node list, for de-dupping primarily */
+ struct hash *cmd_hash;
+ };
+
+This struct corresponds to a CLI mode. The last three fields are most relevant
+here.
+
+cmdgraph
+ This is a pointer to the command graph that was described in the first part
+ of this section. It is the datastructure used for matching user input to
+ commands.
+
+cmd_vector
+ This is a list of all the ``struct cmd_element`` defined in the mode.
+
+cmd_hash
+ This is a hash table of all the ``struct cmd_element`` defined in the mode.
+ When ``install_element`` is called, it checks that the element it is given is
+ not already present in the hash table as a safeguard against duplicate calls
+ resulting in a command being defined twice, which renders the command
+ ambiguous.
+
+All ``struct cmd_node`` are themselves held in a static vector defined in
+:file:`lib/command.c` that defines the global CLI space.
+
+Command Abbreviation & Matching Priority
+----------------------------------------
+It is possible for users to elide parts of tokens when the CLI matcher does not
+need them to make an unambiguous match. This is best explained by example.
+
+Command definitions:
+
+::
+
+ command dog cow
+ command dog crow
+
+User input:
+
+::
+
+ c d c -> ambiguous command
+ c d co -> match "command dog cow"
+
+
+The parser will look ahead and attempt to disambiguate the input based on tokens
+later on in the input string.
+
+Command definitions:
+
+::
+
+ show ip bgp A.B.C.D
+ show ipv6 bgp X:X::X:X
+
+User enters:
+
+::
+
+ s i b 4.3.2.1 -> match "show ip bgp A.B.C.D"
+ s i b ::e0 -> match "show ipv6 bgp X:X::X:X"
+
+Reading left to right, both of these commands would be ambiguous since 'i' does
+not explicitly select either 'ip' or 'ipv6'. However, since the user later
+provides a token that matches only one of the commands (an IPv4 or IPv6 address)
+the parser is able to look ahead and select the appropriate command. This has
+some implications for parsing the ``*argv[]`` that is passed to the command
+handler.
+
+Now consider a command definition such as:
+
+::
+
+ command <foo|VAR>
+
+'foo' only matches the string 'foo', but 'VAR' matches any input, including
+'foo'. Who wins? In situations like this the matcher will always choose the
+'better' match, so 'foo' will win.
+
+Consider also:
+
+::
+
+ show <ip|ipv6> foo
+
+User input:
+
+::
+
+ show ip foo
+
+``ip`` partially matches ``ipv6`` but exactly matches ``ip``, so ``ip`` will
+win.
+
+Adding a CLI Node
+-----------------
+
+To add a new CLI node, you should:
+
+- define a new numerical node constant
+- define a node structure in the relevant daemon
+- call ``install_node()`` in the relevant daemon
+- define and install the new node in vtysh
+- define corresponding node entry commands in daemon and vtysh
+- add a new entry to the ``ctx_keywords`` dictionary in ``tools/frr-reload.py``
+
+Defining the numerical node constant
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Add your new node value to the enum before ``NODE_TYPE_MAX`` in
+``lib/command.h``:
+
+.. code-block:: c
+
+ enum node_type {
+ AUTH_NODE, // Authentication mode of vty interface.
+ VIEW_NODE, // View node. Default mode of vty interface.
+ [...]
+ MY_NEW_NODE,
+ NODE_TYPE_MAX, // maximum
+ };
+
+Defining a node structure
+^^^^^^^^^^^^^^^^^^^^^^^^^
+In your daemon-specific code where you define your new commands that
+attach to the new node, add a node definition:
+
+.. code-block:: c
+
+ static struct cmd_node my_new_node = {
+ .name = "my new node name",
+ .node = MY_NEW_NODE, // enum node_type lib/command.h
+ .parent_node = CONFIG_NODE,
+ .prompt = "%s(my-new-node-prompt)# ",
+ .config_write = my_new_node_config_write,
+ };
+
+You will need to define ``my_new_node_config_write(struct vty \*vty)``
+(or omit this field if you have no relevant configuration to save).
+
+Calling ``install_node()``
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+In the daemon's initialization function, before installing your new commands
+with ``install_element()``, add a call ``install_node(&my_new_node)``.
+
+Defining and installing the new node in vtysh
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+The build tools automatically collect command definitions for vtysh.
+However, new nodes must be coded in vtysh specifically.
+
+In ``vtysh/vtysh.c``, define a stripped-down node structure and
+call ``install_node()``:
+
+.. code-block:: c
+
+ static struct cmd_node my_new_node = {
+ .name = "my new node name",
+ .node = MY_NEW_NODE, /* enum node_type lib/command.h */
+ .parent_node = CONFIG_NODE,
+ .prompt = "%s(my-new-node-prompt)# ",
+ };
+ [...]
+ void vtysh_init_vty(void)
+ {
+ [...]
+ install_node(&my_new_node)
+ [...]
+ }
+
+Defining corresponding node entry commands in daemon and vtysh
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+The command that descends into the new node is typically programmed
+with ``VTY_PUSH_CONTEXT`` or equivalent in the daemon's CLI handler function.
+(If the CLI has been updated to use the new northbound architecture,
+``VTY_PUSH_XPATH`` is used instead.)
+
+In vtysh, you must implement a corresponding node change so that vtysh
+tracks the daemon's movement through the node tree.
+
+Although the build tools typically scan daemon code for CLI definitions
+to replicate their parsing in vtysh, the node-descent function in the
+daemon must be blocked from this replication so that a hand-coded
+skeleton can be written in ``vtysh.c``.
+
+Accordingly, use one of the ``*_NOSH`` macros such as ``DEFUN_NOSH``,
+``DEFPY_NOSH``, or ``DEFUN_YANG_NOSH`` for the daemon's node-descent
+CLI definition, and use ``DEFUNSH`` in ``vtysh.c`` for the vtysh equivalent.
+
+.. seealso:: :ref:`vtysh-special-defuns`
+
+Examples:
+
+``zebra_whatever.c``
+
+.. code-block:: c
+
+ DEFPY_NOSH(my_new_node,
+ my_new_node_cmd,
+ "my-new-node foo",
+ "New Thing\n"
+ "A foo\n")
+ {
+ [...]
+ VTY_PUSH_CONTEXT(MY_NEW_NODE, bar);
+ [...]
+ }
+
+
+``ripd_whatever.c``
+
+.. code-block:: c
+
+ DEFPY_YANG_NOSH(my_new_node,
+ my_new_node_cmd,
+ "my-new-node foo",
+ "New Thing\n"
+ "A foo\n")
+ {
+ [...]
+ VTY_PUSH_XPATH(MY_NEW_NODE, xbar);
+ [...]
+ }
+
+
+``vtysh.c``
+
+.. code-block:: c
+
+ DEFUNSH(VTYSH_ZEBRA, my_new_node,
+ my_new_node_cmd,
+ "my-new-node foo",
+ "New Thing\n"
+ "A foo\n")
+ {
+ vty->node = MY_NEW_NODE;
+ return CMD_SUCCESS;
+ }
+ [...]
+ install_element(CONFIG_NODE, &my_new_node_cmd);
+
+
+Adding a new entry to the ``ctx_keywords`` dictionary
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+In file ``tools/frr-reload.py``, the ``ctx_keywords`` dictionary
+describes the various node relationships.
+Add a new node entry at the appropriate level in this dictionary.
+
+.. code-block:: python
+
+ ctx_keywords = {
+ [...]
+ "key chain ": {
+ "key ": {}
+ },
+ [...]
+ "my-new-node": {},
+ [...]
+ }
+
+
+
+Inspection & Debugging
+----------------------
+
+Permutations
+^^^^^^^^^^^^
+It is sometimes useful to check all the possible combinations of input that
+would match an arbitrary definition string. There is a tool in
+:file:`tools/permutations` that reads CLI definition strings on ``stdin`` and
+prints out all matching input permutations. It also dumps a text representation
+of the graph, which is more useful for debugging than anything else. It looks
+like this:
+
+.. code-block:: shell
+
+ $ ./permutations "show [ip] bgp [<view|vrf> WORD]"
+
+ show ip bgp view WORD
+ show ip bgp vrf WORD
+ show ip bgp
+ show bgp view WORD
+ show bgp vrf WORD
+ show bgp
+
+This functionality is also built into VTY/VTYSH; :clicmd:`list permutations`
+will list all possible matching input permutations in the current CLI node.
+
+Graph Inspection
+^^^^^^^^^^^^^^^^
+When in the Telnet or VTYSH console, :clicmd:`show cli graph` will dump the
+entire command space of the current mode in the DOT graph language. This can be
+fed into one of the various GraphViz layout engines, such as ``dot``,
+``neato``, etc.
+
+For example, to generate an image of the entire command space for the top-level
+mode (``ENABLE_NODE``):
+
+.. code-block:: shell
+
+ sudo vtysh -c 'show cli graph' | dot -Tjpg -Grankdir=LR > graph.jpg
+
+To do the same for the BGP mode:
+
+.. code-block:: shell
+
+ sudo vtysh -c 'conf t' -c 'router bgp' -c 'show cli graph' | dot -Tjpg -Grankdir=LR > bgpgraph.jpg
+
+This information is very helpful when debugging command resolution, tracking
+down duplicate / ambiguous commands, and debugging patches to the CLI graph
+builder.