diff --git a/src/qemu_conf.c b/src/qemu_conf.c index b17d0c8..3d45669 100644 --- a/src/qemu_conf.c +++ b/src/qemu_conf.c @@ -428,13 +428,16 @@ virCapsPtr qemudCapsInit(void) { return NULL; } - -static int qemudExtractVersionInfo(const char *qemu, int *version, int *flags) { +static int qemudExtractCommandOutput(const char *app, char *buf, size_t size) { pid_t child; int newstdout[2]; + struct stat sb; - *flags = 0; - *version = 0; + if (stat(app, &sb) < 0) { + qemudLog(QEMUD_ERR, _("Cannot find binary %s: %s"), app, + strerror(errno)); + return -1; + } if (pipe(newstdout) < 0) { return -1; @@ -458,21 +461,19 @@ static int qemudExtractVersionInfo(const char *qemu, int *version, int *flags) { /* Just in case QEMU is translated someday.. */ setenv("LANG", "C", 1); - execl(qemu, qemu, (char*)NULL); + execl(app, app, (char *) NULL); cleanup1: _exit(-1); /* Just in case */ } else { /* Parent */ - char help[8192]; /* Ought to be enough to hold QEMU help screen */ - int got = 0, ret = -1; - int major, minor, micro; + int got = 0, ret = -1; if (close(newstdout[1]) < 0) goto cleanup2; - while (got < (sizeof(help)-1)) { + while (got < size-1) { int len; - if ((len = read(newstdout[0], help+got, sizeof(help)-got-1)) <= 0) { + if ((len = read(newstdout[0], buf+got, size-got-1)) <= 0) { if (!len) break; if (errno == EINTR) @@ -481,30 +482,9 @@ static int qemudExtractVersionInfo(const char *qemu, int *version, int *flags) { } got += len; } - help[got] = '\0'; - - if (sscanf(help, "QEMU PC emulator version %d.%d.%d", &major,&minor, µ) != 3) { - goto cleanup2; - } - - *version = (major * 1000 * 1000) + (minor * 1000) + micro; - if (strstr(help, "-no-kqemu")) - *flags |= QEMUD_CMD_FLAG_KQEMU; - if (strstr(help, "-no-reboot")) - *flags |= QEMUD_CMD_FLAG_NO_REBOOT; - if (strstr(help, "-name")) - *flags |= QEMUD_CMD_FLAG_NAME; - if (strstr(help, "-drive")) - *flags |= QEMUD_CMD_FLAG_DRIVE; - if (strstr(help, "boot=on")) - *flags |= QEMUD_CMD_FLAG_DRIVE_BOOT; - if (*version >= 9000) - *flags |= QEMUD_CMD_FLAG_VNC_COLON; + buf[got] = '\0'; ret = 0; - qemudDebug("Version %d %d %d Cooked version: %d, with flags ? %d", - major, minor, micro, *version, *flags); - cleanup2: if (close(newstdout[0]) < 0) ret = -1; @@ -515,8 +495,8 @@ static int qemudExtractVersionInfo(const char *qemu, int *version, int *flags) { goto rewait; } qemudLog(QEMUD_ERR, - _("Unexpected exit status from qemu %d pid %lu"), - got, (unsigned long)child); + _("Unexpected exit status from app %s, exit %d: pid %lu"), + app, got, (unsigned long)child); ret = -1; } /* Check & log unexpected exit status, but don't fail, @@ -524,38 +504,119 @@ static int qemudExtractVersionInfo(const char *qemu, int *version, int *flags) { * actually read a valid version number above */ if (WEXITSTATUS(got) != 1) { qemudLog(QEMUD_WARN, - _("Unexpected exit status '%d', qemu probably failed"), - got); + _("Unexpected exit status '%d', app '%s' probably failed"), + got, app); } return ret; } } -int qemudExtractVersion(virConnectPtr conn, - struct qemud_driver *driver) { +static int qemudExtractQemuVersionInfo(const char *app, int *version, + int *flags) { + + char help[8192]; /* Ought to be enough to hold QEMU help screen */ + int major, minor, micro; + int ret = -1; + + *flags = 0; + *version = 0; + + if (qemudExtractCommandOutput(app, help, sizeof(help)) < 0) { + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("Failed to extract KVM output.")); + goto error; + } + + if (sscanf(help, "QEMU PC emulator version %d.%d.%d", &major, &minor, + µ) != 3) { + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("Failed to parse QEMU version.")); + goto error; + } + + *version = (major * 1000 * 1000) + (minor * 1000) + micro; + if (strstr(help, "-no-kqemu")) + *flags |= QEMUD_CMD_FLAG_KQEMU; + if (strstr(help, "-no-reboot")) + *flags |= QEMUD_CMD_FLAG_NO_REBOOT; + if (strstr(help, "-name")) + *flags |= QEMUD_CMD_FLAG_NAME; + if (strstr(help, "-drive")) + *flags |= QEMUD_CMD_FLAG_DRIVE; + if (strstr(help, "boot=on")) + *flags |= QEMUD_CMD_FLAG_DRIVE_BOOT; + if (*version >= 9000) + *flags |= QEMUD_CMD_FLAG_VNC_COLON; + + qemudDebug("Version %d %d %d Cooked version: %d, with flags ? %d", + major, minor, micro, *version, *flags); + + ret = 0; + error: + return ret; +} + +static int qemudExtractKVMVersionInfo(const char *app, int *version) { + + char buf[8192]; /* Ought to be enough to hold QEMU help screen */ + char ignore[50] = "\0"; + int ret = -1; + + *version = 0; + + if (qemudExtractCommandOutput(app, buf, sizeof(buf)) < 0) { + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("Failed to extract KVM output.")); + goto error; + } + + // Format = "QEMU PC emulator version major.minor.micro(-cvs) (KVM-ver)" + if (sscanf(buf, "QEMU PC emulator version %s (kvm-%d", ignore, + version) != 2) { + qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("Failed to parse KVM version.")); + goto error; + } + + qemudDebug("KVM version: %d", *version); + + ret = 0; + error: + return ret; +} + +int qemudExtractVersion(struct qemud_driver *driver) { const char *binary; - struct stat sb; int ignored; + struct utsname utsname; - if (driver->qemuVersion > 0) - return 0; + uname(&utsname); - if ((binary = virCapabilitiesDefaultGuestEmulator(driver->caps, - "hvm", - "i686", - "qemu")) == NULL) - return -1; + if (driver->qemuVersion <= 0) { - if (stat(binary, &sb) < 0) { - qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("Cannot find QEMU binary %s: %s"), binary, - strerror(errno)); - return -1; + if ((binary = virCapabilitiesDefaultGuestEmulator(driver->caps, + "hvm", + "i686", + "qemu")) == NULL) + return -1; + + if (qemudExtractQemuVersionInfo(binary, &driver->qemuVersion, + &ignored) < 0) + return -1; } - if (qemudExtractVersionInfo(binary, &driver->qemuVersion, &ignored) < 0) { - return -1; + if (driver->kvmVersion <= 0) { + // If kvm isn't supported, don't return error since kvm is optional + if ((binary = virCapabilitiesDefaultGuestEmulator(driver->caps, + "hvm", + utsname.machine, + "kvm")) == NULL) + return 0; + + if (qemudExtractKVMVersionInfo(binary, &driver->kvmVersion) < 0) { + return -1; + } } return 0; @@ -2324,9 +2385,9 @@ int qemudBuildCommandLine(virConnectPtr conn, } if (vm->qemuVersion == 0) { - if (qemudExtractVersionInfo(vm->def->os.binary, - &(vm->qemuVersion), - &(vm->qemuCmdFlags)) < 0) + if (qemudExtractQemuVersionInfo(vm->def->os.binary, + &(vm->qemuVersion), + &(vm->qemuCmdFlags)) < 0) return -1; } diff --git a/src/qemu_conf.h b/src/qemu_conf.h index 5d07c89..914d418 100644 --- a/src/qemu_conf.h +++ b/src/qemu_conf.h @@ -392,6 +392,7 @@ struct qemud_network { /* Main driver state */ struct qemud_driver { int qemuVersion; + int kvmVersion; int nactivevms; int ninactivevms; struct qemud_vm *vms; @@ -451,8 +452,7 @@ struct qemud_network *qemudFindNetworkByName(const struct qemud_driver *driver, virCapsPtr qemudCapsInit (void); -int qemudExtractVersion (virConnectPtr conn, - struct qemud_driver *driver); +int qemudExtractVersion (struct qemud_driver *driver); int qemudBuildCommandLine (virConnectPtr conn, struct qemud_driver *driver, struct qemud_vm *vm, diff --git a/src/qemu_driver.c b/src/qemu_driver.c index 34193bd..4df9d59 100644 --- a/src/qemu_driver.c +++ b/src/qemu_driver.c @@ -222,6 +222,12 @@ qemudStartup(void) { if ((qemu_driver->caps = qemudCapsInit()) == NULL) goto out_of_memory; + // Dependent on capabilities being initialized + if (qemudExtractVersion(qemu_driver) < 0) { + qemudShutdown(); + return -1; + } + if (qemudLoadDriverConfig(qemu_driver, driverConf) < 0) { qemudShutdown(); return -1; @@ -1566,16 +1572,24 @@ static const char *qemudGetType(virConnectPtr conn ATTRIBUTE_UNUSED) { } static int qemudGetMaxVCPUs(virConnectPtr conn, const char *type) { + struct qemud_driver *driver = (struct qemud_driver *)conn->privateData; + if (!type) return 16; if (STRCASEEQ(type, "qemu")) return 16; - /* XXX future KVM will support SMP. Need to probe - kernel to figure out KVM module version i guess */ - if (STRCASEEQ(type, "kvm")) - return 1; + if (STRCASEEQ(type, "kvm")) { + // KVM-30 added support for up to 4 vcpus + // KVM-62 raised this to 16 + if (driver->kvmVersion < 30) + return 1; + else if (driver->kvmVersion < 62) + return 4; + else + return 16; + } if (STRCASEEQ(type, "kqemu")) return 1; @@ -1690,7 +1704,7 @@ static virDomainPtr qemudDomainLookupByName(virConnectPtr conn, static int qemudGetVersion(virConnectPtr conn, unsigned long *version) { struct qemud_driver *driver = (struct qemud_driver *)conn->privateData; - if (qemudExtractVersion(conn, driver) < 0) + if (qemudExtractVersion(driver) < 0) return -1; *version = qemu_driver->qemuVersion;