From patchwork Fri Sep 25 14:03:52 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Marc-Andr=C3=A9_Lureau?= X-Patchwork-Id: 522836 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id D859C14027C for ; Sat, 26 Sep 2015 00:20:17 +1000 (AEST) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b=D5q94Msp; dkim-atps=neutral Received: from localhost ([::1]:42103 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZfTrD-0007Ql-P4 for incoming@patchwork.ozlabs.org; Fri, 25 Sep 2015 10:20:15 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:41835) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZfTcD-0007E1-UU for qemu-devel@nongnu.org; Fri, 25 Sep 2015 10:04:47 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ZfTc8-0003ho-JR for qemu-devel@nongnu.org; Fri, 25 Sep 2015 10:04:45 -0400 Received: from mail-wi0-x235.google.com ([2a00:1450:400c:c05::235]:36224) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZfTc8-0003hY-AI for qemu-devel@nongnu.org; Fri, 25 Sep 2015 10:04:40 -0400 Received: by wicgb1 with SMTP id gb1so22059339wic.1 for ; Fri, 25 Sep 2015 07:04:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-type:content-transfer-encoding; bh=oAFQ++Wx8i2nksvyscNWVsXtxbCR3SQVZZrvY8WruJo=; b=D5q94MspGT7TPM3XSGz1w26yZeeVOOpx4VEqgVo7SkBTH5ceuteRIHTvPVzTGATcgb Ov+ofzN5R51O9sZiLX1OQzBEZlRy5nLMbt1F1PSlFgVCcZdXGksul8tjuMdCRH8RhW5k qYzA+RaSu5DaRzA1bpOHdbMm7ycW3604DVUZCH+o9MHvfsp8FdKd9mNXVWooYoTHtrux NuqjPgQflxUfT27RGxJFinki1U2Z1fdIs6Wya1GgOkFQuTAnY89G7FDfwnCwYKk9xovh n8jUVfH2N8haavyvqR4AH3jCAp16jLEs4mFxLvMCVxhNqdrkHRjGSRoNeXsgAkh20WYb jd1g== X-Received: by 10.181.8.72 with SMTP id di8mr3749741wid.62.1443189879721; Fri, 25 Sep 2015 07:04:39 -0700 (PDT) Received: from localhost ([149.6.167.210]) by smtp.gmail.com with ESMTPSA id lv4sm2785626wjb.43.2015.09.25.07.04.38 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 25 Sep 2015 07:04:39 -0700 (PDT) From: marcandre.lureau@redhat.com To: qemu-devel@nongnu.org Date: Fri, 25 Sep 2015 16:03:52 +0200 Message-Id: <1443189844-20341-25-git-send-email-marcandre.lureau@redhat.com> X-Mailer: git-send-email 2.4.3 In-Reply-To: <1443189844-20341-1-git-send-email-marcandre.lureau@redhat.com> References: <1443189844-20341-1-git-send-email-marcandre.lureau@redhat.com> MIME-Version: 1.0 X-detected-operating-system: by eggs.gnu.org: Error: Malformed IPv6 address (bad octet value). X-Received-From: 2a00:1450:400c:c05::235 Cc: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , armbru@redhat.com, mdroth@linux.vnet.ibm.com Subject: [Qemu-devel] [PATCH 24/36] qmp: add QmpClient X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org From: Marc-André Lureau 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 --- 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);