2 # Copyright (C) 2009 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;
24 use File::Temp qw/tempdir/;
25 use Locale::TextDomain 'libguestfs';
28 eval "use Sys::Virt;";
29 eval "use XML::XPath;";
30 eval "use XML::XPath::XMLParser;";
36 Sys::Guestfs::Lib - Useful functions for using libguestfs from Perl
40 use Sys::Guestfs::Lib qw(open_guest inspect_all_partitions ...);
42 $g = open_guest ($name);
44 %fses = inspect_all_partitions ($g, \@partitions);
46 (and many more calls - see the rest of this manpage)
50 C<Sys::Guestfs::Lib> is an extra library of useful functions for using
51 the libguestfs API from Perl. It also provides tighter integration
54 The basic libguestfs API is not covered by this manpage. Please refer
55 instead to L<Sys::Guestfs(3)> and L<guestfs(3)>. The libvirt API is
56 also not covered. For that, see L<Sys::Virt(3)>.
58 =head1 BASIC FUNCTIONS
64 use vars qw(@EXPORT_OK @ISA);
67 @EXPORT_OK = qw(open_guest feature_available
68 get_partitions resolve_windows_path
69 inspect_all_partitions inspect_partition
70 inspect_operating_systems mount_operating_system inspect_in_detail
71 inspect_linux_kernel);
75 $g = open_guest ($name);
77 $g = open_guest ($name, rw => 1, ...);
79 $g = open_guest ($name, address => $uri, ...);
81 $g = open_guest ([$img1, $img2, ...], address => $uri, ...);
83 ($g, $conn, $dom, @images) = open_guest ($name);
85 This function opens a libguestfs handle for either the libvirt domain
86 called C<$name>, or the disk image called C<$name>. Any disk images
87 found through libvirt or specified explicitly are attached to the
90 The C<Sys::Guestfs> handle C<$g> is returned, or if there was an error
91 it throws an exception. To catch errors, wrap the call in an eval
94 The first parameter is either a string referring to a libvirt domain
95 or a disk image, or (if a guest has several disk images) an arrayref
96 C<[$img1, $img2, ...]>.
98 The handle is I<read-only> by default. Use the optional parameter
99 C<rw =E<gt> 1> to open a read-write handle. However if you open a
100 read-write handle, this function will refuse to use active libvirt
103 The handle is still in the config state when it is returned, so you
104 have to call C<$g-E<gt>launch ()>.
106 The optional C<address> parameter can be added to specify the libvirt
109 The implicit libvirt handle is closed after this function, I<unless>
110 you call the function in C<wantarray> context, in which case the
111 function returns a tuple of: the open libguestfs handle, the open
112 libvirt handle, and the open libvirt domain handle, and a list of
113 images. (This is useful if you want to do other things like pulling
114 the XML description of the guest). Note that if this is a straight
115 disk image, then C<$conn> and C<$dom> will be C<undef>.
117 If the C<Sys::Virt> module is not available, then libvirt is bypassed,
118 and this function can only open disk images.
120 The optional C<interface> parameter can be used to open devices with
121 C<add_drive{,_ro}_with_if>. See
122 L<Sys::Guestfs/guestfs_add_drive_with_if> for more details.
132 my $rw = $params{rw};
133 my $address = $params{address};
134 my $interface = $params{interface};
137 if (ref ($first) eq "ARRAY") {
139 } elsif (ref ($first) eq "SCALAR") {
142 die __"open_guest: first parameter must be a string or an arrayref"
149 die __x("guest image {imagename} does not exist or is not readable",
154 die __"open_guest: no libvirt support (install Sys::Virt, XML::XPath and XML::XPath::XMLParser)"
155 unless exists $INC{"Sys/Virt.pm"} &&
156 exists $INC{"XML/XPath.pm"} &&
157 exists $INC{"XML/XPath/XMLParser.pm"};
159 die __"open_guest: too many domains listed on command line"
162 my @libvirt_args = ();
163 push @libvirt_args, address => $address if defined $address;
165 $conn = Sys::Virt->new (readonly => 1, @libvirt_args);
166 die __"open_guest: cannot connect to libvirt" unless $conn;
168 my @doms = $conn->list_defined_domains ();
169 my $isitinactive = 1;
171 # In the case where we want read-only access to a domain,
172 # allow the user to specify an active domain too.
173 push @doms, $conn->list_domains ();
177 if ($_->get_name () eq $images[0]) {
185 die __x("{imagename} is not the name of an inactive libvirt domain\n",
186 imagename => $images[0]);
188 die __x("{imagename} is not the name of a libvirt domain\n",
189 imagename => $images[0]);
193 # Get the names of the image(s).
194 my $xml = $dom->get_xml_description ();
196 my $p = XML::XPath->new (xml => $xml);
197 my @disks = $p->findnodes ('//devices/disk/source/@dev');
198 push (@disks, $p->findnodes ('//devices/disk/source/@file'));
200 die __x("{imagename} seems to have no disk devices\n",
201 imagename => $images[0])
204 @images = map { $_->getData } @disks;
207 # We've now got the list of @images, so feed them to libguestfs.
208 my $g = Sys::Guestfs->new ();
212 $g->add_drive_with_if ($_, $interface);
218 $g->add_drive_ro_with_if ($_, $interface);
220 $g->add_drive_ro ($_);
225 return wantarray ? ($g, $conn, $dom, @images) : $g
228 =head2 feature_available
230 $bool = feature_available ($g, $feature [, $feature ...]);
232 This function is a useful wrapper around the basic
233 C<$g-E<gt>available> call.
235 C<$g-E<gt>available> tests for availability of a list of features and
236 dies with an error if any is not available.
238 This call tests for the list of features and returns true if all are
239 available, or false otherwise.
241 For a list of features you can test for, see L<guestfs(3)/AVAILABILITY>.
245 sub feature_available {
248 eval { $g->available (\@_); };
252 =head2 get_partitions
254 @partitions = get_partitions ($g);
256 This function takes an open libguestfs handle C<$g> and returns all
257 partitions and logical volumes found on it.
259 What is returned is everything that could contain a filesystem (or
260 swap). Physical volumes are excluded from the list, and so are any
261 devices which are partitioned (eg. C</dev/sda> would not be returned
262 if C</dev/sda1> exists).
270 my @partitions = $g->list_partitions ();
271 my @pvs = $g->pvs ();
272 @partitions = grep { ! _is_pv ($_, @pvs) } @partitions;
275 @lvs = $g->lvs () if feature_available ($g, "lvm2");
277 return sort (@lvs, @partitions);
285 return 1 if $_ eq $t;
290 =head2 resolve_windows_path
292 $path = resolve_windows_path ($g, $path);
294 $path = resolve_windows_path ($g, "/windows/system");
295 ==> "/WINDOWS/System"
296 or undef if no path exists
298 This function, which is specific to FAT/NTFS filesystems (ie. Windows
299 guests), lets you look up a case insensitive C<$path> in the
300 filesystem and returns the true, case sensitive path as required by
301 the underlying kernel or NTFS-3g driver.
303 If C<$path> does not exist then this function returns C<undef>.
305 The C<$path> parameter must begin with C</> character and be separated
306 by C</> characters. Do not use C<\>, drive names, etc.
310 sub resolve_windows_path
316 eval { $r = $g->case_sensitive_path ($path); };
320 =head2 file_architecture
322 $arch = file_architecture ($g, $path)
324 The C<file_architecture> function lets you get the architecture for a
325 particular binary or library in the guest. By "architecture" we mean
326 what processor it is compiled for (eg. C<i586> or C<x86_64>).
328 The function works on at least the following types of files:
334 many types of Un*x binary
338 many types of Un*x shared library
342 Windows Win32 and Win64 binaries
346 Windows Win32 and Win64 DLLs
348 Win32 binaries and DLLs return C<i386>.
350 Win64 binaries and DLLs return C<x86_64>.
358 Linux new-style initrd images
362 some non-x86 Linux vmlinuz kernels
366 What it can't do currently:
372 static libraries (libfoo.a)
376 Linux old-style initrd as compressed ext2 filesystem (RHEL 3)
380 x86 Linux vmlinuz kernels
382 x86 vmlinuz images (bzImage format) consist of a mix of 16-, 32- and
383 compressed code, and are horribly hard to unpack. If you want to find
384 the architecture of a kernel, use the architecture of the associated
385 initrd or kernel module(s) instead.
391 sub _elf_arch_to_canonical
395 if ($_ eq "Intel 80386") {
397 } elsif ($_ eq "Intel 80486") {
398 return "i486"; # probably not in the wild
399 } elsif ($_ eq "x86-64") {
401 } elsif ($_ eq "AMD x86-64") {
403 } elsif (/SPARC32/) {
405 } elsif (/SPARC V9/) {
407 } elsif ($_ eq "IA-64") {
409 } elsif (/64.*PowerPC/) {
411 } elsif (/PowerPC/) {
414 warn __x("returning non-canonical architecture type '{arch}'",
420 my @_initrd_binaries = ("nash", "modprobe", "sh", "bash");
422 sub file_architecture
428 # Our basic tool is 'file' ...
429 my $file = $g->file ($path);
431 if ($file =~ /ELF.*(?:executable|shared object|relocatable), (.+?),/) {
432 # ELF executable or shared object. We need to convert
433 # what file(1) prints into the canonical form.
434 return _elf_arch_to_canonical ($1);
435 } elsif ($file =~ /PE32 executable/) {
436 return "i386"; # Win32 executable or DLL
437 } elsif ($file =~ /PE32\+ executable/) {
438 return "x86_64"; # Win64 executable or DLL
441 elsif ($file =~ /cpio archive/) {
442 # Probably an initrd.
444 if ($file =~ /gzip/) {
446 } elsif ($file =~ /bzip2/) {
450 # Download and unpack it to find a binary file.
451 my $dir = tempdir (CLEANUP => 1);
452 $g->download ($path, "$dir/initrd");
454 my $bins = join " ", map { "bin/$_" } @_initrd_binaries;
455 my $cmd = "cd $dir && $zcat initrd | cpio --quiet -id $bins";
456 my $r = system ($cmd);
457 die __x("cpio command failed: {error}", error => $?)
460 foreach my $bin (@_initrd_binaries) {
461 if (-f "$dir/bin/$bin") {
462 $_ = `file $dir/bin/$bin`;
463 if (/ELF.*executable, (.+?),/) {
464 return _elf_arch_to_canonical ($1);
469 die __x("file_architecture: no known binaries found in initrd image: {path}",
473 die __x("file_architecture: unknown architecture: {path}",
477 =head1 OPERATING SYSTEM INSPECTION FUNCTIONS
479 The functions in this section can be used to inspect the operating
480 system(s) available inside a virtual machine image. For example, you
481 can find out if the VM is Linux or Windows, how the partitions are
482 meant to be mounted, and what applications are installed.
484 If you just want a simple command-line interface to this
485 functionality, use the L<virt-inspector(1)> tool. The documentation
486 below covers the case where you want to access this functionality from
489 Once you have the list of partitions (from C<get_partitions>) there
490 are several steps involved:
496 Look at each partition separately and find out what is on it.
498 The information you get back includes whether the partition contains a
499 filesystem or swapspace, what sort of filesystem (eg. ext3, ntfs), and
500 a first pass guess at the content of the filesystem (eg. Linux boot,
503 The result of this step is a C<%fs> hash of information, one hash for
506 See: C<inspect_partition>, C<inspect_all_partitions>
510 Work out the relationship between partitions.
512 In this step we work out how partitions are related to each other. In
513 the case of a single-boot VM, we work out how the partitions are
514 mounted in respect of each other (eg. C</dev/sda1> is mounted as
515 C</boot>). In the case of a multi-boot VM where there are several
516 roots, we may identify several operating system roots, and mountpoints
519 The result of this step is a single hash called C<%oses> which is
520 described in more detail below, but at the top level looks like:
523 '/dev/VG/Root1' => \%os1,
524 '/dev/VG/Root2' => \%os2,
530 '/' => '/dev/VG/Root1',
531 '/boot' => '/dev/sda1',
536 (example shows a multi-boot VM containing two root partitions).
538 See: C<inspect_operating_systems>
544 Previous to this point we've essentially been looking at each
545 partition in isolation. Now we construct a true guest filesystem by
546 mounting up all of the disks. Only once everything is mounted up can
547 we run commands in the OS context to do more detailed inspection.
549 See: C<mount_operating_system>
553 Check for kernels and applications.
555 This step now does more detailed inspection, where we can look for
556 kernels, applications and more installed in the guest.
558 The result of this is an enhanced C<%os> hash.
560 See: C<inspect_in_detail>
566 This library does not contain functions for generating output based on
567 the analysis steps above. Use a command line tool such as
568 L<virt-inspector(1)> to get useful output.
572 =head2 inspect_all_partitions
574 %fses = inspect_all_partitions ($g, \@partitions);
576 %fses = inspect_all_partitions ($g, \@partitions, use_windows_registry => 1);
578 This calls C<inspect_partition> for each partition in the list
581 The result is a hash which maps partition name to C<\%fs> hashref.
583 The contents of the C<%fs> hash and the meaning of the
584 C<use_windows_registry> flag are explained below.
588 # Turn /dev/vd* and /dev/hd* into canonical device names
589 # (see BLOCK DEVICE NAMING in guestfs(3)).
591 sub _canonical_dev ($)
594 return "/dev/sd$1" if $dev =~ m{^/dev/[vh]d(\w+)};
598 sub inspect_all_partitions
604 return map { _canonical_dev ($_) => inspect_partition ($g, $_, @_) } @parts;
607 =head2 inspect_partition
609 \%fs = inspect_partition ($g, $partition);
611 \%fs = inspect_partition ($g, $partition, use_windows_registry => 1);
613 This function inspects the device named C<$partition> in isolation and
614 tries to determine what it is. It returns information such as whether
615 the partition is formatted, and with what, whether it is mountable,
616 and what it appears to contain (eg. a Windows root, or a Linux /usr).
618 If C<use_windows_registry> is set to 1, then we will try to download
619 and parse the content of the Windows registry (for Windows root
620 devices). However since this is an expensive and error-prone
621 operation, we don't do this by default. It also requires the external
622 program C<reged>, patched to remove numerous crashing bugs in the
625 The returned value is a hashref C<\%fs> which may contain the
626 following top-level keys (any key can be missing):
632 Filesystem type, eg. "ext2" or "ntfs"
636 Apparent filesystem OS, eg. "linux" or "windows"
640 If set, the partition is a swap partition.
652 If set, the partition could be mounted by libguestfs.
656 Filesystem content, if we could determine it. One of: "linux-grub",
657 "linux-root", "linux-usrlocal", "linux-usr", "windows-root".
661 (For Linux root partitions only).
662 Operating system distribution. One of: "fedora", "rhel", "centos",
663 "scientific", "debian".
667 (For Linux root partitions only)
668 The package format used by the guest distribution. One of: "rpm", "deb".
670 =item package_management
672 (For Linux root partitions only)
673 The package management tool used by the guest distribution. One of: "rhn",
676 =item os_major_version
678 (For root partitions only).
679 Operating system major version number.
681 =item os_minor_version
683 (For root partitions only).
684 Operating system minor version number.
688 (For Linux root partitions only).
689 The contents of the C</etc/fstab> file.
693 (For Windows root partitions only).
694 The contents of the C</boot.ini> (NTLDR) file.
698 The value is an arrayref, which is a list of Windows registry
699 file contents, in Windows C<.REG> format.
705 sub inspect_partition
709 my $dev = shift; # LV or partition name.
712 my $use_windows_registry = $params{use_windows_registry};
714 my %r; # Result hash.
716 # First try 'file(1)' on it.
717 my $file = $g->file ($dev);
718 if ($file =~ /ext2 filesystem data/) {
721 } elsif ($file =~ /ext3 filesystem data/) {
724 } elsif ($file =~ /ext4 filesystem data/) {
727 } elsif ($file =~ m{Linux/i386 swap file}) {
733 # If it's ext2/3/4, then we want the UUID and label.
734 if (exists $r{fstype} && $r{fstype} =~ /^ext/) {
735 $r{uuid} = $g->get_e2uuid ($dev);
736 $r{label} = $g->get_e2label ($dev);
739 # Try mounting it, fnarrr.
741 $r{is_mountable} = 1;
742 eval { $g->mount_ro ($dev, "/") };
744 # It's not mountable, probably empty or some format
745 # we don't understand.
746 $r{is_mountable} = 0;
751 if ($g->is_file ("/grub/menu.lst") ||
752 $g->is_file ("/grub/grub.conf")) {
753 $r{content} = "linux-grub";
754 _check_grub ($g, \%r);
759 if ($g->is_dir ("/etc") && $g->is_dir ("/bin") &&
760 $g->is_file ("/etc/fstab")) {
761 $r{content} = "linux-root";
763 _check_linux_root ($g, \%r);
768 if ($g->is_dir ("/etc") && $g->is_dir ("/bin") &&
769 $g->is_dir ("/share") && !$g->exists ("/local") &&
770 !$g->is_file ("/etc/fstab")) {
771 $r{content} = "linux-usrlocal";
776 if ($g->is_dir ("/etc") && $g->is_dir ("/bin") &&
777 $g->is_dir ("/share") && $g->exists ("/local") &&
778 !$g->is_file ("/etc/fstab")) {
779 $r{content} = "linux-usr";
784 if ($g->is_file ("/AUTOEXEC.BAT") ||
785 $g->is_file ("/autoexec.bat") ||
786 $g->is_dir ("/Program Files") ||
787 $g->is_dir ("/WINDOWS") ||
788 $g->is_file ("/boot.ini") ||
789 $g->is_file ("/ntldr")) {
790 $r{fstype} = "ntfs"; # XXX this is a guess
791 $r{fsos} = "windows";
792 $r{content} = "windows-root";
794 _check_windows_root ($g, \%r, $use_windows_registry);
804 sub _check_linux_root
810 # Look into /etc to see if we recognise the operating system.
811 # N.B. don't use $g->is_file here, because it might be a symlink
812 if ($g->exists ("/etc/redhat-release")) {
813 $r->{package_format} = "rpm";
815 $_ = $g->cat ("/etc/redhat-release");
816 if (/Fedora release (\d+)(?:\.(\d+))?/) {
817 chomp; $r->{product_name} = $_;
818 $r->{osdistro} = "fedora";
819 $r->{os_major_version} = "$1";
820 $r->{os_minor_version} = "$2" if(defined($2));
821 $r->{package_management} = "yum";
824 elsif (/(Red Hat Enterprise Linux|CentOS|Scientific Linux)/) {
825 chomp; $r->{product_name} = $_;
829 if($distro eq "Red Hat Enterprise Linux") {
830 $r->{osdistro} = "rhel";
833 elsif($distro eq "CentOS") {
834 $r->{osdistro} = "centos";
835 $r->{package_management} = "yum";
838 elsif($distro eq "Scientific Linux") {
839 $r->{osdistro} = "scientific";
840 $r->{package_management} = "yum";
843 # Shouldn't be possible
846 if (/$distro.*release (\d+).*Update (\d+)/) {
847 $r->{os_major_version} = "$1";
848 $r->{os_minor_version} = "$2";
851 elsif (/$distro.*release (\d+)(?:\.(\d+))?/) {
852 $r->{os_major_version} = "$1";
855 $r->{os_minor_version} = "$2";
857 $r->{os_minor_version} = "0";
861 # Package management in RHEL changed in version 5
862 if ($r->{osdistro} eq "rhel") {
863 if ($r->{os_major_version} >= 5) {
864 $r->{package_management} = "yum";
866 $r->{package_management} = "rhn";
872 $r->{osdistro} = "redhat-based";
874 } elsif ($g->is_file ("/etc/debian_version")) {
875 $r->{package_format} = "deb";
876 $r->{package_management} = "apt";
878 $_ = $g->cat ("/etc/debian_version");
879 if (/(\d+)\.(\d+)/) {
880 chomp; $r->{product_name} = $_;
881 $r->{osdistro} = "debian";
882 $r->{os_major_version} = "$1";
883 $r->{os_minor_version} = "$2";
885 $r->{osdistro} = "debian";
889 # Parse the contents of /etc/fstab. This is pretty vital so
890 # we can determine where filesystems are supposed to be mounted.
891 eval "\$_ = \$g->cat ('/etc/fstab');";
893 my @lines = split /\n/;
896 my @fields = split /[ \t]+/;
898 my $spec = $fields[0]; # first column (dev/label/uuid)
899 my $file = $fields[1]; # second column (mountpoint)
900 if ($spec =~ m{^/} ||
901 $spec =~ m{^LABEL=} ||
902 $spec =~ m{^UUID=} ||
904 push @fstab, [$spec, $file]
908 $r->{fstab} = \@fstab if @fstab;
911 # Determine the architecture of this root.
913 foreach ("/bin/bash", "/bin/ls", "/bin/echo", "/bin/rm", "/bin/sh") {
914 if ($g->is_file ($_)) {
915 $arch = file_architecture ($g, $_);
920 $r->{arch} = $arch if defined $arch;
923 # We only support NT. The control file /boot.ini contains a list of
924 # Windows installations and their %systemroot%s in a simple text
927 # XXX We could parse this better. This won't work if /boot.ini is on
928 # a different drive from the %systemroot%, and in other unusual cases.
930 sub _check_windows_root
935 my $use_windows_registry = shift;
937 my $boot_ini = resolve_windows_path ($g, "/boot.ini");
938 $r->{boot_ini} = $boot_ini;
940 if (defined $r->{boot_ini}) {
941 $_ = $g->cat ($boot_ini);
942 my @lines = split /\n/;
948 } elsif (m/^default=.*?\\(\w+)$/i) {
951 } elsif (m/\\(\w+)=/) {
957 if (defined $systemroot) {
958 $r->{systemroot} = resolve_windows_path ($g, "/$systemroot");
959 if (defined $r->{systemroot}) {
960 _check_windows_arch ($g, $r, $r->{systemroot});
961 if ($use_windows_registry) {
962 _check_windows_registry ($g, $r, $r->{systemroot});
969 # Find Windows userspace arch.
971 sub _check_windows_arch
976 my $systemroot = shift;
979 resolve_windows_path ($g, $r->{systemroot} . "/system32/cmd.exe");
980 $r->{arch} = file_architecture ($g, $cmd_exe) if $cmd_exe;
983 sub _check_windows_registry
988 my $systemroot = shift;
990 # Download the system registry files. Only download the
991 # interesting ones, and we don't bother with user profiles at all.
993 my $configdir = resolve_windows_path ($g, "$systemroot/system32/config");
994 if (defined $configdir) {
995 my $softwaredir = resolve_windows_path ($g, "$configdir/software");
996 if (defined $softwaredir) {
997 _load_windows_registry ($g, $r, $softwaredir,
998 "HKEY_LOCAL_MACHINE\\SOFTWARE");
1000 my $systemdir = resolve_windows_path ($g, "$configdir/system");
1001 if (defined $systemdir) {
1002 _load_windows_registry ($g, $r, $systemdir,
1003 "HKEY_LOCAL_MACHINE\\System");
1008 sub _load_windows_registry
1013 my $regfile = shift;
1016 my $dir = tempdir (CLEANUP => 1);
1018 $g->download ($regfile, "$dir/reg");
1020 # 'reged' command is particularly noisy. Redirect stdout and
1021 # stderr to /dev/null temporarily.
1022 open SAVEOUT, ">&STDOUT";
1023 open SAVEERR, ">&STDERR";
1024 open STDOUT, ">/dev/null";
1025 open STDERR, ">/dev/null";
1027 my @cmd = ("reged", "-x", "$dir/reg", "$prefix", "\\", "$dir/out");
1028 my $res = system (@cmd);
1032 open STDOUT, ">&SAVEOUT";
1033 open STDERR, ">&SAVEERR";
1037 unless ($res == 0) {
1038 warn __x("reged command failed: {errormsg}", errormsg => $?);
1042 # Some versions of reged segfault on inputs. If that happens we
1043 # may get no / partial output file. Anyway, if it exists, load
1046 unless (open F, "$dir/out") {
1047 warn __x("no output from reged command: {errormsg}", errormsg => $!);
1050 { local $/ = undef; $content = <F>; }
1054 @registry = @{$r->{registry}} if exists $r->{registry};
1055 push @registry, $content;
1056 $r->{registry} = \@registry;
1065 # Grub version, if we care.
1068 =head2 inspect_operating_systems
1070 \%oses = inspect_operating_systems ($g, \%fses);
1072 This function works out how partitions are related to each other. In
1073 the case of a single-boot VM, we work out how the partitions are
1074 mounted in respect of each other (eg. C</dev/sda1> is mounted as
1075 C</boot>). In the case of a multi-boot VM where there are several
1076 roots, we may identify several operating system roots, and mountpoints
1079 This function returns a hashref C<\%oses> which at the top level looks
1083 '/dev/VG/Root' => \%os,
1086 (There can be multiple roots for a multi-boot VM).
1088 The C<\%os> hash contains the following keys (any can be omitted):
1094 Operating system type, eg. "linux", "windows".
1098 Operating system userspace architecture, eg. "i386", "x86_64".
1102 Operating system distribution, eg. "debian".
1106 Free text product name.
1110 Operating system major version, eg. "4".
1114 Operating system minor version, eg "3".
1118 The value is a reference to the root partition C<%fs> hash.
1122 The value is the name of the root partition (as a string).
1127 The value is a hashref like this:
1130 '/' => '/dev/VG/Root',
1131 '/boot' => '/dev/sda1',
1136 Filesystems (including swap devices and unmounted partitions).
1137 The value is a hashref like this:
1140 '/dev/sda1' => \%fs,
1141 '/dev/VG/Root' => \%fs,
1142 '/dev/VG/Swap' => \%fs,
1149 sub inspect_operating_systems
1157 foreach (sort keys %$fses) {
1158 if ($fses->{$_}->{is_root}) {
1160 root => $fses->{$_},
1163 _get_os_version ($g, \%r);
1164 _assign_mount_points ($g, $fses, \%r);
1178 $r->{os} = $r->{root}->{fsos} if exists $r->{root}->{fsos};
1179 $r->{product_name} = $r->{root}->{product_name}
1180 if exists $r->{root}->{product_name};
1181 $r->{distro} = $r->{root}->{osdistro} if exists $r->{root}->{osdistro};
1182 $r->{major_version} = $r->{root}->{os_major_version}
1183 if exists $r->{root}->{os_major_version};
1184 $r->{minor_version} = $r->{root}->{os_minor_version}
1185 if exists $r->{root}->{os_minor_version};
1186 $r->{package_format} = $r->{root}->{package_format}
1187 if exists $r->{root}->{package_format};
1188 $r->{package_management} = $r->{root}->{package_management}
1189 if exists $r->{root}->{package_management};
1190 $r->{arch} = $r->{root}->{arch} if exists $r->{root}->{arch};
1193 sub _assign_mount_points
1200 $r->{mounts} = { "/" => $r->{root_device} };
1201 $r->{filesystems} = { $r->{root_device} => $r->{root} };
1203 # Use /etc/fstab if we have it to mount the rest.
1204 if (exists $r->{root}->{fstab}) {
1205 my @fstab = @{$r->{root}->{fstab}};
1207 my ($spec, $file) = @$_;
1209 my ($dev, $fs) = _find_filesystem ($g, $fses, $spec);
1211 $r->{mounts}->{$file} = $dev;
1212 $r->{filesystems}->{$dev} = $fs;
1213 if (exists $fs->{used}) {
1218 $fs->{spec} = $spec;
1224 # Find filesystem by device name, LABEL=.. or UUID=..
1225 sub _find_filesystem
1231 if (/^LABEL=(.*)/) {
1233 foreach (sort keys %$fses) {
1234 if (exists $fses->{$_}->{label} &&
1235 $fses->{$_}->{label} eq $label) {
1236 return ($_, $fses->{$_});
1239 warn __x("unknown filesystem label {label}\n", label => $label);
1241 } elsif (/^UUID=(.*)/) {
1243 foreach (sort keys %$fses) {
1244 if (exists $fses->{$_}->{uuid} &&
1245 $fses->{$_}->{uuid} eq $uuid) {
1246 return ($_, $fses->{$_});
1249 warn __x("unknown filesystem UUID {uuid}\n", uuid => $uuid);
1252 return ($_, $fses->{$_}) if exists $fses->{$_};
1254 # The following is to handle the case where an fstab entry specifies a
1255 # specific device rather than its label or uuid, and the libguestfs
1256 # appliance has named the device differently due to the use of a
1258 # This will work as long as the underlying drivers recognise devices in
1260 if (m{^/dev/hd(.*)} && exists $fses->{"/dev/sd$1"}) {
1261 return ("/dev/sd$1", $fses->{"/dev/sd$1"});
1263 if (m{^/dev/xvd(.*)} && exists $fses->{"/dev/sd$1"}) {
1264 return ("/dev/sd$1", $fses->{"/dev/sd$1"});
1266 if (m{^/dev/mapper/(.*)-(.*)$} && exists $fses->{"/dev/$1/$2"}) {
1267 return ("/dev/$1/$2", $fses->{"/dev/$1/$2"});
1270 return () if m{/dev/cdrom};
1272 warn __x("unknown filesystem {fs}\n", fs => $_);
1277 =head2 mount_operating_system
1279 mount_operating_system ($g, \%os, [$ro]);
1281 This function mounts the operating system described in the
1282 C<%os> hash according to the C<mounts> table in that hash (see
1283 C<inspect_operating_systems>).
1285 The partitions are mounted read-only unless the third parameter
1286 is specified as zero explicitly.
1288 To reverse the effect of this call, use the standard
1289 libguestfs API call C<$g-E<gt>umount_all ()>.
1293 sub mount_operating_system
1298 my $ro = shift; # Read-only?
1300 $ro = 1 unless defined $ro; # ro defaults to 1 if unspecified
1302 my $mounts = $os->{mounts};
1304 # Have to mount / first. Luckily '/' is early in the ASCII
1305 # character set, so this should be OK.
1306 foreach (sort keys %$mounts) {
1307 if($_ ne "swap" && $_ ne "none" && ($_ eq '/' || $g->is_dir ($_))) {
1309 $g->mount_ro ($mounts->{$_}, $_)
1311 $g->mount_options ("", $mounts->{$_}, $_)
1317 =head2 inspect_in_detail
1319 mount_operating_system ($g, \%os);
1320 inspect_in_detail ($g, \%os);
1323 The C<inspect_in_detail> function inspects the mounted operating
1324 system for installed applications, installed kernels, kernel modules,
1325 system architecture, and more.
1327 It adds extra keys to the existing C<%os> hash reflecting what it
1328 finds. These extra keys are:
1334 List of applications.
1338 Boot configurations. A hash containing:
1344 An array of boot configurations. Each array entry is a hash containing:
1350 A reference to the expanded initrd structure (see below) for the initrd used by
1351 this boot configuration.
1355 A reference to the expanded kernel structure (see below) for the kernel used by
1356 this boot configuration.
1360 The human readable name of the configuration.
1364 The kernel command line.
1370 The index of the default configuration in the configs array.
1374 The path of the filesystem containing the grub partition.
1382 This is a hash of kernel version =E<gt> a hash with the following keys:
1392 Kernel architecture (eg. C<x86-64>).
1400 The path to the kernel's vmlinuz file.
1404 If the kernel was installed in a package, the name of that package.
1408 =item modprobe_aliases
1411 The contents of the modprobe configuration.
1413 =item initrd_modules
1416 The kernel modules installed in the initrd. The value is
1417 a hashref of kernel version to list of modules.
1423 sub inspect_in_detail
1429 _check_for_applications ($g, $os);
1430 _check_for_kernels ($g, $os);
1431 if ($os->{os} eq "linux") {
1432 _find_modprobe_aliases ($g, $os);
1436 sub _check_for_applications
1444 my $osn = $os->{os};
1445 if ($osn eq "linux") {
1446 my $package_format = $os->{package_format};
1447 if (defined $package_format && $package_format eq "rpm") {
1448 my @lines = $g->command_lines
1451 "--qf", "%{name} %{epoch} %{version} %{release} %{arch}\n"]);
1452 @lines = sort @lines;
1454 if (m/^(.*) (.*) (.*) (.*) (.*)$/) {
1456 undef $epoch if $epoch eq "(none)";
1467 } elsif (defined $package_format && $package_format eq "deb") {
1468 my @lines = $g->command_lines
1470 "-f", '${Package} ${Version} ${Architecture} ${Status}\n',
1472 @lines = sort @lines;
1474 if (m/^(.*) (.*) (.*) (.*) (.*) (.*)$/) {
1475 if ( $6 eq "installed" ) {
1486 } elsif ($osn eq "windows") {
1488 # I worked out a general plan for this, but haven't
1489 # implemented it yet. We can iterate over /Program Files
1490 # looking for *.EXE files, which we download, then use
1491 # i686-pc-mingw32-windres on, to find the VERSIONINFO
1492 # section, which has a lot of useful information.
1495 $os->{apps} = \@apps;
1498 # Find the path which needs to be prepended to paths in grub.conf to make them
1500 sub _find_grub_prefix
1504 my $fses = $os->{filesystems};
1505 die("filesystems undefined") unless(defined($fses));
1507 # Look for the filesystem which contains grub
1509 foreach my $dev (keys(%$fses)) {
1510 my $fsinfo = $fses->{$dev};
1511 if(exists($fsinfo->{content}) && $fsinfo->{content} eq "linux-grub") {
1517 my $mounts = $os->{mounts};
1518 die("mounts undefined") unless(defined($mounts));
1520 # Find where the filesystem is mounted
1521 if(defined($grubdev)) {
1522 foreach my $mount (keys(%$mounts)) {
1523 if($mounts->{$mount} eq $grubdev) {
1524 return "" if($mount eq '/');
1529 die("$grubdev defined in filesystems, but not in mounts");
1532 # If we didn't find it, look for /boot/grub/menu.lst, then try to work out
1533 # what filesystem it's on. We use menu.lst rather than grub.conf because
1534 # debian only uses menu.lst, and anaconda creates a symlink for it.
1535 die(__"Can't find grub on guest") unless($g->exists('/boot/grub/menu.lst'));
1537 # Look for the most specific mount point in mounts
1538 foreach my $path qw(/boot/grub /boot /) {
1539 if(exists($mounts->{$path})) {
1540 return "" if($path eq '/');
1545 die("Couldn't determine which filesystem holds /boot/grub/menu.lst");
1548 sub _check_for_kernels
1552 if ($os->{os} eq "linux" && feature_available ($g, "augeas")) {
1553 # Iterate over entries in grub.conf, populating $os->{boot}
1554 # For every kernel we find, inspect it and add to $os->{kernels}
1556 my $grub = _find_grub_prefix($g, $os);
1557 my $grub_conf = "/etc/grub.conf";
1559 # Debian and other's have no /etc/grub.conf:
1560 if ( ! -f "$grub_conf" ) {
1561 $grub_conf = "$grub/grub/menu.lst";
1570 # ->{title} = "Fedora (2.6.29.6-213.fc11.i686.PAE)"
1571 # ->{kernel} = \kernel
1572 # ->{cmdline} = "ro root=/dev/mapper/vg_mbooth-lv_root rhgb"
1573 # ->{initrd} = \initrd
1574 # ->{default} = \config
1575 # ->{grub_fs} = "/boot"
1577 $g->aug_init("/", 16);
1580 # Get all configurations from grub
1581 foreach my $bootable
1582 ($g->aug_match("/files/$grub_conf/title"))
1585 $config{title} = $g->aug_get($bootable);
1588 eval { $grub_kernel = $g->aug_get("$bootable/kernel"); };
1590 warn __x("Grub entry {title} has no kernel",
1591 title => $config{title});
1594 # Check we've got a kernel entry
1595 if(defined($grub_kernel)) {
1596 my $path = "$grub$grub_kernel";
1598 # Reconstruct the kernel command line
1600 foreach my $arg ($g->aug_match("$bootable/kernel/*")) {
1601 $arg =~ m{/kernel/([^/]*)$}
1602 or die("Unexpected return from aug_match: $arg");
1606 eval { $value = $g->aug_get($arg); };
1608 if(defined($value)) {
1609 push(@args, "$name=$value");
1614 $config{cmdline} = join(' ', @args) if(scalar(@args) > 0);
1617 inspect_linux_kernel($g, $path, $os->{package_format});
1619 # Check the kernel was recognised
1620 if(defined($kernel)) {
1621 # Put this kernel on the top level kernel list
1622 $os->{kernels} ||= [];
1623 push(@{$os->{kernels}}, $kernel);
1625 $config{kernel} = $kernel;
1627 # Look for an initrd entry
1630 $initrd = $g->aug_get("$bootable/initrd");
1635 _inspect_initrd($g, $os, "$grub$initrd",
1636 $kernel->{version});
1638 warn __x("Grub entry {title} does not specify an ".
1639 "initrd", title => $config{title});
1644 push(@configs, \%config);
1648 # Create the top level boot entry
1650 $boot{configs} = \@configs;
1651 $boot{grub_fs} = $grub;
1653 # Add the default configuration
1655 $boot{default} = $g->aug_get("/files/$grub_conf/default");
1658 warn __"No grub default specified";
1661 $os->{boot} = \%boot;
1664 elsif ($os->{os} eq "windows") {
1669 =head2 inspect_linux_kernel
1671 my $kernel_hash = inspect_linux_kernel($g, $vmlinuz_path, $package_format);
1673 inspect_linux_kernel returns a hash describing the target linux kernel. For the
1674 contents of the hash, see the I<kernels> structure described under
1675 L</inspect_in_detail>.
1679 sub inspect_linux_kernel
1681 my ($g, $path, $package_format) = @_;
1685 $kernel{path} = $path;
1687 # If this is a packaged kernel, try to work out the name of the package
1688 # which installed it. This lets us know what to install to replace it with,
1689 # e.g. kernel, kernel-smp, kernel-hugemem, kernel-PAE
1690 if($package_format eq "rpm") {
1692 eval { $package = $g->command(['rpm', '-qf', '--qf',
1693 '%{NAME}', $path]); };
1694 $kernel{package} = $package if defined($package);;
1697 # Try to get the kernel version by running file against it
1699 my $filedesc = $g->file($path);
1700 if($filedesc =~ /^$path: Linux kernel .*\bversion\s+(\S+)\b/) {
1704 # Sometimes file can't work out the kernel version, for example because it's
1705 # a Xen PV kernel. In this case try to guess the version from the filename
1707 if($path =~ m{/boot/vmlinuz-(.*)}) {
1710 # Check /lib/modules/$version exists
1711 if(!$g->is_dir("/lib/modules/$version")) {
1712 warn __x("Didn't find modules directory {modules} for kernel ".
1713 "{path}", modules => "/lib/modules/$version",
1720 warn __x("Couldn't guess kernel version number from path for ".
1721 "kernel {path}", path => $path);
1728 $kernel{version} = $version;
1733 my $prefix = "/lib/modules/$version";
1734 foreach my $module ($g->find ($prefix)) {
1735 if ($module =~ m{/([^/]+)\.(?:ko|o)$}) {
1736 $any_module = "$prefix$module" unless defined $any_module;
1741 $kernel{modules} = \@modules;
1743 # Determine kernel architecture by looking at the arch
1744 # of any kernel module.
1745 $kernel{arch} = file_architecture ($g, $any_module);
1750 # Find all modprobe aliases. Specifically, this looks in the following
1752 # * /etc/conf.modules
1753 # * /etc/modules.conf
1754 # * /etc/modprobe.conf
1755 # * /etc/modprobe.d/*
1757 sub _find_modprobe_aliases
1764 $g->aug_init("/", 16);
1766 # Register additional paths to the Modprobe lens
1767 $g->aug_set("/augeas/load/Modprobe/incl[last()+1]", "/etc/modules.conf");
1768 $g->aug_set("/augeas/load/Modprobe/incl[last()+1]", "/etc/conf.modules");
1770 # Make augeas reload
1773 my %modprobe_aliases;
1775 for my $pattern qw(/files/etc/conf.modules/alias
1776 /files/etc/modules.conf/alias
1777 /files/etc/modprobe.conf/alias
1778 /files/etc/modprobe.d/*/alias) {
1779 for my $path ( $g->aug_match($pattern) ) {
1780 $path =~ m{^/files(.*)/alias(?:\[\d*\])?$}
1781 or die __x("{path} doesn't match augeas pattern",
1786 $alias = $g->aug_get($path);
1789 $modulename = $g->aug_get($path.'/modulename');
1792 $aliasinfo{modulename} = $modulename;
1793 $aliasinfo{augeas} = $path;
1794 $aliasinfo{file} = $file;
1796 $modprobe_aliases{$alias} = \%aliasinfo;
1800 $os->{modprobe_aliases} = \%modprobe_aliases;
1803 # Get a listing of device drivers from an initrd
1806 my ($g, $os, $path, $version) = @_;
1810 # Disregard old-style compressed ext2 files and only work with real
1811 # compressed cpio files, since cpio takes ages to (fail to) process anything
1813 if ($g->file ($path) =~ /cpio/) {
1815 @modules = $g->initrd_list ($path);
1818 @modules = grep { m{([^/]+)\.(?:ko|o)$} } @modules;
1820 warn __x("{filename}: could not read initrd format",
1821 filename => "$path");
1825 # Add to the top level initrd_modules entry
1826 $os->{initrd_modules} ||= {};
1827 $os->{initrd_modules}->{$version} = \@modules;
1836 Copyright (C) 2009 Red Hat Inc.
1840 Please see the file COPYING.LIB for the full license.
1844 L<virt-inspector(1)>,
1847 L<http://libguestfs.org/>,
1849 L<http://libvirt.org/>,