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