From patchwork Fri Mar 11 23:05:43 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anthony Liguori X-Patchwork-Id: 86472 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 2266EB6FA1 for ; Sat, 12 Mar 2011 10:18:13 +1100 (EST) Received: from localhost ([127.0.0.1]:56640 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1PyBaU-0001mg-0R for incoming@patchwork.ozlabs.org; Fri, 11 Mar 2011 18:17:38 -0500 Received: from [140.186.70.92] (port=54724 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1PyBPR-0004HW-TY for qemu-devel@nongnu.org; Fri, 11 Mar 2011 18:06:17 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1PyBPN-0006yg-RB for qemu-devel@nongnu.org; Fri, 11 Mar 2011 18:06:13 -0500 Received: from e4.ny.us.ibm.com ([32.97.182.144]:48046) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1PyBPN-0006yb-MD for qemu-devel@nongnu.org; Fri, 11 Mar 2011 18:06:09 -0500 Received: from d01dlp01.pok.ibm.com (d01dlp01.pok.ibm.com [9.56.224.56]) by e4.ny.us.ibm.com (8.14.4/8.13.1) with ESMTP id p2BMkqCI015190 for ; Fri, 11 Mar 2011 17:46:52 -0500 Received: from d01relay07.pok.ibm.com (d01relay07.pok.ibm.com [9.56.227.147]) by d01dlp01.pok.ibm.com (Postfix) with ESMTP id AAC0638C8038 for ; Fri, 11 Mar 2011 18:06:06 -0500 (EST) Received: from d01av04.pok.ibm.com (d01av04.pok.ibm.com [9.56.224.64]) by d01relay07.pok.ibm.com (8.13.8/8.13.8/NCO v10.0) with ESMTP id p2BN68fS2506804 for ; Fri, 11 Mar 2011 18:06:08 -0500 Received: from d01av04.pok.ibm.com (loopback [127.0.0.1]) by d01av04.pok.ibm.com (8.14.4/8.13.1/NCO v10.0 AVout) with ESMTP id p2BN67lp028230 for ; Fri, 11 Mar 2011 18:06:07 -0500 Received: from localhost.localdomain (sig-9-65-217-31.mts.ibm.com [9.65.217.31]) by d01av04.pok.ibm.com (8.14.4/8.13.1/NCO v10.0 AVin) with ESMTP id p2BN5lH0026958; Fri, 11 Mar 2011 18:06:05 -0500 From: Anthony Liguori To: qemu-devel@nongnu.org Date: Fri, 11 Mar 2011 17:05:43 -0600 Message-Id: <1299884745-521-14-git-send-email-aliguori@us.ibm.com> X-Mailer: git-send-email 1.7.0.4 In-Reply-To: <1299884745-521-1-git-send-email-aliguori@us.ibm.com> References: <1299884745-521-1-git-send-email-aliguori@us.ibm.com> X-Content-Scanned: Fidelis XPS MAILER X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6, seldom 2.4 (older, 4) X-Received-From: 32.97.182.144 Cc: Adam Litke , Anthony Liguori , Luiz Capitulino , Avi Kivity , Markus Armbruster Subject: [Qemu-devel] [PATCH 13/15] qapi: add code generator for libqmp (v2) X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Signed-off-by: Anthony Liguori --- 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 + * + * 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 +#include +#include + +#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 + * + * 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 +#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 + * + * 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