From 7cd468a3d7dee7d6c92f69a0bb7061ae208ec727 Mon Sep 17 00:00:00 2001 From: Damjan Marion Date: Mon, 19 Dec 2016 23:05:39 +0100 Subject: Reorganize source tree to use single autotools instance Change-Id: I7b51f88292e057c6443b12224486f2d0c9f8ae23 Signed-off-by: Damjan Marion --- src/vlib/cli.c | 1173 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1173 insertions(+) create mode 100644 src/vlib/cli.c (limited to 'src/vlib/cli.c') diff --git a/src/vlib/cli.c b/src/vlib/cli.c new file mode 100644 index 00000000000..2d141115857 --- /dev/null +++ b/src/vlib/cli.c @@ -0,0 +1,1173 @@ +/* + * Copyright (c) 2015 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: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * cli.c: command line interface + * + * Copyright (c) 2008 Eliot Dresselhaus + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * 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 OR COPYRIGHT HOLDERS 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. + */ + +#include +#include + +/* Root of all show commands. */ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (vlib_cli_show_command, static) = { + .path = "show", + .short_help = "Show commands", +}; +/* *INDENT-ON* */ + +/* Root of all clear commands. */ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (vlib_cli_clear_command, static) = { + .path = "clear", + .short_help = "Clear commands", +}; +/* *INDENT-ON* */ + +/* Root of all set commands. */ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (vlib_cli_set_command, static) = { + .path = "set", + .short_help = "Set commands", +}; +/* *INDENT-ON* */ + +/* Root of all test commands. */ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (vlib_cli_test_command, static) = { + .path = "test", + .short_help = "Test commands", +}; +/* *INDENT-ON* */ + +/* Returns bitmap of commands which match key. */ +static uword * +vlib_cli_sub_command_match (vlib_cli_command_t * c, unformat_input_t * input) +{ + int i, n; + uword *match = 0; + vlib_cli_parse_position_t *p; + + unformat_skip_white_space (input); + + for (i = 0;; i++) + { + uword k; + + k = unformat_get_input (input); + switch (k) + { + case 'a' ... 'z': + case 'A' ... 'Z': + case '0' ... '9': + case '-': + case '_': + break; + + case ' ': + case '\t': + case '\r': + case '\n': + case UNFORMAT_END_OF_INPUT: + /* White space or end of input removes any non-white + matches that were before possible. */ + if (i < vec_len (c->sub_command_positions) + && clib_bitmap_count_set_bits (match) > 1) + { + p = vec_elt_at_index (c->sub_command_positions, i); + for (n = 0; n < vec_len (p->bitmaps); n++) + match = clib_bitmap_andnot (match, p->bitmaps[n]); + } + goto done; + + default: + unformat_put_input (input); + goto done; + } + + if (i >= vec_len (c->sub_command_positions)) + { + no_match: + clib_bitmap_free (match); + return 0; + } + + p = vec_elt_at_index (c->sub_command_positions, i); + if (vec_len (p->bitmaps) == 0) + goto no_match; + + n = k - p->min_char; + if (n < 0 || n >= vec_len (p->bitmaps)) + goto no_match; + + if (i == 0) + match = clib_bitmap_dup (p->bitmaps[n]); + else + match = clib_bitmap_and (match, p->bitmaps[n]); + + if (clib_bitmap_is_zero (match)) + goto no_match; + } + +done: + return match; +} + +/* Looks for string based sub-input formatted { SUB-INPUT }. */ +uword +unformat_vlib_cli_sub_input (unformat_input_t * i, va_list * args) +{ + unformat_input_t *sub_input = va_arg (*args, unformat_input_t *); + u8 *s; + uword c; + + while (1) + { + c = unformat_get_input (i); + switch (c) + { + case ' ': + case '\t': + case '\n': + case '\r': + case '\f': + break; + + case '{': + default: + /* Put back paren. */ + if (c != UNFORMAT_END_OF_INPUT) + unformat_put_input (i); + + if (c == '{' && unformat (i, "%v", &s)) + { + unformat_init_vector (sub_input, s); + return 1; + } + return 0; + } + } + return 0; +} + +static vlib_cli_command_t * +get_sub_command (vlib_cli_main_t * cm, vlib_cli_command_t * parent, u32 si) +{ + vlib_cli_sub_command_t *s = vec_elt_at_index (parent->sub_commands, si); + return vec_elt_at_index (cm->commands, s->index); +} + +static uword +unformat_vlib_cli_sub_command (unformat_input_t * i, va_list * args) +{ + vlib_main_t *vm = va_arg (*args, vlib_main_t *); + vlib_cli_command_t *c = va_arg (*args, vlib_cli_command_t *); + vlib_cli_command_t **result = va_arg (*args, vlib_cli_command_t **); + vlib_cli_main_t *cm = &vm->cli_main; + uword *match_bitmap, is_unique, index; + + { + vlib_cli_sub_rule_t *sr; + vlib_cli_parse_rule_t *r; + vec_foreach (sr, c->sub_rules) + { + void **d; + r = vec_elt_at_index (cm->parse_rules, sr->rule_index); + vec_add2 (cm->parse_rule_data, d, 1); + vec_reset_length (d[0]); + if (r->data_size) + d[0] = _vec_resize (d[0], + /* length increment */ 1, + r->data_size, + /* header_bytes */ 0, + /* data align */ sizeof (uword)); + if (unformat_user (i, r->unformat_function, vm, d[0])) + { + *result = vec_elt_at_index (cm->commands, sr->command_index); + return 1; + } + } + } + + match_bitmap = vlib_cli_sub_command_match (c, i); + is_unique = clib_bitmap_count_set_bits (match_bitmap) == 1; + index = ~0; + if (is_unique) + { + index = clib_bitmap_first_set (match_bitmap); + *result = get_sub_command (cm, c, index); + } + clib_bitmap_free (match_bitmap); + + return is_unique; +} + +static u8 * +format_vlib_cli_command_help (u8 * s, va_list * args) +{ + vlib_cli_command_t *c = va_arg (*args, vlib_cli_command_t *); + int is_long = va_arg (*args, int); + if (is_long && c->long_help) + s = format (s, "%s", c->long_help); + else if (c->short_help) + s = format (s, "%s", c->short_help); + else + s = format (s, "%v commands", c->path); + return s; +} + +static u8 * +format_vlib_cli_parse_rule_name (u8 * s, va_list * args) +{ + vlib_cli_parse_rule_t *r = va_arg (*args, vlib_cli_parse_rule_t *); + return format (s, "<%U>", format_c_identifier, r->name); +} + +static u8 * +format_vlib_cli_path (u8 * s, va_list * args) +{ + u8 *path = va_arg (*args, u8 *); + int i, in_rule; + in_rule = 0; + for (i = 0; i < vec_len (path); i++) + { + switch (path[i]) + { + case '%': + in_rule = 1; + vec_add1 (s, '<'); /* start of */ + break; + + case '_': + /* _ -> space in rules. */ + vec_add1 (s, in_rule ? ' ' : '_'); + break; + + case ' ': + if (in_rule) + { + vec_add1 (s, '>'); /* end of */ + in_rule = 0; + } + vec_add1 (s, ' '); + break; + + default: + vec_add1 (s, path[i]); + break; + } + } + + if (in_rule) + vec_add1 (s, '>'); /* terminate */ + + return s; +} + +static vlib_cli_command_t * +all_subs (vlib_cli_main_t * cm, vlib_cli_command_t * subs, u32 command_index) +{ + vlib_cli_command_t *c = vec_elt_at_index (cm->commands, command_index); + vlib_cli_sub_command_t *sc; + vlib_cli_sub_rule_t *sr; + + if (c->function) + vec_add1 (subs, c[0]); + + vec_foreach (sr, c->sub_rules) + subs = all_subs (cm, subs, sr->command_index); + vec_foreach (sc, c->sub_commands) subs = all_subs (cm, subs, sc->index); + + return subs; +} + +static int +vlib_cli_cmp_rule (void *a1, void *a2) +{ + vlib_cli_sub_rule_t *r1 = a1; + vlib_cli_sub_rule_t *r2 = a2; + + return vec_cmp (r1->name, r2->name); +} + +static int +vlib_cli_cmp_command (void *a1, void *a2) +{ + vlib_cli_command_t *c1 = a1; + vlib_cli_command_t *c2 = a2; + + return vec_cmp (c1->path, c2->path); +} + +static clib_error_t * +vlib_cli_dispatch_sub_commands (vlib_main_t * vm, + vlib_cli_main_t * cm, + unformat_input_t * input, + uword parent_command_index) +{ + vlib_cli_command_t *parent, *c; + clib_error_t *error = 0; + unformat_input_t sub_input; + u8 *string; + uword is_main_dispatch = cm == &vm->cli_main; + + parent = vec_elt_at_index (cm->commands, parent_command_index); + if (is_main_dispatch && unformat (input, "help")) + { + uword help_at_end_of_line, i; + + help_at_end_of_line = + unformat_check_input (input) == UNFORMAT_END_OF_INPUT; + while (1) + { + c = parent; + if (unformat_user + (input, unformat_vlib_cli_sub_command, vm, c, &parent)) + ; + + else if (!(unformat_check_input (input) == UNFORMAT_END_OF_INPUT)) + goto unknown; + + else + break; + } + + /* help SUB-COMMAND => long format help. + "help" at end of line: show all commands. */ + if (!help_at_end_of_line) + vlib_cli_output (vm, "%U", format_vlib_cli_command_help, c, + /* is_long */ 1); + + else if (vec_len (c->sub_commands) + vec_len (c->sub_rules) == 0) + vlib_cli_output (vm, "%v: no sub-commands", c->path); + + else + { + vlib_cli_sub_command_t *sc; + vlib_cli_sub_rule_t *sr, *subs; + + subs = vec_dup (c->sub_rules); + + /* Add in rules if any. */ + vec_foreach (sc, c->sub_commands) + { + vec_add2 (subs, sr, 1); + sr->name = sc->name; + sr->command_index = sc->index; + sr->rule_index = ~0; + } + + vec_sort_with_function (subs, vlib_cli_cmp_rule); + + for (i = 0; i < vec_len (subs); i++) + { + vlib_cli_command_t *d; + vlib_cli_parse_rule_t *r; + + d = vec_elt_at_index (cm->commands, subs[i].command_index); + r = + subs[i].rule_index != ~0 ? vec_elt_at_index (cm->parse_rules, + subs + [i].rule_index) : + 0; + + if (r) + vlib_cli_output + (vm, " %-30U %U", + format_vlib_cli_parse_rule_name, r, + format_vlib_cli_command_help, d, /* is_long */ 0); + else + vlib_cli_output + (vm, " %-30v %U", + subs[i].name, + format_vlib_cli_command_help, d, /* is_long */ 0); + } + + vec_free (subs); + } + } + + else if (is_main_dispatch + && (unformat (input, "choices") || unformat (input, "?"))) + { + vlib_cli_command_t *sub, *subs; + + subs = all_subs (cm, 0, parent_command_index); + vec_sort_with_function (subs, vlib_cli_cmp_command); + vec_foreach (sub, subs) + vlib_cli_output (vm, " %-40U %U", + format_vlib_cli_path, sub->path, + format_vlib_cli_command_help, sub, /* is_long */ 0); + vec_free (subs); + } + + else if (unformat (input, "comment %v", &string)) + { + vec_free (string); + } + + else if (unformat (input, "uncomment %U", + unformat_vlib_cli_sub_input, &sub_input)) + { + error = + vlib_cli_dispatch_sub_commands (vm, cm, &sub_input, + parent_command_index); + unformat_free (&sub_input); + } + + else + if (unformat_user (input, unformat_vlib_cli_sub_command, vm, parent, &c)) + { + unformat_input_t *si; + uword has_sub_commands = + vec_len (c->sub_commands) + vec_len (c->sub_rules) > 0; + + si = input; + if (unformat_user (input, unformat_vlib_cli_sub_input, &sub_input)) + si = &sub_input; + + if (has_sub_commands) + error = vlib_cli_dispatch_sub_commands (vm, cm, si, c - cm->commands); + + if (has_sub_commands && !error) + /* Found valid sub-command. */ ; + + else if (c->function) + { + clib_error_t *c_error; + + /* Skip white space for benefit of called function. */ + unformat_skip_white_space (si); + + if (unformat (si, "?")) + { + vlib_cli_output (vm, " %-40U %U", format_vlib_cli_path, c->path, format_vlib_cli_command_help, c, /* is_long */ + 0); + } + else + { + if (!c->is_mp_safe) + vlib_worker_thread_barrier_sync (vm); + + c_error = c->function (vm, si, c); + + if (!c->is_mp_safe) + vlib_worker_thread_barrier_release (vm); + + if (c_error) + { + error = + clib_error_return (0, "%v: %v", c->path, c_error->what); + clib_error_free (c_error); + /* Free sub input. */ + if (si != input) + unformat_free (si); + + return error; + } + } + + /* Free any previous error. */ + clib_error_free (error); + } + + else if (!error) + error = clib_error_return (0, "%v: no sub-commands", c->path); + + /* Free sub input. */ + if (si != input) + unformat_free (si); + } + + else + goto unknown; + + return error; + +unknown: + if (parent->path) + return clib_error_return (0, "%v: unknown input `%U'", parent->path, + format_unformat_error, input); + else + return clib_error_return (0, "unknown input `%U'", format_unformat_error, + input); +} + + +void vlib_unix_error_report (vlib_main_t *, clib_error_t *) + __attribute__ ((weak)); + +void +vlib_unix_error_report (vlib_main_t * vm, clib_error_t * error) +{ +} + +/* Process CLI input. */ +void +vlib_cli_input (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_output_function_t * function, uword function_arg) +{ + vlib_process_t *cp = vlib_get_current_process (vm); + vlib_cli_main_t *cm = &vm->cli_main; + clib_error_t *error; + vlib_cli_output_function_t *save_function; + uword save_function_arg; + + save_function = cp->output_function; + save_function_arg = cp->output_function_arg; + + cp->output_function = function; + cp->output_function_arg = function_arg; + + do + { + vec_reset_length (cm->parse_rule_data); + error = vlib_cli_dispatch_sub_commands (vm, &vm->cli_main, input, /* parent */ + 0); + } + while (!error && !unformat (input, "%U", unformat_eof)); + + if (error) + { + vlib_cli_output (vm, "%v", error->what); + vlib_unix_error_report (vm, error); + clib_error_free (error); + } + + cp->output_function = save_function; + cp->output_function_arg = save_function_arg; +} + +/* Output to current CLI connection. */ +void +vlib_cli_output (vlib_main_t * vm, char *fmt, ...) +{ + vlib_process_t *cp = vlib_get_current_process (vm); + va_list va; + u8 *s; + + va_start (va, fmt); + s = va_format (0, fmt, &va); + va_end (va); + + /* Terminate with \n if not present. */ + if (vec_len (s) > 0 && s[vec_len (s) - 1] != '\n') + vec_add1 (s, '\n'); + + if ((!cp) || (!cp->output_function)) + fformat (stdout, "%v", s); + else + cp->output_function (cp->output_function_arg, s, vec_len (s)); + + vec_free (s); +} + +static clib_error_t * +show_memory_usage (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + int verbose = 0; + clib_error_t *error; + u32 index = 0; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "verbose")) + verbose = 1; + else + { + error = clib_error_return (0, "unknown input `%U'", + format_unformat_error, input); + return error; + } + } + + /* *INDENT-OFF* */ + foreach_vlib_main ( + ({ + vlib_cli_output (vm, "Thread %d %v\n", index, vlib_worker_threads[index].name); + vlib_cli_output (vm, "%U\n", format_mheap, clib_per_cpu_mheaps[index], verbose); + index++; + })); + /* *INDENT-ON* */ + return 0; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (show_memory_usage_command, static) = { + .path = "show memory", + .short_help = "Show current memory usage", + .function = show_memory_usage, +}; +/* *INDENT-ON* */ + +static clib_error_t * +show_cpu (vlib_main_t * vm, unformat_input_t * input, + vlib_cli_command_t * cmd) +{ +#define _(a,b,c) vlib_cli_output (vm, "%-25s " b, a ":", c); + _("Model name", "%U", format_cpu_model_name); + _("Microarchitecture", "%U", format_cpu_uarch); + _("Flags", "%U", format_cpu_flags); + _("Base frequency", "%.2f GHz", + ((f64) vm->clib_time.clocks_per_second) * 1e-9); +#undef _ + return 0; +} + +/*? + * Displays various information about the CPU. + * + * @cliexpar + * @cliexstart{show cpu} + * Model name: Intel(R) Xeon(R) CPU E5-2667 v4 @ 3.20GHz + * Microarchitecture: Broadwell (Broadwell-EP/EX) + * Flags: sse3 ssse3 sse41 sse42 avx avx2 aes + * Base Frequency: 3.20 GHz + * @cliexend +?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (show_cpu_command, static) = { + .path = "show cpu", + .short_help = "Show cpu information", + .function = show_cpu, +}; + +/* *INDENT-ON* */ +static clib_error_t * +enable_disable_memory_trace (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + clib_error_t *error = 0; + int enable; + + if (!unformat_user (input, unformat_vlib_enable_disable, &enable)) + { + error = clib_error_return (0, "expecting enable/on or disable/off"); + goto done; + } + + clib_mem_trace (enable); + +done: + return error; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (enable_disable_memory_trace_command, static) = { + .path = "memory-trace", + .short_help = "Enable/disable memory allocation trace", + .function = enable_disable_memory_trace, +}; +/* *INDENT-ON* */ + + +static clib_error_t * +test_heap_validate (vlib_main_t * vm, unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + clib_error_t *error = 0; + void *heap; + mheap_t *mheap; + + if (unformat (input, "on")) + { + /* *INDENT-OFF* */ + foreach_vlib_main({ + heap = clib_per_cpu_mheaps[this_vlib_main->cpu_index]; + mheap = mheap_header(heap); + mheap->flags |= MHEAP_FLAG_VALIDATE; + // Turn off small object cache because it delays detection of errors + mheap->flags &= ~MHEAP_FLAG_SMALL_OBJECT_CACHE; + }); + /* *INDENT-ON* */ + + } + else if (unformat (input, "off")) + { + /* *INDENT-OFF* */ + foreach_vlib_main({ + heap = clib_per_cpu_mheaps[this_vlib_main->cpu_index]; + mheap = mheap_header(heap); + mheap->flags &= ~MHEAP_FLAG_VALIDATE; + mheap->flags |= MHEAP_FLAG_SMALL_OBJECT_CACHE; + }); + /* *INDENT-ON* */ + } + else if (unformat (input, "now")) + { + /* *INDENT-OFF* */ + foreach_vlib_main({ + heap = clib_per_cpu_mheaps[this_vlib_main->cpu_index]; + mheap = mheap_header(heap); + mheap_validate(heap); + }); + /* *INDENT-ON* */ + vlib_cli_output (vm, "heap validation complete"); + + } + else + { + return clib_error_return (0, "unknown input `%U'", + format_unformat_error, input); + } + + return error; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (cmd_test_heap_validate,static) = { + .path = "test heap-validate", + .short_help = " validate heap on future allocs/frees or right now", + .function = test_heap_validate, +}; +/* *INDENT-ON* */ + +#ifdef TEST_CODE +/* + * A trivial test harness to verify the per-process output_function + * is working correcty. + */ + +static clib_error_t * +sleep_ten_seconds (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + u16 i; + u16 my_id = rand (); + + vlib_cli_output (vm, "Starting 10 seconds sleep with id %u\n", my_id); + + for (i = 0; i < 10; i++) + { + vlib_process_wait_for_event_or_clock (vm, 1.0); + vlib_cli_output (vm, "Iteration number %u, my id: %u\n", i, my_id); + } + vlib_cli_output (vm, "Done with sleep with id %u\n", my_id); + return 0; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (ping_command, static) = { + .path = "test sleep", + .function = sleep_ten_seconds, + .short_help = "Sleep for 10 seconds", +}; +/* *INDENT-ON* */ +#endif /* ifdef TEST_CODE */ + +static uword +vlib_cli_normalize_path (char *input, char **result) +{ + char *i = input; + char *s = 0; + uword l = 0; + uword index_of_last_space = ~0; + + while (*i != 0) + { + u8 c = *i++; + /* Multiple white space -> single space. */ + switch (c) + { + case ' ': + case '\t': + case '\n': + case '\r': + if (l > 0 && s[l - 1] != ' ') + { + vec_add1 (s, ' '); + l++; + } + break; + + default: + if (l > 0 && s[l - 1] == ' ') + index_of_last_space = vec_len (s); + vec_add1 (s, c); + l++; + break; + } + } + + /* Remove any extra space at end. */ + if (l > 0 && s[l - 1] == ' ') + _vec_len (s) -= 1; + + *result = s; + return index_of_last_space; +} + +always_inline uword +parent_path_len (char *path) +{ + word i; + for (i = vec_len (path) - 1; i >= 0; i--) + { + if (path[i] == ' ') + return i; + } + return ~0; +} + +static void +add_sub_command (vlib_cli_main_t * cm, uword parent_index, uword child_index) +{ + vlib_cli_command_t *p, *c; + vlib_cli_sub_command_t *sub_c; + u8 *sub_name; + word i, l; + + p = vec_elt_at_index (cm->commands, parent_index); + c = vec_elt_at_index (cm->commands, child_index); + + l = parent_path_len (c->path); + if (l == ~0) + sub_name = vec_dup ((u8 *) c->path); + else + { + ASSERT (l + 1 < vec_len (c->path)); + sub_name = 0; + vec_add (sub_name, c->path + l + 1, vec_len (c->path) - (l + 1)); + } + + if (sub_name[0] == '%') + { + uword *q; + vlib_cli_sub_rule_t *sr; + + /* Remove %. */ + vec_delete (sub_name, 1, 0); + + if (!p->sub_rule_index_by_name) + p->sub_rule_index_by_name = hash_create_vec ( /* initial length */ 32, + sizeof (sub_name[0]), + sizeof (uword)); + q = hash_get_mem (p->sub_rule_index_by_name, sub_name); + if (q) + { + sr = vec_elt_at_index (p->sub_rules, q[0]); + ASSERT (sr->command_index == child_index); + return; + } + + q = hash_get_mem (cm->parse_rule_index_by_name, sub_name); + if (!q) + { + clib_error ("reference to unknown rule `%%%v' in path `%v'", + sub_name, c->path); + return; + } + + hash_set_mem (p->sub_rule_index_by_name, sub_name, + vec_len (p->sub_rules)); + vec_add2 (p->sub_rules, sr, 1); + sr->name = sub_name; + sr->rule_index = q[0]; + sr->command_index = child_index; + return; + } + + if (!p->sub_command_index_by_name) + p->sub_command_index_by_name = hash_create_vec ( /* initial length */ 32, + sizeof (c->path[0]), + sizeof (uword)); + + /* Check if sub-command has already been created. */ + if (hash_get_mem (p->sub_command_index_by_name, sub_name)) + { + vec_free (sub_name); + return; + } + + vec_add2 (p->sub_commands, sub_c, 1); + sub_c->index = child_index; + sub_c->name = sub_name; + hash_set_mem (p->sub_command_index_by_name, sub_c->name, + sub_c - p->sub_commands); + + vec_validate (p->sub_command_positions, vec_len (sub_c->name) - 1); + for (i = 0; i < vec_len (sub_c->name); i++) + { + int n; + vlib_cli_parse_position_t *pos; + + pos = vec_elt_at_index (p->sub_command_positions, i); + + if (!pos->bitmaps) + pos->min_char = sub_c->name[i]; + + n = sub_c->name[i] - pos->min_char; + if (n < 0) + { + pos->min_char = sub_c->name[i]; + vec_insert (pos->bitmaps, -n, 0); + n = 0; + } + + vec_validate (pos->bitmaps, n); + pos->bitmaps[n] = + clib_bitmap_ori (pos->bitmaps[n], sub_c - p->sub_commands); + } +} + +static void +vlib_cli_make_parent (vlib_cli_main_t * cm, uword ci) +{ + uword p_len, pi, *p; + char *p_path; + vlib_cli_command_t *c, *parent; + + /* Root command (index 0) should have already been added. */ + ASSERT (vec_len (cm->commands) > 0); + + c = vec_elt_at_index (cm->commands, ci); + p_len = parent_path_len (c->path); + + /* No space? Parent is root command. */ + if (p_len == ~0) + { + add_sub_command (cm, 0, ci); + return; + } + + p_path = 0; + vec_add (p_path, c->path, p_len); + + p = hash_get_mem (cm->command_index_by_path, p_path); + + /* Parent exists? */ + if (!p) + { + /* Parent does not exist; create it. */ + vec_add2 (cm->commands, parent, 1); + parent->path = p_path; + hash_set_mem (cm->command_index_by_path, parent->path, + parent - cm->commands); + pi = parent - cm->commands; + } + else + { + pi = p[0]; + vec_free (p_path); + } + + add_sub_command (cm, pi, ci); + + /* Create parent's parent. */ + if (!p) + vlib_cli_make_parent (cm, pi); +} + +always_inline uword +vlib_cli_command_is_empty (vlib_cli_command_t * c) +{ + return (c->long_help == 0 && c->short_help == 0 && c->function == 0); +} + +clib_error_t * +vlib_cli_register (vlib_main_t * vm, vlib_cli_command_t * c) +{ + vlib_cli_main_t *cm = &vm->cli_main; + clib_error_t *error = 0; + uword ci, *p; + char *normalized_path; + + if ((error = vlib_call_init_function (vm, vlib_cli_init))) + return error; + + (void) vlib_cli_normalize_path (c->path, &normalized_path); + + if (!cm->command_index_by_path) + cm->command_index_by_path = hash_create_vec ( /* initial length */ 32, + sizeof (c->path[0]), + sizeof (uword)); + + /* See if command already exists with given path. */ + p = hash_get_mem (cm->command_index_by_path, normalized_path); + if (p) + { + vlib_cli_command_t *d; + + ci = p[0]; + d = vec_elt_at_index (cm->commands, ci); + + /* If existing command was created via vlib_cli_make_parent + replaced it with callers data. */ + if (vlib_cli_command_is_empty (d)) + { + vlib_cli_command_t save = d[0]; + + ASSERT (!vlib_cli_command_is_empty (c)); + + /* Copy callers fields. */ + d[0] = c[0]; + + /* Save internal fields. */ + d->path = save.path; + d->sub_commands = save.sub_commands; + d->sub_command_index_by_name = save.sub_command_index_by_name; + d->sub_command_positions = save.sub_command_positions; + d->sub_rules = save.sub_rules; + } + else + error = + clib_error_return (0, "duplicate command name with path %v", + normalized_path); + + vec_free (normalized_path); + if (error) + return error; + } + else + { + /* Command does not exist: create it. */ + + /* Add root command (index 0). */ + if (vec_len (cm->commands) == 0) + { + /* Create command with index 0; path is empty string. */ + vec_resize (cm->commands, 1); + } + + ci = vec_len (cm->commands); + hash_set_mem (cm->command_index_by_path, normalized_path, ci); + vec_add1 (cm->commands, c[0]); + + c = vec_elt_at_index (cm->commands, ci); + c->path = normalized_path; + + /* Don't inherit from registration. */ + c->sub_commands = 0; + c->sub_command_index_by_name = 0; + c->sub_command_positions = 0; + } + + vlib_cli_make_parent (cm, ci); + return 0; +} + +clib_error_t * +vlib_cli_register_parse_rule (vlib_main_t * vm, vlib_cli_parse_rule_t * r_reg) +{ + vlib_cli_main_t *cm = &vm->cli_main; + vlib_cli_parse_rule_t *r; + clib_error_t *error = 0; + u8 *r_name; + uword *p; + + if (!cm->parse_rule_index_by_name) + cm->parse_rule_index_by_name = hash_create_vec ( /* initial length */ 32, + sizeof (r->name[0]), + sizeof (uword)); + + /* Make vector copy of name. */ + r_name = format (0, "%s", r_reg->name); + + if ((p = hash_get_mem (cm->parse_rule_index_by_name, r_name))) + { + vec_free (r_name); + return clib_error_return (0, "duplicate parse rule name `%s'", + r_reg->name); + } + + vec_add2 (cm->parse_rules, r, 1); + r[0] = r_reg[0]; + r->name = (char *) r_name; + hash_set_mem (cm->parse_rule_index_by_name, r->name, r - cm->parse_rules); + + return error; +} + +#if 0 +/* $$$ turn back on again someday, maybe */ +static clib_error_t *vlib_cli_register_parse_rules (vlib_main_t * vm, + vlib_cli_parse_rule_t * + lo, + vlib_cli_parse_rule_t * + hi) + __attribute__ ((unused)) +{ + clib_error_t *error = 0; + vlib_cli_parse_rule_t *r; + + for (r = lo; r < hi; r = clib_elf_section_data_next (r, 0)) + { + if (!r->name || strlen (r->name) == 0) + { + error = clib_error_return (0, "parse rule with no name"); + goto done; + } + + error = vlib_cli_register_parse_rule (vm, r); + if (error) + goto done; + } + +done: + return error; +} +#endif + +static clib_error_t * +vlib_cli_init (vlib_main_t * vm) +{ + vlib_cli_main_t *cm = &vm->cli_main; + clib_error_t *error = 0; + vlib_cli_command_t *cmd; + + cmd = cm->cli_command_registrations; + + while (cmd) + { + error = vlib_cli_register (vm, cmd); + if (error) + return error; + cmd = cmd->next_cli_command; + } + return error; +} + +VLIB_INIT_FUNCTION (vlib_cli_init); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ -- cgit 1.2.3-korg