[libvirt] [PATCH 00/14] Introduce support for the JSON based QEMU monitor

This is an updated series fully implementing the new QEMU monitor protocol based on JSON instead of plain text. I previously posted a proof of concept here http://www.redhat.com/archives/libvir-list/2009-November/msg00098.html Since that time, the QEMU side is actually working, so I've been able todo a real impl for libvirt now & also test it. I threw out the JSON library code I used last time, since it turned out to be quite unreliable code. Instead I'm using YAJL which is a much nicer library & integrates with libvirt very nicely. Daniel

This introduces simple API for handling JSON data. There is an internal data structure 'virJSONValuePtr' which stores a arbitrary nested JSON value (number, string, array, object, nul, etc). There are APIs for constructing/querying objects and APIs for parsing/formatting string formatted JSON data. This uses the YAJL library for parsing/formatting from http://lloyd.github.com/yajl/ * src/util/json.h, src/util/json.c: Data structures and APIs for representing JSON data, and parsing/formatting it * configure.in: Add check for yajl library * libvirt.spec.in: Add build requires for yajl * src/Makefile.am: Add json.c/h * src/libvirt_private.syms: Export JSON symbols to drivers --- configure.in | 55 +++ libvirt.spec.in | 14 + po/POTFILES.in | 1 + src/Makefile.am | 8 +- src/libvirt_private.syms | 47 ++ src/util/json.c | 1043 ++++++++++++++++++++++++++++++++++++++++++++++ src/util/json.h | 131 ++++++ 7 files changed, 1296 insertions(+), 3 deletions(-) create mode 100644 src/util/json.c create mode 100644 src/util/json.h diff --git a/configure.in b/configure.in index f735bba..41f50fc 100644 --- a/configure.in +++ b/configure.in @@ -645,6 +645,56 @@ AC_SUBST([SASL_CFLAGS]) AC_SUBST([SASL_LIBS]) +dnl YAJL JSON library http://lloyd.github.com/yajl/ +AC_ARG_WITH([yajl], + [ --with-yajl use YAJL for JSON parsing/formatting], + [], + [with_yajl=check]) + +YAJL_CFLAGS= +YAJL_LIBS= +if test "x$with_yajl" != "xno"; then + if test "x$with_yajl" != "xyes" -a "x$with_yajl" != "xcheck"; then + YAJL_CFLAGS="-I$with_yajl/include" + YAJL_LIBS="-L$with_yajl/lib" + fi + fail=0 + old_cppflags="$CPPFLAGS" + old_ldflags="$LDFLAGS" + CPPFLAGS="$CPPFLAGS $YAJL_CFLAGS" + LDFLAGS="$LDFLAGS $YAJL_LIBS" + AC_CHECK_HEADER([yajl/yajl_common.h],[],[ + if test "x$with_yajl" != "xcheck" ; then + with_yajl=no + else + fail=1 + fi]) + if test "x$with_yajl" != "xno" ; then + AC_CHECK_LIB([yajl], [yajl_parse],[ + YAJL_LIBS="$YAJL_LIBS -lyajl" + with_yajl=yes + ],[ + if test "x$with_yajl" = "xcheck" ; then + with_yajl=no + else + fail=1 + fi + ]) + fi + test $fail = 1 && + AC_MSG_ERROR([You must install the YAJL development package in order to compile libvirt]) + CPPFLAGS="$old_cppflags" + LDFLAGS="$old_ldflags" + if test "x$with_yajl" = "xyes" ; then + AC_DEFINE_UNQUOTED([HAVE_YAJL], 1, + [whether YAJL is available for JSON parsing/formatting]) + fi +fi +AM_CONDITIONAL([HAVE_YAJL], [test "x$with_yajl" = "xyes"]) +AC_SUBST([YAJL_CFLAGS]) +AC_SUBST([YAJL_LIBS]) + + dnl PolicyKit library POLKIT_CFLAGS= POLKIT_LIBS= @@ -1859,6 +1909,11 @@ AC_MSG_NOTICE([ sasl: $SASL_CFLAGS $SASL_LIBS]) else AC_MSG_NOTICE([ sasl: no]) fi +if test "$with_yajl" != "no" ; then +AC_MSG_NOTICE([ yajl: $YAJL_CFLAGS $YAJL_LIBS]) +else +AC_MSG_NOTICE([ yajl: no]) +fi if test "$with_avahi" = "yes" ; then AC_MSG_NOTICE([ avahi: $AVAHI_CFLAGS $AVAHI_LIBS]) else diff --git a/libvirt.spec.in b/libvirt.spec.in index dba14df..408ad05 100644 --- a/libvirt.spec.in +++ b/libvirt.spec.in @@ -60,6 +60,7 @@ %define with_netcf 0%{!?_without_netcf:0} %define with_udev 0%{!?_without_udev:0} %define with_hal 0%{!?_without_hal:0} +%define with_yajl 0%{!?_without_yajl:0} # Non-server/HV driver defaults which are always enabled %define with_python 0%{!?_without_python:1} @@ -141,6 +142,11 @@ %define with_hal 0%{!?_without_hal:%{server_drivers}} %endif +# Enable yajl library for JSON mode with QEMU +%if 0%{?fedora} >= 13 || 0%{?rhel} >= 6 +%define with_yajl 0%{!?_without_yajl:%{server_drivers}} +%endif + # Force QEMU to run as non-root %if 0%{?fedora} >= 12 || 0%{?rhel} >= 6 %define qemu_user qemu @@ -257,6 +263,9 @@ BuildRequires: hal-devel BuildRequires: libudev-devel >= 145 BuildRequires: libpciaccess-devel >= 0.10.9 %endif +%if %{with_yajl} +BuildRequires: yajl-devel +%endif %if %{with_avahi} BuildRequires: avahi-devel %endif @@ -495,6 +504,10 @@ of recent versions of Linux (and other OSes). %define _without_udev --without-udev %endif +%if ! %{with_yajl} +%define _without_yajl --without-yajl +%endif + %configure %{?_without_xen} \ %{?_without_qemu} \ %{?_without_openvz} \ @@ -522,6 +535,7 @@ of recent versions of Linux (and other OSes). %{?_without_selinux} \ %{?_without_hal} \ %{?_without_udev} \ + %{?_without_yajl} \ --with-qemu-user=%{qemu_user} \ --with-qemu-group=%{qemu_group} \ --with-init-script=redhat \ diff --git a/po/POTFILES.in b/po/POTFILES.in index 116aa87..9864259 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -50,6 +50,7 @@ src/uml/uml_driver.c src/util/bridge.c src/util/conf.c src/util/iptables.c +src/util/json.c src/util/logging.c src/util/pci.c src/util/processinfo.c diff --git a/src/Makefile.am b/src/Makefile.am index 9a3c9c8..b0775a8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -52,6 +52,7 @@ UTIL_SOURCES = \ util/hash.c util/hash.h \ util/iptables.c util/iptables.h \ util/ebtables.c util/ebtables.h \ + util/json.c util/json.h \ util/logging.c util/logging.h \ util/memory.c util/memory.h \ util/pci.c util/pci.h \ @@ -283,8 +284,8 @@ noinst_LTLIBRARIES = libvirt_util.la libvirt_la_LIBADD = libvirt_util.la libvirt_util_la_SOURCES = \ $(UTIL_SOURCES) -libvirt_util_la_CFLAGS = $(CAPNG_CFLAGS) -libvirt_util_la_LDFLAGS = $(CAPNG_LIBS) +libvirt_util_la_CFLAGS = $(CAPNG_CFLAGS) $(YAJL_CFLAGS) +libvirt_util_la_LDFLAGS = $(CAPNG_LIBS) $(YAJL_LIBS) noinst_LTLIBRARIES += libvirt_conf.la @@ -828,12 +829,13 @@ libvirt_lxc_SOURCES = \ $(NODE_INFO_SOURCES) \ $(ENCRYPTION_CONF_SOURCES) \ $(DOMAIN_CONF_SOURCES) -libvirt_lxc_LDFLAGS = $(WARN_CFLAGS) $(COVERAGE_LDCFLAGS) $(CAPNG_LIBS) +libvirt_lxc_LDFLAGS = $(WARN_CFLAGS) $(COVERAGE_LDCFLAGS) $(CAPNG_LIBS) $(YAJL_LIBS) libvirt_lxc_LDADD = $(LIBXML_LIBS) $(NUMACTL_LIBS) ../gnulib/lib/libgnu.la libvirt_lxc_CFLAGS = \ $(LIBPARTED_CFLAGS) \ $(NUMACTL_CFLAGS) \ $(CAPNG_CFLAGS) \ + $(YAJL_CFLAGS) \ -I@top_srcdir@/src/conf endif endif diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index e880c2e..ee7f3ed 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -269,6 +269,53 @@ virRegisterDeviceMonitor; virRegisterSecretDriver; +# json.h +virJSONValueFree; +virJSONValueNewString; +virJSONValueNewStringLen; +virJSONValueNewNumberInt; +virJSONValueNewNumberUint; +virJSONValueNewNumberLong; +virJSONValueNewNumberUlong; +virJSONValueNewNumberDouble; +virJSONValueNewBoolean; +virJSONValueNewNull; +virJSONValueNewArray; +virJSONValueNewObject; +virJSONValueObjectAppend; +virJSONValueObjectAppendString; +virJSONValueObjectAppendNumberInt; +virJSONValueObjectAppendNumberUint; +virJSONValueObjectAppendNumberLong; +virJSONValueObjectAppendNumberUlong; +virJSONValueObjectAppendNumberDouble; +virJSONValueObjectAppendBoolean; +virJSONValueObjectAppendNull; +virJSONValueArrayAppend; +virJSONValueObjectHasKey; +virJSONValueObjectGet; +virJSONValueArraySize; +virJSONValueArrayGet; +virJSONValueGetString; +virJSONValueGetNumberInt; +virJSONValueGetNumberUint; +virJSONValueGetNumberLong; +virJSONValueGetNumberUlong; +virJSONValueGetNumberDouble; +virJSONValueGetBoolean; +virJSONValueIsNull; +virJSONValueObjectGetString; +virJSONValueObjectGetNumberInt; +virJSONValueObjectGetNumberUint; +virJSONValueObjectGetNumberLong; +virJSONValueObjectGetNumberUlong; +virJSONValueObjectGetNumberDouble; +virJSONValueObjectGetBoolean; +virJSONValueObjectIsNull; +virJSONValueFromString; +virJSONValueToString; + + # logging.h virLogMessage; virLogGetNbFilters; diff --git a/src/util/json.c b/src/util/json.c new file mode 100644 index 0000000..f359b95 --- /dev/null +++ b/src/util/json.c @@ -0,0 +1,1043 @@ +/* + * json.h: JSON object parsing/formatting + * + * Copyright (C) 2008 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#include <config.h> + +#include "json.h" +#include "memory.h" +#include "virterror_internal.h" +#include "logging.h" +#include "util.h" + +#include <yajl/yajl_gen.h> +#include <yajl/yajl_parse.h> + +/* XXX fixme */ +#define VIR_FROM_THIS VIR_FROM_NONE +#define ReportError(conn, code, fmt...) \ + virReportErrorHelper(conn, VIR_FROM_NONE, code, __FILE__, \ + __FUNCTION__, __LINE__, fmt) + + +typedef struct _virJSONParserState virJSONParserState; +typedef virJSONParserState *virJSONParserStatePtr; +struct _virJSONParserState { + virJSONValuePtr value; + char *key; +}; + +typedef struct _virJSONParser virJSONParser; +typedef virJSONParser *virJSONParserPtr; +struct _virJSONParser { + virJSONValuePtr head; + virJSONParserStatePtr state; + unsigned int nstate; +}; + + +void virJSONValueFree(virJSONValuePtr value) +{ + int i; + if (!value) + return; + + switch (value->type) { + case VIR_JSON_TYPE_OBJECT: + for (i = 0 ; i < value->data.array.nvalues ; i++) { + VIR_FREE(value->data.object.pairs[i].key); + virJSONValueFree(value->data.object.pairs[i].value); + } + VIR_FREE(value->data.object.pairs); + break; + case VIR_JSON_TYPE_ARRAY: + for (i = 0 ; i < value->data.array.nvalues ; i++) + virJSONValueFree(value->data.array.values[i]); + VIR_FREE(value->data.array.values); + break; + case VIR_JSON_TYPE_STRING: + VIR_FREE(value->data.string); + break; + case VIR_JSON_TYPE_NUMBER: + VIR_FREE(value->data.number); + break; + + } +} + + +virJSONValuePtr virJSONValueNewString(const char *data) +{ + virJSONValuePtr val; + + if (!data) + return virJSONValueNewNull(); + + if (VIR_ALLOC(val) < 0) + return NULL; + + val->type = VIR_JSON_TYPE_STRING; + if (!(val->data.string = strdup(data))) { + VIR_FREE(val); + return NULL; + } + + return val; +} + +virJSONValuePtr virJSONValueNewStringLen(const char *data, size_t length) +{ + virJSONValuePtr val; + + if (!data) + return virJSONValueNewNull(); + + if (VIR_ALLOC(val) < 0) + return NULL; + + val->type = VIR_JSON_TYPE_STRING; + if (!(val->data.string = strndup(data, length))) { + VIR_FREE(val); + return NULL; + } + + return val; +} + +static virJSONValuePtr virJSONValueNewNumber(const char *data) +{ + virJSONValuePtr val; + + if (VIR_ALLOC(val) < 0) + return NULL; + + val->type = VIR_JSON_TYPE_NUMBER; + if (!(val->data.number = strdup(data))) { + VIR_FREE(val); + return NULL; + } + + return val; +} + +virJSONValuePtr virJSONValueNewNumberInt(int data) +{ + virJSONValuePtr val = NULL; + char *str; + if (virAsprintf(&str, "%i", data) < 0) + return NULL; + val = virJSONValueNewNumber(str); + VIR_FREE(str); + return val; +} + + +virJSONValuePtr virJSONValueNewNumberUint(unsigned int data) +{ + virJSONValuePtr val = NULL; + char *str; + if (virAsprintf(&str, "%u", data) < 0) + return NULL; + val = virJSONValueNewNumber(str); + VIR_FREE(str); + return val; +} + + +virJSONValuePtr virJSONValueNewNumberLong(long long data) +{ + virJSONValuePtr val = NULL; + char *str; + if (virAsprintf(&str, "%lld", data) < 0) + return NULL; + val = virJSONValueNewNumber(str); + VIR_FREE(str); + return val; +} + + +virJSONValuePtr virJSONValueNewNumberUlong(unsigned long long data) +{ + virJSONValuePtr val = NULL; + char *str; + if (virAsprintf(&str, "%llu", data) < 0) + return NULL; + val = virJSONValueNewNumber(str); + VIR_FREE(str); + return val; +} + + +virJSONValuePtr virJSONValueNewNumberDouble(double data) +{ + virJSONValuePtr val = NULL; + char *str; + if (virAsprintf(&str, "%lf", data) < 0) + return NULL; + val = virJSONValueNewNumber(str); + VIR_FREE(str); + return val; +} + + +virJSONValuePtr virJSONValueNewBoolean(int boolean) +{ + virJSONValuePtr val; + + if (VIR_ALLOC(val) < 0) + return NULL; + + val->type = VIR_JSON_TYPE_BOOLEAN; + val->data.boolean = boolean; + + return val; +} + +virJSONValuePtr virJSONValueNewNull(void) +{ + virJSONValuePtr val; + + if (VIR_ALLOC(val) < 0) + return NULL; + + val->type = VIR_JSON_TYPE_NULL; + + return val; +} + +virJSONValuePtr virJSONValueNewArray(void) +{ + virJSONValuePtr val; + + if (VIR_ALLOC(val) < 0) + return NULL; + + val->type = VIR_JSON_TYPE_ARRAY; + + return val; +} + +virJSONValuePtr virJSONValueNewObject(void) +{ + virJSONValuePtr val; + + if (VIR_ALLOC(val) < 0) + return NULL; + + val->type = VIR_JSON_TYPE_OBJECT; + + return val; +} + +int virJSONValueObjectAppend(virJSONValuePtr object, const char *key, virJSONValuePtr value) +{ + char *newkey; + + if (object->type != VIR_JSON_TYPE_OBJECT) + return -1; + + if (virJSONValueObjectHasKey(object, key)) + return -1; + + if (!(newkey = strdup(key))) + return -1; + + if (VIR_REALLOC_N(object->data.object.pairs, + object->data.object.npairs + 1) < 0) { + VIR_FREE(newkey); + return -1; + } + + object->data.object.pairs[object->data.object.npairs].key = newkey; + object->data.object.pairs[object->data.object.npairs].value = value; + object->data.object.npairs++; + + return 0; +} + + +int virJSONValueObjectAppendString(virJSONValuePtr object, const char *key, const char *value) +{ + virJSONValuePtr jvalue = virJSONValueNewString(value); + if (!jvalue) + return -1; + if (virJSONValueObjectAppend(object, key, jvalue) < 0) { + virJSONValueFree(jvalue); + return -1; + } + return 0; +} + +int virJSONValueObjectAppendNumberInt(virJSONValuePtr object, const char *key, int number) +{ + virJSONValuePtr jvalue = virJSONValueNewNumberInt(number); + if (!jvalue) + return -1; + if (virJSONValueObjectAppend(object, key, jvalue) < 0) { + virJSONValueFree(jvalue); + return -1; + } + return 0; +} + + +int virJSONValueObjectAppendNumberUint(virJSONValuePtr object, const char *key, unsigned int number) +{ + virJSONValuePtr jvalue = virJSONValueNewNumberUint(number); + if (!jvalue) + return -1; + if (virJSONValueObjectAppend(object, key, jvalue) < 0) { + virJSONValueFree(jvalue); + return -1; + } + return 0; +} + +int virJSONValueObjectAppendNumberLong(virJSONValuePtr object, const char *key, long long number) +{ + virJSONValuePtr jvalue = virJSONValueNewNumberLong(number); + if (!jvalue) + return -1; + if (virJSONValueObjectAppend(object, key, jvalue) < 0) { + virJSONValueFree(jvalue); + return -1; + } + return 0; +} + +int virJSONValueObjectAppendNumberUlong(virJSONValuePtr object, const char *key, unsigned long long number) +{ + virJSONValuePtr jvalue = virJSONValueNewNumberUlong(number); + if (!jvalue) + return -1; + if (virJSONValueObjectAppend(object, key, jvalue) < 0) { + virJSONValueFree(jvalue); + return -1; + } + return 0; +} + +int virJSONValueObjectAppendNumberDouble(virJSONValuePtr object, const char *key, double number) +{ + virJSONValuePtr jvalue = virJSONValueNewNumberDouble(number); + if (!jvalue) + return -1; + if (virJSONValueObjectAppend(object, key, jvalue) < 0) { + virJSONValueFree(jvalue); + return -1; + } + return 0; +} + +int virJSONValueObjectAppendBoolean(virJSONValuePtr object, const char *key, int boolean) +{ + virJSONValuePtr jvalue = virJSONValueNewBoolean(boolean); + if (!jvalue) + return -1; + if (virJSONValueObjectAppend(object, key, jvalue) < 0) { + virJSONValueFree(jvalue); + return -1; + } + return 0; +} + +int virJSONValueObjectAppendNull(virJSONValuePtr object, const char *key) +{ + virJSONValuePtr jvalue = virJSONValueNewNull(); + if (!jvalue) + return -1; + if (virJSONValueObjectAppend(object, key, jvalue) < 0) { + virJSONValueFree(jvalue); + return -1; + } + return 0; +} + + +int virJSONValueArrayAppend(virJSONValuePtr array, virJSONValuePtr value) +{ + if (array->type != VIR_JSON_TYPE_ARRAY) + return -1; + + if (VIR_REALLOC_N(array->data.array.values, + array->data.array.nvalues + 1) < 0) + return -1; + + array->data.array.values[array->data.array.nvalues] = value; + array->data.array.nvalues++; + + return 0; +} + +int virJSONValueObjectHasKey(virJSONValuePtr object, const char *key) +{ + int i; + + if (object->type != VIR_JSON_TYPE_OBJECT) + return -1; + + for (i = 0 ; i < object->data.object.npairs ; i++) { + if (STREQ(object->data.object.pairs[i].key, key)) + return 1; + } + + return 0; +} + +virJSONValuePtr virJSONValueObjectGet(virJSONValuePtr object, const char *key) +{ + int i; + + if (object->type != VIR_JSON_TYPE_OBJECT) + return NULL; + + for (i = 0 ; i < object->data.object.npairs ; i++) { + if (STREQ(object->data.object.pairs[i].key, key)) + return object->data.object.pairs[i].value; + } + + return NULL; +} + +int virJSONValueArraySize(virJSONValuePtr array) +{ + if (array->type != VIR_JSON_TYPE_ARRAY) + return -1; + + return array->data.array.nvalues; +} + + +virJSONValuePtr virJSONValueArrayGet(virJSONValuePtr array, unsigned int element) +{ + if (array->type != VIR_JSON_TYPE_ARRAY) + return NULL; + + if (element >= array->data.array.nvalues) + return NULL; + + return array->data.array.values[element]; +} + +char *virJSONValueGetString(virJSONValuePtr string) +{ + if (string->type != VIR_JSON_TYPE_STRING) + return NULL; + + return string->data.string; +} + + +int virJSONValueGetNumberInt(virJSONValuePtr number, int *value) +{ + if (number->type != VIR_JSON_TYPE_NUMBER) + return -1; + + return virStrToLong_i(number->data.number, NULL, 10, value); +} + +int virJSONValueGetNumberUint(virJSONValuePtr number, unsigned int *value) +{ + if (number->type != VIR_JSON_TYPE_NUMBER) + return -1; + + return virStrToLong_ui(number->data.number, NULL, 10, value); +} + +int virJSONValueGetNumberLong(virJSONValuePtr number, long long *value) +{ + if (number->type != VIR_JSON_TYPE_NUMBER) + return -1; + + return virStrToLong_ll(number->data.number, NULL, 10, value); +} + +int virJSONValueGetNumberUlong(virJSONValuePtr number, unsigned long long *value) +{ + if (number->type != VIR_JSON_TYPE_NUMBER) + return -1; + + return virStrToLong_ull(number->data.number, NULL, 10, value); +} + +int virJSONValueGetNumberDouble(virJSONValuePtr number, double *value) +{ + if (number->type != VIR_JSON_TYPE_NUMBER) + return -1; + + return virStrToDouble(number->data.number, NULL, value); +} + + +int virJSONValueGetBoolean(virJSONValuePtr val) +{ + if (val->type != VIR_JSON_TYPE_NUMBER) + return -1; + + return val->data.boolean; +} + + +int virJSONValueIsNull(virJSONValuePtr val) +{ + if (val->type != VIR_JSON_TYPE_NULL) + return 0; + + return 1; +} + + +char *virJSONValueObjectGetString(virJSONValuePtr object, const char *key) +{ + virJSONValuePtr val; + if (object->type != VIR_JSON_TYPE_OBJECT) + return NULL; + + val = virJSONValueObjectGet(object, key); + if (!val) + return NULL; + + return virJSONValueGetString(val); +} + + +int virJSONValueObjectGetNumberInt(virJSONValuePtr object, const char *key, int *value) +{ + virJSONValuePtr val; + if (object->type != VIR_JSON_TYPE_OBJECT) + return -1; + + val = virJSONValueObjectGet(object, key); + if (!val) + return -1; + + return virJSONValueGetNumberInt(val, value); +} + + +int virJSONValueObjectGetNumberUint(virJSONValuePtr object, const char *key, unsigned int *value) +{ + virJSONValuePtr val; + if (object->type != VIR_JSON_TYPE_OBJECT) + return -1; + + val = virJSONValueObjectGet(object, key); + if (!val) + return -1; + + return virJSONValueGetNumberUint(val, value); +} + + +int virJSONValueObjectGetNumberLong(virJSONValuePtr object, const char *key, long long *value) +{ + virJSONValuePtr val; + if (object->type != VIR_JSON_TYPE_OBJECT) + return -1; + + val = virJSONValueObjectGet(object, key); + if (!val) + return -1; + + return virJSONValueGetNumberLong(val, value); +} + + +int virJSONValueObjectGetNumberUlong(virJSONValuePtr object, const char *key, unsigned long long *value) +{ + virJSONValuePtr val; + if (object->type != VIR_JSON_TYPE_OBJECT) + return -1; + + val = virJSONValueObjectGet(object, key); + if (!val) + return -1; + + return virJSONValueGetNumberUlong(val, value); +} + + +int virJSONValueObjectGetNumberDouble(virJSONValuePtr object, const char *key, double *value) +{ + virJSONValuePtr val; + if (object->type != VIR_JSON_TYPE_OBJECT) + return -1; + + val = virJSONValueObjectGet(object, key); + if (!val) + return -1; + + return virJSONValueGetNumberDouble(val, value); +} + + +int virJSONValueObjectGetBoolean(virJSONValuePtr object, const char *key) +{ + virJSONValuePtr val; + if (object->type != VIR_JSON_TYPE_OBJECT) + return -1; + + val = virJSONValueObjectGet(object, key); + if (!val) + return -1; + + return virJSONValueGetBoolean(val); +} + + +int virJSONValueObjectIsNull(virJSONValuePtr object, const char *key) +{ + virJSONValuePtr val; + if (object->type != VIR_JSON_TYPE_OBJECT) + return -1; + + val = virJSONValueObjectGet(object, key); + if (!val) + return -1; + + return virJSONValueIsNull(val); +} + + +#if HAVE_YAJL +static int virJSONParserInsertValue(virJSONParserPtr parser, + virJSONValuePtr value) +{ + if (!parser->head) { + parser->head = value; + } else { + virJSONParserStatePtr state; + if (!parser->nstate) { + VIR_DEBUG0("got a value to insert without a container"); + return -1; + } + + state = &parser->state[parser->nstate-1]; + + switch (state->value->type) { + case VIR_JSON_TYPE_OBJECT: { + if (!state->key) { + VIR_DEBUG0("missing key when inserting object value"); + return -1; + } + + if (virJSONValueObjectAppend(state->value, + state->key, + value) < 0) + return -1; + + VIR_FREE(state->key); + } break; + + case VIR_JSON_TYPE_ARRAY: { + if (state->key) { + VIR_DEBUG0("unexpected key when inserting array value"); + return -1; + } + + if (virJSONValueArrayAppend(state->value, + value) < 0) + return -1; + } break; + + default: + VIR_DEBUG0("unexpected value type, not a container"); + return -1; + } + } + + return 0; +} + +static int virJSONParserHandleNull(void * ctx) +{ + virJSONParserPtr parser = ctx; + virJSONValuePtr value = virJSONValueNewNull(); + + VIR_DEBUG("parser=%p", parser); + + if (!value) + return 0; + + if (virJSONParserInsertValue(parser, value) < 0) { + virJSONValueFree(value); + return 0; + } + + return 1; +} + +static int virJSONParserHandleBoolean(void * ctx, int boolean) +{ + virJSONParserPtr parser = ctx; + virJSONValuePtr value = virJSONValueNewBoolean(boolean); + + VIR_DEBUG("parser=%p boolean=%d", parser, boolean); + + if (!value) + return 0; + + if (virJSONParserInsertValue(parser, value) < 0) { + virJSONValueFree(value); + return 0; + } + + return 1; +} + +static int virJSONParserHandleNumber(void * ctx, + const char * s, + unsigned int l) +{ + virJSONParserPtr parser = ctx; + char *str = strndup(s, l); + virJSONValuePtr value; + + if (!str) + return -1; + value = virJSONValueNewNumber(str); + VIR_FREE(str); + + VIR_DEBUG("parser=%p str=%s", parser, str); + + if (!value) + return 0; + + if (virJSONParserInsertValue(parser, value) < 0) { + virJSONValueFree(value); + return 0; + } + + return 1; +} + +static int virJSONParserHandleString(void * ctx, + const unsigned char * stringVal, + unsigned int stringLen) +{ + virJSONParserPtr parser = ctx; + virJSONValuePtr value = virJSONValueNewStringLen((const char *)stringVal, + stringLen); + + VIR_DEBUG("parser=%p str=%p", parser, (const char *)stringVal); + + if (!value) + return 0; + + if (virJSONParserInsertValue(parser, value) < 0) { + virJSONValueFree(value); + return 0; + } + + return 1; +} + +static int virJSONParserHandleMapKey(void * ctx, + const unsigned char * stringVal, + unsigned int stringLen) +{ + virJSONParserPtr parser = ctx; + virJSONParserStatePtr state; + + VIR_DEBUG("parser=%p key=%p", parser, (const char *)stringVal); + + if (!parser->nstate) + return 0; + + state = &parser->state[parser->nstate-1]; + if (state->key) + return 0; + state->key = strndup((const char *)stringVal, stringLen); + if (!state->key) + return 0; + return 1; +} + +static int virJSONParserHandleStartMap(void * ctx) +{ + virJSONParserPtr parser = ctx; + virJSONValuePtr value = virJSONValueNewObject(); + + VIR_DEBUG("parser=%p", parser); + + if (!value) + return 0; + + if (virJSONParserInsertValue(parser, value) < 0) { + virJSONValueFree(value); + return 0; + } + + if (VIR_REALLOC_N(parser->state, + parser->nstate + 1) < 0) + return 0; + + parser->state[parser->nstate].value = value; + parser->state[parser->nstate].key = NULL; + parser->nstate++; + + return 1; +} + + +static int virJSONParserHandleEndMap(void * ctx) +{ + virJSONParserPtr parser = ctx; + virJSONParserStatePtr state; + + VIR_DEBUG("parser=%p", parser); + + if (!parser->nstate) + return 0; + + state = &(parser->state[parser->nstate-1]); + if (state->key) { + VIR_FREE(state->key); + return 0; + } + + if (VIR_REALLOC_N(parser->state, + parser->nstate - 1) < 0) + return 0; + parser->nstate--; + + return 1; +} + +static int virJSONParserHandleStartArray(void * ctx) +{ + virJSONParserPtr parser = ctx; + virJSONValuePtr value = virJSONValueNewArray(); + + VIR_DEBUG("parser=%p", parser); + + if (!value) + return 0; + + if (virJSONParserInsertValue(parser, value) < 0) { + virJSONValueFree(value); + return 0; + } + + if (VIR_REALLOC_N(parser->state, + parser->nstate + 1) < 0) + return 0; + + parser->state[parser->nstate].value = value; + parser->state[parser->nstate].key = NULL; + parser->nstate++; + + return 1; +} + +static int virJSONParserHandleEndArray(void * ctx) +{ + virJSONParserPtr parser = ctx; + virJSONParserStatePtr state; + + VIR_DEBUG("parser=%p", parser); + + if (!parser->nstate) + return 0; + + state = &(parser->state[parser->nstate-1]); + if (state->key) { + VIR_FREE(state->key); + return 0; + } + + if (VIR_REALLOC_N(parser->state, + parser->nstate - 1) < 0) + return 0; + parser->nstate--; + + return 1; +} + +static const yajl_callbacks parserCallbacks = { + virJSONParserHandleNull, + virJSONParserHandleBoolean, + NULL, + NULL, + virJSONParserHandleNumber, + virJSONParserHandleString, + virJSONParserHandleStartMap, + virJSONParserHandleMapKey, + virJSONParserHandleEndMap, + virJSONParserHandleStartArray, + virJSONParserHandleEndArray +}; + + +/* XXX add an incremental streaming parser - yajl trivially supports it */ +virJSONValuePtr virJSONValueFromString(const char *jsonstring) +{ + yajl_parser_config cfg = { 1, 1 }; + yajl_handle hand; + virJSONParser parser = { NULL, NULL, 0 }; + + VIR_DEBUG("string=%s", jsonstring); + + hand = yajl_alloc(&parserCallbacks, &cfg, NULL, &parser); + + if (yajl_parse(hand, + (const unsigned char *)jsonstring, + strlen(jsonstring)) != yajl_status_ok) { + unsigned char *errstr = yajl_get_error(hand, 1, + (const unsigned char*)jsonstring, + strlen(jsonstring)); + + ReportError(NULL, VIR_ERR_INTERNAL_ERROR, + _("cannot parse json %s: %s"), + jsonstring, (const char*) errstr); + VIR_FREE(errstr); + virJSONValueFree(parser.head); + goto cleanup; + } + +cleanup: + yajl_free(hand); + + if (parser.nstate) { + int i; + VIR_WARN("cleanup state %d", parser.nstate); + for (i = 0 ; i < parser.nstate ; i++) { + VIR_FREE(parser.state[i].key); + } + } + + VIR_DEBUG("result=%p", parser.head); + + return parser.head; +} + + +static int virJSONValueToStringOne(virJSONValuePtr object, + yajl_gen g) +{ + int i; + + VIR_DEBUG("object=%p type=%d gen=%p", object, object->type, g); + + switch (object->type) { + case VIR_JSON_TYPE_OBJECT: + if (yajl_gen_map_open(g) != yajl_gen_status_ok) + return -1; + for (i = 0; i < object->data.object.npairs ; i++) { + if (yajl_gen_string(g, + (unsigned char *)object->data.object.pairs[i].key, + strlen(object->data.object.pairs[i].key)) + != yajl_gen_status_ok) + return -1; + if (virJSONValueToStringOne(object->data.object.pairs[i].value, g) < 0) + return -1; + } + if (yajl_gen_map_close(g) != yajl_gen_status_ok) + return -1; + break; + case VIR_JSON_TYPE_ARRAY: + if (yajl_gen_array_open(g) != yajl_gen_status_ok) + return -1; + for (i = 0; i < object->data.array.nvalues ; i++) { + if (virJSONValueToStringOne(object->data.array.values[i], g) < 0) + return -1; + } + if (yajl_gen_array_close(g) != yajl_gen_status_ok) + return -1; + break; + + case VIR_JSON_TYPE_STRING: + if (yajl_gen_string(g, (unsigned char *)object->data.string, + strlen(object->data.string)) != yajl_gen_status_ok) + return -1; + break; + + case VIR_JSON_TYPE_NUMBER: + if (yajl_gen_number(g, object->data.number, + strlen(object->data.number)) != yajl_gen_status_ok) + return -1; + break; + + case VIR_JSON_TYPE_BOOLEAN: + if (yajl_gen_bool(g, object->data.boolean) != yajl_gen_status_ok) + return -1; + break; + + case VIR_JSON_TYPE_NULL: + if (yajl_gen_null(g) != yajl_gen_status_ok) + return -1; + break; + + default: + return -1; + } + + return 0; +} + +char *virJSONValueToString(virJSONValuePtr object) +{ + yajl_gen_config conf = { 0, " " }; /* Turns off pretty printing since QEMU can't cope */ + yajl_gen g; + const unsigned char *str; + char *ret = NULL; + unsigned int len; + + VIR_DEBUG("object=%p", object); + + g = yajl_gen_alloc(&conf, NULL); + + if (virJSONValueToStringOne(object, g) < 0) { + virReportOOMError(NULL); + goto cleanup; + } + + if (yajl_gen_get_buf(g, &str, &len) != yajl_gen_status_ok) { + virReportOOMError(NULL); + goto cleanup; + } + + if (!(ret = strdup((const char *)str))) + virReportOOMError(NULL); + +cleanup: + yajl_gen_free(g); + + VIR_DEBUG("result=%s", NULLSTR(ret)); + + return ret; +} + + +#else +virJSONValuePtr virJSONValueFromString(const char *jsonstring ATTRIBUTE_UNUSED) +{ + ReprotError(NULL, VIR_ERR_INTERNAL_ERROR, "%s", + _("No JSON parser implementation is available")); + return NULL; +} +char *virJSONValueToString(virJSONValuePtr object) +{ + ReprotError(NULL, VIR_ERR_INTERNAL_ERROR, "%s", + _("No JSON parser implementation is available")); + return NULL; +} +#endif diff --git a/src/util/json.h b/src/util/json.h new file mode 100644 index 0000000..bab93cf --- /dev/null +++ b/src/util/json.h @@ -0,0 +1,131 @@ +/* + * json.h: JSON object parsing/formatting + * + * Copyright (C) 2008 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#ifndef __VIR_JSON_H_ +#define __VIR_JSON_H_ + +#include "internal.h" + + +enum { + VIR_JSON_TYPE_OBJECT, + VIR_JSON_TYPE_ARRAY, + VIR_JSON_TYPE_STRING, + VIR_JSON_TYPE_NUMBER, + VIR_JSON_TYPE_BOOLEAN, + VIR_JSON_TYPE_NULL, +}; + +typedef struct _virJSONValue virJSONValue; +typedef virJSONValue *virJSONValuePtr; + +typedef struct _virJSONObject virJSONObject; +typedef virJSONObject *virJSONObjectPtr; + +typedef struct _virJSONObjectPair virJSONObjectPair; +typedef virJSONObjectPair *virJSONObjectPairPtr; + +typedef struct _virJSONArray virJSONArray; +typedef virJSONArray *virJSONArrayPtr; + + +struct _virJSONObjectPair { + char *key; + virJSONValuePtr value; +}; + +struct _virJSONObject { + unsigned int npairs; + virJSONObjectPairPtr pairs; +}; + +struct _virJSONArray { + unsigned int nvalues; + virJSONValuePtr *values; +}; + +struct _virJSONValue { + int type; + + union { + virJSONObject object; + virJSONArray array; + char *string; + char *number; /* int/float/etc format is context defined so we can't parse it here :-( */ + int boolean; + } data; +}; + +void virJSONValueFree(virJSONValuePtr value); + +virJSONValuePtr virJSONValueNewString(const char *data); +virJSONValuePtr virJSONValueNewStringLen(const char *data, size_t length); +virJSONValuePtr virJSONValueNewNumberInt(int data); +virJSONValuePtr virJSONValueNewNumberUint(unsigned int data); +virJSONValuePtr virJSONValueNewNumberLong(long long data); +virJSONValuePtr virJSONValueNewNumberUlong(unsigned long long data); +virJSONValuePtr virJSONValueNewNumberDouble(double data); +virJSONValuePtr virJSONValueNewBoolean(int boolean); +virJSONValuePtr virJSONValueNewNull(void); +virJSONValuePtr virJSONValueNewArray(void); +virJSONValuePtr virJSONValueNewObject(void); + +int virJSONValueObjectAppend(virJSONValuePtr object, const char *key, virJSONValuePtr value); +int virJSONValueArrayAppend(virJSONValuePtr object, virJSONValuePtr value); + +int virJSONValueObjectHasKey(virJSONValuePtr object, const char *key); +virJSONValuePtr virJSONValueObjectGet(virJSONValuePtr object, const char *key); + +int virJSONValueArraySize(virJSONValuePtr object); +virJSONValuePtr virJSONValueArrayGet(virJSONValuePtr object, unsigned int element); + +char *virJSONValueGetString(virJSONValuePtr object); +int virJSONValueGetNumberInt(virJSONValuePtr object, int *value); +int virJSONValueGetNumberUint(virJSONValuePtr object, unsigned int *value); +int virJSONValueGetNumberLong(virJSONValuePtr object, long long *value); +int virJSONValueGetNumberUlong(virJSONValuePtr object, unsigned long long *value); +int virJSONValueGetNumberDouble(virJSONValuePtr object, double *value); +int virJSONValueGetBoolean(virJSONValuePtr object); +int virJSONValueIsNull(virJSONValuePtr object); + +char *virJSONValueObjectGetString(virJSONValuePtr object, const char *key); +int virJSONValueObjectGetNumberInt(virJSONValuePtr object, const char *key, int *value); +int virJSONValueObjectGetNumberUint(virJSONValuePtr object, const char *key, unsigned int *value); +int virJSONValueObjectGetNumberLong(virJSONValuePtr object, const char *key, long long *value); +int virJSONValueObjectGetNumberUlong(virJSONValuePtr object, const char *key, unsigned long long *value); +int virJSONValueObjectGetNumberDouble(virJSONValuePtr object, const char *key, double *value); +int virJSONValueObjectGetBoolean(virJSONValuePtr object, const char *key); +int virJSONValueObjectIsNull(virJSONValuePtr object, const char *key); + +int virJSONValueObjectAppendString(virJSONValuePtr object, const char *key, const char *value); +int virJSONValueObjectAppendNumberInt(virJSONValuePtr object, const char *key, int number); +int virJSONValueObjectAppendNumberUint(virJSONValuePtr object, const char *key, unsigned int number); +int virJSONValueObjectAppendNumberLong(virJSONValuePtr object, const char *key, long long number); +int virJSONValueObjectAppendNumberUlong(virJSONValuePtr object, const char *key, unsigned long long number); +int virJSONValueObjectAppendNumberDouble(virJSONValuePtr object, const char *key, double number); +int virJSONValueObjectAppendBoolean(virJSONValuePtr object, const char *key, int boolean); +int virJSONValueObjectAppendNull(virJSONValuePtr object, const char *key); + +virJSONValuePtr virJSONValueFromString(const char *jsonstring); +char *virJSONValueToString(virJSONValuePtr object); + +#endif /* __VIR_JSON_H_ */ -- 1.6.5.2

2009/11/26 Daniel P. Berrange <berrange@redhat.com>:
This introduces simple API for handling JSON data. There is an internal data structure 'virJSONValuePtr' which stores a arbitrary nested JSON value (number, string, array, object, nul, etc). There are APIs for constructing/querying objects and APIs for parsing/formatting string formatted JSON data.
This uses the YAJL library for parsing/formatting from
* src/util/json.h, src/util/json.c: Data structures and APIs for representing JSON data, and parsing/formatting it * configure.in: Add check for yajl library * libvirt.spec.in: Add build requires for yajl * src/Makefile.am: Add json.c/h * src/libvirt_private.syms: Export JSON symbols to drivers
Tests for the JSON parsing/formating callbacks would be a nice bonus. ACK. Matthias

On Thu, Nov 26, 2009 at 06:27:19PM +0000, Daniel P. Berrange wrote:
This introduces simple API for handling JSON data. There is an internal data structure 'virJSONValuePtr' which stores a arbitrary nested JSON value (number, string, array, object, nul, etc). There are APIs for constructing/querying objects and APIs for parsing/formatting string formatted JSON data.
This uses the YAJL library for parsing/formatting from
* src/util/json.h, src/util/json.c: Data structures and APIs for representing JSON data, and parsing/formatting it * configure.in: Add check for yajl library * libvirt.spec.in: Add build requires for yajl * src/Makefile.am: Add json.c/h * src/libvirt_private.syms: Export JSON symbols to drivers
hum ... that becomes an external library, one more dependancy on the other hand we don't have to track bug fixes.... how widely used is yajl ?
--- configure.in | 55 +++ libvirt.spec.in | 14 + po/POTFILES.in | 1 + src/Makefile.am | 8 +- src/libvirt_private.syms | 47 ++ src/util/json.c | 1043 ++++++++++++++++++++++++++++++++++++++++++++++ src/util/json.h | 131 ++++++
I see that we are encapsulating it and exposing a completely distinct API, that's fine then, we can switch or embbed our own if needed later.
7 files changed, 1296 insertions(+), 3 deletions(-) create mode 100644 src/util/json.c create mode 100644 src/util/json.h
diff --git a/configure.in b/configure.in index f735bba..41f50fc 100644 --- a/configure.in +++ b/configure.in @@ -645,6 +645,56 @@ AC_SUBST([SASL_CFLAGS]) AC_SUBST([SASL_LIBS])
+dnl YAJL JSON library http://lloyd.github.com/yajl/ +AC_ARG_WITH([yajl], + [ --with-yajl use YAJL for JSON parsing/formatting], + [], + [with_yajl=check]) + +YAJL_CFLAGS= +YAJL_LIBS= +if test "x$with_yajl" != "xno"; then + if test "x$with_yajl" != "xyes" -a "x$with_yajl" != "xcheck"; then + YAJL_CFLAGS="-I$with_yajl/include" + YAJL_LIBS="-L$with_yajl/lib" + fi + fail=0 + old_cppflags="$CPPFLAGS" + old_ldflags="$LDFLAGS" + CPPFLAGS="$CPPFLAGS $YAJL_CFLAGS" + LDFLAGS="$LDFLAGS $YAJL_LIBS" + AC_CHECK_HEADER([yajl/yajl_common.h],[],[ + if test "x$with_yajl" != "xcheck" ; then + with_yajl=no + else + fail=1 + fi]) + if test "x$with_yajl" != "xno" ; then + AC_CHECK_LIB([yajl], [yajl_parse],[ + YAJL_LIBS="$YAJL_LIBS -lyajl" + with_yajl=yes + ],[ + if test "x$with_yajl" = "xcheck" ; then + with_yajl=no + else + fail=1 + fi + ]) + fi + test $fail = 1 && + AC_MSG_ERROR([You must install the YAJL development package in order to compile libvirt]) + CPPFLAGS="$old_cppflags" + LDFLAGS="$old_ldflags" + if test "x$with_yajl" = "xyes" ; then + AC_DEFINE_UNQUOTED([HAVE_YAJL], 1, + [whether YAJL is available for JSON parsing/formatting]) + fi +fi +AM_CONDITIONAL([HAVE_YAJL], [test "x$with_yajl" = "xyes"]) +AC_SUBST([YAJL_CFLAGS]) +AC_SUBST([YAJL_LIBS]) + + dnl PolicyKit library POLKIT_CFLAGS= POLKIT_LIBS= @@ -1859,6 +1909,11 @@ AC_MSG_NOTICE([ sasl: $SASL_CFLAGS $SASL_LIBS]) else AC_MSG_NOTICE([ sasl: no]) fi +if test "$with_yajl" != "no" ; then +AC_MSG_NOTICE([ yajl: $YAJL_CFLAGS $YAJL_LIBS]) +else +AC_MSG_NOTICE([ yajl: no]) +fi if test "$with_avahi" = "yes" ; then AC_MSG_NOTICE([ avahi: $AVAHI_CFLAGS $AVAHI_LIBS]) else diff --git a/libvirt.spec.in b/libvirt.spec.in index dba14df..408ad05 100644 --- a/libvirt.spec.in +++ b/libvirt.spec.in @@ -60,6 +60,7 @@ %define with_netcf 0%{!?_without_netcf:0} %define with_udev 0%{!?_without_udev:0} %define with_hal 0%{!?_without_hal:0} +%define with_yajl 0%{!?_without_yajl:0}
# Non-server/HV driver defaults which are always enabled %define with_python 0%{!?_without_python:1} @@ -141,6 +142,11 @@ %define with_hal 0%{!?_without_hal:%{server_drivers}} %endif
+# Enable yajl library for JSON mode with QEMU +%if 0%{?fedora} >= 13 || 0%{?rhel} >= 6 +%define with_yajl 0%{!?_without_yajl:%{server_drivers}} +%endif +
yajl is not in F12, did someone add it to rawhide already ?
# Force QEMU to run as non-root %if 0%{?fedora} >= 12 || 0%{?rhel} >= 6 %define qemu_user qemu @@ -257,6 +263,9 @@ BuildRequires: hal-devel BuildRequires: libudev-devel >= 145 BuildRequires: libpciaccess-devel >= 0.10.9 %endif +%if %{with_yajl} +BuildRequires: yajl-devel +%endif %if %{with_avahi} BuildRequires: avahi-devel %endif @@ -495,6 +504,10 @@ of recent versions of Linux (and other OSes). %define _without_udev --without-udev %endif
+%if ! %{with_yajl} +%define _without_yajl --without-yajl +%endif + %configure %{?_without_xen} \ %{?_without_qemu} \ %{?_without_openvz} \ @@ -522,6 +535,7 @@ of recent versions of Linux (and other OSes). %{?_without_selinux} \ %{?_without_hal} \ %{?_without_udev} \ + %{?_without_yajl} \ --with-qemu-user=%{qemu_user} \ --with-qemu-group=%{qemu_group} \ --with-init-script=redhat \ diff --git a/po/POTFILES.in b/po/POTFILES.in index 116aa87..9864259 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -50,6 +50,7 @@ src/uml/uml_driver.c src/util/bridge.c src/util/conf.c src/util/iptables.c +src/util/json.c src/util/logging.c src/util/pci.c src/util/processinfo.c diff --git a/src/Makefile.am b/src/Makefile.am index 9a3c9c8..b0775a8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -52,6 +52,7 @@ UTIL_SOURCES = \ util/hash.c util/hash.h \ util/iptables.c util/iptables.h \ util/ebtables.c util/ebtables.h \ + util/json.c util/json.h \ util/logging.c util/logging.h \ util/memory.c util/memory.h \ util/pci.c util/pci.h \ @@ -283,8 +284,8 @@ noinst_LTLIBRARIES = libvirt_util.la libvirt_la_LIBADD = libvirt_util.la libvirt_util_la_SOURCES = \ $(UTIL_SOURCES) -libvirt_util_la_CFLAGS = $(CAPNG_CFLAGS) -libvirt_util_la_LDFLAGS = $(CAPNG_LIBS) +libvirt_util_la_CFLAGS = $(CAPNG_CFLAGS) $(YAJL_CFLAGS) +libvirt_util_la_LDFLAGS = $(CAPNG_LIBS) $(YAJL_LIBS)
noinst_LTLIBRARIES += libvirt_conf.la @@ -828,12 +829,13 @@ libvirt_lxc_SOURCES = \ $(NODE_INFO_SOURCES) \ $(ENCRYPTION_CONF_SOURCES) \ $(DOMAIN_CONF_SOURCES) -libvirt_lxc_LDFLAGS = $(WARN_CFLAGS) $(COVERAGE_LDCFLAGS) $(CAPNG_LIBS) +libvirt_lxc_LDFLAGS = $(WARN_CFLAGS) $(COVERAGE_LDCFLAGS) $(CAPNG_LIBS) $(YAJL_LIBS) libvirt_lxc_LDADD = $(LIBXML_LIBS) $(NUMACTL_LIBS) ../gnulib/lib/libgnu.la libvirt_lxc_CFLAGS = \ $(LIBPARTED_CFLAGS) \ $(NUMACTL_CFLAGS) \ $(CAPNG_CFLAGS) \ + $(YAJL_CFLAGS) \ -I@top_srcdir@/src/conf endif endif diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index e880c2e..ee7f3ed 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -269,6 +269,53 @@ virRegisterDeviceMonitor; virRegisterSecretDriver;
+# json.h +virJSONValueFree; +virJSONValueNewString; +virJSONValueNewStringLen; +virJSONValueNewNumberInt; +virJSONValueNewNumberUint; +virJSONValueNewNumberLong; +virJSONValueNewNumberUlong; +virJSONValueNewNumberDouble; +virJSONValueNewBoolean; +virJSONValueNewNull; +virJSONValueNewArray; +virJSONValueNewObject; +virJSONValueObjectAppend; +virJSONValueObjectAppendString; +virJSONValueObjectAppendNumberInt; +virJSONValueObjectAppendNumberUint; +virJSONValueObjectAppendNumberLong; +virJSONValueObjectAppendNumberUlong; +virJSONValueObjectAppendNumberDouble; +virJSONValueObjectAppendBoolean; +virJSONValueObjectAppendNull; +virJSONValueArrayAppend; +virJSONValueObjectHasKey; +virJSONValueObjectGet; +virJSONValueArraySize; +virJSONValueArrayGet; +virJSONValueGetString; +virJSONValueGetNumberInt; +virJSONValueGetNumberUint; +virJSONValueGetNumberLong; +virJSONValueGetNumberUlong; +virJSONValueGetNumberDouble; +virJSONValueGetBoolean; +virJSONValueIsNull; +virJSONValueObjectGetString; +virJSONValueObjectGetNumberInt; +virJSONValueObjectGetNumberUint; +virJSONValueObjectGetNumberLong; +virJSONValueObjectGetNumberUlong; +virJSONValueObjectGetNumberDouble; +virJSONValueObjectGetBoolean; +virJSONValueObjectIsNull; +virJSONValueFromString; +virJSONValueToString; + + # logging.h virLogMessage; virLogGetNbFilters; diff --git a/src/util/json.c b/src/util/json.c new file mode 100644 index 0000000..f359b95 --- /dev/null +++ b/src/util/json.c @@ -0,0 +1,1043 @@ +/* + * json.h: JSON object parsing/formatting
json.c
+ * Copyright (C) 2008 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#include <config.h> + +#include "json.h" +#include "memory.h" +#include "virterror_internal.h" +#include "logging.h" +#include "util.h" + +#include <yajl/yajl_gen.h> +#include <yajl/yajl_parse.h> + +/* XXX fixme */ +#define VIR_FROM_THIS VIR_FROM_NONE
hum, I would defined a new domain after all we have one for XML parsing, but not urgent.
+#define ReportError(conn, code, fmt...) \ + virReportErrorHelper(conn, VIR_FROM_NONE, code, __FILE__, \ + __FUNCTION__, __LINE__, fmt) + + [...] +static virJSONValuePtr virJSONValueNewNumber(const char *data) +{ + virJSONValuePtr val; + + if (VIR_ALLOC(val) < 0) + return NULL; + + val->type = VIR_JSON_TYPE_NUMBER; + if (!(val->data.number = strdup(data))) { + VIR_FREE(val); + return NULL; + }
so we keep numbers as their string value ... surprizing but maybe that's the most flexible for our use.
+ return val; +} [...] + +#if HAVE_YAJL +static int virJSONParserInsertValue(virJSONParserPtr parser, + virJSONValuePtr value)
okay so that's the YAJL specific part [...]
+static const yajl_callbacks parserCallbacks = { + virJSONParserHandleNull, + virJSONParserHandleBoolean, + NULL, + NULL, + virJSONParserHandleNumber, + virJSONParserHandleString, + virJSONParserHandleStartMap, + virJSONParserHandleMapKey, + virJSONParserHandleEndMap, + virJSONParserHandleStartArray, + virJSONParserHandleEndArray +};
and a driver block for it
+ +/* XXX add an incremental streaming parser - yajl trivially supports it */
what would be the point ? I doubt there is a memory problem, it's more the incremental aspect I suppose. [...]
+ +#else +virJSONValuePtr virJSONValueFromString(const char *jsonstring ATTRIBUTE_UNUSED) +{ + ReprotError(NULL, VIR_ERR_INTERNAL_ERROR, "%s", + _("No JSON parser implementation is available")); + return NULL; +} +char *virJSONValueToString(virJSONValuePtr object) +{ + ReprotError(NULL, VIR_ERR_INTERNAL_ERROR, "%s", + _("No JSON parser implementation is available")); + return NULL; +}
and the fallback ACK, it' weird there ain't any JSON C API yet in F12, there is one for nearly all high level language but noone seems to have imposed a good C one. so now we are betting on yajl, maybe another one will prevail but since we have a good decoupling, fine by me, ACK Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

On Thu, Dec 03, 2009 at 01:11:19PM +0100, Daniel Veillard wrote:
On Thu, Nov 26, 2009 at 06:27:19PM +0000, Daniel P. Berrange wrote:
This uses the YAJL library for parsing/formatting from
* src/util/json.h, src/util/json.c: Data structures and APIs for representing JSON data, and parsing/formatting it * configure.in: Add check for yajl library * libvirt.spec.in: Add build requires for yajl * src/Makefile.am: Add json.c/h * src/libvirt_private.syms: Export JSON symbols to drivers
hum ... that becomes an external library, one more dependancy on the other hand we don't have to track bug fixes.... how widely used is yajl ?
It is very unclear - JSON doesn't really appear to be used much from C at all - it is mostly something that python/perl/javascript people use. There are six parsers listed on http://json.org/ and I ended up picking yajl because it was a very nicely designed API that integrated well with what I wanted.
diff --git a/libvirt.spec.in b/libvirt.spec.in index dba14df..408ad05 100644 --- a/libvirt.spec.in +++ b/libvirt.spec.in @@ -60,6 +60,7 @@ %define with_netcf 0%{!?_without_netcf:0} %define with_udev 0%{!?_without_udev:0} %define with_hal 0%{!?_without_hal:0} +%define with_yajl 0%{!?_without_yajl:0}
# Non-server/HV driver defaults which are always enabled %define with_python 0%{!?_without_python:1} @@ -141,6 +142,11 @@ %define with_hal 0%{!?_without_hal:%{server_drivers}} %endif
+# Enable yajl library for JSON mode with QEMU +%if 0%{?fedora} >= 13 || 0%{?rhel} >= 6 +%define with_yajl 0%{!?_without_yajl:%{server_drivers}} +%endif +
yajl is not in F12, did someone add it to rawhide already ?
No, its not in Fedora at all. If we go for this patch, then I will have to prepare an yajl RPM for Fedora.
+#define ReportError(conn, code, fmt...) \ + virReportErrorHelper(conn, VIR_FROM_NONE, code, __FILE__, \ + __FUNCTION__, __LINE__, fmt) + + [...] +static virJSONValuePtr virJSONValueNewNumber(const char *data) +{ + virJSONValuePtr val; + + if (VIR_ALLOC(val) < 0) + return NULL; + + val->type = VIR_JSON_TYPE_NUMBER; + if (!(val->data.number = strdup(data))) { + VIR_FREE(val); + return NULL; + }
so we keep numbers as their string value ... surprizing but maybe that's the most flexible for our use.
The reason for this is that at a low level JSON does not define whether a number is an int, floating point, etc, etc. Thus when we parse numbers we just get a string. Only the application (in this case libvirt's QEMU driver) knows what number format is applicable for a particular context. So when the libvirt QEMU monitor code wants to set/get a number, it will use one of the wrapper methods like virJSONValueNewNumberInt(int value) virJSONValueNewNumberLong(long long value) virJSONValueNewNumberDouble(double value)
+ +/* XXX add an incremental streaming parser - yajl trivially supports it */
what would be the point ? I doubt there is a memory problem, it's more the incremental aspect I suppose.
It is all about how we decent the message boundaries. The current QEMU code will always terminate a JSON message with a '\r\n', so we can easily detect boundaries without needing to parse the data. If QEMU developers suddenly change their mind and remove the '\r\n', we would need an incremental streaming parser, so we can detect where the message boundaries are. Hopefully they won't change their minds :-)
+ +#else +virJSONValuePtr virJSONValueFromString(const char *jsonstring ATTRIBUTE_UNUSED) +{ + ReprotError(NULL, VIR_ERR_INTERNAL_ERROR, "%s", + _("No JSON parser implementation is available")); + return NULL; +} +char *virJSONValueToString(virJSONValuePtr object) +{ + ReprotError(NULL, VIR_ERR_INTERNAL_ERROR, "%s", + _("No JSON parser implementation is available")); + return NULL; +}
and the fallback
ACK, it' weird there ain't any JSON C API yet in F12, there is one for nearly all high level language but noone seems to have imposed a good C one. so now we are betting on yajl, maybe another one will prevail but since we have a good decoupling, fine by me,
The only other option I see is to actually include the YAJL parsing/formatting code in the libvirt source tree, but that is rather evil in itself :-) Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Thu, Dec 03, 2009 at 12:41:24PM +0000, Daniel P. Berrange wrote:
On Thu, Dec 03, 2009 at 01:11:19PM +0100, Daniel Veillard wrote:
On Thu, Nov 26, 2009 at 06:27:19PM +0000, Daniel P. Berrange wrote:
This uses the YAJL library for parsing/formatting from
* src/util/json.h, src/util/json.c: Data structures and APIs for representing JSON data, and parsing/formatting it * configure.in: Add check for yajl library * libvirt.spec.in: Add build requires for yajl * src/Makefile.am: Add json.c/h * src/libvirt_private.syms: Export JSON symbols to drivers
hum ... that becomes an external library, one more dependancy on the other hand we don't have to track bug fixes.... how widely used is yajl ?
It is very unclear - JSON doesn't really appear to be used much from C at all - it is mostly something that python/perl/javascript people use. There are six parsers listed on http://json.org/ and I ended up picking yajl because it was a very nicely designed API that integrated well with what I wanted.
http://lloyd.github.com/yajl/ The licence is BSD ... but with what looks like an advertizing clause see point 2. below: ------------------------------------------------------------------------ Copyright 2007-2009, Lloyd Hilaiel. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of Lloyd Hilaiel nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ------------------------------------------------------------------------
yajl is not in F12, did someone add it to rawhide already ?
No, its not in Fedora at all. If we go for this patch, then I will have to prepare an yajl RPM for Fedora.
humpf ...
+#define ReportError(conn, code, fmt...) \ + virReportErrorHelper(conn, VIR_FROM_NONE, code, __FILE__, \ + __FUNCTION__, __LINE__, fmt) + + [...] +static virJSONValuePtr virJSONValueNewNumber(const char *data) +{ + virJSONValuePtr val; + + if (VIR_ALLOC(val) < 0) + return NULL; + + val->type = VIR_JSON_TYPE_NUMBER; + if (!(val->data.number = strdup(data))) { + VIR_FREE(val); + return NULL; + }
so we keep numbers as their string value ... surprizing but maybe that's the most flexible for our use.
The reason for this is that at a low level JSON does not define whether a number is an int, floating point, etc, etc. Thus when we parse numbers we just get a string.
okay
Only the application (in this case libvirt's QEMU driver) knows what number format is applicable for a particular context. So when the libvirt QEMU monitor code wants to set/get a number, it will use one of the wrapper methods like
virJSONValueNewNumberInt(int value) virJSONValueNewNumberLong(long long value) virJSONValueNewNumberDouble(double value)
okay ... I start wondering if I should not write yet another parser, after all it's trivial compared to XML or HTML from libxml2 ...
+ +/* XXX add an incremental streaming parser - yajl trivially supports it */
what would be the point ? I doubt there is a memory problem, it's more the incremental aspect I suppose.
It is all about how we decent the message boundaries. The current QEMU code will always terminate a JSON message with a '\r\n', so we can easily detect boundaries without needing to parse the data.
If QEMU developers suddenly change their mind and remove the '\r\n', we would need an incremental streaming parser, so we can detect where the message boundaries are.
Ah, I see, this is tricking so many people using XML, as the parser can't tell when the document is finished (it accepts unlimited extra blanks/comment/PI after the main emelemt)
Hopefully they won't change their minds :-)
:-)
ACK, it' weird there ain't any JSON C API yet in F12, there is one for nearly all high level language but noone seems to have imposed a good C one. so now we are betting on yajl, maybe another one will prevail but since we have a good decoupling, fine by me,
The only other option I see is to actually include the YAJL parsing/formatting code in the libvirt source tree, but that is rather evil in itself :-)
yes if it ships as a standalone library then we should ot do this. The alternative would be to write another one, use it only internally and possibly separate it later or stuff it in libxml2 or ... Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

On Thu, Dec 03, 2009 at 01:50:59PM +0100, Daniel Veillard wrote:
The licence is BSD ... but with what looks like an advertizing clause see point 2. below:
------------------------------------------------------------------------ Copyright 2007-2009, Lloyd Hilaiel.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of Lloyd Hilaiel nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ------------------------------------------------------------------------
I don't think that is the BSD+Advertizing - that would have a fourth clause explicitly mentioning Advertizing. This looks just like a standard 3-clause BSD to me, when comparing to: https://fedoraproject.org/wiki/Licensing:BSD Regards, Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Thu, Dec 03, 2009 at 12:56:32PM +0000, Daniel P. Berrange wrote:
On Thu, Dec 03, 2009 at 01:50:59PM +0100, Daniel Veillard wrote:
The licence is BSD ... but with what looks like an advertizing clause see point 2. below:
------------------------------------------------------------------------ Copyright 2007-2009, Lloyd Hilaiel.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of Lloyd Hilaiel nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ------------------------------------------------------------------------
I don't think that is the BSD+Advertizing - that would have a fourth clause explicitly mentioning Advertizing. This looks just like a standard 3-clause BSD to me, when comparing to:
Okay, rereading it, yes this looks fine. Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

On 12/03/2009 01:11 PM, Daniel Veillard wrote:
hum ... that becomes an external library, one more dependancy on the other hand we don't have to track bug fixes....
What about using submodules + static linking of libyajl? That would fix the "dependency" part and make bug tracking a bit harder. Paolo

On Thu, Dec 03, 2009 at 03:41:08PM +0100, Paolo Bonzini wrote:
On 12/03/2009 01:11 PM, Daniel Veillard wrote:
hum ... that becomes an external library, one more dependancy on the other hand we don't have to track bug fixes....
What about using submodules + static linking of libyajl? That would fix the "dependency" part and make bug tracking a bit harder.
That won't fly with Fedora packaging rules which have a strict policy on not allowing use of any bundled 3rd party source. Even if upstream project ships it, Fedora will require that it is packaged & maintained as a separate package. It is not hard to build & package YAJL, since it is a tiny library with no tricky dependancies to satisfy, so I'm going to package it for Fedora. Other distros can have the choice of packaging it separately too, or just including it directly in their libvirt packages as per their own policies. Regards, Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

Add a --system flag to autogen.sh which gets turned into the args --prefix=/usr --sysconfdir=/etc --localstatedir=/var to make it easy to build with settings that match an RPM build * autogen.sh: Add --system flag --- autogen.sh | 12 ++++++++++-- 1 files changed, 10 insertions(+), 2 deletions(-) diff --git a/autogen.sh b/autogen.sh index 415f3ec..1ddc251 100755 --- a/autogen.sh +++ b/autogen.sh @@ -49,9 +49,17 @@ test -f src/libvirt.c || { exit 1 } -if test -z "$*"; then + +EXTRA_ARGS= +if test "x$1" = "x--system"; then + shift + EXTRA_ARGS="--prefix=/usr --sysconfdir=/etc --localstatedir=/var" + echo "Running ./configure with $EXTRA_ARGS $@" +else + if test -z "$*"; then echo "I am going to run ./configure with no arguments - if you wish " echo "to pass any to it, please specify them on the $0 command line." + fi fi # Ensure that whenever we pull in a gnulib update or otherwise change to a @@ -77,7 +85,7 @@ if test x$OBJ_DIR != x; then cd "$OBJ_DIR" fi -$srcdir/configure "$@" && { +$srcdir/configure $EXTRA_ARGS "$@" && { echo echo "Now type 'make' to compile libvirt." } -- 1.6.5.2

2009/11/26 Daniel P. Berrange <berrange@redhat.com>:
Add a --system flag to autogen.sh which gets turned into the args --prefix=/usr --sysconfdir=/etc --localstatedir=/var to make it easy to build with settings that match an RPM build
* autogen.sh: Add --system flag --- autogen.sh | 12 ++++++++++-- 1 files changed, 10 insertions(+), 2 deletions(-)
ACK. Matthias

On Thu, Nov 26, 2009 at 06:27:20PM +0000, Daniel P. Berrange wrote:
Add a --system flag to autogen.sh which gets turned into the args --prefix=/usr --sysconfdir=/etc --localstatedir=/var to make it easy to build with settings that match an RPM build
* autogen.sh: Add --system flag
I usually do --prefix=/usr , and IIRC I hacked that to turn sysconfdir into /etc in that case, but fine it's even better and more complete ! Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

If QEMU shuts down while we're in the middle of processing a monitor command, the monitor will be freed, and upon cleaning up we attempt to do qemuMonitorUnlock(priv->mon) when priv->mon is NULL. To address this we introduce proper reference counting into the qemuMonitorPtr object, and hold an extra reference whenever executing a command. * src/qemu/qemu_driver.c: Hold a reference on the monitor while executing commands, and only NULL-ify the priv->mon field when the last reference is released * src/qemu/qemu_monitor.h, src/qemu/qemu_monitor.c: Add reference counting to handle safe deletion of monitor objects --- src/qemu/qemu_driver.c | 13 ++++++-- src/qemu/qemu_monitor.c | 81 +++++++++++++++++++++++++++++------------------ src/qemu/qemu_monitor.h | 5 ++- 3 files changed, 64 insertions(+), 35 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index c9b5ac2..3befe3d 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -271,6 +271,7 @@ static void qemuDomainObjEnterMonitor(virDomainObjPtr obj) qemuDomainObjPrivatePtr priv = obj->privateData; qemuMonitorLock(priv->mon); + qemuMonitorRef(priv->mon); virDomainObjUnlock(obj); } @@ -281,10 +282,15 @@ static void qemuDomainObjEnterMonitor(virDomainObjPtr obj) */ static void qemuDomainObjExitMonitor(virDomainObjPtr obj) { + int refs; qemuDomainObjPrivatePtr priv = obj->privateData; + refs = qemuMonitorUnref(priv->mon); qemuMonitorUnlock(priv->mon); virDomainObjLock(obj); + + if (refs == 0) + priv->mon = NULL; } @@ -301,6 +307,7 @@ static void qemuDomainObjEnterMonitorWithDriver(struct qemud_driver *driver, vir qemuDomainObjPrivatePtr priv = obj->privateData; qemuMonitorLock(priv->mon); + qemuMonitorRef(priv->mon); virDomainObjUnlock(obj); qemuDriverUnlock(driver); } @@ -315,6 +322,7 @@ static void qemuDomainObjExitMonitorWithDriver(struct qemud_driver *driver, virD { qemuDomainObjPrivatePtr priv = obj->privateData; + qemuMonitorUnref(priv->mon); qemuMonitorUnlock(priv->mon); qemuDriverLock(driver); virDomainObjLock(obj); @@ -2399,10 +2407,9 @@ static void qemudShutdownVMDaemon(virConnectPtr conn, _("Failed to send SIGTERM to %s (%d)"), vm->def->name, vm->pid); - if (priv->mon) { - qemuMonitorClose(priv->mon); + if (priv->mon && + qemuMonitorClose(priv->mon) == 0) priv->mon = NULL; - } if (vm->monitor_chr) { if (vm->monitor_chr->type == VIR_DOMAIN_CHR_TYPE_UNIX) diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index f0ef81b..3829e7a 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -42,6 +42,8 @@ struct _qemuMonitor { virMutex lock; virCond notify; + int refs; + virDomainObjPtr dom; int fd; @@ -67,12 +69,14 @@ struct _qemuMonitor { * the next monitor msg */ int lastErrno; - /* If the monitor callback is currently active */ + /* If the monitor EOF callback is currently active (stops more commands being run) */ unsigned eofcb: 1; - /* If the monitor callback should free the closed monitor */ + /* If the monitor is in process of shutting down */ unsigned closed: 1; + }; + void qemuMonitorLock(qemuMonitorPtr mon) { virMutexLock(&mon->lock); @@ -84,21 +88,33 @@ void qemuMonitorUnlock(qemuMonitorPtr mon) } -static void qemuMonitorFree(qemuMonitorPtr mon, int lockDomain) +static void qemuMonitorFree(qemuMonitorPtr mon) { - VIR_DEBUG("mon=%p, lockDomain=%d", mon, lockDomain); - if (mon->vm) { - if (lockDomain) - virDomainObjLock(mon->vm); - if (!virDomainObjUnref(mon->vm) && lockDomain) - virDomainObjUnlock(mon->vm); - } + VIR_DEBUG("mon=%p", mon); + if (mon->vm) + virDomainObjUnref(mon->vm); if (virCondDestroy(&mon->notify) < 0) {} virMutexDestroy(&mon->lock); VIR_FREE(mon); } +int qemuMonitorRef(qemuMonitorPtr mon) +{ + mon->refs++; + return mon->refs; +} + +int qemuMonitorUnref(qemuMonitorPtr mon) +{ + mon->refs--; + + if (mon->refs == 0) + qemuMonitorFree(mon); + + return mon->refs; +} + static int qemuMonitorOpenUnix(const char *monitor) @@ -346,8 +362,10 @@ static void qemuMonitorIO(int watch, int fd, int events, void *opaque) { qemuMonitorPtr mon = opaque; int quit = 0, failed = 0; + virDomainObjPtr vm; qemuMonitorLock(mon); + qemuMonitorRef(mon); VIR_DEBUG("Monitor %p I/O on watch %d fd %d events %d", mon, watch, fd, events); if (mon->fd != fd || mon->watch != watch) { @@ -409,22 +427,20 @@ qemuMonitorIO(int watch, int fd, int events, void *opaque) { if (failed || quit) { /* Make sure anyone waiting wakes up now */ virCondSignal(&mon->notify); - mon->eofcb = 1; qemuMonitorUnlock(mon); VIR_DEBUG("Triggering EOF callback error? %d", failed); mon->eofCB(mon, mon->vm, failed); qemuMonitorLock(mon); - if (mon->closed) { - qemuMonitorUnlock(mon); - VIR_DEBUG("Delayed free of monitor %p", mon); - qemuMonitorFree(mon, 1); - } else { - qemuMonitorUnlock(mon); - } - } else { - qemuMonitorUnlock(mon); } + + /* Safe 'vm' to an intermediate ptr, since 'mon' may be + * freed after the Unref call */ + vm = mon->vm; + virDomainObjLock(vm); + if (qemuMonitorUnref(mon) > 0) + qemuMonitorUnlock(mon); + virDomainObjUnlock(vm); } @@ -453,6 +469,7 @@ qemuMonitorOpen(virDomainObjPtr vm, return NULL; } mon->fd = -1; + mon->refs = 1; mon->vm = vm; mon->eofCB = eofCB; qemuMonitorLock(mon); @@ -512,10 +529,14 @@ cleanup: } -void qemuMonitorClose(qemuMonitorPtr mon) +int qemuMonitorClose(qemuMonitorPtr mon) { + int refs; + if (!mon) - return; + return 0; + + VIR_DEBUG("mon=%p", mon); qemuMonitorLock(mon); if (!mon->closed) { @@ -523,18 +544,17 @@ void qemuMonitorClose(qemuMonitorPtr mon) virEventRemoveHandle(mon->watch); if (mon->fd != -1) close(mon->fd); - /* NB: don't reset fd / watch fields, since active - * callback may still want them */ + /* NB: ordinarily one might immediately set mon->watch to -1 + * and mon->fd to -1, but there may be a callback active + * that is still relying on these fields being valid. So + * we merely close them, but not clear their values and + * use this explicit 'closed' flag to track this state */ mon->closed = 1; } - if (mon->eofcb) { - VIR_DEBUG("Mark monitor to be deleted %p", mon); + if ((refs = qemuMonitorUnref(mon)) > 0) qemuMonitorUnlock(mon); - } else { - VIR_DEBUG("Delete monitor now %p", mon); - qemuMonitorFree(mon, 0); - } + return refs; } @@ -552,7 +572,6 @@ int qemuMonitorSend(qemuMonitorPtr mon, if (mon->eofcb) { msg->lastErrno = EIO; - qemuMonitorUnlock(mon); return -1; } diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 71688cb..27b43b7 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -78,11 +78,14 @@ typedef int (*qemuMonitorDiskSecretLookup)(qemuMonitorPtr mon, qemuMonitorPtr qemuMonitorOpen(virDomainObjPtr vm, qemuMonitorEOFNotify eofCB); -void qemuMonitorClose(qemuMonitorPtr mon); +int qemuMonitorClose(qemuMonitorPtr mon); void qemuMonitorLock(qemuMonitorPtr mon); void qemuMonitorUnlock(qemuMonitorPtr mon); +int qemuMonitorRef(qemuMonitorPtr mon); +int qemuMonitorUnref(qemuMonitorPtr mon); + void qemuMonitorRegisterDiskSecretLookup(qemuMonitorPtr mon, qemuMonitorDiskSecretLookup secretCB); -- 1.6.5.2

2009/11/26 Daniel P. Berrange <berrange@redhat.com>:
If QEMU shuts down while we're in the middle of processing a monitor command, the monitor will be freed, and upon cleaning up we attempt to do qemuMonitorUnlock(priv->mon) when priv->mon is NULL.
To address this we introduce proper reference counting into the qemuMonitorPtr object, and hold an extra reference whenever executing a command.
* src/qemu/qemu_driver.c: Hold a reference on the monitor while executing commands, and only NULL-ify the priv->mon field when the last reference is released * src/qemu/qemu_monitor.h, src/qemu/qemu_monitor.c: Add reference counting to handle safe deletion of monitor objects
The locking pattern below results in destroying a locked mutex. It this intended? qemuMonitorLock(mon); [...] if (qemuMonitorUnref(mon) > 0) qemuMonitorUnlock(mon); Well, this patch makes the TCK deadlock for me, seems to be a lock ordering issue combined with a race condition; it doesn't happen every run. I don't understand all details of the locking and refcounting scheme of the QEMU monitor yet, it's quite complex and gets even more complex. I attached some GDB and Valgrind traces. Debugging is hindered by libvirtd blocking on poll() in virEventRunOnce() often and I haven't found out why yet. Matthias

On Thu, Dec 03, 2009 at 12:47:02AM +0100, Matthias Bolte wrote:
2009/11/26 Daniel P. Berrange <berrange@redhat.com>:
If QEMU shuts down while we're in the middle of processing a monitor command, the monitor will be freed, and upon cleaning up we attempt to do qemuMonitorUnlock(priv->mon) when priv->mon is NULL.
To address this we introduce proper reference counting into the qemuMonitorPtr object, and hold an extra reference whenever executing a command.
* src/qemu/qemu_driver.c: Hold a reference on the monitor while executing commands, and only NULL-ify the priv->mon field when the last reference is released * src/qemu/qemu_monitor.h, src/qemu/qemu_monitor.c: Add reference counting to handle safe deletion of monitor objects
The locking pattern below results in destroying a locked mutex. It this intended?
No, that's a bug, the qemuMonitorUnref() call itself should have called unlock if the ref count hit 0, before destroying it.
qemuMonitorLock(mon); [...] if (qemuMonitorUnref(mon) > 0) qemuMonitorUnlock(mon);
Well, this patch makes the TCK deadlock for me, seems to be a lock ordering issue combined with a race condition; it doesn't happen every run. I don't understand all details of the locking and refcounting scheme of the QEMU monitor yet, it's quite complex and gets even more complex.
Yes, that problem shown by valgrind will indeed deadlock. I'll fix this. The domain object lock must never be acquired if the thread holds the monitor lock already. We must have strict ordering from top to bottom (driver -> domain object -> qemu monitor) Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Thu, Dec 03, 2009 at 11:12:40AM +0000, Daniel P. Berrange wrote:
On Thu, Dec 03, 2009 at 12:47:02AM +0100, Matthias Bolte wrote:
2009/11/26 Daniel P. Berrange <berrange@redhat.com>:
If QEMU shuts down while we're in the middle of processing a monitor command, the monitor will be freed, and upon cleaning up we attempt to do qemuMonitorUnlock(priv->mon) when priv->mon is NULL.
To address this we introduce proper reference counting into the qemuMonitorPtr object, and hold an extra reference whenever executing a command.
* src/qemu/qemu_driver.c: Hold a reference on the monitor while executing commands, and only NULL-ify the priv->mon field when the last reference is released * src/qemu/qemu_monitor.h, src/qemu/qemu_monitor.c: Add reference counting to handle safe deletion of monitor objects
The locking pattern below results in destroying a locked mutex. It this intended?
No, that's a bug, the qemuMonitorUnref() call itself should have called unlock if the ref count hit 0, before destroying it.
qemuMonitorLock(mon); [...] if (qemuMonitorUnref(mon) > 0) qemuMonitorUnlock(mon);
Well, this patch makes the TCK deadlock for me, seems to be a lock ordering issue combined with a race condition; it doesn't happen every run. I don't understand all details of the locking and refcounting scheme of the QEMU monitor yet, it's quite complex and gets even more complex.
Yes, that problem shown by valgrind will indeed deadlock. I'll fix this. The domain object lock must never be acquired if the thread holds the monitor lock already. We must have strict ordering from top to bottom (driver -> domain object -> qemu monitor)
This is the patch I'm proposing instead. It removes the ref count adjust of virDomainObjPtr from qemuMonitor, since that requires that you have the bad lock ordering. Instead that extra ref count is added/removed by the qemu driver code. This means the qemuMonitor never has to touch any locks on virDomainObjPtr which complies with our strict top->bottom ordering requirement. commit c2b32f964b0eed861dea11e5037993e3a3c3fb3d Author: Daniel P. Berrange <berrange@redhat.com> Date: Thu Nov 26 13:29:29 2009 +0000 Fix crash when deleting monitor while a command is in progress If QEMU shuts down while we're in the middle of processing a monitor command, the monitor will be freed, and upon cleaning up we attempt to do qemuMonitorUnlock(priv->mon) when priv->mon is NULL. To address this we introduce proper reference counting into the qemuMonitorPtr object, and hold an extra reference whenever executing a command. * src/qemu/qemu_driver.c: Hold a reference on the monitor while executing commands, and only NULL-ify the priv->mon field when the last reference is released * src/qemu/qemu_monitor.h, src/qemu/qemu_monitor.c: Add reference counting to handle safe deletion of monitor objects diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 43d20ea..fd47dc0 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -272,6 +272,7 @@ static void qemuDomainObjEnterMonitor(virDomainObjPtr obj) qemuDomainObjPrivatePtr priv = obj->privateData; qemuMonitorLock(priv->mon); + qemuMonitorRef(priv->mon); virDomainObjUnlock(obj); } @@ -283,9 +284,16 @@ static void qemuDomainObjEnterMonitor(virDomainObjPtr obj) static void qemuDomainObjExitMonitor(virDomainObjPtr obj) { qemuDomainObjPrivatePtr priv = obj->privateData; + int refs; + refs = qemuMonitorUnref(priv->mon); qemuMonitorUnlock(priv->mon); virDomainObjLock(obj); + + if (refs == 0) { + virDomainObjUnref(obj); + priv->mon = NULL; + } } @@ -302,6 +310,7 @@ static void qemuDomainObjEnterMonitorWithDriver(struct qemud_driver *driver, vir qemuDomainObjPrivatePtr priv = obj->privateData; qemuMonitorLock(priv->mon); + qemuMonitorRef(priv->mon); virDomainObjUnlock(obj); qemuDriverUnlock(driver); } @@ -315,10 +324,17 @@ static void qemuDomainObjEnterMonitorWithDriver(struct qemud_driver *driver, vir static void qemuDomainObjExitMonitorWithDriver(struct qemud_driver *driver, virDomainObjPtr obj) { qemuDomainObjPrivatePtr priv = obj->privateData; + int refs; + refs = qemuMonitorUnref(priv->mon); qemuMonitorUnlock(priv->mon); qemuDriverLock(driver); virDomainObjLock(obj); + + if (refs == 0) { + virDomainObjUnref(obj); + priv->mon = NULL; + } } @@ -653,6 +669,10 @@ qemuConnectMonitor(virDomainObjPtr vm) { qemuDomainObjPrivatePtr priv = vm->privateData; + /* Hold an extra reference because we can't allow 'vm' to be + * deleted while the monitor is active */ + virDomainObjRef(vm); + if ((priv->mon = qemuMonitorOpen(vm, qemuHandleMonitorEOF)) == NULL) { VIR_ERROR(_("Failed to connect monitor for %s\n"), vm->def->name); return -1; @@ -2400,8 +2420,9 @@ static void qemudShutdownVMDaemon(virConnectPtr conn, _("Failed to send SIGTERM to %s (%d)"), vm->def->name, vm->pid); - if (priv->mon) { - qemuMonitorClose(priv->mon); + if (priv->mon && + qemuMonitorClose(priv->mon) == 0) { + virDomainObjUnref(vm); priv->mon = NULL; } diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index f0ef81b..a2e10bc 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -42,7 +42,7 @@ struct _qemuMonitor { virMutex lock; virCond notify; - virDomainObjPtr dom; + int refs; int fd; int watch; @@ -67,12 +67,14 @@ struct _qemuMonitor { * the next monitor msg */ int lastErrno; - /* If the monitor callback is currently active */ + /* If the monitor EOF callback is currently active (stops more commands being run) */ unsigned eofcb: 1; - /* If the monitor callback should free the closed monitor */ + /* If the monitor is in process of shutting down */ unsigned closed: 1; + }; + void qemuMonitorLock(qemuMonitorPtr mon) { virMutexLock(&mon->lock); @@ -84,21 +86,33 @@ void qemuMonitorUnlock(qemuMonitorPtr mon) } -static void qemuMonitorFree(qemuMonitorPtr mon, int lockDomain) +static void qemuMonitorFree(qemuMonitorPtr mon) { - VIR_DEBUG("mon=%p, lockDomain=%d", mon, lockDomain); - if (mon->vm) { - if (lockDomain) - virDomainObjLock(mon->vm); - if (!virDomainObjUnref(mon->vm) && lockDomain) - virDomainObjUnlock(mon->vm); - } + VIR_DEBUG("mon=%p", mon); if (virCondDestroy(&mon->notify) < 0) {} virMutexDestroy(&mon->lock); VIR_FREE(mon); } +int qemuMonitorRef(qemuMonitorPtr mon) +{ + mon->refs++; + return mon->refs; +} + +int qemuMonitorUnref(qemuMonitorPtr mon) +{ + mon->refs--; + + if (mon->refs == 0) { + qemuMonitorUnlock(mon); + qemuMonitorFree(mon); + } + + return mon->refs; +} + static int qemuMonitorOpenUnix(const char *monitor) @@ -348,6 +362,7 @@ qemuMonitorIO(int watch, int fd, int events, void *opaque) { int quit = 0, failed = 0; qemuMonitorLock(mon); + qemuMonitorRef(mon); VIR_DEBUG("Monitor %p I/O on watch %d fd %d events %d", mon, watch, fd, events); if (mon->fd != fd || mon->watch != watch) { @@ -407,23 +422,17 @@ qemuMonitorIO(int watch, int fd, int events, void *opaque) { * but is this safe ? I think it is, because the callback * will try to acquire the virDomainObjPtr mutex next */ if (failed || quit) { + qemuMonitorEOFNotify eofCB = mon->eofCB; + virDomainObjPtr vm = mon->vm; /* Make sure anyone waiting wakes up now */ virCondSignal(&mon->notify); - mon->eofcb = 1; - qemuMonitorUnlock(mon); - VIR_DEBUG("Triggering EOF callback error? %d", failed); - mon->eofCB(mon, mon->vm, failed); - - qemuMonitorLock(mon); - if (mon->closed) { + if (qemuMonitorUnref(mon) > 0) qemuMonitorUnlock(mon); - VIR_DEBUG("Delayed free of monitor %p", mon); - qemuMonitorFree(mon, 1); - } else { - qemuMonitorUnlock(mon); - } + VIR_DEBUG("Triggering EOF callback error? %d", failed); + (eofCB)(mon, vm, failed); } else { - qemuMonitorUnlock(mon); + if (qemuMonitorUnref(mon) > 0) + qemuMonitorUnlock(mon); } } @@ -453,10 +462,10 @@ qemuMonitorOpen(virDomainObjPtr vm, return NULL; } mon->fd = -1; + mon->refs = 1; mon->vm = vm; mon->eofCB = eofCB; qemuMonitorLock(mon); - virDomainObjRef(vm); switch (vm->monitor_chr->type) { case VIR_DOMAIN_CHR_TYPE_UNIX: @@ -512,10 +521,14 @@ cleanup: } -void qemuMonitorClose(qemuMonitorPtr mon) +int qemuMonitorClose(qemuMonitorPtr mon) { + int refs; + if (!mon) - return; + return 0; + + VIR_DEBUG("mon=%p", mon); qemuMonitorLock(mon); if (!mon->closed) { @@ -523,18 +536,17 @@ void qemuMonitorClose(qemuMonitorPtr mon) virEventRemoveHandle(mon->watch); if (mon->fd != -1) close(mon->fd); - /* NB: don't reset fd / watch fields, since active - * callback may still want them */ + /* NB: ordinarily one might immediately set mon->watch to -1 + * and mon->fd to -1, but there may be a callback active + * that is still relying on these fields being valid. So + * we merely close them, but not clear their values and + * use this explicit 'closed' flag to track this state */ mon->closed = 1; } - if (mon->eofcb) { - VIR_DEBUG("Mark monitor to be deleted %p", mon); + if ((refs = qemuMonitorUnref(mon)) > 0) qemuMonitorUnlock(mon); - } else { - VIR_DEBUG("Delete monitor now %p", mon); - qemuMonitorFree(mon, 0); - } + return refs; } @@ -552,7 +564,6 @@ int qemuMonitorSend(qemuMonitorPtr mon, if (mon->eofcb) { msg->lastErrno = EIO; - qemuMonitorUnlock(mon); return -1; } diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 71688cb..27b43b7 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -78,11 +78,14 @@ typedef int (*qemuMonitorDiskSecretLookup)(qemuMonitorPtr mon, qemuMonitorPtr qemuMonitorOpen(virDomainObjPtr vm, qemuMonitorEOFNotify eofCB); -void qemuMonitorClose(qemuMonitorPtr mon); +int qemuMonitorClose(qemuMonitorPtr mon); void qemuMonitorLock(qemuMonitorPtr mon); void qemuMonitorUnlock(qemuMonitorPtr mon); +int qemuMonitorRef(qemuMonitorPtr mon); +int qemuMonitorUnref(qemuMonitorPtr mon); + void qemuMonitorRegisterDiskSecretLookup(qemuMonitorPtr mon, qemuMonitorDiskSecretLookup secretCB); -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

Hi Daniel, I'm giving it some heavy testing and seems to be rock solid! Thanks a lot for Your effort! I'm going to add few tens of guests to torture libvirt with multiple simultaneous accesses even more... regards nik On Mon, Dec 07, 2009 at 02:18:44PM +0000, Daniel P. Berrange wrote:
On Thu, Dec 03, 2009 at 11:12:40AM +0000, Daniel P. Berrange wrote:
On Thu, Dec 03, 2009 at 12:47:02AM +0100, Matthias Bolte wrote:
2009/11/26 Daniel P. Berrange <berrange@redhat.com>:
If QEMU shuts down while we're in the middle of processing a monitor command, the monitor will be freed, and upon cleaning up we attempt to do qemuMonitorUnlock(priv->mon) when priv->mon is NULL.
To address this we introduce proper reference counting into the qemuMonitorPtr object, and hold an extra reference whenever executing a command.
* src/qemu/qemu_driver.c: Hold a reference on the monitor while executing commands, and only NULL-ify the priv->mon field when the last reference is released * src/qemu/qemu_monitor.h, src/qemu/qemu_monitor.c: Add reference counting to handle safe deletion of monitor objects
The locking pattern below results in destroying a locked mutex. It this intended?
No, that's a bug, the qemuMonitorUnref() call itself should have called unlock if the ref count hit 0, before destroying it.
qemuMonitorLock(mon); [...] if (qemuMonitorUnref(mon) > 0) qemuMonitorUnlock(mon);
Well, this patch makes the TCK deadlock for me, seems to be a lock ordering issue combined with a race condition; it doesn't happen every run. I don't understand all details of the locking and refcounting scheme of the QEMU monitor yet, it's quite complex and gets even more complex.
Yes, that problem shown by valgrind will indeed deadlock. I'll fix this. The domain object lock must never be acquired if the thread holds the monitor lock already. We must have strict ordering from top to bottom (driver -> domain object -> qemu monitor)
This is the patch I'm proposing instead. It removes the ref count adjust of virDomainObjPtr from qemuMonitor, since that requires that you have the bad lock ordering. Instead that extra ref count is added/removed by the qemu driver code. This means the qemuMonitor never has to touch any locks on virDomainObjPtr which complies with our strict top->bottom ordering requirement.
commit c2b32f964b0eed861dea11e5037993e3a3c3fb3d Author: Daniel P. Berrange <berrange@redhat.com> Date: Thu Nov 26 13:29:29 2009 +0000
Fix crash when deleting monitor while a command is in progress
If QEMU shuts down while we're in the middle of processing a monitor command, the monitor will be freed, and upon cleaning up we attempt to do qemuMonitorUnlock(priv->mon) when priv->mon is NULL.
To address this we introduce proper reference counting into the qemuMonitorPtr object, and hold an extra reference whenever executing a command.
* src/qemu/qemu_driver.c: Hold a reference on the monitor while executing commands, and only NULL-ify the priv->mon field when the last reference is released * src/qemu/qemu_monitor.h, src/qemu/qemu_monitor.c: Add reference counting to handle safe deletion of monitor objects
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 43d20ea..fd47dc0 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -272,6 +272,7 @@ static void qemuDomainObjEnterMonitor(virDomainObjPtr obj) qemuDomainObjPrivatePtr priv = obj->privateData;
qemuMonitorLock(priv->mon); + qemuMonitorRef(priv->mon); virDomainObjUnlock(obj); }
@@ -283,9 +284,16 @@ static void qemuDomainObjEnterMonitor(virDomainObjPtr obj) static void qemuDomainObjExitMonitor(virDomainObjPtr obj) { qemuDomainObjPrivatePtr priv = obj->privateData; + int refs;
+ refs = qemuMonitorUnref(priv->mon); qemuMonitorUnlock(priv->mon); virDomainObjLock(obj); + + if (refs == 0) { + virDomainObjUnref(obj); + priv->mon = NULL; + } }
@@ -302,6 +310,7 @@ static void qemuDomainObjEnterMonitorWithDriver(struct qemud_driver *driver, vir qemuDomainObjPrivatePtr priv = obj->privateData;
qemuMonitorLock(priv->mon); + qemuMonitorRef(priv->mon); virDomainObjUnlock(obj); qemuDriverUnlock(driver); } @@ -315,10 +324,17 @@ static void qemuDomainObjEnterMonitorWithDriver(struct qemud_driver *driver, vir static void qemuDomainObjExitMonitorWithDriver(struct qemud_driver *driver, virDomainObjPtr obj) { qemuDomainObjPrivatePtr priv = obj->privateData; + int refs;
+ refs = qemuMonitorUnref(priv->mon); qemuMonitorUnlock(priv->mon); qemuDriverLock(driver); virDomainObjLock(obj); + + if (refs == 0) { + virDomainObjUnref(obj); + priv->mon = NULL; + } }
@@ -653,6 +669,10 @@ qemuConnectMonitor(virDomainObjPtr vm) { qemuDomainObjPrivatePtr priv = vm->privateData;
+ /* Hold an extra reference because we can't allow 'vm' to be + * deleted while the monitor is active */ + virDomainObjRef(vm); + if ((priv->mon = qemuMonitorOpen(vm, qemuHandleMonitorEOF)) == NULL) { VIR_ERROR(_("Failed to connect monitor for %s\n"), vm->def->name); return -1; @@ -2400,8 +2420,9 @@ static void qemudShutdownVMDaemon(virConnectPtr conn, _("Failed to send SIGTERM to %s (%d)"), vm->def->name, vm->pid);
- if (priv->mon) { - qemuMonitorClose(priv->mon); + if (priv->mon && + qemuMonitorClose(priv->mon) == 0) { + virDomainObjUnref(vm); priv->mon = NULL; }
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index f0ef81b..a2e10bc 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -42,7 +42,7 @@ struct _qemuMonitor { virMutex lock; virCond notify;
- virDomainObjPtr dom; + int refs;
int fd; int watch; @@ -67,12 +67,14 @@ struct _qemuMonitor { * the next monitor msg */ int lastErrno;
- /* If the monitor callback is currently active */ + /* If the monitor EOF callback is currently active (stops more commands being run) */ unsigned eofcb: 1; - /* If the monitor callback should free the closed monitor */ + /* If the monitor is in process of shutting down */ unsigned closed: 1; + };
+ void qemuMonitorLock(qemuMonitorPtr mon) { virMutexLock(&mon->lock); @@ -84,21 +86,33 @@ void qemuMonitorUnlock(qemuMonitorPtr mon) }
-static void qemuMonitorFree(qemuMonitorPtr mon, int lockDomain) +static void qemuMonitorFree(qemuMonitorPtr mon) { - VIR_DEBUG("mon=%p, lockDomain=%d", mon, lockDomain); - if (mon->vm) { - if (lockDomain) - virDomainObjLock(mon->vm); - if (!virDomainObjUnref(mon->vm) && lockDomain) - virDomainObjUnlock(mon->vm); - } + VIR_DEBUG("mon=%p", mon); if (virCondDestroy(&mon->notify) < 0) {} virMutexDestroy(&mon->lock); VIR_FREE(mon); }
+int qemuMonitorRef(qemuMonitorPtr mon) +{ + mon->refs++; + return mon->refs; +} + +int qemuMonitorUnref(qemuMonitorPtr mon) +{ + mon->refs--; + + if (mon->refs == 0) { + qemuMonitorUnlock(mon); + qemuMonitorFree(mon); + } + + return mon->refs; +} +
static int qemuMonitorOpenUnix(const char *monitor) @@ -348,6 +362,7 @@ qemuMonitorIO(int watch, int fd, int events, void *opaque) { int quit = 0, failed = 0;
qemuMonitorLock(mon); + qemuMonitorRef(mon); VIR_DEBUG("Monitor %p I/O on watch %d fd %d events %d", mon, watch, fd, events);
if (mon->fd != fd || mon->watch != watch) { @@ -407,23 +422,17 @@ qemuMonitorIO(int watch, int fd, int events, void *opaque) { * but is this safe ? I think it is, because the callback * will try to acquire the virDomainObjPtr mutex next */ if (failed || quit) { + qemuMonitorEOFNotify eofCB = mon->eofCB; + virDomainObjPtr vm = mon->vm; /* Make sure anyone waiting wakes up now */ virCondSignal(&mon->notify); - mon->eofcb = 1; - qemuMonitorUnlock(mon); - VIR_DEBUG("Triggering EOF callback error? %d", failed); - mon->eofCB(mon, mon->vm, failed); - - qemuMonitorLock(mon); - if (mon->closed) { + if (qemuMonitorUnref(mon) > 0) qemuMonitorUnlock(mon); - VIR_DEBUG("Delayed free of monitor %p", mon); - qemuMonitorFree(mon, 1); - } else { - qemuMonitorUnlock(mon); - } + VIR_DEBUG("Triggering EOF callback error? %d", failed); + (eofCB)(mon, vm, failed); } else { - qemuMonitorUnlock(mon); + if (qemuMonitorUnref(mon) > 0) + qemuMonitorUnlock(mon); } }
@@ -453,10 +462,10 @@ qemuMonitorOpen(virDomainObjPtr vm, return NULL; } mon->fd = -1; + mon->refs = 1; mon->vm = vm; mon->eofCB = eofCB; qemuMonitorLock(mon); - virDomainObjRef(vm);
switch (vm->monitor_chr->type) { case VIR_DOMAIN_CHR_TYPE_UNIX: @@ -512,10 +521,14 @@ cleanup: }
-void qemuMonitorClose(qemuMonitorPtr mon) +int qemuMonitorClose(qemuMonitorPtr mon) { + int refs; + if (!mon) - return; + return 0; + + VIR_DEBUG("mon=%p", mon);
qemuMonitorLock(mon); if (!mon->closed) { @@ -523,18 +536,17 @@ void qemuMonitorClose(qemuMonitorPtr mon) virEventRemoveHandle(mon->watch); if (mon->fd != -1) close(mon->fd); - /* NB: don't reset fd / watch fields, since active - * callback may still want them */ + /* NB: ordinarily one might immediately set mon->watch to -1 + * and mon->fd to -1, but there may be a callback active + * that is still relying on these fields being valid. So + * we merely close them, but not clear their values and + * use this explicit 'closed' flag to track this state */ mon->closed = 1; }
- if (mon->eofcb) { - VIR_DEBUG("Mark monitor to be deleted %p", mon); + if ((refs = qemuMonitorUnref(mon)) > 0) qemuMonitorUnlock(mon); - } else { - VIR_DEBUG("Delete monitor now %p", mon); - qemuMonitorFree(mon, 0); - } + return refs; }
@@ -552,7 +564,6 @@ int qemuMonitorSend(qemuMonitorPtr mon,
if (mon->eofcb) { msg->lastErrno = EIO; - qemuMonitorUnlock(mon); return -1; }
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 71688cb..27b43b7 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -78,11 +78,14 @@ typedef int (*qemuMonitorDiskSecretLookup)(qemuMonitorPtr mon, qemuMonitorPtr qemuMonitorOpen(virDomainObjPtr vm, qemuMonitorEOFNotify eofCB);
-void qemuMonitorClose(qemuMonitorPtr mon); +int qemuMonitorClose(qemuMonitorPtr mon);
void qemuMonitorLock(qemuMonitorPtr mon); void qemuMonitorUnlock(qemuMonitorPtr mon);
+int qemuMonitorRef(qemuMonitorPtr mon); +int qemuMonitorUnref(qemuMonitorPtr mon); + void qemuMonitorRegisterDiskSecretLookup(qemuMonitorPtr mon, qemuMonitorDiskSecretLookup secretCB);
-- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|
-- Libvir-list mailing list Libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
-- ------------------------------------- Nikola CIPRICH LinuxBox.cz, s.r.o. 28. rijna 168, 709 01 Ostrava tel.: +420 596 603 142 fax: +420 596 621 273 mobil: +420 777 093 799 www.linuxbox.cz mobil servis: +420 737 238 656 email servis: servis@linuxbox.cz -------------------------------------

Thanks. I know from https://www.redhat.com/archives/libvir-list/2009-December/msg00064.html that there is a deadlock problem in this fix. I would appreciate a notice when a solution is found. Thanks a lot. -- Shi Jin, PhD --- On Thu, 11/26/09, Daniel P. Berrange <berrange@redhat.com> wrote:
From: Daniel P. Berrange <berrange@redhat.com> Subject: [libvirt] [PATCH 03/14] Fix crash when deleting monitor while a command is in progress To: libvir-list@redhat.com Date: Thursday, November 26, 2009, 11:27 AM If QEMU shuts down while we're in the middle of processing a monitor command, the monitor will be freed, and upon cleaning up we attempt to do qemuMonitorUnlock(priv->mon) when priv->mon is NULL.
To address this we introduce proper reference counting into the qemuMonitorPtr object, and hold an extra reference whenever executing a command.
* src/qemu/qemu_driver.c: Hold a reference on the monitor while executing commands, and only NULL-ify the priv->mon field when the last reference is released * src/qemu/qemu_monitor.h, src/qemu/qemu_monitor.c: Add reference counting to handle safe deletion of monitor objects --- src/qemu/qemu_driver.c | 13 ++++++-- src/qemu/qemu_monitor.c | 81 +++++++++++++++++++++++++++++------------------ src/qemu/qemu_monitor.h | 5 ++- 3 files changed, 64 insertions(+), 35 deletions(-)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index c9b5ac2..3befe3d 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -271,6 +271,7 @@ static void qemuDomainObjEnterMonitor(virDomainObjPtr obj) qemuDomainObjPrivatePtr priv = obj->privateData;
qemuMonitorLock(priv->mon); + qemuMonitorRef(priv->mon); virDomainObjUnlock(obj); }
@@ -281,10 +282,15 @@ static void qemuDomainObjEnterMonitor(virDomainObjPtr obj) */ static void qemuDomainObjExitMonitor(virDomainObjPtr obj) { + int refs; qemuDomainObjPrivatePtr priv = obj->privateData;
+ refs = qemuMonitorUnref(priv->mon); qemuMonitorUnlock(priv->mon); virDomainObjLock(obj); + + if (refs == 0) + priv->mon = NULL; }
@@ -301,6 +307,7 @@ static void qemuDomainObjEnterMonitorWithDriver(struct qemud_driver *driver, vir qemuDomainObjPrivatePtr priv = obj->privateData;
qemuMonitorLock(priv->mon); + qemuMonitorRef(priv->mon); virDomainObjUnlock(obj); qemuDriverUnlock(driver); } @@ -315,6 +322,7 @@ static void qemuDomainObjExitMonitorWithDriver(struct qemud_driver *driver, virD { qemuDomainObjPrivatePtr priv = obj->privateData;
+ qemuMonitorUnref(priv->mon); qemuMonitorUnlock(priv->mon); qemuDriverLock(driver); virDomainObjLock(obj); @@ -2399,10 +2407,9 @@ static void qemudShutdownVMDaemon(virConnectPtr conn, _("Failed to send SIGTERM to %s (%d)"), vm->def->name, vm->pid);
- if (priv->mon) { - qemuMonitorClose(priv->mon); + if (priv->mon && + qemuMonitorClose(priv->mon) == 0) priv->mon = NULL; - }
if (vm->monitor_chr) { if (vm->monitor_chr->type == VIR_DOMAIN_CHR_TYPE_UNIX) diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index f0ef81b..3829e7a 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -42,6 +42,8 @@ struct _qemuMonitor { virMutex lock; virCond notify;
+ int refs; + virDomainObjPtr dom;
int fd; @@ -67,12 +69,14 @@ struct _qemuMonitor { * the next monitor msg */ int lastErrno;
- /* If the monitor callback is currently active */ + /* If the monitor EOF callback is currently active (stops more commands being run) */ unsigned eofcb: 1; - /* If the monitor callback should free the closed monitor */ + /* If the monitor is in process of shutting down */ unsigned closed: 1; + };
+ void qemuMonitorLock(qemuMonitorPtr mon) { virMutexLock(&mon->lock); @@ -84,21 +88,33 @@ void qemuMonitorUnlock(qemuMonitorPtr mon) }
-static void qemuMonitorFree(qemuMonitorPtr mon, int lockDomain) +static void qemuMonitorFree(qemuMonitorPtr mon) { - VIR_DEBUG("mon=%p, lockDomain=%d", mon, lockDomain); - if (mon->vm) { - if (lockDomain) - virDomainObjLock(mon->vm); - if (!virDomainObjUnref(mon->vm) && lockDomain) - virDomainObjUnlock(mon->vm); - } + VIR_DEBUG("mon=%p", mon); + if (mon->vm) + virDomainObjUnref(mon->vm); if (virCondDestroy(&mon->notify) < 0) {} virMutexDestroy(&mon->lock); VIR_FREE(mon); }
+int qemuMonitorRef(qemuMonitorPtr mon) +{ + mon->refs++; + return mon->refs; +} + +int qemuMonitorUnref(qemuMonitorPtr mon) +{ + mon->refs--; + + if (mon->refs == 0) + qemuMonitorFree(mon); + + return mon->refs; +} +
static int qemuMonitorOpenUnix(const char *monitor) @@ -346,8 +362,10 @@ static void qemuMonitorIO(int watch, int fd, int events, void *opaque) { qemuMonitorPtr mon = opaque; int quit = 0, failed = 0; + virDomainObjPtr vm;
qemuMonitorLock(mon); + qemuMonitorRef(mon); VIR_DEBUG("Monitor %p I/O on watch %d fd %d events %d", mon, watch, fd, events);
if (mon->fd != fd || mon->watch != watch) { @@ -409,22 +427,20 @@ qemuMonitorIO(int watch, int fd, int events, void *opaque) { if (failed || quit) { /* Make sure anyone waiting wakes up now */ virCondSignal(&mon->notify); - mon->eofcb = 1; qemuMonitorUnlock(mon); VIR_DEBUG("Triggering EOF callback error? %d", failed); mon->eofCB(mon, mon->vm, failed);
qemuMonitorLock(mon); - if (mon->closed) { - qemuMonitorUnlock(mon); - VIR_DEBUG("Delayed free of monitor %p", mon); - qemuMonitorFree(mon, 1); - } else { - qemuMonitorUnlock(mon); - } - } else { - qemuMonitorUnlock(mon); } + + /* Safe 'vm' to an intermediate ptr, since 'mon' may be + * freed after the Unref call */ + vm = mon->vm; + virDomainObjLock(vm); + if (qemuMonitorUnref(mon) > 0) + qemuMonitorUnlock(mon); + virDomainObjUnlock(vm); }
@@ -453,6 +469,7 @@ qemuMonitorOpen(virDomainObjPtr vm, return NULL; } mon->fd = -1; + mon->refs = 1; mon->vm = vm; mon->eofCB = eofCB; qemuMonitorLock(mon); @@ -512,10 +529,14 @@ cleanup: }
-void qemuMonitorClose(qemuMonitorPtr mon) +int qemuMonitorClose(qemuMonitorPtr mon) { + int refs; + if (!mon) - return; + return 0; + + VIR_DEBUG("mon=%p", mon);
qemuMonitorLock(mon); if (!mon->closed) { @@ -523,18 +544,17 @@ void qemuMonitorClose(qemuMonitorPtr mon) virEventRemoveHandle(mon->watch); if (mon->fd != -1) close(mon->fd); - /* NB: don't reset fd / watch fields, since active - * callback may still want them */ + /* NB: ordinarily one might immediately set mon->watch to -1 + * and mon->fd to -1, but there may be a callback active + * that is still relying on these fields being valid. So + * we merely close them, but not clear their values and + * use this explicit 'closed' flag to track this state */ mon->closed = 1; }
- if (mon->eofcb) { - VIR_DEBUG("Mark monitor to be deleted %p", mon); + if ((refs = qemuMonitorUnref(mon)) > 0) qemuMonitorUnlock(mon); - } else { - VIR_DEBUG("Delete monitor now %p", mon); - qemuMonitorFree(mon, 0); - } + return refs; }
@@ -552,7 +572,6 @@ int qemuMonitorSend(qemuMonitorPtr mon,
if (mon->eofcb) { msg->lastErrno = EIO; - qemuMonitorUnlock(mon); return -1; }
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 71688cb..27b43b7 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -78,11 +78,14 @@ typedef int (*qemuMonitorDiskSecretLookup)(qemuMonitorPtr mon, qemuMonitorPtr qemuMonitorOpen(virDomainObjPtr vm, qemuMonitorEOFNotify eofCB);
-void qemuMonitorClose(qemuMonitorPtr mon); +int qemuMonitorClose(qemuMonitorPtr mon);
void qemuMonitorLock(qemuMonitorPtr mon); void qemuMonitorUnlock(qemuMonitorPtr mon);
+int qemuMonitorRef(qemuMonitorPtr mon); +int qemuMonitorUnref(qemuMonitorPtr mon); + void qemuMonitorRegisterDiskSecretLookup(qemuMonitorPtr mon, qemuMonitorDiskSecretLookup secretCB);
-- 1.6.5.2
-- Libvir-list mailing list Libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list

The qemuMonitorEscape() method, and the VIR_ENUM for migration status will be needed by the JSON monitor too, so move that code into the shared qemu_monitor.c file instead of qemu_monitor_text.c * src/qemu/qemu_monitor.h: Declare qemuMonitorMigrationStatus enum and qemuMonitorEscapeArg and qemuMonitorEscapeShell methods * src/qemu/qemu_monitor.c: Implement qemuMonitorMigrationStatus enum and qemuMonitorEscapeArg and qemuMonitorEscapeShell methods * src/qemu/qemu_monitor_text.c: Remove above methods/enum --- src/qemu/qemu_monitor.c | 87 ++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.h | 5 ++ src/qemu/qemu_monitor_text.c | 87 ------------------------------------------ 3 files changed, 92 insertions(+), 87 deletions(-) diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 3829e7a..21f8c4b 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -77,6 +77,93 @@ struct _qemuMonitor { }; +VIR_ENUM_IMPL(qemuMonitorMigrationStatus, + QEMU_MONITOR_MIGRATION_STATUS_LAST, + "inactive", "active", "completed", "failed", "cancelled") + +static char *qemuMonitorEscape(const char *in, int shell) +{ + int len = 0; + int i, j; + char *out; + + /* To pass through the QEMU monitor, we need to use escape + sequences: \r, \n, \", \\ + + To pass through both QEMU + the shell, we need to escape + the single character ' as the five characters '\\'' + */ + + for (i = 0; in[i] != '\0'; i++) { + switch(in[i]) { + case '\r': + case '\n': + case '"': + case '\\': + len += 2; + break; + case '\'': + if (shell) + len += 5; + else + len += 1; + break; + default: + len += 1; + break; + } + } + + if (VIR_ALLOC_N(out, len + 1) < 0) + return NULL; + + for (i = j = 0; in[i] != '\0'; i++) { + switch(in[i]) { + case '\r': + out[j++] = '\\'; + out[j++] = 'r'; + break; + case '\n': + out[j++] = '\\'; + out[j++] = 'n'; + break; + case '"': + case '\\': + out[j++] = '\\'; + out[j++] = in[i]; + break; + case '\'': + if (shell) { + out[j++] = '\''; + out[j++] = '\\'; + out[j++] = '\\'; + out[j++] = '\''; + out[j++] = '\''; + } else { + out[j++] = in[i]; + } + break; + default: + out[j++] = in[i]; + break; + } + } + out[j] = '\0'; + + return out; +} + +char *qemuMonitorEscapeArg(const char *in) +{ + return qemuMonitorEscape(in, 0); +} + +char *qemuMonitorEscapeShell(const char *in) +{ + return qemuMonitorEscape(in, 1); +} + + void qemuMonitorLock(qemuMonitorPtr mon) { virMutexLock(&mon->lock); diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 27b43b7..88d3dd7 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -75,6 +75,9 @@ typedef int (*qemuMonitorDiskSecretLookup)(qemuMonitorPtr mon, char **secret, size_t *secretLen); +char *qemuMonitorEscapeArg(const char *in); +char *qemuMonitorEscapeShell(const char *in); + qemuMonitorPtr qemuMonitorOpen(virDomainObjPtr vm, qemuMonitorEOFNotify eofCB); @@ -158,6 +161,8 @@ enum { QEMU_MONITOR_MIGRATION_STATUS_LAST }; +VIR_ENUM_DECL(qemuMonitorMigrationStatus) + int qemuMonitorGetMigrationStatus(qemuMonitorPtr mon, int *status, unsigned long long *transferred, diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c index d39a417..2a8b3bd 100644 --- a/src/qemu/qemu_monitor_text.c +++ b/src/qemu/qemu_monitor_text.c @@ -51,88 +51,6 @@ typedef int qemuMonitorExtraPromptHandler(qemuMonitorPtr mon, void *data); -static char *qemuMonitorEscape(const char *in, int shell) -{ - int len = 0; - int i, j; - char *out; - - /* To pass through the QEMU monitor, we need to use escape - sequences: \r, \n, \", \\ - - To pass through both QEMU + the shell, we need to escape - the single character ' as the five characters '\\'' - */ - - for (i = 0; in[i] != '\0'; i++) { - switch(in[i]) { - case '\r': - case '\n': - case '"': - case '\\': - len += 2; - break; - case '\'': - if (shell) - len += 5; - else - len += 1; - break; - default: - len += 1; - break; - } - } - - if (VIR_ALLOC_N(out, len + 1) < 0) - return NULL; - - for (i = j = 0; in[i] != '\0'; i++) { - switch(in[i]) { - case '\r': - out[j++] = '\\'; - out[j++] = 'r'; - break; - case '\n': - out[j++] = '\\'; - out[j++] = 'n'; - break; - case '"': - case '\\': - out[j++] = '\\'; - out[j++] = in[i]; - break; - case '\'': - if (shell) { - out[j++] = '\''; - out[j++] = '\\'; - out[j++] = '\\'; - out[j++] = '\''; - out[j++] = '\''; - } else { - out[j++] = in[i]; - } - break; - default: - out[j++] = in[i]; - break; - } - } - out[j] = '\0'; - - return out; -} - -static char *qemuMonitorEscapeArg(const char *in) -{ - return qemuMonitorEscape(in, 0); -} - -static char *qemuMonitorEscapeShell(const char *in) -{ - return qemuMonitorEscape(in, 1); -} - /* When connecting to a monitor, QEMU will print a greeting like * * QEMU 0.11.0 monitor - type 'help' for more information @@ -904,11 +822,6 @@ cleanup: #define MIGRATION_REMAINING_PREFIX "remaining ram: " #define MIGRATION_TOTAL_PREFIX "total ram: " -VIR_ENUM_DECL(qemuMonitorMigrationStatus) -VIR_ENUM_IMPL(qemuMonitorMigrationStatus, - QEMU_MONITOR_MIGRATION_STATUS_LAST, - "inactive", "active", "completed", "failed", "cancelled") - int qemuMonitorTextGetMigrationStatus(qemuMonitorPtr mon, int *status, unsigned long long *transferred, -- 1.6.5.2

2009/11/26 Daniel P. Berrange <berrange@redhat.com>:
The qemuMonitorEscape() method, and the VIR_ENUM for migration status will be needed by the JSON monitor too, so move that code into the shared qemu_monitor.c file instead of qemu_monitor_text.c
* src/qemu/qemu_monitor.h: Declare qemuMonitorMigrationStatus enum and qemuMonitorEscapeArg and qemuMonitorEscapeShell methods * src/qemu/qemu_monitor.c: Implement qemuMonitorMigrationStatus enum and qemuMonitorEscapeArg and qemuMonitorEscapeShell methods * src/qemu/qemu_monitor_text.c: Remove above methods/enum ---
ACK. Matthias

On Thu, Nov 26, 2009 at 06:27:22PM +0000, Daniel P. Berrange wrote:
The qemuMonitorEscape() method, and the VIR_ENUM for migration status will be needed by the JSON monitor too, so move that code into the shared qemu_monitor.c file instead of qemu_monitor_text.c
* src/qemu/qemu_monitor.h: Declare qemuMonitorMigrationStatus enum and qemuMonitorEscapeArg and qemuMonitorEscapeShell methods * src/qemu/qemu_monitor.c: Implement qemuMonitorMigrationStatus enum and qemuMonitorEscapeArg and qemuMonitorEscapeShell methods * src/qemu/qemu_monitor_text.c: Remove above methods/enum --- src/qemu/qemu_monitor.c | 87 ++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.h | 5 ++ src/qemu/qemu_monitor_text.c | 87 ------------------------------------------ 3 files changed, 92 insertions(+), 87 deletions(-)
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 3829e7a..21f8c4b 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -77,6 +77,93 @@ struct _qemuMonitor { };
+VIR_ENUM_IMPL(qemuMonitorMigrationStatus, + QEMU_MONITOR_MIGRATION_STATUS_LAST, + "inactive", "active", "completed", "failed", "cancelled") + +static char *qemuMonitorEscape(const char *in, int shell) +{ + int len = 0; + int i, j; + char *out; + + /* To pass through the QEMU monitor, we need to use escape + sequences: \r, \n, \", \\ + + To pass through both QEMU + the shell, we need to escape + the single character ' as the five characters '\\'' + */ + + for (i = 0; in[i] != '\0'; i++) { + switch(in[i]) { + case '\r': + case '\n': + case '"': + case '\\': + len += 2; + break; + case '\'': + if (shell) + len += 5; + else + len += 1; + break; + default: + len += 1; + break; + } + } + + if (VIR_ALLOC_N(out, len + 1) < 0) + return NULL; + + for (i = j = 0; in[i] != '\0'; i++) { + switch(in[i]) { + case '\r': + out[j++] = '\\'; + out[j++] = 'r'; + break; + case '\n': + out[j++] = '\\'; + out[j++] = 'n'; + break; + case '"': + case '\\': + out[j++] = '\\'; + out[j++] = in[i]; + break; + case '\'': + if (shell) { + out[j++] = '\''; + out[j++] = '\\'; + out[j++] = '\\'; + out[j++] = '\''; + out[j++] = '\''; + } else { + out[j++] = in[i]; + } + break; + default: + out[j++] = in[i]; + break; + } + } + out[j] = '\0'; + + return out; +} + +char *qemuMonitorEscapeArg(const char *in) +{ + return qemuMonitorEscape(in, 0); +} + +char *qemuMonitorEscapeShell(const char *in) +{ + return qemuMonitorEscape(in, 1); +} + + void qemuMonitorLock(qemuMonitorPtr mon) { virMutexLock(&mon->lock); diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 27b43b7..88d3dd7 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -75,6 +75,9 @@ typedef int (*qemuMonitorDiskSecretLookup)(qemuMonitorPtr mon, char **secret, size_t *secretLen);
+char *qemuMonitorEscapeArg(const char *in); +char *qemuMonitorEscapeShell(const char *in); + qemuMonitorPtr qemuMonitorOpen(virDomainObjPtr vm, qemuMonitorEOFNotify eofCB);
@@ -158,6 +161,8 @@ enum { QEMU_MONITOR_MIGRATION_STATUS_LAST };
+VIR_ENUM_DECL(qemuMonitorMigrationStatus) + int qemuMonitorGetMigrationStatus(qemuMonitorPtr mon, int *status, unsigned long long *transferred, diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c index d39a417..2a8b3bd 100644 --- a/src/qemu/qemu_monitor_text.c +++ b/src/qemu/qemu_monitor_text.c @@ -51,88 +51,6 @@ typedef int qemuMonitorExtraPromptHandler(qemuMonitorPtr mon, void *data);
-static char *qemuMonitorEscape(const char *in, int shell) -{ - int len = 0; - int i, j; - char *out; - - /* To pass through the QEMU monitor, we need to use escape - sequences: \r, \n, \", \\ - - To pass through both QEMU + the shell, we need to escape - the single character ' as the five characters '\\'' - */ - - for (i = 0; in[i] != '\0'; i++) { - switch(in[i]) { - case '\r': - case '\n': - case '"': - case '\\': - len += 2; - break; - case '\'': - if (shell) - len += 5; - else - len += 1; - break; - default: - len += 1; - break; - } - } - - if (VIR_ALLOC_N(out, len + 1) < 0) - return NULL; - - for (i = j = 0; in[i] != '\0'; i++) { - switch(in[i]) { - case '\r': - out[j++] = '\\'; - out[j++] = 'r'; - break; - case '\n': - out[j++] = '\\'; - out[j++] = 'n'; - break; - case '"': - case '\\': - out[j++] = '\\'; - out[j++] = in[i]; - break; - case '\'': - if (shell) { - out[j++] = '\''; - out[j++] = '\\'; - out[j++] = '\\'; - out[j++] = '\''; - out[j++] = '\''; - } else { - out[j++] = in[i]; - } - break; - default: - out[j++] = in[i]; - break; - } - } - out[j] = '\0'; - - return out; -} - -static char *qemuMonitorEscapeArg(const char *in) -{ - return qemuMonitorEscape(in, 0); -} - -static char *qemuMonitorEscapeShell(const char *in) -{ - return qemuMonitorEscape(in, 1); -} - /* When connecting to a monitor, QEMU will print a greeting like * * QEMU 0.11.0 monitor - type 'help' for more information @@ -904,11 +822,6 @@ cleanup: #define MIGRATION_REMAINING_PREFIX "remaining ram: " #define MIGRATION_TOTAL_PREFIX "total ram: "
-VIR_ENUM_DECL(qemuMonitorMigrationStatus) -VIR_ENUM_IMPL(qemuMonitorMigrationStatus, - QEMU_MONITOR_MIGRATION_STATUS_LAST, - "inactive", "active", "completed", "failed", "cancelled") - int qemuMonitorTextGetMigrationStatus(qemuMonitorPtr mon, int *status, unsigned long long *transferred,
ACK Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

* src/qemu/qemu_monitor_text.c: Add missing underscore in the migrate_cancel monitor command impl --- src/qemu/qemu_monitor_text.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c index 2a8b3bd..db15573 100644 --- a/src/qemu/qemu_monitor_text.c +++ b/src/qemu/qemu_monitor_text.c @@ -1038,7 +1038,7 @@ int qemuMonitorTextMigrateCancel(qemuMonitorPtr mon) { char *info = NULL; - if (qemuMonitorCommand(mon, "migrate cancel", &info) < 0) { + if (qemuMonitorCommand(mon, "migrate_cancel", &info) < 0) { qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", _("cannot run monitor command to cancel migration")); return -1; -- 1.6.5.2

On Thu, Nov 26, 2009 at 06:27:23PM +0000, Daniel P. Berrange wrote:
* src/qemu/qemu_monitor_text.c: Add missing underscore in the migrate_cancel monitor command impl --- src/qemu/qemu_monitor_text.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c index 2a8b3bd..db15573 100644 --- a/src/qemu/qemu_monitor_text.c +++ b/src/qemu/qemu_monitor_text.c @@ -1038,7 +1038,7 @@ int qemuMonitorTextMigrateCancel(qemuMonitorPtr mon) { char *info = NULL;
- if (qemuMonitorCommand(mon, "migrate cancel", &info) < 0) { + if (qemuMonitorCommand(mon, "migrate_cancel", &info) < 0) { qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", _("cannot run monitor command to cancel migration")); return -1;
OOps. ACK :-) How did you spot that ? Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

On Thu, Dec 03, 2009 at 01:30:30PM +0100, Daniel Veillard wrote:
On Thu, Nov 26, 2009 at 06:27:23PM +0000, Daniel P. Berrange wrote:
* src/qemu/qemu_monitor_text.c: Add missing underscore in the migrate_cancel monitor command impl --- src/qemu/qemu_monitor_text.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c index 2a8b3bd..db15573 100644 --- a/src/qemu/qemu_monitor_text.c +++ b/src/qemu/qemu_monitor_text.c @@ -1038,7 +1038,7 @@ int qemuMonitorTextMigrateCancel(qemuMonitorPtr mon) { char *info = NULL;
- if (qemuMonitorCommand(mon, "migrate cancel", &info) < 0) { + if (qemuMonitorCommand(mon, "migrate_cancel", &info) < 0) { qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", _("cannot run monitor command to cancel migration")); return -1;
OOps. ACK :-) How did you spot that ?
Purely by luck :-) Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

The current QEMU disk media change does not support setting the disk format. The new JSON monitor will support this, so add an extra parameter to pass this info in * src/qemu/qemu_driver.c: Pass in disk format when changing media * src/qemu/qemu_monitor.h, src/qemu/qemu_monitor.c, src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h: Add a 'format' arg to qemuMonitorChangeMedia() --- src/qemu/qemu_driver.c | 9 ++++++++- src/qemu/qemu_monitor.c | 9 +++++---- src/qemu/qemu_monitor.h | 3 ++- src/qemu/qemu_monitor_text.c | 7 ++++--- src/qemu/qemu_monitor_text.h | 3 ++- 5 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 3befe3d..2907976 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -4716,7 +4716,14 @@ static int qemudDomainChangeEjectableMedia(virConnectPtr conn, qemuDomainObjPrivatePtr priv = vm->privateData; qemuDomainObjEnterMonitorWithDriver(driver, vm); if (newdisk->src) { - ret = qemuMonitorChangeMedia(priv->mon, devname, newdisk->src); + const char *format = NULL; + if (newdisk->type != VIR_DOMAIN_DISK_TYPE_DIR) { + if (newdisk->driverType) + format = newdisk->driverType; + else if (origdisk->driverType) + format = origdisk->driverType; + } + ret = qemuMonitorChangeMedia(priv->mon, devname, newdisk->src, format); } else { ret = qemuMonitorEjectMedia(priv->mon, devname); } diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 21f8c4b..a6b445e 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -783,12 +783,13 @@ int qemuMonitorEjectMedia(qemuMonitorPtr mon, int qemuMonitorChangeMedia(qemuMonitorPtr mon, const char *devname, - const char *newmedia) + const char *newmedia, + const char *format) { - DEBUG("mon=%p, fd=%d devname=%s newmedia=%s", - mon, mon->fd, devname, newmedia); + DEBUG("mon=%p, fd=%d devname=%s newmedia=%s format=%s", + mon, mon->fd, devname, newmedia, format); - return qemuMonitorTextChangeMedia(mon, devname, newmedia); + return qemuMonitorTextChangeMedia(mon, devname, newmedia, format); } diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 88d3dd7..14198fb 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -136,7 +136,8 @@ int qemuMonitorEjectMedia(qemuMonitorPtr mon, const char *devname); int qemuMonitorChangeMedia(qemuMonitorPtr mon, const char *devname, - const char *newmedia); + const char *newmedia, + const char *format); int qemuMonitorSaveVirtualMemory(qemuMonitorPtr mon, diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c index db15573..0d5ad79 100644 --- a/src/qemu/qemu_monitor_text.c +++ b/src/qemu/qemu_monitor_text.c @@ -693,7 +693,8 @@ cleanup: int qemuMonitorTextChangeMedia(qemuMonitorPtr mon, const char *devname, - const char *newmedia) + const char *newmedia, + const char *format ATTRIBUTE_UNUSED) { char *cmd = NULL; char *reply = NULL; @@ -1016,8 +1017,8 @@ cleanup: } int qemuMonitorTextMigrateToUnix(qemuMonitorPtr mon, - int background, - const char *unixfile) + int background, + const char *unixfile) { char *dest = NULL; int ret = -1; diff --git a/src/qemu/qemu_monitor_text.h b/src/qemu/qemu_monitor_text.h index 6bca07a..bc52ad2 100644 --- a/src/qemu/qemu_monitor_text.h +++ b/src/qemu/qemu_monitor_text.h @@ -62,7 +62,8 @@ int qemuMonitorTextEjectMedia(qemuMonitorPtr mon, const char *devname); int qemuMonitorTextChangeMedia(qemuMonitorPtr mon, const char *devname, - const char *newmedia); + const char *newmedia, + const char *format); int qemuMonitorTextSaveVirtualMemory(qemuMonitorPtr mon, -- 1.6.5.2

2009/11/26 Daniel P. Berrange <berrange@redhat.com>:
The current QEMU disk media change does not support setting the disk format. The new JSON monitor will support this, so add an extra parameter to pass this info in
* src/qemu/qemu_driver.c: Pass in disk format when changing media * src/qemu/qemu_monitor.h, src/qemu/qemu_monitor.c, src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h: Add a 'format' arg to qemuMonitorChangeMedia() ---
ACK. Matthias

On Thu, Nov 26, 2009 at 06:27:24PM +0000, Daniel P. Berrange wrote:
The current QEMU disk media change does not support setting the disk format. The new JSON monitor will support this, so add an extra parameter to pass this info in
* src/qemu/qemu_driver.c: Pass in disk format when changing media * src/qemu/qemu_monitor.h, src/qemu/qemu_monitor.c, src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h: Add a 'format' arg to qemuMonitorChangeMedia() --- src/qemu/qemu_driver.c | 9 ++++++++- src/qemu/qemu_monitor.c | 9 +++++---- src/qemu/qemu_monitor.h | 3 ++- src/qemu/qemu_monitor_text.c | 7 ++++--- src/qemu/qemu_monitor_text.h | 3 ++- 5 files changed, 21 insertions(+), 10 deletions(-)
ACK, Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

The code to start CPUs executing has nothing todo with CPU affinity masks, so pull it out of the qemudInitCpuAffinity() method and up into qemudStartVMDaemon() * src/qemu/qemu_driver.c: Pull code to start CPUs executing out of qemudInitCpuAffinity() --- src/qemu/qemu_driver.c | 37 +++++++++++++++++-------------------- 1 files changed, 17 insertions(+), 20 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 2907976..b1f5894 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -1370,13 +1370,10 @@ qemuDetectVcpuPIDs(virConnectPtr conn, } static int -qemudInitCpus(virConnectPtr conn, - struct qemud_driver *driver, - virDomainObjPtr vm, - const char *migrateFrom) { +qemudInitCpuAffinity(virConnectPtr conn, + virDomainObjPtr vm) { int i, hostcpus, maxcpu = QEMUD_CPUMASK_LEN; virNodeInfo nodeinfo; - qemuDomainObjPrivatePtr priv = vm->privateData; unsigned char *cpumap; int cpumaplen; @@ -1421,20 +1418,6 @@ qemudInitCpus(virConnectPtr conn, } VIR_FREE(cpumap); - /* XXX This resume doesn't really belong here. Move it up to caller */ - if (migrateFrom == NULL) { - /* Allow the CPUS to start executing */ - qemuDomainObjEnterMonitorWithDriver(driver, vm); - if (qemuMonitorStartCPUs(priv->mon, conn) < 0) { - if (virGetLastError() == NULL) - qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - "%s", _("resume operation failed")); - qemuDomainObjExitMonitorWithDriver(driver, vm); - return -1; - } - qemuDomainObjExitMonitorWithDriver(driver, vm); - } - return 0; } @@ -2324,7 +2307,7 @@ static int qemudStartVMDaemon(virConnectPtr conn, if (qemuDetectVcpuPIDs(conn, driver, vm) < 0) goto abort; - if (qemudInitCpus(conn, driver, vm, migrateFrom) < 0) + if (qemudInitCpuAffinity(conn, vm) < 0) goto abort; if (qemuInitPasswords(driver, vm) < 0) @@ -2337,6 +2320,20 @@ static int qemudStartVMDaemon(virConnectPtr conn, } qemuDomainObjExitMonitorWithDriver(driver, vm); + if (migrateFrom == NULL) { + /* Allow the CPUS to start executing */ + qemuDomainObjEnterMonitorWithDriver(driver, vm); + if (qemuMonitorStartCPUs(priv->mon, conn) < 0) { + if (virGetLastError() == NULL) + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("resume operation failed")); + qemuDomainObjExitMonitorWithDriver(driver, vm); + return -1; + } + qemuDomainObjExitMonitorWithDriver(driver, vm); + } + + if (virDomainSaveStatus(conn, driver->stateDir, vm) < 0) goto abort; -- 1.6.5.2

2009/11/26 Daniel P. Berrange <berrange@redhat.com>:
The code to start CPUs executing has nothing todo with CPU affinity masks, so pull it out of the qemudInitCpuAffinity() method and up into qemudStartVMDaemon()
* src/qemu/qemu_driver.c: Pull code to start CPUs executing out of qemudInitCpuAffinity() --- src/qemu/qemu_driver.c | 37 +++++++++++++++++-------------------- 1 files changed, 17 insertions(+), 20 deletions(-)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 2907976..b1f5894 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -1370,13 +1370,10 @@ qemuDetectVcpuPIDs(virConnectPtr conn, }
static int -qemudInitCpus(virConnectPtr conn, - struct qemud_driver *driver, - virDomainObjPtr vm, - const char *migrateFrom) { +qemudInitCpuAffinity(virConnectPtr conn, + virDomainObjPtr vm) { int i, hostcpus, maxcpu = QEMUD_CPUMASK_LEN; virNodeInfo nodeinfo; - qemuDomainObjPrivatePtr priv = vm->privateData; unsigned char *cpumap; int cpumaplen;
@@ -1421,20 +1418,6 @@ qemudInitCpus(virConnectPtr conn, } VIR_FREE(cpumap);
- /* XXX This resume doesn't really belong here. Move it up to caller */ - if (migrateFrom == NULL) { - /* Allow the CPUS to start executing */ - qemuDomainObjEnterMonitorWithDriver(driver, vm); - if (qemuMonitorStartCPUs(priv->mon, conn) < 0) { - if (virGetLastError() == NULL) - qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - "%s", _("resume operation failed")); - qemuDomainObjExitMonitorWithDriver(driver, vm); - return -1; - } - qemuDomainObjExitMonitorWithDriver(driver, vm); - } - return 0; }
@@ -2324,7 +2307,7 @@ static int qemudStartVMDaemon(virConnectPtr conn, if (qemuDetectVcpuPIDs(conn, driver, vm) < 0) goto abort;
- if (qemudInitCpus(conn, driver, vm, migrateFrom) < 0) + if (qemudInitCpuAffinity(conn, vm) < 0) goto abort;
if (qemuInitPasswords(driver, vm) < 0) @@ -2337,6 +2320,20 @@ static int qemudStartVMDaemon(virConnectPtr conn, } qemuDomainObjExitMonitorWithDriver(driver, vm);
Why do you exit the monitor here if you maybe just reenter it in the following if-block? I would just merge the two enter/exit blocks and put the final exit after the following if-block.
+ if (migrateFrom == NULL) { + /* Allow the CPUS to start executing */ + qemuDomainObjEnterMonitorWithDriver(driver, vm); + if (qemuMonitorStartCPUs(priv->mon, conn) < 0) { + if (virGetLastError() == NULL) + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("resume operation failed")); + qemuDomainObjExitMonitorWithDriver(driver, vm); + return -1;
goto abort instead of return -1.
+ } + qemuDomainObjExitMonitorWithDriver(driver, vm); + } + + if (virDomainSaveStatus(conn, driver->stateDir, vm) < 0) goto abort;
Matthias

On Thu, Dec 03, 2009 at 12:48:47AM +0100, Matthias Bolte wrote:
2009/11/26 Daniel P. Berrange <berrange@redhat.com>:
The code to start CPUs executing has nothing todo with CPU affinity masks, so pull it out of the qemudInitCpuAffinity() method and up into qemudStartVMDaemon()
* src/qemu/qemu_driver.c: Pull code to start CPUs executing out of qemudInitCpuAffinity() --- src/qemu/qemu_driver.c | 37 +++++++++++++++++-------------------- 1 files changed, 17 insertions(+), 20 deletions(-)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 2907976..b1f5894 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -1370,13 +1370,10 @@ qemuDetectVcpuPIDs(virConnectPtr conn, }
static int -qemudInitCpus(virConnectPtr conn, - struct qemud_driver *driver, - virDomainObjPtr vm, - const char *migrateFrom) { +qemudInitCpuAffinity(virConnectPtr conn, + virDomainObjPtr vm) { int i, hostcpus, maxcpu = QEMUD_CPUMASK_LEN; virNodeInfo nodeinfo; - qemuDomainObjPrivatePtr priv = vm->privateData; unsigned char *cpumap; int cpumaplen;
@@ -1421,20 +1418,6 @@ qemudInitCpus(virConnectPtr conn, } VIR_FREE(cpumap);
- /* XXX This resume doesn't really belong here. Move it up to caller */ - if (migrateFrom == NULL) { - /* Allow the CPUS to start executing */ - qemuDomainObjEnterMonitorWithDriver(driver, vm); - if (qemuMonitorStartCPUs(priv->mon, conn) < 0) { - if (virGetLastError() == NULL) - qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - "%s", _("resume operation failed")); - qemuDomainObjExitMonitorWithDriver(driver, vm); - return -1; - } - qemuDomainObjExitMonitorWithDriver(driver, vm); - } - return 0; }
@@ -2324,7 +2307,7 @@ static int qemudStartVMDaemon(virConnectPtr conn, if (qemuDetectVcpuPIDs(conn, driver, vm) < 0) goto abort;
- if (qemudInitCpus(conn, driver, vm, migrateFrom) < 0) + if (qemudInitCpuAffinity(conn, vm) < 0) goto abort;
if (qemuInitPasswords(driver, vm) < 0) @@ -2337,6 +2320,20 @@ static int qemudStartVMDaemon(virConnectPtr conn, } qemuDomainObjExitMonitorWithDriver(driver, vm);
Why do you exit the monitor here if you maybe just reenter it in the following if-block? I would just merge the two enter/exit blocks and put the final exit after the following if-block.
There's not really any particular reason for that - it just ended up that way after re-factoring, so we might as well merge it
+ if (migrateFrom == NULL) { + /* Allow the CPUS to start executing */ + qemuDomainObjEnterMonitorWithDriver(driver, vm); + if (qemuMonitorStartCPUs(priv->mon, conn) < 0) { + if (virGetLastError() == NULL) + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("resume operation failed")); + qemuDomainObjExitMonitorWithDriver(driver, vm); + return -1;
goto abort instead of return -1.
Ah yes :-)
+ } + qemuDomainObjExitMonitorWithDriver(driver, vm); + } + + if (virDomainSaveStatus(conn, driver->stateDir, vm) < 0) goto abort;
Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Thu, Dec 03, 2009 at 11:13:38AM +0000, Daniel P. Berrange wrote:
On Thu, Dec 03, 2009 at 12:48:47AM +0100, Matthias Bolte wrote:
2009/11/26 Daniel P. Berrange <berrange@redhat.com>:
The code to start CPUs executing has nothing todo with CPU affinity masks, so pull it out of the qemudInitCpuAffinity() method and up into qemudStartVMDaemon()
* src/qemu/qemu_driver.c: Pull code to start CPUs executing out of qemudInitCpuAffinity() --- src/qemu/qemu_driver.c | 37 +++++++++++++++++-------------------- 1 files changed, 17 insertions(+), 20 deletions(-)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 2907976..b1f5894 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -1370,13 +1370,10 @@ qemuDetectVcpuPIDs(virConnectPtr conn, }
static int -qemudInitCpus(virConnectPtr conn, - struct qemud_driver *driver, - virDomainObjPtr vm, - const char *migrateFrom) { +qemudInitCpuAffinity(virConnectPtr conn, + virDomainObjPtr vm) { int i, hostcpus, maxcpu = QEMUD_CPUMASK_LEN; virNodeInfo nodeinfo; - qemuDomainObjPrivatePtr priv = vm->privateData; unsigned char *cpumap; int cpumaplen;
@@ -1421,20 +1418,6 @@ qemudInitCpus(virConnectPtr conn, } VIR_FREE(cpumap);
- /* XXX This resume doesn't really belong here. Move it up to caller */ - if (migrateFrom == NULL) { - /* Allow the CPUS to start executing */ - qemuDomainObjEnterMonitorWithDriver(driver, vm); - if (qemuMonitorStartCPUs(priv->mon, conn) < 0) { - if (virGetLastError() == NULL) - qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - "%s", _("resume operation failed")); - qemuDomainObjExitMonitorWithDriver(driver, vm); - return -1; - } - qemuDomainObjExitMonitorWithDriver(driver, vm); - } - return 0; }
@@ -2324,7 +2307,7 @@ static int qemudStartVMDaemon(virConnectPtr conn, if (qemuDetectVcpuPIDs(conn, driver, vm) < 0) goto abort;
- if (qemudInitCpus(conn, driver, vm, migrateFrom) < 0) + if (qemudInitCpuAffinity(conn, vm) < 0) goto abort;
if (qemuInitPasswords(driver, vm) < 0) @@ -2337,6 +2320,20 @@ static int qemudStartVMDaemon(virConnectPtr conn, } qemuDomainObjExitMonitorWithDriver(driver, vm);
Why do you exit the monitor here if you maybe just reenter it in the following if-block? I would just merge the two enter/exit blocks and put the final exit after the following if-block.
There's not really any particular reason for that - it just ended up that way after re-factoring, so we might as well merge it
+ if (migrateFrom == NULL) { + /* Allow the CPUS to start executing */ + qemuDomainObjEnterMonitorWithDriver(driver, vm); + if (qemuMonitorStartCPUs(priv->mon, conn) < 0) { + if (virGetLastError() == NULL) + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("resume operation failed")); + qemuDomainObjExitMonitorWithDriver(driver, vm); + return -1;
goto abort instead of return -1.
Ah yes :-)
+ } + qemuDomainObjExitMonitorWithDriver(driver, vm); + } + + if (virDomainSaveStatus(conn, driver->stateDir, vm) < 0) goto abort;
ACK, makes sense once fixed :-) Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

* src/uml/uml_driver.c: Introduce umlDomainObjPrivateData for storing monitor device state. Remove unneccessary VIR_FREE on vcpupids --- src/uml/uml_driver.c | 58 +++++++++++++++++++++++++++++++++++++++---------- 1 files changed, 46 insertions(+), 12 deletions(-) diff --git a/src/uml/uml_driver.c b/src/uml/uml_driver.c index 7ef5a0b..48ef103 100644 --- a/src/uml/uml_driver.c +++ b/src/uml/uml_driver.c @@ -65,8 +65,37 @@ /* For storing short-lived temporary files. */ #define TEMPDIR LOCAL_STATE_DIR "/cache/libvirt" +typedef struct _umlDomainObjPrivate umlDomainObjPrivate; +typedef umlDomainObjPrivate *umlDomainObjPrivatePtr; +struct _umlDomainObjPrivate { + int monitor; + int monitorWatch; +}; + + static int umlShutdown(void); +static void *umlDomainObjPrivateAlloc(void) +{ + umlDomainObjPrivatePtr priv; + + if (VIR_ALLOC(priv) < 0) + return NULL; + + priv->monitor = -1; + priv->monitorWatch = -1; + + return priv; +} + +static void umlDomainObjPrivateFree(void *data) +{ + umlDomainObjPrivatePtr priv = data; + + VIR_FREE(priv); +} + + static void umlDriverLock(struct uml_driver *driver) { virMutexLock(&driver->lock); @@ -382,6 +411,8 @@ umlStartup(int privileged) { if ((uml_driver->caps = umlCapsInit()) == NULL) goto out_of_memory; + uml_driver->caps->privateDataAllocFunc = umlDomainObjPrivateAlloc; + uml_driver->caps->privateDataFreeFunc = umlDomainObjPrivateFree; if ((uml_driver->inotifyFD = inotify_init()) < 0) { VIR_ERROR0(_("cannot initialize inotify")); @@ -606,6 +637,7 @@ static int umlOpenMonitor(virConnectPtr conn, struct sockaddr_un addr; struct stat sb; int retries = 0; + umlDomainObjPrivatePtr priv = vm->privateData; if (umlMonitorAddress(conn, driver, vm, &addr) < 0) return -1; @@ -621,7 +653,7 @@ restat: return -1; } - if ((vm->monitor = socket(PF_UNIX, SOCK_DGRAM, 0)) < 0) { + if ((priv->monitor = socket(PF_UNIX, SOCK_DGRAM, 0)) < 0) { virReportSystemError(conn, errno, "%s", _("cannot open socket")); return -1; @@ -630,11 +662,11 @@ restat: memset(addr.sun_path, 0, sizeof addr.sun_path); sprintf(addr.sun_path + 1, "libvirt-uml-%u", vm->pid); VIR_DEBUG("Reply address for monitor is '%s'", addr.sun_path+1); - if (bind(vm->monitor, (struct sockaddr *)&addr, sizeof addr) < 0) { + if (bind(priv->monitor, (struct sockaddr *)&addr, sizeof addr) < 0) { virReportSystemError(conn, errno, "%s", _("cannot bind socket")); - close(vm->monitor); - vm->monitor = -1; + close(priv->monitor); + priv->monitor = -1; return -1; } @@ -673,6 +705,7 @@ static int umlMonitorCommand(virConnectPtr conn, int retlen = 0, ret = 0; struct sockaddr_un addr; unsigned int addrlen; + umlDomainObjPrivatePtr priv = vm->privateData; VIR_DEBUG("Run command '%s'", cmd); @@ -697,7 +730,7 @@ static int umlMonitorCommand(virConnectPtr conn, return -1; } - if (sendto(vm->monitor, &req, sizeof req, 0, + if (sendto(priv->monitor, &req, sizeof req, 0, (struct sockaddr *)&addr, sizeof addr) != (sizeof req)) { virReportSystemError(conn, errno, _("cannot send command %s"), @@ -707,7 +740,7 @@ static int umlMonitorCommand(virConnectPtr conn, do { addrlen = sizeof(addr); - if (recvfrom(vm->monitor, &res, sizeof res, 0, + if (recvfrom(priv->monitor, &res, sizeof res, 0, (struct sockaddr *)&addr, &addrlen) < 0) { virReportSystemError(conn, errno, _("cannot read reply %s"), @@ -781,6 +814,7 @@ static int umlStartVMDaemon(virConnectPtr conn, struct stat sb; fd_set keepfd; char ebuf[1024]; + umlDomainObjPrivatePtr priv = vm->privateData; FD_ZERO(&keepfd); @@ -867,7 +901,7 @@ static int umlStartVMDaemon(virConnectPtr conn, VIR_WARN(_("Unable to write argv to logfile: %s"), virStrerror(errno, ebuf, sizeof ebuf)); - vm->monitor = -1; + priv->monitor = -1; ret = virExecDaemonize(conn, argv, progenv, &keepfd, &pid, -1, &logfd, &logfd, @@ -900,14 +934,16 @@ static void umlShutdownVMDaemon(virConnectPtr conn ATTRIBUTE_UNUSED, virDomainObjPtr vm) { int ret; + umlDomainObjPrivatePtr priv = vm->privateData; + if (!virDomainObjIsActive(vm)) return; virKillProcess(vm->pid, SIGTERM); - if (vm->monitor != -1) - close(vm->monitor); - vm->monitor = -1; + if (priv->monitor != -1) + close(priv->monitor); + priv->monitor = -1; if ((ret = waitpid(vm->pid, NULL, 0)) != vm->pid) { VIR_WARN(_("Got unexpected pid %d != %d"), @@ -917,8 +953,6 @@ static void umlShutdownVMDaemon(virConnectPtr conn ATTRIBUTE_UNUSED, vm->pid = -1; vm->def->id = -1; vm->state = VIR_DOMAIN_SHUTOFF; - VIR_FREE(vm->vcpupids); - vm->nvcpupids = 0; umlCleanupTapDevices(conn, vm); -- 1.6.5.2

2009/11/26 Daniel P. Berrange <berrange@redhat.com>:
* src/uml/uml_driver.c: Introduce umlDomainObjPrivateData for storing monitor device state. Remove unneccessary VIR_FREE on vcpupids ---
ACK. Matthias

On Thu, Nov 26, 2009 at 06:27:26PM +0000, Daniel P. Berrange wrote:
* src/uml/uml_driver.c: Introduce umlDomainObjPrivateData for storing monitor device state. Remove unneccessary VIR_FREE on vcpupids --- src/uml/uml_driver.c | 58 +++++++++++++++++++++++++++++++++++++++---------- 1 files changed, 46 insertions(+), 12 deletions(-) [...] - if (vm->monitor != -1) - close(vm->monitor); - vm->monitor = -1; + if (priv->monitor != -1) + close(priv->monitor); + priv->monitor = -1;
Okay, it's never set by that driver.
if ((ret = waitpid(vm->pid, NULL, 0)) != vm->pid) { VIR_WARN(_("Got unexpected pid %d != %d"), @@ -917,8 +953,6 @@ static void umlShutdownVMDaemon(virConnectPtr conn ATTRIBUTE_UNUSED, vm->pid = -1; vm->def->id = -1; vm->state = VIR_DOMAIN_SHUTOFF; - VIR_FREE(vm->vcpupids); - vm->nvcpupids = 0;
umlCleanupTapDevices(conn, vm);
ACK Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

* src/lxc/lxc_driver.c: Introduce lxcDomainObjPrivate for storing monitor device state --- src/lxc/lxc_driver.c | 84 +++++++++++++++++++++++++++++++++++++++---------- 1 files changed, 67 insertions(+), 17 deletions(-) diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index 138c6fc..c8e2dca 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -53,6 +53,14 @@ #define VIR_FROM_THIS VIR_FROM_LXC +typedef struct _lxcDomainObjPrivate lxcDomainObjPrivate; +typedef lxcDomainObjPrivate *lxcDomainObjPrivatePtr; +struct _lxcDomainObjPrivate { + int monitor; + int monitorWatch; +}; + + static int lxcStartup(int privileged); static int lxcShutdown(void); static lxc_driver_t *lxc_driver = NULL; @@ -68,6 +76,27 @@ static void lxcDriverUnlock(lxc_driver_t *driver) virMutexUnlock(&driver->lock); } +static void *lxcDomainObjPrivateAlloc(void) +{ + lxcDomainObjPrivatePtr priv; + + if (VIR_ALLOC(priv) < 0) + return NULL; + + priv->monitor = -1; + priv->monitorWatch = -1; + + return priv; +} + +static void lxcDomainObjPrivateFree(void *data) +{ + lxcDomainObjPrivatePtr priv = data; + + VIR_FREE(priv); +} + + static void lxcDomainEventFlush(int timer, void *opaque); static void lxcDomainEventQueue(lxc_driver_t *driver, virDomainEventPtr event); @@ -671,6 +700,7 @@ static int lxcVmCleanup(virConnectPtr conn, int childStatus = -1; virCgroupPtr cgroup; int i; + lxcDomainObjPrivatePtr priv = vm->privateData; while (((waitRc = waitpid(vm->pid, &childStatus, 0)) == -1) && errno == EINTR) @@ -689,8 +719,8 @@ static int lxcVmCleanup(virConnectPtr conn, DEBUG("container exited with rc: %d", rc); } - virEventRemoveHandle(vm->monitorWatch); - close(vm->monitor); + virEventRemoveHandle(priv->monitorWatch); + close(priv->monitor); virFileDeletePid(driver->stateDir, vm->def->name); virDomainDeleteConfig(conn, driver->stateDir, NULL, vm); @@ -698,7 +728,8 @@ static int lxcVmCleanup(virConnectPtr conn, vm->state = VIR_DOMAIN_SHUTOFF; vm->pid = -1; vm->def->id = -1; - vm->monitor = -1; + priv->monitor = -1; + priv->monitorWatch = -1; for (i = 0 ; i < vm->def->nnets ; i++) { vethInterfaceUpOrDown(vm->def->nets[i]->ifname, 0); @@ -919,12 +950,15 @@ static void lxcMonitorEvent(int watch, lxc_driver_t *driver = lxc_driver; virDomainObjPtr vm = data; virDomainEventPtr event = NULL; + lxcDomainObjPrivatePtr priv; lxcDriverLock(driver); virDomainObjLock(vm); lxcDriverUnlock(driver); - if (vm->monitor != fd || vm->monitorWatch != watch) { + priv = vm->privateData; + + if (priv->monitor != fd || priv->monitorWatch != watch) { virEventRemoveHandle(watch); goto cleanup; } @@ -1157,6 +1191,7 @@ static int lxcVmStart(virConnectPtr conn, int logfd = -1; unsigned int nveths = 0; char **veths = NULL; + lxcDomainObjPrivatePtr priv = vm->privateData; if ((r = virFileMakePath(driver->logDir)) < 0) { virReportSystemError(conn, r, @@ -1209,7 +1244,7 @@ static int lxcVmStart(virConnectPtr conn, /* Connect to the controller as a client *first* because * this will block until the child has written their * pid file out to disk */ - if ((vm->monitor = lxcMonitorClient(conn, driver, vm)) < 0) + if ((priv->monitor = lxcMonitorClient(conn, driver, vm)) < 0) goto cleanup; /* And get its pid */ @@ -1223,8 +1258,8 @@ static int lxcVmStart(virConnectPtr conn, vm->def->id = vm->pid; vm->state = VIR_DOMAIN_RUNNING; - if ((vm->monitorWatch = virEventAddHandle( - vm->monitor, + if ((priv->monitorWatch = virEventAddHandle( + priv->monitor, VIR_EVENT_HANDLE_ERROR | VIR_EVENT_HANDLE_HANGUP, lxcMonitorEvent, vm, NULL)) < 0) { @@ -1240,9 +1275,9 @@ cleanup: vethDelete(veths[i]); VIR_FREE(veths[i]); } - if (rc != 0 && vm->monitor != -1) { - close(vm->monitor); - vm->monitor = -1; + if (rc != 0 && priv->monitor != -1) { + close(priv->monitor); + priv->monitor = -1; } if (parentTty != -1) close(parentTty); @@ -1610,16 +1645,19 @@ lxcReconnectVM(void *payload, const char *name ATTRIBUTE_UNUSED, void *opaque) lxc_driver_t *driver = opaque; char *config = NULL; virDomainDefPtr tmp; + lxcDomainObjPrivatePtr priv; virDomainObjLock(vm); - if ((vm->monitor = lxcMonitorClient(NULL, driver, vm)) < 0) { + + priv = vm->privateData; + if ((priv->monitor = lxcMonitorClient(NULL, driver, vm)) < 0) { goto cleanup; } /* Read pid from controller */ if ((virFileReadPid(lxc_driver->stateDir, vm->def->name, &vm->pid)) != 0) { - close(vm->monitor); - vm->monitor = -1; + close(priv->monitor); + priv->monitor = -1; goto cleanup; } @@ -1639,10 +1677,19 @@ lxcReconnectVM(void *payload, const char *name ATTRIBUTE_UNUSED, void *opaque) if (vm->pid != 0) { vm->def->id = vm->pid; vm->state = VIR_DOMAIN_RUNNING; + + if ((priv->monitorWatch = virEventAddHandle( + priv->monitor, + VIR_EVENT_HANDLE_ERROR | VIR_EVENT_HANDLE_HANGUP, + lxcMonitorEvent, + vm, NULL)) < 0) { + lxcVmTerminate(NULL, driver, vm, 0); + goto cleanup; + } } else { vm->def->id = -1; - close(vm->monitor); - vm->monitor = -1; + close(priv->monitor); + priv->monitor = -1; } cleanup: @@ -1715,6 +1762,9 @@ static int lxcStartup(int privileged) if ((lxc_driver->caps = lxcCapsInit()) == NULL) goto cleanup; + lxc_driver->caps->privateDataAllocFunc = lxcDomainObjPrivateAlloc; + lxc_driver->caps->privateDataFreeFunc = lxcDomainObjPrivateFree; + if (virDomainLoadAllConfigs(NULL, lxc_driver->caps, &lxc_driver->domains, @@ -2250,7 +2300,7 @@ static int lxcDomainSuspend(virDomainPtr dom) VIR_DOMAIN_EVENT_SUSPENDED_PAUSED); } - if (virDomainSaveStatus(dom->conn, driver->stateDir, vm) < 0) + if (virDomainSaveStatus(dom->conn, driver->caps, driver->stateDir, vm) < 0) goto cleanup; ret = 0; @@ -2315,7 +2365,7 @@ static int lxcDomainResume(virDomainPtr dom) VIR_DOMAIN_EVENT_RESUMED_UNPAUSED); } - if (virDomainSaveStatus(dom->conn, driver->stateDir, vm) < 0) + if (virDomainSaveStatus(dom->conn, driver->caps, driver->stateDir, vm) < 0) goto cleanup; ret = 0; -- 1.6.5.2

2009/11/26 Daniel P. Berrange <berrange@redhat.com>:
* src/lxc/lxc_driver.c: Introduce lxcDomainObjPrivate for storing monitor device state --- src/lxc/lxc_driver.c | 84 +++++++++++++++++++++++++++++++++++++++---------- 1 files changed, 67 insertions(+), 17 deletions(-)
diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index 138c6fc..c8e2dca 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c
[...]
@@ -2250,7 +2300,7 @@ static int lxcDomainSuspend(virDomainPtr dom) VIR_DOMAIN_EVENT_SUSPENDED_PAUSED); }
- if (virDomainSaveStatus(dom->conn, driver->stateDir, vm) < 0) + if (virDomainSaveStatus(dom->conn, driver->caps, driver->stateDir, vm) < 0) goto cleanup; ret = 0;
@@ -2315,7 +2365,7 @@ static int lxcDomainResume(virDomainPtr dom) VIR_DOMAIN_EVENT_RESUMED_UNPAUSED); }
- if (virDomainSaveStatus(dom->conn, driver->stateDir, vm) < 0) + if (virDomainSaveStatus(dom->conn, driver->caps, driver->stateDir, vm) < 0) goto cleanup; ret = 0;
The virDomainSaveStatus change depends on the next patch. ACK. Matthias

On Thu, Nov 26, 2009 at 06:27:27PM +0000, Daniel P. Berrange wrote:
* src/lxc/lxc_driver.c: Introduce lxcDomainObjPrivate for storing monitor device state --- src/lxc/lxc_driver.c | 84 +++++++++++++++++++++++++++++++++++++++---------- 1 files changed, 67 insertions(+), 17 deletions(-)
Okay, that's more uniform that way, ACK, Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

Now that drivers are using a private domain object state blob, the virDomainObjFormat/Parse methods are no longer able to directly serialize all neccessary state to/from XML. It is thus neccessary to introduce a pair of callbacks fo serializing private state. The code for serializing vCPU PIDs and the monitor device config can now move out of domain_conf.c and into the qemu_driver.c where they belong. * src/conf/capabilities.h: Add callbacks for serializing private state to/from XML * src/conf/domain_conf.c, src/conf/domain_conf.h: Remove the monitor, monitor_chr, monitorWatch, nvcpupids and vcpupids fields from virDomainObjPtr. Remove code that serialized those fields * src/libvirt_private.syms: Export virXPathBoolean * src/qemu/qemu_driver.c: Add callbacks for serializing monitor and vcpupid data to/from XML * src/qemu/qemu_monitor.h, src/qemu/qemu_monitor.c: Pass monitor char device config into qemuMonitorOpen directly. --- src/conf/capabilities.h | 5 + src/conf/domain_conf.c | 103 ++-------------------- src/conf/domain_conf.h | 8 +-- src/libvirt_private.syms | 1 + src/qemu/qemu_driver.c | 217 ++++++++++++++++++++++++++++++++++++--------- src/qemu/qemu_monitor.c | 9 +- src/qemu/qemu_monitor.h | 1 + 7 files changed, 195 insertions(+), 149 deletions(-) diff --git a/src/conf/capabilities.h b/src/conf/capabilities.h index 7234cf4..f8cbae6 100644 --- a/src/conf/capabilities.h +++ b/src/conf/capabilities.h @@ -26,6 +26,9 @@ #include "internal.h" #include "util.h" +#include "buf.h" + +#include <libxml/xpath.h> typedef struct _virCapsGuestFeature virCapsGuestFeature; typedef virCapsGuestFeature *virCapsGuestFeaturePtr; @@ -117,6 +120,8 @@ struct _virCaps { unsigned int emulatorRequired : 1; void *(*privateDataAllocFunc)(void); void (*privateDataFreeFunc)(void *); + int (*privateDataXMLFormat)(virBufferPtr, void *); + int (*privateDataXMLParse)(xmlXPathContextPtr, void *); }; diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 42820a7..e9f79b7 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -615,10 +615,6 @@ static void virDomainObjFree(virDomainObjPtr dom) virDomainDefFree(dom->def); virDomainDefFree(dom->newDef); - virDomainChrDefFree(dom->monitor_chr); - - VIR_FREE(dom->vcpupids); - if (dom->privateDataFreeFunc) (dom->privateDataFreeFunc)(dom->privateData); @@ -675,8 +671,6 @@ static virDomainObjPtr virDomainObjNew(virConnectPtr conn, virDomainObjLock(domain); domain->state = VIR_DOMAIN_SHUTOFF; - domain->monitorWatch = -1; - domain->monitor = -1; domain->refs = 1; VIR_DEBUG("obj=%p", domain); @@ -3389,9 +3383,6 @@ static virDomainObjPtr virDomainObjParseXML(virConnectPtr conn, xmlNodePtr config; xmlNodePtr oldnode; virDomainObjPtr obj; - char *monitorpath; - xmlNodePtr *nodes = NULL; - int n, i; if (!(obj = virDomainObjNew(conn, caps))) return NULL; @@ -3430,69 +3421,13 @@ static virDomainObjPtr virDomainObjParseXML(virConnectPtr conn, } obj->pid = (pid_t)val; - if (VIR_ALLOC(obj->monitor_chr) < 0) { - virReportOOMError(conn); - goto error; - } - - if (!(monitorpath = - virXPathString(conn, "string(./monitor[1]/@path)", ctxt))) { - virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("no monitor path")); + if (caps->privateDataXMLParse && + ((caps->privateDataXMLParse)(ctxt, obj->privateData)) < 0) goto error; - } - - tmp = virXPathString(conn, "string(./monitor[1]/@type)", ctxt); - if (tmp) - obj->monitor_chr->type = virDomainChrTypeFromString(tmp); - else - obj->monitor_chr->type = VIR_DOMAIN_CHR_TYPE_PTY; - VIR_FREE(tmp); - - switch (obj->monitor_chr->type) { - case VIR_DOMAIN_CHR_TYPE_PTY: - obj->monitor_chr->data.file.path = monitorpath; - break; - case VIR_DOMAIN_CHR_TYPE_UNIX: - obj->monitor_chr->data.nix.path = monitorpath; - break; - default: - VIR_FREE(monitorpath); - virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("unsupported monitor type '%s'"), - virDomainChrTypeToString(obj->monitor_chr->type)); - break; - } - - n = virXPathNodeSet(conn, "./vcpus/vcpu", ctxt, &nodes); - if (n < 0) - goto error; - if (n) { - obj->nvcpupids = n; - if (VIR_REALLOC_N(obj->vcpupids, obj->nvcpupids) < 0) { - virReportOOMError(conn); - goto error; - } - - for (i = 0 ; i < n ; i++) { - char *pidstr = virXMLPropString(nodes[i], "pid"); - if (!pidstr) - goto error; - - if (virStrToLong_i(pidstr, NULL, 10, &(obj->vcpupids[i])) < 0) { - VIR_FREE(pidstr); - goto error; - } - VIR_FREE(pidstr); - } - VIR_FREE(nodes); - } return obj; error: - VIR_FREE(nodes); - virDomainChrDefFree(obj->monitor_chr); virDomainObjUnref(obj); return NULL; } @@ -4858,43 +4793,20 @@ char *virDomainDefFormat(virConnectPtr conn, } char *virDomainObjFormat(virConnectPtr conn, + virCapsPtr caps, virDomainObjPtr obj, int flags) { char *config_xml = NULL, *xml = NULL; virBuffer buf = VIR_BUFFER_INITIALIZER; - const char *monitorpath; virBufferVSprintf(&buf, "<domstatus state='%s' pid='%d'>\n", virDomainStateTypeToString(obj->state), obj->pid); - /* obj->monitor_chr is set only for qemu */ - if (obj->monitor_chr) { - switch (obj->monitor_chr->type) { - case VIR_DOMAIN_CHR_TYPE_UNIX: - monitorpath = obj->monitor_chr->data.nix.path; - break; - default: - case VIR_DOMAIN_CHR_TYPE_PTY: - monitorpath = obj->monitor_chr->data.file.path; - break; - } - - virBufferEscapeString(&buf, " <monitor path='%s'", monitorpath); - virBufferVSprintf(&buf, " type='%s'/>\n", - virDomainChrTypeToString(obj->monitor_chr->type)); - } - - - if (obj->nvcpupids) { - int i; - virBufferAddLit(&buf, " <vcpus>\n"); - for (i = 0 ; i < obj->nvcpupids ; i++) { - virBufferVSprintf(&buf, " <vcpu pid='%d'/>\n", obj->vcpupids[i]); - } - virBufferAddLit(&buf, " </vcpus>\n"); - } + if (caps->privateDataXMLFormat && + ((caps->privateDataXMLFormat)(&buf, obj->privateData)) < 0) + goto error; if (!(config_xml = virDomainDefFormat(conn, obj->def, @@ -4994,6 +4906,7 @@ cleanup: } int virDomainSaveStatus(virConnectPtr conn, + virCapsPtr caps, const char *statusDir, virDomainObjPtr obj) { @@ -5001,7 +4914,7 @@ int virDomainSaveStatus(virConnectPtr conn, int ret = -1; char *xml; - if (!(xml = virDomainObjFormat(conn, obj, flags))) + if (!(xml = virDomainObjFormat(conn, caps, obj, flags))) goto cleanup; if (virDomainSaveXML(conn, statusDir, obj->def, xml)) diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 6201463..ac39dcd 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -644,15 +644,9 @@ struct _virDomainObj { virMutex lock; int refs; - int monitor; - virDomainChrDefPtr monitor_chr; - int monitorWatch; int pid; int state; - int nvcpupids; - int *vcpupids; - unsigned int autostart : 1; unsigned int persistent : 1; @@ -744,6 +738,7 @@ char *virDomainDefFormat(virConnectPtr conn, virDomainDefPtr def, int flags); char *virDomainObjFormat(virConnectPtr conn, + virCapsPtr caps, virDomainObjPtr obj, int flags); @@ -770,6 +765,7 @@ int virDomainSaveConfig(virConnectPtr conn, const char *configDir, virDomainDefPtr def); int virDomainSaveStatus(virConnectPtr conn, + virCapsPtr caps, const char *statusDir, virDomainObjPtr obj); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index ee7f3ed..09628ba 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -583,6 +583,7 @@ virStrerror; # xml.h +virXPathBoolean; virXPathLong; virXPathNode; virXPathNodeSet; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index b1f5894..468e0ab 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -72,6 +72,7 @@ #include "security/security_driver.h" #include "cgroup.h" #include "libvirt_internal.h" +#include "xml.h" #define VIR_FROM_THIS VIR_FROM_QEMU @@ -85,6 +86,10 @@ struct _qemuDomainObjPrivate { * information, not merely actions */ qemuMonitorPtr mon; + virDomainChrDefPtr monConfig; + + int nvcpupids; + int *vcpupids; }; static int qemudShutdown(void); @@ -138,6 +143,9 @@ static void qemuDomainObjPrivateFree(void *data) { qemuDomainObjPrivatePtr priv = data; + virDomainChrDefFree(priv->monConfig); + VIR_FREE(priv->vcpupids); + /* This should never be non-NULL if we get here, but just in case... */ if (priv->mon) { VIR_ERROR0("Unexpected QEMU monitor still active during domain deletion"); @@ -147,6 +155,116 @@ static void qemuDomainObjPrivateFree(void *data) } +static int qemuDomainObjPrivateXMLFormat(virBufferPtr buf, void *data) +{ + qemuDomainObjPrivatePtr priv = data; + const char *monitorpath; + + /* priv->monitor_chr is set only for qemu */ + if (priv->monConfig) { + switch (priv->monConfig->type) { + case VIR_DOMAIN_CHR_TYPE_UNIX: + monitorpath = priv->monConfig->data.nix.path; + break; + default: + case VIR_DOMAIN_CHR_TYPE_PTY: + monitorpath = priv->monConfig->data.file.path; + break; + } + + virBufferEscapeString(buf, " <monitor path='%s'", monitorpath); + virBufferVSprintf(buf, " type='%s'/>\n", + virDomainChrTypeToString(priv->monConfig->type)); + } + + + if (priv->nvcpupids) { + int i; + virBufferAddLit(buf, " <vcpus>\n"); + for (i = 0 ; i < priv->nvcpupids ; i++) { + virBufferVSprintf(buf, " <vcpu pid='%d'/>\n", priv->vcpupids[i]); + } + virBufferAddLit(buf, " </vcpus>\n"); + } + + return 0; +} + +static int qemuDomainObjPrivateXMLParse(xmlXPathContextPtr ctxt, void *data) +{ + qemuDomainObjPrivatePtr priv = data; + char *monitorpath; + char *tmp; + int n, i; + xmlNodePtr *nodes = NULL; + + if (VIR_ALLOC(priv->monConfig) < 0) { + virReportOOMError(NULL); + goto error; + } + + if (!(monitorpath = + virXPathString(NULL, "string(./monitor[1]/@path)", ctxt))) { + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("no monitor path")); + goto error; + } + + tmp = virXPathString(NULL, "string(./monitor[1]/@type)", ctxt); + if (tmp) + priv->monConfig->type = virDomainChrTypeFromString(tmp); + else + priv->monConfig->type = VIR_DOMAIN_CHR_TYPE_PTY; + VIR_FREE(tmp); + + switch (priv->monConfig->type) { + case VIR_DOMAIN_CHR_TYPE_PTY: + priv->monConfig->data.file.path = monitorpath; + break; + case VIR_DOMAIN_CHR_TYPE_UNIX: + priv->monConfig->data.nix.path = monitorpath; + break; + default: + VIR_FREE(monitorpath); + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("unsupported monitor type '%s'"), + virDomainChrTypeToString(priv->monConfig->type)); + goto error; + } + + n = virXPathNodeSet(NULL, "./vcpus/vcpu", ctxt, &nodes); + if (n < 0) + goto error; + if (n) { + priv->nvcpupids = n; + if (VIR_REALLOC_N(priv->vcpupids, priv->nvcpupids) < 0) { + virReportOOMError(NULL); + goto error; + } + + for (i = 0 ; i < n ; i++) { + char *pidstr = virXMLPropString(nodes[i], "pid"); + if (!pidstr) + goto error; + + if (virStrToLong_i(pidstr, NULL, 10, &(priv->vcpupids[i])) < 0) { + VIR_FREE(pidstr); + goto error; + } + VIR_FREE(pidstr); + } + VIR_FREE(nodes); + } + + return 0; + +error: + VIR_FREE(nodes); + return -1; +} + + + /* * obj must be locked before calling, qemud_driver must NOT be locked * @@ -660,7 +778,9 @@ qemuConnectMonitor(virDomainObjPtr vm) { qemuDomainObjPrivatePtr priv = vm->privateData; - if ((priv->mon = qemuMonitorOpen(vm, qemuHandleMonitorEOF)) == NULL) { + if ((priv->mon = qemuMonitorOpen(vm, + priv->monConfig, + qemuHandleMonitorEOF)) == NULL) { VIR_ERROR(_("Failed to connect monitor for %s\n"), vm->def->name); return -1; } @@ -917,6 +1037,8 @@ qemudStartup(int privileged) { qemu_driver->caps->privateDataAllocFunc = qemuDomainObjPrivateAlloc; qemu_driver->caps->privateDataFreeFunc = qemuDomainObjPrivateFree; + qemu_driver->caps->privateDataXMLFormat = qemuDomainObjPrivateXMLFormat; + qemu_driver->caps->privateDataXMLParse = qemuDomainObjPrivateXMLParse; if ((qemu_driver->activePciHostdevs = pciDeviceListNew(NULL)) == NULL) goto error; @@ -1334,12 +1456,12 @@ qemuDetectVcpuPIDs(virConnectPtr conn, qemuDomainObjPrivatePtr priv = vm->privateData; if (vm->def->virtType != VIR_DOMAIN_VIRT_KVM) { - vm->nvcpupids = 1; - if (VIR_ALLOC_N(vm->vcpupids, vm->nvcpupids) < 0) { + priv->nvcpupids = 1; + if (VIR_ALLOC_N(priv->vcpupids, priv->nvcpupids) < 0) { virReportOOMError(conn); return -1; } - vm->vcpupids[0] = vm->pid; + priv->vcpupids[0] = vm->pid; return 0; } @@ -1364,8 +1486,8 @@ qemuDetectVcpuPIDs(virConnectPtr conn, return -1; } - vm->nvcpupids = ncpupids; - vm->vcpupids = cpupids; + priv->nvcpupids = ncpupids; + priv->vcpupids = cpupids; return 0; } @@ -1376,6 +1498,7 @@ qemudInitCpuAffinity(virConnectPtr conn, virNodeInfo nodeinfo; unsigned char *cpumap; int cpumaplen; + qemuDomainObjPrivatePtr priv = vm->privateData; if (nodeGetInfo(conn, &nodeinfo) < 0) return -1; @@ -1409,8 +1532,8 @@ qemudInitCpuAffinity(virConnectPtr conn, /* The XML config only gives a per-VM affinity, so we apply * the same mapping to all vCPUs */ - for (i = 0 ; i < vm->nvcpupids ; i++) { - if (virProcessInfoSetAffinity(vm->vcpupids[i], + for (i = 0 ; i < priv->nvcpupids ; i++) { + if (virProcessInfoSetAffinity(priv->vcpupids[i], cpumap, cpumaplen, maxcpu) < 0) { VIR_FREE(cpumap); return -1; @@ -2089,15 +2212,15 @@ static int qemudSecurityHook(void *data) { static int qemuPrepareMonitorChr(virConnectPtr conn, struct qemud_driver *driver, - virDomainChrDefPtr monitor_chr, + virDomainChrDefPtr monConfig, const char *vm) { - monitor_chr->targetType = VIR_DOMAIN_CHR_TARGET_TYPE_MONITOR; + monConfig->targetType = VIR_DOMAIN_CHR_TARGET_TYPE_MONITOR; - monitor_chr->type = VIR_DOMAIN_CHR_TYPE_UNIX; - monitor_chr->data.nix.listen = 1; + monConfig->type = VIR_DOMAIN_CHR_TYPE_UNIX; + monConfig->data.nix.listen = 1; - if (virAsprintf(&monitor_chr->data.nix.path, "%s/%s.monitor", + if (virAsprintf(&monConfig->data.nix.path, "%s/%s.monitor", driver->libDir, vm) < 0) { virReportOOMError(conn); return -1; @@ -2197,12 +2320,12 @@ static int qemudStartVMDaemon(virConnectPtr conn, if (qemuPrepareHostDevices(conn, driver, vm->def) < 0) goto cleanup; - if (VIR_ALLOC(vm->monitor_chr) < 0) { + if (VIR_ALLOC(priv->monConfig) < 0) { virReportOOMError(conn); goto cleanup; } - if (qemuPrepareMonitorChr(conn, driver, vm->monitor_chr, vm->def->name) < 0) + if (qemuPrepareMonitorChr(conn, driver, priv->monConfig, vm->def->name) < 0) goto cleanup; if ((ret = virFileDeletePid(driver->stateDir, vm->def->name)) != 0) { @@ -2219,7 +2342,7 @@ static int qemudStartVMDaemon(virConnectPtr conn, } vm->def->id = driver->nextvmid++; - if (qemudBuildCommandLine(conn, driver, vm->def, vm->monitor_chr, + if (qemudBuildCommandLine(conn, driver, vm->def, priv->monConfig, qemuCmdFlags, &argv, &progenv, &tapfds, &ntapfds, migrateFrom) < 0) goto cleanup; @@ -2334,7 +2457,7 @@ static int qemudStartVMDaemon(virConnectPtr conn, } - if (virDomainSaveStatus(conn, driver->stateDir, vm) < 0) + if (virDomainSaveStatus(conn, driver->caps, driver->stateDir, vm) < 0) goto abort; return 0; @@ -2408,11 +2531,11 @@ static void qemudShutdownVMDaemon(virConnectPtr conn, qemuMonitorClose(priv->mon) == 0) priv->mon = NULL; - if (vm->monitor_chr) { - if (vm->monitor_chr->type == VIR_DOMAIN_CHR_TYPE_UNIX) - unlink(vm->monitor_chr->data.nix.path); - virDomainChrDefFree(vm->monitor_chr); - vm->monitor_chr = NULL; + if (priv->monConfig) { + if (priv->monConfig->type == VIR_DOMAIN_CHR_TYPE_UNIX) + unlink(priv->monConfig->data.nix.path); + virDomainChrDefFree(priv->monConfig); + priv->monConfig = NULL; } /* shut it off for sure */ @@ -2450,8 +2573,8 @@ retry: vm->pid = -1; vm->def->id = -1; vm->state = VIR_DOMAIN_SHUTOFF; - VIR_FREE(vm->vcpupids); - vm->nvcpupids = 0; + VIR_FREE(priv->vcpupids); + priv->nvcpupids = 0; if (vm->newDef) { virDomainDefFree(vm->def); @@ -2949,7 +3072,7 @@ static int qemudDomainSuspend(virDomainPtr dom) { VIR_DOMAIN_EVENT_SUSPENDED, VIR_DOMAIN_EVENT_SUSPENDED_PAUSED); } - if (virDomainSaveStatus(dom->conn, driver->stateDir, vm) < 0) + if (virDomainSaveStatus(dom->conn, driver->caps, driver->stateDir, vm) < 0) goto endjob; ret = 0; @@ -3008,7 +3131,7 @@ static int qemudDomainResume(virDomainPtr dom) { VIR_DOMAIN_EVENT_RESUMED, VIR_DOMAIN_EVENT_RESUMED_UNPAUSED); } - if (virDomainSaveStatus(dom->conn, driver->stateDir, vm) < 0) + if (virDomainSaveStatus(dom->conn, driver->caps, driver->stateDir, vm) < 0) goto endjob; ret = 0; @@ -3690,6 +3813,7 @@ qemudDomainPinVcpu(virDomainPtr dom, int maxcpu, hostcpus; virNodeInfo nodeinfo; int ret = -1; + qemuDomainObjPrivatePtr priv; qemuDriverLock(driver); vm = virDomainFindByUUID(&driver->domains, dom->uuid); @@ -3709,10 +3833,12 @@ qemudDomainPinVcpu(virDomainPtr dom, goto cleanup; } - if (vcpu > (vm->nvcpupids-1)) { + priv = vm->privateData; + + if (vcpu > (priv->nvcpupids-1)) { qemudReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_ARG, _("vcpu number out of range %d > %d"), - vcpu, vm->nvcpupids); + vcpu, priv->nvcpupids); goto cleanup; } @@ -3724,8 +3850,8 @@ qemudDomainPinVcpu(virDomainPtr dom, if (maxcpu > hostcpus) maxcpu = hostcpus; - if (vm->vcpupids != NULL) { - if (virProcessInfoSetAffinity(vm->vcpupids[vcpu], + if (priv->vcpupids != NULL) { + if (virProcessInfoSetAffinity(priv->vcpupids[vcpu], cpumap, maplen, maxcpu) < 0) goto cleanup; } else { @@ -3752,6 +3878,7 @@ qemudDomainGetVcpus(virDomainPtr dom, virNodeInfo nodeinfo; int i, v, maxcpu, hostcpus; int ret = -1; + qemuDomainObjPrivatePtr priv; qemuDriverLock(driver); vm = virDomainFindByUUID(&driver->domains, dom->uuid); @@ -3772,6 +3899,8 @@ qemudDomainGetVcpus(virDomainPtr dom, goto cleanup; } + priv = vm->privateData; + if (nodeGetInfo(dom->conn, &nodeinfo) < 0) goto cleanup; @@ -3781,8 +3910,8 @@ qemudDomainGetVcpus(virDomainPtr dom, maxcpu = hostcpus; /* Clamp to actual number of vcpus */ - if (maxinfo > vm->nvcpupids) - maxinfo = vm->nvcpupids; + if (maxinfo > priv->nvcpupids) + maxinfo = priv->nvcpupids; if (maxinfo >= 1) { if (info != NULL) { @@ -3791,11 +3920,11 @@ qemudDomainGetVcpus(virDomainPtr dom, info[i].number = i; info[i].state = VIR_VCPU_RUNNING; - if (vm->vcpupids != NULL && + if (priv->vcpupids != NULL && qemudGetProcessInfo(&(info[i].cpuTime), &(info[i].cpu), vm->pid, - vm->vcpupids[i]) < 0) { + priv->vcpupids[i]) < 0) { virReportSystemError(dom->conn, errno, "%s", _("cannot get vCPU placement & pCPU time")); goto cleanup; @@ -3805,11 +3934,11 @@ qemudDomainGetVcpus(virDomainPtr dom, if (cpumaps != NULL) { memset(cpumaps, 0, maplen * maxinfo); - if (vm->vcpupids != NULL) { + if (priv->vcpupids != NULL) { for (v = 0 ; v < maxinfo ; v++) { unsigned char *cpumap = VIR_GET_CPUMAP(cpumaps, maplen, v); - if (virProcessInfoGetAffinity(vm->vcpupids[v], + if (virProcessInfoGetAffinity(priv->vcpupids[v], cpumap, maplen, maxcpu) < 0) goto cleanup; } @@ -4098,7 +4227,7 @@ static int qemudDomainRestore(virConnectPtr conn, } qemuDomainObjExitMonitorWithDriver(driver, vm); vm->state = VIR_DOMAIN_RUNNING; - virDomainSaveStatus(conn, driver->stateDir, vm); + virDomainSaveStatus(conn, driver->caps, driver->stateDir, vm); } ret = 0; @@ -4206,7 +4335,7 @@ static char *qemuDomainXMLToNative(virConnectPtr conn, unsigned int flags ATTRIBUTE_UNUSED) { struct qemud_driver *driver = conn->privateData; virDomainDefPtr def = NULL; - virDomainChrDef monitor_chr; + virDomainChrDef monConfig; const char *emulator; unsigned int qemuCmdFlags; struct stat sb; @@ -4285,11 +4414,11 @@ static char *qemuDomainXMLToNative(virConnectPtr conn, goto cleanup; } - if (qemuPrepareMonitorChr(conn, driver, &monitor_chr, def->name) < 0) + if (qemuPrepareMonitorChr(conn, driver, &monConfig, def->name) < 0) goto cleanup; if (qemudBuildCommandLine(conn, driver, def, - &monitor_chr, qemuCmdFlags, + &monConfig, qemuCmdFlags, &retargv, &retenv, NULL, NULL, /* Don't want it to create TAP devices */ NULL) < 0) { @@ -4834,7 +4963,7 @@ static int qemudDomainAttachNetDevice(virConnectPtr conn, if (net->type == VIR_DOMAIN_NET_TYPE_BRIDGE || net->type == VIR_DOMAIN_NET_TYPE_NETWORK) { - if (vm->monitor_chr->type != VIR_DOMAIN_CHR_TYPE_UNIX) { + if (priv->monConfig->type != VIR_DOMAIN_CHR_TYPE_UNIX) { qemudReportError(conn, dom, NULL, VIR_ERR_NO_SUPPORT, _("network device type '%s' cannot be attached: " "qemu is not using a unix socket monitor"), @@ -5172,7 +5301,7 @@ static int qemudDomainAttachDevice(virDomainPtr dom, goto endjob; } - if (!ret && virDomainSaveStatus(dom->conn, driver->stateDir, vm) < 0) + if (!ret && virDomainSaveStatus(dom->conn, driver->caps, driver->stateDir, vm) < 0) ret = -1; endjob: @@ -5506,7 +5635,7 @@ static int qemudDomainDetachDevice(virDomainPtr dom, qemudReportError(dom->conn, dom, NULL, VIR_ERR_NO_SUPPORT, "%s", _("only SCSI or virtio disk device can be detached dynamically")); - if (!ret && virDomainSaveStatus(dom->conn, driver->stateDir, vm) < 0) + if (!ret && virDomainSaveStatus(dom->conn, driver->caps, driver->stateDir, vm) < 0) ret = -1; endjob: @@ -7354,7 +7483,7 @@ qemudDomainMigrateFinish2 (virConnectPtr dconn, event = virDomainEventNewFromObj(vm, VIR_DOMAIN_EVENT_RESUMED, VIR_DOMAIN_EVENT_RESUMED_MIGRATED); - virDomainSaveStatus(dconn, driver->stateDir, vm); + virDomainSaveStatus(dconn, driver->caps, driver->stateDir, vm); } else { qemudShutdownVMDaemon (dconn, driver, vm); event = virDomainEventNewFromObj(vm, diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index a6b445e..6c4dfde 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -533,6 +533,7 @@ qemuMonitorIO(int watch, int fd, int events, void *opaque) { qemuMonitorPtr qemuMonitorOpen(virDomainObjPtr vm, + virDomainChrDefPtr config, qemuMonitorEOFNotify eofCB) { qemuMonitorPtr mon; @@ -562,20 +563,20 @@ qemuMonitorOpen(virDomainObjPtr vm, qemuMonitorLock(mon); virDomainObjRef(vm); - switch (vm->monitor_chr->type) { + switch (config->type) { case VIR_DOMAIN_CHR_TYPE_UNIX: mon->hasSendFD = 1; - mon->fd = qemuMonitorOpenUnix(vm->monitor_chr->data.nix.path); + mon->fd = qemuMonitorOpenUnix(config->data.nix.path); break; case VIR_DOMAIN_CHR_TYPE_PTY: - mon->fd = qemuMonitorOpenPty(vm->monitor_chr->data.file.path); + mon->fd = qemuMonitorOpenPty(config->data.file.path); break; default: qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, _("unable to handle monitor type: %s"), - virDomainChrTypeToString(vm->monitor_chr->type)); + virDomainChrTypeToString(config->type)); goto cleanup; } diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 14198fb..5f1a65d 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -79,6 +79,7 @@ char *qemuMonitorEscapeArg(const char *in); char *qemuMonitorEscapeShell(const char *in); qemuMonitorPtr qemuMonitorOpen(virDomainObjPtr vm, + virDomainChrDefPtr config, qemuMonitorEOFNotify eofCB); int qemuMonitorClose(qemuMonitorPtr mon); -- 1.6.5.2

2009/11/26 Daniel P. Berrange <berrange@redhat.com>:
Now that drivers are using a private domain object state blob, the virDomainObjFormat/Parse methods are no longer able to directly serialize all neccessary state to/from XML. It is thus neccessary to introduce a pair of callbacks fo serializing private state.
The code for serializing vCPU PIDs and the monitor device config can now move out of domain_conf.c and into the qemu_driver.c where they belong.
* src/conf/capabilities.h: Add callbacks for serializing private state to/from XML * src/conf/domain_conf.c, src/conf/domain_conf.h: Remove the monitor, monitor_chr, monitorWatch, nvcpupids and vcpupids fields from virDomainObjPtr. Remove code that serialized those fields * src/libvirt_private.syms: Export virXPathBoolean * src/qemu/qemu_driver.c: Add callbacks for serializing monitor and vcpupid data to/from XML * src/qemu/qemu_monitor.h, src/qemu/qemu_monitor.c: Pass monitor char device config into qemuMonitorOpen directly. ---
ACK. Matthias

On Thu, Nov 26, 2009 at 06:27:28PM +0000, Daniel P. Berrange wrote:
Now that drivers are using a private domain object state blob, the virDomainObjFormat/Parse methods are no longer able to directly serialize all neccessary state to/from XML. It is thus neccessary to introduce a pair of callbacks fo serializing private state.
The code for serializing vCPU PIDs and the monitor device config can now move out of domain_conf.c and into the qemu_driver.c where they belong.
Okay, ACK, Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

Initial support for the new QEMU monitor protocol using JSON as the data encoding format instead of plain text * po/POTFILES.in: Add src/qemu/qemu_monitor_json.c * src/qemu/qemu_conf.c, src/qemu/qemu_conf.h: Hack to turn on QMP mode. Replace with a version number check on >= 0.12 later * src/qemu/qemu_monitor.c: Delegate to json monitor if enabled * src/qemu/qemu_monitor_json.c, src/qemu/qemu_monitor_json.h: Add impl of QMP protocol * src/Makefile.am: Add src/qemu/qemu_monitor_json.{c,h} --- po/POTFILES.in | 1 + src/Makefile.am | 2 + src/qemu/qemu_conf.c | 7 + src/qemu/qemu_conf.h | 3 + src/qemu/qemu_driver.c | 16 +- src/qemu/qemu_monitor.c | 260 +++++++-- src/qemu/qemu_monitor.h | 4 + src/qemu/qemu_monitor_json.c | 1423 ++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 156 +++++ tests/qemuxml2argvtest.c | 2 +- 10 files changed, 1825 insertions(+), 49 deletions(-) create mode 100644 src/qemu/qemu_monitor_json.c create mode 100644 src/qemu/qemu_monitor_json.h diff --git a/po/POTFILES.in b/po/POTFILES.in index 9864259..f8e9130 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -29,6 +29,7 @@ src/qemu/qemu_bridge_filter.c src/qemu/qemu_conf.c src/qemu/qemu_driver.c src/qemu/qemu_monitor.c +src/qemu/qemu_monitor_json.c src/qemu/qemu_monitor_text.c src/remote/remote_driver.c src/secret/secret_driver.c diff --git a/src/Makefile.am b/src/Makefile.am index b0775a8..043ca6b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -188,6 +188,8 @@ QEMU_DRIVER_SOURCES = \ qemu/qemu_monitor.c qemu/qemu_monitor.h \ qemu/qemu_monitor_text.c \ qemu/qemu_monitor_text.h \ + qemu/qemu_monitor_json.c \ + qemu/qemu_monitor_json.h \ qemu/qemu_driver.c qemu/qemu_driver.h \ qemu/qemu_bridge_filter.c \ qemu/qemu_bridge_filter.h diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 8f8d490..7d41b5d 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -942,6 +942,9 @@ static unsigned int qemudComputeCmdFlags(const char *help, if (version >= 10000) flags |= QEMUD_CMD_FLAG_0_10; + if (version >= 12000) + flags |= QEMUD_CMD_FLAG_0_12; + return flags; } @@ -1584,6 +1587,7 @@ int qemudBuildCommandLine(virConnectPtr conn, struct qemud_driver *driver, virDomainDefPtr def, virDomainChrDefPtr monitor_chr, + int monitor_json, unsigned int qemuCmdFlags, const char ***retargv, const char ***retenv, @@ -1858,6 +1862,9 @@ int qemudBuildCommandLine(virConnectPtr conn, if (monitor_chr) { virBuffer buf = VIR_BUFFER_INITIALIZER; + if (monitor_json) + virBufferAddLit(&buf, "control,"); + qemudBuildCommandLineChrDevStr(monitor_chr, &buf); if (virBufferError(&buf)) goto error; diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index 1bf3e16..248677a 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -74,6 +74,8 @@ enum qemud_cmd_flags { QEMUD_CMD_FLAG_MIGRATE_QEMU_UNIX = (1 << 21), /* Does qemu support unix domain sockets for migration? */ QEMUD_CMD_FLAG_CHARDEV = (1 << 22), /* Is the new -chardev arg available */ QEMUD_CMD_FLAG_ENABLE_KVM = (1 << 23), /* Is the -enable-kvm flag available to "enable KVM full virtualization support" */ + QEMUD_CMD_FLAG_0_12 = (1 << 24), + QEMUD_CMD_FLAG_MONITOR_JSON = QEMUD_CMD_FLAG_0_12, /* JSON mode for monitor */ }; /* Main driver state */ @@ -168,6 +170,7 @@ int qemudBuildCommandLine (virConnectPtr conn, struct qemud_driver *driver, virDomainDefPtr def, virDomainChrDefPtr monitor_chr, + int monitor_json, unsigned int qemuCmdFlags, const char ***retargv, const char ***retenv, diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 468e0ab..e3759bf 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -87,6 +87,7 @@ struct _qemuDomainObjPrivate { qemuMonitorPtr mon; virDomainChrDefPtr monConfig; + int monJSON; int nvcpupids; int *vcpupids; @@ -173,6 +174,8 @@ static int qemuDomainObjPrivateXMLFormat(virBufferPtr buf, void *data) } virBufferEscapeString(buf, " <monitor path='%s'", monitorpath); + if (priv->monJSON) + virBufferAddLit(buf, " json='1'"); virBufferVSprintf(buf, " type='%s'/>\n", virDomainChrTypeToString(priv->monConfig->type)); } @@ -217,6 +220,9 @@ static int qemuDomainObjPrivateXMLParse(xmlXPathContextPtr ctxt, void *data) priv->monConfig->type = VIR_DOMAIN_CHR_TYPE_PTY; VIR_FREE(tmp); + if (virXPathBoolean(NULL, "int(./monitor[1]/@json)", ctxt)) + priv->monJSON = 1; + switch (priv->monConfig->type) { case VIR_DOMAIN_CHR_TYPE_PTY: priv->monConfig->data.file.path = monitorpath; @@ -780,6 +786,7 @@ qemuConnectMonitor(virDomainObjPtr vm) if ((priv->mon = qemuMonitorOpen(vm, priv->monConfig, + priv->monJSON, qemuHandleMonitorEOF)) == NULL) { VIR_ERROR(_("Failed to connect monitor for %s\n"), vm->def->name); return -1; @@ -2328,6 +2335,11 @@ static int qemudStartVMDaemon(virConnectPtr conn, if (qemuPrepareMonitorChr(conn, driver, priv->monConfig, vm->def->name) < 0) goto cleanup; +#if HAVE_YAJL + if (qemuCmdFlags & QEMUD_CMD_FLAG_MONITOR_JSON) + priv->monJSON = 1; +#endif + if ((ret = virFileDeletePid(driver->stateDir, vm->def->name)) != 0) { virReportSystemError(conn, ret, _("Cannot remove stale PID file for %s"), @@ -2343,7 +2355,7 @@ static int qemudStartVMDaemon(virConnectPtr conn, vm->def->id = driver->nextvmid++; if (qemudBuildCommandLine(conn, driver, vm->def, priv->monConfig, - qemuCmdFlags, &argv, &progenv, + priv->monJSON, qemuCmdFlags, &argv, &progenv, &tapfds, &ntapfds, migrateFrom) < 0) goto cleanup; @@ -4418,7 +4430,7 @@ static char *qemuDomainXMLToNative(virConnectPtr conn, goto cleanup; if (qemudBuildCommandLine(conn, driver, def, - &monConfig, qemuCmdFlags, + &monConfig, 0, qemuCmdFlags, &retargv, &retenv, NULL, NULL, /* Don't want it to create TAP devices */ NULL) < 0) { diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 6c4dfde..502b389 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -30,6 +30,7 @@ #include "qemu_monitor.h" #include "qemu_monitor_text.h" +#include "qemu_monitor_json.h" #include "qemu_conf.h" #include "event.h" #include "virterror_internal.h" @@ -74,6 +75,7 @@ struct _qemuMonitor { /* If the monitor is in process of shutting down */ unsigned closed: 1; + unsigned json: 1; }; @@ -283,9 +285,14 @@ qemuMonitorIOProcess(qemuMonitorPtr mon) msg = mon->msg; VIR_DEBUG("Process %d", (int)mon->bufferOffset); - len = qemuMonitorTextIOProcess(mon, - mon->buffer, mon->bufferOffset, - msg); + if (mon->json) + len = qemuMonitorJSONIOProcess(mon, + mon->buffer, mon->bufferOffset, + msg); + else + len = qemuMonitorTextIOProcess(mon, + mon->buffer, mon->bufferOffset, + msg); if (len < 0) { mon->lastErrno = errno; @@ -534,6 +541,7 @@ qemuMonitorIO(int watch, int fd, int events, void *opaque) { qemuMonitorPtr qemuMonitorOpen(virDomainObjPtr vm, virDomainChrDefPtr config, + int json, qemuMonitorEOFNotify eofCB) { qemuMonitorPtr mon; @@ -560,6 +568,7 @@ qemuMonitorOpen(virDomainObjPtr vm, mon->refs = 1; mon->vm = vm; mon->eofCB = eofCB; + mon->json = json; qemuMonitorLock(mon); virDomainObjRef(vm); @@ -699,43 +708,68 @@ int qemuMonitorStartCPUs(qemuMonitorPtr mon, virConnectPtr conn) { + int ret; DEBUG("mon=%p, fd=%d", mon, mon->fd); - return qemuMonitorTextStartCPUs(mon, conn); + if (mon->json) + ret = qemuMonitorJSONStartCPUs(mon, conn); + else + ret = qemuMonitorTextStartCPUs(mon, conn); + return ret; } int qemuMonitorStopCPUs(qemuMonitorPtr mon) { + int ret; DEBUG("mon=%p, fd=%d", mon, mon->fd); - return qemuMonitorTextStopCPUs(mon); + if (mon->json) + ret = qemuMonitorJSONStopCPUs(mon); + else + ret = qemuMonitorTextStopCPUs(mon); + return ret; } int qemuMonitorSystemPowerdown(qemuMonitorPtr mon) { + int ret; DEBUG("mon=%p, fd=%d", mon, mon->fd); - return qemuMonitorTextSystemPowerdown(mon); + if (mon->json) + ret = qemuMonitorJSONSystemPowerdown(mon); + else + ret = qemuMonitorTextSystemPowerdown(mon); + return ret; } int qemuMonitorGetCPUInfo(qemuMonitorPtr mon, int **pids) { + int ret; DEBUG("mon=%p, fd=%d", mon, mon->fd); - return qemuMonitorTextGetCPUInfo(mon, pids); + if (mon->json) + ret = qemuMonitorJSONGetCPUInfo(mon, pids); + else + ret = qemuMonitorTextGetCPUInfo(mon, pids); + return ret; } int qemuMonitorGetBalloonInfo(qemuMonitorPtr mon, unsigned long *currmem) { + int ret; DEBUG("mon=%p, fd=%d", mon, mon->fd); - return qemuMonitorTextGetBalloonInfo(mon, currmem); + if (mon->json) + ret = qemuMonitorJSONGetBalloonInfo(mon, currmem); + else + ret = qemuMonitorTextGetBalloonInfo(mon, currmem); + return ret; } @@ -747,38 +781,61 @@ int qemuMonitorGetBlockStatsInfo(qemuMonitorPtr mon, long long *wr_bytes, long long *errs) { + int ret; DEBUG("mon=%p, fd=%d dev=%s", mon, mon->fd, devname); - return qemuMonitorTextGetBlockStatsInfo(mon, devname, - rd_req, rd_bytes, - wr_req, wr_bytes, - errs); + if (mon->json) + ret = qemuMonitorJSONGetBlockStatsInfo(mon, devname, + rd_req, rd_bytes, + wr_req, wr_bytes, + errs); + else + ret = qemuMonitorTextGetBlockStatsInfo(mon, devname, + rd_req, rd_bytes, + wr_req, wr_bytes, + errs); + return ret; } int qemuMonitorSetVNCPassword(qemuMonitorPtr mon, const char *password) { + int ret; DEBUG("mon=%p, fd=%d", mon, mon->fd); - return qemuMonitorTextSetVNCPassword(mon, password); + if (mon->json) + ret = qemuMonitorJSONSetVNCPassword(mon, password); + else + ret = qemuMonitorTextSetVNCPassword(mon, password); + return ret; } int qemuMonitorSetBalloon(qemuMonitorPtr mon, unsigned long newmem) { + int ret; DEBUG("mon=%p, fd=%d newmem=%lu", mon, mon->fd, newmem); - return qemuMonitorTextSetBalloon(mon, newmem); + if (mon->json) + ret = qemuMonitorJSONSetBalloon(mon, newmem); + else + ret = qemuMonitorTextSetBalloon(mon, newmem); + return ret; } int qemuMonitorEjectMedia(qemuMonitorPtr mon, const char *devname) { + int ret; DEBUG("mon=%p, fd=%d devname=%s", mon, mon->fd, devname); - return qemuMonitorTextEjectMedia(mon, devname); + if (mon->json) + ret = qemuMonitorJSONEjectMedia(mon, devname); + else + ret = qemuMonitorTextEjectMedia(mon, devname); + return ret; } @@ -787,10 +844,15 @@ int qemuMonitorChangeMedia(qemuMonitorPtr mon, const char *newmedia, const char *format) { + int ret; DEBUG("mon=%p, fd=%d devname=%s newmedia=%s format=%s", mon, mon->fd, devname, newmedia, format); - return qemuMonitorTextChangeMedia(mon, devname, newmedia, format); + if (mon->json) + ret = qemuMonitorJSONChangeMedia(mon, devname, newmedia, format); + else + ret = qemuMonitorTextChangeMedia(mon, devname, newmedia, format); + return ret; } @@ -799,10 +861,15 @@ int qemuMonitorSaveVirtualMemory(qemuMonitorPtr mon, size_t length, const char *path) { + int ret; DEBUG("mon=%p, fd=%d offset=%llu length=%zu path=%s", mon, mon->fd, offset, length, path); - return qemuMonitorTextSaveVirtualMemory(mon, offset, length, path); + if (mon->json) + ret = qemuMonitorJSONSaveVirtualMemory(mon, offset, length, path); + else + ret = qemuMonitorTextSaveVirtualMemory(mon, offset, length, path); + return ret; } int qemuMonitorSavePhysicalMemory(qemuMonitorPtr mon, @@ -810,19 +877,29 @@ int qemuMonitorSavePhysicalMemory(qemuMonitorPtr mon, size_t length, const char *path) { + int ret; DEBUG("mon=%p, fd=%d offset=%llu length=%zu path=%s", mon, mon->fd, offset, length, path); - return qemuMonitorTextSavePhysicalMemory(mon, offset, length, path); + if (mon->json) + ret = qemuMonitorJSONSavePhysicalMemory(mon, offset, length, path); + else + ret = qemuMonitorTextSavePhysicalMemory(mon, offset, length, path); + return ret; } int qemuMonitorSetMigrationSpeed(qemuMonitorPtr mon, unsigned long bandwidth) { + int ret; DEBUG("mon=%p, fd=%d bandwidth=%lu", mon, mon->fd, bandwidth); - return qemuMonitorTextSetMigrationSpeed(mon, bandwidth); + if (mon->json) + ret = qemuMonitorJSONSetMigrationSpeed(mon, bandwidth); + else + ret = qemuMonitorTextSetMigrationSpeed(mon, bandwidth); + return ret; } int qemuMonitorGetMigrationStatus(qemuMonitorPtr mon, @@ -831,12 +908,20 @@ int qemuMonitorGetMigrationStatus(qemuMonitorPtr mon, unsigned long long *remaining, unsigned long long *total) { + int ret; DEBUG("mon=%p, fd=%d", mon, mon->fd); - return qemuMonitorTextGetMigrationStatus(mon, status, - transferred, - remaining, - total); + if (mon->json) + ret = qemuMonitorJSONGetMigrationStatus(mon, status, + transferred, + remaining, + total); + else + ret = qemuMonitorTextGetMigrationStatus(mon, status, + transferred, + remaining, + total); + return ret; } @@ -845,10 +930,15 @@ int qemuMonitorMigrateToHost(qemuMonitorPtr mon, const char *hostname, int port) { + int ret; DEBUG("mon=%p, fd=%d hostname=%s port=%d", mon, mon->fd, hostname, port); - return qemuMonitorTextMigrateToHost(mon, background, hostname, port); + if (mon->json) + ret = qemuMonitorJSONMigrateToHost(mon, background, hostname, port); + else + ret = qemuMonitorTextMigrateToHost(mon, background, hostname, port); + return ret; } @@ -857,35 +947,55 @@ int qemuMonitorMigrateToCommand(qemuMonitorPtr mon, const char * const *argv, const char *target) { + int ret; DEBUG("mon=%p, fd=%d argv=%p target=%s", mon, mon->fd, argv, target); - return qemuMonitorTextMigrateToCommand(mon, background, argv, target); + if (mon->json) + ret = qemuMonitorJSONMigrateToCommand(mon, background, argv, target); + else + ret = qemuMonitorTextMigrateToCommand(mon, background, argv, target); + return ret; } int qemuMonitorMigrateToUnix(qemuMonitorPtr mon, int background, const char *unixfile) { + int ret; DEBUG("mon=%p fd=%d unixfile=%s", mon, mon->fd, unixfile); - return qemuMonitorTextMigrateToUnix(mon, background, unixfile); + if (mon->json) + ret = qemuMonitorJSONMigrateToUnix(mon, background, unixfile); + else + ret = qemuMonitorTextMigrateToUnix(mon, background, unixfile); + return ret; } int qemuMonitorMigrateCancel(qemuMonitorPtr mon) { + int ret; DEBUG("mon=%p fd=%d", mon, mon->fd); - return qemuMonitorTextMigrateCancel(mon); + if (mon->json) + ret = qemuMonitorJSONMigrateCancel(mon); + else + ret = qemuMonitorTextMigrateCancel(mon); + return ret; } int qemuMonitorAddUSBDisk(qemuMonitorPtr mon, const char *path) { + int ret; DEBUG("mon=%p, fd=%d path=%s", mon, mon->fd, path); - return qemuMonitorTextAddUSBDisk(mon, path); + if (mon->json) + ret = qemuMonitorJSONAddUSBDisk(mon, path); + else + ret = qemuMonitorTextAddUSBDisk(mon, path); + return ret; } @@ -893,19 +1003,29 @@ int qemuMonitorAddUSBDeviceExact(qemuMonitorPtr mon, int bus, int dev) { + int ret; DEBUG("mon=%p, fd=%d bus=%d dev=%d", mon, mon->fd, bus, dev); - return qemuMonitorTextAddUSBDeviceExact(mon, bus, dev); + if (mon->json) + ret = qemuMonitorJSONAddUSBDeviceExact(mon, bus, dev); + else + ret = qemuMonitorTextAddUSBDeviceExact(mon, bus, dev); + return ret; } int qemuMonitorAddUSBDeviceMatch(qemuMonitorPtr mon, int vendor, int product) { + int ret; DEBUG("mon=%p, fd=%d vendor=%d product=%d", mon, mon->fd, vendor, product); - return qemuMonitorTextAddUSBDeviceMatch(mon, vendor, product); + if (mon->json) + ret = qemuMonitorJSONAddUSBDeviceMatch(mon, vendor, product); + else + ret = qemuMonitorTextAddUSBDeviceMatch(mon, vendor, product); + return ret; } @@ -918,16 +1038,26 @@ int qemuMonitorAddPCIHostDevice(qemuMonitorPtr mon, unsigned *guestBus, unsigned *guestSlot) { + int ret; DEBUG("mon=%p, fd=%d domain=%d bus=%d slot=%d function=%d", mon, mon->fd, hostDomain, hostBus, hostSlot, hostFunction); - return qemuMonitorTextAddPCIHostDevice(mon, hostDomain, - hostBus, hostSlot, - hostFunction, - guestDomain, - guestBus, - guestSlot); + if (mon->json) + ret = qemuMonitorJSONAddPCIHostDevice(mon, hostDomain, + hostBus, hostSlot, + hostFunction, + guestDomain, + guestBus, + guestSlot); + else + ret = qemuMonitorTextAddPCIHostDevice(mon, hostDomain, + hostBus, hostSlot, + hostFunction, + guestDomain, + guestBus, + guestSlot); + return ret; } @@ -938,11 +1068,17 @@ int qemuMonitorAddPCIDisk(qemuMonitorPtr mon, unsigned *guestBus, unsigned *guestSlot) { + int ret; DEBUG("mon=%p, fd=%d path=%s bus=%s", mon, mon->fd, path, bus); - return qemuMonitorTextAddPCIDisk(mon, path, bus, - guestDomain, guestBus, guestSlot); + if (mon->json) + ret = qemuMonitorJSONAddPCIDisk(mon, path, bus, + guestDomain, guestBus, guestSlot); + else + ret = qemuMonitorTextAddPCIDisk(mon, path, bus, + guestDomain, guestBus, guestSlot); + return ret; } @@ -952,10 +1088,16 @@ int qemuMonitorAddPCINetwork(qemuMonitorPtr mon, unsigned *guestBus, unsigned *guestSlot) { + int ret; DEBUG("mon=%p, fd=%d nicstr=%s", mon, mon->fd, nicstr); - return qemuMonitorTextAddPCINetwork(mon, nicstr, guestDomain, - guestBus, guestSlot); + if (mon->json) + ret = qemuMonitorJSONAddPCINetwork(mon, nicstr, guestDomain, + guestBus, guestSlot); + else + ret = qemuMonitorTextAddPCINetwork(mon, nicstr, guestDomain, + guestBus, guestSlot); + return ret; } @@ -964,11 +1106,17 @@ int qemuMonitorRemovePCIDevice(qemuMonitorPtr mon, unsigned guestBus, unsigned guestSlot) { + int ret; DEBUG("mon=%p, fd=%d domain=%d bus=%d slot=%d", mon, mon->fd, guestDomain, guestBus, guestSlot); - return qemuMonitorTextRemovePCIDevice(mon, guestDomain, - guestBus, guestSlot); + if (mon->json) + ret = qemuMonitorJSONRemovePCIDevice(mon, guestDomain, + guestBus, guestSlot); + else + ret = qemuMonitorTextRemovePCIDevice(mon, guestDomain, + guestBus, guestSlot); + return ret; } @@ -976,30 +1124,45 @@ int qemuMonitorSendFileHandle(qemuMonitorPtr mon, const char *fdname, int fd) { + int ret; DEBUG("mon=%p, fd=%d fdname=%s fd=%d", mon, mon->fd, fdname, fd); - return qemuMonitorTextSendFileHandle(mon, fdname, fd); + if (mon->json) + ret = qemuMonitorJSONSendFileHandle(mon, fdname, fd); + else + ret = qemuMonitorTextSendFileHandle(mon, fdname, fd); + return ret; } int qemuMonitorCloseFileHandle(qemuMonitorPtr mon, const char *fdname) { + int ret; DEBUG("mon=%p, fd=%d fdname=%s", mon, mon->fd, fdname); - return qemuMonitorTextCloseFileHandle(mon, fdname); + if (mon->json) + ret = qemuMonitorJSONCloseFileHandle(mon, fdname); + else + ret = qemuMonitorTextCloseFileHandle(mon, fdname); + return ret; } int qemuMonitorAddHostNetwork(qemuMonitorPtr mon, const char *netstr) { + int ret; DEBUG("mon=%p, fd=%d netstr=%s", mon, mon->fd, netstr); - return qemuMonitorTextAddHostNetwork(mon, netstr); + if (mon->json) + ret = qemuMonitorJSONAddHostNetwork(mon, netstr); + else + ret = qemuMonitorTextAddHostNetwork(mon, netstr); + return ret; } @@ -1007,8 +1170,13 @@ int qemuMonitorRemoveHostNetwork(qemuMonitorPtr mon, int vlan, const char *netname) { + int ret; DEBUG("mon=%p, fd=%d netname=%s", mon, mon->fd, netname); - return qemuMonitorTextRemoveHostNetwork(mon, vlan, netname); + if (mon->json) + ret = qemuMonitorJSONRemoveHostNetwork(mon, vlan, netname); + else + ret = qemuMonitorTextRemoveHostNetwork(mon, vlan, netname); + return ret; } diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 5f1a65d..0d9e315 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -80,6 +80,7 @@ char *qemuMonitorEscapeShell(const char *in); qemuMonitorPtr qemuMonitorOpen(virDomainObjPtr vm, virDomainChrDefPtr config, + int json, qemuMonitorEOFNotify eofCB); int qemuMonitorClose(qemuMonitorPtr mon); @@ -132,6 +133,9 @@ int qemuMonitorSetBalloon(qemuMonitorPtr mon, /* XXX should we pass the virDomainDiskDefPtr instead * and hide devname details inside monitor. Reconsider * this when doing the QMP implementation + * + * XXXX 'eject' has gained a 'force' flag we might like + * to make use of... */ int qemuMonitorEjectMedia(qemuMonitorPtr mon, const char *devname); diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c new file mode 100644 index 0000000..9d71826 --- /dev/null +++ b/src/qemu/qemu_monitor_json.c @@ -0,0 +1,1423 @@ +/* + * qemu_monitor_json.c: interaction with QEMU monitor console + * + * Copyright (C) 2006-2009 Red Hat, Inc. + * Copyright (C) 2006 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <config.h> + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <poll.h> +#include <unistd.h> +#include <string.h> +#include <sys/time.h> + +#include "qemu_monitor_json.h" +#include "qemu_conf.h" +#include "memory.h" +#include "logging.h" +#include "driver.h" +#include "datatypes.h" +#include "virterror_internal.h" +#include "json.h" + +#define VIR_FROM_THIS VIR_FROM_QEMU + + +#define LINE_ENDING "\r\n" + +static int +qemuMonitorJSONIOProcessLine(qemuMonitorPtr mon ATTRIBUTE_UNUSED, + const char *line, + qemuMonitorMessagePtr msg) +{ + virJSONValuePtr obj = NULL; + int ret = -1; + + VIR_DEBUG("Line [%s]", line); + + if (!(obj = virJSONValueFromString(line))) { + VIR_DEBUG0("Parsing JSON string failed"); + errno = EINVAL; + goto cleanup; + } + + if (obj->type != VIR_JSON_TYPE_OBJECT) { + VIR_DEBUG0("Parsed JSON string isn't an object"); + errno = EINVAL; + } + + if (virJSONValueObjectHasKey(obj, "QMP") == 1) { + VIR_DEBUG0("Got QMP capabilities data"); + ret = 0; + goto cleanup; + } + + if (virJSONValueObjectHasKey(obj, "event") == 1) { + VIR_DEBUG0("Got an event"); + ret = 0; + goto cleanup; + } + + if (msg) { + msg->rxBuffer = strdup(line); + msg->rxLength = strlen(line); + msg->finished = 1; + } else { + VIR_DEBUG("Ignoring unexpected JSON message [%s]", line); + } + + ret = 0; + +cleanup: + virJSONValueFree(obj); + return ret; +} + +int qemuMonitorJSONIOProcess(qemuMonitorPtr mon, + const char *data, + size_t len, + qemuMonitorMessagePtr msg) +{ + int used = 0; + /*VIR_DEBUG("Data %d bytes [%s]", len, data);*/ + + while (used < len) { + char *nl = strstr(data + used, LINE_ENDING); + + if (nl) { + int got = nl - (data + used); + char *line = strndup(data + used, got); + used += got + strlen(LINE_ENDING); + line[got] = '\0'; /* kill \n */ + if (qemuMonitorJSONIOProcessLine(mon, line, msg) < 0) { + VIR_FREE(line); + return -1; + } + + VIR_FREE(line); + } else { + break; + } + } + + VIR_DEBUG("Total used %d bytes out of %d available in buffer", used, len); + return used; +} + +static int +qemuMonitorJSONCommandWithFd(qemuMonitorPtr mon, + virJSONValuePtr cmd, + int scm_fd, + virJSONValuePtr *reply) +{ + int ret = -1; + qemuMonitorMessage msg; + char *cmdstr = NULL; + + *reply = NULL; + + memset(&msg, 0, sizeof msg); + + if (!(cmdstr = virJSONValueToString(cmd))) { + virReportOOMError(NULL); + goto cleanup; + } + if (virAsprintf(&msg.txBuffer, "%s\r\n", cmdstr) < 0) { + virReportOOMError(NULL); + goto cleanup; + } + msg.txLength = strlen(msg.txBuffer); + msg.txFD = scm_fd; + + VIR_DEBUG("Send command '%s' for write with FD %d", cmdstr, scm_fd); + + ret = qemuMonitorSend(mon, &msg); + + VIR_DEBUG("Receive command reply ret=%d errno=%d %d bytes '%s'", + ret, msg.lastErrno, msg.rxLength, msg.rxBuffer); + + + /* If we got ret==0, but not reply data something rather bad + * went wrong, so lets fake an EIO error */ + if (!msg.rxBuffer && ret == 0) { + msg.lastErrno = EIO; + ret = -1; + } + + if (ret == 0) { + if (!((*reply) = virJSONValueFromString(msg.rxBuffer))) { + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("cannot parse JSON doc '%s'"), msg.rxBuffer); + goto cleanup; + } + } + + if (ret < 0) + virReportSystemError(NULL, msg.lastErrno, + _("cannot send monitor command '%s'"), cmdstr); + +cleanup: + VIR_FREE(cmdstr); + VIR_FREE(msg.txBuffer); + VIR_FREE(msg.rxBuffer); + + return ret; +} + + +static int +qemuMonitorJSONCommand(qemuMonitorPtr mon, + virJSONValuePtr cmd, + virJSONValuePtr *reply) { + return qemuMonitorJSONCommandWithFd(mon, cmd, -1, reply); +} + +/* Ignoring OOM in this method, since we're already reporting + * a more important error + * + * XXX see qerror.h for different klasses & fill out useful params + */ +static char *qemuMonitorJSONStringifyError(virJSONValuePtr error) +{ + char *klass = virJSONValueObjectGetString(error, "class"); + + if (klass) { + return strdup(klass); + } else { + return strdup(_("Missing QEMU error klass")); + } +} + +static int +qemuMonitorJSONCheckError(virJSONValuePtr cmd, + virJSONValuePtr reply) +{ + if (virJSONValueObjectHasKey(reply, "error")) { + virJSONValuePtr error = virJSONValueObjectGet(reply, "error"); + char *cmdstr = virJSONValueToString(cmd); + char *replystr = virJSONValueToString(reply); + + if (!error) { + VIR_DEBUG("Saw a JSON error, but value is null for %s: %s", + cmdstr, replystr); + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("error running QEMU command '%s': '%s'"), + cmdstr, replystr); + } else { + VIR_DEBUG("Got a JSON error set for %s", cmdstr); + char *detail = qemuMonitorJSONStringifyError(error); + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("error running QEMU command '%s': %s ('%s')"), + cmdstr, detail, replystr); + VIR_FREE(detail); + } + VIR_FREE(cmdstr); + VIR_FREE(replystr); + return -1; + } else if (!virJSONValueObjectHasKey(reply, "return")) { + char *cmdstr = virJSONValueToString(cmd); + char *replystr = virJSONValueToString(reply); + + VIR_DEBUG("Neither 'return' nor 'error' is set in the JSON reply %s: %s", + cmdstr, replystr); + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("error running QEMU command '%s': '%s'"), cmdstr, replystr); + VIR_FREE(cmdstr); + VIR_FREE(replystr); + return -1; + } + return 0; +} + + +static int +qemuMonitorJSONHasError(virJSONValuePtr reply, + const char *klass) +{ + virJSONValuePtr error; + char *thisklass; + + if (!virJSONValueObjectHasKey(reply, "error")) + return 0; + + error = virJSONValueObjectGet(reply, "error"); + if (!error) + return 0; + + if (!virJSONValueObjectHasKey(error, "class")) + return 0; + + thisklass = virJSONValueObjectGetString(error, "class"); + + if (!thisklass) + return 0; + + return STREQ(klass, thisklass); +} + +static int +qemuMonitorJSONCommandAddTimestamp(virJSONValuePtr obj) +{ + struct timeval tv; + virJSONValuePtr timestamp = NULL; + + if (gettimeofday(&tv, NULL) < 0) { + virReportSystemError(NULL, errno, "%s", + _("cannot query time of day")); + return -1; + } + + if (!(timestamp = virJSONValueNewObject())) + goto no_memory; + + if (virJSONValueObjectAppendNumberLong(timestamp, "seconds", tv.tv_sec) < 0) + goto no_memory; + if (virJSONValueObjectAppendNumberLong(timestamp, "microseconds", tv.tv_usec) < 0) + goto no_memory; + + if (virJSONValueObjectAppend(obj, "timestamp", timestamp) < 0) + goto no_memory; + + return 0; + +no_memory: + virReportOOMError(NULL); + virJSONValueFree(timestamp); + return -1; +} + +static virJSONValuePtr +qemuMonitorJSONMakeCommand(const char *cmdname, + ...) +{ + virJSONValuePtr obj; + virJSONValuePtr jargs = NULL; + va_list args; + char *key; + + va_start(args, cmdname); + + if (!(obj = virJSONValueNewObject())) + goto no_memory; + + if (virJSONValueObjectAppendString(obj, "execute", cmdname) < 0) + goto no_memory; + + if (qemuMonitorJSONCommandAddTimestamp(obj) < 0) + goto error; + + while ((key = va_arg(args, char *)) != NULL) { + int ret; + char type; + + if (strlen(key) < 3) { + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("argument key '%s' is too short, missing type prefix"), + key); + goto error; + } + + /* Keys look like s:name the first letter is a type code */ + type = key[0]; + key += 2; + + if (!jargs && + !(jargs = virJSONValueNewObject())) + goto no_memory; + + /* This doesn't supports maps/arrays. This hasn't + * proved to be a problem..... yet :-) */ + switch (type) { + case 's': { + char *val = va_arg(args, char *); + ret = virJSONValueObjectAppendString(jargs, key, val); + } break; + case 'i': { + int val = va_arg(args, int); + ret = virJSONValueObjectAppendNumberInt(jargs, key, val); + } break; + case 'u': { + unsigned int val = va_arg(args, unsigned int); + ret = virJSONValueObjectAppendNumberUint(jargs, key, val); + } break; + case 'I': { + long long val = va_arg(args, long long); + ret = virJSONValueObjectAppendNumberLong(jargs, key, val); + } break; + case 'U': { + unsigned long long val = va_arg(args, unsigned long long); + ret = virJSONValueObjectAppendNumberUlong(jargs, key, val); + } break; + case 'd': { + double val = va_arg(args, double); + ret = virJSONValueObjectAppendNumberDouble(jargs, key, val); + } break; + case 'b': { + int val = va_arg(args, int); + ret = virJSONValueObjectAppendBoolean(jargs, key, val); + } break; + case 'n': { + ret = virJSONValueObjectAppendNull(jargs, key); + } break; + default: + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("unsupported data type '%c' for arg '%s'"), type, key - 2); + goto error; + } + if (ret < 0) + goto no_memory; + } + + if (jargs && + virJSONValueObjectAppend(obj, "arguments", jargs) < 0) + goto no_memory; + + va_end(args); + + return obj; + +no_memory: + virReportOOMError(NULL); +error: + virJSONValueFree(obj); + virJSONValueFree(jargs); + va_end(args); + return NULL; +} + + +int +qemuMonitorJSONStartCPUs(qemuMonitorPtr mon, + virConnectPtr conn ATTRIBUTE_UNUSED) +{ + int ret; + virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("cont", NULL); + virJSONValuePtr reply = NULL; + if (!cmd) + return -1; + + ret = qemuMonitorJSONCommand(mon, cmd, &reply); + + if (ret == 0) + ret = qemuMonitorJSONCheckError(cmd, reply); + + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} + + +int +qemuMonitorJSONStopCPUs(qemuMonitorPtr mon) +{ + int ret; + virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("stop", NULL); + virJSONValuePtr reply = NULL; + if (!cmd) + return -1; + + ret = qemuMonitorJSONCommand(mon, cmd, &reply); + + if (ret == 0) + ret = qemuMonitorJSONCheckError(cmd, reply); + + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} + + +int qemuMonitorJSONSystemPowerdown(qemuMonitorPtr mon) +{ + int ret; + virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("system_powerdown", NULL); + virJSONValuePtr reply = NULL; + if (!cmd) + return -1; + + ret = qemuMonitorJSONCommand(mon, cmd, &reply); + + if (ret == 0) + ret = qemuMonitorJSONCheckError(cmd, reply); + + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} + + +int qemuMonitorJSONGetCPUInfo(qemuMonitorPtr mon, + int **pids) +{ + int ret; + virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("info", + "s:item", "cpus", + NULL); + virJSONValuePtr reply = NULL; + + *pids = NULL; + + if (!cmd) + return -1; + + ret = qemuMonitorJSONCommand(mon, cmd, &reply); + + if (ret == 0) + ret = qemuMonitorJSONCheckError(cmd, reply); + + /* XXX extract PIDs if present - QEMU hasn't implement this yet :-( */ + + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} + + +/* + * Returns: 0 if balloon not supported, +1 if balloon query worked + * or -1 on failure + */ +int qemuMonitorJSONGetBalloonInfo(qemuMonitorPtr mon, + unsigned long *currmem) +{ + int ret; + virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("info", + "s:item", "balloon", + NULL); + virJSONValuePtr reply = NULL; + + *currmem = 0; + + if (!cmd) + return -1; + + ret = qemuMonitorJSONCommand(mon, cmd, &reply); + + if (ret == 0) { + /* See if balloon soft-failed */ + if (qemuMonitorJSONHasError(reply, "DeviceNotActive") || + qemuMonitorJSONHasError(reply, "KVMMissingCap")) + goto cleanup; + + /* See if any other fatal error occurred */ + ret = qemuMonitorJSONCheckError(cmd, reply); + + /* Success */ + if (ret == 0) { + unsigned long long mem; + + if (virJSONValueObjectGetNumberUlong(reply, "return", &mem) < 0) { + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", + _("info balloon reply was missing mem return data")); + ret = -1; + goto cleanup; + } + + *currmem = mem; + ret = 1; + } + } + +cleanup: + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} + + +int qemuMonitorJSONGetBlockStatsInfo(qemuMonitorPtr mon, + const char *devname, + long long *rd_req, + long long *rd_bytes, + long long *wr_req, + long long *wr_bytes, + long long *errs) +{ + int ret; + int i; + int found = 0; + virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("info", + "s:item", "blockstats", + NULL); + virJSONValuePtr reply = NULL; + virJSONValuePtr devices; + + *rd_req = *rd_bytes = *wr_req = *wr_bytes = *errs = 0; + + if (!cmd) + return -1; + + ret = qemuMonitorJSONCommand(mon, cmd, &reply); + + if (ret == 0) { + ret = qemuMonitorJSONCheckError(cmd, reply); + if (ret < 0) + goto cleanup; + } + ret = -1; + + devices = virJSONValueObjectGet(reply, "return"); + if (!devices || devices->type != VIR_JSON_TYPE_ARRAY) { + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("blockstats reply was missing device list")); + goto cleanup; + } + + for (i = 0 ; i < virJSONValueArraySize(devices) ; i++) { + virJSONValuePtr dev = virJSONValueArrayGet(devices, i); + virJSONValuePtr stats; + const char *thisdev; + if (!dev || dev->type != VIR_JSON_TYPE_OBJECT) { + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("blockstats device entry was not in expected format")); + goto cleanup; + } + + if ((thisdev = virJSONValueObjectGetString(dev, "device")) == NULL) { + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("blockstats device entry was not in expected format")); + goto cleanup; + } + + if (STRNEQ(thisdev, devname)) + continue; + + found = 1; + if ((stats = virJSONValueObjectGet(dev, "stats")) == NULL || + stats->type != VIR_JSON_TYPE_OBJECT) { + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("blockstats stats entry was not in expected format")); + goto cleanup; + } + + if (virJSONValueObjectGetNumberLong(stats, "rd_bytes", rd_bytes) < 0) { + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("cannot read %s statistic"), + "rd_bytes"); + goto cleanup; + } + if (virJSONValueObjectGetNumberLong(stats, "rd_operations", rd_req) < 0) { + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("cannot read %s statistic"), + "rd_operations"); + goto cleanup; + } + if (virJSONValueObjectGetNumberLong(stats, "wr_bytes", wr_bytes) < 0) { + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("cannot read %s statistic"), + "wr_bytes"); + goto cleanup; + } + if (virJSONValueObjectGetNumberLong(stats, "wr_operations", wr_req) < 0) { + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("cannot read %s statistic"), + "wr_operations"); + goto cleanup; + } + } + + if (!found) { + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("cannot find statistics for device '%s'"), devname); + goto cleanup; + } + ret = 0; + +cleanup: + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} + + +int qemuMonitorJSONSetVNCPassword(qemuMonitorPtr mon, + const char *password) +{ + int ret; + virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("change", + "s:device", "vnc", + "s:target", "password", + "s:arg", password, + NULL); + virJSONValuePtr reply = NULL; + if (!cmd) + return -1; + + ret = qemuMonitorJSONCommand(mon, cmd, &reply); + + if (ret == 0) + ret = qemuMonitorJSONCheckError(cmd, reply); + + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} + +/* + * Returns: 0 if balloon not supported, +1 if balloon adjust worked + * or -1 on failure + */ +int qemuMonitorJSONSetBalloon(qemuMonitorPtr mon, + unsigned long newmem) +{ + int ret; + virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("balloon", + "U:value", (unsigned long long)newmem, + NULL); + virJSONValuePtr reply = NULL; + if (!cmd) + return -1; + + ret = qemuMonitorJSONCommand(mon, cmd, &reply); + + if (ret == 0) { + /* See if balloon soft-failed */ + if (qemuMonitorJSONHasError(reply, "DeviceNotActive") || + qemuMonitorJSONHasError(reply, "KVMMissingCap")) + goto cleanup; + + /* See if any other fatal error occurred */ + ret = qemuMonitorJSONCheckError(cmd, reply); + + /* Real success */ + if (ret == 0) + ret = 1; + } + +cleanup: + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} + + +int qemuMonitorJSONEjectMedia(qemuMonitorPtr mon, + const char *devname) +{ + int ret; + virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("eject", + "s:device", devname, + "i:force", 0, + NULL); + virJSONValuePtr reply = NULL; + if (!cmd) + return -1; + + ret = qemuMonitorJSONCommand(mon, cmd, &reply); + + if (ret == 0) + ret = qemuMonitorJSONCheckError(cmd, reply); + + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} + + +int qemuMonitorJSONChangeMedia(qemuMonitorPtr mon, + const char *devname, + const char *newmedia, + const char *format) +{ + int ret; + virJSONValuePtr cmd; + if (format) + cmd = qemuMonitorJSONMakeCommand("change", + "s:device", devname, + "s:target", newmedia, + "s:arg", format, + NULL); + else + cmd = qemuMonitorJSONMakeCommand("change", + "s:device", devname, + "s:target", newmedia, + NULL); + + virJSONValuePtr reply = NULL; + if (!cmd) + return -1; + + ret = qemuMonitorJSONCommand(mon, cmd, &reply); + + if (ret == 0) + ret = qemuMonitorJSONCheckError(cmd, reply); + + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} + + +static int qemuMonitorJSONSaveMemory(qemuMonitorPtr mon, + const char *cmdtype, + unsigned long long offset, + size_t length, + const char *path) +{ + int ret; + virJSONValuePtr cmd = qemuMonitorJSONMakeCommand(cmdtype, + "U:val", offset, + "u:size", length, + "s:filename", path, + NULL); + virJSONValuePtr reply = NULL; + if (!cmd) + return -1; + + ret = qemuMonitorJSONCommand(mon, cmd, &reply); + + if (ret == 0) + ret = qemuMonitorJSONCheckError(cmd, reply); + + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} + + +int qemuMonitorJSONSaveVirtualMemory(qemuMonitorPtr mon, + unsigned long long offset, + size_t length, + const char *path) +{ + return qemuMonitorJSONSaveMemory(mon, "memsave", offset, length, path); +} + +int qemuMonitorJSONSavePhysicalMemory(qemuMonitorPtr mon, + unsigned long long offset, + size_t length, + const char *path) +{ + return qemuMonitorJSONSaveMemory(mon, "pmemsave", offset, length, path); +} + + +int qemuMonitorJSONSetMigrationSpeed(qemuMonitorPtr mon, + unsigned long bandwidth) +{ + int ret; + char *bandwidthstr; + virJSONValuePtr cmd; + virJSONValuePtr reply = NULL; + if (virAsprintf(&bandwidthstr, "%lum", bandwidth) < 0) { + virReportOOMError(NULL); + return -1; + } + cmd = qemuMonitorJSONMakeCommand("migrate_set_speed", + "s:value", bandwidthstr, + NULL); + VIR_FREE(bandwidthstr); + if (!cmd) + return -1; + + ret = qemuMonitorJSONCommand(mon, cmd, &reply); + + if (ret == 0) + ret = qemuMonitorJSONCheckError(cmd, reply); + + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} + + +static int +qemuMonitorJSONGetMigrationStatusReply(virJSONValuePtr reply, + int *status, + unsigned long long *transferred, + unsigned long long *remaining, + unsigned long long *total) +{ + virJSONValuePtr ret; + char *statusstr; + + if (!(ret = virJSONValueObjectGet(reply, "return"))) { + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("info migration reply was missing return data")); + return -1; + } + + if (!(statusstr = virJSONValueObjectGetString(ret, "status"))) { + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("info migration reply was missing return status")); + return -1; + } + + if ((*status = qemuMonitorMigrationStatusTypeFromString(statusstr)) < 0) { + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("unexpected migration status in %s"), statusstr); + return -1; + } + + if (*status == QEMU_MONITOR_MIGRATION_STATUS_ACTIVE) { + virJSONValuePtr ram = virJSONValueObjectGet(ret, "ram"); + if (!ram) { + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", + _("migration was active, but no RAM info was set")); + return -1; + } + + if (virJSONValueObjectGetNumberUlong(ram, "transferred", transferred) < 0) { + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", + _("migration was active, but RAM 'transferred' data was missing")); + return -1; + } + if (virJSONValueObjectGetNumberUlong(ram, "remaining", remaining) < 0) { + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", + _("migration was active, but RAM 'remaining' data was missing")); + return -1; + } + if (virJSONValueObjectGetNumberUlong(ram, "total", total) < 0) { + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", + _("migration was active, but RAM 'total' data was missing")); + return -1; + } + } + + return 0; +} + + +int qemuMonitorJSONGetMigrationStatus(qemuMonitorPtr mon, + int *status, + unsigned long long *transferred, + unsigned long long *remaining, + unsigned long long *total) +{ + int ret; + virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("info", + "s:item", "migration", + NULL); + virJSONValuePtr reply = NULL; + + *status = 0; + *transferred = *remaining = *total = 0; + + if (!cmd) + return -1; + + ret = qemuMonitorJSONCommand(mon, cmd, &reply); + + if (ret == 0) + ret = qemuMonitorJSONCheckError(cmd, reply); + + if (ret == 0 && + qemuMonitorJSONGetMigrationStatusReply(reply, + status, + transferred, + remaining, + total) < 0) + ret = -1; + + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} + + +static int qemuMonitorJSONMigrate(qemuMonitorPtr mon, + int background, + const char *uri) +{ + int ret; + virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("migrate", + "i:detach", background ? 1 : 0, + "s:uri", uri, + NULL); + virJSONValuePtr reply = NULL; + + if (!cmd) + return -1; + + ret = qemuMonitorJSONCommand(mon, cmd, &reply); + + if (ret == 0) + ret = qemuMonitorJSONCheckError(cmd, reply); + + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} + + +int qemuMonitorJSONMigrateToHost(qemuMonitorPtr mon, + int background, + const char *hostname, + int port) +{ + char *uri = NULL; + int ret; + + if (virAsprintf(&uri, "tcp:%s:%d", hostname, port) < 0) { + virReportOOMError(NULL); + return -1; + } + + ret = qemuMonitorJSONMigrate(mon, background, uri); + + VIR_FREE(uri); + + return ret; +} + + +int qemuMonitorJSONMigrateToCommand(qemuMonitorPtr mon, + int background, + const char * const *argv, + const char *target) +{ + char *argstr; + char *dest = NULL; + int ret = -1; + char *safe_target = NULL; + + argstr = virArgvToString(argv); + if (!argstr) { + virReportOOMError(NULL); + goto cleanup; + } + + /* Migrate to file */ + safe_target = qemuMonitorEscapeShell(target); + if (!safe_target) { + virReportOOMError(NULL); + goto cleanup; + } + + if (virAsprintf(&dest, "exec:%s >>%s 2>/dev/null", argstr, safe_target) < 0) { + virReportOOMError(NULL); + goto cleanup; + } + + ret = qemuMonitorJSONMigrate(mon, background, dest); + +cleanup: + VIR_FREE(safe_target); + VIR_FREE(argstr); + VIR_FREE(dest); + return ret; +} + +int qemuMonitorJSONMigrateToUnix(qemuMonitorPtr mon, + int background, + const char *unixfile) +{ + char *dest = NULL; + int ret = -1; + + if (virAsprintf(&dest, "unix:%s", unixfile) < 0) { + virReportOOMError(NULL); + return -1; + } + + ret = qemuMonitorJSONMigrate(mon, background, dest); + + VIR_FREE(dest); + + return ret; +} + + +int qemuMonitorJSONMigrateCancel(qemuMonitorPtr mon) +{ + int ret; + virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("migrate_cancel", NULL); + virJSONValuePtr reply = NULL; + if (!cmd) + return -1; + + ret = qemuMonitorJSONCommand(mon, cmd, &reply); + + if (ret == 0) + ret = qemuMonitorJSONCheckError(cmd, reply); + + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} + + +static int qemuMonitorJSONAddUSB(qemuMonitorPtr mon, + const char *dev) +{ + int ret; + virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("usb_add", + "s:devname", dev, + NULL); + virJSONValuePtr reply = NULL; + + if (!cmd) + return -1; + + ret = qemuMonitorJSONCommand(mon, cmd, &reply); + + if (ret == 0) + ret = qemuMonitorJSONCheckError(cmd, reply); + + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} + + +int qemuMonitorJSONAddUSBDisk(qemuMonitorPtr mon, + const char *path) +{ + int ret; + char *disk; + + if (virAsprintf(&disk, "disk:%s", path) < 0) { + virReportOOMError(NULL); + return -1; + } + + ret = qemuMonitorJSONAddUSB(mon, disk); + + VIR_FREE(disk); + + return ret; +} + + +int qemuMonitorJSONAddUSBDeviceExact(qemuMonitorPtr mon, + int bus ATTRIBUTE_UNUSED, + int dev ATTRIBUTE_UNUSED) +{ + int ret; + char *addr; + + if (virAsprintf(&addr, "host:%.3d.%.3d", bus, dev) < 0) { + virReportOOMError(NULL); + return -1; + } + + ret = qemuMonitorJSONAddUSB(mon, addr); + + VIR_FREE(addr); + return ret; +} + + +int qemuMonitorJSONAddUSBDeviceMatch(qemuMonitorPtr mon, + int vendor ATTRIBUTE_UNUSED, + int product ATTRIBUTE_UNUSED) +{ + int ret; + char *addr; + + if (virAsprintf(&addr, "host:%.4x:%.4x", vendor, product) < 0) { + virReportOOMError(NULL); + return -1; + } + + ret = qemuMonitorJSONAddUSB(mon, addr); + + VIR_FREE(addr); + return ret; +} + + +/* XXX qemu also returns a 'function' number now */ +static int +qemuMonitorJSONGetGuestAddress(virJSONValuePtr reply, + unsigned *guestDomain, + unsigned *guestBus, + unsigned *guestSlot) +{ + virJSONValuePtr addr; + + addr = virJSONValueObjectGet(reply, "return"); + if (!addr || addr->type != VIR_JSON_TYPE_OBJECT) { + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("pci_add reply was missing device address")); + return -1; + } + + if (virJSONValueObjectGetNumberUint(addr, "domain", guestDomain) < 0) { + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("pci_add reply was missing device domain number")); + return -1; + } + + if (virJSONValueObjectGetNumberUint(addr, "bus", guestBus) < 0) { + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("pci_add reply was missing device bus number")); + return -1; + } + + if (virJSONValueObjectGetNumberUint(addr, "slot", guestSlot) < 0) { + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("pci_add reply was missing device slot number")); + return -1; + } + + return 0; +} + + +int qemuMonitorJSONAddPCIHostDevice(qemuMonitorPtr mon, + unsigned hostDomain ATTRIBUTE_UNUSED, + unsigned hostBus, + unsigned hostSlot, + unsigned hostFunction, + unsigned *guestDomain, + unsigned *guestBus, + unsigned *guestSlot) +{ + int ret; + virJSONValuePtr cmd; + virJSONValuePtr reply = NULL; + char *dev; + + *guestDomain = *guestBus = *guestSlot = 0; + + /* XXX hostDomain */ + if (virAsprintf(&dev, "host=%.2x:%.2x.%.1x", + hostBus, hostSlot, hostFunction) < 0) { + virReportOOMError(NULL); + return -1; + } + + cmd = qemuMonitorJSONMakeCommand("pci_add", + "s:pci_addr", "auto" + "s:type", "host", + "s:opts", dev, + NULL); + VIR_FREE(dev); + if (!cmd) + return -1; + + ret = qemuMonitorJSONCommand(mon, cmd, &reply); + + if (ret == 0) + ret = qemuMonitorJSONCheckError(cmd, reply); + + if (ret == 0 && + qemuMonitorJSONGetGuestAddress(reply, guestDomain, guestBus, guestSlot) < 0) + ret = -1; + + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} + + +int qemuMonitorJSONAddPCIDisk(qemuMonitorPtr mon, + const char *path, + const char *bus, + unsigned *guestDomain, + unsigned *guestBus, + unsigned *guestSlot) { + int ret; + virJSONValuePtr cmd; + virJSONValuePtr reply = NULL; + char *dev; + + *guestDomain = *guestBus = *guestSlot = 0; + + if (virAsprintf(&dev, "file=%s,if=%s", path, bus) < 0) { + virReportOOMError(NULL); + return -1; + } + + cmd = qemuMonitorJSONMakeCommand("pci_add", + "s:pci_addr", "auto", + "s:type", "storage", + "s:opts", dev, + NULL); + VIR_FREE(dev); + if (!cmd) + return -1; + + ret = qemuMonitorJSONCommand(mon, cmd, &reply); + + if (ret == 0) + ret = qemuMonitorJSONCheckError(cmd, reply); + + if (ret == 0 && + qemuMonitorJSONGetGuestAddress(reply, guestDomain, guestBus, guestSlot) < 0) + ret = -1; + + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} + + +int qemuMonitorJSONAddPCINetwork(qemuMonitorPtr mon, + const char *nicstr, + unsigned *guestDomain, + unsigned *guestBus, + unsigned *guestSlot) +{ + int ret; + virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("pci_add", + "s:pci_addr", "auto", + "s:type", "nic", + "s:opts", nicstr, + NULL); + virJSONValuePtr reply = NULL; + + *guestDomain = *guestBus = *guestSlot = 0; + + if (!cmd) + return -1; + + ret = qemuMonitorJSONCommand(mon, cmd, &reply); + + if (ret == 0) + ret = qemuMonitorJSONCheckError(cmd, reply); + + if (ret == 0 && + qemuMonitorJSONGetGuestAddress(reply, guestDomain, guestBus, guestSlot) < 0) + ret = -1; + + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} + + +int qemuMonitorJSONRemovePCIDevice(qemuMonitorPtr mon, + unsigned guestDomain, + unsigned guestBus, + unsigned guestSlot) +{ + int ret; + virJSONValuePtr cmd; + virJSONValuePtr reply = NULL; + char *addr; + + if (virAsprintf(&addr, "%.4x:%.2x:%.2x", + guestDomain, guestBus, guestSlot) < 0) { + virReportOOMError(NULL); + return -1; + } + + cmd = qemuMonitorJSONMakeCommand("pci_del", + "s:pci_addr", addr, + NULL); + VIR_FREE(addr); + if (!cmd) + return -1; + + ret = qemuMonitorJSONCommand(mon, cmd, &reply); + + if (ret == 0) + ret = qemuMonitorJSONCheckError(cmd, reply); + + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} + + +int qemuMonitorJSONSendFileHandle(qemuMonitorPtr mon, + const char *fdname, + int fd) +{ + int ret; + virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("getfd", + "s:fdname", fdname, + NULL); + virJSONValuePtr reply = NULL; + if (!cmd) + return -1; + + ret = qemuMonitorJSONCommandWithFd(mon, cmd, fd, &reply); + + if (ret == 0) + ret = qemuMonitorJSONCheckError(cmd, reply); + + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} + + +int qemuMonitorJSONCloseFileHandle(qemuMonitorPtr mon, + const char *fdname) +{ + int ret; + virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("closefd", + "s:fdname", fdname, + NULL); + virJSONValuePtr reply = NULL; + if (!cmd) + return -1; + + ret = qemuMonitorJSONCommand(mon, cmd, &reply); + + if (ret == 0) + ret = qemuMonitorJSONCheckError(cmd, reply); + + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} + + +int qemuMonitorJSONAddHostNetwork(qemuMonitorPtr mon, + const char *netstr) +{ + int ret; + virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("host_net_add", + "s:device", netstr, + NULL); + virJSONValuePtr reply = NULL; + if (!cmd) + return -1; + + ret = qemuMonitorJSONCommand(mon, cmd, &reply); + + if (ret == 0) + ret = qemuMonitorJSONCheckError(cmd, reply); + + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} + + +int qemuMonitorJSONRemoveHostNetwork(qemuMonitorPtr mon, + int vlan, + const char *netname) +{ + int ret; + virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("host_net_remove", + "i:vlan", vlan, + "s:device", netname, + NULL); + virJSONValuePtr reply = NULL; + if (!cmd) + return -1; + + ret = qemuMonitorJSONCommand(mon, cmd, &reply); + + if (ret == 0) + ret = qemuMonitorJSONCheckError(cmd, reply); + + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h new file mode 100644 index 0000000..62a88c0 --- /dev/null +++ b/src/qemu/qemu_monitor_json.h @@ -0,0 +1,156 @@ +/* + * qemu_monitor_json.h: interaction with QEMU monitor console + * + * Copyright (C) 2006-2009 Red Hat, Inc. + * Copyright (C) 2006 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + + +#ifndef QEMU_MONITOR_JSON_H +#define QEMU_MONITOR_JSON_H + +#include "internal.h" + +#include "qemu_monitor.h" + +int qemuMonitorJSONIOProcess(qemuMonitorPtr mon, + const char *data, + size_t len, + qemuMonitorMessagePtr msg); + +int qemuMonitorJSONStartCPUs(qemuMonitorPtr mon, + virConnectPtr conn); +int qemuMonitorJSONStopCPUs(qemuMonitorPtr mon); + +int qemuMonitorJSONSystemPowerdown(qemuMonitorPtr mon); + +int qemuMonitorJSONGetCPUInfo(qemuMonitorPtr mon, + int **pids); +int qemuMonitorJSONGetBalloonInfo(qemuMonitorPtr mon, + unsigned long *currmem); +int qemuMonitorJSONGetBlockStatsInfo(qemuMonitorPtr mon, + const char *devname, + long long *rd_req, + long long *rd_bytes, + long long *wr_req, + long long *wr_bytes, + long long *errs); + + +int qemuMonitorJSONSetVNCPassword(qemuMonitorPtr mon, + const char *password); +int qemuMonitorJSONSetBalloon(qemuMonitorPtr mon, + unsigned long newmem); + +int qemuMonitorJSONEjectMedia(qemuMonitorPtr mon, + const char *devname); +int qemuMonitorJSONChangeMedia(qemuMonitorPtr mon, + const char *devname, + const char *newmedia, + const char *format); + + +int qemuMonitorJSONSaveVirtualMemory(qemuMonitorPtr mon, + unsigned long long offset, + size_t length, + const char *path); +int qemuMonitorJSONSavePhysicalMemory(qemuMonitorPtr mon, + unsigned long long offset, + size_t length, + const char *path); + +int qemuMonitorJSONSetMigrationSpeed(qemuMonitorPtr mon, + unsigned long bandwidth); + +int qemuMonitorJSONGetMigrationStatus(qemuMonitorPtr mon, + int *status, + unsigned long long *transferred, + unsigned long long *remaining, + unsigned long long *total); + +int qemuMonitorJSONMigrateToHost(qemuMonitorPtr mon, + int background, + const char *hostname, + int port); + +int qemuMonitorJSONMigrateToCommand(qemuMonitorPtr mon, + int background, + const char * const *argv, + const char *target); + +int qemuMonitorJSONMigrateToUnix(qemuMonitorPtr mon, + int background, + const char *unixfile); + +int qemuMonitorJSONMigrateCancel(qemuMonitorPtr mon); + +int qemuMonitorJSONAddUSBDisk(qemuMonitorPtr mon, + const char *path); + +int qemuMonitorJSONAddUSBDeviceExact(qemuMonitorPtr mon, + int bus, + int dev); +int qemuMonitorJSONAddUSBDeviceMatch(qemuMonitorPtr mon, + int vendor, + int product); + + +int qemuMonitorJSONAddPCIHostDevice(qemuMonitorPtr mon, + unsigned hostDomain, + unsigned hostBus, + unsigned hostSlot, + unsigned hostFunction, + unsigned *guestDomain, + unsigned *guestBus, + unsigned *guestSlot); + +int qemuMonitorJSONAddPCIDisk(qemuMonitorPtr mon, + const char *path, + const char *bus, + unsigned *guestDomain, + unsigned *guestBus, + unsigned *guestSlot); + +int qemuMonitorJSONAddPCINetwork(qemuMonitorPtr mon, + const char *nicstr, + unsigned *guestDomain, + unsigned *guestBus, + unsigned *guestSlot); + +int qemuMonitorJSONRemovePCIDevice(qemuMonitorPtr mon, + unsigned guestDomain, + unsigned guestBus, + unsigned guestSlot); + + +int qemuMonitorJSONSendFileHandle(qemuMonitorPtr mon, + const char *fdname, + int fd); + +int qemuMonitorJSONCloseFileHandle(qemuMonitorPtr mon, + const char *fdname); + +int qemuMonitorJSONAddHostNetwork(qemuMonitorPtr mon, + const char *netstr); + +int qemuMonitorJSONRemoveHostNetwork(qemuMonitorPtr mon, + int vlan, + const char *netname); + +#endif /* QEMU_MONITOR_JSON_H */ diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index 677c5b4..c39cbbc 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -60,7 +60,7 @@ static int testCompareXMLToArgvFiles(const char *xml, goto fail; if (qemudBuildCommandLine(NULL, &driver, - vmdef, &monitor_chr, flags, + vmdef, &monitor_chr, 0, flags, &argv, &qenv, NULL, NULL, migrateFrom) < 0) goto fail; -- 1.6.5.2

2009/11/26 Daniel P. Berrange <berrange@redhat.com>:
Initial support for the new QEMU monitor protocol using JSON as the data encoding format instead of plain text
* po/POTFILES.in: Add src/qemu/qemu_monitor_json.c * src/qemu/qemu_conf.c, src/qemu/qemu_conf.h: Hack to turn on QMP mode. Replace with a version number check on >= 0.12 later * src/qemu/qemu_monitor.c: Delegate to json monitor if enabled * src/qemu/qemu_monitor_json.c, src/qemu/qemu_monitor_json.h: Add impl of QMP protocol * src/Makefile.am: Add src/qemu/qemu_monitor_json.{c,h} ---
[...]
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c new file mode 100644 index 0000000..9d71826 --- /dev/null +++ b/src/qemu/qemu_monitor_json.c @@ -0,0 +1,1423 @@ +/* + * qemu_monitor_json.c: interaction with QEMU monitor console + * + * Copyright (C) 2006-2009 Red Hat, Inc. + * Copyright (C) 2006 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <config.h> + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <poll.h> +#include <unistd.h> +#include <string.h> +#include <sys/time.h> + +#include "qemu_monitor_json.h" +#include "qemu_conf.h" +#include "memory.h" +#include "logging.h" +#include "driver.h" +#include "datatypes.h" +#include "virterror_internal.h" +#include "json.h" + +#define VIR_FROM_THIS VIR_FROM_QEMU + + +#define LINE_ENDING "\r\n" + +static int +qemuMonitorJSONIOProcessLine(qemuMonitorPtr mon ATTRIBUTE_UNUSED, + const char *line, + qemuMonitorMessagePtr msg) +{ + virJSONValuePtr obj = NULL; + int ret = -1; + + VIR_DEBUG("Line [%s]", line); + + if (!(obj = virJSONValueFromString(line))) { + VIR_DEBUG0("Parsing JSON string failed"); + errno = EINVAL; + goto cleanup; + } + + if (obj->type != VIR_JSON_TYPE_OBJECT) { + VIR_DEBUG0("Parsed JSON string isn't an object"); + errno = EINVAL; + } + + if (virJSONValueObjectHasKey(obj, "QMP") == 1) { + VIR_DEBUG0("Got QMP capabilities data"); + ret = 0; + goto cleanup; + } + + if (virJSONValueObjectHasKey(obj, "event") == 1) { + VIR_DEBUG0("Got an event"); + ret = 0; + goto cleanup; + } + + if (msg) { + msg->rxBuffer = strdup(line);
OOM check is missing.
+ msg->rxLength = strlen(line); + msg->finished = 1; + } else { + VIR_DEBUG("Ignoring unexpected JSON message [%s]", line); + } + + ret = 0; + +cleanup: + virJSONValueFree(obj); + return ret; +} + +int qemuMonitorJSONIOProcess(qemuMonitorPtr mon, + const char *data, + size_t len, + qemuMonitorMessagePtr msg) +{ + int used = 0; + /*VIR_DEBUG("Data %d bytes [%s]", len, data);*/ + + while (used < len) { + char *nl = strstr(data + used, LINE_ENDING);
Is it guaranteed that data ends with LINE_ENDING?
+ + if (nl) { + int got = nl - (data + used); + char *line = strndup(data + used, got);
OOM check is missing.
+ used += got + strlen(LINE_ENDING); + line[got] = '\0'; /* kill \n */ + if (qemuMonitorJSONIOProcessLine(mon, line, msg) < 0) { + VIR_FREE(line); + return -1; + } + + VIR_FREE(line); + } else { + break; + } + } + + VIR_DEBUG("Total used %d bytes out of %d available in buffer", used, len);
len has type size_t, so the second %d should be a %zd, otherwise it'll break 64bit compilation.
+ return used; +} +
[...]
+ +int qemuMonitorJSONAddUSBDeviceExact(qemuMonitorPtr mon, + int bus ATTRIBUTE_UNUSED, + int dev ATTRIBUTE_UNUSED) +{
bus and dev aren't unused.
+ int ret; + char *addr; + + if (virAsprintf(&addr, "host:%.3d.%.3d", bus, dev) < 0) { + virReportOOMError(NULL); + return -1; + } + + ret = qemuMonitorJSONAddUSB(mon, addr); + + VIR_FREE(addr); + return ret; +} + + +int qemuMonitorJSONAddUSBDeviceMatch(qemuMonitorPtr mon, + int vendor ATTRIBUTE_UNUSED, + int product ATTRIBUTE_UNUSED) +{
vendor and product aren't unused.
+ int ret; + char *addr; + + if (virAsprintf(&addr, "host:%.4x:%.4x", vendor, product) < 0) { + virReportOOMError(NULL); + return -1; + } + + ret = qemuMonitorJSONAddUSB(mon, addr); + + VIR_FREE(addr); + return ret; +} +
Some minor issues. ACK anyway. Matthias

On Thu, Dec 03, 2009 at 12:51:13AM +0100, Matthias Bolte wrote:
2009/11/26 Daniel P. Berrange <berrange@redhat.com>:
+int qemuMonitorJSONIOProcess(qemuMonitorPtr mon, + const char *data, + size_t len, + qemuMonitorMessagePtr msg) +{ + int used = 0; + /*VIR_DEBUG("Data %d bytes [%s]", len, data);*/ + + while (used < len) { + char *nl = strstr(data + used, LINE_ENDING);
Is it guaranteed that data ends with LINE_ENDING?
That is the current QEMU JSON protocol defintion. The JSON messages will always be formatted on a single line, followed by their declared LINE_ENDING (\r\n) There's a small chance they may change that before release, in which case we'd need to switch over to a streaming JSON parser that detects the logical end of the JSON object stream Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Thu, Nov 26, 2009 at 06:27:29PM +0000, Daniel P. Berrange wrote:
Initial support for the new QEMU monitor protocol using JSON as the data encoding format instead of plain text [...] --- a/src/Makefile.am +++ b/src/Makefile.am @@ -188,6 +188,8 @@ QEMU_DRIVER_SOURCES = \ qemu/qemu_monitor.c qemu/qemu_monitor.h \ qemu/qemu_monitor_text.c \ qemu/qemu_monitor_text.h \ + qemu/qemu_monitor_json.c \ + qemu/qemu_monitor_json.h \ qemu/qemu_driver.c qemu/qemu_driver.h \ qemu/qemu_bridge_filter.c \
Hum could you take the opportunity to cleanup the tab/space mix in the 2 following lines, above ? [...]
@@ -283,9 +285,14 @@ qemuMonitorIOProcess(qemuMonitorPtr mon) msg = mon->msg;
VIR_DEBUG("Process %d", (int)mon->bufferOffset); - len = qemuMonitorTextIOProcess(mon, - mon->buffer, mon->bufferOffset, - msg); + if (mon->json) + len = qemuMonitorJSONIOProcess(mon, + mon->buffer, mon->bufferOffset, + msg); + else + len = qemuMonitorTextIOProcess(mon, + mon->buffer, mon->bufferOffset, + msg);
I have just one doubt here, assuming we have a json handle, is the text monitor still available. In which case should we try to fallback assuming the json interface get into troubles ? I assume if one fail then the other should fail too but ... [...]
+#define LINE_ENDING "\r\n" + +static int +qemuMonitorJSONIOProcessLine(qemuMonitorPtr mon ATTRIBUTE_UNUSED, + const char *line, + qemuMonitorMessagePtr msg) +{ + virJSONValuePtr obj = NULL; + int ret = -1; + + VIR_DEBUG("Line [%s]", line); + + if (!(obj = virJSONValueFromString(line))) { + VIR_DEBUG0("Parsing JSON string failed"); + errno = EINVAL; + goto cleanup; + } + + if (obj->type != VIR_JSON_TYPE_OBJECT) { + VIR_DEBUG0("Parsed JSON string isn't an object"); + errno = EINVAL; + } + + if (virJSONValueObjectHasKey(obj, "QMP") == 1) { + VIR_DEBUG0("Got QMP capabilities data"); + ret = 0; + goto cleanup; + } + + if (virJSONValueObjectHasKey(obj, "event") == 1) { + VIR_DEBUG0("Got an event"); + ret = 0; + goto cleanup; + } + + if (msg) { + msg->rxBuffer = strdup(line); + msg->rxLength = strlen(line); + msg->finished = 1; + } else { + VIR_DEBUG("Ignoring unexpected JSON message [%s]", line); + } + + ret = 0; + +cleanup: + virJSONValueFree(obj); + return ret; +}
I must be missing something in that routine, we parse the json blob, get an object, check some of the object content and discard it, saving the raw text .... seems to me we dropped the actual parsed content instead of handling it, no ?
+int qemuMonitorJSONIOProcess(qemuMonitorPtr mon, + const char *data, + size_t len, + qemuMonitorMessagePtr msg) +{ + int used = 0; + /*VIR_DEBUG("Data %d bytes [%s]", len, data);*/ + + while (used < len) { + char *nl = strstr(data + used, LINE_ENDING); + + if (nl) { + int got = nl - (data + used); + char *line = strndup(data + used, got); + used += got + strlen(LINE_ENDING); + line[got] = '\0'; /* kill \n */ + if (qemuMonitorJSONIOProcessLine(mon, line, msg) < 0) { + VIR_FREE(line); + return -1; + } + + VIR_FREE(line); + } else { + break; + } + }
Okay, I understand the problem of progressive parsing now... if somehow they extend the json blob with some (error) string containing LINE_ENDING we're stuck I'm afraid.
+ VIR_DEBUG("Total used %d bytes out of %d available in buffer", used, len); + return used; +}
+static virJSONValuePtr +qemuMonitorJSONMakeCommand(const char *cmdname, + ...)
Hum, let's add a SENTINEL attribute !
+{ + virJSONValuePtr obj; + virJSONValuePtr jargs = NULL; + va_list args; + char *key; + + va_start(args, cmdname); + + if (!(obj = virJSONValueNewObject())) + goto no_memory; + + if (virJSONValueObjectAppendString(obj, "execute", cmdname) < 0) + goto no_memory; + + if (qemuMonitorJSONCommandAddTimestamp(obj) < 0) + goto error; + + while ((key = va_arg(args, char *)) != NULL) { + int ret; + char type; + + if (strlen(key) < 3) { + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("argument key '%s' is too short, missing type prefix"), + key); + goto error; + } + + /* Keys look like s:name the first letter is a type code */ + type = key[0]; + key += 2;
Hum, we add a type info on top using prefixing ... weird but why not ...
+ if (!jargs && + !(jargs = virJSONValueNewObject())) + goto no_memory; + + /* This doesn't supports maps/arrays. This hasn't + * proved to be a problem..... yet :-) */ + switch (type) { + case 's': { + char *val = va_arg(args, char *); + ret = virJSONValueObjectAppendString(jargs, key, val); + } break; + case 'i': { + int val = va_arg(args, int); + ret = virJSONValueObjectAppendNumberInt(jargs, key, val); + } break; + case 'u': { + unsigned int val = va_arg(args, unsigned int); + ret = virJSONValueObjectAppendNumberUint(jargs, key, val); + } break; + case 'I': { + long long val = va_arg(args, long long); + ret = virJSONValueObjectAppendNumberLong(jargs, key, val); + } break; + case 'U': { + unsigned long long val = va_arg(args, unsigned long long); + ret = virJSONValueObjectAppendNumberUlong(jargs, key, val); + } break; + case 'd': { + double val = va_arg(args, double); + ret = virJSONValueObjectAppendNumberDouble(jargs, key, val); + } break; + case 'b': { + int val = va_arg(args, int); + ret = virJSONValueObjectAppendBoolean(jargs, key, val); + } break; + case 'n': { + ret = virJSONValueObjectAppendNull(jargs, key); + } break; + default: + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("unsupported data type '%c' for arg '%s'"), type, key - 2); + goto error; + } + if (ret < 0) + goto no_memory; + } + + if (jargs && + virJSONValueObjectAppend(obj, "arguments", jargs) < 0) + goto no_memory; + + va_end(args); + + return obj; + +no_memory: + virReportOOMError(NULL); +error: + virJSONValueFree(obj); + virJSONValueFree(jargs); + va_end(args); + return NULL; +} [...] +{ + int ret; + virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("balloon", + "U:value", (unsigned long long)newmem,
I would rather drop alognment of args and fit into 80 cols. [...] good to see a more formal interface, now they just have to make it XML and that would be perfect ! >:->
diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h new file mode 100644 index 0000000..62a88c0 --- /dev/null +++ b/src/qemu/qemu_monitor_json.h @@ -0,0 +1,156 @@ [...] diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index 677c5b4..c39cbbc 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -60,7 +60,7 @@ static int testCompareXMLToArgvFiles(const char *xml, goto fail;
if (qemudBuildCommandLine(NULL, &driver, - vmdef, &monitor_chr, flags, + vmdef, &monitor_chr, 0, flags, &argv, &qenv, NULL, NULL, migrateFrom) < 0) goto fail;
As pointed earlier by Matthias it would be good to have some kind of regression testing for JSON code, maybe not down to QEmu actual format but at least parsing and serializing the common stuff. ACK Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

On Thu, Dec 03, 2009 at 04:05:54PM +0100, Daniel Veillard wrote:
On Thu, Nov 26, 2009 at 06:27:29PM +0000, Daniel P. Berrange wrote:
Initial support for the new QEMU monitor protocol using JSON as the data encoding format instead of plain text [...] --- a/src/Makefile.am +++ b/src/Makefile.am @@ -188,6 +188,8 @@ QEMU_DRIVER_SOURCES = \ qemu/qemu_monitor.c qemu/qemu_monitor.h \ qemu/qemu_monitor_text.c \ qemu/qemu_monitor_text.h \ + qemu/qemu_monitor_json.c \ + qemu/qemu_monitor_json.h \ qemu/qemu_driver.c qemu/qemu_driver.h \ qemu/qemu_bridge_filter.c \
Hum could you take the opportunity to cleanup the tab/space mix in the 2 following lines, above ?
[...]
@@ -283,9 +285,14 @@ qemuMonitorIOProcess(qemuMonitorPtr mon) msg = mon->msg;
VIR_DEBUG("Process %d", (int)mon->bufferOffset); - len = qemuMonitorTextIOProcess(mon, - mon->buffer, mon->bufferOffset, - msg); + if (mon->json) + len = qemuMonitorJSONIOProcess(mon, + mon->buffer, mon->bufferOffset, + msg); + else + len = qemuMonitorTextIOProcess(mon, + mon->buffer, mon->bufferOffset, + msg);
I have just one doubt here, assuming we have a json handle, is the text monitor still available. In which case should we try to fallback assuming the json interface get into troubles ? I assume if one fail then the other should fail too but ...
No, QEMU works in an either/or mode - you can't have both active.
+#define LINE_ENDING "\r\n" + +static int +qemuMonitorJSONIOProcessLine(qemuMonitorPtr mon ATTRIBUTE_UNUSED, + const char *line, + qemuMonitorMessagePtr msg) +{ + virJSONValuePtr obj = NULL; + int ret = -1; + + VIR_DEBUG("Line [%s]", line); + + if (!(obj = virJSONValueFromString(line))) { + VIR_DEBUG0("Parsing JSON string failed"); + errno = EINVAL; + goto cleanup; + } + + if (obj->type != VIR_JSON_TYPE_OBJECT) { + VIR_DEBUG0("Parsed JSON string isn't an object"); + errno = EINVAL; + } + + if (virJSONValueObjectHasKey(obj, "QMP") == 1) { + VIR_DEBUG0("Got QMP capabilities data"); + ret = 0; + goto cleanup; + } + + if (virJSONValueObjectHasKey(obj, "event") == 1) { + VIR_DEBUG0("Got an event"); + ret = 0; + goto cleanup; + } + + if (msg) { + msg->rxBuffer = strdup(line); + msg->rxLength = strlen(line); + msg->finished = 1; + } else { + VIR_DEBUG("Ignoring unexpected JSON message [%s]", line); + } + + ret = 0; + +cleanup: + virJSONValueFree(obj); + return ret; +}
I must be missing something in that routine, we parse the json blob, get an object, check some of the object content and discard it, saving the raw text .... seems to me we dropped the actual parsed content instead of handling it, no ?
Yes this is a little bit of a wierd scenario, mostly an artifact of the way we have code sharing between the text & json modes. The shared I/O handling code simply works on char * buffers, and so we can't easily pass back the parsed JSON object at this point. So the object here is only used for detecting events - it is reparsed later on in the place which handles method replies/errors. A little inefficient perhaps, but these messages are very small so its not really too bad
+ while ((key = va_arg(args, char *)) != NULL) { + int ret; + char type; + + if (strlen(key) < 3) { + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("argument key '%s' is too short, missing type prefix"), + key); + goto error; + } + + /* Keys look like s:name the first letter is a type code */ + type = key[0]; + key += 2;
Hum, we add a type info on top using prefixing ... weird but why not ...
I originally had 3 values per param in the callers, eg qemuMonitorJSONMakeCommand("eject", "device, VIR_JSON_VALUE_STRING, "hda", NULL) but I thought it was getting far too verbose, so I switched to this simple type format character qemuMonitorJSONMakeCommand("eject", "s:device, "hda", NULL) Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Thu, Dec 03, 2009 at 03:28:02PM +0000, Daniel P. Berrange wrote:
On Thu, Dec 03, 2009 at 04:05:54PM +0100, Daniel Veillard wrote:
On Thu, Nov 26, 2009 at 06:27:29PM +0000, Daniel P. Berrange wrote:
Initial support for the new QEMU monitor protocol using JSON as the data encoding format instead of plain text [...] --- a/src/Makefile.am +++ b/src/Makefile.am @@ -188,6 +188,8 @@ QEMU_DRIVER_SOURCES = \ qemu/qemu_monitor.c qemu/qemu_monitor.h \ qemu/qemu_monitor_text.c \ qemu/qemu_monitor_text.h \ + qemu/qemu_monitor_json.c \ + qemu/qemu_monitor_json.h \ qemu/qemu_driver.c qemu/qemu_driver.h \ qemu/qemu_bridge_filter.c \
Hum could you take the opportunity to cleanup the tab/space mix in the 2 following lines, above ?
[...]
@@ -283,9 +285,14 @@ qemuMonitorIOProcess(qemuMonitorPtr mon) msg = mon->msg;
VIR_DEBUG("Process %d", (int)mon->bufferOffset); - len = qemuMonitorTextIOProcess(mon, - mon->buffer, mon->bufferOffset, - msg); + if (mon->json) + len = qemuMonitorJSONIOProcess(mon, + mon->buffer, mon->bufferOffset, + msg); + else + len = qemuMonitorTextIOProcess(mon, + mon->buffer, mon->bufferOffset, + msg);
I have just one doubt here, assuming we have a json handle, is the text monitor still available. In which case should we try to fallback assuming the json interface get into troubles ? I assume if one fail then the other should fail too but ...
No, QEMU works in an either/or mode - you can't have both active.
okay
+#define LINE_ENDING "\r\n" + +static int +qemuMonitorJSONIOProcessLine(qemuMonitorPtr mon ATTRIBUTE_UNUSED, + const char *line, + qemuMonitorMessagePtr msg) +{ + virJSONValuePtr obj = NULL; + int ret = -1; + + VIR_DEBUG("Line [%s]", line); + + if (!(obj = virJSONValueFromString(line))) { + VIR_DEBUG0("Parsing JSON string failed"); + errno = EINVAL; + goto cleanup; + } + + if (obj->type != VIR_JSON_TYPE_OBJECT) { + VIR_DEBUG0("Parsed JSON string isn't an object"); + errno = EINVAL; + } + + if (virJSONValueObjectHasKey(obj, "QMP") == 1) { + VIR_DEBUG0("Got QMP capabilities data"); + ret = 0; + goto cleanup; + } + + if (virJSONValueObjectHasKey(obj, "event") == 1) { + VIR_DEBUG0("Got an event"); + ret = 0; + goto cleanup; + } + + if (msg) { + msg->rxBuffer = strdup(line); + msg->rxLength = strlen(line); + msg->finished = 1; + } else { + VIR_DEBUG("Ignoring unexpected JSON message [%s]", line); + } + + ret = 0; + +cleanup: + virJSONValueFree(obj); + return ret; +}
I must be missing something in that routine, we parse the json blob, get an object, check some of the object content and discard it, saving the raw text .... seems to me we dropped the actual parsed content instead of handling it, no ?
Yes this is a little bit of a wierd scenario, mostly an artifact of the way we have code sharing between the text & json modes. The shared I/O handling code simply works on char * buffers, and so we can't easily pass back the parsed JSON object at this point. So the object here is only used for detecting events - it is reparsed later on in the place which handles method replies/errors. A little inefficient perhaps, but these messages are very small so its not really too bad
Okay, double parsing but fine, I just wanted to make sure :-)
+ while ((key = va_arg(args, char *)) != NULL) { + int ret; + char type; + + if (strlen(key) < 3) { + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("argument key '%s' is too short, missing type prefix"), + key); + goto error; + } + + /* Keys look like s:name the first letter is a type code */ + type = key[0]; + key += 2;
Hum, we add a type info on top using prefixing ... weird but why not ...
I originally had 3 values per param in the callers, eg
qemuMonitorJSONMakeCommand("eject", "device, VIR_JSON_VALUE_STRING, "hda", NULL)
but I thought it was getting far too verbose, so I switched to this simple type format character
qemuMonitorJSONMakeCommand("eject", "s:device, "hda", NULL)
Yup that's nicer :-) Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

With addition of events there will be alot of callbacks. To avoid having to add many APIs to register callbacks, provide them all at once in a big table * src/qemu/qemu_driver.c: Pass in a callback table to QEMU monitor code * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h Replace the EOF and disk secret callbacks with a callback table --- src/qemu/qemu_driver.c | 10 ++++++---- src/qemu/qemu_monitor.c | 31 ++++++++++++++++++------------- src/qemu/qemu_monitor.h | 39 ++++++++++++++++++++------------------- 3 files changed, 44 insertions(+), 36 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index e3759bf..bf4557e 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -779,6 +779,11 @@ cleanup: return ret; } +static qemuMonitorCallbacks monitorCallbacks = { + .eofNotify = qemuHandleMonitorEOF, + .diskSecretLookup = findVolumeQcowPassphrase, +}; + static int qemuConnectMonitor(virDomainObjPtr vm) { @@ -787,14 +792,11 @@ qemuConnectMonitor(virDomainObjPtr vm) if ((priv->mon = qemuMonitorOpen(vm, priv->monConfig, priv->monJSON, - qemuHandleMonitorEOF)) == NULL) { + &monitorCallbacks)) == NULL) { VIR_ERROR(_("Failed to connect monitor for %s\n"), vm->def->name); return -1; } - qemuMonitorRegisterDiskSecretLookup(priv->mon, - findVolumeQcowPassphrase); - return 0; } diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 502b389..11025a7 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -53,8 +53,7 @@ struct _qemuMonitor { virDomainObjPtr vm; - qemuMonitorEOFNotify eofCB; - qemuMonitorDiskSecretLookup secretCB; + qemuMonitorCallbacksPtr cb; /* If there's a command being processed this will be * non-NULL */ @@ -523,7 +522,7 @@ qemuMonitorIO(int watch, int fd, int events, void *opaque) { virCondSignal(&mon->notify); qemuMonitorUnlock(mon); VIR_DEBUG("Triggering EOF callback error? %d", failed); - mon->eofCB(mon, mon->vm, failed); + mon->cb->eofNotify(mon, mon->vm, failed); qemuMonitorLock(mon); } @@ -542,10 +541,16 @@ qemuMonitorPtr qemuMonitorOpen(virDomainObjPtr vm, virDomainChrDefPtr config, int json, - qemuMonitorEOFNotify eofCB) + qemuMonitorCallbacksPtr cb) { qemuMonitorPtr mon; + if (!cb || !cb->eofNotify) { + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", + _("EOF notify callback must be supplied")); + return NULL; + } + if (VIR_ALLOC(mon) < 0) { virReportOOMError(NULL); return NULL; @@ -567,8 +572,8 @@ qemuMonitorOpen(virDomainObjPtr vm, mon->fd = -1; mon->refs = 1; mon->vm = vm; - mon->eofCB = eofCB; mon->json = json; + mon->cb = cb; qemuMonitorLock(mon); virDomainObjRef(vm); @@ -655,13 +660,6 @@ int qemuMonitorClose(qemuMonitorPtr mon) } -void qemuMonitorRegisterDiskSecretLookup(qemuMonitorPtr mon, - qemuMonitorDiskSecretLookup secretCB) -{ - mon->secretCB = secretCB; -} - - int qemuMonitorSend(qemuMonitorPtr mon, qemuMonitorMessagePtr msg) { @@ -697,10 +695,17 @@ int qemuMonitorGetDiskSecret(qemuMonitorPtr mon, char **secret, size_t *secretLen) { + int ret = -1; *secret = NULL; *secretLen = 0; - return mon->secretCB(mon, conn, mon->vm, path, secret, secretLen); + qemuMonitorRef(mon); + qemuMonitorUnlock(mon); + if (mon->cb && mon->cb->diskSecretLookup) + ret = mon->cb->diskSecretLookup(mon, conn, mon->vm, path, secret, secretLen); + qemuMonitorLock(mon); + qemuMonitorUnref(mon); + return ret; } diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 0d9e315..7aa6ee5 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -59,21 +59,25 @@ struct _qemuMonitorMessage { void *passwordOpaque; }; -typedef void (*qemuMonitorEOFNotify)(qemuMonitorPtr mon, - virDomainObjPtr vm, - int withError); - -/* XXX we'd really like to avoid virCOnnectPtr here - * It is required so the callback can find the active - * secret driver. Need to change this to work like the - * security drivers do, to avoid this - */ -typedef int (*qemuMonitorDiskSecretLookup)(qemuMonitorPtr mon, - virConnectPtr conn, - virDomainObjPtr vm, - const char *path, - char **secret, - size_t *secretLen); +typedef struct _qemuMonitorCallbacks qemuMonitorCallbacks; +typedef qemuMonitorCallbacks *qemuMonitorCallbacksPtr; +struct _qemuMonitorCallbacks { + void (*eofNotify)(qemuMonitorPtr mon, + virDomainObjPtr vm, + int withError); + /* XXX we'd really like to avoid virCOnnectPtr here + * It is required so the callback can find the active + * secret driver. Need to change this to work like the + * security drivers do, to avoid this + */ + int (*diskSecretLookup)(qemuMonitorPtr mon, + virConnectPtr conn, + virDomainObjPtr vm, + const char *path, + char **secret, + size_t *secretLen); +}; + char *qemuMonitorEscapeArg(const char *in); char *qemuMonitorEscapeShell(const char *in); @@ -81,7 +85,7 @@ char *qemuMonitorEscapeShell(const char *in); qemuMonitorPtr qemuMonitorOpen(virDomainObjPtr vm, virDomainChrDefPtr config, int json, - qemuMonitorEOFNotify eofCB); + qemuMonitorCallbacksPtr cb); int qemuMonitorClose(qemuMonitorPtr mon); @@ -91,9 +95,6 @@ void qemuMonitorUnlock(qemuMonitorPtr mon); int qemuMonitorRef(qemuMonitorPtr mon); int qemuMonitorUnref(qemuMonitorPtr mon); -void qemuMonitorRegisterDiskSecretLookup(qemuMonitorPtr mon, - qemuMonitorDiskSecretLookup secretCB); - /* This API is for use by the internal Text/JSON monitor impl code only */ int qemuMonitorSend(qemuMonitorPtr mon, qemuMonitorMessagePtr msg); -- 1.6.5.2

2009/11/26 Daniel P. Berrange <berrange@redhat.com>:
With addition of events there will be alot of callbacks. To avoid having to add many APIs to register callbacks, provide them all at once in a big table
* src/qemu/qemu_driver.c: Pass in a callback table to QEMU monitor code * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h Replace the EOF and disk secret callbacks with a callback table ---
[...]
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 0d9e315..7aa6ee5 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -59,21 +59,25 @@ struct _qemuMonitorMessage { void *passwordOpaque; };
-typedef void (*qemuMonitorEOFNotify)(qemuMonitorPtr mon, - virDomainObjPtr vm, - int withError); - -/* XXX we'd really like to avoid virCOnnectPtr here - * It is required so the callback can find the active - * secret driver. Need to change this to work like the - * security drivers do, to avoid this - */ -typedef int (*qemuMonitorDiskSecretLookup)(qemuMonitorPtr mon, - virConnectPtr conn, - virDomainObjPtr vm, - const char *path, - char **secret, - size_t *secretLen); +typedef struct _qemuMonitorCallbacks qemuMonitorCallbacks; +typedef qemuMonitorCallbacks *qemuMonitorCallbacksPtr; +struct _qemuMonitorCallbacks { + void (*eofNotify)(qemuMonitorPtr mon, + virDomainObjPtr vm, + int withError); + /* XXX we'd really like to avoid virCOnnectPtr here
s/virCOnnectPtr/virConnectPtr/
+ * It is required so the callback can find the active + * secret driver. Need to change this to work like the + * security drivers do, to avoid this + */ + int (*diskSecretLookup)(qemuMonitorPtr mon, + virConnectPtr conn, + virDomainObjPtr vm, + const char *path, + char **secret, + size_t *secretLen); +}; +
char *qemuMonitorEscapeArg(const char *in); char *qemuMonitorEscapeShell(const char *in);
ACK. Matthias

On Thu, Nov 26, 2009 at 06:27:30PM +0000, Daniel P. Berrange wrote:
With addition of events there will be alot of callbacks. To avoid having to add many APIs to register callbacks, provide them all at once in a big table
* src/qemu/qemu_driver.c: Pass in a callback table to QEMU monitor code * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h Replace the EOF and disk secret callbacks with a callback table --- src/qemu/qemu_driver.c | 10 ++++++---- src/qemu/qemu_monitor.c | 31 ++++++++++++++++++------------- src/qemu/qemu_monitor.h | 39 ++++++++++++++++++++------------------- 3 files changed, 44 insertions(+), 36 deletions(-)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index e3759bf..bf4557e 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -779,6 +779,11 @@ cleanup: return ret; }
+static qemuMonitorCallbacks monitorCallbacks = { + .eofNotify = qemuHandleMonitorEOF, + .diskSecretLookup = findVolumeQcowPassphrase, +}; + static int qemuConnectMonitor(virDomainObjPtr vm) { @@ -787,14 +792,11 @@ qemuConnectMonitor(virDomainObjPtr vm) if ((priv->mon = qemuMonitorOpen(vm, priv->monConfig, priv->monJSON, - qemuHandleMonitorEOF)) == NULL) { + &monitorCallbacks)) == NULL) { VIR_ERROR(_("Failed to connect monitor for %s\n"), vm->def->name); return -1; }
- qemuMonitorRegisterDiskSecretLookup(priv->mon, - findVolumeQcowPassphrase); - return 0; }
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 502b389..11025a7 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -53,8 +53,7 @@ struct _qemuMonitor {
virDomainObjPtr vm;
- qemuMonitorEOFNotify eofCB; - qemuMonitorDiskSecretLookup secretCB; + qemuMonitorCallbacksPtr cb;
/* If there's a command being processed this will be * non-NULL */ @@ -523,7 +522,7 @@ qemuMonitorIO(int watch, int fd, int events, void *opaque) { virCondSignal(&mon->notify); qemuMonitorUnlock(mon); VIR_DEBUG("Triggering EOF callback error? %d", failed); - mon->eofCB(mon, mon->vm, failed); + mon->cb->eofNotify(mon, mon->vm, failed);
qemuMonitorLock(mon); } @@ -542,10 +541,16 @@ qemuMonitorPtr qemuMonitorOpen(virDomainObjPtr vm, virDomainChrDefPtr config, int json, - qemuMonitorEOFNotify eofCB) + qemuMonitorCallbacksPtr cb) { qemuMonitorPtr mon;
+ if (!cb || !cb->eofNotify) { + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", + _("EOF notify callback must be supplied")); + return NULL; + } + if (VIR_ALLOC(mon) < 0) { virReportOOMError(NULL); return NULL; @@ -567,8 +572,8 @@ qemuMonitorOpen(virDomainObjPtr vm, mon->fd = -1; mon->refs = 1; mon->vm = vm; - mon->eofCB = eofCB; mon->json = json; + mon->cb = cb; qemuMonitorLock(mon); virDomainObjRef(vm);
@@ -655,13 +660,6 @@ int qemuMonitorClose(qemuMonitorPtr mon) }
-void qemuMonitorRegisterDiskSecretLookup(qemuMonitorPtr mon, - qemuMonitorDiskSecretLookup secretCB) -{ - mon->secretCB = secretCB; -} - - int qemuMonitorSend(qemuMonitorPtr mon, qemuMonitorMessagePtr msg) { @@ -697,10 +695,17 @@ int qemuMonitorGetDiskSecret(qemuMonitorPtr mon, char **secret, size_t *secretLen) { + int ret = -1; *secret = NULL; *secretLen = 0;
- return mon->secretCB(mon, conn, mon->vm, path, secret, secretLen); + qemuMonitorRef(mon); + qemuMonitorUnlock(mon); + if (mon->cb && mon->cb->diskSecretLookup) + ret = mon->cb->diskSecretLookup(mon, conn, mon->vm, path, secret, secretLen); + qemuMonitorLock(mon); + qemuMonitorUnref(mon); + return ret; }
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 0d9e315..7aa6ee5 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -59,21 +59,25 @@ struct _qemuMonitorMessage { void *passwordOpaque; };
-typedef void (*qemuMonitorEOFNotify)(qemuMonitorPtr mon, - virDomainObjPtr vm, - int withError); - -/* XXX we'd really like to avoid virCOnnectPtr here - * It is required so the callback can find the active - * secret driver. Need to change this to work like the - * security drivers do, to avoid this - */ -typedef int (*qemuMonitorDiskSecretLookup)(qemuMonitorPtr mon, - virConnectPtr conn, - virDomainObjPtr vm, - const char *path, - char **secret, - size_t *secretLen); +typedef struct _qemuMonitorCallbacks qemuMonitorCallbacks; +typedef qemuMonitorCallbacks *qemuMonitorCallbacksPtr; +struct _qemuMonitorCallbacks { + void (*eofNotify)(qemuMonitorPtr mon, + virDomainObjPtr vm, + int withError); + /* XXX we'd really like to avoid virCOnnectPtr here + * It is required so the callback can find the active + * secret driver. Need to change this to work like the + * security drivers do, to avoid this + */ + int (*diskSecretLookup)(qemuMonitorPtr mon, + virConnectPtr conn, + virDomainObjPtr vm, + const char *path, + char **secret, + size_t *secretLen); +}; +
char *qemuMonitorEscapeArg(const char *in); char *qemuMonitorEscapeShell(const char *in); @@ -81,7 +85,7 @@ char *qemuMonitorEscapeShell(const char *in); qemuMonitorPtr qemuMonitorOpen(virDomainObjPtr vm, virDomainChrDefPtr config, int json, - qemuMonitorEOFNotify eofCB); + qemuMonitorCallbacksPtr cb);
int qemuMonitorClose(qemuMonitorPtr mon);
@@ -91,9 +95,6 @@ void qemuMonitorUnlock(qemuMonitorPtr mon); int qemuMonitorRef(qemuMonitorPtr mon); int qemuMonitorUnref(qemuMonitorPtr mon);
-void qemuMonitorRegisterDiskSecretLookup(qemuMonitorPtr mon, - qemuMonitorDiskSecretLookup secretCB); - /* This API is for use by the internal Text/JSON monitor impl code only */ int qemuMonitorSend(qemuMonitorPtr mon, qemuMonitorMessagePtr msg);
ACK, too bad that patch is slightly dependant on the json changes, it might be useful to apply independantly, Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Add callbacks for reset, shutdown, poweroff and stop events. Add convenience methods for emiting those events --- src/qemu/qemu_monitor.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.h | 13 ++++++++++ 2 files changed, 73 insertions(+), 0 deletions(-) diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 11025a7..b0ed182 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -709,6 +709,66 @@ int qemuMonitorGetDiskSecret(qemuMonitorPtr mon, } +int qemuMonitorEmitShutdown(qemuMonitorPtr mon) +{ + int ret = -1; + VIR_DEBUG("mon=%p", mon); + + qemuMonitorRef(mon); + qemuMonitorUnlock(mon); + if (mon->cb && mon->cb->domainShutdown) + ret = mon->cb->domainShutdown(mon, mon->vm); + qemuMonitorLock(mon); + qemuMonitorUnref(mon); + return ret; +} + + +int qemuMonitorEmitReset(qemuMonitorPtr mon) +{ + int ret = -1; + VIR_DEBUG("mon=%p", mon); + + qemuMonitorRef(mon); + qemuMonitorUnlock(mon); + if (mon->cb && mon->cb->domainReset) + ret = mon->cb->domainReset(mon, mon->vm); + qemuMonitorLock(mon); + qemuMonitorUnref(mon); + return ret; +} + + +int qemuMonitorEmitPowerdown(qemuMonitorPtr mon) +{ + int ret = -1; + VIR_DEBUG("mon=%p", mon); + + qemuMonitorRef(mon); + qemuMonitorUnlock(mon); + if (mon->cb && mon->cb->domainPowerdown) + ret = mon->cb->domainPowerdown(mon, mon->vm); + qemuMonitorLock(mon); + qemuMonitorUnref(mon); + return ret; +} + + +int qemuMonitorEmitStop(qemuMonitorPtr mon) +{ + int ret = -1; + VIR_DEBUG("mon=%p", mon); + + qemuMonitorRef(mon); + qemuMonitorUnlock(mon); + if (mon->cb && mon->cb->domainStop) + ret = mon->cb->domainStop(mon, mon->vm); + qemuMonitorLock(mon); + qemuMonitorUnref(mon); + return ret; +} + + int qemuMonitorStartCPUs(qemuMonitorPtr mon, virConnectPtr conn) diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 7aa6ee5..8b1e3a3 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -76,6 +76,15 @@ struct _qemuMonitorCallbacks { const char *path, char **secret, size_t *secretLen); + + int (*domainShutdown)(qemuMonitorPtr mon, + virDomainObjPtr vm); + int (*domainReset)(qemuMonitorPtr mon, + virDomainObjPtr vm); + int (*domainPowerdown)(qemuMonitorPtr mon, + virDomainObjPtr vm); + int (*domainStop)(qemuMonitorPtr mon, + virDomainObjPtr vm); }; @@ -106,6 +115,10 @@ int qemuMonitorGetDiskSecret(qemuMonitorPtr mon, char **secret, size_t *secretLen); +int qemuMonitorEmitShutdown(qemuMonitorPtr mon); +int qemuMonitorEmitReset(qemuMonitorPtr mon); +int qemuMonitorEmitPowerdown(qemuMonitorPtr mon); +int qemuMonitorEmitStop(qemuMonitorPtr mon); int qemuMonitorStartCPUs(qemuMonitorPtr mon, virConnectPtr conn); -- 1.6.5.2

2009/11/26 Daniel P. Berrange <berrange@redhat.com>:
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Add callbacks for reset, shutdown, poweroff and stop events. Add convenience methods for emiting those events ---
ACK. Matthias

On Thu, Nov 26, 2009 at 06:27:31PM +0000, Daniel P. Berrange wrote:
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Add callbacks for reset, shutdown, poweroff and stop events. Add convenience methods for emiting those events --- src/qemu/qemu_monitor.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.h | 13 ++++++++++ 2 files changed, 73 insertions(+), 0 deletions(-)
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 11025a7..b0ed182 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -709,6 +709,66 @@ int qemuMonitorGetDiskSecret(qemuMonitorPtr mon, }
+int qemuMonitorEmitShutdown(qemuMonitorPtr mon) +{ + int ret = -1; + VIR_DEBUG("mon=%p", mon); + + qemuMonitorRef(mon); + qemuMonitorUnlock(mon); + if (mon->cb && mon->cb->domainShutdown) + ret = mon->cb->domainShutdown(mon, mon->vm); + qemuMonitorLock(mon); + qemuMonitorUnref(mon); + return ret; +} + + +int qemuMonitorEmitReset(qemuMonitorPtr mon) +{ + int ret = -1; + VIR_DEBUG("mon=%p", mon); + + qemuMonitorRef(mon); + qemuMonitorUnlock(mon); + if (mon->cb && mon->cb->domainReset) + ret = mon->cb->domainReset(mon, mon->vm); + qemuMonitorLock(mon); + qemuMonitorUnref(mon); + return ret; +} + + +int qemuMonitorEmitPowerdown(qemuMonitorPtr mon) +{ + int ret = -1; + VIR_DEBUG("mon=%p", mon); + + qemuMonitorRef(mon); + qemuMonitorUnlock(mon); + if (mon->cb && mon->cb->domainPowerdown) + ret = mon->cb->domainPowerdown(mon, mon->vm); + qemuMonitorLock(mon); + qemuMonitorUnref(mon); + return ret; +} + + +int qemuMonitorEmitStop(qemuMonitorPtr mon) +{ + int ret = -1; + VIR_DEBUG("mon=%p", mon); + + qemuMonitorRef(mon); + qemuMonitorUnlock(mon); + if (mon->cb && mon->cb->domainStop) + ret = mon->cb->domainStop(mon, mon->vm); + qemuMonitorLock(mon); + qemuMonitorUnref(mon); + return ret; +} + + int qemuMonitorStartCPUs(qemuMonitorPtr mon, virConnectPtr conn) diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 7aa6ee5..8b1e3a3 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -76,6 +76,15 @@ struct _qemuMonitorCallbacks { const char *path, char **secret, size_t *secretLen); + + int (*domainShutdown)(qemuMonitorPtr mon, + virDomainObjPtr vm); + int (*domainReset)(qemuMonitorPtr mon, + virDomainObjPtr vm); + int (*domainPowerdown)(qemuMonitorPtr mon, + virDomainObjPtr vm); + int (*domainStop)(qemuMonitorPtr mon, + virDomainObjPtr vm); };
@@ -106,6 +115,10 @@ int qemuMonitorGetDiskSecret(qemuMonitorPtr mon, char **secret, size_t *secretLen);
+int qemuMonitorEmitShutdown(qemuMonitorPtr mon); +int qemuMonitorEmitReset(qemuMonitorPtr mon); +int qemuMonitorEmitPowerdown(qemuMonitorPtr mon); +int qemuMonitorEmitStop(qemuMonitorPtr mon);
int qemuMonitorStartCPUs(qemuMonitorPtr mon, virConnectPtr conn);
ACK Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

* src/qemu/qemu_monitor_json.c: Hook up reset, shutdown, poweroff and stop events --- src/qemu/qemu_monitor_json.c | 69 ++++++++++++++++++++++++++++++++++++++++-- 1 files changed, 66 insertions(+), 3 deletions(-) diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 9d71826..050472c 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -45,8 +45,51 @@ #define LINE_ENDING "\r\n" +static void qemuMonitorJSONHandleShutdown(qemuMonitorPtr mon, virJSONValuePtr data); +static void qemuMonitorJSONHandleReset(qemuMonitorPtr mon, virJSONValuePtr data); +static void qemuMonitorJSONHandlePowerdown(qemuMonitorPtr mon, virJSONValuePtr data); +static void qemuMonitorJSONHandleStop(qemuMonitorPtr mon, virJSONValuePtr data); + +struct { + const char *type; + void (*handler)(qemuMonitorPtr mon, virJSONValuePtr data); +} eventHandlers[] = { + { "SHUTDOWN", qemuMonitorJSONHandleShutdown, }, + { "RESET", qemuMonitorJSONHandleReset, }, + { "POWERDOWN", qemuMonitorJSONHandlePowerdown, }, + { "STOP", qemuMonitorJSONHandleStop, }, +}; + + +static int +qemuMonitorJSONIOProcessEvent(qemuMonitorPtr mon, + virJSONValuePtr obj) +{ + char *type; + int i; + VIR_DEBUG("mon=%p obj=%p", mon, obj); + + type = virJSONValueObjectGetString(obj, "event"); + if (!type) { + VIR_INFO0("missing event type in message"); + errno = EINVAL; + return -1; + } + + for (i = 0 ; i < ARRAY_CARDINALITY(eventHandlers) ; i++) { + if (STREQ(eventHandlers[i].type, type)) { + virJSONValuePtr data = virJSONValueObjectGet(obj, "data"); + VIR_DEBUG("handle %s handler=%p data=%p", type, + eventHandlers[i].handler, data); + (eventHandlers[i].handler)(mon, data); + break; + } + } + return 0; +} + static int -qemuMonitorJSONIOProcessLine(qemuMonitorPtr mon ATTRIBUTE_UNUSED, +qemuMonitorJSONIOProcessLine(qemuMonitorPtr mon, const char *line, qemuMonitorMessagePtr msg) { @@ -73,8 +116,7 @@ qemuMonitorJSONIOProcessLine(qemuMonitorPtr mon ATTRIBUTE_UNUSED, } if (virJSONValueObjectHasKey(obj, "event") == 1) { - VIR_DEBUG0("Got an event"); - ret = 0; + ret = qemuMonitorJSONIOProcessEvent(mon, obj); goto cleanup; } @@ -406,6 +448,27 @@ error: } +static void qemuMonitorJSONHandleShutdown(qemuMonitorPtr mon, virJSONValuePtr data ATTRIBUTE_UNUSED) +{ + qemuMonitorEmitShutdown(mon); +} + +static void qemuMonitorJSONHandleReset(qemuMonitorPtr mon, virJSONValuePtr data ATTRIBUTE_UNUSED) +{ + qemuMonitorEmitReset(mon); +} + +static void qemuMonitorJSONHandlePowerdown(qemuMonitorPtr mon, virJSONValuePtr data ATTRIBUTE_UNUSED) +{ + qemuMonitorEmitPowerdown(mon); +} + +static void qemuMonitorJSONHandleStop(qemuMonitorPtr mon, virJSONValuePtr data ATTRIBUTE_UNUSED) +{ + qemuMonitorEmitStop(mon); +} + + int qemuMonitorJSONStartCPUs(qemuMonitorPtr mon, virConnectPtr conn ATTRIBUTE_UNUSED) -- 1.6.5.2

2009/11/26 Daniel P. Berrange <berrange@redhat.com>:
* src/qemu/qemu_monitor_json.c: Hook up reset, shutdown, poweroff and stop events --- src/qemu/qemu_monitor_json.c | 69 ++++++++++++++++++++++++++++++++++++++++-- 1 files changed, 66 insertions(+), 3 deletions(-)
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 9d71826..050472c 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -45,8 +45,51 @@
#define LINE_ENDING "\r\n"
+static void qemuMonitorJSONHandleShutdown(qemuMonitorPtr mon, virJSONValuePtr data); +static void qemuMonitorJSONHandleReset(qemuMonitorPtr mon, virJSONValuePtr data); +static void qemuMonitorJSONHandlePowerdown(qemuMonitorPtr mon, virJSONValuePtr data); +static void qemuMonitorJSONHandleStop(qemuMonitorPtr mon, virJSONValuePtr data); + +struct { + const char *type; + void (*handler)(qemuMonitorPtr mon, virJSONValuePtr data); +} eventHandlers[] = { + { "SHUTDOWN", qemuMonitorJSONHandleShutdown, }, + { "RESET", qemuMonitorJSONHandleReset, }, + { "POWERDOWN", qemuMonitorJSONHandlePowerdown, }, + { "STOP", qemuMonitorJSONHandleStop, }, +}; + + +static int +qemuMonitorJSONIOProcessEvent(qemuMonitorPtr mon, + virJSONValuePtr obj) +{ + char *type; + int i; + VIR_DEBUG("mon=%p obj=%p", mon, obj); + + type = virJSONValueObjectGetString(obj, "event"); + if (!type) { + VIR_INFO0("missing event type in message");
I thing this should be a warning, not just info.
+ errno = EINVAL; + return -1; + } + + for (i = 0 ; i < ARRAY_CARDINALITY(eventHandlers) ; i++) { + if (STREQ(eventHandlers[i].type, type)) { + virJSONValuePtr data = virJSONValueObjectGet(obj, "data"); + VIR_DEBUG("handle %s handler=%p data=%p", type, + eventHandlers[i].handler, data); + (eventHandlers[i].handler)(mon, data); + break; + } + } + return 0; +} + static int -qemuMonitorJSONIOProcessLine(qemuMonitorPtr mon ATTRIBUTE_UNUSED, +qemuMonitorJSONIOProcessLine(qemuMonitorPtr mon, const char *line, qemuMonitorMessagePtr msg) {
ACK. Matthias

On Thu, Nov 26, 2009 at 06:27:32PM +0000, Daniel P. Berrange wrote:
* src/qemu/qemu_monitor_json.c: Hook up reset, shutdown, poweroff and stop events --- src/qemu/qemu_monitor_json.c | 69 ++++++++++++++++++++++++++++++++++++++++-- 1 files changed, 66 insertions(+), 3 deletions(-)
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 9d71826..050472c 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -45,8 +45,51 @@
#define LINE_ENDING "\r\n"
+static void qemuMonitorJSONHandleShutdown(qemuMonitorPtr mon, virJSONValuePtr data); +static void qemuMonitorJSONHandleReset(qemuMonitorPtr mon, virJSONValuePtr data); +static void qemuMonitorJSONHandlePowerdown(qemuMonitorPtr mon, virJSONValuePtr data); +static void qemuMonitorJSONHandleStop(qemuMonitorPtr mon, virJSONValuePtr data); + +struct { + const char *type; + void (*handler)(qemuMonitorPtr mon, virJSONValuePtr data); +} eventHandlers[] = { + { "SHUTDOWN", qemuMonitorJSONHandleShutdown, }, + { "RESET", qemuMonitorJSONHandleReset, }, + { "POWERDOWN", qemuMonitorJSONHandlePowerdown, }, + { "STOP", qemuMonitorJSONHandleStop, }, +}; + + +static int +qemuMonitorJSONIOProcessEvent(qemuMonitorPtr mon, + virJSONValuePtr obj) +{ + char *type; + int i; + VIR_DEBUG("mon=%p obj=%p", mon, obj); + + type = virJSONValueObjectGetString(obj, "event"); + if (!type) { + VIR_INFO0("missing event type in message"); + errno = EINVAL; + return -1; + } + + for (i = 0 ; i < ARRAY_CARDINALITY(eventHandlers) ; i++) { + if (STREQ(eventHandlers[i].type, type)) { + virJSONValuePtr data = virJSONValueObjectGet(obj, "data"); + VIR_DEBUG("handle %s handler=%p data=%p", type, + eventHandlers[i].handler, data); + (eventHandlers[i].handler)(mon, data); + break; + } + } + return 0; +} + static int -qemuMonitorJSONIOProcessLine(qemuMonitorPtr mon ATTRIBUTE_UNUSED, +qemuMonitorJSONIOProcessLine(qemuMonitorPtr mon, const char *line, qemuMonitorMessagePtr msg) { @@ -73,8 +116,7 @@ qemuMonitorJSONIOProcessLine(qemuMonitorPtr mon ATTRIBUTE_UNUSED, }
if (virJSONValueObjectHasKey(obj, "event") == 1) { - VIR_DEBUG0("Got an event"); - ret = 0; + ret = qemuMonitorJSONIOProcessEvent(mon, obj); goto cleanup; }
@@ -406,6 +448,27 @@ error: }
+static void qemuMonitorJSONHandleShutdown(qemuMonitorPtr mon, virJSONValuePtr data ATTRIBUTE_UNUSED) +{ + qemuMonitorEmitShutdown(mon); +} + +static void qemuMonitorJSONHandleReset(qemuMonitorPtr mon, virJSONValuePtr data ATTRIBUTE_UNUSED) +{ + qemuMonitorEmitReset(mon); +} + +static void qemuMonitorJSONHandlePowerdown(qemuMonitorPtr mon, virJSONValuePtr data ATTRIBUTE_UNUSED) +{ + qemuMonitorEmitPowerdown(mon); +} + +static void qemuMonitorJSONHandleStop(qemuMonitorPtr mon, virJSONValuePtr data ATTRIBUTE_UNUSED) +{ + qemuMonitorEmitStop(mon); +} + + int qemuMonitorJSONStartCPUs(qemuMonitorPtr mon, virConnectPtr conn ATTRIBUTE_UNUSED)
ACK, Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/
participants (6)
-
Daniel P. Berrange
-
Daniel Veillard
-
Matthias Bolte
-
Nikola Ciprich
-
Paolo Bonzini
-
Shi Jin