guestfish.pod \
guestfish-bash-completion.sh
+# These source files (all related to option parsing) are shared
+# between guestfish and guestmount. Keep a convenient list here just
+# so we know which ones are shared. These files must not include
+# other guestfish files.
+SHARED_SOURCE_FILES = \
+ inspect.c \
+ options.h \
+ options.c \
+ virt.c
+
guestfish_SOURCES = \
$(generator_built) \
+ $(SHARED_SOURCE_FILES) \
alloc.c \
copy.c \
destpaths.c \
fish.h \
glob.c \
hexedit.c \
- inspect.c \
lcd.c \
man.c \
more.c \
rmsd.h \
supported.c \
tilde.c \
- time.c \
- virt.c
+ time.c
# This convenience library is solely to avoid compiler warnings
# in its generated sources.
#include <guestfs.h>
#include "fish.h"
+#include "options.h"
+
#include "c-ctype.h"
#include "closeout.h"
#include "progname.h"
-/* List of drives added via -a, -d or -N options. */
-struct drv {
- struct drv *next;
- enum { drv_a, drv_d, drv_N } type;
- union {
- struct {
- char *filename; /* disk filename */
- const char *format; /* format (NULL == autodetect) */
- } a;
- struct {
- char *guest; /* guest name */
- } d;
- struct {
- char *filename; /* disk filename (testX.img) */
- prep_data *data; /* prepared type */
- char *device; /* device inside the appliance */
- } N;
- };
-};
-
-struct mp {
- struct mp *next;
- char *device;
- char *mountpoint;
-};
-
static void set_up_terminal (void);
-static char add_drives (struct drv *drv, char next_drive);
static void prepare_drives (struct drv *drv);
-static void mount_mps (struct mp *mp);
-static void free_drives (struct drv *drv);
-static void free_mps (struct mp *mp);
static int launch (void);
static void interactive (void);
static void shell_script (void);
" -r|--ro Mount read-only\n"
" --selinux Enable SELinux support\n"
" -v|--verbose Verbose messages\n"
- " -x Echo each command before executing it\n"
" -V|--version Display version and exit\n"
+ " -x Echo each command before executing it\n"
"For more information, see the manpage %s(1).\n"),
program_name, program_name, program_name,
program_name, program_name, program_name,
break;
case 'a':
- if (access (optarg, R_OK) != 0) {
- perror (optarg);
- exit (EXIT_FAILURE);
- }
- drv = malloc (sizeof (struct drv));
- if (!drv) {
- perror ("malloc");
- exit (EXIT_FAILURE);
- }
- drv->type = drv_a;
- drv->a.filename = optarg;
- drv->a.format = format;
- drv->next = drvs;
- drvs = drv;
+ OPTION_a;
break;
case 'c':
- libvirt_uri = optarg;
+ OPTION_c;
break;
case 'd':
- drv = malloc (sizeof (struct drv));
- if (!drv) {
- perror ("malloc");
- exit (EXIT_FAILURE);
- }
- drv->type = drv_d;
- drv->d.guest = optarg;
- drv->next = drvs;
- drvs = drv;
- break;
-
- case 'N':
- if (STRCASEEQ (optarg, "list") ||
- STRCASEEQ (optarg, "help") ||
- STRCASEEQ (optarg, "h") ||
- STRCASEEQ (optarg, "?")) {
- list_prepared_drives ();
- exit (EXIT_SUCCESS);
- }
- drv = malloc (sizeof (struct drv));
- if (!drv) {
- perror ("malloc");
- exit (EXIT_FAILURE);
- }
- drv->type = drv_N;
- if (asprintf (&drv->N.filename, "test%d.img",
- next_prepared_drive++) == -1) {
- perror ("asprintf");
- exit (EXIT_FAILURE);
- }
- drv->N.data = create_prepared_file (optarg, drv->N.filename);
- drv->N.device = NULL; /* filled in by add_drives */
- drv->next = drvs;
- drvs = drv;
+ OPTION_d;
break;
case 'D':
}
case 'i':
- inspector = 1;
+ OPTION_i;
break;
case 'm':
- mp = malloc (sizeof (struct mp));
- if (!mp) {
- perror ("malloc");
- exit (EXIT_FAILURE);
- }
- p = strchr (optarg, ':');
- if (p) {
- *p = '\0';
- mp->mountpoint = p+1;
- } else
- mp->mountpoint = bad_cast ("/");
- mp->device = optarg;
- mp->next = mps;
- mps = mp;
+ OPTION_m;
break;
case 'n':
- guestfs_set_autosync (g, 0);
+ OPTION_n;
+ break;
+
+ case 'N':
+ if (STRCASEEQ (optarg, "list") ||
+ STRCASEEQ (optarg, "help") ||
+ STRCASEEQ (optarg, "h") ||
+ STRCASEEQ (optarg, "?")) {
+ list_prepared_drives ();
+ exit (EXIT_SUCCESS);
+ }
+ drv = malloc (sizeof (struct drv));
+ if (!drv) {
+ perror ("malloc");
+ exit (EXIT_FAILURE);
+ }
+ drv->type = drv_N;
+ if (asprintf (&drv->N.filename, "test%d.img",
+ next_prepared_drive++) == -1) {
+ perror ("asprintf");
+ exit (EXIT_FAILURE);
+ }
+ drv->N.data = create_prepared_file (optarg, drv->N.filename);
+ drv->N.data_free = free_prep_data;
+ drv->N.device = NULL; /* filled in by add_drives */
+ drv->next = drvs;
+ drvs = drv;
break;
case 'r':
- read_only = 1;
+ OPTION_r;
break;
case 'v':
- verbose++;
- guestfs_set_verbose (g, verbose);
+ OPTION_v;
break;
- case 'V': {
- struct guestfs_version *v = guestfs_version (g);
- printf ("%s %"PRIi64".%"PRIi64".%"PRIi64"%s\n", program_name,
- v->major, v->minor, v->release, v->extra);
- exit (EXIT_SUCCESS);
- }
+ case 'V':
+ OPTION_V;
+ break;
case 'x':
- guestfs_set_trace (g, 1);
+ OPTION_x;
break;
case HELP_OPTION:
pclose (fp);
}
-/* List is built in reverse order, so mount them in reverse order. */
-static void
-mount_mps (struct mp *mp)
-{
- int r;
-
- if (mp) {
- mount_mps (mp->next);
-
- /* Don't use guestfs_mount here because that will default to mount
- * options -o sync,noatime. For more information, see guestfs(3)
- * section "LIBGUESTFS GOTCHAS".
- */
- const char *options = read_only ? "ro" : "";
- r = guestfs_mount_options (g, options, mp->device, mp->mountpoint);
- if (r == -1) {
- /* Display possible mountpoints before exiting. */
- char **fses = guestfs_list_filesystems (g);
- if (fses == NULL || fses[0] == NULL)
- goto out;
- fprintf (stderr,
- _("guestfish: '%s' could not be mounted. Did you mean one of these?\n"),
- mp->device);
- size_t i;
- for (i = 0; fses[i] != NULL; i += 2)
- fprintf (stderr, "\t%s (%s)\n", fses[i], fses[i+1]);
-
- out:
- exit (EXIT_FAILURE);
- }
- }
-}
-
-static char
-add_drives (struct drv *drv, char next_drive)
-{
- int r;
- struct guestfs_add_drive_opts_argv ad_optargs;
-
- if (next_drive > 'z') {
- fprintf (stderr,
- _("guestfish: too many drives added on the command line\n"));
- exit (EXIT_FAILURE);
- }
-
- if (drv) {
- next_drive = add_drives (drv->next, next_drive);
-
- switch (drv->type) {
- case drv_a:
- ad_optargs.bitmask = 0;
- if (read_only) {
- ad_optargs.bitmask |= GUESTFS_ADD_DRIVE_OPTS_READONLY_BITMASK;
- ad_optargs.readonly = 1;
- }
- if (drv->a.format) {
- ad_optargs.bitmask |= GUESTFS_ADD_DRIVE_OPTS_FORMAT_BITMASK;
- ad_optargs.format = drv->a.format;
- }
- r = guestfs_add_drive_opts_argv (g, drv->a.filename, &ad_optargs);
- if (r == -1)
- exit (EXIT_FAILURE);
-
- next_drive++;
- break;
-
- case drv_d:
- r = add_libvirt_drives (drv->d.guest);
- if (r == -1)
- exit (EXIT_FAILURE);
-
- next_drive += r;
- break;
-
- case drv_N:
- /* guestfs_add_drive (ie. autodetecting) should be safe here
- * since we have just created the prepared disk. At the moment
- * it will always be "raw" but in a theoretical future we might
- * create other formats.
- */
- /* -N option is not affected by --ro */
- r = guestfs_add_drive (g, drv->N.filename);
- if (r == -1)
- exit (EXIT_FAILURE);
-
- if (asprintf (&drv->N.device, "/dev/sd%c", next_drive) == -1) {
- perror ("asprintf");
- exit (EXIT_FAILURE);
- }
-
- next_drive++;
- break;
-
- default: /* keep GCC happy */
- abort ();
- }
- }
-
- return next_drive;
-}
-
static void
prepare_drives (struct drv *drv)
{
}
}
-static void
-free_drives (struct drv *drv)
-{
- if (!drv) return;
- free_drives (drv->next);
-
- switch (drv->type) {
- case drv_a: /* a.filename and a.format are optargs, don't free them */ break;
- case drv_d: /* d.filename is optarg, don't free it */ break;
- case drv_N:
- free (drv->N.filename);
- free (drv->N.device);
- free_prep_data (drv->N.data);
- break;
- default: ; /* keep GCC happy */
- }
- free (drv);
-}
-
-static void
-free_mps (struct mp *mp)
-{
- if (!mp) return;
- free_mps (mp->next);
-
- /* The drive and mountpoint fields are not allocated
- * from the heap, so we should not free them here.
- */
-
- free (mp);
-}
-
static int
launch (void)
{
/* in hexedit.c */
extern int run_hexedit (const char *cmd, size_t argc, char *argv[]);
-/* in inspect.c */
-extern void inspect_mount (void);
-extern void print_inspect_prompt (void);
-
/* in lcd.c */
extern int run_lcd (const char *cmd, size_t argc, char *argv[]);
extern void prepare_drive (const char *filename, prep_data *data,
const char *device);
extern void prep_error (prep_data *data, const char *filename, const char *fs, ...) __attribute__((noreturn, format (printf,3,4)));
-extern void free_prep_data (prep_data *data);
+extern void free_prep_data (void *data);
/* in prep_lv.c */
extern int vg_lv_parse (const char *device, char **vg, char **lv);
/* in tilde.c */
extern char *try_tilde_expansion (char *path);
-/* in virt.c */
-extern int add_libvirt_drives (const char *guest);
-
/* This should just list all the built-in commands so they can
* be added to the generated auto-completion code.
*/
-/* guestfish - the filesystem interactive shell
+/* libguestfs - guestfish and guestmount shared option parsing
* Copyright (C) 2010 Red Hat Inc.
*
* This program is free software; you can redistribute it and/or modify
#include <stdlib.h>
#include <string.h>
-#include "fish.h"
+#include "guestfs.h"
+
+#include "options.h"
/* Global that saves the root device between inspect_mount and
* print_inspect_prompt.
*/
static char *root = NULL;
+static void
+free_strings (char **argv)
+{
+ int argc;
+
+ for (argc = 0; argv[argc] != NULL; ++argc)
+ free (argv[argc]);
+ free (argv);
+}
+
+static int
+count_strings (char *const *argv)
+{
+ int c;
+
+ for (c = 0; argv[c]; ++c)
+ ;
+ return c;
+}
+
static int
compare_keys_len (const void *p1, const void *p2)
{
exit (EXIT_FAILURE);
if (roots[0] == NULL) {
- fprintf (stderr, _("guestfish: no operating system was found on this disk\n"));
+ fprintf (stderr, _("%s: no operating system was found on this disk\n"),
+ program_name);
exit (EXIT_FAILURE);
}
if (roots[1] != NULL) {
- fprintf (stderr, _("guestfish: multi-boot operating systems are not supported by the -i option\n"));
+ fprintf (stderr, _("%s: multi-boot operating systems are not supported by the -i option\n"),
+ program_name);
exit (EXIT_FAILURE);
}
--- /dev/null
+/* libguestfs - guestfish and guestmount shared option parsing
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "guestfs.h"
+
+#include "options.h"
+
+char
+add_drives (struct drv *drv, char next_drive)
+{
+ int r;
+ struct guestfs_add_drive_opts_argv ad_optargs;
+
+ if (next_drive > 'z') {
+ fprintf (stderr,
+ _("%s: too many drives added on the command line\n"),
+ program_name);
+ exit (EXIT_FAILURE);
+ }
+
+ if (drv) {
+ next_drive = add_drives (drv->next, next_drive);
+
+ switch (drv->type) {
+ case drv_a:
+ ad_optargs.bitmask = 0;
+ if (read_only) {
+ ad_optargs.bitmask |= GUESTFS_ADD_DRIVE_OPTS_READONLY_BITMASK;
+ ad_optargs.readonly = 1;
+ }
+ if (drv->a.format) {
+ ad_optargs.bitmask |= GUESTFS_ADD_DRIVE_OPTS_FORMAT_BITMASK;
+ ad_optargs.format = drv->a.format;
+ }
+ r = guestfs_add_drive_opts_argv (g, drv->a.filename, &ad_optargs);
+ if (r == -1)
+ exit (EXIT_FAILURE);
+
+ next_drive++;
+ break;
+
+ case drv_d:
+ r = add_libvirt_drives (drv->d.guest);
+ if (r == -1)
+ exit (EXIT_FAILURE);
+
+ next_drive += r;
+ break;
+
+ case drv_N:
+ /* guestfs_add_drive (ie. autodetecting) should be safe here
+ * since we have just created the prepared disk. At the moment
+ * it will always be "raw" but in a theoretical future we might
+ * create other formats.
+ */
+ /* -N option is not affected by --ro */
+ r = guestfs_add_drive (g, drv->N.filename);
+ if (r == -1)
+ exit (EXIT_FAILURE);
+
+ if (asprintf (&drv->N.device, "/dev/sd%c", next_drive) == -1) {
+ perror ("asprintf");
+ exit (EXIT_FAILURE);
+ }
+
+ next_drive++;
+ break;
+
+ default: /* keep GCC happy */
+ abort ();
+ }
+ }
+
+ return next_drive;
+}
+
+/* List is built in reverse order, so mount them in reverse order. */
+void
+mount_mps (struct mp *mp)
+{
+ int r;
+
+ if (mp) {
+ mount_mps (mp->next);
+
+ /* Don't use guestfs_mount here because that will default to mount
+ * options -o sync,noatime. For more information, see guestfs(3)
+ * section "LIBGUESTFS GOTCHAS".
+ */
+ const char *options = read_only ? "ro" : "";
+ r = guestfs_mount_options (g, options, mp->device, mp->mountpoint);
+ if (r == -1) {
+ /* Display possible mountpoints before exiting. */
+ char **fses = guestfs_list_filesystems (g);
+ if (fses == NULL || fses[0] == NULL)
+ goto out;
+ fprintf (stderr,
+ _("%s: '%s' could not be mounted. Did you mean one of these?\n"),
+ program_name, mp->device);
+ size_t i;
+ for (i = 0; fses[i] != NULL; i += 2)
+ fprintf (stderr, "\t%s (%s)\n", fses[i], fses[i+1]);
+
+ out:
+ exit (EXIT_FAILURE);
+ }
+ }
+}
+
+void
+free_drives (struct drv *drv)
+{
+ if (!drv) return;
+ free_drives (drv->next);
+
+ switch (drv->type) {
+ case drv_a: /* a.filename and a.format are optargs, don't free them */ break;
+ case drv_d: /* d.filename is optarg, don't free it */ break;
+ case drv_N:
+ free (drv->N.filename);
+ free (drv->N.device);
+ drv->N.data_free (drv->N.data);
+ break;
+ default: ; /* keep GCC happy */
+ }
+ free (drv);
+}
+
+void
+free_mps (struct mp *mp)
+{
+ if (!mp) return;
+ free_mps (mp->next);
+
+ /* The drive and mountpoint fields are not allocated
+ * from the heap, so we should not free them here.
+ */
+
+ free (mp);
+}
--- /dev/null
+/* libguestfs - guestfish and guestmount shared option parsing
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef OPTIONS_H
+#define OPTIONS_H
+
+#ifdef HAVE_GETTEXT
+#include "gettext.h"
+#ifndef _
+#define _(str) dgettext(PACKAGE, (str))
+#endif
+#ifndef N_
+#define N_(str) dgettext(PACKAGE, (str))
+#endif
+#else
+#ifndef _
+#define _(str) str
+#endif
+#ifndef _
+#define N_(str) str
+#endif
+#endif
+
+#ifndef STREQ
+#define STREQ(a,b) (strcmp((a),(b)) == 0)
+#endif
+#ifndef STRCASEEQ
+#define STRCASEEQ(a,b) (strcasecmp((a),(b)) == 0)
+#endif
+#ifndef STRNEQ
+#define STRNEQ(a,b) (strcmp((a),(b)) != 0)
+#endif
+#ifndef STRCASENEQ
+#define STRCASENEQ(a,b) (strcasecmp((a),(b)) != 0)
+#endif
+#ifndef STREQLEN
+#define STREQLEN(a,b,n) (strncmp((a),(b),(n)) == 0)
+#endif
+#ifndef STRCASEEQLEN
+#define STRCASEEQLEN(a,b,n) (strncasecmp((a),(b),(n)) == 0)
+#endif
+#ifndef STRNEQLEN
+#define STRNEQLEN(a,b,n) (strncmp((a),(b),(n)) != 0)
+#endif
+#ifndef STRCASENEQLEN
+#define STRCASENEQLEN(a,b,n) (strncasecmp((a),(b),(n)) != 0)
+#endif
+#ifndef STRPREFIX
+#define STRPREFIX(a,b) (strncmp((a),(b),strlen((b))) == 0)
+#endif
+
+/* Provided by guestfish or guestmount. */
+extern guestfs_h *g;
+extern int read_only;
+extern int verbose;
+extern int inspector;
+extern const char *libvirt_uri;
+extern const char *program_name;
+
+/* List of drives added via -a, -d or -N options. */
+struct drv {
+ struct drv *next;
+ enum { drv_a, drv_d, drv_N } type;
+ union {
+ struct {
+ char *filename; /* disk filename */
+ const char *format; /* format (NULL == autodetect) */
+ } a;
+ struct {
+ char *guest; /* guest name */
+ } d;
+ struct {
+ char *filename; /* disk filename (testX.img) */
+ void *data; /* prepared type */
+ void (*data_free)(void*); /* function to free 'data' */
+ char *device; /* device inside the appliance */
+ } N;
+ };
+};
+
+struct mp {
+ struct mp *next;
+ char *device;
+ char *mountpoint;
+};
+
+/* in inspect.c */
+extern void inspect_mount (void);
+extern void print_inspect_prompt (void);
+
+/* in options.c */
+extern char add_drives (struct drv *drv, char next_drive);
+extern void mount_mps (struct mp *mp);
+extern void free_drives (struct drv *drv);
+extern void free_mps (struct mp *mp);
+
+/* in virt.c */
+extern int add_libvirt_drives (const char *guest);
+
+#define OPTION_a \
+ if (access (optarg, R_OK) != 0) { \
+ perror (optarg); \
+ exit (EXIT_FAILURE); \
+ } \
+ drv = malloc (sizeof (struct drv)); \
+ if (!drv) { \
+ perror ("malloc"); \
+ exit (EXIT_FAILURE); \
+ } \
+ drv->type = drv_a; \
+ drv->a.filename = optarg; \
+ drv->a.format = format; \
+ drv->next = drvs; \
+ drvs = drv
+
+#define OPTION_c \
+ libvirt_uri = optarg
+
+#define OPTION_d \
+ drv = malloc (sizeof (struct drv)); \
+ if (!drv) { \
+ perror ("malloc"); \
+ exit (EXIT_FAILURE); \
+ } \
+ drv->type = drv_d; \
+ drv->d.guest = optarg; \
+ drv->next = drvs; \
+ drvs = drv
+
+#define OPTION_i \
+ inspector = 1
+
+#define OPTION_m \
+ mp = malloc (sizeof (struct mp)); \
+ if (!mp) { \
+ perror ("malloc"); \
+ exit (EXIT_FAILURE); \
+ } \
+ p = strchr (optarg, ':'); \
+ if (p) { \
+ *p = '\0'; \
+ mp->mountpoint = p+1; \
+ } else \
+ mp->mountpoint = bad_cast ("/"); \
+ mp->device = optarg; \
+ mp->next = mps; \
+ mps = mp
+
+#define OPTION_n \
+ guestfs_set_autosync (g, 0)
+
+#define OPTION_r \
+ read_only = 1
+
+#define OPTION_v \
+ verbose++; \
+ guestfs_set_verbose (g, verbose)
+
+#define OPTION_V \
+ { \
+ struct guestfs_version *v = guestfs_version (g); \
+ printf ("%s %"PRIi64".%"PRIi64".%"PRIi64"%s\n", \
+ program_name, \
+ v->major, v->minor, v->release, v->extra); \
+ exit (EXIT_SUCCESS); \
+ }
+
+#define OPTION_x \
+ guestfs_set_trace (g, 1)
+
+#endif /* OPTIONS_H */
}
void
-free_prep_data (prep_data *data)
+free_prep_data (void *vp)
{
+ prep_data *data = vp;
size_t i;
for (i = 0; i < data->prep->nr_params; ++i)
-/* guestfish - the filesystem interactive shell
+/* libguestfs - guestfish and guestmount shared option parsing
* Copyright (C) 2010 Red Hat Inc.
*
* This program is free software; you can redistribute it and/or modify
#include <libxml/parser.h>
#include <libxml/tree.h>
-#include "fish.h"
+#include "guestfs.h"
+
+#include "options.h"
/* Implements the guts of the '-d' option.
*
conn = virConnectOpenReadOnly (libvirt_uri);
if (!conn) {
err = virGetLastError ();
- fprintf (stderr, _("guestfish: could not connect to libvirt (code %d, domain %d): %s\n"),
- err->code, err->domain, err->message);
+ fprintf (stderr, _("%s: could not connect to libvirt (code %d, domain %d): %s\n"),
+ program_name, err->code, err->domain, err->message);
goto cleanup;
}
dom = virDomainLookupByName (conn, guest);
if (!dom) {
err = virConnGetLastError (conn);
- fprintf (stderr, _("guestfish: no libvirt domain called '%s': %s\n"),
- guest, err->message);
+ fprintf (stderr, _("%s: no libvirt domain called '%s': %s\n"),
+ program_name, guest, err->message);
goto cleanup;
}
if (!read_only) {
virDomainInfo info;
if (virDomainGetInfo (dom, &info) == -1) {
err = virConnGetLastError (conn);
- fprintf (stderr, _("guestfish: error getting domain info about '%s': %s\n"),
- guest, err->message);
+ fprintf (stderr, _("%s: error getting domain info about '%s': %s\n"),
+ program_name, guest, err->message);
goto cleanup;
}
if (info.state != VIR_DOMAIN_SHUTOFF) {
- fprintf (stderr, _("guestfish: error: '%s' is a live virtual machine.\nYou must use '--ro' because write access to a running virtual machine can\ncause disk corruption.\n"),
- guest);
+ fprintf (stderr, _("%s: error: '%s' is a live virtual machine.\nYou must use '--ro' because write access to a running virtual machine can\ncause disk corruption.\n"),
+ program_name, guest);
goto cleanup;
}
}
if (!xml) {
err = virConnGetLastError (conn);
- fprintf (stderr, _("guestfish: error reading libvirt XML information about '%s': %s\n"),
- guest, err->message);
+ fprintf (stderr, _("%s: error reading libvirt XML information about '%s': %s\n"),
+ program_name, guest, err->message);
goto cleanup;
}
*/
doc = xmlParseMemory (xml, strlen (xml));
if (doc == NULL) {
- fprintf (stderr, _("guestfish: unable to parse XML information returned by libvirt\n"));
+ fprintf (stderr, _("%s: unable to parse XML information returned by libvirt\n"),
+ program_name);
goto cleanup;
}
xpathCtx = xmlXPathNewContext (doc);
if (xpathCtx == NULL) {
- fprintf (stderr, _("guestfish: unable to create new XPath context\n"));
+ fprintf (stderr, _("%s: unable to create new XPath context\n"),
+ program_name);
goto cleanup;
}
/* This gives us a set of all the <disk> nodes. */
xpathObj = xmlXPathEvalExpression (BAD_CAST "//devices/disk", xpathCtx);
if (xpathObj == NULL) {
- fprintf (stderr, _("guestfish: unable to evaluate XPath expression\n"));
+ fprintf (stderr, _("%s: unable to evaluate XPath expression\n"),
+ program_name);
goto cleanup;
}
}
if (nr_added == 0) {
- fprintf (stderr, _("guestfish: libvirt domain '%s' has no disks\n"),
- guest);
+ fprintf (stderr, _("%s: libvirt domain '%s' has no disks\n"),
+ program_name, guest);
goto cleanup;
}
bin_PROGRAMS = guestmount
+# These source files (all related to option parsing) are shared
+# between guestfish and guestmount.
+SHARED_SOURCE_FILES = \
+ ../fish/inspect.c \
+ ../fish/options.h \
+ ../fish/options.c \
+ ../fish/virt.c
+
guestmount_SOURCES = \
+ $(SHARED_SOURCE_FILES) \
dircache.c \
dircache.h \
guestmount.c \
guestmount_CFLAGS = \
-I$(top_srcdir)/src -I$(top_builddir)/src \
+ -I$(top_srcdir)/fish \
-I$(srcdir)/../gnulib/lib -I../gnulib/lib \
-DGUESTFS_DEFAULT_PATH='"$(libdir)/guestfs"' \
-DLOCALEBASEDIR=\""$(datadir)/locale"\" \
- $(FUSE_CFLAGS) \
+ $(FUSE_CFLAGS) $(LIBVIRT_CFLAGS) $(LIBXML2_CFLAGS) \
$(WARN_CFLAGS) $(WERROR_CFLAGS)
guestmount_LDADD = \
$(FUSE_LIBS) -lulockmgr \
+ $(LIBVIRT_LIBS) $(LIBXML2_LIBS) \
$(top_builddir)/src/libguestfs.la \
../gnulib/lib/libgnu.la
/* guestmount - mount guests using libguestfs and FUSE
- * Copyright (C) 2009 Red Hat Inc.
+ * 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
#include "progname.h"
#include "guestmount.h"
+#include "options.h"
#include "dircache.h"
/* See <attr/xattr.h> */
#define ENOATTR ENODATA
#endif
-static guestfs_h *g = NULL;
-static int read_only = 0;
+guestfs_h *g = NULL;
+int read_only = 0;
int verbose = 0;
+int inspector = 0;
+const char *libvirt_uri;
int dir_cache_timeout = 60;
/* This is ugly: guestfs errors are strings, FUSE wants -errno. We
.removexattr = fg_removexattr,
};
-struct drv {
- struct drv *next;
- char *filename;
- const char *format;
-};
-
-struct mp {
- struct mp *next;
- char *device;
- char *mountpoint;
-};
-
-static void add_drives (struct drv *);
-static void mount_mps (struct mp *);
-
static void __attribute__((noreturn))
fuse_help (void)
{
fprintf (stdout,
_("%s: FUSE module for libguestfs\n"
"%s lets you mount a virtual machine filesystem\n"
- "Copyright (C) 2009 Red Hat Inc.\n"
+ "Copyright (C) 2009-2010 Red Hat Inc.\n"
"Usage:\n"
" %s [--options] [-- [--FUSE-options]] mountpoint\n"
"Options:\n"
" -a|--add image Add image\n"
+ " -c|--connect uri Specify libvirt URI for -d option\n"
" --dir-cache-timeout Set readdir cache timeout (default 5 sec)\n"
+ " -d|--domain guest Add disks from libvirt guest\n"
" --format[=raw|..] Force disk format for -a option\n"
" --fuse-help Display extra FUSE options\n"
+ " -i|--inspector Automatically mount filesystems\n"
" --help Display help message and exit\n"
" -m|--mount dev[:mnt] Mount dev on mnt (if omitted, /)\n"
" -n|--no-sync Don't autosync\n"
" -o|--option opt Pass extra option to FUSE\n"
" -r|--ro Mount read-only\n"
" --selinux Enable SELinux support\n"
- " --trace Trace guestfs API calls (to stderr)\n"
" -v|--verbose Verbose messages\n"
" -V|--version Display version and exit\n"
+ " -x|--trace Trace guestfs API calls\n"
),
program_name, program_name, program_name);
}
/* The command line arguments are broadly compatible with (a subset
* of) guestfish. Thus we have to deal mainly with -a, -m and --ro.
*/
- static const char *options = "a:m:no:rv?V";
+ static const char *options = "a:c:d:im:no:rv?Vx";
static const struct option long_options[] = {
{ "add", 1, 0, 'a' },
+ { "connect", 1, 0, 'c' },
{ "dir-cache-timeout", 1, 0, 0 },
+ { "domain", 1, 0, 'd' },
{ "format", 2, 0, 0 },
{ "fuse-help", 0, 0, 0 },
{ "help", 0, 0, HELP_OPTION },
+ { "inspector", 0, 0, 'i' },
{ "mount", 1, 0, 'm' },
{ "no-sync", 0, 0, 'n' },
{ "option", 1, 0, 'o' },
{ "ro", 0, 0, 'r' },
{ "selinux", 0, 0, 0 },
- { "trace", 0, 0, 0 },
+ { "trace", 0, 0, 'x' },
{ "verbose", 0, 0, 'v' },
{ "version", 0, 0, 'V' },
{ 0, 0, 0, 0 }
fuse_help ();
else if (STREQ (long_options[option_index].name, "selinux"))
guestfs_set_selinux (g, 1);
- else if (STREQ (long_options[option_index].name, "trace")) {
- ADD_FUSE_ARG ("-f");
- guestfs_set_trace (g, 1);
- guestfs_set_recovery_proc (g, 1);
- }
else if (STREQ (long_options[option_index].name, "format")) {
if (!optarg || STREQ (optarg, ""))
format = NULL;
break;
case 'a':
- if (access (optarg, R_OK) != 0) {
- perror (optarg);
- exit (EXIT_FAILURE);
- }
- drv = malloc (sizeof (struct drv));
- if (!drv) {
- perror ("malloc");
- exit (EXIT_FAILURE);
- }
- drv->filename = optarg;
- drv->format = format;
- drv->next = drvs;
- drvs = drv;
+ OPTION_a;
+ break;
+
+ case 'c':
+ OPTION_c;
+ break;
+
+ case 'd':
+ OPTION_d;
+ break;
+
+ case 'i':
+ OPTION_i;
break;
case 'm':
- mp = malloc (sizeof (struct mp));
- if (!mp) {
- perror ("malloc");
- exit (EXIT_FAILURE);
- }
- p = strchr (optarg, ':');
- if (p) {
- *p = '\0';
- mp->mountpoint = p+1;
- } else
- mp->mountpoint = bad_cast ("/");
- mp->device = optarg;
- mp->next = mps;
- mps = mp;
+ OPTION_m;
break;
case 'n':
- guestfs_set_autosync (g, 0);
+ OPTION_n;
break;
case 'o':
break;
case 'r':
- read_only = 1;
+ OPTION_r;
break;
case 'v':
- verbose++;
- guestfs_set_verbose (g, verbose);
+ OPTION_v;
break;
- case 'V': {
- struct guestfs_version *v = guestfs_version (g);
- printf ("%s %"PRIi64".%"PRIi64".%"PRIi64"%s\n", program_name,
- v->major, v->minor, v->release, v->extra);
- exit (EXIT_SUCCESS);
- }
+ case 'V':
+ OPTION_V;
+ break;
+
+ case 'x':
+ OPTION_x;
+ ADD_FUSE_ARG ("-f");
+ guestfs_set_recovery_proc (g, 1);
+ break;
case HELP_OPTION:
usage (EXIT_SUCCESS);
}
}
- /* We must have at least one -a and at least one -m. */
- if (!drvs || !mps) {
+ /* Check we have the right options. */
+ if (!drvs || !(mps || inspector)) {
fprintf (stderr,
- _("%s: must have at least one -a and at least one -m option\n"),
+ _("%s: must have at least one -a/-d and at least one -m/-i option\n"),
program_name);
exit (EXIT_FAILURE);
}
}
/* Do the guest drives and mountpoints. */
- add_drives (drvs);
+ add_drives (drvs, 'a');
if (guestfs_launch (g) == -1)
exit (EXIT_FAILURE);
+ if (inspector)
+ inspect_mount ();
mount_mps (mps);
+ free_drives (drvs);
+ free_mps (mps);
+
/* FUSE example does this, not clear if it's necessary, but ... */
if (guestfs_umask (g, 0) == -1)
exit (EXIT_FAILURE);
exit (r == -1 ? 1 : 0);
}
-
-/* List is built in reverse order, so add them in reverse order. */
-static void
-add_drives (struct drv *drv)
-{
- int r;
- struct guestfs_add_drive_opts_argv ad_optargs;
-
- if (drv) {
- add_drives (drv->next);
-
- ad_optargs.bitmask = 0;
- if (read_only) {
- ad_optargs.bitmask |= GUESTFS_ADD_DRIVE_OPTS_READONLY_BITMASK;
- ad_optargs.readonly = 1;
- }
- if (drv->format) {
- ad_optargs.bitmask |= GUESTFS_ADD_DRIVE_OPTS_FORMAT_BITMASK;
- ad_optargs.format = drv->format;
- }
- r = guestfs_add_drive_opts_argv (g, drv->filename, &ad_optargs);
- if (r == -1)
- exit (EXIT_FAILURE);
- }
-}
-
-/* List is built in reverse order, so mount them in reverse order. */
-static void
-mount_mps (struct mp *mp)
-{
- int r;
-
- if (mp) {
- mount_mps (mp->next);
-
- /* Don't use guestfs_mount here because that will default to mount
- * options -o sync,noatime. For more information, see guestfs(3)
- * section "LIBGUESTFS GOTCHAS".
- */
- const char *options = read_only ? "ro" : "";
- r = guestfs_mount_options (g, options, mp->device, mp->mountpoint);
- if (r == -1)
- exit (EXIT_FAILURE);
- }
-}
guestmount [--options] -a disk.img -m device [--ro] mountpoint
+ guestmount [--options] -a disk.img -i [--ro] mountpoint
+
+ guestmount [--options] -d Guest -i [--ro] mountpoint
+
=head1 WARNING
You must I<not> use C<guestmount> in read-write mode on live virtual
userspace") to make it appear as a mountable device.
Along with other options, you have to give at least one device (I<-a>
-option) and at least one mountpoint (I<-m> option). How this works is
-better explained in the L<guestfish(1)> manual page, or you can use
-L<virt-inspector(1)> and/or the wrapper script
-C<guestmount-wrapper> to help you.
+option) or libvirt domain (I<-d> option), and at least one mountpoint
+(I<-m> option) or use the I<-i> inspection option. How this works is
+better explained in the L<guestfish(1)> manual page, or by looking at
+the examples below.
FUSE lets you mount filesystems as non-root. The mountpoint must be
owned by you, and the filesystem will not be visible to any other
guestmount -a linux.img -m /dev/VG/LV -m /dev/sda1:/boot --ro /mnt
-To get L<virt-inspector(1)> to do the hard work of detecting guest
-mountpoints for you:
+To get libguestfs to detect guest mountpoints for you:
+
+ guestmount -a guest.img -i --ro /mnt
- guestmount $(virt-inspector --ro-fish MyGuest) /mnt
+For a libvirt guest called "Guest" you could do:
-(or use --fish if you don't want it to be a read only mount). The
-option is called I<--ro-fish> or I<--fish> because these parameters
-are compatible with L<guestfish(1)>.
+ guestmount -d Guest -i --ro /mnt
If you don't know what filesystems are contained in a guest or
disk image, use L<virt-list-filesystems(1)> first:
virt-list-filesystems MyGuest
If you want to trace the libguestfs calls but without excessive
-debugging, we recommend:
+debugging information, we recommend:
- guestmount [-a ... -m ...] --trace /mnt
+ guestmount [...] --trace /mnt
If you want to debug the program, we recommend:
- guestmount [-a ... -m ...] --trace --verbose /mnt
+ guestmount [...] --trace --verbose /mnt
=head1 OPTIONS
The format of the disk image is auto-detected. To override this and
force a particular format use the I<--format=..> option.
+=item B<-c URI> | B<--connect URI>
+
+When used in conjunction with the I<-d> option, this specifies
+the libvirt URI to use. The default is to use the default libvirt
+connection.
+
+=item B<-d libvirt-domain> | B<--domain libvirt-domain>
+
+Add disks from the named libvirt domain. If the I<--ro> option is
+also used, then any libvirt domain can be used. However in write
+mode, only libvirt domains which are shut down can be named here.
+
=item B<--dir-cache-timeout N>
Set the readdir cache timeout to I<N> seconds, the default being 60
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>.
+L<guestfs(3)/guestfs_add_drive_opts>.
=item B<--fuse-help>
Display brief help and exit.
+=item B<-i> | B<--inspector>
+
+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.
+
=item B<-m dev[:mnt]> | B<--mount dev[:mnt]>
Mount the named partition or logical volume on the given mountpoint
Enable SELinux support for the guest.
-=item B<--trace>
-
-Trace libguestfs calls (to stderr).
-
-This also stops the daemon from forking into the background.
-
=item B<-v> | B<--verbose>
Enable verbose messages from underlying libguestfs.
Display the program version and exit.
+=item B<-x> | B<--trace>
+
+Trace libguestfs calls.
+
+This also stops the daemon from forking into the background.
+
=back
=head1 SEE ALSO
=head1 COPYRIGHT
-Copyright (C) 2009 Red Hat Inc.
+Copyright (C) 2009-2010 Red Hat Inc.
L<http://libguestfs.org/>
This program is free software; you can redistribute it and/or modify
fish/lcd.c
fish/man.c
fish/more.c
+fish/options.c
fish/prep.c
fish/prep_boot.c
fish/prep_disk.c