#include <stdio.h>
#include <stdlib.h>
+#include <inttypes.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <locale.h>
+#include <langinfo.h>
#include <termios.h>
#ifdef HAVE_LIBREADLINE
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);
#ifdef HAVE_LIBREADLINE
static void add_history_line (const char *);
#endif
-static void print_shell_quote (FILE *stream, const char *str);
+
+static int override_progress_bars = -1;
/* Currently open libguestfs handle. */
guestfs_h *g;
int command_num = 0;
int keys_from_stdin = 0;
const char *libvirt_uri = NULL;
+int inspector = 0;
+int utf8_mode = 0;
+int have_terminfo = 0;
+int progress_bars = 0;
static void __attribute__((noreturn))
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"
" -n|--no-sync Don't autosync\n"
" -N|--new type Create prepared disk (test1.img, ...)\n"
+ " --progress-bars Enable progress bars even when not interactive\n"
+ " --no-progress-bars Disable progress bars\n"
" --remote[=pid] Send commands to remote %s\n"
" -r|--ro Mount read-only\n"
" --selinux Enable SELinux support\n"
bindtextdomain (PACKAGE, LOCALEBASEDIR);
textdomain (PACKAGE);
+ set_up_terminal ();
+
enum { HELP_OPTION = CHAR_MAX + 1 };
static const char *options = "a:c:d:Df:h::im:nN:rv?Vx";
{ "new", 1, 0, 'N' },
{ "no-dest-paths", 0, 0, 'D' },
{ "no-sync", 0, 0, 'n' },
+ { "progress-bars", 0, 0, 0 },
+ { "no-progress-bars", 0, 0, 0 },
{ "remote", 2, 0, 0 },
{ "ro", 0, 0, 'r' },
{ "selinux", 0, 0, 0 },
struct mp *mp;
char *p, *file = NULL;
int c;
- int inspector = 0;
int option_index;
struct sigaction sa;
int next_prepared_drive = 1;
* 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);
guestfs_set_selinux (g, 1);
} else if (STREQ (long_options[option_index].name, "keys-from-stdin")) {
keys_from_stdin = 1;
+ } else if (STREQ (long_options[option_index].name, "progress-bars")) {
+ override_progress_bars = 1;
+ } else if (STREQ (long_options[option_index].name, "no-progress-bars")) {
+ override_progress_bars = 0;
} else {
fprintf (stderr, _("%s: unknown long option: %s (%d)\n"),
program_name, long_options[option_index].name, option_index);
break;
case 'N':
- if (STRCASEEQ (optarg, "list")) {
+ if (STRCASEEQ (optarg, "list") ||
+ STRCASEEQ (optarg, "help") ||
+ STRCASEEQ (optarg, "h") ||
+ STRCASEEQ (optarg, "?")) {
list_prepared_drives ();
exit (EXIT_SUCCESS);
}
guestfs_set_verbose (g, verbose);
break;
- case 'V':
- printf ("%s %s\n", program_name, PACKAGE_VERSION);
+ 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 'x':
guestfs_set_trace (g, 1);
}
}
- /* 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]);
- 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);
+ 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;
+ }
- int r = system (cmd2);
- if (r == -1) {
- perror (cmd2);
- exit (EXIT_FAILURE);
+ optind++;
}
-
- 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
guestfs_set_recovery_proc (g, 0);
if (launch () == -1) exit (EXIT_FAILURE);
+
+ if (inspector)
+ inspect_mount ();
+
prepare_drives (drvs);
mount_mps (mps);
}
}
}
+ /* Decide if we display progress bars. */
+ progress_bars =
+ override_progress_bars >= 0
+ ? override_progress_bars
+ : (optind >= argc && isatty (0));
+
+ if (progress_bars)
+ guestfs_set_progress_callback (g, progress_callback, NULL);
+
/* Interactive, shell script, or command(s) on the command line? */
if (optind >= argc) {
if (isatty (0))
exit (EXIT_SUCCESS);
}
+/* The <term.h> header file which defines this has "issues". */
+extern int tgetent (char *, const char *);
+
+static void
+set_up_terminal (void)
+{
+ /* http://www.cl.cam.ac.uk/~mgk25/unicode.html#activate */
+ utf8_mode = STREQ (nl_langinfo (CODESET), "UTF-8");
+
+ char *term = getenv ("TERM");
+ if (term == NULL) {
+ //fprintf (stderr, _("guestfish: TERM (terminal type) not defined.\n"));
+ return;
+ }
+
+ int r = tgetent (NULL, term);
+ if (r == -1) {
+ fprintf (stderr, _("guestfish: could not access termcap or terminfo database.\n"));
+ return;
+ }
+ if (r == 0) {
+ fprintf (stderr, _("guestfish: terminal type \"%s\" not defined.\n"),
+ term);
+ return;
+ }
+
+ have_terminfo = 1;
+}
+
void
pod2text (const char *name, const char *shortdesc, const char *str)
{
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"
" 'quit' to quit the shell\n"
"\n"));
+ if (inspector) {
+ print_inspect_prompt ();
+ printf ("\n");
+ }
+ }
+
while (!quit) {
char *pipe = NULL;
int pid = 0;
int i, r;
+ reset_progress_bar ();
+
/* This counts the commands issued, starting at 1. */
command_num++;
else if (STRCASEEQ (cmd, "alloc") ||
STRCASEEQ (cmd, "allocate"))
r = do_alloc (cmd, argc, argv);
+ else if (STRCASEEQ (cmd, "copy-in") ||
+ STRCASEEQ (cmd, "copy_in"))
+ r = do_copy_in (cmd, argc, argv);
+ else if (STRCASEEQ (cmd, "copy-out") ||
+ STRCASEEQ (cmd, "copy_out"))
+ r = do_copy_out (cmd, argc, argv);
else if (STRCASEEQ (cmd, "echo"))
r = do_echo (cmd, argc, argv);
else if (STRCASEEQ (cmd, "edit") ||
printf ("%-20s %s\n",
"alloc", _("allocate an image"));
printf ("%-20s %s\n",
+ "copy-in", _("copy files into an image"));
+ printf ("%-20s %s\n",
+ "copy-out", _("copy files out of an image"));
+ printf ("%-20s %s\n",
"echo", _("display a line of text"));
printf ("%-20s %s\n",
"edit", _("edit a file in the image"));
));
return 0;
}
+ else if (STRCASEEQ (cmd, "copy-in") ||
+ STRCASEEQ (cmd, "copy_in")) {
+ printf (_("copy-in - copy files into an image\n"
+ " copy-in <local> [<local> ...] <remotedir>\n"
+ "\n"
+ " Copy local files or directories recursively into the\n"
+ " image, placing them on a remote directory.\n"
+ ));
+ return 0;
+ }
+ else if (STRCASEEQ (cmd, "copy-out") ||
+ STRCASEEQ (cmd, "copy_out")) {
+ printf (_("copy-out - copy files out of an image\n"
+ " copy-out <remote> [<remote> ...] <localdir>\n"
+ "\n"
+ " Copy remote files or directories recursively out of the\n"
+ " image, placing them in a local directory.\n"
+ ));
+ return 0;
+ }
else if (STRCASEEQ (cmd, "echo")) {
printf (_("echo - display a line of text\n"
" echo [<params> ...]\n"
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);
- }
-}