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