*** empty log message ***
1 parent edc89fe commit b0516f529cd339de8575657db9ff1f30710358f4
@tundra tundra authored on 31 Oct 2013
Showing 1 changed file
View
440
Deconstructing_Linux_udev_Rules.rst
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",
ACTION=="add", KERNEL=="sd*", PROGRAM=="/sbin/scsi_id --whitelisted /dev/$name",
RESULT=="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"
ACTION=="add", KERNEL=="raw1", SYMLINK+="rmy_fine_disk01", OWNER:="3009", GROUP:="421", MODE:="0600"
 
 
What Does All This Mean?
========================
 
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.)
 
key-value pairs are separated by commas. The value (right side) is
double quoted. Pay attention to the operators because they too
mean something. For example, ``=`` (assignment) is not the same thing as ``==``
(checking for equality).
 
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
every time 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 about ``sda``, ``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,``
- ``ACTION=="add", KERNEL=="sd*"``
 
``KERNEL=="sd*" matches any time the kernel emits a message with
the string ``sd`` followed by anything. For example, the kernel
sending messages about ``sda``, ``sdb``, ``sdc`` and so on would
all match. But we only want to tune in to these messages when a
drive is being *added*. We want to ignore other kernel messages
with ``sd*`` in them when drives are removed or reporting an error.
So, we also include the ``ACTION="add"`` key value pair.
 
Effeectively, this lets us look at every drive being added to the
system, so we can spot the one we're looking for.
 
- ``PROGRAM=="/sbin/scsi_id --whitelisted /dev/$name"``,
``RESULT=="VBOX_HARDDISK_VB5f712327-2bb4be0c"``
 
This is an assignment or "action" key-value pair. Each time the
matches above are true, the ``scsi_id`` program is then run to do
further checking. ``$name`` in this case is the exact string the
``KERNEL`` matching triggered on. So, if it was ``hdx``, then the
command here would be::
scsi_id --whitelisted /dev/hdx
 
If ``scsi_id`` returns a string that matches ``VBOX_HARDDISK....``,
then the ``RESULT`` key-value match is also true. In other words,
we're looking for a drive that's being added *that has a
specific unique ID*. (On SAN-connected systems, this is called
the drive's "World Wide ID" or just ``wwid``.)
 
 
.. NOTE:: When you have multiple match key-value pairs in a
``udev`` rule, *all* of them have to be true for the
rule to be invoked. In order for our rule to be
applied, the kernel has to report that something called
``sd*`` has been seen AND it's being *added* AND it's ID
is the string ``VBOX....``. If ANY of these conditions
are not met, the rule is not applied.
 
Unfortunately, the command to use to get the unique ID of a drive
varies by distro. On ``CentOS`` and ``Redhat`` the command is
``scsi_id``. On ``Ubuntu`` and ``Mint`` it's ``scsidev -s`` (you
may have to install the ``scsitools`` package to get this
utility). Other distros may have other ways of doing this.
 
.. WARNING:: If you're running ``Linux`` as a guest under
``VMWare``, beware! By default, recent versions of
``VMWare ESX, Workstation,`` and ``Player`` do *not*
enable unique disk identification like physical
machines (and ``Oracle VirtualBox``) do. You can
read all about it here:
 
http://www.dizwell.com/tag/scsi_id/
 
Basically, you have to set ``disk.EnableUUID = "TRUE"``
in your virtual machine's ``.vmx`` file.
 
Why are we bothering with all this? When disks are added and/or
removed from a system, *they are NOT guaranteed to be assigned to
the same device node in* ``/dev/``. Your drive could show up as
``/dev/sdh`` one time and ``/dev/sdx`` the next. This is the
price we pay for having a dynamic device system that allows hot
swapping USB devices on your laptop or live presentation of SAN
storage to a server. We thus have to use something that
*uniquely* identifies the drive every time.
 
If we got this far, it means that all our matching tests were
successful: we've found the drive we're looking for. Now we can do
what we set out to do in the first place:
 
- ``SYMLINK+="my_fine_disk01"``
 
This creates a symbolic link to whatever device mountpoint ended
up being assigned to our disk by the operating system. In other
words, if the mount point changed from ``/dev/sdd`` to
``/dev/sdl`` after a reboot, our rule would figure it out and
point the link named ``my_fine_disk01`` at that mountpoint.
 
Notice the use of the ``+=`` operator here instead of
the more usual ``=``. The use of the ``+=`` operator
means, *"Add another symbolic link to this mountpoint."*
 
The real purpose of this is to provide a *consistent device name*
for software to use when referencing this disk. Say I'm writing
an application. I don't have to know where the disk is mounted.
I just have to always refer to ``/dev/my_fine_disk01`` and let all
this ``udev`` magic do the hard stuff. Remember, it's our job as
Snotty Systems Engineers to relieve applications programmers of as
much thinking as possible. Really, it is. Look in the job
description.
 
.. NOTE:: Why use a symlink? Why not just rename the mountpoint.
It *is* possible to do this with ``udev`` with the
``NAME+=...`` key-value construct. I prefer not to do
this because you lose visibility into the underlying
device name when you do this.
 
It's handy to know that the actual device name is, say,
``sdk``. For example, ejecting SAN-attached storage
requires you to sent things to
``/sys/block/sdk/device/delete``. If you overwrite
``/dev/sdk`` with ``my_fine_disk01``, it's not
immediately clear what the underlying device actually
is. A symbolic link covers both bases.
 
The only reason to not use a symlink and to actually
rename the node is if you happen to have software that
does not properly deal with symlinks. People that write
such awful software are called "n00bs", "sloppy", "bozos"
or, possibly just, "programmers at Computer Associates".
 
- ``OWNER:="3009", GROUP:="421", MODE:="0600``
 
These key-values pairs change the owner, group, and
permissions on whatever mountpoint our drive ended up
on. That way, we're assured that, no matter where the
drive gets mounted, it will have ownership and permissions
we expect.
 
Here the use of the ``+=`` operator means something different. It
means, *"I am the final rule in this matter. No subsequent rule
can change this setting."* That's how we prevent rules that are
read after us (ones with higher numbers in their name) from
overriding what we want.
 
One other thing here: Notice the use of numeric values for ``UID``
and ``GID``. You *could* use the actual user- and group names
here. In fact, most ``udev`` tutorials show it this way. It is a
*bad idea*, especially in large, high complexity datacenters.
When a machine boots that uses remote authentication like
``ldap``, you cannot guarantee you'll have access to the
authentication server at the time ``udev`` wants to set these
ownership and permissions values. This can happen when you have
slow, crufty networks that take a long time to nail up a
connection between a server and its ``ldap`` authority. The
numeric values are always right (unless some genius is in the
habit of changing them often in ``ldap``). And you'll see the
proper user- and group names on your mountpoint when ``ldap``
connectivity is established. If you want to know what the numeric
values for user- and group are, do this::
 
id username
 
 
- ``RUN=="/bin/raw /dev/raw/raw1 /dev/$name"``
 
Now, let's look at the second rule:
 
- ``KERNEL=="raw1"``
- ``SYMLINK+="rmy_fine-disk01"``
Now we're ready to create a raw character device associated with
our matching drive. If you don't know what this is, you probably
don't need it. If you ever work with database servers, you'll
find out soon enough :)
 
Basically, the command above magically creates a raw character
device of ``/dev/raw/raw1`` associated with /dev/sd... (our
mountpoint).
 
"But why", you may ask, "are you using the ``RUN==`` construct?
Isn't that what ``PROGRAM==`` does?" Not exactly, Grasshopper.
``PROGRAM==`` *always* runs regardless of prior matching.
``RUN==`` *only* runs if all prior matching has been succesful.
 
Why is that important here? Say we boot the system, and the
kernel discovers drives ``/dev/sdh, /dev/sdi,`` and ``/dev/sdj``
and let's suppose that the first one has the matching ``wwid``.
With ``RUN==`` the raw character device will only be created when
the full set of matching occurs - i.e., When the kernel reports
the addition of ``/dev/sdh``. But if you use ``PROGRAM==``, the
raw device will be associated *every time the kernel reports a new
``/dev/sd*``. The last one to be reported will "win". In this
case, that means ``/dev/raw/raw1`` will be associated with
``/dev/sdj`` - not what we want here. Do NOT email me asking how
I figured this out. It wasn't fun.
 
 
With that under our belts, the second rule should be pretty
simple to understand:
 
- ``ACTION=="add", KERNEL=="raw1"``
 
We want to match any time the kernel reports something
called ``raw1`` being added to the system. Oh, wait,
we just did that at the end of the previous rule.
 
- ``SYMLINK+="rmy_fine_disk01"``
 
Let's symlink ``/dev/raw/raw1`` to ``/dev/rmy_fine_dis01``.
 
 
- ``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``.
would be a matter of changing the unique ID for the ``RESULT`` field
of the first rule. You'd also have to change any references to
``my_fine_disk01`` and ``raw1``.
 
If you want to know the current state of what raw devices exist
do this::
 
raw -qa
 
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.
substituting for ``/block/sdd``::
 
udevadm test /block/sdd 2>&1| less
 
Most of what is described above applies analagously for non-disk
devices like cameras and scanners. The principles are pretty
much the same.
 
 
Author
======
 
 
Document Revision Information
=============================
 
``$Id: Deconstructing_Linux_udev_Rules.rst,v 1.109 2013/11/01 00:08:30 tundra Exp $``
``$Id: Deconstructing_Linux_udev_Rules.rst,v 1.110 2013/11/01 01:56:06 tundra Exp $``
 
You can find the latest version of this document at:
 
http://www.tundraware.com/TechnicalNotes/Deconstructing-Linux-udev-Rules
 
 
This document produced with ``emacs`` and ``RestructuredText``.