Daily Backup Script

This is a daily backup script I run on my servers.

It backs up all my mySQL databases, Trac data, Subversion, /etc, /home and various other important directories.

Simply drop this in /usr/local/bin and then make a symbolic link from /etc/cron.daily to it.

#!/bin/bash

#
# written by Daevid Vincent (http://daevid.com) on 02.26.08
#

# pretty much this is the only line you have to deal with:
DIRLIST="/etc /root /home /var/spool/mail /var/lib/mysql /var/local/trac /var/local/svn /usr/local/bin /var/lib/mailman"

MYSQLDBDIR="/var/lib/mysql"
MYSQLDUMPOPTS="--opt --complete-insert --flush-logs --force --quote-names --add-drop-database --lock-tables --insert-ignore --comments"

# edit this file if you have exclusions in the above directories.
EXCLUDELIST="/etc/backup_exclude.txt"

# this is best if it is a separate HardDrive, as we are going to mount it rw,
# then when done, mount it back as ro so it's more secure and less chance of catastrophe
BACKUPDIR="/backups"

LIST="/tmp/backlist_$$.txt"

# I experimented with --bzip and it seemed to have corrupted archives more often than not.
# something you really don't want in a backup scenario. plus the compression wasn't that
# much more significant than using good old standard gzip. YMMV.

TARFLAGS="--create --gzip --preserve-permissions --totals --dereference --ignore-failed-read --exclude-from=$EXCLUDELIST"

set $(date)

echo ""

# For example, to back up all files that have changed in the last week, one might use:
# find / -local ! -type d -mtime -7 -type f -print | tar ca -

# NOTE:
# 	ctime, as tested by find, is related to the status of the file (permissions, timestamp, name?), 
#	whereas mtime refers to the data within the file. 
#	And, of course, atime is when the file was last accessed.

# make sure we're running as root
if (( `/usr/bin/id -u` != 0 )); then { echo "Sorry, must be root.  Exiting..."; exit; } fi

echo "mounting $BACKUPDIR as read-write"
#mount -o remount,rw $BACKUPDIR || error "could not remount $BACKUPDIR read-write"

echo "Dumping all databases found in $MYSQLDBDIR"
cd $MYSQLDBDIR
for mydb in `ls -1 -d */`
do
        dbname=`echo $mydb | sed -e 's/\/$//g'`        
	#echo "mysqldump $MYSQLDUMPOPTS --databases $dbname > /tmp/$6-$2-$3_mysql_db-$dbname.sql"
	echo -n "mysqldump database $dbname..."
	mysqldump $MYSQLDUMPOPTS --databases $dbname > "/tmp/$6-$2-$3_mysql_db-$dbname.sql"
	echo "complete"
done

echo "Combining all separate databases into a single .tgz file"

	if test "$1" = "Sun" ; then
        # weekly a full backup of all data and config. settings:
	    /bin/tar $TARFLAGS --file $BACKUPDIR/$6-$2-$3_FULL_mysql_db.tgz /tmp/$6-$2-$3_mysql_db-*.sql 
            /bin/rm -f $BACKUPDIR/*_DIFF_mysql_db.tgz
	    echo "tarballing mysqldump of ALL databases to $BACKUPDIR/$6-$2-$3_FULL_mysql_db.sql"
	else
	# incremental backups
	    /bin/tar $TARFLAGS --file $BACKUPDIR/$6-$2-$3_DIFF_mysql_db.tgz /tmp/$6-$2-$3_mysql_db-*.sql 
	    echo "tarballing mysqldump of ALL databases to $BACKUPDIR/$6-$2-$3_DIFF_mysql_db.sql"
	fi

/bin/rm --force /tmp/$6-$2-$3_mysql_db-*.sql

echo "Backing up Trac databases"
/usr/bin/trac-admin /var/local/trac/trac.symcell.com hotcopy /tmp/trac_symcell_$6-$2-$3 >/dev/null
/usr/bin/trac-admin /var/local/trac/trac.daevid.com hotcopy /tmp/trac_daevid_$6-$2-$3 >/dev/null
tar zcpf /backups/$6-$2-$3_trac.tgz /tmp/trac_*$6-$2-$3
rm -rf /tmp/trac_*_$6-$2-$3

echo "Backing up key directories"
cd /
for DIR in $DIRLIST
do	
	# some fancy mangling to handle "/" in the naming below...
	DIRNAME=`echo $DIR | sed 's|^/||;'`        
	DIRNAME=`echo $DIRNAME | sed 's|/$||;'`        
        DIRNAME=`echo $DIRNAME | sed -e 's/\//_/g'`        
	#echo "changing $DIR to $DIRNAME"

	if test "$1" = "Sun" ; then
        # weekly a full backup of all data and config. settings:
	    echo -n "Full backup of $BACKUPDIR/$6-$2-$3_FULL_${DIRNAME}.tgz..."
	    /bin/tar $TARFLAGS --file $BACKUPDIR/$6-$2-$3_FULL_${DIRNAME}.tgz $DIR
#            rm -f $BACKUPDIR/$6-$2-$3_DIFF_${DIRNAME}.**
            /bin/rm -f $BACKUPDIR/*_DIFF_${DIRNAME}.tgz
	    echo "completed."
	else
	# incremental backups
	    echo -n "Differential backup of $BACKUPDIR/$6-$2-$3_DIFF_${DIRNAME}.tgz..."
	    find $DIR -depth -type f -ctime -1 -print > $LIST
	    /bin/tar $TARFLAGS --file $BACKUPDIR/$6-$2-$3_DIFF_${DIRNAME}.tgz --files-from=$LIST
	    /bin/rm -f $LIST
	    echo "completed."
	fi

	echo ""
done


echo ""

echo "changing $BACKUPDIR perms to 0640"
chmod 640 $BACKUPDIR/*.tgz

echo "Deleting files older than 30 days ONLY in $BACKUPDIR"
find $BACKUPDIR -maxdepth 1 -mtime +30 -type f -exec rm -rf "{}" \;

#echo "mounting $BACKUPDIR as read-only again"
#mount -o remount,ro $BACKUPDIR || error "could not remount $BACKUPDIR read-only"

echo "System backups to $BACKUPDIR completed."

mount /mnt/nas
echo "Copying backups to NAS"
cp -vpf $BACKUPDIR/$6-$2-$3_*.tgz /mnt/nas/
echo "Deleting files older than 15 days ONLY in /mnt/nas/"
find /mnt/nas/ -maxdepth 1 -mtime +15 -type f -exec rm -rf "{}" \;
umount /mnt/nas

mount /mnt/htpc
# for some reason we have to delete FIRST on this NTFS drive or else this purges the new files too?!
echo "Deleting files older than 15 days ONLY in /mnt/htpc/"
find /mnt/htpc/ -maxdepth 1 -mtime +15 -type f -exec rm -rf "{}" \;
echo "Copying backups to HTPC"
cp -vpf $BACKUPDIR/$6-$2-$3_*.tgz /mnt/htpc/
umount /mnt/htpc

echo "********************************* Daily backup for $6-$2-$3 completed *********************************"

This is my /etc/backup_exclude.txt file.

/home/lost+found
/home/dae51d/public_html/mp3
/home/fhp/HTML/nexus6/live
/home/fhp/HTML/nexus6/MP3 edits
/home/fhp/HTML/nexus6/demo
/root/.mozilla/default
/home/reviewit/Cepstral Voices
*.mp3
.mp3
*.MP3
.MP3
*.m3u
*.M3U
.wwv
*.wwv
*.wav

This is my restore script for the mysql dumps created above. Simply untar a full backup from above, then put this file in the same directory, and run it.

#!/bin/bash

#
# written by Daevid Vincent (http://daevid.com) on 02.26.08
#

# make sure we're running as root
if (( `/usr/bin/id -u` != 0 )); then { echo "Sorry, must be root.  Exiting..."; exit; } fi

echo "Restoring all databases found"
for mydb in `ls -1 -d *.sql`
do
	dbname=${mydb:21}
	dbname=`echo $dbname | cut -d "." -f1`
	#echo "mysql $dbname < $mydb"
	echo -n "restoring database $mydb..."
	mysql -e "CREATE DATABASE /*!32312 IF NOT EXISTS*/ $dbname ;"
	mysql $dbname < $mydb
	echo "complete"
done