]>
Commit | Line | Data |
---|---|---|
c40cc0a0 MR |
1 | /* |
2 | * Input Visitor | |
3 | * | |
08f9541d | 4 | * Copyright (C) 2012-2016 Red Hat, Inc. |
c40cc0a0 MR |
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 | ||
cbf21151 | 15 | #include "qemu/osdep.h" |
7b1b5d19 PB |
16 | #include "qapi/qmp-input-visitor.h" |
17 | #include "qapi/visitor-impl.h" | |
1de7afc9 | 18 | #include "qemu/queue.h" |
c40cc0a0 | 19 | #include "qemu-common.h" |
7b1b5d19 PB |
20 | #include "qapi/qmp/types.h" |
21 | #include "qapi/qmp/qerror.h" | |
c40cc0a0 MR |
22 | |
23 | #define QIV_STACK_SIZE 1024 | |
24 | ||
25 | typedef struct StackObject | |
26 | { | |
4faaec6a PB |
27 | QObject *obj; |
28 | const QListEntry *entry; | |
e38ac962 | 29 | GHashTable *h; |
c40cc0a0 MR |
30 | } StackObject; |
31 | ||
32 | struct QmpInputVisitor | |
33 | { | |
34 | Visitor visitor; | |
c40cc0a0 MR |
35 | StackObject stack[QIV_STACK_SIZE]; |
36 | int nb_stack; | |
e38ac962 | 37 | bool strict; |
c40cc0a0 MR |
38 | }; |
39 | ||
40 | static QmpInputVisitor *to_qiv(Visitor *v) | |
41 | { | |
42 | return container_of(v, QmpInputVisitor, visitor); | |
43 | } | |
44 | ||
4faaec6a | 45 | static QObject *qmp_input_get_object(QmpInputVisitor *qiv, |
e8316d7e KW |
46 | const char *name, |
47 | bool consume) | |
c40cc0a0 | 48 | { |
4faaec6a | 49 | QObject *qobj = qiv->stack[qiv->nb_stack - 1].obj; |
c40cc0a0 | 50 | |
47c6d3ec PB |
51 | if (qobj) { |
52 | if (name && qobject_type(qobj) == QTYPE_QDICT) { | |
e8316d7e | 53 | if (qiv->stack[qiv->nb_stack - 1].h && consume) { |
e38ac962 PB |
54 | g_hash_table_remove(qiv->stack[qiv->nb_stack - 1].h, name); |
55 | } | |
47c6d3ec | 56 | return qdict_get(qobject_to_qdict(qobj), name); |
4faaec6a | 57 | } else if (qiv->stack[qiv->nb_stack - 1].entry) { |
47c6d3ec PB |
58 | return qlist_entry_obj(qiv->stack[qiv->nb_stack - 1].entry); |
59 | } | |
c40cc0a0 MR |
60 | } |
61 | ||
62 | return qobj; | |
63 | } | |
64 | ||
e38ac962 PB |
65 | static void qdict_add_key(const char *key, QObject *obj, void *opaque) |
66 | { | |
67 | GHashTable *h = opaque; | |
68 | g_hash_table_insert(h, (gpointer) key, NULL); | |
69 | } | |
70 | ||
4faaec6a | 71 | static void qmp_input_push(QmpInputVisitor *qiv, QObject *obj, Error **errp) |
c40cc0a0 | 72 | { |
e38ac962 | 73 | GHashTable *h; |
c40cc0a0 MR |
74 | |
75 | if (qiv->nb_stack >= QIV_STACK_SIZE) { | |
f231b88d | 76 | error_setg(errp, "An internal buffer overran"); |
c40cc0a0 MR |
77 | return; |
78 | } | |
e38ac962 PB |
79 | |
80 | qiv->stack[qiv->nb_stack].obj = obj; | |
81 | qiv->stack[qiv->nb_stack].entry = NULL; | |
82 | qiv->stack[qiv->nb_stack].h = NULL; | |
83 | ||
84 | if (qiv->strict && qobject_type(obj) == QTYPE_QDICT) { | |
85 | h = g_hash_table_new(g_str_hash, g_str_equal); | |
86 | qdict_iter(qobject_to_qdict(obj), qdict_add_key, h); | |
87 | qiv->stack[qiv->nb_stack].h = h; | |
88 | } | |
89 | ||
90 | qiv->nb_stack++; | |
c40cc0a0 MR |
91 | } |
92 | ||
57a33d89 | 93 | |
c40cc0a0 MR |
94 | static void qmp_input_pop(QmpInputVisitor *qiv, Error **errp) |
95 | { | |
57a33d89 | 96 | assert(qiv->nb_stack > 0); |
e38ac962 | 97 | |
57a33d89 NK |
98 | if (qiv->strict) { |
99 | GHashTable * const top_ht = qiv->stack[qiv->nb_stack - 1].h; | |
100 | if (top_ht) { | |
f96493b1 EB |
101 | GHashTableIter iter; |
102 | const char *key; | |
103 | ||
104 | g_hash_table_iter_init(&iter, top_ht); | |
105 | if (g_hash_table_iter_next(&iter, (void **)&key, NULL)) { | |
c6bd8c70 | 106 | error_setg(errp, QERR_QMP_EXTRA_MEMBER, key); |
57a33d89 NK |
107 | } |
108 | g_hash_table_unref(top_ht); | |
e38ac962 | 109 | } |
e38ac962 PB |
110 | } |
111 | ||
c40cc0a0 | 112 | qiv->nb_stack--; |
c40cc0a0 MR |
113 | } |
114 | ||
0b2a0d6b | 115 | static void qmp_input_start_struct(Visitor *v, const char *name, void **obj, |
337283df | 116 | size_t size, Error **errp) |
c40cc0a0 MR |
117 | { |
118 | QmpInputVisitor *qiv = to_qiv(v); | |
e8316d7e | 119 | QObject *qobj = qmp_input_get_object(qiv, name, true); |
8b714d37 | 120 | Error *err = NULL; |
c40cc0a0 MR |
121 | |
122 | if (!qobj || qobject_type(qobj) != QTYPE_QDICT) { | |
c6bd8c70 MA |
123 | error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", |
124 | "QDict"); | |
c40cc0a0 MR |
125 | return; |
126 | } | |
127 | ||
8b714d37 PB |
128 | qmp_input_push(qiv, qobj, &err); |
129 | if (err) { | |
130 | error_propagate(errp, err); | |
c40cc0a0 MR |
131 | return; |
132 | } | |
133 | ||
134 | if (obj) { | |
7267c094 | 135 | *obj = g_malloc0(size); |
c40cc0a0 MR |
136 | } |
137 | } | |
138 | ||
139 | static void qmp_input_end_struct(Visitor *v, Error **errp) | |
140 | { | |
141 | QmpInputVisitor *qiv = to_qiv(v); | |
142 | ||
143 | qmp_input_pop(qiv, errp); | |
144 | } | |
145 | ||
146 | static void qmp_input_start_list(Visitor *v, const char *name, Error **errp) | |
147 | { | |
148 | QmpInputVisitor *qiv = to_qiv(v); | |
e8316d7e | 149 | QObject *qobj = qmp_input_get_object(qiv, name, true); |
c40cc0a0 MR |
150 | |
151 | if (!qobj || qobject_type(qobj) != QTYPE_QLIST) { | |
c6bd8c70 MA |
152 | error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", |
153 | "list"); | |
c40cc0a0 MR |
154 | return; |
155 | } | |
156 | ||
157 | qmp_input_push(qiv, qobj, errp); | |
158 | } | |
159 | ||
e65d89bf EB |
160 | static GenericList *qmp_input_next_list(Visitor *v, GenericList **list, |
161 | size_t size) | |
c40cc0a0 MR |
162 | { |
163 | QmpInputVisitor *qiv = to_qiv(v); | |
164 | GenericList *entry; | |
165 | StackObject *so = &qiv->stack[qiv->nb_stack - 1]; | |
3a86a0fa PB |
166 | bool first; |
167 | ||
168 | if (so->entry == NULL) { | |
169 | so->entry = qlist_first(qobject_to_qlist(so->obj)); | |
170 | first = true; | |
171 | } else { | |
172 | so->entry = qlist_next(so->entry); | |
173 | first = false; | |
174 | } | |
c40cc0a0 MR |
175 | |
176 | if (so->entry == NULL) { | |
177 | return NULL; | |
178 | } | |
179 | ||
e65d89bf | 180 | entry = g_malloc0(size); |
3a86a0fa PB |
181 | if (first) { |
182 | *list = entry; | |
183 | } else { | |
c40cc0a0 MR |
184 | (*list)->next = entry; |
185 | } | |
c40cc0a0 MR |
186 | |
187 | return entry; | |
188 | } | |
189 | ||
08f9541d | 190 | static void qmp_input_end_list(Visitor *v) |
c40cc0a0 MR |
191 | { |
192 | QmpInputVisitor *qiv = to_qiv(v); | |
193 | ||
bdd8e6b5 | 194 | qmp_input_pop(qiv, &error_abort); |
c40cc0a0 MR |
195 | } |
196 | ||
dbf11922 EB |
197 | static void qmp_input_start_alternate(Visitor *v, const char *name, |
198 | GenericAlternate **obj, size_t size, | |
199 | bool promote_int, Error **errp) | |
69dd62df KW |
200 | { |
201 | QmpInputVisitor *qiv = to_qiv(v); | |
202 | QObject *qobj = qmp_input_get_object(qiv, name, false); | |
203 | ||
204 | if (!qobj) { | |
dbf11922 | 205 | *obj = NULL; |
c6bd8c70 | 206 | error_setg(errp, QERR_MISSING_PARAMETER, name ? name : "null"); |
69dd62df KW |
207 | return; |
208 | } | |
dbf11922 EB |
209 | *obj = g_malloc0(size); |
210 | (*obj)->type = qobject_type(qobj); | |
211 | if (promote_int && (*obj)->type == QTYPE_QINT) { | |
212 | (*obj)->type = QTYPE_QFLOAT; | |
d00341af | 213 | } |
69dd62df KW |
214 | } |
215 | ||
0b2a0d6b | 216 | static void qmp_input_type_int64(Visitor *v, const char *name, int64_t *obj, |
4c40314a | 217 | Error **errp) |
c40cc0a0 MR |
218 | { |
219 | QmpInputVisitor *qiv = to_qiv(v); | |
fcf73f66 | 220 | QInt *qint = qobject_to_qint(qmp_input_get_object(qiv, name, true)); |
c40cc0a0 | 221 | |
fcf73f66 | 222 | if (!qint) { |
c6bd8c70 MA |
223 | error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", |
224 | "integer"); | |
c40cc0a0 MR |
225 | return; |
226 | } | |
227 | ||
fcf73f66 | 228 | *obj = qint_get_int(qint); |
c40cc0a0 MR |
229 | } |
230 | ||
0b2a0d6b | 231 | static void qmp_input_type_uint64(Visitor *v, const char *name, uint64_t *obj, |
f755dea7 EB |
232 | Error **errp) |
233 | { | |
234 | /* FIXME: qobject_to_qint mishandles values over INT64_MAX */ | |
235 | QmpInputVisitor *qiv = to_qiv(v); | |
236 | QInt *qint = qobject_to_qint(qmp_input_get_object(qiv, name, true)); | |
237 | ||
238 | if (!qint) { | |
239 | error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", | |
240 | "integer"); | |
241 | return; | |
242 | } | |
243 | ||
244 | *obj = qint_get_int(qint); | |
245 | } | |
246 | ||
0b2a0d6b | 247 | static void qmp_input_type_bool(Visitor *v, const char *name, bool *obj, |
c40cc0a0 MR |
248 | Error **errp) |
249 | { | |
250 | QmpInputVisitor *qiv = to_qiv(v); | |
14b61600 | 251 | QBool *qbool = qobject_to_qbool(qmp_input_get_object(qiv, name, true)); |
c40cc0a0 | 252 | |
14b61600 | 253 | if (!qbool) { |
c6bd8c70 MA |
254 | error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", |
255 | "boolean"); | |
c40cc0a0 MR |
256 | return; |
257 | } | |
258 | ||
14b61600 | 259 | *obj = qbool_get_bool(qbool); |
c40cc0a0 MR |
260 | } |
261 | ||
0b2a0d6b | 262 | static void qmp_input_type_str(Visitor *v, const char *name, char **obj, |
c40cc0a0 MR |
263 | Error **errp) |
264 | { | |
265 | QmpInputVisitor *qiv = to_qiv(v); | |
7f027843 | 266 | QString *qstr = qobject_to_qstring(qmp_input_get_object(qiv, name, true)); |
c40cc0a0 | 267 | |
7f027843 | 268 | if (!qstr) { |
c6bd8c70 MA |
269 | error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", |
270 | "string"); | |
c40cc0a0 MR |
271 | return; |
272 | } | |
273 | ||
7f027843 | 274 | *obj = g_strdup(qstring_get_str(qstr)); |
c40cc0a0 MR |
275 | } |
276 | ||
0b2a0d6b | 277 | static void qmp_input_type_number(Visitor *v, const char *name, double *obj, |
c40cc0a0 MR |
278 | Error **errp) |
279 | { | |
280 | QmpInputVisitor *qiv = to_qiv(v); | |
e8316d7e | 281 | QObject *qobj = qmp_input_get_object(qiv, name, true); |
fcf73f66 MA |
282 | QInt *qint; |
283 | QFloat *qfloat; | |
c40cc0a0 | 284 | |
fcf73f66 MA |
285 | qint = qobject_to_qint(qobj); |
286 | if (qint) { | |
287 | *obj = qint_get_int(qobject_to_qint(qobj)); | |
c40cc0a0 MR |
288 | return; |
289 | } | |
290 | ||
fcf73f66 MA |
291 | qfloat = qobject_to_qfloat(qobj); |
292 | if (qfloat) { | |
1ee51876 | 293 | *obj = qfloat_get_double(qobject_to_qfloat(qobj)); |
fcf73f66 | 294 | return; |
1ee51876 | 295 | } |
fcf73f66 MA |
296 | |
297 | error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", | |
298 | "number"); | |
c40cc0a0 MR |
299 | } |
300 | ||
0b2a0d6b | 301 | static void qmp_input_type_any(Visitor *v, const char *name, QObject **obj, |
28770e05 MA |
302 | Error **errp) |
303 | { | |
304 | QmpInputVisitor *qiv = to_qiv(v); | |
305 | QObject *qobj = qmp_input_get_object(qiv, name, true); | |
306 | ||
307 | qobject_incref(qobj); | |
308 | *obj = qobj; | |
309 | } | |
310 | ||
0b2a0d6b | 311 | static void qmp_input_optional(Visitor *v, const char *name, bool *present) |
c40cc0a0 MR |
312 | { |
313 | QmpInputVisitor *qiv = to_qiv(v); | |
e8316d7e | 314 | QObject *qobj = qmp_input_get_object(qiv, name, true); |
c40cc0a0 MR |
315 | |
316 | if (!qobj) { | |
317 | *present = false; | |
318 | return; | |
319 | } | |
320 | ||
321 | *present = true; | |
322 | } | |
323 | ||
c40cc0a0 MR |
324 | Visitor *qmp_input_get_visitor(QmpInputVisitor *v) |
325 | { | |
326 | return &v->visitor; | |
327 | } | |
328 | ||
329 | void qmp_input_visitor_cleanup(QmpInputVisitor *v) | |
330 | { | |
4faaec6a | 331 | qobject_decref(v->stack[0].obj); |
7267c094 | 332 | g_free(v); |
c40cc0a0 MR |
333 | } |
334 | ||
335 | QmpInputVisitor *qmp_input_visitor_new(QObject *obj) | |
336 | { | |
337 | QmpInputVisitor *v; | |
338 | ||
7267c094 | 339 | v = g_malloc0(sizeof(*v)); |
c40cc0a0 MR |
340 | |
341 | v->visitor.start_struct = qmp_input_start_struct; | |
342 | v->visitor.end_struct = qmp_input_end_struct; | |
343 | v->visitor.start_list = qmp_input_start_list; | |
344 | v->visitor.next_list = qmp_input_next_list; | |
345 | v->visitor.end_list = qmp_input_end_list; | |
dbf11922 | 346 | v->visitor.start_alternate = qmp_input_start_alternate; |
0f71a1e0 | 347 | v->visitor.type_enum = input_type_enum; |
4c40314a | 348 | v->visitor.type_int64 = qmp_input_type_int64; |
f755dea7 | 349 | v->visitor.type_uint64 = qmp_input_type_uint64; |
c40cc0a0 MR |
350 | v->visitor.type_bool = qmp_input_type_bool; |
351 | v->visitor.type_str = qmp_input_type_str; | |
352 | v->visitor.type_number = qmp_input_type_number; | |
28770e05 | 353 | v->visitor.type_any = qmp_input_type_any; |
e2cd0f4f | 354 | v->visitor.optional = qmp_input_optional; |
c40cc0a0 | 355 | |
4faaec6a PB |
356 | qmp_input_push(v, obj, NULL); |
357 | qobject_incref(obj); | |
c40cc0a0 MR |
358 | |
359 | return v; | |
360 | } | |
e38ac962 PB |
361 | |
362 | QmpInputVisitor *qmp_input_visitor_new_strict(QObject *obj) | |
363 | { | |
364 | QmpInputVisitor *v; | |
365 | ||
366 | v = qmp_input_visitor_new(obj); | |
367 | v->strict = true; | |
368 | ||
369 | return v; | |
370 | } |