src/osinfo.c has generic code to read the libosinfo DB. Add a wrapper
around it to expose it in mllib for use in ocaml code.
---
lib/Makefile.am | 2 +
lib/osinfo-iso.c | 464 ++++++++++++++++++++++++++++++++++++++++++++++++++++
lib/osinfo.c | 477 ++++++------------------------------------------------
lib/osinfo.h | 27 ++++
mllib/Makefile.am | 11 +-
mllib/osinfo-c.c | 100 ++++++++++++
mllib/osinfo.ml | 26 +++
mllib/osinfo.mli | 31 ++++
8 files changed, 704 insertions(+), 434 deletions(-)
create mode 100644 lib/osinfo-iso.c
create mode 100644 lib/osinfo.h
create mode 100644 mllib/osinfo-c.c
create mode 100644 mllib/osinfo.ml
create mode 100644 mllib/osinfo.mli
diff --git a/lib/Makefile.am b/lib/Makefile.am
index e1ab1bff9..7a3580a00 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -112,7 +112,9 @@ libguestfs_la_SOURCES = \
lpj.c \
match.c \
mountable.c \
+ osinfo.h \
osinfo.c \
+ osinfo-iso.c \
private-data.c \
proto.c \
qemu.c \
diff --git a/lib/osinfo-iso.c b/lib/osinfo-iso.c
new file mode 100644
index 000000000..0c3c19971
--- /dev/null
+++ b/lib/osinfo-iso.c
@@ -0,0 +1,464 @@
+/* libguestfs
+ * Copyright (C) 2012 Red Hat Inc.
+ *
+ * This library 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; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/* Read libosinfo XML files to parse out just the
+ * os/media/iso/system-id and os/media/iso/volume-id fields, which we
+ * can then use to map install media to operating systems.
+ *
+ * Note some assumptions here:
+ *
+ * (1) We have to do some translation of the distro names and versions
+ * stored in the libosinfo files and the standard names returned by
+ * libguestfs.
+ *
+ * (2) Media detection is only part of the story. We may still need
+ * to inspect inside the image.
+ *
+ * (3) We only read the XML database files (at most) once per process,
+ * and keep them cached. They are only read at all if someone tries
+ * to inspect a CD/DVD/ISO.
+ *
+ * XXX Currently the database is not freed when the program exits /
+ * library is unloaded, although we should probably do that.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <libintl.h>
+#include <sys/stat.h>
+
+#include <libxml/parser.h>
+#include <libxml/xpath.h>
+
+#include "ignore-value.h"
+#include "glthread/lock.h"
+#include "c-ctype.h"
+
+#include "guestfs.h"
+#include "guestfs-internal.h"
+
+#include "osinfo.h"
+
+gl_lock_define_initialized (static, osinfo_db_lock);
+static ssize_t osinfo_db_size = 0; /* 0 = unread, -1 = error, >= 1 = #records */
+static struct osinfo *osinfo_db = NULL;
+
+
+static void free_osinfo_db_entry (struct osinfo *);
+
+#define XMLSTREQ(a,b) (xmlStrEqual((a),(b)) == 1)
+
+static int
+read_osinfo_db_xml (guestfs_h *g, const char *pathname, void *data);
+
+/* Given one or more fields from the header of a CD/DVD/ISO, look up
+ * the media in the libosinfo database and return our best guess for
+ * the operating system.
+ *
+ * This returns:
+ * -1 => a fatal error ('error' has been called, caller must not ignore it)
+ * 0 => could not locate the OS
+ * 1 => matching OS found, the osinfo_ret pointer has been filled in
+ */
+int
+guestfs_int_osinfo_map (guestfs_h *g, const struct guestfs_isoinfo *isoinfo,
+ const struct osinfo **osinfo_ret)
+{
+ size_t i;
+
+ /* We only need to lock the database when reading it for the first time. */
+ gl_lock_lock (osinfo_db_lock);
+ if (osinfo_db_size == 0) {
+ if (read_osinfo_db (g, read_osinfo_db_xml, NULL) == -1) {
+ /* Fatal error: free any database entries which have been read, and
+ * mark the database as having a permanent error.
+ */
+ if (osinfo_db_size > 0) {
+ for (i = 0; i < (size_t) osinfo_db_size; ++i)
+ free_osinfo_db_entry (&osinfo_db[i]);
+ }
+ free (osinfo_db);
+ osinfo_db = NULL;
+ osinfo_db_size = -1;
+ gl_lock_unlock (osinfo_db_lock);
+ return -1;
+ }
+ }
+ gl_lock_unlock (osinfo_db_lock);
+
+ if (osinfo_db_size <= 0)
+ return 0;
+
+ /* Look in the database to see if we can find a match. */
+ for (i = 0; i < (size_t) osinfo_db_size; ++i) {
+ if (osinfo_db[i].re_system_id) {
+ if (!isoinfo->iso_system_id ||
+ !match (g, isoinfo->iso_system_id, osinfo_db[i].re_system_id))
+ continue;
+ }
+
+ if (osinfo_db[i].re_volume_id) {
+ if (!isoinfo->iso_volume_id ||
+ !match (g, isoinfo->iso_volume_id, osinfo_db[i].re_volume_id))
+ continue;
+ }
+
+ if (osinfo_db[i].re_publisher_id) {
+ if (!isoinfo->iso_publisher_id ||
+ !match (g, isoinfo->iso_publisher_id, osinfo_db[i].re_publisher_id))
+ continue;
+ }
+
+ if (osinfo_db[i].re_application_id) {
+ if (!isoinfo->iso_application_id ||
+ !match (g, isoinfo->iso_application_id, osinfo_db[i].re_application_id))
+ continue;
+ }
+
+ debug (g, "osinfo: mapped disk to database entry %zu", i);
+
+ if (osinfo_ret)
+ *osinfo_ret = &osinfo_db[i];
+ return 1;
+ }
+
+ debug (g, "osinfo: no mapping found");
+
+ return 0;
+}
+
+static int read_iso_node (guestfs_h *g, xmlNodePtr iso_node, struct osinfo *osinfo);
+static int read_media_node (guestfs_h *g, xmlXPathContextPtr xpathCtx, xmlNodePtr
media_node, struct osinfo *osinfo);
+static int read_os_node (guestfs_h *g, xmlXPathContextPtr xpathCtx, xmlNodePtr os_node,
struct osinfo *osinfo);
+
+/* Read a single XML file from pathname (which is a full path).
+ * Only memory allocation failures are fatal errors here.
+ */
+static int
+read_osinfo_db_xml (guestfs_h *g, const char *pathname, void *opaque)
+{
+ CLEANUP_XMLFREEDOC xmlDocPtr doc = NULL;
+ CLEANUP_XMLXPATHFREECONTEXT xmlXPathContextPtr xpathCtx = NULL;
+ CLEANUP_XMLXPATHFREEOBJECT xmlXPathObjectPtr xpathObj = NULL;
+ xmlNodeSetPtr nodes;
+ xmlNodePtr iso_node, media_node, os_node;
+ struct osinfo *osinfo;
+ size_t i;
+
+ doc = xmlReadFile (pathname, NULL, XML_PARSE_NONET);
+ if (doc == NULL) {
+ debug (g, "osinfo: unable to parse XML file %s", pathname);
+ return 0;
+ }
+
+ xpathCtx = xmlXPathNewContext (doc);
+ if (xpathCtx == NULL) {
+ error (g, _("osinfo: unable to create new XPath context"));
+ return -1;
+ }
+
+ /* Get all <iso> nodes at any depth, then use the parent pointers in
+ * order to work back up the tree.
+ */
+ xpathObj = xmlXPathEvalExpression (BAD_CAST "/libosinfo/os/media/iso",
+ xpathCtx);
+ if (xpathObj == NULL) {
+ error (g, _("osinfo: %s: unable to evaluate XPath expression"),
+ pathname);
+ return -1;
+ }
+
+ nodes = xpathObj->nodesetval;
+
+ if (nodes != NULL) {
+ for (i = 0; i < (size_t) nodes->nodeNr; ++i) {
+ iso_node = nodes->nodeTab[i];
+ assert (iso_node != NULL);
+ assert (STREQ ((const char *) iso_node->name, "iso"));
+ assert (iso_node->type == XML_ELEMENT_NODE);
+
+ media_node = iso_node->parent;
+ assert (media_node != NULL);
+ assert (STREQ ((const char *) media_node->name, "media"));
+ assert (media_node->type == XML_ELEMENT_NODE);
+
+ os_node = media_node->parent;
+ assert (os_node != NULL);
+ assert (STREQ ((const char *) os_node->name, "os"));
+ assert (os_node->type == XML_ELEMENT_NODE);
+
+ /* Allocate an osinfo record. */
+ osinfo_db_size++;
+ osinfo_db = safe_realloc (g, osinfo_db,
+ sizeof (struct osinfo) * osinfo_db_size);
+ osinfo = &osinfo_db[osinfo_db_size-1];
+ memset (osinfo, 0, sizeof *osinfo);
+
+ /* Read XML fields into the new osinfo record. */
+ if (read_iso_node (g, iso_node, osinfo) == -1 ||
+ read_media_node (g, xpathCtx, media_node, osinfo) == -1 ||
+ read_os_node (g, xpathCtx, os_node, osinfo) == -1) {
+ free_osinfo_db_entry (osinfo);
+ osinfo_db_size--;
+ return -1;
+ }
+
+#if 0
+ debug (g, "osinfo: %s: %s%s%s%s=> arch %s live %s installer %s product %s
type %d distro %d version %d.%d",
+ pathname,
+ osinfo->re_system_id ? "<system-id/> " : "",
+ osinfo->re_volume_id ? "<volume-id/> " : "",
+ osinfo->re_publisher_id ? "<publisher-id/> " :
"",
+ osinfo->re_application_id ? "<application-id/> " :
"",
+ osinfo->arch ? osinfo->arch : "(none)",
+ osinfo->is_live_disk ? "true" : "false",
+ osinfo->is_installer ? "true" : "false",
+ osinfo->product_name ? osinfo->product_name : "(none)",
+ (int) osinfo->type, (int) osinfo->distro,
+ osinfo->major_version, osinfo->minor_version);
+#endif
+ }
+ }
+
+ return 0;
+}
+
+static int compile_re (guestfs_h *g, xmlNodePtr child, pcre **re);
+
+/* Read the regular expressions under the <iso> node. libosinfo
+ * itself uses the glib function 'g_regex_match_simple'. That appears
+ * to implement PCRE, however I have not checked in detail.
+ */
+static int
+read_iso_node (guestfs_h *g, xmlNodePtr iso_node, struct osinfo *osinfo)
+{
+ xmlNodePtr child;
+
+ for (child = iso_node->children; child; child = child->next) {
+ if (STREQ ((const char *) child->name, "system-id")) {
+ if (compile_re (g, child, &osinfo->re_system_id) == -1)
+ return -1;
+ }
+ else if (STREQ ((const char *) child->name, "volume-id")) {
+ if (compile_re (g, child, &osinfo->re_volume_id) == -1)
+ return -1;
+ }
+ else if (STREQ ((const char *) child->name, "publisher-id")) {
+ if (compile_re (g, child, &osinfo->re_publisher_id) == -1)
+ return -1;
+ }
+ else if (STREQ ((const char *) child->name, "application-id")) {
+ if (compile_re (g, child, &osinfo->re_application_id) == -1)
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int
+compile_re (guestfs_h *g, xmlNodePtr node, pcre **re)
+{
+ const char *err;
+ int offset;
+ CLEANUP_FREE char *content = (char *) xmlNodeGetContent (node);
+
+ if (content) {
+ *re = pcre_compile (content, 0, &err, &offset, NULL);
+ if (*re == NULL)
+ debug (g, "osinfo: could not parse regular expression '%s': %s
(ignored)",
+ content, err);
+ }
+
+ return 0;
+}
+
+/* Read the attributes of the <media/> node. */
+static int
+read_media_node (guestfs_h *g, xmlXPathContextPtr xpathCtx,
+ xmlNodePtr media_node, struct osinfo *osinfo)
+{
+ osinfo->arch = (char *) xmlGetProp (media_node, BAD_CAST "arch");
+
+ osinfo->is_live_disk = 0; /* If no 'live' attr, defaults to false. */
+ {
+ CLEANUP_XMLFREE xmlChar *content = NULL;
+ content = xmlGetProp (media_node, BAD_CAST "live");
+ if (content)
+ osinfo->is_live_disk = XMLSTREQ (content, BAD_CAST "true");
+ }
+
+ osinfo->is_installer = true; /* If no 'installer' attr, defaults to true.
*/
+ {
+ CLEANUP_XMLFREE xmlChar *content = NULL;
+ content = xmlGetProp (media_node, BAD_CAST "installer");
+ if (content)
+ osinfo->is_installer = XMLSTREQ (content, BAD_CAST "true");
+ }
+
+ return 0;
+}
+
+static int parse_version (guestfs_h *g, xmlNodePtr node, struct osinfo *osinfo);
+static int parse_family (guestfs_h *g, xmlNodePtr node, struct osinfo *osinfo);
+static int parse_distro (guestfs_h *g, xmlNodePtr node, struct osinfo *osinfo);
+
+/* Read some fields under the <os/> node. */
+static int
+read_os_node (guestfs_h *g, xmlXPathContextPtr xpathCtx,
+ xmlNodePtr os_node, struct osinfo *osinfo)
+{
+ xmlNodePtr child;
+
+ for (child = os_node->children; child; child = child->next) {
+ if (STREQ ((const char *) child->name, "name"))
+ osinfo->product_name = (char *) xmlNodeGetContent (child);
+ else if (STREQ ((const char *) child->name, "version")) {
+ if (parse_version (g, child, osinfo) == -1)
+ return -1;
+ }
+ else if (STREQ ((const char *) child->name, "family")) {
+ if (parse_family (g, child, osinfo) == -1)
+ return -1;
+ }
+ else if (STREQ ((const char *) child->name, "distro")) {
+ if (parse_distro (g, child, osinfo) == -1)
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int
+parse_version (guestfs_h *g, xmlNodePtr node, struct osinfo *osinfo)
+{
+ CLEANUP_FREE char *content = NULL;
+
+ content = (char *) xmlNodeGetContent (node);
+ /* We parse either "X.Y" or "X" as version strings, so try to
parse
+ * only if the first character is a digit.
+ */
+ if (content && c_isdigit (content[0])) {
+ struct version version;
+ const int res = guestfs_int_version_from_x_y_or_x (g, &version, content);
+ if (res < 0)
+ return -1;
+ else if (res > 0) {
+ osinfo->major_version = version.v_major;
+ osinfo->minor_version = version.v_minor;
+ }
+ }
+
+ return 0;
+}
+
+static int
+parse_family (guestfs_h *g, xmlNodePtr node, struct osinfo *osinfo)
+{
+ CLEANUP_FREE char *content = NULL;
+
+ osinfo->type = OS_TYPE_UNKNOWN;
+
+ content = (char *) xmlNodeGetContent (node);
+ if (content) {
+ if (STREQ (content, "linux"))
+ osinfo->type = OS_TYPE_LINUX;
+ else if (STRPREFIX (content, "win"))
+ osinfo->type = OS_TYPE_WINDOWS;
+ else if (STREQ (content, "freebsd"))
+ osinfo->type = OS_TYPE_FREEBSD;
+ else if (STREQ (content, "netbsd"))
+ osinfo->type = OS_TYPE_NETBSD;
+ else if (STREQ (content, "msdos"))
+ osinfo->type = OS_TYPE_DOS;
+ else if (STREQ (content, "openbsd"))
+ osinfo->type = OS_TYPE_OPENBSD;
+ else
+ debug (g, "osinfo: warning: unknown <family> '%s'",
content);
+ }
+
+ return 0;
+}
+
+static int
+parse_distro (guestfs_h *g, xmlNodePtr node, struct osinfo *osinfo)
+{
+ CLEANUP_FREE char *content = NULL;
+
+ osinfo->distro = OS_DISTRO_UNKNOWN;
+
+ content = (char *) xmlNodeGetContent (node);
+ if (content) {
+ if (STREQ (content, "altlinux"))
+ osinfo->distro = OS_DISTRO_ALTLINUX;
+ else if (STREQ (content, "centos"))
+ osinfo->distro = OS_DISTRO_CENTOS;
+ else if (STREQ (content, "debian"))
+ osinfo->distro = OS_DISTRO_DEBIAN;
+ else if (STREQ (content, "fedora"))
+ osinfo->distro = OS_DISTRO_FEDORA;
+ else if (STREQ (content, "freebsd"))
+ osinfo->distro = OS_DISTRO_FREEBSD;
+ else if (STREQ (content, "mageia"))
+ osinfo->distro = OS_DISTRO_MAGEIA;
+ else if (STREQ (content, "mandriva"))
+ osinfo->distro = OS_DISTRO_MANDRIVA;
+ else if (STREQ (content, "netbsd"))
+ osinfo->distro = OS_DISTRO_NETBSD;
+ else if (STREQ (content, "openbsd"))
+ osinfo->distro = OS_DISTRO_OPENBSD;
+ else if (STREQ (content, "opensuse"))
+ osinfo->distro = OS_DISTRO_OPENSUSE;
+ else if (STREQ (content, "rhel"))
+ osinfo->distro = OS_DISTRO_RHEL;
+ else if (STREQ (content, "sled") || STREQ (content, "sles"))
+ osinfo->distro = OS_DISTRO_SLES;
+ else if (STREQ (content, "ubuntu"))
+ osinfo->distro = OS_DISTRO_UBUNTU;
+ else if (STRPREFIX (content, "win"))
+ osinfo->distro = OS_DISTRO_WINDOWS;
+ else
+ debug (g, "osinfo: warning: unknown <distro> '%s'",
content);
+ }
+
+ return 0;
+}
+
+static void
+free_osinfo_db_entry (struct osinfo *osinfo)
+{
+ free (osinfo->product_name);
+ free (osinfo->arch);
+
+ if (osinfo->re_system_id)
+ pcre_free (osinfo->re_system_id);
+ if (osinfo->re_volume_id)
+ pcre_free (osinfo->re_volume_id);
+ if (osinfo->re_publisher_id)
+ pcre_free (osinfo->re_publisher_id);
+ if (osinfo->re_application_id)
+ pcre_free (osinfo->re_application_id);
+}
diff --git a/lib/osinfo.c b/lib/osinfo.c
index ea2a7659a..6b5ad6de7 100644
--- a/lib/osinfo.c
+++ b/lib/osinfo.c
@@ -30,19 +30,6 @@
* release of an OS. We can easily add support for this if it becomes
* necessary.
*
- * (3) We have to do some translation of the distro names and versions
- * stored in the libosinfo files and the standard names returned by
- * libguestfs.
- *
- * (4) Media detection is only part of the story. We may still need
- * to inspect inside the image.
- *
- * (5) We only read the XML database files (at most) once per process,
- * and keep them cached. They are only read at all if someone tries
- * to inspect a CD/DVD/ISO.
- *
- * XXX Currently the database is not freed when the program exits /
- * library is unloaded, although we should probably do that.
*/
#include <config.h>
@@ -52,95 +39,47 @@
#include <string.h>
#include <unistd.h>
#include <dirent.h>
+#include <errno.h>
#include <assert.h>
#include <sys/types.h>
#include <libintl.h>
#include <sys/stat.h>
-#include <libxml/parser.h>
-#include <libxml/xpath.h>
-
#include "ignore-value.h"
-#include "glthread/lock.h"
#include "c-ctype.h"
#include "guestfs.h"
#include "guestfs-internal.h"
-gl_lock_define_initialized (static, osinfo_db_lock);
-static ssize_t osinfo_db_size = 0; /* 0 = unread, -1 = error, >= 1 = #records */
-static struct osinfo *osinfo_db = NULL;
-
-static int read_osinfo_db (guestfs_h *g);
-static void free_osinfo_db_entry (struct osinfo *);
+#include "osinfo.h"
-#define XMLSTREQ(a,b) (xmlStrEqual((a),(b)) == 1)
-/* Given one or more fields from the header of a CD/DVD/ISO, look up
- * the media in the libosinfo database and return our best guess for
- * the operating system.
- *
- * This returns:
- * -1 => a fatal error ('error' has been called, caller must not ignore it)
- * 0 => could not locate the OS
- * 1 => matching OS found, the osinfo_ret pointer has been filled in
- */
-int
-guestfs_int_osinfo_map (guestfs_h *g, const struct guestfs_isoinfo *isoinfo,
- const struct osinfo **osinfo_ret)
+#ifndef GUESTFS_PRIVATE
+void guestfs_int_debug (guestfs_h *g, const char *fs, ...)
{
- size_t i;
-
- /* We only need to lock the database when reading it for the first time. */
- gl_lock_lock (osinfo_db_lock);
- if (osinfo_db_size == 0) {
- if (read_osinfo_db (g) == -1) {
- gl_lock_unlock (osinfo_db_lock);
- return -1;
- }
- }
- gl_lock_unlock (osinfo_db_lock);
-
- if (osinfo_db_size <= 0)
- return 0;
-
- /* Look in the database to see if we can find a match. */
- for (i = 0; i < (size_t) osinfo_db_size; ++i) {
- if (osinfo_db[i].re_system_id) {
- if (!isoinfo->iso_system_id ||
- !match (g, isoinfo->iso_system_id, osinfo_db[i].re_system_id))
- continue;
- }
-
- if (osinfo_db[i].re_volume_id) {
- if (!isoinfo->iso_volume_id ||
- !match (g, isoinfo->iso_volume_id, osinfo_db[i].re_volume_id))
- continue;
- }
+ va_list args;
- if (osinfo_db[i].re_publisher_id) {
- if (!isoinfo->iso_publisher_id ||
- !match (g, isoinfo->iso_publisher_id, osinfo_db[i].re_publisher_id))
- continue;
- }
-
- if (osinfo_db[i].re_application_id) {
- if (!isoinfo->iso_application_id ||
- !match (g, isoinfo->iso_application_id, osinfo_db[i].re_application_id))
- continue;
- }
+ va_start (args, fs);
+ vfprintf (stderr, fs, args);
+ va_end (args);
+}
- debug (g, "osinfo: mapped disk to database entry %zu", i);
+void
+guestfs_int_perrorf (guestfs_h *g, const char *fs, ...)
+{
+ va_list args;
+ CLEANUP_FREE char *msg = NULL;
+ int err;
- if (osinfo_ret)
- *osinfo_ret = &osinfo_db[i];
- return 1;
- }
+ va_start (args, fs);
+ err = vasprintf (&msg, fs, args);
+ va_end (args);
- debug (g, "osinfo: no mapping found");
+ if (err < 0) return;
- return 0;
+ perror(msg);
}
+#endif /* GUESTFS_PRIVATE */
/* Read the libosinfo XML database files. The lock is held while
* this is called.
@@ -156,19 +95,18 @@ guestfs_int_osinfo_map (guestfs_h *g, const struct guestfs_isoinfo
*isoinfo,
* Try to use the shared osinfo database layout (and location) first:
*
https://gitlab.com/libosinfo/libosinfo/blob/master/docs/database-layout.txt
*/
-static int read_osinfo_db_xml (guestfs_h *g, const char *filename);
+static int read_osinfo_db_flat (guestfs_h *g, const char *directory,
+ read_osinfo_db_callback callback, void *opaque);
+static int read_osinfo_db_three_levels (guestfs_h *g, const char *directory,
+ read_osinfo_db_callback callback, void *opaque);
+static int read_osinfo_db_directory (guestfs_h *g, const char *directory,
+ read_osinfo_db_callback callback, void *opaque);
-static int read_osinfo_db_flat (guestfs_h *g, const char *directory);
-static int read_osinfo_db_three_levels (guestfs_h *g, const char *directory);
-static int read_osinfo_db_directory (guestfs_h *g, const char *directory);
-
-static int
-read_osinfo_db (guestfs_h *g)
+int
+read_osinfo_db (guestfs_h *g,
+ read_osinfo_db_callback callback, void *opaque)
{
int r;
- size_t i;
-
- assert (osinfo_db_size == 0);
/* (1) Try the shared osinfo directory, using either the
* $OSINFO_SYSTEM_DIR envvar or its default value.
@@ -181,59 +119,47 @@ read_osinfo_db (guestfs_h *g)
if (path == NULL)
path = "/usr/share/osinfo";
os_path = safe_asprintf (g, "%s/os", path);
- r = read_osinfo_db_three_levels (g, os_path);
+ r = read_osinfo_db_three_levels (g, os_path, callback, opaque);
}
if (r == -1)
- goto error;
+ return -1;
else if (r == 1)
return 0;
/* (2) Try the libosinfo directory, using the newer three-directory
* layout ($LIBOSINFO_DB_PATH / "os" / $group-ID / [file.xml]).
*/
- r = read_osinfo_db_three_levels (g, LIBOSINFO_DB_PATH "/os");
+ r = read_osinfo_db_three_levels (g, LIBOSINFO_DB_PATH "/os", callback,
opaque);
if (r == -1)
- goto error;
+ return -1;
else if (r == 1)
return 0;
/* (3) Try the libosinfo directory, using the old flat directory
* layout ($LIBOSINFO_DB_PATH / "oses" / [file.xml]).
*/
- r = read_osinfo_db_flat (g, LIBOSINFO_DB_PATH "/oses");
+ r = read_osinfo_db_flat (g, LIBOSINFO_DB_PATH "/oses", callback, opaque);
if (r == -1)
- goto error;
+ return -1;
else if (r == 1)
return 0;
/* Nothing found. */
return 0;
-
- error:
- /* Fatal error: free any database entries which have been read, and
- * mark the database as having a permanent error.
- */
- if (osinfo_db_size > 0) {
- for (i = 0; i < (size_t) osinfo_db_size; ++i)
- free_osinfo_db_entry (&osinfo_db[i]);
- }
- free (osinfo_db);
- osinfo_db = NULL;
- osinfo_db_size = -1;
-
- return -1;
}
static int
-read_osinfo_db_flat (guestfs_h *g, const char *directory)
+read_osinfo_db_flat (guestfs_h *g, const char *directory,
+ read_osinfo_db_callback callback, void *opaque)
{
debug (g, "osinfo: loading flat database from %s", directory);
- return read_osinfo_db_directory (g, directory);
+ return read_osinfo_db_directory (g, directory, callback, opaque);
}
static int
-read_osinfo_db_three_levels (guestfs_h *g, const char *directory)
+read_osinfo_db_three_levels (guestfs_h *g, const char *directory,
+ read_osinfo_db_callback callback, void *opaque)
{
DIR *dir;
int r;
@@ -259,7 +185,7 @@ read_osinfo_db_three_levels (guestfs_h *g, const char *directory)
/* Iterate only on directories. */
if (stat (pathname, &sb) == 0 && S_ISDIR (sb.st_mode)) {
- r = read_osinfo_db_directory (g, pathname);
+ r = read_osinfo_db_directory (g, pathname, callback, opaque);
if (r == -1)
goto error;
}
@@ -289,7 +215,8 @@ read_osinfo_db_three_levels (guestfs_h *g, const char *directory)
}
static int
-read_osinfo_db_directory (guestfs_h *g, const char *directory)
+read_osinfo_db_directory (guestfs_h *g, const char *directory,
+ read_osinfo_db_callback callback, void *opaque)
{
DIR *dir;
int r;
@@ -311,7 +238,7 @@ read_osinfo_db_directory (guestfs_h *g, const char *directory)
CLEANUP_FREE char *pathname = NULL;
pathname = safe_asprintf (g, "%s/%s", directory, d->d_name);
- r = read_osinfo_db_xml (g, pathname);
+ r = callback (g, pathname, opaque);
if (r == -1)
goto error;
}
@@ -339,317 +266,3 @@ read_osinfo_db_directory (guestfs_h *g, const char *directory)
return -1;
}
-
-static int read_iso_node (guestfs_h *g, xmlNodePtr iso_node, struct osinfo *osinfo);
-static int read_media_node (guestfs_h *g, xmlXPathContextPtr xpathCtx, xmlNodePtr
media_node, struct osinfo *osinfo);
-static int read_os_node (guestfs_h *g, xmlXPathContextPtr xpathCtx, xmlNodePtr os_node,
struct osinfo *osinfo);
-
-/* Read a single XML file from pathname (which is a full path).
- * Only memory allocation failures are fatal errors here.
- */
-static int
-read_osinfo_db_xml (guestfs_h *g, const char *pathname)
-{
- CLEANUP_XMLFREEDOC xmlDocPtr doc = NULL;
- CLEANUP_XMLXPATHFREECONTEXT xmlXPathContextPtr xpathCtx = NULL;
- CLEANUP_XMLXPATHFREEOBJECT xmlXPathObjectPtr xpathObj = NULL;
- xmlNodeSetPtr nodes;
- xmlNodePtr iso_node, media_node, os_node;
- struct osinfo *osinfo;
- size_t i;
-
- doc = xmlReadFile (pathname, NULL, XML_PARSE_NONET);
- if (doc == NULL) {
- debug (g, "osinfo: unable to parse XML file %s", pathname);
- return 0;
- }
-
- xpathCtx = xmlXPathNewContext (doc);
- if (xpathCtx == NULL) {
- error (g, _("osinfo: unable to create new XPath context"));
- return -1;
- }
-
- /* Get all <iso> nodes at any depth, then use the parent pointers in
- * order to work back up the tree.
- */
- xpathObj = xmlXPathEvalExpression (BAD_CAST "/libosinfo/os/media/iso",
- xpathCtx);
- if (xpathObj == NULL) {
- error (g, _("osinfo: %s: unable to evaluate XPath expression"),
- pathname);
- return -1;
- }
-
- nodes = xpathObj->nodesetval;
-
- if (nodes != NULL) {
- for (i = 0; i < (size_t) nodes->nodeNr; ++i) {
- iso_node = nodes->nodeTab[i];
- assert (iso_node != NULL);
- assert (STREQ ((const char *) iso_node->name, "iso"));
- assert (iso_node->type == XML_ELEMENT_NODE);
-
- media_node = iso_node->parent;
- assert (media_node != NULL);
- assert (STREQ ((const char *) media_node->name, "media"));
- assert (media_node->type == XML_ELEMENT_NODE);
-
- os_node = media_node->parent;
- assert (os_node != NULL);
- assert (STREQ ((const char *) os_node->name, "os"));
- assert (os_node->type == XML_ELEMENT_NODE);
-
- /* Allocate an osinfo record. */
- osinfo_db_size++;
- osinfo_db = safe_realloc (g, osinfo_db,
- sizeof (struct osinfo) * osinfo_db_size);
- osinfo = &osinfo_db[osinfo_db_size-1];
- memset (osinfo, 0, sizeof *osinfo);
-
- /* Read XML fields into the new osinfo record. */
- if (read_iso_node (g, iso_node, osinfo) == -1 ||
- read_media_node (g, xpathCtx, media_node, osinfo) == -1 ||
- read_os_node (g, xpathCtx, os_node, osinfo) == -1) {
- free_osinfo_db_entry (osinfo);
- osinfo_db_size--;
- return -1;
- }
-
-#if 0
- debug (g, "osinfo: %s: %s%s%s%s=> arch %s live %s installer %s product %s
type %d distro %d version %d.%d",
- pathname,
- osinfo->re_system_id ? "<system-id/> " : "",
- osinfo->re_volume_id ? "<volume-id/> " : "",
- osinfo->re_publisher_id ? "<publisher-id/> " :
"",
- osinfo->re_application_id ? "<application-id/> " :
"",
- osinfo->arch ? osinfo->arch : "(none)",
- osinfo->is_live_disk ? "true" : "false",
- osinfo->is_installer ? "true" : "false",
- osinfo->product_name ? osinfo->product_name : "(none)",
- (int) osinfo->type, (int) osinfo->distro,
- osinfo->major_version, osinfo->minor_version);
-#endif
- }
- }
-
- return 0;
-}
-
-static int compile_re (guestfs_h *g, xmlNodePtr child, pcre **re);
-
-/* Read the regular expressions under the <iso> node. libosinfo
- * itself uses the glib function 'g_regex_match_simple'. That appears
- * to implement PCRE, however I have not checked in detail.
- */
-static int
-read_iso_node (guestfs_h *g, xmlNodePtr iso_node, struct osinfo *osinfo)
-{
- xmlNodePtr child;
-
- for (child = iso_node->children; child; child = child->next) {
- if (STREQ ((const char *) child->name, "system-id")) {
- if (compile_re (g, child, &osinfo->re_system_id) == -1)
- return -1;
- }
- else if (STREQ ((const char *) child->name, "volume-id")) {
- if (compile_re (g, child, &osinfo->re_volume_id) == -1)
- return -1;
- }
- else if (STREQ ((const char *) child->name, "publisher-id")) {
- if (compile_re (g, child, &osinfo->re_publisher_id) == -1)
- return -1;
- }
- else if (STREQ ((const char *) child->name, "application-id")) {
- if (compile_re (g, child, &osinfo->re_application_id) == -1)
- return -1;
- }
- }
-
- return 0;
-}
-
-static int
-compile_re (guestfs_h *g, xmlNodePtr node, pcre **re)
-{
- const char *err;
- int offset;
- CLEANUP_FREE char *content = (char *) xmlNodeGetContent (node);
-
- if (content) {
- *re = pcre_compile (content, 0, &err, &offset, NULL);
- if (*re == NULL)
- debug (g, "osinfo: could not parse regular expression '%s': %s
(ignored)",
- content, err);
- }
-
- return 0;
-}
-
-/* Read the attributes of the <media/> node. */
-static int
-read_media_node (guestfs_h *g, xmlXPathContextPtr xpathCtx,
- xmlNodePtr media_node, struct osinfo *osinfo)
-{
- osinfo->arch = (char *) xmlGetProp (media_node, BAD_CAST "arch");
-
- osinfo->is_live_disk = 0; /* If no 'live' attr, defaults to false. */
- {
- CLEANUP_XMLFREE xmlChar *content = NULL;
- content = xmlGetProp (media_node, BAD_CAST "live");
- if (content)
- osinfo->is_live_disk = XMLSTREQ (content, BAD_CAST "true");
- }
-
- osinfo->is_installer = true; /* If no 'installer' attr, defaults to true.
*/
- {
- CLEANUP_XMLFREE xmlChar *content = NULL;
- content = xmlGetProp (media_node, BAD_CAST "installer");
- if (content)
- osinfo->is_installer = XMLSTREQ (content, BAD_CAST "true");
- }
-
- return 0;
-}
-
-static int parse_version (guestfs_h *g, xmlNodePtr node, struct osinfo *osinfo);
-static int parse_family (guestfs_h *g, xmlNodePtr node, struct osinfo *osinfo);
-static int parse_distro (guestfs_h *g, xmlNodePtr node, struct osinfo *osinfo);
-
-/* Read some fields under the <os/> node. */
-static int
-read_os_node (guestfs_h *g, xmlXPathContextPtr xpathCtx,
- xmlNodePtr os_node, struct osinfo *osinfo)
-{
- xmlNodePtr child;
-
- for (child = os_node->children; child; child = child->next) {
- if (STREQ ((const char *) child->name, "name"))
- osinfo->product_name = (char *) xmlNodeGetContent (child);
- else if (STREQ ((const char *) child->name, "version")) {
- if (parse_version (g, child, osinfo) == -1)
- return -1;
- }
- else if (STREQ ((const char *) child->name, "family")) {
- if (parse_family (g, child, osinfo) == -1)
- return -1;
- }
- else if (STREQ ((const char *) child->name, "distro")) {
- if (parse_distro (g, child, osinfo) == -1)
- return -1;
- }
- }
-
- return 0;
-}
-
-static int
-parse_version (guestfs_h *g, xmlNodePtr node, struct osinfo *osinfo)
-{
- CLEANUP_FREE char *content = NULL;
-
- content = (char *) xmlNodeGetContent (node);
- /* We parse either "X.Y" or "X" as version strings, so try to
parse
- * only if the first character is a digit.
- */
- if (content && c_isdigit (content[0])) {
- struct version version;
- const int res = guestfs_int_version_from_x_y_or_x (g, &version, content);
- if (res < 0)
- return -1;
- else if (res > 0) {
- osinfo->major_version = version.v_major;
- osinfo->minor_version = version.v_minor;
- }
- }
-
- return 0;
-}
-
-static int
-parse_family (guestfs_h *g, xmlNodePtr node, struct osinfo *osinfo)
-{
- CLEANUP_FREE char *content = NULL;
-
- osinfo->type = OS_TYPE_UNKNOWN;
-
- content = (char *) xmlNodeGetContent (node);
- if (content) {
- if (STREQ (content, "linux"))
- osinfo->type = OS_TYPE_LINUX;
- else if (STRPREFIX (content, "win"))
- osinfo->type = OS_TYPE_WINDOWS;
- else if (STREQ (content, "freebsd"))
- osinfo->type = OS_TYPE_FREEBSD;
- else if (STREQ (content, "netbsd"))
- osinfo->type = OS_TYPE_NETBSD;
- else if (STREQ (content, "msdos"))
- osinfo->type = OS_TYPE_DOS;
- else if (STREQ (content, "openbsd"))
- osinfo->type = OS_TYPE_OPENBSD;
- else
- debug (g, "osinfo: warning: unknown <family> '%s'",
content);
- }
-
- return 0;
-}
-
-static int
-parse_distro (guestfs_h *g, xmlNodePtr node, struct osinfo *osinfo)
-{
- CLEANUP_FREE char *content = NULL;
-
- osinfo->distro = OS_DISTRO_UNKNOWN;
-
- content = (char *) xmlNodeGetContent (node);
- if (content) {
- if (STREQ (content, "altlinux"))
- osinfo->distro = OS_DISTRO_ALTLINUX;
- else if (STREQ (content, "centos"))
- osinfo->distro = OS_DISTRO_CENTOS;
- else if (STREQ (content, "debian"))
- osinfo->distro = OS_DISTRO_DEBIAN;
- else if (STREQ (content, "fedora"))
- osinfo->distro = OS_DISTRO_FEDORA;
- else if (STREQ (content, "freebsd"))
- osinfo->distro = OS_DISTRO_FREEBSD;
- else if (STREQ (content, "mageia"))
- osinfo->distro = OS_DISTRO_MAGEIA;
- else if (STREQ (content, "mandriva"))
- osinfo->distro = OS_DISTRO_MANDRIVA;
- else if (STREQ (content, "netbsd"))
- osinfo->distro = OS_DISTRO_NETBSD;
- else if (STREQ (content, "openbsd"))
- osinfo->distro = OS_DISTRO_OPENBSD;
- else if (STREQ (content, "opensuse"))
- osinfo->distro = OS_DISTRO_OPENSUSE;
- else if (STREQ (content, "rhel"))
- osinfo->distro = OS_DISTRO_RHEL;
- else if (STREQ (content, "sled") || STREQ (content, "sles"))
- osinfo->distro = OS_DISTRO_SLES;
- else if (STREQ (content, "ubuntu"))
- osinfo->distro = OS_DISTRO_UBUNTU;
- else if (STRPREFIX (content, "win"))
- osinfo->distro = OS_DISTRO_WINDOWS;
- else
- debug (g, "osinfo: warning: unknown <distro> '%s'",
content);
- }
-
- return 0;
-}
-
-static void
-free_osinfo_db_entry (struct osinfo *osinfo)
-{
- free (osinfo->product_name);
- free (osinfo->arch);
-
- if (osinfo->re_system_id)
- pcre_free (osinfo->re_system_id);
- if (osinfo->re_volume_id)
- pcre_free (osinfo->re_volume_id);
- if (osinfo->re_publisher_id)
- pcre_free (osinfo->re_publisher_id);
- if (osinfo->re_application_id)
- pcre_free (osinfo->re_application_id);
-}
diff --git a/lib/osinfo.h b/lib/osinfo.h
new file mode 100644
index 000000000..68846747e
--- /dev/null
+++ b/lib/osinfo.h
@@ -0,0 +1,27 @@
+/* libguestfs
+ * Copyright (C) 2017 Red Hat Inc.
+ * Copyright (C) 2017 SUSE Inc.
+ *
+ * This library 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; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef OSINFO_H
+#define OSINFO_H
+
+typedef int (*read_osinfo_db_callback) (guestfs_h *g, const char *path, void *opaque);
+
+extern int read_osinfo_db (guestfs_h *g, read_osinfo_db_callback callback, void
*opaque);
+
+#endif /* OSINFO_H */
diff --git a/mllib/Makefile.am b/mllib/Makefile.am
index aa5472ade..2541ce280 100644
--- a/mllib/Makefile.am
+++ b/mllib/Makefile.am
@@ -40,6 +40,7 @@ SOURCES_MLI = \
getopt.mli \
JSON.mli \
mkdtemp.mli \
+ osinfo.mli \
planner.mli \
progress.mli \
regedit.mli \
@@ -71,7 +72,8 @@ SOURCES_ML = \
exit.ml \
checksums.ml \
xml.ml \
- xpath_helpers.ml
+ xpath_helpers.ml \
+ osinfo.ml
SOURCES_C = \
../common/visit/visit.c \
@@ -79,6 +81,9 @@ SOURCES_C = \
../common/options/keys.c \
../common/options/uri.c \
../common/progress/progress.c \
+ ../lib/alloc.c \
+ ../lib/osinfo.c \
+ ../lib/osinfo.h \
common_utils-c.c \
dev_t-c.c \
exit-c.c \
@@ -86,6 +91,7 @@ SOURCES_C = \
fsync-c.c \
getopt-c.c \
mkdtemp-c.c \
+ osinfo-c.c \
progress-c.c \
statvfs-c.c \
uri-c.c \
@@ -119,7 +125,8 @@ libmllib_a_CPPFLAGS = \
-I$(top_srcdir)/lib \
-I$(top_srcdir)/common/visit \
-I$(top_srcdir)/common/options \
- -I$(top_srcdir)/common/progress
+ -I$(top_srcdir)/common/progress \
+ -DLIBOSINFO_DB_PATH='"$(datadir)/libosinfo/db"'
libmllib_a_CFLAGS = \
$(WARN_CFLAGS) $(WERROR_CFLAGS) \
$(LIBVIRT_CFLAGS) $(LIBXML2_CFLAGS) \
diff --git a/mllib/osinfo-c.c b/mllib/osinfo-c.c
new file mode 100644
index 000000000..2f03322d3
--- /dev/null
+++ b/mllib/osinfo-c.c
@@ -0,0 +1,100 @@
+/* Bindings for osinfo db reading function.
+ * Copyright (C) 2017 SUSE Inc.
+ *
+ * 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.
+ */
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include <caml/alloc.h>
+#include <caml/callback.h>
+#include <caml/fail.h>
+#include <caml/memory.h>
+#include <caml/mlvalues.h>
+
+#include "guestfs.h"
+#include "guestfs-internal.h"
+#include "osinfo.h"
+
+#pragma GCC diagnostic ignored "-Wmissing-prototypes"
+
+struct callback_wrapper_args {
+ /* In both case we are pointing to local roots, hence why these are
+ * value* not value.
+ */
+ value *exnp; /* Safe place to store any exception
+ raised by callback */
+ value *fvp; /* callback. */
+};
+
+static int read_osinfo_db_callback_wrapper (guestfs_h *g, const char *path, void
*opaque);
+
+value
+guestfs_int_mllib_read_osinfo_db (value gv, value fv)
+{
+ CAMLparam2 (gv, fv);
+ guestfs_h *g = (guestfs_h *) Int64_val (gv);
+ struct callback_wrapper_args args;
+
+ /* This stack address is used to point to the exception, if one is
+ * raised in the visitor_function. Note that the macro initializes
+ * this to Val_unit, which is how we know if an exception was set.
+ */
+ CAMLlocal1 (exn);
+
+ args.exnp = &exn;
+ args.fvp = &fv;
+
+ if (read_osinfo_db (g, read_osinfo_db_callback_wrapper, &args) == -1) {
+ if (exn != Val_unit) {
+ /* The failure was caused by the callback raising an
+ * exception. Re-raise it here.
+ */
+ caml_raise (exn);
+ }
+
+ caml_failwith ("read_osinfo_db");
+}
+
+ CAMLreturn (Val_unit);
+}
+
+static int
+read_osinfo_db_callback_wrapper (guestfs_h *g, const char *path, void *opaque)
+{
+ CAMLparam0 ();
+ CAMLlocal2 (pathv, v);
+ struct callback_wrapper_args *args = opaque;
+
+ assert (path != NULL);
+ assert (args != NULL);
+
+ pathv = caml_copy_string (path);
+
+ v = caml_callback_exn (*args->fvp, pathv);
+
+ if (Is_exception_result (v)) {
+ *args->exnp = Extract_exception (v);
+ return -1;
+ }
+
+ /* No error, return normally. */
+ CAMLreturnT (int, 0);
+}
diff --git a/mllib/osinfo.ml b/mllib/osinfo.ml
new file mode 100644
index 000000000..f5afbd889
--- /dev/null
+++ b/mllib/osinfo.ml
@@ -0,0 +1,26 @@
+(* virt-builder
+ * Copyright (C) 2016 - SUSE Inc.
+ *
+ * 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.
+ *)
+open Common_utils
+
+type osinfo_db_callback = string -> unit
+
+external c_read_osinfo_db : int64 -> osinfo_db_callback -> unit =
+ "guestfs_int_mllib_read_osinfo_db"
+
+let read_osinfo_db g f =
+ c_read_osinfo_db (Guestfs.c_pointer g) f
diff --git a/mllib/osinfo.mli b/mllib/osinfo.mli
new file mode 100644
index 000000000..d106edae2
--- /dev/null
+++ b/mllib/osinfo.mli
@@ -0,0 +1,31 @@
+(* virt-builder
+ * Copyright (C) 2016 - SUSE Inc.
+ *
+ * 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.
+ *)
+
+(** Bindings for the src/osinfo.h libosinfo database reading API. *)
+
+type osinfo_db_callback = string -> unit
+(** The osinfo_db_callback is a callback called for each data file
+ in the libosinfo database. The argument of the function is
+ the absolute path of the data file.
+
+ The callback may raise an exception, which will cause the whole
+ database read to fail with an error (raising the same exception). *)
+
+val read_osinfo_db : Guestfs.t -> osinfo_db_callback -> unit
+(** [read_osinfo_db g callback] will find all the libosinfo database
+ files and call the callback on them. *)
--
2.11.0