#include <sys/types.h>
#include <sys/wait.h>
#include <locale.h>
+#include <termios.h>
#ifdef HAVE_LIBREADLINE
#include <readline/readline.h>
#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 {
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);
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)
" -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"
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' },
int inspector = 0;
int option_index;
struct sigaction sa;
- char next_drive = 'a';
int next_prepared_drive = 1;
initialize_readline ();
}
} 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);
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':
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;
}
/* 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);
}
}
-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
{
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);
}
}
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)
{