udev add/remove events (controller hotplug) not reliable inside container
User story
Users want hotplugging gamepads, joysticks etc. to work in all games.
Description
libudev in the container is able to enumerate input devices (list what we already have), but does not get notified when devices are added, removed or changed. This is for a couple of reasons:
- With an unprivileged (non-setuid) bwrap, we are not allowed to map uid 0 into the container, which means libudev cannot know that it can trust the netlink messages from udevd
- With any container and host that differ in OS or age, we cannot guarantee that the "wire protocol" between the host's udevd and the container's libudev is compatible
Those who have access to the old git repo can consult steam/pressure-vessel#5 for more information.
We tried to address this in SDL2 (only, not Wine) by disabling libudev integration, which made it fall back on polling (ugh). However, this caused too many regressions and had to be reverted: some devices are misdetected by SDL's heuristic, and it can go badly wrong when we disconnect a device and plug in a new device between poll intervals, if the new device happens to get the same device node name (SDL will think it's still the old device).
A possible long-term solution (tracked internally as T23048) would be to have something that runs outside the container (a portal in Flatpak terminology) that receives events and passes them into the container, translating them if necessary, and then modify libraries inside the container so that they detect that they are running in the container and automatically subscribe to events from the portal instead of subscribing to events directly from udevd. https://github.com/flatpak/xdg-desktop-portal/issues/227 and https://github.com/flatpak/flatpak/issues/3725 track the need for this to happen for Flatpak apps. However, those are somewhat stalled upstream: the Flatpak developers have chosen to rely on a Wayland-specific interface that doesn't exist yet. It's also quite a lot of container-specific code, both in the container infrastructure (!159) and in SDL/Wine (which their respective upstreams might not be very happy about merging).
Another possible solution (tracked internally as T24378) is to patch SDL, Wine, and everything else that monitors input devices so that instead of using a udev_monitor
, it can use inotify on /dev
and/or /dev/input
, then apply a heuristic to decide which of those devices are of interest (joysticks) and which are not. This can be done with surprisingly little code. In Wine, the hardest part is fitting it into the existing code paths; in SDL, the hardest part is making the heuristic better. This is the short-term solution that @smcv is currently pursuing.
Either of these could be done at a high level (in SDL2 and other libudev users), or at a low level (in a modified libudev, or replacing libudev with a mock version that has this behaviour), or both. I think the low-level approach of modifying or replacing libudev is a bad idea, because its scope is extremely broad - not just input devices - and to be a real replacement, we would have to make it work for all device types, even those that we know we don't actually care about.
For pressure-vessel, a complicating factor is that the current implementation can only run on the host and put games in its own containers, but we want future versions to be also able to run in a Flatpak container and put games in Flatpak "sub-sandboxes". When we do that, we will be restricted by what Flatpak supports and what it allows us to do.