3 # Copyright (C) 2009 Red Hat Inc.
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 use Sys::Guestfs::Lib qw(open_guest get_partitions resolve_windows_path);
27 use File::Temp qw/tempdir/;
31 eval "use YAML::Any;";
37 virt-inspector - Display OS version, kernel, drivers, mount points, applications, etc. in a virtual machine
41 virt-inspector [--connect URI] domname
43 virt-inspector guest.img [guest.img ...]
47 B<virt-inspector> examines a virtual machine and tries to determine
48 the version of the OS, the kernel version, what drivers are installed,
49 whether the virtual machine is fully virtualized (FV) or
50 para-virtualized (PV), what applications are installed and more.
52 Virt-inspector can produce output in several formats, including a
53 readable text report, and XML for feeding into other programs.
55 Virt-inspector should only be run on I<inactive> virtual machines.
56 The program tries to determine that the machine is inactive and will
57 refuse to run if it thinks you are trying to inspect a running domain.
59 In the normal usage, use C<virt-inspector domname> where C<domname> is
60 the libvirt domain (see: C<virsh list --all>).
62 You can also run virt-inspector directly on disk images from a single
63 virtual machine. Use C<virt-inspector guest.img>. In rare cases a
64 domain has several block devices, in which case you should list them
65 one after another, with the first corresponding to the guest's
66 C</dev/sda>, the second to the guest's C</dev/sdb> and so on.
68 Virt-inspector can only inspect and report upon I<one domain at a
69 time>. To inspect several virtual machines, you have to run
70 virt-inspector several times (for example, from a shell script
73 Because virt-inspector needs direct access to guest images, it won't
74 normally work over remote libvirt connections.
92 =item B<--connect URI> | B<-c URI>
94 If using libvirt, connect to the given I<URI>. If omitted,
95 then we connect to the default libvirt hypervisor.
97 Libvirt is only used if you specify a C<domname> on the
98 command line. If you specify guest block devices directly,
99 then libvirt is not used at all.
107 Force reading a particular guest even if it appears to be active. In
108 earlier versions of virt-inspector, this could be dangerous (for
109 example, corrupting the guest's disk image). However in more recent
110 versions, it should not cause corruption, but might cause
111 virt-inspector to crash or produce incorrect results.
119 The following options select the output format. Use only one of them.
120 The default is a readable text report.
124 =item B<--text> (default)
130 Produce no output at all.
134 If you select I<--xml> then you get XML output which can be fed
139 If you select I<--yaml> then you get YAML output which can be fed
144 If you select I<--perl> then you get Perl structures output which
145 can be used directly in another Perl program.
151 If you select I<--fish> then we print a L<guestfish(1)> command
152 line which will automatically mount up the filesystems on the
153 correct mount points. Try this for example:
155 guestfish $(virt-inspector --fish guest.img)
157 I<--ro-fish> is the same, but the I<--ro> option is passed to
158 guestfish so that the filesystems are mounted read-only.
162 In "query mode" we answer common questions about the guest, such
163 as whether it is fullvirt or needs a Xen hypervisor to run.
165 See section I<QUERY MODE> below.
169 my $windows_registry;
171 =item B<--windows-registry>
173 If this item is passed, I<and> the guest is Windows, I<and> the
174 external program C<reged> is available (see SEE ALSO section), then we
175 attempt to parse the Windows registry. This allows much more
176 information to be gathered for Windows guests.
178 This is quite an expensive and slow operation, so we don't do it by
185 GetOptions ("help|?" => \$help,
186 "connect|c=s" => \$uri,
188 "text" => sub { $output = "text" },
189 "none" => sub { $output = "none" },
190 "xml" => sub { $output = "xml" },
191 "yaml" => sub { $output = "yaml" },
192 "perl" => sub { $output = "perl" },
193 "fish" => sub { $output = "fish" },
194 "guestfish" => sub { $output = "fish" },
195 "ro-fish" => sub { $output = "ro-fish" },
196 "ro-guestfish" => sub { $output = "ro-fish" },
197 "query" => sub { $output = "query" },
198 "windows-registry" => \$windows_registry,
200 pod2usage (1) if $help;
201 pod2usage ("$0: no image or VM names given") if @ARGV == 0;
204 $rw = 1 if $output eq "fish";
207 $g = open_guest (\@ARGV, rw => $rw, address => $uri);
209 $g = open_guest (\@ARGV, rw => $rw);
219 Linux (distro + version)
223 +--- Filesystems ---------- Installed apps --- Kernel & drivers
224 ----------- -------------- ----------------
225 mount point => device List of apps Extra information
226 mount point => device and versions about kernel(s)
229 (plus lots of extra information
230 about each filesystem)
232 The output of virt-inspector is a complex two-level data structure.
234 At the top level is a list of the operating systems installed on the
235 guest. (For the vast majority of guests, only a single OS is
236 installed.) The data returned for the OS includes the name (Linux,
237 Windows), the distribution and version.
239 The diagram above shows what we return for each OS.
241 With the I<--xml> option the output is mapped into an XML document.
242 Unfortunately there is no clear schema for this document
243 (contributions welcome) but you can get an idea of the format by
244 looking at other documents and as a last resort the source for this
247 With the I<--fish> or I<--ro-fish> option the mount points are mapped to
248 L<guestfish(1)> command line parameters, so that you can go in
249 afterwards and inspect the guest with everything mounted in the
250 right place. For example:
252 guestfish $(virt-inspector --ro-fish guest.img)
253 ==> guestfish --ro -a guest.img -m /dev/VG/LV:/ -m /dev/sda1:/boot
257 # List of possible filesystems.
258 my @devices = get_partitions ($g);
260 # Now query each one to build up a picture of what's in it.
261 my %fses = map { $_ => check_fs ($_) } @devices;
263 # Now the complex checking code itself.
264 # check_fs takes a device name (LV or partition name) and returns
265 # a hashref containing everything we can find out about the device.
268 my $dev = shift; # LV or partition name.
270 my %r; # Result hash.
272 # First try 'file(1)' on it.
273 my $file = $g->file ($dev);
274 if ($file =~ /ext2 filesystem data/) {
277 } elsif ($file =~ /ext3 filesystem data/) {
280 } elsif ($file =~ /ext4 filesystem data/) {
283 } elsif ($file =~ m{Linux/i386 swap file}) {
289 # If it's ext2/3/4, then we want the UUID and label.
290 if (exists $r{fstype} && $r{fstype} =~ /^ext/) {
291 $r{uuid} = $g->get_e2uuid ($dev);
292 $r{label} = $g->get_e2label ($dev);
295 # Try mounting it, fnarrr.
297 $r{is_mountable} = 1;
298 eval { $g->mount_ro ($dev, "/") };
300 # It's not mountable, probably empty or some format
301 # we don't understand.
302 $r{is_mountable} = 0;
307 if ($g->is_file ("/grub/menu.lst") ||
308 $g->is_file ("/grub/grub.conf")) {
309 $r{content} = "linux-grub";
315 if ($g->is_dir ("/etc") && $g->is_dir ("/bin") &&
316 $g->is_file ("/etc/fstab")) {
317 $r{content} = "linux-root";
319 check_linux_root (\%r);
324 if ($g->is_dir ("/etc") && $g->is_dir ("/bin") &&
325 $g->is_dir ("/share") && !$g->exists ("/local") &&
326 !$g->is_file ("/etc/fstab")) {
327 $r{content} = "linux-usrlocal";
332 if ($g->is_dir ("/etc") && $g->is_dir ("/bin") &&
333 $g->is_dir ("/share") && $g->exists ("/local") &&
334 !$g->is_file ("/etc/fstab")) {
335 $r{content} = "linux-usr";
340 if ($g->is_file ("/AUTOEXEC.BAT") ||
341 $g->is_file ("/autoexec.bat") ||
342 $g->is_dir ("/Program Files") ||
343 $g->is_dir ("/WINDOWS") ||
344 $g->is_file ("/boot.ini") ||
345 $g->is_file ("/ntldr")) {
346 $r{fstype} = "ntfs"; # XXX this is a guess
347 $r{fsos} = "windows";
348 $r{content} = "windows-root";
350 check_windows_root (\%r);
365 # Look into /etc to see if we recognise the operating system.
366 if ($g->is_file ("/etc/redhat-release")) {
367 $_ = $g->cat ("/etc/redhat-release");
368 if (/Fedora release (\d+\.\d+)/) {
369 $r->{osdistro} = "fedora";
370 $r->{osversion} = "$1"
371 } elsif (/(Red Hat Enterprise Linux|CentOS|Scientific Linux).*release (\d+).*Update (\d+)/) {
372 $r->{osdistro} = "redhat";
373 $r->{osversion} = "$2.$3";
374 } elsif (/(Red Hat Enterprise Linux|CentOS|Scientific Linux).*release (\d+(?:\.(\d+))?)/) {
375 $r->{osdistro} = "redhat";
376 $r->{osversion} = "$2";
378 $r->{osdistro} = "redhat";
380 } elsif ($g->is_file ("/etc/debian_version")) {
381 $_ = $g->cat ("/etc/debian_version");
383 $r->{osdistro} = "debian";
384 $r->{osversion} = "$1";
386 $r->{osdistro} = "debian";
390 # Parse the contents of /etc/fstab. This is pretty vital so
391 # we can determine where filesystems are supposed to be mounted.
392 eval "\$_ = \$g->cat ('/etc/fstab');";
394 my @lines = split /\n/;
397 my @fields = split /[ \t]+/;
399 my $spec = $fields[0]; # first column (dev/label/uuid)
400 my $file = $fields[1]; # second column (mountpoint)
401 if ($spec =~ m{^/} ||
402 $spec =~ m{^LABEL=} ||
403 $spec =~ m{^UUID=} ||
405 push @fstab, [$spec, $file]
409 $r->{fstab} = \@fstab if @fstab;
413 # We only support NT. The control file /boot.ini contains a list of
414 # Windows installations and their %systemroot%s in a simple text
417 # XXX We could parse this better. This won't work if /boot.ini is on
418 # a different drive from the %systemroot%, and in other unusual cases.
420 sub check_windows_root
425 my $boot_ini = resolve_windows_path ($g, "/boot.ini");
426 $r->{boot_ini} = $boot_ini;
428 if (defined $r->{boot_ini}) {
429 $_ = $g->cat ($boot_ini);
430 my @lines = split /\n/;
436 } elsif (m/^default=.*?\\(\w+)$/i) {
439 } elsif (m/\\(\w+)=/) {
445 if (defined $systemroot) {
446 $r->{systemroot} = resolve_windows_path ($g, "/$systemroot");
447 if (defined $r->{systemroot} && $windows_registry) {
448 check_windows_registry ($r, $r->{systemroot});
454 sub check_windows_registry
458 my $systemroot = shift;
460 # Download the system registry files. Only download the
461 # interesting ones, and we don't bother with user profiles at all.
463 my $configdir = resolve_windows_path ($g, "$systemroot/system32/config");
464 if (defined $configdir) {
465 my $softwaredir = resolve_windows_path ($g, "$configdir/software");
466 if (defined $softwaredir) {
467 load_windows_registry ($r, $softwaredir,
468 "HKEY_LOCAL_MACHINE\\SOFTWARE");
470 my $systemdir = resolve_windows_path ($g, "$configdir/system");
471 if (defined $systemdir) {
472 load_windows_registry ($r, $systemdir,
473 "HKEY_LOCAL_MACHINE\\System");
478 sub load_windows_registry
485 my $dir = tempdir (CLEANUP => 1);
487 $g->download ($regfile, "$dir/reg");
489 # 'reged' command is particularly noisy. Redirect stdout and
490 # stderr to /dev/null temporarily.
491 open SAVEOUT, ">&STDOUT";
492 open SAVEERR, ">&STDERR";
493 open STDOUT, ">/dev/null";
494 open STDERR, ">/dev/null";
496 my @cmd = ("reged", "-x", "$dir/reg", "$prefix", "\\", "$dir/out");
497 my $res = system (@cmd);
501 open STDOUT, ">&SAVEOUT";
502 open STDERR, ">&SAVEERR";
507 warn "reged command failed: $?";
511 # Some versions of reged segfault on inputs. If that happens we
512 # may get no / partial output file. Anyway, if it exists, load
515 unless (open F, "$dir/out") {
516 warn "no output from reged command: $!";
519 { local $/ = undef; $content = <F>; }
523 @registry = @{$r->{registry}} if exists $r->{registry};
524 push @registry, $content;
525 $r->{registry} = \@registry;
533 # Grub version, if we care.
536 #print Dumper (\%fses);
538 #----------------------------------------------------------------------
539 # Now find out how many operating systems we've got. Usually just one.
543 foreach (sort keys %fses) {
544 if ($fses{$_}->{is_root}) {
549 get_os_version (\%r);
550 assign_mount_points (\%r);
560 $r->{os} = $r->{root}->{fsos} if exists $r->{root}->{fsos};
561 $r->{distro} = $r->{root}->{osdistro} if exists $r->{root}->{osdistro};
562 $r->{version} = $r->{root}->{osversion} if exists $r->{root}->{osversion};
565 sub assign_mount_points
570 $r->{mounts} = { "/" => $r->{root_device} };
571 $r->{filesystems} = { $r->{root_device} => $r->{root} };
573 # Use /etc/fstab if we have it to mount the rest.
574 if (exists $r->{root}->{fstab}) {
575 my @fstab = @{$r->{root}->{fstab}};
577 my ($spec, $file) = @$_;
579 my ($dev, $fs) = find_filesystem ($spec);
581 $r->{mounts}->{$file} = $dev;
582 $r->{filesystems}->{$dev} = $fs;
583 if (exists $fs->{used}) {
594 # Find filesystem by device name, LABEL=.. or UUID=..
601 foreach (sort keys %fses) {
602 if (exists $fses{$_}->{label} &&
603 $fses{$_}->{label} eq $label) {
604 return ($_, $fses{$_});
607 warn "unknown filesystem label $label\n";
609 } elsif (/^UUID=(.*)/) {
611 foreach (sort keys %fses) {
612 if (exists $fses{$_}->{uuid} &&
613 $fses{$_}->{uuid} eq $uuid) {
614 return ($_, $fses{$_});
617 warn "unknown filesystem UUID $uuid\n";
620 return ($_, $fses{$_}) if exists $fses{$_};
622 # The following is to handle the case where an fstab entry specifies a
623 # specific device rather than its label or uuid, and the libguestfs
624 # appliance has named the device differently due to the use of a
626 # This will work as long as the underlying drivers recognise devices in
628 if (m{^/dev/hd(.*)} && exists $fses{"/dev/sd$1"}) {
629 return ("/dev/sd$1", $fses{"/dev/sd$1"});
631 if (m{^/dev/xvd(.*)} && exists $fses{"/dev/sd$1"}) {
632 return ("/dev/sd$1", $fses{"/dev/sd$1"});
635 return () if m{/dev/cdrom};
637 warn "unknown filesystem $_\n";
642 #print Dumper(\%oses);
644 #----------------------------------------------------------------------
645 # Mount up the disks so we can check for applications
646 # and kernels. Skip this if the output is "*fish" because
647 # we don't need to know.
649 if ($output !~ /.*fish$/) {
651 foreach $root_dev (sort keys %oses) {
652 my $mounts = $oses{$root_dev}->{mounts};
653 # Have to mount / first. Luckily '/' is early in the ASCII
654 # character set, so this should be OK.
655 foreach (sort keys %$mounts) {
656 $g->mount_ro ($mounts->{$_}, $_)
657 if $_ ne "swap" && ($_ eq '/' || $g->is_dir ($_));
660 check_for_applications ($root_dev);
661 check_for_kernels ($root_dev);
662 if ($oses{$root_dev}->{os} eq "linux") {
663 check_for_modprobe_aliases ($root_dev);
664 check_for_initrd ($root_dev);
671 sub check_for_applications
674 my $root_dev = shift;
678 my $os = $oses{$root_dev}->{os};
679 if ($os eq "linux") {
680 my $distro = $oses{$root_dev}->{distro};
681 if (defined $distro && ($distro eq "redhat" || $distro eq "fedora")) {
682 my @lines = $g->command_lines
685 "--qf", "%{name} %{epoch} %{version} %{release} %{arch}\n"]);
687 if (m/^(.*) (.*) (.*) (.*) (.*)$/) {
689 $epoch = "" if $epoch eq "(none)";
701 } elsif ($os eq "windows") {
703 # I worked out a general plan for this, but haven't
704 # implemented it yet. We can iterate over /Program Files
705 # looking for *.EXE files, which we download, then use
706 # i686-pc-mingw32-windres on, to find the VERSIONINFO
707 # section, which has a lot of useful information.
710 $oses{$root_dev}->{apps} = \@apps;
713 sub check_for_kernels
716 my $root_dev = shift;
720 my $os = $oses{$root_dev}->{os};
721 if ($os eq "linux") {
722 # Installed kernels will have a corresponding /lib/modules/<version>
723 # directory, which is the easiest way to find out what kernels
724 # are installed, and what modules are available.
725 foreach ($g->ls ("/lib/modules")) {
726 if ($g->is_dir ("/lib/modules/$_")) {
728 $kernel{version} = $_;
732 foreach ($g->find ("/lib/modules/$_")) {
733 if (m,/([^/]+)\.ko$, || m,([^/]+)\.o$,) {
738 $kernel{modules} = \@modules;
740 push @kernels, \%kernel;
744 } elsif ($os eq "windows") {
748 $oses{$root_dev}->{kernels} = \@kernels;
751 # Check /etc/modprobe.conf to see if there are any specified
752 # drivers associated with network (ethX) or hard drives. Normally
753 # one might find something like:
756 # alias scsi_hostadapter xenblk
758 # XXX This doesn't look beyond /etc/modprobe.conf, eg. in /etc/modprobe.d/
760 sub check_for_modprobe_aliases
763 my $root_dev = shift;
767 $success = $g->aug_init("/", 16);
769 # Register /etc/modules.conf and /etc/conf.modules to the Modprobe lens
771 @results = $g->aug_match("/augeas/load/Modprobe/incl");
773 # Calculate the next index of /augeas/load/Modprobe/incl
775 foreach ( @results ) {
776 next unless m{/augeas/load/Modprobe/incl\[(\d*)]};
777 $i = $1 + 1 if ($1 == $i);
780 $success = $g->aug_set("/augeas/load/Modprobe/incl[$i]",
781 "/etc/modules.conf");
783 $success = $g->aug_set("/augeas/load/Modprobe/incl[$i]",
784 "/etc/conf.modules");
787 $success = $g->aug_load();
789 my %modprobe_aliases;
791 for my $pattern qw(/files/etc/conf.modules/alias
792 /files/etc/modules.conf/alias
793 /files/etc/modprobe.conf/alias
794 /files/etc/modprobe.d/*/alias) {
795 @results = $g->aug_match($pattern);
797 for my $path ( @results ) {
798 $path =~ m{^/files(.*)/alias(?:\[\d*\])?$}
799 or die("$path doesn't match augeas pattern");
803 $alias = $g->aug_get($path);
806 $modulename = $g->aug_get($path.'/modulename');
809 $aliasinfo{modulename} = $modulename;
810 $aliasinfo{augeas} = $path;
811 $aliasinfo{file} = $file;
813 $modprobe_aliases{$alias} = \%aliasinfo;
817 $oses{$root_dev}->{modprobe_aliases} = \%modprobe_aliases;
820 # Get a listing of device drivers in any initrd corresponding to a
821 # kernel. This is an indication of what can possibly be booted.
826 my $root_dev = shift;
830 foreach my $initrd ($g->ls ("/boot")) {
831 if ($initrd =~ m/^initrd-(.*)\.img$/ && $g->is_file ("/boot/$initrd")) {
836 @modules = $g->initrd_list ("/boot/$initrd");
839 @modules = grep { m,([^/]+)\.ko$, || m,([^/]+)\.o$, } @modules;
840 $initrd_modules{$version} = \@modules
842 warn "/boot/$initrd: could not read initrd format"
847 $oses{$root_dev}->{initrd_modules} = \%initrd_modules;
850 #----------------------------------------------------------------------
853 if ($output eq "fish" || $output eq "ro-fish") {
854 my @osdevs = keys %oses;
855 # This only works if there is a single OS.
856 die "--fish output is only possible with a single OS\n" if @osdevs != 1;
858 my $root_dev = $osdevs[0];
860 if ($output eq "ro-fish") {
864 print "-a $_ " foreach @ARGV;
866 my $mounts = $oses{$root_dev}->{mounts};
867 # Have to mount / first. Luckily '/' is early in the ASCII
868 # character set, so this should be OK.
869 foreach (sort keys %$mounts) {
870 print "-m $mounts->{$_}:$_ " if $_ ne "swap";
876 elsif ($output eq "perl") {
877 print Dumper(\%oses);
881 elsif ($output eq "yaml") {
882 die "virt-inspector: no YAML support\n"
883 unless exists $INC{"YAML/Any.pm"};
888 # Plain text output (the default).
889 elsif ($output eq "text") {
894 elsif ($output eq "xml") {
899 elsif ($output eq "query") {
905 output_text_os ($oses{$_}) foreach sort keys %oses;
912 print $os->{os}, " " if exists $os->{os};
913 print $os->{distro}, " " if exists $os->{distro};
914 print $os->{version}, " " if exists $os->{version};
915 print "on ", $os->{root_device}, ":\n";
917 print " Mountpoints:\n";
918 my $mounts = $os->{mounts};
919 foreach (sort keys %$mounts) {
920 printf " %-30s %s\n", $mounts->{$_}, $_
923 print " Filesystems:\n";
924 my $filesystems = $os->{filesystems};
925 foreach (sort keys %$filesystems) {
927 print " label: $filesystems->{$_}{label}\n"
928 if exists $filesystems->{$_}{label};
929 print " UUID: $filesystems->{$_}{uuid}\n"
930 if exists $filesystems->{$_}{uuid};
931 print " type: $filesystems->{$_}{fstype}\n"
932 if exists $filesystems->{$_}{fstype};
933 print " content: $filesystems->{$_}{content}\n"
934 if exists $filesystems->{$_}{content};
937 if (exists $os->{modprobe_aliases}) {
938 my %aliases = %{$os->{modprobe_aliases}};
939 my @keys = sort keys %aliases;
941 print " Modprobe aliases:\n";
943 printf " %-30s %s\n", $_, $aliases{$_}->{modulename}
948 if (exists $os->{initrd_modules}) {
949 my %modvers = %{$os->{initrd_modules}};
950 my @keys = sort keys %modvers;
952 print " Initrd modules:\n";
954 my @modules = @{$modvers{$_}};
956 print " $_\n" foreach @modules;
961 print " Applications:\n";
962 my @apps = @{$os->{apps}};
964 print " $_->{name} $_->{version}\n"
968 my @kernels = @{$os->{kernels}};
970 print " $_->{version}\n";
971 my @modules = @{$_->{modules}};
977 if (exists $os->{root}->{registry}) {
978 print " Windows Registry entries:\n";
979 # These are just lumps of text - dump them out.
980 foreach (@{$os->{root}->{registry}}) {
988 my $xml = new XML::Writer(DATA_MODE => 1, DATA_INDENT => 2);
990 $xml->startTag("operatingsystems");
991 output_xml_os ($oses{$_}, $xml) foreach sort keys %oses;
992 $xml->endTag("operatingsystems");
1001 $xml->startTag("operatingsystem");
1003 foreach ( [ "name" => "os" ],
1004 [ "distro" => "distro" ],
1005 [ "version" => "version" ],
1006 [ "root" => "root_device" ] ) {
1007 $xml->dataElement($_->[0], $os->{$_->[1]}) if exists $os->{$_->[1]};
1010 $xml->startTag("mountpoints");
1011 my $mounts = $os->{mounts};
1012 foreach (sort keys %$mounts) {
1013 $xml->dataElement("mountpoint", $_, "dev" => $mounts->{$_});
1015 $xml->endTag("mountpoints");
1017 $xml->startTag("filesystems");
1018 my $filesystems = $os->{filesystems};
1019 foreach (sort keys %$filesystems) {
1020 $xml->startTag("filesystem", "dev" => $_);
1022 foreach my $field ( [ "label" => "label" ],
1023 [ "uuid" => "uuid" ],
1024 [ "type" => "fstype" ],
1025 [ "content" => "content" ],
1026 [ "spec" => "spec" ] ) {
1027 $xml->dataElement($field->[0], $filesystems->{$_}{$field->[1]})
1028 if exists $filesystems->{$_}{$field->[1]};
1031 $xml->endTag("filesystem");
1033 $xml->endTag("filesystems");
1035 if (exists $os->{modprobe_aliases}) {
1036 my %aliases = %{$os->{modprobe_aliases}};
1037 my @keys = sort keys %aliases;
1039 $xml->startTag("modprobealiases");
1041 $xml->startTag("alias", "device" => $_);
1043 foreach my $field ( [ "modulename" => "modulename" ],
1044 [ "augeas" => "augeas" ],
1045 [ "file" => "file" ] ) {
1046 $xml->dataElement($field->[0], $aliases{$_}->{$field->[1]});
1049 $xml->endTag("alias");
1051 $xml->endTag("modprobealiases");
1055 if (exists $os->{initrd_modules}) {
1056 my %modvers = %{$os->{initrd_modules}};
1057 my @keys = sort keys %modvers;
1059 $xml->startTag("initrds");
1061 my @modules = @{$modvers{$_}};
1062 $xml->startTag("initrd", "version" => $_);
1063 $xml->dataElement("module", $_) foreach @modules;
1064 $xml->endTag("initrd");
1066 $xml->endTag("initrds");
1070 $xml->startTag("applications");
1071 my @apps = @{$os->{apps}};
1073 $xml->startTag("application");
1074 $xml->dataElement("name", $_->{name});
1075 $xml->dataElement("version", $_->{version});
1076 $xml->endTag("application");
1078 $xml->endTag("applications");
1080 $xml->startTag("kernels");
1081 my @kernels = @{$os->{kernels}};
1082 foreach (@kernels) {
1083 $xml->startTag("kernel", "version" => $_->{version});
1084 $xml->startTag("modules");
1085 my @modules = @{$_->{modules}};
1086 foreach (@modules) {
1087 $xml->dataElement("module", $_);
1089 $xml->endTag("modules");
1090 $xml->endTag("kernel");
1092 $xml->endTag("kernels");
1094 if (exists $os->{root}->{registry}) {
1095 $xml->startTag("windowsregistryentries");
1096 # These are just lumps of text - dump them out.
1097 foreach (@{$os->{root}->{registry}}) {
1098 $xml->dataElement("windowsregistryentry", $_);
1100 $xml->endTag("windowsregistryentries");
1103 $xml->endTag("operatingsystem");
1108 When you use C<virt-inspector --query>, the output is a series of
1116 (each answer is usually C<yes> or C<no>, or the line is completely
1117 missing if we could not determine the answer at all).
1119 If the guest is multiboot, you can get apparently conflicting answers
1120 (eg. C<windows=yes> and C<linux=yes>, or a guest which is both
1121 fullvirt and has a Xen PV kernel). This is normal, and just means
1122 that the guest can do both things, although it might require operator
1123 intervention such as selecting a boot option when the guest is
1126 This section describes the full range of answers possible.
1134 output_query_windows ();
1135 output_query_linux ();
1136 output_query_rhel ();
1137 output_query_fedora ();
1138 output_query_debian ();
1139 output_query_fullvirt ();
1140 output_query_xen_domU_kernel ();
1141 output_query_xen_pv_drivers ();
1142 output_query_virtio_drivers ();
1145 =item windows=(yes|no)
1147 Answer C<yes> if Microsoft Windows is installed in the guest.
1151 sub output_query_windows
1154 foreach my $os (keys %oses) {
1155 $windows="yes" if $oses{$os}->{os} eq "windows";
1157 print "windows=$windows\n";
1160 =item linux=(yes|no)
1162 Answer C<yes> if a Linux kernel is installed in the guest.
1166 sub output_query_linux
1169 foreach my $os (keys %oses) {
1170 $linux="yes" if $oses{$os}->{os} eq "linux";
1172 print "linux=$linux\n";
1177 Answer C<yes> if the guest contains Red Hat Enterprise Linux.
1181 sub output_query_rhel
1184 foreach my $os (keys %oses) {
1185 $rhel="yes" if $oses{$os}->{os} eq "linux" && $oses{$os}->{distro} eq "redhat";
1187 print "rhel=$rhel\n";
1190 =item fedora=(yes|no)
1192 Answer C<yes> if the guest contains the Fedora Linux distribution.
1196 sub output_query_fedora
1199 foreach my $os (keys %oses) {
1200 $fedora="yes" if $oses{$os}->{os} eq "linux" && $oses{$os}->{distro} eq "fedora";
1202 print "fedora=$fedora\n";
1205 =item debian=(yes|no)
1207 Answer C<yes> if the guest contains the Debian Linux distribution.
1211 sub output_query_debian
1214 foreach my $os (keys %oses) {
1215 $debian="yes" if $oses{$os}->{os} eq "linux" && $oses{$os}->{distro} eq "debian";
1217 print "debian=$debian\n";
1220 =item fullvirt=(yes|no)
1222 Answer C<yes> if there is at least one operating system kernel
1223 installed in the guest which runs fully virtualized. Such a guest
1224 would require a hypervisor which supports full system virtualization.
1228 sub output_query_fullvirt
1230 # The assumption is full-virt, unless all installed kernels
1231 # are identified as paravirt.
1232 # XXX Fails on Windows guests.
1233 foreach my $os (keys %oses) {
1234 foreach my $kernel (@{$oses{$os}->{kernels}}) {
1235 my $is_pv = $kernel->{version} =~ m/xen/;
1237 print "fullvirt=yes\n";
1242 print "fullvirt=no\n";
1245 =item xen_domU_kernel=(yes|no)
1247 Answer C<yes> if there is at least one Linux kernel installed in
1248 the guest which is compiled as a Xen DomU (a Xen paravirtualized
1253 sub output_query_xen_domU_kernel
1255 foreach my $os (keys %oses) {
1256 foreach my $kernel (@{$oses{$os}->{kernels}}) {
1257 my $is_xen = $kernel->{version} =~ m/xen/;
1259 print "xen_domU_kernel=yes\n";
1264 print "xen_domU_kernel=no\n";
1267 =item xen_pv_drivers=(yes|no)
1269 Answer C<yes> if the guest has Xen paravirtualized drivers installed
1270 (usually the kernel itself will be fully virtualized, but the PV
1271 drivers have been installed by the administrator for performance
1276 sub output_query_xen_pv_drivers
1278 foreach my $os (keys %oses) {
1279 foreach my $kernel (@{$oses{$os}->{kernels}}) {
1280 foreach my $module (@{$kernel->{modules}}) {
1281 if ($module =~ m/xen-/) {
1282 print "xen_pv_drivers=yes\n";
1288 print "xen_pv_drivers=no\n";
1291 =item virtio_drivers=(yes|no)
1293 Answer C<yes> if the guest has virtio paravirtualized drivers
1294 installed. Virtio drivers are commonly used to improve the
1299 sub output_query_virtio_drivers
1301 foreach my $os (keys %oses) {
1302 foreach my $kernel (@{$oses{$os}->{kernels}}) {
1303 foreach my $module (@{$kernel->{modules}}) {
1304 if ($module =~ m/virtio_/) {
1305 print "virtio_drivers=yes\n";
1311 print "virtio_drivers=no\n";
1321 L<Sys::Guestfs::Lib(3)>,
1323 L<http://libguestfs.org/>.
1325 For Windows registry parsing we require the C<reged> program
1326 from L<http://home.eunet.no/~pnordahl/ntpasswd/>.
1330 Richard W.M. Jones L<http://et.redhat.com/~rjones/>
1334 Copyright (C) 2009 Red Hat Inc.
1336 This program is free software; you can redistribute it and/or modify
1337 it under the terms of the GNU General Public License as published by
1338 the Free Software Foundation; either version 2 of the License, or
1339 (at your option) any later version.
1341 This program is distributed in the hope that it will be useful,
1342 but WITHOUT ANY WARRANTY; without even the implied warranty of
1343 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1344 GNU General Public License for more details.
1346 You should have received a copy of the GNU General Public License
1347 along with this program; if not, write to the Free Software
1348 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.