Patchwork [15/22] qapi: add new QMP server that uses CharDriverState

login
register
mail settings
Submitter Anthony Liguori
Date March 7, 2011, 1:22 a.m.
Message ID <1299460984-15849-16-git-send-email-aliguori@us.ibm.com>
Download mbox | patch
Permalink /patch/85616/
State New
Headers show

Comments

Anthony Liguori - March 7, 2011, 1:22 a.m.
This will replace the current QMP server once all the functions are implemented.

Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
Stefan Hajnoczi - March 7, 2011, 1:52 p.m.
On Mon, Mar 7, 2011 at 1:22 AM, Anthony Liguori <aliguori@us.ibm.com> wrote:
> +static void qmp_chr_send_greeting(QmpSession *s)
> +{
> +    VersionInfo *info;
> +    QObject *vers;
> +    QObject *greeting;
> +    QString *str;
> +
> +    info = qmp_query_version(NULL);
> +    vers = qmp_marshal_type_VersionInfo(info);
> +    qmp_free_version_info(info);
> +
> +    greeting = qobject_from_jsonf("{'QMP': {'version': %p, 'capabilities': []} }",
> +                                  vers);
> +    str = qobject_to_json(greeting);
> +    qobject_decref(greeting);
> +
> +    qemu_chr_write(s->chr, (void *)str->string, str->length);
> +    qemu_chr_write(s->chr, (void *)"\n", 1);
> +    QDECREF(str);
> +}

Is vers leaked?

Stefan
Anthony Liguori - March 7, 2011, 2:02 p.m.
On 03/07/2011 07:52 AM, Stefan Hajnoczi wrote:
> On Mon, Mar 7, 2011 at 1:22 AM, Anthony Liguori<aliguori@us.ibm.com>  wrote:
>    
>> +static void qmp_chr_send_greeting(QmpSession *s)
>> +{
>> +    VersionInfo *info;
>> +    QObject *vers;
>> +    QObject *greeting;
>> +    QString *str;
>> +
>> +    info = qmp_query_version(NULL);
>> +    vers = qmp_marshal_type_VersionInfo(info);
>> +    qmp_free_version_info(info);
>> +
>> +    greeting = qobject_from_jsonf("{'QMP': {'version': %p, 'capabilities': []} }",
>> +                                  vers);
>> +    str = qobject_to_json(greeting);
>> +    qobject_decref(greeting);
>> +
>> +    qemu_chr_write(s->chr, (void *)str->string, str->length);
>> +    qemu_chr_write(s->chr, (void *)"\n", 1);
>> +    QDECREF(str);
>> +}
>>      
> Is vers leaked?
>    

No, %p takes ownership of the object.  qdict_put* also does FWIW.

The ownership semantics of QObject functions are very challenging.  We 
really need a concept of floating references to let stuff like this 
continue to work without explicitly transferring ownership.

But really, a big part of this refactoring is isolating QObject so that 
we can eventually replace it with GVariant so it may not be worth 
worrying about.

Regards,

Anthony Liguori

> Stefan
>
>

Patch

diff --git a/qmp-core.c b/qmp-core.c
index 3a6242c..3a4d240 100644
--- a/qmp-core.c
+++ b/qmp-core.c
@@ -44,6 +44,15 @@  struct QmpState
     QTAILQ_HEAD(, DefaultQmpConnection) default_connections;
 };
 
+typedef struct QmpSession
+{
+    JSONMessageParser parser;
+    QmpState state;
+    CharDriverState *chr;
+    int max_global_handle;
+    QTAILQ_HEAD(, QmpConnection) connections;
+} QmpSession;
+
 static QTAILQ_HEAD(, QmpCommand) qmp_commands =
     QTAILQ_HEAD_INITIALIZER(qmp_commands);
 
@@ -67,6 +76,18 @@  void qmp_register_stateful_command(const char *name, QmpStatefulCommandFunc *fn)
     QTAILQ_INSERT_TAIL(&qmp_commands, cmd, node);
 }
 
+static QmpCommand *qmp_find_command(const char *name)
+{
+    QmpCommand *i;
+
+    QTAILQ_FOREACH(i, &qmp_commands, node) {
+        if (strcmp(i->name, name) == 0) {
+            return i;
+        }
+    }
+    return NULL;
+}
+
 char *qobject_as_string(QObject *obj)
 {
     char buffer[1024];
@@ -178,3 +199,197 @@  void qmp_signal_disconnect(QmpSignal *obj, int handle)
         }
     }
 }
+
+static QObject *qmp_dispatch_err(QmpState *state, QList *tokens, Error **errp)
+{
+    const char *command;
+    QDict *args, *dict;
+    QObject *request;
+    QmpCommand *cmd;
+    QObject *ret = NULL;
+    Error *err = NULL;
+
+    request = json_parser_parse_err(tokens, NULL, &err);
+    if (request == NULL) {
+        if (err == NULL) {
+            error_set(errp, QERR_JSON_PARSE_ERROR, "no valid JSON object");
+        } else {
+            error_propagate(errp, err);
+        }
+        return NULL;
+    }
+    if (qobject_type(request) != QTYPE_QDICT) {
+        error_set(errp, QERR_JSON_PARSE_ERROR, "request is not a dictionary");
+        return NULL;
+    }
+
+    dict = qobject_to_qdict(request);
+    if (!qdict_haskey(dict, "execute")) {
+        error_set(errp, QERR_JSON_PARSE_ERROR, "no execute key");
+        return NULL;
+    }
+
+    command = qdict_get_str(dict, "execute");
+    cmd = qmp_find_command(command);
+    if (cmd == NULL) {
+        error_set(errp, QERR_COMMAND_NOT_FOUND, command);
+        return NULL;
+    }
+
+    if (!qdict_haskey(dict, "arguments")) {
+        args = qdict_new();
+    } else {
+        args = qdict_get_qdict(dict, "arguments");
+        QINCREF(args);
+    }
+
+    if (cmd->stateful) {
+        cmd->sfn(state, args, &ret, errp);
+    } else {
+        cmd->fn(args, &ret, errp);
+    }
+
+    QDECREF(args);
+    qobject_decref(request);
+
+    return ret;
+}
+
+static QObject *qmp_dispatch(QmpState *state, QList *tokens)
+{
+    Error *err = NULL;
+    QObject *ret;
+    QDict *rsp;
+
+    ret = qmp_dispatch_err(state, tokens, &err);
+
+    rsp = qdict_new();
+    if (err) {
+        qdict_put_obj(rsp, "error", error_get_qobject(err));
+        error_free(err);
+    } else {
+        if (ret) {
+            qdict_put_obj(rsp, "return", ret);
+        } else {
+            qdict_put(rsp, "return", qdict_new());
+        }
+    }
+
+    return QOBJECT(rsp);
+}
+
+static void qmp_chr_parse(JSONMessageParser *parser, QList *tokens)
+{
+    QmpSession *s = container_of(parser, QmpSession, parser);
+    QObject *rsp;
+    QString *str;
+    
+    rsp = qmp_dispatch(&s->state, tokens);
+
+    str = qobject_to_json(rsp);
+    qemu_chr_write(s->chr, (void *)str->string, str->length);
+    qemu_chr_write(s->chr, (void *)"\n", 1);
+
+    QDECREF(str);
+    qobject_decref(rsp);
+}
+
+static int qmp_chr_can_receive(void *opaque)
+{
+    return 1024;
+}
+
+static void qmp_chr_receive(void *opaque, const uint8_t *buf, int size)
+{
+    QmpSession *s = opaque;
+    json_message_parser_feed(&s->parser, (char *)buf, size);
+}
+
+static void qmp_chr_send_greeting(QmpSession *s)
+{
+    VersionInfo *info;
+    QObject *vers;
+    QObject *greeting;
+    QString *str;
+
+    info = qmp_query_version(NULL);
+    vers = qmp_marshal_type_VersionInfo(info);
+    qmp_free_version_info(info);
+
+    greeting = qobject_from_jsonf("{'QMP': {'version': %p, 'capabilities': []} }",
+                                  vers);
+    str = qobject_to_json(greeting);
+    qobject_decref(greeting);
+
+    qemu_chr_write(s->chr, (void *)str->string, str->length);
+    qemu_chr_write(s->chr, (void *)"\n", 1);
+    QDECREF(str);
+}
+
+static void qmp_chr_event(void *opaque, int event)
+{
+    QmpSession *s = opaque;
+    switch (event) {
+    case CHR_EVENT_OPENED:
+        // FIXME disconnect any connected signals including defaults
+        json_message_parser_init(&s->parser, qmp_chr_parse);
+        qmp_chr_send_greeting(s);
+        break;
+    case CHR_EVENT_CLOSED:
+        json_message_parser_flush(&s->parser);
+        break;
+    }
+}
+
+static int qmp_chr_add_connection(QmpState *state,  QmpConnection *conn)
+{
+    QmpSession *s = container_of(state, QmpSession, state);
+
+    QTAILQ_INSERT_TAIL(&s->connections, conn, node);
+    return ++s->max_global_handle;
+}
+
+static void qmp_chr_send_event(QmpState *state, QObject *event)
+{
+    QmpSession *s = container_of(state, QmpSession, state);
+    QString *str;
+
+    str = qobject_to_json(event);
+    qemu_chr_write(s->chr, (void *)str->string, str->length);
+    qemu_chr_write(s->chr, (void *)"\n", 1);
+    QDECREF(str);
+}
+
+static void qmp_chr_del_connection(QmpState *state, int global_handle, Error **errp)
+{
+    QmpSession *s = container_of(state, QmpSession, state);
+    QmpConnection *conn;
+
+    QTAILQ_FOREACH(conn, &s->connections, node) {
+        if (conn->global_handle == global_handle) {
+            qmp_signal_disconnect(conn->signal, conn->handle);
+            QTAILQ_REMOVE(&s->connections, conn, node);
+            qemu_free(conn);
+            return;
+        }
+    }
+
+    error_set(errp, QERR_INVALID_PARAMETER_VALUE, "tag", "valid event handle");
+}
+
+void qmp_init_chardev(CharDriverState *chr)
+{
+    QmpSession *s = qemu_mallocz(sizeof(*s));
+
+    s->chr = chr;
+    s->state.add_connection = qmp_chr_add_connection;
+    s->state.event = qmp_chr_send_event;
+    s->state.del_connection = qmp_chr_del_connection;
+    QTAILQ_INIT(&s->state.default_connections);
+
+    s->max_global_handle = 0;
+    QTAILQ_INIT(&s->connections);
+
+    qemu_chr_add_handlers(chr, qmp_chr_can_receive, qmp_chr_receive,
+                          qmp_chr_event, s);
+}
diff --git a/qmp-core.h b/qmp-core.h
index 5ce02f7..808edf3 100644
--- a/qmp-core.h
+++ b/qmp-core.h
@@ -48,8 +48,6 @@  typedef struct QmpConnection
 
 void qmp_register_command(const char *name, QmpCommandFunc *fn);
 void qmp_register_stateful_command(const char *name, QmpStatefulCommandFunc *fn);
-void qmp_init_chardev(CharDriverState *chr);
-
 char *qobject_as_string(QObject *obj);
 
 QmpSignal *qmp_signal_init(void);
@@ -82,4 +80,6 @@  void qmp_state_event(QmpConnection *conn, QObject *data);
     }                                                        \
 } while(0)
 
+void qmp_init_chardev(CharDriverState *chr);
+
 #endif