#!/bin/bash
#
# This is a simple backup script using borg. It's supposed to serve as a
# starting point and to be adjusted to your system.
#
# Important steps:
# - define a host "backup" in root's .ssh/config
# - read the script and adjust to your needs
# - As root run `borg init -v --encryption=keyfile backup:borg-$HOSTNAME`
# (note that zsh uses $HOST instead of $HOSTNAME)
# - If you want, increase the max_segment_size in
# ssh://backup/borg-$HOSTNAME/config from the default 5MiB
#
# Copyright ©2014-2017 Florian Pritz
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
# See gpl-3.0.txt for full license text.
set -e
main() {
if [[ $UID != 0 ]]; then
exec sudo "$0" "$@"
fi
TMPDIR="$(mktemp -d "/tmp/${0##*/}.XXXXXX")"
trap "rm -rf '${TMPDIR}'" EXIT TERM
# these mountpoints will be excluded. mountpoints not listed in either this
# or the includeMountpoints variable below will throw an error
excludeMountpoints=(
/tmp
/sys
/dev
/proc
/run
/mnt/levant/nfs
/media
)
# these mountpoints will be included
includeMountpoints=(
/
/boot
/home
/mnt/data
)
# list of patterns that should be excluded. This supports shell globbing as
# well as regex pattern. Refer to man borg for details.
IFS='' read -r -d '' excludeList_borg < "$TMPDIR/exclude-list-borg"
# save some data that's useful for restores
local backupDataDir=/root/backup-data/
mkdir -p "$backupDataDir"
fdisk -l > "$backupDataDir/fdisk"
vgdisplay > "$backupDataDir/vgdisplay"
pvdisplay > "$backupDataDir/pvdisplay"
lvdisplay > "$backupDataDir/lvdisplay"
df -a > "$backupDataDir/df"
findmnt -l > "$backupDataDir/findmnt"
# TODO: create snapshots here if possible. Sadly that's really difficult so
# it will probably not be implemented
local backupdir="borg-$HOSTNAME"
backup_borg / "backup:$backupdir"
ssh backup "touch $backupdir/last-backup-timestamp"
}
backup_borg() {
local src=$1
local dst=$2
local -a options=(
--verbose
--numeric-owner
--compression lz4
--stats
--exclude-from "$TMPDIR/exclude-list-borg"
)
if tty -s; then
options+=(--progress)
fi
borg create "${options[@]}" "$dst::backup-$(date "+%Y%m%d-%H%M%S")" "$src"
borg prune --keep-within 7d --keep-daily 7 --keep-weekly 12 --keep-monthly 12 --keep-yearly 3 -v "$dst"
}
### support functions below ###
##
# usage : in_array( $needle, $haystack )
# return : 0 - found
# 1 - not found
##
in_array() {
local needle=$1; shift
local item
for item in "$@"; do
[[ $item = "$needle" ]] && return 0 # Found
done
return 1 # Not Found
}
# same as in_array except 0 is returned if any item in haystack starts with needle
in_array_startswith() {
local needle=$1; shift
local item
for item in "$@"; do
[[ "$needle" == "$item"* ]] && return 0 # Found
done
return 1 # Not Found
}
exclude_mountpoints() {
local error=0
for fs in "${excludeMountpoints[@]}"; do
excludeList_borg+="sh:$fs/*"$'\n'
done
while read line; do
local mountpoint=$(echo "$line" | cut -d\ -f2 | sed 's#\040# #g;')
if ! in_array "$mountpoint" "${includeMountpoints[@]}"; then
if ! in_array_startswith "$mountpoint/" "${excludeMountpoints[@]/%//}"; then
error=1
echo "Warning: mountpoint not excluded or included: $mountpoint" >&2
fi
fi
done