[Libosinfo] [PATCH db-tools 1/2] Add osinfo-db-import tool for unpacking an osinfo database archive

Daniel P. Berrange berrange at redhat.com
Thu Jun 16 08:20:44 UTC 2016


Add a new osinfo-db-import tool which takes an archive in the
tar (pax) format, with XZ compression and can extract its contents
into one of the standard libosinfo database locations.

This makes it easy for users to download new copies of the
database and unpack them in the right place.

eg

 $ osinfo-db-import -v osinfo-db-2016-06-15.tar.xz
 osinfo-db-import: d osinfo-db-2016-06-15/
 osinfo-db-import: r osinfo-db-2016-06-15/pci.ids
 osinfo-db-import: r osinfo-db-2016-06-15/usb.ids
 osinfo-db-import: d osinfo-db-2016-06-15/datamaps/
 osinfo-db-import: r osinfo-db-2016-06-15/datamaps/windows-lang.xml
 osinfo-db-import: r osinfo-db-2016-06-15/datamaps/x11-keyboard.xml
 osinfo-db-import: d osinfo-db-2016-06-15/devices/
 osinfo-db-import: r osinfo-db-2016-06-15/devices/qemu-pci.xml
 osinfo-db-import: r osinfo-db-2016-06-15/devices/qemu-ps2.xml
 osinfo-db-import: r osinfo-db-2016-06-15/devices/qemu-usb.xml
 osinfo-db-import: r osinfo-db-2016-06-15/devices/virtio-pci.xml
 osinfo-db-import: r osinfo-db-2016-06-15/devices/xen.xml
 ...snip...

Signed-off-by: Daniel P. Berrange <berrange at redhat.com>
---
 .gitignore                    |   3 +
 configure.ac                  |   1 +
 mingw-osinfo-db-tools.spec.in |   6 +-
 osinfo-db-tools.spec.in       |   3 +
 po/POTFILES.in                |   1 +
 tools/Makefile.am             |  12 +-
 tools/osinfo-db-import.c      | 468 ++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 490 insertions(+), 4 deletions(-)
 create mode 100644 tools/osinfo-db-import.c

diff --git a/.gitignore b/.gitignore
index 03c5264..297976f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -37,6 +37,9 @@ build/
 tools/osinfo-db-validate
 tools/osinfo-db-validate.exe
 tools/osinfo-db-validate.1
+tools/osinfo-db-import
+tools/osinfo-db-import.exe
+tools/osinfo-db-import.1
 po/.intltool-merge-cache
 po/Makefile.in.in
 po/POTFILES
diff --git a/configure.ac b/configure.ac
index 28e0fc6..49fae1a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -42,6 +42,7 @@ GLIB_ENCODED_VERSION="GLIB_VERSION_2_36"
 
 PKG_CHECK_MODULES([LIBXML], [libxml-2.0 >= 2.6.0])
 PKG_CHECK_MODULES([LIBXSLT], [libxslt >= 1.0.0])
+PKG_CHECK_MODULES([LIBARCHIVE], [libarchive >= 3.0.0])
 
 PKG_CHECK_MODULES([GLIB], [glib-2.0 >= $GLIB_MINIMUM_VERSION gobject-2.0 gio-2.0])
 GLIB_CFLAGS="$GLIB_CFLAGS -DGLIB_VERSION_MIN_REQUIRED=$GLIB_ENCODED_VERSION"
diff --git a/mingw-osinfo-db-tools.spec.in b/mingw-osinfo-db-tools.spec.in
index 750b060..f3035f9 100644
--- a/mingw-osinfo-db-tools.spec.in
+++ b/mingw-osinfo-db-tools.spec.in
@@ -26,8 +26,8 @@ BuildRequires: mingw32-libxml2
 BuildRequires: mingw64-libxml2
 BuildRequires: mingw32-libxslt
 BuildRequires: mingw64-libxslt
-BuildRequires: mingw32-libsoup
-BuildRequires: mingw64-libsoup
+BuildRequires: mingw32-libarchive
+BuildRequires: mingw64-libarchive
 
 BuildRequires: pkgconfig
 
@@ -74,11 +74,13 @@ rm -rf $RPM_BUILD_ROOT%{mingw64_datadir}/man
 
 %files -n mingw32-osinfo-db-tools
 %doc AUTHORS ChangeLog COPYING.LIB NEWS README
+%{mingw32_bindir}/osinfo-db-import.exe
 %{mingw32_bindir}/osinfo-db-validate.exe
 %{mingw32_datadir}/locale/*/LC_MESSAGES/osinfo-db-tools.mo
 
 %files -n mingw64-osinfo-db-tools
 %doc AUTHORS ChangeLog COPYING.LIB NEWS README
+%{mingw64_bindir}/osinfo-db-import.exe
 %{mingw64_bindir}/osinfo-db-validate.exe
 %{mingw64_datadir}/locale/*/LC_MESSAGES/osinfo-db-tools.mo
 
diff --git a/osinfo-db-tools.spec.in b/osinfo-db-tools.spec.in
index 6285ff1..1295f6e 100644
--- a/osinfo-db-tools.spec.in
+++ b/osinfo-db-tools.spec.in
@@ -12,6 +12,7 @@ BuildRequires: intltool
 BuildRequires: glib2-devel
 BuildRequires: libxml2-devel >= 2.6.0
 BuildRequires: libxslt-devel >= 1.0.0
+BuildRequires: libarchive-devel
 BuildRequires: /usr/bin/pod2man
 
 %description
@@ -33,7 +34,9 @@ information about operating systems for use with virtualization
 %files -f %{name}.lang
 %defattr(-, root, root)
 %doc AUTHORS ChangeLog COPYING.LIB NEWS README
+%{_bindir}/osinfo-db-import
 %{_bindir}/osinfo-db-validate
+%{_mandir}/man1/osinfo-db-import.1*
 %{_mandir}/man1/osinfo-db-validate.1*
 
 %changelog
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 31b35e2..c1e69ef 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -1 +1,2 @@
+tools/osinfo-db-import.c
 tools/osinfo-db-validate.c
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 97a9cb6..5ade53a 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -3,14 +3,16 @@ AM_CFLAGS = $(GOBJECT_CFLAGS) \
 	    $(GLIB_CFLAGS)    \
 	    $(LIBXML_CFLAGS)  \
 	    -DPKGDATADIR="\"$(pkgdatadir)\"" \
+	    -DDATA_DIR="\"$(datadir)\"" \
+	    -DSYSCONFDIR="\"$(sysconfdir)\"" \
 	    -DLOCALEDIR="\"$(datadir)/locale\"" \
 	    $(WARN_CFLAGS) \
 	    -I$(top_srcdir) \
             $(NULL)
 
-bin_PROGRAMS = osinfo-db-validate
+bin_PROGRAMS = osinfo-db-validate osinfo-db-import
 
-man1_MANS = osinfo-db-validate.1
+man1_MANS = osinfo-db-validate.1 osinfo-db-import.1
 
 CLEANFILES = $(man1_MANS)
 
@@ -24,3 +26,9 @@ osinfo_db_validate_LDADD = $(GOBJECT_LIBS)	\
 		      $(GIO_LIBS)		\
 		      $(GLIB_LIBS)		\
 		      $(LIBXML_LIBS)
+
+osinfo_db_import_SOURCES = osinfo-db-import.c
+osinfo_db_import_LDADD = $(GOBJECT_LIBS)	\
+		      $(GIO_LIBS)		\
+		      $(GLIB_LIBS)		\
+		      $(LIBARCHIVE_LIBS)
diff --git a/tools/osinfo-db-import.c b/tools/osinfo-db-import.c
new file mode 100644
index 0000000..327562a
--- /dev/null
+++ b/tools/osinfo-db-import.c
@@ -0,0 +1,468 @@
+/*
+ * Copyright (C) 2016 Red Hat, Inc
+ *
+ * osinfo-db-import: import a database archive
+ *
+ * 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 <locale.h>
+#include <glib/gi18n.h>
+#include <stdlib.h>
+#include <archive.h>
+#include <archive_entry.h>
+
+const char *argv0;
+
+static GFile *osinfo_db_import_get_system_path(const gchar *root)
+{
+    GFile *file;
+    gchar *dbdir;
+    const gchar *path = g_getenv("OSINFO_DATA_DIR");
+    if (!path)
+        path = DATA_DIR "/libosinfo";
+
+    dbdir = g_strdup_printf("%s%s/db", root, path);
+    file = g_file_new_for_path(dbdir);
+    g_free(dbdir);
+    return file;
+}
+
+static GFile *osinfo_db_import_get_local_path(const gchar *root)
+{
+    GFile *file;
+    gchar *dbdir;
+
+    dbdir = g_strdup_printf("%s" SYSCONFDIR "/libosinfo/db", root);
+    file = g_file_new_for_path(dbdir);
+    g_free(dbdir);
+    return file;
+}
+
+static GFile *osinfo_db_import_get_user_path(const gchar *root)
+{
+    GFile *file;
+    gchar *dbdir;
+    const gchar *configdir = g_get_user_config_dir();
+
+    dbdir = g_strdup_printf("%s%s/libosinfo/db", root, configdir);
+    file = g_file_new_for_path(dbdir);
+    g_free(dbdir);
+    return file;
+}
+
+static GFile *osinfo_db_import_get_custom_path(const gchar *dir,
+                                               const gchar *root)
+{
+    GFile *file;
+    gchar *dbdir;
+
+    dbdir = g_strdup_printf("%s%s", root, dir);
+    file = g_file_new_for_path(dbdir);
+    g_free(dbdir);
+    return file;
+}
+
+
+static GFile *osinfo_db_import_get_path(const char *root,
+                                        gboolean user,
+                                        gboolean local,
+                                        gboolean system,
+                                        const char *custom)
+{
+    if (custom) {
+        return osinfo_db_import_get_custom_path(custom, root);
+    } else if (user) {
+        return osinfo_db_import_get_user_path(root);
+    } else if (local) {
+        return osinfo_db_import_get_local_path(root);
+    } else if (system) {
+        return osinfo_db_import_get_system_path(root);
+#ifndef WIN32
+    } else if (geteuid() == 0) {
+        return osinfo_db_import_get_local_path(root);
+#endif
+    } else {
+        return osinfo_db_import_get_user_path(root);
+    }
+}
+
+
+static int osinfo_db_import_create_reg(GFile *file,
+                                       struct archive *arc,
+                                       struct archive_entry *entry)
+{
+    GFileOutputStream *os = NULL;
+    GError *err = NULL;
+    int ret;
+    int r;
+    const void *buf;
+    size_t size;
+    gint64 offset;
+
+    os = g_file_replace(file, NULL, FALSE, G_FILE_CREATE_REPLACE_DESTINATION,
+                        NULL, &err);
+    if (!os) {
+        g_printerr("%s: %s\n",
+                   argv0,  err->message);
+        return -1;
+    }
+
+    for (;;) {
+        r = archive_read_data_block(arc, &buf, &size, &offset);
+        if (r == ARCHIVE_EOF)
+            break;
+        if (r != ARCHIVE_OK) {
+            g_printerr("%s: cannot write to %s\n",
+                       argv0, g_file_get_path(file));
+            goto cleanup;
+        }
+
+        if (!g_seekable_seek(G_SEEKABLE(os), offset, G_SEEK_SET, NULL, NULL)) {
+            g_printerr("%s: cannot seek to %" PRId64 " in %s\n",
+                       argv0, (uint64_t)offset, g_file_get_path(file));
+            goto cleanup;
+        }
+        if (!g_output_stream_write_all(G_OUTPUT_STREAM(os), buf, size, NULL, NULL, NULL)) {
+            g_printerr("%s: cannot write to %s\n",
+                       argv0, g_file_get_path(file));
+            goto cleanup;
+        }
+    }
+    ret = 0;
+ cleanup:
+    g_object_unref(os);
+    return ret;
+}
+
+static int osinfo_db_import_create_dir(GFile *file,
+                                       struct archive_entry *entry)
+{
+    GError *err = NULL;
+    if (!g_file_make_directory_with_parents(file, NULL, &err) &&
+        err->code != G_IO_ERROR_EXISTS) {
+        g_printerr("%s: %s\n", argv0, err->message);
+        return -1;
+    }
+    if (err)
+        g_error_free(err);
+    return 0;
+}
+
+
+static int osinfo_db_import_create(GFile *file,
+                                   struct archive *arc,
+                                   struct archive_entry *entry,
+                                   gboolean verbose)
+{
+    int type = archive_entry_filetype(entry) & AE_IFMT;
+
+    switch (type) {
+    case AE_IFREG:
+        if (verbose) {
+            g_print("%s: r %s\n", argv0, archive_entry_pathname(entry));
+        }
+        return osinfo_db_import_create_reg(file, arc, entry);
+
+    case AE_IFDIR:
+        if (verbose) {
+            g_print("%s: d %s\n", argv0, archive_entry_pathname(entry));
+        }
+        return osinfo_db_import_create_dir(file, entry);
+
+    default:
+        g_printerr("%s: unsupported file type for %s\n",
+                   argv0, archive_entry_pathname(entry));
+        return -1;
+    }
+}
+
+static GFile *osinfo_db_import_get_file(GFile *target,
+                                        struct archive_entry *entry)
+{
+    const gchar *entpath = archive_entry_pathname(entry);
+    const gchar *tmp = strchr(entpath, '/');
+    if (!tmp) {
+        tmp = "";
+    } else {
+        tmp++;
+    }
+    return g_file_resolve_relative_path(target, tmp);
+}
+
+static int osinfo_db_import_extract(GFile *target,
+                                    const char *source,
+                                    gboolean verbose)
+{
+    struct archive *arc;
+    struct archive_entry *entry;
+    int ret = -1;
+    int r;
+    GFile *file = NULL;
+    GError *err = NULL;
+
+    arc = archive_read_new();
+
+    archive_read_support_format_tar(arc);
+    archive_read_support_filter_xz(arc);
+
+    if (source != NULL && g_str_equal(source, "-"))
+        source = NULL;
+
+    if ((r = archive_read_open_filename(arc, source, 10240)) != ARCHIVE_OK) {
+        g_printerr("%s: cannot open archive %s: %s\n",
+                   argv0, source, archive_error_string(arc));
+        goto cleanup;
+    }
+
+    for (;;) {
+        r = archive_read_next_header(arc, &entry);
+        if (r == ARCHIVE_EOF)
+            break;
+        if (r != ARCHIVE_OK) {
+            g_printerr("%s: cannot read next archive entry in %s: %s\n",
+                       argv0, source, archive_error_string(arc));
+            goto cleanup;
+        }
+
+        file = osinfo_db_import_get_file(target, entry);
+        if (osinfo_db_import_create(file, arc, entry, verbose) < 0) {
+            goto cleanup;
+        }
+        g_object_unref(file);
+        file = NULL;
+    }
+
+    if (archive_read_close(arc) != ARCHIVE_OK) {
+        g_printerr("%s: cannot finish reading archive %s: %s\n",
+                   argv0, source, archive_error_string(arc));
+        goto cleanup;
+    }
+
+    ret = 0;
+ cleanup:
+    archive_read_free(arc);
+    if (err)
+        g_error_free(err);
+    if (file)
+        g_object_unref(file);
+    return ret;
+}
+
+gint main(gint argc, gchar **argv)
+{
+    GOptionContext *context;
+    GError *error = NULL;
+    gint ret = EXIT_FAILURE;
+    gboolean verbose = FALSE;
+    gboolean user = FALSE;
+    gboolean local = FALSE;
+    gboolean system = FALSE;
+    const gchar *root = "";
+    const gchar *archive = NULL;
+    const gchar *custom = NULL;
+    int locs = 0;
+    GFile *dir = NULL;
+    const GOptionEntry entries[] = {
+      { "verbose", 'v', 0, G_OPTION_ARG_NONE, (void*)&verbose,
+        N_("Verbose progress information"), NULL, },
+      { "user", 0, 0, G_OPTION_ARG_NONE, (void *)&user,
+        N_("Import into user directory"), NULL, },
+      { "local", 0, 0, G_OPTION_ARG_NONE, (void *)&local,
+        N_("Import into local directory"), NULL, },
+      { "system", 0, 0, G_OPTION_ARG_NONE, (void *)&system,
+        N_("Import into system directory"), NULL, },
+      { "dir", 0, 0, G_OPTION_ARG_STRING, (void *)&custom,
+        N_("Import into custom directory"), NULL, },
+      { "root", 0, 0, G_OPTION_ARG_STRING, &root,
+        N_("Installation root directory"), NULL, },
+      { NULL, 0, 0, 0, NULL, NULL, NULL },
+    };
+    argv0 = argv[0];
+
+    setlocale(LC_ALL, "");
+    textdomain(GETTEXT_PACKAGE);
+    bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
+    bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
+
+    context = g_option_context_new(_("- Validate XML documents "));
+
+    g_option_context_add_main_entries(context, entries, GETTEXT_PACKAGE);
+
+    if (!g_option_context_parse(context, &argc, &argv, &error)) {
+        g_printerr(_("%s: error while parsing commandline options: %s\n\n"),
+                   argv0, error->message);
+        g_printerr("%s\n", g_option_context_get_help(context, FALSE, NULL));
+        goto error;
+    }
+
+    if (argc > 2) {
+        g_printerr(_("%s: expected path to one archive file to import\n"),
+                   argv0);
+        goto error;
+    }
+
+    if (local)
+        locs++;
+    if (system)
+        locs++;
+    if (user)
+        locs++;
+    if (custom)
+        locs++;
+    if (locs > 1) {
+        g_printerr(_("Only one of --user, --local, --system & --dir can be used"));
+        goto error;
+    }
+
+    archive = argc == 2 ? argv[1] : NULL;
+    dir = osinfo_db_import_get_path(root, user, local, system, custom);
+    if (osinfo_db_import_extract(dir, archive, verbose) < 0)
+        goto error;
+
+    ret = EXIT_SUCCESS;
+
+ error:
+    if (dir) {
+        g_object_unref(dir);
+    }
+    g_clear_error(&error);
+    g_option_context_free(context);
+
+    return ret;
+}
+
+
+/*
+=pod
+
+=head1 NAME
+
+osinfo-db-import - Import an osinfo database archive
+
+=head1 SYNOPSIS
+
+osinfo-db-import [OPTIONS...] ARCHIVE-FILE
+
+=head1 DESCRIPTION
+
+The B<osinfo-db-import> tool will take an osinfo database
+archive and extract its contents into one of the standard
+database locations on the current host:
+
+=over 1
+
+=item B<system>
+
+This is the primary system-wide database location, intended
+for use by operating system vendors distributing database
+files in the native package format.
+
+=item B<local>
+
+This is the secondary system-wide database location, intended
+for use by system administrators wishing to provide an updated
+database for all users.
+
+=item B<user>
+
+This is the user private database location, intended for use
+by unprivileged local users wishing to provide applications
+they use with an updated database.
+
+=back
+
+If run by a privileged account (ie root), the B<local> database
+location will be used by default, otherwise the B<user> location
+will be used.
+
+=head1 OPTIONS
+
+=over 8
+
+=item B<--user>
+
+Override the default behaviour to force installation into the
+B<user> database location.
+
+=item B<--local>
+
+Override the default behaviour to force installation into the
+B<local> database location.
+
+=item B<--system>
+
+Override the default behaviour to force installation into the
+B<system> database location.
+
+=item B<--dir=PATH>
+
+Override the default behaviour to force installation into the
+custom directory B<PATH>.
+
+=item B<--root=PATH>
+
+Prefix the installation location with the root directory
+given by C<PATH>. This is useful when wishing to install
+into a chroot environment or equivalent.
+
+=item B<-v>, B<--verbose>
+
+Display verbose progress information when installing files
+
+=back
+
+=head1 EXIT STATUS
+
+The exit status will be 0 if all files were installed
+successfully, or 1 if at least one file could not be
+installed.
+
+=head1 SEE ALSO
+
+C<osinfo-db-export(1)>
+
+=head1 AUTHORS
+
+Daniel P. Berrange <berrange at redhat.com>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2016 Red Hat, Inc.
+
+=head1 LICENSE
+
+C<osinfo-db-import> 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:
+ */
-- 
2.5.5




More information about the Libosinfo mailing list