[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH 13/15] qapi: add code generator for libqmp (v2)
From: |
Anthony Liguori |
Subject: |
[Qemu-devel] [PATCH 13/15] qapi: add code generator for libqmp (v2) |
Date: |
Fri, 11 Mar 2011 17:05:43 -0600 |
Signed-off-by: Anthony Liguori <address@hidden>
---
v1 -> v2
- update code generator to use multiline
- proxy command support
- async command support
diff --git a/Makefile b/Makefile
index 47a755d..5170675 100644
--- a/Makefile
+++ b/Makefile
@@ -4,7 +4,7 @@ GENERATED_HEADERS = config-host.h trace.h qemu-options.def
ifeq ($(TRACE_BACKEND),dtrace)
GENERATED_HEADERS += trace-dtrace.h
endif
-GENERATED_HEADERS += qmp-types.h qmp-marshal-types.h qmp.h
+GENERATED_HEADERS += qmp-types.h qmp-marshal-types.h qmp.h libqmp.h
ifneq ($(wildcard config-host.mak),)
# Put the all: rule here so that config-host.mak can contain dependencies.
@@ -165,9 +165,16 @@ qmp.h: $(SRC_PATH)/qmp-schema.json $(SRC_PATH)/qmp-gen.py
qmp-marshal.c: $(SRC_PATH)/qmp-schema.json $(SRC_PATH)/qmp-gen.py
$(call quiet-command,python $(SRC_PATH)/qmp-gen.py --body < $< > $@, "
GEN $@")
+libqmp.h: $(SRC_PATH)/qmp-schema.json $(SRC_PATH)/qmp-gen.py
+ $(call quiet-command,python $(SRC_PATH)/qmp-gen.py --lib-header < $< >
$@, " GEN $@")
+
+libqmp.c: $(SRC_PATH)/qmp-schema.json $(SRC_PATH)/qmp-gen.py
+ $(call quiet-command,python $(SRC_PATH)/qmp-gen.py --lib-body < $< >
$@, " GEN $@")
+
qmp-types.o: qmp-types.c qmp-types.h
qmp-marshal-types.o: qmp-marshal-types.c qmp-marshal-types.h qmp-types.h
qmp-marshal.o: qmp-marshal.c qmp.h qmp-types.h qmp-marshal-types.h
+libqmp.o: libqmp.c libqmp.h qmp-types.h
version.o: $(SRC_PATH)/version.rc config-host.mak
$(call quiet-command,$(WINDRES) -I. -o $@ $<," RC $(TARGET_DIR)$@")
diff --git a/libqmp-core.c b/libqmp-core.c
new file mode 100644
index 0000000..4613d4f
--- /dev/null
+++ b/libqmp-core.c
@@ -0,0 +1,361 @@
+/*
+ * QAPI
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Anthony Liguori <address@hidden>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2. See
+ * the COPYING.LIB file in the top-level directory.
+ */
+#include "libqmp.h"
+#include "libqmp-internal.h"
+#include "libqmp-core.h"
+#include "json-streamer.h"
+#include "json-parser.h"
+#include "dirent.h"
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <assert.h>
+
+#ifndef container_of
+#define offset_of(type, field) \
+ ((unsigned long)(&((type *)0)->field))
+#define container_of(obj, type, field) \
+ ((type *)(((char *)obj) - offsetof(type, field)))
+#endif
+
+//#define DEBUG_LIBQMP 1
+
+typedef struct FdQmpSession
+{
+ QmpSession session;
+ JSONMessageParser parser;
+ QObject *result;
+ bool got_greeting;
+ int fd;
+ int event_count;
+} FdQmpSession;
+
+static EventTrampolineFunc *get_event_trampoline(QmpSession *sess, const char
*name)
+{
+ QmpEventTrampoline *t;
+
+ QTAILQ_FOREACH(t, &sess->events, node) {
+ if (strcmp(t->name, name) == 0) {
+ return t->dispatch;
+ }
+ }
+
+ return NULL;
+}
+
+static void fd_qmp_session_process_event(FdQmpSession *fs, QDict *response)
+{
+ EventTrampolineFunc *tramp;
+ QmpSignal *signal;
+ const char *event;
+ int tag;
+
+ event = qdict_get_str(response, "event");
+ tramp = get_event_trampoline(&fs->session, event);
+
+ fs->event_count++;
+
+ if (tramp && qdict_haskey(response, "tag")) {
+ tag = qdict_get_int(response, "tag");
+
+ QTAILQ_FOREACH(signal, &fs->session.signals, node) {
+ if (signal->global_handle == tag) {
+ QmpConnection *conn;
+ QDict *args = NULL;
+ Error *err = NULL;
+
+ if (qdict_haskey(response, "data")) {
+ args = qobject_to_qdict(qdict_get(response, "data"));
+ }
+
+ QTAILQ_FOREACH(conn, &signal->connections, node) {
+ tramp(args, conn->fn, conn->opaque, &err);
+ if (err) {
+ error_free(err);
+ }
+ }
+
+ break;
+ }
+ }
+ }
+}
+
+static void fd_qmp_session_parse(JSONMessageParser *parser, QList *tokens)
+{
+ FdQmpSession *fs = container_of(parser, FdQmpSession, parser);
+ QObject *result;
+
+ result = json_parser_parse(tokens, NULL);
+ if (!fs->got_greeting) {
+ fs->got_greeting = true;
+ qobject_decref(result);
+ } else {
+ QDict *response = qobject_to_qdict(result);
+ if (qdict_haskey(response, "event")) {
+ fd_qmp_session_process_event(fs, response);
+ qobject_decref(result);
+ } else {
+ qobject_decref(fs->result);
+ fs->result = result;
+ }
+ }
+}
+
+static QDict *fd_qmp_session_read(FdQmpSession *fs)
+{
+ QDict *response;
+
+ assert(fs->result == NULL);
+ fs->result = NULL;
+ while (!fs->result) {
+ char buffer[1024];
+ ssize_t len;
+
+ len = read(fs->fd, buffer, sizeof(buffer));
+ if (len == -1 && errno == EINTR) {
+ continue;
+ }
+ if (len < 1) {
+ abort();
+ }
+
+#if defined(DEBUG_LIBQMP)
+ fwrite(buffer, len, 1, stdout);
+ fflush(stdout);
+#endif
+ json_message_parser_feed(&fs->parser, buffer, len);
+ }
+
+ response = qobject_to_qdict(fs->result);
+ fs->result = NULL;
+
+ return response;
+}
+
+static bool qmp_session_fd_wait_event(QmpSession *s, struct timeval *tv)
+{
+ FdQmpSession *fs = (FdQmpSession *)s;
+ fd_set readfds;
+ int ret;
+
+ FD_ZERO(&readfds);
+ FD_SET(fs->fd, &readfds);
+
+ do {
+ ret = select(fs->fd + 1, &readfds, NULL, NULL, tv);
+ } while (ret == -1 && errno == EINTR);
+
+ if (ret) {
+ char buffer[1024];
+ ssize_t len;
+ int saved_event_count;
+
+ do {
+ len = read(fs->fd, buffer, sizeof(buffer));
+ } while (len == -1 && errno == EINTR);
+
+ if (len < 1) {
+ abort();
+ }
+
+#if defined(DEBUG_LIBQMP)
+ fwrite(buffer, len, 1, stdout);
+ fflush(stdout);
+#endif
+ saved_event_count = fs->event_count;
+ json_message_parser_feed(&fs->parser, buffer, len);
+ return (saved_event_count != fs->event_count);
+ }
+
+ return false;
+}
+
+static QObject *qmp_session_fd_dispatch(QmpSession *s, const char *name,
+ QDict *args, Error **err)
+{
+ FdQmpSession *fs = (FdQmpSession *)s;
+ QString *str;
+ const char *buffer;
+ size_t size;
+ size_t offset;
+ QDict *request = qdict_new();
+ QDict *response;
+
+ qdict_put(request, "execute", qstring_from_str(name));
+
+ if (qdict_size(args)) {
+ QINCREF(args);
+ qdict_put(request, "arguments", args);
+ }
+
+ str = qobject_to_json(QOBJECT(request));
+ buffer = qstring_get_str(str);
+ size = str->length;
+
+ offset = 0;
+ while (offset < size) {
+ ssize_t len;
+
+ len = write(fs->fd, buffer + offset, size - offset);
+ if (len == -1 && errno == EINTR) {
+ continue;
+ }
+ if (len < 1) {
+ abort();
+ }
+
+#if defined(DEBUG_LIBQMP)
+ fwrite(buffer + offset, size - offset, 1, stdout);
+ fflush(stdout);
+#endif
+ offset += len;
+ }
+
+ QDECREF(str);
+ QDECREF(request);
+
+ response = fd_qmp_session_read(fs);
+
+ if (qdict_haskey(response, "error")) {
+ error_set_qobject(err, qdict_get(response, "error"));
+ QDECREF(response);
+ return NULL;
+ } else {
+ QObject *result = qdict_get(response, "return");
+ qobject_incref(result);
+ QDECREF(response);
+ return result;
+ }
+}
+
+void libqmp_register_event(QmpSession *sess, const char *name,
EventTrampolineFunc *func)
+{
+ QmpEventTrampoline *t = qemu_mallocz(sizeof(*t));
+ t->name = name;
+ t->dispatch = func;
+ QTAILQ_INSERT_TAIL(&sess->events, t, node);
+}
+
+QmpSession *qmp_session_new(int fd)
+{
+ FdQmpSession *s = qemu_mallocz(sizeof(*s));
+
+ s->fd = fd;
+ s->session.dispatch = qmp_session_fd_dispatch;
+ s->session.wait_event = qmp_session_fd_wait_event;
+ s->got_greeting = false;
+
+ QTAILQ_INIT(&s->session.events);
+ QTAILQ_INIT(&s->session.signals);
+ json_message_parser_init(&s->parser, fd_qmp_session_parse);
+
+ libqmp_init_events(&s->session);
+ libqmp_qmp_capabilities(&s->session, NULL);
+
+ return &s->session;
+}
+
+void qmp_session_destroy(QmpSession *s)
+{
+ FdQmpSession *fs = container_of(s, FdQmpSession, session);
+
+ while (!QTAILQ_EMPTY(&s->events)) {
+ QmpEventTrampoline *t = QTAILQ_FIRST(&s->events);
+ QTAILQ_REMOVE(&s->events, t, node);
+ qemu_free(t);
+ }
+ if (fs->result) {
+ qobject_decref(fs->result);
+ fs->result = NULL;
+ }
+ json_message_parser_destroy(&fs->parser);
+ close(fs->fd);
+ qemu_free(fs);
+}
+
+typedef struct GenericSignal
+{
+ QmpSignal *signal;
+} GenericSignal;
+
+void *libqmp_signal_new(QmpSession *s, size_t size, int global_handle)
+{
+ GenericSignal *obj;
+ obj = qemu_mallocz(size);
+ obj->signal = qemu_mallocz(sizeof(QmpSignal));
+ obj->signal->sess = s;
+ obj->signal->global_handle = global_handle;
+ // FIXME validate there isn't another global_handle
+ QTAILQ_INIT(&obj->signal->connections);
+ QTAILQ_INSERT_TAIL(&s->signals, obj->signal, node);
+ return obj;
+}
+
+int libqmp_signal_connect(QmpSignal *obj, void *func, void *opaque)
+{
+ QmpConnection *conn;
+
+ conn = qemu_mallocz(sizeof(*conn));
+ conn->fn = func;
+ conn->opaque = opaque;
+ conn->handle = ++obj->max_handle;
+ QTAILQ_INSERT_TAIL(&obj->connections, conn, node);
+ return conn->handle;
+}
+
+void libqmp_signal_disconnect(QmpSignal *obj, int handle)
+{
+ QmpConnection *conn;
+
+ QTAILQ_FOREACH(conn, &obj->connections, node) {
+ if (conn->handle == handle) {
+ break;
+ }
+ }
+ if (conn) {
+ QTAILQ_REMOVE(&obj->connections, conn, node);
+ qemu_free(conn);
+ }
+}
+
+void libqmp_signal_free(void *base, QmpSignal *obj)
+{
+ QTAILQ_REMOVE(&obj->sess->signals, obj, node);
+
+ libqmp_put_event(obj->sess, obj->global_handle, NULL);
+ while (!QTAILQ_EMPTY(&obj->connections)) {
+ QmpConnection *conn = QTAILQ_FIRST(&obj->connections);
+ QTAILQ_REMOVE(&obj->connections, conn, node);
+ qemu_free(conn);
+ }
+ qemu_free(obj);
+ qemu_free(base);
+}
+
+bool libqmp_wait_event(QmpSession *s, struct timeval *tv)
+{
+ return s->wait_event(s, tv);
+}
+
+bool libqmp_poll_event(QmpSession *s)
+{
+ struct timeval tv = { 0, 0 };
+ bool got_event = false;
+ bool ret;
+
+ do {
+ ret = libqmp_wait_event(s, &tv);
+ got_event |= ret;
+ } while (ret);
+
+ return got_event;
+}
diff --git a/libqmp-core.h b/libqmp-core.h
new file mode 100644
index 0000000..c1063e6
--- /dev/null
+++ b/libqmp-core.h
@@ -0,0 +1,44 @@
+/*
+ * QAPI
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Anthony Liguori <address@hidden>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2. See
+ * the COPYING.LIB file in the top-level directory.
+ */
+#ifndef LIBQMP_CORE_H
+#define LIBQMP_CORE_H
+
+#include <sys/types.h>
+#include "qmp-types.h"
+#include "error.h"
+
+typedef struct QmpSession QmpSession;
+
+QmpSession *qmp_session_new(int fd);
+void qmp_session_destroy(QmpSession *s);
+
+bool libqmp_poll_event(QmpSession *s);
+bool libqmp_wait_event(QmpSession *s, struct timeval *tv);
+
+void *libqmp_signal_new(QmpSession *s, size_t size, int global_handle);
+int libqmp_signal_connect(QmpSignal *obj, void *func, void *opaque);
+void libqmp_signal_disconnect(QmpSignal *obj, int handle);
+void libqmp_signal_free(void *base, QmpSignal *obj);
+
+#define libqmp_signal_init(s, type, global_handle) \
+ ((type *)libqmp_signal_new(s, sizeof(type), global_handle))
+
+#define signal_connect(obj, fn, opaque) \
+ libqmp_signal_connect((obj)->signal, (obj)->func = fn, opaque)
+
+#define signal_disconnect(obj, handle) \
+ libqmp_signal_disconnect((obj)->signal, handle)
+
+#define signal_unref(obj) \
+ libqmp_signal_free((obj), (obj)->signal)
+
+#endif
diff --git a/libqmp-internal.h b/libqmp-internal.h
new file mode 100644
index 0000000..01a3dd8
--- /dev/null
+++ b/libqmp-internal.h
@@ -0,0 +1,56 @@
+/*
+ * QAPI
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Anthony Liguori <address@hidden>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2. See
+ * the COPYING.LIB file in the top-level directory.
+ */
+#ifndef LIBQMP_INTERNAL_H
+#define LIBQMP_INTERNAL_H
+
+#include "qemu-objects.h"
+#include "qmp-marshal-types.h"
+#include "error_int.h"
+
+typedef void (EventTrampolineFunc)(QDict *qmp__args, void *qmp__fn, void
*qmp__opaque, Error **qmp__errp);
+
+typedef struct QmpEventTrampoline
+{
+ const char *name;
+ EventTrampolineFunc *dispatch;
+ QTAILQ_ENTRY(QmpEventTrampoline) node;
+} QmpEventTrampoline;
+
+typedef struct QmpConnection
+{
+ void *fn;
+ void *opaque;
+ int handle;
+ QTAILQ_ENTRY(QmpConnection) node;
+} QmpConnection;
+
+struct QmpSignal
+{
+ QmpSession *sess;
+ int global_handle;
+ int max_handle;
+ QTAILQ_HEAD(, QmpConnection) connections;
+ QTAILQ_ENTRY(QmpSignal) node;
+};
+
+struct QmpSession
+{
+ QObject *(*dispatch)(QmpSession *session, const char *name, QDict *args,
Error **err);
+ bool (*wait_event)(QmpSession *session, struct timeval *tv);
+ QTAILQ_HEAD(, QmpEventTrampoline) events;
+ QTAILQ_HEAD(, QmpSignal) signals;
+};
+
+void libqmp_init_events(QmpSession *sess);
+void libqmp_register_event(QmpSession *sess, const char *name,
EventTrampolineFunc *func);
+
+#endif
diff --git a/qmp-gen.py b/qmp-gen.py
index f1bdb2f..35f64cf 100644
--- a/qmp-gen.py
+++ b/qmp-gen.py
@@ -925,6 +925,321 @@ qmp__out:
return ret
+
+def gen_lib_decl(name, options, retval, suffix='', proxy=False, async=False):
+ if proxy:
+ async=True
+
+ if proxy:
+ args = []
+ else:
+ args = ['QmpSession *qmp__session']
+ for argname, argtype, optional in parse_args(options):
+ if argtype == '**':
+ args.append('KeyValues * %s' % c_var(argname))
+ else:
+ if optional:
+ args.append('bool has_%s' % c_var(argname))
+ args.append('%s %s' % (qmp_type_to_c(argtype), c_var(argname)))
+
+ args.append('Error **qmp__err')
+
+ if proxy:
+ prefix = 'qmp'
+ else:
+ prefix = 'libqmp'
+
+ if proxy:
+ qmp_retval = 'void'
+ args.append('%sCompletionFunc *qmp__cc' % camel_case(name))
+ args.append('void *qmp__opaque')
+ else:
+ qmp_retval = qmp_type_to_c(retval, True)
+
+ return mcgen('''
+%(ret)s %(prefix)s_%(name)s(%(args)s)%(suffix)s
+''', ret=qmp_retval, prefix=prefix, name=c_var(name), args=', '.join(args),
suffix=suffix)
+
+def gen_lib_declaration(name, options, retval):
+ return gen_lib_decl(name, options, retval, ';')
+
+def gen_lib_event_definition(name, typeinfo):
+ args = ''
+ for argname, argtype, optional in parse_args(typeinfo):
+ if optional:
+ args += cgen(' bool has_%(name)s;', name=c_var(argname))
+ args += cgen(' %(type)s %(name)s = 0;', type=qmp_type_to_c(argtype,
True), name=c_var(argname))
+
+ ret = mcgen('''
+
+static void libqmp_notify_%(fn_name)s(QDict *qmp__args, void *qmp__fn, void
*qmp__opaque, Error **qmp__errp)
+{
+ %(fn_ret)s *qmp__native_fn = qmp__fn;
+ Error *qmp__err = NULL;
+%(args)s
+
+ (void)qmp__err;
+''',
+ fn_name=de_camel_case(qmp_event_to_c(name)),
+ fn_ret=qmp_event_func_to_c(name), args=args)
+
+ for argname, argtype, optional in parse_args(typeinfo):
+ if optional:
+ ret += cgen(' BUILD_BUG()')
+ ret += mcgen('''
+
+ if (!qdict_haskey(qmp__args, "%(name)s")) {
+ error_set(qmp__errp, QERR_MISSING_PARAMETER, "%(name)s");
+ goto qmp__out;
+ }
+
+ %(c_name)s = %(unmarshal)s(qdict_get(qmp__args, "%(name)s"), &qmp__err);
+ if (qmp__err) {
+ if (error_is_type(qmp__err, QERR_INVALID_PARAMETER_TYPE)) {
+ error_set(qmp__errp, QERR_INVALID_PARAMETER_TYPE, "%(name)s",
+ error_get_field(qmp__err, "expected"));
+ error_free(qmp__err);
+ qmp__err = NULL;
+ } else {
+ error_propagate(qmp__errp, qmp__err);
+ }
+ goto qmp__out;
+ }
+''', name=argname, c_name=c_var(argname),
unmarshal=qmp_type_from_qobj(argtype))
+
+ arglist = ['qmp__opaque']
+ for argname, argtype, optional in parse_args(typeinfo):
+ arglist.append(c_var(argname))
+ ret += mcgen('''
+
+ qmp__native_fn(%(args)s);
+''', args=', '.join(arglist))
+
+ has_label = False
+ for argname, argtype, optional in parse_args(typeinfo):
+ if not has_label:
+ ret += mcgen('''
+qmp__out:
+''')
+ has_label = True
+
+ if qmp_type_should_free(argtype):
+ ret += cgen(' %(free)s(%(name)s);',
free=qmp_free_func(argtype), name=c_var(argname))
+ ret += mcgen('''
+ return;
+}
+''')
+ return ret
+
+def gen_async_lib_definition(name, options, retval):
+ ret = mcgen('''
+
+typedef struct %(cc_name)sCompletionCB
+{
+ %(cc_name)sCompletionFunc *cb;
+ void *opaque;
+} %(cc_name)sCompletionCB;
+
+static void qmp_%(c_name)s_cb(void *qmp__opaque, QObject *qmp__retval, Error
*qmp__err)
+{
+ %(cc_name)sCompletionCB *qmp__cb = qmp__opaque;
+''',
+ cc_name=camel_case(name), c_name=c_var(name))
+
+ if retval != 'none':
+ ret += cgen(' %(ret_type)s qmp__native_retval = 0;',
+ ret_type=qmp_type_to_c(retval, True))
+
+ if type(retval) == list:
+ ret += mcgen('''
+
+ if (!qmp__err) {
+ QList *qmp__list_retval = qobject_to_qlist(qmp__retval);
+ QListEntry *qmp__i;
+ QLIST_FOREACH_ENTRY(qmp__list_retval, qmp__i) {
+ %(ret_type)s qmp__native_i = %(unmarshal)s(qmp__i->value,
&qmp__err);
+ if (qmp__err) {
+ %(free)s(qmp__native_retval);
+ break;
+ }
+ qmp__native_i->next = qmp__native_retval;
+ qmp__native_retval = qmp__native_i;
+ }
+ }
+''',
+ ret_type=qmp_type_to_c(retval[0], True),
+ unmarshal=qmp_type_from_qobj(retval[0]),
+ free=qmp_free_func(retval[0]))
+ elif is_dict(retval):
+ ret += mcgen('''
+ // FIXME (using an anonymous dict as return value)')
+ BUILD_BUG();
+''')
+ elif retval != 'none':
+ ret += mcgen('''
+
+ if (!qmp__err) {
+ qmp__native_retval = %(unmarshal)s(qmp__retval, &qmp__err);
+ }
+''',
+ unmarshal=qmp_type_from_qobj(retval))
+ ret += cgen('')
+ if retval == 'none':
+ ret += cgen(' qmp__cb->cb(qmp__cb->opaque, qmp__err);')
+ else:
+ ret += cgen(' qmp__cb->cb(qmp__cb->opaque, qmp__native_retval,
qmp__err);')
+ ret += cgen('}')
+
+ return ret
+
+def gen_lib_definition(name, options, retval, proxy=False, async=False):
+ if proxy:
+ async = True
+
+ ret = ''
+ if proxy:
+ ret += gen_async_lib_definition(name, options, retval)
+
+ fn_decl = gen_lib_decl(name, options, retval, proxy=proxy, async=async)
+ ret += mcgen('''
+
+%(fn_decl)s
+{
+ QDict *qmp__args = qdict_new();
+''',
+ fn_decl=fn_decl)
+ if async:
+ ret += mcgen('''
+ %(cc_name)sCompletionCB *qmp__cb = qemu_mallocz(sizeof(*qmp__cb));
+
+ qmp__cb->cb = qmp__cc;
+ qmp__cb->opaque = qmp__opaque;
+''',
+ cc_name=camel_case(name))
+ else:
+ ret += mcgen('''
+ Error *qmp__local_err = NULL;
+ QObject *qmp__retval = NULL;
+''')
+ if retval != 'none':
+ ret += cgen(' %(ret_type)s qmp__native_retval = 0;',
+ ret_type=qmp_type_to_c(retval, True))
+ if qmp_type_is_event(retval):
+ ret += cgen(' int qmp__global_handle = 0;')
+ ret += cgen('')
+
+ for argname, argtype, optional in parse_args(options):
+ if argtype == '**':
+ ret += mcgen('''
+ {
+ KeyValues *qmp__i;
+ for (qmp__i = %(name)s; qmp__i; qmp__i = qmp__i->next) {
+ qdict_put(qmp__args, qmp__i->key, qstring_from_str(qmp__i->value));
+ }
+ }
+''',
+ name=c_var(argname))
+ else:
+ if optional:
+ ret += mcgen('''
+ if (has_%(c_name)s) {
+''',
+ c_name=c_var(argname))
+ push_indent()
+ ret += mcgen('''
+ qdict_put_obj(qmp__args, "%(name)s", %(marshal)s(%(c_name)s));
+''',
+ name=argname, c_name=c_var(argname),
+ marshal=qmp_type_to_qobj(argtype))
+ if optional:
+ pop_indent()
+ ret += mcgen('''
+ }
+''')
+
+ if proxy:
+ ret += mcgen('''
+ qmp_guest_dispatch("%(name)s", qmp__args, qmp__err, qmp_%(c_name)s_cb,
qmp__cb);
+''',
+ name=name, c_name=c_var(name))
+ else:
+ ret += mcgen('''
+ qmp__retval = qmp__session->dispatch(qmp__session, "%(name)s", qmp__args,
&qmp__local_err);
+''',
+ name=name)
+ ret += mcgen('''
+
+ QDECREF(qmp__args);
+''')
+
+ if async:
+ pass
+ elif type(retval) == list:
+ ret += mcgen('''
+
+ if (!qmp__local_err) {
+ QList *qmp__list_retval = qobject_to_qlist(qmp__retval);
+ QListEntry *qmp__i;
+ QLIST_FOREACH_ENTRY(qmp__list_retval, qmp__i) {
+ %(type)s qmp__native_i = %(unmarshal)s(qmp__i->value,
&qmp__local_err);
+ if (qmp__local_err) {
+ %(free)s(qmp__native_retval);
+ break;
+ }
+ qmp__native_i->next = qmp__native_retval;
+ qmp__native_retval = qmp__native_i;
+ }
+ qobject_decref(qmp__retval);
+ }
+ error_propagate(qmp__err, qmp__local_err);
+ return qmp__native_retval;
+''',
+ type=qmp_type_to_c(retval[0], True),
+ unmarshal=qmp_type_from_qobj(retval[0]),
+ free=qmp_free_func(retval[0]))
+ elif is_dict(retval):
+ ret += mcgen('''
+ // FIXME (using an anonymous dict as return value)
+ BUILD_BUG();
+''')
+ elif qmp_type_is_event(retval):
+ if proxy:
+ ret += cgen(' BUILD_BUG();')
+ ret += mcgen('''
+ if (!qmp__local_err) {
+ qmp__global_handle = %(unmarshal)s(qmp__retval, &qmp__local_err);
+ qobject_decref(qmp__retval);
+ qmp__retval = NULL;
+ }
+ if (!qmp__local_err) {
+ qmp__native_retval = libqmp_signal_init(qmp__session, %(type)s,
qmp__global_handle);
+ }
+ error_propagate(qmp__err, qmp__local_err);
+ return qmp__native_retval;
+''',
+ unmarshal=qmp_type_from_qobj('int'),
+ type=qmp_event_to_c(retval))
+ elif retval != 'none':
+ ret += mcgen('''
+
+ if (!qmp__local_err) {
+ qmp__native_retval = %(unmarshal)s(qmp__retval, &qmp__local_err);
+ qobject_decref(qmp__retval);
+ }
+ error_propagate(qmp__err, qmp__local_err);
+ return qmp__native_retval;
+''',
+ unmarshal=qmp_type_from_qobj(retval))
+ else:
+ ret += mcgen('''
+ qobject_decref(qmp__retval);
+ error_propagate(qmp__err, qmp__local_err);
+''')
+
+ ret += cgen('}')
+
+ return ret
+
def tokenize(data):
while len(data):
if data[0] in ['{', '}', ':', ',', '[', ']']:
@@ -1028,6 +1343,18 @@ def generate(kind):
#include "qmp.h"
#include "qmp-core.h"
''')
+ elif kind == 'lib-header':
+ ret += mcgen('''
+#ifndef LIBQMP_H
+#define LIBQMP_H
+
+#include "libqmp-core.h"
+''')
+ elif kind == 'lib-body':
+ ret += mcgen('''
+#include "libqmp.h"
+#include "libqmp-internal.h"
+''')
exprs = []
expr = ''
@@ -1084,6 +1411,8 @@ def generate(kind):
event_types[name] = data
if kind == 'types-header':
ret += gen_type_declaration(name, data)
+ elif kind == 'lib-body':
+ ret += gen_lib_event_definition(name, data)
elif s.has_key('command'):
name = s['command']
options = {}
@@ -1094,10 +1423,17 @@ def generate(kind):
retval = s['returns']
if kind == 'body':
async = qmp_is_async_cmd(name)
+ proxy = qmp_is_proxy_cmd(name)
+ if proxy:
+ ret += gen_lib_definition(name, options, retval,
proxy=True)
ret += gen_definition(name, options, retval, async=async)
elif kind == 'header':
async = qmp_is_async_cmd(name)
ret += gen_declaration(name, options, retval, async=async)
+ elif kind == 'lib-body':
+ ret += gen_lib_definition(name, options, retval)
+ elif kind == 'lib-header':
+ ret += gen_lib_declaration(name, options, retval)
if kind.endswith('header'):
ret += cgen('#endif')
@@ -1135,6 +1471,21 @@ static void qmp_init_marshal(void)
qapi_init(qmp_init_marshal);
''')
+ elif kind == 'lib-body':
+ ret += mcgen('''
+
+void libqmp_init_events(QmpSession *sess)
+{
+''')
+ for event in event_types:
+ ret += mcgen('''
+ libqmp_register_event(sess, "%(name)s", &libqmp_notify_%(c_event_name)s);
+''',
+ name=event,
+ c_event_name=de_camel_case(qmp_event_to_c(event)))
+ ret += mcgen('''
+}
+''')
return ret
--
1.7.0.4
- Re: [Qemu-devel] [PATCH 07/15] qapi: add query-version QMP command, (continued)
- [Qemu-devel] [PATCH 11/15] qapi: add QMP qmp_capabilities command, Anthony Liguori, 2011/03/11
- [Qemu-devel] [PATCH 06/15] qapi: add code generators for QMP command marshaling, Anthony Liguori, 2011/03/11
- [Qemu-devel] [PATCH 02/15] qapi: add code generator for type marshallers, Anthony Liguori, 2011/03/11
- [Qemu-devel] [PATCH 14/15] qapi: add test-libqmp, Anthony Liguori, 2011/03/11
- [Qemu-devel] [PATCH 15/15] qapi: generate HTML report for test-libqmp, Anthony Liguori, 2011/03/11
- [Qemu-devel] [PATCH 13/15] qapi: add code generator for libqmp (v2),
Anthony Liguori <=
- [Qemu-devel] [PATCH 12/15] qapi: add QMP put-event command, Anthony Liguori, 2011/03/11
- [Qemu-devel] Re: [PATCH 00/15] QAPI Round 1 (core code generator) (v2), Luiz Capitulino, 2011/03/16