# Using Udev for YourSuccess
by Seth Kenlon

**Udev** is the sub-system in Linux that supplies your computer with device
events. In plain English, that meas it's the code that detects when you have things plugged into your computer, like a network card, external hard drives (including USB thumbdrives), mousen,
keyboards, joysticks and gamepads, DVD-ROM drives, and so on. That makes it a potentially useful
utility, and it's well-enough exposed to a stadard user such that you can manually 
script it to, for instance, perform certain tasks when a certain hard drive is plugged in.

This article teaches you how to create a udev script triggered by some udev event, such as
plugging in a specific thumbdrive. Once you understand the process for working with udev, you can use it to do all manner of things, like loading a specific driver when a gamepad is attached, or performing an automatic backup when you backup drive is attached.

## A basic script

I find that the best way to work with udev is in small chunks. Don't
write the entire script up front, but instead start with something
that simply confirms that udev does indeed trigger some custom event.

Depending on the ultimate goal of your script, you won't be able to guarantee that you will ever
see the results of a script with your own eyes, so make
your script log that it was successfully triggered. The usual place for log files is in the **/var** directory, but that's mostly the root user's domain, so for testing, use **/tmp**, which is accessible by normal users and also usually gets cleaned out every so often.

Open your favourite text editor and enter this simple script:

    #!/usr/bin/bash

    /usr/bin/date >> /tmp/udev.log

Place this in **/usr/local/bin** or some such place in the default executable path. Call it **trigger.sh**
and, of course, make it executable with **chmod +x**:

    $ sudo mv trigger.sh /usr/local/bin
    $ sudo chmod +x /usr/local/bin/trigger.sh

This script has nothing to do with udev. When this script is executed, this script places a timestamp in the file **/tmp/udev.log**. Test the script yourself:

    $ /usr/local/bin/trigger.sh
    $ cat /tmp/udev.log
    Tue Oct 31 01:05:28 NZDT 2035

The next step is to make udev, rather than yourself, trigger the script.


## Unique Device Identification

In order for your script to be triggered by a device event, udev
must know under what conditions it should call the script. In real
life, you can identify a thumbdrive by its colour, the manufacturer, and the fact that you just plugged it into your computer.
Your computer, however, obviously needs a different set of criteria.

Udev identifies devices by serial numbers, manufacturers, and even 
vendor ID and product ID numbers. Since this is early in the life span
of your udev script, be as broad, non-specific, and
all-inclusive as possible. In other words, you want first to catch nearly
any valid udev event to trigger your script.

With the **udevadm monitor** command, you can tap into udev in real-time and
see what it sees when you plug in different devices. Become root, and
try it.

    $ su
    # udevadm monitor

The **monitor** function prints received events for:

* UDEV: the event which udev sends out after rule processing
* KERNEL: the kernel uevent

With **udevadm monitor** running, plug in a thumbdrive and watch as all
kinds of information is spewed out onto your screen. Notice,
particularly, that the type of event is an **ADD** event. That's a good way
of identifying what type of event you want.

The **udevadm monitor** command provides a lot of good info, but you can see it with
prettier formatting with the command **udevadm info**, assuming you know
where your thumbdrive is currently located in your **/dev** tree. If not,
unplug and then plug your thumbdrive back in and then immediately issue
this command:

    $ su -c 'dmesg | tail | fgrep -i sd*'

Assuming that command returned **sdb: sdb1**, for instance, then your
thumbdrive is being assigned the **sdb** label by the kernel.

Alternately, you can use the **lsblk** command to see all drives, including sizes and partitions, attached to your system.

Now that you have established where your drive is currently located in your file system, you can view udev
information about that device:

    # udevadm info -a -n /dev/sdb | less


This returns a lot of information. Focus on the first block of info for now.

Your job is to pick out parts of udev's report about a device that are most unique to that device, and then tell udev to trigger your script when those unique attributes are detected.

What's happening on a technical level is that the **udevadm info** process reports on a device (specified by the device path), and then
"walks" up the chain of parent devices. For every device found, it prints all possible attributes, using a key-value format. You can compose a rule to match according to the attributes of a device plus attributes from one single parent device.

    looking at device '/devices/000:000/blah/blah//block/sdb':
      KERNEL=="sdb"
      SUBSYSTEM=="block"
      DRIVER==""
      ATTR{ro}=="0" 
      ATTR{size}=="125722368"
      ATTR{stat}==" 2765 1537 5393"
      ATTR{range}=="16"
      ATTR{discard\_alignment}=="0"
      ATTR{removable}=="1"
      ATTR{blah}=="blah"

A udev rule must contain one attribute from one single parent device.

Parent attributes are things that describe a device from the most basic level, such as *it's something that has been plugged into a physical port* or *it is something with a size* or *this is a removable device*.

Since the KERNEL label of **sdb** can change depending upon how many
other drives you happen to have plugged in before you plug that
thumbdrive in, that's not the optimal parent attribute for a udev
rule. However, it works for a proof of concept, so you could use
it. An even better candidate is the SUBSYSTEM attribute, which
identifies that this is a "block" system device (which is why the **lsblk** command lists the device).

Open a file called **80-local.rules** in **/etc/udev/rules.d** and enter this code:

    SUBSYSTEM=="block", ACTION=="add", RUN+="/usr/local/bin/trigger.sh"

Save the file, unplug your test thumbdrive, and then reboot.

Wait, *reboot* on a Linux machine?

Theoretically, you can just issue **udevadm control --reload**, which
should load all rules, but at this stage in the game, it's best to
eliminate all variables. Udev is complex enough without lying in bed
all night wondering if that rule didn't work because of a syntax
error, or if you just should have rebooted. So reboot regardless of
what your POSIX pride tells you.

When your system is back online, switch to a text console (with
**ctl-alt-F3** or similar) and plug your thumbdrive in. If you are running a
recent kernel, you will probably see a bunch of output in your console
when you plug the drive in. If you see an error message such as **Could
not execute /usr/local/bin/trigger.sh**, then you probably forgot to make the
script executable. Otherwise, hopefully all you see is that a
device was plugged in and that it got some kind of kernel device
assignment, and so on.

Now, the moment of truth:

    $ cat /tmp/udev.log
    Tue Oct 31 01:35:28 NZDT 2035

If you see a very recent date and time returned from **/tmp/udev.log**, then the
udev has successfully triggered your script.


## Refining the Rule into Something Useful

The problem with the rule right now is that it's very generic.
Plugging in a mouse, a thumbdrive, or someone else's thumbdrive will all
indiscriminately trigger your script. Now is the time to start focusing
in on the exact thumbdrive you want to trigger your script.

One way to do this is with the vendor ID and product ID. To get
these numbers, you can use the lsusb command.

    $ lsusb
    Bus 001 Device 002: ID 8087:0024 Slacker Corp. Hub
    Bus 002 Device 002: ID 8087:0024 Slacker Corp. Hub 
    Bus 003 Device 005: ID 03f0:3307 TyCoon Corp. 
    Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 hub
    Bus 001 Device 003: ID 13d3:5165 SBo Networks

In this example, the **03f0:3307** before **TyCoon Corp.** denotes
the idVendor and idProduct attributes. You can also see these numbers in the output of
**udevadm info -a -n /dev/sdb | grep vendor**, but I find the output of **lsusb**
a little easier on the eyes.

You can now include these attributes in your rule.

    SUBSYSTEM=="block", ATTRS{idVendor}=="03f0", ACTION=="add", RUN+="/usr/local/bin/thumb.sh"

Test this (yes, you should still reboot, just to make sure you're
getting fresh reactions from udev), and it should work the same as
before, only now if you plug in, say, a thumbdrive manufactured by a
different company (therefore with a different idVendor) or a mouse or
a printer, the script is not triggered.

Keep adding in new attributes to further focus in on that *one* unique
thumbdrive that you actually want to have trigger your script. Using **udevadm info -a -n /dev/sdb**, you can find out things like the vendor name, or
sometimes a serial number, or the product name, and so on.

For your own sanity, be sure to add only one new attribute at a time.
Most mistakes I have made and have seen other people online make is to
throw a bunch of attributes into their udev rule and wonder why the
thing no longer works. Testing attributes one by one is the safest way to ensure udev can identify your device successfully.

## Security

This brings up the security concerns of writing **udev** rules to
automatically do something when a drive is plugged in. On my machines,
I don't even have auto-mount turned on, and yet this article proposed
scripts and rules that execute commands just by having something
plugged in.

Two things to bear in mind here.

1.  Focus your udev rules once you have them working so that they only
    trigger scripts when you really want them to. Executing a script
    that blindly copies data to or from your computer is a bad idea if
    anyone who happens to be carrying a thumbdrive of the same brand as
    yours comes along and plugs it into your box.

2.  Do not write your udev rule and scripts and then forget about them.
    I know which computers have my udev rules on them, and those boxen
    are much more my personal computers than the ones that I take around
    to conferences or have in my office at work. The more "social" a
    computer is, the less likely it is to get a udev rule on it that
    could potentially result in my data ending up on someone else's
    device or someone else's data or malware on *my* device.

In other words, as with so much of the power that a GNU system provides
you, it is your job to be mindful of how you are wielding that power. If
you abuse it, or fail to treat it with respect, then it very well could
go horribly wrong. 


## Udev in the real world

Now that you can confirm that our script is triggered by udev, 
you can turn your attention to the function of the script. Right now, it is
useless, doing nothing more than logging the fact that it has been
executed.

I use udev to trigger [automated backups](https://gitlab.com/slackermedia/attachup) of my thumbdrives. The idea 
is that the master copies of my active documents are on my thumbdrive
(since it goes everywhere I go and could be worked on at any moment), and
those master documents get backed up to my computer each time I plug the 
drive into that machine. In other words, my computer is the backup 
drive and my production data is mobile.

Since that's what I use udev for the most, it's the example I'll use
here, but **udev** can grab lots of other things, like gamepads (this is useful on systems that aren't set to load the **xboxdrv** module when a gamepad is attached) and cameras 
and microphones (useful to set inputs when a specific mic is attached), so don't think that this one example is all it's good for.

A simple version of my backup system is a two-command process:

    SUBSYSTEM=="block", ATTRS{idVendor}=="03f0", ACTION=="add", SYMLINK+="safety%n"
    SUBSYSTEM=="block", ATTRS{idVendor}=="03f0", ACTION=="add", RUN+="/usr/local/bin/trigger.sh"

The first line detects my thumbdrive with the attributes already
discussed, and then assigns the
thumbdrive a symlink within the device tree. The symlink it assigns is
**safety%n**. The **%n** is a udev macro that resolves to *whatever number the
kernel gives to the device*, such as **sdb1**, **sdb2**, **sdb3**, and so on. So **%n**
would be the **1** or the **2** or the **3**.

This creates only a symlink in the dev tree, so it does not interfere
with the normal process of plugging in a device. This means that if you
do use a desktop environment that likes to auto-mount devices, you won't be causing problems for it.

The second line runs the script.

My backup script looks like this:

    #!/usr/bin/bash

    mount /dev/safety1 /mnt/hd
    sleep 2
    rsync -az /mnt/hd/ /home/seth/backups/ && umount /dev/safety1

The script uses the symlink, which avoids the possibility of **udev** naming
the drive something unexpected (for instance, if I have a thumbdrive called DISK
plugged into my computer already, and I plug in my other thumbdrive also
called DISK, the second one will be labelled DISK_, which would foil my
script). It mounts **safety1** (the first partition of the drive) at my
preferred mount point of **/mnt/hd**.

Once safely mounted, it uses [rsync](https://opensource.com/article/17/1/rsync-backup-linux) to backup the drive
to my backup folder (my actual script uses rdiff-backup, and yours can use whatever automated backup solution you prefer).

## Udev is Your Dev

Udev is a very flexible system, and enables you to define rules and functions in ways that few other systems dare provide users. Learn it and use it, and enjoy the power of POSIX.