3 # Copyright (C) 2010 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);
26 use File::Temp qw/tempfile/;
29 use Locale::TextDomain 'libguestfs';
35 virt-inspector - Display operating system version and other information about a virtual machine
39 virt-inspector [--connect URI] domname
41 virt-inspector guest.img [guest.img ...]
45 B<virt-inspector> examines a virtual machine or disk image and tries
46 to determine the version of the operating system and other information
47 about the virtual machine.
49 Virt-inspector produces XML output for feeding into other programs.
51 In the normal usage, use C<virt-inspector domname> where C<domname> is
52 the libvirt domain (see: C<virsh list --all>).
54 You can also run virt-inspector directly on disk images from a single
55 virtual machine. Use C<virt-inspector guest.img>. In rare cases a
56 domain has several block devices, in which case you should list them
57 one after another, with the first corresponding to the guest's
58 C</dev/sda>, the second to the guest's C</dev/sdb> and so on.
60 Virt-inspector can only inspect and report upon I<one domain at a
61 time>. To inspect several virtual machines, you have to run
62 virt-inspector several times (for example, from a shell script
65 Because virt-inspector needs direct access to guest images, it won't
66 normally work over remote libvirt connections.
86 Display version number and exit.
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.
105 =item B<--format> raw
107 Specify the format of disk images given on the command line. If this
108 is omitted then the format is autodetected from the content of the
111 If disk images are requested from libvirt, then this program asks
112 libvirt for this information. In this case, the value of the format
113 parameter is ignored.
115 If working with untrusted raw-format guest disk images, you should
116 ensure the format is always specified.
122 GetOptions ("help|?" => \$help,
123 "version" => \$version,
124 "connect|c=s" => \$uri,
125 "format=s" => \$format,
127 pod2usage (1) if $help;
129 my $g = Sys::Guestfs->new ();
130 my %h = $g->version ();
131 print "$h{major}.$h{minor}.$h{release}$h{extra}\n";
134 pod2usage (__"virt-inspector: no image or VM names given") if @ARGV == 0;
137 push @args, address => $uri if defined $uri;
138 push @args, format => $format if defined $format;
139 my $g = open_guest (@args);
143 my @roots = $g->inspect_os ();
145 die __x("{prog}: No operating system could be detected inside this disk image.\n\nThis may be because the file is not a disk image, or is not a virtual machine\nimage, or because the OS type is not understood by libguestfs.\n\nIf you feel this is an error, please file a bug report including as much\ninformation about the disk image as possible.\n",
146 prog => basename ($0));
151 The virt-inspector XML is described precisely in a RELAX NG schema
152 which is supplied with libguestfs. This section is just an overview.
154 The top-level element is E<lt>operatingsystemsE<gt>, and it contains
155 one or more E<lt>operatingsystemE<gt> elements. You would only see
156 more than one E<lt>operatingsystemE<gt> element if the virtual machine
157 is multi-boot, which is vanishingly rare in real world VMs.
159 =head2 E<lt>operatingsystemE<gt>
161 In the E<lt>operatingsystemE<gt> tag are various optional fields that
162 describe the operating system, its architecture, the descriptive
163 "product name" string, the type of OS and so on, as in this example:
167 <root>/dev/sda2</root>
170 <distro>windows</distro>
171 <product_name>Windows 7 Enterprise</product_name>
172 <major_version>6</major_version>
173 <minor_version>1</minor_version>
174 <windows_systemroot>/Windows</windows_systemroot>
176 These fields are derived from the libguestfs inspection API, and
177 you can find more details in L<guestfs(3)/INSPECTION>.
179 The E<lt>rootE<gt> element is the root filesystem device, but from the
180 point of view of libguestfs (block devices may have completely
181 different names inside the VM itself).
185 # Start the XML output.
186 my $xml = new XML::Writer (DATA_MODE => 1, DATA_INDENT => 2);
188 $xml->startTag ("operatingsystems");
191 foreach $root (@roots) {
192 my %fses = $g->inspect_get_mountpoints ($root);
193 my @fses = sort { length $a <=> length $b } keys %fses;
195 $g->mount_ro ($fses{$_}, $_);
198 $xml->startTag ("operatingsystem");
201 $xml->dataElement (root => canonicalize ($root));
203 my ($s, $distro, $major_version);
204 $s = $g->inspect_get_type ($root);
205 $xml->dataElement (name => $s) if $s ne "unknown";
206 $s = $g->inspect_get_arch ($root);
207 $xml->dataElement (arch => $s) if $s ne "unknown";
208 $distro = $g->inspect_get_distro ($root);
209 $xml->dataElement (distro => $distro) if $distro ne "unknown";
210 $s = $g->inspect_get_product_name ($root);
211 $xml->dataElement (product_name => $s) if $s ne "unknown";
212 $major_version = $g->inspect_get_major_version ($root);
213 $xml->dataElement (major_version => $major_version);
214 $s = $g->inspect_get_minor_version ($root);
215 $xml->dataElement (minor_version => $s);
218 $s = $g->inspect_get_windows_systemroot ($root);
219 $xml->dataElement (windows_systemroot => $s);
223 output_mountpoints ($root, \@fses, \%fses);
226 output_filesystems ($root);
228 # Package format / management and applications.
229 output_applications ($root, $distro, $major_version);
231 $xml->endTag("operatingsystem");
236 # End the XML output.
237 $xml->endTag ("operatingsystems");
240 =head2 E<lt>mountpointsE<gt>
242 Un*x-like guests typically have multiple filesystems which are mounted
243 at various mountpoints, and these are described in the
244 E<lt>mountpointsE<gt> element which looks like this:
250 <mountpoint dev="/dev/vg_f13x64/lv_root">/</mountpoint>
251 <mountpoint dev="/dev/sda1">/boot</mountpoint>
254 As with E<lt>rootE<gt>, devices are from the point of view of
255 libguestfs, and may have completely different names inside the guest.
256 Only mountable filesystems appear in this list, not things like swap
261 sub output_mountpoints
268 $xml->startTag ("mountpoints");
270 $xml->dataElement ("mountpoint", $_,
271 dev => canonicalize ($fshash->{$_}));
273 $xml->endTag ("mountpoints");
276 =head2 E<lt>filesystemsE<gt>
278 E<lt>filesystemsE<gt> is like E<lt>mountpointsE<gt> but covers I<all>
279 filesystems belonging to the guest, including swap and empty
280 partitions. (In the rare case of a multi-boot guest, it covers
281 filesystems belonging to this OS or shared by this OS and other OSes).
283 You might see something like this:
289 <filesystem dev="/dev/vg_f13x64/lv_root">
291 <label>Fedora-13-x86_64</label>
292 <uuid>e6a4db1e-15c2-477b-ac2a-699181c396aa</uuid>
295 The optional elements within E<lt>filesystemE<gt> are the filesystem
296 type, the label, and the UUID.
300 sub output_filesystems
305 $xml->startTag ("filesystems");
307 my @fses = $g->inspect_get_filesystems ($root);
310 $xml->startTag ("filesystem",
311 dev => canonicalize ($_));
314 my $type = $g->vfs_type ($_);
315 $xml->dataElement (type => $type)
316 if defined $type && $type ne "";
320 my $label = $g->vfs_label ($_);
321 $xml->dataElement (label => $label)
322 if defined $label && $label ne "";
326 my $uuid = $g->vfs_uuid ($_);
327 $xml->dataElement (uuid => $uuid)
328 if defined $uuid && $uuid ne "";
331 $xml->endTag ("filesystem");
334 $xml->endTag ("filesystems");
337 =head2 E<lt>applicationsE<gt>
339 The related elements E<lt>package_formatE<gt>,
340 E<lt>package_managementE<gt> and E<lt>applicationsE<gt> describe
341 applications installed in the virtual machine. At the moment we are
342 only able to list RPMs and Debian packages installed, but in future we
343 will support other Linux distros and Windows.
345 E<lt>package_formatE<gt>, if present, describes the packaging
346 system used. Typical values would be C<rpm> and C<deb>.
348 E<lt>package_managementE<gt>, if present, describes the package
349 manager. Typical values include C<yum>, C<up2date> and C<apt>
351 E<lt>applicationsE<gt> lists the packages or applications
359 <name>coreutils</name>
360 <version>8.5</version>
364 (The version and release fields may not be available for
369 sub output_applications
374 my $major_version = shift;
376 # Based on the distro, take a guess at the package format
377 # and package management.
378 my ($package_format, $package_management);
379 if (defined $distro) {
380 if ($distro eq "archlinux") {
381 $package_format = "pacman";
382 $package_management = "pacman";
384 elsif ($distro eq "debian" || $distro eq "ubuntu") {
385 $package_format = "deb";
386 $package_management = "apt";
388 elsif ($distro eq "fedora" || $distro eq "meego") {
389 $package_format = "rpm";
390 $package_management = "yum";
392 elsif ($distro eq "gentoo") {
393 $package_format = "ebuild";
394 $package_management = "portage";
396 elsif ($distro eq "pardus") {
397 $package_format = "pisi";
398 $package_management = "pisi";
400 elsif ($distro =~ /redhat/ || $distro =~ /rhel/) {
401 if ($major_version >= 5) {
402 $package_format = "rpm";
403 $package_management = "yum";
405 $package_format = "rpm";
406 $package_management = "up2date";
412 $xml->dataElement (package_format => $package_format)
413 if defined $package_format;
414 $xml->dataElement (package_management => $package_management)
415 if defined $package_management;
417 # Do we know how to get a list of applications?
418 if (defined $package_format) {
419 if ($package_format eq "rpm") {
420 output_applications_rpm ($root);
422 elsif ($package_format eq "deb") {
423 output_applications_deb ($root);
428 sub output_applications_rpm
433 # Previous virt-inspector ran the 'rpm' program from the guest.
434 # This is insecure, and unnecessary because we can get the same
435 # information directly from the RPM database.
440 my ($fh, $filename) = tempfile (UNLINK => 1);
441 my $fddev = "/dev/fd/" . fileno ($fh);
442 $g->download ("/var/lib/rpm/Name", $fddev);
443 close $fh or die "close: $!";
445 # Read the database with the Berkeley DB dump tool.
446 my $cmd = "db_dump -p '$filename'";
447 open PIPE, "$cmd |" or die "close: $!";
450 last if /^HEADER=END$/;
454 last if /^DATA=END$/;
456 # First character on each data line is a space.
457 if (length $_ > 0 && substr ($_, 0, 1) eq ' ') {
460 # Name should never contain non-printable chars.
461 die "name contains non-printable chars" if /\\/;
462 push @applications, $_;
464 $_ = <PIPE>; # discard value
466 close PIPE or die "close: $!";
468 if (!$@ && @applications > 0) {
469 @applications = sort @applications;
470 $xml->startTag ("applications");
471 foreach (@applications) {
472 $xml->startTag ("application");
473 $xml->dataElement (name => $_);
474 $xml->endTag ("application");
476 $xml->endTag ("applications");
480 sub output_applications_deb
488 my ($fh, $filename) = tempfile (UNLINK => 1);
489 my $fddev = "/dev/fd/" . fileno ($fh);
490 $g->download ("/var/lib/dpkg/status", $fddev);
491 close $fh or die "close: $!";
493 # Read the file. Each package is separated by a blank line.
494 open FILE, $filename or die "$filename: $!";
495 my ($name, $installed, $version, $release);
498 if (/^Package: (.*)/) {
500 } elsif (/^Status: .*\binstalled\b/) {
502 } elsif (/^Version: (.*?)-(.*)/) {
507 defined $name && defined $version && defined $release) {
508 push @applications, [ $name, $version, $release ];
516 close FILE or die "$filename: $!";
518 if (!$@ && @applications > 0) {
519 @applications = sort { $a->[0] cmp $b->[0] } @applications;
520 $xml->startTag ("applications");
521 foreach (@applications) {
522 $xml->startTag ("application");
523 $xml->dataElement (name => $_->[0]);
524 $xml->dataElement (version => $_->[1]);
525 $xml->dataElement (release => $_->[2]);
526 $xml->endTag ("application");
528 $xml->endTag ("applications");
532 # The reverse of device name translation, see
533 # BLOCK DEVICE NAMING in guestfs(3).
538 if (m{^/dev/[hv]d([a-z]\d)$}) {
546 You can use the XPath query language, and/or the xpath tool, in order
547 to select parts of the XML.
551 $ virt-inspector Guest | xpath //filesystems
555 <filesystem dev="/dev/vg_f13x64/lv_root">
559 $ virt-inspector Guest | \
560 xpath "string(//filesystem[@dev='/dev/sda1']/type)"
561 Query didn't return a nodeset. Value: ext4
565 Libvirt guest names can contain arbitrary characters, some of which
566 have meaning to the shell such as C<#> and space. You may need to
567 quote or escape these characters on the command line. See the shell
568 manual page L<sh(1)> for details.
575 L<Sys::Guestfs::Lib(3)>,
577 L<http://www.w3.org/TR/xpath/>,
578 L<http://libguestfs.org/>.
586 Richard W.M. Jones L<http://people.redhat.com/~rjones/>
590 Matthew Booth L<mbooth@redhat.com>
596 Copyright (C) 2010 Red Hat Inc.
598 This program is free software; you can redistribute it and/or modify
599 it under the terms of the GNU General Public License as published by
600 the Free Software Foundation; either version 2 of the License, or
601 (at your option) any later version.
603 This program is distributed in the hope that it will be useful,
604 but WITHOUT ANY WARRANTY; without even the implied warranty of
605 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
606 GNU General Public License for more details.
608 You should have received a copy of the GNU General Public License
609 along with this program; if not, write to the Free Software
610 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.