diff --git a/zfsbk.sh b/zfsbk.sh new file mode 100755 index 0000000..20c1c26 --- /dev/null +++ b/zfsbk.sh @@ -0,0 +1,116 @@ +#! /bin/sh + +# make a backup collection having 1 full and N-1 incremental backups. +# set to 1 to avoid incrementals and always take full dumps. +max_incremental=4 +zpool="zroot" +destination_store="u84693@u84693.your-backup.de:calaf/" + +usage () { + echo "Usage:" + echo "zfsbk.sh [numincr]" + echo "Takes & sends backup with given name. If snapshots with" + echo "this name exist, backup incrementally. Limit incremental" + echo "backup sequences to 'numincr' steps (default 1 = alw. full)." + echo + echo "Optional envvars:" + echo "UPLOAD_PATH upload generated backup to this path. scp:// or rsync://" +} + +# first argument: name of backup set +if [ $# -lt 1 ] +then + usage + exit 1 +fi + +bkname=$1 +if ! echo $bkname | grep -qiE '^[a-z0-9-]+$' +then + echo "Given bkname is invalid. Want alphanumeric and hyphens." + exit 1 +fi +if echo "$2" | grep -qE '^[1-9][0-9]*$' +then + max_incremental=$2 +fi + +export PATH=$PATH:/usr/local/bin + +# list snapshots by decreasing time. Arguments: [name] restrict listing to this tag +list_named_snaps () { + local bkname=$1 + snapdir="/.zfs/snapshot" + for i in $snapdir/zbk-$bkname-* + do + test -e "$i" && (echo $i |sed "s@^$snapdir/@@") + done | sort -r +} + +# remove all datasets for a given tag. Arguments: $1 -> bkname +reset_sequence () { + local bkname=$1 + list_named_snaps $bkname | while read snapname + do + zfs destroy -r $zpool@$snapname + done +} + +# take snapshot +#echo ./zfssnap.sh $bkname $max_incremental +if ! ./zfssnap.sh $bkname $max_incremental +then + echo "Error creating snapshot! Code $?" + exit 1 +fi + +# get label +num_snaps=`list_named_snaps $bkname | awk 'END {print NR}'` +label_latest=`list_named_snaps $bkname | awk NR==1` + +# decide what to do based on how many snapshots were found +if [ $num_snaps -eq 0 ] +then + # no snapshots. Something's wrong + echo "Error, no snapshots found for $bkname!" + list_named_snaps $bkname + exit 1 +elif [ $num_snaps -eq 1 ] +then + # first snapshot. Send full + bkfile="/backups/${label_latest}.dump" + #echo zfs send -R $zpool@$label_latest + zfs send -R $zpool@$label_latest 2>/dev/null > $bkfile +else + # n-th snapshot. Send incrementally + label_2ndlatest=`list_named_snaps $bkname | awk NR==2` + bkfile="/backups/${label_latest}--${label_2ndlatest}.dump" + #echo zfs send -i $label_2ndlatest -R $zpool@$label_latest + zfs send -i $label_2ndlatest -R $zpool@$label_latest 2>/dev/null > $bkfile +fi + +if [ $num_snaps -eq $max_incremental ] +then + # finished the incremental sequence. Take full backup next time + echo "Sequence of $max_incremental increm steps complete. Resetting." + reset_sequence $bkname +fi + + +# upload backup to remote location +if [ "x$UPLOAD_PATH" != x ] +then + #echo "Archiving $bkfile remotely..." + if echo "$UPLOAD_PATH" | grep -qE '^rsync://' + then + rsync -qz $bkfile ${UPLOAD_PATH#rsync://} + elif echo "$UPLOAD_PATH" | grep -qE '^scp://' + then + scp -BCq $bkfile ${UPLOAD_PATH#scp://} + else + echo "UPLOAD_PATH not understood! '$UPLOAD_PATH'" + echo "Expecting rsync://.. or scp://.." + exit 1 + fi +fi +