summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--monitor.c113
-rw-r--r--monitor.h2
2 files changed, 111 insertions, 4 deletions
diff --git a/monitor.c b/monitor.c
index 1cdb0c848f..be34329cfc 100644
--- a/monitor.c
+++ b/monitor.c
@@ -76,6 +76,12 @@
*
*/
+typedef struct MonitorCompletionData MonitorCompletionData;
+struct MonitorCompletionData {
+ Monitor *mon;
+ void (*user_print)(Monitor *mon, const QObject *data);
+};
+
typedef struct mon_cmd_t {
const char *name;
const char *args_type;
@@ -85,9 +91,13 @@ typedef struct mon_cmd_t {
union {
void (*info)(Monitor *mon);
void (*info_new)(Monitor *mon, QObject **ret_data);
+ int (*info_async)(Monitor *mon, MonitorCompletion *cb, void *opaque);
void (*cmd)(Monitor *mon, const QDict *qdict);
void (*cmd_new)(Monitor *mon, const QDict *params, QObject **ret_data);
+ int (*cmd_async)(Monitor *mon, const QDict *params,
+ MonitorCompletion *cb, void *opaque);
} mhandler;
+ int async;
} mon_cmd_t;
/* file descriptors passed via SCM_RIGHTS */
@@ -255,6 +265,11 @@ static inline int monitor_handler_ported(const mon_cmd_t *cmd)
return cmd->user_print != NULL;
}
+static inline bool monitor_handler_is_async(const mon_cmd_t *cmd)
+{
+ return cmd->async != 0;
+}
+
static inline int monitor_has_error(const Monitor *mon)
{
return mon->error != NULL;
@@ -453,6 +468,65 @@ static void do_commit(Monitor *mon, const QDict *qdict)
}
}
+static void user_monitor_complete(void *opaque, QObject *ret_data)
+{
+ MonitorCompletionData *data = (MonitorCompletionData *)opaque;
+
+ if (ret_data) {
+ data->user_print(data->mon, ret_data);
+ }
+ monitor_resume(data->mon);
+ qemu_free(data);
+}
+
+static void qmp_monitor_complete(void *opaque, QObject *ret_data)
+{
+ monitor_protocol_emitter(opaque, ret_data);
+}
+
+static void qmp_async_cmd_handler(Monitor *mon, const mon_cmd_t *cmd,
+ const QDict *params)
+{
+ cmd->mhandler.cmd_async(mon, params, qmp_monitor_complete, mon);
+}
+
+static void qmp_async_info_handler(Monitor *mon, const mon_cmd_t *cmd)
+{
+ cmd->mhandler.info_async(mon, qmp_monitor_complete, mon);
+}
+
+static void user_async_cmd_handler(Monitor *mon, const mon_cmd_t *cmd,
+ const QDict *params)
+{
+ int ret;
+
+ MonitorCompletionData *cb_data = qemu_malloc(sizeof(*cb_data));
+ cb_data->mon = mon;
+ cb_data->user_print = cmd->user_print;
+ monitor_suspend(mon);
+ ret = cmd->mhandler.cmd_async(mon, params,
+ user_monitor_complete, cb_data);
+ if (ret < 0) {
+ monitor_resume(mon);
+ qemu_free(cb_data);
+ }
+}
+
+static void user_async_info_handler(Monitor *mon, const mon_cmd_t *cmd)
+{
+ int ret;
+
+ MonitorCompletionData *cb_data = qemu_malloc(sizeof(*cb_data));
+ cb_data->mon = mon;
+ cb_data->user_print = cmd->user_print;
+ monitor_suspend(mon);
+ ret = cmd->mhandler.info_async(mon, user_monitor_complete, cb_data);
+ if (ret < 0) {
+ monitor_resume(mon);
+ qemu_free(cb_data);
+ }
+}
+
static void do_info(Monitor *mon, const QDict *qdict, QObject **ret_data)
{
const mon_cmd_t *cmd;
@@ -476,7 +550,19 @@ static void do_info(Monitor *mon, const QDict *qdict, QObject **ret_data)
goto help;
}
- if (monitor_handler_ported(cmd)) {
+ if (monitor_handler_is_async(cmd)) {
+ if (monitor_ctrl_mode(mon)) {
+ qmp_async_info_handler(mon, cmd);
+ } else {
+ user_async_info_handler(mon, cmd);
+ }
+ /*
+ * Indicate that this command is asynchronous and will not return any
+ * data (not even empty). Instead, the data will be returned via a
+ * completion callback.
+ */
+ *ret_data = qobject_from_jsonf("{ '__mon_async': 'return' }");
+ } else if (monitor_handler_ported(cmd)) {
cmd->mhandler.info_new(mon, ret_data);
if (!monitor_ctrl_mode(mon)) {
@@ -3588,6 +3674,11 @@ static void monitor_print_error(Monitor *mon)
mon->error = NULL;
}
+static int is_async_return(const QObject *data)
+{
+ return data && qdict_haskey(qobject_to_qdict(data), "__mon_async");
+}
+
static void monitor_call_handler(Monitor *mon, const mon_cmd_t *cmd,
const QDict *params)
{
@@ -3595,7 +3686,15 @@ static void monitor_call_handler(Monitor *mon, const mon_cmd_t *cmd,
cmd->mhandler.cmd_new(mon, params, &data);
- if (monitor_ctrl_mode(mon)) {
+ if (is_async_return(data)) {
+ /*
+ * Asynchronous commands have no initial return data but they can
+ * generate errors. Data is returned via the async completion handler.
+ */
+ if (monitor_ctrl_mode(mon) && monitor_has_error(mon)) {
+ monitor_protocol_emitter(mon, NULL);
+ }
+ } else if (monitor_ctrl_mode(mon)) {
/* Monitor Protocol */
monitor_protocol_emitter(mon, data);
} else {
@@ -3620,7 +3719,9 @@ static void handle_user_command(Monitor *mon, const char *cmdline)
qemu_errors_to_mon(mon);
- if (monitor_handler_ported(cmd)) {
+ if (monitor_handler_is_async(cmd)) {
+ user_async_cmd_handler(mon, cmd, qdict);
+ } else if (monitor_handler_ported(cmd)) {
monitor_call_handler(mon, cmd, qdict);
} else {
cmd->mhandler.cmd(mon, qdict);
@@ -4082,7 +4183,11 @@ static void handle_qmp_command(JSONMessageParser *parser, QList *tokens)
goto err_out;
}
- monitor_call_handler(mon, cmd, args);
+ if (monitor_handler_is_async(cmd)) {
+ qmp_async_cmd_handler(mon, cmd, args);
+ } else {
+ monitor_call_handler(mon, cmd, args);
+ }
goto out;
err_input:
diff --git a/monitor.h b/monitor.h
index 2da30e8804..b0f9270a93 100644
--- a/monitor.h
+++ b/monitor.h
@@ -44,4 +44,6 @@ void monitor_printf(Monitor *mon, const char *fmt, ...)
void monitor_print_filename(Monitor *mon, const char *filename);
void monitor_flush(Monitor *mon);
+typedef void (MonitorCompletion)(void *opaque, QObject *ret_data);
+
#endif /* !MONITOR_H */