Since the adoption of Kernel 2.6, Linux has used the udev system to handle devices such as USB connected peripherals. If you want to change the behavior when you plug something into a USB port, this section is for you. As an example, we will use a USB thumb drive but these methods should translate to any device handled by udev. As a goal for this exercise we decided to create a symlink and execute a script when a specific thumb drive was loaded. The operating system we used for this exercise is Ubuntu 9.04 Jaunty Jackalope.
Before writing rules, we have to gather all the information needed to identify our device. We found the block device node that the drive was assigned to by plugging it in and checking /var/log/messages. We then pass that location (/dev/sdd1) to two commands that we run at the same time. Some distributions use the “udevinfo” command but with Ubuntu 9.04 the command has changed to “udevadm info”:
The output of this is pretty meaty. We need to find the top of the chain that provides the block node which is used for mounting removable storage (in our case, /dev/sdd1). Using this KERNEL as identification will ensure that our symlink points to a mountable block device and not some part of the USB controller. We are also looking for device specific identifiers that differentiate this particular thumbdrive from all others:
In writing a udev rule, any of these characteristics can be used as conditions for the rule’s execution. That being said, only properties from one parent of the device and from the device itself can be match. Trying to match values from more than one parent in the chain will be invalid and will not work.
Rule files are stored in the /etc/udev/rules.d/ directory. We got some advice from the README in that directory on how to name rule files:
Files should be named xx-descriptive-name.rules, the xx should be
chosen first according to the following sequence points:
< 60 most user rules; if you want to prevent an assignment being
overriden by default rules, use the := operator.
these cannot access persistent information such as that from
< 70 rules that run helpers such as vol_id to populate the udev db
< 90 rules that run other programs (often using information in the
>=90 rules that should run last
We plan to run a script with this rule so we gave it a name that started with a higher number than our other rules but lower than 90. We used the filename:
The first part of a udev rule is the matching keys. We will use the KERNEL entry from the very top of the chain as well as the idVendor, idProduct, and serial attributes from the device specific information. This will positively identify this particular thumb drive and ignore all others. The kernel argument uses a question mark as a wild card so that if our drive were mounted on a different node
(ie: sda1, sdb1, sdc1, etc.) it could still be identified.
Now that we have the keys necessary to identify the particular hardware we’re looking for we can add assignment arguments. In our case we added two. The first creates a symlink to this device inside of the /dev/ directory. The second executes a script in our home directory:
Here is the final rule assembled into one line:
We added this as the only line in our rule file and then restarted udev using these commands:
The Script (and the bug workaround)
We wanted to use the pop-up notification we covered a while back but couldn’t get it to work. After a bit of frustration we found out that the notify-send package has trouble putting notifications on a user’s screen when called from a script run by root. There is a workaround for this bug. We altered the script just a bit for our purposes and pasted it to a new file named: /usr/local/bin/alt-notify-send
We then created the script that the udev rule calls. This is placed in our home directory at /home/mike/notify-plugin.sh
The script can do just about anything we want it to. In this case it calls the notification workaround script passing two strings from the udev rule, a delay time, and an icon to display with the pop-up.
Order of events:
Now that everything’s in place, let’s take a look at what happens when our drive is plugged in.
- -USB drive is plugged into the computer
- -Udev checks the /etc/udev/rules.d/ directory and starts using the rule files in order
- -Udev gets to our file: 81-thumbdrive.rules and matches the “sd?1” kernel, idVendor, idProduct, and serial number of the thumbdrive.
- -If udev confirms a match on our four conditions, a symlink is created at /dev/hackaday and the /home/mike/notify-plugin.sh script is executed, passing it a message that includes the kernel information.
- -Our script executes creating a pop-up notification using the alt-notify-send workaround.
- -HAL takes over, automatically mounting our drive (this is part of Ubuntu’s removable storage handling and unrelated to our udev rule).
Here we see the symlink pointing to our block device and the pop-up notification:
Udev rules give you control over the hardware attached to your machine. If you are working on a USB connected project, these rules will allow you to set permissions for access, execute scripts when added or removed, and provide a persistent symlink for accessing this hardware without relying on the same node name each time the device is connected. We use a udev rule to allow avrdude to access our AVR Dragon programmer without root permission. In this case our rule sets read/write permissions for owner and group, then assigns the device to the “plugdev” group. As long as the user trying to run avrdude is a member of the plugdev group the program will be able to access the dragon. Here’s the rule:
We hope this helps clarify how the udev system works. Give it a try with your next project.