diff options
Diffstat (limited to 'qom')
-rw-r--r-- | qom/container.c | 52 | ||||
-rw-r--r-- | qom/meson.build | 10 | ||||
-rw-r--r-- | qom/object.c | 2813 | ||||
-rw-r--r-- | qom/object_interfaces.c | 367 | ||||
-rw-r--r-- | qom/qom-hmp-cmds.c | 152 | ||||
-rw-r--r-- | qom/qom-qmp-cmds.c | 237 | ||||
-rw-r--r-- | qom/qom-qobject.c | 45 | ||||
-rw-r--r-- | qom/trace-events | 5 | ||||
-rw-r--r-- | qom/trace.h | 1 |
9 files changed, 3682 insertions, 0 deletions
diff --git a/qom/container.c b/qom/container.c new file mode 100644 index 000000000..455e8410c --- /dev/null +++ b/qom/container.c @@ -0,0 +1,52 @@ +/* + * Device Container + * + * Copyright IBM, Corp. 2012 + * + * Authors: + * Anthony Liguori <aliguori@us.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qom/object.h" +#include "qemu/module.h" + +static const TypeInfo container_info = { + .name = "container", + .parent = TYPE_OBJECT, +}; + +static void container_register_types(void) +{ + type_register_static(&container_info); +} + +Object *container_get(Object *root, const char *path) +{ + Object *obj, *child; + char **parts; + int i; + + parts = g_strsplit(path, "/", 0); + assert(parts != NULL && parts[0] != NULL && !parts[0][0]); + obj = root; + + for (i = 1; parts[i] != NULL; i++, obj = child) { + child = object_resolve_path_component(obj, parts[i]); + if (!child) { + child = object_new("container"); + object_property_add_child(obj, parts[i], child); + object_unref(child); + } + } + + g_strfreev(parts); + + return obj; +} + + +type_init(container_register_types) diff --git a/qom/meson.build b/qom/meson.build new file mode 100644 index 000000000..062a3789d --- /dev/null +++ b/qom/meson.build @@ -0,0 +1,10 @@ +qom_ss.add(genh) +qom_ss.add(files( + 'container.c', + 'object.c', + 'object_interfaces.c', + 'qom-qobject.c', +)) + +qmp_ss.add(files('qom-qmp-cmds.c')) +softmmu_ss.add(files('qom-hmp-cmds.c')) diff --git a/qom/object.c b/qom/object.c new file mode 100644 index 000000000..4f0677cca --- /dev/null +++ b/qom/object.c @@ -0,0 +1,2813 @@ +/* + * QEMU Object Model + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori <aliguori@us.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "hw/qdev-core.h" +#include "qapi/error.h" +#include "qom/object.h" +#include "qom/object_interfaces.h" +#include "qemu/cutils.h" +#include "qapi/visitor.h" +#include "qapi/string-input-visitor.h" +#include "qapi/string-output-visitor.h" +#include "qapi/qobject-input-visitor.h" +#include "qapi/forward-visitor.h" +#include "qapi/qapi-builtin-visit.h" +#include "qapi/qmp/qerror.h" +#include "qapi/qmp/qjson.h" +#include "trace.h" + +/* TODO: replace QObject with a simpler visitor to avoid a dependency + * of the QOM core on QObject? */ +#include "qom/qom-qobject.h" +#include "qapi/qmp/qbool.h" +#include "qapi/qmp/qnum.h" +#include "qapi/qmp/qstring.h" +#include "qemu/error-report.h" + +#define MAX_INTERFACES 32 + +typedef struct InterfaceImpl InterfaceImpl; +typedef struct TypeImpl TypeImpl; + +struct InterfaceImpl +{ + const char *typename; +}; + +struct TypeImpl +{ + const char *name; + + size_t class_size; + + size_t instance_size; + size_t instance_align; + + void (*class_init)(ObjectClass *klass, void *data); + void (*class_base_init)(ObjectClass *klass, void *data); + + void *class_data; + + void (*instance_init)(Object *obj); + void (*instance_post_init)(Object *obj); + void (*instance_finalize)(Object *obj); + + bool abstract; + + const char *parent; + TypeImpl *parent_type; + + ObjectClass *class; + + int num_interfaces; + InterfaceImpl interfaces[MAX_INTERFACES]; +}; + +static Type type_interface; + +static GHashTable *type_table_get(void) +{ + static GHashTable *type_table; + + if (type_table == NULL) { + type_table = g_hash_table_new(g_str_hash, g_str_equal); + } + + return type_table; +} + +static bool enumerating_types; + +static void type_table_add(TypeImpl *ti) +{ + assert(!enumerating_types); + g_hash_table_insert(type_table_get(), (void *)ti->name, ti); +} + +static TypeImpl *type_table_lookup(const char *name) +{ + return g_hash_table_lookup(type_table_get(), name); +} + +static TypeImpl *type_new(const TypeInfo *info) +{ + TypeImpl *ti = g_malloc0(sizeof(*ti)); + int i; + + g_assert(info->name != NULL); + + if (type_table_lookup(info->name) != NULL) { + fprintf(stderr, "Registering `%s' which already exists\n", info->name); + abort(); + } + + ti->name = g_strdup(info->name); + ti->parent = g_strdup(info->parent); + + ti->class_size = info->class_size; + ti->instance_size = info->instance_size; + ti->instance_align = info->instance_align; + + ti->class_init = info->class_init; + ti->class_base_init = info->class_base_init; + ti->class_data = info->class_data; + + ti->instance_init = info->instance_init; + ti->instance_post_init = info->instance_post_init; + ti->instance_finalize = info->instance_finalize; + + ti->abstract = info->abstract; + + for (i = 0; info->interfaces && info->interfaces[i].type; i++) { + ti->interfaces[i].typename = g_strdup(info->interfaces[i].type); + } + ti->num_interfaces = i; + + return ti; +} + +static TypeImpl *type_register_internal(const TypeInfo *info) +{ + TypeImpl *ti; + ti = type_new(info); + + type_table_add(ti); + return ti; +} + +TypeImpl *type_register(const TypeInfo *info) +{ + assert(info->parent); + return type_register_internal(info); +} + +TypeImpl *type_register_static(const TypeInfo *info) +{ + return type_register(info); +} + +void type_register_static_array(const TypeInfo *infos, int nr_infos) +{ + int i; + + for (i = 0; i < nr_infos; i++) { + type_register_static(&infos[i]); + } +} + +static TypeImpl *type_get_by_name(const char *name) +{ + if (name == NULL) { + return NULL; + } + + return type_table_lookup(name); +} + +static TypeImpl *type_get_parent(TypeImpl *type) +{ + if (!type->parent_type && type->parent) { + type->parent_type = type_get_by_name(type->parent); + if (!type->parent_type) { + fprintf(stderr, "Type '%s' is missing its parent '%s'\n", + type->name, type->parent); + abort(); + } + } + + return type->parent_type; +} + +static bool type_has_parent(TypeImpl *type) +{ + return (type->parent != NULL); +} + +static size_t type_class_get_size(TypeImpl *ti) +{ + if (ti->class_size) { + return ti->class_size; + } + + if (type_has_parent(ti)) { + return type_class_get_size(type_get_parent(ti)); + } + + return sizeof(ObjectClass); +} + +static size_t type_object_get_size(TypeImpl *ti) +{ + if (ti->instance_size) { + return ti->instance_size; + } + + if (type_has_parent(ti)) { + return type_object_get_size(type_get_parent(ti)); + } + + return 0; +} + +size_t object_type_get_instance_size(const char *typename) +{ + TypeImpl *type = type_get_by_name(typename); + + g_assert(type != NULL); + return type_object_get_size(type); +} + +static bool type_is_ancestor(TypeImpl *type, TypeImpl *target_type) +{ + assert(target_type); + + /* Check if target_type is a direct ancestor of type */ + while (type) { + if (type == target_type) { + return true; + } + + type = type_get_parent(type); + } + + return false; +} + +static void type_initialize(TypeImpl *ti); + +static void type_initialize_interface(TypeImpl *ti, TypeImpl *interface_type, + TypeImpl *parent_type) +{ + InterfaceClass *new_iface; + TypeInfo info = { }; + TypeImpl *iface_impl; + + info.parent = parent_type->name; + info.name = g_strdup_printf("%s::%s", ti->name, interface_type->name); + info.abstract = true; + + iface_impl = type_new(&info); + iface_impl->parent_type = parent_type; + type_initialize(iface_impl); + g_free((char *)info.name); + + new_iface = (InterfaceClass *)iface_impl->class; + new_iface->concrete_class = ti->class; + new_iface->interface_type = interface_type; + + ti->class->interfaces = g_slist_append(ti->class->interfaces, new_iface); +} + +static void object_property_free(gpointer data) +{ + ObjectProperty *prop = data; + + if (prop->defval) { + qobject_unref(prop->defval); + prop->defval = NULL; + } + g_free(prop->name); + g_free(prop->type); + g_free(prop->description); + g_free(prop); +} + +static void type_initialize(TypeImpl *ti) +{ + TypeImpl *parent; + + if (ti->class) { + return; + } + + ti->class_size = type_class_get_size(ti); + ti->instance_size = type_object_get_size(ti); + /* Any type with zero instance_size is implicitly abstract. + * This means interface types are all abstract. + */ + if (ti->instance_size == 0) { + ti->abstract = true; + } + if (type_is_ancestor(ti, type_interface)) { + assert(ti->instance_size == 0); + assert(ti->abstract); + assert(!ti->instance_init); + assert(!ti->instance_post_init); + assert(!ti->instance_finalize); + assert(!ti->num_interfaces); + } + ti->class = g_malloc0(ti->class_size); + + parent = type_get_parent(ti); + if (parent) { + type_initialize(parent); + GSList *e; + int i; + + g_assert(parent->class_size <= ti->class_size); + g_assert(parent->instance_size <= ti->instance_size); + memcpy(ti->class, parent->class, parent->class_size); + ti->class->interfaces = NULL; + + for (e = parent->class->interfaces; e; e = e->next) { + InterfaceClass *iface = e->data; + ObjectClass *klass = OBJECT_CLASS(iface); + + type_initialize_interface(ti, iface->interface_type, klass->type); + } + + for (i = 0; i < ti->num_interfaces; i++) { + TypeImpl *t = type_get_by_name(ti->interfaces[i].typename); + if (!t) { + error_report("missing interface '%s' for object '%s'", + ti->interfaces[i].typename, parent->name); + abort(); + } + for (e = ti->class->interfaces; e; e = e->next) { + TypeImpl *target_type = OBJECT_CLASS(e->data)->type; + + if (type_is_ancestor(target_type, t)) { + break; + } + } + + if (e) { + continue; + } + + type_initialize_interface(ti, t, t); + } + } + + ti->class->properties = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, + object_property_free); + + ti->class->type = ti; + + while (parent) { + if (parent->class_base_init) { + parent->class_base_init(ti->class, ti->class_data); + } + parent = type_get_parent(parent); + } + + if (ti->class_init) { + ti->class_init(ti->class, ti->class_data); + } +} + +static void object_init_with_type(Object *obj, TypeImpl *ti) +{ + if (type_has_parent(ti)) { + object_init_with_type(obj, type_get_parent(ti)); + } + + if (ti->instance_init) { + ti->instance_init(obj); + } +} + +static void object_post_init_with_type(Object *obj, TypeImpl *ti) +{ + if (ti->instance_post_init) { + ti->instance_post_init(obj); + } + + if (type_has_parent(ti)) { + object_post_init_with_type(obj, type_get_parent(ti)); + } +} + +bool object_apply_global_props(Object *obj, const GPtrArray *props, + Error **errp) +{ + int i; + + if (!props) { + return true; + } + + for (i = 0; i < props->len; i++) { + GlobalProperty *p = g_ptr_array_index(props, i); + Error *err = NULL; + + if (object_dynamic_cast(obj, p->driver) == NULL) { + continue; + } + if (p->optional && !object_property_find(obj, p->property)) { + continue; + } + p->used = true; + if (!object_property_parse(obj, p->property, p->value, &err)) { + error_prepend(&err, "can't apply global %s.%s=%s: ", + p->driver, p->property, p->value); + /* + * If errp != NULL, propagate error and return. + * If errp == NULL, report a warning, but keep going + * with the remaining globals. + */ + if (errp) { + error_propagate(errp, err); + return false; + } else { + warn_report_err(err); + } + } + } + + return true; +} + +/* + * Global property defaults + * Slot 0: accelerator's global property defaults + * Slot 1: machine's global property defaults + * Slot 2: global properties from legacy command line option + * Each is a GPtrArray of of GlobalProperty. + * Applied in order, later entries override earlier ones. + */ +static GPtrArray *object_compat_props[3]; + +/* + * Retrieve @GPtrArray for global property defined with options + * other than "-global". These are generally used for syntactic + * sugar and legacy command line options. + */ +void object_register_sugar_prop(const char *driver, const char *prop, + const char *value, bool optional) +{ + GlobalProperty *g; + if (!object_compat_props[2]) { + object_compat_props[2] = g_ptr_array_new(); + } + g = g_new0(GlobalProperty, 1); + g->driver = g_strdup(driver); + g->property = g_strdup(prop); + g->value = g_strdup(value); + g->optional = optional; + g_ptr_array_add(object_compat_props[2], g); +} + +/* + * Set machine's global property defaults to @compat_props. + * May be called at most once. + */ +void object_set_machine_compat_props(GPtrArray *compat_props) +{ + assert(!object_compat_props[1]); + object_compat_props[1] = compat_props; +} + +/* + * Set accelerator's global property defaults to @compat_props. + * May be called at most once. + */ +void object_set_accelerator_compat_props(GPtrArray *compat_props) +{ + assert(!object_compat_props[0]); + object_compat_props[0] = compat_props; +} + +void object_apply_compat_props(Object *obj) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(object_compat_props); i++) { + object_apply_global_props(obj, object_compat_props[i], + i == 2 ? &error_fatal : &error_abort); + } +} + +static void object_class_property_init_all(Object *obj) +{ + ObjectPropertyIterator iter; + ObjectProperty *prop; + + object_class_property_iter_init(&iter, object_get_class(obj)); + while ((prop = object_property_iter_next(&iter))) { + if (prop->init) { + prop->init(obj, prop); + } + } +} + +static void object_initialize_with_type(Object *obj, size_t size, TypeImpl *type) +{ + type_initialize(type); + + g_assert(type->instance_size >= sizeof(Object)); + g_assert(type->abstract == false); + g_assert(size >= type->instance_size); + + memset(obj, 0, type->instance_size); + obj->class = type->class; + object_ref(obj); + object_class_property_init_all(obj); + obj->properties = g_hash_table_new_full(g_str_hash, g_str_equal, + NULL, object_property_free); + object_init_with_type(obj, type); + object_post_init_with_type(obj, type); +} + +void object_initialize(void *data, size_t size, const char *typename) +{ + TypeImpl *type = type_get_by_name(typename); + +#ifdef CONFIG_MODULES + if (!type) { + module_load_qom_one(typename); + type = type_get_by_name(typename); + } +#endif + if (!type) { + error_report("missing object type '%s'", typename); + abort(); + } + + object_initialize_with_type(data, size, type); +} + +bool object_initialize_child_with_props(Object *parentobj, + const char *propname, + void *childobj, size_t size, + const char *type, + Error **errp, ...) +{ + va_list vargs; + bool ok; + + va_start(vargs, errp); + ok = object_initialize_child_with_propsv(parentobj, propname, + childobj, size, type, errp, + vargs); + va_end(vargs); + return ok; +} + +bool object_initialize_child_with_propsv(Object *parentobj, + const char *propname, + void *childobj, size_t size, + const char *type, + Error **errp, va_list vargs) +{ + bool ok = false; + Object *obj; + UserCreatable *uc; + + object_initialize(childobj, size, type); + obj = OBJECT(childobj); + + if (!object_set_propv(obj, errp, vargs)) { + goto out; + } + + object_property_add_child(parentobj, propname, obj); + + uc = (UserCreatable *)object_dynamic_cast(obj, TYPE_USER_CREATABLE); + if (uc) { + if (!user_creatable_complete(uc, errp)) { + object_unparent(obj); + goto out; + } + } + + ok = true; + +out: + /* + * We want @obj's reference to be 1 on success, 0 on failure. + * On success, it's 2: one taken by object_initialize(), and one + * by object_property_add_child(). + * On failure in object_initialize() or earlier, it's 1. + * On failure afterwards, it's also 1: object_unparent() releases + * the reference taken by object_property_add_child(). + */ + object_unref(obj); + return ok; +} + +void object_initialize_child_internal(Object *parent, + const char *propname, + void *child, size_t size, + const char *type) +{ + object_initialize_child_with_props(parent, propname, child, size, type, + &error_abort, NULL); +} + +static inline bool object_property_is_child(ObjectProperty *prop) +{ + return strstart(prop->type, "child<", NULL); +} + +static void object_property_del_all(Object *obj) +{ + g_autoptr(GHashTable) done = g_hash_table_new(NULL, NULL); + ObjectProperty *prop; + ObjectPropertyIterator iter; + bool released; + + do { + released = false; + object_property_iter_init(&iter, obj); + while ((prop = object_property_iter_next(&iter)) != NULL) { + if (g_hash_table_add(done, prop)) { + if (prop->release) { + prop->release(obj, prop->name, prop->opaque); + released = true; + break; + } + } + } + } while (released); + + g_hash_table_unref(obj->properties); +} + +static void object_property_del_child(Object *obj, Object *child) +{ + ObjectProperty *prop; + GHashTableIter iter; + gpointer key, value; + + g_hash_table_iter_init(&iter, obj->properties); + while (g_hash_table_iter_next(&iter, &key, &value)) { + prop = value; + if (object_property_is_child(prop) && prop->opaque == child) { + if (prop->release) { + prop->release(obj, prop->name, prop->opaque); + prop->release = NULL; + } + break; + } + } + g_hash_table_iter_init(&iter, obj->properties); + while (g_hash_table_iter_next(&iter, &key, &value)) { + prop = value; + if (object_property_is_child(prop) && prop->opaque == child) { + g_hash_table_iter_remove(&iter); + break; + } + } +} + +void object_unparent(Object *obj) +{ + if (obj->parent) { + object_property_del_child(obj->parent, obj); + } +} + +static void object_deinit(Object *obj, TypeImpl *type) +{ + if (type->instance_finalize) { + type->instance_finalize(obj); + } + + if (type_has_parent(type)) { + object_deinit(obj, type_get_parent(type)); + } +} + +static void object_finalize(void *data) +{ + Object *obj = data; + TypeImpl *ti = obj->class->type; + + object_property_del_all(obj); + object_deinit(obj, ti); + + g_assert(obj->ref == 0); + g_assert(obj->parent == NULL); + if (obj->free) { + obj->free(obj); + } +} + +/* Find the minimum alignment guaranteed by the system malloc. */ +#if __STDC_VERSION__ >= 201112L +typedef max_align_t qemu_max_align_t; +#else +typedef union { + long l; + void *p; + double d; + long double ld; +} qemu_max_align_t; +#endif + +static Object *object_new_with_type(Type type) +{ + Object *obj; + size_t size, align; + void (*obj_free)(void *); + + g_assert(type != NULL); + type_initialize(type); + + size = type->instance_size; + align = type->instance_align; + + /* + * Do not use qemu_memalign unless required. Depending on the + * implementation, extra alignment implies extra overhead. + */ + if (likely(align <= __alignof__(qemu_max_align_t))) { + obj = g_malloc(size); + obj_free = g_free; + } else { + obj = qemu_memalign(align, size); + obj_free = qemu_vfree; + } + + object_initialize_with_type(obj, size, type); + obj->free = obj_free; + + return obj; +} + +Object *object_new_with_class(ObjectClass *klass) +{ + return object_new_with_type(klass->type); +} + +Object *object_new(const char *typename) +{ + TypeImpl *ti = type_get_by_name(typename); + + return object_new_with_type(ti); +} + + +Object *object_new_with_props(const char *typename, + Object *parent, + const char *id, + Error **errp, + ...) +{ + va_list vargs; + Object *obj; + + va_start(vargs, errp); + obj = object_new_with_propv(typename, parent, id, errp, vargs); + va_end(vargs); + + return obj; +} + + +Object *object_new_with_propv(const char *typename, + Object *parent, + const char *id, + Error **errp, + va_list vargs) +{ + Object *obj; + ObjectClass *klass; + UserCreatable *uc; + + klass = object_class_by_name(typename); + if (!klass) { + error_setg(errp, "invalid object type: %s", typename); + return NULL; + } + + if (object_class_is_abstract(klass)) { + error_setg(errp, "object type '%s' is abstract", typename); + return NULL; + } + obj = object_new_with_type(klass->type); + + if (!object_set_propv(obj, errp, vargs)) { + goto error; + } + + if (id != NULL) { + object_property_add_child(parent, id, obj); + } + + uc = (UserCreatable *)object_dynamic_cast(obj, TYPE_USER_CREATABLE); + if (uc) { + if (!user_creatable_complete(uc, errp)) { + if (id != NULL) { + object_unparent(obj); + } + goto error; + } + } + + object_unref(obj); + return obj; + + error: + object_unref(obj); + return NULL; +} + + +bool object_set_props(Object *obj, + Error **errp, + ...) +{ + va_list vargs; + bool ret; + + va_start(vargs, errp); + ret = object_set_propv(obj, errp, vargs); + va_end(vargs); + + return ret; +} + + +bool object_set_propv(Object *obj, + Error **errp, + va_list vargs) +{ + const char *propname; + + propname = va_arg(vargs, char *); + while (propname != NULL) { + const char *value = va_arg(vargs, char *); + + g_assert(value != NULL); + if (!object_property_parse(obj, propname, value, errp)) { + return false; + } + propname = va_arg(vargs, char *); + } + + return true; +} + + +Object *object_dynamic_cast(Object *obj, const char *typename) +{ + if (obj && object_class_dynamic_cast(object_get_class(obj), typename)) { + return obj; + } + + return NULL; +} + +Object *object_dynamic_cast_assert(Object *obj, const char *typename, + const char *file, int line, const char *func) +{ + trace_object_dynamic_cast_assert(obj ? obj->class->type->name : "(null)", + typename, file, line, func); + +#ifdef CONFIG_QOM_CAST_DEBUG + int i; + Object *inst; + + for (i = 0; obj && i < OBJECT_CLASS_CAST_CACHE; i++) { + if (qatomic_read(&obj->class->object_cast_cache[i]) == typename) { + goto out; + } + } + + inst = object_dynamic_cast(obj, typename); + + if (!inst && obj) { + fprintf(stderr, "%s:%d:%s: Object %p is not an instance of type %s\n", + file, line, func, obj, typename); + abort(); + } + + assert(obj == inst); + + if (obj && obj == inst) { + for (i = 1; i < OBJECT_CLASS_CAST_CACHE; i++) { + qatomic_set(&obj->class->object_cast_cache[i - 1], + qatomic_read(&obj->class->object_cast_cache[i])); + } + qatomic_set(&obj->class->object_cast_cache[i - 1], typename); + } + +out: +#endif + return obj; +} + +ObjectClass *object_class_dynamic_cast(ObjectClass *class, + const char *typename) +{ + ObjectClass *ret = NULL; + TypeImpl *target_type; + TypeImpl *type; + + if (!class) { + return NULL; + } + + /* A simple fast path that can trigger a lot for leaf classes. */ + type = class->type; + if (type->name == typename) { + return class; + } + + target_type = type_get_by_name(typename); + if (!target_type) { + /* target class type unknown, so fail the cast */ + return NULL; + } + + if (type->class->interfaces && + type_is_ancestor(target_type, type_interface)) { + int found = 0; + GSList *i; + + for (i = class->interfaces; i; i = i->next) { + ObjectClass *target_class = i->data; + + if (type_is_ancestor(target_class->type, target_type)) { + ret = target_class; + found++; + } + } + + /* The match was ambiguous, don't allow a cast */ + if (found > 1) { + ret = NULL; + } + } else if (type_is_ancestor(type, target_type)) { + ret = class; + } + + return ret; +} + +ObjectClass *object_class_dynamic_cast_assert(ObjectClass *class, + const char *typename, + const char *file, int line, + const char *func) +{ + ObjectClass *ret; + + trace_object_class_dynamic_cast_assert(class ? class->type->name : "(null)", + typename, file, line, func); + +#ifdef CONFIG_QOM_CAST_DEBUG + int i; + + for (i = 0; class && i < OBJECT_CLASS_CAST_CACHE; i++) { + if (qatomic_read(&class->class_cast_cache[i]) == typename) { + ret = class; + goto out; + } + } +#else + if (!class || !class->interfaces) { + return class; + } +#endif + + ret = object_class_dynamic_cast(class, typename); + if (!ret && class) { + fprintf(stderr, "%s:%d:%s: Object %p is not an instance of type %s\n", + file, line, func, class, typename); + abort(); + } + +#ifdef CONFIG_QOM_CAST_DEBUG + if (class && ret == class) { + for (i = 1; i < OBJECT_CLASS_CAST_CACHE; i++) { + qatomic_set(&class->class_cast_cache[i - 1], + qatomic_read(&class->class_cast_cache[i])); + } + qatomic_set(&class->class_cast_cache[i - 1], typename); + } +out: +#endif + return ret; +} + +const char *object_get_typename(const Object *obj) +{ + return obj->class->type->name; +} + +ObjectClass *object_get_class(Object *obj) +{ + return obj->class; +} + +bool object_class_is_abstract(ObjectClass *klass) +{ + return klass->type->abstract; +} + +const char *object_class_get_name(ObjectClass *klass) +{ + return klass->type->name; +} + +ObjectClass *object_class_by_name(const char *typename) +{ + TypeImpl *type = type_get_by_name(typename); + + if (!type) { + return NULL; + } + + type_initialize(type); + + return type->class; +} + +ObjectClass *module_object_class_by_name(const char *typename) +{ + ObjectClass *oc; + + oc = object_class_by_name(typename); +#ifdef CONFIG_MODULES + if (!oc) { + module_load_qom_one(typename); + oc = object_class_by_name(typename); + } +#endif + return oc; +} + +ObjectClass *object_class_get_parent(ObjectClass *class) +{ + TypeImpl *type = type_get_parent(class->type); + + if (!type) { + return NULL; + } + + type_initialize(type); + + return type->class; +} + +typedef struct OCFData +{ + void (*fn)(ObjectClass *klass, void *opaque); + const char *implements_type; + bool include_abstract; + void *opaque; +} OCFData; + +static void object_class_foreach_tramp(gpointer key, gpointer value, + gpointer opaque) +{ + OCFData *data = opaque; + TypeImpl *type = value; + ObjectClass *k; + + type_initialize(type); + k = type->class; + + if (!data->include_abstract && type->abstract) { + return; + } + + if (data->implements_type && + !object_class_dynamic_cast(k, data->implements_type)) { + return; + } + + data->fn(k, data->opaque); +} + +void object_class_foreach(void (*fn)(ObjectClass *klass, void *opaque), + const char *implements_type, bool include_abstract, + void *opaque) +{ + OCFData data = { fn, implements_type, include_abstract, opaque }; + + enumerating_types = true; + g_hash_table_foreach(type_table_get(), object_class_foreach_tramp, &data); + enumerating_types = false; +} + +static int do_object_child_foreach(Object *obj, + int (*fn)(Object *child, void *opaque), + void *opaque, bool recurse) +{ + GHashTableIter iter; + ObjectProperty *prop; + int ret = 0; + + g_hash_table_iter_init(&iter, obj->properties); + while (g_hash_table_iter_next(&iter, NULL, (gpointer *)&prop)) { + if (object_property_is_child(prop)) { + Object *child = prop->opaque; + + ret = fn(child, opaque); + if (ret != 0) { + break; + } + if (recurse) { + ret = do_object_child_foreach(child, fn, opaque, true); + if (ret != 0) { + break; + } + } + } + } + return ret; +} + +int object_child_foreach(Object *obj, int (*fn)(Object *child, void *opaque), + void *opaque) +{ + return do_object_child_foreach(obj, fn, opaque, false); +} + +int object_child_foreach_recursive(Object *obj, + int (*fn)(Object *child, void *opaque), + void *opaque) +{ + return do_object_child_foreach(obj, fn, opaque, true); +} + +static void object_class_get_list_tramp(ObjectClass *klass, void *opaque) +{ + GSList **list = opaque; + + *list = g_slist_prepend(*list, klass); +} + +GSList *object_class_get_list(const char *implements_type, + bool include_abstract) +{ + GSList *list = NULL; + + object_class_foreach(object_class_get_list_tramp, + implements_type, include_abstract, &list); + return list; +} + +static gint object_class_cmp(gconstpointer a, gconstpointer b) +{ + return strcasecmp(object_class_get_name((ObjectClass *)a), + object_class_get_name((ObjectClass *)b)); +} + +GSList *object_class_get_list_sorted(const char *implements_type, + bool include_abstract) +{ + return g_slist_sort(object_class_get_list(implements_type, include_abstract), + object_class_cmp); +} + +Object *object_ref(void *objptr) +{ + Object *obj = OBJECT(objptr); + if (!obj) { + return NULL; + } + qatomic_inc(&obj->ref); + return obj; +} + +void object_unref(void *objptr) +{ + Object *obj = OBJECT(objptr); + if (!obj) { + return; + } + g_assert(obj->ref > 0); + + /* parent always holds a reference to its children */ + if (qatomic_fetch_dec(&obj->ref) == 1) { + object_finalize(obj); + } +} + +ObjectProperty * +object_property_try_add(Object *obj, const char *name, const char *type, + ObjectPropertyAccessor *get, + ObjectPropertyAccessor *set, + ObjectPropertyRelease *release, + void *opaque, Error **errp) +{ + ObjectProperty *prop; + size_t name_len = strlen(name); + + if (name_len >= 3 && !memcmp(name + name_len - 3, "[*]", 4)) { + int i; + ObjectProperty *ret = NULL; + char *name_no_array = g_strdup(name); + + name_no_array[name_len - 3] = '\0'; + for (i = 0; i < INT16_MAX; ++i) { + char *full_name = g_strdup_printf("%s[%d]", name_no_array, i); + + ret = object_property_try_add(obj, full_name, type, get, set, + release, opaque, NULL); + g_free(full_name); + if (ret) { + break; + } + } + g_free(name_no_array); + assert(ret); + return ret; + } + + if (object_property_find(obj, name) != NULL) { + error_setg(errp, "attempt to add duplicate property '%s' to object (type '%s')", + name, object_get_typename(obj)); + return NULL; + } + + prop = g_malloc0(sizeof(*prop)); + + prop->name = g_strdup(name); + prop->type = g_strdup(type); + + prop->get = get; + prop->set = set; + prop->release = release; + prop->opaque = opaque; + + g_hash_table_insert(obj->properties, prop->name, prop); + return prop; +} + +ObjectProperty * +object_property_add(Object *obj, const char *name, const char *type, + ObjectPropertyAccessor *get, + ObjectPropertyAccessor *set, + ObjectPropertyRelease *release, + void *opaque) +{ + return object_property_try_add(obj, name, type, get, set, release, + opaque, &error_abort); +} + +ObjectProperty * +object_class_property_add(ObjectClass *klass, + const char *name, + const char *type, + ObjectPropertyAccessor *get, + ObjectPropertyAccessor *set, + ObjectPropertyRelease *release, + void *opaque) +{ + ObjectProperty *prop; + + assert(!object_class_property_find(klass, name)); + + prop = g_malloc0(sizeof(*prop)); + + prop->name = g_strdup(name); + prop->type = g_strdup(type); + + prop->get = get; + prop->set = set; + prop->release = release; + prop->opaque = opaque; + + g_hash_table_insert(klass->properties, prop->name, prop); + + return prop; +} + +ObjectProperty *object_property_find(Object *obj, const char *name) +{ + ObjectProperty *prop; + ObjectClass *klass = object_get_class(obj); + + prop = object_class_property_find(klass, name); + if (prop) { + return prop; + } + + return g_hash_table_lookup(obj->properties, name); +} + +ObjectProperty *object_property_find_err(Object *obj, const char *name, + Error **errp) +{ + ObjectProperty *prop = object_property_find(obj, name); + if (!prop) { + error_setg(errp, "Property '%s.%s' not found", + object_get_typename(obj), name); + } + return prop; +} + +void object_property_iter_init(ObjectPropertyIterator *iter, + Object *obj) +{ + g_hash_table_iter_init(&iter->iter, obj->properties); + iter->nextclass = object_get_class(obj); +} + +ObjectProperty *object_property_iter_next(ObjectPropertyIterator *iter) +{ + gpointer key, val; + while (!g_hash_table_iter_next(&iter->iter, &key, &val)) { + if (!iter->nextclass) { + return NULL; + } + g_hash_table_iter_init(&iter->iter, iter->nextclass->properties); + iter->nextclass = object_class_get_parent(iter->nextclass); + } + return val; +} + +void object_class_property_iter_init(ObjectPropertyIterator *iter, + ObjectClass *klass) +{ + g_hash_table_iter_init(&iter->iter, klass->properties); + iter->nextclass = object_class_get_parent(klass); +} + +ObjectProperty *object_class_property_find(ObjectClass *klass, const char *name) +{ + ObjectClass *parent_klass; + + parent_klass = object_class_get_parent(klass); + if (parent_klass) { + ObjectProperty *prop = + object_class_property_find(parent_klass, name); + if (prop) { + return prop; + } + } + + return g_hash_table_lookup(klass->properties, name); +} + +ObjectProperty *object_class_property_find_err(ObjectClass *klass, + const char *name, + Error **errp) +{ + ObjectProperty *prop = object_class_property_find(klass, name); + if (!prop) { + error_setg(errp, "Property '.%s' not found", name); + } + return prop; +} + + +void object_property_del(Object *obj, const char *name) +{ + ObjectProperty *prop = g_hash_table_lookup(obj->properties, name); + + if (prop->release) { + prop->release(obj, name, prop->opaque); + } + g_hash_table_remove(obj->properties, name); +} + +bool object_property_get(Object *obj, const char *name, Visitor *v, + Error **errp) +{ + Error *err = NULL; + ObjectProperty *prop = object_property_find_err(obj, name, errp); + + if (prop == NULL) { + return false; + } + + if (!prop->get) { + error_setg(errp, QERR_PERMISSION_DENIED); + return false; + } + prop->get(obj, v, name, prop->opaque, &err); + error_propagate(errp, err); + return !err; +} + +bool object_property_set(Object *obj, const char *name, Visitor *v, + Error **errp) +{ + ERRP_GUARD(); + ObjectProperty *prop = object_property_find_err(obj, name, errp); + + if (prop == NULL) { + return false; + } + + if (!prop->set) { + error_setg(errp, QERR_PERMISSION_DENIED); + return false; + } + prop->set(obj, v, name, prop->opaque, errp); + return !*errp; +} + +bool object_property_set_str(Object *obj, const char *name, + const char *value, Error **errp) +{ + QString *qstr = qstring_from_str(value); + bool ok = object_property_set_qobject(obj, name, QOBJECT(qstr), errp); + + qobject_unref(qstr); + return ok; +} + +char *object_property_get_str(Object *obj, const char *name, + Error **errp) +{ + QObject *ret = object_property_get_qobject(obj, name, errp); + QString *qstring; + char *retval; + + if (!ret) { + return NULL; + } + qstring = qobject_to(QString, ret); + if (!qstring) { + error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name, "string"); + retval = NULL; + } else { + retval = g_strdup(qstring_get_str(qstring)); + } + + qobject_unref(ret); + return retval; +} + +bool object_property_set_link(Object *obj, const char *name, + Object *value, Error **errp) +{ + g_autofree char *path = NULL; + + if (value) { + path = object_get_canonical_path(value); + } + return object_property_set_str(obj, name, path ?: "", errp); +} + +Object *object_property_get_link(Object *obj, const char *name, + Error **errp) +{ + char *str = object_property_get_str(obj, name, errp); + Object *target = NULL; + + if (str && *str) { + target = object_resolve_path(str, NULL); + if (!target) { + error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, + "Device '%s' not found", str); + } + } + + g_free(str); + return target; +} + +bool object_property_set_bool(Object *obj, const char *name, + bool value, Error **errp) +{ + QBool *qbool = qbool_from_bool(value); + bool ok = object_property_set_qobject(obj, name, QOBJECT(qbool), errp); + + qobject_unref(qbool); + return ok; +} + +bool object_property_get_bool(Object *obj, const char *name, + Error **errp) +{ + QObject *ret = object_property_get_qobject(obj, name, errp); + QBool *qbool; + bool retval; + + if (!ret) { + return false; + } + qbool = qobject_to(QBool, ret); + if (!qbool) { + error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name, "boolean"); + retval = false; + } else { + retval = qbool_get_bool(qbool); + } + + qobject_unref(ret); + return retval; +} + +bool object_property_set_int(Object *obj, const char *name, + int64_t value, Error **errp) +{ + QNum *qnum = qnum_from_int(value); + bool ok = object_property_set_qobject(obj, name, QOBJECT(qnum), errp); + + qobject_unref(qnum); + return ok; +} + +int64_t object_property_get_int(Object *obj, const char *name, + Error **errp) +{ + QObject *ret = object_property_get_qobject(obj, name, errp); + QNum *qnum; + int64_t retval; + + if (!ret) { + return -1; + } + + qnum = qobject_to(QNum, ret); + if (!qnum || !qnum_get_try_int(qnum, &retval)) { + error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name, "int"); + retval = -1; + } + + qobject_unref(ret); + return retval; +} + +static void object_property_init_defval(Object *obj, ObjectProperty *prop) +{ + Visitor *v = qobject_input_visitor_new(prop->defval); + + assert(prop->set != NULL); + prop->set(obj, v, prop->name, prop->opaque, &error_abort); + + visit_free(v); +} + +static void object_property_set_default(ObjectProperty *prop, QObject *defval) +{ + assert(!prop->defval); + assert(!prop->init); + + prop->defval = defval; + prop->init = object_property_init_defval; +} + +void object_property_set_default_bool(ObjectProperty *prop, bool value) +{ + object_property_set_default(prop, QOBJECT(qbool_from_bool(value))); +} + +void object_property_set_default_str(ObjectProperty *prop, const char *value) +{ + object_property_set_default(prop, QOBJECT(qstring_from_str(value))); +} + +void object_property_set_default_int(ObjectProperty *prop, int64_t value) +{ + object_property_set_default(prop, QOBJECT(qnum_from_int(value))); +} + +void object_property_set_default_uint(ObjectProperty *prop, uint64_t value) +{ + object_property_set_default(prop, QOBJECT(qnum_from_uint(value))); +} + +bool object_property_set_uint(Object *obj, const char *name, + uint64_t value, Error **errp) +{ + QNum *qnum = qnum_from_uint(value); + bool ok = object_property_set_qobject(obj, name, QOBJECT(qnum), errp); + + qobject_unref(qnum); + return ok; +} + +uint64_t object_property_get_uint(Object *obj, const char *name, + Error **errp) +{ + QObject *ret = object_property_get_qobject(obj, name, errp); + QNum *qnum; + uint64_t retval; + + if (!ret) { + return 0; + } + qnum = qobject_to(QNum, ret); + if (!qnum || !qnum_get_try_uint(qnum, &retval)) { + error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name, "uint"); + retval = 0; + } + + qobject_unref(ret); + return retval; +} + +typedef struct EnumProperty { + const QEnumLookup *lookup; + int (*get)(Object *, Error **); + void (*set)(Object *, int, Error **); +} EnumProperty; + +int object_property_get_enum(Object *obj, const char *name, + const char *typename, Error **errp) +{ + char *str; + int ret; + ObjectProperty *prop = object_property_find_err(obj, name, errp); + EnumProperty *enumprop; + + if (prop == NULL) { + return -1; + } + + if (!g_str_equal(prop->type, typename)) { + error_setg(errp, "Property %s on %s is not '%s' enum type", + name, object_class_get_name( + object_get_class(obj)), typename); + return -1; + } + + enumprop = prop->opaque; + + str = object_property_get_str(obj, name, errp); + if (!str) { + return -1; + } + + ret = qapi_enum_parse(enumprop->lookup, str, -1, errp); + g_free(str); + + return ret; +} + +bool object_property_parse(Object *obj, const char *name, + const char *string, Error **errp) +{ + Visitor *v = string_input_visitor_new(string); + bool ok = object_property_set(obj, name, v, errp); + + visit_free(v); + return ok; +} + +char *object_property_print(Object *obj, const char *name, bool human, + Error **errp) +{ + Visitor *v; + char *string = NULL; + + v = string_output_visitor_new(human, &string); + if (!object_property_get(obj, name, v, errp)) { + goto out; + } + + visit_complete(v, &string); + +out: + visit_free(v); + return string; +} + +const char *object_property_get_type(Object *obj, const char *name, Error **errp) +{ + ObjectProperty *prop = object_property_find_err(obj, name, errp); + if (prop == NULL) { + return NULL; + } + + return prop->type; +} + +Object *object_get_root(void) +{ + static Object *root; + + if (!root) { + root = object_new("container"); + } + + return root; +} + +Object *object_get_objects_root(void) +{ + return container_get(object_get_root(), "/objects"); +} + +Object *object_get_internal_root(void) +{ + static Object *internal_root; + + if (!internal_root) { + internal_root = object_new("container"); + } + + return internal_root; +} + +static void object_get_child_property(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + Object *child = opaque; + char *path; + + path = object_get_canonical_path(child); + visit_type_str(v, name, &path, errp); + g_free(path); +} + +static Object *object_resolve_child_property(Object *parent, void *opaque, + const char *part) +{ + return opaque; +} + +static void object_finalize_child_property(Object *obj, const char *name, + void *opaque) +{ + Object *child = opaque; + + if (child->class->unparent) { + (child->class->unparent)(child); + } + child->parent = NULL; + object_unref(child); +} + +ObjectProperty * +object_property_try_add_child(Object *obj, const char *name, + Object *child, Error **errp) +{ + g_autofree char *type = NULL; + ObjectProperty *op; + + assert(!child->parent); + + type = g_strdup_printf("child<%s>", object_get_typename(child)); + + op = object_property_try_add(obj, name, type, object_get_child_property, + NULL, object_finalize_child_property, + child, errp); + if (!op) { + return NULL; + } + op->resolve = object_resolve_child_property; + object_ref(child); + child->parent = obj; + return op; +} + +ObjectProperty * +object_property_add_child(Object *obj, const char *name, + Object *child) +{ + return object_property_try_add_child(obj, name, child, &error_abort); +} + +void object_property_allow_set_link(const Object *obj, const char *name, + Object *val, Error **errp) +{ + /* Allow the link to be set, always */ +} + +typedef struct { + union { + Object **targetp; + Object *target; /* if OBJ_PROP_LINK_DIRECT, when holding the pointer */ + ptrdiff_t offset; /* if OBJ_PROP_LINK_CLASS */ + }; + void (*check)(const Object *, const char *, Object *, Error **); + ObjectPropertyLinkFlags flags; +} LinkProperty; + +static Object ** +object_link_get_targetp(Object *obj, LinkProperty *lprop) +{ + if (lprop->flags & OBJ_PROP_LINK_DIRECT) { + return &lprop->target; + } else if (lprop->flags & OBJ_PROP_LINK_CLASS) { + return (void *)obj + lprop->offset; + } else { + return lprop->targetp; + } +} + +static void object_get_link_property(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + LinkProperty *lprop = opaque; + Object **targetp = object_link_get_targetp(obj, lprop); + char *path; + + if (*targetp) { + path = object_get_canonical_path(*targetp); + visit_type_str(v, name, &path, errp); + g_free(path); + } else { + path = (char *)""; + visit_type_str(v, name, &path, errp); + } +} + +/* + * object_resolve_link: + * + * Lookup an object and ensure its type matches the link property type. This + * is similar to object_resolve_path() except type verification against the + * link property is performed. + * + * Returns: The matched object or NULL on path lookup failures. + */ +static Object *object_resolve_link(Object *obj, const char *name, + const char *path, Error **errp) +{ + const char *type; + char *target_type; + bool ambiguous = false; + Object *target; + + /* Go from link<FOO> to FOO. */ + type = object_property_get_type(obj, name, NULL); + target_type = g_strndup(&type[5], strlen(type) - 6); + target = object_resolve_path_type(path, target_type, &ambiguous); + + if (ambiguous) { + error_setg(errp, "Path '%s' does not uniquely identify an object", + path); + } else if (!target) { + target = object_resolve_path(path, &ambiguous); + if (target || ambiguous) { + error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name, target_type); + } else { + error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, + "Device '%s' not found", path); + } + target = NULL; + } + g_free(target_type); + + return target; +} + +static void object_set_link_property(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + Error *local_err = NULL; + LinkProperty *prop = opaque; + Object **targetp = object_link_get_targetp(obj, prop); + Object *old_target = *targetp; + Object *new_target; + char *path = NULL; + + if (!visit_type_str(v, name, &path, errp)) { + return; + } + + if (*path) { + new_target = object_resolve_link(obj, name, path, errp); + if (!new_target) { + g_free(path); + return; + } + } else { + new_target = NULL; + } + + g_free(path); + + prop->check(obj, name, new_target, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + *targetp = new_target; + if (prop->flags & OBJ_PROP_LINK_STRONG) { + object_ref(new_target); + object_unref(old_target); + } +} + +static Object *object_resolve_link_property(Object *parent, void *opaque, + const char *part) +{ + LinkProperty *lprop = opaque; + + return *object_link_get_targetp(parent, lprop); +} + +static void object_release_link_property(Object *obj, const char *name, + void *opaque) +{ + LinkProperty *prop = opaque; + Object **targetp = object_link_get_targetp(obj, prop); + + if ((prop->flags & OBJ_PROP_LINK_STRONG) && *targetp) { + object_unref(*targetp); + } + if (!(prop->flags & OBJ_PROP_LINK_CLASS)) { + g_free(prop); + } +} + +static ObjectProperty * +object_add_link_prop(Object *obj, const char *name, + const char *type, void *ptr, + void (*check)(const Object *, const char *, + Object *, Error **), + ObjectPropertyLinkFlags flags) +{ + LinkProperty *prop = g_malloc(sizeof(*prop)); + g_autofree char *full_type = NULL; + ObjectProperty *op; + + if (flags & OBJ_PROP_LINK_DIRECT) { + prop->target = ptr; + } else { + prop->targetp = ptr; + } + prop->check = check; + prop->flags = flags; + + full_type = g_strdup_printf("link<%s>", type); + + op = object_property_add(obj, name, full_type, + object_get_link_property, + check ? object_set_link_property : NULL, + object_release_link_property, + prop); + op->resolve = object_resolve_link_property; + return op; +} + +ObjectProperty * +object_property_add_link(Object *obj, const char *name, + const char *type, Object **targetp, + void (*check)(const Object *, const char *, + Object *, Error **), + ObjectPropertyLinkFlags flags) +{ + return object_add_link_prop(obj, name, type, targetp, check, flags); +} + +ObjectProperty * +object_class_property_add_link(ObjectClass *oc, + const char *name, + const char *type, ptrdiff_t offset, + void (*check)(const Object *obj, const char *name, + Object *val, Error **errp), + ObjectPropertyLinkFlags flags) +{ + LinkProperty *prop = g_new0(LinkProperty, 1); + char *full_type; + ObjectProperty *op; + + prop->offset = offset; + prop->check = check; + prop->flags = flags | OBJ_PROP_LINK_CLASS; + + full_type = g_strdup_printf("link<%s>", type); + + op = object_class_property_add(oc, name, full_type, + object_get_link_property, + check ? object_set_link_property : NULL, + object_release_link_property, + prop); + + op->resolve = object_resolve_link_property; + + g_free(full_type); + return op; +} + +ObjectProperty * +object_property_add_const_link(Object *obj, const char *name, + Object *target) +{ + return object_add_link_prop(obj, name, + object_get_typename(target), target, + NULL, OBJ_PROP_LINK_DIRECT); +} + +const char *object_get_canonical_path_component(const Object *obj) +{ + ObjectProperty *prop = NULL; + GHashTableIter iter; + + if (obj->parent == NULL) { + return NULL; + } + + g_hash_table_iter_init(&iter, obj->parent->properties); + while (g_hash_table_iter_next(&iter, NULL, (gpointer *)&prop)) { + if (!object_property_is_child(prop)) { + continue; + } + + if (prop->opaque == obj) { + return prop->name; + } + } + + /* obj had a parent but was not a child, should never happen */ + g_assert_not_reached(); + return NULL; +} + +char *object_get_canonical_path(const Object *obj) +{ + Object *root = object_get_root(); + char *newpath, *path = NULL; + + if (obj == root) { + return g_strdup("/"); + } + + do { + const char *component = object_get_canonical_path_component(obj); + + if (!component) { + /* A canonical path must be complete, so discard what was + * collected so far. + */ + g_free(path); + return NULL; + } + + newpath = g_strdup_printf("/%s%s", component, path ? path : ""); + g_free(path); + path = newpath; + obj = obj->parent; + } while (obj != root); + + return path; +} + +Object *object_resolve_path_component(Object *parent, const char *part) +{ + ObjectProperty *prop = object_property_find(parent, part); + if (prop == NULL) { + return NULL; + } + + if (prop->resolve) { + return prop->resolve(parent, prop->opaque, part); + } else { + return NULL; + } +} + +static Object *object_resolve_abs_path(Object *parent, + char **parts, + const char *typename) +{ + Object *child; + + if (*parts == NULL) { + return object_dynamic_cast(parent, typename); + } + + if (strcmp(*parts, "") == 0) { + return object_resolve_abs_path(parent, parts + 1, typename); + } + + child = object_resolve_path_component(parent, *parts); + if (!child) { + return NULL; + } + + return object_resolve_abs_path(child, parts + 1, typename); +} + +static Object *object_resolve_partial_path(Object *parent, + char **parts, + const char *typename, + bool *ambiguous) +{ + Object *obj; + GHashTableIter iter; + ObjectProperty *prop; + + obj = object_resolve_abs_path(parent, parts, typename); + + g_hash_table_iter_init(&iter, parent->properties); + while (g_hash_table_iter_next(&iter, NULL, (gpointer *)&prop)) { + Object *found; + + if (!object_property_is_child(prop)) { + continue; + } + + found = object_resolve_partial_path(prop->opaque, parts, + typename, ambiguous); + if (found) { + if (obj) { + *ambiguous = true; + return NULL; + } + obj = found; + } + + if (*ambiguous) { + return NULL; + } + } + + return obj; +} + +Object *object_resolve_path_type(const char *path, const char *typename, + bool *ambiguousp) +{ + Object *obj; + char **parts; + + parts = g_strsplit(path, "/", 0); + assert(parts); + + if (parts[0] == NULL || strcmp(parts[0], "") != 0) { + bool ambiguous = false; + obj = object_resolve_partial_path(object_get_root(), parts, + typename, &ambiguous); + if (ambiguousp) { + *ambiguousp = ambiguous; + } + } else { + obj = object_resolve_abs_path(object_get_root(), parts + 1, typename); + } + + g_strfreev(parts); + + return obj; +} + +Object *object_resolve_path(const char *path, bool *ambiguous) +{ + return object_resolve_path_type(path, TYPE_OBJECT, ambiguous); +} + +Object *object_resolve_path_at(Object *parent, const char *path) +{ + g_auto(GStrv) parts = g_strsplit(path, "/", 0); + + if (*path == '/') { + return object_resolve_abs_path(object_get_root(), parts + 1, + TYPE_OBJECT); + } + return object_resolve_abs_path(parent, parts, TYPE_OBJECT); +} + +typedef struct StringProperty +{ + char *(*get)(Object *, Error **); + void (*set)(Object *, const char *, Error **); +} StringProperty; + +static void property_get_str(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + StringProperty *prop = opaque; + char *value; + Error *err = NULL; + + value = prop->get(obj, &err); + if (err) { + error_propagate(errp, err); + return; + } + + visit_type_str(v, name, &value, errp); + g_free(value); +} + +static void property_set_str(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + StringProperty *prop = opaque; + char *value; + + if (!visit_type_str(v, name, &value, errp)) { + return; + } + + prop->set(obj, value, errp); + g_free(value); +} + +static void property_release_data(Object *obj, const char *name, + void *opaque) +{ + g_free(opaque); +} + +ObjectProperty * +object_property_add_str(Object *obj, const char *name, + char *(*get)(Object *, Error **), + void (*set)(Object *, const char *, Error **)) +{ + StringProperty *prop = g_malloc0(sizeof(*prop)); + + prop->get = get; + prop->set = set; + + return object_property_add(obj, name, "string", + get ? property_get_str : NULL, + set ? property_set_str : NULL, + property_release_data, + prop); +} + +ObjectProperty * +object_class_property_add_str(ObjectClass *klass, const char *name, + char *(*get)(Object *, Error **), + void (*set)(Object *, const char *, + Error **)) +{ + StringProperty *prop = g_malloc0(sizeof(*prop)); + + prop->get = get; + prop->set = set; + + return object_class_property_add(klass, name, "string", + get ? property_get_str : NULL, + set ? property_set_str : NULL, + NULL, + prop); +} + +typedef struct BoolProperty +{ + bool (*get)(Object *, Error **); + void (*set)(Object *, bool, Error **); +} BoolProperty; + +static void property_get_bool(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + BoolProperty *prop = opaque; + bool value; + Error *err = NULL; + + value = prop->get(obj, &err); + if (err) { + error_propagate(errp, err); + return; + } + + visit_type_bool(v, name, &value, errp); +} + +static void property_set_bool(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + BoolProperty *prop = opaque; + bool value; + + if (!visit_type_bool(v, name, &value, errp)) { + return; + } + + prop->set(obj, value, errp); +} + +ObjectProperty * +object_property_add_bool(Object *obj, const char *name, + bool (*get)(Object *, Error **), + void (*set)(Object *, bool, Error **)) +{ + BoolProperty *prop = g_malloc0(sizeof(*prop)); + + prop->get = get; + prop->set = set; + + return object_property_add(obj, name, "bool", + get ? property_get_bool : NULL, + set ? property_set_bool : NULL, + property_release_data, + prop); +} + +ObjectProperty * +object_class_property_add_bool(ObjectClass *klass, const char *name, + bool (*get)(Object *, Error **), + void (*set)(Object *, bool, Error **)) +{ + BoolProperty *prop = g_malloc0(sizeof(*prop)); + + prop->get = get; + prop->set = set; + + return object_class_property_add(klass, name, "bool", + get ? property_get_bool : NULL, + set ? property_set_bool : NULL, + NULL, + prop); +} + +static void property_get_enum(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + EnumProperty *prop = opaque; + int value; + Error *err = NULL; + + value = prop->get(obj, &err); + if (err) { + error_propagate(errp, err); + return; + } + + visit_type_enum(v, name, &value, prop->lookup, errp); +} + +static void property_set_enum(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + EnumProperty *prop = opaque; + int value; + + if (!visit_type_enum(v, name, &value, prop->lookup, errp)) { + return; + } + prop->set(obj, value, errp); +} + +ObjectProperty * +object_property_add_enum(Object *obj, const char *name, + const char *typename, + const QEnumLookup *lookup, + int (*get)(Object *, Error **), + void (*set)(Object *, int, Error **)) +{ + EnumProperty *prop = g_malloc(sizeof(*prop)); + + prop->lookup = lookup; + prop->get = get; + prop->set = set; + + return object_property_add(obj, name, typename, + get ? property_get_enum : NULL, + set ? property_set_enum : NULL, + property_release_data, + prop); +} + +ObjectProperty * +object_class_property_add_enum(ObjectClass *klass, const char *name, + const char *typename, + const QEnumLookup *lookup, + int (*get)(Object *, Error **), + void (*set)(Object *, int, Error **)) +{ + EnumProperty *prop = g_malloc(sizeof(*prop)); + + prop->lookup = lookup; + prop->get = get; + prop->set = set; + + return object_class_property_add(klass, name, typename, + get ? property_get_enum : NULL, + set ? property_set_enum : NULL, + NULL, + prop); +} + +typedef struct TMProperty { + void (*get)(Object *, struct tm *, Error **); +} TMProperty; + +static void property_get_tm(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + TMProperty *prop = opaque; + Error *err = NULL; + struct tm value; + + prop->get(obj, &value, &err); + if (err) { + error_propagate(errp, err); + return; + } + + if (!visit_start_struct(v, name, NULL, 0, errp)) { + return; + } + if (!visit_type_int32(v, "tm_year", &value.tm_year, errp)) { + goto out_end; + } + if (!visit_type_int32(v, "tm_mon", &value.tm_mon, errp)) { + goto out_end; + } + if (!visit_type_int32(v, "tm_mday", &value.tm_mday, errp)) { + goto out_end; + } + if (!visit_type_int32(v, "tm_hour", &value.tm_hour, errp)) { + goto out_end; + } + if (!visit_type_int32(v, "tm_min", &value.tm_min, errp)) { + goto out_end; + } + if (!visit_type_int32(v, "tm_sec", &value.tm_sec, errp)) { + goto out_end; + } + visit_check_struct(v, errp); +out_end: + visit_end_struct(v, NULL); +} + +ObjectProperty * +object_property_add_tm(Object *obj, const char *name, + void (*get)(Object *, struct tm *, Error **)) +{ + TMProperty *prop = g_malloc0(sizeof(*prop)); + + prop->get = get; + + return object_property_add(obj, name, "struct tm", + get ? property_get_tm : NULL, NULL, + property_release_data, + prop); +} + +ObjectProperty * +object_class_property_add_tm(ObjectClass *klass, const char *name, + void (*get)(Object *, struct tm *, Error **)) +{ + TMProperty *prop = g_malloc0(sizeof(*prop)); + + prop->get = get; + + return object_class_property_add(klass, name, "struct tm", + get ? property_get_tm : NULL, + NULL, NULL, prop); +} + +static char *object_get_type(Object *obj, Error **errp) +{ + return g_strdup(object_get_typename(obj)); +} + +static void property_get_uint8_ptr(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + uint8_t value = *(uint8_t *)opaque; + visit_type_uint8(v, name, &value, errp); +} + +static void property_set_uint8_ptr(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + uint8_t *field = opaque; + uint8_t value; + + if (!visit_type_uint8(v, name, &value, errp)) { + return; + } + + *field = value; +} + +static void property_get_uint16_ptr(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + uint16_t value = *(uint16_t *)opaque; + visit_type_uint16(v, name, &value, errp); +} + +static void property_set_uint16_ptr(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + uint16_t *field = opaque; + uint16_t value; + + if (!visit_type_uint16(v, name, &value, errp)) { + return; + } + + *field = value; +} + +static void property_get_uint32_ptr(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + uint32_t value = *(uint32_t *)opaque; + visit_type_uint32(v, name, &value, errp); +} + +static void property_set_uint32_ptr(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + uint32_t *field = opaque; + uint32_t value; + + if (!visit_type_uint32(v, name, &value, errp)) { + return; + } + + *field = value; +} + +static void property_get_uint64_ptr(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + uint64_t value = *(uint64_t *)opaque; + visit_type_uint64(v, name, &value, errp); +} + +static void property_set_uint64_ptr(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + uint64_t *field = opaque; + uint64_t value; + + if (!visit_type_uint64(v, name, &value, errp)) { + return; + } + + *field = value; +} + +ObjectProperty * +object_property_add_uint8_ptr(Object *obj, const char *name, + const uint8_t *v, + ObjectPropertyFlags flags) +{ + ObjectPropertyAccessor *getter = NULL; + ObjectPropertyAccessor *setter = NULL; + + if ((flags & OBJ_PROP_FLAG_READ) == OBJ_PROP_FLAG_READ) { + getter = property_get_uint8_ptr; + } + + if ((flags & OBJ_PROP_FLAG_WRITE) == OBJ_PROP_FLAG_WRITE) { + setter = property_set_uint8_ptr; + } + + return object_property_add(obj, name, "uint8", + getter, setter, NULL, (void *)v); +} + +ObjectProperty * +object_class_property_add_uint8_ptr(ObjectClass *klass, const char *name, + const uint8_t *v, + ObjectPropertyFlags flags) +{ + ObjectPropertyAccessor *getter = NULL; + ObjectPropertyAccessor *setter = NULL; + + if ((flags & OBJ_PROP_FLAG_READ) == OBJ_PROP_FLAG_READ) { + getter = property_get_uint8_ptr; + } + + if ((flags & OBJ_PROP_FLAG_WRITE) == OBJ_PROP_FLAG_WRITE) { + setter = property_set_uint8_ptr; + } + + return object_class_property_add(klass, name, "uint8", + getter, setter, NULL, (void *)v); +} + +ObjectProperty * +object_property_add_uint16_ptr(Object *obj, const char *name, + const uint16_t *v, + ObjectPropertyFlags flags) +{ + ObjectPropertyAccessor *getter = NULL; + ObjectPropertyAccessor *setter = NULL; + + if ((flags & OBJ_PROP_FLAG_READ) == OBJ_PROP_FLAG_READ) { + getter = property_get_uint16_ptr; + } + + if ((flags & OBJ_PROP_FLAG_WRITE) == OBJ_PROP_FLAG_WRITE) { + setter = property_set_uint16_ptr; + } + + return object_property_add(obj, name, "uint16", + getter, setter, NULL, (void *)v); +} + +ObjectProperty * +object_class_property_add_uint16_ptr(ObjectClass *klass, const char *name, + const uint16_t *v, + ObjectPropertyFlags flags) +{ + ObjectPropertyAccessor *getter = NULL; + ObjectPropertyAccessor *setter = NULL; + + if ((flags & OBJ_PROP_FLAG_READ) == OBJ_PROP_FLAG_READ) { + getter = property_get_uint16_ptr; + } + + if ((flags & OBJ_PROP_FLAG_WRITE) == OBJ_PROP_FLAG_WRITE) { + setter = property_set_uint16_ptr; + } + + return object_class_property_add(klass, name, "uint16", + getter, setter, NULL, (void *)v); +} + +ObjectProperty * +object_property_add_uint32_ptr(Object *obj, const char *name, + const uint32_t *v, + ObjectPropertyFlags flags) +{ + ObjectPropertyAccessor *getter = NULL; + ObjectPropertyAccessor *setter = NULL; + + if ((flags & OBJ_PROP_FLAG_READ) == OBJ_PROP_FLAG_READ) { + getter = property_get_uint32_ptr; + } + + if ((flags & OBJ_PROP_FLAG_WRITE) == OBJ_PROP_FLAG_WRITE) { + setter = property_set_uint32_ptr; + } + + return object_property_add(obj, name, "uint32", + getter, setter, NULL, (void *)v); +} + +ObjectProperty * +object_class_property_add_uint32_ptr(ObjectClass *klass, const char *name, + const uint32_t *v, + ObjectPropertyFlags flags) +{ + ObjectPropertyAccessor *getter = NULL; + ObjectPropertyAccessor *setter = NULL; + + if ((flags & OBJ_PROP_FLAG_READ) == OBJ_PROP_FLAG_READ) { + getter = property_get_uint32_ptr; + } + + if ((flags & OBJ_PROP_FLAG_WRITE) == OBJ_PROP_FLAG_WRITE) { + setter = property_set_uint32_ptr; + } + + return object_class_property_add(klass, name, "uint32", + getter, setter, NULL, (void *)v); +} + +ObjectProperty * +object_property_add_uint64_ptr(Object *obj, const char *name, + const uint64_t *v, + ObjectPropertyFlags flags) +{ + ObjectPropertyAccessor *getter = NULL; + ObjectPropertyAccessor *setter = NULL; + + if ((flags & OBJ_PROP_FLAG_READ) == OBJ_PROP_FLAG_READ) { + getter = property_get_uint64_ptr; + } + + if ((flags & OBJ_PROP_FLAG_WRITE) == OBJ_PROP_FLAG_WRITE) { + setter = property_set_uint64_ptr; + } + + return object_property_add(obj, name, "uint64", + getter, setter, NULL, (void *)v); +} + +ObjectProperty * +object_class_property_add_uint64_ptr(ObjectClass *klass, const char *name, + const uint64_t *v, + ObjectPropertyFlags flags) +{ + ObjectPropertyAccessor *getter = NULL; + ObjectPropertyAccessor *setter = NULL; + + if ((flags & OBJ_PROP_FLAG_READ) == OBJ_PROP_FLAG_READ) { + getter = property_get_uint64_ptr; + } + + if ((flags & OBJ_PROP_FLAG_WRITE) == OBJ_PROP_FLAG_WRITE) { + setter = property_set_uint64_ptr; + } + + return object_class_property_add(klass, name, "uint64", + getter, setter, NULL, (void *)v); +} + +typedef struct { + Object *target_obj; + char *target_name; +} AliasProperty; + +static void property_get_alias(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + AliasProperty *prop = opaque; + Visitor *alias_v = visitor_forward_field(v, prop->target_name, name); + + object_property_get(prop->target_obj, prop->target_name, alias_v, errp); + visit_free(alias_v); +} + +static void property_set_alias(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + AliasProperty *prop = opaque; + Visitor *alias_v = visitor_forward_field(v, prop->target_name, name); + + object_property_set(prop->target_obj, prop->target_name, alias_v, errp); + visit_free(alias_v); +} + +static Object *property_resolve_alias(Object *obj, void *opaque, + const char *part) +{ + AliasProperty *prop = opaque; + + return object_resolve_path_component(prop->target_obj, prop->target_name); +} + +static void property_release_alias(Object *obj, const char *name, void *opaque) +{ + AliasProperty *prop = opaque; + + g_free(prop->target_name); + g_free(prop); +} + +ObjectProperty * +object_property_add_alias(Object *obj, const char *name, + Object *target_obj, const char *target_name) +{ + AliasProperty *prop; + ObjectProperty *op; + ObjectProperty *target_prop; + g_autofree char *prop_type = NULL; + + target_prop = object_property_find_err(target_obj, target_name, + &error_abort); + + if (object_property_is_child(target_prop)) { + prop_type = g_strdup_printf("link%s", + target_prop->type + strlen("child")); + } else { + prop_type = g_strdup(target_prop->type); + } + + prop = g_malloc(sizeof(*prop)); + prop->target_obj = target_obj; + prop->target_name = g_strdup(target_name); + + op = object_property_add(obj, name, prop_type, + property_get_alias, + property_set_alias, + property_release_alias, + prop); + op->resolve = property_resolve_alias; + if (target_prop->defval) { + op->defval = qobject_ref(target_prop->defval); + } + + object_property_set_description(obj, op->name, + target_prop->description); + return op; +} + +void object_property_set_description(Object *obj, const char *name, + const char *description) +{ + ObjectProperty *op; + + op = object_property_find_err(obj, name, &error_abort); + g_free(op->description); + op->description = g_strdup(description); +} + +void object_class_property_set_description(ObjectClass *klass, + const char *name, + const char *description) +{ + ObjectProperty *op; + + op = g_hash_table_lookup(klass->properties, name); + g_free(op->description); + op->description = g_strdup(description); +} + +static void object_class_init(ObjectClass *klass, void *data) +{ + object_class_property_add_str(klass, "type", object_get_type, + NULL); +} + +static void register_types(void) +{ + static TypeInfo interface_info = { + .name = TYPE_INTERFACE, + .class_size = sizeof(InterfaceClass), + .abstract = true, + }; + + static TypeInfo object_info = { + .name = TYPE_OBJECT, + .instance_size = sizeof(Object), + .class_init = object_class_init, + .abstract = true, + }; + + type_interface = type_register_internal(&interface_info); + type_register_internal(&object_info); +} + +type_init(register_types) diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c new file mode 100644 index 000000000..3b61c195c --- /dev/null +++ b/qom/object_interfaces.c @@ -0,0 +1,367 @@ +#include "qemu/osdep.h" + +#include "qemu/cutils.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-qom.h" +#include "qapi/qapi-visit-qom.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qerror.h" +#include "qapi/qmp/qjson.h" +#include "qapi/qobject-input-visitor.h" +#include "qapi/qobject-output-visitor.h" +#include "qom/object_interfaces.h" +#include "qemu/help_option.h" +#include "qemu/id.h" +#include "qemu/module.h" +#include "qemu/option.h" +#include "qemu/qemu-print.h" +#include "qapi/opts-visitor.h" +#include "qemu/config-file.h" + +bool user_creatable_complete(UserCreatable *uc, Error **errp) +{ + UserCreatableClass *ucc = USER_CREATABLE_GET_CLASS(uc); + Error *err = NULL; + + if (ucc->complete) { + ucc->complete(uc, &err); + error_propagate(errp, err); + } + return !err; +} + +bool user_creatable_can_be_deleted(UserCreatable *uc) +{ + + UserCreatableClass *ucc = USER_CREATABLE_GET_CLASS(uc); + + if (ucc->can_be_deleted) { + return ucc->can_be_deleted(uc); + } else { + return true; + } +} + +static void object_set_properties_from_qdict(Object *obj, const QDict *qdict, + Visitor *v, Error **errp) +{ + const QDictEntry *e; + + if (!visit_start_struct(v, NULL, NULL, 0, errp)) { + return; + } + for (e = qdict_first(qdict); e; e = qdict_next(qdict, e)) { + if (!object_property_set(obj, e->key, v, errp)) { + goto out; + } + } + visit_check_struct(v, errp); +out: + visit_end_struct(v, NULL); +} + +void object_set_properties_from_keyval(Object *obj, const QDict *qdict, + bool from_json, Error **errp) +{ + Visitor *v; + if (from_json) { + v = qobject_input_visitor_new(QOBJECT(qdict)); + } else { + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); + } + object_set_properties_from_qdict(obj, qdict, v, errp); + visit_free(v); +} + +Object *user_creatable_add_type(const char *type, const char *id, + const QDict *qdict, + Visitor *v, Error **errp) +{ + ERRP_GUARD(); + Object *obj; + ObjectClass *klass; + Error *local_err = NULL; + + if (id != NULL && !id_wellformed(id)) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "id", "an identifier"); + error_append_hint(errp, "Identifiers consist of letters, digits, " + "'-', '.', '_', starting with a letter.\n"); + return NULL; + } + + klass = object_class_by_name(type); + if (!klass) { + error_setg(errp, "invalid object type: %s", type); + return NULL; + } + + if (!object_class_dynamic_cast(klass, TYPE_USER_CREATABLE)) { + error_setg(errp, "object type '%s' isn't supported by object-add", + type); + return NULL; + } + + if (object_class_is_abstract(klass)) { + error_setg(errp, "object type '%s' is abstract", type); + return NULL; + } + + assert(qdict); + obj = object_new(type); + object_set_properties_from_qdict(obj, qdict, v, &local_err); + if (local_err) { + goto out; + } + + if (id != NULL) { + object_property_try_add_child(object_get_objects_root(), + id, obj, &local_err); + if (local_err) { + goto out; + } + } + + if (!user_creatable_complete(USER_CREATABLE(obj), &local_err)) { + if (id != NULL) { + object_property_del(object_get_objects_root(), id); + } + goto out; + } +out: + if (local_err) { + error_propagate(errp, local_err); + object_unref(obj); + return NULL; + } + return obj; +} + +void user_creatable_add_qapi(ObjectOptions *options, Error **errp) +{ + Visitor *v; + QObject *qobj; + QDict *props; + Object *obj; + + v = qobject_output_visitor_new(&qobj); + visit_type_ObjectOptions(v, NULL, &options, &error_abort); + visit_complete(v, &qobj); + visit_free(v); + + props = qobject_to(QDict, qobj); + qdict_del(props, "qom-type"); + qdict_del(props, "id"); + + v = qobject_input_visitor_new(QOBJECT(props)); + obj = user_creatable_add_type(ObjectType_str(options->qom_type), + options->id, props, v, errp); + object_unref(obj); + qobject_unref(qobj); + visit_free(v); +} + +char *object_property_help(const char *name, const char *type, + QObject *defval, const char *description) +{ + GString *str = g_string_new(NULL); + + g_string_append_printf(str, " %s=<%s>", name, type); + if (description || defval) { + if (str->len < 24) { + g_string_append_printf(str, "%*s", 24 - (int)str->len, ""); + } + g_string_append(str, " - "); + } + if (description) { + g_string_append(str, description); + } + if (defval) { + g_autofree char *def_json = g_string_free(qobject_to_json(defval), + false); + g_string_append_printf(str, " (default: %s)", def_json); + } + + return g_string_free(str, false); +} + +static void user_creatable_print_types(void) +{ + GSList *l, *list; + + qemu_printf("List of user creatable objects:\n"); + list = object_class_get_list_sorted(TYPE_USER_CREATABLE, false); + for (l = list; l != NULL; l = l->next) { + ObjectClass *oc = OBJECT_CLASS(l->data); + qemu_printf(" %s\n", object_class_get_name(oc)); + } + g_slist_free(list); +} + +bool type_print_class_properties(const char *type) +{ + ObjectClass *klass; + ObjectPropertyIterator iter; + ObjectProperty *prop; + GPtrArray *array; + int i; + + klass = object_class_by_name(type); + if (!klass) { + return false; + } + + array = g_ptr_array_new(); + object_class_property_iter_init(&iter, klass); + while ((prop = object_property_iter_next(&iter))) { + if (!prop->set) { + continue; + } + + g_ptr_array_add(array, + object_property_help(prop->name, prop->type, + prop->defval, prop->description)); + } + g_ptr_array_sort(array, (GCompareFunc)qemu_pstrcmp0); + if (array->len > 0) { + qemu_printf("%s options:\n", type); + } else { + qemu_printf("There are no options for %s.\n", type); + } + for (i = 0; i < array->len; i++) { + qemu_printf("%s\n", (char *)array->pdata[i]); + } + g_ptr_array_set_free_func(array, g_free); + g_ptr_array_free(array, true); + return true; +} + +bool user_creatable_print_help(const char *type, QemuOpts *opts) +{ + if (is_help_option(type)) { + user_creatable_print_types(); + return true; + } + + if (qemu_opt_has_help_opt(opts)) { + return type_print_class_properties(type); + } + + return false; +} + +static void user_creatable_print_help_from_qdict(QDict *args) +{ + const char *type = qdict_get_try_str(args, "qom-type"); + + if (!type || !type_print_class_properties(type)) { + user_creatable_print_types(); + } +} + +ObjectOptions *user_creatable_parse_str(const char *optarg, Error **errp) +{ + ERRP_GUARD(); + QObject *obj; + bool help; + Visitor *v; + ObjectOptions *options; + + if (optarg[0] == '{') { + obj = qobject_from_json(optarg, errp); + if (!obj) { + return NULL; + } + v = qobject_input_visitor_new(obj); + } else { + QDict *args = keyval_parse(optarg, "qom-type", &help, errp); + if (*errp) { + return NULL; + } + if (help) { + user_creatable_print_help_from_qdict(args); + qobject_unref(args); + return NULL; + } + + obj = QOBJECT(args); + v = qobject_input_visitor_new_keyval(obj); + } + + visit_type_ObjectOptions(v, NULL, &options, errp); + visit_free(v); + qobject_unref(obj); + + return options; +} + +bool user_creatable_add_from_str(const char *optarg, Error **errp) +{ + ERRP_GUARD(); + ObjectOptions *options; + + options = user_creatable_parse_str(optarg, errp); + if (!options) { + return false; + } + + user_creatable_add_qapi(options, errp); + qapi_free_ObjectOptions(options); + return !*errp; +} + +void user_creatable_process_cmdline(const char *optarg) +{ + if (!user_creatable_add_from_str(optarg, &error_fatal)) { + /* Help was printed */ + exit(EXIT_SUCCESS); + } +} + +bool user_creatable_del(const char *id, Error **errp) +{ + QemuOptsList *opts_list; + Object *container; + Object *obj; + + container = object_get_objects_root(); + obj = object_resolve_path_component(container, id); + if (!obj) { + error_setg(errp, "object '%s' not found", id); + return false; + } + + if (!user_creatable_can_be_deleted(USER_CREATABLE(obj))) { + error_setg(errp, "object '%s' is in use, can not be deleted", id); + return false; + } + + /* + * if object was defined on the command-line, remove its corresponding + * option group entry + */ + opts_list = qemu_find_opts_err("object", NULL); + if (opts_list) { + qemu_opts_del(qemu_opts_find(opts_list, id)); + } + + object_unparent(obj); + return true; +} + +void user_creatable_cleanup(void) +{ + object_unparent(object_get_objects_root()); +} + +static void register_types(void) +{ + static const TypeInfo uc_interface_info = { + .name = TYPE_USER_CREATABLE, + .parent = TYPE_INTERFACE, + .class_size = sizeof(UserCreatableClass), + }; + + type_register_static(&uc_interface_info); +} + +type_init(register_types) diff --git a/qom/qom-hmp-cmds.c b/qom/qom-hmp-cmds.c new file mode 100644 index 000000000..453fbfeab --- /dev/null +++ b/qom/qom-hmp-cmds.c @@ -0,0 +1,152 @@ +/* + * HMP commands related to QOM + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "hw/qdev-core.h" +#include "monitor/hmp.h" +#include "monitor/monitor.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-qom.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qjson.h" +#include "qom/object.h" + +void hmp_qom_list(Monitor *mon, const QDict *qdict) +{ + const char *path = qdict_get_try_str(qdict, "path"); + ObjectPropertyInfoList *list; + Error *err = NULL; + + if (path == NULL) { + monitor_printf(mon, "/\n"); + return; + } + + list = qmp_qom_list(path, &err); + if (err == NULL) { + ObjectPropertyInfoList *start = list; + while (list != NULL) { + ObjectPropertyInfo *value = list->value; + + monitor_printf(mon, "%s (%s)\n", + value->name, value->type); + list = list->next; + } + qapi_free_ObjectPropertyInfoList(start); + } + hmp_handle_error(mon, err); +} + +void hmp_qom_set(Monitor *mon, const QDict *qdict) +{ + const bool json = qdict_get_try_bool(qdict, "json", false); + const char *path = qdict_get_str(qdict, "path"); + const char *property = qdict_get_str(qdict, "property"); + const char *value = qdict_get_str(qdict, "value"); + Error *err = NULL; + + if (!json) { + Object *obj = object_resolve_path(path, NULL); + + if (!obj) { + error_set(&err, ERROR_CLASS_DEVICE_NOT_FOUND, + "Device '%s' not found", path); + } else { + object_property_parse(obj, property, value, &err); + } + } else { + QObject *obj = qobject_from_json(value, &err); + + if (!err) { + qmp_qom_set(path, property, obj, &err); + } + } + + hmp_handle_error(mon, err); +} + +void hmp_qom_get(Monitor *mon, const QDict *qdict) +{ + const char *path = qdict_get_str(qdict, "path"); + const char *property = qdict_get_str(qdict, "property"); + Error *err = NULL; + QObject *obj = qmp_qom_get(path, property, &err); + + if (err == NULL) { + GString *str = qobject_to_json_pretty(obj, true); + monitor_printf(mon, "%s\n", str->str); + g_string_free(str, true); + } + + qobject_unref(obj); + hmp_handle_error(mon, err); +} + +typedef struct QOMCompositionState { + Monitor *mon; + int indent; +} QOMCompositionState; + +static void print_qom_composition(Monitor *mon, Object *obj, int indent); + +static int qom_composition_compare(const void *a, const void *b) +{ + return g_strcmp0(object_get_canonical_path_component(*(Object **)a), + object_get_canonical_path_component(*(Object **)b)); +} + +static int insert_qom_composition_child(Object *obj, void *opaque) +{ + g_array_append_val(opaque, obj); + return 0; +} + +static void print_qom_composition(Monitor *mon, Object *obj, int indent) +{ + GArray *children = g_array_new(false, false, sizeof(Object *)); + const char *name; + int i; + + if (obj == object_get_root()) { + name = ""; + } else { + name = object_get_canonical_path_component(obj); + } + monitor_printf(mon, "%*s/%s (%s)\n", indent, "", name, + object_get_typename(obj)); + + object_child_foreach(obj, insert_qom_composition_child, children); + g_array_sort(children, qom_composition_compare); + + for (i = 0; i < children->len; i++) { + print_qom_composition(mon, g_array_index(children, Object *, i), + indent + 2); + } + g_array_free(children, TRUE); +} + +void hmp_info_qom_tree(Monitor *mon, const QDict *dict) +{ + const char *path = qdict_get_try_str(dict, "path"); + Object *obj; + bool ambiguous = false; + + if (path) { + obj = object_resolve_path(path, &ambiguous); + if (!obj) { + monitor_printf(mon, "Path '%s' could not be resolved.\n", path); + return; + } + if (ambiguous) { + monitor_printf(mon, "Warning: Path '%s' is ambiguous.\n", path); + return; + } + } else { + obj = qdev_get_machine(); + } + print_qom_composition(mon, obj, 0); +} diff --git a/qom/qom-qmp-cmds.c b/qom/qom-qmp-cmds.c new file mode 100644 index 000000000..2d6f41ecc --- /dev/null +++ b/qom/qom-qmp-cmds.c @@ -0,0 +1,237 @@ +/* + * QMP commands related to QOM + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori <aliguori@us.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ + +#include "qemu/osdep.h" +#include "block/qdict.h" +#include "hw/qdev-core.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-qdev.h" +#include "qapi/qapi-commands-qom.h" +#include "qapi/qapi-visit-qom.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qerror.h" +#include "qapi/qobject-input-visitor.h" +#include "qapi/qobject-output-visitor.h" +#include "qemu/cutils.h" +#include "qom/object_interfaces.h" +#include "qom/qom-qobject.h" + +ObjectPropertyInfoList *qmp_qom_list(const char *path, Error **errp) +{ + Object *obj; + bool ambiguous = false; + ObjectPropertyInfoList *props = NULL; + ObjectProperty *prop; + ObjectPropertyIterator iter; + + obj = object_resolve_path(path, &ambiguous); + if (obj == NULL) { + if (ambiguous) { + error_setg(errp, "Path '%s' is ambiguous", path); + } else { + error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, + "Device '%s' not found", path); + } + return NULL; + } + + object_property_iter_init(&iter, obj); + while ((prop = object_property_iter_next(&iter))) { + ObjectPropertyInfo *value = g_malloc0(sizeof(ObjectPropertyInfo)); + + QAPI_LIST_PREPEND(props, value); + + value->name = g_strdup(prop->name); + value->type = g_strdup(prop->type); + } + + return props; +} + +void qmp_qom_set(const char *path, const char *property, QObject *value, + Error **errp) +{ + Object *obj; + + obj = object_resolve_path(path, NULL); + if (!obj) { + error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, + "Device '%s' not found", path); + return; + } + + object_property_set_qobject(obj, property, value, errp); +} + +QObject *qmp_qom_get(const char *path, const char *property, Error **errp) +{ + Object *obj; + + obj = object_resolve_path(path, NULL); + if (!obj) { + error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, + "Device '%s' not found", path); + return NULL; + } + + return object_property_get_qobject(obj, property, errp); +} + +static void qom_list_types_tramp(ObjectClass *klass, void *data) +{ + ObjectTypeInfoList **pret = data; + ObjectTypeInfo *info; + ObjectClass *parent = object_class_get_parent(klass); + + info = g_malloc0(sizeof(*info)); + info->name = g_strdup(object_class_get_name(klass)); + info->has_abstract = info->abstract = object_class_is_abstract(klass); + if (parent) { + info->has_parent = true; + info->parent = g_strdup(object_class_get_name(parent)); + } + + QAPI_LIST_PREPEND(*pret, info); +} + +ObjectTypeInfoList *qmp_qom_list_types(bool has_implements, + const char *implements, + bool has_abstract, + bool abstract, + Error **errp) +{ + ObjectTypeInfoList *ret = NULL; + + module_load_qom_all(); + object_class_foreach(qom_list_types_tramp, implements, abstract, &ret); + + return ret; +} + +ObjectPropertyInfoList *qmp_device_list_properties(const char *typename, + Error **errp) +{ + ObjectClass *klass; + Object *obj; + ObjectProperty *prop; + ObjectPropertyIterator iter; + ObjectPropertyInfoList *prop_list = NULL; + + klass = module_object_class_by_name(typename); + if (klass == NULL) { + error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, + "Device '%s' not found", typename); + return NULL; + } + + if (!object_class_dynamic_cast(klass, TYPE_DEVICE) + || object_class_is_abstract(klass)) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "typename", + "a non-abstract device type"); + return NULL; + } + + obj = object_new(typename); + + object_property_iter_init(&iter, obj); + while ((prop = object_property_iter_next(&iter))) { + ObjectPropertyInfo *info; + + /* Skip Object and DeviceState properties */ + if (strcmp(prop->name, "type") == 0 || + strcmp(prop->name, "realized") == 0 || + strcmp(prop->name, "hotpluggable") == 0 || + strcmp(prop->name, "hotplugged") == 0 || + strcmp(prop->name, "parent_bus") == 0) { + continue; + } + + /* Skip legacy properties since they are just string versions of + * properties that we already list. + */ + if (strstart(prop->name, "legacy-", NULL)) { + continue; + } + + info = g_new0(ObjectPropertyInfo, 1); + info->name = g_strdup(prop->name); + info->type = g_strdup(prop->type); + info->has_description = !!prop->description; + info->description = g_strdup(prop->description); + info->default_value = qobject_ref(prop->defval); + info->has_default_value = !!info->default_value; + + QAPI_LIST_PREPEND(prop_list, info); + } + + object_unref(obj); + + return prop_list; +} + +ObjectPropertyInfoList *qmp_qom_list_properties(const char *typename, + Error **errp) +{ + ObjectClass *klass; + Object *obj = NULL; + ObjectProperty *prop; + ObjectPropertyIterator iter; + ObjectPropertyInfoList *prop_list = NULL; + + klass = object_class_by_name(typename); + if (klass == NULL) { + error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, + "Class '%s' not found", typename); + return NULL; + } + + if (!object_class_dynamic_cast(klass, TYPE_OBJECT)) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "typename", + "a QOM type"); + return NULL; + } + + if (object_class_is_abstract(klass)) { + object_class_property_iter_init(&iter, klass); + } else { + obj = object_new(typename); + object_property_iter_init(&iter, obj); + } + while ((prop = object_property_iter_next(&iter))) { + ObjectPropertyInfo *info; + + info = g_malloc0(sizeof(*info)); + info->name = g_strdup(prop->name); + info->type = g_strdup(prop->type); + info->has_description = !!prop->description; + info->description = g_strdup(prop->description); + + QAPI_LIST_PREPEND(prop_list, info); + } + + object_unref(obj); + + return prop_list; +} + +void qmp_object_add(ObjectOptions *options, Error **errp) +{ + user_creatable_add_qapi(options, errp); +} + +void qmp_object_del(const char *id, Error **errp) +{ + user_creatable_del(id, errp); +} diff --git a/qom/qom-qobject.c b/qom/qom-qobject.c new file mode 100644 index 000000000..21ce22de9 --- /dev/null +++ b/qom/qom-qobject.c @@ -0,0 +1,45 @@ +/* + * QEMU Object Model - QObject wrappers + * + * Copyright (C) 2012 Red Hat, Inc. + * + * Author: Paolo Bonzini <pbonzini@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qom/object.h" +#include "qom/qom-qobject.h" +#include "qapi/visitor.h" +#include "qapi/qobject-input-visitor.h" +#include "qapi/qobject-output-visitor.h" + +bool object_property_set_qobject(Object *obj, + const char *name, QObject *value, + Error **errp) +{ + Visitor *v; + bool ok; + + v = qobject_input_visitor_new(value); + ok = object_property_set(obj, name, v, errp); + visit_free(v); + return ok; +} + +QObject *object_property_get_qobject(Object *obj, const char *name, + Error **errp) +{ + QObject *ret = NULL; + Visitor *v; + + v = qobject_output_visitor_new(&ret); + if (object_property_get(obj, name, v, errp)) { + visit_complete(v, &ret); + } + visit_free(v); + return ret; +} diff --git a/qom/trace-events b/qom/trace-events new file mode 100644 index 000000000..b2e9f4a71 --- /dev/null +++ b/qom/trace-events @@ -0,0 +1,5 @@ +# See docs/devel/tracing.rst for syntax documentation. + +# object.c +object_dynamic_cast_assert(const char *type, const char *target, const char *file, int line, const char *func) "%s->%s (%s:%d:%s)" +object_class_dynamic_cast_assert(const char *type, const char *target, const char *file, int line, const char *func) "%s->%s (%s:%d:%s)" diff --git a/qom/trace.h b/qom/trace.h new file mode 100644 index 000000000..f2895e699 --- /dev/null +++ b/qom/trace.h @@ -0,0 +1 @@ +#include "trace/trace-qom.h" |