]> git.proxmox.com Git - mirror_qemu.git/blob - qapi/qmp-input-visitor.c
virtio: decrement vq->inuse in virtqueue_discard()
[mirror_qemu.git] / qapi / qmp-input-visitor.c
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/qmp-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)
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 return qiv->root;
68 }
69
70 /* We are in a container; find the next element. */
71 tos = QSLIST_FIRST(&qiv->stack);
72 qobj = tos->obj;
73 assert(qobj);
74
75 if (qobject_type(qobj) == QTYPE_QDICT) {
76 assert(name);
77 ret = qdict_get(qobject_to_qdict(qobj), name);
78 if (tos->h && consume && ret) {
79 bool removed = g_hash_table_remove(tos->h, name);
80 assert(removed);
81 }
82 } else {
83 assert(qobject_type(qobj) == QTYPE_QLIST);
84 assert(!name);
85 ret = qlist_entry_obj(tos->entry);
86 if (consume) {
87 tos->entry = qlist_next(tos->entry);
88 }
89 }
90
91 return ret;
92 }
93
94 static void qdict_add_key(const char *key, QObject *obj, void *opaque)
95 {
96 GHashTable *h = opaque;
97 g_hash_table_insert(h, (gpointer) key, NULL);
98 }
99
100 static const QListEntry *qmp_input_push(QmpInputVisitor *qiv, QObject *obj,
101 void *qapi, Error **errp)
102 {
103 GHashTable *h;
104 StackObject *tos = g_new0(StackObject, 1);
105
106 assert(obj);
107 tos->obj = obj;
108 tos->qapi = qapi;
109
110 if (qiv->strict && qobject_type(obj) == QTYPE_QDICT) {
111 h = g_hash_table_new(g_str_hash, g_str_equal);
112 qdict_iter(qobject_to_qdict(obj), qdict_add_key, h);
113 tos->h = h;
114 } else if (qobject_type(obj) == QTYPE_QLIST) {
115 tos->entry = qlist_first(qobject_to_qlist(obj));
116 }
117
118 QSLIST_INSERT_HEAD(&qiv->stack, tos, node);
119 return tos->entry;
120 }
121
122
123 static void qmp_input_check_struct(Visitor *v, Error **errp)
124 {
125 QmpInputVisitor *qiv = to_qiv(v);
126 StackObject *tos = QSLIST_FIRST(&qiv->stack);
127
128 assert(tos && !tos->entry);
129 if (qiv->strict) {
130 GHashTable *const top_ht = tos->h;
131 if (top_ht) {
132 GHashTableIter iter;
133 const char *key;
134
135 g_hash_table_iter_init(&iter, top_ht);
136 if (g_hash_table_iter_next(&iter, (void **)&key, NULL)) {
137 error_setg(errp, QERR_QMP_EXTRA_MEMBER, key);
138 }
139 }
140 }
141 }
142
143 static void qmp_input_stack_object_free(StackObject *tos)
144 {
145 if (tos->h) {
146 g_hash_table_unref(tos->h);
147 }
148
149 g_free(tos);
150 }
151
152 static void qmp_input_pop(Visitor *v, void **obj)
153 {
154 QmpInputVisitor *qiv = to_qiv(v);
155 StackObject *tos = QSLIST_FIRST(&qiv->stack);
156
157 assert(tos && tos->qapi == obj);
158 QSLIST_REMOVE_HEAD(&qiv->stack, node);
159 qmp_input_stack_object_free(tos);
160 }
161
162 static void qmp_input_start_struct(Visitor *v, const char *name, void **obj,
163 size_t size, Error **errp)
164 {
165 QmpInputVisitor *qiv = to_qiv(v);
166 QObject *qobj = qmp_input_get_object(qiv, name, true);
167 Error *err = NULL;
168
169 if (obj) {
170 *obj = NULL;
171 }
172 if (!qobj || qobject_type(qobj) != QTYPE_QDICT) {
173 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
174 "QDict");
175 return;
176 }
177
178 qmp_input_push(qiv, qobj, obj, &err);
179 if (err) {
180 error_propagate(errp, err);
181 return;
182 }
183
184 if (obj) {
185 *obj = g_malloc0(size);
186 }
187 }
188
189
190 static void qmp_input_start_list(Visitor *v, const char *name,
191 GenericList **list, size_t size, Error **errp)
192 {
193 QmpInputVisitor *qiv = to_qiv(v);
194 QObject *qobj = qmp_input_get_object(qiv, name, true);
195 const QListEntry *entry;
196
197 if (!qobj || qobject_type(qobj) != QTYPE_QLIST) {
198 if (list) {
199 *list = NULL;
200 }
201 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
202 "list");
203 return;
204 }
205
206 entry = qmp_input_push(qiv, qobj, list, errp);
207 if (list) {
208 if (entry) {
209 *list = g_malloc0(size);
210 } else {
211 *list = NULL;
212 }
213 }
214 }
215
216 static GenericList *qmp_input_next_list(Visitor *v, GenericList *tail,
217 size_t size)
218 {
219 QmpInputVisitor *qiv = to_qiv(v);
220 StackObject *so = QSLIST_FIRST(&qiv->stack);
221
222 if (!so->entry) {
223 return NULL;
224 }
225 tail->next = g_malloc0(size);
226 return tail->next;
227 }
228
229
230 static void qmp_input_start_alternate(Visitor *v, const char *name,
231 GenericAlternate **obj, size_t size,
232 bool promote_int, Error **errp)
233 {
234 QmpInputVisitor *qiv = to_qiv(v);
235 QObject *qobj = qmp_input_get_object(qiv, name, false);
236
237 if (!qobj) {
238 *obj = NULL;
239 error_setg(errp, QERR_MISSING_PARAMETER, name ? name : "null");
240 return;
241 }
242 *obj = g_malloc0(size);
243 (*obj)->type = qobject_type(qobj);
244 if (promote_int && (*obj)->type == QTYPE_QINT) {
245 (*obj)->type = QTYPE_QFLOAT;
246 }
247 }
248
249 static void qmp_input_type_int64(Visitor *v, const char *name, int64_t *obj,
250 Error **errp)
251 {
252 QmpInputVisitor *qiv = to_qiv(v);
253 QInt *qint = qobject_to_qint(qmp_input_get_object(qiv, name, true));
254
255 if (!qint) {
256 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
257 "integer");
258 return;
259 }
260
261 *obj = qint_get_int(qint);
262 }
263
264 static void qmp_input_type_uint64(Visitor *v, const char *name, uint64_t *obj,
265 Error **errp)
266 {
267 /* FIXME: qobject_to_qint mishandles values over INT64_MAX */
268 QmpInputVisitor *qiv = to_qiv(v);
269 QInt *qint = qobject_to_qint(qmp_input_get_object(qiv, name, true));
270
271 if (!qint) {
272 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
273 "integer");
274 return;
275 }
276
277 *obj = qint_get_int(qint);
278 }
279
280 static void qmp_input_type_bool(Visitor *v, const char *name, bool *obj,
281 Error **errp)
282 {
283 QmpInputVisitor *qiv = to_qiv(v);
284 QBool *qbool = qobject_to_qbool(qmp_input_get_object(qiv, name, true));
285
286 if (!qbool) {
287 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
288 "boolean");
289 return;
290 }
291
292 *obj = qbool_get_bool(qbool);
293 }
294
295 static void qmp_input_type_str(Visitor *v, const char *name, char **obj,
296 Error **errp)
297 {
298 QmpInputVisitor *qiv = to_qiv(v);
299 QString *qstr = qobject_to_qstring(qmp_input_get_object(qiv, name, true));
300
301 if (!qstr) {
302 *obj = NULL;
303 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
304 "string");
305 return;
306 }
307
308 *obj = g_strdup(qstring_get_str(qstr));
309 }
310
311 static void qmp_input_type_number(Visitor *v, const char *name, double *obj,
312 Error **errp)
313 {
314 QmpInputVisitor *qiv = to_qiv(v);
315 QObject *qobj = qmp_input_get_object(qiv, name, true);
316 QInt *qint;
317 QFloat *qfloat;
318
319 qint = qobject_to_qint(qobj);
320 if (qint) {
321 *obj = qint_get_int(qobject_to_qint(qobj));
322 return;
323 }
324
325 qfloat = qobject_to_qfloat(qobj);
326 if (qfloat) {
327 *obj = qfloat_get_double(qobject_to_qfloat(qobj));
328 return;
329 }
330
331 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
332 "number");
333 }
334
335 static void qmp_input_type_any(Visitor *v, const char *name, QObject **obj,
336 Error **errp)
337 {
338 QmpInputVisitor *qiv = to_qiv(v);
339 QObject *qobj = qmp_input_get_object(qiv, name, true);
340
341 qobject_incref(qobj);
342 *obj = qobj;
343 }
344
345 static void qmp_input_type_null(Visitor *v, const char *name, Error **errp)
346 {
347 QmpInputVisitor *qiv = to_qiv(v);
348 QObject *qobj = qmp_input_get_object(qiv, name, true);
349
350 if (qobject_type(qobj) != QTYPE_QNULL) {
351 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
352 "null");
353 }
354 }
355
356 static void qmp_input_optional(Visitor *v, const char *name, bool *present)
357 {
358 QmpInputVisitor *qiv = to_qiv(v);
359 QObject *qobj = qmp_input_get_object(qiv, name, false);
360
361 if (!qobj) {
362 *present = false;
363 return;
364 }
365
366 *present = true;
367 }
368
369 static void qmp_input_free(Visitor *v)
370 {
371 QmpInputVisitor *qiv = to_qiv(v);
372 while (!QSLIST_EMPTY(&qiv->stack)) {
373 StackObject *tos = QSLIST_FIRST(&qiv->stack);
374
375 QSLIST_REMOVE_HEAD(&qiv->stack, node);
376 qmp_input_stack_object_free(tos);
377 }
378
379 qobject_decref(qiv->root);
380 g_free(qiv);
381 }
382
383 Visitor *qmp_input_visitor_new(QObject *obj, bool strict)
384 {
385 QmpInputVisitor *v;
386
387 v = g_malloc0(sizeof(*v));
388
389 v->visitor.type = VISITOR_INPUT;
390 v->visitor.start_struct = qmp_input_start_struct;
391 v->visitor.check_struct = qmp_input_check_struct;
392 v->visitor.end_struct = qmp_input_pop;
393 v->visitor.start_list = qmp_input_start_list;
394 v->visitor.next_list = qmp_input_next_list;
395 v->visitor.end_list = qmp_input_pop;
396 v->visitor.start_alternate = qmp_input_start_alternate;
397 v->visitor.type_int64 = qmp_input_type_int64;
398 v->visitor.type_uint64 = qmp_input_type_uint64;
399 v->visitor.type_bool = qmp_input_type_bool;
400 v->visitor.type_str = qmp_input_type_str;
401 v->visitor.type_number = qmp_input_type_number;
402 v->visitor.type_any = qmp_input_type_any;
403 v->visitor.type_null = qmp_input_type_null;
404 v->visitor.optional = qmp_input_optional;
405 v->visitor.free = qmp_input_free;
406 v->strict = strict;
407
408 v->root = obj;
409 qobject_incref(obj);
410
411 return &v->visitor;
412 }