# -*- mode: sh; sh-basic-offset: 3; indent-tabs-mode: nil; -*-
# vim: set filetype=sh sw=3 sts=3 expandtab autoindent:
#
# borg handler script for backupninja
# requires borgbackup
#
# Guillaume Subiron, Sysnove, 2016
#
# Copyright 2016 Guillaume Subiron <guillaume@sysnove.fr>
#
# This work is free. You can redistribute it and/or modify it under the
# terms of the Do What The Fuck You Want To Public License, Version 2,
# as published by Sam Hocevar. See the http://www.wtfpl.net/ file for more details.
#
#

debug "export BORG_RELOCATED_REPO_ACCESS_IS_OK=yes"
export BORG_RELOCATED_REPO_ACCESS_IS_OK=yes

debug "export BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK=yes"
export BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK=yes

### GET CONFIG ###

getconf testconnect yes
getconf nicelevel 0
getconf ionicelevel
getconf bwlimit

setsection source
getconf init yes
getconf include
getconf exclude
getconf create_options
getconf prune yes
getconf keep 30d
getconf prune_options
getconf cache_directory
getconf ignore_missing

setsection dest
getconf user
getconf host
getconf port
getconf directory
# strip trailing /
directory=${directory%/}
getconf archive {now:%Y-%m-%dT%H:%M:%S}
getconf compression lz4
getconf encryption none
getconf passphrase
getconf sshoptions

debug "export BORG_PASSPHRASE=\"<redacted>\""
export BORG_PASSPHRASE="$passphrase"

if [ -n "$sshoptions" ]; then
   debug "export BORG_RSH=\"ssh $sshoptions\""
   export BORG_RSH="ssh $sshoptions"
fi

### CHECK CONFIG ###

# source includes at least one path
[ -n "$include" ] || fatal "No source includes specified"

# destination specific checks
[ -n "$directory" ] || fatal "Destination directory not set"
if [ "$host" != "localhost" ]; then
   [ -n "$user" ] || fatal "Destination user not set"
   [ -n "$host" ] || fatal "Destination host not set"
  execstr_repository="ssh://${user}@${host}${port:+:${port}}${directory}"
else
  execstr_repository="$directory"
fi
execstr_archive="$archive"

if [ -n "$cache_directory" ]; then
   cache_parent_dir=$(dirname "$(readlink -f "$cache_directory")")
   [ -d "$cache_parent_dir" ] || fatal "Cache directory parent dir '$cache_parent_dir' is absent or is not a directory."
   debug "export BORG_CACHE_DIR=\"${cache_directory}\""
   export BORG_CACHE_DIR="$cache_directory"
else
   # Cache dir not set, let's clear out the environment variable to avoid
   # having this directory be pointed to a random destination.
   # Also apparently if we set the variable to an empty string, borg uses the
   # empty string as though it was some path we specified and backup runs
   # error out, so we need to unset the variable completely.
   debug "unset BORG_CACHE_DIR"
   unset BORG_CACHE_DIR
fi

# Check that the ionicelevel is valid
if [ -n "$ionicelevel" ] && echo "$ionicelevel" | grep -vq "^[0-7]$"; then
   fatal "The value of ionicelevel is expected to be either empty or an integer from 0 to 7. Got: $ionicelevel"
fi

# Only use ionice if ionicelevel is not empty
nice="nice -n $nicelevel"
if [ -n "$ionicelevel" ]; then
   nice="ionice -c2 -n $ionicelevel $nice"
fi

# check the connection at the source and destination
[ -n "$test" ] || test=0
if [ "$host" != "localhost" ] && ([ "$testconnect" = "yes" ] || [ "${test}" -eq 1 ]); then
   debug "ssh $sshoptions -o PasswordAuthentication=no ${host}${port:+ -p ${port}} -l $user 'echo -n 1'"
   local ret=`ssh $sshoptions -o PasswordAuthentication=no ${host}${port:+ -p ${port}} -l $user 'echo -n 1'`
   if [ "$ret" = 1 ]; then
      debug "Connected to $host as $user successfully"
   else
      teststr="borg list --show-rc -v $execstr_repository"
      debug "$teststr"
      output=`su -c "$teststr" 2>&1`
      if echo "$output" | grep "terminating with success status" \
         || echo "$output" | grep "^\S\+ is not a valid repository." \
         || echo "$output" | grep "^Repository \S\+ does not exist."; then
         debug "Connected to $host as $user successfully (forced command)"
      else
         error $output
         fatal "Can't connect to $host as $user."
      fi
   fi
fi

### INIT IF NEEDED ###

if [ "$init" == "yes" ]; then
   initstr="borg init --encryption=$encryption $execstr_repository"
   debug "executing borg init"
   debug "$initstr"
   if [ $test = 0 ]; then
      output="`su -c "$initstr" 2>&1`"
      if [ $? = 2 ]; then
         debug $output
         info "Repository was already initialized"
      else
         warning $output
         warning "Repository has been initialized"
      fi
   fi
fi

### EXECUTE ###

execstr="borg create --stats --compression $compression"

set -o noglob

# includes
SAVEIFS=$IFS
IFS=$(echo -en "\n\b")
for i in $include; do
   includes="${includes} '$i'"
done
IFS=$SAVEIFS

# excludes
SAVEIFS=$IFS
IFS=$(echo -en "\n\b")
for i in $exclude; do
   excludes="${excludes} --exclude '$i'"
done
IFS=$SAVEIFS

set +o noglob

if [ ! -z $bwlimit ]; then
   execstr="${execstr} --remote-ratelimit=${bwlimit}"
fi

if [ ! -z "$create_options" ]; then
   execstr="${execstr} ${create_options}"
fi

# include client-part and server-part
execstr="${execstr} ${excludes} $execstr_repository::$execstr_archive ${includes}"

debug "executing borg create"
debug "$nice $execstr"

if [ $test = 0 ]; then
   output=`$nice su -c "$execstr" 2>&1`
   ret=$?
   if [ $ret = 0 ]; then
      debug $output
      info "Successfully finished backing up source."
   elif [ $ret = 1 ]; then
      warnmsg=$(echo "$output" | /usr/bin/sed -n '1,/^-\+$/{x;p;d;}; x' | /usr/bin/sed '/^$/d')
      if [ "$ignore_missing" = "yes" ] && ! echo "$warnmsg" | grep -qv '\[Errno 2\] No such file or directory:'; then
         debug $output
         info "Backing up source finished with missing file warnings."
      else
         warning $output
         warning "Backing up source finished with warnings."
      fi
   else
      error $output
      fatal "Failed backing up source."
   fi
fi

### REMOVE OLD BACKUPS ###

# borg prune
if [ "$prune" == "yes" ]; then
   if [ ! "$keep" == "0" ]; then
      prune_options="${prune_options} --keep-within=${keep}"
   fi
   prunestr="borg prune $prune_options $execstr_repository"
   debug "executing borg prune"
   debug "$prunestr"
   if [ $test = 0 ]; then
      output="`su -c "$prunestr" 2>&1`"
      ret=$?
      if [ $ret = 0 ]; then
         debug $output
         info "Removing old backups succeeded."
      elif [ $ret = 1 ]; then
         warning $output
         warning "Removing old backups finished with warnings."
      else
         error $output
         fatal "Failed removing old backups."
      fi
   fi
fi

debug "unset BORG_PASSPHRASE"
unset BORG_PASSPHRASE

debug "unset BORG_RSH"
unset BORG_RSH

return 0
