diff --git a/ChangeLog b/ChangeLog index 7247f9a6afba5e309720f28c79dfd5faac365e11..0bf674c3d168da286a49ef48153bd9ac86a89afb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +Sat Apr 26 10:21:28 EST 2008 Daniel P. Berrange + + * src/xm_internal.c, src/xml.c, src/xml.h, src/xend_internal.c, + src/xend_internal.h: Added support for serial and parallel + devices + * tests/sexpr2xmltest.c, tests/xml2sexprtest.c, tests/xmconfigtest.c: + added tests for serial and parallel devices + * tests/sexpr2xmldata/*, tests/xml2sexprdata/*, tests/xmconfigdata/*: + updated for new test cases + Fri Apr 25 16:45:28 EST 2008 Daniel P. Berrange * src/internal.c: Convenience macros for fixed arrays diff --git a/src/internal.h b/src/internal.h index 67d31b3639d15bd72f1a01fcb8c15627ea0a2cea..02cd3584a1e62472d114ba8747adbe54a79bc3a6 100644 --- a/src/internal.h +++ b/src/internal.h @@ -65,7 +65,7 @@ extern "C" { #define STRCASEEQLEN(a,b,n) (strncasecmp((a),(b),(n)) == 0) #define STRNEQLEN(a,b,n) (strncmp((a),(b),(n)) != 0) #define STRCASENEQLEN(a,b,n) (strncasecmp((a),(b),(n)) != 0) - +#define STRPREFIX(a,b) (strncmp((a),(b),strlen((b))) == 0) #define NUL_TERMINATE(buf) do { (buf)[sizeof(buf)-1] = '\0'; } while (0) #define ARRAY_CARDINALITY(Array) (sizeof (Array) / sizeof *(Array)) diff --git a/src/xend_internal.c b/src/xend_internal.c index 6ba4571db6f0443fd4a887f50b17c90ac485caca..7eb4877e10ec81cabcd5f1b16053736c88bd01dc 100644 --- a/src/xend_internal.c +++ b/src/xend_internal.c @@ -1376,6 +1376,244 @@ xend_parse_sexp_desc_os(virConnectPtr xend, struct sexpr *node, virBufferPtr buf 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; + int ret = -1; + + if (value[0] == '/') { + type = "dev"; + } else if (STRPREFIX(value, "null")) { + type = "null"; + value = NULL; + } else if (STRPREFIX(value, "vc")) { + type = "vc"; + value = NULL; + } else if (STRPREFIX(value, "pty")) { + type = "pty"; + value = NULL; + } else if (STRPREFIX(value, "stdio")) { + type = "stdio"; + value = NULL; + } else if (STRPREFIX(value, "file:")) { + type = "file"; + value += sizeof("file:")-1; + } else if (STRPREFIX(value, "pipe:")) { + type = "pipe"; + value += sizeof("pipe:")-1; + } else if (STRPREFIX(value, "tcp:")) { + type = "tcp"; + value += sizeof("tcp:")-1; + } else if (STRPREFIX(value, "telnet:")) { + type = "tcp"; + value += sizeof("telnet:")-1; + telnet = 1; + } else if (STRPREFIX(value, "udp:")) { + type = "udp"; + value += sizeof("udp:")-1; + } else if (STRPREFIX(value, "unix:")) { + type = "unix"; + value += sizeof("unix:")-1; + } else { + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + _("Unknown char device type")); + return -1; + } + + /* Compat with legacy 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, " \n", + tty) < 0) + goto no_memory; + } else if (STREQ(type, "file") || + STREQ(type, "pipe")) { + if (virBufferVSprintf(buf, " \n", + value) < 0) + goto no_memory; + } else if (STREQ(type, "tcp")) { + const char *offset = strchr(value, ':'); + const char *offset2; + const char *mode, *protocol; + + 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"; + protocol = telnet ? "telnet":"raw"; + + if (bindHost) { + if (virBufferVSprintf(buf, + " \n", + mode, bindHost, bindPort) < 0) + goto no_memory; + } else { + if (virBufferVSprintf(buf, + " \n", + mode, bindPort) < 0) + goto no_memory; + } + if (virBufferVSprintf(buf, + " \n", + protocol) < 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, + " \n", + connectHost, connectPort) < 0) + goto no_memory; + } else { + if (virBufferVSprintf(buf, + " \n", + connectPort) < 0) + goto no_memory; + } + } + if (bindPort) { + if (bindHost) { + if (virBufferVSprintf(buf, + " \n", + bindHost, bindPort) < 0) + goto no_memory; + } else { + if (virBufferVSprintf(buf, + " \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 (offset != NULL && + strstr(offset, ",listen") != NULL) + dolisten = 1; + + if (virBufferVSprintf(buf, " \n", + dolisten ? "bind" : "connect", path) < 0) + goto no_memory; + } + + if (virBufferVSprintf(buf, " \n", + portNum) < 0) + goto no_memory; + + if (virBufferVSprintf(buf, " \n", + devtype) < 0) + goto no_memory; + + ret = 0; + + if (ret == -1) { +no_memory: + virXendError(conn, VIR_ERR_NO_MEMORY, + _("no memory for char device config")); + } + +error: + + free(path); + free(bindHost); + free(bindPort); + free(connectHost); + free(connectPort); + + return ret; +} + /** * xend_parse_sexp_desc: * @conn: the connection associated with the XML @@ -1828,10 +2066,33 @@ xend_parse_sexp_desc(virConnectPtr conn, struct sexpr *root, } tty = xenStoreDomainGetConsolePath(conn, domid); - if (tty) { - virBufferVSprintf(&buf, " \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 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, " \n", tty); + virBufferVSprintf(&buf, " \n", tty); + } else { + virBufferAddLit(&buf, " \n"); + } + virBufferAddLit(&buf, " \n"); + virBufferAddLit(&buf, " \n"); } + free(tty); virBufferAddLit(&buf, " \n"); virBufferAddLit(&buf, "\n"); diff --git a/src/xend_internal.h b/src/xend_internal.h index e157e88b5e20329f663963fe37070b198f195bca..d56d73d60dc2e39bcc4f393ae3afff2de38ef31e 100644 --- a/src/xend_internal.h +++ b/src/xend_internal.h @@ -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(virConnectPtr xend, */ 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 */ diff --git a/src/xm_internal.c b/src/xm_internal.c index 3d845dcd57c9ca9343e9a1fb43db0bbf40d69da9..811a62bdee55d39d393c7fcdc1bfa6b04ffcd17f 100644 --- a/src/xm_internal.c +++ b/src/xm_internal.c @@ -1025,11 +1025,22 @@ char *xenXMDomainFormatXML(virConnectPtr conn, virConfPtr conf) { } if (hvm) { - if (xenXMConfigGetString(conf, "serial", &str) == 0 && !strcmp(str, "pty")) { - virBufferAddLit(buf, " \n"); + if (xenXMConfigGetString(conf, "parallel", &str) == 0) { + if (STRNEQ(str, "none")) + xend_parse_sexp_desc_char(conn, buf, "parallel", 0, str, NULL); } - } else { /* Paravirt implicitly always has a console */ - virBufferAddLit(buf, " \n"); + 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 single console */ + virBufferAddLit(buf, " \n"); + virBufferAddLit(buf, " \n"); + virBufferAddLit(buf, " \n"); } virBufferAddLit(buf, " \n"); @@ -2267,14 +2278,38 @@ virConfPtr xenXMParseXMLToConfig(virConnectPtr conn, const char *xml) { 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); diff --git a/src/xml.c b/src/xml.c index 8e95103b196edce7880a9dbd7a9b3656237be0b0..ceea9872193457677c314c1a57c9277f4c06918c 100644 --- a/src/xml.c +++ b/src/xml.c @@ -673,6 +673,178 @@ virDomainParseXMLGraphicsDescVFB(virConnectPtr conn ATTRIBUTE_UNUSED, } +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 *protocol = 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, "udp")) { + xmlFree(mode); + mode = NULL; + } + } + } else if (xmlStrEqual(cur->name, BAD_CAST "protocol")) { + if (protocol == NULL) + protocol = xmlGetProp(cur, BAD_CAST "type"); + } + } + 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 (protocol != NULL && + STREQ((const char *)protocol, "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(protocol); + xmlFree(type); + xmlFree(bindHost); + xmlFree(bindService); + xmlFree(connectHost); + xmlFree(connectService); + xmlFree(path); + + return 0; + +cleanup: + xmlFree(mode); + xmlFree(protocol); + 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 conn, xmlNodePtr node, 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); diff --git a/src/xml.h b/src/xml.h index 2d30b6576f71e4cb1026c3f7002408201e3f73a5..d91a1b0e7ba4044acb2b3130d5717cf145f7a48a 100644 --- a/src/xml.h +++ b/src/xml.h @@ -44,6 +44,10 @@ char * virSaveCpuSet (virConnectPtr conn, 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, diff --git a/tests/sexpr2xmldata/sexpr2xml-curmem.xml b/tests/sexpr2xmldata/sexpr2xml-curmem.xml index 15853cd82738de93891bd2a3a19a2dd57213b021..a609f97e833fd42fe7433676d9f21e0543ae7935 100644 --- a/tests/sexpr2xmldata/sexpr2xml-curmem.xml +++ b/tests/sexpr2xmldata/sexpr2xml-curmem.xml @@ -28,5 +28,8 @@ + + + diff --git a/tests/sexpr2xmldata/sexpr2xml-disk-block-shareable.xml b/tests/sexpr2xmldata/sexpr2xml-disk-block-shareable.xml index 9c2f19dcc926cb531e6608a0d82f734e83b5066c..35735399f1e5dd9f96c8e00f0aad4180101bb306 100644 --- a/tests/sexpr2xmldata/sexpr2xml-disk-block-shareable.xml +++ b/tests/sexpr2xmldata/sexpr2xml-disk-block-shareable.xml @@ -21,5 +21,8 @@