X-Git-Url: http://git.annexia.org/?p=libguestfs.git;a=blobdiff_plain;f=fish%2Ffish.c;h=bc7d96c88967d14974ab6b40920b6ea46dc21549;hp=470c625105d7b6281f8df4017c3d0082cb7b22de;hb=1a9aa565b38eafe48621bc2fe42d35ea6a907708;hpb=f2b7a8e15c49ebc70c7ea56aefb340362aae5a99 diff --git a/fish/fish.c b/fish/fish.c index 470c625..bc7d96c 100644 --- a/fish/fish.c +++ b/fish/fish.c @@ -29,6 +29,7 @@ #include #include #include +#include #ifdef HAVE_LIBREADLINE #include @@ -42,11 +43,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,7 +68,7 @@ struct mp { char *mountpoint; }; -static void add_drives (struct drv *drv); +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 int launch (void); @@ -80,6 +93,8 @@ int remote_control_listen = 0; int remote_control = 0; int exit_on_error = 1; int command_num = 0; +int keys_from_stdin = 0; +const char *libvirt_uri = NULL; static void __attribute__((noreturn)) usage (int status) @@ -107,9 +122,12 @@ 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" " -f|--file file Read commands from file\n" " -i|--inspector Run virt-inspector to get disk mountpoints\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" @@ -142,13 +160,16 @@ main (int argc, char *argv[]) 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' }, { "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' }, @@ -170,7 +191,6 @@ main (int argc, char *argv[]) int inspector = 0; int option_index; struct sigaction sa; - char next_drive = 'a'; int next_prepared_drive = 1; initialize_readline (); @@ -239,6 +259,8 @@ 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 { fprintf (stderr, _("%s: unknown long option: %s (%d)\n"), program_name, long_options[option_index].name, option_index); @@ -256,15 +278,26 @@ 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': @@ -277,16 +310,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; @@ -470,12 +501,23 @@ main (int argc, char *argv[]) } /* 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 (next_prepared_drive > 1 || mps != NULL) { + /* 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); prepare_drives (drvs); mount_mps (mps); @@ -567,21 +609,60 @@ mount_mps (struct mp *mp) } } -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 @@ -589,8 +670,8 @@ 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); } } @@ -1699,6 +1780,67 @@ file_out (const char *arg) return ret; } +/* 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) +{ + 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; + } + + /* 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 (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; +} + static void print_shell_quote (FILE *stream, const char *str) {