在 Linux 中如何编写基本的 udev 规则
读者对象
理解 udev 背后的基本概念,学习如何写简单的规则。
要求
- root 权限
难度
中等
约定
-
#
- 要求给定的命令使用 root 权限或者直接以一个 root 用户或者使用sudo
命令去运行。 -
$
- 要求给定的命令以一个普通的非特权用户运行。
介绍
在 GNU/Linux 系统中,虽然设备的底层支持是在内核层面处理的,但是,它们相关的事件管理是在用户空间中通过
udev
来管理的。确切地说是由
udevd
守护进程来完成的。学习如何去写规则,并应用到发生的这些事件上,将有助于我们修改系统的行为并使它适合我们的需要。
规则如何组织
udev 规则是定义在一个以
.rules
为扩展名的文件中。那些文件主要放在两个位置:
/usr/lib/udev/rules.d
,这个目录用于存放系统安装的规则;
/etc/udev/rules.d/
这个目录是保留给自定义规则的。
定义那些规则的文件的命名惯例是使用一个数字作为前缀(比如,
50-udev-default.rules
),并且以它们在目录中的词汇顺序进行处理的。在
/etc/udev/rules.d
中安装的文件,会覆盖安装在系统默认路径中的同名文件。
规则语法
如果你理解了 udev 规则的行为逻辑,它的语法并不复杂。一个规则由两个主要的节构成:
match
部分,它使用一系列用逗号分隔的键定义了规则应用的条件,而
action
部分,是当条件满足时,我们执行一些动作。
测试案例
讲解可能的选项的最好方法莫过于配置一个真实的案例,因此,我们去定义一个规则作为演示,当鼠标被连接时禁用触摸板。显然,在该规则定义中提供的属性将反映我的硬件。
我们将在
/etc/udev/rules.d/99-togglemouse.rules
文件中用我们喜欢的文本编辑器来写我们的规则。一条规则定义允许跨多个行,但是,如果是这种情况,必须在一个换行字符之前使用一个反斜线(
\
)表示行的延续,就和 shell 脚本一样。这是我们的规则:
ACTION=="add" \
, ATTRS{idProduct}=="c52f" \
, ATTRS{idVendor}=="046d" \
, ENV{DISPLAY}=":0" \
, ENV{XAUTHORITY}="/run/user/1000/gdm/Xauthority" \
, RUN+="/usr/bin/xinput --disable 16"
我们来分析一下这个规则。
操作符
首先,对已经使用以及将要使用的操作符解释如下:
== 和 != 操作符
==
是相等操作符,而
!=
是不等于操作符。通过使用它们,我们可以确认规则上应用的键是否匹配各自的值。
分配操作符 = 和 :=
=
是赋值操作符,是用于为一个键赋值。当我们想要赋值,并且想确保它不会被其它规则所覆盖,我们就需要使用
:=
操作符来代替,使用这个操作符分配的值,它就不能被改变。
+= 和 -= 操作符
+=
和
-=
操作符各自用于从一个指定的键定义的值列表中增加或者移除一个值。
我们使用的键
现在,来分析一下在这个规则中我们使用的键。首先,我们有一个
ACTION
键:通过使用它,当在一个设备上发生了特定的事件,我们将指定我们要应用的规则的具体内容。有效的值有
add
、
remove
以及
change
。
然后,我们使用
ATTRS
关键字去指定一个属性去匹配。我们可以使用
udevadm info
命令去列出一个设备属性,提供它的名字或者
sysfs
路径即可:
udevadm info -ap /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.1/0003:046D:C52F.0010/input/input39
Udevadm info starts with the device specified by the devpath and then
walks up the chain of parent devices. It prints for every device
found, all possible attributes in the udev rules key format.
A rule to match, can be composed by the attributes of the device
and the attributes from one single parent device.
looking at device '/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.1/0003:046D:C52F.0010/input/input39':
KERNEL=="input39"
SUBSYSTEM=="input"
DRIVER==""
ATTR{name}=="Logitech USB Receiver"
ATTR{phys}=="usb-0000:00:1d.0-1.2/input1"
ATTR{properties}=="0"
ATTR{uniq}==""
looking at parent device '/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.1/0003:046D:C52F.0010':
KERNELS=="0003:046D:C52F.0010"
SUBSYSTEMS=="hid"
DRIVERS=="hid-generic"
ATTRS{country}=="00"
looking at parent device '/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.1':
KERNELS=="2-1.2:1.1"
SUBSYSTEMS=="usb"
DRIVERS=="usbhid"
ATTRS{authorized}=="1"
ATTRS{bAlternateSetting}==" 0"
ATTRS{bInterfaceClass}=="03"
ATTRS{bInterfaceNumber}=="01"
ATTRS{bInterfaceProtocol}=="00"
ATTRS{bInterfaceSubClass}=="00"
ATTRS{bNumEndpoints}=="01"
ATTRS{supports_autosuspend}=="1"
looking at parent device '/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2':
KERNELS=="2-1.2"
SUBSYSTEMS=="usb"
DRIVERS=="usb"
ATTRS{authorized}=="1"
ATTRS{avoid_reset_quirk}=="0"
ATTRS{bConfigurationValue}=="1"
ATTRS{bDeviceClass}=="00"
ATTRS{bDeviceProtocol}=="00"
ATTRS{bDeviceSubClass}=="00"
ATTRS{bMaxPacketSize0}=="8"
ATTRS{bMaxPower}=="98mA"
ATTRS{bNumConfigurations}=="1"
ATTRS{bNumInterfaces}==" 2"
ATTRS{bcdDevice}=="3000"
ATTRS{bmAttributes}=="a0"
ATTRS{busnum}=="2"
ATTRS{configuration}=="RQR30.00_B0009"
ATTRS{devnum}=="12"
ATTRS{devpath}=="1.2"
ATTRS{idProduct}=="c52f"
ATTRS{idVendor}=="046d"
ATTRS{ltm_capable}=="no"
ATTRS{manufacturer}=="Logitech"
ATTRS{maxchild}=="0"
ATTRS{product}=="USB Receiver"
ATTRS{quirks}=="0x0"