>From 4483a1afdd3b9b0d8930e833f38667251cbf4b01 Mon Sep 17 00:00:00 2001 From: Richard W.M. Jones 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 is returned. Please read L 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. + +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 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 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. + +If C returns C (this +is an install disk), then this returns true if a live image +was detected on the disk. + +Please read L 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. + +If C returns C (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 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. + +If C returns C (this +is an install disk), then this returns true if the disk is +part of a set. + +Please read L 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, the second to the guest's C 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. 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 6 1 /Windows + installed These fields are derived from the libguestfs inspection API, and you can find more details in L. @@ -243,6 +247,27 @@ The version and release fields may not be available for some types guests. Other fields are possible, see L. +=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 EformatE tag will contain C +and other fields may be present to indicate a live CD, network +installer, or one part of a multipart CD. For example: + + + + /dev/sda + linux + i386 + ubuntu + Ubuntu 10.10 "Maverick Meerkat" + 10 + 10 + installer + + =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 @@ + + + + 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 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 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 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 in the usual way. @@ -608,6 +609,25 @@ again. (L 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 to return the format of the +operating system, which currently can be C (a regular +operating system) or C (some sort of install disk). + +Further information is available about the operating system that can +be installed using the regular inspection APIs like +L, +L etc. + +Some additional information specific to installer disks is also +available from the L, +L and L +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