Udev rule basics
Tutorials
writing udev rules http://reactivated.net/writing_udev_rules.html debian wiki https://wiki.debian.org/udev
Overview
udev rules are matched against devices
- at system boot
- when new devices are detected by the kernel (ex: usb-stick plugged in)
rules consist of a variable number of matches against device attributes,
and a variable number of actions to take for matching devices.
Walkthrough
1. identify target device/attributes
Use
udevadm
to list your target device's attributes.
We will use these to identify your device in our udev rule.udevadm info \ --name=/dev/video2 \ --attribute-walkoutput
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:1a.0/usb1/1-1/1-1.2/1-1.2.4/1-1.2.4:1.0/video4linux/video2': KERNEL=="video2" SUBSYSTEM=="video4linux" DRIVER=="" ATTR{dev_debug}=="0" ATTR{index}=="0" ATTR{name}=="Logitech Webcam C930e" ATTR{power/control}=="auto" ATTR{power/runtime_active_time}=="0" ATTR{power/runtime_status}=="unsupported" ATTR{power/runtime_suspended_time}=="0" looking at parent device '/devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2.4/1-1.2.4:1.0': KERNELS=="1-1.2.4:1.0" SUBSYSTEMS=="usb" DRIVERS=="uvcvideo" ATTRS{authorized}=="1" ATTRS{bAlternateSetting}==" 0" ATTRS{bInterfaceClass}=="0e" ATTRS{bInterfaceNumber}=="00" ATTRS{bInterfaceProtocol}=="00" ATTRS{bInterfaceSubClass}=="01" ATTRS{bNumEndpoints}=="01" ATTRS{iad_bFirstInterface}=="00" ATTRS{iad_bFunctionClass}=="0e" ATTRS{iad_bFunctionProtocol}=="00" ATTRS{iad_bFunctionSubClass}=="03" ATTRS{iad_bInterfaceCount}=="02" ATTRS{supports_autosuspend}=="1" looking at parent device '/devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2.4': KERNELS=="1-1.2.4" SUBSYSTEMS=="usb" DRIVERS=="usb" ATTRS{authorized}=="1" ATTRS{avoid_reset_quirk}=="0" ATTRS{bConfigurationValue}=="1" ATTRS{bDeviceClass}=="ef" ATTRS{bDeviceProtocol}=="01" ATTRS{bDeviceSubClass}=="02" ATTRS{bMaxPacketSize0}=="64" ATTRS{bMaxPower}=="500mA" ATTRS{bNumConfigurations}=="1" ATTRS{bNumInterfaces}==" 4" ATTRS{bcdDevice}=="0013" ATTRS{bmAttributes}=="80" ATTRS{busnum}=="1" ATTRS{configuration}=="" ATTRS{devnum}=="20" ATTRS{devpath}=="1.2.4" ATTRS{idProduct}=="0843" ATTRS{idVendor}=="046d" ATTRS{ltm_capable}=="no" ATTRS{maxchild}=="0" ATTRS{power/active_duration}=="26230" ATTRS{power/autosuspend}=="2" ATTRS{power/autosuspend_delay_ms}=="2000" ATTRS{power/connected_duration}=="18625980" ATTRS{power/control}=="auto" ATTRS{power/level}=="auto" ATTRS{power/persist}=="1" ATTRS{power/runtime_active_time}=="24390" ATTRS{power/runtime_status}=="suspended" ATTRS{power/runtime_suspended_time}=="18599227" ATTRS{product}=="Logitech Webcam C930e" ATTRS{quirks}=="0x40" ATTRS{removable}=="unknown" ATTRS{rx_lanes}=="1" ATTRS{serial}=="C1282FDE" ATTRS{speed}=="480" ATTRS{tx_lanes}=="1" ATTRS{urbnum}=="5189" ATTRS{version}==" 2.00" looking at parent device '/devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2': KERNELS=="1-1.2" SUBSYSTEMS=="usb" DRIVERS=="usb" ATTRS{authorized}=="1" ATTRS{avoid_reset_quirk}=="0" ATTRS{bConfigurationValue}=="1" ATTRS{bDeviceClass}=="09" ATTRS{bDeviceProtocol}=="01" ATTRS{bDeviceSubClass}=="00" ATTRS{bMaxPacketSize0}=="64" ATTRS{bMaxPower}=="100mA" ATTRS{bNumConfigurations}=="1" ATTRS{bNumInterfaces}==" 1" ATTRS{bcdDevice}=="0655" ATTRS{bmAttributes}=="e0" ATTRS{busnum}=="1" ATTRS{configuration}=="" ATTRS{devnum}=="17" ATTRS{devpath}=="1.2" ATTRS{idProduct}=="0610" ATTRS{idVendor}=="05e3" ATTRS{ltm_capable}=="no" ATTRS{manufacturer}=="GenesysLogic" ATTRS{maxchild}=="4" ATTRS{power/active_duration}=="18627270" ATTRS{power/autosuspend}=="0" ATTRS{power/autosuspend_delay_ms}=="0" ATTRS{power/connected_duration}=="18627270" ATTRS{power/control}=="auto" ATTRS{power/level}=="auto" ATTRS{power/runtime_active_time}=="18627027" ATTRS{power/runtime_status}=="active" ATTRS{power/runtime_suspended_time}=="0" ATTRS{power/wakeup}=="disabled" ATTRS{power/wakeup_abort_count}=="" ATTRS{power/wakeup_active}=="" ATTRS{power/wakeup_active_count}=="" ATTRS{power/wakeup_count}=="" ATTRS{power/wakeup_expire_count}=="" ATTRS{power/wakeup_last_time_ms}=="" ATTRS{power/wakeup_max_time_ms}=="" ATTRS{power/wakeup_total_time_ms}=="" ATTRS{product}=="USB2.1 Hub" ATTRS{quirks}=="0x0" ATTRS{removable}=="removable" ATTRS{rx_lanes}=="1" ATTRS{speed}=="480" ATTRS{tx_lanes}=="1" ATTRS{urbnum}=="89" ATTRS{version}==" 2.10" looking at parent device '/devices/pci0000:00/0000:00:1a.0/usb1/1-1': KERNELS=="1-1" SUBSYSTEMS=="usb" DRIVERS=="usb" ATTRS{authorized}=="1" ATTRS{avoid_reset_quirk}=="0" ATTRS{bConfigurationValue}=="1" ATTRS{bDeviceClass}=="09" ATTRS{bDeviceProtocol}=="01" ATTRS{bDeviceSubClass}=="00" ATTRS{bMaxPacketSize0}=="64" ATTRS{bMaxPower}=="0mA" ATTRS{bNumConfigurations}=="1" ATTRS{bNumInterfaces}==" 1" ATTRS{bcdDevice}=="0000" ATTRS{bmAttributes}=="e0" ATTRS{busnum}=="1" ATTRS{configuration}=="" ATTRS{devnum}=="2" ATTRS{devpath}=="1" ATTRS{idProduct}=="0024" ATTRS{idVendor}=="8087" ATTRS{ltm_capable}=="no" ATTRS{maxchild}=="6" ATTRS{power/active_duration}=="19875980" ATTRS{power/autosuspend}=="0" ATTRS{power/autosuspend_delay_ms}=="0" ATTRS{power/connected_duration}=="19875980" ATTRS{power/control}=="auto" ATTRS{power/level}=="auto" ATTRS{power/runtime_active_time}=="19875638" ATTRS{power/runtime_status}=="active" ATTRS{power/runtime_suspended_time}=="0" ATTRS{power/wakeup}=="disabled" ATTRS{power/wakeup_abort_count}=="" ATTRS{power/wakeup_active}=="" ATTRS{power/wakeup_active_count}=="" ATTRS{power/wakeup_count}=="" ATTRS{power/wakeup_expire_count}=="" ATTRS{power/wakeup_last_time_ms}=="" ATTRS{power/wakeup_max_time_ms}=="" ATTRS{power/wakeup_total_time_ms}=="" ATTRS{quirks}=="0x0" ATTRS{removable}=="fixed" ATTRS{rx_lanes}=="1" ATTRS{speed}=="480" ATTRS{tx_lanes}=="1" ATTRS{urbnum}=="88" ATTRS{version}==" 2.00" looking at parent device '/devices/pci0000:00/0000:00:1a.0/usb1': KERNELS=="usb1" SUBSYSTEMS=="usb" DRIVERS=="usb" ATTRS{authorized}=="1" ATTRS{authorized_default}=="1" ATTRS{avoid_reset_quirk}=="0" ATTRS{bConfigurationValue}=="1" ATTRS{bDeviceClass}=="09" ATTRS{bDeviceProtocol}=="00" ATTRS{bDeviceSubClass}=="00" ATTRS{bMaxPacketSize0}=="64" ATTRS{bMaxPower}=="0mA" ATTRS{bNumConfigurations}=="1" ATTRS{bNumInterfaces}==" 1" ATTRS{bcdDevice}=="0510" ATTRS{bmAttributes}=="e0" ATTRS{busnum}=="1" ATTRS{configuration}=="" ATTRS{devnum}=="1" ATTRS{devpath}=="0" ATTRS{idProduct}=="0002" ATTRS{idVendor}=="1d6b" ATTRS{interface_authorized_default}=="1" ATTRS{ltm_capable}=="no" ATTRS{manufacturer}=="Linux 5.10.36-1-lts ehci_hcd" ATTRS{maxchild}=="2" ATTRS{power/active_duration}=="19876230" ATTRS{power/autosuspend}=="0" ATTRS{power/autosuspend_delay_ms}=="0" ATTRS{power/connected_duration}=="19876240" ATTRS{power/control}=="auto" ATTRS{power/level}=="auto" ATTRS{power/runtime_active_time}=="19876209" ATTRS{power/runtime_status}=="active" ATTRS{power/runtime_suspended_time}=="0" ATTRS{power/wakeup}=="disabled" ATTRS{power/wakeup_abort_count}=="" ATTRS{power/wakeup_active}=="" ATTRS{power/wakeup_active_count}=="" ATTRS{power/wakeup_count}=="" ATTRS{power/wakeup_expire_count}=="" ATTRS{power/wakeup_last_time_ms}=="" ATTRS{power/wakeup_max_time_ms}=="" ATTRS{power/wakeup_total_time_ms}=="" ATTRS{product}=="EHCI Host Controller" ATTRS{quirks}=="0x0" ATTRS{removable}=="unknown" ATTRS{rx_lanes}=="1" ATTRS{serial}=="0000:00:1a.0" ATTRS{speed}=="480" ATTRS{tx_lanes}=="1" ATTRS{urbnum}=="24" ATTRS{version}==" 2.00" looking at parent device '/devices/pci0000:00/0000:00:1a.0': KERNELS=="0000:00:1a.0" SUBSYSTEMS=="pci" DRIVERS=="ehci-pci" ATTRS{ari_enabled}=="0" ATTRS{broken_parity_status}=="0" ATTRS{class}=="0x0c0320" ATTRS{companion}=="" ATTRS{consistent_dma_mask_bits}=="32" ATTRS{d3cold_allowed}=="1" ATTRS{device}=="0x1e2d" ATTRS{dma_mask_bits}=="32" ATTRS{driver_override}=="(null)" ATTRS{enable}=="1" ATTRS{irq}=="16" ATTRS{local_cpulist}=="0-7" ATTRS{local_cpus}=="ff" ATTRS{msi_bus}=="1" ATTRS{numa_node}=="-1" ATTRS{power/control}=="on" ATTRS{power/runtime_active_time}=="19876401" ATTRS{power/runtime_status}=="active" ATTRS{power/runtime_suspended_time}=="0" ATTRS{power/wakeup}=="enabled" ATTRS{power/wakeup_abort_count}=="0" ATTRS{power/wakeup_active}=="0" ATTRS{power/wakeup_active_count}=="0" ATTRS{power/wakeup_count}=="0" ATTRS{power/wakeup_expire_count}=="0" ATTRS{power/wakeup_last_time_ms}=="0" ATTRS{power/wakeup_max_time_ms}=="0" ATTRS{power/wakeup_total_time_ms}=="0" ATTRS{revision}=="0x04" ATTRS{subsystem_device}=="0x5006" ATTRS{subsystem_vendor}=="0x1458" ATTRS{uframe_periodic_max}=="100" ATTRS{vendor}=="0x8086" looking at parent device '/devices/pci0000:00': KERNELS=="pci0000:00" SUBSYSTEMS=="" DRIVERS=="" ATTRS{power/control}=="auto" ATTRS{power/runtime_active_time}=="0" ATTRS{power/runtime_status}=="unsupported" ATTRS{power/runtime_suspended_time}=="0"
2. Create rule-file in /etc/udev/rules.d/*.rules
NOTE:
If using systemd-udev, you cannot name/symlink devices that clash with assigned names.
You can start a systemd service that moves the devices though. See examples in video4linux.Generally speaking:
- attribute matches use equality operators
==
or!=
.- actions use assignment
=
# 50-logitech_as_default_webcam.rules ATTR{name}=="Logitech Webcam C930e" `# matches` \ SUBSYSTEM=="video4linux" \ ATTR{index}=="0" \ NAME="video0" `# an action (bind to /dev/video0)` # and for second device exposed by same webcam ATTR{name}=="Logitech Webcam C930e" SUBSYSTEM=="video4linux" \ ATTR{index}=="1" `# <-- I'm different` \ NAME="video1"Common match attributes
ATTR{serial}=="123456789" # when present, often unique even between duplicate devicesCommon actions
NAME="my_printer" # device mounted as /dev/my_printer SYMLINK+="my_printer" # symlink from default created at `/dev/my_printer` RUN+="/usr/bin/do_the_thing" # run a programOther rules in play
The default udev ruleset is generally written to
/usr/lib/udev/rules.d/50-udev-default.rules
grep through it and understand how your rule might interfere with others.
3. test, by reloading rules
# confirm rule assigned correct device (without reloading rules) # NOTE: does not matter if /dev/video0 will change, looks up by raw device udevadm test $(udevadm info -q path -n /dev/video0) # reload rules udevadm control --reload-rules
Honorable Mentions
starting a systemd service
You can call a systemd service by ensuring
- The event is tagged with systemd
- Assign the service in
ENV{SYSTEMD_WANTS}
This is best for long running jobs, but also allows you to easily pull logs.
# /etc/udev/rules.d/50-set-default-webcam.rules ACTION=="add", SUBSYSTEM=="video4linux", TAG+="systemd", ENV{SYSTEMD_WANTS}="set-default-webcam.service"# /etc/systemd/system/set-default-webcam.service [Unit] Description=Set default webcam [Service] Type=oneshot ExecStart=/usr/bin/some-commmand arg arg arg [Install] WantedBy=multi-user.target