Udev
This page was originally written by Don Tanner aka dtanner, but due to the fact that udev has historically been a moving target, most of it is now out of date. This is an attempt to bring it up to date, but for the most part, you should refer to the official udev documentation.
The information in this page is, to the best of my knowledge, current as of Slackware 11.0 -- the general ideas still apply on later versions, but sysfs keys have different names and such. See the relevant man pages and udev documentation for information on how to adapt this for later udev (and Slackware) versions.
Users often want to have a device (such as a removable usb flash memory stick) always get assigned the same device node - this makes it easy to specify the node in /etc/fstab for mounting it. You don't actually want to assign the same device node (in other words, rather than renaming the *real* device node, you want to create a persistent symlink to it - changing the name of the node itself can cause problems later, especially with respect to logging - the kernel logs will show device nodes that no longer exist). To do this, first plug in the removable memory stick and run dmesg | tail to see what device node is assigned.
rworkman@tritium:~$ dmesg | tail sda: Mode Sense: 03 00 00 00 sda: assuming drive cache: write through SCSI device sda: 2001888 512-byte hdwr sectors (1025 MB) sda: Write Protect is off sda: Mode Sense: 03 00 00 00 sda: assuming drive cache: write through sda: sda1 sd 0:0:0:0: Attached scsi removable disk sda usb-storage: device scan complete sd 0:0:0:0: Attached scsi generic sg0 type 0
As you can see, the device is attached to /dev/sda (and since it has one partition, what I'm actually interested in is /dev/sda1). Now use udevinfo(8) to find out the sysfs path to the device.
rworkman@tritium:~$ udevinfo -q path -n /dev/sda1 /block/sda/sda1
Now use udevinfo(8) again to show all of the keys exported to sysfs:
rworkman@tritium:~$ udevinfo -q all -a -p /block/sda/sda1 Udevinfo 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 '/block/sda/sda1': KERNEL=="sda1" SUBSYSTEM=="block" DRIVER=="" SYSFS{stat}==" 131 131 0 0" SYSFS{size}=="1999627" SYSFS{start}=="245" SYSFS{dev}=="8:1" looking at parent device '/block/sda': ID=="sda" BUS=="block" DRIVER=="" SYSFS{stat}==" 11 121 139 28 0 0 0 0 0 28 28" SYSFS{size}=="2001888" SYSFS{removable}=="1" SYSFS{range}=="16" SYSFS{dev}=="8:0" looking at parent device '/devices/pci0000:00/0000:00:1d.7/usb4/4-6/4-6:1.0/host0/target0:0:0/0:0:0:0': ID=="0:0:0:0" BUS=="scsi" DRIVER=="sd" SYSFS{ioerr_cnt}=="0x1" SYSFS{iodone_cnt}=="0x15" SYSFS{iorequest_cnt}=="0x15" SYSFS{iocounterbits}=="32" SYSFS{timeout}=="30" SYSFS{state}=="running" SYSFS{rev}=="0.2 " SYSFS{model}=="Cruzer Micro " SYSFS{vendor}=="SanDisk " SYSFS{scsi_level}=="3" SYSFS{type}=="0" SYSFS{queue_type}=="none" SYSFS{queue_depth}=="1" SYSFS{device_blocked}=="0" SYSFS{max_sectors}=="240" looking at parent device '/devices/pci0000:00/0000:00:1d.7/usb4/4-6/4-6:1.0/host0/target0:0:0': ID=="target0:0:0" BUS=="" DRIVER=="" looking at parent device '/devices/pci0000:00/0000:00:1d.7/usb4/4-6/4-6:1.0/host0': ID=="host0" BUS=="" DRIVER=="" looking at parent device '/devices/pci0000:00/0000:00:1d.7/usb4/4-6/4-6:1.0': ID=="4-6:1.0" BUS=="usb" DRIVER=="usb-storage" SYSFS{modalias}=="usb:v0781p5151d0020dc00dsc00dp00ic08isc06ip50" SYSFS{bInterfaceProtocol}=="50" SYSFS{bInterfaceSubClass}=="06" SYSFS{bInterfaceClass}=="08" SYSFS{bNumEndpoints}=="02" SYSFS{bAlternateSetting}==" 0" SYSFS{bInterfaceNumber}=="00" looking at parent device '/devices/pci0000:00/0000:00:1d.7/usb4/4-6': ID=="4-6" BUS=="usb" DRIVER=="usb" SYSFS{configuration}=="" SYSFS{serial}=="SNDK5D1BF51877607705" SYSFS{product}=="Cruzer Micro" SYSFS{manufacturer}=="SanDisk Corporation" SYSFS{maxchild}=="0" SYSFS{version}==" 2.00" SYSFS{devnum}=="2" SYSFS{speed}=="480" SYSFS{bMaxPacketSize0}=="64" SYSFS{bNumConfigurations}=="1" SYSFS{bDeviceProtocol}=="00" SYSFS{bDeviceSubClass}=="00" SYSFS{bDeviceClass}=="00" SYSFS{bcdDevice}=="0020" SYSFS{idProduct}=="5151" SYSFS{idVendor}=="0781" SYSFS{bMaxPower}=="200mA" SYSFS{bmAttributes}=="80" SYSFS{bConfigurationValue}=="1" SYSFS{bNumInterfaces}==" 1" looking at parent device '/devices/pci0000:00/0000:00:1d.7/usb4': ID=="usb4" BUS=="usb" DRIVER=="usb" SYSFS{configuration}=="" SYSFS{serial}=="0000:00:1d.7" SYSFS{product}=="EHCI Host Controller" SYSFS{manufacturer}=="Linux 2.6.17.13 ehci_hcd" SYSFS{maxchild}=="6" SYSFS{version}==" 2.00" SYSFS{devnum}=="1" SYSFS{speed}=="480" SYSFS{bMaxPacketSize0}=="64" SYSFS{bNumConfigurations}=="1" SYSFS{bDeviceProtocol}=="01" SYSFS{bDeviceSubClass}=="00" SYSFS{bDeviceClass}=="09" SYSFS{bcdDevice}=="0206" SYSFS{idProduct}=="0000" SYSFS{idVendor}=="0000" SYSFS{bMaxPower}==" 0mA" SYSFS{bmAttributes}=="e0" SYSFS{bConfigurationValue}=="1" SYSFS{bNumInterfaces}==" 1" looking at parent device '/devices/pci0000:00/0000:00:1d.7': ID=="0000:00:1d.7" BUS=="pci" DRIVER=="ehci_hcd" SYSFS{modalias}=="pci:v00008086d000024CDsv00001028sd00000126bc0Csc03i20" SYSFS{local_cpus}=="1" SYSFS{irq}=="7" SYSFS{class}=="0x0c0320" SYSFS{subsystem_device}=="0x0126" SYSFS{subsystem_vendor}=="0x1028" SYSFS{device}=="0x24cd" SYSFS{vendor}=="0x8086" looking at parent device '/devices/pci0000:00': ID=="pci0000:00" BUS=="" DRIVER==""
As you can see, this is a lot of information. The actual device information exported (the first block that says looking at device '/block/sda/sda1' is not useful to us, as there's no guarantee that it is unique (in other words, some other device could very easily have the same information). Continue looking down the output (which is actually up the device chain) to parent devices until you find one that has unique information in it. In this case, the third block (looking at parent device '/devices/pci0000:00/0000:00:1d.7/usb4/4-6/4-6:1.0/host0/target0:0:0/0:0:0:0':) might at first appear useful, but it's not -- instead, look at the seventh block (looking at parent device '/devices/pci0000:00/0000:00:1d.7/usb4/4-6') which has the serial number of the device exported.
looking at parent device '/devices/pci0000:00/0000:00:1d.7/usb4/4-6': ID=="4-6" BUS=="usb" DRIVER=="usb" SYSFS{configuration}=="" SYSFS{serial}=="SNDK5D1BF51877607705" SYSFS{product}=="Cruzer Micro" SYSFS{manufacturer}=="SanDisk Corporation" SYSFS{maxchild}=="0" SYSFS{version}==" 2.00" SYSFS{devnum}=="2" SYSFS{speed}=="480" SYSFS{bMaxPacketSize0}=="64" SYSFS{bNumConfigurations}=="1" SYSFS{bDeviceProtocol}=="00" SYSFS{bDeviceSubClass}=="00" SYSFS{bDeviceClass}=="00" SYSFS{bcdDevice}=="0020" SYSFS{idProduct}=="5151" SYSFS{idVendor}=="0781" SYSFS{bMaxPower}=="200mA" SYSFS{bmAttributes}=="80" SYSFS{bConfigurationValue}=="1" SYSFS{bNumInterfaces}==" 1"
This is the information that we can use to create a custom udev rule for this device. It's important to remember that all of the information in a single udev rule must come from the same block of information (all from the same device or parent device) or it will not work. Based on the keys above, we should be able to use this udev rule to always create a custom symlink to /dev/cruzer when the device is plugged in:
BUS=="usb", SYSFS{manufacturer}=="SanDisk Corporation", SYSFS{product}=="Cruzer Micro", SYSFS{serial}=="SNDK5D1BF51877607705", KERNEL=="sd?1", NAME="%k", SYMLINK+="cruzer"
Note that the "==" string is a test operator and the "=" string is an assignment operator. In essence, the above rule tests for all the conditions with "==" and makes assignments with "=" - in other words, the rule will assign the default name for the device node (in this case, /dev/sda1), and make a symlink to it at /dev/cruzer.
Now that we've got a rule, where do we put it? You might think that it would go in the default /etc/udev/rules.d/udev.rules file, but you'd be wrong -- this file should *never* be edited by the system admin unless you are prepared to make the edits after every udev update/upgrade. The default rules file is *always* overwritten by an upgraded udev package, as these rules *must* remain in sync with the upstream udev rules. Therefore, the proper place to put our new custom rule is in a custom rules file. The choice of naming for this file is up to you, but note that it must end with the suffix .rules for udevd(8) to look at it. A common choice of name is either 10-custom.rules or 10-local.rules. Place the rule above into whichever name you choose for the file and unplug the device. To see if it works properly, the easiest thing to do is run udevtrigger(8) with no arguments:
root@tritium:/home/rworkman# udevtrigger root@tritium:/home/rworkman# ls -l /dev/cruzer lrwxrwxrwx 1 root root 4 2007-05-13 16:25 /dev/cruzer -> sda1
As you can see, the symlink to the device was created correctly, and I can now add this device to /etc/fstab using the /dev/cruzer link.
Note that udev's rule parsing order can influence whether your custom rule works as expected. That's not to say it will influence whether it works - it just might not work as expected. For example, your rule in 10-local.rules might work properly, but then one of the default rules in udev.rules (this file is named 50-udev.rules in Slackware post-11.0) will be evaluated and change the device nodes and such to the defaults. If you suspect this is happening, either try placing your custom rule in a file that will be evaluated after the default rules, or the ":=" operator (see udev(7)) might help.
See also:
- udev(7) -- type "man 7 udev"
- udevd(8)
- udevinfo(8)
- udevmonitor(8)
- udevtrigger(8)
- udevsettle(8)
- udevtest(8)
- /usr/doc/udev-$VERSION/ -- especially the ./docs/writing_udev_rules/ directory
OLD INFO - this information is almost certainly out of date, and the sysfs keys are definitely changed, but just in case anyone still needs it, the idea is still basically the same, so I'm leaving it here. Your mileage may vary...
Cranky Card Readers
Some card readers fail to report the existence of any partitions if they are plugged in before a card is inserted. Thus, if the card reader is assigned /dev/sda
the /dev/cardreader
symlink is created, but /dev/sda1
does not yet exist, and the /dev/cardreader1
symlink is missing. One solution to this problem is to set up a rule that causes all possible device node names to be created when the cardreader is plugged in.
The following rule causes all device nodes to be created for any removable media with a size of zero, i.e., a card reader with no card in it:
SUBSYSTEM=="block", SYSFS{size}=="0", SYSFS{removable}=="1", NAME{all_partitions}="cardreader%n"