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>
---
monitor/hmp.c | 70 ++++++++++++++++++++++++--------------
monitor/monitor-internal.h | 1 -
monitor/monitor.c | 14 ++++++--
monitor/qmp.c | 47 ++++++++++++++++---------
qapi/qom.json | 43 +++++++++++++++++++++++
stubs/monitor-internal.c | 1 +
system/vl.c | 8 ++++-
7 files changed, 138 insertions(+), 46 deletions(-)
diff --git a/monitor/hmp.c b/monitor/hmp.c
index a51de8e467..75b93f29a3 100644
--- a/monitor/hmp.c
+++ b/monitor/hmp.c
@@ -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,41 +1612,52 @@ 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_get_objects_root(),
- id ? id : autoid,
- errp,
- "chardev", chardev_id,
- "readline", use_readline ? "yes" : "no",
- NULL);
-
- if (!obj) {
- return;
- }
+ object_new_with_props(TYPE_MONITOR_HMP,
+ object_get_objects_root(),
+ id ? id : autoid,
+ errp,
+ "chardev", chardev_id,
+ "readline", use_readline ? "yes" : "no",
+ NULL);
+}
- 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->use_readline) {
- mon->rs = readline_init(monitor_readline_printf,
- monitor_readline_flush,
- mon,
- monitor_find_completion);
- monitor_read_command(mon, 0);
+ if (mon->parent_obj.chardev_id) {
+ if (mon->use_readline) {
+ mon->rs = readline_init(monitor_readline_printf,
+ monitor_readline_flush,
+ mon,
+ monitor_find_completion);
+ monitor_read_command(mon, 0);
+ }
+
+ qemu_chr_fe_set_handlers(&mon->parent_obj.chr,
+ monitor_can_read,
+ monitor_read,
+ monitor_event, NULL,
+ &mon->parent_obj, NULL, true);
+ monitor_list_append(&mon->parent_obj);
}
+}
- qemu_chr_fe_set_handlers(&mon->parent_obj.chr,
- 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;
}
/**
diff --git a/monitor/monitor-internal.h b/monitor/monitor-internal.h
index fe5703af6d..012d442a20 100644
--- a/monitor/monitor-internal.h
+++ b/monitor/monitor-internal.h
@@ -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);
diff --git a/monitor/monitor.c b/monitor/monitor.c
index f1b189c1c2..f0ca6b04ed 100644
--- a/monitor/monitor.c
+++ b/monitor/monitor.c
@@ -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) {
diff --git a/monitor/qmp.c b/monitor/qmp.c
index bd3e6f8375..67ee491f5a 100644
--- a/monitor/qmp.c
+++ b/monitor/qmp.c
@@ -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_get_objects_root(),
- id ? id : autoid,
- errp,
- "chardev", chardev_id,
- "pretty", pretty ? "yes" : "no",
- NULL);
-
- if (!obj) {
- return;
- }
+ object_new_with_props(TYPE_MONITOR_QMP,
+ object_get_objects_root(),
+ id ? id : autoid,
+ errp,
+ "chardev", chardev_id,
+ "pretty", pretty ? "yes" : "no",
+ NULL);
+}
- 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;
+}
diff --git a/qapi/qom.json b/qapi/qom.json
index dd45ac1087..33a827b9fa 100644
--- a/qapi/qom.json
+++ b/qapi/qom.json
@@ -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',
diff --git a/stubs/monitor-internal.c b/stubs/monitor-internal.c
index f94b5e5c21..731fad221e 100644
--- a/stubs/monitor-internal.c
+++ b/stubs/monitor-internal.c
@@ -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();
}
diff --git a/system/vl.c b/system/vl.c
index 4737c03872..63bdd06dfa 100644
--- a/system/vl.c
+++ b/system/vl.c
@@ -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;
}
--
2.55.0