#!/bin/bash # alfplayer # 2014-06-12 # # Mirror the whole Parabola repository using rsync and keep snapshots set -e script_filename="$(basename "$0")" # Create temporary log file log_tmp="$(mktemp -p /var/tmp)" # Default configuration values. # They can be overridden by setting the variables in the calling environment. # 1 to set, 0 to unset : ${repo:=rsync://repo.parabolagnulinux.org:875/repos} : ${base_dir:=/srv/http} : ${parabola_dir:=${base_dir}/parabola} : ${oldest_version:=10} # snapshots older than this one will be deleted (see also no_delete above) : ${log_file:=${script_filename}.log} #: ${mail_to:=your@mail.com} # comment out to disable #: ${date_exclude:=2014.06.19} # disable running this script this date (see next line) : ${forcerun:=0} # force running on excluded date : ${terminal:=1} # outputs to stdout using rsync --progress (and logs to a file) : ${no_delete:=0} # disable deletion of oldest snapshots #: ${TZ:=UTC} # set time zone export TZ # Lock with flock (provided by util-linux), save to log file and send email on exit LOCKFILE="/var/lock/${script_filename}" LOCKFD=99 _lock() { flock -$1 $LOCKFD; } # Wait until there is no process writing to ${log_tmp} # Waiting can be necessary if command groups are not executed until the end _wait_log_tmp() { if [[ -e "${log_tmp}" ]] ; then while fuser "${log_tmp}" > /dev/null ; do sleep 0.2 done fi } _no_more_locking() { set +e # Save exit status es=$? _wait_log_tmp cat "${log_tmp}" >> "${log_file}" if [[ $? == 0 ]] ; then log_written=1 else echo "=> ERROR: Failed to write to log file: ${log_file}" >&2 fi if [[ -e ${parabola_dir}.tmp ]] ; then echo "=> WARNING: Directory with partial transfer ${LOCAL}.tmp remains in file system" fi if [[ ${mail_to} ]] ; then echo "=> Sending output to ${mail_to}" mail -s "[$(hostname)] ${script_filename}" "${mail_to}" < "${log_tmp}" fi if [[ ${log_written} == 1 ]] ; then rm -f "${log_tmp}" fi if [[ $es != 0 ]] ; then echo "=> WARNING: Unsuccessful script termination. Exit status: $es" if [[ ${mail_to} ]] ; then echo "=> Sending error notification to ${mail_to}" mail -s "[$(hostname)] ${script_filename} failed. See logged output." "${mail_to}" < /dev/null fi fi _lock u _lock xn && \ rm -f $LOCKFILE } _prepare_locking() { eval "exec $LOCKFD>\"$LOCKFILE\""; trap _no_more_locking EXIT; } _prepare_locking # Lock now. The lock is disabled automatically when the script exits (with any error code). if ! _lock xn ; then echo "=> ERROR: Could not obtain lock. Exiting." >&2 exit 1 fi date="$(date +%Y.%m.%d)" current="${parabola_dir}-${date}" local_useful=0 { if [[ ${date_exclude} && ${forcerun} != 1 ]] ; then if [[ $DATE == ${date_exclude} ]] ; then echo "Manually disabled: ${date}. Exiting." exit 0 fi fi # Test if ${parabola_dir} is an existing symlink pointing to an existing directory if [[ -h ${parabola_dir} ]] ; then last_path="$(readlink -f "${parabola_dir}")" if [[ -d ${last_path} ]] ; then last="${last_path##*/}" local_useful=1 else echo "=> ERROR: ${parabola_dir} is a symlink which does not point to an existing directory." >&2 exit 1 fi else echo "=> ERROR: ${parabola_dir} does not exist or is not a symlink." >&2 exit 1 fi echo echo "=> Creating snapshot for date ${date}" if [[ -e "${current}" ]] ; then echo "${current} already exists. Exiting." >&2 exit 1 fi if [[ -e "${parabola_dir}".tmp ]] ; then echo "${parabola_dir}.tmp already exists. Resuming." else cp -al "${last_path}" "${parabola_dir}".tmp fi } &> >(tee -a "${log_tmp}") _wait_log_tmp rsync "${repo}"/ -rtvlH ${terminal:+--progress} --log-file="${log_tmp}" --safe-links --delete --link-dest="$base_dir"/archlinux/ --link-dest="$parabola_dir" "$parabola_dir".tmp { cd "$base_dir" echo "=> Delete versions older than the ${oldest_version} oldest version" delete_list=( $(for dir in parabola-* ; do echo "$(find "$dir" -type f -printf '%T@\n' | sort -n | tail -1) $dir" ; done | awk '{print $2}' | head -n -"${oldest_version}") ) for dir in ${delete_list[@]} ; do if [[ ${no_delete} != 1 ]] ; then echo "$dir" rm -rf "$dir" else echo "DRY-RUN: rm -rf $dir" fi done echo "=> Start serving the new repository version" mv "${parabola_dir}.tmp" "${current}" if [[ ${local_useful} == 1 ]] ; then echo " => Deleting ${parabola_dir} symlink" rm -rf "${parabola_dir}" fi # Create symlink echo " => Creating symlink ${current} to ${parabola_dir}" ln -s ${current##*/} ${parabola_dir} echo "=> Disk space report" df -h / echo "=> ${script_filename} finished successfully. Finish time: $(date --rfc-3339=seconds)" } &> >(tee -a "${log_tmp}") _wait_log_tmp