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