mirror of
https://github.com/libguestfs/libguestfs.git
synced 2026-03-21 22:53:37 +00:00
rescue: Rewrite virt-rescue in C.
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -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
|
||||
|
||||
3
HACKING
3
HACKING
@@ -148,6 +148,9 @@ python/
|
||||
regressions/
|
||||
Regression tests.
|
||||
|
||||
rescue/
|
||||
'virt-rescue' command and documentation.
|
||||
|
||||
ruby/
|
||||
Ruby bindings.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -869,6 +869,7 @@ AC_CONFIG_FILES([Makefile
|
||||
csharp/Makefile
|
||||
cat/Makefile
|
||||
df/Makefile
|
||||
rescue/Makefile
|
||||
ocaml/META perl/Makefile.PL])
|
||||
AC_OUTPUT
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
67
rescue/Makefile.am
Normal file
67
rescue/Makefile.am
Normal file
@@ -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
|
||||
52
rescue/run-rescue-locally
Executable file
52
rescue/run-rescue-locally
Executable file
@@ -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);
|
||||
526
rescue/virt-rescue.c
Normal file
526
rescue/virt-rescue.c
Normal file
@@ -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 <config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <inttypes.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
|
||||
#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
|
||||
@@ -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<must> use the --ro option.
|
||||
|
||||
@@ -104,83 +82,82 @@ use to make scripted changes to guests, use L<guestfish(1)>.
|
||||
|
||||
=over 4
|
||||
|
||||
=cut
|
||||
|
||||
my $help;
|
||||
|
||||
=item B<--help>
|
||||
|
||||
Display brief help.
|
||||
|
||||
=cut
|
||||
=item B<-a> file
|
||||
|
||||
my $version;
|
||||
=item B<--add> file
|
||||
|
||||
=item B<--version>
|
||||
Add I<file> 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.
|
||||
|
||||
Display version number and exit.
|
||||
|
||||
=cut
|
||||
|
||||
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<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.
|
||||
If you specify guest block devices directly (I<-a>), then libvirt is
|
||||
not used at all.
|
||||
|
||||
=cut
|
||||
=item B<-d> guest
|
||||
|
||||
my $format;
|
||||
=item B<--domain> guest
|
||||
|
||||
=item B<--format> raw
|
||||
Add all the disks from the named libvirt guest.
|
||||
|
||||
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.
|
||||
=item B<--format=raw|qcow2|..>
|
||||
|
||||
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.
|
||||
=item B<--format>
|
||||
|
||||
If working with untrusted raw-format guest disk images, you should
|
||||
ensure the format is always specified.
|
||||
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.
|
||||
|
||||
=cut
|
||||
For example:
|
||||
|
||||
my $memsize;
|
||||
virt-rescue --format=raw -a disk.img
|
||||
|
||||
=item B<--memsize MB> | B<-m MB>
|
||||
forces raw format (no auto-detection) for C<disk.img>.
|
||||
|
||||
virt-rescue --format=raw -a disk.img --format -a another.img
|
||||
|
||||
forces raw format (no auto-detection) for C<disk.img> and reverts to
|
||||
auto-detection for C<another.img>.
|
||||
|
||||
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</add-drive-opts>.
|
||||
|
||||
=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
|
||||
=item B<-r>
|
||||
|
||||
my $readonly;
|
||||
|
||||
=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<guestfs(3)/SELINUX> 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
|
||||
=head1 OLD-STYLE COMMAND LINE ARGUMENTS
|
||||
|
||||
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
|
||||
}
|
||||
Previous versions of virt-rescue allowed you to write either:
|
||||
|
||||
pod2usage (__"virt-rescue: no image or VM names rescue given")
|
||||
if @ARGV == 0;
|
||||
virt-rescue disk.img [disk.img ...]
|
||||
|
||||
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);
|
||||
or
|
||||
|
||||
# Setting "direct mode" is required for the rescue appliance.
|
||||
$g->set_direct (1);
|
||||
virt-rescue guestname
|
||||
|
||||
# Set other features.
|
||||
$g->set_selinux (1) if $selinux;
|
||||
$g->set_memsize ($memsize) if defined $memsize;
|
||||
$g->set_network (1) if $network;
|
||||
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.
|
||||
|
||||
# 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;
|
||||
For compatibility the old style is still supported.
|
||||
|
||||
=head1 ENVIRONMENT VARIABLES
|
||||
|
||||
@@ -269,9 +221,7 @@ manual page L<sh(1)> for details.
|
||||
L<guestfs(3)>,
|
||||
L<guestfish(1)>,
|
||||
L<virt-cat(1)>,
|
||||
L<Sys::Guestfs(3)>,
|
||||
L<Sys::Guestfs::Lib(3)>,
|
||||
L<Sys::Virt(3)>,
|
||||
L<virt-edit(1)>,
|
||||
L<http://libguestfs.org/>.
|
||||
|
||||
=head1 AUTHOR
|
||||
@@ -22,7 +22,6 @@ tools = \
|
||||
list-filesystems \
|
||||
list-partitions \
|
||||
make-fs \
|
||||
rescue \
|
||||
resize \
|
||||
tar \
|
||||
win-reg
|
||||
|
||||
Reference in New Issue
Block a user