udev
Rulesudev
is one of those pieces of Linux
that is fairly well
documented and not very well understood. This note isn't intended as
a general introduction to writing udev
rules, but, rather, a brief
introduction to the topic by way of specific example.
Note
The examples and descriptions below assume you
are running as root
. Most of the commands
described will either return nothing or will not
work at all unless you are root
.
udev
?There are many clever uses for udev
documented on the Web, but
the most common use is to ensure that when you connect a device -
disk, tape, usb thumbdrive, camera... whatever - to a Linux
system, that device shows up with the same name every time.
Original Unix
derivatives had a static tree of devices the system
could support. This was encoded in the /dev
file tree hierarchy.
This was pretty inflexible in the face of devices being added- and
removed from the system as it ran. For this reason, modern device
handling in Linux
and most other Unix
derivatives is
dynamic - the content of /dev
changes to reflect the actual
state of the system as things get connected or disconnected. (Exactly
how this is done is outside the purpose of this document, but if you
care, investigate how the Linux /sys
filesystem works.)
While the example below is "cooked", it is very much rooted in real
world udev
applications. We want to do the following things:
- Identify a specific disk no matter what name it was assigned name under
/dev
.- Create a symbolic link to that disk so that - no matter what it's name under
/dev/
might be at the moment - the symbolic link is always the same.- Change the user and group ownership of that disk to something other than the default (
root:disk
).- Set specific permissions for the disk.
- Create a corresponding "raw" character device under
/dev/raw
associated with our disk above.
udev
Rules Live?User created rules - well, created by root
, actually - are found in
/dev/udev/rules.d
. If you look there, you'll see that the files
there begin with numbers like 50
or 60
. udev
reads rules
in lexical order. That means it reads the 50...
file before the
60...
file before the 70...
file and so on. This is important
because you have to be careful to insert your rule in early enough in
the lexical order so that it can override any subsequent defaults.
Unfortunately, because of the way udev
works, rules read later
in the lexical order can also override earlier rules if we're not careful.
We'll see an example of this below, and how to fix it.
In our case, we'll create our rules in the file
15-ExampleRules.rules
which should pretty much guarantee that our
rules will be the first ones read.
udev
Read RulesWhen udev
first starts, or any time it is informed that rules have
been changed, it first reads a set of system-wide default rules in
/lib/udev/rules.d/
. Then it reads the rules in
/etc/udev/rules.d
. If you name your own rule file the same as one
of system-wide rules, yours will take precedence. There is also a way
to install "temporary" rules, but the location for such rules is
distro-specific.
Ordinarily, the running udev
daemon is automatically informed
that a rule file has changed and it will reread them all again
when this happens. You can also force a rule reload with:
udevadm control --reload-rules
Another way to do this is to restart the udev
daemon or reboot to
get the latest rules read in. Note that the daemon restart procedure
is also distro-specific, so you'll have to figure out what works on
your system.
We need two rules to achieve our goals above. Notice that the first
rule below is broken across multiple lines to make it more readable,
but it is all on one line in the actual rules file. It is possible to
break rules across lines but you have to ensure that you follow the
syntax that udev
expects. To keep things from mysteriously
breaking, I typically put the entire rule one one line:
KERNEL=="sd*", PROGRAM=="/sbin/scsi_id --whitelisted /dev/$name",
RESULT=="1ATA_VBOX_HARDDISK_VB5f712327-2bb4be0c", SYMLINK+="my_fine-disk01",
OWNER:="3009", GROUP:="421", MODE:="0600",
RUN=="/bin/raw /dev/raw/raw1 /dev/$name"
KERNEL=="raw1", SYMLINK+="rmy_fine-disk01", OWNER:="3009", GROUP:="421", MODE:="0600"
Rules are made up of key-value pairs separated by an operator. These
key-value pairs are separated by commas. Key-value pairs either
match or assign. Match key-value pairs check to see if a
particular thing "matches" what we're looking for. Think of them as
if
statements in a programming language. Assignment key-value
statements take some sort of action * usually on the thing that was
previously matched*. But, you're not restricted to this. It's
entirely possible to write a rule that operates on something
completely unrelated to the matched condition. For instance, you
could write a rule that says, reboot the computer everytime my little
brother plugs in his favorite thumbdrive. (This is, however,
considered very bad manners and may get you sent to your room without
dinner.)
Let's take each rule apart, one key-value pair at a time:
KERNEL=="sd*"
This matches any time the kernel emits a message with the string
sd
followed by anything. For example, the kernel sending messages aboutsda
,sdb
,sdc
and so on would all match.Why are we doing this?
PROGRAM=="/sbin/scsi_id --whitelisted /dev/$name"
RESULT=="1ATA_VBOX_HARDDISK_VB5f712327-2bb4be0c"
SYMLINK+="my_fine-disk01"
OWNER:="3009", GROUP:="421", MODE:="0600,
RUN=="/bin/raw /dev/raw/raw1 /dev/$name"
Now, let's look at the second rule:
KERNEL=="raw1"
SYMLINK+="rmy_fine-disk01"
OWNER:="3009", GROUP:="421", MODE:="0600
Obviously, you'd have to have another pair of rules for each
additional disk you want to manage this way. Adding another disk
would be a matter of using scsi_id
to get its wwid
for the
RESULT
field of the first rule. You'd also have to change any
references to my_fine_disk01
and raw1
.
For reasons that are not entirely clear (to me anyway), the raw
command only knows how to create raw devices whose names begin with
raw
, go figure.
Another way to get a unique ID for a device is to tail your system log
(tail -f /var/log/messages
or tail -f /var/log/syslog
) and
watch what happens when you plug your device into, say, a USB port.
If you want to know all the attributes udev
knows about a particular
device, use this, substituting your device for /dev/sdd
udevadm info --query=all --name /dev/sdd 2>&1| less
The output of this command can be helful in figuring out just which attributes and values you need to get to a running rule.
Finally, you can test your rules to see what is matching, again
substituting for /block/sdd
:
udevadm test /block/sdd 2>&1| less
Tim Daneliuk - tundra@tundraware.com
Comments, corrections, clarifications, and/or improvements welcome!
$Id: Deconstructing_Linux_udev_Rules.rst,v 1.109 2013/11/01 00:08:30 tundra Exp $
You can find the latest version of this document at:
http://www.tundraware.com/TechnicalNotes/Deconstructing-Linux-udev-Rules