250 lines
7.0 KiB
Bash
Executable File
250 lines
7.0 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
#DEBUG="true"
|
|
#DEBUG_CLEANUP="true"
|
|
|
|
CONFIG="$1"
|
|
[ -n "$CONFIG" ] || CONFIG="/etc/backup.conf"
|
|
[ -f "$CONFIG" ] || exit 1
|
|
|
|
DATE="$(date +%Y-%m-%d)"
|
|
BACKUP_PREFIX="$(hostname).$DATE"
|
|
|
|
umask 0077
|
|
tmpdir=$(mktemp -d) || exit 1
|
|
|
|
. $CONFIG
|
|
|
|
[ -n "$LOCAL_STORAGE_PATH" -a ! -d "$LOCAL_STORAGE_PATH" ] && {
|
|
echo "directory does not exist: <${LOCAL_STORAGE_PATH}>"
|
|
exit 1
|
|
}
|
|
|
|
# log to stderr when debugging
|
|
[ -z "$DEBUG" ] && exec 2>${tmpdir}/msg
|
|
|
|
filesize() {
|
|
du -shcx $1 2>/dev/null | awk '/total/{ printf("%4s",$1); }'
|
|
}
|
|
|
|
filestat() {
|
|
format="[%A] %U(%u):%G(%g)"
|
|
[ -b "$1" -o -c "$1" ] && format="$format Maj:%T Min:%t"
|
|
stat -c "$format %N" $1 2>/dev/null
|
|
}
|
|
|
|
storage() {
|
|
FILENAME="$1"
|
|
|
|
# put file in local storage directory and copy it later to remote storage
|
|
if [ -n "$LOCAL_STORAGE_PATH" ]; then
|
|
cat > ${LOCAL_STORAGE_PATH}/${FILENAME}
|
|
echo "$FILENAME: ($(filesize ${LOCAL_STORAGE_PATH}/${FILENAME}))" >&2
|
|
|
|
# copy file only via ssh
|
|
elif [ "$REMOTE_STORAGE_TYPE" == "ssh" ]; then
|
|
ssh $REMOTE_STORAGE_HOST "cat > ${REMOTE_STORAGE_PATH}/${FILENAME}"
|
|
|
|
# copy file only via ftp
|
|
elif [ "$REMOTE_STORAGE_TYPE" == "ftp" ]; then
|
|
ncftpput $ftp_cred -U 0077 -V -c $REMOTE_STORAGE_HOST "${REMOTE_STORAGE_PATH}/${FILENAME}"
|
|
fi
|
|
}
|
|
|
|
# assumes '.' as seperator and date (YYYY-MM-DD) in 2nd field
|
|
# eg. $(hostname).$(date).tar.gz)
|
|
calcold() {
|
|
filelist="$1"
|
|
keep="$2"
|
|
cut -d'.' -f2 $filelist > ${filelist}.dates
|
|
date -d "-${keep}" "+%Y-%m-%d-XXX" >> ${filelist}.dates
|
|
sort -r -u < ${filelist}.dates > ${filelist}.dates.uniq
|
|
|
|
cnt=0
|
|
mark=0
|
|
for datestamp in $(cat $filelist.dates.uniq); do
|
|
if $(echo $datestamp | grep -q "XXX"); then
|
|
mark=1
|
|
continue;
|
|
fi
|
|
|
|
cnt=$[cnt +1]
|
|
# keep at least this +1 backups, even if it's older
|
|
if [ "$cnt" -gt 1 -a "$mark" = "1" ]; then
|
|
grep "$datestamp" $filelist
|
|
fi
|
|
done
|
|
}
|
|
|
|
echo "$(basename $0) running on $(hostname) [${DATE}]" >&2
|
|
|
|
if [ -n "$LOCAL_STORAGE_PATH" ]; then
|
|
echo "LOCAL_STORAGE: $LOCAL_STORAGE_PATH (keep $LOCAL_STORAGE_KEEP)" >&2
|
|
else
|
|
echo "LOCAL_STORAGE: none" >&2
|
|
fi
|
|
|
|
case "$REMOTE_STORAGE_TYPE" in
|
|
ftp) # ftp server
|
|
ftp_user=$(echo $REMOTE_STORAGE_CRED | cut -d':' -f1)
|
|
ftp_pass=$(echo $REMOTE_STORAGE_CRED | cut -d':' -f2)
|
|
ftp_cred="-u $ftp_user -p $ftp_pass"
|
|
|
|
[ -z "$REMOTE_STORAGE_PATH" ] && REMOTE_STORAGE_PATH="/"
|
|
echo "REMOTE_STORAGE: ftp://${REMOTE_STORAGE_HOST}/${REMOTE_STORAGE_PATH} (keep $REMOTE_STORAGE_KEEP)" >&2
|
|
;;
|
|
|
|
ssh) # ssh / scp
|
|
[ -z "$REMOTE_STORAGE_PATH" ] && REMOTE_STORAGE_PATH="."
|
|
[ -n "$REMOTE_STORAGE_BANDWIDTH" ] && REMOTE_STORAGE_BANDWIDTH="-l $REMOTE_STORAGE_BANDWIDTH"
|
|
echo "REMOTE_STORAGE: ssh://${REMOTE_STORAGE_HOST}:${REMOTE_STORAGE_PATH} (keep $REMOTE_STORAGE_KEEP)" >&2
|
|
;;
|
|
|
|
*) echo "REMOTE_STORAGE: none" >&2
|
|
REMOTE_STORAGE_TYPE="none"
|
|
;;
|
|
esac
|
|
|
|
created_files=""
|
|
for name in $BACKUPS; do
|
|
eval type=\${BACKUP_${name}}
|
|
|
|
echo -e "\nBACKUP_$name: (type: ${type})" >&2
|
|
|
|
case "$type" in
|
|
tar) # tar.gz of files
|
|
eval files=\${BACKUP_${name}_FILES}
|
|
eval exclude=\${BACKUP_${name}_EXCLUDE}
|
|
|
|
for i in $files; do
|
|
echo " adding $i" >&2
|
|
done
|
|
|
|
excluded="--exclude $tmpdir"
|
|
[ -n "$LOCAL_STORAGE_PATH" ] && excluded="$excluded --exclude $LOCAL_STORAGE_PATH"
|
|
for tmp in $exclude; do
|
|
echo " excluding $(filestat $tmp)" >&2
|
|
excluded="${excluded} --exclude ${tmp}"
|
|
done
|
|
|
|
filename="${BACKUP_PREFIX}.${name}.tar.gz"
|
|
[ -n "$files" ] && tar --numeric-owner -C / -czf - $excluded $files | storage $filename
|
|
;;
|
|
|
|
mysql) # mysql dump of some/all tables
|
|
eval user=\${BACKUP_${name}_USER}
|
|
eval pass=\${BACKUP_${name}_PASS}
|
|
eval db_list=\${BACKUP_${name}_DBS}
|
|
|
|
auth="-u${user} -p${pass}"
|
|
[ -n "$pass" ] || auth="-u ${user}"
|
|
|
|
if [ -z "$db_list" ]; then
|
|
db_list=$(echo "show databases;" | /usr/bin/mysql ${auth} -r --column-name=FALSE)
|
|
fi
|
|
|
|
mysql_schema_dump="/usr/bin/mysqldump ${auth} --opt --all --no-data"
|
|
mysql_data_dump="/usr/bin/mysqldump ${auth} --opt --add-locks=FALSE --no-create-info"
|
|
|
|
created_mysql_files=""
|
|
for db in $db_list; do
|
|
echo " database $db" >&2
|
|
|
|
$mysql_schema_dump --databases $db > ${tmpdir}/${db}-schema.sql
|
|
$mysql_data_dump --databases $db > ${tmpdir}/${db}-data.sql
|
|
created_mysql_files="$created_mysql_files ${db}-schema.sql ${db}-data.sql"
|
|
done
|
|
|
|
filename="${BACKUP_PREFIX}.${name}.tar.gz"
|
|
[ -n "$created_mysql_files" ] && tar -C $tmpdir -czf - $created_mysql_files | storage $filename
|
|
;;
|
|
|
|
ldap) # slapcat of slapd database
|
|
filename="${BACKUP_PREFIX}.${name}.ldif.gz"
|
|
slapcat | gzip | storage $filename
|
|
;;
|
|
|
|
*) echo "Invalid backup type: <${type}>" >&2
|
|
continue
|
|
;;
|
|
esac
|
|
created_files="$created_files $filename"
|
|
done
|
|
|
|
[ -n "$created_files" ] || {
|
|
echo -e "\nERROR(?): no files created." >&2
|
|
rm -rf $tmpdir
|
|
exit
|
|
}
|
|
|
|
# store a log (only when not debugging)
|
|
if [ -z "$DEBUG" ]; then
|
|
cat ${tmpdir}/msg | storage ${BACKUP_PREFIX}.log
|
|
created_files="$created_files ${BACKUP_PREFIX}.log"
|
|
fi
|
|
|
|
if [ -n "$LOCAL_STORAGE_PATH" ]; then
|
|
|
|
local_files=""
|
|
# check files & prefix with local path
|
|
for created in $created_files; do
|
|
[ -e "${LOCAL_STORAGE_PATH}/${created}" ] && local_files="$local_files ${LOCAL_STORAGE_PATH}/${created}"
|
|
done
|
|
|
|
# copy local files to remote storage
|
|
case "$REMOTE_STORAGE_TYPE" in
|
|
ftp) echo -e "\ncopy archives to REMOTE_STORAGE" >&2
|
|
ncftpput $ftp_cred -U 0077 -V $REMOTE_STORAGE_HOST $REMOTE_STORAGE_PATH $local_files
|
|
;;
|
|
|
|
ssh) echo -e "\ncopy archives to REMOTE_STORAGE" >&2
|
|
scp -p -q $REMOTE_STORAGE_BANDWIDTH $local_files "${REMOTE_STORAGE_HOST}:${REMOTE_STORAGE_PATH}"
|
|
;;
|
|
esac
|
|
|
|
# cleanup local storage
|
|
echo -e "\nremoving old (> $LOCAL_STORAGE_KEEP) backups from LOCAL_STORAGE" >&2
|
|
|
|
{ cd ${LOCAL_STORAGE_PATH}; ls -1 $(hostname).* > ${tmpdir}/local.files; }
|
|
local_old_files=$(calcold ${tmpdir}/local.files "$LOCAL_STORAGE_KEEP")
|
|
|
|
for old_file in $local_old_files; do
|
|
echo " removing $old_file ($(filesize $old_file))" >&2
|
|
[ -z "$DEBUG_CLEANUP" ] && rm -f ${LOCAL_STORAGE_PATH}/${old_file}
|
|
done
|
|
fi
|
|
|
|
# cleanup remote storage
|
|
case "$REMOTE_STORAGE_TYPE" in
|
|
ftp) echo -e "\nremoving old (> $REMOTE_STORAGE_KEEP) backups from REMOTE_STORAGE" >&2
|
|
|
|
ncftpls $ftp_cred "ftp://${REMOTE_STORAGE_HOST}${REMOTE_STORAGE_PATH}" > ${tmpdir}/remote.files
|
|
remote_old_files=$(calcold ${tmpdir}/remote.files "$REMOTE_STORAGE_KEEP")
|
|
|
|
for old_file in $remote_old_files; do
|
|
echo " removing $old_file" >&2
|
|
[ -z "DEBUG_CLEANUP" ] && \
|
|
ncftpls $ftp_cred -X "DELE $old_file" "ftp://${REMOTE_STORAGE_HOST}${REMOTE_STORAGE_PATH}" > /dev/null
|
|
done
|
|
;;
|
|
|
|
ssh) echo -e "\nremoving old (> $REMOTE_STORAGE_KEEP) backups from REMOTE_STORAGE" >&2
|
|
|
|
ssh $REMOTE_STORAGE_HOST "cd ${REMOTE_STORAGE_PATH}; ls -1 $(hostname).*" > ${tmpdir}/remote.files
|
|
remote_old_files=$(calcold ${tmpdir}/remote.files "$REMOTE_STORAGE_KEEP")
|
|
|
|
if [ -n "$remote_old_files" ]; then
|
|
remote_old_paths=""
|
|
for old_file in $remote_old_files; do
|
|
echo " removing $old_file" >&2
|
|
remote_old_paths="$remote_old_paths ${REMOTE_STORAGE_PATH}/${old_file}"
|
|
done
|
|
[ -z "$DEBUG_CLEANUP" ] && ssh $REMOTE_STORAGE_HOST "rm $remote_old_paths"
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
[ -n "$MAILADDRESS" -a -z "$DEBUG" ] && mail -s "full-system-backup on $(hostname)" $MAILADDRESS < ${tmpdir}/msg
|
|
|
|
rm -rf $tmpdir
|