X-Git-Url: http://git.annexia.org/?a=blobdiff_plain;f=src%2Fguestfs.c;h=a00e8db653bacf9700e07c19d5e0d9bd309f6d76;hb=a6802f80b3b91bfc7d6846db2989c381385e6a15;hp=1d6d40bf7322edfb98c81108e384412b42ec0860;hpb=214babb060d9e85e72bad4633310a9b4a5fcb9dd;p=libguestfs.git diff --git a/src/guestfs.c b/src/guestfs.c index 1d6d40b..a00e8db 100644 --- a/src/guestfs.c +++ b/src/guestfs.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -91,9 +92,14 @@ static int recv_from_daemon (guestfs_h *g, uint32_t *size_rtn, void **buf_rtn); static int accept_from_daemon (guestfs_h *g); static int check_peer_euid (guestfs_h *g, int sock, uid_t *rtn); static void close_handles (void); +static int qemu_supports (guestfs_h *g, const char *option); #define UNIX_PATH_MAX 108 +#ifndef MAX +#define MAX(a,b) ((a)>(b)?(a):(b)) +#endif + /* Also in guestfsd.c */ #define GUESTFWD_ADDR "10.0.2.4" #define GUESTFWD_PORT "6666" @@ -748,7 +754,8 @@ guestfs__config (guestfs_h *g, } int -guestfs__add_drive (guestfs_h *g, const char *filename) +guestfs__add_drive_with_if (guestfs_h *g, const char *filename, + const char *drive_if) { size_t len = strlen (filename) + 64; char buf[len]; @@ -771,12 +778,12 @@ guestfs__add_drive (guestfs_h *g, const char *filename) int fd = open (filename, O_RDONLY|O_DIRECT); if (fd >= 0) { close (fd); - snprintf (buf, len, "file=%s,cache=off,if=" DRIVE_IF, filename); + snprintf (buf, len, "file=%s,cache=off,if=%s", filename, drive_if); } else { fd = open (filename, O_RDONLY); if (fd >= 0) { close (fd); - snprintf (buf, len, "file=%s,if=" DRIVE_IF, filename); + snprintf (buf, len, "file=%s,if=%s", filename, drive_if); } else { perrorf (g, "%s", filename); return -1; @@ -787,11 +794,9 @@ guestfs__add_drive (guestfs_h *g, const char *filename) } int -guestfs__add_drive_ro (guestfs_h *g, const char *filename) +guestfs__add_drive_ro_with_if (guestfs_h *g, const char *filename, + const char *drive_if) { - size_t len = strlen (filename) + 64; - char buf[len]; - if (strchr (filename, ',') != NULL) { error (g, _("filename cannot contain ',' (comma) character")); return -1; @@ -802,12 +807,41 @@ guestfs__add_drive_ro (guestfs_h *g, const char *filename) return -1; } - snprintf (buf, len, "file=%s,snapshot=on,if=%s", filename, DRIVE_IF); + if (qemu_supports (g, NULL) == -1) + return -1; + + /* Only SCSI and virtio drivers support readonly mode. + * This is only supported as a QEMU feature since 2010/01. + */ + int supports_ro = 0; + if ((STREQ (drive_if, "scsi") || STREQ (drive_if, "virtio")) && + qemu_supports (g, "readonly=on")) + supports_ro = 1; + + size_t len = strlen (filename) + 100; + char buf[len]; + + snprintf (buf, len, "file=%s,snapshot=on,%sif=%s", + filename, + supports_ro ? "readonly=on," : "", + drive_if); return guestfs__config (g, "-drive", buf); } int +guestfs__add_drive (guestfs_h *g, const char *filename) +{ + return guestfs__add_drive_with_if (g, filename, DRIVE_IF); +} + +int +guestfs__add_drive_ro (guestfs_h *g, const char *filename) +{ + return guestfs__add_drive_ro_with_if (g, filename, DRIVE_IF); +} + +int guestfs__add_cdrom (guestfs_h *g, const char *filename) { if (strchr (filename, ',') != NULL) { @@ -856,8 +890,7 @@ dir_contains_files (const char *dir, ...) static void print_timestamped_message (guestfs_h *g, const char *fs, ...); static int build_supermin_appliance (guestfs_h *g, const char *path, char **kernel, char **initrd); -static int test_qemu (guestfs_h *g); -static int qemu_supports (guestfs_h *g, const char *option); +static int is_openable (guestfs_h *g, const char *path, int flags); static void print_cmdline (guestfs_h *g); static const char *kernel_name = "vmlinuz." REPO "." host_cpu; @@ -1008,7 +1041,7 @@ guestfs__launch (guestfs_h *g) print_timestamped_message (g, "begin testing qemu features"); /* Get qemu help text and version. */ - if (test_qemu (g) == -1) + if (qemu_supports (g, NULL) == -1) goto cleanup0; /* Choose which vmchannel implementation to use. */ @@ -1091,6 +1124,22 @@ guestfs__launch (guestfs_h *g) */ g->cmdline[0] = g->qemu; + /* qemu sometimes needs this option to enable hardware + * virtualization, but some versions of 'qemu-kvm' will use KVM + * regardless (even where this option appears in the help text). + * It is rumoured that there are versions of qemu where supplying + * this option when hardware virtualization is not available will + * cause qemu to fail, so we we have to check at least that + * /dev/kvm is openable. That's not reliable, since /dev/kvm + * might be openable by qemu but not by us (think: SELinux) in + * which case the user would not get hardware virtualization, + * although at least shouldn't fail. A giant clusterfuck with the + * qemu command line, again. + */ + if (qemu_supports (g, "-enable-kvm") && + is_openable (g, "/dev/kvm", O_RDWR)) + add_cmdline (g, "-enable-kvm"); + /* Newer versions of qemu (from around 2009/12) changed the * behaviour of monitors so that an implicit '-monitor stdio' is * assumed if we are in -nographic mode and there is no other @@ -1506,8 +1555,9 @@ build_supermin_appliance (guestfs_h *g, const char *path, snprintf (cmd, sizeof cmd, "PATH='%s':$PATH " - "libguestfs-supermin-helper '%s' " host_cpu " " REPO " %s %s", + "libguestfs-supermin-helper%s '%s' " host_cpu " " REPO " %s %s", path, + g->verbose ? " --verbose" : "", path, *kernel, *initrd); if (g->verbose) print_timestamped_message (g, "%s", cmd); @@ -1547,7 +1597,7 @@ print_timestamped_message (guestfs_h *g, const char *fs, ...) va_list args; char *msg; int err; - struct timeval tv, diff; + struct timeval tv; va_start (args, fs); err = vasprintf (&msg, fs, args); @@ -1575,12 +1625,7 @@ test_qemu (guestfs_h *g) char cmd[1024]; FILE *fp; - free (g->qemu_help); - free (g->qemu_version); - g->qemu_help = NULL; - g->qemu_version = NULL; - - snprintf (cmd, sizeof cmd, "LC_ALL=C '%s' -help", g->qemu); + snprintf (cmd, sizeof cmd, "LC_ALL=C '%s' -nographic -help", g->qemu); fp = popen (cmd, "r"); /* qemu -help should always work (qemu -version OTOH wasn't @@ -1602,7 +1647,8 @@ test_qemu (guestfs_h *g) if (pclose (fp) == -1) goto error; - snprintf (cmd, sizeof cmd, "LC_ALL=C '%s' -version 2>/dev/null", g->qemu); + snprintf (cmd, sizeof cmd, "LC_ALL=C '%s' -nographic -version 2>/dev/null", + g->qemu); fp = popen (cmd, "r"); if (fp) { @@ -1640,11 +1686,40 @@ read_all (guestfs_h *g, FILE *fp, char **ret) /* Test if option is supported by qemu command line (just by grepping * the help text). + * + * The first time this is used, it has to run the external qemu + * binary. If that fails, it returns -1. + * + * To just do the first-time run of the qemu binary, call this with + * option == NULL, in which case it will return -1 if there was an + * error doing that. */ static int qemu_supports (guestfs_h *g, const char *option) { - return g->qemu_help && strstr (g->qemu_help, option) != NULL; + if (!g->qemu_help) { + if (test_qemu (g) == -1) + return -1; + } + + if (option == NULL) + return 1; + + return strstr (g->qemu_help, option) != NULL; +} + +/* Check if a file can be opened. */ +static int +is_openable (guestfs_h *g, const char *path, int flags) +{ + int fd = open (path, flags); + if (fd == -1) { + if (g->verbose) + perror (path); + return 0; + } + close (fd); + return 1; } /* Check the peer effective UID for a TCP socket. Ideally we'd like @@ -2588,7 +2663,6 @@ guestfs___recv_file (guestfs_h *g, const char *filename) { void *buf; int fd, r; - size_t len; fd = open (filename, O_WRONLY|O_CREAT|O_TRUNC|O_NOCTTY, 0666); if (fd == -1) {