Skip to content

Commit

Permalink
Fix PermissionError when reconnecting mtdev input devices (kivy#7587)
Browse files Browse the repository at this point in the history
The Linux kernel creates input devices then hands permission management off
to udev. This results in a period of time when the device exists, but is
readable only by root. The MTDMotionEventProvider handles reconnects by
spinning in a loop waiting for the device to reappear. As soon as the
device reappears, the provider immediately attempts to create a new mtdev
Device. This happens faster than udev can update the file permissions so
the Device construction fails raising an error causing no further
reconnects to be attempted.

This patch adds some spinning code to the Device object to re-try opening
the device file for a period of time if a PermissionError was received. The
retries will be applied on initial connection to the MT device (in case the
device happened to be newly attached just as the application was starting)
as well as reconnection attempts. Attempts are time-limited so that real
system configuration errors will still be detected, but long enough of a
duration that any reasonable system should have enough time for udev to fix
the permissions. For reference, a Raspberry Pi 4 (a small single-board
computer) takes about 0.6s between creating the file and fixing the
permissions.
  • Loading branch information
duelafn authored Jul 29, 2021
1 parent 57775fe commit a55ad40
Showing 1 changed file with 17 additions and 1 deletion.
18 changes: 17 additions & 1 deletion kivy/lib/mtdev.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
# flake8: noqa

import os
import time
from ctypes import cdll, Structure, c_ulong, c_int, c_ushort, \
c_void_p, pointer, POINTER, byref

Expand Down Expand Up @@ -141,7 +142,22 @@ def __init__(self, filename):
self._fd = -1
self._device = mtdev()

self._fd = os.open(filename, os.O_NONBLOCK | os.O_RDONLY)
# Linux kernel creates input devices then hands permission changes
# off to udev. This results in a period of time when the device is
# readable only by root. Device reconnects can be processed by
# MTDMotionEventProvider faster than udev can get a chance to run,
# so we spin for a period of time to allow udev to fix permissions.
# We limit the loop time in case the system is misconfigured and
# the user really does not (and will not) have permission to access
# the device.
# Note: udev takes about 0.6 s on a Raspberry Pi 4
permission_wait_until = time.time() + 3.0
while self._fd == -1:
try:
self._fd = os.open(filename, os.O_NONBLOCK | os.O_RDONLY)
except PermissionError:
if time.time() > permission_wait_until:
raise
ret = mtdev_open(pointer(self._device), self._fd)
if ret != 0:
os.close(self._fd)
Expand Down

0 comments on commit a55ad40

Please sign in to comment.