From c5cb65f0aac3298e634b183f73fda6644a158018 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Fri, 19 Nov 2010 12:48:21 +0000 Subject: [PATCH] inspector: Rewrite virt-inspector in C. --- .gitignore | 4 +- Makefile.am | 12 +- configure.ac | 9 +- fish/inspect.c | 14 +- fish/options.h | 3 + inspector/Makefile.am | 51 +++- inspector/example-debian.xml | 3 +- inspector/example-fedora.xml | 1 + inspector/example-ubuntu.xml | 1 + inspector/example-windows.xml | 1 + inspector/run-inspector-locally | 3 +- inspector/virt-inspector | 492 ------------------------------ inspector/virt-inspector.c | 657 ++++++++++++++++++++++++++++++++++++++++ inspector/virt-inspector.pod | 311 +++++++++++++++++++ po/POTFILES.in | 2 +- 15 files changed, 1034 insertions(+), 530 deletions(-) delete mode 100755 inspector/virt-inspector create mode 100644 inspector/virt-inspector.c create mode 100755 inspector/virt-inspector.pod diff --git a/.gitignore b/.gitignore index 16f2925..9a70868 100644 --- a/.gitignore +++ b/.gitignore @@ -137,9 +137,9 @@ images/ubuntu.img images/windows.img initramfs initramfs.timestamp +inspector/virt-inspector inspector/virt-inspector.1 -inspector/virt-inspector.pl -inspector/stamp-inspector-generator +inspector/virt-inspector.static install-sh *.jar java/api diff --git a/Makefile.am b/Makefile.am index 6f17146..ec84cc5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -35,8 +35,8 @@ SUBDIRS += gnulib/tests capitests regressions test-tool # Guestfish. SUBDIRS += fish -# virt-cat (in C). -SUBDIRS += cat +# virt-tools in C. +SUBDIRS += cat inspector # Language bindings. if HAVE_PERL @@ -64,11 +64,7 @@ endif # Unconditional because nothing is built yet. SUBDIRS += csharp -# Virt-inspector, tools and guestmount. -if HAVE_INSPECTOR -SUBDIRS += inspector -endif - +# Perl tools and guestmount. if HAVE_TOOLS SUBDIRS += tools endif @@ -255,5 +251,7 @@ bindist: cp test-tool/libguestfs-test-tool.static $(BINTMPDIR)$(bindir)/libguestfs-test-tool $(MAKE) -C cat virt-cat.static cp cat/virt-cat.static $(BINTMPDIR)$(bindir)/virt-cat + $(MAKE) -C inspector virt-inspector.static + cp inspector/virt-inspector.static $(BINTMPDIR)$(bindir)/virt-inspector (cd $(BINTMPDIR) && tar cf - .) | \ gzip -c -9 > libguestfs-$(VERSION)-$(host_cpu).tar.gz diff --git a/configure.ac b/configure.ac index 5f42651..0ebcc51 100644 --- a/configure.ac +++ b/configure.ac @@ -469,6 +469,7 @@ PKG_CHECK_MODULES([LIBXML2], [libxml-2.0], AC_DEFINE([HAVE_LIBXML2],[1],[libxml2 found at compile time.]) ], [AC_MSG_WARN([libxml2 not found, some core features will be disabled])]) +AM_CONDITIONAL([HAVE_LIBXML2],[test "x$LIBXML2_LIBS" != "x"]) dnl hivex library (highly recommended) dnl This used to be a part of libguestfs, but was spun off into its @@ -787,7 +788,7 @@ AS_IF([test "x$enable_php" != "xno"], ]) AM_CONDITIONAL([HAVE_PHP], [test "x$PHP" != "xno" && test "x$PHPIZE" != "xno"]) -dnl Check for Perl modules needed by virt-df, inspector, etc. +dnl Check for Perl modules needed by Perl virt tools (virt-df, etc.) AS_IF([test "x$PERL" != "xno"], [ missing_perl_modules=no @@ -801,12 +802,10 @@ AS_IF([test "x$PERL" != "xno"], fi done if test "x$missing_perl_modules" = "xyes"; then - AC_MSG_WARN([some Perl modules required to compile virt-inspector and the other virt-* tools are missing]) + AC_MSG_WARN([some Perl modules required to compile the Perl virt-* tools are missing]) fi ]) -AM_CONDITIONAL([HAVE_INSPECTOR], - [test "x$PERL" != "xno" && test "x$missing_perl_modules" != "xyes"]) AM_CONDITIONAL([HAVE_TOOLS], [test "x$PERL" != "xno" && test "x$missing_perl_modules" != "xyes"]) @@ -882,8 +881,6 @@ echo -n "Haskell bindings .................... " if test "x$HAVE_HASKELL_TRUE" = "x"; then echo "yes"; else echo "no"; fi echo -n "PHP bindings ........................ " if test "x$HAVE_PHP_TRUE" = "x"; then echo "yes"; else echo "no"; fi -echo -n "virt-inspector ...................... " -if test "x$HAVE_INSPECTOR_TRUE" = "x"; then echo "yes"; else echo "no"; fi echo -n "virt-* tools ........................ " if test "x$HAVE_TOOLS_TRUE" = "x"; then echo "yes"; else echo "no"; fi echo "supermin appliance .................. $enable_supermin" diff --git a/fish/inspect.c b/fish/inspect.c index 2817112..28c1b88 100644 --- a/fish/inspect.c +++ b/fish/inspect.c @@ -28,8 +28,6 @@ #include "options.h" -static void do_decrypt (void); - /* Global that saves the root device between inspect_mount and * print_inspect_prompt. */ @@ -75,7 +73,7 @@ compare_keys (const void *p1, const void *p2) void inspect_mount (void) { - do_decrypt (); + inspect_do_decrypt (); char **roots = guestfs_inspect_os (g); if (roots == NULL) @@ -96,6 +94,12 @@ inspect_mount (void) root = roots[0]; free (roots); + inspect_mount_root (root); +} + +void +inspect_mount_root (const char *root) +{ char **mountpoints = guestfs_inspect_get_mountpoints (g, root); if (mountpoints == NULL) exit (EXIT_FAILURE); @@ -178,8 +182,8 @@ make_mapname (const char *device, char *mapname, size_t len) * for Fedora whole-disk encryption. WIP to make this work for other * encryption schemes. */ -static void -do_decrypt (void) +void +inspect_do_decrypt (void) { char **partitions = guestfs_list_partitions (g); if (partitions == NULL) diff --git a/fish/options.h b/fish/options.h index 9cb7f4b..155aad8 100644 --- a/fish/options.h +++ b/fish/options.h @@ -104,6 +104,9 @@ struct mp { /* in inspect.c */ extern void inspect_mount (void); extern void print_inspect_prompt (void); +/* (low-level inspection functions, used by virt-inspector only) */ +extern void inspect_do_decrypt (void); +extern void inspect_mount_root (const char *root); /* in key.c */ extern char *read_key (const char *param); diff --git a/inspector/Makefile.am b/inspector/Makefile.am index 5e6828f..0441acc 100644 --- a/inspector/Makefile.am +++ b/inspector/Makefile.am @@ -1,5 +1,5 @@ # libguestfs virt-inspector -# Copyright (C) 2009 Red Hat Inc. +# Copyright (C) 2009-2010 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 @@ -25,22 +25,48 @@ EXAMPLE_XML = \ EXTRA_DIST = \ run-inspector-locally \ - virt-inspector + virt-inspector.pod docdir = @docdir@ dist_doc_DATA = \ virt-inspector.rng \ $(EXAMPLE_XML) -if HAVE_INSPECTOR +if HAVE_LIBXML2 -bin_SCRIPTS = virt-inspector +bin_PROGRAMS = virt-inspector + +SHARED_SOURCE_FILES = \ + ../fish/inspect.c \ + ../fish/keys.c \ + ../fish/options.h \ + ../fish/options.c \ + ../fish/virt.c + +virt_inspector_SOURCES = \ + $(SHARED_SOURCE_FILES) \ + virt-inspector.c + +virt_inspector_CFLAGS = \ + -I$(top_srcdir)/src -I$(top_builddir)/src \ + -I$(top_srcdir)/fish \ + -I$(srcdir)/../gnulib/lib -I../gnulib/lib \ + -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ + $(LIBXML2_CFLAGS) \ + $(WARN_CFLAGS) $(WERROR_CFLAGS) + +virt_inspector_LDADD = \ + $(top_builddir)/src/libguestfs.la \ + $(LIBXML2_LIBS) \ + ../gnulib/lib/libgnu.la + +# Manual pages and HTML files for the website. man_MANS = virt-inspector.1 noinst_DATA = \ $(top_builddir)/html/virt-inspector.1.html -virt-inspector.1: virt-inspector +virt-inspector.1: virt-inspector.pod $(POD2MAN) \ --section 1 \ -c "Virtualization Support" \ @@ -63,6 +89,8 @@ TESTS_ENVIRONMENT = $(XMLLINT) --noout --relaxng virt-inspector.rng endif +endif + # These rules require virt-inspector so there is no good time that we # can run them. Instead you can run them by hand. @@ -75,13 +103,8 @@ example-ubuntu.xml: example-windows.xml: ./run-inspector-locally ../images/windows.img | fgrep -v '' > $@ -endif - -# Make symlink from virt-inspector.pl to virt-inspector. This is just -# to keep xgettext happy since it uses the file extension to determine -# the implementation language of a file. - -all-local: virt-inspector.pl +# Build a partly-static library (for the binary distribution). -virt-inspector.pl: virt-inspector - ln -sf $< $@ +virt-inspector.static$(EXEEXT): $(virt_inspector_OBJECTS) $(virt_inspector_DEPENDENCIES) + $(top_srcdir)/relink-static.sh \ + $(virt_inspector_LINK) $(virt_inspector_OBJECTS) -static $(virt_inspector_LDADD) $(virt_inspector_LIBS) $(LIBVIRT_LIBS) $(LIBXML2_LIBS) -lpcre -lhivex -lmagic -lz -lm diff --git a/inspector/example-debian.xml b/inspector/example-debian.xml index 185cf0f..cead573 100644 --- a/inspector/example-debian.xml +++ b/inspector/example-debian.xml @@ -1,3 +1,4 @@ + /dev/debian/root @@ -13,8 +14,8 @@ / /usr /var - /home /boot + /home diff --git a/inspector/example-fedora.xml b/inspector/example-fedora.xml index d790513..6e03a12 100644 --- a/inspector/example-fedora.xml +++ b/inspector/example-fedora.xml @@ -1,3 +1,4 @@ + /dev/VG/Root diff --git a/inspector/example-ubuntu.xml b/inspector/example-ubuntu.xml index c916744..61338e1 100644 --- a/inspector/example-ubuntu.xml +++ b/inspector/example-ubuntu.xml @@ -1,3 +1,4 @@ + /dev/sda2 diff --git a/inspector/example-windows.xml b/inspector/example-windows.xml index 2fe4bf5..346b2ed 100644 --- a/inspector/example-windows.xml +++ b/inspector/example-windows.xml @@ -1,3 +1,4 @@ + /dev/sda2 diff --git a/inspector/run-inspector-locally b/inspector/run-inspector-locally index 5a9c998..e5c9f4a 100755 --- a/inspector/run-inspector-locally +++ b/inspector/run-inspector-locally @@ -47,6 +47,5 @@ $path = abs_path(dirname($path).'/..'); $ENV{LD_LIBRARY_PATH} = $path.'/src/.libs'; $ENV{LIBGUESTFS_PATH} = $path.'/appliance'; -$ENV{PERL5LIB} = $path.'/perl/blib/lib:'.$path.'/perl/blib/arch'; -exec('perl', $path.'/inspector/virt-inspector', @ARGV); +exec($path.'/inspector/virt-inspector', @ARGV); diff --git a/inspector/virt-inspector b/inspector/virt-inspector deleted file mode 100755 index c721b1d..0000000 --- a/inspector/virt-inspector +++ /dev/null @@ -1,492 +0,0 @@ -#!/usr/bin/perl -w -# virt-inspector -# Copyright (C) 2010 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 Sys::Guestfs::Lib qw(open_guest); -use Pod::Usage; -use Getopt::Long; -use File::Temp qw/tempfile/; -use File::Basename; -use XML::Writer; -use Locale::TextDomain 'libguestfs'; - -=encoding utf8 - -=head1 NAME - -virt-inspector - Display operating system version and other information about a virtual machine - -=head1 SYNOPSIS - - virt-inspector [--connect URI] domname - - virt-inspector guest.img [guest.img ...] - -=head1 DESCRIPTION - -B examines a virtual machine or disk image and tries -to determine the version of the operating system and other information -about the virtual machine. - -Virt-inspector produces XML output for feeding into other programs. - -In the normal usage, use C where C is -the libvirt domain (see: C). - -You can also run virt-inspector directly on disk images from a single -virtual machine. Use C. 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, the second to the guest's C and so on. - -Virt-inspector can only inspect and report upon I. 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 $version; - -=item B<--version> - -Display version number and exit. - -=cut - -my $uri; - -=item B<--connect URI> | B<-c URI> - -If using libvirt, connect to the given I. If omitted, -then we connect to the default libvirt hypervisor. - -Libvirt is only used if you specify a C on the -command line. If you specify guest block devices directly, -then libvirt is not used at all. - -=cut - -my $format; - -=item B<--format> raw - -Specify the format of disk images given on the command line. If this -is omitted then the format is autodetected from the content of the -disk image. - -If disk images are requested from libvirt, then this program asks -libvirt for this information. In this case, the value of the format -parameter is ignored. - -If working with untrusted raw-format guest disk images, you should -ensure the format is always specified. - -=back - -=cut - -GetOptions ("help|?" => \$help, - "version" => \$version, - "connect|c=s" => \$uri, - "format=s" => \$format, - ) or pod2usage (2); -pod2usage (1) if $help; -if ($version) { - my $g = Sys::Guestfs->new (); - my %h = $g->version (); - print "$h{major}.$h{minor}.$h{release}$h{extra}\n"; - exit -} -pod2usage (__"virt-inspector: no image or VM names given") if @ARGV == 0; - -my @args = (\@ARGV); -push @args, address => $uri if defined $uri; -push @args, format => $format if defined $format; -my $g = open_guest (@args); - -$g->launch (); - -my @roots = $g->inspect_os (); -if (@roots == 0) { - 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", - prog => basename ($0)); -} - -=head1 XML FORMAT - -The virt-inspector XML is described precisely in a RELAX NG schema -which is supplied with libguestfs. This section is just an overview. - -The top-level element is EoperatingsystemsE, and it contains -one or more EoperatingsystemE elements. You would only see -more than one EoperatingsystemE element if the virtual machine -is multi-boot, which is vanishingly rare in real world VMs. - -=head2 EoperatingsystemE - -In the EoperatingsystemE tag are various optional fields that -describe the operating system, its architecture, the descriptive -"product name" string, the type of OS and so on, as in this example: - - - - /dev/sda2 - windows - i386 - windows - Windows 7 Enterprise - 6 - 1 - /Windows - -These fields are derived from the libguestfs inspection API, and -you can find more details in L. - -The ErootE element is the root filesystem device, but from the -point of view of libguestfs (block devices may have completely -different names inside the VM itself). - -=cut - -# Start the XML output. -my $xml = new XML::Writer (DATA_MODE => 1, DATA_INDENT => 2); - -$xml->startTag ("operatingsystems"); - -my $root; -foreach $root (@roots) { - # Note that output_applications requires the filesystems - # to be mounted up. - my %fses = $g->inspect_get_mountpoints ($root); - my @fses = sort { length $a <=> length $b } keys %fses; - foreach (@fses) { - $g->mount_ro ($fses{$_}, $_); - } - - $xml->startTag ("operatingsystem"); - - # Basic OS fields. - $xml->dataElement (root => canonicalize ($root)); - - my $s; - $s = $g->inspect_get_type ($root); - $xml->dataElement (name => $s) if $s ne "unknown"; - $s = $g->inspect_get_arch ($root); - $xml->dataElement (arch => $s) if $s ne "unknown"; - $s = $g->inspect_get_distro ($root); - $xml->dataElement (distro => $s) if $s ne "unknown"; - $s = $g->inspect_get_product_name ($root); - $xml->dataElement (product_name => $s) if $s ne "unknown"; - $s = $g->inspect_get_major_version ($root); - $xml->dataElement (major_version => $s); - $s = $g->inspect_get_minor_version ($root); - $xml->dataElement (minor_version => $s); - $s = $g->inspect_get_package_format ($root); - $xml->dataElement (package_format => $s) if $s ne "unknown"; - $s = $g->inspect_get_package_management ($root); - $xml->dataElement (package_management => $s) if $s ne "unknown"; - - eval { - $s = $g->inspect_get_windows_systemroot ($root); - $xml->dataElement (windows_systemroot => $s); - }; - - # Mountpoints. - output_mountpoints ($root, \@fses, \%fses); - - # Filesystems. - output_filesystems ($root); - - # Package format / management and applications. - output_applications ($root); - - $xml->endTag("operatingsystem"); - - $g->umount_all (); -} - -# End the XML output. -$xml->endTag ("operatingsystems"); -$xml->end (); - -=head2 EmountpointsE - -Un*x-like guests typically have multiple filesystems which are mounted -at various mountpoints, and these are described in the -EmountpointsE element which looks like this: - - - - ... - - / - /boot - - -As with ErootE, devices are from the point of view of -libguestfs, and may have completely different names inside the guest. -Only mountable filesystems appear in this list, not things like swap -devices. - -=cut - -sub output_mountpoints -{ - local $_; - my $root = shift; - my $fskeys = shift; - my $fshash = shift; - - $xml->startTag ("mountpoints"); - foreach (@$fskeys) { - $xml->dataElement ("mountpoint", $_, - dev => canonicalize ($fshash->{$_})); - } - $xml->endTag ("mountpoints"); -} - -=head2 EfilesystemsE - -EfilesystemsE is like EmountpointsE but covers I -filesystems belonging to the guest, including swap and empty -partitions. (In the rare case of a multi-boot guest, it covers -filesystems belonging to this OS or shared by this OS and other OSes). - -You might see something like this: - - - - ... - - - ext4 - - e6a4db1e-15c2-477b-ac2a-699181c396aa - - -The optional elements within EfilesystemE are the filesystem -type, the label, and the UUID. - -=cut - -sub output_filesystems -{ - local $_; - my $root = shift; - - $xml->startTag ("filesystems"); - - my @fses = $g->inspect_get_filesystems ($root); - @fses = sort @fses; - foreach (@fses) { - $xml->startTag ("filesystem", - dev => canonicalize ($_)); - - eval { - my $type = $g->vfs_type ($_); - $xml->dataElement (type => $type) - if defined $type && $type ne ""; - }; - - eval { - my $label = $g->vfs_label ($_); - $xml->dataElement (label => $label) - if defined $label && $label ne ""; - }; - - eval { - my $uuid = $g->vfs_uuid ($_); - $xml->dataElement (uuid => $uuid) - if defined $uuid && $uuid ne ""; - }; - - $xml->endTag ("filesystem"); - } - - $xml->endTag ("filesystems"); -} - -=head2 EapplicationsE - -The related elements Epackage_formatE, -Epackage_managementE and EapplicationsE describe -applications installed in the virtual machine. At the moment we are -only able to list RPMs and Debian packages installed, but in future we -will support other Linux distros and Windows. - -Epackage_formatE, if present, describes the packaging -system used. Typical values would be C and C. - -Epackage_managementE, if present, describes the package -manager. Typical values include C, C and C - -EapplicationsE lists the packages or applications -installed. - - - - ... - - - coreutils - 8.5 - 1 - - -The version and release fields may not be available for some types -guests. Other fields are possible, see -L. - -=cut - -sub output_applications -{ - local $_; - my $root = shift; - - my @apps = $g->inspect_list_applications ($root); - - if (@apps) { - $xml->startTag ("applications"); - foreach (@apps) { - $xml->startTag ("application"); - $xml->dataElement (name => $_->{app_name}); - $xml->dataElement (display_name => $_->{app_display_name}) - if $_->{app_display_name} ne ""; - $xml->dataElement (epoch => $_->{app_epoch}) - if $_->{app_epoch} != 0; - $xml->dataElement (version => $_->{app_version}) - if $_->{app_version} ne ""; - $xml->dataElement (release => $_->{app_release}) - if $_->{app_release} ne ""; - $xml->dataElement (install_path => $_->{app_install_path}) - if $_->{app_install_path} ne ""; - $xml->dataElement (publisher => $_->{app_publisher}) - if $_->{app_publisher} ne ""; - $xml->dataElement (url => $_->{app_url}) - if $_->{app_url} ne ""; - $xml->dataElement (source_package => $_->{app_source_package}) - if $_->{app_source_package} ne ""; - $xml->dataElement (summary => $_->{app_summary}) - if $_->{app_summary} ne ""; - $xml->dataElement (description => $_->{app_description}) - if $_->{app_description} ne ""; - $xml->endTag ("application"); - } - $xml->endTag ("applications"); - } -} - -# The reverse of device name translation, see -# BLOCK DEVICE NAMING in guestfs(3). -sub canonicalize -{ - local $_ = shift; - - if (m{^/dev/[hv]d([a-z]\d)$}) { - return "/dev/sd$1"; - } - $_; -} - -=head1 USING XPATH - -You can use the XPath query language, and/or the xpath tool, in order -to select parts of the XML. - -For example: - - $ virt-inspector Guest | xpath //filesystems - Found 1 nodes: - -- NODE -- - - - ext4 - [etc] - - $ virt-inspector Guest | \ - xpath "string(//filesystem[@dev='/dev/sda1']/type)" - Query didn't return a nodeset. Value: ext4 - -=head1 SHELL QUOTING - -Libvirt guest names can contain arbitrary characters, some of which -have meaning to the shell such as C<#> and space. You may need to -quote or escape these characters on the command line. See the shell -manual page L for details. - -=head1 SEE ALSO - -L, -L, -L, -L, -L, -L, -L. - -=head1 AUTHORS - -=over 4 - -=item * - -Richard W.M. Jones L - -=item * - -Matthew Booth L - -=back - -=head1 COPYRIGHT - -Copyright (C) 2010 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/inspector/virt-inspector.c b/inspector/virt-inspector.c new file mode 100644 index 0000000..a3c0a04 --- /dev/null +++ b/inspector/virt-inspector.c @@ -0,0 +1,657 @@ +/* virt-inspector + * Copyright (C) 2010 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. + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "progname.h" +#include "c-ctype.h" + +#include "guestfs.h" +#include "options.h" + +/* Currently open libguestfs handle. */ +guestfs_h *g; + +int read_only = 1; +int verbose = 0; +int keys_from_stdin = 0; +int echo_keys = 0; +const char *libvirt_uri = NULL; +int inspector = 1; + +static void output (char **roots); +static void output_roots (xmlTextWriterPtr xo, char **roots); +static void output_root (xmlTextWriterPtr xo, char *root); +static void output_mountpoints (xmlTextWriterPtr xo, char *root); +static void output_filesystems (xmlTextWriterPtr xo, char *root); +static void output_applications (xmlTextWriterPtr xo, char *root); +static void canonicalize (char *dev); +static void free_strings (char **argv); +static int count_strings (char *const*argv); + +static inline char * +bad_cast (char const *s) +{ + return (char *) s; +} + +static void __attribute__((noreturn)) +usage (int status) +{ + if (status != EXIT_SUCCESS) + fprintf (stderr, _("Try `%s --help' for more information.\n"), + program_name); + else { + fprintf (stdout, + _("%s: display information about a virtual machine\n" + "Copyright (C) 2010 Red Hat Inc.\n" + "Usage:\n" + " %s [--options] -d domname file [file ...]\n" + " %s [--options] -a disk.img [-a disk.img ...] file [file ...]\n" + "Options:\n" + " -a|--add image Add image\n" + " -c|--connect uri Specify libvirt URI for -d option\n" + " -d|--domain guest Add disks from libvirt guest\n" + " --echo-keys Don't turn off echo for passphrases\n" + " --format[=raw|..] Force disk format for -a option\n" + " --help Display brief help\n" + " --keys-from-stdin Read passphrases from stdin\n" + " -v|--verbose Verbose messages\n" + " -V|--version Display version and exit\n" + " -x Echo each command before executing it\n" + "For more information, see the manpage %s(1).\n"), + program_name, program_name, program_name, + program_name); + } + exit (status); +} + +int +main (int argc, char *argv[]) +{ + /* Set global program name that is not polluted with libtool artifacts. */ + set_program_name (argv[0]); + + setlocale (LC_ALL, ""); + bindtextdomain (PACKAGE, LOCALEBASEDIR); + textdomain (PACKAGE); + + enum { HELP_OPTION = CHAR_MAX + 1 }; + + static const char *options = "a:c:d:vVx"; + static const struct option long_options[] = { + { "add", 1, 0, 'a' }, + { "connect", 1, 0, 'c' }, + { "domain", 1, 0, 'd' }, + { "echo-keys", 0, 0, 0 }, + { "format", 2, 0, 0 }, + { "help", 0, 0, HELP_OPTION }, + { "keys-from-stdin", 0, 0, 0 }, + { "verbose", 0, 0, 'v' }, + { "version", 0, 0, 'V' }, + { 0, 0, 0, 0 } + }; + struct drv *drvs = NULL; + struct drv *drv; + char *p, *file = NULL; + const char *format = NULL; + int c; + int option_index; + int next_prepared_drive = 1; + + g = guestfs_create (); + if (g == NULL) { + fprintf (stderr, _("guestfs_create: failed to create handle\n")); + exit (EXIT_FAILURE); + } + + argv[0] = bad_cast (program_name); + + for (;;) { + c = getopt_long (argc, argv, options, long_options, &option_index); + if (c == -1) break; + + switch (c) { + case 0: /* options which are long only */ + if (STREQ (long_options[option_index].name, "keys-from-stdin")) { + keys_from_stdin = 1; + } else if (STREQ (long_options[option_index].name, "echo-keys")) { + echo_keys = 1; + } else if (STREQ (long_options[option_index].name, "format")) { + if (!optarg || STREQ (optarg, "")) + format = NULL; + else + format = optarg; + } else { + fprintf (stderr, _("%s: unknown long option: %s (%d)\n"), + program_name, long_options[option_index].name, option_index); + exit (EXIT_FAILURE); + } + break; + + case 'a': + OPTION_a; + break; + + case 'c': + OPTION_c; + break; + + case 'd': + OPTION_d; + break; + + case 'h': + usage (EXIT_SUCCESS); + + case 'v': + OPTION_v; + break; + + case 'V': + OPTION_V; + break; + + case 'x': + OPTION_x; + break; + + case HELP_OPTION: + usage (EXIT_SUCCESS); + + default: + usage (EXIT_FAILURE); + } + } + + /* Old-style syntax? There were no -a or -d options in the old + * virt-inspector which is how we detect this. + */ + if (drvs == NULL) { + while (optind < argc) { + if (strchr (argv[optind], '/') || + access (argv[optind], F_OK) == 0) { /* simulate -a option */ + drv = malloc (sizeof (struct drv)); + if (!drv) { + perror ("malloc"); + exit (EXIT_FAILURE); + } + drv->type = drv_a; + drv->a.filename = argv[optind]; + drv->a.format = NULL; + drv->next = drvs; + drvs = drv; + } else { /* simulate -d option */ + drv = malloc (sizeof (struct drv)); + if (!drv) { + perror ("malloc"); + exit (EXIT_FAILURE); + } + drv->type = drv_d; + drv->d.guest = argv[optind]; + drv->next = drvs; + drvs = drv; + } + + optind++; + } + } + + /* These are really constants, but they have to be variables for the + * options parsing code. Assert here that they have known-good + * values. + */ + assert (read_only == 1); + assert (inspector == 1); + + /* Must be no extra arguments on the command line. */ + if (optind != argc) + usage (EXIT_FAILURE); + + /* User must have specified some drives. */ + if (drvs == NULL) + usage (EXIT_FAILURE); + + /* Add drives, inspect and mount. Note that inspector is always true, + * and there is no -m option. + */ + add_drives (drvs, 'a'); + + if (guestfs_launch (g) == -1) + exit (EXIT_FAILURE); + + /* Free up data structures, no longer needed after this point. */ + free_drives (drvs); + + /* NB. Can't call inspect_mount () here (ie. normal processing of + * the -i option) because it can only handle a single root. So we + * use low-level APIs. + */ + inspect_do_decrypt (); + + char **roots = guestfs_inspect_os (g); + if (roots == NULL) { + fprintf (stderr, _("%s: 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\nNOTE for Red Hat Enterprise Linux 6 users: for Windows guest support you must\ninstall the separate libguestfs-winsupport package.\n\nIf you feel this is an error, please file a bug report including as much\ninformation about the disk image as possible.\n"), + program_name); + exit (EXIT_FAILURE); + } + + output (roots); + + free_strings (roots); + + guestfs_close (g); + + exit (EXIT_SUCCESS); +} + +#define DISABLE_GUESTFS_ERRORS_FOR(stmt) do { \ + guestfs_error_handler_cb old_error_cb; \ + void *old_error_data; \ + old_error_cb = guestfs_get_error_handler (g, &old_error_data); \ + guestfs_set_error_handler (g, NULL, NULL); \ + stmt; \ + guestfs_set_error_handler (g, old_error_cb, old_error_data); \ + } while (0) + +#define XMLERROR(code,e) do { \ + if ((e) == (code)) { \ + fprintf (stderr, _("%s: XML write error at \"%s\": %m\n"), \ + #e, program_name); \ + exit (EXIT_FAILURE); \ + } \ + } while (0) + +static void +output (char **roots) +{ + xmlOutputBufferPtr ob = xmlOutputBufferCreateFd (1, NULL); + if (ob == NULL) { + fprintf (stderr, + _("%s: xmlOutputBufferCreateFd: failed to open stdout\n"), + program_name); + exit (EXIT_FAILURE); + } + + xmlTextWriterPtr xo = xmlNewTextWriter (ob); + if (xo == NULL) { + fprintf (stderr, + _("%s: xmlNewTextWriter: failed to create libxml2 writer\n"), + program_name); + exit (EXIT_FAILURE); + } + + /* Pretty-print the output. */ + XMLERROR (-1, xmlTextWriterSetIndent (xo, 1)); + XMLERROR (-1, xmlTextWriterSetIndentString (xo, BAD_CAST " ")); + + XMLERROR (-1, xmlTextWriterStartDocument (xo, NULL, NULL, NULL)); + output_roots (xo, roots); + XMLERROR (-1, xmlTextWriterEndDocument (xo)); + + /* 'ob' is freed by this too. */ + xmlFreeTextWriter (xo); +} + +static void +output_roots (xmlTextWriterPtr xo, char **roots) +{ + size_t i; + + XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "operatingsystems")); + for (i = 0; roots[i] != NULL; ++i) + output_root (xo, roots[i]); + XMLERROR (-1, xmlTextWriterEndElement (xo)); +} + +static void +output_root (xmlTextWriterPtr xo, char *root) +{ + char *str; + int i; + char buf[32]; + char canonical_root[strlen (root) + 1]; + + XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "operatingsystem")); + + strcpy (canonical_root, root); + canonicalize (canonical_root); + XMLERROR (-1, + xmlTextWriterWriteElement (xo, BAD_CAST "root", BAD_CAST canonical_root)); + + str = guestfs_inspect_get_type (g, root); + if (!str) exit (EXIT_FAILURE); + if (STRNEQ (str, "unknown")) + XMLERROR (-1, + xmlTextWriterWriteElement (xo, BAD_CAST "name", BAD_CAST str)); + free (str); + + str = guestfs_inspect_get_arch (g, root); + if (!str) exit (EXIT_FAILURE); + if (STRNEQ (str, "unknown")) + XMLERROR (-1, + xmlTextWriterWriteElement (xo, BAD_CAST "arch", BAD_CAST str)); + free (str); + + str = guestfs_inspect_get_distro (g, root); + if (!str) exit (EXIT_FAILURE); + if (STRNEQ (str, "unknown")) + XMLERROR (-1, + xmlTextWriterWriteElement (xo, BAD_CAST "distro", BAD_CAST str)); + free (str); + + str = guestfs_inspect_get_product_name (g, root); + if (!str) exit (EXIT_FAILURE); + if (STRNEQ (str, "unknown")) + XMLERROR (-1, + xmlTextWriterWriteElement (xo, BAD_CAST "product_name", BAD_CAST str)); + free (str); + + i = guestfs_inspect_get_major_version (g, root); + snprintf (buf, sizeof buf, "%d", i); + XMLERROR (-1, + xmlTextWriterWriteElement (xo, BAD_CAST "major_version", BAD_CAST buf)); + i = guestfs_inspect_get_minor_version (g, root); + snprintf (buf, sizeof buf, "%d", i); + XMLERROR (-1, + xmlTextWriterWriteElement (xo, BAD_CAST "minor_version", BAD_CAST buf)); + + str = guestfs_inspect_get_package_format (g, root); + if (!str) exit (EXIT_FAILURE); + if (STRNEQ (str, "unknown")) + XMLERROR (-1, + xmlTextWriterWriteElement (xo, BAD_CAST "package_format", BAD_CAST str)); + free (str); + + str = guestfs_inspect_get_package_management (g, root); + if (!str) exit (EXIT_FAILURE); + if (STRNEQ (str, "unknown")) + XMLERROR (-1, + xmlTextWriterWriteElement (xo, BAD_CAST "package_management", + BAD_CAST str)); + free (str); + + /* inspect-get-windows-systemroot will fail with non-windows guests, + * or if the systemroot could not be determined for a windows guest. + * Disable error output around this call. + */ + DISABLE_GUESTFS_ERRORS_FOR ( + str = guestfs_inspect_get_windows_systemroot (g, root); + if (str) + XMLERROR (-1, + xmlTextWriterWriteElement (xo, BAD_CAST "windows_systemroot", + BAD_CAST str)); + free (str); + ); + + output_mountpoints (xo, root); + + output_filesystems (xo, root); + + output_applications (xo, root); + + XMLERROR (-1, xmlTextWriterEndElement (xo)); +} + +static int +compare_keys (const void *p1, const void *p2) +{ + const char *key1 = * (char * const *) p1; + const char *key2 = * (char * const *) p2; + + return strcmp (key1, key2); +} + +static int +compare_keys_len (const void *p1, const void *p2) +{ + const char *key1 = * (char * const *) p1; + const char *key2 = * (char * const *) p2; + int c; + + c = strlen (key1) - strlen (key2); + if (c != 0) + return c; + + return compare_keys (p1, p2); +} + +static void +output_mountpoints (xmlTextWriterPtr xo, char *root) +{ + char **mountpoints; + size_t i; + + mountpoints = guestfs_inspect_get_mountpoints (g, root); + if (mountpoints == NULL) + exit (EXIT_FAILURE); + + /* Sort by key length, shortest key first, and then name, so the + * output is stable. + */ + qsort (mountpoints, count_strings (mountpoints) / 2, 2 * sizeof (char *), + compare_keys_len); + + XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "mountpoints")); + + for (i = 0; mountpoints[i] != NULL; i += 2) { + canonicalize (mountpoints[i+1]); + + XMLERROR (-1, + xmlTextWriterStartElement (xo, BAD_CAST "mountpoint")); + XMLERROR (-1, + xmlTextWriterWriteAttribute (xo, BAD_CAST "dev", + BAD_CAST mountpoints[i+1])); + XMLERROR (-1, + xmlTextWriterWriteString (xo, BAD_CAST mountpoints[i])); + XMLERROR (-1, xmlTextWriterEndElement (xo)); + } + + XMLERROR (-1, xmlTextWriterEndElement (xo)); + + free_strings (mountpoints); +} + +static void +output_filesystems (xmlTextWriterPtr xo, char *root) +{ + char **filesystems; + char *str; + size_t i; + + filesystems = guestfs_inspect_get_filesystems (g, root); + if (filesystems == NULL) + exit (EXIT_FAILURE); + + /* Sort by name so the output is stable. */ + qsort (filesystems, count_strings (filesystems), sizeof (char *), + compare_keys); + + XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "filesystems")); + + for (i = 0; filesystems[i] != NULL; ++i) { + canonicalize (filesystems[i]); + + XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "filesystem")); + XMLERROR (-1, + xmlTextWriterWriteAttribute (xo, BAD_CAST "dev", + BAD_CAST filesystems[i])); + + DISABLE_GUESTFS_ERRORS_FOR ( + str = guestfs_vfs_type (g, filesystems[i]); + if (str && str[0]) + XMLERROR (-1, + xmlTextWriterWriteElement (xo, BAD_CAST "type", + BAD_CAST str)); + free (str); + ); + + DISABLE_GUESTFS_ERRORS_FOR ( + str = guestfs_vfs_label (g, filesystems[i]); + if (str && str[0]) + XMLERROR (-1, + xmlTextWriterWriteElement (xo, BAD_CAST "label", + BAD_CAST str)); + free (str); + ); + + DISABLE_GUESTFS_ERRORS_FOR ( + str = guestfs_vfs_uuid (g, filesystems[i]); + if (str && str[0]) + XMLERROR (-1, + xmlTextWriterWriteElement (xo, BAD_CAST "uuid", + BAD_CAST str)); + free (str); + ); + + XMLERROR (-1, xmlTextWriterEndElement (xo)); + } + + XMLERROR (-1, xmlTextWriterEndElement (xo)); + + free_strings (filesystems); +} + +static void +output_applications (xmlTextWriterPtr xo, char *root) +{ + struct guestfs_application_list *apps; + size_t i; + + /* We need to mount everything up in order to read out the list of + * applications. + */ + inspect_mount_root (root); + + /* This returns an empty list if we simply couldn't determine the + * applications, so if it returns NULL then it's a real error. + */ + apps = guestfs_inspect_list_applications (g, root); + if (apps == NULL) + exit (EXIT_FAILURE); + if (guestfs_umount_all (g) == -1) + exit (EXIT_FAILURE); + + XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "applications")); + + for (i = 0; i < apps->len; ++i) { + XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "application")); + + assert (apps->val[i].app_name && apps->val[i].app_name[0]); + XMLERROR (-1, + xmlTextWriterWriteElement (xo, BAD_CAST "name", + BAD_CAST apps->val[i].app_name)); + + if (apps->val[i].app_display_name && apps->val[i].app_display_name[0]) + XMLERROR (-1, + xmlTextWriterWriteElement (xo, BAD_CAST "display_name", + BAD_CAST apps->val[i].app_display_name)); + + if (apps->val[i].app_epoch != 0) { + char buf[32]; + + snprintf (buf, sizeof buf, "%d", apps->val[i].app_epoch); + + XMLERROR (-1, + xmlTextWriterWriteElement (xo, BAD_CAST "epoch", BAD_CAST buf)); + } + + if (apps->val[i].app_version && apps->val[i].app_version[0]) + XMLERROR (-1, + xmlTextWriterWriteElement (xo, BAD_CAST "version", + BAD_CAST apps->val[i].app_version)); + if (apps->val[i].app_release && apps->val[i].app_release[0]) + XMLERROR (-1, + xmlTextWriterWriteElement (xo, BAD_CAST "release", + BAD_CAST apps->val[i].app_release)); + if (apps->val[i].app_install_path && apps->val[i].app_install_path[0]) + XMLERROR (-1, + xmlTextWriterWriteElement (xo, BAD_CAST "install_path", + BAD_CAST apps->val[i].app_install_path)); + if (apps->val[i].app_publisher && apps->val[i].app_publisher[0]) + XMLERROR (-1, + xmlTextWriterWriteElement (xo, BAD_CAST "publisher", + BAD_CAST apps->val[i].app_publisher)); + if (apps->val[i].app_url && apps->val[i].app_url[0]) + XMLERROR (-1, + xmlTextWriterWriteElement (xo, BAD_CAST "url", + BAD_CAST apps->val[i].app_url)); + if (apps->val[i].app_source_package && apps->val[i].app_source_package[0]) + XMLERROR (-1, + xmlTextWriterWriteElement (xo, BAD_CAST "source_package", + BAD_CAST apps->val[i].app_source_package)); + if (apps->val[i].app_summary && apps->val[i].app_summary[0]) + XMLERROR (-1, + xmlTextWriterWriteElement (xo, BAD_CAST "summary", + BAD_CAST apps->val[i].app_summary)); + if (apps->val[i].app_description && apps->val[i].app_description[0]) + XMLERROR (-1, + xmlTextWriterWriteElement (xo, BAD_CAST "description", + BAD_CAST apps->val[i].app_description)); + + XMLERROR (-1, xmlTextWriterEndElement (xo)); + } + + XMLERROR (-1, xmlTextWriterEndElement (xo)); + + guestfs_free_application_list (apps); +} + +/* "/dev/vda1" -> "/dev/sda1" + * See BLOCK DEVICE NAMING in guestfs(3). + */ +static void +canonicalize (char *dev) +{ + if (STRPREFIX (dev, "/dev/") && + (dev[5] == 'h' || dev[5] == 'v') && + dev[6] == 'd' && + c_isalpha (dev[7]) && + c_isdigit (dev[8])) + dev[5] = 's'; +} + +static void +free_strings (char **argv) +{ + int argc; + + for (argc = 0; argv[argc] != NULL; ++argc) + free (argv[argc]); + free (argv); +} + +static int +count_strings (char *const *argv) +{ + int c; + + for (c = 0; argv[c]; ++c) + ; + return c; +} diff --git a/inspector/virt-inspector.pod b/inspector/virt-inspector.pod new file mode 100755 index 0000000..0205842 --- /dev/null +++ b/inspector/virt-inspector.pod @@ -0,0 +1,311 @@ +=encoding utf8 + +=head1 NAME + +virt-inspector - Display operating system version and other information about a virtual machine + +=head1 SYNOPSIS + + virt-inspector [--options] -d domname + + virt-inspector [--options] -a disk.img [-a disk.img ...] + +Old-style: + + virt-inspector domname + + virt-inspector disk.img [disk.img ...] + +=head1 DESCRIPTION + +B examines a virtual machine or disk image and tries +to determine the version of the operating system and other information +about the virtual machine. + +Virt-inspector produces XML output for feeding into other programs. + +In the normal usage, use C where C is +the libvirt domain (see: C). + +You can also run virt-inspector directly on disk images from a single +virtual machine. Use C. In rare cases a +domain has several block devices, in which case you should list +several I<-a> options one after another, with the first corresponding +to the guest's C, the second to the guest's C and +so on. + +Virt-inspector can only inspect and report upon I. 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 + +=item B<--help> + +Display brief help. + +=item B<-a> file + +=item B<--add> file + +Add I which should be a disk image from a virtual machine. If +the virtual machine has multiple block devices, you must supply all of +them with separate I<-a> options. + +The format of the disk image is auto-detected. To override this and +force a particular format use the I<--format=..> option. + +=item B<-c URI> + +=item B<--connect URI> + +If using libvirt, connect to the given I. If omitted, +then we connect to the default libvirt hypervisor. + +Libvirt is only used if you specify a C on the +command line. If you specify guest block devices directly (I<-a>), +then libvirt is not used at all. + +=item B<-d> guest + +=item B<--domain> guest + +Add all the disks from the named libvirt guest. + +=item B<--echo-keys> + +When prompting for keys and passphrases, virt-inspector normally turns +echoing off so you cannot see what you are typing. If you are not +worried about Tempest attacks and there is no one else in the room you +can specify this flag to see what you are typing. + +=item B<--format=raw|qcow2|..> + +=item B<--format> + +Specify the format of disk images given on the command line. If this +is omitted then the format is autodetected from the content of the +disk image. + +If disk images are requested from libvirt, then this program asks +libvirt for this information. In this case, the value of the format +parameter is ignored. + +If working with untrusted raw-format guest disk images, you should +ensure the format is always specified. + +=item B<--keys-from-stdin> + +Read key or passphrase parameters from stdin. The default is +to try to read passphrases from the user by opening C. + +=item B<-v> + +=item B<--verbose> + +Enable verbose messages for debugging. + +=item B<-V> + +=item B<--version> + +Display version number and exit. + +=item B<-x> + +Enable tracing of libguestfs API calls. + +=back + +=head1 OLD-STYLE COMMAND LINE ARGUMENTS + +Previous versions of virt-inspector allowed you to write either: + + virt-inspector disk.img [disk.img ...] + +or + + virt-inspector guestname + +whereas in this version you should use I<-a> or I<-d> respectively +to avoid the confusing case where a disk image might have the same +name as a guest. + +For compatibility the old style is still supported. + +=head1 XML FORMAT + +The virt-inspector XML is described precisely in a RELAX NG schema +which is supplied with libguestfs. This section is just an overview. + +The top-level element is EoperatingsystemsE, and it contains +one or more EoperatingsystemE elements. You would only see +more than one EoperatingsystemE element if the virtual machine +is multi-boot, which is vanishingly rare in real world VMs. + +=head2 EoperatingsystemE + +In the EoperatingsystemE tag are various optional fields that +describe the operating system, its architecture, the descriptive +"product name" string, the type of OS and so on, as in this example: + + + + /dev/sda2 + windows + i386 + windows + Windows 7 Enterprise + 6 + 1 + /Windows + +These fields are derived from the libguestfs inspection API, and +you can find more details in L. + +The ErootE element is the root filesystem device, but from the +point of view of libguestfs (block devices may have completely +different names inside the VM itself). + +=head2 EmountpointsE + +Un*x-like guests typically have multiple filesystems which are mounted +at various mountpoints, and these are described in the +EmountpointsE element which looks like this: + + + + ... + + / + /boot + + +As with ErootE, devices are from the point of view of +libguestfs, and may have completely different names inside the guest. +Only mountable filesystems appear in this list, not things like swap +devices. + +=head2 EfilesystemsE + +EfilesystemsE is like EmountpointsE but covers I +filesystems belonging to the guest, including swap and empty +partitions. (In the rare case of a multi-boot guest, it covers +filesystems belonging to this OS or shared by this OS and other OSes). + +You might see something like this: + + + + ... + + + ext4 + + e6a4db1e-15c2-477b-ac2a-699181c396aa + + +The optional elements within EfilesystemE are the filesystem +type, the label, and the UUID. + +=head2 EapplicationsE + +The related elements Epackage_formatE, +Epackage_managementE and EapplicationsE describe +applications installed in the virtual machine. At the moment we are +only able to list RPMs and Debian packages installed, but in future we +will support other Linux distros and Windows. + +Epackage_formatE, if present, describes the packaging +system used. Typical values would be C and C. + +Epackage_managementE, if present, describes the package +manager. Typical values include C, C and C + +EapplicationsE lists the packages or applications +installed. + + + + ... + + + coreutils + 8.5 + 1 + + +The version and release fields may not be available for some types +guests. Other fields are possible, see +L. + +=head1 USING XPATH + +You can use the XPath query language, and/or the xpath tool, in order +to select parts of the XML. + +For example: + + $ virt-inspector -d Guest | xpath //filesystems + Found 1 nodes: + -- NODE -- + + + ext4 + [etc] + + $ virt-inspector -d Guest | \ + xpath "string(//filesystem[@dev='/dev/sda1']/type)" + Query didn't return a nodeset. Value: ext4 + +=head1 SHELL QUOTING + +Libvirt guest names can contain arbitrary characters, some of which +have meaning to the shell such as C<#> and space. You may need to +quote or escape these characters on the command line. See the shell +manual page L for details. + +=head1 SEE ALSO + +L, +L, +L, +L. + +=head1 AUTHORS + +=over 4 + +=item * + +Richard W.M. Jones L + +=item * + +Matthew Booth L + +=back + +=head1 COPYRIGHT + +Copyright (C) 2010 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/po/POTFILES.in b/po/POTFILES.in index 3ddb0c2..0e96a2c 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -106,7 +106,7 @@ fish/time.c fish/virt.c fuse/dircache.c fuse/guestmount.c -inspector/virt-inspector.pl +inspector/virt-inspector.c java/com_redhat_et_libguestfs_GuestFS.c ocaml/guestfs_c.c ocaml/guestfs_c_actions.c -- 1.8.3.1