From 961e3c8428be81ae74f7e8ee293b16808a74d45e Mon Sep 17 00:00:00 2001 From: Dave Barach Date: Thu, 18 Jun 2020 17:04:18 -0400 Subject: vlib: add recursive macro expander to debug cli All of the pieces have been sitting around for years. Added several debug CLI commands: "define " "undefine " "show macro [noeval]" "echo " Macros may refer to other macros. To defer evaluation: "define foo \$(bar)" or some such. The macro evaluator is not smart about "define foo \$(foo)" or more complicated circular definitions, so don't do that. Environment variables are available, simply use $ The macro expander has a table of (overrideable) builtin names, which are evaluated by calling functions. Simple example: echo $USER define ip1 192.168.1.1/24 define ip2 192.168.2.1/24 loop create loop create set int ip address loop0 $ip1 set int ip address loop1 $ip2 show int addr show macro undefine ip1 undefine ip2 Type: feature Signed-off-by: Dave Barach Change-Id: I08a800647bac573d8ae3cfd75c40061d41c5f976 --- src/vat/main.c | 4 +- src/vat/vat.h | 2 +- src/vlib/unix/cli.c | 143 ++++++++++++++++++++++++++++++++++++++++++++- src/vppinfra/macros.c | 88 +++++++++++++++++++++++----- src/vppinfra/macros.h | 33 ++++++----- src/vppinfra/test_macros.c | 4 +- 6 files changed, 237 insertions(+), 37 deletions(-) diff --git a/src/vat/main.c b/src/vat/main.c index 139594944d2..a6779c591ce 100644 --- a/src/vat/main.c +++ b/src/vat/main.c @@ -208,14 +208,14 @@ init_error_string_table (vat_main_t * vam) } static i8 * -eval_current_file (macro_main_t * mm, i32 complain) +eval_current_file (clib_macro_main_t * mm, i32 complain) { vat_main_t *vam = &vat_main; return ((i8 *) format (0, "%s%c", vam->current_file, 0)); } static i8 * -eval_current_line (macro_main_t * mm, i32 complain) +eval_current_line (clib_macro_main_t * mm, i32 complain) { vat_main_t *vam = &vat_main; return ((i8 *) format (0, "%d%c", vam->input_line_number, 0)); diff --git a/src/vat/vat.h b/src/vat/vat.h index bcaddfe0126..3e9476f0a66 100644 --- a/src/vat/vat.h +++ b/src/vat/vat.h @@ -158,7 +158,7 @@ typedef struct uword *help_by_name; /* macro table */ - macro_main_t macro_main; + clib_macro_main_t macro_main; /* Errors by number */ uword *error_string_by_error_number; diff --git a/src/vlib/unix/cli.c b/src/vlib/unix/cli.c index b553e12bac2..6b02fdf9bf5 100644 --- a/src/vlib/unix/cli.c +++ b/src/vlib/unix/cli.c @@ -61,6 +61,7 @@ #include #include #include +#include /** ANSI escape code. */ #define ESC "\x1b" @@ -491,6 +492,10 @@ typedef struct /** List of new sessions */ unix_cli_new_session_t *new_sessions; + + /* Macro expander */ + clib_macro_main_t macro_main; + } unix_cli_main_t; /** CLI global state */ @@ -2563,6 +2568,23 @@ more: int rv __attribute__ ((unused)) = write (um->log_fd, lv, vec_len (lv)); } + /* Run the command through the macro processor */ + if (vec_len (cf->current_command)) + { + u8 *expanded; + vec_validate (cf->current_command, vec_len (cf->current_command)); + cf->current_command[vec_len (cf->current_command) - 1] = 0; + /* The macro expander expects proper C-strings, not vectors */ + expanded = (u8 *) clib_macro_eval (&cm->macro_main, + (i8 *) cf->current_command, + 1 /* complain */ ); + /* Macro processor NULL terminates the return */ + _vec_len (expanded) -= 1; + vec_reset_length (cf->current_command); + vec_append (cf->current_command, expanded); + vec_free (expanded); + } + /* Build an unformat structure around our command */ unformat_init_vector (&input, cf->current_command); @@ -2896,6 +2918,8 @@ unix_cli_file_add (unix_cli_main_t * cm, char *name, int fd) cf->clib_file_index = clib_file_add (fm, &template); cf->output_vector = 0; cf->input_vector = 0; + vec_validate (cf->current_command, 0); + _vec_len (cf->current_command) = 0; vlib_start_process (vm, n->runtime_index); @@ -3905,6 +3929,123 @@ VLIB_CLI_COMMAND (cli_unix_wait_cmd, static) = { }; /* *INDENT-ON* */ +static clib_error_t * +echo_cmd (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + + /* Get a line of input. */ + if (!unformat_user (input, unformat_line_input, line_input)) + { + vlib_cli_output (vm, ""); + return 0; + } + + vlib_cli_output (vm, "%v", line_input->buffer); + + unformat_free (line_input); + return 0; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (cli_unix_echo_cmd, static) = { + .path = "echo", + .short_help = "echo ", + .function = echo_cmd, +}; +/* *INDENT-ON* */ + +static clib_error_t * +define_cmd_fn (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + u8 *macro_name; + unformat_input_t _line_input, *line_input = &_line_input; + unix_cli_main_t *cm = &unix_cli_main; + clib_error_t *error; + + if (!unformat (input, "%s", ¯o_name)) + return clib_error_return (0, "missing variable name..."); + + /* Remove white space */ + (void) unformat (input, ""); + + /* Get a line of input. */ + if (!unformat_user (input, unformat_line_input, line_input)) + { + error = clib_error_return (0, "missing value for '%s'...", macro_name); + vec_free (macro_name); + return error; + } + /* the macro expander expects c-strings, not vectors... */ + vec_add1 (line_input->buffer, 0); + clib_macro_set_value (&cm->macro_main, (char *) macro_name, + (char *) line_input->buffer); + vec_free (macro_name); + unformat_free (line_input); + return 0; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (define_cmd, static) = { + .path = "define", + .short_help = "define ", + .function = define_cmd_fn, +}; + +/* *INDENT-ON* */ + +static clib_error_t * +undefine_cmd_fn (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + u8 *macro_name; + unix_cli_main_t *cm = &unix_cli_main; + + if (!unformat (input, "%s", ¯o_name)) + return clib_error_return (0, "missing variable name..."); + + if (clib_macro_unset (&cm->macro_main, (char *) macro_name)) + vlib_cli_output (vm, "%s wasn't set...", macro_name); + + vec_free (macro_name); + return 0; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (undefine_cmd, static) = { + .path = "undefine", + .short_help = "undefine ", + .function = undefine_cmd_fn, +}; +/* *INDENT-ON* */ + +static clib_error_t * +show_macro_cmd_fn (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + unix_cli_main_t *cm = &unix_cli_main; + int evaluate = 1; + + if (unformat (input, "noevaluate %=", &evaluate, 0)) + ; + else if (unformat (input, "noeval %=", &evaluate, 0)) + ; + + vlib_cli_output (vm, "%U", format_clib_macro_main, &cm->macro_main, + evaluate); + return 0; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (show_macro, static) = { + .path = "show macro", + .short_help = "show macro [noevaluate]", + .function = show_macro_cmd_fn, +}; +/* *INDENT-ON* */ + static clib_error_t * unix_cli_init (vlib_main_t * vm) { @@ -3913,7 +4054,7 @@ unix_cli_init (vlib_main_t * vm) /* Breadcrumb to indicate the new session process * has not been started */ cm->new_session_process_node_index = ~0; - + clib_macro_init (&cm->macro_main); return 0; } diff --git a/src/vppinfra/macros.c b/src/vppinfra/macros.c index 6649277d897..bc6df55b39d 100644 --- a/src/vppinfra/macros.c +++ b/src/vppinfra/macros.c @@ -1,9 +1,9 @@ /* - macros.c - a simple macro expander - - Copyright (c) 2010, 2014 Cisco and/or its affiliates. - - * Licensed under the Apache License, Version 2.0 (the "License"); + * macros.c - a simple macro expander + * + * Copyright (c) 2010-2020 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * @@ -28,10 +28,10 @@ macro_isalnum (i8 c) } static i8 * -builtin_eval (macro_main_t * mm, i8 * varname, i32 complain) +builtin_eval (clib_macro_main_t * mm, i8 * varname, i32 complain) { uword *p; - i8 *(*fp) (macro_main_t *, i32); + i8 *(*fp) (clib_macro_main_t *, i32); p = hash_get_mem (mm->the_builtin_eval_hash, varname); if (p == 0) @@ -41,7 +41,7 @@ builtin_eval (macro_main_t * mm, i8 * varname, i32 complain) } int -clib_macro_unset (macro_main_t * mm, char *name) +clib_macro_unset (clib_macro_main_t * mm, char *name) { hash_pair_t *p; u8 *key, *value; @@ -61,7 +61,7 @@ clib_macro_unset (macro_main_t * mm, char *name) } int -clib_macro_set_value (macro_main_t * mm, char *name, char *value) +clib_macro_set_value (clib_macro_main_t * mm, char *name, char *value) { u8 *key_copy, *value_copy; int rv; @@ -76,7 +76,7 @@ clib_macro_set_value (macro_main_t * mm, char *name, char *value) } i8 * -clib_macro_get_value (macro_main_t * mm, char *name) +clib_macro_get_value (clib_macro_main_t * mm, char *name) { uword *p; @@ -92,7 +92,7 @@ clib_macro_get_value (macro_main_t * mm, char *name) * looks up $foobar in the variable table. */ i8 * -clib_macro_eval (macro_main_t * mm, i8 * s, i32 complain) +clib_macro_eval (clib_macro_main_t * mm, i8 * s, i32 complain) { i8 *rv = 0; i8 *varname, *varvalue; @@ -189,7 +189,7 @@ clib_macro_eval (macro_main_t * mm, i8 * s, i32 complain) * looks up $foobar in the variable table. */ i8 * -clib_macro_eval_dollar (macro_main_t * mm, i8 * s, i32 complain) +clib_macro_eval_dollar (clib_macro_main_t * mm, i8 * s, i32 complain) { i8 *s2; i8 *rv; @@ -201,14 +201,14 @@ clib_macro_eval_dollar (macro_main_t * mm, i8 * s, i32 complain) } void -clib_macro_add_builtin (macro_main_t * mm, char *name, void *eval_fn) +clib_macro_add_builtin (clib_macro_main_t * mm, char *name, void *eval_fn) { hash_set_mem (mm->the_builtin_eval_hash, name, (uword) eval_fn); } #ifdef CLIB_UNIX static i8 * -eval_hostname (macro_main_t * mm, i32 complain) +eval_hostname (clib_macro_main_t * mm, i32 complain) { char tmp[128]; if (gethostname (tmp, sizeof (tmp))) @@ -218,7 +218,7 @@ eval_hostname (macro_main_t * mm, i32 complain) #endif void -clib_macro_init (macro_main_t * mm) +clib_macro_init (clib_macro_main_t * mm) { if (mm->the_builtin_eval_hash != 0) { @@ -235,7 +235,7 @@ clib_macro_init (macro_main_t * mm) } void -clib_macro_free (macro_main_t * mm) +clib_macro_free (clib_macro_main_t * mm) { hash_pair_t *p; u8 **strings_to_free = 0; @@ -257,6 +257,62 @@ clib_macro_free (macro_main_t * mm) hash_free (mm->the_value_table_hash); } +typedef struct +{ + u8 *name; + u8 *value; +} name_sort_t; + +static int +name_compare (void *a1, void *a2) +{ + name_sort_t *ns1 = a1; + name_sort_t *ns2 = a2; + + return strcmp ((char *) ns1->name, (char *) ns2->name); +} + + +u8 * +format_clib_macro_main (u8 * s, va_list * args) +{ + clib_macro_main_t *mm = va_arg (*args, clib_macro_main_t *); + int evaluate = va_arg (*args, int); + hash_pair_t *p; + name_sort_t *nses = 0, *ns; + int i; + + /* *INDENT-OFF* */ + hash_foreach_pair (p, mm->the_value_table_hash, + ({ + vec_add2 (nses, ns, 1); + ns->name = (u8 *)(p->key); + ns->value = (u8 *)(p->value[0]); + })); + /* *INDENT-ON* */ + + if (vec_len (nses) == 0) + return s; + + vec_sort_with_function (nses, name_compare); + + for (i = 0; i < vec_len (nses); i++) + { + s = format (s, "%-20s", nses[i].name); + if (evaluate == 0) + s = format (s, "%s\n", nses[i].value); + else + { + u8 *rv = (u8 *) clib_macro_eval_dollar (mm, (i8 *) nses[i].name, + 0 /* no complain */ ); + s = format (s, "%s\n", rv); + vec_free (rv); + } + } + return s; +} + + /* * fd.io coding-style-patch-verification: ON * diff --git a/src/vppinfra/macros.h b/src/vppinfra/macros.h index 5c2e7033f8d..54ceda7016b 100644 --- a/src/vppinfra/macros.h +++ b/src/vppinfra/macros.h @@ -1,9 +1,9 @@ /* - macros.h - definitions for a simple macro expander - - Copyright (c) 2010, 2014 Cisco and/or its affiliates. - - * Licensed under the Apache License, Version 2.0 (the "License"); + * macros.h - definitions for a simple macro expander + * + * Copyright (c) 2010-2020 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * @@ -32,16 +32,19 @@ typedef struct { uword *the_builtin_eval_hash; uword *the_value_table_hash; -} macro_main_t; - -int clib_macro_unset (macro_main_t * mm, char *name); -int clib_macro_set_value (macro_main_t * mm, char *name, char *value); -void clib_macro_add_builtin (macro_main_t * mm, char *name, void *eval_fn); -i8 *clib_macro_get_value (macro_main_t * mm, char *name); -i8 *clib_macro_eval (macro_main_t * mm, i8 * s, i32 complain); -i8 *clib_macro_eval_dollar (macro_main_t * mm, i8 * s, i32 complain); -void clib_macro_init (macro_main_t * mm); -void clib_macro_free (macro_main_t * mm); +} clib_macro_main_t; + +int clib_macro_unset (clib_macro_main_t * mm, char *name); +int clib_macro_set_value (clib_macro_main_t * mm, char *name, char *value); +void clib_macro_add_builtin (clib_macro_main_t * mm, char *name, + void *eval_fn); +i8 *clib_macro_get_value (clib_macro_main_t * mm, char *name); +i8 *clib_macro_eval (clib_macro_main_t * mm, i8 * s, i32 complain); +i8 *clib_macro_eval_dollar (clib_macro_main_t * mm, i8 * s, i32 complain); +void clib_macro_init (clib_macro_main_t * mm); +void clib_macro_free (clib_macro_main_t * mm); + +format_function_t format_clib_macro_main; #endif /* included_macros_h */ diff --git a/src/vppinfra/test_macros.c b/src/vppinfra/test_macros.c index cf6172ab1f1..f130604d691 100644 --- a/src/vppinfra/test_macros.c +++ b/src/vppinfra/test_macros.c @@ -16,12 +16,12 @@ #include -macro_main_t macro_main; +clib_macro_main_t clib_macro_main; int test_macros_main (unformat_input_t * input) { - macro_main_t *mm = ¯o_main; + clib_macro_main_t *mm = &clib_macro_main; clib_macro_init (mm); -- cgit 1.2.3-korg