
On Wed, May 13, 2009 at 06:53:44PM +0200, Daniel Veillard wrote:
On Wed, May 13, 2009 at 03:40:54PM +0100, Daniel P. Berrange wrote:
This provides the QEMU driver implementation which is able to convert from QEMU argv into domain XML. This is alot of hard code, because we have to parse and interpret arbitrary QEMU args and had no existing code doing this. This is also actually the single most useful feature of this patchset, and the original motivation. With this available, it makes it very easy for people using QEMU to switch over to using libvirt
Here's an update with the VIR_REALLOCs in the loop changed to alloc in chunks of 10. Also fixes a few misc bugs in the conversions, and the style items pointed out last time. Daniel diff -r 832729f8dd03 src/domain_conf.h --- a/src/domain_conf.h Tue May 19 18:43:30 2009 +0100 +++ b/src/domain_conf.h Tue May 19 18:43:59 2009 +0100 @@ -253,7 +253,7 @@ enum virDomainSoundModel { VIR_DOMAIN_SOUND_MODEL_SB16, VIR_DOMAIN_SOUND_MODEL_ES1370, VIR_DOMAIN_SOUND_MODEL_PCSPK, - VIR_DOMAIN_SOUND_MODEL_ES97, + VIR_DOMAIN_SOUND_MODEL_AC97, VIR_DOMAIN_SOUND_MODEL_LAST }; diff -r 832729f8dd03 src/qemu_conf.c --- a/src/qemu_conf.c Tue May 19 18:43:30 2009 +0100 +++ b/src/qemu_conf.c Tue May 19 18:43:59 2009 +0100 @@ -1,5 +1,5 @@ /* - * config.c: VM configuration management + * qemu_conf.c: QEMU configuration management * * Copyright (C) 2006, 2007, 2008, 2009 Red Hat, Inc. * Copyright (C) 2006 Daniel P. Berrange @@ -36,6 +36,7 @@ #include <arpa/inet.h> #include <sys/utsname.h> +#include "c-ctype.h" #include "virterror_internal.h" #include "qemu_conf.h" #include "uuid.h" @@ -1542,6 +1543,1229 @@ int qemudBuildCommandLine(virConnectPtr } +/* + * This method takes a string representing a QEMU command line ARGV set + * optionall prefixed by a list of environment variables. It then tries + * to split it up into a NULL terminated list of env & argv, splitting + * on space + */ +static int qemuStringToArgvEnv(const char *args, + const char ***retenv, + const char ***retargv) +{ + char **arglist = NULL; + int argcount = 0; + int argalloc = 0; + int envend; + int i; + const char *curr = args; + const char **progenv = NULL; + const char **progargv = NULL; + + /* Iterate over string, splitting on sequences of ' ' */ + while (curr && *curr != '\0') { + char *arg; + const char *next = strchr(curr, ' '); + if (!next) + next = strchr(curr, '\n'); + + if (next) + arg = strndup(curr, next-curr); + else + arg = strdup(curr); + + if (!arg) + goto no_memory; + + if (argalloc == argcount) { + if (VIR_REALLOC_N(arglist, argalloc+10) < 0) { + VIR_FREE(arg); + goto no_memory; + } + argalloc+=10; + } + + arglist[argcount++] = arg; + + while (next && c_isspace(*next)) + next++; + + curr = next; + } + + /* Iterate over list of args, finding first arg not containining + * the '=' character (eg, skip over env vars FOO=bar) */ + for (envend = 0 ; ((envend < argcount) && + (strchr(arglist[envend], '=') != NULL)); + envend++) + ; /* nada */ + + /* Copy the list of env vars */ + if (envend > 0) { + if (VIR_REALLOC_N(progenv, envend+1) < 0) + goto no_memory; + for (i = 0 ; i < envend ; i++) { + progenv[i] = arglist[i]; + } + progenv[i] = NULL; + } + + /* Copy the list of argv */ + if (VIR_REALLOC_N(progargv, argcount-envend + 1) < 0) + goto no_memory; + for (i = envend ; i < argcount ; i++) + progargv[i-envend] = arglist[i]; + progargv[i-envend] = NULL; + + VIR_FREE(arglist); + + *retenv = progenv; + *retargv = progargv; + + return 0; + +no_memory: + for (i = 0 ; progenv && progenv[i] ; i++) + VIR_FREE(progenv[i]); + VIR_FREE(progenv); + for (i = 0 ; i < argcount ; i++) + VIR_FREE(arglist[i]); + VIR_FREE(arglist); + return -1; +} + + +/* + * Search for a named env variable, and return the value part + */ +static const char *qemuFindEnv(const char **progenv, + const char *name) +{ + int i; + int len = strlen(name); + + for (i = 0 ; progenv[i] ; i++) { + if (STREQLEN(progenv[i], name, len) && + progenv[i][len] == '=') + return progenv[i] + len + 1; + } + return NULL; +} + +/* + * Takes a string containing a set of key=value,key=value,key... + * parameters and splits them up, returning two arrays with + * the individual keys and values + */ +static int +qemuParseCommandLineKeywords(virConnectPtr conn, + const char *str, + char ***retkeywords, + char ***retvalues) +{ + int keywordCount = 0; + int keywordAlloc = 0; + char **keywords = NULL; + char **values = NULL; + const char *start = str; + int i; + + *retkeywords = NULL; + *retvalues = NULL; + + while (start) { + const char *separator; + const char *endmark; + char *keyword; + char *value; + + if (!(separator = strchr(start, '='))) { + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("malformed keyword arguments in '%s'"), str); + goto error; + } + if (!(keyword = strndup(start, separator - start))) + goto no_memory; + + separator++; + endmark = strchr(separator, ','); + + value = endmark ? + strndup(separator, endmark - separator) : + strdup(separator); + if (!value) { + VIR_FREE(keyword); + goto no_memory; + } + + if (keywordAlloc == keywordCount) { + if (VIR_REALLOC_N(keywords, keywordAlloc + 10) < 0 || + VIR_REALLOC_N(values, keywordAlloc + 10) < 0) { + VIR_FREE(keyword); + VIR_FREE(value); + goto no_memory; + } + keywordAlloc += 10; + } + + keywords[keywordCount] = keyword; + values[keywordCount] = value; + keywordCount++; + + start = endmark ? endmark + 1 : NULL; + } + + *retkeywords = keywords; + *retvalues = values; + + return keywordCount; + +no_memory: + virReportOOMError(conn); +error: + for (i = 0 ; i < keywordCount ; i++) { + VIR_FREE(keywords[i]); + VIR_FREE(values[i]); + } + VIR_FREE(keywords); + VIR_FREE(values); + return -1; +} + +/* + * Tries to parse new style QEMU -drive args. + * + * eg -drive file=/dev/HostVG/VirtData1,if=ide,index=1 + * + * Will fail if not using the 'index' keyword + */ +static virDomainDiskDefPtr +qemuParseCommandLineDisk(virConnectPtr conn, + const char *val) +{ + virDomainDiskDefPtr def = NULL; + char **keywords; + char **values; + int nkeywords; + int i; + int idx = -1; + + if ((nkeywords = qemuParseCommandLineKeywords(conn, val, + &keywords, + &values)) < 0) + return NULL; + + if (VIR_ALLOC(def) < 0) { + virReportOOMError(conn); + goto cleanup; + } + + def->bus = VIR_DOMAIN_DISK_BUS_IDE; + def->device = VIR_DOMAIN_DISK_DEVICE_DISK; + + for (i = 0 ; i < nkeywords ; i++) { + if (STREQ(keywords[i], "file")) { + if (values[i] && STRNEQ(values[i], "")) { + def->src = values[i]; + values[i] = NULL; + if (STRPREFIX(def->src, "/dev/")) + def->type = VIR_DOMAIN_DISK_TYPE_BLOCK; + else + def->type = VIR_DOMAIN_DISK_TYPE_FILE; + } else { + def->type = VIR_DOMAIN_DISK_TYPE_FILE; + } + } else if (STREQ(keywords[i], "if")) { + if (STREQ(values[i], "ide")) + def->bus = VIR_DOMAIN_DISK_BUS_IDE; + else if (STREQ(values[i], "scsi")) + def->bus = VIR_DOMAIN_DISK_BUS_SCSI; + else if (STREQ(values[i], "virtio")) + def->bus = VIR_DOMAIN_DISK_BUS_VIRTIO; + else if (STREQ(values[i], "xen")) + def->bus = VIR_DOMAIN_DISK_BUS_XEN; + } else if (STREQ(keywords[i], "media")) { + if (STREQ(values[i], "cdrom")) { + def->device = VIR_DOMAIN_DISK_DEVICE_CDROM; + def->readonly = 1; + } else if (STREQ(values[i], "floppy")) + def->device = VIR_DOMAIN_DISK_DEVICE_FLOPPY; + } else if (STREQ(keywords[i], "format")) { + def->driverName = strdup("qemu"); + if (!def->driverName) { + virDomainDiskDefFree(def); + def = NULL; + virReportOOMError(conn); + goto cleanup; + } + def->driverType = values[i]; + values[i] = NULL; + } else if (STREQ(keywords[i], "cache")) { + if (STREQ(values[i], "off") || + STREQ(values[i], "none")) + def->cachemode = VIR_DOMAIN_DISK_CACHE_DISABLE; + else if (STREQ(values[i], "writeback") || + STREQ(values[i], "on")) + def->cachemode = VIR_DOMAIN_DISK_CACHE_WRITEBACK; + else if (STREQ(values[i], "writethrough")) + def->cachemode = VIR_DOMAIN_DISK_CACHE_WRITETHRU; + } else if (STREQ(keywords[i], "index")) { + if (virStrToLong_i(values[i], NULL, 10, &idx) < 0) { + virDomainDiskDefFree(def); + def = NULL; + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("cannot parse drive index '%s'"), val); + goto cleanup; + } + } + } + + if (!def->src && + def->device == VIR_DOMAIN_DISK_DEVICE_DISK) { + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("missing file parameter in drive '%s'"), val); + virDomainDiskDefFree(def); + def = NULL; + goto cleanup; + } + if (idx == -1) { + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("missing index parameter in drive '%s'"), val); + virDomainDiskDefFree(def); + def = NULL; + goto cleanup; + } + + if (def->bus == VIR_DOMAIN_DISK_BUS_IDE) { + def->dst = strdup("hda"); + } else if (def->bus == VIR_DOMAIN_DISK_BUS_SCSI) { + def->dst = strdup("sda"); + } else if (def->bus == VIR_DOMAIN_DISK_BUS_VIRTIO) { + def->dst = strdup("vda"); + } else if (def->bus == VIR_DOMAIN_DISK_BUS_XEN) { + def->dst = strdup("xvda"); + } else { + def->dst = strdup("hda"); + } + + if (!def->dst) { + virDomainDiskDefFree(def); + def = NULL; + virReportOOMError(conn); + goto cleanup; + } + if (STREQ(def->dst, "xvda")) + def->dst[3] = 'a' + idx; + else + def->dst[2] = 'a' + idx; + +cleanup: + for (i = 0 ; i < nkeywords ; i++) { + VIR_FREE(keywords[i]); + VIR_FREE(values[i]); + } + VIR_FREE(keywords); + VIR_FREE(values); + return def; +} + +/* + * Tries to find a NIC definition matching a vlan we want + */ +static const char * +qemuFindNICForVLAN(virConnectPtr conn, + int nnics, + const char **nics, + int wantvlan) +{ + int i; + for (i = 0 ; i < nnics ; i++) { + int gotvlan; + const char *tmp = strstr(nics[i], "vlan="); + char *end; + if (tmp) + tmp += strlen("vlan="); + + if (virStrToLong_i(tmp, &end, 10, &gotvlan) < 0) { + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("cannot parse NIC vlan in '%s'"), nics[i]); + return NULL; + } + + if (gotvlan == wantvlan) + return nics[i]; + } + + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("cannot find NIC definition for vlan %d"), wantvlan); + return NULL; +} + + +/* + * Tries to parse a QEMU -net backend argument. Gets given + * a list of all known -net frontend arguments to try and + * match up against. Horribly complicated stuff + */ +static virDomainNetDefPtr +qemuParseCommandLineNet(virConnectPtr conn, + const char *val, + int nnics, + const char **nics) +{ + virDomainNetDefPtr def = NULL; + char **keywords; + char **values; + int nkeywords; + const char *nic; + int wantvlan = 0; + const char *tmp; + int i; + + tmp = strchr(val, ','); + if (!tmp) { + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("cannot extract NIC type from '%s'"), val); + return NULL; + } + + if ((nkeywords = qemuParseCommandLineKeywords(conn, + tmp+1, + &keywords, + &values)) < 0) + return NULL; + + if (VIR_ALLOC(def) < 0) { + virReportOOMError(conn); + goto cleanup; + } + + /* 'tap' could turn into libvirt type=ethernet, type=bridge or + * type=network, but we can't tell, so use the generic config */ + if (STRPREFIX(val, "tap,")) + def->type = VIR_DOMAIN_NET_TYPE_ETHERNET; + else if (STRPREFIX(val, "socket")) + def->type = VIR_DOMAIN_NET_TYPE_CLIENT; + else if (STRPREFIX(val, "user")) + def->type = VIR_DOMAIN_NET_TYPE_USER; + else + def->type = VIR_DOMAIN_NET_TYPE_ETHERNET; + + for (i = 0 ; i < nkeywords ; i++) { + if (STREQ(keywords[i], "vlan")) { + if (virStrToLong_i(values[i], NULL, 10, &wantvlan) < 0) { + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("cannot parse vlan in '%s'"), val); + virDomainNetDefFree(def); + def = NULL; + goto cleanup; + } + } else if (def->type == VIR_DOMAIN_NET_TYPE_ETHERNET && + STREQ(keywords[i], "script") && STRNEQ(values[i], "")) { + def->data.ethernet.script = values[i]; + values[i] = NULL; + } else if (def->type == VIR_DOMAIN_NET_TYPE_ETHERNET && + STREQ(keywords[i], "ifname")) { + def->ifname = values[i]; + values[i] = NULL; + } + } + + + /* Done parsing the nic backend. Now to try and find corresponding + * frontend, based off vlan number. NB this assumes a 1-1 mapping + */ + + nic = qemuFindNICForVLAN(conn, nnics, nics, wantvlan); + if (!nic) { + virDomainNetDefFree(def); + def = NULL; + goto cleanup; + } + + if (!STRPREFIX(nic, "nic,")) { + virDomainNetDefFree(def); + def = NULL; + goto cleanup; + } + + for (i = 0 ; i < nkeywords ; i++) { + VIR_FREE(keywords[i]); + VIR_FREE(values[i]); + } + VIR_FREE(keywords); + VIR_FREE(values); + + if ((nkeywords = qemuParseCommandLineKeywords(conn, + nic + strlen("nic,"), + &keywords, + &values)) < 0) { + virDomainNetDefFree(def); + def = NULL; + goto cleanup; + } + + for (i = 0 ; i < nkeywords ; i++) { + if (STREQ(keywords[i], "macaddr")) { + virParseMacAddr(values[i], def->mac); + } else if (STREQ(keywords[i], "model")) { + def->model = values[i]; + values[i] = NULL; + } + } + +cleanup: + for (i = 0 ; i < nkeywords ; i++) { + VIR_FREE(keywords[i]); + VIR_FREE(values[i]); + } + VIR_FREE(keywords); + VIR_FREE(values); + return def; +} + + +/* + * Tries to parse a QEMU PCI device + */ +static virDomainHostdevDefPtr +qemuParseCommandLinePCI(virConnectPtr conn, + const char *val) +{ + virDomainHostdevDefPtr def = NULL; + int bus = 0, slot = 0, func = 0; + const char *start; + char *end; + + if (!STRPREFIX(val, "host=")) { + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("unknown PCI device syntax '%s'"), val); + VIR_FREE(def); + goto cleanup; + } + + start = val + strlen("host="); + if (virStrToLong_i(start, &end, 16, &bus) < 0 || !end || *end != ':') { + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("cannot extract PCI device bus '%s'"), val); + VIR_FREE(def); + goto cleanup; + } + start = end + 1; + if (virStrToLong_i(start, &end, 16, &slot) < 0 || !end || *end != '.') { + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("cannot extract PCI device slot '%s'"), val); + VIR_FREE(def); + goto cleanup; + } + start = end + 1; + if (virStrToLong_i(start, NULL, 16, &func) < 0) { + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("cannot extract PCI device function '%s'"), val); + VIR_FREE(def); + goto cleanup; + } + + if (VIR_ALLOC(def) < 0) { + virReportOOMError(conn); + goto cleanup; + } + + def->mode = VIR_DOMAIN_HOSTDEV_MODE_SUBSYS; + def->managed = 1; + def->source.subsys.type = VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI; + def->source.subsys.u.pci.bus = bus; + def->source.subsys.u.pci.slot = slot; + def->source.subsys.u.pci.function = func; + +cleanup: + return def; +} + + +/* + * Tries to parse a QEMU USB device + */ +static virDomainHostdevDefPtr +qemuParseCommandLineUSB(virConnectPtr conn, + const char *val) +{ + virDomainHostdevDefPtr def = NULL; + int first = 0, second = 0; + const char *start; + char *end; + + if (!STRPREFIX(val, "host:")) { + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("unknown PCI device syntax '%s'"), val); + VIR_FREE(def); + goto cleanup; + } + + start = val + strlen("host:"); + if (strchr(start, ':')) { + if (virStrToLong_i(start, &end, 16, &first) < 0 || !end || *end != ':') { + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("cannot extract USB device vendor '%s'"), val); + VIR_FREE(def); + goto cleanup; + } + start = end + 1; + if (virStrToLong_i(start, NULL, 16, &second) < 0) { + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("cannot extract PCI device product '%s'"), val); + VIR_FREE(def); + goto cleanup; + } + } else { + if (virStrToLong_i(start, &end, 10, &first) < 0 || !end || *end != '.') { + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("cannot extract PCI device bus '%s'"), val); + VIR_FREE(def); + goto cleanup; + } + start = end + 1; + if (virStrToLong_i(start, NULL, 10, &second) < 0) { + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("cannot extract PCI device address '%s'"), val); + VIR_FREE(def); + goto cleanup; + } + } + + if (VIR_ALLOC(def) < 0) { + virReportOOMError(conn); + goto cleanup; + } + + def->mode = VIR_DOMAIN_HOSTDEV_MODE_SUBSYS; + def->managed = 0; + def->source.subsys.type = VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB; + if (*end == '.') { + def->source.subsys.u.usb.bus = first; + def->source.subsys.u.usb.device = second; + } else { + def->source.subsys.u.usb.vendor = first; + def->source.subsys.u.usb.product = second; + } + +cleanup: + return def; +} + + +/* + * Tries to parse a QEMU serial/parallel device + */ +static virDomainChrDefPtr +qemuParseCommandLineChr(virConnectPtr conn, + const char *val) +{ + virDomainChrDefPtr def; + + if (VIR_ALLOC(def) < 0) + goto no_memory; + + if (STREQ(val, "null")) { + def->type = VIR_DOMAIN_CHR_TYPE_NULL; + } else if (STREQ(val, "vc")) { + def->type = VIR_DOMAIN_CHR_TYPE_VC; + } else if (STREQ(val, "pty")) { + def->type = VIR_DOMAIN_CHR_TYPE_PTY; + } else if (STRPREFIX(val, "file:")) { + def->type = VIR_DOMAIN_CHR_TYPE_FILE; + def->data.file.path = strdup(val+strlen("file:")); + if (!def->data.file.path) + goto no_memory; + } else if (STRPREFIX(val, "pipe:")) { + def->type = VIR_DOMAIN_CHR_TYPE_PIPE; + def->data.file.path = strdup(val+strlen("pipe:")); + if (!def->data.file.path) + goto no_memory; + } else if (STREQ(val, "stdio")) { + def->type = VIR_DOMAIN_CHR_TYPE_STDIO; + } else if (STRPREFIX(val, "udp:")) { + const char *svc1, *host2, *svc2; + def->type = VIR_DOMAIN_CHR_TYPE_UDP; + val += strlen("udp:"); + svc1 = strchr(val, ':'); + host2 = svc1 ? strchr(svc1, '@') : NULL; + svc2 = host2 ? strchr(host2, ':') : NULL; + + if (svc1) + def->data.udp.connectHost = strndup(val, svc1-val); + else + def->data.udp.connectHost = strdup(val); + if (svc1) { + svc1++; + if (host2) + def->data.udp.connectService = strndup(svc1, host2-svc1); + else + def->data.udp.connectService = strdup(svc1); + } + + if (host2) { + host2++; + if (svc2) + def->data.udp.bindHost = strndup(host2, svc2-host2); + else + def->data.udp.bindHost = strdup(host2); + } + if (svc2) { + svc2++; + def->data.udp.bindService = strdup(svc2); + } + } else if (STRPREFIX(val, "tcp:") || + STRPREFIX(val, "telnet:")) { + const char *opt, *svc; + def->type = VIR_DOMAIN_CHR_TYPE_TCP; + if (STRPREFIX(val, "tcp:")) { + val += strlen("tcp:"); + } else { + val += strlen("telnet:"); + def->data.tcp.protocol = VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNET; + } + svc = strchr(val, ':'); + if (!svc) { + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("cannot find port number in character device %s"), val); + goto error; + } + opt = strchr(svc, ','); + if (opt && strstr(opt, "server")) + def->data.tcp.listen = 1; + + def->data.tcp.host = strndup(val, svc-val); + svc++; + if (opt) { + def->data.tcp.service = strndup(svc, opt-svc); + } else { + def->data.tcp.service = strdup(svc); + } + } else if (STRPREFIX(val, "unix:")) { + const char *opt; + val += strlen("unix:"); + opt = strchr(val, ','); + def->type = VIR_DOMAIN_CHR_TYPE_UNIX; + if (opt) { + if (strstr(opt, "listen")) + def->data.nix.listen = 1; + def->data.nix.path = strndup(val, opt-val); + } else { + def->data.nix.path = strdup(val); + } + if (!def->data.nix.path) + goto no_memory; + + } else if (STRPREFIX(val, "/dev")) { + def->type = VIR_DOMAIN_CHR_TYPE_DEV; + def->data.file.path = strdup(val); + if (!def->data.file.path) + goto no_memory; + } else { + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("unknown character device syntax %s"), val); + goto error; + } + + return def; + +no_memory: + virReportOOMError(conn); +error: + virDomainChrDefFree(def); + return NULL; +} + +/* + * Analyse the env and argv settings and reconstruct a + * virDomainDefPtr representing these settings as closely + * as is practical. This is not an exact science.... + */ +virDomainDefPtr qemuParseCommandLine(virConnectPtr conn, + const char **progenv, + const char **progargv) +{ + virDomainDefPtr def; + int i; + int nographics = 0; + int fullscreen = 0; + char *path; + int nnics = 0; + const char **nics = NULL; + + if (!progargv[0]) { + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("no emulator path found")); + return NULL; + } + + if (VIR_ALLOC(def) < 0) + goto no_memory; + + def->id = -1; + def->memory = def->maxmem = 64 * 1024; + def->vcpus = 1; + def->features = (1 << VIR_DOMAIN_FEATURE_ACPI) + /*| (1 << VIR_DOMAIN_FEATURE_APIC)*/; + def->onReboot = VIR_DOMAIN_LIFECYCLE_RESTART; + def->onCrash = VIR_DOMAIN_LIFECYCLE_DESTROY; + def->onPoweroff = VIR_DOMAIN_LIFECYCLE_DESTROY; + def->virtType = VIR_DOMAIN_VIRT_QEMU; + if (!(def->emulator = strdup(progargv[0]))) + goto no_memory; + + if (strstr(def->emulator, "kvm")) { + def->virtType = VIR_DOMAIN_VIRT_KVM; + def->features |= (1 << VIR_DOMAIN_FEATURE_PAE); + } + + + if (strstr(def->emulator, "xenner")) { + def->virtType = VIR_DOMAIN_VIRT_KVM; + def->os.type = strdup("xen"); + } else { + def->os.type = strdup("hvm"); + } + if (!def->os.type) + goto no_memory; + + if (STRPREFIX(def->emulator, "qemu")) + path = def->emulator; + else + path = strstr(def->emulator, "qemu"); + if (path && + STRPREFIX(path, "qemu-system-")) + def->os.arch = strdup(path + strlen("qemu-system-")); + else + def->os.arch = strdup("i686"); + if (!def->os.arch) + goto no_memory; + +#define WANT_VALUE() \ + const char *val = progargv[++i]; \ + if (!val) { \ + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, \ + _("missing value for %s argument"), arg); \ + goto error; \ + } + + /* One initial loop to get list of NICs, so we + * can correlate them later */ + for (i = 1 ; progargv[i] ; i++) { + const char *arg = progargv[i]; + /* Make sure we have a single - for all options to + simplify next logic */ + if (STRPREFIX(arg, "--")) + arg++; + + if (STREQ(arg, "-net")) { + WANT_VALUE(); + if (STRPREFIX(val, "nic")) { + if (VIR_REALLOC_N(nics, nnics+1) < 0) + goto no_memory; + nics[nnics++] = val; + } + } + } + + /* Now the real processing loop */ + for (i = 1 ; progargv[i] ; i++) { + const char *arg = progargv[i]; + /* Make sure we have a single - for all options to + simplify next logic */ + if (STRPREFIX(arg, "--")) + arg++; + + if (STREQ(arg, "-vnc")) { + virDomainGraphicsDefPtr vnc; + char *tmp; + WANT_VALUE(); + if (VIR_ALLOC(vnc) < 0) + goto no_memory; + vnc->type = VIR_DOMAIN_GRAPHICS_TYPE_VNC; + + tmp = strchr(val, ':'); + if (tmp) { + char *opts; + if (virStrToLong_i(tmp+1, &opts, 10, &vnc->data.vnc.port) < 0) { + VIR_FREE(vnc); + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, \ + _("cannot parse VNC port '%s'"), tmp+1); + goto error; + } + vnc->data.vnc.listenAddr = strndup(val, tmp-val); + if (!vnc->data.vnc.listenAddr) { + VIR_FREE(vnc); + goto no_memory; + } + vnc->data.vnc.port += 5900; + vnc->data.vnc.autoport = 0; + } else { + vnc->data.vnc.autoport = 1; + } + + if (VIR_REALLOC_N(def->graphics, def->ngraphics+1) < 0) { + virDomainGraphicsDefFree(vnc); + goto no_memory; + } + def->graphics[def->ngraphics++] = vnc; + } else if (STREQ(arg, "-m")) { + int mem; + WANT_VALUE(); + if (virStrToLong_i(val, NULL, 10, &mem) < 0) { + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, \ + _("cannot parse memory level '%s'"), val); + goto error; + } + def->memory = def->maxmem = mem * 1024; + } else if (STREQ(arg, "-smp")) { + int vcpus; + WANT_VALUE(); + if (virStrToLong_i(val, NULL, 10, &vcpus) < 0) { + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, \ + _("cannot parse CPU count '%s'"), val); + goto error; + } + def->vcpus = vcpus; + } else if (STREQ(arg, "-uuid")) { + WANT_VALUE(); + if (virUUIDParse(val, def->uuid) < 0) { + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, \ + _("cannot parse UUID '%s'"), val); + goto error; + } + } else if (STRPREFIX(arg, "-hd") || + STRPREFIX(arg, "-sd") || + STRPREFIX(arg, "-fd") || + STREQ(arg, "-cdrom")) { + WANT_VALUE(); + virDomainDiskDefPtr disk; + if (VIR_ALLOC(disk) < 0) + goto no_memory; + + if (STRPREFIX(val, "/dev/")) + disk->type = VIR_DOMAIN_DISK_TYPE_BLOCK; + else + disk->type = VIR_DOMAIN_DISK_TYPE_FILE; + if (STREQ(arg, "-cdrom")) { + disk->device = VIR_DOMAIN_DISK_DEVICE_CDROM; + disk->dst = strdup("hdc"); + disk->readonly = 1; + } else { + if (STRPREFIX(arg, "-fd")) { + disk->device = VIR_DOMAIN_DISK_DEVICE_FLOPPY; + disk->bus = VIR_DOMAIN_DISK_BUS_FDC; + } else { + disk->device = VIR_DOMAIN_DISK_DEVICE_DISK; + if (STRPREFIX(arg, "-hd")) + disk->bus = VIR_DOMAIN_DISK_BUS_IDE; + else + disk->bus = VIR_DOMAIN_DISK_BUS_SCSI; + } + disk->dst = strdup(arg + 1); + } + disk->src = strdup(val); + if (!disk->src || + !disk->dst) { + virDomainDiskDefFree(disk); + goto no_memory; + } + + if (VIR_REALLOC_N(def->disks, def->ndisks+1) < 0) { + virDomainDiskDefFree(disk); + goto no_memory; + } + def->disks[def->ndisks++] = disk; + } else if (STREQ(arg, "-no-acpi")) { + def->features &= ~(1 << VIR_DOMAIN_FEATURE_ACPI); + } else if (STREQ(arg, "-no-reboot")) { + def->onReboot = VIR_DOMAIN_LIFECYCLE_DESTROY; + } else if (STREQ(arg, "-no-kvm")) { + def->virtType = VIR_DOMAIN_VIRT_QEMU; + } else if (STREQ(arg, "-nographic")) { + nographics = 1; + } else if (STREQ(arg, "-full-screen")) { + fullscreen = 1; + } else if (STREQ(arg, "-localtime")) { + def->localtime = 1; + } else if (STREQ(arg, "-kernel")) { + WANT_VALUE(); + if (!(def->os.kernel = strdup(val))) + goto no_memory; + } else if (STREQ(arg, "-initrd")) { + WANT_VALUE(); + if (!(def->os.initrd = strdup(val))) + goto no_memory; + } else if (STREQ(arg, "-append")) { + WANT_VALUE(); + if (!(def->os.cmdline = strdup(val))) + goto no_memory; + } else if (STREQ(arg, "-boot")) { + int n, b = 0; + WANT_VALUE(); + for (n = 0 ; val[n] && b < VIR_DOMAIN_BOOT_LAST ; n++) { + if (val[n] == 'a') + def->os.bootDevs[b++] = VIR_DOMAIN_BOOT_FLOPPY; + else if (val[n] == 'c') + def->os.bootDevs[b++] = VIR_DOMAIN_BOOT_DISK; + else if (val[n] == 'd') + def->os.bootDevs[b++] = VIR_DOMAIN_BOOT_CDROM; + else if (val[n] == 'n') + def->os.bootDevs[b++] = VIR_DOMAIN_BOOT_NET; + } + def->os.nBootDevs = b; + } else if (STREQ(arg, "-name")) { + WANT_VALUE(); + if (!(def->name = strdup(val))) + goto no_memory; + } else if (STREQ(arg, "-M")) { + WANT_VALUE(); + if (!(def->os.machine = strdup(val))) + goto no_memory; + } else if (STREQ(arg, "-serial")) { + WANT_VALUE(); + if (STRNEQ(val, "none")) { + virDomainChrDefPtr chr; + if (!(chr = qemuParseCommandLineChr(conn, val))) + goto error; + if (VIR_REALLOC_N(def->serials, def->nserials+1) < 0) { + virDomainChrDefFree(chr); + goto no_memory; + } + chr->dstPort = def->nserials; + def->serials[def->nserials++] = chr; + } + } else if (STREQ(arg, "-parallel")) { + WANT_VALUE(); + if (STRNEQ(val, "none")) { + virDomainChrDefPtr chr; + if (!(chr = qemuParseCommandLineChr(conn, val))) + goto error; + if (VIR_REALLOC_N(def->parallels, def->nparallels+1) < 0) { + virDomainChrDefFree(chr); + goto no_memory; + } + chr->dstPort = def->nparallels; + def->parallels[def->nparallels++] = chr; + } + } else if (STREQ(arg, "-usbdevice")) { + WANT_VALUE(); + if (STREQ(val, "tablet") || + STREQ(val, "mouse")) { + virDomainInputDefPtr input; + if (VIR_ALLOC(input) < 0) + goto no_memory; + input->bus = VIR_DOMAIN_INPUT_BUS_USB; + if (STREQ(val, "tablet")) + input->type = VIR_DOMAIN_INPUT_TYPE_TABLET; + else + input->type = VIR_DOMAIN_INPUT_TYPE_MOUSE; + if (VIR_REALLOC_N(def->inputs, def->ninputs+1) < 0) { + virDomainInputDefFree(input); + goto no_memory; + } + def->inputs[def->ninputs++] = input; + } else if (STRPREFIX(val, "disk:")) { + virDomainDiskDefPtr disk; + if (VIR_ALLOC(disk) < 0) + goto no_memory; + disk->src = strdup(val + strlen("disk:")); + if (!disk->src) { + virDomainDiskDefFree(disk); + goto no_memory; + } + if (STRPREFIX(disk->src, "/dev/")) + disk->type = VIR_DOMAIN_DISK_TYPE_BLOCK; + else + disk->type = VIR_DOMAIN_DISK_TYPE_FILE; + disk->device = VIR_DOMAIN_DISK_DEVICE_DISK; + disk->bus = VIR_DOMAIN_DISK_BUS_USB; + if (!(disk->dst = strdup("sda"))) { + virDomainDiskDefFree(disk); + goto no_memory; + } + if (VIR_REALLOC_N(def->disks, def->ndisks+1) < 0) { + virDomainDiskDefFree(disk); + goto no_memory; + } + def->disks[def->ndisks++] = disk; + } else { + virDomainHostdevDefPtr hostdev; + if (!(hostdev = qemuParseCommandLineUSB(conn, val))) + goto error; + if (VIR_REALLOC_N(def->hostdevs, def->nhostdevs+1) < 0) { + virDomainHostdevDefFree(hostdev); + goto no_memory; + } + def->hostdevs[def->nhostdevs++] = hostdev; + } + } else if (STREQ(arg, "-net")) { + WANT_VALUE(); + if (!STRPREFIX(val, "nic") && STRNEQ(val, "none")) { + virDomainNetDefPtr net; + if (!(net = qemuParseCommandLineNet(conn, val, nnics, nics))) + goto error; + if (VIR_REALLOC_N(def->nets, def->nnets+1) < 0) { + virDomainNetDefFree(net); + goto no_memory; + } + def->nets[def->nnets++] = net; + } + } else if (STREQ(arg, "-drive")) { + virDomainDiskDefPtr disk; + WANT_VALUE(); + if (!(disk = qemuParseCommandLineDisk(conn, val))) + goto error; + if (VIR_REALLOC_N(def->disks, def->ndisks+1) < 0) { + virDomainDiskDefFree(disk); + goto no_memory; + } + def->disks[def->ndisks++] = disk; + } else if (STREQ(arg, "-pcidevice")) { + virDomainHostdevDefPtr hostdev; + WANT_VALUE(); + if (!(hostdev = qemuParseCommandLinePCI(conn, val))) + goto error; + if (VIR_REALLOC_N(def->hostdevs, def->nhostdevs+1) < 0) { + virDomainHostdevDefFree(hostdev); + goto no_memory; + } + def->hostdevs[def->nhostdevs++] = hostdev; + } else if (STREQ(arg, "-soundhw")) { + const char *start; + WANT_VALUE(); + start = val; + while (start) { + const char *tmp = strchr(start, ','); + int type = -1; + if (STRPREFIX(start, "pcspk")) { + type = VIR_DOMAIN_SOUND_MODEL_PCSPK; + } else if (STRPREFIX(start, "sb16")) { + type = VIR_DOMAIN_SOUND_MODEL_SB16; + } else if (STRPREFIX(start, "es1370")) { + type = VIR_DOMAIN_SOUND_MODEL_ES1370; + } else if (STRPREFIX(start, "ac97")) { + type = VIR_DOMAIN_SOUND_MODEL_AC97; + } + + if (type != -1) { + virDomainSoundDefPtr snd; + if (VIR_ALLOC(snd) < 0) + goto no_memory; + snd->model = type; + if (VIR_REALLOC_N(def->sounds, def->nsounds+1) < 0) { + VIR_FREE(snd); + goto no_memory; + } + def->sounds[def->nsounds++] = snd; + } + + start = tmp ? tmp + 1 : NULL; + } + } else if (STREQ(arg, "-bootloader")) { + WANT_VALUE(); + def->os.bootloader = strdup(val); + if (!def->os.bootloader) + goto no_memory; + } else if (STREQ(arg, "-domid")) { + WANT_VALUE(); + /* ignore, generted on the fly */ + } else if (STREQ(arg, "-usb")) { + /* ignore, always added by libvirt */ + } else if (STREQ(arg, "-pidfile")) { + WANT_VALUE(); + /* ignore, used by libvirt as needed */ + } else if (STREQ(arg, "-incoming")) { + WANT_VALUE(); + /* ignore, used via restore/migrate APIs */ + } else if (STREQ(arg, "-monitor")) { + WANT_VALUE(); + /* ignore, used internally by libvirt */ + } else if (STREQ(arg, "-S")) { + /* ignore, always added by libvirt */ + } else { + VIR_WARN(_("unknown QEMU argument '%s' during conversion"), arg); +#if 0 + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("unknown argument '%s'"), arg); + goto error; +#endif + } + } + +#undef WANT_VALUE + + if (!nographics && def->ngraphics == 0) { + virDomainGraphicsDefPtr sdl; + const char *display = qemuFindEnv(progenv, "DISPLAY"); + const char *xauth = qemuFindEnv(progenv, "XAUTHORITY"); + if (VIR_ALLOC(sdl) < 0) + goto no_memory; + sdl->type = VIR_DOMAIN_GRAPHICS_TYPE_SDL; + sdl->data.sdl.fullscreen = fullscreen; + if (display && + !(sdl->data.sdl.display = strdup(display))) { + VIR_FREE(sdl); + goto no_memory; + } + if (xauth && + !(sdl->data.sdl.xauth = strdup(xauth))) { + VIR_FREE(sdl); + goto no_memory; + } + + if (VIR_REALLOC_N(def->graphics, def->ngraphics+1) < 0) { + virDomainGraphicsDefFree(sdl); + goto no_memory; + } + def->graphics[def->ngraphics++] = sdl; + } + + VIR_FREE(nics); + + if (!def->name) { + if (!(def->name = strdup("unnamed"))) + goto no_memory; + } + + return def; + +no_memory: + virReportOOMError(conn); +error: + virDomainDefFree(def); + VIR_FREE(nics); + return NULL; +} + + +virDomainDefPtr qemuParseCommandLineString(virConnectPtr conn, + const char *args) +{ + const char **progenv = NULL; + const char **progargv = NULL; + virDomainDefPtr def = NULL; + int i; + + if (qemuStringToArgvEnv(args, &progenv, &progargv) < 0) + goto cleanup; + + def = qemuParseCommandLine(conn, progenv, progargv); + +cleanup: + for (i = 0 ; progargv && progargv[i] ; i++) + VIR_FREE(progargv[i]); + VIR_FREE(progargv); + + for (i = 0 ; progenv && progenv[i] ; i++) + VIR_FREE(progenv[i]); + VIR_FREE(progenv); + + return def; +} + + /* Called from SAX on parsing errors in the XML. */ static void catchXMLError (void *ctx, const char *msg ATTRIBUTE_UNUSED, ...) diff -r 832729f8dd03 src/qemu_conf.h --- a/src/qemu_conf.h Tue May 19 18:43:30 2009 +0100 +++ b/src/qemu_conf.h Tue May 19 18:43:59 2009 +0100 @@ -1,5 +1,5 @@ /* - * config.h: VM configuration management + * qemu_conf.h: QEMU configuration management * * Copyright (C) 2006, 2007, 2009 Red Hat, Inc. * Copyright (C) 2006 Daniel P. Berrange @@ -135,6 +135,12 @@ int qemudBuildCommandLine int *ntapfds, const char *migrateFrom); +virDomainDefPtr qemuParseCommandLine(virConnectPtr conn, + const char **progenv, + const char **progargv); +virDomainDefPtr qemuParseCommandLineString(virConnectPtr conn, + const char *args); + const char *qemudVirtTypeToString (int type); qemudDomainStatusPtr qemudDomainStatusParseFile(virConnectPtr conn, virCapsPtr caps, diff -r 832729f8dd03 src/qemu_driver.c --- a/src/qemu_driver.c Tue May 19 18:43:30 2009 +0100 +++ b/src/qemu_driver.c Tue May 19 18:43:59 2009 +0100 @@ -3422,6 +3422,30 @@ cleanup: } +static char *qemuDomainXMLFromNative(virConnectPtr conn, + const char *format, + const char *config, + unsigned int flags ATTRIBUTE_UNUSED) { + virDomainDefPtr def = NULL; + char *xml = NULL; + + if (STRNEQ(format, QEMU_CONFIG_FORMAT_ARGV)) { + qemudReportError(conn, NULL, NULL, VIR_ERR_INVALID_ARG, + _("unsupported config type %s"), format); + goto cleanup; + } + + def = qemuParseCommandLineString(conn, config); + if (!def) + goto cleanup; + + xml = virDomainDefFormat(conn, def, VIR_DOMAIN_XML_INACTIVE); + +cleanup: + virDomainDefFree(def); + return xml; +} + static char *qemuDomainXMLToNative(virConnectPtr conn, const char *format, const char *xmlData, @@ -3438,6 +3462,8 @@ static char *qemuDomainXMLToNative(virCo char *ret = NULL; int i; + qemuDriverLock(driver); + if (STRNEQ(format, QEMU_CONFIG_FORMAT_ARGV)) { qemudReportError(conn, NULL, NULL, VIR_ERR_INVALID_ARG, _("unsupported config type %s"), format); @@ -3536,6 +3562,7 @@ static char *qemuDomainXMLToNative(virCo ret = virBufferContentAndReset(&buf); cleanup: + qemuDriverUnlock(driver); for (tmp = retargv ; tmp && *tmp ; tmp++) VIR_FREE(*tmp); VIR_FREE(retargv); @@ -5351,7 +5378,7 @@ static virDriver qemuDriver = { qemudDomainGetSecurityLabel, /* domainGetSecurityLabel */ qemudNodeGetSecurityModel, /* nodeGetSecurityModel */ qemudDomainDumpXML, /* domainDumpXML */ - NULL, /* domainXmlFromNative */ + qemuDomainXMLFromNative, /* domainXmlFromNative */ qemuDomainXMLToNative, /* domainXMLToNative */ qemudListDefinedDomains, /* listDefinedDomains */ qemudNumDefinedDomains, /* numOfDefinedDomains */ diff -r 832729f8dd03 src/vbox/vbox_tmpl.c --- a/src/vbox/vbox_tmpl.c Tue May 19 18:43:30 2009 +0100 +++ b/src/vbox/vbox_tmpl.c Tue May 19 18:43:59 2009 +0100 @@ -1878,7 +1878,7 @@ static char *vboxDomainDumpXML(virDomain if (audioController == AudioControllerType_SB16) { def->sounds[0]->model = VIR_DOMAIN_SOUND_MODEL_SB16; } else if (audioController == AudioControllerType_AC97) { - def->sounds[0]->model = VIR_DOMAIN_SOUND_MODEL_ES97; + def->sounds[0]->model = VIR_DOMAIN_SOUND_MODEL_AC97; } } else { VIR_FREE(def->sounds); @@ -2934,7 +2934,7 @@ static virDomainPtr vboxDomainDefineXML( if (NS_SUCCEEDED(rc)) { if (def->sounds[0]->model == VIR_DOMAIN_SOUND_MODEL_SB16) { audioAdapter->vtbl->SetAudioController(audioAdapter, AudioControllerType_SB16); - } else if (def->sounds[0]->model == VIR_DOMAIN_SOUND_MODEL_ES97) { + } else if (def->sounds[0]->model == VIR_DOMAIN_SOUND_MODEL_AC97) { audioAdapter->vtbl->SetAudioController(audioAdapter, AudioControllerType_AC97); } } -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|