From 21f0a22072f0543c416c440665bcf75ae09e6233 Mon Sep 17 00:00:00 2001 From: Pino Toscano Date: Mon, 12 Feb 2018 11:24:06 +0100 Subject: [PATCH] Switch from YAJL to Jansson While YAJL mostly works fine, it did not see any active development in the last 3 years. OTOH, Jansson is another JSON C implementation, with a very liberal license, and a much nicer API. Hence, switch all of libguestfs from YAJL to Jansson: - configure checks, and buildsystem in general - packages pulled in the appliance - actual implementations - contrib scripts - documentation This also makes use of the better APIs available (e.g. json_object_get, json_array_foreach, and json_object_foreach). This does not change the API of our OCaml Yajl module. (cherry picked from commit bd1c5c9f4dcf38458099db8a0bf4659a07ef055d) --- appliance/packagelist.in | 13 ++-- builder/Makefile.am | 4 +- builder/yajl-c.c | 66 ++++++++-------- contrib/p2v/aux-scripts/do-build.sh | 8 +- contrib/p2v/build-p2v-iso.sh | 3 +- daemon/Makefile.am | 4 +- daemon/ldm.c | 113 ++++++++++++---------------- docs/guestfs-building.pod | 2 +- lib/Makefile.am | 4 +- lib/info.c | 113 +++++++++++----------------- lib/qemu.c | 64 ++++++---------- m4/guestfs-libraries.m4 | 4 +- 12 files changed, 168 insertions(+), 230 deletions(-) diff --git a/appliance/packagelist.in b/appliance/packagelist.in index 8ded2588a..f92a6ce95 100644 --- a/appliance/packagelist.in +++ b/appliance/packagelist.in @@ -35,6 +35,7 @@ ifelse(REDHAT,1, hivex iproute iputils + jansson kernel libcap libldm @@ -51,7 +52,6 @@ ifelse(REDHAT,1, systemd dnl for /sbin/reboot and udevd vim-minimal xz - yajl yara zfs-fuse ) @@ -82,12 +82,12 @@ dnl iproute has been renamed to iproute2 libc-bin libcap2 libhivex0 + libjansson4 libpcre3 libsystemd0 libsystemd-id128-0 libsystemd-journal0 libtirpc1 - libyajl2 libyara3 linux-image dnl syslinux 'suggests' mtools, but in reality it's a hard dependency: @@ -116,6 +116,7 @@ ifelse(ARCHLINUX,1, hivex iproute2 iputils + jansson libcap libtirpc linux @@ -131,7 +132,6 @@ ifelse(ARCHLINUX,1, systemd vim xz - yajl yara ) @@ -153,9 +153,9 @@ ifelse(SUSE,1, iputils libcap2 libhivex0 + libjansson4 libselinux1 libtirpc3 - libyajl2 libyara3 mkisofs ntfsprogs @@ -177,6 +177,7 @@ ifelse(FRUGALWARE,1, hfsplus iproute2 iputils + jansson kernel libcap libtirpc @@ -188,7 +189,6 @@ ifelse(FRUGALWARE,1, systemd vim xz - yajl xfsprogs-acl xfsprogs-attr gptfdisk @@ -209,9 +209,9 @@ ifelse(MAGEIA,1, iproute2 iputils libcap + libjansson4 libldm libtirpc - libyajl dnl syslinux uses mtools without depending on it mtools nilfs-utils @@ -224,7 +224,6 @@ ifelse(MAGEIA,1, systemd /* for /sbin/reboot and udevd */ vim-minimal xz - yajl ) acl diff --git a/builder/Makefile.am b/builder/Makefile.am index e5872bdd9..c7b50778a 100644 --- a/builder/Makefile.am +++ b/builder/Makefile.am @@ -157,7 +157,7 @@ virt_builder_CFLAGS = \ $(LIBLZMA_CFLAGS) \ $(LIBTINFO_CFLAGS) \ $(LIBXML2_CFLAGS) \ - $(YAJL_CFLAGS) + $(JANSSON_CFLAGS) BOBJECTS = $(SOURCES_ML:.ml=.cmo) XOBJECTS = $(BOBJECTS:.cmo=.cmx) @@ -211,7 +211,7 @@ OCAMLCLIBS = \ $(LIBCRYPT_LIBS) \ $(LIBLZMA_LIBS) \ $(LIBXML2_LIBS) \ - $(YAJL_LIBS) \ + $(JANSSON_LIBS) \ $(LIBINTL) \ -lgnu diff --git a/builder/yajl-c.c b/builder/yajl-c.c index 3c2402e42..e53755f55 100644 --- a/builder/yajl-c.c +++ b/builder/yajl-c.c @@ -23,24 +23,17 @@ #include #include -#include +#include #include #include -/* GCC can't work out that the YAJL_IS_ test is sufficient to - * ensure that YAJL_GET_ later doesn't return NULL. - */ -#if defined(__GNUC__) && __GNUC__ >= 6 /* gcc >= 6 */ -#pragma GCC diagnostic ignored "-Wnull-dereference" -#endif - #define Val_none (Val_int (0)) value virt_builder_yajl_tree_parse (value stringv); static value -convert_yajl_value (yajl_val val, int level) +convert_json_t (json_t *val, int level) { CAMLparam0 (); CAMLlocal4 (rv, lv, v, sv); @@ -48,46 +41,51 @@ convert_yajl_value (yajl_val val, int level) if (level > 20) caml_invalid_argument ("too many levels of object/array nesting"); - if (YAJL_IS_OBJECT (val)) { - const size_t len = YAJL_GET_OBJECT(val)->len; + if (json_is_object (val)) { + const size_t len = json_object_size (val); size_t i; + const char *key; + json_t *jvalue; rv = caml_alloc (1, 3); lv = caml_alloc_tuple (len); - for (i = 0; i < len; ++i) { + i = 0; + json_object_foreach (val, key, jvalue) { v = caml_alloc_tuple (2); - sv = caml_copy_string (YAJL_GET_OBJECT(val)->keys[i]); + sv = caml_copy_string (key); Store_field (v, 0, sv); - sv = convert_yajl_value (YAJL_GET_OBJECT(val)->values[i], level + 1); + sv = convert_json_t (jvalue, level + 1); Store_field (v, 1, sv); Store_field (lv, i, v); + ++i; } Store_field (rv, 0, lv); - } else if (YAJL_IS_ARRAY (val)) { - const size_t len = YAJL_GET_ARRAY(val)->len; + } else if (json_is_array (val)) { + const size_t len = json_array_size (val); size_t i; + json_t *jvalue; rv = caml_alloc (1, 4); lv = caml_alloc_tuple (len); - for (i = 0; i < len; ++i) { - v = convert_yajl_value (YAJL_GET_ARRAY(val)->values[i], level + 1); + json_array_foreach (val, i, jvalue) { + v = convert_json_t (jvalue, level + 1); Store_field (lv, i, v); } Store_field (rv, 0, lv); - } else if (YAJL_IS_STRING (val)) { + } else if (json_is_string (val)) { rv = caml_alloc (1, 0); - v = caml_copy_string (YAJL_GET_STRING(val)); + v = caml_copy_string (json_string_value (val)); Store_field (rv, 0, v); - } else if (YAJL_IS_DOUBLE (val)) { + } else if (json_is_real (val)) { rv = caml_alloc (1, 2); - v = caml_copy_double (YAJL_GET_DOUBLE(val)); + v = caml_copy_double (json_real_value (val)); Store_field (rv, 0, v); - } else if (YAJL_IS_INTEGER (val)) { + } else if (json_is_integer (val)) { rv = caml_alloc (1, 1); - v = caml_copy_int64 (YAJL_GET_INTEGER(val)); + v = caml_copy_int64 (json_integer_value (val)); Store_field (rv, 0, v); - } else if (YAJL_IS_TRUE (val)) { + } else if (json_is_true (val)) { rv = caml_alloc (1, 5); Store_field (rv, 0, Val_true); - } else if (YAJL_IS_FALSE (val)) { + } else if (json_is_false (val)) { rv = caml_alloc (1, 5); Store_field (rv, 0, Val_false); } else @@ -101,21 +99,21 @@ virt_builder_yajl_tree_parse (value stringv) { CAMLparam1 (stringv); CAMLlocal1 (rv); - yajl_val tree; - char error_buf[256]; + json_t *tree; + json_error_t err; - tree = yajl_tree_parse (String_val (stringv), error_buf, sizeof error_buf); + tree = json_loads (String_val (stringv), JSON_DECODE_ANY, &err); if (tree == NULL) { - char buf[256 + sizeof error_buf]; - if (strlen (error_buf) > 0) - snprintf (buf, sizeof buf, "JSON parse error: %s", error_buf); + char buf[256 + JSON_ERROR_TEXT_LENGTH]; + if (strlen (err.text) > 0) + snprintf (buf, sizeof buf, "JSON parse error: %s", err.text); else snprintf (buf, sizeof buf, "unknown JSON parse error"); caml_invalid_argument (buf); } - rv = convert_yajl_value (tree, 1); - yajl_tree_free (tree); + rv = convert_json_t (tree, 1); + json_decref (tree); CAMLreturn (rv); } diff --git a/contrib/p2v/aux-scripts/do-build.sh b/contrib/p2v/aux-scripts/do-build.sh index 5edb53d0e..dd6424bb4 100644 --- a/contrib/p2v/aux-scripts/do-build.sh +++ b/contrib/p2v/aux-scripts/do-build.sh @@ -53,8 +53,8 @@ case $osversion in # This just forces configure to ignore these missing dependencies. export LIBTINFO_CFLAGS=-D_GNU_SOURCE export LIBTINFO_LIBS=-lncurses - export YAJL_CFLAGS=-D_GNU_SOURCE - export YAJL_LIBS=-lyajl + export JANSSON_CFLAGS=-D_GNU_SOURCE + export JANSSON_LIBS=-ljansson # Remove some unsupported flags that the configure script hard codes. sed -i -e 's/-fno-strict-overflow//' configure sed -i -e 's/-Wno-strict-overflow//' configure @@ -66,8 +66,8 @@ case $osversion in # This just forces configure to ignore these missing dependencies. export LIBTINFO_CFLAGS=-D_GNU_SOURCE export LIBTINFO_LIBS=-lncurses - export YAJL_CFLAGS=-D_GNU_SOURCE - export YAJL_LIBS=-lyajl + export JANSSON_CFLAGS=-D_GNU_SOURCE + export JANSSON_LIBS=-ljansson ;; esac diff --git a/contrib/p2v/build-p2v-iso.sh b/contrib/p2v/build-p2v-iso.sh index c80a1b134..ae25cebc8 100755 --- a/contrib/p2v/build-p2v-iso.sh +++ b/contrib/p2v/build-p2v-iso.sh @@ -86,7 +86,6 @@ done # Various hacks for different versions of RHEL. if=virtio netdev=virtio-net-pci -pkgs="$pkgs,yajl-devel" declare -a epel case $osversion in rhel-5.*|centos-5.*) @@ -105,10 +104,12 @@ case $osversion in rhel-6.*|centos-6.*) epel[0]="--run-command" epel[1]="yum install -y --nogpgcheck https://dl.fedoraproject.org/pub/epel/epel-release-latest-6.noarch.rpm" + pkgs="$pkgs,jansson-devel" ;; rhel-7.*|centos-7.*) epel[0]="--run-command" epel[1]="yum install -y --nogpgcheck https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm" + pkgs="$pkgs,jansson-devel" ;; esac diff --git a/daemon/Makefile.am b/daemon/Makefile.am index 6240f517d..d9ed5625e 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -218,7 +218,7 @@ guestfsd_LDADD = \ camldaemon.o \ $(ACL_LIBS) \ $(CAP_LIBS) \ - $(YAJL_LIBS) \ + $(JANSSON_LIBS) \ $(SELINUX_LIBS) \ $(AUGEAS_LIBS) \ $(HIVEX_LIBS) \ @@ -256,7 +256,7 @@ guestfsd_CFLAGS = \ $(AUGEAS_CFLAGS) \ $(HIVEX_CFLAGS) \ $(SD_JOURNAL_CFLAGS) \ - $(YAJL_CFLAGS) \ + $(JANSSON_CFLAGS) \ $(PCRE_CFLAGS) # Parts of the daemon are written in OCaml. These are linked into a diff --git a/daemon/ldm.c b/daemon/ldm.c index 2f4d2aef3..be4fb9701 100644 --- a/daemon/ldm.c +++ b/daemon/ldm.c @@ -25,19 +25,12 @@ #include #include -#include +#include #include "daemon.h" #include "actions.h" #include "optgroups.h" -/* GCC can't work out that the YAJL_IS_ test is sufficient to - * ensure that YAJL_GET_ later doesn't return NULL. - */ -#if defined(__GNUC__) && GUESTFS_GCC_VERSION >= 60000 /* gcc >= 6 */ -#pragma GCC diagnostic ignored "-Wnull-dereference" -#endif - int optgroup_ldm_available (void) { @@ -72,44 +65,42 @@ do_ldmtool_remove_all (void) return 0; } -static yajl_val +static json_t * parse_json (const char *json, const char *func) { - yajl_val tree; - char parse_error[1024]; + json_t *tree; + json_error_t err; if (verbose) fprintf (stderr, "%s: parsing json: %s\n", func, json); - tree = yajl_tree_parse (json, parse_error, sizeof parse_error); + tree = json_loads (json, 0, &err); if (tree == NULL) { reply_with_error ("parse error: %s", - strlen (parse_error) ? parse_error : "unknown error"); + strlen (err.text) ? err.text : "unknown error"); return NULL; } - /* Caller should free this by doing 'yajl_tree_free (tree);'. */ + /* Caller should free this by doing 'json_decref (tree);'. */ return tree; } #define TYPE_ERROR ((char **) -1) static char ** -json_value_to_string_list (yajl_val node) +json_value_to_string_list (json_t *node) { CLEANUP_FREE_STRINGSBUF DECLARE_STRINGSBUF (strs); - yajl_val n; - size_t i, len; + json_t *n; + size_t i; - if (! YAJL_IS_ARRAY (node)) + if (!json_is_array (node)) return TYPE_ERROR; - len = YAJL_GET_ARRAY (node)->len; - for (i = 0; i < len; ++i) { - n = YAJL_GET_ARRAY (node)->values[i]; - if (! YAJL_IS_STRING (n)) + json_array_foreach (node, i, n) { + if (!json_is_string (n)) return TYPE_ERROR; - if (add_string (&strs, YAJL_GET_STRING (n)) == -1) + if (add_string (&strs, json_string_value (n)) == -1) return NULL; } if (end_stringsbuf (&strs) == -1) @@ -123,14 +114,14 @@ parse_json_get_string_list (const char *json, const char *func, const char *cmd) { char **ret; - yajl_val tree = NULL; + json_t *tree = NULL; tree = parse_json (json, func); if (tree == NULL) return NULL; ret = json_value_to_string_list (tree); - yajl_tree_free (tree); + json_decref (tree); if (ret == TYPE_ERROR) { reply_with_error ("output of '%s' was not a JSON array of strings", cmd); return NULL; @@ -144,43 +135,40 @@ static char * parse_json_get_object_string (const char *json, const char *key, int flags, const char *func, const char *cmd) { - char *str, *ret; - yajl_val tree = NULL, node; - size_t i, len; + const char *str; + char *ret; + json_t *tree = NULL, *node; tree = parse_json (json, func); if (tree == NULL) return NULL; - if (! YAJL_IS_OBJECT (tree)) + if (!json_is_object (tree)) goto bad_type; - len = YAJL_GET_OBJECT (tree)->len; - for (i = 0; i < len; ++i) { - if (STREQ (YAJL_GET_OBJECT (tree)->keys[i], key)) { - node = YAJL_GET_OBJECT (tree)->values[i]; - - if ((flags & GET_STRING_NULL_TO_EMPTY) && YAJL_IS_NULL (node)) - ret = strdup (""); - else { - str = YAJL_GET_STRING (node); - if (str == NULL) - goto bad_type; - ret = strdup (str); - } - if (ret == NULL) - reply_with_perror ("strdup"); - - yajl_tree_free (tree); + node = json_object_get (tree, key); + if (node == NULL) + goto bad_type; - return ret; - } + if ((flags & GET_STRING_NULL_TO_EMPTY) && json_is_null (node)) + ret = strdup (""); + else { + str = json_string_value (node); + if (str == NULL) + goto bad_type; + ret = strndup (str, json_string_length (node)); } + if (ret == NULL) + reply_with_perror ("strdup"); + + json_decref (tree); + + return ret; bad_type: reply_with_error ("output of '%s' was not a JSON object " "containing a key '%s' of type string", cmd, key); - yajl_tree_free (tree); + json_decref (tree); return NULL; } @@ -189,33 +177,30 @@ parse_json_get_object_string_list (const char *json, const char *key, const char *func, const char *cmd) { char **ret; - yajl_val tree, node; - size_t i, len; + json_t *tree, *node; tree = parse_json (json, func); if (tree == NULL) return NULL; - if (! YAJL_IS_OBJECT (tree)) + if (!json_is_object (tree)) goto bad_type; - len = YAJL_GET_OBJECT (tree)->len; - for (i = 0; i < len; ++i) { - if (STREQ (YAJL_GET_OBJECT (tree)->keys[i], key)) { - node = YAJL_GET_OBJECT (tree)->values[i]; - ret = json_value_to_string_list (node); - if (ret == TYPE_ERROR) - goto bad_type; - yajl_tree_free (tree); - return ret; - } - } + node = json_object_get (tree, key); + if (node == NULL) + goto bad_type; + + ret = json_value_to_string_list (node); + if (ret == TYPE_ERROR) + goto bad_type; + json_decref (tree); + return ret; bad_type: reply_with_error ("output of '%s' was not a JSON object " "containing a key '%s' of type array of strings", cmd, key); - yajl_tree_free (tree); + json_decref (tree); return NULL; } diff --git a/docs/guestfs-building.pod b/docs/guestfs-building.pod index d350b1d73..2029429fd 100644 --- a/docs/guestfs-building.pod +++ b/docs/guestfs-building.pod @@ -172,7 +172,7 @@ I. I. -=item yajl E 2.0.4 +=item Jansson I. diff --git a/lib/Makefile.am b/lib/Makefile.am index 91c4e0a2e..d075174d9 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -141,7 +141,7 @@ libguestfs_la_CFLAGS = \ $(PCRE_CFLAGS) \ $(LIBVIRT_CFLAGS) \ $(LIBXML2_CFLAGS) \ - $(YAJL_CFLAGS) + $(JANSSON_CFLAGS) libguestfs_la_LIBADD = \ ../common/errnostring/liberrnostring.la \ @@ -152,7 +152,7 @@ libguestfs_la_LIBADD = \ $(PCRE_LIBS) \ $(LIBVIRT_LIBS) $(LIBXML2_LIBS) \ $(SELINUX_LIBS) \ - $(YAJL_LIBS) \ + $(JANSSON_LIBS) \ ../gnulib/lib/libgnu.la \ $(GETADDRINFO_LIB) \ $(HOSTENT_LIB) \ diff --git a/lib/info.c b/lib/info.c index 93fadcd39..2eadc1c11 100644 --- a/lib/info.c +++ b/lib/info.c @@ -37,53 +37,46 @@ #include #endif -#include +#include #include "guestfs.h" #include "guestfs-internal.h" #include "guestfs-internal-actions.h" #ifdef HAVE_ATTRIBUTE_CLEANUP -#define CLEANUP_YAJL_TREE_FREE __attribute__((cleanup(cleanup_yajl_tree_free))) +#define CLEANUP_JSON_T_DECREF __attribute__((cleanup(cleanup_json_t_decref))) static void -cleanup_yajl_tree_free (void *ptr) +cleanup_json_t_decref (void *ptr) { - yajl_tree_free (* (yajl_val *) ptr); + json_decref (* (json_t **) ptr); } #else -#define CLEANUP_YAJL_TREE_FREE +#define CLEANUP_JSON_T_DECREF #endif -static yajl_val get_json_output (guestfs_h *g, const char *filename); +static json_t *get_json_output (guestfs_h *g, const char *filename); static void set_child_rlimits (struct command *); char * guestfs_impl_disk_format (guestfs_h *g, const char *filename) { - size_t i, len; - CLEANUP_YAJL_TREE_FREE yajl_val tree = get_json_output (g, filename); + CLEANUP_JSON_T_DECREF json_t *tree = get_json_output (g, filename); + json_t *node; if (tree == NULL) return NULL; - if (! YAJL_IS_OBJECT (tree)) + if (!json_is_object (tree)) goto bad_type; - len = YAJL_GET_OBJECT(tree)->len; - for (i = 0; i < len; ++i) { - if (STREQ (YAJL_GET_OBJECT(tree)->keys[i], "format")) { - const char *str; - yajl_val node = YAJL_GET_OBJECT(tree)->values[i]; - if (YAJL_IS_NULL (node)) - goto bad_type; - str = YAJL_GET_STRING (node); - if (str == NULL) - goto bad_type; - return safe_strdup (g, str); /* caller frees */ - } - } + node = json_object_get (tree, "format"); + if (!json_is_string (node)) + goto bad_type; + + return safe_strndup (g, json_string_value (node), + json_string_length (node)); /* caller frees */ bad_type: error (g, _("qemu-img info: JSON output did not contain ‘format’ key")); @@ -93,30 +86,20 @@ guestfs_impl_disk_format (guestfs_h *g, const char *filename) int64_t guestfs_impl_disk_virtual_size (guestfs_h *g, const char *filename) { - size_t i, len; - CLEANUP_YAJL_TREE_FREE yajl_val tree = get_json_output (g, filename); + CLEANUP_JSON_T_DECREF json_t *tree = get_json_output (g, filename); + json_t *node; if (tree == NULL) return -1; - if (! YAJL_IS_OBJECT (tree)) + if (!json_is_object (tree)) goto bad_type; - len = YAJL_GET_OBJECT(tree)->len; - for (i = 0; i < len; ++i) { - if (STREQ (YAJL_GET_OBJECT(tree)->keys[i], "virtual-size")) { - yajl_val node = YAJL_GET_OBJECT(tree)->values[i]; - if (YAJL_IS_NULL (node)) - goto bad_type; - if (! YAJL_IS_NUMBER (node)) - goto bad_type; - if (! YAJL_IS_INTEGER (node)) { - error (g, _("qemu-img info: ‘virtual-size’ is not representable as a 64 bit integer")); - return -1; - } - return YAJL_GET_INTEGER (node); - } - } + node = json_object_get (tree, "virtual-size"); + if (!json_is_integer (node)) + goto bad_type; + + return json_integer_value (node); bad_type: error (g, _("qemu-img info: JSON output did not contain ‘virtual-size’ key")); @@ -126,29 +109,25 @@ guestfs_impl_disk_virtual_size (guestfs_h *g, const char *filename) int guestfs_impl_disk_has_backing_file (guestfs_h *g, const char *filename) { - size_t i, len; - CLEANUP_YAJL_TREE_FREE yajl_val tree = get_json_output (g, filename); + CLEANUP_JSON_T_DECREF json_t *tree = get_json_output (g, filename); + json_t *node; if (tree == NULL) return -1; - if (! YAJL_IS_OBJECT (tree)) + if (!json_is_object (tree)) goto bad_type; - len = YAJL_GET_OBJECT(tree)->len; - for (i = 0; i < len; ++i) { - if (STREQ (YAJL_GET_OBJECT(tree)->keys[i], "backing-filename")) { - yajl_val node = YAJL_GET_OBJECT(tree)->values[i]; - /* Work on the assumption that if this field is null, it means - * no backing file, rather than being an error. - */ - if (YAJL_IS_NULL (node)) - return 0; - return 1; - } - } + node = json_object_get (tree, "backing-filename"); + if (node == NULL) + return 0; /* no backing-filename key means no backing file */ - return 0; /* no backing-filename key means no backing file */ + /* Work on the assumption that if this field is null, it means + * no backing file, rather than being an error. + */ + if (json_is_null (node)) + return 0; + return 1; bad_type: error (g, _("qemu-img info: JSON output was not an object")); @@ -161,12 +140,12 @@ guestfs_impl_disk_has_backing_file (guestfs_h *g, const char *filename) static void parse_json (guestfs_h *g, void *treevp, const char *input, size_t len); #define PARSE_JSON_NO_OUTPUT ((void *) -1) -static yajl_val +static json_t * get_json_output (guestfs_h *g, const char *filename) { CLEANUP_CMD_CLOSE struct command *cmd = guestfs_int_new_command (g); int r; - yajl_val tree = NULL; + json_t *tree = NULL; guestfs_int_cmd_add_arg (cmd, "qemu-img"); guestfs_int_cmd_add_arg (cmd, "info"); @@ -196,16 +175,15 @@ get_json_output (guestfs_h *g, const char *filename) return NULL; } - return tree; /* caller must call yajl_tree_free (tree) */ + return tree; /* caller must call json_decref (tree) */ } /* Parse the JSON document printed by qemu-img info --output json. */ static void parse_json (guestfs_h *g, void *treevp, const char *input, size_t len) { - yajl_val *tree_ret = treevp; - CLEANUP_FREE char *input_copy = NULL; - char parse_error[256]; + json_t **tree_ret = treevp; + json_error_t err; assert (*tree_ret == NULL); @@ -218,15 +196,12 @@ parse_json (guestfs_h *g, void *treevp, const char *input, size_t len) return; } - /* 'input' is not \0-terminated; we have to make it so. */ - input_copy = safe_strndup (g, input, len); + debug (g, "%s: qemu-img info JSON output:\n%.*s\n", __func__, (int) len, input); - debug (g, "%s: qemu-img info JSON output:\n%s\n", __func__, input_copy); - - *tree_ret = yajl_tree_parse (input_copy, parse_error, sizeof parse_error); + *tree_ret = json_loadb (input, len, 0, &err); if (*tree_ret == NULL) { - if (strlen (parse_error) > 0) - error (g, _("qemu-img info: JSON parse error: %s"), parse_error); + if (strlen (err.text) > 0) + error (g, _("qemu-img info: JSON parse error: %s"), err.text); else error (g, _("qemu-img info: unknown JSON parse error")); } diff --git a/lib/qemu.c b/lib/qemu.c index 6159b5a52..a50eca988 100644 --- a/lib/qemu.c +++ b/lib/qemu.c @@ -37,7 +37,7 @@ #include -#include +#include #include "full-write.h" #include "ignore-value.h" @@ -57,7 +57,7 @@ struct qemu_data { /* The following fields are derived from the fields above. */ struct version qemu_version; /* Parsed qemu version number. */ - yajl_val qmp_schema_tree; /* qmp_schema parsed into a JSON tree */ + json_t *qmp_schema_tree; /* qmp_schema parsed into a JSON tree */ }; static char *cache_filename (guestfs_h *g, const char *cachedir, const struct stat *, const char *suffix); @@ -73,7 +73,7 @@ static int write_cache_qmp_schema (guestfs_h *g, const struct qemu_data *data, c static int read_cache_qemu_stat (guestfs_h *g, struct qemu_data *data, const char *filename); static int write_cache_qemu_stat (guestfs_h *g, const struct qemu_data *data, const char *filename); static void parse_qemu_version (guestfs_h *g, const char *, struct version *qemu_version); -static void parse_json (guestfs_h *g, const char *, yajl_val *); +static void parse_json (guestfs_h *g, const char *, json_t **); static void read_all (guestfs_h *g, void *retv, const char *buf, size_t len); static int generic_read_cache (guestfs_h *g, const char *filename, char **strp); static int generic_write_cache (guestfs_h *g, const char *filename, const char *str); @@ -405,17 +405,17 @@ parse_qemu_version (guestfs_h *g, const char *qemu_help, * is not possible. */ static void -parse_json (guestfs_h *g, const char *json, yajl_val *treep) +parse_json (guestfs_h *g, const char *json, json_t **treep) { - char parse_error[256] = ""; + json_error_t err; if (!json) return; - *treep = yajl_tree_parse (json, parse_error, sizeof parse_error); + *treep = json_loads (json, 0, &err); if (*treep == NULL) { - if (strlen (parse_error) > 0) - debug (g, "QMP parse error: %s (ignored)", parse_error); + if (strlen (err.text) > 0) + debug (g, "QMP parse error: %s (ignored)", err.text); else debug (g, "QMP unknown parse error (ignored)"); } @@ -580,17 +580,6 @@ guestfs_int_qemu_supports_device (guestfs_h *g, return strstr (data->qemu_devices, device_name) != NULL; } -/* GCC can't work out that the YAJL_IS_ test is sufficient to - * ensure that YAJL_GET_ later doesn't return NULL. - */ -#pragma GCC diagnostic push -#if defined(__GNUC__) && __GNUC__ >= 6 /* gcc >= 6 */ -#pragma GCC diagnostic ignored "-Wnull-dereference" -#endif -#if defined(__GNUC__) && GUESTFS_GCC_VERSION >= 40800 /* gcc >= 4.8.0 */ -#pragma GCC diagnostic ignored "-Wnonnull" -#endif - /** * Test if the qemu binary uses mandatory file locking, added in * QEMU >= 2.10 (but sometimes disabled). @@ -599,11 +588,7 @@ int guestfs_int_qemu_mandatory_locking (guestfs_h *g, const struct qemu_data *data) { - const char *return_path[] = { "return", NULL }; - const char *meta_type_path[] = { "meta-type", NULL }; - const char *members_path[] = { "members", NULL }; - const char *name_path[] = { "name", NULL }; - yajl_val schema, v, meta_type, members, m, name; + json_t *schema, *v, *meta_type, *members, *m, *name; size_t i, j; /* If there's no QMP schema, fall back to checking the version. */ @@ -616,27 +601,24 @@ guestfs_int_qemu_mandatory_locking (guestfs_h *g, * Extract the schema from the wrapper. Note the returned ‘schema’ * will be an array. */ - schema = yajl_tree_get (data->qmp_schema_tree, return_path, yajl_t_array); - if (schema == NULL) + schema = json_object_get (data->qmp_schema_tree, "return"); + if (!json_is_array (schema)) goto fallback; - assert (YAJL_IS_ARRAY(schema)); /* Now look for any member of the array which has: * { "meta-type": "object", * "members": [ ... { "name": "locking", ... } ... ] ... } */ - for (i = 0; i < YAJL_GET_ARRAY(schema)->len; ++i) { - v = YAJL_GET_ARRAY(schema)->values[i]; - meta_type = yajl_tree_get (v, meta_type_path, yajl_t_string); - if (meta_type && YAJL_IS_STRING (meta_type) && - STREQ (YAJL_GET_STRING (meta_type), "object")) { - members = yajl_tree_get (v, members_path, yajl_t_array); - if (members) { - for (j = 0; j < YAJL_GET_ARRAY(members)->len; ++j) { - m = YAJL_GET_ARRAY(members)->values[j]; - name = yajl_tree_get (m, name_path, yajl_t_string); - if (name && YAJL_IS_STRING (name) && - STREQ (YAJL_GET_STRING (name), "locking")) + json_array_foreach (schema, i, v) { + meta_type = json_object_get (v, "meta-type"); + if (json_is_string (meta_type) && + STREQ (json_string_value (meta_type), "object")) { + members = json_object_get (v, "members"); + if (json_is_array (members)) { + json_array_foreach (members, j, m) { + name = json_object_get (v, "name"); + if (json_is_string (name) && + STREQ (json_string_value (name), "locking")) return 1; } } @@ -646,8 +628,6 @@ guestfs_int_qemu_mandatory_locking (guestfs_h *g, return 0; } -#pragma GCC diagnostic pop - /** * Escape a qemu parameter. * @@ -996,7 +976,7 @@ guestfs_int_free_qemu_data (struct qemu_data *data) free (data->qemu_help); free (data->qemu_devices); free (data->qmp_schema); - yajl_tree_free (data->qmp_schema_tree); + json_decref (data->qmp_schema_tree); free (data); } } diff --git a/m4/guestfs-libraries.m4 b/m4/guestfs-libraries.m4 index 78a9e792f..2fdbd9669 100644 --- a/m4/guestfs-libraries.m4 +++ b/m4/guestfs-libraries.m4 @@ -299,8 +299,8 @@ LIBS="$LIBS $LIBXML2_LIBS" AC_CHECK_FUNCS([xmlBufferDetach]) LIBS="$old_LIBS" -dnl Check for yajl JSON library (required). -PKG_CHECK_MODULES([YAJL], [yajl >= 2.0.4]) +dnl Check for Jansson JSON library (required). +PKG_CHECK_MODULES([JANSSON], [jansson]) dnl Check for C++ (optional, we just use this to test the header works). AC_PROG_CXX -- 2.21.0