You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
273 lines
8.9 KiB
273 lines
8.9 KiB
9 years ago
|
#! /bin/bash -E
|
||
|
|
||
|
## @id $Id$
|
||
|
## 1 2 3 4 5 6 7 8
|
||
|
## 45678901234567890123456789012345678901234567890123456789012345678901234567890
|
||
|
|
||
|
#set -x
|
||
|
|
||
|
# Simple setup of a list of hosts (internal and external):
|
||
|
#
|
||
|
# setup-backup dev.swisssign.com dev0001 dev0002 dev0004 devmac0001
|
||
|
#
|
||
|
# What the setup script really does is:
|
||
|
#
|
||
|
# HOSTS="dev_swisssign_com dev0001 dev0002 dev0004 devmac0001"
|
||
|
# for period in daily weekly monthly; do
|
||
|
# cd /etc/cron.$period;
|
||
|
# for h in $HOSTS; do
|
||
|
# sudo ln -s $0 backup-$h;
|
||
|
# done;
|
||
|
# done
|
||
|
|
||
|
################################################################################
|
||
|
# Configuration (read from @sysconfdir@/backup.conf and ~/.backup
|
||
|
# or host specific backup from backup-HOST.conf and .backup-HOST
|
||
|
################################################################################
|
||
|
|
||
|
# disable: dont' start backup if a certain file exists
|
||
|
DISABLE=${DISABLE:-/var/run/nobackup}
|
||
|
if [ -e ${DISABLE} ]; then
|
||
|
return
|
||
|
fi
|
||
|
|
||
|
# get host name from filename (name files with _ instead of .)
|
||
|
# e.g.
|
||
|
# $0 (this file name) is backup-dev_marc_waeckerlin_org
|
||
|
# $HOST becomes dev.marc.waeckerlin.org
|
||
|
# you can test for $HOST in /etc/backup.conf or ~/.backup.conf
|
||
|
HOST=${HOST:-$(basename ${0##*/backup-} .sh | sed 's/_/./g')}
|
||
|
|
||
|
# Get defaults from a file /etc/backup.conf or ~/.backup.conf
|
||
|
# file is bash syntax and shall define variables, i.e.
|
||
|
#
|
||
|
# EMAIL="me@freemail.com"
|
||
|
# NUM=2
|
||
|
# TARGET="/media/backup"
|
||
|
# MORE_ARGS="--exclude atheismus.ch_error_log --exclude marc_error_log"
|
||
|
#
|
||
|
prefix="@prefix@"
|
||
|
test -e "@sysconfdir@/backup.conf" && . "@sysconfdir@/backup.conf"
|
||
|
test -e ~/.backup && . ~/.backup
|
||
|
test -e "@sysconfdir@/backup-$HOST.conf" && . "@sysconfdir@/backup-$HOST.conf"
|
||
|
test -e ~/".backup-$HOST" && . ~/".backup-$HOST"
|
||
|
|
||
|
# E-Mail to report errors
|
||
|
EMAIL=${EMAIL:-""}
|
||
|
|
||
|
# Number of backups to keep per type
|
||
|
NUM=${NUM:-5}
|
||
|
|
||
|
# Target to backup to
|
||
|
TARGET=${TARGET:-"/var/backup"}
|
||
|
|
||
|
# The following variables are detected automatically
|
||
|
# and setup with good defaulte
|
||
|
# You rarely need to change them
|
||
|
|
||
|
# get name of host to backup from file name
|
||
|
# e.g. /var/cron/backup-my-host-com backups from url my.host.com
|
||
|
# if your hostnaim contains dashes '-', then you're lost
|
||
|
# the keyword 'backup-generic' defaults to this host here
|
||
|
if [ "$HOST" = "generic" -o -z "$HOST" ]; then
|
||
|
HOST=$HOSTNAME
|
||
|
HOSTADDR=${HOSTADDR:-""}
|
||
|
BACKUP=${BACKUP:-""}
|
||
|
else
|
||
|
BACKUP=${BACKUP:-"-e ssh"}
|
||
|
HOSTADDR=${HOSTADDR:-"${HOST}:"}
|
||
|
fi
|
||
|
SOURCES=${SOURCES:-$(ssh $HOST mount | awk '/^\// && !/'${TARGET//\//\\\/}'/ && $2=="on" && $6!~/bind/ {print $3}')}
|
||
|
LOGFILE=${LOGFILE:-"/var/log/backup.log"}
|
||
|
LOCK=${LOCK:-${TARGET}/${HOST}.lock}
|
||
|
TMPFILE=${TMPFILE:-/tmp/${0##*/}.$$}
|
||
|
RSYNC_CMD=${RSYNC_CMD:-"nice -n 19 ionice -c 2 -n 7 rsync"}
|
||
|
LOCAL_RSYNC_CMD=${LOCAL_RSYNC_CMD:-$RSYNC_CMD}
|
||
|
REMOTE_RSYNC_CMD=${REMOTE_RSYNC_CMD:-$RSYNC_CMD}
|
||
|
ARGS=${ARGS:-"-aqx --delete"}
|
||
|
ARGS="$ARGS --rsync-path='${REMOTE_RSYNC_CMD}' $BACKUP --exclude ${TARGET} ${MORE_ARGS}"
|
||
|
################################################################################
|
||
|
|
||
|
function error() {
|
||
|
if test -f "${TMPFILE}"; then
|
||
|
if test -n "$EMAIL"; then
|
||
|
mail -s "Backup ERROR: $0:$1 ret:$2" "${EMAIL}" < "${TMPFILE}"
|
||
|
fi
|
||
|
rm "${TMPFILE}" 2> /dev/null || true
|
||
|
echo "$(date) $0 $$ ******** Backup ERROR: $0:$1 ret:$2" >> "${LOGFILE}"
|
||
|
else
|
||
|
if test -n "$EMAIL"; then
|
||
|
echo "Aborted without logfile $TMPFILE" | \
|
||
|
mail -s "Backup ERROR: $0:$1 ret:$2" "${EMAIL}"
|
||
|
fi
|
||
|
echo "$(date) $0 $$ ******** Backup ERROR: $0:$1 ret:$2" >> "${LOGFILE}"
|
||
|
fi
|
||
|
exit 1
|
||
|
}
|
||
|
|
||
|
trap 'error ${LINENO} $?' ERR
|
||
|
|
||
|
umask 022
|
||
|
export PATH=/sbin:/bin:/usr/sbin:/usr/bin
|
||
|
|
||
|
# check rsync capabilities
|
||
|
RSYNC_VERSION=$(rsync --version | sed -n 's,.*rsync version \([0-9.]*\).*,\1,p' | awk -F. '{printf "%04d%04d", $1, $2}')
|
||
|
if [ $RSYNC_VERSION -ge 00020006 ]; then
|
||
|
HAS_MULTIDEST=1
|
||
|
else
|
||
|
HAS_MULTIDEST=0
|
||
|
fi
|
||
|
|
||
|
(
|
||
|
|
||
|
echo "$(date) $0 $$ ########################################################"
|
||
|
|
||
|
# Prüfen, ob Server erreichbar ist
|
||
|
if ! ping -q -w 5 -c 1 $HOST 2> /dev/null > /dev/null; then
|
||
|
echo "$(date) $0 Server $HOST ist nicht erreichbar!" 1>&2
|
||
|
error
|
||
|
exit 2
|
||
|
else
|
||
|
echo "$(date) $0 $$ Server $HOST is reachable"
|
||
|
fi
|
||
|
|
||
|
# locking
|
||
|
echo "$(date) $0 $$ ******** Aquire lock for $HOST from $0"
|
||
|
flock -x 200
|
||
|
echo "$(date) $0 $$ ******** Got lock for $HOST from $0"
|
||
|
|
||
|
## Prüfen, ob Sicherungsmedium vorhanden ist
|
||
|
if [ "$TARGET" != "${TARGET#/media/}" ]; then
|
||
|
if ! (mount | grep "$TARGET" 2> /dev/null > /dev/null); then
|
||
|
echo "$(date) $0 Die Partition $TARGET ist nicht eingebunden!" 1>&2
|
||
|
error
|
||
|
exit 2
|
||
|
fi
|
||
|
else
|
||
|
if ! test -d "${TARGET}"; then
|
||
|
echo "$(date) $0 Der Sicherungspfad $TARGET ist nicht vorhanden!" 1>&2
|
||
|
error
|
||
|
exit 2
|
||
|
fi
|
||
|
fi
|
||
|
|
||
|
echo "$(date) $0 $$ Backup ${HOST} to ${TARGET}"
|
||
|
|
||
|
FULL=$(LANG= df -P ${TARGET} | awk '$1 !~ /^File/ {print $5}' | tr -d '%' | tail -1)
|
||
|
echo "$(date) $0 $$ Füllgrad: ${FULL}%"
|
||
|
if [ "$FULL" -gt 80 ]; then
|
||
|
if [ "$FULL" -gt 98 ]; then
|
||
|
echo "$(date) $0 **** Alarm! **** ${TARGET} ist $FULL% voll" 1>&2
|
||
|
error
|
||
|
exit 2
|
||
|
fi
|
||
|
echo "$(date) $0 $$ **** Warnung! **** ${TARGET} ist $FULL% voll"
|
||
|
fi
|
||
|
|
||
|
# Sicherungstyp eruieren
|
||
|
case "$(pwd)/$0" in
|
||
|
*hourly*) TYPE="hourly";;
|
||
|
*daily*) TYPE="daily";;
|
||
|
*weekly*) TYPE="weekly";;
|
||
|
*monthly*) TYPE="monthly";;
|
||
|
*) TYPE="manual";;
|
||
|
esac
|
||
|
|
||
|
# 3. Was ist die Referenz?
|
||
|
#
|
||
|
# Die Sicherung macht Hardlinks entweder zu hourly.1 oder wenn der nicht
|
||
|
# existiert zu daily.1 oder wenn der nicht existiert zu manual.1
|
||
|
#
|
||
|
# Die Idee ist, immer zum aktuellsten zu verlinken. Auf einem System
|
||
|
# mit regelmässigem Backup ist das gestern, auf einem System mit
|
||
|
# Backup von Hand, ist das das letzte manuelle. (Beispiel: Mein Server
|
||
|
# hat ein tägliches Backup, mein Notebook ein manuelles: Automatisch
|
||
|
# wenn ich es am USB angehängt habe.)
|
||
|
#
|
||
|
# Prüfung daily oder manual?
|
||
|
# a) gibt es den hourly.1? -> hourly
|
||
|
# b) gibt es den daily.1? -> daily
|
||
|
# c) gibt es manual.1? -> manual
|
||
|
# d) sonst -> Was ich bin...
|
||
|
if [ -e ${TARGET}/${HOST}-hourly.1 ]; then
|
||
|
echo "$(date) $0 $$ **** Found an hourly backup for reference"
|
||
|
REFTYPE=hourly
|
||
|
elif [ -e ${TARGET}/${HOST}-daily.1 ]; then
|
||
|
echo "$(date) $0 $$ **** Found an daily backup for reference"
|
||
|
REFTYPE=daily
|
||
|
elif [ -e ${TARGET}/${HOST}-manual.1 ]; then
|
||
|
echo "$(date) $0 $$ **** Found an manual backup for reference"
|
||
|
REFTYPE=manual
|
||
|
else
|
||
|
echo "$(date) $0 $$ **** No reference found, try $TYPE"
|
||
|
REFTYPE=$TYPE
|
||
|
fi
|
||
|
|
||
|
REFNUM=1
|
||
|
echo "$(date) $0 $$ **** SOURCES:" $SOURCES
|
||
|
|
||
|
# 4b. Sonst aktuelle Sicherungskopie gegen neueste Sicherung anlegen
|
||
|
for S in ${SOURCES}; do
|
||
|
SRC=${S%/}/
|
||
|
|
||
|
# Detect possible references, prefered (from above) first
|
||
|
REFERENCES=
|
||
|
for dest in ${TARGET}/${HOST}-${REFTYPE}.${REFNUM}${SRC} \
|
||
|
${TARGET}/${HOST}-${REFTYPE}.[0-9]*${SRC} \
|
||
|
${TARGET}/${HOST}-{hourly,daily,weekly,monthly}.${REFNUM}${SRC} \
|
||
|
${TARGET}/*-${REFTYPE}.${REFNUM}${SRC}; do
|
||
|
if [ -d ${dest} -a "${REFERENCES//$dest/}" = "${REFERENCES}" ]; then
|
||
|
REFERENCES="${REFERENCES} $dest"
|
||
|
if [ $HAS_MULTIDEST -eq 0 ]; then
|
||
|
break;
|
||
|
fi
|
||
|
fi
|
||
|
done
|
||
|
DESTINATIONS="${REFERENCES// / --link-dest=}"
|
||
|
echo "$(date) $0 $$ **** REFERENCES: ${REFERENCES}"
|
||
|
if [ -d ${TARGET}/${HOST}-${TYPE}.new${SRC} ]; then
|
||
|
rm -r ${TARGET}/${HOST}-${TYPE}.new${SRC}
|
||
|
fi
|
||
|
CMD="${LOCAL_RSYNC_CMD} ${ARGS} ${DESTINATIONS} ${HOSTADDR}${SRC%/}/ ${TARGET}/${HOST}-${TYPE}.new${SRC%/}/"
|
||
|
echo "$(date) $0 $$ **** BACKUP: ${CMD}"
|
||
|
bash -c "$CMD" || true
|
||
|
RES=${PIPESTATUS[0]}
|
||
|
if [ $RES -eq 0 ]; then
|
||
|
echo "$(date) $0 $$ **** SUCCESS: ${CMD}"
|
||
|
elif [ $RES -eq 24 ]; then
|
||
|
echo "$(date) $0 $$ **** WARNING: ${CMD}"
|
||
|
mail -s "Backup Warning: $0" "${EMAIL}" < "${TMPFILE}"
|
||
|
else
|
||
|
echo "$(date) $0 **** ERROR $RES: ${CMD}" 1>&2
|
||
|
error
|
||
|
exit 2
|
||
|
fi
|
||
|
done
|
||
|
touch ${TARGET}/${HOST}-${TYPE}.new/backup-timestamp
|
||
|
|
||
|
# 5. Altern
|
||
|
if [ -d "${TARGET}/${HOST}-${TYPE}.1" ]; then
|
||
|
# 1. Älteste Sicherungskopie löschen
|
||
|
if [ -d "${TARGET}/${HOST}-${TYPE}.${NUM}" ]; then
|
||
|
echo "$(date) $0 $$ **** REMOVE: ${TARGET}/${HOST}-${TYPE}.${NUM}"
|
||
|
rm -rf "${TARGET}/${HOST}-${TYPE}.${NUM}"
|
||
|
fi
|
||
|
# 2. Bestehende Sicherungskopien um eine Stufe altern lassen
|
||
|
while [ $((--NUM)) -gt 0 ]; do
|
||
|
if [ -d "${TARGET}/${HOST}-${TYPE}.${NUM}" ]; then
|
||
|
echo "$(date) $0 $$ **** MOVE: ${TARGET}/${HOST}-${TYPE}.${NUM}"
|
||
|
mv ${TARGET}/${HOST}-${TYPE}.${NUM} \
|
||
|
${TARGET}/${HOST}-${TYPE}.$((NUM+1)) 2> /dev/null
|
||
|
fi
|
||
|
done
|
||
|
fi
|
||
|
|
||
|
# 6. move xxx.new to xxx.1
|
||
|
mv ${TARGET}/${HOST}-${TYPE}.new ${TARGET}/${HOST}-${TYPE}.1
|
||
|
|
||
|
echo "$(date) $0 $$ ############################################### Finished"
|
||
|
|
||
|
) 200> "${LOCK}" 2>&1 | tee "${TMPFILE}" >> "${LOGFILE}"
|
||
|
|
||
|
rm "${TMPFILE}" 2> /dev/null || true
|