[Libguestfs] [PATCH INCOMPLETE] Add ability to inspect install disks and live CDs.

Richard W.M. Jones rjones at redhat.com
Fri Jan 14 22:59:25 UTC 2011


This patch is not complete yet because it needs to support Fedora,
RHEL and Windows CDs.

Some examples of the current output:

$ virt-inspector ubuntu-10.10-desktop-amd64.iso 
<?xml version="1.0"?>
<operatingsystems>
  <operatingsystem>
    <root>/dev/sda</root>
    <name>linux</name>
    <distro>ubuntu</distro>
    <product_name>Ubuntu 10.10 "Maverick Meerkat" - Release amd64 (20101007)</product_name>
    <major_version>10</major_version>
    <minor_version>10</minor_version>
    <format>installer</format>
    <live/>
    <mountpoints>
      <mountpoint dev="/dev/sda">/</mountpoint>
    </mountpoints>
    <filesystems>
      <filesystem dev="/dev/sda">
        <type>iso9660</type>
        <label>Ubuntu 10.10 amd64</label>
      </filesystem>
    </filesystems>
    <applications/>
  </operatingsystem>
</operatingsystems>

$ virt-inspector debian-505-amd64-netinst.iso 
<?xml version="1.0"?>
<operatingsystems>
  <operatingsystem>
    <root>/dev/sda</root>
    <name>linux</name>
    <distro>debian</distro>
    <product_name>Debian GNU/Linux 5.0.5 "Lenny" - Official amd64 NETINST Binary-1 20100627-10:37</product_name>
    <major_version>5</major_version>
    <minor_version>0</minor_version>
    <format>installer</format>
    <netinst/>
    <mountpoints>
      <mountpoint dev="/dev/sda">/</mountpoint>
    </mountpoints>
    <filesystems>
      <filesystem dev="/dev/sda">
        <type>iso9660</type>
        <label>Debian 5.0.5 amd64 Bin-1</label>
      </filesystem>
    </filesystems>
    <applications/>
  </operatingsystem>
</operatingsystems>

Rich.

-- 
Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones
libguestfs lets you edit virtual machines.  Supports shell scripting,
bindings from many languages.  http://et.redhat.com/~rjones/libguestfs/
See what it can do: http://et.redhat.com/~rjones/libguestfs/recipes.html
-------------- next part --------------
>From 4483a1afdd3b9b0d8930e833f38667251cbf4b01 Mon Sep 17 00:00:00 2001
From: Richard W.M. Jones <rjones at redhat.com>
Date: Fri, 14 Jan 2011 22:20:51 +0000
Subject: [PATCH] Add ability to inspect install disks and live CDs.

---
 generator/generator_actions.ml |   75 +++++++++++++++++++
 inspector/virt-inspector.c     |   31 ++++++++-
 inspector/virt-inspector.pod   |   25 ++++++
 inspector/virt-inspector.rng   |    4 +
 src/guestfs-internal.h         |   12 +++
 src/guestfs.pod                |   28 ++++++-
 src/inspect.c                  |  159 +++++++++++++++++++++++++++++++++++++---
 7 files changed, 319 insertions(+), 15 deletions(-)

diff --git a/generator/generator_actions.ml b/generator/generator_actions.ml
index 50c33a8..7cb8c1e 100644
--- a/generator/generator_actions.ml
+++ b/generator/generator_actions.ml
@@ -1293,6 +1293,81 @@ string C<unknown> is returned.
 
 Please read L<guestfs(3)/INSPECTION> for more details.");
 
+  ("inspect_get_format", (RString "format", [Device "root"], []), -1, [],
+   [],
+   "get format of inspected operating system",
+   "\
+This function should only be called with a root device string
+as returned by C<guestfs_inspect_os>.
+
+This returns the format of the inspected operating system.  You
+can use it to detect install images, live CDs and similar.
+
+Currently defined formats are:
+
+=over 4
+
+=item \"installed\"
+
+This is an installed operating system.
+
+=item \"installer\"
+
+The disk image being inspected is not an installed operating system,
+but a I<bootable> install disk, live CD, or similar.
+
+=item \"unknown\"
+
+The format of this disk image is not known.
+
+=back
+
+Future versions of libguestfs may return other strings here.
+The caller should be prepared to handle any string.
+
+Please read L<guestfs(3)/INSPECTION> for more details.");
+
+  ("inspect_is_live", (RBool "live", [Device "root"], []), -1, [],
+   [],
+   "get live flag for install disk",
+   "\
+This function should only be called with a root device string
+as returned by C<guestfs_inspect_os>.
+
+If C<guestfs_inspect_get_format> returns C<installer> (this
+is an install disk), then this returns true if a live image
+was detected on the disk.
+
+Please read L<guestfs(3)/INSPECTION> for more details.");
+
+  ("inspect_is_netinst", (RBool "netinst", [Device "root"], []), -1, [],
+   [],
+   "get netinst (network installer) flag for install disk",
+   "\
+This function should only be called with a root device string
+as returned by C<guestfs_inspect_os>.
+
+If C<guestfs_inspect_get_format> returns C<installer> (this
+is an install disk), then this returns true if the disk is
+a network installer, ie. not a self-contained install CD but
+one which is likely to require network access to complete
+the install.
+
+Please read L<guestfs(3)/INSPECTION> for more details.");
+
+  ("inspect_is_multipart", (RBool "multipart", [Device "root"], []), -1, [],
+   [],
+   "get multipart flag for install disk",
+   "\
+This function should only be called with a root device string
+as returned by C<guestfs_inspect_os>.
+
+If C<guestfs_inspect_get_format> returns C<installer> (this
+is an install disk), then this returns true if the disk is
+part of a set.
+
+Please read L<guestfs(3)/INSPECTION> for more details.");
+
 ]
 
 (* daemon_functions are any functions which cause some action
diff --git a/inspector/virt-inspector.c b/inspector/virt-inspector.c
index d3e00a9..68f8b46 100644
--- a/inspector/virt-inspector.c
+++ b/inspector/virt-inspector.c
@@ -331,7 +331,7 @@ static void
 output_root (xmlTextWriterPtr xo, char *root)
 {
   char *str;
-  int i;
+  int i, r;
   char buf[32];
   char canonical_root[strlen (root) + 1];
 
@@ -407,6 +407,35 @@ output_root (xmlTextWriterPtr xo, char *root)
     free (str);
   );
 
+  str = guestfs_inspect_get_format (g, root);
+  if (!str) exit (EXIT_FAILURE);
+  if (STRNEQ (str, "unknown"))
+    XMLERROR (-1,
+      xmlTextWriterWriteElement (xo, BAD_CAST "format",
+                                 BAD_CAST str));
+  free (str);
+
+  r = guestfs_inspect_is_live (g, root);
+  if (r > 0) {
+    XMLERROR (-1,
+              xmlTextWriterStartElement (xo, BAD_CAST "live"));
+    XMLERROR (-1, xmlTextWriterEndElement (xo));
+  }
+
+  r = guestfs_inspect_is_netinst (g, root);
+  if (r > 0) {
+    XMLERROR (-1,
+              xmlTextWriterStartElement (xo, BAD_CAST "netinst"));
+    XMLERROR (-1, xmlTextWriterEndElement (xo));
+  }
+
+  r = guestfs_inspect_is_multipart (g, root);
+  if (r > 0) {
+    XMLERROR (-1,
+              xmlTextWriterStartElement (xo, BAD_CAST "multipart"));
+    XMLERROR (-1, xmlTextWriterEndElement (xo));
+  }
+
   output_mountpoints (xo, root);
 
   output_filesystems (xo, root);
diff --git a/inspector/virt-inspector.pod b/inspector/virt-inspector.pod
index eade662..cd0e617 100755
--- a/inspector/virt-inspector.pod
+++ b/inspector/virt-inspector.pod
@@ -34,6 +34,9 @@ several I<-a> options one after another, with the first corresponding
 to the guest's C</dev/sda>, the second to the guest's C</dev/sdb> and
 so on.
 
+You can also run virt-inspector on install disks, live CDs, bootable
+USB keys and similar.
+
 Virt-inspector can only inspect and report upon I<one domain at a
 time>.  To inspect several virtual machines, you have to run
 virt-inspector several times (for example, from a shell script
@@ -165,6 +168,7 @@ describe the operating system, its architecture, the descriptive
      <major_version>6</major_version>
      <minor_version>1</minor_version>
      <windows_systemroot>/Windows</windows_systemroot>
+     <format>installed</format>
 
 These fields are derived from the libguestfs inspection API, and
 you can find more details in L<guestfs(3)/INSPECTION>.
@@ -243,6 +247,27 @@ The version and release fields may not be available for some types
 guests.  Other fields are possible, see
 L<guestfs(3)/guestfs_inspect_list_applications>.
 
+=head2 INSPECTING INSTALL DISKS, LIVE CDs
+
+Virt-inspector can detect some operating system installers on
+install disks, live CDs, bootable USB keys and more.
+
+In this case the E<lt>formatE<gt> tag will contain C<installer>
+and other fields may be present to indicate a live CD, network
+installer, or one part of a multipart CD.  For example:
+
+ <operatingsystems>
+   <operatingsystem>
+     <root>/dev/sda</root>
+     <name>linux</name>
+     <arch>i386</arch>
+     <distro>ubuntu</distro>
+     <product_name>Ubuntu 10.10 "Maverick Meerkat"</product_name>
+     <major_version>10</major_version>
+     <minor_version>10</minor_version>
+     <format>installer</format>
+     <live/>
+
 =head1 USING XPATH
 
 You can use the XPath query language, and/or the xpath tool, in order
diff --git a/inspector/virt-inspector.rng b/inspector/virt-inspector.rng
index 10aa6db..702696e 100644
--- a/inspector/virt-inspector.rng
+++ b/inspector/virt-inspector.rng
@@ -39,6 +39,10 @@
 
             <optional><element name="package_format"><text/></element></optional>
             <optional><element name="package_management"><text/></element></optional>
+            <optional><element name="format"><text/></element></optional>
+            <optional><element name="live"/></optional>
+            <optional><element name="netinst"/></optional>
+            <optional><element name="multipart"/></optional>
 
             <ref name="mountpoints"/>
             <ref name="filesystems"/>
diff --git a/src/guestfs-internal.h b/src/guestfs-internal.h
index bb68298..08b459b 100644
--- a/src/guestfs-internal.h
+++ b/src/guestfs-internal.h
@@ -160,6 +160,14 @@ enum inspect_fs_content {
   FS_CONTENT_LINUX_USR_LOCAL,
   FS_CONTENT_LINUX_VAR,
   FS_CONTENT_FREEBSD_ROOT,
+  FS_CONTENT_INSTALLER,
+};
+
+enum inspect_os_format {
+  OS_FORMAT_UNKNOWN = 0,
+  OS_FORMAT_INSTALLED,
+  OS_FORMAT_INSTALLER,
+  /* in future: supplemental disks */
 };
 
 enum inspect_os_type {
@@ -221,6 +229,10 @@ struct inspect_fs {
   char *arch;
   char *hostname;
   char *windows_systemroot;
+  enum inspect_os_format format;
+  int is_live_disk;
+  int is_netinst_disk;
+  int is_multipart_disk;
   struct inspect_fstab_entry *fstab;
   size_t nr_fstab;
 };
diff --git a/src/guestfs.pod b/src/guestfs.pod
index d9045a5..ab4e768 100644
--- a/src/guestfs.pod
+++ b/src/guestfs.pod
@@ -550,10 +550,11 @@ device (I<not> the underlying encrypted block device).
 =head2 INSPECTION
 
 Libguestfs has APIs for inspecting an unknown disk image to find out
-if it contains operating systems.  (These APIs used to be in a
-separate Perl-only library called L<Sys::Guestfs::Lib(3)> but since
-version 1.5.3 the most frequently used part of this library has been
-rewritten in C and moved into the core code).
+if it contains operating systems, an install CD or a live CD.  (These
+APIs used to be in a separate Perl-only library called
+L<Sys::Guestfs::Lib(3)> but since version 1.5.3 the most frequently
+used part of this library has been rewritten in C and moved into the
+core code).
 
 Add all disks belonging to the unknown virtual machine and call
 L</guestfs_launch> in the usual way.
@@ -608,6 +609,25 @@ again.  (L</guestfs_inspect_list_applications> works a little
 differently from the other calls and does read the disks.  See
 documentation for that function for details).
 
+=head3 INSPECTING INSTALL DISKS
+
+Libguestfs (since 1.9.4) can detect some install disks, install
+CDs, live CDs and more.
+
+Call L</guestfs_inspect_get_format> to return the format of the
+operating system, which currently can be C<installed> (a regular
+operating system) or C<installer> (some sort of install disk).
+
+Further information is available about the operating system that can
+be installed using the regular inspection APIs like
+L</guestfs_inspect_get_product_name>,
+L</guestfs_inspect_get_major_version> etc.
+
+Some additional information specific to installer disks is also
+available from the L</guestfs_inspect_is_live>,
+L</guestfs_inspect_is_netinst> and L</guestfs_inspect_is_multipart>
+calls.
+
 =head2 SPECIAL CONSIDERATIONS FOR WINDOWS GUESTS
 
 Libguestfs can mount NTFS partitions.  It does this using the
diff --git a/src/inspect.c b/src/inspect.c
index 46c7fe4..1feed0b 100644
--- a/src/inspect.c
+++ b/src/inspect.c
@@ -110,7 +110,7 @@ free_regexps (void)
 }
 
 /* The main inspection code. */
-static int check_for_filesystem_on (guestfs_h *g, const char *device);
+static int check_for_filesystem_on (guestfs_h *g, const char *device, int is_block, int is_partnum);
 
 char **
 guestfs__inspect_os (guestfs_h *g)
@@ -133,7 +133,7 @@ guestfs__inspect_os (guestfs_h *g)
 
   size_t i;
   for (i = 0; devices[i] != NULL; ++i) {
-    if (check_for_filesystem_on (g, devices[i]) == -1) {
+    if (check_for_filesystem_on (g, devices[i], 1, 0) == -1) {
       guestfs___free_string_list (devices);
       guestfs___free_inspect_info (g);
       return NULL;
@@ -150,7 +150,7 @@ guestfs__inspect_os (guestfs_h *g)
   }
 
   for (i = 0; partitions[i] != NULL; ++i) {
-    if (check_for_filesystem_on (g, partitions[i]) == -1) {
+    if (check_for_filesystem_on (g, partitions[i], 0, i+1) == -1) {
       guestfs___free_string_list (partitions);
       guestfs___free_inspect_info (g);
       return NULL;
@@ -168,7 +168,7 @@ guestfs__inspect_os (guestfs_h *g)
     }
 
     for (i = 0; lvs[i] != NULL; ++i) {
-      if (check_for_filesystem_on (g, lvs[i]) == -1) {
+      if (check_for_filesystem_on (g, lvs[i], 0, 0) == -1) {
         guestfs___free_string_list (lvs);
         guestfs___free_inspect_info (g);
         return NULL;
@@ -191,9 +191,10 @@ guestfs__inspect_os (guestfs_h *g)
 /* Find out if 'device' contains a filesystem.  If it does, add
  * another entry in g->fses.
  */
-static int check_filesystem (guestfs_h *g, const char *device);
+static int check_filesystem (guestfs_h *g, const char *device, int is_block, int is_partnum);
 static int check_linux_root (guestfs_h *g, struct inspect_fs *fs);
 static int check_freebsd_root (guestfs_h *g, struct inspect_fs *fs);
+static int check_installer_root (guestfs_h *g, struct inspect_fs *fs);
 static void check_architecture (guestfs_h *g, struct inspect_fs *fs);
 static int check_hostname_unix (guestfs_h *g, struct inspect_fs *fs);
 static int check_hostname_redhat (guestfs_h *g, struct inspect_fs *fs);
@@ -216,7 +217,8 @@ static int inspect_with_augeas (guestfs_h *g, struct inspect_fs *fs, const char
 static char *first_line_of_file (guestfs_h *g, const char *filename);
 
 static int
-check_for_filesystem_on (guestfs_h *g, const char *device)
+check_for_filesystem_on (guestfs_h *g, const char *device,
+                         int is_block, int is_partnum)
 {
   /* Get vfs-type in order to check if it's a Linux(?) swap device.
    * If there's an error we should ignore it, so to do that we have to
@@ -230,8 +232,9 @@ check_for_filesystem_on (guestfs_h *g, const char *device)
   int is_swap = vfs_type && STREQ (vfs_type, "swap");
 
   if (g->verbose)
-    fprintf (stderr, "check_for_filesystem_on: %s (%s)\n",
-             device, vfs_type ? vfs_type : "failed to get vfs type");
+    fprintf (stderr, "check_for_filesystem_on: %s %d %d (%s)\n",
+             device, is_block, is_partnum,
+             vfs_type ? vfs_type : "failed to get vfs type");
 
   if (is_swap) {
     free (vfs_type);
@@ -252,7 +255,7 @@ check_for_filesystem_on (guestfs_h *g, const char *device)
     return 0;
 
   /* Do the rest of the checks. */
-  r = check_filesystem (g, device);
+  r = check_filesystem (g, device, is_block, is_partnum);
 
   /* Unmount the filesystem. */
   if (guestfs_umount_all (g) == -1)
@@ -261,8 +264,15 @@ check_for_filesystem_on (guestfs_h *g, const char *device)
   return r;
 }
 
+/* is_block and is_partnum are just hints: is_block is true if the
+ * filesystem is a whole block device (eg. /dev/sda).  is_partnum
+ * is > 0 if the filesystem is a direct partition, and in this case
+ * it is the partition number counting from 1
+ * (eg. /dev/sda1 => is_partnum == 1).
+ */
 static int
-check_filesystem (guestfs_h *g, const char *device)
+check_filesystem (guestfs_h *g, const char *device,
+                  int is_block, int is_partnum)
 {
   if (extend_fses (g) == -1)
     return -1;
@@ -295,6 +305,7 @@ check_filesystem (guestfs_h *g, const char *device)
 
     fs->is_root = 1;
     fs->content = FS_CONTENT_FREEBSD_ROOT;
+    fs->format = OS_FORMAT_INSTALLED;
     if (check_freebsd_root (g, fs) == -1)
       return -1;
   }
@@ -304,6 +315,7 @@ check_filesystem (guestfs_h *g, const char *device)
            guestfs_is_file (g, "/etc/fstab") > 0) {
     fs->is_root = 1;
     fs->content = FS_CONTENT_LINUX_ROOT;
+    fs->format = OS_FORMAT_INSTALLED;
     if (check_linux_root (g, fs) == -1)
       return -1;
   }
@@ -340,9 +352,26 @@ check_filesystem (guestfs_h *g, const char *device)
            guestfs_is_file (g, "/ntldr") > 0) {
     fs->is_root = 1;
     fs->content = FS_CONTENT_WINDOWS_ROOT;
+    fs->format = OS_FORMAT_INSTALLED;
     if (check_windows_root (g, fs) == -1)
       return -1;
   }
+  /* Install CD/disk?  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_dir (g, "/isolinux") > 0 ||
+            guestfs_is_dir (g, "/EFI/BOOT") > 0 ||
+            guestfs_is_file (g, "/images/install.img") > 0 ||
+            guestfs_is_dir (g, "/.disk") > 0 ||
+            guestfs_is_file (g, "/.discinfo") > 0 ||
+            guestfs_is_file (g, "/i386/sis.inf")) > 0) {
+    fs->is_root = 1;
+    fs->content = FS_CONTENT_INSTALLER;
+    fs->format = OS_FORMAT_INSTALLER;
+    if (check_installer_root (g, fs) == -1)
+      return -1;
+  }
 
   return 0;
 }
@@ -638,6 +667,69 @@ check_freebsd_root (guestfs_h *g, struct inspect_fs *fs)
   return 0;
 }
 
+/* The currently mounted device is very likely to be an installer. */
+static int
+check_installer_root (guestfs_h *g, struct inspect_fs *fs)
+{
+  /* Debian/Ubuntu disks are easy ...
+   *
+   * These files are added by the debian-cd program, and it is worth
+   * looking at the source code to determine exact values, in
+   * particular '/usr/share/debian-cd/tools/start_new_disc'
+   *
+   * XXX Architecture?  We could parse it out of the product name
+   * string, but that seems quite hairy.  We could look for the names
+   * of packages.  Also note that some Debian install disks are
+   * multiarch.
+   */
+  if (guestfs_is_file (g, "/.disk/info") > 0) {
+    fs->product_name = first_line_of_file (g, "/.disk/info");
+    if (!fs->product_name)
+      return -1;
+
+    fs->type = OS_TYPE_LINUX;
+    if (STRPREFIX (fs->product_name, "Ubuntu"))
+      fs->distro = OS_DISTRO_UBUNTU;
+    else if (STRPREFIX (fs->product_name, "Debian"))
+      fs->distro = OS_DISTRO_DEBIAN;
+
+    (void) parse_major_minor (g, fs);
+  }
+  if (guestfs_is_file (g, "/.disk/cd_type") > 0) {
+    char *cd_type = first_line_of_file (g, "/.disk/cd_type");
+    if (!cd_type)
+      return -1;
+
+    if (STRPREFIX (cd_type, "dvd/single") ||
+        STRPREFIX (cd_type, "full_cd/single")) {
+      fs->is_multipart_disk = 0;
+      fs->is_netinst_disk = 0;
+    }
+    else if (STRPREFIX (cd_type, "dvd") ||
+             STRPREFIX (cd_type, "full_cd")) {
+      fs->is_multipart_disk = 1;
+      fs->is_netinst_disk = 0;
+    }
+    else if (STRPREFIX (cd_type, "not_complete")) {
+      fs->is_multipart_disk = 0;
+      fs->is_netinst_disk = 1;
+    }
+
+    free (cd_type);
+  }
+
+  /* else XXX Fedora */
+
+  /* else XXX Windows */
+
+  /* The presence of certain files indicates a live CD. */
+  if (guestfs_is_file (g, "/casper/filesystem.squashfs") > 0 ||
+      guestfs_is_file (g, "/images/install.img") > 0)
+    fs->is_live_disk = 1;
+
+  return 0;
+}
+
 static void
 check_architecture (guestfs_h *g, struct inspect_fs *fs)
 {
@@ -1526,6 +1618,53 @@ guestfs__inspect_get_windows_systemroot (guestfs_h *g, const char *root)
   return safe_strdup (g, fs->windows_systemroot);
 }
 
+char *
+guestfs__inspect_get_format (guestfs_h *g, const char *root)
+{
+  struct inspect_fs *fs = search_for_root (g, root);
+  if (!fs)
+    return NULL;
+
+  char *ret;
+  switch (fs->format) {
+  case OS_FORMAT_INSTALLED: ret = safe_strdup (g, "installed"); break;
+  case OS_FORMAT_INSTALLER: ret = safe_strdup (g, "installer"); break;
+  case OS_FORMAT_UNKNOWN: default: ret = safe_strdup (g, "unknown"); break;
+  }
+
+  return ret;
+}
+
+int
+guestfs__inspect_is_live (guestfs_h *g, const char *root)
+{
+  struct inspect_fs *fs = search_for_root (g, root);
+  if (!fs)
+    return -1;
+
+  return fs->is_live_disk;
+}
+
+int
+guestfs__inspect_is_netinst (guestfs_h *g, const char *root)
+{
+  struct inspect_fs *fs = search_for_root (g, root);
+  if (!fs)
+    return -1;
+
+  return fs->is_netinst_disk;
+}
+
+int
+guestfs__inspect_is_multipart (guestfs_h *g, const char *root)
+{
+  struct inspect_fs *fs = search_for_root (g, root);
+  if (!fs)
+    return -1;
+
+  return fs->is_multipart_disk;
+}
+
 char **
 guestfs__inspect_get_mountpoints (guestfs_h *g, const char *root)
 {
-- 
1.7.3.4



More information about the Libguestfs mailing list