New user's registration have been closed due to high spamming and low trafic on this forum. Please contact forum admins directly if you need an account. Thanks !

Automatic backup, triggered by connecting device

A collection of tips on howto tweak your Bubba.
6feet5
Posts: 269
Joined: 13 Apr 2007, 17:32
Location: Gnesta, Sweden
Contact:

Automatic backup, triggered by connecting device

Post by 6feet5 »

This how-to describes how to start a backup when a specific USB harddisk is connected to the bubba.

This entry is for the B2 (BUBBA|two).
There is a B1 (BUBBA|server) version later on in this thread (here it is).


DISCLAIMER: You use all information found here on your own risk.

Log on to your bubba, using ssh, and become root. Then install the backup program:

Code: Select all

aptitude install rsync
Copy the following text to a file and name it /usr/local/bin/fork:

Code: Select all

#!/bin/sh
/usr/bin/nohup $@ 1>/dev/null 2>&1 </dev/null &
This script will make sure that our backup script detaches itself from udev, or udev would be blocked during the whole backup operation.
Make script executable by typing:

Code: Select all

chmod a+x /usr/local/bin/fork
If you want, you can redirect output to a file instead of /dev/null. This way you can see any output that might be produced, which will help when looking for errors.

Next step is to find out what device to use. Since the device name can differ from time to time, depending on what order different devices are connected, we need a name that doesn't change. udev helps us with this by providing a name based on uuid. Here's how to locate it:
1) Connect your USB device.
2) Locate the device name in the kernel log:

Code: Select all

tail -n 20 /var/log/kern.log
3) Now find the uuid name linked to the device name by typing:

Code: Select all

ls -l /dev/disk/by-uuid
Create a new file, /etc/default/usb_backup, and put the following text in it, replacing the uuid with the one you got in previous step:
#
# The UUID of receiving device (eg. USB hard disk)
# This is found in /dev/disk/by-uuid
#
UUID=c7f180e1-3548-488c-956e-d8e3ba5a52ce

#
# Array of folders to backup. Separate each entry with a space and make sure to use ""
# if name contain spaces.
#
BACKUP_FOLDERS=("/etc" "/home" "/root" "/var/lib" "/var/mail" "/var/spool/cron/crontab")

#
# Should script be allowed to start when device is connected?
# 0 = start when connected; 1 = don't start when connected
#
NO_START=0

#
# At what rate should LED flash during backup. Lower value means higher frequency
#
LED_RATE=8192
Change the paths to what ever you want to back up. If you want to play it safe, use a test path to start with, just in case something goes wrong.

Now to the actual backup script. Create a new file, /usr/local/bin/usb_backup, and paste the following text to it:

Code: Select all

#!/bin/bash
#
# A simple backup solution for a BUBBA|two.
# Written by Johan Stenarson
#
# It is expected to be run as a udev rule, therefor you need to set 
# environment variable ACTION to 'add' when testing it from a shell. 
# Eg:
#    ACTION=add usb_backup
#

CMD=usb_backup
CONFIG=/etc/default/$CMD

### Include user settings if there are any
test -f $CONFIG && . $CONFIG

## Does settings allow script to start
if [ "$NO_START" != "0" ]; then
   logger -t usb_backup "Not starting backup - edit $CONFIG and change NO_START to 0";
   exit 0;
fi

### Mount point for USB device during backup
MOUNT_POINT=/tmp/backup_mount_point

### Quit immediately if this isn't an add action
if [ "${ACTION}" != "add" ]; then
   exit 0;
fi

### Test if script is already running
if [ -f /var/run/$CMD.pid ]; then
   PID="`cat /var/run/$CMD.pid`"
   for i in `ps x|grep $CMD|sed 's/^ *\([0-9]*\).*/\1/g'`; do
      if [ "$i" == "$PID" ]; then
         exit 2
      fi
   done
   # Script not running but .pid file exists, continue
fi

echo $$ >/var/run/$CMD.pid

### A fast flashing LED will indicate progress
echo $LED_RATE > /sys/devices/platform/bubbatwo/ledfreq
echo blink > /sys/devices/platform/bubbatwo/ledmode

### Give udev some time to finish
a=0
while [ ! -e /dev/disk/by-uuid/$UUID ]; do
    ((a++))
    if ((a>20)); then
        ### Give up after 20 seconds/retries
        logger -t usb_backup "Node hasn't been created after 20 seconds"
        exit -1
    fi
    sleep 1
done

### Mount device by uuid
mkdir -p $MOUNT_POINT && mount /dev/disk/by-uuid/$UUID $MOUNT_POINT

for FOLDER in ${BACKUP_FOLDERS[*]}
do
   DEST=`dirname "$FOLDER"`
   mkdir -p "$MOUNT_POINT/autobackup$DEST"
   rsync -a "$FOLDER" "$MOUNT_POINT/autobackup$DEST"
done

### We're done, clean up
umount $MOUNT_POINT && rmdir $MOUNT_POINT
rm /var/run/$CMD.pid
/etc/init.d/led_on
Make it executable:

Code: Select all

chmod a+x /usr/local/bin/usb_backup


You can test the script from the command line by typing:

Code: Select all

ACTION=add /usr/local/bin/usb_backup
The first part on the line will set ACTION to add, this is what udev will do when you connect the device. When it is disconnected, ACTION will be remove. The script will test what action is being performed, and quit if it differ from add, so we don't start a backup when device is being disconnect.

The script will backup selected folders to a directory called autobackup in the root on the USB device and it will try to maintain the directory structure.

Next step is to make a udev rule that identifies the USB device and starts the script. For this we'll use udevinfo that will show a list of attributes that identifies the device. Unfortunately, this step will differ from case to case, depending on what hardware you're using, but here's how I did it. My device was named /dev/sdb. sdb is the important part and I'll use it with udevinfo like this:

Code: Select all

udevinfo -a -p /sys/block/sdb
You will of course replace sdb with the device name you got. Now you should get a list that look something like this (I have removed lines above and below sample):
.......
looking at device '/devices/platform/fsl-ehci.0/usb1/1-1/1-1.1/1-1.1:1.0/host17/target17:0:0/17:0:0:0/block/sdb':
KERNEL=="sdb"
SUBSYSTEM=="block"
DRIVER==""
ATTR{dev}=="8:16"
ATTR{range}=="16"
ATTR{removable}=="0"
ATTR{size}=="490234751"
ATTR{capability}=="12"
ATTR{stat}==" 28 666 1099 240 0 0 0 0 0 188 240"

looking at parent device '/devices/platform/fsl-ehci.0/usb1/1-1/1-1.1/1-1.1:1.0/host17/target17:0:0/17:0:0:0/block':
KERNELS=="block"
SUBSYSTEMS==""
DRIVERS==""

looking at parent device '/devices/platform/fsl-ehci.0/usb1/1-1/1-1.1/1-1.1:1.0/host17/target17:0:0/17:0:0:0':
KERNELS=="17:0:0:0"
SUBSYSTEMS=="scsi"
DRIVERS=="sd"
ATTRS{device_blocked}=="0"
ATTRS{type}=="0"
ATTRS{scsi_level}=="0"
ATTRS{vendor}=="Maxtor 6"
ATTRS{model}=="L250R0 "
ATTRS{rev}=="BAJ4"

ATTRS{state}=="running"
ATTRS{timeout}=="30"
ATTRS{iocounterbits}=="32"
ATTRS{iorequest_cnt}=="0x24"
ATTRS{iodone_cnt}=="0x24"
ATTRS{ioerr_cnt}=="0x0"
ATTRS{modalias}=="scsi:t-0x00"
ATTRS{evt_media_change}=="0"
ATTRS{queue_depth}=="1"
ATTRS{queue_type}=="none"
ATTRS{max_sectors}=="240"

looking at parent device '/devices/platform/fsl-ehci.0/usb1/1-1/1-1.1/1-1.1:1.0/host17/target17:0:0':
KERNELS=="target17:0:0"
SUBSYSTEMS=="scsi"
DRIVERS==""
......
Now you need to pick a couple of attributes that identifies the device. Be sure to pick attributes from same section. A serial number would've been perfect, but my device didn't have one as an attribute. The list above show that third section contains a couple of attributes that identifies my hard disk, ATTRS{vendor}, ATTRS{model} and ATTRS{rev} (highlighted in text). I only have one such device so this is unique enough for me. If your list contains a serialnumber, use it. I will also use the SUBSYSTEMS="scsi" attribute.
Now that we have the attributes, we can build the udev rule. Paste the text (replacing attributes with the ones you got) into a file named /etc/udev/rules.d/010_backup.rules:
SUBSYSTEM=="scsi", ATTRS{vendor}=="Maxtor 6", ATTRS{model}=="L250R0*", ATTRS{rev}=="BAJ4", RUN+="/usr/local/bin/fork /usr/local/bin/usb_backup"
That's it. You should now be able to connect the drive and the LED should start to flash. Backup will proceed and once it is finished, LED will stop blinking.

This script has only been tested on a BUBBA|two, though it shouldn't be to difficult to get it running on a BUBBA|server. One snag is that the udev is to old to support the RUN command in the udev rule (at least it is on my B1). It might be possible to upgrade udev, though I haven't tested this so I don't know what other effects this would have. Or you can wait for the upcoming new release, mentioned somewhere else (I think, or am I imagining things now). Another solution would be to skip the udev part and let a cron job look for the uuid device name. The cron job would call the backup script if device name exist or else it will quit.

Anyone that want to learn more about udev rules can read Writing udev rules.

Don't hesitate to post if you find any errors, have suggestions on how to improve it or have questions.

/Johan

EDIT: I have improved the part that waits for udev to settle.
EDIT: Added a link to the B1 version later on in this thread.
Last edited by 6feet5 on 18 Feb 2010, 17:25, edited 2 times in total.
ahoff
Posts: 105
Joined: 01 Apr 2008, 20:50
Location: Swe

Post by ahoff »

Great HOWTO. Tanks.
I intend to take it even further by using a tellstick to power up an external usb-drive automaticly every sunday night. And when the backup script finish, I let the tellstick power down the usb-device automaticly.

Just need to buy myself a BUBBA-storage :D

/Åke
Last edited by ahoff on 11 Nov 2009, 15:27, edited 1 time in total.
jonte
Posts: 65
Joined: 05 Nov 2008, 11:52

Post by jonte »

Was thinking of trying this solution out.
Just wondering what directories are worth (essential) backing up.

I was thinking of backing up my home dir and the storage dir.

Johan, you mentioned:

Code: Select all

BACKUP_FOLDERS=("/etc" "/home" "/root" "/var/lib" "/var/mail" "/var/spool/cron/crontab") 
in this howto.

Is it a good idea to include these directories and just add my "/storage" to the ones described above?

//Jonte
6feet5
Posts: 269
Joined: 13 Apr 2007, 17:32
Location: Gnesta, Sweden
Contact:

Post by 6feet5 »

I think so, but I'm no expert and it depend on what you consider essential.

/etc, /var/spool/cron/crontab and /var/lib will help you restore your bubba if it crash. Note however that you still need to manually install all extra packages you've added to the original setup, but the configuration is backed up (in /etc). You could probably list all installed packages and store this file on the backup unit to make it even easier to restore the system in case of a crash.

/home folder contain all users home folders and the public storage area (so you don't need to add storage explicitly unless you're thinking of another folder named storage)

/var/mail could probably be omitted since ordinary mail is stored in the home folder. I added it since I was unsure if any of the software running on bubba use this folder to send mail to administrator. Better safe than sorry, but you could probably skip it.

/root could probably be omitted as well, but I have a couple of scripts and stuff stored there so that's why I added it to the list.

HTH,

/Johan
jonte
Posts: 65
Joined: 05 Nov 2008, 11:52

Post by jonte »

6feet5 wrote: HTH,

/Johan
Sure does :)
Thanks a bunch!

//J
Xnij
Posts: 22
Joined: 25 Oct 2008, 18:02

Post by Xnij »

Nice script! Thank you for sharing it with us. :)

In the /usr/local/bin/usb_backup script, why do you append the parent directory to the rsync destination directory? Is this a safety precaution so that if you have trailing slashes in BACKUP_FOLDERS it won't trash the destination folder?

Also, from what I gather the -r option to rsync is implied by -a.

A neat feature would be able to exclude folders. For example, I would like to backup the /home folder but exclude some video folders from /home/storage. Rsync does have an --exclude option so I'm going to look into using that.

Again, thanks for a great backup script.
6feet5
Posts: 269
Joined: 13 Apr 2007, 17:32
Location: Gnesta, Sweden
Contact:

Post by 6feet5 »

In the /usr/local/bin/usb_backup script, why do you append the parent directory to the rsync destination directory? Is this a safety precaution so that if you have trailing slashes in BACKUP_FOLDERS it won't trash the destination folder?
Are you referring to the three line in the for-loop? If so, that part is there to keep the folder heirarchy. I thought it would be easier to restore the system if it was obvious which folder goes where. Say BACKUP_FOLDERS contain something like "/var/spool/cron/crontab". Without the three lines you would get a crontab-folder in the destination folder. As I made it, you get a "var/spool/cron/crontab" folder in the destination folder. The protection you mention is an added bonus I haven't thought of :-)

Your right about the -r option. I was playing around with the options and forgot to look them though.

As for the exclude option, I was thinking about adding such a setting, like the BACKUP_FOLDERS setting, so you could add patterns to exclude, but haven't done any work on it yet. An improvement for the future. Feel free to post your solution, if you add it.

Another option to consider is --delete (or one of the other delete options, see man page). As it is now, renaming a huge file between backups would end up in several copies of the file in the destiniation folder, since files missing in the source aren't deleted in the destination. This was intentional, to prevent the backupscript from deleting files by accident. Lets say you delete /home by accident, then think "hey, I've got a backup" and connect the drive. The drive is recognized and the backup script starts, notices /home is empty so it starts removing files from backup folder. Not a good thing :-)
As it is now, you have the option to prevent the script from starting, by setting NO_START=1. This means it should be safe to add a delete option to the rsync command, provided you remember to set it to 1 the day you want to restore files deleted by accident.

/Johan
Xnij
Posts: 22
Joined: 25 Oct 2008, 18:02

Post by Xnij »

Ah, of course. Smart :)

What I like about this script is the simplicity so adding extra complexity with excluded directories might just be asking for trouble.

I'm of the same opinion when it comes to rsync's --delete option - better to back up to much than to loose it all.

Another nice feature would be for it to email an error message if the backup fails in some way (e.g. no space left on usb drive). Nothing is worse than when you think you have a recent backup and you actually don't.

If I do end up hacking on it I will post any updates here.
6feet5
Posts: 269
Joined: 13 Apr 2007, 17:32
Location: Gnesta, Sweden
Contact:

Post by 6feet5 »

This reminds me of an improvement I made to the script I'm running, but forgot to tell about. I added some logging to the script.

Step one is to tell rsync to log its actions. Do so by replacing the rsync line with:

Code: Select all

rsync --log-file /var/log/$CMD.log -a "$FOLDER" "$MOUNT_POINT/autobackup$DEST"
It is possible to control the format of the logfile, though I haven't bothered. See 'man rsyncd.conf' for more information on this.

Step two is to add the new log to the web-admin interface for easy access. Start your preferred editor and load '/usr/share/web-admin/admin/controllers/settings.php'. Locate the function 'logs()', and add the following line to the array:

Code: Select all

'usb_backup.log' => 'usb_backup.log'
Save it and quit. It should now be available among the other logfiles in the drop-down combo box on the "Settings" page. As further improvement, you might consider running logrotate on the new log. I'll leave that as an exercise for the reader ;-)

I mentioned in a previous post it might be of interest to know what packages are installed on your bubba (for easy recovery). Add the following line after the for-loop (but before unmounting device ;-))

Code: Select all

aptitude search ~i|sed -n 's/i   \([^ ]*\).*/\1/p' > "$MOUNT_POINT/autobackup/packagelist.txt"
This will give you a file named packagelist.txt in you backup folder. It will contain the package name of all installed packages (excluding those installed automatically due to dependancies). Note that this list contain ALL packages, not just the ones you've installed extra.

/Johan
jonte
Posts: 65
Joined: 05 Nov 2008, 11:52

Post by jonte »

6feet5 wrote:This reminds me of an improvement I made to the script I'm running, but forgot to tell about. I added some logging to the script.

Step one is to tell rsync to log its actions. Do so by replacing the rsync line with:

Code: Select all

rsync --log-file /var/log/$CMD.log -a "$FOLDER" "$MOUNT_POINT/autobackup$DEST"

Thanks once again, great mod!

Just a question though. I noticed that the -r (recursive) option to the rsync command is missing in the above command. Is it intentional or just a typo?


//J
6feet5
Posts: 269
Joined: 13 Apr 2007, 17:32
Location: Gnesta, Sweden
Contact:

Post by 6feet5 »

Yes, it was intentional, as noted in a previous post ;-)

The -r is implied in the -a option. Actually, the -a activates other options as well, see man page for more info.

/Johan
Xnij
Posts: 22
Joined: 25 Oct 2008, 18:02

Post by Xnij »

Ok, finally found the time to mess around with this script.

I've added the ability to exclude folders. If somebody wants to use this *please* test it thoroughly before using as my backup scenario might differ from yours.

In /etc/default/usb_backup I've added the variable BACKUP_FOLDERS_EXCLUDE :
BACKUP_FOLDERS=("/etc" "/root" "/var/lib" "/home")

# Array of folders to exclude from backup.
BACKUP_FOLDERS_EXCLUDE=("/home/user/tmp" "/home/storage/video/films" "/home/storage/video/series" \
"/home/storage/video/tv")
Which is an array similar to BACKUP_FOLDERS containing the absolute path to folders which are to be excluded. Watch out for trailing slashes as I'm not sure it that stuffs up the script.

And in /usr/local/bin/usb_backup I've modified the for loop to build the --exclude option which is passed to rsync:

Code: Select all

for FOLDER in ${BACKUP_FOLDERS[*]}
do 
   EXCLUDE_LIST=""
   for EXCLUDE in ${BACKUP_FOLDERS_EXCLUDE[*]}
   do  
       # Build exclude list. --exclude patterns are relative to the
       # backup directory. Prefixed wih '/' to ensure correct folder
       # is excluded.
       if [[ $EXCLUDE == ${FOLDER}* ]]
       then
           EXCLUDE_LIST="$EXCLUDE_LIST --exclude ${EXCLUDE#$FOLDER}"
       fi
   done

   mkdir -p "$MOUNT_POINT/autobackup$FOLDER"  

   # Make sure to include hidden files in the wildcard in "$FOLDER"/*
   shopt -s dotglob

   rsync -a $EXCLUDE_LIST --log-file /var/log/$CMD.log "$FOLDER"/* "$MOUNT_POINT/autobackup$FOLDER/"
done

I had to make some changes to the rsync source and destination parameters so that the EXCLUDE_LIST excludes were relative to the source directory. Also, from what I understand, if you don't specify a initial '/' in the excludes you run the risk of excluding all folders with that name i.e. '--exclude bin' wound exclude all folders (and files) with that name, whereas '--exclude /bin' would only exclude the directory directly relative to the source directory. Rsync is quite picky regarding this. An added bonus of this is that the temporary variable DEST is not required anymore.

Edit: Noticed that hidden files weren't being backed up. Added bash option dotglob.
Xnij
Posts: 22
Joined: 25 Oct 2008, 18:02

Post by Xnij »

Have noticed another problem. If one of the folders defined in BACKUP_FOLDERS or BACKUP_FOLDERS_EXCLUDE have spaces in the name the script will fail.

This isn't a problem for me as I don't have such folders included.

I'll see if I can figure it out. Mind you, I'm no bash expert, but I'll give it a go.
ahoff
Posts: 105
Joined: 01 Apr 2008, 20:50
Location: Swe

Post by ahoff »

My logfile is full of lines like this:
2009/03/27 18:28:23 [4964] rsync: chown "/tmp/backup_mount_point/autobackup/home/ahoff/backup/Hoffit/Fakturaor In" failed: Operation not permitted (1)
Any idea why?
Xnij
Posts: 22
Joined: 25 Oct 2008, 18:02

Post by Xnij »

Which file system do you have on the usb drive?

Which version of the script are you running?

If you're using Fat32 I'm not certain it can handle permissions so that might be your problem.
Post Reply