X-Git-Url: http://git.annexia.org/?p=libguestfs.git;a=blobdiff_plain;f=fish%2Ffish.c;h=de11b2fef0ec2f9c6d3403566b1a48ea0ba4cb27;hp=06ce960d810bcf147f20b087b96a1ea878187c03;hb=8ea62c8d7f3f7f7e4057b93105cf979271aa13f4;hpb=5af010c96d687a7cf5251107a74770a231f42b77 diff --git a/fish/fish.c b/fish/fish.c index 06ce960..de11b2f 100644 --- a/fish/fish.c +++ b/fish/fish.c @@ -20,15 +20,17 @@ #include #include +#include #include #include #include #include #include -#include #include #include #include +#include +#include #ifdef HAVE_LIBREADLINE #include @@ -42,11 +44,23 @@ #include "closeout.h" #include "progname.h" +/* List of drives added via -a, -d or -N options. */ struct drv { struct drv *next; - char *filename; /* disk filename (for -a or -N options) */ - prep_data *data; /* prepared type (for -N option only) */ - char *device; /* device inside the appliance */ + enum { drv_a, drv_d, drv_N } type; + union { + struct { + char *filename; /* disk filename */ + } 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 { @@ -55,9 +69,12 @@ struct mp { char *mountpoint; }; -static void add_drives (struct drv *drv); +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); @@ -68,7 +85,8 @@ 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); + +static int override_progress_bars = -1; /* Currently open libguestfs handle. */ guestfs_h *g; @@ -80,6 +98,13 @@ int remote_control_listen = 0; int remote_control = 0; int exit_on_error = 1; int command_num = 0; +int keys_from_stdin = 0; +int echo_keys = 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) @@ -91,11 +116,11 @@ usage (int status) fprintf (stdout, _("%s: guest filesystem shell\n" "%s lets you edit virtual machine filesystems\n" - "Copyright (C) 2009 Red Hat Inc.\n" + "Copyright (C) 2009-2010 Red Hat Inc.\n" "Usage:\n" " %s [--options] cmd [: cmd : cmd ...]\n" - " %s -i libvirt-domain\n" - " %s -i disk-image(s)\n" + " %s [--ro] -i -a disk-image\n" + " %s [--ro] -i -d libvirt-domain\n" "or for interactive use:\n" " %s\n" "or from a shell script:\n" @@ -107,20 +132,26 @@ usage (int status) " -h|--cmd-help List available commands\n" " -h|--cmd-help cmd Display detailed help on 'cmd'\n" " -a|--add image Add image\n" + " -c|--connect uri Specify libvirt URI for -d option\n" + " -d|--domain guest Add disks from libvirt guest\n" " -D|--no-dest-paths Don't tab-complete paths from guest fs\n" + " --echo-keys Don't turn off echo for passphrases\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" " -v|--verbose Verbose messages\n" " -x Echo each command before executing it\n" " -V|--version Display version and exit\n" - "For more information, see the manpage %s(1).\n"), + "For more information, see the manpage %s(1).\n"), program_name, program_name, program_name, program_name, program_name, program_name, program_name, program_name, program_name); @@ -140,20 +171,28 @@ main (int argc, char *argv[]) bindtextdomain (PACKAGE, LOCALEBASEDIR); textdomain (PACKAGE); + set_up_terminal (); + enum { HELP_OPTION = CHAR_MAX + 1 }; - static const char *options = "a:Df:h::im:nN:rv?Vx"; + static const char *options = "a:c:d:Df:h::im:nN:rv?Vx"; static const struct option long_options[] = { { "add", 1, 0, 'a' }, { "cmd-help", 2, 0, 'h' }, + { "connect", 1, 0, 'c' }, + { "domain", 1, 0, 'd' }, + { "echo-keys", 0, 0, 0 }, { "file", 1, 0, 'f' }, { "help", 0, 0, HELP_OPTION }, { "inspector", 0, 0, 'i' }, + { "keys-from-stdin", 0, 0, 0 }, { "listen", 0, 0, 0 }, { "mount", 1, 0, 'm' }, { "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 }, @@ -167,10 +206,8 @@ main (int argc, char *argv[]) struct mp *mp; char *p, *file = NULL; int c; - int inspector = 0; int option_index; struct sigaction sa; - char next_drive = 'a'; int next_prepared_drive = 1; initialize_readline (); @@ -208,7 +245,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); @@ -239,6 +276,14 @@ main (int argc, char *argv[]) } } else if (STREQ (long_options[option_index].name, "selinux")) { 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 if (STREQ (long_options[option_index].name, "echo-keys")) { + echo_keys = 1; } else { fprintf (stderr, _("%s: unknown long option: %s (%d)\n"), program_name, long_options[option_index].name, option_index); @@ -256,19 +301,33 @@ main (int argc, char *argv[]) perror ("malloc"); exit (EXIT_FAILURE); } - drv->filename = optarg; - drv->data = NULL; - /* We could fill the device field in, but in fact we - * only use it for the -N option at present. - */ - drv->device = NULL; + drv->type = drv_a; + drv->a.filename = optarg; + drv->next = drvs; + drvs = drv; + break; + + case 'c': + libvirt_uri = optarg; + 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; - next_drive++; 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); } @@ -277,16 +336,14 @@ main (int argc, char *argv[]) perror ("malloc"); exit (EXIT_FAILURE); } - if (asprintf (&drv->filename, "test%d.img", + drv->type = drv_N; + if (asprintf (&drv->N.filename, "test%d.img", next_prepared_drive++) == -1) { perror ("asprintf"); exit (EXIT_FAILURE); } - drv->data = create_prepared_file (optarg, drv->filename); - if (asprintf (&drv->device, "/dev/sd%c", next_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; break; @@ -304,14 +361,18 @@ main (int argc, char *argv[]) file = optarg; break; - case 'h': + case 'h': { + int r = 0; + if (optarg) - display_command (optarg); + r = display_command (optarg); else if (argv[optind] && argv[optind][0] != '-') - display_command (argv[optind++]); + r = display_command (argv[optind++]); else list_commands (); - exit (EXIT_SUCCESS); + + exit (r == 0 ? EXIT_SUCCESS : EXIT_FAILURE); + } case 'i': inspector = 1; @@ -347,9 +408,12 @@ main (int argc, char *argv[]) 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); @@ -363,120 +427,70 @@ 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]); - 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); + 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 + * will think the main program has died and kill qemu. Therefore + * don't use the recovery process for this case. (A better + * solution would be to call launch () etc after the fork, but + * that greatly complicates the code here). + */ + if (remote_control_listen) + guestfs_set_recovery_proc (g, 0); + if (launch () == -1) exit (EXIT_FAILURE); + + if (inspector) + inspect_mount (); + prepare_drives (drvs); mount_mps (mps); } + /* Free up data structures, no longer needed after this point. */ + free_drives (drvs); + free_mps (mps); + /* Remote control? */ if (remote_control_listen && remote_control) { fprintf (stderr, @@ -510,6 +524,15 @@ main (int argc, char *argv[]) } } + /* 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)) @@ -525,6 +548,35 @@ main (int argc, char *argv[]) exit (EXIT_SUCCESS); } +/* The 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) { @@ -558,26 +610,78 @@ mount_mps (struct mp *mp) */ const char *options = read_only ? "ro" : ""; r = guestfs_mount_options (g, options, mp->device, mp->mountpoint); - if (r == -1) + 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 void -add_drives (struct drv *drv) +static char +add_drives (struct drv *drv, char next_drive) { int r; + if (next_drive > 'z') { + fprintf (stderr, + _("guestfish: too many drives added on the command line\n")); + exit (EXIT_FAILURE); + } + if (drv) { - add_drives (drv->next); + next_drive = add_drives (drv->next, next_drive); - if (drv->data /* -N option is not affected by --ro */ || !read_only) - r = guestfs_add_drive (g, drv->filename); - else - r = guestfs_add_drive_ro (g, drv->filename); - if (r == -1) - exit (EXIT_FAILURE); + switch (drv->type) { + case drv_a: + if (!read_only) + r = guestfs_add_drive (g, drv->a.filename); + else + r = guestfs_add_drive_ro (g, drv->a.filename); + 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: + /* -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 @@ -585,9 +689,41 @@ prepare_drives (struct drv *drv) { if (drv) { prepare_drives (drv->next); - if (drv->data) - prepare_drive (drv->filename, drv->data, drv->device); + if (drv->type == drv_N) + prepare_drive (drv->N.filename, drv->N.data, drv->N.device); + } +} + +static void +free_drives (struct drv *drv) +{ + if (!drv) return; + free_drives (drv->next); + + switch (drv->type) { + case drv_a: free (drv->a.filename); break; + case drv_d: free (drv->d.guest); 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 @@ -662,7 +798,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" @@ -672,6 +808,12 @@ script (int prompt) " 'quit' to quit the shell\n" "\n")); + if (inspector) { + print_inspect_prompt (); + printf ("\n"); + } + } + while (!quit) { char *pipe = NULL; @@ -899,6 +1041,8 @@ issue_command (const char *cmd, char *argv[], const char *pipecmd) int pid = 0; int i, r; + reset_progress_bar (); + /* This counts the commands issued, starting at 1. */ command_num++; @@ -957,11 +1101,11 @@ issue_command (const char *cmd, char *argv[], const char *pipecmd) /* Otherwise execute it locally. */ else if (STRCASEEQ (cmd, "help")) { - if (argc == 0) + if (argc == 0) { list_commands (); - else - display_command (argv[0]); - r = 0; + r = 0; + } else + r = display_command (argv[0]); } else if (STRCASEEQ (cmd, "quit") || STRCASEEQ (cmd, "exit") || @@ -969,31 +1113,6 @@ issue_command (const char *cmd, char *argv[], const char *pipecmd) quit = 1; r = 0; } - else if (STRCASEEQ (cmd, "alloc") || - STRCASEEQ (cmd, "allocate")) - r = do_alloc (cmd, argc, argv); - else if (STRCASEEQ (cmd, "echo")) - r = do_echo (cmd, argc, argv); - else if (STRCASEEQ (cmd, "edit") || - STRCASEEQ (cmd, "vi") || - STRCASEEQ (cmd, "emacs")) - r = do_edit (cmd, argc, argv); - else if (STRCASEEQ (cmd, "lcd")) - r = do_lcd (cmd, argc, argv); - else if (STRCASEEQ (cmd, "glob")) - r = do_glob (cmd, argc, argv); - else if (STRCASEEQ (cmd, "man") || - STRCASEEQ (cmd, "manual")) - r = do_man (cmd, argc, argv); - else if (STRCASEEQ (cmd, "more") || - STRCASEEQ (cmd, "less")) - r = do_more (cmd, argc, argv); - else if (STRCASEEQ (cmd, "reopen")) - r = do_reopen (cmd, argc, argv); - else if (STRCASEEQ (cmd, "sparse")) - r = do_sparse (cmd, argc, argv); - else if (STRCASEEQ (cmd, "time")) - r = do_time (cmd, argc, argv); else r = run_action (cmd, argc, argv); @@ -1024,169 +1143,38 @@ issue_command (const char *cmd, char *argv[], const char *pipecmd) void list_builtin_commands (void) { - /* help, man and quit should appear at the top */ + /* help and quit should appear at the top */ printf ("%-20s %s\n", "help", _("display a list of commands or help on a command")); printf ("%-20s %s\n", - "man", _("read the manual")); - printf ("%-20s %s\n", "quit", _("quit guestfish")); - printf ("%-20s %s\n", - "alloc", _("allocate an image")); - printf ("%-20s %s\n", - "echo", _("display a line of text")); - printf ("%-20s %s\n", - "edit", _("edit a file in the image")); - printf ("%-20s %s\n", - "lcd", _("local change directory")); - printf ("%-20s %s\n", - "glob", _("expand wildcards in command")); - printf ("%-20s %s\n", - "more", _("view a file in the pager")); - printf ("%-20s %s\n", - "reopen", _("close and reopen libguestfs handle")); - printf ("%-20s %s\n", - "sparse", _("allocate a sparse image file")); - printf ("%-20s %s\n", - "time", _("measure time taken to run command")); - /* actions are printed after this (see list_commands) */ } -void +int display_builtin_command (const char *cmd) { /* help for actions is auto-generated, see display_command */ - if (STRCASEEQ (cmd, "alloc") || - STRCASEEQ (cmd, "allocate")) - printf (_("alloc - allocate an image\n" - " alloc \n" - "\n" - " This creates an empty (zeroed) file of the given size,\n" - " and then adds so it can be further examined.\n" - "\n" - " For more advanced image creation, see qemu-img utility.\n" - "\n" - " Size can be specified (where means a number):\n" - " number of kilobytes\n" - " eg: 1440 standard 3.5\" floppy\n" - " K or KB number of kilobytes\n" - " M or MB number of megabytes\n" - " G or GB number of gigabytes\n" - " T or TB number of terabytes\n" - " P or PB number of petabytes\n" - " E or EB number of exabytes\n" - " sects number of 512 byte sectors\n")); - else if (STRCASEEQ (cmd, "echo")) - printf (_("echo - display a line of text\n" - " echo [ ...]\n" - "\n" - " This echos the parameters to the terminal.\n")); - else if (STRCASEEQ (cmd, "edit") || - STRCASEEQ (cmd, "vi") || - STRCASEEQ (cmd, "emacs")) - printf (_("edit - edit a file in the image\n" - " edit \n" - "\n" - " This is used to edit a file.\n" - "\n" - " It is the equivalent of (and is implemented by)\n" - " running \"cat\", editing locally, and then \"write-file\".\n" - "\n" - " Normally it uses $EDITOR, but if you use the aliases\n" - " \"vi\" or \"emacs\" you will get those editors.\n" - "\n" - " NOTE: This will not work reliably for large files\n" - " (> 2 MB) or binary files containing \\0 bytes.\n")); - else if (STRCASEEQ (cmd, "lcd")) - printf (_("lcd - local change directory\n" - " lcd \n" - "\n" - " Change guestfish's current directory. This command is\n" - " useful if you want to download files to a particular\n" - " place.\n")); - else if (STRCASEEQ (cmd, "glob")) - printf (_("glob - expand wildcards in command\n" - " glob [ ...]\n" - "\n" - " Glob runs with wildcards expanded in any\n" - " command args. Note that the command is run repeatedly\n" - " once for each expanded argument.\n")); - else if (STRCASEEQ (cmd, "man") || - STRCASEEQ (cmd, "manual")) - printf (_("man - read the manual\n" - " man\n" - "\n" - " Opens the manual page for guestfish.\n")); - else if (STRCASEEQ (cmd, "help")) + if (STRCASEEQ (cmd, "help")) { printf (_("help - display a list of commands or help on a command\n" " help cmd\n" " help\n")); - else if (STRCASEEQ (cmd, "more") || - STRCASEEQ (cmd, "less")) - printf (_("more - view a file in the pager\n" - " more \n" - "\n" - " This is used to view a file in the pager.\n" - "\n" - " It is the equivalent of (and is implemented by)\n" - " running \"cat\" and using the pager.\n" - "\n" - " Normally it uses $PAGER, but if you use the alias\n" - " \"less\" then it always uses \"less\".\n" - "\n" - " NOTE: This will not work reliably for large files\n" - " (> 2 MB) or binary files containing \\0 bytes.\n")); + return 0; + } else if (STRCASEEQ (cmd, "quit") || STRCASEEQ (cmd, "exit") || - STRCASEEQ (cmd, "q")) + STRCASEEQ (cmd, "q")) { printf (_("quit - quit guestfish\n" " quit\n")); - else if (STRCASEEQ (cmd, "reopen")) - printf (_("reopen - close and reopen the libguestfs handle\n" - " reopen\n" - "\n" - "Close and reopen the libguestfs handle. It is not necessary to use\n" - "this normally, because the handle is closed properly when guestfish\n" - "exits. However this is occasionally useful for testing.\n")); - else if (STRCASEEQ (cmd, "sparse")) - printf (_("sparse - allocate a sparse image file\n" - " sparse \n" - "\n" - " This creates an empty sparse file of the given size,\n" - " and then adds so it can be further examined.\n" - "\n" - " In all respects it works the same as the 'alloc'\n" - " command, except that the image file is allocated\n" - " sparsely, which means that disk blocks are not assigned\n" - " to the file until they are needed. Sparse disk files\n" - " only use space when written to, but they are slower\n" - " and there is a danger you could run out of real disk\n" - " space during a write operation.\n" - "\n" - " For more advanced image creation, see qemu-img utility.\n" - "\n" - " Size can be specified (where means a number):\n" - " number of kilobytes\n" - " eg: 1440 standard 3.5\" floppy\n" - " K or KB number of kilobytes\n" - " M or MB number of megabytes\n" - " G or GB number of gigabytes\n" - " T or TB number of terabytes\n" - " P or PB number of petabytes\n" - " E or EB number of exabytes\n" - " sects number of 512 byte sectors\n")); - else if (STRCASEEQ (cmd, "time")) - printf (_("time - measure time taken to run command\n" - " time [ ...]\n" - "\n" - " This runs as usual, and prints the elapsed\n" - " time afterwards.\n")); - else + return 0; + } + else { fprintf (stderr, _("%s: command not known, use -h to list all commands\n"), cmd); + return -1; + } } /* This is printed when the user types in an unknown command for the @@ -1419,6 +1407,13 @@ initialize_readline (void) rl_readline_name = "guestfish"; rl_attempted_completion_function = do_completion; + + /* Note that .inputrc (or /etc/inputrc) is not read until the first + * call the readline(), which happens later. Therefore, these + * provide default values which can be overridden by the user if + * they wish. + */ + (void) rl_variable_bind ("completion-ignore-case", "on"); #endif } @@ -1662,16 +1657,65 @@ file_out (const char *arg) return ret; } -static void -print_shell_quote (FILE *stream, const char *str) +/* Read a passphrase ('Key') from /dev/tty with echo off. + * The caller (cmds.c) will call free on the string afterwards. + * Based on the code in cryptsetup file lib/utils.c. + */ +char * +read_key (const char *param) { -#define SAFE(c) (c_isalnum((c)) || \ - (c) == '/' || (c) == '-' || (c) == '_' || (c) == '.') - int i; + FILE *infp, *outfp; + struct termios orig, temp; + char *ret = NULL; + + /* Read and write to /dev/tty if available. */ + if (keys_from_stdin || + (infp = outfp = fopen ("/dev/tty", "w+")) == NULL) { + infp = stdin; + outfp = stdout; + } - for (i = 0; str[i]; ++i) { - if (!SAFE(str[i])) - putc ('\\', stream); - putc (str[i], stream); + /* Print the prompt and set no echo. */ + int tty = isatty (fileno (infp)); + int tcset = 0; + if (tty) { + fprintf (outfp, _("Enter key or passphrase (\"%s\"): "), param); + + if (!echo_keys) { + if (tcgetattr (fileno (infp), &orig) == -1) { + perror ("tcgetattr"); + goto error; + } + memcpy (&temp, &orig, sizeof temp); + temp.c_lflag &= ~ECHO; + + tcsetattr (fileno (infp), TCSAFLUSH, &temp); + tcset = 1; + } + } + + size_t n = 0; + ssize_t len; + len = getline (&ret, &n, infp); + if (len == -1) { + perror ("getline"); + ret = NULL; + goto error; } + + /* Remove the terminating \n if there is one. */ + if (len > 0 && ret[len-1] == '\n') + ret[len-1] = '\0'; + + error: + /* Restore echo, close file descriptor. */ + if (tty && tcset) { + printf ("\n"); + tcsetattr (fileno (infp), TCSAFLUSH, &orig); + } + + if (infp != stdin) + fclose (infp); /* outfp == infp, so this is closed also */ + + return ret; }