lib: Break up functions, begin matcher

Moved test hook out of command.c into vtysh.c,
renamed graph modules, added matching code

Signed-off-by: Quentin Young <qlyoung@cumulusnetworks.com>
This commit is contained in:
Quentin Young 2016-07-19 21:14:27 +00:00
parent 340a2b4ac0
commit 9d0662e009
10 changed files with 310 additions and 25 deletions

View File

@ -11,7 +11,7 @@ libzebra_la_LDFLAGS = -version-info 0:0:0
libzebra_la_SOURCES = \ libzebra_la_SOURCES = \
network.c pid_output.c getopt.c getopt1.c daemon.c \ network.c pid_output.c getopt.c getopt1.c daemon.c \
checksum.c vector.c linklist.c vty.c \ checksum.c vector.c linklist.c vty.c \
cmdtree.c command_parse.y command_lex.l \ command_graph.c command_parse.y command_lex.l command_match.c grammar_sandbox.c \
command.c \ command.c \
sockunion.c prefix.c thread.c if.c memory.c buffer.c table.c hash.c \ sockunion.c prefix.c thread.c if.c memory.c buffer.c table.c hash.c \
filter.c routemap.c distribute.c stream.c str.c log.c plist.c \ filter.c routemap.c distribute.c stream.c str.c log.c plist.c \
@ -28,7 +28,7 @@ libzebra_la_LIBADD = @LIB_REGEX@ @LIBCAP@
pkginclude_HEADERS = \ pkginclude_HEADERS = \
buffer.h checksum.h filter.h getopt.h hash.h \ buffer.h checksum.h filter.h getopt.h hash.h \
if.h linklist.h log.h \ if.h linklist.h log.h \
cmdtree.h \ command_graph.h command_match.h grammar_sandbox.h \
command.h \ command.h \
memory.h network.h prefix.h routemap.h distribute.h sockunion.h \ memory.h network.h prefix.h routemap.h distribute.h sockunion.h \
str.h stream.h table.h thread.h vector.h version.h vty.h zebra.h \ str.h stream.h table.h thread.h vector.h version.h vty.h zebra.h \

View File

@ -34,8 +34,6 @@ Boston, MA 02111-1307, USA. */
#include "workqueue.h" #include "workqueue.h"
#include "vrf.h" #include "vrf.h"
#include "grammar_sandbox.c"
/* Command vector which includes some level of command lists. Normally /* Command vector which includes some level of command lists. Normally
each daemon maintains each own cmdvec. */ each daemon maintains each own cmdvec. */
vector cmdvec = NULL; vector cmdvec = NULL;
@ -4077,10 +4075,6 @@ cmd_init (int terminal)
install_element (ENABLE_NODE, &show_version_cmd); install_element (ENABLE_NODE, &show_version_cmd);
install_element (ENABLE_NODE, &show_commandtree_cmd); install_element (ENABLE_NODE, &show_commandtree_cmd);
/**/
grammar_sandbox_init();
/**/
if (terminal) if (terminal)
{ {
install_element (ENABLE_NODE, &config_terminal_length_cmd); install_element (ENABLE_NODE, &config_terminal_length_cmd);

View File

@ -7,7 +7,7 @@
*/ */
#include <zebra.h> #include <zebra.h>
#include "cmdtree.h" #include "command_graph.h"
#include "memory.h" #include "memory.h"
struct graph_node * struct graph_node *

View File

@ -1,3 +1,6 @@
#ifndef COMMAND_GRAPH_H
#define COMMAND_GRAPH_H
#include "vty.h" #include "vty.h"
#include "vector.h" #include "vector.h"
@ -78,3 +81,5 @@ new_node(enum graph_node_type);
*/ */
extern void extern void
walk_graph(struct graph_node *, int); walk_graph(struct graph_node *, int);
#endif

174
lib/command_match.c Normal file
View File

@ -0,0 +1,174 @@
#include <zebra.h>
#include "memory.h"
#include "command_match.h"
enum match_type
match_command (struct graph_node *start, enum filter_type filter, const char *input)
{
// match input on DFA
return exact_match;
}
#define IPV4_ADDR_STR "0123456789."
#define IPV4_PREFIX_STR "0123456789./"
enum match_type
cmd_ipv4_match (const char *str)
{
struct sockaddr_in sin_dummy;
if (str == NULL)
return partly_match;
if (strspn (str, IPV4_ADDR_STR) != strlen (str))
return no_match;
if (inet_pton(AF_INET, str, &sin_dummy.sin_addr) != 1)
return no_match;
return exact_match;
}
enum match_type
cmd_ipv4_prefix_match (const char *str)
{
struct sockaddr_in sin_dummy;
const char *delim = "/\0";
char *dupe, *prefix, *mask, *context, *endptr;
int nmask = -1;
if (str == NULL)
return partly_match;
if (strspn (str, IPV4_PREFIX_STR) != strlen (str))
return no_match;
/* tokenize to address + mask */
dupe = XMALLOC(MTYPE_TMP, strlen(str)+1);
strncpy(dupe, str, strlen(str)+1);
prefix = strtok_r(dupe, delim, &context);
mask = strtok_r(NULL, delim, &context);
if (!mask)
return partly_match;
/* validate prefix */
if (inet_pton(AF_INET, prefix, &sin_dummy.sin_addr) != 1)
return no_match;
/* validate mask */
nmask = strtol (mask, &endptr, 10);
if (*endptr != '\0' || nmask < 0 || nmask > 32)
return no_match;
XFREE(MTYPE_TMP, dupe);
return exact_match;
}
#define IPV6_ADDR_STR "0123456789abcdefABCDEF:."
#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:./"
#ifdef HAVE_IPV6
enum match_type
cmd_ipv6_match (const char *str)
{
struct sockaddr_in6 sin6_dummy;
int ret;
if (str == NULL)
return partly_match;
if (strspn (str, IPV6_ADDR_STR) != strlen (str))
return no_match;
ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
if (ret == 1)
return exact_match;
return no_match;
}
enum match_type
cmd_ipv6_prefix_match (const char *str)
{
struct sockaddr_in6 sin6_dummy;
const char *delim = "/\0";
char *dupe, *prefix, *mask, *context, *endptr;
int nmask = -1;
if (str == NULL)
return partly_match;
if (strspn (str, IPV6_PREFIX_STR) != strlen (str))
return no_match;
/* tokenize to address + mask */
dupe = XMALLOC(MTYPE_TMP, strlen(str)+1);
strncpy(dupe, str, strlen(str)+1);
prefix = strtok_r(dupe, delim, &context);
mask = strtok_r(NULL, delim, &context);
if (!mask)
return partly_match;
/* validate prefix */
if (inet_pton(AF_INET6, prefix, &sin6_dummy.sin6_addr) != 1)
return no_match;
/* validate mask */
nmask = strtol (mask, &endptr, 10);
if (*endptr != '\0' || nmask < 0 || nmask > 128)
return no_match;
XFREE(MTYPE_TMP, dupe);
return exact_match;
}
#endif
enum match_type
cmd_range_match (struct graph_node *rangenode, const char *str)
{
char *endptr = NULL;
signed long long val;
if (str == NULL)
return 1;
val = strtoll (str, &endptr, 10);
if (*endptr != '\0')
return 0;
val = llabs(val);
if (val < rangenode->min || val > rangenode->max)
return no_match;
else
return exact_match;
}
enum match_type
cmd_word_match(struct graph_node *wordnode,
enum filter_type filter,
const char *word)
{
if (filter == FILTER_RELAXED)
if (!word || !strlen(word))
return partly_match;
if (!word)
return no_match;
if (filter == FILTER_RELAXED && !strncmp(wordnode->text, word, strlen(word)))
{
if (!strcmp(wordnode->text, word))
return exact_match;
return partly_match;
}
if (filter == FILTER_STRICT && !strcmp(wordnode->text, word))
return exact_match;
return no_match;
}

69
lib/command_match.h Normal file
View File

@ -0,0 +1,69 @@
#ifndef COMMAND_MATCH_H
#define COMMAND_MATCH_H
#include "command_graph.h"
/**
* Filter types. These tell the parser whether to allow
* partial matching on tokens.
*/
enum filter_type
{
FILTER_RELAXED,
FILTER_STRICT
};
/**
* Command matcher result value.
*/
enum matcher_rv
{
MATCHER_OK,
MATCHER_COMPLETE,
MATCHER_INCOMPLETE,
MATCHER_NO_MATCH,
MATCHER_AMBIGUOUS,
MATCHER_EXCEED_ARGC_MAX
};
/* Completion match types. */
enum match_type
{
no_match,
partly_match,
exact_match
};
/**
* Defines which matcher_rv values constitute
* an error. Should be used against matcher_rv
* return values to do basic error checking.
*/
#define MATCHER_ERROR(matcher_rv) \
( (matcher_rv) == MATCHER_INCOMPLETE \
|| (matcher_rv) == MATCHER_NO_MATCH \
|| (matcher_rv) == MATCHER_AMBIGUOUS \
|| (matcher_rv) == MATCHER_EXCEED_ARGC_MAX \
)
enum match_type
cmd_ipv4_match (const char *);
enum match_type
cmd_ipv4_prefix_match (const char *);
enum match_type
cmd_ipv6_match (const char *);
enum match_type
cmd_ipv6_prefix_match (const char *);
enum match_type
cmd_range_match (struct graph_node *, const char *str);
enum match_type
cmd_word_match (struct graph_node *, enum filter_type, const char *);
enum match_type
match_command (struct graph_node *, enum filter_type, const char *);
#endif

View File

@ -1,5 +1,15 @@
/*
* Command format string parser.
*
* Turns a command definition into a DFA that together with the functions
* provided in command_match.c may be used to map command line input to a
* function.
*
* @author Quentin Young <qlyoung@cumulusnetworks.com>
*/
%{ %{
#include "cmdtree.h" #include "command_graph.h"
extern int yylex(void); extern int yylex(void);
extern void yyerror(const char *); extern void yyerror(const char *);
@ -8,16 +18,22 @@ extern void yyerror(const char *);
#define YYDEBUG 1 #define YYDEBUG 1
%} %}
%code provides { %code provides {
extern struct graph_node *cmd_parse_format_new(const char *, const char *, struct graph_node *); extern struct
extern void set_buffer_string(const char *); graph_node *cmd_parse_format(const char *,
const char *,
struct graph_node *);
extern void
set_buffer_string(const char *);
} }
/* valid types for tokens */
%union{ %union{
int integer; int integer;
char *string; char *string;
struct graph_node *node; struct graph_node *node;
} }
/* some helpful state variables */
%{ %{
struct graph_node *startnode, // command root node struct graph_node *startnode, // command root node
*currnode, // current node *currnode, // current node
@ -66,7 +82,7 @@ sentence_root: WORD
$$ = new_node(WORD_GN); $$ = new_node(WORD_GN);
$$->text = strdup(yylval.string); $$->text = strdup(yylval.string);
fprintf(stderr, ">>>>>>>> YYLVAL.STRING: %s\n", yylval.string); fprintf(stderr, ">>>>>>>> YYLVAL.STRING: %s\n", yylval.string);
fprintf(stderr, ">>>>>>>> TEXT: %s\n", $$->text); fprintf(stderr, ">>>>>>>> TEXT: %s\n", $$->text);
currnode = $$; currnode = $$;
currnode->is_root = 1; currnode->is_root = 1;
@ -270,7 +286,7 @@ void yyerror(char const *message) {
} }
struct graph_node * struct graph_node *
cmd_parse_format_new(const char *string, const char *desc, struct graph_node *start) cmd_parse_format(const char *string, const char *desc, struct graph_node *start)
{ {
fprintf(stderr, "parsing: %s\n", string); fprintf(stderr, "parsing: %s\n", string);

View File

@ -1,17 +1,14 @@
#include "command.h" #include "command.h"
#include "command_graph.h"
#include "command_parse.h" #include "command_parse.h"
#include "cmdtree.h" #include "command_match.h"
#define GRAMMAR_STR "CLI grammar sandbox\n" #define GRAMMAR_STR "CLI grammar sandbox\n"
struct graph_node * nodegraph; struct graph_node * nodegraph;
DEFUN (grammar_test, /*
grammar_test_cmd, char* combine_vararg(char* argv, int argc) {
"grammar parse .COMMAND",
GRAMMAR_STR
"command to pass to new parser\n")
{
size_t linesize = 0; size_t linesize = 0;
for (int i = 0; i < argc; i++) for (int i = 0; i < argc; i++)
linesize += strlen(argv[i]) + 1; linesize += strlen(argv[i]) + 1;
@ -24,8 +21,19 @@ DEFUN (grammar_test,
strcat(cat, " "); strcat(cat, " ");
} }
//struct graph_node *result = new_node(NUL_GN); return cat;
cmd_parse_format_new((const char*) cat, "lol", nodegraph); }
*/
DEFUN (grammar_test,
grammar_test_cmd,
"grammar parse .COMMAND",
GRAMMAR_STR
"command to pass to new parser\n")
{
const char* command = argv_concat(argv, argc, 0);
cmd_parse_format(command, "lol", nodegraph);
walk_graph(nodegraph, 0); walk_graph(nodegraph, 0);
return CMD_SUCCESS; return CMD_SUCCESS;
@ -37,8 +45,20 @@ DEFUN (grammar_test_show,
GRAMMAR_STR GRAMMAR_STR
"print current accumulated DFA\n") "print current accumulated DFA\n")
{ {
walk_graph(nodegraph, 0); walk_graph(nodegraph, 0);
return CMD_SUCCESS; return CMD_SUCCESS;
}
DEFUN (grammar_test_match,
grammar_test_match_cmd,
"grammar match .COMMAND",
GRAMMAR_STR
"attempt to match input on DFA\n"
"command to match")
{
const char* command = argv_concat(argv, argc, 0);
match_command(nodegraph, FILTER_STRICT, command);
return CMD_SUCCESS;
} }
@ -48,4 +68,5 @@ void grammar_sandbox_init() {
nodegraph = new_node(NUL_GN); nodegraph = new_node(NUL_GN);
install_element (ENABLE_NODE, &grammar_test_cmd); install_element (ENABLE_NODE, &grammar_test_cmd);
install_element (ENABLE_NODE, &grammar_test_show_cmd); install_element (ENABLE_NODE, &grammar_test_show_cmd);
install_element (ENABLE_NODE, &grammar_test_match_cmd);
} }

2
lib/grammar_sandbox.h Normal file
View File

@ -0,0 +1,2 @@
void
grammar_sandbox_init(void);

View File

@ -42,6 +42,8 @@
#include "bgpd/bgp_vty.h" #include "bgpd/bgp_vty.h"
#include "vrf.h" #include "vrf.h"
#include "lib/grammar_sandbox.h"
/* Struct VTY. */ /* Struct VTY. */
struct vty *vty; struct vty *vty;
@ -3076,4 +3078,6 @@ vtysh_init_vty (void)
install_element (CONFIG_NODE, &vtysh_enable_password_text_cmd); install_element (CONFIG_NODE, &vtysh_enable_password_text_cmd);
install_element (CONFIG_NODE, &no_vtysh_enable_password_cmd); install_element (CONFIG_NODE, &no_vtysh_enable_password_cmd);
/* grammar sandbox */
grammar_sandbox_init();
} }