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