From: Richard W.M. Jones Date: Fri, 26 Nov 2010 11:36:50 +0000 (+0000) Subject: rescue: Rewrite virt-rescue in C. X-Git-Tag: 1.7.15~4 X-Git-Url: http://git.annexia.org/?a=commitdiff_plain;h=a6054bf90f39a84c1a8f8f7d3b9e81e84cb13696;p=libguestfs.git rescue: Rewrite virt-rescue in C. --- diff --git a/.gitignore b/.gitignore index f86affa..7da3f32 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 fed296d..f59dd66 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 7dd0e97..86d4e0a 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 ac5e67a..90dffb0 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 5072fc7..fd8778a 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 0000000..1208be1 --- /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 0000000..95f3cc4 --- /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 0000000..3ae28e3 --- /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/tools/virt-rescue b/rescue/virt-rescue.pod similarity index 58% rename from tools/virt-rescue rename to rescue/virt-rescue.pod index 809d52c..c24d9ec 100755 --- a/tools/virt-rescue +++ b/rescue/virt-rescue.pod @@ -1,31 +1,3 @@ -#!/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 @@ -34,6 +6,12 @@ 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 ...] @@ -58,11 +36,11 @@ 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 -d GuestName - virt-rescue --ro /path/to/disk.img + virt-rescue --ro -a /path/to/disk.img - virt-rescue /dev/sdc + virt-rescue -a /dev/sdc For live VMs you I use the --ro option. @@ -104,83 +82,82 @@ use to make scripted changes to guests, use L. =over 4 -=cut - -my $help; - =item B<--help> Display brief help. -=cut - -my $version; +=item B<-a> file -=item B<--version> - -Display version number and exit. +=item B<--add> file -=cut +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. -my $append; +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. -=cut +=item B<-c> URI -my $uri; - -=item B<--connect URI> | 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, then libvirt is not used -at all. +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|..> -=cut +=item B<--format> -my $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. -=item B<--format> raw +For example: -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. + virt-rescue --format=raw -a disk.img -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. +forces raw format (no auto-detection) for C. -If working with untrusted raw-format guest disk images, you should -ensure the format is always specified. + virt-rescue --format=raw -a disk.img --format -a another.img -=cut +forces raw format (no auto-detection) for C and reverts to +auto-detection for C. -my $memsize; +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<--memsize MB> | B<-m MB> +=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. -=cut - -my $network; - =item B<--network> Enable QEMU user networking in the guest. -=cut - -my $readonly; +=item B<-r> -=item B<--ro> | B<-r> +=item B<--ro> Open the image read-only. @@ -188,69 +165,44 @@ 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. +=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 -=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 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 @@ -269,9 +221,7 @@ manual page L for details. L, L, L, -L, -L, -L, +L, L. =head1 AUTHOR diff --git a/tools/Makefile.am b/tools/Makefile.am index 256d15c..1803049 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