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));
149 # Start the XML output.
150 my $xml = new XML::Writer (DATA_MODE => 1, DATA_INDENT => 2);
152 $xml->startTag ("operatingsystems");
155 foreach $root (@roots) {
156 my %fses = $g->inspect_get_mountpoints ($root);
157 my @fses = sort { length $a <=> length $b } keys %fses;
159 $g->mount_ro ($fses{$_}, $_);
162 $xml->startTag ("operatingsystem");
165 $xml->dataElement (root => canonicalize ($root));
167 my ($s, $distro, $major_version);
168 $s = $g->inspect_get_type ($root);
169 $xml->dataElement (name => $s) if $s ne "unknown";
170 $s = $g->inspect_get_arch ($root);
171 $xml->dataElement (arch => $s) if $s ne "unknown";
172 $distro = $g->inspect_get_distro ($root);
173 $xml->dataElement (distro => $distro) if $distro ne "unknown";
174 $s = $g->inspect_get_product_name ($root);
175 $xml->dataElement (product_name => $s) if $s ne "unknown";
176 $major_version = $g->inspect_get_major_version ($root);
177 $xml->dataElement (major_version => $major_version);
178 $s = $g->inspect_get_minor_version ($root);
179 $xml->dataElement (minor_version => $s);
182 $s = $g->inspect_get_windows_systemroot ($root);
183 $xml->dataElement (windows_systemroot => $s);
187 output_mountpoints ($root, \@fses, \%fses);
190 output_filesystems ($root);
192 # Package format / management and applications.
193 output_applications ($root, $distro, $major_version);
195 $xml->endTag("operatingsystem");
200 # End the XML output.
201 $xml->endTag ("operatingsystems");
204 sub output_mountpoints
211 $xml->startTag ("mountpoints");
213 $xml->dataElement ("mountpoint", $_,
214 dev => canonicalize ($fshash->{$_}));
216 $xml->endTag ("mountpoints");
219 sub output_filesystems
224 $xml->startTag ("filesystems");
226 my @fses = $g->inspect_get_filesystems ($root);
228 $xml->startTag ("filesystem",
229 dev => canonicalize ($_));
232 my $type = $g->vfs_type ($_);
233 $xml->dataElement (type => $type)
234 if defined $type && $type ne "";
238 my $label = $g->vfs_label ($_);
239 $xml->dataElement (label => $label)
240 if defined $label && $label ne "";
244 my $uuid = $g->vfs_uuid ($_);
245 $xml->dataElement (uuid => $uuid)
246 if defined $uuid && $uuid ne "";
249 $xml->endTag ("filesystem");
252 $xml->endTag ("filesystems");
255 sub output_applications
260 my $major_version = shift;
262 # Based on the distro, take a guess at the package format
263 # and package management.
264 my ($package_format, $package_management);
265 if (defined $distro) {
266 if ($distro eq "debian") {
267 $package_format = "deb";
268 $package_management = "apt";
270 elsif ($distro eq "fedora") {
271 $package_format = "rpm";
272 $package_management = "yum";
274 elsif ($distro =~ /redhat/ || $distro =~ /rhel/) {
275 if ($major_version >= 5) {
276 $package_format = "rpm";
277 $package_management = "yum";
279 $package_format = "rpm";
280 $package_management = "up2date";
286 $xml->dataElement (package_format => $package_format)
287 if defined $package_format;
288 $xml->dataElement (package_management => $package_management)
289 if defined $package_management;
291 # Do we know how to get a list of applications?
292 if (defined $package_format) {
293 if ($package_format eq "rpm") {
294 output_applications_rpm ($root);
300 sub output_applications_rpm
305 # Previous virt-inspector ran the 'rpm' program from the guest.
306 # This is insecure, and unnecessary because we can get the same
307 # information directly from the RPM database.
312 my ($fh, $filename) = tempfile (UNLINK => 1);
313 my $fddev = "/dev/fd/" . fileno ($fh);
314 $g->download ("/var/lib/rpm/Name", $fddev);
315 close $fh or die "close: $!";
317 # Read the database with the Berkeley DB dump tool.
318 my $cmd = "db_dump -p '$filename'";
319 open PIPE, "$cmd |" or die "close: $!";
322 last if /^HEADER=END$/;
326 last if /^DATA=END$/;
328 # First character on each data line is a space.
329 if (length $_ > 0 && substr ($_, 0, 1) eq ' ') {
332 # Name should never contain non-printable chars.
333 die "name contains non-printable chars" if /\\/;
334 push @applications, $_;
336 $_ = <PIPE>; # discard value
338 close PIPE or die "close: $!";
340 if (!$@ && @applications > 0) {
341 @applications = sort @applications;
342 $xml->startTag ("applications");
343 foreach (@applications) {
344 $xml->startTag ("application");
345 $xml->dataElement (name => $_);
346 $xml->endTag ("application");
348 $xml->endTag ("applications");
352 # The reverse of device name translation, see
353 # BLOCK DEVICE NAMING in guestfs(3).
358 if (m{^/dev/[hv]d([a-z]\d)$}) {
366 Libvirt guest names can contain arbitrary characters, some of which
367 have meaning to the shell such as C<#> and space. You may need to
368 quote or escape these characters on the command line. See the shell
369 manual page L<sh(1)> for details.
376 L<Sys::Guestfs::Lib(3)>,
378 L<http://libguestfs.org/>.
386 Richard W.M. Jones L<http://people.redhat.com/~rjones/>
390 Matthew Booth L<mbooth@redhat.com>
396 Copyright (C) 2010 Red Hat Inc.
398 This program is free software; you can redistribute it and/or modify
399 it under the terms of the GNU General Public License as published by
400 the Free Software Foundation; either version 2 of the License, or
401 (at your option) any later version.
403 This program is distributed in the hope that it will be useful,
404 but WITHOUT ANY WARRANTY; without even the implied warranty of
405 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
406 GNU General Public License for more details.
408 You should have received a copy of the GNU General Public License
409 along with this program; if not, write to the Free Software
410 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.