* [Qemu-devel] [PATCH 00/22] QAPI Round 1
@ 2011-03-07 1:22 Anthony Liguori
2011-03-07 1:22 ` [Qemu-devel] [PATCH 01/22] Add hard build dependency on glib Anthony Liguori
` (26 more replies)
0 siblings, 27 replies; 106+ messages in thread
From: Anthony Liguori @ 2011-03-07 1:22 UTC (permalink / raw)
To: qemu-devel; +Cc: Adam Litke, Markus Armbruster, Luiz Capitulino
This is the first round of QAPI. This lays all the basic ground work for QAPI
including a client library, new server, and new signal/slot mechanism.
The QAPI development branch contains conversions of every 0.14 QMP command and
also converts all possible HMP commands to use the QMP versions of these
commands.
The bulk of QAPI is generated by a JSON schema using a Python code generator.
The test suite is based on glib.
Reviewing the code generator is hard. I'll reply to each note with the code
generator output to show what kind of code actually is generated.
This series adds an unconditional build dependency on Python and glib.
More information about QAPI can be found on the wiki:
http://wiki.qemu.org/Features/QAPI
^ permalink raw reply [flat|nested] 106+ messages in thread
* [Qemu-devel] [PATCH 01/22] Add hard build dependency on glib
2011-03-07 1:22 [Qemu-devel] [PATCH 00/22] QAPI Round 1 Anthony Liguori
@ 2011-03-07 1:22 ` Anthony Liguori
2011-03-07 10:59 ` Daniel P. Berrange
2011-03-07 1:22 ` [Qemu-devel] [PATCH 02/22] qerror: expose a function to format an error Anthony Liguori
` (25 subsequent siblings)
26 siblings, 1 reply; 106+ messages in thread
From: Anthony Liguori @ 2011-03-07 1:22 UTC (permalink / raw)
To: qemu-devel
Cc: Adam Litke, Anthony Liguori, Markus Armbruster, Luiz Capitulino
GLib is an extremely common library that has a portable thread implementation
along with tons of other goodies.
GLib and GObject have a fantastic amount of infrastructure we can leverage in
QEMU including an object oriented programming infrastructure.
Short term, it has a very nice thread pool implementation that we could leverage
in something like virtio-9p. It also has a test harness implementation that
this series will use.
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
diff --git a/Makefile b/Makefile
index eca4c76..6b1d716 100644
--- a/Makefile
+++ b/Makefile
@@ -104,6 +104,8 @@ audio/audio.o audio/fmodaudio.o: QEMU_CFLAGS += $(FMOD_CFLAGS)
QEMU_CFLAGS+=$(CURL_CFLAGS)
+QEMU_CFLAGS+=$(GLIB_CFLAGS)
+
ui/cocoa.o: ui/cocoa.m
ui/sdl.o audio/sdlaudio.o ui/sdl_zoom.o baum.o: QEMU_CFLAGS += $(SDL_CFLAGS)
diff --git a/Makefile.objs b/Makefile.objs
index 9e98a66..0ba02c7 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -322,3 +322,5 @@ vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
vl.o: QEMU_CFLAGS+=$(SDL_CFLAGS)
+vl.o: QEMU_CFLAGS+=$(GLIB_CFLAGS)
+
diff --git a/Makefile.target b/Makefile.target
index 220589e..0bd42da 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -204,6 +204,7 @@ QEMU_CFLAGS += $(VNC_TLS_CFLAGS)
QEMU_CFLAGS += $(VNC_SASL_CFLAGS)
QEMU_CFLAGS += $(VNC_JPEG_CFLAGS)
QEMU_CFLAGS += $(VNC_PNG_CFLAGS)
+QEMU_CFLAGS += $(GLIB_CFLAGS)
# xen backend driver support
obj-$(CONFIG_XEN) += xen_machine_pv.o xen_domainbuild.o
diff --git a/configure b/configure
index ef51a58..e1305ae 100755
--- a/configure
+++ b/configure
@@ -1662,6 +1662,18 @@ EOF
fi
##########################################
+# glib support probe
+if $pkg_config --modversion gthread-2.0 > /dev/null 2>&1 ; then
+ glib_cflags=`$pkg_config --cflags gthread-2.0 2>/dev/null`
+ glib_libs=`$pkg_config --libs gthread-2.0 2>/dev/null`
+ libs_softmmu="$glib_libs $libs_softmmu"
+ libs_tools="$glib_libs $libs_softmmu"
+else
+ echo "glib-2.0 required to compile QEMU"
+ exit 1
+fi
+
+##########################################
# kvm probe
if test "$kvm" != "no" ; then
cat > $TMPC <<EOF
@@ -2757,6 +2769,7 @@ if test "$bluez" = "yes" ; then
echo "CONFIG_BLUEZ=y" >> $config_host_mak
echo "BLUEZ_CFLAGS=$bluez_cflags" >> $config_host_mak
fi
+echo "GLIB_CFLAGS=$glib_cflags" >> $config_host_mak
if test "$xen" = "yes" ; then
echo "CONFIG_XEN=y" >> $config_host_mak
fi
--
1.7.0.4
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [Qemu-devel] [PATCH 02/22] qerror: expose a function to format an error
2011-03-07 1:22 [Qemu-devel] [PATCH 00/22] QAPI Round 1 Anthony Liguori
2011-03-07 1:22 ` [Qemu-devel] [PATCH 01/22] Add hard build dependency on glib Anthony Liguori
@ 2011-03-07 1:22 ` Anthony Liguori
2011-03-07 11:14 ` Stefan Hajnoczi
2011-03-07 1:22 ` [Qemu-devel] [PATCH 03/22] qapi: add Error object Anthony Liguori
` (24 subsequent siblings)
26 siblings, 1 reply; 106+ messages in thread
From: Anthony Liguori @ 2011-03-07 1:22 UTC (permalink / raw)
To: qemu-devel
Cc: Adam Litke, Anthony Liguori, Markus Armbruster, Luiz Capitulino
This will let Error share the QError human formatting. This is only used for
HMP.
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
diff --git a/qerror.c b/qerror.c
index 4855604..13d53c9 100644
--- a/qerror.c
+++ b/qerror.c
@@ -326,12 +326,18 @@ QError *qerror_from_info(const char *file, int linenr, const char *func,
return qerr;
}
-static void parse_error(const QError *qerror, int c)
+static void parse_error(const QErrorStringTable *entry, int c)
{
- qerror_abort(qerror, "expected '%c' in '%s'", c, qerror->entry->desc);
+#if 0
+ qerror_abort(qerror, "expected '%c' in '%s'", c, entry->desc);
+#else
+ fprintf(stderr, "expected '%c' in '%s'", c, entry->desc);
+ abort();
+#endif
}
-static const char *append_field(QString *outstr, const QError *qerror,
+static const char *append_field(QDict *error, QString *outstr,
+ const QErrorStringTable *entry,
const char *start)
{
QObject *obj;
@@ -339,24 +345,31 @@ static const char *append_field(QString *outstr, const QError *qerror,
QString *key_qs;
const char *end, *key;
- if (*start != '%')
- parse_error(qerror, '%');
+ if (*start != '%') {
+ parse_error(entry, '%');
+ }
start++;
- if (*start != '(')
- parse_error(qerror, '(');
+ if (*start != '(') {
+ parse_error(entry, '(');
+ }
start++;
end = strchr(start, ')');
- if (!end)
- parse_error(qerror, ')');
+ if (!end) {
+ parse_error(entry, ')');
+ }
key_qs = qstring_from_substr(start, 0, end - start - 1);
key = qstring_get_str(key_qs);
- qdict = qobject_to_qdict(qdict_get(qerror->error, "data"));
+ qdict = qobject_to_qdict(qdict_get(error, "data"));
obj = qdict_get(qdict, key);
if (!obj) {
+#if 0
qerror_abort(qerror, "key '%s' not found in QDict", key);
+#else
+ abort();
+#endif
}
switch (qobject_type(obj)) {
@@ -367,41 +380,66 @@ static const char *append_field(QString *outstr, const QError *qerror,
qstring_append_int(outstr, qdict_get_int(qdict, key));
break;
default:
+#if 0
qerror_abort(qerror, "invalid type '%c'", qobject_type(obj));
+#else
+ abort();
+#endif
}
QDECREF(key_qs);
return ++end;
}
-/**
- * qerror_human(): Format QError data into human-readable string.
- *
- * Formats according to member 'desc' of the specified QError object.
- */
-QString *qerror_human(const QError *qerror)
+static QString *qerror_format_desc(QDict *error,
+ const QErrorStringTable *entry)
{
- const char *p;
QString *qstring;
+ const char *p;
- assert(qerror->entry != NULL);
+ assert(entry != NULL);
qstring = qstring_new();
- for (p = qerror->entry->desc; *p != '\0';) {
+ for (p = entry->desc; *p != '\0';) {
if (*p != '%') {
qstring_append_chr(qstring, *p++);
} else if (*(p + 1) == '%') {
qstring_append_chr(qstring, '%');
p += 2;
} else {
- p = append_field(qstring, qerror, p);
+ p = append_field(error, qstring, entry, p);
}
}
return qstring;
}
+QString *qerror_format(const char *fmt, QDict *error)
+{
+ const QErrorStringTable *entry = NULL;
+ int i;
+
+ for (i = 0; qerror_table[i].error_fmt; i++) {
+ if (strcmp(qerror_table[i].error_fmt, fmt) == 0) {
+ entry = &qerror_table[i];
+ break;
+ }
+ }
+
+ return qerror_format_desc(error, entry);
+}
+
+/**
+ * qerror_human(): Format QError data into human-readable string.
+ *
+ * Formats according to member 'desc' of the specified QError object.
+ */
+QString *qerror_human(const QError *qerror)
+{
+ return qerror_format_desc(qerror->error, qerror->entry);
+}
+
/**
* qerror_print(): Print QError data
*
diff --git a/qerror.h b/qerror.h
index f732d45..fd63ee9 100644
--- a/qerror.h
+++ b/qerror.h
@@ -42,6 +42,7 @@ void qerror_report_internal(const char *file, int linenr, const char *func,
#define qerror_report(fmt, ...) \
qerror_report_internal(__FILE__, __LINE__, __func__, fmt, ## __VA_ARGS__)
QError *qobject_to_qerror(const QObject *obj);
+QString *qerror_format(const char *fmt, QDict *error);
/*
* QError class list
--
1.7.0.4
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [Qemu-devel] [PATCH 03/22] qapi: add Error object
2011-03-07 1:22 [Qemu-devel] [PATCH 00/22] QAPI Round 1 Anthony Liguori
2011-03-07 1:22 ` [Qemu-devel] [PATCH 01/22] Add hard build dependency on glib Anthony Liguori
2011-03-07 1:22 ` [Qemu-devel] [PATCH 02/22] qerror: expose a function to format an error Anthony Liguori
@ 2011-03-07 1:22 ` Anthony Liguori
2011-03-07 11:06 ` Daniel P. Berrange
2011-03-07 11:38 ` Stefan Hajnoczi
2011-03-07 1:22 ` [Qemu-devel] [PATCH 04/22] qerror: split out the reporting bits of QError Anthony Liguori
` (23 subsequent siblings)
26 siblings, 2 replies; 106+ messages in thread
From: Anthony Liguori @ 2011-03-07 1:22 UTC (permalink / raw)
To: qemu-devel
Cc: Adam Litke, Anthony Liguori, Markus Armbruster, Luiz Capitulino
The Error class is similar to QError (now deprecated) except that it supports
propagation. This allows for higher quality error handling. It's losely
modeled after glib style GErrors.
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
diff --git a/Makefile.objs b/Makefile.objs
index 0ba02c7..da31530 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -15,6 +15,7 @@ oslib-obj-$(CONFIG_POSIX) += oslib-posix.o
block-obj-y = cutils.o cache-utils.o qemu-malloc.o qemu-option.o module.o
block-obj-y += nbd.o block.o aio.o aes.o qemu-config.o
+block-obj-y += error.o
block-obj-$(CONFIG_POSIX) += posix-aio-compat.o
block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
diff --git a/error.c b/error.c
new file mode 100644
index 0000000..5d84106
--- /dev/null
+++ b/error.c
@@ -0,0 +1,122 @@
+/*
+ * QEMU Error Objects
+ *
+ * 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 "error.h"
+#include "error_int.h"
+#include "qemu-objects.h"
+#include "qerror.h"
+#include <assert.h>
+
+struct Error
+{
+ QDict *obj;
+ const char *fmt;
+ char *msg;
+};
+
+void error_set(Error **errp, const char *fmt, ...)
+{
+ Error *err;
+ va_list ap;
+
+ if (errp == NULL) {
+ return;
+ }
+
+ err = qemu_mallocz(sizeof(*err));
+
+ va_start(ap, fmt);
+ err->obj = qobject_to_qdict(qobject_from_jsonv(fmt, &ap));
+ va_end(ap);
+ err->fmt = fmt;
+
+ *errp = err;
+}
+
+bool error_is_set(Error **errp)
+{
+ return (errp && *errp);
+}
+
+const char *error_get_pretty(Error *err)
+{
+ if (err->msg == NULL) {
+ QString *str;
+ str = qerror_format(err->fmt, err->obj);
+ err->msg = qemu_strdup(qstring_get_str(str));
+ }
+
+ return err->msg;
+}
+
+const char *error_get_field(Error *err, const char *field)
+{
+ if (strcmp(field, "class") == 0) {
+ return qdict_get_str(err->obj, field);
+ } else {
+ QDict *dict = qdict_get_qdict(err->obj, "data");
+ return qdict_get_str(dict, field);
+ }
+}
+
+void error_free(Error *err)
+{
+ QDECREF(err->obj);
+ qemu_free(err->msg);
+ qemu_free(err);
+}
+
+bool error_is_type(Error *err, const char *fmt)
+{
+ char *ptr;
+ char *end;
+ char classname[1024];
+
+ ptr = strstr(fmt, "'class': '");
+ assert(ptr != NULL);
+ ptr += strlen("'class': '");
+
+ end = strchr(ptr, '\'');
+ assert(end != NULL);
+
+ memcpy(classname, ptr, (end - ptr));
+ classname[(end - ptr)] = 0;
+
+ return strcmp(classname, error_get_field(err, "class")) == 0;
+}
+
+void error_propagate(Error **dst_err, Error *local_err)
+{
+ if (dst_err) {
+ *dst_err = local_err;
+ } else if (local_err) {
+ error_free(local_err);
+ }
+}
+
+QObject *error_get_qobject(Error *err)
+{
+ QINCREF(err->obj);
+ return QOBJECT(err->obj);
+}
+
+void error_set_qobject(Error **errp, QObject *obj)
+{
+ Error *err;
+ if (errp == NULL) {
+ return;
+ }
+ err = qemu_mallocz(sizeof(*err));
+ err->obj = qobject_to_qdict(obj);
+ qobject_incref(obj);
+
+ *errp = err;
+}
diff --git a/error.h b/error.h
new file mode 100644
index 0000000..317d487
--- /dev/null
+++ b/error.h
@@ -0,0 +1,65 @@
+/*
+ * QEMU Error Objects
+ *
+ * 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 ERROR_H
+#define ERROR_H
+
+#include <stdbool.h>
+
+/**
+ * A class representing internal errors within QEMU. An error has a string
+ * typename and optionally a set of named string parameters.
+ */
+typedef struct Error Error;
+
+/**
+ * Set an indirect pointer to an error given a printf-style format parameter.
+ * Currently, qerror.h defines these error formats. This function is not
+ * meant to be used outside of QEMU.
+ */
+void error_set(Error **err, const char *fmt, ...)
+ __attribute__((format(printf, 2, 3)));
+
+/**
+ * Returns true if an indirect pointer to an error is pointing to a valid
+ * error object.
+ */
+bool error_is_set(Error **err);
+
+/**
+ * Get a human readable representation of an error object.
+ */
+const char *error_get_pretty(Error *err);
+
+/**
+ * Get an individual named error field.
+ */
+const char *error_get_field(Error *err, const char *field);
+
+/**
+ * Propagate an error to an indirect pointer to an error. This function will
+ * always transfer ownership of the error reference and handles the case where
+ * dst_err is NULL correctly.
+ */
+void error_propagate(Error **dst_err, Error *local_err);
+
+/**
+ * Free an error object.
+ */
+void error_free(Error *err);
+
+/**
+ * Determine if an error is of a speific type (based on the qerror format).
+ * Non-QEMU users should get the `class' field to identify the error type.
+ */
+bool error_is_type(Error *err, const char *fmt);
+
+#endif
diff --git a/error_int.h b/error_int.h
new file mode 100644
index 0000000..eaba65e
--- /dev/null
+++ b/error_int.h
@@ -0,0 +1,27 @@
+/*
+ * QEMU Error Objects
+ *
+ * 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 QEMU_ERROR_INT_H
+#define QEMU_ERROR_INT_H
+
+#include "qemu-common.h"
+#include "qobject.h"
+#include "error.h"
+
+/**
+ * Internal QEMU functions for working with Error.
+ *
+ * These are used to convert QErrors to Errors
+ */
+QObject *error_get_qobject(Error *err);
+void error_set_qobject(Error **errp, QObject *obj);
+
+#endif
--
1.7.0.4
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [Qemu-devel] [PATCH 04/22] qerror: split out the reporting bits of QError
2011-03-07 1:22 [Qemu-devel] [PATCH 00/22] QAPI Round 1 Anthony Liguori
` (2 preceding siblings ...)
2011-03-07 1:22 ` [Qemu-devel] [PATCH 03/22] qapi: add Error object Anthony Liguori
@ 2011-03-07 1:22 ` Anthony Liguori
2011-03-07 1:22 ` [Qemu-devel] [PATCH 05/22] qerror: add new error message for invalid enum values Anthony Liguori
` (22 subsequent siblings)
26 siblings, 0 replies; 106+ messages in thread
From: Anthony Liguori @ 2011-03-07 1:22 UTC (permalink / raw)
To: qemu-devel
Cc: Adam Litke, Anthony Liguori, Markus Armbruster, Luiz Capitulino
These make it very hard to compile QError outside of QEMU.
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
diff --git a/Makefile.objs b/Makefile.objs
index da31530..69f0383 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -2,7 +2,7 @@
# QObject
qobject-obj-y = qint.o qstring.o qdict.o qlist.o qfloat.o qbool.o
qobject-obj-y += qjson.o json-lexer.o json-streamer.o json-parser.o
-qobject-obj-y += qerror.o
+qobject-obj-y += qerror.o qerror-report.o
#######################################################################
# oslib-obj-y is code depending on the OS (win32 vs posix)
diff --git a/qerror-report.c b/qerror-report.c
new file mode 100644
index 0000000..1ebb111
--- /dev/null
+++ b/qerror-report.c
@@ -0,0 +1,131 @@
+/*
+ * QError Module
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * Authors:
+ * Luiz Capitulino <lcapitulino@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+
+#include "qemu-common.h"
+#include "qerror.h"
+#include "monitor.h"
+#include "qjson.h"
+
+static void GCC_FMT_ATTR(2, 3) qerror_abort(const QError *qerr,
+ const char *fmt, ...)
+{
+ va_list ap;
+
+ fprintf(stderr, "qerror: bad call in function '%s':\n", qerr->func);
+ fprintf(stderr, "qerror: -> ");
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+
+ fprintf(stderr, "\nqerror: call at %s:%d\n", qerr->file, qerr->linenr);
+ abort();
+}
+
+static void GCC_FMT_ATTR(2, 0) qerror_set_data(QError *qerr,
+ const char *fmt, va_list *va)
+{
+ QObject *obj;
+
+ obj = qobject_from_jsonv(fmt, va);
+ if (!obj) {
+ qerror_abort(qerr, "invalid format '%s'", fmt);
+ }
+ if (qobject_type(obj) != QTYPE_QDICT) {
+ qerror_abort(qerr, "error format is not a QDict '%s'", fmt);
+ }
+
+ qerr->error = qobject_to_qdict(obj);
+
+ obj = qdict_get(qerr->error, "class");
+ if (!obj) {
+ qerror_abort(qerr, "missing 'class' key in '%s'", fmt);
+ }
+ if (qobject_type(obj) != QTYPE_QSTRING) {
+ qerror_abort(qerr, "'class' key value should be a QString");
+ }
+
+ obj = qdict_get(qerr->error, "data");
+ if (!obj) {
+ qerror_abort(qerr, "missing 'data' key in '%s'", fmt);
+ }
+ if (qobject_type(obj) != QTYPE_QDICT) {
+ qerror_abort(qerr, "'data' key value should be a QDICT");
+ }
+}
+
+/**
+ * qerror_from_info(): Create a new QError from error information
+ *
+ * The information consists of:
+ *
+ * - file the file name of where the error occurred
+ * - linenr the line number of where the error occurred
+ * - func the function name of where the error occurred
+ * - fmt JSON printf-like dictionary, there must exist keys 'class' and
+ * 'data'
+ * - va va_list of all arguments specified by fmt
+ *
+ * Return strong reference.
+ */
+QError *qerror_from_info(const char *file, int linenr, const char *func,
+ const char *fmt, va_list *va)
+{
+ QError *qerr;
+
+ qerr = qerror_new();
+ loc_save(&qerr->loc);
+ qerr->linenr = linenr;
+ qerr->file = file;
+ qerr->func = func;
+
+ if (!fmt) {
+ qerror_abort(qerr, "QDict not specified");
+ }
+
+ qerror_set_data(qerr, fmt, va);
+ qerror_set_desc(qerr, fmt);
+
+ return qerr;
+}
+
+/**
+ * qerror_print(): Print QError data
+ *
+ * This function will print the member 'desc' of the specified QError object,
+ * it uses error_report() for this, so that the output is routed to the right
+ * place (ie. stderr or Monitor's device).
+ */
+void qerror_print(QError *qerror)
+{
+ QString *qstring = qerror_human(qerror);
+ loc_push_restore(&qerror->loc);
+ error_report("%s", qstring_get_str(qstring));
+ loc_pop(&qerror->loc);
+ QDECREF(qstring);
+}
+
+void qerror_report_internal(const char *file, int linenr, const char *func,
+ const char *fmt, ...)
+{
+ va_list va;
+ QError *qerror;
+
+ va_start(va, fmt);
+ qerror = qerror_from_info(file, linenr, func, fmt, &va);
+ va_end(va);
+
+ qerror_print(qerror);
+ QDECREF(qerror);
+}
+
+
diff --git a/qerror.c b/qerror.c
index 13d53c9..78d3884 100644
--- a/qerror.c
+++ b/qerror.c
@@ -243,39 +243,7 @@ static void GCC_FMT_ATTR(2, 3) qerror_abort(const QError *qerr,
abort();
}
-static void GCC_FMT_ATTR(2, 0) qerror_set_data(QError *qerr,
- const char *fmt, va_list *va)
-{
- QObject *obj;
-
- obj = qobject_from_jsonv(fmt, va);
- if (!obj) {
- qerror_abort(qerr, "invalid format '%s'", fmt);
- }
- if (qobject_type(obj) != QTYPE_QDICT) {
- qerror_abort(qerr, "error format is not a QDict '%s'", fmt);
- }
-
- qerr->error = qobject_to_qdict(obj);
-
- obj = qdict_get(qerr->error, "class");
- if (!obj) {
- qerror_abort(qerr, "missing 'class' key in '%s'", fmt);
- }
- if (qobject_type(obj) != QTYPE_QSTRING) {
- qerror_abort(qerr, "'class' key value should be a QString");
- }
-
- obj = qdict_get(qerr->error, "data");
- if (!obj) {
- qerror_abort(qerr, "missing 'data' key in '%s'", fmt);
- }
- if (qobject_type(obj) != QTYPE_QDICT) {
- qerror_abort(qerr, "'data' key value should be a QDICT");
- }
-}
-
-static void qerror_set_desc(QError *qerr, const char *fmt)
+void qerror_set_desc(QError *qerr, const char *fmt)
{
int i;
@@ -291,41 +259,6 @@ static void qerror_set_desc(QError *qerr, const char *fmt)
qerror_abort(qerr, "error format '%s' not found", fmt);
}
-/**
- * qerror_from_info(): Create a new QError from error information
- *
- * The information consists of:
- *
- * - file the file name of where the error occurred
- * - linenr the line number of where the error occurred
- * - func the function name of where the error occurred
- * - fmt JSON printf-like dictionary, there must exist keys 'class' and
- * 'data'
- * - va va_list of all arguments specified by fmt
- *
- * Return strong reference.
- */
-QError *qerror_from_info(const char *file, int linenr, const char *func,
- const char *fmt, va_list *va)
-{
- QError *qerr;
-
- qerr = qerror_new();
- loc_save(&qerr->loc);
- qerr->linenr = linenr;
- qerr->file = file;
- qerr->func = func;
-
- if (!fmt) {
- qerror_abort(qerr, "QDict not specified");
- }
-
- qerror_set_data(qerr, fmt, va);
- qerror_set_desc(qerr, fmt);
-
- return qerr;
-}
-
static void parse_error(const QErrorStringTable *entry, int c)
{
#if 0
@@ -441,40 +374,6 @@ QString *qerror_human(const QError *qerror)
}
/**
- * qerror_print(): Print QError data
- *
- * This function will print the member 'desc' of the specified QError object,
- * it uses error_report() for this, so that the output is routed to the right
- * place (ie. stderr or Monitor's device).
- */
-void qerror_print(QError *qerror)
-{
- QString *qstring = qerror_human(qerror);
- loc_push_restore(&qerror->loc);
- error_report("%s", qstring_get_str(qstring));
- loc_pop(&qerror->loc);
- QDECREF(qstring);
-}
-
-void qerror_report_internal(const char *file, int linenr, const char *func,
- const char *fmt, ...)
-{
- va_list va;
- QError *qerror;
-
- va_start(va, fmt);
- qerror = qerror_from_info(file, linenr, func, fmt, &va);
- va_end(va);
-
- if (monitor_cur_is_qmp()) {
- monitor_set_error(cur_mon, qerror);
- } else {
- qerror_print(qerror);
- QDECREF(qerror);
- }
-}
-
-/**
* qobject_to_qerror(): Convert a QObject into a QError
*/
QError *qobject_to_qerror(const QObject *obj)
diff --git a/qerror.h b/qerror.h
index fd63ee9..495d0a4 100644
--- a/qerror.h
+++ b/qerror.h
@@ -43,6 +43,7 @@ void qerror_report_internal(const char *file, int linenr, const char *func,
qerror_report_internal(__FILE__, __LINE__, __func__, fmt, ## __VA_ARGS__)
QError *qobject_to_qerror(const QObject *obj);
QString *qerror_format(const char *fmt, QDict *error);
+void qerror_set_desc(QError *qerr, const char *fmt);
/*
* QError class list
--
1.7.0.4
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [Qemu-devel] [PATCH 05/22] qerror: add new error message for invalid enum values
2011-03-07 1:22 [Qemu-devel] [PATCH 00/22] QAPI Round 1 Anthony Liguori
` (3 preceding siblings ...)
2011-03-07 1:22 ` [Qemu-devel] [PATCH 04/22] qerror: split out the reporting bits of QError Anthony Liguori
@ 2011-03-07 1:22 ` Anthony Liguori
2011-03-07 1:22 ` [Qemu-devel] [PATCH 06/22] qapi: add JSON parsing error message Anthony Liguori
` (21 subsequent siblings)
26 siblings, 0 replies; 106+ messages in thread
From: Anthony Liguori @ 2011-03-07 1:22 UTC (permalink / raw)
To: qemu-devel
Cc: Adam Litke, Anthony Liguori, Markus Armbruster, Luiz Capitulino
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
diff --git a/qerror.c b/qerror.c
index 78d3884..5a1e637 100644
--- a/qerror.c
+++ b/qerror.c
@@ -109,6 +109,10 @@ static const QErrorStringTable qerror_table[] = {
.desc = "Duplicate ID '%(id)' for %(object)",
},
{
+ .error_fmt = QERR_ENUM_VALUE_INVALID,
+ .desc = "Enum value '%(value)' is invalid for type '%(type)'",
+ },
+ {
.error_fmt = QERR_FD_NOT_FOUND,
.desc = "File descriptor named '%(name)' not found",
},
diff --git a/qerror.h b/qerror.h
index 495d0a4..35e7253 100644
--- a/qerror.h
+++ b/qerror.h
@@ -98,6 +98,9 @@ void qerror_set_desc(QError *qerr, const char *fmt);
#define QERR_DUPLICATE_ID \
"{ 'class': 'DuplicateId', 'data': { 'id': %s, 'object': %s } }"
+#define QERR_ENUM_VALUE_INVALID \
+ "{ 'class': 'EnumValueInvalid', 'data': { 'type': %s, 'value': %s } }"
+
#define QERR_FD_NOT_FOUND \
"{ 'class': 'FdNotFound', 'data': { 'name': %s } }"
--
1.7.0.4
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [Qemu-devel] [PATCH 06/22] qapi: add JSON parsing error message
2011-03-07 1:22 [Qemu-devel] [PATCH 00/22] QAPI Round 1 Anthony Liguori
` (4 preceding siblings ...)
2011-03-07 1:22 ` [Qemu-devel] [PATCH 05/22] qerror: add new error message for invalid enum values Anthony Liguori
@ 2011-03-07 1:22 ` Anthony Liguori
2011-03-07 1:22 ` [Qemu-devel] [PATCH 07/22] json: propagate error from parser Anthony Liguori
` (20 subsequent siblings)
26 siblings, 0 replies; 106+ messages in thread
From: Anthony Liguori @ 2011-03-07 1:22 UTC (permalink / raw)
To: qemu-devel
Cc: Adam Litke, Anthony Liguori, Markus Armbruster, Luiz Capitulino
Using a string like this is a cop-out. I plan on changing this before 0.15.
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
diff --git a/qerror.c b/qerror.c
index 5a1e637..c12dd3d 100644
--- a/qerror.c
+++ b/qerror.c
@@ -145,6 +145,10 @@ static const QErrorStringTable qerror_table[] = {
.desc = "Invalid JSON syntax",
},
{
+ .error_fmt = QERR_JSON_PARSE_ERROR,
+ .desc = "Error parsing JSON: %(message)",
+ },
+ {
.error_fmt = QERR_KVM_MISSING_CAP,
.desc = "Using KVM without %(capability), %(feature) unavailable",
},
diff --git a/qerror.h b/qerror.h
index 35e7253..a0fb98d 100644
--- a/qerror.h
+++ b/qerror.h
@@ -125,6 +125,9 @@ void qerror_set_desc(QError *qerr, const char *fmt);
#define QERR_JSON_PARSING \
"{ 'class': 'JSONParsing', 'data': {} }"
+#define QERR_JSON_PARSE_ERROR \
+ "{ 'class': 'JSONParseError', 'data': { 'message': %s } }"
+
#define QERR_KVM_MISSING_CAP \
"{ 'class': 'KVMMissingCap', 'data': { 'capability': %s, 'feature': %s } }"
--
1.7.0.4
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [Qemu-devel] [PATCH 07/22] json: propagate error from parser
2011-03-07 1:22 [Qemu-devel] [PATCH 00/22] QAPI Round 1 Anthony Liguori
` (5 preceding siblings ...)
2011-03-07 1:22 ` [Qemu-devel] [PATCH 06/22] qapi: add JSON parsing error message Anthony Liguori
@ 2011-03-07 1:22 ` Anthony Liguori
2011-03-07 1:22 ` [Qemu-devel] [PATCH 08/22] qapi: add code generator for qmp-types Anthony Liguori
` (19 subsequent siblings)
26 siblings, 0 replies; 106+ messages in thread
From: Anthony Liguori @ 2011-03-07 1:22 UTC (permalink / raw)
To: qemu-devel
Cc: Adam Litke, Anthony Liguori, Markus Armbruster, Luiz Capitulino
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
diff --git a/json-parser.c b/json-parser.c
index 6c06ef9..ac4063a 100644
--- a/json-parser.c
+++ b/json-parser.c
@@ -22,9 +22,11 @@
#include "qbool.h"
#include "json-parser.h"
#include "json-lexer.h"
+#include "qerror.h"
typedef struct JSONParserContext
{
+ Error *err;
} JSONParserContext;
#define BUG_ON(cond) assert(!(cond))
@@ -95,11 +97,15 @@ static void GCC_FMT_ATTR(3, 4) parse_error(JSONParserContext *ctxt,
QObject *token, const char *msg, ...)
{
va_list ap;
+ char message[1024];
va_start(ap, msg);
- fprintf(stderr, "parse error: ");
- vfprintf(stderr, msg, ap);
- fprintf(stderr, "\n");
+ vsnprintf(message, sizeof(message), msg, ap);
va_end(ap);
+ if (ctxt->err) {
+ error_free(ctxt->err);
+ ctxt->err = NULL;
+ }
+ error_set(&ctxt->err, QERR_JSON_PARSE_ERROR, message);
}
/**
@@ -565,6 +571,11 @@ static QObject *parse_value(JSONParserContext *ctxt, QList **tokens, va_list *ap
QObject *json_parser_parse(QList *tokens, va_list *ap)
{
+ return json_parser_parse_err(tokens, ap, NULL);
+}
+
+QObject *json_parser_parse_err(QList *tokens, va_list *ap, Error **errp)
+{
JSONParserContext ctxt = {};
QList *working = qlist_copy(tokens);
QObject *result;
@@ -573,5 +584,7 @@ QObject *json_parser_parse(QList *tokens, va_list *ap)
QDECREF(working);
+ error_propagate(errp, ctxt.err);
+
return result;
}
diff --git a/json-parser.h b/json-parser.h
index 97f43f6..8f2b5ec 100644
--- a/json-parser.h
+++ b/json-parser.h
@@ -16,7 +16,9 @@
#include "qemu-common.h"
#include "qlist.h"
+#include "error.h"
QObject *json_parser_parse(QList *tokens, va_list *ap);
+QObject *json_parser_parse_err(QList *tokens, va_list *ap, Error **errp);
#endif
--
1.7.0.4
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [Qemu-devel] [PATCH 08/22] qapi: add code generator for qmp-types
2011-03-07 1:22 [Qemu-devel] [PATCH 00/22] QAPI Round 1 Anthony Liguori
` (6 preceding siblings ...)
2011-03-07 1:22 ` [Qemu-devel] [PATCH 07/22] json: propagate error from parser Anthony Liguori
@ 2011-03-07 1:22 ` Anthony Liguori
2011-03-07 1:57 ` Anthony Liguori
2011-03-07 1:22 ` [Qemu-devel] [PATCH 09/22] qapi: add code generator for type marshallers Anthony Liguori
` (18 subsequent siblings)
26 siblings, 1 reply; 106+ messages in thread
From: Anthony Liguori @ 2011-03-07 1:22 UTC (permalink / raw)
To: qemu-devel
Cc: Adam Litke, Anthony Liguori, Markus Armbruster, Luiz Capitulino
Only generate qmp-types.[ch]. These files contain the type definitions for
QMP along with the alloc/free functions for these types. Functions to convert
enum values to integers and vice versa are also included.
qmp-types is used both within QEMU and within libqmp
Special alloc/free functions are provided to ensure that all structures are
padded when allocated. This makes sure that libqmp can provide a forward
compatible interface since all additions to a structure will have a boolean
enable flag.
The free function is convenient since individual structures may have pointers
that also require freeing.
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
diff --git a/Makefile b/Makefile
index 6b1d716..6b9fd69 100644
--- a/Makefile
+++ b/Makefile
@@ -4,6 +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
ifneq ($(wildcard config-host.mak),)
# Put the all: rule here so that config-host.mak can contain dependencies.
@@ -146,6 +147,14 @@ trace-dtrace.o: trace-dtrace.dtrace $(GENERATED_HEADERS)
simpletrace.o: simpletrace.c $(GENERATED_HEADERS)
+qmp-types.c: $(SRC_PATH)/qmp-schema.json $(SRC_PATH)/qmp-gen.py
+ $(call quiet-command,python $(SRC_PATH)/qmp-gen.py --types-body < $< > $@, " GEN $@")
+
+qmp-types.h: $(SRC_PATH)/qmp-schema.json $(SRC_PATH)/qmp-gen.py
+ $(call quiet-command,python $(SRC_PATH)/qmp-gen.py --types-header < $< > $@, " GEN $@")
+
+qmp-types.o: qmp-types.c qmp-types.h
+
version.o: $(SRC_PATH)/version.rc config-host.mak
$(call quiet-command,$(WINDRES) -I. -o $@ $<," RC $(TARGET_DIR)$@")
diff --git a/Makefile.objs b/Makefile.objs
index 69f0383..710d99f 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -15,7 +15,7 @@ oslib-obj-$(CONFIG_POSIX) += oslib-posix.o
block-obj-y = cutils.o cache-utils.o qemu-malloc.o qemu-option.o module.o
block-obj-y += nbd.o block.o aio.o aes.o qemu-config.o
-block-obj-y += error.o
+block-obj-y += error.o qmp-types.o
block-obj-$(CONFIG_POSIX) += posix-aio-compat.o
block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
diff --git a/ordereddict.py b/ordereddict.py
new file mode 100644
index 0000000..e17269f
--- /dev/null
+++ b/ordereddict.py
@@ -0,0 +1,128 @@
+# Copyright (c) 2009 Raymond Hettinger
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation files
+# (the "Software"), to deal in the Software without restriction,
+# including without limitation the rights to use, copy, modify, merge,
+# publish, distribute, sublicense, and/or sell copies of the Software,
+# and to permit persons to whom the Software is furnished to do so,
+# subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+# OTHER DEALINGS IN THE SOFTWARE.
+
+from UserDict import DictMixin
+
+class OrderedDict(dict, DictMixin):
+
+ def __init__(self, *args, **kwds):
+ if len(args) > 1:
+ raise TypeError('expected at most 1 arguments, got %d' % len(args))
+ try:
+ self.__end
+ except AttributeError:
+ self.clear()
+ self.update(*args, **kwds)
+
+ def clear(self):
+ self.__end = end = []
+ end += [None, end, end] # sentinel node for doubly linked list
+ self.__map = {} # key --> [key, prev, next]
+ dict.clear(self)
+
+ def __setitem__(self, key, value):
+ if key not in self:
+ end = self.__end
+ curr = end[1]
+ curr[2] = end[1] = self.__map[key] = [key, curr, end]
+ dict.__setitem__(self, key, value)
+
+ def __delitem__(self, key):
+ dict.__delitem__(self, key)
+ key, prev, next = self.__map.pop(key)
+ prev[2] = next
+ next[1] = prev
+
+ def __iter__(self):
+ end = self.__end
+ curr = end[2]
+ while curr is not end:
+ yield curr[0]
+ curr = curr[2]
+
+ def __reversed__(self):
+ end = self.__end
+ curr = end[1]
+ while curr is not end:
+ yield curr[0]
+ curr = curr[1]
+
+ def popitem(self, last=True):
+ if not self:
+ raise KeyError('dictionary is empty')
+ if last:
+ key = reversed(self).next()
+ else:
+ key = iter(self).next()
+ value = self.pop(key)
+ return key, value
+
+ def __reduce__(self):
+ items = [[k, self[k]] for k in self]
+ tmp = self.__map, self.__end
+ del self.__map, self.__end
+ inst_dict = vars(self).copy()
+ self.__map, self.__end = tmp
+ if inst_dict:
+ return (self.__class__, (items,), inst_dict)
+ return self.__class__, (items,)
+
+ def keys(self):
+ return list(self)
+
+ setdefault = DictMixin.setdefault
+ update = DictMixin.update
+ pop = DictMixin.pop
+ values = DictMixin.values
+ items = DictMixin.items
+ iterkeys = DictMixin.iterkeys
+ itervalues = DictMixin.itervalues
+ iteritems = DictMixin.iteritems
+
+ def __repr__(self):
+ if not self:
+ return '%s()' % (self.__class__.__name__,)
+ return '%s(%r)' % (self.__class__.__name__, self.items())
+
+ def copy(self):
+ return self.__class__(self)
+
+ @classmethod
+ def fromkeys(cls, iterable, value=None):
+ d = cls()
+ for key in iterable:
+ d[key] = value
+ return d
+
+ def __eq__(self, other):
+ if isinstance(other, OrderedDict):
+ if len(self) != len(other):
+ return False
+ for p, q in zip(self.items(), other.items()):
+ if p != q:
+ return False
+ return True
+ return dict.__eq__(self, other)
+
+ def __ne__(self, other):
+ return not self == other
+
diff --git a/qmp-gen.py b/qmp-gen.py
new file mode 100644
index 0000000..0889fa3
--- /dev/null
+++ b/qmp-gen.py
@@ -0,0 +1,393 @@
+##
+# QAPI Code Generator
+#
+# Copyright IBM, Corp. 2011
+#
+# Authors:
+# Anthony Liguori <aliguori@us.ibm.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2. See
+# the COPYING file in the top-level directory.
+##
+import sys
+from ordereddict import OrderedDict
+
+enum_types = []
+event_types = {}
+
+def c_var(name):
+ return '_'.join(name.split('-'))
+
+def genindent(count):
+ ret = ""
+ for i in range(count):
+ ret += " "
+ return ret
+
+def is_dict(obj):
+ if type(obj) in [dict, OrderedDict]:
+ return True
+ return False
+
+def qmp_array_type_to_c(typename):
+ if type(typename) == list or is_dict(typename):
+ return qmp_type_to_c(typename)
+ elif typename == 'int':
+ return 'IntArray *'
+ elif typename == 'str':
+ return 'StringArray *'
+ elif typename == 'bool':
+ return 'BoolArray *'
+ elif typename == 'number':
+ return 'DoubleArray *'
+ else:
+ return qmp_type_to_c(typename)
+
+def qmp_type_should_free(typename):
+ if (type(typename) == list or
+ typename == 'str' or
+ (typename not in ['int', 'bool', 'number'] and
+ typename not in enum_types and not typename.isupper())):
+ return True
+ return False
+
+def qmp_free_func(typename):
+ if type(typename) == list:
+ return qmp_free_func(typename[0])
+ elif typename == 'str':
+ return 'qemu_free'
+ else:
+ return 'qmp_free_%s' % (de_camel_case(typename))
+
+def qmp_type_is_event(typename):
+ if type(typename) == str and typename.isupper():
+ return True
+ return False
+
+def qmp_type_to_c(typename, retval=False, indent=0):
+ if type(typename) == list:
+ return qmp_array_type_to_c(typename[0])
+ elif is_dict(typename):
+ string = 'struct {\n'
+ for key in typename:
+ name = key
+ if key.startswith('*'):
+ name = key[1:]
+ string += "%sbool has_%s;\n" % (genindent(indent + 4), c_var(name))
+ string += "%s%s %s;\n" % (genindent(indent + 4),
+ qmp_type_to_c(typename[key],
+ True,
+ indent=(indent + 4)),
+ c_var(name))
+ string += "%s}" % genindent(indent)
+ return string
+ elif typename == 'int':
+ return 'int64_t'
+ elif not retval and typename == 'str':
+ return 'const char *'
+ elif retval and typename == 'str':
+ return 'char *'
+ elif typename == 'bool':
+ return 'bool'
+ elif typename == 'number':
+ return 'double'
+ elif typename == 'none':
+ return 'void'
+ elif typename in enum_types:
+ return typename
+ elif qmp_type_is_event(typename):
+ return 'struct %s *' % qmp_event_to_c(typename)
+ else:
+ return 'struct %s *' % typename
+
+def qmp_type_to_qobj_ctor(typename):
+ return 'qmp_marshal_type_%s' % typename
+
+def qmp_type_from_qobj(typename):
+ return qobj_to_c(typename)
+
+def parse_args(typeinfo):
+ for member in typeinfo:
+ argname = member
+ argtype = typeinfo[member]
+ optional = False
+ if member.startswith('*'):
+ argname = member[1:]
+ optional = True
+ yield (argname, argtype, optional)
+
+def de_camel_case(name):
+ new_name = ''
+ for ch in name:
+ if ch.isupper() and new_name:
+ new_name += '_'
+ new_name += ch.lower()
+ return new_name
+
+def camel_case(name):
+ new_name = ''
+ first = True
+ for ch in name:
+ if ch == '_':
+ first = True
+ elif first:
+ new_name += ch.upper()
+ first = False
+ else:
+ new_name += ch.lower()
+ return new_name
+
+def qmp_event_to_c(name):
+ return '%sEvent' % camel_case(name)
+
+def qmp_event_func_to_c(name):
+ return '%sFunc' % camel_case(name)
+
+def inprint(string, indent):
+ print '%s%s' % (genindent(indent), string)
+
+def enum_abbreviation(name):
+ ab = ''
+ for ch in name:
+ if ch.isupper():
+ ab += ch
+ return ab
+
+def print_enum_declaration(name, entries):
+ print
+ print 'typedef enum %s {' % name
+ i = 0
+ for entry in entries:
+ print ' %s_%s = %d,' % (enum_abbreviation(name), entry.upper(), i)
+ i += 1
+ print '} %s;' % name
+ print
+ print '%s qmp_type_%s_from_str(const char *str, Error **errp);' % (name, de_camel_case(name))
+ print 'const char *qmp_type_%s_to_str(%s value, Error **errp);' % (de_camel_case(name), name)
+
+def print_enum_definition(name, entries):
+ print '''
+%s qmp_type_%s_from_str(const char *str, Error **errp)
+{''' % (name, de_camel_case(name))
+ first = True
+ for entry in entries:
+ if first:
+ print ' if (strcmp(str, "%s") == 0) {' % entry
+ first = False
+ else:
+ print ' } else if (strcmp(str, "%s") == 0) {' % entry
+ print ' return %s_%s;' % (enum_abbreviation(name), entry.upper())
+ print ''' } else {
+ error_set(errp, QERR_ENUM_VALUE_INVALID, "%s", str);
+ return %s_%s;
+ }
+}''' % (name, enum_abbreviation(name), entries[0].upper())
+
+ print '''
+const char *qmp_type_%s_to_str(%s value, Error **errp)
+{''' % (de_camel_case(name), name)
+ first = True
+ for entry in entries:
+ enum = '%s_%s' % (enum_abbreviation(name), entry.upper())
+ if first:
+ print ' if (value == %s) {' % enum
+ first = False
+ else:
+ print ' } else if (value == %s) {' % enum
+ print ' return "%s";' % entry
+ print ''' } else {
+ char buf[32];
+ snprintf(buf, sizeof(buf), "%%d", value);
+ error_set(errp, QERR_ENUM_VALUE_INVALID, "%s", buf);
+ return NULL;
+ }
+}''' % name
+
+def print_type_declaration(name, typeinfo):
+ if type(typeinfo) == str:
+ print
+ print "typedef %s %s;" % (qmp_type_to_c(typeinfo), name)
+ elif is_dict(typeinfo) and not name.isupper():
+ print
+ print "typedef struct %s %s;" % (name, name)
+ print "struct %s {" % name
+ for key in typeinfo:
+ member = key
+ if key.startswith('*'):
+ member = key[1:]
+ print " bool has_%s;" % c_var(member)
+ print " %s %s;" % (qmp_type_to_c(typeinfo[key], True, indent=4), c_var(member))
+ print " %s *next;" % c_var(name)
+ print "};"
+ print
+ print "%s *qmp_alloc_%s(void);" % (name, de_camel_case(name))
+ print "void qmp_free_%s(%s *obj);" % (de_camel_case(name), name)
+ elif is_dict(typeinfo) and name.isupper():
+ arglist = ['void *opaque']
+ for argname, argtype, optional in parse_args(typeinfo):
+ arglist.append('%s %s' % (qmp_type_to_c(argtype), argname))
+ print
+ print 'typedef void (%s)(%s);' % (qmp_event_func_to_c(name), ', '.join(arglist))
+ print
+ print 'typedef struct %s {' % qmp_event_to_c(name)
+ print ' QmpSignal *signal;'
+ print ' %s *func;' % qmp_event_func_to_c(name)
+ print '} %s;' % qmp_event_to_c(name)
+
+def print_type_free(typeinfo, prefix, indent=4):
+ for argname, argtype, optional in parse_args(typeinfo):
+ if type(argtype) == list:
+ argtype = argtype[0]
+
+ if is_dict(argtype):
+ if optional:
+ inprint('if (%shas_%s) {' % (prefix, argname), indent)
+ print_type_free(argtype, '%s%s.' % (prefix, argname), indent + 4)
+ inprint('}', indent)
+ else:
+ print_type_free(argtype, '%s%s.' % (prefix, argname), indent)
+ elif qmp_type_should_free(argtype):
+ if optional:
+ inprint('if (%shas_%s) {' % (prefix, argname), indent)
+ inprint(' %s(%s%s);' % (qmp_free_func(argtype), prefix, argname), indent)
+ inprint('}', indent)
+ else:
+ inprint('%s(%s%s);' % (qmp_free_func(argtype), prefix, argname), indent)
+
+def print_type_definition(name, typeinfo):
+ if qmp_type_is_event(name):
+ return
+
+ c_var_name = de_camel_case(name)
+
+ print '''
+void qmp_free_%s(%s *obj)
+{
+ if (!obj) {
+ return;
+ }''' % (c_var_name, name)
+
+ print_type_free(typeinfo, 'obj->')
+
+ print '''
+ %s(obj->next);
+ qemu_free(obj);
+}''' % (qmp_free_func(name))
+
+ print '''
+%s *qmp_alloc_%s(void)
+{
+ BUILD_ASSERT(sizeof(%s) < 512);
+ return qemu_mallocz(512);
+}''' % (name, c_var_name, name)
+
+def tokenize(data):
+ while len(data):
+ if data[0] in ['{', '}', ':', ',', '[', ']']:
+ yield data[0]
+ data = data[1:]
+ elif data[0] in ' \n':
+ data = data[1:]
+ elif data[0] == "'":
+ data = data[1:]
+ string = ''
+ while data[0] != "'":
+ string += data[0]
+ data = data[1:]
+ data = data[1:]
+ yield string
+
+def parse_value(tokens):
+ if tokens[0] == '{':
+ ret = OrderedDict()
+ tokens = tokens[1:]
+ while tokens[0] != '}':
+ key = tokens[0]
+ tokens = tokens[1:]
+
+ tokens = tokens[1:] # :
+
+ value, tokens = parse_value(tokens)
+
+ if tokens[0] == ',':
+ tokens = tokens[1:]
+
+ ret[key] = value
+ tokens = tokens[1:]
+ return ret, tokens
+ elif tokens[0] == '[':
+ ret = []
+ tokens = tokens[1:]
+ while tokens[0] != ']':
+ value, tokens = parse_value(tokens)
+ if tokens[0] == ',':
+ tokens = tokens[1:]
+ ret.append(value)
+ tokens = tokens[1:]
+ return ret, tokens
+ else:
+ return tokens[0], tokens[1:]
+
+def ordered_eval(string):
+ return parse_value(map(lambda x: x, tokenize(string)))[0]
+
+if len(sys.argv) == 2:
+ if sys.argv[1] == '--types-body':
+ kind = 'types-body'
+ elif sys.argv[1] == '--types-header':
+ kind = 'types-header'
+
+if kind == 'types-header':
+ print '''/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT EDIT */
+#ifndef QMP_TYPES_H
+#define QMP_TYPES_H
+
+#include "qmp-types-core.h"
+
+'''
+elif kind == 'types-body':
+ print '''/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT EDIT */
+
+#include "qmp-types.h"
+#include "qemu-common.h"
+'''
+
+exprs = []
+expr = ''
+
+for line in sys.stdin:
+ if line.startswith('#') or line == '\n':
+ continue
+
+ if line.startswith(' '):
+ expr += line
+ elif expr:
+ s = ordered_eval(expr)
+ exprs.append(s)
+ expr = line
+ else:
+ expr += line
+
+if expr:
+ s = ordered_eval(expr)
+ exprs.append(s)
+
+for s in exprs:
+ if is_dict(s):
+ key = s.keys()[0]
+ if is_dict(s[key]):
+ if qmp_type_is_event(key):
+ event_types[key] = s[key]
+ if kind == 'types-body':
+ print_type_definition(key, s[key])
+ elif kind == 'types-header':
+ print_type_declaration(key, s[key])
+ else:
+ enum_types.append(key)
+ if kind == 'types-header':
+ print_enum_declaration(key, s[key])
+ elif kind == 'types-body':
+ print_enum_definition(key, s[key])
+
+if kind.endswith('header'):
+ print '#endif'
diff --git a/qmp-schema.json b/qmp-schema.json
new file mode 100644
index 0000000..e69de29
diff --git a/qmp-types-core.h b/qmp-types-core.h
new file mode 100644
index 0000000..dbf1fd8
--- /dev/null
+++ b/qmp-types-core.h
@@ -0,0 +1,27 @@
+/*
+ * 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 QMP_TYPES_CORE_H
+#define QMP_TYPES_CORE_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "error.h"
+
+typedef struct QmpSignal QmpSignal;
+
+#define BUILD_ASSERT(cond) do { \
+ (void)sizeof(int[-1+!!(cond)]); \
+} while (0)
+
+#define BUILD_BUG() BUILD_ASSERT(0)
+
+#endif
--
1.7.0.4
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [Qemu-devel] [PATCH 09/22] qapi: add code generator for type marshallers
2011-03-07 1:22 [Qemu-devel] [PATCH 00/22] QAPI Round 1 Anthony Liguori
` (7 preceding siblings ...)
2011-03-07 1:22 ` [Qemu-devel] [PATCH 08/22] qapi: add code generator for qmp-types Anthony Liguori
@ 2011-03-07 1:22 ` Anthony Liguori
2011-03-07 1:22 ` [Qemu-devel] [PATCH 10/22] qapi: add core QMP server support Anthony Liguori
` (17 subsequent siblings)
26 siblings, 0 replies; 106+ messages in thread
From: Anthony Liguori @ 2011-03-07 1:22 UTC (permalink / raw)
To: qemu-devel
Cc: Adam Litke, Anthony Liguori, Markus Armbruster, Luiz Capitulino
This code will marshal a QMP type from native representation to a QObject and
vice versa.
Marshaling from a native representation to a QObject can never generate an
Error although the opposite direction may.
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
diff --git a/Makefile b/Makefile
index 6b9fd69..8f3a4d3 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
+GENERATED_HEADERS += qmp-types.h qmp-marshal-types.h
ifneq ($(wildcard config-host.mak),)
# Put the all: rule here so that config-host.mak can contain dependencies.
@@ -153,7 +153,14 @@ qmp-types.c: $(SRC_PATH)/qmp-schema.json $(SRC_PATH)/qmp-gen.py
qmp-types.h: $(SRC_PATH)/qmp-schema.json $(SRC_PATH)/qmp-gen.py
$(call quiet-command,python $(SRC_PATH)/qmp-gen.py --types-header < $< > $@, " GEN $@")
+qmp-marshal-types.c: $(SRC_PATH)/qmp-schema.json $(SRC_PATH)/qmp-gen.py
+ $(call quiet-command,python $(SRC_PATH)/qmp-gen.py --marshal-body < $< > $@, " GEN $@")
+
+qmp-marshal-types.h: $(SRC_PATH)/qmp-schema.json $(SRC_PATH)/qmp-gen.py
+ $(call quiet-command,python $(SRC_PATH)/qmp-gen.py --marshal-header < $< > $@, " 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
version.o: $(SRC_PATH)/version.rc config-host.mak
$(call quiet-command,$(WINDRES) -I. -o $@ $<," RC $(TARGET_DIR)$@")
diff --git a/Makefile.objs b/Makefile.objs
index 710d99f..f51eab3 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -102,6 +102,7 @@ common-obj-y += qdev.o qdev-properties.o
common-obj-y += block-migration.o
common-obj-y += pflib.o
common-obj-y += bitmap.o bitops.o
+common-obj-y += qmp-marshal-types.o qmp-marshal-types-core.o
common-obj-$(CONFIG_BRLAPI) += baum.o
common-obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o
diff --git a/qmp-gen.py b/qmp-gen.py
index 0889fa3..8adcc2b 100644
--- a/qmp-gen.py
+++ b/qmp-gen.py
@@ -281,6 +281,154 @@ void qmp_free_%s(%s *obj)
return qemu_mallocz(512);
}''' % (name, c_var_name, name)
+def print_metatype_def(typeinfo, name, lhs, indent=0):
+ if indent == 0:
+ sep = '->'
+ else:
+ sep = '.'
+ new_lhs = 'qmp__member%d' % (indent / 4)
+
+ if type(typeinfo) == str:
+ inprint(' %s = %s(%s);' % (lhs, qmp_type_to_qobj_ctor(typeinfo), name), indent)
+ elif is_dict(typeinfo):
+ inprint(' {', indent)
+ inprint(' QDict *qmp__dict = qdict_new();', indent)
+ inprint(' QObject *%s;' % new_lhs, indent)
+ print
+ for key in typeinfo:
+ member = key
+ if key.startswith('*'):
+ member = key[1:]
+ inprint(' if (%s%shas_%s) {' % (name, sep, c_var(member)), indent)
+ print_metatype_def(typeinfo[key], '%s%s%s' % (name, sep, c_var(member)), new_lhs, indent + 8)
+ inprint(' qdict_put_obj(qmp__dict, "%s", %s);' % (member, new_lhs), indent)
+ inprint(' }', indent)
+ else:
+ print_metatype_def(typeinfo[key], '%s%s%s' % (name, sep, c_var(member)), new_lhs, indent + 4)
+ inprint(' qdict_put_obj(qmp__dict, "%s", %s);' % (member, new_lhs), indent)
+ print
+ inprint(' %s = QOBJECT(qmp__dict);' % lhs, indent)
+ inprint(' }', indent)
+ elif type(typeinfo) == list:
+ inprint(' {', indent)
+ inprint(' QList *qmp__list = qlist_new();', indent)
+ inprint(' %s %s_i;' % (qmp_type_to_c(typeinfo[0], True), new_lhs), indent)
+ print
+ inprint(' for (%s_i = %s; %s_i != NULL; %s_i = %s_i->next) {' % (new_lhs, name, new_lhs, new_lhs, new_lhs), indent)
+ inprint(' QObject *qmp__member = %s(%s_i);' % (qmp_type_to_qobj_ctor(typeinfo[0]), new_lhs), indent)
+ inprint(' qlist_append_obj(qmp__list, qmp__member);', indent)
+ inprint(' }', indent)
+ inprint(' %s = QOBJECT(qmp__list);' % lhs, indent)
+ inprint(' }', indent)
+
+def qobj_to_c(typename):
+ return 'qmp_unmarshal_type_%s' % typename
+
+def print_metatype_undef(typeinfo, name, lhs, indent=0):
+ if indent == 0:
+ sep = '->'
+ else:
+ sep = '.'
+
+ indent += 4
+
+ if type(typeinfo) == str:
+ inprint('%s = %s(%s, &qmp__err);' % (lhs, qobj_to_c(typeinfo), name), indent)
+ inprint('if (qmp__err) {', indent)
+ inprint(' goto qmp__err_out;', indent)
+ inprint('}', indent)
+ elif is_dict(typeinfo):
+ objname = 'qmp__object%d' % ((indent - 4) / 4)
+ inprint('{', indent)
+ inprint(' QDict *qmp__dict = qobject_to_qdict(%s);' % c_var(name), indent)
+ inprint(' QObject *%s;' % objname, indent)
+ for key in typeinfo:
+ member = key
+ optional = False
+ if key.startswith('*'):
+ member = key[1:]
+ optional = True
+ if optional:
+ inprint('if (qdict_haskey(qmp__dict, "%s")) {' % (member), indent + 4)
+ inprint(' %s = qdict_get(qmp__dict, "%s");' % (objname, member), indent + 4)
+ inprint(' %s%shas_%s = true;' % (lhs, sep, c_var(member)), indent + 4)
+ print_metatype_undef(typeinfo[key], objname, '%s%s%s' % (lhs, sep, c_var(member)), indent + 4)
+ inprint('} else {', indent + 4)
+ inprint(' %s%shas_%s = false;' % (lhs, sep, c_var(member)), indent + 4)
+ inprint('}', indent + 4)
+ else:
+ inprint('%s = qdict_get(qmp__dict, "%s");' % (objname, key), indent + 4)
+ print_metatype_undef(typeinfo[key], objname, '%s%s%s' % (lhs, sep, c_var(member)), indent)
+ inprint('}', indent)
+ elif type(typeinfo) == list:
+ objname = 'qmp__object%d' % ((indent - 4) / 4)
+ inprint('{', indent)
+ inprint(' QList *qmp__list = qobject_to_qlist(%s);' % c_var(name), indent)
+ inprint(' QListEntry *%s;' % objname, indent)
+ inprint(' QLIST_FOREACH_ENTRY(qmp__list, %s) {' % objname, indent)
+ inprint(' %s qmp__node = %s(%s->value, &qmp__err);' % (qmp_type_to_c(typeinfo[0], True), qmp_type_from_qobj(typeinfo[0]), objname), indent)
+ inprint(' if (qmp__err) {', indent)
+ inprint(' goto qmp__err_out;', indent)
+ inprint(' }', indent)
+ inprint(' qmp__node->next = %s;' % lhs, indent)
+ inprint(' %s = qmp__node;' % lhs, indent)
+ inprint(' }', indent)
+ inprint('}', indent)
+
+def print_type_marshal_definition(name, typeinfo):
+ c_var_name = de_camel_case(name)
+ if qmp_type_is_event(name):
+ return
+
+ print '''
+QObject *qmp_marshal_type_%s(%s src)
+{''' % (name, qmp_type_to_c(name))
+ print ' QObject *qmp__retval;'
+ print_metatype_def(typeinfo, 'src', 'qmp__retval')
+ print ''' return qmp__retval;
+}
+
+%s qmp_unmarshal_type_%s(QObject *src, Error **errp)
+{''' % (qmp_type_to_c(name), name)
+ print ' Error *qmp__err = NULL;'
+ print ' %s qmp__retval = qmp_alloc_%s();' % (qmp_type_to_c(name), c_var_name)
+ print_metatype_undef(typeinfo, 'src', 'qmp__retval')
+ print ''' return qmp__retval;
+qmp__err_out:
+ error_propagate(errp, qmp__err);
+ %s(qmp__retval);
+ return NULL;
+}''' % (qmp_free_func(name))
+
+def print_type_marshal_declaration(name, typeinfo):
+ if qmp_type_is_event(name):
+ return
+
+ if is_dict(typeinfo):
+ print
+ print 'QObject *qmp_marshal_type_%s(%s src);' % (name, qmp_type_to_c(name))
+ print '%s qmp_unmarshal_type_%s(QObject *src, Error **errp);' % (qmp_type_to_c(name), name)
+
+def print_enum_marshal_declaration(name, entries):
+ print
+ print 'QObject *qmp_marshal_type_%s(%s value);' % (name, name)
+ print '%s qmp_unmarshal_type_%s(QObject *obj, Error **errp);' % (name, name)
+
+def print_enum_marshal_definition(name, entries):
+ print '''
+QObject *qmp_marshal_type_%s(%s value)
+{
+ return QOBJECT(qint_from_int(value));
+}
+''' % (name, name)
+
+ print '''
+%s qmp_unmarshal_type_%s(QObject *obj, Error **errp)
+{
+ return (%s)qint_get_int(qobject_to_qint(obj));
+}
+''' % (name, name, name)
+
def tokenize(data):
while len(data):
if data[0] in ['{', '}', ':', ',', '[', ']']:
@@ -336,6 +484,10 @@ if len(sys.argv) == 2:
kind = 'types-body'
elif sys.argv[1] == '--types-header':
kind = 'types-header'
+ elif sys.argv[1] == '--marshal-body':
+ kind = 'marshal-body'
+ elif sys.argv[1] == '--marshal-header':
+ kind = 'marshal-header'
if kind == 'types-header':
print '''/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT EDIT */
@@ -351,6 +503,20 @@ elif kind == 'types-body':
#include "qmp-types.h"
#include "qemu-common.h"
'''
+elif kind == 'marshal-header':
+ print '''/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT EDIT */
+#ifndef QMP_MARSHAL_TYPES_H
+#define QMP_MARSHAL_TYPES_H
+
+#include "qmp-marshal-types-core.h"
+
+'''
+elif kind == 'marshal-body':
+ print '''/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT EDIT */
+
+#include "qmp-marshal-types.h"
+#include "qerror.h"
+'''
exprs = []
expr = ''
@@ -382,12 +548,20 @@ for s in exprs:
print_type_definition(key, s[key])
elif kind == 'types-header':
print_type_declaration(key, s[key])
+ elif kind == 'marshal-body':
+ print_type_marshal_definition(key, s[key])
+ elif kind == 'marshal-header':
+ print_type_marshal_declaration(key, s[key])
else:
enum_types.append(key)
if kind == 'types-header':
print_enum_declaration(key, s[key])
elif kind == 'types-body':
print_enum_definition(key, s[key])
+ elif kind == 'marshal-header':
+ print_enum_marshal_declaration(key, s[key])
+ elif kind == 'marshal-body':
+ print_enum_marshal_definition(key, s[key])
if kind.endswith('header'):
print '#endif'
diff --git a/qmp-marshal-types-core.c b/qmp-marshal-types-core.c
new file mode 100644
index 0000000..6a55733
--- /dev/null
+++ b/qmp-marshal-types-core.c
@@ -0,0 +1,71 @@
+/*
+ * 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 "qmp-marshal-types-core.h"
+#include "qerror.h"
+
+QObject *qmp_marshal_type_int(int64_t value)
+{
+ return QOBJECT(qint_from_int(value));
+}
+
+QObject *qmp_marshal_type_str(const char *value)
+{
+ return QOBJECT(qstring_from_str(value));
+}
+
+QObject *qmp_marshal_type_bool(bool value)
+{
+ return QOBJECT(qbool_from_int(value));
+}
+
+QObject *qmp_marshal_type_number(double value)
+{
+ return QOBJECT(qfloat_from_double(value));
+}
+
+int64_t qmp_unmarshal_type_int(QObject *value, Error **errp)
+{
+ if (qobject_type(value) != QTYPE_QINT) {
+ error_set(errp, QERR_INVALID_PARAMETER_TYPE, "<unknown>", "int");
+ return 0;
+ }
+ return qint_get_int(qobject_to_qint(value));
+}
+
+char *qmp_unmarshal_type_str(QObject *value, Error **errp)
+{
+ if (qobject_type(value) != QTYPE_QSTRING) {
+ error_set(errp, QERR_INVALID_PARAMETER_TYPE, "<unknown>", "string");
+ return 0;
+ }
+ return qemu_strdup(qstring_get_str(qobject_to_qstring(value)));
+}
+
+bool qmp_unmarshal_type_bool(QObject *value, Error **errp)
+{
+ if (qobject_type(value) != QTYPE_QBOOL) {
+ error_set(errp, QERR_INVALID_PARAMETER_TYPE, "<unknown>", "bool");
+ return 0;
+ }
+ return qbool_get_int(qobject_to_qbool(value));
+}
+
+double qmp_unmarshal_type_number(QObject *value, Error **errp)
+{
+ if (qobject_type(value) != QTYPE_QFLOAT) {
+ error_set(errp, QERR_INVALID_PARAMETER_TYPE, "<unknown>", "float");
+ return 0;
+ }
+ return qfloat_get_double(qobject_to_qfloat(value));
+}
+
+
diff --git a/qmp-marshal-types-core.h b/qmp-marshal-types-core.h
new file mode 100644
index 0000000..38fe696
--- /dev/null
+++ b/qmp-marshal-types-core.h
@@ -0,0 +1,31 @@
+/*
+ * 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 QMP_MARSHAL_TYPES_CORE_H
+#define QMP_MARSHAL_TYPES_CORE_H
+
+#include "qemu-common.h"
+#include "qemu-objects.h"
+#include "qerror.h"
+#include "error.h"
+#include "qmp-types.h"
+
+QObject *qmp_marshal_type_int(int64_t value);
+QObject *qmp_marshal_type_str(const char *value);
+QObject *qmp_marshal_type_bool(bool value);
+QObject *qmp_marshal_type_number(double value);
+
+int64_t qmp_unmarshal_type_int(QObject *value, Error **errp);
+char *qmp_unmarshal_type_str(QObject *value, Error **errp);
+bool qmp_unmarshal_type_bool(QObject *value, Error **errp);
+double qmp_unmarshal_type_number(QObject *value, Error **errp);
+
+#endif
--
1.7.0.4
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [Qemu-devel] [PATCH 10/22] qapi: add core QMP server support
2011-03-07 1:22 [Qemu-devel] [PATCH 00/22] QAPI Round 1 Anthony Liguori
` (8 preceding siblings ...)
2011-03-07 1:22 ` [Qemu-devel] [PATCH 09/22] qapi: add code generator for type marshallers Anthony Liguori
@ 2011-03-07 1:22 ` Anthony Liguori
2011-03-07 13:09 ` Stefan Hajnoczi
2011-03-07 1:22 ` [Qemu-devel] [PATCH 11/22] qapi: add signal support to core QMP server Anthony Liguori
` (16 subsequent siblings)
26 siblings, 1 reply; 106+ messages in thread
From: Anthony Liguori @ 2011-03-07 1:22 UTC (permalink / raw)
To: qemu-devel
Cc: Adam Litke, Anthony Liguori, Markus Armbruster, Luiz Capitulino
This is the infrastructure to register commands.
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
diff --git a/Makefile.objs b/Makefile.objs
index f51eab3..dbdce3c 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -103,6 +103,7 @@ common-obj-y += block-migration.o
common-obj-y += pflib.o
common-obj-y += bitmap.o bitops.o
common-obj-y += qmp-marshal-types.o qmp-marshal-types-core.o
+common-obj-y += qmp-core.o
common-obj-$(CONFIG_BRLAPI) += baum.o
common-obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o
diff --git a/qmp-core.c b/qmp-core.c
new file mode 100644
index 0000000..78aef3a
--- /dev/null
+++ b/qmp-core.c
@@ -0,0 +1,77 @@
+/*
+ * 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 "qmp.h"
+#include "qmp-core.h"
+#include "json-lexer.h"
+#include "json-parser.h"
+#include "json-streamer.h"
+#include "qemu_socket.h"
+#include <glib.h>
+#include "qemu-queue.h"
+#include "sysemu.h"
+
+typedef struct QmpCommand
+{
+ const char *name;
+ bool stateful;
+ QmpCommandFunc *fn;
+ QmpStatefulCommandFunc *sfn;
+ QTAILQ_ENTRY(QmpCommand) node;
+} QmpCommand;
+
+static QTAILQ_HEAD(, QmpCommand) qmp_commands =
+ QTAILQ_HEAD_INITIALIZER(qmp_commands);
+
+void qmp_register_command(const char *name, QmpCommandFunc *fn)
+{
+ QmpCommand *cmd = qemu_mallocz(sizeof(*cmd));
+
+ cmd->name = name;
+ cmd->stateful = false;
+ cmd->fn = fn;
+ QTAILQ_INSERT_TAIL(&qmp_commands, cmd, node);
+}
+
+void qmp_register_stateful_command(const char *name, QmpStatefulCommandFunc *fn)
+{
+ QmpCommand *cmd = qemu_mallocz(sizeof(*cmd));
+
+ cmd->name = name;
+ cmd->stateful = true;
+ cmd->sfn = fn;
+ QTAILQ_INSERT_TAIL(&qmp_commands, cmd, node);
+}
+
+char *qobject_as_string(QObject *obj)
+{
+ char buffer[1024];
+
+ switch (qobject_type(obj)) {
+ case QTYPE_QINT:
+ snprintf(buffer, sizeof(buffer), "%" PRId64,
+ qint_get_int(qobject_to_qint(obj)));
+ return qemu_strdup(buffer);
+ case QTYPE_QSTRING:
+ return qemu_strdup(qstring_get_str(qobject_to_qstring(obj)));
+ case QTYPE_QFLOAT:
+ snprintf(buffer, sizeof(buffer), "%.17g",
+ qfloat_get_double(qobject_to_qfloat(obj)));
+ return qemu_strdup(buffer);
+ case QTYPE_QBOOL:
+ if (qbool_get_int(qobject_to_qbool(obj))) {
+ return qemu_strdup("on");
+ }
+ return qemu_strdup("off");
+ default:
+ return NULL;
+ }
+}
diff --git a/qmp-core.h b/qmp-core.h
new file mode 100644
index 0000000..e3235ec
--- /dev/null
+++ b/qmp-core.h
@@ -0,0 +1,30 @@
+/*
+ * 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 QMP_CORE_H
+#define QMP_CORE_H
+
+#include "monitor.h"
+#include "qmp-marshal-types.h"
+#include "error_int.h"
+
+typedef struct QmpState QmpState;
+
+typedef void (QmpCommandFunc)(const QDict *, QObject **, Error **);
+typedef void (QmpStatefulCommandFunc)(QmpState *qmp__sess, const QDict *, QObject **, Error **);
+
+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);
+
+#endif
--
1.7.0.4
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [Qemu-devel] [PATCH 11/22] qapi: add signal support to core QMP server
2011-03-07 1:22 [Qemu-devel] [PATCH 00/22] QAPI Round 1 Anthony Liguori
` (9 preceding siblings ...)
2011-03-07 1:22 ` [Qemu-devel] [PATCH 10/22] qapi: add core QMP server support Anthony Liguori
@ 2011-03-07 1:22 ` Anthony Liguori
2011-03-07 13:21 ` Stefan Hajnoczi
2011-03-07 1:22 ` [Qemu-devel] [PATCH 12/22] qapi: add QAPI module type Anthony Liguori
` (15 subsequent siblings)
26 siblings, 1 reply; 106+ messages in thread
From: Anthony Liguori @ 2011-03-07 1:22 UTC (permalink / raw)
To: qemu-devel
Cc: Adam Litke, Anthony Liguori, Markus Armbruster, Luiz Capitulino
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
diff --git a/qmp-core.c b/qmp-core.c
index 78aef3a..3a6242c 100644
--- a/qmp-core.c
+++ b/qmp-core.c
@@ -28,6 +28,22 @@ typedef struct QmpCommand
QTAILQ_ENTRY(QmpCommand) node;
} QmpCommand;
+typedef struct DefaultQmpConnection
+{
+ QmpSignal *obj;
+ int handle;
+ QTAILQ_ENTRY(DefaultQmpConnection) node;
+} DefaultQmpConnection;
+
+struct QmpState
+{
+ int (*add_connection)(QmpState *s, QmpConnection *conn);
+ void (*del_connection)(QmpState *s, int global_handle, Error **errp);
+ void (*event)(QmpState *s, QObject *data);
+
+ QTAILQ_HEAD(, DefaultQmpConnection) default_connections;
+};
+
static QTAILQ_HEAD(, QmpCommand) qmp_commands =
QTAILQ_HEAD_INITIALIZER(qmp_commands);
@@ -75,3 +91,90 @@ char *qobject_as_string(QObject *obj)
return NULL;
}
}
+
+void qmp_state_add_connection(QmpState *sess, const char *event_name, QmpSignal *obj, int handle, QmpConnection *conn)
+{
+ conn->state = sess;
+ conn->event_name = event_name;
+ conn->signal = obj;
+ conn->handle = handle;
+ conn->global_handle = sess->add_connection(sess, conn);
+}
+
+void qmp_state_del_connection(QmpState *sess, int global_handle, Error **errp)
+{
+ sess->del_connection(sess, global_handle, errp);
+}
+
+void qmp_state_event(QmpConnection *conn, QObject *data)
+{
+ QDict *event = qdict_new();
+ qemu_timeval tv;
+ QObject *ts;
+
+ qemu_gettimeofday(&tv);
+
+ ts = qobject_from_jsonf("{ 'seconds': %" PRId64 ", "
+ "'microseconds': %" PRId64 " }",
+ (int64_t)tv.tv_sec, (int64_t)tv.tv_usec);
+ qdict_put_obj(event, "timestamp", ts);
+
+ qdict_put(event, "event", qstring_from_str(conn->event_name));
+ if (data) {
+ qobject_incref(data);
+ qdict_put_obj(event, "data", data);
+ }
+
+ qdict_put(event, "tag", qint_from_int(conn->global_handle));
+
+ conn->state->event(conn->state, QOBJECT(event));
+ QDECREF(event);
+}
+
+QmpSignal *qmp_signal_init(void)
+{
+ QmpSignal *obj = qemu_mallocz(sizeof(*obj));
+ obj->max_handle = 0;
+ obj->ref = 1;
+ QTAILQ_INIT(&obj->slots);
+ return obj;
+}
+
+void qmp_signal_ref(QmpSignal *obj)
+{
+ obj->ref++;
+}
+
+void qmp_signal_unref(QmpSignal *obj)
+{
+ if (--obj->ref) {
+ qemu_free(obj);
+ }
+}
+
+int qmp_signal_connect(QmpSignal *obj, void *func, void *opaque)
+{
+ int handle = ++obj->max_handle;
+ QmpSlot *slot = qemu_mallocz(sizeof(*slot));
+
+ slot->handle = handle;
+ slot->func = func;
+ slot->opaque = opaque;
+
+ QTAILQ_INSERT_TAIL(&obj->slots, slot, node);
+
+ return handle;
+}
+
+void qmp_signal_disconnect(QmpSignal *obj, int handle)
+{
+ QmpSlot *slot;
+
+ QTAILQ_FOREACH(slot, &obj->slots, node) {
+ if (slot->handle == handle) {
+ QTAILQ_REMOVE(&obj->slots, slot, node);
+ qemu_free(slot);
+ break;
+ }
+ }
+}
diff --git a/qmp-core.h b/qmp-core.h
index e3235ec..5ce02f7 100644
--- a/qmp-core.h
+++ b/qmp-core.h
@@ -21,10 +21,65 @@ typedef struct QmpState QmpState;
typedef void (QmpCommandFunc)(const QDict *, QObject **, Error **);
typedef void (QmpStatefulCommandFunc)(QmpState *qmp__sess, const QDict *, QObject **, Error **);
+typedef struct QmpSlot
+{
+ int handle;
+ void *func;
+ void *opaque;
+ QTAILQ_ENTRY(QmpSlot) node;
+} QmpSlot;
+
+struct QmpSignal
+{
+ int max_handle;
+ int ref;
+ QTAILQ_HEAD(, QmpSlot) slots;
+};
+
+typedef struct QmpConnection
+{
+ QmpState *state;
+ const char *event_name;
+ QmpSignal *signal;
+ int handle;
+ int global_handle;
+ QTAILQ_ENTRY(QmpConnection) node;
+} 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);
+void qmp_signal_ref(QmpSignal *obj);
+void qmp_signal_unref(QmpSignal *obj);
+int qmp_signal_connect(QmpSignal *obj, void *func, void *opaque);
+void qmp_signal_disconnect(QmpSignal *obj, int handle);
+
+void qmp_state_add_connection(QmpState *sess, const char *name, QmpSignal *obj, int handle, QmpConnection *conn);
+void qmp_state_del_connection(QmpState *sess, int global_handle, Error **errp);
+void qmp_state_event(QmpConnection *conn, QObject *data);
+
+#define signal_init(obj) do { \
+ (obj)->signal = qmp_signal_init(); \
+} while (0)
+
+#define signal_unref(obj) qmp_signal_unref((obj)->signal)
+
+#define signal_connect(obj, fn, opaque) \
+ qmp_signal_connect((obj)->signal, (obj)->func = fn, opaque)
+
+#define signal_disconnect(obj, handle) \
+ qmp_signal_disconnect((obj)->signal, handle)
+
+#define signal_notify(obj, ...) do { \
+ QmpSlot *qmp__slot; \
+ QTAILQ_FOREACH(qmp__slot, &(obj)->signal->slots, node) { \
+ (obj)->func = qmp__slot->func; \
+ (obj)->func(qmp__slot->opaque, ## __VA_ARGS__); \
+ } \
+} while(0)
+
#endif
--
1.7.0.4
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [Qemu-devel] [PATCH 12/22] qapi: add QAPI module type
2011-03-07 1:22 [Qemu-devel] [PATCH 00/22] QAPI Round 1 Anthony Liguori
` (10 preceding siblings ...)
2011-03-07 1:22 ` [Qemu-devel] [PATCH 11/22] qapi: add signal support to core QMP server Anthony Liguori
@ 2011-03-07 1:22 ` Anthony Liguori
2011-03-07 1:22 ` [Qemu-devel] [PATCH 13/22] qapi: add code generators for QMP command marshaling Anthony Liguori
` (14 subsequent siblings)
26 siblings, 0 replies; 106+ messages in thread
From: Anthony Liguori @ 2011-03-07 1:22 UTC (permalink / raw)
To: qemu-devel
Cc: Adam Litke, Anthony Liguori, Markus Armbruster, Luiz Capitulino
This lets us register marshaling handlers using a module init function.
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
diff --git a/module.h b/module.h
index 9263f1c..ef66730 100644
--- a/module.h
+++ b/module.h
@@ -24,12 +24,14 @@ typedef enum {
MODULE_INIT_BLOCK,
MODULE_INIT_DEVICE,
MODULE_INIT_MACHINE,
+ MODULE_INIT_QAPI,
MODULE_INIT_MAX
} module_init_type;
#define block_init(function) module_init(function, MODULE_INIT_BLOCK)
#define device_init(function) module_init(function, MODULE_INIT_DEVICE)
#define machine_init(function) module_init(function, MODULE_INIT_MACHINE)
+#define qapi_init(function) module_init(function, MODULE_INIT_QAPI)
void register_module_init(void (*fn)(void), module_init_type type);
diff --git a/vl.c b/vl.c
index b436952..68c6715 100644
--- a/vl.c
+++ b/vl.c
@@ -1966,6 +1966,8 @@ int main(int argc, char **argv, char **envp)
cyls = heads = secs = 0;
translation = BIOS_ATA_TRANSLATION_AUTO;
+ module_call_init(MODULE_INIT_QAPI);
+
for (i = 0; i < MAX_NODES; i++) {
node_mem[i] = 0;
node_cpumask[i] = 0;
--
1.7.0.4
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [Qemu-devel] [PATCH 13/22] qapi: add code generators for QMP command marshaling
2011-03-07 1:22 [Qemu-devel] [PATCH 00/22] QAPI Round 1 Anthony Liguori
` (11 preceding siblings ...)
2011-03-07 1:22 ` [Qemu-devel] [PATCH 12/22] qapi: add QAPI module type Anthony Liguori
@ 2011-03-07 1:22 ` Anthony Liguori
2011-03-07 13:27 ` Stefan Hajnoczi
2011-03-07 1:22 ` [Qemu-devel] [PATCH 14/22] qapi: add query-version QMP command Anthony Liguori
` (13 subsequent siblings)
26 siblings, 1 reply; 106+ messages in thread
From: Anthony Liguori @ 2011-03-07 1:22 UTC (permalink / raw)
To: qemu-devel
Cc: Adam Litke, Anthony Liguori, Markus Armbruster, Luiz Capitulino
This generates qmp.h which contains the declarations of all of QMP functions
to be dispatched, plus a function that registers marshallers for each of
the QMP functions.
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
diff --git a/Makefile b/Makefile
index 8f3a4d3..47a755d 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
+GENERATED_HEADERS += qmp-types.h qmp-marshal-types.h qmp.h
ifneq ($(wildcard config-host.mak),)
# Put the all: rule here so that config-host.mak can contain dependencies.
@@ -159,8 +159,15 @@ qmp-marshal-types.c: $(SRC_PATH)/qmp-schema.json $(SRC_PATH)/qmp-gen.py
qmp-marshal-types.h: $(SRC_PATH)/qmp-schema.json $(SRC_PATH)/qmp-gen.py
$(call quiet-command,python $(SRC_PATH)/qmp-gen.py --marshal-header < $< > $@, " GEN $@")
+qmp.h: $(SRC_PATH)/qmp-schema.json $(SRC_PATH)/qmp-gen.py
+ $(call quiet-command,python $(SRC_PATH)/qmp-gen.py --header < $< > $@, " GEN $@")
+
+qmp-marshal.c: $(SRC_PATH)/qmp-schema.json $(SRC_PATH)/qmp-gen.py
+ $(call quiet-command,python $(SRC_PATH)/qmp-gen.py --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
version.o: $(SRC_PATH)/version.rc config-host.mak
$(call quiet-command,$(WINDRES) -I. -o $@ $<," RC $(TARGET_DIR)$@")
diff --git a/Makefile.objs b/Makefile.objs
index dbdce3c..5dae800 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -103,7 +103,7 @@ common-obj-y += block-migration.o
common-obj-y += pflib.o
common-obj-y += bitmap.o bitops.o
common-obj-y += qmp-marshal-types.o qmp-marshal-types-core.o
-common-obj-y += qmp-core.o
+common-obj-y += qmp-core.o qmp-marshal.o
common-obj-$(CONFIG_BRLAPI) += baum.o
common-obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o
diff --git a/qmp-gen.py b/qmp-gen.py
index 8adcc2b..aac0f90 100644
--- a/qmp-gen.py
+++ b/qmp-gen.py
@@ -429,6 +429,227 @@ QObject *qmp_marshal_type_%s(%s value)
}
''' % (name, name, name)
+def print_declaration(name, required, optional, retval):
+ args = []
+ if name in ['qmp_capabilities', 'put-event']:
+ return
+ 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 **err')
+
+ print '%s qmp_%s(%s);' % (qmp_type_to_c(retval, True), c_var(name), ', '.join(args))
+
+def print_definition(name, required, optional, retval):
+ if qmp_type_is_event(retval):
+ arglist = ['void *opaque']
+ for member in event_types[retval]:
+ argname = c_var(member)
+ argtype = event_types[retval][member]
+ if argname[0] == '*':
+ argname = argname[1:]
+ arglist.append('bool has_%s' % argname)
+ arglist.append('%s %s' % (qmp_type_to_c(argtype), argname))
+ print '''
+static void qmp_marshal_%s(%s)
+{
+ QDict *qmp__args = qdict_new();
+ QmpConnection *qmp__conn = opaque;
+''' % (qmp_event_to_c(retval), ', '.join(arglist))
+
+ for member in event_types[retval]:
+ argname = member
+ argtype = event_types[retval][member]
+ opt = False
+ if argname[0] == '*':
+ argname = argname[1:]
+ opt = True
+ if opt:
+ print ' if (has_%s) {' % c_var(argname)
+ print ' qdict_put_obj(qmp__args, "%s", %s(%s));' % (argname, qmp_type_to_qobj_ctor(argtype), c_var(argname))
+ print ' }'
+ else:
+ print ' qdict_put_obj(qmp__args, "%s", %s(%s));' % (argname, qmp_type_to_qobj_ctor(argtype), c_var(argname))
+
+ print '''
+ qmp_state_event(qmp__conn, QOBJECT(qmp__args));
+ QDECREF(qmp__args);
+}'''
+ print '''
+static void qmp_marshal_%s(QmpState *qmp__sess, const QDict *qdict, QObject **ret_data, Error **err)
+{
+ int qmp__handle;
+ QmpConnection *qmp__connection = qemu_mallocz(sizeof(QmpConnection));''' % c_var(name)
+ elif name in ['qmp_capabilities', 'put-event']:
+ print '''
+static void qmp_marshal_%s(QmpState *qmp__sess, const QDict *qdict, QObject **ret_data, Error **err)
+{''' % c_var(name)
+ else:
+ print '''
+static void qmp_marshal_%s(const QDict *qdict, QObject **ret_data, Error **err)
+{''' % c_var(name)
+ print ' Error *qmp__err = NULL;'
+
+ for key in required:
+ print ' %s %s = 0;' % (qmp_type_to_c(required[key], True), c_var(key))
+
+ for key in optional:
+ if optional[key] == '**':
+ print ' KeyValues * %s = 0;' % c_var(key)
+ else:
+ print ' bool has_%s = false;' % (c_var(key))
+ print ' %s %s = 0;' % (qmp_type_to_c(optional[key], True), c_var(key))
+
+ if retval != 'none':
+ print ' %s qmp_retval = 0;' % (qmp_type_to_c(retval, True))
+
+ print '''
+ (void)qmp__err;'''
+
+ for key in required:
+ print '''
+ if (!qdict_haskey(qdict, "%s")) {
+ error_set(err, QERR_MISSING_PARAMETER, "%s");
+ goto qmp__out;
+ }
+
+ %s = %s(qdict_get(qdict, "%s"), &qmp__err);
+ if (qmp__err) {
+ if (error_is_type(qmp__err, QERR_INVALID_PARAMETER_TYPE)) {
+ error_set(err, QERR_INVALID_PARAMETER_TYPE, "%s",
+ error_get_field(qmp__err, "expected"));
+ error_free(qmp__err);
+ qmp__err = NULL;
+ } else {
+ error_propagate(err, qmp__err);
+ }
+ goto qmp__out;
+ }
+''' % (key, key, c_var(key), qmp_type_from_qobj(required[key]), key, key)
+
+ for key in optional:
+ if optional[key] == '**':
+ print '''
+ {
+ const QDictEntry *qmp__qdict_i;
+
+ %s = NULL;
+ for (qmp__qdict_i = qdict_first(qdict); qmp__qdict_i; qmp__qdict_i = qdict_next(qdict, qmp__qdict_i)) {
+ KeyValues *qmp__i;''' % c_var(key)
+ for key1 in required.keys() + optional.keys():
+ if key1 == key:
+ continue
+ print ''' if (strcmp(qmp__qdict_i->key, "%s") == 0) {
+ continue;
+ }''' % key1
+ print '''
+ qmp__i = qmp_alloc_key_values();
+ qmp__i->key = qemu_strdup(qmp__qdict_i->key);
+ qmp__i->value = qobject_as_string(qmp__qdict_i->value);
+ qmp__i->next = %s;
+ %s = qmp__i;
+ }
+ }''' % (c_var(key), c_var(key))
+ else:
+ print '''
+ if (qdict_haskey(qdict, "%s")) {
+ %s = %s(qdict_get(qdict, "%s"), &qmp__err);
+ if (qmp__err) {
+ if (error_is_type(qmp__err, QERR_INVALID_PARAMETER_TYPE)) {
+ error_set(err, QERR_INVALID_PARAMETER_TYPE, "%s",
+ error_get_field(qmp__err, "expected"));
+ error_free(qmp__err);
+ qmp__err = NULL;
+ } else {
+ error_propagate(err, qmp__err);
+ }
+ goto qmp__out;
+ }
+ has_%s = true;
+ }
+''' % (key, c_var(key), qmp_type_from_qobj(optional[key]), key, key, c_var(key))
+
+ args = []
+ for key in required:
+ args.append(c_var(key))
+ for key in optional:
+ if optional[key] == '**':
+ args.append(c_var(key))
+ else:
+ args.append('has_%s' % c_var(key))
+ args.append(c_var(key))
+ args.append('err')
+
+ if name in ['qmp_capabilities', 'put-event']:
+ args = ['qmp__sess'] + args
+
+ arglist = ', '.join(args)
+ fn = 'qmp_%s' % c_var(name)
+
+ if name == 'put-event':
+ print ' qmp_state_del_connection(%s);' % arglist
+ elif retval == 'none':
+ print ' %s(%s);' % (fn, arglist)
+ else:
+ print ' qmp_retval = %s(%s);' % (fn, arglist)
+
+ print '''
+ if (error_is_set(err)) {
+ goto qmp__out;
+ }'''
+
+ if retval == 'none':
+ pass
+ elif qmp_type_is_event(retval):
+ print ' qmp__handle = signal_connect(qmp_retval, qmp_marshal_%s, qmp__connection);' % (qmp_event_to_c(retval))
+ print ' qmp_state_add_connection(qmp__sess, "%s", qmp_retval->signal, qmp__handle, qmp__connection);' % retval
+ print ' *ret_data = QOBJECT(qint_from_int(qmp__connection->global_handle));'
+ print ' qmp__connection = NULL;'
+ elif type(retval) == str:
+ print ' *ret_data = %s(qmp_retval);' % qmp_type_to_qobj_ctor(retval)
+ elif type(retval) == list:
+ print ''' *ret_data = QOBJECT(qlist_new());
+ if (qmp_retval) {
+ QList *list = qobject_to_qlist(*ret_data);
+ %s i;
+ for (i = qmp_retval; i != NULL; i = i->next) {
+ QObject *obj = %s(i);
+ qlist_append_obj(list, obj);
+ }
+ }''' % (qmp_type_to_c(retval[0], True), qmp_type_to_qobj_ctor(retval[0]))
+
+ print
+ print 'qmp__out:'
+ if qmp_type_is_event(retval):
+ print ' qemu_free(qmp__connection);'
+ print
+ args = []
+ for argname in required:
+ argtype = required[argname]
+ if qmp_type_should_free(argtype):
+ print ' %s(%s);' % (qmp_free_func(argtype), c_var(argname))
+ for argname in optional:
+ argtype = optional[argname]
+ if argtype == '**':
+ print ' %s(%s);' % (qmp_free_func('KeyValues'), c_var(argname))
+ elif qmp_type_should_free(argtype):
+ print ' if (has_%s) {' % c_var(argname)
+ print ' %s(%s);' % (qmp_free_func(argtype), c_var(argname))
+ print ' }'
+ if retval != 'none':
+ if qmp_type_should_free(retval):
+ print ' %s(%s);' % (qmp_free_func(retval), 'qmp_retval')
+ print ' return;'
+
+ print '}'
+
def tokenize(data):
while len(data):
if data[0] in ['{', '}', ':', ',', '[', ']']:
@@ -488,6 +709,11 @@ if len(sys.argv) == 2:
kind = 'marshal-body'
elif sys.argv[1] == '--marshal-header':
kind = 'marshal-header'
+ elif sys.argv[1] == '--body':
+ kind = 'body'
+ elif sys.argv[1] == '--header':
+ kind = 'header'
+
if kind == 'types-header':
print '''/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT EDIT */
@@ -517,6 +743,22 @@ elif kind == 'marshal-body':
#include "qmp-marshal-types.h"
#include "qerror.h"
'''
+elif kind == 'header':
+ print '''/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT EDIT */
+#ifndef QMP_MARSHAL_H
+#define QMP_MARSHAL_H
+
+#include "qemu-common.h"
+#include "qemu-objects.h"
+#include "qmp-types.h"
+#include "error.h"
+'''
+elif kind == 'body':
+ print '''/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT EDIT */
+
+#include "qmp.h"
+#include "qmp-core.h"
+'''
exprs = []
expr = ''
@@ -562,6 +804,26 @@ for s in exprs:
print_enum_marshal_declaration(key, s[key])
elif kind == 'marshal-body':
print_enum_marshal_definition(key, s[key])
+ else:
+ name, required, optional, retval = s
+ if kind == 'body':
+ print_definition(name, required, optional, retval)
+ elif kind == 'header':
+ print_declaration(name, required, optional, retval)
if kind.endswith('header'):
print '#endif'
+elif kind == 'body':
+ print
+ print 'static void qmp_init_marshal(void)'
+ print '{'
+ for s in exprs:
+ if type(s) != list:
+ continue
+ if qmp_type_is_event(s[3]) or s[0] in ['qmp_capabilities', 'put-event']:
+ print ' qmp_register_stateful_command("%s", &qmp_marshal_%s);' % (s[0], c_var(s[0]))
+ else:
+ print ' qmp_register_command("%s", &qmp_marshal_%s);' % (s[0], c_var(s[0]))
+ print '};'
+ print
+ print 'qapi_init(qmp_init_marshal);'
--
1.7.0.4
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [Qemu-devel] [PATCH 14/22] qapi: add query-version QMP command
2011-03-07 1:22 [Qemu-devel] [PATCH 00/22] QAPI Round 1 Anthony Liguori
` (12 preceding siblings ...)
2011-03-07 1:22 ` [Qemu-devel] [PATCH 13/22] qapi: add code generators for QMP command marshaling Anthony Liguori
@ 2011-03-07 1:22 ` Anthony Liguori
2011-03-07 13:35 ` Stefan Hajnoczi
2011-03-09 13:36 ` Avi Kivity
2011-03-07 1:22 ` [Qemu-devel] [PATCH 15/22] qapi: add new QMP server that uses CharDriverState Anthony Liguori
` (12 subsequent siblings)
26 siblings, 2 replies; 106+ messages in thread
From: Anthony Liguori @ 2011-03-07 1:22 UTC (permalink / raw)
To: qemu-devel
Cc: Adam Litke, Anthony Liguori, Markus Armbruster, Luiz Capitulino
This is used internally by QMP. It's also a pretty good example of a typical
command conversion.
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
diff --git a/Makefile.objs b/Makefile.objs
index 5dae800..e1a2756 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -103,7 +103,7 @@ common-obj-y += block-migration.o
common-obj-y += pflib.o
common-obj-y += bitmap.o bitops.o
common-obj-y += qmp-marshal-types.o qmp-marshal-types-core.o
-common-obj-y += qmp-core.o qmp-marshal.o
+common-obj-y += qmp-core.o qmp-marshal.o qmp.o
common-obj-$(CONFIG_BRLAPI) += baum.o
common-obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o
diff --git a/qmp-schema.json b/qmp-schema.json
index e69de29..b343f5e 100644
--- a/qmp-schema.json
+++ b/qmp-schema.json
@@ -0,0 +1,38 @@
+# *-*- Mode: Python -*-*
+### 0.14.0 commands. Do not modify. ###
+
+##
+# @VersionInfo:
+#
+# A description of QEMU's version.
+#
+# @qemu.major: The major version of QEMU
+#
+# @qemu.minor: The minor version of QEMU
+#
+# @qemu.micro: The micro version of QEMU. By current convention, a micro
+# version of 50 signifies a development branch. A micro version
+# greater than or equal to 90 signifies a release candidate for
+# the next minor version. A micro version of less than 50
+# signifies a stable release.
+#
+# @package: QEMU will always set this field to an empty string. Downstream
+# versions of QEMU should set this to a non-empty string. The
+# exact format depends on the downstream however it highly
+# recommended that a unique name is used.
+#
+# Since: 0.14.0
+##
+{ 'VersionInfo': {'qemu': {'major': 'int', 'minor': 'int', 'micro': 'int'},
+ 'package': 'str'} }
+
+##
+# @query-version:
+#
+# Returns the current version of QEMU.
+#
+# Returns: A @VersionInfo object describing the current version of QEMU.
+#
+# Since: 0.14.0
+##
+[ 'query-version', {}, {}, 'VersionInfo' ]
diff --git a/qmp.c b/qmp.c
new file mode 100644
index 0000000..7b626f5
--- /dev/null
+++ b/qmp.c
@@ -0,0 +1,31 @@
+/*
+ * 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 "qemu-common.h"
+#include "qmp-core.h"
+#include "qmp.h"
+
+VersionInfo *qmp_query_version(Error **err)
+{
+ VersionInfo *info = qmp_alloc_version_info();
+ const char *version = QEMU_VERSION;
+ char *tmp;
+
+ info->qemu.major = strtol(version, &tmp, 10);
+ tmp++;
+ info->qemu.minor = strtol(tmp, &tmp, 10);
+ tmp++;
+ info->qemu.micro = strtol(tmp, &tmp, 10);
+ info->package = qemu_strdup(QEMU_PKGVERSION);
+
+ return info;
+}
+
--
1.7.0.4
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [Qemu-devel] [PATCH 15/22] qapi: add new QMP server that uses CharDriverState
2011-03-07 1:22 [Qemu-devel] [PATCH 00/22] QAPI Round 1 Anthony Liguori
` (13 preceding siblings ...)
2011-03-07 1:22 ` [Qemu-devel] [PATCH 14/22] qapi: add query-version QMP command Anthony Liguori
@ 2011-03-07 1:22 ` Anthony Liguori
2011-03-07 13:52 ` Stefan Hajnoczi
2011-03-07 1:22 ` [Qemu-devel] [PATCH 16/22] vl: add a new -qmp2 option to expose experimental QMP server Anthony Liguori
` (11 subsequent siblings)
26 siblings, 1 reply; 106+ messages in thread
From: Anthony Liguori @ 2011-03-07 1:22 UTC (permalink / raw)
To: qemu-devel
Cc: Adam Litke, Anthony Liguori, Markus Armbruster, Luiz Capitulino
This will replace the current QMP server once all the functions are implemented.
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
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
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [Qemu-devel] [PATCH 16/22] vl: add a new -qmp2 option to expose experimental QMP server
2011-03-07 1:22 [Qemu-devel] [PATCH 00/22] QAPI Round 1 Anthony Liguori
` (14 preceding siblings ...)
2011-03-07 1:22 ` [Qemu-devel] [PATCH 15/22] qapi: add new QMP server that uses CharDriverState Anthony Liguori
@ 2011-03-07 1:22 ` Anthony Liguori
2011-03-07 1:22 ` [Qemu-devel] [PATCH 17/22] qapi: add QMP quit command Anthony Liguori
` (10 subsequent siblings)
26 siblings, 0 replies; 106+ messages in thread
From: Anthony Liguori @ 2011-03-07 1:22 UTC (permalink / raw)
To: qemu-devel
Cc: Adam Litke, Anthony Liguori, Markus Armbruster, Luiz Capitulino
This is temporary until we implement all QMP commands.
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
diff --git a/qemu-options.hx b/qemu-options.hx
index badb730..957d935 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -1881,6 +1881,9 @@ serial port).
The default device is @code{vc} in graphical mode and @code{stdio} in
non graphical mode.
ETEXI
+DEF("qmp2", HAS_ARG, QEMU_OPTION_qmp2, \
+ "-qmp2 chardev experimental QMP server\n",
+ QEMU_ARCH_ALL)
DEF("qmp", HAS_ARG, QEMU_OPTION_qmp, \
"-qmp dev like -monitor but opens in 'control' mode\n",
QEMU_ARCH_ALL)
diff --git a/vl.c b/vl.c
index 68c6715..d1d0197 100644
--- a/vl.c
+++ b/vl.c
@@ -162,6 +162,7 @@ int main(int argc, char **argv)
#include "qemu-queue.h"
#include "cpus.h"
#include "arch_init.h"
+#include "qmp-core.h"
#include "ui/qemu-spice.h"
@@ -1917,6 +1918,8 @@ static const QEMUOption *lookup_opt(int argc, char **argv,
return popt;
}
+#define MAX_QMP_CHARDEVS 16
+
int main(int argc, char **argv, char **envp)
{
const char *gdbstub_dev = NULL;
@@ -1941,6 +1944,8 @@ int main(int argc, char **argv, char **envp)
const char *incoming = NULL;
int show_vnc_port = 0;
int defconfig = 1;
+ int nb_qmp_chardevs = 0;
+ const char *qmp_chardevs[MAX_QMP_CHARDEVS];
#ifdef CONFIG_SIMPLE_TRACE
const char *trace_file = NULL;
@@ -2391,6 +2396,13 @@ int main(int argc, char **argv, char **envp)
monitor_parse(optarg, "control");
default_monitor = 0;
break;
+ case QEMU_OPTION_qmp2:
+ if (nb_qmp_chardevs == MAX_QMP_CHARDEVS) {
+ fprintf(stderr, "-qmp: too many QMP chardevs\n");
+ exit(1);
+ }
+ qmp_chardevs[nb_qmp_chardevs++] = optarg;
+ break;
case QEMU_OPTION_mon:
opts = qemu_opts_parse(qemu_find_opts("mon"), optarg, 1);
if (!opts) {
@@ -3092,6 +3104,15 @@ int main(int argc, char **argv, char **envp)
}
#endif
+ for (i = 0; i < nb_qmp_chardevs; i++) {
+ CharDriverState *chr = qemu_chr_find(qmp_chardevs[i]);
+ if (chr == NULL) {
+ fprintf(stderr, "-qmp: unknown chardev `%s'\n", qmp_chardevs[i]);
+ exit(1);
+ }
+ qmp_init_chardev(chr);
+ }
+
/* display setup */
dpy_resize(ds);
dcl = ds->listeners;
--
1.7.0.4
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [Qemu-devel] [PATCH 17/22] qapi: add QMP quit command
2011-03-07 1:22 [Qemu-devel] [PATCH 00/22] QAPI Round 1 Anthony Liguori
` (15 preceding siblings ...)
2011-03-07 1:22 ` [Qemu-devel] [PATCH 16/22] vl: add a new -qmp2 option to expose experimental QMP server Anthony Liguori
@ 2011-03-07 1:22 ` Anthony Liguori
2011-03-07 1:23 ` [Qemu-devel] [PATCH 18/22] qapi: add QMP qmp_capabilities command Anthony Liguori
` (9 subsequent siblings)
26 siblings, 0 replies; 106+ messages in thread
From: Anthony Liguori @ 2011-03-07 1:22 UTC (permalink / raw)
To: qemu-devel
Cc: Adam Litke, Anthony Liguori, Markus Armbruster, Luiz Capitulino
This is needed by the test suite.
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
diff --git a/qmp-schema.json b/qmp-schema.json
index b343f5e..0581e67 100644
--- a/qmp-schema.json
+++ b/qmp-schema.json
@@ -36,3 +36,15 @@
# Since: 0.14.0
##
[ 'query-version', {}, {}, 'VersionInfo' ]
+
+##
+# @quit:
+#
+# This command will cause the QEMU process to exit gracefully. While every
+# attempt is made to send the QMP response before terminating, this is not
+# guaranteed. When using this interface, a premature EOF would not be
+# unexpected.
+#
+# Since: 0.14.0
+##
+[ 'quit', {}, {}, 'none' ]
diff --git a/qmp.c b/qmp.c
index 7b626f5..837ac95 100644
--- a/qmp.c
+++ b/qmp.c
@@ -12,6 +12,7 @@
#include "qemu-common.h"
#include "qmp-core.h"
#include "qmp.h"
+#include "sysemu.h"
VersionInfo *qmp_query_version(Error **err)
{
@@ -29,3 +30,8 @@ VersionInfo *qmp_query_version(Error **err)
return info;
}
+void qmp_quit(Error **err)
+{
+ no_shutdown = 0;
+ qemu_system_shutdown_request();
+}
--
1.7.0.4
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [Qemu-devel] [PATCH 18/22] qapi: add QMP qmp_capabilities command
2011-03-07 1:22 [Qemu-devel] [PATCH 00/22] QAPI Round 1 Anthony Liguori
` (16 preceding siblings ...)
2011-03-07 1:22 ` [Qemu-devel] [PATCH 17/22] qapi: add QMP quit command Anthony Liguori
@ 2011-03-07 1:23 ` Anthony Liguori
2011-03-07 1:23 ` [Qemu-devel] [PATCH 19/22] qapi: add QMP put-event command Anthony Liguori
` (8 subsequent siblings)
26 siblings, 0 replies; 106+ messages in thread
From: Anthony Liguori @ 2011-03-07 1:23 UTC (permalink / raw)
To: qemu-devel
Cc: Adam Litke, Anthony Liguori, Markus Armbruster, Luiz Capitulino
For now, it's a nop. In the near future, it will be used to register default
signals.
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
diff --git a/qmp-core.c b/qmp-core.c
index 3a4d240..72e4fea 100644
--- a/qmp-core.c
+++ b/qmp-core.c
@@ -393,3 +393,7 @@ void qmp_init_chardev(CharDriverState *chr)
qemu_chr_add_handlers(chr, qmp_chr_can_receive, qmp_chr_receive,
qmp_chr_event, s);
}
+
+void qmp_qmp_capabilities(QmpState *state, Error **errp)
+{
+}
diff --git a/qmp-core.h b/qmp-core.h
index 808edf3..d15d349 100644
--- a/qmp-core.h
+++ b/qmp-core.h
@@ -82,4 +82,6 @@ void qmp_state_event(QmpConnection *conn, QObject *data);
void qmp_init_chardev(CharDriverState *chr);
+void qmp_qmp_capabilities(QmpState *state, Error **errp);
+
#endif
diff --git a/qmp-schema.json b/qmp-schema.json
index 0581e67..3f2dd4e 100644
--- a/qmp-schema.json
+++ b/qmp-schema.json
@@ -48,3 +48,13 @@
# Since: 0.14.0
##
[ 'quit', {}, {}, 'none' ]
+
+##
+# @qmp_capabilities:
+#
+# Currently a nop command. To communicate with older servers, this should be
+# sent first before executing new commands.
+#
+# Since: 0.14.0
+##
+[ 'qmp_capabilities', {}, {}, 'none' ]
--
1.7.0.4
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [Qemu-devel] [PATCH 19/22] qapi: add QMP put-event command
2011-03-07 1:22 [Qemu-devel] [PATCH 00/22] QAPI Round 1 Anthony Liguori
` (17 preceding siblings ...)
2011-03-07 1:23 ` [Qemu-devel] [PATCH 18/22] qapi: add QMP qmp_capabilities command Anthony Liguori
@ 2011-03-07 1:23 ` Anthony Liguori
2011-03-09 13:31 ` Avi Kivity
2011-03-07 1:23 ` [Qemu-devel] [PATCH 20/22] qapi: add code generator for libqmp Anthony Liguori
` (7 subsequent siblings)
26 siblings, 1 reply; 106+ messages in thread
From: Anthony Liguori @ 2011-03-07 1:23 UTC (permalink / raw)
To: qemu-devel
Cc: Adam Litke, Anthony Liguori, Markus Armbruster, Luiz Capitulino
This is needed for libqmp to support events. put-event is used to disconnect
from signals.
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
diff --git a/qmp-schema.json b/qmp-schema.json
index 3f2dd4e..a13885f 100644
--- a/qmp-schema.json
+++ b/qmp-schema.json
@@ -58,3 +58,18 @@
# Since: 0.14.0
##
[ 'qmp_capabilities', {}, {}, 'none' ]
+
+##
+# @put_event:
+#
+# Disconnect a signal. This command is used to disconnect from a signal based
+# on the handle returned by a signal accessor.
+#
+# @tag: the handle returned by a signal accessor.
+#
+# Returns: Nothing on success.
+# If @tag is not a valid handle, InvalidParameterValue
+#
+# Since: 0.15.0
+##
+[ 'put-event', {'tag': 'int'}, {}, 'none' ]
--
1.7.0.4
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [Qemu-devel] [PATCH 20/22] qapi: add code generator for libqmp
2011-03-07 1:22 [Qemu-devel] [PATCH 00/22] QAPI Round 1 Anthony Liguori
` (18 preceding siblings ...)
2011-03-07 1:23 ` [Qemu-devel] [PATCH 19/22] qapi: add QMP put-event command Anthony Liguori
@ 2011-03-07 1:23 ` Anthony Liguori
2011-03-07 1:23 ` [Qemu-devel] [PATCH 21/22] qapi: add test-libqmp Anthony Liguori
` (6 subsequent siblings)
26 siblings, 0 replies; 106+ messages in thread
From: Anthony Liguori @ 2011-03-07 1:23 UTC (permalink / raw)
To: qemu-devel
Cc: Adam Litke, Anthony Liguori, Markus Armbruster, Luiz Capitulino
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
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 '}'
--
1.7.0.4
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [Qemu-devel] [PATCH 21/22] qapi: add test-libqmp
2011-03-07 1:22 [Qemu-devel] [PATCH 00/22] QAPI Round 1 Anthony Liguori
` (19 preceding siblings ...)
2011-03-07 1:23 ` [Qemu-devel] [PATCH 20/22] qapi: add code generator for libqmp Anthony Liguori
@ 2011-03-07 1:23 ` Anthony Liguori
2011-03-07 1:23 ` [Qemu-devel] [PATCH 22/22] qapi: generate HTML report for test-libqmp Anthony Liguori
` (5 subsequent siblings)
26 siblings, 0 replies; 106+ messages in thread
From: Anthony Liguori @ 2011-03-07 1:23 UTC (permalink / raw)
To: qemu-devel
Cc: Adam Litke, Anthony Liguori, Markus Armbruster, Luiz Capitulino
This provides a glib-test based testing framework for QMP
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
diff --git a/Makefile b/Makefile
index 5170675..1d363d7 100644
--- a/Makefile
+++ b/Makefile
@@ -72,6 +72,8 @@ defconfig:
-include config-all-devices.mak
+TOOLS += test-libqmp
+
build-all: $(DOCS) $(TOOLS) recurse-all
config-host.h: config-host.h-timestamp
@@ -205,6 +207,15 @@ check-qlist: check-qlist.o qlist.o qint.o $(CHECK_PROG_DEPS)
check-qfloat: check-qfloat.o qfloat.o $(CHECK_PROG_DEPS)
check-qjson: check-qjson.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o qjson.o json-streamer.o json-lexer.o json-parser.o $(CHECK_PROG_DEPS)
+LIBQMP_OBJS := qmp-types.o libqmp.o error.o libqmp-core.o
+LIBQMP_OBJS += qmp-marshal-types-core.o qmp-marshal-types.o
+LIBQMP_OBJS += qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o qjson.o
+LIBQMP_OBJS += qerror.o
+LIBQMP_OBJS += json-streamer.o json-lexer.o json-parser.o
+LIBQMP_OBJS += $(oslib-obj-y) $(trace-obj-y) qemu-malloc.o
+
+test-libqmp: test-libqmp.o $(LIBQMP_OBJS) qemu-timer-common.o
+
clean:
# avoid old build problems by removing potentially incorrect old files
rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h
diff --git a/test-libqmp.c b/test-libqmp.c
new file mode 100644
index 0000000..9b73987
--- /dev/null
+++ b/test-libqmp.c
@@ -0,0 +1,170 @@
+/*
+ * 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 <stdio.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+#include <stdlib.h>
+#include <glib.h>
+#include <sys/wait.h>
+#include "config-host.h"
+#include "libqmp.h"
+#include "qerror.h"
+
+#define g_assert_noerr(err) g_assert(err == NULL);
+#define g_assert_anyerr(err) g_assert(err != NULL);
+#define g_assert_cmperr(err, op, type) do { \
+ g_assert_anyerr(err); \
+ g_assert_cmpstr(error_get_field(err, "class"), op, type); \
+} while (0)
+
+static pid_t last_qemu_pid = -1;
+
+static QmpSession *qemu(const char *fmt, ...)
+{
+ char buffer0[4096];
+ char buffer1[4096];
+ const char *pid_filename = "/tmp/test-libqmp-qemu.pid";
+ const char *path = "/tmp/test-libqmp-qemu.sock";
+ struct sockaddr_un addr;
+ va_list ap;
+ int ret;
+ int fd;
+
+ va_start(ap, fmt);
+ vsnprintf(buffer0, sizeof(buffer0), fmt, ap);
+ va_end(ap);
+
+ snprintf(buffer1, sizeof(buffer1),
+ "i386-softmmu/qemu "
+ "-enable-kvm "
+ "-name test-libqmp "
+ "-qmp2 qmp "
+ "-chardev socket,id=qmp,path=%s,server=on,wait=off "
+ "-vnc none "
+ "-daemonize "
+ "-pidfile %s "
+ "%s", path, pid_filename, buffer0);
+ g_test_message("Executing %s\n", buffer1);
+ ret = system(buffer1);
+ g_assert(ret != -1);
+
+ {
+ FILE *f;
+ char buffer[1024];
+ char *ptr;
+
+ f = fopen(pid_filename, "r");
+ g_assert(f != NULL);
+
+ ptr = fgets(buffer, sizeof(buffer), f);
+ g_assert(ptr != NULL);
+
+ fclose(f);
+
+ last_qemu_pid = atoi(buffer);
+ }
+
+ fd = socket(PF_UNIX, SOCK_STREAM, 0);
+ g_assert(fd != -1);
+
+ addr.sun_family = AF_UNIX;
+ snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", path);
+ ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
+ g_assert(ret != -1);
+
+ return qmp_session_new(fd);
+}
+
+static void wait_for_pid_exit(pid_t pid)
+{
+ FILE *f = NULL;
+
+ /* This is ugly but I don't know of a better way */
+ do {
+ char buffer[1024];
+
+ if (f) {
+ fclose(f);
+ usleep(10000);
+ }
+
+ snprintf(buffer, sizeof(buffer), "/proc/%d/stat", pid);
+ f = fopen(buffer, "r");
+ } while (f);
+}
+
+static void qemu_destroy(QmpSession *sess)
+{
+ wait_for_pid_exit(last_qemu_pid);
+ last_qemu_pid = -1;
+ qmp_session_destroy(sess);
+}
+
+static void test_version(void)
+{
+ QmpSession *sess = NULL;
+ VersionInfo *info;
+ char version[1024];
+ char *ptr, *end;
+ int major, minor, micro;
+
+ /* Even though we use the same string as the source input, we do parse it
+ * a little bit different for no other reason that to make sure we catch
+ * potential bugs.
+ */
+ snprintf(version, sizeof(version), "%s", QEMU_VERSION);
+ ptr = version;
+
+ end = strchr(ptr, '.');
+ g_assert(end != NULL);
+ *end = 0;
+ major = atoi(ptr);
+ ptr = end + 1;
+
+ end = strchr(ptr, '.');
+ g_assert(end != NULL);
+ *end = 0;
+ minor = atoi(ptr);
+ ptr = end + 1;
+
+ micro = atoi(ptr);
+ while (g_ascii_isdigit(*ptr)) ptr++;
+
+ sess = qemu("-S");
+
+ info = libqmp_query_version(sess, NULL);
+
+ g_assert_cmpint(major, ==, info->qemu.major);
+ g_assert_cmpint(minor, ==, info->qemu.minor);
+ g_assert_cmpint(micro, ==, info->qemu.micro);
+ g_assert_cmpstr(ptr, ==, info->package);
+
+ qmp_free_version_info(info);
+
+ libqmp_quit(sess, NULL);
+
+ qemu_destroy(sess);
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+
+ g_test_add_func("/0.14/misc/version", test_version);
+
+ g_test_run();
+
+ return 0;
+}
--
1.7.0.4
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [Qemu-devel] [PATCH 22/22] qapi: generate HTML report for test-libqmp
2011-03-07 1:22 [Qemu-devel] [PATCH 00/22] QAPI Round 1 Anthony Liguori
` (20 preceding siblings ...)
2011-03-07 1:23 ` [Qemu-devel] [PATCH 21/22] qapi: add test-libqmp Anthony Liguori
@ 2011-03-07 1:23 ` Anthony Liguori
2011-03-07 2:11 ` Anthony Liguori
2011-03-07 1:26 ` [Qemu-devel] [PATCH] qapi: qmp-types.c and qmp-types.h Anthony Liguori
` (4 subsequent siblings)
26 siblings, 1 reply; 106+ messages in thread
From: Anthony Liguori @ 2011-03-07 1:23 UTC (permalink / raw)
To: qemu-devel
Cc: Adam Litke, Anthony Liguori, Markus Armbruster, Luiz Capitulino
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
diff --git a/Makefile b/Makefile
index 1d363d7..c5a4820 100644
--- a/Makefile
+++ b/Makefile
@@ -216,6 +216,15 @@ LIBQMP_OBJS += $(oslib-obj-y) $(trace-obj-y) qemu-malloc.o
test-libqmp: test-libqmp.o $(LIBQMP_OBJS) qemu-timer-common.o
+check: test-libqmp
+ $(call quiet-command, ./test-libqmp, " CHECK $@")
+
+test-report.html: test-report.log
+ $(call quiet-command, gtester-report $< > $@, " GEN $@")
+
+test-report.log: test-libqmp
+ $(call quiet-command, gtester -k -o $@ ./test-libqmp 2>/dev/null >/dev/null || true, " TEST $<")
+
clean:
# avoid old build problems by removing potentially incorrect old files
rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h
--
1.7.0.4
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [Qemu-devel] [PATCH] qapi: qmp-types.c and qmp-types.h
2011-03-07 1:22 [Qemu-devel] [PATCH 00/22] QAPI Round 1 Anthony Liguori
` (21 preceding siblings ...)
2011-03-07 1:23 ` [Qemu-devel] [PATCH 22/22] qapi: generate HTML report for test-libqmp Anthony Liguori
@ 2011-03-07 1:26 ` Anthony Liguori
2011-03-07 1:27 ` [Qemu-devel] [PATCH] qapi: qmp-marshal-types.c and qmp-marshal-types.h Anthony Liguori
` (3 subsequent siblings)
26 siblings, 0 replies; 106+ messages in thread
From: Anthony Liguori @ 2011-03-07 1:26 UTC (permalink / raw)
To: qemu-devel
Cc: Adam Litke, Anthony Liguori, Markus Armbruster, Luiz Capitulino
These are generated type functions. This file will not be committed but is
included to help review.
diff --git a/qmp-types.c b/qmp-types.c
new file mode 100644
index 0000000..3162265
--- /dev/null
+++ b/qmp-types.c
@@ -0,0 +1,22 @@
+/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT EDIT */
+
+#include "qmp-types.h"
+#include "qemu-common.h"
+
+
+void qmp_free_version_info(VersionInfo *obj)
+{
+ if (!obj) {
+ return;
+ }
+ qemu_free(obj->package);
+
+ qmp_free_version_info(obj->next);
+ qemu_free(obj);
+}
+
+VersionInfo *qmp_alloc_version_info(void)
+{
+ BUILD_ASSERT(sizeof(VersionInfo) < 512);
+ return qemu_mallocz(512);
+}
diff --git a/qmp-types.h b/qmp-types.h
new file mode 100644
index 0000000..e6a4c8c
--- /dev/null
+++ b/qmp-types.h
@@ -0,0 +1,22 @@
+/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT EDIT */
+#ifndef QMP_TYPES_H
+#define QMP_TYPES_H
+
+#include "qmp-types-core.h"
+
+
+
+typedef struct VersionInfo VersionInfo;
+struct VersionInfo {
+ struct {
+ int64_t major;
+ int64_t minor;
+ int64_t micro;
+ } qemu;
+ char * package;
+ VersionInfo *next;
+};
+
+VersionInfo *qmp_alloc_version_info(void);
+void qmp_free_version_info(VersionInfo *obj);
+#endif
--
1.7.0.4
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [Qemu-devel] [PATCH] qapi: qmp-marshal-types.c and qmp-marshal-types.h
2011-03-07 1:22 [Qemu-devel] [PATCH 00/22] QAPI Round 1 Anthony Liguori
` (22 preceding siblings ...)
2011-03-07 1:26 ` [Qemu-devel] [PATCH] qapi: qmp-types.c and qmp-types.h Anthony Liguori
@ 2011-03-07 1:27 ` Anthony Liguori
2011-03-07 1:28 ` [Qemu-devel] [PATCH] qapi: add qmp-marshal.c and qmp.h Anthony Liguori
` (2 subsequent siblings)
26 siblings, 0 replies; 106+ messages in thread
From: Anthony Liguori @ 2011-03-07 1:27 UTC (permalink / raw)
To: qemu-devel
Cc: Adam Litke, Anthony Liguori, Markus Armbruster, Luiz Capitulino
This is not to be committed.
diff --git a/qmp-marshal-types.c b/qmp-marshal-types.c
new file mode 100644
index 0000000..c3ab141
--- /dev/null
+++ b/qmp-marshal-types.c
@@ -0,0 +1,77 @@
+/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT EDIT */
+
+#include "qmp-marshal-types.h"
+#include "qerror.h"
+
+
+QObject *qmp_marshal_type_VersionInfo(struct VersionInfo * src)
+{
+ QObject *qmp__retval;
+ {
+ QDict *qmp__dict = qdict_new();
+ QObject *qmp__member0;
+
+ {
+ QDict *qmp__dict = qdict_new();
+ QObject *qmp__member1;
+
+ qmp__member1 = qmp_marshal_type_int(src->qemu.major);
+ qdict_put_obj(qmp__dict, "major", qmp__member1);
+
+ qmp__member1 = qmp_marshal_type_int(src->qemu.minor);
+ qdict_put_obj(qmp__dict, "minor", qmp__member1);
+
+ qmp__member1 = qmp_marshal_type_int(src->qemu.micro);
+ qdict_put_obj(qmp__dict, "micro", qmp__member1);
+
+ qmp__member0 = QOBJECT(qmp__dict);
+ }
+ qdict_put_obj(qmp__dict, "qemu", qmp__member0);
+
+ qmp__member0 = qmp_marshal_type_str(src->package);
+ qdict_put_obj(qmp__dict, "package", qmp__member0);
+
+ qmp__retval = QOBJECT(qmp__dict);
+ }
+ return qmp__retval;
+}
+
+struct VersionInfo * qmp_unmarshal_type_VersionInfo(QObject *src, Error **errp)
+{
+ Error *qmp__err = NULL;
+ struct VersionInfo * qmp__retval = qmp_alloc_version_info();
+ {
+ QDict *qmp__dict = qobject_to_qdict(src);
+ QObject *qmp__object0;
+ qmp__object0 = qdict_get(qmp__dict, "qemu");
+ {
+ QDict *qmp__dict = qobject_to_qdict(qmp__object0);
+ QObject *qmp__object1;
+ qmp__object1 = qdict_get(qmp__dict, "major");
+ qmp__retval->qemu.major = qmp_unmarshal_type_int(qmp__object1, &qmp__err);
+ if (qmp__err) {
+ goto qmp__err_out;
+ }
+ qmp__object1 = qdict_get(qmp__dict, "minor");
+ qmp__retval->qemu.minor = qmp_unmarshal_type_int(qmp__object1, &qmp__err);
+ if (qmp__err) {
+ goto qmp__err_out;
+ }
+ qmp__object1 = qdict_get(qmp__dict, "micro");
+ qmp__retval->qemu.micro = qmp_unmarshal_type_int(qmp__object1, &qmp__err);
+ if (qmp__err) {
+ goto qmp__err_out;
+ }
+ }
+ qmp__object0 = qdict_get(qmp__dict, "package");
+ qmp__retval->package = qmp_unmarshal_type_str(qmp__object0, &qmp__err);
+ if (qmp__err) {
+ goto qmp__err_out;
+ }
+ }
+ return qmp__retval;
+qmp__err_out:
+ error_propagate(errp, qmp__err);
+ qmp_free_version_info(qmp__retval);
+ return NULL;
+}
diff --git a/qmp-marshal-types.h b/qmp-marshal-types.h
new file mode 100644
index 0000000..ce4f6ea
--- /dev/null
+++ b/qmp-marshal-types.h
@@ -0,0 +1,11 @@
+/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT EDIT */
+#ifndef QMP_MARSHAL_TYPES_H
+#define QMP_MARSHAL_TYPES_H
+
+#include "qmp-marshal-types-core.h"
+
+
+
+QObject *qmp_marshal_type_VersionInfo(struct VersionInfo * src);
+struct VersionInfo * qmp_unmarshal_type_VersionInfo(QObject *src, Error **errp);
+#endif
--
1.7.0.4
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [Qemu-devel] [PATCH] qapi: add qmp-marshal.c and qmp.h
2011-03-07 1:22 [Qemu-devel] [PATCH 00/22] QAPI Round 1 Anthony Liguori
` (23 preceding siblings ...)
2011-03-07 1:27 ` [Qemu-devel] [PATCH] qapi: qmp-marshal-types.c and qmp-marshal-types.h Anthony Liguori
@ 2011-03-07 1:28 ` Anthony Liguori
2011-03-07 1:29 ` [Qemu-devel] [PATCH] qapi: add libqmp.c and libqmp.h Anthony Liguori
2011-03-07 15:08 ` [Qemu-devel] [PATCH 00/22] QAPI Round 1 Stefan Hajnoczi
26 siblings, 0 replies; 106+ messages in thread
From: Anthony Liguori @ 2011-03-07 1:28 UTC (permalink / raw)
To: qemu-devel
Cc: Adam Litke, Anthony Liguori, Markus Armbruster, Luiz Capitulino
Not for committing.
diff --git a/qmp-marshal.c b/qmp-marshal.c
new file mode 100644
index 0000000..0cb643a
--- /dev/null
+++ b/qmp-marshal.c
@@ -0,0 +1,102 @@
+/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT EDIT */
+
+#include "qmp.h"
+#include "qmp-core.h"
+
+
+static void qmp_marshal_query_version(const QDict *qdict, QObject **ret_data, Error **err)
+{
+ Error *qmp__err = NULL;
+ struct VersionInfo * qmp_retval = 0;
+
+ (void)qmp__err;
+ qmp_retval = qmp_query_version(err);
+
+ if (error_is_set(err)) {
+ goto qmp__out;
+ }
+ *ret_data = qmp_marshal_type_VersionInfo(qmp_retval);
+
+qmp__out:
+
+ qmp_free_version_info(qmp_retval);
+ return;
+}
+
+static void qmp_marshal_quit(const QDict *qdict, QObject **ret_data, Error **err)
+{
+ Error *qmp__err = NULL;
+
+ (void)qmp__err;
+ qmp_quit(err);
+
+ if (error_is_set(err)) {
+ goto qmp__out;
+ }
+
+qmp__out:
+
+ return;
+}
+
+static void qmp_marshal_qmp_capabilities(QmpState *qmp__sess, const QDict *qdict, QObject **ret_data, Error **err)
+{
+ Error *qmp__err = NULL;
+
+ (void)qmp__err;
+ qmp_qmp_capabilities(qmp__sess, err);
+
+ if (error_is_set(err)) {
+ goto qmp__out;
+ }
+
+qmp__out:
+
+ return;
+}
+
+static void qmp_marshal_put_event(QmpState *qmp__sess, const QDict *qdict, QObject **ret_data, Error **err)
+{
+ Error *qmp__err = NULL;
+ int64_t tag = 0;
+
+ (void)qmp__err;
+
+ if (!qdict_haskey(qdict, "tag")) {
+ error_set(err, QERR_MISSING_PARAMETER, "tag");
+ goto qmp__out;
+ }
+
+ tag = qmp_unmarshal_type_int(qdict_get(qdict, "tag"), &qmp__err);
+ if (qmp__err) {
+ if (error_is_type(qmp__err, QERR_INVALID_PARAMETER_TYPE)) {
+ error_set(err, QERR_INVALID_PARAMETER_TYPE, "tag",
+ error_get_field(qmp__err, "expected"));
+ error_free(qmp__err);
+ qmp__err = NULL;
+ } else {
+ error_propagate(err, qmp__err);
+ }
+ goto qmp__out;
+ }
+
+ qmp_state_del_connection(qmp__sess, tag, err);
+
+ if (error_is_set(err)) {
+ goto qmp__out;
+ }
+
+qmp__out:
+
+ return;
+}
+
+static void qmp_init_marshal(void)
+{
+ qmp_register_command("query-version", &qmp_marshal_query_version);
+ qmp_register_command("quit", &qmp_marshal_quit);
+ qmp_register_stateful_command("qmp_capabilities", &qmp_marshal_qmp_capabilities);
+ qmp_register_stateful_command("put-event", &qmp_marshal_put_event);
+};
+
+qapi_init(qmp_init_marshal);
diff --git a/qmp.h b/qmp.h
new file mode 100644
index 0000000..edada79
--- /dev/null
+++ b/qmp.h
@@ -0,0 +1,12 @@
+/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT EDIT */
+#ifndef QMP_MARSHAL_H
+#define QMP_MARSHAL_H
+
+#include "qemu-common.h"
+#include "qemu-objects.h"
+#include "qmp-types.h"
+#include "error.h"
+
+struct VersionInfo * qmp_query_version(Error **err);
+void qmp_quit(Error **err);
+#endif
--
1.7.0.4
^ permalink raw reply related [flat|nested] 106+ messages in thread
* [Qemu-devel] [PATCH] qapi: add libqmp.c and libqmp.h
2011-03-07 1:22 [Qemu-devel] [PATCH 00/22] QAPI Round 1 Anthony Liguori
` (24 preceding siblings ...)
2011-03-07 1:28 ` [Qemu-devel] [PATCH] qapi: add qmp-marshal.c and qmp.h Anthony Liguori
@ 2011-03-07 1:29 ` Anthony Liguori
2011-03-07 15:08 ` [Qemu-devel] [PATCH 00/22] QAPI Round 1 Stefan Hajnoczi
26 siblings, 0 replies; 106+ messages in thread
From: Anthony Liguori @ 2011-03-07 1:29 UTC (permalink / raw)
To: qemu-devel
Cc: Adam Litke, Anthony Liguori, Markus Armbruster, Luiz Capitulino
Not for committing
diff --git a/libqmp.c b/libqmp.c
new file mode 100644
index 0000000..9d90f01
--- /dev/null
+++ b/libqmp.c
@@ -0,0 +1,69 @@
+/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT EDIT */
+
+#include "libqmp.h"
+#include "libqmp-internal.h"
+
+
+struct VersionInfo * libqmp_query_version(QmpSession *qmp__session, Error **qmp__err)
+{
+ QDict *qmp__args = qdict_new();
+ Error *qmp__local_err = NULL;
+ QObject *qmp__retval = NULL;
+ struct VersionInfo * qmp__native_retval = 0;
+
+ qmp__retval = qmp__session->dispatch(qmp__session, "query-version", qmp__args, &qmp__local_err);
+
+ QDECREF(qmp__args);
+
+ if (!qmp__local_err) {
+ qmp__native_retval = qmp_unmarshal_type_VersionInfo(qmp__retval, &qmp__local_err);
+ qobject_decref(qmp__retval);
+ }
+ error_propagate(qmp__err, qmp__local_err);
+ return qmp__native_retval;
+}
+
+void libqmp_quit(QmpSession *qmp__session, Error **qmp__err)
+{
+ QDict *qmp__args = qdict_new();
+ Error *qmp__local_err = NULL;
+ QObject *qmp__retval = NULL;
+
+ qmp__retval = qmp__session->dispatch(qmp__session, "quit", qmp__args, &qmp__local_err);
+
+ QDECREF(qmp__args);
+ qobject_decref(qmp__retval);
+ error_propagate(qmp__err, qmp__local_err);
+}
+
+void libqmp_qmp_capabilities(QmpSession *qmp__session, Error **qmp__err)
+{
+ QDict *qmp__args = qdict_new();
+ Error *qmp__local_err = NULL;
+ QObject *qmp__retval = NULL;
+
+ qmp__retval = qmp__session->dispatch(qmp__session, "qmp_capabilities", qmp__args, &qmp__local_err);
+
+ QDECREF(qmp__args);
+ qobject_decref(qmp__retval);
+ error_propagate(qmp__err, qmp__local_err);
+}
+
+void libqmp_put_event(QmpSession *qmp__session, int64_t tag, Error **qmp__err)
+{
+ QDict *qmp__args = qdict_new();
+ Error *qmp__local_err = NULL;
+ QObject *qmp__retval = NULL;
+
+ qdict_put_obj(qmp__args, "tag", qmp_marshal_type_int(tag));
+
+ qmp__retval = qmp__session->dispatch(qmp__session, "put-event", qmp__args, &qmp__local_err);
+
+ QDECREF(qmp__args);
+ qobject_decref(qmp__retval);
+ error_propagate(qmp__err, qmp__local_err);
+}
+
+void libqmp_init_events(QmpSession *sess)
+{
+}
diff --git a/libqmp.h b/libqmp.h
new file mode 100644
index 0000000..da6c5e1
--- /dev/null
+++ b/libqmp.h
@@ -0,0 +1,11 @@
+/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT EDIT */
+#ifndef LIBQMP_H
+#define LIBQMP_H
+
+#include "libqmp-core.h"
+
+struct VersionInfo * libqmp_query_version(QmpSession *qmp__session, Error **qmp__err);
+void libqmp_quit(QmpSession *qmp__session, Error **qmp__err);
+void libqmp_qmp_capabilities(QmpSession *qmp__session, Error **qmp__err);
+void libqmp_put_event(QmpSession *qmp__session, int64_t tag, Error **qmp__err);
+#endif
--
1.7.0.4
^ permalink raw reply related [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 08/22] qapi: add code generator for qmp-types
2011-03-07 1:22 ` [Qemu-devel] [PATCH 08/22] qapi: add code generator for qmp-types Anthony Liguori
@ 2011-03-07 1:57 ` Anthony Liguori
0 siblings, 0 replies; 106+ messages in thread
From: Anthony Liguori @ 2011-03-07 1:57 UTC (permalink / raw)
Cc: Markus Armbruster, Luiz Capitulino, qemu-devel, Adam Litke
On 03/06/2011 07:22 PM, Anthony Liguori wrote:
> Only generate qmp-types.[ch]. These files contain the type definitions for
> QMP along with the alloc/free functions for these types. Functions to convert
> enum values to integers and vice versa are also included.
>
> qmp-types is used both within QEMU and within libqmp
>
> Special alloc/free functions are provided to ensure that all structures are
> padded when allocated. This makes sure that libqmp can provide a forward
> compatible interface since all additions to a structure will have a boolean
> enable flag.
>
> The free function is convenient since individual structures may have pointers
> that also require freeing.
>
> Signed-off-by: Anthony Liguori<aliguori@us.ibm.com>
>
> diff --git a/Makefile b/Makefile
> index 6b1d716..6b9fd69 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -4,6 +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
>
> ifneq ($(wildcard config-host.mak),)
> # Put the all: rule here so that config-host.mak can contain dependencies.
> @@ -146,6 +147,14 @@ trace-dtrace.o: trace-dtrace.dtrace $(GENERATED_HEADERS)
>
> simpletrace.o: simpletrace.c $(GENERATED_HEADERS)
>
> +qmp-types.c: $(SRC_PATH)/qmp-schema.json $(SRC_PATH)/qmp-gen.py
> + $(call quiet-command,python $(SRC_PATH)/qmp-gen.py --types-body< $< > $@, " GEN $@")
> +
> +qmp-types.h: $(SRC_PATH)/qmp-schema.json $(SRC_PATH)/qmp-gen.py
> + $(call quiet-command,python $(SRC_PATH)/qmp-gen.py --types-header< $< > $@, " GEN $@")
> +
> +qmp-types.o: qmp-types.c qmp-types.h
> +
> version.o: $(SRC_PATH)/version.rc config-host.mak
> $(call quiet-command,$(WINDRES) -I. -o $@ $<," RC $(TARGET_DIR)$@")
>
> diff --git a/Makefile.objs b/Makefile.objs
> index 69f0383..710d99f 100644
> --- a/Makefile.objs
> +++ b/Makefile.objs
> @@ -15,7 +15,7 @@ oslib-obj-$(CONFIG_POSIX) += oslib-posix.o
>
> block-obj-y = cutils.o cache-utils.o qemu-malloc.o qemu-option.o module.o
> block-obj-y += nbd.o block.o aio.o aes.o qemu-config.o
> -block-obj-y += error.o
> +block-obj-y += error.o qmp-types.o
> block-obj-$(CONFIG_POSIX) += posix-aio-compat.o
> block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
>
> diff --git a/ordereddict.py b/ordereddict.py
> new file mode 100644
> index 0000000..e17269f
> --- /dev/null
> +++ b/ordereddict.py
>
Python 2.7 introduces a standard OrderedDict and this is a compatibility
wrapper for older versions of Python. I should have mentioned this in
the commit message.
Regards,
Anthony Liguori
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 22/22] qapi: generate HTML report for test-libqmp
2011-03-07 1:23 ` [Qemu-devel] [PATCH 22/22] qapi: generate HTML report for test-libqmp Anthony Liguori
@ 2011-03-07 2:11 ` Anthony Liguori
0 siblings, 0 replies; 106+ messages in thread
From: Anthony Liguori @ 2011-03-07 2:11 UTC (permalink / raw)
Cc: Luiz Capitulino, qemu-devel, Markus Armbruster
On 03/06/2011 07:23 PM, Anthony Liguori wrote:
> Signed-off-by: Anthony Liguori<aliguori@us.ibm.com>
>
For an example of how this looks when we've got more test cases with an
artificial failure injected:
http://www.codemonkey.ws/files/test-report.html
Regards,
Anthony Liguori
> diff --git a/Makefile b/Makefile
> index 1d363d7..c5a4820 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -216,6 +216,15 @@ LIBQMP_OBJS += $(oslib-obj-y) $(trace-obj-y) qemu-malloc.o
>
> test-libqmp: test-libqmp.o $(LIBQMP_OBJS) qemu-timer-common.o
>
> +check: test-libqmp
> + $(call quiet-command, ./test-libqmp, " CHECK $@")
> +
> +test-report.html: test-report.log
> + $(call quiet-command, gtester-report $< > $@, " GEN $@")
> +
> +test-report.log: test-libqmp
> + $(call quiet-command, gtester -k -o $@ ./test-libqmp 2>/dev/null>/dev/null || true, " TEST $<")
> +
> clean:
> # avoid old build problems by removing potentially incorrect old files
> rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h
>
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 01/22] Add hard build dependency on glib
2011-03-07 1:22 ` [Qemu-devel] [PATCH 01/22] Add hard build dependency on glib Anthony Liguori
@ 2011-03-07 10:59 ` Daniel P. Berrange
2011-03-07 13:55 ` Anthony Liguori
0 siblings, 1 reply; 106+ messages in thread
From: Daniel P. Berrange @ 2011-03-07 10:59 UTC (permalink / raw)
To: Anthony Liguori
Cc: Markus Armbruster, Luiz Capitulino, qemu-devel, Adam Litke
On Sun, Mar 06, 2011 at 07:22:43PM -0600, Anthony Liguori wrote:
> GLib is an extremely common library that has a portable thread implementation
> along with tons of other goodies.
>
> GLib and GObject have a fantastic amount of infrastructure we can leverage in
> QEMU including an object oriented programming infrastructure.
>
> Short term, it has a very nice thread pool implementation that we could leverage
> in something like virtio-9p. It also has a test harness implementation that
> this series will use.
>
> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
>
> diff --git a/Makefile b/Makefile
> index eca4c76..6b1d716 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -104,6 +104,8 @@ audio/audio.o audio/fmodaudio.o: QEMU_CFLAGS += $(FMOD_CFLAGS)
>
> QEMU_CFLAGS+=$(CURL_CFLAGS)
>
> +QEMU_CFLAGS+=$(GLIB_CFLAGS)
> +
> ui/cocoa.o: ui/cocoa.m
>
> ui/sdl.o audio/sdlaudio.o ui/sdl_zoom.o baum.o: QEMU_CFLAGS += $(SDL_CFLAGS)
> diff --git a/Makefile.objs b/Makefile.objs
> index 9e98a66..0ba02c7 100644
> --- a/Makefile.objs
> +++ b/Makefile.objs
> @@ -322,3 +322,5 @@ vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
>
> vl.o: QEMU_CFLAGS+=$(SDL_CFLAGS)
>
> +vl.o: QEMU_CFLAGS+=$(GLIB_CFLAGS)
> +
> diff --git a/Makefile.target b/Makefile.target
> index 220589e..0bd42da 100644
> --- a/Makefile.target
> +++ b/Makefile.target
> @@ -204,6 +204,7 @@ QEMU_CFLAGS += $(VNC_TLS_CFLAGS)
> QEMU_CFLAGS += $(VNC_SASL_CFLAGS)
> QEMU_CFLAGS += $(VNC_JPEG_CFLAGS)
> QEMU_CFLAGS += $(VNC_PNG_CFLAGS)
> +QEMU_CFLAGS += $(GLIB_CFLAGS)
>
> # xen backend driver support
> obj-$(CONFIG_XEN) += xen_machine_pv.o xen_domainbuild.o
> diff --git a/configure b/configure
> index ef51a58..e1305ae 100755
> --- a/configure
> +++ b/configure
> @@ -1662,6 +1662,18 @@ EOF
> fi
>
> ##########################################
> +# glib support probe
> +if $pkg_config --modversion gthread-2.0 > /dev/null 2>&1 ; then
> + glib_cflags=`$pkg_config --cflags gthread-2.0 2>/dev/null`
> + glib_libs=`$pkg_config --libs gthread-2.0 2>/dev/null`
> + libs_softmmu="$glib_libs $libs_softmmu"
> + libs_tools="$glib_libs $libs_softmmu"
> +else
> + echo "glib-2.0 required to compile QEMU"
> + exit 1
> +fi
It would be preferable to specify an explicit '--atleast-version=2.xxx'
since I doubt QEMU will work with all 2.x versions of glib. Obviously
the min version will limit what platforms QEMU can easily be deployed
on, so we need to be as flexible as possible.
Regards,
Daniel
--
|: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org -o- http://virt-manager.org :|
|: http://autobuild.org -o- http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 03/22] qapi: add Error object
2011-03-07 1:22 ` [Qemu-devel] [PATCH 03/22] qapi: add Error object Anthony Liguori
@ 2011-03-07 11:06 ` Daniel P. Berrange
2011-03-07 13:59 ` Anthony Liguori
2011-03-07 14:24 ` Anthony Liguori
2011-03-07 11:38 ` Stefan Hajnoczi
1 sibling, 2 replies; 106+ messages in thread
From: Daniel P. Berrange @ 2011-03-07 11:06 UTC (permalink / raw)
To: Anthony Liguori
Cc: Markus Armbruster, Luiz Capitulino, qemu-devel, Adam Litke
On Sun, Mar 06, 2011 at 07:22:45PM -0600, Anthony Liguori wrote:
> The Error class is similar to QError (now deprecated) except that it supports
> propagation. This allows for higher quality error handling. It's losely
> modeled after glib style GErrors.
I know this offers more functionality than GError, but if we're going
to be using GLib IMHO it would be very desirable to just use GError
everywhere. Are the extra arbitary key,value pair fields really a
compelling enough addition to make us not use GError ? libvirt started
off with a very flexible error object that allowed extra string & int
values to be passed with each error, but after 5 years dev there is
not a single place in our code where we use this. Simple error code
and formatted strings have been sufficient, much like GError would
allow.
Regards,
Daniel
--
|: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org -o- http://virt-manager.org :|
|: http://autobuild.org -o- http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 02/22] qerror: expose a function to format an error
2011-03-07 1:22 ` [Qemu-devel] [PATCH 02/22] qerror: expose a function to format an error Anthony Liguori
@ 2011-03-07 11:14 ` Stefan Hajnoczi
2011-03-07 13:38 ` Anthony Liguori
0 siblings, 1 reply; 106+ messages in thread
From: Stefan Hajnoczi @ 2011-03-07 11:14 UTC (permalink / raw)
To: Anthony Liguori
Cc: Markus Armbruster, Luiz Capitulino, qemu-devel, Adam Litke
On Mon, Mar 7, 2011 at 1:22 AM, Anthony Liguori <aliguori@us.ibm.com> wrote:
> This will let Error share the QError human formatting. This is only used for
> HMP.
>
> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
>
> diff --git a/qerror.c b/qerror.c
> index 4855604..13d53c9 100644
> --- a/qerror.c
> +++ b/qerror.c
> @@ -326,12 +326,18 @@ QError *qerror_from_info(const char *file, int linenr, const char *func,
> return qerr;
> }
>
> -static void parse_error(const QError *qerror, int c)
> +static void parse_error(const QErrorStringTable *entry, int c)
> {
> - qerror_abort(qerror, "expected '%c' in '%s'", c, qerror->entry->desc);
> +#if 0
> + qerror_abort(qerror, "expected '%c' in '%s'", c, entry->desc);
> +#else
> + fprintf(stderr, "expected '%c' in '%s'", c, entry->desc);
> + abort();
> +#endif
It is not obvious to me what these #if 0 are doing. Was this just a
quick hack that needs to be fixed before merge?
Stefan
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 03/22] qapi: add Error object
2011-03-07 1:22 ` [Qemu-devel] [PATCH 03/22] qapi: add Error object Anthony Liguori
2011-03-07 11:06 ` Daniel P. Berrange
@ 2011-03-07 11:38 ` Stefan Hajnoczi
2011-03-07 13:36 ` Anthony Liguori
1 sibling, 1 reply; 106+ messages in thread
From: Stefan Hajnoczi @ 2011-03-07 11:38 UTC (permalink / raw)
To: Anthony Liguori
Cc: Markus Armbruster, Luiz Capitulino, qemu-devel, Adam Litke
On Mon, Mar 7, 2011 at 1:22 AM, Anthony Liguori <aliguori@us.ibm.com> wrote:
> +struct Error
> +{
> + QDict *obj;
> + const char *fmt;
> + char *msg;
> +};
I wonder why fmt is const char * but msg is char *. Users should use
error_get_pretty() instead of accessing msg directly and that function
returns const char * so it seems that msg should be const char * to
start with.
> +
> +void error_set(Error **errp, const char *fmt, ...)
> +{
> + Error *err;
> + va_list ap;
> +
> + if (errp == NULL) {
> + return;
> + }
> +
> + err = qemu_mallocz(sizeof(*err));
> +
> + va_start(ap, fmt);
> + err->obj = qobject_to_qdict(qobject_from_jsonv(fmt, &ap));
vsprintf() and friends pass va_list by value, they don't use a
pointer. Perhaps you want to follow that idiom?
> +bool error_is_type(Error *err, const char *fmt)
> +{
> + char *ptr;
> + char *end;
> + char classname[1024];
> +
> + ptr = strstr(fmt, "'class': '");
> + assert(ptr != NULL);
> + ptr += strlen("'class': '");
> +
> + end = strchr(ptr, '\'');
> + assert(end != NULL);
> +
> + memcpy(classname, ptr, (end - ptr));
> + classname[(end - ptr)] = 0;
> +
> + return strcmp(classname, error_get_field(err, "class")) == 0;
I'd get rid of the buffer/memcpy and use strncmp in-place instead:
const char *error_class = error_get_field(err, "class");
if (strlen(error_class) != end - ptr) {
return false;
}
return strncmp(ptr, error_class, end - ptr) == 0;
Stefan
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 10/22] qapi: add core QMP server support
2011-03-07 1:22 ` [Qemu-devel] [PATCH 10/22] qapi: add core QMP server support Anthony Liguori
@ 2011-03-07 13:09 ` Stefan Hajnoczi
2011-03-07 13:39 ` Anthony Liguori
0 siblings, 1 reply; 106+ messages in thread
From: Stefan Hajnoczi @ 2011-03-07 13:09 UTC (permalink / raw)
To: Anthony Liguori
Cc: Markus Armbruster, Luiz Capitulino, qemu-devel, Adam Litke
On Mon, Mar 7, 2011 at 1:22 AM, Anthony Liguori <aliguori@us.ibm.com> wrote:
> +char *qobject_as_string(QObject *obj)
> +{
> + char buffer[1024];
> +
> + switch (qobject_type(obj)) {
> + case QTYPE_QINT:
> + snprintf(buffer, sizeof(buffer), "%" PRId64,
> + qint_get_int(qobject_to_qint(obj)));
> + return qemu_strdup(buffer);
> + case QTYPE_QSTRING:
> + return qemu_strdup(qstring_get_str(qobject_to_qstring(obj)));
> + case QTYPE_QFLOAT:
> + snprintf(buffer, sizeof(buffer), "%.17g",
> + qfloat_get_double(qobject_to_qfloat(obj)));
> + return qemu_strdup(buffer);
qemu_asprintf() would be a nice helper function to have ;).
Stefan
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 11/22] qapi: add signal support to core QMP server
2011-03-07 1:22 ` [Qemu-devel] [PATCH 11/22] qapi: add signal support to core QMP server Anthony Liguori
@ 2011-03-07 13:21 ` Stefan Hajnoczi
2011-03-07 13:53 ` Anthony Liguori
0 siblings, 1 reply; 106+ messages in thread
From: Stefan Hajnoczi @ 2011-03-07 13:21 UTC (permalink / raw)
To: Anthony Liguori
Cc: Markus Armbruster, Luiz Capitulino, qemu-devel, Adam Litke
On Mon, Mar 7, 2011 at 1:22 AM, Anthony Liguori <aliguori@us.ibm.com> wrote:
> diff --git a/qmp-core.h b/qmp-core.h
> index e3235ec..5ce02f7 100644
> --- a/qmp-core.h
> +++ b/qmp-core.h
> @@ -21,10 +21,65 @@ typedef struct QmpState QmpState;
> typedef void (QmpCommandFunc)(const QDict *, QObject **, Error **);
> typedef void (QmpStatefulCommandFunc)(QmpState *qmp__sess, const QDict *, QObject **, Error **);
>
> +typedef struct QmpSlot
> +{
> + int handle;
> + void *func;
This should be a void (*func)() pointer for architectures where
function pointers don't fit into void * (e.g. ppc and itanium).
> +QmpSignal *qmp_signal_init(void);
> +void qmp_signal_ref(QmpSignal *obj);
> +void qmp_signal_unref(QmpSignal *obj);
> +int qmp_signal_connect(QmpSignal *obj, void *func, void *opaque);
Same function pointer issue here.
Stefan
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 13/22] qapi: add code generators for QMP command marshaling
2011-03-07 1:22 ` [Qemu-devel] [PATCH 13/22] qapi: add code generators for QMP command marshaling Anthony Liguori
@ 2011-03-07 13:27 ` Stefan Hajnoczi
2011-03-07 13:44 ` Anthony Liguori
0 siblings, 1 reply; 106+ messages in thread
From: Stefan Hajnoczi @ 2011-03-07 13:27 UTC (permalink / raw)
To: Anthony Liguori
Cc: Markus Armbruster, Luiz Capitulino, qemu-devel, Adam Litke
On Mon, Mar 7, 2011 at 1:22 AM, Anthony Liguori <aliguori@us.ibm.com> wrote:
> +def print_definition(name, required, optional, retval):
This function is pretty long. Is there a logical way to split it up
into several smaller meaningful functions?
Stefan
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 14/22] qapi: add query-version QMP command
2011-03-07 1:22 ` [Qemu-devel] [PATCH 14/22] qapi: add query-version QMP command Anthony Liguori
@ 2011-03-07 13:35 ` Stefan Hajnoczi
2011-03-07 13:41 ` Anthony Liguori
2011-03-09 13:36 ` Avi Kivity
1 sibling, 1 reply; 106+ messages in thread
From: Stefan Hajnoczi @ 2011-03-07 13:35 UTC (permalink / raw)
To: Anthony Liguori
Cc: Markus Armbruster, Luiz Capitulino, qemu-devel, Adam Litke
On Mon, Mar 7, 2011 at 1:22 AM, Anthony Liguori <aliguori@us.ibm.com> wrote:
> diff --git a/qmp-schema.json b/qmp-schema.json
> index e69de29..b343f5e 100644
> --- a/qmp-schema.json
> +++ b/qmp-schema.json
> @@ -0,0 +1,38 @@
> +# *-*- Mode: Python -*-*
By the way JSON does not seem to support comments (neither /* */ nor
#). So this comment feature you're using is an extension to JSON. I
also can't see how to do multi-line strings in JSON so "doc-strings"
aren't in the cards either.
Stefan
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 03/22] qapi: add Error object
2011-03-07 11:38 ` Stefan Hajnoczi
@ 2011-03-07 13:36 ` Anthony Liguori
2011-03-07 13:55 ` Stefan Hajnoczi
0 siblings, 1 reply; 106+ messages in thread
From: Anthony Liguori @ 2011-03-07 13:36 UTC (permalink / raw)
To: Stefan Hajnoczi
Cc: qemu-devel, Adam Litke, Markus Armbruster, Luiz Capitulino
On 03/07/2011 05:38 AM, Stefan Hajnoczi wrote:
> On Mon, Mar 7, 2011 at 1:22 AM, Anthony Liguori<aliguori@us.ibm.com> wrote:
>
>> +struct Error
>> +{
>> + QDict *obj;
>> + const char *fmt;
>> + char *msg;
>> +};
>>
> I wonder why fmt is const char * but msg is char *. Users should use
> error_get_pretty() instead of accessing msg directly and that function
> returns const char * so it seems that msg should be const char * to
> start with.
>
fmt doesn't need to be free'd whereas msg does. If you make msg const
char *, the compiler will complain when you pass that to qemu_free(). I
tend to think of the difference between 'const char *' and 'char *' as a
string that I don't own vs. a string that I do own the reference to.
It's not universally true but it tends to work nicely most of the time.
>> +
>> +void error_set(Error **errp, const char *fmt, ...)
>> +{
>> + Error *err;
>> + va_list ap;
>> +
>> + if (errp == NULL) {
>> + return;
>> + }
>> +
>> + err = qemu_mallocz(sizeof(*err));
>> +
>> + va_start(ap, fmt);
>> + err->obj = qobject_to_qdict(qobject_from_jsonv(fmt,&ap));
>>
> vsprintf() and friends pass va_list by value, they don't use a
> pointer. Perhaps you want to follow that idiom?
>
This va_list is passed to a recursive decent parser. The nature of
va_list is such that if you pass by value, you cannot access it within a
function after you've passed it to another function. Passing by
reference seems to fix this (at least with GCC). I'm not 100% confident
this is a strictly standards compliant solution but it's been working so
far. Note that this isn't introduecd by this series.
>> +bool error_is_type(Error *err, const char *fmt)
>> +{
>> + char *ptr;
>> + char *end;
>> + char classname[1024];
>> +
>> + ptr = strstr(fmt, "'class': '");
>> + assert(ptr != NULL);
>> + ptr += strlen("'class': '");
>> +
>> + end = strchr(ptr, '\'');
>> + assert(end != NULL);
>> +
>> + memcpy(classname, ptr, (end - ptr));
>> + classname[(end - ptr)] = 0;
>> +
>> + return strcmp(classname, error_get_field(err, "class")) == 0;
>>
> I'd get rid of the buffer/memcpy and use strncmp in-place instead:
>
> const char *error_class = error_get_field(err, "class");
> if (strlen(error_class) != end - ptr) {
> return false;
> }
> return strncmp(ptr, error_class, end - ptr) == 0;
>
Yeah, that's definitely better.
Regards,
Anthony Liguori
> Stefan
>
>
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 02/22] qerror: expose a function to format an error
2011-03-07 11:14 ` Stefan Hajnoczi
@ 2011-03-07 13:38 ` Anthony Liguori
0 siblings, 0 replies; 106+ messages in thread
From: Anthony Liguori @ 2011-03-07 13:38 UTC (permalink / raw)
To: Stefan Hajnoczi
Cc: qemu-devel, Adam Litke, Markus Armbruster, Luiz Capitulino
On 03/07/2011 05:14 AM, Stefan Hajnoczi wrote:
> On Mon, Mar 7, 2011 at 1:22 AM, Anthony Liguori<aliguori@us.ibm.com> wrote:
>
>> This will let Error share the QError human formatting. This is only used for
>> HMP.
>>
>> Signed-off-by: Anthony Liguori<aliguori@us.ibm.com>
>>
>> diff --git a/qerror.c b/qerror.c
>> index 4855604..13d53c9 100644
>> --- a/qerror.c
>> +++ b/qerror.c
>> @@ -326,12 +326,18 @@ QError *qerror_from_info(const char *file, int linenr, const char *func,
>> return qerr;
>> }
>>
>> -static void parse_error(const QError *qerror, int c)
>> +static void parse_error(const QErrorStringTable *entry, int c)
>> {
>> - qerror_abort(qerror, "expected '%c' in '%s'", c, qerror->entry->desc);
>> +#if 0
>> + qerror_abort(qerror, "expected '%c' in '%s'", c, entry->desc);
>> +#else
>> + fprintf(stderr, "expected '%c' in '%s'", c, entry->desc);
>> + abort();
>> +#endif
>>
> It is not obvious to me what these #if 0 are doing. Was this just a
> quick hack that needs to be fixed before merge?
>
I should just drop the #if 0 parts.
This code path should disappear once we finish the QAPI conversion. The
specific path only comes into play when a QError definition is incorrect
which should only happen in development.
I didn't think it was worth trying to preserve the error messages as
part of the code refactoring.
Regards,
Anthony Liguori
> Stefan
>
>
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 10/22] qapi: add core QMP server support
2011-03-07 13:09 ` Stefan Hajnoczi
@ 2011-03-07 13:39 ` Anthony Liguori
2011-03-07 13:46 ` Daniel P. Berrange
0 siblings, 1 reply; 106+ messages in thread
From: Anthony Liguori @ 2011-03-07 13:39 UTC (permalink / raw)
To: Stefan Hajnoczi
Cc: qemu-devel, Adam Litke, Markus Armbruster, Luiz Capitulino
On 03/07/2011 07:09 AM, Stefan Hajnoczi wrote:
> On Mon, Mar 7, 2011 at 1:22 AM, Anthony Liguori<aliguori@us.ibm.com> wrote:
>
>> +char *qobject_as_string(QObject *obj)
>> +{
>> + char buffer[1024];
>> +
>> + switch (qobject_type(obj)) {
>> + case QTYPE_QINT:
>> + snprintf(buffer, sizeof(buffer), "%" PRId64,
>> + qint_get_int(qobject_to_qint(obj)));
>> + return qemu_strdup(buffer);
>> + case QTYPE_QSTRING:
>> + return qemu_strdup(qstring_get_str(qobject_to_qstring(obj)));
>> + case QTYPE_QFLOAT:
>> + snprintf(buffer, sizeof(buffer), "%.17g",
>> + qfloat_get_double(qobject_to_qfloat(obj)));
>> + return qemu_strdup(buffer);
>>
> qemu_asprintf() would be a nice helper function to have ;).
>
Indeed :-)
Regards,
Anthony Liguori
> Stefan
>
>
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 14/22] qapi: add query-version QMP command
2011-03-07 13:35 ` Stefan Hajnoczi
@ 2011-03-07 13:41 ` Anthony Liguori
2011-03-09 13:28 ` Avi Kivity
0 siblings, 1 reply; 106+ messages in thread
From: Anthony Liguori @ 2011-03-07 13:41 UTC (permalink / raw)
To: Stefan Hajnoczi
Cc: qemu-devel, Adam Litke, Markus Armbruster, Luiz Capitulino
On 03/07/2011 07:35 AM, Stefan Hajnoczi wrote:
> On Mon, Mar 7, 2011 at 1:22 AM, Anthony Liguori<aliguori@us.ibm.com> wrote:
>
>> diff --git a/qmp-schema.json b/qmp-schema.json
>> index e69de29..b343f5e 100644
>> --- a/qmp-schema.json
>> +++ b/qmp-schema.json
>> @@ -0,0 +1,38 @@
>> +# *-*- Mode: Python -*-*
>>
> By the way JSON does not seem to support comments (neither /* */ nor
> #). So this comment feature you're using is an extension to JSON.
Yeah, it's only loosely JSON as I don't use a JSON parser.
Regards,
Anthony Liguori
> I
> also can't see how to do multi-line strings in JSON so "doc-strings"
> aren't in the cards either.
>
> Stefan
>
>
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 13/22] qapi: add code generators for QMP command marshaling
2011-03-07 13:27 ` Stefan Hajnoczi
@ 2011-03-07 13:44 ` Anthony Liguori
2011-03-07 14:38 ` Stefan Hajnoczi
0 siblings, 1 reply; 106+ messages in thread
From: Anthony Liguori @ 2011-03-07 13:44 UTC (permalink / raw)
To: Stefan Hajnoczi
Cc: qemu-devel, Adam Litke, Markus Armbruster, Luiz Capitulino
On 03/07/2011 07:27 AM, Stefan Hajnoczi wrote:
> On Mon, Mar 7, 2011 at 1:22 AM, Anthony Liguori<aliguori@us.ibm.com> wrote:
>
>> +def print_definition(name, required, optional, retval):
>>
> This function is pretty long. Is there a logical way to split it up
> into several smaller meaningful functions?
>
It seems long because of the code generation but there's really not an
awful lot of logic in it.
It might be worth spending a little time looking at a template system.
I'm not terribly familiar with any of the popular ones and I think they
usually focus on HTML generation but it might help readability.
Regards,
Anthony Liguori
> Stefan
>
>
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 10/22] qapi: add core QMP server support
2011-03-07 13:39 ` Anthony Liguori
@ 2011-03-07 13:46 ` Daniel P. Berrange
2011-03-07 13:54 ` Anthony Liguori
0 siblings, 1 reply; 106+ messages in thread
From: Daniel P. Berrange @ 2011-03-07 13:46 UTC (permalink / raw)
To: Anthony Liguori
Cc: Stefan Hajnoczi, Luiz Capitulino, qemu-devel, Markus Armbruster,
Adam Litke
On Mon, Mar 07, 2011 at 07:39:41AM -0600, Anthony Liguori wrote:
> On 03/07/2011 07:09 AM, Stefan Hajnoczi wrote:
> >On Mon, Mar 7, 2011 at 1:22 AM, Anthony Liguori<aliguori@us.ibm.com> wrote:
> >>+char *qobject_as_string(QObject *obj)
> >>+{
> >>+ char buffer[1024];
> >>+
> >>+ switch (qobject_type(obj)) {
> >>+ case QTYPE_QINT:
> >>+ snprintf(buffer, sizeof(buffer), "%" PRId64,
> >>+ qint_get_int(qobject_to_qint(obj)));
> >>+ return qemu_strdup(buffer);
> >>+ case QTYPE_QSTRING:
> >>+ return qemu_strdup(qstring_get_str(qobject_to_qstring(obj)));
> >>+ case QTYPE_QFLOAT:
> >>+ snprintf(buffer, sizeof(buffer), "%.17g",
> >>+ qfloat_get_double(qobject_to_qfloat(obj)));
> >>+ return qemu_strdup(buffer);
> >qemu_asprintf() would be a nice helper function to have ;).
>
> Indeed :-)
Since you've introduced glib, you get that function for free:
g_strdup_printf()/g_strdup_vprintf()
similarly qemu_strdup & malloc related friends could be replaced with
the equivalent glib functions.
Regards,
Daniel
--
|: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org -o- http://virt-manager.org :|
|: http://autobuild.org -o- http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 15/22] qapi: add new QMP server that uses CharDriverState
2011-03-07 1:22 ` [Qemu-devel] [PATCH 15/22] qapi: add new QMP server that uses CharDriverState Anthony Liguori
@ 2011-03-07 13:52 ` Stefan Hajnoczi
2011-03-07 14:02 ` Anthony Liguori
0 siblings, 1 reply; 106+ messages in thread
From: Stefan Hajnoczi @ 2011-03-07 13:52 UTC (permalink / raw)
To: Anthony Liguori
Cc: Markus Armbruster, Luiz Capitulino, qemu-devel, Adam Litke
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
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 11/22] qapi: add signal support to core QMP server
2011-03-07 13:21 ` Stefan Hajnoczi
@ 2011-03-07 13:53 ` Anthony Liguori
2011-03-07 14:36 ` Stefan Hajnoczi
0 siblings, 1 reply; 106+ messages in thread
From: Anthony Liguori @ 2011-03-07 13:53 UTC (permalink / raw)
To: Stefan Hajnoczi
Cc: qemu-devel, Adam Litke, Anthony Liguori, Markus Armbruster,
Luiz Capitulino
On 03/07/2011 07:21 AM, Stefan Hajnoczi wrote:
> On Mon, Mar 7, 2011 at 1:22 AM, Anthony Liguori<aliguori@us.ibm.com> wrote:
>
>> diff --git a/qmp-core.h b/qmp-core.h
>> index e3235ec..5ce02f7 100644
>> --- a/qmp-core.h
>> +++ b/qmp-core.h
>> @@ -21,10 +21,65 @@ typedef struct QmpState QmpState;
>> typedef void (QmpCommandFunc)(const QDict *, QObject **, Error **);
>> typedef void (QmpStatefulCommandFunc)(QmpState *qmp__sess, const QDict *, QObject **, Error **);
>>
>> +typedef struct QmpSlot
>> +{
>> + int handle;
>> + void *func;
>>
> This should be a void (*func)()
Technically void (*)() is an obsolete type in standard C.
I can switch to void (*)(void) but it requires casting and requires a
typeof() :-/
Regards,
Anthony Liguori
> pointer for architectures where
> function pointers don't fit into void * (e.g. ppc and itanium).
>
>
>> +QmpSignal *qmp_signal_init(void);
>> +void qmp_signal_ref(QmpSignal *obj);
>> +void qmp_signal_unref(QmpSignal *obj);
>> +int qmp_signal_connect(QmpSignal *obj, void *func, void *opaque);
>>
> Same function pointer issue here.
>
> Stefan
>
>
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 10/22] qapi: add core QMP server support
2011-03-07 13:46 ` Daniel P. Berrange
@ 2011-03-07 13:54 ` Anthony Liguori
0 siblings, 0 replies; 106+ messages in thread
From: Anthony Liguori @ 2011-03-07 13:54 UTC (permalink / raw)
To: Daniel P. Berrange
Cc: Adam Litke, Stefan Hajnoczi, qemu-devel, Markus Armbruster,
Luiz Capitulino
On 03/07/2011 07:46 AM, Daniel P. Berrange wrote:
> On Mon, Mar 07, 2011 at 07:39:41AM -0600, Anthony Liguori wrote:
>
>> On 03/07/2011 07:09 AM, Stefan Hajnoczi wrote:
>>
>>> On Mon, Mar 7, 2011 at 1:22 AM, Anthony Liguori<aliguori@us.ibm.com> wrote:
>>>
>>>> +char *qobject_as_string(QObject *obj)
>>>> +{
>>>> + char buffer[1024];
>>>> +
>>>> + switch (qobject_type(obj)) {
>>>> + case QTYPE_QINT:
>>>> + snprintf(buffer, sizeof(buffer), "%" PRId64,
>>>> + qint_get_int(qobject_to_qint(obj)));
>>>> + return qemu_strdup(buffer);
>>>> + case QTYPE_QSTRING:
>>>> + return qemu_strdup(qstring_get_str(qobject_to_qstring(obj)));
>>>> + case QTYPE_QFLOAT:
>>>> + snprintf(buffer, sizeof(buffer), "%.17g",
>>>> + qfloat_get_double(qobject_to_qfloat(obj)));
>>>> + return qemu_strdup(buffer);
>>>>
>>> qemu_asprintf() would be a nice helper function to have ;).
>>>
>> Indeed :-)
>>
> Since you've introduced glib, you get that function for free:
>
> g_strdup_printf()/g_strdup_vprintf()
>
> similarly qemu_strdup& malloc related friends could be replaced with
> the equivalent glib functions.
>
Good point.
Regards,
Anthony Liguori
> Regards,
> Daniel
>
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 03/22] qapi: add Error object
2011-03-07 13:36 ` Anthony Liguori
@ 2011-03-07 13:55 ` Stefan Hajnoczi
0 siblings, 0 replies; 106+ messages in thread
From: Stefan Hajnoczi @ 2011-03-07 13:55 UTC (permalink / raw)
To: Anthony Liguori
Cc: qemu-devel, Adam Litke, Markus Armbruster, Luiz Capitulino
On Mon, Mar 7, 2011 at 1:36 PM, Anthony Liguori <anthony@codemonkey.ws> wrote:
> On 03/07/2011 05:38 AM, Stefan Hajnoczi wrote:
>>
>> On Mon, Mar 7, 2011 at 1:22 AM, Anthony Liguori<aliguori@us.ibm.com>
>> wrote:
>>
>>>
>>> +struct Error
>>> +{
>>> + QDict *obj;
>>> + const char *fmt;
>>> + char *msg;
>>> +};
>>>
>>
>> I wonder why fmt is const char * but msg is char *. Users should use
>> error_get_pretty() instead of accessing msg directly and that function
>> returns const char * so it seems that msg should be const char * to
>> start with.
>>
>
> fmt doesn't need to be free'd whereas msg does. If you make msg const char
> *, the compiler will complain when you pass that to qemu_free(). I tend to
> think of the difference between 'const char *' and 'char *' as a string that
> I don't own vs. a string that I do own the reference to.
>
> It's not universally true but it tends to work nicely most of the time.
Thanks, that makes sense.
Stefan
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 01/22] Add hard build dependency on glib
2011-03-07 10:59 ` Daniel P. Berrange
@ 2011-03-07 13:55 ` Anthony Liguori
0 siblings, 0 replies; 106+ messages in thread
From: Anthony Liguori @ 2011-03-07 13:55 UTC (permalink / raw)
To: Daniel P. Berrange
Cc: qemu-devel, Adam Litke, Markus Armbruster, Luiz Capitulino
On 03/07/2011 04:59 AM, Daniel P. Berrange wrote:
> On Sun, Mar 06, 2011 at 07:22:43PM -0600, Anthony Liguori wrote:
>
>> GLib is an extremely common library that has a portable thread implementation
>> along with tons of other goodies.
>>
>> GLib and GObject have a fantastic amount of infrastructure we can leverage in
>> QEMU including an object oriented programming infrastructure.
>>
>> Short term, it has a very nice thread pool implementation that we could leverage
>> in something like virtio-9p. It also has a test harness implementation that
>> this series will use.
>>
>> Signed-off-by: Anthony Liguori<aliguori@us.ibm.com>
>>
>> diff --git a/Makefile b/Makefile
>> index eca4c76..6b1d716 100644
>> --- a/Makefile
>> +++ b/Makefile
>> @@ -104,6 +104,8 @@ audio/audio.o audio/fmodaudio.o: QEMU_CFLAGS += $(FMOD_CFLAGS)
>>
>> QEMU_CFLAGS+=$(CURL_CFLAGS)
>>
>> +QEMU_CFLAGS+=$(GLIB_CFLAGS)
>> +
>> ui/cocoa.o: ui/cocoa.m
>>
>> ui/sdl.o audio/sdlaudio.o ui/sdl_zoom.o baum.o: QEMU_CFLAGS += $(SDL_CFLAGS)
>> diff --git a/Makefile.objs b/Makefile.objs
>> index 9e98a66..0ba02c7 100644
>> --- a/Makefile.objs
>> +++ b/Makefile.objs
>> @@ -322,3 +322,5 @@ vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
>>
>> vl.o: QEMU_CFLAGS+=$(SDL_CFLAGS)
>>
>> +vl.o: QEMU_CFLAGS+=$(GLIB_CFLAGS)
>> +
>> diff --git a/Makefile.target b/Makefile.target
>> index 220589e..0bd42da 100644
>> --- a/Makefile.target
>> +++ b/Makefile.target
>> @@ -204,6 +204,7 @@ QEMU_CFLAGS += $(VNC_TLS_CFLAGS)
>> QEMU_CFLAGS += $(VNC_SASL_CFLAGS)
>> QEMU_CFLAGS += $(VNC_JPEG_CFLAGS)
>> QEMU_CFLAGS += $(VNC_PNG_CFLAGS)
>> +QEMU_CFLAGS += $(GLIB_CFLAGS)
>>
>> # xen backend driver support
>> obj-$(CONFIG_XEN) += xen_machine_pv.o xen_domainbuild.o
>> diff --git a/configure b/configure
>> index ef51a58..e1305ae 100755
>> --- a/configure
>> +++ b/configure
>> @@ -1662,6 +1662,18 @@ EOF
>> fi
>>
>> ##########################################
>> +# glib support probe
>> +if $pkg_config --modversion gthread-2.0> /dev/null 2>&1 ; then
>> + glib_cflags=`$pkg_config --cflags gthread-2.0 2>/dev/null`
>> + glib_libs=`$pkg_config --libs gthread-2.0 2>/dev/null`
>> + libs_softmmu="$glib_libs $libs_softmmu"
>> + libs_tools="$glib_libs $libs_softmmu"
>> +else
>> + echo "glib-2.0 required to compile QEMU"
>> + exit 1
>> +fi
>>
> It would be preferable to specify an explicit '--atleast-version=2.xxx'
> since I doubt QEMU will work with all 2.x versions of glib. Obviously
> the min version will limit what platforms QEMU can easily be deployed
> on, so we need to be as flexible as possible.
>
Yeah, right now, we don't really depend on a specific version but it
would be good to have for when we do.
The testing bits are not part of the main build (they depend on a pretty
recent glib).
Regards,
Anthony Liguori
> Regards,
> Daniel
>
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 03/22] qapi: add Error object
2011-03-07 11:06 ` Daniel P. Berrange
@ 2011-03-07 13:59 ` Anthony Liguori
2011-03-07 14:24 ` Anthony Liguori
1 sibling, 0 replies; 106+ messages in thread
From: Anthony Liguori @ 2011-03-07 13:59 UTC (permalink / raw)
To: Daniel P. Berrange
Cc: qemu-devel, Adam Litke, Markus Armbruster, Luiz Capitulino
On 03/07/2011 05:06 AM, Daniel P. Berrange wrote:
> On Sun, Mar 06, 2011 at 07:22:45PM -0600, Anthony Liguori wrote:
>
>> The Error class is similar to QError (now deprecated) except that it supports
>> propagation. This allows for higher quality error handling. It's losely
>> modeled after glib style GErrors.
>>
> I know this offers more functionality than GError, but if we're going
> to be using GLib IMHO it would be very desirable to just use GError
> everywhere. Are the extra arbitary key,value pair fields really a
> compelling enough addition to make us not use GError ? libvirt started
> off with a very flexible error object that allowed extra string& int
> values to be passed with each error, but after 5 years dev there is
> not a single place in our code where we use this. Simple error code
> and formatted strings have been sufficient, much like GError would
> allow.
>
I wrote Error without even thinking of using GError. My motivation was
to move us off of QError.
We would lose key/value pairs which are actually quite useful. Consider
executing the qmp_cont() command after starting a guest with -S. Right
now, we'll throw a DeviceEncrypted error message that contains the drive
name and encrypted filename which means a management app can immediately
prompt the user for a password and then execute block_passwd.
Without key/value pairs, a management tool would need another round trip
to do an info block and find out which devices are encrypted before
doing the prompt.
I need to think a bit more about it.
Regards,
Anthony Liguori
> Regards,
> Daniel
>
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 15/22] qapi: add new QMP server that uses CharDriverState
2011-03-07 13:52 ` Stefan Hajnoczi
@ 2011-03-07 14:02 ` Anthony Liguori
0 siblings, 0 replies; 106+ messages in thread
From: Anthony Liguori @ 2011-03-07 14:02 UTC (permalink / raw)
To: Stefan Hajnoczi
Cc: qemu-devel, Adam Litke, Markus Armbruster, Luiz Capitulino
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
>
>
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 03/22] qapi: add Error object
2011-03-07 11:06 ` Daniel P. Berrange
2011-03-07 13:59 ` Anthony Liguori
@ 2011-03-07 14:24 ` Anthony Liguori
1 sibling, 0 replies; 106+ messages in thread
From: Anthony Liguori @ 2011-03-07 14:24 UTC (permalink / raw)
To: Daniel P. Berrange
Cc: qemu-devel, Adam Litke, Markus Armbruster, Luiz Capitulino
On 03/07/2011 05:06 AM, Daniel P. Berrange wrote:
> On Sun, Mar 06, 2011 at 07:22:45PM -0600, Anthony Liguori wrote:
>
>> The Error class is similar to QError (now deprecated) except that it supports
>> propagation. This allows for higher quality error handling. It's losely
>> modeled after glib style GErrors.
>>
> I know this offers more functionality than GError, but if we're going
> to be using GLib IMHO it would be very desirable to just use GError
> everywhere. Are the extra arbitary key,value pair fields really a
> compelling enough addition to make us not use GError ?
So here's the rational for not using GError (or really any message based
Error object).
GError uses a domain, code, and message. Since theses errors escape
over the wire, the message part ends up not being very useful for clients.
There's no way to exhaustively enumerate all of the possible messages
from the client's perspective which means there's no way to provide a
localized version of the message. That means that if a management tool
wishes to expose these messages, they'd have to expose the English
string which is extremely undesirable.
With the current Error design, a management tool can create a localized
error message table to use to generate proper messages for end users.
It can also make designs based on additional error content.
Using GError would basically devolve into having an error code. It's
doable, but if we were going to use an error code, I wouldn't want to
have a custom message parameter and instead use a generated string. My
concern is that people will put critical information in the message
parameter that a management tool is unable to use. Worse yet, a
management tool may end up parsing these strings and then we're stuck
maintaining yet another format.
Just imagine the head ache of needed to worry about a formatted string
that contains a file name and the file name contains special
characters... It's the monitor all over again.
Regards,
Anthony Liguori
> libvirt started
> off with a very flexible error object that allowed extra string& int
> values to be passed with each error, but after 5 years dev there is
> not a single place in our code where we use this. Simple error code
> and formatted strings have been sufficient, much like GError would
> allow.
>
> Regards,
> Daniel
>
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 11/22] qapi: add signal support to core QMP server
2011-03-07 13:53 ` Anthony Liguori
@ 2011-03-07 14:36 ` Stefan Hajnoczi
2011-03-07 14:41 ` Anthony Liguori
0 siblings, 1 reply; 106+ messages in thread
From: Stefan Hajnoczi @ 2011-03-07 14:36 UTC (permalink / raw)
To: Anthony Liguori
Cc: qemu-devel, Adam Litke, Anthony Liguori, Markus Armbruster,
Luiz Capitulino
On Mon, Mar 7, 2011 at 1:53 PM, Anthony Liguori <anthony@codemonkey.ws> wrote:
> On 03/07/2011 07:21 AM, Stefan Hajnoczi wrote:
>>
>> On Mon, Mar 7, 2011 at 1:22 AM, Anthony Liguori<aliguori@us.ibm.com>
>> wrote:
>>
>>>
>>> diff --git a/qmp-core.h b/qmp-core.h
>>> index e3235ec..5ce02f7 100644
>>> --- a/qmp-core.h
>>> +++ b/qmp-core.h
>>> @@ -21,10 +21,65 @@ typedef struct QmpState QmpState;
>>> typedef void (QmpCommandFunc)(const QDict *, QObject **, Error **);
>>> typedef void (QmpStatefulCommandFunc)(QmpState *qmp__sess, const QDict
>>> *, QObject **, Error **);
>>>
>>> +typedef struct QmpSlot
>>> +{
>>> + int handle;
>>> + void *func;
>>>
>>
>> This should be a void (*func)()
>
> Technically void (*)() is an obsolete type in standard C.
>
> I can switch to void (*)(void) but it requires casting and requires a
> typeof() :-/
You're right, void (*)(void) is the proper form for a general function
pointer. I think doing this in a portable way is worthwhile.
Stefan
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 13/22] qapi: add code generators for QMP command marshaling
2011-03-07 13:44 ` Anthony Liguori
@ 2011-03-07 14:38 ` Stefan Hajnoczi
0 siblings, 0 replies; 106+ messages in thread
From: Stefan Hajnoczi @ 2011-03-07 14:38 UTC (permalink / raw)
To: Anthony Liguori
Cc: qemu-devel, Adam Litke, Markus Armbruster, Luiz Capitulino
On Mon, Mar 7, 2011 at 1:44 PM, Anthony Liguori <anthony@codemonkey.ws> wrote:
> On 03/07/2011 07:27 AM, Stefan Hajnoczi wrote:
>>
>> On Mon, Mar 7, 2011 at 1:22 AM, Anthony Liguori<aliguori@us.ibm.com>
>> wrote:
>>
>>>
>>> +def print_definition(name, required, optional, retval):
>>>
>>
>> This function is pretty long. Is there a logical way to split it up
>> into several smaller meaningful functions?
>>
>
> It seems long because of the code generation but there's really not an awful
> lot of logic in it.
>
> It might be worth spending a little time looking at a template system. I'm
> not terribly familiar with any of the popular ones and I think they usually
> focus on HTML generation but it might help readability.
There's a really simple one built into Python:
http://docs.python.org/release/2.5.4/lib/node40.html
Stefan
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 11/22] qapi: add signal support to core QMP server
2011-03-07 14:36 ` Stefan Hajnoczi
@ 2011-03-07 14:41 ` Anthony Liguori
0 siblings, 0 replies; 106+ messages in thread
From: Anthony Liguori @ 2011-03-07 14:41 UTC (permalink / raw)
To: Stefan Hajnoczi
Cc: Markus Armbruster, Luiz Capitulino, qemu-devel, Adam Litke
On 03/07/2011 08:36 AM, Stefan Hajnoczi wrote:
>
>> Technically void (*)() is an obsolete type in standard C.
>>
>> I can switch to void (*)(void) but it requires casting and requires a
>> typeof() :-/
>>
> You're right, void (*)(void) is the proper form for a general function
> pointer. I think doing this in a portable way is worthwhile.
>
Yeah, using a function pointer is definitely better than using a void
*. We'll just have to live with the use of typeof() though which
fortunately is supported in some form by basically every compiler out there.
Regards,
Anthony Liguori
> Stefan
>
>
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 00/22] QAPI Round 1
2011-03-07 1:22 [Qemu-devel] [PATCH 00/22] QAPI Round 1 Anthony Liguori
` (25 preceding siblings ...)
2011-03-07 1:29 ` [Qemu-devel] [PATCH] qapi: add libqmp.c and libqmp.h Anthony Liguori
@ 2011-03-07 15:08 ` Stefan Hajnoczi
2011-03-07 15:59 ` Anthony Liguori
2011-03-07 20:59 ` Anthony Liguori
26 siblings, 2 replies; 106+ messages in thread
From: Stefan Hajnoczi @ 2011-03-07 15:08 UTC (permalink / raw)
To: Anthony Liguori
Cc: Markus Armbruster, Luiz Capitulino, qemu-devel, Adam Litke
On Mon, Mar 7, 2011 at 1:22 AM, Anthony Liguori <aliguori@us.ibm.com> wrote:
> More information about QAPI can be found on the wiki:
>
> http://wiki.qemu.org/Features/QAPI
Thanks for the good documentation. A few thoughts:
A "Naming Conventions" section would be a helpful summary of the
different entities that .json schemas describe (events, enums, struct,
functions) and what format their names need to conform to. It seems
like C identifier rules apply but you also used '-' in names.
The *optional notation for struct fields could also be used for
function arguments instead of using a separate dict there. I'm
suggesting this for consistency because users currently need to learn
two ways of expressing optional elements.
The signals and slots magic makes sense when looking at the patches,
but I wasn't confident about their semantics just by reading the
documentation. I had a vague idea about why the Event struct is
needed but didn't fully understand. Perhaps it's just me but I wanted
to mention it in case you want to polish up that section.
How do async commands work? You mentioned them when talking about
QAPI but it's not obvious to me that there is any "native" support for
async commands?
Stefan
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 00/22] QAPI Round 1
2011-03-07 15:08 ` [Qemu-devel] [PATCH 00/22] QAPI Round 1 Stefan Hajnoczi
@ 2011-03-07 15:59 ` Anthony Liguori
2011-03-08 11:12 ` Avi Kivity
2011-03-07 20:59 ` Anthony Liguori
1 sibling, 1 reply; 106+ messages in thread
From: Anthony Liguori @ 2011-03-07 15:59 UTC (permalink / raw)
To: Stefan Hajnoczi
Cc: qemu-devel, Adam Litke, Anthony Liguori, Markus Armbruster,
Luiz Capitulino
On 03/07/2011 09:08 AM, Stefan Hajnoczi wrote:
> On Mon, Mar 7, 2011 at 1:22 AM, Anthony Liguori<aliguori@us.ibm.com> wrote:
>> More information about QAPI can be found on the wiki:
>>
>> http://wiki.qemu.org/Features/QAPI
> Thanks for the good documentation. A few thoughts:
>
> A "Naming Conventions" section would be a helpful summary of the
> different entities that .json schemas describe (events, enums, struct,
> functions) and what format their names need to conform to. It seems
> like C identifier rules apply but you also used '-' in names.
Yes, this was on my TODO for qmp-schema.json and I've just not gotten
around to it. I'll do it for the next version.
> The *optional notation for struct fields could also be used for
> function arguments instead of using a separate dict there. I'm
> suggesting this for consistency because users currently need to learn
> two ways of expressing optional elements.
Yeah, I've been thinking the same.
> The signals and slots magic makes sense when looking at the patches,
> but I wasn't confident about their semantics just by reading the
> documentation. I had a vague idea about why the Event struct is
> needed but didn't fully understand. Perhaps it's just me but I wanted
> to mention it in case you want to polish up that section.
Okay, I'll add more docs.
> How do async commands work? You mentioned them when talking about
> QAPI but it's not obvious to me that there is any "native" support for
> async commands?
Async commands are interesting..
Generating async commands with QAPI is easy. Errors are propagated now
so the callback would just need to take the return value and error
type. It would need to be a special callback type for each function but
that's easy with the code generator.
Async commands are problematic from the client perspective though. I
think there are two relevant use-cases for client software. There's the
probably most dominant, I don't really care about small pauses users who
are going to be executing the RPCs synchronously either using libqmp or
Python. They may use threading to have some parallelism but generally,
the expectation is going to be that QEMU doesn't introduce too much
delay in processing a given RPC.
OTOH, there are users that will be purely event driven that will treat
every RPC asynchronously. In both cases, it's more or less
all-or-nothing. Having some commands delay for really long periods of
time means that you either force users to treat some commands specially,
or you force all users into an event driven model.
And of course, if you have async commands, you need to cancel commands,
and then the context is within a single QMP session which means that you
need to figure out what to do if you drop the session while an async
command is executing. For instance, if a management tool executes the
migrate command, and it's implemented as an async command, if the
management tool loses it's connection, should migration be automatically
cancelled?
I'm really on the fence about async commands. At the moment, I'm
leaning towards just not every implementing them.
Regards,
Anthony Liguori
> Stefan
>
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 00/22] QAPI Round 1
2011-03-07 15:08 ` [Qemu-devel] [PATCH 00/22] QAPI Round 1 Stefan Hajnoczi
2011-03-07 15:59 ` Anthony Liguori
@ 2011-03-07 20:59 ` Anthony Liguori
2011-03-07 22:00 ` Stefan Hajnoczi
1 sibling, 1 reply; 106+ messages in thread
From: Anthony Liguori @ 2011-03-07 20:59 UTC (permalink / raw)
To: Stefan Hajnoczi
Cc: qemu-devel, Adam Litke, Markus Armbruster, Luiz Capitulino
On 03/07/2011 09:08 AM, Stefan Hajnoczi wrote:
> The signals and slots magic makes sense when looking at the patches,
> but I wasn't confident about their semantics just by reading the
> documentation. I had a vague idea about why the Event struct is
> needed but didn't fully understand. Perhaps it's just me but I wanted
> to mention it in case you want to polish up that section.
This is the problem with writing documentation, it seems clear to me :-/
I'm not sure what bit of magic you're referring to. Are you talking
about the general rationale for why they they need to exist? Or is it
the way that they're supported with macro functions that seems confusing?
Regards,
Anthony Liguori
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 00/22] QAPI Round 1
2011-03-07 20:59 ` Anthony Liguori
@ 2011-03-07 22:00 ` Stefan Hajnoczi
0 siblings, 0 replies; 106+ messages in thread
From: Stefan Hajnoczi @ 2011-03-07 22:00 UTC (permalink / raw)
To: Anthony Liguori
Cc: qemu-devel, Adam Litke, Markus Armbruster, Luiz Capitulino
On Mon, Mar 7, 2011 at 8:59 PM, Anthony Liguori <anthony@codemonkey.ws> wrote:
> On 03/07/2011 09:08 AM, Stefan Hajnoczi wrote:
>>
>> The signals and slots magic makes sense when looking at the patches,
>> but I wasn't confident about their semantics just by reading the
>> documentation. I had a vague idea about why the Event struct is
>> needed but didn't fully understand. Perhaps it's just me but I wanted
>> to mention it in case you want to polish up that section.
>
> This is the problem with writing documentation, it seems clear to me :-/
>
> I'm not sure what bit of magic you're referring to. Are you talking about
> the general rationale for why they they need to exist? Or is it the way
> that they're supported with macro functions that seems confusing?
The macros themselves are perfectly clear and I kind of suspected what
you were doing when reading the docs. It's just that the docs alone
didn't fully make me understand the point of the *Event structures.
Maybe it's just me :).
Stefan
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 00/22] QAPI Round 1
2011-03-07 15:59 ` Anthony Liguori
@ 2011-03-08 11:12 ` Avi Kivity
2011-03-08 13:35 ` Anthony Liguori
0 siblings, 1 reply; 106+ messages in thread
From: Avi Kivity @ 2011-03-08 11:12 UTC (permalink / raw)
To: Anthony Liguori
Cc: Adam Litke, Anthony Liguori, Stefan Hajnoczi, qemu-devel,
Luiz Capitulino, Markus Armbruster
On 03/07/2011 05:59 PM, Anthony Liguori wrote:
>
>> How do async commands work? You mentioned them when talking about
>> QAPI but it's not obvious to me that there is any "native" support for
>> async commands?
>
> Async commands are interesting..
Would there be anything in them other than starting each command in its
own thread? If it then drops the right locks it can execute in parallel
with other commands, if it doesn't, then it's synchronous (and
presumably doesn't depend on external or guest events).
>
> Generating async commands with QAPI is easy. Errors are propagated
> now so the callback would just need to take the return value and error
> type. It would need to be a special callback type for each function
> but that's easy with the code generator.
>
> Async commands are problematic from the client perspective though. I
> think there are two relevant use-cases for client software. There's
> the probably most dominant, I don't really care about small pauses
> users who are going to be executing the RPCs synchronously either
> using libqmp or Python. They may use threading to have some
> parallelism but generally, the expectation is going to be that QEMU
> doesn't introduce too much delay in processing a given RPC.
It's more for intrinsically long-running commands like migration. These
can be split into a start-migration and cancel-migration command (and it
also helps for querying status, which doesn't follow naturally for an
asychronous command).
>
> OTOH, there are users that will be purely event driven that will treat
> every RPC asynchronously. In both cases, it's more or less
> all-or-nothing. Having some commands delay for really long periods of
> time means that you either force users to treat some commands
> specially, or you force all users into an event driven model.
>
> And of course, if you have async commands, you need to cancel
> commands, and then the context is within a single QMP session which
> means that you need to figure out what to do if you drop the session
> while an async command is executing. For instance, if a management
> tool executes the migrate command, and it's implemented as an async
> command, if the management tool loses it's connection, should
> migration be automatically cancelled?
Sounds reasonable. The rules could be:
- a command (all commands are async) takes a ref on the monitor context
- if the session is dropped, call the current command's cancel callback
as a result, a dropped session causes the command to either cancel or
complete.
> I'm really on the fence about async commands. At the moment, I'm
> leaning towards just not every implementing them.
Do we have a list of candidates for async commands?
--
error compiling committee.c: too many arguments to function
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 00/22] QAPI Round 1
2011-03-08 11:12 ` Avi Kivity
@ 2011-03-08 13:35 ` Anthony Liguori
2011-03-08 13:45 ` Avi Kivity
0 siblings, 1 reply; 106+ messages in thread
From: Anthony Liguori @ 2011-03-08 13:35 UTC (permalink / raw)
To: Avi Kivity
Cc: Stefan Hajnoczi, Luiz Capitulino, qemu-devel, Markus Armbruster,
Adam Litke
On 03/08/2011 05:12 AM, Avi Kivity wrote:
> On 03/07/2011 05:59 PM, Anthony Liguori wrote:
>>
>>> How do async commands work? You mentioned them when talking about
>>> QAPI but it's not obvious to me that there is any "native" support for
>>> async commands?
>>
>> Async commands are interesting..
>
> Would there be anything in them other than starting each command in
> its own thread? If it then drops the right locks it can execute in
> parallel with other commands, if it doesn't, then it's synchronous
> (and presumably doesn't depend on external or guest events).
I've implemented them (for virt-agent) and I'll have it in v2 of round 1.
I use callbacks. If a function is marked to be async, it generates a
signature like:
typedef void (GuestViewFileCompletionFunc)(void *qmp__opaque, const char
* qmp__retval, Error *qmp__err);
void qmp_guest_view_file(const char * filename, Error **err,
GuestViewFileCompletionFunc *qmp__cc, void *qmp__opaque);
The handler just needs to squirrel away qmp__cc and qmp__opaque and call
it whenever the command completes.
>>
>> OTOH, there are users that will be purely event driven that will
>> treat every RPC asynchronously. In both cases, it's more or less
>> all-or-nothing. Having some commands delay for really long periods
>> of time means that you either force users to treat some commands
>> specially, or you force all users into an event driven model.
>>
>> And of course, if you have async commands, you need to cancel
>> commands, and then the context is within a single QMP session which
>> means that you need to figure out what to do if you drop the session
>> while an async command is executing. For instance, if a management
>> tool executes the migrate command, and it's implemented as an async
>> command, if the management tool loses it's connection, should
>> migration be automatically cancelled?
>
> Sounds reasonable. The rules could be:
> - a command (all commands are async) takes a ref on the monitor context
> - if the session is dropped, call the current command's cancel callback
>
> as a result, a dropped session causes the command to either cancel or
> complete.
Yup, this is pretty much what I'm now doing.
>> I'm really on the fence about async commands. At the moment, I'm
>> leaning towards just not every implementing them.
>
> Do we have a list of candidates for async commands?
Yeah, all of the guest commands need to be asynchronous so we definitely
need to support it.
Regards,
Anthony Liguori
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 00/22] QAPI Round 1
2011-03-08 13:35 ` Anthony Liguori
@ 2011-03-08 13:45 ` Avi Kivity
2011-03-08 13:54 ` Anthony Liguori
0 siblings, 1 reply; 106+ messages in thread
From: Avi Kivity @ 2011-03-08 13:45 UTC (permalink / raw)
To: Anthony Liguori
Cc: Stefan Hajnoczi, Luiz Capitulino, qemu-devel, Markus Armbruster,
Adam Litke
On 03/08/2011 03:35 PM, Anthony Liguori wrote:
> On 03/08/2011 05:12 AM, Avi Kivity wrote:
>> On 03/07/2011 05:59 PM, Anthony Liguori wrote:
>>>
>>>> How do async commands work? You mentioned them when talking about
>>>> QAPI but it's not obvious to me that there is any "native" support for
>>>> async commands?
>>>
>>> Async commands are interesting..
>>
>> Would there be anything in them other than starting each command in
>> its own thread? If it then drops the right locks it can execute in
>> parallel with other commands, if it doesn't, then it's synchronous
>> (and presumably doesn't depend on external or guest events).
>
> I've implemented them (for virt-agent) and I'll have it in v2 of round 1.
>
> I use callbacks. If a function is marked to be async, it generates a
> signature like:
>
> typedef void (GuestViewFileCompletionFunc)(void *qmp__opaque, const
> char * qmp__retval, Error *qmp__err);
> void qmp_guest_view_file(const char * filename, Error **err,
> GuestViewFileCompletionFunc *qmp__cc, void *qmp__opaque);
>
> The handler just needs to squirrel away qmp__cc and qmp__opaque and
> call it whenever the command completes.
Okay. My preference would be threads, but we can always start with one
model and use adapters to switch to another.
(and instead of an opaque/func pair, I'd do
typedef struct GuestViewFileCompletion {
void (*complete)(struct GuestViewFileCompletion *gvfc);
} GuestViewFileCompletion;
so there's less squirrelling and more type-safetying.
)
(and gah, do we really need a vfs/rpc in qemu?)
--
error compiling committee.c: too many arguments to function
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 00/22] QAPI Round 1
2011-03-08 13:45 ` Avi Kivity
@ 2011-03-08 13:54 ` Anthony Liguori
2011-03-08 14:00 ` Avi Kivity
0 siblings, 1 reply; 106+ messages in thread
From: Anthony Liguori @ 2011-03-08 13:54 UTC (permalink / raw)
To: Avi Kivity
Cc: Adam Litke, Stefan Hajnoczi, qemu-devel, Markus Armbruster,
Luiz Capitulino
On 03/08/2011 07:45 AM, Avi Kivity wrote:
> On 03/08/2011 03:35 PM, Anthony Liguori wrote:
>> On 03/08/2011 05:12 AM, Avi Kivity wrote:
>>> On 03/07/2011 05:59 PM, Anthony Liguori wrote:
>>>>
>>>>> How do async commands work? You mentioned them when talking about
>>>>> QAPI but it's not obvious to me that there is any "native" support
>>>>> for
>>>>> async commands?
>>>>
>>>> Async commands are interesting..
>>>
>>> Would there be anything in them other than starting each command in
>>> its own thread? If it then drops the right locks it can execute in
>>> parallel with other commands, if it doesn't, then it's synchronous
>>> (and presumably doesn't depend on external or guest events).
>>
>> I've implemented them (for virt-agent) and I'll have it in v2 of
>> round 1.
>>
>> I use callbacks. If a function is marked to be async, it generates a
>> signature like:
>>
>> typedef void (GuestViewFileCompletionFunc)(void *qmp__opaque, const
>> char * qmp__retval, Error *qmp__err);
>> void qmp_guest_view_file(const char * filename, Error **err,
>> GuestViewFileCompletionFunc *qmp__cc, void *qmp__opaque);
>>
>> The handler just needs to squirrel away qmp__cc and qmp__opaque and
>> call it whenever the command completes.
>
> Okay. My preference would be threads, but we can always start with
> one model and use adapters to switch to another.
>
> (and instead of an opaque/func pair, I'd do
>
> typedef struct GuestViewFileCompletion {
> void (*complete)(struct GuestViewFileCompletion *gvfc);
> } GuestViewFileCompletion;
>
> so there's less squirrelling and more type-safetying.
> )
The opaque is a bit nicer to use because instead of having to generate
another structure with this embedded, I can just pass the command state
directly.
That said, once I get this all working nicely, I'll take a look at using
a structure and see whether it makes it uglier. I do prefer the
embedded struct.
> (and gah, do we really need a vfs/rpc in qemu?)
Fun, eh :-) Unfortunately, our friends at VMware provide a
VixVM_CopyFileFromGuestToHost API so there's an expectation that we
provide a similar interface.
Regards,
Anthony Liguori
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 00/22] QAPI Round 1
2011-03-08 13:54 ` Anthony Liguori
@ 2011-03-08 14:00 ` Avi Kivity
2011-03-08 14:10 ` Anthony Liguori
0 siblings, 1 reply; 106+ messages in thread
From: Avi Kivity @ 2011-03-08 14:00 UTC (permalink / raw)
To: Anthony Liguori
Cc: Adam Litke, Stefan Hajnoczi, qemu-devel, Markus Armbruster,
Luiz Capitulino
On 03/08/2011 03:54 PM, Anthony Liguori wrote:
>
>> (and gah, do we really need a vfs/rpc in qemu?)
>
> Fun, eh :-) Unfortunately, our friends at VMware provide a
> VixVM_CopyFileFromGuestToHost API so there's an expectation that we
> provide a similar interface.
>
Yes, but do we have to terminate it in qemu?
Especially as I expect we don't really want to copy a file to the host,
but instead stream it to some other channel.
--
error compiling committee.c: too many arguments to function
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 00/22] QAPI Round 1
2011-03-08 14:00 ` Avi Kivity
@ 2011-03-08 14:10 ` Anthony Liguori
2011-03-08 14:17 ` Avi Kivity
0 siblings, 1 reply; 106+ messages in thread
From: Anthony Liguori @ 2011-03-08 14:10 UTC (permalink / raw)
To: Avi Kivity
Cc: Stefan Hajnoczi, Luiz Capitulino, Adam Litke, Markus Armbruster,
qemu-devel
On 03/08/2011 08:00 AM, Avi Kivity wrote:
> On 03/08/2011 03:54 PM, Anthony Liguori wrote:
>>
>>> (and gah, do we really need a vfs/rpc in qemu?)
>>
>> Fun, eh :-) Unfortunately, our friends at VMware provide a
>> VixVM_CopyFileFromGuestToHost API so there's an expectation that we
>> provide a similar interface.
>>
>
> Yes, but do we have to terminate it in qemu?
No, I'm in the process of writing up my latest proposal.
The idea is pretty simple. QAPI generates code for libqmp that takes
native arguments for a command and generates a QObject. It also
generates code for QEMU that takes a QObject and generates native
arguments to pass to a function.
For guest commands, we combine the two such that we unmarshal the
incoming QObject to native arguments, then pass it to another function
that marshals the arguments to a QObject. The QObject is then passed to
the guest-agent which uses the same generated code as QEMU to unmarshal
the qobject to native arguments and dispatch to a function.
That means the only new code we need for the guest agent is the
JSON-over-virtio-serial transport. To implement guest commands, we just
add the command to the schema, implement the native arguments version in
guest-agent, and that's it.
QEMU will buffer all input and output to the guest acting as a first
line of defence from a security PoV. That means that the guest doesn't
get to talk directly to the management tools which removes that as a
direct attack surface.
The nature of QEMU is such that if we do tagging correctly, we can also
support live migration transparently to the guest too.
Regards,
Anthony Liguori
> Especially as I expect we don't really want to copy a file to the
> host, but instead stream it to some other channel.
>
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 00/22] QAPI Round 1
2011-03-08 14:10 ` Anthony Liguori
@ 2011-03-08 14:17 ` Avi Kivity
2011-03-08 14:20 ` Anthony Liguori
2011-03-08 14:38 ` Anthony Liguori
0 siblings, 2 replies; 106+ messages in thread
From: Avi Kivity @ 2011-03-08 14:17 UTC (permalink / raw)
To: Anthony Liguori
Cc: Stefan Hajnoczi, Luiz Capitulino, Adam Litke, Markus Armbruster,
qemu-devel
On 03/08/2011 04:10 PM, Anthony Liguori wrote:
> On 03/08/2011 08:00 AM, Avi Kivity wrote:
>> On 03/08/2011 03:54 PM, Anthony Liguori wrote:
>>>
>>>> (and gah, do we really need a vfs/rpc in qemu?)
>>>
>>> Fun, eh :-) Unfortunately, our friends at VMware provide a
>>> VixVM_CopyFileFromGuestToHost API so there's an expectation that we
>>> provide a similar interface.
>>>
>>
>> Yes, but do we have to terminate it in qemu?
>
> No, I'm in the process of writing up my latest proposal.
>
> The idea is pretty simple. QAPI generates code for libqmp that takes
> native arguments for a command and generates a QObject. It also
> generates code for QEMU that takes a QObject and generates native
> arguments to pass to a function.
>
> For guest commands, we combine the two such that we unmarshal the
> incoming QObject to native arguments, then pass it to another function
> that marshals the arguments to a QObject. The QObject is then passed
> to the guest-agent which uses the same generated code as QEMU to
> unmarshal the qobject to native arguments and dispatch to a function.
>
> That means the only new code we need for the guest agent is the
> JSON-over-virtio-serial transport. To implement guest commands, we
> just add the command to the schema, implement the native arguments
> version in guest-agent, and that's it.
>
> QEMU will buffer all input and output to the guest acting as a first
> line of defence from a security PoV. That means that the guest
> doesn't get to talk directly to the management tools which removes
> that as a direct attack surface.
>
> The nature of QEMU is such that if we do tagging correctly, we can
> also support live migration transparently to the guest too.
Okay, do I understand correctly that qemu does not understand each
command individually? It just reads the schema and converts from one
rpc protocol to another (even if they are the same protocol)?
So: mgmt -> json -> qemu -> (qobject -> ) json -> guest (and back again).
If that's the case, I like it.
--
error compiling committee.c: too many arguments to function
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 00/22] QAPI Round 1
2011-03-08 14:17 ` Avi Kivity
@ 2011-03-08 14:20 ` Anthony Liguori
2011-03-08 14:38 ` Anthony Liguori
1 sibling, 0 replies; 106+ messages in thread
From: Anthony Liguori @ 2011-03-08 14:20 UTC (permalink / raw)
To: Avi Kivity
Cc: qemu-devel, Stefan Hajnoczi, Adam Litke, Markus Armbruster,
Luiz Capitulino
On 03/08/2011 08:17 AM, Avi Kivity wrote:
> On 03/08/2011 04:10 PM, Anthony Liguori wrote:
>> On 03/08/2011 08:00 AM, Avi Kivity wrote:
>>> On 03/08/2011 03:54 PM, Anthony Liguori wrote:
>>>>
>>>>> (and gah, do we really need a vfs/rpc in qemu?)
>>>>
>>>> Fun, eh :-) Unfortunately, our friends at VMware provide a
>>>> VixVM_CopyFileFromGuestToHost API so there's an expectation that we
>>>> provide a similar interface.
>>>>
>>>
>>> Yes, but do we have to terminate it in qemu?
>>
>> No, I'm in the process of writing up my latest proposal.
>>
>> The idea is pretty simple. QAPI generates code for libqmp that takes
>> native arguments for a command and generates a QObject. It also
>> generates code for QEMU that takes a QObject and generates native
>> arguments to pass to a function.
>>
>> For guest commands, we combine the two such that we unmarshal the
>> incoming QObject to native arguments, then pass it to another
>> function that marshals the arguments to a QObject. The QObject is
>> then passed to the guest-agent which uses the same generated code as
>> QEMU to unmarshal the qobject to native arguments and dispatch to a
>> function.
>>
>> That means the only new code we need for the guest agent is the
>> JSON-over-virtio-serial transport. To implement guest commands, we
>> just add the command to the schema, implement the native arguments
>> version in guest-agent, and that's it.
>>
>> QEMU will buffer all input and output to the guest acting as a first
>> line of defence from a security PoV. That means that the guest
>> doesn't get to talk directly to the management tools which removes
>> that as a direct attack surface.
>>
>> The nature of QEMU is such that if we do tagging correctly, we can
>> also support live migration transparently to the guest too.
>
> Okay, do I understand correctly that qemu does not understand each
> command individually? It just reads the schema and converts from one
> rpc protocol to another (even if they are the same protocol)?
>
> So: mgmt -> json -> qemu -> (qobject -> ) json -> guest (and back again).
Exactly.
>
> If that's the case, I like it.
QEMU will have the ability to execute commands to the guest agent and
that will be used by something like HMP to provide friendly versions of
the interfaces. However, my long term plan is to move HMP out of core
QEMU and the QAPI refactoring is making the HMP code not depend on any
core QEMU functions, only on the QMP interface.
Regards,
Anthony Liguori
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 00/22] QAPI Round 1
2011-03-08 14:17 ` Avi Kivity
2011-03-08 14:20 ` Anthony Liguori
@ 2011-03-08 14:38 ` Anthony Liguori
2011-03-08 14:52 ` Avi Kivity
1 sibling, 1 reply; 106+ messages in thread
From: Anthony Liguori @ 2011-03-08 14:38 UTC (permalink / raw)
To: Avi Kivity
Cc: Stefan Hajnoczi, qemu-devel, Markus Armbruster, Michael D Roth,
Adam Litke, Luiz Capitulino
On 03/08/2011 08:17 AM, Avi Kivity wrote:
>> No, I'm in the process of writing up my latest proposal.
>>
>> The idea is pretty simple. QAPI generates code for libqmp that takes
>> native arguments for a command and generates a QObject. It also
>> generates code for QEMU that takes a QObject and generates native
>> arguments to pass to a function.
>>
>> For guest commands, we combine the two such that we unmarshal the
>> incoming QObject to native arguments, then pass it to another
>> function that marshals the arguments to a QObject. The QObject is
>> then passed to the guest-agent which uses the same generated code as
>> QEMU to unmarshal the qobject to native arguments and dispatch to a
>> function.
>>
>> That means the only new code we need for the guest agent is the
>> JSON-over-virtio-serial transport. To implement guest commands, we
>> just add the command to the schema, implement the native arguments
>> version in guest-agent, and that's it.
>>
>> QEMU will buffer all input and output to the guest acting as a first
>> line of defence from a security PoV. That means that the guest
>> doesn't get to talk directly to the management tools which removes
>> that as a direct attack surface.
>>
>> The nature of QEMU is such that if we do tagging correctly, we can
>> also support live migration transparently to the guest too.
>
http://wiki.qemu.org/Features/QAPI/GuestAgent
Regards,
Anthony Liguori
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 00/22] QAPI Round 1
2011-03-08 14:38 ` Anthony Liguori
@ 2011-03-08 14:52 ` Avi Kivity
2011-03-08 15:03 ` Anthony Liguori
0 siblings, 1 reply; 106+ messages in thread
From: Avi Kivity @ 2011-03-08 14:52 UTC (permalink / raw)
To: Anthony Liguori
Cc: Stefan Hajnoczi, qemu-devel, Markus Armbruster, Michael D Roth,
Adam Litke, Luiz Capitulino
On 03/08/2011 04:38 PM, Anthony Liguori wrote:
>
> http://wiki.qemu.org/Features/QAPI/GuestAgent
>
Nice. A couple of remarks.
We may want to use a different way of separating the namespaces -
enacapsulation:
{
'command': 'guest-command',
arguments: {
'command': 'read-file',
'arguments': {
'path': '/var/log/messages'
}
}
}
This serves to decouple the two protocols, which may have different
support lifetimes (hopefully not).
In addition to commands we may also implement a key-value store. A
large number of commands may be easy to implement using a single
key/value, and it's also easy to live migrate (key/values being stored
in but not understood by qemu).
--
error compiling committee.c: too many arguments to function
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 00/22] QAPI Round 1
2011-03-08 14:52 ` Avi Kivity
@ 2011-03-08 15:03 ` Anthony Liguori
2011-03-08 17:44 ` Avi Kivity
0 siblings, 1 reply; 106+ messages in thread
From: Anthony Liguori @ 2011-03-08 15:03 UTC (permalink / raw)
To: Avi Kivity
Cc: Stefan Hajnoczi, qemu-devel, Luiz Capitulino, Michael D Roth,
Adam Litke, Markus Armbruster
On 03/08/2011 08:52 AM, Avi Kivity wrote:
> On 03/08/2011 04:38 PM, Anthony Liguori wrote:
>>
>> http://wiki.qemu.org/Features/QAPI/GuestAgent
>>
>
> Nice. A couple of remarks.
>
> We may want to use a different way of separating the namespaces -
> enacapsulation:
>
> {
> 'command': 'guest-command',
> arguments: {
> 'command': 'read-file',
> 'arguments': {
> 'path': '/var/log/messages'
> }
> }
> }
>
> This serves to decouple the two protocols, which may have different
> support lifetimes (hopefully not).
We could also have a more proper namespace in the protocol. Like:
{ 'execute': { 'namespace': 'guest', 'command': 'read-file' },
'arguments': { 'path': '/var/log/messages' } }
I'm not sure I see this as all that critical though. I sort of like the
idea of it being transparent to the client whether a guest agent
implements a command or QEMU does.
> In addition to commands we may also implement a key-value store. A
> large number of commands may be easy to implement using a single
> key/value, and it's also easy to live migrate (key/values being stored
> in but not understood by qemu).
Do you mean the guest querying QEMU for key-values? QMP doesn't have a
reverse direction RPC. It only has events which is one of the things
that makes live migration work nicely.
Regards,
Anthony Liguori
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 00/22] QAPI Round 1
2011-03-08 15:03 ` Anthony Liguori
@ 2011-03-08 17:44 ` Avi Kivity
2011-03-08 19:19 ` Anthony Liguori
0 siblings, 1 reply; 106+ messages in thread
From: Avi Kivity @ 2011-03-08 17:44 UTC (permalink / raw)
To: Anthony Liguori
Cc: Stefan Hajnoczi, qemu-devel, Luiz Capitulino, Michael D Roth,
Adam Litke, Markus Armbruster
On 03/08/2011 05:03 PM, Anthony Liguori wrote:
>> We may want to use a different way of separating the namespaces -
>> enacapsulation:
>>
>> {
>> 'command': 'guest-command',
>> arguments: {
>> 'command': 'read-file',
>> 'arguments': {
>> 'path': '/var/log/messages'
>> }
>> }
>> }
>>
>> This serves to decouple the two protocols, which may have different
>> support lifetimes (hopefully not).
>
>
> We could also have a more proper namespace in the protocol. Like:
>
> { 'execute': { 'namespace': 'guest', 'command': 'read-file' },
> 'arguments': { 'path': '/var/log/messages' } }
Yeah, looks better.
>
> I'm not sure I see this as all that critical though. I sort of like
> the idea of it being transparent to the client whether a guest agent
> implements a command or QEMU does.
A qemu command is guaranteed to be executed faithfully, modulo bugs and
runtime errors. A guest command is guaranteed to be executed (or not)
in the most hostile way possible to the management agent. It's
important to make that clear in the ABI.
We should also have separate schemas, one including the other. So we
can have a monitor channel that only executes guest commands, for instance.
>
>> In addition to commands we may also implement a key-value store. A
>> large number of commands may be easy to implement using a single
>> key/value, and it's also easy to live migrate (key/values being
>> stored in but not understood by qemu).
>
> Do you mean the guest querying QEMU for key-values? QMP doesn't have
> a reverse direction RPC. It only has events which is one of the
> things that makes live migration work nicely.
Both the guest and the management agent (and both can listen for
events). I don't see why guest->qemu RPC is problematic for migration -
at least when qemu terminates it.
--
error compiling committee.c: too many arguments to function
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 00/22] QAPI Round 1
2011-03-08 17:44 ` Avi Kivity
@ 2011-03-08 19:19 ` Anthony Liguori
2011-03-09 8:51 ` Avi Kivity
0 siblings, 1 reply; 106+ messages in thread
From: Anthony Liguori @ 2011-03-08 19:19 UTC (permalink / raw)
To: Avi Kivity
Cc: Stefan Hajnoczi, Markus Armbruster, qemu-devel, Michael D Roth,
Adam Litke, Luiz Capitulino
On 03/08/2011 11:44 AM, Avi Kivity wrote:
>>
>> I'm not sure I see this as all that critical though. I sort of like
>> the idea of it being transparent to the client whether a guest agent
>> implements a command or QEMU does.
>
> A qemu command is guaranteed to be executed faithfully, modulo bugs
> and runtime errors. A guest command is guaranteed to be executed (or
> not) in the most hostile way possible to the management agent. It's
> important to make that clear in the ABI.
>
> We should also have separate schemas, one including the other. So we
> can have a monitor channel that only executes guest commands, for
> instance.
I need to think about about this a little more. Since libqmp would hide
most of this, the user-visible details wouldn't really change. So while
I understand and agree with your point, I don't think just changing the
protocol like this is going to achieve the goal.
>>> In addition to commands we may also implement a key-value store. A
>>> large number of commands may be easy to implement using a single
>>> key/value, and it's also easy to live migrate (key/values being
>>> stored in but not understood by qemu).
>>
>> Do you mean the guest querying QEMU for key-values? QMP doesn't have
>> a reverse direction RPC. It only has events which is one of the
>> things that makes live migration work nicely.
>
> Both the guest and the management agent (and both can listen for
> events). I don't see why guest->qemu RPC is problematic for migration
> - at least when qemu terminates it.
If it's terminated in QEMU, it's fine, but then it's not QMP anymore.
Let me think about whether there's a way to achieve this without a
guest->qemu RPC.
Regards,
Anthony Liguori
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 00/22] QAPI Round 1
2011-03-08 19:19 ` Anthony Liguori
@ 2011-03-09 8:51 ` Avi Kivity
2011-03-09 13:12 ` Anthony Liguori
0 siblings, 1 reply; 106+ messages in thread
From: Avi Kivity @ 2011-03-09 8:51 UTC (permalink / raw)
To: Anthony Liguori
Cc: Stefan Hajnoczi, Markus Armbruster, qemu-devel, Michael D Roth,
Adam Litke, Luiz Capitulino
On 03/08/2011 09:19 PM, Anthony Liguori wrote:
>> Both the guest and the management agent (and both can listen for
>> events). I don't see why guest->qemu RPC is problematic for
>> migration - at least when qemu terminates it.
>
>
> If it's terminated in QEMU, it's fine, but then it's not QMP anymore.
> Let me think about whether there's a way to achieve this without a
> guest->qemu RPC.
Why not?
{ execute: 'write-keystore' arguments: { 'key': 'foo', 'value': 'bar' } }
etc.
--
error compiling committee.c: too many arguments to function
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 00/22] QAPI Round 1
2011-03-09 8:51 ` Avi Kivity
@ 2011-03-09 13:12 ` Anthony Liguori
2011-03-09 13:14 ` Avi Kivity
0 siblings, 1 reply; 106+ messages in thread
From: Anthony Liguori @ 2011-03-09 13:12 UTC (permalink / raw)
To: Avi Kivity
Cc: qemu-devel, Stefan Hajnoczi, Markus Armbruster, Luiz Capitulino,
Michael D Roth, Adam Litke
On 03/09/2011 02:51 AM, Avi Kivity wrote:
> On 03/08/2011 09:19 PM, Anthony Liguori wrote:
>>> Both the guest and the management agent (and both can listen for
>>> events). I don't see why guest->qemu RPC is problematic for
>>> migration - at least when qemu terminates it.
>>
>>
>> If it's terminated in QEMU, it's fine, but then it's not QMP
>> anymore. Let me think about whether there's a way to achieve this
>> without a guest->qemu RPC.
>
> Why not?
>
> { execute: 'write-keystore' arguments: { 'key': 'foo', 'value': 'bar' } }
This is coming from the guest? QMP doesn't do bidirectional RPC today.
It could, but that is a fundamental change in the protocol.
Regards,
Anthony Liguori
> etc.
>
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 00/22] QAPI Round 1
2011-03-09 13:12 ` Anthony Liguori
@ 2011-03-09 13:14 ` Avi Kivity
2011-03-09 13:51 ` Anthony Liguori
2011-03-09 13:51 ` Michael Roth
0 siblings, 2 replies; 106+ messages in thread
From: Avi Kivity @ 2011-03-09 13:14 UTC (permalink / raw)
To: Anthony Liguori
Cc: qemu-devel, Stefan Hajnoczi, Markus Armbruster, Luiz Capitulino,
Michael D Roth, Adam Litke
On 03/09/2011 03:12 PM, Anthony Liguori wrote:
> On 03/09/2011 02:51 AM, Avi Kivity wrote:
>> On 03/08/2011 09:19 PM, Anthony Liguori wrote:
>>>> Both the guest and the management agent (and both can listen for
>>>> events). I don't see why guest->qemu RPC is problematic for
>>>> migration - at least when qemu terminates it.
>>>
>>>
>>> If it's terminated in QEMU, it's fine, but then it's not QMP
>>> anymore. Let me think about whether there's a way to achieve this
>>> without a guest->qemu RPC.
>>
>> Why not?
>>
>> { execute: 'write-keystore' arguments: { 'key': 'foo', 'value': 'bar'
>> } }
>
> This is coming from the guest?
Yes.
> QMP doesn't do bidirectional RPC today. It could, but that is a
> fundamental change in the protocol.
>
Could use a separate channel for talking to qemu.
--
error compiling committee.c: too many arguments to function
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 14/22] qapi: add query-version QMP command
2011-03-07 13:41 ` Anthony Liguori
@ 2011-03-09 13:28 ` Avi Kivity
2011-03-09 13:44 ` Anthony Liguori
0 siblings, 1 reply; 106+ messages in thread
From: Avi Kivity @ 2011-03-09 13:28 UTC (permalink / raw)
To: Anthony Liguori
Cc: Stefan Hajnoczi, Luiz Capitulino, qemu-devel, Markus Armbruster,
Adam Litke
On 03/07/2011 03:41 PM, Anthony Liguori wrote:
> On 03/07/2011 07:35 AM, Stefan Hajnoczi wrote:
>> On Mon, Mar 7, 2011 at 1:22 AM, Anthony Liguori<aliguori@us.ibm.com>
>> wrote:
>>> diff --git a/qmp-schema.json b/qmp-schema.json
>>> index e69de29..b343f5e 100644
>>> --- a/qmp-schema.json
>>> +++ b/qmp-schema.json
>>> @@ -0,0 +1,38 @@
>>> +# *-*- Mode: Python -*-*
>> By the way JSON does not seem to support comments (neither /* */ nor
>> #). So this comment feature you're using is an extension to JSON.
>
> Yeah, it's only loosely JSON as I don't use a JSON parser.
Goes kind of against all the buzzwords you're letting fly here...
--
error compiling committee.c: too many arguments to function
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 19/22] qapi: add QMP put-event command
2011-03-07 1:23 ` [Qemu-devel] [PATCH 19/22] qapi: add QMP put-event command Anthony Liguori
@ 2011-03-09 13:31 ` Avi Kivity
2011-03-09 13:48 ` Anthony Liguori
0 siblings, 1 reply; 106+ messages in thread
From: Avi Kivity @ 2011-03-09 13:31 UTC (permalink / raw)
To: Anthony Liguori
Cc: Markus Armbruster, Luiz Capitulino, qemu-devel, Adam Litke
On 03/07/2011 03:23 AM, Anthony Liguori wrote:
> This is needed for libqmp to support events. put-event is used to disconnect
> from signals.
>
> Signed-off-by: Anthony Liguori<aliguori@us.ibm.com>
>
> diff --git a/qmp-schema.json b/qmp-schema.json
> index 3f2dd4e..a13885f 100644
> --- a/qmp-schema.json
> +++ b/qmp-schema.json
> @@ -58,3 +58,18 @@
> # Since: 0.14.0
> ##
> [ 'qmp_capabilities', {}, {}, 'none' ]
> +
> +##
> +# @put_event:
> +#
> +# Disconnect a signal. This command is used to disconnect from a signal based
> +# on the handle returned by a signal accessor.
> +#
> +# @tag: the handle returned by a signal accessor.
> +#
> +# Returns: Nothing on success.
> +# If @tag is not a valid handle, InvalidParameterValue
> +#
> +# Since: 0.15.0
> +##
> +[ 'put-event', {'tag': 'int'}, {}, 'none' ]
Why is tag an int? don't we use strings for command ids and similar?
Also could be better named, disconnect-event or unlisten-event.
--
error compiling committee.c: too many arguments to function
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 14/22] qapi: add query-version QMP command
2011-03-07 1:22 ` [Qemu-devel] [PATCH 14/22] qapi: add query-version QMP command Anthony Liguori
2011-03-07 13:35 ` Stefan Hajnoczi
@ 2011-03-09 13:36 ` Avi Kivity
2011-03-09 14:11 ` Anthony Liguori
1 sibling, 1 reply; 106+ messages in thread
From: Avi Kivity @ 2011-03-09 13:36 UTC (permalink / raw)
To: Anthony Liguori
Cc: Markus Armbruster, Luiz Capitulino, qemu-devel, Adam Litke
On 03/07/2011 03:22 AM, Anthony Liguori wrote:
> This is used internally by QMP. It's also a pretty good example of a typical
> command conversion.
>
> +##
> +{ 'VersionInfo': {'qemu': {'major': 'int', 'minor': 'int', 'micro': 'int'},
> + 'package': 'str'} }
> +
> +##
> +# @query-version:
> +#
> +# Returns the current version of QEMU.
> +#
> +# Returns: A @VersionInfo object describing the current version of QEMU.
> +#
> +# Since: 0.14.0
> +##
> +[ 'query-version', {}, {}, 'VersionInfo' ]
(just picking on a patch that has a bit of schema in it)
Something that can be added to the schema are default values for newly
added parameters. This way we can avoid an explosion of commands where
adding an optional parameter suffices; should be easier for the user to
pick the right command and easier for us to document and support.
--
error compiling committee.c: too many arguments to function
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 14/22] qapi: add query-version QMP command
2011-03-09 13:28 ` Avi Kivity
@ 2011-03-09 13:44 ` Anthony Liguori
2011-03-09 13:51 ` Avi Kivity
0 siblings, 1 reply; 106+ messages in thread
From: Anthony Liguori @ 2011-03-09 13:44 UTC (permalink / raw)
To: Avi Kivity
Cc: Adam Litke, Stefan Hajnoczi, qemu-devel, Markus Armbruster,
Luiz Capitulino
On 03/09/2011 07:28 AM, Avi Kivity wrote:
> On 03/07/2011 03:41 PM, Anthony Liguori wrote:
>> On 03/07/2011 07:35 AM, Stefan Hajnoczi wrote:
>>> On Mon, Mar 7, 2011 at 1:22 AM, Anthony
>>> Liguori<aliguori@us.ibm.com> wrote:
>>>> diff --git a/qmp-schema.json b/qmp-schema.json
>>>> index e69de29..b343f5e 100644
>>>> --- a/qmp-schema.json
>>>> +++ b/qmp-schema.json
>>>> @@ -0,0 +1,38 @@
>>>> +# *-*- Mode: Python -*-*
>>> By the way JSON does not seem to support comments (neither /* */ nor
>>> #). So this comment feature you're using is an extension to JSON.
>>
>> Yeah, it's only loosely JSON as I don't use a JSON parser.
>
> Goes kind of against all the buzzwords you're letting fly here...
The schema defines arguments in a dictionary because in QMP, the
argument order doesn't matter. But the argument order matters in C so I
need to use a custom parser to preserve dictionary order.
There's no way to do commenting in JSON and I really wanted to have
inline documentation.
But otherwise, it's valid JSON.
Regards,
Anthony Liguori
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 19/22] qapi: add QMP put-event command
2011-03-09 13:31 ` Avi Kivity
@ 2011-03-09 13:48 ` Anthony Liguori
2011-03-09 13:58 ` Avi Kivity
0 siblings, 1 reply; 106+ messages in thread
From: Anthony Liguori @ 2011-03-09 13:48 UTC (permalink / raw)
To: Avi Kivity; +Cc: qemu-devel, Adam Litke, Markus Armbruster, Luiz Capitulino
On 03/09/2011 07:31 AM, Avi Kivity wrote:
> On 03/07/2011 03:23 AM, Anthony Liguori wrote:
>> This is needed for libqmp to support events. put-event is used to
>> disconnect
>> from signals.
>>
>> Signed-off-by: Anthony Liguori<aliguori@us.ibm.com>
>>
>> diff --git a/qmp-schema.json b/qmp-schema.json
>> index 3f2dd4e..a13885f 100644
>> --- a/qmp-schema.json
>> +++ b/qmp-schema.json
>> @@ -58,3 +58,18 @@
>> # Since: 0.14.0
>> ##
>> [ 'qmp_capabilities', {}, {}, 'none' ]
>> +
>> +##
>> +# @put_event:
>> +#
>> +# Disconnect a signal. This command is used to disconnect from a
>> signal based
>> +# on the handle returned by a signal accessor.
>> +#
>> +# @tag: the handle returned by a signal accessor.
>> +#
>> +# Returns: Nothing on success.
>> +# If @tag is not a valid handle, InvalidParameterValue
>> +#
>> +# Since: 0.15.0
>> +##
>> +[ 'put-event', {'tag': 'int'}, {}, 'none' ]
>
> Why is tag an int?
It's a handle so the type doesn't matter as long as I can make sure
values are unique. ints are easier to work with because they don't
require memory allocation.
> don't we use strings for command ids and similar?
id's can be any valid JSON value.
But a handle is not the same thing as an id.
> Also could be better named, disconnect-event or unlisten-event.
I was going for symmetry with the signal accessors which are typically
in the format 'get-block-io-error-event'.
Maybe it would be better to do 'connect-block-io-error-event' and
'disconnect-event'?
Regards,
Anthony Liguori
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 00/22] QAPI Round 1
2011-03-09 13:14 ` Avi Kivity
@ 2011-03-09 13:51 ` Anthony Liguori
2011-03-09 13:55 ` Avi Kivity
2011-03-09 13:51 ` Michael Roth
1 sibling, 1 reply; 106+ messages in thread
From: Anthony Liguori @ 2011-03-09 13:51 UTC (permalink / raw)
To: Avi Kivity
Cc: Stefan Hajnoczi, qemu-devel, Luiz Capitulino, Michael D Roth,
Adam Litke, Markus Armbruster
On 03/09/2011 07:14 AM, Avi Kivity wrote:
> On 03/09/2011 03:12 PM, Anthony Liguori wrote:
>> On 03/09/2011 02:51 AM, Avi Kivity wrote:
>>> On 03/08/2011 09:19 PM, Anthony Liguori wrote:
>>>>> Both the guest and the management agent (and both can listen for
>>>>> events). I don't see why guest->qemu RPC is problematic for
>>>>> migration - at least when qemu terminates it.
>>>>
>>>>
>>>> If it's terminated in QEMU, it's fine, but then it's not QMP
>>>> anymore. Let me think about whether there's a way to achieve this
>>>> without a guest->qemu RPC.
>>>
>>> Why not?
>>>
>>> { execute: 'write-keystore' arguments: { 'key': 'foo', 'value':
>>> 'bar' } }
>>
>> This is coming from the guest?
>
> Yes.
So what about:
{ 'KeyStore': { '*foo': 'str', '*otherkey': 'str' } }
[ 'guest-write-keystore', { 'keystore': 'KeyStore' }, 'none' ]
{ 'GUEST_UPDATE_KEYSTORE': { 'keys': [ 'str' ] } }
In this model, the key store is actually stored in the guest. If the
guest wants the latest version of a value, it sends an event to get keys
updated. The host can update keys at any point in time.
Regards,
Anthony Liguori
>
>> QMP doesn't do bidirectional RPC today. It could, but that is a
>> fundamental change in the protocol.
>>
>
> Could use a separate channel for talking to qemu.
>
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 00/22] QAPI Round 1
2011-03-09 13:14 ` Avi Kivity
2011-03-09 13:51 ` Anthony Liguori
@ 2011-03-09 13:51 ` Michael Roth
1 sibling, 0 replies; 106+ messages in thread
From: Michael Roth @ 2011-03-09 13:51 UTC (permalink / raw)
To: Avi Kivity
Cc: Stefan Hajnoczi, qemu-devel, Luiz Capitulino, Michael D Roth,
Adam Litke, Markus Armbruster
On 03/09/2011 07:14 AM, Avi Kivity wrote:
> On 03/09/2011 03:12 PM, Anthony Liguori wrote:
>> On 03/09/2011 02:51 AM, Avi Kivity wrote:
>>> On 03/08/2011 09:19 PM, Anthony Liguori wrote:
>>>>> Both the guest and the management agent (and both can listen for
>>>>> events). I don't see why guest->qemu RPC is problematic for
>>>>> migration - at least when qemu terminates it.
>>>>
>>>>
>>>> If it's terminated in QEMU, it's fine, but then it's not QMP
>>>> anymore. Let me think about whether there's a way to achieve this
>>>> without a guest->qemu RPC.
>>>
>>> Why not?
>>>
>>> { execute: 'write-keystore' arguments: { 'key': 'foo', 'value': 'bar'
>>> } }
>>
>> This is coming from the guest?
>
> Yes.
>
>> QMP doesn't do bidirectional RPC today. It could, but that is a
>> fundamental change in the protocol.
>>
>
> Could use a separate channel for talking to qemu.
>
That's a possibility. Might be tricky to do right though...we wouldn't
want to have a guest get a normal QMP session over the channel, so we'd
likely end up with a separate QMP server that uses a different
guest-specific dispatch table for commands.
Would be nice to not have to rely on multiple channels
though...particularly in the case of isa-serial channels where available
ports might be scarce. But I wouldn't consider that a showstopper either.
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 14/22] qapi: add query-version QMP command
2011-03-09 13:44 ` Anthony Liguori
@ 2011-03-09 13:51 ` Avi Kivity
2011-03-09 14:13 ` Anthony Liguori
0 siblings, 1 reply; 106+ messages in thread
From: Avi Kivity @ 2011-03-09 13:51 UTC (permalink / raw)
To: Anthony Liguori
Cc: Adam Litke, Stefan Hajnoczi, qemu-devel, Markus Armbruster,
Luiz Capitulino
On 03/09/2011 03:44 PM, Anthony Liguori wrote:
>>> Yeah, it's only loosely JSON as I don't use a JSON parser.
>>
>> Goes kind of against all the buzzwords you're letting fly here...
>
>
> The schema defines arguments in a dictionary because in QMP, the
> argument order doesn't matter. But the argument order matters in C so
> I need to use a custom parser to preserve dictionary order.
We could extend our parser to annotate the dictionary with the original
order. Not worth it though.
>
> There's no way to do commenting in JSON and I really wanted to have
> inline documentation.
>
> But otherwise, it's valid JSON.
>
We should then have a transformation that generates a valid json for
clients to use. We could even include the documentation as a 'doc': key.
--
error compiling committee.c: too many arguments to function
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 00/22] QAPI Round 1
2011-03-09 13:51 ` Anthony Liguori
@ 2011-03-09 13:55 ` Avi Kivity
2011-03-09 14:15 ` Anthony Liguori
0 siblings, 1 reply; 106+ messages in thread
From: Avi Kivity @ 2011-03-09 13:55 UTC (permalink / raw)
To: Anthony Liguori
Cc: Stefan Hajnoczi, qemu-devel, Luiz Capitulino, Michael D Roth,
Adam Litke, Markus Armbruster
On 03/09/2011 03:51 PM, Anthony Liguori wrote:
>>>> { execute: 'write-keystore' arguments: { 'key': 'foo', 'value':
>>>> 'bar' } }
>>>
>>> This is coming from the guest?
>>
>> Yes.
>
>
> So what about:
>
> { 'KeyStore': { '*foo': 'str', '*otherkey': 'str' } }
> [ 'guest-write-keystore', { 'keystore': 'KeyStore' }, 'none' ]
>
> { 'GUEST_UPDATE_KEYSTORE': { 'keys': [ 'str' ] } }
(why the CAPS?)
>
> In this model, the key store is actually stored in the guest. If the
> guest wants the latest version of a value, it sends an event to get
> keys updated. The host can update keys at any point in time.
This survives live migration, but not guest reboots or crashes.
--
error compiling committee.c: too many arguments to function
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 19/22] qapi: add QMP put-event command
2011-03-09 13:48 ` Anthony Liguori
@ 2011-03-09 13:58 ` Avi Kivity
2011-03-09 14:26 ` Anthony Liguori
0 siblings, 1 reply; 106+ messages in thread
From: Avi Kivity @ 2011-03-09 13:58 UTC (permalink / raw)
To: Anthony Liguori
Cc: qemu-devel, Adam Litke, Markus Armbruster, Luiz Capitulino
On 03/09/2011 03:48 PM, Anthony Liguori wrote:
>>> +[ 'put-event', {'tag': 'int'}, {}, 'none' ]
>>
>> Why is tag an int?
> +##
>
> It's a handle so the type doesn't matter as long as I can make sure
> values are unique. ints are easier to work with because they don't
> require memory allocation.
I think it's nicer for the client to use a string. Instead of a global
ID allocator, it can use unique IDs or unique prefixes + local IDs.
Should also aid a little in debugging.
>
>> don't we use strings for command ids and similar?
>
> id's can be any valid JSON value.
>
> But a handle is not the same thing as an id.
Why not?
I hope handles are client-provided?
>
>> Also could be better named, disconnect-event or unlisten-event.
>
> I was going for symmetry with the signal accessors which are typically
> in the format 'get-block-io-error-event'.
>
> Maybe it would be better to do 'connect-block-io-error-event' and
> 'disconnect-event'?
Yes.
But I'm confused, do we have a per-event command on the wire? Or just C
stubs?
--
error compiling committee.c: too many arguments to function
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 14/22] qapi: add query-version QMP command
2011-03-09 13:36 ` Avi Kivity
@ 2011-03-09 14:11 ` Anthony Liguori
2011-03-09 14:37 ` Avi Kivity
0 siblings, 1 reply; 106+ messages in thread
From: Anthony Liguori @ 2011-03-09 14:11 UTC (permalink / raw)
To: Avi Kivity
Cc: qemu-devel, Adam Litke, Anthony Liguori, Markus Armbruster,
Luiz Capitulino
On 03/09/2011 07:36 AM, Avi Kivity wrote:
> On 03/07/2011 03:22 AM, Anthony Liguori wrote:
>> This is used internally by QMP. It's also a pretty good example of a
>> typical
>> command conversion.
>>
>> +##
>> +{ 'VersionInfo': {'qemu': {'major': 'int', 'minor': 'int', 'micro':
>> 'int'},
>> + 'package': 'str'} }
>> +
>> +##
>> +# @query-version:
>> +#
>> +# Returns the current version of QEMU.
>> +#
>> +# Returns: A @VersionInfo object describing the current version of
>> QEMU.
>> +#
>> +# Since: 0.14.0
>> +##
>> +[ 'query-version', {}, {}, 'VersionInfo' ]
>
> (just picking on a patch that has a bit of schema in it)
If you want to see more of the schema in action
http://repo.or.cz/w/qemu/aliguori.git/blob/refs/heads/glib:/qmp-schema.json
> Something that can be added to the schema are default values for newly
> added parameters. This way we can avoid an explosion of commands
> where adding an optional parameter suffices; should be easier for the
> user to pick the right command and easier for us to document and support.
Adding a parameter to a command, even if the schema specifies a default
value, is going to break the C library ABI so it's something we should
strongly discourage.
We definitely want to systematically document defaults but the question
is whether that belongs in the documentation for the command or the
schema itself. Since a default doesn't affect the wire protocol in any
way, shape, or form, I think there a pretty strong argument that it
belongs in the documentation and not the schema.
gtk-doc typically uses a tag to identify defaults. I think it's
something like #default although that is purely a marking concept (the
value isn't parsed out or anything). Whether we'd want to automatically
parse the value following the #default tag and change the internal
signature is probably a discussion we can defer. Since this only really
should apply to structures, I suspect it's not a huge win.
Regards,
Anthony Liguori
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 14/22] qapi: add query-version QMP command
2011-03-09 13:51 ` Avi Kivity
@ 2011-03-09 14:13 ` Anthony Liguori
0 siblings, 0 replies; 106+ messages in thread
From: Anthony Liguori @ 2011-03-09 14:13 UTC (permalink / raw)
To: Avi Kivity
Cc: Stefan Hajnoczi, Luiz Capitulino, Adam Litke, Markus Armbruster,
qemu-devel
On 03/09/2011 07:51 AM, Avi Kivity wrote:
> On 03/09/2011 03:44 PM, Anthony Liguori wrote:
>>>> Yeah, it's only loosely JSON as I don't use a JSON parser.
>>>
>>> Goes kind of against all the buzzwords you're letting fly here...
>>
>>
>> The schema defines arguments in a dictionary because in QMP, the
>> argument order doesn't matter. But the argument order matters in C
>> so I need to use a custom parser to preserve dictionary order.
>
> We could extend our parser to annotate the dictionary with the
> original order. Not worth it though.
>
>>
>> There's no way to do commenting in JSON and I really wanted to have
>> inline documentation.
>>
>> But otherwise, it's valid JSON.
>>
>
> We should then have a transformation that generates a valid json for
> clients to use. We could even include the documentation as a 'doc': key.
Yes, as I mentioned on the call, that's my plan for 0.15. I hadn't
thought about the doc bit though.
Regards,
Anthony Liguori
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 00/22] QAPI Round 1
2011-03-09 13:55 ` Avi Kivity
@ 2011-03-09 14:15 ` Anthony Liguori
0 siblings, 0 replies; 106+ messages in thread
From: Anthony Liguori @ 2011-03-09 14:15 UTC (permalink / raw)
To: Avi Kivity
Cc: Stefan Hajnoczi, Markus Armbruster, qemu-devel, Michael D Roth,
Adam Litke, Luiz Capitulino
On 03/09/2011 07:55 AM, Avi Kivity wrote:
> On 03/09/2011 03:51 PM, Anthony Liguori wrote:
>>>>> { execute: 'write-keystore' arguments: { 'key': 'foo', 'value':
>>>>> 'bar' } }
>>>>
>>>> This is coming from the guest?
>>>
>>> Yes.
>>
>>
>> So what about:
>>
>> { 'KeyStore': { '*foo': 'str', '*otherkey': 'str' } }
>> [ 'guest-write-keystore', { 'keystore': 'KeyStore' }, 'none' ]
>>
>> { 'GUEST_UPDATE_KEYSTORE': { 'keys': [ 'str' ] } }
>
> (why the CAPS?)
Sad to say, but legacy. That's how we do events today.
>>
>> In this model, the key store is actually stored in the guest. If the
>> guest wants the latest version of a value, it sends an event to get
>> keys updated. The host can update keys at any point in time.
>
> This survives live migration, but not guest reboots or crashes.
Right, management tool (or QEMU) needs to keep a copy if it's to survive
reset.
But since we don't have a concrete use-case, I'm really just trying to
show that we don't need bidirectional RPC for these types of things.
Regards,
Anthony Liguori
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 19/22] qapi: add QMP put-event command
2011-03-09 13:58 ` Avi Kivity
@ 2011-03-09 14:26 ` Anthony Liguori
2011-03-10 12:39 ` Avi Kivity
0 siblings, 1 reply; 106+ messages in thread
From: Anthony Liguori @ 2011-03-09 14:26 UTC (permalink / raw)
To: Avi Kivity; +Cc: Luiz Capitulino, qemu-devel, Markus Armbruster
On 03/09/2011 07:58 AM, Avi Kivity wrote:
> On 03/09/2011 03:48 PM, Anthony Liguori wrote:
>>>> +[ 'put-event', {'tag': 'int'}, {}, 'none' ]
>>>
>>> Why is tag an int?
>> +##
>>
>> It's a handle so the type doesn't matter as long as I can make sure
>> values are unique. ints are easier to work with because they don't
>> require memory allocation.
>
> I think it's nicer for the client to use a string. Instead of a
> global ID allocator, it can use unique IDs or unique prefixes + local
> IDs. Should also aid a little in debugging.
handle's are opaque to clients.
I don't have a huge objection to using strings and it may make sense to
do a prefix like you're suggesting. I won't do that initially but since
handles are opaque, we can make that change later.
>>> don't we use strings for command ids and similar?
>>
>> id's can be any valid JSON value.
>>
>> But a handle is not the same thing as an id.
>
> Why not?
>
> I hope handles are client-provided?
No, they are generated by the server. It makes sense because really a
handle is a marshalled version of a signal. The signal accessor looks
something like this:
{ 'BLOCK_IO_ERROR': { 'device': 'str', 'action': 'str', 'operation':
'str' } }
[ 'get-block-io-error-event': {}, 'BLOCK_IO_ERROR' }
The way we marshal a 'BLOCK_IO_ERROR' type is by generating a unique
handle and returning that.
While this looks like an int on the wire, at both the server and libqmp
level, it looks like a BlockIoErrorEvent object. So in QEMU:
BlockIoErrorEvent *qmp_get_block_io_error_event(Error **errp)
{
}
And in libqmp:
BlockIoErrorEvent *libqmp_get_block_io_error_event(QmpSession *sess,
Error **errp)
{
}
>>> Also could be better named, disconnect-event or unlisten-event.
>>
>> I was going for symmetry with the signal accessors which are
>> typically in the format 'get-block-io-error-event'.
>>
>> Maybe it would be better to do 'connect-block-io-error-event' and
>> 'disconnect-event'?
>
> Yes.
>
> But I'm confused, do we have a per-event command on the wire? Or just
> C stubs?
Ignoring default events, you'll never see an event until you execute a
signal accessor function. When you execute this function, you will
start receiving the events and those events will carry a tag containing
the handle returned by the signal accessor.
Within libqmp, any time you execute a signal accessor, a new signal
object is created of the appropriate type. When that object is
destroyed, you send a put-event to stop receiving the signal.
When you connect to a signal object (via libqmp), you don't execute the
signal accessor because the object is already receiving the signal.
Default events (which exist to preserve compatibility) are a set of
events that are automatically connected to after qmp_capabilities is
executed. Because these connections are implicit, they arrive without a
handle in the event object.
At this point, libqmp just ignores default events. In the future, I'd
like to add a command that can be executed before qmp_capabilities that
will avoid connecting to default events.
Regards,
Anthony Liguori
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 14/22] qapi: add query-version QMP command
2011-03-09 14:11 ` Anthony Liguori
@ 2011-03-09 14:37 ` Avi Kivity
2011-03-09 14:47 ` Anthony Liguori
0 siblings, 1 reply; 106+ messages in thread
From: Avi Kivity @ 2011-03-09 14:37 UTC (permalink / raw)
To: Anthony Liguori
Cc: qemu-devel, Adam Litke, Anthony Liguori, Markus Armbruster,
Luiz Capitulino
On 03/09/2011 04:11 PM, Anthony Liguori wrote:
>> (just picking on a patch that has a bit of schema in it)
>
>
> If you want to see more of the schema in action
> http://repo.or.cz/w/qemu/aliguori.git/blob/refs/heads/glib:/qmp-schema.json
Thanks. Something a little worrying is the reliance on capitalization
and punctuation ( {} vs [] ) do distinguish among the different types of
declarations. It's not immediately clear if something is a type, event,
or command.
We could do
[
{ 'type': 'MyType', fields: [['a', 'str'], ['b', 'int'], ['c',
'AnotherType']] }
{ 'event': 'MY_EVENT', 'arguments': [ ... ] }
{ 'command': 'my-command', 'arguments': [ ... ], 'return': ... }
]
which leaves us room for additional metainformation.
The concern is more about non-qemu consumers of the schema.
>
>> Something that can be added to the schema are default values for
>> newly added parameters. This way we can avoid an explosion of
>> commands where adding an optional parameter suffices; should be
>> easier for the user to pick the right command and easier for us to
>> document and support.
>
> Adding a parameter to a command, even if the schema specifies a
> default value, is going to break the C library ABI so it's something
> we should strongly discourage.
We could add compatibility signatures when we extend a command:
{ 'command': 'x', arguments: [['a', 'str']], return: ...,
'signatures': { 'x': [], 'x2': ['a'] } }
That lets the wire protocol extend x without introducing a new command,
but for libqmp it adds a new x2() API with the new parameter.
>
> We definitely want to systematically document defaults but the
> question is whether that belongs in the documentation for the command
> or the schema itself. Since a default doesn't affect the wire
> protocol in any way, shape, or form, I think there a pretty strong
> argument that it belongs in the documentation and not the schema.
Agree.
>
> gtk-doc typically uses a tag to identify defaults. I think it's
> something like #default although that is purely a marking concept (the
> value isn't parsed out or anything). Whether we'd want to
> automatically parse the value following the #default tag and change
> the internal signature is probably a discussion we can defer. Since
> this only really should apply to structures, I suspect it's not a huge
> win.
--
error compiling committee.c: too many arguments to function
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 14/22] qapi: add query-version QMP command
2011-03-09 14:37 ` Avi Kivity
@ 2011-03-09 14:47 ` Anthony Liguori
2011-03-10 12:41 ` Avi Kivity
0 siblings, 1 reply; 106+ messages in thread
From: Anthony Liguori @ 2011-03-09 14:47 UTC (permalink / raw)
To: Avi Kivity
Cc: Anthony Liguori, Luiz Capitulino, qemu-devel, Markus Armbruster
On 03/09/2011 08:37 AM, Avi Kivity wrote:
> On 03/09/2011 04:11 PM, Anthony Liguori wrote:
>>> (just picking on a patch that has a bit of schema in it)
>>
>>
>> If you want to see more of the schema in action
>> http://repo.or.cz/w/qemu/aliguori.git/blob/refs/heads/glib:/qmp-schema.json
>
> Thanks. Something a little worrying is the reliance on capitalization
> and punctuation ( {} vs [] ) do distinguish among the different types
> of declarations. It's not immediately clear if something is a type,
> event, or command.
>
> We could do
>
> [
>
> { 'type': 'MyType', fields: [['a', 'str'], ['b', 'int'], ['c',
> 'AnotherType']] }
> { 'event': 'MY_EVENT', 'arguments': [ ... ] }
> { 'command': 'my-command', 'arguments': [ ... ], 'return': ... }
>
> ]
>
> which leaves us room for additional metainformation.
>
> The concern is more about non-qemu consumers of the schema.
Yeah, I dislike it too and I've been leaning towards changing it.
My preference would be:
{ 'type': 'MyType', 'fields': { 'a': 'str', 'b': 'int', 'c':
'AnotherType' } }
{ 'event': 'MY_EVENT', 'arguments': {...} }
{ 'command': 'my-command', 'arguments': {...}, 'returns': 'int' }
I do prefer the dictionary syntax for arguments over a list because a
list implies order. Plus I think the syntax is just awkward and a whole
lot easier to get wrong (too many/few elements in list).
I don't think I want to make this sort of change just yet. Also note
that the schema that will be exposed over the wire is not directly
related to the schema we use for code generation.
>>> Something that can be added to the schema are default values for
>>> newly added parameters. This way we can avoid an explosion of
>>> commands where adding an optional parameter suffices; should be
>>> easier for the user to pick the right command and easier for us to
>>> document and support.
>>
>> Adding a parameter to a command, even if the schema specifies a
>> default value, is going to break the C library ABI so it's something
>> we should strongly discourage.
>
> We could add compatibility signatures when we extend a command:
>
>
> { 'command': 'x', arguments: [['a', 'str']], return: ...,
> 'signatures': { 'x': [], 'x2': ['a'] } }
>
> That lets the wire protocol extend x without introducing a new
> command, but for libqmp it adds a new x2() API with the new parameter.
I'd rather just not add arguments.
Treating QMP as a C API makes it easier for us to maintain compatibility
both internally and externally.
Regards,
Anthony Liguori
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 19/22] qapi: add QMP put-event command
2011-03-09 14:26 ` Anthony Liguori
@ 2011-03-10 12:39 ` Avi Kivity
2011-03-10 14:12 ` Anthony Liguori
0 siblings, 1 reply; 106+ messages in thread
From: Avi Kivity @ 2011-03-10 12:39 UTC (permalink / raw)
To: Anthony Liguori; +Cc: Luiz Capitulino, qemu-devel, Markus Armbruster
On 03/09/2011 04:26 PM, Anthony Liguori wrote:
> On 03/09/2011 07:58 AM, Avi Kivity wrote:
>> On 03/09/2011 03:48 PM, Anthony Liguori wrote:
>>>>> +[ 'put-event', {'tag': 'int'}, {}, 'none' ]
>>>>
>>>> Why is tag an int?
>>> +##
>>>
>>> It's a handle so the type doesn't matter as long as I can make sure
>>> values are unique. ints are easier to work with because they don't
>>> require memory allocation.
>>
>> I think it's nicer for the client to use a string. Instead of a
>> global ID allocator, it can use unique IDs or unique prefixes + local
>> IDs. Should also aid a little in debugging.
>
> handle's are opaque to clients.
>
> I don't have a huge objection to using strings and it may make sense
> to do a prefix like you're suggesting. I won't do that initially but
> since handles are opaque, we can make that change later.
What I mean is that the client should specify the handle, like it does
for everything else it gives a name (netdevs, blockdevs, SCM_RIGHT fds,
etc).
{ execute: listen-event, arguments: { event: blah, id: blah00001 } }
{ execute: unlisten-event arguments: { id: blah00001 } }
>
>>>> don't we use strings for command ids and similar?
>>>
>>> id's can be any valid JSON value.
>>>
>>> But a handle is not the same thing as an id.
>>
>> Why not?
>>
>> I hope handles are client-provided?
>
> No, they are generated by the server. It makes sense because really a
> handle is a marshalled version of a signal. The signal accessor looks
> something like this:
>
> { 'BLOCK_IO_ERROR': { 'device': 'str', 'action': 'str', 'operation':
> 'str' } }
> [ 'get-block-io-error-event': {}, 'BLOCK_IO_ERROR' }
>
> The way we marshal a 'BLOCK_IO_ERROR' type is by generating a unique
> handle and returning that.
I don't follow at all. Where's the handle here? Why don't we return
the BLOCK_IO_ERROR as an object, on the wire?
>
> While this looks like an int on the wire, at both the server and
> libqmp level, it looks like a BlockIoErrorEvent object. So in QEMU:
>
> BlockIoErrorEvent *qmp_get_block_io_error_event(Error **errp)
> {
> }
>
> And in libqmp:
>
> BlockIoErrorEvent *libqmp_get_block_io_error_event(QmpSession *sess,
> Error **errp)
> {
> }
What would the wire exchange look like?
>
>>>> Also could be better named, disconnect-event or unlisten-event.
>>>
>>> I was going for symmetry with the signal accessors which are
>>> typically in the format 'get-block-io-error-event'.
>>>
>>> Maybe it would be better to do 'connect-block-io-error-event' and
>>> 'disconnect-event'?
>>
>> Yes.
>>
>> But I'm confused, do we have a per-event command on the wire? Or
>> just C stubs?
>
> Ignoring default events, you'll never see an event until you execute a
> signal accessor function. When you execute this function, you will
> start receiving the events and those events will carry a tag
> containing the handle returned by the signal accessor.
A "signal accessor" is a command to start listening to a signal?
So why not have the signal accessor provide the tag? Like execute: blah
provides a tag?
>
> Within libqmp, any time you execute a signal accessor, a new signal
> object is created of the appropriate type. When that object is
> destroyed, you send a put-event to stop receiving the signal.
>
> When you connect to a signal object (via libqmp), you don't execute
> the signal accessor because the object is already receiving the signal.
>
> Default events (which exist to preserve compatibility) are a set of
> events that are automatically connected to after qmp_capabilities is
> executed. Because these connections are implicit, they arrive without
> a handle in the event object.
>
> At this point, libqmp just ignores default events. In the future, I'd
> like to add a command that can be executed before qmp_capabilities
> that will avoid connecting to default events.
I'm really confused. Part of that is because the conversation mixes
libqmp, server API, and wire protocol. I'd like to understand the wire
protocol first, everything else follows from that.
--
error compiling committee.c: too many arguments to function
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 14/22] qapi: add query-version QMP command
2011-03-09 14:47 ` Anthony Liguori
@ 2011-03-10 12:41 ` Avi Kivity
2011-03-10 12:46 ` Avi Kivity
2011-03-10 13:52 ` Anthony Liguori
0 siblings, 2 replies; 106+ messages in thread
From: Avi Kivity @ 2011-03-10 12:41 UTC (permalink / raw)
To: Anthony Liguori
Cc: Anthony Liguori, Luiz Capitulino, qemu-devel, Markus Armbruster
On 03/09/2011 04:47 PM, Anthony Liguori wrote:
>> [
>>
>> { 'type': 'MyType', fields: [['a', 'str'], ['b', 'int'], ['c',
>> 'AnotherType']] }
>> { 'event': 'MY_EVENT', 'arguments': [ ... ] }
>> { 'command': 'my-command', 'arguments': [ ... ], 'return': ... }
>>
>> ]
>>
>> which leaves us room for additional metainformation.
>>
>> The concern is more about non-qemu consumers of the schema.
>
>
> Yeah, I dislike it too and I've been leaning towards changing it.
>
> My preference would be:
>
> { 'type': 'MyType', 'fields': { 'a': 'str', 'b': 'int', 'c':
> 'AnotherType' } }
> { 'event': 'MY_EVENT', 'arguments': {...} }
> { 'command': 'my-command', 'arguments': {...}, 'returns': 'int' }
>
> I do prefer the dictionary syntax for arguments over a list because a
> list implies order. Plus I think the syntax is just awkward and a
> whole lot easier to get wrong (too many/few elements in list).
Yeah. We can rationalize it by saying that most dynamic consumers of
the schema will not care about argument order, and that if they do, they
can implement a custom parser.
> I don't think I want to make this sort of change just yet. Also note
> that the schema that will be exposed over the wire is not directly
> related to the schema we use for code generation.
Right, we have to nail down the format for the former, though.
--
error compiling committee.c: too many arguments to function
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 14/22] qapi: add query-version QMP command
2011-03-10 12:41 ` Avi Kivity
@ 2011-03-10 12:46 ` Avi Kivity
2011-03-10 13:52 ` Anthony Liguori
1 sibling, 0 replies; 106+ messages in thread
From: Avi Kivity @ 2011-03-10 12:46 UTC (permalink / raw)
To: Anthony Liguori
Cc: Anthony Liguori, Luiz Capitulino, qemu-devel, Markus Armbruster
On 03/10/2011 02:41 PM, Avi Kivity wrote:
>> I don't think I want to make this sort of change just yet. Also note
>> that the schema that will be exposed over the wire is not directly
>> related to the schema we use for code generation.
>
> Right, we have to nail down the format for the former, though.
>
btw. Back in the day when json was proposed vs. a custom text-line
oriented protocol, it beat established RPCs because they were all so
cumbersome with IDLs and code generation and general heavyweightness.
And now we're making our json-rpc exactly like that. There's a moral in
there somewhere.
--
error compiling committee.c: too many arguments to function
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 14/22] qapi: add query-version QMP command
2011-03-10 12:41 ` Avi Kivity
2011-03-10 12:46 ` Avi Kivity
@ 2011-03-10 13:52 ` Anthony Liguori
1 sibling, 0 replies; 106+ messages in thread
From: Anthony Liguori @ 2011-03-10 13:52 UTC (permalink / raw)
To: Avi Kivity; +Cc: Luiz Capitulino, qemu-devel, Markus Armbruster
On 03/10/2011 06:41 AM, Avi Kivity wrote:
>> My preference would be:
>>
>> { 'type': 'MyType', 'fields': { 'a': 'str', 'b': 'int', 'c':
>> 'AnotherType' } }
>> { 'event': 'MY_EVENT', 'arguments': {...} }
>> { 'command': 'my-command', 'arguments': {...}, 'returns': 'int' }
>>
>> I do prefer the dictionary syntax for arguments over a list because a
>> list implies order. Plus I think the syntax is just awkward and a
>> whole lot easier to get wrong (too many/few elements in list).
>
>
> Yeah. We can rationalize it by saying that most dynamic consumers of
> the schema will not care about argument order, and that if they do,
> they can implement a custom parser.
>
>> I don't think I want to make this sort of change just yet. Also note
>> that the schema that will be exposed over the wire is not directly
>> related to the schema we use for code generation.
>
> Right, we have to nail down the format for the former, though.
There's also enum syntax which I guess would be:
{ 'enum': 'MyEnum', values: [ 'value1', 'value2', 'value3' ] }
The other thing that I'm delaying until post 0.15 is errors. I'd like
to define errors as part of the schema. Something like:
{ 'error': 'FileNotFound', 'arguments': { 'filename': 'str' } }
Regards,
Anthony Liguori
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 19/22] qapi: add QMP put-event command
2011-03-10 12:39 ` Avi Kivity
@ 2011-03-10 14:12 ` Anthony Liguori
2011-03-10 14:24 ` Avi Kivity
0 siblings, 1 reply; 106+ messages in thread
From: Anthony Liguori @ 2011-03-10 14:12 UTC (permalink / raw)
To: Avi Kivity; +Cc: Markus Armbruster, qemu-devel, Luiz Capitulino
On 03/10/2011 06:39 AM, Avi Kivity wrote:
> What I mean is that the client should specify the handle, like it does
> for everything else it gives a name (netdevs, blockdevs, SCM_RIGHT
> fds, etc).
>
> { execute: listen-event, arguments: { event: blah, id: blah00001 } }
> { execute: unlisten-event arguments: { id: blah00001 } }
Yeah, I understand, it just doesn't fit the model quite as well of
signal accessors.
Normally, in a signal/slot event model, you'd have some notion of an
object model and/or hierarchy. For instance, with dbus, you'd do
something like:
bus = dbus.SystemBus()
hal = # magic to get a hal object
device = hal.FindDeviceByCapability('media.storage')
device.connect_to_signal('media-changed', fn)
We don't have objects and I don't mean to introduce them, but I like the
idea of treating signals as objects and returning them via an accessor
function.
So the idea is that the handle is a marshalled form of the signal object.
>> { 'BLOCK_IO_ERROR': { 'device': 'str', 'action': 'str', 'operation':
>> 'str' } }
>> [ 'get-block-io-error-event': {}, 'BLOCK_IO_ERROR' }
>>
>> The way we marshal a 'BLOCK_IO_ERROR' type is by generating a unique
>> handle and returning that.
>
> I don't follow at all. Where's the handle here? Why don't we return
> the BLOCK_IO_ERROR as an object, on the wire?
How we marshal an object depends on the RPC layer.
We marshal events on the wire as an integer handle. That is only a
concept within the wire protocol.
We could just as easily return an object but without diving into JSON
class hinting, it'd be pretty meaningless because we'd just return "{
'handle': 32}" instead of "32".
>> While this looks like an int on the wire, at both the server and
>> libqmp level, it looks like a BlockIoErrorEvent object. So in QEMU:
>>
>> BlockIoErrorEvent *qmp_get_block_io_error_event(Error **errp)
>> {
>> }
>>
>> And in libqmp:
>>
>> BlockIoErrorEvent *libqmp_get_block_io_error_event(QmpSession *sess,
>> Error **errp)
>> {
>> }
>
> What would the wire exchange look like?
> { 'execute': 'get-block-io-error-event' }
< { 'return' : 32 }
...
< { 'event': 'BLOCK_IO_ERROR', 'data': { 'action': 'stop', 'device':
'ide0-hd0', 'operation': 'read' }, 'tag': 32 }
...
> { 'execute': 'put-event', 'arguments': { 'tag': 32 } }
>> Ignoring default events, you'll never see an event until you execute
>> a signal accessor function. When you execute this function, you will
>> start receiving the events and those events will carry a tag
>> containing the handle returned by the signal accessor.
>
> A "signal accessor" is a command to start listening to a signal?
Yes, it basically enables the signal for the session.
> So why not have the signal accessor provide the tag? Like execute:
> blah provides a tag?
How would this map to a C API? You'd either have to completely drop the
notion of signal objects and use a separate mechanism to register
callbacks against a tag (and lose type safety) or do some major munging
to have the C API take a radically different signature than the wire
protocol.
>>
>> Within libqmp, any time you execute a signal accessor, a new signal
>> object is created of the appropriate type. When that object is
>> destroyed, you send a put-event to stop receiving the signal.
>>
>> When you connect to a signal object (via libqmp), you don't execute
>> the signal accessor because the object is already receiving the signal.
>>
>> Default events (which exist to preserve compatibility) are a set of
>> events that are automatically connected to after qmp_capabilities is
>> executed. Because these connections are implicit, they arrive
>> without a handle in the event object.
>>
>> At this point, libqmp just ignores default events. In the future,
>> I'd like to add a command that can be executed before
>> qmp_capabilities that will avoid connecting to default events.
>
> I'm really confused. Part of that is because the conversation mixes
> libqmp, server API, and wire protocol. I'd like to understand the
> wire protocol first, everything else follows from that.
No, it's the opposite for me. We design a good C API and then figure
out how to make it work well as a wire protocol. The whole point of
this effort is to build an API that we can consume within QEMU such that
we can start breaking large chunks of code out of the main executable.
Regards,
Anthony Liguori
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 19/22] qapi: add QMP put-event command
2011-03-10 14:12 ` Anthony Liguori
@ 2011-03-10 14:24 ` Avi Kivity
2011-03-10 15:30 ` Avi Kivity
2011-03-10 15:33 ` Anthony Liguori
0 siblings, 2 replies; 106+ messages in thread
From: Avi Kivity @ 2011-03-10 14:24 UTC (permalink / raw)
To: Anthony Liguori; +Cc: Markus Armbruster, qemu-devel, Luiz Capitulino
On 03/10/2011 04:12 PM, Anthony Liguori wrote:
> On 03/10/2011 06:39 AM, Avi Kivity wrote:
>> What I mean is that the client should specify the handle, like it
>> does for everything else it gives a name (netdevs, blockdevs,
>> SCM_RIGHT fds, etc).
>>
>> { execute: listen-event, arguments: { event: blah, id: blah00001 } }
>> { execute: unlisten-event arguments: { id: blah00001 } }
>
> Yeah, I understand, it just doesn't fit the model quite as well of
> signal accessors.
>
> Normally, in a signal/slot event model, you'd have some notion of an
> object model and/or hierarchy. For instance, with dbus, you'd do
> something like:
>
> bus = dbus.SystemBus()
> hal = # magic to get a hal object
> device = hal.FindDeviceByCapability('media.storage')
>
> device.connect_to_signal('media-changed', fn)
>
> We don't have objects and I don't mean to introduce them, but I like
> the idea of treating signals as objects and returning them via an
> accessor function.
>
> So the idea is that the handle is a marshalled form of the signal object.
It's not a marshalled form, it's an opaque handle. A marshalled form
doesn't destroy information.
Anyway it would work with a client-provided tag just as well.
connect_to_signal() could manufacture one and provide it to the server.
>
>>> { 'BLOCK_IO_ERROR': { 'device': 'str', 'action': 'str', 'operation':
>>> 'str' } }
>>> [ 'get-block-io-error-event': {}, 'BLOCK_IO_ERROR' }
>>>
>>> The way we marshal a 'BLOCK_IO_ERROR' type is by generating a unique
>>> handle and returning that.
>>
>> I don't follow at all. Where's the handle here? Why don't we return
>> the BLOCK_IO_ERROR as an object, on the wire?
>
> How we marshal an object depends on the RPC layer.
>
> We marshal events on the wire as an integer handle. That is only a
> concept within the wire protocol.
I don't think it's an accurate description. We marshall an event as a
json object according to the schema above (BLOCK_IO_ERROR). How we
marshall a subscription to an event, or an unsubscription to an event,
depends on how we specify the protocol. It has nothing to do with
client or server internal rpc stubs.
>
> We could just as easily return an object but without diving into JSON
> class hinting, it'd be pretty meaningless because we'd just return "{
> 'handle': 32}" instead of "32".
Right, I suggest we return nothing at all. Instead the client provides
the handle.
>
>>> While this looks like an int on the wire, at both the server and
>>> libqmp level, it looks like a BlockIoErrorEvent object. So in QEMU:
>>>
>>> BlockIoErrorEvent *qmp_get_block_io_error_event(Error **errp)
>>> {
>>> }
>>>
>>> And in libqmp:
>>>
>>> BlockIoErrorEvent *libqmp_get_block_io_error_event(QmpSession *sess,
>>> Error **errp)
>>> {
>>> }
>>
>> What would the wire exchange look like?
>
> > { 'execute': 'get-block-io-error-event' }
> < { 'return' : 32 }
> ...
> < { 'event': 'BLOCK_IO_ERROR', 'data': { 'action': 'stop', 'device':
> 'ide0-hd0', 'operation': 'read' }, 'tag': 32 }
> ...
> > { 'execute': 'put-event', 'arguments': { 'tag': 32 } }
Well, I may be biased, but I prefer my variant.
btw, it's good to decree that a subscription is immediately followed by
an event with the current state (which means events have to provide
state and be idempotent); so the subscribe-and-query pattern is provided
automatically.
btw2, I now nominate subscribe and unsubscribe as replacements for get
and put.
>
>>> Ignoring default events, you'll never see an event until you execute
>>> a signal accessor function. When you execute this function, you
>>> will start receiving the events and those events will carry a tag
>>> containing the handle returned by the signal accessor.
>>
>> A "signal accessor" is a command to start listening to a signal?
>
> Yes, it basically enables the signal for the session.
Okay, the subscription command.
>
>> So why not have the signal accessor provide the tag? Like execute:
>> blah provides a tag?
>
> How would this map to a C API? You'd either have to completely drop
> the notion of signal objects and use a separate mechanism to register
> callbacks against a tag (and lose type safety) or do some major
> munging to have the C API take a radically different signature than
> the wire protocol.
A C API could create and maintain tags under the covers (an int that
keeps increasing would do).
>
>>>
>>> Within libqmp, any time you execute a signal accessor, a new signal
>>> object is created of the appropriate type. When that object is
>>> destroyed, you send a put-event to stop receiving the signal.
>>>
>>> When you connect to a signal object (via libqmp), you don't execute
>>> the signal accessor because the object is already receiving the signal.
>>>
>>> Default events (which exist to preserve compatibility) are a set of
>>> events that are automatically connected to after qmp_capabilities is
>>> executed. Because these connections are implicit, they arrive
>>> without a handle in the event object.
>>>
>>> At this point, libqmp just ignores default events. In the future,
>>> I'd like to add a command that can be executed before
>>> qmp_capabilities that will avoid connecting to default events.
>>
>> I'm really confused. Part of that is because the conversation mixes
>> libqmp, server API, and wire protocol. I'd like to understand the
>> wire protocol first, everything else follows from that.
>
> No, it's the opposite for me. We design a good C API and then figure
> out how to make it work well as a wire protocol. The whole point of
> this effort is to build an API that we can consume within QEMU such
> that we can start breaking large chunks of code out of the main
> executable.
That makes a C centric wire protocol. Clients don't have to be C.
--
error compiling committee.c: too many arguments to function
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 19/22] qapi: add QMP put-event command
2011-03-10 14:24 ` Avi Kivity
@ 2011-03-10 15:30 ` Avi Kivity
2011-03-10 15:41 ` Anthony Liguori
2011-03-10 15:33 ` Anthony Liguori
1 sibling, 1 reply; 106+ messages in thread
From: Avi Kivity @ 2011-03-10 15:30 UTC (permalink / raw)
To: Anthony Liguori; +Cc: Markus Armbruster, qemu-devel, Luiz Capitulino
On 03/10/2011 04:24 PM, Avi Kivity wrote:
>>> What would the wire exchange look like?
>>
>> > { 'execute': 'get-block-io-error-event' }
>> < { 'return' : 32 }
>> ...
>> < { 'event': 'BLOCK_IO_ERROR', 'data': { 'action': 'stop', 'device':
>> 'ide0-hd0', 'operation': 'read' }, 'tag': 32 }
>> ...
>> > { 'execute': 'put-event', 'arguments': { 'tag': 32 } }
>
>
> Well, I may be biased, but I prefer my variant.
>
> btw, it's good to decree that a subscription is immediately followed
> by an event with the current state (which means events have to provide
> state and be idempotent); so the subscribe-and-query pattern is
> provided automatically.
>
> btw2, I now nominate subscribe and unsubscribe as replacements for get
> and put.
I also think it should be at the protocol layer:
> { execute: some-command, id: foo, arguments: { ... } }
< { result: { ... }, id: foo }
> { subscribe: block-io-error, id: bar, arguments: { ... } }
< { result: { ... } id: bar }
< { event: block-io-error, id: bar, data : { ... } }
> { unsubscribe: block-io-error, id: bar }
< { result: { ... } id: bar }
So events are now protocol-level pieces like commands, and the use of
tags is uniform.
--
error compiling committee.c: too many arguments to function
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 19/22] qapi: add QMP put-event command
2011-03-10 14:24 ` Avi Kivity
2011-03-10 15:30 ` Avi Kivity
@ 2011-03-10 15:33 ` Anthony Liguori
2011-03-10 15:45 ` Avi Kivity
1 sibling, 1 reply; 106+ messages in thread
From: Anthony Liguori @ 2011-03-10 15:33 UTC (permalink / raw)
To: Avi Kivity; +Cc: Luiz Capitulino, Markus Armbruster, qemu-devel
On 03/10/2011 08:24 AM, Avi Kivity wrote:
> On 03/10/2011 04:12 PM, Anthony Liguori wrote:
>> On 03/10/2011 06:39 AM, Avi Kivity wrote:
>>> What I mean is that the client should specify the handle, like it
>>> does for everything else it gives a name (netdevs, blockdevs,
>>> SCM_RIGHT fds, etc).
>>>
>>> { execute: listen-event, arguments: { event: blah, id: blah00001 } }
>>> { execute: unlisten-event arguments: { id: blah00001 } }
>>
>> Yeah, I understand, it just doesn't fit the model quite as well of
>> signal accessors.
>>
>> Normally, in a signal/slot event model, you'd have some notion of an
>> object model and/or hierarchy. For instance, with dbus, you'd do
>> something like:
>>
>> bus = dbus.SystemBus()
>> hal = # magic to get a hal object
>> device = hal.FindDeviceByCapability('media.storage')
>>
>> device.connect_to_signal('media-changed', fn)
>>
>> We don't have objects and I don't mean to introduce them, but I like
>> the idea of treating signals as objects and returning them via an
>> accessor function.
>>
>> So the idea is that the handle is a marshalled form of the signal
>> object.
>
> It's not a marshalled form, it's an opaque handle. A marshalled form
> doesn't destroy information.
>
> Anyway it would work with a client-provided tag just as well.
> connect_to_signal() could manufacture one and provide it to the server.
It's all just bits, right? :-)
We pretty much need to keep the QEMU signature the same. That would
mean an internal signature of:
BlockIoErrorEvent *qmp_connect_block_io_error_event(Error **errp)
{
}
So the marshal function would then need to do something like:
void qmp_marshal_connect_block_io_error_event(QmpState *state, QDict
*args, QObject **ret_data, Error **errp)
{
BlockIoErrorEvent *ev;
QObject *tag = qdict_get_obj(args, "tag");
ev = qmp_connect_block_io_error_event(errp);
qmp_state_add_connection(state, ev->signal, tag);
}
That's not too bad, but would the schema be:
[ 'connect-block-io-error-event', {}, 'BLOCK_IO_ERROR' ]
Or would it be:
[ 'connect-block-io-error-event', { 'tag': 'variant' }, 'none' ]
I'm really opposed to adding a variant type to the schema. I'm also not
a big fan of there not being a 1-1 relationship to the wire protocol and
the C API.
I think it's easy to rationalize 'this is how events are marshalled' vs.
'for events, a totally different declaration is generated'.
>>>> { 'BLOCK_IO_ERROR': { 'device': 'str', 'action': 'str',
>>>> 'operation': 'str' } }
>>>> [ 'get-block-io-error-event': {}, 'BLOCK_IO_ERROR' }
>>>>
>>>> The way we marshal a 'BLOCK_IO_ERROR' type is by generating a
>>>> unique handle and returning that.
>>>
>>> I don't follow at all. Where's the handle here? Why don't we
>>> return the BLOCK_IO_ERROR as an object, on the wire?
>>
>> How we marshal an object depends on the RPC layer.
>>
>> We marshal events on the wire as an integer handle. That is only a
>> concept within the wire protocol.
>
> I don't think it's an accurate description. We marshall an event as a
> json object according to the schema above (BLOCK_IO_ERROR). How we
> marshall a subscription to an event, or an unsubscription to an event,
> depends on how we specify the protocol.
It's not really a subscription to an event. It is an event.
Maybe signal accessor is the wrong word. Maybe signal factory conveys a
better notion of what it is.
>>
>> > { 'execute': 'get-block-io-error-event' }
>> < { 'return' : 32 }
>> ...
>> < { 'event': 'BLOCK_IO_ERROR', 'data': { 'action': 'stop', 'device':
>> 'ide0-hd0', 'operation': 'read' }, 'tag': 32 }
>> ...
>> > { 'execute': 'put-event', 'arguments': { 'tag': 32 } }
>
> Well, I may be biased, but I prefer my variant.
>
> btw, it's good to decree that a subscription is immediately followed
> by an event with the current state (which means events have to provide
> state and be idempotent); so the subscribe-and-query pattern is
> provided automatically.
Not all events are updates of data. BLOCK_IO_ERROR is a great example
of this. There is no useful information that can be sent after a
connection.
> btw2, I now nominate subscribe and unsubscribe as replacements for get
> and put.
Subscribe implies sub/pub in my mind and we're not publishing events so
I don't think it fits the model.
A pub/sub event model would be interesting to think through but without
a global namespace and object model, I don't think we can make it fit well.
That's why I'm using signal/slots. It's much more conducive to a
procedural model.
I'm really confused. Part of that is because the conversation mixes
libqmp, server API, and wire protocol. I'd like to understand the wire
protocol first, everything else follows from that.
>> No, it's the opposite for me. We design a good C API and then figure
>> out how to make it work well as a wire protocol. The whole point of
>> this effort is to build an API that we can consume within QEMU such
>> that we can start breaking large chunks of code out of the main
>> executable.
>
> That makes a C centric wire protocol. Clients don't have to be C.
But a C client is by far the most important of all clients--QEMU. If we
use QMP extensively internally, then we guarantee that the API is
expressive and robust.
If we build the API only for third-party clients, we end up with pretty
much what we have today. An API with good intentions but that's more or
less impossible to use in practice.
Regards,
Anthony Liguori
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 19/22] qapi: add QMP put-event command
2011-03-10 15:30 ` Avi Kivity
@ 2011-03-10 15:41 ` Anthony Liguori
2011-03-10 15:49 ` Avi Kivity
0 siblings, 1 reply; 106+ messages in thread
From: Anthony Liguori @ 2011-03-10 15:41 UTC (permalink / raw)
To: Avi Kivity; +Cc: Luiz Capitulino, Markus Armbruster, qemu-devel
On 03/10/2011 09:30 AM, Avi Kivity wrote:
> On 03/10/2011 04:24 PM, Avi Kivity wrote:
>>>> What would the wire exchange look like?
>>>
>>> > { 'execute': 'get-block-io-error-event' }
>>> < { 'return' : 32 }
>>> ...
>>> < { 'event': 'BLOCK_IO_ERROR', 'data': { 'action': 'stop', 'device':
>>> 'ide0-hd0', 'operation': 'read' }, 'tag': 32 }
>>> ...
>>> > { 'execute': 'put-event', 'arguments': { 'tag': 32 } }
>>
>>
>> Well, I may be biased, but I prefer my variant.
>>
>> btw, it's good to decree that a subscription is immediately followed
>> by an event with the current state (which means events have to
>> provide state and be idempotent); so the subscribe-and-query pattern
>> is provided automatically.
>>
>> btw2, I now nominate subscribe and unsubscribe as replacements for
>> get and put.
>
> I also think it should be at the protocol layer:
>
> > { execute: some-command, id: foo, arguments: { ... } }
> < { result: { ... }, id: foo }
> > { subscribe: block-io-error, id: bar, arguments: { ... } }
> < { result: { ... } id: bar }
> < { event: block-io-error, id: bar, data : { ... } }
> > { unsubscribe: block-io-error, id: bar }
> < { result: { ... } id: bar }
>
> So events are now protocol-level pieces like commands, and the use of
> tags is uniform.
Maybe for QMPv2, but for QMPv1, this is going to introduce an extremely
incompatible change.
Actually, we missed the json-rpc boat in designing QMP. It should look
like:
> { method: some-command, id: foo, params: { ... } }
< { result: { ... }, id: foo, params: { ... }, error: null }
> { method: connect-block-io-error, id: bar, params: { ... } }
< { result: { ... }, id: bar, error: null }
< { method: block-io-error, id: null, params: { ... } }
Keys are different and null is passed instead of not including a tag.
Events are sent exactly like methods but id is null. A result is never
sent to the server for an event.
One of the good things about using a code generator and IDL though is
that we can add a -qmp dev,protocol=json-rpc that encodes
appropriately. We can probably do this translation at the QObject level
really. But in the future, we can also add -qmp dev,protocol=xml-rpc,
-qmp dev,protocol=rest, or -qmp dev,protocol=asn1-rpc
Regards,
Anthony Liguori
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 19/22] qapi: add QMP put-event command
2011-03-10 15:33 ` Anthony Liguori
@ 2011-03-10 15:45 ` Avi Kivity
2011-03-10 16:04 ` Anthony Liguori
0 siblings, 1 reply; 106+ messages in thread
From: Avi Kivity @ 2011-03-10 15:45 UTC (permalink / raw)
To: Anthony Liguori; +Cc: Luiz Capitulino, Markus Armbruster, qemu-devel
On 03/10/2011 05:33 PM, Anthony Liguori wrote:
>
> We pretty much need to keep the QEMU signature the same. That would
> mean an internal signature of:
>
> BlockIoErrorEvent *qmp_connect_block_io_error_event(Error **errp)
> {
> }
>
> So the marshal function would then need to do something like:
>
> void qmp_marshal_connect_block_io_error_event(QmpState *state, QDict
> *args, QObject **ret_data, Error **errp)
> {
> BlockIoErrorEvent *ev;
> QObject *tag = qdict_get_obj(args, "tag");
>
> ev = qmp_connect_block_io_error_event(errp);
> qmp_state_add_connection(state, ev->signal, tag);
> }
That can be mashed around however we like.
>
> That's not too bad, but would the schema be:
>
> [ 'connect-block-io-error-event', {}, 'BLOCK_IO_ERROR' ]
>
> Or would it be:
>
> [ 'connect-block-io-error-event', { 'tag': 'variant' }, 'none' ]
>
> I'm really opposed to adding a variant type to the schema. I'm also
> not a big fan of there not being a 1-1 relationship to the wire
> protocol and the C API.
>
> I think it's easy to rationalize 'this is how events are marshalled'
> vs. 'for events, a totally different declaration is generated'.
Under my latest proposal it wouldn't be in the schema at all (like
command tags are not in the schema) since it's a protocol-level entity.
>> I don't think it's an accurate description. We marshall an event as
>> a json object according to the schema above (BLOCK_IO_ERROR). How we
>> marshall a subscription to an event, or an unsubscription to an
>> event, depends on how we specify the protocol.
>
> It's not really a subscription to an event. It is an event.
No, the event happens (potentially) multiple times. Or zero. You don't
"get" the event, you ask qemu to notify you.
>
> Maybe signal accessor is the wrong word. Maybe signal factory conveys
> a better notion of what it is.
It's even more confusing to me.
>
>>>
>>> > { 'execute': 'get-block-io-error-event' }
>>> < { 'return' : 32 }
>>> ...
>>> < { 'event': 'BLOCK_IO_ERROR', 'data': { 'action': 'stop', 'device':
>>> 'ide0-hd0', 'operation': 'read' }, 'tag': 32 }
>>> ...
>>> > { 'execute': 'put-event', 'arguments': { 'tag': 32 } }
>>
>> Well, I may be biased, but I prefer my variant.
>>
>> btw, it's good to decree that a subscription is immediately followed
>> by an event with the current state (which means events have to
>> provide state and be idempotent); so the subscribe-and-query pattern
>> is provided automatically.
>
> Not all events are updates of data. BLOCK_IO_ERROR is a great example
> of this. There is no useful information that can be sent after a
> connection.
You could say the blockdev's state is fine, or it has encountered an error.
How do you detect if there's an error if you've crashed (you=client in
this case)?
>
>> btw2, I now nominate subscribe and unsubscribe as replacements for
>> get and put.
>
> Subscribe implies sub/pub in my mind and we're not publishing events
> so I don't think it fits the model.
>
> A pub/sub event model would be interesting to think through but
> without a global namespace and object model, I don't think we can make
> it fit well.
I feel we're still not communicating. What does 'get-*-event' mean?
I think you're using some nomenclature that is unfamiliar to me.
> That's why I'm using signal/slots. It's much more conducive to a
> procedural model.
I still don't follow. We have a connection, over which we ask the other
side to let us know when something happens, then that other side lets us
know when it happens, then we ask it to stop, then it stops. There are
no signals or slots anywhere. If there are in the code, let's not mix
it up with the protocol.
>> That makes a C centric wire protocol. Clients don't have to be C.
>
> But a C client is by far the most important of all clients--QEMU. If
> we use QMP extensively internally, then we guarantee that the API is
> expressive and robust.
No, internally we have the most scope to fix mistakes.
>
> If we build the API only for third-party clients, we end up with
> pretty much what we have today. An API with good intentions but
> that's more or less impossible to use in practice.
Or we have something that's nice for C but hard to use or inconsistent
with whatever language a management client is written in.
--
error compiling committee.c: too many arguments to function
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 19/22] qapi: add QMP put-event command
2011-03-10 15:41 ` Anthony Liguori
@ 2011-03-10 15:49 ` Avi Kivity
2011-03-10 16:42 ` Anthony Liguori
0 siblings, 1 reply; 106+ messages in thread
From: Avi Kivity @ 2011-03-10 15:49 UTC (permalink / raw)
To: Anthony Liguori; +Cc: Luiz Capitulino, Markus Armbruster, qemu-devel
On 03/10/2011 05:41 PM, Anthony Liguori wrote:
>> I also think it should be at the protocol layer:
>>
>> > { execute: some-command, id: foo, arguments: { ... } }
>> < { result: { ... }, id: foo }
>> > { subscribe: block-io-error, id: bar, arguments: { ... } }
>> < { result: { ... } id: bar }
>> < { event: block-io-error, id: bar, data : { ... } }
>> > { unsubscribe: block-io-error, id: bar }
>> < { result: { ... } id: bar }
>>
>> So events are now protocol-level pieces like commands, and the use of
>> tags is uniform.
>
>
> Maybe for QMPv2, but for QMPv1, this is going to introduce an
> extremely incompatible change.
Why? It's 100% backwards compatible.
>
> Actually, we missed the json-rpc boat in designing QMP. It should
> look like:
>
IIRC I looked at it and it didn't impress me.
> > { method: some-command, id: foo, params: { ... } }
> < { result: { ... }, id: foo, params: { ... }, error: null }
> > { method: connect-block-io-error, id: bar, params: { ... } }
> < { result: { ... }, id: bar, error: null }
> < { method: block-io-error, id: null, params: { ... } }
>
> Keys are different and null is passed instead of not including a tag.
> Events are sent exactly like methods but id is null. A result is
> never sent to the server for an event.
That means that we need a per-event command, instead of making it
protocol-level.
>
> One of the good things about using a code generator and IDL though is
> that we can add a -qmp dev,protocol=json-rpc that encodes
> appropriately. We can probably do this translation at the QObject
> level really. But in the future, we can also add -qmp
> dev,protocol=xml-rpc, -qmp dev,protocol=rest, or -qmp
> dev,protocol=asn1-rpc
>
Let's get one protocol that works first.
--
error compiling committee.c: too many arguments to function
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 19/22] qapi: add QMP put-event command
2011-03-10 15:45 ` Avi Kivity
@ 2011-03-10 16:04 ` Anthony Liguori
2011-03-12 20:42 ` Avi Kivity
0 siblings, 1 reply; 106+ messages in thread
From: Anthony Liguori @ 2011-03-10 16:04 UTC (permalink / raw)
To: Avi Kivity; +Cc: qemu-devel, Markus Armbruster, Luiz Capitulino
On 03/10/2011 09:45 AM, Avi Kivity wrote:
>>
>>> btw2, I now nominate subscribe and unsubscribe as replacements for
>>> get and put.
>>
>> Subscribe implies sub/pub in my mind and we're not publishing events
>> so I don't think it fits the model.
>>
>> A pub/sub event model would be interesting to think through but
>> without a global namespace and object model, I don't think we can
>> make it fit well.
>
> I feel we're still not communicating. What does 'get-*-event' mean?
>
> I think you're using some nomenclature that is unfamiliar to me.
No, I'm just defending something that I think fundamentally sucks.
I very purposefully am trying to avoid heavy protocol visible changes at
this stage. The only reason I added signal accessors is that the
current event model is unusable from a C API.
I am in full agreement that the current signal model needs to be
rethought and should be changed at the protocol level. I just don't
want to do that right now because there are a ton of internal
improvements that can be made by without doing that.
The signal accessors are ugly but they're just a handful of commands
that can be deprecated over time. We should revisit events but we
should take the time to design it and plan for a protocol addition for 0.16.
>
>> That's why I'm using signal/slots. It's much more conducive to a
>> procedural model.
>
> I still don't follow. We have a connection, over which we ask the
> other side to let us know when something happens, then that other side
> lets us know when it happens, then we ask it to stop, then it stops.
> There are no signals or slots anywhere. If there are in the code,
> let's not mix it up with the protocol.
Dropping my legacy baggage, here's what I'd like to see us do from a
protocol perspective. I'd like to first introduce class hinting and
switch all of the string handles that we use today to class handles. So:
{ execute: query-block }
{ return: [ { __jsonclass__: 'BlockDevice', id=ide0-hd0 }, {
__jsonclass__: 'BlockDevice', id=ide1-cd0 } ] }
{ execute: connect, arguments: { 'obj': { __jsonclass__: BlockDevice,
id=ide0-hd0 }, 'signal': 'io-error' } }
{ return: { __jsonclass__: Connection, id=1 } }
{ signal: 'io-error', connection: { __jsonclass__: Connection, id=1 },
arguments: { action='stop', ... } }
{ execute: disconnect, arguments: { 'connection': { __jsonclass__:
Connection, id=1 } } }
{ return: null }
The advantages here are many. You get much stronger typing in C. If
the schema is done right, it trivially maps to an object model in Python.
Regards,
Anthony Liguori
>>> That makes a C centric wire protocol. Clients don't have to be C.
>>
>> But a C client is by far the most important of all clients--QEMU. If
>> we use QMP extensively internally, then we guarantee that the API is
>> expressive and robust.
>
> No, internally we have the most scope to fix mistakes.
>
>>
>> If we build the API only for third-party clients, we end up with
>> pretty much what we have today. An API with good intentions but
>> that's more or less impossible to use in practice.
>
> Or we have something that's nice for C but hard to use or inconsistent
> with whatever language a management client is written in.
>
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 19/22] qapi: add QMP put-event command
2011-03-10 15:49 ` Avi Kivity
@ 2011-03-10 16:42 ` Anthony Liguori
2011-03-12 20:37 ` Avi Kivity
0 siblings, 1 reply; 106+ messages in thread
From: Anthony Liguori @ 2011-03-10 16:42 UTC (permalink / raw)
To: Avi Kivity; +Cc: qemu-devel, Markus Armbruster, Luiz Capitulino
On 03/10/2011 09:49 AM, Avi Kivity wrote:
> On 03/10/2011 05:41 PM, Anthony Liguori wrote:
>>> I also think it should be at the protocol layer:
>>>
>>> > { execute: some-command, id: foo, arguments: { ... } }
>>> < { result: { ... }, id: foo }
>>> > { subscribe: block-io-error, id: bar, arguments: { ... } }
>>> < { result: { ... } id: bar }
>>> < { event: block-io-error, id: bar, data : { ... } }
>>> > { unsubscribe: block-io-error, id: bar }
>>> < { result: { ... } id: bar }
>>>
>>> So events are now protocol-level pieces like commands, and the use
>>> of tags is uniform.
>>
>>
>> Maybe for QMPv2, but for QMPv1, this is going to introduce an
>> extremely incompatible change.
>
> Why? It's 100% backwards compatible.
It's a very significant change for clients. While technical compatible,
it would require a change to the client infrastructure to support the
new feature.
I'm not saying we shouldn't make a change like this, but we should
minimize these type of changes.
Regards,
Anthony Liguori
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 19/22] qapi: add QMP put-event command
2011-03-10 16:42 ` Anthony Liguori
@ 2011-03-12 20:37 ` Avi Kivity
0 siblings, 0 replies; 106+ messages in thread
From: Avi Kivity @ 2011-03-12 20:37 UTC (permalink / raw)
To: Anthony Liguori; +Cc: qemu-devel, Markus Armbruster, Luiz Capitulino
On 03/10/2011 06:42 PM, Anthony Liguori wrote:
>>> Maybe for QMPv2, but for QMPv1, this is going to introduce an
>>> extremely incompatible change.
>>
>> Why? It's 100% backwards compatible.
>
>
> It's a very significant change for clients. While technical
> compatible, it would require a change to the client infrastructure to
> support the new feature.
Yes. That's generally a good thing, you implement something at the
infrastructure level once instead of at a higher level N times.
> I'm not saying we shouldn't make a change like this, but we should
> minimize these type of changes.
I agree, so we should design the protocol well, without planned
obsolescence. We'll make some mistakes, but at least they'll be unplanned.
--
I have a truly marvellous patch that fixes the bug which this
signature is too narrow to contain.
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 19/22] qapi: add QMP put-event command
2011-03-10 16:04 ` Anthony Liguori
@ 2011-03-12 20:42 ` Avi Kivity
2011-03-12 23:30 ` Anthony Liguori
0 siblings, 1 reply; 106+ messages in thread
From: Avi Kivity @ 2011-03-12 20:42 UTC (permalink / raw)
To: Anthony Liguori; +Cc: qemu-devel, Markus Armbruster, Luiz Capitulino
On 03/10/2011 06:04 PM, Anthony Liguori wrote:
> On 03/10/2011 09:45 AM, Avi Kivity wrote:
>>>
>>>> btw2, I now nominate subscribe and unsubscribe as replacements for
>>>> get and put.
>>>
>>> Subscribe implies sub/pub in my mind and we're not publishing events
>>> so I don't think it fits the model.
>>>
>>> A pub/sub event model would be interesting to think through but
>>> without a global namespace and object model, I don't think we can
>>> make it fit well.
>>
>> I feel we're still not communicating. What does 'get-*-event' mean?
>>
>> I think you're using some nomenclature that is unfamiliar to me.
>
> No, I'm just defending something that I think fundamentally sucks.
>
> I very purposefully am trying to avoid heavy protocol visible changes
> at this stage. The only reason I added signal accessors is that the
> current event model is unusable from a C API.
>
> I am in full agreement that the current signal model needs to be
> rethought and should be changed at the protocol level. I just don't
> want to do that right now because there are a ton of internal
> improvements that can be made by without doing that.
>
> The signal accessors are ugly but they're just a handful of commands
> that can be deprecated over time. We should revisit events but we
> should take the time to design it and plan for a protocol addition for
> 0.16.
It would be much better to avoid introducing deprecated commands.
They're a maintenance burden for both server and client, and a testing
burden as well. I feel it's better to spend more time to get something
we truly like.
>
>>
>>> That's why I'm using signal/slots. It's much more conducive to a
>>> procedural model.
>>
>> I still don't follow. We have a connection, over which we ask the
>> other side to let us know when something happens, then that other
>> side lets us know when it happens, then we ask it to stop, then it
>> stops. There are no signals or slots anywhere. If there are in the
>> code, let's not mix it up with the protocol.
>
> Dropping my legacy baggage, here's what I'd like to see us do from a
> protocol perspective. I'd like to first introduce class hinting and
> switch all of the string handles that we use today to class handles.
> So:
>
> { execute: query-block }
> { return: [ { __jsonclass__: 'BlockDevice', id=ide0-hd0 }, {
> __jsonclass__: 'BlockDevice', id=ide1-cd0 } ] }
>
> { execute: connect, arguments: { 'obj': { __jsonclass__: BlockDevice,
> id=ide0-hd0 }, 'signal': 'io-error' } }
> { return: { __jsonclass__: Connection, id=1 } }
>
> { signal: 'io-error', connection: { __jsonclass__: Connection, id=1 },
> arguments: { action='stop', ... } }
>
> { execute: disconnect, arguments: { 'connection': { __jsonclass__:
> Connection, id=1 } } }
> { return: null }
>
> The advantages here are many. You get much stronger typing in C. If
> the schema is done right, it trivially maps to an object model in Python.
I note that connect is not protocol level (i.e. execute: connect,
signal: io-error instead of connect; io-error).
Regarding class hinting, I don't object to that, but I feel that the
schema provides all of the benefits already.
--
I have a truly marvellous patch that fixes the bug which this
signature is too narrow to contain.
^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [Qemu-devel] [PATCH 19/22] qapi: add QMP put-event command
2011-03-12 20:42 ` Avi Kivity
@ 2011-03-12 23:30 ` Anthony Liguori
0 siblings, 0 replies; 106+ messages in thread
From: Anthony Liguori @ 2011-03-12 23:30 UTC (permalink / raw)
To: Avi Kivity; +Cc: Luiz Capitulino, qemu-devel, Markus Armbruster
On 03/12/2011 02:42 PM, Avi Kivity wrote:
> On 03/10/2011 06:04 PM, Anthony Liguori wrote:
>> On 03/10/2011 09:45 AM, Avi Kivity wrote:
>>>>
>>>>> btw2, I now nominate subscribe and unsubscribe as replacements for
>>>>> get and put.
>>>>
>>>> Subscribe implies sub/pub in my mind and we're not publishing
>>>> events so I don't think it fits the model.
>>>>
>>>> A pub/sub event model would be interesting to think through but
>>>> without a global namespace and object model, I don't think we can
>>>> make it fit well.
>>>
>>> I feel we're still not communicating. What does 'get-*-event' mean?
>>>
>>> I think you're using some nomenclature that is unfamiliar to me.
>>
>> No, I'm just defending something that I think fundamentally sucks.
>>
>> I very purposefully am trying to avoid heavy protocol visible changes
>> at this stage. The only reason I added signal accessors is that the
>> current event model is unusable from a C API.
>>
>> I am in full agreement that the current signal model needs to be
>> rethought and should be changed at the protocol level. I just don't
>> want to do that right now because there are a ton of internal
>> improvements that can be made by without doing that.
>>
>> The signal accessors are ugly but they're just a handful of commands
>> that can be deprecated over time. We should revisit events but we
>> should take the time to design it and plan for a protocol addition
>> for 0.16.
>
> It would be much better to avoid introducing deprecated commands.
> They're a maintenance burden for both server and client, and a testing
> burden as well. I feel it's better to spend more time to get
> something we truly like.
I'm really not interested in getting something I like. We're at a
deadlock right now with QMP. It's essentially not usable on its own
because there are so few commands implemented. As we speak, the libvirt
guys are adding new features based on monitor commands because they've
waited too long for the QMP versions to show up.
And more monitor commands keep getting added often without a QMP
version. There's almost no incentive to care about QMP because it's not
that useful. We need to break the deadlock and make QMP useful. That's
the goal of this effort.
The only reason I even touched events is because the current event
infrastructure is so broken. They cannot be consumed internally in any
meaningful way which means they fall into the not useful bucket.
If there was an obvious alternative, I'd switch to it. But it's not as
easy as it sounds. If you have a protocol level connect, then you need
a way to expose events at the protocol level to a client. You could use
an accessor and return a signal object but then you have a complicated
life cycle.
When you return a signal object to a QMP client, the object needs to
persist until 1) the session goes away 2) the object is explicitly
released by the client. On top of this, you also have connections which
introduce a second life cycle.
In reality, do you ever want to get a handle to a signal without
connecting to it? I doubt it. So if you make connecting to an signal
implicit in accessing it, then you only have to deal with one life
cycle, the signal connection. This is the current implementation.
There are nicer models but they really require having an object model
whereas the signals are properties of the objects. That means you don't
have to deal with an extra life cycle (because there is no explicit
signal object).
But really, this is one of the least important things we need to worry
about. The number of commands we have with extremely hard to use
semantics are alarming. The change command caused us a CVE because it's
semantics are so subtle. device_del has caused device detach to be
fundamentally broken in libvirt because it's semantics are subtle and
undocumented. These are the important problems to solve. It's not as
sexy as a JSON RPC signalling model, but it's far more important.
Regards,
Anthony Liguori
^ permalink raw reply [flat|nested] 106+ messages in thread
end of thread, other threads:[~2011-03-12 23:30 UTC | newest]
Thread overview: 106+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-03-07 1:22 [Qemu-devel] [PATCH 00/22] QAPI Round 1 Anthony Liguori
2011-03-07 1:22 ` [Qemu-devel] [PATCH 01/22] Add hard build dependency on glib Anthony Liguori
2011-03-07 10:59 ` Daniel P. Berrange
2011-03-07 13:55 ` Anthony Liguori
2011-03-07 1:22 ` [Qemu-devel] [PATCH 02/22] qerror: expose a function to format an error Anthony Liguori
2011-03-07 11:14 ` Stefan Hajnoczi
2011-03-07 13:38 ` Anthony Liguori
2011-03-07 1:22 ` [Qemu-devel] [PATCH 03/22] qapi: add Error object Anthony Liguori
2011-03-07 11:06 ` Daniel P. Berrange
2011-03-07 13:59 ` Anthony Liguori
2011-03-07 14:24 ` Anthony Liguori
2011-03-07 11:38 ` Stefan Hajnoczi
2011-03-07 13:36 ` Anthony Liguori
2011-03-07 13:55 ` Stefan Hajnoczi
2011-03-07 1:22 ` [Qemu-devel] [PATCH 04/22] qerror: split out the reporting bits of QError Anthony Liguori
2011-03-07 1:22 ` [Qemu-devel] [PATCH 05/22] qerror: add new error message for invalid enum values Anthony Liguori
2011-03-07 1:22 ` [Qemu-devel] [PATCH 06/22] qapi: add JSON parsing error message Anthony Liguori
2011-03-07 1:22 ` [Qemu-devel] [PATCH 07/22] json: propagate error from parser Anthony Liguori
2011-03-07 1:22 ` [Qemu-devel] [PATCH 08/22] qapi: add code generator for qmp-types Anthony Liguori
2011-03-07 1:57 ` Anthony Liguori
2011-03-07 1:22 ` [Qemu-devel] [PATCH 09/22] qapi: add code generator for type marshallers Anthony Liguori
2011-03-07 1:22 ` [Qemu-devel] [PATCH 10/22] qapi: add core QMP server support Anthony Liguori
2011-03-07 13:09 ` Stefan Hajnoczi
2011-03-07 13:39 ` Anthony Liguori
2011-03-07 13:46 ` Daniel P. Berrange
2011-03-07 13:54 ` Anthony Liguori
2011-03-07 1:22 ` [Qemu-devel] [PATCH 11/22] qapi: add signal support to core QMP server Anthony Liguori
2011-03-07 13:21 ` Stefan Hajnoczi
2011-03-07 13:53 ` Anthony Liguori
2011-03-07 14:36 ` Stefan Hajnoczi
2011-03-07 14:41 ` Anthony Liguori
2011-03-07 1:22 ` [Qemu-devel] [PATCH 12/22] qapi: add QAPI module type Anthony Liguori
2011-03-07 1:22 ` [Qemu-devel] [PATCH 13/22] qapi: add code generators for QMP command marshaling Anthony Liguori
2011-03-07 13:27 ` Stefan Hajnoczi
2011-03-07 13:44 ` Anthony Liguori
2011-03-07 14:38 ` Stefan Hajnoczi
2011-03-07 1:22 ` [Qemu-devel] [PATCH 14/22] qapi: add query-version QMP command Anthony Liguori
2011-03-07 13:35 ` Stefan Hajnoczi
2011-03-07 13:41 ` Anthony Liguori
2011-03-09 13:28 ` Avi Kivity
2011-03-09 13:44 ` Anthony Liguori
2011-03-09 13:51 ` Avi Kivity
2011-03-09 14:13 ` Anthony Liguori
2011-03-09 13:36 ` Avi Kivity
2011-03-09 14:11 ` Anthony Liguori
2011-03-09 14:37 ` Avi Kivity
2011-03-09 14:47 ` Anthony Liguori
2011-03-10 12:41 ` Avi Kivity
2011-03-10 12:46 ` Avi Kivity
2011-03-10 13:52 ` Anthony Liguori
2011-03-07 1:22 ` [Qemu-devel] [PATCH 15/22] qapi: add new QMP server that uses CharDriverState Anthony Liguori
2011-03-07 13:52 ` Stefan Hajnoczi
2011-03-07 14:02 ` Anthony Liguori
2011-03-07 1:22 ` [Qemu-devel] [PATCH 16/22] vl: add a new -qmp2 option to expose experimental QMP server Anthony Liguori
2011-03-07 1:22 ` [Qemu-devel] [PATCH 17/22] qapi: add QMP quit command Anthony Liguori
2011-03-07 1:23 ` [Qemu-devel] [PATCH 18/22] qapi: add QMP qmp_capabilities command Anthony Liguori
2011-03-07 1:23 ` [Qemu-devel] [PATCH 19/22] qapi: add QMP put-event command Anthony Liguori
2011-03-09 13:31 ` Avi Kivity
2011-03-09 13:48 ` Anthony Liguori
2011-03-09 13:58 ` Avi Kivity
2011-03-09 14:26 ` Anthony Liguori
2011-03-10 12:39 ` Avi Kivity
2011-03-10 14:12 ` Anthony Liguori
2011-03-10 14:24 ` Avi Kivity
2011-03-10 15:30 ` Avi Kivity
2011-03-10 15:41 ` Anthony Liguori
2011-03-10 15:49 ` Avi Kivity
2011-03-10 16:42 ` Anthony Liguori
2011-03-12 20:37 ` Avi Kivity
2011-03-10 15:33 ` Anthony Liguori
2011-03-10 15:45 ` Avi Kivity
2011-03-10 16:04 ` Anthony Liguori
2011-03-12 20:42 ` Avi Kivity
2011-03-12 23:30 ` Anthony Liguori
2011-03-07 1:23 ` [Qemu-devel] [PATCH 20/22] qapi: add code generator for libqmp Anthony Liguori
2011-03-07 1:23 ` [Qemu-devel] [PATCH 21/22] qapi: add test-libqmp Anthony Liguori
2011-03-07 1:23 ` [Qemu-devel] [PATCH 22/22] qapi: generate HTML report for test-libqmp Anthony Liguori
2011-03-07 2:11 ` Anthony Liguori
2011-03-07 1:26 ` [Qemu-devel] [PATCH] qapi: qmp-types.c and qmp-types.h Anthony Liguori
2011-03-07 1:27 ` [Qemu-devel] [PATCH] qapi: qmp-marshal-types.c and qmp-marshal-types.h Anthony Liguori
2011-03-07 1:28 ` [Qemu-devel] [PATCH] qapi: add qmp-marshal.c and qmp.h Anthony Liguori
2011-03-07 1:29 ` [Qemu-devel] [PATCH] qapi: add libqmp.c and libqmp.h Anthony Liguori
2011-03-07 15:08 ` [Qemu-devel] [PATCH 00/22] QAPI Round 1 Stefan Hajnoczi
2011-03-07 15:59 ` Anthony Liguori
2011-03-08 11:12 ` Avi Kivity
2011-03-08 13:35 ` Anthony Liguori
2011-03-08 13:45 ` Avi Kivity
2011-03-08 13:54 ` Anthony Liguori
2011-03-08 14:00 ` Avi Kivity
2011-03-08 14:10 ` Anthony Liguori
2011-03-08 14:17 ` Avi Kivity
2011-03-08 14:20 ` Anthony Liguori
2011-03-08 14:38 ` Anthony Liguori
2011-03-08 14:52 ` Avi Kivity
2011-03-08 15:03 ` Anthony Liguori
2011-03-08 17:44 ` Avi Kivity
2011-03-08 19:19 ` Anthony Liguori
2011-03-09 8:51 ` Avi Kivity
2011-03-09 13:12 ` Anthony Liguori
2011-03-09 13:14 ` Avi Kivity
2011-03-09 13:51 ` Anthony Liguori
2011-03-09 13:55 ` Avi Kivity
2011-03-09 14:15 ` Anthony Liguori
2011-03-09 13:51 ` Michael Roth
2011-03-07 20:59 ` Anthony Liguori
2011-03-07 22:00 ` Stefan Hajnoczi
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).