[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