Automatic backup when inserting a drive

I bought a 500 GB 2.5″ external disk drive to backup the data of my laptop. It is small, quiet, easy to move and far enough for the important data I want to backup, mostly documents, e-mails or script from work.

Being lazy, it happened that I did not backup my data. Yes, it is a shame, but inserting a drive and launching the commands to rsync the discs was preventing me from this best practice.

So, I decided to make it automatic. The goal was that the only thing I would have to do would be to insert the drive, and then remove it when it is done.

Thanks to the magic of Gnu/Linux, it had been very easy. I will show below how I did it, thought they are many things that could be improved (but I haven’t felt the need so far).

Udev

Udev not only allows to create /dev entries dynamically, but offers a lot of triggers to perfom all kind of actions when some hardware is inserted.

The udevinfo command will show you a lot of output concerning your drive. What we want is a unique way to differenciate the backup drive from any other drive that will be inserted in the future.

What would be better than the manufacturer serial ?

So let’s look for it :

$ udevinfo -a -p /sys/block/sdc | grep serial

*UPDATE 06/2011* It seems that on recent versions, the syntax of this command slightly changed into this :

$ udevadm info -a -p /sys/block/sdc | grep serial

Copy the serial.

Now we have to create a rule file, that will tell to udev what to do when this particular drive is inserted.

This is done in the /etc/udev/rules.d folder. Let’s create a file 30-mnt.rules or anything you like.

We edit this file so that it contains :

ACTION=="add",KERNEL=="sd*",SUBSYSTEMS=="usb", ATTRS{serial}=="57442D57584E3430394C5A38", RUN+="/home/jc/bin/backup/bckp-home.sh %k"

ACTION==”add” will tell udev that this action must be triggered when the drive is inserted.
SUBSYSTEMS could be changed according to the drive you are using (scsi, usb, …).
ATTRS{serial} must contain the serial you just grabbed.
RUN+=”/path/to/bin/backup.sh %k” tells udev to launch the backup script. %k, which contains the device name, sdc, is passed as an argument.

Optionally, it is quite convenient, you may want to make a symlink to the /dev/sd? device, with :

KERNEL=="sd*",SUBSYSTEMS=="scsi", ATTRS{model}=="GJ0250EAGSQ     ", SYMLINK+="ultrabay%n"

The shell script

Now, the script itself :

#!/bin/sh
LOGFILE=/PATH/TO/bckp.log
echo "--- BCKP - INFO : \$1=_${1}_" >> $LOGFILE
[[ $1 ]] || { echo "ERROR : missing parameter">>$LOGFILE; exit 1; }

# give time for the user, if needed to kill the process
sleep 6
MOUNT_PATH=$(grep $(echo $1) /etc/mtab | awk '{print $2}')
[[ $MOUNT_PATH ]] || {
  echo "ERROR fretching mount point">>$LOGFILE;
  exit 1;
}
echo " Synchronizing $MOUNT_PATH)">>$LOG

# add here all you rsync commands
rsync -av --delete /PATH/TO/DATA $MOUNT_PATH/backup/
...
exit 0

Testing it

Now, let’s reload udev :

$ sudo udevadm control --reload-rules

To test if it works :

$ sudo udevadm trigger

or maybe :

$ /etc/init.d/boot.udev restart

Plug off/in your drive, and the script should be executed as expected.

Optional : setting more options with Hal

It is not necessary at all for the backup script to work, but it would be very practical to haveĀ  a fixed mount point for a drive.
For instance, I use a second drive (in the untrabay slot of my thinkpad) that contains all my virtual machines.

The benefice is to prevent a performance drain of the system when many virtual machines are doing I/O like swapping or anything else.

Create a file like /etc/hal/fdi/policy/15-static-mount.fdi, containing :

<?xml version="1.0" encoding="ISO-8859-1"?>
<deviceinfo version="0.2">
  <device>
    <match key="volume.uuid" string="aa0019ef-86e0-4011-b996-31ef3e7174c8">
      <merge key="volume.policy.should_mount" type="bool">true</merge>
      <merge key="volume.fstype" type="string">ext4</merge>
      <merge key="volume.policy.desired_mount_point" type="string">ultrabay</merge>
      <merge key="volume.label" type="string">Fuji</merge>
      <merge key="volume.policy.mount_option.noatime" type="bool">true</merge>
      <merge key="volume.policy.mount_option.acl" type="bool">true</merge>
    </match>
  </device>
</deviceinfo>

The drive is matched by it uuid. You can get the uuid of your disk with :

$ ls -la /dev/disk/by-uuid/

You can, if you want, set the volume label and specify several options of the file system.

However, the most interesting option is the “desired_mount_point” one which allow you to fix the mount point. In the example, the disk will always be mounted in

/media/ultrabay

, and not the system disk, or disk_1, etc.

Coming next !

That’s all for today folks. Let me know if there are some things not clear or that can be optimized.

Next time, we will see how to run the same script from Hal instead. We will also use Zenity to get a nice GUI prompt when the disk is inserted.