diff --git a/Deconstructing_Linux_udev_Rules.rst b/Deconstructing_Linux_udev_Rules.rst index 8ffdcff..9000c7d 100644 --- a/Deconstructing_Linux_udev_Rules.rst +++ b/Deconstructing_Linux_udev_Rules.rst @@ -106,54 +106,228 @@ 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*"`` + - ``ACTION=="add", 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. + ``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. - Why are we doing this? + 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=="1ATA_VBOX_HARDDISK_VB5f712327-2bb4be0c"`` - - ``SYMLINK+="my_fine-disk01"`` - - ``OWNER:="3009", GROUP:="421", MODE:="0600,`` + - ``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: + 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 :) - - ``KERNEL=="raw1"`` - - ``SYMLINK+="rmy_fine-disk01"`` + 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`` @@ -164,9 +338,14 @@ 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 @@ -189,6 +368,10 @@ 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 ====== @@ -201,10 +384,10 @@ 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``.