When executing a script or scheduling one, always use the full path. You can use a . to shorten it if you know what you are doing but like I said, use the full path to avoid problems.
Full path example:
/var/scripts/runme.sh
or
sudo /var/scripts/runme.sh
Short path example:
./runme.sh
or
sudo ./runme.sh
The short path example will ONLY work if you are currently "in" the directory where the script is located.
--------------------------------------------------------------------
I looked around the web for backup scripts too but nothing did exactly what I wanted so I made up my own...centralized around the mysql dump command.
Here is the schedule that runs my backup every day at 11pm. I have it saved to a file so I can archive changes to the schedule. I then load it by typing something similar to crontab -u root /var/scripts/prod/crontab.root
Code:
########################################
# Name: Crontab Schedule for root user
# Author: LHammonds
# To enable, type: crontab -u root crontab.root
# To disable, type: crontab -u root emptyfile
############# Update Log ###############
# 2011-12-19 - LTH - Created schedule
########################################
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
# m h dom mon dow command
#
# Backup MySQL to a local folder, archive and store offsite.
#
0 23 * * * /var/scripts/prod/mysql-backup.sh > /dev/null 2>&1
I also use a standard "include" file to keep a single place for all my global variables and functions.
/var/scripts/common/standard.conf
Code:
## Global Variables ##
TEMPDIR="/var/temp"
SHAREDIR="/srv/samba/share"
MYDOMAIN="example.com"
ADMINEMAIL="admin@${MYDOMAIN}"
REPORTEMAIL="lhammonds@${MYDOMAIN}"
BACKUPDIR="/var/backup"
OFFSITEDIR="/mnt/backup"
OFFSITETESTFILE="${OFFSITEDIR}/online.txt"
ARCHIVEMETHOD="tar.7z" ## Choices are tar.7z or tgz
HOSTNAME=$(hostname -s)
SCRIPTNAME=$0
MAILFILE="${TEMPDIR}/mailfile.$$"
## Global Functions ##
function f_sendmail()
{
## Purpose: Send email message.
## Parameter #1 = Subject
## Parameter #2 = Body
sendemail -f "${ADMINEMAIL}" -t "${REPORTEMAIL}" -u "${1}" -m "${2}\n\nServer: ${HOSTNAME}\nProgram: ${SCRIPTNAME}\nLog: ${LOGFILE}" -s mailserver:25 1>/dev/null 2>&1
}
function f_mount()
{
## Mount the pre-configured Windows share folder.
## NOTE: The Windows share should have a file called "online.txt"
if [ ! -f ${OFFSITEDIR}/online.txt ]; then
mount -t cifs //srv-winbackup/mysql ${OFFSITEDIR} --options nouser,rw,nofail,noatime,noexec,credentials=/etc/cifspw
fi
}
function f_umount()
{
## Dismount the Windows share folder.
## NOTE: The unmounted folder should have a file called "offline.txt"
if [ -f ${OFFSITEDIR}/online.txt ]; then
umount ${OFFSITEDIR}
fi
}
The mount stuff above simply mounts a Windows share where I save my offsite files.
And here is the script I use to backup my database. The archives are stored offsite and whenever there is not enough room at the offsite location to copy the archive, it goes through and removes the oldest archives until there is enough room. That means you can go back to an old archive as far back as your storage space will allow.
/var/scripts/prod/mysql-backup.sh
Code:
#!/bin/bash
#############################################
## Name : mysql-backup.sh
## Version : 1.1
## Date : 2012-01-09
## Author : LHammonds
## Purpose : Complete backup of MySQL database.
## Compatibility : Verified on Ubuntu Server 10.04.3 LTS, MySQL 5.1.41
## Requirements : p7zip-full (if ARCHIVEMETHOD=tar.7z), sendemail
## Run Frequency : Once per day after hours or as needed (will not shutdown service)
## Exit Codes : (if multiple errors, value is the addition of codes)
## 0 = success
## 1 = 7zip not installed
## 2 = archive failure
## 4 = archive purge failure
## 8 = configuration error
## 16 = mount warning
################ CHANGE LOG #################
## DATE WHO WHAT WAS CHANGED
## ---------- --- ----------------------------
## 2011-12-19 LTH Created script.
## 2012-01-09 LTH Bugfix - f_PurgeOldestArchive
#############################################
## Import common variables and functions. ##
source /var/scripts/common/standard.conf
LOGFILE="${TEMPDIR}/mysql-backup.log"
LOCKFILE="${TEMPDIR}/mysql-backup.lock"
TARGETDIR="${BACKUPDIR}/mysql"
OFFSITEBACKDIR="${OFFSITEDIR}/mysql"
ARCHIVEFILE="`date +%Y-%m-%d-%H-%M`_mysql-backup.${ARCHIVEMETHOD}"
ERRORFLAG=0
#######################################
## FUNCTIONS ##
#######################################
function f_PurgeOldestArchive()
{
## Purpose: Delete the oldest archive on the remote site.
## Return values:
## 0 = Success
## 1 = Cannot delete file
## 9 = Configuration error, path empty
## Variable Error Check. *
if [ ${OFFSITEBACKDIR} = "" ]; then
## Make darn sure the path is not empty since we do NOT
## want to start purging files from a random location.
echo "`date +%Y-%m-%d_%H:%M:%S` --- Purge error: OFFSITEBACKDIR site variable is empty!" >> ${LOGFILE}
return 9
fi
## Get the name of the oldest file.
OLDESTFILE=`ls -1t ${OFFSITEBACKDIR} | tail -1`
if [ "${OLDESTFILE}" = "" ]; then
## Error. Filename variable empty.
echo "`date +%Y-%m-%d_%H:%M:%S` --- Purge error: OLDESTFILE variable is empty." >> ${LOGFILE}
return 9
else
FILESIZE=`ls -lak "${OFFSITEBACKDIR}/${OLDESTFILE}" | awk '{ print $5 }' | sed -e :a -e 's/\(.*[0-9]\)\([0-9]\{3\}\)/\1,\2/;ta'`
echo "`date +%Y-%m-%d_%H:%M:%S` --- Purging old file: ${OFFSITEBACKDIR}/${OLDESTFILE}, Size = ${FILESIZE} kb" >> ${LOGFILE}
rm "${OFFSITEBACKDIR}/${OLDESTFILE}"
if [ -f "${OFFSITEBACKDIR}/${OLDESTFILE}" ]; then
## File still exists. Return error.
return 1
else
return 0
fi
fi
}
function f_cleanup()
{
echo "`date +%Y-%m-%d_%H:%M:%S` - MySQL backup exit code: ${ERRORFLAG}" >> ${LOGFILE}
if [ -f ${LOCKFILE} ];then
## Remove lock file so other rsync jobs can run.
rm ${LOCKFILE} 1>/dev/null 2>&1
fi
## Email the result to the administrator.
if [ ${ERRORFLAG} -eq 0 ]; then
f_sendmail "MySQL Backup Success" "MySQL backup completed with no errors."
else
f_sendmail "MySQL Backup ERROR" "MySQL backup failed. ERRORFLAG = ${ERRORFLAG}"
fi
}
function f_emergencyexit()
{
## Purpose: Exit script as cleanly as possible.
## Parameter #1 = Error Code
f_cleanup
exit $1
}
#######################################
## MAIN PROGRAM ##
#######################################
## Binaries ##
TAR="$(which tar)"
MY7ZIP="$(which 7za)"
MYSQL="$(which mysql)"
MYSQLDUMP="$(which mysqldump)"
if [ -f ${LOCKFILE} ]; then
## Program lock file detected. Abort script.
f_sendmail "MySQL Backup Aborted - Lock File" "This script tried to run but detected the lock file: ${LOCKFILE}\n\nPlease check to make sure the file does not remain when this script is not actually running."
exit 1
else
## Create the lock file to ensure only one script is running at a time.
echo "`date +%Y-%m-%d_%H:%M:%S` ${SCRIPTNAME}" > ${LOCKFILE}
fi
echo "`date +%Y-%m-%d_%H:%M:%S` - MySQL backup started." >> ${LOGFILE}
## If the 7-Zip archive method is specified, make sure the package is installed.
if [ "${ARCHIVEMETHOD}" = "tar.7z" ]; then
if [ ! -f "/usr/bin/7za" ]; then
## Required package (7-Zip) not installed.
echo "`date +%Y-%m-%d_%H:%M:%S` - CRITICAL ERROR: 7-Zip package not installed. Please install by typing 'aptitude -y install p7zip-full'" >> ${LOGFILE}
ERRORFLAG=1
f_emergencyexit ${ERRORFLAG}
fi
fi
echo "`date +%Y-%m-%d_%H:%M:%S` --- Partition status:" >> ${LOGFILE}
df -h >> ${LOGFILE}
## Document the current uptime.
${MYSQL} -e status | grep -i uptime >> ${LOGFILE}
StartTime="$(date +%s)"
echo "`date +%Y-%m-%d_%H:%M:%S` --- Space consumed in ${MYSQLDIR} = `du -sh ${MYSQLDIR} | awk '{ print $1 }'`" >> ${LOGFILE}
## Backup all databases.
${MYSQLDUMP} --all-databases > ${TARGETDIR}/mysql-all.sql
## Loop through every database.
DATABASES=$(echo "show databases;"|mysql --skip-column-names)
for DATABASE in ${DATABASES}
do
if [ "${DATABASE}" != "information_schema" ]; then
## Backup individual database.
${MYSQLDUMP} ${DATABASE} > ${TARGETDIR}/${DATABASE}.sql
## Create database sub-folder.
mkdir -p ${TARGETDIR}/${DATABASE}
## Export each table in the database individually.
for TABLE in `echo "show tables" | $MYSQL ${DATABASE}|grep -v Tables_in_`;
do
FILE=${TARGETDIR}/${DATABASE}/${TABLE}.sql
case "${TABLE}" in
general_log)
${MYSQLDUMP} ${DATABASE} ${TABLE} --skip-lock-tables > ${FILE}
;;
slow_log)
${MYSQLDUMP} ${DATABASE} ${TABLE} --skip-lock-tables > ${FILE}
;;
*)
${MYSQLDUMP} ${DATABASE} ${TABLE} > ${FILE}
;;
esac
done
fi
done
## Compress the backup into a single file based on archive method specified.
echo "`date +%Y-%m-%d_%H:%M:%S` --- Compressing archive: ${TEMPDIR}/${ARCHIVEFILE}" >> ${LOGFILE}
case "${ARCHIVEMETHOD}" in
tar.7z)
${TAR} -cpf - ${TARGETDIR} | ${MY7ZIP} a -si -mx=9 -w${TEMPDIR} ${TEMPDIR}/${ARCHIVEFILE} 1>/dev/null 2>&1
RETURNVALUE=$?
## Restore using one of the following commands (do not uncomment, only for notation):
## 7za x -so -w/tmp ${TEMPDIR}/${ARCHIVEFILE} | tar -C / -xf -
## 7za x -so -w/tmp ${TEMPDIR}/${ARCHIVEFILE} | tar -C ${TEMPDIR}/restore --strip-components=1 -xf -
;;
tgz)
${TAR} -cpzf ${TEMPDIR}/${ARCHIVEFILE} ${TARGETDIR} 1>/dev/null 2>&1
RETURNVALUE=$?
## Restore using one of the following commands (do not uncomment, only for notation):
## tar -C / -xzf ${TEMPDIR}/${ARCHIVEFILE}
## tar -C ${TEMPDIR}/restore --strip-components=1 -xzf ${TEMPDIR}/${ARCHIVEFILE}
;;
*)
${TAR} -cpzf ${TEMPDIR}/${ARCHIVEFILE} ${TARGETDIR} 1>/dev/null 2>&1
RETURNVALUE=$?
;;
esac
if [ ${RETURNVALUE} -ne 0 ]; then
## tar command failed. Send warning email.
f_sendmail "MySQL Backup Failure - tar" "tar failed with return value of ${RETURNVALUE}"
ERRORFLAG=$((${ERRORFLAG} + 2))
fi
## Mount the remote folder. ##
f_mount
if [ ! -f ${OFFSITETESTFILE} ]; then
## Could not find expected file on remote site. Assuming failed mount.
ERRORFLAG=$((${ERRORFLAG} + 16))
echo "`date +%Y-%m-%d_%H:%M:%S` --- ERROR: Cannot detect remote location: ${OFFSITETESTFILE}" >> ${LOGFILE}
f_emergencyexit ${ERRORFLAG}
fi
FREESPACE=`df -k ${OFFSITEDIR} | grep ${OFFSITEDIR} | awk '{ print $3 }'`
BACKUPSIZE=`ls -lak "${TEMPDIR}/${ARCHIVEFILE}" | awk '{ print $5 }'`
## Make sure space is available on the remote server to copy the file.
if [ ${FREESPACE} -lt ${BACKUPSIZE} ]; then
## Not enough free space available. Purge existing backups until there is room.
ENOUGHSPACE=0
while [ ${ENOUGHSPACE} -eq 0 ]
do
f_PurgeOldestArchive
RETURNVALUE=$?
case ${RETURNVALUE} in
1)
## Cannot purge archives to free up space. End program gracefully.
echo "`date +%Y-%m-%d_%H:%M:%S` - ERROR: Not enough free space on ${OFFSITEBACKDIR} and cannot purge old archives. Script aborted." >> ${LOGFILE}
## Stop and exit the script with an error code.
ERRORFLAG=$((${ERRORFLAG} + 4))
f_emergencyexit ${ERRORFLAG}
;;
9)
## Configuration error, end program gracefully.
echo "`date +%Y-%m-%d_%H:%M:%S` - ERROR: Configuration problem. Script aborted." >> ${LOGFILE}
## Stop and exit the script with an error code.
ERRORFLAG=$((${ERRORFLAG} + 8))
f_emergencyexit ${ERRORFLAG}
;;
esac
FREESPACE=`df -k ${OFFSITEDIR} | grep ${OFFSITEDIR} | awk '{ print $3 }'`
if [ ${FREESPACE} -gt ${BACKUPSIZE} ]; then
## Enough space is now available.
ENOUGHSPACE=1
else
## Not enough space is available yet.
ENOUGHSPACE=0
fi
done
fi
## Copy the backup to an offsite storage location.
echo "`date +%Y-%m-%d_%H:%M:%S` --- Copying archive file to offsite location." >> ${LOGFILE}
cp ${TEMPDIR}/${ARCHIVEFILE} ${OFFSITEBACKDIR}/${ARCHIVEFILE} 1>/dev/null 2>&1
if [ ! -f ${OFFSITEBACKDIR}/${ARCHIVEFILE} ]; then
## NON-FATAL ERROR: Copy command did not work. Send email notification.
echo "`date +%Y-%m-%d_%H:%M:%S` --- WARNING: Remote copy failed. ${OFFSITEBACKDIR}/${ARCHIVEFILE} does not exist!" >> ${LOGFILE}
f_sendmail "Offline RSync Backup Failure - Remote Copy" "Remote copy failed. ${OFFSITEBACKDIR}/${ARCHIVEFILE} does not exist\n\nBackup file still remains in this location: ${HOSTNAME}:${TEMPDIR}/${ARCHIVEFILE}"
else
## Remove local copy of the compressed backup file
rm ${TEMPDIR}/${ARCHIVEFILE}
fi
## Unmount the Windows shared folder.
f_umount
## Calculate total time for backup.
FinishTime="$(date +%s)"
ElapsedTime="$(expr ${FinishTime} - ${StartTime})"
Hours=$((${ElapsedTime} / 3600))
ElapsedTime=$((${ElapsedTime} - ${Hours} * 3600))
Minutes=$((${ElapsedTime} / 60))
Seconds=$((${ElapsedTime} - ${Minutes} * 60))
echo "`date +%Y-%m-%d_%H:%M:%S` --- Total backup time: ${Hours} hour(s) ${Minutes} minute(s) ${Seconds} second(s)" >> ${LOGFILE}
echo "`date +%Y-%m-%d_%H:%M:%S` - Offline RSync backup completed." >> ${LOGFILE}
## Perform cleanup routine.
f_cleanup
## Exit with the combined return code value.
exit ${ERRORFLAG}
Do whatever you want with it or ignore it completely. But remember, have fun!
LHammonds
Bookmarks