blob: bd92080667008dcf990ff3479b86ff9053949eb6 [file] [log] [blame]
Paolo Bonzinia020f982012-02-09 09:36:37 +01001/*
2 * String parsing visitor
3 *
Eric Blake08f95412016-01-29 06:48:59 -07004 * Copyright Red Hat, Inc. 2012-2016
Paolo Bonzinia020f982012-02-09 09:36:37 +01005 *
6 * Author: Paolo Bonzini <pbonzini@redhat.com>
David Hildenbrandc9fba9d2018-11-21 17:44:18 +01007 * David Hildenbrand <david@redhat.com>
Paolo Bonzinia020f982012-02-09 09:36:37 +01008 *
9 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
10 * See the COPYING.LIB file in the top-level directory.
Paolo Bonzinia020f982012-02-09 09:36:37 +010011 */
12
Peter Maydellcbf21152016-01-29 17:49:57 +000013#include "qemu/osdep.h"
Markus Armbrusterda34e652016-03-14 09:01:28 +010014#include "qapi/error.h"
Paolo Bonzinia020f982012-02-09 09:36:37 +010015#include "qemu-common.h"
Paolo Bonzini7b1b5d12012-12-17 18:19:43 +010016#include "qapi/string-input-visitor.h"
17#include "qapi/visitor-impl.h"
18#include "qapi/qmp/qerror.h"
Max Reitz84be6292017-11-14 19:01:23 +010019#include "qapi/qmp/qnull.h"
Paolo Bonzinia5829cc2014-02-08 11:01:44 +010020#include "qemu/option.h"
David Hildenbrand4b69d4c2018-11-21 17:44:15 +010021#include "qemu/cutils.h"
Hu Tao659268f2014-06-10 19:15:27 +080022
David Hildenbrandc9fba9d2018-11-21 17:44:18 +010023typedef enum ListMode {
24 /* no list parsing active / no list expected */
25 LM_NONE,
26 /* we have an unparsed string remaining */
27 LM_UNPARSED,
28 /* we have an unfinished int64 range */
29 LM_INT64_RANGE,
30 /* we have an unfinished uint64 range */
31 LM_UINT64_RANGE,
32 /* we have parsed the string completely and no range is remaining */
33 LM_END,
34} ListMode;
35
36/* protect against DOS attacks, limit the amount of elements per range */
37#define RANGE_MAX_ELEMENTS 65536
38
39typedef union RangeElement {
40 int64_t i64;
41 uint64_t u64;
42} RangeElement;
Paolo Bonzinia020f982012-02-09 09:36:37 +010043
44struct StringInputVisitor
45{
46 Visitor visitor;
Hu Tao659268f2014-06-10 19:15:27 +080047
David Hildenbrandc9fba9d2018-11-21 17:44:18 +010048 /* List parsing state */
49 ListMode lm;
50 RangeElement rangeNext;
51 RangeElement rangeEnd;
52 const char *unparsed_string;
53 void *list;
Hu Tao659268f2014-06-10 19:15:27 +080054
David Hildenbrandc9fba9d2018-11-21 17:44:18 +010055 /* The original string to parse */
Paolo Bonzinia020f982012-02-09 09:36:37 +010056 const char *string;
57};
58
Eric Blaked7bea752016-01-29 06:48:38 -070059static StringInputVisitor *to_siv(Visitor *v)
60{
61 return container_of(v, StringInputVisitor, visitor);
62}
63
David Hildenbrandc9fba9d2018-11-21 17:44:18 +010064static void start_list(Visitor *v, const char *name, GenericList **list,
65 size_t size, Error **errp)
Hu Tao659268f2014-06-10 19:15:27 +080066{
Eric Blaked7bea752016-01-29 06:48:38 -070067 StringInputVisitor *siv = to_siv(v);
Hu Tao659268f2014-06-10 19:15:27 +080068
David Hildenbrandc9fba9d2018-11-21 17:44:18 +010069 assert(siv->lm == LM_NONE);
Eric Blake1158bb22016-06-09 10:48:34 -060070 siv->list = list;
David Hildenbrandc9fba9d2018-11-21 17:44:18 +010071 siv->unparsed_string = siv->string;
Eric Blaked9f62dd2016-04-28 15:45:31 -060072
David Hildenbrandc9fba9d2018-11-21 17:44:18 +010073 if (!siv->string[0]) {
74 if (list) {
75 *list = NULL;
Hu Tao659268f2014-06-10 19:15:27 +080076 }
David Hildenbrandc9fba9d2018-11-21 17:44:18 +010077 siv->lm = LM_END;
Eric Blaked9f62dd2016-04-28 15:45:31 -060078 } else {
David Hildenbrandc9fba9d2018-11-21 17:44:18 +010079 if (list) {
80 *list = g_malloc0(size);
81 }
82 siv->lm = LM_UNPARSED;
Hu Tao659268f2014-06-10 19:15:27 +080083 }
84}
85
Eric Blaked9f62dd2016-04-28 15:45:31 -060086static GenericList *next_list(Visitor *v, GenericList *tail, size_t size)
Hu Tao659268f2014-06-10 19:15:27 +080087{
Eric Blaked7bea752016-01-29 06:48:38 -070088 StringInputVisitor *siv = to_siv(v);
Hu Tao659268f2014-06-10 19:15:27 +080089
David Hildenbrandc9fba9d2018-11-21 17:44:18 +010090 switch (siv->lm) {
91 case LM_END:
Hu Tao659268f2014-06-10 19:15:27 +080092 return NULL;
David Hildenbrandc9fba9d2018-11-21 17:44:18 +010093 case LM_INT64_RANGE:
94 case LM_UINT64_RANGE:
95 case LM_UNPARSED:
96 /* we have an unparsed string or something left in a range */
97 break;
98 default:
99 abort();
Hu Tao659268f2014-06-10 19:15:27 +0800100 }
101
Eric Blaked9f62dd2016-04-28 15:45:31 -0600102 tail->next = g_malloc0(size);
103 return tail->next;
Hu Tao659268f2014-06-10 19:15:27 +0800104}
105
Markus Armbrustera4a1c702017-03-03 13:32:45 +0100106static void check_list(Visitor *v, Error **errp)
107{
108 const StringInputVisitor *siv = to_siv(v);
Markus Armbrustera4a1c702017-03-03 13:32:45 +0100109
David Hildenbrandc9fba9d2018-11-21 17:44:18 +0100110 switch (siv->lm) {
111 case LM_INT64_RANGE:
112 case LM_UINT64_RANGE:
113 case LM_UNPARSED:
114 error_setg(errp, "Fewer list elements expected");
Markus Armbrustera4a1c702017-03-03 13:32:45 +0100115 return;
David Hildenbrandc9fba9d2018-11-21 17:44:18 +0100116 case LM_END:
Markus Armbrustera4a1c702017-03-03 13:32:45 +0100117 return;
David Hildenbrandc9fba9d2018-11-21 17:44:18 +0100118 default:
119 abort();
Markus Armbrustera4a1c702017-03-03 13:32:45 +0100120 }
Markus Armbrustera4a1c702017-03-03 13:32:45 +0100121}
122
Eric Blake1158bb22016-06-09 10:48:34 -0600123static void end_list(Visitor *v, void **obj)
Hu Tao659268f2014-06-10 19:15:27 +0800124{
Eric Blake1158bb22016-06-09 10:48:34 -0600125 StringInputVisitor *siv = to_siv(v);
126
David Hildenbrandc9fba9d2018-11-21 17:44:18 +0100127 assert(siv->lm != LM_NONE);
Eric Blake1158bb22016-06-09 10:48:34 -0600128 assert(siv->list == obj);
David Hildenbrandc9fba9d2018-11-21 17:44:18 +0100129 siv->list = NULL;
130 siv->unparsed_string = NULL;
131 siv->lm = LM_NONE;
132}
133
134static int try_parse_int64_list_entry(StringInputVisitor *siv, int64_t *obj)
135{
136 const char *endptr;
137 int64_t start, end;
138
139 /* parse a simple int64 or range */
140 if (qemu_strtoi64(siv->unparsed_string, &endptr, 0, &start)) {
141 return -EINVAL;
142 }
143 end = start;
144
145 switch (endptr[0]) {
146 case '\0':
147 siv->unparsed_string = endptr;
148 break;
149 case ',':
150 siv->unparsed_string = endptr + 1;
151 break;
152 case '-':
153 /* parse the end of the range */
154 if (qemu_strtoi64(endptr + 1, &endptr, 0, &end)) {
155 return -EINVAL;
156 }
157 if (start > end || end - start >= RANGE_MAX_ELEMENTS) {
158 return -EINVAL;
159 }
160 switch (endptr[0]) {
161 case '\0':
162 siv->unparsed_string = endptr;
163 break;
164 case ',':
165 siv->unparsed_string = endptr + 1;
166 break;
167 default:
168 return -EINVAL;
169 }
170 break;
171 default:
172 return -EINVAL;
173 }
174
175 /* we have a proper range (with maybe only one element) */
176 siv->lm = LM_INT64_RANGE;
177 siv->rangeNext.i64 = start;
178 siv->rangeEnd.i64 = end;
179 return 0;
Hu Tao659268f2014-06-10 19:15:27 +0800180}
181
Eric Blake0b2a0d62016-01-29 06:48:56 -0700182static void parse_type_int64(Visitor *v, const char *name, int64_t *obj,
Eric Blake4c403142016-01-29 06:48:49 -0700183 Error **errp)
Paolo Bonzinia020f982012-02-09 09:36:37 +0100184{
Eric Blaked7bea752016-01-29 06:48:38 -0700185 StringInputVisitor *siv = to_siv(v);
David Hildenbrandc9fba9d2018-11-21 17:44:18 +0100186 int64_t val;
Paolo Bonzinia020f982012-02-09 09:36:37 +0100187
David Hildenbrandc9fba9d2018-11-21 17:44:18 +0100188 switch (siv->lm) {
189 case LM_NONE:
190 /* just parse a simple int64, bail out if not completely consumed */
191 if (qemu_strtoi64(siv->string, NULL, 0, &val)) {
192 error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
193 name ? name : "null", "int64");
194 return;
195 }
196 *obj = val;
Eric Blake74f24cb2016-04-28 15:45:30 -0600197 return;
David Hildenbrandc9fba9d2018-11-21 17:44:18 +0100198 case LM_UNPARSED:
199 if (try_parse_int64_list_entry(siv, obj)) {
200 error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
201 "list of int64 values or ranges");
202 return;
Hu Tao659268f2014-06-10 19:15:27 +0800203 }
David Hildenbrandc9fba9d2018-11-21 17:44:18 +0100204 assert(siv->lm == LM_INT64_RANGE);
205 /* fall through */
206 case LM_INT64_RANGE:
207 /* return the next element in the range */
208 assert(siv->rangeNext.i64 <= siv->rangeEnd.i64);
209 *obj = siv->rangeNext.i64++;
Hu Tao659268f2014-06-10 19:15:27 +0800210
David Hildenbrandc9fba9d2018-11-21 17:44:18 +0100211 if (siv->rangeNext.i64 > siv->rangeEnd.i64 || *obj == INT64_MAX) {
212 /* end of range, check if there is more to parse */
213 siv->lm = siv->unparsed_string[0] ? LM_UNPARSED : LM_END;
Hu Tao659268f2014-06-10 19:15:27 +0800214 }
David Hildenbrandc9fba9d2018-11-21 17:44:18 +0100215 return;
216 case LM_END:
217 error_setg(errp, "Fewer list elements expected");
218 return;
219 default:
220 abort();
221 }
222}
Hu Tao659268f2014-06-10 19:15:27 +0800223
David Hildenbrandc9fba9d2018-11-21 17:44:18 +0100224static int try_parse_uint64_list_entry(StringInputVisitor *siv, uint64_t *obj)
225{
226 const char *endptr;
227 uint64_t start, end;
228
229 /* parse a simple uint64 or range */
230 if (qemu_strtou64(siv->unparsed_string, &endptr, 0, &start)) {
231 return -EINVAL;
232 }
233 end = start;
234
235 switch (endptr[0]) {
236 case '\0':
237 siv->unparsed_string = endptr;
238 break;
239 case ',':
240 siv->unparsed_string = endptr + 1;
241 break;
242 case '-':
243 /* parse the end of the range */
244 if (qemu_strtou64(endptr + 1, &endptr, 0, &end)) {
245 return -EINVAL;
246 }
247 if (start > end || end - start >= RANGE_MAX_ELEMENTS) {
248 return -EINVAL;
249 }
250 switch (endptr[0]) {
251 case '\0':
252 siv->unparsed_string = endptr;
253 break;
254 case ',':
255 siv->unparsed_string = endptr + 1;
256 break;
257 default:
258 return -EINVAL;
259 }
260 break;
261 default:
262 return -EINVAL;
Hu Tao659268f2014-06-10 19:15:27 +0800263 }
264
David Hildenbrandc9fba9d2018-11-21 17:44:18 +0100265 /* we have a proper range (with maybe only one element) */
266 siv->lm = LM_UINT64_RANGE;
267 siv->rangeNext.u64 = start;
268 siv->rangeEnd.u64 = end;
269 return 0;
Paolo Bonzinia020f982012-02-09 09:36:37 +0100270}
271
Eric Blake0b2a0d62016-01-29 06:48:56 -0700272static void parse_type_uint64(Visitor *v, const char *name, uint64_t *obj,
Eric Blakef755dea2016-01-29 06:48:50 -0700273 Error **errp)
274{
David Hildenbrandc9fba9d2018-11-21 17:44:18 +0100275 StringInputVisitor *siv = to_siv(v);
276 uint64_t val;
277
278 switch (siv->lm) {
279 case LM_NONE:
280 /* just parse a simple uint64, bail out if not completely consumed */
281 if (qemu_strtou64(siv->string, NULL, 0, &val)) {
282 error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
283 "uint64");
284 return;
285 }
286 *obj = val;
287 return;
288 case LM_UNPARSED:
289 if (try_parse_uint64_list_entry(siv, obj)) {
290 error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
291 "list of uint64 values or ranges");
292 return;
293 }
294 assert(siv->lm == LM_UINT64_RANGE);
295 /* fall through */
296 case LM_UINT64_RANGE:
297 /* return the next element in the range */
298 assert(siv->rangeNext.u64 <= siv->rangeEnd.u64);
299 *obj = siv->rangeNext.u64++;
300
301 if (siv->rangeNext.u64 > siv->rangeEnd.u64 || *obj == UINT64_MAX) {
302 /* end of range, check if there is more to parse */
303 siv->lm = siv->unparsed_string[0] ? LM_UNPARSED : LM_END;
304 }
305 return;
306 case LM_END:
307 error_setg(errp, "Fewer list elements expected");
308 return;
309 default:
310 abort();
Eric Blakef755dea2016-01-29 06:48:50 -0700311 }
312}
313
Eric Blake0b2a0d62016-01-29 06:48:56 -0700314static void parse_type_size(Visitor *v, const char *name, uint64_t *obj,
Paolo Bonzinia5829cc2014-02-08 11:01:44 +0100315 Error **errp)
316{
Eric Blaked7bea752016-01-29 06:48:38 -0700317 StringInputVisitor *siv = to_siv(v);
Paolo Bonzinia5829cc2014-02-08 11:01:44 +0100318 Error *err = NULL;
319 uint64_t val;
320
David Hildenbrandc9fba9d2018-11-21 17:44:18 +0100321 assert(siv->lm == LM_NONE);
Markus Armbrusterf332e832017-03-03 13:32:36 +0100322 parse_option_size(name, siv->string, &val, &err);
Paolo Bonzinia5829cc2014-02-08 11:01:44 +0100323 if (err) {
324 error_propagate(errp, err);
325 return;
326 }
327
328 *obj = val;
329}
330
Eric Blake0b2a0d62016-01-29 06:48:56 -0700331static void parse_type_bool(Visitor *v, const char *name, bool *obj,
Paolo Bonzinia020f982012-02-09 09:36:37 +0100332 Error **errp)
333{
Eric Blaked7bea752016-01-29 06:48:38 -0700334 StringInputVisitor *siv = to_siv(v);
Paolo Bonzinia020f982012-02-09 09:36:37 +0100335
David Hildenbrandc9fba9d2018-11-21 17:44:18 +0100336 assert(siv->lm == LM_NONE);
Markus Armbrusterf332e832017-03-03 13:32:36 +0100337 if (!strcasecmp(siv->string, "on") ||
338 !strcasecmp(siv->string, "yes") ||
339 !strcasecmp(siv->string, "true")) {
340 *obj = true;
341 return;
342 }
343 if (!strcasecmp(siv->string, "off") ||
344 !strcasecmp(siv->string, "no") ||
345 !strcasecmp(siv->string, "false")) {
346 *obj = false;
347 return;
Paolo Bonzinia020f982012-02-09 09:36:37 +0100348 }
349
Markus Armbrusterc6bd8c72015-03-17 11:54:50 +0100350 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
351 "boolean");
Paolo Bonzinia020f982012-02-09 09:36:37 +0100352}
353
Eric Blake0b2a0d62016-01-29 06:48:56 -0700354static void parse_type_str(Visitor *v, const char *name, char **obj,
Paolo Bonzinia020f982012-02-09 09:36:37 +0100355 Error **errp)
356{
Eric Blaked7bea752016-01-29 06:48:38 -0700357 StringInputVisitor *siv = to_siv(v);
Markus Armbrusterf332e832017-03-03 13:32:36 +0100358
David Hildenbrandc9fba9d2018-11-21 17:44:18 +0100359 assert(siv->lm == LM_NONE);
Markus Armbrusterf332e832017-03-03 13:32:36 +0100360 *obj = g_strdup(siv->string);
Paolo Bonzinia020f982012-02-09 09:36:37 +0100361}
362
Eric Blake0b2a0d62016-01-29 06:48:56 -0700363static void parse_type_number(Visitor *v, const char *name, double *obj,
Paolo Bonzinia020f982012-02-09 09:36:37 +0100364 Error **errp)
365{
Eric Blaked7bea752016-01-29 06:48:38 -0700366 StringInputVisitor *siv = to_siv(v);
Paolo Bonzinia020f982012-02-09 09:36:37 +0100367 double val;
368
David Hildenbrandc9fba9d2018-11-21 17:44:18 +0100369 assert(siv->lm == LM_NONE);
David Hildenbrand4b69d4c2018-11-21 17:44:15 +0100370 if (qemu_strtod_finite(siv->string, NULL, &val)) {
Markus Armbrusterc6bd8c72015-03-17 11:54:50 +0100371 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
372 "number");
Paolo Bonzinia020f982012-02-09 09:36:37 +0100373 return;
374 }
375
376 *obj = val;
377}
378
Markus Armbrusterd2f95f42017-06-26 18:22:59 +0200379static void parse_type_null(Visitor *v, const char *name, QNull **obj,
380 Error **errp)
Greg Kurza7333712016-12-16 16:26:09 +0100381{
382 StringInputVisitor *siv = to_siv(v);
383
David Hildenbrandc9fba9d2018-11-21 17:44:18 +0100384 assert(siv->lm == LM_NONE);
Markus Armbrusterd2f95f42017-06-26 18:22:59 +0200385 *obj = NULL;
386
David Hildenbrandc9fba9d2018-11-21 17:44:18 +0100387 if (siv->string[0]) {
Greg Kurza7333712016-12-16 16:26:09 +0100388 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
389 "null");
Markus Armbrusterd2f95f42017-06-26 18:22:59 +0200390 return;
Greg Kurza7333712016-12-16 16:26:09 +0100391 }
Markus Armbrusterd2f95f42017-06-26 18:22:59 +0200392
393 *obj = qnull();
Greg Kurza7333712016-12-16 16:26:09 +0100394}
395
Eric Blake2c0ef9f2016-06-09 10:48:35 -0600396static void string_input_free(Visitor *v)
397{
398 StringInputVisitor *siv = to_siv(v);
399
Eric Blake7a0525c2016-06-09 10:48:37 -0600400 g_free(siv);
Eric Blake2c0ef9f2016-06-09 10:48:35 -0600401}
402
Eric Blake7a0525c2016-06-09 10:48:37 -0600403Visitor *string_input_visitor_new(const char *str)
Paolo Bonzinia020f982012-02-09 09:36:37 +0100404{
405 StringInputVisitor *v;
406
Markus Armbrusterf332e832017-03-03 13:32:36 +0100407 assert(str);
Paolo Bonzinia020f982012-02-09 09:36:37 +0100408 v = g_malloc0(sizeof(*v));
409
Eric Blake983f52d2016-04-28 15:45:09 -0600410 v->visitor.type = VISITOR_INPUT;
Eric Blake4c403142016-01-29 06:48:49 -0700411 v->visitor.type_int64 = parse_type_int64;
Eric Blakef755dea2016-01-29 06:48:50 -0700412 v->visitor.type_uint64 = parse_type_uint64;
Paolo Bonzinia5829cc2014-02-08 11:01:44 +0100413 v->visitor.type_size = parse_type_size;
Paolo Bonzinia020f982012-02-09 09:36:37 +0100414 v->visitor.type_bool = parse_type_bool;
415 v->visitor.type_str = parse_type_str;
416 v->visitor.type_number = parse_type_number;
Greg Kurza7333712016-12-16 16:26:09 +0100417 v->visitor.type_null = parse_type_null;
Hu Tao659268f2014-06-10 19:15:27 +0800418 v->visitor.start_list = start_list;
419 v->visitor.next_list = next_list;
Markus Armbrustera4a1c702017-03-03 13:32:45 +0100420 v->visitor.check_list = check_list;
Hu Tao659268f2014-06-10 19:15:27 +0800421 v->visitor.end_list = end_list;
Eric Blake2c0ef9f2016-06-09 10:48:35 -0600422 v->visitor.free = string_input_free;
Paolo Bonzinia020f982012-02-09 09:36:37 +0100423
424 v->string = str;
David Hildenbrandc9fba9d2018-11-21 17:44:18 +0100425 v->lm = LM_NONE;
Eric Blake7a0525c2016-06-09 10:48:37 -0600426 return &v->visitor;
Paolo Bonzinia020f982012-02-09 09:36:37 +0100427}