[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH 24/36] qmp: add QmpClient
From: |
marcandre . lureau |
Subject: |
[Qemu-devel] [PATCH 24/36] qmp: add QmpClient |
Date: |
Fri, 25 Sep 2015 16:03:52 +0200 |
From: Marc-André Lureau <address@hidden>
Add a new QmpClient structure holding the dispatch return callback
and the list of pending QmpReturns.
When a client disconnects, call qmp_client_destroy(). This will remove
all pending returns from the client list, and prevent a reply from being
sent later to new clients. That way clients will only receive reply they
expect, and not one from a previous client.
Signed-off-by: Marc-André Lureau <address@hidden>
---
include/qapi/qmp/dispatch.h | 20 +++++++++++++++-----
monitor.c | 9 ++++++---
qapi/qmp-dispatch.c | 33 +++++++++++++++++++++++++++------
qga/main.c | 10 ++++++----
tests/test-qmp-commands.c | 31 ++++++++++++++++++++++---------
5 files changed, 76 insertions(+), 27 deletions(-)
diff --git a/include/qapi/qmp/dispatch.h b/include/qapi/qmp/dispatch.h
index b62acba..747fff9 100644
--- a/include/qapi/qmp/dispatch.h
+++ b/include/qapi/qmp/dispatch.h
@@ -18,14 +18,23 @@
#include "qapi/qmp/qdict.h"
#include "qapi/error.h"
-typedef void (QmpDispatchReturn) (QObject *rsp, void *opaque);
+typedef struct QmpClient QmpClient;
+
+typedef void (QmpDispatchReturn) (QmpClient *client, QObject *rsp);
typedef struct QmpReturn {
QDict *rsp;
- QmpDispatchReturn *return_cb;
- void *opaque;
+ QmpClient *client;
+
+ QLIST_ENTRY(QmpReturn) link;
} QmpReturn;
+struct QmpClient {
+ QmpDispatchReturn *return_cb;
+
+ QLIST_HEAD(, QmpReturn) pending;
+};
+
typedef void (QmpCommandFunc)(QDict *, QObject **, Error **);
typedef enum QmpCommandType
@@ -52,8 +61,9 @@ typedef struct QmpCommand
void qmp_register_command(const char *name, QmpCommandFunc *fn,
QmpCommandOptions options);
QmpCommand *qmp_find_command(const char *name);
-void qmp_dispatch(QObject *request, QDict *rsp,
- QmpDispatchReturn *return_cb, void *opaque);
+void qmp_client_init(QmpClient *client, QmpDispatchReturn *return_cb);
+void qmp_client_destroy(QmpClient *client);
+void qmp_dispatch(QmpClient *client, QObject *request, QDict *rsp);
void qmp_disable_command(const char *name);
void qmp_enable_command(const char *name);
bool qmp_command_is_enabled(const QmpCommand *cmd);
diff --git a/monitor.c b/monitor.c
index 677061d..ca3db31 100644
--- a/monitor.c
+++ b/monitor.c
@@ -173,6 +173,7 @@ typedef struct {
* mode.
*/
bool in_command_mode; /* are we in command mode? */
+ QmpClient client;
} MonitorQMP;
/*
@@ -3570,9 +3571,9 @@ static QDict *qmp_check_input_obj(QObject *input_obj,
Error **errp)
return input_dict;
}
-static void qmp_dispatch_return(QObject *rsp, void *opaque)
+static void qmp_dispatch_return(QmpClient *client, QObject *rsp)
{
- Monitor *mon = opaque;
+ Monitor *mon = container_of(client, Monitor, qmp.client);
monitor_json_emitter(mon, rsp);
}
@@ -3613,7 +3614,7 @@ static void handle_qmp_command(JSONMessageParser *parser,
QList *tokens)
goto err_out;
}
- qmp_dispatch(req, rqdict, qmp_dispatch_return, mon);
+ qmp_dispatch(&mon->qmp.client, req, rqdict);
rsp = NULL;
err_out:
@@ -3714,6 +3715,7 @@ static void monitor_qmp_event(void *opaque, int event)
json_message_parser_init(&mon->qmp.parser, handle_qmp_command);
mon_refcount--;
monitor_fdsets_cleanup();
+ qmp_client_destroy(&mon->qmp.client);
break;
}
}
@@ -3848,6 +3850,7 @@ void monitor_init(CharDriverState *chr, int flags)
monitor_qmp_event, mon);
qemu_chr_fe_set_echo(chr, true);
json_message_parser_init(&mon->qmp.parser, handle_qmp_command);
+ qmp_client_init(&mon->qmp.client, qmp_dispatch_return);
} else {
qemu_chr_add_handlers(chr, monitor_can_read, monitor_read,
monitor_event, mon);
diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c
index 263ab65..d2cc300 100644
--- a/qapi/qmp-dispatch.c
+++ b/qapi/qmp-dispatch.c
@@ -121,9 +121,13 @@ QObject *qmp_build_error_object(Error *err)
static void do_qmp_return(QmpReturn *qret)
{
QDict *rsp = qret->rsp;
+ QmpClient *client = qret->client;
- qret->return_cb(QOBJECT(rsp), qret->opaque);
+ if (client) {
+ client->return_cb(client, QOBJECT(rsp));
+ }
+ QLIST_REMOVE(qret, link);
qobject_decref(QOBJECT(rsp));
g_free(qret);
}
@@ -143,25 +147,42 @@ void qmp_return_error(QmpReturn *qret, Error *err)
do_qmp_return(qret);
}
-void qmp_dispatch(QObject *request, QDict *rsp,
- QmpDispatchReturn *return_cb, void *opaque)
+void qmp_client_init(QmpClient *client, QmpDispatchReturn *return_cb)
+{
+ client->return_cb = return_cb;
+ QLIST_INIT(&client->pending);
+}
+
+void qmp_client_destroy(QmpClient *client)
+{
+ QmpReturn *ret, *next;
+
+ QLIST_FOREACH_SAFE(ret, &client->pending, link, next) {
+ ret->client = NULL;
+ QLIST_REMOVE(ret, link);
+ }
+}
+
+void qmp_dispatch(QmpClient *client, QObject *request, QDict *rsp)
{
Error *err = NULL;
QmpReturn *qret = g_new0(QmpReturn, 1);
QObject *ret;
- assert(return_cb);
+ assert(client);
qret->rsp = rsp ?: qdict_new();
- qret->return_cb = return_cb;
- qret->opaque = opaque;
+ qret->client = client;
+ QLIST_INSERT_HEAD(&client->pending, qret, link);
ret = do_qmp_dispatch(request, qret, &err);
if (err) {
assert(!ret);
qmp_return_error(qret, err);
+ return;
} else if (ret) {
qmp_return(qret, ret);
+ return;
}
}
diff --git a/qga/main.c b/qga/main.c
index 21dba8f..9cd9764 100644
--- a/qga/main.c
+++ b/qga/main.c
@@ -93,6 +93,7 @@ struct GAState {
#endif
gchar *pstate_filepath;
GAPersistentState pstate;
+ QmpClient client;
};
struct GAState *ga_state;
@@ -546,9 +547,9 @@ static int send_response(GAState *s, QObject *payload)
return 0;
}
-static void dispatch_return_cb(QObject *rsp, void *opaque)
+static void dispatch_return_cb(QmpClient *client, QObject *rsp)
{
- GAState *s = opaque;
+ GAState *s = container_of(client, GAState, client);
int ret = send_response(s, rsp);
if (ret) {
@@ -560,7 +561,7 @@ static void process_command(GAState *s, QDict *req)
{
g_assert(req);
g_debug("processing command");
- qmp_dispatch(QOBJECT(req), NULL, dispatch_return_cb, s);
+ qmp_dispatch(&ga_state->client, QOBJECT(req), NULL);
}
/* handle requests/control events coming in over the channel */
@@ -1293,6 +1294,7 @@ static int run_agent(GAState *s, GAConfig *config)
ga_command_state_init_all(s->command_state);
json_message_parser_init(&s->parser, process_event);
ga_state = s;
+ qmp_client_init(&s->client, dispatch_return_cb);
#ifndef _WIN32
if (!register_signal_handlers()) {
g_critical("failed to register signal handlers");
@@ -1390,7 +1392,7 @@ end:
g_list_foreach(config->blacklist, free_blacklist_entry, NULL);
g_free(s->pstate_filepath);
g_free(s->state_filepath_isfrozen);
-
+ qmp_client_destroy(&s->client);
if (config->daemonize) {
unlink(config->pid_filepath);
}
diff --git a/tests/test-qmp-commands.c b/tests/test-qmp-commands.c
index 64d26af..4f49a6e 100644
--- a/tests/test-qmp-commands.c
+++ b/tests/test-qmp-commands.c
@@ -76,7 +76,7 @@ __org_qemu_x_Union1
*qmp___org_qemu_x_command(__org_qemu_x_EnumList *a,
return ret;
}
-static void dispatch_cmd_return(QObject *resp, void *opaque)
+static void dispatch_cmd_return(QmpClient *client, QObject *resp)
{
assert(resp != NULL);
assert(!qdict_haskey(qobject_to_qdict(resp), "error"));
@@ -85,16 +85,20 @@ static void dispatch_cmd_return(QObject *resp, void *opaque)
/* test commands with no input and no return value */
static void test_dispatch_cmd(void)
{
+ QmpClient client;
QDict *req = qdict_new();
- qdict_put_obj(req, "execute", QOBJECT(qstring_from_str("user_def_cmd")));
+ qmp_client_init(&client, dispatch_cmd_return);
- qmp_dispatch(QOBJECT(req), NULL, dispatch_cmd_return, NULL);
+ qdict_put_obj(req, "execute", QOBJECT(qstring_from_str("user_def_cmd")));
+ qmp_dispatch(&client, QOBJECT(req), NULL);
QDECREF(req);
+
+ qmp_client_destroy(&client);
}
-static void dispatch_cmd_error_return(QObject *resp, void *opaque)
+static void dispatch_cmd_error_return(QmpClient *client, QObject *resp)
{
assert(resp != NULL);
assert(qdict_haskey(qobject_to_qdict(resp), "error"));
@@ -103,18 +107,22 @@ static void dispatch_cmd_error_return(QObject *resp, void
*opaque)
/* test commands that return an error due to invalid parameters */
static void test_dispatch_cmd_error(void)
{
+ QmpClient client;
QDict *req = qdict_new();
- qdict_put_obj(req, "execute", QOBJECT(qstring_from_str("user_def_cmd2")));
+ qmp_client_init(&client, dispatch_cmd_error_return);
- qmp_dispatch(QOBJECT(req), NULL, dispatch_cmd_error_return, NULL);
+ qdict_put_obj(req, "execute", QOBJECT(qstring_from_str("user_def_cmd2")));
+ qmp_dispatch(&client, QOBJECT(req), NULL);
QDECREF(req);
+
+ qmp_client_destroy(&client);
}
static QObject *ret;
-static void qmp_dispatch_return(QObject *resp_obj, void *opaque)
+static void qmp_dispatch_return(QmpClient *client, QObject *resp_obj)
{
QDict *resp = qobject_to_qdict(resp_obj);
assert(resp && !qdict_haskey(resp, "error"));
@@ -125,7 +133,13 @@ static void qmp_dispatch_return(QObject *resp_obj, void
*opaque)
static QObject *test_qmp_dispatch(QDict *req)
{
- qmp_dispatch(QOBJECT(req), NULL, qmp_dispatch_return, NULL);
+ QmpClient client;
+
+ qmp_client_init(&client, qmp_dispatch_return);
+
+ qmp_dispatch(&client, QOBJECT(req), NULL);
+
+ qmp_client_destroy(&client);
return ret;
}
@@ -243,7 +257,6 @@ static void test_dealloc_partial(void)
qapi_free_UserDefTwo(ud2);
}
-
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
--
2.4.3
- Re: [Qemu-devel] [PATCH 21/36] misc: spelling, (continued)
- [Qemu-devel] [PATCH 19/36] qmp: implement qmp_query_commands without qmp_cmds, marcandre . lureau, 2015/09/25
- [Qemu-devel] [PATCH 05/36] qapi: move examples to json schema, marcandre . lureau, 2015/09/25
- [Qemu-devel] [PATCH 17/36] qmp: use qmp_dispatch(), marcandre . lureau, 2015/09/25
- [Qemu-devel] [PATCH 23/36] qmp: use a return callback for the command reply, marcandre . lureau, 2015/09/25
- [Qemu-devel] [PATCH 20/36] qmp: remove old qmp-commands table, marcandre . lureau, 2015/09/25
- [Qemu-devel] [PATCH 22/36] qmp: teach qmp_dispatch() to take a pre-filled QDict, marcandre . lureau, 2015/09/25
- [Qemu-devel] [PATCH 24/36] qmp: add QmpClient,
marcandre . lureau <=
- [Qemu-devel] [PATCH 26/36] qmp: check that async command have an 'id', marcandre . lureau, 2015/09/25
- [Qemu-devel] [PATCH 27/36] scripts: learn 'async' qapi commands, marcandre . lureau, 2015/09/25
- [Qemu-devel] [PATCH 25/36] qmp: introduce async command type, marcandre . lureau, 2015/09/25
- [Qemu-devel] [PATCH 28/36] scripts: ensure -async commands are declared async, marcandre . lureau, 2015/09/25
- [Qemu-devel] [PATCH 30/36] tests: change /0.15/* tests to /qmp/*, marcandre . lureau, 2015/09/25
- [Qemu-devel] [PATCH 31/36] tests: add /qmp/dispatch_cmd_async test, marcandre . lureau, 2015/09/25
- [Qemu-devel] [PATCH 29/36] qapi: take 'id' from request, marcandre . lureau, 2015/09/25
- [Qemu-devel] [PATCH 34/36] console: graphic_hw_update return true if async, marcandre . lureau, 2015/09/25
- [Qemu-devel] [PATCH 35/36] console: add graphic_hw_update_done(), marcandre . lureau, 2015/09/25