#!/bin/bash # Backup info export BACKUP_DRIVE_UUID={{ disk.uuid }} export BACKUP_DRIVE={{ disk.name }} export BACKUP_DRIVE_PASSWORD={{ disk.password }} # For notifications export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/{{ notifications.user.uid }}/bus export USER={{ notifications.user.name }} export SOURCE_DIR=${SOURCE_DIR:=/} # Fix basename / showing up as "/" -> change to "root" if [ $(basename $SOURCE_DIR) = / ]; then export SNAPSHOT_PREFIX=${SNAPSHOT_PREFIX:=root} else export SNAPSHOT_PREFIX=${SNAPSHOT_PREFIX:=$(basename $SOURCE_DIR)} fi # Set snapshot prefix based on basename export SNAPSHOT_TIME=$(date +"%y_%m_%d-%H.%M") export SNAPSHOT_NAME=$SNAPSHOT_PREFIX-$SNAPSHOT_TIME export SNAPSHOT_DIR=${SNAPSHOT_DIR:=/.snapshots} export LATEST=$SNAPSHOT_PREFIX-latest export BACKUP_DRIVE=backup0 export BACKUP_DRIVE_MNT=${BACKUP_DRIVE_MNT:=/mnt/$BACKUP_DRIVE} export BACKUP_DIR=${BACKUP_DIR:=/mnt/$BACKUP_DRIVE/$(hostname)} # Show snapshot settings echo "SOURCE_DIR: $SOURCE_DIR" echo "SNAPSHOT_PREFIX: $SNAPSHOT_PREFIX" echo "SNAPSHOT_TIME: $SNAPSHOT_TIME" echo "SNAPSHOT_NAME: $SNAPSHOT_NAME" echo "SNAPSHOT_DIR: $SNAPSHOT_DIR" echo "LATEST: $LATEST" echo "BACKUP_DRIVE_MNT: $BACKUP_DRIVE_MNT" echo "BACKUP_DIR: $BACKUP_DIR" # Create readonly snapshot btrfs subvolume snapshot -r $SOURCE_DIR $SNAPSHOT_DIR/$SNAPSHOT_NAME # Attempt to mount backup disk # Check if backup disk is mounted mountpoint $BACKUP_DRIVE_MNT if [ $? != 0 ]; then cryptsetup luksClose $BACKUP_DRIVE cryptsetup luksOpen /dev/disk/by-uuid/$BACKUP_DRIVE_UUID $BACKUP_DRIVE --key-file=$BACKUP_DRIVE_PASSWORD mount $BACKUP_DRIVE_MNT fi # Check if backup disk is mounted mountpoint $BACKUP_DRIVE_MNT if [ $? = 0 ]; then # backup drive mounted if [ ! -d $BACKUP_DIR ]; then mkdir -p $BACKUP_DIR fi # Sync latest backups # The "latest" symlinks can get out of sync for a variety of reasons, # including backups run while the disk is unplugged. # They must be kept in sync so that snapshots are sent with a parent # that actually exists on the backup drive # # In order to keep them in sync we'll do the following: # 1. Check the -latest on the backup drive matches # -latest in the snapshot directory # 2. If they don't match, assume the backup drive has the correct # snapshot. Replace the -latest symlink in the snapshots # directory with the one from the backup drive # # First check if the symlinks exist if [ -L $SNAPSHOT_DIR/$LATEST ] && [ -L $BACKUP_DIR/$LATEST ]; then # Get the actual name of the latest backup LATEST_SNAPSHOT=$(basename $(readlink $SNAPSHOT_DIR/$LATEST)) LATEST_BACKUP=$(basename $(readlink $BACKUP_DIR/$LATEST)) # If the latest backups don't match if [ $LATEST_SNAPSHOT != $LATEST_BACKUP ] then echo "Detected drift. Synchronizing latest snapshot with backup. Set to $LATEST_BACKUP." # Remove and replace the snapshot directory's latest rm $SNAPSHOT_DIR/$LATEST ln -s $SNAPSHOT_DIR/$LATEST_BACKUP $SNAPSHOT_DIR/$LATEST fi fi # If we have a latest, use it as the parent if [ -L $BACKUP_DIR/$LATEST ]; then btrfs send -p $SNAPSHOT_DIR/$LATEST $SNAPSHOT_DIR/$SNAPSHOT_NAME | btrfs receive $BACKUP_DIR else btrfs send $SNAPSHOT_DIR/$SNAPSHOT_NAME | btrfs receive $BACKUP_DIR fi # Update latest in backup dir rm -f $BACKUP_DIR/$LATEST ln -s $BACKUP_DIR/$SNAPSHOT_NAME $BACKUP_DIR/$LATEST sudo -E -u $USER notify-send "Backup complete" "Snapshot $SNAPSHOT_NAME completed successfully" else sudo -E -u $USER notify-send "Backup failed" "$BACKUP_DRIVE_MNT not mounted. Snapshot $SNAPSHOT_NAME not synced" -u critical fi # Update latest in snapshot dir rm -f $SNAPSHOT_DIR/$LATEST ln -s $SNAPSHOT_DIR/$SNAPSHOT_NAME $SNAPSHOT_DIR/$LATEST