xref: /qemu/qapi/qobject-input-visitor.c (revision b3db211f3c80bb996a704d665fe275619f728bd4)
1 /*
2  * Input Visitor
3  *
4  * Copyright (C) 2012-2016 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 
24 #define QIV_STACK_SIZE 1024
25 
26 typedef struct StackObject
27 {
28     QObject *obj; /* Object being visited */
29     void *qapi; /* sanity check that caller uses same pointer */
30 
31     GHashTable *h;           /* If obj is dict: unvisited keys */
32     const QListEntry *entry; /* If obj is list: unvisited tail */
33 
34     QSLIST_ENTRY(StackObject) node;
35 } StackObject;
36 
37 struct QmpInputVisitor
38 {
39     Visitor visitor;
40 
41     /* Root of visit at visitor creation. */
42     QObject *root;
43 
44     /* Stack of objects being visited (all entries will be either
45      * QDict or QList). */
46     QSLIST_HEAD(, StackObject) stack;
47 
48     /* True to reject parse in visit_end_struct() if unvisited keys remain. */
49     bool strict;
50 };
51 
52 static QmpInputVisitor *to_qiv(Visitor *v)
53 {
54     return container_of(v, QmpInputVisitor, visitor);
55 }
56 
57 static QObject *qmp_input_get_object(QmpInputVisitor *qiv,
58                                      const char *name,
59                                      bool consume, Error **errp)
60 {
61     StackObject *tos;
62     QObject *qobj;
63     QObject *ret;
64 
65     if (QSLIST_EMPTY(&qiv->stack)) {
66         /* Starting at root, name is ignored. */
67         assert(qiv->root);
68         return qiv->root;
69     }
70 
71     /* We are in a container; find the next element. */
72     tos = QSLIST_FIRST(&qiv->stack);
73     qobj = tos->obj;
74     assert(qobj);
75 
76     if (qobject_type(qobj) == QTYPE_QDICT) {
77         assert(name);
78         ret = qdict_get(qobject_to_qdict(qobj), name);
79         if (tos->h && consume && ret) {
80             bool removed = g_hash_table_remove(tos->h, name);
81             assert(removed);
82         }
83         if (!ret) {
84             error_setg(errp, QERR_MISSING_PARAMETER, name);
85         }
86     } else {
87         assert(qobject_type(qobj) == QTYPE_QLIST);
88         assert(!name);
89         ret = qlist_entry_obj(tos->entry);
90         assert(ret);
91         if (consume) {
92             tos->entry = qlist_next(tos->entry);
93         }
94     }
95 
96     return ret;
97 }
98 
99 static void qdict_add_key(const char *key, QObject *obj, void *opaque)
100 {
101     GHashTable *h = opaque;
102     g_hash_table_insert(h, (gpointer) key, NULL);
103 }
104 
105 static const QListEntry *qmp_input_push(QmpInputVisitor *qiv, QObject *obj,
106                                         void *qapi, Error **errp)
107 {
108     GHashTable *h;
109     StackObject *tos = g_new0(StackObject, 1);
110 
111     assert(obj);
112     tos->obj = obj;
113     tos->qapi = qapi;
114 
115     if (qiv->strict && qobject_type(obj) == QTYPE_QDICT) {
116         h = g_hash_table_new(g_str_hash, g_str_equal);
117         qdict_iter(qobject_to_qdict(obj), qdict_add_key, h);
118         tos->h = h;
119     } else if (qobject_type(obj) == QTYPE_QLIST) {
120         tos->entry = qlist_first(qobject_to_qlist(obj));
121     }
122 
123     QSLIST_INSERT_HEAD(&qiv->stack, tos, node);
124     return tos->entry;
125 }
126 
127 
128 static void qmp_input_check_struct(Visitor *v, Error **errp)
129 {
130     QmpInputVisitor *qiv = to_qiv(v);
131     StackObject *tos = QSLIST_FIRST(&qiv->stack);
132 
133     assert(tos && !tos->entry);
134     if (qiv->strict) {
135         GHashTable *const top_ht = tos->h;
136         if (top_ht) {
137             GHashTableIter iter;
138             const char *key;
139 
140             g_hash_table_iter_init(&iter, top_ht);
141             if (g_hash_table_iter_next(&iter, (void **)&key, NULL)) {
142                 error_setg(errp, QERR_QMP_EXTRA_MEMBER, key);
143             }
144         }
145     }
146 }
147 
148 static void qmp_input_stack_object_free(StackObject *tos)
149 {
150     if (tos->h) {
151         g_hash_table_unref(tos->h);
152     }
153 
154     g_free(tos);
155 }
156 
157 static void qmp_input_pop(Visitor *v, void **obj)
158 {
159     QmpInputVisitor *qiv = to_qiv(v);
160     StackObject *tos = QSLIST_FIRST(&qiv->stack);
161 
162     assert(tos && tos->qapi == obj);
163     QSLIST_REMOVE_HEAD(&qiv->stack, node);
164     qmp_input_stack_object_free(tos);
165 }
166 
167 static void qmp_input_start_struct(Visitor *v, const char *name, void **obj,
168                                    size_t size, Error **errp)
169 {
170     QmpInputVisitor *qiv = to_qiv(v);
171     QObject *qobj = qmp_input_get_object(qiv, name, true, errp);
172     Error *err = NULL;
173 
174     if (obj) {
175         *obj = NULL;
176     }
177     if (!qobj) {
178         return;
179     }
180     if (qobject_type(qobj) != QTYPE_QDICT) {
181         error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
182                    "QDict");
183         return;
184     }
185 
186     qmp_input_push(qiv, qobj, obj, &err);
187     if (err) {
188         error_propagate(errp, err);
189         return;
190     }
191 
192     if (obj) {
193         *obj = g_malloc0(size);
194     }
195 }
196 
197 
198 static void qmp_input_start_list(Visitor *v, const char *name,
199                                  GenericList **list, size_t size, Error **errp)
200 {
201     QmpInputVisitor *qiv = to_qiv(v);
202     QObject *qobj = qmp_input_get_object(qiv, name, true, errp);
203     const QListEntry *entry;
204 
205     if (!qobj) {
206         return;
207     }
208     if (qobject_type(qobj) != QTYPE_QLIST) {
209         if (list) {
210             *list = NULL;
211         }
212         error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
213                    "list");
214         return;
215     }
216 
217     entry = qmp_input_push(qiv, qobj, list, errp);
218     if (list) {
219         if (entry) {
220             *list = g_malloc0(size);
221         } else {
222             *list = NULL;
223         }
224     }
225 }
226 
227 static GenericList *qmp_input_next_list(Visitor *v, GenericList *tail,
228                                         size_t size)
229 {
230     QmpInputVisitor *qiv = to_qiv(v);
231     StackObject *so = QSLIST_FIRST(&qiv->stack);
232 
233     if (!so->entry) {
234         return NULL;
235     }
236     tail->next = g_malloc0(size);
237     return tail->next;
238 }
239 
240 
241 static void qmp_input_start_alternate(Visitor *v, const char *name,
242                                       GenericAlternate **obj, size_t size,
243                                       bool promote_int, Error **errp)
244 {
245     QmpInputVisitor *qiv = to_qiv(v);
246     QObject *qobj = qmp_input_get_object(qiv, name, false, errp);
247 
248     if (!qobj) {
249         *obj = NULL;
250         return;
251     }
252     *obj = g_malloc0(size);
253     (*obj)->type = qobject_type(qobj);
254     if (promote_int && (*obj)->type == QTYPE_QINT) {
255         (*obj)->type = QTYPE_QFLOAT;
256     }
257 }
258 
259 static void qmp_input_type_int64(Visitor *v, const char *name, int64_t *obj,
260                                  Error **errp)
261 {
262     QmpInputVisitor *qiv = to_qiv(v);
263     QObject *qobj = qmp_input_get_object(qiv, name, true, errp);
264     QInt *qint;
265 
266     if (!qobj) {
267         return;
268     }
269     qint = qobject_to_qint(qobj);
270     if (!qint) {
271         error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
272                    "integer");
273         return;
274     }
275 
276     *obj = qint_get_int(qint);
277 }
278 
279 static void qmp_input_type_uint64(Visitor *v, const char *name, uint64_t *obj,
280                                   Error **errp)
281 {
282     /* FIXME: qobject_to_qint mishandles values over INT64_MAX */
283     QmpInputVisitor *qiv = to_qiv(v);
284     QObject *qobj = qmp_input_get_object(qiv, name, true, errp);
285     QInt *qint;
286 
287     if (!qobj) {
288         return;
289     }
290     qint = qobject_to_qint(qobj);
291     if (!qint) {
292         error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
293                    "integer");
294         return;
295     }
296 
297     *obj = qint_get_int(qint);
298 }
299 
300 static void qmp_input_type_bool(Visitor *v, const char *name, bool *obj,
301                                 Error **errp)
302 {
303     QmpInputVisitor *qiv = to_qiv(v);
304     QObject *qobj = qmp_input_get_object(qiv, name, true, errp);
305     QBool *qbool;
306 
307     if (!qobj) {
308         return;
309     }
310     qbool = qobject_to_qbool(qobj);
311     if (!qbool) {
312         error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
313                    "boolean");
314         return;
315     }
316 
317     *obj = qbool_get_bool(qbool);
318 }
319 
320 static void qmp_input_type_str(Visitor *v, const char *name, char **obj,
321                                Error **errp)
322 {
323     QmpInputVisitor *qiv = to_qiv(v);
324     QObject *qobj = qmp_input_get_object(qiv, name, true, errp);
325     QString *qstr;
326 
327     *obj = NULL;
328     if (!qobj) {
329         return;
330     }
331     qstr = qobject_to_qstring(qobj);
332     if (!qstr) {
333         error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
334                    "string");
335         return;
336     }
337 
338     *obj = g_strdup(qstring_get_str(qstr));
339 }
340 
341 static void qmp_input_type_number(Visitor *v, const char *name, double *obj,
342                                   Error **errp)
343 {
344     QmpInputVisitor *qiv = to_qiv(v);
345     QObject *qobj = qmp_input_get_object(qiv, name, true, errp);
346     QInt *qint;
347     QFloat *qfloat;
348 
349     if (!qobj) {
350         return;
351     }
352     qint = qobject_to_qint(qobj);
353     if (qint) {
354         *obj = qint_get_int(qobject_to_qint(qobj));
355         return;
356     }
357 
358     qfloat = qobject_to_qfloat(qobj);
359     if (qfloat) {
360         *obj = qfloat_get_double(qobject_to_qfloat(qobj));
361         return;
362     }
363 
364     error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
365                "number");
366 }
367 
368 static void qmp_input_type_any(Visitor *v, const char *name, QObject **obj,
369                                Error **errp)
370 {
371     QmpInputVisitor *qiv = to_qiv(v);
372     QObject *qobj = qmp_input_get_object(qiv, name, true, errp);
373 
374     *obj = NULL;
375     if (!qobj) {
376         return;
377     }
378 
379     qobject_incref(qobj);
380     *obj = qobj;
381 }
382 
383 static void qmp_input_type_null(Visitor *v, const char *name, Error **errp)
384 {
385     QmpInputVisitor *qiv = to_qiv(v);
386     QObject *qobj = qmp_input_get_object(qiv, name, true, errp);
387 
388     if (!qobj) {
389         return;
390     }
391 
392     if (qobject_type(qobj) != QTYPE_QNULL) {
393         error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
394                    "null");
395     }
396 }
397 
398 static void qmp_input_optional(Visitor *v, const char *name, bool *present)
399 {
400     QmpInputVisitor *qiv = to_qiv(v);
401     QObject *qobj = qmp_input_get_object(qiv, name, false, NULL);
402 
403     if (!qobj) {
404         *present = false;
405         return;
406     }
407 
408     *present = true;
409 }
410 
411 static void qmp_input_free(Visitor *v)
412 {
413     QmpInputVisitor *qiv = to_qiv(v);
414     while (!QSLIST_EMPTY(&qiv->stack)) {
415         StackObject *tos = QSLIST_FIRST(&qiv->stack);
416 
417         QSLIST_REMOVE_HEAD(&qiv->stack, node);
418         qmp_input_stack_object_free(tos);
419     }
420 
421     qobject_decref(qiv->root);
422     g_free(qiv);
423 }
424 
425 Visitor *qmp_input_visitor_new(QObject *obj, bool strict)
426 {
427     QmpInputVisitor *v;
428 
429     assert(obj);
430     v = g_malloc0(sizeof(*v));
431 
432     v->visitor.type = VISITOR_INPUT;
433     v->visitor.start_struct = qmp_input_start_struct;
434     v->visitor.check_struct = qmp_input_check_struct;
435     v->visitor.end_struct = qmp_input_pop;
436     v->visitor.start_list = qmp_input_start_list;
437     v->visitor.next_list = qmp_input_next_list;
438     v->visitor.end_list = qmp_input_pop;
439     v->visitor.start_alternate = qmp_input_start_alternate;
440     v->visitor.type_int64 = qmp_input_type_int64;
441     v->visitor.type_uint64 = qmp_input_type_uint64;
442     v->visitor.type_bool = qmp_input_type_bool;
443     v->visitor.type_str = qmp_input_type_str;
444     v->visitor.type_number = qmp_input_type_number;
445     v->visitor.type_any = qmp_input_type_any;
446     v->visitor.type_null = qmp_input_type_null;
447     v->visitor.optional = qmp_input_optional;
448     v->visitor.free = qmp_input_free;
449     v->strict = strict;
450 
451     v->root = obj;
452     qobject_incref(obj);
453 
454     return &v->visitor;
455 }
456