From: "Daniel P. Berrange" <berrange@xxxxxxxxxx> 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@xxxxxxxxxx> + */ + +#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; +} + + +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 +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@xxxxxxxxxx> + +=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: + */ -- 1.7.7.6