auf.suno
Connector, investor, futurist, geek, software developer, innovator, sportsman, libertarian, business enabler, cosmopolitan, autodidact, funny finch, tech evangelist,
purist, agnostic, Kärnten fan, foodie, artist, globetrotter, social liberal but fiscal conservative, Schöngeist... elegantiorum litterarum amans oder studiosus...

This is the website of Markus Gattol. It is composed, driven and secured/encrypted exclusively by Open Source Software. The speciality of this website
is that it is seamlessly integrating into my daily working environment (Python + MongoDB + Linux + SSH + GIT + ZeroMQ) which therefore means it
becomes a fully fledged and automatized publishing and communication platform. It will be under construction until 2014.

Open Source / Free Software, because freedom is in everyone's language...
Frihed Svoboda Libertà Vrijheid เสรีภาพ Liberté Freiheit Cê̤ṳ-iù Ελευθερία Свобода חרות Bebas Libertada 自由
auf.suno
Website Sections
Home
FAQs
About Me
Tweets by @markusgattol
Udev or how I manage my Gadgets
Status: This page is considered finished. However changes/reviews might happen.
Last changed: Sunday 2014-11-23 08:52 UTC
Abstract:

Udev is the device manager for the Linux 2.6 kernel series. Its primary function is managing device nodes in /dev. I use it to manage my various devices and gadgets e.g. digicam, external USB HDD (Hard Disk Drive), music player etc. Udev creates sane configurations for various devices out of the box. It might thus not be necessary to temper with it at all -- in fact, most users never use it. However, if one wants to create some degree of automation on his system e.g. download photos from his digicam automatically to the computer and sort them into categories, he might wish to configure udev to provide him with all the means necessary to put this sort of automation in place.
Table of Contents
Theory Part
Udev - What is it?
Udev - How does it work?
Udev - What are its Use Cases?
Practical Part
Gathering Information
Advanced Topics
Testing and Debugging

Gadgets of various kinds without the ability to connect to each another and share data as well as computations1 are pointless. In order for gadgets to be able to exchange information with humans and/or other non-human entities they either need to fully configure and manage themselves or someone/something needs to do it. This page is not going to cover all necessary things like software communication protocols, information theory, hardware related issues, communication theory, physics, communication standards or whatever else is necessary in order for gadgets being able to exchange information with other entities.

This page will just focus on how to manage and configure a gadget for use with today's (Anno Domini + 2008 years) common computers running DebianGNU/Linux, enabling it to exchange information with the computer and/or enabling it to directly or indirectly (via other gadgets) exchange information with other gadgets and therefor become a good and useful fellow to humans.

Theory Part

What happens, if someone with a recent Linux kernel (series 2.6), plugs a device (external HDD (Hard Disk Drive), portable music player, camcorder, printer, etc.) to his running computer or boots his computer with, say his digital camera already connected to his computer via the USB cable? Furthermore, for those who know what happens in such cases, how exactly can one influence those things in order to customize them to his personal benefit? The reader who knows all this can skip this section, anyone else might continue reading.

Udev - What is it?

Udev is a device manager, a piece of software that does something if something happens in a way we can influence. In order to understand what udev is, let us consider why someone, back in time, might have thought that something like udev is needed. Nowadays, almost everybody who owns a computer, also has got another device that can be, or needs to be connected to the computer in order to make serve us well.

Without the Linux Kernel and udev, no device would be noticed by the computer. The Linux Kernel and udev work together hand in hand in order to notice devices and make them usable.

The predecessor of udev was roughly speaking the Linux Kernel itself. This approach turned out to be rather problematic and complicated for various reasons. That in mind, and full of ambitions, folks put their heads together and invented a new, rather different approach to cope with the relationship computer/attached devices. In fact, that is what we nowadays know as udev and uevent2.

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 its successor.

Over the years, the things that we might use udev rules for have changed, as well as the flexibility of rules themselves. On a modern system, udev provides persistent naming for some device types out of the box, eliminating the need for custom rules for those devices. However, some users will still require/desire the extra level of customization.

We assume, that one has already installed udev. With DebianGNU/Linux, just issue apt-get install udev or aptitude install udev to install udev if not already in place. I am not going cover every single detail how to write udev rules but I will introduce all of the main concepts. One can take a look into the udev man page (man 7 udev) for detailed information. However, one needs to know the udev terminology in order to understand the remainder of this section.

device node
On typical Linux-based systems, the /dev directory is used to store file-like device nodes which refer to certain devices in the system. Each node points to a particular part of the system (a device), which might or might not exist. Userspace applications can use these device nodes to interface with the systems hardware, for example, the X server will listen to /dev/input/mice so that it can relate the user's mouse movements to moving the visual mouse pointer. Another example for device nodes is given in section Linux & Partition Types.
devfs
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 to the system), as well as some other functionality, but the system proved to have problems which could not be easily fixed. So another, new approach (udev + uevent + sysfs with /dev) was initiated in order to improve and ease things.
sysfs
sysfs is a new in-memory filesystem to the 2.6 kernels. It is managed by the kernel, and exports basic information about the devices currently plugged to the system. Udev can use this information to create device nodes corresponding to currently plugged/connected hardware. sysfs is mounted at /sys and is browseable. One may wish to investigate some of the files stored there before getting to grips with udev. Throughout this page, I will use the terms /sys and sysfs interchangeably.
uevent
Now, since the whole shebang takes place in kernelspace and in userspace, event information3 has to be send from kernelspace to userspace. uevent is the kernel interface, used to send event information from the kernel to the udev daemon (udevd) running in userspace. For more detailed information download the current Linux kernel sources and take a look at the file ../lib/kobject_uevent.c.
udev
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. Those are the ones that can be used to create an automated scenario as I mentioned within the abstract.

Udev - How does it work?

Udev basically consists the parts listed below

  • Kernel device event management
  • Event processing
  • Event queue management
  • Booting and Cold-plugging
  • Kernel module loading

Kernel device event management

On an udev system, besides the maintenance of the dynamic /dev directory, the kernel driver core event handling is handed-over exclusively to udev. One of the first actions during bootup is, the /dev directory gets a tmpfs filesystem mounted. All initial and static device nodes from the /lib/udev/devices= directory are copied to the empty /dev directory. After that, the udev daemon is started. The daemon listens to uevents from the driver core. Therefore the execution of /sbin/hotplug is not necessary and completely disabled by setting /sys/kernel/uevent_helper (or /proc/sys/kernel/hotplug on older kernels) setting it to an empty string. All added or removed devices will cause an uevent to be sent to the udev daemon, which runs an event process to match against udev rules and possibly create a device node and symlinks and run specified programs to set up the device.

Event processing

The udev daemon (udevd) reads and parses all provided rules from the /etc/udev/rules.d/*.rules files once at startup and keeps them in memory. If rules within those files are changed, added or removed, the daemon receives an inotify event and updates the in-memory representation of the rules. Every event matches against the set of provided rules. The rules can add or change event environment keys, request a specific name for the device node to be created, add symlinks pointing to the node or add programs to be run after the device node is created. The driver core's uevents are received from a kernel netlink socket, which look like this on the wire:

recv(4, "addclass/input/input9/mouse2\0ACTION=add\0
         DEVPATH=/class/input/input9/mouse2\0SUBSYSTEM=input\0
         SEQNUM=1064\0PHYSDEVPATH=/devices/pci0000:00/0000:00:1d.1/usb2/2-2/2-2:1.0\0
         PHYSDEVBUS=usb\0PHYSDEVDRIVER=usbhid\0MAJOR=13\0MINOR=34\0", 2048, 0) = 221

An udev rule can match on any property the kernel adds to the event itself, exports in sysfs or the rule can request additional information from external programs. The rules syntax and the provided keys to match or import data is described in the udev man pages. An advanced example is the persistent device naming, implemented to provide stable names for all disks devices - regardless of their order of recognition or the connection used to plug the device:

1  sa@pc1:~$ ls -lR /dev/disk
2  /dev/disk:
3  total 0
4  drwxr-xr-x 2 root root 300 2006-11-22 17:36 by-id
5  drwxr-xr-x 2 root root 100 2006-11-22 06:50 by-label
6  drwxr-xr-x 2 root root 300 2006-11-22 17:36 by-path
7  drwxr-xr-x 2 root root 120 2006-11-22 06:50 by-uuid
8

Udev provides out-of-the-box persistent naming for storage devices within the /dev/disk directory. To view the persistent names which have been created for storage hardware, we can use the command in line 1 which actually works for all storage types (not just HDD's).

The command in line 1 lists directory contents recursively. As we can see, /dev/disk has four subdirectories (line 4 to 7). Udev provides persistent naming for some device types out of the box. This is a very useful feature, and in many circumstances means that our journey ends here — we simply do not have to write any rules since the names devices get this way are unique and can be used as is.


 9  /dev/disk/by-id:
10  total 0
11  lrwxrwxrwx 1 root root  9 2006-11-22 06:50 ata-HITACHI_DK23EA-60_7L0725 -> ../../hda
12  lrwxrwxrwx 1 root root 10 2006-11-22 06:50 ata-HITACHI_DK23EA-60_7L0725-part1 -> ../../hda1
13  lrwxrwxrwx 1 root root 10 2006-11-22 06:50 ata-HITACHI_DK23EA-60_7L0725-part2 -> ../../hda2
14  lrwxrwxrwx 1 root root 10 2006-11-22 06:50 ata-HITACHI_DK23EA-60_7L0725-part3 -> ../../hda3
15  lrwxrwxrwx 1 root root 10 2006-11-22 06:50 ata-HITACHI_DK23EA-60_7L0725-part4 -> ../../hda4
16  lrwxrwxrwx 1 root root 10 2006-11-22 06:50 ata-HITACHI_DK23EA-60_7L0725-part5 -> ../../hda5
17  lrwxrwxrwx 1 root root  9 2006-11-22 06:50 ata-HITACHI_DK23EA-60_8P2936 -> ../../hdb
18  lrwxrwxrwx 1 root root 10 2006-11-22 06:50 ata-HITACHI_DK23EA-60_8P2936-part1 -> ../../hdb1
19  lrwxrwxrwx 1 root root 10 2006-11-22 06:50 ata-HITACHI_DK23EA-60_8P2936-part2 -> ../../hdb2
20  lrwxrwxrwx 1 root root  9 2006-11-22 06:50 ata-TOSHIBA_DVD-ROM_SD-R6012_X25S407429 -> ../../hdc
21  lrwxrwxrwx 1 root root  9 2006-11-22 17:36 usb-Maxtor_6_B250R0_DEF107679C83 -> ../../sdb
22  lrwxrwxrwx 1 root root 10 2006-11-22 17:36 usb-Maxtor_6_B250R0_DEF107679C83-part2 -> ../../sdb2
23  lrwxrwxrwx 1 root root  9 2006-11-22 06:50 usb-NEWTREND_MMC_READER_BULK -> ../../sda
24

Line 9 to 23 lists device nodes and corresponding mount-points per ID (Identifier). Note, that I could for example bind mount (see man 8 mount) my partition containing my private holiday photos etc. on /dev/hdb2 using the symbolic link (ata-HITACHI_DK23EA-60_8P2936-part2) in line 19 instead of writing my own udev rule.


25  /dev/disk/by-label:
26  total 0
27  lrwxrwxrwx 1 root root 10 2006-11-22 06:50 boot -> ../../hda1
28  lrwxrwxrwx 1 root root 10 2006-11-22 06:50 homemedia -> ../../hdb2
29  lrwxrwxrwx 1 root root 10 2006-11-22 06:50 tmp -> ../../hda5
30

Line 25 to 29 lists device nodes and corresponding mount-points per label.


31  /dev/disk/by-path:
32  total 0
33  lrwxrwxrwx 1 root root  9 2006-11-22 06:50 pci-0000:00:1d.0-usb-0:2:1.0-scsi-0:0:0:0 -> ../../sda
34  lrwxrwxrwx 1 root root  9 2006-11-22 06:50 pci-0000:00:1f.1-ide-0:0 -> ../../hda
35  lrwxrwxrwx 1 root root 10 2006-11-22 06:50 pci-0000:00:1f.1-ide-0:0-part1 -> ../../hda1
36  lrwxrwxrwx 1 root root 10 2006-11-22 06:50 pci-0000:00:1f.1-ide-0:0-part2 -> ../../hda2
37  lrwxrwxrwx 1 root root 10 2006-11-22 06:50 pci-0000:00:1f.1-ide-0:0-part3 -> ../../hda3
38  lrwxrwxrwx 1 root root 10 2006-11-22 06:50 pci-0000:00:1f.1-ide-0:0-part4 -> ../../hda4
39  lrwxrwxrwx 1 root root 10 2006-11-22 06:50 pci-0000:00:1f.1-ide-0:0-part5 -> ../../hda5
40  lrwxrwxrwx 1 root root  9 2006-11-22 06:50 pci-0000:00:1f.1-ide-0:1 -> ../../hdb
41  lrwxrwxrwx 1 root root 10 2006-11-22 06:50 pci-0000:00:1f.1-ide-0:1-part1 -> ../../hdb1
42  lrwxrwxrwx 1 root root 10 2006-11-22 06:50 pci-0000:00:1f.1-ide-0:1-part2 -> ../../hdb2
43  lrwxrwxrwx 1 root root  9 2006-11-22 06:50 pci-0000:00:1f.1-ide-1:0 -> ../../hdc
44  lrwxrwxrwx 1 root root  9 2006-11-22 17:36 pci-0000:02:05.2-usb-0:2:1.0-scsi-0:0:0:0 -> ../../sdb
45  lrwxrwxrwx 1 root root 10 2006-11-22 17:36 pci-0000:02:05.2-usb-0:2:1.0-scsi-0:0:0:0-part2 -> ../..
46  /sdb2

Line 31 to 46 lists device nodes and corresponding mount-points per path.


47  /dev/disk/by-uuid:
48  total 0
49  lrwxrwxrwx 1 root root 10 2006-11-22 06:50 3440df43-900c-4e92-9962-f980fd7b57f9 -> ../../hda5
50  lrwxrwxrwx 1 root root 10 2006-11-22 06:50 6a77635b-973d-4623-b288-fbebb3eb8624 -> ../../hda1
51  lrwxrwxrwx 1 root root 10 2006-11-22 06:50 a2ee5d89-dab9-45c5-96b7-bb2734f7fe58 -> ../../hdb2
52  lrwxrwxrwx 1 root root  9 2006-11-22 06:50 ca82dc09-c720-4dcf-a4b4-3ad32559db7c -> ../../md0
53  sa@pc1:~$

Line 47 to 53 lists device nodes and corresponding mount-points per uuid.

Event queue management

The udev daemon takes care of the right order of event execution and serializes events for devices which depend on other events. Events for child devices will be delayed until the event for the parent device has returned. That way, partition events will wait for the main block device event to finish, so the partition event can import the parents information, already stored in the udev database.

Udevd throttles the execution of events and limits the running processes, if there are already too many in running state. If the event carries a TIMEOUT key passed by the kernel, the event will run immedediately, regardless of the throttling state.

The current state of the event queue is visible in /etc/.udev/queue. If that directory exists, then events are currently queued or already running. Every event in this directory is represented as a symlink to the corresponding sysfs device. With the removal of the last event, the whole directory goes away. All event processes which have failed, cause of an error, or executed programs returning a failure, will be represented in /etc/.udev/failed. A later successful event for the same device, will remove the symlink from that directory.

Booting and Cold-plugging

All device events happening during the boot process until the udev daemon is running are obviously lost, because the infrastructure to handle these events lives on the root file system and is not available at that time. To cover that hole, the kernel now provides an uevent file for every device in the sysfs file system . By writing add to that file, the kernel will just emit exactly the same event again, as the one lost during bootup. A simple loop over all uevent files in /sys triggers all events again, to create the device nodes and do the device setup.

From userspace, there is no difference between a device coldplug sequence and a device discovery during run time. In both cases, the same rules are used to match and the same configured programs are run. This replaces the former *.agent and *.rc scripts in the /etc/hotplug directory.

Certain steps during bootup will need to synchronize with the kernel event handling. This can be accomplished by watching the event queue directory at /etc/.udev/queue. If events fail cause of missing dependencies at this stage, they can be retried at a later stage, by picking up the stored symlinks from the /etc/.udev/failed directory.

Kernel module loading

The kernel bus drivers probe for devices. For every detected device, the kernel creates an internal device structure and the driver core sends an event to udev. Devices identify themselves by an ID (Identifier), which tells what kind of device it is. Usually these IDs consist of vendor and product id and other susbsytem specific values. Every bus has its own scheme for these id's. The kernel takes this information, composes a MODALIAS string from it and sends that string along with the event. For an USB mouse it looks like this: MODALIAS=usb:v046DpC03Ed2000dc00dsc00dp00ic03isc01ip02.

Every device driver carries a list of known IDs for devices it can handle. The list is contained in the kernel module file itself. The program depmod reads the id lists and creates the file modules.alias in the kernel's /lib/modules directory for all currently available modules. With this infrastructure, module loading is as easy as calling modprobe for every event that carries a MODALIAS key. If modprobe $MODALIAS is called, it matches the device alias composed for the device with the aliases provided by the module. If a matching entry is found, that module will be loaded. This replaces the entire hotplug shell script logic to search matching modules in the *.map files. The former /etc/hotplug/blacklist file is replaced by the module-init-tools native blacklist command.

Udev - What are its Use Cases?

Udev rules are flexible and very powerful. Here are some of the things we can use rules for:

  • 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 .

Writing rules is not a workaround for the problem where no device nodes for a articular device exist. Even if there are no matching rules, udev will create the device node with the default name supplied by the kernel. Having persistently named device nodes has several advantages. Assume we own two USB storage devices which might for example be a digital camera and a USB flash drive.

These devices are typically assigned device nodes /dev/sda and /dev/sdb but the exact assignment depends on the order they are plugged to the computer. This may cause problems to some users if they for example set up application software to automatically search for data, down/upload data etc. In such cases, one would benefit greatly if each device could be named persistently every time it is plugged e.g. /dev/camera and /dev/flashdisk.


For example, one has stored all his mp3s files on his external USB HDD and his music application software searches for those files using a particular device node. Which device node entry should he create for the music player to search for stored music? Is it /dev/sda or /dev/sdb? Without udev rules, the HDD once appears to be found at /dev/sda and for the next time at /dev/sdb or maybe /dev/sdi depending on when he plugs in the devices or what is currently going on with the system — he simply can not determine on which device node the external USB HDD (Hard Disk Drive) loaded with all his music files shows up.

This is exactly one of the moments where folks learn to love udev and the ability to write udev rules in order to create persistent device nodes. For example, one could write an udev rule that takes care for the HDD with mp3 music files always appearing as /dev/usb/usb_disk_music.

By the moment, device nodes for any particular device become persistent, mounting devices to fixed places within the file system tree (e.g. /dev/usb/usb_disk_music mounted to mount point /media/music) becomes a trivial task. This mounting can also be automated with udev as I will show further down. In fact I have a pictures use case example where can be seen how useful it can be to have some udev rules in place.

Writing udev rules

Writing udev rules is nothing complicated but there are three prerequisites for writing udev rules:

  • One needs to know the theory (what is mentioned on this page).
  • Gathering information from random devices used to write udev rules.
  • The basic syntax how to write rules and a basic understanding of Unix-like OSs (Operating Systems).
Rule files:

When deciding how to name a device and which additional actions to perform, udev reads a series of rules from files. These files are kept in the /etc/udev/rules.d directory, and they all must have the .rules suffix. Default udev rules are stored in /etc/udev/rules.d/udev.rules. One may find it interesting to look over this file — it includes a few examples, and then some default rules proving a devfs-style /dev layout. However, we should not write rules into this file directly.

Files in /etc/udev/rules.d/ are parsed in lexicographical order, and in some circumstances, the order in which rules are parsed is important. In general, we want our own rules to be parsed before the defaults, so I suggest to create files/symbolic links with a lower lexicographical order than the default .rules files.

Within a rules file, lines starting with # are treated as comments. Every other non-blank line is a rule. Rules cannot span multiple lines.

One device can be matched by more than one rule. This has it is practical advantages, for example, we can write two rules which match the same device, where each one provides its own alternate name for the device. Both alternate names will be created, even if the rules are in separate files. It is important to understand that udev will not stop processing when it finds a matching rule, it will continue searching and attempt to apply every rule that it knows about. This behavior can also be disabled4.

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. Here is an example rule to illustrate the above:

KERNEL=="hdb", NAME="my_spare_disk"

The above rule includes one match key (KERNEL) and one assignment key (NAME). The semantics of these keys and their properties will be detailed later. It is important to note that the match key is related to its value through the equality operator (==), whereas the assignment key is related to its value through the assignment operator (=).

Basic Rules:

Udev provides several different match keys which can be used to write rules which match devices very precisely.

Match Key Description
ACTION Match the name of the event action.
DEVPATH Match the devpath of the event device.
KERNEL Match the name of the event device.
SUBSYSTEM Match the subsystem of the event device.
ATTR{filename} Match sysfs attribute values of the event device. Up to five ATTR keys can be specified per rule. Trailing whitespace in the attribute values is ignored, if the specified match value does not contain trailing whitespace itself. Depending on the type of operator, this key is also used to set the value of a sysfs attribute.
KERNELS Search the devpath upwards for a matching device name.
SUBSYSTEMS Search the devpath upwards for a matching device subsystem name.
DRIVERS Search the devpath upwards for a matching device driver name.
ATTRS{filename} Search the devpath upwards for a device with matching sysfs attribute values. Up to five ATTRS keys can be specified per rule. All attributes must match on the same device. Trailing whitespace in the attribute values is ignored, if the specified match value does not contain trailing whitespace itself.
ENV{key} Match against the value of an environment variable. Up to five ENV keys can be specified per rule. Depending on the type of operator, this key is also used to export a variable to the environment.
PROGRAM Execute external program. The key is true, if the program returns without exit code zero. The whole event environment is available to the executed program. The program's output printed to stdout is available for the RESULT key.
RESULT Match the returned string of the last PROGRAM call. This key can be used in the same or in any later rule after a PROGRAM call.

After we have used a series of match keys to precisely match a device, udev gives us fine control over what happens next, through a range of assignment keys.


Assignment Key Description
NAME The name of the node to be created, or the name the network interface should be renamed to. Only one rule can set the node name, all later rules with a NAME key will be ignored.
SYMLINK The name of a symlink targeting the node. Every matching rule can add this value to the list of symlinks to be created along with the device node. Multiple symlinks may be specified by separating the names by the space character.
OWNER,GROUP, MODE The permissions for the device node. Every specified value overwrites the compiled-in default value.
ATTR{key} The value that should be written to a sysfs attribute of the event device. Depending on the type of operator, this key is also used to match against the value of a sysfs attribute.
ENV{key} Export a variable to the environment. Depending on the type of operator, this key is also to match against an environment variable.
RUN Add a program to the list of programs to be executed for a specific device. This can only be used for very short running tasks. Running an event process for a long period of time may block all further events for this or a dependent device. Long running tasks need to be immediately detached from the event process itself.
LABEL Named label where a GOTO can jump to.
GOTO Jumps to the next LABEL with a matching name
IMPORT{type} Import the printed result or the value of a file in environment key format into the event environment. program will execute an external program and read its output. file will import a text file. If no option is given, udev will determine it from the executable bit of of the file permissions.
WAIT_FOR_SYSFS Wait for the specified sysfs file of the device to be created. Can be used to fight against kernel sysfs timing issues.
OPTIONS last_rule stops further rules application. No later rules will have any effect. ignore_device will ignore this event completely. ignore_remove will ignore any later remove event for this device. This may be useful as a workaround for broken device drivers. all_partitions will create the device nodes for all available partitions of a block device. This may be useful for removable media devices where media changes are not detected.

As hinted above, udev only creates one true device node for one device. If we wish to provide alternate names for this device node, we need to use the symbolic link functionality. With the SYMLINK assignment, we 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 +=. We can append multiple symlinks to the list from any one rule by separating each one with a space.


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.


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. Note that we did not specify a device node name, so udev uses the default. In order to preserve the standard /dev layout, our own rules will typically leave the NAME alone but create some symlinks and/or perform other assignments.


KERNEL=="hdc", SYMLINK+="cdrom cdrom0"

The above rule is probably more typical of the types of rules one might be writing. It creates two symbolic links at /dev/cdrom and /dev/cdrom0, both of which point at /dev/hdc. Again, no NAME assignment was specified, so the default kernel name (hdc) is used.

Matching sysfs attributes:

The match keys introduced so far only provide limited matching capabilities. Realistically we require much finer control. We want to identify devices based on advanced properties such as vendor codes, exact product numbers, serial numbers, storage capacities, number of partitions, etc.

Many drivers export information like this into sysfs, and udev allows us to incorporate sysfs-matching into our rules, using the ATTR or ATTRS key with a slightly different syntax. Below is an example, using sysfs attributes to match a certain device.

SUBSYSTEM=="block", ATTR{size}=="212941575",  ATTRS{model}=="B250R0*",SYMLINK:="usb/usb_disk_backup"
String substitutions:

When writing rules which will potentially handle multiple similar devices, udev's printf-like string substitution operators are very useful. We can simply include these operators in any assignments our rule makes, and udev will evaluate them when they are executed.

The most common operators are %k and %n. %k evaluates to the kernel name for the device, e.g. sda3 for a device that would (by default) appear at /dev/sda3. %n evaluates to the kernel number for the device (the partition number for storage devices), e.g. 3 for /dev/sda3. Udev also provides several other substitution operators for more advanced functionality.

There is also an alternative syntax for these operators - $kernel and $number for the examples above. For this reason, if we wish to match a literal % in a rule then we must write %%, and if we wish to match a literal $ then we have to write $$. To illustrate the concept of string substitution, some example rules are shown below.

KERNEL=="mice", NAME="input/%k"
KERNEL=="loop0", NAME="loop/%n", SYMLINK+="%k"

The first rule ensures that the mice device node appears exclusively in the /dev/input directory (by default it would be at /dev/mice). The second rule ensures that the device node named loop0 is created at /dev/loop/0 but also creates a symbolic link at /dev/loop0 as usual. The use of the above rules is questionable, as they all could be rewritten without using any substitution operators. The true power of these substitutions will become apparent in the next section.

String matching:

As well as matching strings exactly, udev allows us to use shell-style pattern matching. There are 3 patterns supported:

* Match any character, zero or more times.
? Match any character exactly once.
[] Matches any single character specified within the brackets. For example, the pattern string tty[SR] would match either ttyS or ttyR. Ranges are also supported within this match with the - character. For example, to match on the range of all digits, the pattern [0-9] would be used. If the first character following the [ is a !, any characters not enclosed are matched.

Here are some examples which incorporate the above patterns. Note the use of the string substitution operators.

KERNEL=="fd[0-9]*", NAME="floppy/%n", SYMLINK+="%k"
KERNEL=="hiddev*", NAME="usb/%k"

The first rule matches all floppy disk drives, and ensures that the device nodes are placed in the /dev/floppy directory, as well as creating a symbolic link from the default name. The second rule ensures that hiddev devices are only present in the /dev/usb directory.

Practical Part

As opposed to the theoretical (vital to know!) part, this one is going to show how to actually use that former gained knowledge to set up and configure devices and gadgets using udev.

Gathering Information

This subsection is about how to gather the information necessary to write udev rules.

The sysfs Tree:

The concept of using interesting information from sysfs was briefly touched upon above. In order to write rules based on this information, we first need to know the names of the attributes and their current values. Sysfs is actually a very simple structure. It is logically divided into directories. Each directory contains a number of files (attributes) which typically contain just one value. Some symbolic links are present, which link various parts of the tree together.

Some directories are referred to as top-level device paths. These directories act as the top-level glue which chain other parts of sysfs to the device in question. Top-level device paths can be classified as sysfs directories which contain a dev file e.g. find /sys -name dev will list these.

For example, on my system, the /sys/block/hda directory is the device path for my hard disk. It is chained to the controller through the /sys/block/hda/device symbolic link, which is in turn chained to the device driver through the /sys/block/hda/device/driver/ symbolic link and to the IDE bus through /sys/block/hda/device/bus/.

When we write rules based on sysfs information, we are simply matching attribute contents of some files in one part of the chain i.e. I can determine the size of my hard disk (partition) as follows:

sa@pc1:~$ cat /sys/block/hda/size
117210240
sa@pc1:~$

In an udev rule, I could use ATTR{size}=="117210240" to identify this HDD. As udev iterates through the entire device chain, I could alternatively opt to match attributes in another part of the chain (e.g. attributes in /sys/class/block/sda/device/). Although this serves as a useful introduction as to the structure of sysfs and exactly how udev matches values, manually trawling through sysfs is both time consuming and unnecessary as can be seen below.

udevinfo:

With DebianGNU/Linux, if the package udev is installed (line 7 below, the ii indicates it is) entering udevinfo, is probably the most straightforward way we can use to construct rules. All we need to know is the sysfs device path of the device in question.

 1  sa@pc1:~$ dpkg -l udev
 2  Desired=Unknown/Install/Remove/Purge/Hold
 3  | Status=Not/Installed/Config-files/Unpacked/Failed-config/Half-installed
 4  |/ Err?=(none)/Hold/Reinst-required/X=both-problems (Status,Err: uppercase=bad)
 5  ||/ Name                 Version              Description
 6  +++-====================-====================-========================================================
 7  ii  udev                 0.100-2.3            /dev/ and hotplug management daemon
 8  sa@pc1:~$ apt-file list udev | grep bin
 9  udev: sbin/scsi_id
10  udev: sbin/udevcontrol
11  udev: sbin/udevd
12  udev: sbin/udevsettle
13  udev: sbin/udevtrigger
14  udev: usr/bin/udevinfo
15  udev: usr/bin/udevtest
16  udev: usr/sbin/udevmonitor
17  sa@pc1:~$

Line 9 to 16 above show all files which are contained within the udev package AND which contain the string bin.


sa@pc1:~$ udevinfo -a -p /sys/block/hda | head -n 25

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/hda':
    KERNEL=="hda"
    SUBSYSTEM=="block"
    DRIVER==""
    ATTR{stat}=="   24776     1698   650729   359804    42230   109624
1249854  8888792        0   542312  9248596"
    ATTR{size}=="117210240"
    ATTR{removable}=="0"
    ATTR{range}=="64"
    ATTR{dev}=="3:0"

  looking at parent device '/devices/pci0000:00/0000:00:1f.1/ide0/0.0':
    KERNELS=="0.0"
    SUBSYSTEMS=="ide"
    DRIVERS=="ide-disk"
    ATTRS{modalias}=="ide:m-disk"
    ATTRS{drivename}=="hda"
    ATTRS{media}=="disk"

sa@pc1:~$

As we can see, udevinfo simply produces a list of attributes we can use as-is as match keys in our udev rules. From the above udevinfo output, I could produce the following rule for this device:

SUBSYSTEM=="block", ATTR{size}=="117210240", NAME="my_hard_disk"

We are usually provided with a large number of attributes, and we must pick a number of them to construct a rule. In general, we want to choose attributes which identify our device in a persistent and human-recognisable way. In the examples above, I chose the size of my disk. I did not use meaningless stuff such as ATTR{stat}==" 24776 1698 650729 359804 42230 109624 1249854 8888792 0 542312 9248596".

Another point to note is that it is common for text attributes appearing in udevinfo output to be padded with spaces (e.g. ATTR{stat}==" 24776 1698... 9248596" above). In our rules, we can either specify the extra spaces, we can cut them off or supply a *.

The only tricky thing using udevinfo is that we are required to know the top-level device path (/sys/block/hda in the example above). This is not always obvious. However, as we are generally writing rules for device nodes which already exist, we can use udevinfo to look up the device path for us (the first line could also be written like that udevinfo -a -p $(udevinfo -q path -n /dev/hda). I prefer using back-quotes instead

sa@pc1:~$ udevinfo -a -p `udevinfo -q path -n /dev/hda`

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/hda':
    KERNEL=="hda"
    SUBSYSTEM=="block"
    DRIVER==""
    ATTR{stat}=="   25142     1703   655793   367320    61293
140074  1664590 11570192        0   695884 11937512"
    ATTR{size}=="117210240"
    ATTR{removable}=="0"
    ATTR{range}=="64"
    ATTR{dev}=="3:0"

  looking at parent device '/devices/pci0000:00/0000:00:1f.1/ide0/0.0':
    KERNELS=="0.0"
    SUBSYSTEMS=="ide"
    DRIVERS=="ide-disk"
    ATTRS{modalias}=="ide:m-disk"
    ATTRS{drivename}=="hda"
    ATTRS{media}=="disk"

  looking at parent device '/devices/pci0000:00/0000:00:1f.1/ide0':
    KERNELS=="ide0"
    SUBSYSTEMS==""
    DRIVERS==""

  looking at parent device '/devices/pci0000:00/0000:00:1f.1':
    KERNELS=="0000:00:1f.1"
    SUBSYSTEMS=="pci"
    DRIVERS=="PIIX_IDE"
    ATTRS{modalias}=="pci:v00008086d0000248Asv00000000sd00000000bc01sc01i8a"
    ATTRS{local_cpus}=="ff"
    ATTRS{irq}=="10"
    ATTRS{class}=="0x01018a"
    ATTRS{subsystem_device}=="0x0000"
    ATTRS{subsystem_vendor}=="0x0000"
    ATTRS{device}=="0x248a"
    ATTRS{vendor}=="0x8086"

  looking at parent device '/devices/pci0000:00':
    KERNELS=="pci0000:00"
    SUBSYSTEMS==""
    DRIVERS==""

sa@pc1:~$

Advanced Topics

This subsection covers some advanced topics which, at least from my point of view, are very practical in real-world scenarios.

Controlling permissions and ownership:

Udev allows us to use additional assignments in rules to control ownership and permission attributes on each device.

The GROUP assignment allows us to define which Unix group should own the device node. Below is an example rule which defines that the video group will own the framebuffer devices:

KERNEL=="fb[0-9]*", NAME="fb/%n", SYMLINK+="%k", GROUP="video"

The OWNER key, perhaps less useful, allows us to define which Unix user should have ownership permissions on the device node. Assuming the slightly odd situation where we would want john to own our floppy devices, we could write:

KERNEL=="fd[0-9]*", OWNER="john"

Udev defaults to creating nodes with Unix permissions of 0660 (read/write to owner and group). In case we need others, we can override these defaults on certain devices using rules including the MODE assignment. As an example, the following rule defines that the inotify node shall be readable and writable to everyone:

KERNEL=="inotify", NAME="misc/%k", SYMLINK+="%k", MODE="0666"

Using external programs to name devices:

Under some circumstances, we may require more flexibility than standard udev rules can provide. In this case, we can use udev to run a program and use the standard output from that program to provide device naming. In order to use this functionality, we simply specify the absolute path of the program to run (and any parameters) in the PROGRAM assignment. Doing so, we could then use some variant of the %c substitution in the NAME/SYMLINK assignments.

The following examples refer to a fictional program found at /bin/device_namer. device_namer takes one command line argument which is the kernel name for the device. Based upon this kernel name, device_namer does its magic and produces some output to the usual stdout pipe, split into several parts. Each part is just a single word, and parts are separated by a single space.


In our first example, we assume that device_namer outputs a number of parts, each one to form a symbolic link (alternative name) for the device in question.

KERNEL=="hda", PROGRAM="/bin/device_namer %k", SYMLINK+="%c"

The next example assumes that device_namer outputs two parts, the first being the device name, and the second being the name for an additional symbolic link. We now introduce the %c{N} substitution, which refers to part N of the output:

KERNEL=="hda", PROGRAM="/bin/device_namer %k", NAME="%c{1}", SYMLINK+="%c{2}"

The next example assumes that device_namer outputs one part for the device name, followed by any number of parts which will form additional symbolic links. We now introduce the %c{N+} substitution, which evaluates to part N, N+1, N+2... until the end of the output.

KERNEL=="hda", PROGRAM="/bin/device_namer %k", NAME="%c{1}", SYMLINK+="%c{2+}"

Output parts can be used in any assignment key, not only NAME and SYMLINK. The example below uses a fictional program to determine the Unix group which should own the device:

KERNEL=="hda", PROGRAM="/bin/who_owns_device %k", GROUP="%c"

Running external programs upon certain events:

Yet another reason for writing udev rules is to run a particular program when a device is connected or disconnected. For example, we might want to execute a script to automatically download all of our photos from our digital camera when it is connected or we want to trigger a backup from the local HDD to the external HDD as soon as we plug the external HDD to the computer. I will provide an example for the latter case – see

Do not confuse this with the PROGRAM functionality described above. PROGRAM is used for running programs which produce device names (and they should not do anything other than that). When those programs are being executed, the device node has not yet been created, so acting upon the device in any way is not possible.

The functionality introduced here allows you to run a program after the device node is put in place. This program can act on the device, however it must not run for any extended period of time, because udev is effectively paused while these programs are running. One workaround for this limitation is to make sure your program immediately detaches itself.

Here is an example rule which demonstrates the use of the RUN list assignment:

KERNEL=="sdb", RUN+="/usr/bin/my_program"

When /usr/bin/my_program is executed, various parts of the udev environment are available as environment variables, including key values such as SUBSYSTEM. We can also use the ACTION environment variable to detect whether the device is being connected or disconnected — ACTION will be either a add or remove respectively.

Environment interaction:

Udev provides an ENV key for environment variables which can be used for both matching and assignment. In the assignment case, we can set environment variables which we can then match against later. We can also set environment variables which can be used by any external programs invoked using the techniques mentioned above. A fictional example rule which sets an environment variable is shown below.

KERNEL=="fd0", SYMLINK+="floppy", ENV{some_var}="value"

In the matching case, we can ensure that rules only run depending on the value of an environment variable. Note that the environment that udev sees will not be the same user environment as one gets on the console. A fictional rule involving an environment match is shown below.

KERNEL=="fd0", ENV{an_env_var}=="yes", SYMLINK+="floppy"

The above rule only creates the /dev/floppy link if $an_env_var is set to yes in udev's environment.

Additional options:

Another assignment which can prove useful is the OPTIONS list. A few options are available:

last_rule stops further rules application. No later rules will have any effect. ignore_device will ignore this event completely. ignore_remove will ignore any later remove event for this device. This may be useful as a workaround for broken device drivers. all_partitions will create the device nodes for all available partitions of a block device. This may be useful for removable media devices where media changes are not detected.

For example, the rule below sets the group ownership on my hard disk node, and ensures that no later rule can have any effect:

KERNEL=="sda", GROUP="disk", OPTIONS+="last_rule"

Testing and Debugging

This section provides some information useful while writing udev rules since it is about some tools to debug rules and look behind the curtain what is going on in order to react to possible issues.

udevtrigger:

Assuming we are on a recent kernel with inotify5 support, udev will automatically monitor our rules directory and automatically pick up any modifications we make to the rule files.

Despite this, udev will not automatically reprocess all devices and attempt to apply the new rule(s). For example, if we write a rule to add an extra symbolic link for our camera while the camera is plugged, we cannot expect the extra symbolic link to show up right away. To make the symbolic link show up, we can either disconnect and reconnect our camera, or alternatively in the case of non-removable devices, we can run udevtrigger – see man 8 udevtrigger.

If your kernel does not have inotify support, new rules will not be detected automatically. In this situation, you must run udevcontrol reload_rules after making any rule file modifications for those modifications to take effect.

udevtest:

If we know the top-level device path in sysfs, we can use udevtest to show the actions which udev would take. This may help us debug our rules. For example, assuming we want to debug a rule which acts on /sys/block/sdb (my external USB HDD):

pc1:/home/sa# udevtest /block/sdb
This program is for debugging only, it does not create any node,
or run any program specified by a RUN key. It may show incorrect results,
if rules match against subsystem specfic kernel event variables.

main: looking at device '/block/sdb' from subsystem 'block'
run_program: 'usb_id -x'
run_program: '/lib/udev/usb_id' (stdout) 'ID_VENDOR=Maxtor_6'
run_program: '/lib/udev/usb_id' (stdout) 'ID_MODEL=B250R0'
run_program: '/lib/udev/usb_id' (stdout) 'ID_REVISION=0000'
run_program: '/lib/udev/usb_id' (stdout) 'ID_SERIAL=Maxtor_6_B250R0_DEF107679C83'
run_program: '/lib/udev/usb_id' (stdout) 'ID_TYPE=disk'
run_program: '/lib/udev/usb_id' (stdout) 'ID_BUS=usb'
run_program: '/lib/udev/usb_id' returned with status 0
run_program: 'edd_id --export /dev/.tmp-8-16'
run_program: '/lib/udev/edd_id' (stderr) 'no kernel EDD support'
run_program: '/lib/udev/edd_id' returned with status 2
run_program: 'path_id /block/sdb'
run_program: '/lib/udev/path_id' (stdout) 'ID_PATH=pci-0000:02:05.2-usb-0:2:1.0-scsi-0:0:0:0'
run_program: '/lib/udev/path_id' returned with status 0
udev_rules_get_name: add symlink 'disk/by-id/usb-Maxtor_6_B250R0_DEF107679C83'
udev_rules_get_name: add symlink 'disk/by-path/pci-0000:02:05.2-usb-0:2:1.0-scsi-0:0:0:0'
run_program: 'vol_id --export /dev/.tmp-8-16'
run_program: '/lib/udev/vol_id' (stderr) '/dev/.tmp-8-16: unknown volume type'
run_program: '/lib/udev/vol_id' returned with status 4
udev_rules_get_name: no node name set, will use kernel name 'sdb'
udev_device_event: device '/block/sdb' already in database, validate currently present symlinks
udev_node_add: creating device node '/dev/sdb', major = '8', minor = '16', mode = '0660', uid = '0', gid = '6'
udev_node_add: creating symlink '/dev/disk/by-id/usb-Maxtor_6_B250R0_DEF107679C83' to '../../sdb'
udev_node_add: creating symlink '/dev/disk/by-path/pci-0000:02:05.2-usb-0:2:1.0-scsi-0:0:0:0' to '../../sdb'
main: run: 'socket:/org/kernel/udev/monitor'
main: run: '/usr/share/usbmount/usbmount add'
main: run: 'socket:/org/freedesktop/hal/udev_event'
pc1:/home/sa#

Note the /sys prefix was removed from the udevtest command line argument, this is because udevtest operates on device paths. Also note that udevtest is purely a testing/debugging tool, it does not create any device nodes, despite what the output suggests!

1. The technology we have in place today (early 21st century) always moves around data over different kinds of networks in different ways. The computation never moves around but stays where it is (could be our iPod, notebook or cell – computing happens locally). There have been and there are proposals to move around computations as well in cases where it makes sense. Nobody really picked that idea up so far...

2. The kernel interface used to exchange information between udev and the Linux Kernel.

3. For example, plug or unplugging a device to a computer creates an event.

4. Putting OPTIONS="last_rule" in a udev rule does that job.

5. I also strongly recommend using the powers of inotify-tools instead of being a goof and playing the cron job card on even scenarios where some inotify based approach should be used (running a cron job every 30 seconds to figure if mldonkey has finished downloading some stuff is... well, not so smart — and yes, folks do such things a lot).

Creative Commons License
The content of this site is licensed under Creative Commons Attribution-Share Alike 3.0 License.