monitor: implement "user creatable" interface for adding monitors

Implement the user creatable QOM interface and define the monitor-qmp
and monitor-hmp types in QAPI. This unlocks the ability to create them
on the command line with -object or in HMP/QMP with object_add.

For example:

  $QEMU -chardev stdio,id=monchr0 -object monitor-hmp,id=mon0,chrdev=monchr0

Initially the "prepare_delete" callback is hardcoded to return an error
which means -object and object_add can be used, but object_del will fail.
Support for deleting monitors will be introduced in subsequent commits.

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Tested-by: Peter Krempa <pkrempa@redhat.com>
Acked-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
Message-ID: <20260706135824.2623960-24-berrange@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
This commit is contained in:
Daniel P. Berrangé
2026-07-06 14:58:11 +01:00
committed by Markus Armbruster
parent b465a08b0c
commit 75f31e3a8a
7 changed files with 138 additions and 46 deletions

View File

@@ -40,6 +40,7 @@
#include "qemu/target-info.h"
#include "qemu/units.h"
#include "qapi/error.h"
#include "qom/object_interfaces.h"
#include "exec/gdbstub.h"
#include "system/block-backend.h"
#include "trace.h"
@@ -71,10 +72,13 @@ static void monitor_hmp_set_readline(Object *obj, bool val, Error **errp)
int monitor_hmp_vprintf(Monitor *mon, const char *fmt, va_list ap)
G_GNUC_PRINTF(2, 0);
static void monitor_hmp_accept_input(Monitor *mon);
static void monitor_hmp_complete(UserCreatable *uc, Error **errp);
static bool monitor_hmp_prepare_delete(UserCreatable *uc, Error **errp);
static void monitor_hmp_class_init(ObjectClass *cls, const void *data)
{
MonitorClass *moncls = MONITOR_CLASS(cls);
UserCreatableClass *ucc = USER_CREATABLE_CLASS(cls);
object_class_property_add_bool(cls, "readline",
monitor_hmp_get_readline,
@@ -82,6 +86,9 @@ static void monitor_hmp_class_init(ObjectClass *cls, const void *data)
moncls->vprintf = monitor_hmp_vprintf;
moncls->accept_input = monitor_hmp_accept_input;
ucc->complete = monitor_hmp_complete;
ucc->prepare_delete = monitor_hmp_prepare_delete;
}
static void monitor_hmp_init(Object *obj)
@@ -1605,29 +1612,31 @@ static void monitor_readline_flush(void *opaque)
void monitor_new_hmp(const char *id, const char *chardev_id,
bool use_readline, Error **errp)
{
ERRP_GUARD();
MonitorHMP *mon;
g_autofree char *autoid = id ? NULL : monitor_compat_id();
Object *obj = object_new_with_props(TYPE_MONITOR_HMP,
object_new_with_props(TYPE_MONITOR_HMP,
object_get_objects_root(),
id ? id : autoid,
errp,
"chardev", chardev_id,
"readline", use_readline ? "yes" : "no",
NULL);
if (!obj) {
return;
}
mon = MONITOR_HMP(obj);
static void monitor_hmp_complete(UserCreatable *uc, Error **errp)
{
MonitorHMP *mon = MONITOR_HMP(uc);
UserCreatableClass *ucc_parent =
USER_CREATABLE_CLASS(
object_class_get_parent(
OBJECT_CLASS(MONITOR_HMP_GET_CLASS(mon))));
ERRP_GUARD();
monitor_complete(MONITOR(mon), errp);
ucc_parent->complete(uc, errp);
if (*errp) {
object_unparent(OBJECT(mon));
return;
}
if (mon->parent_obj.chardev_id) {
if (mon->use_readline) {
mon->rs = readline_init(monitor_readline_printf,
monitor_readline_flush,
@@ -1637,10 +1646,19 @@ void monitor_new_hmp(const char *id, const char *chardev_id,
}
qemu_chr_fe_set_handlers(&mon->parent_obj.chr,
monitor_can_read, monitor_read, monitor_event,
NULL, &mon->parent_obj, NULL, true);
monitor_can_read,
monitor_read,
monitor_event, NULL,
&mon->parent_obj, NULL, true);
monitor_list_append(&mon->parent_obj);
}
}
static bool monitor_hmp_prepare_delete(UserCreatable *uc, Error **errp)
{
error_setg(errp, "Deleting HMP monitors is not supported");
return false;
}
/**
* Is @name in the '|' separated list of names @list?

View File

@@ -204,7 +204,6 @@ extern QmpCommandList qmp_commands, qmp_cap_negotiation_commands;
extern QemuMutex monitor_lock;
extern MonitorList mon_list;
void monitor_complete(Monitor *mon, Error **errp);
bool monitor_requires_iothread(const Monitor *mon);
int monitor_can_read(void *opaque);
void monitor_list_append(Monitor *mon);

View File

@@ -29,6 +29,7 @@
#include "qapi/qapi-emit-events.h"
#include "qapi/qapi-visit-control.h"
#include "qobject/qdict.h"
#include "qom/object_interfaces.h"
#include "qemu/error-report.h"
#include "qemu/option.h"
#include "system/qtest.h"
@@ -75,7 +76,8 @@ static bool monitor_destroyed;
int monitor_device_index;
OBJECT_DEFINE_ABSTRACT_TYPE(Monitor, monitor, MONITOR, OBJECT);
OBJECT_DEFINE_TYPE_EXTENDED(Monitor, monitor, MONITOR, OBJECT, true,
{ TYPE_USER_CREATABLE }, {});
static void monitor_finalize(Object *obj)
{
@@ -103,11 +105,17 @@ static void monitor_set_chardev_id(Object *obj, const char *str, Error **errp)
mon->chardev_id = g_strdup(str);
}
static void monitor_complete(UserCreatable *uc, Error **errp);
static void monitor_class_init(ObjectClass *cls, const void *data)
{
UserCreatableClass *ucc = USER_CREATABLE_CLASS(cls);
object_class_property_add_str(cls, "chardev",
monitor_get_chardev_id,
monitor_set_chardev_id);
ucc->complete = monitor_complete;
}
static void monitor_init(Object *obj)
@@ -689,8 +697,10 @@ char *monitor_compat_id(void)
return g_strdup_printf("compat_monitor%d", monitor_device_index++);
}
void monitor_complete(Monitor *mon, Error **errp)
static void monitor_complete(UserCreatable *uc, Error **errp)
{
Monitor *mon = MONITOR(uc);
if (mon->chardev_id) {
Chardev *chr = qemu_chr_find(mon->chardev_id);
if (chr == NULL) {

View File

@@ -31,6 +31,7 @@
#include "qobject/qdict.h"
#include "qobject/qjson.h"
#include "qobject/qlist.h"
#include "qom/object_interfaces.h"
#include "trace.h"
/*
@@ -101,10 +102,13 @@ static void monitor_qmp_set_pretty(Object *obj, bool val, Error **errp)
static void monitor_qmp_emit_event(Monitor *mon, QAPIEvent event, QDict *qdict);
static bool monitor_qmp_requires_iothread(const Monitor *mon);
static void monitor_qmp_complete(UserCreatable *uc, Error **errp);
static bool monitor_qmp_prepare_delete(UserCreatable *uc, Error **errp);
static void monitor_qmp_class_init(ObjectClass *cls, const void *data)
{
MonitorClass *moncls = MONITOR_CLASS(cls);
UserCreatableClass *ucc = USER_CREATABLE_CLASS(cls);
object_class_property_add_bool(cls, "pretty",
monitor_qmp_get_pretty,
@@ -112,6 +116,9 @@ static void monitor_qmp_class_init(ObjectClass *cls, const void *data)
moncls->emit_event = monitor_qmp_emit_event;
moncls->requires_iothread = monitor_qmp_requires_iothread;
ucc->complete = monitor_qmp_complete;
ucc->prepare_delete = monitor_qmp_prepare_delete;
}
static void handle_qmp_command(void *opaque, QObject *req, Error *err);
@@ -583,25 +590,27 @@ static void monitor_qmp_setup_handlers_bh(void *opaque)
void monitor_new_qmp(const char *id, const char *chardev_id,
bool pretty, Error **errp)
{
ERRP_GUARD();
MonitorQMP *mon;
g_autofree char *autoid = id ? NULL : monitor_compat_id();
Object *obj = object_new_with_props(TYPE_MONITOR_QMP,
object_new_with_props(TYPE_MONITOR_QMP,
object_get_objects_root(),
id ? id : autoid,
errp,
"chardev", chardev_id,
"pretty", pretty ? "yes" : "no",
NULL);
if (!obj) {
return;
}
mon = MONITOR_QMP(obj);
monitor_complete(MONITOR(mon), errp);
static void monitor_qmp_complete(UserCreatable *uc, Error **errp)
{
MonitorQMP *mon = MONITOR_QMP(uc);
UserCreatableClass *ucc_parent =
USER_CREATABLE_CLASS(
object_class_get_parent(
OBJECT_CLASS(MONITOR_QMP_GET_CLASS(mon))));
ERRP_GUARD();
ucc_parent->complete(uc, errp);
if (*errp) {
object_unparent(OBJECT(mon));
return;
}
@@ -633,3 +642,9 @@ void monitor_new_qmp(const char *id, const char *chardev_id,
monitor_list_append(&mon->parent_obj);
}
}
static bool monitor_qmp_prepare_delete(UserCreatable *uc, Error **errp)
{
error_setg(errp, "Deleting QMP monitors is not supported");
return false;
}

View File

@@ -1187,6 +1187,45 @@
'data': { '*cpu-affinity': ['uint16'],
'*node-affinity': ['uint16'] } }
##
# @MonitorProperties:
#
# Properties for all monitors
#
# @chardev: ID of the character device providing the monitor transport
#
# Since: 11.1
##
{ 'struct': 'MonitorProperties',
'data': { 'chardev': 'str' }}
##
# @MonitorHMPProperties:
#
# Properties for the HMP monitor
#
# @readline: whether to enable readline for line editing
# (default: true)
#
# Since: 11.1
##
{ 'struct': 'MonitorHMPProperties',
'base': 'MonitorProperties',
'data': { '*readline': 'bool' } }
##
# @MonitorQMPProperties:
#
# Properties for the QMP monitor
#
# @pretty: whether to pretty print JSON responses (default: false)
#
# Since: 11.1
##
{ 'struct': 'MonitorQMPProperties',
'base': 'MonitorProperties',
'data': { '*pretty': 'bool' } }
##
# @ObjectType:
#
@@ -1237,6 +1276,8 @@
'memory-backend-ram',
{ 'name': 'memory-backend-shm',
'if': 'CONFIG_POSIX' },
'monitor-hmp',
'monitor-qmp',
'pef-guest',
{ 'name': 'pr-manager-helper',
'if': 'CONFIG_LINUX' },
@@ -1315,6 +1356,8 @@
'memory-backend-ram': 'MemoryBackendProperties',
'memory-backend-shm': { 'type': 'MemoryBackendShmProperties',
'if': 'CONFIG_POSIX' },
'monitor-hmp': 'MonitorHMPProperties',
'monitor-qmp': 'MonitorQMPProperties',
'pr-manager-helper': { 'type': 'PrManagerHelperProperties',
'if': 'CONFIG_LINUX' },
'qtest': 'QtestProperties',

View File

@@ -11,4 +11,5 @@ int monitor_get_fd(Monitor *mon, const char *name, Error **errp)
void monitor_new_hmp(const char *id, const char *chardev_id,
bool use_readline, Error **errp)
{
g_assert_not_reached();
}

View File

@@ -1829,6 +1829,10 @@ static void object_option_add_visitor(Visitor *v)
{
ObjectOption *opt = g_new0(ObjectOption, 1);
visit_type_ObjectOptions(v, NULL, &opt->opts, &error_fatal);
if (opt->opts->qom_type == OBJECT_TYPE_MONITOR_HMP ||
opt->opts->qom_type == OBJECT_TYPE_MONITOR_QMP) {
default_monitor = 0;
}
QTAILQ_INSERT_TAIL(&object_opts, opt, next);
}
@@ -1970,7 +1974,9 @@ static bool object_create_early(const char *type)
/* Reason: property "chardev" */
if (g_str_equal(type, "rng-egd") ||
g_str_equal(type, "qtest")) {
g_str_equal(type, "qtest") ||
g_str_equal(type, "monitor-hmp") ||
g_str_equal(type, "monitor-qmp")) {
return false;
}