-
Notifications
You must be signed in to change notification settings - Fork 0
Device Access on Linux
By default the panel's hidraw node is root:root 0600, so nothing can open it
unprivileged. The fix is one udev rule — and one detail about that rule which is
extremely easy to get wrong.
# /etc/udev/rules.d/70-icue-nexus.rules
KERNEL=="hidraw*", ATTRS{idVendor}=="1b1c", ATTRS{idProduct}=="1b8e", TAG+="uaccess"
sudo udevadm control --reload-rules
sudo udevadm trigger --action=add --subsystem-match=hidrawThe packages install this for you and reload udev in their post-install step. If the panel was already plugged in when you installed, replug it or reboot so the ACL is actually applied.
This is the trap. A rule numbered 99- looks perfectly correct, applies the
tag without error, and grants nothing at all.
TAG+="uaccess" is not itself a permission. It is a marker consumed by
/usr/lib/udev/rules.d/73-seat-late.rules, which is what actually calls
RUN{builtin}+="uaccess" and writes the ACL. udev evaluates rule files in
lexical order, so a rule numbered above 73 applies its tag after the
consumer has already run. The tag is present; nobody ever looks at it again.
Number it 70.
The failure mode is nasty because every intermediate check passes:
udevadm info /dev/hidrawN | grep TAGS # shows uaccess — proves nothingThe only thing that settles it is the ACL itself:
getfacl /dev/hidrawN # look for user:<you>:rw-
ls -l /dev/hidrawN # crw-rw----+ — the + is the ACLIf there is no + and no user: entry, the rule ran and granted nothing.
uaccess grants an ACL to the active local seat's user only, and revokes it
when they log out. MODE="0666" grants the device to every process on the
machine, permanently. For a device that is a display on your keyboard the
difference is small in practice, but there is no reason to take the wider option.
Two hidraw nodes appear for this device and only one is real
(see Reverse Engineering the NEXUS).
To identify them:
for h in /sys/class/hidraw/hidraw*; do
echo "$(basename "$h") $(grep HID_ID "$h/device/uevent")"
done | grep -i 1B8EInterface 0 is the one with the vendor usage page. Opening interface 1 succeeds and then does nothing, which is a confusing way to spend an afternoon.
Two processes writing the same hidraw node do not get an error. HID output
reports from two handles simply interleave, and the panel flickers between two
different pictures. Nothing fails, nothing is logged.
Anything that owns this device needs a single-instance guard, decided before
the device is opened. This project arbitrates with an exclusive FileShare.None
lock file — flock() under the hood — because that is atomic against two
simultaneous launches in a way that checking for a pid file or a socket is not,
and because the kernel releases it however the process dies, so a crash cannot
leave the application permanently unstartable.
A second launch then talks to the first over a Unix socket and asks it to raise
its window, which is what the user wanted by launching again in the first place.
One caveat learned the hard way: a bound socket whose owner has stopped
accepting still lets connect and send succeed, so "sent" is not evidence of
anything. Wait for the holder to acknowledge.