mirror of https://gitee.com/openkylin/libvirt.git
Support for <channel> in domain and QEmu backend
allows the following to be specified in a domain: <channel type='pipe'> <source path='/tmp/guestfwd'/> <target type='guestfwd' address='10.0.2.1' port='4600'/> </channel> * proxy/Makefile.am: add network.c as dep of domain_conf.c * docs/schemas/domain.rng src/conf/domain_conf.[ch]: extend the domain schemas and the parsing/serialization side for the new construct QEmu support will add 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 * src/qemu/qemu_conf.c: Add argument output for channel * tests/qemuxml2(argv|xml)test.c: Add test for <channel> domain syntax
This commit is contained in:
parent
7400396801
commit
af249ea468
|
@ -930,6 +930,19 @@
|
||||||
definition doesn't fully specify the constraints on this node.
|
definition doesn't fully specify the constraints on this node.
|
||||||
-->
|
-->
|
||||||
<define name="qemucdev">
|
<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">
|
<attribute name="type">
|
||||||
<choice>
|
<choice>
|
||||||
<value>dev</value>
|
<value>dev</value>
|
||||||
|
@ -944,43 +957,36 @@
|
||||||
<value>pty</value>
|
<value>pty</value>
|
||||||
</choice>
|
</choice>
|
||||||
</attribute>
|
</attribute>
|
||||||
<interleave>
|
</define>
|
||||||
<optional>
|
<define name="qemucdevSrcDef">
|
||||||
<oneOrMore>
|
<optional>
|
||||||
<element name="source">
|
<oneOrMore>
|
||||||
<optional>
|
<element name="source">
|
||||||
<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">
|
|
||||||
<optional>
|
<optional>
|
||||||
<attribute name="type"/>
|
<attribute name="mode"/>
|
||||||
|
</optional>
|
||||||
|
<optional>
|
||||||
|
<attribute name="path"/>
|
||||||
|
</optional>
|
||||||
|
<optional>
|
||||||
|
<attribute name="host"/>
|
||||||
|
</optional>
|
||||||
|
<optional>
|
||||||
|
<attribute name="service"/>
|
||||||
|
</optional>
|
||||||
|
<optional>
|
||||||
|
<attribute name="wiremode"/>
|
||||||
</optional>
|
</optional>
|
||||||
</element>
|
</element>
|
||||||
</optional>
|
</oneOrMore>
|
||||||
<optional>
|
</optional>
|
||||||
<element name="target">
|
<optional>
|
||||||
<optional>
|
<element name="protocol">
|
||||||
<attribute name="port"/>
|
<optional>
|
||||||
</optional>
|
<attribute name="type"/>
|
||||||
</element>
|
</optional>
|
||||||
</optional>
|
</element>
|
||||||
</interleave>
|
</optional>
|
||||||
</define>
|
</define>
|
||||||
<!--
|
<!--
|
||||||
The description for a console
|
The description for a console
|
||||||
|
@ -1044,6 +1050,24 @@
|
||||||
<ref name="qemucdev"/>
|
<ref name="qemucdev"/>
|
||||||
</element>
|
</element>
|
||||||
</define>
|
</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">
|
<define name="input">
|
||||||
<element name="input">
|
<element name="input">
|
||||||
<attribute name="type">
|
<attribute name="type">
|
||||||
|
@ -1158,6 +1182,7 @@
|
||||||
<ref name="console"/>
|
<ref name="console"/>
|
||||||
<ref name="parallel"/>
|
<ref name="parallel"/>
|
||||||
<ref name="serial"/>
|
<ref name="serial"/>
|
||||||
|
<ref name="channel"/>
|
||||||
</choice>
|
</choice>
|
||||||
</zeroOrMore>
|
</zeroOrMore>
|
||||||
<optional>
|
<optional>
|
||||||
|
|
|
@ -17,6 +17,7 @@ libvirt_proxy_SOURCES = libvirt_proxy.c \
|
||||||
@top_srcdir@/src/util/buf.c \
|
@top_srcdir@/src/util/buf.c \
|
||||||
@top_srcdir@/src/util/logging.c \
|
@top_srcdir@/src/util/logging.c \
|
||||||
@top_srcdir@/src/util/memory.c \
|
@top_srcdir@/src/util/memory.c \
|
||||||
|
@top_srcdir@/src/util/network.c \
|
||||||
@top_srcdir@/src/util/threads.c \
|
@top_srcdir@/src/util/threads.c \
|
||||||
@top_srcdir@/src/util/util.c \
|
@top_srcdir@/src/util/util.c \
|
||||||
@top_srcdir@/src/util/uuid.c \
|
@top_srcdir@/src/util/uuid.c \
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
#include "buf.h"
|
#include "buf.h"
|
||||||
#include "c-ctype.h"
|
#include "c-ctype.h"
|
||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
|
#include "network.h"
|
||||||
|
|
||||||
#define VIR_FROM_THIS VIR_FROM_DOMAIN
|
#define VIR_FROM_THIS VIR_FROM_DOMAIN
|
||||||
|
|
||||||
|
@ -132,7 +133,8 @@ VIR_ENUM_IMPL(virDomainChrTarget, VIR_DOMAIN_CHR_TARGET_TYPE_LAST,
|
||||||
"monitor",
|
"monitor",
|
||||||
"parallel",
|
"parallel",
|
||||||
"serial",
|
"serial",
|
||||||
"console")
|
"console",
|
||||||
|
"guestfwd")
|
||||||
|
|
||||||
VIR_ENUM_IMPL(virDomainChr, VIR_DOMAIN_CHR_TYPE_LAST,
|
VIR_ENUM_IMPL(virDomainChr, VIR_DOMAIN_CHR_TYPE_LAST,
|
||||||
"null",
|
"null",
|
||||||
|
@ -412,6 +414,12 @@ void virDomainChrDefFree(virDomainChrDefPtr def)
|
||||||
if (!def)
|
if (!def)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
switch (def->targetType) {
|
||||||
|
case VIR_DOMAIN_CHR_TARGET_TYPE_GUESTFWD:
|
||||||
|
VIR_FREE(def->target.addr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
switch (def->type) {
|
switch (def->type) {
|
||||||
case VIR_DOMAIN_CHR_TYPE_PTY:
|
case VIR_DOMAIN_CHR_TYPE_PTY:
|
||||||
case VIR_DOMAIN_CHR_TYPE_DEV:
|
case VIR_DOMAIN_CHR_TYPE_DEV:
|
||||||
|
@ -541,6 +549,7 @@ void virDomainDefFree(virDomainDefPtr def)
|
||||||
for (i = 0 ; i < def->nnets ; i++)
|
for (i = 0 ; i < def->nnets ; i++)
|
||||||
virDomainNetDefFree(def->nets[i]);
|
virDomainNetDefFree(def->nets[i]);
|
||||||
VIR_FREE(def->nets);
|
VIR_FREE(def->nets);
|
||||||
|
|
||||||
for (i = 0 ; i < def->nserials ; i++)
|
for (i = 0 ; i < def->nserials ; i++)
|
||||||
virDomainChrDefFree(def->serials[i]);
|
virDomainChrDefFree(def->serials[i]);
|
||||||
VIR_FREE(def->serials);
|
VIR_FREE(def->serials);
|
||||||
|
@ -549,6 +558,10 @@ void virDomainDefFree(virDomainDefPtr def)
|
||||||
virDomainChrDefFree(def->parallels[i]);
|
virDomainChrDefFree(def->parallels[i]);
|
||||||
VIR_FREE(def->parallels);
|
VIR_FREE(def->parallels);
|
||||||
|
|
||||||
|
for (i = 0 ; i < def->nchannels ; i++)
|
||||||
|
virDomainChrDefFree(def->channels[i]);
|
||||||
|
VIR_FREE(def->channels);
|
||||||
|
|
||||||
virDomainChrDefFree(def->console);
|
virDomainChrDefFree(def->console);
|
||||||
|
|
||||||
for (i = 0 ; i < def->nsounds ; i++)
|
for (i = 0 ; i < def->nsounds ; i++)
|
||||||
|
@ -1332,7 +1345,10 @@ virDomainChrDefParseXML(virConnectPtr conn,
|
||||||
char *path = NULL;
|
char *path = NULL;
|
||||||
char *mode = NULL;
|
char *mode = NULL;
|
||||||
char *protocol = NULL;
|
char *protocol = NULL;
|
||||||
|
const char *nodeName;
|
||||||
const char *targetType = NULL;
|
const char *targetType = NULL;
|
||||||
|
const char *addrStr = NULL;
|
||||||
|
const char *portStr = NULL;
|
||||||
virDomainChrDefPtr def;
|
virDomainChrDefPtr def;
|
||||||
|
|
||||||
if (VIR_ALLOC(def) < 0) {
|
if (VIR_ALLOC(def) < 0) {
|
||||||
|
@ -1346,17 +1362,15 @@ virDomainChrDefParseXML(virConnectPtr conn,
|
||||||
else if ((def->type = virDomainChrTypeFromString(type)) < 0)
|
else if ((def->type = virDomainChrTypeFromString(type)) < 0)
|
||||||
def->type = VIR_DOMAIN_CHR_TYPE_NULL;
|
def->type = VIR_DOMAIN_CHR_TYPE_NULL;
|
||||||
|
|
||||||
targetType = (const char *) node->name;
|
nodeName = (const char *) node->name;
|
||||||
if (targetType == NULL) {
|
if ((def->targetType = virDomainChrTargetTypeFromString(nodeName)) < 0) {
|
||||||
/* Shouldn't be possible */
|
/* channel is handled below */
|
||||||
virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, "%s",
|
if(STRNEQ(nodeName, "channel")) {
|
||||||
_("node->name is NULL in virDomainChrDefParseXML()"));
|
virDomainReportError(conn, VIR_ERR_INVALID_DOMAIN,
|
||||||
return NULL;
|
_("unknown target type for character device: %s"),
|
||||||
}
|
nodeName);
|
||||||
if ((def->targetType = virDomainChrTargetTypeFromString(targetType)) < 0) {
|
return NULL;
|
||||||
virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
}
|
||||||
_("unknown target type for character device: %s"),
|
|
||||||
targetType);
|
|
||||||
def->targetType = VIR_DOMAIN_CHR_TARGET_TYPE_NULL;
|
def->targetType = VIR_DOMAIN_CHR_TARGET_TYPE_NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1405,6 +1419,89 @@ virDomainChrDefParseXML(virConnectPtr conn,
|
||||||
} else if (xmlStrEqual(cur->name, BAD_CAST "protocol")) {
|
} else if (xmlStrEqual(cur->name, BAD_CAST "protocol")) {
|
||||||
if (protocol == NULL)
|
if (protocol == NULL)
|
||||||
protocol = virXMLPropString(cur, "type");
|
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;
|
cur = cur->next;
|
||||||
|
@ -1534,6 +1631,9 @@ cleanup:
|
||||||
VIR_FREE(connectHost);
|
VIR_FREE(connectHost);
|
||||||
VIR_FREE(connectService);
|
VIR_FREE(connectService);
|
||||||
VIR_FREE(path);
|
VIR_FREE(path);
|
||||||
|
VIR_FREE(targetType);
|
||||||
|
VIR_FREE(addrStr);
|
||||||
|
VIR_FREE(portStr);
|
||||||
|
|
||||||
return def;
|
return def;
|
||||||
|
|
||||||
|
@ -3006,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 */
|
/* analysis of the input devices */
|
||||||
if ((n = virXPathNodeSet(conn, "./devices/input", ctxt, &nodes)) < 0) {
|
if ((n = virXPathNodeSet(conn, "./devices/input", ctxt, &nodes)) < 0) {
|
||||||
|
@ -3991,13 +4110,26 @@ virDomainChrDefFormat(virConnectPtr conn,
|
||||||
{
|
{
|
||||||
const char *type = virDomainChrTypeToString(def->type);
|
const char *type = virDomainChrTypeToString(def->type);
|
||||||
const char *targetName = virDomainChrTargetTypeToString(def->targetType);
|
const char *targetName = virDomainChrTargetTypeToString(def->targetType);
|
||||||
|
const char *elementName;
|
||||||
|
|
||||||
const char *elementName = targetName; /* Currently always the same */
|
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;
|
||||||
|
|
||||||
|
default:
|
||||||
|
elementName = targetName;
|
||||||
|
}
|
||||||
|
|
||||||
if (!type) {
|
if (!type) {
|
||||||
virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
||||||
_("unexpected char type %d"), def->type);
|
_("unexpected char type %d"), def->type);
|
||||||
return -1;
|
ret = -1;
|
||||||
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Compat with legacy <console tty='/dev/pts/5'/> syntax */
|
/* Compat with legacy <console tty='/dev/pts/5'/> syntax */
|
||||||
|
@ -4079,6 +4211,25 @@ virDomainChrDefFormat(virConnectPtr conn,
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (def->targetType) {
|
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_PARALLEL:
|
||||||
case VIR_DOMAIN_CHR_TARGET_TYPE_SERIAL:
|
case VIR_DOMAIN_CHR_TARGET_TYPE_SERIAL:
|
||||||
case VIR_DOMAIN_CHR_TARGET_TYPE_CONSOLE:
|
case VIR_DOMAIN_CHR_TARGET_TYPE_CONSOLE:
|
||||||
|
@ -4096,7 +4247,10 @@ virDomainChrDefFormat(virConnectPtr conn,
|
||||||
virBufferVSprintf(buf, " </%s>\n",
|
virBufferVSprintf(buf, " </%s>\n",
|
||||||
elementName);
|
elementName);
|
||||||
|
|
||||||
return 0;
|
cleanup:
|
||||||
|
VIR_FREE(addr);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
@ -4562,6 +4716,10 @@ char *virDomainDefFormat(virConnectPtr conn,
|
||||||
goto cleanup;
|
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++)
|
for (n = 0 ; n < def->ninputs ; n++)
|
||||||
if (def->inputs[n]->bus == VIR_DOMAIN_INPUT_BUS_USB &&
|
if (def->inputs[n]->bus == VIR_DOMAIN_INPUT_BUS_USB &&
|
||||||
virDomainInputDefFormat(conn, &buf, def->inputs[n]) < 0)
|
virDomainInputDefFormat(conn, &buf, def->inputs[n]) < 0)
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "threads.h"
|
#include "threads.h"
|
||||||
#include "hash.h"
|
#include "hash.h"
|
||||||
|
#include "network.h"
|
||||||
|
|
||||||
/* Private component of virDomainXMLFlags */
|
/* Private component of virDomainXMLFlags */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
@ -217,6 +218,7 @@ enum virDomainChrTargetType {
|
||||||
VIR_DOMAIN_CHR_TARGET_TYPE_PARALLEL,
|
VIR_DOMAIN_CHR_TARGET_TYPE_PARALLEL,
|
||||||
VIR_DOMAIN_CHR_TARGET_TYPE_SERIAL,
|
VIR_DOMAIN_CHR_TARGET_TYPE_SERIAL,
|
||||||
VIR_DOMAIN_CHR_TARGET_TYPE_CONSOLE,
|
VIR_DOMAIN_CHR_TARGET_TYPE_CONSOLE,
|
||||||
|
VIR_DOMAIN_CHR_TARGET_TYPE_GUESTFWD,
|
||||||
|
|
||||||
VIR_DOMAIN_CHR_TARGET_TYPE_LAST
|
VIR_DOMAIN_CHR_TARGET_TYPE_LAST
|
||||||
};
|
};
|
||||||
|
@ -249,6 +251,7 @@ struct _virDomainChrDef {
|
||||||
int targetType;
|
int targetType;
|
||||||
union {
|
union {
|
||||||
int port; /* parallel, serial, console */
|
int port; /* parallel, serial, console */
|
||||||
|
virSocketAddrPtr addr; /* guestfwd */
|
||||||
} target;
|
} target;
|
||||||
|
|
||||||
int type;
|
int type;
|
||||||
|
@ -623,6 +626,9 @@ struct _virDomainDef {
|
||||||
int nparallels;
|
int nparallels;
|
||||||
virDomainChrDefPtr *parallels;
|
virDomainChrDefPtr *parallels;
|
||||||
|
|
||||||
|
int nchannels;
|
||||||
|
virDomainChrDefPtr *channels;
|
||||||
|
|
||||||
/* Only 1 */
|
/* Only 1 */
|
||||||
virDomainChrDefPtr console;
|
virDomainChrDefPtr console;
|
||||||
virSecurityLabelDef seclabel;
|
virSecurityLabelDef seclabel;
|
||||||
|
|
|
@ -51,6 +51,7 @@
|
||||||
#include "xml.h"
|
#include "xml.h"
|
||||||
#include "nodeinfo.h"
|
#include "nodeinfo.h"
|
||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
|
#include "network.h"
|
||||||
|
|
||||||
#define VIR_FROM_THIS VIR_FROM_QEMU
|
#define VIR_FROM_THIS VIR_FROM_QEMU
|
||||||
|
|
||||||
|
@ -1411,6 +1412,97 @@ qemuBuildHostNetStr(virConnectPtr conn,
|
||||||
return 0;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int qemudBuildCommandLineChrDevTargetStr(virDomainChrDefPtr dev,
|
||||||
|
const char *const id,
|
||||||
|
virBufferPtr buf)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
const char *addr = NULL;
|
||||||
|
|
||||||
|
int port;
|
||||||
|
switch (dev->targetType) {
|
||||||
|
case VIR_DOMAIN_CHR_TARGET_TYPE_GUESTFWD:
|
||||||
|
addr = virSocketFormatAddr(dev->target.addr);
|
||||||
|
port = virSocketGetPort(dev->target.addr);
|
||||||
|
|
||||||
|
virBufferVSprintf(buf, "user,guestfwd=tcp:%s:%i-chardev:%s",
|
||||||
|
addr, port, id);
|
||||||
|
|
||||||
|
VIR_FREE(addr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This function outputs an all-in-one character device command line option */
|
||||||
static int qemudBuildCommandLineChrDevStr(virDomainChrDefPtr dev,
|
static int qemudBuildCommandLineChrDevStr(virDomainChrDefPtr dev,
|
||||||
char *buf,
|
char *buf,
|
||||||
int buflen)
|
int buflen)
|
||||||
|
@ -2088,6 +2180,46 @@ int qemudBuildCommandLine(virConnectPtr conn,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (i = 0 ; i < def->nchannels ; i++) {
|
||||||
|
virBuffer buf = VIR_BUFFER_INITIALIZER;
|
||||||
|
const char *argStr;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
qemudBuildCommandLineChrDevChardevStr(channel, id, &buf);
|
||||||
|
argStr = virBufferContentAndReset(&buf);
|
||||||
|
if (argStr == NULL)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
ADD_ARG_LIT("-chardev");
|
||||||
|
ADD_ARG_LIT(argStr);
|
||||||
|
|
||||||
|
VIR_FREE(argStr);
|
||||||
|
|
||||||
|
qemudBuildCommandLineChrDevTargetStr(channel, id, &buf);
|
||||||
|
argStr = virBufferContentAndReset(&buf);
|
||||||
|
if (argStr == NULL)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
ADD_ARG_LIT("-net");
|
||||||
|
ADD_ARG_LIT(argStr);
|
||||||
|
|
||||||
|
VIR_FREE(argStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ADD_ARG_LIT("-usb");
|
ADD_ARG_LIT("-usb");
|
||||||
for (i = 0 ; i < def->ninputs ; i++) {
|
for (i = 0 ; i < def->ninputs ; i++) {
|
||||||
virDomainInputDefPtr input = def->inputs[i];
|
virDomainInputDefPtr input = def->inputs[i];
|
||||||
|
|
|
@ -268,11 +268,13 @@ mymain(int argc, char **argv)
|
||||||
DO_TEST("serial-many", 0);
|
DO_TEST("serial-many", 0);
|
||||||
DO_TEST("parallel-tcp", 0);
|
DO_TEST("parallel-tcp", 0);
|
||||||
DO_TEST("console-compat", 0);
|
DO_TEST("console-compat", 0);
|
||||||
|
|
||||||
|
DO_TEST("channel-guestfwd", QEMUD_CMD_FLAG_CHARDEV);
|
||||||
|
|
||||||
DO_TEST("sound", 0);
|
DO_TEST("sound", 0);
|
||||||
|
|
||||||
DO_TEST("hostdev-usb-product", 0);
|
DO_TEST("hostdev-usb-product", 0);
|
||||||
DO_TEST("hostdev-usb-address", 0);
|
DO_TEST("hostdev-usb-address", 0);
|
||||||
|
|
||||||
DO_TEST("hostdev-pci-address", QEMUD_CMD_FLAG_PCIDEVICE);
|
DO_TEST("hostdev-pci-address", QEMUD_CMD_FLAG_PCIDEVICE);
|
||||||
|
|
||||||
DO_TEST_FULL("restore-v1", QEMUD_CMD_FLAG_MIGRATE_KVM_STDIO, "stdio");
|
DO_TEST_FULL("restore-v1", QEMUD_CMD_FLAG_MIGRATE_KVM_STDIO, "stdio");
|
||||||
|
|
|
@ -129,6 +129,7 @@ mymain(int argc, char **argv)
|
||||||
DO_TEST("serial-many");
|
DO_TEST("serial-many");
|
||||||
DO_TEST("parallel-tcp");
|
DO_TEST("parallel-tcp");
|
||||||
DO_TEST("console-compat");
|
DO_TEST("console-compat");
|
||||||
|
DO_TEST("channel-guestfwd");
|
||||||
|
|
||||||
DO_TEST("hostdev-usb-product");
|
DO_TEST("hostdev-usb-product");
|
||||||
DO_TEST("hostdev-usb-address");
|
DO_TEST("hostdev-usb-address");
|
||||||
|
|
Loading…
Reference in New Issue