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