1 /* 2 * Input Visitor 3 * 4 * Copyright (C) 2012-2017 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 #include "qemu/cutils.h" 24 25 typedef struct StackObject { 26 const char *name; /* Name of @obj in its parent, if any */ 27 QObject *obj; /* QDict or QList being visited */ 28 void *qapi; /* sanity check that caller uses same pointer */ 29 30 GHashTable *h; /* If @obj is QDict: unvisited keys */ 31 const QListEntry *entry; /* If @obj is QList: unvisited tail */ 32 unsigned index; /* If @obj is QList: list index of @entry */ 33 34 QSLIST_ENTRY(StackObject) node; /* parent */ 35 } StackObject; 36 37 struct QObjectInputVisitor { 38 Visitor visitor; 39 40 /* Root of visit at visitor creation. */ 41 QObject *root; 42 43 /* Stack of objects being visited (all entries will be either 44 * QDict or QList). */ 45 QSLIST_HEAD(, StackObject) stack; 46 47 GString *errname; /* Accumulator for full_name() */ 48 }; 49 50 static QObjectInputVisitor *to_qiv(Visitor *v) 51 { 52 return container_of(v, QObjectInputVisitor, visitor); 53 } 54 55 static const char *full_name_nth(QObjectInputVisitor *qiv, const char *name, 56 int n) 57 { 58 StackObject *so; 59 char buf[32]; 60 61 if (qiv->errname) { 62 g_string_truncate(qiv->errname, 0); 63 } else { 64 qiv->errname = g_string_new(""); 65 } 66 67 QSLIST_FOREACH(so , &qiv->stack, node) { 68 if (n) { 69 n--; 70 } else if (qobject_type(so->obj) == QTYPE_QDICT) { 71 g_string_prepend(qiv->errname, name ?: "<anonymous>"); 72 g_string_prepend_c(qiv->errname, '.'); 73 } else { 74 snprintf(buf, sizeof(buf), "[%u]", so->index); 75 g_string_prepend(qiv->errname, buf); 76 } 77 name = so->name; 78 } 79 assert(!n); 80 81 if (name) { 82 g_string_prepend(qiv->errname, name); 83 } else if (qiv->errname->str[0] == '.') { 84 g_string_erase(qiv->errname, 0, 1); 85 } else if (!qiv->errname->str[0]) { 86 return "<anonymous>"; 87 } 88 89 return qiv->errname->str; 90 } 91 92 static const char *full_name(QObjectInputVisitor *qiv, const char *name) 93 { 94 return full_name_nth(qiv, name, 0); 95 } 96 97 static QObject *qobject_input_try_get_object(QObjectInputVisitor *qiv, 98 const char *name, 99 bool consume) 100 { 101 StackObject *tos; 102 QObject *qobj; 103 QObject *ret; 104 105 if (QSLIST_EMPTY(&qiv->stack)) { 106 /* Starting at root, name is ignored. */ 107 assert(qiv->root); 108 return qiv->root; 109 } 110 111 /* We are in a container; find the next element. */ 112 tos = QSLIST_FIRST(&qiv->stack); 113 qobj = tos->obj; 114 assert(qobj); 115 116 if (qobject_type(qobj) == QTYPE_QDICT) { 117 assert(name); 118 ret = qdict_get(qobject_to_qdict(qobj), name); 119 if (tos->h && consume && ret) { 120 bool removed = g_hash_table_remove(tos->h, name); 121 assert(removed); 122 } 123 } else { 124 assert(qobject_type(qobj) == QTYPE_QLIST); 125 assert(!name); 126 if (tos->entry) { 127 ret = qlist_entry_obj(tos->entry); 128 if (consume) { 129 tos->entry = qlist_next(tos->entry); 130 } 131 } else { 132 ret = NULL; 133 } 134 if (consume) { 135 tos->index++; 136 } 137 } 138 139 return ret; 140 } 141 142 static QObject *qobject_input_get_object(QObjectInputVisitor *qiv, 143 const char *name, 144 bool consume, Error **errp) 145 { 146 QObject *obj = qobject_input_try_get_object(qiv, name, consume); 147 148 if (!obj) { 149 error_setg(errp, QERR_MISSING_PARAMETER, full_name(qiv, name)); 150 } 151 return obj; 152 } 153 154 static void qdict_add_key(const char *key, QObject *obj, void *opaque) 155 { 156 GHashTable *h = opaque; 157 g_hash_table_insert(h, (gpointer) key, NULL); 158 } 159 160 static const QListEntry *qobject_input_push(QObjectInputVisitor *qiv, 161 const char *name, 162 QObject *obj, void *qapi) 163 { 164 GHashTable *h; 165 StackObject *tos = g_new0(StackObject, 1); 166 167 assert(obj); 168 tos->name = name; 169 tos->obj = obj; 170 tos->qapi = qapi; 171 172 if (qobject_type(obj) == QTYPE_QDICT) { 173 h = g_hash_table_new(g_str_hash, g_str_equal); 174 qdict_iter(qobject_to_qdict(obj), qdict_add_key, h); 175 tos->h = h; 176 } else { 177 assert(qobject_type(obj) == QTYPE_QLIST); 178 tos->entry = qlist_first(qobject_to_qlist(obj)); 179 tos->index = -1; 180 } 181 182 QSLIST_INSERT_HEAD(&qiv->stack, tos, node); 183 return tos->entry; 184 } 185 186 187 static void qobject_input_check_struct(Visitor *v, Error **errp) 188 { 189 QObjectInputVisitor *qiv = to_qiv(v); 190 StackObject *tos = QSLIST_FIRST(&qiv->stack); 191 GHashTableIter iter; 192 const char *key; 193 194 assert(tos && !tos->entry); 195 196 g_hash_table_iter_init(&iter, tos->h); 197 if (g_hash_table_iter_next(&iter, (void **)&key, NULL)) { 198 error_setg(errp, "Parameter '%s' is unexpected", 199 full_name(qiv, key)); 200 } 201 } 202 203 static void qobject_input_stack_object_free(StackObject *tos) 204 { 205 if (tos->h) { 206 g_hash_table_unref(tos->h); 207 } 208 209 g_free(tos); 210 } 211 212 static void qobject_input_pop(Visitor *v, void **obj) 213 { 214 QObjectInputVisitor *qiv = to_qiv(v); 215 StackObject *tos = QSLIST_FIRST(&qiv->stack); 216 217 assert(tos && tos->qapi == obj); 218 QSLIST_REMOVE_HEAD(&qiv->stack, node); 219 qobject_input_stack_object_free(tos); 220 } 221 222 static void qobject_input_start_struct(Visitor *v, const char *name, void **obj, 223 size_t size, Error **errp) 224 { 225 QObjectInputVisitor *qiv = to_qiv(v); 226 QObject *qobj = qobject_input_get_object(qiv, name, true, errp); 227 228 if (obj) { 229 *obj = NULL; 230 } 231 if (!qobj) { 232 return; 233 } 234 if (qobject_type(qobj) != QTYPE_QDICT) { 235 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, 236 full_name(qiv, name), "object"); 237 return; 238 } 239 240 qobject_input_push(qiv, name, qobj, obj); 241 242 if (obj) { 243 *obj = g_malloc0(size); 244 } 245 } 246 247 248 static void qobject_input_start_list(Visitor *v, const char *name, 249 GenericList **list, size_t size, 250 Error **errp) 251 { 252 QObjectInputVisitor *qiv = to_qiv(v); 253 QObject *qobj = qobject_input_get_object(qiv, name, true, errp); 254 const QListEntry *entry; 255 256 if (list) { 257 *list = NULL; 258 } 259 if (!qobj) { 260 return; 261 } 262 if (qobject_type(qobj) != QTYPE_QLIST) { 263 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, 264 full_name(qiv, name), "array"); 265 return; 266 } 267 268 entry = qobject_input_push(qiv, name, qobj, list); 269 if (entry && list) { 270 *list = g_malloc0(size); 271 } 272 } 273 274 static GenericList *qobject_input_next_list(Visitor *v, GenericList *tail, 275 size_t size) 276 { 277 QObjectInputVisitor *qiv = to_qiv(v); 278 StackObject *tos = QSLIST_FIRST(&qiv->stack); 279 280 assert(tos && tos->obj && qobject_type(tos->obj) == QTYPE_QLIST); 281 282 if (!tos->entry) { 283 return NULL; 284 } 285 tail->next = g_malloc0(size); 286 return tail->next; 287 } 288 289 static void qobject_input_check_list(Visitor *v, Error **errp) 290 { 291 QObjectInputVisitor *qiv = to_qiv(v); 292 StackObject *tos = QSLIST_FIRST(&qiv->stack); 293 294 assert(tos && tos->obj && qobject_type(tos->obj) == QTYPE_QLIST); 295 296 if (tos->entry) { 297 error_setg(errp, "Only %u list elements expected in %s", 298 tos->index + 1, full_name_nth(qiv, NULL, 1)); 299 } 300 } 301 302 303 static void qobject_input_start_alternate(Visitor *v, const char *name, 304 GenericAlternate **obj, size_t size, 305 bool promote_int, Error **errp) 306 { 307 QObjectInputVisitor *qiv = to_qiv(v); 308 QObject *qobj = qobject_input_get_object(qiv, name, false, errp); 309 310 if (!qobj) { 311 *obj = NULL; 312 return; 313 } 314 *obj = g_malloc0(size); 315 (*obj)->type = qobject_type(qobj); 316 if (promote_int && (*obj)->type == QTYPE_QINT) { 317 (*obj)->type = QTYPE_QFLOAT; 318 } 319 } 320 321 static void qobject_input_type_int64(Visitor *v, const char *name, int64_t *obj, 322 Error **errp) 323 { 324 QObjectInputVisitor *qiv = to_qiv(v); 325 QObject *qobj = qobject_input_get_object(qiv, name, true, errp); 326 QInt *qint; 327 328 if (!qobj) { 329 return; 330 } 331 qint = qobject_to_qint(qobj); 332 if (!qint) { 333 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, 334 full_name(qiv, name), "integer"); 335 return; 336 } 337 338 *obj = qint_get_int(qint); 339 } 340 341 342 static void qobject_input_type_int64_keyval(Visitor *v, const char *name, 343 int64_t *obj, Error **errp) 344 { 345 QObjectInputVisitor *qiv = to_qiv(v); 346 QObject *qobj = qobject_input_get_object(qiv, name, true, errp); 347 QString *qstr; 348 349 if (!qobj) { 350 return; 351 } 352 qstr = qobject_to_qstring(qobj); 353 if (!qstr) { 354 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, 355 full_name(qiv, name), "string"); 356 return; 357 } 358 359 if (qemu_strtoi64(qstring_get_str(qstr), NULL, 0, obj) < 0) { 360 /* TODO report -ERANGE more nicely */ 361 error_setg(errp, QERR_INVALID_PARAMETER_VALUE, 362 full_name(qiv, name), "integer"); 363 } 364 } 365 366 static void qobject_input_type_uint64(Visitor *v, const char *name, 367 uint64_t *obj, Error **errp) 368 { 369 /* FIXME: qobject_to_qint mishandles values over INT64_MAX */ 370 QObjectInputVisitor *qiv = to_qiv(v); 371 QObject *qobj = qobject_input_get_object(qiv, name, true, errp); 372 QInt *qint; 373 374 if (!qobj) { 375 return; 376 } 377 qint = qobject_to_qint(qobj); 378 if (!qint) { 379 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, 380 full_name(qiv, name), "integer"); 381 return; 382 } 383 384 *obj = qint_get_int(qint); 385 } 386 387 static void qobject_input_type_uint64_keyval(Visitor *v, const char *name, 388 uint64_t *obj, Error **errp) 389 { 390 QObjectInputVisitor *qiv = to_qiv(v); 391 QObject *qobj = qobject_input_get_object(qiv, name, true, errp); 392 QString *qstr; 393 394 if (!qobj) { 395 return; 396 } 397 qstr = qobject_to_qstring(qobj); 398 if (!qstr) { 399 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, 400 full_name(qiv, name), "string"); 401 return; 402 } 403 404 if (qemu_strtou64(qstring_get_str(qstr), NULL, 0, obj) < 0) { 405 /* TODO report -ERANGE more nicely */ 406 error_setg(errp, QERR_INVALID_PARAMETER_VALUE, 407 full_name(qiv, name), "integer"); 408 } 409 } 410 411 static void qobject_input_type_bool(Visitor *v, const char *name, bool *obj, 412 Error **errp) 413 { 414 QObjectInputVisitor *qiv = to_qiv(v); 415 QObject *qobj = qobject_input_get_object(qiv, name, true, errp); 416 QBool *qbool; 417 418 if (!qobj) { 419 return; 420 } 421 qbool = qobject_to_qbool(qobj); 422 if (!qbool) { 423 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, 424 full_name(qiv, name), "boolean"); 425 return; 426 } 427 428 *obj = qbool_get_bool(qbool); 429 } 430 431 static void qobject_input_type_bool_keyval(Visitor *v, const char *name, 432 bool *obj, Error **errp) 433 { 434 QObjectInputVisitor *qiv = to_qiv(v); 435 QObject *qobj = qobject_input_get_object(qiv, name, true, errp); 436 QString *qstr; 437 const char *str; 438 439 if (!qobj) { 440 return; 441 } 442 qstr = qobject_to_qstring(qobj); 443 if (!qstr) { 444 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, 445 full_name(qiv, name), "string"); 446 return; 447 } 448 449 str = qstring_get_str(qstr); 450 if (!strcmp(str, "on")) { 451 *obj = true; 452 } else if (!strcmp(str, "off")) { 453 *obj = false; 454 } else { 455 error_setg(errp, QERR_INVALID_PARAMETER_VALUE, 456 full_name(qiv, name), "'on' or 'off'"); 457 } 458 } 459 460 static void qobject_input_type_str(Visitor *v, const char *name, char **obj, 461 Error **errp) 462 { 463 QObjectInputVisitor *qiv = to_qiv(v); 464 QObject *qobj = qobject_input_get_object(qiv, name, true, errp); 465 QString *qstr; 466 467 *obj = NULL; 468 if (!qobj) { 469 return; 470 } 471 qstr = qobject_to_qstring(qobj); 472 if (!qstr) { 473 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, 474 full_name(qiv, name), "string"); 475 return; 476 } 477 478 *obj = g_strdup(qstring_get_str(qstr)); 479 } 480 481 static void qobject_input_type_number(Visitor *v, const char *name, double *obj, 482 Error **errp) 483 { 484 QObjectInputVisitor *qiv = to_qiv(v); 485 QObject *qobj = qobject_input_get_object(qiv, name, true, errp); 486 QInt *qint; 487 QFloat *qfloat; 488 489 if (!qobj) { 490 return; 491 } 492 qint = qobject_to_qint(qobj); 493 if (qint) { 494 *obj = qint_get_int(qobject_to_qint(qobj)); 495 return; 496 } 497 498 qfloat = qobject_to_qfloat(qobj); 499 if (qfloat) { 500 *obj = qfloat_get_double(qobject_to_qfloat(qobj)); 501 return; 502 } 503 504 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, 505 full_name(qiv, name), "number"); 506 } 507 508 static void qobject_input_type_number_keyval(Visitor *v, const char *name, 509 double *obj, Error **errp) 510 { 511 QObjectInputVisitor *qiv = to_qiv(v); 512 QObject *qobj = qobject_input_get_object(qiv, name, true, errp); 513 QString *qstr; 514 const char *str; 515 char *endp; 516 517 if (!qobj) { 518 return; 519 } 520 qstr = qobject_to_qstring(qobj); 521 if (!qstr) { 522 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, 523 full_name(qiv, name), "string"); 524 return; 525 } 526 527 str = qstring_get_str(qstr); 528 errno = 0; 529 *obj = strtod(str, &endp); 530 if (errno || endp == str || *endp) { 531 /* TODO report -ERANGE more nicely */ 532 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, 533 full_name(qiv, name), "number"); 534 } 535 } 536 537 static void qobject_input_type_any(Visitor *v, const char *name, QObject **obj, 538 Error **errp) 539 { 540 QObjectInputVisitor *qiv = to_qiv(v); 541 QObject *qobj = qobject_input_get_object(qiv, name, true, errp); 542 543 *obj = NULL; 544 if (!qobj) { 545 return; 546 } 547 548 qobject_incref(qobj); 549 *obj = qobj; 550 } 551 552 static void qobject_input_type_null(Visitor *v, const char *name, Error **errp) 553 { 554 QObjectInputVisitor *qiv = to_qiv(v); 555 QObject *qobj = qobject_input_get_object(qiv, name, true, errp); 556 557 if (!qobj) { 558 return; 559 } 560 561 if (qobject_type(qobj) != QTYPE_QNULL) { 562 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, 563 full_name(qiv, name), "null"); 564 } 565 } 566 567 static void qobject_input_type_size_keyval(Visitor *v, const char *name, 568 uint64_t *obj, Error **errp) 569 { 570 QObjectInputVisitor *qiv = to_qiv(v); 571 QObject *qobj = qobject_input_get_object(qiv, name, true, errp); 572 QString *qstr; 573 574 if (!qobj) { 575 return; 576 } 577 qstr = qobject_to_qstring(qobj); 578 if (!qstr) { 579 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, 580 full_name(qiv, name), "string"); 581 return; 582 } 583 584 if (qemu_strtosz(qstring_get_str(qstr), NULL, obj) < 0) { 585 /* TODO report -ERANGE more nicely */ 586 error_setg(errp, QERR_INVALID_PARAMETER_VALUE, 587 full_name(qiv, name), "size"); 588 } 589 } 590 591 static void qobject_input_optional(Visitor *v, const char *name, bool *present) 592 { 593 QObjectInputVisitor *qiv = to_qiv(v); 594 QObject *qobj = qobject_input_try_get_object(qiv, name, false); 595 596 if (!qobj) { 597 *present = false; 598 return; 599 } 600 601 *present = true; 602 } 603 604 static void qobject_input_free(Visitor *v) 605 { 606 QObjectInputVisitor *qiv = to_qiv(v); 607 608 while (!QSLIST_EMPTY(&qiv->stack)) { 609 StackObject *tos = QSLIST_FIRST(&qiv->stack); 610 611 QSLIST_REMOVE_HEAD(&qiv->stack, node); 612 qobject_input_stack_object_free(tos); 613 } 614 615 qobject_decref(qiv->root); 616 if (qiv->errname) { 617 g_string_free(qiv->errname, TRUE); 618 } 619 g_free(qiv); 620 } 621 622 static QObjectInputVisitor *qobject_input_visitor_base_new(QObject *obj) 623 { 624 QObjectInputVisitor *v = g_malloc0(sizeof(*v)); 625 626 assert(obj); 627 628 v->visitor.type = VISITOR_INPUT; 629 v->visitor.start_struct = qobject_input_start_struct; 630 v->visitor.check_struct = qobject_input_check_struct; 631 v->visitor.end_struct = qobject_input_pop; 632 v->visitor.start_list = qobject_input_start_list; 633 v->visitor.next_list = qobject_input_next_list; 634 v->visitor.check_list = qobject_input_check_list; 635 v->visitor.end_list = qobject_input_pop; 636 v->visitor.start_alternate = qobject_input_start_alternate; 637 v->visitor.optional = qobject_input_optional; 638 v->visitor.free = qobject_input_free; 639 640 v->root = obj; 641 qobject_incref(obj); 642 643 return v; 644 } 645 646 Visitor *qobject_input_visitor_new(QObject *obj) 647 { 648 QObjectInputVisitor *v = qobject_input_visitor_base_new(obj); 649 650 v->visitor.type_int64 = qobject_input_type_int64; 651 v->visitor.type_uint64 = qobject_input_type_uint64; 652 v->visitor.type_bool = qobject_input_type_bool; 653 v->visitor.type_str = qobject_input_type_str; 654 v->visitor.type_number = qobject_input_type_number; 655 v->visitor.type_any = qobject_input_type_any; 656 v->visitor.type_null = qobject_input_type_null; 657 658 return &v->visitor; 659 } 660 661 Visitor *qobject_input_visitor_new_keyval(QObject *obj) 662 { 663 QObjectInputVisitor *v = qobject_input_visitor_base_new(obj); 664 665 v->visitor.type_int64 = qobject_input_type_int64_keyval; 666 v->visitor.type_uint64 = qobject_input_type_uint64_keyval; 667 v->visitor.type_bool = qobject_input_type_bool_keyval; 668 v->visitor.type_str = qobject_input_type_str; 669 v->visitor.type_number = qobject_input_type_number_keyval; 670 v->visitor.type_any = qobject_input_type_any; 671 v->visitor.type_null = qobject_input_type_null; 672 v->visitor.type_size = qobject_input_type_size_keyval; 673 674 return &v->visitor; 675 } 676