conf: Parse/format XML input type 'evdev'

Signed-off-by: Kristina Hanicova <khanicov@redhat.com>
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
This commit is contained in:
Kristina Hanicova 2021-05-21 13:01:07 +02:00 committed by Michal Privoznik
parent 3bfbc3c0ef
commit cff0444e51
19 changed files with 182 additions and 23 deletions

View File

@ -5728,26 +5728,37 @@ to provide a graphics tablet for absolute cursor movement.
<input type='passthrough' bus='virtio'>
<source evdev='/dev/input/event1'/>
</input>
<input type='evdev'>
<source dev='/dev/input/event1234' grab='all' repeat='on'/>
</input>
</devices>
...
``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 ``<address>`` 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

View File

@ -5443,6 +5443,26 @@
</attribute>
</element>
</group>
<group>
<attribute name="type">
<value>evdev</value>
</attribute>
<element name="source">
<attribute name="dev">
<ref name="absFilePath"/>
</attribute>
<optional>
<attribute name="grab">
<value>all</value>
</attribute>
</optional>
<optional>
<attribute name="repeat">
<ref name="virOnOff"/>
</attribute>
</optional>
</element>
</group>
</choice>
<optional>
<attribute name="model">

View File

@ -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));

View File

@ -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, "<source evdev='%s'/>\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);

View File

@ -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);

View File

@ -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);

View File

@ -475,6 +475,8 @@ virDomainInputBusTypeToString;
virDomainInputDefFind;
virDomainInputDefFree;
virDomainInputDefGetPath;
virDomainInputSourceGrabTypeFromString;
virDomainInputSourceGrabTypeToString;
virDomainInputTypeToString;
virDomainIOMMUModelTypeFromString;
virDomainIOMMUModelTypeToString;

View File

@ -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);

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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,

View File

@ -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"),

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -0,0 +1,34 @@
<domain type='qemu'>
<name>QEMUGuest1</name>
<uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
<memory unit='KiB'>219100</memory>
<currentMemory unit='KiB'>219100</currentMemory>
<vcpu placement='static'>1</vcpu>
<os>
<type arch='i686' machine='pc'>hvm</type>
<boot dev='hd'/>
</os>
<cpu mode='custom' match='exact' check='none'>
<model fallback='forbid'>qemu64</model>
</cpu>
<clock offset='utc'/>
<on_poweroff>destroy</on_poweroff>
<on_reboot>restart</on_reboot>
<on_crash>destroy</on_crash>
<devices>
<emulator>/usr/bin/qemu-system-i386</emulator>
<controller type='pci' index='0' model='pci-root'/>
<controller type='usb' index='0' model='piix3-uhci'>
<address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/>
</controller>
<input type='evdev'>
<source dev='/dev/input/event1234' grab='all' repeat='on'/>
</input>
<input type='mouse' bus='ps2'/>
<input type='keyboard' bus='ps2'/>
<audio id='1' type='none'/>
<memballoon model='virtio'>
<address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
</memballoon>
</devices>
</domain>

View File

@ -0,0 +1 @@
../qemuxml2argvdata/input-linux.xml

View File

@ -1290,6 +1290,8 @@ mymain(void)
DO_TEST("virtio-input-passthrough",
QEMU_CAPS_VIRTIO_INPUT_HOST);
DO_TEST_CAPS_LATEST("input-linux");
DO_TEST("memorybacking-set", NONE);
DO_TEST("memorybacking-unset", NONE);