diff options
-rw-r--r-- | .gitignore | 5 | ||||
-rw-r--r-- | Makefile | 14 | ||||
-rwxr-xr-x | backup-progress.in | 6 | ||||
-rw-r--r-- | backup@.service.in | 2 | ||||
-rw-r--r-- | forwarddown.in | 111 | ||||
-rwxr-xr-x | hardlinked-backup.in (renamed from backup.in) | 83 | ||||
-rwxr-xr-x | last-backups.in | 19 | ||||
-rw-r--r-- | man.commons.in | 2 | ||||
-rw-r--r-- | remove-old-backups.service.in | 8 | ||||
-rw-r--r-- | remove-old-backups.timer.in | 10 | ||||
-rwxr-xr-x | sendmailadvanced.hook | 18 |
11 files changed, 256 insertions, 22 deletions
@@ -1,11 +1,14 @@ -backup backup@.service backup@.timer backup-progress backup-statistics fast-repair +forwarddown +hardlinked-backup last-backups remove-old-backups man.commons *.common *.1 +remove-old-backups.service +remove-old-backups.timer @@ -26,14 +26,16 @@ BINDIR = /usr/bin LIBDIR = /usr/lib MANDIR = /usr/share/man -VERSION = 1.4.10 +VERSION = 2.0.2 all: man.commons \ - backup backup.1 \ + hardlinked-backup hardlinked-backup.1 \ backup@.service backup@.timer \ backup-progress \ backup-statistics backup-statistics.1 \ + forwarddown forwarddown.1 \ last-backups last-backups.1 \ + remove-old-backups.service remove-old-backups.timer \ remove-old-backups remove-old-backups.1 %: %.in @@ -53,10 +55,11 @@ all: man.commons \ .PHONY: install dist clean install: all - install -D -m0755 -t $(DESTDIR)$(BINDIR) backup backup-progress backup-statistics last-backups remove-old-backups - install -D -m0644 -t $(DESTDIR)$(LIBDIR)/systemd/system backup@.service backup@.timer - install -D -m0644 -t $(DESTDIR)$(MANDIR)/man1 backup.1 backup-statistics.1 last-backups.1 remove-old-backups.1 + install -D -m0755 -t $(DESTDIR)$(BINDIR) backup-progress backup-statistics forwarddown hardlinked-backup last-backups remove-old-backups + install -D -m0644 -t $(DESTDIR)$(LIBDIR)/systemd/system backup@.service backup@.timer remove-old-backups.service remove-old-backups.timer + install -D -m0644 -t $(DESTDIR)$(MANDIR)/man1 backup-statistics.1 forwarddown.1 hardlinked-backup.1 last-backups.1 remove-old-backups.1 install -D -m0644 -t $(DESTDIR)$(ETCDIR) backup.conf + install -D -m0755 -T sendmailadvanced.hook $(DESTDIR)$(ETCDIR)/sendmailadvanced.hooks/last-backups clean: git clean -x -d -f @@ -65,6 +68,7 @@ dist: clean git status --porcelain 2> /dev/null | grep -q "\S" && (git add .; git commit -m"neue Version: $(VERSION)") || true ! git tag -d v$(VERSION) 2> /dev/null git tag v$(VERSION) + knock-knock || true git push git push --tags diff --git a/backup-progress.in b/backup-progress.in index 4b6e911..a2063b8 100755 --- a/backup-progress.in +++ b/backup-progress.in @@ -10,7 +10,7 @@ declare -A sz for backupID in "${!backups[@]}"; do [ -s "/tmp/${backupID}.pid" ] || continue - kill -0 $(cat "/tmp/${backupID}.pid") || continue + [ -d "/proc/$(cat "/tmp/${backupID}.pid")" ] || continue dir="${backups["${backupID}"]%% *}" dir="${dir%/}" nm[${#nm[@]}]=$( @@ -26,14 +26,14 @@ for backupID in "${!backups[@]}"; do | tail -n1 ) last_size=$( - timeout "${du_timeout}" du -sb "${dir}/${last}" \ + ${du_timeout+timeout "${du_timeout}"} du -sb "${dir}/${last}" \ | awk '{print $1}' ) if [ -z "${last_size}" ]; then last_size='4096' fi current_size=$( - timeout "${du_timeout}" du -sb "${dir}/aktuell" \ + ${du_timeout+timeout "${du_timeout}"} du -sb "${dir}/aktuell" \ | awk '{print $1}' ) if [ -z "${current_size}" ]; then diff --git a/backup@.service.in b/backup@.service.in index 19fef7d..4ce52d5 100644 --- a/backup@.service.in +++ b/backup@.service.in @@ -5,7 +5,7 @@ After=network-online.target local-fs.target [Service] Type=simple -ExecStart=#BINDIR#/backup %I +ExecStart=#BINDIR#/hardlinked-backup %I SuccessExitStatus=4 RestartForceExitStatus=5 11 RestartSec=10 diff --git a/forwarddown.in b/forwarddown.in new file mode 100644 index 0000000..31b4487 --- /dev/null +++ b/forwarddown.in @@ -0,0 +1,111 @@ +#!/bin/bash + +[ -r "#ETCDIR#/backup.conf" ] && \ + . "#ETCDIR#/backup.conf" + +usage() +{ + >&2 echo \ +'Usage: forwarddown key-id date backup-id1 backup-id2 ... +Write encrypted, compressed tar from backed up files not newer than date to stdout. + +Options: + key-id key id of key to encrypt to + date backup should not be newer than the given date + backup-id[n] backups to restore +#HELPTEXT# #' + [ -z "$1" ] && exit 1 + exit $1 +} + +if [ $# -eq 1 ]; then + if [ "$1" == "--help" ]; then + usage 0 + elif [ "$1" == "--version" ]; then + echo '#VERSION#' + exit + fi +fi + +if [ $# -lt 3 ]; then + usage +fi + +key_id=$( + printf '%s\n' "$1" \ + | tr '[:lower:]' '[:upper:]' +) +shift + +if ! gpg --list-keys --with-colon "${key_id}" \ +| grep -qxF 'fpr:::::::::'"${key_id}"':'; then + >&2 printf 'Cannot find gpg key with fingerprint "%s"\n' "${key_id}" + usage +fi + +if ! date=$(date -u '+%Y_%m_%d' -d"${1}"); then + >&2 printf 'Cannot parse date "%s"\n' "${1}" + usage +fi +shift + +forwarddown_dirs=() + +for backup_id in "$@"; do + if [ -z "${backups[${backup_id}]}" ]; then + >&2 printf 'backup id "%s" is unknown\n' "${backup_id}" + usage + fi + forwarddown_dirs+=("${backups[${backup_id}]## *}") +done + +date=$( + { + find "${forwarddown_dirs[@]%% *}" -mindepth 1 -maxdepth 1 -type d -printf '0 %f\n' + printf '1 %s\n' "${date}" + } \ + | sort -k2,2 -k1,1 \ + | sed ' + s/^0 // + t + /^1 / q + ' \ + | uniq -c \ + | sed ' + s/^\s*'"${#forwarddown_dirs[@]}"'\s\+// + t + d + ' \ + | tail -n1 +) + +transforms=( + $( + for backup_id in "$@"; do + printf -- '--transform=s@^%s/%s/@%s/%s/@\n' \ + "${backups[${backup_id}]%% *}" \ + "${date}" \ + "${date}" \ + "${backup_id}" + done + ) +) + +directories=( + $( + for backup_id in "$@"; do + printf '%s/%s/\n' \ + "${backups[${backup_id}]%% *}" \ + "${date}" + done + ) +) + +tar \ + -c \ + -f - \ + --absolute-names \ + "${transforms[@]}" \ + "${directories[@]}" \ +| pigz --best -c - \ +| gpg -e -r "${key_id}" -o - - diff --git a/backup.in b/hardlinked-backup.in index 2c73947..ab6fac0 100755 --- a/backup.in +++ b/hardlinked-backup.in @@ -11,7 +11,7 @@ rsync --help | \ usage() { >&2 echo \ -'Usage: backup /tmp/pidFile /path/to/destination/ user@source:path [proxy_user@ssh_host] +'Usage: hardlinked-backup /tmp/pidFile /path/to/destination/ user@source:path [proxy_user@ssh_host] Backup files from remote with rsync, possibly via SSH-tunnel. With no arguments, information is expected to be in array $backups in #ETCDIR#/backup.conf with name of executable (e.g. a symlink) as key. @@ -149,7 +149,7 @@ elif [ "$#" -eq 4 ]; then exit 1 fi rsyncShell="-e ssh -${ipVer} -p${lokPort} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" - tunnelBefehl="ssh -${ipVer} -t -t -L${localAddress}:${lokPort}:${QuellIP}:22 ${sshHopp}" + tunnelBefehl="ssh -${ipVer} -t -t -L${localAddress}:${lokPort}:${QuellIP}:22 ${sshHopp} while sleep 60; do date; done" Quelle="$(echo "$3" | sed "s|${QuellIP}|${localAddress}|")" else usage @@ -160,6 +160,7 @@ rsyncOptions="${rsyncOptions}${ipVer}" if [ ! -d ${Basis} ]; then for neededMount in "${neededMounts[@]}"; do if ! mountpoint -q "${neededMount}"; then + >&2 printf 'Mountpoint %s is not available.\n' "${neededMount}" exit 11 fi done @@ -178,7 +179,6 @@ if [ ! "$(whoami)" == "root" ]; then exit 3 fi -[ -w "${Basis}" ] || exit 11 [ -e "${neues_Datum}" ] && exit 4 if ${seldom}; then for date_diff in $(seq ${seldomness}); do @@ -187,7 +187,8 @@ if ${seldom}; then fi done fi -[ -s "${pidFile}" ] && kill -0 $(cat "${pidFile}") 2>/dev/null && exit 5 +[ -w "${Basis}" ] || exit 11 +[ -s "${pidFile}" ] && [ -d "/proc/$(cat "${pidFile}")" ] && exit 5 echo $$ > "${pidFile}" @@ -202,13 +203,79 @@ for toExclude in "${excludes[@]}"; do excludeArgs="${excludeArgs} --exclude ${toExclude}" done -if [ ! -e "${neues}/wip" ]; then - mkdir -p "${neues}/wip" -fi +mkdir -p "${neues}/wip" chmod 750 "${neues}"{,/wip} chown root:root "${neues}"{,/wip} if [ -z "${rsyncShell}" ]; then preConnectHook + if [ -z "${Quelle#*@*:/}" ]; then + ssh "-${ipVer}" "${Quelle%:/}" ' + pacman-conf Architecture + find /var/lib/pacman/local -name desc -exec cat {} + + ' \ + | { + read -r arch + sed -n ' + /^%\(NAME\|VERSION\|ARCH\)%/ { + N + s/\n/ / + p + } + ' \ + | sed ' + N + N + s@^%NAME% \(\S\+\)\n%VERSION% \(\S\+\)\n%ARCH% \(\S\+\)$@\1-\2-\3.pkg.tar.zst@ + t + w /dev/stderr + d + ' \ + | while read -r file; do + cache_file='/var/cache/pacman/pkg/'"${file}" + if [ ! -f "${cache_file}" ]; then + case "${arch}" in + 'x86_64') + for url in \ + 'https://mirror.f4st.host/archlinux/pool/packages/'"${file}" \ + 'https://archive.archlinux.org/packages/'"${file:0:1}"'/'"${file%-*}"'/'"${file}" \ + 'https://arch.eckner.net/os/'"${arch}"'/'"${file}"; do + curl -Lo "${cache_file}" "${url}" || continue + tar --zstd -tf "${cache_file}" >/dev/null 2>&1 && break + done + ;; + 'i486'|'i686'|'pentium4') + for url in \ + 'https://mirror.archlinux32.org/pool/'"${file}" \ + 'https://archive.archlinux32.org/packages/'"${file:0:1}"'/'"${file%-*}"'/'"${file}" \ + 'https://arch.eckner.net/os/'"${arch}"'/'"${file}"; do + curl -Lo "${cache_file}" "${url}" || continue + tar --zstd -tf "${cache_file}" >/dev/null 2>&1 && break + done + ;; + 'armv6h') + for url in \ + 'http://software.is.never.null/arch/'"${arch}"'/'{extra,community,core,alarm,aur}'/'"${file}" \ + 'https://arch.eckner.net/os/'"${arch}"'/'"${file}"; do + curl -Lo "${cache_file}" "${url}" || continue + tar --zstd -tf "${cache_file}" >/dev/null 2>&1 && break + done + ;; + 'armv7h'|'aarch64') + for url in \ + 'https://mirror.archlinuxarm.org/'"${arch}"'/'{extra,community,core,alarm,aur}'/'"${file}" \ + 'https://arch.eckner.net/os/'"${arch}"'/'"${file}"; do + curl -Lo "${cache_file}" "${url}" || continue + tar --zstd -tf "${cache_file}" >/dev/null 2>&1 && break + done + ;; + esac + fi + if [ -f "${cache_file}" ]; then + tar -C "${neues:?}/wip/" --zstd -xkf "${cache_file}" + fi + done + } + fi rsync ${rsyncOptions} \ ${linkdests} \ ${excludeArgs} \ @@ -231,7 +298,7 @@ erg=$? [ -n "${backgroundPid}" ] && kill "${backgroundPid}" -if [ ${erg} -eq 0 ] || [ ${erg} -eq 24 ]; then +if [ ${erg} -eq 0 ] || [ ${erg} -eq 24 ] && ! rmdir "${neues}/wip" 2>/dev/null; then chmod o-rwx "${neues}/wip" neueres_Datum="${Basis}/$(date "+%Y_%m_%d")" if [ ! -e "${neueres_Datum}" ]; then diff --git a/last-backups.in b/last-backups.in index 9777392..84e8f64 100755 --- a/last-backups.in +++ b/last-backups.in @@ -55,10 +55,23 @@ do grep "^[0-9]\{4\}_[0-9]\{2\}_[0-9]\{2\}$" | \ tail -n1 ) - [ "${lbu}" == "" ] && lbu="2000_01_01" + [ "${lbu}" == "" ] && lbu="1970_01_01" delta=$[$(date +%s) - $(date -d $(echo ${lbu} | sed "s/_//g") +%s)] - namen[${#namen[@]}]="$(basename $(echo ${dest} | sed "s#/\(${recognSubdirRegex}\)\$#_\1#" | sed "s#/rsync\$##; s#/duplicity\$##"))" - if [ -f "/tmp/${backupID}.pid" ] && pgrep -x backup | grep -qxF "$(cat "/tmp/${backupID}.pid")"; then + namen[${#namen[@]}]=$( + basename $( + printf '%s\n' "${dest}" \ + | sed ' + s#/rsync$## + s#/duplicity$## + ' \ + | sed ' + :a + s#/\('"${recognSubdirRegex}"'\)$#_\1# + ta + ' + ) + ) + if [ -f "/tmp/${backupID}.pid" ] && [ -d "/proc/$(cat "/tmp/${backupID}.pid")" ]; then laeuft='_laeuft' else laeuft='' diff --git a/man.commons.in b/man.commons.in index 4ebb720..d94775f 100644 --- a/man.commons.in +++ b/man.commons.in @@ -3,8 +3,8 @@ [AUTHOR] Erich Eckner <opensource at eckner dot net>. [SEE ALSO] -backup(1) backup-statistics(1) +hardlinked-backup(1) last-backups(1) remove-old-backups(1) [CONFIG] diff --git a/remove-old-backups.service.in b/remove-old-backups.service.in new file mode 100644 index 0000000..dea3649 --- /dev/null +++ b/remove-old-backups.service.in @@ -0,0 +1,8 @@ +[Unit] +Description=Remove old backups. +Requires=network-online.target local-fs.target +After=network-online.target local-fs.target + +[Service] +Type=simple +ExecStart=#BINDIR#/remove-old-backups diff --git a/remove-old-backups.timer.in b/remove-old-backups.timer.in new file mode 100644 index 0000000..673e18e --- /dev/null +++ b/remove-old-backups.timer.in @@ -0,0 +1,10 @@ +[Unit] +Description=Regularly remove old backups. + +[Timer] +AccuracySec=1us +RandomizedDelaySec=1day +OnCalendar=*-*-* 00:00:00 + +[Install] +WantedBy=timers.target diff --git a/sendmailadvanced.hook b/sendmailadvanced.hook new file mode 100755 index 0000000..3492399 --- /dev/null +++ b/sendmailadvanced.hook @@ -0,0 +1,18 @@ +#!/bin/sh + +case "$1" in + 'head') + : + ;; + 'foot') + echo '----------------------- Last Backups Begin -------------------------' + echo + last-backups + echo + echo '------------------------ Last Backups End --------------------------' + ;; + *) + >&2 printf 'usage: %s head|foot\n' "${0##*/}" + exit 1 + ;; +esac |