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