From 5265647e9aa6b3fa5e3b6f0dc795f763375b5e62 Mon Sep 17 00:00:00 2001 From: Anthony PERARD Date: Thu, 29 Sep 2011 16:28:24 +0100 Subject: libxl: Introduce JSON parser We use the yajl parser, but we need to make a tree from the parse result to use it outside the parser. So this patch include json_object struct that is used to hold the JSON data. Signed-off-by: Anthony PERARD Committed-by: Ian Jackson Acked-by: Ian Campbell --- README | 1 + tools/libxl/Makefile | 5 +- tools/libxl/libxl_internal.h | 100 ++++++++ tools/libxl/libxl_json.c | 560 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 665 insertions(+), 1 deletion(-) create mode 100644 tools/libxl/libxl_json.c diff --git a/README b/README index ff154b2e78..c9bf699be6 100644 --- a/README +++ b/README @@ -47,6 +47,7 @@ provided by your OS distributor: * Development install of openssl (e.g., openssl-dev) * Development install of x11 (e.g. xorg-x11-dev) * Development install of uuid (e.g. uuid-dev) + * Development install of yajl (e.g. libyajl-dev) * bridge-utils package (/sbin/brctl) * iproute package (/sbin/ip) * hotplug or udev diff --git a/tools/libxl/Makefile b/tools/libxl/Makefile index 1f6b418c81..e50874e0fd 100644 --- a/tools/libxl/Makefile +++ b/tools/libxl/Makefile @@ -32,9 +32,12 @@ endif LIBXL_OBJS-$(CONFIG_X86) += libxl_cpuid.o LIBXL_OBJS-$(CONFIG_IA64) += libxl_nocpuid.o +LIBXL_LIBS += -lyajl + LIBXL_OBJS = flexarray.o libxl.o libxl_create.o libxl_dm.o libxl_pci.o \ libxl_dom.o libxl_exec.o libxl_xshelp.o libxl_device.o \ - libxl_internal.o libxl_utils.o libxl_uuid.o $(LIBXL_OBJS-y) + libxl_internal.o libxl_utils.o libxl_uuid.o libxl_json.o \ + $(LIBXL_OBJS-y) LIBXL_OBJS += _libxl_types.o libxl_flask.o _libxl_types_internal.o $(LIBXL_OBJS): CFLAGS += $(CFLAGS_libxenctrl) $(CFLAGS_libxenguest) $(CFLAGS_libxenstore) $(CFLAGS_libblktapctl) diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h index 2ebd18d2ea..9414ebf10f 100644 --- a/tools/libxl/libxl_internal.h +++ b/tools/libxl/libxl_internal.h @@ -394,4 +394,104 @@ _hidden int libxl__e820_alloc(libxl_ctx *ctx, uint32_t domid, libxl_domain_confi #define STRINGIFY(x) #x #define TOSTRING(x) STRINGIFY(x) +/* from libxl_json */ +#include + +_hidden yajl_gen_status libxl__yajl_gen_asciiz(yajl_gen hand, const char *str); + +typedef enum { + JSON_ERROR, + JSON_NULL, + JSON_TRUE, + JSON_FALSE, + JSON_INTEGER, + JSON_DOUBLE, + JSON_STRING, + JSON_MAP, + JSON_ARRAY, + JSON_ANY +} libxl__json_node_type; + +typedef struct libxl__json_object { + libxl__json_node_type type; + union { + long i; + double d; + char *string; + /* List of libxl__json_object */ + flexarray_t *array; + /* List of libxl__json_map_node */ + flexarray_t *map; + } u; + struct libxl__json_object *parent; +} libxl__json_object; + +typedef struct { + char *map_key; + libxl__json_object *obj; +} libxl__json_map_node; + +typedef struct libxl__yajl_ctx libxl__yajl_ctx; + +static inline bool libxl__json_object_is_string(const libxl__json_object *o) +{ + return o != NULL && o->type == JSON_STRING; +} +static inline bool libxl__json_object_is_integer(const libxl__json_object *o) +{ + return o != NULL && o->type == JSON_INTEGER; +} +static inline bool libxl__json_object_is_map(const libxl__json_object *o) +{ + return o != NULL && o->type == JSON_MAP; +} +static inline bool libxl__json_object_is_array(const libxl__json_object *o) +{ + return o != NULL && o->type == JSON_ARRAY; +} + +static inline +const char *libxl__json_object_get_string(const libxl__json_object *o) +{ + if (libxl__json_object_is_string(o)) + return o->u.string; + else + return NULL; +} +static inline +flexarray_t *libxl__json_object_get_map(const libxl__json_object *o) +{ + if (libxl__json_object_is_map(o)) + return o->u.map; + else + return NULL; +} +static inline +flexarray_t *libxl__json_object_get_array(const libxl__json_object *o) +{ + if (libxl__json_object_is_array(o)) + return o->u.array; + else + return NULL; +} +static inline long libxl__json_object_get_integer(const libxl__json_object *o) +{ + if (libxl__json_object_is_integer(o)) + return o->u.i; + else + return -1; +} + +_hidden libxl__json_object *libxl__json_array_get(const libxl__json_object *o, + int i); +_hidden +libxl__json_map_node *libxl__json_map_node_get(const libxl__json_object *o, + int i); +_hidden const libxl__json_object *libxl__json_map_get(const char *key, + const libxl__json_object *o, + libxl__json_node_type expected_type); +_hidden void libxl__json_object_free(libxl__gc *gc, libxl__json_object *obj); + +_hidden libxl__json_object *libxl__json_parse(libxl__gc *gc, const char *s); + #endif diff --git a/tools/libxl/libxl_json.c b/tools/libxl/libxl_json.c new file mode 100644 index 0000000000..e77192560b --- /dev/null +++ b/tools/libxl/libxl_json.c @@ -0,0 +1,560 @@ +/* + * Copyright (C) 2011 Citrix Ltd. + * + * This program 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; version 2.1 only. with the special + * exception on linking described in file LICENSE. + * + * This program 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. + */ + +#include +#include + +#include +#include + +#include "libxl_internal.h" + +/* #define DEBUG_ANSWER */ + +struct libxl__yajl_ctx { + libxl__gc *gc; + yajl_handle hand; + libxl__json_object *head; + libxl__json_object *current; +#ifdef DEBUG_ANSWER + yajl_gen g; +#endif +}; + +#ifdef DEBUG_ANSWER +# define DEBUG_GEN_ALLOC(ctx) \ + if ((ctx)->g == NULL) { \ + yajl_gen_config conf = { 1, " " }; \ + (ctx)->g = yajl_gen_alloc(&conf, NULL); \ + } +# define DEBUG_GEN_FREE(ctx) \ + if ((ctx)->g) yajl_gen_free((ctx)->g) +# define DEBUG_GEN(ctx, type) yajl_gen_##type(ctx->g) +# define DEBUG_GEN_VALUE(ctx, type, value) yajl_gen_##type(ctx->g, value) +# define DEBUG_GEN_STRING(ctx, str, n) yajl_gen_string(ctx->g, str, n) +# define DEBUG_GEN_REPORT(yajl_ctx) \ + do { \ + const unsigned char *buf = NULL; \ + unsigned int len = 0; \ + yajl_gen_get_buf((yajl_ctx)->g, &buf, &len); \ + LIBXL__LOG(libxl__gc_owner((yajl_ctx)->gc), \ + LIBXL__LOG_DEBUG, "response:\n%s", buf); \ + yajl_gen_free((yajl_ctx)->g); \ + (yajl_ctx)->g = NULL; \ + } while (0) +#else +# define DEBUG_GEN_ALLOC(ctx) ((void)0) +# define DEBUG_GEN_FREE(ctx) ((void)0) +# define DEBUG_GEN(ctx, type) ((void)0) +# define DEBUG_GEN_VALUE(ctx, type, value) ((void)0) +# define DEBUG_GEN_STRING(ctx, value, lenght) ((void)0) +# define DEBUG_GEN_REPORT(ctx) ((void)0) +#endif + +/* + * YAJL Helper + */ + +yajl_gen_status libxl__yajl_gen_asciiz(yajl_gen hand, const char *str) +{ + return yajl_gen_string(hand, (const unsigned char *)str, strlen(str)); +} + + +/* + * libxl__json_object helper functions + */ + +static libxl__json_object *json_object_alloc(libxl__gc *gc, + libxl__json_node_type type) +{ + libxl__json_object *obj; + + obj = calloc(1, sizeof (libxl__json_object)); + if (obj == NULL) { + LIBXL__LOG_ERRNO(libxl__gc_owner(gc), LIBXL__LOG_ERROR, + "Failed to allocate a libxl__json_object"); + return NULL; + } + + obj->type = type; + + if (type == JSON_MAP || type == JSON_ARRAY) { + flexarray_t *array = flexarray_make(1, 1); + if (array == NULL) { + LIBXL__LOG_ERRNO(libxl__gc_owner(gc), LIBXL__LOG_ERROR, + "Failed to allocate a flexarray"); + free(obj); + return NULL; + } + if (type == JSON_MAP) + obj->u.map = array; + else + obj->u.array = array; + } + + return obj; +} + +static int json_object_append_to(libxl__gc *gc, + libxl__json_object *obj, + libxl__json_object *dst) +{ + assert(dst != NULL); + + switch (dst->type) { + case JSON_MAP: { + libxl__json_map_node *last; + + if (dst->u.map->count == 0) { + LIBXL__LOG(libxl__gc_owner(gc), LIBXL__LOG_ERROR, + "Try to add a value to an empty map (with no key)"); + return -1; + } + flexarray_get(dst->u.map, dst->u.map->count - 1, (void**)&last); + last->obj = obj; + break; + } + case JSON_ARRAY: + if (flexarray_append(dst->u.array, obj) == 2) { + LIBXL__LOG_ERRNO(libxl__gc_owner(gc), LIBXL__LOG_ERROR, + "Failed to grow a flexarray"); + return -1; + } + break; + default: + LIBXL__LOG(libxl__gc_owner(gc), LIBXL__LOG_ERROR, + "Try append an object is not a map/array (%i)\n", + dst->type); + return -1; + } + + obj->parent = dst; + return 0; +} + +void libxl__json_object_free(libxl__gc *gc, libxl__json_object *obj) +{ + int index = 0; + + if (obj == NULL) + return; + switch (obj->type) { + case JSON_STRING: + free(obj->u.string); + break; + case JSON_MAP: { + libxl__json_map_node *node = NULL; + + for (index = 0; index < obj->u.map->count; index++) { + if (flexarray_get(obj->u.map, index, (void**)&node) != 0) + break; + libxl__json_object_free(gc, node->obj); + free(node->map_key); + free(node); + node = NULL; + } + flexarray_free(obj->u.map); + break; + } + case JSON_ARRAY: { + libxl__json_object *node = NULL; + break; + + for (index = 0; index < obj->u.array->count; index++) { + if (flexarray_get(obj->u.array, index, (void**)&node) != 0) + break; + libxl__json_object_free(gc, node); + node = NULL; + } + flexarray_free(obj->u.array); + break; + } + default: + break; + } + free(obj); +} + +libxl__json_object *libxl__json_array_get(const libxl__json_object *o, int i) +{ + flexarray_t *array = NULL; + libxl__json_object *obj = NULL; + + if ((array = libxl__json_object_get_array(o)) == NULL) { + return NULL; + } + + if (i >= array->count) + return NULL; + + if (flexarray_get(array, i, (void**)&obj) != 0) + return NULL; + + return obj; +} + +libxl__json_map_node *libxl__json_map_node_get(const libxl__json_object *o, + int i) +{ + flexarray_t *array = NULL; + libxl__json_map_node *obj = NULL; + + if ((array = libxl__json_object_get_map(o)) == NULL) { + return NULL; + } + + if (i >= array->count) + return NULL; + + if (flexarray_get(array, i, (void**)&obj) != 0) + return NULL; + + return obj; +} + +const libxl__json_object *libxl__json_map_get(const char *key, + const libxl__json_object *o, + libxl__json_node_type expected_type) +{ + flexarray_t *maps = NULL; + int index = 0; + + if (libxl__json_object_is_map(o)) { + libxl__json_map_node *node = NULL; + + maps = o->u.map; + for (index = 0; index < maps->count; index++) { + if (flexarray_get(maps, index, (void**)&node) != 0) + return NULL; + if (strcmp(key, node->map_key) == 0) { + if (expected_type == JSON_ANY + || (node->obj && node->obj->type == expected_type)) { + return node->obj; + } else { + return NULL; + } + } + } + } + return NULL; +} + + +/* + * JSON callbacks + */ + +static int json_callback_null(void *opaque) +{ + libxl__yajl_ctx *ctx = opaque; + libxl__json_object *obj; + + DEBUG_GEN(ctx, null); + + if ((obj = json_object_alloc(ctx->gc, JSON_NULL)) == NULL) + return 0; + + if (json_object_append_to(ctx->gc, obj, ctx->current) == -1) { + libxl__json_object_free(ctx->gc, obj); + return 0; + } + + return 1; +} + +static int json_callback_boolean(void *opaque, int boolean) +{ + libxl__yajl_ctx *ctx = opaque; + libxl__json_object *obj; + + DEBUG_GEN_VALUE(ctx, bool, boolean); + + if ((obj = json_object_alloc(ctx->gc, + boolean ? JSON_TRUE : JSON_FALSE)) == NULL) + return 0; + + if (json_object_append_to(ctx->gc, obj, ctx->current) == -1) { + libxl__json_object_free(ctx->gc, obj); + return 0; + } + + return 1; +} + +static int json_callback_integer(void *opaque, long value) +{ + libxl__yajl_ctx *ctx = opaque; + libxl__json_object *obj; + + DEBUG_GEN_VALUE(ctx, integer, value); + + if ((obj = json_object_alloc(ctx->gc, JSON_INTEGER)) == NULL) + return 0; + obj->u.i = value; + + if (json_object_append_to(ctx->gc, obj, ctx->current) == -1) { + libxl__json_object_free(ctx->gc, obj); + return 0; + } + + return 1; +} + +static int json_callback_double(void *opaque, double value) +{ + libxl__yajl_ctx *ctx = opaque; + libxl__json_object *obj; + + DEBUG_GEN_VALUE(ctx, double, value); + + if ((obj = json_object_alloc(ctx->gc, JSON_DOUBLE)) == NULL) + return 0; + obj->u.d = value; + + if (json_object_append_to(ctx->gc, obj, ctx->current) == -1) { + libxl__json_object_free(ctx->gc, obj); + return 0; + } + + return 1; +} + +static int json_callback_string(void *opaque, const unsigned char *str, + unsigned int len) +{ + libxl__yajl_ctx *ctx = opaque; + char *t = NULL; + libxl__json_object *obj = NULL; + + t = malloc(len + 1); + if (t == NULL) { + LIBXL__LOG_ERRNO(libxl__gc_owner(ctx->gc), LIBXL__LOG_ERROR, + "Failed to allocate"); + return 0; + } + + DEBUG_GEN_STRING(ctx, str, len); + + strncpy(t, (const char *) str, len); + t[len] = 0; + + if ((obj = json_object_alloc(ctx->gc, JSON_STRING)) == NULL) { + free(t); + return 0; + } + obj->u.string = t; + + if (json_object_append_to(ctx->gc, obj, ctx->current) == -1) { + libxl__json_object_free(ctx->gc, obj); + return 0; + } + + return 1; +} + +static int json_callback_map_key(void *opaque, const unsigned char *str, + unsigned int len) +{ + libxl__yajl_ctx *ctx = opaque; + char *t = NULL; + libxl__json_object *obj = ctx->current; + + t = malloc(len + 1); + if (t == NULL) { + LIBXL__LOG_ERRNO(libxl__gc_owner(ctx->gc), LIBXL__LOG_ERROR, + "Failed to allocate"); + return 0; + } + + DEBUG_GEN_STRING(ctx, str, len); + + strncpy(t, (const char *) str, len); + t[len] = 0; + + if (libxl__json_object_is_map(obj)) { + libxl__json_map_node *node = malloc(sizeof (libxl__json_map_node)); + if (node == NULL) { + LIBXL__LOG_ERRNO(libxl__gc_owner(ctx->gc), LIBXL__LOG_ERROR, + "Failed to allocate"); + return 0; + } + + node->map_key = t; + node->obj = NULL; + + if (flexarray_append(obj->u.map, node) == 2) { + LIBXL__LOG_ERRNO(libxl__gc_owner(ctx->gc), LIBXL__LOG_ERROR, + "Failed to grow a flexarray"); + return 0; + } + } else { + LIBXL__LOG(libxl__gc_owner(ctx->gc), LIBXL__LOG_ERROR, + "Current json object is not a map"); + return 0; + } + + return 1; +} + +static int json_callback_start_map(void *opaque) +{ + libxl__yajl_ctx *ctx = opaque; + libxl__json_object *obj = NULL; + + DEBUG_GEN(ctx, map_open); + + if ((obj = json_object_alloc(ctx->gc, JSON_MAP)) == NULL) + return 0; + + if (ctx->current) { + if (json_object_append_to(ctx->gc, obj, ctx->current) == -1) { + libxl__json_object_free(ctx->gc, obj); + return 0; + } + } + + ctx->current = obj; + if (ctx->head == NULL) { + ctx->head = obj; + } + + return 1; +} + +static int json_callback_end_map(void *opaque) +{ + libxl__yajl_ctx *ctx = opaque; + + DEBUG_GEN(ctx, map_close); + + if (ctx->current) { + ctx->current = ctx->current->parent; + } else { + LIBXL__LOG(libxl__gc_owner(ctx->gc), LIBXL__LOG_ERROR, + "No current libxl__json_object, cannot use his parent."); + return 0; + } + + return 1; +} + +static int json_callback_start_array(void *opaque) +{ + libxl__yajl_ctx *ctx = opaque; + libxl__json_object *obj = NULL; + + DEBUG_GEN(ctx, array_open); + + if ((obj = json_object_alloc(ctx->gc, JSON_ARRAY)) == NULL) + return 0; + + if (ctx->current) { + if (json_object_append_to(ctx->gc, obj, ctx->current) == -1) { + libxl__json_object_free(ctx->gc, obj); + return 0; + } + } + + ctx->current = obj; + if (ctx->head == NULL) { + ctx->head = obj; + } + + return 1; +} + +static int json_callback_end_array(void *opaque) +{ + libxl__yajl_ctx *ctx = opaque; + + DEBUG_GEN(ctx, array_close); + + if (ctx->current) { + ctx->current = ctx->current->parent; + } else { + LIBXL__LOG(libxl__gc_owner(ctx->gc), LIBXL__LOG_ERROR, + "No current libxl__json_object, cannot use his parent."); + return 0; + } + + return 1; +} + +static yajl_callbacks callbacks = { + json_callback_null, + json_callback_boolean, + json_callback_integer, + json_callback_double, + NULL, + json_callback_string, + json_callback_start_map, + json_callback_map_key, + json_callback_end_map, + json_callback_start_array, + json_callback_end_array +}; + +static void yajl_ctx_free(libxl__yajl_ctx *yajl_ctx) +{ + if (yajl_ctx->hand) { + yajl_free(yajl_ctx->hand); + yajl_ctx->hand = NULL; + } + DEBUG_GEN_FREE(yajl_ctx); +} + +libxl__json_object *libxl__json_parse(libxl__gc *gc, const char *s) +{ + yajl_status status; + libxl__yajl_ctx yajl_ctx; + + memset(&yajl_ctx, 0, sizeof (yajl_ctx)); + yajl_ctx.gc = gc; + + DEBUG_GEN_ALLOC(&yajl_ctx); + + if (yajl_ctx.hand == NULL) { + yajl_parser_config cfg = { + .allowComments = 1, + .checkUTF8 = 1, + }; + yajl_ctx.hand = yajl_alloc(&callbacks, &cfg, NULL, &yajl_ctx); + } + status = yajl_parse(yajl_ctx.hand, (const unsigned char *)s, strlen(s)); + status = yajl_parse_complete(yajl_ctx.hand); + + if (status == yajl_status_ok) { + libxl__json_object *o = yajl_ctx.head; + + DEBUG_GEN_REPORT(&yajl_ctx); + + yajl_ctx.head = NULL; + + yajl_ctx_free(&yajl_ctx); + return o; + } else { + unsigned char *str = yajl_get_error(yajl_ctx.hand, 1, + (const unsigned char *)s, + strlen(s)); + + LIBXL__LOG(libxl__gc_owner(gc), LIBXL__LOG_ERROR, + "yajl error: %s", str); + yajl_free_error(yajl_ctx.hand, str); + + libxl__json_object_free(gc, yajl_ctx.head); + yajl_ctx_free(&yajl_ctx); + return NULL; + } +} -- cgit v1.2.3