mirror of
https://github.com/qemu/qemu.git
synced 2026-07-08 17:46:17 +00:00
While most objects can perform all their cleanup in the finalizer method, there can be interactions with other resources / subsystems / threads which require that some cleanup be performed on an user creatable object before unparenting it and entering finalization. The current 'can_be_deleted' method runs in the deletion path and is intended to be used to block deletion. While it could be used to perform cleanup tasks, its name suggests it should be free of side-effects. Generalize this by renaming it to 'prepare_delete', explicitly allowing for cleanup to be provided. Existing users of 'can_be_deleted' are re-written, which provides them with more detailed/tailored error messages. Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> Message-ID: <20260706135824.2623960-2-berrange@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com>
288 lines
7.1 KiB
C
288 lines
7.1 KiB
C
#include "qemu/osdep.h"
|
|
|
|
#include "qemu/cutils.h"
|
|
#include "qapi/error.h"
|
|
#include "qapi/qapi-visit-qom.h"
|
|
#include "qobject/qobject.h"
|
|
#include "qobject/qbool.h"
|
|
#include "qobject/qdict.h"
|
|
#include "qapi/qmp/qerror.h"
|
|
#include "qobject/qjson.h"
|
|
#include "qobject/qstring.h"
|
|
#include "qapi/qobject-input-visitor.h"
|
|
#include "qapi/qobject-output-visitor.h"
|
|
#include "qom/object_interfaces.h"
|
|
#include "qemu/help_option.h"
|
|
#include "qemu/id.h"
|
|
#include "qemu/module.h"
|
|
#include "qemu/option.h"
|
|
#include "qemu/qemu-print.h"
|
|
#include "qapi/opts-visitor.h"
|
|
#include "qemu/config-file.h"
|
|
#include "qemu/keyval.h"
|
|
|
|
bool user_creatable_complete(UserCreatable *uc, Error **errp)
|
|
{
|
|
UserCreatableClass *ucc = USER_CREATABLE_GET_CLASS(uc);
|
|
ERRP_GUARD();
|
|
|
|
if (ucc->complete) {
|
|
ucc->complete(uc, errp);
|
|
}
|
|
return !*errp;
|
|
}
|
|
|
|
bool user_creatable_prepare_delete(UserCreatable *uc, Error **errp)
|
|
{
|
|
UserCreatableClass *ucc = USER_CREATABLE_GET_CLASS(uc);
|
|
|
|
if (ucc->prepare_delete) {
|
|
return ucc->prepare_delete(uc, errp);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void user_creatable_add_qapi(ObjectOptions *options, Error **errp)
|
|
{
|
|
Visitor *v;
|
|
QObject *qobj;
|
|
QDict *props;
|
|
|
|
v = qobject_output_visitor_new(&qobj);
|
|
visit_type_ObjectOptions(v, NULL, &options, &error_abort);
|
|
visit_complete(v, &qobj);
|
|
visit_free(v);
|
|
|
|
props = qobject_to(QDict, qobj);
|
|
qdict_del(props, "qom-type");
|
|
qdict_del(props, "id");
|
|
|
|
v = qobject_input_visitor_new(QOBJECT(props));
|
|
object_new_with_props_from_qdict(ObjectType_str(options->qom_type),
|
|
object_get_objects_root(),
|
|
options->id, props, v, errp);
|
|
qobject_unref(qobj);
|
|
visit_free(v);
|
|
}
|
|
|
|
char *object_property_help(const char *name, const char *type,
|
|
QObject *defval, const char *description)
|
|
{
|
|
GString *str = g_string_new(NULL);
|
|
|
|
g_string_append_printf(str, " %s=<%s>", name, type);
|
|
if (description || defval) {
|
|
if (str->len < 24) {
|
|
g_string_append_printf(str, "%*s", 24 - (int)str->len, "");
|
|
}
|
|
g_string_append(str, " - ");
|
|
}
|
|
if (description) {
|
|
g_string_append(str, description);
|
|
}
|
|
if (defval) {
|
|
g_autofree char *def_json = NULL;
|
|
const char *def;
|
|
|
|
switch (qobject_type(defval)) {
|
|
case QTYPE_QSTRING:
|
|
def = qstring_get_str(qobject_to(QString, defval));
|
|
break;
|
|
|
|
case QTYPE_QBOOL:
|
|
def = qbool_get_bool(qobject_to(QBool, defval)) ? "on" : "off";
|
|
break;
|
|
|
|
default:
|
|
def_json = g_string_free(qobject_to_json(defval), false);
|
|
def = def_json;
|
|
break;
|
|
}
|
|
|
|
g_string_append_printf(str, " (default: %s)", def);
|
|
}
|
|
|
|
return g_string_free(str, false);
|
|
}
|
|
|
|
static void user_creatable_print_types(void)
|
|
{
|
|
GSList *l, *list;
|
|
|
|
qemu_printf("List of user creatable objects:\n");
|
|
list = object_class_get_list_sorted(TYPE_USER_CREATABLE, false);
|
|
for (l = list; l != NULL; l = l->next) {
|
|
ObjectClass *oc = OBJECT_CLASS(l->data);
|
|
qemu_printf(" %s\n", object_class_get_name(oc));
|
|
}
|
|
g_slist_free(list);
|
|
}
|
|
|
|
bool type_print_class_properties(const char *type)
|
|
{
|
|
ObjectClass *klass;
|
|
ObjectPropertyIterator iter;
|
|
ObjectProperty *prop;
|
|
GPtrArray *array;
|
|
int i;
|
|
|
|
klass = object_class_by_name(type);
|
|
if (!klass) {
|
|
return false;
|
|
}
|
|
|
|
array = g_ptr_array_new();
|
|
object_class_property_iter_init(&iter, klass);
|
|
while ((prop = object_property_iter_next(&iter))) {
|
|
if (!prop->set) {
|
|
continue;
|
|
}
|
|
|
|
g_ptr_array_add(array,
|
|
object_property_help(prop->name, prop->type,
|
|
prop->defval, prop->description));
|
|
}
|
|
g_ptr_array_sort(array, (GCompareFunc)qemu_pstrcmp0);
|
|
if (array->len > 0) {
|
|
qemu_printf("%s options:\n", type);
|
|
} else {
|
|
qemu_printf("There are no options for %s.\n", type);
|
|
}
|
|
for (i = 0; i < array->len; i++) {
|
|
qemu_printf("%s\n", (char *)array->pdata[i]);
|
|
}
|
|
g_ptr_array_set_free_func(array, g_free);
|
|
g_ptr_array_free(array, true);
|
|
return true;
|
|
}
|
|
|
|
bool user_creatable_print_help(const char *type, QemuOpts *opts)
|
|
{
|
|
if (is_help_option(type)) {
|
|
user_creatable_print_types();
|
|
return true;
|
|
}
|
|
|
|
if (qemu_opt_has_help_opt(opts)) {
|
|
return type_print_class_properties(type);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static void user_creatable_print_help_from_qdict(QDict *args)
|
|
{
|
|
const char *type = qdict_get_try_str(args, "qom-type");
|
|
|
|
if (!type || !type_print_class_properties(type)) {
|
|
user_creatable_print_types();
|
|
}
|
|
}
|
|
|
|
ObjectOptions *user_creatable_parse_str(const char *str, Error **errp)
|
|
{
|
|
ERRP_GUARD();
|
|
QObject *obj;
|
|
bool help;
|
|
Visitor *v;
|
|
ObjectOptions *options;
|
|
|
|
if (str[0] == '{') {
|
|
obj = qobject_from_json(str, errp);
|
|
if (!obj) {
|
|
return NULL;
|
|
}
|
|
v = qobject_input_visitor_new(obj);
|
|
} else {
|
|
QDict *args = keyval_parse(str, "qom-type", &help, errp);
|
|
if (*errp) {
|
|
return NULL;
|
|
}
|
|
if (help) {
|
|
user_creatable_print_help_from_qdict(args);
|
|
qobject_unref(args);
|
|
return NULL;
|
|
}
|
|
|
|
obj = QOBJECT(args);
|
|
v = qobject_input_visitor_new_keyval(obj);
|
|
}
|
|
|
|
visit_type_ObjectOptions(v, NULL, &options, errp);
|
|
visit_free(v);
|
|
qobject_unref(obj);
|
|
|
|
return options;
|
|
}
|
|
|
|
bool user_creatable_add_from_str(const char *str, Error **errp)
|
|
{
|
|
ERRP_GUARD();
|
|
ObjectOptions *options;
|
|
|
|
options = user_creatable_parse_str(str, errp);
|
|
if (!options) {
|
|
return false;
|
|
}
|
|
|
|
user_creatable_add_qapi(options, errp);
|
|
qapi_free_ObjectOptions(options);
|
|
return !*errp;
|
|
}
|
|
|
|
void user_creatable_process_cmdline(const char *cmdline)
|
|
{
|
|
if (!user_creatable_add_from_str(cmdline, &error_fatal)) {
|
|
/* Help was printed */
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
}
|
|
|
|
bool user_creatable_del(const char *id, Error **errp)
|
|
{
|
|
QemuOptsList *opts_list;
|
|
Object *container;
|
|
Object *obj;
|
|
|
|
container = object_get_objects_root();
|
|
obj = object_resolve_path_component(container, id);
|
|
if (!obj) {
|
|
error_setg(errp, "object '%s' not found", id);
|
|
return false;
|
|
}
|
|
|
|
if (!user_creatable_prepare_delete(USER_CREATABLE(obj), errp)) {
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* if object was defined on the command-line, remove its corresponding
|
|
* option group entry
|
|
*/
|
|
opts_list = qemu_find_opts_err("object", NULL);
|
|
if (opts_list) {
|
|
qemu_opts_del(qemu_opts_find(opts_list, id));
|
|
}
|
|
|
|
object_unparent(obj);
|
|
return true;
|
|
}
|
|
|
|
void user_creatable_cleanup(void)
|
|
{
|
|
object_unparent(object_get_objects_root());
|
|
}
|
|
|
|
static void register_types(void)
|
|
{
|
|
static const TypeInfo uc_interface_info = {
|
|
.name = TYPE_USER_CREATABLE,
|
|
.parent = TYPE_INTERFACE,
|
|
.class_size = sizeof(UserCreatableClass),
|
|
};
|
|
|
|
type_register_static(&uc_interface_info);
|
|
}
|
|
|
|
type_init(register_types)
|