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;
880 # Determine the architecture of this root.
882 foreach ("/bin/bash", "/bin/ls", "/bin/echo", "/bin/rm", "/bin/sh") {
883 if ($g->is_file ($_)) {
884 $arch = file_architecture ($g, $_);
889 $r->{arch} = $arch if defined $arch;
892 # We only support NT. The control file /boot.ini contains a list of
893 # Windows installations and their %systemroot%s in a simple text
896 # XXX We could parse this better. This won't work if /boot.ini is on
897 # a different drive from the %systemroot%, and in other unusual cases.
899 sub _check_windows_root
904 my $use_windows_registry = shift;
906 my $boot_ini = resolve_windows_path ($g, "/boot.ini");
907 $r->{boot_ini} = $boot_ini;
909 if (defined $r->{boot_ini}) {
910 $_ = $g->cat ($boot_ini);
911 my @lines = split /\n/;
917 } elsif (m/^default=.*?\\(\w+)$/i) {
920 } elsif (m/\\(\w+)=/) {
926 if (defined $systemroot) {
927 $r->{systemroot} = resolve_windows_path ($g, "/$systemroot");
928 if (defined $r->{systemroot}) {
929 _check_windows_arch ($g, $r, $r->{systemroot});
930 if ($use_windows_registry) {
931 _check_windows_registry ($g, $r, $r->{systemroot});
938 # Find Windows userspace arch.
940 sub _check_windows_arch
945 my $systemroot = shift;
948 resolve_windows_path ($g, $r->{systemroot} . "/system32/cmd.exe");
949 $r->{arch} = file_architecture ($g, $cmd_exe) if $cmd_exe;
952 sub _check_windows_registry
957 my $systemroot = shift;
959 # Download the system registry files. Only download the
960 # interesting ones, and we don't bother with user profiles at all.
962 my $configdir = resolve_windows_path ($g, "$systemroot/system32/config");
963 if (defined $configdir) {
964 my $softwaredir = resolve_windows_path ($g, "$configdir/software");
965 if (defined $softwaredir) {
966 _load_windows_registry ($g, $r, $softwaredir,
967 "HKEY_LOCAL_MACHINE\\SOFTWARE");
969 my $systemdir = resolve_windows_path ($g, "$configdir/system");
970 if (defined $systemdir) {
971 _load_windows_registry ($g, $r, $systemdir,
972 "HKEY_LOCAL_MACHINE\\System");
977 sub _load_windows_registry
985 my $dir = tempdir (CLEANUP => 1);
987 $g->download ($regfile, "$dir/reg");
989 # 'reged' command is particularly noisy. Redirect stdout and
990 # stderr to /dev/null temporarily.
991 open SAVEOUT, ">&STDOUT";
992 open SAVEERR, ">&STDERR";
993 open STDOUT, ">/dev/null";
994 open STDERR, ">/dev/null";
996 my @cmd = ("reged", "-x", "$dir/reg", "$prefix", "\\", "$dir/out");
997 my $res = system (@cmd);
1001 open STDOUT, ">&SAVEOUT";
1002 open STDERR, ">&SAVEERR";
1006 unless ($res == 0) {
1007 warn __x("reged command failed: {errormsg}", errormsg => $?);
1011 # Some versions of reged segfault on inputs. If that happens we
1012 # may get no / partial output file. Anyway, if it exists, load
1015 unless (open F, "$dir/out") {
1016 warn __x("no output from reged command: {errormsg}", errormsg => $!);
1019 { local $/ = undef; $content = <F>; }
1023 @registry = @{$r->{registry}} if exists $r->{registry};
1024 push @registry, $content;
1025 $r->{registry} = \@registry;
1034 # Grub version, if we care.
1037 =head2 inspect_operating_systems
1039 \%oses = inspect_operating_systems ($g, \%fses);
1041 This function works out how partitions are related to each other. In
1042 the case of a single-boot VM, we work out how the partitions are
1043 mounted in respect of each other (eg. C</dev/sda1> is mounted as
1044 C</boot>). In the case of a multi-boot VM where there are several
1045 roots, we may identify several operating system roots, and mountpoints
1048 This function returns a hashref C<\%oses> which at the top level looks
1052 '/dev/VG/Root' => \%os,
1055 (There can be multiple roots for a multi-boot VM).
1057 The C<\%os> hash contains the following keys (any can be omitted):
1063 Operating system type, eg. "linux", "windows".
1067 Operating system userspace architecture, eg. "i386", "x86_64".
1071 Operating system distribution, eg. "debian".
1075 Operating system major version, eg. "4".
1079 Operating system minor version, eg "3".
1083 The value is a reference to the root partition C<%fs> hash.
1087 The value is the name of the root partition (as a string).
1092 The value is a hashref like this:
1095 '/' => '/dev/VG/Root',
1096 '/boot' => '/dev/sda1',
1101 Filesystems (including swap devices and unmounted partitions).
1102 The value is a hashref like this:
1105 '/dev/sda1' => \%fs,
1106 '/dev/VG/Root' => \%fs,
1107 '/dev/VG/Swap' => \%fs,
1114 sub inspect_operating_systems
1122 foreach (sort keys %$fses) {
1123 if ($fses->{$_}->{is_root}) {
1125 root => $fses->{$_},
1128 _get_os_version ($g, \%r);
1129 _assign_mount_points ($g, $fses, \%r);
1143 $r->{os} = $r->{root}->{fsos} if exists $r->{root}->{fsos};
1144 $r->{distro} = $r->{root}->{osdistro} if exists $r->{root}->{osdistro};
1145 $r->{major_version} = $r->{root}->{os_major_version}
1146 if exists $r->{root}->{os_major_version};
1147 $r->{minor_version} = $r->{root}->{os_minor_version}
1148 if exists $r->{root}->{os_minor_version};
1149 $r->{package_format} = $r->{root}->{package_format}
1150 if exists $r->{root}->{package_format};
1151 $r->{package_management} = $r->{root}->{package_management}
1152 if exists $r->{root}->{package_management};
1153 $r->{arch} = $r->{root}->{arch} if exists $r->{root}->{arch};
1156 sub _assign_mount_points
1163 $r->{mounts} = { "/" => $r->{root_device} };
1164 $r->{filesystems} = { $r->{root_device} => $r->{root} };
1166 # Use /etc/fstab if we have it to mount the rest.
1167 if (exists $r->{root}->{fstab}) {
1168 my @fstab = @{$r->{root}->{fstab}};
1170 my ($spec, $file) = @$_;
1172 my ($dev, $fs) = _find_filesystem ($g, $fses, $spec);
1174 $r->{mounts}->{$file} = $dev;
1175 $r->{filesystems}->{$dev} = $fs;
1176 if (exists $fs->{used}) {
1181 $fs->{spec} = $spec;
1187 # Find filesystem by device name, LABEL=.. or UUID=..
1188 sub _find_filesystem
1194 if (/^LABEL=(.*)/) {
1196 foreach (sort keys %$fses) {
1197 if (exists $fses->{$_}->{label} &&
1198 $fses->{$_}->{label} eq $label) {
1199 return ($_, $fses->{$_});
1202 warn __x("unknown filesystem label {label}\n", label => $label);
1204 } elsif (/^UUID=(.*)/) {
1206 foreach (sort keys %$fses) {
1207 if (exists $fses->{$_}->{uuid} &&
1208 $fses->{$_}->{uuid} eq $uuid) {
1209 return ($_, $fses->{$_});
1212 warn __x("unknown filesystem UUID {uuid}\n", uuid => $uuid);
1215 return ($_, $fses->{$_}) if exists $fses->{$_};
1217 # The following is to handle the case where an fstab entry specifies a
1218 # specific device rather than its label or uuid, and the libguestfs
1219 # appliance has named the device differently due to the use of a
1221 # This will work as long as the underlying drivers recognise devices in
1223 if (m{^/dev/hd(.*)} && exists $fses->{"/dev/sd$1"}) {
1224 return ("/dev/sd$1", $fses->{"/dev/sd$1"});
1226 if (m{^/dev/xvd(.*)} && exists $fses->{"/dev/sd$1"}) {
1227 return ("/dev/sd$1", $fses->{"/dev/sd$1"});
1229 if (m{^/dev/mapper/(.*)-(.*)$} && exists $fses->{"/dev/$1/$2"}) {
1230 return ("/dev/$1/$2", $fses->{"/dev/$1/$2"});
1233 return () if m{/dev/cdrom};
1235 warn __x("unknown filesystem {fs}\n", fs => $_);
1240 =head2 mount_operating_system
1242 mount_operating_system ($g, \%os, [$ro]);
1244 This function mounts the operating system described in the
1245 C<%os> hash according to the C<mounts> table in that hash (see
1246 C<inspect_operating_systems>).
1248 The partitions are mounted read-only unless the third parameter
1249 is specified as zero explicitly.
1251 To reverse the effect of this call, use the standard
1252 libguestfs API call C<$g-E<gt>umount_all ()>.
1256 sub mount_operating_system
1261 my $ro = shift; # Read-only?
1263 $ro = 1 unless defined $ro; # ro defaults to 1 if unspecified
1265 my $mounts = $os->{mounts};
1267 # Have to mount / first. Luckily '/' is early in the ASCII
1268 # character set, so this should be OK.
1269 foreach (sort keys %$mounts) {
1270 if($_ ne "swap" && $_ ne "none" && ($_ eq '/' || $g->is_dir ($_))) {
1272 $g->mount_ro ($mounts->{$_}, $_)
1274 $g->mount ($mounts->{$_}, $_)
1280 =head2 inspect_in_detail
1282 mount_operating_system ($g, \%os);
1283 inspect_in_detail ($g, \%os);
1286 The C<inspect_in_detail> function inspects the mounted operating
1287 system for installed applications, installed kernels, kernel modules,
1288 system architecture, and more.
1290 It adds extra keys to the existing C<%os> hash reflecting what it
1291 finds. These extra keys are:
1297 List of applications.
1303 This is a hash of kernel version =E<gt> a hash with the following keys:
1313 Kernel architecture (eg. C<x86-64>).
1321 =item modprobe_aliases
1324 The contents of the modprobe configuration.
1326 =item initrd_modules
1329 The kernel modules installed in the initrd. The value is
1330 a hashref of kernel version to list of modules.
1336 sub inspect_in_detail
1342 _check_for_applications ($g, $os);
1343 _check_for_kernels ($g, $os);
1344 if ($os->{os} eq "linux") {
1345 _check_for_modprobe_aliases ($g, $os);
1346 _check_for_initrd ($g, $os);
1350 sub _check_for_applications
1358 my $osn = $os->{os};
1359 if ($osn eq "linux") {
1360 my $package_format = $os->{package_format};
1361 if (defined $package_format && $package_format eq "rpm") {
1362 my @lines = $g->command_lines
1365 "--qf", "%{name} %{epoch} %{version} %{release} %{arch}\n"]);
1367 if (m/^(.*) (.*) (.*) (.*) (.*)$/) {
1369 $epoch = "" if $epoch eq "(none)";
1381 } elsif ($osn eq "windows") {
1383 # I worked out a general plan for this, but haven't
1384 # implemented it yet. We can iterate over /Program Files
1385 # looking for *.EXE files, which we download, then use
1386 # i686-pc-mingw32-windres on, to find the VERSIONINFO
1387 # section, which has a lot of useful information.
1390 $os->{apps} = \@apps;
1393 sub _check_for_kernels
1401 my $osn = $os->{os};
1402 if ($osn eq "linux") {
1403 # Installed kernels will have a corresponding /lib/modules/<version>
1404 # directory, which is the easiest way to find out what kernels
1405 # are installed, and what modules are available.
1406 foreach ($g->ls ("/lib/modules")) {
1407 if ($g->is_dir ("/lib/modules/$_")) {
1409 $kernel{version} = $_;
1414 my $prefix = "/lib/modules/$_";
1415 foreach ($g->find ($prefix)) {
1416 if (m,/([^/]+)\.ko$, || m,([^/]+)\.o$,) {
1417 $any_module = "$prefix$_" unless defined $any_module;
1422 $kernel{modules} = \@modules;
1424 # Determine kernel architecture by looking at the arch
1425 # of any kernel module.
1426 $kernel{arch} = file_architecture ($g, $any_module);
1428 push @kernels, \%kernel;
1432 } elsif ($osn eq "windows") {
1436 $os->{kernels} = \@kernels;
1439 # Check /etc/modprobe.conf to see if there are any specified
1440 # drivers associated with network (ethX) or hard drives. Normally
1441 # one might find something like:
1444 # alias scsi_hostadapter xenblk
1446 # XXX This doesn't look beyond /etc/modprobe.conf, eg. in /etc/modprobe.d/
1448 sub _check_for_modprobe_aliases
1456 $success = $g->aug_init("/", 16);
1458 # Register /etc/modules.conf and /etc/conf.modules to the Modprobe lens
1460 @results = $g->aug_match("/augeas/load/Modprobe/incl");
1462 # Calculate the next index of /augeas/load/Modprobe/incl
1464 foreach ( @results ) {
1465 next unless m{/augeas/load/Modprobe/incl\[(\d*)]};
1466 $i = $1 + 1 if ($1 == $i);
1469 $success = $g->aug_set("/augeas/load/Modprobe/incl[$i]",
1470 "/etc/modules.conf");
1472 $success = $g->aug_set("/augeas/load/Modprobe/incl[$i]",
1473 "/etc/conf.modules");
1475 # Make augeas reload
1476 $success = $g->aug_load();
1478 my %modprobe_aliases;
1480 for my $pattern qw(/files/etc/conf.modules/alias
1481 /files/etc/modules.conf/alias
1482 /files/etc/modprobe.conf/alias
1483 /files/etc/modprobe.d/*/alias) {
1484 @results = $g->aug_match($pattern);
1486 for my $path ( @results ) {
1487 $path =~ m{^/files(.*)/alias(?:\[\d*\])?$}
1488 or die __x("{path} doesn't match augeas pattern",
1493 $alias = $g->aug_get($path);
1496 $modulename = $g->aug_get($path.'/modulename');
1499 $aliasinfo{modulename} = $modulename;
1500 $aliasinfo{augeas} = $path;
1501 $aliasinfo{file} = $file;
1503 $modprobe_aliases{$alias} = \%aliasinfo;
1507 $os->{modprobe_aliases} = \%modprobe_aliases;
1510 # Get a listing of device drivers in any initrd corresponding to a
1511 # kernel. This is an indication of what can possibly be booted.
1513 sub _check_for_initrd
1521 foreach my $initrd ($g->ls ("/boot")) {
1522 if ($initrd =~ m/^initrd-(.*)\.img$/ && $g->is_file ("/boot/$initrd")) {
1526 # Disregard old-style compressed ext2 files and only
1527 # work with real compressed cpio files, since cpio
1528 # takes ages to (fail to) process anything else.
1529 if ($g->file ("/boot/$initrd") =~ /cpio/) {
1531 @modules = $g->initrd_list ("/boot/$initrd");
1534 @modules = grep { m,([^/]+)\.ko$, || m,([^/]+)\.o$, }
1536 $initrd_modules{$version} = \@modules
1538 warn __x("{filename}: could not read initrd format",
1539 filename => "/boot/$initrd");
1545 $os->{initrd_modules} = \%initrd_modules;
1553 Copyright (C) 2009 Red Hat Inc.
1557 Please see the file COPYING.LIB for the full license.
1561 L<virt-inspector(1)>,
1564 L<http://libguestfs.org/>,
1566 L<http://libvirt.org/>,