#!/bin/bash

# Released under the terms of GPLv3 or at your option any later version.
# No warranties.
# Copyright 2009 Enrico Tassi <gares@fettunta.org>

# The config file name
CONFFILE=~/.smd/loop

# The lock file name
LOCKFILE=~/.smd/loop.lock

if [ -e $LOCKFILE ]; then
	if ps -p `cat $LOCKFILE` > /dev/null | grep smd-loop; then
		echo Another smd-loop instance is running. If it is not the case
		echo remove $LOCKFILE and retry
		echo "any: smd-loop@localhost: TAGS: error::context(locking) probable-cause(another-instance-is-running) human-intervention(necessary) suggested-actions(run(kill `cat $LOCKFILE`) run(rm $LOCKFILE))"
		exit 1
	else
		echo "Found lock file of a dead instance. Ignored."
	fi
fi
echo $$ > $LOCKFILE

# The log file
LOGFILE=~/.smd/log/loop.log
mkdir -p `dirname $LOGFILE`
> $LOGFILE
log() {
	echo `date '+%x %X'`: $@ >> $LOGFILE 
}
log_cat() {
	cat $1 | sed 's/^/output: /' >> $LOGFILE 
}

# The length of a minute, decrease to debug
MINUTE=60

# The clock, incremented every $MINUTE
TIME=1

# Verbose
VERBOSE=0

# Just create a template
TEMPLATE_ONLY=0

# Temp file, used to store subprocesses' output
OUTPUT=`mktemp -q /tmp/smd-loop.XXXXXXXXXX`

# List of commands that failed
STOP_TAG="__STOP__"
declare -a FAILURES=("$STOP_TAG")

# Prefix
PREFIX="@PREFIX@"

if [ `echo $PREFIX | cut -c -1` = "@" ]; then
	echo "smd-loop not installed, assuming smd-pull is ./smd-pull"
	echo "smd-loop not installed, assuming smd-push is ./smd-push"
	log "smd-loop not installed, assuming smd-pull is ./smd-pull"
	log "smd-loop not installed, assuming smd-push is ./smd-push"
	PULL="./smd-pull"
	PUSH="./smd-push"
else
	PULL="$PREFIX/bin/smd-pull"
	PUSH="$PREFIX/bin/smd-push"
fi

remove_from_failures() {
	local item="$1"
	for ((i=0; ; i++)); do
		if [ "${FAILURES[$i]}" = "$item" ]; then
			unset FAILURES[$i]
		fi

		if [ "${FAILURES[$i]}" = "$STOP_TAG" ]; then
			break
		fi
	done
}

add_to_failures() {
	local item="$1"
	for ((i=0; ; i++)); do
		if [ "${FAILURES[$i]}" = "" ]; then
			FAILURES[$i]="$item"
			break
		fi

		if [ "${FAILURES[$i]}" = "$STOP_TAG" ]; then
			FAILURES[$i]="$item"
			FAILURES[`expr $i + 1`]="$STOP_TAG"
			break
		fi
	done
}

has_not_failed() {
	local item="$1"
	local found=0

	for ((i=0; ; i++)); do
		if [ "${FAILURES[$i]}" = "$item" ]; then
			found=1
		fi

		if [ "${FAILURES[$i]}" = "$STOP_TAG" ]; then
			break
		fi
	done

	return $found
}

perform() {
	local cmd=$1
	local endpoint=$2

	local cmd_line="$cmd -v $endpoint"
	if [ "$VERBOSE" = 1 ]; then
		echo smd-loop: $cmd_line 1>&2
	fi
	log "$cmd_line"
	$cmd_line > $OUTPUT 2>&1
	if [ $? = 0 ]; then
		cat $OUTPUT
		remove_from_failures "$cmd_line"
		log "completed successfully"
	else
		if grep -q 'human-intervention(avoidable)' $OUTPUT &&
		   grep -q 'suggested-actions(retry)' $OUTPUT &&
		   has_not_failed "$cmd_line"; then
			if [ "$VERBOSE" = 1 ]; then
				echo smd-loop: warning: failed: $cmd_line 1>&2
				echo smd-loop: warning: will retry later 1>&2
			fi
			add_to_failures "$cmd_line"
			log "avoidable failure, retry later"
			log_cat $OUTPUT
		else
			cat $OUTPUT
			log "persistent or non avoidable failure"
			log_cat $OUTPUT
			exit 1
		fi
	fi
}

cleanup() {
	rm -f $OUTPUT
	rm -f $LOCKFILE
	log "exiting"
}

cleanup_killed() {
	clenup
	log "killed"
	exit 1
}

trap cleanup "EXIT" 
trap cleanup_killed "SIGTERM" 

if [ "$1" = "-v" ]; then
	VERBOSE=1
	shift
fi

if [ "$1" = "-t" ]; then
	TEMPLATE_ONLY=1
	shift
fi

if [ ! -f $CONFFILE ]; then
	mkdir -p `dirname $CONFFILE`
	cat > $CONFFILE <<-EOT
	# smd-loop configuration file
	#
	# Line starting with '#' are comments.
	# Frequences are in minutes.
	#
	# pull-frequency push-frequency endpoint-name
	  3              10             default
	EOT

	echo No config file found: created a default one
	echo Please edit it: $CONFFILE
	exit 1
fi

if [ "$TEMPLATE_ONLY" = 1 ]; then
	exit 0
fi

log "starting"
while true; do
	while read pull push endpoint; do
		do_pull=1
		do_push=1
		if [ $pull -gt 0 ]; then do_pull=$((TIME % pull)); fi
		if [ $push -gt 0 ]; then do_push=$((TIME % push)); fi
	
		if [ $do_pull -eq 0 ]; then perform $PULL $endpoint; fi
		if [ $do_push -eq 0 ]; then perform $PUSH $endpoint; fi
	done < <(grep -v '^#' $CONFFILE)
	TIME=$((TIME+1))
	sleep $MINUTE 
done
