[virt-tools-list] [PATCH libosinfo 3/4] Add an osinfo-db-validate command for verifying XML files

Daniel Veillard veillard at redhat.com
Fri Feb 24 08:01:09 UTC 2012


On Thu, Feb 23, 2012 at 05:20:01PM +0000, Daniel P. Berrange wrote:
> From: "Daniel P. Berrange" <berrange at redhat.com>
> 
> The osinfo-db-validate command takes a list of paths or URIs
> on the command line and validates them against the RNG
> schema
> ---
>  .gitignore                 |    3 +
>  libosinfo.spec.in          |    2 +
>  mingw32-libosinfo.spec.in  |    3 +-
>  tools/Makefile.am          |   21 +++-
>  tools/osinfo-db-validate.c |  343 ++++++++++++++++++++++++++++++++++++++++++++
>  5 files changed, 368 insertions(+), 4 deletions(-)
>  create mode 100644 tools/osinfo-db-validate.c
> 
> diff --git a/.gitignore b/.gitignore
> index b40076d..e13208e 100644
> --- a/.gitignore
> +++ b/.gitignore
> @@ -56,3 +56,6 @@ gtk-doc.make
>  data/pci.ids
>  data/usb.ids
>  tools/osinfo-detect
> +tools/osinfo-db-validate
> +tools/osinfo-db-validate.exe
> +tools/osinfo-db-validate.1
> diff --git a/libosinfo.spec.in b/libosinfo.spec.in
> index 192b660..00c6741 100644
> --- a/libosinfo.spec.in
> +++ b/libosinfo.spec.in
> @@ -99,6 +99,7 @@ rm -fr %{buildroot}
>  %{_bindir}/osinfo-pciids-convert
>  %{_bindir}/osinfo-usbids-convert
>  %{_bindir}/osinfo-detect
> +%{_bindir}/osinfo-db-validate
>  %dir %{_datadir}/libosinfo/
>  %dir %{_datadir}/libosinfo/data/
>  %dir %{_datadir}/libosinfo/schemas/
> @@ -108,6 +109,7 @@ rm -fr %{buildroot}
>  %{_datadir}/libosinfo/data/oses
>  %{_datadir}/libosinfo/data/hypervisors
>  %{_datadir}/libosinfo/schemas/libosinfo.rng
> +%{_mandir}/man1/osinfo-db-validate.1*
>  %{_libdir}/%{name}-1.0.so.*
>  /lib/udev/rules.d/95-osinfo.rules
>  %if %{with_gir}
> diff --git a/mingw32-libosinfo.spec.in b/mingw32-libosinfo.spec.in
> index 4287271..ca9a20d 100644
> --- a/mingw32-libosinfo.spec.in
> +++ b/mingw32-libosinfo.spec.in
> @@ -62,6 +62,7 @@ rm -rf $RPM_BUILD_ROOT
>  %{_mingw32_bindir}/osinfo-pciids-convert
>  %{_mingw32_bindir}/osinfo-usbids-convert
>  %{_mingw32_bindir}/osinfo-detect.exe
> +%{_mingw32_bindir}/osinfo-db-validate.exe
>  %{_mingw32_bindir}/libosinfo-1.0-0.dll
>  %{_mingw32_libdir}/libosinfo-1.0.dll.a
>  %{_mingw32_libdir}/libosinfo-1.0.la
> @@ -77,7 +78,7 @@ rm -rf $RPM_BUILD_ROOT
>  %{_mingw32_datadir}/libosinfo/data/devices
>  %{_mingw32_datadir}/libosinfo/data/oses
>  %{_mingw32_datadir}/libosinfo/data/hypervisors
> -%{_mingw32_datadir}/libosinfo/schemas/libosinfo.rng
> +%{_mingw32_mandir}/man1/osinfo-db-validate.1*
>  
>  
>  %changelog
> diff --git a/tools/Makefile.am b/tools/Makefile.am
> index f87ccd4..7ffc60a 100644
> --- a/tools/Makefile.am
> +++ b/tools/Makefile.am
> @@ -1,14 +1,29 @@
>  AM_CFLAGS = $(GOBJECT_CFLAGS) \
>  	    $(GIO_CFLAGS)     \
>  	    $(LIBXML_CFLAGS)  \
> -	    -I$(top_srcdir)
> +	    -DPKGDATADIR="\"$(pkgdatadir)\"" \
> +	    $(WARN_CFLAGS) \
> +	    -I$(top_srcdir) \
> +            $(NULL)
>  
> -bin_PROGRAMS = osinfo-detect
> +bin_PROGRAMS = osinfo-detect osinfo-db-validate
>  
> -osinfo_detect_SOURCES = osinfo-detect.c
> +man1_MANS = osinfo-db-validate.1
> +
> +POD2MAN = pod2man -c "Virtualization Support" -r "$(PACKAGE)-$(VERSION)"
>  
> +%.1: %.c Makefile
> +	$(AM_V_GEN)$(POD2MAN) $< $(srcdir)/$@
> +
> +osinfo_detect_SOURCES = osinfo-detect.c
>  osinfo_detect_LDADD = $(GOBJECT_LIBS) 	     \
>  		      $(GIO_LIBS)     	     \
>  		      $(LIBXML_LIBS)  	     \
>  		      $(top_builddir)/osinfo/libosinfo-1.0.la
>  
> +osinfo_db_validate_SOURCES = osinfo-db-validate.c
> +osinfo_db_validate_LDADD = $(GOBJECT_LIBS)   \
> +		      $(GIO_LIBS)     	     \
> +		      $(LIBXML_LIBS)  	     \
> +		      $(top_builddir)/osinfo/libosinfo-1.0.la
> +
> diff --git a/tools/osinfo-db-validate.c b/tools/osinfo-db-validate.c
> new file mode 100644
> index 0000000..8836a31
> --- /dev/null
> +++ b/tools/osinfo-db-validate.c
> @@ -0,0 +1,343 @@
> +/*
> + * Copyright (C) 2012 Red Hat, Inc
> + *
> + * osinfo-validate: validate that XML file(s) follows the published schema
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * 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 General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, write to the Free Software Foundation,
> + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
> + *
> + * Authors:
> + *   Daniel P. Berrange <berrange at redhat.com>
> + */
> +
> +#include <config.h>
> +
> +#include <glib.h>
> +#include <gio/gio.h>
> +#include <libxml/relaxng.h>
> +
> +#define SCHEMA PKGDATADIR "/schemas/libosinfo.rng"
> +
> +static gboolean verbose = FALSE;
> +
> +static const GOptionEntry entries[] = {
> +    { "verbose", 'v', 0, G_OPTION_ARG_NONE, (void*)&verbose,
> +      "Verbose progress information", NULL, },
> +    { NULL, 0, 0, 0, NULL, NULL, NULL }
> +};
> +
> +
> +static void validate_generic_error_nop(void *userData G_GNUC_UNUSED,
> +                                       const char *msg G_GNUC_UNUSED,
> +                                       ...)
> +{
> +}
> +
> +static void validate_structured_error_nop(void *userData G_GNUC_UNUSED,
> +                                          xmlErrorPtr error G_GNUC_UNUSED)
> +{
> +    if (error->file)
> +        g_printerr("%s:%d %s", error->file, error->line, error->message);
> +    else
> +        g_printerr("Schema validity error %s", error->message);
> +}
> +
> +static xmlDocPtr parse_file(GFile *file, GError **error)
> +{
> +    xmlDocPtr doc = NULL;
> +    xmlParserCtxtPtr pctxt;
> +    gchar *data = NULL;
> +    gsize length;
> +    gchar *uri = g_file_get_uri(file);
> +
> +    if (!g_file_load_contents(file, NULL, &data, &length, NULL, error))
> +        goto cleanup;
> +
> +    if (!(pctxt = xmlNewParserCtxt())) {
> +        g_set_error(error, 0, 0, "%s",
> +                    "Unable to create libxml parser");
> +        goto cleanup;
> +    }
> +
> +    if (!(doc = xmlCtxtReadDoc(pctxt, (const xmlChar*)data, uri, NULL,
> +                               XML_PARSE_NOENT | XML_PARSE_NONET |
> +                               XML_PARSE_NOWARNING))) {
> +        g_set_error(error, 0, 0,
> +                    "Unable to parse XML document %s",
> +                    uri);
> +        goto cleanup;
> +    }
> +
> + cleanup:
> +    g_free(uri);
> +    g_free(data);
> +    return doc;
> +}
> +
> +static gboolean validate_file(xmlRelaxNGValidCtxtPtr rngValid, GFile *file, GFileInfo *info, GError **error);
> +
> +
> +static gboolean validate_file_regular(xmlRelaxNGValidCtxtPtr rngValid,
> +                                      GFile *file,
> +                                      GError **error)
> +{
> +    gboolean ret = FALSE;
> +    xmlDocPtr doc = NULL;
> +    gchar *uri = g_file_get_uri(file);
> +
> +    if (!g_str_has_suffix(uri, ".xml")) {
> +        ret = TRUE;
> +        goto cleanup;
> +    }
> +
> +    if (!(doc = parse_file(file, error)))
> +        goto cleanup;
> +
> +    if (xmlRelaxNGValidateDoc(rngValid, doc) != 0) {
> +        g_set_error(error, 0, 0,
> +                    "Unable to validate doc %s",
> +                    uri);
> +        goto cleanup;
> +    }
> +
> +    ret = TRUE;
> +
> + cleanup:
> +    //g_free(uri);
> +    xmlFreeDoc(doc);
> +    return ret;
> +}

  That works, I would just raise the fact that instead of loading a DOM
tree to validate it and discard it, this can be replaced by a streaming
RNG validation while parsing using the xml (see streamFile in xmllint.c
http://git.gnome.org/browse/libxml2/tree/xmllint.c#n1881 )
  But that's just an optimization :-)

> +
> +static gboolean validate_file_directory(xmlRelaxNGValidCtxtPtr rngValid, GFile *file, GError **error)
> +{
> +    gboolean ret = FALSE;
> +    GFileEnumerator *children = NULL;
> +    GFileInfo *info = NULL;
> +
> +    if (!(children = g_file_enumerate_children(file, "standard::*", 0, NULL, error)))
> +        goto cleanup;
> +
> +    while ((info = g_file_enumerator_next_file(children, NULL, error))) {
> +        GFile *child = g_file_get_child(file, g_file_info_get_name(info));
> +        if (!validate_file(rngValid, child, info, error)) {
> +            g_object_unref(child);
> +            goto cleanup;
> +        }
> +        g_object_unref(child);
> +    }
> +
> +    if (*error)
> +        goto cleanup;
> +
> +    ret = TRUE;
> +
> + cleanup:
> +    g_object_unref(children);
> +    return ret;
> +}
> +
> +
> +static gboolean validate_file(xmlRelaxNGValidCtxtPtr rngValid, GFile *file, GFileInfo *info, GError **error)
> +{
> +    gboolean ret = FALSE;
> +    GFileInfo *thisinfo = NULL;
> +    gchar *uri = g_file_get_uri(file);
> +
> +    if (verbose)
> +        g_print("Process %s\n", uri);
> +
> +    if (!info) {
> +        if (!(thisinfo = g_file_query_info(file, "standard::*",
> +                                           G_FILE_QUERY_INFO_NONE,
> +                                           NULL, error)))
> +            goto cleanup;
> +        info = thisinfo;
> +    }
> +
> +    if (g_file_info_get_file_type(info) == G_FILE_TYPE_DIRECTORY) {
> +        if (!validate_file_directory(rngValid, file, error))
> +            goto cleanup;
> +    } else if (g_file_info_get_file_type(info) == G_FILE_TYPE_REGULAR) {
> +        if (!validate_file_regular(rngValid, file, error))
> +            goto cleanup;
> +    } else {
> +        g_set_error(error, 0, 0,
> +                    "Unable to handle file type for %s",
> +                    uri);
> +        goto cleanup;
> +    }
> +
> +    ret = TRUE;
> +
> + cleanup:
> +    g_free(uri);
> +    if (thisinfo)
> +        g_object_unref(thisinfo);
> +    return ret;
> +}
> +
> +
> +static gboolean validate_files(gint argc, gchar **argv, GError **error)
> +{
> +    xmlRelaxNGParserCtxtPtr rngParser = NULL;
> +    xmlRelaxNGPtr rng = NULL;
> +    xmlRelaxNGValidCtxtPtr rngValid = NULL;
> +    gboolean ret = FALSE;
> +    gsize i;
> +
> +    xmlSetGenericErrorFunc(NULL, validate_generic_error_nop);
> +    xmlSetStructuredErrorFunc(NULL, validate_structured_error_nop);
> +
> +    rngParser = xmlRelaxNGNewParserCtxt(SCHEMA);
> +    if (!rngParser) {
> +        g_set_error(error, 0, 0,
> +                    "Unable to create RNG parser for %s",
> +                    SCHEMA);
> +        goto cleanup;
> +    }
> +
> +    rng = xmlRelaxNGParse(rngParser);
> +    if (!rng) {
> +        g_set_error(error, 0, 0,
> +                    "Unable to parse RNG %s",
> +                    SCHEMA);
> +        goto cleanup;
> +    }
> +
> +    rngValid = xmlRelaxNGNewValidCtxt(rng);
> +    if (!rngValid) {
> +        g_set_error(error, 0, 0,
> +                    "Unable to create RNG validation context %s",
> +                    SCHEMA);
> +        goto cleanup;
> +    }
> +
> +    for (i = 0 ; i < argc ; i++) {
> +        GFile *file = g_file_new_for_commandline_arg(argv[i]);
> +        if (!validate_file(rngValid, file, NULL, error)) {
> +            g_object_unref(file);
> +            goto cleanup;
> +        }
> +        g_object_unref(file);
> +    }
> +
> +    ret = TRUE;
> +
> + cleanup:
> +    xmlRelaxNGFreeValidCtxt(rngValid);
> +    xmlRelaxNGFreeParserCtxt(rngParser);
> +    xmlRelaxNGFree(rng);
> +    return ret;
> +}
> +
> +gint main(gint argc, gchar **argv)
> +{
> +    GOptionContext *context;
> +    GError *error = NULL;
> +    gint ret = EXIT_FAILURE;
> +
> +    g_type_init();
> +
> +    context = g_option_context_new("- Validate XML documents ");
> +
> +    g_option_context_add_main_entries(context, entries, NULL);
> +
> +    if (!g_option_context_parse(context, &argc, &argv, &error)) {
> +        g_printerr("Error while parsing options: %s\n", error->message);
> +        g_printerr("%s\n", g_option_context_get_help(context, FALSE, NULL));
> +	goto error;
> +    }
> +
> +    if (!validate_files(argc - 1, argv + 1, &error)) {
> +        g_printerr("%s\n", error->message);
> +        goto error;
> +    }
> +
> +    ret = EXIT_SUCCESS;
> +
> + error:
> +    g_clear_error(&error);
> +    g_option_context_free(context);
> +
> +    return ret;
> +}
> +
> +/*
> +=pod
> +
> +=head1 NAME
> +
> +osinfo-db-validate - Validate libosinfo XML data files
> +
> +=head1 SYNOPSIS
> +
> +osinfo-db-validate [OPTIONS...] LOCAL-PATH1 [LOCAL-PATH2...]
> +
> +osinfo-db-validate [OPTIONS...] URI1 [URI2...]
> +
> +=head1 DESCRIPTION
> +
> +Check that all files (C<LOCAL-PATH1> or C<URI1>) comply with the
> +libosinfo XML schema. The local path may point to a directory
> +containing XML files, or directory to an XML file. The uris must

     "or directly to an XML file." ???

> +point directly to remote XML files
> +
> +Any validation errors will be displayed on the console when
> +detected.
> +
> +=head1 OPTIONS
> +
> +=over 8
> +
> +=item B<-v>, B<--verbose>
> +
> +Display verbose progress information when validating files
> +
> +=back
> +
> +=head1 EXIT STATUS
> +
> +The exit status will be 0 if all files passed validation,
> +or 1 if a validation error was hit.
> +
> +=head1 SEE ALSO
> +
> +C<xmllint(1)>
> +
> +=head1 AUTHORS
> +
> +Daniel P. Berrange <berrange at redhat.com>
> +
> +=head1 COPYRIGHT
> +
> +Copyright (C) 2012 Red Hat, Inc.
> +
> +=head1 LICENSE
> +
> +osinfo-validate is distributed under the termsof the GNU LGPL v2+
> +license. This is free software; see the source for copying conditions.
> +There is NO warranty; not even for MERCHANTABILITY or FITNESS
> +FOR A PARTICULAR PURPOSE
> +
> +=cut
> +*/
> +
> +/*
> + * Local variables:
> + *  indent-tabs-mode: nil
> + *  c-indent-level: 4
> + *  c-basic-offset: 4
> + * End:
> + */

  Looks good to me with the small doc fix :-)

Daniel

-- 
Daniel Veillard      | libxml Gnome XML XSLT toolkit  http://xmlsoft.org/
daniel at veillard.com  | Rpmfind RPM search engine http://rpmfind.net/
http://veillard.com/ | virtualization library  http://libvirt.org/




More information about the virt-tools-list mailing list