249 lines
8.4 KiB
Bash
Executable file
249 lines
8.4 KiB
Bash
Executable file
#!/bin/bash
|
|
|
|
startDate=$(date +%s)
|
|
|
|
function bkRun {
|
|
local date=$(date +%Y-%m-%d)
|
|
local month=$(date +%Y-%m)
|
|
local bkSuffix=("${INCR[0]}" "${FULL[0]}")
|
|
local keepTime=("${INCR[1]}" "${FULL[1]}")
|
|
for i in ${!SUBVOLS[@]}; do
|
|
snapName=${VOLNAME[$i]}.$date
|
|
getSnap "$i"
|
|
if [[ -d "$SNAPDIR$snapName" && "$force" != true ]]; then
|
|
echo "backup already run today"
|
|
continue
|
|
else
|
|
btrfs subvolume snapshot -r ${SUBVOLS[$i]} $SNAPDIR$snapName
|
|
sync
|
|
fi
|
|
if [[ -d "$snapLast" && $1 = "incremental" ]]; then
|
|
btrfs send -p $snapLast $SNAPDIR$snapName | gpg --batch -er "$PUBKEY" -o - | \
|
|
rclone rcat $RCLONEDIR$month"-${INCR[0]}/"$snapName.pgp
|
|
else
|
|
btrfs send $SNAPDIR$snapName | gpg --batch -er "$PUBKEY" -o - | \
|
|
rclone rcat $RCLONEDIR$month"-${FULL[0]}/"$snapName.pgp
|
|
if [[ $1 = "full" ]]; then
|
|
for o in ${!bkSuffix[@]}; do
|
|
mapfile -t oldBk < <(rclone lsf $RCLONEDIR | grep ....-..-${bkSuffix[$o]} | head -n -${keepTime[$o]})
|
|
for n in ${!oldBk[@]}; do
|
|
rclone delete $RCLONEDIR${oldBk[$n]}
|
|
done
|
|
done
|
|
fi
|
|
fi
|
|
mapfile -t oldSnaps < <(ls -a $SNAPDIR | grep ${VOLNAME[$i]}.\....-..- | head -n -$KEEPLOCAL)
|
|
for snap in ${!oldSnaps[@]}; do
|
|
btrfs subvolume delete $SNAPDIR${oldSnaps[$snap]}
|
|
done
|
|
done
|
|
rclone sync /etc/btrbk.conf $RCLONEDIR"config/"
|
|
end
|
|
}
|
|
|
|
function runRestore {
|
|
echo "not implemented yet™"
|
|
exit 1
|
|
if [ -z ${rDate+x} ]; then
|
|
for o in ${!VOLNAME[@]}; do
|
|
if [[ "${VOLNAME[$o]}" = "${restoreVol}" ]]; then
|
|
getSnap "$o"
|
|
break
|
|
elif [[ ! "${VOLNAME[@]}" =~ "${restoreVol}" ]]; then
|
|
echo "Subvolume doesnt exist"
|
|
exit 1
|
|
fi
|
|
done
|
|
# mkdir /mnt/$BTRFSUUID
|
|
# mount -o subvolid=1 -U $BTRFSUUID /mnt/$BTRFSUUID
|
|
# btrfs subvolume delete /mnt/$BTRFSUUID/
|
|
fi
|
|
}
|
|
|
|
function runFullRestore {
|
|
[[ "${restoreDir}" != */ ]] && restoreDir="${restoreDir}/"
|
|
if ! btrfs subvolume list "$restoreDir" > /dev/null 2>&1; then
|
|
echo "destination is not a valid btrfs"
|
|
exit 1
|
|
fi
|
|
read -e -r -p "Do You want to run rclone config?[y/N]: " rcloneConf
|
|
case "$rcloneConf" in
|
|
[yY]) rclone config;;
|
|
*) ;;
|
|
esac
|
|
echo "Specify the remote path [remote:/path/to/backupdir]"
|
|
while true; do
|
|
read -e -r -p "Dir: " rcloneDir
|
|
[[ "${rcloneDir}" != */ ]] && rcloneDir="${rcloneDir}/"
|
|
if ! rclone lsf "$rcloneDir" > /dev/null 2>&1; then
|
|
echo "Not a valid directory"
|
|
continue
|
|
else
|
|
break
|
|
fi
|
|
done
|
|
if ! [ -e "/etc/btrbk.conf" ]; then
|
|
rclone copy $rcloneDir"config/btrbk.conf" "/tmp/"
|
|
fi
|
|
source /tmp/btrbk.conf
|
|
read -e -r -p "Import GPG key?[y/N]: " gpgtest
|
|
case "$gpgtest" in
|
|
[yY])
|
|
echo "Enter Path of your gpg secret key"
|
|
while true; do
|
|
read -e -r -p "Secretkey: " gpgPriv
|
|
[ ! -f "$gpgPriv" ] && echo "Not a File" && continue
|
|
while true; do
|
|
read -e -r -s -p "Passphrase: " gpgPass
|
|
gpg --pinentry-mode=loopback --passphrase "$gpgPass" --import $gpgPriv > /dev/null 2>&1 && break
|
|
echo "Wrong Passphrase or file is not a valid secret key"
|
|
done
|
|
break
|
|
done
|
|
echo "${PUBKEY}:6:" | gpg --import-ownertrust;;
|
|
*)
|
|
echo "Enter gpg Passphrase"
|
|
while true; do
|
|
read -e -s -r -p "Passphrase: " gpgPass
|
|
echo | gpg --local-user "$PUBKEY" --pinentry-mode=loopback --passphrase "$gpgPass" -as > /dev/null 2>&1 && \
|
|
echo "Passphrase correct" && break
|
|
echo "Wrong Passphrase"
|
|
done;;
|
|
esac
|
|
local bkSuffix=("${FULL[0]}" "${INCR[0]}")
|
|
btrfs subvolume create $restoreDir"restore" 2>/dev/null
|
|
lastBkDate=$(rclone lsf "$rcloneDir" | grep ....-..-${FULL[0]} | tail -n -1 | sed "s/full\/$//")
|
|
for i in ${!bkSuffix[@]}; do
|
|
mapfile -t bkFile < <(rclone lsf "$rcloneDir$lastBkDate${bkSuffix[$i]}" 2>/dev/null)
|
|
for o in ${!bkFile[@]}; do
|
|
rclone cat "$rcloneDir$lastBkDate${bkSuffix[$i]}/${bkFile[$o]}" | \
|
|
gpg --batch --pinentry-mode=loopback --passphrase "$gpgPass" -d | \
|
|
btrfs receive $restoreDir"restore"
|
|
done
|
|
done
|
|
for i in ${!VOLNAME[@]}; do
|
|
newestSnap=$(ls -a $restoreDir"restore" | grep ${VOLNAME[$i]}.\....-..- | tail -n -1 )
|
|
btrfs subvolume snapshot $restoreDir"restore/"$newestSnap $restoreDir${VOLNAME[$i]}
|
|
done
|
|
read -e -r -p "Delete restore subvolume?[Y/n]: " delOldVol
|
|
case $delOldVol in
|
|
[nN]) ;;
|
|
*)
|
|
mapfile -t delVol < <(ls $restoreDir"restore")
|
|
for i in ${!delVol[@]}; do
|
|
btrfs subvolume delete $restoreDir"restore/"${delVol[$i]}
|
|
done
|
|
btrfs subvolume delete $restoreDir"restore";;
|
|
esac
|
|
btrfs subvolume create $restoreDir"snapshots"
|
|
end
|
|
}
|
|
|
|
function getSnap {
|
|
for i in $1; do
|
|
snapLast=$SNAPDIR${VOLNAME[$i]}.$(date -d "$date -1 day" +%Y-%m-%d)
|
|
if ! [ -d "$snapLast" ]; then
|
|
snapLast=$SNAPDIR$(ls -a $SNAPDIR | grep ${VOLNAME[$i]}.$(date -d "$date" +%Y-%m)- | tail -n 1)
|
|
fi
|
|
done
|
|
}
|
|
|
|
function testConf {
|
|
source /etc/btrbk.conf
|
|
[[ "${RCLONEDIR}" != */ ]] && RCLONEDIR="${RCLONEDIR}/"
|
|
[[ "${SNAPDIR}" != */ ]] && SNAPDIR="${SNAPDIR}/"
|
|
if [ -z ${rDate+x} ]; then
|
|
if ! date -d "$rDate" > /dev/null 2>&1; then
|
|
echo "date is in wrong format: YYYY-MM-DD"
|
|
exit 1
|
|
fi
|
|
fi
|
|
if rclone ls $RCLONEDIR > /dev/null 2>&1; then
|
|
if gpg --list-key $PUBKEY > /dev/null 2>&1; then
|
|
if [ "$restore" = true ]; then
|
|
runRestore
|
|
elif [ "$backup" = true ]; then
|
|
testDate
|
|
fi
|
|
else
|
|
echo "pubkey wrong or not found"
|
|
exit 1
|
|
fi
|
|
else
|
|
echo "rclone dir wrong or not found"
|
|
exit 1
|
|
fi
|
|
exit 1
|
|
}
|
|
|
|
function testDate {
|
|
if [ "$BKCYCLE" = "weekly" ]; then
|
|
local dateType="+%u"
|
|
elif [ "$BKCYCLE" = "monthly" ]; then
|
|
local dateType="+%-d"
|
|
else
|
|
echo "BKCYCLE not correctly configured"
|
|
exit 1
|
|
fi
|
|
for i in ${!BKDAY[@]}; do
|
|
if [ `date $dateType` = ${BKDAY[$i]} ]; then
|
|
bkRun "full"
|
|
fi
|
|
done
|
|
bkRun "incremental"
|
|
}
|
|
|
|
function testRoot {
|
|
if ! [ $(id -u) = 0 ]; then
|
|
echo "Please run with root privileges"
|
|
exit 2
|
|
fi
|
|
}
|
|
|
|
function end {
|
|
echo "finished in $(( ($(date +%s) - startDate) / 60 )) min"
|
|
exit 0
|
|
}
|
|
|
|
function echoHelp {
|
|
echo "Usage: btrbk [options]"
|
|
echo "A script to automatically or manually backup your btrfs subvolumes."
|
|
echo "options:"
|
|
echo " -b, --backup runs backup manually if not already run today"
|
|
echo " -f, --force force backup to override today's backup"
|
|
echo " --date=YYYY-MM-DD define a date from which to restore when using -r"
|
|
echo " --restore=VOLNAME restores the latest snapshot of a given subvolume"
|
|
echo " --full-restore=PATH this walks you through an complete config restore"
|
|
echo " and will restore the whole system to a given directory"
|
|
echo " -h, --help display this help and exit"
|
|
exit 1
|
|
}
|
|
|
|
options=$(getopt -l "backup,force,date::,restore::,full-restore::,help" -o "bfh" -- "$@")
|
|
eval set -- "$options"
|
|
for i in ${!options[@]}; do
|
|
case $1 in
|
|
-b|--backup) backup=true; shift;;
|
|
-f|--force) force=true; shift;;
|
|
--date) rDate="$2"; shift;;
|
|
--restore) restore=true; restoreVol=$2; shift;;
|
|
--full-restore) fullRestore=true; restoreDir=$2; shift;;
|
|
-h|--help) echoHelp; shift; break;;
|
|
--) shift; break;;
|
|
\?) echo "error"; exit 1;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
if [ "$fullRestore" = true ]; then
|
|
testRoot
|
|
runFullRestore
|
|
elif [ "$backup" = true ] || [ "$restore" = true ]; then
|
|
testRoot
|
|
testConf
|
|
else
|
|
echo "No valid option given."
|
|
echo "Try 'btrbk --help' for more information."
|
|
exit 2
|
|
fi
|