--autosysroot option uses suggestions to user on how to mount filesystems
and change root suggested by --suggest option in virt-rescue.
Commands are passed on kernel command line in format
guestfs_command=command;. Command ends with a semicolon and there can be
multiple commands specified. These are executed just before bash starts.
On successfull run user is presented directly with bash in chroot
environment.
RFE: RHBZ#1183493
Depends on commit: utils: make guestfs_int_count_strings return 0 on NULL input
---
appliance/init | 6 +++
rescue/rescue.c | 160 ++++++++++++++++++++++++++++++++++++++++++++------------
2 files changed, 134 insertions(+), 32 deletions(-)
diff --git a/appliance/init b/appliance/init
index 413a95f..82dac9f 100755
--- a/appliance/init
+++ b/appliance/init
@@ -182,6 +182,7 @@ else
:> $HOME/.bashrc
grep -Eo 'TERM=[^[:space:]]+' /proc/cmdline >> $HOME/.bashrc
+
echo "PS1='><rescue> '" >> $HOME/.bashrc
echo "export TERM PS1" >> $HOME/.bashrc
@@ -194,6 +195,11 @@ else
echo "You have to mount the guest's partitions under /sysroot"
echo "before you can examine them."
echo
+ echo 'Executing commands from kernel cmdline'
+
+ grep -Eo 'guestfs_command=([^;]+);[[:space:]]*' /proc/cmdline | sed -n
's/guestfs_command=\(.*;\)/\1/p'
+ eval $(grep -Eo 'guestfs_command=([^;]+);[[:space:]]*' /proc/cmdline | sed -n
's/guestfs_command=\(.*;\)/\1/p')
+
bash -i
echo
echo "virt-rescue: Syncing the disk now before exiting ..."
diff --git a/rescue/rescue.c b/rescue/rescue.c
index c46c775..9311d9e 100644
--- a/rescue/rescue.c
+++ b/rescue/rescue.c
@@ -37,7 +37,7 @@
#include "options.h"
static void add_scratch_disks (int n, struct drv **drvs);
-static void do_suggestion (struct drv *drvs);
+static char ** do_suggestion (struct drv *drvs);
/* Currently open libguestfs handle. */
guestfs_h *g;
@@ -50,6 +50,8 @@ int echo_keys = 0;
const char *libvirt_uri = NULL;
int inspector = 0;
+static void use_suggestions (char * *cmds);
+
static void __attribute__((noreturn))
usage (int status)
{
@@ -87,21 +89,27 @@ usage (int status)
exit (status);
}
-int
-main (int argc, char *argv[])
-{
- setlocale (LC_ALL, "");
- bindtextdomain (PACKAGE, LOCALEBASEDIR);
- textdomain (PACKAGE);
-
- parse_config ();
-
+ struct drv *drvs = NULL;
+ struct drv *drv;
+ char * *cmds = NULL;
+ const char *format = NULL;
+ bool format_consumed = true;
+ int c;
+ int option_index;
+ int network = 0;
+ char *append = NULL;
+ int memsize = 0;
+ int smp = 0;
+ int suggest = 0;
+ int autosysroot = 0;
+void parse_opts(int argc, char * argv[]) {
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 },
+ { "autosysroot", 0, 0, 0 },
{ "connect", 1, 0, 'c' },
{ "domain", 1, 0, 'd' },
{ "format", 2, 0, 0 },
@@ -120,22 +128,10 @@ main (int argc, char *argv[])
{ "version", 0, 0, 'V' },
{ 0, 0, 0, 0 }
};
- struct drv *drvs = NULL;
- struct drv *drv;
- const char *format = NULL;
- bool format_consumed = true;
- int c;
- int option_index;
- int network = 0;
- const char *append = NULL;
- int memsize = 0;
- int smp = 0;
- int suggest = 0;
- g = guestfs_create ();
- if (g == NULL)
- error (EXIT_FAILURE, errno, "guestfs_create");
+ option_index = 0;
+ optind = 0;
for (;;) {
c = getopt_long (argc, argv, options, long_options, &option_index);
if (c == -1) break;
@@ -150,7 +146,9 @@ main (int argc, char *argv[])
if (guestfs_set_selinux (g, 1) == -1)
exit (EXIT_FAILURE);
} else if (STREQ (long_options[option_index].name, "append")) {
- append = optarg;
+ append = strdup (optarg);
+ } else if (STREQ (long_options[option_index].name, "autosysroot")) {
+ autosysroot = 1;
} else if (STREQ (long_options[option_index].name, "network")) {
network = 1;
} else if (STREQ (long_options[option_index].name, "format")) {
@@ -259,10 +257,67 @@ main (int argc, char *argv[])
}
}
+}
+
+int
+main (int argc, char *argv[])
+{
+ setlocale (LC_ALL, "");
+ bindtextdomain (PACKAGE, LOCALEBASEDIR);
+ textdomain (PACKAGE);
+
+ parse_config ();
+ g = guestfs_create ();
+ if (g == NULL)
+ error (EXIT_FAILURE, errno, "guestfs_create");
+
+ parse_opts (argc, argv);
+
/* --suggest flag */
if (suggest) {
- do_suggestion (drvs);
- exit (EXIT_SUCCESS);
+ cmds = do_suggestion (drvs);
+ if (!autosysroot) {
+ exit (EXIT_SUCCESS);
+ } else {
+ /* Shut down libguestfs so we can start a new one */
+ guestfs_shutdown (g);
+ guestfs_close (g);
+
+ /* remove --suggest flag */
+ size_t newcount = argc;
+ for (int i = 0; i < argc; i++) {
+ if (strcmp (argv[i], "--suggest") == 0) {
+ newcount--;
+ }
+ }
+
+ char ** args = malloc (sizeof (char*) * (newcount));
+ for (int i = 0, j = 0; i < argc; i++) {
+ if (strcmp (argv[i], "--suggest") != 0) {
+ args[j] = strdup (argv[i]);
+ j++;
+ }
+ }
+
+ /* clear options */
+ drvs = NULL;
+ if (format) free(format);
+ format_consumed = true;
+ c = 0;
+ option_index = 0;
+ network = 0;
+ if (append) free(append);
+ memsize = 0;
+ smp = 0;
+ suggest = 0;
+ autosysroot = 0;
+
+ /* Create new guestfs handle, this time for rescue instead of OS detection */
+ g = guestfs_create ();
+ /* Parse options with --suggest flag removed */
+ parse_opts (newcount, args);
+ argc = newcount;
+ }
}
/* These are really constants, but they have to be variables for the
@@ -305,6 +360,11 @@ main (int argc, char *argv[])
}
}
+ /* Now it's time to set suggestions. */
+ if (autosysroot) {
+ use_suggestions (cmds);
+ }
+
/* Set other features. */
if (memsize > 0)
if (guestfs_set_memsize (g, memsize) == -1)
@@ -359,14 +419,33 @@ compare_keys_len (const void *p1, const void *p2)
return strlen (key1) - strlen (key2);
}
+/* Use autodetected suggested filesystems */
+static void
+use_suggestions (char * *cmds)
+{
+ size_t i;
+ if (cmds == NULL) return;
+ read_only = 0;
+
+ /* Craft kernel command line from suggested commands */
+ for (i = 0; cmds[i] != NULL; i++) {
+ char *old_append = append;
+ append = xasprintf ("%s guestfs_command=%s;", append == NULL? ""
: append, cmds[i]);
+ if (old_append != NULL)
+ free (old_append);
+ }
+}
+
/* virt-rescue --suggest flag does a kind of inspection on the
* drives and suggests mount commands that you should use.
*/
-static void
+static char **
do_suggestion (struct drv *drvs)
{
CLEANUP_FREE_STRING_LIST char **roots = NULL;
size_t i;
+ char * *cmds = NULL;
+
/* For inspection, force add_drives to add the drives read-only. */
read_only = 1;
@@ -393,7 +472,7 @@ do_suggestion (struct drv *drvs)
if (roots[0] == NULL) {
suggest_filesystems ();
- return;
+ return NULL;
}
printf (_("This disk contains one or more operating systems. You can use these
mount\n"
@@ -428,10 +507,16 @@ do_suggestion (struct drv *drvs)
qsort (mps, guestfs_int_count_strings (mps) / 2, 2 * sizeof (char *),
compare_keys_len);
- for (j = 0; mps[j] != NULL; j += 2)
- printf ("mount %s /sysroot%s\n", mps[j+1], mps[j]);
+ /* First we save commands to mount system root. */
+ for (j = 0; mps[j] != NULL; j += 2) {
+ int cnt = guestfs_int_count_strings (cmds);
+ cmds = realloc (cmds, sizeof (char *) * (cnt + 2));
- /* If it's Linux, print the bind-mounts and a chroot command. */
+ cmds[cnt] = xasprintf ("mount %s /sysroot%s", mps[j+1], mps[j]);
+ cmds[cnt+1] = NULL;
+ }
+
+ /* If it's Linux, print the bind-mounts and a chroot command and save them. */
if (type && STREQ (type, "linux")) {
printf ("mount --rbind /dev /sysroot/dev\n");
printf ("mount --rbind /proc /sysroot/proc\n");
@@ -439,10 +524,21 @@ do_suggestion (struct drv *drvs)
printf ("\n");
printf ("cd /sysroot\n");
printf ("chroot /sysroot\n");
+
+ int cnt = guestfs_int_count_strings (cmds);
+ cmds = realloc (cmds, sizeof (char *) * (cnt + 6));
+
+ cmds[cnt+0] = xasprintf ("mount --rbind /dev /sysroot/dev");
+ cmds[cnt+1] = xasprintf ("mount --rbind /proc /sysroot/proc");
+ cmds[cnt+2] = xasprintf ("mount --rbind /sys /sysroot/sys");
+ cmds[cnt+3] = xasprintf ("cd /sysroot");
+ cmds[cnt+4] = xasprintf ("chroot /sysroot");
+ cmds[cnt+5] = NULL;
}
printf ("\n");
}
+ return cmds;
}
/* Inspection failed, so it doesn't contain any OS that we recognise.
--
2.5.5