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, format => $format, ...);
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, ...]>. For disk images, if the C<format> parameter
107 is specified then that format is forced.
109 The handle is I<read-only> by default. Use the optional parameter
110 C<rw =E<gt> 1> to open a read-write handle. However if you open a
111 read-write handle, this function will refuse to use active libvirt
114 The handle is still in the config state when it is returned, so you
115 have to call C<$g-E<gt>launch ()>.
117 The optional C<address> parameter can be added to specify the libvirt
120 The implicit libvirt handle is closed after this function, I<unless>
121 you call the function in C<wantarray> context, in which case the
122 function returns a tuple of: the open libguestfs handle, the open
123 libvirt handle, and the open libvirt domain handle, and a list of
124 [image,format] pairs. (This is useful if you want to do other things
125 like pulling the XML description of the guest). Note that if this is
126 a straight disk image, then C<$conn> and C<$dom> will be C<undef>.
128 If the C<Sys::Virt> module is not available, then libvirt is bypassed,
129 and this function can only open disk images.
131 The optional C<interface> parameter can be used to open devices with a
132 specified qemu interface. See L<Sys::Guestfs/guestfs_add_drive_opts>
143 my $rw = $params{rw};
144 my $address = $params{address};
145 my $interface = $params{interface};
146 my $format = $params{format}; # undef == autodetect
149 if (ref ($first) eq "ARRAY") {
151 } elsif (ref ($first) eq "SCALAR") {
154 croak __"open_guest: first parameter must be a string or an arrayref"
157 # Check each element of @images is defined.
158 # (See https://bugzilla.redhat.com/show_bug.cgi?id=601092#c3).
160 croak __"open_guest: first argument contains undefined element"
168 croak __x("guest image {imagename} does not exist or is not readable",
173 @images = map { [ $_, $format ] } @images;
175 die __"open_guest: no libvirt support (install Sys::Virt, XML::XPath and XML::XPath::XMLParser)"
176 unless exists $INC{"Sys/Virt.pm"} &&
177 exists $INC{"XML/XPath.pm"} &&
178 exists $INC{"XML/XPath/XMLParser.pm"};
180 die __"open_guest: too many domains listed on command line"
183 my @libvirt_args = ();
184 push @libvirt_args, address => $address if defined $address;
186 $conn = Sys::Virt->new (readonly => 1, @libvirt_args);
187 die __"open_guest: cannot connect to libvirt" unless $conn;
189 my @doms = $conn->list_defined_domains ();
190 my $isitinactive = 1;
192 # In the case where we want read-only access to a domain,
193 # allow the user to specify an active domain too.
194 push @doms, $conn->list_domains ();
198 if ($_->get_name () eq $images[0]) {
206 die __x("{imagename} is not the name of an inactive libvirt domain\n",
207 imagename => $images[0]);
209 die __x("{imagename} is not the name of a libvirt domain\n",
210 imagename => $images[0]);
214 # Get the names of the image(s).
215 my $xml = $dom->get_xml_description ();
217 my $p = XML::XPath->new (xml => $xml);
218 my $nodes = $p->find ('//devices/disk');
222 foreach $node ($nodes->get_nodelist) {
223 # The filename can be in dev or file attribute, hence:
224 my $filename = $p->find ('./source/@dev', $node);
226 $filename = $p->find ('./source/@file', $node);
227 next unless $filename;
229 $filename = $filename->to_literal;
231 # Get the disk format (may not be set).
232 my $format = $p->find ('./driver/@type', $node);
233 $format = $format->to_literal if $format;
235 push @disks, [ $filename, $format ];
238 die __x("{imagename} seems to have no disk devices\n",
239 imagename => $images[0])
245 # We've now got the list of @images, so feed them to libguestfs.
246 my $g = Sys::Guestfs->new ();
248 my @args = ($_->[0]);
249 push @args, format => $_->[1] if defined $_->[1];
250 push @args, readonly => 1 unless $rw;
251 push @args, iface => $interface if defined $interface;
252 $g->add_drive_opts (@args);
255 return wantarray ? ($g, $conn, $dom, @images) : $g
258 =head2 feature_available
260 $bool = feature_available ($g, $feature [, $feature ...]);
262 This function is a useful wrapper around the basic
263 C<$g-E<gt>available> call.
265 C<$g-E<gt>available> tests for availability of a list of features and
266 dies with an error if any is not available.
268 This call tests for the list of features and returns true if all are
269 available, or false otherwise.
271 For a list of features you can test for, see L<guestfs(3)/AVAILABILITY>.
275 sub feature_available {
278 eval { $g->available (\@_); };
282 =head2 get_partitions
284 @partitions = get_partitions ($g);
286 This function takes an open libguestfs handle C<$g> and returns all
287 partitions and logical volumes found on it.
289 What is returned is everything that could contain a filesystem (or
290 swap). Physical volumes are not normally included from the list
291 except if they contain a filesystem directly. Nor are devices which
292 are partitioned (eg. C</dev/sda> would not be returned if C</dev/sda1>
302 # Look to see if any devices directly contain filesystems (RHBZ#590167).
303 my @devices = $g->list_devices ();
304 my @fses_on_device = ();
306 eval { $g->mount_ro ($_, "/"); };
307 push @fses_on_device, $_ unless $@;
311 my @partitions = $g->list_partitions ();
312 my @pvs = $g->pvs ();
313 @partitions = grep { ! _is_pv ($_, @pvs) } @partitions;
316 @lvs = $g->lvs () if feature_available ($g, "lvm2");
318 return sort (@fses_on_device, @lvs, @partitions);
326 return 1 if $_ eq $t;
331 =head2 resolve_windows_path
333 $path = resolve_windows_path ($g, $path);
335 $path = resolve_windows_path ($g, "/windows/system");
336 ==> "/WINDOWS/System"
337 or undef if no path exists
339 This function, which is specific to FAT/NTFS filesystems (ie. Windows
340 guests), lets you look up a case insensitive C<$path> in the
341 filesystem and returns the true, case sensitive path as required by
342 the underlying kernel or NTFS-3g driver.
344 If C<$path> does not exist then this function returns C<undef>.
346 The C<$path> parameter must begin with C</> character and be separated
347 by C</> characters. Do not use C<\>, drive names, etc.
351 sub resolve_windows_path
357 eval { $r = $g->case_sensitive_path ($path); };
361 =head2 file_architecture
363 Deprecated function. Replace any calls to this function with:
365 $g->file_architecture ($path);
369 sub file_architecture
374 return $g->file_architecture ($path);
377 =head1 OPERATING SYSTEM INSPECTION FUNCTIONS
379 The functions in this section can be used to inspect the operating
380 system(s) available inside a virtual machine image. For example, you
381 can find out if the VM is Linux or Windows, how the partitions are
382 meant to be mounted, and what applications are installed.
384 If you just want a simple command-line interface to this
385 functionality, use the L<virt-inspector(1)> tool. The documentation
386 below covers the case where you want to access this functionality from
389 Once you have the list of partitions (from C<get_partitions>) there
390 are several steps involved:
396 Look at each partition separately and find out what is on it.
398 The information you get back includes whether the partition contains a
399 filesystem or swapspace, what sort of filesystem (eg. ext3, ntfs), and
400 a first pass guess at the content of the filesystem (eg. Linux boot,
403 The result of this step is a C<%fs> hash of information, one hash for
406 See: C<inspect_partition>, C<inspect_all_partitions>
410 Work out the relationship between partitions.
412 In this step we work out how partitions are related to each other. In
413 the case of a single-boot VM, we work out how the partitions are
414 mounted in respect of each other (eg. C</dev/sda1> is mounted as
415 C</boot>). In the case of a multi-boot VM where there are several
416 roots, we may identify several operating system roots, and mountpoints
419 The result of this step is a single hash called C<%oses> which is
420 described in more detail below, but at the top level looks like:
423 '/dev/VG/Root1' => \%os1,
424 '/dev/VG/Root2' => \%os2,
430 '/' => '/dev/VG/Root1',
431 '/boot' => '/dev/sda1',
436 (example shows a multi-boot VM containing two root partitions).
438 See: C<inspect_operating_systems>
444 Previous to this point we've essentially been looking at each
445 partition in isolation. Now we construct a true guest filesystem by
446 mounting up all of the disks. Only once everything is mounted up can
447 we run commands in the OS context to do more detailed inspection.
449 See: C<mount_operating_system>
453 Check for kernels and applications.
455 This step now does more detailed inspection, where we can look for
456 kernels, applications and more installed in the guest.
458 The result of this is an enhanced C<%os> hash.
460 See: C<inspect_in_detail>
466 This library does not contain functions for generating output based on
467 the analysis steps above. Use a command line tool such as
468 L<virt-inspector(1)> to get useful output.
472 =head2 inspect_all_partitions
474 %fses = inspect_all_partitions ($g, \@partitions);
476 This calls C<inspect_partition> for each partition in the list
479 The result is a hash which maps partition name to C<\%fs> hashref.
481 The contents of the C<%fs> hash is explained below.
485 # Turn /dev/vd* and /dev/hd* into canonical device names
486 # (see BLOCK DEVICE NAMING in guestfs(3)).
488 sub _canonical_dev ($)
491 return "/dev/sd$1" if $dev =~ m{^/dev/[vh]d(\w+)};
495 sub inspect_all_partitions
501 return map { _canonical_dev ($_) => inspect_partition ($g, $_) } @parts;
504 =head2 inspect_partition
506 \%fs = inspect_partition ($g, $partition);
508 This function inspects the device named C<$partition> in isolation and
509 tries to determine what it is. It returns information such as whether
510 the partition is formatted, and with what, whether it is mountable,
511 and what it appears to contain (eg. a Windows root, or a Linux /usr).
513 If the Perl module L<Win::Hivex(3)> is installed, then additional
514 information is made available for Windows guests, if we can locate and
515 read their registries.
517 The returned value is a hashref C<\%fs> which may contain the
518 following top-level keys (any key can be missing):
524 Filesystem type, eg. "ext2" or "ntfs"
528 Apparent filesystem OS, eg. "linux" or "windows"
532 If set, the partition is a swap partition.
544 If set, the partition could be mounted by libguestfs.
548 Filesystem content, if we could determine it. One of: "linux-grub",
549 "linux-root", "linux-usrlocal", "linux-usr", "windows-root".
553 (For Linux root partitions only).
554 Operating system distribution. One of: "fedora", "rhel", "centos",
555 "scientific", "debian".
559 (For Linux root partitions only)
560 The package format used by the guest distribution. One of: "rpm", "deb".
562 =item package_management
564 (For Linux root partitions only)
565 The package management tool used by the guest distribution. One of: "rhn",
568 =item os_major_version
570 (For root partitions only).
571 Operating system major version number.
573 =item os_minor_version
575 (For root partitions only).
576 Operating system minor version number.
580 (For Linux root partitions only).
581 The contents of the C</etc/fstab> file.
585 (For Windows root partitions only).
586 The contents of the C</boot.ini> (NTLDR) file.
590 The value is an arrayref, which is a list of Windows registry
591 file contents, in Windows C<.REG> format.
597 sub inspect_partition
601 my $dev = shift; # LV or partition name.
603 my %r; # Result hash.
605 # First try 'file(1)' on it.
606 my $file = $g->file ($dev);
607 if ($file =~ /ext2 filesystem data/) {
610 } elsif ($file =~ /ext3 filesystem data/) {
613 } elsif ($file =~ /ext4 filesystem data/) {
616 } elsif ($file =~ m{Linux/i386 swap file}) {
622 # If it's ext2/3/4, then we want the UUID and label.
623 if (exists $r{fstype} && $r{fstype} =~ /^ext/) {
624 $r{uuid} = $g->get_e2uuid ($dev);
625 $r{label} = $g->get_e2label ($dev);
628 # Try mounting it, fnarrr.
630 $r{is_mountable} = 1;
631 eval { $g->mount_ro ($dev, "/") };
633 # It's not mountable, probably empty or some format
634 # we don't understand.
635 $r{is_mountable} = 0;
640 if ($g->is_file ("/grub/menu.lst") ||
641 $g->is_file ("/grub/grub.conf")) {
642 $r{content} = "linux-grub";
643 _check_grub ($g, \%r);
648 if ($g->is_dir ("/etc") && $g->is_dir ("/bin") &&
649 $g->is_file ("/etc/fstab")) {
650 $r{content} = "linux-root";
652 _check_linux_root ($g, \%r);
657 if ($g->is_dir ("/etc") && $g->is_dir ("/bin") &&
658 $g->is_dir ("/share") && !$g->exists ("/local") &&
659 !$g->is_file ("/etc/fstab")) {
660 $r{content} = "linux-usrlocal";
665 if ($g->is_dir ("/etc") && $g->is_dir ("/bin") &&
666 $g->is_dir ("/share") && $g->exists ("/local") &&
667 !$g->is_file ("/etc/fstab")) {
668 $r{content} = "linux-usr";
673 if ($g->is_file ("/AUTOEXEC.BAT") ||
674 $g->is_file ("/autoexec.bat") ||
675 $g->is_dir ("/Program Files") ||
676 $g->is_dir ("/WINDOWS") ||
677 $g->is_file ("/boot.ini") ||
678 $g->is_file ("/ntldr")) {
679 $r{fstype} = "ntfs"; # XXX this is a guess
680 $r{fsos} = "windows";
681 $r{content} = "windows-root";
683 _check_windows_root ($g, \%r);
693 sub _check_linux_root
699 # Look into /etc to see if we recognise the operating system.
700 # N.B. don't use $g->is_file here, because it might be a symlink
701 if ($g->exists ("/etc/redhat-release")) {
702 $r->{package_format} = "rpm";
704 $_ = $g->cat ("/etc/redhat-release");
705 if (/Fedora release (\d+)(?:\.(\d+))?/) {
706 chomp; $r->{product_name} = $_;
707 $r->{osdistro} = "fedora";
708 $r->{os_major_version} = "$1";
709 $r->{os_minor_version} = "$2" if(defined($2));
710 $r->{package_management} = "yum";
713 elsif (/(Red Hat Enterprise Linux|CentOS|Scientific Linux)/) {
714 chomp; $r->{product_name} = $_;
718 if($distro eq "Red Hat Enterprise Linux") {
719 $r->{osdistro} = "rhel";
722 elsif($distro eq "CentOS") {
723 $r->{osdistro} = "centos";
724 $r->{package_management} = "yum";
727 elsif($distro eq "Scientific Linux") {
728 $r->{osdistro} = "scientific";
729 $r->{package_management} = "yum";
732 # Shouldn't be possible
735 if (/$distro.*release (\d+).*Update (\d+)/) {
736 $r->{os_major_version} = "$1";
737 $r->{os_minor_version} = "$2";
740 elsif (/$distro.*release (\d+)(?:\.(\d+))?/) {
741 $r->{os_major_version} = "$1";
744 $r->{os_minor_version} = "$2";
746 $r->{os_minor_version} = "0";
750 # Package management in RHEL changed in version 5
751 if ($r->{osdistro} eq "rhel") {
752 if ($r->{os_major_version} >= 5) {
753 $r->{package_management} = "yum";
755 $r->{package_management} = "rhn";
761 $r->{osdistro} = "redhat-based";
763 } elsif ($g->is_file ("/etc/debian_version")) {
764 $r->{package_format} = "deb";
765 $r->{package_management} = "apt";
767 $_ = $g->cat ("/etc/debian_version");
768 if (/(\d+)\.(\d+)/) {
769 chomp; $r->{product_name} = $_;
770 $r->{osdistro} = "debian";
771 $r->{os_major_version} = "$1";
772 $r->{os_minor_version} = "$2";
774 $r->{osdistro} = "debian";
778 # Parse the contents of /etc/fstab. This is pretty vital so
779 # we can determine where filesystems are supposed to be mounted.
780 eval "\$_ = \$g->cat ('/etc/fstab');";
782 my @lines = split /\n/;
785 my @fields = split /[ \t]+/;
787 my $spec = $fields[0]; # first column (dev/label/uuid)
788 my $file = $fields[1]; # second column (mountpoint)
789 if ($spec =~ m{^/} ||
790 $spec =~ m{^LABEL=} ||
791 $spec =~ m{^UUID=} ||
793 push @fstab, [$spec, $file]
797 $r->{fstab} = \@fstab if @fstab;
800 # Determine the architecture of this root.
802 foreach ("/bin/bash", "/bin/ls", "/bin/echo", "/bin/rm", "/bin/sh") {
803 if ($g->is_file ($_)) {
804 $arch = file_architecture ($g, $_);
809 $r->{arch} = $arch if defined $arch;
812 # We only support NT. The control file /boot.ini contains a list of
813 # Windows installations and their %systemroot%s in a simple text
816 # XXX We don't handle the case where /boot.ini is on a different
817 # partition very well (Windows Vista and later).
819 sub _check_windows_root
825 my $boot_ini = resolve_windows_path ($g, "/boot.ini");
826 $r->{boot_ini} = $boot_ini;
829 if (defined $r->{boot_ini}) {
830 $_ = $g->cat ($boot_ini);
831 my @lines = split /\n/;
836 } elsif (m/^default=.*?\\(\w+)$/i) {
839 } elsif (m/\\(\w+)=/) {
846 if (!defined $systemroot) {
847 # Last ditch ... try to guess %systemroot% location.
848 foreach ("windows", "winnt") {
849 my $dir = resolve_windows_path ($g, "/$_/system32");
857 if (defined $systemroot) {
858 $r->{systemroot} = resolve_windows_path ($g, "/$systemroot");
859 if (defined $r->{systemroot}) {
860 _check_windows_arch ($g, $r, $r->{systemroot});
861 _check_windows_registry ($g, $r, $r->{systemroot});
866 # Find Windows userspace arch.
868 sub _check_windows_arch
873 my $systemroot = shift;
876 resolve_windows_path ($g, $r->{systemroot} . "/system32/cmd.exe");
877 $r->{arch} = file_architecture ($g, $cmd_exe) if $cmd_exe;
880 sub _check_windows_registry
885 my $systemroot = shift;
887 # Download the system registry files. Only download the
888 # interesting ones (SOFTWARE and SYSTEM). We don't bother with
891 return unless exists $INC{"Win/Hivex.pm"};
893 my $configdir = resolve_windows_path ($g, "$systemroot/system32/config");
894 return unless defined $configdir;
896 my $tmpdir = tempdir (CLEANUP => 1);
898 my $software = resolve_windows_path ($g, "$configdir/software");
900 if (defined $software) {
902 $g->download ($software, "$tmpdir/software");
903 $software_hive = Win::Hivex->open ("$tmpdir/software");
906 $r->{windows_software_hive} = $software;
909 my $system = resolve_windows_path ($g, "$configdir/system");
911 if (defined $system) {
913 $g->download ($system, "$tmpdir/system");
914 $system_hive = Win::Hivex->open ("$tmpdir/system");
917 $r->{windows_system_hive} = $system;
920 # Get the ProductName, major and minor version, etc.
921 if (defined $software_hive) {
924 $cv_node = $software_hive->root;
925 $cv_node = $software_hive->node_get_child ($cv_node, $_)
926 foreach ("Microsoft", "Windows NT", "CurrentVersion");
931 my @values = $software_hive->node_values ($cv_node);
934 my $k = $software_hive->value_key ($_);
935 if ($k eq "ProductName") {
936 $_ = $software_hive->value_string ($_);
937 $r->{product_name} = $_ if defined $_;
938 } elsif ($k eq "CurrentVersion") {
939 $_ = $software_hive->value_string ($_);
940 if (defined $_ && m/^(\d+)\.(\d+)/) {
941 $r->{os_major_version} = $1;
942 $r->{os_minor_version} = $2;
944 } elsif ($k eq "CurrentBuild") {
945 $_ = $software_hive->value_string ($_);
946 $r->{windows_current_build} = $_ if defined $_;
947 } elsif ($k eq "SoftwareType") {
948 $_ = $software_hive->value_string ($_);
949 $r->{windows_software_type} = $_ if defined $_;
950 } elsif ($k eq "CurrentType") {
951 $_ = $software_hive->value_string ($_);
952 $r->{windows_current_type} = $_ if defined $_;
953 } elsif ($k eq "RegisteredOwner") {
954 $_ = $software_hive->value_string ($_);
955 $r->{windows_registered_owner} = $_ if defined $_;
956 } elsif ($k eq "RegisteredOrganization") {
957 $_ = $software_hive->value_string ($_);
958 $r->{windows_registered_organization} = $_ if defined $_;
959 } elsif ($k eq "InstallationType") {
960 $_ = $software_hive->value_string ($_);
961 $r->{windows_installation_type} = $_ if defined $_;
962 } elsif ($k eq "EditionID") {
963 $_ = $software_hive->value_string ($_);
964 $r->{windows_edition_id} = $_ if defined $_;
965 } elsif ($k eq "ProductID") {
966 $_ = $software_hive->value_string ($_);
967 $r->{windows_product_id} = $_ if defined $_;
980 # Grub version, if we care.
983 =head2 inspect_operating_systems
985 \%oses = inspect_operating_systems ($g, \%fses);
987 This function works out how partitions are related to each other. In
988 the case of a single-boot VM, we work out how the partitions are
989 mounted in respect of each other (eg. C</dev/sda1> is mounted as
990 C</boot>). In the case of a multi-boot VM where there are several
991 roots, we may identify several operating system roots, and mountpoints
994 This function returns a hashref C<\%oses> which at the top level looks
998 '/dev/VG/Root' => \%os,
1001 There can be multiple roots for a multi-boot VM, but this function
1002 will throw an error if no roots (ie. OSes) could be found.
1004 The C<\%os> hash contains the following keys (any can be omitted):
1010 Operating system type, eg. "linux", "windows".
1014 Operating system userspace architecture, eg. "i386", "x86_64".
1018 Operating system distribution, eg. "debian".
1022 Free text product name.
1026 Operating system major version, eg. "4".
1030 Operating system minor version, eg "3".
1034 The value is a reference to the root partition C<%fs> hash.
1038 The value is the name of the root partition (as a string).
1043 The value is a hashref like this:
1046 '/' => '/dev/VG/Root',
1047 '/boot' => '/dev/sda1',
1052 Filesystems (including swap devices and unmounted partitions).
1053 The value is a hashref like this:
1056 '/dev/sda1' => \%fs,
1057 '/dev/VG/Root' => \%fs,
1058 '/dev/VG/Swap' => \%fs,
1065 sub inspect_operating_systems
1073 foreach (sort keys %$fses) {
1074 if ($fses->{$_}->{is_root}) {
1076 root => $fses->{$_},
1079 _get_os_version ($g, \%r);
1080 _assign_mount_points ($g, $fses, \%r);
1085 # If we didn't find any operating systems then it's an error (RHBZ#591142).
1086 if (0 == keys %oses) {
1087 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";
1099 $r->{os} = $r->{root}->{fsos} if exists $r->{root}->{fsos};
1100 $r->{product_name} = $r->{root}->{product_name}
1101 if exists $r->{root}->{product_name};
1102 $r->{distro} = $r->{root}->{osdistro} if exists $r->{root}->{osdistro};
1103 $r->{major_version} = $r->{root}->{os_major_version}
1104 if exists $r->{root}->{os_major_version};
1105 $r->{minor_version} = $r->{root}->{os_minor_version}
1106 if exists $r->{root}->{os_minor_version};
1107 $r->{package_format} = $r->{root}->{package_format}
1108 if exists $r->{root}->{package_format};
1109 $r->{package_management} = $r->{root}->{package_management}
1110 if exists $r->{root}->{package_management};
1111 $r->{arch} = $r->{root}->{arch} if exists $r->{root}->{arch};
1114 sub _assign_mount_points
1121 $r->{mounts} = { "/" => $r->{root_device} };
1122 $r->{filesystems} = { $r->{root_device} => $r->{root} };
1124 # Use /etc/fstab if we have it to mount the rest.
1125 if (exists $r->{root}->{fstab}) {
1126 my @fstab = @{$r->{root}->{fstab}};
1128 my ($spec, $file) = @$_;
1130 my ($dev, $fs) = _find_filesystem ($g, $fses, $spec);
1132 $r->{mounts}->{$file} = $dev;
1133 $r->{filesystems}->{$dev} = $fs;
1134 if (exists $fs->{used}) {
1139 $fs->{spec} = $spec;
1145 # Find filesystem by device name, LABEL=.. or UUID=..
1146 sub _find_filesystem
1152 if (/^LABEL=(.*)/) {
1154 foreach (sort keys %$fses) {
1155 if (exists $fses->{$_}->{label} &&
1156 $fses->{$_}->{label} eq $label) {
1157 return ($_, $fses->{$_});
1160 warn __x("unknown filesystem label {label}\n", label => $label);
1162 } elsif (/^UUID=(.*)/) {
1164 foreach (sort keys %$fses) {
1165 if (exists $fses->{$_}->{uuid} &&
1166 $fses->{$_}->{uuid} eq $uuid) {
1167 return ($_, $fses->{$_});
1170 warn __x("unknown filesystem UUID {uuid}\n", uuid => $uuid);
1173 return ($_, $fses->{$_}) if exists $fses->{$_};
1175 # The following is to handle the case where an fstab entry specifies a
1176 # specific device rather than its label or uuid, and the libguestfs
1177 # appliance has named the device differently due to the use of a
1179 # This will work as long as the underlying drivers recognise devices in
1181 if (m{^/dev/hd(.*)} && exists $fses->{"/dev/sd$1"}) {
1182 return ("/dev/sd$1", $fses->{"/dev/sd$1"});
1184 if (m{^/dev/xvd(.*)} && exists $fses->{"/dev/sd$1"}) {
1185 return ("/dev/sd$1", $fses->{"/dev/sd$1"});
1187 if (m{^/dev/mapper/(.*)-(.*)$} && exists $fses->{"/dev/$1/$2"}) {
1188 return ("/dev/$1/$2", $fses->{"/dev/$1/$2"});
1191 return () if m{/dev/cdrom};
1193 warn __x("unknown filesystem {fs}\n", fs => $_);
1198 =head2 mount_operating_system
1200 mount_operating_system ($g, \%os, [$ro]);
1202 This function mounts the operating system described in the
1203 C<%os> hash according to the C<mounts> table in that hash (see
1204 C<inspect_operating_systems>).
1206 The partitions are mounted read-only unless the third parameter
1207 is specified as zero explicitly.
1209 To reverse the effect of this call, use the standard
1210 libguestfs API call C<$g-E<gt>umount_all ()>.
1214 sub mount_operating_system
1219 my $ro = shift; # Read-only?
1221 $ro = 1 unless defined $ro; # ro defaults to 1 if unspecified
1223 my $mounts = $os->{mounts};
1225 # Have to mount / first. Luckily '/' is early in the ASCII
1226 # character set, so this should be OK.
1227 foreach (sort keys %$mounts) {
1228 if($_ ne "swap" && $_ ne "none" && ($_ eq '/' || $g->is_dir ($_))) {
1230 $g->mount_ro ($mounts->{$_}, $_)
1232 $g->mount_options ("", $mounts->{$_}, $_)
1238 =head2 inspect_in_detail
1240 mount_operating_system ($g, \%os);
1241 inspect_in_detail ($g, \%os);
1244 The C<inspect_in_detail> function inspects the mounted operating
1245 system for installed applications, installed kernels, kernel modules,
1246 system architecture, and more.
1248 It adds extra keys to the existing C<%os> hash reflecting what it
1249 finds. These extra keys are:
1255 List of applications.
1259 Boot configurations. A hash containing:
1265 An array of boot configurations. Each array entry is a hash containing:
1271 A reference to the expanded initrd structure (see below) for the initrd used by
1272 this boot configuration.
1276 A reference to the expanded kernel structure (see below) for the kernel used by
1277 this boot configuration.
1281 The human readable name of the configuration.
1285 The kernel command line.
1291 The index of the default configuration in the configs array.
1295 The path of the filesystem containing the grub partition.
1303 This is a hash of kernel version =E<gt> a hash with the following keys:
1313 Kernel architecture (eg. C<x86-64>).
1321 The path to the kernel's vmlinuz file.
1325 If the kernel was installed in a package, the name of that package.
1329 =item modprobe_aliases
1332 The contents of the modprobe configuration.
1334 =item initrd_modules
1337 The kernel modules installed in the initrd. The value is
1338 a hashref of kernel version to list of modules.
1344 sub inspect_in_detail
1350 _check_for_applications ($g, $os);
1351 _check_for_kernels ($g, $os);
1352 if ($os->{os} eq "linux") {
1353 _find_modprobe_aliases ($g, $os);
1357 sub _check_for_applications
1365 my $osn = $os->{os};
1366 if ($osn eq "linux") {
1367 my $package_format = $os->{package_format};
1368 if (defined $package_format && $package_format eq "rpm") {
1371 @lines = $g->command_lines
1374 "%{name} %{epoch} %{version} %{release} %{arch}\n"]);
1377 warn(__x("Error running rpm -qa: {error}", error => $@)) if ($@);
1379 @lines = sort @lines;
1381 if (m/^(.*) (.*) (.*) (.*) (.*)$/) {
1383 undef $epoch if $epoch eq "(none)";
1394 } elsif (defined $package_format && $package_format eq "deb") {
1397 @lines = $g->command_lines
1399 "-f", '${Package} ${Version} ${Architecture} ${Status}\n',
1403 warn(__x("Error running dpkg-query: {error}", error => $@)) if ($@);
1405 @lines = sort @lines;
1407 if (m/^(.*) (.*) (.*) (.*) (.*) (.*)$/) {
1408 if ( $6 eq "installed" ) {
1419 } elsif ($osn eq "windows") {
1421 # I worked out a general plan for this, but haven't
1422 # implemented it yet. We can iterate over /Program Files
1423 # looking for *.EXE files, which we download, then use
1424 # i686-pc-mingw32-windres on, to find the VERSIONINFO
1425 # section, which has a lot of useful information.
1428 $os->{apps} = \@apps;
1431 # Find the path which needs to be prepended to paths in grub.conf to make them
1433 sub _find_grub_prefix
1437 my $fses = $os->{filesystems};
1438 die("filesystems undefined") unless(defined($fses));
1440 # Look for the filesystem which contains grub
1442 foreach my $dev (keys(%$fses)) {
1443 my $fsinfo = $fses->{$dev};
1444 if(exists($fsinfo->{content}) && $fsinfo->{content} eq "linux-grub") {
1450 my $mounts = $os->{mounts};
1451 die("mounts undefined") unless(defined($mounts));
1453 # Find where the filesystem is mounted
1454 if(defined($grubdev)) {
1455 foreach my $mount (keys(%$mounts)) {
1456 if($mounts->{$mount} eq $grubdev) {
1457 return "" if($mount eq '/');
1462 die("$grubdev defined in filesystems, but not in mounts");
1465 # If we didn't find it, look for /boot/grub/menu.lst, then try to work out
1466 # what filesystem it's on. We use menu.lst rather than grub.conf because
1467 # debian only uses menu.lst, and anaconda creates a symlink for it.
1468 die(__"Can't find grub on guest") unless($g->exists('/boot/grub/menu.lst'));
1470 # Look for the most specific mount point in mounts
1471 foreach my $path qw(/boot/grub /boot /) {
1472 if(exists($mounts->{$path})) {
1473 return "" if($path eq '/');
1478 die("Couldn't determine which filesystem holds /boot/grub/menu.lst");
1481 sub _check_for_kernels
1485 if ($os->{os} eq "linux" && feature_available ($g, "augeas")) {
1486 # Iterate over entries in grub.conf, populating $os->{boot}
1487 # For every kernel we find, inspect it and add to $os->{kernels}
1489 my $grub = _find_grub_prefix($g, $os);
1490 my $grub_conf = "/etc/grub.conf";
1492 # Debian and other's have no /etc/grub.conf:
1493 if ( ! -f "$grub_conf" ) {
1494 $grub_conf = "$grub/grub/menu.lst";
1503 # ->{title} = "Fedora (2.6.29.6-213.fc11.i686.PAE)"
1504 # ->{kernel} = \kernel
1505 # ->{cmdline} = "ro root=/dev/mapper/vg_mbooth-lv_root rhgb"
1506 # ->{initrd} = \initrd
1507 # ->{default} = \config
1508 # ->{grub_fs} = "/boot"
1510 $g->aug_init("/", 16);
1513 # Get all configurations from grub
1514 foreach my $bootable
1515 ($g->aug_match("/files/$grub_conf/title"))
1518 $config{title} = $g->aug_get($bootable);
1521 eval { $grub_kernel = $g->aug_get("$bootable/kernel"); };
1523 warn __x("Grub entry {title} has no kernel",
1524 title => $config{title});
1527 # Check we've got a kernel entry
1528 if(defined($grub_kernel)) {
1529 my $path = "$grub$grub_kernel";
1531 # Reconstruct the kernel command line
1533 foreach my $arg ($g->aug_match("$bootable/kernel/*")) {
1534 $arg =~ m{/kernel/([^/]*)$}
1535 or die("Unexpected return from aug_match: $arg");
1539 eval { $value = $g->aug_get($arg); };
1541 if(defined($value)) {
1542 push(@args, "$name=$value");
1547 $config{cmdline} = join(' ', @args) if(scalar(@args) > 0);
1550 if ($g->exists($path)) {
1552 inspect_linux_kernel($g, $path, $os->{package_format});
1554 warn __x("grub refers to {path}, which doesn't exist\n",
1558 # Check the kernel was recognised
1559 if(defined($kernel)) {
1560 # Put this kernel on the top level kernel list
1561 $os->{kernels} ||= [];
1562 push(@{$os->{kernels}}, $kernel);
1564 $config{kernel} = $kernel;
1566 # Look for an initrd entry
1569 $initrd = $g->aug_get("$bootable/initrd");
1574 _inspect_initrd($g, $os, "$grub$initrd",
1575 $kernel->{version});
1577 warn __x("Grub entry {title} does not specify an ".
1578 "initrd", title => $config{title});
1583 push(@configs, \%config);
1587 # Create the top level boot entry
1589 $boot{configs} = \@configs;
1590 $boot{grub_fs} = $grub;
1592 # Add the default configuration
1594 $boot{default} = $g->aug_get("/files/$grub_conf/default");
1597 $os->{boot} = \%boot;
1600 elsif ($os->{os} eq "windows") {
1605 =head2 inspect_linux_kernel
1607 my $kernel_hash = inspect_linux_kernel($g, $vmlinuz_path, $package_format);
1609 inspect_linux_kernel returns a hash describing the target linux kernel. For the
1610 contents of the hash, see the I<kernels> structure described under
1611 L</inspect_in_detail>.
1615 sub inspect_linux_kernel
1617 my ($g, $path, $package_format) = @_;
1621 $kernel{path} = $path;
1623 # If this is a packaged kernel, try to work out the name of the package
1624 # which installed it. This lets us know what to install to replace it with,
1625 # e.g. kernel, kernel-smp, kernel-hugemem, kernel-PAE
1626 if($package_format eq "rpm") {
1628 eval { $package = $g->command(['rpm', '-qf', '--qf',
1629 '%{NAME}', $path]); };
1630 $kernel{package} = $package if defined($package);;
1633 # Try to get the kernel version by running file against it
1635 my $filedesc = $g->file($path);
1636 if($filedesc =~ /^$path: Linux kernel .*\bversion\s+(\S+)\b/) {
1640 # Sometimes file can't work out the kernel version, for example because it's
1641 # a Xen PV kernel. In this case try to guess the version from the filename
1643 if($path =~ m{/boot/vmlinuz-(.*)}) {
1646 # Check /lib/modules/$version exists
1647 if(!$g->is_dir("/lib/modules/$version")) {
1648 warn __x("Didn't find modules directory {modules} for kernel ".
1649 "{path}", modules => "/lib/modules/$version",
1656 warn __x("Couldn't guess kernel version number from path for ".
1657 "kernel {path}", path => $path);
1664 $kernel{version} = $version;
1669 my $prefix = "/lib/modules/$version";
1670 foreach my $module ($g->find ($prefix)) {
1671 if ($module =~ m{/([^/]+)\.(?:ko|o)$}) {
1672 $any_module = "$prefix$module" unless defined $any_module;
1677 $kernel{modules} = \@modules;
1679 # Determine kernel architecture by looking at the arch
1680 # of any kernel module.
1681 $kernel{arch} = file_architecture ($g, $any_module);
1686 # Find all modprobe aliases. Specifically, this looks in the following
1688 # * /etc/conf.modules
1689 # * /etc/modules.conf
1690 # * /etc/modprobe.conf
1691 # * /etc/modprobe.d/*
1693 sub _find_modprobe_aliases
1700 $g->aug_init("/", 16);
1702 my %modprobe_aliases;
1704 for my $pattern qw(/files/etc/conf.modules/alias
1705 /files/etc/modules.conf/alias
1706 /files/etc/modprobe.conf/alias
1707 /files/etc/modprobe.d/*/alias) {
1708 for my $path ( $g->aug_match($pattern) ) {
1709 $path =~ m{^/files(.*)/alias(?:\[\d*\])?$}
1710 or die __x("{path} doesn't match augeas pattern",
1715 $alias = $g->aug_get($path);
1718 $modulename = $g->aug_get($path.'/modulename');
1721 $aliasinfo{modulename} = $modulename;
1722 $aliasinfo{augeas} = $path;
1723 $aliasinfo{file} = $file;
1725 $modprobe_aliases{$alias} = \%aliasinfo;
1729 $os->{modprobe_aliases} = \%modprobe_aliases;
1732 # Get a listing of device drivers from an initrd
1735 my ($g, $os, $path, $version) = @_;
1739 # Disregard old-style compressed ext2 files and only work with real
1740 # compressed cpio files, since cpio takes ages to (fail to) process anything
1742 if ($g->exists($path) && $g->file($path) =~ /cpio/) {
1744 @modules = $g->initrd_list ($path);
1747 @modules = grep { m{([^/]+)\.(?:ko|o)$} } @modules;
1749 warn __x("{filename}: could not read initrd format",
1750 filename => "$path");
1754 # Add to the top level initrd_modules entry
1755 $os->{initrd_modules} ||= {};
1756 $os->{initrd_modules}->{$version} = \@modules;
1765 Copyright (C) 2009 Red Hat Inc.
1769 Please see the file COPYING.LIB for the full license.
1773 L<virt-inspector(1)>,
1776 L<http://libguestfs.org/>,
1778 L<http://libvirt.org/>,