From a6054bf90f39a84c1a8f8f7d3b9e81e84cb13696 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Fri, 26 Nov 2010 11:36:50 +0000 Subject: rescue: Rewrite virt-rescue in C. --- .gitignore | 4 + HACKING | 3 + Makefile.am | 4 +- configure.ac | 1 + po/POTFILES.in | 2 +- rescue/Makefile.am | 67 ++++++ rescue/run-rescue-locally | 52 +++++ rescue/virt-rescue.c | 526 ++++++++++++++++++++++++++++++++++++++++++++++ rescue/virt-rescue.pod | 247 ++++++++++++++++++++++ tools/Makefile.am | 1 - tools/virt-rescue | 297 -------------------------- 11 files changed, 904 insertions(+), 300 deletions(-) create mode 100644 rescue/Makefile.am create mode 100755 rescue/run-rescue-locally create mode 100644 rescue/virt-rescue.c create mode 100755 rescue/virt-rescue.pod delete mode 100755 tools/virt-rescue diff --git a/.gitignore b/.gitignore index f86affa8..7da3f321 100644 --- a/.gitignore +++ b/.gitignore @@ -283,6 +283,10 @@ regressions/test1.img regressions/test2.img regressions/test.err regressions/test.out +rescue/stamp-virt-rescue.pod +rescue/virt-rescue +rescue/virt-rescue.1 +rescue/virt-rescue.static ruby/bindtests.rb ruby/examples/guestfs-ruby.3 ruby/examples/stamp-guestfs-ruby.pod diff --git a/HACKING b/HACKING index fed296d0..f59dd666 100644 --- a/HACKING +++ b/HACKING @@ -148,6 +148,9 @@ python/ regressions/ Regression tests. +rescue/ + 'virt-rescue' command and documentation. + ruby/ Ruby bindings. diff --git a/Makefile.am b/Makefile.am index 7dd0e978..86d4e0a5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -36,7 +36,7 @@ SUBDIRS += gnulib/tests capitests regressions test-tool SUBDIRS += fish # virt-tools in C. -SUBDIRS += cat df inspector +SUBDIRS += cat df inspector rescue # Language bindings. if HAVE_PERL @@ -225,5 +225,7 @@ bindist: cp df/virt-df.static $(BINTMPDIR)$(bindir)/virt-df $(MAKE) -C inspector virt-inspector.static cp inspector/virt-inspector.static $(BINTMPDIR)$(bindir)/virt-inspector + $(MAKE) -C rescue virt-rescue.static + cp rescue/virt-rescue.static $(BINTMPDIR)$(bindir)/virt-rescue (cd $(BINTMPDIR) && tar cf - .) | \ gzip -c -9 > libguestfs-$(VERSION)-$(host_cpu).tar.gz diff --git a/configure.ac b/configure.ac index ac5e67a7..90dffb0e 100644 --- a/configure.ac +++ b/configure.ac @@ -869,6 +869,7 @@ AC_CONFIG_FILES([Makefile csharp/Makefile cat/Makefile df/Makefile + rescue/Makefile ocaml/META perl/Makefile.PL]) AC_OUTPUT diff --git a/po/POTFILES.in b/po/POTFILES.in index 5072fc76..fd8778a7 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -126,6 +126,7 @@ regressions/rhbz501893.c regressions/test-launch-race.pl regressions/test-lvm-mapping.pl regressions/test-noexec-stack.pl +rescue/virt-rescue.c ruby/ext/guestfs/_guestfs.c src/actions.c src/appliance.c @@ -145,7 +146,6 @@ tools/virt-edit.pl tools/virt-list-filesystems.pl tools/virt-list-partitions.pl tools/virt-make-fs.pl -tools/virt-rescue.pl tools/virt-resize.pl tools/virt-tar.pl tools/virt-win-reg.pl diff --git a/rescue/Makefile.am b/rescue/Makefile.am new file mode 100644 index 00000000..1208be1c --- /dev/null +++ b/rescue/Makefile.am @@ -0,0 +1,67 @@ +# libguestfs virt-rescue +# 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 $(top_srcdir)/subdir-rules.mk + +EXTRA_DIST = \ + run-rescue-locally \ + virt-rescue.pod + +CLEANFILES = stamp-virt-rescue.pod + +bin_PROGRAMS = virt-rescue + +SHARED_SOURCE_FILES = \ + ../fish/inspect.c \ + ../fish/keys.c \ + ../fish/options.h \ + ../fish/options.c \ + ../fish/virt.c + +virt_rescue_SOURCES = \ + $(SHARED_SOURCE_FILES) \ + virt-rescue.c + +virt_rescue_CFLAGS = \ + -I$(top_srcdir)/src -I$(top_builddir)/src \ + -I$(top_srcdir)/fish \ + -I$(srcdir)/../gnulib/lib -I../gnulib/lib \ + -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ + $(WARN_CFLAGS) $(WERROR_CFLAGS) + +virt_rescue_LDADD = \ + $(top_builddir)/src/libguestfs.la \ + ../gnulib/lib/libgnu.la + +# Manual pages and HTML files for the website. +man_MANS = virt-rescue.1 +noinst_DATA = $(top_builddir)/html/virt-rescue.1.html + +virt-rescue.1 $(top_builddir)/html/virt-rescue.1.html: stamp-virt-rescue.pod + +stamp-virt-rescue.pod: virt-rescue.pod + $(top_srcdir)/podwrapper.sh \ + --man virt-rescue.1 \ + --html $(top_builddir)/html/virt-rescue.1.html \ + $< + touch $@ + +# Build a partly-static binary (for the binary distribution). + +virt-rescue.static$(EXEEXT): $(virt_rescue_OBJECTS) $(virt_rescue_DEPENDENCIES) + $(top_srcdir)/relink-static.sh \ + $(virt_rescue_LINK) $(virt_rescue_OBJECTS) -static $(virt_rescue_LDADD) $(virt_rescue_LIBS) $(LIBVIRT_LIBS) $(LIBXML2_LIBS) -lpcre -lhivex -lmagic -lz -lm diff --git a/rescue/run-rescue-locally b/rescue/run-rescue-locally new file mode 100755 index 00000000..95f3cc4a --- /dev/null +++ b/rescue/run-rescue-locally @@ -0,0 +1,52 @@ +#!/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-rescue-locally [usual virt-rescue args ...] + +use strict; +use warnings; + +use File::Basename qw(dirname); +use File::Spec; +use Cwd qw(abs_path); + +my $path = $0; + +# 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'; + +#print (join " ", ("$path/rescue/virt-rescue", @ARGV), "\n"); +exec("$path/rescue/virt-rescue", @ARGV); diff --git a/rescue/virt-rescue.c b/rescue/virt-rescue.c new file mode 100644 index 00000000..3ae28e30 --- /dev/null +++ b/rescue/virt-rescue.c @@ -0,0 +1,526 @@ +/* virt-rescue + * 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 "progname.h" +#include "xvasprintf.h" + +#include "guestfs.h" +#include "options.h" + +/* Currently open libguestfs handle. */ +guestfs_h *g; + +int read_only = 0; +int verbose = 0; +int keys_from_stdin = 0; +int echo_keys = 0; +const char *libvirt_uri = NULL; +int inspector = 0; + +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: Run a rescue shell on a virtual machine\n" + "Copyright (C) 2009-2010 Red Hat Inc.\n" + "Usage:\n" + " %s [--options] -d domname\n" + " %s [--options] -a disk.img [-a disk.img ...]\n" + "Options:\n" + " -a|--add image Add image\n" + " --append kernelopts Append kernel options\n" + " -c|--connect uri Specify libvirt URI for -d option\n" + " -d|--domain guest Add disks from libvirt guest\n" + " --format[=raw|..] Force disk format for -a option\n" + " --help Display brief help\n" + " -m|--memsize MB Set memory size in megabytes\n" + " --network Enable network\n" + " -r|--ro Access read-only\n" + " --selinux Enable SELinux\n" + " -v|--verbose Verbose messages\n" + " -V|--version Display version and exit\n" + " -x Trace libguestfs API calls\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:m:rvVx"; + static const struct option long_options[] = { + { "add", 1, 0, 'a' }, + { "append", 1, 0, 0 }, + { "connect", 1, 0, 'c' }, + { "domain", 1, 0, 'd' }, + { "format", 2, 0, 0 }, + { "help", 0, 0, HELP_OPTION }, + { "memsize", 1, 0, 'm' }, + { "network", 0, 0, 0 }, + { "ro", 0, 0, 'r' }, + { "selinux", 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 network = 0; + const char *append = NULL; + char *append_full; + int memsize = 0; + + 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, "selinux")) { + guestfs_set_selinux (g, 1); + } else if (STREQ (long_options[option_index].name, "append")) { + append = optarg; + } else if (STREQ (long_options[option_index].name, "network")) { + network = 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 'm': + if (sscanf (optarg, "%u", &memsize) != 1) { + fprintf (stderr, _("%s: could not parse memory size '%s'\n"), + program_name, optarg); + exit (EXIT_FAILURE); + } + break; + + case 'r': + OPTION_r; + break; + + 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-rescue 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 (inspector == 0); + assert (keys_from_stdin == 0); + assert (echo_keys == 0); + + /* 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); + + /* Setting "direct mode" is required for the rescue appliance. */ + guestfs_set_direct (g, 1); + + /* Set other features. */ + if (memsize > 0) + guestfs_set_memsize (g, memsize); + if (network) + guestfs_set_network (g, 1); + + /* Kernel command line must include guestfs_rescue=1 (see + * appliance/init) as well as other options. + */ + append_full = xasprintf ("guestfs_rescue=1%s%s", + append ? " " : "", + append ? append : ""); + guestfs_set_append (g, append_full); + free (append_full); + + /* Add drives. */ + add_drives (drvs, 'a'); + + /* Free up data structures, no longer needed after this point. */ + free_drives (drvs); + + /* Run the appliance. This won't return until the user quits the + * appliance. + */ + guestfs_set_error_handler (g, NULL, NULL); + guestfs_launch (g); + + /* launch() expects guestfsd to start. However, virt-rescue doesn't + * run guestfsd, so this will always fail with ECHILD when the + * appliance exits unexpectedly. + */ + if (errno != ECHILD) { + fprintf (stderr, "%s: %s\n", program_name, guestfs_last_error (g)); + guestfs_close (g); + exit (EXIT_FAILURE); + } + + guestfs_close (g); + + exit (EXIT_SUCCESS); +} + +/* The following was a nice idea, but in fact it doesn't work. This is + * because qemu has some (broken) pty emulation itself. + */ +#if 0 + int fd_m, fd_s, r; + pid_t pid; + struct termios tsorig, tsnew; + + /* Set up pty. */ + fd_m = posix_openpt (O_RDWR); + if (fd_m == -1) { + perror ("posix_openpt"); + exit (EXIT_FAILURE); + } + r = grantpt (fd_m); + if (r == -1) { + perror ("grantpt"); + exit (EXIT_FAILURE); + } + r = unlockpt (fd_m); + if (r == -1) { + perror ("unlockpt"); + exit (EXIT_FAILURE); + } + fd_s = open (ptsname (fd_m), O_RDWR); + if (fd_s == -1) { + perror ("open ptsname"); + exit (EXIT_FAILURE); + } + + pid = fork (); + if (pid == -1) { + perror ("fork"); + exit (EXIT_FAILURE); + } + if (pid == 0) { + /* Child process. */ + +#if 1 + /* Set raw mode. */ + r = tcgetattr (fd_s, &tsorig); + tsnew = tsorig; + cfmakeraw (&tsnew); + tcsetattr (fd_s, TCSANOW, &tsnew); +#endif + + /* Close the master side of pty and set slave side as + * stdin/stdout/stderr. + */ + close (fd_m); + + close (0); + close (1); + close (2); + if (dup (fd_s) == -1 || dup (fd_s) == -1 || dup (fd_s) == -1) { + perror ("dup"); + exit (EXIT_FAILURE); + } + close (fd_s); + +#if 1 + if (setsid () == -1) + perror ("warning: failed to setsid"); + if (ioctl (0, TIOCSCTTY, 0) == -1) + perror ("warning: failed to TIOCSCTTY"); +#endif + + /* Run the appliance. This won't return until the user quits the + * appliance. + */ + guestfs_set_error_handler (g, NULL, NULL); + r = guestfs_launch (g); + + /* launch() expects guestfsd to start. However, virt-rescue doesn't + * run guestfsd, so this will always fail with ECHILD when the + * appliance exits unexpectedly. + */ + if (errno != ECHILD) { + fprintf (stderr, "%s: %s\n", program_name, guestfs_last_error (g)); + guestfs_close (g); + exit (EXIT_FAILURE); + } + + guestfs_close (g); + _exit (EXIT_SUCCESS); + } + + /* Parent process continues ... */ + + /* Close slave side of pty. */ + close (fd_s); + + /* Set raw mode. */ + r = tcgetattr (fd_s, &tsorig); + tsnew = tsorig; + cfmakeraw (&tsnew); + tcsetattr (fd_s, TCSANOW, &tsnew); + + /* Send input and output to master side of pty. */ + r = multiplex (fd_m); + tcsetattr (fd_s, TCSANOW, &tsorig); /* Restore cooked mode. */ + if (r == -1) + exit (EXIT_FAILURE); + + if (waitpid (pid, &r, 0) == -1) { + perror ("waitpid"); + exit (EXIT_FAILURE); + } + if (!WIFEXITED (r)) { + /* abnormal child exit */ + fprintf (stderr, _("%s: unknown child exit status (%d)\n"), + program_name, r); + exit (EXIT_FAILURE); + } + else + exit (WEXITSTATUS (r)); /* normal exit, return child process's status */ +} + +/* Naive and simple multiplex function. */ +static int +multiplex (int fd_m) +{ + int r, eof_stdin = 0; + fd_set rfds, wfds; + char tobuf[BUFSIZ], frombuf[BUFSIZ]; /* to/from slave */ + size_t tosize = 0, fromsize = 0; + ssize_t n; + size_t count; + long flags_0, flags_1, flags_fd_m; + + flags_0 = fcntl (0, F_GETFL); + fcntl (0, F_SETFL, O_NONBLOCK | flags_0); + flags_1 = fcntl (0, F_GETFL); + fcntl (1, F_SETFL, O_NONBLOCK | flags_1); + flags_fd_m = fcntl (0, F_GETFL); + fcntl (fd_m, F_SETFL, O_NONBLOCK | flags_fd_m); + + for (;;) { + FD_ZERO (&rfds); + FD_ZERO (&wfds); + + /* Still space in to-buffer? If so, we can read from the user. */ + if (!eof_stdin && tosize < BUFSIZ) + FD_SET (0, &rfds); + /* Still space in from-buffer? If so, we can read from the slave. */ + if (fromsize < BUFSIZ) + FD_SET (fd_m, &rfds); + /* Content in to-buffer? If so, we want to write to the slave. */ + if (tosize > 0) + FD_SET (fd_m, &wfds); + /* Content in from-buffer? If so, we want to write to the user. */ + if (fromsize > 0) + FD_SET (1, &wfds); + + r = select (fd_m+1, &rfds, &wfds, NULL, NULL); + if (r == -1) { + if (errno == EAGAIN || errno == EINTR) + continue; + perror ("select"); + return -1; + } + + /* Input from user: Put it in the to-buffer. */ + if (FD_ISSET (0, &rfds)) { + count = BUFSIZ - tosize; + n = read (0, &tobuf[tosize], count); + if (n == -1) { + perror ("read"); + return -1; + } + if (n == 0) { /* stdin was closed */ + eof_stdin = 1; + /* This is what telnetd does ... */ + tobuf[tosize] = '\004'; + tosize += 1; + } else + tosize += n; + } + + /* Input from slave: Put it in the from-buffer. */ + if (FD_ISSET (fd_m, &rfds)) { + count = BUFSIZ - fromsize; + n = read (fd_m, &frombuf[fromsize], count); + if (n == -1) { + if (errno != EIO) /* EIO if slave process dies */ + perror ("read"); + break; + } + if (n == 0) /* slave closed the connection */ + break; + fromsize += n; + } + + /* Can write to user. */ + if (FD_ISSET (1, &wfds)) { + n = write (1, frombuf, fromsize); + if (n == -1) { + perror ("write"); + return -1; + } + memmove (frombuf, &frombuf[n], BUFSIZ - n); + fromsize -= n; + } + + /* Can write to slave. */ + if (FD_ISSET (fd_m, &wfds)) { + n = write (fd_m, tobuf, tosize); + if (n == -1) { + perror ("write"); + return -1; + } + memmove (tobuf, &tobuf[n], BUFSIZ - n); + tosize -= n; + } + } /* for (;;) */ + + /* We end up here when slave has closed the connection. */ + close (fd_m); + + /* Restore blocking behaviour. */ + fcntl (1, F_SETFL, flags_1); + + /* Last chance to write out any remaining data in the buffers, but + * don't bother about errors. + */ + ignore_value (write (1, frombuf, fromsize)); + + return 0; +} +#endif diff --git a/rescue/virt-rescue.pod b/rescue/virt-rescue.pod new file mode 100755 index 00000000..c24d9ec9 --- /dev/null +++ b/rescue/virt-rescue.pod @@ -0,0 +1,247 @@ +=encoding utf8 + +=head1 NAME + +virt-rescue - Run a rescue shell on a virtual machine + +=head1 SYNOPSIS + + virt-rescue [--options] -d domname + + virt-rescue [--options] -a disk.img [-a disk.img ...] + +Old style: + + virt-rescue [--options] domname + + virt-rescue [--options] disk.img [disk.img ...] + +=head1 WARNING + +You must I use C on live virtual machines. Doing so +will probably result in disk corruption in the VM. C +tries to stop you from doing this, but doesn't catch all cases. + +However if you use the I<--ro> (read only) option, then you can attach +a shell to a live virtual machine. The results might be strange or +inconsistent at times but you won't get disk corruption. + +=head1 DESCRIPTION + +virt-rescue is like a Rescue CD, but for virtual machines, and without +the need for a CD. virt-rescue gives you a rescue shell and some +simple recovery tools which you can use to examine or rescue a virtual +machine or disk image. + +You can run virt-rescue on any virtual machine known to libvirt, or +directly on disk image(s): + + virt-rescue -d GuestName + + virt-rescue --ro -a /path/to/disk.img + + virt-rescue -a /dev/sdc + +For live VMs you I use the --ro option. + +When you run virt-rescue on a virtual machine or disk image, you are +placed in an interactive bash shell where you can use many ordinary +Linux commands. What you see in C (C, C etc) is the +rescue appliance. You must mount the virtual machine's filesystems by +hand. There is an empty directory called C where you can +mount filesystems. + +In the example below, we list logical volumes, then choose one to +mount under C: + + > 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 + +If you don't know what filesystems are available on the virtual +machine then you can use commands such as L and L +to find out. + +=head2 NOTES + +Virt-rescue can be used on I disk image file or device, not just +a virtual machine. For example you can use it on a blank file if you +want to partition that file (although we would recommend using +L instead as it is more suitable for this purpose). You +can even use virt-rescue on things like SD cards. + +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. To get a structured shell that you can +use to make scripted changes to guests, use L. + +=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<--append kernelopts> + +Pass additional options to the rescue kernel. + +=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. + +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<--format=raw|qcow2|..> + +=item B<--format> + +The default for the I<-a> option is to auto-detect the format of the +disk image. Using this forces the disk format for I<-a> options which +follow on the command line. Using I<--format> with no argument +switches back to auto-detection for subsequent I<-a> options. + +For example: + + virt-rescue --format=raw -a disk.img + +forces raw format (no auto-detection) for C. + + virt-rescue --format=raw -a disk.img --format -a another.img + +forces raw format (no auto-detection) for C and reverts to +auto-detection for C. + +If you have untrusted raw-format guest disk images, you should use +this option to specify the disk format. This avoids a possible +security problem with malicious guests (CVE-2010-3851). See also +L. + +=item B<-m MB> + +=item B<--memsize MB> + +Change the amount of memory allocated to the rescue system. The +default is set by libguestfs and is small but adequate for running +system tools. The occasional program might need more memory. The +parameter is specified in megabytes. + +=item B<--network> + +Enable QEMU user networking in the guest. + +=item B<-r> + +=item B<--ro> + +Open the image read-only. + +The option must always be used if the disk image or virtual machine +might be running, and is generally recommended in cases where you +don't need write access to the disk. + +=item B<--selinux> + +Enable SELinux in the rescue appliance. You should read +L before using this option. + +=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-rescue allowed you to write either: + + virt-rescue disk.img [disk.img ...] + +or + + virt-rescue 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 ENVIRONMENT VARIABLES + +Several environment variables affect virt-rescue. See +L for the complete list. + +=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. + +=head1 AUTHOR + +Richard W.M. Jones L + +=head1 COPYRIGHT + +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 +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/Makefile.am b/tools/Makefile.am index 256d15cf..18030496 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -22,7 +22,6 @@ tools = \ list-filesystems \ list-partitions \ make-fs \ - rescue \ resize \ tar \ win-reg diff --git a/tools/virt-rescue b/tools/virt-rescue deleted file mode 100755 index 809d52c6..00000000 --- a/tools/virt-rescue +++ /dev/null @@ -1,297 +0,0 @@ -#!/usr/bin/perl -w -# virt-rescue -# 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 -# 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 Errno; -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 WARNING - -You must I use C on live virtual machines. Doing so -will probably result in disk corruption in the VM. C -tries to stop you from doing this, but doesn't catch all cases. - -However if you use the I<--ro> (read only) option, then you can attach -a shell to a live virtual machine. The results might be strange or -inconsistent at times but you won't get disk corruption. - -=head1 DESCRIPTION - -virt-rescue is like a Rescue CD, but for virtual machines, and without -the need for a CD. virt-rescue gives you a rescue shell and some -simple recovery tools which you can use to examine or rescue a virtual -machine or disk image. - -You can run virt-rescue on any virtual machine known to libvirt, or -directly on disk image(s): - - virt-rescue GuestName - - virt-rescue --ro /path/to/disk.img - - virt-rescue /dev/sdc - -For live VMs you I use the --ro option. - -When you run virt-rescue on a virtual machine or disk image, you are -placed in an interactive bash shell where you can use many ordinary -Linux commands. What you see in C (C, C etc) is the -rescue appliance. You must mount the virtual machine's filesystems by -hand. There is an empty directory called C where you can -mount filesystems. - -In the example below, we list logical volumes, then choose one to -mount under C: - - > 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 - -If you don't know what filesystems are available on the virtual -machine then you can use commands such as L and L -to find out. - -=head2 NOTES - -Virt-rescue can be used on I disk image file or device, not just -a virtual machine. For example you can use it on a blank file if you -want to partition that file (although we would recommend using -L instead as it is more suitable for this purpose). You -can even use virt-rescue on things like SD cards. - -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. To get a structured shell that you can -use to make scripted changes to guests, use L. - -=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 $append; - -=item B<--append kernelopts> - -Pass additional options to the rescue kernel. - -=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. - -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. - -=cut - -my $memsize; - -=item B<--memsize MB> | B<-m MB> - -Change the amount of memory allocated to the rescue system. The -default is set by libguestfs and is small but adequate for running -system tools. The occasional program might need more memory. The -parameter is specified in megabytes. - -=cut - -my $network; - -=item B<--network> - -Enable QEMU user networking in the guest. - -=cut - -my $readonly; - -=item B<--ro> | B<-r> - -Open the image read-only. - -The option must always be used if the disk image or virtual machine -might be running, and is generally recommended in cases where you -don't need write access to the disk. - -=cut - -my $selinux; - -=item B<--selinux> - -Enable SELinux in the rescue appliance. You should read -L before using this option. - -=back - -=cut - -GetOptions ("help|?" => \$help, - "version" => \$version, - "append=s" => \$append, - "connect|c=s" => \$uri, - "format=s" => \$format, - "memsize|m=i" => \$memsize, - "network" => \$network, - "ro|r" => \$readonly, - "selinux" => \$selinux, - ) 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; -push @args, format => $format if defined $format; -my $g = open_guest (@args); - -# Setting "direct mode" is required for the rescue appliance. -$g->set_direct (1); - -# Set other features. -$g->set_selinux (1) if $selinux; -$g->set_memsize ($memsize) if defined $memsize; -$g->set_network (1) if $network; - -# Set the kernel command line, which must include guestfs_rescue=1 -# (see appliance/init). -my $str = "guestfs_rescue=1"; -$str .= " $append" if defined $append; -$g->set_append ($str); - -# Run the appliance. This won't return until the user quits the -# appliance. -eval { $g->launch (); }; - -# launch() expects guestfsd to start. However, virt-rescue doesn't run guestfsd, -# so this will always fail with ECHILD when the appliance exits unexpectedly. -die $@ unless $!{ECHILD}; - -exit 0; - -=head1 ENVIRONMENT VARIABLES - -Several environment variables affect virt-rescue. See -L for the complete list. - -=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 AUTHOR - -Richard W.M. Jones L - -=head1 COPYRIGHT - -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 -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. -- cgit