Linux Backup Script mit Hardlink-Rotation

#!/bin/bash

function outputToEmailAndLog {
  echo "[`date +"%Y-%m-%d_%H:%M:%S %z(%Z)"`] ${1}" >> ${logFile}
  echo "${1}" >> ${emailFile}
  echo "${1}"
}

function outputToLog {
  echo "[`date +"%Y-%m-%d_%H:%M:%S %z(%Z)"`] ${1}" >> ${logFile}
  echo "${1}"
}

# createMissingDirectories(destinationPath)
function createMissingDirectories {
  [ -d ${1} ] || mkdir ${1}
  [ -d ${1}/current ] || mkdir ${1}/current
  [ -d ${1}/daily ] || mkdir ${1}/daily
  [ -d ${1}/weekly ] || mkdir ${1}/weekly
  [ -d ${1}/monthly ] || mkdir ${1}/monthly
}

# rsyncCopy(sourcePath, destinationPath)
function rsyncCopy {
  # Copy current data
  outputToEmailAndLog "Copy from ${1} to ${2}"
  rsync -avzO --delete ${1}/ ${2}/current >> ${logFile} 2>&1
  if ! [ $? = 24 -o $? = 0 ] ; then {
    outputToEmailAndLog "Error: Rsync finished ${1} with errors!"
    errorOccurred=true
  } fi
}

# hardlinkRotate(rotatePath, intervalSign)
function hardlinkRotate {
  outputToEmailAndLog "Rotate ${1} ${2}"
  if [ "${2}" = "daily" -o "${2}" = "weekly" -o "${2}" = "monthly" ]; then {
    # Rotate hardlinks
    dailyBackupDirectory=${1}/daily/${startDateTime}
    cp -al ${1}/current ${dailyBackupDirectory}
    if ! [ $? = 0 ] ; then {
      outputToEmailAndLog "Error: Rotate finished ${1} with errors!"
      errorOccurred=true
    } fi
    touch ${dailyBackupDirectory}
  } fi
  
  if [ "${2}" = "weekly" -o "${2}" = "monthly" ]; then {
    # Delete old hardlink directories and moving other directories
    find ${1}/daily -maxdepth 1 -type d -mtime +7 -exec rm -r {} ';'
    oldestDailyBackupDirectory=$(ls -d ${1}/daily/* -r | tail -n1)
    outputToEmailAndLog "Using weekly ${oldestDailyBackupDirectory}"
    if [ "${oldestDailyBackupDirectory}" != "" ]; then {
      mv ${oldestDailyBackupDirectory} ${1}/weekly/
      if ! [ $? = 0 ] ; then {
        outputToEmailAndLog "Error: Rotate finished ${1} with errors!"
        errorOccurred=true
      } fi
    } fi
  } fi
  
  if [ "${2}" = "monthly" ]; then {
    find ${1}/weekly -maxdepth 1 -type d -mtime +30 -exec rm -r {} ';'
    oldestWeeklyBackupDirectory=$(ls -d ${1}/weekly/* -r | tail -n1)
    outputToEmailAndLog "Using monthly ${oldestWeeklyBackupDirectory}"
    if [ "${oldestWeeklyBackupDirectory}" != "" ]; then {
      mv ${oldestWeeklyBackupDirectory} ${1}/monthly/
      if ! [ $? = 0 ] ; then {
        outputToEmailAndLog "Error: Rotate finished ${1} with errors!"
        errorOccurred=true
      } fi
    } fi
  } fi
  
  # Flush filesystem inode cache
  sync
}

weekday=`date -u +"%u"`
dayOfMonth=`date -u +"%d"`
startDateTime=`date +"%Y%m%d_%H%M%S"`
startDateTimeHumanReadable=`date +"%d.%m.%Y %H:%M:%S"`
errorOccurred=false

if [ "${dayOfMonth}" = "01" ]; then {
  intervalSign=monthly
} elif [ "${weekday}" = "7" ]; then {
  intervalSign=weekly
} else {
  intervalSign=daily
} fi

# Delete old log files older than 14 days
find /media/backup/log/backup_* -mtime +14 -exec rm {} ';'

logFile=/media/backup/log/backup_${startDateTime}_${intervalSign}.log
emailFile=/media/backup/log/backup_${startDateTime}_${intervalSign}.email

# Log start
outputToEmailAndLog "Starting ${intervalSign} backup from /media/data to /media/backup at ${startDateTimeHumanReadable}"

createMissingDirectories /media/backup/samba
rsyncCopy /media/data/samba /media/backup/samba
hardlinkRotate /media/backup/samba ${intervalSign}

createMissingDirectories /media/backup/gitRepository
rsyncCopy /media/data/gitRepository /media/backup/gitRepository
hardlinkRotate /media/backup/gitRepository ${intervalSign}

createMissingDirectories /media/backup/scripts
rsyncCopy /media/data/scripts /media/backup/scripts
hardlinkRotate /media/backup/scripts ${intervalSign}

# Flush filesystem inode cache
sync

# Log end
outputToEmailAndLog "Finished backup at `date +"%d.%m.%Y %H:%M:%S"`"
outputToEmailAndLog ""

# Check disk usage
outputToEmailAndLog "Usage of data disk: $(df /dev/sdc1 -h --output=used | tail -1) of $(df /dev/sdc1 -h --output=size | tail -1) ($(df /dev/sdc1 -h --output=pcent | tail -1))"
outputToEmailAndLog "Usage of backup disk: $(df /dev/sdd1 -h --output=used | tail -1) of $(df /dev/sdd1 -h --output=size | tail -1) ($(df /dev/sdd1 -h --output=pcent | tail -1))"
outputToEmailAndLog ""

if [ -f /var/run/reboot-required ]; then
  outputToEmailAndLog "Ein Neustart des Servers `${HOSTNAME}` ist erforderlich!"
  outputToEmailAndLog ""
fi

# Send email
errorSign=
if [ "${errorOccurred}" = "true" ]; then {
        errorSign=Error:
} fi
mail -s "${errorSign}${HOSTNAME} ${intervalSign} backup" "test@example.com" < ${emailFile}
if [ $? = 0 ]; then {
        outputToLog "Email was sent"
        outputToLog ""
} else {
        outputToLog "Error while sending email"
        outputToLog ""
        exit 1
} fi