2011-12-04 03:10:08 +04:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2012-12-17 21:19:50 +04:00
|
|
|
#include "qom/object.h"
|
qom: Add object_new_with_props() / object_new_withpropv() helpers
It is reasonably common to want to create an object, set a
number of properties, register it in the hierarchy and then
mark it as complete (if a user creatable type). This requires
quite a lot of error prone, verbose, boilerplate code to achieve.
First a pair of functions object_set_props() / object_set_propv()
are added which allow for a list of objects to be set in
one single API call.
Then object_new_with_props() / object_new_with_propv() constructors
are added which simplify the sequence of calls to create an
object, populate properties, register in the object composition
tree and mark the object complete, into a single method call.
Usage would be:
Error *err = NULL;
Object *obj;
obj = object_new_with_propv(TYPE_MEMORY_BACKEND_FILE,
object_get_objects_root(),
"hostmem0",
&err,
"share", "yes",
"mem-path", "/dev/shm/somefile",
"prealloc", "yes",
"size", "1048576",
NULL);
Note all property values are passed in string form and will
be parsed into their required data types, using normal QOM
semantics for parsing from string format.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Andreas Färber <afaerber@suse.de>
2015-05-13 19:14:06 +03:00
|
|
|
#include "qom/object_interfaces.h"
|
2011-12-04 03:10:08 +04:00
|
|
|
#include "qemu-common.h"
|
2012-12-17 21:19:43 +04:00
|
|
|
#include "qapi/visitor.h"
|
2014-05-14 13:43:33 +04:00
|
|
|
#include "qapi-visit.h"
|
2012-02-09 12:52:59 +04:00
|
|
|
#include "qapi/string-input-visitor.h"
|
|
|
|
#include "qapi/string-output-visitor.h"
|
2012-12-17 21:19:43 +04:00
|
|
|
#include "qapi/qmp/qerror.h"
|
2013-05-10 16:16:39 +04:00
|
|
|
#include "trace.h"
|
2011-12-04 03:10:08 +04:00
|
|
|
|
2012-02-01 20:16:22 +04:00
|
|
|
/* TODO: replace QObject with a simpler visitor to avoid a dependency
|
|
|
|
* of the QOM core on QObject? */
|
2012-12-17 21:19:50 +04:00
|
|
|
#include "qom/qom-qobject.h"
|
2012-12-17 21:19:43 +04:00
|
|
|
#include "qapi/qmp/qobject.h"
|
|
|
|
#include "qapi/qmp/qbool.h"
|
|
|
|
#include "qapi/qmp/qint.h"
|
|
|
|
#include "qapi/qmp/qstring.h"
|
2012-02-01 20:16:22 +04:00
|
|
|
|
2011-12-04 03:10:08 +04:00
|
|
|
#define MAX_INTERFACES 32
|
|
|
|
|
|
|
|
typedef struct InterfaceImpl InterfaceImpl;
|
|
|
|
typedef struct TypeImpl TypeImpl;
|
|
|
|
|
|
|
|
struct InterfaceImpl
|
|
|
|
{
|
2012-08-10 07:16:10 +04:00
|
|
|
const char *typename;
|
2011-12-04 03:10:08 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
struct TypeImpl
|
|
|
|
{
|
|
|
|
const char *name;
|
|
|
|
|
|
|
|
size_t class_size;
|
|
|
|
|
|
|
|
size_t instance_size;
|
|
|
|
|
|
|
|
void (*class_init)(ObjectClass *klass, void *data);
|
2012-05-02 15:30:55 +04:00
|
|
|
void (*class_base_init)(ObjectClass *klass, void *data);
|
2011-12-04 03:10:08 +04:00
|
|
|
void (*class_finalize)(ObjectClass *klass, void *data);
|
|
|
|
|
|
|
|
void *class_data;
|
|
|
|
|
|
|
|
void (*instance_init)(Object *obj);
|
2013-07-11 00:08:41 +04:00
|
|
|
void (*instance_post_init)(Object *obj);
|
2011-12-04 03:10:08 +04:00
|
|
|
void (*instance_finalize)(Object *obj);
|
|
|
|
|
|
|
|
bool abstract;
|
|
|
|
|
|
|
|
const char *parent;
|
|
|
|
TypeImpl *parent_type;
|
|
|
|
|
|
|
|
ObjectClass *class;
|
|
|
|
|
|
|
|
int num_interfaces;
|
|
|
|
InterfaceImpl interfaces[MAX_INTERFACES];
|
|
|
|
};
|
|
|
|
|
2015-10-13 15:37:40 +03:00
|
|
|
struct ObjectPropertyIterator {
|
2015-10-13 15:37:45 +03:00
|
|
|
GHashTableIter iter;
|
2015-10-13 15:37:40 +03:00
|
|
|
};
|
|
|
|
|
2012-02-03 14:51:39 +04:00
|
|
|
static Type type_interface;
|
|
|
|
|
2011-12-04 03:10:08 +04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2013-12-03 19:42:00 +04:00
|
|
|
static bool enumerating_types;
|
|
|
|
|
2011-12-04 03:10:08 +04:00
|
|
|
static void type_table_add(TypeImpl *ti)
|
|
|
|
{
|
2013-12-03 19:42:00 +04:00
|
|
|
assert(!enumerating_types);
|
2011-12-04 03:10:08 +04:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2013-12-03 19:41:59 +04:00
|
|
|
static TypeImpl *type_new(const TypeInfo *info)
|
2011-12-04 03:10:08 +04:00
|
|
|
{
|
|
|
|
TypeImpl *ti = g_malloc0(sizeof(*ti));
|
2012-08-10 07:16:10 +04:00
|
|
|
int i;
|
2011-12-04 03:10:08 +04:00
|
|
|
|
|
|
|
g_assert(info->name != NULL);
|
|
|
|
|
2012-01-25 23:37:36 +04:00
|
|
|
if (type_table_lookup(info->name) != NULL) {
|
|
|
|
fprintf(stderr, "Registering `%s' which already exists\n", info->name);
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
|
2011-12-04 03:10:08 +04:00
|
|
|
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->class_init = info->class_init;
|
2012-05-02 15:30:55 +04:00
|
|
|
ti->class_base_init = info->class_base_init;
|
2011-12-04 03:10:08 +04:00
|
|
|
ti->class_finalize = info->class_finalize;
|
|
|
|
ti->class_data = info->class_data;
|
|
|
|
|
|
|
|
ti->instance_init = info->instance_init;
|
2013-07-11 00:08:41 +04:00
|
|
|
ti->instance_post_init = info->instance_post_init;
|
2011-12-04 03:10:08 +04:00
|
|
|
ti->instance_finalize = info->instance_finalize;
|
|
|
|
|
|
|
|
ti->abstract = info->abstract;
|
|
|
|
|
2012-08-10 07:16:10 +04:00
|
|
|
for (i = 0; info->interfaces && info->interfaces[i].type; i++) {
|
|
|
|
ti->interfaces[i].typename = g_strdup(info->interfaces[i].type);
|
2011-12-04 03:10:08 +04:00
|
|
|
}
|
2012-08-10 07:16:10 +04:00
|
|
|
ti->num_interfaces = i;
|
2011-12-04 03:10:08 +04:00
|
|
|
|
2013-12-03 19:41:59 +04:00
|
|
|
return ti;
|
|
|
|
}
|
2011-12-04 03:10:08 +04:00
|
|
|
|
2013-12-03 19:41:59 +04:00
|
|
|
static TypeImpl *type_register_internal(const TypeInfo *info)
|
|
|
|
{
|
|
|
|
TypeImpl *ti;
|
|
|
|
ti = type_new(info);
|
|
|
|
|
|
|
|
type_table_add(ti);
|
2011-12-04 03:10:08 +04:00
|
|
|
return ti;
|
|
|
|
}
|
|
|
|
|
2012-04-04 17:58:40 +04:00
|
|
|
TypeImpl *type_register(const TypeInfo *info)
|
|
|
|
{
|
|
|
|
assert(info->parent);
|
|
|
|
return type_register_internal(info);
|
|
|
|
}
|
|
|
|
|
2011-12-04 03:10:08 +04:00
|
|
|
TypeImpl *type_register_static(const TypeInfo *info)
|
|
|
|
{
|
|
|
|
return type_register(info);
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
g_assert(type->parent_type != NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2012-02-28 15:57:10 +04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2012-08-10 07:16:10 +04:00
|
|
|
static bool type_is_ancestor(TypeImpl *type, TypeImpl *target_type)
|
2011-12-04 03:10:08 +04:00
|
|
|
{
|
2012-08-10 07:16:10 +04:00
|
|
|
assert(target_type);
|
|
|
|
|
2015-11-03 05:36:42 +03:00
|
|
|
/* Check if target_type is a direct ancestor of type */
|
2012-08-10 07:16:10 +04:00
|
|
|
while (type) {
|
|
|
|
if (type == target_type) {
|
|
|
|
return true;
|
|
|
|
}
|
2011-12-04 03:10:08 +04:00
|
|
|
|
2012-08-10 07:16:10 +04:00
|
|
|
type = type_get_parent(type);
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void type_initialize(TypeImpl *ti);
|
|
|
|
|
2013-12-03 19:41:59 +04:00
|
|
|
static void type_initialize_interface(TypeImpl *ti, TypeImpl *interface_type,
|
|
|
|
TypeImpl *parent_type)
|
2012-08-10 07:16:10 +04:00
|
|
|
{
|
|
|
|
InterfaceClass *new_iface;
|
|
|
|
TypeInfo info = { };
|
|
|
|
TypeImpl *iface_impl;
|
|
|
|
|
2013-12-03 19:41:59 +04:00
|
|
|
info.parent = parent_type->name;
|
|
|
|
info.name = g_strdup_printf("%s::%s", ti->name, interface_type->name);
|
2012-08-10 07:16:10 +04:00
|
|
|
info.abstract = true;
|
|
|
|
|
2013-12-03 19:41:59 +04:00
|
|
|
iface_impl = type_new(&info);
|
|
|
|
iface_impl->parent_type = parent_type;
|
2012-08-10 07:16:10 +04:00
|
|
|
type_initialize(iface_impl);
|
|
|
|
g_free((char *)info.name);
|
|
|
|
|
|
|
|
new_iface = (InterfaceClass *)iface_impl->class;
|
|
|
|
new_iface->concrete_class = ti->class;
|
2013-12-03 19:41:59 +04:00
|
|
|
new_iface->interface_type = interface_type;
|
2012-08-10 07:16:10 +04:00
|
|
|
|
|
|
|
ti->class->interfaces = g_slist_append(ti->class->interfaces,
|
|
|
|
iface_impl->class);
|
2011-12-04 03:10:08 +04:00
|
|
|
}
|
|
|
|
|
2012-02-28 15:57:11 +04:00
|
|
|
static void type_initialize(TypeImpl *ti)
|
2011-12-04 03:10:08 +04:00
|
|
|
{
|
2012-03-31 18:45:54 +04:00
|
|
|
TypeImpl *parent;
|
2011-12-04 03:10:08 +04:00
|
|
|
|
|
|
|
if (ti->class) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ti->class_size = type_class_get_size(ti);
|
2012-02-28 15:57:10 +04:00
|
|
|
ti->instance_size = type_object_get_size(ti);
|
2011-12-04 03:10:08 +04:00
|
|
|
|
|
|
|
ti->class = g_malloc0(ti->class_size);
|
|
|
|
|
2012-03-31 18:45:54 +04:00
|
|
|
parent = type_get_parent(ti);
|
|
|
|
if (parent) {
|
2012-02-28 15:57:11 +04:00
|
|
|
type_initialize(parent);
|
2012-08-10 07:16:10 +04:00
|
|
|
GSList *e;
|
|
|
|
int i;
|
2011-12-04 03:10:08 +04:00
|
|
|
|
2015-11-16 19:49:20 +03:00
|
|
|
g_assert_cmpint(parent->class_size, <=, ti->class_size);
|
2012-03-31 18:45:54 +04:00
|
|
|
memcpy(ti->class, parent->class, parent->class_size);
|
2013-02-19 08:02:09 +04:00
|
|
|
ti->class->interfaces = NULL;
|
2012-08-10 07:16:10 +04:00
|
|
|
|
|
|
|
for (e = parent->class->interfaces; e; e = e->next) {
|
2013-12-03 19:41:59 +04:00
|
|
|
InterfaceClass *iface = e->data;
|
|
|
|
ObjectClass *klass = OBJECT_CLASS(iface);
|
|
|
|
|
|
|
|
type_initialize_interface(ti, iface->interface_type, klass->type);
|
2012-08-10 07:16:10 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < ti->num_interfaces; i++) {
|
|
|
|
TypeImpl *t = type_get_by_name(ti->interfaces[i].typename);
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2013-12-03 19:41:59 +04:00
|
|
|
type_initialize_interface(ti, t, t);
|
2012-08-10 07:16:10 +04:00
|
|
|
}
|
2012-03-31 18:45:54 +04:00
|
|
|
}
|
2011-12-04 03:10:08 +04:00
|
|
|
|
2012-03-31 18:45:54 +04:00
|
|
|
ti->class->type = ti;
|
2012-05-02 15:30:55 +04:00
|
|
|
|
2012-03-31 18:45:54 +04:00
|
|
|
while (parent) {
|
|
|
|
if (parent->class_base_init) {
|
|
|
|
parent->class_base_init(ti->class, ti->class_data);
|
2012-05-02 15:30:55 +04:00
|
|
|
}
|
2012-03-31 18:45:54 +04:00
|
|
|
parent = type_get_parent(parent);
|
2011-12-04 03:10:08 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-11 00:08:41 +04:00
|
|
|
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));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-13 15:37:45 +03:00
|
|
|
static void object_property_free(gpointer data)
|
|
|
|
{
|
|
|
|
ObjectProperty *prop = data;
|
|
|
|
|
|
|
|
g_free(prop->name);
|
|
|
|
g_free(prop->type);
|
|
|
|
g_free(prop->description);
|
|
|
|
g_free(prop);
|
|
|
|
}
|
|
|
|
|
2013-08-30 20:28:37 +04:00
|
|
|
void object_initialize_with_type(void *data, size_t size, TypeImpl *type)
|
2011-12-04 03:10:08 +04:00
|
|
|
{
|
|
|
|
Object *obj = data;
|
|
|
|
|
|
|
|
g_assert(type != NULL);
|
2012-02-28 15:57:11 +04:00
|
|
|
type_initialize(type);
|
2012-02-28 15:57:10 +04:00
|
|
|
|
2015-11-16 19:49:20 +03:00
|
|
|
g_assert_cmpint(type->instance_size, >=, sizeof(Object));
|
2011-12-04 03:10:08 +04:00
|
|
|
g_assert(type->abstract == false);
|
2015-11-16 19:49:20 +03:00
|
|
|
g_assert_cmpint(size, >=, type->instance_size);
|
2011-12-04 03:10:08 +04:00
|
|
|
|
|
|
|
memset(obj, 0, type->instance_size);
|
|
|
|
obj->class = type->class;
|
2012-11-23 12:47:12 +04:00
|
|
|
object_ref(obj);
|
2015-10-13 15:37:45 +03:00
|
|
|
obj->properties = g_hash_table_new_full(g_str_hash, g_str_equal,
|
|
|
|
NULL, object_property_free);
|
2011-12-04 03:10:08 +04:00
|
|
|
object_init_with_type(obj, type);
|
2013-07-11 00:08:41 +04:00
|
|
|
object_post_init_with_type(obj, type);
|
2011-12-04 03:10:08 +04:00
|
|
|
}
|
|
|
|
|
2013-08-23 21:37:12 +04:00
|
|
|
void object_initialize(void *data, size_t size, const char *typename)
|
2011-12-04 03:10:08 +04:00
|
|
|
{
|
|
|
|
TypeImpl *type = type_get_by_name(typename);
|
|
|
|
|
2013-08-30 20:28:37 +04:00
|
|
|
object_initialize_with_type(data, size, type);
|
2011-12-04 03:10:08 +04:00
|
|
|
}
|
|
|
|
|
2012-05-27 02:32:40 +04:00
|
|
|
static inline bool object_property_is_child(ObjectProperty *prop)
|
|
|
|
{
|
|
|
|
return strstart(prop->type, "child<", NULL);
|
|
|
|
}
|
|
|
|
|
2012-01-30 18:55:55 +04:00
|
|
|
static void object_property_del_all(Object *obj)
|
|
|
|
{
|
2015-10-13 15:37:45 +03:00
|
|
|
ObjectProperty *prop;
|
|
|
|
GHashTableIter iter;
|
|
|
|
gpointer key, value;
|
|
|
|
bool released;
|
|
|
|
|
|
|
|
do {
|
|
|
|
released = false;
|
|
|
|
g_hash_table_iter_init(&iter, obj->properties);
|
|
|
|
while (g_hash_table_iter_next(&iter, &key, &value)) {
|
|
|
|
prop = value;
|
|
|
|
if (prop->release) {
|
|
|
|
prop->release(obj, prop->name, prop->opaque);
|
|
|
|
prop->release = NULL;
|
|
|
|
released = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
g_hash_table_iter_remove(&iter);
|
2012-01-30 18:55:55 +04:00
|
|
|
}
|
2015-10-13 15:37:45 +03:00
|
|
|
} while (released);
|
2012-01-30 18:55:55 +04:00
|
|
|
|
2015-10-13 15:37:45 +03:00
|
|
|
g_hash_table_unref(obj->properties);
|
2012-01-30 18:55:55 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void object_property_del_child(Object *obj, Object *child, Error **errp)
|
|
|
|
{
|
|
|
|
ObjectProperty *prop;
|
2015-10-13 15:37:45 +03:00
|
|
|
GHashTableIter iter;
|
|
|
|
gpointer key, value;
|
2012-01-30 18:55:55 +04:00
|
|
|
|
2015-10-13 15:37:45 +03:00
|
|
|
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;
|
2012-05-27 02:32:40 +04:00
|
|
|
if (object_property_is_child(prop) && prop->opaque == child) {
|
2015-10-13 15:37:45 +03:00
|
|
|
g_hash_table_iter_remove(&iter);
|
2012-02-28 12:54:15 +04:00
|
|
|
break;
|
2012-01-30 18:55:55 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void object_unparent(Object *obj)
|
|
|
|
{
|
2013-03-18 23:01:37 +04:00
|
|
|
if (obj->parent) {
|
|
|
|
object_property_del_child(obj->parent, obj, NULL);
|
|
|
|
}
|
2012-01-30 18:55:55 +04:00
|
|
|
}
|
|
|
|
|
2011-12-04 03:10:08 +04:00
|
|
|
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));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-23 12:47:16 +04:00
|
|
|
static void object_finalize(void *data)
|
2011-12-04 03:10:08 +04:00
|
|
|
{
|
|
|
|
Object *obj = data;
|
|
|
|
TypeImpl *ti = obj->class->type;
|
|
|
|
|
2012-01-30 18:55:55 +04:00
|
|
|
object_property_del_all(obj);
|
2014-06-11 13:58:30 +04:00
|
|
|
object_deinit(obj, ti);
|
2011-12-23 18:47:39 +04:00
|
|
|
|
2015-11-16 19:49:20 +03:00
|
|
|
g_assert_cmpint(obj->ref, ==, 0);
|
2012-11-23 12:47:14 +04:00
|
|
|
if (obj->free) {
|
|
|
|
obj->free(obj);
|
|
|
|
}
|
2011-12-04 03:10:08 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
Object *object_new_with_type(Type type)
|
|
|
|
{
|
|
|
|
Object *obj;
|
|
|
|
|
|
|
|
g_assert(type != NULL);
|
2012-02-28 15:57:11 +04:00
|
|
|
type_initialize(type);
|
2011-12-04 03:10:08 +04:00
|
|
|
|
|
|
|
obj = g_malloc(type->instance_size);
|
2013-08-30 20:28:37 +04:00
|
|
|
object_initialize_with_type(obj, type->instance_size, type);
|
2012-11-23 12:47:14 +04:00
|
|
|
obj->free = g_free;
|
2011-12-04 03:10:08 +04:00
|
|
|
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
|
|
|
Object *object_new(const char *typename)
|
|
|
|
{
|
|
|
|
TypeImpl *ti = type_get_by_name(typename);
|
|
|
|
|
|
|
|
return object_new_with_type(ti);
|
|
|
|
}
|
|
|
|
|
qom: Add object_new_with_props() / object_new_withpropv() helpers
It is reasonably common to want to create an object, set a
number of properties, register it in the hierarchy and then
mark it as complete (if a user creatable type). This requires
quite a lot of error prone, verbose, boilerplate code to achieve.
First a pair of functions object_set_props() / object_set_propv()
are added which allow for a list of objects to be set in
one single API call.
Then object_new_with_props() / object_new_with_propv() constructors
are added which simplify the sequence of calls to create an
object, populate properties, register in the object composition
tree and mark the object complete, into a single method call.
Usage would be:
Error *err = NULL;
Object *obj;
obj = object_new_with_propv(TYPE_MEMORY_BACKEND_FILE,
object_get_objects_root(),
"hostmem0",
&err,
"share", "yes",
"mem-path", "/dev/shm/somefile",
"prealloc", "yes",
"size", "1048576",
NULL);
Note all property values are passed in string form and will
be parsed into their required data types, using normal QOM
semantics for parsing from string format.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Andreas Färber <afaerber@suse.de>
2015-05-13 19:14:06 +03:00
|
|
|
|
|
|
|
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;
|
|
|
|
Error *local_err = NULL;
|
|
|
|
|
|
|
|
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(typename);
|
|
|
|
|
|
|
|
if (object_set_propv(obj, &local_err, vargs) < 0) {
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
object_property_add_child(parent, id, obj, &local_err);
|
|
|
|
if (local_err) {
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (object_dynamic_cast(obj, TYPE_USER_CREATABLE)) {
|
|
|
|
user_creatable_complete(obj, &local_err);
|
|
|
|
if (local_err) {
|
|
|
|
object_unparent(obj);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
object_unref(OBJECT(obj));
|
|
|
|
return obj;
|
|
|
|
|
|
|
|
error:
|
|
|
|
if (local_err) {
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
}
|
|
|
|
object_unref(obj);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int object_set_props(Object *obj,
|
|
|
|
Error **errp,
|
|
|
|
...)
|
|
|
|
{
|
|
|
|
va_list vargs;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
va_start(vargs, errp);
|
|
|
|
ret = object_set_propv(obj, errp, vargs);
|
|
|
|
va_end(vargs);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int object_set_propv(Object *obj,
|
|
|
|
Error **errp,
|
|
|
|
va_list vargs)
|
|
|
|
{
|
|
|
|
const char *propname;
|
|
|
|
Error *local_err = NULL;
|
|
|
|
|
|
|
|
propname = va_arg(vargs, char *);
|
|
|
|
while (propname != NULL) {
|
|
|
|
const char *value = va_arg(vargs, char *);
|
|
|
|
|
|
|
|
g_assert(value != NULL);
|
|
|
|
object_property_parse(obj, value, propname, &local_err);
|
|
|
|
if (local_err) {
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
propname = va_arg(vargs, char *);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-12-04 03:10:08 +04:00
|
|
|
Object *object_dynamic_cast(Object *obj, const char *typename)
|
|
|
|
{
|
2012-11-23 19:56:17 +04:00
|
|
|
if (obj && object_class_dynamic_cast(object_get_class(obj), typename)) {
|
2012-02-03 14:57:23 +04:00
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
2011-12-04 03:10:08 +04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-05-10 16:16:38 +04:00
|
|
|
Object *object_dynamic_cast_assert(Object *obj, const char *typename,
|
|
|
|
const char *file, int line, const char *func)
|
2011-12-04 03:10:08 +04:00
|
|
|
{
|
2013-05-10 16:16:39 +04:00
|
|
|
trace_object_dynamic_cast_assert(obj ? obj->class->type->name : "(null)",
|
|
|
|
typename, file, line, func);
|
|
|
|
|
2013-05-10 16:16:40 +04:00
|
|
|
#ifdef CONFIG_QOM_CAST_DEBUG
|
2013-05-14 00:22:24 +04:00
|
|
|
int i;
|
|
|
|
Object *inst;
|
|
|
|
|
2013-05-22 05:19:16 +04:00
|
|
|
for (i = 0; obj && i < OBJECT_CLASS_CAST_CACHE; i++) {
|
2013-11-28 08:27:33 +04:00
|
|
|
if (obj->class->object_cast_cache[i] == typename) {
|
2013-05-14 00:22:24 +04:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inst = object_dynamic_cast(obj, typename);
|
2011-12-04 03:10:08 +04:00
|
|
|
|
2012-11-23 19:56:17 +04:00
|
|
|
if (!inst && obj) {
|
2013-05-10 16:16:38 +04:00
|
|
|
fprintf(stderr, "%s:%d:%s: Object %p is not an instance of type %s\n",
|
|
|
|
file, line, func, obj, typename);
|
2011-12-04 03:10:08 +04:00
|
|
|
abort();
|
|
|
|
}
|
|
|
|
|
2013-05-10 16:16:40 +04:00
|
|
|
assert(obj == inst);
|
2013-05-14 00:22:24 +04:00
|
|
|
|
2013-05-22 05:19:16 +04:00
|
|
|
if (obj && obj == inst) {
|
2013-05-14 00:22:24 +04:00
|
|
|
for (i = 1; i < OBJECT_CLASS_CAST_CACHE; i++) {
|
2013-11-28 08:27:33 +04:00
|
|
|
obj->class->object_cast_cache[i - 1] =
|
|
|
|
obj->class->object_cast_cache[i];
|
2013-05-14 00:22:24 +04:00
|
|
|
}
|
2013-11-28 08:27:33 +04:00
|
|
|
obj->class->object_cast_cache[i - 1] = typename;
|
2013-05-14 00:22:24 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
2013-05-10 16:16:40 +04:00
|
|
|
#endif
|
|
|
|
return obj;
|
2011-12-04 03:10:08 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
ObjectClass *object_class_dynamic_cast(ObjectClass *class,
|
|
|
|
const char *typename)
|
|
|
|
{
|
2012-08-10 07:16:10 +04:00
|
|
|
ObjectClass *ret = NULL;
|
2013-05-10 16:16:36 +04:00
|
|
|
TypeImpl *target_type;
|
|
|
|
TypeImpl *type;
|
2011-12-04 03:10:08 +04:00
|
|
|
|
2013-05-10 16:16:36 +04:00
|
|
|
if (!class) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-05-10 16:16:37 +04:00
|
|
|
/* A simple fast path that can trigger a lot for leaf classes. */
|
2013-05-10 16:16:36 +04:00
|
|
|
type = class->type;
|
2013-05-10 16:16:37 +04:00
|
|
|
if (type->name == typename) {
|
|
|
|
return class;
|
|
|
|
}
|
|
|
|
|
2013-05-10 16:16:36 +04:00
|
|
|
target_type = type_get_by_name(typename);
|
2013-04-30 17:02:16 +04:00
|
|
|
if (!target_type) {
|
|
|
|
/* target class type unknown, so fail the cast */
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-02-19 08:02:10 +04:00
|
|
|
if (type->class->interfaces &&
|
|
|
|
type_is_ancestor(target_type, type_interface)) {
|
2012-08-10 07:16:10 +04:00
|
|
|
int found = 0;
|
|
|
|
GSList *i;
|
2011-12-04 03:10:08 +04:00
|
|
|
|
2012-08-10 07:16:10 +04:00
|
|
|
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;
|
2011-12-04 03:10:08 +04:00
|
|
|
}
|
|
|
|
|
2012-08-10 07:16:10 +04:00
|
|
|
return ret;
|
2011-12-04 03:10:08 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
ObjectClass *object_class_dynamic_cast_assert(ObjectClass *class,
|
2013-05-10 16:16:38 +04:00
|
|
|
const char *typename,
|
|
|
|
const char *file, int line,
|
|
|
|
const char *func)
|
2011-12-04 03:10:08 +04:00
|
|
|
{
|
2013-05-10 16:16:39 +04:00
|
|
|
ObjectClass *ret;
|
|
|
|
|
|
|
|
trace_object_class_dynamic_cast_assert(class ? class->type->name : "(null)",
|
|
|
|
typename, file, line, func);
|
2011-12-04 03:10:08 +04:00
|
|
|
|
2013-05-14 00:22:24 +04:00
|
|
|
#ifdef CONFIG_QOM_CAST_DEBUG
|
|
|
|
int i;
|
|
|
|
|
2013-06-18 13:18:59 +04:00
|
|
|
for (i = 0; class && i < OBJECT_CLASS_CAST_CACHE; i++) {
|
2013-11-28 08:27:33 +04:00
|
|
|
if (class->class_cast_cache[i] == typename) {
|
2013-05-14 00:22:24 +04:00
|
|
|
ret = class;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#else
|
2013-06-18 13:18:59 +04:00
|
|
|
if (!class || !class->interfaces) {
|
2013-05-10 16:16:40 +04:00
|
|
|
return class;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2013-05-10 16:16:39 +04:00
|
|
|
ret = object_class_dynamic_cast(class, typename);
|
2013-05-10 16:16:36 +04:00
|
|
|
if (!ret && class) {
|
2013-05-10 16:16:38 +04:00
|
|
|
fprintf(stderr, "%s:%d:%s: Object %p is not an instance of type %s\n",
|
|
|
|
file, line, func, class, typename);
|
2011-12-04 03:10:08 +04:00
|
|
|
abort();
|
|
|
|
}
|
|
|
|
|
2013-05-14 00:22:24 +04:00
|
|
|
#ifdef CONFIG_QOM_CAST_DEBUG
|
2013-06-18 13:18:59 +04:00
|
|
|
if (class && ret == class) {
|
2013-05-14 00:22:24 +04:00
|
|
|
for (i = 1; i < OBJECT_CLASS_CAST_CACHE; i++) {
|
2013-11-28 08:27:33 +04:00
|
|
|
class->class_cast_cache[i - 1] = class->class_cast_cache[i];
|
2013-05-14 00:22:24 +04:00
|
|
|
}
|
2013-11-28 08:27:33 +04:00
|
|
|
class->class_cast_cache[i - 1] = typename;
|
2013-05-14 00:22:24 +04:00
|
|
|
}
|
|
|
|
out:
|
|
|
|
#endif
|
2011-12-04 03:10:08 +04:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *object_get_typename(Object *obj)
|
|
|
|
{
|
|
|
|
return obj->class->type->name;
|
|
|
|
}
|
|
|
|
|
|
|
|
ObjectClass *object_get_class(Object *obj)
|
|
|
|
{
|
|
|
|
return obj->class;
|
|
|
|
}
|
|
|
|
|
2013-01-23 15:20:18 +04:00
|
|
|
bool object_class_is_abstract(ObjectClass *klass)
|
|
|
|
{
|
|
|
|
return klass->type->abstract;
|
|
|
|
}
|
|
|
|
|
2011-12-04 03:10:08 +04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2012-02-28 15:57:11 +04:00
|
|
|
type_initialize(type);
|
2011-12-04 03:10:08 +04:00
|
|
|
|
|
|
|
return type->class;
|
|
|
|
}
|
|
|
|
|
2012-05-02 15:30:54 +04:00
|
|
|
ObjectClass *object_class_get_parent(ObjectClass *class)
|
|
|
|
{
|
|
|
|
TypeImpl *type = type_get_parent(class->type);
|
|
|
|
|
|
|
|
if (!type) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
type_initialize(type);
|
|
|
|
|
|
|
|
return type->class;
|
|
|
|
}
|
|
|
|
|
2011-12-04 03:10:08 +04:00
|
|
|
typedef struct OCFData
|
|
|
|
{
|
|
|
|
void (*fn)(ObjectClass *klass, void *opaque);
|
2011-12-23 00:11:53 +04:00
|
|
|
const char *implements_type;
|
|
|
|
bool include_abstract;
|
2011-12-04 03:10:08 +04:00
|
|
|
void *opaque;
|
|
|
|
} OCFData;
|
|
|
|
|
|
|
|
static void object_class_foreach_tramp(gpointer key, gpointer value,
|
|
|
|
gpointer opaque)
|
|
|
|
{
|
|
|
|
OCFData *data = opaque;
|
|
|
|
TypeImpl *type = value;
|
2011-12-23 00:11:53 +04:00
|
|
|
ObjectClass *k;
|
2011-12-04 03:10:08 +04:00
|
|
|
|
2012-02-28 15:57:11 +04:00
|
|
|
type_initialize(type);
|
2011-12-23 00:11:53 +04:00
|
|
|
k = type->class;
|
2011-12-04 03:10:08 +04:00
|
|
|
|
2011-12-23 00:11:53 +04:00
|
|
|
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);
|
2011-12-04 03:10:08 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void object_class_foreach(void (*fn)(ObjectClass *klass, void *opaque),
|
2011-12-23 00:11:53 +04:00
|
|
|
const char *implements_type, bool include_abstract,
|
2011-12-04 03:10:08 +04:00
|
|
|
void *opaque)
|
|
|
|
{
|
2011-12-23 00:11:53 +04:00
|
|
|
OCFData data = { fn, implements_type, include_abstract, opaque };
|
2011-12-04 03:10:08 +04:00
|
|
|
|
2013-12-03 19:42:00 +04:00
|
|
|
enumerating_types = true;
|
2011-12-04 03:10:08 +04:00
|
|
|
g_hash_table_foreach(type_table_get(), object_class_foreach_tramp, &data);
|
2013-12-03 19:42:00 +04:00
|
|
|
enumerating_types = false;
|
2011-12-04 03:10:08 +04:00
|
|
|
}
|
2012-01-30 18:55:55 +04:00
|
|
|
|
2015-09-08 19:38:43 +03:00
|
|
|
static int do_object_child_foreach(Object *obj,
|
|
|
|
int (*fn)(Object *child, void *opaque),
|
|
|
|
void *opaque, bool recurse)
|
2012-04-12 01:30:20 +04:00
|
|
|
{
|
2015-10-13 15:37:45 +03:00
|
|
|
GHashTableIter iter;
|
|
|
|
ObjectProperty *prop;
|
2012-04-12 01:30:20 +04:00
|
|
|
int ret = 0;
|
|
|
|
|
2015-10-13 15:37:45 +03:00
|
|
|
g_hash_table_iter_init(&iter, obj->properties);
|
|
|
|
while (g_hash_table_iter_next(&iter, NULL, (gpointer *)&prop)) {
|
2012-04-12 01:30:20 +04:00
|
|
|
if (object_property_is_child(prop)) {
|
2015-09-08 19:38:43 +03:00
|
|
|
Object *child = prop->opaque;
|
|
|
|
|
|
|
|
ret = fn(child, opaque);
|
2012-04-12 01:30:20 +04:00
|
|
|
if (ret != 0) {
|
|
|
|
break;
|
|
|
|
}
|
2015-09-08 19:38:43 +03:00
|
|
|
if (recurse) {
|
|
|
|
do_object_child_foreach(child, fn, opaque, true);
|
|
|
|
}
|
2012-04-12 01:30:20 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-09-08 19:38:43 +03:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2012-02-26 02:07:34 +04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2012-01-30 18:55:55 +04:00
|
|
|
void object_ref(Object *obj)
|
|
|
|
{
|
2014-06-06 10:13:36 +04:00
|
|
|
if (!obj) {
|
|
|
|
return;
|
|
|
|
}
|
2015-11-16 19:49:20 +03:00
|
|
|
atomic_inc(&obj->ref);
|
2012-01-30 18:55:55 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void object_unref(Object *obj)
|
|
|
|
{
|
2014-06-06 10:13:36 +04:00
|
|
|
if (!obj) {
|
|
|
|
return;
|
|
|
|
}
|
2015-11-16 19:49:20 +03:00
|
|
|
g_assert_cmpint(obj->ref, >, 0);
|
2012-01-30 18:55:55 +04:00
|
|
|
|
|
|
|
/* parent always holds a reference to its children */
|
2013-07-02 13:36:39 +04:00
|
|
|
if (atomic_fetch_dec(&obj->ref) == 1) {
|
2012-01-30 18:55:55 +04:00
|
|
|
object_finalize(obj);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-05 15:11:51 +04:00
|
|
|
ObjectProperty *
|
|
|
|
object_property_add(Object *obj, const char *name, const char *type,
|
|
|
|
ObjectPropertyAccessor *get,
|
|
|
|
ObjectPropertyAccessor *set,
|
|
|
|
ObjectPropertyRelease *release,
|
|
|
|
void *opaque, Error **errp)
|
2012-01-30 18:55:55 +04:00
|
|
|
{
|
2013-03-25 17:15:13 +04:00
|
|
|
ObjectProperty *prop;
|
2014-08-20 10:55:52 +04:00
|
|
|
size_t name_len = strlen(name);
|
|
|
|
|
|
|
|
if (name_len >= 3 && !memcmp(name + name_len - 3, "[*]", 4)) {
|
|
|
|
int i;
|
|
|
|
ObjectProperty *ret;
|
|
|
|
char *name_no_array = g_strdup(name);
|
|
|
|
|
|
|
|
name_no_array[name_len - 3] = '\0';
|
|
|
|
for (i = 0; ; ++i) {
|
|
|
|
char *full_name = g_strdup_printf("%s[%d]", name_no_array, i);
|
|
|
|
|
|
|
|
ret = object_property_add(obj, full_name, type, get, set,
|
|
|
|
release, opaque, NULL);
|
|
|
|
g_free(full_name);
|
|
|
|
if (ret) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
g_free(name_no_array);
|
|
|
|
return ret;
|
|
|
|
}
|
2013-03-25 17:15:13 +04:00
|
|
|
|
2015-10-13 15:37:45 +03:00
|
|
|
if (g_hash_table_lookup(obj->properties, name) != NULL) {
|
|
|
|
error_setg(errp, "attempt to add duplicate property '%s'"
|
2013-03-25 17:15:13 +04:00
|
|
|
" to object (type '%s')", name,
|
|
|
|
object_get_typename(obj));
|
2015-10-13 15:37:45 +03:00
|
|
|
return NULL;
|
2013-03-25 17:15:13 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
prop = g_malloc0(sizeof(*prop));
|
2012-01-30 18:55:55 +04:00
|
|
|
|
|
|
|
prop->name = g_strdup(name);
|
|
|
|
prop->type = g_strdup(type);
|
|
|
|
|
|
|
|
prop->get = get;
|
|
|
|
prop->set = set;
|
|
|
|
prop->release = release;
|
|
|
|
prop->opaque = opaque;
|
|
|
|
|
2015-10-13 15:37:45 +03:00
|
|
|
g_hash_table_insert(obj->properties, prop->name, prop);
|
2014-06-05 15:11:51 +04:00
|
|
|
return prop;
|
2012-01-30 18:55:55 +04:00
|
|
|
}
|
|
|
|
|
2012-04-12 20:00:18 +04:00
|
|
|
ObjectProperty *object_property_find(Object *obj, const char *name,
|
|
|
|
Error **errp)
|
2012-01-30 18:55:55 +04:00
|
|
|
{
|
|
|
|
ObjectProperty *prop;
|
|
|
|
|
2015-10-13 15:37:45 +03:00
|
|
|
prop = g_hash_table_lookup(obj->properties, name);
|
|
|
|
if (prop) {
|
|
|
|
return prop;
|
2012-01-30 18:55:55 +04:00
|
|
|
}
|
|
|
|
|
2014-03-22 03:42:26 +04:00
|
|
|
error_setg(errp, "Property '.%s' not found", name);
|
2012-01-30 18:55:55 +04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2015-10-13 15:37:40 +03:00
|
|
|
ObjectPropertyIterator *object_property_iter_init(Object *obj)
|
|
|
|
{
|
|
|
|
ObjectPropertyIterator *ret = g_new0(ObjectPropertyIterator, 1);
|
2015-10-13 15:37:45 +03:00
|
|
|
g_hash_table_iter_init(&ret->iter, obj->properties);
|
2015-10-13 15:37:40 +03:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
void object_property_iter_free(ObjectPropertyIterator *iter)
|
|
|
|
{
|
|
|
|
if (!iter) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
g_free(iter);
|
|
|
|
}
|
|
|
|
|
|
|
|
ObjectProperty *object_property_iter_next(ObjectPropertyIterator *iter)
|
|
|
|
{
|
2015-10-13 15:37:45 +03:00
|
|
|
gpointer key, val;
|
|
|
|
if (!g_hash_table_iter_next(&iter->iter, &key, &val)) {
|
|
|
|
return NULL;
|
2015-10-13 15:37:40 +03:00
|
|
|
}
|
2015-10-13 15:37:45 +03:00
|
|
|
return val;
|
2015-10-13 15:37:40 +03:00
|
|
|
}
|
|
|
|
|
2012-01-30 18:55:55 +04:00
|
|
|
void object_property_del(Object *obj, const char *name, Error **errp)
|
|
|
|
{
|
2015-10-13 15:37:45 +03:00
|
|
|
ObjectProperty *prop = g_hash_table_lookup(obj->properties, name);
|
|
|
|
|
|
|
|
if (!prop) {
|
|
|
|
error_setg(errp, "Property '.%s' not found", name);
|
2011-12-24 01:34:39 +04:00
|
|
|
return;
|
|
|
|
}
|
2012-01-30 18:55:55 +04:00
|
|
|
|
2011-12-24 01:34:39 +04:00
|
|
|
if (prop->release) {
|
|
|
|
prop->release(obj, name, prop->opaque);
|
|
|
|
}
|
2015-10-13 15:37:45 +03:00
|
|
|
g_hash_table_remove(obj->properties, name);
|
2012-01-30 18:55:55 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void object_property_get(Object *obj, Visitor *v, const char *name,
|
|
|
|
Error **errp)
|
|
|
|
{
|
2012-04-12 20:00:18 +04:00
|
|
|
ObjectProperty *prop = object_property_find(obj, name, errp);
|
2012-01-30 18:55:55 +04:00
|
|
|
if (prop == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!prop->get) {
|
2015-03-17 13:54:50 +03:00
|
|
|
error_setg(errp, QERR_PERMISSION_DENIED);
|
2012-01-30 18:55:55 +04:00
|
|
|
} else {
|
|
|
|
prop->get(obj, v, prop->opaque, name, errp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void object_property_set(Object *obj, Visitor *v, const char *name,
|
|
|
|
Error **errp)
|
|
|
|
{
|
2012-04-12 20:00:18 +04:00
|
|
|
ObjectProperty *prop = object_property_find(obj, name, errp);
|
2012-01-30 18:55:55 +04:00
|
|
|
if (prop == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!prop->set) {
|
2015-03-17 13:54:50 +03:00
|
|
|
error_setg(errp, QERR_PERMISSION_DENIED);
|
2012-01-30 18:55:55 +04:00
|
|
|
} else {
|
|
|
|
prop->set(obj, v, prop->opaque, name, errp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-01 20:16:22 +04:00
|
|
|
void object_property_set_str(Object *obj, const char *value,
|
|
|
|
const char *name, Error **errp)
|
|
|
|
{
|
|
|
|
QString *qstr = qstring_from_str(value);
|
|
|
|
object_property_set_qobject(obj, QOBJECT(qstr), name, errp);
|
|
|
|
|
|
|
|
QDECREF(qstr);
|
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2015-03-17 13:54:50 +03:00
|
|
|
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name, "string");
|
2012-02-01 20:16:22 +04:00
|
|
|
retval = NULL;
|
|
|
|
} else {
|
|
|
|
retval = g_strdup(qstring_get_str(qstring));
|
|
|
|
}
|
|
|
|
|
|
|
|
QDECREF(qstring);
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2012-02-02 13:51:57 +04:00
|
|
|
void object_property_set_link(Object *obj, Object *value,
|
|
|
|
const char *name, Error **errp)
|
|
|
|
{
|
2014-09-26 09:19:19 +04:00
|
|
|
if (value) {
|
|
|
|
gchar *path = object_get_canonical_path(value);
|
|
|
|
object_property_set_str(obj, path, name, errp);
|
|
|
|
g_free(path);
|
|
|
|
} else {
|
|
|
|
object_property_set_str(obj, "", name, errp);
|
|
|
|
}
|
2012-02-02 13:51:57 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2015-03-16 10:57:47 +03:00
|
|
|
error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
|
|
|
|
"Device '%s' not found", str);
|
2012-02-02 13:51:57 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
g_free(str);
|
|
|
|
return target;
|
|
|
|
}
|
|
|
|
|
2012-02-01 20:16:22 +04:00
|
|
|
void object_property_set_bool(Object *obj, bool value,
|
|
|
|
const char *name, Error **errp)
|
|
|
|
{
|
2015-05-16 01:24:59 +03:00
|
|
|
QBool *qbool = qbool_from_bool(value);
|
2012-02-01 20:16:22 +04:00
|
|
|
object_property_set_qobject(obj, QOBJECT(qbool), name, errp);
|
|
|
|
|
|
|
|
QDECREF(qbool);
|
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2015-03-17 13:54:50 +03:00
|
|
|
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name, "boolean");
|
2012-02-01 20:16:22 +04:00
|
|
|
retval = false;
|
|
|
|
} else {
|
2015-05-16 01:24:59 +03:00
|
|
|
retval = qbool_get_bool(qbool);
|
2012-02-01 20:16:22 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
QDECREF(qbool);
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
void object_property_set_int(Object *obj, int64_t value,
|
|
|
|
const char *name, Error **errp)
|
|
|
|
{
|
|
|
|
QInt *qint = qint_from_int(value);
|
|
|
|
object_property_set_qobject(obj, QOBJECT(qint), name, errp);
|
|
|
|
|
|
|
|
QDECREF(qint);
|
|
|
|
}
|
|
|
|
|
|
|
|
int64_t object_property_get_int(Object *obj, const char *name,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
QObject *ret = object_property_get_qobject(obj, name, errp);
|
|
|
|
QInt *qint;
|
|
|
|
int64_t retval;
|
|
|
|
|
|
|
|
if (!ret) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
qint = qobject_to_qint(ret);
|
|
|
|
if (!qint) {
|
2015-03-17 13:54:50 +03:00
|
|
|
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name, "int");
|
2012-02-01 20:16:22 +04:00
|
|
|
retval = -1;
|
|
|
|
} else {
|
|
|
|
retval = qint_get_int(qint);
|
|
|
|
}
|
|
|
|
|
|
|
|
QDECREF(qint);
|
|
|
|
return retval;
|
2012-02-09 12:52:59 +04:00
|
|
|
}
|
|
|
|
|
qom: Add an object_property_add_enum() helper function
A QOM property can be parsed as enum using the visit_type_enum()
helper function, but this forces callers to use the more complex
generic object_property_add() method when registering it. It
also requires that users of that object have access to the
string map when they want to read the property value.
This patch introduces a specialized object_property_add_enum()
method which simplifies the use of enum properties, so the
setters/getters directly get passed the int value.
typedef enum {
MYDEV_TYPE_FROG,
MYDEV_TYPE_ALLIGATOR,
MYDEV_TYPE_PLATYPUS,
MYDEV_TYPE_LAST
} MyDevType;
Then provide a table of enum <-> string mappings
static const char *const mydevtypemap[MYDEV_TYPE_LAST + 1] = {
[MYDEV_TYPE_FROG] = "frog",
[MYDEV_TYPE_ALLIGATOR] = "alligator",
[MYDEV_TYPE_PLATYPUS] = "platypus",
[MYDEV_TYPE_LAST] = NULL,
};
Assuming an object struct of
typedef struct {
Object parent_obj;
MyDevType devtype;
...other fields...
} MyDev;
The property can then be registered as follows:
static int mydev_prop_get_devtype(Object *obj,
Error **errp G_GNUC_UNUSED)
{
MyDev *dev = MYDEV(obj);
return dev->devtype;
}
static void mydev_prop_set_devtype(Object *obj,
int value,
Error **errp G_GNUC_UNUSED)
{
MyDev *dev = MYDEV(obj);
dev->devtype = value;
}
object_property_add_enum(obj, "devtype",
mydevtypemap, "MyDevType",
mydev_prop_get_devtype,
mydev_prop_set_devtype,
NULL);
Note there is no need to check the range of 'value' in
the setter, because the string->enum conversion code will
have already done that and reported an error as required.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Andreas Färber <afaerber@suse.de>
2015-05-13 19:14:08 +03:00
|
|
|
typedef struct EnumProperty {
|
|
|
|
const char * const *strings;
|
|
|
|
int (*get)(Object *, Error **);
|
|
|
|
void (*set)(Object *, int, Error **);
|
|
|
|
} EnumProperty;
|
|
|
|
|
2014-05-14 13:43:33 +04:00
|
|
|
int object_property_get_enum(Object *obj, const char *name,
|
2015-05-27 18:07:56 +03:00
|
|
|
const char *typename, Error **errp)
|
2014-05-14 13:43:33 +04:00
|
|
|
{
|
2015-08-25 21:00:45 +03:00
|
|
|
Error *err = NULL;
|
2014-05-14 13:43:33 +04:00
|
|
|
StringOutputVisitor *sov;
|
|
|
|
StringInputVisitor *siv;
|
2014-08-18 10:46:34 +04:00
|
|
|
char *str;
|
2014-05-14 13:43:33 +04:00
|
|
|
int ret;
|
2015-05-27 18:07:56 +03:00
|
|
|
ObjectProperty *prop = object_property_find(obj, name, errp);
|
|
|
|
EnumProperty *enumprop;
|
|
|
|
|
|
|
|
if (prop == NULL) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
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 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
enumprop = prop->opaque;
|
2014-05-14 13:43:33 +04:00
|
|
|
|
|
|
|
sov = string_output_visitor_new(false);
|
2015-08-25 21:00:45 +03:00
|
|
|
object_property_get(obj, string_output_get_visitor(sov), name, &err);
|
|
|
|
if (err) {
|
|
|
|
error_propagate(errp, err);
|
|
|
|
string_output_visitor_cleanup(sov);
|
|
|
|
return 0;
|
|
|
|
}
|
2014-08-18 10:46:34 +04:00
|
|
|
str = string_output_get_string(sov);
|
|
|
|
siv = string_input_visitor_new(str);
|
2014-05-14 13:43:33 +04:00
|
|
|
string_output_visitor_cleanup(sov);
|
|
|
|
visit_type_enum(string_input_get_visitor(siv),
|
2015-05-27 18:07:56 +03:00
|
|
|
&ret, enumprop->strings, NULL, name, errp);
|
2014-08-18 10:46:34 +04:00
|
|
|
|
|
|
|
g_free(str);
|
2014-05-14 13:43:33 +04:00
|
|
|
string_input_visitor_cleanup(siv);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
void object_property_get_uint16List(Object *obj, const char *name,
|
|
|
|
uint16List **list, Error **errp)
|
|
|
|
{
|
2015-08-25 21:00:45 +03:00
|
|
|
Error *err = NULL;
|
2014-05-14 13:43:33 +04:00
|
|
|
StringOutputVisitor *ov;
|
|
|
|
StringInputVisitor *iv;
|
2014-08-18 10:46:34 +04:00
|
|
|
char *str;
|
2014-05-14 13:43:33 +04:00
|
|
|
|
|
|
|
ov = string_output_visitor_new(false);
|
|
|
|
object_property_get(obj, string_output_get_visitor(ov),
|
2015-08-25 21:00:45 +03:00
|
|
|
name, &err);
|
|
|
|
if (err) {
|
|
|
|
error_propagate(errp, err);
|
|
|
|
goto out;
|
|
|
|
}
|
2014-08-18 10:46:34 +04:00
|
|
|
str = string_output_get_string(ov);
|
|
|
|
iv = string_input_visitor_new(str);
|
2014-05-14 13:43:33 +04:00
|
|
|
visit_type_uint16List(string_input_get_visitor(iv),
|
|
|
|
list, NULL, errp);
|
2014-08-18 10:46:34 +04:00
|
|
|
|
|
|
|
g_free(str);
|
2014-05-14 13:43:33 +04:00
|
|
|
string_input_visitor_cleanup(iv);
|
2015-08-25 21:00:45 +03:00
|
|
|
out:
|
|
|
|
string_output_visitor_cleanup(ov);
|
2014-05-14 13:43:33 +04:00
|
|
|
}
|
|
|
|
|
2012-02-09 12:52:59 +04:00
|
|
|
void object_property_parse(Object *obj, const char *string,
|
|
|
|
const char *name, Error **errp)
|
|
|
|
{
|
2015-09-30 01:21:09 +03:00
|
|
|
StringInputVisitor *siv;
|
|
|
|
siv = string_input_visitor_new(string);
|
|
|
|
object_property_set(obj, string_input_get_visitor(siv), name, errp);
|
2012-02-09 12:52:59 +04:00
|
|
|
|
2015-09-30 01:21:09 +03:00
|
|
|
string_input_visitor_cleanup(siv);
|
2012-02-09 12:52:59 +04:00
|
|
|
}
|
|
|
|
|
2014-02-08 14:01:50 +04:00
|
|
|
char *object_property_print(Object *obj, const char *name, bool human,
|
2012-02-09 12:52:59 +04:00
|
|
|
Error **errp)
|
|
|
|
{
|
2015-09-30 01:21:09 +03:00
|
|
|
StringOutputVisitor *sov;
|
2014-09-27 09:13:55 +04:00
|
|
|
char *string = NULL;
|
|
|
|
Error *local_err = NULL;
|
2012-02-09 12:52:59 +04:00
|
|
|
|
2015-09-30 01:21:09 +03:00
|
|
|
sov = string_output_visitor_new(human);
|
|
|
|
object_property_get(obj, string_output_get_visitor(sov), name, &local_err);
|
2014-09-27 09:13:55 +04:00
|
|
|
if (local_err) {
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2015-09-30 01:21:09 +03:00
|
|
|
string = string_output_get_string(sov);
|
2014-09-27 09:13:55 +04:00
|
|
|
|
|
|
|
out:
|
2015-09-30 01:21:09 +03:00
|
|
|
string_output_visitor_cleanup(sov);
|
2012-02-09 12:52:59 +04:00
|
|
|
return string;
|
2012-02-01 20:16:22 +04:00
|
|
|
}
|
|
|
|
|
2012-01-30 18:55:55 +04:00
|
|
|
const char *object_property_get_type(Object *obj, const char *name, Error **errp)
|
|
|
|
{
|
2012-04-12 20:00:18 +04:00
|
|
|
ObjectProperty *prop = object_property_find(obj, name, errp);
|
2012-01-30 18:55:55 +04:00
|
|
|
if (prop == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return prop->type;
|
|
|
|
}
|
|
|
|
|
|
|
|
Object *object_get_root(void)
|
|
|
|
{
|
2011-12-23 19:08:05 +04:00
|
|
|
static Object *root;
|
2012-01-30 18:55:55 +04:00
|
|
|
|
2011-12-23 19:08:05 +04:00
|
|
|
if (!root) {
|
|
|
|
root = object_new("container");
|
2012-01-30 18:55:55 +04:00
|
|
|
}
|
|
|
|
|
2011-12-23 19:08:05 +04:00
|
|
|
return root;
|
2012-01-30 18:55:55 +04:00
|
|
|
}
|
|
|
|
|
2015-05-13 19:14:05 +03:00
|
|
|
Object *object_get_objects_root(void)
|
|
|
|
{
|
|
|
|
return container_get(object_get_root(), "/objects");
|
|
|
|
}
|
|
|
|
|
2012-01-30 18:55:55 +04:00
|
|
|
static void object_get_child_property(Object *obj, Visitor *v, void *opaque,
|
|
|
|
const char *name, Error **errp)
|
|
|
|
{
|
|
|
|
Object *child = opaque;
|
|
|
|
gchar *path;
|
|
|
|
|
|
|
|
path = object_get_canonical_path(child);
|
|
|
|
visit_type_str(v, &path, name, errp);
|
|
|
|
g_free(path);
|
|
|
|
}
|
|
|
|
|
2014-06-05 15:11:51 +04:00
|
|
|
static Object *object_resolve_child_property(Object *parent, void *opaque, const gchar *part)
|
|
|
|
{
|
|
|
|
return opaque;
|
|
|
|
}
|
|
|
|
|
2011-12-23 18:47:39 +04:00
|
|
|
static void object_finalize_child_property(Object *obj, const char *name,
|
|
|
|
void *opaque)
|
|
|
|
{
|
|
|
|
Object *child = opaque;
|
|
|
|
|
2014-06-11 13:57:38 +04:00
|
|
|
if (child->class->unparent) {
|
|
|
|
(child->class->unparent)(child);
|
|
|
|
}
|
|
|
|
child->parent = NULL;
|
2011-12-23 18:47:39 +04:00
|
|
|
object_unref(child);
|
|
|
|
}
|
|
|
|
|
2012-01-30 18:55:55 +04:00
|
|
|
void object_property_add_child(Object *obj, const char *name,
|
|
|
|
Object *child, Error **errp)
|
|
|
|
{
|
2013-12-21 02:21:08 +04:00
|
|
|
Error *local_err = NULL;
|
2012-01-30 18:55:55 +04:00
|
|
|
gchar *type;
|
2014-06-05 15:11:51 +04:00
|
|
|
ObjectProperty *op;
|
2012-01-30 18:55:55 +04:00
|
|
|
|
2014-09-26 09:19:52 +04:00
|
|
|
if (child->parent != NULL) {
|
|
|
|
error_setg(errp, "child object is already parented");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-01-30 18:55:55 +04:00
|
|
|
type = g_strdup_printf("child<%s>", object_get_typename(OBJECT(child)));
|
|
|
|
|
2014-06-05 15:11:51 +04:00
|
|
|
op = object_property_add(obj, name, type, object_get_child_property, NULL,
|
|
|
|
object_finalize_child_property, child, &local_err);
|
2013-12-21 02:21:08 +04:00
|
|
|
if (local_err) {
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
goto out;
|
|
|
|
}
|
2014-06-05 15:11:51 +04:00
|
|
|
|
|
|
|
op->resolve = object_resolve_child_property;
|
2012-01-30 18:55:55 +04:00
|
|
|
object_ref(child);
|
|
|
|
child->parent = obj;
|
|
|
|
|
2013-12-21 02:21:08 +04:00
|
|
|
out:
|
2012-01-30 18:55:55 +04:00
|
|
|
g_free(type);
|
|
|
|
}
|
|
|
|
|
2014-03-19 11:58:56 +04:00
|
|
|
void object_property_allow_set_link(Object *obj, const char *name,
|
|
|
|
Object *val, Error **errp)
|
|
|
|
{
|
|
|
|
/* Allow the link to be set, always */
|
|
|
|
}
|
|
|
|
|
qom: Make QOM link property unref optional
Some object_property_add_link() callers expect property deletion to
unref the link property object. Other callers expect to manage the
refcount themselves. The former are currently broken and therefore leak
the link property object.
This patch adds a flags argument to object_property_add_link() so the
caller can specify which refcount behavior they require. The new
OBJ_PROP_LINK_UNREF_ON_RELEASE flag causes the link pointer to be
unreferenced when the property is deleted.
This fixes refcount leaks in qdev.c, xilinx_axidma.c, xilinx_axienet.c,
s390-virtio-bus.c, virtio-pci.c, virtio-rng.c, and ui/console.c.
Rationale for refcount behavior:
* hw/core/qdev.c
- bus children are explicitly unreferenced, don't interfere
- parent_bus is essentially a read-only property that doesn't hold a
refcount, don't unref
- hotplug_handler is leaked, do unref
* hw/dma/xilinx_axidma.c
- rx stream "dma" links are set using set_link, therefore they
need unref
- tx streams are set using set_link, therefore they need unref
* hw/net/xilinx_axienet.c
- same reasoning as hw/dma/xilinx_axidma.c
* hw/pcmcia/pxa2xx.c
- pxa2xx bypasses set_link and therefore does not use refcounts
* hw/s390x/s390-virtio-bus.c
* hw/virtio/virtio-pci.c
* hw/virtio/virtio-rng.c
* ui/console.c
- set_link is used and there is no explicit unref, do unref
Cc: Peter Crosthwaite <peter.crosthwaite@petalogix.com>
Cc: Alexander Graf <agraf@suse.de>
Cc: Anthony Liguori <aliguori@amazon.com>
Cc: "Michael S. Tsirkin" <mst@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Andreas Färber <afaerber@suse.de>
2014-03-19 11:58:55 +04:00
|
|
|
typedef struct {
|
|
|
|
Object **child;
|
2014-03-19 11:58:56 +04:00
|
|
|
void (*check)(Object *, const char *, Object *, Error **);
|
qom: Make QOM link property unref optional
Some object_property_add_link() callers expect property deletion to
unref the link property object. Other callers expect to manage the
refcount themselves. The former are currently broken and therefore leak
the link property object.
This patch adds a flags argument to object_property_add_link() so the
caller can specify which refcount behavior they require. The new
OBJ_PROP_LINK_UNREF_ON_RELEASE flag causes the link pointer to be
unreferenced when the property is deleted.
This fixes refcount leaks in qdev.c, xilinx_axidma.c, xilinx_axienet.c,
s390-virtio-bus.c, virtio-pci.c, virtio-rng.c, and ui/console.c.
Rationale for refcount behavior:
* hw/core/qdev.c
- bus children are explicitly unreferenced, don't interfere
- parent_bus is essentially a read-only property that doesn't hold a
refcount, don't unref
- hotplug_handler is leaked, do unref
* hw/dma/xilinx_axidma.c
- rx stream "dma" links are set using set_link, therefore they
need unref
- tx streams are set using set_link, therefore they need unref
* hw/net/xilinx_axienet.c
- same reasoning as hw/dma/xilinx_axidma.c
* hw/pcmcia/pxa2xx.c
- pxa2xx bypasses set_link and therefore does not use refcounts
* hw/s390x/s390-virtio-bus.c
* hw/virtio/virtio-pci.c
* hw/virtio/virtio-rng.c
* ui/console.c
- set_link is used and there is no explicit unref, do unref
Cc: Peter Crosthwaite <peter.crosthwaite@petalogix.com>
Cc: Alexander Graf <agraf@suse.de>
Cc: Anthony Liguori <aliguori@amazon.com>
Cc: "Michael S. Tsirkin" <mst@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Andreas Färber <afaerber@suse.de>
2014-03-19 11:58:55 +04:00
|
|
|
ObjectPropertyLinkFlags flags;
|
|
|
|
} LinkProperty;
|
|
|
|
|
2012-01-30 18:55:55 +04:00
|
|
|
static void object_get_link_property(Object *obj, Visitor *v, void *opaque,
|
|
|
|
const char *name, Error **errp)
|
|
|
|
{
|
qom: Make QOM link property unref optional
Some object_property_add_link() callers expect property deletion to
unref the link property object. Other callers expect to manage the
refcount themselves. The former are currently broken and therefore leak
the link property object.
This patch adds a flags argument to object_property_add_link() so the
caller can specify which refcount behavior they require. The new
OBJ_PROP_LINK_UNREF_ON_RELEASE flag causes the link pointer to be
unreferenced when the property is deleted.
This fixes refcount leaks in qdev.c, xilinx_axidma.c, xilinx_axienet.c,
s390-virtio-bus.c, virtio-pci.c, virtio-rng.c, and ui/console.c.
Rationale for refcount behavior:
* hw/core/qdev.c
- bus children are explicitly unreferenced, don't interfere
- parent_bus is essentially a read-only property that doesn't hold a
refcount, don't unref
- hotplug_handler is leaked, do unref
* hw/dma/xilinx_axidma.c
- rx stream "dma" links are set using set_link, therefore they
need unref
- tx streams are set using set_link, therefore they need unref
* hw/net/xilinx_axienet.c
- same reasoning as hw/dma/xilinx_axidma.c
* hw/pcmcia/pxa2xx.c
- pxa2xx bypasses set_link and therefore does not use refcounts
* hw/s390x/s390-virtio-bus.c
* hw/virtio/virtio-pci.c
* hw/virtio/virtio-rng.c
* ui/console.c
- set_link is used and there is no explicit unref, do unref
Cc: Peter Crosthwaite <peter.crosthwaite@petalogix.com>
Cc: Alexander Graf <agraf@suse.de>
Cc: Anthony Liguori <aliguori@amazon.com>
Cc: "Michael S. Tsirkin" <mst@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Andreas Färber <afaerber@suse.de>
2014-03-19 11:58:55 +04:00
|
|
|
LinkProperty *lprop = opaque;
|
|
|
|
Object **child = lprop->child;
|
2012-01-30 18:55:55 +04:00
|
|
|
gchar *path;
|
|
|
|
|
|
|
|
if (*child) {
|
|
|
|
path = object_get_canonical_path(*child);
|
|
|
|
visit_type_str(v, &path, name, errp);
|
|
|
|
g_free(path);
|
|
|
|
} else {
|
|
|
|
path = (gchar *)"";
|
|
|
|
visit_type_str(v, &path, name, errp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-19 11:58:53 +04:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
gchar *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) {
|
2015-11-11 09:51:20 +03:00
|
|
|
error_setg(errp, "Path '%s' does not uniquely identify an object",
|
|
|
|
path);
|
2014-03-19 11:58:53 +04:00
|
|
|
} else if (!target) {
|
|
|
|
target = object_resolve_path(path, &ambiguous);
|
|
|
|
if (target || ambiguous) {
|
2015-03-17 13:54:50 +03:00
|
|
|
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name, target_type);
|
2014-03-19 11:58:53 +04:00
|
|
|
} else {
|
2015-03-16 10:57:47 +03:00
|
|
|
error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
|
|
|
|
"Device '%s' not found", path);
|
2014-03-19 11:58:53 +04:00
|
|
|
}
|
|
|
|
target = NULL;
|
|
|
|
}
|
|
|
|
g_free(target_type);
|
|
|
|
|
|
|
|
return target;
|
|
|
|
}
|
|
|
|
|
2012-01-30 18:55:55 +04:00
|
|
|
static void object_set_link_property(Object *obj, Visitor *v, void *opaque,
|
|
|
|
const char *name, Error **errp)
|
|
|
|
{
|
2014-03-19 11:58:54 +04:00
|
|
|
Error *local_err = NULL;
|
qom: Make QOM link property unref optional
Some object_property_add_link() callers expect property deletion to
unref the link property object. Other callers expect to manage the
refcount themselves. The former are currently broken and therefore leak
the link property object.
This patch adds a flags argument to object_property_add_link() so the
caller can specify which refcount behavior they require. The new
OBJ_PROP_LINK_UNREF_ON_RELEASE flag causes the link pointer to be
unreferenced when the property is deleted.
This fixes refcount leaks in qdev.c, xilinx_axidma.c, xilinx_axienet.c,
s390-virtio-bus.c, virtio-pci.c, virtio-rng.c, and ui/console.c.
Rationale for refcount behavior:
* hw/core/qdev.c
- bus children are explicitly unreferenced, don't interfere
- parent_bus is essentially a read-only property that doesn't hold a
refcount, don't unref
- hotplug_handler is leaked, do unref
* hw/dma/xilinx_axidma.c
- rx stream "dma" links are set using set_link, therefore they
need unref
- tx streams are set using set_link, therefore they need unref
* hw/net/xilinx_axienet.c
- same reasoning as hw/dma/xilinx_axidma.c
* hw/pcmcia/pxa2xx.c
- pxa2xx bypasses set_link and therefore does not use refcounts
* hw/s390x/s390-virtio-bus.c
* hw/virtio/virtio-pci.c
* hw/virtio/virtio-rng.c
* ui/console.c
- set_link is used and there is no explicit unref, do unref
Cc: Peter Crosthwaite <peter.crosthwaite@petalogix.com>
Cc: Alexander Graf <agraf@suse.de>
Cc: Anthony Liguori <aliguori@amazon.com>
Cc: "Michael S. Tsirkin" <mst@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Andreas Färber <afaerber@suse.de>
2014-03-19 11:58:55 +04:00
|
|
|
LinkProperty *prop = opaque;
|
|
|
|
Object **child = prop->child;
|
2014-03-19 11:58:54 +04:00
|
|
|
Object *old_target = *child;
|
|
|
|
Object *new_target = NULL;
|
|
|
|
char *path = NULL;
|
2012-01-30 18:55:55 +04:00
|
|
|
|
2014-03-19 11:58:54 +04:00
|
|
|
visit_type_str(v, &path, name, &local_err);
|
2012-01-30 18:55:55 +04:00
|
|
|
|
2014-03-19 11:58:54 +04:00
|
|
|
if (!local_err && strcmp(path, "") != 0) {
|
|
|
|
new_target = object_resolve_link(obj, name, path, &local_err);
|
2012-01-30 18:55:55 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
g_free(path);
|
2014-03-19 11:58:54 +04:00
|
|
|
if (local_err) {
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
return;
|
|
|
|
}
|
2012-02-22 21:22:26 +04:00
|
|
|
|
2014-03-19 11:58:56 +04:00
|
|
|
prop->check(obj, name, new_target, &local_err);
|
|
|
|
if (local_err) {
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-06-06 10:13:36 +04:00
|
|
|
object_ref(new_target);
|
2014-03-19 11:58:54 +04:00
|
|
|
*child = new_target;
|
2014-06-06 10:13:36 +04:00
|
|
|
object_unref(old_target);
|
2012-01-30 18:55:55 +04:00
|
|
|
}
|
|
|
|
|
2014-06-05 15:11:51 +04:00
|
|
|
static Object *object_resolve_link_property(Object *parent, void *opaque, const gchar *part)
|
|
|
|
{
|
|
|
|
LinkProperty *lprop = opaque;
|
|
|
|
|
|
|
|
return *lprop->child;
|
|
|
|
}
|
|
|
|
|
qom: Make QOM link property unref optional
Some object_property_add_link() callers expect property deletion to
unref the link property object. Other callers expect to manage the
refcount themselves. The former are currently broken and therefore leak
the link property object.
This patch adds a flags argument to object_property_add_link() so the
caller can specify which refcount behavior they require. The new
OBJ_PROP_LINK_UNREF_ON_RELEASE flag causes the link pointer to be
unreferenced when the property is deleted.
This fixes refcount leaks in qdev.c, xilinx_axidma.c, xilinx_axienet.c,
s390-virtio-bus.c, virtio-pci.c, virtio-rng.c, and ui/console.c.
Rationale for refcount behavior:
* hw/core/qdev.c
- bus children are explicitly unreferenced, don't interfere
- parent_bus is essentially a read-only property that doesn't hold a
refcount, don't unref
- hotplug_handler is leaked, do unref
* hw/dma/xilinx_axidma.c
- rx stream "dma" links are set using set_link, therefore they
need unref
- tx streams are set using set_link, therefore they need unref
* hw/net/xilinx_axienet.c
- same reasoning as hw/dma/xilinx_axidma.c
* hw/pcmcia/pxa2xx.c
- pxa2xx bypasses set_link and therefore does not use refcounts
* hw/s390x/s390-virtio-bus.c
* hw/virtio/virtio-pci.c
* hw/virtio/virtio-rng.c
* ui/console.c
- set_link is used and there is no explicit unref, do unref
Cc: Peter Crosthwaite <peter.crosthwaite@petalogix.com>
Cc: Alexander Graf <agraf@suse.de>
Cc: Anthony Liguori <aliguori@amazon.com>
Cc: "Michael S. Tsirkin" <mst@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Andreas Färber <afaerber@suse.de>
2014-03-19 11:58:55 +04:00
|
|
|
static void object_release_link_property(Object *obj, const char *name,
|
|
|
|
void *opaque)
|
|
|
|
{
|
|
|
|
LinkProperty *prop = opaque;
|
|
|
|
|
|
|
|
if ((prop->flags & OBJ_PROP_LINK_UNREF_ON_RELEASE) && *prop->child) {
|
|
|
|
object_unref(*prop->child);
|
|
|
|
}
|
|
|
|
g_free(prop);
|
|
|
|
}
|
|
|
|
|
2012-01-30 18:55:55 +04:00
|
|
|
void object_property_add_link(Object *obj, const char *name,
|
|
|
|
const char *type, Object **child,
|
2014-03-19 11:58:56 +04:00
|
|
|
void (*check)(Object *, const char *,
|
|
|
|
Object *, Error **),
|
qom: Make QOM link property unref optional
Some object_property_add_link() callers expect property deletion to
unref the link property object. Other callers expect to manage the
refcount themselves. The former are currently broken and therefore leak
the link property object.
This patch adds a flags argument to object_property_add_link() so the
caller can specify which refcount behavior they require. The new
OBJ_PROP_LINK_UNREF_ON_RELEASE flag causes the link pointer to be
unreferenced when the property is deleted.
This fixes refcount leaks in qdev.c, xilinx_axidma.c, xilinx_axienet.c,
s390-virtio-bus.c, virtio-pci.c, virtio-rng.c, and ui/console.c.
Rationale for refcount behavior:
* hw/core/qdev.c
- bus children are explicitly unreferenced, don't interfere
- parent_bus is essentially a read-only property that doesn't hold a
refcount, don't unref
- hotplug_handler is leaked, do unref
* hw/dma/xilinx_axidma.c
- rx stream "dma" links are set using set_link, therefore they
need unref
- tx streams are set using set_link, therefore they need unref
* hw/net/xilinx_axienet.c
- same reasoning as hw/dma/xilinx_axidma.c
* hw/pcmcia/pxa2xx.c
- pxa2xx bypasses set_link and therefore does not use refcounts
* hw/s390x/s390-virtio-bus.c
* hw/virtio/virtio-pci.c
* hw/virtio/virtio-rng.c
* ui/console.c
- set_link is used and there is no explicit unref, do unref
Cc: Peter Crosthwaite <peter.crosthwaite@petalogix.com>
Cc: Alexander Graf <agraf@suse.de>
Cc: Anthony Liguori <aliguori@amazon.com>
Cc: "Michael S. Tsirkin" <mst@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Andreas Färber <afaerber@suse.de>
2014-03-19 11:58:55 +04:00
|
|
|
ObjectPropertyLinkFlags flags,
|
2012-01-30 18:55:55 +04:00
|
|
|
Error **errp)
|
|
|
|
{
|
qom: Make QOM link property unref optional
Some object_property_add_link() callers expect property deletion to
unref the link property object. Other callers expect to manage the
refcount themselves. The former are currently broken and therefore leak
the link property object.
This patch adds a flags argument to object_property_add_link() so the
caller can specify which refcount behavior they require. The new
OBJ_PROP_LINK_UNREF_ON_RELEASE flag causes the link pointer to be
unreferenced when the property is deleted.
This fixes refcount leaks in qdev.c, xilinx_axidma.c, xilinx_axienet.c,
s390-virtio-bus.c, virtio-pci.c, virtio-rng.c, and ui/console.c.
Rationale for refcount behavior:
* hw/core/qdev.c
- bus children are explicitly unreferenced, don't interfere
- parent_bus is essentially a read-only property that doesn't hold a
refcount, don't unref
- hotplug_handler is leaked, do unref
* hw/dma/xilinx_axidma.c
- rx stream "dma" links are set using set_link, therefore they
need unref
- tx streams are set using set_link, therefore they need unref
* hw/net/xilinx_axienet.c
- same reasoning as hw/dma/xilinx_axidma.c
* hw/pcmcia/pxa2xx.c
- pxa2xx bypasses set_link and therefore does not use refcounts
* hw/s390x/s390-virtio-bus.c
* hw/virtio/virtio-pci.c
* hw/virtio/virtio-rng.c
* ui/console.c
- set_link is used and there is no explicit unref, do unref
Cc: Peter Crosthwaite <peter.crosthwaite@petalogix.com>
Cc: Alexander Graf <agraf@suse.de>
Cc: Anthony Liguori <aliguori@amazon.com>
Cc: "Michael S. Tsirkin" <mst@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Andreas Färber <afaerber@suse.de>
2014-03-19 11:58:55 +04:00
|
|
|
Error *local_err = NULL;
|
|
|
|
LinkProperty *prop = g_malloc(sizeof(*prop));
|
2012-01-30 18:55:55 +04:00
|
|
|
gchar *full_type;
|
2014-06-05 15:11:51 +04:00
|
|
|
ObjectProperty *op;
|
2012-01-30 18:55:55 +04:00
|
|
|
|
qom: Make QOM link property unref optional
Some object_property_add_link() callers expect property deletion to
unref the link property object. Other callers expect to manage the
refcount themselves. The former are currently broken and therefore leak
the link property object.
This patch adds a flags argument to object_property_add_link() so the
caller can specify which refcount behavior they require. The new
OBJ_PROP_LINK_UNREF_ON_RELEASE flag causes the link pointer to be
unreferenced when the property is deleted.
This fixes refcount leaks in qdev.c, xilinx_axidma.c, xilinx_axienet.c,
s390-virtio-bus.c, virtio-pci.c, virtio-rng.c, and ui/console.c.
Rationale for refcount behavior:
* hw/core/qdev.c
- bus children are explicitly unreferenced, don't interfere
- parent_bus is essentially a read-only property that doesn't hold a
refcount, don't unref
- hotplug_handler is leaked, do unref
* hw/dma/xilinx_axidma.c
- rx stream "dma" links are set using set_link, therefore they
need unref
- tx streams are set using set_link, therefore they need unref
* hw/net/xilinx_axienet.c
- same reasoning as hw/dma/xilinx_axidma.c
* hw/pcmcia/pxa2xx.c
- pxa2xx bypasses set_link and therefore does not use refcounts
* hw/s390x/s390-virtio-bus.c
* hw/virtio/virtio-pci.c
* hw/virtio/virtio-rng.c
* ui/console.c
- set_link is used and there is no explicit unref, do unref
Cc: Peter Crosthwaite <peter.crosthwaite@petalogix.com>
Cc: Alexander Graf <agraf@suse.de>
Cc: Anthony Liguori <aliguori@amazon.com>
Cc: "Michael S. Tsirkin" <mst@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Andreas Färber <afaerber@suse.de>
2014-03-19 11:58:55 +04:00
|
|
|
prop->child = child;
|
2014-03-19 11:58:56 +04:00
|
|
|
prop->check = check;
|
qom: Make QOM link property unref optional
Some object_property_add_link() callers expect property deletion to
unref the link property object. Other callers expect to manage the
refcount themselves. The former are currently broken and therefore leak
the link property object.
This patch adds a flags argument to object_property_add_link() so the
caller can specify which refcount behavior they require. The new
OBJ_PROP_LINK_UNREF_ON_RELEASE flag causes the link pointer to be
unreferenced when the property is deleted.
This fixes refcount leaks in qdev.c, xilinx_axidma.c, xilinx_axienet.c,
s390-virtio-bus.c, virtio-pci.c, virtio-rng.c, and ui/console.c.
Rationale for refcount behavior:
* hw/core/qdev.c
- bus children are explicitly unreferenced, don't interfere
- parent_bus is essentially a read-only property that doesn't hold a
refcount, don't unref
- hotplug_handler is leaked, do unref
* hw/dma/xilinx_axidma.c
- rx stream "dma" links are set using set_link, therefore they
need unref
- tx streams are set using set_link, therefore they need unref
* hw/net/xilinx_axienet.c
- same reasoning as hw/dma/xilinx_axidma.c
* hw/pcmcia/pxa2xx.c
- pxa2xx bypasses set_link and therefore does not use refcounts
* hw/s390x/s390-virtio-bus.c
* hw/virtio/virtio-pci.c
* hw/virtio/virtio-rng.c
* ui/console.c
- set_link is used and there is no explicit unref, do unref
Cc: Peter Crosthwaite <peter.crosthwaite@petalogix.com>
Cc: Alexander Graf <agraf@suse.de>
Cc: Anthony Liguori <aliguori@amazon.com>
Cc: "Michael S. Tsirkin" <mst@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Andreas Färber <afaerber@suse.de>
2014-03-19 11:58:55 +04:00
|
|
|
prop->flags = flags;
|
|
|
|
|
2012-01-30 18:55:55 +04:00
|
|
|
full_type = g_strdup_printf("link<%s>", type);
|
|
|
|
|
2014-06-05 15:11:51 +04:00
|
|
|
op = object_property_add(obj, name, full_type,
|
|
|
|
object_get_link_property,
|
|
|
|
check ? object_set_link_property : NULL,
|
|
|
|
object_release_link_property,
|
|
|
|
prop,
|
|
|
|
&local_err);
|
qom: Make QOM link property unref optional
Some object_property_add_link() callers expect property deletion to
unref the link property object. Other callers expect to manage the
refcount themselves. The former are currently broken and therefore leak
the link property object.
This patch adds a flags argument to object_property_add_link() so the
caller can specify which refcount behavior they require. The new
OBJ_PROP_LINK_UNREF_ON_RELEASE flag causes the link pointer to be
unreferenced when the property is deleted.
This fixes refcount leaks in qdev.c, xilinx_axidma.c, xilinx_axienet.c,
s390-virtio-bus.c, virtio-pci.c, virtio-rng.c, and ui/console.c.
Rationale for refcount behavior:
* hw/core/qdev.c
- bus children are explicitly unreferenced, don't interfere
- parent_bus is essentially a read-only property that doesn't hold a
refcount, don't unref
- hotplug_handler is leaked, do unref
* hw/dma/xilinx_axidma.c
- rx stream "dma" links are set using set_link, therefore they
need unref
- tx streams are set using set_link, therefore they need unref
* hw/net/xilinx_axienet.c
- same reasoning as hw/dma/xilinx_axidma.c
* hw/pcmcia/pxa2xx.c
- pxa2xx bypasses set_link and therefore does not use refcounts
* hw/s390x/s390-virtio-bus.c
* hw/virtio/virtio-pci.c
* hw/virtio/virtio-rng.c
* ui/console.c
- set_link is used and there is no explicit unref, do unref
Cc: Peter Crosthwaite <peter.crosthwaite@petalogix.com>
Cc: Alexander Graf <agraf@suse.de>
Cc: Anthony Liguori <aliguori@amazon.com>
Cc: "Michael S. Tsirkin" <mst@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Andreas Färber <afaerber@suse.de>
2014-03-19 11:58:55 +04:00
|
|
|
if (local_err) {
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
g_free(prop);
|
2014-06-05 15:11:51 +04:00
|
|
|
goto out;
|
qom: Make QOM link property unref optional
Some object_property_add_link() callers expect property deletion to
unref the link property object. Other callers expect to manage the
refcount themselves. The former are currently broken and therefore leak
the link property object.
This patch adds a flags argument to object_property_add_link() so the
caller can specify which refcount behavior they require. The new
OBJ_PROP_LINK_UNREF_ON_RELEASE flag causes the link pointer to be
unreferenced when the property is deleted.
This fixes refcount leaks in qdev.c, xilinx_axidma.c, xilinx_axienet.c,
s390-virtio-bus.c, virtio-pci.c, virtio-rng.c, and ui/console.c.
Rationale for refcount behavior:
* hw/core/qdev.c
- bus children are explicitly unreferenced, don't interfere
- parent_bus is essentially a read-only property that doesn't hold a
refcount, don't unref
- hotplug_handler is leaked, do unref
* hw/dma/xilinx_axidma.c
- rx stream "dma" links are set using set_link, therefore they
need unref
- tx streams are set using set_link, therefore they need unref
* hw/net/xilinx_axienet.c
- same reasoning as hw/dma/xilinx_axidma.c
* hw/pcmcia/pxa2xx.c
- pxa2xx bypasses set_link and therefore does not use refcounts
* hw/s390x/s390-virtio-bus.c
* hw/virtio/virtio-pci.c
* hw/virtio/virtio-rng.c
* ui/console.c
- set_link is used and there is no explicit unref, do unref
Cc: Peter Crosthwaite <peter.crosthwaite@petalogix.com>
Cc: Alexander Graf <agraf@suse.de>
Cc: Anthony Liguori <aliguori@amazon.com>
Cc: "Michael S. Tsirkin" <mst@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Andreas Färber <afaerber@suse.de>
2014-03-19 11:58:55 +04:00
|
|
|
}
|
2012-01-30 18:55:55 +04:00
|
|
|
|
2014-06-05 15:11:51 +04:00
|
|
|
op->resolve = object_resolve_link_property;
|
|
|
|
|
|
|
|
out:
|
2012-01-30 18:55:55 +04:00
|
|
|
g_free(full_type);
|
|
|
|
}
|
|
|
|
|
2015-05-05 19:29:00 +03:00
|
|
|
void object_property_add_const_link(Object *obj, const char *name,
|
|
|
|
Object *target, Error **errp)
|
|
|
|
{
|
|
|
|
char *link_type;
|
|
|
|
ObjectProperty *op;
|
|
|
|
|
|
|
|
link_type = g_strdup_printf("link<%s>", object_get_typename(target));
|
|
|
|
op = object_property_add(obj, name, link_type,
|
|
|
|
object_get_child_property, NULL,
|
|
|
|
NULL, target, errp);
|
|
|
|
if (op != NULL) {
|
|
|
|
op->resolve = object_resolve_child_property;
|
|
|
|
}
|
|
|
|
g_free(link_type);
|
|
|
|
}
|
|
|
|
|
2014-03-03 14:30:02 +04:00
|
|
|
gchar *object_get_canonical_path_component(Object *obj)
|
|
|
|
{
|
|
|
|
ObjectProperty *prop = NULL;
|
2015-10-13 15:37:45 +03:00
|
|
|
GHashTableIter iter;
|
2014-03-03 14:30:02 +04:00
|
|
|
|
|
|
|
g_assert(obj);
|
|
|
|
g_assert(obj->parent != NULL);
|
|
|
|
|
2015-10-13 15:37:45 +03:00
|
|
|
g_hash_table_iter_init(&iter, obj->parent->properties);
|
|
|
|
while (g_hash_table_iter_next(&iter, NULL, (gpointer *)&prop)) {
|
2014-03-03 14:30:02 +04:00
|
|
|
if (!object_property_is_child(prop)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (prop->opaque == obj) {
|
|
|
|
return g_strdup(prop->name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* obj had a parent but was not a child, should never happen */
|
|
|
|
g_assert_not_reached();
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-01-30 18:55:55 +04:00
|
|
|
gchar *object_get_canonical_path(Object *obj)
|
|
|
|
{
|
|
|
|
Object *root = object_get_root();
|
2014-03-03 14:30:02 +04:00
|
|
|
char *newpath, *path = NULL;
|
2012-01-30 18:55:55 +04:00
|
|
|
|
|
|
|
while (obj != root) {
|
2014-03-03 14:30:02 +04:00
|
|
|
char *component = object_get_canonical_path_component(obj);
|
2012-01-30 18:55:55 +04:00
|
|
|
|
2014-03-03 14:30:02 +04:00
|
|
|
if (path) {
|
|
|
|
newpath = g_strdup_printf("%s/%s", component, path);
|
|
|
|
g_free(component);
|
|
|
|
g_free(path);
|
|
|
|
path = newpath;
|
|
|
|
} else {
|
|
|
|
path = component;
|
2012-01-30 18:55:55 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
obj = obj->parent;
|
|
|
|
}
|
|
|
|
|
2014-03-03 14:30:02 +04:00
|
|
|
newpath = g_strdup_printf("/%s", path ? path : "");
|
2012-01-30 18:55:55 +04:00
|
|
|
g_free(path);
|
|
|
|
|
|
|
|
return newpath;
|
|
|
|
}
|
|
|
|
|
2013-01-15 05:55:10 +04:00
|
|
|
Object *object_resolve_path_component(Object *parent, const gchar *part)
|
2012-03-27 20:38:45 +04:00
|
|
|
{
|
2012-04-12 20:00:18 +04:00
|
|
|
ObjectProperty *prop = object_property_find(parent, part, NULL);
|
2012-03-27 20:38:45 +04:00
|
|
|
if (prop == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-06-05 15:11:51 +04:00
|
|
|
if (prop->resolve) {
|
|
|
|
return prop->resolve(parent, prop->opaque, part);
|
2012-03-27 20:38:45 +04:00
|
|
|
} else {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-01-30 18:55:55 +04:00
|
|
|
static Object *object_resolve_abs_path(Object *parent,
|
|
|
|
gchar **parts,
|
2012-02-03 14:21:01 +04:00
|
|
|
const char *typename,
|
2012-01-30 18:55:55 +04:00
|
|
|
int index)
|
|
|
|
{
|
|
|
|
Object *child;
|
|
|
|
|
|
|
|
if (parts[index] == NULL) {
|
2012-02-03 14:21:01 +04:00
|
|
|
return object_dynamic_cast(parent, typename);
|
2012-01-30 18:55:55 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (strcmp(parts[index], "") == 0) {
|
2012-02-03 14:21:01 +04:00
|
|
|
return object_resolve_abs_path(parent, parts, typename, index + 1);
|
2012-01-30 18:55:55 +04:00
|
|
|
}
|
|
|
|
|
2012-03-27 20:38:45 +04:00
|
|
|
child = object_resolve_path_component(parent, parts[index]);
|
2012-01-30 18:55:55 +04:00
|
|
|
if (!child) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-02-03 14:21:01 +04:00
|
|
|
return object_resolve_abs_path(child, parts, typename, index + 1);
|
2012-01-30 18:55:55 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static Object *object_resolve_partial_path(Object *parent,
|
|
|
|
gchar **parts,
|
2012-02-03 14:21:01 +04:00
|
|
|
const char *typename,
|
2012-01-30 18:55:55 +04:00
|
|
|
bool *ambiguous)
|
|
|
|
{
|
|
|
|
Object *obj;
|
2015-10-13 15:37:45 +03:00
|
|
|
GHashTableIter iter;
|
2012-01-30 18:55:55 +04:00
|
|
|
ObjectProperty *prop;
|
|
|
|
|
2012-02-03 14:21:01 +04:00
|
|
|
obj = object_resolve_abs_path(parent, parts, typename, 0);
|
2012-01-30 18:55:55 +04:00
|
|
|
|
2015-10-13 15:37:45 +03:00
|
|
|
g_hash_table_iter_init(&iter, parent->properties);
|
|
|
|
while (g_hash_table_iter_next(&iter, NULL, (gpointer *)&prop)) {
|
2012-01-30 18:55:55 +04:00
|
|
|
Object *found;
|
|
|
|
|
2012-05-27 02:32:40 +04:00
|
|
|
if (!object_property_is_child(prop)) {
|
2012-01-30 18:55:55 +04:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2012-02-03 14:21:01 +04:00
|
|
|
found = object_resolve_partial_path(prop->opaque, parts,
|
|
|
|
typename, ambiguous);
|
2012-01-30 18:55:55 +04:00
|
|
|
if (found) {
|
|
|
|
if (obj) {
|
|
|
|
if (ambiguous) {
|
|
|
|
*ambiguous = true;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
obj = found;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ambiguous && *ambiguous) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
2012-02-03 14:21:01 +04:00
|
|
|
Object *object_resolve_path_type(const char *path, const char *typename,
|
|
|
|
bool *ambiguous)
|
2012-01-30 18:55:55 +04:00
|
|
|
{
|
|
|
|
Object *obj;
|
|
|
|
gchar **parts;
|
|
|
|
|
|
|
|
parts = g_strsplit(path, "/", 0);
|
2013-04-18 20:44:02 +04:00
|
|
|
assert(parts);
|
2012-01-30 18:55:55 +04:00
|
|
|
|
2013-04-18 20:44:02 +04:00
|
|
|
if (parts[0] == NULL || strcmp(parts[0], "") != 0) {
|
2012-01-30 18:55:55 +04:00
|
|
|
if (ambiguous) {
|
|
|
|
*ambiguous = false;
|
|
|
|
}
|
2012-02-03 14:21:01 +04:00
|
|
|
obj = object_resolve_partial_path(object_get_root(), parts,
|
|
|
|
typename, ambiguous);
|
2012-01-30 18:55:55 +04:00
|
|
|
} else {
|
2012-02-03 14:21:01 +04:00
|
|
|
obj = object_resolve_abs_path(object_get_root(), parts, typename, 1);
|
2012-01-30 18:55:55 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
g_strfreev(parts);
|
|
|
|
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
2012-02-03 14:21:01 +04:00
|
|
|
Object *object_resolve_path(const char *path, bool *ambiguous)
|
|
|
|
{
|
|
|
|
return object_resolve_path_type(path, TYPE_OBJECT, ambiguous);
|
|
|
|
}
|
|
|
|
|
2012-01-30 18:55:55 +04:00
|
|
|
typedef struct StringProperty
|
|
|
|
{
|
|
|
|
char *(*get)(Object *, Error **);
|
|
|
|
void (*set)(Object *, const char *, Error **);
|
|
|
|
} StringProperty;
|
|
|
|
|
2012-02-01 20:16:22 +04:00
|
|
|
static void property_get_str(Object *obj, Visitor *v, void *opaque,
|
|
|
|
const char *name, Error **errp)
|
2012-01-30 18:55:55 +04:00
|
|
|
{
|
|
|
|
StringProperty *prop = opaque;
|
|
|
|
char *value;
|
2015-08-25 21:00:46 +03:00
|
|
|
Error *err = NULL;
|
2012-01-30 18:55:55 +04:00
|
|
|
|
2015-08-25 21:00:46 +03:00
|
|
|
value = prop->get(obj, &err);
|
|
|
|
if (err) {
|
|
|
|
error_propagate(errp, err);
|
|
|
|
return;
|
2012-01-30 18:55:55 +04:00
|
|
|
}
|
2015-08-25 21:00:46 +03:00
|
|
|
|
|
|
|
visit_type_str(v, &value, name, errp);
|
|
|
|
g_free(value);
|
2012-01-30 18:55:55 +04:00
|
|
|
}
|
|
|
|
|
2012-02-01 20:16:22 +04:00
|
|
|
static void property_set_str(Object *obj, Visitor *v, void *opaque,
|
|
|
|
const char *name, Error **errp)
|
2012-01-30 18:55:55 +04:00
|
|
|
{
|
|
|
|
StringProperty *prop = opaque;
|
|
|
|
char *value;
|
|
|
|
Error *local_err = NULL;
|
|
|
|
|
|
|
|
visit_type_str(v, &value, name, &local_err);
|
|
|
|
if (local_err) {
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
prop->set(obj, value, errp);
|
|
|
|
g_free(value);
|
|
|
|
}
|
|
|
|
|
2012-02-01 20:16:22 +04:00
|
|
|
static void property_release_str(Object *obj, const char *name,
|
|
|
|
void *opaque)
|
2012-01-30 18:55:55 +04:00
|
|
|
{
|
|
|
|
StringProperty *prop = opaque;
|
|
|
|
g_free(prop);
|
|
|
|
}
|
|
|
|
|
|
|
|
void object_property_add_str(Object *obj, const char *name,
|
|
|
|
char *(*get)(Object *, Error **),
|
|
|
|
void (*set)(Object *, const char *, Error **),
|
|
|
|
Error **errp)
|
|
|
|
{
|
2014-03-04 18:28:18 +04:00
|
|
|
Error *local_err = NULL;
|
2012-01-30 18:55:55 +04:00
|
|
|
StringProperty *prop = g_malloc0(sizeof(*prop));
|
|
|
|
|
|
|
|
prop->get = get;
|
|
|
|
prop->set = set;
|
|
|
|
|
|
|
|
object_property_add(obj, name, "string",
|
2012-02-01 20:16:22 +04:00
|
|
|
get ? property_get_str : NULL,
|
|
|
|
set ? property_set_str : NULL,
|
|
|
|
property_release_str,
|
2014-03-04 18:28:18 +04:00
|
|
|
prop, &local_err);
|
|
|
|
if (local_err) {
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
g_free(prop);
|
|
|
|
}
|
2012-01-30 18:55:55 +04:00
|
|
|
}
|
2012-03-31 18:45:54 +04:00
|
|
|
|
2012-06-25 19:32:46 +04:00
|
|
|
typedef struct BoolProperty
|
|
|
|
{
|
|
|
|
bool (*get)(Object *, Error **);
|
|
|
|
void (*set)(Object *, bool, Error **);
|
|
|
|
} BoolProperty;
|
|
|
|
|
|
|
|
static void property_get_bool(Object *obj, Visitor *v, void *opaque,
|
|
|
|
const char *name, Error **errp)
|
|
|
|
{
|
|
|
|
BoolProperty *prop = opaque;
|
|
|
|
bool value;
|
2015-08-25 21:00:45 +03:00
|
|
|
Error *err = NULL;
|
|
|
|
|
|
|
|
value = prop->get(obj, &err);
|
|
|
|
if (err) {
|
|
|
|
error_propagate(errp, err);
|
|
|
|
return;
|
|
|
|
}
|
2012-06-25 19:32:46 +04:00
|
|
|
|
|
|
|
visit_type_bool(v, &value, name, errp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void property_set_bool(Object *obj, Visitor *v, void *opaque,
|
|
|
|
const char *name, Error **errp)
|
|
|
|
{
|
|
|
|
BoolProperty *prop = opaque;
|
|
|
|
bool value;
|
|
|
|
Error *local_err = NULL;
|
|
|
|
|
|
|
|
visit_type_bool(v, &value, name, &local_err);
|
|
|
|
if (local_err) {
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
prop->set(obj, value, errp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void property_release_bool(Object *obj, const char *name,
|
|
|
|
void *opaque)
|
|
|
|
{
|
|
|
|
BoolProperty *prop = opaque;
|
|
|
|
g_free(prop);
|
|
|
|
}
|
|
|
|
|
|
|
|
void object_property_add_bool(Object *obj, const char *name,
|
|
|
|
bool (*get)(Object *, Error **),
|
|
|
|
void (*set)(Object *, bool, Error **),
|
|
|
|
Error **errp)
|
|
|
|
{
|
2014-03-04 18:28:18 +04:00
|
|
|
Error *local_err = NULL;
|
2012-06-25 19:32:46 +04:00
|
|
|
BoolProperty *prop = g_malloc0(sizeof(*prop));
|
|
|
|
|
|
|
|
prop->get = get;
|
|
|
|
prop->set = set;
|
|
|
|
|
|
|
|
object_property_add(obj, name, "bool",
|
|
|
|
get ? property_get_bool : NULL,
|
|
|
|
set ? property_set_bool : NULL,
|
|
|
|
property_release_bool,
|
2014-03-04 18:28:18 +04:00
|
|
|
prop, &local_err);
|
|
|
|
if (local_err) {
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
g_free(prop);
|
|
|
|
}
|
2012-06-25 19:32:46 +04:00
|
|
|
}
|
|
|
|
|
qom: Add an object_property_add_enum() helper function
A QOM property can be parsed as enum using the visit_type_enum()
helper function, but this forces callers to use the more complex
generic object_property_add() method when registering it. It
also requires that users of that object have access to the
string map when they want to read the property value.
This patch introduces a specialized object_property_add_enum()
method which simplifies the use of enum properties, so the
setters/getters directly get passed the int value.
typedef enum {
MYDEV_TYPE_FROG,
MYDEV_TYPE_ALLIGATOR,
MYDEV_TYPE_PLATYPUS,
MYDEV_TYPE_LAST
} MyDevType;
Then provide a table of enum <-> string mappings
static const char *const mydevtypemap[MYDEV_TYPE_LAST + 1] = {
[MYDEV_TYPE_FROG] = "frog",
[MYDEV_TYPE_ALLIGATOR] = "alligator",
[MYDEV_TYPE_PLATYPUS] = "platypus",
[MYDEV_TYPE_LAST] = NULL,
};
Assuming an object struct of
typedef struct {
Object parent_obj;
MyDevType devtype;
...other fields...
} MyDev;
The property can then be registered as follows:
static int mydev_prop_get_devtype(Object *obj,
Error **errp G_GNUC_UNUSED)
{
MyDev *dev = MYDEV(obj);
return dev->devtype;
}
static void mydev_prop_set_devtype(Object *obj,
int value,
Error **errp G_GNUC_UNUSED)
{
MyDev *dev = MYDEV(obj);
dev->devtype = value;
}
object_property_add_enum(obj, "devtype",
mydevtypemap, "MyDevType",
mydev_prop_get_devtype,
mydev_prop_set_devtype,
NULL);
Note there is no need to check the range of 'value' in
the setter, because the string->enum conversion code will
have already done that and reported an error as required.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Andreas Färber <afaerber@suse.de>
2015-05-13 19:14:08 +03:00
|
|
|
static void property_get_enum(Object *obj, Visitor *v, void *opaque,
|
|
|
|
const char *name, Error **errp)
|
|
|
|
{
|
|
|
|
EnumProperty *prop = opaque;
|
|
|
|
int value;
|
2015-08-25 21:00:45 +03:00
|
|
|
Error *err = NULL;
|
|
|
|
|
|
|
|
value = prop->get(obj, &err);
|
|
|
|
if (err) {
|
|
|
|
error_propagate(errp, err);
|
|
|
|
return;
|
|
|
|
}
|
qom: Add an object_property_add_enum() helper function
A QOM property can be parsed as enum using the visit_type_enum()
helper function, but this forces callers to use the more complex
generic object_property_add() method when registering it. It
also requires that users of that object have access to the
string map when they want to read the property value.
This patch introduces a specialized object_property_add_enum()
method which simplifies the use of enum properties, so the
setters/getters directly get passed the int value.
typedef enum {
MYDEV_TYPE_FROG,
MYDEV_TYPE_ALLIGATOR,
MYDEV_TYPE_PLATYPUS,
MYDEV_TYPE_LAST
} MyDevType;
Then provide a table of enum <-> string mappings
static const char *const mydevtypemap[MYDEV_TYPE_LAST + 1] = {
[MYDEV_TYPE_FROG] = "frog",
[MYDEV_TYPE_ALLIGATOR] = "alligator",
[MYDEV_TYPE_PLATYPUS] = "platypus",
[MYDEV_TYPE_LAST] = NULL,
};
Assuming an object struct of
typedef struct {
Object parent_obj;
MyDevType devtype;
...other fields...
} MyDev;
The property can then be registered as follows:
static int mydev_prop_get_devtype(Object *obj,
Error **errp G_GNUC_UNUSED)
{
MyDev *dev = MYDEV(obj);
return dev->devtype;
}
static void mydev_prop_set_devtype(Object *obj,
int value,
Error **errp G_GNUC_UNUSED)
{
MyDev *dev = MYDEV(obj);
dev->devtype = value;
}
object_property_add_enum(obj, "devtype",
mydevtypemap, "MyDevType",
mydev_prop_get_devtype,
mydev_prop_set_devtype,
NULL);
Note there is no need to check the range of 'value' in
the setter, because the string->enum conversion code will
have already done that and reported an error as required.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Andreas Färber <afaerber@suse.de>
2015-05-13 19:14:08 +03:00
|
|
|
|
|
|
|
visit_type_enum(v, &value, prop->strings, NULL, name, errp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void property_set_enum(Object *obj, Visitor *v, void *opaque,
|
|
|
|
const char *name, Error **errp)
|
|
|
|
{
|
|
|
|
EnumProperty *prop = opaque;
|
|
|
|
int value;
|
2015-08-25 21:00:45 +03:00
|
|
|
Error *err = NULL;
|
qom: Add an object_property_add_enum() helper function
A QOM property can be parsed as enum using the visit_type_enum()
helper function, but this forces callers to use the more complex
generic object_property_add() method when registering it. It
also requires that users of that object have access to the
string map when they want to read the property value.
This patch introduces a specialized object_property_add_enum()
method which simplifies the use of enum properties, so the
setters/getters directly get passed the int value.
typedef enum {
MYDEV_TYPE_FROG,
MYDEV_TYPE_ALLIGATOR,
MYDEV_TYPE_PLATYPUS,
MYDEV_TYPE_LAST
} MyDevType;
Then provide a table of enum <-> string mappings
static const char *const mydevtypemap[MYDEV_TYPE_LAST + 1] = {
[MYDEV_TYPE_FROG] = "frog",
[MYDEV_TYPE_ALLIGATOR] = "alligator",
[MYDEV_TYPE_PLATYPUS] = "platypus",
[MYDEV_TYPE_LAST] = NULL,
};
Assuming an object struct of
typedef struct {
Object parent_obj;
MyDevType devtype;
...other fields...
} MyDev;
The property can then be registered as follows:
static int mydev_prop_get_devtype(Object *obj,
Error **errp G_GNUC_UNUSED)
{
MyDev *dev = MYDEV(obj);
return dev->devtype;
}
static void mydev_prop_set_devtype(Object *obj,
int value,
Error **errp G_GNUC_UNUSED)
{
MyDev *dev = MYDEV(obj);
dev->devtype = value;
}
object_property_add_enum(obj, "devtype",
mydevtypemap, "MyDevType",
mydev_prop_get_devtype,
mydev_prop_set_devtype,
NULL);
Note there is no need to check the range of 'value' in
the setter, because the string->enum conversion code will
have already done that and reported an error as required.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Andreas Färber <afaerber@suse.de>
2015-05-13 19:14:08 +03:00
|
|
|
|
2015-08-25 21:00:45 +03:00
|
|
|
visit_type_enum(v, &value, prop->strings, NULL, name, &err);
|
|
|
|
if (err) {
|
|
|
|
error_propagate(errp, err);
|
|
|
|
return;
|
|
|
|
}
|
qom: Add an object_property_add_enum() helper function
A QOM property can be parsed as enum using the visit_type_enum()
helper function, but this forces callers to use the more complex
generic object_property_add() method when registering it. It
also requires that users of that object have access to the
string map when they want to read the property value.
This patch introduces a specialized object_property_add_enum()
method which simplifies the use of enum properties, so the
setters/getters directly get passed the int value.
typedef enum {
MYDEV_TYPE_FROG,
MYDEV_TYPE_ALLIGATOR,
MYDEV_TYPE_PLATYPUS,
MYDEV_TYPE_LAST
} MyDevType;
Then provide a table of enum <-> string mappings
static const char *const mydevtypemap[MYDEV_TYPE_LAST + 1] = {
[MYDEV_TYPE_FROG] = "frog",
[MYDEV_TYPE_ALLIGATOR] = "alligator",
[MYDEV_TYPE_PLATYPUS] = "platypus",
[MYDEV_TYPE_LAST] = NULL,
};
Assuming an object struct of
typedef struct {
Object parent_obj;
MyDevType devtype;
...other fields...
} MyDev;
The property can then be registered as follows:
static int mydev_prop_get_devtype(Object *obj,
Error **errp G_GNUC_UNUSED)
{
MyDev *dev = MYDEV(obj);
return dev->devtype;
}
static void mydev_prop_set_devtype(Object *obj,
int value,
Error **errp G_GNUC_UNUSED)
{
MyDev *dev = MYDEV(obj);
dev->devtype = value;
}
object_property_add_enum(obj, "devtype",
mydevtypemap, "MyDevType",
mydev_prop_get_devtype,
mydev_prop_set_devtype,
NULL);
Note there is no need to check the range of 'value' in
the setter, because the string->enum conversion code will
have already done that and reported an error as required.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Andreas Färber <afaerber@suse.de>
2015-05-13 19:14:08 +03:00
|
|
|
prop->set(obj, value, errp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void property_release_enum(Object *obj, const char *name,
|
|
|
|
void *opaque)
|
|
|
|
{
|
|
|
|
EnumProperty *prop = opaque;
|
|
|
|
g_free(prop);
|
|
|
|
}
|
|
|
|
|
|
|
|
void object_property_add_enum(Object *obj, const char *name,
|
|
|
|
const char *typename,
|
|
|
|
const char * const *strings,
|
|
|
|
int (*get)(Object *, Error **),
|
|
|
|
void (*set)(Object *, int, Error **),
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
Error *local_err = NULL;
|
|
|
|
EnumProperty *prop = g_malloc(sizeof(*prop));
|
|
|
|
|
|
|
|
prop->strings = strings;
|
|
|
|
prop->get = get;
|
|
|
|
prop->set = set;
|
|
|
|
|
|
|
|
object_property_add(obj, name, typename,
|
|
|
|
get ? property_get_enum : NULL,
|
|
|
|
set ? property_set_enum : NULL,
|
|
|
|
property_release_enum,
|
|
|
|
prop, &local_err);
|
|
|
|
if (local_err) {
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
g_free(prop);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-06 06:55:45 +03:00
|
|
|
typedef struct TMProperty {
|
|
|
|
void (*get)(Object *, struct tm *, Error **);
|
|
|
|
} TMProperty;
|
|
|
|
|
|
|
|
static void property_get_tm(Object *obj, Visitor *v, void *opaque,
|
|
|
|
const char *name, Error **errp)
|
|
|
|
{
|
|
|
|
TMProperty *prop = opaque;
|
|
|
|
Error *err = NULL;
|
|
|
|
struct tm value;
|
|
|
|
|
|
|
|
prop->get(obj, &value, &err);
|
|
|
|
if (err) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
visit_start_struct(v, NULL, "struct tm", name, 0, &err);
|
|
|
|
if (err) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
visit_type_int32(v, &value.tm_year, "tm_year", &err);
|
|
|
|
if (err) {
|
|
|
|
goto out_end;
|
|
|
|
}
|
|
|
|
visit_type_int32(v, &value.tm_mon, "tm_mon", &err);
|
|
|
|
if (err) {
|
|
|
|
goto out_end;
|
|
|
|
}
|
|
|
|
visit_type_int32(v, &value.tm_mday, "tm_mday", &err);
|
|
|
|
if (err) {
|
|
|
|
goto out_end;
|
|
|
|
}
|
|
|
|
visit_type_int32(v, &value.tm_hour, "tm_hour", &err);
|
|
|
|
if (err) {
|
|
|
|
goto out_end;
|
|
|
|
}
|
|
|
|
visit_type_int32(v, &value.tm_min, "tm_min", &err);
|
|
|
|
if (err) {
|
|
|
|
goto out_end;
|
|
|
|
}
|
|
|
|
visit_type_int32(v, &value.tm_sec, "tm_sec", &err);
|
|
|
|
if (err) {
|
|
|
|
goto out_end;
|
|
|
|
}
|
|
|
|
out_end:
|
|
|
|
error_propagate(errp, err);
|
|
|
|
err = NULL;
|
|
|
|
visit_end_struct(v, errp);
|
|
|
|
out:
|
|
|
|
error_propagate(errp, err);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static void property_release_tm(Object *obj, const char *name,
|
|
|
|
void *opaque)
|
|
|
|
{
|
|
|
|
TMProperty *prop = opaque;
|
|
|
|
g_free(prop);
|
|
|
|
}
|
|
|
|
|
|
|
|
void object_property_add_tm(Object *obj, const char *name,
|
|
|
|
void (*get)(Object *, struct tm *, Error **),
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
Error *local_err = NULL;
|
|
|
|
TMProperty *prop = g_malloc0(sizeof(*prop));
|
|
|
|
|
|
|
|
prop->get = get;
|
|
|
|
|
|
|
|
object_property_add(obj, name, "struct tm",
|
|
|
|
get ? property_get_tm : NULL, NULL,
|
|
|
|
property_release_tm,
|
|
|
|
prop, &local_err);
|
|
|
|
if (local_err) {
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
g_free(prop);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-04-02 19:33:51 +04:00
|
|
|
static char *qdev_get_type(Object *obj, Error **errp)
|
|
|
|
{
|
|
|
|
return g_strdup(object_get_typename(obj));
|
|
|
|
}
|
|
|
|
|
2013-09-22 11:10:17 +04:00
|
|
|
static void property_get_uint8_ptr(Object *obj, Visitor *v,
|
|
|
|
void *opaque, const char *name,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
uint8_t value = *(uint8_t *)opaque;
|
|
|
|
visit_type_uint8(v, &value, name, errp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void property_get_uint16_ptr(Object *obj, Visitor *v,
|
|
|
|
void *opaque, const char *name,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
uint16_t value = *(uint16_t *)opaque;
|
|
|
|
visit_type_uint16(v, &value, name, errp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void property_get_uint32_ptr(Object *obj, Visitor *v,
|
|
|
|
void *opaque, const char *name,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
uint32_t value = *(uint32_t *)opaque;
|
|
|
|
visit_type_uint32(v, &value, name, errp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void property_get_uint64_ptr(Object *obj, Visitor *v,
|
|
|
|
void *opaque, const char *name,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
uint64_t value = *(uint64_t *)opaque;
|
|
|
|
visit_type_uint64(v, &value, name, errp);
|
|
|
|
}
|
|
|
|
|
|
|
|
void object_property_add_uint8_ptr(Object *obj, const char *name,
|
|
|
|
const uint8_t *v, Error **errp)
|
|
|
|
{
|
|
|
|
object_property_add(obj, name, "uint8", property_get_uint8_ptr,
|
|
|
|
NULL, NULL, (void *)v, errp);
|
|
|
|
}
|
|
|
|
|
|
|
|
void object_property_add_uint16_ptr(Object *obj, const char *name,
|
|
|
|
const uint16_t *v, Error **errp)
|
|
|
|
{
|
|
|
|
object_property_add(obj, name, "uint16", property_get_uint16_ptr,
|
|
|
|
NULL, NULL, (void *)v, errp);
|
|
|
|
}
|
|
|
|
|
|
|
|
void object_property_add_uint32_ptr(Object *obj, const char *name,
|
|
|
|
const uint32_t *v, Error **errp)
|
|
|
|
{
|
|
|
|
object_property_add(obj, name, "uint32", property_get_uint32_ptr,
|
|
|
|
NULL, NULL, (void *)v, errp);
|
|
|
|
}
|
|
|
|
|
|
|
|
void object_property_add_uint64_ptr(Object *obj, const char *name,
|
|
|
|
const uint64_t *v, Error **errp)
|
|
|
|
{
|
|
|
|
object_property_add(obj, name, "uint64", property_get_uint64_ptr,
|
|
|
|
NULL, NULL, (void *)v, errp);
|
|
|
|
}
|
|
|
|
|
2014-06-18 13:58:28 +04:00
|
|
|
typedef struct {
|
|
|
|
Object *target_obj;
|
2015-04-09 22:57:29 +03:00
|
|
|
char *target_name;
|
2014-06-18 13:58:28 +04:00
|
|
|
} AliasProperty;
|
|
|
|
|
|
|
|
static void property_get_alias(Object *obj, struct Visitor *v, void *opaque,
|
|
|
|
const char *name, Error **errp)
|
|
|
|
{
|
|
|
|
AliasProperty *prop = opaque;
|
|
|
|
|
|
|
|
object_property_get(prop->target_obj, v, prop->target_name, errp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void property_set_alias(Object *obj, struct Visitor *v, void *opaque,
|
|
|
|
const char *name, Error **errp)
|
|
|
|
{
|
|
|
|
AliasProperty *prop = opaque;
|
|
|
|
|
|
|
|
object_property_set(prop->target_obj, v, prop->target_name, errp);
|
|
|
|
}
|
|
|
|
|
2014-06-05 15:11:51 +04:00
|
|
|
static Object *property_resolve_alias(Object *obj, void *opaque,
|
|
|
|
const gchar *part)
|
|
|
|
{
|
|
|
|
AliasProperty *prop = opaque;
|
|
|
|
|
|
|
|
return object_resolve_path_component(prop->target_obj, prop->target_name);
|
|
|
|
}
|
|
|
|
|
2014-06-18 13:58:28 +04:00
|
|
|
static void property_release_alias(Object *obj, const char *name, void *opaque)
|
|
|
|
{
|
|
|
|
AliasProperty *prop = opaque;
|
|
|
|
|
2015-04-09 22:57:29 +03:00
|
|
|
g_free(prop->target_name);
|
2014-06-18 13:58:28 +04:00
|
|
|
g_free(prop);
|
|
|
|
}
|
|
|
|
|
|
|
|
void object_property_add_alias(Object *obj, const char *name,
|
|
|
|
Object *target_obj, const char *target_name,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
AliasProperty *prop;
|
2014-06-05 15:11:51 +04:00
|
|
|
ObjectProperty *op;
|
2014-06-18 13:58:28 +04:00
|
|
|
ObjectProperty *target_prop;
|
2014-06-10 13:17:35 +04:00
|
|
|
gchar *prop_type;
|
2014-09-27 09:13:56 +04:00
|
|
|
Error *local_err = NULL;
|
2014-06-18 13:58:28 +04:00
|
|
|
|
|
|
|
target_prop = object_property_find(target_obj, target_name, errp);
|
|
|
|
if (!target_prop) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-06-10 13:17:35 +04:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2014-06-18 13:58:28 +04:00
|
|
|
prop = g_malloc(sizeof(*prop));
|
|
|
|
prop->target_obj = target_obj;
|
2015-04-09 22:57:29 +03:00
|
|
|
prop->target_name = g_strdup(target_name);
|
2014-06-18 13:58:28 +04:00
|
|
|
|
2014-06-10 13:17:35 +04:00
|
|
|
op = object_property_add(obj, name, prop_type,
|
2014-06-05 15:11:51 +04:00
|
|
|
property_get_alias,
|
|
|
|
property_set_alias,
|
|
|
|
property_release_alias,
|
2014-09-27 09:13:56 +04:00
|
|
|
prop, &local_err);
|
|
|
|
if (local_err) {
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
g_free(prop);
|
|
|
|
goto out;
|
|
|
|
}
|
2014-06-05 15:11:51 +04:00
|
|
|
op->resolve = property_resolve_alias;
|
2014-06-10 13:17:35 +04:00
|
|
|
|
2015-03-27 19:34:10 +03:00
|
|
|
object_property_set_description(obj, op->name,
|
2014-10-07 10:33:21 +04:00
|
|
|
target_prop->description,
|
|
|
|
&error_abort);
|
|
|
|
|
2014-09-27 09:13:56 +04:00
|
|
|
out:
|
2014-06-10 13:17:35 +04:00
|
|
|
g_free(prop_type);
|
2014-06-18 13:58:28 +04:00
|
|
|
}
|
|
|
|
|
2014-10-07 10:33:21 +04:00
|
|
|
void object_property_set_description(Object *obj, const char *name,
|
|
|
|
const char *description, Error **errp)
|
|
|
|
{
|
|
|
|
ObjectProperty *op;
|
|
|
|
|
|
|
|
op = object_property_find(obj, name, errp);
|
|
|
|
if (!op) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
g_free(op->description);
|
|
|
|
op->description = g_strdup(description);
|
|
|
|
}
|
|
|
|
|
2012-04-02 19:33:51 +04:00
|
|
|
static void object_instance_init(Object *obj)
|
|
|
|
{
|
|
|
|
object_property_add_str(obj, "type", qdev_get_type, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
2012-03-31 18:45:54 +04:00
|
|
|
static void register_types(void)
|
|
|
|
{
|
|
|
|
static TypeInfo interface_info = {
|
|
|
|
.name = TYPE_INTERFACE,
|
2012-08-10 07:16:10 +04:00
|
|
|
.class_size = sizeof(InterfaceClass),
|
2012-03-31 18:45:54 +04:00
|
|
|
.abstract = true,
|
|
|
|
};
|
|
|
|
|
|
|
|
static TypeInfo object_info = {
|
|
|
|
.name = TYPE_OBJECT,
|
|
|
|
.instance_size = sizeof(Object),
|
2012-04-02 19:33:51 +04:00
|
|
|
.instance_init = object_instance_init,
|
2012-03-31 18:45:54 +04:00
|
|
|
.abstract = true,
|
|
|
|
};
|
|
|
|
|
2012-04-04 17:58:40 +04:00
|
|
|
type_interface = type_register_internal(&interface_info);
|
|
|
|
type_register_internal(&object_info);
|
2012-03-31 18:45:54 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
type_init(register_types)
|