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
70 inspect_linux_kernel);
74 $g = open_guest ($name);
76 $g = open_guest ($name, rw => 1, ...);
78 $g = open_guest ($name, address => $uri, ...);
80 $g = open_guest ([$img1, $img2, ...], address => $uri, ...);
82 ($g, $conn, $dom, @images) = open_guest ($name);
84 This function opens a libguestfs handle for either the libvirt domain
85 called C<$name>, or the disk image called C<$name>. Any disk images
86 found through libvirt or specified explicitly are attached to the
89 The C<Sys::Guestfs> handle C<$g> is returned, or if there was an error
90 it throws an exception. To catch errors, wrap the call in an eval
93 The first parameter is either a string referring to a libvirt domain
94 or a disk image, or (if a guest has several disk images) an arrayref
95 C<[$img1, $img2, ...]>.
97 The handle is I<read-only> by default. Use the optional parameter
98 C<rw =E<gt> 1> to open a read-write handle. However if you open a
99 read-write handle, this function will refuse to use active libvirt
102 The handle is still in the config state when it is returned, so you
103 have to call C<$g-E<gt>launch ()>.
105 The optional C<address> parameter can be added to specify the libvirt
106 URI. In addition, L<Sys::Virt(3)> lists other parameters which are
107 passed through to C<Sys::Virt-E<gt>new> unchanged.
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.
128 my $readwrite = $params{rw};
131 if (ref ($first) eq "ARRAY") {
133 } elsif (ref ($first) eq "SCALAR") {
136 die __"open_guest: first parameter must be a string or an arrayref"
143 die __x("guest image {imagename} does not exist or is not readable",
148 die __"open_guest: no libvirt support (install Sys::Virt, XML::XPath and XML::XPath::XMLParser)"
149 unless exists $INC{"Sys/Virt.pm"} &&
150 exists $INC{"XML/XPath.pm"} &&
151 exists $INC{"XML/XPath/XMLParser.pm"};
153 die __"open_guest: too many domains listed on command line"
156 $conn = Sys::Virt->new (readonly => 1, @_);
157 die __"open_guest: cannot connect to libvirt" unless $conn;
159 my @doms = $conn->list_defined_domains ();
160 my $isitinactive = 1;
161 unless ($readwrite) {
162 # In the case where we want read-only access to a domain,
163 # allow the user to specify an active domain too.
164 push @doms, $conn->list_domains ();
168 if ($_->get_name () eq $images[0]) {
176 die __x("{imagename} is not the name of an inactive libvirt domain\n",
177 imagename => $images[0]);
179 die __x("{imagename} is not the name of a libvirt domain\n",
180 imagename => $images[0]);
184 # Get the names of the image(s).
185 my $xml = $dom->get_xml_description ();
187 my $p = XML::XPath->new (xml => $xml);
188 my @disks = $p->findnodes ('//devices/disk/source/@dev');
189 push (@disks, $p->findnodes ('//devices/disk/source/@file'));
191 die __x("{imagename} seems to have no disk devices\n",
192 imagename => $images[0])
195 @images = map { $_->getData } @disks;
198 # We've now got the list of @images, so feed them to libguestfs.
199 my $g = Sys::Guestfs->new ();
204 $g->add_drive_ro ($_);
208 return wantarray ? ($g, $conn, $dom, @images) : $g
211 =head2 get_partitions
213 @partitions = get_partitions ($g);
215 This function takes an open libguestfs handle C<$g> and returns all
216 partitions and logical volumes found on it.
218 What is returned is everything that could contain a filesystem (or
219 swap). Physical volumes are excluded from the list, and so are any
220 devices which are partitioned (eg. C</dev/sda> would not be returned
221 if C</dev/sda1> exists).
229 my @partitions = $g->list_partitions ();
230 my @pvs = $g->pvs ();
231 @partitions = grep { ! _is_pv ($_, @pvs) } @partitions;
233 my @lvs = $g->lvs ();
235 return sort (@lvs, @partitions);
243 return 1 if $_ eq $t;
248 =head2 resolve_windows_path
250 $path = resolve_windows_path ($g, $path);
252 $path = resolve_windows_path ($g, "/windows/system");
253 ==> "/WINDOWS/System"
254 or undef if no path exists
256 This function, which is specific to FAT/NTFS filesystems (ie. Windows
257 guests), lets you look up a case insensitive C<$path> in the
258 filesystem and returns the true, case sensitive path as required by
259 the underlying kernel or NTFS-3g driver.
261 If C<$path> does not exist then this function returns C<undef>.
263 The C<$path> parameter must begin with C</> character and be separated
264 by C</> characters. Do not use C<\>, drive names, etc.
268 sub resolve_windows_path
274 eval { $r = $g->case_sensitive_path ($path); };
278 =head2 file_architecture
280 $arch = file_architecture ($g, $path)
282 The C<file_architecture> function lets you get the architecture for a
283 particular binary or library in the guest. By "architecture" we mean
284 what processor it is compiled for (eg. C<i586> or C<x86_64>).
286 The function works on at least the following types of files:
292 many types of Un*x binary
296 many types of Un*x shared library
300 Windows Win32 and Win64 binaries
304 Windows Win32 and Win64 DLLs
306 Win32 binaries and DLLs return C<i386>.
308 Win64 binaries and DLLs return C<x86_64>.
316 Linux new-style initrd images
320 some non-x86 Linux vmlinuz kernels
324 What it can't do currently:
330 static libraries (libfoo.a)
334 Linux old-style initrd as compressed ext2 filesystem (RHEL 3)
338 x86 Linux vmlinuz kernels
340 x86 vmlinuz images (bzImage format) consist of a mix of 16-, 32- and
341 compressed code, and are horribly hard to unpack. If you want to find
342 the architecture of a kernel, use the architecture of the associated
343 initrd or kernel module(s) instead.
349 sub _elf_arch_to_canonical
353 if ($_ eq "Intel 80386") {
355 } elsif ($_ eq "Intel 80486") {
356 return "i486"; # probably not in the wild
357 } elsif ($_ eq "x86-64") {
359 } elsif ($_ eq "AMD x86-64") {
361 } elsif (/SPARC32/) {
363 } elsif (/SPARC V9/) {
365 } elsif ($_ eq "IA-64") {
367 } elsif (/64.*PowerPC/) {
369 } elsif (/PowerPC/) {
372 warn __x("returning non-canonical architecture type '{arch}'",
378 my @_initrd_binaries = ("nash", "modprobe", "sh", "bash");
380 sub file_architecture
386 # Our basic tool is 'file' ...
387 my $file = $g->file ($path);
389 if ($file =~ /ELF.*(?:executable|shared object|relocatable), (.+?),/) {
390 # ELF executable or shared object. We need to convert
391 # what file(1) prints into the canonical form.
392 return _elf_arch_to_canonical ($1);
393 } elsif ($file =~ /PE32 executable/) {
394 return "i386"; # Win32 executable or DLL
395 } elsif ($file =~ /PE32\+ executable/) {
396 return "x86_64"; # Win64 executable or DLL
399 elsif ($file =~ /cpio archive/) {
400 # Probably an initrd.
402 if ($file =~ /gzip/) {
404 } elsif ($file =~ /bzip2/) {
408 # Download and unpack it to find a binary file.
409 my $dir = tempdir (CLEANUP => 1);
410 $g->download ($path, "$dir/initrd");
412 my $bins = join " ", map { "bin/$_" } @_initrd_binaries;
413 my $cmd = "cd $dir && $zcat initrd | cpio --quiet -id $bins";
414 my $r = system ($cmd);
415 die __x("cpio command failed: {error}", error => $?)
418 foreach my $bin (@_initrd_binaries) {
419 if (-f "$dir/bin/$bin") {
420 $_ = `file $dir/bin/$bin`;
421 if (/ELF.*executable, (.+?),/) {
422 return _elf_arch_to_canonical ($1);
427 die __x("file_architecture: no known binaries found in initrd image: {path}",
431 die __x("file_architecture: unknown architecture: {path}",
435 =head1 OPERATING SYSTEM INSPECTION FUNCTIONS
437 The functions in this section can be used to inspect the operating
438 system(s) available inside a virtual machine image. For example, you
439 can find out if the VM is Linux or Windows, how the partitions are
440 meant to be mounted, and what applications are installed.
442 If you just want a simple command-line interface to this
443 functionality, use the L<virt-inspector(1)> tool. The documentation
444 below covers the case where you want to access this functionality from
447 Once you have the list of partitions (from C<get_partitions>) there
448 are several steps involved:
454 Look at each partition separately and find out what is on it.
456 The information you get back includes whether the partition contains a
457 filesystem or swapspace, what sort of filesystem (eg. ext3, ntfs), and
458 a first pass guess at the content of the filesystem (eg. Linux boot,
461 The result of this step is a C<%fs> hash of information, one hash for
464 See: C<inspect_partition>, C<inspect_all_partitions>
468 Work out the relationship between partitions.
470 In this step we work out how partitions are related to each other. In
471 the case of a single-boot VM, we work out how the partitions are
472 mounted in respect of each other (eg. C</dev/sda1> is mounted as
473 C</boot>). In the case of a multi-boot VM where there are several
474 roots, we may identify several operating system roots, and mountpoints
477 The result of this step is a single hash called C<%oses> which is
478 described in more detail below, but at the top level looks like:
481 '/dev/VG/Root1' => \%os1,
482 '/dev/VG/Root2' => \%os2,
488 '/' => '/dev/VG/Root1',
489 '/boot' => '/dev/sda1',
494 (example shows a multi-boot VM containing two root partitions).
496 See: C<inspect_operating_systems>
502 Previous to this point we've essentially been looking at each
503 partition in isolation. Now we construct a true guest filesystem by
504 mounting up all of the disks. Only once everything is mounted up can
505 we run commands in the OS context to do more detailed inspection.
507 See: C<mount_operating_system>
511 Check for kernels and applications.
513 This step now does more detailed inspection, where we can look for
514 kernels, applications and more installed in the guest.
516 The result of this is an enhanced C<%os> hash.
518 See: C<inspect_in_detail>
524 This library does not contain functions for generating output based on
525 the analysis steps above. Use a command line tool such as
526 L<virt-inspector(1)> to get useful output.
530 =head2 inspect_all_partitions
532 %fses = inspect_all_partitions ($g, \@partitions);
534 %fses = inspect_all_partitions ($g, \@partitions, use_windows_registry => 1);
536 This calls C<inspect_partition> for each partition in the list
539 The result is a hash which maps partition name to C<\%fs> hashref.
541 The contents of the C<%fs> hash and the meaning of the
542 C<use_windows_registry> flag are explained below.
546 # Turn /dev/vd* and /dev/hd* into canonical device names
547 # (see BLOCK DEVICE NAMING in guestfs(3)).
549 sub _canonical_dev ($)
552 return "/dev/sd$1" if $dev =~ m{^/dev/[vh]d(\w+)};
556 sub inspect_all_partitions
562 return map { _canonical_dev ($_) => inspect_partition ($g, $_, @_) } @parts;
565 =head2 inspect_partition
567 \%fs = inspect_partition ($g, $partition);
569 \%fs = inspect_partition ($g, $partition, use_windows_registry => 1);
571 This function inspects the device named C<$partition> in isolation and
572 tries to determine what it is. It returns information such as whether
573 the partition is formatted, and with what, whether it is mountable,
574 and what it appears to contain (eg. a Windows root, or a Linux /usr).
576 If C<use_windows_registry> is set to 1, then we will try to download
577 and parse the content of the Windows registry (for Windows root
578 devices). However since this is an expensive and error-prone
579 operation, we don't do this by default. It also requires the external
580 program C<reged>, patched to remove numerous crashing bugs in the
583 The returned value is a hashref C<\%fs> which may contain the
584 following top-level keys (any key can be missing):
590 Filesystem type, eg. "ext2" or "ntfs"
594 Apparent filesystem OS, eg. "linux" or "windows"
598 If set, the partition is a swap partition.
610 If set, the partition could be mounted by libguestfs.
614 Filesystem content, if we could determine it. One of: "linux-grub",
615 "linux-root", "linux-usrlocal", "linux-usr", "windows-root".
619 (For Linux root partitions only).
620 Operating system distribution. One of: "fedora", "rhel", "centos",
621 "scientific", "debian".
625 (For Linux root partitions only)
626 The package format used by the guest distribution. One of: "rpm", "dpkg".
628 =item package_management
630 (For Linux root partitions only)
631 The package management tool used by the guest distribution. One of: "rhn",
634 =item os_major_version
636 (For root partitions only).
637 Operating system major version number.
639 =item os_minor_version
641 (For root partitions only).
642 Operating system minor version number.
646 (For Linux root partitions only).
647 The contents of the C</etc/fstab> file.
651 (For Windows root partitions only).
652 The contents of the C</boot.ini> (NTLDR) file.
656 The value is an arrayref, which is a list of Windows registry
657 file contents, in Windows C<.REG> format.
663 sub inspect_partition
667 my $dev = shift; # LV or partition name.
670 my $use_windows_registry = $params{use_windows_registry};
672 my %r; # Result hash.
674 # First try 'file(1)' on it.
675 my $file = $g->file ($dev);
676 if ($file =~ /ext2 filesystem data/) {
679 } elsif ($file =~ /ext3 filesystem data/) {
682 } elsif ($file =~ /ext4 filesystem data/) {
685 } elsif ($file =~ m{Linux/i386 swap file}) {
691 # If it's ext2/3/4, then we want the UUID and label.
692 if (exists $r{fstype} && $r{fstype} =~ /^ext/) {
693 $r{uuid} = $g->get_e2uuid ($dev);
694 $r{label} = $g->get_e2label ($dev);
697 # Try mounting it, fnarrr.
699 $r{is_mountable} = 1;
700 eval { $g->mount_ro ($dev, "/") };
702 # It's not mountable, probably empty or some format
703 # we don't understand.
704 $r{is_mountable} = 0;
709 if ($g->is_file ("/grub/menu.lst") ||
710 $g->is_file ("/grub/grub.conf")) {
711 $r{content} = "linux-grub";
712 _check_grub ($g, \%r);
717 if ($g->is_dir ("/etc") && $g->is_dir ("/bin") &&
718 $g->is_file ("/etc/fstab")) {
719 $r{content} = "linux-root";
721 _check_linux_root ($g, \%r);
726 if ($g->is_dir ("/etc") && $g->is_dir ("/bin") &&
727 $g->is_dir ("/share") && !$g->exists ("/local") &&
728 !$g->is_file ("/etc/fstab")) {
729 $r{content} = "linux-usrlocal";
734 if ($g->is_dir ("/etc") && $g->is_dir ("/bin") &&
735 $g->is_dir ("/share") && $g->exists ("/local") &&
736 !$g->is_file ("/etc/fstab")) {
737 $r{content} = "linux-usr";
742 if ($g->is_file ("/AUTOEXEC.BAT") ||
743 $g->is_file ("/autoexec.bat") ||
744 $g->is_dir ("/Program Files") ||
745 $g->is_dir ("/WINDOWS") ||
746 $g->is_file ("/boot.ini") ||
747 $g->is_file ("/ntldr")) {
748 $r{fstype} = "ntfs"; # XXX this is a guess
749 $r{fsos} = "windows";
750 $r{content} = "windows-root";
752 _check_windows_root ($g, \%r, $use_windows_registry);
762 sub _check_linux_root
768 # Look into /etc to see if we recognise the operating system.
769 # N.B. don't use $g->is_file here, because it might be a symlink
770 if ($g->exists ("/etc/redhat-release")) {
771 $r->{package_format} = "rpm";
773 $_ = $g->cat ("/etc/redhat-release");
774 if (/Fedora release (\d+)(?:\.(\d+))?/) {
775 $r->{osdistro} = "fedora";
776 $r->{os_major_version} = "$1";
777 $r->{os_minor_version} = "$2" if(defined($2));
778 $r->{package_management} = "yum";
781 elsif (/(Red Hat Enterprise Linux|CentOS|Scientific Linux)/) {
784 if($distro eq "Red Hat Enterprise Linux") {
785 $r->{osdistro} = "rhel";
788 elsif($distro eq "CentOS") {
789 $r->{osdistro} = "centos";
790 $r->{package_management} = "yum";
793 elsif($distro eq "Scientific Linux") {
794 $r->{osdistro} = "scientific";
795 $r->{package_management} = "yum";
798 # Shouldn't be possible
801 if (/$distro.*release (\d+).*Update (\d+)/) {
802 $r->{os_major_version} = "$1";
803 $r->{os_minor_version} = "$2";
806 elsif (/$distro.*release (\d+)(?:\.(\d+))?/) {
807 $r->{os_major_version} = "$1";
810 $r->{os_minor_version} = "$2";
812 $r->{os_minor_version} = "0";
816 # Package management in RHEL changed in version 5
817 if ($r->{osdistro} eq "rhel") {
818 if ($r->{os_major_version} >= 5) {
819 $r->{package_management} = "yum";
821 $r->{package_management} = "rhn";
827 $r->{osdistro} = "redhat-based";
829 } elsif ($g->is_file ("/etc/debian_version")) {
830 $r->{package_format} = "dpkg";
831 $r->{package_management} = "apt";
833 $_ = $g->cat ("/etc/debian_version");
834 if (/(\d+)\.(\d+)/) {
835 $r->{osdistro} = "debian";
836 $r->{os_major_version} = "$1";
837 $r->{os_minor_version} = "$2";
839 $r->{osdistro} = "debian";
843 # Parse the contents of /etc/fstab. This is pretty vital so
844 # we can determine where filesystems are supposed to be mounted.
845 eval "\$_ = \$g->cat ('/etc/fstab');";
847 my @lines = split /\n/;
850 my @fields = split /[ \t]+/;
852 my $spec = $fields[0]; # first column (dev/label/uuid)
853 my $file = $fields[1]; # second column (mountpoint)
854 if ($spec =~ m{^/} ||
855 $spec =~ m{^LABEL=} ||
856 $spec =~ m{^UUID=} ||
858 push @fstab, [$spec, $file]
862 $r->{fstab} = \@fstab if @fstab;
865 # Determine the architecture of this root.
867 foreach ("/bin/bash", "/bin/ls", "/bin/echo", "/bin/rm", "/bin/sh") {
868 if ($g->is_file ($_)) {
869 $arch = file_architecture ($g, $_);
874 $r->{arch} = $arch if defined $arch;
877 # We only support NT. The control file /boot.ini contains a list of
878 # Windows installations and their %systemroot%s in a simple text
881 # XXX We could parse this better. This won't work if /boot.ini is on
882 # a different drive from the %systemroot%, and in other unusual cases.
884 sub _check_windows_root
889 my $use_windows_registry = shift;
891 my $boot_ini = resolve_windows_path ($g, "/boot.ini");
892 $r->{boot_ini} = $boot_ini;
894 if (defined $r->{boot_ini}) {
895 $_ = $g->cat ($boot_ini);
896 my @lines = split /\n/;
902 } elsif (m/^default=.*?\\(\w+)$/i) {
905 } elsif (m/\\(\w+)=/) {
911 if (defined $systemroot) {
912 $r->{systemroot} = resolve_windows_path ($g, "/$systemroot");
913 if (defined $r->{systemroot}) {
914 _check_windows_arch ($g, $r, $r->{systemroot});
915 if ($use_windows_registry) {
916 _check_windows_registry ($g, $r, $r->{systemroot});
923 # Find Windows userspace arch.
925 sub _check_windows_arch
930 my $systemroot = shift;
933 resolve_windows_path ($g, $r->{systemroot} . "/system32/cmd.exe");
934 $r->{arch} = file_architecture ($g, $cmd_exe) if $cmd_exe;
937 sub _check_windows_registry
942 my $systemroot = shift;
944 # Download the system registry files. Only download the
945 # interesting ones, and we don't bother with user profiles at all.
947 my $configdir = resolve_windows_path ($g, "$systemroot/system32/config");
948 if (defined $configdir) {
949 my $softwaredir = resolve_windows_path ($g, "$configdir/software");
950 if (defined $softwaredir) {
951 _load_windows_registry ($g, $r, $softwaredir,
952 "HKEY_LOCAL_MACHINE\\SOFTWARE");
954 my $systemdir = resolve_windows_path ($g, "$configdir/system");
955 if (defined $systemdir) {
956 _load_windows_registry ($g, $r, $systemdir,
957 "HKEY_LOCAL_MACHINE\\System");
962 sub _load_windows_registry
970 my $dir = tempdir (CLEANUP => 1);
972 $g->download ($regfile, "$dir/reg");
974 # 'reged' command is particularly noisy. Redirect stdout and
975 # stderr to /dev/null temporarily.
976 open SAVEOUT, ">&STDOUT";
977 open SAVEERR, ">&STDERR";
978 open STDOUT, ">/dev/null";
979 open STDERR, ">/dev/null";
981 my @cmd = ("reged", "-x", "$dir/reg", "$prefix", "\\", "$dir/out");
982 my $res = system (@cmd);
986 open STDOUT, ">&SAVEOUT";
987 open STDERR, ">&SAVEERR";
992 warn __x("reged command failed: {errormsg}", errormsg => $?);
996 # Some versions of reged segfault on inputs. If that happens we
997 # may get no / partial output file. Anyway, if it exists, load
1000 unless (open F, "$dir/out") {
1001 warn __x("no output from reged command: {errormsg}", errormsg => $!);
1004 { local $/ = undef; $content = <F>; }
1008 @registry = @{$r->{registry}} if exists $r->{registry};
1009 push @registry, $content;
1010 $r->{registry} = \@registry;
1019 # Grub version, if we care.
1022 =head2 inspect_operating_systems
1024 \%oses = inspect_operating_systems ($g, \%fses);
1026 This function works out how partitions are related to each other. In
1027 the case of a single-boot VM, we work out how the partitions are
1028 mounted in respect of each other (eg. C</dev/sda1> is mounted as
1029 C</boot>). In the case of a multi-boot VM where there are several
1030 roots, we may identify several operating system roots, and mountpoints
1033 This function returns a hashref C<\%oses> which at the top level looks
1037 '/dev/VG/Root' => \%os,
1040 (There can be multiple roots for a multi-boot VM).
1042 The C<\%os> hash contains the following keys (any can be omitted):
1048 Operating system type, eg. "linux", "windows".
1052 Operating system userspace architecture, eg. "i386", "x86_64".
1056 Operating system distribution, eg. "debian".
1060 Operating system major version, eg. "4".
1064 Operating system minor version, eg "3".
1068 The value is a reference to the root partition C<%fs> hash.
1072 The value is the name of the root partition (as a string).
1077 The value is a hashref like this:
1080 '/' => '/dev/VG/Root',
1081 '/boot' => '/dev/sda1',
1086 Filesystems (including swap devices and unmounted partitions).
1087 The value is a hashref like this:
1090 '/dev/sda1' => \%fs,
1091 '/dev/VG/Root' => \%fs,
1092 '/dev/VG/Swap' => \%fs,
1099 sub inspect_operating_systems
1107 foreach (sort keys %$fses) {
1108 if ($fses->{$_}->{is_root}) {
1110 root => $fses->{$_},
1113 _get_os_version ($g, \%r);
1114 _assign_mount_points ($g, $fses, \%r);
1128 $r->{os} = $r->{root}->{fsos} if exists $r->{root}->{fsos};
1129 $r->{distro} = $r->{root}->{osdistro} if exists $r->{root}->{osdistro};
1130 $r->{major_version} = $r->{root}->{os_major_version}
1131 if exists $r->{root}->{os_major_version};
1132 $r->{minor_version} = $r->{root}->{os_minor_version}
1133 if exists $r->{root}->{os_minor_version};
1134 $r->{package_format} = $r->{root}->{package_format}
1135 if exists $r->{root}->{package_format};
1136 $r->{package_management} = $r->{root}->{package_management}
1137 if exists $r->{root}->{package_management};
1138 $r->{arch} = $r->{root}->{arch} if exists $r->{root}->{arch};
1141 sub _assign_mount_points
1148 $r->{mounts} = { "/" => $r->{root_device} };
1149 $r->{filesystems} = { $r->{root_device} => $r->{root} };
1151 # Use /etc/fstab if we have it to mount the rest.
1152 if (exists $r->{root}->{fstab}) {
1153 my @fstab = @{$r->{root}->{fstab}};
1155 my ($spec, $file) = @$_;
1157 my ($dev, $fs) = _find_filesystem ($g, $fses, $spec);
1159 $r->{mounts}->{$file} = $dev;
1160 $r->{filesystems}->{$dev} = $fs;
1161 if (exists $fs->{used}) {
1166 $fs->{spec} = $spec;
1172 # Find filesystem by device name, LABEL=.. or UUID=..
1173 sub _find_filesystem
1179 if (/^LABEL=(.*)/) {
1181 foreach (sort keys %$fses) {
1182 if (exists $fses->{$_}->{label} &&
1183 $fses->{$_}->{label} eq $label) {
1184 return ($_, $fses->{$_});
1187 warn __x("unknown filesystem label {label}\n", label => $label);
1189 } elsif (/^UUID=(.*)/) {
1191 foreach (sort keys %$fses) {
1192 if (exists $fses->{$_}->{uuid} &&
1193 $fses->{$_}->{uuid} eq $uuid) {
1194 return ($_, $fses->{$_});
1197 warn __x("unknown filesystem UUID {uuid}\n", uuid => $uuid);
1200 return ($_, $fses->{$_}) if exists $fses->{$_};
1202 # The following is to handle the case where an fstab entry specifies a
1203 # specific device rather than its label or uuid, and the libguestfs
1204 # appliance has named the device differently due to the use of a
1206 # This will work as long as the underlying drivers recognise devices in
1208 if (m{^/dev/hd(.*)} && exists $fses->{"/dev/sd$1"}) {
1209 return ("/dev/sd$1", $fses->{"/dev/sd$1"});
1211 if (m{^/dev/xvd(.*)} && exists $fses->{"/dev/sd$1"}) {
1212 return ("/dev/sd$1", $fses->{"/dev/sd$1"});
1214 if (m{^/dev/mapper/(.*)-(.*)$} && exists $fses->{"/dev/$1/$2"}) {
1215 return ("/dev/$1/$2", $fses->{"/dev/$1/$2"});
1218 return () if m{/dev/cdrom};
1220 warn __x("unknown filesystem {fs}\n", fs => $_);
1225 =head2 mount_operating_system
1227 mount_operating_system ($g, \%os, [$ro]);
1229 This function mounts the operating system described in the
1230 C<%os> hash according to the C<mounts> table in that hash (see
1231 C<inspect_operating_systems>).
1233 The partitions are mounted read-only unless the third parameter
1234 is specified as zero explicitly.
1236 To reverse the effect of this call, use the standard
1237 libguestfs API call C<$g-E<gt>umount_all ()>.
1241 sub mount_operating_system
1246 my $ro = shift; # Read-only?
1248 $ro = 1 unless defined $ro; # ro defaults to 1 if unspecified
1250 my $mounts = $os->{mounts};
1252 # Have to mount / first. Luckily '/' is early in the ASCII
1253 # character set, so this should be OK.
1254 foreach (sort keys %$mounts) {
1255 if($_ ne "swap" && $_ ne "none" && ($_ eq '/' || $g->is_dir ($_))) {
1257 $g->mount_ro ($mounts->{$_}, $_)
1259 $g->mount ($mounts->{$_}, $_)
1265 =head2 inspect_in_detail
1267 mount_operating_system ($g, \%os);
1268 inspect_in_detail ($g, \%os);
1271 The C<inspect_in_detail> function inspects the mounted operating
1272 system for installed applications, installed kernels, kernel modules,
1273 system architecture, and more.
1275 It adds extra keys to the existing C<%os> hash reflecting what it
1276 finds. These extra keys are:
1282 List of applications.
1286 Boot configurations. A hash containing:
1292 An array of boot configurations. Each array entry is a hash containing:
1298 A reference to the expanded initrd structure (see below) for the initrd used by
1299 this boot configuration.
1303 A reference to the expanded kernel structure (see below) for the kernel used by
1304 this boot configuration.
1308 The human readable name of the configuration.
1312 The kernel command line.
1318 The index of the default configuration in the configs array.
1322 The path of the filesystem containing the grub partition.
1330 This is a hash of kernel version =E<gt> a hash with the following keys:
1340 Kernel architecture (eg. C<x86-64>).
1348 The path to the kernel's vmlinuz file.
1352 If the kernel was installed in a package, the name of that package.
1356 =item modprobe_aliases
1359 The contents of the modprobe configuration.
1361 =item initrd_modules
1364 The kernel modules installed in the initrd. The value is
1365 a hashref of kernel version to list of modules.
1371 sub inspect_in_detail
1377 _check_for_applications ($g, $os);
1378 _check_for_kernels ($g, $os);
1379 if ($os->{os} eq "linux") {
1380 _find_modprobe_aliases ($g, $os);
1384 sub _check_for_applications
1392 my $osn = $os->{os};
1393 if ($osn eq "linux") {
1394 my $package_format = $os->{package_format};
1395 if (defined $package_format && $package_format eq "rpm") {
1396 my @lines = $g->command_lines
1399 "--qf", "%{name} %{epoch} %{version} %{release} %{arch}\n"]);
1401 if (m/^(.*) (.*) (.*) (.*) (.*)$/) {
1403 $epoch = "" if $epoch eq "(none)";
1415 } elsif ($osn eq "windows") {
1417 # I worked out a general plan for this, but haven't
1418 # implemented it yet. We can iterate over /Program Files
1419 # looking for *.EXE files, which we download, then use
1420 # i686-pc-mingw32-windres on, to find the VERSIONINFO
1421 # section, which has a lot of useful information.
1424 $os->{apps} = \@apps;
1427 # Find the path which needs to be prepended to paths in grub.conf to make them
1429 sub _find_grub_prefix
1433 my $fses = $os->{filesystems};
1434 die("filesystems undefined") unless(defined($fses));
1436 # Look for the filesystem which contains grub
1438 foreach my $dev (keys(%$fses)) {
1439 my $fsinfo = $fses->{$dev};
1440 if(exists($fsinfo->{content}) && $fsinfo->{content} eq "linux-grub") {
1446 my $mounts = $os->{mounts};
1447 die("mounts undefined") unless(defined($mounts));
1449 # Find where the filesystem is mounted
1450 if(defined($grubdev)) {
1451 foreach my $mount (keys(%$mounts)) {
1452 if($mounts->{$mount} eq $grubdev) {
1453 return "" if($mount eq '/');
1458 die("$grubdev defined in filesystems, but not in mounts");
1461 # If we didn't find it, look for /boot/grub/menu.lst, then try to work out
1462 # what filesystem it's on. We use menu.lst rather than grub.conf because
1463 # debian only uses menu.lst, and anaconda creates a symlink for it.
1464 die(__"Can't find grub on guest") unless($g->exists('/boot/grub/menu.lst'));
1466 # Look for the most specific mount point in mounts
1467 foreach my $path qw(/boot/grub /boot /) {
1468 if(exists($mounts->{$path})) {
1469 return "" if($path eq '/');
1474 die("Couldn't determine which filesystem holds /boot/grub/menu.lst");
1477 sub _check_for_kernels
1481 if ($os->{os} eq "linux") {
1482 # Iterate over entries in grub.conf, populating $os->{boot}
1483 # For every kernel we find, inspect it and add to $os->{kernels}
1485 my $grub = _find_grub_prefix($g, $os);
1493 # ->{title} = "Fedora (2.6.29.6-213.fc11.i686.PAE)"
1494 # ->{kernel} = \kernel
1495 # ->{cmdline} = "ro root=/dev/mapper/vg_mbooth-lv_root rhgb"
1496 # ->{initrd} = \initrd
1497 # ->{default} = \config
1498 # ->{grub_fs} = "/boot"
1500 $g->aug_init("/", 16);
1503 # Get all configurations from grub
1504 foreach my $bootable
1505 ($g->aug_match("/files/etc/grub.conf/title"))
1508 $config{title} = $g->aug_get($bootable);
1511 eval { $grub_kernel = $g->aug_get("$bootable/kernel"); };
1513 warn __x("Grub entry {title} has no kernel",
1514 title => $config{title});
1517 # Check we've got a kernel entry
1518 if(defined($grub_kernel)) {
1519 my $path = "$grub$grub_kernel";
1521 # Reconstruct the kernel command line
1523 foreach my $arg ($g->aug_match("$bootable/kernel/*")) {
1524 $arg =~ m{/kernel/([^/]*)$}
1525 or die("Unexpected return from aug_match: $arg");
1529 eval { $value = $g->aug_get($arg); };
1531 if(defined($value)) {
1532 push(@args, "$name=$value");
1537 $config{cmdline} = join(' ', @args) if(scalar(@args) > 0);
1540 inspect_linux_kernel($g, $path, $os->{package_format});
1542 # Check the kernel was recognised
1543 if(defined($kernel)) {
1544 # Put this kernel on the top level kernel list
1545 $os->{kernels} ||= [];
1546 push(@{$os->{kernels}}, $kernel);
1548 $config{kernel} = $kernel;
1550 # Look for an initrd entry
1553 $initrd = $g->aug_get("$bootable/initrd");
1558 _inspect_initrd($g, $os, "$grub$initrd",
1559 $kernel->{version});
1561 warn __x("Grub entry {title} does not specify an ".
1562 "initrd", title => $config{title});
1567 push(@configs, \%config);
1571 # Create the top level boot entry
1573 $boot{configs} = \@configs;
1574 $boot{grub_fs} = $grub;
1576 # Add the default configuration
1578 $boot{default} = $g->aug_get("/files/etc/grub.conf/default");
1581 warn __"No grub default specified";
1584 $os->{boot} = \%boot;
1587 elsif ($os->{os} eq "windows") {
1592 =head2 inspect_linux_kernel
1594 my $kernel_hash = inspect_linux_kernel($g, $vmlinuz_path, $package_format);
1596 inspect_linux_kernel returns a hash describing the target linux kernel. For the
1597 contents of the hash, see the I<kernels> structure described under
1598 L</inspect_in_detail>.
1602 sub inspect_linux_kernel
1604 my ($g, $path, $package_format) = @_;
1608 $kernel{path} = $path;
1610 # If this is a packaged kernel, try to work out the name of the package
1611 # which installed it. This lets us know what to install to replace it with,
1612 # e.g. kernel, kernel-smp, kernel-hugemem, kernel-PAE
1613 if($package_format eq "rpm") {
1615 eval { $package = $g->command(['rpm', '-qf', '--qf',
1616 '%{NAME}', $path]); };
1617 $kernel{package} = $package if defined($package);;
1620 # Try to get the kernel version by running file against it
1622 my $filedesc = $g->file($path);
1623 if($filedesc =~ /^$path: Linux kernel .*\bversion\s+(\S+)\b/) {
1627 # Sometimes file can't work out the kernel version, for example because it's
1628 # a Xen PV kernel. In this case try to guess the version from the filename
1630 if($path =~ m{/boot/vmlinuz-(.*)}) {
1633 # Check /lib/modules/$version exists
1634 if(!$g->is_dir("/lib/modules/$version")) {
1635 warn __x("Didn't find modules directory {modules} for kernel ".
1636 "{path}", modules => "/lib/modules/$version",
1643 warn __x("Couldn't guess kernel version number from path for ".
1644 "kernel {path}", path => $path);
1651 $kernel{version} = $version;
1656 my $prefix = "/lib/modules/$version";
1657 foreach my $module ($g->find ($prefix)) {
1658 if ($module =~ m{/([^/]+)\.(?:ko|o)$}) {
1659 $any_module = "$prefix$module" unless defined $any_module;
1664 $kernel{modules} = \@modules;
1666 # Determine kernel architecture by looking at the arch
1667 # of any kernel module.
1668 $kernel{arch} = file_architecture ($g, $any_module);
1673 # Find all modprobe aliases. Specifically, this looks in the following
1675 # * /etc/conf.modules
1676 # * /etc/modules.conf
1677 # * /etc/modprobe.conf
1678 # * /etc/modprobe.d/*
1680 sub _find_modprobe_aliases
1687 $g->aug_init("/", 16);
1689 # Register additional paths to the Modprobe lens
1690 $g->aug_set("/augeas/load/Modprobe/incl[last()+1]", "/etc/modules.conf");
1691 $g->aug_set("/augeas/load/Modprobe/incl[last()+1]", "/etc/conf.modules");
1693 # Make augeas reload
1696 my %modprobe_aliases;
1698 for my $pattern qw(/files/etc/conf.modules/alias
1699 /files/etc/modules.conf/alias
1700 /files/etc/modprobe.conf/alias
1701 /files/etc/modprobe.d/*/alias) {
1702 for my $path ( $g->aug_match($pattern) ) {
1703 $path =~ m{^/files(.*)/alias(?:\[\d*\])?$}
1704 or die __x("{path} doesn't match augeas pattern",
1709 $alias = $g->aug_get($path);
1712 $modulename = $g->aug_get($path.'/modulename');
1715 $aliasinfo{modulename} = $modulename;
1716 $aliasinfo{augeas} = $path;
1717 $aliasinfo{file} = $file;
1719 $modprobe_aliases{$alias} = \%aliasinfo;
1723 $os->{modprobe_aliases} = \%modprobe_aliases;
1726 # Get a listing of device drivers from an initrd
1729 my ($g, $os, $path, $version) = @_;
1733 # Disregard old-style compressed ext2 files and only work with real
1734 # compressed cpio files, since cpio takes ages to (fail to) process anything
1736 if ($g->file ($path) =~ /cpio/) {
1738 @modules = $g->initrd_list ($path);
1741 @modules = grep { m{([^/]+)\.(?:ko|o)$} } @modules;
1743 warn __x("{filename}: could not read initrd format",
1744 filename => "$path");
1748 # Add to the top level initrd_modules entry
1749 $os->{initrd_modules} ||= {};
1750 $os->{initrd_modules}->{$version} = \@modules;
1759 Copyright (C) 2009 Red Hat Inc.
1763 Please see the file COPYING.LIB for the full license.
1767 L<virt-inspector(1)>,
1770 L<http://libguestfs.org/>,
1772 L<http://libvirt.org/>,