#!/usr/bin/bash # Written in 2020 by Max Christian Pohle # check for a configuration file and source it in if its there test -f /etc/default/systemd-zfs-partition-backup &&\ source /etc/default/systemd-zfs-partition-backup DISK_NAME=$1 logger -p debug "disk: $DISK_NAME" function beep() { test -f /usr/bin/beep &&\ env -u SUDO_GID -u SUDO_COMMAND -u SUDO_USER -u SUDO_UID /usr/bin/beep $@ } function fail() { # logger -p debug -t "$0" "Backup '$1' -> '$2' failed." logger -p debug "$@" beep -f 40 -l 500 # sudo -u max beep -f 523 -l 150 -n -f 54 -l 600 } function start() { UNIT_NAME=$(systemd-escape --suffix=automount $1) systemctl start "$UNIT_NAME" || (logger -p info "$UNIT_NAME not found." ; exit 1) } function stop() { UNIT_NAME=$(systemd-escape --suffix=automount $1) systemctl stop "$UNIT_NAME" || (logger -p info "$UNIT_NAME not found." ; exit 1) } # we assume, that we can mount the device somewhere under media start "media/$DISK_NAME" # without leading slash for the unit file DATASETS_WITH_DISKNAME=($(zfs list -H -o name | grep $DISK_NAME)) if [ ${#DATASETS_WITH_DISKNAME[@]} -eq 0 ]; then fail "No dataset found with '$DISK_NAME' as last part of its name."\ "Please create such if you want to backup that partition." exit 0 fi logger -p debug "${#DATASETS_WITH_DISKNAME[@]} Datasets: $DATASETS_WITH_DISKNAME" # iterate over all datasets for i in ${DATASETS_WITH_DISKNAME[@]}; do logger -p debug "assertion: "$i" =~ .*/$DISK_NAME" # compare the 'tail' of the PATH against the disk name... if [[ $i =~ .*/$DISK_NAME ]]; then DISK_MOUNTPOINT="/media/$DISK_NAME" if ! findmnt $DISK_MOUNTPOINT; then logger -p debug "Expected to find disk '$DISK_NAME' mounted under '$DISK_MOUNTPOINT'. But it is not there. Aborting backup." beep -f 40 -l 500 exit 1 fi DATASET=$i DATASET_LAST_SNAPSHOT=$(zfs list -t snap -o name -s creation "$DATASET" | tail -n1) ||\ (fail "we could not find a previous backup. Backup may still be possible (first time backup?)") ZFS_MOUNTPOINT=$(zfs get mountpoint -H -o value "$DATASET") ||\ (fail "could not determine mountpoint for dataset '$DATASET'"; exit 1) # sanity check: is it really mounted? findmnt -no source "$ZFS_MOUNTPOINT" ||\ (fail "Dataset $DATASET is not mounted at $ZFS_MOUNTPOINT" ; exit 1) logger -p debug "Backup from '$DISK_MOUNTPOINT' -> '$ZFS_MOUNTPOINT' possible. Starting..." beep -f 147 -l 120 -n -f 294 -l 200 -n -f 831 set -x rsync -ahoi -x --delete --info=all \ "$DISK_MOUNTPOINT/" "$ZFS_MOUNTPOINT/" \ 2>&1 | tee $ZFS_MOUNTPOINT/backup-report.txt set +x zfs snap $DATASET@$(date +%Y-%m-%d--%H-%M-%S) ||\ (fail "Backup successful, but a snapshot of the dataset '$DATASET' was impossible :(") if [ -n "$SYSTEMD_ZFS_PARTITION_BACKUP_DIFF_FILENAME" ]; then zfs diff $DATASET_LAST_SNAPSHOT > $ZFS_MOUNTPOINT_VERIFIED/"$SYSTEMD_ZFS_PARTITION_BACKUP_DIFF_FILENAME" if [ -n "$SYSTEMD_ZFS_PARTITION_BACKUP_EMAIL" ]; then logger -p debug "Here comes a list of changes since your last backup: $LAST_SNAPSHOT" |\ mailx -A typesafe -a $ZFS_MOUNTPOINT_VERIFIED/"$SYSTEMD_ZFS_PARTITION_BACKUP_DIFF_FILENAME" -s 'Backup overview' "$SYSTEMD_ZFS_PARTITION_BACKUP_EMAIL" fi fi beep -f 831 -l 120 -n -f 294 -l 200 -n -f 147 else logger -p error "assertion failed: [[ $i =~ .*/$DISK_NAME ]] => false" fi done stop "media/$DISK_NAME" # without leading slash for the unit file