summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore5
-rw-r--r--Makefile14
-rwxr-xr-xbackup-progress.in6
-rw-r--r--backup@.service.in2
-rw-r--r--forwarddown.in111
-rwxr-xr-xhardlinked-backup.in (renamed from backup.in)83
-rwxr-xr-xlast-backups.in19
-rw-r--r--man.commons.in2
-rw-r--r--remove-old-backups.service.in8
-rw-r--r--remove-old-backups.timer.in10
-rwxr-xr-xsendmailadvanced.hook18
11 files changed, 256 insertions, 22 deletions
diff --git a/.gitignore b/.gitignore
index b917e42..4987e86 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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
diff --git a/Makefile b/Makefile
index 2433800..538f706 100644
--- a/Makefile
+++ b/Makefile
@@ -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