summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorRichard Jones <rjones@redhat.com>2009-10-19 10:18:46 +0100
committerRichard Jones <rjones@redhat.com>2009-10-19 10:18:46 +0100
commit945b6e0a085611b45b2ab0752a66e6e60b21666c (patch)
tree003770dd080fa3644dd2a77f3ed40592f19c03a5 /tools
parent854f8e0d5643e55d045f5816a520d49a057990ef (diff)
downloadlibguestfs-945b6e0a085611b45b2ab0752a66e6e60b21666c.tar.gz
libguestfs-945b6e0a085611b45b2ab0752a66e6e60b21666c.tar.xz
libguestfs-945b6e0a085611b45b2ab0752a66e6e60b21666c.zip
Move virt tools (virt-cat, virt-edit etc) into tools/ subdirectory.
This moves the tool programs into a single directory: cat/* -> tools/virt-cat df/* -> tools/virt-df edit/* -> tools/virt-edit rescue/* -> tools/virt-rescue This in itself simplifies the build process because we only need one Makefile and one copy of 'run-locally'. 'run-*-locally' has become just 'run-locally' and takes an extra parameter which is the name of the tool, eg: run-locally cat [virt-cat params...] virt-inspector stays in its own directory, because this contains more than just a single Perl script.
Diffstat (limited to 'tools')
-rw-r--r--tools/Makefile.am50
-rwxr-xr-xtools/run-locally56
-rwxr-xr-xtools/virt-cat194
-rwxr-xr-xtools/virt-df366
-rwxr-xr-xtools/virt-edit210
-rwxr-xr-xtools/virt-rescue169
6 files changed, 1045 insertions, 0 deletions
diff --git a/tools/Makefile.am b/tools/Makefile.am
new file mode 100644
index 00000000..d5fc23c2
--- /dev/null
+++ b/tools/Makefile.am
@@ -0,0 +1,50 @@
+# libguestfs virt-* tools
+# 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.
+
+tools = cat df edit rescue
+
+EXTRA_DIST = \
+ run-locally \
+ $(tools:%=virt-%)
+
+if HAVE_TOOLS
+
+bin_SCRIPTS = $(tools:%=virt-%)
+
+# XXX Bug in automake? If you list virt-cat.1 explicitly, then it
+# builds and installs the man pages. However if this is removed,
+# then the man pages are neither built nor installed.
+man_MANS = virt-cat.1 $(patsubst %,virt-%.1,$(filter-out cat,$(tools)))
+
+noinst_DATA = $(tools:%=$(top_builddir)/html/virt-%.1.html)
+
+virt-%.1: virt-%
+ $(POD2MAN) \
+ --section 1 \
+ -c "Virtualization Support" \
+ --release "$(PACKAGE_NAME)-$(PACKAGE_VERSION)" \
+ $< > $@-t && mv $@-t $@
+
+$(top_builddir)/html/virt-%.1.html: virt-%
+ mkdir -p $(top_builddir)/html
+ cd $(top_builddir) && pod2html \
+ --css 'pod.css' \
+ --htmldir html \
+ --outfile html/$<.1.html \
+ tools/$<
+
+endif
diff --git a/tools/run-locally b/tools/run-locally
new file mode 100755
index 00000000..0bf1c0a7
--- /dev/null
+++ b/tools/run-locally
@@ -0,0 +1,56 @@
+#!/usr/bin/perl
+# 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-* tools in
+# place without needing to do 'make install' first. You can also run
+# the tools by creating a symlink to this script and putting it in
+# your path.
+#
+# Use it like this:
+# ./run-locally tool [usual virt-tool args ...]
+# eg:
+# ./run-locally cat domname /etc/passwd
+
+use strict;
+use warnings;
+
+use File::Basename qw(dirname);
+use File::Spec;
+use Cwd qw(abs_path);
+
+my $path = $0;
+my $tool = shift @ARGV;
+
+# Follow symlinks until we get to the real file
+while(-l $path) {
+ my $link = readlink($path) or die "readlink: $path: $!";
+ if(File::Spec->file_name_is_absolute($link)) {
+ $path = $link;
+ } else {
+ $path = File::Spec->catfile(dirname($path), $link);
+ }
+}
+
+# Get the absolute path of the parent directory
+$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';
+
+print (join " ", ("$path/tools/virt-$tool", @ARGV), "\n");
+exec('perl', "$path/tools/virt-$tool", @ARGV);
diff --git a/tools/virt-cat b/tools/virt-cat
new file mode 100755
index 00000000..329ba6e3
--- /dev/null
+++ b/tools/virt-cat
@@ -0,0 +1,194 @@
+#!/usr/bin/perl -w
+# virt-cat
+# 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 Sys::Guestfs::Lib qw(open_guest get_partitions resolve_windows_path
+ inspect_all_partitions inspect_partition
+ inspect_operating_systems mount_operating_system);
+use Pod::Usage;
+use Getopt::Long;
+use Locale::TextDomain 'libguestfs';
+
+=encoding utf8
+
+=head1 NAME
+
+virt-cat - Display a file in a virtual machine
+
+=head1 SYNOPSIS
+
+ virt-cat [--options] domname file
+
+ virt-cat [--options] disk.img [disk.img ...] file
+
+=head1 DESCRIPTION
+
+C<virt-cat> is a command line tool to display the contents of C<file>
+where C<file> exists in the named virtual machine (or disk image).
+
+C<virt-cat> can be used to quickly view a single file. To edit a
+file, use C<virt-edit>. For more complex cases you should look at the
+L<guestfish(1)> tool.
+
+=head1 EXAMPLES
+
+Display C</etc/fstab> file from inside the libvirt VM called
+C<mydomain>:
+
+ virt-cat mydomain /etc/fstab
+
+List syslog messages from a VM:
+
+ virt-cat mydomain /var/log/messages | tail
+
+Find out what DHCP IP address a VM acquired:
+
+ virt-cat mydomain /var/log/messages | grep 'dhclient: bound to' | tail
+
+Find out what packages were recently installed:
+
+ virt-cat mydomain /var/log/yum.log | tail
+
+Find out who is logged on inside a virtual machine:
+
+ virt-cat mydomain /var/run/utmp > /tmp/utmp
+ who /tmp/utmp
+
+or who was logged on:
+
+ virt-cat mydomain /var/log/wtmp > /tmp/wtmp
+ last -f /tmp/wtmp
+
+=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<URI>. If omitted, then we
+connect to the default libvirt hypervisor.
+
+If you specify guest block devices directly, then libvirt is not used
+at all.
+
+=back
+
+=cut
+
+GetOptions ("help|?" => \$help,
+ "version" => \$version,
+ "connect|c=s" => \$uri,
+ ) 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-cat: no image, VM names or filenames to cat given")
+ if @ARGV <= 1;
+
+my $filename = pop @ARGV;
+
+my $g;
+if ($uri) {
+ $g = open_guest (\@ARGV, address => $uri);
+} else {
+ $g = open_guest (\@ARGV);
+}
+
+$g->launch ();
+
+# List of possible filesystems.
+my @partitions = get_partitions ($g);
+
+# Now query each one to build up a picture of what's in it.
+my %fses =
+ inspect_all_partitions ($g, \@partitions,
+ use_windows_registry => 0);
+
+my $oses = inspect_operating_systems ($g, \%fses);
+
+my @roots = keys %$oses;
+die __"no root device found in this operating system image" if @roots == 0;
+die __"multiboot operating systems are not supported by virt-cat" if @roots > 1;
+my $root_dev = $roots[0];
+
+my $os = $oses->{$root_dev};
+mount_operating_system ($g, $os);
+
+# Allow this to fail in case eg. the file does not exist.
+# NB: https://bugzilla.redhat.com/show_bug.cgi?id=501888
+print $g->download($filename, "/dev/stdout");
+
+=head1 SEE ALSO
+
+L<guestfs(3)>,
+L<guestfish(1)>,
+L<virt-edit(1)>,
+L<Sys::Guestfs(3)>,
+L<Sys::Guestfs::Lib(3)>,
+L<Sys::Virt(3)>,
+L<http://libguestfs.org/>.
+
+=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/tools/virt-df b/tools/virt-df
new file mode 100755
index 00000000..78eb25c9
--- /dev/null
+++ b/tools/virt-df
@@ -0,0 +1,366 @@
+#!/usr/bin/perl -w
+# virt-df
+# 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 Sys::Guestfs::Lib qw(open_guest get_partitions resolve_windows_path
+ inspect_all_partitions inspect_partition
+ inspect_operating_systems mount_operating_system inspect_in_detail);
+use Pod::Usage;
+use Getopt::Long;
+use Data::Dumper;
+use File::Temp qw/tempdir/;
+use XML::Writer;
+use Locale::TextDomain 'libguestfs';
+
+=encoding utf8
+
+=head1 NAME
+
+virt-df - Display free space on virtual filesystems
+
+=head1 SYNOPSIS
+
+ virt-df [--options]
+
+ virt-df [--options] domname
+
+ virt-df [--options] disk.img [disk.img ...]
+
+=head1 DESCRIPTION
+
+C<virt-df> is a command line tool to display free space on virtual
+machine filesystems. Unlike other tools, it doesn't just display the
+amount of space allocated to a virtual machine, but can look inside
+the virtual machine to see how much space is really being used.
+
+It is like the L<df(1)> command, but for virtual machines, except that
+it also works for Windows virtual machines.
+
+If used without any arguments, C<virt-df> checks with libvirt to get a
+list of all active and inactive guests, and performs a C<df>-type
+operation on each one in turn, printing out the results.
+
+If used with any argument(s), C<virt-df> performs a C<df>-type
+operation on either the single named libvirt domain, or on the disk
+image(s) listed on the command line (which must all belong to a single
+VM). In this mode (with arguments), C<virt-df> will I<only work for a
+single guest>. If you want to run on multiple guests, then you have
+to invoke C<virt-df> multiple times.
+
+Use the C<--csv> option to get a format which can be easily parsed by
+other programs. Other options are mostly similar to standard C<df>
+options. See below for the complete list.
+
+=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<URI>. If omitted, then we
+connect to the default libvirt hypervisor.
+
+If you specify guest block devices directly, then libvirt is not used
+at all.
+
+=cut
+
+my $csv;
+
+=item B<--csv>
+
+Write out the results in CSV format (comma-separated values). This
+format can be imported easily into databases and spreadsheets, but
+read L</NOTE ABOUT CSV FORMAT> below.
+
+=cut
+
+my $human;
+
+=item B<--human-readable> | B<-h>
+
+Print sizes in human-readable format.
+
+=cut
+
+my $inodes;
+
+=item B<--inodes> | B<-i>
+
+Print inodes instead of blocks.
+
+=back
+
+=cut
+
+GetOptions ("help|?" => \$help,
+ "version" => \$version,
+ "connect|c=s" => \$uri,
+ "csv" => \$csv,
+ "human-readable|human|h" => \$human,
+ "inodes|i" => \$inodes,
+ ) 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
+}
+
+# Open the guest handle.
+
+if (@ARGV == 0) {
+ my $conn;
+
+ if ($uri) {
+ $conn = Sys::Virt->new (readonly => 1, address => $uri);
+ } else {
+ $conn = Sys::Virt->new (readonly => 1);
+ }
+
+ my @doms = $conn->list_defined_domains ();
+ push @doms, $conn->list_domains ();
+
+ my @domnames = map { $_->get_name () } @doms;
+
+ if (@domnames) {
+ print_title ();
+ foreach (@domnames) {
+ do_df ($_);
+ }
+ }
+} else {
+ print_title ();
+ do_df (@ARGV);
+}
+
+sub do_df
+{
+ my $g;
+
+ if ($uri) {
+ $g = open_guest (\@_, address => $uri);
+ } else {
+ $g = open_guest (\@_);
+ }
+
+ $g->launch ();
+
+ my @partitions = get_partitions ($g);
+
+ # Think of a printable name for this domain. Just choose the
+ # first parameter passed to this function, which will work for
+ # most cases (it'll either be the domain name or the first disk
+ # image name).
+ my $domname = $_[0];
+
+ # Mount each partition in turn, and if mountable, do a statvfs on it.
+ foreach my $partition (@partitions) {
+ my %stat;
+ eval {
+ $g->mount_ro ($partition, "/");
+ %stat = $g->statvfs ("/");
+ };
+ if (!$@) {
+ print_stat ($domname, $partition, \%stat);
+ }
+ $g->umount_all ();
+ }
+}
+
+sub print_stat
+{
+ my $domname = shift;
+ my $partition = shift;
+ my $stat = shift;
+
+ my @cols = ($domname, $partition);
+
+ if (!$inodes) {
+ my $bsize = $stat->{bsize}; # block size
+ my $blocks = $stat->{blocks}; # total number of blocks
+ my $bfree = $stat->{bfree}; # blocks free (total)
+ my $bavail = $stat->{bavail}; # blocks free (for non-root users)
+
+ my $factor = $bsize / 1024;
+
+ push @cols, $blocks*$factor; # total 1K blocks
+ push @cols, ($blocks-$bfree)*$factor; # total 1K blocks used
+ push @cols, $bavail*$factor; # total 1K blocks available
+
+ # XXX %used column comes out different from the native 'df'
+ # program. Need to check how 'df' calculates this.
+ push @cols, 100.0 - 100.0 * $bavail / $blocks;
+
+ if ($human) {
+ $cols[2] = human_size ($cols[2]);
+ $cols[3] = human_size ($cols[3]);
+ $cols[4] = human_size ($cols[4]);
+ }
+ } else {
+ my $files = $stat->{files}; # total number of inodes
+ my $ffree = $stat->{ffree}; # inodes free (total)
+ my $favail = $stat->{favail}; # inodes free (for non-root users)
+
+ push @cols, $files;
+ push @cols, $files-$ffree;
+ push @cols, $ffree;
+
+ # XXX %used column comes out different from the native 'df'
+ # program. Need to check how 'df' calculates this.
+ push @cols, 100.0 - 100.0 * $favail / $files;
+ }
+
+ print_cols (@cols);
+}
+
+sub print_title
+{
+ my @cols = (__"Virtual Machine", __"Filesystem");
+ if (!$inodes) {
+ if (!$human) {
+ push @cols, __"1K-blocks";
+ } else {
+ push @cols, __"Size";
+ }
+ push @cols, __"Used";
+ push @cols, __"Available";
+ push @cols, __"Use%";
+ } else {
+ push @cols, __"Inodes";
+ push @cols, __"IUsed";
+ push @cols, __"IFree";
+ push @cols, __"IUse%";
+ }
+
+ if (!$csv) {
+ # ignore $cols[0] in this mode
+ printf "%-36s%10s %10s %10s %5s\n",
+ $cols[1], $cols[2], $cols[3], $cols[4], $cols[5];
+ } else {
+ print (join (",", @cols), "\n");
+ }
+}
+
+sub print_cols
+{
+ if (!$csv) {
+ my $label = sprintf "%s:%s", $_[0], $_[1];
+
+ printf ("%-36s", $label);
+ print "\n"," "x36 if length ($label) > 36;
+
+ my $percent = sprintf "%3.1f%%", $_[5];
+ printf ("%10s %10s %10s %5s\n", $_[2], $_[3], $_[4], $percent);
+ } else {
+ printf ("\"%s\",\"%s\",%d,%d,%d,%.1f%%\n", @_);
+ }
+}
+
+# Convert a number of 1K blocks to a human-readable number.
+sub human_size
+{
+ local $_ = shift;
+
+ if ($_ < 1024) {
+ sprintf "%dK", $_;
+ } elsif ($_ < 1024 * 1024) {
+ sprintf "%.1fM", ($_ / 1024);
+ } else {
+ sprintf "%.1fG", ($_ / 1024 / 1024);
+ }
+}
+
+=head1 NOTE ABOUT CSV FORMAT
+
+Comma-separated values (CSV) is a deceptive format. It I<seems> like
+it should be easy to parse, but it is definitely not easy to parse.
+
+Myth: Just split fields at commas. Reality: This does I<not> work
+reliably. This example has two columns:
+
+ "foo,bar",baz
+
+Myth: Read the file one line at a time. Reality: This does I<not>
+work reliably. This example has one row:
+
+ "foo
+ bar",baz
+
+For shell scripts, use C<csvtool> (L<http://merjis.com/developers/csv>
+also packaged in major Linux distributions).
+
+For other languages, use a CSV processing library (eg. C<Text::CSV>
+for Perl or Python's built-in csv library).
+
+Most spreadsheets and databases can import CSV directly.
+
+=head1 SEE ALSO
+
+L<guestfs(3)>,
+L<guestfish(1)>,
+L<Sys::Guestfs(3)>,
+L<Sys::Guestfs::Lib(3)>,
+L<Sys::Virt(3)>,
+L<http://libguestfs.org/>.
+
+=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/tools/virt-edit b/tools/virt-edit
new file mode 100755
index 00000000..46e86a17
--- /dev/null
+++ b/tools/virt-edit
@@ -0,0 +1,210 @@
+#!/usr/bin/perl -w
+# virt-edit
+# 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 Sys::Guestfs::Lib qw(open_guest get_partitions resolve_windows_path
+ inspect_all_partitions inspect_partition
+ inspect_operating_systems mount_operating_system);
+use Pod::Usage;
+use Getopt::Long;
+use File::Temp qw/tempfile/;
+use Locale::TextDomain 'libguestfs';
+
+=encoding utf8
+
+=head1 NAME
+
+virt-edit - Edit a file in a virtual machine
+
+=head1 SYNOPSIS
+
+ virt-edit [--options] domname file
+
+ virt-edit [--options] disk.img [disk.img ...] file
+
+=head1 DESCRIPTION
+
+C<virt-edit> is a command line tool to edit C<file> where C<file>
+exists in the named virtual machine (or disk image).
+
+B<Note> you must I<not> use virt-edit on live virtual machines. If
+you do this, you risk disk corruption in the VM.
+
+If you want to just view a file, use L<virt-cat(1)>. For more complex
+cases you should look at the L<guestfish(1)> tool.
+
+=head1 EXAMPLES
+
+ virt-edit mydomain /boot/grub/grub.conf
+
+ virt-edit mydomain /etc/passwd
+
+=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<URI>. If omitted, then we
+connect to the default libvirt hypervisor.
+
+If you specify guest block devices directly, then libvirt is not used
+at all.
+
+=back
+
+=cut
+
+GetOptions ("help|?" => \$help,
+ "version" => \$version,
+ "connect|c=s" => \$uri,
+ ) 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-edit: no image, VM names or filenames to edit given")
+ if @ARGV <= 1;
+
+my $filename = pop @ARGV;
+
+my $g;
+if ($uri) {
+ $g = open_guest (\@ARGV, address => $uri, rw => 1);
+} else {
+ $g = open_guest (\@ARGV, rw => 1);
+}
+
+$g->launch ();
+
+# List of possible filesystems.
+my @partitions = get_partitions ($g);
+
+# Now query each one to build up a picture of what's in it.
+my %fses =
+ inspect_all_partitions ($g, \@partitions,
+ use_windows_registry => 0);
+
+my $oses = inspect_operating_systems ($g, \%fses);
+
+my @roots = keys %$oses;
+die __"no root device found in this operating system image" if @roots == 0;
+die __"multiboot operating systems are not supported by virt-edit" if @roots > 1;
+my $root_dev = $roots[0];
+
+my $os = $oses->{$root_dev};
+mount_operating_system ($g, $os, 0);
+
+my ($fh, $tempname) = tempfile ();
+
+# Allow this to fail in case eg. the file does not exist.
+$g->download($filename, $tempname);
+
+my $oldctime = (stat ($tempname))[10];
+
+my $editor = $ENV{EDITOR};
+$editor ||= "vi";
+system ("$editor $tempname") == 0
+ or die "edit failed: $editor: $?";
+
+my $newctime = (stat ($tempname))[10];
+
+if ($oldctime != $newctime) {
+ $g->upload ($tempname, $filename)
+} else {
+ print __"File not changed.\n";
+}
+
+$g->sync ();
+$g->umount_all ();
+
+undef $g;
+
+exit 0;
+
+=head1 ENVIRONMENT VARIABLES
+
+=over 4
+
+=item C<EDITOR>
+
+If set, this string is used as the editor. It may contain arguments,
+eg. C<"emacs -nw">
+
+If not set, C<vi> is used.
+
+=back
+
+=head1 SEE ALSO
+
+L<guestfs(3)>,
+L<guestfish(1)>,
+L<virt-cat(1)>,
+L<Sys::Guestfs(3)>,
+L<Sys::Guestfs::Lib(3)>,
+L<Sys::Virt(3)>,
+L<http://libguestfs.org/>.
+
+=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/tools/virt-rescue b/tools/virt-rescue
new file mode 100755
index 00000000..9ad2fa4f
--- /dev/null
+++ b/tools/virt-rescue
@@ -0,0 +1,169 @@
+#!/usr/bin/perl -w
+# virt-rescue
+# 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 Sys::Guestfs::Lib qw(open_guest);
+use Pod::Usage;
+use Getopt::Long;
+use Locale::TextDomain 'libguestfs';
+
+=encoding utf8
+
+=head1 NAME
+
+virt-rescue - Run a rescue shell on a virtual machine
+
+=head1 SYNOPSIS
+
+ virt-rescue [--options] domname
+
+ virt-rescue [--options] disk.img [disk.img ...]
+
+=head1 DESCRIPTION
+
+virt-rescue gives you a rescue shell and some simple recovery tools
+which you can use on a virtual machine disk image.
+
+After running virt-rescue, what you see under C</> is the recovery
+appliance. You must mount the virtual machine's filesystems by hand,
+eg:
+
+ # lvs
+ LV VG Attr LSize Origin Snap% Move Log Copy% Convert
+ lv_root vg_f11x64 -wi-a- 8.83G
+ lv_swap vg_f11x64 -wi-a- 992.00M
+ # mount /dev/vg_f11x64/lv_root /sysroot
+ # ls /sysroot
+
+B<Note> that the virtual machine must not be powered on when you use
+this tool. Doing so will probably result in disk corruption in the
+VM. However if you use the I<--ro> (read only) option, then you can
+attach a shell to a running machine, but the results might be strange
+or inconsistent.
+
+This tool is just designed for quick interactive hacking on a virtual
+machine. For more structured access to a virtual machine disk image,
+you should use L<guestfs(3)>. To get a structured shell, use
+L<guestfish(1)>.
+
+=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<URI>. If omitted, then we
+connect to the default libvirt hypervisor.
+
+If you specify guest block devices directly, then libvirt is not used
+at all.
+
+=cut
+
+my $readonly;
+
+=item B<--ro> | B<-r>
+
+Open the image read-only.
+
+=back
+
+=cut
+
+GetOptions ("help|?" => \$help,
+ "version" => \$version,
+ "connect|c=s" => \$uri,
+ "ro|r" => \$readonly,
+ ) 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-rescue: no image or VM names rescue given")
+ if @ARGV == 0;
+
+my @args = (\@ARGV);
+push @args, address => $uri if $uri;
+push @args, rw => 1 unless $readonly;
+my $g = open_guest (@args);
+
+$g->set_direct (1);
+$g->set_append ("guestfs_rescue=1");
+
+$g->launch ();
+
+exit 0;
+
+=head1 SEE ALSO
+
+L<guestfs(3)>,
+L<guestfish(1)>,
+L<virt-cat(1)>,
+L<Sys::Guestfs(3)>,
+L<Sys::Guestfs::Lib(3)>,
+L<Sys::Virt(3)>,
+L<http://libguestfs.org/>.
+
+=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.