xref: /qemu/qapi/qobject-input-visitor.c (revision 15280c360e54a65e2c7be1a47bfbe41dce1ef986)
1 /*
2  * Input Visitor
3  *
4  * Copyright (C) 2012-2017 Red Hat, Inc.
5  * Copyright IBM, Corp. 2011
6  *
7  * Authors:
8  *  Anthony Liguori   <aliguori@us.ibm.com>
9  *
10  * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
11  * See the COPYING.LIB file in the top-level directory.
12  *
13  */
14 
15 #include "qemu/osdep.h"
16 #include <math.h>
17 #include "qapi/error.h"
18 #include "qapi/qobject-input-visitor.h"
19 #include "qapi/visitor-impl.h"
20 #include "qemu/queue.h"
21 #include "qemu-common.h"
22 #include "qapi/qmp/qjson.h"
23 #include "qapi/qmp/qbool.h"
24 #include "qapi/qmp/qerror.h"
25 #include "qapi/qmp/qnull.h"
26 #include "qapi/qmp/qnum.h"
27 #include "qemu/cutils.h"
28 #include "qemu/option.h"
29 
30 typedef struct StackObject {
31     const char *name;            /* Name of @obj in its parent, if any */
32     QObject *obj;                /* QDict or QList being visited */
33     void *qapi; /* sanity check that caller uses same pointer */
34 
35     GHashTable *h;              /* If @obj is QDict: unvisited keys */
36     const QListEntry *entry;    /* If @obj is QList: unvisited tail */
37     unsigned index;             /* If @obj is QList: list index of @entry */
38 
39     QSLIST_ENTRY(StackObject) node; /* parent */
40 } StackObject;
41 
42 struct QObjectInputVisitor {
43     Visitor visitor;
44 
45     /* Root of visit at visitor creation. */
46     QObject *root;
47     bool keyval;                /* Assume @root made with keyval_parse() */
48 
49     /* Stack of objects being visited (all entries will be either
50      * QDict or QList). */
51     QSLIST_HEAD(, StackObject) stack;
52 
53     GString *errname;           /* Accumulator for full_name() */
54 };
55 
56 static QObjectInputVisitor *to_qiv(Visitor *v)
57 {
58     return container_of(v, QObjectInputVisitor, visitor);
59 }
60 
61 /*
62  * Find the full name of something @qiv is currently visiting.
63  * @qiv is visiting something named @name in the stack of containers
64  * @qiv->stack.
65  * If @n is zero, return its full name.
66  * If @n is positive, return the full name of the @n-th container
67  * counting from the top.  The stack of containers must have at least
68  * @n elements.
69  * The returned string is valid until the next full_name_nth(@v) or
70  * destruction of @v.
71  */
72 static const char *full_name_nth(QObjectInputVisitor *qiv, const char *name,
73                                  int n)
74 {
75     StackObject *so;
76     char buf[32];
77 
78     if (qiv->errname) {
79         g_string_truncate(qiv->errname, 0);
80     } else {
81         qiv->errname = g_string_new("");
82     }
83 
84     QSLIST_FOREACH(so , &qiv->stack, node) {
85         if (n) {
86             n--;
87         } else if (qobject_type(so->obj) == QTYPE_QDICT) {
88             g_string_prepend(qiv->errname, name ?: "<anonymous>");
89             g_string_prepend_c(qiv->errname, '.');
90         } else {
91             snprintf(buf, sizeof(buf),
92                      qiv->keyval ? ".%u" : "[%u]",
93                      so->index);
94             g_string_prepend(qiv->errname, buf);
95         }
96         name = so->name;
97     }
98     assert(!n);
99 
100     if (name) {
101         g_string_prepend(qiv->errname, name);
102     } else if (qiv->errname->str[0] == '.') {
103         g_string_erase(qiv->errname, 0, 1);
104     } else if (!qiv->errname->str[0]) {
105         return "<anonymous>";
106     }
107 
108     return qiv->errname->str;
109 }
110 
111 static const char *full_name(QObjectInputVisitor *qiv, const char *name)
112 {
113     return full_name_nth(qiv, name, 0);
114 }
115 
116 static QObject *qobject_input_try_get_object(QObjectInputVisitor *qiv,
117                                              const char *name,
118                                              bool consume)
119 {
120     StackObject *tos;
121     QObject *qobj;
122     QObject *ret;
123 
124     if (QSLIST_EMPTY(&qiv->stack)) {
125         /* Starting at root, name is ignored. */
126         assert(qiv->root);
127         return qiv->root;
128     }
129 
130     /* We are in a container; find the next element. */
131     tos = QSLIST_FIRST(&qiv->stack);
132     qobj = tos->obj;
133     assert(qobj);
134 
135     if (qobject_type(qobj) == QTYPE_QDICT) {
136         assert(name);
137         ret = qdict_get(qobject_to_qdict(qobj), name);
138         if (tos->h && consume && ret) {
139             bool removed = g_hash_table_remove(tos->h, name);
140             assert(removed);
141         }
142     } else {
143         assert(qobject_type(qobj) == QTYPE_QLIST);
144         assert(!name);
145         if (tos->entry) {
146             ret = qlist_entry_obj(tos->entry);
147             if (consume) {
148                 tos->entry = qlist_next(tos->entry);
149             }
150         } else {
151             ret = NULL;
152         }
153         if (consume) {
154             tos->index++;
155         }
156     }
157 
158     return ret;
159 }
160 
161 static QObject *qobject_input_get_object(QObjectInputVisitor *qiv,
162                                          const char *name,
163                                          bool consume, Error **errp)
164 {
165     QObject *obj = qobject_input_try_get_object(qiv, name, consume);
166 
167     if (!obj) {
168         error_setg(errp, QERR_MISSING_PARAMETER, full_name(qiv, name));
169     }
170     return obj;
171 }
172 
173 static const char *qobject_input_get_keyval(QObjectInputVisitor *qiv,
174                                             const char *name,
175                                             Error **errp)
176 {
177     QObject *qobj;
178     QString *qstr;
179 
180     qobj = qobject_input_get_object(qiv, name, true, errp);
181     if (!qobj) {
182         return NULL;
183     }
184 
185     qstr = qobject_to_qstring(qobj);
186     if (!qstr) {
187         switch (qobject_type(qobj)) {
188         case QTYPE_QDICT:
189         case QTYPE_QLIST:
190             error_setg(errp, "Parameters '%s.*' are unexpected",
191                        full_name(qiv, name));
192             return NULL;
193         default:
194             /* Non-string scalar (should this be an assertion?) */
195             error_setg(errp, "Internal error: parameter %s invalid",
196                        full_name(qiv, name));
197             return NULL;
198         }
199     }
200 
201     return qstring_get_str(qstr);
202 }
203 
204 static void qdict_add_key(const char *key, QObject *obj, void *opaque)
205 {
206     GHashTable *h = opaque;
207     g_hash_table_insert(h, (gpointer) key, NULL);
208 }
209 
210 static const QListEntry *qobject_input_push(QObjectInputVisitor *qiv,
211                                             const char *name,
212                                             QObject *obj, void *qapi)
213 {
214     GHashTable *h;
215     StackObject *tos = g_new0(StackObject, 1);
216 
217     assert(obj);
218     tos->name = name;
219     tos->obj = obj;
220     tos->qapi = qapi;
221 
222     if (qobject_type(obj) == QTYPE_QDICT) {
223         h = g_hash_table_new(g_str_hash, g_str_equal);
224         qdict_iter(qobject_to_qdict(obj), qdict_add_key, h);
225         tos->h = h;
226     } else {
227         assert(qobject_type(obj) == QTYPE_QLIST);
228         tos->entry = qlist_first(qobject_to_qlist(obj));
229         tos->index = -1;
230     }
231 
232     QSLIST_INSERT_HEAD(&qiv->stack, tos, node);
233     return tos->entry;
234 }
235 
236 
237 static void qobject_input_check_struct(Visitor *v, Error **errp)
238 {
239     QObjectInputVisitor *qiv = to_qiv(v);
240     StackObject *tos = QSLIST_FIRST(&qiv->stack);
241     GHashTableIter iter;
242     const char *key;
243 
244     assert(tos && !tos->entry);
245 
246     g_hash_table_iter_init(&iter, tos->h);
247     if (g_hash_table_iter_next(&iter, (void **)&key, NULL)) {
248         error_setg(errp, "Parameter '%s' is unexpected",
249                    full_name(qiv, key));
250     }
251 }
252 
253 static void qobject_input_stack_object_free(StackObject *tos)
254 {
255     if (tos->h) {
256         g_hash_table_unref(tos->h);
257     }
258 
259     g_free(tos);
260 }
261 
262 static void qobject_input_pop(Visitor *v, void **obj)
263 {
264     QObjectInputVisitor *qiv = to_qiv(v);
265     StackObject *tos = QSLIST_FIRST(&qiv->stack);
266 
267     assert(tos && tos->qapi == obj);
268     QSLIST_REMOVE_HEAD(&qiv->stack, node);
269     qobject_input_stack_object_free(tos);
270 }
271 
272 static void qobject_input_start_struct(Visitor *v, const char *name, void **obj,
273                                        size_t size, Error **errp)
274 {
275     QObjectInputVisitor *qiv = to_qiv(v);
276     QObject *qobj = qobject_input_get_object(qiv, name, true, errp);
277 
278     if (obj) {
279         *obj = NULL;
280     }
281     if (!qobj) {
282         return;
283     }
284     if (qobject_type(qobj) != QTYPE_QDICT) {
285         error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
286                    full_name(qiv, name), "object");
287         return;
288     }
289 
290     qobject_input_push(qiv, name, qobj, obj);
291 
292     if (obj) {
293         *obj = g_malloc0(size);
294     }
295 }
296 
297 static void qobject_input_end_struct(Visitor *v, void **obj)
298 {
299     QObjectInputVisitor *qiv = to_qiv(v);
300     StackObject *tos = QSLIST_FIRST(&qiv->stack);
301 
302     assert(qobject_type(tos->obj) == QTYPE_QDICT && tos->h);
303     qobject_input_pop(v, obj);
304 }
305 
306 
307 static void qobject_input_start_list(Visitor *v, const char *name,
308                                      GenericList **list, size_t size,
309                                      Error **errp)
310 {
311     QObjectInputVisitor *qiv = to_qiv(v);
312     QObject *qobj = qobject_input_get_object(qiv, name, true, errp);
313     const QListEntry *entry;
314 
315     if (list) {
316         *list = NULL;
317     }
318     if (!qobj) {
319         return;
320     }
321     if (qobject_type(qobj) != QTYPE_QLIST) {
322         error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
323                    full_name(qiv, name), "array");
324         return;
325     }
326 
327     entry = qobject_input_push(qiv, name, qobj, list);
328     if (entry && list) {
329         *list = g_malloc0(size);
330     }
331 }
332 
333 static GenericList *qobject_input_next_list(Visitor *v, GenericList *tail,
334                                             size_t size)
335 {
336     QObjectInputVisitor *qiv = to_qiv(v);
337     StackObject *tos = QSLIST_FIRST(&qiv->stack);
338 
339     assert(tos && tos->obj && qobject_type(tos->obj) == QTYPE_QLIST);
340 
341     if (!tos->entry) {
342         return NULL;
343     }
344     tail->next = g_malloc0(size);
345     return tail->next;
346 }
347 
348 static void qobject_input_check_list(Visitor *v, Error **errp)
349 {
350     QObjectInputVisitor *qiv = to_qiv(v);
351     StackObject *tos = QSLIST_FIRST(&qiv->stack);
352 
353     assert(tos && tos->obj && qobject_type(tos->obj) == QTYPE_QLIST);
354 
355     if (tos->entry) {
356         error_setg(errp, "Only %u list elements expected in %s",
357                    tos->index + 1, full_name_nth(qiv, NULL, 1));
358     }
359 }
360 
361 static void qobject_input_end_list(Visitor *v, void **obj)
362 {
363     QObjectInputVisitor *qiv = to_qiv(v);
364     StackObject *tos = QSLIST_FIRST(&qiv->stack);
365 
366     assert(qobject_type(tos->obj) == QTYPE_QLIST && !tos->h);
367     qobject_input_pop(v, obj);
368 }
369 
370 static void qobject_input_start_alternate(Visitor *v, const char *name,
371                                           GenericAlternate **obj, size_t size,
372                                           Error **errp)
373 {
374     QObjectInputVisitor *qiv = to_qiv(v);
375     QObject *qobj = qobject_input_get_object(qiv, name, false, errp);
376 
377     if (!qobj) {
378         *obj = NULL;
379         return;
380     }
381     *obj = g_malloc0(size);
382     (*obj)->type = qobject_type(qobj);
383 }
384 
385 static void qobject_input_type_int64(Visitor *v, const char *name, int64_t *obj,
386                                      Error **errp)
387 {
388     QObjectInputVisitor *qiv = to_qiv(v);
389     QObject *qobj = qobject_input_get_object(qiv, name, true, errp);
390     QNum *qnum;
391 
392     if (!qobj) {
393         return;
394     }
395     qnum = qobject_to_qnum(qobj);
396     if (!qnum || !qnum_get_try_int(qnum, obj)) {
397         error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
398                    full_name(qiv, name), "integer");
399     }
400 }
401 
402 static void qobject_input_type_int64_keyval(Visitor *v, const char *name,
403                                             int64_t *obj, Error **errp)
404 {
405     QObjectInputVisitor *qiv = to_qiv(v);
406     const char *str = qobject_input_get_keyval(qiv, name, errp);
407 
408     if (!str) {
409         return;
410     }
411 
412     if (qemu_strtoi64(str, NULL, 0, obj) < 0) {
413         /* TODO report -ERANGE more nicely */
414         error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
415                    full_name(qiv, name), "integer");
416     }
417 }
418 
419 static void qobject_input_type_uint64(Visitor *v, const char *name,
420                                       uint64_t *obj, Error **errp)
421 {
422     QObjectInputVisitor *qiv = to_qiv(v);
423     QObject *qobj = qobject_input_get_object(qiv, name, true, errp);
424     QNum *qnum;
425     int64_t val;
426 
427     if (!qobj) {
428         return;
429     }
430     qnum = qobject_to_qnum(qobj);
431     if (!qnum) {
432         goto err;
433     }
434 
435     if (qnum_get_try_uint(qnum, obj)) {
436         return;
437     }
438 
439     /* Need to accept negative values for backward compatibility */
440     if (qnum_get_try_int(qnum, &val)) {
441         *obj = val;
442         return;
443     }
444 
445 err:
446     error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
447                full_name(qiv, name), "uint64");
448 }
449 
450 static void qobject_input_type_uint64_keyval(Visitor *v, const char *name,
451                                              uint64_t *obj, Error **errp)
452 {
453     QObjectInputVisitor *qiv = to_qiv(v);
454     const char *str = qobject_input_get_keyval(qiv, name, errp);
455 
456     if (!str) {
457         return;
458     }
459 
460     if (qemu_strtou64(str, NULL, 0, obj) < 0) {
461         /* TODO report -ERANGE more nicely */
462         error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
463                    full_name(qiv, name), "integer");
464     }
465 }
466 
467 static void qobject_input_type_bool(Visitor *v, const char *name, bool *obj,
468                                     Error **errp)
469 {
470     QObjectInputVisitor *qiv = to_qiv(v);
471     QObject *qobj = qobject_input_get_object(qiv, name, true, errp);
472     QBool *qbool;
473 
474     if (!qobj) {
475         return;
476     }
477     qbool = qobject_to_qbool(qobj);
478     if (!qbool) {
479         error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
480                    full_name(qiv, name), "boolean");
481         return;
482     }
483 
484     *obj = qbool_get_bool(qbool);
485 }
486 
487 static void qobject_input_type_bool_keyval(Visitor *v, const char *name,
488                                            bool *obj, Error **errp)
489 {
490     QObjectInputVisitor *qiv = to_qiv(v);
491     const char *str = qobject_input_get_keyval(qiv, name, errp);
492 
493     if (!str) {
494         return;
495     }
496 
497     if (!strcmp(str, "on")) {
498         *obj = true;
499     } else if (!strcmp(str, "off")) {
500         *obj = false;
501     } else {
502         error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
503                    full_name(qiv, name), "'on' or 'off'");
504     }
505 }
506 
507 static void qobject_input_type_str(Visitor *v, const char *name, char **obj,
508                                    Error **errp)
509 {
510     QObjectInputVisitor *qiv = to_qiv(v);
511     QObject *qobj = qobject_input_get_object(qiv, name, true, errp);
512     QString *qstr;
513 
514     *obj = NULL;
515     if (!qobj) {
516         return;
517     }
518     qstr = qobject_to_qstring(qobj);
519     if (!qstr) {
520         error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
521                    full_name(qiv, name), "string");
522         return;
523     }
524 
525     *obj = g_strdup(qstring_get_str(qstr));
526 }
527 
528 static void qobject_input_type_str_keyval(Visitor *v, const char *name,
529                                           char **obj, Error **errp)
530 {
531     QObjectInputVisitor *qiv = to_qiv(v);
532     const char *str = qobject_input_get_keyval(qiv, name, errp);
533 
534     *obj = g_strdup(str);
535 }
536 
537 static void qobject_input_type_number(Visitor *v, const char *name, double *obj,
538                                       Error **errp)
539 {
540     QObjectInputVisitor *qiv = to_qiv(v);
541     QObject *qobj = qobject_input_get_object(qiv, name, true, errp);
542     QNum *qnum;
543 
544     if (!qobj) {
545         return;
546     }
547     qnum = qobject_to_qnum(qobj);
548     if (!qnum) {
549         error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
550                    full_name(qiv, name), "number");
551         return;
552     }
553 
554     *obj = qnum_get_double(qnum);
555 }
556 
557 static void qobject_input_type_number_keyval(Visitor *v, const char *name,
558                                              double *obj, Error **errp)
559 {
560     QObjectInputVisitor *qiv = to_qiv(v);
561     const char *str = qobject_input_get_keyval(qiv, name, errp);
562     char *endp;
563 
564     if (!str) {
565         return;
566     }
567 
568     errno = 0;
569     *obj = strtod(str, &endp);
570     if (errno || endp == str || *endp || !isfinite(*obj)) {
571         /* TODO report -ERANGE more nicely */
572         error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
573                    full_name(qiv, name), "number");
574     }
575 }
576 
577 static void qobject_input_type_any(Visitor *v, const char *name, QObject **obj,
578                                    Error **errp)
579 {
580     QObjectInputVisitor *qiv = to_qiv(v);
581     QObject *qobj = qobject_input_get_object(qiv, name, true, errp);
582 
583     *obj = NULL;
584     if (!qobj) {
585         return;
586     }
587 
588     qobject_incref(qobj);
589     *obj = qobj;
590 }
591 
592 static void qobject_input_type_null(Visitor *v, const char *name,
593                                     QNull **obj, Error **errp)
594 {
595     QObjectInputVisitor *qiv = to_qiv(v);
596     QObject *qobj = qobject_input_get_object(qiv, name, true, errp);
597 
598     *obj = NULL;
599     if (!qobj) {
600         return;
601     }
602 
603     if (qobject_type(qobj) != QTYPE_QNULL) {
604         error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
605                    full_name(qiv, name), "null");
606         return;
607     }
608     *obj = qnull();
609 }
610 
611 static void qobject_input_type_size_keyval(Visitor *v, const char *name,
612                                            uint64_t *obj, Error **errp)
613 {
614     QObjectInputVisitor *qiv = to_qiv(v);
615     const char *str = qobject_input_get_keyval(qiv, name, errp);
616 
617     if (!str) {
618         return;
619     }
620 
621     if (qemu_strtosz(str, NULL, obj) < 0) {
622         /* TODO report -ERANGE more nicely */
623         error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
624                    full_name(qiv, name), "size");
625     }
626 }
627 
628 static void qobject_input_optional(Visitor *v, const char *name, bool *present)
629 {
630     QObjectInputVisitor *qiv = to_qiv(v);
631     QObject *qobj = qobject_input_try_get_object(qiv, name, false);
632 
633     if (!qobj) {
634         *present = false;
635         return;
636     }
637 
638     *present = true;
639 }
640 
641 static void qobject_input_free(Visitor *v)
642 {
643     QObjectInputVisitor *qiv = to_qiv(v);
644 
645     while (!QSLIST_EMPTY(&qiv->stack)) {
646         StackObject *tos = QSLIST_FIRST(&qiv->stack);
647 
648         QSLIST_REMOVE_HEAD(&qiv->stack, node);
649         qobject_input_stack_object_free(tos);
650     }
651 
652     qobject_decref(qiv->root);
653     if (qiv->errname) {
654         g_string_free(qiv->errname, TRUE);
655     }
656     g_free(qiv);
657 }
658 
659 static QObjectInputVisitor *qobject_input_visitor_base_new(QObject *obj)
660 {
661     QObjectInputVisitor *v = g_malloc0(sizeof(*v));
662 
663     assert(obj);
664 
665     v->visitor.type = VISITOR_INPUT;
666     v->visitor.start_struct = qobject_input_start_struct;
667     v->visitor.check_struct = qobject_input_check_struct;
668     v->visitor.end_struct = qobject_input_end_struct;
669     v->visitor.start_list = qobject_input_start_list;
670     v->visitor.next_list = qobject_input_next_list;
671     v->visitor.check_list = qobject_input_check_list;
672     v->visitor.end_list = qobject_input_end_list;
673     v->visitor.start_alternate = qobject_input_start_alternate;
674     v->visitor.optional = qobject_input_optional;
675     v->visitor.free = qobject_input_free;
676 
677     v->root = obj;
678     qobject_incref(obj);
679 
680     return v;
681 }
682 
683 Visitor *qobject_input_visitor_new(QObject *obj)
684 {
685     QObjectInputVisitor *v = qobject_input_visitor_base_new(obj);
686 
687     v->visitor.type_int64 = qobject_input_type_int64;
688     v->visitor.type_uint64 = qobject_input_type_uint64;
689     v->visitor.type_bool = qobject_input_type_bool;
690     v->visitor.type_str = qobject_input_type_str;
691     v->visitor.type_number = qobject_input_type_number;
692     v->visitor.type_any = qobject_input_type_any;
693     v->visitor.type_null = qobject_input_type_null;
694 
695     return &v->visitor;
696 }
697 
698 Visitor *qobject_input_visitor_new_keyval(QObject *obj)
699 {
700     QObjectInputVisitor *v = qobject_input_visitor_base_new(obj);
701 
702     v->visitor.type_int64 = qobject_input_type_int64_keyval;
703     v->visitor.type_uint64 = qobject_input_type_uint64_keyval;
704     v->visitor.type_bool = qobject_input_type_bool_keyval;
705     v->visitor.type_str = qobject_input_type_str_keyval;
706     v->visitor.type_number = qobject_input_type_number_keyval;
707     v->visitor.type_any = qobject_input_type_any;
708     v->visitor.type_null = qobject_input_type_null;
709     v->visitor.type_size = qobject_input_type_size_keyval;
710     v->keyval = true;
711 
712     return &v->visitor;
713 }
714 
715 Visitor *qobject_input_visitor_new_str(const char *str,
716                                        const char *implied_key,
717                                        Error **errp)
718 {
719     bool is_json = str[0] == '{';
720     QObject *obj;
721     QDict *args;
722     Visitor *v;
723 
724     if (is_json) {
725         obj = qobject_from_json(str, errp);
726         if (!obj) {
727             /* Work around qobject_from_json() lossage TODO fix that */
728             if (errp && !*errp) {
729                 error_setg(errp, "JSON parse error");
730                 return NULL;
731             }
732             return NULL;
733         }
734         args = qobject_to_qdict(obj);
735         assert(args);
736         v = qobject_input_visitor_new(QOBJECT(args));
737     } else {
738         args = keyval_parse(str, implied_key, errp);
739         if (!args) {
740             return NULL;
741         }
742         v = qobject_input_visitor_new_keyval(QOBJECT(args));
743     }
744     QDECREF(args);
745 
746     return v;
747 }
748