SME Server, udev, and automated backups

We recently decided to make our backup procedure easier while using SME Server. Rather than using the web interface to back up data to a laptop and take it home, we wanted a USB caddy that you plug into the server, leave for 10mins, then remove and take it offsite. The way to do this is with udev.

On a recent distribution, this is as simple as adding a file to /etc/udev/rules.d/ containing this snippet:

BUS=="usb", SYSFS{serial}=="0602141116530", \
KERNEL=="/dev/sd?1", NAME="backupusbdrive", \
SYMLINK+="usbdevices/backupusbdrive", RUN+="/usr/local/bin/"

Now setup fstab, add some lines like this to /usr/local/bin/, run chmod u+x /usr/local/bin/ and you're done:


mount /mnt/backup
# ensure the target directory exists
mkdir -p /mnt/backup/home
rsync -ar --delete-after /home/ /mnt/backup/home
umount /mnt/backupusbdrive

The SYSFS{serial} argument is obtained as described in, section sysfs.

Unfortunately, SME Server 7 is based on CentOS 4.3, which uses an older kernel and hence an older version of udev. This means that RUN isn't available to us, and a few syntax changes are in order (older versions used a single = instead of the == used in the current udev). The udev snippet becomes:

BUS="usb", SYSFS{serial}="0602141116530", \
KERNEL="/dev/sd?1", NAME="backupusbdrive", \

We need to use some other method to run the script - I chose to use a cron job that runs every minute. If the device /dev/backupusbdrive doesn't exist, the script just exits.

Put this snippet in /etc/crontab:

*/1 * * * * root /usr/local/bin/

The contents of the script are included at the end of this post, and should be copied and pasted into the file /usr/local/bin/ Don't forget to chmod u+x /usr/local/bin/

If you're on a CentOS box (or pretty much anything else running a 2.6.9 kernel) this is where you stop. However SME Server users need to perform a couple more steps...

SME Server uses a template system. If you don't make your changes in /etc the right way, the next time you change something in the server manager your changes will disappear, because the file is regenerated. Run these commands to set up your config correctly (change the udev fragment according to your hardware. note the use of the \ character before { and }):

mkdir -p /etc/e-smith/templates-custom/etc/udev/rules.d/11-backupusbdrive.rules/
echo '
BUS="usb", SYSFS\{serial\}="0602141116530", KERNEL="/dev/sd?1", NAME="backupusbdrive", SYMLINK="usbdevices/backupusbdrive"
' > /etc/e-smith/templates-custom/etc/udev/rules.d/11-backupusbdrive.rules/99backupusbdrive-fragment
/sbin/e-smith/expand-template /etc/udev/rules.d/11-backupusbdrive.rules

mkdir -p /etc/e-smith/templates-custom/etc/crontab/
echo '
*/1 * * * * root /root/
' > /etc/e-smith/templates-custom/etc/crontab/99backuptousbdrive
/sbin/e-smith/expand-template /etc/crontab

And lastly, here is the script that cron runs. This content should be pasted into the file /usr/local/bin/


# - a Cron script for automated USB drive backups
# Copyright (C) 2006 Glynn Tucker Consulting Engineers
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

# Script to run when the usb backup drive is plugged in. This script should probably reside in /root

# This variable should match the administrator's email address

# Ensure the device is plugged in
if [ ! -b /dev/backupusbdrive ]; then
        exit 1

# Ensure the script isn't already running
if [ -f /var/run/ ]; then
        PID="`cat /var/run/`"
        for i in `ps x | grep | sed 's/^ *\([0-9]*\).*/\1/g'`; do
                if [ "$i" == "$PID" ]; then
                        exit 2
        # Script isn't running but .pid file exists, the previous run mustn't have completed so continue
echo $$ > /var/run/ &&
mkdir -p /tmp/usb_backup_mount_point &&
mount -t auto /dev/backupusbdrive /tmp/usb_backup_mount_point &&

mkdir -p /tmp/usb_backup_mount_point/home/e-smith/files

# perform backup
echo "Performing backup..." &&
echo "=======================================================================================" &&
rsync -ar --delete-after /home/e-smith/files/ /tmp/usb_backup_mount_point/home/e-smith/files 2>&1
echo "=======================================================================================" &&

# wait a few seconds to make sure the disk is no longer being used
sleep 5

if [ "$?" == "0" ]; then
        SCRIPT_MESSAGE="Backup completed successfully. It is safe to remove the drive"
        SCRIPT_MESSAGE="Backup failed. To retry, please remove the drive, wait 10 seconds, and re-insert"

umount /tmp/usb_backup_mount_point &&
rmdir /tmp/usb_backup_mount_point &&
echo "Backup Script Complete. Safe to remove drive."

echo $SCRIPT_MESSAGE | mail -s "USB Backup Report" $ADMIN_EMAIL

if [ "$?" == "0" ]; then

        # keep this script running until unplug to prevent the cron job from restarting the backup script
        while [ -b /dev/backupusbdrive ]; do
                sleep 5

rm /var/run/


Darren Wurf