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(a)/src/util/buf.c \
@top_srcdir(a)/src/util/logging.c \
@top_srcdir(a)/src/util/memory.c \
+ @top_srcdir(a)/src/util/network.c \
@top_srcdir(a)/src/util/threads.c \
@top_srcdir(a)/src/util/util.c \
@top_srcdir(a)/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