qemu_opts_foreach() runs its callback with the error location set to
the option's location. Any errors the callback reports use the
option's location automatically.
Commit 90998d5
moved the actual error reporting from "inside"
qemu_opts_foreach() to after it. Here's a typical hunk:
if (qemu_opts_foreach(qemu_find_opts("object"),
- object_create,
- object_create_initial, NULL)) {
+ user_creatable_add_opts_foreach,
+ object_create_initial, &err)) {
+ error_report_err(err);
exit(1);
}
Before, object_create() reports from within qemu_opts_foreach(), using
the option's location. Afterwards, we do it after
qemu_opts_foreach(), using whatever location happens to be current
there. Commonly a "none" location.
This is because Error objects don't have location information.
Problematic.
Reproducer:
$ qemu-system-x86_64 -nodefaults -display none -object secret,id=foo,foo=bar
qemu-system-x86_64: Property '.foo' not found
Note no location. This commit restores it:
qemu-system-x86_64: -object secret,id=foo,foo=bar: Property '.foo' not found
Note that the qemu_opts_foreach() bug just fixed could mask the bug
here: if the location it leaves dangling hasn't been clobbered, yet,
it's the correct one.
Reported-by: Eric Blake <eblake@redhat.com>
Cc: Daniel P. Berrange <berrange@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-Id: <1461767349-15329-4-git-send-email-armbru@redhat.com>
Reviewed-by: Daniel P. Berrange <berrange@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
[Paragraph on Error added to commit message]
223 lines
5 KiB
C
223 lines
5 KiB
C
#include "qemu/osdep.h"
|
|
#include "qapi/error.h"
|
|
#include "qom/object_interfaces.h"
|
|
#include "qemu/module.h"
|
|
#include "qapi-visit.h"
|
|
#include "qapi/qmp-output-visitor.h"
|
|
#include "qapi/opts-visitor.h"
|
|
|
|
void user_creatable_complete(Object *obj, Error **errp)
|
|
{
|
|
|
|
UserCreatableClass *ucc;
|
|
UserCreatable *uc =
|
|
(UserCreatable *)object_dynamic_cast(obj, TYPE_USER_CREATABLE);
|
|
|
|
if (!uc) {
|
|
return;
|
|
}
|
|
|
|
ucc = USER_CREATABLE_GET_CLASS(uc);
|
|
if (ucc->complete) {
|
|
ucc->complete(uc, errp);
|
|
}
|
|
}
|
|
|
|
bool user_creatable_can_be_deleted(UserCreatable *uc, Error **errp)
|
|
{
|
|
|
|
UserCreatableClass *ucc = USER_CREATABLE_GET_CLASS(uc);
|
|
|
|
if (ucc->can_be_deleted) {
|
|
return ucc->can_be_deleted(uc, errp);
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
Object *user_creatable_add(const QDict *qdict,
|
|
Visitor *v, Error **errp)
|
|
{
|
|
char *type = NULL;
|
|
char *id = NULL;
|
|
Object *obj = NULL;
|
|
Error *local_err = NULL, *end_err = NULL;
|
|
QDict *pdict;
|
|
|
|
pdict = qdict_clone_shallow(qdict);
|
|
|
|
visit_start_struct(v, NULL, NULL, 0, &local_err);
|
|
if (local_err) {
|
|
goto out;
|
|
}
|
|
|
|
qdict_del(pdict, "qom-type");
|
|
visit_type_str(v, "qom-type", &type, &local_err);
|
|
if (local_err) {
|
|
goto out_visit;
|
|
}
|
|
|
|
qdict_del(pdict, "id");
|
|
visit_type_str(v, "id", &id, &local_err);
|
|
if (local_err) {
|
|
goto out_visit;
|
|
}
|
|
|
|
obj = user_creatable_add_type(type, id, pdict, v, &local_err);
|
|
if (local_err) {
|
|
goto out_visit;
|
|
}
|
|
|
|
out_visit:
|
|
visit_end_struct(v, &end_err);
|
|
if (end_err) {
|
|
error_propagate(&local_err, end_err);
|
|
if (obj) {
|
|
user_creatable_del(id, NULL);
|
|
}
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
QDECREF(pdict);
|
|
g_free(id);
|
|
g_free(type);
|
|
if (local_err) {
|
|
error_propagate(errp, local_err);
|
|
object_unref(obj);
|
|
return NULL;
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
|
|
Object *user_creatable_add_type(const char *type, const char *id,
|
|
const QDict *qdict,
|
|
Visitor *v, Error **errp)
|
|
{
|
|
Object *obj;
|
|
ObjectClass *klass;
|
|
const QDictEntry *e;
|
|
Error *local_err = NULL;
|
|
|
|
klass = object_class_by_name(type);
|
|
if (!klass) {
|
|
error_setg(errp, "invalid object type: %s", type);
|
|
return NULL;
|
|
}
|
|
|
|
if (!object_class_dynamic_cast(klass, TYPE_USER_CREATABLE)) {
|
|
error_setg(errp, "object type '%s' isn't supported by object-add",
|
|
type);
|
|
return NULL;
|
|
}
|
|
|
|
if (object_class_is_abstract(klass)) {
|
|
error_setg(errp, "object type '%s' is abstract", type);
|
|
return NULL;
|
|
}
|
|
|
|
obj = object_new(type);
|
|
if (qdict) {
|
|
for (e = qdict_first(qdict); e; e = qdict_next(qdict, e)) {
|
|
object_property_set(obj, v, e->key, &local_err);
|
|
if (local_err) {
|
|
goto out;
|
|
}
|
|
}
|
|
}
|
|
|
|
object_property_add_child(object_get_objects_root(),
|
|
id, obj, &local_err);
|
|
if (local_err) {
|
|
goto out;
|
|
}
|
|
|
|
user_creatable_complete(obj, &local_err);
|
|
if (local_err) {
|
|
object_property_del(object_get_objects_root(),
|
|
id, &error_abort);
|
|
goto out;
|
|
}
|
|
out:
|
|
if (local_err) {
|
|
error_propagate(errp, local_err);
|
|
object_unref(obj);
|
|
return NULL;
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
|
|
Object *user_creatable_add_opts(QemuOpts *opts, Error **errp)
|
|
{
|
|
OptsVisitor *ov;
|
|
QDict *pdict;
|
|
Object *obj = NULL;
|
|
|
|
ov = opts_visitor_new(opts);
|
|
pdict = qemu_opts_to_qdict(opts, NULL);
|
|
|
|
obj = user_creatable_add(pdict, opts_get_visitor(ov), errp);
|
|
opts_visitor_cleanup(ov);
|
|
QDECREF(pdict);
|
|
return obj;
|
|
}
|
|
|
|
|
|
int user_creatable_add_opts_foreach(void *opaque, QemuOpts *opts, Error **errp)
|
|
{
|
|
bool (*type_predicate)(const char *) = opaque;
|
|
Object *obj = NULL;
|
|
Error *err = NULL;
|
|
const char *type;
|
|
|
|
type = qemu_opt_get(opts, "qom-type");
|
|
if (type && type_predicate &&
|
|
!type_predicate(type)) {
|
|
return 0;
|
|
}
|
|
|
|
obj = user_creatable_add_opts(opts, &err);
|
|
if (!obj) {
|
|
error_report_err(err);
|
|
return -1;
|
|
}
|
|
object_unref(obj);
|
|
return 0;
|
|
}
|
|
|
|
|
|
void user_creatable_del(const char *id, Error **errp)
|
|
{
|
|
Object *container;
|
|
Object *obj;
|
|
|
|
container = object_get_objects_root();
|
|
obj = object_resolve_path_component(container, id);
|
|
if (!obj) {
|
|
error_setg(errp, "object '%s' not found", id);
|
|
return;
|
|
}
|
|
|
|
if (!user_creatable_can_be_deleted(USER_CREATABLE(obj), errp)) {
|
|
error_setg(errp, "object '%s' is in use, can not be deleted", id);
|
|
return;
|
|
}
|
|
object_unparent(obj);
|
|
}
|
|
|
|
static void register_types(void)
|
|
{
|
|
static const TypeInfo uc_interface_info = {
|
|
.name = TYPE_USER_CREATABLE,
|
|
.parent = TYPE_INTERFACE,
|
|
.class_size = sizeof(UserCreatableClass),
|
|
};
|
|
|
|
type_register_static(&uc_interface_info);
|
|
}
|
|
|
|
type_init(register_types)
|