# -*- mode:sh -*-
# Little common functions

# push a mirror attached to us.
# Arguments (using an array named SIGNAL_OPTS):
#
# $MIRROR      - Name for the mirror, also basename for the logfile
# $HOSTNAME    - Hostname to push to
# $USERNAME    - Username there
# $SSHPROTO    - Protocol version, either 1 or 2.
# $SSHKEY      - the ssh private key file to use for this push
# $SSHOPTS     - any other option ssh accepts, passed blindly, be careful
# $PUSHLOCKOWN - own lockfile name to touch after stage1 in pushtype=staged
# $PUSHTYPE    - what kind of push should be done?
#                all    - normal, just push once with ssh backgrounded and finish
#                staged - staged. first push stage1, then wait for $PUSHLOCKs to appear,
#                         then push stage2
# $PUSHARCHIVE - what archive to sync? (Multiple mirrors behind one ssh key!)
# $PUSHCB      - do we want a callback?
#
# This function assumes that the variable LOG is set to a directory where
# logfiles can be written to.
# Additionally $PUSHLOCKS has to be defined as a set of space delimited strings
# (list of "lock"files) to wait for if you want pushtype=staged
#
# Pushes might be done in background (for type all).
signal () {
	ARGS="SIGNAL_OPTS[*]"
	local ${!ARGS}

	MIRROR=${MIRROR:-""}
	HOSTNAME=${HOSTNAME:-""}
	USERNAME=${USERNAME:-""}
	SSHPROTO=${SSHPROTO:-""}
	SSHKEY=${SSHKEY:-""}
	SSHOPTS=${SSHOPTS:-""}
	PUSHLOCKOWN=${PUSHLOCKOWN:-""}
	PUSHTYPE=${PUSHTYPE:-"all"}
	PUSHARCHIVE=${PUSHARCHIVE:-""}
	PUSHCB=${PUSHCB:-""}

	# Defaults we always want, no matter what
	SSH_OPTIONS="-o BatchMode=yes -o SetupTimeOut=45 -o ConnectTimeout=45 -o PasswordAuthentication=no"

	if [ -n "${SSH_OPTS}" ]; then
		SSH_OPTIONS="${SSH_OPTIONS} ${SSH_OPTS}"
	fi

	if [ ${SSHPROTO} -ne 1 ] && [ ${SSHPROTO} -ne 2 ]; then
		# Idiots, we only want 1 or 2. Cant decide? Lets force 2
		${SSHPROTO}=2
	fi

	date -u >> "${LOGDIR}/${MIRROR}.log"

	PUSHARGS=""
	if [ -n "${PUSHARCHIVE}" ]; then
		PUSHARGS="${PUSHARGS} sync:archive:${PUSHARCHIVE}"
	fi
	if [ -n "${PUSHCB}" ]; then
		PUSHARGS="${PUSHARGS} sync:callback"
	fi

	if [ "xallx" = "x${PUSHTYPE}x" ]; then
		# Default normal "fire and forget" push
		PUSHARGS1="sync:all"
		ssh $SSH_OPTIONS -i "${SSHKEY}" -o"user ${USERNAME}" -${SSHPROTO} "${HOSTNAME}" "${PUSHARGS} ${PUSHARGS1}" >>"${LOGDIR}/${MIRROR}.log" 2>&1 &
	elif [ "xstagedx" = "x${PUSHTYPE}x" ]; then
		# Want a staged push. Fine, lets do that

		# Step1: Do a push to only sync stage1, do not background
		PUSHARGS1="sync:stage1"
		ssh $SSH_OPTIONS -i "${SSHKEY}" -o"user ${USERNAME}" -${SSHPROTO} "${HOSTNAME}" "${PUSHARGS} ${PUSHARGS1}" >>"${LOGDIR}/${MIRROR}.log" 2>&1
		touch "${PUSHLOCKOWN}"

		# Step2: Wait for all the other "lock"files to appear.
		tries=0
		# We do not wait forever
		while [ ${tries} -lt ${PUSHDELAY} ]; do
			total=0
			found=0
			for file in ${PUSHLOCKS}; do
				total=$((total + 1))
				if [ -f ${file} ]; then
					found=$((found + 1))
				fi
			done
			if [ ${total} -eq ${found} ] || [ -f "${LOCKDIR}/all_stage1" ]; then
				touch "${LOCKDIR}/all_stage1"
				break
			fi
			sleep 5
		done
		rm -f "${PUSHLOCKOWN}"

		# Step3: It either timed out or we have all the "lock"files, sync stage2
		PUSHARGS2="sync:stage2"
		ssh $SSH_OPTIONS -i "${SSHKEY}" -o"user ${USERNAME}" -${SSHPROTO} "${HOSTNAME}" "${PUSHARGS} ${PUSHARGS2}" >>"${LOGDIR}/${MIRROR}.log" 2>&1
	else
		# Can't decide? Then you get nothing.
		return
	fi

}

# callback, used by ftpsync
callback () {
	# Defaults we always want, no matter what
	SSH_OPTIONS="-o BatchMode=yes -o SetupTimeOut=45 -o ConnectTimeout=45 -o PasswordAuthentication=no"
	ssh $SSH_OPTIONS -i "$3" -o"user $1" "$2" callback:${HOSTNAME}
}

# log something (basically echo it together with a timestamp)
#
# Set $PROGRAM to a string to have it added to the output.
log () {
	if [ -z "${PROGRAM}" ]; then
		echo "$(date +"%b %d %H:%M:%S") $(hostname -s) [$$] $@"
	else
		echo "$(date +"%b %d %H:%M:%S") $(hostname -s) ${PROGRAM}[$$]: $@"
	fi
}

# log the message using log() but then also send a mail
# to the address configured in MAILTO (if non-empty)
error () {
	log "$@"
	if [ -z "${MAILTO}" ]; then
		echo "$@" | mail -e -s "[$PROGRAM@$(hostname -s)] ERROR [$$]" ${MAILTO}
	fi
}

# run a hook
# needs array variable HOOK setup with HOOKNR being a number an HOOKSCR
# the script to run.
hook () {
	ARGS='HOOK[@]'
	local "${!ARGS}"
	if [ -n "${HOOKSCR}" ]; then
		log "Running hook $HOOKNR: ${HOOKSCR}"
		set +e
		${HOOKSCR}
		result=$?
		set -e
		log "Back from hook $HOOKNR, got returncode ${result}"
		return $result
	else
		return 0
	fi
}

# Return the list of 2-stage mirrors.
get2stage() {
	egrep '^staged' "${MIRRORS}" | {
		while read MTYPE MLNAME MHOSTNAME MUSER MPROTO MKEYFILE; do
			PUSHLOCKS="${LOCKDIR}/${MLNAME}.stage1 ${PUSHLOCKS}"
		done
		echo "$PUSHLOCKS"
	}
}

# Rotate logfiles
savelog() {
    torotate="$1"
	count=${2:-${LOGROTATE}}
	while [ ${count} -gt 0 ]; do
		prev=$(( count - 1 ))
		if [ -e "${torotate}.${prev}" ]; then
			mv "${torotate}.${prev}" "${torotate}.${count}"
		fi
		count=$prev
	done
	mv "${torotate}" "${torotate}.0"
}
