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 internal.h | 4 qemu_conf.c | 646 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- qemu_conf.h | 60 +++++ 3 files changed, 701 insertions(+), 9 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 18 Apr 2008 20:01:36 -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 18 Apr 2008 20:01:37 -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 *wiremode = 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_TCP) + wiremode = xmlGetProp(cur, BAD_CAST "wiremode"); + + if (chr->srcType == QEMUD_CHR_SRC_TYPE_UDP) { + xmlFree(mode); + mode = NULL; + } + } + } + } + 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 (wiremode != NULL && + STREQ((const char *)wiremode, "telnet")) + chr->srcData.tcp.wiremode = QEMUD_CHR_SRC_TCP_WIRE_MODE_TELNET; + else + chr->srcData.tcp.wiremode = QEMUD_CHR_SRC_TCP_WIRE_MODE_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(wiremode); + 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.wiremode == QEMUD_CHR_SRC_TCP_WIRE_MODE_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,125 @@ 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' wiremode='%s'/>\n", + dev->srcData.tcp.listen ? "bind" : "connect", + dev->srcData.tcp.host, + dev->srcData.tcp.service, + dev->srcData.tcp.wiremode == QEMUD_CHR_SRC_TCP_WIRE_MODE_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 +3462,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 +3694,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 +3762,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 18 Apr 2008 20:01:37 -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_WIRE_MODE_RAW, + QEMUD_CHR_SRC_TCP_WIRE_MODE_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 wiremode; + } 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 */ -- |: 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