2 # Copyright (C) 2009-2010 Red Hat Inc.
4 # This library is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU Lesser General Public
6 # License as published by the Free Software Foundation; either
7 # version 2 of the License, or (at your option) any later version.
9 # This library is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 # Lesser General Public License for more details.
14 # You should have received a copy of the GNU Lesser General Public
15 # License along with this library; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 package Sys::Guestfs::Lib;
23 # The minor part of this version number is incremented when some
24 # change is made to this module. The major part is incremented if we
25 # make a change which is not backwards compatible. It is not related
26 # to the libguestfs version number.
27 use vars qw($VERSION);
33 use File::Temp qw/tempdir/;
34 use Locale::TextDomain 'libguestfs';
37 eval "use Sys::Virt;";
38 eval "use XML::XPath;";
39 eval "use XML::XPath::XMLParser;";
40 eval "use Win::Hivex;";
46 Sys::Guestfs::Lib - Useful functions for using libguestfs from Perl
50 use Sys::Guestfs::Lib qw(open_guest inspect_all_partitions ...);
52 $g = open_guest ($name);
54 %fses = inspect_all_partitions ($g, \@partitions);
56 (and many more calls - see the rest of this manpage)
60 C<Sys::Guestfs::Lib> is an extra library of useful functions for using
61 the libguestfs API from Perl. It also provides tighter integration
64 The basic libguestfs API is not covered by this manpage. Please refer
65 instead to L<Sys::Guestfs(3)> and L<guestfs(3)>. The libvirt API is
66 also not covered. For that, see L<Sys::Virt(3)>.
68 =head1 BASIC FUNCTIONS
74 use vars qw(@EXPORT_OK @ISA);
77 @EXPORT_OK = qw(open_guest feature_available
78 get_partitions resolve_windows_path
79 inspect_all_partitions inspect_partition
80 inspect_operating_systems mount_operating_system inspect_in_detail
81 inspect_linux_kernel);
85 $g = open_guest ($name);
87 $g = open_guest ($name, rw => 1, ...);
89 $g = open_guest ($name, address => $uri, ...);
91 $g = open_guest ([$img1, $img2, ...], address => $uri, ...);
93 ($g, $conn, $dom, @images) = open_guest ($name);
95 This function opens a libguestfs handle for either the libvirt domain
96 called C<$name>, or the disk image called C<$name>. Any disk images
97 found through libvirt or specified explicitly are attached to the
100 The C<Sys::Guestfs> handle C<$g> is returned, or if there was an error
101 it throws an exception. To catch errors, wrap the call in an eval
104 The first parameter is either a string referring to a libvirt domain
105 or a disk image, or (if a guest has several disk images) an arrayref
106 C<[$img1, $img2, ...]>.
108 The handle is I<read-only> by default. Use the optional parameter
109 C<rw =E<gt> 1> to open a read-write handle. However if you open a
110 read-write handle, this function will refuse to use active libvirt
113 The handle is still in the config state when it is returned, so you
114 have to call C<$g-E<gt>launch ()>.
116 The optional C<address> parameter can be added to specify the libvirt
119 The implicit libvirt handle is closed after this function, I<unless>
120 you call the function in C<wantarray> context, in which case the
121 function returns a tuple of: the open libguestfs handle, the open
122 libvirt handle, and the open libvirt domain handle, and a list of
123 images. (This is useful if you want to do other things like pulling
124 the XML description of the guest). Note that if this is a straight
125 disk image, then C<$conn> and C<$dom> will be C<undef>.
127 If the C<Sys::Virt> module is not available, then libvirt is bypassed,
128 and this function can only open disk images.
130 The optional C<interface> parameter can be used to open devices with
131 C<add_drive{,_ro}_with_if>. See
132 L<Sys::Guestfs/guestfs_add_drive_with_if> for more details.
142 my $rw = $params{rw};
143 my $address = $params{address};
144 my $interface = $params{interface};
147 if (ref ($first) eq "ARRAY") {
149 } elsif (ref ($first) eq "SCALAR") {
152 croak __"open_guest: first parameter must be a string or an arrayref"
159 croak __x("guest image {imagename} does not exist or is not readable",
164 die __"open_guest: no libvirt support (install Sys::Virt, XML::XPath and XML::XPath::XMLParser)"
165 unless exists $INC{"Sys/Virt.pm"} &&
166 exists $INC{"XML/XPath.pm"} &&
167 exists $INC{"XML/XPath/XMLParser.pm"};
169 die __"open_guest: too many domains listed on command line"
172 my @libvirt_args = ();
173 push @libvirt_args, address => $address if defined $address;
175 $conn = Sys::Virt->new (readonly => 1, @libvirt_args);
176 die __"open_guest: cannot connect to libvirt" unless $conn;
178 my @doms = $conn->list_defined_domains ();
179 my $isitinactive = 1;
181 # In the case where we want read-only access to a domain,
182 # allow the user to specify an active domain too.
183 push @doms, $conn->list_domains ();
187 if ($_->get_name () eq $images[0]) {
195 die __x("{imagename} is not the name of an inactive libvirt domain\n",
196 imagename => $images[0]);
198 die __x("{imagename} is not the name of a libvirt domain\n",
199 imagename => $images[0]);
203 # Get the names of the image(s).
204 my $xml = $dom->get_xml_description ();
206 my $p = XML::XPath->new (xml => $xml);
207 my @disks = $p->findnodes ('//devices/disk/source/@dev');
208 push (@disks, $p->findnodes ('//devices/disk/source/@file'));
210 die __x("{imagename} seems to have no disk devices\n",
211 imagename => $images[0])
214 @images = map { $_->getData } @disks;
217 # We've now got the list of @images, so feed them to libguestfs.
218 my $g = Sys::Guestfs->new ();
222 $g->add_drive_with_if ($_, $interface);
228 $g->add_drive_ro_with_if ($_, $interface);
230 $g->add_drive_ro ($_);
235 return wantarray ? ($g, $conn, $dom, @images) : $g
238 =head2 feature_available
240 $bool = feature_available ($g, $feature [, $feature ...]);
242 This function is a useful wrapper around the basic
243 C<$g-E<gt>available> call.
245 C<$g-E<gt>available> tests for availability of a list of features and
246 dies with an error if any is not available.
248 This call tests for the list of features and returns true if all are
249 available, or false otherwise.
251 For a list of features you can test for, see L<guestfs(3)/AVAILABILITY>.
255 sub feature_available {
258 eval { $g->available (\@_); };
262 =head2 get_partitions
264 @partitions = get_partitions ($g);
266 This function takes an open libguestfs handle C<$g> and returns all
267 partitions and logical volumes found on it.
269 What is returned is everything that could contain a filesystem (or
270 swap). Physical volumes are not normally included from the list
271 except if they contain a filesystem directly. Nor are devices which
272 are partitioned (eg. C</dev/sda> would not be returned if C</dev/sda1>
282 # Look to see if any devices directly contain filesystems (RHBZ#590167).
283 my @devices = $g->list_devices ();
284 my @fses_on_device = ();
286 eval { $g->mount_ro ($_, "/"); };
287 push @fses_on_device, $_ unless $@;
291 my @partitions = $g->list_partitions ();
292 my @pvs = $g->pvs ();
293 @partitions = grep { ! _is_pv ($_, @pvs) } @partitions;
296 @lvs = $g->lvs () if feature_available ($g, "lvm2");
298 return sort (@fses_on_device, @lvs, @partitions);
306 return 1 if $_ eq $t;
311 =head2 resolve_windows_path
313 $path = resolve_windows_path ($g, $path);
315 $path = resolve_windows_path ($g, "/windows/system");
316 ==> "/WINDOWS/System"
317 or undef if no path exists
319 This function, which is specific to FAT/NTFS filesystems (ie. Windows
320 guests), lets you look up a case insensitive C<$path> in the
321 filesystem and returns the true, case sensitive path as required by
322 the underlying kernel or NTFS-3g driver.
324 If C<$path> does not exist then this function returns C<undef>.
326 The C<$path> parameter must begin with C</> character and be separated
327 by C</> characters. Do not use C<\>, drive names, etc.
331 sub resolve_windows_path
337 eval { $r = $g->case_sensitive_path ($path); };
341 =head2 file_architecture
343 $arch = file_architecture ($g, $path)
345 The C<file_architecture> function lets you get the architecture for a
346 particular binary or library in the guest. By "architecture" we mean
347 what processor it is compiled for (eg. C<i586> or C<x86_64>).
349 The function works on at least the following types of files:
355 many types of Un*x binary
359 many types of Un*x shared library
363 Windows Win32 and Win64 binaries
367 Windows Win32 and Win64 DLLs
369 Win32 binaries and DLLs return C<i386>.
371 Win64 binaries and DLLs return C<x86_64>.
379 Linux new-style initrd images
383 some non-x86 Linux vmlinuz kernels
387 What it can't do currently:
393 static libraries (libfoo.a)
397 Linux old-style initrd as compressed ext2 filesystem (RHEL 3)
401 x86 Linux vmlinuz kernels
403 x86 vmlinuz images (bzImage format) consist of a mix of 16-, 32- and
404 compressed code, and are horribly hard to unpack. If you want to find
405 the architecture of a kernel, use the architecture of the associated
406 initrd or kernel module(s) instead.
412 sub _elf_arch_to_canonical
416 if ($_ eq "Intel 80386") {
418 } elsif ($_ eq "Intel 80486") {
419 return "i486"; # probably not in the wild
420 } elsif ($_ eq "x86-64") {
422 } elsif ($_ eq "AMD x86-64") {
424 } elsif (/SPARC32/) {
426 } elsif (/SPARC V9/) {
428 } elsif ($_ eq "IA-64") {
430 } elsif (/64.*PowerPC/) {
432 } elsif (/PowerPC/) {
435 warn __x("returning non-canonical architecture type '{arch}'",
441 my @_initrd_binaries = ("nash", "modprobe", "sh", "bash");
443 sub file_architecture
449 # Our basic tool is 'file' ...
450 my $file = $g->file ($path);
452 if ($file =~ /ELF.*(?:executable|shared object|relocatable), (.+?),/) {
453 # ELF executable or shared object. We need to convert
454 # what file(1) prints into the canonical form.
455 return _elf_arch_to_canonical ($1);
456 } elsif ($file =~ /PE32 executable/) {
457 return "i386"; # Win32 executable or DLL
458 } elsif ($file =~ /PE32\+ executable/) {
459 return "x86_64"; # Win64 executable or DLL
462 elsif ($file =~ /cpio archive/) {
463 # Probably an initrd.
465 if ($file =~ /gzip/) {
467 } elsif ($file =~ /bzip2/) {
471 # Download and unpack it to find a binary file.
472 my $dir = tempdir (CLEANUP => 1);
473 $g->download ($path, "$dir/initrd");
475 my $bins = join " ", map { "bin/$_" } @_initrd_binaries;
476 my $cmd = "cd $dir && $zcat initrd | cpio --quiet -id $bins";
477 my $r = system ($cmd);
478 die __x("cpio command failed: {error}", error => $?)
481 foreach my $bin (@_initrd_binaries) {
482 if (-f "$dir/bin/$bin") {
483 $_ = `file $dir/bin/$bin`;
484 if (/ELF.*executable, (.+?),/) {
485 return _elf_arch_to_canonical ($1);
490 die __x("file_architecture: no known binaries found in initrd image: {path}",
494 die __x("file_architecture: unknown architecture: {path}",
498 =head1 OPERATING SYSTEM INSPECTION FUNCTIONS
500 The functions in this section can be used to inspect the operating
501 system(s) available inside a virtual machine image. For example, you
502 can find out if the VM is Linux or Windows, how the partitions are
503 meant to be mounted, and what applications are installed.
505 If you just want a simple command-line interface to this
506 functionality, use the L<virt-inspector(1)> tool. The documentation
507 below covers the case where you want to access this functionality from
510 Once you have the list of partitions (from C<get_partitions>) there
511 are several steps involved:
517 Look at each partition separately and find out what is on it.
519 The information you get back includes whether the partition contains a
520 filesystem or swapspace, what sort of filesystem (eg. ext3, ntfs), and
521 a first pass guess at the content of the filesystem (eg. Linux boot,
524 The result of this step is a C<%fs> hash of information, one hash for
527 See: C<inspect_partition>, C<inspect_all_partitions>
531 Work out the relationship between partitions.
533 In this step we work out how partitions are related to each other. In
534 the case of a single-boot VM, we work out how the partitions are
535 mounted in respect of each other (eg. C</dev/sda1> is mounted as
536 C</boot>). In the case of a multi-boot VM where there are several
537 roots, we may identify several operating system roots, and mountpoints
540 The result of this step is a single hash called C<%oses> which is
541 described in more detail below, but at the top level looks like:
544 '/dev/VG/Root1' => \%os1,
545 '/dev/VG/Root2' => \%os2,
551 '/' => '/dev/VG/Root1',
552 '/boot' => '/dev/sda1',
557 (example shows a multi-boot VM containing two root partitions).
559 See: C<inspect_operating_systems>
565 Previous to this point we've essentially been looking at each
566 partition in isolation. Now we construct a true guest filesystem by
567 mounting up all of the disks. Only once everything is mounted up can
568 we run commands in the OS context to do more detailed inspection.
570 See: C<mount_operating_system>
574 Check for kernels and applications.
576 This step now does more detailed inspection, where we can look for
577 kernels, applications and more installed in the guest.
579 The result of this is an enhanced C<%os> hash.
581 See: C<inspect_in_detail>
587 This library does not contain functions for generating output based on
588 the analysis steps above. Use a command line tool such as
589 L<virt-inspector(1)> to get useful output.
593 =head2 inspect_all_partitions
595 %fses = inspect_all_partitions ($g, \@partitions);
597 This calls C<inspect_partition> for each partition in the list
600 The result is a hash which maps partition name to C<\%fs> hashref.
602 The contents of the C<%fs> hash is explained below.
606 # Turn /dev/vd* and /dev/hd* into canonical device names
607 # (see BLOCK DEVICE NAMING in guestfs(3)).
609 sub _canonical_dev ($)
612 return "/dev/sd$1" if $dev =~ m{^/dev/[vh]d(\w+)};
616 sub inspect_all_partitions
622 return map { _canonical_dev ($_) => inspect_partition ($g, $_) } @parts;
625 =head2 inspect_partition
627 \%fs = inspect_partition ($g, $partition);
629 This function inspects the device named C<$partition> in isolation and
630 tries to determine what it is. It returns information such as whether
631 the partition is formatted, and with what, whether it is mountable,
632 and what it appears to contain (eg. a Windows root, or a Linux /usr).
634 If the Perl module L<Win::Hivex(3)> is installed, then additional
635 information is made available for Windows guests, if we can locate and
636 read their registries.
638 The returned value is a hashref C<\%fs> which may contain the
639 following top-level keys (any key can be missing):
645 Filesystem type, eg. "ext2" or "ntfs"
649 Apparent filesystem OS, eg. "linux" or "windows"
653 If set, the partition is a swap partition.
665 If set, the partition could be mounted by libguestfs.
669 Filesystem content, if we could determine it. One of: "linux-grub",
670 "linux-root", "linux-usrlocal", "linux-usr", "windows-root".
674 (For Linux root partitions only).
675 Operating system distribution. One of: "fedora", "rhel", "centos",
676 "scientific", "debian".
680 (For Linux root partitions only)
681 The package format used by the guest distribution. One of: "rpm", "deb".
683 =item package_management
685 (For Linux root partitions only)
686 The package management tool used by the guest distribution. One of: "rhn",
689 =item os_major_version
691 (For root partitions only).
692 Operating system major version number.
694 =item os_minor_version
696 (For root partitions only).
697 Operating system minor version number.
701 (For Linux root partitions only).
702 The contents of the C</etc/fstab> file.
706 (For Windows root partitions only).
707 The contents of the C</boot.ini> (NTLDR) file.
711 The value is an arrayref, which is a list of Windows registry
712 file contents, in Windows C<.REG> format.
718 sub inspect_partition
722 my $dev = shift; # LV or partition name.
724 my %r; # Result hash.
726 # First try 'file(1)' on it.
727 my $file = $g->file ($dev);
728 if ($file =~ /ext2 filesystem data/) {
731 } elsif ($file =~ /ext3 filesystem data/) {
734 } elsif ($file =~ /ext4 filesystem data/) {
737 } elsif ($file =~ m{Linux/i386 swap file}) {
743 # If it's ext2/3/4, then we want the UUID and label.
744 if (exists $r{fstype} && $r{fstype} =~ /^ext/) {
745 $r{uuid} = $g->get_e2uuid ($dev);
746 $r{label} = $g->get_e2label ($dev);
749 # Try mounting it, fnarrr.
751 $r{is_mountable} = 1;
752 eval { $g->mount_ro ($dev, "/") };
754 # It's not mountable, probably empty or some format
755 # we don't understand.
756 $r{is_mountable} = 0;
761 if ($g->is_file ("/grub/menu.lst") ||
762 $g->is_file ("/grub/grub.conf")) {
763 $r{content} = "linux-grub";
764 _check_grub ($g, \%r);
769 if ($g->is_dir ("/etc") && $g->is_dir ("/bin") &&
770 $g->is_file ("/etc/fstab")) {
771 $r{content} = "linux-root";
773 _check_linux_root ($g, \%r);
778 if ($g->is_dir ("/etc") && $g->is_dir ("/bin") &&
779 $g->is_dir ("/share") && !$g->exists ("/local") &&
780 !$g->is_file ("/etc/fstab")) {
781 $r{content} = "linux-usrlocal";
786 if ($g->is_dir ("/etc") && $g->is_dir ("/bin") &&
787 $g->is_dir ("/share") && $g->exists ("/local") &&
788 !$g->is_file ("/etc/fstab")) {
789 $r{content} = "linux-usr";
794 if ($g->is_file ("/AUTOEXEC.BAT") ||
795 $g->is_file ("/autoexec.bat") ||
796 $g->is_dir ("/Program Files") ||
797 $g->is_dir ("/WINDOWS") ||
798 $g->is_file ("/boot.ini") ||
799 $g->is_file ("/ntldr")) {
800 $r{fstype} = "ntfs"; # XXX this is a guess
801 $r{fsos} = "windows";
802 $r{content} = "windows-root";
804 _check_windows_root ($g, \%r);
814 sub _check_linux_root
820 # Look into /etc to see if we recognise the operating system.
821 # N.B. don't use $g->is_file here, because it might be a symlink
822 if ($g->exists ("/etc/redhat-release")) {
823 $r->{package_format} = "rpm";
825 $_ = $g->cat ("/etc/redhat-release");
826 if (/Fedora release (\d+)(?:\.(\d+))?/) {
827 chomp; $r->{product_name} = $_;
828 $r->{osdistro} = "fedora";
829 $r->{os_major_version} = "$1";
830 $r->{os_minor_version} = "$2" if(defined($2));
831 $r->{package_management} = "yum";
834 elsif (/(Red Hat Enterprise Linux|CentOS|Scientific Linux)/) {
835 chomp; $r->{product_name} = $_;
839 if($distro eq "Red Hat Enterprise Linux") {
840 $r->{osdistro} = "rhel";
843 elsif($distro eq "CentOS") {
844 $r->{osdistro} = "centos";
845 $r->{package_management} = "yum";
848 elsif($distro eq "Scientific Linux") {
849 $r->{osdistro} = "scientific";
850 $r->{package_management} = "yum";
853 # Shouldn't be possible
856 if (/$distro.*release (\d+).*Update (\d+)/) {
857 $r->{os_major_version} = "$1";
858 $r->{os_minor_version} = "$2";
861 elsif (/$distro.*release (\d+)(?:\.(\d+))?/) {
862 $r->{os_major_version} = "$1";
865 $r->{os_minor_version} = "$2";
867 $r->{os_minor_version} = "0";
871 # Package management in RHEL changed in version 5
872 if ($r->{osdistro} eq "rhel") {
873 if ($r->{os_major_version} >= 5) {
874 $r->{package_management} = "yum";
876 $r->{package_management} = "rhn";
882 $r->{osdistro} = "redhat-based";
884 } elsif ($g->is_file ("/etc/debian_version")) {
885 $r->{package_format} = "deb";
886 $r->{package_management} = "apt";
888 $_ = $g->cat ("/etc/debian_version");
889 if (/(\d+)\.(\d+)/) {
890 chomp; $r->{product_name} = $_;
891 $r->{osdistro} = "debian";
892 $r->{os_major_version} = "$1";
893 $r->{os_minor_version} = "$2";
895 $r->{osdistro} = "debian";
899 # Parse the contents of /etc/fstab. This is pretty vital so
900 # we can determine where filesystems are supposed to be mounted.
901 eval "\$_ = \$g->cat ('/etc/fstab');";
903 my @lines = split /\n/;
906 my @fields = split /[ \t]+/;
908 my $spec = $fields[0]; # first column (dev/label/uuid)
909 my $file = $fields[1]; # second column (mountpoint)
910 if ($spec =~ m{^/} ||
911 $spec =~ m{^LABEL=} ||
912 $spec =~ m{^UUID=} ||
914 push @fstab, [$spec, $file]
918 $r->{fstab} = \@fstab if @fstab;
921 # Determine the architecture of this root.
923 foreach ("/bin/bash", "/bin/ls", "/bin/echo", "/bin/rm", "/bin/sh") {
924 if ($g->is_file ($_)) {
925 $arch = file_architecture ($g, $_);
930 $r->{arch} = $arch if defined $arch;
933 # We only support NT. The control file /boot.ini contains a list of
934 # Windows installations and their %systemroot%s in a simple text
937 # XXX We don't handle the case where /boot.ini is on a different
938 # partition very well (Windows Vista and later).
940 sub _check_windows_root
946 my $boot_ini = resolve_windows_path ($g, "/boot.ini");
947 $r->{boot_ini} = $boot_ini;
950 if (defined $r->{boot_ini}) {
951 $_ = $g->cat ($boot_ini);
952 my @lines = split /\n/;
957 } elsif (m/^default=.*?\\(\w+)$/i) {
960 } elsif (m/\\(\w+)=/) {
967 if (!defined $systemroot) {
968 # Last ditch ... try to guess %systemroot% location.
969 foreach ("windows", "winnt") {
970 my $dir = resolve_windows_path ($g, "/$_/system32");
978 if (defined $systemroot) {
979 $r->{systemroot} = resolve_windows_path ($g, "/$systemroot");
980 if (defined $r->{systemroot}) {
981 _check_windows_arch ($g, $r, $r->{systemroot});
982 _check_windows_registry ($g, $r, $r->{systemroot});
987 # Find Windows userspace arch.
989 sub _check_windows_arch
994 my $systemroot = shift;
997 resolve_windows_path ($g, $r->{systemroot} . "/system32/cmd.exe");
998 $r->{arch} = file_architecture ($g, $cmd_exe) if $cmd_exe;
1001 sub _check_windows_registry
1006 my $systemroot = shift;
1008 # Download the system registry files. Only download the
1009 # interesting ones (SOFTWARE and SYSTEM). We don't bother with
1012 return unless exists $INC{"Win/Hivex.pm"};
1014 my $configdir = resolve_windows_path ($g, "$systemroot/system32/config");
1015 return unless defined $configdir;
1017 my $tmpdir = tempdir (CLEANUP => 1);
1019 my $software = resolve_windows_path ($g, "$configdir/software");
1021 if (defined $software) {
1023 $g->download ($software, "$tmpdir/software");
1024 $software_hive = Win::Hivex->open ("$tmpdir/software");
1027 $r->{windows_software_hive} = $software;
1030 my $system = resolve_windows_path ($g, "$configdir/system");
1032 if (defined $system) {
1034 $g->download ($system, "$tmpdir/system");
1035 $system_hive = Win::Hivex->open ("$tmpdir/system");
1038 $r->{windows_system_hive} = $system;
1041 # Get the ProductName, major and minor version, etc.
1042 if (defined $software_hive) {
1045 $cv_node = $software_hive->root;
1046 $cv_node = $software_hive->node_get_child ($cv_node, $_)
1047 foreach ("Microsoft", "Windows NT", "CurrentVersion");
1052 my @values = $software_hive->node_values ($cv_node);
1055 my $k = $software_hive->value_key ($_);
1056 if ($k eq "ProductName") {
1057 $_ = $software_hive->value_string ($_);
1058 $r->{product_name} = $_ if defined $_;
1059 } elsif ($k eq "CurrentVersion") {
1060 $_ = $software_hive->value_string ($_);
1061 if (defined $_ && m/^(\d+)\.(\d+)/) {
1062 $r->{os_major_version} = $1;
1063 $r->{os_minor_version} = $2;
1065 } elsif ($k eq "CurrentBuild") {
1066 $_ = $software_hive->value_string ($_);
1067 $r->{windows_current_build} = $_ if defined $_;
1068 } elsif ($k eq "SoftwareType") {
1069 $_ = $software_hive->value_string ($_);
1070 $r->{windows_software_type} = $_ if defined $_;
1071 } elsif ($k eq "CurrentType") {
1072 $_ = $software_hive->value_string ($_);
1073 $r->{windows_current_type} = $_ if defined $_;
1074 } elsif ($k eq "RegisteredOwner") {
1075 $_ = $software_hive->value_string ($_);
1076 $r->{windows_registered_owner} = $_ if defined $_;
1077 } elsif ($k eq "RegisteredOrganization") {
1078 $_ = $software_hive->value_string ($_);
1079 $r->{windows_registered_organization} = $_ if defined $_;
1080 } elsif ($k eq "InstallationType") {
1081 $_ = $software_hive->value_string ($_);
1082 $r->{windows_installation_type} = $_ if defined $_;
1083 } elsif ($k eq "EditionID") {
1084 $_ = $software_hive->value_string ($_);
1085 $r->{windows_edition_id} = $_ if defined $_;
1086 } elsif ($k eq "ProductID") {
1087 $_ = $software_hive->value_string ($_);
1088 $r->{windows_product_id} = $_ if defined $_;
1101 # Grub version, if we care.
1104 =head2 inspect_operating_systems
1106 \%oses = inspect_operating_systems ($g, \%fses);
1108 This function works out how partitions are related to each other. In
1109 the case of a single-boot VM, we work out how the partitions are
1110 mounted in respect of each other (eg. C</dev/sda1> is mounted as
1111 C</boot>). In the case of a multi-boot VM where there are several
1112 roots, we may identify several operating system roots, and mountpoints
1115 This function returns a hashref C<\%oses> which at the top level looks
1119 '/dev/VG/Root' => \%os,
1122 There can be multiple roots for a multi-boot VM, but this function
1123 will throw an error if no roots (ie. OSes) could be found.
1125 The C<\%os> hash contains the following keys (any can be omitted):
1131 Operating system type, eg. "linux", "windows".
1135 Operating system userspace architecture, eg. "i386", "x86_64".
1139 Operating system distribution, eg. "debian".
1143 Free text product name.
1147 Operating system major version, eg. "4".
1151 Operating system minor version, eg "3".
1155 The value is a reference to the root partition C<%fs> hash.
1159 The value is the name of the root partition (as a string).
1164 The value is a hashref like this:
1167 '/' => '/dev/VG/Root',
1168 '/boot' => '/dev/sda1',
1173 Filesystems (including swap devices and unmounted partitions).
1174 The value is a hashref like this:
1177 '/dev/sda1' => \%fs,
1178 '/dev/VG/Root' => \%fs,
1179 '/dev/VG/Swap' => \%fs,
1186 sub inspect_operating_systems
1194 foreach (sort keys %$fses) {
1195 if ($fses->{$_}->{is_root}) {
1197 root => $fses->{$_},
1200 _get_os_version ($g, \%r);
1201 _assign_mount_points ($g, $fses, \%r);
1206 # If we didn't find any operating systems then it's an error (RHBZ#591142).
1207 if (0 == keys %oses) {
1208 die __"No operating system could be detected inside this disk image.\n\nThis may be because the file is not a disk image, or is not a virtual machine\nimage, or because the OS type is not understood by virt-inspector.\n\nIf you feel this is an error, please file a bug report including as much\ninformation about the disk image as possible.\n";
1220 $r->{os} = $r->{root}->{fsos} if exists $r->{root}->{fsos};
1221 $r->{product_name} = $r->{root}->{product_name}
1222 if exists $r->{root}->{product_name};
1223 $r->{distro} = $r->{root}->{osdistro} if exists $r->{root}->{osdistro};
1224 $r->{major_version} = $r->{root}->{os_major_version}
1225 if exists $r->{root}->{os_major_version};
1226 $r->{minor_version} = $r->{root}->{os_minor_version}
1227 if exists $r->{root}->{os_minor_version};
1228 $r->{package_format} = $r->{root}->{package_format}
1229 if exists $r->{root}->{package_format};
1230 $r->{package_management} = $r->{root}->{package_management}
1231 if exists $r->{root}->{package_management};
1232 $r->{arch} = $r->{root}->{arch} if exists $r->{root}->{arch};
1235 sub _assign_mount_points
1242 $r->{mounts} = { "/" => $r->{root_device} };
1243 $r->{filesystems} = { $r->{root_device} => $r->{root} };
1245 # Use /etc/fstab if we have it to mount the rest.
1246 if (exists $r->{root}->{fstab}) {
1247 my @fstab = @{$r->{root}->{fstab}};
1249 my ($spec, $file) = @$_;
1251 my ($dev, $fs) = _find_filesystem ($g, $fses, $spec);
1253 $r->{mounts}->{$file} = $dev;
1254 $r->{filesystems}->{$dev} = $fs;
1255 if (exists $fs->{used}) {
1260 $fs->{spec} = $spec;
1266 # Find filesystem by device name, LABEL=.. or UUID=..
1267 sub _find_filesystem
1273 if (/^LABEL=(.*)/) {
1275 foreach (sort keys %$fses) {
1276 if (exists $fses->{$_}->{label} &&
1277 $fses->{$_}->{label} eq $label) {
1278 return ($_, $fses->{$_});
1281 warn __x("unknown filesystem label {label}\n", label => $label);
1283 } elsif (/^UUID=(.*)/) {
1285 foreach (sort keys %$fses) {
1286 if (exists $fses->{$_}->{uuid} &&
1287 $fses->{$_}->{uuid} eq $uuid) {
1288 return ($_, $fses->{$_});
1291 warn __x("unknown filesystem UUID {uuid}\n", uuid => $uuid);
1294 return ($_, $fses->{$_}) if exists $fses->{$_};
1296 # The following is to handle the case where an fstab entry specifies a
1297 # specific device rather than its label or uuid, and the libguestfs
1298 # appliance has named the device differently due to the use of a
1300 # This will work as long as the underlying drivers recognise devices in
1302 if (m{^/dev/hd(.*)} && exists $fses->{"/dev/sd$1"}) {
1303 return ("/dev/sd$1", $fses->{"/dev/sd$1"});
1305 if (m{^/dev/xvd(.*)} && exists $fses->{"/dev/sd$1"}) {
1306 return ("/dev/sd$1", $fses->{"/dev/sd$1"});
1308 if (m{^/dev/mapper/(.*)-(.*)$} && exists $fses->{"/dev/$1/$2"}) {
1309 return ("/dev/$1/$2", $fses->{"/dev/$1/$2"});
1312 return () if m{/dev/cdrom};
1314 warn __x("unknown filesystem {fs}\n", fs => $_);
1319 =head2 mount_operating_system
1321 mount_operating_system ($g, \%os, [$ro]);
1323 This function mounts the operating system described in the
1324 C<%os> hash according to the C<mounts> table in that hash (see
1325 C<inspect_operating_systems>).
1327 The partitions are mounted read-only unless the third parameter
1328 is specified as zero explicitly.
1330 To reverse the effect of this call, use the standard
1331 libguestfs API call C<$g-E<gt>umount_all ()>.
1335 sub mount_operating_system
1340 my $ro = shift; # Read-only?
1342 $ro = 1 unless defined $ro; # ro defaults to 1 if unspecified
1344 my $mounts = $os->{mounts};
1346 # Have to mount / first. Luckily '/' is early in the ASCII
1347 # character set, so this should be OK.
1348 foreach (sort keys %$mounts) {
1349 if($_ ne "swap" && $_ ne "none" && ($_ eq '/' || $g->is_dir ($_))) {
1351 $g->mount_ro ($mounts->{$_}, $_)
1353 $g->mount_options ("", $mounts->{$_}, $_)
1359 =head2 inspect_in_detail
1361 mount_operating_system ($g, \%os);
1362 inspect_in_detail ($g, \%os);
1365 The C<inspect_in_detail> function inspects the mounted operating
1366 system for installed applications, installed kernels, kernel modules,
1367 system architecture, and more.
1369 It adds extra keys to the existing C<%os> hash reflecting what it
1370 finds. These extra keys are:
1376 List of applications.
1380 Boot configurations. A hash containing:
1386 An array of boot configurations. Each array entry is a hash containing:
1392 A reference to the expanded initrd structure (see below) for the initrd used by
1393 this boot configuration.
1397 A reference to the expanded kernel structure (see below) for the kernel used by
1398 this boot configuration.
1402 The human readable name of the configuration.
1406 The kernel command line.
1412 The index of the default configuration in the configs array.
1416 The path of the filesystem containing the grub partition.
1424 This is a hash of kernel version =E<gt> a hash with the following keys:
1434 Kernel architecture (eg. C<x86-64>).
1442 The path to the kernel's vmlinuz file.
1446 If the kernel was installed in a package, the name of that package.
1450 =item modprobe_aliases
1453 The contents of the modprobe configuration.
1455 =item initrd_modules
1458 The kernel modules installed in the initrd. The value is
1459 a hashref of kernel version to list of modules.
1465 sub inspect_in_detail
1471 _check_for_applications ($g, $os);
1472 _check_for_kernels ($g, $os);
1473 if ($os->{os} eq "linux") {
1474 _find_modprobe_aliases ($g, $os);
1478 sub _check_for_applications
1486 my $osn = $os->{os};
1487 if ($osn eq "linux") {
1488 my $package_format = $os->{package_format};
1489 if (defined $package_format && $package_format eq "rpm") {
1492 @lines = $g->command_lines
1495 "%{name} %{epoch} %{version} %{release} %{arch}\n"]);
1498 warn(__x("Error running rpm -qa: {error}", error => $@)) if ($@);
1500 @lines = sort @lines;
1502 if (m/^(.*) (.*) (.*) (.*) (.*)$/) {
1504 undef $epoch if $epoch eq "(none)";
1515 } elsif (defined $package_format && $package_format eq "deb") {
1518 @lines = $g->command_lines
1520 "-f", '${Package} ${Version} ${Architecture} ${Status}\n',
1524 warn(__x("Error running dpkg-query: {error}", error => $@)) if ($@);
1526 @lines = sort @lines;
1528 if (m/^(.*) (.*) (.*) (.*) (.*) (.*)$/) {
1529 if ( $6 eq "installed" ) {
1540 } elsif ($osn eq "windows") {
1542 # I worked out a general plan for this, but haven't
1543 # implemented it yet. We can iterate over /Program Files
1544 # looking for *.EXE files, which we download, then use
1545 # i686-pc-mingw32-windres on, to find the VERSIONINFO
1546 # section, which has a lot of useful information.
1549 $os->{apps} = \@apps;
1552 # Find the path which needs to be prepended to paths in grub.conf to make them
1554 sub _find_grub_prefix
1558 my $fses = $os->{filesystems};
1559 die("filesystems undefined") unless(defined($fses));
1561 # Look for the filesystem which contains grub
1563 foreach my $dev (keys(%$fses)) {
1564 my $fsinfo = $fses->{$dev};
1565 if(exists($fsinfo->{content}) && $fsinfo->{content} eq "linux-grub") {
1571 my $mounts = $os->{mounts};
1572 die("mounts undefined") unless(defined($mounts));
1574 # Find where the filesystem is mounted
1575 if(defined($grubdev)) {
1576 foreach my $mount (keys(%$mounts)) {
1577 if($mounts->{$mount} eq $grubdev) {
1578 return "" if($mount eq '/');
1583 die("$grubdev defined in filesystems, but not in mounts");
1586 # If we didn't find it, look for /boot/grub/menu.lst, then try to work out
1587 # what filesystem it's on. We use menu.lst rather than grub.conf because
1588 # debian only uses menu.lst, and anaconda creates a symlink for it.
1589 die(__"Can't find grub on guest") unless($g->exists('/boot/grub/menu.lst'));
1591 # Look for the most specific mount point in mounts
1592 foreach my $path qw(/boot/grub /boot /) {
1593 if(exists($mounts->{$path})) {
1594 return "" if($path eq '/');
1599 die("Couldn't determine which filesystem holds /boot/grub/menu.lst");
1602 sub _check_for_kernels
1606 if ($os->{os} eq "linux" && feature_available ($g, "augeas")) {
1607 # Iterate over entries in grub.conf, populating $os->{boot}
1608 # For every kernel we find, inspect it and add to $os->{kernels}
1610 my $grub = _find_grub_prefix($g, $os);
1611 my $grub_conf = "/etc/grub.conf";
1613 # Debian and other's have no /etc/grub.conf:
1614 if ( ! -f "$grub_conf" ) {
1615 $grub_conf = "$grub/grub/menu.lst";
1624 # ->{title} = "Fedora (2.6.29.6-213.fc11.i686.PAE)"
1625 # ->{kernel} = \kernel
1626 # ->{cmdline} = "ro root=/dev/mapper/vg_mbooth-lv_root rhgb"
1627 # ->{initrd} = \initrd
1628 # ->{default} = \config
1629 # ->{grub_fs} = "/boot"
1631 $g->aug_init("/", 16);
1634 # Get all configurations from grub
1635 foreach my $bootable
1636 ($g->aug_match("/files/$grub_conf/title"))
1639 $config{title} = $g->aug_get($bootable);
1642 eval { $grub_kernel = $g->aug_get("$bootable/kernel"); };
1644 warn __x("Grub entry {title} has no kernel",
1645 title => $config{title});
1648 # Check we've got a kernel entry
1649 if(defined($grub_kernel)) {
1650 my $path = "$grub$grub_kernel";
1652 # Reconstruct the kernel command line
1654 foreach my $arg ($g->aug_match("$bootable/kernel/*")) {
1655 $arg =~ m{/kernel/([^/]*)$}
1656 or die("Unexpected return from aug_match: $arg");
1660 eval { $value = $g->aug_get($arg); };
1662 if(defined($value)) {
1663 push(@args, "$name=$value");
1668 $config{cmdline} = join(' ', @args) if(scalar(@args) > 0);
1671 if ($g->exists($path)) {
1673 inspect_linux_kernel($g, $path, $os->{package_format});
1675 warn __x("grub refers to {path}, which doesn't exist\n",
1679 # Check the kernel was recognised
1680 if(defined($kernel)) {
1681 # Put this kernel on the top level kernel list
1682 $os->{kernels} ||= [];
1683 push(@{$os->{kernels}}, $kernel);
1685 $config{kernel} = $kernel;
1687 # Look for an initrd entry
1690 $initrd = $g->aug_get("$bootable/initrd");
1695 _inspect_initrd($g, $os, "$grub$initrd",
1696 $kernel->{version});
1698 warn __x("Grub entry {title} does not specify an ".
1699 "initrd", title => $config{title});
1704 push(@configs, \%config);
1708 # Create the top level boot entry
1710 $boot{configs} = \@configs;
1711 $boot{grub_fs} = $grub;
1713 # Add the default configuration
1715 $boot{default} = $g->aug_get("/files/$grub_conf/default");
1718 $os->{boot} = \%boot;
1721 elsif ($os->{os} eq "windows") {
1726 =head2 inspect_linux_kernel
1728 my $kernel_hash = inspect_linux_kernel($g, $vmlinuz_path, $package_format);
1730 inspect_linux_kernel returns a hash describing the target linux kernel. For the
1731 contents of the hash, see the I<kernels> structure described under
1732 L</inspect_in_detail>.
1736 sub inspect_linux_kernel
1738 my ($g, $path, $package_format) = @_;
1742 $kernel{path} = $path;
1744 # If this is a packaged kernel, try to work out the name of the package
1745 # which installed it. This lets us know what to install to replace it with,
1746 # e.g. kernel, kernel-smp, kernel-hugemem, kernel-PAE
1747 if($package_format eq "rpm") {
1749 eval { $package = $g->command(['rpm', '-qf', '--qf',
1750 '%{NAME}', $path]); };
1751 $kernel{package} = $package if defined($package);;
1754 # Try to get the kernel version by running file against it
1756 my $filedesc = $g->file($path);
1757 if($filedesc =~ /^$path: Linux kernel .*\bversion\s+(\S+)\b/) {
1761 # Sometimes file can't work out the kernel version, for example because it's
1762 # a Xen PV kernel. In this case try to guess the version from the filename
1764 if($path =~ m{/boot/vmlinuz-(.*)}) {
1767 # Check /lib/modules/$version exists
1768 if(!$g->is_dir("/lib/modules/$version")) {
1769 warn __x("Didn't find modules directory {modules} for kernel ".
1770 "{path}", modules => "/lib/modules/$version",
1777 warn __x("Couldn't guess kernel version number from path for ".
1778 "kernel {path}", path => $path);
1785 $kernel{version} = $version;
1790 my $prefix = "/lib/modules/$version";
1791 foreach my $module ($g->find ($prefix)) {
1792 if ($module =~ m{/([^/]+)\.(?:ko|o)$}) {
1793 $any_module = "$prefix$module" unless defined $any_module;
1798 $kernel{modules} = \@modules;
1800 # Determine kernel architecture by looking at the arch
1801 # of any kernel module.
1802 $kernel{arch} = file_architecture ($g, $any_module);
1807 # Find all modprobe aliases. Specifically, this looks in the following
1809 # * /etc/conf.modules
1810 # * /etc/modules.conf
1811 # * /etc/modprobe.conf
1812 # * /etc/modprobe.d/*
1814 sub _find_modprobe_aliases
1821 $g->aug_init("/", 16);
1823 # Register additional paths to the Modprobe lens
1824 $g->aug_set("/augeas/load/Modprobe/incl[last()+1]", "/etc/modules.conf");
1825 $g->aug_set("/augeas/load/Modprobe/incl[last()+1]", "/etc/conf.modules");
1827 # Make augeas reload
1830 my %modprobe_aliases;
1832 for my $pattern qw(/files/etc/conf.modules/alias
1833 /files/etc/modules.conf/alias
1834 /files/etc/modprobe.conf/alias
1835 /files/etc/modprobe.d/*/alias) {
1836 for my $path ( $g->aug_match($pattern) ) {
1837 $path =~ m{^/files(.*)/alias(?:\[\d*\])?$}
1838 or die __x("{path} doesn't match augeas pattern",
1843 $alias = $g->aug_get($path);
1846 $modulename = $g->aug_get($path.'/modulename');
1849 $aliasinfo{modulename} = $modulename;
1850 $aliasinfo{augeas} = $path;
1851 $aliasinfo{file} = $file;
1853 $modprobe_aliases{$alias} = \%aliasinfo;
1857 $os->{modprobe_aliases} = \%modprobe_aliases;
1860 # Get a listing of device drivers from an initrd
1863 my ($g, $os, $path, $version) = @_;
1867 # Disregard old-style compressed ext2 files and only work with real
1868 # compressed cpio files, since cpio takes ages to (fail to) process anything
1870 if ($g->exists($path) && $g->file($path) =~ /cpio/) {
1872 @modules = $g->initrd_list ($path);
1875 @modules = grep { m{([^/]+)\.(?:ko|o)$} } @modules;
1877 warn __x("{filename}: could not read initrd format",
1878 filename => "$path");
1882 # Add to the top level initrd_modules entry
1883 $os->{initrd_modules} ||= {};
1884 $os->{initrd_modules}->{$version} = \@modules;
1893 Copyright (C) 2009 Red Hat Inc.
1897 Please see the file COPYING.LIB for the full license.
1901 L<virt-inspector(1)>,
1904 L<http://libguestfs.org/>,
1906 L<http://libvirt.org/>,