Accessing hardware is done through the files under/dev
. Those files can be created manually using mknod. Examples to create device nodes when preparing an initial RAM:
mknod /mnt/initrd/dev/console c 5 1 a character device with major 5 and minor 1
mknod /mnt/initrd/dev/sda1 b 8 1
Having such files does not mean having the necessary kernel device driver and hardware. Also the other way round gives a problem when having the hardware and the the device driver but not having the /dev
file. To be more flexible udev creates the desired files in /dev
automatically and does this using kernel uevents (also hot plugged uevents, and there is netlink that allows IPC from kernel to userspace programs as udev). So device drivers will be linked to the /dev
files. Udev replaced devfs.
After doing its job, udev usually triggers udisks (from the Devicekit project, in the past HAL was used, but HAL is now deprecated) to let applications via D-Bus know that a memory device is plugged in. Similar approaches exist for other than memory devices.
Udev does not mount filesystems but informs the systems about new devices that might hold filesystems! It is up to the system what happens then.
Luckily udev, udisks and D-Bus have all a monitor that can be started on the command line to observe how those events are passed between them. udevadm monitor for udev, udisks --monitor for udisks and dbus-monitor --system for dbus.
/sys
is where the sysfs file system is mounted that contains all info about the devices. udev can rename the dev files from the default name the kernel uses, create
symbolic links, launch scripts.
The default rules create for instance/dev/disk/by-id
where the
memory devices plugged in can be identified. Type ls -lR /dev/disk
If you have a lot of different USB memory devices (Memory sticks, Hard disks, Flash card reader.. ) then
you don't like to guess what is behind sda1. Depending on the sequence how the USB software finds the devices the first device found will be
sda1 and this is nowadays the first SATA hard disk. It is obviously not very user friendly. Better would be: If
you plug in your USB Hard disk, it would be mounted under an unique name. Usually newer desktop environment create a mounting point under/media
and choose a name. To have a useful name give your file system (memory device)a label.
It is also possible to use those /dev
files with /etc/fstab
. In the past this was necessary for some desktop environments, but the modern way is that memory devices are mounted under /media
.
To create other than default names for the dev files rules can be set. However a better approach is keeping the default names but making symbolic links with the new name to the /dev
files. The custom rules are in:/etc/udev/rules.d/10-local.rules
.
/etc/udev/rules.d/50-udev.rules
contains the
default rules, no need to edit it since the rules in
/etc/udev/rules.d/10-local.rules
have
priority.
udev remembers certain things that might be obsolete when time goes by. If you run into problems after unplugging devices it could have a simple reason. They got an other and new name assigned by udev that is stored in the following files. Therefore check the files containing the word persistent in their names:
/etc/udev/rules.d/70-persistent-net.rules
if you have plugged in network cards.
/etc/udev/rules.d/70-persistent-cd.rules
if you exchanged CD drives.
You might find in those file an Ethernet card named eth0 that you have replaced by a new Ethernet card that got the name eth1. Just delete those lines since they get reproduced automatically the correct way.
To write udev rules plug-in all your USB devices and observe the newly created /dev files or when your desktop system pops up already with new devices just mount them and you might see the /dev name or mounting point. You can also run udevadm monitor to see what is going on when you plug in stuff or do the cat /etc/mtab or ls -lR /dev/disk
Knowing the /dev file or mounting point, the following commands tell you how to find a criteria to identify your different devices.
Looking from the hardware side. Go in/sys/block
and look for some
node. Unplug the device and plug it in so you can find it. The following command prints out a device found by
the system including all its data to be used as rules.
udevadm info -a -p /sys/block/sda
Looking at a usb device looks more complicated, its path can be get using udevadm monitor and then see it parameters
udevadm info -a -p /sys/devices/pci0000\:00/0000\:00\:10.4/usb2/2-8/2-8.2/
The other way round showing where in /sys the data is:
udevadm info -q path -n /dev/sda
Or combining both is more straight forward not needing to scan through /sys manually.
udevadm info -a -p $(udevadm info -q path -n /dev/sda)
Or if you are lazy to just see what is needed, the serial number of the device:
udevadm info -a -p $(udevadm info -q path -n /dev/sda)| grep serial
Devices are organized in a tree. The device itself is the child device and controller on the motherboard is its parent device. The controller on the motherboard is also a child device of e.g the PCI subsystem. This is important for writing rules, since all match keys need to be on the tree branch.
Every line in a udev rule file is a rule.
There are match keys with == and assignment keys with = . Multiple match keys can be added but all match key must point to the same device so no mixture between child and parent devices are supported. If all match keys match the assignments are done. The assignment could be NAME=<my dev name>
however less radical is the assignment is the SYMLINK+=<my dev name>
that creates a link to the default /dev file, it has also a += assignment operator. GROUP, OWNER and MODE are other assignment keys to get those parameters to the /dev file created.
Edit:/etc/udev/rules.d/10-local.rules
The following file shows an example how such rules look like, however they are no more necessary on a new desktop environment using dbus and udisks.
Example 7.4. udev rule
#Intenso usb stick 32GByte BUS=="usb", KERNEL=="sd*", ATTRS{serial}=="08121800042367",NAME="intenso%n" #Freecom hard disk BUS=="usb", KERNEL=="sd*", ATTRS{serial}=="13E32A4102CD01FW401",NAME="usbhd%n" #Apacer Card Reader BUS=="scsi", KERNEL=="sd*", SYSFS{model}=="IC1210 CF",SYMLINK+="cflash%n" BUS=="scsi", KERNEL=="sd*", SYSFS{model}=="IC1210 MS",SYMLINK+="mstick%n" BUS=="scsi", KERNEL=="sd*", SYSFS{model}=="IC1210 MMC/SD",SYMLINK+="mmcsd%n" BUS=="scsi", KERNEL=="sd*", SYSFS{model}=="IC1210 SM",SYMLINK+="smedia%n" #any further usb memory stick BUS=="usb", DRIVERS=="usb-storage", KERNEL=="sd*",NAME="usbstk%n"
Every line is an udev rule. After reboot udev reads the rules and stores them in memory (a part of the udev database) to have them faster available when required. According the man pages the udev demon should detect automatically changes in those files and reload them but there is also udevadm control --reload-rules for it. A line is separated with commas in keys. There are two kind of keys identification keys and assignment keys. That a rule takes action all identification keys must match (logical and). All those identification keys have to be in the same /sys directory. In the sample above a device with the product serial number
13E32A4102CD0671FW401
and sitting on the usb bus will create the assignment. The assignment key NAME results in device nodes usbhd, usbhd1 to usbhd(last partition).
For multi card reader select in kernel: kernel probing multiple LUNs.
If you have problems to read/var/log/messages
, try to delete
the problem nodes as /dev/sda. Maybe there are conflicts between static /dev (manually produced) and udev
produce /dev. Not everything on the USB is considered as USB, it might be SCSI (as the card reader above)!
Some device files might not be able to be created by udev, since they have to be there during boot before udev comes alive. So create them statically:
mknod -m 660 console c 5 1
mknod -m 660 null c 1 3
/lib/udev-state/devices.tar.bz2
contains /dev
directory to be restored at next boot. This way the devices not created or not supported by udev (static /dev)
survive. Mechanism can be configured in/etc/conf.d/rc
. Side effect
is that all nodes once created (even by udev) will be there forever (so delete the ones where you are sure).
Problem: The empty card reader creates the /dev/sda file but not /dev/sda1 since there is no partition. When a card gets plugged in no udev event is produced, so no dev file is created. With fdisk /dev/sda the /dev/sda1 will be created or with a reboot and the flash card plugged.
RC_DEVICE_TARBALL="yes"
in/etc/config.d/rc
saves it for the next boot.
The problem of the multi slot card readers is to find out what /dev/sd* is mapped to what slot? The sample above tells how to find it out. To scare you, the mapping is not static, if you plug in other memory sticks it could produce an other mapping (as windows does). Way out are udev rules.
Edit/etc/udev/rules.d/10-local.rules
#Apacer Card Reader BUS=="scsi", KERNEL=="sd*", SYSFS{model}=="IC1210 CF", NAME="cflash%n" BUS=="scsi", KERNEL=="sd*", SYSFS{model}=="IC1210 MS", NAME="mstick%n" BUS=="scsi", KERNEL=="sd*", SYSFS{model}=="IC1210 MMC/SD", NAME="mmcsd%n" BUS=="scsi", KERNEL=="sd*", SYSFS{model}=="IC1210 SM", NAME="smedia%n"
and you have it fixed. This is also where my /dev/mmcsd comes from. All usb memory sticks plugged in can be accessed under /dev/usbstk and not one time sda then sde and so on. it took me a long time until my computer behaves like this, but it is not that complicated when you know how.
Here a rule that changes group ownership:
SUBSYSTEMS=="usb", ATTRS{product}=="i2c-tiny-usb", GROUP="uucp"
Since more than one driver might be involved the access might still fail.
Sometimes udev is responsible that some devices do not work anymore. The reason might be simple, a mess with the device names might exist. It seems that udev seem to remember well what has had been plugged once into your computer, and uses the default names for the devices that have been removed from the PC. Therefore check files as /etc/udev/rules.d/*-persistent-*.rules and delete the line that contain old hardware.
To see what udev does, you can run test and observe the output: udevadm test /sys/block/sdb . Check for the udev_rules_apply_to_event: that point to the files containing the rules and even the line numbers within it.
udevadm test --action=add /devices/pci0000:00/0000:00:10.4/usb2/2-8/2-8.4/2-8.4:1.3
PROGRAM is not used to run some programs to perform some action, even it this would work. Program produces a variable %c that can then be used to create a name of the /dev file. During the run of the program referred by COMMAND the device node is not been created so it can not be accessed.
To run Programs RUN must be used. Those programs run after the /dev file is created and therefore can do things as mount. RUN has often an issues and might not run. A good way is to move custom rules to the back of the uderv rules this is done to put them in a file with a high number. Also udevadm test --action=add /sys/devices/pci0000\:00/0000\:00\:10.4/usb2/2-8/2-8.4/ shows in the last lines a list of what run commands are executed. Replacing --action=remove shows what is going on when the device is pulgged out.
More complex rules might exist for a ebook reader that will report two discs an internal one and an external one. ls -lR /dev/disk shows them as sdg and sdh but this might change if other devices are plugged in. Unfortunately the outputs of udevadm info -a -p $(udevadm info -q path -n /dev/sdg) and udevadm info -a -p $(udevadm info -q path -n /dev/sdh) differ just in the file size and they have the same parent device, so no easy udev rule can be made. The problem is that parent and child devices need to be matched in a single udev rule. ATTRS{serial}=="3238204E6F76" can be used in the udev rule to match the ebook reader, but this is not enough to decide if it is the internal or external disk. This would just create a single /dev file.
The way out is the RUN+= assignment that can call an external script that returns ebookint for the internal disk and ebookext for the external disk. To know to what the kernel is looking udev has to pass the /dev file to the script, this is the contents of %k and what the script reports back is %c.
The rule looks therefore quite simple:
ATTRS{serial}=="3238204E6F76", RUN+="<path to script>
%k", NAME="%c"
What is missing is the script. The script can be written in any language and gets as command line parameter what is passed in the udev rule in our case %k is the device name. To pass back data it needs to print to the standard output.
A good test is just create a file
RUN+="/usr/bin/touch /tmp/udev.touch"
and be aware that absolute paths must be set also to the program. udev starts when the PATH environmental variable hasn't setup completely
A little test script shows that the concept works:
#!/bin/bash device=ebook echo $1$device
And two dev files are created, since udev calls this rule twice:
cd /dev
ls | grep ebook
sdgebook
sdhebook
Now regular programming is required the rule stays unchanged.
The following udev python script uses a temporary file to keep track of the sequence the rule is called and defines the first call to be the internal disk and the second the external disk. The delay is necessary, since the two calls follow to shortly after each other. An attempt to use environmental variables failed since the export
#!/usr/bin/python import os import time if os.path.exists("/tmp/udevflag"): os.system("rm -f /tmp/udevflag"); print "ebookext" else: print "ebookint" os.system("touch /tmp/udevflag"); time.sleep(1)
In the config file /etc/udev/udev.conf logging can be activated. The default rules are in /lib/udev/rules.d/ and should no t be altered, /etc/udev/rules.d/ hold the customized rules and finally /etc/udev/rules.d/10-local.rules hold the local administrators rules.
Advanced rules make use of LABELS and GOTO to disable rules to be checked when not relevant. A common use of such labels are
ACTION!="add", GOTO="my_rules_end"
<rules when adding>
LABEL="my_rules_end"
udevadm info --export-db prints the udev database to the screen. It points to /sys and /dev and shows major and minor among other informations.