From 90e7981082a2685b235724a6dd9b737cb90fe553 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Tue, 27 Nov 2012 17:38:31 +0000 Subject: inspection: Read libosinfo database in order to inspect OS install CD/DVD/ISOs (RHBZ#803650, RHBZ#805417). --- src/Makefile.am | 2 + src/guestfs-internal.h | 30 +++ src/inspect-fs-cd.c | 46 ++++ src/inspect-fs.c | 42 +++- src/osinfo.c | 604 +++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 714 insertions(+), 10 deletions(-) create mode 100644 src/osinfo.c (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index db5fb518..198b4f20 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -168,6 +168,7 @@ libguestfs_la_SOURCES = \ listfs.c \ lpj.c \ match.c \ + osinfo.c \ private-data.c \ proto.c \ tmpdirs.c \ @@ -194,6 +195,7 @@ libguestfs_la_LIBADD += liberrnostring.la libprotocol.la libguestfs_la_CFLAGS = \ -DGUESTFS_DEFAULT_PATH='"$(libdir)/guestfs"' \ -DGUESTFS_WARN_DEPRECATED=1 \ + -DLIBOSINFO_DB_PATH='"$(datadir)/libosinfo/db"' \ $(PCRE_CFLAGS) \ $(LIBVIRT_CFLAGS) $(LIBXML2_CFLAGS) \ $(WARN_CFLAGS) $(WERROR_CFLAGS) \ diff --git a/src/guestfs-internal.h b/src/guestfs-internal.h index 9e9b7084..3e87245e 100644 --- a/src/guestfs-internal.h +++ b/src/guestfs-internal.h @@ -47,6 +47,7 @@ #define STRNEQLEN(a,b,n) (strncmp((a),(b),(n)) != 0) #define STRCASENEQLEN(a,b,n) (strncasecmp((a),(b),(n)) != 0) #define STRPREFIX(a,b) (strncmp((a),(b),strlen((b))) == 0) +#define STRSUFFIX(a,b) (strlen((a)) >= strlen((b)) && STREQ((a)+strlen((a))-strlen((b)),(b))) #define _(str) dgettext(PACKAGE, (str)) #define N_(str) dgettext(PACKAGE, (str)) @@ -590,6 +591,8 @@ extern int guestfs___parse_unsigned_int_ignore_trailing (guestfs_h *g, const cha extern int guestfs___parse_major_minor (guestfs_h *g, struct inspect_fs *fs); extern char *guestfs___first_line_of_file (guestfs_h *g, const char *filename); extern int guestfs___first_egrep_of_file (guestfs_h *g, const char *filename, const char *eregex, int iflag, char **ret); +extern void guestfs___check_package_format (guestfs_h *g, struct inspect_fs *fs); +extern void guestfs___check_package_management (guestfs_h *g, struct inspect_fs *fs); /* inspect-fs-unix.c */ extern int guestfs___check_linux_root (guestfs_h *g, struct inspect_fs *fs); @@ -604,6 +607,7 @@ extern int guestfs___check_windows_root (guestfs_h *g, struct inspect_fs *fs); /* inspect-fs-cd.c */ extern int guestfs___check_installer_root (guestfs_h *g, struct inspect_fs *fs); +extern int guestfs___check_installer_iso (guestfs_h *g, struct inspect_fs *fs, const char *device); /* dbdump.c */ typedef int (*guestfs___db_dump_callback) (guestfs_h *g, const unsigned char *key, size_t keylen, const unsigned char *value, size_t valuelen, void *opaque); @@ -622,6 +626,32 @@ extern void guestfs___free_fuse (guestfs_h *g); extern virConnectPtr guestfs___open_libvirt_connection (guestfs_h *g, const char *uri, unsigned int flags); #endif +/* osinfo.c */ +struct osinfo { + /* Data provided by libosinfo database. */ + enum inspect_os_type type; + enum inspect_os_distro distro; + char *product_name; + int major_version; + int minor_version; + char *arch; + int is_live_disk; + +#if 0 + /* Not yet available in libosinfo database. */ + char *product_variant; + int is_netinst_disk; + int is_multipart_disk; +#endif + + /* The regular expressions used to match ISOs. */ + pcre *re_system_id; + pcre *re_volume_id; + pcre *re_publisher_id; + pcre *re_application_id; +}; +extern int guestfs___osinfo_map (guestfs_h *g, const struct guestfs_isoinfo *isoinfo, const struct osinfo **osinfo_ret); + /* command.c */ struct command; typedef void (*cmd_stdout_callback) (guestfs_h *g, void *data, const char *line, size_t len); diff --git a/src/inspect-fs-cd.c b/src/inspect-fs-cd.c index aef61d6a..05559b96 100644 --- a/src/inspect-fs-cd.c +++ b/src/inspect-fs-cd.c @@ -477,3 +477,49 @@ guestfs___check_installer_root (guestfs_h *g, struct inspect_fs *fs) return 0; } + +/* This is called for whole block devices. See if the device is an + * ISO and we are able to read the ISO info from it. In that case, + * try using libosinfo to map from the volume ID and other strings + * directly to the operating system type. + */ +int +guestfs___check_installer_iso (guestfs_h *g, struct inspect_fs *fs, + const char *device) +{ + struct guestfs_isoinfo *isoinfo; + const struct osinfo *osinfo; + int r; + + guestfs_push_error_handler (g, NULL, NULL); + isoinfo = guestfs_isoinfo_device (g, device); + guestfs_pop_error_handler (g); + if (!isoinfo) + return 0; + + r = guestfs___osinfo_map (g, isoinfo, &osinfo); + guestfs_free_isoinfo (isoinfo); + if (r == -1) /* Fatal error. */ + return -1; + if (r == 0) /* Could not locate any matching ISO. */ + return 0; + + /* Otherwise we matched an ISO, so fill in the fs fields. */ + fs->device = safe_strdup (g, device); + fs->is_root = 1; + fs->content = FS_CONTENT_INSTALLER; + fs->format = OS_FORMAT_INSTALLER; + fs->type = osinfo->type; + fs->distro = osinfo->distro; + fs->product_name = + osinfo->product_name ? safe_strdup (g, osinfo->product_name) : NULL; + fs->major_version = osinfo->major_version; + fs->minor_version = osinfo->minor_version; + fs->arch = osinfo->arch ? safe_strdup (g, osinfo->arch) : NULL; + fs->is_live_disk = osinfo->is_live_disk; + + guestfs___check_package_format (g, fs); + guestfs___check_package_management (g, fs); + + return 1; +} diff --git a/src/inspect-fs.c b/src/inspect-fs.c index 0ddeae13..2c81e412 100644 --- a/src/inspect-fs.c +++ b/src/inspect-fs.c @@ -80,8 +80,6 @@ free_regexps (void) } static int check_filesystem (guestfs_h *g, const char *device, int is_block, int is_partnum); -static void check_package_format (guestfs_h *g, struct inspect_fs *fs); -static void check_package_management (guestfs_h *g, struct inspect_fs *fs); static int extend_fses (guestfs_h *g); /* Find out if 'device' contains a filesystem. If it does, add @@ -118,6 +116,24 @@ guestfs___check_for_filesystem_on (guestfs_h *g, const char *device, return 0; } + /* If it's a whole device, see if it is an install ISO. */ + if (is_block) { + if (extend_fses (g) == -1) + return -1; + fs = &g->fses[g->nr_fses-1]; + + r = guestfs___check_installer_iso (g, fs, device); + if (r == -1) { /* Fatal error. */ + g->nr_fses--; + return -1; + } + if (r > 0) /* Found something. */ + return 0; + + /* Didn't find anything. Fall through ... */ + g->nr_fses--; + } + /* Try mounting the device. As above, ignore errors. */ guestfs_push_error_handler (g, NULL, NULL); if (vfs_type && STREQ (vfs_type, "ufs")) { /* Hack for the *BSDs. */ @@ -276,8 +292,14 @@ check_filesystem (guestfs_h *g, const char *device, */ fs->arch = safe_strdup (g, "i386"); } - /* Install CD/disk? Skip these checks if it's not a whole device - * (eg. CD) or the first partition (eg. bootable USB key). + /* Install CD/disk? + * + * Note that we checked (above) for an install ISO, but there are + * other types of install image (eg. USB keys) which that check + * wouldn't have picked up. + * + * Skip these checks if it's not a whole device (eg. CD) or the + * first partition (eg. bootable USB key). */ else if ((is_block || is_partnum == 1) && (guestfs_is_file (g, "/isolinux/isolinux.cfg") > 0 || @@ -298,8 +320,8 @@ check_filesystem (guestfs_h *g, const char *device, /* The above code should have set fs->type and fs->distro fields, so * we can now guess the package management system. */ - check_package_format (g, fs); - check_package_management (g, fs); + guestfs___check_package_format (g, fs); + guestfs___check_package_management (g, fs); return 0; } @@ -403,8 +425,8 @@ guestfs___parse_major_minor (guestfs_h *g, struct inspect_fs *fs) * simple function of the distro and major_version fields, so these * can never return an error. We might be cleverer in future. */ -static void -check_package_format (guestfs_h *g, struct inspect_fs *fs) +void +guestfs___check_package_format (guestfs_h *g, struct inspect_fs *fs) { switch (fs->distro) { case OS_DISTRO_FEDORA: @@ -450,8 +472,8 @@ check_package_format (guestfs_h *g, struct inspect_fs *fs) } } -static void -check_package_management (guestfs_h *g, struct inspect_fs *fs) +void +guestfs___check_package_management (guestfs_h *g, struct inspect_fs *fs) { switch (fs->distro) { case OS_DISTRO_FEDORA: diff --git a/src/osinfo.c b/src/osinfo.c new file mode 100644 index 00000000..0be06112 --- /dev/null +++ b/src/osinfo.c @@ -0,0 +1,604 @@ +/* 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) Ignore the libosinfo library itself, since we don't care + * for GObject nonsense. The XML database contains all we need. + * + * (2) Ignore os/upgrades and os/derives-from fields. This is + * safe(-ish) since the media identifiers always change for every + * 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 + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_LIBXML2 +#include +#include +#endif + +#include "ignore-value.h" +#include "glthread/lock.h" + +#include "guestfs.h" +#include "guestfs-internal.h" + +#ifdef HAVE_LIBXML2 + +static pcre *re_major_minor; + +static void compile_regexps (void) __attribute__((constructor)); +static void free_regexps (void) __attribute__((destructor)); + +static void +compile_regexps (void) +{ + const char *err; + int offset; + +#define COMPILE(re,pattern,options) \ + do { \ + re = pcre_compile ((pattern), (options), &err, &offset, NULL); \ + if (re == NULL) { \ + ignore_value (write (2, err, strlen (err))); \ + abort (); \ + } \ + } while (0) + + COMPILE (re_major_minor, "(\\d+)\\.(\\d+)", 0); +} + +static void +free_regexps (void) +{ + pcre_free (re_major_minor); +} + +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 *); + +/* 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___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) == -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; +} + +/* Read the libosinfo XML database files. The lock is held while + * this is called. + * + * Returns: + * -1 => a fatal error ('error' has been called) + * 0 => OK + * + * Note that failure to find or parse the XML files is *not* a fatal + * error, since we should fall back silently if these are not + * available. Although we'll emit some debug if this happens. + */ +#define LIBOSINFO_DB_OS_PATH LIBOSINFO_DB_PATH "/oses" + +static int read_osinfo_db_xml (guestfs_h *g, const char *filename); + +static int +read_osinfo_db (guestfs_h *g) +{ + DIR *dir = NULL; + struct dirent *d; + int r; + size_t i; + + assert (osinfo_db_size == 0); + + dir = opendir (LIBOSINFO_DB_OS_PATH); + if (!dir) { + debug (g, "osinfo: %s: %s", LIBOSINFO_DB_OS_PATH, strerror (errno)); + return -1; + } + + debug (g, "osinfo: loading database from %s", LIBOSINFO_DB_OS_PATH); + + for (;;) { + errno = 0; + d = readdir (dir); + if (!d) break; + + if (STRSUFFIX (d->d_name, ".xml")) { + r = read_osinfo_db_xml (g, d->d_name); + if (r == -1) + goto error; + } + } + + /* Check for failure in readdir. */ + if (errno != 0) { + perrorf (g, "readdir: %s", LIBOSINFO_DB_OS_PATH); + goto error; + } + + /* Close the directory handle. */ + r = closedir (dir); + dir = NULL; + if (r == -1) { + perrorf (g, "closedir: %s", LIBOSINFO_DB_OS_PATH); + goto error; + } + + return 0; + + error: + if (dir) + closedir (dir); + + /* 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_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 LIBOSINFO_DB_OS_PATH/filename. Only + * memory allocation failures are fatal errors here. + */ +static int +read_osinfo_db_xml (guestfs_h *g, const char *filename) +{ + const size_t pathname_len = + strlen (LIBOSINFO_DB_OS_PATH) + strlen (filename) + 2; + char pathname[pathname_len]; + xmlDocPtr doc = NULL; + xmlXPathContextPtr xpathCtx = NULL; + xmlXPathObjectPtr xpathObj = NULL; + xmlNodeSetPtr nodes; + xmlNodePtr iso_node, media_node, os_node; + struct osinfo *osinfo; + size_t i; + int ret = 0; + + snprintf (pathname, pathname_len, "%s/%s", LIBOSINFO_DB_OS_PATH, filename); + + doc = xmlParseFile (pathname); + if (doc == NULL) { + debug (g, "osinfo: unable to parse XML file %s", pathname); + goto cleanup; + } + + xpathCtx = xmlXPathNewContext (doc); + if (xpathCtx == NULL) { + error (g, _("osinfo: unable to create new XPath context")); + ret = -1; + goto cleanup; + } + + /* Get all 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); + ret = -1; + goto cleanup; + } + + nodes = xpathObj->nodesetval; + + 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--; + ret = -1; + goto cleanup; + } + +#if 0 + debug (g, "osinfo: %s: %s%s%s%s=> arch %s live %s product %s type %d distro %d version %d.%d", + filename, + osinfo->re_system_id ? " " : "", + osinfo->re_volume_id ? " " : "", + osinfo->re_publisher_id ? " " : "", + osinfo->re_application_id ? " " : "", + osinfo->arch ? osinfo->arch : "(none)", + osinfo->is_live_disk ? "true" : "false", + osinfo->product_name ? osinfo->product_name : "(none)", + (int) osinfo->type, (int) osinfo->distro, + osinfo->major_version, osinfo->minor_version); +#endif + } + + cleanup: + if (xpathObj) xmlXPathFreeObject (xpathObj); + if (xpathCtx) xmlXPathFreeContext (xpathCtx); + if (doc) xmlFreeDoc (doc); + + return ret; +} + +static int compile_re (guestfs_h *g, xmlNodePtr child, pcre **re); + +/* Read the regular expressions under the 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) +{ + char *content; + const char *err; + int offset; + + 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); + } + free (content); + + return 0; +} + +/* Read the attributes of the node. */ +static int +read_media_node (guestfs_h *g, xmlXPathContextPtr xpathCtx, + xmlNodePtr media_node, struct osinfo *osinfo) +{ + xmlXPathObjectPtr xp; + xmlNodePtr attr; + char *content; + + xpathCtx->node = media_node; + xp = xmlXPathEvalExpression (BAD_CAST "./@arch", xpathCtx); + if (xp && xp->nodesetval && xp->nodesetval->nodeNr > 0) { + attr = xp->nodesetval->nodeTab[0]; + assert (attr); + assert (attr->type == XML_ATTRIBUTE_NODE); + osinfo->arch = (char *) xmlNodeGetContent (attr); + } + xmlXPathFreeObject (xp); + + osinfo->is_live_disk = 0; /* If no 'live' attr, defaults to false. */ + + xpathCtx->node = media_node; + xp = xmlXPathEvalExpression (BAD_CAST "./@live", xpathCtx); + if (xp && xp->nodesetval && xp->nodesetval->nodeNr > 0) { + attr = xp->nodesetval->nodeTab[0]; + assert (attr); + assert (attr->type == XML_ATTRIBUTE_NODE); + content = (char *) xmlNodeGetContent (attr); + osinfo->is_live_disk = STREQ (content, "true"); + free (content); + } + xmlXPathFreeObject (xp); + + 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 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) +{ + char *content; + char *major, *minor; + + content = (char *) xmlNodeGetContent (node); + if (content) { + if (match2 (g, content, re_major_minor, &major, &minor)) { + osinfo->major_version = guestfs___parse_unsigned_int (g, major); + free (major); + if (osinfo->major_version == -1) { + free (minor); + return -1; + } + osinfo->minor_version = guestfs___parse_unsigned_int (g, minor); + free (minor); + if (osinfo->minor_version == -1) + return -1; + } + } + + free (content); + + return 0; +} + +static int +parse_family (guestfs_h *g, xmlNodePtr node, struct osinfo *osinfo) +{ + char *content; + + 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 '%s'", content); + } + + free (content); + + return 0; +} + +static int +parse_distro (guestfs_h *g, xmlNodePtr node, struct osinfo *osinfo) +{ + char *content; + + osinfo->distro = OS_DISTRO_UNKNOWN; + + content = (char *) xmlNodeGetContent (node); + if (content) { + 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, "mandriva")) + osinfo->distro = OS_DISTRO_MANDRIVA; + 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, "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 '%s'", content); + } + + free (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); +} + +#else /* !HAVE_LIBXML2 */ + +int +guestfs___osinfo_map (guestfs_h *g, + const char *system_id, + const char *volume_id, + const char *publisher_id, + const char *application_id, + const struct osinfo **osinfo_ret) +{ + debug (g, "osinfo: libxml2 not available"); + return 0; +} + +#endif /* !HAVE_LIBXML2 */ -- cgit