From 76e0da0ef07a921e27b12a447692d1e952d922f4 Mon Sep 17 00:00:00 2001 From: Max Christian Pohle Date: Sat, 11 Jul 2020 14:11:07 +0200 Subject: Spring cleaning --- .gitignore | 4 ++ backup/backup.sh | 75 ---------------------- backup/systemd/max-backup@.service | 8 --- backup/udev/rules.d/99-max-autobackup.rules | 2 - systemd-zfs-partition-backup.sh | 75 ++++++++++++++++++++++ systemd-zfs-partition-backup/AUR/PKGBUILD | 24 +++++++ .../systemd/max-backup@.service | 7 ++ .../udev/rules.d/99-max-autobackup.rules | 2 + zfs-flatten.sh | 67 ------------------- zfs-squash-datasets.sh | 67 +++++++++++++++++++ 10 files changed, 179 insertions(+), 152 deletions(-) create mode 100644 .gitignore delete mode 100755 backup/backup.sh delete mode 100644 backup/systemd/max-backup@.service delete mode 100644 backup/udev/rules.d/99-max-autobackup.rules create mode 100755 systemd-zfs-partition-backup.sh create mode 100644 systemd-zfs-partition-backup/AUR/PKGBUILD create mode 100644 systemd-zfs-partition-backup/systemd/max-backup@.service create mode 100644 systemd-zfs-partition-backup/udev/rules.d/99-max-autobackup.rules delete mode 100644 zfs-flatten.sh create mode 100755 zfs-squash-datasets.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e42ceac --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +**.tar.xz +**.tar.bz2 +**/AUR/pkg/ +**/AUR/src/ diff --git a/backup/backup.sh b/backup/backup.sh deleted file mode 100755 index 91acdfc..0000000 --- a/backup/backup.sh +++ /dev/null @@ -1,75 +0,0 @@ -#!/usr/bin/bash -logger "================================================================================" - -# BACKUP_NAME=$ID_SERIAL-part$ID_PART_ENTRY_NUMBER -BACKUP_NAME=$(echo $1 | sed -e 's/\\x2d/-/g') -MOUNT_TARGET=/media/$BACKUP_NAME - -function fail() { - logger -s -t "$0" "Backup '$1' -> '$2' failed." - sudo -u max beep -f 40 -l 500 - # sudo -u max beep -f 523 -l 150 -n -f 54 -l 600 -} - -function start() { - DEVNAME=$(echo $1 | sed 's/-/\\x2d/') - systemctl restart media-$DEVNAME.automount - - logger -t "$0" "Backup '$1' -> '$2' started." - sudo -u max beep -f 147 -l 120 -n -f 294 -l 200 -n -f 831 -} - -function stop() { - DEVNAME=$(echo $1 | sed 's/-/\\x2d/') - systemctl stop media-$DEVNAME.automount - - logger -t "$0" "Backup '$1' -> '$2' finished." - sudo -u max beep -f 831 -l 120 -n -f 294 -l 200 -n -f 147 -} - -function zfs_snapshot() { - zfs diff $LAST_SNAPSHOT > $ZFS_PATH/backup-differences.txt - zfs snap media-backup/$BACKUP_NAME@$(date +%Y-%m-%d--%H-%M) - echo "Here comes a list of changes since your last backup: $LAST_SNAPSHOT" | mailx -A typesafe -a $ZFS_PATH/backup-differences.txt -s 'Backup overview' max@entwicklerseite.de -} - -start $1 - -if ! findmnt $MOUNT_TARGET; then - logger "$MOUNT_TARGET is not mounted. Cannot back it up." - exit 0 -fi - -logger ">> Inspecting $BACKUP_NAME" -ZFS_PATH=$(zfs get mountpoint -H -o value media-backup/$BACKUP_NAME) || fail "$MOUNT_TARGET" "(no zfs dataset found)" -ZFS_MOUNTPATH=$(findmnt -no source $ZFS_PATH) || fail "$ZFS_PATH (not mounted)" "(no association)" -LAST_SNAPSHOT=$(zfs list -t snap -o name -s creation media-backup/$BACKUP_NAME | tail -n1) - - -# Make sure, that we are really writing where a mountpoint is -if [ "$ZFS_PATH" == "/$ZFS_MOUNTPATH" ]; then - sudo -u max beep -f 831 - logger "We can start $MOUNT_TARGET -> $ZFS_PATH" - rsync -ahoi -x --delete --info=all \ - "/media/$BACKUP_NAME/" \ - "$ZFS_PATH" \ - 2>&1 | tee $ZFS_PATH/backup-report.txt && zfs_snapshot - -else - fail $ZFS_PATH $ZFS_MOUNTPATH - exit 1 -fi - - - -stop $1 - -# sleep a second so that /dev/disk/by-id/*-part become available -# sleep 1 - -# DISK=$(readlink -f /dev/disk/by-id/$ID_BUS-$ID_SERIAL) -# if ! [ -e "$DISK" ]; then -# fail -# logger "$DISK: Disk not there :(" -# exit 1 -# fi diff --git a/backup/systemd/max-backup@.service b/backup/systemd/max-backup@.service deleted file mode 100644 index 44c6bf8..0000000 --- a/backup/systemd/max-backup@.service +++ /dev/null @@ -1,8 +0,0 @@ -[Unit] -Description=Generic backup - -[Service] -Type=simple -ExecStart=/home/max/backup/backup.sh %I - - diff --git a/backup/udev/rules.d/99-max-autobackup.rules b/backup/udev/rules.d/99-max-autobackup.rules deleted file mode 100644 index e17bba0..0000000 --- a/backup/udev/rules.d/99-max-autobackup.rules +++ /dev/null @@ -1,2 +0,0 @@ -ACTION=="add", ENV{ID_PART_ENTRY_NUMBER}=="[0-9]", ENV{SYSTEMD_WANTS}="max-backup@$env{ID_SERIAL}\\x2dpart$env{ID_PART_ENTRY_NUMBER}.service" -LABEL="External disk drive backup" diff --git a/systemd-zfs-partition-backup.sh b/systemd-zfs-partition-backup.sh new file mode 100755 index 0000000..91acdfc --- /dev/null +++ b/systemd-zfs-partition-backup.sh @@ -0,0 +1,75 @@ +#!/usr/bin/bash +logger "================================================================================" + +# BACKUP_NAME=$ID_SERIAL-part$ID_PART_ENTRY_NUMBER +BACKUP_NAME=$(echo $1 | sed -e 's/\\x2d/-/g') +MOUNT_TARGET=/media/$BACKUP_NAME + +function fail() { + logger -s -t "$0" "Backup '$1' -> '$2' failed." + sudo -u max beep -f 40 -l 500 + # sudo -u max beep -f 523 -l 150 -n -f 54 -l 600 +} + +function start() { + DEVNAME=$(echo $1 | sed 's/-/\\x2d/') + systemctl restart media-$DEVNAME.automount + + logger -t "$0" "Backup '$1' -> '$2' started." + sudo -u max beep -f 147 -l 120 -n -f 294 -l 200 -n -f 831 +} + +function stop() { + DEVNAME=$(echo $1 | sed 's/-/\\x2d/') + systemctl stop media-$DEVNAME.automount + + logger -t "$0" "Backup '$1' -> '$2' finished." + sudo -u max beep -f 831 -l 120 -n -f 294 -l 200 -n -f 147 +} + +function zfs_snapshot() { + zfs diff $LAST_SNAPSHOT > $ZFS_PATH/backup-differences.txt + zfs snap media-backup/$BACKUP_NAME@$(date +%Y-%m-%d--%H-%M) + echo "Here comes a list of changes since your last backup: $LAST_SNAPSHOT" | mailx -A typesafe -a $ZFS_PATH/backup-differences.txt -s 'Backup overview' max@entwicklerseite.de +} + +start $1 + +if ! findmnt $MOUNT_TARGET; then + logger "$MOUNT_TARGET is not mounted. Cannot back it up." + exit 0 +fi + +logger ">> Inspecting $BACKUP_NAME" +ZFS_PATH=$(zfs get mountpoint -H -o value media-backup/$BACKUP_NAME) || fail "$MOUNT_TARGET" "(no zfs dataset found)" +ZFS_MOUNTPATH=$(findmnt -no source $ZFS_PATH) || fail "$ZFS_PATH (not mounted)" "(no association)" +LAST_SNAPSHOT=$(zfs list -t snap -o name -s creation media-backup/$BACKUP_NAME | tail -n1) + + +# Make sure, that we are really writing where a mountpoint is +if [ "$ZFS_PATH" == "/$ZFS_MOUNTPATH" ]; then + sudo -u max beep -f 831 + logger "We can start $MOUNT_TARGET -> $ZFS_PATH" + rsync -ahoi -x --delete --info=all \ + "/media/$BACKUP_NAME/" \ + "$ZFS_PATH" \ + 2>&1 | tee $ZFS_PATH/backup-report.txt && zfs_snapshot + +else + fail $ZFS_PATH $ZFS_MOUNTPATH + exit 1 +fi + + + +stop $1 + +# sleep a second so that /dev/disk/by-id/*-part become available +# sleep 1 + +# DISK=$(readlink -f /dev/disk/by-id/$ID_BUS-$ID_SERIAL) +# if ! [ -e "$DISK" ]; then +# fail +# logger "$DISK: Disk not there :(" +# exit 1 +# fi diff --git a/systemd-zfs-partition-backup/AUR/PKGBUILD b/systemd-zfs-partition-backup/AUR/PKGBUILD new file mode 100644 index 0000000..f9d7315 --- /dev/null +++ b/systemd-zfs-partition-backup/AUR/PKGBUILD @@ -0,0 +1,24 @@ +# Maintainer: Max Christian Pohle +pkgname=systemd-zfs-partition-backup +pkgver=2020_07_11 +# release number, always starts with 1 +pkgrel=1 +pkgdesc="automatically backup usb* drives to zfs datasets, encryption supported" +arch=('any') +url="http://git.entwicklerseite.de/zfs-auto-backup" +license=('MIT') +depends=( + 'systemd' + 'kbd' + 'mkinitcpio' +) +source=("http://git.entwicklerseite.de/zfs-bash-tools/snapshot/zfs-bash-tools-master.tar.bz2") +sha256sums=('a734c145709687c5621f430d3baec3105977984dfbb35348beab4b793aeb8e1b') + +package() { + install -D zfs-bash-tools-master/${pkgname}.sh ../pkg/${pkgname}/usr/bin/${pkgname}.sh + install -D zfs-bash-tools-master/${pkgname}/systemd/max-backup\@.service ../pkg/${pkgname}/usr/lib/systemd/system/${pkgname}.service + install -D zfs-bash-tools-master/${pkgname}/udev/rules.d/99-max-autobackup.rules ../pkg/${pkgname}/etc/udev/rules.d/99-${pkgname}.rules +} + +# vim: ft=bash diff --git a/systemd-zfs-partition-backup/systemd/max-backup@.service b/systemd-zfs-partition-backup/systemd/max-backup@.service new file mode 100644 index 0000000..d7948a8 --- /dev/null +++ b/systemd-zfs-partition-backup/systemd/max-backup@.service @@ -0,0 +1,7 @@ +[Unit] +Description=Generic backup + +[Service] +Type=simple +ExecStart=/usr/bin/systemd-zfs-partition-backup.sh %I + diff --git a/systemd-zfs-partition-backup/udev/rules.d/99-max-autobackup.rules b/systemd-zfs-partition-backup/udev/rules.d/99-max-autobackup.rules new file mode 100644 index 0000000..e17bba0 --- /dev/null +++ b/systemd-zfs-partition-backup/udev/rules.d/99-max-autobackup.rules @@ -0,0 +1,2 @@ +ACTION=="add", ENV{ID_PART_ENTRY_NUMBER}=="[0-9]", ENV{SYSTEMD_WANTS}="max-backup@$env{ID_SERIAL}\\x2dpart$env{ID_PART_ENTRY_NUMBER}.service" +LABEL="External disk drive backup" diff --git a/zfs-flatten.sh b/zfs-flatten.sh deleted file mode 100644 index c61af40..0000000 --- a/zfs-flatten.sh +++ /dev/null @@ -1,67 +0,0 @@ -DATASET_ROOT=$1 -DATASET_TARGET=$2 - -MOUNTPOINT_TARGET=$(zfs get -H mountpoint -o value $DATASET_TARGET) - -if ! test -d "$MOUNTPOINT_TARGET"; then - echo "Please run this program with a source and a target dataset name as arguments" - echo "e.g. $0 zpool/dataset1 zpool/dataset2" - exit -fi - -# make sure, that ctrl-c also works in the inner loops -trap exit INT - -# recursively contains all snapshot names (the part after the @) from root on, sorted and only once -SNAPSHOTS=$(zfs list -H -r $DATASET_ROOT -oname -tsnap | cut -d@ -f2 | sort | uniq) -DATASETS=$(zfs list -H -r $DATASET_ROOT -oname -t filesystem) - -echo "We are going to flatten the hierachy of this dataset:" -printf " %s\n" $DATASETS -echo "into the dataset with the name:" -echo " $DATASET_TARGET" -echo "mounted under:" -echo " $MOUNTPOINT_TARGET" -echo "" -echo "You have 10 seconds to abort this with CTRL-C" -sleep 10 - -for SNAPSHOT in $SNAPSHOTS; do - echo "" - echo "" - echo "===========================" - echo "Next snapshot is: $SNAPSHOT" - echo "===========================" - - for DATASET in $DATASETS; do - - MOUNTPOINT=$(zfs get -H mountpoint -o value $DATASET) - - if [[ $MOUNTPOINT == "legacy" ]]; then - MOUNTPOINT=$(findmnt -n -o target $DATASET) - if test -d $MOUNTPOINT; then - >&2 echo "Dataset has a legacy mountpoint, but was not found in the fstab: $DATASET" - continue - fi - fi - - if [[ $MOUNTPOINT == "-" ]]; then - >&2 echo "Dataset does not have a mount point or is a vdev: $DATASET" - continue - fi - - SNAPDIR=$(realpath $MOUNTPOINT/.zfs/snapshot/$SNAPSHOT) - if test -d $SNAPDIR; then - TARGET_DIR="$(realpath $MOUNTPOINT_TARGET)$(realpath $MOUNTPOINT)" - mkdir -p "$TARGET_DIR" - echo rsync --info=stats3 -a "$SNAPDIR/" "$TARGET_DIR/" - if ! rsync --info=stats3 -a "$SNAPDIR/" "$TARGET_DIR/" ; then - >&2 echo "rsync could not entirely copy from $SNAPDIR to $DATASET_TARGET" - fi - fi - done - NEW_SNAPSHOT="$DATASET_TARGET@$SNAPSHOT" - echo "Files copied, creating snapshot: $NEW_SNAPSHOT" - zfs snap $NEW_SNAPSHOT -done - diff --git a/zfs-squash-datasets.sh b/zfs-squash-datasets.sh new file mode 100755 index 0000000..c61af40 --- /dev/null +++ b/zfs-squash-datasets.sh @@ -0,0 +1,67 @@ +DATASET_ROOT=$1 +DATASET_TARGET=$2 + +MOUNTPOINT_TARGET=$(zfs get -H mountpoint -o value $DATASET_TARGET) + +if ! test -d "$MOUNTPOINT_TARGET"; then + echo "Please run this program with a source and a target dataset name as arguments" + echo "e.g. $0 zpool/dataset1 zpool/dataset2" + exit +fi + +# make sure, that ctrl-c also works in the inner loops +trap exit INT + +# recursively contains all snapshot names (the part after the @) from root on, sorted and only once +SNAPSHOTS=$(zfs list -H -r $DATASET_ROOT -oname -tsnap | cut -d@ -f2 | sort | uniq) +DATASETS=$(zfs list -H -r $DATASET_ROOT -oname -t filesystem) + +echo "We are going to flatten the hierachy of this dataset:" +printf " %s\n" $DATASETS +echo "into the dataset with the name:" +echo " $DATASET_TARGET" +echo "mounted under:" +echo " $MOUNTPOINT_TARGET" +echo "" +echo "You have 10 seconds to abort this with CTRL-C" +sleep 10 + +for SNAPSHOT in $SNAPSHOTS; do + echo "" + echo "" + echo "===========================" + echo "Next snapshot is: $SNAPSHOT" + echo "===========================" + + for DATASET in $DATASETS; do + + MOUNTPOINT=$(zfs get -H mountpoint -o value $DATASET) + + if [[ $MOUNTPOINT == "legacy" ]]; then + MOUNTPOINT=$(findmnt -n -o target $DATASET) + if test -d $MOUNTPOINT; then + >&2 echo "Dataset has a legacy mountpoint, but was not found in the fstab: $DATASET" + continue + fi + fi + + if [[ $MOUNTPOINT == "-" ]]; then + >&2 echo "Dataset does not have a mount point or is a vdev: $DATASET" + continue + fi + + SNAPDIR=$(realpath $MOUNTPOINT/.zfs/snapshot/$SNAPSHOT) + if test -d $SNAPDIR; then + TARGET_DIR="$(realpath $MOUNTPOINT_TARGET)$(realpath $MOUNTPOINT)" + mkdir -p "$TARGET_DIR" + echo rsync --info=stats3 -a "$SNAPDIR/" "$TARGET_DIR/" + if ! rsync --info=stats3 -a "$SNAPDIR/" "$TARGET_DIR/" ; then + >&2 echo "rsync could not entirely copy from $SNAPDIR to $DATASET_TARGET" + fi + fi + done + NEW_SNAPSHOT="$DATASET_TARGET@$SNAPSHOT" + echo "Files copied, creating snapshot: $NEW_SNAPSHOT" + zfs snap $NEW_SNAPSHOT +done + -- cgit v1.2.3