@samitouri / QOSamiQemu / commits / 75f31e3a8a

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>

Daniel P. Berrangé committed Jul 6, 2026 at 14:58 UTC 75f31e3a8a16fef89b103516646c70b723b64c39
7 files changed +138 -46
monitor/hmp.c
+44 -26
@@ -40,6 +40,7 @@
40 #include "qemu/target-info.h"
41 #include "qemu/units.h"
42 #include "qapi/error.h"
43 +#include "qom/object_interfaces.h"
44 #include "exec/gdbstub.h"
45 #include "system/block-backend.h"
46 #include "trace.h"
@@ -71,10 +72,13 @@ static void monitor_hmp_set_readline(Object *obj, bool val, Error **errp)
72 int monitor_hmp_vprintf(Monitor *mon, const char *fmt, va_list ap)
73 G_GNUC_PRINTF(2, 0);
74 static void monitor_hmp_accept_input(Monitor *mon);
75 +static void monitor_hmp_complete(UserCreatable *uc, Error **errp);
76 +static bool monitor_hmp_prepare_delete(UserCreatable *uc, Error **errp);
77
78 static void monitor_hmp_class_init(ObjectClass *cls, const void *data)
79 {
80 MonitorClass *moncls = MONITOR_CLASS(cls);
81 + UserCreatableClass *ucc = USER_CREATABLE_CLASS(cls);
82
83 object_class_property_add_bool(cls, "readline",
84 monitor_hmp_get_readline,
@@ -82,6 +86,9 @@ static void monitor_hmp_class_init(ObjectClass *cls, const void *data)
86
87 moncls->vprintf = monitor_hmp_vprintf;
88 moncls->accept_input = monitor_hmp_accept_input;
89 +
90 + ucc->complete = monitor_hmp_complete;
91 + ucc->prepare_delete = monitor_hmp_prepare_delete;
92 }
93
94 static void monitor_hmp_init(Object *obj)
@@ -1605,41 +1612,52 @@ static void monitor_readline_flush(void *opaque)
1612 void monitor_new_hmp(const char *id, const char *chardev_id,
1613 bool use_readline, Error **errp)
1614 {
1608 - ERRP_GUARD();
1609 - MonitorHMP *mon;
1615 g_autofree char *autoid = id ? NULL : monitor_compat_id();
1611 - Object *obj = object_new_with_props(TYPE_MONITOR_HMP,
1612 - object_get_objects_root(),
1613 - id ? id : autoid,
1614 - errp,
1615 - "chardev", chardev_id,
1616 - "readline", use_readline ? "yes" : "no",
1617 - NULL);
1618 -
1619 - if (!obj) {
1620 - return;
1621 - }
1616 + object_new_with_props(TYPE_MONITOR_HMP,
1617 + object_get_objects_root(),
1618 + id ? id : autoid,
1619 + errp,
1620 + "chardev", chardev_id,
1621 + "readline", use_readline ? "yes" : "no",
1622 + NULL);
1623 +}
1624
1623 - mon = MONITOR_HMP(obj);
1625 +static void monitor_hmp_complete(UserCreatable *uc, Error **errp)
1626 +{
1627 + MonitorHMP *mon = MONITOR_HMP(uc);
1628 + UserCreatableClass *ucc_parent =
1629 + USER_CREATABLE_CLASS(
1630 + object_class_get_parent(
1631 + OBJECT_CLASS(MONITOR_HMP_GET_CLASS(mon))));
1632 + ERRP_GUARD();
1633
1625 - monitor_complete(MONITOR(mon), errp);
1634 + ucc_parent->complete(uc, errp);
1635 if (*errp) {
1627 - object_unparent(OBJECT(mon));
1636 return;
1637 }
1638
1631 - if (mon->use_readline) {
1632 - mon->rs = readline_init(monitor_readline_printf,
1633 - monitor_readline_flush,
1634 - mon,
1635 - monitor_find_completion);
1636 - monitor_read_command(mon, 0);
1639 + if (mon->parent_obj.chardev_id) {
1640 + if (mon->use_readline) {
1641 + mon->rs = readline_init(monitor_readline_printf,
1642 + monitor_readline_flush,
1643 + mon,
1644 + monitor_find_completion);
1645 + monitor_read_command(mon, 0);
1646 + }
1647 +
1648 + qemu_chr_fe_set_handlers(&mon->parent_obj.chr,
1649 + monitor_can_read,
1650 + monitor_read,
1651 + monitor_event, NULL,
1652 + &mon->parent_obj, NULL, true);
1653 + monitor_list_append(&mon->parent_obj);
1654 }
1655 +}
1656
1639 - qemu_chr_fe_set_handlers(&mon->parent_obj.chr,
1640 - monitor_can_read, monitor_read, monitor_event,
1641 - NULL, &mon->parent_obj, NULL, true);
1642 - monitor_list_append(&mon->parent_obj);
1657 +static bool monitor_hmp_prepare_delete(UserCreatable *uc, Error **errp)
1658 +{
1659 + error_setg(errp, "Deleting HMP monitors is not supported");
1660 + return false;
1661 }
1662
1663 /**
monitor/monitor-internal.h
-1
@@ -204,7 +204,6 @@ extern QmpCommandList qmp_commands, qmp_cap_negotiation_commands;
204 extern QemuMutex monitor_lock;
205 extern MonitorList mon_list;
206
207 -void monitor_complete(Monitor *mon, Error **errp);
207 bool monitor_requires_iothread(const Monitor *mon);
208 int monitor_can_read(void *opaque);
209 void monitor_list_append(Monitor *mon);
monitor/monitor.c
+12 -2
@@ -29,6 +29,7 @@
29 #include "qapi/qapi-emit-events.h"
30 #include "qapi/qapi-visit-control.h"
31 #include "qobject/qdict.h"
32 +#include "qom/object_interfaces.h"
33 #include "qemu/error-report.h"
34 #include "qemu/option.h"
35 #include "system/qtest.h"
@@ -75,7 +76,8 @@ static bool monitor_destroyed;
76
77 int monitor_device_index;
78
78 -OBJECT_DEFINE_ABSTRACT_TYPE(Monitor, monitor, MONITOR, OBJECT);
79 +OBJECT_DEFINE_TYPE_EXTENDED(Monitor, monitor, MONITOR, OBJECT, true,
80 + { TYPE_USER_CREATABLE }, {});
81
82 static void monitor_finalize(Object *obj)
83 {
@@ -103,11 +105,17 @@ static void monitor_set_chardev_id(Object *obj, const char *str, Error **errp)
105 mon->chardev_id = g_strdup(str);
106 }
107
108 +static void monitor_complete(UserCreatable *uc, Error **errp);
109 +
110 static void monitor_class_init(ObjectClass *cls, const void *data)
111 {
112 + UserCreatableClass *ucc = USER_CREATABLE_CLASS(cls);
113 +
114 object_class_property_add_str(cls, "chardev",
115 monitor_get_chardev_id,
116 monitor_set_chardev_id);
117 +
118 + ucc->complete = monitor_complete;
119 }
120
121 static void monitor_init(Object *obj)
@@ -689,8 +697,10 @@ char *monitor_compat_id(void)
697 return g_strdup_printf("compat_monitor%d", monitor_device_index++);
698 }
699
692 -void monitor_complete(Monitor *mon, Error **errp)
700 +static void monitor_complete(UserCreatable *uc, Error **errp)
701 {
702 + Monitor *mon = MONITOR(uc);
703 +
704 if (mon->chardev_id) {
705 Chardev *chr = qemu_chr_find(mon->chardev_id);
706 if (chr == NULL) {
monitor/qmp.c
+31 -16
@@ -31,6 +31,7 @@
31 #include "qobject/qdict.h"
32 #include "qobject/qjson.h"
33 #include "qobject/qlist.h"
34 +#include "qom/object_interfaces.h"
35 #include "trace.h"
36
37 /*
@@ -101,10 +102,13 @@ static void monitor_qmp_set_pretty(Object *obj, bool val, Error **errp)
102
103 static void monitor_qmp_emit_event(Monitor *mon, QAPIEvent event, QDict *qdict);
104 static bool monitor_qmp_requires_iothread(const Monitor *mon);
105 +static void monitor_qmp_complete(UserCreatable *uc, Error **errp);
106 +static bool monitor_qmp_prepare_delete(UserCreatable *uc, Error **errp);
107
108 static void monitor_qmp_class_init(ObjectClass *cls, const void *data)
109 {
110 MonitorClass *moncls = MONITOR_CLASS(cls);
111 + UserCreatableClass *ucc = USER_CREATABLE_CLASS(cls);
112
113 object_class_property_add_bool(cls, "pretty",
114 monitor_qmp_get_pretty,
@@ -112,6 +116,9 @@ static void monitor_qmp_class_init(ObjectClass *cls, const void *data)
116
117 moncls->emit_event = monitor_qmp_emit_event;
118 moncls->requires_iothread = monitor_qmp_requires_iothread;
119 +
120 + ucc->complete = monitor_qmp_complete;
121 + ucc->prepare_delete = monitor_qmp_prepare_delete;
122 }
123
124 static void handle_qmp_command(void *opaque, QObject *req, Error *err);
@@ -583,25 +590,27 @@ static void monitor_qmp_setup_handlers_bh(void *opaque)
590 void monitor_new_qmp(const char *id, const char *chardev_id,
591 bool pretty, Error **errp)
592 {
586 - ERRP_GUARD();
587 - MonitorQMP *mon;
593 g_autofree char *autoid = id ? NULL : monitor_compat_id();
589 - Object *obj = object_new_with_props(TYPE_MONITOR_QMP,
590 - object_get_objects_root(),
591 - id ? id : autoid,
592 - errp,
593 - "chardev", chardev_id,
594 - "pretty", pretty ? "yes" : "no",
595 - NULL);
596 -
597 - if (!obj) {
598 - return;
599 - }
594 + object_new_with_props(TYPE_MONITOR_QMP,
595 + object_get_objects_root(),
596 + id ? id : autoid,
597 + errp,
598 + "chardev", chardev_id,
599 + "pretty", pretty ? "yes" : "no",
600 + NULL);
601 +}
602
601 - mon = MONITOR_QMP(obj);
602 - monitor_complete(MONITOR(mon), errp);
603 +static void monitor_qmp_complete(UserCreatable *uc, Error **errp)
604 +{
605 + MonitorQMP *mon = MONITOR_QMP(uc);
606 + UserCreatableClass *ucc_parent =
607 + USER_CREATABLE_CLASS(
608 + object_class_get_parent(
609 + OBJECT_CLASS(MONITOR_QMP_GET_CLASS(mon))));
610 + ERRP_GUARD();
611 +
612 + ucc_parent->complete(uc, errp);
613 if (*errp) {
604 - object_unparent(OBJECT(mon));
614 return;
615 }
616
@@ -633,3 +642,9 @@ void monitor_new_qmp(const char *id, const char *chardev_id,
642 monitor_list_append(&mon->parent_obj);
643 }
644 }
645 +
646 +static bool monitor_qmp_prepare_delete(UserCreatable *uc, Error **errp)
647 +{
648 + error_setg(errp, "Deleting QMP monitors is not supported");
649 + return false;
650 +}
qapi/qom.json
+43
@@ -1187,6 +1187,45 @@
1187 'data': { '*cpu-affinity': ['uint16'],
1188 '*node-affinity': ['uint16'] } }
1189
1190 +##
1191 +# @MonitorProperties:
1192 +#
1193 +# Properties for all monitors
1194 +#
1195 +# @chardev: ID of the character device providing the monitor transport
1196 +#
1197 +# Since: 11.1
1198 +##
1199 +{ 'struct': 'MonitorProperties',
1200 + 'data': { 'chardev': 'str' }}
1201 +
1202 +##
1203 +# @MonitorHMPProperties:
1204 +#
1205 +# Properties for the HMP monitor
1206 +#
1207 +# @readline: whether to enable readline for line editing
1208 +# (default: true)
1209 +#
1210 +# Since: 11.1
1211 +##
1212 +{ 'struct': 'MonitorHMPProperties',
1213 + 'base': 'MonitorProperties',
1214 + 'data': { '*readline': 'bool' } }
1215 +
1216 +##
1217 +# @MonitorQMPProperties:
1218 +#
1219 +# Properties for the QMP monitor
1220 +#
1221 +# @pretty: whether to pretty print JSON responses (default: false)
1222 +#
1223 +# Since: 11.1
1224 +##
1225 +{ 'struct': 'MonitorQMPProperties',
1226 + 'base': 'MonitorProperties',
1227 + 'data': { '*pretty': 'bool' } }
1228 +
1229 ##
1230 # @ObjectType:
1231 #
@@ -1237,6 +1276,8 @@
1276 'memory-backend-ram',
1277 { 'name': 'memory-backend-shm',
1278 'if': 'CONFIG_POSIX' },
1279 + 'monitor-hmp',
1280 + 'monitor-qmp',
1281 'pef-guest',
1282 { 'name': 'pr-manager-helper',
1283 'if': 'CONFIG_LINUX' },
@@ -1315,6 +1356,8 @@
1356 'memory-backend-ram': 'MemoryBackendProperties',
1357 'memory-backend-shm': { 'type': 'MemoryBackendShmProperties',
1358 'if': 'CONFIG_POSIX' },
1359 + 'monitor-hmp': 'MonitorHMPProperties',
1360 + 'monitor-qmp': 'MonitorQMPProperties',
1361 'pr-manager-helper': { 'type': 'PrManagerHelperProperties',
1362 'if': 'CONFIG_LINUX' },
1363 'qtest': 'QtestProperties',
stubs/monitor-internal.c
+1
@@ -11,4 +11,5 @@ int monitor_get_fd(Monitor *mon, const char *name, Error **errp)
11 void monitor_new_hmp(const char *id, const char *chardev_id,
12 bool use_readline, Error **errp)
13 {
14 + g_assert_not_reached();
15 }
system/vl.c
+7 -1
@@ -1829,6 +1829,10 @@ static void object_option_add_visitor(Visitor *v)
1829 {
1830 ObjectOption *opt = g_new0(ObjectOption, 1);
1831 visit_type_ObjectOptions(v, NULL, &opt->opts, &error_fatal);
1832 + if (opt->opts->qom_type == OBJECT_TYPE_MONITOR_HMP ||
1833 + opt->opts->qom_type == OBJECT_TYPE_MONITOR_QMP) {
1834 + default_monitor = 0;
1835 + }
1836 QTAILQ_INSERT_TAIL(&object_opts, opt, next);
1837 }
1838
@@ -1970,7 +1974,9 @@ static bool object_create_early(const char *type)
1974
1975 /* Reason: property "chardev" */
1976 if (g_str_equal(type, "rng-egd") ||
1973 - g_str_equal(type, "qtest")) {
1977 + g_str_equal(type, "qtest") ||
1978 + g_str_equal(type, "monitor-hmp") ||
1979 + g_str_equal(type, "monitor-qmp")) {
1980 return false;
1981 }
1982