From: Marc-André Lureau <marcandre.lur...@redhat.com> 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 <marcandre.lur...@redhat.com> --- 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