Linux udev rule to create persistent device name
by Wenwei Weng
udev is targeted at Linux kernels 2.6 and beyond to provide a userspace solution for a dynamic /dev directory, with persistent device naming. The previous /dev implementation, devfs, is now deprecated, and udev is seen as the successor.
Background: udev and sysfs
The original /dev directories were just populated with every device that might possibly appear in the system. /dev directories were typically very large because of this. devfs came along to provide a more manageable approach (noticeably, it only populated /dev with hardware that is plugged into the system), as well as some other functionality, but the system proved to have problems which could not be easily fixed.
udev is the “new” way of managing /dev directories, designed to clear up some issues with previous /dev implementations, and provide a robust path forward. In order to create and name /dev device nodes corresponding to devices that are present in the system, udev relies on matching information provided by sysfs with rules provided by the user.
sysfs is a new filesystem to the 2.6 kernels. It is managed by the kernel, and exports basic information about the devices currently plugged into your system. udev can use this information to create device nodes corresponding to your hardware. sysfs is mounted at /sys and is browseable.
what udev rule can do?
udev rules are flexible and very powerful. Here are some of the things you can use rules to achieve:
- Rename a device node from the default name to something else
- Provide an alternative/persistent name for a device node by creating a symbolic link to the default device node
- Name a device node based on the output of a program
- Change permissions and ownership of a device node
- Launch a script when a device node is created or deleted (typically when a device is attached or unplugged)
- Rename network interfaces
Built-in persistent naming schemes
udev provides persistent naming for some device types out of the box. This is a very useful feature, and in many circumstances means that your journey ends here: you do not have to write any rules.
udev provides out-of-the-box persistent naming for storage devices in the /dev/disk directory. To view the persistent names which have been created for your storage hardware, you can use the following command:
[root@sg-centos-hv1 dev]# ls -lR /dev/disk/
/dev/disk/:
total 0
drwxr-xr-x. 2 root root 400 Jun 19 10:15 by-id
drwxr-xr-x. 2 root root 180 Jun 19 10:15 by-path
drwxr-xr-x. 2 root root 120 Jun 19 10:15 by-uuid
/dev/disk/by-id:
total 0
lrwxrwxrwx. 1 root root 9 Jun 19 10:16 ata-ST91000640NS_9XG1BQRV -> ../../sda
lrwxrwxrwx. 1 root root 10 Jun 19 10:15 ata-ST91000640NS_9XG1BQRV-part1 -> ../../sda1
lrwxrwxrwx. 1 root root 10 Jun 19 10:15 ata-ST91000640NS_9XG1BQRV-part2 -> ../../sda2
lrwxrwxrwx. 1 root root 10 Jun 19 10:15 dm-name-vg_sgcentoshv1-lv_home -> ../../dm-2
lrwxrwxrwx. 1 root root 10 Jun 19 10:15 dm-name-vg_sgcentoshv1-lv_root -> ../../dm-0
lrwxrwxrwx. 1 root root 10 Jun 19 10:15 dm-name-vg_sgcentoshv1-lv_swap -> ../../dm-1
lrwxrwxrwx. 1 root root 10 Jun 19 10:15 dm-uuid-LVM-3CQarSxJz8rzmI2nxhj4pKqUFrwrRXkiBpTs4P7cIHC1eetajjlqx2I2PqA9gy3s -> ../../dm-1
lrwxrwxrwx. 1 root root 10 Jun 19 10:15 dm-uuid-LVM-3CQarSxJz8rzmI2nxhj4pKqUFrwrRXkiebnL5kGmac6gqEPPF59925qRUMVH24Co -> ../../dm-0
lrwxrwxrwx. 1 root root 10 Jun 19 10:15 dm-uuid-LVM-3CQarSxJz8rzmI2nxhj4pKqUFrwrRXkiqZYut3A0vRuKrOfNR622Jpoa0zNqmfKy -> ../../dm-2
lrwxrwxrwx. 1 root root 9 Jun 19 10:16 scsi-SATA_ST91000640NS_9XG1BQRV -> ../../sda
lrwxrwxrwx. 1 root root 10 Jun 19 10:15 scsi-SATA_ST91000640NS_9XG1BQRV-part1 -> ../../sda1
lrwxrwxrwx. 1 root root 10 Jun 19 10:15 scsi-SATA_ST91000640NS_9XG1BQRV-part2 -> ../../sda2
lrwxrwxrwx. 1 root root 9 Jun 19 10:15 usb-Cisco_Virtual_CD_DVD_20080930-1-0:0 -> ../../sr1
lrwxrwxrwx. 1 root root 9 Jun 19 10:15 usb-Cisco_Virtual_FDD_HDD_20080930-1-0:1 -> ../../sdb
lrwxrwxrwx. 1 root root 9 Jun 19 10:15 usb-Cisco_Virtual_Floppy_20080930-1-0:2 -> ../../sdc
lrwxrwxrwx. 1 root root 9 Jun 19 10:16 wwn-0x5000c5004067bd70 -> ../../sda
lrwxrwxrwx. 1 root root 10 Jun 19 10:15 wwn-0x5000c5004067bd70-part1 -> ../../sda1
lrwxrwxrwx. 1 root root 10 Jun 19 10:15 wwn-0x5000c5004067bd70-part2 -> ../../sda2
/dev/disk/by-path:
total 0
lrwxrwxrwx. 1 root root 9 Jun 19 10:15 pci-0000:00:1a.7-usb-0:4:1.0-scsi-0:0:0:0 -> ../../sr1
lrwxrwxrwx. 1 root root 9 Jun 19 10:15 pci-0000:00:1a.7-usb-0:4:1.0-scsi-0:0:0:1 -> ../../sdb
lrwxrwxrwx. 1 root root 9 Jun 19 10:15 pci-0000:00:1a.7-usb-0:4:1.0-scsi-0:0:0:2 -> ../../sdc
lrwxrwxrwx. 1 root root 9 Jun 19 10:16 pci-0000:00:1f.2-scsi-0:0:0:0 -> ../../sda
lrwxrwxrwx. 1 root root 10 Jun 19 10:15 pci-0000:00:1f.2-scsi-0:0:0:0-part1 -> ../../sda1
lrwxrwxrwx. 1 root root 10 Jun 19 10:15 pci-0000:00:1f.2-scsi-0:0:0:0-part2 -> ../../sda2
lrwxrwxrwx. 1 root root 9 Jun 19 10:15 pci-0000:00:1f.5-scsi-1:0:0:0 -> ../../sr0
/dev/disk/by-uuid:
total 0
lrwxrwxrwx. 1 root root 10 Jun 19 10:15 24a65001-96cb-4eec-9149-a297f2a0bbaf -> ../../sda1
lrwxrwxrwx. 1 root root 10 Jun 19 10:15 32b80fc5-9055-4732-8991-1e24d46d0665 -> ../../dm-1
lrwxrwxrwx. 1 root root 10 Jun 19 10:15 e2f3cb08-02be-4d27-89be-4c4a58912886 -> ../../dm-0
lrwxrwxrwx. 1 root root 10 Jun 19 10:15 f77f5f9e-d434-47ed-b310-e6b5fda624f4 -> ../../dm-2
[root@sg-centos-hv1 dev]#
This works for all storage types.
Rule writing
These udev rule files are kept in the /etc/udev/rules.d directory, and they all must have the .rules suffix, and parsed in lexical order, and in some circumstances, the order in which rules are parsed is important. In general, you want your own rules to be parsed before the defaults, so it is good idea to create a file at /etc/udev/rules.d/10-local.rules and write all your rules into this file.
Rule syntax
Each rule is constructed from a series of key-value pairs, which are separated by commas. match keys are conditions used to identify the device which the rule is acting upon. When all match keys in a rule correspond to the device being handled, then the rule is applied and the actions of the assignment keys are invoked. Every rule should consist of at least one match key and at least one assignment key. e.g. KERNEL==”hdb”, NAME=”my_special_disk”
Basic Rules
udev provides several different match keys which can be used to write rules which match devices very precisely. Some of the most common keys are introduced below, others will be introduced later in this document. For a complete list, see the udev man page.
- KERNEL - match against the kernel name for the device
- SUBSYSTEM - match against the subsystem of the device
- DRIVER - match against the name of the driver backing the device After you have used a series of match keys to precisely match a device, udev gives you fine control over what happens next, through a range of assignment keys. For a complete list of possible assignment keys, see the udev man page. The most basic assignment keys are introduced below. Others will be introduced later in this document.
- NAME - the name that shall be used for the device node
- SYMLINK - a list of symbolic links which act as alternative names for the device node As hinted above, udev only creates one true device node for one device. If you wish to provide alternate names for this device node, you use the symbolic link functionality. With the SYMLINK assignment, you are actually maintaining a list of symbolic links, all of which will be pointed at the real device node. To manipulate these links, we introduce a new operator for appending to lists: +=. You can append multiple symlinks to the list from any one rule by separating each one with a space. example #1: KERNEL=="hdb", NAME="my_spare_disk" The above rule says: match a device which was named by the kernel as hdb, and instead of calling it hdb, name the device node as my_spare_disk. The device node appears at /dev/my_spare_disk. example #2: KERNEL=="hdb", DRIVER=="ide-disk", SYMLINK+="sparedisk" The above rule says: match a device which was named by the kernel as hdb AND where the driver is ide-disk. Name the device node with the default name and create a symbolic link to it named sparedisk. ### Matching sysfs attributes and find them from sysfs To make the match criteria more granular, usually it is needed to get attributes from sysfs, which is exposed through /sys/. There is linux tool "udevadm" which can retrieve sysfs information and show it in a proper way: e.g. to get a disk /dev/sda info, this can be done below:
iot@iot-sparrow:~$ udevadm info -help
udevadm info [OPTIONS] [DEVPATH|FILE]
Query sysfs or the udev database.
-h --help Print this message
--version Print version of the program
-q --query=TYPE Query device information:
name Name of device node
symlink Pointing to node
path sysfs device path
property The device properties
all All values
-p --path=SYSPATH sysfs device path used for query or attribute walk
-n --name=NAME Node or symlink name used for query or attribute walk
-r --root Prepend dev directory to path names
-a --attribute-walk Print all key matches walking along the chain
of parent devices
-d --device-id-of-file=FILE Print major:minor of device containing this file
-x --export Export key/value pairs
-P --export-prefix Export the key name with a prefix
-e --export-db Export the content of the udev database
-c --cleanup-db Clean up the udev database
iot@iot-sparrow:~$
iot@iot-sparrow:~$ udevadm info -q all -n /dev/sda
P: /devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda
N: sda
S: disk/by-id/ata-WDC_WD2502ABYS-02B7A0_WD-WCAT1H620711
S: disk/by-id/wwn-0x50014ee1038de847
S: disk/by-path/pci-0000:00:1f.2-ata-1
E: DEVLINKS=/dev/disk/by-id/ata-WDC_WD2502ABYS-02B7A0_WD-WCAT1H620711 /dev/disk/by-path/pci-0000:00:1f.2-ata-1 /dev/disk/by-id/wwn-0x50014ee1038de847
E: DEVNAME=/dev/sda
E: DEVPATH=/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda
E: DEVTYPE=disk
E: ID_ATA=1
E: ID_ATA_DOWNLOAD_MICROCODE=1
E: ID_ATA_FEATURE_SET_AAM=1
E: ID_ATA_FEATURE_SET_AAM_CURRENT_VALUE=254
E: ID_ATA_FEATURE_SET_AAM_ENABLED=1
E: ID_ATA_FEATURE_SET_AAM_VENDOR_RECOMMENDED_VALUE=128
E: ID_ATA_FEATURE_SET_HPA=1
E: ID_ATA_FEATURE_SET_HPA_ENABLED=1
E: ID_ATA_FEATURE_SET_PM=1
E: ID_ATA_FEATURE_SET_PM_ENABLED=1
E: ID_ATA_FEATURE_SET_PUIS=1
E: ID_ATA_FEATURE_SET_PUIS_ENABLED=0
E: ID_ATA_FEATURE_SET_SECURITY=1
E: ID_ATA_FEATURE_SET_SECURITY_ENABLED=0
E: ID_ATA_FEATURE_SET_SECURITY_ENHANCED_ERASE_UNIT_MIN=48
E: ID_ATA_FEATURE_SET_SECURITY_ERASE_UNIT_MIN=48
E: ID_ATA_FEATURE_SET_SECURITY_FROZEN=1
E: ID_ATA_FEATURE_SET_SMART=1
E: ID_ATA_FEATURE_SET_SMART_ENABLED=1
E: ID_ATA_ROTATION_RATE_RPM=7200
E: ID_ATA_SATA=1
E: ID_ATA_SATA_SIGNAL_RATE_GEN1=1
E: ID_ATA_SATA_SIGNAL_RATE_GEN2=1
E: ID_ATA_WRITE_CACHE=1
E: ID_ATA_WRITE_CACHE_ENABLED=1
E: ID_BUS=ata
E: ID_MODEL=WDC_WD2502ABYS-02B7A0
E: ID_MODEL_ENC=WDC\x20WD2502ABYS-02B7A0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20
E: ID_PART_TABLE_TYPE=dos
E: ID_PART_TABLE_UUID=00087b41
E: ID_PATH=pci-0000:00:1f.2-ata-1
E: ID_PATH_TAG=pci-0000_00_1f_2-ata-1
E: ID_REVISION=02.03B03
E: ID_SERIAL=WDC_WD2502ABYS-02B7A0_WD-WCAT1H620711
E: ID_SERIAL_SHORT=WD-WCAT1H620711
E: ID_TYPE=disk
E: ID_WWN=0x50014ee1038de847
E: ID_WWN_WITH_EXTENSION=0x50014ee1038de847
E: MAJOR=8
E: MINOR=0
E: SUBSYSTEM=block
E: TAGS=:systemd:
E: USEC_INITIALIZED=2100399
iot@iot-sparrow:~$
iot@iot-sparrow:~$ ls -l /dev/ttyUSB*
crw-rw---- 1 root dialout 188, 0 Jul 21 15:20 /dev/ttyUSB0
crw-rw---- 1 root dialout 188, 1 Jul 20 16:15 /dev/ttyUSB1
crw-rw---- 1 root dialout 188, 10 Jul 21 14:55 /dev/ttyUSB10
crw-rw---- 1 root dialout 188, 11 Jul 22 22:58 /dev/ttyUSB11
crw-rw---- 1 root dialout 188, 12 Jul 21 16:32 /dev/ttyUSB12
crw-rw---- 1 root dialout 188, 2 Jul 23 23:03 /dev/ttyUSB2
crw-rw---- 1 root dialout 188, 3 Jul 21 12:31 /dev/ttyUSB3
crw-rw---- 1 root dialout 188, 4 Jul 21 17:23 /dev/ttyUSB4
crw-rw---- 1 root dialout 188, 5 Jul 23 13:05 /dev/ttyUSB5
crw-rw---- 1 root dialout 188, 6 Jul 20 16:15 /dev/ttyUSB6
crw-rw---- 1 root dialout 188, 7 Jul 20 17:02 /dev/ttyUSB7
crw-rw---- 1 root dialout 188, 8 Jul 21 07:24 /dev/ttyUSB8
crw-rw---- 1 root dialout 188, 9 Jul 22 19:07 /dev/ttyUSB9
#first find the path of USB serail adapter for /dev/ttyUSB0
iot@iot-sparrow:~$ udevadm info -q path -n /dev/ttyUSB0
/devices/pci0000:00/0000:00:1d.7/usb2/2-4/2-4.2/2-4.2:1.0/ttyUSB0/tty/ttyUSB0
# second create a udev rule for it
iot@iot-sparrow:~$ cat /etc/udev/rules.d/10-usbconsoles.rules | grep UUT03
KERNELS=="2-4.2:1.0",SUBSYSTEMS=="usb",SYMLINK+="UUT03"
# with rule in place, we have below:
iot@iot-sparrow:~$ ls -l /dev/UUT03
lrwxrwxrwx 1 root root 7 Jul 20 16:15 /dev/UUT03 -> ttyUSB0
iot@iot-sparrow:~$ udevadm info -a -p /sys/class/net/enp1s0
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:1e.0/0000:01:00.0/net/enp1s0':
KERNEL=="enp1s0"
SUBSYSTEM=="net"
DRIVER==""
ATTR{addr_assign_type}=="0"
ATTR{addr_len}=="6"
ATTR{address}=="00:1b:21:9b:11:d1"
ATTR{broadcast}=="ff:ff:ff:ff:ff:ff"
ATTR{carrier}=="1"
ATTR{carrier_changes}=="2"
ATTR{dev_id}=="0x0"
ATTR{dev_port}=="0"
ATTR{dormant}=="0"
ATTR{duplex}=="full"
ATTR{flags}=="0x1003"
ATTR{gro_flush_timeout}=="0"
ATTR{ifalias}==""
ATTR{ifindex}=="3"
ATTR{iflink}=="3"
ATTR{link_mode}=="0"
ATTR{mtu}=="1500"
ATTR{name_assign_type}=="4"
ATTR{netdev_group}=="0"
ATTR{operstate}=="up"
ATTR{proto_down}=="0"
ATTR{speed}=="100"
ATTR{tx_queue_len}=="1000"
ATTR{type}=="1"
looking at parent device '/devices/pci0000:00/0000:00:1e.0/0000:01:00.0':
KERNELS=="0000:01:00.0"
SUBSYSTEMS=="pci"
DRIVERS=="e1000"
ATTRS{broken_parity_status}=="0"
ATTRS{class}=="0x020000"
ATTRS{consistent_dma_mask_bits}=="32"
ATTRS{d3cold_allowed}=="1"
ATTRS{device}=="0x107c"
ATTRS{dma_mask_bits}=="32"
ATTRS{driver_override}=="(null)"
ATTRS{enable}=="1"
ATTRS{irq}=="16"
ATTRS{local_cpulist}=="0-1"
ATTRS{local_cpus}=="3"
ATTRS{msi_bus}=="1"
ATTRS{numa_node}=="-1"
ATTRS{subsystem_device}=="0x1376"
ATTRS{subsystem_vendor}=="0x8086"
ATTRS{vendor}=="0x8086"
looking at parent device '/devices/pci0000:00/0000:00:1e.0':
KERNELS=="0000:00:1e.0"
SUBSYSTEMS=="pci"
DRIVERS==""
ATTRS{broken_parity_status}=="0"
ATTRS{class}=="0x060401"
ATTRS{consistent_dma_mask_bits}=="32"
ATTRS{d3cold_allowed}=="0"
ATTRS{device}=="0x244e"
ATTRS{dma_mask_bits}=="32"
ATTRS{driver_override}=="(null)"
ATTRS{enable}=="1"
ATTRS{irq}=="0"
ATTRS{local_cpulist}=="0-1"
ATTRS{local_cpus}=="3"
ATTRS{msi_bus}=="1"
ATTRS{numa_node}=="-1"
ATTRS{subsystem_device}=="0x83ca"
ATTRS{subsystem_vendor}=="0x1043"
ATTRS{vendor}=="0x8086"
looking at parent device '/devices/pci0000:00':
KERNELS=="pci0000:00"
SUBSYSTEMS==""
DRIVERS==""
iot@iot-sparrow:~$
iot@iot-sparrow:~$ udevadm monitor -e
monitor will print the received events for:
UDEV - the event which udev sends out after rule processing
KERNEL - the kernel uevent
Subscribe via RSS