qemu/qom/object_interfaces.c

345 lines
8.7 KiB
C
Raw Normal View History

#include "qemu/osdep.h"
#include "qemu/cutils.h"
2016-03-14 11:01:28 +03:00
#include "qapi/error.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qerror.h"
#include "qapi/qmp/qjson.h"
#include "qapi/qobject-input-visitor.h"
#include "qom/object_interfaces.h"
#include "qemu/help_option.h"
#include "qemu/id.h"
#include "qemu/module.h"
#include "qemu/option.h"
#include "qapi/opts-visitor.h"
monitor: fix object_del for command-line-created objects Currently objects specified on the command-line are only partially cleaned up when 'object_del' is issued in either HMP or QMP: the object itself is fully finalized, but the QemuOpts are not removed. This results in the following behavior: x86_64-softmmu/qemu-system-x86_64 -monitor stdio \ -object memory-backend-ram,id=ram1,size=256M QEMU 2.7.91 monitor - type 'help' for more information (qemu) object_del ram1 (qemu) object_del ram1 object 'ram1' not found (qemu) object_add memory-backend-ram,id=ram1,size=256M Duplicate ID 'ram1' for object Try "help object_add" for more information which can be an issue for use-cases like memory hotplug. This happens on the HMP side because hmp_object_add() attempts to create a temporary QemuOpts entry with ID 'ram1', which ends up conflicting with the command-line-created entry, since it was never cleaned up during the previous hmp_object_del() call. We address this by adding a check in user_creatable_del(), which is called by both qmp_object_del() and hmp_object_del() to handle the actual object cleanup, to determine whether an option group entry matching the object's ID is present and removing it if it is. Note that qmp_object_add() never attempts to create a temporary QemuOpts entry, so it does not encounter the duplicate ID error, which is why this isn't generally visible in libvirt. Cc: "Dr. David Alan Gilbert" <dgilbert@redhat.com> Cc: Markus Armbruster <armbru@redhat.com> Cc: Eric Blake <eblake@redhat.com> Cc: Daniel Berrange <berrange@redhat.com> Cc: qemu-stable@nongnu.org Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com> Reviewed-by: Daniel P. Berrange <berrange@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> Message-Id: <1496531612-22166-3-git-send-email-mdroth@linux.vnet.ibm.com> Signed-off-by: Markus Armbruster <armbru@redhat.com>
2017-06-04 02:13:32 +03:00
#include "qemu/config-file.h"
bool user_creatable_complete(UserCreatable *uc, Error **errp)
{
UserCreatableClass *ucc = USER_CREATABLE_GET_CLASS(uc);
Error *err = NULL;
if (ucc->complete) {
ucc->complete(uc, &err);
error_propagate(errp, err);
}
return !err;
}
bool user_creatable_can_be_deleted(UserCreatable *uc)
{
UserCreatableClass *ucc = USER_CREATABLE_GET_CLASS(uc);
if (ucc->can_be_deleted) {
return ucc->can_be_deleted(uc);
} else {
return true;
}
}
Object *user_creatable_add_type(const char *type, const char *id,
const QDict *qdict,
Visitor *v, Error **errp)
{
ERRP_GUARD();
Object *obj;
ObjectClass *klass;
const QDictEntry *e;
Error *local_err = NULL;
if (id != NULL && !id_wellformed(id)) {
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "id", "an identifier");
error_append_hint(errp, "Identifiers consist of letters, digits, "
"'-', '.', '_', starting with a letter.\n");
return NULL;
}
klass = object_class_by_name(type);
if (!klass) {
error_setg(errp, "invalid object type: %s", type);
return NULL;
}
if (!object_class_dynamic_cast(klass, TYPE_USER_CREATABLE)) {
error_setg(errp, "object type '%s' isn't supported by object-add",
type);
return NULL;
}
if (object_class_is_abstract(klass)) {
error_setg(errp, "object type '%s' is abstract", type);
return NULL;
}
qom: Wrap prop visit in visit_start_struct The qmp-input visitor was allowing callers to play rather fast and loose: when visiting a QDict, you could grab members of the root dictionary without first pushing into the dict; the final such culprit was the QOM code for converting to and from object properties. But we are about to tighten the input visitor, at which point user_creatable_add_type() as called with a QMP input visitor via qmp_object_add() MUST follow the same paradigms as everyone else, of pushing into the struct before grabbing its keys. The use of 'err ? NULL : &err' is temporary; a later patch will clean that up when it splits visit_end_struct(). Furthermore, note that both callers always pass qdict, so we can convert the conditional into an assert and reduce indentation. The change has no impact to the testsuite now, but is required to avoid a failure in tests/test-netfilter once qmp-input is made stricter to detect inconsistent 'name' arguments on the root visit. Since user_creatable_add_type() is also called with OptsVisitor through user_creatable_add_opts(), we must also check that there is no negative impact there; both pre- and post-patch, we see: $ ./x86_64-softmmu/qemu-system-x86_64 -nographic -nodefaults -qmp stdio -object secret,id=sec0,data=letmein,format=raw,foo=bar qemu-system-x86_64: -object secret,id=sec0,data=letmein,format=raw,foo=bar: Property '.foo' not found That is, the only new checking that the new visit_end_struct() can perform is for excess input, but we already catch excess input earlier in object_property_set(). Signed-off-by: Eric Blake <eblake@redhat.com> Message-Id: <1461879932-9020-10-git-send-email-eblake@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-04-29 00:45:17 +03:00
assert(qdict);
obj = object_new(type);
qapi: Use returned bool to check for failure, Coccinelle part The previous commit enables conversion of visit_foo(..., &err); if (err) { ... } to if (!visit_foo(..., errp)) { ... } for visitor functions that now return true / false on success / error. Coccinelle script: @@ identifier fun =~ "check_list|input_type_enum|lv_start_struct|lv_type_bool|lv_type_int64|lv_type_str|lv_type_uint64|output_type_enum|parse_type_bool|parse_type_int64|parse_type_null|parse_type_number|parse_type_size|parse_type_str|parse_type_uint64|print_type_bool|print_type_int64|print_type_null|print_type_number|print_type_size|print_type_str|print_type_uint64|qapi_clone_start_alternate|qapi_clone_start_list|qapi_clone_start_struct|qapi_clone_type_bool|qapi_clone_type_int64|qapi_clone_type_null|qapi_clone_type_number|qapi_clone_type_str|qapi_clone_type_uint64|qapi_dealloc_start_list|qapi_dealloc_start_struct|qapi_dealloc_type_anything|qapi_dealloc_type_bool|qapi_dealloc_type_int64|qapi_dealloc_type_null|qapi_dealloc_type_number|qapi_dealloc_type_str|qapi_dealloc_type_uint64|qobject_input_check_list|qobject_input_check_struct|qobject_input_start_alternate|qobject_input_start_list|qobject_input_start_struct|qobject_input_type_any|qobject_input_type_bool|qobject_input_type_bool_keyval|qobject_input_type_int64|qobject_input_type_int64_keyval|qobject_input_type_null|qobject_input_type_number|qobject_input_type_number_keyval|qobject_input_type_size_keyval|qobject_input_type_str|qobject_input_type_str_keyval|qobject_input_type_uint64|qobject_input_type_uint64_keyval|qobject_output_start_list|qobject_output_start_struct|qobject_output_type_any|qobject_output_type_bool|qobject_output_type_int64|qobject_output_type_null|qobject_output_type_number|qobject_output_type_str|qobject_output_type_uint64|start_list|visit_check_list|visit_check_struct|visit_start_alternate|visit_start_list|visit_start_struct|visit_type_.*"; expression list args; typedef Error; Error *err; @@ - fun(args, &err); - if (err) + if (!fun(args, &err)) { ... } A few line breaks tidied up manually. Signed-off-by: Markus Armbruster <armbru@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com> Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> Message-Id: <20200707160613.848843-19-armbru@redhat.com>
2020-07-07 19:05:46 +03:00
if (!visit_start_struct(v, NULL, NULL, 0, &local_err)) {
qom: Wrap prop visit in visit_start_struct The qmp-input visitor was allowing callers to play rather fast and loose: when visiting a QDict, you could grab members of the root dictionary without first pushing into the dict; the final such culprit was the QOM code for converting to and from object properties. But we are about to tighten the input visitor, at which point user_creatable_add_type() as called with a QMP input visitor via qmp_object_add() MUST follow the same paradigms as everyone else, of pushing into the struct before grabbing its keys. The use of 'err ? NULL : &err' is temporary; a later patch will clean that up when it splits visit_end_struct(). Furthermore, note that both callers always pass qdict, so we can convert the conditional into an assert and reduce indentation. The change has no impact to the testsuite now, but is required to avoid a failure in tests/test-netfilter once qmp-input is made stricter to detect inconsistent 'name' arguments on the root visit. Since user_creatable_add_type() is also called with OptsVisitor through user_creatable_add_opts(), we must also check that there is no negative impact there; both pre- and post-patch, we see: $ ./x86_64-softmmu/qemu-system-x86_64 -nographic -nodefaults -qmp stdio -object secret,id=sec0,data=letmein,format=raw,foo=bar qemu-system-x86_64: -object secret,id=sec0,data=letmein,format=raw,foo=bar: Property '.foo' not found That is, the only new checking that the new visit_end_struct() can perform is for excess input, but we already catch excess input earlier in object_property_set(). Signed-off-by: Eric Blake <eblake@redhat.com> Message-Id: <1461879932-9020-10-git-send-email-eblake@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-04-29 00:45:17 +03:00
goto out;
}
for (e = qdict_first(qdict); e; e = qdict_next(qdict, e)) {
if (!object_property_set(obj, e->key, v, &local_err)) {
qom: Wrap prop visit in visit_start_struct The qmp-input visitor was allowing callers to play rather fast and loose: when visiting a QDict, you could grab members of the root dictionary without first pushing into the dict; the final such culprit was the QOM code for converting to and from object properties. But we are about to tighten the input visitor, at which point user_creatable_add_type() as called with a QMP input visitor via qmp_object_add() MUST follow the same paradigms as everyone else, of pushing into the struct before grabbing its keys. The use of 'err ? NULL : &err' is temporary; a later patch will clean that up when it splits visit_end_struct(). Furthermore, note that both callers always pass qdict, so we can convert the conditional into an assert and reduce indentation. The change has no impact to the testsuite now, but is required to avoid a failure in tests/test-netfilter once qmp-input is made stricter to detect inconsistent 'name' arguments on the root visit. Since user_creatable_add_type() is also called with OptsVisitor through user_creatable_add_opts(), we must also check that there is no negative impact there; both pre- and post-patch, we see: $ ./x86_64-softmmu/qemu-system-x86_64 -nographic -nodefaults -qmp stdio -object secret,id=sec0,data=letmein,format=raw,foo=bar qemu-system-x86_64: -object secret,id=sec0,data=letmein,format=raw,foo=bar: Property '.foo' not found That is, the only new checking that the new visit_end_struct() can perform is for excess input, but we already catch excess input earlier in object_property_set(). Signed-off-by: Eric Blake <eblake@redhat.com> Message-Id: <1461879932-9020-10-git-send-email-eblake@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-04-29 00:45:17 +03:00
break;
}
}
qapi: Split visit_end_struct() into pieces As mentioned in previous patches, we want to call visit_end_struct() functions unconditionally, so that visitors can release resources tied up since the matching visit_start_struct() without also having to worry about error priority if more than one error occurs. Even though error_propagate() can be safely used to ignore a second error during cleanup caused by a first error, it is simpler if the cleanup cannot set an error. So, split out the error checking portion (basically, input visitors checking for unvisited keys) into a new function visit_check_struct(), which can be safely skipped if any earlier errors are encountered, and leave the cleanup portion (which never fails, but must be called unconditionally if visit_start_struct() succeeded) in visit_end_struct(). Generated code in qapi-visit.c has diffs resembling: |@@ -59,10 +59,12 @@ void visit_type_ACPIOSTInfo(Visitor *v, | goto out_obj; | } | visit_type_ACPIOSTInfo_members(v, obj, &err); |- error_propagate(errp, err); |- err = NULL; |+ if (err) { |+ goto out_obj; |+ } |+ visit_check_struct(v, &err); | out_obj: |- visit_end_struct(v, &err); |+ visit_end_struct(v); | out: and in qapi-event.c: @@ -47,7 +47,10 @@ void qapi_event_send_acpi_device_ost(ACP | goto out; | } | visit_type_q_obj_ACPI_DEVICE_OST_arg_members(v, &param, &err); |- visit_end_struct(v, err ? NULL : &err); |+ if (!err) { |+ visit_check_struct(v, &err); |+ } |+ visit_end_struct(v); | if (err) { | goto out; Signed-off-by: Eric Blake <eblake@redhat.com> Message-Id: <1461879932-9020-20-git-send-email-eblake@redhat.com> [Conflict with a doc fixup resolved] Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-04-29 00:45:27 +03:00
if (!local_err) {
visit_check_struct(v, &local_err);
}
2016-06-09 19:48:34 +03:00
visit_end_struct(v, NULL);
qom: Wrap prop visit in visit_start_struct The qmp-input visitor was allowing callers to play rather fast and loose: when visiting a QDict, you could grab members of the root dictionary without first pushing into the dict; the final such culprit was the QOM code for converting to and from object properties. But we are about to tighten the input visitor, at which point user_creatable_add_type() as called with a QMP input visitor via qmp_object_add() MUST follow the same paradigms as everyone else, of pushing into the struct before grabbing its keys. The use of 'err ? NULL : &err' is temporary; a later patch will clean that up when it splits visit_end_struct(). Furthermore, note that both callers always pass qdict, so we can convert the conditional into an assert and reduce indentation. The change has no impact to the testsuite now, but is required to avoid a failure in tests/test-netfilter once qmp-input is made stricter to detect inconsistent 'name' arguments on the root visit. Since user_creatable_add_type() is also called with OptsVisitor through user_creatable_add_opts(), we must also check that there is no negative impact there; both pre- and post-patch, we see: $ ./x86_64-softmmu/qemu-system-x86_64 -nographic -nodefaults -qmp stdio -object secret,id=sec0,data=letmein,format=raw,foo=bar qemu-system-x86_64: -object secret,id=sec0,data=letmein,format=raw,foo=bar: Property '.foo' not found That is, the only new checking that the new visit_end_struct() can perform is for excess input, but we already catch excess input earlier in object_property_set(). Signed-off-by: Eric Blake <eblake@redhat.com> Message-Id: <1461879932-9020-10-git-send-email-eblake@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-04-29 00:45:17 +03:00
if (local_err) {
goto out;
}
if (id != NULL) {
qom: Introduce object_property_try_add_child() object_property_add() does not allow object_property_try_add() to gracefully fail as &error_abort is passed as an error handle. However such failure can easily be triggered from the QMP shell when, for instance, one attempts to create an object with an id that already exists. This is achieved from the following call path: qmp_object_add -> user_creatable_add_dict -> user_creatable_add_type -> object_property_add_child -> object_property_add For instance, from the qmp-shell, call twice: object-add qom-type=memory-backend-ram id=mem1 props.size=1073741824 and QEMU aborts. This behavior is undesired as a user/management application mistake in reusing a property ID shouldn't result in loss of the VM and live data within. This patch introduces a new function, object_property_try_add_child() which takes an error handle and turn object_property_try_add() into a non-static one. Now the call path becomes: user_creatable_add_type -> object_property_try_add_child -> object_property_try_add and the error is returned gracefully to the QMP client. (QEMU) object-add qom-type=memory-backend-ram id=mem2 props.size=4294967296 {"return": {}} (QEMU) object-add qom-type=memory-backend-ram id=mem2 props.size=4294967296 {"error": {"class": "GenericError", "desc": "attempt to add duplicate property 'mem2' to object (type 'container')"}} Signed-off-by: Eric Auger <eric.auger@redhat.com> Fixes: d2623129a7de ("qom: Drop parameter @errp of object_property_add() & friends") Reviewed-by: Markus Armbruster <armbru@redhat.com> Reviewed-by: Greg Kurz <groug@kaod.org> Tested-by: Greg Kurz <groug@kaod.org> Message-Id: <20200629193424.30280-2-eric.auger@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2020-06-29 22:34:22 +03:00
object_property_try_add_child(object_get_objects_root(),
id, obj, &local_err);
if (local_err) {
goto out;
}
}
if (!user_creatable_complete(USER_CREATABLE(obj), &local_err)) {
if (id != NULL) {
object_property_del(object_get_objects_root(), id);
}
goto out;
}
out:
if (local_err) {
error_propagate(errp, local_err);
object_unref(obj);
return NULL;
}
return obj;
}
bool user_creatable_add_dict(QDict *qdict, bool keyval, Error **errp)
{
Visitor *v;
Object *obj;
g_autofree char *type = NULL;
g_autofree char *id = NULL;
type = g_strdup(qdict_get_try_str(qdict, "qom-type"));
if (!type) {
error_setg(errp, QERR_MISSING_PARAMETER, "qom-type");
return false;
}
qdict_del(qdict, "qom-type");
id = g_strdup(qdict_get_try_str(qdict, "id"));
if (!id) {
error_setg(errp, QERR_MISSING_PARAMETER, "id");
return false;
}
qdict_del(qdict, "id");
if (keyval) {
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
} else {
v = qobject_input_visitor_new(QOBJECT(qdict));
}
obj = user_creatable_add_type(type, id, qdict, v, errp);
visit_free(v);
object_unref(obj);
return !!obj;
}
Object *user_creatable_add_opts(QemuOpts *opts, Error **errp)
{
Visitor *v;
QDict *pdict;
Object *obj;
const char *id = qemu_opts_id(opts);
qom: Avoid unvisited 'id'/'qom-type' in user_creatable_add_opts A regression in commit 15c2f669e caused us to silently ignore excess input to the QemuOpts visitor. Later, commit ea4641 accidentally abused that situation, by removing "qom-type" and "id" from the corresponding QDict but leaving them defined in the QemuOpts, when using the pair of containers to create a user-defined object. Note that since we are already traversing two separate items (a QDict and a QemuOpts), we are already able to flag bogus arguments, as in: $ ./x86_64-softmmu/qemu-system-x86_64 -nodefaults -nographic -qmp stdio -object memory-backend-ram,id=mem1,size=4k,bogus=huh qemu-system-x86_64: -object memory-backend-ram,id=mem1,size=4k,bogus=huh: Property '.bogus' not found So the only real concern is that when we re-enable strict checking in the QemuOpts visitor, we do not want to start flagging the two leftover keys as unvisited. Rearrange the code to clean out the QemuOpts listing in advance, rather than removing items from the QDict. Since "qom-type" is usually an automatic implicit default, we don't have to restore it (this does mean that once instantiated, QemuOpts is not necessarily an accurate representation of the original command line - but this is not the first place to do that); however "id" has to be put back (requiring us to cast away a const). [As a side note, hmp_object_add() turns a QDict into a QemuOpts, then calls user_creatable_add_opts() which converts QemuOpts into a new QDict. There are probably a lot of wasteful conversions like this, but cleaning them up is a much bigger task than the immediate regression fix.] CC: qemu-stable@nongnu.org Signed-off-by: Eric Blake <eblake@redhat.com> Message-Id: <20170322144525.18964-3-eblake@redhat.com> Tested-by: Laurent Vivier <lvivier@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com>
2017-03-22 17:45:24 +03:00
char *type = qemu_opt_get_del(opts, "qom-type");
if (!type) {
error_setg(errp, QERR_MISSING_PARAMETER, "qom-type");
return NULL;
}
if (!id) {
error_setg(errp, QERR_MISSING_PARAMETER, "id");
qemu_opt_set(opts, "qom-type", type, &error_abort);
qom: Avoid unvisited 'id'/'qom-type' in user_creatable_add_opts A regression in commit 15c2f669e caused us to silently ignore excess input to the QemuOpts visitor. Later, commit ea4641 accidentally abused that situation, by removing "qom-type" and "id" from the corresponding QDict but leaving them defined in the QemuOpts, when using the pair of containers to create a user-defined object. Note that since we are already traversing two separate items (a QDict and a QemuOpts), we are already able to flag bogus arguments, as in: $ ./x86_64-softmmu/qemu-system-x86_64 -nodefaults -nographic -qmp stdio -object memory-backend-ram,id=mem1,size=4k,bogus=huh qemu-system-x86_64: -object memory-backend-ram,id=mem1,size=4k,bogus=huh: Property '.bogus' not found So the only real concern is that when we re-enable strict checking in the QemuOpts visitor, we do not want to start flagging the two leftover keys as unvisited. Rearrange the code to clean out the QemuOpts listing in advance, rather than removing items from the QDict. Since "qom-type" is usually an automatic implicit default, we don't have to restore it (this does mean that once instantiated, QemuOpts is not necessarily an accurate representation of the original command line - but this is not the first place to do that); however "id" has to be put back (requiring us to cast away a const). [As a side note, hmp_object_add() turns a QDict into a QemuOpts, then calls user_creatable_add_opts() which converts QemuOpts into a new QDict. There are probably a lot of wasteful conversions like this, but cleaning them up is a much bigger task than the immediate regression fix.] CC: qemu-stable@nongnu.org Signed-off-by: Eric Blake <eblake@redhat.com> Message-Id: <20170322144525.18964-3-eblake@redhat.com> Tested-by: Laurent Vivier <lvivier@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com>
2017-03-22 17:45:24 +03:00
g_free(type);
return NULL;
}
qom: Avoid unvisited 'id'/'qom-type' in user_creatable_add_opts A regression in commit 15c2f669e caused us to silently ignore excess input to the QemuOpts visitor. Later, commit ea4641 accidentally abused that situation, by removing "qom-type" and "id" from the corresponding QDict but leaving them defined in the QemuOpts, when using the pair of containers to create a user-defined object. Note that since we are already traversing two separate items (a QDict and a QemuOpts), we are already able to flag bogus arguments, as in: $ ./x86_64-softmmu/qemu-system-x86_64 -nodefaults -nographic -qmp stdio -object memory-backend-ram,id=mem1,size=4k,bogus=huh qemu-system-x86_64: -object memory-backend-ram,id=mem1,size=4k,bogus=huh: Property '.bogus' not found So the only real concern is that when we re-enable strict checking in the QemuOpts visitor, we do not want to start flagging the two leftover keys as unvisited. Rearrange the code to clean out the QemuOpts listing in advance, rather than removing items from the QDict. Since "qom-type" is usually an automatic implicit default, we don't have to restore it (this does mean that once instantiated, QemuOpts is not necessarily an accurate representation of the original command line - but this is not the first place to do that); however "id" has to be put back (requiring us to cast away a const). [As a side note, hmp_object_add() turns a QDict into a QemuOpts, then calls user_creatable_add_opts() which converts QemuOpts into a new QDict. There are probably a lot of wasteful conversions like this, but cleaning them up is a much bigger task than the immediate regression fix.] CC: qemu-stable@nongnu.org Signed-off-by: Eric Blake <eblake@redhat.com> Message-Id: <20170322144525.18964-3-eblake@redhat.com> Tested-by: Laurent Vivier <lvivier@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com>
2017-03-22 17:45:24 +03:00
qemu_opts_set_id(opts, NULL);
pdict = qemu_opts_to_qdict(opts, NULL);
v = opts_visitor_new(opts);
obj = user_creatable_add_type(type, id, pdict, v, errp);
visit_free(v);
qom: Avoid unvisited 'id'/'qom-type' in user_creatable_add_opts A regression in commit 15c2f669e caused us to silently ignore excess input to the QemuOpts visitor. Later, commit ea4641 accidentally abused that situation, by removing "qom-type" and "id" from the corresponding QDict but leaving them defined in the QemuOpts, when using the pair of containers to create a user-defined object. Note that since we are already traversing two separate items (a QDict and a QemuOpts), we are already able to flag bogus arguments, as in: $ ./x86_64-softmmu/qemu-system-x86_64 -nodefaults -nographic -qmp stdio -object memory-backend-ram,id=mem1,size=4k,bogus=huh qemu-system-x86_64: -object memory-backend-ram,id=mem1,size=4k,bogus=huh: Property '.bogus' not found So the only real concern is that when we re-enable strict checking in the QemuOpts visitor, we do not want to start flagging the two leftover keys as unvisited. Rearrange the code to clean out the QemuOpts listing in advance, rather than removing items from the QDict. Since "qom-type" is usually an automatic implicit default, we don't have to restore it (this does mean that once instantiated, QemuOpts is not necessarily an accurate representation of the original command line - but this is not the first place to do that); however "id" has to be put back (requiring us to cast away a const). [As a side note, hmp_object_add() turns a QDict into a QemuOpts, then calls user_creatable_add_opts() which converts QemuOpts into a new QDict. There are probably a lot of wasteful conversions like this, but cleaning them up is a much bigger task than the immediate regression fix.] CC: qemu-stable@nongnu.org Signed-off-by: Eric Blake <eblake@redhat.com> Message-Id: <20170322144525.18964-3-eblake@redhat.com> Tested-by: Laurent Vivier <lvivier@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com>
2017-03-22 17:45:24 +03:00
qemu_opts_set_id(opts, (char *) id);
qemu_opt_set(opts, "qom-type", type, &error_abort);
qom: Avoid unvisited 'id'/'qom-type' in user_creatable_add_opts A regression in commit 15c2f669e caused us to silently ignore excess input to the QemuOpts visitor. Later, commit ea4641 accidentally abused that situation, by removing "qom-type" and "id" from the corresponding QDict but leaving them defined in the QemuOpts, when using the pair of containers to create a user-defined object. Note that since we are already traversing two separate items (a QDict and a QemuOpts), we are already able to flag bogus arguments, as in: $ ./x86_64-softmmu/qemu-system-x86_64 -nodefaults -nographic -qmp stdio -object memory-backend-ram,id=mem1,size=4k,bogus=huh qemu-system-x86_64: -object memory-backend-ram,id=mem1,size=4k,bogus=huh: Property '.bogus' not found So the only real concern is that when we re-enable strict checking in the QemuOpts visitor, we do not want to start flagging the two leftover keys as unvisited. Rearrange the code to clean out the QemuOpts listing in advance, rather than removing items from the QDict. Since "qom-type" is usually an automatic implicit default, we don't have to restore it (this does mean that once instantiated, QemuOpts is not necessarily an accurate representation of the original command line - but this is not the first place to do that); however "id" has to be put back (requiring us to cast away a const). [As a side note, hmp_object_add() turns a QDict into a QemuOpts, then calls user_creatable_add_opts() which converts QemuOpts into a new QDict. There are probably a lot of wasteful conversions like this, but cleaning them up is a much bigger task than the immediate regression fix.] CC: qemu-stable@nongnu.org Signed-off-by: Eric Blake <eblake@redhat.com> Message-Id: <20170322144525.18964-3-eblake@redhat.com> Tested-by: Laurent Vivier <lvivier@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com>
2017-03-22 17:45:24 +03:00
g_free(type);
qobject_unref(pdict);
return obj;
}
int user_creatable_add_opts_foreach(void *opaque, QemuOpts *opts, Error **errp)
{
bool (*type_opt_predicate)(const char *, QemuOpts *) = opaque;
Object *obj = NULL;
const char *type;
type = qemu_opt_get(opts, "qom-type");
if (type && type_opt_predicate &&
!type_opt_predicate(type, opts)) {
return 0;
}
obj = user_creatable_add_opts(opts, errp);
if (!obj) {
return -1;
}
object_unref(obj);
return 0;
}
char *object_property_help(const char *name, const char *type,
QObject *defval, const char *description)
{
GString *str = g_string_new(NULL);
g_string_append_printf(str, " %s=<%s>", name, type);
if (description || defval) {
if (str->len < 24) {
g_string_append_printf(str, "%*s", 24 - (int)str->len, "");
}
g_string_append(str, " - ");
}
if (description) {
g_string_append(str, description);
}
if (defval) {
g_autofree char *def_json = g_string_free(qobject_to_json(defval),
true);
g_string_append_printf(str, " (default: %s)", def_json);
}
return g_string_free(str, false);
}
static void user_creatable_print_types(void)
{
GSList *l, *list;
printf("List of user creatable objects:\n");
list = object_class_get_list_sorted(TYPE_USER_CREATABLE, false);
for (l = list; l != NULL; l = l->next) {
ObjectClass *oc = OBJECT_CLASS(l->data);
printf(" %s\n", object_class_get_name(oc));
}
g_slist_free(list);
}
static bool user_creatable_print_type_properites(const char *type)
{
ObjectClass *klass;
ObjectPropertyIterator iter;
ObjectProperty *prop;
GPtrArray *array;
int i;
klass = object_class_by_name(type);
if (!klass) {
return false;
}
array = g_ptr_array_new();
object_class_property_iter_init(&iter, klass);
while ((prop = object_property_iter_next(&iter))) {
if (!prop->set) {
continue;
}
g_ptr_array_add(array,
object_property_help(prop->name, prop->type,
prop->defval, prop->description));
}
g_ptr_array_sort(array, (GCompareFunc)qemu_pstrcmp0);
if (array->len > 0) {
printf("%s options:\n", type);
} else {
printf("There are no options for %s.\n", type);
}
for (i = 0; i < array->len; i++) {
printf("%s\n", (char *)array->pdata[i]);
}
g_ptr_array_set_free_func(array, g_free);
g_ptr_array_free(array, true);
return true;
}
bool user_creatable_print_help(const char *type, QemuOpts *opts)
{
if (is_help_option(type)) {
user_creatable_print_types();
return true;
}
if (qemu_opt_has_help_opt(opts)) {
return user_creatable_print_type_properites(type);
}
return false;
}
void user_creatable_print_help_from_qdict(QDict *args)
{
const char *type = qdict_get_try_str(args, "qom-type");
if (!type || !user_creatable_print_type_properites(type)) {
user_creatable_print_types();
}
}
bool 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 false;
}
if (!user_creatable_can_be_deleted(USER_CREATABLE(obj))) {
error_setg(errp, "object '%s' is in use, can not be deleted", id);
return false;
}
monitor: fix object_del for command-line-created objects Currently objects specified on the command-line are only partially cleaned up when 'object_del' is issued in either HMP or QMP: the object itself is fully finalized, but the QemuOpts are not removed. This results in the following behavior: x86_64-softmmu/qemu-system-x86_64 -monitor stdio \ -object memory-backend-ram,id=ram1,size=256M QEMU 2.7.91 monitor - type 'help' for more information (qemu) object_del ram1 (qemu) object_del ram1 object 'ram1' not found (qemu) object_add memory-backend-ram,id=ram1,size=256M Duplicate ID 'ram1' for object Try "help object_add" for more information which can be an issue for use-cases like memory hotplug. This happens on the HMP side because hmp_object_add() attempts to create a temporary QemuOpts entry with ID 'ram1', which ends up conflicting with the command-line-created entry, since it was never cleaned up during the previous hmp_object_del() call. We address this by adding a check in user_creatable_del(), which is called by both qmp_object_del() and hmp_object_del() to handle the actual object cleanup, to determine whether an option group entry matching the object's ID is present and removing it if it is. Note that qmp_object_add() never attempts to create a temporary QemuOpts entry, so it does not encounter the duplicate ID error, which is why this isn't generally visible in libvirt. Cc: "Dr. David Alan Gilbert" <dgilbert@redhat.com> Cc: Markus Armbruster <armbru@redhat.com> Cc: Eric Blake <eblake@redhat.com> Cc: Daniel Berrange <berrange@redhat.com> Cc: qemu-stable@nongnu.org Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com> Reviewed-by: Daniel P. Berrange <berrange@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> Message-Id: <1496531612-22166-3-git-send-email-mdroth@linux.vnet.ibm.com> Signed-off-by: Markus Armbruster <armbru@redhat.com>
2017-06-04 02:13:32 +03:00
/*
* if object was defined on the command-line, remove its corresponding
* option group entry
*/
qemu_opts_del(qemu_opts_find(qemu_find_opts_err("object", &error_abort),
id));
object_unparent(obj);
return true;
}
void user_creatable_cleanup(void)
{
object_unparent(object_get_objects_root());
}
static void register_types(void)
{
static const TypeInfo uc_interface_info = {
.name = TYPE_USER_CREATABLE,
.parent = TYPE_INTERFACE,
.class_size = sizeof(UserCreatableClass),
};
type_register_static(&uc_interface_info);
}
type_init(register_types)