| /* 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; |
| } |