[libvirt] Add support for qemu's guestfwd

This series adds support for a vmchannel using qemu's guestfwd. I'll be following up with support for virtio-serial and RHEL5's vmchannel. A vmchannel is specified as: <vmchannel type='pipe'> <source path='/tmp/vmchannel'/> <target type='guestfwd' address='10.0.2.1' port='4600'/> </vmchannel> There are a couple of minor complications. Firstly, the existing chrdev structure assumes that all front-ends take only a port. There's a bit of code churn adding a second union to this struct to allow for different data for different backends. Secondly, it turns out that the existing syntax for character devices doesn't really work for guestfwd. Specifically it won't allow common options to be added. This is fixed in qemu git with the new -chardev syntax. Consequently, this patch only adds support for guestfwd using -chardev. We add detection for -chardev, and a new internal utility function to output -chardev command lines for all existing character device backends. Matt

Currently a character device's target (it's interface in the guest) has only a single property: port. This patch is in preparation for adding targets which require other properties. Target properties are moved into a union in virDomainChrDef, and a targetType field is added to identify which union member should be used. All current code which touches a virDomainChrDef is updated both to use the new union field, and to populate targetType if necessary. --- src/conf/domain_conf.c | 66 +++++++++++++++++++++++++++++++++++++--------- src/conf/domain_conf.h | 18 +++++++++++- src/esx/esx_vmx.c | 56 +++++++++++++++++++++------------------ src/qemu/qemu_conf.c | 6 +++- src/qemu/qemu_driver.c | 2 + src/uml/uml_conf.c | 12 ++++---- src/uml/uml_driver.c | 2 +- src/vbox/vbox_tmpl.c | 22 ++++++++-------- src/xen/xend_internal.c | 3 ++ src/xen/xm_internal.c | 3 ++ 10 files changed, 129 insertions(+), 61 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index de07e13..0e49482 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -127,6 +127,13 @@ VIR_ENUM_IMPL(virDomainNet, VIR_DOMAIN_NET_TYPE_LAST, "bridge", "internal") +VIR_ENUM_IMPL(virDomainChrTarget, VIR_DOMAIN_CHR_TARGET_TYPE_LAST, + "null", + "monitor", + "parallel", + "serial", + "console") + VIR_ENUM_IMPL(virDomainChr, VIR_DOMAIN_CHR_TYPE_LAST, "null", "vc", @@ -1305,6 +1312,7 @@ virDomainChrDefParseXML(virConnectPtr conn, char *path = NULL; char *mode = NULL; char *protocol = NULL; + const char *targetType = NULL; virDomainChrDefPtr def; if (VIR_ALLOC(def) < 0) { @@ -1318,6 +1326,21 @@ virDomainChrDefParseXML(virConnectPtr conn, else if ((def->type = virDomainChrTypeFromString(type)) < 0) def->type = VIR_DOMAIN_CHR_TYPE_NULL; + targetType = (const char *) node->name; + if (targetType == NULL) { + /* Shouldn't be possible */ + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + "node->name is NULL at %s:%i", + __FILE__, __LINE__); + return NULL; + } + if ((def->targetType = virDomainChrTargetTypeFromString(targetType)) < 0) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unknown target type for character device: %s"), + targetType); + def->targetType = VIR_DOMAIN_CHR_TARGET_TYPE_NULL; + } + cur = node->children; while (cur != NULL) { if (cur->type == XML_ELEMENT_NODE) { @@ -2911,7 +2934,7 @@ static virDomainDefPtr virDomainDefParseXML(virConnectPtr conn, if (!chr) goto error; - chr->dstPort = i; + chr->target.port = i; def->parallels[def->nparallels++] = chr; } VIR_FREE(nodes); @@ -2931,7 +2954,7 @@ static virDomainDefPtr virDomainDefParseXML(virConnectPtr conn, if (!chr) goto error; - chr->dstPort = i; + chr->target.port = i; def->serials[def->nserials++] = chr; } VIR_FREE(nodes); @@ -2943,7 +2966,7 @@ static virDomainDefPtr virDomainDefParseXML(virConnectPtr conn, if (!chr) goto error; - chr->dstPort = 0; + chr->target.port = 0; /* * For HVM console actually created a serial device * while for non-HVM it was a parvirt console @@ -3945,10 +3968,12 @@ static int virDomainChrDefFormat(virConnectPtr conn, virBufferPtr buf, virDomainChrDefPtr def, - const char *name, int flags) { const char *type = virDomainChrTypeToString(def->type); + const char *targetName = virDomainChrTargetTypeToString(def->targetType); + + const char *elementName = targetName; /* Currently always the same */ if (!type) { virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, @@ -3958,8 +3983,8 @@ virDomainChrDefFormat(virConnectPtr conn, /* Compat with legacy <console tty='/dev/pts/5'/> syntax */ virBufferVSprintf(buf, " <%s type='%s'", - name, type); - if (STREQ(name, "console") && + elementName, type); + if (def->targetType == VIR_DOMAIN_CHR_TARGET_TYPE_CONSOLE && def->type == VIR_DOMAIN_CHR_TYPE_PTY && !(flags & VIR_DOMAIN_XML_INACTIVE) && def->data.file.path) { @@ -4034,11 +4059,23 @@ virDomainChrDefFormat(virConnectPtr conn, break; } - virBufferVSprintf(buf, " <target port='%d'/>\n", - def->dstPort); + switch (def->targetType) { + case VIR_DOMAIN_CHR_TARGET_TYPE_PARALLEL: + case VIR_DOMAIN_CHR_TARGET_TYPE_SERIAL: + case VIR_DOMAIN_CHR_TARGET_TYPE_CONSOLE: + virBufferVSprintf(buf, " <target port='%d'/>\n", + def->target.port); + break; + + default: + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unexpected character destination type %d"), + def->targetType); + return -1; + } virBufferVSprintf(buf, " </%s>\n", - name); + elementName); return 0; } @@ -4485,21 +4522,24 @@ char *virDomainDefFormat(virConnectPtr conn, goto cleanup; for (n = 0 ; n < def->nserials ; n++) - if (virDomainChrDefFormat(conn, &buf, def->serials[n], "serial", flags) < 0) + if (virDomainChrDefFormat(conn, &buf, def->serials[n], flags) < 0) goto cleanup; for (n = 0 ; n < def->nparallels ; n++) - if (virDomainChrDefFormat(conn, &buf, def->parallels[n], "parallel", flags) < 0) + if (virDomainChrDefFormat(conn, &buf, def->parallels[n], flags) < 0) goto cleanup; /* If there's a PV console that's preferred.. */ if (def->console) { - if (virDomainChrDefFormat(conn, &buf, def->console, "console", flags) < 0) + if (virDomainChrDefFormat(conn, &buf, def->console, flags) < 0) goto cleanup; } else if (def->nserials != 0) { /* ..else for legacy compat duplicate the first serial device as a * console */ - if (virDomainChrDefFormat(conn, &buf, def->serials[0], "console", flags) < 0) + virDomainChrDef console; + memcpy(&console, def->serials[0], sizeof(console)); + console.targetType = VIR_DOMAIN_CHR_TARGET_TYPE_CONSOLE; + if (virDomainChrDefFormat(conn, &buf, &console, flags) < 0) goto cleanup; } diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 389e259..3bfa84d 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -211,7 +211,17 @@ virNetHasValidPciAddr(virDomainNetDefPtr def) return def->pci_addr.domain || def->pci_addr.bus || def->pci_addr.slot; } -enum virDomainChrSrcType { +enum virDomainChrTargetType { + VIR_DOMAIN_CHR_TARGET_TYPE_NULL = 0, + VIR_DOMAIN_CHR_TARGET_TYPE_MONITOR, + VIR_DOMAIN_CHR_TARGET_TYPE_PARALLEL, + VIR_DOMAIN_CHR_TARGET_TYPE_SERIAL, + VIR_DOMAIN_CHR_TARGET_TYPE_CONSOLE, + + VIR_DOMAIN_CHR_TARGET_TYPE_LAST +}; + +enum virDomainChrType { VIR_DOMAIN_CHR_TYPE_NULL, VIR_DOMAIN_CHR_TYPE_VC, VIR_DOMAIN_CHR_TYPE_PTY, @@ -236,7 +246,10 @@ enum virDomainChrTcpProtocol { typedef struct _virDomainChrDef virDomainChrDef; typedef virDomainChrDef *virDomainChrDefPtr; struct _virDomainChrDef { - int dstPort; + int targetType; + union { + int port; /* parallel, serial, console */ + } target; int type; union { @@ -808,6 +821,7 @@ VIR_ENUM_DECL(virDomainDiskBus) VIR_ENUM_DECL(virDomainDiskCache) VIR_ENUM_DECL(virDomainFS) VIR_ENUM_DECL(virDomainNet) +VIR_ENUM_DECL(virDomainChrTarget) VIR_ENUM_DECL(virDomainChr) VIR_ENUM_DECL(virDomainSoundModel) VIR_ENUM_DECL(virDomainWatchdogModel) diff --git a/src/esx/esx_vmx.c b/src/esx/esx_vmx.c index 8e2bdec..4d498ef 100644 --- a/src/esx/esx_vmx.c +++ b/src/esx/esx_vmx.c @@ -319,7 +319,7 @@ def->nets[0]... serial0.startConnected = "true" # defaults to "true" def->serials[0]... -->dstPort = <port> +->target.port = <port> ## serials: device ############################################################# @@ -378,7 +378,7 @@ def->serials[0]... parallel0.startConnected = "true" # defaults to "true" def->parallels[0]... -->dstPort = <port> +->target.port = <port> ## parallels: device ############################################################# @@ -1868,6 +1868,8 @@ esxVMX_ParseSerial(virConnectPtr conn, esxVI_Context *ctx, virConfPtr conf, goto failure; } + (*def)->targetType = VIR_DOMAIN_CHR_TARGET_TYPE_SERIAL; + snprintf(prefix, sizeof(prefix), "serial%d", port); ESX_BUILD_VMX_NAME(present); @@ -1904,13 +1906,13 @@ esxVMX_ParseSerial(virConnectPtr conn, esxVI_Context *ctx, virConfPtr conf, /* Setup virDomainChrDef */ if (STRCASEEQ(fileType, "device")) { - (*def)->dstPort = port; + (*def)->target.port = port; (*def)->type = VIR_DOMAIN_CHR_TYPE_DEV; (*def)->data.file.path = fileName; fileName = NULL; } else if (STRCASEEQ(fileType, "file")) { - (*def)->dstPort = port; + (*def)->target.port = port; (*def)->type = VIR_DOMAIN_CHR_TYPE_FILE; (*def)->data.file.path = esxVMX_ParseFileName(conn, ctx, fileName, datastoreName, @@ -1924,7 +1926,7 @@ esxVMX_ParseSerial(virConnectPtr conn, esxVI_Context *ctx, virConfPtr conf, * FIXME: Differences between client/server and VM/application pipes * not representable in domain XML form */ - (*def)->dstPort = port; + (*def)->target.port = port; (*def)->type = VIR_DOMAIN_CHR_TYPE_PIPE; (*def)->data.file.path = fileName; @@ -1990,6 +1992,8 @@ esxVMX_ParseParallel(virConnectPtr conn, esxVI_Context *ctx, virConfPtr conf, goto failure; } + (*def)->targetType = VIR_DOMAIN_CHR_TARGET_TYPE_PARALLEL; + snprintf(prefix, sizeof(prefix), "parallel%d", port); ESX_BUILD_VMX_NAME(present); @@ -2026,13 +2030,13 @@ esxVMX_ParseParallel(virConnectPtr conn, esxVI_Context *ctx, virConfPtr conf, /* Setup virDomainChrDef */ if (STRCASEEQ(fileType, "device")) { - (*def)->dstPort = port; + (*def)->target.port = port; (*def)->type = VIR_DOMAIN_CHR_TYPE_DEV; (*def)->data.file.path = fileName; fileName = NULL; } else if (STRCASEEQ(fileType, "file")) { - (*def)->dstPort = port; + (*def)->target.port = port; (*def)->type = VIR_DOMAIN_CHR_TYPE_FILE; (*def)->data.file.path = esxVMX_ParseFileName(conn, ctx, fileName, datastoreName, @@ -2703,9 +2707,9 @@ esxVMX_FormatSerial(virConnectPtr conn, esxVI_Context *ctx, { char *fileName = NULL; - if (def->dstPort < 0 || def->dstPort > 3) { + if (def->target.port < 0 || def->target.port > 3) { ESX_ERROR(conn, VIR_ERR_INTERNAL_ERROR, - "Serial port index %d out of [0..3] range", def->dstPort); + "Serial port index %d out of [0..3] range", def->target.port); return -1; } @@ -2716,20 +2720,20 @@ esxVMX_FormatSerial(virConnectPtr conn, esxVI_Context *ctx, return -1; } - virBufferVSprintf(buffer, "serial%d.present = \"true\"\n", def->dstPort); + virBufferVSprintf(buffer, "serial%d.present = \"true\"\n", def->target.port); /* def:type -> vmx:fileType and def:data.file.path -> vmx:fileName */ switch (def->type) { case VIR_DOMAIN_CHR_TYPE_DEV: virBufferVSprintf(buffer, "serial%d.fileType = \"device\"\n", - def->dstPort); + def->target.port); virBufferVSprintf(buffer, "serial%d.fileName = \"%s\"\n", - def->dstPort, def->data.file.path); + def->target.port, def->data.file.path); break; case VIR_DOMAIN_CHR_TYPE_FILE: virBufferVSprintf(buffer, "serial%d.fileType = \"file\"\n", - def->dstPort); + def->target.port); fileName = esxVMX_FormatFileName(conn, ctx, def->data.file.path); @@ -2738,22 +2742,22 @@ esxVMX_FormatSerial(virConnectPtr conn, esxVI_Context *ctx, } virBufferVSprintf(buffer, "serial%d.fileName = \"%s\"\n", - def->dstPort, fileName); + def->target.port, fileName); VIR_FREE(fileName); break; case VIR_DOMAIN_CHR_TYPE_PIPE: virBufferVSprintf(buffer, "serial%d.fileType = \"pipe\"\n", - def->dstPort); + def->target.port); /* FIXME: Based on VI Client GUI default */ virBufferVSprintf(buffer, "serial%d.pipe.endPoint = \"client\"\n", - def->dstPort); + def->target.port); /* FIXME: Based on VI Client GUI default */ virBufferVSprintf(buffer, "serial%d.tryNoRxLoss = \"false\"\n", - def->dstPort); + def->target.port); virBufferVSprintf(buffer, "serial%d.fileName = \"%s\"\n", - def->dstPort, def->data.file.path); + def->target.port, def->data.file.path); break; default: @@ -2766,7 +2770,7 @@ esxVMX_FormatSerial(virConnectPtr conn, esxVI_Context *ctx, /* vmx:yieldOnMsrRead */ /* FIXME: Based on VI Client GUI default */ virBufferVSprintf(buffer, "serial%d.yieldOnMsrRead = \"true\"\n", - def->dstPort); + def->target.port); return 0; } @@ -2779,9 +2783,9 @@ esxVMX_FormatParallel(virConnectPtr conn, esxVI_Context *ctx, { char *fileName = NULL; - if (def->dstPort < 0 || def->dstPort > 2) { + if (def->target.port < 0 || def->target.port > 2) { ESX_ERROR(conn, VIR_ERR_INTERNAL_ERROR, - "Parallel port index %d out of [0..2] range", def->dstPort); + "Parallel port index %d out of [0..2] range", def->target.port); return -1; } @@ -2792,20 +2796,20 @@ esxVMX_FormatParallel(virConnectPtr conn, esxVI_Context *ctx, return -1; } - virBufferVSprintf(buffer, "parallel%d.present = \"true\"\n", def->dstPort); + virBufferVSprintf(buffer, "parallel%d.present = \"true\"\n", def->target.port); /* def:type -> vmx:fileType and def:data.file.path -> vmx:fileName */ switch (def->type) { case VIR_DOMAIN_CHR_TYPE_DEV: virBufferVSprintf(buffer, "parallel%d.fileType = \"device\"\n", - def->dstPort); + def->target.port); virBufferVSprintf(buffer, "parallel%d.fileName = \"%s\"\n", - def->dstPort, def->data.file.path); + def->target.port, def->data.file.path); break; case VIR_DOMAIN_CHR_TYPE_FILE: virBufferVSprintf(buffer, "parallel%d.fileType = \"file\"\n", - def->dstPort); + def->target.port); fileName = esxVMX_FormatFileName(conn, ctx, def->data.file.path); @@ -2814,7 +2818,7 @@ esxVMX_FormatParallel(virConnectPtr conn, esxVI_Context *ctx, } virBufferVSprintf(buffer, "parallel%d.fileName = \"%s\"\n", - def->dstPort, fileName); + def->target.port, fileName); VIR_FREE(fileName); break; diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index a095cb7..767965c 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -3374,7 +3374,8 @@ virDomainDefPtr qemuParseCommandLine(virConnectPtr conn, virDomainChrDefFree(chr); goto no_memory; } - chr->dstPort = def->nserials; + chr->targetType = VIR_DOMAIN_CHR_TARGET_TYPE_SERIAL; + chr->target.port = def->nserials; def->serials[def->nserials++] = chr; } } else if (STREQ(arg, "-parallel")) { @@ -3387,7 +3388,8 @@ virDomainDefPtr qemuParseCommandLine(virConnectPtr conn, virDomainChrDefFree(chr); goto no_memory; } - chr->dstPort = def->nparallels; + chr->targetType = VIR_DOMAIN_CHR_TARGET_TYPE_PARALLEL; + chr->target.port = def->nparallels; def->parallels[def->nparallels++] = chr; } } else if (STREQ(arg, "-usbdevice")) { diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index a91c60f..f6af383 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -1921,6 +1921,8 @@ qemuPrepareMonitorChr(virConnectPtr conn, virDomainChrDefPtr monitor_chr, const char *vm) { + monitor_chr->targetType = VIR_DOMAIN_CHR_TARGET_TYPE_MONITOR; + monitor_chr->type = VIR_DOMAIN_CHR_TYPE_UNIX; monitor_chr->data.nix.listen = 1; diff --git a/src/uml/uml_conf.c b/src/uml/uml_conf.c index 1c1db61..0ace58f 100644 --- a/src/uml/uml_conf.c +++ b/src/uml/uml_conf.c @@ -279,21 +279,21 @@ umlBuildCommandLineChr(virConnectPtr conn, switch (def->type) { case VIR_DOMAIN_CHR_TYPE_NULL: - if (virAsprintf(&ret, "%s%d=null", dev, def->dstPort) < 0) { + if (virAsprintf(&ret, "%s%d=null", dev, def->target.port) < 0) { virReportOOMError(conn); return NULL; } break; case VIR_DOMAIN_CHR_TYPE_PTY: - if (virAsprintf(&ret, "%s%d=pts", dev, def->dstPort) < 0) { + if (virAsprintf(&ret, "%s%d=pts", dev, def->target.port) < 0) { virReportOOMError(conn); return NULL; } break; case VIR_DOMAIN_CHR_TYPE_DEV: - if (virAsprintf(&ret, "%s%d=tty:%s", dev, def->dstPort, + if (virAsprintf(&ret, "%s%d=tty:%s", dev, def->target.port, def->data.file.path) < 0) { virReportOOMError(conn); return NULL; @@ -301,7 +301,7 @@ umlBuildCommandLineChr(virConnectPtr conn, break; case VIR_DOMAIN_CHR_TYPE_STDIO: - if (virAsprintf(&ret, "%s%d=fd:0,fd:1", dev, def->dstPort) < 0) { + if (virAsprintf(&ret, "%s%d=fd:0,fd:1", dev, def->target.port) < 0) { virReportOOMError(conn); return NULL; } @@ -314,7 +314,7 @@ umlBuildCommandLineChr(virConnectPtr conn, return NULL; } - if (virAsprintf(&ret, "%s%d=port:%s", dev, def->dstPort, + if (virAsprintf(&ret, "%s%d=port:%s", dev, def->target.port, def->data.tcp.service) < 0) { virReportOOMError(conn); return NULL; @@ -502,7 +502,7 @@ int umlBuildCommandLine(virConnectPtr conn, virDomainChrDefPtr chr = NULL; char *ret; for (j = 0 ; j < vm->def->nserials ; j++) - if (vm->def->serials[j]->dstPort == i) + if (vm->def->serials[j]->target.port == i) chr = vm->def->serials[j]; if (chr) ret = umlBuildCommandLineChr(conn, chr, "ssl"); diff --git a/src/uml/uml_driver.c b/src/uml/uml_driver.c index 80cf477..c94ad93 100644 --- a/src/uml/uml_driver.c +++ b/src/uml/uml_driver.c @@ -169,7 +169,7 @@ umlIdentifyOneChrPTY(virConnectPtr conn, char *cmd; char *res = NULL; int retries = 0; - if (virAsprintf(&cmd, "config %s%d", dev, def->dstPort) < 0) { + if (virAsprintf(&cmd, "config %s%d", dev, def->target.port) < 0) { virReportOOMError(conn); return -1; } diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c index 4741c57..e83368e 100644 --- a/src/vbox/vbox_tmpl.c +++ b/src/vbox/vbox_tmpl.c @@ -2415,9 +2415,9 @@ static char *vboxDomainDumpXML(virDomainPtr dom, int flags) { serialPort->vtbl->GetIRQ(serialPort, &IRQ); serialPort->vtbl->GetIOBase(serialPort, &IOBase); if ((IRQ == 4) && (IOBase == 1016)) { - def->serials[serialPortIncCount]->dstPort = 0; + def->serials[serialPortIncCount]->target.port = 0; } else if ((IRQ == 3) && (IOBase == 760)) { - def->serials[serialPortIncCount]->dstPort = 1; + def->serials[serialPortIncCount]->target.port = 1; } serialPort->vtbl->GetPath(serialPort, &pathUtf16); @@ -2481,9 +2481,9 @@ static char *vboxDomainDumpXML(virDomainPtr dom, int flags) { parallelPort->vtbl->GetIRQ(parallelPort, &IRQ); parallelPort->vtbl->GetIOBase(parallelPort, &IOBase); if ((IRQ == 7) && (IOBase == 888)) { - def->parallels[parallelPortIncCount]->dstPort = 0; + def->parallels[parallelPortIncCount]->target.port = 0; } else if ((IRQ == 5) && (IOBase == 632)) { - def->parallels[parallelPortIncCount]->dstPort = 1; + def->parallels[parallelPortIncCount]->target.port = 1; } def->parallels[parallelPortIncCount]->type = VIR_DOMAIN_CHR_TYPE_FILE; @@ -3505,7 +3505,7 @@ static virDomainPtr vboxDomainDefineXML(virConnectPtr conn, const char *xml) { ISerialPort *serialPort = NULL; DEBUG("SerialPort(%d): Type: %d", i, def->serials[i]->type); - DEBUG("SerialPort(%d): dstPort: %d", i, def->serials[i]->dstPort); + DEBUG("SerialPort(%d): target.port: %d", i, def->serials[i]->target.port); machine->vtbl->GetSerialPort(machine, i, &serialPort); if (serialPort) { @@ -3520,17 +3520,17 @@ static virDomainPtr vboxDomainDefineXML(virConnectPtr conn, const char *xml) { * TODO: make this more flexible */ /* TODO: to improve the libvirt XMl handling so - * that def->serials[i]->dstPort shows real port + * that def->serials[i]->target.port shows real port * and not always start at 0 */ if (def->serials[i]->type == VIR_DOMAIN_CHR_TYPE_DEV) { serialPort->vtbl->SetPath(serialPort, pathUtf16); - if (def->serials[i]->dstPort == 0) { + if (def->serials[i]->target.port == 0) { serialPort->vtbl->SetIRQ(serialPort, 4); serialPort->vtbl->SetIOBase(serialPort, 1016); DEBUG(" serialPort-%d irq: %d, iobase 0x%x, path: %s", i, 4, 1016, def->serials[i]->data.file.path); - } else if (def->serials[i]->dstPort == 1) { + } else if (def->serials[i]->target.port == 1) { serialPort->vtbl->SetIRQ(serialPort, 3); serialPort->vtbl->SetIOBase(serialPort, 760); DEBUG(" serialPort-%d irq: %d, iobase 0x%x, path: %s", @@ -3539,12 +3539,12 @@ static virDomainPtr vboxDomainDefineXML(virConnectPtr conn, const char *xml) { serialPort->vtbl->SetHostMode(serialPort, PortMode_HostDevice); } else if (def->serials[i]->type == VIR_DOMAIN_CHR_TYPE_PIPE) { serialPort->vtbl->SetPath(serialPort, pathUtf16); - if (def->serials[i]->dstPort == 0) { + if (def->serials[i]->target.port == 0) { serialPort->vtbl->SetIRQ(serialPort, 4); serialPort->vtbl->SetIOBase(serialPort, 1016); DEBUG(" serialPort-%d irq: %d, iobase 0x%x, path: %s", i, 4, 1016, def->serials[i]->data.file.path); - } else if (def->serials[i]->dstPort == 1) { + } else if (def->serials[i]->target.port == 1) { serialPort->vtbl->SetIRQ(serialPort, 3); serialPort->vtbl->SetIOBase(serialPort, 760); DEBUG(" serialPort-%d irq: %d, iobase 0x%x, path: %s", @@ -3585,7 +3585,7 @@ static virDomainPtr vboxDomainDefineXML(virConnectPtr conn, const char *xml) { IParallelPort *parallelPort = NULL; DEBUG("ParallelPort(%d): Type: %d", i, def->parallels[i]->type); - DEBUG("ParallelPort(%d): dstPort: %d", i, def->parallels[i]->dstPort); + DEBUG("ParallelPort(%d): target.port: %d", i, def->parallels[i]->target.port); machine->vtbl->GetParallelPort(machine, i, ¶llelPort); if (parallelPort) { diff --git a/src/xen/xend_internal.c b/src/xen/xend_internal.c index d3ab019..b8790d3 100644 --- a/src/xen/xend_internal.c +++ b/src/xen/xend_internal.c @@ -2569,6 +2569,7 @@ xenDaemonParseSxpr(virConnectPtr conn, virDomainChrDefFree(chr); goto no_memory; } + chr->targetType = VIR_DOMAIN_CHR_TARGET_TYPE_SERIAL; def->serials[def->nserials++] = chr; } tmp = sexpr_node(root, "domain/image/hvm/parallel"); @@ -2581,12 +2582,14 @@ xenDaemonParseSxpr(virConnectPtr conn, virDomainChrDefFree(chr); goto no_memory; } + chr->targetType = VIR_DOMAIN_CHR_TARGET_TYPE_PARALLEL; def->parallels[def->nparallels++] = chr; } } else { /* Fake a paravirt console, since that's not in the sexpr */ if (!(def->console = xenDaemonParseSxprChar(conn, "pty", tty))) goto error; + def->console->targetType = VIR_DOMAIN_CHR_TARGET_TYPE_CONSOLE; } VIR_FREE(tty); diff --git a/src/xen/xm_internal.c b/src/xen/xm_internal.c index 732b2d3..ad97db2 100644 --- a/src/xen/xm_internal.c +++ b/src/xen/xm_internal.c @@ -1415,6 +1415,7 @@ xenXMDomainConfigParse(virConnectPtr conn, virConfPtr conf) { virDomainChrDefFree(chr); goto no_memory; } + chr->targetType = VIR_DOMAIN_CHR_TARGET_TYPE_PARALLEL; def->parallels[0] = chr; def->nparallels++; chr = NULL; @@ -1431,12 +1432,14 @@ xenXMDomainConfigParse(virConnectPtr conn, virConfPtr conf) { virDomainChrDefFree(chr); goto no_memory; } + chr->targetType = VIR_DOMAIN_CHR_TARGET_TYPE_SERIAL; def->serials[0] = chr; def->nserials++; } } else { if (!(def->console = xenDaemonParseSxprChar(conn, "pty", NULL))) goto cleanup; + def->console->targetType = VIR_DOMAIN_CHR_TARGET_TYPE_CONSOLE; } if (hvm) { -- 1.6.2.5

--- src/qemu/qemu_conf.c | 2 ++ src/qemu/qemu_conf.h | 1 + 2 files changed, 3 insertions(+), 0 deletions(-) diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 767965c..c4690b2 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -875,6 +875,8 @@ static unsigned int qemudComputeCmdFlags(const char *help, flags |= QEMUD_CMD_FLAG_PCIDEVICE; if (strstr(help, "-mem-path")) flags |= QEMUD_CMD_FLAG_MEM_PATH; + if (strstr(help, "-chardev")) + flags |= QEMUD_CMD_FLAG_CHARDEV; if (version >= 9000) flags |= QEMUD_CMD_FLAG_VNC_COLON; diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index f9a970f..4aa764b 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -71,6 +71,7 @@ enum qemud_cmd_flags { QEMUD_CMD_FLAG_DRIVE_SERIAL = (1 << 19), /* -driver serial= available */ QEMUD_CMD_FLAG_XEN_DOMID = (1 << 20), /* -xen-domid (new style xen integration) */ QEMUD_CMD_FLAG_MIGRATE_QEMU_UNIX = (1 << 21), /* Does qemu support unix domain sockets for migration? */ + QEMUD_CMD_FLAG_CHARDEV = (1 << 22), /* Is the new -chardev arg available */ }; /* Main driver state */ -- 1.6.2.5

Note that, on its own, this patch will generate a warning about an unused static function. --- src/qemu/qemu_conf.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 87 insertions(+), 0 deletions(-) diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index c4690b2..3f0fbc1 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -1386,6 +1386,93 @@ qemuBuildHostNetStr(virConnectPtr conn, return 0; } +/* This function outputs a -chardev command line option which describes only the + * host side of the character device */ +static int qemudBuildCommandLineChrDevChardevStr(virDomainChrDefPtr dev, + const char *const id, + char *buf, + int buflen) +{ + switch(dev->type) { + case VIR_DOMAIN_CHR_TYPE_NULL: + if (snprintf(buf, buflen, "null,id=%s", id) >= buflen) + return -1; + break; + + case VIR_DOMAIN_CHR_TYPE_VC: + if (snprintf(buf, buflen, "vc,id=%s", id) >= buflen) + return -1; + break; + + case VIR_DOMAIN_CHR_TYPE_PTY: + if (snprintf(buf, buflen, "pty,id=%s", id) >= buflen) + return -1; + break; + + case VIR_DOMAIN_CHR_TYPE_DEV: + if (snprintf(buf, buflen, "tty,id=%s,path=%s", + id, dev->data.file.path) >= buflen) + return -1; + break; + + case VIR_DOMAIN_CHR_TYPE_FILE: + if (snprintf(buf, buflen, "file,id=%s,path=%s", + id, dev->data.file.path) >= buflen) + return -1; + break; + + case VIR_DOMAIN_CHR_TYPE_PIPE: + if (snprintf(buf, buflen, "pipe,id=%s,path=%s", + id, dev->data.file.path) >= buflen) + return -1; + break; + + case VIR_DOMAIN_CHR_TYPE_STDIO: + if (snprintf(buf, buflen, "stdio,id=%s", id) >= buflen) + return -1; + break; + + case VIR_DOMAIN_CHR_TYPE_UDP: + if (snprintf(buf, buflen, + "udp,id=%s,host=%s,port=%s,localaddr=%s,localport=%s", + id, + dev->data.udp.connectHost, + dev->data.udp.connectService, + dev->data.udp.bindHost, + dev->data.udp.bindService) >= buflen) + return -1; + break; + + case VIR_DOMAIN_CHR_TYPE_TCP: + { + bool telnet = + dev->data.tcp.protocol == VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNET; + + if (snprintf(buf, buflen, + "socket,id=%s,host=%s,port=%s%s%s", + id, + dev->data.tcp.host, + dev->data.tcp.service, + telnet ? ",telnet" : "", + dev->data.tcp.listen ? ",server,nowait" : "") >= buflen) + return -1; + break; + } + + case VIR_DOMAIN_CHR_TYPE_UNIX: + if (snprintf(buf, buflen, + "socket,id=%s,path=%s%s", + id, + dev->data.nix.path, + dev->data.nix.listen ? ",server,nowait" : "") >= buflen) + return -1; + break; + } + + return 0; +} + +/* This function outputs an all-in-one character device command line option */ static int qemudBuildCommandLineChrDevStr(virDomainChrDefPtr dev, char *buf, int buflen) -- 1.6.2.5

This patch allows the following to be specified in a qemu domain: <vmchannel type='pipe'> <source path='/tmp/vmchannel'/> <target type='guestfwd' address='10.0.2.1' port='4600'/> </vmchannel> This will output the following on the qemu command line: -chardev pipe,id=vmchannel0,path=/tmp/vmchannel \ -net user,guestfwd=tcp:10.0.2.1:4600-chardev:vmchannel0 --- docs/schemas/domain.rng | 92 ++++++++---- proxy/Makefile.am | 1 + src/conf/domain_conf.c | 157 +++++++++++++++++++- src/conf/domain_conf.h | 6 + src/qemu/qemu_conf.c | 55 +++++++ .../qemuxml2argv-vmchannel-guestfwd.args | 1 + .../qemuxml2argv-vmchannel-guestfwd.xml | 26 ++++ tests/qemuxml2argvtest.c | 4 +- tests/qemuxml2xmltest.c | 1 + 9 files changed, 305 insertions(+), 38 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-vmchannel-guestfwd.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-vmchannel-guestfwd.xml diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng index 0a6ab61..54bbdd8 100644 --- a/docs/schemas/domain.rng +++ b/docs/schemas/domain.rng @@ -930,6 +930,19 @@ definition doesn't fully specify the constraints on this node. --> <define name="qemucdev"> + <ref name="qemucdevSrcType"/> + <interleave> + <ref name="qemucdevSrcDef"/> + <optional> + <element name="target"> + <optional> + <attribute name="port"/> + </optional> + </element> + </optional> + </interleave> + </define> + <define name="qemucdevSrcType"> <attribute name="type"> <choice> <value>dev</value> @@ -944,43 +957,36 @@ <value>pty</value> </choice> </attribute> - <interleave> - <optional> - <oneOrMore> - <element name="source"> - <optional> - <attribute name="mode"/> - </optional> - <optional> - <attribute name="path"/> - </optional> - <optional> - <attribute name="host"/> - </optional> - <optional> - <attribute name="service"/> - </optional> - <optional> - <attribute name="wiremode"/> - </optional> - </element> - </oneOrMore> - </optional> - <optional> - <element name="protocol"> + </define> + <define name="qemucdevSrcDef"> + <optional> + <oneOrMore> + <element name="source"> <optional> - <attribute name="type"/> + <attribute name="mode"/> </optional> - </element> - </optional> - <optional> - <element name="target"> <optional> - <attribute name="port"/> + <attribute name="path"/> + </optional> + <optional> + <attribute name="host"/> + </optional> + <optional> + <attribute name="service"/> + </optional> + <optional> + <attribute name="wiremode"/> </optional> </element> - </optional> - </interleave> + </oneOrMore> + </optional> + <optional> + <element name="protocol"> + <optional> + <attribute name="type"/> + </optional> + </element> + </optional> </define> <!-- The description for a console @@ -1044,6 +1050,27 @@ <ref name="qemucdev"/> </element> </define> + <define name="vmchannel"> + <element name="vmchannel"> + <ref name="qemucdevSrcType"/> + <interleave> + <ref name="qemucdevSrcDef"/> + <element name="target"> + <attribute name="type"> + <choice> + <value>guestfwd</value> + </choice> + </attribute> + <optional> + <attribute name="address"/> + </optional> + <optional> + <attribute name="port"/> + </optional> + </element> + </interleave> + </element> + </define> <define name="input"> <element name="input"> <attribute name="type"> @@ -1158,6 +1185,7 @@ <ref name="console"/> <ref name="parallel"/> <ref name="serial"/> + <ref name="vmchannel"/> </choice> </zeroOrMore> <optional> diff --git a/proxy/Makefile.am b/proxy/Makefile.am index 3e0050b..42f6a81 100644 --- a/proxy/Makefile.am +++ b/proxy/Makefile.am @@ -17,6 +17,7 @@ libvirt_proxy_SOURCES = libvirt_proxy.c \ @top_srcdir@/src/util/buf.c \ @top_srcdir@/src/util/logging.c \ @top_srcdir@/src/util/memory.c \ + @top_srcdir@/src/util/network.c \ @top_srcdir@/src/util/threads.c \ @top_srcdir@/src/util/util.c \ @top_srcdir@/src/util/uuid.c \ diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 0e49482..7708a75 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -40,6 +40,7 @@ #include "buf.h" #include "c-ctype.h" #include "logging.h" +#include "network.h" #define VIR_FROM_THIS VIR_FROM_DOMAIN @@ -132,7 +133,8 @@ VIR_ENUM_IMPL(virDomainChrTarget, VIR_DOMAIN_CHR_TARGET_TYPE_LAST, "monitor", "parallel", "serial", - "console") + "console", + "guestfwd") VIR_ENUM_IMPL(virDomainChr, VIR_DOMAIN_CHR_TYPE_LAST, "null", @@ -412,6 +414,12 @@ void virDomainChrDefFree(virDomainChrDefPtr def) if (!def) return; + switch (def->targetType) { + case VIR_DOMAIN_CHR_TARGET_TYPE_GUESTFWD: + VIR_FREE(def->target.addr); + break; + } + switch (def->type) { case VIR_DOMAIN_CHR_TYPE_PTY: case VIR_DOMAIN_CHR_TYPE_DEV: @@ -1335,9 +1343,13 @@ virDomainChrDefParseXML(virConnectPtr conn, return NULL; } if ((def->targetType = virDomainChrTargetTypeFromString(targetType)) < 0) { - virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("unknown target type for character device: %s"), - targetType); + /* vmchannel is handled below */ + if(STRNEQ(targetType, "vmchannel")) { + virDomainReportError(conn, VIR_ERR_INVALID_DOMAIN, + _("unknown target type for character device: %s"), + targetType); + return NULL; + } def->targetType = VIR_DOMAIN_CHR_TARGET_TYPE_NULL; } @@ -1386,6 +1398,89 @@ virDomainChrDefParseXML(virConnectPtr conn, } else if (xmlStrEqual(cur->name, BAD_CAST "protocol")) { if (protocol == NULL) protocol = virXMLPropString(cur, "type"); + } else if (xmlStrEqual(cur->name, BAD_CAST "target")) { + /* If target type isn't set yet, expect it to be set here */ + if(def->targetType == VIR_DOMAIN_CHR_TARGET_TYPE_NULL) { + targetType = virXMLPropString(cur, "type"); + if(targetType == NULL) { + virDomainReportError(conn, VIR_ERR_INVALID_DOMAIN, + _("character device does not " + "define a recognised target type")); + goto error; + } + if ((def->targetType = + virDomainChrTargetTypeFromString(targetType)) < 0) + { + virDomainReportError(conn, VIR_ERR_INVALID_DOMAIN, + _("unknown target type for " + "character device: %s"), + targetType); + goto error; + } + } + + switch (def->targetType) { + case VIR_DOMAIN_CHR_TARGET_TYPE_PARALLEL: + case VIR_DOMAIN_CHR_TARGET_TYPE_SERIAL: + case VIR_DOMAIN_CHR_TARGET_TYPE_CONSOLE: + { + const char *portStr = virXMLPropString(cur, "port"); + if(portStr == NULL) { + /* Not required. It will be assigned automatically + * later */ + break; + } + + unsigned int port; + if(virStrToLong_ui(portStr, NULL, 10, &port) < 0) { + virDomainReportError(conn, VIR_ERR_INVALID_DOMAIN, + _("Invalid port number: %s"), + portStr); + goto error; + } + break; + } + + case VIR_DOMAIN_CHR_TARGET_TYPE_GUESTFWD: + { + const char *addrStr = virXMLPropString(cur, "address"); + const char *portStr = virXMLPropString(cur, "port"); + + if(addrStr == NULL) { + virDomainReportError(conn, VIR_ERR_INVALID_DOMAIN, + _("guestfwd vmchannel does " + "not define a target " + "address")); + goto error; + } + if(VIR_ALLOC(def->target.addr) < 0) { + virReportOOMError(conn); + goto error; + } + if(virSocketParseAddr(addrStr, def->target.addr, 0) < 0) + { + virDomainReportError(conn, VIR_ERR_INVALID_DOMAIN, + _("%s is not a valid address"), + addrStr); + goto error; + } + + if(portStr == NULL) { + virDomainReportError(conn, VIR_ERR_INVALID_DOMAIN, + _("guestfwd vmchannel does " + "not define a target port")); + goto error; + } + unsigned int port; + if(virStrToLong_ui(portStr, NULL, 10, &port) < 0) { + virDomainReportError(conn, VIR_ERR_INVALID_DOMAIN, + _("Invalid port number: %s"), + portStr); + goto error; + } + virSocketSetPort(def->target.addr, port); + } + } } } cur = cur->next; @@ -2987,6 +3082,25 @@ static virDomainDefPtr virDomainDefParseXML(virConnectPtr conn, } } + if ((n = virXPathNodeSet(conn, "./devices/vmchannel", ctxt, &nodes)) < 0) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("cannot extract vmchannel devices")); + goto error; + } + if (n && VIR_ALLOC_N(def->vmchannels, n) < 0) + goto no_memory; + + for (i = 0 ; i < n ; i++) { + virDomainChrDefPtr chr = virDomainChrDefParseXML(conn, + nodes[i], + flags); + if (!chr) + goto error; + + def->vmchannels[def->nvmchannels++] = chr; + } + VIR_FREE(nodes); + /* analysis of the input devices */ if ((n = virXPathNodeSet(conn, "./devices/input", ctxt, &nodes)) < 0) { @@ -3973,7 +4087,16 @@ virDomainChrDefFormat(virConnectPtr conn, const char *type = virDomainChrTypeToString(def->type); const char *targetName = virDomainChrTargetTypeToString(def->targetType); - const char *elementName = targetName; /* Currently always the same */ + const char *elementName; + switch (def->targetType) { + /* vmchannel types are in a common vmchannel element */ + case VIR_DOMAIN_CHR_TARGET_TYPE_GUESTFWD: + elementName = "vmchannel"; + break; + + default: + elementName = targetName; + } if (!type) { virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, @@ -4060,6 +4183,26 @@ virDomainChrDefFormat(virConnectPtr conn, } switch (def->targetType) { + case VIR_DOMAIN_CHR_TARGET_TYPE_GUESTFWD: + { + char *addr = virSocketFormatAddr(def->target.addr); + if (addr == NULL) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("Unable to format guestfwd address")); + return -1; + } + int port = virSocketGetPort(def->target.addr); + if (port < 0) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("Unable to format guestfwd port")); + return -1; + } + virBufferVSprintf(buf, " <target type='guestfwd' address='%s' port='%d'/>\n", + addr, port); + VIR_FREE(addr); + break; + } + case VIR_DOMAIN_CHR_TARGET_TYPE_PARALLEL: case VIR_DOMAIN_CHR_TARGET_TYPE_SERIAL: case VIR_DOMAIN_CHR_TARGET_TYPE_CONSOLE: @@ -4543,6 +4686,10 @@ char *virDomainDefFormat(virConnectPtr conn, goto cleanup; } + for (n = 0 ; n < def->nvmchannels ; n++) + if (virDomainChrDefFormat(conn, &buf, def->vmchannels[n], flags) < 0) + goto cleanup; + for (n = 0 ; n < def->ninputs ; n++) if (def->inputs[n]->bus == VIR_DOMAIN_INPUT_BUS_USB && virDomainInputDefFormat(conn, &buf, def->inputs[n]) < 0) diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 3bfa84d..a30a7a3 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -34,6 +34,7 @@ #include "util.h" #include "threads.h" #include "hash.h" +#include "network.h" /* Private component of virDomainXMLFlags */ typedef enum { @@ -217,6 +218,7 @@ enum virDomainChrTargetType { VIR_DOMAIN_CHR_TARGET_TYPE_PARALLEL, VIR_DOMAIN_CHR_TARGET_TYPE_SERIAL, VIR_DOMAIN_CHR_TARGET_TYPE_CONSOLE, + VIR_DOMAIN_CHR_TARGET_TYPE_GUESTFWD, VIR_DOMAIN_CHR_TARGET_TYPE_LAST }; @@ -249,6 +251,7 @@ struct _virDomainChrDef { int targetType; union { int port; /* parallel, serial, console */ + virSocketAddrPtr addr; /* guestfwd */ } target; int type; @@ -623,6 +626,9 @@ struct _virDomainDef { int nparallels; virDomainChrDefPtr *parallels; + int nvmchannels; + virDomainChrDefPtr *vmchannels; + /* Only 1 */ virDomainChrDefPtr console; virSecurityLabelDef seclabel; diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 3f0fbc1..93c383c 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -50,6 +50,7 @@ #include "xml.h" #include "nodeinfo.h" #include "logging.h" +#include "network.h" #define VIR_FROM_THIS VIR_FROM_QEMU @@ -1472,6 +1473,28 @@ static int qemudBuildCommandLineChrDevChardevStr(virDomainChrDefPtr dev, return 0; } +static int qemudBuildCommandLineChrDevTargetStr(virDomainChrDefPtr dev, + const char *const id, + char *buf, + int buflen) +{ + switch (dev->targetType) { + case VIR_DOMAIN_CHR_TARGET_TYPE_GUESTFWD: + { + char *addr = virSocketFormatAddr(dev->target.addr); + in_port_t port = virSocketGetPort(dev->target.addr); + + if (snprintf(buf, buflen, "user,guestfwd=tcp:%s:%i-chardev:%s", + addr, port, id) >= buflen) { + return -1; + } + break; + } + } + + return 0; +} + /* This function outputs an all-in-one character device command line option */ static int qemudBuildCommandLineChrDevStr(virDomainChrDefPtr dev, char *buf, @@ -2150,6 +2173,38 @@ int qemudBuildCommandLine(virConnectPtr conn, } } + if (def->nvmchannels) { + for (i = 0 ; i < def->nvmchannels ; i++) { + char buf[4096]; + char id[16]; + + virDomainChrDefPtr vmchannel = def->vmchannels[i]; + + if (snprintf(id, sizeof(id), "vmchannel%i", i) > sizeof(id)) + goto error; + + switch(vmchannel->targetType) { + case VIR_DOMAIN_CHR_TARGET_TYPE_GUESTFWD: + if (!(qemuCmdFlags & QEMUD_CMD_FLAG_CHARDEV)) { + qemudReportError(conn, NULL, NULL, VIR_ERR_NO_SUPPORT, + "%s", _("guestfwd requires QEMU to support -chardev")); + goto error; + } + + if (qemudBuildCommandLineChrDevChardevStr(vmchannel, id, + buf, sizeof(buf)) < 0) + goto error; + ADD_ARG_LIT("-chardev"); + ADD_ARG_LIT(buf); + ADD_ARG_LIT("-net"); + if (qemudBuildCommandLineChrDevTargetStr(vmchannel, id, + buf, sizeof(buf)) < 0) + goto error; + ADD_ARG_LIT(buf); + } + } + } + ADD_ARG_LIT("-usb"); for (i = 0 ; i < def->ninputs ; i++) { virDomainInputDefPtr input = def->inputs[i]; diff --git a/tests/qemuxml2argvdata/qemuxml2argv-vmchannel-guestfwd.args b/tests/qemuxml2argvdata/qemuxml2argv-vmchannel-guestfwd.args new file mode 100644 index 0000000..825e9c5 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-vmchannel-guestfwd.args @@ -0,0 +1 @@ +LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test /usr/bin/qemu -S -M pc -m 214 -smp 1 -nographic -monitor unix:/tmp/test-monitor,server,nowait -no-acpi -boot c -hda /dev/HostVG/QEMUGuest1 -net none -serial none -parallel none -chardev pipe,id=vmchannel0,path=/tmp/vmchannel -net user,guestfwd=tcp:10.0.2.1:4600-chardev:vmchannel0 -usb diff --git a/tests/qemuxml2argvdata/qemuxml2argv-vmchannel-guestfwd.xml b/tests/qemuxml2argvdata/qemuxml2argv-vmchannel-guestfwd.xml new file mode 100644 index 0000000..e3baecc --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-vmchannel-guestfwd.xml @@ -0,0 +1,26 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory>219200</memory> + <currentMemory>219200</currentMemory> + <vcpu cpuset='1-4,8-20,525'>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu</emulator> + <disk type='block' device='disk'> + <source dev='/dev/HostVG/QEMUGuest1'/> + <target dev='hda' bus='ide'/> + </disk> + <vmchannel type='pipe'> + <source path='/tmp/vmchannel'/> + <target type='guestfwd' address='10.0.2.1' port='4600'/> + </vmchannel> + </devices> +</domain> diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index 3255146..26ea502 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -268,11 +268,13 @@ mymain(int argc, char **argv) DO_TEST("serial-many", 0); DO_TEST("parallel-tcp", 0); DO_TEST("console-compat", 0); + + DO_TEST("vmchannel-guestfwd", QEMUD_CMD_FLAG_CHARDEV); + DO_TEST("sound", 0); DO_TEST("hostdev-usb-product", 0); DO_TEST("hostdev-usb-address", 0); - DO_TEST("hostdev-pci-address", QEMUD_CMD_FLAG_PCIDEVICE); DO_TEST_FULL("restore-v1", QEMUD_CMD_FLAG_MIGRATE_KVM_STDIO, "stdio"); diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c index 2cba47b..1d28bbd 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -129,6 +129,7 @@ mymain(int argc, char **argv) DO_TEST("serial-many"); DO_TEST("parallel-tcp"); DO_TEST("console-compat"); + DO_TEST("vmchannel-guestfwd"); DO_TEST("hostdev-usb-product"); DO_TEST("hostdev-usb-address"); -- 1.6.2.5
participants (1)
-
Matthew Booth