[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH 15/22] qapi: add new QMP server that uses CharDriver
From: |
Anthony Liguori |
Subject: |
[Qemu-devel] [PATCH 15/22] qapi: add new QMP server that uses CharDriverState |
Date: |
Sun, 6 Mar 2011 19:22:57 -0600 |
This will replace the current QMP server once all the functions are implemented.
Signed-off-by: Anthony Liguori <address@hidden>
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
--
1.7.0.4
- Re: [Qemu-devel] [PATCH 10/22] qapi: add core QMP server support, (continued)
[Qemu-devel] [PATCH 12/22] qapi: add QAPI module type, Anthony Liguori, 2011/03/06
[Qemu-devel] [PATCH 06/22] qapi: add JSON parsing error message, Anthony Liguori, 2011/03/06
[Qemu-devel] [PATCH 13/22] qapi: add code generators for QMP command marshaling, Anthony Liguori, 2011/03/06
[Qemu-devel] [PATCH 15/22] qapi: add new QMP server that uses CharDriverState,
Anthony Liguori <=
[Qemu-devel] [PATCH 14/22] qapi: add query-version QMP command, Anthony Liguori, 2011/03/06
- Re: [Qemu-devel] [PATCH 14/22] qapi: add query-version QMP command, Stefan Hajnoczi, 2011/03/07
- Re: [Qemu-devel] [PATCH 14/22] qapi: add query-version QMP command, Anthony Liguori, 2011/03/07
- Re: [Qemu-devel] [PATCH 14/22] qapi: add query-version QMP command, Avi Kivity, 2011/03/09
- Re: [Qemu-devel] [PATCH 14/22] qapi: add query-version QMP command, Anthony Liguori, 2011/03/09
- Re: [Qemu-devel] [PATCH 14/22] qapi: add query-version QMP command, Avi Kivity, 2011/03/09
- Re: [Qemu-devel] [PATCH 14/22] qapi: add query-version QMP command, Anthony Liguori, 2011/03/09
Re: [Qemu-devel] [PATCH 14/22] qapi: add query-version QMP command, Avi Kivity, 2011/03/09