From cff0444e51b4523c6219ac31e80cb9846ce708da Mon Sep 17 00:00:00 2001 From: Kristina Hanicova Date: Fri, 21 May 2021 13:01:07 +0200 Subject: [PATCH] conf: Parse/format XML input type 'evdev' Signed-off-by: Kristina Hanicova Signed-off-by: Michal Privoznik Reviewed-by: Michal Privoznik --- docs/formatdomain.rst | 37 ++++++---- docs/schemas/domaincommon.rng | 20 ++++++ src/conf/domain_audit.c | 1 + src/conf/domain_conf.c | 69 ++++++++++++++++--- src/conf/domain_conf.h | 12 ++++ src/conf/domain_validate.c | 8 +++ src/libvirt_private.syms | 2 + src/qemu/qemu_cgroup.c | 2 + src/qemu/qemu_command.c | 1 + src/qemu/qemu_domain_address.c | 1 + src/qemu/qemu_hotplug.c | 1 + src/qemu/qemu_validate.c | 6 ++ src/security/security_apparmor.c | 1 + src/security/security_dac.c | 2 + src/security/security_selinux.c | 2 + src/security/virt-aa-helper.c | 3 +- tests/qemuxml2argvdata/input-linux.xml | 34 +++++++++ .../input-linux.x86_64-latest.xml | 1 + tests/qemuxml2xmltest.c | 2 + 19 files changed, 182 insertions(+), 23 deletions(-) create mode 100644 tests/qemuxml2argvdata/input-linux.xml create mode 120000 tests/qemuxml2xmloutdata/input-linux.x86_64-latest.xml diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index fa5c14febc..e115165ad2 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -5728,26 +5728,37 @@ to provide a graphics tablet for absolute cursor movement. + + + ... ``input`` The ``input`` element has one mandatory attribute, the ``type`` whose value - can be 'mouse', 'tablet', ( :since:`since 1.2.2` ) 'keyboard' or ( - :since:`since 1.3.0` ) 'passthrough'. The tablet provides absolute cursor - movement, while the mouse uses relative movement. The optional ``bus`` - attribute can be used to refine the exact device type. It takes values "xen" - (paravirtualized), "ps2" and "usb" or ( :since:`since 1.3.0` ) "virtio". + can be 'mouse', 'tablet', ( :since:`since 1.2.2` ) 'keyboard', ( + :since:`since 1.3.0` ) 'passthrough' or ( :since:`since 7.4.0` ) 'evdev'. + The tablet provides absolute cursor movement, while the mouse uses relative + movement. The optional ``bus`` attribute can be used to refine the exact + device type. It takes values "xen" (paravirtualized), "ps2" and "usb" or ( + :since:`since 1.3.0` ) "virtio". The ``input`` element has an optional sub-element ``
`` which can tie -the device to a particular PCI slot, `documented above <#elementsAddress>`__. On -S390, ``address`` can be used to provide a CCW address for an input device ( -:since:`since 4.2.0` ). For type ``passthrough``, the mandatory sub-element -``source`` must have an ``evdev`` attribute containing the absolute path to the -event device passed through to guests. (KVM only) :since:`Since 5.2.0` , the -``input`` element accepts a ``model`` attribute which has the values 'virtio', -'virtio-transitional' and 'virtio-non-transitional'. See `Virtio transitional -devices <#elementsVirtioTransitional>`__ for more details. +the device to a particular PCI slot, `documented above <#elementsAddress>`__. +On S390, ``address`` can be used to provide a CCW address for an input device ( +:since:`since 4.2.0` ). For types ``passthrough`` and ``evdev``, the mandatory +sub-element ``source`` must have an ``evdev`` (for ``passthrough``) or ``dev`` +(for ``evdev``) attribute containing the absolute path to the event device +passed through to guests. +For type ``evdev``, ``source`` can have two optional attributes ``grab`` with +value 'all' which when enabled grabs all input devices instead of just one and +``repeat`` with value 'on'/'off' to enable/disable auto-repeat events ( +:since:`Since 7.4.0`). +``input`` type ``evdev`` is currently supported only on linux devices. +(KVM only) :since:`Since 5.2.0` , the ``input`` element accepts a +``model`` attribute which has the values 'virtio', 'virtio-transitional' and +'virtio-non-transitional'. See `Virtio transitional devices +<#elementsVirtioTransitional>`__ for more details. The subelement ``driver`` can be used to tune the virtio options of the device: `Virtio-specific options <#elementsVirtio>`__ can also be set. ( :since:`Since diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index a2e5c50c1d..16e1c549c2 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -5443,6 +5443,26 @@ + + + evdev + + + + + + + + all + + + + + + + + + diff --git a/src/conf/domain_audit.c b/src/conf/domain_audit.c index b1226fd5dc..b08233fa49 100644 --- a/src/conf/domain_audit.c +++ b/src/conf/domain_audit.c @@ -956,6 +956,7 @@ virDomainAuditInput(virDomainObj *vm, break; case VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH: + case VIR_DOMAIN_INPUT_TYPE_EVDEV: VIR_AUDIT(VIR_AUDIT_RECORD_RESOURCE, success, "virt=%s resrc=evdev reason=%s %s uuid=%s path=%s", virt, reason, vmname, uuidstr, VIR_AUDIT_STR(input->source.evdev)); diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index e35c38caa3..bb21c9ca50 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -864,6 +864,7 @@ VIR_ENUM_IMPL(virDomainInput, "tablet", "keyboard", "passthrough", + "evdev", ); VIR_ENUM_IMPL(virDomainInputBus, @@ -873,6 +874,7 @@ VIR_ENUM_IMPL(virDomainInputBus, "xen", "parallels", "virtio", + "none", ); VIR_ENUM_IMPL(virDomainInputModel, @@ -883,6 +885,12 @@ VIR_ENUM_IMPL(virDomainInputModel, "virtio-non-transitional", ); +VIR_ENUM_IMPL(virDomainInputSourceGrab, + VIR_DOMAIN_INPUT_SOURCE_GRAB_LAST, + "default", + "all", +); + VIR_ENUM_IMPL(virDomainGraphics, VIR_DOMAIN_GRAPHICS_TYPE_LAST, "sdl", @@ -1879,6 +1887,7 @@ const char *virDomainInputDefGetPath(virDomainInputDef *input) return NULL; case VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH: + case VIR_DOMAIN_INPUT_TYPE_EVDEV: return input->source.evdev; } return NULL; @@ -11819,10 +11828,10 @@ virDomainInputDefParseXML(virDomainXMLOption *xmlopt, { VIR_XPATH_NODE_AUTORESTORE(ctxt) virDomainInputDef *def; - g_autofree char *evdev = NULL; g_autofree char *type = NULL; g_autofree char *bus = NULL; g_autofree char *model = NULL; + xmlNodePtr source = NULL; def = g_new0(virDomainInputDef, 1); @@ -11924,6 +11933,8 @@ virDomainInputDefParseXML(virDomainXMLOption *xmlopt, } else if (ARCH_IS_S390(dom->os.arch) || def->type == VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH) { def->bus = VIR_DOMAIN_INPUT_BUS_VIRTIO; + } else if (def->type == VIR_DOMAIN_INPUT_TYPE_EVDEV) { + def->bus = VIR_DOMAIN_INPUT_BUS_NONE; } else { def->bus = VIR_DOMAIN_INPUT_BUS_USB; } @@ -11948,12 +11959,36 @@ virDomainInputDefParseXML(virDomainXMLOption *xmlopt, goto error; } - if ((evdev = virXPathString("string(./source/@evdev)", ctxt))) - def->source.evdev = virFileSanitizePath(evdev); - if (def->type == VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH && !def->source.evdev) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("Missing evdev path for input device passthrough")); - goto error; + if ((source = virXPathNode("./source", ctxt))) { + g_autofree char *evdev = NULL; + + if (def->type == VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH) + evdev = virXMLPropString(source, "evdev"); + else if (def->type == VIR_DOMAIN_INPUT_TYPE_EVDEV) + evdev = virXMLPropString(source, "dev"); + + if (evdev) + def->source.evdev = virFileSanitizePath(evdev); + + if (def->type == VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH || + def->type == VIR_DOMAIN_INPUT_TYPE_EVDEV) { + if (!def->source.evdev) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("Missing evdev path for input device")); + goto error; + } + } + + if (def->type == VIR_DOMAIN_INPUT_TYPE_EVDEV) { + if (virXMLPropEnum(source, "grab", + virDomainInputSourceGrabTypeFromString, + VIR_XML_PROP_NONZERO, &def->source.grab) < 0) + goto error; + + if (virXMLPropTristateSwitch(source, "repeat", + VIR_XML_PROP_NONE, &def->source.repeat) < 0) + goto error; + } } if (virDomainVirtioOptionsParseXML(virXPathNode("./driver", ctxt), @@ -25912,9 +25947,12 @@ virDomainInputDefFormat(virBuffer *buf, { const char *type = virDomainInputTypeToString(def->type); const char *bus = virDomainInputBusTypeToString(def->bus); + const char *grab = virDomainInputSourceGrabTypeToString(def->source.grab); + const char *repeat = virTristateSwitchTypeToString(def->source.repeat); g_auto(virBuffer) attrBuf = VIR_BUFFER_INITIALIZER; g_auto(virBuffer) childBuf = VIR_BUFFER_INIT_CHILD(buf); g_auto(virBuffer) driverAttrBuf = VIR_BUFFER_INITIALIZER; + g_auto(virBuffer) sourceAttrBuf = VIR_BUFFER_INITIALIZER; /* don't format keyboard into migratable XML for backward compatibility */ if (flags & VIR_DOMAIN_DEF_FORMAT_MIGRATABLE && @@ -25934,7 +25972,9 @@ virDomainInputDefFormat(virBuffer *buf, return -1; } - virBufferAsprintf(&attrBuf, " type='%s' bus='%s'", type, bus); + virBufferAsprintf(&attrBuf, " type='%s'", type); + if (def->bus != VIR_DOMAIN_INPUT_BUS_NONE) + virBufferAsprintf(&attrBuf, " bus='%s'", bus); if (def->model) { const char *model = virDomainInputModelTypeToString(def->model); @@ -25952,7 +25992,18 @@ virDomainInputDefFormat(virBuffer *buf, virXMLFormatElement(&childBuf, "driver", &driverAttrBuf, NULL); - virBufferEscapeString(&childBuf, "\n", def->source.evdev); + if (def->type == VIR_DOMAIN_INPUT_TYPE_EVDEV) + virBufferEscapeString(&sourceAttrBuf, " dev='%s'", def->source.evdev); + else + virBufferEscapeString(&sourceAttrBuf, " evdev='%s'", def->source.evdev); + + if (def->source.grab) + virBufferAsprintf(&sourceAttrBuf, " grab='%s'", grab); + if (def->source.repeat) + virBufferAsprintf(&sourceAttrBuf, " repeat='%s'", repeat); + + virXMLFormatElement(&childBuf, "source", &sourceAttrBuf, NULL); + virDomainDeviceInfoFormat(&childBuf, &def->info, flags); virXMLFormatElement(buf, "input", &attrBuf, &childBuf); diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index fab856a5c7..8d64d4a1e2 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1387,6 +1387,7 @@ typedef enum { VIR_DOMAIN_INPUT_TYPE_TABLET, VIR_DOMAIN_INPUT_TYPE_KBD, VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH, + VIR_DOMAIN_INPUT_TYPE_EVDEV, VIR_DOMAIN_INPUT_TYPE_LAST } virDomainInputType; @@ -1397,6 +1398,7 @@ typedef enum { VIR_DOMAIN_INPUT_BUS_XEN, VIR_DOMAIN_INPUT_BUS_PARALLELS, /* pseudo device for VNC in containers */ VIR_DOMAIN_INPUT_BUS_VIRTIO, + VIR_DOMAIN_INPUT_BUS_NONE, VIR_DOMAIN_INPUT_BUS_LAST } virDomainInputBus; @@ -1410,12 +1412,21 @@ typedef enum { VIR_DOMAIN_INPUT_MODEL_LAST } virDomainInputModel; +typedef enum { + VIR_DOMAIN_INPUT_SOURCE_GRAB_DEFAULT, + VIR_DOMAIN_INPUT_SOURCE_GRAB_ALL, + + VIR_DOMAIN_INPUT_SOURCE_GRAB_LAST +} virDomainInputSourceGrab; + struct _virDomainInputDef { int type; int bus; int model; /* virDomainInputModel */ struct { char *evdev; + virDomainInputSourceGrab grab; + virTristateSwitch repeat; } source; virDomainDeviceInfo info; virDomainVirtioOptions *virtio; @@ -3844,6 +3855,7 @@ VIR_ENUM_DECL(virDomainRedirdevBus); VIR_ENUM_DECL(virDomainInput); VIR_ENUM_DECL(virDomainInputBus); VIR_ENUM_DECL(virDomainInputModel); +VIR_ENUM_DECL(virDomainInputSourceGrab); VIR_ENUM_DECL(virDomainGraphics); VIR_ENUM_DECL(virDomainGraphicsListen); VIR_ENUM_DECL(virDomainGraphicsAuthConnected); diff --git a/src/conf/domain_validate.c b/src/conf/domain_validate.c index 686b9e8d16..98202a3adc 100644 --- a/src/conf/domain_validate.c +++ b/src/conf/domain_validate.c @@ -1961,6 +1961,14 @@ virDomainInputDefValidate(const virDomainInputDef *input) } break; + case VIR_DOMAIN_INPUT_TYPE_EVDEV: + if (input->bus != VIR_DOMAIN_INPUT_BUS_NONE) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("input evdev doesn't support bus element")); + return -1; + } + break; + case VIR_DOMAIN_INPUT_TYPE_LAST: default: virReportEnumRangeError(virDomainInputType, input->type); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 6fbdee4124..7c7627052b 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -475,6 +475,8 @@ virDomainInputBusTypeToString; virDomainInputDefFind; virDomainInputDefFree; virDomainInputDefGetPath; +virDomainInputSourceGrabTypeFromString; +virDomainInputSourceGrabTypeToString; virDomainInputTypeToString; virDomainIOMMUModelTypeFromString; virDomainIOMMUModelTypeToString; diff --git a/src/qemu/qemu_cgroup.c b/src/qemu/qemu_cgroup.c index 0e8835fb86..038d6478b2 100644 --- a/src/qemu/qemu_cgroup.c +++ b/src/qemu/qemu_cgroup.c @@ -362,6 +362,7 @@ qemuSetupInputCgroup(virDomainObj *vm, switch (dev->type) { case VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH: + case VIR_DOMAIN_INPUT_TYPE_EVDEV: VIR_DEBUG("Process path '%s' for input device", dev->source.evdev); ret = virCgroupAllowDevicePath(priv->cgroup, dev->source.evdev, VIR_CGROUP_DEVICE_RW, false); @@ -385,6 +386,7 @@ qemuTeardownInputCgroup(virDomainObj *vm, switch (dev->type) { case VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH: + case VIR_DOMAIN_INPUT_TYPE_EVDEV: VIR_DEBUG("Process path '%s' for input device", dev->source.evdev); ret = virCgroupDenyDevicePath(priv->cgroup, dev->source.evdev, VIR_CGROUP_DEVICE_RWM, false); diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index dcc060bde9..9954bb0678 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -4017,6 +4017,7 @@ qemuBuildVirtioInputDevStr(const virDomainDef *def, return NULL; } break; + case VIR_DOMAIN_INPUT_TYPE_EVDEV: case VIR_DOMAIN_INPUT_TYPE_LAST: default: virReportEnumRangeError(virDomainInputType, dev->type); diff --git a/src/qemu/qemu_domain_address.c b/src/qemu/qemu_domain_address.c index a11e40d9b2..10cf5eb6f4 100644 --- a/src/qemu/qemu_domain_address.c +++ b/src/qemu/qemu_domain_address.c @@ -973,6 +973,7 @@ qemuDomainDeviceCalculatePCIConnectFlags(virDomainDeviceDef *dev, case VIR_DOMAIN_INPUT_BUS_USB: case VIR_DOMAIN_INPUT_BUS_XEN: case VIR_DOMAIN_INPUT_BUS_PARALLELS: + case VIR_DOMAIN_INPUT_BUS_NONE: case VIR_DOMAIN_INPUT_BUS_LAST: return 0; } diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index a64cddb9e7..c6f275e11d 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -5805,6 +5805,7 @@ qemuDomainDetachPrepInput(virDomainObj *vm, case VIR_DOMAIN_INPUT_BUS_LAST: case VIR_DOMAIN_INPUT_BUS_USB: case VIR_DOMAIN_INPUT_BUS_VIRTIO: + case VIR_DOMAIN_INPUT_BUS_NONE: break; } diff --git a/src/qemu/qemu_validate.c b/src/qemu/qemu_validate.c index e6ddb43113..168b302904 100644 --- a/src/qemu/qemu_validate.c +++ b/src/qemu/qemu_validate.c @@ -4563,6 +4563,7 @@ qemuValidateDomainDeviceDefInput(const virDomainInputDef *input, case VIR_DOMAIN_INPUT_TYPE_MOUSE: case VIR_DOMAIN_INPUT_TYPE_TABLET: case VIR_DOMAIN_INPUT_TYPE_KBD: + case VIR_DOMAIN_INPUT_TYPE_EVDEV: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("virtio (non-)transitional models are not " "supported for input type=%s"), @@ -4608,6 +4609,11 @@ qemuValidateDomainDeviceDefInput(const virDomainInputDef *input, cap = QEMU_CAPS_VIRTIO_INPUT_HOST; ccwCap = QEMU_CAPS_LAST; break; + case VIR_DOMAIN_INPUT_TYPE_EVDEV: + baseName = "input-linux"; + cap = QEMU_CAPS_INPUT_LINUX; + ccwCap = QEMU_CAPS_LAST; + break; case VIR_DOMAIN_INPUT_TYPE_LAST: default: virReportEnumRangeError(virDomainInputType, diff --git a/src/security/security_apparmor.c b/src/security/security_apparmor.c index a2a8435fe4..84363015dc 100644 --- a/src/security/security_apparmor.c +++ b/src/security/security_apparmor.c @@ -720,6 +720,7 @@ AppArmorSetInputLabel(virSecurityManager *mgr, switch ((virDomainInputType)input->type) { case VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH: + case VIR_DOMAIN_INPUT_TYPE_EVDEV: if (input->source.evdev == NULL) { virReportError(VIR_ERR_INTERNAL_ERROR, _("%s: passthrough input device has no source"), diff --git a/src/security/security_dac.c b/src/security/security_dac.c index e973964735..76bfce7762 100644 --- a/src/security/security_dac.c +++ b/src/security/security_dac.c @@ -1815,6 +1815,7 @@ virSecurityDACSetInputLabel(virSecurityManager *mgr, switch ((virDomainInputType)input->type) { case VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH: + case VIR_DOMAIN_INPUT_TYPE_EVDEV: if (virSecurityDACGetIds(seclabel, priv, &user, &group, NULL, NULL) < 0) return -1; @@ -1843,6 +1844,7 @@ virSecurityDACRestoreInputLabel(virSecurityManager *mgr, switch ((virDomainInputType)input->type) { case VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH: + case VIR_DOMAIN_INPUT_TYPE_EVDEV: ret = virSecurityDACRestoreFileLabel(mgr, input->source.evdev); break; diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c index 29628d8953..b50f4463cc 100644 --- a/src/security/security_selinux.c +++ b/src/security/security_selinux.c @@ -1513,6 +1513,7 @@ virSecuritySELinuxSetInputLabel(virSecurityManager *mgr, switch ((virDomainInputType)input->type) { case VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH: + case VIR_DOMAIN_INPUT_TYPE_EVDEV: if (virSecuritySELinuxSetFilecon(mgr, input->source.evdev, seclabel->imagelabel, true) < 0) return -1; @@ -1543,6 +1544,7 @@ virSecuritySELinuxRestoreInputLabel(virSecurityManager *mgr, switch ((virDomainInputType)input->type) { case VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH: + case VIR_DOMAIN_INPUT_TYPE_EVDEV: rc = virSecuritySELinuxRestoreFileLabel(mgr, input->source.evdev, true); break; diff --git a/src/security/virt-aa-helper.c b/src/security/virt-aa-helper.c index e0d78ae309..52cfebf6e0 100644 --- a/src/security/virt-aa-helper.c +++ b/src/security/virt-aa-helper.c @@ -1149,7 +1149,8 @@ get_files(vahControl * ctl) for (i = 0; i < ctl->def->ninputs; i++) { if (ctl->def->inputs[i] && - ctl->def->inputs[i]->type == VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH) { + (ctl->def->inputs[i]->type == VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH || + ctl->def->inputs[i]->type == VIR_DOMAIN_INPUT_TYPE_EVDEV)) { if (vah_add_file(&buf, ctl->def->inputs[i]->source.evdev, "rw") != 0) goto cleanup; } diff --git a/tests/qemuxml2argvdata/input-linux.xml b/tests/qemuxml2argvdata/input-linux.xml new file mode 100644 index 0000000000..c0702676c1 --- /dev/null +++ b/tests/qemuxml2argvdata/input-linux.xml @@ -0,0 +1,34 @@ + + QEMUGuest1 + c7a5fdbd-edaf-9455-926a-d65c16db1809 + 219100 + 219100 + 1 + + hvm + + + + qemu64 + + + destroy + restart + destroy + + /usr/bin/qemu-system-i386 + + +
+ + + + + + +