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 -- Libvir-list mailing list Libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list