Linux and udev – Running a script on flash drive insertion

I was tasked with moving some data from one, internet-connected network to another network that does not have internet access.

This was to be done via Sneakernet, possibly just using scripts stored on the USB flash drive.

At first, it was quite an involved process. The more I used the process though I kept thinking about simplifying the process. What if I could just plug the flash drive into a computer and the transfer happens automatically?

Welcome to the weird and wonderful (and sometimes frustrating) world of udev.

Simply put, udev is the piece of software that handles device management for the Linux kernel. Whenever you plug in a printer, udev manages that. A camera? udev handles that too.

udev has a system in place where it allows you to write rules for devices that get plugged into the system that overrides the default rules.

For example, default rules for flash drives try to mount all the partitions on that flash drive.

What I needed was for udev to detect a specific flash drive being inserted and then run a script that transfers files from a specific location to a directory on that flash drive and finally, unmount it.

Sounds easy, right?

Turns out it isn’t.

First off, I had to target the flash drive. The specific flash drive.

When a device is plugged into a computer running Linux, udev can display what it sees about that device, including if any partitions have been mounted.

To see what udev sees, we need to run the following command:

sudo udevadm monitor --environment --udev

Now when you plug your flash drive in, you can see udev interacting with the device.

In the output, you will see value such as ID_VENDOR_ID and ID_MODEL_ID, which identifies the device you plugged in.

Now that we those fields, we need to write a rule that matches our device.

Rules are stored in: /etc/udev/rules.d

An example rule file would be named something like: 85-myflashdrive.rules

So, how do I target my specific drive? Here is my rule file:

SUBSYSTEMS=="usb", ACTION=="add", KERNEL=="sda2", ATTRS{serial}=="12063366994561B1"

Most of these attributes can be discovered using the udevadm monitor command. For most of it though, I used the –attribute-walk option of another command:

sudo udevadm info --name=/dev/sda2 --attribute-walk

This should give you more than enough attributes to choose from to uniquely identify the device.

Now that I have my rule targeting my flash drive, I need to make it run a script. Simply modify the .rules file to run a script:

SUBSYSTEMS=="usb", ACTION=="add", KERNEL=="sda1", ATTRS{serial}=="12060000000001B1", RUN+="/home/pi/"

Only, this didn’t work.

The rule matched multiple times, no matter how uniquely I tried to match my device. This in turn ran my script multiple times, which caused all sorts of problems for me.

I’ve found many people online with the same sort of problem, but no one’s solution worked for me. I tried lockfiles, I tried different targeting methods. Obviously, I’m doing something wrong, but I finally thought of a workaround. I modified my .rules file:

SUBSYSTEMS=="usb", ACTION=="add", KERNEL=="sda2", ATTRS{serial}=="12060000000001B1", RUN+="/home/pi/"

That script simply writes “inserted” to a text file.

Now I have another script running in my crontab that looks at that text file. Once it sees that the text file contains “inserted”, it copies the files over to the drive, unmounts the drive, and then writes “removed” to the text file.

Crude, I know. But it solved the problem with the script running multiple times. My process works flawlessly. The script running on crontab also has a lockfile, so even if the process is not finished by the time the crontab runs my script again, the lockfile tells my script that it’s busy and it should try again in a minute.

While researching for this task, I came upon a whole lot of other resources that were a great amount of help.

This /dev/blog post was a good starting point for finding out more about specific devices.

Also, the biggest resource for writing rules was this page.

Hopefully, I’ve shed some light on the intricacies of udev, even though I will probably never grok it completely.