On Fri, Apr 18, 2008 at 09:25:28PM +0100, Daniel P. Berrange wrote: > This supports the serial/character devices in QEMU. It is complete > except for fact that I don't extract the TTY path for type='pty'. > This needs addressing before merging This patch now includes the ability to extract the TTY path for serial and parallel devices, so we finally have 'virsh console' working for QEMU. THat said, plain QEMU/KVM has busted serial console which translates \r\n into \n\n, but I've sent a patch for that upstream http://lists.gnu.org/archive/html/qemu-devel/2008-04/msg00461.html internal.h | 4 qemu_conf.c | 648 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++- qemu_conf.h | 60 +++++ qemu_driver.c | 77 +++++- 4 files changed, 761 insertions(+), 28 deletions(-) Dan. Index: src/internal.h =================================================================== RCS file: /data/cvs/libvirt/src/internal.h,v retrieving revision 1.69 diff -u -p -r1.69 internal.h --- src/internal.h 18 Apr 2008 09:26:45 -0000 1.69 +++ src/internal.h 24 Apr 2008 15:09:59 -0000 @@ -66,6 +66,10 @@ extern "C" { #define STRNEQLEN(a,b,n) (strncmp((a),(b),(n)) != 0) #define STRCASENEQLEN(a,b,n) (strncasecmp((a),(b),(n)) != 0) + +#define NUL_TERMINATE(buf) do { (buf)[sizeof(buf)-1] = '\0'; } while (0) +#define ARRAY_CARDINALITY(Array) (sizeof (Array) / sizeof *(Array)) + /* If configured with --enable-debug=yes then library calls * are printed to stderr for debugging. */ Index: src/qemu_conf.c =================================================================== RCS file: /data/cvs/libvirt/src/qemu_conf.c,v retrieving revision 1.48 diff -u -p -r1.48 qemu_conf.c --- src/qemu_conf.c 10 Apr 2008 16:54:54 -0000 1.48 +++ src/qemu_conf.c 24 Apr 2008 15:10:00 -0000 @@ -205,6 +205,8 @@ void qemudFreeVMDef(struct qemud_vm_def struct qemud_vm_disk_def *disk = def->disks; struct qemud_vm_net_def *net = def->nets; struct qemud_vm_input_def *input = def->inputs; + struct qemud_vm_chr_def *serial = def->serials; + struct qemud_vm_chr_def *parallel = def->parallels; while (disk) { struct qemud_vm_disk_def *prev = disk; @@ -221,6 +223,16 @@ void qemudFreeVMDef(struct qemud_vm_def input = input->next; free(prev); } + while (serial) { + struct qemud_vm_chr_def *prev = serial; + serial = serial->next; + free(prev); + } + while (parallel) { + struct qemud_vm_chr_def *prev = parallel; + parallel = parallel->next; + free(prev); + } xmlFree(def->keymap); free(def); } @@ -945,6 +957,333 @@ static int qemudParseInterfaceXML(virCon } +/* Parse the XML definition for a character device + * @param net pre-allocated & zero'd net record + * @param node XML nodeset to parse for net definition + * @return 0 on success, -1 on failure + * + * The XML we're dealing with looks like + * + * <serial type="pty"> + * <source path="/dev/pts/3"/> + * <target port="1"/> + * </serial> + * + * <serial type="dev"> + * <source path="/dev/ttyS0"/> + * <target port="1"/> + * </serial> + * + * <serial type="tcp"> + * <source mode="connect" host="0.0.0.0" service="2445"/> + * <target port="1"/> + * </serial> + * + * <serial type="tcp"> + * <source mode="bind" host="0.0.0.0" service="2445"/> + * <target port="1"/> + * </serial> + * + * <serial type="udp"> + * <source mode="bind" host="0.0.0.0" service="2445"/> + * <source mode="connect" host="0.0.0.0" service="2445"/> + * <target port="1"/> + * </serial> + * + * <serial type="unix"> + * <source mode="bind" path="/tmp/foo"/> + * <target port="1"/> + * </serial> + * + */ +static int qemudParseCharXML(virConnectPtr conn, + struct qemud_vm_chr_def *chr, + int portNum, + xmlNodePtr node) { + xmlNodePtr cur; + xmlChar *type = NULL; + xmlChar *bindHost = NULL; + xmlChar *bindService = NULL; + xmlChar *connectHost = NULL; + xmlChar *connectService = NULL; + xmlChar *path = NULL; + xmlChar *mode = NULL; + xmlChar *protocol = NULL; + int ret = -1; + + chr->srcType = QEMUD_CHR_SRC_TYPE_PTY; + type = xmlGetProp(node, BAD_CAST "type"); + if (type != NULL) { + if (xmlStrEqual(type, BAD_CAST "null")) + chr->srcType = QEMUD_CHR_SRC_TYPE_NULL; + else if (xmlStrEqual(type, BAD_CAST "vc")) + chr->srcType = QEMUD_CHR_SRC_TYPE_VC; + else if (xmlStrEqual(type, BAD_CAST "pty")) + chr->srcType = QEMUD_CHR_SRC_TYPE_PTY; + else if (xmlStrEqual(type, BAD_CAST "dev")) + chr->srcType = QEMUD_CHR_SRC_TYPE_DEV; + else if (xmlStrEqual(type, BAD_CAST "file")) + chr->srcType = QEMUD_CHR_SRC_TYPE_FILE; + else if (xmlStrEqual(type, BAD_CAST "pipe")) + chr->srcType = QEMUD_CHR_SRC_TYPE_PIPE; + else if (xmlStrEqual(type, BAD_CAST "stdio")) + chr->srcType = QEMUD_CHR_SRC_TYPE_STDIO; + else if (xmlStrEqual(type, BAD_CAST "udp")) + chr->srcType = QEMUD_CHR_SRC_TYPE_UDP; + else if (xmlStrEqual(type, BAD_CAST "tcp")) + chr->srcType = QEMUD_CHR_SRC_TYPE_TCP; + else if (xmlStrEqual(type, BAD_CAST "unix")) + chr->srcType = QEMUD_CHR_SRC_TYPE_UNIX; + else + chr->srcType = QEMUD_CHR_SRC_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"); + + switch (chr->srcType) { + case QEMUD_CHR_SRC_TYPE_PTY: + case QEMUD_CHR_SRC_TYPE_DEV: + case QEMUD_CHR_SRC_TYPE_FILE: + case QEMUD_CHR_SRC_TYPE_PIPE: + case QEMUD_CHR_SRC_TYPE_UNIX: + if (path == NULL) + path = xmlGetProp(cur, BAD_CAST "path"); + + break; + + case QEMUD_CHR_SRC_TYPE_UDP: + case QEMUD_CHR_SRC_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 (chr->srcType == QEMUD_CHR_SRC_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; + } + + + chr->dstPort = portNum; + + switch (chr->srcType) { + case QEMUD_CHR_SRC_TYPE_NULL: + /* Nada */ + break; + + case QEMUD_CHR_SRC_TYPE_VC: + break; + + case QEMUD_CHR_SRC_TYPE_PTY: + /* @path attribute is an output only property - pty is auto-allocted */ + break; + + case QEMUD_CHR_SRC_TYPE_DEV: + case QEMUD_CHR_SRC_TYPE_FILE: + case QEMUD_CHR_SRC_TYPE_PIPE: + if (path == NULL) { + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("Missing source path attribute for char device")); + goto cleanup; + } + + strncpy(chr->srcData.file.path, (const char *)path, + sizeof(chr->srcData.file.path)); + NUL_TERMINATE(chr->srcData.file.path); + break; + + case QEMUD_CHR_SRC_TYPE_STDIO: + /* Nada */ + break; + + case QEMUD_CHR_SRC_TYPE_TCP: + if (mode == NULL || + STREQ((const char *)mode, "connect")) { + if (connectHost == NULL) { + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("Missing source host attribute for char device")); + goto cleanup; + } + if (connectService == NULL) { + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("Missing source service attribute for char device")); + goto cleanup; + } + + strncpy(chr->srcData.tcp.host, (const char *)connectHost, + sizeof(chr->srcData.tcp.host)); + NUL_TERMINATE(chr->srcData.tcp.host); + strncpy(chr->srcData.tcp.service, (const char *)connectService, + sizeof(chr->srcData.tcp.service)); + NUL_TERMINATE(chr->srcData.tcp.service); + + chr->srcData.tcp.listen = 0; + } else { + if (bindHost == NULL) { + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("Missing source host attribute for char device")); + goto cleanup; + } + if (bindService == NULL) { + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("Missing source service attribute for char device")); + goto cleanup; + } + + strncpy(chr->srcData.tcp.host, (const char *)bindHost, + sizeof(chr->srcData.tcp.host)); + NUL_TERMINATE(chr->srcData.tcp.host); + strncpy(chr->srcData.tcp.service, (const char *)bindService, + sizeof(chr->srcData.tcp.service)); + NUL_TERMINATE(chr->srcData.tcp.service); + + chr->srcData.tcp.listen = 1; + } + if (protocol != NULL && + STREQ((const char *)protocol, "telnet")) + chr->srcData.tcp.protocol = QEMUD_CHR_SRC_TCP_PROTOCOL_TELNET; + else + chr->srcData.tcp.protocol = QEMUD_CHR_SRC_TCP_PROTOCOL_RAW; + break; + + case QEMUD_CHR_SRC_TYPE_UDP: + if (connectService == NULL) { + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("Missing source service attribute for char device")); + goto cleanup; + } + + if (connectHost != NULL) { + strncpy(chr->srcData.udp.connectHost, (const char *)connectHost, + sizeof(chr->srcData.udp.connectHost)); + NUL_TERMINATE(chr->srcData.udp.connectHost); + } + strncpy(chr->srcData.udp.connectService, (const char *)connectService, + sizeof(chr->srcData.udp.connectService)); + NUL_TERMINATE(chr->srcData.udp.connectService); + + if (bindHost != NULL) { + strncpy(chr->srcData.udp.bindHost, (const char *)bindHost, + sizeof(chr->srcData.udp.bindHost)); + NUL_TERMINATE(chr->srcData.udp.bindHost); + } + if (bindService != NULL) { + strncpy(chr->srcData.udp.bindService, (const char *)bindService, + sizeof(chr->srcData.udp.bindService)); + NUL_TERMINATE(chr->srcData.udp.bindService); + } + break; + + case QEMUD_CHR_SRC_TYPE_UNIX: + if (path == NULL) { + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("Missing source path attribute for char device")); + goto cleanup; + } + + if (mode != NULL && + STRNEQ((const char *)mode, "connect")) + chr->srcData.nix.listen = 1; + else + chr->srcData.nix.listen = 0; + + strncpy(chr->srcData.nix.path, (const char *)path, + sizeof(chr->srcData.nix.path)); + NUL_TERMINATE(chr->srcData.nix.path); + break; + } + + ret = 0; + +cleanup: + xmlFree(mode); + xmlFree(protocol); + xmlFree(type); + xmlFree(bindHost); + xmlFree(bindService); + xmlFree(connectHost); + xmlFree(connectService); + xmlFree(path); + + return ret; +} + + +static int qemudParseCharXMLDevices(virConnectPtr conn, + xmlXPathContextPtr ctxt, + const char *xpath, + unsigned int *ndevs, + struct qemud_vm_chr_def **devs) +{ + xmlXPathObjectPtr obj; + int i, ret = -1; + + obj = xmlXPathEval(BAD_CAST xpath, ctxt); + if ((obj != NULL) && (obj->type == XPATH_NODESET) && + (obj->nodesetval != NULL) && (obj->nodesetval->nodeNr >= 0)) { + struct qemud_vm_chr_def *prev = *devs; + if (ndevs == NULL && + obj->nodesetval->nodeNr > 1) { + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("too many character devices")); + goto cleanup; + } + + for (i = 0; i < obj->nodesetval->nodeNr; i++) { + struct qemud_vm_chr_def *chr = calloc(1, sizeof(*chr)); + if (!chr) { + qemudReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, + "%s", + _("failed to allocate space for char device")); + goto cleanup; + } + + if (qemudParseCharXML(conn, chr, i, obj->nodesetval->nodeTab[i]) < 0) { + free(chr); + goto cleanup; + } + if (ndevs) + (*ndevs)++; + chr->next = NULL; + if (i == 0) { + *devs = chr; + } else { + prev->next = chr; + } + prev = chr; + } + } + + ret = 0; + +cleanup: + xmlXPathFreeObject(obj); + return ret; +} + + /* Parse the XML definition for a network interface */ static int qemudParseInputXML(virConnectPtr conn, struct qemud_vm_input_def *input, @@ -1423,6 +1762,45 @@ static struct qemud_vm_def *qemudParseXM } } xmlXPathFreeObject(obj); + obj = NULL; + + /* analysis of the character devices */ + if (qemudParseCharXMLDevices(conn, ctxt, + "/domain/devices/parallel", + &def->nparallels, + &def->parallels) < 0) + goto error; + if (qemudParseCharXMLDevices(conn, ctxt, + "/domain/devices/serial", + &def->nserials, + &def->serials) < 0) + goto error; + + /* + * If no serial devices were listed, then look for console + * devices which is the legacy syntax for the same thing + */ + if (def->nserials == 0) { + obj = xmlXPathEval(BAD_CAST "/domain/devices/console", ctxt); + if ((obj != NULL) && (obj->type == XPATH_NODESET) && + (obj->nodesetval != NULL) && (obj->nodesetval->nodeNr == 1)) { + struct qemud_vm_chr_def *chr = calloc(1, sizeof(*chr)); + if (!chr) { + qemudReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, + "%s", + _("failed to allocate space for char device")); + goto error; + } + + if (qemudParseCharXML(conn, chr, 0, obj->nodesetval->nodeTab[0]) < 0) { + free(chr); + goto error; + } + def->nserials = 1; + def->serials = chr; + } + xmlXPathFreeObject(obj); + } /* analysis of the network devices */ @@ -1617,6 +1995,78 @@ qemudNetworkIfaceConnect(virConnectPtr c return NULL; } +static int qemudBuildCommandLineChrDevStr(struct qemud_vm_chr_def *dev, + char *buf, + int buflen) +{ + switch (dev->srcType) { + case QEMUD_CHR_SRC_TYPE_NULL: + strncpy(buf, "null", buflen); + buf[buflen-1] = '\0'; + break; + + case QEMUD_CHR_SRC_TYPE_VC: + strncpy(buf, "vc", buflen); + buf[buflen-1] = '\0'; + break; + + case QEMUD_CHR_SRC_TYPE_PTY: + strncpy(buf, "pty", buflen); + buf[buflen-1] = '\0'; + break; + + case QEMUD_CHR_SRC_TYPE_DEV: + if (snprintf(buf, buflen, "%s", + dev->srcData.file.path) >= buflen) + return -1; + break; + + case QEMUD_CHR_SRC_TYPE_FILE: + if (snprintf(buf, buflen, "file:%s", + dev->srcData.file.path) >= buflen) + return -1; + break; + + case QEMUD_CHR_SRC_TYPE_PIPE: + if (snprintf(buf, buflen, "pipe:%s", + dev->srcData.file.path) >= buflen) + return -1; + break; + + case QEMUD_CHR_SRC_TYPE_STDIO: + strncpy(buf, "stdio", buflen); + buf[buflen-1] = '\0'; + break; + + case QEMUD_CHR_SRC_TYPE_UDP: + if (snprintf(buf, buflen, "udp:%s:%s@%s:%s", + dev->srcData.udp.connectHost, + dev->srcData.udp.connectService, + dev->srcData.udp.bindHost, + dev->srcData.udp.bindService) >= buflen) + return -1; + break; + + case QEMUD_CHR_SRC_TYPE_TCP: + if (snprintf(buf, buflen, "%s:%s:%s%s", + dev->srcData.tcp.protocol == QEMUD_CHR_SRC_TCP_PROTOCOL_TELNET ? "telnet" : "tcp", + dev->srcData.tcp.host, + dev->srcData.tcp.service, + dev->srcData.tcp.listen ? ",listen" : "") >= buflen) + return -1; + break; + + case QEMUD_CHR_SRC_TYPE_UNIX: + if (snprintf(buf, buflen, "unix:%s%s", + dev->srcData.nix.path, + dev->srcData.nix.listen ? ",listen" : "") >= buflen) + return -1; + break; + } + + return 0; +} + /* * Constructs a argv suitable for launching qemu with config defined * for a given virtual machine. @@ -1633,6 +2083,8 @@ int qemudBuildCommandLine(virConnectPtr struct qemud_vm_disk_def *disk = vm->def->disks; struct qemud_vm_net_def *net = vm->def->nets; struct qemud_vm_input_def *input = vm->def->inputs; + struct qemud_vm_chr_def *serial = vm->def->serials; + struct qemud_vm_chr_def *parallel = vm->def->parallels; struct utsname ut; int disableKQEMU = 0; @@ -1681,6 +2133,8 @@ int qemudBuildCommandLine(virConnectPtr (vm->def->nnets > 0 ? (4 * vm->def->nnets) : 2) + /* networks */ 1 + /* usb */ 2 * vm->def->ninputs + /* input devices */ + (vm->def->nserials > 0 ? (2 * vm->def->nserials) : 2) + /* character devices */ + (vm->def->nparallels > 0 ? (2 * vm->def->nparallels) : 2) + /* character devices */ 2 + /* memory*/ 2 + /* cpus */ 2 + /* boot device */ @@ -1913,6 +2367,48 @@ int qemudBuildCommandLine(virConnectPtr } } + if (!serial) { + if (!((*argv)[++n] = strdup("-serial"))) + goto no_memory; + if (!((*argv)[++n] = strdup("none"))) + goto no_memory; + } else { + while (serial) { + char buf[4096]; + + if (qemudBuildCommandLineChrDevStr(serial, buf, sizeof(buf)) < 0) + goto error; + + if (!((*argv)[++n] = strdup("-serial"))) + goto no_memory; + if (!((*argv)[++n] = strdup(buf))) + goto no_memory; + + serial = serial->next; + } + } + + if (!parallel) { + if (!((*argv)[++n] = strdup("-parallel"))) + goto no_memory; + if (!((*argv)[++n] = strdup("none"))) + goto no_memory; + } else { + while (parallel) { + char buf[4096]; + + if (qemudBuildCommandLineChrDevStr(parallel, buf, sizeof(buf)) < 0) + goto error; + + if (!((*argv)[++n] = strdup("-parallel"))) + goto no_memory; + if (!((*argv)[++n] = strdup(buf))) + goto no_memory; + + parallel = parallel->next; + } + } + if (!((*argv)[++n] = strdup("-usb"))) goto no_memory; while (input) { @@ -2838,6 +3334,127 @@ int qemudScanConfigs(struct qemud_driver return 0; } +static int qemudGenerateXMLChar(virBufferPtr buf, + const struct qemud_vm_chr_def *dev, + const char *type) +{ + const char *const types[] = { + "null", + "vc", + "pty", + "dev", + "file", + "pipe", + "stdio", + "udp", + "tcp", + "unix" + }; + /*verify(ARRAY_CARDINALITY(types) == QEMUD_CHR_SRC_TYPE_LAST);*/ + + if (dev->srcType < 0 || dev->srcType >= QEMUD_CHR_SRC_TYPE_LAST) + return -1; + + /* Compat with legacy <console tty='/dev/pts/5'/> syntax */ + if (STREQ(type, "console") && + dev->srcType == QEMUD_CHR_SRC_TYPE_PTY && + dev->srcData.file.path[0] != '\0') { + if (virBufferVSprintf(buf, " <%s type='%s' tty='%s'>\n", + type, types[dev->srcType], + dev->srcData.file.path) < 0) + return -1; + } else { + if (virBufferVSprintf(buf, " <%s type='%s'>\n", + type, types[dev->srcType]) < 0) + return -1; + } + + switch (dev->srcType) { + case QEMUD_CHR_SRC_TYPE_NULL: + case QEMUD_CHR_SRC_TYPE_VC: + case QEMUD_CHR_SRC_TYPE_STDIO: + /* nada */ + break; + + case QEMUD_CHR_SRC_TYPE_PTY: + case QEMUD_CHR_SRC_TYPE_DEV: + case QEMUD_CHR_SRC_TYPE_FILE: + case QEMUD_CHR_SRC_TYPE_PIPE: + if (dev->srcType != QEMUD_CHR_SRC_TYPE_PTY || + dev->srcData.file.path[0]) { + if (virBufferVSprintf(buf, " <source path='%s'/>\n", + dev->srcData.file.path) < 0) + return -1; + } + break; + + case QEMUD_CHR_SRC_TYPE_UDP: + if (dev->srcData.udp.bindService[0] != '\0' && + dev->srcData.udp.bindHost[0] != '\0') { + if (virBufferVSprintf(buf, " <source mode='bind' host='%s' service='%s'/>\n", + dev->srcData.udp.bindHost, + dev->srcData.udp.bindService) < 0) + return -1; + } else if (dev->srcData.udp.bindHost[0] !='\0') { + if (virBufferVSprintf(buf, " <source mode='bind' host='%s'/>\n", + dev->srcData.udp.bindHost) < 0) + return -1; + } else if (dev->srcData.udp.bindService[0] != '\0') { + if (virBufferVSprintf(buf, " <source mode='bind' service='%s'/>\n", + dev->srcData.udp.bindService) < 0) + return -1; + } + + if (dev->srcData.udp.connectService[0] != '\0' && + dev->srcData.udp.connectHost[0] != '\0') { + if (virBufferVSprintf(buf, " <source mode='connect' host='%s' service='%s'/>\n", + dev->srcData.udp.connectHost, + dev->srcData.udp.connectService) < 0) + return -1; + } else if (dev->srcData.udp.connectHost[0] != '\0') { + if (virBufferVSprintf(buf, " <source mode='connect' host='%s'/>\n", + dev->srcData.udp.connectHost) < 0) + return -1; + } else if (dev->srcData.udp.connectService[0] != '\0') { + if (virBufferVSprintf(buf, " <source mode='connect' service='%s'/>\n", + dev->srcData.udp.connectService) < 0) + return -1; + } + + break; + + case QEMUD_CHR_SRC_TYPE_TCP: + if (virBufferVSprintf(buf, " <source mode='%s' host='%s' service='%s'/>\n", + dev->srcData.tcp.listen ? "bind" : "connect", + dev->srcData.tcp.host, + dev->srcData.tcp.service) < 0) + return -1; + if (virBufferVSprintf(buf, " <protocol type='%s'/>\n", + dev->srcData.tcp.protocol == QEMUD_CHR_SRC_TCP_PROTOCOL_TELNET + ? "telnet" : "raw") < 0) + return -1; + break; + + case QEMUD_CHR_SRC_TYPE_UNIX: + if (virBufferVSprintf(buf, " <source mode='%s' path='%s'/>\n", + dev->srcData.nix.listen ? "bind" : "connect", + dev->srcData.nix.path) < 0) + return -1; + break; + } + + if (virBufferVSprintf(buf, " <target port='%d'/>\n", + dev->dstPort) < 0) + return -1; + + if (virBufferVSprintf(buf, " </%s>\n", + type) < 0) + return -1; + + return 0; +} + + /* Generate an XML document describing the guest's configuration */ char *qemudGenerateXML(virConnectPtr conn, struct qemud_driver *driver ATTRIBUTE_UNUSED, @@ -2847,9 +3464,10 @@ char *qemudGenerateXML(virConnectPtr con virBufferPtr buf = 0; unsigned char *uuid; char uuidstr[VIR_UUID_STRING_BUFLEN]; - struct qemud_vm_disk_def *disk; - struct qemud_vm_net_def *net; - struct qemud_vm_input_def *input; + const struct qemud_vm_disk_def *disk; + const struct qemud_vm_net_def *net; + const struct qemud_vm_input_def *input; + const struct qemud_vm_chr_def *chr; const char *type = NULL; int n; @@ -3078,6 +3696,27 @@ char *qemudGenerateXML(virConnectPtr con net = net->next; } + chr = def->serials; + while (chr) { + if (qemudGenerateXMLChar(buf, chr, "serial") < 0) + goto no_memory; + + chr = chr->next; + } + + chr = def->parallels; + while (chr) { + if (qemudGenerateXMLChar(buf, chr, "parallel") < 0) + goto no_memory; + + chr = chr->next; + } + + /* First serial device is the primary console */ + if (def->nserials > 0 && + qemudGenerateXMLChar(buf, def->serials, "console") < 0) + goto no_memory; + input = def->inputs; while (input) { if (input->bus != QEMU_INPUT_BUS_PS2 && @@ -3125,9 +3764,6 @@ char *qemudGenerateXML(virConnectPtr con break; } - if (def->graphicsType == QEMUD_GRAPHICS_VNC) { - } - if (virBufferAddLit(buf, " </devices>\n") < 0) goto no_memory; Index: src/qemu_conf.h =================================================================== RCS file: /data/cvs/libvirt/src/qemu_conf.h,v retrieving revision 1.22 diff -u -p -r1.22 qemu_conf.h --- src/qemu_conf.h 10 Apr 2008 16:53:29 -0000 1.22 +++ src/qemu_conf.h 24 Apr 2008 15:10:00 -0000 @@ -119,6 +119,54 @@ struct qemud_vm_net_def { struct qemud_vm_net_def *next; }; +enum qemu_vm_chr_dst_type { + QEMUD_CHR_SRC_TYPE_NULL, + QEMUD_CHR_SRC_TYPE_VC, + QEMUD_CHR_SRC_TYPE_PTY, + QEMUD_CHR_SRC_TYPE_DEV, + QEMUD_CHR_SRC_TYPE_FILE, + QEMUD_CHR_SRC_TYPE_PIPE, + QEMUD_CHR_SRC_TYPE_STDIO, + QEMUD_CHR_SRC_TYPE_UDP, + QEMUD_CHR_SRC_TYPE_TCP, + QEMUD_CHR_SRC_TYPE_UNIX, + + QEMUD_CHR_SRC_TYPE_LAST, +}; + +enum { + QEMUD_CHR_SRC_TCP_PROTOCOL_RAW, + QEMUD_CHR_SRC_TCP_PROTOCOL_TELNET, +}; + +struct qemud_vm_chr_def { + int dstPort; + + int srcType; + union { + struct { + char path[PATH_MAX]; + } file; /* pty, file, pipe, or device */ + struct { + char host[BR_INET_ADDR_MAXLEN]; + char service[BR_INET_ADDR_MAXLEN]; + int listen; + int protocol; + } tcp; + struct { + char bindHost[BR_INET_ADDR_MAXLEN]; + char bindService[BR_INET_ADDR_MAXLEN]; + char connectHost[BR_INET_ADDR_MAXLEN]; + char connectService[BR_INET_ADDR_MAXLEN]; + } udp; + struct { + char path[PATH_MAX]; + int listen; + } nix; + } srcData; + + struct qemud_vm_chr_def *next; +}; enum qemu_vm_input_type { QEMU_INPUT_TYPE_MOUSE, @@ -215,14 +263,20 @@ struct qemud_vm_def { char vncListen[BR_INET_ADDR_MAXLEN]; char *keymap; - int ndisks; + unsigned int ndisks; struct qemud_vm_disk_def *disks; - int nnets; + unsigned int nnets; struct qemud_vm_net_def *nets; - int ninputs; + unsigned int ninputs; struct qemud_vm_input_def *inputs; + + unsigned int nserials; + struct qemud_vm_chr_def *serials; + + unsigned int nparallels; + struct qemud_vm_chr_def *parallels; }; /* Guest VM runtime state */ Index: src/qemu_driver.c =================================================================== RCS file: /data/cvs/libvirt/src/qemu_driver.c,v retrieving revision 1.67 diff -u -p -r1.67 qemu_driver.c --- src/qemu_driver.c 10 Apr 2008 16:54:54 -0000 1.67 +++ src/qemu_driver.c 24 Apr 2008 15:10:00 -0000 @@ -381,7 +381,6 @@ qemudReadMonitorOutput(virConnectPtr con const char *what) { #define MONITOR_TIMEOUT 3000 - int got = 0; buf[0] = '\0'; @@ -498,48 +497,88 @@ static int qemudOpenMonitor(virConnectPt return ret; } -static int qemudExtractMonitorPath(const char *haystack, char *path, int pathmax) { +static int qemudExtractMonitorPath(const char *haystack, + size_t *offset, + char *path, int pathmax) { static const char needle[] = "char device redirected to"; char *tmp; - if (!(tmp = strstr(haystack, needle))) + /* First look for our magic string */ + if (!(tmp = strstr(haystack + *offset, needle))) return -1; + /* Grab all the trailing data */ strncpy(path, tmp+sizeof(needle), pathmax-1); path[pathmax-1] = '\0'; - while (*path) { - /* - * The monitor path ends at first whitespace char - * so lets search for it & NULL terminate it there - */ - if (isspace(*path)) { - *path = '\0'; + /* + * And look for first whitespace character and nul terminate + * to mark end of the pty path + */ + tmp = path; + while (*tmp) { + if (isspace(*tmp)) { + *tmp = '\0'; + *offset += sizeof(needle) + strlen(path); return 0; } - path++; + tmp++; } /* * We found a path, but didn't find any whitespace, * so it must be still incomplete - we should at - * least see a \n + * least see a \n - indicate that we want to carry + * on trying again */ return -1; } static int -qemudOpenMonitorPath(virConnectPtr conn, - struct qemud_driver *driver, - struct qemud_vm *vm, - const char *output, - int fd ATTRIBUTE_UNUSED) +qemudFindCharDevicePTYs(virConnectPtr conn, + struct qemud_driver *driver, + struct qemud_vm *vm, + const char *output, + int fd ATTRIBUTE_UNUSED) { char monitor[PATH_MAX]; + size_t offset = 0; + struct qemud_vm_chr_def *chr; + + /* The order in which QEMU prints out the PTY paths is + the order in which it procsses its monitor, serial + and parallel device args. This code must match that + ordering.... */ - if (qemudExtractMonitorPath(output, monitor, sizeof(monitor)) < 0) + /* So first comes the monitor device */ + if (qemudExtractMonitorPath(output, &offset, monitor, sizeof(monitor)) < 0) return 1; /* keep reading */ + /* then the serial devices */ + chr = vm->def->serials; + while (chr) { + if (chr->srcType == QEMUD_CHR_SRC_TYPE_PTY) { + if (qemudExtractMonitorPath(output, &offset, + chr->srcData.file.path, + sizeof(chr->srcData.file.path)) < 0) + return 1; /* keep reading */ + } + chr = chr->next; + } + + /* and finally the parallel devices */ + chr = vm->def->parallels; + while (chr) { + if (chr->srcType == QEMUD_CHR_SRC_TYPE_PTY) { + if (qemudExtractMonitorPath(output, &offset, + chr->srcData.file.path, + sizeof(chr->srcData.file.path)) < 0) + return 1; /* keep reading */ + } + chr = chr->next; + } + + /* Got them all, so now open the monitor console */ return qemudOpenMonitor(conn, driver, vm, monitor); } @@ -550,7 +589,7 @@ static int qemudWaitForMonitor(virConnec int ret = qemudReadMonitorOutput(conn, driver, vm, vm->stderr, buf, sizeof(buf), - qemudOpenMonitorPath, + qemudFindCharDevicePTYs, "console"); buf[sizeof(buf)-1] = '\0'; -- |: 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