qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Anthony Liguori <aliguori@us.ibm.com>
To: Michael Roth <mdroth@linux.vnet.ibm.com>
Cc: aliguori@linux.vnet.ibm.com, agl@linux.vnet.ibm.com,
	qemu-devel@nongnu.org, Jes.Sorensen@redhat.com
Subject: [Qemu-devel] Re: [RFC][PATCH v1 07/12] qmp proxy: core code for proxying qmp requests to guest
Date: Fri, 25 Mar 2011 16:27:58 -0500	[thread overview]
Message-ID: <4D8D08DE.8050805@us.ibm.com> (raw)
In-Reply-To: <1301082479-4058-8-git-send-email-mdroth@linux.vnet.ibm.com>

On 03/25/2011 02:47 PM, Michael Roth wrote:
> This provides a QmpProxy class, 1 instance of which is shared by all QMP
> servers/sessions to send/receive QMP requests/responses between QEMU and
> the QEMU guest agent.
>
> A single qmp_proxy_send_request() is the only interface currently needed
> by a QMP session, QAPI/QMP's existing async support handles all the work
> of doing callbacks and routing responses to the proper session.
>
> Currently the class requires a path to a listening socket that either
> corresponds to the chardev that the guest agent is communicating
> through, or a local socket so we can communicate with a host-side
> "guest" agent for testing purposes.
>
> A subsequent patch will introduce a new chardev that sets up the
> socket chardev and initializes the QmpProxy instance to abstract this
> away from the user. Unifying this with local "guest" agent support may
> not be feasible, so another command-line option may be needed support
> host-side-only testing.
>
> Signed-off-by: Michael Roth<mdroth@linux.vnet.ibm.com>
> ---
>   qmp-core.c       |    8 ++
>   qmp-core.h       |    7 +-
>   qmp-proxy-core.h |   20 ++++
>   qmp-proxy.c      |  335 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>   vl.c             |    1 +
>   5 files changed, 365 insertions(+), 6 deletions(-)
>   create mode 100644 qmp-proxy-core.h
>   create mode 100644 qmp-proxy.c
>
> diff --git a/qmp-core.c b/qmp-core.c
> index 9f3d182..dab50a1 100644
> --- a/qmp-core.c
> +++ b/qmp-core.c
> @@ -937,7 +937,15 @@ void qmp_async_complete_command(QmpCommandState *cmd, QObject *retval, Error *er
>       qemu_free(cmd);
>   }
>
> +extern QmpProxy *qmp_proxy_default;
> +
>   void qmp_guest_dispatch(const char *name, const QDict *args, Error **errp,
>                           QmpGuestCompletionFunc *cb, void *opaque)
>   {
> +    if (!qmp_proxy_default) {
> +        /* TODO: should set errp here */
> +        fprintf(stderr, "qmp proxy: no guest proxy found\n");
> +        return;
> +    }
> +    qmp_proxy_send_request(qmp_proxy_default, name, args, errp, cb, opaque);
>   }
> diff --git a/qmp-core.h b/qmp-core.h
> index b676020..114d290 100644
> --- a/qmp-core.h
> +++ b/qmp-core.h
> @@ -4,6 +4,7 @@
>   #include "monitor.h"
>   #include "qmp-marshal-types.h"
>   #include "error_int.h"
> +#include "qmp-proxy-core.h"
>
>   struct QmpCommandState
>   {
> @@ -85,11 +86,5 @@ int qmp_state_get_fd(QmpState *sess);
>       }                                                        \
>   } while(0)
>
> -typedef void (QmpGuestCompletionFunc)(void *opaque, QObject *ret_data, Error *err);
> -
> -void qmp_guest_dispatch(const char *name, const QDict *args, Error **errp,
> -                        QmpGuestCompletionFunc *cb, void *opaque);
> -
> -
>   #endif
>
> diff --git a/qmp-proxy-core.h b/qmp-proxy-core.h
> new file mode 100644
> index 0000000..47ac85d
> --- /dev/null
> +++ b/qmp-proxy-core.h
> @@ -0,0 +1,20 @@
> +#ifndef QMP_PROXY_CORE_H
> +#define QMP_PROXY_CORE_H
> +
> +#define QMP_PROXY_PATH_DEFAULT "/tmp/qmp-proxy.sock"
> +
> +typedef void (QmpGuestCompletionFunc)(void *opaque, QObject *ret_data,
> +                                      Error *err);
> +
> +void qmp_guest_dispatch(const char *name, const QDict *args, Error **errp,
> +                        QmpGuestCompletionFunc *cb, void *opaque);
> +
> +typedef struct QmpProxy QmpProxy;
> +
> +void qmp_proxy_send_request(QmpProxy *p, const char *name,
> +                            const QDict *args, Error **errp,
> +                            QmpGuestCompletionFunc *cb, void *opaque);
> +QmpProxy *qmp_proxy_new(const char *channel_path);
> +void qmp_proxy_close(QmpProxy *p);
> +
> +#endif
> diff --git a/qmp-proxy.c b/qmp-proxy.c
> new file mode 100644
> index 0000000..eaa6e6e
> --- /dev/null
> +++ b/qmp-proxy.c
> @@ -0,0 +1,335 @@
> +/*
> + * QMP definitions for communicating with guest agent
> + *
> + * Copyright IBM Corp. 2011
> + *
> + * Authors:
> + *  Michael Roth<mdroth@linux.vnet.ibm.com>
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
> + * See the COPYING file in the top-level directory.
> + *
> + */
> +
> +#include "qmp.h"
> +#include "qmp-core.h"
> +#include "qemu-queue.h"
> +#include "json-parser.h"
> +#include "json-streamer.h"
> +#include "qemu_socket.h"
> +
> +#define QMP_SENTINEL 0xFF
> +
> +typedef struct QmpProxyRequest {
> +    const char *name;
> +    const QDict *args;
> +    QmpGuestCompletionFunc *cb;
> +    void *opaque;
> +    QString *json;
> +    QTAILQ_ENTRY(QmpProxyRequest) entry;
> +} QmpProxyRequest;
> +
> +typedef struct QmpProxyWriteState {
> +    QmpProxyRequest *current_request;
> +    const char *buf;
> +    size_t size;
> +    size_t pos;
> +    bool use_sentinel;
> +} QmpProxyWriteState;
> +
> +typedef struct QmpProxyReadState {
> +    char *buf;
> +    size_t size;
> +    size_t pos;
> +} QmpProxyReadState;

I suspect you should use GStrings for both the rx and tx buffers.  See 
the QmpUnixSession for an example.

> +struct QmpProxy {
> +    int fd;
> +    const char *path;
> +    QmpProxyWriteState write_state;
> +    QmpProxyReadState read_state;
> +    JSONMessageParser parser;
> +    QTAILQ_HEAD(, QmpProxyRequest) sent_requests;
> +    QTAILQ_HEAD(, QmpProxyRequest) queued_requests;
> +    QString *xport_event;
> +    QString *xport_event_sending;
> +};
> +
> +static void qmp_proxy_read_handler(void *opaque);
> +static void qmp_proxy_write_handler(void *opaque);
> +
> +static int qmp_proxy_cancel_request(QmpProxy *p, QmpProxyRequest *r)
> +{
> +    if (r->name) {
> +        if (r->cb) {
> +            r->cb(r->opaque, NULL, NULL);
> +        }
> +    }
> +
> +    return 0;
> +}
> +
> +static int qmp_proxy_cancel_all(QmpProxy *p)
> +{
> +    QmpProxyRequest *r, *tmp;
> +    QTAILQ_FOREACH_SAFE(r,&p->queued_requests, entry, tmp) {
> +        qmp_proxy_cancel_request(p, r);
> +        QTAILQ_REMOVE(&p->queued_requests, r, entry);
> +    }
> +    QTAILQ_FOREACH_SAFE(r,&p->sent_requests, entry, tmp) {
> +        qmp_proxy_cancel_request(p, r);
> +        QTAILQ_REMOVE(&p->queued_requests, r, entry);
> +    }
> +
> +    return 0;
> +}
> +
> +static void qmp_proxy_send_host_ack(QmpProxy *p, int session_id)
> +{
> +    QDict *evt = qdict_new();
> +
> +    /* only the last ack matters, nuke any outstanding ones. need to rethink
> +     * this approach if a host->guest reset event is added
> +     */
> +    if (p->xport_event) {
> +        QDECREF(p->xport_event);
> +    }
> +
> +    qdict_put_obj(evt, "_xport_event", QOBJECT(qstring_from_str("host_ack")));
> +    qdict_put_obj(evt, "_xport_arg_sid", QOBJECT(qint_from_int(session_id)));

I don't quite follow what this is doing.

> +    p->xport_event = qobject_to_json(QOBJECT(evt));
> +
> +    qemu_set_fd_handler(p->fd, qmp_proxy_read_handler,
> +                        qmp_proxy_write_handler, p);
> +}
> +
> +static void qmp_proxy_process_event(JSONMessageParser *parser, QList *tokens)
> +{
> +    QmpProxy *p = container_of(parser, QmpProxy, parser);
> +    QmpProxyRequest *r;
> +    QObject *obj;
> +    QDict *qdict;
> +    Error *err = NULL;
> +    const char *cmd;
> +    int session_id;
> +
> +    fprintf(stderr, "qmp proxy: called\n");
> +    obj = json_parser_parse_err(tokens, NULL,&err);
> +    if (!obj) {
> +        fprintf(stderr, "qmp proxy: failed to parse\n");
> +        return;
> +    } else {
> +        fprintf(stderr, "qmp proxy: parse successful\n");
> +        qdict = qobject_to_qdict(obj);
> +    }
> +
> +    /* check for transport-only commands/events */
> +    if (qdict_haskey(qdict, "_xport_event")) {
> +        cmd = qdict_get_try_str(qdict, "_xport_event");
> +        if (cmd&&  strcmp(cmd, "guest_init") == 0) {
> +            /* reset outstanding requests, then send an ack with the
> +             * session id they passed us
> +             */
> +            session_id = qdict_get_try_int(qdict, "_xport_arg_sid", 0);
> +            if (!session_id) {
> +                fprintf(stderr, "received invalid guest_init event\n");
> +            }
> +            qmp_proxy_cancel_all(p);
> +            qmp_proxy_send_host_ack(p, session_id);
> +
> +            return;
> +        }
> +    } else if (qdict_haskey(qdict, "return")) {
> +        fprintf(stderr, "received return\n");
> +        r = QTAILQ_FIRST(&p->sent_requests);
> +        if (!r) {
> +            fprintf(stderr, "received return, but no request queued\n");
> +            return;
> +        }
> +        /* XXX: can't assume type here */
> +        fprintf(stderr, "recieved response for cmd: %s\nreturn: %s\n",
> +                r->name, qstring_get_str(qobject_to_json(QOBJECT(qdict))));
> +        /* TODO: we don't know what kind of qtype the return value is, what
> +         * really need is for the qmp async callbacks to handle demarshalling
> +         * for us, for now we just pass the whole response up the stack, which
> +         * means everything except commands with no return value, like
> +         * guest-ping, will result in errors reported to the client
> +         */
> +        r->cb(r->opaque, QOBJECT(qdict), NULL);
> +        QTAILQ_REMOVE(&p->sent_requests, r, entry);
> +        fprintf(stderr, "done handling response\n");
> +    } else {
> +        fprintf(stderr, "received invalid payload format\n");
> +    }
> +}
> +
> +static void qmp_proxy_read_handler(void *opaque)
> +{
> +    QmpProxy *p = opaque;
> +    char buf[4096];
> +    int ret;
> +
> +    do {
> +        ret = read(p->fd, buf, 4096);
> +        if (ret == -1) {
> +            if (errno != EAGAIN&&  errno != EINTR) {
> +                fprintf(stderr, "qmp proxy: error reading request: %s",
> +                        strerror(errno));
> +            }
> +            return;
> +        } else if (ret == 0) {
> +            /* TODO: is this recoverable? should only happen for hot-unplug
> +             * in the chardev case, but for testing via a local guest agent
> +             * we may want to do some special handling...
> +             */
> +            fprintf(stderr, "qmp proxy: connection closed unexpectedly");
> +            qmp_proxy_cancel_all(p);
> +            qemu_set_fd_handler(p->fd, NULL, NULL, p);
> +            return;
> +        }
> +        buf[ret] = 0;

You have a buffer overflow here.

> +        json_message_parser_feed(&p->parser, (char *)buf, ret);

cast is unnecessary.

> +    } while (ret>  0);
> +}
> +
> +static void qmp_proxy_write_handler(void *opaque)
> +{
> +    QmpProxy *p = opaque;
> +    QmpProxyWriteState s = p->write_state;
> +    QmpProxyRequest *r;
> +    char sentinel = QMP_SENTINEL;
> +    int ret;
> +
> +send_another:

Better to avoid being clever here and have a separate send_once function 
and then a loop() that calls send_once.

Regards,

Anthony Liguori

  reply	other threads:[~2011-03-25 21:28 UTC|newest]

Thread overview: 38+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-03-25 19:47 [Qemu-devel] [RFC][PATCH v1 00/11] QEMU Guest Agent: QMP-based host/guest communication (virtagent) Michael Roth
2011-03-25 19:47 ` [Qemu-devel] [RFC][PATCH v1 01/12] json-lexer: make lexer error-recovery more deterministic Michael Roth
2011-03-25 21:18   ` [Qemu-devel] " Anthony Liguori
2011-03-25 19:47 ` [Qemu-devel] [RFC][PATCH v1 02/12] json-streamer: add handling for JSON_ERROR token/state Michael Roth
2011-03-25 19:47 ` [Qemu-devel] [RFC][PATCH v1 03/12] json-parser: add handling for NULL token list Michael Roth
2011-03-25 19:47 ` [Qemu-devel] [RFC][PATCH v1 04/12] qapi: fix function name typo in qmp-gen.py Michael Roth
2011-03-25 19:47 ` [Qemu-devel] [RFC][PATCH v1 05/12] qapi: fix handling for null-return async callbacks Michael Roth
2011-03-25 21:22   ` [Qemu-devel] " Anthony Liguori
2011-03-28 16:47     ` Luiz Capitulino
2011-03-28 17:01       ` Anthony Liguori
2011-03-28 17:06         ` Luiz Capitulino
2011-03-28 17:19           ` Anthony Liguori
2011-03-28 17:27             ` Luiz Capitulino
2011-03-28 17:39               ` Anthony Liguori
2011-03-28 17:59       ` Michael Roth
2011-03-28 18:27         ` Anthony Liguori
2011-03-28 20:42           ` Michael Roth
2011-03-28 20:45             ` Anthony Liguori
2011-03-25 19:47 ` [Qemu-devel] [RFC][PATCH v1 06/12] qmp proxy: build qemu with guest proxy dependency Michael Roth
2011-03-25 19:47 ` [Qemu-devel] [RFC][PATCH v1 07/12] qmp proxy: core code for proxying qmp requests to guest Michael Roth
2011-03-25 21:27   ` Anthony Liguori [this message]
2011-03-25 21:56     ` [Qemu-devel] " Michael Roth
2011-03-28 19:05       ` Anthony Liguori
2011-03-28 19:57         ` Michael Roth
2011-03-25 19:47 ` [Qemu-devel] [RFC][PATCH v1 08/12] qemu-char: add qmp_proxy chardev Michael Roth
2011-03-25 21:29   ` [Qemu-devel] " Anthony Liguori
2011-03-25 22:11     ` Michael Roth
2011-03-28 17:45       ` Anthony Liguori
2011-03-29 18:54         ` Michael Roth
2011-03-25 19:47 ` [Qemu-devel] [RFC][PATCH v1 09/12] guest agent: core marshal/dispatch interfaces Michael Roth
2011-03-25 19:47 ` [Qemu-devel] [RFC][PATCH v1 10/12] guest agent: qemu-ga daemon Michael Roth
2011-04-01  9:45   ` [Qemu-devel] " Jes Sorensen
2011-03-25 19:47 ` [Qemu-devel] [RFC][PATCH v1 11/12] guest agent: guest-side command implementations Michael Roth
2011-03-25 19:47 ` [Qemu-devel] [RFC][PATCH v1 12/12] guest agent: build qemu-ga, add QEMU-wide gio dep Michael Roth
2011-03-25 20:42 ` [Qemu-devel] Re: [RFC][PATCH v1 00/11] QEMU Guest Agent: QMP-based host/guest communication (virtagent) Michael Roth
2011-03-25 22:03   ` Anthony Liguori
2011-03-25 22:36     ` Michael Roth
2011-03-28 17:03       ` Anthony Liguori

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4D8D08DE.8050805@us.ibm.com \
    --to=aliguori@us.ibm.com \
    --cc=Jes.Sorensen@redhat.com \
    --cc=agl@linux.vnet.ibm.com \
    --cc=aliguori@linux.vnet.ibm.com \
    --cc=mdroth@linux.vnet.ibm.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).