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