#!/bin/zsh -f

# user definable parameters
# days: for how many days should backups be kept
days=30
targetlvol="/dev/vol01/mysql"
backupdir="/backup/mysql"
# Important!
#   The mysql user (that is supplied by the username and password parameters)
#   must have the RELOAD privilege to be able to issue a global lock on all
#   tables (via a "FLUSH TABLES WITH READ LOCK").
mysql_username="backup_user"
mysql_password="password"
backup_basename="myserver__mysql"
tmplvolname="backup_snapshot_lvol"
storedir="${backupdir}/store"
logfile="${backupdir}/backup.log"
datecmd=(date --rfc-2822)

umask 0077

[[ ! -d "${backupdir}" || ! -r "${backupdir}" || ! -x "${backupdir}" || ! -w "${backupdir}" ]] && {echo "Backup directory (${backupdir}) does not exist or is not readable+writeable+searchable by current process. Backup failed." >&2; exit 1}
touch "${logfile}" 2> /dev/null
[[ ! -w "${logfile}" ]] && {echo "Error (${0:t}): cannot write to logfile (${logfile})!" >&2; exit 1}

echo "**********************************
Starting backup at: `${datecmd[@]}`" >> "${logfile}"

if [[ -z "${backup_basename}" ]]; then
  backup_basename="$(hostname --long)"
fi

echo "
`${datecmd[@]}`
Parameters:
  mysql username=${mysql_username}
  mysql password=${(l:${#mysql_password}::*:)}
  targetlvol=${targetlvol}
  backup basename=${backup_basename}" >> "${logfile}"

[[ -z "${mysql_username}" || -z "${mysql_password}" || -z "${targetlvol}" || -z "${backup_basename}" ]] && {echo "Not enough parameters!" >> "${logfile}" } && return

backupfile="${backupdir}/${backup_basename}__$(date +%Y-%m-%d)"
if [[ -f "${backupfile}.tar.bz2" ]]; then
  rm -f "${backupfile}.tar.bz2" 2>> "${logfile}" || {echo "Backup file already exists and could not be deleted. Backup of target failed." >> "${logfile}"; return}
fi

echo "Locking database ..." >> "${logfile}"
mysql --user="${mysql_username}" --password="${mysql_password}" >> "${logfile}" 2>&1 <<EOF1
flush tables with read lock;
flush logs;
EOF1
db_locked=1
if lvdisplay "${targetlvol}" > /dev/null 2>&1; then
  echo "Creating LVM snapshot ..." >> "${logfile}"
  if lvcreate --snapshot --size=500M --name="${tmplvolname}" "${targetlvol}" >> "${logfile}" 2>&1; then
    echo "Unlocking database ..." >> "${logfile}"
    mysql --user="${mysql_username}" --password="${mysql_password}" >> "${logfile}" 2>&1 <<EOF2
unlock tables;
EOF2
    db_locked=0
    mntdir="/tmp/backup${RANDOM}"
    spath="$(dirname "${targetlvol}")/${tmplvolname}"
    if mkdir "${mntdir}" >> "${logfile}" 2>&1; then
      if mount "${spath}" "${mntdir}" >> "${logfile}" 2>&1; then
        echo "Creating archive ..." >> "${logfile}"
        cd "${mntdir}" >> "${logfile}" 2>&1
        nice -n +19 tar cjf "${backupfile}.tar.bz2" ./* >> "${logfile}" 2>&1
        cd /
        umount "${mntdir}" >> "${logfile}" 2>&1
      else
        echo "Failed to mount the snapshot." >> "${logfile}"
      fi
      rmdir "${mntdir}" >> "${logfile}" 2>&1
    else
      echo "Failed to create mount point directory." >> "${logfile}"
    fi
    echo "Removing snapshot ..." >> "${logfile}"
    lvremove -f "${spath}" >> "${logfile}" 2>&1
  else
    echo "Failed to create snapshot of ${targetlvol}." >> "${logfile}"
  fi
else
  echo "No LVM logical volume was found at ${targetlvol}." >> "${logfile}"
fi
if [[ ${db_locked} -gt 0 ]]; then
  mysql --user="${mysql_username}" --password="${mysql_password}" >> "${logfile}" 2>&1 <<EOF3
unlock tables;
EOF3
fi
[[ ! -f "${backupfile}.tar.bz2" ]] && {echo "Failed to create backup." >> "${logfile}"; return}
if [[ "$(date +%d)" == "01" && -d "${storedir}" && -f "${backupfile}.tar.bz2" ]]; then
  echo "Today is the first day of the month. Making a copy of the backup in ${storedir} ..." >> "${logfile}"
  cp "${backupfile}.tar.bz2" "${storedir}" >> "${logfile}" 2>&1
fi

echo "
Cleaning up old backups ..." >> "${logfile}"
[[ ${days} -gt 0 ]] && ls -1t "${backupdir}/${backup_basename}__"*".tar.bz2" | tail -n +$((days+1)) | xargs -n 1 -r sh -c 'echo "$@ was deleted." && rm "$@"' delete_backup >> "${logfile}" 2>&1
echo "
End of backup at: $(${datecmd[@]})
" >> "${logfile}"


