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 :| -- Libvir-list mailing list Libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list