#!/bin/bash

set -e
set -u
# ERR traps should be inherited from functions too. (And command
# substitutions and subshells and whatnot, but for us the function is
# the important part here)
set -E

# A pipeline's return status is the value of the last (rightmost)
# command to exit with a non-zero status, or zero if all commands exit
# success fully.
set -o pipefail

# archive debian releases
#
# Copyright (C) 2021 Joerg Jaspert <joerg@debian.org>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; version 2.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

# shellcheck disable=SC2046
BINDIR=$(dirname $(readlink -f "$0"))
# shellcheck source=bin/include-git
source "${BINDIR}/include-git" ## INCLUDE COMMON

########################################################################
########################################################################
## functions                                                          ##
########################################################################
########################################################################

# Check what we are supposed to do
check_commandline() {
    while [[ $# -gt 0 ]]; do
        case "$1" in
            dist:*)
                # Which dist to archive, no default
                DIST=${1##dist:}
                ;;
            arch:*)
                # Which architecture to archive, no default
                ARCH=${1##arch:}
                ;;
            di:*)
                # Should we sync debian-installer? If set false, then
                # no, otherwise (true) will take same value as DIST
                DI=${1##di:}
                ;;
            section:*)
                # Which sections to archive? Defaults to main,contrib,non-free,main/debian-installer
                SECTIONS=${1##section:}
                ;;
            *)
                echo "Unknown option ${1} ignored"
                ;;
        esac
        shift  # Check next set of parameters.
    done
}

# All the stuff we want to do when we exit, no matter where
cleanup() {
    rc=$?

    trap - ERR TERM HUP INT QUIT EXIT
    # all done. Mail the log, exit.

    savelog "$LOG" > /dev/null

    rm -f "${LOCK}"

    exit $rc
}

########################################################################
########################################################################

# Who we are
PROGRAM="archive_release"
# Which dist to archive
DIST=""
# Which architecture to archive
ARCH=""
# Base target dir for the archive
ARCHIVEDIR=/srv/archive.debian.org/debian-archive
# Who should get error mails
# shellcheck disable=SC2034 # used in included common file
MAILTO=ftp-master@debian.org
# Log options
LOG=${LOG:-"${BASEDIR}/log/${PROGRAM}.log"}
LOGROTATE=${LOGROTATE:-14}
# Our trace and lock files
LOCK="${LOCKDIR}/${PROGRAM}"
# Store extra debmirror args
DEBEXTRA=""

# Check we are on the archive master host
HOST=$(hostname -s)
if [[ ${HOST} != fasolo ]]; then
    error "Not running on current archive-master host, exiting"
    exit 2
fi

create_lockdir
create_logdir

# Some sane defaults
cd "${ARCHIVEDIR:-}"
umask 022

# Open log and close stdin
open_log "$LOG"
exec 2>&1 <&-

# Check if we got told about stuff via ssh
if [[ -n ${SSH_ORIGINAL_COMMAND:-} ]]; then
    # shellcheck disable=SC2086
    check_commandline ${SSH_ORIGINAL_COMMAND}
fi

# Now, we can locally override (or supply) it all on commandline
if [[ $# -gt 0 ]]; then
    check_commandline "$@"
fi

# Nothing set yet, we error out
if [[ -z ${ARCH} ]] || [[ -z ${DIST} ]]; then
    error "Need arch and dist and maybe di supplied.
Possible arguments:
   dist:DISTNAME              - Which dist to archive
   arch:ARCH,...              - List of architectures to archive, comma separated
   di:[true|false|archlist]   - Sync d-i dists too? If true, dist list is taken from dist: param.
                                                    If false, not synced at all.
                                                    Otherwise comma separated list expected.
                                d-i architectures always taken from arch: parameter
   section:SECTIONS           - Which sections to archive
                                defaults to debmirror default of main,contrib,non-free,main/debian-installer
"
    exit 1
fi

log "Archival of ${DIST}, ${ARCH} started"

# We default to true
DI=${DI:-true}
# Now check if we should sync debian-installer
if [[ ${DI} == false ]]; then
    :
elif [[ ${DI} == true ]]; then
    DEBEXTRA="${DEBEXTRA} --di-dist=${DIST} --di-arch=${ARCH}"
else
    DEBEXTRA="${DEBEXTRA} --di-dist=${DI} --di-arch=${ARCH}"
fi

# Default is debmirrors default, but user may override it
SECTIONS=${SECTIONS:-"main,contrib,non-free,main/debian-installer"}

########################################################################

# Are we syncing security or main?
case ${DIST} in
    */updates|*-security)
        # Our target dir
        TO=${ARCHIVEDIR}/debian-security
        # From where to sync this?
        RSYNC_HOST=security-master.debian.org
        # And whats the rsync root/share called
        RSYNC_ROOT="debian-security"
        # Find a user/passwort we can use against rsync to fetch data
        # Data is stored in ~archvsync/etc/ftpsync-security.conf
        RSYNC_USER=$(awk -F '"' '/RSYNC_USER/ {print $2}' "${HOME}/etc/ftpsync-security.conf" )
        RSYNC_PASSWORD=$(awk -F '"' '/RSYNC_PASSWORD/ {print $2}' "${HOME}/etc/ftpsync-security.conf" )
        ;;
    *)
        # Our target dir
        TO=${ARCHIVEDIR}/debian
        # From where to sync this?
        RSYNC_HOST=fasolo.debian.org
        # And whats the rsync root/share called
        RSYNC_ROOT="debian"
        # Find a user/passwort we can use against rsync to fetch data
        # Data is stored in ~archvsync/rsyncd/*.secrets and we really don't care which user/pass we take
        LOGINDATA=$(grep -v '^#' "${HOME}/rsyncd/debian.secrets" | shuf -n 1)
        RSYNC_USER=${LOGINDATA%%:*}
        RSYNC_PASSWORD=${LOGINDATA##*:}
        ;;
esac

# rsync needs this
export RSYNC_PASSWORD

# Check to see if another sync is in progress
# shellcheck disable=SC2091,2046
if ! ( set -o noclobber; echo "$$" > "${LOCK}") 2> /dev/null; then
    # the lock will contain the right pid, thanks to $BASHPID
    # shellcheck disable=SC2086
    if ! $(kill -0 $(< "${LOCK}") 2>/dev/null); then
        # Process does either not exist or is not owned by us.
        echo "$$" > "${LOCK}"
    else
        echo "Unable to start archiving, lock file still exists, PID $(< ${LOCK})"
        exit 1
    fi
fi

# We want to cleanup, always
trap cleanup EXIT TERM HUP INT QUIT
log "Start at: $(LC_ALL=POSIX LANG=POSIX date -u -R)"

# Base debmirror args, thinks we always want.
BASEARGS="--progress --verbose --method=rsync --omit-suite-symlinks --rsync-extra=none --rsync-batch=2000 --i18n --getcontents --no-check-gpg --diff=none --nocleanup "

# shellcheck disable=SC2086
debmirror ${BASEARGS} ${DEBEXTRA} \
          --dist=${DIST}          \
          --arch=${ARCH}          \
          --section=${SECTIONS}   \
          --user=${RSYNC_USER}    \
          --host=${RSYNC_HOST}    \
          --root=${RSYNC_ROOT}    \
          ${TO}

# Remove the LOCK
rm -f "${LOCK}"

log "Trigger archive.d.o mirrors"
${BINDIR:+${BINDIR}/}runmirrors archive
