diff options
Diffstat (limited to 'src/vnet/dev/args.c')
-rw-r--r-- | src/vnet/dev/args.c | 237 |
1 files changed, 237 insertions, 0 deletions
diff --git a/src/vnet/dev/args.c b/src/vnet/dev/args.c new file mode 100644 index 00000000000..e302517cc61 --- /dev/null +++ b/src/vnet/dev/args.c @@ -0,0 +1,237 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2023 Cisco Systems, Inc. + */ + +#include "vppinfra/pool.h" +#include <vnet/vnet.h> +#include <vnet/dev/dev.h> +#include <vnet/dev/counters.h> +#include <vnet/dev/log.h> +#include <vnet/dev/types.h> +#include <vppinfra/format_table.h> + +VLIB_REGISTER_LOG_CLASS (dev_log, static) = { + .class_name = "dev", + .subclass_name = "args", +}; + +void +vnet_dev_arg_clear_value (vnet_dev_arg_t *a) +{ + if (a->type == VNET_DEV_ARG_TYPE_STRING) + vec_free (a->val.string); + a->val = (typeof (a->val)){}; + a->val_set = 0; +} + +void +vnet_dev_arg_free (vnet_dev_arg_t **vp) +{ + vnet_dev_arg_t *v; + vec_foreach (v, *vp) + vnet_dev_arg_clear_value (v); + vec_free (*vp); +} + +vnet_dev_rv_t +vnet_dev_arg_parse (vlib_main_t *vm, vnet_dev_t *dev, vnet_dev_arg_t *args, + u8 *str) +{ + vnet_dev_rv_t rv = VNET_DEV_OK; + unformat_input_t in; + u8 *name = 0; + u8 *err = 0; + + log_debug (dev, "input '%v'", str); + if (args == 0) + return rv; + + unformat_init_string (&in, (char *) str, vec_len (str)); + + while (unformat (&in, "%U=", unformat_token, "a-zA-Z0-9_", &name)) + { + vnet_dev_arg_t *a = args; + vec_add1 (name, 0); + while (a < vec_end (args)) + if (strcmp (a->name, (char *) name) == 0) + break; + else + a++; + + if (a->type == VNET_DEV_ARG_TYPE_BOOL) + { + + if (unformat (&in, "true") || unformat (&in, "1") || + unformat (&in, "on") || unformat (&in, "yes")) + a->val.boolean = 1; + else if (unformat (&in, "false") || unformat (&in, "0") || + unformat (&in, "off") || unformat (&in, "no")) + a->val.boolean = 0; + else + { + log_err (dev, "unable to parse args: %U", format_unformat_error, + &in); + err = format ( + 0, + "boolean value expected ('yes', 'no', '0', '1', 'on', " + "'off', 'true' or 'false') for argument '%s', found '%U'", + a->name, format_unformat_error, &in); + goto done; + } + } + else if (a->type == VNET_DEV_ARG_TYPE_UINT32) + { + u32 val, min = 0, max = CLIB_U32_MAX; + if (!unformat (&in, "%u", &val)) + { + err = format (0, + "unsigned integer in range %u - %u expected for " + "argument '%s', found '%U'", + min, max, a->name, format_unformat_error, &in); + goto done; + } + + if (a->min || a->max) + { + min = a->min; + max = a->max; + } + + if (val < min || val > max) + { + err = format (0, + "unsigned integer in range %u - %u expected for " + "argument '%s', found '%u'", + min, max, a->name, val); + goto done; + } + a->val.uint32 = val; + } + else if (a->type == VNET_DEV_ARG_TYPE_STRING) + { + if (!unformat (&in, "%U", unformat_double_quoted_string, + &a->val.string)) + { + err = format ( + 0, + "double quoted string expected for argument '%s', found '%U'", + a->name, format_unformat_error, &in); + goto done; + } + + if (a->min && vec_len (a->val.string) < a->min) + { + err = + format (0, "string '%v' too short, must be at least %u chars", + a->val.string, a->min); + goto done; + } + if (a->max && vec_len (a->val.string) > a->max) + { + err = format ( + 0, "string '%v' too long, must be no longer than %u chars", + a->val.string, a->max); + goto done; + } + } + else + { + err = format (0, "unknown argument '%s'", name); + goto done; + } + + a->val_set = 1; + log_debug (dev, "name '%s' type %U value %U", name, + format_vnet_dev_arg_type, a->type, format_vnet_dev_arg_value, + a->type, &a->val); + vec_free (name); + unformat (&in, ","); + } + + if (unformat_check_input (&in) != UNFORMAT_END_OF_INPUT) + err = format (0, "unable to parse argument name '%U'", + format_unformat_error, &in); + +done: + if (err) + { + vnet_dev_arg_t *a = 0; + log_err (dev, "%v", err); + vec_free (err); + vec_foreach (a, args) + vnet_dev_arg_clear_value (a); + rv = VNET_DEV_ERR_INVALID_ARG; + } + + vec_free (name); + unformat_free (&in); + return rv; +} + +u8 * +format_vnet_dev_arg_type (u8 *s, va_list *args) +{ + vnet_dev_arg_type_t t = va_arg (*args, u32); + switch (t) + { +#define _(n, f, val) \ + case VNET_DEV_ARG_TYPE_##n: \ + return format (s, #n); + foreach_vnet_dev_arg_type +#undef _ + default : ASSERT (0); + break; + } + return s; +} + +u8 * +format_vnet_dev_arg_value (u8 *s, va_list *args) +{ + vnet_dev_arg_type_t t = va_arg (*args, u32); + vnet_dev_arg_value_t *v = va_arg (*args, vnet_dev_arg_value_t *); + + switch (t) + { +#define _(n, f, value) \ + case VNET_DEV_ARG_TYPE_##n: \ + s = format (s, f, v->value); \ + break; + foreach_vnet_dev_arg_type +#undef _ + default : break; + } + return s; +} + +u8 * +format_vnet_dev_args (u8 *s, va_list *va) +{ + vnet_dev_arg_t *a, *args = va_arg (*va, vnet_dev_arg_t *); + table_t t = { .no_ansi = 1 }; + + table_add_header_col (&t, 4, "Name", "Value", "Default", "Description"); + table_set_cell_align (&t, -1, 0, TTAA_LEFT); + table_set_cell_align (&t, -1, 3, TTAA_LEFT); + vec_foreach (a, args) + { + int r = a - args; + table_format_cell (&t, r, 0, "%s", a->name); + if (a->val_set) + table_format_cell (&t, r, 1, "%U", format_vnet_dev_arg_value, a->type, + &a->val); + else + table_format_cell (&t, r, 1, "<not set>"); + + table_format_cell (&t, r, 2, "%U", format_vnet_dev_arg_value, a->type, + &a->default_val); + table_format_cell (&t, r, 3, "%s", a->desc); + table_set_cell_align (&t, r, 0, TTAA_LEFT); + table_set_cell_align (&t, r, 3, TTAA_LEFT); + } + + s = format (s, "%U", format_table, &t); + + table_free (&t); + return s; +} |