#!/bin/zsh -f

# user definable parameters
# days: how many of the latest backups should be kept
days=30
backupdir="/backup/config"
store="${backupdir}/store"
logfile="${backupdir}/backup.log"
datecmd=(date --rfc-2822)

umask 0077

# parameters: path_to_target (-|basename_of_backup_files) [options to the "find" command]
function do_backup() {
  target="$1"
  if [[ "${2:--}" == "-" ]]; then
    # we take the substring of ${target} starting with the second character
    # (since it surely starts with a slash that we do not want to see
    # in the filename)
    # and replace all slashes with underscores
    basename="`hostname`__${target[2,-1]:gs./._}"
  else
    basename="$2"
  fi
  find_params=(${*[3,-1]})
  no_print=1
  for i in "${find_params[@]}"; do
    if [[ "${i}" == -*print* ]]; then
      no_print=0
      break
    fi
  done
  if [[ ${no_print} -eq 1 ]]; then
    find_params=("${find_params[@]}" "-print")
  fi
  echo "
`${datecmd[@]}`
Target: ${target}
Find parameters: ${find_params[@]}" >> "${logfile}"
  if [[ -f "${target}" ]]; then
    if [[ ! -r "${target}" ]]; then
      echo "Target is a file that the current process cannot read from. Backup of target failed." >> "${logfile}"
      return
    fi
  else
    if [[ -d "${target}" ]]; then
      if [[ ! -x "${target}" || ! -r "${target}" ]]; then
        echo "Target is a directory that the current process cannot read from or search in. Backup of target failed." >> "${logfile}"
        return
      fi
    else
      echo "Target is not an existing file or directory that the current process could read from. Backup of target failed." >> "${logfile}"
      return
    fi
  fi
  backupfile="${backupdir}/${basename}__`date +%Y-%m-%d`.tar.bz2"
  if [[ -f "${backupfile}" ]]; then
    rm -f "${backupfile}" 2>> "${logfile}" || {echo "Backup file already exists and could not be deleted. Backup of target failed." >> "${logfile}"; return}
  fi
  {find "${target}" ${find_params[@]} | nice -n +19 tar --create --absolute-names --no-recursion --files-from - --bzip2 --file "${backupfile}"} 2>> "${logfile}"
  [[ ! -f "${backupfile}" ]] && {echo "Failed to create backup."; return}
  if [[ "`date +%d`" == "01" ]]; then
    if [[ ! -d "${store}" || ! -w "${store}" ]]; then
      echo "The store directory (${store}) does not exist or the current process cannot write into it." >> "${logfile}"
    else
      echo "Today is the first day of the month. Making a copy of the backup in ${store} ..." >> "${logfile}"
      cp "${backupfile}" "${store}" >> "${logfile}" 2>&1 || echo "Failed to make a copy of the backup in ${store}." >> "${logfile}"
    fi
  fi

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


[[ ! -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}"


# List of backups
# Parameters: path_to_target (-|basename_of_backup_files) [options to the "find" command]
# Note: the "-print" parameter is optional.
# (It is automatically appended during backup if it is not specified.)

do_backup /etc -


echo "
End of backup at: `${datecmd[@]}`
" >> "${logfile}"

