This patch updates Xen HVM to allow use of serial ¶llel ports, though
XenD limits you to single one of each even though QEMU supports many.
It also updates the <console> tag to support new syntax extensions.
xend_internal.c | 256 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
xend_internal.h | 9 +
xm_internal.c | 55 +++++++++---
xml.c | 217 +++++++++++++++++++++++++++++++++++++++++++++--
xml.h | 4
5 files changed, 519 insertions(+), 22 deletions(-)
Dan.
Index: src/xend_internal.c
===================================================================
RCS file: /data/cvs/libvirt/src/xend_internal.c,v
retrieving revision 1.180
diff -u -p -r1.180 xend_internal.c
--- src/xend_internal.c 10 Apr 2008 16:54:54 -0000 1.180
+++ src/xend_internal.c 18 Apr 2008 20:01:37 -0000
@@ -1376,6 +1376,233 @@ xend_parse_sexp_desc_os(virConnectPtr xe
return(0);
}
+
+int
+xend_parse_sexp_desc_char(virConnectPtr conn,
+ virBufferPtr buf,
+ const char *devtype,
+ int portNum,
+ const char *value,
+ const char *tty)
+{
+ const char *type;
+ int telnet = 0;
+ char *bindPort = NULL;
+ char *bindHost = NULL;
+ char *connectPort = NULL;
+ char *connectHost = NULL;
+ char *path = NULL;
+
+ if (value[0] == '/') {
+ type = "dev";
+ } else if (STREQLEN(value, "null", 4)) {
+ type = "null";
+ value = NULL;
+ } else if (STREQLEN(value, "vc", 2)) {
+ type = "vc";
+ value = NULL;
+ } else if (STREQLEN(value, "pty", 3)) {
+ type = "pty";
+ value = NULL;
+ } else if (STREQLEN(value, "stdio", 5)) {
+ type = "stdio";
+ value = NULL;
+ } else if (STREQLEN(value, "file:", 5)) {
+ type = "file";
+ value += 5;
+ } else if (STREQLEN(value, "pipe:", 5)) {
+ type = "pipe";
+ value += 5;
+ } else if (STREQLEN(value, "tcp:", 4)) {
+ type = "tcp";
+ value += 4;
+ } else if (STREQLEN(value, "telnet:", 4)) {
+ type = "tcp";
+ value += 7;
+ telnet = 1;
+ } else if (STREQLEN(value, "udp:", 4)) {
+ type = "udp";
+ value += 4;
+ } else if (STREQLEN(value, "unix:", 5)) {
+ type = "unix";
+ value += 5;
+ } else {
+ virXendError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("Unknown char device type"));
+ return -1;
+ }
+
+ /* Compat with legacy <console tty='/dev/pts/5'/> syntax */
+ if (STREQ(devtype, "console") &&
+ STREQ(type, "pty") &&
+ tty != NULL) {
+ if (virBufferVSprintf(buf, " <%s type='%s'
tty='%s'>\n",
+ devtype, type, tty) < 0)
+ goto no_memory;
+ } else {
+ if (virBufferVSprintf(buf, " <%s type='%s'>\n",
+ devtype, type) < 0)
+ goto no_memory;
+ }
+
+ if (STREQ(type, "null") ||
+ STREQ(type, "vc") ||
+ STREQ(type, "stdio")) {
+ /* no source needed */
+ } else if (STREQ(type, "pty")) {
+ if (tty &&
+ virBufferVSprintf(buf, " <source
path='%s'/>\n",
+ tty) < 0)
+ goto no_memory;
+ } else if (STREQ(type, "file") ||
+ STREQ(type, "pipe")) {
+ if (virBufferVSprintf(buf, " <source
path='%s'/>\n",
+ value) < 0)
+ goto no_memory;
+ } else if (STREQ(type, "tcp")) {
+ const char *offset = strchr(value, ':');
+ const char *offset2;
+ const char *mode, *wire;
+
+ if (offset == NULL) {
+ virXendError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("malformed char device string"));
+ goto error;
+ }
+
+ if (offset != value &&
+ (bindHost = strndup(value, offset - value)) == NULL)
+ goto no_memory;
+
+ offset2 = strchr(offset, ',');
+ if (offset2 == NULL)
+ bindPort = strdup(offset+1);
+ else
+ bindPort = strndup(offset+1, offset2-(offset+1));
+ if (bindPort == NULL)
+ goto no_memory;
+
+ if (offset2 && strstr(offset2, ",listen"))
+ mode = "bind";
+ else
+ mode = "connect";
+ wire = telnet ? "telnet":"raw";
+
+ if (bindHost) {
+ if (virBufferVSprintf(buf,
+ " <source mode='%s'
host='%s' service='%s' wiremode='%s'/>\n",
+ mode, bindHost, bindPort, wire) < 0)
+ goto no_memory;
+ } else {
+ if (virBufferVSprintf(buf,
+ " <source mode='%s'
service='%s' wiremode='%s'/>\n",
+ mode, bindPort, wire) < 0)
+ goto no_memory;
+ }
+ } else if (STREQ(type, "udp")) {
+ const char *offset = strchr(value, ':');
+ const char *offset2, *offset3;
+
+ if (offset == NULL) {
+ virXendError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("malformed char device string"));
+ goto error;
+ }
+
+ if (offset != value &&
+ (connectHost = strndup(value, offset - value)) == NULL)
+ goto no_memory;
+
+ offset2 = strchr(offset, '@');
+ if (offset2 != NULL) {
+ if ((connectPort = strndup(offset + 1, offset2-(offset+1))) == NULL)
+ goto no_memory;
+
+ offset3 = strchr(offset2, ':');
+ if (offset3 == NULL) {
+ virXendError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("malformed char device string"));
+ goto error;
+ }
+
+ if (offset3 > (offset2 + 1) &&
+ (bindHost = strndup(offset2 + 1, offset3 - (offset2+1))) == NULL)
+ goto no_memory;
+
+ if ((bindPort = strdup(offset3 + 1)) == NULL)
+ goto no_memory;
+ } else {
+ if ((connectPort = strdup(offset + 1)) == NULL)
+ goto no_memory;
+ }
+
+ if (connectPort) {
+ if (connectHost) {
+ if (virBufferVSprintf(buf,
+ " <source mode='connect'
host='%s' service='%s'/>\n",
+ connectHost, connectPort) < 0)
+ goto no_memory;
+ } else {
+ if (virBufferVSprintf(buf,
+ " <source mode='connect'
service='%s'/>\n",
+ connectPort) < 0)
+ goto no_memory;
+ }
+ }
+ if (bindPort) {
+ if (bindHost) {
+ if (virBufferVSprintf(buf,
+ " <source mode='bind'
host='%s' service='%s'/>\n",
+ bindHost, bindPort) < 0)
+ goto no_memory;
+ } else {
+ if (virBufferVSprintf(buf,
+ " <source mode='bind'
service='%s'/>\n",
+ bindPort) < 0)
+ goto no_memory;
+ }
+ }
+
+ } else if (STREQ(type, "unix")) {
+ const char *offset = strchr(value, ',');
+ int dolisten = 0;
+ if (offset)
+ path = strndup(value, (offset - value));
+ else
+ path = strdup(value);
+ if (path == NULL)
+ goto no_memory;
+
+ if (strstr(offset, ",listen") != NULL)
+ dolisten = 1;
+
+ if (virBufferVSprintf(buf, " <source mode='%s'
path='%s'/>\n",
+ dolisten ? "bind" : "connect", path)
< 0) {
+ free(path);
+ goto no_memory;
+ }
+
+ free(path);
+ }
+
+ if (virBufferVSprintf(buf, " <target port='%d'/>\n",
+ portNum) < 0)
+ goto no_memory;
+
+ if (virBufferVSprintf(buf, " </%s>\n",
+ devtype) < 0)
+ goto no_memory;
+
+ return 0;
+
+no_memory:
+ virXendError(conn, VIR_ERR_NO_MEMORY,
+ _("no memory for char device config"));
+
+error:
+ return -1;
+}
+
/**
* xend_parse_sexp_desc:
* @conn: the connection associated with the XML
@@ -1828,10 +2055,33 @@ xend_parse_sexp_desc(virConnectPtr conn,
}
tty = xenStoreDomainGetConsolePath(conn, domid);
- if (tty) {
- virBufferVSprintf(&buf, " <console tty='%s'/>\n",
tty);
- free(tty);
+ if (hvm) {
+ tmp = sexpr_node(root, "domain/image/hvm/serial");
+ if (tmp && STRNEQ(tmp, "none")) {
+ if (xend_parse_sexp_desc_char(conn, &buf, "serial", 0, tmp,
tty) < 0)
+ goto error;
+ /* Add back-compat <console/> tag for primary console */
+ if (xend_parse_sexp_desc_char(conn, &buf, "console", 0, tmp,
tty) < 0)
+ goto error;
+ }
+ tmp = sexpr_node(root, "domain/image/hvm/parallel");
+ if (tmp && STRNEQ(tmp, "none")) {
+ /* XXX does XenD stuff parallel port tty info into xenstore somewhere ? */
+ if (xend_parse_sexp_desc_char(conn, &buf, "parallel", 0, tmp,
NULL) < 0)
+ goto error;
+ }
+ } else {
+ /* Paravirt always has a console */
+ if (tty) {
+ virBufferVSprintf(&buf, " <console type='pty'
tty='%s'>\n", tty);
+ virBufferVSprintf(&buf, " <source
path='%s'/>\n", tty);
+ } else {
+ virBufferAddLit(&buf, " <console
type='pty'>\n");
+ }
+ virBufferAddLit(&buf, " <target port='0'/>\n");
+ virBufferAddLit(&buf, " </console>\n");
}
+ free(tty);
virBufferAddLit(&buf, " </devices>\n");
virBufferAddLit(&buf, "</domain>\n");
Index: src/xend_internal.h
===================================================================
RCS file: /data/cvs/libvirt/src/xend_internal.h,v
retrieving revision 1.40
diff -u -p -r1.40 xend_internal.h
--- src/xend_internal.h 10 Apr 2008 16:54:54 -0000 1.40
+++ src/xend_internal.h 18 Apr 2008 20:01:37 -0000
@@ -20,12 +20,12 @@
#include "libvirt/libvirt.h"
#include "capabilities.h"
+#include "buf.h"
#ifdef __cplusplus
extern "C" {
#endif
-
/**
* \brief Setup the connection to a xend instance via TCP
* \param host The host name to connect to
@@ -180,6 +180,13 @@ char *xenDaemonDomainDumpXMLByName(virCo
*/
int xend_log(virConnectPtr xend, char *buffer, size_t n_buffer);
+ int xend_parse_sexp_desc_char(virConnectPtr conn,
+ virBufferPtr buf,
+ const char *devtype,
+ int portNum,
+ const char *value,
+ const char *tty);
+
char *xend_parse_domain_sexp(virConnectPtr conn, char *root, int xendConfigVersion);
/* refactored ones */
Index: src/xm_internal.c
===================================================================
RCS file: /data/cvs/libvirt/src/xm_internal.c,v
retrieving revision 1.70
diff -u -p -r1.70 xm_internal.c
--- src/xm_internal.c 10 Apr 2008 16:54:54 -0000 1.70
+++ src/xm_internal.c 18 Apr 2008 20:01:37 -0000
@@ -1025,11 +1025,22 @@ char *xenXMDomainFormatXML(virConnectPtr
}
if (hvm) {
- if (xenXMConfigGetString(conf, "serial", &str) == 0 &&
!strcmp(str, "pty")) {
- virBufferAddLit(buf, " <console/>\n");
+ if (xenXMConfigGetString(conf, "parallel", &str) == 0) {
+ if (STRNEQ(str, "none"))
+ xend_parse_sexp_desc_char(conn, buf, "parallel", 0, str,
NULL);
+ }
+ if (xenXMConfigGetString(conf, "serial", &str) == 0) {
+ if (STRNEQ(str, "none")) {
+ xend_parse_sexp_desc_char(conn, buf, "serial", 0, str, NULL);
+ /* Add back-compat console tag for primary console */
+ xend_parse_sexp_desc_char(conn, buf, "console", 0, str, NULL);
+ }
}
- } else { /* Paravirt implicitly always has a console */
- virBufferAddLit(buf, " <console/>\n");
+ } else {
+ /* Paravirt implicitly always has a single console */
+ virBufferAddLit(buf, " <console type='pty'>\n");
+ virBufferAddLit(buf, " <target port='0'/>\n");
+ virBufferAddLit(buf, " </console>\n");
}
virBufferAddLit(buf, " </devices>\n");
@@ -2267,14 +2278,38 @@ virConfPtr xenXMParseXMLToConfig(virConn
obj = NULL;
if (hvm) {
- obj = xmlXPathEval(BAD_CAST "count(/domain/devices/console) > 0",
ctxt);
- if ((obj != NULL) && (obj->type == XPATH_BOOLEAN) &&
- (obj->boolval)) {
- if (xenXMConfigSetString(conf, "serial", "pty") < 0)
+ xmlNodePtr cur;
+ cur = virXPathNode("/domain/devices/parallel[1]", ctxt);
+ if (cur != NULL) {
+ char scratch[PATH_MAX];
+
+ if (virDomainParseXMLOSDescHVMChar(conn, scratch, sizeof(scratch), cur) <
0) {
+ goto error;
+ }
+
+ if (xenXMConfigSetString(conf, "parallel", scratch) < 0)
+ goto error;
+ } else {
+ if (xenXMConfigSetString(conf, "parallel", "none") <
0)
goto error;
}
- xmlXPathFreeObject(obj);
- obj = NULL;
+
+ cur = virXPathNode("/domain/devices/serial[1]", ctxt);
+ if (cur != NULL) {
+ char scratch[PATH_MAX];
+ if (virDomainParseXMLOSDescHVMChar(conn, scratch, sizeof(scratch), cur) <
0)
+ goto error;
+ if (xenXMConfigSetString(conf, "serial", scratch) < 0)
+ goto error;
+ } else {
+ if (virXPathBoolean("count(/domain/devices/console) > 0", ctxt))
{
+ if (xenXMConfigSetString(conf, "serial", "pty") <
0)
+ goto error;
+ } else {
+ if (xenXMConfigSetString(conf, "serial", "none") <
0)
+ goto error;
+ }
+ }
}
xmlFreeDoc(doc);
Index: src/xml.c
===================================================================
RCS file: /data/cvs/libvirt/src/xml.c,v
retrieving revision 1.117
diff -u -p -r1.117 xml.c
--- src/xml.c 10 Apr 2008 16:54:54 -0000 1.117
+++ src/xml.c 18 Apr 2008 20:01:37 -0000
@@ -673,6 +673,178 @@ virDomainParseXMLGraphicsDescVFB(virConn
}
+int
+virDomainParseXMLOSDescHVMChar(virConnectPtr conn,
+ char *buf,
+ size_t buflen,
+ xmlNodePtr node)
+{
+ xmlChar *type = NULL;
+ xmlChar *path = NULL;
+ xmlChar *bindHost = NULL;
+ xmlChar *bindService = NULL;
+ xmlChar *connectHost = NULL;
+ xmlChar *connectService = NULL;
+ xmlChar *mode = NULL;
+ xmlChar *wiremode = NULL;
+ xmlNodePtr cur;
+
+ type = xmlGetProp(node, BAD_CAST "type");
+
+ if (type != NULL) {
+ cur = node->children;
+ while (cur != NULL) {
+ if (cur->type == XML_ELEMENT_NODE) {
+ if (xmlStrEqual(cur->name, BAD_CAST "source")) {
+ if (mode == NULL)
+ mode = xmlGetProp(cur, BAD_CAST "mode");
+
+ if (STREQ((const char *)type, "dev") ||
+ STREQ((const char *)type, "file") ||
+ STREQ((const char *)type, "pipe") ||
+ STREQ((const char *)type, "unix")) {
+ if (path == NULL)
+ path = xmlGetProp(cur, BAD_CAST "path");
+
+ } else if (STREQ((const char *)type, "udp") ||
+ STREQ((const char *)type, "tcp")) {
+ if (mode == NULL ||
+ STREQ((const char *)mode, "connect")) {
+
+ if (connectHost == NULL)
+ connectHost = xmlGetProp(cur, BAD_CAST
"host");
+ if (connectService == NULL)
+ connectService = xmlGetProp(cur, BAD_CAST
"service");
+ } else {
+ if (bindHost == NULL)
+ bindHost = xmlGetProp(cur, BAD_CAST "host");
+ if (bindService == NULL)
+ bindService = xmlGetProp(cur, BAD_CAST
"service");
+ }
+
+ if (STREQ((const char*)type, "tcp"))
+ wiremode = xmlGetProp(cur, BAD_CAST "wiremode");
+
+ if (STREQ((const char*)type, "udp")) {
+ xmlFree(mode);
+ mode = NULL;
+ }
+ }
+ }
+ }
+ cur = cur->next;
+ }
+ }
+
+ if (type == NULL ||
+ STREQ((const char *)type, "pty")) {
+ strncpy(buf, "pty", buflen);
+ } else if (STREQ((const char *)type, "null") ||
+ STREQ((const char *)type, "stdio") ||
+ STREQ((const char *)type, "vc")) {
+ snprintf(buf, buflen, "%s", type);
+ } else if (STREQ((const char *)type, "file") ||
+ STREQ((const char *)type, "dev") ||
+ STREQ((const char *)type, "pipe")) {
+ if (path == NULL) {
+ virXMLError(conn, VIR_ERR_XML_ERROR,
+ _("Missing source path attribute for char device"),
0);
+ goto cleanup;
+ }
+
+ if (STREQ((const char *)type, "dev"))
+ strncpy(buf, (const char *)path, buflen);
+ else
+ snprintf(buf, buflen, "%s:%s", type, path);
+ } else if (STREQ((const char *)type, "tcp")) {
+ int telnet = 0;
+ if (wiremode != NULL &&
+ STREQ((const char *)wiremode, "telnet"))
+ telnet = 1;
+
+ if (mode == NULL ||
+ STREQ((const char *)mode, "connect")) {
+ if (connectHost == NULL) {
+ virXMLError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("Missing source host attribute for char device"),
0);
+ goto cleanup;
+ }
+ if (connectService == NULL) {
+ virXMLError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("Missing source service attribute for char
device"), 0);
+ goto cleanup;
+ }
+
+ snprintf(buf, buflen, "%s:%s:%s",
+ (telnet ? "telnet" : "tcp"),
+ connectHost, connectService);
+ } else {
+ if (bindHost == NULL) {
+ virXMLError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("Missing source host attribute for char device"),
0);
+ goto cleanup;
+ }
+ if (bindService == NULL) {
+ virXMLError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("Missing source service attribute for char
device"), 0);
+ goto cleanup;
+ }
+
+ snprintf(buf, buflen, "%s:%s:%s,listen",
+ (telnet ? "telnet" : "tcp"),
+ bindHost, bindService);
+ }
+ } else if (STREQ((const char *)type, "udp")) {
+ if (connectService == NULL) {
+ virXMLError(conn, VIR_ERR_XML_ERROR,
+ _("Missing source service attribute for char device"),
0);
+ goto cleanup;
+ }
+
+ snprintf(buf, buflen, "udp:%s:%s@%s:%s",
+ connectHost ? (const char *)connectHost : "",
+ connectService,
+ bindHost ? (const char *)bindHost : "",
+ bindService ? (const char *)bindService : "");
+ } else if (STREQ((const char *)type, "unix")) {
+ if (path == NULL) {
+ virXMLError(conn, VIR_ERR_XML_ERROR,
+ _("Missing source path attribute for char device"),
0);
+ goto cleanup;
+ }
+
+ if (mode == NULL ||
+ STREQ((const char *)mode, "connect")) {
+ snprintf(buf, buflen, "%s:%s", type, path);
+ } else {
+ snprintf(buf, buflen, "%s:%s,listen", type, path);
+ }
+ }
+ buf[buflen-1] = '\0';
+
+ xmlFree(mode);
+ xmlFree(wiremode);
+ xmlFree(type);
+ xmlFree(bindHost);
+ xmlFree(bindService);
+ xmlFree(connectHost);
+ xmlFree(connectService);
+ xmlFree(path);
+
+ return 0;
+
+cleanup:
+ xmlFree(mode);
+ xmlFree(wiremode);
+ xmlFree(type);
+ xmlFree(bindHost);
+ xmlFree(bindService);
+ xmlFree(connectHost);
+ xmlFree(connectService);
+ xmlFree(path);
+ return -1;
+}
+
/**
* virDomainParseXMLOSDescHVM:
* @conn: pointer to the hypervisor connection
@@ -877,24 +1049,53 @@ virDomainParseXMLOSDescHVM(virConnectPtr
nodes = NULL;
}
-
- res = virXPathBoolean("count(domain/devices/console) > 0", ctxt);
- if (res < 0) {
- virXMLError(conn, VIR_ERR_XML_ERROR, NULL, 0);
- goto error;
+ cur = virXPathNode("/domain/devices/parallel[1]", ctxt);
+ if (cur != NULL) {
+ char scratch[PATH_MAX];
+ if (virDomainParseXMLOSDescHVMChar(conn, scratch, sizeof(scratch), cur) < 0)
+ goto error;
+ if (virBufferVSprintf(buf, "(parallel %s)", scratch) < 0)
+ goto no_memory;
+ } else {
+ if (virBufferAddLit(buf, "(parallel none)") < 0)
+ goto no_memory;
}
- if (res) {
- virBufferAddLit(buf, "(serial pty)");
+
+ cur = virXPathNode("/domain/devices/serial[1]", ctxt);
+ if (cur != NULL) {
+ char scratch[PATH_MAX];
+ if (virDomainParseXMLOSDescHVMChar(conn, scratch, sizeof(scratch), cur) < 0)
+ goto error;
+ if (virBufferVSprintf(buf, "(serial %s)", scratch) < 0)
+ goto no_memory;
+ } else {
+ res = virXPathBoolean("count(domain/devices/console) > 0", ctxt);
+ if (res < 0) {
+ virXMLError(conn, VIR_ERR_XML_ERROR, NULL, 0);
+ goto error;
+ }
+ if (res) {
+ if (virBufferAddLit(buf, "(serial pty)") < 0)
+ goto no_memory;
+ } else {
+ if (virBufferAddLit(buf, "(serial none)") < 0)
+ goto no_memory;
+ }
}
str = virXPathString("string(/domain/clock/@offset)", ctxt);
if (str != NULL && !strcmp(str, "localtime")) {
- virBufferAddLit(buf, "(localtime 1)");
+ if (virBufferAddLit(buf, "(localtime 1)") < 0)
+ goto no_memory;
}
free(str);
return (0);
+no_memory:
+ virXMLError(conn, VIR_ERR_XML_ERROR,
+ "cannot allocate memory for buffer", 0);
+
error:
free(nodes);
return (-1);
Index: src/xml.h
===================================================================
RCS file: /data/cvs/libvirt/src/xml.h,v
retrieving revision 1.23
diff -u -p -r1.23 xml.h
--- src/xml.h 10 Apr 2008 16:54:54 -0000 1.23
+++ src/xml.h 18 Apr 2008 20:01:37 -0000
@@ -44,6 +44,10 @@ char * virSaveCpuSet (virConnec
char * virConvertCpuSet(virConnectPtr conn,
const char *str,
int maxcpu);
+int virDomainParseXMLOSDescHVMChar(virConnectPtr conn,
+ char *buf,
+ size_t buflen,
+ xmlNodePtr node);
char * virDomainParseXMLDesc(virConnectPtr conn,
const char *xmldesc,
char **name,
--
|: Red Hat, Engineering, Boston -o-
http://people.redhat.com/berrange/ :|
|:
http://libvirt.org -o-
http://virt-manager.org -o-
http://ovirt.org :|
|:
http://autobuild.org -o-
http://search.cpan.org/~danberr/ :|
|: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|