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