mirror of
https://github.com/libguestfs/libguestfs.git
synced 2026-03-21 22:53:37 +00:00
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:
@@ -44,6 +44,7 @@ guestfish_SOURCES = \
|
||||
fish.c \
|
||||
fish.h \
|
||||
glob.c \
|
||||
inspect.c \
|
||||
lcd.c \
|
||||
man.c \
|
||||
more.c \
|
||||
|
||||
164
fish/fish.c
164
fish/fish.c
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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[]);
|
||||
|
||||
|
||||
@@ -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
117
fish/inspect.c
Normal 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);
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user