#include "qom/object.h"
#include "qemu-common.h"
#include "qapi/visitor.h"
+#include "qapi-visit.h"
#include "qapi/string-input-visitor.h"
#include "qapi/string-output-visitor.h"
#include "qapi/qmp/qerror.h"
return strstart(prop->type, "child<", NULL);
}
-static inline bool object_property_is_link(ObjectProperty *prop)
-{
- return strstart(prop->type, "link<", NULL);
-}
-
static void object_property_del_all(Object *obj)
{
while (!QTAILQ_EMPTY(&obj->properties)) {
g_free(prop->name);
g_free(prop->type);
+ g_free(prop->description);
g_free(prop);
}
}
void object_unparent(Object *obj)
{
- if (!obj->parent) {
- return;
- }
-
- object_ref(obj);
- if (obj->class->unparent) {
- (obj->class->unparent)(obj);
- }
if (obj->parent) {
object_property_del_child(obj->parent, obj, NULL);
}
- object_unref(obj);
}
static void object_deinit(Object *obj, TypeImpl *type)
Object *obj = data;
TypeImpl *ti = obj->class->type;
- object_deinit(obj, ti);
object_property_del_all(obj);
+ object_deinit(obj, ti);
g_assert(obj->ref == 0);
if (obj->free) {
int object_child_foreach(Object *obj, int (*fn)(Object *child, void *opaque),
void *opaque)
{
- ObjectProperty *prop;
+ ObjectProperty *prop, *next;
int ret = 0;
- QTAILQ_FOREACH(prop, &obj->properties, node) {
+ QTAILQ_FOREACH_SAFE(prop, &obj->properties, node, next) {
if (object_property_is_child(prop)) {
ret = fn(prop->opaque, opaque);
if (ret != 0) {
void object_ref(Object *obj)
{
+ if (!obj) {
+ return;
+ }
atomic_inc(&obj->ref);
}
void object_unref(Object *obj)
{
+ if (!obj) {
+ return;
+ }
g_assert(obj->ref > 0);
/* parent always holds a reference to its children */
}
}
-void object_property_add(Object *obj, const char *name, const char *type,
- ObjectPropertyAccessor *get,
- ObjectPropertyAccessor *set,
- ObjectPropertyRelease *release,
- void *opaque, Error **errp)
+ObjectProperty *
+object_property_add(Object *obj, const char *name, const char *type,
+ ObjectPropertyAccessor *get,
+ ObjectPropertyAccessor *set,
+ ObjectPropertyRelease *release,
+ void *opaque, Error **errp)
{
ObjectProperty *prop;
+ size_t name_len = strlen(name);
+
+ if (name_len >= 3 && !memcmp(name + name_len - 3, "[*]", 4)) {
+ int i;
+ ObjectProperty *ret;
+ 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;
+ }
QTAILQ_FOREACH(prop, &obj->properties, node) {
if (strcmp(prop->name, name) == 0) {
error_setg(errp, "attempt to add duplicate property '%s'"
" to object (type '%s')", name,
object_get_typename(obj));
- return;
+ return NULL;
}
}
prop->opaque = opaque;
QTAILQ_INSERT_TAIL(&obj->properties, prop, node);
+ return prop;
}
ObjectProperty *object_property_find(Object *obj, const char *name,
}
}
- error_set(errp, QERR_PROPERTY_NOT_FOUND, "", name);
+ error_setg(errp, "Property '.%s' not found", name);
return NULL;
}
g_free(prop->name);
g_free(prop->type);
+ g_free(prop->description);
g_free(prop);
}
void object_property_set_link(Object *obj, Object *value,
const char *name, Error **errp)
{
- gchar *path = object_get_canonical_path(value);
- object_property_set_str(obj, path, name, errp);
- g_free(path);
+ 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);
+ }
}
Object *object_property_get_link(Object *obj, const char *name,
return retval;
}
+int object_property_get_enum(Object *obj, const char *name,
+ const char *strings[], Error **errp)
+{
+ StringOutputVisitor *sov;
+ StringInputVisitor *siv;
+ char *str;
+ int ret;
+
+ sov = string_output_visitor_new(false);
+ object_property_get(obj, string_output_get_visitor(sov), name, errp);
+ str = string_output_get_string(sov);
+ siv = string_input_visitor_new(str);
+ string_output_visitor_cleanup(sov);
+ visit_type_enum(string_input_get_visitor(siv),
+ &ret, strings, NULL, name, errp);
+
+ g_free(str);
+ string_input_visitor_cleanup(siv);
+
+ return ret;
+}
+
+void object_property_get_uint16List(Object *obj, const char *name,
+ uint16List **list, Error **errp)
+{
+ StringOutputVisitor *ov;
+ StringInputVisitor *iv;
+ char *str;
+
+ ov = string_output_visitor_new(false);
+ object_property_get(obj, string_output_get_visitor(ov),
+ name, errp);
+ str = string_output_get_string(ov);
+ iv = string_input_visitor_new(str);
+ visit_type_uint16List(string_input_get_visitor(iv),
+ list, NULL, errp);
+
+ g_free(str);
+ string_output_visitor_cleanup(ov);
+ string_input_visitor_cleanup(iv);
+}
+
void object_property_parse(Object *obj, const char *string,
const char *name, Error **errp)
{
string_input_visitor_cleanup(mi);
}
-char *object_property_print(Object *obj, const char *name,
+char *object_property_print(Object *obj, const char *name, bool human,
Error **errp)
{
StringOutputVisitor *mo;
- char *string;
+ char *string = NULL;
+ Error *local_err = NULL;
+
+ mo = string_output_visitor_new(human);
+ object_property_get(obj, string_output_get_visitor(mo), name, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ goto out;
+ }
- mo = string_output_visitor_new();
- object_property_get(obj, string_output_get_visitor(mo), name, errp);
string = string_output_get_string(mo);
+
+out:
string_output_visitor_cleanup(mo);
return string;
}
g_free(path);
}
+static Object *object_resolve_child_property(Object *parent, void *opaque, const gchar *part)
+{
+ return opaque;
+}
+
static void object_finalize_child_property(Object *obj, const char *name,
void *opaque)
{
Object *child = opaque;
+ if (child->class->unparent) {
+ (child->class->unparent)(child);
+ }
+ child->parent = NULL;
object_unref(child);
}
{
Error *local_err = NULL;
gchar *type;
+ ObjectProperty *op;
+
+ if (child->parent != NULL) {
+ error_setg(errp, "child object is already parented");
+ return;
+ }
type = g_strdup_printf("child<%s>", object_get_typename(OBJECT(child)));
- object_property_add(obj, name, type, object_get_child_property, NULL,
- object_finalize_child_property, child, &local_err);
+ op = object_property_add(obj, name, type, object_get_child_property, NULL,
+ object_finalize_child_property, child, &local_err);
if (local_err) {
error_propagate(errp, local_err);
goto out;
}
+
+ op->resolve = object_resolve_child_property;
object_ref(child);
- g_assert(child->parent == NULL);
child->parent = obj;
out:
g_free(type);
}
+void object_property_allow_set_link(Object *obj, const char *name,
+ Object *val, Error **errp)
+{
+ /* Allow the link to be set, always */
+}
+
+typedef struct {
+ Object **child;
+ void (*check)(Object *, const char *, Object *, Error **);
+ ObjectPropertyLinkFlags flags;
+} LinkProperty;
+
static void object_get_link_property(Object *obj, Visitor *v, void *opaque,
const char *name, Error **errp)
{
- Object **child = opaque;
+ LinkProperty *lprop = opaque;
+ Object **child = lprop->child;
gchar *path;
if (*child) {
}
}
-static void object_set_link_property(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+/*
+ * object_resolve_link:
+ *
+ * Lookup an object and ensure its type matches the link property type. This
+ * is similar to object_resolve_path() except type verification against the
+ * link property is performed.
+ *
+ * Returns: The matched object or NULL on path lookup failures.
+ */
+static Object *object_resolve_link(Object *obj, const char *name,
+ const char *path, Error **errp)
{
- Object **child = opaque;
- Object *old_target;
- bool ambiguous = false;
const char *type;
- char *path;
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) {
+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
+ "Path '%s' does not uniquely identify an object", path);
+ } else if (!target) {
+ target = object_resolve_path(path, &ambiguous);
+ if (target || ambiguous) {
+ error_set(errp, QERR_INVALID_PARAMETER_TYPE, name, target_type);
+ } else {
+ error_set(errp, QERR_DEVICE_NOT_FOUND, path);
+ }
+ target = NULL;
+ }
+ g_free(target_type);
- visit_type_str(v, &path, name, errp);
-
- old_target = *child;
- *child = NULL;
+ return target;
+}
- if (strcmp(path, "") != 0) {
- Object *target;
+static void object_set_link_property(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ Error *local_err = NULL;
+ LinkProperty *prop = opaque;
+ Object **child = prop->child;
+ Object *old_target = *child;
+ Object *new_target = NULL;
+ char *path = NULL;
- /* Go from link<FOO> to FOO. */
- target_type = g_strndup(&type[5], strlen(type) - 6);
- target = object_resolve_path_type(path, target_type, &ambiguous);
+ visit_type_str(v, &path, name, &local_err);
- if (ambiguous) {
- error_set(errp, QERR_AMBIGUOUS_PATH, path);
- } else if (target) {
- object_ref(target);
- *child = target;
- } else {
- target = object_resolve_path(path, &ambiguous);
- if (target || ambiguous) {
- error_set(errp, QERR_INVALID_PARAMETER_TYPE, name, target_type);
- } else {
- error_set(errp, QERR_DEVICE_NOT_FOUND, path);
- }
- }
- g_free(target_type);
+ if (!local_err && strcmp(path, "") != 0) {
+ new_target = object_resolve_link(obj, name, path, &local_err);
}
g_free(path);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
- if (old_target != NULL) {
- object_unref(old_target);
+ prop->check(obj, name, new_target, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
}
+
+ object_ref(new_target);
+ *child = new_target;
+ object_unref(old_target);
+}
+
+static Object *object_resolve_link_property(Object *parent, void *opaque, const gchar *part)
+{
+ LinkProperty *lprop = opaque;
+
+ return *lprop->child;
+}
+
+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);
}
void object_property_add_link(Object *obj, const char *name,
const char *type, Object **child,
+ void (*check)(Object *, const char *,
+ Object *, Error **),
+ ObjectPropertyLinkFlags flags,
Error **errp)
{
+ Error *local_err = NULL;
+ LinkProperty *prop = g_malloc(sizeof(*prop));
gchar *full_type;
+ ObjectProperty *op;
+
+ prop->child = child;
+ prop->check = check;
+ prop->flags = flags;
full_type = g_strdup_printf("link<%s>", type);
- object_property_add(obj, name, full_type,
- object_get_link_property,
- object_set_link_property,
- NULL, child, errp);
+ 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);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ g_free(prop);
+ goto out;
+ }
+
+ op->resolve = object_resolve_link_property;
+out:
g_free(full_type);
}
+gchar *object_get_canonical_path_component(Object *obj)
+{
+ ObjectProperty *prop = NULL;
+
+ g_assert(obj);
+ g_assert(obj->parent != NULL);
+
+ QTAILQ_FOREACH(prop, &obj->parent->properties, node) {
+ 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;
+}
+
gchar *object_get_canonical_path(Object *obj)
{
Object *root = object_get_root();
- char *newpath = NULL, *path = NULL;
+ char *newpath, *path = NULL;
while (obj != root) {
- ObjectProperty *prop = NULL;
-
- g_assert(obj->parent != NULL);
-
- QTAILQ_FOREACH(prop, &obj->parent->properties, node) {
- if (!object_property_is_child(prop)) {
- continue;
- }
+ char *component = object_get_canonical_path_component(obj);
- if (prop->opaque == obj) {
- if (path) {
- newpath = g_strdup_printf("%s/%s", prop->name, path);
- g_free(path);
- path = newpath;
- } else {
- path = g_strdup(prop->name);
- }
- break;
- }
+ if (path) {
+ newpath = g_strdup_printf("%s/%s", component, path);
+ g_free(component);
+ g_free(path);
+ path = newpath;
+ } else {
+ path = component;
}
- g_assert(prop != NULL);
-
obj = obj->parent;
}
- newpath = g_strdup_printf("/%s", path);
+ newpath = g_strdup_printf("/%s", path ? path : "");
g_free(path);
return newpath;
return NULL;
}
- if (object_property_is_link(prop)) {
- return *(Object **)prop->opaque;
- } else if (object_property_is_child(prop)) {
- return prop->opaque;
+ if (prop->resolve) {
+ return prop->resolve(parent, prop->opaque, part);
} else {
return NULL;
}
void (*set)(Object *, const char *, Error **),
Error **errp)
{
+ Error *local_err = NULL;
StringProperty *prop = g_malloc0(sizeof(*prop));
prop->get = get;
get ? property_get_str : NULL,
set ? property_set_str : NULL,
property_release_str,
- prop, errp);
+ prop, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ g_free(prop);
+ }
}
typedef struct BoolProperty
void (*set)(Object *, bool, Error **),
Error **errp)
{
+ Error *local_err = NULL;
BoolProperty *prop = g_malloc0(sizeof(*prop));
prop->get = get;
get ? property_get_bool : NULL,
set ? property_set_bool : NULL,
property_release_bool,
- prop, errp);
+ prop, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ g_free(prop);
+ }
}
static char *qdev_get_type(Object *obj, Error **errp)
NULL, NULL, (void *)v, errp);
}
+typedef struct {
+ Object *target_obj;
+ const char *target_name;
+} 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);
+}
+
+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);
+}
+
+static void property_release_alias(Object *obj, const char *name, void *opaque)
+{
+ AliasProperty *prop = opaque;
+
+ g_free(prop);
+}
+
+void object_property_add_alias(Object *obj, const char *name,
+ Object *target_obj, const char *target_name,
+ Error **errp)
+{
+ AliasProperty *prop;
+ ObjectProperty *op;
+ ObjectProperty *target_prop;
+ gchar *prop_type;
+ Error *local_err = NULL;
+
+ target_prop = object_property_find(target_obj, target_name, errp);
+ if (!target_prop) {
+ return;
+ }
+
+ if (object_property_is_child(target_prop)) {
+ prop_type = g_strdup_printf("link%s",
+ target_prop->type + strlen("child"));
+ } else {
+ prop_type = g_strdup(target_prop->type);
+ }
+
+ prop = g_malloc(sizeof(*prop));
+ prop->target_obj = target_obj;
+ prop->target_name = target_name;
+
+ op = object_property_add(obj, name, prop_type,
+ property_get_alias,
+ property_set_alias,
+ property_release_alias,
+ prop, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ g_free(prop);
+ goto out;
+ }
+ op->resolve = property_resolve_alias;
+
+ object_property_set_description(obj, name,
+ target_prop->description,
+ &error_abort);
+
+out:
+ g_free(prop_type);
+}
+
+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);
+}
+
static void object_instance_init(Object *obj)
{
object_property_add_str(obj, "type", qdev_get_type, NULL, NULL);