]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Input Visitor | |
3 | * | |
4 | * Copyright IBM, Corp. 2011 | |
5 | * | |
6 | * Authors: | |
7 | * Anthony Liguori <aliguori@us.ibm.com> | |
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 | ||
14 | #include "qapi/qmp-input-visitor.h" | |
15 | #include "qapi/visitor-impl.h" | |
16 | #include "qemu/queue.h" | |
17 | #include "qemu-common.h" | |
18 | #include "qapi/qmp/types.h" | |
19 | #include "qapi/qmp/qerror.h" | |
20 | ||
21 | #define QIV_STACK_SIZE 1024 | |
22 | ||
23 | typedef struct StackObject | |
24 | { | |
25 | QObject *obj; | |
26 | const QListEntry *entry; | |
27 | GHashTable *h; | |
28 | } StackObject; | |
29 | ||
30 | struct QmpInputVisitor | |
31 | { | |
32 | Visitor visitor; | |
33 | StackObject stack[QIV_STACK_SIZE]; | |
34 | int nb_stack; | |
35 | bool strict; | |
36 | }; | |
37 | ||
38 | static QmpInputVisitor *to_qiv(Visitor *v) | |
39 | { | |
40 | return container_of(v, QmpInputVisitor, visitor); | |
41 | } | |
42 | ||
43 | static QObject *qmp_input_get_object(QmpInputVisitor *qiv, | |
44 | const char *name, | |
45 | bool consume) | |
46 | { | |
47 | QObject *qobj = qiv->stack[qiv->nb_stack - 1].obj; | |
48 | ||
49 | if (qobj) { | |
50 | if (name && qobject_type(qobj) == QTYPE_QDICT) { | |
51 | if (qiv->stack[qiv->nb_stack - 1].h && consume) { | |
52 | g_hash_table_remove(qiv->stack[qiv->nb_stack - 1].h, name); | |
53 | } | |
54 | return qdict_get(qobject_to_qdict(qobj), name); | |
55 | } else if (qiv->stack[qiv->nb_stack - 1].entry) { | |
56 | return qlist_entry_obj(qiv->stack[qiv->nb_stack - 1].entry); | |
57 | } | |
58 | } | |
59 | ||
60 | return qobj; | |
61 | } | |
62 | ||
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 | ||
69 | static void qmp_input_push(QmpInputVisitor *qiv, QObject *obj, Error **errp) | |
70 | { | |
71 | GHashTable *h; | |
72 | ||
73 | if (qiv->nb_stack >= QIV_STACK_SIZE) { | |
74 | error_set(errp, QERR_BUFFER_OVERRUN); | |
75 | return; | |
76 | } | |
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++; | |
89 | } | |
90 | ||
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 | ||
98 | static void qmp_input_pop(QmpInputVisitor *qiv, Error **errp) | |
99 | { | |
100 | assert(qiv->nb_stack > 0); | |
101 | ||
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); | |
108 | error_set(errp, QERR_QMP_EXTRA_MEMBER, key); | |
109 | } | |
110 | g_hash_table_unref(top_ht); | |
111 | } | |
112 | } | |
113 | ||
114 | qiv->nb_stack--; | |
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); | |
121 | QObject *qobj = qmp_input_get_object(qiv, name, true); | |
122 | Error *err = NULL; | |
123 | ||
124 | if (!qobj || qobject_type(qobj) != QTYPE_QDICT) { | |
125 | error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", | |
126 | "QDict"); | |
127 | return; | |
128 | } | |
129 | ||
130 | qmp_input_push(qiv, qobj, &err); | |
131 | if (err) { | |
132 | error_propagate(errp, err); | |
133 | return; | |
134 | } | |
135 | ||
136 | if (obj) { | |
137 | *obj = g_malloc0(size); | |
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 | ||
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 | ||
160 | static void qmp_input_start_list(Visitor *v, const char *name, Error **errp) | |
161 | { | |
162 | QmpInputVisitor *qiv = to_qiv(v); | |
163 | QObject *qobj = qmp_input_get_object(qiv, name, true); | |
164 | ||
165 | if (!qobj || qobject_type(qobj) != QTYPE_QLIST) { | |
166 | error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", | |
167 | "list"); | |
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]; | |
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 | } | |
189 | ||
190 | if (so->entry == NULL) { | |
191 | return NULL; | |
192 | } | |
193 | ||
194 | entry = g_malloc0(sizeof(*entry)); | |
195 | if (first) { | |
196 | *list = entry; | |
197 | } else { | |
198 | (*list)->next = entry; | |
199 | } | |
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 | ||
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) { | |
218 | error_set(errp, QERR_MISSING_PARAMETER, name ? name : "null"); | |
219 | return; | |
220 | } | |
221 | *kind = qobjects[qobject_type(qobj)]; | |
222 | } | |
223 | ||
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); | |
228 | QObject *qobj = qmp_input_get_object(qiv, name, true); | |
229 | ||
230 | if (!qobj || qobject_type(qobj) != QTYPE_QINT) { | |
231 | error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", | |
232 | "integer"); | |
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); | |
243 | QObject *qobj = qmp_input_get_object(qiv, name, true); | |
244 | ||
245 | if (!qobj || qobject_type(qobj) != QTYPE_QBOOL) { | |
246 | error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", | |
247 | "boolean"); | |
248 | return; | |
249 | } | |
250 | ||
251 | *obj = qbool_get_int(qobject_to_qbool(qobj)); | |
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); | |
258 | QObject *qobj = qmp_input_get_object(qiv, name, true); | |
259 | ||
260 | if (!qobj || qobject_type(qobj) != QTYPE_QSTRING) { | |
261 | error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", | |
262 | "string"); | |
263 | return; | |
264 | } | |
265 | ||
266 | *obj = g_strdup(qstring_get_str(qobject_to_qstring(qobj))); | |
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); | |
273 | QObject *qobj = qmp_input_get_object(qiv, name, true); | |
274 | ||
275 | if (!qobj || (qobject_type(qobj) != QTYPE_QFLOAT && | |
276 | qobject_type(qobj) != QTYPE_QINT)) { | |
277 | error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", | |
278 | "number"); | |
279 | return; | |
280 | } | |
281 | ||
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 | } | |
287 | } | |
288 | ||
289 | static void qmp_input_start_optional(Visitor *v, bool *present, | |
290 | const char *name, Error **errp) | |
291 | { | |
292 | QmpInputVisitor *qiv = to_qiv(v); | |
293 | QObject *qobj = qmp_input_get_object(qiv, name, true); | |
294 | ||
295 | if (!qobj) { | |
296 | *present = false; | |
297 | return; | |
298 | } | |
299 | ||
300 | *present = true; | |
301 | } | |
302 | ||
303 | Visitor *qmp_input_get_visitor(QmpInputVisitor *v) | |
304 | { | |
305 | return &v->visitor; | |
306 | } | |
307 | ||
308 | void qmp_input_visitor_cleanup(QmpInputVisitor *v) | |
309 | { | |
310 | qobject_decref(v->stack[0].obj); | |
311 | g_free(v); | |
312 | } | |
313 | ||
314 | QmpInputVisitor *qmp_input_visitor_new(QObject *obj) | |
315 | { | |
316 | QmpInputVisitor *v; | |
317 | ||
318 | v = g_malloc0(sizeof(*v)); | |
319 | ||
320 | v->visitor.start_struct = qmp_input_start_struct; | |
321 | v->visitor.end_struct = qmp_input_end_struct; | |
322 | v->visitor.start_implicit_struct = qmp_input_start_implicit_struct; | |
323 | v->visitor.end_implicit_struct = qmp_input_end_implicit_struct; | |
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; | |
327 | v->visitor.type_enum = input_type_enum; | |
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; | |
332 | v->visitor.start_optional = qmp_input_start_optional; | |
333 | v->visitor.get_next_type = qmp_input_get_next_type; | |
334 | ||
335 | qmp_input_push(v, obj, NULL); | |
336 | qobject_incref(obj); | |
337 | ||
338 | return v; | |
339 | } | |
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 | } |