--- /dev/null
+pkgbase="bsnap"
+pkgname=("bsnap-borg" "bsnap-zfs" "bsnap-lvm")
+pkgver=1.0.0
+pkgrel=1
+arch=("any")
+source=("backup"
+ "backup.service"
+ "backup.timer"
+ "bsnap-lvm"
+ "setup-root-ssh"
+ "bsnap-zfs"
+ "etc-borg-root-env"
+ "etc-borg-root-exclude"
+ "znap-daily.service"
+ "znap-daily.timer"
+ "znap-frequent.service"
+ "znap-frequent.timer"
+ "znap-hourly.service"
+ "znap-hourly.timer"
+ "znap-take")
+md5sums=('8b910ad468d754fa21f0a89dcab1715e'
+ 'ebc356d63196ff3ea1b3bdc51d00ae3a'
+ 'c0b4804dc26055ef88b3abdbba5f5b7c'
+ '7b7c3a2d6bff8737b9bd1e766e65818b'
+ '6f173fb271b64dfcc84b2536b076b36f'
+ 'ebf29f2525fc5ca671f4c24b90908d11'
+ '05897a7215d9ef75e174e3dec94ca416'
+ '5e2165087fa3d83c0a10f8837750bcfd'
+ 'b233a09cdbd997699e1ba03559ef892a'
+ 'eeafe687944641a401a7f7f902dac73a'
+ '606b981ecbde6f7c90f135e91986b678'
+ '916f9b7a74c4516da6aad084c0c32e3e'
+ '832f2cd78dc266d97d3ad37d11448462'
+ '0624408bb7c90a36e11f89eb64a8923e'
+ '4fa1ebcd4e6f4db9c92d8c1f555975fc')
+
+package_bsnap-borg() {
+ pkgdesc="Borg backup tools"
+ depends=("borg")
+ backup=("etc/borg/root/exclude" "etc/borg/root/env")
+ install="bsnap-borg.install"
+
+ mkdir -p "${pkgdir}/usr/bin/"
+ install -m 0755 backup setup-root-ssh "${pkgdir}/usr/bin/"
+
+ mkdir -p "${pkgdir}/etc/borg/root/"
+ install -m 0644 -T etc-borg-root-exclude "${pkgdir}/etc/borg/root/exclude"
+ install -m 0600 -T etc-borg-root-env "${pkgdir}/etc/borg/root/env"
+
+ mkdir -p "${pkgdir}/usr/lib/systemd/system/"
+ install -m 0644 backup.{timer,service} "${pkgdir}/usr/lib/systemd/system/"
+}
+
+package_bsnap-zfs() {
+ pkgdesc="Backup / snapshot tools for ZFS"
+ depends=("zfs-utils" "zfs-auto-snapshot")
+ conflicts=("bsnap-lvm")
+ install="bsnap-zfs.install"
+
+ mkdir -p "${pkgdir}/usr/bin/"
+ install -m 0755 -T bsnap-zfs "${pkgdir}/usr/bin/bsnap"
+ install -m 0755 znap-take "${pkgdir}/usr/bin/"
+
+ mkdir -p "${pkgdir}/usr/lib/systemd/system/"
+ install -m 0644 znap-*.{timer,service} "${pkgdir}/usr/lib/systemd/system/"
+}
+
+package_bsnap-lvm() {
+ pkgdesc="Backup / snapshot tools for LVM"
+ depends=("lvm2")
+ conflicts=("bsnap-zfs")
+
+ mkdir -p "${pkgdir}/usr/bin/"
+ install -m 0755 -T bsnap-lvm "${pkgdir}/usr/bin/bsnap"
+}
--- /dev/null
+#!/bin/bash
+
+set -eu
+
+if [ "$(id -u)" -ne 0 ]; then
+ echo "Must be root"
+ exit 1
+fi
+
+if pgrep borg > /dev/null; then
+ echo "Borg is still running!"
+ exit 1
+fi
+
+if [ -z "${1:-}" ]; then
+ echo "Usage: $0 root"
+ exit 1
+fi
+
+BACKUP_PROFILE="$1"
+source "/etc/borg/${BACKUP_PROFILE}/env"
+HOME=/root
+
+ionice -c 3 -p $$
+renice -n 19 -p $$ > /dev/null
+
+if [ "$BACKUP_SNAP" -eq 1 ]; then
+ echo ">>> Snapshotting"
+ bsnap on
+fi
+
+cd "$BACKUP_PATH"
+
+echo ">>> Starting backup"
+borg create \
+ $([ -t 0 ] && echo --progress) \
+ --info --stats \
+ --compression lz4 \
+ --exclude-from "/etc/borg/${BACKUP_PROFILE}/exclude" \
+ "${BACKUP_REPO}::{hostname}-{now}" .
+
+echo ">>> Pruning old backups"
+borg prune \
+ --info --stats \
+ --keep-daily=7 \
+ --keep-weekly=4 \
+ --keep-monthly=12 \
+ --prefix='{hostname}-' \
+ "$BACKUP_REPO"
+
+cd /
+
+if [ "$BACKUP_SNAP" -eq 1 ]; then
+ echo ">>> Unsnapshotting"
+ bsnap off
+fi
+
+exit 0
+++ /dev/null
-#!/bin/bash
-
-set -eu
-
-cd ~/backup-mysql/
-
-DATABASES="$(echo 'show databases' | mysql | tail -n+2)"
-for db in ${DATABASES}; do
- mysqldump --add-drop-table --single-transaction "${db}" | \
- gzip -9 > "${db}-$(date '+%Y%m%d').sql.gz"
-
- rm -f $(ls -1 "${db}-"* | sort -r | tail -n +10)
-done
--- /dev/null
+[Unit]
+Description=Borg backup service
+
+[Service]
+Type=oneshot
+ExecStart=/usr/bin/backup root
--- /dev/null
+[Unit]
+Description=Borg backup timer
+
+[Timer]
+OnCalendar=daily
+AccuracySec=86400
+Persistent=true
+
+[Install]
+WantedBy=timers.target
+++ /dev/null
-#!/bin/bash
-
-set -eu
-
-if pgrep borg > /dev/null; then
- echo "Borg is still running!"
- exit 1
-fi
-
-source "$1"
-
-set -x
-
-ionice -c 3 -p $$
-renice -n 19 -p $$ > /dev/null
-
-[ "$BACKUP_SNAP" -eq 1 ] && bsnap on
-
-cd "$BACKUP_PATH"
-
-borg create \
- $([ -t 0 ] && echo --progress) \
- --info --stats \
- --compression lz4 \
- --exclude-from "$BACKUP_EXCLUDE_FILE" \
- "${BACKUP_REPO}::{hostname}-{now}" .
-
-borg prune \
- --info --stats \
- --keep-daily=7 \
- --keep-weekly=4 \
- --keep-monthly=12 \
- --prefix='{hostname}-' \
- "$BACKUP_REPO"
-
-cd /
-
-[ "$BACKUP_SNAP" -eq 1 ] && bsnap off
-
-exit 0
+++ /dev/null
-#!/bin/bash
-
-set -eux
-
-cd "$(dirname "$0")"
-
-install -m 0755 backup /usr/local/bin/
-
-if [ "${1:-}" != "full" ]; then
- exit 0
-fi
-
-BORG_PASSPHRASE="$(dd if=/dev/urandom of=/dev/stdout bs=1 count=15 2>/dev/null | base64)"
-install -m 0644 -T etc-borg-exclude /etc/borg-exclude
-install -m 0600 -T etc-borg-env /etc/borg-env
-sed -i "s|SECRET|$BORG_PASSPHRASE|" /etc/borg-env
-
-install -m 0644 -T etc-cron.d-backup /etc/cron.d/backup
-H=$((16#$(hostname|md5sum|head -c 1) % 7 + 14))
-M=$((16#$(hostname|md5sum|head -c 2) % 60))
-sed -i -e "s|\$H|$H|" -e "s|\$M|$M|" /etc/cron.d/backup
-
-[ -f /root/.ssh/id_rsa.pub ] || ssh-keygen
-cat /root/.ssh/id_rsa.pub
-install -m 0600 -T root-ssh-config /root/.ssh/config
+++ /dev/null
-SHELL=/bin/bash
-PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
-MAILTO=root
-
-# m h dom mon dow user command
- $M $H * * * root chronic backup /etc/borg-env
+++ /dev/null
-Host *
- HashKnownHosts no
-
-Host backuphost
- User borg
- Hostname backup.example.com
--- /dev/null
+#! bash
+
+function post_install {
+ if grep -q "SECRET" /etc/borg/root/env; then
+ echo ">>> Generating passphrase for /etc/borg/root/env"
+ BORG_PASSPHRASE="$(dd if=/dev/urandom of=/dev/stdout bs=1 count=15 2>/dev/null | base64)"
+ sed -i "s|SECRET|$BORG_PASSPHRASE|" "${pkgdir}/etc/borg/root/env"
+ fi
+
+ if ! systemctl is-enabled backup.timer > /dev/null; then
+ echo ">>> You should run: systemctl enable backup.timer"
+ fi
+}
--- /dev/null
+#!/bin/bash
+
+set -eu
+
+function snap {
+ unsnap
+
+ znap-take bsnap 1
+ mount_snapshots
+ mkdir -p /a/boot
+ mount --bind /boot /a/boot
+}
+
+function unsnap {
+ if ! [ -d /a ]; then
+ return
+ fi
+
+ umount /a/boot || true
+ rmdir /a/boot || true
+
+ if ls &> /dev/null /a/*; then
+ umount /a/* || true
+ rmdir /a/* || true
+ fi
+ rmdir /a || true
+}
+
+function list_snapshots {
+ local fsname
+ for fsname in $(zfs list -H -o name); do
+ zfs list -H -o name -S creation -t snapshot -d 1 -r "$fsname" | \
+ grep '@znap_....-..-..-...._bsnap' | \
+ head -n1
+ done
+}
+
+function mount_snapshots {
+ local snapname
+ local fsname
+ local mountpoint
+
+ for snapname in $(list_snapshots); do
+ fsname="${snapname%@*}"
+ mountpoint="/a/$(echo "$fsname" | cut -d/ -f2- | tr '/' '_')"
+ mkdir -p "$mountpoint"
+ mount -t zfs "$snapname" "$mountpoint"
+ done
+}
+
+if [ "$(id -u)" -ne 0 ]; then
+ echo "Must be root"
+ exit 1
+fi
+
+if [ "${1:-}" = "off" ]; then
+ unsnap
+elif [ "${1:-}" = "on" ]; then
+ snap
+else
+ echo "Usage: $0 on|off"
+ exit 1
+fi
--- /dev/null
+#! bash
+
+function post_install {
+ if ! zfs get all -t filesystem | grep -q "backup:"; then
+ echo
+ echo ">>> You'll need to configure ZFS properties for automatic snapshots to work"
+ echo ">>> zfs set backup:frequent=on backup:hourly=on backup:daily=on backup:bsnap=on tank/root"
+ echo
+ echo ">>> Then enable and start some of the znap-*.timer systemd units."
+ fi
+}
BACKUP_SNAP=1
-BACKUP_EXCLUDE_FILE="/etc/borg-exclude"
BACKUP_PATH="/a"
BACKUP_REPO="backuphost:root"
export BORG_PASSPHRASE="SECRET"
sh:**/.thumbnails
sh:**/tmp
-sh:var/cache/apt/*
-sh:var/cache/pacman/*
+sh:var/cache
sh:var/lib/systemd/coredump
+++ /dev/null
-#!/bin/bash
-
-set -eux
-
-cd "$(dirname "$0")"
-
-install -m 0755 bsnap /usr/local/bin/
+++ /dev/null
-#!/bin/bash
-
-for script in /backup/scripts/*; do
- if ! grep -q '^function run_backup' "$script"; then
- continue
- fi
- if [ ! -x "$script" ]; then
- echo "Skipping $script because it is not executable"
- continue
- fi
- output="$("$script" 2>&1)"
- if [ "$?" -ne 0 ]; then
- echo "Failed to run: $script"
- echo "$output"
- fi
-done
-
+++ /dev/null
-#! bash
-
-set -e
-ionice -c 3 -p $$
-renice -n 19 -p $$ > /dev/null
-
-script="$(basename "${BASH_SOURCE[1]}" .sh)"
-
-(
-if ! flock -n -x 200; then
- echo "Failed to get a lock!"
- exit 1
-fi
-
-run_backup
-
-) 200>"/run/lock/backup-${script}"
-
+++ /dev/null
-SHELL=/bin/bash
-PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
-MAILTO=root
-
-# m h dom mon dow user command
- 0 2 * * * root /backup/scripts/backup-all
+++ /dev/null
-#!/bin/bash
-
-function run_backup {
- rdiff-backup \
- --preserve-numerical-ids \
- --exclude-sockets \
- --exclude '/home/*/tmp' \
- --include '/home' \
- --exclude '/var/cache' \
- --exclude '/var/log' \
- --exclude '/var/tmp' \
- --exclude '/tmp' \
- --include '/var' \
- --include '/boot' \
- --exclude-other-filesystems \
- my-host.example.com::/ /backup/my-host
-
- rdiff-backup -v1 --force --remove-older-than 365D /backup/my-host
-}
-
-. /backup/scripts/common
-
+++ /dev/null
-#!/bin/bash
-
-function run_backup {
- rsync -a --numeric-ids --delete my-host.example.com:/backup/my-host/ /backup/my-host/
-}
-
-. /backup/scripts/common
-
--- /dev/null
+#!/bin/bash
+
+if [ "$(id -u)" -ne 0 ]; then
+ echo "Must be root"
+ exit 1
+fi
+
+set -eu
+
+if [ -f /root/.ssh/id_rsa.pub ]; then
+ echo "SSH public key already exists. Exiting!"
+ exit 1
+fi
+
+if [ -f /root/.ssh/config ]; then
+ echo "SSH config already exists. Exiting!"
+ exit 1
+fi
+
+umask 0077
+
+echo ">>> ssh-keygen"
+ssh-keygen
+
+cat /root/.ssh/id_rsa.pub
+
+echo ">>> creating .ssh/config"
+cat > /root/.ssh/config <<EOF
+Host *
+ HashKnownHosts no
+
+Host backuphost
+ User borg
+ Hostname backup.example.com
+EOF
+++ /dev/null
-#!/bin/bash
-
-set -eu
-
-function snap {
- unsnap
-
- znap-list | xargs -n1 znap-mount /a
- mkdir -p /a/boot
- mount --bind /boot /a/boot
-}
-
-function unsnap {
- umount /a/boot || true
- rmdir /a/boot || true
- znap-umount-all /a
-}
-
-if [ "$(id -u)" -ne 0 ]; then
- echo "Must be root"
- exit 1
-fi
-
-if [ "${1:-}" = "off" ]; then
- unsnap
-elif [ "${1:-}" = "on" ]; then
- snap
-else
- echo "Usage: $0 on|off"
- exit 1
-fi
+++ /dev/null
-#!/bin/bash
-
-set -eux
-
-cd "$(dirname "$0")"
-
-install -m 0755 bsnap znap-list znap-mount znap-umount-all /usr/local/bin/
+++ /dev/null
-#!/bin/bash
-zfs get all -s local,received -t filesystem
+++ /dev/null
-#!/bin/bash
-
-set -eu
-
-function root_dataset {
- mount | grep 'on / ' | cut -d' ' -f1
-}
-
-function find_latest_snapshot_timestamp {
- zfs list -H -t snapshot -r "$(root_dataset)" -o name -S creation \
- | grep @znap | head -n1 | cut -d'@' -f2
-}
-
-function list_snapshots {
- local timestamp="$1"
- zfs list -H -t snapshot -o name | grep "$timestamp"
-}
-
-list_snapshots "$(find_latest_snapshot_timestamp)"
+++ /dev/null
-#!/bin/bash
-
-if [ -z "$1" ] || [ -z "$2" ]; then
- echo "Usage: $0 prefix snapshot"
- exit 1
-fi
-
-set -eu
-
-PREFIX="$1"
-SNAPSHOT="$2"
-DATASET="${SNAPSHOT%@*}"
-MOUNTPOINT="${PREFIX}/$(echo "$DATASET" | cut -d@ -f1 | cut -d/ -f2- | tr '/' '_')"
-
-if [ "$(zfs get -H -o value backup:skip "$DATASET")" = "true" ]; then
- exit 0
-fi
-
-set -x
-mkdir -p "$MOUNTPOINT"
-mount -t zfs "$SNAPSHOT" "$MOUNTPOINT"
+++ /dev/null
-#!/bin/bash
-
-if [ -z "$1" ]; then
- echo "Usage: $0 prefix"
- exit 1
-fi
-
-set -eu
-
-PREFIX="$1"
-
-set -x
-
-if ls &> /dev/null "$PREFIX"/*; then
- umount "$PREFIX"/* || true
- rmdir "$PREFIX"/* || true
-fi
-rmdir "$PREFIX" || true
--- /dev/null
+[Unit]
+Description=ZFS daily snapshot service
+
+[Service]
+Type=oneshot
+ExecStart=/usr/bin/znap-take daily 30
--- /dev/null
+[Unit]
+Description=ZFS daily snapshot timer
+
+[Timer]
+OnCalendar=daily
+Persistent=true
+
+[Install]
+WantedBy=timers.target
--- /dev/null
+[Unit]
+Description=ZFS frequent snapshot service
+
+[Service]
+Type=oneshot
+ExecStart=/usr/bin/znap-take frequent 10
--- /dev/null
+[Unit]
+Description=ZFS frequent snapshot timer
+
+[Timer]
+OnCalendar=*:0/5
+Persistent=true
+
+[Install]
+WantedBy=timers.target
--- /dev/null
+[Unit]
+Description=ZFS hourly snapshot service
+
+[Service]
+Type=oneshot
+ExecStart=/usr/bin/znap-take hourly 12
--- /dev/null
+[Unit]
+Description=ZFS hourly snapshot timer
+
+[Timer]
+OnCalendar=hourly
+Persistent=true
+
+[Install]
+WantedBy=timers.target
--- /dev/null
+#!/bin/bash
+
+set -eu
+
+if [ -z "${1:-}" ] || [ -z "${2:-}" ]; then
+ echo "Usage: $0 label keep"
+ exit 1
+fi
+
+label="$1"
+keep="$2"
+
+zfs list -H -o "backup:${label},name" | \
+ grep '^on ' | \
+ cut -d ' ' -f2 | \
+ xargs -d '\n' zfs-auto-snapshot --prefix=znap --label="$label" --keep="$keep"