]>
Commit | Line | Data |
---|---|---|
c40cc0a0 MR |
1 | /* |
2 | * Input Visitor | |
3 | * | |
4 | * Copyright IBM, Corp. 2011 | |
5 | * | |
6 | * Authors: | |
7 | * Anthony Liguori <[email protected]> | |
8 | * | |
9 | * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. | |
10 | * See the COPYING.LIB file in the top-level directory. | |
11 | * | |
12 | */ | |
13 | ||
7b1b5d19 PB |
14 | #include "qapi/qmp-input-visitor.h" |
15 | #include "qapi/visitor-impl.h" | |
1de7afc9 | 16 | #include "qemu/queue.h" |
c40cc0a0 | 17 | #include "qemu-common.h" |
7b1b5d19 PB |
18 | #include "qapi/qmp/types.h" |
19 | #include "qapi/qmp/qerror.h" | |
c40cc0a0 MR |
20 | |
21 | #define QIV_STACK_SIZE 1024 | |
22 | ||
23 | typedef struct StackObject | |
24 | { | |
4faaec6a PB |
25 | QObject *obj; |
26 | const QListEntry *entry; | |
e38ac962 | 27 | GHashTable *h; |
c40cc0a0 MR |
28 | } StackObject; |
29 | ||
30 | struct QmpInputVisitor | |
31 | { | |
32 | Visitor visitor; | |
c40cc0a0 MR |
33 | StackObject stack[QIV_STACK_SIZE]; |
34 | int nb_stack; | |
e38ac962 | 35 | bool strict; |
c40cc0a0 MR |
36 | }; |
37 | ||
38 | static QmpInputVisitor *to_qiv(Visitor *v) | |
39 | { | |
40 | return container_of(v, QmpInputVisitor, visitor); | |
41 | } | |
42 | ||
4faaec6a | 43 | static QObject *qmp_input_get_object(QmpInputVisitor *qiv, |
e8316d7e KW |
44 | const char *name, |
45 | bool consume) | |
c40cc0a0 | 46 | { |
4faaec6a | 47 | QObject *qobj = qiv->stack[qiv->nb_stack - 1].obj; |
c40cc0a0 | 48 | |
47c6d3ec PB |
49 | if (qobj) { |
50 | if (name && qobject_type(qobj) == QTYPE_QDICT) { | |
e8316d7e | 51 | if (qiv->stack[qiv->nb_stack - 1].h && consume) { |
e38ac962 PB |
52 | g_hash_table_remove(qiv->stack[qiv->nb_stack - 1].h, name); |
53 | } | |
47c6d3ec | 54 | return qdict_get(qobject_to_qdict(qobj), name); |
4faaec6a | 55 | } else if (qiv->stack[qiv->nb_stack - 1].entry) { |
47c6d3ec PB |
56 | return qlist_entry_obj(qiv->stack[qiv->nb_stack - 1].entry); |
57 | } | |
c40cc0a0 MR |
58 | } |
59 | ||
60 | return qobj; | |
61 | } | |
62 | ||
e38ac962 PB |
63 | static void qdict_add_key(const char *key, QObject *obj, void *opaque) |
64 | { | |
65 | GHashTable *h = opaque; | |
66 | g_hash_table_insert(h, (gpointer) key, NULL); | |
67 | } | |
68 | ||
4faaec6a | 69 | static void qmp_input_push(QmpInputVisitor *qiv, QObject *obj, Error **errp) |
c40cc0a0 | 70 | { |
e38ac962 | 71 | GHashTable *h; |
c40cc0a0 MR |
72 | |
73 | if (qiv->nb_stack >= QIV_STACK_SIZE) { | |
f231b88d | 74 | error_setg(errp, "An internal buffer overran"); |
c40cc0a0 MR |
75 | return; |
76 | } | |
e38ac962 PB |
77 | |
78 | qiv->stack[qiv->nb_stack].obj = obj; | |
79 | qiv->stack[qiv->nb_stack].entry = NULL; | |
80 | qiv->stack[qiv->nb_stack].h = NULL; | |
81 | ||
82 | if (qiv->strict && qobject_type(obj) == QTYPE_QDICT) { | |
83 | h = g_hash_table_new(g_str_hash, g_str_equal); | |
84 | qdict_iter(qobject_to_qdict(obj), qdict_add_key, h); | |
85 | qiv->stack[qiv->nb_stack].h = h; | |
86 | } | |
87 | ||
88 | qiv->nb_stack++; | |
c40cc0a0 MR |
89 | } |
90 | ||
57a33d89 NK |
91 | /** Only for qmp_input_pop. */ |
92 | static gboolean always_true(gpointer key, gpointer val, gpointer user_pkey) | |
93 | { | |
94 | *(const char **)user_pkey = (const char *)key; | |
95 | return TRUE; | |
96 | } | |
97 | ||
c40cc0a0 MR |
98 | static void qmp_input_pop(QmpInputVisitor *qiv, Error **errp) |
99 | { | |
57a33d89 | 100 | assert(qiv->nb_stack > 0); |
e38ac962 | 101 | |
57a33d89 NK |
102 | if (qiv->strict) { |
103 | GHashTable * const top_ht = qiv->stack[qiv->nb_stack - 1].h; | |
104 | if (top_ht) { | |
105 | if (g_hash_table_size(top_ht)) { | |
106 | const char *key; | |
107 | g_hash_table_find(top_ht, always_true, &key); | |
c6bd8c70 | 108 | error_setg(errp, QERR_QMP_EXTRA_MEMBER, key); |
57a33d89 NK |
109 | } |
110 | g_hash_table_unref(top_ht); | |
e38ac962 | 111 | } |
e38ac962 PB |
112 | } |
113 | ||
c40cc0a0 | 114 | qiv->nb_stack--; |
c40cc0a0 MR |
115 | } |
116 | ||
117 | static void qmp_input_start_struct(Visitor *v, void **obj, const char *kind, | |
118 | const char *name, size_t size, Error **errp) | |
119 | { | |
120 | QmpInputVisitor *qiv = to_qiv(v); | |
e8316d7e | 121 | QObject *qobj = qmp_input_get_object(qiv, name, true); |
8b714d37 | 122 | Error *err = NULL; |
c40cc0a0 MR |
123 | |
124 | if (!qobj || qobject_type(qobj) != QTYPE_QDICT) { | |
c6bd8c70 MA |
125 | error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", |
126 | "QDict"); | |
c40cc0a0 MR |
127 | return; |
128 | } | |
129 | ||
8b714d37 PB |
130 | qmp_input_push(qiv, qobj, &err); |
131 | if (err) { | |
132 | error_propagate(errp, err); | |
c40cc0a0 MR |
133 | return; |
134 | } | |
135 | ||
136 | if (obj) { | |
7267c094 | 137 | *obj = g_malloc0(size); |
c40cc0a0 MR |
138 | } |
139 | } | |
140 | ||
141 | static void qmp_input_end_struct(Visitor *v, Error **errp) | |
142 | { | |
143 | QmpInputVisitor *qiv = to_qiv(v); | |
144 | ||
145 | qmp_input_pop(qiv, errp); | |
146 | } | |
147 | ||
761d524d KW |
148 | static void qmp_input_start_implicit_struct(Visitor *v, void **obj, |
149 | size_t size, Error **errp) | |
150 | { | |
151 | if (obj) { | |
152 | *obj = g_malloc0(size); | |
153 | } | |
154 | } | |
155 | ||
156 | static void qmp_input_end_implicit_struct(Visitor *v, Error **errp) | |
157 | { | |
158 | } | |
159 | ||
c40cc0a0 MR |
160 | static void qmp_input_start_list(Visitor *v, const char *name, Error **errp) |
161 | { | |
162 | QmpInputVisitor *qiv = to_qiv(v); | |
e8316d7e | 163 | QObject *qobj = qmp_input_get_object(qiv, name, true); |
c40cc0a0 MR |
164 | |
165 | if (!qobj || qobject_type(qobj) != QTYPE_QLIST) { | |
c6bd8c70 MA |
166 | error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", |
167 | "list"); | |
c40cc0a0 MR |
168 | return; |
169 | } | |
170 | ||
171 | qmp_input_push(qiv, qobj, errp); | |
172 | } | |
173 | ||
174 | static GenericList *qmp_input_next_list(Visitor *v, GenericList **list, | |
175 | Error **errp) | |
176 | { | |
177 | QmpInputVisitor *qiv = to_qiv(v); | |
178 | GenericList *entry; | |
179 | StackObject *so = &qiv->stack[qiv->nb_stack - 1]; | |
3a86a0fa PB |
180 | bool first; |
181 | ||
182 | if (so->entry == NULL) { | |
183 | so->entry = qlist_first(qobject_to_qlist(so->obj)); | |
184 | first = true; | |
185 | } else { | |
186 | so->entry = qlist_next(so->entry); | |
187 | first = false; | |
188 | } | |
c40cc0a0 MR |
189 | |
190 | if (so->entry == NULL) { | |
191 | return NULL; | |
192 | } | |
193 | ||
7267c094 | 194 | entry = g_malloc0(sizeof(*entry)); |
3a86a0fa PB |
195 | if (first) { |
196 | *list = entry; | |
197 | } else { | |
c40cc0a0 MR |
198 | (*list)->next = entry; |
199 | } | |
c40cc0a0 MR |
200 | |
201 | return entry; | |
202 | } | |
203 | ||
204 | static void qmp_input_end_list(Visitor *v, Error **errp) | |
205 | { | |
206 | QmpInputVisitor *qiv = to_qiv(v); | |
207 | ||
208 | qmp_input_pop(qiv, errp); | |
209 | } | |
210 | ||
69dd62df KW |
211 | static void qmp_input_get_next_type(Visitor *v, int *kind, const int *qobjects, |
212 | const char *name, Error **errp) | |
213 | { | |
214 | QmpInputVisitor *qiv = to_qiv(v); | |
215 | QObject *qobj = qmp_input_get_object(qiv, name, false); | |
216 | ||
217 | if (!qobj) { | |
c6bd8c70 | 218 | error_setg(errp, QERR_MISSING_PARAMETER, name ? name : "null"); |
69dd62df KW |
219 | return; |
220 | } | |
221 | *kind = qobjects[qobject_type(qobj)]; | |
222 | } | |
223 | ||
c40cc0a0 MR |
224 | static void qmp_input_type_int(Visitor *v, int64_t *obj, const char *name, |
225 | Error **errp) | |
226 | { | |
227 | QmpInputVisitor *qiv = to_qiv(v); | |
e8316d7e | 228 | QObject *qobj = qmp_input_get_object(qiv, name, true); |
c40cc0a0 MR |
229 | |
230 | if (!qobj || qobject_type(qobj) != QTYPE_QINT) { | |
c6bd8c70 MA |
231 | error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", |
232 | "integer"); | |
c40cc0a0 MR |
233 | return; |
234 | } | |
235 | ||
236 | *obj = qint_get_int(qobject_to_qint(qobj)); | |
237 | } | |
238 | ||
239 | static void qmp_input_type_bool(Visitor *v, bool *obj, const char *name, | |
240 | Error **errp) | |
241 | { | |
242 | QmpInputVisitor *qiv = to_qiv(v); | |
e8316d7e | 243 | QObject *qobj = qmp_input_get_object(qiv, name, true); |
c40cc0a0 MR |
244 | |
245 | if (!qobj || qobject_type(qobj) != QTYPE_QBOOL) { | |
c6bd8c70 MA |
246 | error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", |
247 | "boolean"); | |
c40cc0a0 MR |
248 | return; |
249 | } | |
250 | ||
fc48ffc3 | 251 | *obj = qbool_get_bool(qobject_to_qbool(qobj)); |
c40cc0a0 MR |
252 | } |
253 | ||
254 | static void qmp_input_type_str(Visitor *v, char **obj, const char *name, | |
255 | Error **errp) | |
256 | { | |
257 | QmpInputVisitor *qiv = to_qiv(v); | |
e8316d7e | 258 | QObject *qobj = qmp_input_get_object(qiv, name, true); |
c40cc0a0 MR |
259 | |
260 | if (!qobj || qobject_type(qobj) != QTYPE_QSTRING) { | |
c6bd8c70 MA |
261 | error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", |
262 | "string"); | |
c40cc0a0 MR |
263 | return; |
264 | } | |
265 | ||
7267c094 | 266 | *obj = g_strdup(qstring_get_str(qobject_to_qstring(qobj))); |
c40cc0a0 MR |
267 | } |
268 | ||
269 | static void qmp_input_type_number(Visitor *v, double *obj, const char *name, | |
270 | Error **errp) | |
271 | { | |
272 | QmpInputVisitor *qiv = to_qiv(v); | |
e8316d7e | 273 | QObject *qobj = qmp_input_get_object(qiv, name, true); |
c40cc0a0 | 274 | |
1ee51876 MR |
275 | if (!qobj || (qobject_type(qobj) != QTYPE_QFLOAT && |
276 | qobject_type(qobj) != QTYPE_QINT)) { | |
c6bd8c70 MA |
277 | error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", |
278 | "number"); | |
c40cc0a0 MR |
279 | return; |
280 | } | |
281 | ||
1ee51876 MR |
282 | if (qobject_type(qobj) == QTYPE_QINT) { |
283 | *obj = qint_get_int(qobject_to_qint(qobj)); | |
284 | } else { | |
285 | *obj = qfloat_get_double(qobject_to_qfloat(qobj)); | |
286 | } | |
c40cc0a0 MR |
287 | } |
288 | ||
e2cd0f4f MA |
289 | static void qmp_input_optional(Visitor *v, bool *present, const char *name, |
290 | Error **errp) | |
c40cc0a0 MR |
291 | { |
292 | QmpInputVisitor *qiv = to_qiv(v); | |
e8316d7e | 293 | QObject *qobj = qmp_input_get_object(qiv, name, true); |
c40cc0a0 MR |
294 | |
295 | if (!qobj) { | |
296 | *present = false; | |
297 | return; | |
298 | } | |
299 | ||
300 | *present = true; | |
301 | } | |
302 | ||
c40cc0a0 MR |
303 | Visitor *qmp_input_get_visitor(QmpInputVisitor *v) |
304 | { | |
305 | return &v->visitor; | |
306 | } | |
307 | ||
308 | void qmp_input_visitor_cleanup(QmpInputVisitor *v) | |
309 | { | |
4faaec6a | 310 | qobject_decref(v->stack[0].obj); |
7267c094 | 311 | g_free(v); |
c40cc0a0 MR |
312 | } |
313 | ||
314 | QmpInputVisitor *qmp_input_visitor_new(QObject *obj) | |
315 | { | |
316 | QmpInputVisitor *v; | |
317 | ||
7267c094 | 318 | v = g_malloc0(sizeof(*v)); |
c40cc0a0 MR |
319 | |
320 | v->visitor.start_struct = qmp_input_start_struct; | |
321 | v->visitor.end_struct = qmp_input_end_struct; | |
761d524d KW |
322 | v->visitor.start_implicit_struct = qmp_input_start_implicit_struct; |
323 | v->visitor.end_implicit_struct = qmp_input_end_implicit_struct; | |
c40cc0a0 MR |
324 | v->visitor.start_list = qmp_input_start_list; |
325 | v->visitor.next_list = qmp_input_next_list; | |
326 | v->visitor.end_list = qmp_input_end_list; | |
0f71a1e0 | 327 | v->visitor.type_enum = input_type_enum; |
c40cc0a0 MR |
328 | v->visitor.type_int = qmp_input_type_int; |
329 | v->visitor.type_bool = qmp_input_type_bool; | |
330 | v->visitor.type_str = qmp_input_type_str; | |
331 | v->visitor.type_number = qmp_input_type_number; | |
e2cd0f4f | 332 | v->visitor.optional = qmp_input_optional; |
69dd62df | 333 | v->visitor.get_next_type = qmp_input_get_next_type; |
c40cc0a0 | 334 | |
4faaec6a PB |
335 | qmp_input_push(v, obj, NULL); |
336 | qobject_incref(obj); | |
c40cc0a0 MR |
337 | |
338 | return v; | |
339 | } | |
e38ac962 PB |
340 | |
341 | QmpInputVisitor *qmp_input_visitor_new_strict(QObject *obj) | |
342 | { | |
343 | QmpInputVisitor *v; | |
344 | ||
345 | v = qmp_input_visitor_new(obj); | |
346 | v->strict = true; | |
347 | ||
348 | return v; | |
349 | } |