2021-03-04 14:21:35 +01:00
|
|
|
#!/bin/bash
|
|
|
|
|
|
|
|
# ZSC-Sync - zfs snapshot cron sync
|
|
|
|
# Usage: <script> <remote> <remote_dataset>
|
|
|
|
|
|
|
|
ZSC_CONFIG="$HOME/.zsc"
|
|
|
|
|
|
|
|
REMOTE=$1
|
|
|
|
if test -z "$REMOTE"; then
|
|
|
|
echo "No remote specified"
|
|
|
|
exit -1
|
|
|
|
fi
|
|
|
|
|
|
|
|
REMOTE_ROOT_DATASET=$2
|
|
|
|
if test -z "$REMOTE_ROOT_DATASET"; then
|
|
|
|
echo "No remote root dataset specified"
|
|
|
|
exit -1
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
### LOCK ###
|
2021-03-08 12:21:51 +01:00
|
|
|
echo "Locking $0"
|
|
|
|
if ! lockfile-create -p -r 2 "$0"; then
|
2021-03-04 14:21:35 +01:00
|
|
|
echo "Another backup is running, aborting"
|
|
|
|
exit 49
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
2021-03-08 12:56:08 +01:00
|
|
|
echo "Fetching remote snapshots..."
|
|
|
|
remote_snapshots=$(ssh $REMOTE zfs list -H -t snapshot -o name -s creation)
|
|
|
|
|
|
|
|
echo "Fetching subdatasets..."
|
|
|
|
remote_subdatasets=$(ssh $REMOTE zfs list -H -o name -s creation -r "$REMOTE_ROOT_DATASET")
|
|
|
|
|
|
|
|
echo "Fetching resume tokens..."
|
|
|
|
resume_tokens=$(ssh $REMOTE zfs get -H -p receive_resume_token -o name,value -t filesystem)
|
|
|
|
|
|
|
|
|
2021-03-08 12:21:09 +01:00
|
|
|
### Resume existing transfer ###
|
|
|
|
# Returns 0 if no action was performed, 1 if a transfer was resumed and finished successfully, -1 if there was an error during resumed transfer
|
|
|
|
resume_transfer() {
|
|
|
|
remote_dataset=$1
|
|
|
|
|
2021-03-08 12:56:08 +01:00
|
|
|
RESUME_TOKEN=$(echo -e "$resume_tokens" | grep -P "^$remote_dataset\t.*\$" | cut -d$'\t' -f2)
|
2021-03-08 12:21:09 +01:00
|
|
|
if [ "$RESUME_TOKEN" != "" ] && [ "$RESUME_TOKEN" != "-" ]; then
|
|
|
|
echo "Resuming interrupted transfer..."
|
|
|
|
zfs send -v -t "$RESUME_TOKEN" | ssh $REMOTE zfs receive -F -s "$remote_dataset" || return -1
|
|
|
|
echo "Resumed transfer finished."
|
|
|
|
return 1
|
|
|
|
fi
|
|
|
|
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-03-04 14:21:35 +01:00
|
|
|
handle_dataset() {
|
|
|
|
DATASET=$1
|
2021-03-04 15:22:33 +01:00
|
|
|
RECURSIVE=$2
|
|
|
|
D=$3
|
|
|
|
W=$4
|
|
|
|
M=$5
|
|
|
|
Y=$6
|
2021-03-04 14:21:35 +01:00
|
|
|
|
2021-03-04 15:22:33 +01:00
|
|
|
PREFIX=$7
|
2021-03-04 14:21:35 +01:00
|
|
|
if test -z "$PREFIX"; then
|
|
|
|
PREFIX="zsc-"
|
|
|
|
fi
|
|
|
|
|
|
|
|
if test "!no_prefix!" = "$PREFIX"; then
|
|
|
|
PREFIX=""
|
|
|
|
fi
|
|
|
|
|
2021-03-08 12:56:08 +01:00
|
|
|
TARGET_REMOTE_DATASET="$REMOTE_ROOT_DATASET/"$(echo -e "$DATASET" | sed -r "s/\//_/g")
|
2021-03-04 14:21:35 +01:00
|
|
|
|
2021-03-04 15:22:33 +01:00
|
|
|
echo "[$DATASET -> $TARGET_REMOTE_DATASET]"
|
|
|
|
echo -e
|
2021-03-04 14:21:35 +01:00
|
|
|
|
|
|
|
|
2021-03-08 12:21:09 +01:00
|
|
|
resume_transfer "$TARGET_REMOTE_DATASET" || return 0
|
2021-03-04 14:21:35 +01:00
|
|
|
|
|
|
|
|
|
|
|
### Sync existing snapshots ###
|
2021-03-08 12:56:08 +01:00
|
|
|
REMOTE_SNAPSHOTS=($(echo -e "$remote_snapshots" | grep "$TARGET_REMOTE_DATASET@" | cut -d"@" -f 2))
|
|
|
|
LOCAL_SNAPSHOTS=($(zfs list -H -t snapshot -o name -s creation "$DATASET" | cut -d"@" -f 2))
|
2021-03-04 14:21:35 +01:00
|
|
|
|
|
|
|
echo "Remote snapshots: ${#REMOTE_SNAPSHOTS[@]}"
|
|
|
|
echo "Local snapshots: ${#LOCAL_SNAPSHOTS[@]}"
|
2021-03-04 15:22:33 +01:00
|
|
|
echo -e
|
2021-03-04 14:21:35 +01:00
|
|
|
|
|
|
|
for local_snapshot in "${LOCAL_SNAPSHOTS[@]}"; do
|
|
|
|
found=false
|
|
|
|
for remote_snapshot in "${REMOTE_SNAPSHOTS[@]}"; do
|
|
|
|
if [ "$remote_snapshot" = "$local_snapshot" ] ; then
|
|
|
|
found=true
|
|
|
|
break
|
|
|
|
fi
|
|
|
|
done
|
|
|
|
|
|
|
|
if [ $found = true ] ; then
|
2021-03-08 12:56:08 +01:00
|
|
|
echo "> $local_snapshot found remotely"
|
2021-03-04 14:21:35 +01:00
|
|
|
FROM_SNAPSHOT=$local_snapshot
|
|
|
|
TO_SNAPSHOT=""
|
|
|
|
else
|
2021-03-08 12:56:08 +01:00
|
|
|
echo "> $local_snapshot needs sync"
|
2021-03-04 14:21:35 +01:00
|
|
|
TO_SNAPSHOT=$local_snapshot
|
|
|
|
fi
|
|
|
|
done
|
|
|
|
|
2021-03-08 12:56:08 +01:00
|
|
|
echo "Snapshots are synchronized up to $FROM_SNAPSHOT"
|
2021-03-04 15:22:33 +01:00
|
|
|
echo -e
|
2021-03-04 14:21:35 +01:00
|
|
|
|
|
|
|
if test ! -z "$TO_SNAPSHOT"; then
|
|
|
|
echo "Synchronizing snapshots from $FROM_SNAPSHOT to $TO_SNAPSHOT ..."
|
|
|
|
|
|
|
|
if test ! -z "$FROM_SNAPSHOT"; then
|
|
|
|
incr="-I $FROM_SNAPSHOT"
|
|
|
|
fi
|
2021-03-04 15:22:33 +01:00
|
|
|
if [ $RECURSIVE = true ] ; then
|
2021-03-04 14:21:35 +01:00
|
|
|
recu="-R"
|
|
|
|
fi
|
2021-03-04 15:22:33 +01:00
|
|
|
zfs send -v -p $recu $incr "$DATASET@$TO_SNAPSHOT" | ssh $REMOTE zfs receive -F -s "$TARGET_REMOTE_DATASET" || return 1
|
2021-03-04 14:21:35 +01:00
|
|
|
|
|
|
|
echo "Done"
|
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
2021-03-07 15:50:48 +01:00
|
|
|
init_subdataset() {
|
|
|
|
DATASET=$1
|
|
|
|
PARENT_DATASET=$2
|
|
|
|
|
|
|
|
TARGET_REMOTE_DATASET="$REMOTE_ROOT_DATASET/"$(echo -e $PARENT_DATASET | sed -r "s/\//_/g")"/"${DATASET:$(echo -e "$PARENT_DATASET" | wc -c)}
|
|
|
|
|
|
|
|
|
2021-03-08 12:21:09 +01:00
|
|
|
resume_transfer "$TARGET_REMOTE_DATASET" || return 0
|
2021-03-07 15:50:48 +01:00
|
|
|
|
2021-03-08 12:56:08 +01:00
|
|
|
if [ $(echo -e "$remote_subdatasets" | grep -e "^$TARGET_REMOTE_DATASET\$" | wc -l) -eq 0 ]; then
|
2021-03-07 15:50:48 +01:00
|
|
|
echo "Subdataset not found on $TARGET_REMOTE_DATASET, initializing..."
|
|
|
|
|
|
|
|
LOCAL_SNAPSHOT=($(zfs list -H -t snapshot -o name -s creation "$DATASET" | cut -d"@" -f 2 | tail -n 1))
|
|
|
|
|
|
|
|
if test -z "$LOCAL_SNAPSHOT"; then
|
|
|
|
echo "No local snapshot"
|
|
|
|
return 1
|
|
|
|
fi
|
|
|
|
|
|
|
|
echo "[$DATASET@$LOCAL_SNAPSHOT -> $TARGET_REMOTE_DATASET]"
|
|
|
|
zfs send -v -p "$DATASET@$LOCAL_SNAPSHOT" | ssh $REMOTE zfs receive -F -s "$TARGET_REMOTE_DATASET" || return 1
|
|
|
|
|
|
|
|
echo -e "Done"
|
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
2021-03-04 15:22:33 +01:00
|
|
|
|
|
|
|
for line in $(cat "$ZSC_CONFIG") ; do
|
|
|
|
IFS=: read dataset recursive d w m y prefix <<< "$line"
|
2021-03-04 14:21:35 +01:00
|
|
|
echo "| ##### Dataset: $dataset Recursive: $recursive Prefix: $prefix d: $d w: $w m: $m y: $y #####"
|
|
|
|
|
|
|
|
if [ $recursive = "true" ]; then
|
|
|
|
SUBDATASETS=($(zfs list -o name | grep "$dataset/"))
|
|
|
|
echo -e "| Subdatasets: ${SUBDATASETS[@]}"
|
|
|
|
echo -e '|'
|
2021-03-07 15:50:48 +01:00
|
|
|
for p in "${SUBDATASETS[@]}"; do
|
|
|
|
init_subdataset $p $dataset | awk '{print "| " $0}' || exit 1
|
|
|
|
done
|
2021-03-04 14:21:35 +01:00
|
|
|
fi
|
|
|
|
|
2021-03-04 15:22:33 +01:00
|
|
|
handle_dataset $dataset $recursive $d $w $m $y $prefix | awk '{print "| " $0}' || exit 1
|
2021-03-04 14:21:35 +01:00
|
|
|
|
|
|
|
echo -e "----------------------------------------------------------------------"
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
|
|
### UNLOCK ###
|
2021-03-08 12:21:51 +01:00
|
|
|
echo "Unlocking $0"
|
|
|
|
lockfile-remove "$0"
|