[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

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



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 &quot;Maverick Meerkat&quot; - 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 &quot;Lenny&quot; - 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
>From 4483a1afdd3b9b0d8930e833f38667251cbf4b01 Mon Sep 17 00:00:00 2001
From: Richard W.M. Jones <rjones 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 &quot;Maverick Meerkat&quot;</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


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]