fish: Reimplement -i option using new C-based inspection.

Don't shell out to virt-inspector.  Instead, use the new C-based
inspection APIs.

This is much faster.

The new syntax is slightly different:

  guestfish -a disk.img -i
  guestfish -d guest -i

However, the old syntax still works.
This commit is contained in:
Richard Jones
2010-08-02 17:43:23 +01:00
parent 1a9aa565b3
commit 4440e22f4f
6 changed files with 187 additions and 137 deletions

View File

@@ -44,6 +44,7 @@ guestfish_SOURCES = \
fish.c \
fish.h \
glob.c \
inspect.c \
lcd.c \
man.c \
more.c \

View File

@@ -81,7 +81,6 @@ static void cleanup_readline (void);
#ifdef HAVE_LIBREADLINE
static void add_history_line (const char *);
#endif
static void print_shell_quote (FILE *stream, const char *str);
/* Currently open libguestfs handle. */
guestfs_h *g;
@@ -95,6 +94,7 @@ int exit_on_error = 1;
int command_num = 0;
int keys_from_stdin = 0;
const char *libvirt_uri = NULL;
int inspector = 0;
static void __attribute__((noreturn))
usage (int status)
@@ -126,7 +126,7 @@ usage (int status)
" -d|--domain guest Add disks from libvirt guest\n"
" -D|--no-dest-paths Don't tab-complete paths from guest fs\n"
" -f|--file file Read commands from file\n"
" -i|--inspector Run virt-inspector to get disk mountpoints\n"
" -i|--inspector Automatically mount filesystems\n"
" --keys-from-stdin Read passphrases from stdin\n"
" --listen Listen for remote commands\n"
" -m|--mount dev[:mnt] Mount dev on mnt (if omitted, /)\n"
@@ -188,7 +188,6 @@ main (int argc, char *argv[])
struct mp *mp;
char *p, *file = NULL;
int c;
int inspector = 0;
int option_index;
struct sigaction sa;
int next_prepared_drive = 1;
@@ -228,7 +227,7 @@ main (int argc, char *argv[])
* using it just above.
*
* getopt_long uses argv[0], so give it the sanitized name. Save a copy
* of the original, in case it's needed in virt-inspector mode, below.
* of the original, in case it's needed below.
*/
char *real_argv0 = argv[0];
argv[0] = bad_cast (program_name);
@@ -398,115 +397,46 @@ main (int argc, char *argv[])
}
}
/* Inspector mode invalidates most of the other arguments. */
if (inspector) {
if (drvs || mps || remote_control_listen || remote_control ||
guestfs_get_selinux (g)) {
fprintf (stderr, _("%s: cannot use -i option with -a, -m, -N, "
"--listen, --remote or --selinux\n"),
program_name);
exit (EXIT_FAILURE);
}
if (optind >= argc) {
fprintf (stderr,
_("%s: -i requires a libvirt domain or path(s) to disk image(s)\n"),
program_name);
exit (EXIT_FAILURE);
}
char *cmd;
size_t cmdlen;
FILE *fp = open_memstream (&cmd, &cmdlen);
if (fp == NULL) {
perror ("open_memstream");
exit (EXIT_FAILURE);
}
fprintf (fp, "virt-inspector");
/* Old-style -i syntax? Since -a/-d/-N and -i was disallowed
* previously, if we have -i without any drives but with something
* on the command line, it must be old-style syntax.
*/
if (inspector && drvs == NULL && optind < argc) {
while (optind < argc) {
fputc (' ', fp);
print_shell_quote (fp, argv[optind]);
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->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++;
}
if (read_only)
fprintf (fp, " --ro-fish");
else
fprintf (fp, " --fish");
if (fclose (fp) == -1) {
perror ("fclose");
exit (EXIT_FAILURE);
}
if (verbose)
fprintf (stderr,
"%s -i: running: %s\n", program_name, cmd);
FILE *pp = popen (cmd, "r");
if (pp == NULL) {
perror (cmd);
exit (EXIT_FAILURE);
}
char *cmd2;
fp = open_memstream (&cmd2, &cmdlen);
if (fp == NULL) {
perror ("open_memstream");
exit (EXIT_FAILURE);
}
fprintf (fp, "%s", real_argv0);
if (guestfs_get_verbose (g))
fprintf (fp, " -v");
if (!guestfs_get_autosync (g))
fprintf (fp, " -n");
if (guestfs_get_trace (g))
fprintf (fp, " -x");
char *insp = NULL;
size_t insplen;
if (getline (&insp, &insplen, pp) == -1) {
perror (cmd);
exit (EXIT_FAILURE);
}
fprintf (fp, " %s", insp);
if (pclose (pp) == -1) {
perror (cmd);
exit (EXIT_FAILURE);
}
if (fclose (fp) == -1) {
perror ("fclose");
exit (EXIT_FAILURE);
}
if (verbose)
fprintf (stderr,
"%s -i: running: %s\n", program_name, cmd2);
int r = system (cmd2);
if (r == -1) {
perror (cmd2);
exit (EXIT_FAILURE);
}
free (cmd);
free (cmd2);
free (insp);
exit (WEXITSTATUS (r));
}
/* If we've got drives to add, add them now. */
add_drives (drvs, 'a');
/* If we've got mountpoints or prepared drives, we must launch the
* guest and mount them.
/* If we've got mountpoints or prepared drives or -i option, we must
* launch the guest and mount them.
*/
if (next_prepared_drive > 1 || mps != NULL) {
if (next_prepared_drive > 1 || mps != NULL || inspector) {
/* RHBZ#612178: If --listen flag is given, then we will fork into
* the background in rc_listen(). However you can't do this while
* holding a libguestfs handle open because the recovery process
@@ -519,6 +449,10 @@ main (int argc, char *argv[])
guestfs_set_recovery_proc (g, 0);
if (launch () == -1) exit (EXIT_FAILURE);
if (inspector)
inspect_mount ();
prepare_drives (drvs);
mount_mps (mps);
}
@@ -747,7 +681,7 @@ script (int prompt)
int global_exit_on_error = !prompt;
int tilde_candidate;
if (prompt)
if (prompt) {
printf (_("\n"
"Welcome to guestfish, the libguestfs filesystem interactive shell for\n"
"editing virtual machine filesystems.\n"
@@ -757,6 +691,12 @@ script (int prompt)
" 'quit' to quit the shell\n"
"\n"));
if (inspector) {
print_inspect_prompt ();
printf ("\n");
}
}
while (!quit) {
char *pipe = NULL;
@@ -1840,17 +1780,3 @@ read_key (const char *param)
return ret;
}
static void
print_shell_quote (FILE *stream, const char *str)
{
#define SAFE(c) (c_isalnum((c)) || \
(c) == '/' || (c) == '-' || (c) == '_' || (c) == '.')
int i;
for (i = 0; str[i]; ++i) {
if (!SAFE(str[i]))
putc ('\\', stream);
putc (str[i], stream);
}
}

View File

@@ -96,6 +96,10 @@ extern int do_echo (const char *cmd, int argc, char *argv[]);
/* in edit.c */
extern int do_edit (const char *cmd, int argc, char *argv[]);
/* in inspect.c */
extern void inspect_mount (void);
extern void print_inspect_prompt (void);
/* in lcd.c */
extern int do_lcd (const char *cmd, int argc, char *argv[]);

View File

@@ -16,9 +16,9 @@ guestfish - the libguestfs Filesystem Interactive SHell
guestfish -d libvirt-domain
guestfish -i libvirt-domain
guestfish -a disk.img -i
guestfish -i disk.img [disk.img ...]
guestfish -d libvirt-domain -i
=head1 WARNING
@@ -75,13 +75,14 @@ Edit C</boot/grub/grub.conf> interactively:
--mount /dev/sda1:/boot \
edit /boot/grub/grub.conf
=head2 Using virt-inspector
=head2 Mount disks automatically
Use the I<-i> option to get virt-inspector to mount
the filesystems automatically as they would be mounted
in the virtual machine:
Use the I<-i> option to automatically mount the
disks from a virtual machine:
guestfish --ro -i disk.img cat /etc/group
guestfish --ro -a disk.img -i cat /etc/group
guestfish --ro -d libvirt-domain -i cat /etc/group
=head2 As a script interpreter
@@ -170,28 +171,28 @@ scripts, use:
=item B<-i> | B<--inspector>
Run virt-inspector on the named libvirt domain or list of disk
images. If virt-inspector is available and if it can identify
the domain or disk images, then partitions will be mounted
correctly at start-up.
Using L<virt-inspector(1)> code, inspect the disks looking for
an operating system and mount filesystems as they would be
mounted on the real virtual machine.
Typical usage is either:
guestfish -i myguest
guestfish -d myguest -i
(for an inactive libvirt domain called I<myguest>), or:
guestfish --ro -i myguest
guestfish --ro -d myguest -i
(for active domains, readonly), or specify the block device directly:
guestfish -i /dev/Guests/MyGuest
guestfish -a /dev/Guests/MyGuest -i
You cannot use I<-a>, I<-m>, I<-N>, I<--listen>, I<--remote> or
I<--selinux> in conjunction with this option, and options other than
I<--ro> might not behave correctly.
Note that the command line syntax changed slightly over older
versions of guestfish. You can still use the old syntax:
See also: L<virt-inspector(1)>.
guestfish [--ro] -i disk.img
guestfish [--ro] -i libvirt-domain
=item B<--keys-from-stdin>

117
fish/inspect.c Normal file
View File

@@ -0,0 +1,117 @@
/* guestfish - the filesystem interactive shell
* 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 <string.h>
#include "fish.h"
/* Global that saves the root device between inspect_mount and
* print_inspect_prompt.
*/
static char *root = NULL;
static int
compare_keys_len (const void *p1, const void *p2)
{
const char *key1 = * (char * const *) p1;
const char *key2 = * (char * const *) p2;
return strlen (key1) - strlen (key2);
}
static int
compare_keys (const void *p1, const void *p2)
{
const char *key1 = * (char * const *) p1;
const char *key2 = * (char * const *) p2;
return strcasecmp (key1, key2);
}
/* This function implements the -i option. */
void
inspect_mount (void)
{
char **roots = guestfs_inspect_os (g);
if (roots == NULL)
exit (EXIT_FAILURE);
if (roots[0] == NULL) {
fprintf (stderr, _("guestfish: no operating system was found on this disk\n"));
exit (EXIT_FAILURE);
}
if (roots[1] != NULL) {
fprintf (stderr, _("guestfish: multi-boot operating systems are not supported by the -i option\n"));
exit (EXIT_FAILURE);
}
root = roots[0];
free (roots);
char **mountpoints = guestfs_inspect_get_mountpoints (g, root);
if (mountpoints == NULL)
exit (EXIT_FAILURE);
/* Sort by key length, shortest key first, so that we end up
* mounting the filesystems in the correct order.
*/
qsort (mountpoints, count_strings (mountpoints) / 2, 2 * sizeof (char *),
compare_keys_len);
size_t i;
for (i = 0; mountpoints[i] != NULL; i += 2) {
int r;
if (!read_only)
r = guestfs_mount_options (g, "", mountpoints[i+1], mountpoints[i]);
else
r = guestfs_mount_ro (g, mountpoints[i+1], mountpoints[i]);
if (r == -1)
exit (EXIT_FAILURE);
}
free_strings (mountpoints);
}
/* This function is called only if the above function was called,
* and only after we've printed the prompt in interactive mode.
*/
void
print_inspect_prompt (void)
{
char *name = guestfs_inspect_get_product_name (g, root);
if (STRNEQ (name, "unknown"))
printf (_("Operating system: %s\n"), name);
free (name);
char **mountpoints = guestfs_inspect_get_mountpoints (g, root);
if (mountpoints == NULL)
return;
/* Sort by key. */
qsort (mountpoints, count_strings (mountpoints) / 2, 2 * sizeof (char *),
compare_keys);
size_t i;
for (i = 0; mountpoints[i] != NULL; i += 2)
printf (_("%s mounted on %s\n"), mountpoints[i+1], mountpoints[i]);
free_strings (mountpoints);
}

View File

@@ -76,6 +76,7 @@ fish/echo.c
fish/edit.c
fish/fish.c
fish/glob.c
fish/inspect.c
fish/lcd.c
fish/man.c
fish/more.c