summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--HACKING3
-rw-r--r--Makefile.am3
-rw-r--r--configure.ac21
-rw-r--r--inspector/Makefile.am38
-rwxr-xr-xinspector/run-inspector-locally29
-rwxr-xr-xinspector/virt-inspector.pl566
-rw-r--r--libguestfs.spec.in25
8 files changed, 686 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index 2b1e4b2c..d67bddd6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -43,6 +43,7 @@ html/recipes.html
initramfs
initramfs.timestamp
initramfs.*.img
+inspector/virt-inspector.1
install-sh
java/api
java/com_redhat_et_libguestfs_GuestFS.h
diff --git a/HACKING b/HACKING
index df20e2df..f77defef 100644
--- a/HACKING
+++ b/HACKING
@@ -46,6 +46,9 @@ images/
Also contains some files used by the test suite.
+inspector/
+ Virtual machine image inspector (virt-inspector).
+
java/
Java bindings.
diff --git a/Makefile.am b/Makefile.am
index 416fcb18..68934b9f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -34,6 +34,9 @@ endif
if HAVE_JAVA
SUBDIRS += java
endif
+if HAVE_INSPECTOR
+SUBDIRS += inspector
+endif
EXTRA_DIST = \
make-initramfs.sh update-initramfs.sh \
diff --git a/configure.ac b/configure.ac
index 67f763b6..731867f0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -376,6 +376,24 @@ AC_SUBST(JNI_VERSION_INFO)
AM_CONDITIONAL([HAVE_JAVA],[test -n "$JAVAC"])
+dnl Check for Perl modules needed by the inspector.
+missing_perl_modules=no
+for pm in Pod::Usage Getopt::Long Sys::Virt Data::Dumper; do
+ AC_MSG_CHECKING([for $pm])
+ if ! perl -M$pm -e1 >/dev/null 2>&1; then
+ AC_MSG_RESULT([no])
+ missing_perl_modules=yes
+ else
+ AC_MSG_RESULT([yes])
+ fi
+done
+if test "x$missing_perl_modules" = "xyes"; then
+ AC_MSG_WARN([some Perl modules required to compile virt-inspector are missing])
+fi
+
+AM_CONDITIONAL([HAVE_INSPECTOR],
+ [test "x$PERL" != "xno" -a "x$missing_perl_modules" != "xyes"])
+
dnl Run in subdirs.
AC_CONFIG_SUBDIRS([daemon])
@@ -388,6 +406,7 @@ AC_CONFIG_FILES([Makefile src/Makefile fish/Makefile examples/Makefile
python/Makefile
ruby/Makefile ruby/Rakefile
java/Makefile
+ inspector/Makefile
make-initramfs.sh update-initramfs.sh
libguestfs.spec libguestfs.pc
ocaml/META perl/Makefile.PL])
@@ -415,6 +434,8 @@ echo -n "Ruby bindings ....................... "
if test "x$HAVE_RUBY_TRUE" = "x"; then echo "yes"; else echo "no"; fi
echo -n "Java bindings ....................... "
if test "x$HAVE_JAVA_TRUE" = "x"; then echo "yes"; else echo "no"; fi
+echo -n "virt-inspector ...................... "
+if test "x$HAVE_INSPECTOR" = "x"; then echo "yes"; else echo "no"; fi
echo
echo "If any optional component is configured 'no' when you expected 'yes'"
echo "then you should check the preceeding messages."
diff --git a/inspector/Makefile.am b/inspector/Makefile.am
new file mode 100644
index 00000000..528e183a
--- /dev/null
+++ b/inspector/Makefile.am
@@ -0,0 +1,38 @@
+# libguestfs virt-inspector
+# Copyright (C) 2009 Red Hat Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+EXTRA_DIST = \
+ virt-inspector.pl
+
+CLEANFILES = *~
+
+if HAVE_INSPECTOR
+
+man_MANS = virt-inspector.1
+
+virt-inspector.1: virt-inspector.pl
+ $(POD2MAN) \
+ --section 1 \
+ -c "Virtualization Support" \
+ --release "$(PACKAGE_NAME)-$(PACKAGE_VERSION)" \
+ $< > $@
+
+install-data-hook:
+ mkdir -p $(DESTDIR)$(bindir)
+ install -m 0755 virt-inspector.pl $(DESTDIR)$(bindir)/virt-inspector
+
+endif \ No newline at end of file
diff --git a/inspector/run-inspector-locally b/inspector/run-inspector-locally
new file mode 100755
index 00000000..9aebfd7f
--- /dev/null
+++ b/inspector/run-inspector-locally
@@ -0,0 +1,29 @@
+#!/bin/sh -
+# libguestfs inspector
+# Copyright (C) 2009 Red Hat Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+# This script sets up the environment so you can run
+# virt-inspector from the top-level source directory
+# without needing to do 'make install' first.
+#
+# Use it like this:
+# ./inspector/run-inspector-locally [usual virt-inspector args ...]
+
+export LD_LIBRARY_PATH=$(pwd)/src/.libs
+export LIBGUESTFS_PATH=$(pwd)
+export PERL5LIB=$(pwd)/perl/blib/lib:$(pwd)/perl/blib/arch
+perl ./inspector/virt-inspector.pl "$@"
diff --git a/inspector/virt-inspector.pl b/inspector/virt-inspector.pl
new file mode 100755
index 00000000..12851c25
--- /dev/null
+++ b/inspector/virt-inspector.pl
@@ -0,0 +1,566 @@
+#!/usr/bin/perl -w
+# virt-inspector
+# Copyright (C) 2009 Red Hat Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+use warnings;
+use strict;
+
+use Sys::Guestfs;
+use Pod::Usage;
+use Getopt::Long;
+use Data::Dumper;
+
+# Optional:
+eval "use Sys::Virt;";
+
+=encoding utf8
+
+=head1 NAME
+
+virt-inspector - Display OS version, kernel, drivers, mount points, applications, etc. in a virtual machine
+
+=head1 SYNOPSIS
+
+ virt-inspector [--connect URI] domname
+
+ virt-inspector guest.img [guest.img ...]
+
+=head1 DESCRIPTION
+
+B<virt-inspector> examines a virtual machine and tries to determine
+the version of the OS, the kernel version, what drivers are installed,
+whether the virtual machine is fully virtualized (FV) or
+para-virtualized (PV), what applications are installed and more.
+
+Virt-inspector can produce output in several formats, including a
+readable text report, and XML for feeding into other programs.
+
+Virt-inspector should only be run on I<inactive> virtual machines.
+The program tries to determine that the machine is inactive and will
+refuse to run if it thinks you are trying to inspect a running domain.
+
+In the normal usage, use C<virt-inspector domname> where C<domname> is
+the libvirt domain (see: C<virsh list --all>).
+
+You can also run virt-inspector directly on disk images from a single
+virtual machine. Use C<virt-inspector guest.img>. In rare cases a
+domain has several block devices, in which case you should list them
+one after another, with the first corresponding to the guest's
+C</dev/sda>, the second to the guest's C</dev/sdb> and so on.
+
+Virt-inspector can only inspect and report upon I<one domain at a
+time>. To inspect several virtual machines, you have to run
+virt-inspector several times (for example, from a shell script
+for-loop).
+
+Because virt-inspector needs direct access to guest images, it won't
+normally work over remote libvirt connections.
+
+=head1 OPTIONS
+
+=over 4
+
+=cut
+
+my $help;
+
+=item B<--help>
+
+Display brief help.
+
+=cut
+
+my $uri;
+
+=item B<--connect URI> | B<-c URI>
+
+If using libvirt, connect to the given I<URI>. If omitted,
+then we connect to the default libvirt hypervisor.
+
+Libvirt is only used if you specify a C<domname> on the
+command line. If you specify guest block devices directly,
+then libvirt is not used at all.
+
+=cut
+
+my $force;
+
+=item B<--force>
+
+Force reading a particular guest even if it appears to
+be active, or if the guest image is writable. This is
+dangerous and can even corrupt the guest image.
+
+=cut
+
+my $output = "text";
+
+=item B<--text> (default)
+
+=item B<--xml>
+
+=item B<--fish>
+
+=item B<--ro-fish>
+
+Select the output format. The default is a readable text report.
+
+If you select I<--xml> then you get XML output which can be fed
+to other programs.
+
+If you select I<--fish> then we print a L<guestfish(1)> command
+line which will automatically mount up the filesystems on the
+correct mount points. Try this for example:
+
+ eval `virt-inspector --fish guest.img`
+
+I<--ro-fish> is the same, but the I<--ro> option is passed to
+guestfish so that the filesystems are mounted read-only.
+
+=back
+
+=cut
+
+GetOptions ("help|?" => \$help,
+ "connect|c=s" => \$uri,
+ "force" => \$force,
+ "xml" => sub { $output = "xml" },
+ "fish" => sub { $output = "fish" },
+ "guestfish" => sub { $output = "fish" },
+ "ro-fish" => sub { $output = "ro-fish" },
+ "ro-guestfish" => sub { $output = "ro-fish" })
+ or pod2usage (2);
+pod2usage (1) if $help;
+pod2usage ("$0: no image or VM names given") if @ARGV == 0;
+
+# Domain name or guest image(s)?
+
+my @images;
+if (-e $ARGV[0]) {
+ @images = @ARGV;
+
+ # Until we get an 'add_drive_ro' call, we must check that qemu
+ # will only open this image in readonly mode.
+ # XXX Remove this hack at some point ... or at least push it
+ # into libguestfs.
+
+ foreach (@images) {
+ if (! -r $_) {
+ die "guest image $_ does not exist or is not readable\n"
+ } elsif (-w $_ && !$force) {
+ die ("guest image $_ is writable! REFUSING TO PROCEED.\n".
+ "You can use --force to override this BUT that action\n".
+ "MAY CORRUPT THE DISK IMAGE.\n");
+ }
+ }
+} else {
+ die "no libvirt support (install Sys::Virt)"
+ unless exists $INC{"Sys/Virt.pm"};
+
+ pod2usage ("$0: too many domains listed on command line") if @ARGV > 1;
+
+ my $vmm;
+ if (defined $uri) {
+ $vmm = Sys::Virt->new (uri => $uri, readonly => 1);
+ } else {
+ $vmm = Sys::Virt->new (readonly => 1);
+ }
+ die "cannot connect to libvirt $uri\n" unless $vmm;
+
+ my @doms = $vmm->list_defined_domains ();
+ my $dom;
+ foreach (@doms) {
+ if ($_->get_name () eq $ARGV[0]) {
+ $dom = $_;
+ last;
+ }
+ }
+ die "$ARGV[0] is not the name of an inactive libvirt domain\n"
+ unless $dom;
+
+ # Get the names of the image(s).
+ my $xml = $dom->get_xml_description ();
+
+ my $p = new XML::XPath::XMLParser (xml => $xml);
+ my $disks = $p->find ("//devices/disk");
+ print "disks:\n";
+ foreach ($disks->get_nodelist) {
+ print XML::XPath::XMLParser::as_string($_);
+ }
+
+ die "XXX"
+}
+
+# We've now got the list of @images, so feed them to libguestfs.
+my $g = Sys::Guestfs->new ();
+$g->add_drive ($_) foreach @images;
+$g->launch ();
+$g->wait_ready ();
+
+# We want to get the list of LVs and partitions (ie. anything that
+# could contain a filesystem). Discard any partitions which are PVs.
+my @partitions = $g->list_partitions ();
+my @pvs = $g->pvs ();
+sub is_pv {
+ my $t = shift;
+ foreach (@pvs) {
+ return 1 if $_ eq $t;
+ }
+ 0;
+}
+@partitions = grep { ! is_pv ($_) } @partitions;
+
+my @lvs = $g->lvs ();
+
+=head1 OUTPUT FORMAT
+
+ Operating system(s)
+ -------------------
+ Linux (distro + version)
+ Windows (version)
+ |
+ |
+ +--- Filesystems ---------- Installed apps --- Kernel & drivers
+ ----------- -------------- ----------------
+ mount point => device List of apps Extra information
+ mount point => device and versions about kernel(s)
+ ... and drivers
+ swap => swap device
+ (plus lots of extra information
+ about each filesystem)
+
+The output of virt-inspector is a complex two-level data structure.
+
+At the top level is a list of the operating systems installed on the
+guest. (For the vast majority of guests, only a single OS is
+installed.) The data returned for the OS includes the name (Linux,
+Windows), the distribution and version.
+
+The diagram above shows what we return for each OS.
+
+With the I<--xml> option the output is mapped into an XML document.
+Unfortunately there is no clear schema for this document
+(contributions welcome) but you can get an idea of the format by
+looking at other documents and as a last resort the source for this
+program.
+
+With the I<--fish> or I<--ro-fish> option the mount points are mapped to
+L<guestfish(1)> command line parameters, so that you can go in
+afterwards and inspect the guest with everything mounted in the
+right place. For example:
+
+ eval `virt-inspector --ro-fish guest.img`
+ ==> guestfish --ro -a guest.img -m /dev/VG/LV:/ -m /dev/sda1:/boot
+
+=cut
+
+# List of possible filesystems.
+my @devices = sort (@lvs, @partitions);
+
+# Now query each one to build up a picture of what's in it.
+my %fses = map { $_ => check_fs ($_) } @devices;
+
+# Now the complex checking code itself.
+# check_fs takes a device name (LV or partition name) and returns
+# a hashref containing everything we can find out about the device.
+sub check_fs {
+ local $_;
+ my $dev = shift; # LV or partition name.
+
+ my %r; # Result hash.
+
+ # First try 'file(1)' on it.
+ my $file = $g->file ($dev);
+ if ($file =~ /ext2 filesystem data/) {
+ $r{fstype} = "ext2";
+ $r{fsos} = "linux";
+ } elsif ($file =~ /ext3 filesystem data/) {
+ $r{fstype} = "ext3";
+ $r{fsos} = "linux";
+ } elsif ($file =~ /ext4 filesystem data/) {
+ $r{fstype} = "ext4";
+ $r{fsos} = "linux";
+ } elsif ($file =~ m{Linux/i386 swap file}) {
+ $r{fstype} = "swap";
+ $r{fsos} = "linux";
+ $r{is_swap} = 1;
+ }
+
+ # If it's ext2/3/4, then we want the UUID and label.
+ if (exists $r{fstype} && $r{fstype} =~ /^ext/) {
+ $r{uuid} = $g->get_e2uuid ($dev);
+ $r{label} = $g->get_e2label ($dev);
+ }
+
+ # Try mounting it, fnarrr.
+ if (!$r{is_swap}) {
+ $r{is_mountable} = 1;
+ eval { $g->mount_ro ($dev, "/") };
+ if ($@) {
+ # It's not mountable, probably empty or some format
+ # we don't understand.
+ $r{is_mountable} = 0;
+ goto OUT;
+ }
+
+ # Grub /boot?
+ if ($g->is_file ("/grub/menu.lst") ||
+ $g->is_file ("/grub/grub.conf")) {
+ $r{content} = "linux-grub";
+ check_grub (\%r);
+ goto OUT;
+ }
+
+ # Linux root?
+ if ($g->is_dir ("/etc") && $g->is_dir ("/bin") &&
+ $g->is_file ("/etc/fstab")) {
+ $r{content} = "linux-root";
+ $r{is_root} = 1;
+ check_linux_root (\%r);
+ goto OUT;
+ }
+
+ # Linux /usr/local.
+ if ($g->is_dir ("/etc") && $g->is_dir ("/bin") &&
+ $g->is_dir ("/share") && !$g->exists ("/local") &&
+ !$g->is_file ("/etc/fstab")) {
+ $r{content} = "linux-usrlocal";
+ goto OUT;
+ }
+
+ # Linux /usr.
+ if ($g->is_dir ("/etc") && $g->is_dir ("/bin") &&
+ $g->is_dir ("/share") && $g->exists ("/local") &&
+ !$g->is_file ("/etc/fstab")) {
+ $r{content} = "linux-usr";
+ goto OUT;
+ }
+
+ # Windows root?
+ if ($g->is_file ("/AUTOEXEC.BAT") ||
+ $g->is_file ("/autoexec.bat") ||
+ $g->is_dir ("/Program Files") ||
+ $g->is_dir ("/WINDOWS") ||
+ $g->is_file ("/ntldr")) {
+ $r{fstype} = "ntfs"; # XXX this is a guess
+ $r{fsos} = "windows";
+ $r{content} = "windows-root";
+ $r{is_root} = 1;
+ check_windows_root (\%r);
+ goto OUT;
+ }
+ }
+
+ OUT:
+ $g->umount_all ();
+ return \%r;
+}
+
+sub check_linux_root
+{
+ local $_;
+ my $r = shift;
+
+ # Look into /etc to see if we recognise the operating system.
+ if ($g->is_file ("/etc/redhat-release")) {
+ $_ = $g->cat ("/etc/redhat-release");
+ if (/Fedora release (\d+\.\d+)/) {
+ $r->{osdistro} = "fedora";
+ $r->{osversion} = "$1"
+ } elsif (/(Red Hat Enterprise Linux|CentOS|Scientific Linux).*release (\d+).*Update (\d+)/) {
+ $r->{osdistro} = "redhat";
+ $r->{osversion} = "$2.$3";
+ } elsif (/(Red Hat Enterprise Linux|CentOS|Scientific Linux).*release (\d+(?:\.(\d+))?)/) {
+ $r->{osdistro} = "redhat";
+ $r->{osversion} = "$2";
+ } else {
+ $r->{osdistro} = "redhat";
+ }
+ } elsif ($g->is_file ("/etc/debian_version")) {
+ $_ = $g->cat ("/etc/debian_version");
+ if (/(\d+\.\d+)/) {
+ $r->{osdistro} = "debian";
+ $r->{osversion} = "$1";
+ } else {
+ $r->{osdistro} = "debian";
+ }
+ }
+
+ # Parse the contents of /etc/fstab. This is pretty vital so
+ # we can determine where filesystems are supposed to be mounted.
+ eval "\$_ = \$g->cat ('/etc/fstab');";
+ if (!$@ && $_) {
+ my @lines = split /\n/;
+ my @fstab;
+ foreach (@lines) {
+ my @fields = split /[ \t]+/;
+ if (@fields >= 2) {
+ my $spec = $fields[0]; # first column (dev/label/uuid)
+ my $file = $fields[1]; # second column (mountpoint)
+ if ($spec =~ m{^/} ||
+ $spec =~ m{^LABEL=} ||
+ $spec =~ m{^UUID=} ||
+ $file eq "swap") {
+ push @fstab, [$spec, $file]
+ }
+ }
+ }
+ $r->{fstab} = \@fstab if @fstab;
+ }
+}
+
+sub check_windows_root
+{
+ local $_;
+ my $r = shift;
+
+ # XXX Windows version.
+ # List of applications.
+}
+
+sub check_grub
+{
+ local $_;
+ my $r = shift;
+
+ # XXX Kernel versions, grub version.
+}
+
+#print Dumper (\%fses);
+
+# Now find out how many operating systems we've got. Usually just one.
+
+my %oses = ();
+
+foreach (sort keys %fses) {
+ if ($fses{$_}->{is_root}) {
+ my %r = (
+ root => $fses{$_},
+ root_device => $_
+ );
+ get_os_version (\%r);
+ assign_mount_points (\%r);
+ $oses{$_} = \%r;
+ }
+}
+
+sub get_os_version
+{
+ local $_;
+ my $r = shift;
+
+ $r->{os} = $r->{root}->{fsos} if exists $r->{root}->{fsos};
+ $r->{distro} = $r->{root}->{osdistro} if exists $r->{root}->{osdistro};
+ $r->{version} = $r->{root}->{osversion} if exists $r->{root}->{osversion};
+}
+
+sub assign_mount_points
+{
+ local $_;
+ my $r = shift;
+
+ $r->{mounts} = { "/" => $r->{root_device} };
+ $r->{filesystems} = { $r->{root_device} => $r->{root} };
+
+ # Use /etc/fstab if we have it to mount the rest.
+ if (exists $r->{root}->{fstab}) {
+ my @fstab = @{$r->{root}->{fstab}};
+ foreach (@fstab) {
+ my ($spec, $file) = @$_;
+
+ my ($dev, $fs) = find_filesystem ($spec);
+ if ($dev) {
+ $r->{mounts}->{$file} = $dev;
+ $r->{filesystems}->{$dev} = $fs;
+ if (exists $fs->{used}) {
+ $fs->{used}++
+ } else {
+ $fs->{used} = 1
+ }
+ }
+ }
+ }
+}
+
+# Find filesystem by device name, LABEL=.. or UUID=..
+sub find_filesystem
+{
+ local $_ = shift;
+
+ if (/^LABEL=(.*)/) {
+ my $label = $1;
+ foreach (sort keys %fses) {
+ if (exists $fses{$_}->{label} &&
+ $fses{$_}->{label} eq $label) {
+ return ($_, $fses{$_});
+ }
+ }
+ warn "unknown filesystem label $label\n";
+ return ();
+ } elsif (/^UUID=(.*)/) {
+ my $uuid = $1;
+ foreach (sort keys %fses) {
+ if (exists $fses{$_}->{uuid} &&
+ $fses{$_}->{uuid} eq $uuid) {
+ return ($_, $fses{$_});
+ }
+ }
+ warn "unknown filesystem UUID $uuid\n";
+ return ();
+ } else {
+ return ($_, $fses{$_}) if exists $fses{$_};
+ warn "unknown filesystem $_\n";
+ return ();
+ }
+}
+
+print Dumper (\%oses);
+
+
+
+
+
+
+
+=head1 SEE ALSO
+
+L<guestfs(3)>,
+L<guestfish(1)>,
+L<Sys::Guestfs(3)>,
+L<Sys::Virt(3)>
+
+=head1 AUTHOR
+
+Richard W.M. Jones L<http://et.redhat.com/~rjones/>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2009 Red Hat Inc.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
diff --git a/libguestfs.spec.in b/libguestfs.spec.in
index 1830bc5a..dd67b802 100644
--- a/libguestfs.spec.in
+++ b/libguestfs.spec.in
@@ -48,6 +48,9 @@ BuildRequires: java >= 1.5.0
BuildRequires: jpackage-utils
BuildRequires: java-devel
+# For virt-inspector:
+BuildRequires: perl-Sys-Virt
+
# Runtime requires:
Requires: qemu >= 0.10-7
@@ -113,6 +116,22 @@ modifying virtual machine disk images from the command line and shell
scripts.
+%package -n virt-inspector
+Summary: Display OS version, kernel, drivers, etc in a virtual machine
+Group: Development/Tools
+License: GPLv2+
+Requires: %{name} = %{version}-%{release}
+Requires: guestfish
+Requires: perl-Sys-Virt
+
+
+%description -n virt-inspector
+Virt-inspector examines a virtual machine and tries to determine the
+version of the OS, the kernel version, what drivers are installed,
+whether the virtual machine is fully virtualized (FV) or
+para-virtualized (PV), what applications are installed and more.
+
+
%package -n ocaml-%{name}
Summary: OCaml bindings for %{name}
Group: Development/Libraries
@@ -332,6 +351,12 @@ rm -rf $RPM_BUILD_ROOT
%{_mandir}/man1/guestfish.1*
+%files -n virt-inspector
+%defattr(-,root,root,-)
+%{_bindir}/virt-inspector
+%{_mandir}/man1/virt-inspector.1*
+
+
%files -n ocaml-%{name}
%defattr(-,root,root,-)
%doc README