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