#!/bin/bash # Perform mirroring tasks for slackware.uk. # Version: 0.4.0 # Copyright (c) 2026: # Darren 'Tadgy' Austin # 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