random-commands/mirror

403 lines
13 KiB
Bash
Executable file

#!/bin/bash
# Perform mirroring tasks for slackware.uk.
# Version: 0.4.0
# Copyright (c) 2026:
# Darren 'Tadgy' Austin <darren (at) afterdark.org.uk>
# Licensed under the terms of the GNU General Public License version 3.
# shellcheck disable=SC2034
# Script config.
DEBUG=":" # Set to 'echo' for debugging, or ':' for none
SLEEP="5m" # Sleep time between syncs when there was an error in the sync
ERRORS_MAX="3" # After this many errors in the sync tries, give up
MAX_PROC="3600" # If a run takes longer than this time (in seconds), a new run is started
MAX_RUNS="3" # Maximum number of runs
# Sync config.
IPV4="5.101.171.215"
DATADIR="/data/depository"
RSYNC_COMMAND="/usr/bin/rsync"
RSYNC_REMOTE_OPTIONS=('-4' "--address=$IPV4" '--no-motd' '--contimeout=30' '--timeout=60' '-aH' '--no-owner' '--no-group' '--no-perms' '--chmod=u+w,go-w,Dugo+rx,Fugo+rX' '--partial' '--partial-dir=.rsync-tmp' '--delete-delay' '--delay-updates')
RSYNC_LOCAL_OPTIONS=('-aH' '--chmod=u+w,go-w,Dugo+rx,Fugo+rX' '--partial' '--partial-dir=.rsync-tmp' '--delay-updates')
# RSYNC_VERBOSE=('--verbose' '--human-readable')
# RSYNC_VERBOSE=('--progress' '--verbose' '--stats' '--human-readable')
LFTP_COMMAND="/usr/bin/lftp"
LFTP_OPTIONS="set net:socket-bind-ipv4 $IPV4 && set xfer:log false && set net:timeout 30s && set xfer:timeout 60s && set mirror:parallel-directories true && mirror --continue --delete --parallel=10"
# LFTP_VERBOSE=('--verbose=3')
# Copy the new ISO into place when it's released.
# Only set this when a new Slackware release is imminent.
#NEW_SLACK_VERSION="15.1"
#
# Remotes
#
# Mirror info for people/alien
ALIEN_TYPE="rsync"
ALIEN_MIRROR="bear.alienbase.nl"
ALIEN_MODULE="mirrors/people/alien/"
ALIEN_DESTDIR="people/alien/"
ALIEN_OPTIONS=()
ALIEN_FILTER=()
# Mirror info for people/alien-current-iso
CURRENTISO_TYPE="rsync"
CURRENTISO_MIRROR="bear.alienbase.nl"
CURRENTISO_MODULE="mirrors/slackware/"
CURRENTISO_DESTDIR="people/alien-current-iso/"
CURRENTISO_OPTIONS=()
CURRENTISO_FILTER=('--include=slackware*-current-iso/***' '--exclude=*')
# Mirror info for people/alien-kde
KTOWN_TYPE="rsync"
KTOWN_MIRROR="slackware.nl"
KTOWN_MODULE="mirrors/alien-kde/"
KTOWN_DESTDIR="people/alien-kde/"
KTOWN_OPTIONS=()
KTOWN_FILTER=()
# Mirror info for liveslak/
LIVESLAK_TYPE="rsync"
LIVESLAK_MIRROR="bear.alienbase.nl"
LIVESLAK_MODULE="mirrors/slackware-live/"
LIVESLAK_DESTDIR="liveslak/"
LIVESLAK_OPTIONS=()
LIVESLAK_FILTER=()
# Mirror info for porteus
PORTEUS_TYPE="rsync"
PORTEUS_MIRROR="mirrors.dotsrc.org"
PORTEUS_MODULE="porteus/"
PORTEUS_DESTDIR="porteus/"
PORTEUS_OPTIONS=()
PORTEUS_FILTER=()
# Mirror info for people/r0ni
R0NI_TYPE="rsync"
R0NI_MIRROR="slackware.lngn.net"
R0NI_MODULE="slackware-lngn-net/"
R0NI_DESTDIR="people/r0ni/"
R0NI_OPTIONS=()
R0NI_FILTER=()
# Mirror info for people/rworkman
RWORKMAN_TYPE="rsync"
RWORKMAN_MIRROR="harrier.slackbuilds.org"
RWORKMAN_MODULE="all-public/rworkman/"
RWORKMAN_DESTDIR="people/rworkman/"
RWORKMAN_OPTIONS=()
RWORKMAN_FILTER=('--exclude=index.php' '--exclude=favicon.ico')
# Mirror info for Salix
SALIX_TYPE="rsync"
SALIX_MIRROR="rsync.salixos.org"
SALIX_MODULE="salix/"
SALIX_DESTDIR="salix/"
SALIX_OPTIONS=()
SALIX_FILTER=('--exclude=/sbo')
# Mirror info for slackbuilds.org
SBO_TYPE="rsync"
SBO_MIRROR="slackbuilds.org"
SBO_MODULE="slackbuilds/"
SBO_DESTDIR="sbo/"
SBO_OPTIONS=()
SBO_FILTER=()
# Mirror info for slackware
SLACKTREE_TYPE="rsync"
SLACKTREE_MIRROR="ftp.osuosl.org"
SLACKTREE_MODULE="slackware/"
SLACKTREE_DESTDIR="slackware/"
SLACKTREE_OPTIONS=()
SLACKTREE_FILTER=('--exclude=/*-iso' '--exclude=/slackware-pre*' '--exclude=/slackware-[1234789].*' '--exclude=/slackware-1[012].*')
# Mirror info for Zenwalk
ZENWALK_TYPE="rsync"
ZENWALK_MIRROR="download.zenwalk.org"
ZENWALK_MODULE="zenwalk/"
ZENWALK_DESTDIR="zenwalk/"
ZENWALK_OPTIONS=()
ZENWALK_FILTER=('--exclude=/download' '--exclude=/x86_64/people')
#
# Locals
#
# Mirror info for the multilib cumulative tree.
MULTILIBARCHIVE_TYPE="rsync"
MULTILIBARCHIVE_LOCAL="people/alien/multilib/"
MULTILIBARCHIVE_DESTDIR="cumulative/multilib/"
MULTILIBARCHIVE_OPTIONS=()
MULTILIBARCHIVE_FILTER=('--exclude=source/')
# Mirror info for Slackware cumulative tree.
SLACKARCHIVE_TYPE="rsync"
SLACKARCHIVE_LOCAL="slackware/"
SLACKARCHIVE_DESTDIR="cumulative/"
SLACKARCHIVE_OPTIONS=()
SLACKARCHIVE_FILTER=('--exclude=source/' '--include=/slackware-15.0' '--include=/slackware64-15.0' '--include=/slackware-current' '--include=/slackware64-current' '--exclude=/*')
#
# lftp example
#
# EXAMPLE_TYPE="lftp"
# EXAMPLE_MIRROR="http://example.com"
# EXAMPLE_MODULE="/"
# EXAMPLE_DESTDIR="example/"
# EXAMPLE_OPTIONS=()
# EXAMPLE_FILTER=('-X' 'exclude-item/')
#
# Inactive/dead projects
#
# SLACKWARELOONG Dead repository address
# SLACKDCE Dead repository address
# ALPHAGEEK Alphageek died
# MICROLINUX Project dead - informed by maintaner
# ALIENOPENVZ Project dead - informed by alienBOB
# ALIENARM Project dead - informed by alienBOB
# SLACKONLY Dead repository address
# SLACKY Dead repository address
# SLACKAR Dead repository address
# SLARM64 Project dead
#######################################################################################################################################
# Only allow the script to be run from the wrapper.
[[ ! -v MIRRORING_USER ]] && {
echo "ERROR: this script should only be run by mirror-wrapper" >&2
exit 1
}
# Make sure the users match.
[[ "$(whoami)" != "$MIRRORING_USER" ]] && {
echo "ERROR: this script should be run by the '$MIRRORING_USER' only - use su to run manually" >&2
exit 1
}
# Move to the depository directory.
cd "$DATADIR" >/dev/null 2>&1 || {
echo "ERROR: $DATADIR does not exist." >&2
exit 1
}
# Only allow one copy of the script to run at any time.
# shellcheck disable=SC2154
if [[ "$FLOCK" != "$0" ]]; then
# shellcheck disable=SC2093
exec env FLOCK="$0" flock -E 10 -e -n "$0" "$0" "$@"
ERR="$?"
if (( ERR == 10 )); then
# File is locked, exit now.
exit 0
elif (( ERR > 0 )); then
echo "ERROR: flock execution error" >&2
exit 1
fi
fi
# Variables
declare -a LFTP_LIST RSYNC_LIST
# Parse command line.
while [[ -n "$1" ]]; do
case "$1" in
-h|-help|--help)
echo "Usage: ${0##*/} [mirror-ID1 mirror-ID2 ...]"
exit 0
;;
*)
if [[ "$1" == "ALL" ]]; then
SYNC_ALL=1
shift
break
else
ITEM="${1^^}"
[[ ! -v ${ITEM}_DESTDIR ]] && {
echo "ERROR: '$1' is not a valid mirror ID" >&2
exit 1
}
TYPE="${ITEM}_TYPE"
if [[ "${!TYPE}" == "rsync" ]]; then
RSYNC_LIST+=("${ITEM}")
elif [[ "${!TYPE}" == "lftp" ]]; then
LFTP_LIST+=("${!ITEM}")
else
echo "ERROR: invalid type of mirroring for '${ITEM}'" >&2
exit 1
fi
fi
shift
;;
esac
done
# What should be sync'd, if not provided on the command line.
(( ${#RSYNC_LIST[@]} == 0 )) && {
# Main tree.
(( ${SYNC_ALL:-0} == 1 )) || [[ $(printf "%(%H)T") =~ .* ]] && RSYNC_LIST+=('SLACKTREE')
# Other remotes.
(( ${SYNC_ALL:-0} == 1 )) || [[ $(printf "%(%H)T") =~ (02|08|14|20) ]] && RSYNC_LIST+=('ALIEN')
(( ${SYNC_ALL:-0} == 1 )) || [[ $(printf "%(%H)T") =~ (02|08|14|20) ]] && RSYNC_LIST+=('CURRENTISO')
(( ${SYNC_ALL:-0} == 1 )) || [[ $(printf "%(%H)T") =~ (02|08|14|20) ]] && RSYNC_LIST+=('KTOWN')
(( ${SYNC_ALL:-0} == 1 )) || [[ $(printf "%(%H)T") =~ (02|08|14|20) ]] && RSYNC_LIST+=('LIVESLAK')
(( ${SYNC_ALL:-0} == 1 )) || [[ $(printf "%(%H)T") =~ (02|08|14|20) ]] && RSYNC_LIST+=('PORTEUS')
(( ${SYNC_ALL:-0} == 1 )) || [[ $(printf "%(%H)T") =~ (02|08|14|20) ]] && RSYNC_LIST+=('R0NI')
(( ${SYNC_ALL:-0} == 1 )) || [[ $(printf "%(%H)T") =~ (02|08|14|20) ]] && RSYNC_LIST+=('RWORKMAN')
(( ${SYNC_ALL:-0} == 1 )) || [[ $(printf "%(%H)T") =~ (02|08|14|20) ]] && RSYNC_LIST+=('SALIX')
(( ${SYNC_ALL:-0} == 1 )) || [[ $(printf "%(%H)T") =~ (02|08|14|20) ]] && RSYNC_LIST+=('SBO')
(( ${SYNC_ALL:-0} == 1 )) || [[ $(printf "%(%H)T") =~ (02|08|14|20) ]] && RSYNC_LIST+=('ZENWALK')
# Locals.
(( ${SYNC_ALL:-0} == 1 )) || [[ $(printf "%(%H)T") =~ .* ]] && RSYNC_LIST+=('SLACKARCHIVE')
(( ${SYNC_ALL:-0} == 1 )) || [[ $(printf "%(%H)T") =~ (02|08|14|20) ]] && RSYNC_LIST+=('MULTILIBARCHIVE')
}
(( ${#LFTP_LIST[@]} == 0 )) && {
# Nothing to sync here currently.
:
}
# Mirror a new Slackware version.
[[ -v NEW_SLACK_VERSION ]] && {
"$DEBUG" "DEBUG: Checking for new Slackware release at $(date)." >&2
#export NEW_SLACK_VERSION
/opt/bin/mirror-new-slackware-release "$NEW_SLACK_VERSION"
"$DEBUG" "DEBUG: Finished checking for new release at $(date)." >&2
}
# Exit now if there's nothing to sync.
(( ${#RSYNC_LIST[@]} == 0 )) && (( ${#LFTP_LIST[@]} == 0 )) && {
exit 0
}
echo "INFO: Sync list: ${RSYNC_LIST[*]} ${LFTP_LIST[*]}" >&2
for RUN in $(seq -s' ' 1 "$MAX_RUNS"); do
START_TIME="$(printf "%(%s)T")"
# Mirrors synced using rsync
echo "INFO: Begining rsync run $RUN at $(printf "%(%c)T")" >&2
for VAR in "${RSYNC_LIST[@]}"; do
TYPE="${VAR}_TYPE"
MIRROR="${VAR}_MIRROR"
LOCAL="${VAR}_LOCAL"
MODULE="${VAR}_MODULE"
PASSWORD="${VAR}_PASSWORD"
DESTDIR="${VAR}_DESTDIR"
OPTIONS="${VAR}_OPTIONS[@]"
FILTER="${VAR}_FILTER[@]"
SRC=""
RSYNC_OPTS=""
ERR_COUNT=0
if [[ "${!TYPE}" != "rsync" ]]; then
echo "WARNING: Wrong type set for '$VAR' when in RSYNC_LIST." >&2
continue
elif [ -n "${!LOCAL}" ] && [ -n "${!MIRROR}" ]; then
echo "WARNING: Mutually exclusive LOCAL and MIRROR set for '$VAR'." >&2
continue
elif [ -z "${!LOCAL}" ] && [ -z "${!MIRROR}" ]; then
echo "WARNING: No LOCAL or MIRROR set for '$VAR'." >&2
continue
elif [ -n "${!MIRROR}" ] && [ -z "${!MODULE}" ]; then
echo "WARNING: No MODULE set for '$VAR'." >&2
continue
elif [ -n "${!LOCAL}" ]; then
# Use a local path.
SRC="${!LOCAL}"
RSYNC_OPTS="RSYNC_LOCAL_OPTIONS[@]"
else
# Use a remote mirror+module.
SRC="${!MIRROR}::${!MODULE}"
RSYNC_OPTS="RSYNC_REMOTE_OPTIONS[@]"
fi
echo "INFO: Begining processing of '$VAR' (run $RUN) at $(printf "%(%c)T")." >&2
"$DEBUG" "DEBUG: Command line: RSYNC_PASSWORD=\"XXX\" $RSYNC_COMMAND ${!RSYNC_OPTS} ${RSYNC_VERBOSE[*]} ${!OPTIONS} ${!FILTER} $SRC ${!DESTDIR}" >&2
while :; do
RSYNC_PASSWORD="${!PASSWORD}" "$RSYNC_COMMAND" "${!RSYNC_OPTS}" "${RSYNC_VERBOSE[@]}" "${!OPTIONS}" "${!FILTER}" "$SRC" "${!DESTDIR}" >&2
ERR_CODE="$?"
case "$ERR_CODE" in
'0')
break
;;
*)
(( ERR_COUNT++ ))
if (( ERR_COUNT < ERRORS_MAX )); then
echo "WARNING: Failed try $ERR_COUNT with error code: $ERR_CODE -- retrying in $SLEEP." >&2
sleep "$SLEEP"
continue
else
echo "WARNING: Failed try $ERR_COUNT with error code: $ERR_CODE -- giving up." >&2
break
fi
;;
esac
done
echo "INFO: Finished processing of '$VAR' at $(printf "%(%c)T")." >&2
done
echo "INFO: End of rsync run $RUN at $(printf "%(%c)T")" >&2
# Mirrors synced using lftp
echo "INFO: Begining lftp run $RUN at $(printf "%(%c)T")" >&2
for VAR in "${LFTP_LIST[@]}"; do
TYPE="${VAR}_TYPE"
MIRROR="${VAR}_MIRROR"
LOCAL="${VAR}_LOCAL"
MODULE="${VAR}_MODULE"
DESTDIR="${VAR}_DESTDIR"
OPTIONS="${VAR}_OPTIONS[@]"
FILTER="${VAR}_FILTER[@]"
ERR_COUNT=0
if [[ "${!TYPE}" != "lftp" ]]; then
echo "WARNING: Wrong type set for '$VAR' when in LFTP_LIST." >&2
continue
elif [ -n "${!LOCAL}" ] && [ -n "${!MIRROR}" ]; then
echo "WARNING: Mutually exclusive LOCAL and MIRROR set for '$VAR'." >&2
continue
elif [ -z "${!LOCAL}" ] && [ -z "${!MIRROR}" ]; then
echo "WARNING: No LOCAL or MIRROR set for '$VAR'." >&2
continue
elif [ -n "${!MIRROR}" ] && [ -z "${!MODULE}" ]; then
echo "WARNING: No MODULE set for '$VAR'." >&2
continue
fi
echo "INFO: Begining processing of '$VAR' (run $RUN) at $(printf "%(%c)T")." >&2
"$DEBUG" "DEBUG: Command line: $LFTP_COMMAND -c \"$LFTP_OPTIONS ${LFTP_VERBOSE[*]} ${!FILTER} ${!MIRROR}${!MODULE} ${!DESTDIR}\"" >&2
while :; do
"$LFTP_COMMAND" -c "$LFTP_OPTIONS ${LFTP_VERBOSE[*]} ${!FILTER} ${!MIRROR}/${!MODULE} ${!DESTDIR}" >&2
ERR_CODE="$?"
case "$ERR_CODE" in
'0')
break
;;
*)
(( ERR_COUNT++ ))
if (( ERR_COUNT < ERRORS_MAX )); then
echo "WARNING: Failed try $ERR_COUNT with error code: $ERR_CODE -- retrying in $SLEEP." >&2
sleep "$SLEEP"
continue
else
echo "WARNING: Failed try $ERR_COUNT with error code: $ERR_CODE -- giving up." >&2
break
fi
;;
esac
done
echo "INFO: Finished processing of $VAR at $(printf "%(%c)T")." >&2
done
echo "INFO: End of lftp run $RUN at $(printf "%(%c)T")" >&2
FINISH_TIME="$(printf "%(%s)T")"
if (( (FINISH_TIME - START_TIME) > MAX_PROC )); then
echo "WARNING: Max processing time (${MAX_PROC}s) exceeded - re-running syncs." >&2
continue
else
echo "INFO: Finished run $RUN at $(printf "%(%c)T")" >&2
break
fi
done
exit 0