udev 规则中的无条件 GOTO(和 Medion RC-0617)

Conditionless GOTO in udev rules (and Medion RC-0617)

我一直在某些 Debian 8.8 衍生版本 (antix16) 下对 Medion RC-0617 遥控器(带有 USB 加密狗)进行逆向工程。

它注册了 3 个不同的 HID 设备 (/dev/hidraw*),我希望将它们符号链接到 /dev/mdremote0、1 和 2,而与关联的 hidraw 设备的数量无关(它们是hidraw1、2 和 3 大多数时间,但这取决于插入的输入设备)以便使用脚本查询它们以执行遥控器按钮的自定义操作。 (同时将他们的文件模式设置为 666 以便作为普通用户访问它们)

udevadm info -a /dev/hidraw1 的输出如下所示(缩写):

// The actual hidraw device (top level)
  looking at device '/devices/pci0000:00/0000:00:1c.4/0000:05:00.0/usb3/3-1/3-1:1.0/0003:04F2:0618.002B/hidraw/hidraw1':
    KERNEL=="hidraw1"
    SUBSYSTEM=="hidraw"
    DRIVER==""
//...

// The last point at which the 3 individual hidraw devices differed from each other
  looking at parent device '/devices/pci0000:00/0000:00:1c.4/0000:05:00.0/usb3/3-1/3-1:1.0':
    KERNELS=="3-1:1.0"
    SUBSYSTEMS=="usb"
    DRIVERS=="usbhid"
    //...
    ATTRS{bInterfaceProtocol}=="01"
    // This entry distinguished the 3 individual hidraw devices from each other
    //...

// The dongle itself, parent device of all 3 hidraw devices
  looking at parent device '/devices/pci0000:00/0000:00:1c.4/0000:05:00.0/usb3/3-1':
    KERNELS=="3-1"
    SUBSYSTEMS=="usb"
    DRIVERS=="usb"
    ATTRS{idVendor}=="04f2"
    ATTRS{idProduct}=="0618"
    ATTRS{product}=="USB Wireless HID Receiver"
    // The dongle can be uniquely identified by idVendor and idProduct
    //...
//...

因此,我需要编写的 udev 规则需要:

我的第一次尝试是这样的:

SUBSYSTEM=="hidraw", SUBSYSTEMS=="usb", ATTRS{idVendor}=="04f2", ATTRS{idProduct}=="0618", ATTRS{bInterfaceProtocol}=="01", SYMLINK="mdremote0", MODE="0666"
SUBSYSTEM=="hidraw", SUBSYSTEMS=="usb", ATTRS{idVendor}=="04f2", ATTRS{idProduct}=="0618", ATTRS{bInterfaceProtocol}=="00", SYMLINK="mdremote1", MODE="0666"
SUBSYSTEM=="hidraw", SUBSYSTEMS=="usb", ATTRS{idVendor}=="04f2", ATTRS{idProduct}=="0618", ATTRS{bInterfaceProtocol}=="02", SYMLINK="mdremote2", MODE="0666"

(bInterfaceProtocol 属性的顺序是有意的)

简而言之,这不起作用,udev 联机帮助页说:

Some of the keys also match against properties of the parent devices in sysfs, not only the device that has generated the event. If multiple keys that match a parent device are specified in a single rule, all these keys must match at one and the same parent device.

所以,我开始了另一种方法:首先与加密狗进行匹配,然后如果此检查不匹配则跳过个别规则,如下所示:

SUBSYSTEM=="hidraw", SUBSYSTEMS=="usb", ATTRS{idVendor}=="04f2", ATTRS{idProduct}=="0618", GOTO="match"
GOTO="end"
LABEL="match"
SUBSYSTEM=="hidraw", SUBSYSTEMS=="usb", ATTRS{bInterfaceProtocol}=="01", SYMLINK="mdremote0", MODE="0666"
SUBSYSTEM=="hidraw", SUBSYSTEMS=="usb", ATTRS{bInterfaceProtocol}=="00", SYMLINK="mdremote1", MODE="0666"
SUBSYSTEM=="hidraw", SUBSYSTEMS=="usb", ATTRS{bInterfaceProtocol}=="02", SYMLINK="mdremote2", MODE="0666"
LABEL="end"

这在第一学期看起来是正确的。但是,你猜怎么着,它也不起作用,mdremote* 符号链接只是指向任何设置了 bInterfaceProtocol 密钥的设备。

的确,第二行 (GOTO="end") 被忽略了。我花了一些时间才弄清楚,但解决方案实际上非常简单:

如果没有匹配条件,udev 将规则视为 "always not matching",因此根本不执行 GOTO。

我的工作 udev 规则文件如下所示:

# Test if the wanted dongle is a parent device
SUBSYSTEM=="hidraw", SUBSYSTEMS=="usb", ATTRS{idVendor}=="04f2", ATTRS{idProduct}=="0618", GOTO="match"
# If not, skip the next 3 rules. The test against SUBSYSTEM=="hidraw" is there to produce a rule match
SUBSYSTEM=="hidraw", GOTO="end"
LABEL="match"
# Those 3 rules actually assign the right symlink depending on the bInterfaceProtocol property.
# Note that ALL of those rules contain the SUBSYSTEM=="hidraw" check, because the GOTO in the second line
# does not get executed for non-hidraw devices and the rules get evaluated for any non-hidraw device.
SUBSYSTEM=="hidraw", SUBSYSTEMS=="usb", ATTRS{bInterfaceProtocol}=="01", SYMLINK="mdremote0", MODE="0666"
SUBSYSTEM=="hidraw", SUBSYSTEMS=="usb", ATTRS{bInterfaceProtocol}=="00", SYMLINK="mdremote1", MODE="0666"
SUBSYSTEM=="hidraw", SUBSYSTEMS=="usb", ATTRS{bInterfaceProtocol}=="02", SYMLINK="mdremote2", MODE="0666"
LABEL="end"

结果证明效果很好。它仍然可以通过为 GOTO="end" 语句提供更好的匹配规则来改进,但我就那样了。

@orwell 我偶然发现了这个并且有点困惑为什么这对你不起作用。我曾尝试做一些类似的事情,并且在无条件 GOTO 和 debian stretch 以及 Arch Linux 上没有遇到任何问题。检查这个:

[c0d3@z3r0 tmp]$ cat /etc/udev/rules.d/45-test.rules
GOTO="end"
IMPORT{program}=="/bin/touch /tmp/noend"
LABEL="end"
IMPORT{program}=="/bin/touch /tmp/end"
[c0d3@z3r0 tmp]$ sudo udevadm control --reload 
[c0d3@z3r0 tmp]$ sudo udevadm trigger
[c0d3@z3r0 tmp]$ ls /tmp/*end
/tmp/end
[c0d3@z3r0 tmp]$