mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-22 21:55:25 +00:00
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.
|
||||
-->
|
||||
<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>
|
||||
<optional>
|
||||
<attribute name="path"/>
|
||||
</optional>
|
||||
<optional>
|
||||
<attribute name="host"/>
|
||||
</optional>
|
||||
<optional>
|
||||
<attribute name="service"/>
|
||||
</optional>
|
||||
<optional>
|
||||
<attribute name="wiremode"/>
|
||||
</optional>
|
||||
</element>
|
||||
</optional>
|
||||
<optional>
|
||||
<element name="target">
|
||||
<optional>
|
||||
<attribute name="port"/>
|
||||
</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>
|
||||
|
@ -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 \
|
||||
|
@ -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,17 +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, "%s",
|
||||
_("node->name is NULL in virDomainChrDefParseXML()"));
|
||||
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;
|
||||
}
|
||||
|
||||
@ -1405,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;
|
||||
@ -1534,6 +1631,9 @@ cleanup:
|
||||
VIR_FREE(connectHost);
|
||||
VIR_FREE(connectService);
|
||||
VIR_FREE(path);
|
||||
VIR_FREE(targetType);
|
||||
VIR_FREE(addrStr);
|
||||
VIR_FREE(portStr);
|
||||
|
||||
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 */
|
||||
if ((n = virXPathNodeSet(conn, "./devices/input", ctxt, &nodes)) < 0) {
|
||||
@ -3991,13 +4110,26 @@ virDomainChrDefFormat(virConnectPtr conn,
|
||||
{
|
||||
const char *type = virDomainChrTypeToString(def->type);
|
||||
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) {
|
||||
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 */
|
||||
@ -4079,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:
|
||||
@ -4096,7 +4247,10 @@ virDomainChrDefFormat(virConnectPtr conn,
|
||||
virBufferVSprintf(buf, " </%s>\n",
|
||||
elementName);
|
||||
|
||||
return 0;
|
||||
cleanup:
|
||||
VIR_FREE(addr);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
@ -4562,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)
|
||||
|
@ -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;
|
||||
|
@ -51,6 +51,7 @@
|
||||
#include "xml.h"
|
||||
#include "nodeinfo.h"
|
||||
#include "logging.h"
|
||||
#include "network.h"
|
||||
|
||||
#define VIR_FROM_THIS VIR_FROM_QEMU
|
||||
|
||||
@ -1411,6 +1412,97 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
char *buf,
|
||||
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");
|
||||
for (i = 0 ; i < def->ninputs ; i++) {
|
||||
virDomainInputDefPtr input = def->inputs[i];
|
||||
|
@ -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");
|
||||
|
@ -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");
|
||||
|
Loading…
Reference in New Issue
Block a user