diff mbox

[20/22] qapi: add code generator for libqmp

Message ID 1299460984-15849-21-git-send-email-aliguori@us.ibm.com
State New
Headers show

Commit Message

Anthony Liguori March 7, 2011, 1:23 a.m. UTC
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
diff mbox

Patch

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   <aliguori@us.ibm.com>
+ *
+ * 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   <aliguori@us.ibm.com>
+ *
+ * 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   <aliguori@us.ibm.com>
+ *
+ * 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 aac0f90..401b85a 100644
--- a/qmp-gen.py
+++ b/qmp-gen.py
@@ -650,6 +650,171 @@  static void qmp_marshal_%s(const QDict *qdict, QObject **ret_data, Error **err)
 
     print '}'
 
+
+def print_lib_decl(name, required, optional, retval, suffix=''):
+    args = ['QmpSession *qmp__session']
+    for key in required:
+        args.append('%s %s' % (qmp_type_to_c(required[key]), c_var(key)))
+
+    for key in optional:
+        if optional[key] == '**':
+            args.append('KeyValues * %s' % c_var(key))
+        else:
+            args.append('bool has_%s' % c_var(key))
+            args.append('%s %s' % (qmp_type_to_c(optional[key]), c_var(key)))
+
+    args.append('Error **qmp__err')
+
+    print '%s libqmp_%s(%s)%s' % (qmp_type_to_c(retval, True), c_var(name), ', '.join(args), suffix)
+
+def print_lib_event_decl(name, end=''):
+    print 'static void libqmp_notify_%s(QDict *qmp__args, void *qmp__fn, void *qmp__opaque, Error **qmp__errp)%s' % (de_camel_case(qmp_event_to_c(name)), end)
+
+def print_lib_declaration(name, required, optional, retval):
+    print_lib_decl(name, required, optional, retval, ';')
+
+def print_lib_event_definition(name, typeinfo):
+    print
+    print_lib_event_decl(name)
+    print '{'
+    print '    %s *qmp__native_fn = qmp__fn;' % (qmp_event_func_to_c(name))
+    print '    Error *qmp__err = NULL;'
+    for argname, argtype, optional in parse_args(typeinfo):
+        if optional:
+            print '    bool has_%s;' % (c_var(argname))
+        print '    %s %s = 0;' % (qmp_type_to_c(argtype, True), c_var(argname))
+
+    print
+    print '    (void)qmp__err;'
+
+    for argname, argtype, optional in parse_args(typeinfo):
+        if optional:
+            print '    BUILD_BUG()'
+        print '''
+    if (!qdict_haskey(qmp__args, "%s")) {
+        error_set(qmp__errp, QERR_MISSING_PARAMETER, "%s");
+        goto qmp__out;
+    }
+
+    %s = %s(qdict_get(qmp__args, "%s"), &qmp__err);
+    if (qmp__err) {
+        if (error_is_type(qmp__err, QERR_INVALID_PARAMETER_TYPE)) {
+            error_set(qmp__errp, QERR_INVALID_PARAMETER_TYPE, "%s",
+                      error_get_field(qmp__err, "expected"));
+            error_free(qmp__err);
+            qmp__err = NULL;
+        } else {
+            error_propagate(qmp__errp, qmp__err);
+        }
+        goto qmp__out;
+    }''' % (argname, argname, c_var(argname), qmp_type_from_qobj(argtype), argname, argname)
+
+    arglist = ['qmp__opaque']
+    for argname, argtype, optional in parse_args(typeinfo):
+        arglist.append(c_var(argname))
+    print
+    print '    qmp__native_fn(%s);' % (', '.join(arglist))
+
+    has_label = False
+    for argname, argtype, optional in parse_args(typeinfo):
+        if not has_label:
+            print
+            print 'qmp__out:'
+            has_label = True
+
+        if qmp_type_should_free(argtype):
+            print '    %s(%s);' % (qmp_free_func(argtype), c_var(argname))
+    print '    return;'
+    print '}'
+
+def print_lib_definition(name, required, optional, retval):
+    print
+    print_lib_decl(name, required, optional, retval)
+    print '''{
+    QDict *qmp__args = qdict_new();
+    Error *qmp__local_err = NULL;'''
+
+    print '    QObject *qmp__retval = NULL;'
+    if retval != 'none':
+        print '    %s qmp__native_retval = 0;' % (qmp_type_to_c(retval, True))
+    if qmp_type_is_event(retval):
+        print '    int qmp__global_handle = 0;'
+    print
+
+    for key in required:
+        argname = key
+        argtype = required[key]
+        print '    qdict_put_obj(qmp__args, "%s", %s(%s));' % (key, qmp_type_to_qobj_ctor(argtype), c_var(argname))
+    if required:
+        print
+
+    for key in optional:
+        argname = key
+        argtype = optional[key]
+        if argtype.startswith('**'):
+            print '''    {
+        KeyValues *qmp__i;
+        for (qmp__i = %s; qmp__i; qmp__i = qmp__i->next) {
+            qdict_put(qmp__args, qmp__i->key, qstring_from_str(qmp__i->value));
+        }
+    }''' % c_var(argname)
+            continue
+        print '    if (has_%s) {' % c_var(argname)
+        print '        qdict_put_obj(qmp__args, "%s", %s(%s));' % (key, qmp_type_to_qobj_ctor(argtype), c_var(argname))
+        print '    }'
+        print
+
+    print '    qmp__retval = qmp__session->dispatch(qmp__session, "%s", qmp__args, &qmp__local_err);' % name
+
+    print
+    print '    QDECREF(qmp__args);'
+
+    if type(retval) == list:
+        print '''
+    if (!qmp__local_err) {
+        QList *qmp__list_retval = qobject_to_qlist(qmp__retval);
+        QListEntry *qmp__i;
+        QLIST_FOREACH_ENTRY(qmp__list_retval, qmp__i) {
+            %s qmp__native_i = %s(qmp__i->value, &qmp__local_err);
+            if (qmp__local_err) {
+                %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;''' % (qmp_type_to_c(retval[0], True), qmp_type_from_qobj(retval[0]), qmp_free_func(retval[0]))
+    elif is_dict(retval):
+        print '    // FIXME (using an anonymous dict as return value)'
+        print '    BUILD_BUG();'
+    elif qmp_type_is_event(retval):
+        print '''    if (!qmp__local_err) {
+        qmp__global_handle = %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, %s, qmp__global_handle);
+    }
+    error_propagate(qmp__err, qmp__local_err);
+    return qmp__native_retval;''' % (qmp_type_from_qobj('int'), qmp_event_to_c(retval))
+    elif retval != 'none':
+        print '''
+    if (!qmp__local_err) {
+        qmp__native_retval = %s(qmp__retval, &qmp__local_err);
+        qobject_decref(qmp__retval);
+    }
+    error_propagate(qmp__err, qmp__local_err);
+    return qmp__native_retval;''' % qmp_type_from_qobj(retval)
+    else:
+        print '    qobject_decref(qmp__retval);'
+        print '    error_propagate(qmp__err, qmp__local_err);'
+
+    print '}'
+
 def tokenize(data):
     while len(data):
         if data[0] in ['{', '}', ':', ',', '[', ']']:
@@ -713,7 +878,10 @@  if len(sys.argv) == 2:
         kind = 'body'
     elif sys.argv[1] == '--header':
         kind = 'header'
-
+    elif sys.argv[1] == '--lib-header':
+        kind = 'lib-header'
+    elif sys.argv[1] == '--lib-body':
+        kind = 'lib-body'
 
 if kind == 'types-header':
     print '''/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT EDIT */
@@ -759,6 +927,19 @@  elif kind == 'body':
 #include "qmp.h"
 #include "qmp-core.h"
 '''
+elif kind == 'lib-header':
+    print '''/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT EDIT */
+#ifndef LIBQMP_H
+#define LIBQMP_H
+
+#include "libqmp-core.h"
+'''
+elif kind == 'lib-body':
+    print '''/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT EDIT */
+
+#include "libqmp.h"
+#include "libqmp-internal.h"
+'''
 
 exprs = []
 expr = ''
@@ -794,6 +975,9 @@  for s in exprs:
                 print_type_marshal_definition(key, s[key])
             elif kind == 'marshal-header':
                 print_type_marshal_declaration(key, s[key])
+            elif kind == 'lib-body':
+                if qmp_type_is_event(key):
+                    print_lib_event_definition(key, event_types[key])
         else:
             enum_types.append(key)
             if kind == 'types-header':
@@ -810,6 +994,10 @@  for s in exprs:
             print_definition(name, required, optional, retval)
         elif kind == 'header':
             print_declaration(name, required, optional, retval)
+        elif kind == 'lib-body':
+            print_lib_definition(name, required, optional, retval)
+        elif kind == 'lib-header':
+            print_lib_declaration(name, required, optional, retval)
 
 if kind.endswith('header'):
     print '#endif'
@@ -827,3 +1015,10 @@  elif kind == 'body':
     print '};'
     print
     print 'qapi_init(qmp_init_marshal);'
+elif kind == 'lib-body':
+    print
+    print 'void libqmp_init_events(QmpSession *sess)'
+    print '{'
+    for event in event_types:
+        print '    libqmp_register_event(sess, "%s", &libqmp_notify_%s);' % (event, de_camel_case(qmp_event_to_c(event)))
+    print '}'