[Libvir] PATCH 2/4: Xen driver support for serial/paralle
Daniel P. Berrange
berrange at redhat.com
Fri Apr 18 20:27:05 UTC 2008
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 :|
More information about the libvir-list
mailing list