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>
<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
<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">
<ref name="console"/>
<ref name="parallel"/>
<ref name="serial"/>
+ <ref name="channel"/>
</choice>
</zeroOrMore>
<optional>
#include "buf.h"
#include "c-ctype.h"
#include "logging.h"
+#include "network.h"
#define VIR_FROM_THIS VIR_FROM_DOMAIN
"monitor",
"parallel",
"serial",
- "console")
+ "console",
+ "guestfwd")
VIR_ENUM_IMPL(virDomainChr, VIR_DOMAIN_CHR_TYPE_LAST,
"null",
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:
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);
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++)
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) {
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;
}
} 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;
VIR_FREE(connectHost);
VIR_FREE(connectService);
VIR_FREE(path);
+ VIR_FREE(targetType);
+ VIR_FREE(addrStr);
+ VIR_FREE(portStr);
return def;
}
}
+ 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) {
{
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 */
}
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:
virBufferVSprintf(buf, " </%s>\n",
elementName);
- return 0;
+cleanup:
+ VIR_FREE(addr);
+
+ return ret;
}
static int
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)
#include "xml.h"
#include "nodeinfo.h"
#include "logging.h"
+#include "network.h"
#define VIR_FROM_THIS VIR_FROM_QEMU
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)
}
}
+ 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];