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.
26 use File::Temp qw/tempdir/;
29 eval "use Sys::Virt;";
35 virt-inspector - Display OS version, kernel, drivers, mount points, applications, etc. in a virtual machine
39 virt-inspector [--connect URI] domname
41 virt-inspector guest.img [guest.img ...]
45 B<virt-inspector> examines a virtual machine and tries to determine
46 the version of the OS, the kernel version, what drivers are installed,
47 whether the virtual machine is fully virtualized (FV) or
48 para-virtualized (PV), what applications are installed and more.
50 Virt-inspector can produce output in several formats, including a
51 readable text report, and XML for feeding into other programs.
53 Virt-inspector should only be run on I<inactive> virtual machines.
54 The program tries to determine that the machine is inactive and will
55 refuse to run if it thinks you are trying to inspect a running domain.
57 In the normal usage, use C<virt-inspector domname> where C<domname> is
58 the libvirt domain (see: C<virsh list --all>).
60 You can also run virt-inspector directly on disk images from a single
61 virtual machine. Use C<virt-inspector guest.img>. In rare cases a
62 domain has several block devices, in which case you should list them
63 one after another, with the first corresponding to the guest's
64 C</dev/sda>, the second to the guest's C</dev/sdb> and so on.
66 Virt-inspector can only inspect and report upon I<one domain at a
67 time>. To inspect several virtual machines, you have to run
68 virt-inspector several times (for example, from a shell script
71 Because virt-inspector needs direct access to guest images, it won't
72 normally work over remote libvirt connections.
90 =item B<--connect URI> | B<-c URI>
92 If using libvirt, connect to the given I<URI>. If omitted,
93 then we connect to the default libvirt hypervisor.
95 Libvirt is only used if you specify a C<domname> on the
96 command line. If you specify guest block devices directly,
97 then libvirt is not used at all.
105 Force reading a particular guest even if it appears to be active. In
106 earlier versions of virt-inspector, this could be dangerous (for
107 example, corrupting the guest's disk image). However in more recent
108 versions, it should not cause corruption, but might cause
109 virt-inspector to crash or produce incorrect results.
117 The following options select the output format. Use only one of them.
118 The default is a readable text report.
122 =item B<--text> (default)
128 Produce no output at all.
132 If you select I<--xml> then you get XML output which can be fed
137 If you select I<--perl> then you get Perl structures output which
138 can be used directly in another Perl program.
144 If you select I<--fish> then we print a L<guestfish(1)> command
145 line which will automatically mount up the filesystems on the
146 correct mount points. Try this for example:
148 guestfish $(virt-inspector --fish guest.img)
150 I<--ro-fish> is the same, but the I<--ro> option is passed to
151 guestfish so that the filesystems are mounted read-only.
155 In "query mode" we answer common questions about the guest, such
156 as whether it is fullvirt or needs a Xen hypervisor to run.
158 See section I<QUERY MODE> below.
162 my $windows_registry;
164 =item B<--windows-registry>
166 If this item is passed, I<and> the guest is Windows, I<and> the
167 external program C<reged> is available (see SEE ALSO section), then we
168 attempt to parse the Windows registry. This allows much more
169 information to be gathered for Windows guests.
171 This is quite an expensive and slow operation, so we don't do it by
178 GetOptions ("help|?" => \$help,
179 "connect|c=s" => \$uri,
181 "text" => sub { $output = "text" },
182 "none" => sub { $output = "none" },
183 "xml" => sub { $output = "xml" },
184 "perl" => sub { $output = "perl" },
185 "fish" => sub { $output = "fish" },
186 "guestfish" => sub { $output = "fish" },
187 "ro-fish" => sub { $output = "ro-fish" },
188 "ro-guestfish" => sub { $output = "ro-fish" },
189 "query" => sub { $output = "query" },
190 "windows-registry" => \$windows_registry,
192 pod2usage (1) if $help;
193 pod2usage ("$0: no image or VM names given") if @ARGV == 0;
195 # Domain name or guest image(s)?
203 die "guest image $_ does not exist or is not readable\n"
207 die "no libvirt support (install Sys::Virt)"
208 unless exists $INC{"Sys/Virt.pm"};
210 pod2usage ("$0: too many domains listed on command line") if @ARGV > 1;
214 $vmm = Sys::Virt->new (uri => $uri, readonly => 1);
216 $vmm = Sys::Virt->new (readonly => 1);
218 die "cannot connect to libvirt $uri\n" unless $vmm;
220 my @doms = $vmm->list_defined_domains ();
223 if ($_->get_name () eq $ARGV[0]) {
228 die "$ARGV[0] is not the name of an inactive libvirt domain\n"
231 # Get the names of the image(s).
232 my $xml = $dom->get_xml_description ();
234 my $p = new XML::XPath::XMLParser (xml => $xml);
235 my $disks = $p->find ("//devices/disk");
237 foreach ($disks->get_nodelist) {
238 print XML::XPath::XMLParser::as_string($_);
244 # We've now got the list of @images, so feed them to libguestfs.
245 my $g = Sys::Guestfs->new ();
246 $g->add_drive_ro ($_) foreach @images;
250 # We want to get the list of LVs and partitions (ie. anything that
251 # could contain a filesystem). Discard any partitions which are PVs.
252 my @partitions = $g->list_partitions ();
253 my @pvs = $g->pvs ();
257 return 1 if $_ eq $t;
261 @partitions = grep { ! is_pv ($_) } @partitions;
263 my @lvs = $g->lvs ();
269 Linux (distro + version)
273 +--- Filesystems ---------- Installed apps --- Kernel & drivers
274 ----------- -------------- ----------------
275 mount point => device List of apps Extra information
276 mount point => device and versions about kernel(s)
279 (plus lots of extra information
280 about each filesystem)
282 The output of virt-inspector is a complex two-level data structure.
284 At the top level is a list of the operating systems installed on the
285 guest. (For the vast majority of guests, only a single OS is
286 installed.) The data returned for the OS includes the name (Linux,
287 Windows), the distribution and version.
289 The diagram above shows what we return for each OS.
291 With the I<--xml> option the output is mapped into an XML document.
292 Unfortunately there is no clear schema for this document
293 (contributions welcome) but you can get an idea of the format by
294 looking at other documents and as a last resort the source for this
297 With the I<--fish> or I<--ro-fish> option the mount points are mapped to
298 L<guestfish(1)> command line parameters, so that you can go in
299 afterwards and inspect the guest with everything mounted in the
300 right place. For example:
302 guestfish $(virt-inspector --ro-fish guest.img)
303 ==> guestfish --ro -a guest.img -m /dev/VG/LV:/ -m /dev/sda1:/boot
307 # List of possible filesystems.
308 my @devices = sort (@lvs, @partitions);
310 # Now query each one to build up a picture of what's in it.
311 my %fses = map { $_ => check_fs ($_) } @devices;
313 # Now the complex checking code itself.
314 # check_fs takes a device name (LV or partition name) and returns
315 # a hashref containing everything we can find out about the device.
318 my $dev = shift; # LV or partition name.
320 my %r; # Result hash.
322 # First try 'file(1)' on it.
323 my $file = $g->file ($dev);
324 if ($file =~ /ext2 filesystem data/) {
327 } elsif ($file =~ /ext3 filesystem data/) {
330 } elsif ($file =~ /ext4 filesystem data/) {
333 } elsif ($file =~ m{Linux/i386 swap file}) {
339 # If it's ext2/3/4, then we want the UUID and label.
340 if (exists $r{fstype} && $r{fstype} =~ /^ext/) {
341 $r{uuid} = $g->get_e2uuid ($dev);
342 $r{label} = $g->get_e2label ($dev);
345 # Try mounting it, fnarrr.
347 $r{is_mountable} = 1;
348 eval { $g->mount_ro ($dev, "/") };
350 # It's not mountable, probably empty or some format
351 # we don't understand.
352 $r{is_mountable} = 0;
357 if ($g->is_file ("/grub/menu.lst") ||
358 $g->is_file ("/grub/grub.conf")) {
359 $r{content} = "linux-grub";
365 if ($g->is_dir ("/etc") && $g->is_dir ("/bin") &&
366 $g->is_file ("/etc/fstab")) {
367 $r{content} = "linux-root";
369 check_linux_root (\%r);
374 if ($g->is_dir ("/etc") && $g->is_dir ("/bin") &&
375 $g->is_dir ("/share") && !$g->exists ("/local") &&
376 !$g->is_file ("/etc/fstab")) {
377 $r{content} = "linux-usrlocal";
382 if ($g->is_dir ("/etc") && $g->is_dir ("/bin") &&
383 $g->is_dir ("/share") && $g->exists ("/local") &&
384 !$g->is_file ("/etc/fstab")) {
385 $r{content} = "linux-usr";
390 if ($g->is_file ("/AUTOEXEC.BAT") ||
391 $g->is_file ("/autoexec.bat") ||
392 $g->is_dir ("/Program Files") ||
393 $g->is_dir ("/WINDOWS") ||
394 $g->is_file ("/boot.ini") ||
395 $g->is_file ("/ntldr")) {
396 $r{fstype} = "ntfs"; # XXX this is a guess
397 $r{fsos} = "windows";
398 $r{content} = "windows-root";
400 check_windows_root (\%r);
415 # Look into /etc to see if we recognise the operating system.
416 if ($g->is_file ("/etc/redhat-release")) {
417 $_ = $g->cat ("/etc/redhat-release");
418 if (/Fedora release (\d+\.\d+)/) {
419 $r->{osdistro} = "fedora";
420 $r->{osversion} = "$1"
421 } elsif (/(Red Hat Enterprise Linux|CentOS|Scientific Linux).*release (\d+).*Update (\d+)/) {
422 $r->{osdistro} = "redhat";
423 $r->{osversion} = "$2.$3";
424 } elsif (/(Red Hat Enterprise Linux|CentOS|Scientific Linux).*release (\d+(?:\.(\d+))?)/) {
425 $r->{osdistro} = "redhat";
426 $r->{osversion} = "$2";
428 $r->{osdistro} = "redhat";
430 } elsif ($g->is_file ("/etc/debian_version")) {
431 $_ = $g->cat ("/etc/debian_version");
433 $r->{osdistro} = "debian";
434 $r->{osversion} = "$1";
436 $r->{osdistro} = "debian";
440 # Parse the contents of /etc/fstab. This is pretty vital so
441 # we can determine where filesystems are supposed to be mounted.
442 eval "\$_ = \$g->cat ('/etc/fstab');";
444 my @lines = split /\n/;
447 my @fields = split /[ \t]+/;
449 my $spec = $fields[0]; # first column (dev/label/uuid)
450 my $file = $fields[1]; # second column (mountpoint)
451 if ($spec =~ m{^/} ||
452 $spec =~ m{^LABEL=} ||
453 $spec =~ m{^UUID=} ||
455 push @fstab, [$spec, $file]
459 $r->{fstab} = \@fstab if @fstab;
463 # We only support NT. The control file /boot.ini contains a list of
464 # Windows installations and their %systemroot%s in a simple text
467 # XXX We could parse this better. This won't work if /boot.ini is on
468 # a different drive from the %systemroot%, and in other unusual cases.
470 sub check_windows_root
475 my $boot_ini = resolve_windows_path ("/", "boot.ini");
476 $r->{boot_ini} = $boot_ini;
478 if (defined $r->{boot_ini}) {
479 $_ = $g->cat ($boot_ini);
480 my @lines = split /\n/;
486 } elsif (m/^default=.*?\\(\w+)$/i) {
489 } elsif (m/\\(\w+)=/) {
495 if (defined $systemroot) {
496 $r->{systemroot} = resolve_windows_path ("/", $systemroot);
497 if (defined $r->{systemroot} && $windows_registry) {
498 check_windows_registry ($r, $r->{systemroot});
504 sub check_windows_registry
508 my $systemroot = shift;
510 # Download the system registry files. Only download the
511 # interesting ones, and we don't bother with user profiles at all.
512 my $system32 = resolve_windows_path ($systemroot, "system32");
513 if (defined $system32) {
514 my $config = resolve_windows_path ($system32, "config");
515 if (defined $config) {
516 my $software = resolve_windows_path ($config, "software");
517 if (defined $software) {
518 load_windows_registry ($r, $software,
519 "HKEY_LOCAL_MACHINE\\SOFTWARE");
521 my $system = resolve_windows_path ($config, "system");
522 if (defined $system) {
523 load_windows_registry ($r, $system,
524 "HKEY_LOCAL_MACHINE\\System");
530 sub load_windows_registry
537 my $dir = tempdir (CLEANUP => 1);
539 $g->download ($regfile, "$dir/reg");
541 # 'reged' command is particularly noisy. Redirect stdout and
542 # stderr to /dev/null temporarily.
543 open SAVEOUT, ">&STDOUT";
544 open SAVEERR, ">&STDERR";
545 open STDOUT, ">/dev/null";
546 open STDERR, ">/dev/null";
548 my @cmd = ("reged", "-x", "$dir/reg", "$prefix", "\\", "$dir/out");
549 my $res = system (@cmd);
553 open STDOUT, ">&SAVEOUT";
554 open STDERR, ">&SAVEERR";
559 warn "reged command failed: $?";
563 # Some versions of reged segfault on inputs. If that happens we
564 # may get no / partial output file. Anyway, if it exists, load
567 unless (open F, "$dir/out") {
568 warn "no output from reged command: $!";
571 { local $/ = undef; $content = <F>; }
575 @registry = @{$r->{registry}} if exists $r->{registry};
576 push @registry, $content;
577 $r->{registry} = \@registry;
580 # Because of case sensitivity, the actual path might have a different
581 # name, and ntfs-3g is always case sensitive. Find out what the real
582 # path is. Returns the correct full path, or undef.
583 sub resolve_windows_path
586 my $parent = shift; # Must exist, with correct case.
589 foreach ($g->ls ($parent)) {
590 if (lc ($_) eq lc ($dir)) {
591 if ($parent eq "/") {
607 # Grub version, if we care.
610 #print Dumper (\%fses);
612 #----------------------------------------------------------------------
613 # Now find out how many operating systems we've got. Usually just one.
617 foreach (sort keys %fses) {
618 if ($fses{$_}->{is_root}) {
623 get_os_version (\%r);
624 assign_mount_points (\%r);
634 $r->{os} = $r->{root}->{fsos} if exists $r->{root}->{fsos};
635 $r->{distro} = $r->{root}->{osdistro} if exists $r->{root}->{osdistro};
636 $r->{version} = $r->{root}->{osversion} if exists $r->{root}->{osversion};
639 sub assign_mount_points
644 $r->{mounts} = { "/" => $r->{root_device} };
645 $r->{filesystems} = { $r->{root_device} => $r->{root} };
647 # Use /etc/fstab if we have it to mount the rest.
648 if (exists $r->{root}->{fstab}) {
649 my @fstab = @{$r->{root}->{fstab}};
651 my ($spec, $file) = @$_;
653 my ($dev, $fs) = find_filesystem ($spec);
655 $r->{mounts}->{$file} = $dev;
656 $r->{filesystems}->{$dev} = $fs;
657 if (exists $fs->{used}) {
667 # Find filesystem by device name, LABEL=.. or UUID=..
674 foreach (sort keys %fses) {
675 if (exists $fses{$_}->{label} &&
676 $fses{$_}->{label} eq $label) {
677 return ($_, $fses{$_});
680 warn "unknown filesystem label $label\n";
682 } elsif (/^UUID=(.*)/) {
684 foreach (sort keys %fses) {
685 if (exists $fses{$_}->{uuid} &&
686 $fses{$_}->{uuid} eq $uuid) {
687 return ($_, $fses{$_});
690 warn "unknown filesystem UUID $uuid\n";
693 return ($_, $fses{$_}) if exists $fses{$_};
695 if (m{^/dev/hd(.*)} && exists $fses{"/dev/sd$1"}) {
696 return ("/dev/sd$1", $fses{"/dev/sd$1"});
698 if (m{^/dev/xvd(.*)} && exists $fses{"/dev/sd$1"}) {
699 return ("/dev/sd$1", $fses{"/dev/sd$1"});
702 return () if m{/dev/cdrom};
704 warn "unknown filesystem $_\n";
709 #print Dumper(\%oses);
711 #----------------------------------------------------------------------
712 # Mount up the disks so we can check for applications
713 # and kernels. Skip this if the output is "*fish" because
714 # we don't need to know.
716 if ($output !~ /.*fish$/) {
717 # Temporary directory for use by check_for_initrd.
718 my $dir = tempdir (CLEANUP => 1);
721 foreach $root_dev (sort keys %oses) {
722 my $mounts = $oses{$root_dev}->{mounts};
723 # Have to mount / first. Luckily '/' is early in the ASCII
724 # character set, so this should be OK.
725 foreach (sort keys %$mounts) {
726 $g->mount_ro ($mounts->{$_}, $_)
727 if $_ ne "swap" && ($_ eq '/' || $g->is_dir ($_));
730 check_for_applications ($root_dev);
731 check_for_kernels ($root_dev);
732 if ($oses{$root_dev}->{os} eq "linux") {
733 check_for_modprobe_aliases ($root_dev);
734 check_for_initrd ($root_dev, $dir);
741 sub check_for_applications
744 my $root_dev = shift;
748 my $os = $oses{$root_dev}->{os};
749 if ($os eq "linux") {
750 my $distro = $oses{$root_dev}->{distro};
751 if (defined $distro && ($distro eq "redhat" || $distro eq "fedora")) {
752 my @lines = $g->command_lines
755 "--qf", "%{name} %{epoch} %{version} %{release} %{arch}\n"]);
757 if (m/^(.*) (.*) (.*) (.*) (.*)$/) {
759 $epoch = "" if $epoch eq "(none)";
771 } elsif ($os eq "windows") {
773 # I worked out a general plan for this, but haven't
774 # implemented it yet. We can iterate over /Program Files
775 # looking for *.EXE files, which we download, then use
776 # i686-pc-mingw32-windres on, to find the VERSIONINFO
777 # section, which has a lot of useful information.
780 $oses{$root_dev}->{apps} = \@apps;
783 sub check_for_kernels
786 my $root_dev = shift;
790 my $os = $oses{$root_dev}->{os};
791 if ($os eq "linux") {
792 # Installed kernels will have a corresponding /lib/modules/<version>
793 # directory, which is the easiest way to find out what kernels
794 # are installed, and what modules are available.
795 foreach ($g->ls ("/lib/modules")) {
796 if ($g->is_dir ("/lib/modules/$_")) {
798 $kernel{version} = $_;
802 foreach ($g->find ("/lib/modules/$_")) {
803 if (m,/([^/]+)\.ko$, || m,([^/]+)\.o$,) {
808 $kernel{modules} = \@modules;
810 push @kernels, \%kernel;
814 } elsif ($os eq "windows") {
818 $oses{$root_dev}->{kernels} = \@kernels;
821 # Check /etc/modprobe.conf to see if there are any specified
822 # drivers associated with network (ethX) or hard drives. Normally
823 # one might find something like:
826 # alias scsi_hostadapter xenblk
828 # XXX This doesn't look beyond /etc/modprobe.conf, eg. in /etc/modprobe.d/
830 sub check_for_modprobe_aliases
833 my $root_dev = shift;
836 eval { @lines = $g->read_lines ("/etc/modprobe.conf"); };
837 return if $@ || !@lines;
839 my %modprobe_aliases;
842 $modprobe_aliases{$1} = $2 if /^\s*alias\s+(\S+)\s+(\S+)/;
845 $oses{$root_dev}->{modprobe_aliases} = \%modprobe_aliases;
848 # Get a listing of device drivers in any initrd corresponding to a
849 # kernel. This is an indication of what can possibly be booted.
854 my $root_dev = shift;
859 foreach my $initrd ($g->ls ("/boot")) {
860 if ($initrd =~ m/^initrd-(.*)\.img$/ && $g->is_file ("/boot/$initrd")) {
863 # We have to download these to a temporary file.
864 $g->download ("/boot/$initrd", "$dir/initrd");
866 my $cmd = "zcat $dir/initrd | file -";
867 open P, "$cmd |" or die "$cmd: $!";
869 { local $/ = undef; $lines = <P>; }
871 if ($lines =~ /ext\d filesystem data/) {
872 # Before initramfs came along, these were compressed
873 # ext2 filesystems. We could run another libguestfs
874 # instance to unpack these, but punt on them for now. (XXX)
875 warn "initrd image is unsupported ext2/3/4 filesystem\n";
877 elsif ($lines =~ /cpio/) {
878 my $cmd = "zcat $dir/initrd | cpio --quiet -it";
879 open P, "$cmd |" or die "$cmd: $!";
882 if m,([^/]+)\.ko$, || m,([^/]+)\.o$,;
885 unlink "$dir/initrd";
886 $initrd_modules{$version} = \@modules;
890 warn "unrecognized initrd image: $lines\n";
895 $oses{$root_dev}->{initrd_modules} = \%initrd_modules;
898 #----------------------------------------------------------------------
901 if ($output eq "fish" || $output eq "ro-fish") {
902 my @osdevs = keys %oses;
903 # This only works if there is a single OS.
904 die "--fish output is only possible with a single OS\n" if @osdevs != 1;
906 my $root_dev = $osdevs[0];
908 if ($output eq "ro-fish") {
912 print "-a $_ " foreach @images;
914 my $mounts = $oses{$root_dev}->{mounts};
915 # Have to mount / first. Luckily '/' is early in the ASCII
916 # character set, so this should be OK.
917 foreach (sort keys %$mounts) {
918 print "-m $mounts->{$_}:$_ " if $_ ne "swap";
924 elsif ($output eq "perl") {
925 print Dumper(\%oses);
928 # Plain text output (the default).
929 elsif ($output eq "text") {
934 elsif ($output eq "xml") {
939 elsif ($output eq "query") {
945 output_text_os ($oses{$_}) foreach sort keys %oses;
952 print $os->{os}, " " if exists $os->{os};
953 print $os->{distro}, " " if exists $os->{distro};
954 print $os->{version}, " " if exists $os->{version};
955 print "on ", $os->{root_device}, ":\n";
957 print " Mountpoints:\n";
958 my $mounts = $os->{mounts};
959 foreach (sort keys %$mounts) {
960 printf " %-30s %s\n", $mounts->{$_}, $_
963 print " Filesystems:\n";
964 my $filesystems = $os->{filesystems};
965 foreach (sort keys %$filesystems) {
967 print " label: $filesystems->{$_}{label}\n"
968 if exists $filesystems->{$_}{label};
969 print " UUID: $filesystems->{$_}{uuid}\n"
970 if exists $filesystems->{$_}{uuid};
971 print " type: $filesystems->{$_}{fstype}\n"
972 if exists $filesystems->{$_}{fstype};
973 print " content: $filesystems->{$_}{content}\n"
974 if exists $filesystems->{$_}{content};
977 if (exists $os->{modprobe_aliases}) {
978 my %aliases = %{$os->{modprobe_aliases}};
979 my @keys = sort keys %aliases;
981 print " Modprobe aliases:\n";
983 printf " %-30s %s\n", $_, $aliases{$_}
988 if (exists $os->{initrd_modules}) {
989 my %modvers = %{$os->{initrd_modules}};
990 my @keys = sort keys %modvers;
992 print " Initrd modules:\n";
994 my @modules = @{$modvers{$_}};
996 print " $_\n" foreach @modules;
1001 print " Applications:\n";
1002 my @apps = @{$os->{apps}};
1004 print " $_->{name} $_->{version}\n"
1007 print " Kernels:\n";
1008 my @kernels = @{$os->{kernels}};
1009 foreach (@kernels) {
1010 print " $_->{version}\n";
1011 my @modules = @{$_->{modules}};
1012 foreach (@modules) {
1017 if (exists $os->{root}->{registry}) {
1018 print " Windows Registry entries:\n";
1019 # These are just lumps of text - dump them out.
1020 foreach (@{$os->{root}->{registry}}) {
1028 print "<operatingsystems>\n";
1029 output_xml_os ($oses{$_}) foreach sort keys %oses;
1030 print "</operatingsystems>\n";
1037 print "<operatingsystem>\n";
1039 print "<os>", $os->{os}, "</os>\n" if exists $os->{os};
1040 print "<distro>", $os->{distro}, "</distro>\n" if exists $os->{distro};
1041 print "<version>", $os->{version}, "</version>\n" if exists $os->{version};
1042 print "<root>", $os->{root_device}, "</root>\n";
1044 print "<mountpoints>\n";
1045 my $mounts = $os->{mounts};
1046 foreach (sort keys %$mounts) {
1047 printf "<mountpoint dev='%s'>%s</mountpoint>\n",
1050 print "</mountpoints>\n";
1052 print "<filesystems>\n";
1053 my $filesystems = $os->{filesystems};
1054 foreach (sort keys %$filesystems) {
1055 print "<filesystem dev='$_'>\n";
1056 print "<label>$filesystems->{$_}{label}</label>\n"
1057 if exists $filesystems->{$_}{label};
1058 print "<uuid>$filesystems->{$_}{uuid}</uuid>\n"
1059 if exists $filesystems->{$_}{uuid};
1060 print "<type>$filesystems->{$_}{fstype}</type>\n"
1061 if exists $filesystems->{$_}{fstype};
1062 print "<content>$filesystems->{$_}{content}</content>\n"
1063 if exists $filesystems->{$_}{content};
1064 print "</filesystem>\n";
1066 print "</filesystems>\n";
1068 if (exists $os->{modprobe_aliases}) {
1069 my %aliases = %{$os->{modprobe_aliases}};
1070 my @keys = sort keys %aliases;
1072 print "<modprobealiases>\n";
1074 printf "<alias device=\"%s\">%s</alias>\n", $_, $aliases{$_}
1076 print "</modprobealiases>\n";
1080 if (exists $os->{initrd_modules}) {
1081 my %modvers = %{$os->{initrd_modules}};
1082 my @keys = sort keys %modvers;
1084 print "<initrds>\n";
1086 my @modules = @{$modvers{$_}};
1087 print "<initrd version=\"$_\">\n";
1088 print "<module>$_</module>\n" foreach @modules;
1089 print "</initrd>\n";
1091 print "</initrds>\n";
1095 print "<applications>\n";
1096 my @apps = @{$os->{apps}};
1098 print "<application>\n";
1099 print "<name>$_->{name}</name><version>$_->{version}</version>\n";
1100 print "</application>\n";
1102 print "</applications>\n";
1104 print "<kernels>\n";
1105 my @kernels = @{$os->{kernels}};
1106 foreach (@kernels) {
1108 print "<version>$_->{version}</version>\n";
1109 print "<modules>\n";
1110 my @modules = @{$_->{modules}};
1111 foreach (@modules) {
1112 print "<module>$_</module>\n";
1114 print "</modules>\n";
1115 print "</kernel>\n";
1117 print "</kernels>\n";
1119 if (exists $os->{root}->{registry}) {
1120 print "<windowsregistryentries>\n";
1121 # These are just lumps of text - dump them out.
1122 foreach (@{$os->{root}->{registry}}) {
1123 print "<windowsregistryentry>\n";
1124 print escape_xml($_), "\n";
1125 print "</windowsregistryentry>\n";
1127 print "</windowsregistryentries>\n";
1130 print "</operatingsystem>\n";
1145 When you use C<virt-inspector --query>, the output is a series of
1153 (each answer is usually C<yes> or C<no>, or the line is completely
1154 missing if we could not determine the answer at all).
1156 If the guest is multiboot, you can get apparently conflicting answers
1157 (eg. C<windows=yes> and C<linux=yes>, or a guest which is both
1158 fullvirt and has a Xen PV kernel). This is normal, and just means
1159 that the guest can do both things, although it might require operator
1160 intervention such as selecting a boot option when the guest is
1163 This section describes the full range of answers possible.
1171 output_query_windows ();
1172 output_query_linux ();
1173 output_query_rhel ();
1174 output_query_fedora ();
1175 output_query_debian ();
1176 output_query_fullvirt ();
1177 output_query_xen_domU_kernel ();
1178 output_query_xen_pv_drivers ();
1179 output_query_virtio_drivers ();
1182 =item windows=(yes|no)
1184 Answer C<yes> if Microsoft Windows is installed in the guest.
1188 sub output_query_windows
1191 foreach my $os (keys %oses) {
1192 $windows="yes" if $oses{$os}->{os} eq "windows";
1194 print "windows=$windows\n";
1197 =item linux=(yes|no)
1199 Answer C<yes> if a Linux kernel is installed in the guest.
1203 sub output_query_linux
1206 foreach my $os (keys %oses) {
1207 $linux="yes" if $oses{$os}->{os} eq "linux";
1209 print "linux=$linux\n";
1214 Answer C<yes> if the guest contains Red Hat Enterprise Linux.
1218 sub output_query_rhel
1221 foreach my $os (keys %oses) {
1222 $rhel="yes" if $oses{$os}->{os} eq "linux" && $oses{$os}->{distro} eq "redhat";
1224 print "rhel=$rhel\n";
1227 =item fedora=(yes|no)
1229 Answer C<yes> if the guest contains the Fedora Linux distribution.
1233 sub output_query_fedora
1236 foreach my $os (keys %oses) {
1237 $fedora="yes" if $oses{$os}->{os} eq "linux" && $oses{$os}->{distro} eq "fedora";
1239 print "fedora=$fedora\n";
1242 =item debian=(yes|no)
1244 Answer C<yes> if the guest contains the Debian Linux distribution.
1248 sub output_query_debian
1251 foreach my $os (keys %oses) {
1252 $debian="yes" if $oses{$os}->{os} eq "linux" && $oses{$os}->{distro} eq "debian";
1254 print "debian=$debian\n";
1257 =item fullvirt=(yes|no)
1259 Answer C<yes> if there is at least one operating system kernel
1260 installed in the guest which runs fully virtualized. Such a guest
1261 would require a hypervisor which supports full system virtualization.
1265 sub output_query_fullvirt
1267 # The assumption is full-virt, unless all installed kernels
1268 # are identified as paravirt.
1269 # XXX Fails on Windows guests.
1270 foreach my $os (keys %oses) {
1271 foreach my $kernel (@{$oses{$os}->{kernels}}) {
1272 my $is_pv = $kernel->{version} =~ m/xen/;
1274 print "fullvirt=yes\n";
1279 print "fullvirt=no\n";
1282 =item xen_domU_kernel=(yes|no)
1284 Answer C<yes> if there is at least one Linux kernel installed in
1285 the guest which is compiled as a Xen DomU (a Xen paravirtualized
1290 sub output_query_xen_domU_kernel
1292 foreach my $os (keys %oses) {
1293 foreach my $kernel (@{$oses{$os}->{kernels}}) {
1294 my $is_xen = $kernel->{version} =~ m/xen/;
1296 print "xen_domU_kernel=yes\n";
1301 print "xen_domU_kernel=no\n";
1304 =item xen_pv_drivers=(yes|no)
1306 Answer C<yes> if the guest has Xen paravirtualized drivers installed
1307 (usually the kernel itself will be fully virtualized, but the PV
1308 drivers have been installed by the administrator for performance
1313 sub output_query_xen_pv_drivers
1315 foreach my $os (keys %oses) {
1316 foreach my $kernel (@{$oses{$os}->{kernels}}) {
1317 foreach my $module (@{$kernel->{modules}}) {
1318 if ($module =~ m/xen-/) {
1319 print "xen_pv_drivers=yes\n";
1325 print "xen_pv_drivers=no\n";
1328 =item virtio_drivers=(yes|no)
1330 Answer C<yes> if the guest has virtio paravirtualized drivers
1331 installed. Virtio drivers are commonly used to improve the
1336 sub output_query_virtio_drivers
1338 foreach my $os (keys %oses) {
1339 foreach my $kernel (@{$oses{$os}->{kernels}}) {
1340 foreach my $module (@{$kernel->{modules}}) {
1341 if ($module =~ m/virtio_/) {
1342 print "virtio_drivers=yes\n";
1348 print "virtio_drivers=no\n";
1359 L<http://libguestfs.org/>.
1361 For Windows registry parsing we require the C<reged> program
1362 from L<http://home.eunet.no/~pnordahl/ntpasswd/>.
1366 Richard W.M. Jones L<http://et.redhat.com/~rjones/>
1370 Copyright (C) 2009 Red Hat Inc.
1372 This program is free software; you can redistribute it and/or modify
1373 it under the terms of the GNU General Public License as published by
1374 the Free Software Foundation; either version 2 of the License, or
1375 (at your option) any later version.
1377 This program is distributed in the hope that it will be useful,
1378 but WITHOUT ANY WARRANTY; without even the implied warranty of
1379 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1380 GNU General Public License for more details.
1382 You should have received a copy of the GNU General Public License
1383 along with this program; if not, write to the Free Software
1384 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.