session: fix workers race to allocate lookup table
[vpp.git] / src / vnet / dev / args.c
1 /* SPDX-License-Identifier: Apache-2.0
2  * Copyright (c) 2023 Cisco Systems, Inc.
3  */
4
5 #include "vppinfra/pool.h"
6 #include <vnet/vnet.h>
7 #include <vnet/dev/dev.h>
8 #include <vnet/dev/counters.h>
9 #include <vnet/dev/log.h>
10 #include <vnet/dev/types.h>
11 #include <vppinfra/format_table.h>
12
13 VLIB_REGISTER_LOG_CLASS (dev_log, static) = {
14   .class_name = "dev",
15   .subclass_name = "args",
16 };
17
18 void
19 vnet_dev_arg_clear_value (vnet_dev_arg_t *a)
20 {
21   if (a->type == VNET_DEV_ARG_TYPE_STRING)
22     vec_free (a->val.string);
23   a->val = (typeof (a->val)){};
24   a->val_set = 0;
25 }
26
27 void
28 vnet_dev_arg_free (vnet_dev_arg_t **vp)
29 {
30   vnet_dev_arg_t *v;
31   vec_foreach (v, *vp)
32     vnet_dev_arg_clear_value (v);
33   vec_free (*vp);
34 }
35
36 vnet_dev_rv_t
37 vnet_dev_arg_parse (vlib_main_t *vm, vnet_dev_t *dev, vnet_dev_arg_t *args,
38                     u8 *str)
39 {
40   vnet_dev_rv_t rv = VNET_DEV_OK;
41   unformat_input_t in;
42   u8 *name = 0;
43   u8 *err = 0;
44
45   log_debug (dev, "input '%v'", str);
46   if (args == 0)
47     return rv;
48
49   unformat_init_string (&in, (char *) str, vec_len (str));
50
51   while (unformat (&in, "%U=", unformat_token, "a-zA-Z0-9_", &name))
52     {
53       vnet_dev_arg_t *a = args;
54       vec_add1 (name, 0);
55       while (a < vec_end (args))
56         if (strcmp (a->name, (char *) name) == 0)
57           break;
58         else
59           a++;
60
61       if (a->type == VNET_DEV_ARG_TYPE_BOOL)
62         {
63
64           if (unformat (&in, "true") || unformat (&in, "1") ||
65               unformat (&in, "on") || unformat (&in, "yes"))
66             a->val.boolean = 1;
67           else if (unformat (&in, "false") || unformat (&in, "0") ||
68                    unformat (&in, "off") || unformat (&in, "no"))
69             a->val.boolean = 0;
70           else
71             {
72               log_err (dev, "unable to parse args: %U", format_unformat_error,
73                        &in);
74               err = format (
75                 0,
76                 "boolean value expected ('yes', 'no', '0', '1', 'on', "
77                 "'off', 'true' or 'false') for argument '%s', found '%U'",
78                 a->name, format_unformat_error, &in);
79               goto done;
80             }
81         }
82       else if (a->type == VNET_DEV_ARG_TYPE_UINT32)
83         {
84           u32 val, min = 0, max = CLIB_U32_MAX;
85           if (!unformat (&in, "%u", &val))
86             {
87               err = format (0,
88                             "unsigned integer in range %u - %u expected for "
89                             "argument '%s', found '%U'",
90                             min, max, a->name, format_unformat_error, &in);
91               goto done;
92             }
93
94           if (a->min || a->max)
95             {
96               min = a->min;
97               max = a->max;
98             }
99
100           if (val < min || val > max)
101             {
102               err = format (0,
103                             "unsigned integer in range %u - %u expected for "
104                             "argument '%s', found '%u'",
105                             min, max, a->name, val);
106               goto done;
107             }
108           a->val.uint32 = val;
109         }
110       else if (a->type == VNET_DEV_ARG_TYPE_STRING)
111         {
112           if (!unformat (&in, "%U", unformat_double_quoted_string,
113                          &a->val.string))
114             {
115               err = format (
116                 0,
117                 "double quoted string expected for argument '%s', found '%U'",
118                 a->name, format_unformat_error, &in);
119               goto done;
120             }
121
122           if (a->min && vec_len (a->val.string) < a->min)
123             {
124               err =
125                 format (0, "string '%v' too short, must be at least %u chars",
126                         a->val.string, a->min);
127               goto done;
128             }
129           if (a->max && vec_len (a->val.string) > a->max)
130             {
131               err = format (
132                 0, "string '%v' too long, must be no longer than %u chars",
133                 a->val.string, a->max);
134               goto done;
135             }
136         }
137       else
138         {
139           err = format (0, "unknown argument '%s'", name);
140           goto done;
141         }
142
143       a->val_set = 1;
144       log_debug (dev, "name '%s' type %U value %U", name,
145                  format_vnet_dev_arg_type, a->type, format_vnet_dev_arg_value,
146                  a->type, &a->val);
147       vec_free (name);
148       unformat (&in, ",");
149     }
150
151   if (unformat_check_input (&in) != UNFORMAT_END_OF_INPUT)
152     err = format (0, "unable to parse argument name '%U'",
153                   format_unformat_error, &in);
154
155 done:
156   if (err)
157     {
158       vnet_dev_arg_t *a = 0;
159       log_err (dev, "%v", err);
160       vec_free (err);
161       vec_foreach (a, args)
162         vnet_dev_arg_clear_value (a);
163       rv = VNET_DEV_ERR_INVALID_ARG;
164     }
165
166   vec_free (name);
167   unformat_free (&in);
168   return rv;
169 }
170
171 u8 *
172 format_vnet_dev_arg_type (u8 *s, va_list *args)
173 {
174   vnet_dev_arg_type_t t = va_arg (*args, u32);
175   switch (t)
176     {
177 #define _(n, f, val)                                                          \
178   case VNET_DEV_ARG_TYPE_##n:                                                 \
179     return format (s, #n);
180       foreach_vnet_dev_arg_type
181 #undef _
182         default : ASSERT (0);
183       break;
184     }
185   return s;
186 }
187
188 u8 *
189 format_vnet_dev_arg_value (u8 *s, va_list *args)
190 {
191   vnet_dev_arg_type_t t = va_arg (*args, u32);
192   vnet_dev_arg_value_t *v = va_arg (*args, vnet_dev_arg_value_t *);
193
194   switch (t)
195     {
196 #define _(n, f, value)                                                        \
197   case VNET_DEV_ARG_TYPE_##n:                                                 \
198     s = format (s, f, v->value);                                              \
199     break;
200       foreach_vnet_dev_arg_type
201 #undef _
202         default : break;
203     }
204   return s;
205 }
206
207 u8 *
208 format_vnet_dev_args (u8 *s, va_list *va)
209 {
210   vnet_dev_arg_t *a, *args = va_arg (*va, vnet_dev_arg_t *);
211   table_t t = { .no_ansi = 1 };
212
213   table_add_header_col (&t, 4, "Name", "Value", "Default", "Description");
214   table_set_cell_align (&t, -1, 0, TTAA_LEFT);
215   table_set_cell_align (&t, -1, 3, TTAA_LEFT);
216   vec_foreach (a, args)
217     {
218       int r = a - args;
219       table_format_cell (&t, r, 0, "%s", a->name);
220       if (a->val_set)
221         table_format_cell (&t, r, 1, "%U", format_vnet_dev_arg_value, a->type,
222                            &a->val);
223       else
224         table_format_cell (&t, r, 1, "<not set>");
225
226       table_format_cell (&t, r, 2, "%U", format_vnet_dev_arg_value, a->type,
227                          &a->default_val);
228       table_format_cell (&t, r, 3, "%s", a->desc);
229       table_set_cell_align (&t, r, 0, TTAA_LEFT);
230       table_set_cell_align (&t, r, 3, TTAA_LEFT);
231     }
232
233   s = format (s, "%U", format_table, &t);
234
235   table_free (&t);
236   return s;
237 }