[libvirt] [RFC PATCHv2 0/5] Smartcard support

Posting now, even though the patch series is not quite polished (still lacking a final test), in order to start the review cycle. Eric Blake (5): domain_conf: split source data out from ChrDef qemu: move monitor device out of domain_conf common code smartcard: add XML support for <smartcard> device smartcard: add domain conf support WIP: smartcard: turn on qemu support cfg.mk | 2 + docs/formatdomain.html.in | 72 +++ docs/schemas/domain.rng | 37 ++ src/conf/domain_conf.c | 567 +++++++++++++++----- src/conf/domain_conf.h | 82 +++- src/libvirt_private.syms | 5 + src/lxc/lxc_driver.c | 12 +- src/qemu/qemu_cgroup.c | 10 +- src/qemu/qemu_command.c | 181 ++++--- src/qemu/qemu_command.h | 9 +- src/qemu/qemu_domain.c | 11 +- src/qemu/qemu_domain.h | 4 +- src/qemu/qemu_driver.c | 50 +- src/qemu/qemu_hotplug.c | 2 +- src/qemu/qemu_monitor.c | 4 +- src/qemu/qemu_monitor.h | 4 +- src/security/security_dac.c | 8 +- src/security/security_selinux.c | 10 +- src/security/virt-aa-helper.c | 42 +- src/uml/uml_conf.c | 16 +- src/uml/uml_driver.c | 14 +- src/vbox/vbox_tmpl.c | 49 +- src/vmx/vmx.c | 84 ++-- src/xen/xen_driver.c | 6 +- src/xen/xend_internal.c | 87 ++-- .../qemuxml2argv-smartcard-host-certificates.xml | 20 + .../qemuxml2argv-smartcard-host.xml | 16 + .../qemuxml2argv-smartcard-passthrough-tcp.xml | 19 + tests/qemuxml2argvtest.c | 5 +- 29 files changed, 997 insertions(+), 431 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-smartcard-host-certificates.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-smartcard-host.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-smartcard-passthrough-tcp.xml -- 1.7.3.4

This opens up the possibility of reusing the smaller ChrSourceDef for both qemu monitor and a passthrough smartcard device. * src/conf/domain_conf.h (_virDomainChrDef): Factor host details... (_virDomainChrSourceDef): ...into new struct. (virDomainChrSourceDefFree): New prototype. * src/conf/domain_conf.c (virDomainChrDefFree) (virDomainChrDefParseXML, virDomainChrDefFormat): Split... (virDomainChrSourceDefClear, virDomainChrSourceDefFree) (virDomainChrSourceDefParseXML, virDomainChrSourceDefFormat): ...into new functions. (virDomainChrDefParseTargetXML): Update clients to reflect type split. * src/vmx/vmx.c (virVMXParseSerial, virVMXParseParallel) (virVMXFormatSerial, virVMXFormatParallel): Likewise. * src/xen/xen_driver.c (xenUnifiedDomainOpenConsole): Likewise. * src/xen/xend_internal.c (xenDaemonParseSxprChar) (xenDaemonFormatSxprChr): Likewise. * src/vbox/vbox_tmpl.c (vboxDomainDumpXML, vboxAttachSerial) (vboxAttachParallel): Likewise. * src/security/security_dac.c (virSecurityDACSetChardevLabel) (virSecurityDACSetChardevCallback) (virSecurityDACRestoreChardevLabel) (virSecurityDACRestoreChardevCallback): Likewise. * src/security/security_selinux.c (SELinuxSetSecurityChardevLabel) (SELinuxSetSecurityChardevCallback) (SELinuxRestoreSecurityChardevLabel) (SELinuxSetSecurityChardevCallback): Likewise. * src/security/virt-aa-helper.c (get_files): Likewise. * src/lxc/lxc_driver.c (lxcVmStart, lxcDomainOpenConsole): Likewise. * src/uml/uml_conf.c (umlBuildCommandLineChr): Likewise. * src/uml/uml_driver.c (umlIdentifyOneChrPTY, umlIdentifyChrPTY) (umlDomainOpenConsole): Likewise. * src/qemu/qemu_command.c (qemuBuildChrChardevStr) (qemuBuildChrArgStr, qemuBuildCommandLine) (qemuParseCommandLineChr): Likewise. * src/qemu/qemu_domain.c (qemuDomainObjPrivateXMLFormat) (qemuDomainObjPrivateXMLParse): Likewise. * src/qemu/qemu_cgroup.c (qemuSetupChardevCgroup): Likewise. * src/qemu/qemu_hotplug.c (qemuDomainAttachNetDevice): Likewise. * src/qemu/qemu_driver.c (qemudFindCharDevicePTYsMonitor) (qemudFindCharDevicePTYs, qemuPrepareChardevDevice) (qemuPrepareMonitorChr, qemudShutdownVMDaemon) (qemuDomainOpenConsole): Likewise. * src/qemu/qemu_command.h (qemuBuildChrChardevStr) (qemuBuildChrArgStr): Delete, now that they are static. * src/libvirt_private.syms (domain_conf.h): New exports. * cfg.mk (useless_free_options): Update list. * tests/qemuxml2argvtest.c (testCompareXMLToArgvFiles): Update tests. --- cfg.mk | 1 + src/conf/domain_conf.c | 342 +++++++++++++++++++++++--------------- src/conf/domain_conf.h | 39 +++-- src/libvirt_private.syms | 1 + src/lxc/lxc_driver.c | 12 +- src/qemu/qemu_cgroup.c | 10 +- src/qemu/qemu_command.c | 133 ++++++++------- src/qemu/qemu_command.h | 7 +- src/qemu/qemu_domain.c | 22 ++-- src/qemu/qemu_driver.c | 45 +++--- src/qemu/qemu_hotplug.c | 6 +- src/qemu/qemu_monitor.c | 10 +- src/security/security_dac.c | 8 +- src/security/security_selinux.c | 10 +- src/security/virt-aa-helper.c | 42 +++--- src/uml/uml_conf.c | 16 +- src/uml/uml_driver.c | 14 +- src/vbox/vbox_tmpl.c | 49 +++--- src/vmx/vmx.c | 84 +++++----- src/xen/xen_driver.c | 6 +- src/xen/xend_internal.c | 87 ++++++---- tests/qemuxml2argvtest.c | 6 +- 22 files changed, 530 insertions(+), 420 deletions(-) diff --git a/cfg.mk b/cfg.mk index 03186b3..d4c791a 100644 --- a/cfg.mk +++ b/cfg.mk @@ -85,6 +85,7 @@ useless_free_options = \ --name=virConfFreeList \ --name=virConfFreeValue \ --name=virDomainChrDefFree \ + --name=virDomainChrSourceDefFree \ --name=virDomainControllerDefFree \ --name=virDomainDefFree \ --name=virDomainDeviceDefFree \ diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 2c54683..0ab2e02 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -1,7 +1,7 @@ /* * domain_conf.c: domain XML processing * - * Copyright (C) 2006-2010 Red Hat, Inc. + * Copyright (C) 2006-2011 Red Hat, Inc. * Copyright (C) 2006-2008 Daniel P. Berrange * * This library is free software; you can redistribute it and/or @@ -614,28 +614,9 @@ void virDomainNetDefFree(virDomainNetDefPtr def) VIR_FREE(def); } -void virDomainChrDefFree(virDomainChrDefPtr def) +static void ATTRIBUTE_NONNULL(1) +virDomainChrSourceDefClear(virDomainChrSourceDefPtr def) { - if (!def) - return; - - switch (def->deviceType) { - case VIR_DOMAIN_CHR_DEVICE_TYPE_CHANNEL: - switch (def->targetType) { - case VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_GUESTFWD: - VIR_FREE(def->target.addr); - break; - - case VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO: - VIR_FREE(def->target.name); - break; - } - break; - - default: - break; - } - switch (def->type) { case VIR_DOMAIN_CHR_TYPE_PTY: case VIR_DOMAIN_CHR_TYPE_DEV: @@ -660,7 +641,41 @@ void virDomainChrDefFree(virDomainChrDefPtr def) VIR_FREE(def->data.nix.path); break; } +} + +void virDomainChrSourceDefFree(virDomainChrSourceDefPtr def) +{ + if (!def) + return; + + virDomainChrSourceDefClear(def); + + VIR_FREE(def); +} +void virDomainChrDefFree(virDomainChrDefPtr def) +{ + if (!def) + return; + + switch (def->deviceType) { + case VIR_DOMAIN_CHR_DEVICE_TYPE_CHANNEL: + switch (def->targetType) { + case VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_GUESTFWD: + VIR_FREE(def->target.addr); + break; + + case VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO: + VIR_FREE(def->target.name); + break; + } + break; + + default: + break; + } + + virDomainChrSourceDefClear(&def->source); virDomainDeviceInfoClear(&def->info); VIR_FREE(def); @@ -2761,51 +2776,15 @@ error: return ret; } - -/* Parse the XML definition for a character device - * @param node XML nodeset to parse for net definition - * - * 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"/> - * <protocol type='raw'/> - * </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 virDomainChrDefPtr -virDomainChrDefParseXML(virCapsPtr caps, - xmlNodePtr node, - int flags) { - xmlNodePtr cur; - char *type = NULL; +/* Parse the source half of the XML definition for a character device, + * where node is the first element of node->children of the parent + * element. def->type must already be valid. Return -1 on failure, + * otherwise the number of ignored children (this intentionally skips + * <target>, which is used by <serial> but not <smartcard>). */ +static int +virDomainChrSourceDefParseXML(virDomainChrSourceDefPtr def, + xmlNodePtr cur) +{ char *bindHost = NULL; char *bindService = NULL; char *connectHost = NULL; @@ -2813,32 +2792,8 @@ virDomainChrDefParseXML(virCapsPtr caps, char *path = NULL; char *mode = NULL; char *protocol = NULL; - const char *nodeName; - virDomainChrDefPtr def; - - if (VIR_ALLOC(def) < 0) { - virReportOOMError(); - return NULL; - } - - type = virXMLPropString(node, "type"); - if (type == NULL) { - def->type = VIR_DOMAIN_CHR_TYPE_PTY; - } else if ((def->type = virDomainChrTypeFromString(type)) < 0) { - virDomainReportError(VIR_ERR_XML_ERROR, - _("unknown type presented to host for character device: %s"), - type); - goto error; - } + int remaining = 0; - nodeName = (const char *) node->name; - if ((def->deviceType = virDomainChrDeviceTypeFromString(nodeName)) < 0) { - virDomainReportError(VIR_ERR_XML_ERROR, - _("unknown character device type: %s"), - nodeName); - } - - cur = node->children; while (cur != NULL) { if (cur->type == XML_ELEMENT_NODE) { if (xmlStrEqual(cur->name, BAD_CAST "source")) { @@ -2883,10 +2838,8 @@ virDomainChrDefParseXML(virCapsPtr caps, } else if (xmlStrEqual(cur->name, BAD_CAST "protocol")) { if (protocol == NULL) protocol = virXMLPropString(cur, "type"); - } else if (xmlStrEqual(cur->name, BAD_CAST "target")) { - if (virDomainChrDefParseTargetXML(caps, def, cur, flags) < 0) { - goto error; - } + } else { + remaining++; } } cur = cur->next; @@ -2937,7 +2890,7 @@ virDomainChrDefParseXML(virCapsPtr caps, connectHost = NULL; def->data.tcp.service = connectService; connectService = NULL; - def->data.tcp.listen = 0; + def->data.tcp.listen = false; } else { if (bindHost == NULL) { virDomainReportError(VIR_ERR_INTERNAL_ERROR, "%s", @@ -2954,7 +2907,7 @@ virDomainChrDefParseXML(virCapsPtr caps, bindHost = NULL; def->data.tcp.service = bindService; bindService = NULL; - def->data.tcp.listen = 1; + def->data.tcp.listen = true; } if (protocol == NULL) @@ -2993,30 +2946,124 @@ virDomainChrDefParseXML(virCapsPtr caps, goto error; } - if (mode != NULL && - STRNEQ(mode, "connect")) - def->data.nix.listen = 1; - else - def->data.nix.listen = 0; + def->data.nix.listen = mode != NULL && STRNEQ(mode, "connect"); def->data.nix.path = path; path = NULL; break; } - if (virDomainDeviceInfoParseXML(node, &def->info, flags) < 0) - goto error; - cleanup: VIR_FREE(mode); VIR_FREE(protocol); - VIR_FREE(type); VIR_FREE(bindHost); VIR_FREE(bindService); VIR_FREE(connectHost); VIR_FREE(connectService); VIR_FREE(path); + return remaining; + +error: + virDomainChrSourceDefClear(def); + remaining = -1; + goto cleanup; +} + +/* Parse the XML definition for a character device + * @param node XML nodeset to parse for net definition + * + * 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"/> + * <protocol type='raw'/> + * </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 virDomainChrDefPtr +virDomainChrDefParseXML(virCapsPtr caps, + xmlNodePtr node, + int flags) { + xmlNodePtr cur; + char *type = NULL; + const char *nodeName; + virDomainChrDefPtr def; + int remaining; + + if (VIR_ALLOC(def) < 0) { + virReportOOMError(); + return NULL; + } + + type = virXMLPropString(node, "type"); + if (type == NULL) { + def->source.type = VIR_DOMAIN_CHR_TYPE_PTY; + } else if ((def->source.type = virDomainChrTypeFromString(type)) < 0) { + virDomainReportError(VIR_ERR_XML_ERROR, + _("unknown type presented to host for character device: %s"), + type); + goto error; + } + + nodeName = (const char *) node->name; + if ((def->deviceType = virDomainChrDeviceTypeFromString(nodeName)) < 0) { + virDomainReportError(VIR_ERR_XML_ERROR, + _("unknown character device type: %s"), + nodeName); + } + + cur = node->children; + remaining = virDomainChrSourceDefParseXML(&def->source, cur); + if (remaining < 0) + goto error; + if (remaining) { + while (cur != NULL) { + if (cur->type == XML_ELEMENT_NODE) { + if (xmlStrEqual(cur->name, BAD_CAST "target")) { + if (virDomainChrDefParseTargetXML(caps, def, cur, + flags) < 0) { + goto error; + } + } + } + cur = cur->next; + } + } + + if (virDomainDeviceInfoParseXML(node, &def->info, flags) < 0) + goto error; + +cleanup: + VIR_FREE(type); + return def; error: @@ -6300,17 +6347,15 @@ virDomainNetDefFormat(virBufferPtr buf, } +/* Assumes that "<device" has already been generated, and starts + * output at " type='type'>". */ static int -virDomainChrDefFormat(virBufferPtr buf, - virDomainChrDefPtr def, - int flags) +virDomainChrSourceDefFormat(virBufferPtr buf, + virDomainChrSourceDefPtr def, + bool tty_compat, + int flags) { const char *type = virDomainChrTypeToString(def->type); - const char *elementName = virDomainChrDeviceTypeToString(def->deviceType); - const char *targetType = virDomainChrTargetTypeToString(def->deviceType, - def->targetType); - - int ret = 0; if (!type) { virDomainReportError(VIR_ERR_INTERNAL_ERROR, @@ -6318,21 +6363,9 @@ virDomainChrDefFormat(virBufferPtr buf, return -1; } - if (!elementName) { - virDomainReportError(VIR_ERR_INTERNAL_ERROR, - _("unexpected char device type %d"), - def->deviceType); - return -1; - } - /* Compat with legacy <console tty='/dev/pts/5'/> syntax */ - virBufferVSprintf(buf, " <%s type='%s'", - elementName, type); - if (def->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE && - def->target.port == 0 && - def->type == VIR_DOMAIN_CHR_TYPE_PTY && - !(flags & VIR_DOMAIN_XML_INACTIVE) && - def->data.file.path) { + virBufferVSprintf(buf, " type='%s'", type); + if (tty_compat) { virBufferEscapeString(buf, " tty='%s'", def->data.file.path); } @@ -6350,7 +6383,8 @@ virDomainChrDefFormat(virBufferPtr buf, case VIR_DOMAIN_CHR_TYPE_FILE: case VIR_DOMAIN_CHR_TYPE_PIPE: if (def->type != VIR_DOMAIN_CHR_TYPE_PTY || - (def->data.file.path && !(flags & VIR_DOMAIN_XML_INACTIVE))) { + (def->data.file.path && + !(flags & VIR_DOMAIN_XML_INACTIVE))) { virBufferEscapeString(buf, " <source path='%s'/>\n", def->data.file.path); } @@ -6359,7 +6393,9 @@ virDomainChrDefFormat(virBufferPtr buf, case VIR_DOMAIN_CHR_TYPE_UDP: if (def->data.udp.bindService && def->data.udp.bindHost) { - virBufferVSprintf(buf, " <source mode='bind' host='%s' service='%s'/>\n", + virBufferVSprintf(buf, + " <source mode='bind' host='%s' " + "service='%s'/>\n", def->data.udp.bindHost, def->data.udp.bindService); } else if (def->data.udp.bindHost) { @@ -6372,25 +6408,30 @@ virDomainChrDefFormat(virBufferPtr buf, if (def->data.udp.connectService && def->data.udp.connectHost) { - virBufferVSprintf(buf, " <source mode='connect' host='%s' service='%s'/>\n", + virBufferVSprintf(buf, + " <source mode='connect' host='%s' " + "service='%s'/>\n", def->data.udp.connectHost, def->data.udp.connectService); } else if (def->data.udp.connectHost) { virBufferVSprintf(buf, " <source mode='connect' host='%s'/>\n", def->data.udp.connectHost); } else if (def->data.udp.connectService) { - virBufferVSprintf(buf, " <source mode='connect' service='%s'/>\n", + virBufferVSprintf(buf, + " <source mode='connect' service='%s'/>\n", def->data.udp.connectService); } break; case VIR_DOMAIN_CHR_TYPE_TCP: - virBufferVSprintf(buf, " <source mode='%s' host='%s' service='%s'/>\n", + virBufferVSprintf(buf, + " <source mode='%s' host='%s' service='%s'/>\n", def->data.tcp.listen ? "bind" : "connect", def->data.tcp.host, def->data.tcp.service); virBufferVSprintf(buf, " <protocol type='%s'/>\n", - virDomainChrTcpProtocolTypeToString(def->data.tcp.protocol)); + virDomainChrTcpProtocolTypeToString( + def->data.tcp.protocol)); break; case VIR_DOMAIN_CHR_TYPE_UNIX: @@ -6401,6 +6442,37 @@ virDomainChrDefFormat(virBufferPtr buf, break; } + return 0; +} + +static int +virDomainChrDefFormat(virBufferPtr buf, + virDomainChrDefPtr def, + int flags) +{ + const char *elementName = virDomainChrDeviceTypeToString(def->deviceType); + const char *targetType = virDomainChrTargetTypeToString(def->deviceType, + def->targetType); + bool tty_compat; + + int ret = 0; + + if (!elementName) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + _("unexpected char device type %d"), + def->deviceType); + return -1; + } + + virBufferVSprintf(buf, " <%s", elementName); + tty_compat = (def->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE && + def->target.port == 0 && + def->source.type == VIR_DOMAIN_CHR_TYPE_PTY && + !(flags & VIR_DOMAIN_XML_INACTIVE) && + def->source.data.file.path); + if (virDomainChrSourceDefFormat(buf, &def->source, tty_compat, flags) < 0) + return -1; + /* Format <target> block */ switch (def->deviceType) { case VIR_DOMAIN_CHR_DEVICE_TYPE_CHANNEL: { diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 6a8ec64..ebc725c 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1,7 +1,7 @@ /* * domain_conf.h: domain XML processing * - * Copyright (C) 2006-2008, 2010 Red Hat, Inc. + * Copyright (C) 2006-2011 Red Hat, Inc. * Copyright (C) 2006-2008 Daniel P. Berrange * * This library is free software; you can redistribute it and/or @@ -393,18 +393,11 @@ enum virDomainChrTcpProtocol { VIR_DOMAIN_CHR_TCP_PROTOCOL_LAST, }; -typedef struct _virDomainChrDef virDomainChrDef; -typedef virDomainChrDef *virDomainChrDefPtr; -struct _virDomainChrDef { - int deviceType; - int targetType; - union { - int port; /* parallel, serial, console */ - virSocketAddrPtr addr; /* guestfwd */ - char *name; /* virtio */ - } target; - - int type; +/* The host side information for a character device. */ +typedef struct _virDomainChrSourceDef virDomainChrSourceDef; +typedef virDomainChrSourceDef *virDomainChrSourceDefPtr; +struct _virDomainChrSourceDef { + int type; /* virDomainChrType */ union { struct { char *path; @@ -412,7 +405,7 @@ struct _virDomainChrDef { struct { char *host; char *service; - int listen; + bool listen; int protocol; } tcp; struct { @@ -423,9 +416,24 @@ struct _virDomainChrDef { } udp; struct { char *path; - int listen; + bool listen; } nix; } data; +}; + +/* A complete character device, both host and domain views. */ +typedef struct _virDomainChrDef virDomainChrDef; +typedef virDomainChrDef *virDomainChrDefPtr; +struct _virDomainChrDef { + int deviceType; + int targetType; + union { + int port; /* parallel, serial, console */ + virSocketAddrPtr addr; /* guestfwd */ + char *name; /* virtio */ + } target; + + virDomainChrSourceDef source; virDomainDeviceInfo info; }; @@ -1077,6 +1085,7 @@ void virDomainControllerDefFree(virDomainControllerDefPtr def); void virDomainFSDefFree(virDomainFSDefPtr def); void virDomainNetDefFree(virDomainNetDefPtr def); void virDomainChrDefFree(virDomainChrDefPtr def); +void virDomainChrSourceDefFree(virDomainChrSourceDefPtr def); void virDomainSoundDefFree(virDomainSoundDefPtr def); void virDomainMemballoonDefFree(virDomainMemballoonDefPtr def); void virDomainWatchdogDefFree(virDomainWatchdogDefPtr def); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index d95ef31..2ce4bed 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -192,6 +192,7 @@ virDomainChrConsoleTargetTypeFromString; virDomainChrConsoleTargetTypeToString; virDomainChrDefForeach; virDomainChrDefFree; +virDomainChrSourceDefFree; virDomainChrTcpProtocolTypeFromString; virDomainChrTcpProtocolTypeToString; virDomainChrTypeFromString; diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index eb58086..5aaaca6 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Red Hat, Inc. + * Copyright (C) 2010-2011 Red Hat, Inc. * Copyright IBM Corp. 2008 * * lxc_driver.c: linux container driver functions @@ -1494,9 +1494,9 @@ static int lxcVmStart(virConnectPtr conn, goto cleanup; } if (vm->def->console && - vm->def->console->type == VIR_DOMAIN_CHR_TYPE_PTY) { - VIR_FREE(vm->def->console->data.file.path); - vm->def->console->data.file.path = parentTtyPath; + vm->def->console->source.type == VIR_DOMAIN_CHR_TYPE_PTY) { + VIR_FREE(vm->def->console->source.data.file.path); + vm->def->console->source.data.file.path = parentTtyPath; } else { VIR_FREE(parentTtyPath); } @@ -2804,13 +2804,13 @@ lxcDomainOpenConsole(virDomainPtr dom, goto cleanup; } - if (chr->type != VIR_DOMAIN_CHR_TYPE_PTY) { + if (chr->source.type != VIR_DOMAIN_CHR_TYPE_PTY) { lxcError(VIR_ERR_INTERNAL_ERROR, _("character device %s is not using a PTY"), devname); goto cleanup; } - if (virFDStreamOpenFile(st, chr->data.file.path, O_RDWR) < 0) + if (virFDStreamOpenFile(st, chr->source.data.file.path, O_RDWR) < 0) goto cleanup; ret = 0; diff --git a/src/qemu/qemu_cgroup.c b/src/qemu/qemu_cgroup.c index 180e7c4..e5536c0 100644 --- a/src/qemu/qemu_cgroup.c +++ b/src/qemu/qemu_cgroup.c @@ -1,7 +1,7 @@ /* * qemu_cgroup.c: QEMU cgroup management * - * Copyright (C) 2006-2007, 2009-2010 Red Hat, Inc. + * Copyright (C) 2006-2011 Red Hat, Inc. * Copyright (C) 2006 Daniel P. Berrange * * This library is free software; you can redistribute it and/or @@ -142,16 +142,16 @@ int qemuSetupChardevCgroup(virDomainDefPtr def, virCgroupPtr cgroup = opaque; int rc; - if (dev->type != VIR_DOMAIN_CHR_TYPE_DEV) + if (dev->source.type != VIR_DOMAIN_CHR_TYPE_DEV) return 0; - VIR_DEBUG("Process path '%s' for disk", dev->data.file.path); - rc = virCgroupAllowDevicePath(cgroup, dev->data.file.path); + VIR_DEBUG("Process path '%s' for disk", dev->source.data.file.path); + rc = virCgroupAllowDevicePath(cgroup, dev->source.data.file.path); if (rc != 0) { virReportSystemError(-rc, _("Unable to allow device %s for %s"), - dev->data.file.path, def->name); + dev->source.data.file.path, def->name); return -1; } diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 86c5bb5..6bc2f8e 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -1,7 +1,7 @@ /* * qemu_command.c: QEMU command generation * - * Copyright (C) 2006-2007, 2009-2010 Red Hat, Inc. + * Copyright (C) 2006-2011 Red Hat, Inc. * Copyright (C) 2006 Daniel P. Berrange * * This library is free software; you can redistribute it and/or @@ -1896,45 +1896,48 @@ qemuBuildUSBHostdevUsbDevStr(virDomainHostdevDefPtr dev) /* This function outputs a -chardev command line option which describes only the * host side of the character device */ -char * -qemuBuildChrChardevStr(virDomainChrDefPtr dev) +static char * +qemuBuildChrChardevStr(virDomainChrSourceDefPtr dev, const char *alias) { virBuffer buf = VIR_BUFFER_INITIALIZER; bool telnet; switch(dev->type) { case VIR_DOMAIN_CHR_TYPE_NULL: - virBufferVSprintf(&buf, "null,id=%s", dev->info.alias); + virBufferVSprintf(&buf, "null,id=%s", alias); break; case VIR_DOMAIN_CHR_TYPE_VC: - virBufferVSprintf(&buf, "vc,id=%s", dev->info.alias); + virBufferVSprintf(&buf, "vc,id=%s", alias); break; case VIR_DOMAIN_CHR_TYPE_PTY: - virBufferVSprintf(&buf, "pty,id=%s", dev->info.alias); + virBufferVSprintf(&buf, "pty,id=%s", alias); break; case VIR_DOMAIN_CHR_TYPE_DEV: - virBufferVSprintf(&buf, "tty,id=%s,path=%s", dev->info.alias, dev->data.file.path); + virBufferVSprintf(&buf, "tty,id=%s,path=%s", alias, + dev->data.file.path); break; case VIR_DOMAIN_CHR_TYPE_FILE: - virBufferVSprintf(&buf, "file,id=%s,path=%s", dev->info.alias, dev->data.file.path); + virBufferVSprintf(&buf, "file,id=%s,path=%s", alias, + dev->data.file.path); break; case VIR_DOMAIN_CHR_TYPE_PIPE: - virBufferVSprintf(&buf, "pipe,id=%s,path=%s", dev->info.alias, dev->data.file.path); + virBufferVSprintf(&buf, "pipe,id=%s,path=%s", alias, + dev->data.file.path); break; case VIR_DOMAIN_CHR_TYPE_STDIO: - virBufferVSprintf(&buf, "stdio,id=%s", dev->info.alias); + virBufferVSprintf(&buf, "stdio,id=%s", alias); break; case VIR_DOMAIN_CHR_TYPE_UDP: virBufferVSprintf(&buf, "udp,id=%s,host=%s,port=%s,localaddr=%s,localport=%s", - dev->info.alias, + alias, dev->data.udp.connectHost, dev->data.udp.connectService, dev->data.udp.bindHost, @@ -1945,7 +1948,7 @@ qemuBuildChrChardevStr(virDomainChrDefPtr dev) telnet = dev->data.tcp.protocol == VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNET; virBufferVSprintf(&buf, "socket,id=%s,host=%s,port=%s%s%s", - dev->info.alias, + alias, dev->data.tcp.host, dev->data.tcp.service, telnet ? ",telnet" : "", @@ -1955,7 +1958,7 @@ qemuBuildChrChardevStr(virDomainChrDefPtr dev) case VIR_DOMAIN_CHR_TYPE_UNIX: virBufferVSprintf(&buf, "socket,id=%s,path=%s%s", - dev->info.alias, + alias, dev->data.nix.path, dev->data.nix.listen ? ",server,nowait" : ""); break; @@ -1974,8 +1977,8 @@ error: } -char * -qemuBuildChrArgStr(virDomainChrDefPtr dev, const char *prefix) +static char * +qemuBuildChrArgStr(virDomainChrSourceDefPtr dev, const char *prefix) { virBuffer buf = VIR_BUFFER_INITIALIZER; @@ -2731,7 +2734,8 @@ qemuBuildCommandLine(virConnectPtr conn, if (qemuCmdFlags & QEMUD_CMD_FLAG_CHARDEV) { virCommandAddArg(cmd, "-chardev"); - if (!(chrdev = qemuBuildChrChardevStr(monitor_chr))) + if (!(chrdev = qemuBuildChrChardevStr(&monitor_chr->source, + monitor_chr->info.alias))) goto error; virCommandAddArg(cmd, chrdev); VIR_FREE(chrdev); @@ -2745,7 +2749,7 @@ qemuBuildCommandLine(virConnectPtr conn, prefix = "control,"; virCommandAddArg(cmd, "-monitor"); - if (!(chrdev = qemuBuildChrArgStr(monitor_chr, prefix))) + if (!(chrdev = qemuBuildChrArgStr(&monitor_chr->source, prefix))) goto error; virCommandAddArg(cmd, chrdev); VIR_FREE(chrdev); @@ -3342,7 +3346,8 @@ qemuBuildCommandLine(virConnectPtr conn, if ((qemuCmdFlags & QEMUD_CMD_FLAG_CHARDEV) && (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { virCommandAddArg(cmd, "-chardev"); - if (!(devstr = qemuBuildChrChardevStr(serial))) + if (!(devstr = qemuBuildChrChardevStr(&serial->source, + serial->info.alias))) goto error; virCommandAddArg(cmd, devstr); VIR_FREE(devstr); @@ -3352,7 +3357,7 @@ qemuBuildCommandLine(virConnectPtr conn, serial->info.alias); } else { virCommandAddArg(cmd, "-serial"); - if (!(devstr = qemuBuildChrArgStr(serial, NULL))) + if (!(devstr = qemuBuildChrArgStr(&serial->source, NULL))) goto error; virCommandAddArg(cmd, devstr); VIR_FREE(devstr); @@ -3373,7 +3378,8 @@ qemuBuildCommandLine(virConnectPtr conn, if ((qemuCmdFlags & QEMUD_CMD_FLAG_CHARDEV) && (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { virCommandAddArg(cmd, "-chardev"); - if (!(devstr = qemuBuildChrChardevStr(parallel))) + if (!(devstr = qemuBuildChrChardevStr(¶llel->source, + parallel->info.alias))) goto error; virCommandAddArg(cmd, devstr); VIR_FREE(devstr); @@ -3383,7 +3389,7 @@ qemuBuildCommandLine(virConnectPtr conn, parallel->info.alias); } else { virCommandAddArg(cmd, "-parallel"); - if (!(devstr = qemuBuildChrArgStr(parallel, NULL))) + if (!(devstr = qemuBuildChrArgStr(¶llel->source, NULL))) goto error; virCommandAddArg(cmd, devstr); VIR_FREE(devstr); @@ -3405,7 +3411,8 @@ qemuBuildCommandLine(virConnectPtr conn, } virCommandAddArg(cmd, "-chardev"); - if (!(devstr = qemuBuildChrChardevStr(channel))) + if (!(devstr = qemuBuildChrChardevStr(&channel->source, + channel->info.alias))) goto error; virCommandAddArg(cmd, devstr); VIR_FREE(devstr); @@ -3431,7 +3438,8 @@ qemuBuildCommandLine(virConnectPtr conn, } virCommandAddArg(cmd, "-chardev"); - if (!(devstr = qemuBuildChrChardevStr(channel))) + if (!(devstr = qemuBuildChrChardevStr(&channel->source, + channel->info.alias))) goto error; virCommandAddArg(cmd, devstr); VIR_FREE(devstr); @@ -3459,7 +3467,8 @@ qemuBuildCommandLine(virConnectPtr conn, } virCommandAddArg(cmd, "-chardev"); - if (!(devstr = qemuBuildChrChardevStr(console))) + if (!(devstr = qemuBuildChrChardevStr(&console->source, + console->info.alias))) goto error; virCommandAddArg(cmd, devstr); VIR_FREE(devstr); @@ -4774,75 +4783,75 @@ qemuParseCommandLineChr(const char *val) goto no_memory; if (STREQ(val, "null")) { - def->type = VIR_DOMAIN_CHR_TYPE_NULL; + def->source.type = VIR_DOMAIN_CHR_TYPE_NULL; } else if (STREQ(val, "vc")) { - def->type = VIR_DOMAIN_CHR_TYPE_VC; + def->source.type = VIR_DOMAIN_CHR_TYPE_VC; } else if (STREQ(val, "pty")) { - def->type = VIR_DOMAIN_CHR_TYPE_PTY; + def->source.type = VIR_DOMAIN_CHR_TYPE_PTY; } else if (STRPREFIX(val, "file:")) { - def->type = VIR_DOMAIN_CHR_TYPE_FILE; - def->data.file.path = strdup(val+strlen("file:")); - if (!def->data.file.path) + def->source.type = VIR_DOMAIN_CHR_TYPE_FILE; + def->source.data.file.path = strdup(val+strlen("file:")); + if (!def->source.data.file.path) goto no_memory; } else if (STRPREFIX(val, "pipe:")) { - def->type = VIR_DOMAIN_CHR_TYPE_PIPE; - def->data.file.path = strdup(val+strlen("pipe:")); - if (!def->data.file.path) + def->source.type = VIR_DOMAIN_CHR_TYPE_PIPE; + def->source.data.file.path = strdup(val+strlen("pipe:")); + if (!def->source.data.file.path) goto no_memory; } else if (STREQ(val, "stdio")) { - def->type = VIR_DOMAIN_CHR_TYPE_STDIO; + def->source.type = VIR_DOMAIN_CHR_TYPE_STDIO; } else if (STRPREFIX(val, "udp:")) { const char *svc1, *host2, *svc2; - def->type = VIR_DOMAIN_CHR_TYPE_UDP; + def->source.type = VIR_DOMAIN_CHR_TYPE_UDP; val += strlen("udp:"); svc1 = strchr(val, ':'); host2 = svc1 ? strchr(svc1, '@') : NULL; svc2 = host2 ? strchr(host2, ':') : NULL; if (svc1) - def->data.udp.connectHost = strndup(val, svc1-val); + def->source.data.udp.connectHost = strndup(val, svc1-val); else - def->data.udp.connectHost = strdup(val); + def->source.data.udp.connectHost = strdup(val); - if (!def->data.udp.connectHost) + if (!def->source.data.udp.connectHost) goto no_memory; if (svc1) { svc1++; if (host2) - def->data.udp.connectService = strndup(svc1, host2-svc1); + def->source.data.udp.connectService = strndup(svc1, host2-svc1); else - def->data.udp.connectService = strdup(svc1); + def->source.data.udp.connectService = strdup(svc1); - if (!def->data.udp.connectService) + if (!def->source.data.udp.connectService) goto no_memory; } if (host2) { host2++; if (svc2) - def->data.udp.bindHost = strndup(host2, svc2-host2); + def->source.data.udp.bindHost = strndup(host2, svc2-host2); else - def->data.udp.bindHost = strdup(host2); + def->source.data.udp.bindHost = strdup(host2); - if (!def->data.udp.bindHost) + if (!def->source.data.udp.bindHost) goto no_memory; } if (svc2) { svc2++; - def->data.udp.bindService = strdup(svc2); - if (!def->data.udp.bindService) + def->source.data.udp.bindService = strdup(svc2); + if (!def->source.data.udp.bindService) goto no_memory; } } else if (STRPREFIX(val, "tcp:") || STRPREFIX(val, "telnet:")) { const char *opt, *svc; - def->type = VIR_DOMAIN_CHR_TYPE_TCP; + def->source.type = VIR_DOMAIN_CHR_TYPE_TCP; if (STRPREFIX(val, "tcp:")) { val += strlen("tcp:"); } else { val += strlen("telnet:"); - def->data.tcp.protocol = VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNET; + def->source.data.tcp.protocol = VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNET; } svc = strchr(val, ':'); if (!svc) { @@ -4852,38 +4861,38 @@ qemuParseCommandLineChr(const char *val) } opt = strchr(svc, ','); if (opt && strstr(opt, "server")) - def->data.tcp.listen = 1; + def->source.data.tcp.listen = true; - def->data.tcp.host = strndup(val, svc-val); - if (!def->data.tcp.host) + def->source.data.tcp.host = strndup(val, svc-val); + if (!def->source.data.tcp.host) goto no_memory; svc++; if (opt) { - def->data.tcp.service = strndup(svc, opt-svc); + def->source.data.tcp.service = strndup(svc, opt-svc); } else { - def->data.tcp.service = strdup(svc); + def->source.data.tcp.service = strdup(svc); } - if (!def->data.tcp.service) + if (!def->source.data.tcp.service) goto no_memory; } else if (STRPREFIX(val, "unix:")) { const char *opt; val += strlen("unix:"); opt = strchr(val, ','); - def->type = VIR_DOMAIN_CHR_TYPE_UNIX; + def->source.type = VIR_DOMAIN_CHR_TYPE_UNIX; if (opt) { if (strstr(opt, "listen")) - def->data.nix.listen = 1; - def->data.nix.path = strndup(val, opt-val); + def->source.data.nix.listen = true; + def->source.data.nix.path = strndup(val, opt-val); } else { - def->data.nix.path = strdup(val); + def->source.data.nix.path = strdup(val); } - if (!def->data.nix.path) + if (!def->source.data.nix.path) goto no_memory; } else if (STRPREFIX(val, "/dev")) { - def->type = VIR_DOMAIN_CHR_TYPE_DEV; - def->data.file.path = strdup(val); - if (!def->data.file.path) + def->source.type = VIR_DOMAIN_CHR_TYPE_DEV; + def->source.data.file.path = strdup(val); + if (!def->source.data.file.path) goto no_memory; } else { qemuReportError(VIR_ERR_INTERNAL_ERROR, diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h index 4c42a10..b84fa3a 100644 --- a/src/qemu/qemu_command.h +++ b/src/qemu/qemu_command.h @@ -1,7 +1,7 @@ /* * qemu_command.h: QEMU command generation * - * Copyright (C) 2006-2007, 2009-2010 Red Hat, Inc. + * Copyright (C) 2006-2011 Red Hat, Inc. * Copyright (C) 2006 Daniel P. Berrange * * This library is free software; you can redistribute it and/or @@ -96,11 +96,6 @@ char * qemuBuildPCIHostdevDevStr(virDomainHostdevDefPtr dev, int qemuOpenPCIConfig(virDomainHostdevDefPtr dev); -/* Current, best practice */ -char * qemuBuildChrChardevStr(virDomainChrDefPtr dev); -/* Legacy, pre device support */ -char * qemuBuildChrArgStr(virDomainChrDefPtr dev, const char *prefix); - char * qemuBuildVirtioSerialPortDevStr(virDomainChrDefPtr dev); /* Legacy, pre device support */ diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 27f353f..48820bb 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -1,7 +1,7 @@ /* * qemu_domain.h: QEMU domain private state * - * Copyright (C) 2006-2007, 2009-2010 Red Hat, Inc. + * Copyright (C) 2006-2011 Red Hat, Inc. * Copyright (C) 2006 Daniel P. Berrange * * This library is free software; you can redistribute it and/or @@ -75,13 +75,13 @@ static int qemuDomainObjPrivateXMLFormat(virBufferPtr buf, void *data) /* priv->monitor_chr is set only for qemu */ if (priv->monConfig) { - switch (priv->monConfig->type) { + switch (priv->monConfig->source.type) { case VIR_DOMAIN_CHR_TYPE_UNIX: - monitorpath = priv->monConfig->data.nix.path; + monitorpath = priv->monConfig->source.data.nix.path; break; default: case VIR_DOMAIN_CHR_TYPE_PTY: - monitorpath = priv->monConfig->data.file.path; + monitorpath = priv->monConfig->source.data.file.path; break; } @@ -89,7 +89,7 @@ static int qemuDomainObjPrivateXMLFormat(virBufferPtr buf, void *data) if (priv->monJSON) virBufferAddLit(buf, " json='1'"); virBufferVSprintf(buf, " type='%s'/>\n", - virDomainChrTypeToString(priv->monConfig->type)); + virDomainChrTypeToString(priv->monConfig->source.type)); } @@ -132,9 +132,9 @@ static int qemuDomainObjPrivateXMLParse(xmlXPathContextPtr ctxt, void *data) tmp = virXPathString("string(./monitor[1]/@type)", ctxt); if (tmp) - priv->monConfig->type = virDomainChrTypeFromString(tmp); + priv->monConfig->source.type = virDomainChrTypeFromString(tmp); else - priv->monConfig->type = VIR_DOMAIN_CHR_TYPE_PTY; + priv->monConfig->source.type = VIR_DOMAIN_CHR_TYPE_PTY; VIR_FREE(tmp); if (virXPathBoolean("count(./monitor[@json = '1']) > 0", ctxt)) { @@ -143,18 +143,18 @@ static int qemuDomainObjPrivateXMLParse(xmlXPathContextPtr ctxt, void *data) priv->monJSON = 0; } - switch (priv->monConfig->type) { + switch (priv->monConfig->source.type) { case VIR_DOMAIN_CHR_TYPE_PTY: - priv->monConfig->data.file.path = monitorpath; + priv->monConfig->source.data.file.path = monitorpath; break; case VIR_DOMAIN_CHR_TYPE_UNIX: - priv->monConfig->data.nix.path = monitorpath; + priv->monConfig->source.data.nix.path = monitorpath; break; default: VIR_FREE(monitorpath); qemuReportError(VIR_ERR_INTERNAL_ERROR, _("unsupported monitor type '%s'"), - virDomainChrTypeToString(priv->monConfig->type)); + virDomainChrTypeToString(priv->monConfig->source.type)); goto error; } diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 9eb9cd5..59f2c14 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -1,7 +1,7 @@ /* * driver.c: core driver methods for managing qemu guests * - * Copyright (C) 2006, 2007, 2008, 2009, 2010 Red Hat, Inc. + * Copyright (C) 2006-2011 Red Hat, Inc. * Copyright (C) 2006 Daniel P. Berrange * * This library is free software; you can redistribute it and/or @@ -1705,7 +1705,7 @@ qemudFindCharDevicePTYsMonitor(virDomainObjPtr vm, #define LOOKUP_PTYS(array, arraylen, idprefix) \ for (i = 0 ; i < (arraylen) ; i++) { \ virDomainChrDefPtr chr = (array)[i]; \ - if (chr->type == VIR_DOMAIN_CHR_TYPE_PTY) { \ + if (chr->source.type == VIR_DOMAIN_CHR_TYPE_PTY) { \ char id[16]; \ \ if (snprintf(id, sizeof(id), idprefix "%i", i) >= sizeof(id)) \ @@ -1713,7 +1713,7 @@ qemudFindCharDevicePTYsMonitor(virDomainObjPtr vm, \ const char *path = (const char *) virHashLookup(paths, id); \ if (path == NULL) { \ - if (chr->data.file.path == NULL) { \ + if (chr->source.data.file.path == NULL) { \ /* neither the log output nor 'info chardev' had a */ \ /* pty path for this chardev, report an error */ \ qemuReportError(VIR_ERR_INTERNAL_ERROR, \ @@ -1726,10 +1726,10 @@ qemudFindCharDevicePTYsMonitor(virDomainObjPtr vm, } \ } \ \ - VIR_FREE(chr->data.file.path); \ - chr->data.file.path = strdup(path); \ + VIR_FREE(chr->source.data.file.path); \ + chr->source.data.file.path = strdup(path); \ \ - if (chr->data.file.path == NULL) { \ + if (chr->source.data.file.path == NULL) { \ virReportOOMError(); \ return -1; \ } \ @@ -1761,9 +1761,9 @@ qemudFindCharDevicePTYs(virDomainObjPtr vm, /* first comes the serial devices */ for (i = 0 ; i < vm->def->nserials ; i++) { virDomainChrDefPtr chr = vm->def->serials[i]; - if (chr->type == VIR_DOMAIN_CHR_TYPE_PTY) { + if (chr->source.type == VIR_DOMAIN_CHR_TYPE_PTY) { if ((ret = qemudExtractTTYPath(output, &offset, - &chr->data.file.path)) != 0) + &chr->source.data.file.path)) != 0) return ret; } } @@ -1771,9 +1771,9 @@ qemudFindCharDevicePTYs(virDomainObjPtr vm, /* then the parallel devices */ for (i = 0 ; i < vm->def->nparallels ; i++) { virDomainChrDefPtr chr = vm->def->parallels[i]; - if (chr->type == VIR_DOMAIN_CHR_TYPE_PTY) { + if (chr->source.type == VIR_DOMAIN_CHR_TYPE_PTY) { if ((ret = qemudExtractTTYPath(output, &offset, - &chr->data.file.path)) != 0) + &chr->source.data.file.path)) != 0) return ret; } } @@ -1781,9 +1781,9 @@ qemudFindCharDevicePTYs(virDomainObjPtr vm, /* then the channel devices */ for (i = 0 ; i < vm->def->nchannels ; i++) { virDomainChrDefPtr chr = vm->def->channels[i]; - if (chr->type == VIR_DOMAIN_CHR_TYPE_PTY) { + if (chr->source.type == VIR_DOMAIN_CHR_TYPE_PTY) { if ((ret = qemudExtractTTYPath(output, &offset, - &chr->data.file.path)) != 0) + &chr->source.data.file.path)) != 0) return ret; } } @@ -2525,13 +2525,14 @@ qemuPrepareChardevDevice(virDomainDefPtr def ATTRIBUTE_UNUSED, void *opaque ATTRIBUTE_UNUSED) { int fd; - if (dev->type != VIR_DOMAIN_CHR_TYPE_FILE) + if (dev->source.type != VIR_DOMAIN_CHR_TYPE_FILE) return 0; - if ((fd = open(dev->data.file.path, O_CREAT | O_APPEND, S_IRUSR|S_IWUSR)) < 0) { + if ((fd = open(dev->source.data.file.path, + O_CREAT | O_APPEND, S_IRUSR|S_IWUSR)) < 0) { virReportSystemError(errno, _("Unable to pre-create chardev file '%s'"), - dev->data.file.path); + dev->source.data.file.path); return -1; } @@ -2574,15 +2575,15 @@ qemuPrepareMonitorChr(struct qemud_driver *driver, { monConfig->deviceType = VIR_DOMAIN_CHR_DEVICE_TYPE_MONITOR; - monConfig->type = VIR_DOMAIN_CHR_TYPE_UNIX; - monConfig->data.nix.listen = 1; + monConfig->source.type = VIR_DOMAIN_CHR_TYPE_UNIX; + monConfig->source.data.nix.listen = true; if (!(monConfig->info.alias = strdup("monitor"))) { virReportOOMError(); return -1; } - if (virAsprintf(&monConfig->data.nix.path, "%s/%s.monitor", + if (virAsprintf(&monConfig->source.data.nix.path, "%s/%s.monitor", driver->libDir, vm) < 0) { virReportOOMError(); return -1; @@ -3017,8 +3018,8 @@ static void qemudShutdownVMDaemon(struct qemud_driver *driver, qemuMonitorClose(priv->mon); if (priv->monConfig) { - if (priv->monConfig->type == VIR_DOMAIN_CHR_TYPE_UNIX) - unlink(priv->monConfig->data.nix.path); + if (priv->monConfig->source.type == VIR_DOMAIN_CHR_TYPE_UNIX) + unlink(priv->monConfig->source.data.nix.path); virDomainChrDefFree(priv->monConfig); priv->monConfig = NULL; } @@ -10289,14 +10290,14 @@ qemuDomainOpenConsole(virDomainPtr dom, goto cleanup; } - if (chr->type != VIR_DOMAIN_CHR_TYPE_PTY) { + if (chr->source.type != VIR_DOMAIN_CHR_TYPE_PTY) { qemuReportError(VIR_ERR_INTERNAL_ERROR, _("character device %s is not using a PTY"), NULLSTR(devname)); goto cleanup; } - if (virFDStreamOpenFile(st, chr->data.file.path, O_RDWR) < 0) + if (virFDStreamOpenFile(st, chr->source.data.file.path, O_RDWR) < 0) goto cleanup; ret = 0; diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 1dc036c..b40150d 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -1,7 +1,7 @@ /* * qemu_hotplug.h: QEMU device hotplug management * - * Copyright (C) 2006-2007, 2009-2010 Red Hat, Inc. + * Copyright (C) 2006-2011 Red Hat, Inc. * Copyright (C) 2006 Daniel P. Berrange * * This library is free software; you can redistribute it and/or @@ -567,7 +567,7 @@ int qemuDomainAttachNetDevice(virConnectPtr conn, if (net->type == VIR_DOMAIN_NET_TYPE_BRIDGE || net->type == VIR_DOMAIN_NET_TYPE_NETWORK) { - if (priv->monConfig->type != VIR_DOMAIN_CHR_TYPE_UNIX) { + if (priv->monConfig->source.type != VIR_DOMAIN_CHR_TYPE_UNIX) { qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("network device type '%s' cannot be attached: " "qemu is not using a unix socket monitor"), @@ -578,7 +578,7 @@ int qemuDomainAttachNetDevice(virConnectPtr conn, if ((tapfd = qemuNetworkIfaceConnect(conn, driver, net, qemuCmdFlags)) < 0) return -1; } else if (net->type == VIR_DOMAIN_NET_TYPE_DIRECT) { - if (priv->monConfig->type != VIR_DOMAIN_CHR_TYPE_UNIX) { + if (priv->monConfig->source.type != VIR_DOMAIN_CHR_TYPE_UNIX) { qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("network device type '%s' cannot be attached: " "qemu is not using a unix socket monitor"), diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 6ad894d..cc8b374 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -1,7 +1,7 @@ /* * qemu_monitor.c: interaction with QEMU monitor console * - * Copyright (C) 2006-2010 Red Hat, Inc. + * Copyright (C) 2006-2011 Red Hat, Inc. * Copyright (C) 2006 Daniel P. Berrange * * This library is free software; you can redistribute it and/or @@ -625,20 +625,20 @@ qemuMonitorOpen(virDomainObjPtr vm, mon->cb = cb; qemuMonitorLock(mon); - switch (config->type) { + switch (config->source.type) { case VIR_DOMAIN_CHR_TYPE_UNIX: mon->hasSendFD = 1; - mon->fd = qemuMonitorOpenUnix(config->data.nix.path); + mon->fd = qemuMonitorOpenUnix(config->source.data.nix.path); break; case VIR_DOMAIN_CHR_TYPE_PTY: - mon->fd = qemuMonitorOpenPty(config->data.file.path); + mon->fd = qemuMonitorOpenPty(config->source.data.file.path); break; default: qemuReportError(VIR_ERR_INTERNAL_ERROR, _("unable to handle monitor type: %s"), - virDomainChrTypeToString(config->type)); + virDomainChrTypeToString(config->source.type)); goto cleanup; } diff --git a/src/security/security_dac.c b/src/security/security_dac.c index de5d011..0f24034 100644 --- a/src/security/security_dac.c +++ b/src/security/security_dac.c @@ -392,7 +392,7 @@ done: static int virSecurityDACSetChardevLabel(virSecurityManagerPtr mgr, - virDomainChrDefPtr dev) + virDomainChrSourceDefPtr dev) { virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr); @@ -430,7 +430,7 @@ done: static int virSecurityDACRestoreChardevLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, - virDomainChrDefPtr dev) + virDomainChrSourceDefPtr dev) { char *in = NULL, *out = NULL; int ret = -1; @@ -472,7 +472,7 @@ virSecurityDACRestoreChardevCallback(virDomainDefPtr def ATTRIBUTE_UNUSED, { virSecurityManagerPtr mgr = opaque; - return virSecurityDACRestoreChardevLabel(mgr, dev); + return virSecurityDACRestoreChardevLabel(mgr, &dev->source); } @@ -531,7 +531,7 @@ virSecurityDACSetChardevCallback(virDomainDefPtr def ATTRIBUTE_UNUSED, { virSecurityManagerPtr mgr = opaque; - return virSecurityDACSetChardevLabel(mgr, dev); + return virSecurityDACSetChardevLabel(mgr, &dev->source); } diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c index d06afde..7b71fd9 100644 --- a/src/security/security_selinux.c +++ b/src/security/security_selinux.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2010 Red Hat, Inc. + * Copyright (C) 2008-2011 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -676,7 +676,7 @@ done: static int SELinuxSetSecurityChardevLabel(virDomainObjPtr vm, - virDomainChrDefPtr dev) + virDomainChrSourceDefPtr dev) { const virSecurityLabelDefPtr secdef = &vm->def->seclabel; @@ -717,7 +717,7 @@ done: static int SELinuxRestoreSecurityChardevLabel(virDomainObjPtr vm, - virDomainChrDefPtr dev) + virDomainChrSourceDefPtr dev) { const virSecurityLabelDefPtr secdef = &vm->def->seclabel; @@ -765,7 +765,7 @@ SELinuxRestoreSecurityChardevCallback(virDomainDefPtr def ATTRIBUTE_UNUSED, { virDomainObjPtr vm = opaque; - return SELinuxRestoreSecurityChardevLabel(vm, dev); + return SELinuxRestoreSecurityChardevLabel(vm, &dev->source); } @@ -1030,7 +1030,7 @@ SELinuxSetSecurityChardevCallback(virDomainDefPtr def ATTRIBUTE_UNUSED, { virDomainObjPtr vm = opaque; - return SELinuxSetSecurityChardevLabel(vm, dev); + return SELinuxSetSecurityChardevLabel(vm, &dev->source); } diff --git a/src/security/virt-aa-helper.c b/src/security/virt-aa-helper.c index 41bc598..b91cf98 100644 --- a/src/security/virt-aa-helper.c +++ b/src/security/virt-aa-helper.c @@ -2,7 +2,7 @@ /* * virt-aa-helper: wrapper program used by AppArmor security driver. * - * Copyright (C) 2010 Red Hat, Inc. + * Copyright (C) 2010-2011 Red Hat, Inc. * Copyright (C) 2009-2010 Canonical Ltd. * * See COPYING.LIB for the License of this software @@ -912,40 +912,40 @@ get_files(vahControl * ctl) for (i = 0; i < ctl->def->nserials; i++) if (ctl->def->serials[i] && - (ctl->def->serials[i]->type == VIR_DOMAIN_CHR_TYPE_PTY || - ctl->def->serials[i]->type == VIR_DOMAIN_CHR_TYPE_DEV || - ctl->def->serials[i]->type == VIR_DOMAIN_CHR_TYPE_FILE || - ctl->def->serials[i]->type == VIR_DOMAIN_CHR_TYPE_PIPE) && - ctl->def->serials[i]->data.file.path) + (ctl->def->serials[i]->source.type == VIR_DOMAIN_CHR_TYPE_PTY || + ctl->def->serials[i]->source.type == VIR_DOMAIN_CHR_TYPE_DEV || + ctl->def->serials[i]->source.type == VIR_DOMAIN_CHR_TYPE_FILE || + ctl->def->serials[i]->source.type == VIR_DOMAIN_CHR_TYPE_PIPE) && + ctl->def->serials[i]->source.data.file.path) if (vah_add_file(&buf, - ctl->def->serials[i]->data.file.path, "rw") != 0) + ctl->def->serials[i]->source.data.file.path, "rw") != 0) goto clean; - if (ctl->def->console && ctl->def->console->data.file.path) - if (vah_add_file(&buf, ctl->def->console->data.file.path, "rw") != 0) + if (ctl->def->console && ctl->def->console->source.data.file.path) + if (vah_add_file(&buf, ctl->def->console->source.data.file.path, "rw") != 0) goto clean; for (i = 0 ; i < ctl->def->nparallels; i++) if (ctl->def->parallels[i] && - (ctl->def->parallels[i]->type == VIR_DOMAIN_CHR_TYPE_PTY || - ctl->def->parallels[i]->type == VIR_DOMAIN_CHR_TYPE_DEV || - ctl->def->parallels[i]->type == VIR_DOMAIN_CHR_TYPE_FILE || - ctl->def->parallels[i]->type == VIR_DOMAIN_CHR_TYPE_PIPE) && - ctl->def->parallels[i]->data.file.path) + (ctl->def->parallels[i]->source.type == VIR_DOMAIN_CHR_TYPE_PTY || + ctl->def->parallels[i]->source.type == VIR_DOMAIN_CHR_TYPE_DEV || + ctl->def->parallels[i]->source.type == VIR_DOMAIN_CHR_TYPE_FILE || + ctl->def->parallels[i]->source.type == VIR_DOMAIN_CHR_TYPE_PIPE) && + ctl->def->parallels[i]->source.data.file.path) if (vah_add_file(&buf, - ctl->def->parallels[i]->data.file.path, + ctl->def->parallels[i]->source.data.file.path, "rw") != 0) goto clean; for (i = 0 ; i < ctl->def->nchannels; i++) if (ctl->def->channels[i] && - (ctl->def->channels[i]->type == VIR_DOMAIN_CHR_TYPE_PTY || - ctl->def->channels[i]->type == VIR_DOMAIN_CHR_TYPE_DEV || - ctl->def->channels[i]->type == VIR_DOMAIN_CHR_TYPE_FILE || - ctl->def->channels[i]->type == VIR_DOMAIN_CHR_TYPE_PIPE) && - ctl->def->channels[i]->data.file.path) + (ctl->def->channels[i]->source.type == VIR_DOMAIN_CHR_TYPE_PTY || + ctl->def->channels[i]->source.type == VIR_DOMAIN_CHR_TYPE_DEV || + ctl->def->channels[i]->source.type == VIR_DOMAIN_CHR_TYPE_FILE || + ctl->def->channels[i]->source.type == VIR_DOMAIN_CHR_TYPE_PIPE) && + ctl->def->channels[i]->source.data.file.path) if (vah_add_file(&buf, - ctl->def->channels[i]->data.file.path, + ctl->def->channels[i]->source.data.file.path, "rw") != 0) goto clean; diff --git a/src/uml/uml_conf.c b/src/uml/uml_conf.c index 55b3ef6..e5dbed9 100644 --- a/src/uml/uml_conf.c +++ b/src/uml/uml_conf.c @@ -1,7 +1,7 @@ /* * uml_conf.c: UML driver configuration * - * Copyright (C) 2006-2010 Red Hat, Inc. + * Copyright (C) 2006-2011 Red Hat, Inc. * Copyright (C) 2006 Daniel P. Berrange * * This library is free software; you can redistribute it and/or @@ -312,7 +312,7 @@ umlBuildCommandLineChr(virDomainChrDefPtr def, { char *ret = NULL; - switch (def->type) { + switch (def->source.type) { case VIR_DOMAIN_CHR_TYPE_NULL: if (virAsprintf(&ret, "%s%d=null", dev, def->target.port) < 0) { virReportOOMError(); @@ -329,7 +329,7 @@ umlBuildCommandLineChr(virDomainChrDefPtr def, case VIR_DOMAIN_CHR_TYPE_DEV: if (virAsprintf(&ret, "%s%d=tty:%s", dev, def->target.port, - def->data.file.path) < 0) { + def->source.data.file.path) < 0) { virReportOOMError(); return NULL; } @@ -343,14 +343,14 @@ umlBuildCommandLineChr(virDomainChrDefPtr def, break; case VIR_DOMAIN_CHR_TYPE_TCP: - if (def->data.tcp.listen != 1) { + if (def->source.data.tcp.listen != 1) { umlReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("only TCP listen is supported for chr device")); return NULL; } if (virAsprintf(&ret, "%s%d=port:%s", dev, def->target.port, - def->data.tcp.service) < 0) { + def->source.data.tcp.service) < 0) { virReportOOMError(); return NULL; } @@ -360,11 +360,11 @@ umlBuildCommandLineChr(virDomainChrDefPtr def, { int fd_out; - if ((fd_out = open(def->data.file.path, + if ((fd_out = open(def->source.data.file.path, O_WRONLY | O_APPEND | O_CREAT, 0660)) < 0) { virReportSystemError(errno, _("failed to open chardev file: %s"), - def->data.file.path); + def->source.data.file.path); return NULL; } if (virAsprintf(&ret, "%s%d=null,fd:%d", dev, def->target.port, fd_out) < 0) { @@ -384,7 +384,7 @@ umlBuildCommandLineChr(virDomainChrDefPtr def, case VIR_DOMAIN_CHR_TYPE_UNIX: default: umlReportError(VIR_ERR_INTERNAL_ERROR, - _("unsupported chr device type %d"), def->type); + _("unsupported chr device type %d"), def->source.type); break; } diff --git a/src/uml/uml_driver.c b/src/uml/uml_driver.c index 92b5153..c6df0a1 100644 --- a/src/uml/uml_driver.c +++ b/src/uml/uml_driver.c @@ -1,7 +1,7 @@ /* * uml_driver.c: core driver methods for managing UML guests * - * Copyright (C) 2006-2010 Red Hat, Inc. + * Copyright (C) 2006-2011 Red Hat, Inc. * Copyright (C) 2006-2008 Daniel P. Berrange * * This library is free software; you can redistribute it and/or @@ -206,8 +206,8 @@ requery: return -1; if (res && STRPREFIX(res, "pts:")) { - VIR_FREE(def->data.file.path); - if ((def->data.file.path = strdup(res + 4)) == NULL) { + VIR_FREE(def->source.data.file.path); + if ((def->source.data.file.path = strdup(res + 4)) == NULL) { virReportOOMError(); VIR_FREE(res); VIR_FREE(cmd); @@ -236,13 +236,13 @@ umlIdentifyChrPTY(struct uml_driver *driver, int i; if (dom->def->console && - dom->def->console->type == VIR_DOMAIN_CHR_TYPE_PTY) + dom->def->console->source.type == VIR_DOMAIN_CHR_TYPE_PTY) if (umlIdentifyOneChrPTY(driver, dom, dom->def->console, "con") < 0) return -1; for (i = 0 ; i < dom->def->nserials; i++) - if (dom->def->serials[i]->type == VIR_DOMAIN_CHR_TYPE_PTY && + if (dom->def->serials[i]->source.type == VIR_DOMAIN_CHR_TYPE_PTY && umlIdentifyOneChrPTY(driver, dom, dom->def->serials[i], "ssl") < 0) return -1; @@ -2124,13 +2124,13 @@ umlDomainOpenConsole(virDomainPtr dom, goto cleanup; } - if (chr->type != VIR_DOMAIN_CHR_TYPE_PTY) { + if (chr->source.type != VIR_DOMAIN_CHR_TYPE_PTY) { umlReportError(VIR_ERR_INTERNAL_ERROR, _("character device %s is not using a PTY"), devname); goto cleanup; } - if (virFDStreamOpenFile(st, chr->data.file.path, O_RDWR) < 0) + if (virFDStreamOpenFile(st, chr->source.data.file.path, O_RDWR) < 0) goto cleanup; ret = 0; diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c index 5ac94c3..77b43f8 100644 --- a/src/vbox/vbox_tmpl.c +++ b/src/vbox/vbox_tmpl.c @@ -8,7 +8,7 @@ */ /* - * Copyright (C) 2010 Red Hat, Inc. + * Copyright (C) 2010-2011 Red Hat, Inc. * Copyright (C) 2008-2009 Sun Microsystems, Inc. * * This file is part of a free software library; you can redistribute @@ -3001,15 +3001,15 @@ static char *vboxDomainDumpXML(virDomainPtr dom, int flags) { serialPort->vtbl->GetHostMode(serialPort, &hostMode); if (hostMode == PortMode_HostPipe) { - def->serials[serialPortIncCount]->type = VIR_DOMAIN_CHR_TYPE_PIPE; + def->serials[serialPortIncCount]->source.type = VIR_DOMAIN_CHR_TYPE_PIPE; } else if (hostMode == PortMode_HostDevice) { - def->serials[serialPortIncCount]->type = VIR_DOMAIN_CHR_TYPE_DEV; + def->serials[serialPortIncCount]->source.type = VIR_DOMAIN_CHR_TYPE_DEV; #if VBOX_API_VERSION >= 3000 } else if (hostMode == PortMode_RawFile) { - def->serials[serialPortIncCount]->type = VIR_DOMAIN_CHR_TYPE_FILE; + def->serials[serialPortIncCount]->source.type = VIR_DOMAIN_CHR_TYPE_FILE; #endif /* VBOX_API_VERSION >= 3000 */ } else { - def->serials[serialPortIncCount]->type = VIR_DOMAIN_CHR_TYPE_NULL; + def->serials[serialPortIncCount]->source.type = VIR_DOMAIN_CHR_TYPE_NULL; } def->serials[serialPortIncCount]->deviceType = VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL; @@ -3026,7 +3026,7 @@ static char *vboxDomainDumpXML(virDomainPtr dom, int flags) { if (pathUtf16) { VBOX_UTF16_TO_UTF8(pathUtf16, &path); - def->serials[serialPortIncCount]->data.file.path = strdup(path); + def->serials[serialPortIncCount]->source.data.file.path = strdup(path); } serialPortIncCount++; @@ -3090,13 +3090,13 @@ static char *vboxDomainDumpXML(virDomainPtr dom, int flags) { def->parallels[parallelPortIncCount]->target.port = 1; } - def->parallels[parallelPortIncCount]->type = VIR_DOMAIN_CHR_TYPE_FILE; + def->parallels[parallelPortIncCount]->source.type = VIR_DOMAIN_CHR_TYPE_FILE; def->parallels[parallelPortIncCount]->deviceType = VIR_DOMAIN_CHR_DEVICE_TYPE_PARALLEL; parallelPort->vtbl->GetPath(parallelPort, &pathUtf16); VBOX_UTF16_TO_UTF8(pathUtf16, &path); - def->parallels[parallelPortIncCount]->data.file.path = strdup(path); + def->parallels[parallelPortIncCount]->source.data.file.path = strdup(path); parallelPortIncCount++; @@ -4267,7 +4267,7 @@ vboxAttachSerial(virDomainDefPtr def, vboxGlobalData *data, IMachine *machine) for (i = 0; (i < def->nserials) && (i < serialPortCount); i++) { ISerialPort *serialPort = NULL; - DEBUG("SerialPort(%d): Type: %d", i, def->serials[i]->type); + DEBUG("SerialPort(%d): Type: %d", i, def->serials[i]->source.type); DEBUG("SerialPort(%d): target.port: %d", i, def->serials[i]->target.port); @@ -4277,8 +4277,9 @@ vboxAttachSerial(virDomainDefPtr def, vboxGlobalData *data, IMachine *machine) serialPort->vtbl->SetEnabled(serialPort, 1); - if (def->serials[i]->data.file.path) { - VBOX_UTF8_TO_UTF16(def->serials[i]->data.file.path, &pathUtf16); + if (def->serials[i]->source.data.file.path) { + VBOX_UTF8_TO_UTF16(def->serials[i]->source.data.file.path, + &pathUtf16); serialPort->vtbl->SetPath(serialPort, pathUtf16); } @@ -4295,20 +4296,20 @@ vboxAttachSerial(virDomainDefPtr def, vboxGlobalData *data, IMachine *machine) serialPort->vtbl->SetIRQ(serialPort, 4); serialPort->vtbl->SetIOBase(serialPort, 1016); DEBUG(" serialPort-%d irq: %d, iobase 0x%x, path: %s", - i, 4, 1016, def->serials[i]->data.file.path); + i, 4, 1016, def->serials[i]->source.data.file.path); } else if (def->serials[i]->target.port == 1) { serialPort->vtbl->SetIRQ(serialPort, 3); serialPort->vtbl->SetIOBase(serialPort, 760); DEBUG(" serialPort-%d irq: %d, iobase 0x%x, path: %s", - i, 3, 760, def->serials[i]->data.file.path); + i, 3, 760, def->serials[i]->source.data.file.path); } - if (def->serials[i]->type == VIR_DOMAIN_CHR_TYPE_DEV) { + if (def->serials[i]->source.type == VIR_DOMAIN_CHR_TYPE_DEV) { serialPort->vtbl->SetHostMode(serialPort, PortMode_HostDevice); - } else if (def->serials[i]->type == VIR_DOMAIN_CHR_TYPE_PIPE) { + } else if (def->serials[i]->source.type == VIR_DOMAIN_CHR_TYPE_PIPE) { serialPort->vtbl->SetHostMode(serialPort, PortMode_HostPipe); #if VBOX_API_VERSION >= 3000 - } else if (def->serials[i]->type == VIR_DOMAIN_CHR_TYPE_FILE) { + } else if (def->serials[i]->source.type == VIR_DOMAIN_CHR_TYPE_FILE) { serialPort->vtbl->SetHostMode(serialPort, PortMode_RawFile); #endif /* VBOX_API_VERSION >= 3000 */ } else { @@ -4345,7 +4346,7 @@ vboxAttachParallel(virDomainDefPtr def, vboxGlobalData *data, IMachine *machine) for (i = 0; (i < def->nparallels) && (i < parallelPortCount); i++) { IParallelPort *parallelPort = NULL; - DEBUG("ParallelPort(%d): Type: %d", i, def->parallels[i]->type); + DEBUG("ParallelPort(%d): Type: %d", i, def->parallels[i]->source.type); DEBUG("ParallelPort(%d): target.port: %d", i, def->parallels[i]->target.port); @@ -4353,28 +4354,28 @@ vboxAttachParallel(virDomainDefPtr def, vboxGlobalData *data, IMachine *machine) if (parallelPort) { PRUnichar *pathUtf16 = NULL; - VBOX_UTF8_TO_UTF16(def->parallels[i]->data.file.path, &pathUtf16); + VBOX_UTF8_TO_UTF16(def->parallels[i]->source.data.file.path, &pathUtf16); /* For now hard code the parallel ports to * LPT1 (Base Addr: 0x378 (decimal: 888), IRQ: 7) * LPT2 (Base Addr: 0x278 (decimal: 632), IRQ: 5) * TODO: make this more flexible */ - if ((def->parallels[i]->type == VIR_DOMAIN_CHR_TYPE_DEV) || - (def->parallels[i]->type == VIR_DOMAIN_CHR_TYPE_PTY) || - (def->parallels[i]->type == VIR_DOMAIN_CHR_TYPE_FILE) || - (def->parallels[i]->type == VIR_DOMAIN_CHR_TYPE_PIPE)) { + if ((def->parallels[i]->source.type == VIR_DOMAIN_CHR_TYPE_DEV) || + (def->parallels[i]->source.type == VIR_DOMAIN_CHR_TYPE_PTY) || + (def->parallels[i]->source.type == VIR_DOMAIN_CHR_TYPE_FILE) || + (def->parallels[i]->source.type == VIR_DOMAIN_CHR_TYPE_PIPE)) { parallelPort->vtbl->SetPath(parallelPort, pathUtf16); if (i == 0) { parallelPort->vtbl->SetIRQ(parallelPort, 7); parallelPort->vtbl->SetIOBase(parallelPort, 888); DEBUG(" parallePort-%d irq: %d, iobase 0x%x, path: %s", - i, 7, 888, def->parallels[i]->data.file.path); + i, 7, 888, def->parallels[i]->source.data.file.path); } else if (i == 1) { parallelPort->vtbl->SetIRQ(parallelPort, 5); parallelPort->vtbl->SetIOBase(parallelPort, 632); DEBUG(" parallePort-%d irq: %d, iobase 0x%x, path: %s", - i, 5, 632, def->parallels[i]->data.file.path); + i, 5, 632, def->parallels[i]->source.data.file.path); } } diff --git a/src/vmx/vmx.c b/src/vmx/vmx.c index c116940..46eeda2 100644 --- a/src/vmx/vmx.c +++ b/src/vmx/vmx.c @@ -2,7 +2,7 @@ /* * vmx.c: VMware VMX parsing/formatting functions * - * Copyright (C) 2010 Red Hat, Inc. + * Copyright (C) 2010-2011 Red Hat, Inc. * Copyright (C) 2009-2010 Matthias Bolte <matthias.bolte@googlemail.com> * * This library is free software; you can redistribute it and/or @@ -401,7 +401,7 @@ def->serials[0]... ->data.tcp.service = <service> # e.g. "telnet://0.0.0.0:42001" ->data.tcp.protocol = <protocol> -->data.tcp.listen = 1 <=> serial0.network.endPoint = "server" # defaults to "server" +->data.tcp.listen = true <=> serial0.network.endPoint = "server" # defaults to "server" ??? <=> serial0.vspc = "foobar" # defaults to <not present>, FIXME: not representable ??? <=> serial0.tryNoRxLoss = "false" # defaults to "false", FIXME: not representable @@ -416,7 +416,7 @@ def->serials[0]... ->data.tcp.service = <service> # e.g. "telnet://192.168.0.17:42001" ->data.tcp.protocol = <protocol> -->data.tcp.listen = 0 <=> serial0.network.endPoint = "client" # defaults to "server" +->data.tcp.listen = false <=> serial0.network.endPoint = "client" # defaults to "server" ??? <=> serial0.vspc = "foobar" # defaults to <not present>, FIXME: not representable ??? <=> serial0.tryNoRxLoss = "false" # defaults to "false", FIXME: not representable @@ -2556,16 +2556,17 @@ virVMXParseSerial(virVMXContext *ctx, virConfPtr conf, int port, /* Setup virDomainChrDef */ if (STRCASEEQ(fileType, "device")) { (*def)->target.port = port; - (*def)->type = VIR_DOMAIN_CHR_TYPE_DEV; - (*def)->data.file.path = fileName; + (*def)->source.type = VIR_DOMAIN_CHR_TYPE_DEV; + (*def)->source.data.file.path = fileName; fileName = NULL; } else if (STRCASEEQ(fileType, "file")) { (*def)->target.port = port; - (*def)->type = VIR_DOMAIN_CHR_TYPE_FILE; - (*def)->data.file.path = ctx->parseFileName(fileName, ctx->opaque); + (*def)->source.type = VIR_DOMAIN_CHR_TYPE_FILE; + (*def)->source.data.file.path = ctx->parseFileName(fileName, + ctx->opaque); - if ((*def)->data.file.path == NULL) { + if ((*def)->source.data.file.path == NULL) { goto cleanup; } } else if (STRCASEEQ(fileType, "pipe")) { @@ -2574,13 +2575,13 @@ virVMXParseSerial(virVMXContext *ctx, virConfPtr conf, int port, * not representable in domain XML form */ (*def)->target.port = port; - (*def)->type = VIR_DOMAIN_CHR_TYPE_PIPE; - (*def)->data.file.path = fileName; + (*def)->source.type = VIR_DOMAIN_CHR_TYPE_PIPE; + (*def)->source.data.file.path = fileName; fileName = NULL; } else if (STRCASEEQ(fileType, "network")) { (*def)->target.port = port; - (*def)->type = VIR_DOMAIN_CHR_TYPE_TCP; + (*def)->source.type = VIR_DOMAIN_CHR_TYPE_TCP; parsedUri = xmlParseURI(fileName); @@ -2596,14 +2597,15 @@ virVMXParseSerial(virVMXContext *ctx, virConfPtr conf, int port, goto cleanup; } - (*def)->data.tcp.host = strdup(parsedUri->server); + (*def)->source.data.tcp.host = strdup(parsedUri->server); - if ((*def)->data.tcp.host == NULL) { + if ((*def)->source.data.tcp.host == NULL) { virReportOOMError(); goto cleanup; } - if (virAsprintf(&(*def)->data.tcp.service, "%d", parsedUri->port) < 0) { + if (virAsprintf(&(*def)->source.data.tcp.service, "%d", + parsedUri->port) < 0) { virReportOOMError(); goto cleanup; } @@ -2613,16 +2615,18 @@ virVMXParseSerial(virVMXContext *ctx, virConfPtr conf, int port, STRCASEEQ(parsedUri->scheme, "tcp") || STRCASEEQ(parsedUri->scheme, "tcp4") || STRCASEEQ(parsedUri->scheme, "tcp6")) { - (*def)->data.tcp.protocol = VIR_DOMAIN_CHR_TCP_PROTOCOL_RAW; + (*def)->source.data.tcp.protocol = VIR_DOMAIN_CHR_TCP_PROTOCOL_RAW; } else if (STRCASEEQ(parsedUri->scheme, "telnet")) { - (*def)->data.tcp.protocol = VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNET; + (*def)->source.data.tcp.protocol + = VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNET; } else if (STRCASEEQ(parsedUri->scheme, "telnets")) { - (*def)->data.tcp.protocol = VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNETS; + (*def)->source.data.tcp.protocol + = VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNETS; } else if (STRCASEEQ(parsedUri->scheme, "ssl") || STRCASEEQ(parsedUri->scheme, "tcp+ssl") || STRCASEEQ(parsedUri->scheme, "tcp4+ssl") || STRCASEEQ(parsedUri->scheme, "tcp6+ssl")) { - (*def)->data.tcp.protocol = VIR_DOMAIN_CHR_TCP_PROTOCOL_TLS; + (*def)->source.data.tcp.protocol = VIR_DOMAIN_CHR_TCP_PROTOCOL_TLS; } else { VMX_ERROR(VIR_ERR_INTERNAL_ERROR, _("VMX entry '%s' contains unsupported scheme '%s'"), @@ -2631,9 +2635,9 @@ virVMXParseSerial(virVMXContext *ctx, virConfPtr conf, int port, } if (network_endPoint == NULL || STRCASEEQ(network_endPoint, "server")) { - (*def)->data.tcp.listen = 1; + (*def)->source.data.tcp.listen = true; } else if (STRCASEEQ(network_endPoint, "client")) { - (*def)->data.tcp.listen = 0; + (*def)->source.data.tcp.listen = false; } else { VMX_ERROR(VIR_ERR_INTERNAL_ERROR, _("Expecting VMX entry '%s' to be 'server' or 'client' " @@ -2746,16 +2750,17 @@ virVMXParseParallel(virVMXContext *ctx, virConfPtr conf, int port, /* Setup virDomainChrDef */ if (STRCASEEQ(fileType, "device")) { (*def)->target.port = port; - (*def)->type = VIR_DOMAIN_CHR_TYPE_DEV; - (*def)->data.file.path = fileName; + (*def)->source.type = VIR_DOMAIN_CHR_TYPE_DEV; + (*def)->source.data.file.path = fileName; fileName = NULL; } else if (STRCASEEQ(fileType, "file")) { (*def)->target.port = port; - (*def)->type = VIR_DOMAIN_CHR_TYPE_FILE; - (*def)->data.file.path = ctx->parseFileName(fileName, ctx->opaque); + (*def)->source.type = VIR_DOMAIN_CHR_TYPE_FILE; + (*def)->source.data.file.path = ctx->parseFileName(fileName, + ctx->opaque); - if ((*def)->data.file.path == NULL) { + if ((*def)->source.data.file.path == NULL) { goto cleanup; } } else { @@ -3582,19 +3587,19 @@ virVMXFormatSerial(virVMXContext *ctx, virDomainChrDefPtr def, virBufferVSprintf(buffer, "serial%d.present = \"true\"\n", def->target.port); /* def:type -> vmx:fileType and def:data.file.path -> vmx:fileName */ - switch (def->type) { + switch (def->source.type) { case VIR_DOMAIN_CHR_TYPE_DEV: virBufferVSprintf(buffer, "serial%d.fileType = \"device\"\n", def->target.port); virBufferVSprintf(buffer, "serial%d.fileName = \"%s\"\n", - def->target.port, def->data.file.path); + def->target.port, def->source.data.file.path); break; case VIR_DOMAIN_CHR_TYPE_FILE: virBufferVSprintf(buffer, "serial%d.fileType = \"file\"\n", def->target.port); - fileName = ctx->formatFileName(def->data.file.path, ctx->opaque); + fileName = ctx->formatFileName(def->source.data.file.path, ctx->opaque); if (fileName == NULL) { return -1; @@ -3616,11 +3621,11 @@ virVMXFormatSerial(virVMXContext *ctx, virDomainChrDefPtr def, virBufferVSprintf(buffer, "serial%d.tryNoRxLoss = \"false\"\n", def->target.port); virBufferVSprintf(buffer, "serial%d.fileName = \"%s\"\n", - def->target.port, def->data.file.path); + def->target.port, def->source.data.file.path); break; case VIR_DOMAIN_CHR_TYPE_TCP: - switch (def->data.tcp.protocol) { + switch (def->source.data.tcp.protocol) { case VIR_DOMAIN_CHR_TCP_PROTOCOL_RAW: protocol = "tcp"; break; @@ -3640,24 +3645,25 @@ virVMXFormatSerial(virVMXContext *ctx, virDomainChrDefPtr def, default: VMX_ERROR(VIR_ERR_INTERNAL_ERROR, _("Unsupported character device TCP protocol '%s'"), - virDomainChrTcpProtocolTypeToString(def->data.tcp.protocol)); + virDomainChrTcpProtocolTypeToString( + def->source.data.tcp.protocol)); return -1; } virBufferVSprintf(buffer, "serial%d.fileType = \"network\"\n", def->target.port); virBufferVSprintf(buffer, "serial%d.fileName = \"%s://%s:%s\"\n", - def->target.port, protocol, def->data.tcp.host, - def->data.tcp.service); + def->target.port, protocol, def->source.data.tcp.host, + def->source.data.tcp.service); virBufferVSprintf(buffer, "serial%d.network.endPoint = \"%s\"\n", def->target.port, - def->data.tcp.listen ? "server" : "client"); + def->source.data.tcp.listen ? "server" : "client"); break; default: VMX_ERROR(VIR_ERR_INTERNAL_ERROR, _("Unsupported character device type '%s'"), - virDomainChrTypeToString(def->type)); + virDomainChrTypeToString(def->source.type)); return -1; } @@ -3688,19 +3694,19 @@ virVMXFormatParallel(virVMXContext *ctx, virDomainChrDefPtr def, def->target.port); /* def:type -> vmx:fileType and def:data.file.path -> vmx:fileName */ - switch (def->type) { + switch (def->source.type) { case VIR_DOMAIN_CHR_TYPE_DEV: virBufferVSprintf(buffer, "parallel%d.fileType = \"device\"\n", def->target.port); virBufferVSprintf(buffer, "parallel%d.fileName = \"%s\"\n", - def->target.port, def->data.file.path); + def->target.port, def->source.data.file.path); break; case VIR_DOMAIN_CHR_TYPE_FILE: virBufferVSprintf(buffer, "parallel%d.fileType = \"file\"\n", def->target.port); - fileName = ctx->formatFileName(def->data.file.path, ctx->opaque); + fileName = ctx->formatFileName(def->source.data.file.path, ctx->opaque); if (fileName == NULL) { return -1; @@ -3715,7 +3721,7 @@ virVMXFormatParallel(virVMXContext *ctx, virDomainChrDefPtr def, default: VMX_ERROR(VIR_ERR_INTERNAL_ERROR, _("Unsupported character device type '%s'"), - virDomainChrTypeToString(def->type)); + virDomainChrTypeToString(def->source.type)); return -1; } diff --git a/src/xen/xen_driver.c b/src/xen/xen_driver.c index 4c11b11..f3613f0 100644 --- a/src/xen/xen_driver.c +++ b/src/xen/xen_driver.c @@ -1,7 +1,7 @@ /* * xen_driver.c: Unified Xen driver. * - * Copyright (C) 2007-2010 Red Hat, Inc. + * Copyright (C) 2007-2011 Red Hat, Inc. * * See COPYING.LIB for the License of this software * @@ -1977,13 +1977,13 @@ xenUnifiedDomainOpenConsole(virDomainPtr dom, goto cleanup; } - if (chr->type != VIR_DOMAIN_CHR_TYPE_PTY) { + if (chr->source.type != VIR_DOMAIN_CHR_TYPE_PTY) { xenUnifiedError(VIR_ERR_INTERNAL_ERROR, _("character device %s is not using a PTY"), devname); goto cleanup; } - if (virFDStreamOpenFile(st, chr->data.file.path, O_RDWR) < 0) + if (virFDStreamOpenFile(st, chr->source.data.file.path, O_RDWR) < 0) goto cleanup; ret = 0; diff --git a/src/xen/xend_internal.c b/src/xen/xend_internal.c index d3633ee..44d5a22 100644 --- a/src/xen/xend_internal.c +++ b/src/xen/xend_internal.c @@ -1,7 +1,7 @@ /* * xend_internal.c: access to Xen though the Xen Daemon interface * - * Copyright (C) 2010 Red Hat, Inc. + * Copyright (C) 2010-2011 Red Hat, Inc. * Copyright (C) 2005 Anthony Liguori <aliguori@us.ibm.com> * * This file is subject to the terms and conditions of the GNU Lesser General @@ -1217,7 +1217,7 @@ xenDaemonParseSxprChar(const char *value, prefix = value; if (value[0] == '/') { - def->type = VIR_DOMAIN_CHR_TYPE_DEV; + def->source.type = VIR_DOMAIN_CHR_TYPE_DEV; } else { if ((tmp = strchr(value, ':')) != NULL) { *tmp = '\0'; @@ -1225,10 +1225,10 @@ xenDaemonParseSxprChar(const char *value, } if (STRPREFIX(prefix, "telnet")) { - def->type = VIR_DOMAIN_CHR_TYPE_TCP; - def->data.tcp.protocol = VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNET; + def->source.type = VIR_DOMAIN_CHR_TYPE_TCP; + def->source.data.tcp.protocol = VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNET; } else { - if ((def->type = virDomainChrTypeFromString(prefix)) < 0) { + if ((def->source.type = virDomainChrTypeFromString(prefix)) < 0) { virXendError(VIR_ERR_INTERNAL_ERROR, _("unknown chr device type '%s'"), prefix); goto error; @@ -1237,16 +1237,16 @@ xenDaemonParseSxprChar(const char *value, } /* Compat with legacy <console tty='/dev/pts/5'/> syntax */ - switch (def->type) { + switch (def->source.type) { case VIR_DOMAIN_CHR_TYPE_PTY: if (tty != NULL && - !(def->data.file.path = strdup(tty))) + !(def->source.data.file.path = strdup(tty))) goto no_memory; break; case VIR_DOMAIN_CHR_TYPE_FILE: case VIR_DOMAIN_CHR_TYPE_PIPE: - if (!(def->data.file.path = strdup(value))) + if (!(def->source.data.file.path = strdup(value))) goto no_memory; break; @@ -1262,19 +1262,21 @@ xenDaemonParseSxprChar(const char *value, } if (offset != value && - (def->data.tcp.host = strndup(value, offset - value)) == NULL) + (def->source.data.tcp.host = strndup(value, + offset - value)) == NULL) goto no_memory; offset2 = strchr(offset, ','); if (offset2 == NULL) - def->data.tcp.service = strdup(offset+1); + def->source.data.tcp.service = strdup(offset+1); else - def->data.tcp.service = strndup(offset+1, offset2-(offset+1)); - if (def->data.tcp.service == NULL) + def->source.data.tcp.service = strndup(offset+1, + offset2-(offset+1)); + if (def->source.data.tcp.service == NULL) goto no_memory; if (offset2 && strstr(offset2, ",server")) - def->data.tcp.listen = 1; + def->source.data.tcp.listen = true; } break; @@ -1290,12 +1292,14 @@ xenDaemonParseSxprChar(const char *value, } if (offset != value && - (def->data.udp.connectHost = strndup(value, offset - value)) == NULL) + (def->source.data.udp.connectHost + = strndup(value, offset - value)) == NULL) goto no_memory; offset2 = strchr(offset, '@'); if (offset2 != NULL) { - if ((def->data.udp.connectService = strndup(offset + 1, offset2-(offset+1))) == NULL) + if ((def->source.data.udp.connectService + = strndup(offset + 1, offset2-(offset+1))) == NULL) goto no_memory; offset3 = strchr(offset2, ':'); @@ -1306,13 +1310,16 @@ xenDaemonParseSxprChar(const char *value, } if (offset3 > (offset2 + 1) && - (def->data.udp.bindHost = strndup(offset2 + 1, offset3 - (offset2+1))) == NULL) + (def->source.data.udp.bindHost + = strndup(offset2 + 1, offset3 - (offset2+1))) == NULL) goto no_memory; - if ((def->data.udp.bindService = strdup(offset3 + 1)) == NULL) + if ((def->source.data.udp.bindService + = strdup(offset3 + 1)) == NULL) goto no_memory; } else { - if ((def->data.udp.connectService = strdup(offset + 1)) == NULL) + if ((def->source.data.udp.connectService + = strdup(offset + 1)) == NULL) goto no_memory; } } @@ -1322,15 +1329,15 @@ xenDaemonParseSxprChar(const char *value, { const char *offset = strchr(value, ','); if (offset) - def->data.nix.path = strndup(value, (offset - value)); + def->source.data.nix.path = strndup(value, (offset - value)); else - def->data.nix.path = strdup(value); - if (def->data.nix.path == NULL) + def->source.data.nix.path = strdup(value); + if (def->source.data.nix.path == NULL) goto no_memory; if (offset != NULL && strstr(offset, ",server") != NULL) - def->data.nix.listen = 1; + def->source.data.nix.listen = true; } break; } @@ -5289,7 +5296,7 @@ int xenDaemonFormatSxprChr(virDomainChrDefPtr def, virBufferPtr buf) { - const char *type = virDomainChrTypeToString(def->type); + const char *type = virDomainChrTypeToString(def->source.type); if (!type) { virXendError(VIR_ERR_INTERNAL_ERROR, @@ -5297,7 +5304,7 @@ xenDaemonFormatSxprChr(virDomainChrDefPtr def, return -1; } - switch (def->type) { + switch (def->source.type) { case VIR_DOMAIN_CHR_TYPE_NULL: case VIR_DOMAIN_CHR_TYPE_STDIO: case VIR_DOMAIN_CHR_TYPE_VC: @@ -5308,34 +5315,42 @@ xenDaemonFormatSxprChr(virDomainChrDefPtr def, case VIR_DOMAIN_CHR_TYPE_FILE: case VIR_DOMAIN_CHR_TYPE_PIPE: virBufferVSprintf(buf, "%s:", type); - virBufferEscapeSexpr(buf, "%s", def->data.file.path); + virBufferEscapeSexpr(buf, "%s", def->source.data.file.path); break; case VIR_DOMAIN_CHR_TYPE_DEV: - virBufferEscapeSexpr(buf, "%s", def->data.file.path); + virBufferEscapeSexpr(buf, "%s", def->source.data.file.path); break; case VIR_DOMAIN_CHR_TYPE_TCP: virBufferVSprintf(buf, "%s:%s:%s%s", - (def->data.tcp.protocol == VIR_DOMAIN_CHR_TCP_PROTOCOL_RAW ? + (def->source.data.tcp.protocol + == VIR_DOMAIN_CHR_TCP_PROTOCOL_RAW ? "tcp" : "telnet"), - (def->data.tcp.host ? def->data.tcp.host : ""), - (def->data.tcp.service ? def->data.tcp.service : ""), - (def->data.tcp.listen ? ",server,nowait" : "")); + (def->source.data.tcp.host ? + def->source.data.tcp.host : ""), + (def->source.data.tcp.service ? + def->source.data.tcp.service : ""), + (def->source.data.tcp.listen ? + ",server,nowait" : "")); break; case VIR_DOMAIN_CHR_TYPE_UDP: virBufferVSprintf(buf, "%s:%s:%s@%s:%s", type, - (def->data.udp.connectHost ? def->data.udp.connectHost : ""), - (def->data.udp.connectService ? def->data.udp.connectService : ""), - (def->data.udp.bindHost ? def->data.udp.bindHost : ""), - (def->data.udp.bindService ? def->data.udp.bindService : "")); + (def->source.data.udp.connectHost ? + def->source.data.udp.connectHost : ""), + (def->source.data.udp.connectService ? + def->source.data.udp.connectService : ""), + (def->source.data.udp.bindHost ? + def->source.data.udp.bindHost : ""), + (def->source.data.udp.bindService ? + def->source.data.udp.bindService : "")); break; case VIR_DOMAIN_CHR_TYPE_UNIX: virBufferVSprintf(buf, "%s:", type); - virBufferEscapeSexpr(buf, "%s", def->data.nix.path); - if (def->data.nix.listen) + virBufferEscapeSexpr(buf, "%s", def->source.data.nix.path); + if (def->source.data.nix.listen) virBufferAddLit(buf, ",server,nowait"); break; } diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index 6760f67..7e3ce2e 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -84,9 +84,9 @@ static int testCompareXMLToArgvFiles(const char *xml, vmdef->id = -1; memset(&monitor_chr, 0, sizeof(monitor_chr)); - monitor_chr.type = VIR_DOMAIN_CHR_TYPE_UNIX; - monitor_chr.data.nix.path = (char *)"/tmp/test-monitor"; - monitor_chr.data.nix.listen = 1; + monitor_chr.source.type = VIR_DOMAIN_CHR_TYPE_UNIX; + monitor_chr.source.data.nix.path = (char *)"/tmp/test-monitor"; + monitor_chr.source.data.nix.listen = true; monitor_chr.info.alias = (char *)"monitor"; flags = QEMUD_CMD_FLAG_VNC_COLON | -- 1.7.3.4

On Thu, Jan 13, 2011 at 05:34:33PM -0700, Eric Blake wrote:
This opens up the possibility of reusing the smaller ChrSourceDef for both qemu monitor and a passthrough smartcard device.
* src/conf/domain_conf.h (_virDomainChrDef): Factor host details... (_virDomainChrSourceDef): ...into new struct. (virDomainChrSourceDefFree): New prototype. * src/conf/domain_conf.c (virDomainChrDefFree) (virDomainChrDefParseXML, virDomainChrDefFormat): Split... (virDomainChrSourceDefClear, virDomainChrSourceDefFree) (virDomainChrSourceDefParseXML, virDomainChrSourceDefFormat): ...into new functions. (virDomainChrDefParseTargetXML): Update clients to reflect type split. * src/vmx/vmx.c (virVMXParseSerial, virVMXParseParallel) (virVMXFormatSerial, virVMXFormatParallel): Likewise. * src/xen/xen_driver.c (xenUnifiedDomainOpenConsole): Likewise. * src/xen/xend_internal.c (xenDaemonParseSxprChar) (xenDaemonFormatSxprChr): Likewise. * src/vbox/vbox_tmpl.c (vboxDomainDumpXML, vboxAttachSerial) (vboxAttachParallel): Likewise. * src/security/security_dac.c (virSecurityDACSetChardevLabel) (virSecurityDACSetChardevCallback) (virSecurityDACRestoreChardevLabel) (virSecurityDACRestoreChardevCallback): Likewise. * src/security/security_selinux.c (SELinuxSetSecurityChardevLabel) (SELinuxSetSecurityChardevCallback) (SELinuxRestoreSecurityChardevLabel) (SELinuxSetSecurityChardevCallback): Likewise. * src/security/virt-aa-helper.c (get_files): Likewise. * src/lxc/lxc_driver.c (lxcVmStart, lxcDomainOpenConsole): Likewise. * src/uml/uml_conf.c (umlBuildCommandLineChr): Likewise. * src/uml/uml_driver.c (umlIdentifyOneChrPTY, umlIdentifyChrPTY) (umlDomainOpenConsole): Likewise. * src/qemu/qemu_command.c (qemuBuildChrChardevStr) (qemuBuildChrArgStr, qemuBuildCommandLine) (qemuParseCommandLineChr): Likewise. * src/qemu/qemu_domain.c (qemuDomainObjPrivateXMLFormat) (qemuDomainObjPrivateXMLParse): Likewise. * src/qemu/qemu_cgroup.c (qemuSetupChardevCgroup): Likewise. * src/qemu/qemu_hotplug.c (qemuDomainAttachNetDevice): Likewise. * src/qemu/qemu_driver.c (qemudFindCharDevicePTYsMonitor) (qemudFindCharDevicePTYs, qemuPrepareChardevDevice) (qemuPrepareMonitorChr, qemudShutdownVMDaemon) (qemuDomainOpenConsole): Likewise. * src/qemu/qemu_command.h (qemuBuildChrChardevStr) (qemuBuildChrArgStr): Delete, now that they are static. * src/libvirt_private.syms (domain_conf.h): New exports. * cfg.mk (useless_free_options): Update list. * tests/qemuxml2argvtest.c (testCompareXMLToArgvFiles): Update tests. --- cfg.mk | 1 + src/conf/domain_conf.c | 342 +++++++++++++++++++++++--------------- src/conf/domain_conf.h | 39 +++-- src/libvirt_private.syms | 1 + src/lxc/lxc_driver.c | 12 +- src/qemu/qemu_cgroup.c | 10 +- src/qemu/qemu_command.c | 133 ++++++++------- src/qemu/qemu_command.h | 7 +- src/qemu/qemu_domain.c | 22 ++-- src/qemu/qemu_driver.c | 45 +++--- src/qemu/qemu_hotplug.c | 6 +- src/qemu/qemu_monitor.c | 10 +- src/security/security_dac.c | 8 +- src/security/security_selinux.c | 10 +- src/security/virt-aa-helper.c | 42 +++--- src/uml/uml_conf.c | 16 +- src/uml/uml_driver.c | 14 +- src/vbox/vbox_tmpl.c | 49 +++--- src/vmx/vmx.c | 84 +++++----- src/xen/xen_driver.c | 6 +- src/xen/xend_internal.c | 87 ++++++---- tests/qemuxml2argvtest.c | 6 +- 22 files changed, 530 insertions(+), 420 deletions(-)
ACK Daniel

* src/conf/domain_conf.h (virDomainChrDeviceType): Drop monitor. * src/conf/domain_conf.c (virDomainChrDevice) (virDomainChrDefParseTargetXML, virDomainChrDefFormat): Drop monitor support. * src/qemu/qemu_command.h (qemuBuildCommandLine): Alter signature. * src/qemu/qemu_monitor.h (qemuMonitorOpen): Likewise. * src/qemu/qemu_domain.h (_qemuDomainObjPrivate): Change type of monConfig. * src/qemu/qemu_domain.c (qemuDomainObjPrivateFree) (qemuDomainObjPrivateXMLFormat, qemuDomainObjPrivateXMLParse): Adjust to type change. * src/qemu/qemu_command.c (qemuBuildCommandLine): Likewise. * src/qemu/qemu_driver.c (qemuPrepareMonitorChr) (qemudStartVMDaemon, qemuDomainXMLToNative, qemuConnectMonitor) (qemudShutdownVMDaemon): Likewise. * src/qemu/qemu_hotplug.c (qemuDomainAttachNetDevice): Likewise. * src/qemu/qemu_monitor.c (qemuMonitorOpen): Likewise. * tests/qemuxml2argvtest.c (testCompareXMLToArgvFiles): Likewise. --- src/conf/domain_conf.c | 9 --------- src/conf/domain_conf.h | 3 +-- src/qemu/qemu_command.c | 7 +++---- src/qemu/qemu_command.h | 2 +- src/qemu/qemu_domain.c | 29 ++++++++++++----------------- src/qemu/qemu_domain.h | 4 ++-- src/qemu/qemu_driver.c | 23 ++++++++--------------- src/qemu/qemu_hotplug.c | 4 ++-- src/qemu/qemu_monitor.c | 10 +++++----- src/qemu/qemu_monitor.h | 4 ++-- tests/qemuxml2argvtest.c | 9 ++++----- 11 files changed, 40 insertions(+), 64 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 0ab2e02..674eddb 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -198,7 +198,6 @@ VIR_ENUM_IMPL(virDomainChrConsoleTarget, "virtio") VIR_ENUM_IMPL(virDomainChrDevice, VIR_DOMAIN_CHR_DEVICE_TYPE_LAST, - "monitor", "parallel", "serial", "console", @@ -2746,10 +2745,6 @@ virDomainChrDefParseTargetXML(virCapsPtr caps, } break; - case VIR_DOMAIN_CHR_DEVICE_TYPE_MONITOR: - /* Nothing to parse */ - break; - default: portStr = virXMLPropString(cur, "port"); if (portStr == NULL) { @@ -6514,10 +6509,6 @@ virDomainChrDefFormat(virBufferPtr buf, break; } - case VIR_DOMAIN_CHR_DEVICE_TYPE_MONITOR: - /* Nothing to format */ - break; - case VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE: virBufferVSprintf(buf, " <target type='%s' port='%d'/>\n", diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index ebc725c..24b82b3 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -344,8 +344,7 @@ struct _virDomainNetDef { }; enum virDomainChrDeviceType { - VIR_DOMAIN_CHR_DEVICE_TYPE_MONITOR = 0, - VIR_DOMAIN_CHR_DEVICE_TYPE_PARALLEL, + VIR_DOMAIN_CHR_DEVICE_TYPE_PARALLEL = 0, VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL, VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE, VIR_DOMAIN_CHR_DEVICE_TYPE_CHANNEL, diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 6bc2f8e..205c303 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -2452,7 +2452,7 @@ virCommandPtr qemuBuildCommandLine(virConnectPtr conn, struct qemud_driver *driver, virDomainDefPtr def, - virDomainChrDefPtr monitor_chr, + virDomainChrSourceDefPtr monitor_chr, bool monitor_json, unsigned long long qemuCmdFlags, const char *migrateFrom, @@ -2734,8 +2734,7 @@ qemuBuildCommandLine(virConnectPtr conn, if (qemuCmdFlags & QEMUD_CMD_FLAG_CHARDEV) { virCommandAddArg(cmd, "-chardev"); - if (!(chrdev = qemuBuildChrChardevStr(&monitor_chr->source, - monitor_chr->info.alias))) + if (!(chrdev = qemuBuildChrChardevStr(monitor_chr, "monitor"))) goto error; virCommandAddArg(cmd, chrdev); VIR_FREE(chrdev); @@ -2749,7 +2748,7 @@ qemuBuildCommandLine(virConnectPtr conn, prefix = "control,"; virCommandAddArg(cmd, "-monitor"); - if (!(chrdev = qemuBuildChrArgStr(&monitor_chr->source, prefix))) + if (!(chrdev = qemuBuildChrArgStr(monitor_chr, prefix))) goto error; virCommandAddArg(cmd, chrdev); VIR_FREE(chrdev); diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h index b84fa3a..b3adc22 100644 --- a/src/qemu/qemu_command.h +++ b/src/qemu/qemu_command.h @@ -40,7 +40,7 @@ virCommandPtr qemuBuildCommandLine(virConnectPtr conn, struct qemud_driver *driver, virDomainDefPtr def, - virDomainChrDefPtr monitor_chr, + virDomainChrSourceDefPtr monitor_chr, bool monitor_json, unsigned long long qemuCmdFlags, const char *migrateFrom, diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 48820bb..fa7c8bd 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -56,7 +56,7 @@ static void qemuDomainObjPrivateFree(void *data) qemuDomainObjPrivatePtr priv = data; qemuDomainPCIAddressSetFree(priv->pciaddrs); - virDomainChrDefFree(priv->monConfig); + virDomainChrSourceDefFree(priv->monConfig); VIR_FREE(priv->vcpupids); /* This should never be non-NULL if we get here, but just in case... */ @@ -75,13 +75,13 @@ static int qemuDomainObjPrivateXMLFormat(virBufferPtr buf, void *data) /* priv->monitor_chr is set only for qemu */ if (priv->monConfig) { - switch (priv->monConfig->source.type) { + switch (priv->monConfig->type) { case VIR_DOMAIN_CHR_TYPE_UNIX: - monitorpath = priv->monConfig->source.data.nix.path; + monitorpath = priv->monConfig->data.nix.path; break; default: case VIR_DOMAIN_CHR_TYPE_PTY: - monitorpath = priv->monConfig->source.data.file.path; + monitorpath = priv->monConfig->data.file.path; break; } @@ -89,7 +89,7 @@ static int qemuDomainObjPrivateXMLFormat(virBufferPtr buf, void *data) if (priv->monJSON) virBufferAddLit(buf, " json='1'"); virBufferVSprintf(buf, " type='%s'/>\n", - virDomainChrTypeToString(priv->monConfig->source.type)); + virDomainChrTypeToString(priv->monConfig->type)); } @@ -118,11 +118,6 @@ static int qemuDomainObjPrivateXMLParse(xmlXPathContextPtr ctxt, void *data) goto error; } - if (!(priv->monConfig->info.alias = strdup("monitor"))) { - virReportOOMError(); - goto error; - } - if (!(monitorpath = virXPathString("string(./monitor[1]/@path)", ctxt))) { qemuReportError(VIR_ERR_INTERNAL_ERROR, @@ -132,9 +127,9 @@ static int qemuDomainObjPrivateXMLParse(xmlXPathContextPtr ctxt, void *data) tmp = virXPathString("string(./monitor[1]/@type)", ctxt); if (tmp) - priv->monConfig->source.type = virDomainChrTypeFromString(tmp); + priv->monConfig->type = virDomainChrTypeFromString(tmp); else - priv->monConfig->source.type = VIR_DOMAIN_CHR_TYPE_PTY; + priv->monConfig->type = VIR_DOMAIN_CHR_TYPE_PTY; VIR_FREE(tmp); if (virXPathBoolean("count(./monitor[@json = '1']) > 0", ctxt)) { @@ -143,18 +138,18 @@ static int qemuDomainObjPrivateXMLParse(xmlXPathContextPtr ctxt, void *data) priv->monJSON = 0; } - switch (priv->monConfig->source.type) { + switch (priv->monConfig->type) { case VIR_DOMAIN_CHR_TYPE_PTY: - priv->monConfig->source.data.file.path = monitorpath; + priv->monConfig->data.file.path = monitorpath; break; case VIR_DOMAIN_CHR_TYPE_UNIX: - priv->monConfig->source.data.nix.path = monitorpath; + priv->monConfig->data.nix.path = monitorpath; break; default: VIR_FREE(monitorpath); qemuReportError(VIR_ERR_INTERNAL_ERROR, _("unsupported monitor type '%s'"), - virDomainChrTypeToString(priv->monConfig->source.type)); + virDomainChrTypeToString(priv->monConfig->type)); goto error; } @@ -185,7 +180,7 @@ static int qemuDomainObjPrivateXMLParse(xmlXPathContextPtr ctxt, void *data) return 0; error: - virDomainChrDefFree(priv->monConfig); + virDomainChrSourceDefFree(priv->monConfig); priv->monConfig = NULL; VIR_FREE(nodes); return -1; diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index 870a900..f14fb79 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -1,7 +1,7 @@ /* * qemu_domain.h: QEMU domain private state * - * Copyright (C) 2006-2007, 2009-2010 Red Hat, Inc. + * Copyright (C) 2006-2011 Red Hat, Inc. * Copyright (C) 2006 Daniel P. Berrange * * This library is free software; you can redistribute it and/or @@ -65,7 +65,7 @@ struct _qemuDomainObjPrivate { unsigned long long jobStart; qemuMonitorPtr mon; - virDomainChrDefPtr monConfig; + virDomainChrSourceDefPtr monConfig; int monJSON; int monitor_warned; bool gotShutdown; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 59f2c14..29b1d74 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -2570,20 +2570,13 @@ static int qemudSecurityHook(void *data) { static int qemuPrepareMonitorChr(struct qemud_driver *driver, - virDomainChrDefPtr monConfig, + virDomainChrSourceDefPtr monConfig, const char *vm) { - monConfig->deviceType = VIR_DOMAIN_CHR_DEVICE_TYPE_MONITOR; + monConfig->type = VIR_DOMAIN_CHR_TYPE_UNIX; + monConfig->data.nix.listen = true; - monConfig->source.type = VIR_DOMAIN_CHR_TYPE_UNIX; - monConfig->source.data.nix.listen = true; - - if (!(monConfig->info.alias = strdup("monitor"))) { - virReportOOMError(); - return -1; - } - - if (virAsprintf(&monConfig->source.data.nix.path, "%s/%s.monitor", + if (virAsprintf(&monConfig->data.nix.path, "%s/%s.monitor", driver->libDir, vm) < 0) { virReportOOMError(); return -1; @@ -3018,9 +3011,9 @@ static void qemudShutdownVMDaemon(struct qemud_driver *driver, qemuMonitorClose(priv->mon); if (priv->monConfig) { - if (priv->monConfig->source.type == VIR_DOMAIN_CHR_TYPE_UNIX) - unlink(priv->monConfig->source.data.nix.path); - virDomainChrDefFree(priv->monConfig); + if (priv->monConfig->type == VIR_DOMAIN_CHR_TYPE_UNIX) + unlink(priv->monConfig->data.nix.path); + virDomainChrSourceDefFree(priv->monConfig); priv->monConfig = NULL; } @@ -6030,7 +6023,7 @@ static char *qemuDomainXMLToNative(virConnectPtr conn, unsigned int flags ATTRIBUTE_UNUSED) { struct qemud_driver *driver = conn->privateData; virDomainDefPtr def = NULL; - virDomainChrDef monConfig; + virDomainChrSourceDef monConfig; unsigned long long qemuCmdFlags; virCommandPtr cmd = NULL; char *ret = NULL; diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index b40150d..9d6697e 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -567,7 +567,7 @@ int qemuDomainAttachNetDevice(virConnectPtr conn, if (net->type == VIR_DOMAIN_NET_TYPE_BRIDGE || net->type == VIR_DOMAIN_NET_TYPE_NETWORK) { - if (priv->monConfig->source.type != VIR_DOMAIN_CHR_TYPE_UNIX) { + if (priv->monConfig->type != VIR_DOMAIN_CHR_TYPE_UNIX) { qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("network device type '%s' cannot be attached: " "qemu is not using a unix socket monitor"), @@ -578,7 +578,7 @@ int qemuDomainAttachNetDevice(virConnectPtr conn, if ((tapfd = qemuNetworkIfaceConnect(conn, driver, net, qemuCmdFlags)) < 0) return -1; } else if (net->type == VIR_DOMAIN_NET_TYPE_DIRECT) { - if (priv->monConfig->source.type != VIR_DOMAIN_CHR_TYPE_UNIX) { + if (priv->monConfig->type != VIR_DOMAIN_CHR_TYPE_UNIX) { qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("network device type '%s' cannot be attached: " "qemu is not using a unix socket monitor"), diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index cc8b374..055e7ce 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -588,7 +588,7 @@ qemuMonitorIO(int watch, int fd, int events, void *opaque) { qemuMonitorPtr qemuMonitorOpen(virDomainObjPtr vm, - virDomainChrDefPtr config, + virDomainChrSourceDefPtr config, int json, qemuMonitorCallbacksPtr cb) { @@ -625,20 +625,20 @@ qemuMonitorOpen(virDomainObjPtr vm, mon->cb = cb; qemuMonitorLock(mon); - switch (config->source.type) { + switch (config->type) { case VIR_DOMAIN_CHR_TYPE_UNIX: mon->hasSendFD = 1; - mon->fd = qemuMonitorOpenUnix(config->source.data.nix.path); + mon->fd = qemuMonitorOpenUnix(config->data.nix.path); break; case VIR_DOMAIN_CHR_TYPE_PTY: - mon->fd = qemuMonitorOpenPty(config->source.data.file.path); + mon->fd = qemuMonitorOpenPty(config->data.file.path); break; default: qemuReportError(VIR_ERR_INTERNAL_ERROR, _("unable to handle monitor type: %s"), - virDomainChrTypeToString(config->source.type)); + virDomainChrTypeToString(config->type)); goto cleanup; } diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 3ac5024..718ea13 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -1,7 +1,7 @@ /* * qemu_monitor.h: interaction with QEMU monitor console * - * Copyright (C) 2006-2010 Red Hat, Inc. + * Copyright (C) 2006-2011 Red Hat, Inc. * Copyright (C) 2006 Daniel P. Berrange * * This library is free software; you can redistribute it and/or @@ -119,7 +119,7 @@ char *qemuMonitorEscapeArg(const char *in); char *qemuMonitorEscapeShell(const char *in); qemuMonitorPtr qemuMonitorOpen(virDomainObjPtr vm, - virDomainChrDefPtr config, + virDomainChrSourceDefPtr config, int json, qemuMonitorCallbacksPtr cb); diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index 7e3ce2e..d7951df 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -39,7 +39,7 @@ static int testCompareXMLToArgvFiles(const char *xml, int ret = -1; unsigned long long flags; virDomainDefPtr vmdef = NULL; - virDomainChrDef monitor_chr; + virDomainChrSourceDef monitor_chr; virConnectPtr conn; char *log = NULL; char *emulator = NULL; @@ -84,10 +84,9 @@ static int testCompareXMLToArgvFiles(const char *xml, vmdef->id = -1; memset(&monitor_chr, 0, sizeof(monitor_chr)); - monitor_chr.source.type = VIR_DOMAIN_CHR_TYPE_UNIX; - monitor_chr.source.data.nix.path = (char *)"/tmp/test-monitor"; - monitor_chr.source.data.nix.listen = true; - monitor_chr.info.alias = (char *)"monitor"; + monitor_chr.type = VIR_DOMAIN_CHR_TYPE_UNIX; + monitor_chr.data.nix.path = (char *)"/tmp/test-monitor"; + monitor_chr.data.nix.listen = true; flags = QEMUD_CMD_FLAG_VNC_COLON | QEMUD_CMD_FLAG_NO_REBOOT | -- 1.7.3.4

On Thu, Jan 13, 2011 at 05:34:34PM -0700, Eric Blake wrote:
* src/conf/domain_conf.h (virDomainChrDeviceType): Drop monitor. * src/conf/domain_conf.c (virDomainChrDevice) (virDomainChrDefParseTargetXML, virDomainChrDefFormat): Drop monitor support. * src/qemu/qemu_command.h (qemuBuildCommandLine): Alter signature. * src/qemu/qemu_monitor.h (qemuMonitorOpen): Likewise. * src/qemu/qemu_domain.h (_qemuDomainObjPrivate): Change type of monConfig. * src/qemu/qemu_domain.c (qemuDomainObjPrivateFree) (qemuDomainObjPrivateXMLFormat, qemuDomainObjPrivateXMLParse): Adjust to type change. * src/qemu/qemu_command.c (qemuBuildCommandLine): Likewise. * src/qemu/qemu_driver.c (qemuPrepareMonitorChr) (qemudStartVMDaemon, qemuDomainXMLToNative, qemuConnectMonitor) (qemudShutdownVMDaemon): Likewise. * src/qemu/qemu_hotplug.c (qemuDomainAttachNetDevice): Likewise. * src/qemu/qemu_monitor.c (qemuMonitorOpen): Likewise. * tests/qemuxml2argvtest.c (testCompareXMLToArgvFiles): Likewise. --- src/conf/domain_conf.c | 9 --------- src/conf/domain_conf.h | 3 +-- src/qemu/qemu_command.c | 7 +++---- src/qemu/qemu_command.h | 2 +- src/qemu/qemu_domain.c | 29 ++++++++++++----------------- src/qemu/qemu_domain.h | 4 ++-- src/qemu/qemu_driver.c | 23 ++++++++--------------- src/qemu/qemu_hotplug.c | 4 ++-- src/qemu/qemu_monitor.c | 10 +++++----- src/qemu/qemu_monitor.h | 4 ++-- tests/qemuxml2argvtest.c | 9 ++++----- 11 files changed, 40 insertions(+), 64 deletions(-)
ACK Daniel

Assuming a hypervisor that supports multiple smartcard devices in the guest, this would be a valid XML description: <devices> <smartcard mode='host'/> <smartcard mode='host-certificates'> <certificate>/path/to/cert1</certificate> <certificate>/path/to/cert2</certificate> <certificate>/path/to/cert3</certificate> </smartcard> <smartcard mode='passthrough' type='tcp'> <source mode='connect' host='127.0.0.1' service='2001'/> <protocol type='raw'/> </smartcard> </devices> * docs/formatdomain.html.in (Smartcard devices): New section. * docs/schemas/domain.rng (smartcard): New define, used in devices. * tests/qemuxml2argvdata/qemuxml2argv-smartcard-host.xml: New file to test schema. * tests/qemuxml2argvdata/qemuxml2argv-smartcard-host-certificates.xml: Likewise. * tests/qemuxml2argvdata/qemuxml2argv-smartcard-passthrough-tcp.xml: Likewise. --- docs/formatdomain.html.in | 72 ++++++++++++++++++++ docs/schemas/domain.rng | 37 ++++++++++ .../qemuxml2argv-smartcard-host-certificates.xml | 20 ++++++ .../qemuxml2argv-smartcard-host.xml | 16 +++++ .../qemuxml2argv-smartcard-passthrough-tcp.xml | 19 +++++ 5 files changed, 164 insertions(+), 0 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-smartcard-host-certificates.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-smartcard-host.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-smartcard-passthrough-tcp.xml diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index dad268d..519795c 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -804,6 +804,78 @@ not used by qemu.</dd> </dl> + <h4><a name="elementsSmartcard">Smartcard devices</a></h4> + + <p> + A virtual smartcard device can be supplied to the guest via the + <code>smartcard</code> element. A USB smartcard reader device on + the host cannot be used on a guest with simple device + passthrough, since it will then not be available on the host, + possibly locking the host computer when it is "removed". + Therefore, some hypervisors provide a specialized virtual device + that can present a smartcard interface to the guest, with + several modes for describing how credentials are obtained from + the host or even a from a channel created to a third-party + smartcard provider. <span class="since">Since 0.8.8</span> + </p> + +<pre> + ... + <devices> + <smartcard mode='host'/> + <smartcard mode='host-certificates'> + <certificate>/path/to/cert1</certificate> + <certificate>/path/to/cert2</certificate> + <certificate>/path/to/cert3</certificate> + </smartcard> + <smartcard mode='passthrough' type='tcp'> + <source mode='connect' host='127.0.0.1' service='2001'/> + <protocol type='raw'/> + </smartcard> + </devices> + ... +</pre> + + <p> + The <code><smartcard></code> element has a mandatory + attribute <code>mode</code>. The following modes are supported; + in each mode, the guest sees a device on its USB bus that + behaves like a physical USB CCID (Chip/Smart Card Interface + Device) card. + </p> + + <ul> + <li><code>mode='host'</code> — the simplest operation, + where the hypervisor relays all requests from the guest into + direct access to the host's smartcard via NSS. No other + attributes or sub-elements are required.</li> + + <li><code>mode='host-certificates'</code> — rather than + requiring a smartcard to be plugged into the host, it is + possible to provide three files residing on the host and + containing NSS certificates. These certificates can be + generated via the command <code>certutil -d /etc/pki/nssdb -x -t + CT,CT,CT -S -s CN=cert1 -n cert1</code>, and the resulting three + files must be supplied as the content of each of + three <code><certificate></code> sub-elements.</li> + + <li><code>mode='passthrough'</code> — rather than having + the hypervisor directly communicate with the host, it is + possible to tunnel all requests through a secondary character + device to a third-party provider (which may in turn be talking + to a smartcard or using three certificate files). In this mode + of operation, an additional attribute <code>type</code>, + matching one of the supported <a href="#elementsConsole">serial + devices</a>, to describe the host side of the tunnel; if + omitted, <code>type='tcp'</code> is assumed. Further + sub-elements, such as <code><source></code>, are required + according to the given type, although + a <code><target></code> sub-element is not required (since + the consumer of the character device is the hypervisor itself, + rather than a device visible in the guest).</li> + </ul> + + <h4><a name="elementsNICS">Network interfaces</a></h4> <pre> diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng index 6de85fd..1110234 100644 --- a/docs/schemas/domain.rng +++ b/docs/schemas/domain.rng @@ -1579,6 +1579,42 @@ </interleave> </element> </define> + <define name="smartcard"> + <element name="smartcard"> + <choice> + <attribute name="mode"> + <value>host</value> + </attribute> + <group> + <attribute name="mode"> + <value>host-certificates</value> + </attribute> + <ref name='certificate'/> + <ref name='certificate'/> + <ref name='certificate'/> + </group> + <group> + <attribute name="mode"> + <value>passthrough</value> + </attribute> + <optional> + <ref name="qemucdevSrcType"/> + </optional> + <interleave> + <ref name="qemucdevSrcDef"/> + <optional> + <ref name="qemucdevTgtDef"/> + </optional> + </interleave> + </group> + </choice> + </element> + </define> + <define name="certificate"> + <element name="certificate"> + <ref name="absFilePath"/> + </element> + </define> <define name="input"> <element name="input"> <attribute name="type"> @@ -1736,6 +1772,7 @@ <ref name="parallel"/> <ref name="serial"/> <ref name="channel"/> + <ref name="smartcard"/> </choice> </zeroOrMore> <optional> diff --git a/tests/qemuxml2argvdata/qemuxml2argv-smartcard-host-certificates.xml b/tests/qemuxml2argvdata/qemuxml2argv-smartcard-host-certificates.xml new file mode 100644 index 0000000..f70395d --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-smartcard-host-certificates.xml @@ -0,0 +1,20 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory>219200</memory> + <currentMemory>219200</currentMemory> + <vcpu>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <devices> + <emulator>/usr/bin/qemu</emulator> + <smartcard mode='host-certificates'> + <certificate>/etc/pki/cert1</certificate> + <certificate>/etc/pki/cert2</certificate> + <certificate>/etc/pki/cert3</certificate> + </smartcard> + <memballoon model='virtio'/> + </devices> +</domain> diff --git a/tests/qemuxml2argvdata/qemuxml2argv-smartcard-host.xml b/tests/qemuxml2argvdata/qemuxml2argv-smartcard-host.xml new file mode 100644 index 0000000..faa2231 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-smartcard-host.xml @@ -0,0 +1,16 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory>219200</memory> + <currentMemory>219200</currentMemory> + <vcpu>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <devices> + <emulator>/usr/bin/qemu</emulator> + <smartcard mode='host'/> + <memballoon model='virtio'/> + </devices> +</domain> diff --git a/tests/qemuxml2argvdata/qemuxml2argv-smartcard-passthrough-tcp.xml b/tests/qemuxml2argvdata/qemuxml2argv-smartcard-passthrough-tcp.xml new file mode 100644 index 0000000..8e2fa52 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-smartcard-passthrough-tcp.xml @@ -0,0 +1,19 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory>219200</memory> + <currentMemory>219200</currentMemory> + <vcpu>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <devices> + <emulator>/usr/bin/qemu</emulator> + <smartcard mode='passthrough' type='tcp'> + <source mode='connect' host='127.0.0.1' service='2001'/> + <protocol type='raw'/> + </smartcard> + <memballoon model='virtio'/> + </devices> +</domain> -- 1.7.3.4

On Thu, Jan 13, 2011 at 05:34:35PM -0700, Eric Blake wrote:
Assuming a hypervisor that supports multiple smartcard devices in the guest, this would be a valid XML description:
This looks pretty reasonable, but is going to require additions to the security driver code. In the SetAllLabel method of the security drivers you'll need to iterate over all smartcards.
<devices> <smartcard mode='host'/>
I guess there is some /dev/smartcard device that needs to be accessed and thus labelled here ?
<smartcard mode='host-certificates'> <certificate>/path/to/cert1</certificate> <certificate>/path/to/cert2</certificate> <certificate>/path/to/cert3</certificate> </smartcard>
Those cert paths will all need labelling with the 'readonly' disk label.
<smartcard mode='passthrough' type='tcp'> <source mode='connect' host='127.0.0.1' service='2001'/> <protocol type='raw'/> </smartcard>
There's already a helper API for labelling chardev configs that can be used.
</devices>
ACK for the patch Daniel

On 01/14/2011 05:24 AM, Daniel P. Berrange wrote:
On Thu, Jan 13, 2011 at 05:34:35PM -0700, Eric Blake wrote:
Assuming a hypervisor that supports multiple smartcard devices in the guest, this would be a valid XML description:
This looks pretty reasonable, but is going to require additions to the security driver code. In the SetAllLabel method of the security drivers you'll need to iterate over all smartcards.
Good catch. I'm working on that portion now. I've gone ahead and pushed 1 and 2, given that they were straight ack and were preliminary patches useful even without smartcard support.
<devices> <smartcard mode='host'/>
I guess there is some /dev/smartcard device that needs to be accessed and thus labelled here ?
I'm not sure. Alon, since -device ccid-card-emulated makes qemu use NSS to access the host's smartcard, do we need to add any particular permissions to a device file to allow qemu access to the host device (and if so, is it /dev/smartcard or something else on the host)?
ACK for the patch
Even though patch 3 is just docs, I'll hold off pushing this until I've completed incorporating the security driver fixes as well. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On Fri, Jan 14, 2011 at 10:22:19AM -0700, Eric Blake wrote:
On 01/14/2011 05:24 AM, Daniel P. Berrange wrote:
On Thu, Jan 13, 2011 at 05:34:35PM -0700, Eric Blake wrote:
Assuming a hypervisor that supports multiple smartcard devices in the guest, this would be a valid XML description:
This looks pretty reasonable, but is going to require additions to the security driver code. In the SetAllLabel method of the security drivers you'll need to iterate over all smartcards.
Good catch. I'm working on that portion now. I've gone ahead and pushed 1 and 2, given that they were straight ack and were preliminary patches useful even without smartcard support.
<devices> <smartcard mode='host'/>
I guess there is some /dev/smartcard device that needs to be accessed and thus labelled here ?
I'm not sure. Alon, since -device ccid-card-emulated makes qemu use NSS to access the host's smartcard, do we need to add any particular permissions to a device file to allow qemu access to the host device (and if so, is it /dev/smartcard or something else on the host)?
The host, in the non certificates backed card case, would have some smartcard reader device, I've tested using CCID devices (the same device I'm emulating), which is a USB device, so the device file in question is /dev/bus/usb/<busnum>/<devicenum> and qemu would need permissions to access that. There are serial port connected readers I think too, so /dev/ttyS*. How would you determine the exact files? you could maybe try to find out which devices NSS accesses? I'll try to find some command (maybe certutil) or example with NSS to access this information.
ACK for the patch
Even though patch 3 is just docs, I'll hold off pushing this until I've completed incorporating the security driver fixes as well.
-- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On Sat, Jan 15, 2011 at 02:37:33PM +0200, Alon Levy wrote:
On Fri, Jan 14, 2011 at 10:22:19AM -0700, Eric Blake wrote:
On 01/14/2011 05:24 AM, Daniel P. Berrange wrote:
On Thu, Jan 13, 2011 at 05:34:35PM -0700, Eric Blake wrote:
Assuming a hypervisor that supports multiple smartcard devices in the guest, this would be a valid XML description:
This looks pretty reasonable, but is going to require additions to the security driver code. In the SetAllLabel method of the security drivers you'll need to iterate over all smartcards.
Good catch. I'm working on that portion now. I've gone ahead and pushed 1 and 2, given that they were straight ack and were preliminary patches useful even without smartcard support.
<devices> <smartcard mode='host'/>
I guess there is some /dev/smartcard device that needs to be accessed and thus labelled here ?
I'm not sure. Alon, since -device ccid-card-emulated makes qemu use NSS to access the host's smartcard, do we need to add any particular permissions to a device file to allow qemu access to the host device (and if so, is it /dev/smartcard or something else on the host)?
The host, in the non certificates backed card case, would have some smartcard reader device, I've tested using CCID devices (the same device I'm emulating), which is a USB device, so the device file in question is /dev/bus/usb/<busnum>/<devicenum> and qemu would need permissions to access that. There are serial port connected readers I think too, so /dev/ttyS*. How would you determine the exact files? you could maybe try to find out which devices NSS accesses? I'll try to find some command (maybe certutil) or example with NSS to access this information.
Yes, how do you tell NSS which USB device to use ? QEMU/NSS will certainly *not* be allowed to scan the USB bus to find a device itself. So it seems like the XML config should let the application explicitly include a path /dev/usb/$BUS/$DEV which is explicitly passed to QEMU. Regards, Daniel

On Mon, Jan 17, 2011 at 11:17:07AM +0000, Daniel P. Berrange wrote:
On Sat, Jan 15, 2011 at 02:37:33PM +0200, Alon Levy wrote:
On Fri, Jan 14, 2011 at 10:22:19AM -0700, Eric Blake wrote:
On 01/14/2011 05:24 AM, Daniel P. Berrange wrote:
On Thu, Jan 13, 2011 at 05:34:35PM -0700, Eric Blake wrote:
Assuming a hypervisor that supports multiple smartcard devices in the guest, this would be a valid XML description:
This looks pretty reasonable, but is going to require additions to the security driver code. In the SetAllLabel method of the security drivers you'll need to iterate over all smartcards.
Good catch. I'm working on that portion now. I've gone ahead and pushed 1 and 2, given that they were straight ack and were preliminary patches useful even without smartcard support.
<devices> <smartcard mode='host'/>
I guess there is some /dev/smartcard device that needs to be accessed and thus labelled here ?
I'm not sure. Alon, since -device ccid-card-emulated makes qemu use NSS to access the host's smartcard, do we need to add any particular permissions to a device file to allow qemu access to the host device (and if so, is it /dev/smartcard or something else on the host)?
The host, in the non certificates backed card case, would have some smartcard reader device, I've tested using CCID devices (the same device I'm emulating), which is a USB device, so the device file in question is /dev/bus/usb/<busnum>/<devicenum> and qemu would need permissions to access that. There are serial port connected readers I think too, so /dev/ttyS*. How would you determine the exact files? you could maybe try to find out which devices NSS accesses? I'll try to find some command (maybe certutil) or example with NSS to access this information.
Yes, how do you tell NSS which USB device to use ? QEMU/NSS will certainly *not* be allowed to scan the USB bus to find a device itself. So it seems like the XML config should let the application explicitly include a path /dev/usb/$BUS/$DEV which is explicitly passed to QEMU.
I haven't told NSS anything so far, I'll try to find out. That said, this is a minor use case from RHEV pov. /That/ said, this is of course important for upstream.
Regards, Daniel

On Mon, Jan 17, 2011 at 03:23:55PM +0200, Alon Levy wrote:
On Mon, Jan 17, 2011 at 11:17:07AM +0000, Daniel P. Berrange wrote:
On Sat, Jan 15, 2011 at 02:37:33PM +0200, Alon Levy wrote:
On Fri, Jan 14, 2011 at 10:22:19AM -0700, Eric Blake wrote:
On 01/14/2011 05:24 AM, Daniel P. Berrange wrote:
On Thu, Jan 13, 2011 at 05:34:35PM -0700, Eric Blake wrote:
Assuming a hypervisor that supports multiple smartcard devices in the guest, this would be a valid XML description:
This looks pretty reasonable, but is going to require additions to the security driver code. In the SetAllLabel method of the security drivers you'll need to iterate over all smartcards.
Good catch. I'm working on that portion now. I've gone ahead and pushed 1 and 2, given that they were straight ack and were preliminary patches useful even without smartcard support.
<devices> <smartcard mode='host'/>
I guess there is some /dev/smartcard device that needs to be accessed and thus labelled here ?
I'm not sure. Alon, since -device ccid-card-emulated makes qemu use NSS to access the host's smartcard, do we need to add any particular permissions to a device file to allow qemu access to the host device (and if so, is it /dev/smartcard or something else on the host)?
The host, in the non certificates backed card case, would have some smartcard reader device, I've tested using CCID devices (the same device I'm emulating), which is a USB device, so the device file in question is /dev/bus/usb/<busnum>/<devicenum> and qemu would need permissions to access that. There are serial port connected readers I think too, so /dev/ttyS*. How would you determine the exact files? you could maybe try to find out which devices NSS accesses? I'll try to find some command (maybe certutil) or example with NSS to access this information.
Yes, how do you tell NSS which USB device to use ? QEMU/NSS will certainly *not* be allowed to scan the USB bus to find a device itself. So it seems like the XML config should let the application explicitly include a path /dev/usb/$BUS/$DEV which is explicitly passed to QEMU.
I haven't told NSS anything so far, I'll try to find out. That said, this is a minor use case from RHEV pov. /That/ said, this is of course important for upstream.
Ok, it sounds very much like NSS must be scanning for devices itself. While RHEV might not care about this, since it has a potential bearing on the XML for this use case, we need to determine what's happening asap. Regards,, Daniel

* src/conf/domain_conf.h (virDomainSmartcardType): New enum. (virDomainSmartcardDef): New struct. (virDomainDef): Include smartcards. (virDomainSmartcardDefIterator): New typedef. (virDomainSmartcardDefFree, virDomainSmartcardDefForeach): New prototypes. * src/conf/domain_conf.c (virDomainSmartcard): Convert between enum and string. (virDomainSmartcardDefParseXML, virDomainSmartcardDefFormat) (virDomainSmartcardDefFree): New functions. (virDomainDefParseXML): Parse the new XML. (virDomainDefFormat): Convert back to XML. (virDomainDefFree): Clean up. (virDomainDeviceInfoIterate): Iterate over passthrough aliases. * src/libvirt_private.syms (domain_conf.h): New exports. * cfg.mk (useless_free_options): List new function. --- cfg.mk | 1 + src/conf/domain_conf.c | 224 ++++++++++++++++++++++++++++++++++++++++++++++ src/conf/domain_conf.h | 40 ++++++++- src/libvirt_private.syms | 4 + 4 files changed, 268 insertions(+), 1 deletions(-) diff --git a/cfg.mk b/cfg.mk index d4c791a..2bf8fd0 100644 --- a/cfg.mk +++ b/cfg.mk @@ -99,6 +99,7 @@ useless_free_options = \ --name=virDomainInputDefFree \ --name=virDomainNetDefFree \ --name=virDomainObjFree \ + --name=virDomainSmartcardDefFree \ --name=virDomainSnapshotDefFree \ --name=virDomainSnapshotObjFree \ --name=virDomainSoundDefFree \ diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 674eddb..a567136 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -221,6 +221,11 @@ VIR_ENUM_IMPL(virDomainChrTcpProtocol, VIR_DOMAIN_CHR_TCP_PROTOCOL_LAST, "telnets", "tls") +VIR_ENUM_IMPL(virDomainSmartcard, VIR_DOMAIN_SMARTCARD_TYPE_LAST, + "host", + "host-certificates", + "passthrough") + VIR_ENUM_IMPL(virDomainSoundModel, VIR_DOMAIN_SOUND_MODEL_LAST, "sb16", "es1370", @@ -680,6 +685,32 @@ void virDomainChrDefFree(virDomainChrDefPtr def) VIR_FREE(def); } +void virDomainSmartcardDefFree(virDomainSmartcardDefPtr def) +{ + size_t i; + if (!def) + return; + + switch (def->type) { + case VIR_DOMAIN_SMARTCARD_TYPE_HOST_CERTIFICATES: + for (i = 0; i < VIR_DOMAIN_SMARTCARD_NUM_CERTIFICATES; i++) + VIR_FREE(def->data.cert.file[i]); + break; + + case VIR_DOMAIN_SMARTCARD_TYPE_PASSTHROUGH: + virDomainChrSourceDefClear(&def->data.passthru); + break; + + case VIR_DOMAIN_SMARTCARD_TYPE_HOST: + default: + break; + } + + virDomainDeviceInfoClear(&def->info); + + VIR_FREE(def); +} + void virDomainSoundDefFree(virDomainSoundDefPtr def) { if (!def) @@ -821,6 +852,10 @@ void virDomainDefFree(virDomainDefPtr def) virDomainNetDefFree(def->nets[i]); VIR_FREE(def->nets); + for (i = 0 ; i < def->nsmartcards ; i++) + virDomainSmartcardDefFree(def->smartcards[i]); + VIR_FREE(def->smartcards); + for (i = 0 ; i < def->nserials ; i++) virDomainChrDefFree(def->serials[i]); VIR_FREE(def->serials); @@ -1180,6 +1215,9 @@ int virDomainDeviceInfoIterate(virDomainDefPtr def, for (i = 0; i < def->ncontrollers ; i++) if (cb(def, &def->controllers[i]->info, opaque) < 0) return -1; + for (i = 0; i < def->nsmartcards ; i++) + if (cb(def, &def->smartcards[i]->info, opaque) < 0) + return -1; for (i = 0; i < def->nserials ; i++) if (cb(def, &def->serials[i]->info, opaque) < 0) return -1; @@ -3067,6 +3105,101 @@ error: goto cleanup; } +static virDomainSmartcardDefPtr +virDomainSmartcardDefParseXML(xmlNodePtr node, + int flags) +{ + xmlNodePtr cur; + char *mode = NULL; + char *type = NULL; + virDomainSmartcardDefPtr def; + int i; + + if (VIR_ALLOC(def) < 0) { + virReportOOMError(); + return NULL; + } + + mode = virXMLPropString(node, "mode"); + if (mode == NULL) { + virDomainReportError(VIR_ERR_XML_ERROR, "%s", + _("missing smartcard device mode")); + goto error; + } + if ((def->type = virDomainSmartcardTypeFromString(mode)) < 0) { + virDomainReportError(VIR_ERR_XML_ERROR, + _("unknown smartcard device mode: %s"), + mode); + goto error; + } + + switch (def->type) { + case VIR_DOMAIN_SMARTCARD_TYPE_HOST: + break; + + case VIR_DOMAIN_SMARTCARD_TYPE_HOST_CERTIFICATES: + i = 0; + for (cur = node->children; + cur && cur->type == XML_ELEMENT_NODE && + xmlStrEqual(cur->name, BAD_CAST "certificate"); + cur = cur->next) { + def->data.cert.file[i++] = (char *)xmlNodeGetContent(cur); + } + if (i != 3 || cur->next) { + virDomainReportError(VIR_ERR_XML_ERROR, "%s", + _("host-certificates mode needs exactly " + "three certificates")); + goto error; + } + break; + + case VIR_DOMAIN_SMARTCARD_TYPE_PASSTHROUGH: + type = virXMLPropString(node, "type"); + if (type == NULL) + def->data.passthru.type = VIR_DOMAIN_CHR_TYPE_TCP; + else if ((def->data.passthru.type = virDomainChrTypeFromString(type)) + < 0) { + virDomainReportError(VIR_ERR_XML_ERROR, + _("unknown type presented to host for " + "character device: %s"), type); + goto error; + } + + cur = node->children; + i = virDomainChrSourceDefParseXML(&def->data.passthru, cur); + if (i < 0) + goto error; + if (i) { + if (virDomainDeviceInfoParseXML(node, &def->info, flags) < 0) + goto error; + /* An active domain may have an alias, but no address. */ + if (def->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) { + virDomainReportError(VIR_ERR_XML_ERROR, + _("smartcard should not have an " + "<address> element")); + goto error; + } + } + break; + + default: + virDomainReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("unknown smartcard mode")); + goto error; + } + +cleanup: + VIR_FREE(mode); + VIR_FREE(type); + + return def; + +error: + virDomainSmartcardDefFree(def); + def = NULL; + goto cleanup; +} + /* Parse the XML definition for a network interface */ static virDomainInputDefPtr virDomainInputDefParseXML(const char *ostype, @@ -5079,6 +5212,26 @@ static virDomainDefPtr virDomainDefParseXML(virCapsPtr caps, VIR_FREE(nodes); + /* analysis of the smartcard devices */ + if ((n = virXPathNodeSet("./devices/smartcard", ctxt, &nodes)) < 0) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("cannot extract smartcard devices")); + goto error; + } + if (n && VIR_ALLOC_N(def->smartcards, n) < 0) + goto no_memory; + + for (i = 0 ; i < n ; i++) { + virDomainSmartcardDefPtr card = virDomainSmartcardDefParseXML(nodes[i], + flags); + if (!card) + goto error; + + def->smartcards[def->nsmartcards++] = card; + } + VIR_FREE(nodes); + + /* analysis of the character devices */ if ((n = virXPathNodeSet("./devices/parallel", ctxt, &nodes)) < 0) { virDomainReportError(VIR_ERR_INTERNAL_ERROR, @@ -6535,6 +6688,50 @@ virDomainChrDefFormat(virBufferPtr buf, } static int +virDomainSmartcardDefFormat(virBufferPtr buf, + virDomainSmartcardDefPtr def, + int flags) +{ + const char *mode = virDomainSmartcardTypeToString(def->type); + size_t i; + + if (!mode) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + _("unexpected smartcard type %d"), def->type); + return -1; + } + + virBufferVSprintf(buf, " <smartcard mode='%s'", mode); + switch (def->type) { + case VIR_DOMAIN_SMARTCARD_TYPE_HOST: + virBufferAddLit(buf, "/>\n"); + return 0; + + case VIR_DOMAIN_SMARTCARD_TYPE_HOST_CERTIFICATES: + virBufferAddLit(buf, ">\n"); + for (i = 0; i < VIR_DOMAIN_SMARTCARD_NUM_CERTIFICATES; i++) + virBufferEscapeString(buf, " <certificate>%s</certificate>\n", + def->data.cert.file[i]); + break; + + case VIR_DOMAIN_SMARTCARD_TYPE_PASSTHROUGH: + if (virDomainChrSourceDefFormat(buf, &def->data.passthru, false, + flags) < 0) + return -1; + if (virDomainDeviceInfoFormat(buf, &def->info, flags) < 0) + return -1; + break; + + default: + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + _("unexpected smartcard type %d"), def->type); + return -1; + } + virBufferAddLit(buf, " </smartcard>\n"); + return 0; +} + +static int virDomainSoundDefFormat(virBufferPtr buf, virDomainSoundDefPtr def, int flags) @@ -7331,6 +7528,10 @@ char *virDomainDefFormat(virDomainDefPtr def, if (virDomainNetDefFormat(&buf, def->nets[n], flags) < 0) goto cleanup; + for (n = 0 ; n < def->nsmartcards ; n++) + if (virDomainSmartcardDefFormat(&buf, def->smartcards[n], flags) < 0) + goto cleanup; + for (n = 0 ; n < def->nserials ; n++) if (virDomainChrDefFormat(&buf, def->serials[n], flags) < 0) goto cleanup; @@ -8409,6 +8610,29 @@ done: } +int virDomainSmartcardDefForeach(virDomainDefPtr def, + bool abortOnError, + virDomainSmartcardDefIterator iter, + void *opaque) +{ + int i; + int rc = 0; + + for (i = 0 ; i < def->nsmartcards ; i++) { + if ((iter)(def, + def->smartcards[i], + opaque) < 0) + rc = -1; + + if (abortOnError && rc != 0) + goto done; + } + +done: + return rc; +} + + int virDomainDiskDefForeachPath(virDomainDiskDefPtr disk, bool allowProbing, bool ignoreOpenFailure, diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 24b82b3..25f1ed0 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -437,6 +437,31 @@ struct _virDomainChrDef { virDomainDeviceInfo info; }; +enum virDomainSmartcardType { + VIR_DOMAIN_SMARTCARD_TYPE_HOST, + VIR_DOMAIN_SMARTCARD_TYPE_HOST_CERTIFICATES, + VIR_DOMAIN_SMARTCARD_TYPE_PASSTHROUGH, + + VIR_DOMAIN_SMARTCARD_TYPE_LAST, +}; + +# define VIR_DOMAIN_SMARTCARD_NUM_CERTIFICATES 3 + +typedef struct _virDomainSmartcardDef virDomainSmartcardDef; +typedef virDomainSmartcardDef *virDomainSmartcardDefPtr; +struct _virDomainSmartcardDef { + int type; /* virDomainSmartcardType */ + union { + /* No extra data for host */ + struct { + char *file[VIR_DOMAIN_SMARTCARD_NUM_CERTIFICATES]; + } cert; /* host-certificates */ + virDomainChrSourceDef passthru; /* passthrough */ + } data; + + virDomainDeviceInfo info; +}; + enum virDomainInputType { VIR_DOMAIN_INPUT_TYPE_MOUSE, VIR_DOMAIN_INPUT_TYPE_TABLET, @@ -1005,6 +1030,9 @@ struct _virDomainDef { int nhostdevs; virDomainHostdevDefPtr *hostdevs; + int nsmartcards; + virDomainSmartcardDefPtr *smartcards; + int nserials; virDomainChrDefPtr *serials; @@ -1083,6 +1111,7 @@ void virDomainDiskHostDefFree(virDomainDiskHostDefPtr def); void virDomainControllerDefFree(virDomainControllerDefPtr def); void virDomainFSDefFree(virDomainFSDefPtr def); void virDomainNetDefFree(virDomainNetDefPtr def); +void virDomainSmartcardDefFree(virDomainSmartcardDefPtr def); void virDomainChrDefFree(virDomainChrDefPtr def); void virDomainChrSourceDefFree(virDomainChrSourceDefPtr def); void virDomainSoundDefFree(virDomainSoundDefPtr def); @@ -1230,6 +1259,15 @@ int virDomainObjListGetInactiveNames(virDomainObjListPtr doms, char **const names, int maxnames); +typedef int (*virDomainSmartcardDefIterator)(virDomainDefPtr def, + virDomainSmartcardDefPtr dev, + void *opaque); + +int virDomainSmartcardDefForeach(virDomainDefPtr def, + bool abortOnError, + virDomainSmartcardDefIterator iter, + void *opaque); + typedef int (*virDomainChrDefIterator)(virDomainDefPtr def, virDomainChrDefPtr dev, void *opaque); @@ -1239,7 +1277,6 @@ int virDomainChrDefForeach(virDomainDefPtr def, virDomainChrDefIterator iter, void *opaque); - typedef int (*virDomainDiskDefPathIterator)(virDomainDiskDefPtr disk, const char *path, size_t depth, @@ -1276,6 +1313,7 @@ VIR_ENUM_DECL(virDomainNet) VIR_ENUM_DECL(virDomainChrDevice) VIR_ENUM_DECL(virDomainChrChannelTarget) VIR_ENUM_DECL(virDomainChrConsoleTarget) +VIR_ENUM_DECL(virDomainSmartcard) VIR_ENUM_DECL(virDomainChr) VIR_ENUM_DECL(virDomainChrTcpProtocol) VIR_ENUM_DECL(virDomainSoundModel) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 2ce4bed..7e4dfca 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -280,6 +280,10 @@ virDomainRemoveInactive; virDomainSaveConfig; virDomainSaveStatus; virDomainSaveXML; +virDomainSmartcardDefForeach; +virDomainSmartcardDefFree; +virDomainSmartcardTypeFromString; +virDomainSmartcardTypeToString; virDomainSnapshotAssignDef; virDomainSnapshotDefFormat; virDomainSnapshotDefFree; -- 1.7.3.4

On Thu, Jan 13, 2011 at 05:34:36PM -0700, Eric Blake wrote:
* src/conf/domain_conf.h (virDomainSmartcardType): New enum. (virDomainSmartcardDef): New struct. (virDomainDef): Include smartcards. (virDomainSmartcardDefIterator): New typedef. (virDomainSmartcardDefFree, virDomainSmartcardDefForeach): New prototypes. * src/conf/domain_conf.c (virDomainSmartcard): Convert between enum and string. (virDomainSmartcardDefParseXML, virDomainSmartcardDefFormat) (virDomainSmartcardDefFree): New functions. (virDomainDefParseXML): Parse the new XML. (virDomainDefFormat): Convert back to XML. (virDomainDefFree): Clean up. (virDomainDeviceInfoIterate): Iterate over passthrough aliases. * src/libvirt_private.syms (domain_conf.h): New exports. * cfg.mk (useless_free_options): List new function. --- cfg.mk | 1 + src/conf/domain_conf.c | 224 ++++++++++++++++++++++++++++++++++++++++++++++ src/conf/domain_conf.h | 40 ++++++++- src/libvirt_private.syms | 4 + 4 files changed, 268 insertions(+), 1 deletions(-)
+static virDomainSmartcardDefPtr +virDomainSmartcardDefParseXML(xmlNodePtr node, + int flags) +{ + xmlNodePtr cur; + char *mode = NULL; + char *type = NULL; + virDomainSmartcardDefPtr def; + int i; + + if (VIR_ALLOC(def) < 0) { + virReportOOMError(); + return NULL; + } + + mode = virXMLPropString(node, "mode"); + if (mode == NULL) { + virDomainReportError(VIR_ERR_XML_ERROR, "%s", + _("missing smartcard device mode")); + goto error; + } + if ((def->type = virDomainSmartcardTypeFromString(mode)) < 0) { + virDomainReportError(VIR_ERR_XML_ERROR, + _("unknown smartcard device mode: %s"), + mode); + goto error; + } + + switch (def->type) { + case VIR_DOMAIN_SMARTCARD_TYPE_HOST: + break; + + case VIR_DOMAIN_SMARTCARD_TYPE_HOST_CERTIFICATES: + i = 0; + for (cur = node->children; + cur && cur->type == XML_ELEMENT_NODE && + xmlStrEqual(cur->name, BAD_CAST "certificate"); + cur = cur->next) { + def->data.cert.file[i++] = (char *)xmlNodeGetContent(cur); + }
I think xmlNodeGetContent can return NULL. We should likely also validate that it starts with a '/', so people can't just supply whitespace or random garbage.
+ if (i != 3 || cur->next) { + virDomainReportError(VIR_ERR_XML_ERROR, "%s", + _("host-certificates mode needs exactly " + "three certificates")); + goto error; + } + break; + + case VIR_DOMAIN_SMARTCARD_TYPE_PASSTHROUGH: + type = virXMLPropString(node, "type"); + if (type == NULL) + def->data.passthru.type = VIR_DOMAIN_CHR_TYPE_TCP;
I'm not sure that we should be defaulting to TCP here, rather than making 'type' compulsory.
+ else if ((def->data.passthru.type = virDomainChrTypeFromString(type)) + < 0) { + virDomainReportError(VIR_ERR_XML_ERROR, + _("unknown type presented to host for " + "character device: %s"), type); + goto error; + } + + cur = node->children; + i = virDomainChrSourceDefParseXML(&def->data.passthru, cur); + if (i < 0) + goto error; + if (i) { + if (virDomainDeviceInfoParseXML(node, &def->info, flags) < 0) + goto error; + /* An active domain may have an alias, but no address. */ + if (def->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) { + virDomainReportError(VIR_ERR_XML_ERROR, + _("smartcard should not have an " + "<address> element")); + goto error; + } + } + break; + + default: + virDomainReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("unknown smartcard mode")); + goto error; + }
Daniel

On 01/14/2011 05:29 AM, Daniel P. Berrange wrote:
+ case VIR_DOMAIN_SMARTCARD_TYPE_HOST_CERTIFICATES: + i = 0; + for (cur = node->children; + cur && cur->type == XML_ELEMENT_NODE && + xmlStrEqual(cur->name, BAD_CAST "certificate"); + cur = cur->next) { + def->data.cert.file[i++] = (char *)xmlNodeGetContent(cur); + }
I think xmlNodeGetContent can return NULL. We should likely also validate that it starts with a '/', so people can't just supply whitespace or random garbage.
Will make that change. Also, given the qemu command line constraints, the file cannot contain ',' (any other character can be parsed fine), so I'm updating patch 5/5 to reject unusable filenames (while keeping the overall domain_conf generic in allowing any filename).
+ case VIR_DOMAIN_SMARTCARD_TYPE_PASSTHROUGH: + type = virXMLPropString(node, "type"); + if (type == NULL) + def->data.passthru.type = VIR_DOMAIN_CHR_TYPE_TCP;
I'm not sure that we should be defaulting to TCP here, rather than making 'type' compulsory.
Hmm. My v1 proposal had the type attribute be mandatory for mode='passthrough', and I had changed it to be optional in v2; but it's easy enough to go back to having type be mandatory for v3. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

Still to go - add .args files to match .xml files in testsuite * src/qemu/qemu_command.c (qemuBuildCommandLine): Emit smartcard options. (qemuAssignDeviceAliases): Assign an alias for smartcard passthrough. * tests/qemuxml2argvtest.c (mymain): Three new tests. * tests/qemuxml2argvdata/...arg: Three new files. --- Well, as you can see, the tests/ part isn't done yet. src/qemu/qemu_command.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 49 insertions(+), 0 deletions(-) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 205c303..d02241f 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -604,6 +604,11 @@ qemuAssignDeviceAliases(virDomainDefPtr def, unsigned long long qemuCmdFlags) if (virAsprintf(&def->channels[i]->info.alias, "channel%d", i) < 0) goto no_memory; } + for (i = 0; i < def->nsmartcards ; i++) { + if (def->smartcards[i]->type == VIR_DOMAIN_SMARTCARD_TYPE_PASSTHROUGH && + virAsprintf(&def->smartcards[i]->info.alias, "smartcard%d", i) < 0) + goto no_memory; + } if (def->console) { if (virAsprintf(&def->console->info.alias, "console%d", i) < 0) goto no_memory; @@ -3332,6 +3337,50 @@ qemuBuildCommandLine(virConnectPtr conn, } } + if (def->nsmartcards) { + /* Requires -chardev and -device usb-ccid */ + if (!(qemuCmdFlags & QEMUD_CMD_FLAG_CHARDEV) || + !(qemuCmdFlags & QEMUD_CMD_FLAG_USB_CCID)) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("smartcard requires QEMU to support -usb-ccid")); + goto error; + } + virCommandAddArgList(cmd, "-device", "usb-ccid", NULL); + } + for (i = 0 ; i < def->nsmartcards ; i++) { + virDomainSmartcardDefPtr smartcard = def->smartcards[i]; + char *devstr; + virBuffer smartcard_buf = VIR_BUFFER_INITIALIZER; + int j; + + switch (smartcard->type) { + case VIR_DOMAIN_SMARTCARD_TYPE_HOST: + virCommandAddArgList(cmd, "-device", "ccid-card-emulated", NULL); + break; + case VIR_DOMAIN_SMARTCARD_TYPE_HOST_CERTIFICATES: + virCommandAddArg(cmd, "-device"); + virBufferAddLit(&smartcard_buf, + "ccid-card-emulated,backend=certificates"); + for (j = 0; j < VIR_DOMAIN_SMARTCARD_NUM_CERTIFICATES; j++) + virBufferVSprintf(&smartcard_buf, ",cert%d=%s", j + 1, + smartcard->data.cert.file[j]); + virCommandAddArgBuffer(cmd, &smartcard_buf); + break; + case VIR_DOMAIN_SMARTCARD_TYPE_PASSTHROUGH: + virCommandAddArg(cmd, "-chardev"); + if (!(devstr = qemuBuildChrChardevStr(&smartcard->data.passthru, + smartcard->info.alias))) + goto error; + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); + + virCommandAddArg(cmd, "-device"); + virCommandAddArgFormat(cmd, "ccid-card-passthru,chardev=%s", + smartcard->info.alias); + break; + } + } + if (!def->nserials) { /* If we have -device, then we set -nodefault already */ if (!(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) -- 1.7.3.4

On Thu, Jan 13, 2011 at 05:34:37PM -0700, Eric Blake wrote:
Still to go - add .args files to match .xml files in testsuite
* src/qemu/qemu_command.c (qemuBuildCommandLine): Emit smartcard options. (qemuAssignDeviceAliases): Assign an alias for smartcard passthrough. * tests/qemuxml2argvtest.c (mymain): Three new tests. * tests/qemuxml2argvdata/...arg: Three new files. ---
Well, as you can see, the tests/ part isn't done yet.
src/qemu/qemu_command.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++
What is the status of this support wrt upstream QEMU ? Has the smartcard stuff been accepted into GIT yet ? Also there is where the lack of security driver integration will cause problems, because QEMU won't start.
1 files changed, 49 insertions(+), 0 deletions(-)
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 205c303..d02241f 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -604,6 +604,11 @@ qemuAssignDeviceAliases(virDomainDefPtr def, unsigned long long qemuCmdFlags) if (virAsprintf(&def->channels[i]->info.alias, "channel%d", i) < 0) goto no_memory; } + for (i = 0; i < def->nsmartcards ; i++) { + if (def->smartcards[i]->type == VIR_DOMAIN_SMARTCARD_TYPE_PASSTHROUGH && + virAsprintf(&def->smartcards[i]->info.alias, "smartcard%d", i) < 0) + goto no_memory; + }
You've assigned device aliases here....
if (def->console) { if (virAsprintf(&def->console->info.alias, "console%d", i) < 0) goto no_memory; @@ -3332,6 +3337,50 @@ qemuBuildCommandLine(virConnectPtr conn, } }
+ if (def->nsmartcards) { + /* Requires -chardev and -device usb-ccid */ + if (!(qemuCmdFlags & QEMUD_CMD_FLAG_CHARDEV) || + !(qemuCmdFlags & QEMUD_CMD_FLAG_USB_CCID)) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("smartcard requires QEMU to support -usb-ccid")); + goto error; + } + virCommandAddArgList(cmd, "-device", "usb-ccid", NULL);
Ideally this would have an id= field too, eg if=ccid0
+ } + for (i = 0 ; i < def->nsmartcards ; i++) { + virDomainSmartcardDefPtr smartcard = def->smartcards[i]; + char *devstr; + virBuffer smartcard_buf = VIR_BUFFER_INITIALIZER; + int j; +
....But in this following block of code never added the aliases to the -device options:
+ switch (smartcard->type) { + case VIR_DOMAIN_SMARTCARD_TYPE_HOST: + virCommandAddArgList(cmd, "-device", "ccid-card-emulated", NULL); + break; + case VIR_DOMAIN_SMARTCARD_TYPE_HOST_CERTIFICATES: + virCommandAddArg(cmd, "-device"); + virBufferAddLit(&smartcard_buf, + "ccid-card-emulated,backend=certificates"); + for (j = 0; j < VIR_DOMAIN_SMARTCARD_NUM_CERTIFICATES; j++) + virBufferVSprintf(&smartcard_buf, ",cert%d=%s", j + 1, + smartcard->data.cert.file[j]);
I guess we need some kind of escaping of special chars in the filename here since we append 3 in a row, it might confuse QEMU's parser. Not sure offhand how QEMU expects that to work. Or since we already depend on existance of -device, we might want to use '-set' for setting the filenames and thus avoid need to escape, eg -device ccid-card-emulated,backend=certificates,id=smartcard0 -set ccid-card-emulated.smartcard0.cert0=$FILENAME -set ccid-card-emulated.smartcard0.cert1=$FILENAME -set ccid-card-emulated.smartcard0.cert2=$FILENAME albeit at the cost of much longer command lines.
+ virCommandAddArgBuffer(cmd, &smartcard_buf); + break; + case VIR_DOMAIN_SMARTCARD_TYPE_PASSTHROUGH: + virCommandAddArg(cmd, "-chardev"); + if (!(devstr = qemuBuildChrChardevStr(&smartcard->data.passthru, + smartcard->info.alias))) + goto error;
The 'id' setting for the chardev should really have a prefix on it, because we won't want it to be the same as the 'id' setting used for the -device option.
+ virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); + + virCommandAddArg(cmd, "-device"); + virCommandAddArgFormat(cmd, "ccid-card-passthru,chardev=%s", + smartcard->info.alias); + break; + } + } + if (!def->nserials) { /* If we have -device, then we set -nodefault already */ if (!(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE))
We currently only provide a single '-device usb-ccid' device. It looks like we're relying on the ccid-card-XXX devices automagically associating themselves with that. It would be better to explicitly link them up by specifying the 'id' of the 'usb-ccid' device to which they must be associated. If QEMU doesn't have such an explicit link, it ought to be added. Also, does the order of '-device ccid-card-XXX' devices matter ie is the ordering a guest visible ABI ? If so, then we need to track an explicit address against each <smartcard> so that we can selectively hotplug/hotunplug devices, and have a stable ABI across migration. Regards, Daniel

On Fri, Jan 14, 2011 at 12:41:13PM +0000, Daniel P. Berrange wrote:
On Thu, Jan 13, 2011 at 05:34:37PM -0700, Eric Blake wrote:
+ virCommandAddArgBuffer(cmd, &smartcard_buf); + break; + case VIR_DOMAIN_SMARTCARD_TYPE_PASSTHROUGH: + virCommandAddArg(cmd, "-chardev"); + if (!(devstr = qemuBuildChrChardevStr(&smartcard->data.passthru, + smartcard->info.alias))) + goto error;
The 'id' setting for the chardev should really have a prefix on it, because we won't want it to be the same as the 'id' setting used for the -device option.
This is in fact a pre-existing flaw in our chardev handling which we need to fix. The 'qemuBuildChrChardevStr' API & its callers are buggy. They takes the assigned alias from the XML and use it directly for the chardev. So we end up with -chardev pty,id=$alias -device isa-serial,chardev=$alias The serial/parallel/channels -device args are all missing any 'id' property entirely which is bad, because it means we won't be able to support hotplug. What we should be generating is -chardev pty,id=cdev$alias -device isa-serial,id=$alias,chardev=cdev$alias So that both parts have a unique identifier assigned, and the alias from the XML is associated with the '-device' arg, and the -chardev is a derivative. Regards, Daniel

On 01/14/2011 06:46 AM, Daniel P. Berrange wrote:
On Fri, Jan 14, 2011 at 12:41:13PM +0000, Daniel P. Berrange wrote:
On Thu, Jan 13, 2011 at 05:34:37PM -0700, Eric Blake wrote:
+ virCommandAddArgBuffer(cmd, &smartcard_buf); + break; + case VIR_DOMAIN_SMARTCARD_TYPE_PASSTHROUGH: + virCommandAddArg(cmd, "-chardev"); + if (!(devstr = qemuBuildChrChardevStr(&smartcard->data.passthru, + smartcard->info.alias))) + goto error;
The 'id' setting for the chardev should really have a prefix on it, because we won't want it to be the same as the 'id' setting used for the -device option.
This is in fact a pre-existing flaw in our chardev handling which we need to fix. The 'qemuBuildChrChardevStr' API & its callers are buggy. They takes the assigned alias from the XML and use it directly for the chardev. So we end up with
-chardev pty,id=$alias -device isa-serial,chardev=$alias
Another big flaw in our chardev handling is that we haven't yet got any support in qemuDomainXMLToNative for parsing -chardev, which means it all shows up as qemu:command elements instead. The last time we were able to convert from qemu command line back to XML was for qemu 0.11.x. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On 01/14/2011 06:46 AM, Daniel P. Berrange wrote:
The 'id' setting for the chardev should really have a prefix on it, because we won't want it to be the same as the 'id' setting used for the -device option.
This is in fact a pre-existing flaw in our chardev handling which we need to fix. The 'qemuBuildChrChardevStr' API & its callers are buggy. They takes the assigned alias from the XML and use it directly for the chardev. So we end up with
-chardev pty,id=$alias -device isa-serial,chardev=$alias
The serial/parallel/channels -device args are all missing any 'id' property entirely which is bad, because it means we won't be able to support hotplug. What we should be generating is
-chardev pty,id=cdev$alias -device isa-serial,id=$alias,chardev=cdev$alias
So that both parts have a unique identifier assigned, and the alias from the XML is associated with the '-device' arg, and the -chardev is a derivative.
Done with the attached patch. git send-email won't let me send it directly, since it has a line longer than SMTP limits for inline mail: fatal: /tmp/canS3cO9Y0/0001-qemu-use-separate-alias-for-chardev-and-associated-d.patch: 163: patch contains a line longer than 998 characters warning: no patches were sent -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On Tue, Jan 25, 2011 at 03:04:57PM -0700, Eric Blake wrote:
On 01/14/2011 06:46 AM, Daniel P. Berrange wrote:
The 'id' setting for the chardev should really have a prefix on it, because we won't want it to be the same as the 'id' setting used for the -device option.
This is in fact a pre-existing flaw in our chardev handling which we need to fix. The 'qemuBuildChrChardevStr' API & its callers are buggy. They takes the assigned alias from the XML and use it directly for the chardev. So we end up with
-chardev pty,id=$alias -device isa-serial,chardev=$alias
The serial/parallel/channels -device args are all missing any 'id' property entirely which is bad, because it means we won't be able to support hotplug. What we should be generating is
-chardev pty,id=cdev$alias -device isa-serial,id=$alias,chardev=cdev$alias
So that both parts have a unique identifier assigned, and the alias from the XML is associated with the '-device' arg, and the -chardev is a derivative.
Done with the attached patch. git send-email won't let me send it directly, since it has a line longer than SMTP limits for inline mail:
fatal: /tmp/canS3cO9Y0/0001-qemu-use-separate-alias-for-chardev-and-associated-d.patch: 163: patch contains a line longer than 998 characters warning: no patches were sent
-- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org
From d4a3f317dbd6b5b513b505d0eee21f9ec1a2d362 Mon Sep 17 00:00:00 2001 From: Eric Blake <eblake@redhat.com> Date: Tue, 25 Jan 2011 14:59:50 -0700 Subject: [PATCH] qemu: use separate alias for chardev and associated device
* src/qemu/qemu_command.c (qemuBuildChrChardevStr): Alter the chardev alias. (qemuBuildCommandLine): Output an id for the chardev counterpart. * tests/qemuxml2argvdata/*: Update tests to match. Reported by Daniel P. Berrange. --- src/qemu/qemu_command.c | 36 +++++++++++--------- .../qemuxml2argv-channel-guestfwd.args | 2 +- .../qemuxml2argv-channel-virtio-auto.args | 2 +- .../qemuxml2argv-channel-virtio.args | 2 +- .../qemuxml2argv-console-compat-chardev.args | 2 +- .../qemuxml2argv-console-virtio.args | 2 +- .../qemuxml2argv-parallel-tcp-chardev.args | 2 +- .../qemuxml2argv-serial-dev-chardev.args | 2 +- .../qemuxml2argv-serial-file-chardev.args | 2 +- .../qemuxml2argv-serial-many-chardev.args | 2 +- .../qemuxml2argv-serial-pty-chardev.args | 2 +- .../qemuxml2argv-serial-tcp-chardev.args | 2 +- .../qemuxml2argv-serial-tcp-telnet-chardev.args | 2 +- .../qemuxml2argv-serial-udp-chardev.args | 2 +- .../qemuxml2argv-serial-unix-chardev.args | 2 +- .../qemuxml2argv-serial-vc-chardev.args | 2 +- 16 files changed, 35 insertions(+), 31 deletions(-)
ACK Daniel

On 01/26/2011 05:29 AM, Daniel P. Berrange wrote:
On Tue, Jan 25, 2011 at 03:04:57PM -0700, Eric Blake wrote:
On 01/14/2011 06:46 AM, Daniel P. Berrange wrote:
The 'id' setting for the chardev should really have a prefix on it, because we won't want it to be the same as the 'id' setting used for the -device option.
Done with the attached patch. git send-email won't let me send it directly, since it has a line longer than SMTP limits for inline mail:
fatal: /tmp/canS3cO9Y0/0001-qemu-use-separate-alias-for-chardev-and-associated-d.patch: 163: patch contains a line longer than 998 characters warning: no patches were sent
-- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org
From d4a3f317dbd6b5b513b505d0eee21f9ec1a2d362 Mon Sep 17 00:00:00 2001 From: Eric Blake <eblake@redhat.com> Date: Tue, 25 Jan 2011 14:59:50 -0700 Subject: [PATCH] qemu: use separate alias for chardev and associated device
* src/qemu/qemu_command.c (qemuBuildChrChardevStr): Alter the chardev alias. (qemuBuildCommandLine): Output an id for the chardev counterpart. * tests/qemuxml2argvdata/*: Update tests to match. Reported by Daniel P. Berrange.
ACK
Thanks. This was independent enough of smartcard that I went ahead and pushed it. (For smartcard, I'm still working on addressing the review comments from v3; mainly the minor tweaks to the xml documentation to match reality, and fallout in the code). -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On 01/14/2011 05:41 AM, Daniel P. Berrange wrote:
On Thu, Jan 13, 2011 at 05:34:37PM -0700, Eric Blake wrote:
Still to go - add .args files to match .xml files in testsuite
* src/qemu/qemu_command.c (qemuBuildCommandLine): Emit smartcard options. (qemuAssignDeviceAliases): Assign an alias for smartcard passthrough. * tests/qemuxml2argvtest.c (mymain): Three new tests. * tests/qemuxml2argvdata/...arg: Three new files. ---
Well, as you can see, the tests/ part isn't done yet.
src/qemu/qemu_command.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++
What is the status of this support wrt upstream QEMU ? Has the smartcard stuff been accepted into GIT yet ?
Submitted, but not yet included (so another reason that I shouldn't necessarily push this upstream yet). http://patchwork.ozlabs.org/patch/78297/
Also there is where the lack of security driver integration will cause problems, because QEMU won't start.
Yes, will address that in v3.
@@ -604,6 +604,11 @@ qemuAssignDeviceAliases(virDomainDefPtr def, unsigned long long qemuCmdFlags) if (virAsprintf(&def->channels[i]->info.alias, "channel%d", i) < 0) goto no_memory; } + for (i = 0; i < def->nsmartcards ; i++) { + if (def->smartcards[i]->type == VIR_DOMAIN_SMARTCARD_TYPE_PASSTHROUGH && + virAsprintf(&def->smartcards[i]->info.alias, "smartcard%d", i) < 0) + goto no_memory; + }
You've assigned device aliases here....
if (def->console) { if (virAsprintf(&def->console->info.alias, "console%d", i) < 0) goto no_memory; @@ -3332,6 +3337,50 @@ qemuBuildCommandLine(virConnectPtr conn, } }
+ if (def->nsmartcards) { + /* Requires -chardev and -device usb-ccid */ + if (!(qemuCmdFlags & QEMUD_CMD_FLAG_CHARDEV) || + !(qemuCmdFlags & QEMUD_CMD_FLAG_USB_CCID)) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("smartcard requires QEMU to support -usb-ccid")); + goto error; + } + virCommandAddArgList(cmd, "-device", "usb-ccid", NULL);
Ideally this would have an id= field too, eg if=ccid0
My understanding is that we only need one -device usb-ccid; that one device is necessary to provide the bus shared by all other -device ccid-card-* entries. So the alias defined above is for the ccid-card. I guess that means you want a hard-coded alias here: virCommandAddArgList(cmd, "-device", "usb-ccid,id=ccid0", NULL); But with Cole's recent sound patch, we added -device hda-output without any id= notation (another case where in addition to the real -device intel-hda,id=sound4 to match the <sound> element, we also needed the secondary bus -device hda-sound as behind-the-scenes glue).
+ } + for (i = 0 ; i < def->nsmartcards ; i++) { + virDomainSmartcardDefPtr smartcard = def->smartcards[i]; + char *devstr; + virBuffer smartcard_buf = VIR_BUFFER_INITIALIZER; + int j; +
....But in this following block of code never added the aliases to the -device options:
I assumed that the alias was only needed for the case of tying a chardev to a -device ccid-card-passthru (the -device ccid-card-emulated does not have an associated chardev). Which is why the DEVICE_TYPE_PASSTHROUGH block _does_ use the alias (just the TYPE_HOST and TYPE_HOST_CERTIFICATES did not use it, but it wasn't generated for those cases).
+ switch (smartcard->type) { + case VIR_DOMAIN_SMARTCARD_TYPE_HOST: + virCommandAddArgList(cmd, "-device", "ccid-card-emulated", NULL); + break; + case VIR_DOMAIN_SMARTCARD_TYPE_HOST_CERTIFICATES: + virCommandAddArg(cmd, "-device"); + virBufferAddLit(&smartcard_buf, + "ccid-card-emulated,backend=certificates"); + for (j = 0; j < VIR_DOMAIN_SMARTCARD_NUM_CERTIFICATES; j++) + virBufferVSprintf(&smartcard_buf, ",cert%d=%s", j + 1, + smartcard->data.cert.file[j]);
I guess we need some kind of escaping of special chars in the filename here since we append 3 in a row, it might confuse QEMU's parser. Not sure offhand how QEMU expects that to work.
It's only confusing if the filename contains a literal comma, so v3 will reject filenames with a literal comma.
Or since we already depend on existance of -device, we might want to use '-set' for setting the filenames and thus avoid need to escape, eg
-device ccid-card-emulated,backend=certificates,id=smartcard0 -set ccid-card-emulated.smartcard0.cert0=$FILENAME -set ccid-card-emulated.smartcard0.cert1=$FILENAME -set ccid-card-emulated.smartcard0.cert2=$FILENAME
albeit at the cost of much longer command lines.
Nah - a literal comma is rare enough to not go with this complexity unless someone has a compelling reason why we should.
+ virCommandAddArgBuffer(cmd, &smartcard_buf); + break; + case VIR_DOMAIN_SMARTCARD_TYPE_PASSTHROUGH: + virCommandAddArg(cmd, "-chardev"); + if (!(devstr = qemuBuildChrChardevStr(&smartcard->data.passthru, + smartcard->info.alias))) + goto error;
The 'id' setting for the chardev should really have a prefix on it, because we won't want it to be the same as the 'id' setting used for the -device option.
But we don't do that for any of our other -chardevs. Oh, are you saying that we first need to patch things to carry dual aliases to distinguish between -chardev and its associated -device, and that every object passed to qemu (and not just -chardev) should be given an alias? Is that a prerequisite to getting this series in, or should it be a separate cleanup patch that can come later?
+ virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); + + virCommandAddArg(cmd, "-device"); + virCommandAddArgFormat(cmd, "ccid-card-passthru,chardev=%s", + smartcard->info.alias); + break; + } + } + if (!def->nserials) { /* If we have -device, then we set -nodefault already */ if (!(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE))
We currently only provide a single '-device usb-ccid' device. It looks like we're relying on the ccid-card-XXX devices automagically associating themselves with that. It would be better to explicitly link them up by specifying the 'id' of the 'usb-ccid' device to which they must be associated. If QEMU doesn't have such an explicit link, it ought to be added.
That's how Alon implemented it. -device usb-ccid is instantiated exactly once, and is not tied to any -chardev. -device ccid-card-emulated automatically uses the services of -device usb-ccid, and does not need any -chardev. And -device ccid-card-passthru automatically uses the services of -device usb-ccid, and requires an associated -chardev (Alon has only tested with a tcp chardev and with a spicevmc,name=smartcard chardev). At this point, your complaints seem to be more about whether Alon's qemu command line implementation makes sense (which, given that the smartcard patches have been proposed but are not yet upstream, may mean that the final qemu implementation will change and my libvirt patch would have to equally change to match), rather than whether I'm correctly mapping to the command line that Alon documented: http://cgit.freedesktop.org/~alon/qemu/commit/?h=usb_ccid.v15&id=024a37bf696ae3a8d148a2ecbecdd3d92d390b2a Of course, providing some .args test files in v3 will help make it a little easier to review.
Also, does the order of '-device ccid-card-XXX' devices matter ie is the ordering a guest visible ABI ? If so, then we need to track an explicit address against each <smartcard> so that we can selectively hotplug/hotunplug devices, and have a stable ABI across migration.
That I'm not sure about. Alon said that the guest sees the smartcard as a USB device, but didn't mention anything about whether -device ccid-card-* can take additional USB addressing parameters to lock down where the device appears on the guest's USB bus. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On Fri, Jan 14, 2011 at 11:25:36AM -0700, Eric Blake wrote:
On 01/14/2011 05:41 AM, Daniel P. Berrange wrote:
On Thu, Jan 13, 2011 at 05:34:37PM -0700, Eric Blake wrote:
+ virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); + + virCommandAddArg(cmd, "-device"); + virCommandAddArgFormat(cmd, "ccid-card-passthru,chardev=%s", + smartcard->info.alias); + break; + } + } + if (!def->nserials) { /* If we have -device, then we set -nodefault already */ if (!(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE))
We currently only provide a single '-device usb-ccid' device. It looks like we're relying on the ccid-card-XXX devices automagically associating themselves with that. It would be better to explicitly link them up by specifying the 'id' of the 'usb-ccid' device to which they must be associated. If QEMU doesn't have such an explicit link, it ought to be added.
That's how Alon implemented it. -device usb-ccid is instantiated exactly once, and is not tied to any -chardev. -device ccid-card-emulated automatically uses the services of -device usb-ccid, and does not need any -chardev. And -device ccid-card-passthru automatically uses the services of -device usb-ccid, and requires an associated -chardev (Alon has only tested with a tcp chardev and with a spicevmc,name=smartcard chardev). At this point, your complaints seem to be more about whether Alon's qemu command line implementation makes sense (which, given that the smartcard patches have been proposed but are not yet upstream, may mean that the final qemu implementation will change and my libvirt patch would have to equally change to match), rather than whether I'm correctly mapping to the command line that Alon documented:
Alon's docs are showing the simplified syntax suitable for end users. This doesn't guarentee a stable guest visible ABI. Looking at the code, we need to set the 'slot' parameter on each ccid device we have. This means we need a new address type for smart card devices, and a corresponding <controller> instance. So in the XML we'd get (including libvirt generated aliases and addresses): <devices> <controller type='ccid' index='0'> <alias id='ccid0'/> </controller> <controller type='ccid' index='1'> <alias id='ccid1'/> </controller> <smartcard mode='host'> <alias id='smartcard0'/> <address type='ccid' controller='0' slot='0'/> </smartcard> <smartcard mode='host-certificates'> <certificate>/path/to/cert1</certificate> <certificate>/path/to/cert2</certificate> <certificate>/path/to/cert3</certificate> <alias id='smartcard1'/> <address type='ccid' controller='0' slot='3'/> </smartcard> <smartcard mode='passthrough' type='tcp'> <source mode='connect' host='127.0.0.1' service='2001'/> <protocol type='raw'/> <alias id='smartcard2'/> <address type='ccid' controller='1' slot='0'/> </smartcard> </devices> Which would end up mapping to -device usb-ccid,id=ccid0 -device usb-ccid,id=ccid1 -device ccid-host,id=smartcard0,bus=ccid0,slot=0 -device ccid-certificates,id=smartcard1,bus=ccid0,slot=3... -device ccid-passthrough,id=smartcard2,bus=ccid1,slot=0... In other words a hierarchy USB bus 0 | +- ccid0 | | | +- smartcard0 (ccid slot 0) | +- smartcard1 (ccid slot 3) | +- ccid1 | +- smartcard2 (ccid slot 0) This is a pretty similar setup to the way we link virtio-serial devices and vmchannels
Also, does the order of '-device ccid-card-XXX' devices matter ie is the ordering a guest visible ABI ? If so, then we need to track an explicit address against each <smartcard> so that we can selectively hotplug/hotunplug devices, and have a stable ABI across migration.
That I'm not sure about. Alon said that the guest sees the smartcard as a USB device, but didn't mention anything about whether -device ccid-card-* can take additional USB addressing parameters to lock down where the device appears on the guest's USB bus.
The 'usb-ccid' is on the USB bus, but we don't have stable addressing for that, because the guest OS controls USB addresses. We do need the stable slots for the <smartcards> though. Daniel

On 01/14/2011 12:23 PM, Daniel P. Berrange wrote:
Alon's docs are showing the simplified syntax suitable for end users. This doesn't guarentee a stable guest visible ABI. Looking at the code, we need to set the 'slot' parameter on each ccid device we have. This means we need a new address type for smart card devices, and a corresponding <controller> instance.
Thanks - that clears up a lot for me. However, I still have a question:
So in the XML we'd get (including libvirt generated aliases and addresses):
<devices> <controller type='ccid' index='0'> <alias id='ccid0'/> </controller>
Are you suggesting that the XML mandate that the user provides a <controller type=ccid'>, or should I still shoot for the idea that if the user omits <controller> but provides <smartcard> that we go ahead and auto-create a controller (assigning it to the next available slot in the process)?
<smartcard mode='host'> <alias id='smartcard0'/> <address type='ccid' controller='0' slot='0'/>
So I _do_ need an (optional) <address> subelement for <smartcard> after all. Will go back and get that into my series.
In other words a hierarchy
USB bus 0 | +- ccid0 | | | +- smartcard0 (ccid slot 0) | +- smartcard1 (ccid slot 3) | +- ccid1 | +- smartcard2 (ccid slot 0)
Very helpful. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On Fri, Jan 14, 2011 at 12:34:21PM -0700, Eric Blake wrote:
On 01/14/2011 12:23 PM, Daniel P. Berrange wrote:
Alon's docs are showing the simplified syntax suitable for end users. This doesn't guarentee a stable guest visible ABI. Looking at the code, we need to set the 'slot' parameter on each ccid device we have. This means we need a new address type for smart card devices, and a corresponding <controller> instance.
Thanks - that clears up a lot for me. However, I still have a question:
So in the XML we'd get (including libvirt generated aliases and addresses):
<devices> <controller type='ccid' index='0'> <alias id='ccid0'/> </controller>
Are you suggesting that the XML mandate that the user provides a <controller type=ccid'>, or should I still shoot for the idea that if the user omits <controller> but provides <smartcard> that we go ahead and auto-create a controller (assigning it to the next available slot in the process)?
The user should be able to specify the <controller> manually, but if a <smartcard> is seen, and there's no <controller> that matches the controller index in the smartcard's <address>, then autocreate one. And when auto-filling in <address>, default to setting controller index to 0 if not present already. Use virDomainDefMaybeAddVirtioSerialController and virtio-serial controller / <channel> as an example of the behaviour to follow.
<smartcard mode='host'> <alias id='smartcard0'/> <address type='ccid' controller='0' slot='0'/>
So I _do_ need an (optional) <address> subelement for <smartcard> after all. Will go back and get that into my series.
In other words a hierarchy
USB bus 0 | +- ccid0 | | | +- smartcard0 (ccid slot 0) | +- smartcard1 (ccid slot 3) | +- ccid1 | +- smartcard2 (ccid slot 0)
Very helpful.
Oh, and if you want to understand wtf QEMU is doing then it is helpful to use 'info qtree' eg, $ qemu -nodefaults -device ....more -devices... -monitor stdio (qemu) info qtree and you'll see all the property names + hierarchy Daniel

On Fri, Jan 14, 2011 at 07:23:17PM +0000, Daniel P. Berrange wrote:
On Fri, Jan 14, 2011 at 11:25:36AM -0700, Eric Blake wrote:
On 01/14/2011 05:41 AM, Daniel P. Berrange wrote:
On Thu, Jan 13, 2011 at 05:34:37PM -0700, Eric Blake wrote:
+ virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); + + virCommandAddArg(cmd, "-device"); + virCommandAddArgFormat(cmd, "ccid-card-passthru,chardev=%s", + smartcard->info.alias); + break; + } + } + if (!def->nserials) { /* If we have -device, then we set -nodefault already */ if (!(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE))
We currently only provide a single '-device usb-ccid' device. It looks like we're relying on the ccid-card-XXX devices automagically associating themselves with that. It would be better to explicitly link them up by specifying the 'id' of the 'usb-ccid' device to which they must be associated. If QEMU doesn't have such an explicit link, it ought to be added.
That's how Alon implemented it. -device usb-ccid is instantiated exactly once, and is not tied to any -chardev. -device ccid-card-emulated automatically uses the services of -device usb-ccid, and does not need any -chardev. And -device ccid-card-passthru automatically uses the services of -device usb-ccid, and requires an associated -chardev (Alon has only tested with a tcp chardev and with a spicevmc,name=smartcard chardev). At this point, your complaints seem to be more about whether Alon's qemu command line implementation makes sense (which, given that the smartcard patches have been proposed but are not yet upstream, may mean that the final qemu implementation will change and my libvirt patch would have to equally change to match), rather than whether I'm correctly mapping to the command line that Alon documented:
Alon's docs are showing the simplified syntax suitable for end users. This doesn't guarentee a stable guest visible ABI. Looking at the code, we need to set the 'slot' parameter on each ccid device we have. This means we need a new address type for smart card devices, and a corresponding <controller> instance. So in the XML we'd get (including libvirt generated aliases and addresses):
<devices> <controller type='ccid' index='0'> <alias id='ccid0'/> </controller> <controller type='ccid' index='1'> <alias id='ccid1'/> </controller> <smartcard mode='host'> <alias id='smartcard0'/> <address type='ccid' controller='0' slot='0'/> </smartcard> <smartcard mode='host-certificates'> <certificate>/path/to/cert1</certificate> <certificate>/path/to/cert2</certificate> <certificate>/path/to/cert3</certificate> <alias id='smartcard1'/> <address type='ccid' controller='0' slot='3'/> </smartcard> <smartcard mode='passthrough' type='tcp'> <source mode='connect' host='127.0.0.1' service='2001'/> <protocol type='raw'/> <alias id='smartcard2'/> <address type='ccid' controller='1' slot='0'/> </smartcard> </devices>
Which would end up mapping to
-device usb-ccid,id=ccid0 -device usb-ccid,id=ccid1
-device ccid-host,id=smartcard0,bus=ccid0,slot=0 -device ccid-certificates,id=smartcard1,bus=ccid0,slot=3... -device ccid-passthrough,id=smartcard2,bus=ccid1,slot=0...
In other words a hierarchy
USB bus 0 | +- ccid0 | | | +- smartcard0 (ccid slot 0) | +- smartcard1 (ccid slot 3) | +- ccid1 | +- smartcard2 (ccid slot 0)
This is a pretty similar setup to the way we link virtio-serial devices and vmchannels
Also, does the order of '-device ccid-card-XXX' devices matter ie is the ordering a guest visible ABI ? If so, then we need to track an explicit address against each <smartcard> so that we can selectively hotplug/hotunplug devices, and have a stable ABI across migration.
That I'm not sure about. Alon said that the guest sees the smartcard as a USB device, but didn't mention anything about whether -device ccid-card-* can take additional USB addressing parameters to lock down where the device appears on the guest's USB bus.
The 'usb-ccid' is on the USB bus, but we don't have stable addressing for that, because the guest OS controls USB addresses. We do need the stable slots for the <smartcards> though.
Ok, that saves me some code searching - was trying to figure out how to specify addresses from command line for usb devices :) Regarding usb-ccid bus: 1) the id is currently done by the qdev code when supplied a NULL bus parameter, i.e. it takes the device id and appends ".0", so you get for instance: -device usb-ccid,id=ccid0 -device usb-ccid,id=ccid1 -device ccid-card-emulated,bus=ccid0.0 -device ccid-card-emulated,bus=ccid1.0 Becomes: bus: ccid1.0 type ccid-bus dev: ccid-card-emulated, id "" dev-prop: backend = "nss-emulated" dev-prop: cert1 = <null> dev-prop: cert2 = <null> dev-prop: cert3 = <null> dev-prop: db = <null> dev-prop: debug = 0 bus-prop: slot = 0 dev: usb-ccid, id "ccid0" dev-prop: debug = 0 addr 0.1, speed 12, name QEMU USB CCID, attached bus: ccid0.0 type ccid-bus dev: ccid-card-emulated, id "" dev-prop: backend = "nss-emulated" dev-prop: cert1 = <null> dev-prop: cert2 = <null> dev-prop: cert3 = <null> dev-prop: db = <null> dev-prop: debug = 0 bus-prop: slot = 0 Tell me if this needs to be changed - for instance I could just reuse the id as the bus name, so it will lose the ".0" suffix. 2) usb-ccid device doesn't support more then one slot atm. This may be changed later, but atm to create two slots you need two busses. Also, this is a bug, two emulated slots will probably fail (haven't tested), since I'm pretty sure I'm doing all the initialization twice in that case, and even if not, my event quering loop doesn't have a concept of a target, so some events will end up at the first, some at the second - chaos. The good news is that this is totally opaque to libvirt, just an implementation bug.
Daniel

On 01/15/2011 06:30 AM, Alon Levy wrote:
In other words a hierarchy
USB bus 0 | +- ccid0 | | | +- smartcard0 (ccid slot 0) | +- smartcard1 (ccid slot 3) | +- ccid1 | +- smartcard2 (ccid slot 0)
I'm okay with enforcing one-to-one correspondence for now (a ccid bus can have at most one smartcard, always at slot 0), as long as the resulting XML is easily extensible for any future qemu patch that allows multiple smartcards per ccid bus.
Regarding usb-ccid bus: 1) the id is currently done by the qdev code when supplied a NULL bus parameter, i.e. it takes the device id and appends ".0", so you get for instance:
-device usb-ccid,id=ccid0 -device usb-ccid,id=ccid1 -device ccid-card-emulated,bus=ccid0.0 -device ccid-card-emulated,bus=ccid1.0
Becomes:
bus: ccid1.0 type ccid-bus dev: ccid-card-emulated, id "" dev-prop: backend = "nss-emulated" dev-prop: cert1 = <null> dev-prop: cert2 = <null> dev-prop: cert3 = <null> dev-prop: db = <null>
Hmm - in the hosts-certificates mode, it looks like I need to support an optional <database> sub-element to populate the ccid-card-emulated.db field when desired. Is that field a pathname, a generic arbitrary string, or something else altogether?
dev-prop: debug = 0 bus-prop: slot = 0
That is, bus can be inferred (take the ccid device and append .0) if omitted in the user's XML, but will be explicitly supplied in the command-line generated by libvirt in case qemu ever allows a non-zero bus-prop: slot in the future.
Tell me if this needs to be changed - for instance I could just reuse the id as the bus name, so it will lose the ".0" suffix.
I think keeping the .0 suffix allows you the future ability to support multiple cards on a single bus.
2) usb-ccid device doesn't support more then one slot atm. This may be changed later, but atm to create two slots you need two busses. Also, this is a bug, two emulated slots will probably fail (haven't tested), since I'm pretty sure I'm doing all the initialization twice in that case, and even if not, my event quering loop doesn't have a concept of a target, so some events will end up at the first, some at the second - chaos. The good news is that this is totally opaque to libvirt, just an implementation bug.
Should libvirt enforce at most one smartcard until this is fixed in qemu, or should I go ahead and assume that smartcard won't be accepted into upstream qemu until after issues like this have been resolved? -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On Mon, Jan 17, 2011 at 11:30:38AM -0700, Eric Blake wrote:
On 01/15/2011 06:30 AM, Alon Levy wrote:
In other words a hierarchy
USB bus 0 | +- ccid0 | | | +- smartcard0 (ccid slot 0) | +- smartcard1 (ccid slot 3) | +- ccid1 | +- smartcard2 (ccid slot 0)
I'm okay with enforcing one-to-one correspondence for now (a ccid bus can have at most one smartcard, always at slot 0), as long as the resulting XML is easily extensible for any future qemu patch that allows multiple smartcards per ccid bus.
Regarding usb-ccid bus: 1) the id is currently done by the qdev code when supplied a NULL bus parameter, i.e. it takes the device id and appends ".0", so you get for instance:
-device usb-ccid,id=ccid0 -device usb-ccid,id=ccid1 -device ccid-card-emulated,bus=ccid0.0 -device ccid-card-emulated,bus=ccid1.0
Becomes:
bus: ccid1.0 type ccid-bus dev: ccid-card-emulated, id "" dev-prop: backend = "nss-emulated" dev-prop: cert1 = <null> dev-prop: cert2 = <null> dev-prop: cert3 = <null> dev-prop: db = <null>
Hmm - in the hosts-certificates mode, it looks like I need to support an optional <database> sub-element to populate the ccid-card-emulated.db field when desired. Is that field a pathname, a generic arbitrary string, or something else altogether?
Pathname is the only thing I've tested, so let's limit it to this. It's NSS specific right now too - but as you see from the arguments it will probably be orthogonal, for instance under windows there might be a cryptoapi-emulated backend and db argument will be reused.
dev-prop: debug = 0 bus-prop: slot = 0
That is, bus can be inferred (take the ccid device and append .0) if omitted in the user's XML, but will be explicitly supplied in the command-line generated by libvirt in case qemu ever allows a non-zero bus-prop: slot in the future.
Tell me if this needs to be changed - for instance I could just reuse the id as the bus name, so it will lose the ".0" suffix.
I think keeping the .0 suffix allows you the future ability to support multiple cards on a single bus.
The question was about changing my behavior, since right now I rely on the default qemu behavior of appending that ".0". So I understand you will supply that as a string bus=<id>.0, and since I actually ignore that but qemu computes the same bus id, it will just work.
2) usb-ccid device doesn't support more then one slot atm. This may be changed later, but atm to create two slots you need two busses. Also, this is a bug, two emulated slots will probably fail (haven't tested), since I'm pretty sure I'm doing all the initialization twice in that case, and even if not, my event quering loop doesn't have a concept of a target, so some events will end up at the first, some at the second - chaos. The good news is that this is totally opaque to libvirt, just an implementation bug.
Should libvirt enforce at most one smartcard until this is fixed in qemu, or should I go ahead and assume that smartcard won't be accepted into upstream qemu until after issues like this have been resolved?
The former. Since it hasn't been accepted yet, it may become the later (I guess I'll have to do it sooner or later).
-- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On 01/17/2011 11:42 AM, Alon Levy wrote:
dev: ccid-card-emulated, id "" dev-prop: backend = "nss-emulated" dev-prop: cert1 = <null> dev-prop: cert2 = <null> dev-prop: cert3 = <null> dev-prop: db = <null>
Hmm - in the hosts-certificates mode, it looks like I need to support an optional <database> sub-element to populate the ccid-card-emulated.db field when desired. Is that field a pathname, a generic arbitrary string, or something else altogether?
Pathname is the only thing I've tested, so let's limit it to this. It's NSS specific right now too - but as you see from the arguments it will probably be orthogonal, for instance under windows there might be a cryptoapi-emulated backend and db argument will be reused.
I think it is pretty easy to add one <smartcard mode=''> per new backend. The toughest part would be how to query whether a given backend is available in a given qemu binary, but if you can please make sure that 'qemu -device usb-ccid,?' lists all valid backends for a given qemu binary, then we're set (the first two backends of nss-emulated and certificates can be assumed if device usb-ccid exists in the first place). That is: <smartcard mode='hosts'> gives backend=nss-emulated <smartcard mode='hosts-certificates'> gives backend=certificates, cert1-3 are mandatory, and db is optional and a hypothetical <smartcard mode='cryptoapi-emulated'> (or some other appropriate name), along with any appropriate sub-elements, is added later if qemu ever adds a third valid backend=cryptoapi-emulated
Tell me if this needs to be changed - for instance I could just reuse the id as the bus name, so it will lose the ".0" suffix.
I think keeping the .0 suffix allows you the future ability to support multiple cards on a single bus.
The question was about changing my behavior, since right now I rely on the default qemu behavior of appending that ".0". So I understand you will supply that as a string bus=<id>.0, and since I actually ignore that but qemu computes the same bus id, it will just work.
Sounds reasonable - it should just work for the given setup of limiting qemu to at most one smartcard (easier to match your current implementation and expand later), without locking us into place once you ever do start supporting multiple smartcards. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On Mon, Jan 17, 2011 at 01:12:28PM -0700, Eric Blake wrote:
On 01/17/2011 11:42 AM, Alon Levy wrote:
dev: ccid-card-emulated, id "" dev-prop: backend = "nss-emulated" dev-prop: cert1 = <null> dev-prop: cert2 = <null> dev-prop: cert3 = <null> dev-prop: db = <null>
Hmm - in the hosts-certificates mode, it looks like I need to support an optional <database> sub-element to populate the ccid-card-emulated.db field when desired. Is that field a pathname, a generic arbitrary string, or something else altogether?
Pathname is the only thing I've tested, so let's limit it to this. It's NSS specific right now too - but as you see from the arguments it will probably be orthogonal, for instance under windows there might be a cryptoapi-emulated backend and db argument will be reused.
I think it is pretty easy to add one <smartcard mode=''> per new backend. The toughest part would be how to query whether a given backend is available in a given qemu binary, but if you can please make sure that 'qemu -device usb-ccid,?' lists all valid backends for a given
You meant ccid-card-emulated, right? usb-ccid doesn't have backends. Looking into existing devices, this doesn't look that difficult, It can be printed like so: ccid-card-emulated.backend=nss-emulated/certificates/anythingelse I'll just make sure the names I use don't contains any '/' chars :) Actually, supplying a list of possible backends is easy. Actually testing which are available at runtime - right now it should be identical, since if you can't find the NSS so qemu won't pass the loading stage. But that still means I will report nss-emulated even if there are no hardware devices. Is that acceptable?
qemu binary, then we're set (the first two backends of nss-emulated and certificates can be assumed if device usb-ccid exists in the first place). That is:
<smartcard mode='hosts'> gives backend=nss-emulated
<smartcard mode='hosts-certificates'> gives backend=certificates, cert1-3 are mandatory, and db is optional
and a hypothetical <smartcard mode='cryptoapi-emulated'> (or some other appropriate name), along with any appropriate sub-elements, is added later if qemu ever adds a third valid backend=cryptoapi-emulated
Tell me if this needs to be changed - for instance I could just reuse the id as the bus name, so it will lose the ".0" suffix.
I think keeping the .0 suffix allows you the future ability to support multiple cards on a single bus.
The question was about changing my behavior, since right now I rely on the default qemu behavior of appending that ".0". So I understand you will supply that as a string bus=<id>.0, and since I actually ignore that but qemu computes the same bus id, it will just work.
Sounds reasonable - it should just work for the given setup of limiting qemu to at most one smartcard (easier to match your current implementation and expand later), without locking us into place once you ever do start supporting multiple smartcards.
-- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On 01/18/2011 02:14 AM, Alon Levy wrote:
I think it is pretty easy to add one <smartcard mode=''> per new backend. The toughest part would be how to query whether a given backend is available in a given qemu binary, but if you can please make sure that 'qemu -device usb-ccid,?' lists all valid backends for a given
You meant ccid-card-emulated, right? usb-ccid doesn't have backends. Looking into existing devices, this doesn't look that difficult, It can be printed like so: ccid-card-emulated.backend=nss-emulated/certificates/anythingelse
Correct, and thanks for picking up my intended meaning.
I'll just make sure the names I use don't contains any '/' chars :)
Actually, supplying a list of possible backends is easy. Actually testing which are available at runtime - right now it should be identical, since if you can't find the NSS so qemu won't pass the loading stage. But that still means I will report nss-emulated even if there are no hardware devices. Is that acceptable?
Yes, always listing backend=nss-emulated when it is compiled in, even when nss will fail at runtime because there was no access to hardware, is an acceptable solution, so long as qemu dies with a reasonable error message in that case (there, we can chalk it up to a configuration issue, where the admin is attempting to use a qemu feature without supplying the correct environment, and since libvirt will relay the qemu error message on to the user, that should be sufficient to point the user to how to fix things). Back to the earlier question of which backend device qemu will access in the host, it sounds like you will need an optional <source path='/path/to/device'/> in the XML to let libvirt know which device needs additional permissions added to allow qemu (shared) access to that device. It may also make sense to have an optional backend parameter, as in: -device ccid-card-emulated,backend=nss-emulated,path=/path/to/device to make qemu force NSS to use the given device, rather than probing, for the case when SELinux will cause NSS probing to fail. It would be nice to document what device(s) are probed when the forced device path is not present, to explain the difference between: <smartcard mode='host'/> and <smartcard mode='host'> <source path='/path/to/device'/> </smartcard> -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On Mon, Jan 17, 2011 at 01:12:28PM -0700, Eric Blake wrote:
On 01/17/2011 11:42 AM, Alon Levy wrote:
dev: ccid-card-emulated, id "" dev-prop: backend = "nss-emulated" dev-prop: cert1 = <null> dev-prop: cert2 = <null> dev-prop: cert3 = <null> dev-prop: db = <null>
Hmm - in the hosts-certificates mode, it looks like I need to support an optional <database> sub-element to populate the ccid-card-emulated.db field when desired. Is that field a pathname, a generic arbitrary string, or something else altogether?
Pathname is the only thing I've tested, so let's limit it to this. It's NSS specific right now too - but as you see from the arguments it will probably be orthogonal, for instance under windows there might be a cryptoapi-emulated backend and db argument will be reused.
I think it is pretty easy to add one <smartcard mode=''> per new backend. The toughest part would be how to query whether a given backend is available in a given qemu binary, but if you can please make sure that 'qemu -device usb-ccid,?' lists all valid backends for a given
After looking a little more closely at the implementation (qdev-properties.c, qdev.h) I think the best way to do this is to add a type called PROP_TYPE_ENUM, and a callback for the printing - right now there is no callback, i.e. the part of the equals sign is a static string, not determined in runtime, and even at compile time to change it requires a new property. So I could: 1. add a static (compile time) determined list that would appear there, local change to ccid-card-emulated.c, so easy to see this accepted. 2. send patch adding PROP_TYPE_ENUM with associated callback, not a big patch, possibly accepted, with the addition that it allows a runtime list to be printed. It also means doing "-device <somedev>,?" would be running device specific callback (doesn't happen right now), I guess that's not a problem.
qemu binary, then we're set (the first two backends of nss-emulated and certificates can be assumed if device usb-ccid exists in the first place). That is:
<smartcard mode='hosts'> gives backend=nss-emulated
<smartcard mode='hosts-certificates'> gives backend=certificates, cert1-3 are mandatory, and db is optional
and a hypothetical <smartcard mode='cryptoapi-emulated'> (or some other appropriate name), along with any appropriate sub-elements, is added later if qemu ever adds a third valid backend=cryptoapi-emulated
Tell me if this needs to be changed - for instance I could just reuse the id as the bus name, so it will lose the ".0" suffix.
I think keeping the .0 suffix allows you the future ability to support multiple cards on a single bus.
The question was about changing my behavior, since right now I rely on the default qemu behavior of appending that ".0". So I understand you will supply that as a string bus=<id>.0, and since I actually ignore that but qemu computes the same bus id, it will just work.
Sounds reasonable - it should just work for the given setup of limiting qemu to at most one smartcard (easier to match your current implementation and expand later), without locking us into place once you ever do start supporting multiple smartcards.
-- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org
participants (3)
-
Alon Levy
-
Daniel P. Berrange
-
Eric Blake