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 get_partitions resolve_windows_path
68 inspect_all_partitions inspect_partition
69 inspect_operating_systems mount_operating_system inspect_in_detail);
73 $g = open_guest ($name);
75 $g = open_guest ($name, rw => 1, ...);
77 $g = open_guest ($name, address => $uri, ...);
79 $g = open_guest ([$img1, $img2, ...], address => $uri, ...);
81 ($g, $conn, $dom, @images) = open_guest ($name);
83 This function opens a libguestfs handle for either the libvirt domain
84 called C<$name>, or the disk image called C<$name>. Any disk images
85 found through libvirt or specified explicitly are attached to the
88 The C<Sys::Guestfs> handle C<$g> is returned, or if there was an error
89 it throws an exception. To catch errors, wrap the call in an eval
92 The first parameter is either a string referring to a libvirt domain
93 or a disk image, or (if a guest has several disk images) an arrayref
94 C<[$img1, $img2, ...]>.
96 The handle is I<read-only> by default. Use the optional parameter
97 C<rw =E<gt> 1> to open a read-write handle. However if you open a
98 read-write handle, this function will refuse to use active libvirt
101 The handle is still in the config state when it is returned, so you
102 have to call C<$g-E<gt>launch ()> and C<$g-E<gt>wait_ready>.
104 The optional C<address> parameter can be added to specify the libvirt
105 URI. In addition, L<Sys::Virt(3)> lists other parameters which are
106 passed through to C<Sys::Virt-E<gt>new> unchanged.
108 The implicit libvirt handle is closed after this function, I<unless>
109 you call the function in C<wantarray> context, in which case the
110 function returns a tuple of: the open libguestfs handle, the open
111 libvirt handle, and the open libvirt domain handle, and a list of
112 images. (This is useful if you want to do other things like pulling
113 the XML description of the guest). Note that if this is a straight
114 disk image, then C<$conn> and C<$dom> will be C<undef>.
116 If the C<Sys::Virt> module is not available, then libvirt is bypassed,
117 and this function can only open disk images.
127 my $readwrite = $params{rw};
130 if (ref ($first) eq "ARRAY") {
132 } elsif (ref ($first) eq "SCALAR") {
135 die __"open_guest: first parameter must be a string or an arrayref"
142 die __x("guest image {imagename} does not exist or is not readable",
147 die __"open_guest: no libvirt support (install Sys::Virt, XML::XPath and XML::XPath::XMLParser)"
148 unless exists $INC{"Sys/Virt.pm"} &&
149 exists $INC{"XML/XPath.pm"} &&
150 exists $INC{"XML/XPath/XMLParser.pm"};
152 die __"open_guest: too many domains listed on command line"
155 $conn = Sys::Virt->new (readonly => 1, @_);
156 die __"open_guest: cannot connect to libvirt" unless $conn;
158 my @doms = $conn->list_defined_domains ();
159 my $isitinactive = 1;
160 unless ($readwrite) {
161 # In the case where we want read-only access to a domain,
162 # allow the user to specify an active domain too.
163 push @doms, $conn->list_domains ();
167 if ($_->get_name () eq $images[0]) {
175 die __x("{imagename} is not the name of an inactive libvirt domain\n",
176 imagename => $images[0]);
178 die __x("{imagename} is not the name of a libvirt domain\n",
179 imagename => $images[0]);
183 # Get the names of the image(s).
184 my $xml = $dom->get_xml_description ();
186 my $p = XML::XPath->new (xml => $xml);
187 my @disks = $p->findnodes ('//devices/disk/source/@dev');
188 push (@disks, $p->findnodes ('//devices/disk/source/@file'));
190 die __x("{imagename} seems to have no disk devices\n",
191 imagename => $images[0])
194 @images = map { $_->getData } @disks;
197 # We've now got the list of @images, so feed them to libguestfs.
198 my $g = Sys::Guestfs->new ();
203 $g->add_drive_ro ($_);
207 return wantarray ? ($g, $conn, $dom, @images) : $g
210 =head2 get_partitions
212 @partitions = get_partitions ($g);
214 This function takes an open libguestfs handle C<$g> and returns all
215 partitions and logical volumes found on it.
217 What is returned is everything that could contain a filesystem (or
218 swap). Physical volumes are excluded from the list, and so are any
219 devices which are partitioned (eg. C</dev/sda> would not be returned
220 if C</dev/sda1> exists).
228 my @partitions = $g->list_partitions ();
229 my @pvs = $g->pvs ();
230 @partitions = grep { ! _is_pv ($_, @pvs) } @partitions;
232 my @lvs = $g->lvs ();
234 return sort (@lvs, @partitions);
242 return 1 if $_ eq $t;
247 =head2 resolve_windows_path
249 $path = resolve_windows_path ($g, $path);
251 $path = resolve_windows_path ($g, "/windows/system");
252 ==> "/WINDOWS/System"
253 or undef if no path exists
255 This function, which is specific to FAT/NTFS filesystems (ie. Windows
256 guests), lets you look up a case insensitive C<$path> in the
257 filesystem and returns the true, case sensitive path as required by
258 the underlying kernel or NTFS-3g driver.
260 If C<$path> does not exist then this function returns C<undef>.
262 The C<$path> parameter must begin with C</> character and be separated
263 by C</> characters. Do not use C<\>, drive names, etc.
267 sub resolve_windows_path
273 if (substr ($path, 0, 1) ne "/") {
274 warn __"resolve_windows_path: path must start with a / character";
278 my @elems = split (/\//, $path);
281 # Start reconstructing the path at the top.
284 foreach my $dir (@elems) {
286 foreach ($g->ls ($path)) {
287 if (lc ($_) eq lc ($dir)) {
297 return undef unless $found;
303 =head2 file_architecture
305 $arch = file_architecture ($g, $path)
307 The C<file_architecture> function lets you get the architecture for a
308 particular binary or library in the guest. By "architecture" we mean
309 what processor it is compiled for (eg. C<i586> or C<x86_64>).
311 The function works on at least the following types of files:
317 many types of Un*x binary
321 many types of Un*x shared library
325 Windows Win32 and Win64 binaries
329 Windows Win32 and Win64 DLLs
331 Win32 binaries and DLLs return C<i386>.
333 Win64 binaries and DLLs return C<x86_64>.
341 Linux new-style initrd images
345 some non-x86 Linux vmlinuz kernels
349 What it can't do currently:
355 static libraries (libfoo.a)
359 Linux old-style initrd as compressed ext2 filesystem (RHEL 3)
363 x86 Linux vmlinuz kernels
365 x86 vmlinuz images (bzImage format) consist of a mix of 16-, 32- and
366 compressed code, and are horribly hard to unpack. If you want to find
367 the architecture of a kernel, use the architecture of the associated
368 initrd or kernel module(s) instead.
374 sub _elf_arch_to_canonical
378 if ($_ eq "Intel 80386") {
380 } elsif ($_ eq "Intel 80486") {
381 return "i486"; # probably not in the wild
382 } elsif ($_ eq "x86-64") {
384 } elsif (/SPARC32/) {
386 } elsif (/SPARC V9/) {
388 } elsif ($_ eq "IA-64") {
390 } elsif (/64.*PowerPC/) {
392 } elsif (/PowerPC/) {
395 warn __x("returning non-canonical architecture type '{arch}'",
401 my @_initrd_binaries = ("nash", "modprobe", "sh", "bash");
403 sub file_architecture
409 # Our basic tool is 'file' ...
410 my $file = $g->file ($path);
412 if ($file =~ /ELF.*(?:executable|shared object|relocatable), (.+?),/) {
413 # ELF executable or shared object. We need to convert
414 # what file(1) prints into the canonical form.
415 return _elf_arch_to_canonical ($1);
416 } elsif ($file =~ /PE32 executable/) {
417 return "i386"; # Win32 executable or DLL
418 } elsif ($file =~ /PE32\+ executable/) {
419 return "x86_64"; # Win64 executable or DLL
422 elsif ($file =~ /cpio archive/) {
423 # Probably an initrd.
425 if ($file =~ /gzip/) {
427 } elsif ($file =~ /bzip2/) {
431 # Download and unpack it to find a binary file.
432 my $dir = tempdir (CLEANUP => 1);
433 $g->download ($path, "$dir/initrd");
435 my $bins = join " ", map { "bin/$_" } @_initrd_binaries;
436 my $cmd = "cd $dir && $zcat initrd | cpio -id $bins";
437 my $r = system ($cmd);
438 die __x("cpio command failed: {error}", error => $?)
441 foreach my $bin (@_initrd_binaries) {
442 if (-f "$dir/bin/$bin") {
443 open PIPE, "file $dir/bin/$bin |";
446 if (/ELF.*executable, (.+?),/) {
447 return _elf_arch_to_canonical ($1);
452 die __x("file_architecture: no known binaries found in initrd image: {path}",
456 die __x("file_architecture: unknown architecture: {path}",
460 =head1 OPERATING SYSTEM INSPECTION FUNCTIONS
462 The functions in this section can be used to inspect the operating
463 system(s) available inside a virtual machine image. For example, you
464 can find out if the VM is Linux or Windows, how the partitions are
465 meant to be mounted, and what applications are installed.
467 If you just want a simple command-line interface to this
468 functionality, use the L<virt-inspector(1)> tool. The documentation
469 below covers the case where you want to access this functionality from
472 Once you have the list of partitions (from C<get_partitions>) there
473 are several steps involved:
479 Look at each partition separately and find out what is on it.
481 The information you get back includes whether the partition contains a
482 filesystem or swapspace, what sort of filesystem (eg. ext3, ntfs), and
483 a first pass guess at the content of the filesystem (eg. Linux boot,
486 The result of this step is a C<%fs> hash of information, one hash for
489 See: C<inspect_partition>, C<inspect_all_partitions>
493 Work out the relationship between partitions.
495 In this step we work out how partitions are related to each other. In
496 the case of a single-boot VM, we work out how the partitions are
497 mounted in respect of each other (eg. C</dev/sda1> is mounted as
498 C</boot>). In the case of a multi-boot VM where there are several
499 roots, we may identify several operating system roots, and mountpoints
502 The result of this step is a single hash called C<%oses> which is
503 described in more detail below, but at the top level looks like:
506 '/dev/VG/Root1' => \%os1,
507 '/dev/VG/Root2' => \%os2,
513 '/' => '/dev/VG/Root1',
514 '/boot' => '/dev/sda1',
519 (example shows a multi-boot VM containing two root partitions).
521 See: C<inspect_operating_systems>
527 Previous to this point we've essentially been looking at each
528 partition in isolation. Now we construct a true guest filesystem by
529 mounting up all of the disks. Only once everything is mounted up can
530 we run commands in the OS context to do more detailed inspection.
532 See: C<mount_operating_system>
536 Check for kernels and applications.
538 This step now does more detailed inspection, where we can look for
539 kernels, applications and more installed in the guest.
541 The result of this is an enhanced C<%os> hash.
543 See: C<inspect_in_detail>
549 This library does not contain functions for generating output based on
550 the analysis steps above. Use a command line tool such as
551 L<virt-inspector(1)> to get useful output.
555 =head2 inspect_all_partitions
557 %fses = inspect_all_partitions ($g, \@partitions);
559 %fses = inspect_all_partitions ($g, \@partitions, use_windows_registry => 1);
561 This calls C<inspect_partition> for each partition in the list
564 The result is a hash which maps partition name to C<\%fs> hashref.
566 The contents of the C<%fs> hash and the meaning of the
567 C<use_windows_registry> flag are explained below.
571 sub inspect_all_partitions
577 return map { $_ => inspect_partition ($g, $_, @_) } @parts;
580 =head2 inspect_partition
582 \%fs = inspect_partition ($g, $partition);
584 \%fs = inspect_partition ($g, $partition, use_windows_registry => 1);
586 This function inspects the device named C<$partition> in isolation and
587 tries to determine what it is. It returns information such as whether
588 the partition is formatted, and with what, whether it is mountable,
589 and what it appears to contain (eg. a Windows root, or a Linux /usr).
591 If C<use_windows_registry> is set to 1, then we will try to download
592 and parse the content of the Windows registry (for Windows root
593 devices). However since this is an expensive and error-prone
594 operation, we don't do this by default. It also requires the external
595 program C<reged>, patched to remove numerous crashing bugs in the
598 The returned value is a hashref C<\%fs> which may contain the
599 following top-level keys (any key can be missing):
605 Filesystem type, eg. "ext2" or "ntfs"
609 Apparent filesystem OS, eg. "linux" or "windows"
613 If set, the partition is a swap partition.
625 If set, the partition could be mounted by libguestfs.
629 Filesystem content, if we could determine it. One of: "linux-grub",
630 "linux-root", "linux-usrlocal", "linux-usr", "windows-root".
634 (For Linux root partitions only).
635 Operating system distribution. One of: "fedora", "rhel", "centos",
636 "scientific", "debian".
640 (For Linux root partitions only)
641 The package format used by the guest distribution. One of: "rpm", "dpkg".
643 =item package_management
645 (For Linux root partitions only)
646 The package management tool used by the guest distribution. One of: "rhn",
649 =item os_major_version
651 (For root partitions only).
652 Operating system major version number.
654 =item os_minor_version
656 (For root partitions only).
657 Operating system minor version number.
661 (For Linux root partitions only).
662 The contents of the C</etc/fstab> file.
666 (For Windows root partitions only).
667 The contents of the C</boot.ini> (NTLDR) file.
671 The value is an arrayref, which is a list of Windows registry
672 file contents, in Windows C<.REG> format.
678 sub inspect_partition
682 my $dev = shift; # LV or partition name.
685 my $use_windows_registry = $params{use_windows_registry};
687 my %r; # Result hash.
689 # First try 'file(1)' on it.
690 my $file = $g->file ($dev);
691 if ($file =~ /ext2 filesystem data/) {
694 } elsif ($file =~ /ext3 filesystem data/) {
697 } elsif ($file =~ /ext4 filesystem data/) {
700 } elsif ($file =~ m{Linux/i386 swap file}) {
706 # If it's ext2/3/4, then we want the UUID and label.
707 if (exists $r{fstype} && $r{fstype} =~ /^ext/) {
708 $r{uuid} = $g->get_e2uuid ($dev);
709 $r{label} = $g->get_e2label ($dev);
712 # Try mounting it, fnarrr.
714 $r{is_mountable} = 1;
715 eval { $g->mount_ro ($dev, "/") };
717 # It's not mountable, probably empty or some format
718 # we don't understand.
719 $r{is_mountable} = 0;
724 if ($g->is_file ("/grub/menu.lst") ||
725 $g->is_file ("/grub/grub.conf")) {
726 $r{content} = "linux-grub";
727 _check_grub ($g, \%r);
732 if ($g->is_dir ("/etc") && $g->is_dir ("/bin") &&
733 $g->is_file ("/etc/fstab")) {
734 $r{content} = "linux-root";
736 _check_linux_root ($g, \%r);
741 if ($g->is_dir ("/etc") && $g->is_dir ("/bin") &&
742 $g->is_dir ("/share") && !$g->exists ("/local") &&
743 !$g->is_file ("/etc/fstab")) {
744 $r{content} = "linux-usrlocal";
749 if ($g->is_dir ("/etc") && $g->is_dir ("/bin") &&
750 $g->is_dir ("/share") && $g->exists ("/local") &&
751 !$g->is_file ("/etc/fstab")) {
752 $r{content} = "linux-usr";
757 if ($g->is_file ("/AUTOEXEC.BAT") ||
758 $g->is_file ("/autoexec.bat") ||
759 $g->is_dir ("/Program Files") ||
760 $g->is_dir ("/WINDOWS") ||
761 $g->is_file ("/boot.ini") ||
762 $g->is_file ("/ntldr")) {
763 $r{fstype} = "ntfs"; # XXX this is a guess
764 $r{fsos} = "windows";
765 $r{content} = "windows-root";
767 _check_windows_root ($g, \%r, $use_windows_registry);
777 sub _check_linux_root
783 # Look into /etc to see if we recognise the operating system.
784 # N.B. don't use $g->is_file here, because it might be a symlink
785 if ($g->exists ("/etc/redhat-release")) {
786 $r->{package_format} = "rpm";
788 $_ = $g->cat ("/etc/redhat-release");
789 if (/Fedora release (\d+)(?:\.(\d+))?/) {
790 $r->{osdistro} = "fedora";
791 $r->{os_major_version} = "$1";
792 $r->{os_minor_version} = "$2" if(defined($2));
793 $r->{package_management} = "yum";
796 elsif (/(Red Hat Enterprise Linux|CentOS|Scientific Linux)/) {
799 if($distro eq "Red Hat Enterprise Linux") {
800 $r->{osdistro} = "rhel";
803 elsif($distro eq "CentOS") {
804 $r->{osdistro} = "centos";
805 $r->{package_management} = "yum";
808 elsif($distro eq "Scientific Linux") {
809 $r->{osdistro} = "scientific";
810 $r->{package_management} = "yum";
813 # Shouldn't be possible
816 if (/$distro.*release (\d+).*Update (\d+)/) {
817 $r->{os_major_version} = "$1";
818 $r->{os_minor_version} = "$2";
821 elsif (/$distro.*release (\d+)(?:\.(\d+))?/) {
822 $r->{os_major_version} = "$1";
825 $r->{os_minor_version} = "$2";
827 $r->{os_minor_version} = "0";
831 # Package management in RHEL changed in version 5
832 if ($r->{osdistro} eq "rhel") {
833 if ($r->{os_major_version} >= 5) {
834 $r->{package_management} = "yum";
836 $r->{package_management} = "rhn";
842 $r->{osdistro} = "redhat-based";
844 } elsif ($g->is_file ("/etc/debian_version")) {
845 $r->{package_format} = "dpkg";
846 $r->{package_management} = "apt";
848 $_ = $g->cat ("/etc/debian_version");
849 if (/(\d+)\.(\d+)/) {
850 $r->{osdistro} = "debian";
851 $r->{os_major_version} = "$1";
852 $r->{os_minor_version} = "$2";
854 $r->{osdistro} = "debian";
858 # Parse the contents of /etc/fstab. This is pretty vital so
859 # we can determine where filesystems are supposed to be mounted.
860 eval "\$_ = \$g->cat ('/etc/fstab');";
862 my @lines = split /\n/;
865 my @fields = split /[ \t]+/;
867 my $spec = $fields[0]; # first column (dev/label/uuid)
868 my $file = $fields[1]; # second column (mountpoint)
869 if ($spec =~ m{^/} ||
870 $spec =~ m{^LABEL=} ||
871 $spec =~ m{^UUID=} ||
873 push @fstab, [$spec, $file]
877 $r->{fstab} = \@fstab if @fstab;
881 # We only support NT. The control file /boot.ini contains a list of
882 # Windows installations and their %systemroot%s in a simple text
885 # XXX We could parse this better. This won't work if /boot.ini is on
886 # a different drive from the %systemroot%, and in other unusual cases.
888 sub _check_windows_root
893 my $use_windows_registry = shift;
895 my $boot_ini = resolve_windows_path ($g, "/boot.ini");
896 $r->{boot_ini} = $boot_ini;
898 if (defined $r->{boot_ini}) {
899 $_ = $g->cat ($boot_ini);
900 my @lines = split /\n/;
906 } elsif (m/^default=.*?\\(\w+)$/i) {
909 } elsif (m/\\(\w+)=/) {
915 if (defined $systemroot) {
916 $r->{systemroot} = resolve_windows_path ($g, "/$systemroot");
917 if (defined $r->{systemroot} && $use_windows_registry) {
918 _check_windows_registry ($g, $r, $r->{systemroot});
924 sub _check_windows_registry
929 my $systemroot = shift;
931 # Download the system registry files. Only download the
932 # interesting ones, and we don't bother with user profiles at all.
934 my $configdir = resolve_windows_path ($g, "$systemroot/system32/config");
935 if (defined $configdir) {
936 my $softwaredir = resolve_windows_path ($g, "$configdir/software");
937 if (defined $softwaredir) {
938 _load_windows_registry ($g, $r, $softwaredir,
939 "HKEY_LOCAL_MACHINE\\SOFTWARE");
941 my $systemdir = resolve_windows_path ($g, "$configdir/system");
942 if (defined $systemdir) {
943 _load_windows_registry ($g, $r, $systemdir,
944 "HKEY_LOCAL_MACHINE\\System");
949 sub _load_windows_registry
957 my $dir = tempdir (CLEANUP => 1);
959 $g->download ($regfile, "$dir/reg");
961 # 'reged' command is particularly noisy. Redirect stdout and
962 # stderr to /dev/null temporarily.
963 open SAVEOUT, ">&STDOUT";
964 open SAVEERR, ">&STDERR";
965 open STDOUT, ">/dev/null";
966 open STDERR, ">/dev/null";
968 my @cmd = ("reged", "-x", "$dir/reg", "$prefix", "\\", "$dir/out");
969 my $res = system (@cmd);
973 open STDOUT, ">&SAVEOUT";
974 open STDERR, ">&SAVEERR";
979 warn __x("reged command failed: {errormsg}", errormsg => $?);
983 # Some versions of reged segfault on inputs. If that happens we
984 # may get no / partial output file. Anyway, if it exists, load
987 unless (open F, "$dir/out") {
988 warn __x("no output from reged command: {errormsg}", errormsg => $!);
991 { local $/ = undef; $content = <F>; }
995 @registry = @{$r->{registry}} if exists $r->{registry};
996 push @registry, $content;
997 $r->{registry} = \@registry;
1006 # Grub version, if we care.
1009 =head2 inspect_operating_systems
1011 \%oses = inspect_operating_systems ($g, \%fses);
1013 This function works out how partitions are related to each other. In
1014 the case of a single-boot VM, we work out how the partitions are
1015 mounted in respect of each other (eg. C</dev/sda1> is mounted as
1016 C</boot>). In the case of a multi-boot VM where there are several
1017 roots, we may identify several operating system roots, and mountpoints
1020 This function returns a hashref C<\%oses> which at the top level looks
1024 '/dev/VG/Root' => \%os,
1027 (There can be multiple roots for a multi-boot VM).
1029 The C<\%os> hash contains the following keys (any can be omitted):
1035 Operating system type, eg. "linux", "windows".
1039 Operating system distribution, eg. "debian".
1043 Operating system major version, eg. "4".
1047 Operating system minor version, eg "3".
1051 The value is a reference to the root partition C<%fs> hash.
1055 The value is the name of the root partition (as a string).
1060 The value is a hashref like this:
1063 '/' => '/dev/VG/Root',
1064 '/boot' => '/dev/sda1',
1069 Filesystems (including swap devices and unmounted partitions).
1070 The value is a hashref like this:
1073 '/dev/sda1' => \%fs,
1074 '/dev/VG/Root' => \%fs,
1075 '/dev/VG/Swap' => \%fs,
1082 sub inspect_operating_systems
1090 foreach (sort keys %$fses) {
1091 if ($fses->{$_}->{is_root}) {
1093 root => $fses->{$_},
1096 _get_os_version ($g, \%r);
1097 _assign_mount_points ($g, $fses, \%r);
1111 $r->{os} = $r->{root}->{fsos} if exists $r->{root}->{fsos};
1112 $r->{distro} = $r->{root}->{osdistro} if exists $r->{root}->{osdistro};
1113 $r->{major_version} = $r->{root}->{os_major_version}
1114 if exists $r->{root}->{os_major_version};
1115 $r->{minor_version} = $r->{root}->{os_minor_version}
1116 if exists $r->{root}->{os_minor_version};
1117 $r->{package_format} = $r->{root}->{package_format}
1118 if exists $r->{root}->{package_format};
1119 $r->{package_management} = $r->{root}->{package_management}
1120 if exists $r->{root}->{package_management};
1123 sub _assign_mount_points
1130 $r->{mounts} = { "/" => $r->{root_device} };
1131 $r->{filesystems} = { $r->{root_device} => $r->{root} };
1133 # Use /etc/fstab if we have it to mount the rest.
1134 if (exists $r->{root}->{fstab}) {
1135 my @fstab = @{$r->{root}->{fstab}};
1137 my ($spec, $file) = @$_;
1139 my ($dev, $fs) = _find_filesystem ($g, $fses, $spec);
1141 $r->{mounts}->{$file} = $dev;
1142 $r->{filesystems}->{$dev} = $fs;
1143 if (exists $fs->{used}) {
1148 $fs->{spec} = $spec;
1154 # Find filesystem by device name, LABEL=.. or UUID=..
1155 sub _find_filesystem
1161 if (/^LABEL=(.*)/) {
1163 foreach (sort keys %$fses) {
1164 if (exists $fses->{$_}->{label} &&
1165 $fses->{$_}->{label} eq $label) {
1166 return ($_, $fses->{$_});
1169 warn __x("unknown filesystem label {label}\n", label => $label);
1171 } elsif (/^UUID=(.*)/) {
1173 foreach (sort keys %$fses) {
1174 if (exists $fses->{$_}->{uuid} &&
1175 $fses->{$_}->{uuid} eq $uuid) {
1176 return ($_, $fses->{$_});
1179 warn __x("unknown filesystem UUID {uuid}\n", uuid => $uuid);
1182 return ($_, $fses->{$_}) if exists $fses->{$_};
1184 # The following is to handle the case where an fstab entry specifies a
1185 # specific device rather than its label or uuid, and the libguestfs
1186 # appliance has named the device differently due to the use of a
1188 # This will work as long as the underlying drivers recognise devices in
1190 if (m{^/dev/hd(.*)} && exists $fses->{"/dev/sd$1"}) {
1191 return ("/dev/sd$1", $fses->{"/dev/sd$1"});
1193 if (m{^/dev/xvd(.*)} && exists $fses->{"/dev/sd$1"}) {
1194 return ("/dev/sd$1", $fses->{"/dev/sd$1"});
1196 if (m{^/dev/mapper/(.*)-(.*)$} && exists $fses->{"/dev/$1/$2"}) {
1197 return ("/dev/$1/$2", $fses->{"/dev/$1/$2"});
1200 return () if m{/dev/cdrom};
1202 warn __x("unknown filesystem {fs}\n", fs => $_);
1207 =head2 mount_operating_system
1209 mount_operating_system ($g, \%os, [$ro]);
1211 This function mounts the operating system described in the
1212 C<%os> hash according to the C<mounts> table in that hash (see
1213 C<inspect_operating_systems>).
1215 The partitions are mounted read-only unless the third parameter
1216 is specified as zero explicitly.
1218 To reverse the effect of this call, use the standard
1219 libguestfs API call C<$g-E<gt>umount_all ()>.
1223 sub mount_operating_system
1228 my $ro = shift; # Read-only?
1230 $ro = 1 unless defined $ro; # ro defaults to 1 if unspecified
1232 my $mounts = $os->{mounts};
1234 # Have to mount / first. Luckily '/' is early in the ASCII
1235 # character set, so this should be OK.
1236 foreach (sort keys %$mounts) {
1237 if($_ ne "swap" && $_ ne "none" && ($_ eq '/' || $g->is_dir ($_))) {
1239 $g->mount_ro ($mounts->{$_}, $_)
1241 $g->mount ($mounts->{$_}, $_)
1247 =head2 inspect_in_detail
1249 mount_operating_system ($g, \%os);
1250 inspect_in_detail ($g, \%os);
1253 The C<inspect_in_detail> function inspects the mounted operating
1254 system for installed applications, installed kernels, kernel modules,
1255 system architecture, and more.
1257 It adds extra keys to the existing C<%os> hash reflecting what it
1258 finds. These extra keys are:
1264 List of applications.
1270 =item modprobe_aliases
1273 The contents of the modprobe configuration.
1275 =item initrd_modules
1278 The kernel modules installed in the initrd. The value is
1279 a hashref of kernel version to list of modules.
1285 sub inspect_in_detail
1291 _check_for_applications ($g, $os);
1292 _check_for_kernels ($g, $os);
1293 if ($os->{os} eq "linux") {
1294 _check_for_modprobe_aliases ($g, $os);
1295 _check_for_initrd ($g, $os);
1299 sub _check_for_applications
1307 my $osn = $os->{os};
1308 if ($osn eq "linux") {
1309 my $package_format = $os->{package_format};
1310 if (defined $package_format && $package_format eq "rpm") {
1311 my @lines = $g->command_lines
1314 "--qf", "%{name} %{epoch} %{version} %{release} %{arch}\n"]);
1316 if (m/^(.*) (.*) (.*) (.*) (.*)$/) {
1318 $epoch = "" if $epoch eq "(none)";
1330 } elsif ($osn eq "windows") {
1332 # I worked out a general plan for this, but haven't
1333 # implemented it yet. We can iterate over /Program Files
1334 # looking for *.EXE files, which we download, then use
1335 # i686-pc-mingw32-windres on, to find the VERSIONINFO
1336 # section, which has a lot of useful information.
1339 $os->{apps} = \@apps;
1342 sub _check_for_kernels
1350 my $osn = $os->{os};
1351 if ($osn eq "linux") {
1352 # Installed kernels will have a corresponding /lib/modules/<version>
1353 # directory, which is the easiest way to find out what kernels
1354 # are installed, and what modules are available.
1355 foreach ($g->ls ("/lib/modules")) {
1356 if ($g->is_dir ("/lib/modules/$_")) {
1358 $kernel{version} = $_;
1362 foreach ($g->find ("/lib/modules/$_")) {
1363 if (m,/([^/]+)\.ko$, || m,([^/]+)\.o$,) {
1368 $kernel{modules} = \@modules;
1370 push @kernels, \%kernel;
1374 } elsif ($osn eq "windows") {
1378 $os->{kernels} = \@kernels;
1381 # Check /etc/modprobe.conf to see if there are any specified
1382 # drivers associated with network (ethX) or hard drives. Normally
1383 # one might find something like:
1386 # alias scsi_hostadapter xenblk
1388 # XXX This doesn't look beyond /etc/modprobe.conf, eg. in /etc/modprobe.d/
1390 sub _check_for_modprobe_aliases
1398 $success = $g->aug_init("/", 16);
1400 # Register /etc/modules.conf and /etc/conf.modules to the Modprobe lens
1402 @results = $g->aug_match("/augeas/load/Modprobe/incl");
1404 # Calculate the next index of /augeas/load/Modprobe/incl
1406 foreach ( @results ) {
1407 next unless m{/augeas/load/Modprobe/incl\[(\d*)]};
1408 $i = $1 + 1 if ($1 == $i);
1411 $success = $g->aug_set("/augeas/load/Modprobe/incl[$i]",
1412 "/etc/modules.conf");
1414 $success = $g->aug_set("/augeas/load/Modprobe/incl[$i]",
1415 "/etc/conf.modules");
1417 # Make augeas reload
1418 $success = $g->aug_load();
1420 my %modprobe_aliases;
1422 for my $pattern qw(/files/etc/conf.modules/alias
1423 /files/etc/modules.conf/alias
1424 /files/etc/modprobe.conf/alias
1425 /files/etc/modprobe.d/*/alias) {
1426 @results = $g->aug_match($pattern);
1428 for my $path ( @results ) {
1429 $path =~ m{^/files(.*)/alias(?:\[\d*\])?$}
1430 or die __x("{path} doesn't match augeas pattern",
1435 $alias = $g->aug_get($path);
1438 $modulename = $g->aug_get($path.'/modulename');
1441 $aliasinfo{modulename} = $modulename;
1442 $aliasinfo{augeas} = $path;
1443 $aliasinfo{file} = $file;
1445 $modprobe_aliases{$alias} = \%aliasinfo;
1449 $os->{modprobe_aliases} = \%modprobe_aliases;
1452 # Get a listing of device drivers in any initrd corresponding to a
1453 # kernel. This is an indication of what can possibly be booted.
1455 sub _check_for_initrd
1463 foreach my $initrd ($g->ls ("/boot")) {
1464 if ($initrd =~ m/^initrd-(.*)\.img$/ && $g->is_file ("/boot/$initrd")) {
1468 # Disregard old-style compressed ext2 files and only
1469 # work with real compressed cpio files, since cpio
1470 # takes ages to (fail to) process anything else.
1471 if ($g->file ("/boot/$initrd") =~ /cpio/) {
1473 @modules = $g->initrd_list ("/boot/$initrd");
1476 @modules = grep { m,([^/]+)\.ko$, || m,([^/]+)\.o$, }
1478 $initrd_modules{$version} = \@modules
1480 warn __x("{filename}: could not read initrd format",
1481 filename => "/boot/$initrd");
1487 $os->{initrd_modules} = \%initrd_modules;
1495 Copyright (C) 2009 Red Hat Inc.
1499 Please see the file COPYING.LIB for the full license.
1503 L<virt-inspector(1)>,
1506 L<http://libguestfs.org/>,
1508 L<http://libvirt.org/>,