1 /* 2 * Copyright (C) 2015 Red Hat, Inc. 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Lesser General Public 6 * License as published by the Free Software Foundation; either 7 * version 2.1 of the License, or (at your option) any later version. 8 * 9 * This library is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * Lesser General Public License for more details. 13 * 14 * You should have received a copy of the GNU Lesser General Public 15 * License along with this library. If not, see 16 * <http://www.gnu.org/licenses/>. 17 * 18 * Author: Daniel P. Berrange <berrange@redhat.com> 19 */ 20 21 #include "qemu/osdep.h" 22 23 #include "qapi/error.h" 24 #include "qapi/qobject-input-visitor.h" 25 #include "qapi/qmp/qdict.h" 26 #include "qapi/qmp/qobject.h" 27 #include "qom/object.h" 28 #include "qemu/module.h" 29 #include "qemu/option.h" 30 #include "qemu/keyval.h" 31 #include "qemu/config-file.h" 32 #include "qom/object_interfaces.h" 33 34 35 #define TYPE_DUMMY "qemu-dummy" 36 37 typedef struct DummyObject DummyObject; 38 typedef struct DummyObjectClass DummyObjectClass; 39 40 DECLARE_INSTANCE_CHECKER(DummyObject, DUMMY_OBJECT, 41 TYPE_DUMMY) 42 43 typedef enum DummyAnimal DummyAnimal; 44 45 enum DummyAnimal { 46 DUMMY_FROG, 47 DUMMY_ALLIGATOR, 48 DUMMY_PLATYPUS, 49 50 DUMMY_LAST, 51 }; 52 53 const QEnumLookup dummy_animal_map = { 54 .array = (const char *const[]) { 55 [DUMMY_FROG] = "frog", 56 [DUMMY_ALLIGATOR] = "alligator", 57 [DUMMY_PLATYPUS] = "platypus", 58 }, 59 .size = DUMMY_LAST 60 }; 61 62 struct DummyObject { 63 Object parent_obj; 64 65 bool bv; 66 DummyAnimal av; 67 char *sv; 68 }; 69 70 struct DummyObjectClass { 71 ObjectClass parent_class; 72 }; 73 74 75 static void dummy_set_bv(Object *obj, 76 bool value, 77 Error **errp) 78 { 79 DummyObject *dobj = DUMMY_OBJECT(obj); 80 81 dobj->bv = value; 82 } 83 84 static bool dummy_get_bv(Object *obj, 85 Error **errp) 86 { 87 DummyObject *dobj = DUMMY_OBJECT(obj); 88 89 return dobj->bv; 90 } 91 92 93 static void dummy_set_av(Object *obj, 94 int value, 95 Error **errp) 96 { 97 DummyObject *dobj = DUMMY_OBJECT(obj); 98 99 dobj->av = value; 100 } 101 102 static int dummy_get_av(Object *obj, 103 Error **errp) 104 { 105 DummyObject *dobj = DUMMY_OBJECT(obj); 106 107 return dobj->av; 108 } 109 110 111 static void dummy_set_sv(Object *obj, 112 const char *value, 113 Error **errp) 114 { 115 DummyObject *dobj = DUMMY_OBJECT(obj); 116 117 g_free(dobj->sv); 118 dobj->sv = g_strdup(value); 119 } 120 121 static char *dummy_get_sv(Object *obj, 122 Error **errp) 123 { 124 DummyObject *dobj = DUMMY_OBJECT(obj); 125 126 return g_strdup(dobj->sv); 127 } 128 129 130 static void dummy_init(Object *obj) 131 { 132 object_property_add_bool(obj, "bv", 133 dummy_get_bv, 134 dummy_set_bv); 135 } 136 137 138 static void dummy_class_init(ObjectClass *cls, void *data) 139 { 140 object_class_property_add_str(cls, "sv", 141 dummy_get_sv, 142 dummy_set_sv); 143 object_class_property_add_enum(cls, "av", 144 "DummyAnimal", 145 &dummy_animal_map, 146 dummy_get_av, 147 dummy_set_av); 148 } 149 150 151 static void dummy_finalize(Object *obj) 152 { 153 DummyObject *dobj = DUMMY_OBJECT(obj); 154 155 g_free(dobj->sv); 156 } 157 158 159 static const TypeInfo dummy_info = { 160 .name = TYPE_DUMMY, 161 .parent = TYPE_OBJECT, 162 .instance_size = sizeof(DummyObject), 163 .instance_init = dummy_init, 164 .instance_finalize = dummy_finalize, 165 .class_size = sizeof(DummyObjectClass), 166 .class_init = dummy_class_init, 167 .interfaces = (InterfaceInfo[]) { 168 { TYPE_USER_CREATABLE }, 169 { } 170 } 171 }; 172 173 174 /* 175 * The following 3 object classes are used to 176 * simulate the kind of relationships seen in 177 * qdev, which result in complex object 178 * property destruction ordering. 179 * 180 * DummyDev has a 'bus' child to a DummyBus 181 * DummyBus has a 'backend' child to a DummyBackend 182 * DummyDev has a 'backend' link to DummyBackend 183 * 184 * When DummyDev is finalized, it unparents the 185 * DummyBackend, which unparents the DummyDev 186 * which deletes the 'backend' link from DummyDev 187 * to DummyBackend. This illustrates that the 188 * object_property_del_all() method needs to 189 * cope with the list of properties being changed 190 * while it iterates over them. 191 */ 192 typedef struct DummyDev DummyDev; 193 typedef struct DummyDevClass DummyDevClass; 194 typedef struct DummyBus DummyBus; 195 typedef struct DummyBusClass DummyBusClass; 196 typedef struct DummyBackend DummyBackend; 197 typedef struct DummyBackendClass DummyBackendClass; 198 199 #define TYPE_DUMMY_DEV "qemu-dummy-dev" 200 #define TYPE_DUMMY_BUS "qemu-dummy-bus" 201 #define TYPE_DUMMY_BACKEND "qemu-dummy-backend" 202 203 DECLARE_INSTANCE_CHECKER(DummyDev, DUMMY_DEV, 204 TYPE_DUMMY_DEV) 205 DECLARE_INSTANCE_CHECKER(DummyBus, DUMMY_BUS, 206 TYPE_DUMMY_BUS) 207 DECLARE_INSTANCE_CHECKER(DummyBackend, DUMMY_BACKEND, 208 TYPE_DUMMY_BACKEND) 209 210 struct DummyDev { 211 Object parent_obj; 212 213 DummyBus *bus; 214 }; 215 216 struct DummyDevClass { 217 ObjectClass parent_class; 218 }; 219 220 struct DummyBus { 221 Object parent_obj; 222 223 DummyBackend *backend; 224 }; 225 226 struct DummyBusClass { 227 ObjectClass parent_class; 228 }; 229 230 struct DummyBackend { 231 Object parent_obj; 232 }; 233 234 struct DummyBackendClass { 235 ObjectClass parent_class; 236 }; 237 238 239 static void dummy_dev_finalize(Object *obj) 240 { 241 DummyDev *dev = DUMMY_DEV(obj); 242 243 object_unref(OBJECT(dev->bus)); 244 } 245 246 static void dummy_dev_init(Object *obj) 247 { 248 DummyDev *dev = DUMMY_DEV(obj); 249 DummyBus *bus = DUMMY_BUS(object_new(TYPE_DUMMY_BUS)); 250 DummyBackend *backend = DUMMY_BACKEND(object_new(TYPE_DUMMY_BACKEND)); 251 252 object_property_add_child(obj, "bus", OBJECT(bus)); 253 dev->bus = bus; 254 object_property_add_child(OBJECT(bus), "backend", OBJECT(backend)); 255 bus->backend = backend; 256 257 object_property_add_link(obj, "backend", TYPE_DUMMY_BACKEND, 258 (Object **)&bus->backend, NULL, 0); 259 } 260 261 static void dummy_dev_unparent(Object *obj) 262 { 263 DummyDev *dev = DUMMY_DEV(obj); 264 object_unparent(OBJECT(dev->bus)); 265 } 266 267 static void dummy_dev_class_init(ObjectClass *klass, void *opaque) 268 { 269 klass->unparent = dummy_dev_unparent; 270 } 271 272 273 static void dummy_bus_finalize(Object *obj) 274 { 275 DummyBus *bus = DUMMY_BUS(obj); 276 277 object_unref(OBJECT(bus->backend)); 278 } 279 280 static void dummy_bus_init(Object *obj) 281 { 282 } 283 284 static void dummy_bus_unparent(Object *obj) 285 { 286 DummyBus *bus = DUMMY_BUS(obj); 287 object_property_del(obj->parent, "backend"); 288 object_unparent(OBJECT(bus->backend)); 289 } 290 291 static void dummy_bus_class_init(ObjectClass *klass, void *opaque) 292 { 293 klass->unparent = dummy_bus_unparent; 294 } 295 296 static void dummy_backend_init(Object *obj) 297 { 298 } 299 300 301 static const TypeInfo dummy_dev_info = { 302 .name = TYPE_DUMMY_DEV, 303 .parent = TYPE_OBJECT, 304 .instance_size = sizeof(DummyDev), 305 .instance_init = dummy_dev_init, 306 .instance_finalize = dummy_dev_finalize, 307 .class_size = sizeof(DummyDevClass), 308 .class_init = dummy_dev_class_init, 309 }; 310 311 static const TypeInfo dummy_bus_info = { 312 .name = TYPE_DUMMY_BUS, 313 .parent = TYPE_OBJECT, 314 .instance_size = sizeof(DummyBus), 315 .instance_init = dummy_bus_init, 316 .instance_finalize = dummy_bus_finalize, 317 .class_size = sizeof(DummyBusClass), 318 .class_init = dummy_bus_class_init, 319 }; 320 321 static const TypeInfo dummy_backend_info = { 322 .name = TYPE_DUMMY_BACKEND, 323 .parent = TYPE_OBJECT, 324 .instance_size = sizeof(DummyBackend), 325 .instance_init = dummy_backend_init, 326 .class_size = sizeof(DummyBackendClass), 327 }; 328 329 static QemuOptsList qemu_object_opts = { 330 .name = "object", 331 .implied_opt_name = "qom-type", 332 .head = QTAILQ_HEAD_INITIALIZER(qemu_object_opts.head), 333 .desc = { 334 { } 335 }, 336 }; 337 338 339 static void test_dummy_createv(void) 340 { 341 Error *err = NULL; 342 Object *parent = object_get_objects_root(); 343 DummyObject *dobj = DUMMY_OBJECT( 344 object_new_with_props(TYPE_DUMMY, 345 parent, 346 "dummy0", 347 &err, 348 "bv", "yes", 349 "sv", "Hiss hiss hiss", 350 "av", "platypus", 351 NULL)); 352 353 g_assert(err == NULL); 354 g_assert_cmpstr(dobj->sv, ==, "Hiss hiss hiss"); 355 g_assert(dobj->bv == true); 356 g_assert(dobj->av == DUMMY_PLATYPUS); 357 358 g_assert(object_resolve_path_component(parent, "dummy0") 359 == OBJECT(dobj)); 360 361 object_unparent(OBJECT(dobj)); 362 } 363 364 365 static Object *new_helper(Error **errp, 366 Object *parent, 367 ...) 368 { 369 va_list vargs; 370 Object *obj; 371 372 va_start(vargs, parent); 373 obj = object_new_with_propv(TYPE_DUMMY, 374 parent, 375 "dummy0", 376 errp, 377 vargs); 378 va_end(vargs); 379 return obj; 380 } 381 382 static void test_dummy_createlist(void) 383 { 384 Error *err = NULL; 385 Object *parent = object_get_objects_root(); 386 DummyObject *dobj = DUMMY_OBJECT( 387 new_helper(&err, 388 parent, 389 "bv", "yes", 390 "sv", "Hiss hiss hiss", 391 "av", "platypus", 392 NULL)); 393 394 g_assert(err == NULL); 395 g_assert_cmpstr(dobj->sv, ==, "Hiss hiss hiss"); 396 g_assert(dobj->bv == true); 397 g_assert(dobj->av == DUMMY_PLATYPUS); 398 399 g_assert(object_resolve_path_component(parent, "dummy0") 400 == OBJECT(dobj)); 401 402 object_unparent(OBJECT(dobj)); 403 } 404 405 static bool test_create_obj(QDict *qdict, Error **errp) 406 { 407 Visitor *v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); 408 Object *obj = user_creatable_add_type(TYPE_DUMMY, "dev0", qdict, v, errp); 409 410 visit_free(v); 411 object_unref(obj); 412 return !!obj; 413 } 414 415 static void test_dummy_createcmdl(void) 416 { 417 QDict *qdict; 418 DummyObject *dobj; 419 Error *err = NULL; 420 bool created, help; 421 const char *params = "bv=yes,sv=Hiss hiss hiss,av=platypus"; 422 423 /* Needed for user_creatable_del. */ 424 qemu_add_opts(&qemu_object_opts); 425 426 qdict = keyval_parse(params, "qom-type", &help, &err); 427 g_assert(err == NULL); 428 g_assert(qdict); 429 g_assert(!help); 430 431 created = test_create_obj(qdict, &err); 432 g_assert(created); 433 g_assert(err == NULL); 434 qobject_unref(qdict); 435 436 dobj = DUMMY_OBJECT(object_resolve_path_component(object_get_objects_root(), 437 "dev0")); 438 g_assert(dobj); 439 g_assert_cmpstr(dobj->sv, ==, "Hiss hiss hiss"); 440 g_assert(dobj->bv == true); 441 g_assert(dobj->av == DUMMY_PLATYPUS); 442 443 qdict = keyval_parse(params, "qom-type", &help, &err); 444 created = test_create_obj(qdict, &err); 445 g_assert(!created); 446 g_assert(err); 447 g_assert(object_resolve_path_component(object_get_objects_root(), "dev0") 448 == OBJECT(dobj)); 449 qobject_unref(qdict); 450 error_free(err); 451 err = NULL; 452 453 qdict = keyval_parse(params, "qom-type", &help, &err); 454 user_creatable_del("dev0", &error_abort); 455 g_assert(object_resolve_path_component(object_get_objects_root(), "dev0") 456 == NULL); 457 458 created = test_create_obj(qdict, &err); 459 g_assert(created); 460 g_assert(err == NULL); 461 qobject_unref(qdict); 462 463 dobj = DUMMY_OBJECT(object_resolve_path_component(object_get_objects_root(), 464 "dev0")); 465 g_assert(dobj); 466 g_assert_cmpstr(dobj->sv, ==, "Hiss hiss hiss"); 467 g_assert(dobj->bv == true); 468 g_assert(dobj->av == DUMMY_PLATYPUS); 469 g_assert(object_resolve_path_component(object_get_objects_root(), "dev0") 470 == OBJECT(dobj)); 471 472 object_unparent(OBJECT(dobj)); 473 } 474 475 static void test_dummy_badenum(void) 476 { 477 Error *err = NULL; 478 Object *parent = object_get_objects_root(); 479 Object *dobj = 480 object_new_with_props(TYPE_DUMMY, 481 parent, 482 "dummy0", 483 &err, 484 "bv", "yes", 485 "sv", "Hiss hiss hiss", 486 "av", "yeti", 487 NULL); 488 489 g_assert(dobj == NULL); 490 g_assert(err != NULL); 491 g_assert_cmpstr(error_get_pretty(err), ==, 492 "Parameter 'av' does not accept value 'yeti'"); 493 494 g_assert(object_resolve_path_component(parent, "dummy0") 495 == NULL); 496 497 error_free(err); 498 } 499 500 501 static void test_dummy_getenum(void) 502 { 503 Error *err = NULL; 504 int val; 505 Object *parent = object_get_objects_root(); 506 DummyObject *dobj = DUMMY_OBJECT( 507 object_new_with_props(TYPE_DUMMY, 508 parent, 509 "dummy0", 510 &err, 511 "av", "platypus", 512 NULL)); 513 514 g_assert(err == NULL); 515 g_assert(dobj->av == DUMMY_PLATYPUS); 516 517 val = object_property_get_enum(OBJECT(dobj), 518 "av", 519 "DummyAnimal", 520 &error_abort); 521 g_assert(val == DUMMY_PLATYPUS); 522 523 /* A bad enum type name */ 524 val = object_property_get_enum(OBJECT(dobj), 525 "av", 526 "BadAnimal", 527 &err); 528 g_assert(val == -1); 529 error_free_or_abort(&err); 530 531 /* A non-enum property name */ 532 val = object_property_get_enum(OBJECT(dobj), 533 "iv", 534 "DummyAnimal", 535 &err); 536 g_assert(val == -1); 537 error_free_or_abort(&err); 538 539 object_unparent(OBJECT(dobj)); 540 } 541 542 543 static void test_dummy_prop_iterator(ObjectPropertyIterator *iter, 544 const char *expected[], int n) 545 { 546 ObjectProperty *prop; 547 int i; 548 549 while ((prop = object_property_iter_next(iter))) { 550 for (i = 0; i < n; i++) { 551 if (!g_strcmp0(prop->name, expected[i])) { 552 break; 553 } 554 } 555 g_assert(i < n); 556 expected[i] = NULL; 557 } 558 559 for (i = 0; i < n; i++) { 560 g_assert(!expected[i]); 561 } 562 } 563 564 static void test_dummy_iterator(void) 565 { 566 const char *expected[] = { 567 "type", /* inherited from TYPE_OBJECT */ 568 "sv", "av", /* class properties */ 569 "bv"}; /* instance property */ 570 Object *parent = object_get_objects_root(); 571 DummyObject *dobj = DUMMY_OBJECT( 572 object_new_with_props(TYPE_DUMMY, 573 parent, 574 "dummy0", 575 &error_abort, 576 "bv", "yes", 577 "sv", "Hiss hiss hiss", 578 "av", "platypus", 579 NULL)); 580 ObjectPropertyIterator iter; 581 582 object_property_iter_init(&iter, OBJECT(dobj)); 583 test_dummy_prop_iterator(&iter, expected, ARRAY_SIZE(expected)); 584 object_unparent(OBJECT(dobj)); 585 } 586 587 static void test_dummy_class_iterator(void) 588 { 589 const char *expected[] = { "type", "av", "sv" }; 590 ObjectPropertyIterator iter; 591 ObjectClass *klass = object_class_by_name(TYPE_DUMMY); 592 593 object_class_property_iter_init(&iter, klass); 594 test_dummy_prop_iterator(&iter, expected, ARRAY_SIZE(expected)); 595 } 596 597 static void test_dummy_delchild(void) 598 { 599 Object *parent = object_get_objects_root(); 600 DummyDev *dev = DUMMY_DEV( 601 object_new_with_props(TYPE_DUMMY_DEV, 602 parent, 603 "dev0", 604 &error_abort, 605 NULL)); 606 607 object_unparent(OBJECT(dev)); 608 } 609 610 static void test_qom_partial_path(void) 611 { 612 Object *root = object_get_objects_root(); 613 Object *cont1 = container_get(root, "/cont1"); 614 Object *obj1 = object_new(TYPE_DUMMY); 615 Object *obj2a = object_new(TYPE_DUMMY); 616 Object *obj2b = object_new(TYPE_DUMMY); 617 bool ambiguous; 618 619 /* Objects created: 620 * /cont1 621 * /cont1/obj1 622 * /cont1/obj2 (obj2a) 623 * /obj2 (obj2b) 624 */ 625 object_property_add_child(cont1, "obj1", obj1); 626 object_unref(obj1); 627 object_property_add_child(cont1, "obj2", obj2a); 628 object_unref(obj2a); 629 object_property_add_child(root, "obj2", obj2b); 630 object_unref(obj2b); 631 632 ambiguous = false; 633 g_assert(!object_resolve_path_type("", TYPE_DUMMY, &ambiguous)); 634 g_assert(ambiguous); 635 g_assert(!object_resolve_path_type("", TYPE_DUMMY, NULL)); 636 637 ambiguous = false; 638 g_assert(!object_resolve_path("obj2", &ambiguous)); 639 g_assert(ambiguous); 640 g_assert(!object_resolve_path("obj2", NULL)); 641 642 ambiguous = false; 643 g_assert(object_resolve_path("obj1", &ambiguous) == obj1); 644 g_assert(!ambiguous); 645 g_assert(object_resolve_path("obj1", NULL) == obj1); 646 647 object_unparent(obj2b); 648 object_unparent(cont1); 649 } 650 651 int main(int argc, char **argv) 652 { 653 g_test_init(&argc, &argv, NULL); 654 655 module_call_init(MODULE_INIT_QOM); 656 type_register_static(&dummy_info); 657 type_register_static(&dummy_dev_info); 658 type_register_static(&dummy_bus_info); 659 type_register_static(&dummy_backend_info); 660 661 g_test_add_func("/qom/proplist/createlist", test_dummy_createlist); 662 g_test_add_func("/qom/proplist/createv", test_dummy_createv); 663 g_test_add_func("/qom/proplist/createcmdline", test_dummy_createcmdl); 664 g_test_add_func("/qom/proplist/badenum", test_dummy_badenum); 665 g_test_add_func("/qom/proplist/getenum", test_dummy_getenum); 666 g_test_add_func("/qom/proplist/iterator", test_dummy_iterator); 667 g_test_add_func("/qom/proplist/class_iterator", test_dummy_class_iterator); 668 g_test_add_func("/qom/proplist/delchild", test_dummy_delchild); 669 g_test_add_func("/qom/resolve/partial", test_qom_partial_path); 670 671 return g_test_run(); 672 } 673