From e63bf65b3661e582a2a6b42b131c3a53f04108a4 Mon Sep 17 00:00:00 2001 From: Erich Eckner Date: Wed, 4 May 2016 10:20:42 +0200 Subject: Makefile, manpage neu, neue Version --- .gitignore | 3 ++ Makefile | 45 +++++++++++++++++++ backup | 115 ------------------------------------------------ backup.in | 117 +++++++++++++++++++++++++++++++++++++++++++++++++ hardlinkedbackups.8.in | 57 ++++++++++++++++++++++++ lastBackups | 71 ------------------------------ lastBackups.in | 73 ++++++++++++++++++++++++++++++ 7 files changed, 295 insertions(+), 186 deletions(-) create mode 100644 .gitignore create mode 100644 Makefile delete mode 100755 backup create mode 100755 backup.in create mode 100644 hardlinkedbackups.8.in delete mode 100755 lastBackups create mode 100755 lastBackups.in diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3ca840a --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +backup +hardlinkedbackups.8 +lastBackups diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..bc36af5 --- /dev/null +++ b/Makefile @@ -0,0 +1,45 @@ +# +# hardlinkedbackups - generates hardlinked incremental backups via rsync (and possibly through a SOCKS-tunnel via ssh) +# +# Copyright (c) 2013-2016 Erich Eckner +# +# 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 2 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +# USA. +# + +DESTDIR = +ETCDIR = /etc +BINDIR = /usr/bin +MANDIR = /usr/share/man + +VERSION = 1.0.2 + +all: hardlinkedbackups.8 backup lastBackups + +%: %.in + sed "s/#VERSION#/$(VERSION)/; s@#BINDIR#@$(BINDIR)@; s@#ETCDIR#@$(ETCDIR)@" $< > $@ + +.PHONY: install dist clean + +install: all + install -D -m0755 -t $(DESTDIR)$(BINDIR) backup lastBackups + install -D -m0644 -t $(DESTDIR)$(MANDIR)/man8 hardlinkedbackups.8 + ln -S $(DESTDIR)$(MANDIR)/man8/{hardlinkedbackups,backups,lastBackups}.8 + install -D -m0644 -t $(DESTDIR)$(ETCDIR)/backup.conf + +clean: + rm -f backup lastBackups hardlinkedbackups.8 + +# End of file diff --git a/backup b/backup deleted file mode 100755 index f6501dc..0000000 --- a/backup +++ /dev/null @@ -1,115 +0,0 @@ -#!/bin/bash - -. /etc/backup.conf - -usage() -{ - echo 'usage:' - echo ' backup /tmp/pidFile /path/to/destination/ user@source:path' - echo ' backup /tmp/pidFile /path/to/destination/ user@source:path proxy_user@ssh_host' - echo 'or as a symlink (e.g. in /etc/cron/daily) named after a key of $backups in /etc/backup.conf without arguments' - exit 1 -} - -if [ $# -eq 0 ] -then - backupID="$(basename $0)" - [ -z "${backups[${backupID}]}" ] && usage - set /tmp/${backupID}.pid ${backups[${backupID}]} -fi - -Basis="$2" -pidFile="$1" -QuellIP=$(echo "$3" | sed "s|^[a-zA-Z]*://||; s|^[a-zA-Z]*@||; s|:\?/.*$||") - -if [ "$#" -eq 3 ] -then - Quelle="$3" -elif [ "$#" -eq 4 ] -then - sshHopp="$4" - lokPort=$[$RANDOM/2+8000] - rsyncShell="-e ssh -p${lokPort} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" - tunnelBefehl="ssh -t -t -L${lokPort}:${QuellIP}:22 ${sshHopp}" - HoppIP="${sshHopp#*@}" - Quelle="$(echo "$3" | sed "s|${QuellIP}|127.0.0.1|")" -else - usage -fi - -ping -c1 ${QuellIP} > /dev/null || exit 11 -if [ -n "${HoppIP}" ] -then - ping -c1 ${HoppIP} > /dev/null || exit 11 -fi - -if [ ! -d ${Basis} ] -then - [ $(mount | grep -c "\S\+\s\+\S\+\s\+/var/ftp\s") -eq 0 ] && exit 11 - exit 2 -fi - -neues_Datum=${Basis}/$(date "+%Y_%m_%d") -neues=${Basis}/aktuell -linkdests="" -for s in $(ls -1tr ${Basis} | grep -v aktuell | tail -n20 ) -do - linkdests="${linkdests} --link-dest ${Basis}/${s}" -done - -if [ ! "$(whoami)" == "root" ] -then - echo "I need to be root." - exit 3 -fi - -[ -w ${Basis} ] || exit 11 -[ -e ${neues_Datum} ] && exit 4 -[ -e ${pidFile} ] && exit 5 - -echo $$ > ${pidFile} - -if [ -n "${tunnelBefehl}" ] -then - ${tunnelBefehl} & - backgroundPid=$! - sleep 4 -fi - -for toExclude in ${excludes} -do - excludeArgs="${excludeArgs} --exclude ${toExclude}" -done - -if [ ! -e ${neues} ] -then - mkdir ${neues} -fi -chmod 750 ${neues} -chown erich:root ${neues} -if [ -z "${rsyncShell}" ] -then - rsync -av -x --no-p --no-o --no-g ${linkdests} \ - ${excludeArgs} \ - ${Quelle} ${neues}/ - sleep 1 - rsync ${Quelle} -else - rsync "${rsyncShell}" -av -x --no-p --no-o --no-g ${linkdests} \ - ${excludeArgs} \ - ${Quelle} ${neues}/ - sleep 1 - rsync "${rsyncShell}" ${Quelle} -fi -erg=$? - -[ -n "${backgroundPid}" ] && kill ${backgroundPid} - -if [ ${erg} -eq 0 ] || [ ${erg} -eq 24 ] -then - mv ${neues} ${neues_Datum} - rm ${pidFile} -else - rm ${pidFile} - exit 11 -fi diff --git a/backup.in b/backup.in new file mode 100755 index 0000000..fdc467e --- /dev/null +++ b/backup.in @@ -0,0 +1,117 @@ +#!/bin/bash + +. #ETCDIR#/backup.conf + +usage() +{ + >&2 echo 'This is backup version #VERSION#' + >&2 echo '' + >&2 echo 'usage:' + >&2 echo ' backup /tmp/pidFile /path/to/destination/ user@source:path' + >&2 echo ' backup /tmp/pidFile /path/to/destination/ user@source:path proxy_user@ssh_host' + >&2 echo 'or as a symlink (e.g. in /etc/cron/daily) named after a key of $backups in #ETCDIR#/backup.conf without arguments' + exit 1 +} + +if [ $# -eq 0 ] +then + backupID="$(basename $0)" + [ -z "${backups[${backupID}]}" ] && usage + set /tmp/${backupID}.pid ${backups[${backupID}]} +fi + +Basis="$2" +pidFile="$1" +QuellIP=$(echo "$3" | sed "s|^[a-zA-Z]*://||; s|^[a-zA-Z]*@||; s|:\?/.*$||") + +if [ "$#" -eq 3 ] +then + Quelle="$3" +elif [ "$#" -eq 4 ] +then + sshHopp="$4" + lokPort=$[$RANDOM/2+8000] + rsyncShell="-e ssh -p${lokPort} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" + tunnelBefehl="ssh -t -t -L${lokPort}:${QuellIP}:22 ${sshHopp}" + HoppIP="${sshHopp#*@}" + Quelle="$(echo "$3" | sed "s|${QuellIP}|127.0.0.1|")" +else + usage +fi + +ping -c1 ${QuellIP} > /dev/null || exit 11 +if [ -n "${HoppIP}" ] +then + ping -c1 ${HoppIP} > /dev/null || exit 11 +fi + +if [ ! -d ${Basis} ] +then + [ $(mount | grep -c "\S\+\s\+\S\+\s\+/var/ftp\s") -eq 0 ] && exit 11 + exit 2 +fi + +neues_Datum=${Basis}/$(date "+%Y_%m_%d") +neues=${Basis}/aktuell +linkdests="" +for s in $(ls -1tr ${Basis} | grep -v aktuell | tail -n20 ) +do + linkdests="${linkdests} --link-dest ${Basis}/${s}" +done + +if [ ! "$(whoami)" == "root" ] +then + echo "I need to be root." + exit 3 +fi + +[ -w ${Basis} ] || exit 11 +[ -e ${neues_Datum} ] && exit 4 +[ -e ${pidFile} ] && exit 5 + +echo $$ > ${pidFile} + +if [ -n "${tunnelBefehl}" ] +then + ${tunnelBefehl} & + backgroundPid=$! + sleep 4 +fi + +for toExclude in ${excludes} +do + excludeArgs="${excludeArgs} --exclude ${toExclude}" +done + +if [ ! -e ${neues} ] +then + mkdir ${neues} +fi +chmod 750 ${neues} +chown erich:root ${neues} +if [ -z "${rsyncShell}" ] +then + rsync -av -x --no-p --no-o --no-g ${linkdests} \ + ${excludeArgs} \ + ${Quelle} ${neues}/ + sleep 1 + rsync ${Quelle} +else + rsync "${rsyncShell}" -av -x --no-p --no-o --no-g ${linkdests} \ + ${excludeArgs} \ + ${Quelle} ${neues}/ + sleep 1 + rsync "${rsyncShell}" ${Quelle} +fi +erg=$? + +[ -n "${backgroundPid}" ] && kill ${backgroundPid} + +if [ ${erg} -eq 0 ] || [ ${erg} -eq 24 ] +then + mv ${neues} ${neues_Datum} + rm ${pidFile} +else + rm ${pidFile} + exit 11 +fi diff --git a/hardlinkedbackups.8.in b/hardlinkedbackups.8.in new file mode 100644 index 0000000..c634fd9 --- /dev/null +++ b/hardlinkedbackups.8.in @@ -0,0 +1,57 @@ +.TH hardlinkedbackups 8 "" "hardlinkedbackups #VERSION#" "" +.SH NAME +hardlinkedbackups \- hardlinked incremental backups via rsync (and possibly through a SOCKS\-tunnel via ssh) +.SH SYNOPSIS +.TP +\fBbackup /tmp/pidFile /path/to/destination/ user@source:path [proxy_user@ssh_host]\fP +.TP +\fBlastBackups\fP +.SH DESCRIPTION +\fBbackup\fP generates incremental backups (by hardlinking old unchanged files) via rsync and possibly a SOCKS\-tunnel. +\fBlastBackups\fP shows date of backups and warns about outdated ones. +.SH USAGE +.TP +.B "backup /tmp/pidFile /path/to/destination/ user@source:path" +Creates incremental backup in \fB/path/to/destination/\fP from \fBuser@source:path\fP, saving its PID in \fB/tmp/pidFile\fP. +.TP +.B "backup /tmp/pidFile /path/to/destination/ user@source:path proxy_user@ssh_host" +Same as above, but tunneling via SSH through \fBproxy_user@ssh_host\fP. +.TP +.B "symLinkName -> #BINDIR#/backup" +Same as one of the above. +Command line parameters are filled from variable \fB$backups["symLinkName"]\fP in \fB#ETCDIR#/backup.conf\fP. +This is designed to be called from a cron daemon for daily backups. +.TP +.B "lastBackups" +reports about actuality of backups defined in \fB#ETCDIR#/backup.conf\fP +.SH CONFIGURATION +The configfile \fB#ETCDIR/backup.conf\fP is a bash script, which defines the following variables: +.TP +.B "excludes" +array of paths to exclude from backup +.TP +.B "backups" +array with command line arguments for predefined backups +.TP +.B "maxWait" +maximum time to wait for destination directories to appear in lastBackups [seconds] +.TP +.B "outdatedLimit" +time before backups are considered outdated [seconds] +.TP +.B "recognSubdirRegex" +regular expression of subdirectories which should be appended to the parent directory in the report +.SH FILES +.TP +.B "#BINDIR#/backup" +program for creation of backups +.TP +.B "#BINDIR#/lastBackups" +program for check of backups +.TP +.B "#ETCDIR#/backup.conf" +configuration of backups +.SH AUTHOR +.nf +Erich Eckner +.fi diff --git a/lastBackups b/lastBackups deleted file mode 100755 index 83fcbed..0000000 --- a/lastBackups +++ /dev/null @@ -1,71 +0,0 @@ -#!/bin/bash - -. /etc/backup.conf - -unset namen -unset veraltet -unset daten - -for backupID in ${!backups[@]} -do - dest="${backups["${backupID}"]%% *}" - dest="${dest%/}" - while [ ! -d "${dest}" ] && [ ${maxWait} -gt 0 ] - do - sleep 1 - maxWait=$[${maxWait}-1] - done - - lbu=$( - ls -1 ${dest} | \ - grep -v "^aktuell$" | \ - sed "s/^duplicity-\(inc\|full\)\.\(.*\.\)\?\([0-9]\{4\}\)\([0-9]\{2\}\)\([0-9]\{2\}\)T[0-9]\{6\}Z\..*$/\3_\4_\5/" | \ - sort -n | \ - uniq | \ - grep "^[0-9]\{4\}_[0-9]\{2\}_[0-9]\{2\}$" | \ - tail -n1 - ) - [ "${lbu}" == "" ] && lbu="2000_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\$##"))" - [ ! "$(ls -A ${dest}/${lbu})" ] && - veraltet[${#veraltet[@]}]="LEER!" || - [ ${delta} -gt ${outdatedLimit} ] && \ - veraltet[${#veraltet[@]}]="VERALTET!" || \ - veraltet[${#veraltet[@]}]="" - daten[${#daten[@]}]="${lbu}" -done - -nl=0 -vl=0 -dl=0 - -for ((i=0; i<${#namen[@]}; i++)) -{ - [ ${#namen[${i}]} -gt ${nl} ] && nl=${#namen[${i}]} - [ ${#veraltet[${i}]} -gt ${vl} ] && vl=${#veraltet[${i}]} - [ ${#daten[${i}]} -gt ${dl} ] && dl=${#daten[${i}]} -} - -for ((i=0; i<${#namen[@]}; i++)) -{ - while [ ${#namen[${i}]} -lt ${nl} ] - do - namen[${i}]="${namen[${i}]} " - done - while [ ${#veraltet[${i}]} -lt ${vl} ] - do - veraltet[${i}]="${veraltet[${i}]} " - done - while [ ${#daten[${i}]} -lt ${dl} ] - do - daten[${i}]="${daten[${i}]} " - done -} - -for ((i=0; i<${#namen[@]}; i++)) -{ - echo "${daten[${i}]} ${veraltet[${i}]} ${namen[${i}]} ${daten[${i}]}" -} | \ - sort | \ - sed "s/^\S\+\s//" diff --git a/lastBackups.in b/lastBackups.in new file mode 100755 index 0000000..ea6040a --- /dev/null +++ b/lastBackups.in @@ -0,0 +1,73 @@ +#!/bin/bash + +# lastBackups version #VERSION# + +. #ETCDIR#/backup.conf + +unset namen +unset veraltet +unset daten + +for backupID in ${!backups[@]} +do + dest="${backups["${backupID}"]%% *}" + dest="${dest%/}" + while [ ! -d "${dest}" ] && [ ${maxWait} -gt 0 ] + do + sleep 1 + maxWait=$[${maxWait}-1] + done + + lbu=$( + ls -1 ${dest} | \ + grep -v "^aktuell$" | \ + sed "s/^duplicity-\(inc\|full\)\.\(.*\.\)\?\([0-9]\{4\}\)\([0-9]\{2\}\)\([0-9]\{2\}\)T[0-9]\{6\}Z\..*$/\3_\4_\5/" | \ + sort -n | \ + uniq | \ + grep "^[0-9]\{4\}_[0-9]\{2\}_[0-9]\{2\}$" | \ + tail -n1 + ) + [ "${lbu}" == "" ] && lbu="2000_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\$##"))" + [ ! "$(ls -A ${dest}/${lbu})" ] && + veraltet[${#veraltet[@]}]="LEER!" || + [ ${delta} -gt ${outdatedLimit} ] && \ + veraltet[${#veraltet[@]}]="VERALTET!" || \ + veraltet[${#veraltet[@]}]="" + daten[${#daten[@]}]="${lbu}" +done + +nl=0 +vl=0 +dl=0 + +for ((i=0; i<${#namen[@]}; i++)) +{ + [ ${#namen[${i}]} -gt ${nl} ] && nl=${#namen[${i}]} + [ ${#veraltet[${i}]} -gt ${vl} ] && vl=${#veraltet[${i}]} + [ ${#daten[${i}]} -gt ${dl} ] && dl=${#daten[${i}]} +} + +for ((i=0; i<${#namen[@]}; i++)) +{ + while [ ${#namen[${i}]} -lt ${nl} ] + do + namen[${i}]="${namen[${i}]} " + done + while [ ${#veraltet[${i}]} -lt ${vl} ] + do + veraltet[${i}]="${veraltet[${i}]} " + done + while [ ${#daten[${i}]} -lt ${dl} ] + do + daten[${i}]="${daten[${i}]} " + done +} + +for ((i=0; i<${#namen[@]}; i++)) +{ + echo "${daten[${i}]} ${veraltet[${i}]} ${namen[${i}]} ${daten[${i}]}" +} | \ + sort | \ + sed "s/^\S\+\s//" -- cgit v1.2.3-54-g00ecf