fuse: Specify format of disks (RHBZ#642934,CVE-2010-3851).
[libguestfs.git] / inspector / virt-inspector
1 #!/usr/bin/perl -w
2 # virt-inspector
3 # Copyright (C) 2009 Red Hat Inc.
4 #
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.
9 #
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.
14 #
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.
18
19 use warnings;
20 use strict;
21
22 use Sys::Guestfs;
23 use Sys::Guestfs::Lib qw(open_guest get_partitions resolve_windows_path
24   inspect_all_partitions inspect_partition
25   inspect_operating_systems mount_operating_system inspect_in_detail);
26 use Pod::Usage;
27 use Getopt::Long;
28 use Data::Dumper;
29 use XML::Writer;
30 use String::ShellQuote qw(shell_quote);
31 use Locale::TextDomain 'libguestfs';
32
33 # Optional:
34 eval "use YAML::Any;";
35
36 =encoding utf8
37
38 =head1 NAME
39
40 virt-inspector - Display OS version, kernel, drivers, mount points, applications, etc. in a virtual machine
41
42 =head1 SYNOPSIS
43
44  virt-inspector [--connect URI] domname
45
46  virt-inspector guest.img [guest.img ...]
47
48 =head1 DESCRIPTION
49
50 B<virt-inspector> examines a virtual machine and tries to determine
51 the version of the OS, the kernel version, what drivers are installed,
52 whether the virtual machine is fully virtualized (FV) or
53 para-virtualized (PV), what applications are installed and more.
54
55 Virt-inspector can produce output in several formats, including a
56 readable text report, and XML for feeding into other programs.
57
58 In the normal usage, use C<virt-inspector domname> where C<domname> is
59 the libvirt domain (see: C<virsh list --all>).
60
61 You can also run virt-inspector directly on disk images from a single
62 virtual machine.  Use C<virt-inspector guest.img>.  In rare cases a
63 domain has several block devices, in which case you should list them
64 one after another, with the first corresponding to the guest's
65 C</dev/sda>, the second to the guest's C</dev/sdb> and so on.
66
67 Virt-inspector can only inspect and report upon I<one domain at a
68 time>.  To inspect several virtual machines, you have to run
69 virt-inspector several times (for example, from a shell script
70 for-loop).
71
72 Because virt-inspector needs direct access to guest images, it won't
73 normally work over remote libvirt connections.
74
75 =head1 OPTIONS
76
77 =over 4
78
79 =cut
80
81 my $help;
82
83 =item B<--help>
84
85 Display brief help.
86
87 =cut
88
89 my $version;
90
91 =item B<--version>
92
93 Display version number and exit.
94
95 =cut
96
97 my $uri;
98
99 =item B<--connect URI> | B<-c URI>
100
101 If using libvirt, connect to the given I<URI>.  If omitted,
102 then we connect to the default libvirt hypervisor.
103
104 Libvirt is only used if you specify a C<domname> on the
105 command line.  If you specify guest block devices directly,
106 then libvirt is not used at all.
107
108 =cut
109
110 my $output = "text";
111
112 =back
113
114 The following options select the output format.  Use only one of them.
115 The default is a readable text report.
116
117 =over 4
118
119 =item B<--text> (default)
120
121 Plain text report.
122
123 =item B<--none>
124
125 Produce no output at all.
126
127 =item B<--xml>
128
129 If you select I<--xml> then you get XML output which can be fed
130 to other programs.
131
132 =item B<--yaml>
133
134 If you select I<--yaml> then you get YAML output which can be fed
135 to other programs.
136
137 =item B<--perl>
138
139 If you select I<--perl> then you get Perl structures output which
140 can be used directly in another Perl program.
141
142 =item B<--fish>
143
144 =item B<--ro-fish>
145
146 If you select I<--fish> then we print a L<guestfish(1)> command
147 line which will automatically mount up the filesystems on the
148 correct mount points.  Try this for example:
149
150  guestfish $(virt-inspector --fish guest.img)
151
152 I<--ro-fish> is the same, but the I<--ro> option is passed to
153 guestfish so that the filesystems are mounted read-only.
154
155 =item B<--query>
156
157 In "query mode" we answer common questions about the guest, such
158 as whether it is fullvirt or needs a Xen hypervisor to run.
159
160 See section I<QUERY MODE> below.
161
162 =cut
163
164 my $windows_registry;
165
166 =item B<--windows-registry>
167
168 This flag is ignored for compatibility with earlier releases of the
169 software.
170
171 In this version, if L<Win::Hivex(3)> is available, then we attempt to
172 parse information out of the Registry for any Windows guest.
173
174 =back
175
176 =cut
177
178 GetOptions ("help|?" => \$help,
179             "version" => \$version,
180             "connect|c=s" => \$uri,
181             "text" => sub { $output = "text" },
182             "none" => sub { $output = "none" },
183             "xml" => sub { $output = "xml" },
184             "yaml" => sub { $output = "yaml" },
185             "perl" => sub { $output = "perl" },
186             "fish" => sub { $output = "fish" },
187             "guestfish" => sub { $output = "fish" },
188             "ro-fish" => sub { $output = "ro-fish" },
189             "ro-guestfish" => sub { $output = "ro-fish" },
190             "query" => sub { $output = "query" },
191             "windows-registry" => \$windows_registry,
192     ) or pod2usage (2);
193 pod2usage (1) if $help;
194 if ($version) {
195     my $g = Sys::Guestfs->new ();
196     my %h = $g->version ();
197     print "$h{major}.$h{minor}.$h{release}$h{extra}\n";
198     exit
199 }
200 pod2usage (__"virt-inspector: no image or VM names given") if @ARGV == 0;
201
202 my $rw = 0;
203
204 # XXX This is a bug: Originally we intended to open the guest with
205 # rw=>1 in order to tell Sys::Guestfs::Lib that we should disallow
206 # active domains.  However this also has the effect of opening the
207 # disk image in write mode, and in any case we don't use this option
208 # in guestfish any more since we moved all the inspection code into
209 # the core library.  We should drop the fish output modes completely.
210 $rw = 1 if $output eq "fish";
211
212 my $g;
213 my @images;
214 if ($uri) {
215     my ($conn, $dom);
216     ($g, $conn, $dom, @images) =
217         open_guest (\@ARGV, rw => $rw, address => $uri);
218 } else {
219     my ($conn, $dom);
220     ($g, $conn, $dom, @images) =
221         open_guest (\@ARGV, rw => $rw);
222 }
223
224 $g->launch ();
225
226 =head1 OUTPUT FORMAT
227
228  Operating system(s)
229  -------------------
230  Linux (distro + version)
231  Windows (version)
232     |
233     |
234     +--- Filesystems ---------- Installed apps --- Kernel & drivers
235          -----------            --------------     ----------------
236          mount point => device  List of apps       Extra information
237          mount point => device  and versions       about kernel(s)
238               ...                                  and drivers
239          swap => swap device
240          (plus lots of extra information
241          about each filesystem)
242
243 The output of virt-inspector is a complex two-level data structure.
244
245 At the top level is a list of the operating systems installed on the
246 guest.  (For the vast majority of guests, only a single OS is
247 installed.)  The data returned for the OS includes the name (Linux,
248 Windows), the distribution and version.
249
250 The diagram above shows what we return for each OS.
251
252 With the I<--xml> option the output is mapped into an XML document.
253 There is a RELAX-NG schema for this XML in the file
254 I<virt-inspector.rng> which normally ships with virt-inspector, or can
255 be found in the source.
256
257 With the I<--fish> or I<--ro-fish> option the mount points are mapped to
258 L<guestfish(1)> command line parameters, so that you can go in
259 afterwards and inspect the guest with everything mounted in the
260 right place.  For example:
261
262  guestfish $(virt-inspector --ro-fish guest.img)
263  ==> guestfish --ro -a guest.img -m /dev/VG/LV:/ -m /dev/sda1:/boot
264
265 =cut
266
267 # List of possible filesystems.
268 my @partitions = get_partitions ($g);
269
270 # Now query each one to build up a picture of what's in it.
271 my %fses =
272     inspect_all_partitions ($g, \@partitions);
273
274 #print "fses -----------\n";
275 #print Dumper(\%fses);
276
277 my $oses = inspect_operating_systems ($g, \%fses);
278
279 #print "oses -----------\n";
280 #print Dumper($oses);
281
282 # Mount up the disks so we can check for applications
283 # and kernels.  Skip this if the output is "*fish" because
284 # we don't need to know.
285
286 if ($output !~ /.*fish$/) {
287     my $root_dev;
288     foreach $root_dev (sort keys %$oses) {
289         my $os = $oses->{$root_dev};
290         mount_operating_system ($g, $os);
291         inspect_in_detail ($g, $os);
292         $g->umount_all ();
293     }
294 }
295
296 #----------------------------------------------------------------------
297 # Output.
298
299 if ($output eq "fish" || $output eq "ro-fish") {
300     my @osdevs = keys %$oses;
301     # This only works if there is a single OS.
302     die __"--fish output is only possible with a single OS\n" if @osdevs != 1;
303
304     my $root_dev = $osdevs[0];
305
306     if ($output eq "ro-fish") {
307         print "--ro ";
308     }
309
310     foreach (@images) {
311         printf "-a %s ", shell_quote ($_);
312     }
313
314     my $mounts = $oses->{$root_dev}->{mounts};
315     # Have to mount / first.  Luckily '/' is early in the ASCII
316     # character set, so this should be OK.
317     foreach (sort keys %$mounts) {
318         if ($_ ne "swap" && $_ ne "none") {
319             printf "-m %s ", shell_quote ("$mounts->{$_}:$_");
320         }
321     }
322     print "\n"
323 }
324
325 # Perl output.
326 elsif ($output eq "perl") {
327     print Dumper(%$oses);
328 }
329
330 # YAML output
331 elsif ($output eq "yaml") {
332     die __"virt-inspector: no YAML support, try installing perl-YAML or libyaml-perl\n"
333         unless exists $INC{"YAML/Any.pm"};
334
335     print Dump(%$oses);
336 }
337
338 # Plain text output (the default).
339 elsif ($output eq "text") {
340     output_text ();
341 }
342
343 # XML output.
344 elsif ($output eq "xml") {
345     output_xml ();
346 }
347
348 # Query mode.
349 elsif ($output eq "query") {
350     output_query ();
351 }
352
353 sub output_text
354 {
355     output_text_os ($oses->{$_}) foreach sort keys %$oses;
356 }
357
358 sub output_text_os
359 {
360     my $os = shift;
361
362     print $os->{os}, " " if exists $os->{os};
363     print $os->{distro}, " " if exists $os->{distro};
364     print $os->{arch}, " " if exists $os->{arch};
365     print $os->{major_version} if exists $os->{major_version};
366     print ".", $os->{minor_version} if exists $os->{minor_version};
367     print " (", $os->{product_name}, ")" if exists $os->{product_name};
368     print " ";
369     print "on ", $os->{root_device}, ":\n";
370
371     print __"  Mountpoints:\n";
372     my $mounts = $os->{mounts};
373     foreach (sort keys %$mounts) {
374         printf "    %-30s %s\n", $mounts->{$_}, $_
375     }
376
377     print __"  Filesystems:\n";
378     my $filesystems = $os->{filesystems};
379     foreach (sort keys %$filesystems) {
380         print "    $_:\n";
381         print "      label: $filesystems->{$_}{label}\n"
382             if exists $filesystems->{$_}{label};
383         print "      UUID: $filesystems->{$_}{uuid}\n"
384             if exists $filesystems->{$_}{uuid};
385         print "      type: $filesystems->{$_}{fstype}\n"
386             if exists $filesystems->{$_}{fstype};
387         print "      content: $filesystems->{$_}{content}\n"
388             if exists $filesystems->{$_}{content};
389     }
390
391     if (exists $os->{modprobe_aliases}) {
392         my %aliases = %{$os->{modprobe_aliases}};
393         my @keys = sort keys %aliases;
394         if (@keys) {
395             print __"  Modprobe aliases:\n";
396             foreach (@keys) {
397                 printf "    %-30s %s\n", $_, $aliases{$_}->{modulename}
398             }
399         }
400     }
401
402     if (exists $os->{initrd_modules}) {
403         my %modvers = %{$os->{initrd_modules}};
404         my @keys = sort keys %modvers;
405         if (@keys) {
406             print __"  Initrd modules:\n";
407             foreach (@keys) {
408                 my @modules = @{$modvers{$_}};
409                 print "    $_:\n";
410                 print "      $_\n" foreach @modules;
411             }
412         }
413     }
414
415     print __"  Applications:\n";
416     my @apps =  @{$os->{apps}};
417     foreach (@apps) {
418         print "    $_->{name} $_->{version}\n"
419     }
420
421     if ($os->{kernels}) {
422         print __"  Kernels:\n";
423         my @kernels = @{$os->{kernels}};
424         foreach (@kernels) {
425             print "    $_->{version} ($_->{arch})\n";
426             my @modules = @{$_->{modules}};
427             foreach (@modules) {
428                 print "      $_\n";
429             }
430         }
431     }
432
433     if (exists $os->{root}->{registry}) {
434         print __"  Windows Registry entries:\n";
435         # These are just lumps of text - dump them out.
436         foreach (@{$os->{root}->{registry}}) {
437             print "$_\n";
438         }
439     }
440 }
441
442 sub output_xml
443 {
444     my $xml = new XML::Writer(DATA_MODE => 1, DATA_INDENT => 2);
445
446     $xml->startTag("operatingsystems");
447     output_xml_os ($oses->{$_}, $xml) foreach sort keys %$oses;
448     $xml->endTag("operatingsystems");
449
450     $xml->end();
451 }
452
453 sub output_xml_os
454 {
455     my ($os, $xml) = @_;
456
457     $xml->startTag("operatingsystem");
458
459     foreach ( [ "name" => "os" ],
460               [ "distro" => "distro" ],
461               [ "product_name" => "product_name" ],
462               [ "arch" => "arch" ],
463               [ "major_version" => "major_version" ],
464               [ "minor_version" => "minor_version" ],
465               [ "package_format" => "package_format" ],
466               [ "package_management" => "package_management" ],
467               [ "root" => "root_device" ] ) {
468         $xml->dataElement($_->[0], $os->{$_->[1]}) if exists $os->{$_->[1]};
469     }
470
471     $xml->startTag("mountpoints");
472     my $mounts = $os->{mounts};
473     foreach (sort keys %$mounts) {
474         $xml->dataElement("mountpoint", $_, "dev" => $mounts->{$_});
475     }
476     $xml->endTag("mountpoints");
477
478     $xml->startTag("filesystems");
479     my $filesystems = $os->{filesystems};
480     foreach (sort keys %$filesystems) {
481         $xml->startTag("filesystem", "dev" => $_);
482
483         foreach my $field ( [ "label" => "label" ],
484                             [ "uuid" => "uuid" ],
485                             [ "type" => "fstype" ],
486                             [ "content" => "content" ],
487                             [ "spec" => "spec" ] ) {
488             $xml->dataElement($field->[0], $filesystems->{$_}{$field->[1]})
489                 if exists $filesystems->{$_}{$field->[1]};
490         }
491
492         $xml->endTag("filesystem");
493     }
494     $xml->endTag("filesystems");
495
496     if (exists $os->{modprobe_aliases}) {
497         my %aliases = %{$os->{modprobe_aliases}};
498         my @keys = sort keys %aliases;
499         if (@keys) {
500             $xml->startTag("modprobealiases");
501             foreach (@keys) {
502                 $xml->startTag("alias", "device" => $_);
503
504                 foreach my $field ( [ "modulename" => "modulename" ],
505                                     [ "augeas" => "augeas" ],
506                                     [ "file" => "file" ] ) {
507                     $xml->dataElement($field->[0], $aliases{$_}->{$field->[1]});
508                 }
509
510                 $xml->endTag("alias");
511             }
512             $xml->endTag("modprobealiases");
513         }
514     }
515
516     if (exists $os->{initrd_modules}) {
517         my %modvers = %{$os->{initrd_modules}};
518         my @keys = sort keys %modvers;
519         if (@keys) {
520             $xml->startTag("initrds");
521             foreach (@keys) {
522                 my @modules = @{$modvers{$_}};
523                 $xml->startTag("initrd", "version" => $_);
524                 $xml->dataElement("module", $_) foreach @modules;
525                 $xml->endTag("initrd");
526             }
527             $xml->endTag("initrds");
528         }
529     }
530
531     $xml->startTag("applications");
532     my @apps =  @{$os->{apps}};
533     foreach (@apps) {
534         $xml->startTag("application");
535         $xml->dataElement("name", $_->{name});
536         $xml->dataElement("epoch", $_->{epoch}) if defined $_->{epoch};
537         $xml->dataElement("version", $_->{version});
538         $xml->dataElement("release", $_->{release});
539         $xml->dataElement("arch", $_->{arch});
540         $xml->endTag("application");
541     }
542     $xml->endTag("applications");
543
544     if(defined($os->{boot}) && defined($os->{boot}->{configs})) {
545         my $default = $os->{boot}->{default};
546         my $configs = $os->{boot}->{configs};
547
548         $xml->startTag("boot");
549         for(my $i = 0; $i < scalar(@$configs); $i++) {
550             my $config = $configs->[$i];
551
552             my @attrs = ();
553             push(@attrs, ("default" => 1)) if($default == $i);
554             $xml->startTag("config", @attrs);
555             $xml->dataElement("title", $config->{title});
556             $xml->dataElement("kernel", $config->{kernel}->{version})
557                 if(defined($config->{kernel}));
558             $xml->dataElement("cmdline", $config->{cmdline})
559                 if(defined($config->{cmdline}));
560             $xml->endTag("config");
561         }
562         $xml->endTag("boot");
563     }
564
565     if ($os->{kernels}) {
566         $xml->startTag("kernels");
567         my @kernels = @{$os->{kernels}};
568         foreach (@kernels) {
569             $xml->startTag("kernel",
570                            "version" => $_->{version},
571                            "arch" => $_->{arch});
572             $xml->startTag("modules");
573             my @modules = @{$_->{modules}};
574             foreach (@modules) {
575                 $xml->dataElement("module", $_);
576             }
577             $xml->endTag("modules");
578             $xml->dataElement("path", $_->{path}) if(defined($_->{path}));
579             $xml->dataElement("package", $_->{package}) if(defined($_->{package}));
580             $xml->endTag("kernel");
581         }
582         $xml->endTag("kernels");
583     }
584
585     if (exists $os->{root}->{registry}) {
586         $xml->startTag("windowsregistryentries");
587         # These are just lumps of text - dump them out.
588         foreach (@{$os->{root}->{registry}}) {
589             $xml->dataElement("windowsregistryentry", $_);
590         }
591         $xml->endTag("windowsregistryentries");
592     }
593
594     $xml->endTag("operatingsystem");
595 }
596
597 =head1 QUERY MODE
598
599 When you use C<virt-inspector --query>, the output is a series of
600 lines of the form:
601
602  windows=no
603  linux=yes
604  fullvirt=yes
605  xen_pv_drivers=no
606
607 (each answer is usually C<yes> or C<no>, or the line is completely
608 missing if we could not determine the answer at all).
609
610 If the guest is multiboot, you can get apparently conflicting answers
611 (eg. C<windows=yes> and C<linux=yes>, or a guest which is both
612 fullvirt and has a Xen PV kernel).  This is normal, and just means
613 that the guest can do both things, although it might require operator
614 intervention such as selecting a boot option when the guest is
615 booting.
616
617 This section describes the full range of answers possible.
618
619 =over 4
620
621 =cut
622
623 sub output_query
624 {
625     output_query_windows ();
626     output_query_linux ();
627     output_query_rhel ();
628     output_query_fedora ();
629     output_query_debian ();
630     output_query_fullvirt ();
631     output_query_xen_domU_kernel ();
632     output_query_xen_pv_drivers ();
633     output_query_virtio_drivers ();
634     output_query_kernel_arch ();
635     output_query_userspace_arch ();
636 }
637
638 =item windows=(yes|no)
639
640 Answer C<yes> if Microsoft Windows is installed in the guest.
641
642 =cut
643
644 sub output_query_windows
645 {
646     my $windows = "no";
647     foreach my $os (keys %$oses) {
648         $windows="yes" if $oses->{$os}->{os} eq "windows";
649     }
650     print "windows=$windows\n";
651 }
652
653 =item linux=(yes|no)
654
655 Answer C<yes> if a Linux kernel is installed in the guest.
656
657 =cut
658
659 sub output_query_linux
660 {
661     my $linux = "no";
662     foreach my $os (keys %$oses) {
663         $linux="yes" if $oses->{$os}->{os} eq "linux";
664     }
665     print "linux=$linux\n";
666 }
667
668 =item rhel=(yes|no)
669
670 Answer C<yes> if the guest contains Red Hat Enterprise Linux.
671
672 =cut
673
674 sub output_query_rhel
675 {
676     my $rhel = "no";
677     foreach my $os (keys %$oses) {
678         $rhel="yes" if ($oses->{$os}->{os} eq "linux" &&
679                         $oses->{$os}->{distro} eq "rhel");
680     }
681     print "rhel=$rhel\n";
682 }
683
684 =item fedora=(yes|no)
685
686 Answer C<yes> if the guest contains the Fedora Linux distribution.
687
688 =cut
689
690 sub output_query_fedora
691 {
692     my $fedora = "no";
693     foreach my $os (keys %$oses) {
694         $fedora="yes" if $oses->{$os}->{os} eq "linux" && $oses->{$os}->{distro} eq "fedora";
695     }
696     print "fedora=$fedora\n";
697 }
698
699 =item debian=(yes|no)
700
701 Answer C<yes> if the guest contains the Debian Linux distribution.
702
703 =cut
704
705 sub output_query_debian
706 {
707     my $debian = "no";
708     foreach my $os (keys %$oses) {
709         $debian="yes" if $oses->{$os}->{os} eq "linux" && $oses->{$os}->{distro} eq "debian";
710     }
711     print "debian=$debian\n";
712 }
713
714 =item fullvirt=(yes|no)
715
716 Answer C<yes> if there is at least one operating system kernel
717 installed in the guest which runs fully virtualized.  Such a guest
718 would require a hypervisor which supports full system virtualization.
719
720 =cut
721
722 sub output_query_fullvirt
723 {
724     # The assumption is full-virt, unless all installed kernels
725     # are identified as paravirt.
726     # XXX Fails on Windows guests.
727     foreach my $os (keys %$oses) {
728         foreach my $kernel (@{$oses->{$os}->{kernels}}) {
729             my $is_pv = $kernel->{version} =~ m/xen/;
730             unless ($is_pv) {
731                 print "fullvirt=yes\n";
732                 return;
733             }
734         }
735     }
736     print "fullvirt=no\n";
737 }
738
739 =item xen_domU_kernel=(yes|no)
740
741 Answer C<yes> if there is at least one Linux kernel installed in
742 the guest which is compiled as a Xen DomU (a Xen paravirtualized
743 guest).
744
745 =cut
746
747 sub output_query_xen_domU_kernel
748 {
749     foreach my $os (keys %$oses) {
750         foreach my $kernel (@{$oses->{$os}->{kernels}}) {
751             my $is_xen = $kernel->{version} =~ m/xen/;
752             if ($is_xen) {
753                 print "xen_domU_kernel=yes\n";
754                 return;
755             }
756         }
757     }
758     print "xen_domU_kernel=no\n";
759 }
760
761 =item xen_pv_drivers=(yes|no)
762
763 Answer C<yes> if the guest has Xen paravirtualized drivers installed
764 (usually the kernel itself will be fully virtualized, but the PV
765 drivers have been installed by the administrator for performance
766 reasons).
767
768 =cut
769
770 sub output_query_xen_pv_drivers
771 {
772     foreach my $os (keys %$oses) {
773         foreach my $kernel (@{$oses->{$os}->{kernels}}) {
774             foreach my $module (@{$kernel->{modules}}) {
775                 if ($module =~ m/xen-/) {
776                     print "xen_pv_drivers=yes\n";
777                     return;
778                 }
779             }
780         }
781     }
782     print "xen_pv_drivers=no\n";
783 }
784
785 =item virtio_drivers=(yes|no)
786
787 Answer C<yes> if the guest has virtio paravirtualized drivers
788 installed.  Virtio drivers are commonly used to improve the
789 performance of KVM.
790
791 =cut
792
793 sub output_query_virtio_drivers
794 {
795     foreach my $os (keys %$oses) {
796         foreach my $kernel (@{$oses->{$os}->{kernels}}) {
797             foreach my $module (@{$kernel->{modules}}) {
798                 if ($module =~ m/virtio_/) {
799                     print "virtio_drivers=yes\n";
800                     return;
801                 }
802             }
803         }
804     }
805     print "virtio_drivers=no\n";
806 }
807
808 =item userspace_arch=(x86_64|...)
809
810 Print the architecture of userspace.
811
812 NB. For multi-boot VMs this can print several lines.
813
814 =cut
815
816 sub output_query_userspace_arch
817 {
818     my %arches;
819
820     foreach my $os (keys %$oses) {
821         $arches{$oses->{$os}->{arch}} = 1 if exists $oses->{$os}->{arch};
822     }
823
824     foreach (sort keys %arches) {
825         print "userspace_arch=$_\n";
826     }
827 }
828
829 =item kernel_arch=(x86_64|...)
830
831 Print the architecture of the kernel.
832
833 NB. For multi-boot VMs this can print several lines.
834
835 =cut
836
837 sub output_query_kernel_arch
838 {
839     my %arches;
840
841     foreach my $os (keys %$oses) {
842         foreach my $kernel (@{$oses->{$os}->{kernels}}) {
843             $arches{$kernel->{arch}} = 1 if exists $kernel->{arch};
844         }
845     }
846
847     foreach (sort keys %arches) {
848         print "kernel_arch=$_\n";
849     }
850 }
851
852 =back
853
854 =head1 SHELL QUOTING
855
856 Libvirt guest names can contain arbitrary characters, some of which
857 have meaning to the shell such as C<#> and space.  You may need to
858 quote or escape these characters on the command line.  See the shell
859 manual page L<sh(1)> for details.
860
861 =head1 SEE ALSO
862
863 L<guestfs(3)>,
864 L<guestfish(1)>,
865 L<Sys::Guestfs(3)>,
866 L<Sys::Guestfs::Lib(3)>,
867 L<Sys::Virt(3)>,
868 L<http://libguestfs.org/>.
869
870 =head1 AUTHORS
871
872 Richard W.M. Jones L<http://people.redhat.com/~rjones/>
873
874 Matthew Booth L<mbooth@redhat.com>
875
876 =head1 COPYRIGHT
877
878 Copyright (C) 2009 Red Hat Inc.
879
880 This program is free software; you can redistribute it and/or modify
881 it under the terms of the GNU General Public License as published by
882 the Free Software Foundation; either version 2 of the License, or
883 (at your option) any later version.
884
885 This program is distributed in the hope that it will be useful,
886 but WITHOUT ANY WARRANTY; without even the implied warranty of
887 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
888 GNU General Public License for more details.
889
890 You should have received a copy of the GNU General Public License
891 along with this program; if not, write to the Free Software
892 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.