]>
Commit | Line | Data |
---|---|---|
c40cc0a0 MR |
1 | /* |
2 | * Input Visitor | |
3 | * | |
cbd8acf3 | 4 | * Copyright (C) 2012-2017 Red Hat, Inc. |
c40cc0a0 MR |
5 | * Copyright IBM, Corp. 2011 |
6 | * | |
7 | * Authors: | |
8 | * Anthony Liguori <[email protected]> | |
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" |
5891c388 | 16 | #include <math.h> |
db291641 | 17 | #include "qapi/compat-policy.h" |
da34e65c | 18 | #include "qapi/error.h" |
b3db211f | 19 | #include "qapi/qobject-input-visitor.h" |
7b1b5d19 | 20 | #include "qapi/visitor-impl.h" |
1de7afc9 | 21 | #include "qemu/queue.h" |
9d1eab4b | 22 | #include "qapi/qmp/qjson.h" |
6b673957 | 23 | #include "qapi/qmp/qbool.h" |
452fcdbc | 24 | #include "qapi/qmp/qdict.h" |
7b1b5d19 | 25 | #include "qapi/qmp/qerror.h" |
47e6b297 | 26 | #include "qapi/qmp/qlist.h" |
15280c36 MA |
27 | #include "qapi/qmp/qnull.h" |
28 | #include "qapi/qmp/qnum.h" | |
fc81fa1e | 29 | #include "qapi/qmp/qstring.h" |
cbd8acf3 | 30 | #include "qemu/cutils.h" |
9d1eab4b | 31 | #include "qemu/option.h" |
c40cc0a0 | 32 | |
a9fc37f6 MA |
33 | typedef struct StackObject { |
34 | const char *name; /* Name of @obj in its parent, if any */ | |
35 | QObject *obj; /* QDict or QList being visited */ | |
1158bb2a | 36 | void *qapi; /* sanity check that caller uses same pointer */ |
b471d012 | 37 | |
a9fc37f6 MA |
38 | GHashTable *h; /* If @obj is QDict: unvisited keys */ |
39 | const QListEntry *entry; /* If @obj is QList: unvisited tail */ | |
40 | unsigned index; /* If @obj is QList: list index of @entry */ | |
3d344c2a | 41 | |
a9fc37f6 | 42 | QSLIST_ENTRY(StackObject) node; /* parent */ |
c40cc0a0 MR |
43 | } StackObject; |
44 | ||
a9fc37f6 | 45 | struct QObjectInputVisitor { |
c40cc0a0 | 46 | Visitor visitor; |
db291641 | 47 | CompatPolicyInput deprecated_policy; |
b471d012 | 48 | |
ce140b17 EB |
49 | /* Root of visit at visitor creation. */ |
50 | QObject *root; | |
0b2c1bee | 51 | bool keyval; /* Assume @root made with keyval_parse() */ |
ce140b17 EB |
52 | |
53 | /* Stack of objects being visited (all entries will be either | |
54 | * QDict or QList). */ | |
3d344c2a | 55 | QSLIST_HEAD(, StackObject) stack; |
b471d012 | 56 | |
a9fc37f6 | 57 | GString *errname; /* Accumulator for full_name() */ |
c40cc0a0 MR |
58 | }; |
59 | ||
09e68369 | 60 | static QObjectInputVisitor *to_qiv(Visitor *v) |
c40cc0a0 | 61 | { |
09e68369 | 62 | return container_of(v, QObjectInputVisitor, visitor); |
c40cc0a0 MR |
63 | } |
64 | ||
6c02258e MA |
65 | /* |
66 | * Find the full name of something @qiv is currently visiting. | |
67 | * @qiv is visiting something named @name in the stack of containers | |
68 | * @qiv->stack. | |
69 | * If @n is zero, return its full name. | |
70 | * If @n is positive, return the full name of the @n-th container | |
71 | * counting from the top. The stack of containers must have at least | |
72 | * @n elements. | |
73 | * The returned string is valid until the next full_name_nth(@v) or | |
74 | * destruction of @v. | |
75 | */ | |
a4a1c70d MA |
76 | static const char *full_name_nth(QObjectInputVisitor *qiv, const char *name, |
77 | int n) | |
a9fc37f6 MA |
78 | { |
79 | StackObject *so; | |
80 | char buf[32]; | |
81 | ||
82 | if (qiv->errname) { | |
83 | g_string_truncate(qiv->errname, 0); | |
84 | } else { | |
85 | qiv->errname = g_string_new(""); | |
86 | } | |
87 | ||
88 | QSLIST_FOREACH(so , &qiv->stack, node) { | |
a4a1c70d MA |
89 | if (n) { |
90 | n--; | |
91 | } else if (qobject_type(so->obj) == QTYPE_QDICT) { | |
92 | g_string_prepend(qiv->errname, name ?: "<anonymous>"); | |
a9fc37f6 MA |
93 | g_string_prepend_c(qiv->errname, '.'); |
94 | } else { | |
0b2c1bee MA |
95 | snprintf(buf, sizeof(buf), |
96 | qiv->keyval ? ".%u" : "[%u]", | |
97 | so->index); | |
a9fc37f6 MA |
98 | g_string_prepend(qiv->errname, buf); |
99 | } | |
100 | name = so->name; | |
101 | } | |
a4a1c70d | 102 | assert(!n); |
a9fc37f6 MA |
103 | |
104 | if (name) { | |
105 | g_string_prepend(qiv->errname, name); | |
106 | } else if (qiv->errname->str[0] == '.') { | |
107 | g_string_erase(qiv->errname, 0, 1); | |
a4a1c70d | 108 | } else if (!qiv->errname->str[0]) { |
a9fc37f6 MA |
109 | return "<anonymous>"; |
110 | } | |
111 | ||
112 | return qiv->errname->str; | |
113 | } | |
114 | ||
a4a1c70d MA |
115 | static const char *full_name(QObjectInputVisitor *qiv, const char *name) |
116 | { | |
117 | return full_name_nth(qiv, name, 0); | |
118 | } | |
119 | ||
a9fc37f6 MA |
120 | static QObject *qobject_input_try_get_object(QObjectInputVisitor *qiv, |
121 | const char *name, | |
122 | bool consume) | |
c40cc0a0 | 123 | { |
ce140b17 EB |
124 | StackObject *tos; |
125 | QObject *qobj; | |
e5826a2f | 126 | QObject *ret; |
b471d012 | 127 | |
3d344c2a | 128 | if (QSLIST_EMPTY(&qiv->stack)) { |
ce140b17 | 129 | /* Starting at root, name is ignored. */ |
5d0cbbcf | 130 | assert(qiv->root); |
ce140b17 EB |
131 | return qiv->root; |
132 | } | |
133 | ||
134 | /* We are in a container; find the next element. */ | |
3d344c2a | 135 | tos = QSLIST_FIRST(&qiv->stack); |
ce140b17 | 136 | qobj = tos->obj; |
b471d012 EB |
137 | assert(qobj); |
138 | ||
ce140b17 EB |
139 | if (qobject_type(qobj) == QTYPE_QDICT) { |
140 | assert(name); | |
7dc847eb | 141 | ret = qdict_get(qobject_to(QDict, qobj), name); |
e5826a2f EB |
142 | if (tos->h && consume && ret) { |
143 | bool removed = g_hash_table_remove(tos->h, name); | |
144 | assert(removed); | |
47c6d3ec | 145 | } |
ce140b17 | 146 | } else { |
b471d012 | 147 | assert(qobject_type(qobj) == QTYPE_QLIST); |
ce140b17 | 148 | assert(!name); |
1f41a645 MA |
149 | if (tos->entry) { |
150 | ret = qlist_entry_obj(tos->entry); | |
151 | if (consume) { | |
152 | tos->entry = qlist_next(tos->entry); | |
153 | } | |
154 | } else { | |
155 | ret = NULL; | |
156 | } | |
fcf3cb21 | 157 | if (consume) { |
a9fc37f6 | 158 | tos->index++; |
fcf3cb21 | 159 | } |
c40cc0a0 MR |
160 | } |
161 | ||
ce140b17 | 162 | return ret; |
c40cc0a0 MR |
163 | } |
164 | ||
a9fc37f6 MA |
165 | static QObject *qobject_input_get_object(QObjectInputVisitor *qiv, |
166 | const char *name, | |
167 | bool consume, Error **errp) | |
168 | { | |
169 | QObject *obj = qobject_input_try_get_object(qiv, name, consume); | |
170 | ||
171 | if (!obj) { | |
172 | error_setg(errp, QERR_MISSING_PARAMETER, full_name(qiv, name)); | |
173 | } | |
174 | return obj; | |
175 | } | |
176 | ||
e3934b42 MA |
177 | static const char *qobject_input_get_keyval(QObjectInputVisitor *qiv, |
178 | const char *name, | |
179 | Error **errp) | |
180 | { | |
181 | QObject *qobj; | |
182 | QString *qstr; | |
183 | ||
184 | qobj = qobject_input_get_object(qiv, name, true, errp); | |
185 | if (!qobj) { | |
186 | return NULL; | |
187 | } | |
188 | ||
7dc847eb | 189 | qstr = qobject_to(QString, qobj); |
e3934b42 | 190 | if (!qstr) { |
31478f26 MA |
191 | switch (qobject_type(qobj)) { |
192 | case QTYPE_QDICT: | |
193 | case QTYPE_QLIST: | |
194 | error_setg(errp, "Parameters '%s.*' are unexpected", | |
195 | full_name(qiv, name)); | |
196 | return NULL; | |
197 | default: | |
198 | /* Non-string scalar (should this be an assertion?) */ | |
199 | error_setg(errp, "Internal error: parameter %s invalid", | |
200 | full_name(qiv, name)); | |
201 | return NULL; | |
202 | } | |
e3934b42 MA |
203 | } |
204 | ||
205 | return qstring_get_str(qstr); | |
206 | } | |
207 | ||
09e68369 | 208 | static const QListEntry *qobject_input_push(QObjectInputVisitor *qiv, |
a9fc37f6 | 209 | const char *name, |
b8874fbf | 210 | QObject *obj, void *qapi) |
c40cc0a0 | 211 | { |
e38ac962 | 212 | GHashTable *h; |
3d344c2a | 213 | StackObject *tos = g_new0(StackObject, 1); |
7b1cd1c6 MA |
214 | QDict *qdict = qobject_to(QDict, obj); |
215 | QList *qlist = qobject_to(QList, obj); | |
216 | const QDictEntry *entry; | |
c40cc0a0 | 217 | |
b471d012 | 218 | assert(obj); |
a9fc37f6 | 219 | tos->name = name; |
b471d012 | 220 | tos->obj = obj; |
1158bb2a | 221 | tos->qapi = qapi; |
e38ac962 | 222 | |
7b1cd1c6 | 223 | if (qdict) { |
e38ac962 | 224 | h = g_hash_table_new(g_str_hash, g_str_equal); |
7b1cd1c6 MA |
225 | for (entry = qdict_first(qdict); |
226 | entry; | |
227 | entry = qdict_next(qdict, entry)) { | |
228 | g_hash_table_insert(h, (void *)qdict_entry_key(entry), NULL); | |
229 | } | |
b471d012 | 230 | tos->h = h; |
048abb7b | 231 | } else { |
7b1cd1c6 MA |
232 | assert(qlist); |
233 | tos->entry = qlist_first(qlist); | |
a9fc37f6 | 234 | tos->index = -1; |
e38ac962 PB |
235 | } |
236 | ||
3d344c2a | 237 | QSLIST_INSERT_HEAD(&qiv->stack, tos, node); |
d9f62dde | 238 | return tos->entry; |
c40cc0a0 MR |
239 | } |
240 | ||
57a33d89 | 241 | |
012d4c96 | 242 | static bool qobject_input_check_struct(Visitor *v, Error **errp) |
c40cc0a0 | 243 | { |
09e68369 | 244 | QObjectInputVisitor *qiv = to_qiv(v); |
3d344c2a | 245 | StackObject *tos = QSLIST_FIRST(&qiv->stack); |
048abb7b MA |
246 | GHashTableIter iter; |
247 | const char *key; | |
e38ac962 | 248 | |
3d344c2a | 249 | assert(tos && !tos->entry); |
048abb7b MA |
250 | |
251 | g_hash_table_iter_init(&iter, tos->h); | |
252 | if (g_hash_table_iter_next(&iter, (void **)&key, NULL)) { | |
253 | error_setg(errp, "Parameter '%s' is unexpected", | |
254 | full_name(qiv, key)); | |
012d4c96 | 255 | return false; |
15c2f669 | 256 | } |
012d4c96 | 257 | return true; |
15c2f669 EB |
258 | } |
259 | ||
09e68369 | 260 | static void qobject_input_stack_object_free(StackObject *tos) |
15c2f669 | 261 | { |
3d344c2a PB |
262 | if (tos->h) { |
263 | g_hash_table_unref(tos->h); | |
264 | } | |
15c2f669 | 265 | |
3d344c2a PB |
266 | g_free(tos); |
267 | } | |
15c2f669 | 268 | |
09e68369 | 269 | static void qobject_input_pop(Visitor *v, void **obj) |
3d344c2a | 270 | { |
09e68369 | 271 | QObjectInputVisitor *qiv = to_qiv(v); |
3d344c2a | 272 | StackObject *tos = QSLIST_FIRST(&qiv->stack); |
e38ac962 | 273 | |
3d344c2a PB |
274 | assert(tos && tos->qapi == obj); |
275 | QSLIST_REMOVE_HEAD(&qiv->stack, node); | |
09e68369 | 276 | qobject_input_stack_object_free(tos); |
c40cc0a0 MR |
277 | } |
278 | ||
012d4c96 | 279 | static bool qobject_input_start_struct(Visitor *v, const char *name, void **obj, |
09e68369 | 280 | size_t size, Error **errp) |
c40cc0a0 | 281 | { |
09e68369 DB |
282 | QObjectInputVisitor *qiv = to_qiv(v); |
283 | QObject *qobj = qobject_input_get_object(qiv, name, true, errp); | |
c40cc0a0 | 284 | |
e58d695e EB |
285 | if (obj) { |
286 | *obj = NULL; | |
287 | } | |
1382d4ab | 288 | if (!qobj) { |
012d4c96 | 289 | return false; |
1382d4ab MAL |
290 | } |
291 | if (qobject_type(qobj) != QTYPE_QDICT) { | |
a9fc37f6 MA |
292 | error_setg(errp, QERR_INVALID_PARAMETER_TYPE, |
293 | full_name(qiv, name), "object"); | |
012d4c96 | 294 | return false; |
c40cc0a0 MR |
295 | } |
296 | ||
a9fc37f6 | 297 | qobject_input_push(qiv, name, qobj, obj); |
c40cc0a0 MR |
298 | |
299 | if (obj) { | |
7267c094 | 300 | *obj = g_malloc0(size); |
c40cc0a0 | 301 | } |
012d4c96 | 302 | return true; |
c40cc0a0 MR |
303 | } |
304 | ||
8b2e41d7 MA |
305 | static void qobject_input_end_struct(Visitor *v, void **obj) |
306 | { | |
307 | QObjectInputVisitor *qiv = to_qiv(v); | |
308 | StackObject *tos = QSLIST_FIRST(&qiv->stack); | |
309 | ||
310 | assert(qobject_type(tos->obj) == QTYPE_QDICT && tos->h); | |
311 | qobject_input_pop(v, obj); | |
312 | } | |
313 | ||
c40cc0a0 | 314 | |
012d4c96 | 315 | static bool qobject_input_start_list(Visitor *v, const char *name, |
09e68369 DB |
316 | GenericList **list, size_t size, |
317 | Error **errp) | |
c40cc0a0 | 318 | { |
09e68369 DB |
319 | QObjectInputVisitor *qiv = to_qiv(v); |
320 | QObject *qobj = qobject_input_get_object(qiv, name, true, errp); | |
d9f62dde | 321 | const QListEntry *entry; |
c40cc0a0 | 322 | |
58561c27 MA |
323 | if (list) { |
324 | *list = NULL; | |
325 | } | |
1382d4ab | 326 | if (!qobj) { |
012d4c96 | 327 | return false; |
1382d4ab MAL |
328 | } |
329 | if (qobject_type(qobj) != QTYPE_QLIST) { | |
a9fc37f6 MA |
330 | error_setg(errp, QERR_INVALID_PARAMETER_TYPE, |
331 | full_name(qiv, name), "array"); | |
012d4c96 | 332 | return false; |
c40cc0a0 MR |
333 | } |
334 | ||
a9fc37f6 | 335 | entry = qobject_input_push(qiv, name, qobj, list); |
58561c27 MA |
336 | if (entry && list) { |
337 | *list = g_malloc0(size); | |
d9f62dde | 338 | } |
012d4c96 | 339 | return true; |
c40cc0a0 MR |
340 | } |
341 | ||
09e68369 DB |
342 | static GenericList *qobject_input_next_list(Visitor *v, GenericList *tail, |
343 | size_t size) | |
c40cc0a0 | 344 | { |
09e68369 | 345 | QObjectInputVisitor *qiv = to_qiv(v); |
a4a1c70d MA |
346 | StackObject *tos = QSLIST_FIRST(&qiv->stack); |
347 | ||
532fb532 | 348 | assert(tos && qobject_to(QList, tos->obj)); |
c40cc0a0 | 349 | |
a4a1c70d | 350 | if (!tos->entry) { |
c40cc0a0 MR |
351 | return NULL; |
352 | } | |
d9f62dde EB |
353 | tail->next = g_malloc0(size); |
354 | return tail->next; | |
c40cc0a0 MR |
355 | } |
356 | ||
012d4c96 | 357 | static bool qobject_input_check_list(Visitor *v, Error **errp) |
a4a1c70d MA |
358 | { |
359 | QObjectInputVisitor *qiv = to_qiv(v); | |
360 | StackObject *tos = QSLIST_FIRST(&qiv->stack); | |
361 | ||
532fb532 | 362 | assert(tos && qobject_to(QList, tos->obj)); |
a4a1c70d MA |
363 | |
364 | if (tos->entry) { | |
365 | error_setg(errp, "Only %u list elements expected in %s", | |
366 | tos->index + 1, full_name_nth(qiv, NULL, 1)); | |
012d4c96 | 367 | return false; |
a4a1c70d | 368 | } |
012d4c96 | 369 | return true; |
a4a1c70d MA |
370 | } |
371 | ||
8b2e41d7 MA |
372 | static void qobject_input_end_list(Visitor *v, void **obj) |
373 | { | |
374 | QObjectInputVisitor *qiv = to_qiv(v); | |
375 | StackObject *tos = QSLIST_FIRST(&qiv->stack); | |
376 | ||
377 | assert(qobject_type(tos->obj) == QTYPE_QLIST && !tos->h); | |
378 | qobject_input_pop(v, obj); | |
379 | } | |
c40cc0a0 | 380 | |
012d4c96 | 381 | static bool qobject_input_start_alternate(Visitor *v, const char *name, |
09e68369 | 382 | GenericAlternate **obj, size_t size, |
60390d2d | 383 | Error **errp) |
69dd62df | 384 | { |
09e68369 DB |
385 | QObjectInputVisitor *qiv = to_qiv(v); |
386 | QObject *qobj = qobject_input_get_object(qiv, name, false, errp); | |
69dd62df KW |
387 | |
388 | if (!qobj) { | |
dbf11922 | 389 | *obj = NULL; |
012d4c96 | 390 | return false; |
69dd62df | 391 | } |
dbf11922 EB |
392 | *obj = g_malloc0(size); |
393 | (*obj)->type = qobject_type(qobj); | |
012d4c96 | 394 | return true; |
69dd62df KW |
395 | } |
396 | ||
012d4c96 | 397 | static bool qobject_input_type_int64(Visitor *v, const char *name, int64_t *obj, |
09e68369 | 398 | Error **errp) |
c40cc0a0 | 399 | { |
09e68369 DB |
400 | QObjectInputVisitor *qiv = to_qiv(v); |
401 | QObject *qobj = qobject_input_get_object(qiv, name, true, errp); | |
01b2ffce | 402 | QNum *qnum; |
c40cc0a0 | 403 | |
1382d4ab | 404 | if (!qobj) { |
012d4c96 | 405 | return false; |
1382d4ab | 406 | } |
7dc847eb | 407 | qnum = qobject_to(QNum, qobj); |
01b2ffce | 408 | if (!qnum || !qnum_get_try_int(qnum, obj)) { |
a9fc37f6 MA |
409 | error_setg(errp, QERR_INVALID_PARAMETER_TYPE, |
410 | full_name(qiv, name), "integer"); | |
012d4c96 | 411 | return false; |
c40cc0a0 | 412 | } |
012d4c96 | 413 | return true; |
c40cc0a0 MR |
414 | } |
415 | ||
012d4c96 | 416 | static bool qobject_input_type_int64_keyval(Visitor *v, const char *name, |
cbd8acf3 DB |
417 | int64_t *obj, Error **errp) |
418 | { | |
419 | QObjectInputVisitor *qiv = to_qiv(v); | |
e3934b42 | 420 | const char *str = qobject_input_get_keyval(qiv, name, errp); |
cbd8acf3 | 421 | |
e3934b42 | 422 | if (!str) { |
012d4c96 | 423 | return false; |
cbd8acf3 DB |
424 | } |
425 | ||
e3934b42 | 426 | if (qemu_strtoi64(str, NULL, 0, obj) < 0) { |
cbd8acf3 DB |
427 | /* TODO report -ERANGE more nicely */ |
428 | error_setg(errp, QERR_INVALID_PARAMETER_VALUE, | |
429 | full_name(qiv, name), "integer"); | |
012d4c96 | 430 | return false; |
cbd8acf3 | 431 | } |
012d4c96 | 432 | return true; |
cbd8acf3 DB |
433 | } |
434 | ||
012d4c96 | 435 | static bool qobject_input_type_uint64(Visitor *v, const char *name, |
09e68369 | 436 | uint64_t *obj, Error **errp) |
f755dea7 | 437 | { |
09e68369 DB |
438 | QObjectInputVisitor *qiv = to_qiv(v); |
439 | QObject *qobj = qobject_input_get_object(qiv, name, true, errp); | |
01b2ffce MAL |
440 | QNum *qnum; |
441 | int64_t val; | |
f755dea7 | 442 | |
1382d4ab | 443 | if (!qobj) { |
012d4c96 | 444 | return false; |
1382d4ab | 445 | } |
7dc847eb | 446 | qnum = qobject_to(QNum, qobj); |
5923f85f MAL |
447 | if (!qnum) { |
448 | goto err; | |
449 | } | |
450 | ||
451 | if (qnum_get_try_uint(qnum, obj)) { | |
012d4c96 | 452 | return true; |
f755dea7 | 453 | } |
5923f85f MAL |
454 | |
455 | /* Need to accept negative values for backward compatibility */ | |
456 | if (qnum_get_try_int(qnum, &val)) { | |
457 | *obj = val; | |
012d4c96 | 458 | return true; |
5923f85f MAL |
459 | } |
460 | ||
461 | err: | |
462 | error_setg(errp, QERR_INVALID_PARAMETER_VALUE, | |
463 | full_name(qiv, name), "uint64"); | |
012d4c96 | 464 | return false; |
f755dea7 EB |
465 | } |
466 | ||
012d4c96 | 467 | static bool qobject_input_type_uint64_keyval(Visitor *v, const char *name, |
cbd8acf3 DB |
468 | uint64_t *obj, Error **errp) |
469 | { | |
470 | QObjectInputVisitor *qiv = to_qiv(v); | |
e3934b42 | 471 | const char *str = qobject_input_get_keyval(qiv, name, errp); |
cbd8acf3 | 472 | |
e3934b42 | 473 | if (!str) { |
012d4c96 | 474 | return false; |
cbd8acf3 DB |
475 | } |
476 | ||
e3934b42 | 477 | if (qemu_strtou64(str, NULL, 0, obj) < 0) { |
cbd8acf3 DB |
478 | /* TODO report -ERANGE more nicely */ |
479 | error_setg(errp, QERR_INVALID_PARAMETER_VALUE, | |
480 | full_name(qiv, name), "integer"); | |
012d4c96 | 481 | return false; |
cbd8acf3 | 482 | } |
012d4c96 | 483 | return true; |
cbd8acf3 DB |
484 | } |
485 | ||
012d4c96 | 486 | static bool qobject_input_type_bool(Visitor *v, const char *name, bool *obj, |
09e68369 | 487 | Error **errp) |
c40cc0a0 | 488 | { |
09e68369 DB |
489 | QObjectInputVisitor *qiv = to_qiv(v); |
490 | QObject *qobj = qobject_input_get_object(qiv, name, true, errp); | |
1382d4ab | 491 | QBool *qbool; |
c40cc0a0 | 492 | |
1382d4ab | 493 | if (!qobj) { |
012d4c96 | 494 | return false; |
1382d4ab | 495 | } |
7dc847eb | 496 | qbool = qobject_to(QBool, qobj); |
14b61600 | 497 | if (!qbool) { |
a9fc37f6 MA |
498 | error_setg(errp, QERR_INVALID_PARAMETER_TYPE, |
499 | full_name(qiv, name), "boolean"); | |
012d4c96 | 500 | return false; |
c40cc0a0 MR |
501 | } |
502 | ||
14b61600 | 503 | *obj = qbool_get_bool(qbool); |
012d4c96 | 504 | return true; |
c40cc0a0 MR |
505 | } |
506 | ||
012d4c96 | 507 | static bool qobject_input_type_bool_keyval(Visitor *v, const char *name, |
cbd8acf3 DB |
508 | bool *obj, Error **errp) |
509 | { | |
510 | QObjectInputVisitor *qiv = to_qiv(v); | |
e3934b42 | 511 | const char *str = qobject_input_get_keyval(qiv, name, errp); |
cbd8acf3 | 512 | |
e3934b42 | 513 | if (!str) { |
012d4c96 | 514 | return false; |
cbd8acf3 DB |
515 | } |
516 | ||
372bcb25 | 517 | if (!qapi_bool_parse(name, str, obj, NULL)) { |
cbd8acf3 DB |
518 | error_setg(errp, QERR_INVALID_PARAMETER_VALUE, |
519 | full_name(qiv, name), "'on' or 'off'"); | |
012d4c96 | 520 | return false; |
cbd8acf3 | 521 | } |
012d4c96 | 522 | return true; |
cbd8acf3 DB |
523 | } |
524 | ||
012d4c96 | 525 | static bool qobject_input_type_str(Visitor *v, const char *name, char **obj, |
09e68369 | 526 | Error **errp) |
c40cc0a0 | 527 | { |
09e68369 DB |
528 | QObjectInputVisitor *qiv = to_qiv(v); |
529 | QObject *qobj = qobject_input_get_object(qiv, name, true, errp); | |
1382d4ab | 530 | QString *qstr; |
c40cc0a0 | 531 | |
1382d4ab MAL |
532 | *obj = NULL; |
533 | if (!qobj) { | |
012d4c96 | 534 | return false; |
1382d4ab | 535 | } |
7dc847eb | 536 | qstr = qobject_to(QString, qobj); |
7f027843 | 537 | if (!qstr) { |
a9fc37f6 MA |
538 | error_setg(errp, QERR_INVALID_PARAMETER_TYPE, |
539 | full_name(qiv, name), "string"); | |
012d4c96 | 540 | return false; |
c40cc0a0 MR |
541 | } |
542 | ||
7f027843 | 543 | *obj = g_strdup(qstring_get_str(qstr)); |
012d4c96 | 544 | return true; |
c40cc0a0 MR |
545 | } |
546 | ||
012d4c96 | 547 | static bool qobject_input_type_str_keyval(Visitor *v, const char *name, |
31478f26 MA |
548 | char **obj, Error **errp) |
549 | { | |
550 | QObjectInputVisitor *qiv = to_qiv(v); | |
551 | const char *str = qobject_input_get_keyval(qiv, name, errp); | |
552 | ||
553 | *obj = g_strdup(str); | |
012d4c96 | 554 | return !!str; |
31478f26 MA |
555 | } |
556 | ||
012d4c96 | 557 | static bool qobject_input_type_number(Visitor *v, const char *name, double *obj, |
09e68369 | 558 | Error **errp) |
c40cc0a0 | 559 | { |
09e68369 DB |
560 | QObjectInputVisitor *qiv = to_qiv(v); |
561 | QObject *qobj = qobject_input_get_object(qiv, name, true, errp); | |
01b2ffce | 562 | QNum *qnum; |
c40cc0a0 | 563 | |
1382d4ab | 564 | if (!qobj) { |
012d4c96 | 565 | return false; |
1382d4ab | 566 | } |
7dc847eb | 567 | qnum = qobject_to(QNum, qobj); |
01b2ffce | 568 | if (!qnum) { |
58634047 MAL |
569 | error_setg(errp, QERR_INVALID_PARAMETER_TYPE, |
570 | full_name(qiv, name), "number"); | |
012d4c96 | 571 | return false; |
1ee51876 | 572 | } |
fcf73f66 | 573 | |
01b2ffce | 574 | *obj = qnum_get_double(qnum); |
012d4c96 | 575 | return true; |
c40cc0a0 MR |
576 | } |
577 | ||
012d4c96 | 578 | static bool qobject_input_type_number_keyval(Visitor *v, const char *name, |
cbd8acf3 DB |
579 | double *obj, Error **errp) |
580 | { | |
581 | QObjectInputVisitor *qiv = to_qiv(v); | |
e3934b42 | 582 | const char *str = qobject_input_get_keyval(qiv, name, errp); |
e08a5241 | 583 | double val; |
cbd8acf3 | 584 | |
e3934b42 | 585 | if (!str) { |
012d4c96 | 586 | return false; |
cbd8acf3 DB |
587 | } |
588 | ||
e08a5241 | 589 | if (qemu_strtod_finite(str, NULL, &val)) { |
cbd8acf3 DB |
590 | /* TODO report -ERANGE more nicely */ |
591 | error_setg(errp, QERR_INVALID_PARAMETER_TYPE, | |
592 | full_name(qiv, name), "number"); | |
012d4c96 | 593 | return false; |
cbd8acf3 | 594 | } |
e08a5241 DH |
595 | |
596 | *obj = val; | |
012d4c96 | 597 | return true; |
cbd8acf3 DB |
598 | } |
599 | ||
012d4c96 | 600 | static bool qobject_input_type_any(Visitor *v, const char *name, QObject **obj, |
09e68369 | 601 | Error **errp) |
28770e05 | 602 | { |
09e68369 DB |
603 | QObjectInputVisitor *qiv = to_qiv(v); |
604 | QObject *qobj = qobject_input_get_object(qiv, name, true, errp); | |
28770e05 | 605 | |
1382d4ab | 606 | *obj = NULL; |
c4897802 | 607 | if (!qobj) { |
012d4c96 | 608 | return false; |
c4897802 MAL |
609 | } |
610 | ||
f5a74a5a | 611 | *obj = qobject_ref(qobj); |
012d4c96 | 612 | return true; |
28770e05 MA |
613 | } |
614 | ||
012d4c96 | 615 | static bool qobject_input_type_null(Visitor *v, const char *name, |
d2f95f4d | 616 | QNull **obj, Error **errp) |
3bc97fd5 | 617 | { |
09e68369 DB |
618 | QObjectInputVisitor *qiv = to_qiv(v); |
619 | QObject *qobj = qobject_input_get_object(qiv, name, true, errp); | |
3df016f1 | 620 | |
d2f95f4d | 621 | *obj = NULL; |
c4897802 | 622 | if (!qobj) { |
012d4c96 | 623 | return false; |
c4897802 MAL |
624 | } |
625 | ||
3df016f1 | 626 | if (qobject_type(qobj) != QTYPE_QNULL) { |
a9fc37f6 MA |
627 | error_setg(errp, QERR_INVALID_PARAMETER_TYPE, |
628 | full_name(qiv, name), "null"); | |
012d4c96 | 629 | return false; |
3df016f1 | 630 | } |
d2f95f4d | 631 | *obj = qnull(); |
012d4c96 | 632 | return true; |
3bc97fd5 EB |
633 | } |
634 | ||
012d4c96 | 635 | static bool qobject_input_type_size_keyval(Visitor *v, const char *name, |
cbd8acf3 DB |
636 | uint64_t *obj, Error **errp) |
637 | { | |
638 | QObjectInputVisitor *qiv = to_qiv(v); | |
e3934b42 | 639 | const char *str = qobject_input_get_keyval(qiv, name, errp); |
cbd8acf3 | 640 | |
e3934b42 | 641 | if (!str) { |
012d4c96 | 642 | return false; |
cbd8acf3 DB |
643 | } |
644 | ||
e3934b42 | 645 | if (qemu_strtosz(str, NULL, obj) < 0) { |
cbd8acf3 DB |
646 | /* TODO report -ERANGE more nicely */ |
647 | error_setg(errp, QERR_INVALID_PARAMETER_VALUE, | |
648 | full_name(qiv, name), "size"); | |
012d4c96 | 649 | return false; |
cbd8acf3 | 650 | } |
012d4c96 | 651 | return true; |
cbd8acf3 DB |
652 | } |
653 | ||
09e68369 | 654 | static void qobject_input_optional(Visitor *v, const char *name, bool *present) |
c40cc0a0 | 655 | { |
09e68369 | 656 | QObjectInputVisitor *qiv = to_qiv(v); |
a9fc37f6 | 657 | QObject *qobj = qobject_input_try_get_object(qiv, name, false); |
c40cc0a0 MR |
658 | |
659 | if (!qobj) { | |
660 | *present = false; | |
661 | return; | |
662 | } | |
663 | ||
664 | *present = true; | |
665 | } | |
666 | ||
db291641 MA |
667 | static bool qobject_input_deprecated_accept(Visitor *v, const char *name, |
668 | Error **errp) | |
669 | { | |
670 | QObjectInputVisitor *qiv = to_qiv(v); | |
671 | ||
672 | switch (qiv->deprecated_policy) { | |
673 | case COMPAT_POLICY_INPUT_ACCEPT: | |
674 | return true; | |
675 | case COMPAT_POLICY_INPUT_REJECT: | |
676 | error_setg(errp, "Deprecated parameter '%s' disabled by policy", | |
677 | name); | |
678 | return false; | |
679 | default: | |
680 | abort(); | |
681 | } | |
682 | } | |
683 | ||
09e68369 | 684 | static void qobject_input_free(Visitor *v) |
2c0ef9f4 | 685 | { |
09e68369 | 686 | QObjectInputVisitor *qiv = to_qiv(v); |
a9fc37f6 | 687 | |
3d344c2a PB |
688 | while (!QSLIST_EMPTY(&qiv->stack)) { |
689 | StackObject *tos = QSLIST_FIRST(&qiv->stack); | |
690 | ||
691 | QSLIST_REMOVE_HEAD(&qiv->stack, node); | |
09e68369 | 692 | qobject_input_stack_object_free(tos); |
3d344c2a | 693 | } |
2c0ef9f4 | 694 | |
cb3e7f08 | 695 | qobject_unref(qiv->root); |
a9fc37f6 MA |
696 | if (qiv->errname) { |
697 | g_string_free(qiv->errname, TRUE); | |
698 | } | |
b70ce101 | 699 | g_free(qiv); |
2c0ef9f4 EB |
700 | } |
701 | ||
abe81bc2 | 702 | static QObjectInputVisitor *qobject_input_visitor_base_new(QObject *obj) |
c40cc0a0 | 703 | { |
abe81bc2 | 704 | QObjectInputVisitor *v = g_malloc0(sizeof(*v)); |
c40cc0a0 | 705 | |
5d0cbbcf | 706 | assert(obj); |
c40cc0a0 | 707 | |
983f52d4 | 708 | v->visitor.type = VISITOR_INPUT; |
09e68369 DB |
709 | v->visitor.start_struct = qobject_input_start_struct; |
710 | v->visitor.check_struct = qobject_input_check_struct; | |
8b2e41d7 | 711 | v->visitor.end_struct = qobject_input_end_struct; |
09e68369 DB |
712 | v->visitor.start_list = qobject_input_start_list; |
713 | v->visitor.next_list = qobject_input_next_list; | |
a4a1c70d | 714 | v->visitor.check_list = qobject_input_check_list; |
8b2e41d7 | 715 | v->visitor.end_list = qobject_input_end_list; |
09e68369 | 716 | v->visitor.start_alternate = qobject_input_start_alternate; |
abe81bc2 | 717 | v->visitor.optional = qobject_input_optional; |
db291641 | 718 | v->visitor.deprecated_accept = qobject_input_deprecated_accept; |
abe81bc2 MA |
719 | v->visitor.free = qobject_input_free; |
720 | ||
f5a74a5a | 721 | v->root = qobject_ref(obj); |
abe81bc2 MA |
722 | |
723 | return v; | |
724 | } | |
725 | ||
726 | Visitor *qobject_input_visitor_new(QObject *obj) | |
727 | { | |
728 | QObjectInputVisitor *v = qobject_input_visitor_base_new(obj); | |
729 | ||
09e68369 DB |
730 | v->visitor.type_int64 = qobject_input_type_int64; |
731 | v->visitor.type_uint64 = qobject_input_type_uint64; | |
732 | v->visitor.type_bool = qobject_input_type_bool; | |
733 | v->visitor.type_str = qobject_input_type_str; | |
734 | v->visitor.type_number = qobject_input_type_number; | |
735 | v->visitor.type_any = qobject_input_type_any; | |
736 | v->visitor.type_null = qobject_input_type_null; | |
c40cc0a0 | 737 | |
b70ce101 | 738 | return &v->visitor; |
c40cc0a0 | 739 | } |
cbd8acf3 | 740 | |
db291641 MA |
741 | void qobject_input_visitor_set_policy(Visitor *v, |
742 | CompatPolicyInput deprecated) | |
743 | { | |
744 | QObjectInputVisitor *qiv = to_qiv(v); | |
745 | ||
746 | qiv->deprecated_policy = deprecated; | |
747 | } | |
748 | ||
cbd8acf3 DB |
749 | Visitor *qobject_input_visitor_new_keyval(QObject *obj) |
750 | { | |
abe81bc2 | 751 | QObjectInputVisitor *v = qobject_input_visitor_base_new(obj); |
cbd8acf3 | 752 | |
cbd8acf3 DB |
753 | v->visitor.type_int64 = qobject_input_type_int64_keyval; |
754 | v->visitor.type_uint64 = qobject_input_type_uint64_keyval; | |
755 | v->visitor.type_bool = qobject_input_type_bool_keyval; | |
31478f26 | 756 | v->visitor.type_str = qobject_input_type_str_keyval; |
cbd8acf3 DB |
757 | v->visitor.type_number = qobject_input_type_number_keyval; |
758 | v->visitor.type_any = qobject_input_type_any; | |
759 | v->visitor.type_null = qobject_input_type_null; | |
760 | v->visitor.type_size = qobject_input_type_size_keyval; | |
0b2c1bee | 761 | v->keyval = true; |
cbd8acf3 DB |
762 | |
763 | return &v->visitor; | |
764 | } | |
9d1eab4b MA |
765 | |
766 | Visitor *qobject_input_visitor_new_str(const char *str, | |
767 | const char *implied_key, | |
768 | Error **errp) | |
769 | { | |
770 | bool is_json = str[0] == '{'; | |
771 | QObject *obj; | |
772 | QDict *args; | |
773 | Visitor *v; | |
774 | ||
775 | if (is_json) { | |
776 | obj = qobject_from_json(str, errp); | |
777 | if (!obj) { | |
9d1eab4b MA |
778 | return NULL; |
779 | } | |
7dc847eb | 780 | args = qobject_to(QDict, obj); |
9d1eab4b MA |
781 | assert(args); |
782 | v = qobject_input_visitor_new(QOBJECT(args)); | |
783 | } else { | |
8bf12c4f | 784 | args = keyval_parse(str, implied_key, NULL, errp); |
9d1eab4b MA |
785 | if (!args) { |
786 | return NULL; | |
787 | } | |
788 | v = qobject_input_visitor_new_keyval(QOBJECT(args)); | |
789 | } | |
cb3e7f08 | 790 | qobject_unref(args); |
9d1eab4b MA |
791 | |
792 | return v; | |
793 | } |