/* SPDX-License-Identifier: Apache-2.0 * Copyright (c) 2023 Cisco Systems, Inc. */ #include "vppinfra/pool.h" #include #include #include #include #include #include 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, ""); 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; }