[libvirt] Add support for qemu's guestfwd

This is an update of the patchset I posted yesterday. There have been a few changes in the 4th patch, which actually adds guestfwd support: Firstly, after discussion on IRC, it was agreed that 'vmchannel' is a poor name for the collection of implementations of private host<->guest channels, because it is the name of a specific implementation. Consequently the name has been changed to just 'channel'. Secondly, I've fixed a bunch of things I noticed while adding actual vmchannel support (patches to follow separately). Lastly, I've changed the relax-ng a bit to check the precise set of attributes for guestfwd. This change is also followed up in the vmchannel support. The patch now supports the following domain XML: <channel type='pipe'> <source path='/tmp/guestfwd'/> <target type='guestfwd' address='10.0.2.1' port='4600'/> </channel> It will output the following on the qemu command line for this: -chardev pipe,id=channel0,path=/tmp/guestfwd \ -net user,guestfwd=tcp:10.0.2.1:4600-chardev:channel0 Support for -chardev is as complete as the current character device support. Because of the legacy vmchannel, there was also some confusion about whether these patches are intended for merging. These patches aren't directly related to vmchannel, and are targeted at merging. 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 a9c8573..e050453 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", @@ -1325,6 +1332,7 @@ virDomainChrDefParseXML(virConnectPtr conn, char *path = NULL; char *mode = NULL; char *protocol = NULL; + const char *targetType = NULL; virDomainChrDefPtr def; if (VIR_ALLOC(def) < 0) { @@ -1338,6 +1346,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) { @@ -2931,7 +2954,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); @@ -2951,7 +2974,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); @@ -2963,7 +2986,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 @@ -3965,10 +3988,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, @@ -3978,8 +4003,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) { @@ -4054,11 +4079,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; } @@ -4505,21 +4542,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 8599ee7..7bd8c63 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 { @@ -812,6 +825,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 a9ff1b4..536bf2d 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 ############################################################# @@ -1871,6 +1871,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); @@ -1907,13 +1909,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, @@ -1927,7 +1929,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; @@ -1993,6 +1995,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); @@ -2029,13 +2033,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, @@ -2706,9 +2710,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; } @@ -2719,20 +2723,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); @@ -2741,22 +2745,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: @@ -2769,7 +2773,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; } @@ -2782,9 +2786,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; } @@ -2795,20 +2799,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); @@ -2817,7 +2821,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 59873d7..4fd158c 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -3381,7 +3381,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")) { @@ -3394,7 +3395,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 2b8b550..0ebf35f 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -1919,6 +1919,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 5c6dbff..bac26d1 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 aecda23..08750f1 100644 --- a/src/vbox/vbox_tmpl.c +++ b/src/vbox/vbox_tmpl.c @@ -2417,9 +2417,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); @@ -2483,9 +2483,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; @@ -3507,7 +3507,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) { @@ -3522,17 +3522,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", @@ -3541,12 +3541,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", @@ -3587,7 +3587,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 b52f66e..5e8931e 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

On Tue, Nov 03, 2009 at 04:07:38PM +0000, Matthew Booth wrote:
@@ -1325,6 +1332,7 @@ virDomainChrDefParseXML(virConnectPtr conn, char *path = NULL; char *mode = NULL; char *protocol = NULL; + const char *targetType = NULL; virDomainChrDefPtr def;
if (VIR_ALLOC(def) < 0) {
Patch looks good to me. I'm going to be a bit picky though and say that if you unconditionally initialize variables when they are declared, as in the hunk above, then you prevent the compiler from detecting when you use a variable without first initializing it. Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones New in Fedora 11: Fedora Windows cross-compiler. Compile Windows programs, test, and build Windows installers. Over 70 libraries supprt'd http://fedoraproject.org/wiki/MinGW http://www.annexia.org/fedora_mingw

On 04/11/09 11:19, Richard W.M. Jones wrote:
On Tue, Nov 03, 2009 at 04:07:38PM +0000, Matthew Booth wrote:
@@ -1325,6 +1332,7 @@ virDomainChrDefParseXML(virConnectPtr conn, char *path = NULL; char *mode = NULL; char *protocol = NULL; + const char *targetType = NULL; virDomainChrDefPtr def;
if (VIR_ALLOC(def)< 0) {
Patch looks good to me. I'm going to be a bit picky though and say that if you unconditionally initialize variables when they are declared, as in the hunk above, then you prevent the compiler from detecting when you use a variable without first initializing it.
In this particular case the initialisation is required. The reason is that we unconditionally free the variable in the cleanup lower down. This pattern means we can 'goto error' and not worry about leaking objects which may or may not have been allocated. Matt -- Matthew Booth, RHCA, RHCSS Red Hat Engineering, Virtualisation Team M: +44 (0)7977 267231 GPG ID: D33C3490 GPG FPR: 3733 612D 2D05 5458 8A8A 1600 3441 EA19 D33C 3490

--- 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 4fd158c..e5d19ef 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

On Tue, Nov 03, 2009 at 04:07:39PM +0000, Matthew Booth wrote:
--- 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 4fd158c..e5d19ef 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 */ };
OK ... We assume therefore that qemu contains support for the relatively new -chardev feature? Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones virt-p2v converts physical machines to virtual machines. Boot with a live CD or over the network (PXE) and turn machines into Xen guests. http://et.redhat.com/~rjones/virt-p2v

On Wed, Nov 04, 2009 at 11:20:10AM +0000, Richard W.M. Jones wrote:
OK ... We assume therefore that qemu contains support for the relatively new -chardev feature?
Right, I see this feature is detected in patch 4/4. Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones virt-top is 'top' for virtual machines. Tiny program with many powerful monitoring features, net stats, disk stats, logging, etc. http://et.redhat.com/~rjones/virt-top

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 e5d19ef..3e37c66 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

On Tue, Nov 03, 2009 at 04:07:40PM +0000, Matthew Booth wrote:
Note that, on its own, this patch will generate a warning about an unused static function. +/* 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;
Looks OK, but wouldn't it be preferable to use a virBuffer here instead of depending on the caller to allocate a large enough buffer? http://libvirt.org/git/?p=libvirt.git;a=blob;f=src/util/buf.h;hb=HEAD Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones virt-p2v converts physical machines to virtual machines. Boot with a live CD or over the network (PXE) and turn machines into Xen guests. http://et.redhat.com/~rjones/virt-p2v

Agree virBuffer is nicer here, although it's not used anywhere else in qemu_conf.c. Here's an updated patch which uses a virBuffer instead.

Note that, on its own, this patch will generate a warning about an unused static function. --- src/qemu/qemu_conf.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 68 insertions(+), 0 deletions(-) diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 2981b51..a9f6885 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -1411,6 +1411,74 @@ qemuBuildHostNetStr(virConnectPtr conn, return 0; } +/* This function outputs a -chardev command line option which describes only the + * host side of the character device */ +static void qemudBuildCommandLineChrDevChardevStr(virDomainChrDefPtr dev, + const char *const id, + virBufferPtr buf) +{ + bool telnet; + switch(dev->type) { + case VIR_DOMAIN_CHR_TYPE_NULL: + virBufferVSprintf(buf, "null,id=%s", id); + break; + + case VIR_DOMAIN_CHR_TYPE_VC: + virBufferVSprintf(buf, "vc,id=%s", id); + break; + + case VIR_DOMAIN_CHR_TYPE_PTY: + virBufferVSprintf(buf, "pty,id=%s", id); + break; + + case VIR_DOMAIN_CHR_TYPE_DEV: + virBufferVSprintf(buf, "tty,id=%s,path=%s", id, dev->data.file.path); + break; + + case VIR_DOMAIN_CHR_TYPE_FILE: + virBufferVSprintf(buf, "file,id=%s,path=%s", id, dev->data.file.path); + break; + + case VIR_DOMAIN_CHR_TYPE_PIPE: + virBufferVSprintf(buf, "pipe,id=%s,path=%s", id, dev->data.file.path); + break; + + case VIR_DOMAIN_CHR_TYPE_STDIO: + virBufferVSprintf(buf, "stdio,id=%s", id); + break; + + case VIR_DOMAIN_CHR_TYPE_UDP: + virBufferVSprintf(buf, + "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); + break; + + case VIR_DOMAIN_CHR_TYPE_TCP: + telnet = dev->data.tcp.protocol == VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNET; + virBufferVSprintf(buf, + "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" : ""); + break; + + case VIR_DOMAIN_CHR_TYPE_UNIX: + virBufferVSprintf(buf, + "socket,id=%s,path=%s%s", + id, + dev->data.nix.path, + dev->data.nix.listen ? ",server,nowait" : ""); + break; + } +} + +/* 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

Would you please rebasae the legacy -vmchannel support accordingly? On Wed, Nov 04, 2009 at 03:57:32PM +0000, Matthew Booth wrote:
Note that, on its own, this patch will generate a warning about an unused static function. --- src/qemu/qemu_conf.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 68 insertions(+), 0 deletions(-)
diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 2981b51..a9f6885 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -1411,6 +1411,74 @@ qemuBuildHostNetStr(virConnectPtr conn, return 0; }
+/* This function outputs a -chardev command line option which describes only the + * host side of the character device */ +static void qemudBuildCommandLineChrDevChardevStr(virDomainChrDefPtr dev, + const char *const id, + virBufferPtr buf) +{ + bool telnet; + switch(dev->type) { + case VIR_DOMAIN_CHR_TYPE_NULL: + virBufferVSprintf(buf, "null,id=%s", id); + break; + + case VIR_DOMAIN_CHR_TYPE_VC: + virBufferVSprintf(buf, "vc,id=%s", id); + break; + + case VIR_DOMAIN_CHR_TYPE_PTY: + virBufferVSprintf(buf, "pty,id=%s", id); + break; + + case VIR_DOMAIN_CHR_TYPE_DEV: + virBufferVSprintf(buf, "tty,id=%s,path=%s", id, dev->data.file.path); + break; + + case VIR_DOMAIN_CHR_TYPE_FILE: + virBufferVSprintf(buf, "file,id=%s,path=%s", id, dev->data.file.path); + break; + + case VIR_DOMAIN_CHR_TYPE_PIPE: + virBufferVSprintf(buf, "pipe,id=%s,path=%s", id, dev->data.file.path); + break; + + case VIR_DOMAIN_CHR_TYPE_STDIO: + virBufferVSprintf(buf, "stdio,id=%s", id); + break; + + case VIR_DOMAIN_CHR_TYPE_UDP: + virBufferVSprintf(buf, + "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); + break; + + case VIR_DOMAIN_CHR_TYPE_TCP: + telnet = dev->data.tcp.protocol == VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNET; + virBufferVSprintf(buf, + "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" : ""); + break; + + case VIR_DOMAIN_CHR_TYPE_UNIX: + virBufferVSprintf(buf, + "socket,id=%s,path=%s%s", + id, + dev->data.nix.path, + dev->data.nix.listen ? ",server,nowait" : ""); + break; + } +} + +/* 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
-- Libvir-list mailing list Libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list

Updated patches are rebased on top of merged channel support. They additionally move some definitions around to hopefully reduce the chances of future merge conflicts.

* src/qemu/qemu_conf.[ch]: Detect -vmchannel command line option --- 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 e1e24c3..fa54974 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -897,6 +897,8 @@ static unsigned int qemudComputeCmdFlags(const char *help, flags |= QEMUD_CMD_FLAG_MEM_PATH; if (strstr(help, "-chardev")) flags |= QEMUD_CMD_FLAG_CHARDEV; + if (strstr(help, "-vmchannel")) + flags |= QEMUD_CMD_FLAG_VMCHANNEL; if (version >= 9000) flags |= QEMUD_CMD_FLAG_VNC_COLON; diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index 5d433d3..54095b5 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -44,6 +44,7 @@ /* Internal flags to keep track of qemu command line capabilities */ enum qemud_cmd_flags { + QEMUD_CMD_FLAG_VMCHANNEL = (1 << 31), /* Is the legacy -vmchannel arg available */ QEMUD_CMD_FLAG_KQEMU = (1 << 0), /* Whether KQEMU is compiled in */ QEMUD_CMD_FLAG_VNC_COLON = (1 << 1), /* Does the VNC take just port, or address + display */ QEMUD_CMD_FLAG_NO_REBOOT = (1 << 2), /* Is the -no-reboot flag available */ -- 1.6.2.5

This patch allows the following to be specified in a QEmu domain: <channel type='unix'> <source mode='bind' path='/tmp/vmchannel'/> <target type='vmchannel' deviceid='0200'/> </channel> * docs/schemas/domain.rng src/conf/domain_conf.[ch]: extend the domain schema and parsing/serialization for the new construct QEmu support adds the following on the qemu command line: -vmchannel di:0200,unix:/tmp/vmchannel,server,nowait * src/qemu/qemu_conf.c: Add -vmchannel argument output * tests/qemuxml2(argv|xml)test.c: Add test for vmchannel <channel> syntax --- docs/schemas/domain.rng | 13 ++++++++- src/conf/domain_conf.c | 30 +++++++++++++++++++- src/conf/domain_conf.h | 2 + src/qemu/qemu_conf.c | 18 ++++++++++++ .../qemuxml2argv-channel-vmchannel.args | 1 + .../qemuxml2argv-channel-vmchannel.xml | 26 +++++++++++++++++ tests/qemuxml2argvtest.c | 1 + tests/qemuxml2xmltest.c | 2 + 8 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-channel-vmchannel.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-channel-vmchannel.xml diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng index b75f17e..39a19ae 100644 --- a/docs/schemas/domain.rng +++ b/docs/schemas/domain.rng @@ -1059,12 +1059,23 @@ <attribute name="port"/> </element> </define> + <define name="vmchannelTarget"> + <element name="target"> + <attribute name="type"> + <value>vmchannel</value> + </attribute> + <attribute name="deviceid"/> + </element> + </define> <define name="channel"> <element name="channel"> <ref name="qemucdevSrcType"/> <interleave> <ref name="qemucdevSrcDef"/> - <ref name="guestfwdTarget"/> + <choice> + <ref name="guestfwdTarget"/> + <ref name="vmchannelTarget"/> + </choice> </interleave> </element> </define> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 918a5d7..c02b959 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -130,6 +130,7 @@ VIR_ENUM_IMPL(virDomainNet, VIR_DOMAIN_NET_TYPE_LAST, VIR_ENUM_IMPL(virDomainChrTarget, VIR_DOMAIN_CHR_TARGET_TYPE_LAST, "null", + "vmchannel", "monitor", "parallel", "serial", @@ -1349,6 +1350,7 @@ virDomainChrDefParseXML(virConnectPtr conn, const char *targetType = NULL; const char *addrStr = NULL; const char *portStr = NULL; + const char *deviceidStr = NULL; virDomainChrDefPtr def; if (VIR_ALLOC(def) < 0) { @@ -1504,6 +1506,26 @@ virDomainChrDefParseXML(virConnectPtr conn, virSocketSetPort(def->target.addr, port); break; + case VIR_DOMAIN_CHR_TARGET_TYPE_VMCHANNEL: + deviceidStr = virXMLPropString(cur, "deviceid"); + + if (deviceidStr == NULL) { + virDomainReportError(conn, VIR_ERR_INVALID_DOMAIN, + _("vmchannel channel does not " + "define a device id")); + goto error; + } + + unsigned int deviceid; + if (virStrToLong_ui(deviceidStr, NULL, 16, &deviceid) < 0) { + virDomainReportError(conn, VIR_ERR_INVALID_DOMAIN, + _("Invalid device id: %s"), + deviceidStr); + goto error; + } + def->target.deviceid = deviceid; + break; + default: virDomainReportError(conn, VIR_ERR_XML_ERROR, _("unexpected target type type %u"), @@ -1514,7 +1536,6 @@ virDomainChrDefParseXML(virConnectPtr conn, cur = cur->next; } - switch (def->type) { case VIR_DOMAIN_CHR_TYPE_NULL: /* Nada */ @@ -1641,6 +1662,7 @@ cleanup: VIR_FREE(targetType); VIR_FREE(addrStr); VIR_FREE(portStr); + VIR_FREE(deviceidStr); return def; @@ -4125,6 +4147,7 @@ virDomainChrDefFormat(virConnectPtr conn, switch (def->targetType) { /* channel types are in a common channel element */ case VIR_DOMAIN_CHR_TARGET_TYPE_GUESTFWD: + case VIR_DOMAIN_CHR_TARGET_TYPE_VMCHANNEL: elementName = "channel"; break; @@ -4237,6 +4260,11 @@ virDomainChrDefFormat(virConnectPtr conn, addr, port); break; + case VIR_DOMAIN_CHR_TARGET_TYPE_VMCHANNEL: + virBufferVSprintf(buf, " <target type='vmchannel' deviceid='%.4X'/>\n", + def->target.deviceid); + break; + case VIR_DOMAIN_CHR_TARGET_TYPE_PARALLEL: case VIR_DOMAIN_CHR_TARGET_TYPE_SERIAL: case VIR_DOMAIN_CHR_TARGET_TYPE_CONSOLE: diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index fadf43f..1549a01 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -214,6 +214,7 @@ virNetHasValidPciAddr(virDomainNetDefPtr def) enum virDomainChrTargetType { VIR_DOMAIN_CHR_TARGET_TYPE_NULL = 0, + VIR_DOMAIN_CHR_TARGET_TYPE_VMCHANNEL, VIR_DOMAIN_CHR_TARGET_TYPE_MONITOR, VIR_DOMAIN_CHR_TARGET_TYPE_PARALLEL, VIR_DOMAIN_CHR_TARGET_TYPE_SERIAL, @@ -252,6 +253,7 @@ struct _virDomainChrDef { union { int port; /* parallel, serial, console */ virSocketAddrPtr addr; /* guestfwd */ + unsigned int deviceid; /* PCI device id for vmchannel */ } target; int type; diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index fa54974..5ee0477 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -2197,6 +2197,24 @@ int qemudBuildCommandLine(virConnectPtr conn, ADD_ARG_LIT("-net"); ADD_ARG(virBufferContentAndReset(&buf)); + break; + + case VIR_DOMAIN_CHR_TARGET_TYPE_VMCHANNEL: + if (!(qemuCmdFlags & QEMUD_CMD_FLAG_VMCHANNEL)) { + qemudReportError(conn, NULL, NULL, VIR_ERR_NO_SUPPORT, + _("vmchannel requires QEMU to support -vmchannel")); + goto error; + } + + virBufferVSprintf(&buf, "di:%.4X,", channel->target.deviceid); + + qemudBuildCommandLineChrDevStr(channel, &buf); + if (virBufferError(&buf)) + goto error; + + ADD_ARG_LIT("-vmchannel"); + ADD_ARG(virBufferContentAndReset(&buf)); + break; } } diff --git a/tests/qemuxml2argvdata/qemuxml2argv-channel-vmchannel.args b/tests/qemuxml2argvdata/qemuxml2argv-channel-vmchannel.args new file mode 100644 index 0000000..aa946d4 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-channel-vmchannel.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 -vmchannel di:0200,unix:/tmp/vmchannel,server,nowait -usb diff --git a/tests/qemuxml2argvdata/qemuxml2argv-channel-vmchannel.xml b/tests/qemuxml2argvdata/qemuxml2argv-channel-vmchannel.xml new file mode 100644 index 0000000..ce4943d --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-channel-vmchannel.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> + <channel type='unix'> + <source mode='bind' path='/tmp/vmchannel'/> + <target type='vmchannel' deviceid='0200'/> + </channel> + </devices> +</domain> diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index c948379..c2809c7 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -270,6 +270,7 @@ mymain(int argc, char **argv) DO_TEST("console-compat", 0); DO_TEST("channel-guestfwd", QEMUD_CMD_FLAG_CHARDEV); + DO_TEST("channel-vmchannel", QEMUD_CMD_FLAG_VMCHANNEL); DO_TEST("sound", 0); diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c index 25ef2ce..54c3e2d 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -129,7 +129,9 @@ mymain(int argc, char **argv) DO_TEST("serial-many"); DO_TEST("parallel-tcp"); DO_TEST("console-compat"); + DO_TEST("channel-guestfwd"); + DO_TEST("channel-vmchannel"); DO_TEST("hostdev-usb-product"); DO_TEST("hostdev-usb-address"); -- 1.6.2.5

This patch allows the following to be specified in a qemu domain: <channel type='pipe'> <source path='/tmp/guestfwd'/> <target type='guestfwd' address='10.0.2.1' port='4600'/> </channel> This will output the following on the qemu command line: -chardev pipe,id=channel0,path=/tmp/guestfwd \ -net user,guestfwd=tcp:10.0.2.1:4600-chardev:channel0 * docs/schemas/domain.rng: Add <channel> and <guestfwd> elements * proxy/Makefile.am: add network.c as dep of domain_conf.c * src/conf/domain_conf.[ch]: Add xml parsing/formatting for channel and guestfwd * src/qemu/qemu_conf.c: Add argument output for guestfwd * tests/qemuxml2(argv|xml)test.c: Add test for guestfwd domain syntax --- docs/schemas/domain.rng | 89 ++++++---- proxy/Makefile.am | 1 + src/conf/domain_conf.c | 189 ++++++++++++++++++-- src/conf/domain_conf.h | 6 + src/qemu/qemu_conf.c | 61 +++++++ .../qemuxml2argv-channel-guestfwd.args | 1 + .../qemuxml2argv-channel-guestfwd.xml | 26 +++ tests/qemuxml2argvtest.c | 4 +- tests/qemuxml2xmltest.c | 1 + 9 files changed, 329 insertions(+), 49 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-channel-guestfwd.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-channel-guestfwd.xml diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng index 0a6ab61..b75f17e 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,24 @@ <ref name="qemucdev"/> </element> </define> + <define name="guestfwdTarget"> + <element name="target"> + <attribute name="type"> + <value>guestfwd</value> + </attribute> + <attribute name="address"/> + <attribute name="port"/> + </element> + </define> + <define name="channel"> + <element name="channel"> + <ref name="qemucdevSrcType"/> + <interleave> + <ref name="qemucdevSrcDef"/> + <ref name="guestfwdTarget"/> + </interleave> + </element> + </define> <define name="input"> <element name="input"> <attribute name="type"> @@ -1158,6 +1182,7 @@ <ref name="console"/> <ref name="parallel"/> <ref name="serial"/> + <ref name="channel"/> </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 e050453..4689bac 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: @@ -541,6 +549,7 @@ void virDomainDefFree(virDomainDefPtr def) for (i = 0 ; i < def->nnets ; i++) virDomainNetDefFree(def->nets[i]); VIR_FREE(def->nets); + for (i = 0 ; i < def->nserials ; i++) virDomainChrDefFree(def->serials[i]); VIR_FREE(def->serials); @@ -549,6 +558,10 @@ void virDomainDefFree(virDomainDefPtr def) virDomainChrDefFree(def->parallels[i]); VIR_FREE(def->parallels); + for (i = 0 ; i < def->nchannels ; i++) + virDomainChrDefFree(def->channels[i]); + VIR_FREE(def->channels); + virDomainChrDefFree(def->console); for (i = 0 ; i < def->nsounds ; i++) @@ -1332,7 +1345,10 @@ virDomainChrDefParseXML(virConnectPtr conn, char *path = NULL; char *mode = NULL; char *protocol = NULL; + const char *nodeName; const char *targetType = NULL; + const char *addrStr = NULL; + const char *portStr = NULL; virDomainChrDefPtr def; if (VIR_ALLOC(def) < 0) { @@ -1346,18 +1362,15 @@ 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); + nodeName = (const char *) node->name; + if ((def->targetType = virDomainChrTargetTypeFromString(nodeName)) < 0) { + /* channel is handled below */ + if(STRNEQ(nodeName, "channel")) { + virDomainReportError(conn, VIR_ERR_INVALID_DOMAIN, + _("unknown target type for character device: %s"), + nodeName); + return NULL; + } def->targetType = VIR_DOMAIN_CHR_TARGET_TYPE_NULL; } @@ -1406,6 +1419,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, "%s", + _("character device target does " + "not define a 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; + } + } + + unsigned int port; + switch (def->targetType) { + case VIR_DOMAIN_CHR_TARGET_TYPE_PARALLEL: + case VIR_DOMAIN_CHR_TARGET_TYPE_SERIAL: + case VIR_DOMAIN_CHR_TARGET_TYPE_CONSOLE: + portStr = virXMLPropString(cur, "port"); + if(portStr == NULL) { + /* Not required. It will be assigned automatically + * later */ + break; + } + + 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: + addrStr = virXMLPropString(cur, "address"); + portStr = virXMLPropString(cur, "port"); + + if(addrStr == NULL) { + virDomainReportError(conn, VIR_ERR_INVALID_DOMAIN, "%s", + _("guestfwd channel 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, "%s", + _("guestfwd channel does " + "not define a target port")); + goto error; + } + 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); + break; + + default: + virDomainReportError(conn, VIR_ERR_INVALID_DOMAIN, + _("unexpected target type type %u"), + def->targetType); + } } } cur = cur->next; @@ -1535,6 +1631,9 @@ cleanup: VIR_FREE(connectHost); VIR_FREE(connectService); VIR_FREE(path); + VIR_FREE(targetType); + VIR_FREE(addrStr); + VIR_FREE(portStr); return def; @@ -3007,6 +3106,25 @@ static virDomainDefPtr virDomainDefParseXML(virConnectPtr conn, } } + if ((n = virXPathNodeSet(conn, "./devices/channel", ctxt, &nodes)) < 0) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("cannot extract channel devices")); + goto error; + } + if (n && VIR_ALLOC_N(def->channels, n) < 0) + goto no_memory; + + for (i = 0 ; i < n ; i++) { + virDomainChrDefPtr chr = virDomainChrDefParseXML(conn, + nodes[i], + flags); + if (!chr) + goto error; + + def->channels[def->nchannels++] = chr; + } + VIR_FREE(nodes); + /* analysis of the input devices */ if ((n = virXPathNodeSet(conn, "./devices/input", ctxt, &nodes)) < 0) { @@ -3992,13 +4110,26 @@ virDomainChrDefFormat(virConnectPtr conn, { const char *type = virDomainChrTypeToString(def->type); const char *targetName = virDomainChrTargetTypeToString(def->targetType); + const char *elementName; + + const char *addr = NULL; + int ret = 0; + + switch (def->targetType) { + /* channel types are in a common channel element */ + case VIR_DOMAIN_CHR_TARGET_TYPE_GUESTFWD: + elementName = "channel"; + break; - const char *elementName = targetName; /* Currently always the same */ + default: + elementName = targetName; + } if (!type) { virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, _("unexpected char type %d"), def->type); - return -1; + ret = -1; + goto cleanup; } /* Compat with legacy <console tty='/dev/pts/5'/> syntax */ @@ -4080,6 +4211,25 @@ virDomainChrDefFormat(virConnectPtr conn, } switch (def->targetType) { + case VIR_DOMAIN_CHR_TARGET_TYPE_GUESTFWD: + addr = virSocketFormatAddr(def->target.addr); + if (addr == NULL) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to format guestfwd address")); + ret = -1; + goto cleanup; + } + int port = virSocketGetPort(def->target.addr); + if (port < 0) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to format guestfwd port")); + ret = -1; + goto cleanup; + } + virBufferVSprintf(buf, " <target type='guestfwd' address='%s' port='%d'/>\n", + addr, port); + break; + case VIR_DOMAIN_CHR_TARGET_TYPE_PARALLEL: case VIR_DOMAIN_CHR_TARGET_TYPE_SERIAL: case VIR_DOMAIN_CHR_TARGET_TYPE_CONSOLE: @@ -4097,7 +4247,10 @@ virDomainChrDefFormat(virConnectPtr conn, virBufferVSprintf(buf, " </%s>\n", elementName); - return 0; +cleanup: + VIR_FREE(addr); + + return ret; } static int @@ -4563,6 +4716,10 @@ char *virDomainDefFormat(virConnectPtr conn, goto cleanup; } + for (n = 0 ; n < def->nchannels ; n++) + if (virDomainChrDefFormat(conn, &buf, def->channels[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 7bd8c63..e826cc7 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 nchannels; + virDomainChrDefPtr *channels; + /* Only 1 */ virDomainChrDefPtr console; virSecurityLabelDef seclabel; diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 3e37c66..0fbc190 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,35 @@ static int qemudBuildCommandLineChrDevChardevStr(virDomainChrDefPtr dev, return 0; } +static int qemudBuildCommandLineChrDevTargetStr(virDomainChrDefPtr dev, + const char *const id, + char *buf, + int buflen) +{ + int ret = 0; + const char *addr = NULL; + + switch (dev->targetType) { + case VIR_DOMAIN_CHR_TARGET_TYPE_GUESTFWD: + { + addr = virSocketFormatAddr(dev->target.addr); + int port = virSocketGetPort(dev->target.addr); + + if (snprintf(buf, buflen, "user,guestfwd=tcp:%s:%i-chardev:%s", + addr, port, id) >= buflen) { + ret = -1; + goto cleanup; + } + break; + } + } + +cleanup: + VIR_FREE(addr); + + return ret; +} + /* This function outputs an all-in-one character device command line option */ static int qemudBuildCommandLineChrDevStr(virDomainChrDefPtr dev, char *buf, @@ -2150,6 +2180,37 @@ int qemudBuildCommandLine(virConnectPtr conn, } } + for (i = 0 ; i < def->nchannels ; i++) { + char buf[4096]; + char id[16]; + + virDomainChrDefPtr channel = def->channels[i]; + + if (snprintf(id, sizeof(id), "channel%i", i) > sizeof(id)) + goto error; + + switch(channel->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(channel, id, + buf, sizeof(buf)) < 0) + goto error; + ADD_ARG_LIT("-chardev"); + ADD_ARG_LIT(buf); + + if (qemudBuildCommandLineChrDevTargetStr(channel, id, + buf, sizeof(buf)) < 0) + goto error; + ADD_ARG_LIT("-net"); + 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-channel-guestfwd.args b/tests/qemuxml2argvdata/qemuxml2argv-channel-guestfwd.args new file mode 100644 index 0000000..b5bb46d --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-channel-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=channel0,path=/tmp/guestfwd -net user,guestfwd=tcp:10.0.2.1:4600-chardev:channel0 -usb diff --git a/tests/qemuxml2argvdata/qemuxml2argv-channel-guestfwd.xml b/tests/qemuxml2argvdata/qemuxml2argv-channel-guestfwd.xml new file mode 100644 index 0000000..51a0c1e --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-channel-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> + <channel type='pipe'> + <source path='/tmp/guestfwd'/> + <target type='guestfwd' address='10.0.2.1' port='4600'/> + </channel> + </devices> +</domain> diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index 3255146..c948379 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("channel-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..25ef2ce 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("channel-guestfwd"); DO_TEST("hostdev-usb-product"); DO_TEST("hostdev-usb-address"); -- 1.6.2.5

On Tue, Nov 03, 2009 at 04:07:41PM +0000, Matthew Booth wrote:
This patch allows the following to be specified in a qemu domain:
<channel type='pipe'> <source path='/tmp/guestfwd'/> <target type='guestfwd' address='10.0.2.1' port='4600'/> </channel>
Patch looks good. I wonder about using type='guestfwd' and whether that name is too specific to qemu. Do other hypervisors have a similar feature? Could we s/guestfwd/forward-tcp-out/ or something like that? Are we planning to also support hostfwd (which is guestfwd in reverse)? Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones libguestfs lets you edit virtual machines. Supports shell scripting, bindings from many languages. http://et.redhat.com/~rjones/libguestfs/ See what it can do: http://et.redhat.com/~rjones/libguestfs/recipes.html
participants (3)
-
Dan Kenigsberg
-
Matthew Booth
-
Richard W.M. Jones