#!/usr/local/bin/cbsd
#v11.0.6
MYARG=""
MYOPTARG="reload"
MYDESC="Daemon for executing nexttask"
CBSDMODULE="taskd"
EXTHELP="wf_taskd"

. ${subrdir}/nc.subr

. ${cbsdinit}

. ${system}

[ -z "${reload}" ] && reload=0

# reset hour for fwupdate
last_hour_fwupdate=0

shmux()
{
	local _mylock SSH_ARGS _locallockfile _locallock _remotelockfile _remotelock

	_locallockfile="${ftmpdir}/shmux_${_ip}.lock"
	_locallock="${LOCKF_CMD} -s -t0 ${_locallockfile}"
	_remotelockfile="ftmp/from_${nodename}.lock"
	_remotelock="${LOCKF_CMD} -s -t0 ${_remotelockfile}"

	. ${nodes}

	if [ -f "${_locallockfile}" ]; then
		if check_locktime ${_locallockfile} >/dev/null 2>&1; then
			cbsdsqlrw nodes "UPDATE nodelist SET idle=datetime('now','localtime') WHERE ip=\"${_ip}\""
		fi
		return 0
	fi

	SSH_ARGS="-oControlPersist=30m -oBatchMode=yes -oStrictHostKeyChecking=no -oConnectTimeout=5 -q -oPort=${_port} -i ${_keyfile} -l ${cbsduser} ${_ip}"
	[ ! -f "${_locallockfile}" ] && ${miscdir}/daemon -u ${cbsduser} ${_locallock} ${SSH_CMD} ${SSH_ARGS} "${_remotelock} /usr/local/cbsd/misc/cbsd_dot --file=var/db/local.sqlite" > ${tmpdir}/inv.${_ip}.updated 2>/dev/null
}

update_jaillock()
{
	local jname

	for jname in $( cbsdsqlro local SELECT jname FROM jails WHERE status=1 ); do
		jaillock="${jailsysdir}/${jname}/locked"
		${DAEMON_CMD} -f -u ${cbsduser} ${LOCKF_CMD} -s -t0 ${ftmpdir}/update_jaillock.lock ${TOUCH_CMD} ${jaillock}
	done
}

update_fw_counters()
{
	local _curhour=$( ${DATE_CMD} "+%H" )

	[ "${_curhour}" = "${last_hour_fwupdate}" ] && return 0
	last_hour_fwupdate="${_curhour}"
	${DAEMON_CMD} ${LOCKF_CMD} -s -t0 ${ftmpdir}/fwcounters.lock /usr/local/bin/cbsd fwcounters jname=alljails
}

# if $1 = "force" skip for cbsd_dot check
update_inventory()
{
	[ -z "${_ip}" ] && return 0
	if [ "${1}" != "force" ]; then
		[ ! -s "${tmpdir}/inv.${_ip}.updated" ] && return 0
		cbsdlogger NOTICE ${CBSD_APP}: update remote inventory for ${_ip}: new changes
	fi
	${TRUNCATE_CMD} -s0 ${tmpdir}/inv.${_ip}.updated
	task mode=new exclusive=1 autoflush=2 owner=cbsddsys jname=#retrinv_${nodename} /usr/local/bin/cbsd retrinv node=${_nodename} >/dev/null 2>&1
	${DAEMON_CMD} -f nexttask
}

if [ -z "${workdir}" ]; then
	echo "cbsdd: empty workdir"
	exit 1
fi

readconf task.conf

[ ! -f "${dbdir}/cbsdtaskd.sqlite" ] && err 1 "No such ${dbdir}/cbsdtaskd.sqlite"
[ -z "${max_simul_jobs}" ] && max_simul_jobs="30"
[ -z "${loop_timeout}" ] && loop_timeout="30"

loop_per_retrinv=10	# one retrinv per 5 loop cycle's ( def: 10*30sec = once per <= 5 min )
retrinv_loop=0

# first of all, checks for orphaned tasks in status 1 (running) and back to status 0,
# due to next iteration take this job again

#store pid
echo $$ > ${workdir}/var/run/cbsdd.pid
trap "${RM_CMD} -f ${workdir}/var/run/cbsdd.pid" HUP INT QUIT ABRT KILL ALRM TERM BUS EXIT

cbsdsqlrw cbsdtaskd 'UPDATE taskd SET status="0" WHERE status="1"' 2>/dev/null

# remove stale lock and trash files
${FIND_CMD} -E ${ftmpdir} -depth 1 -maxdepth 1 \( -name shmux_\*.lock -or -name jstart\.\* -or -name jstop\.\* -or -name \*\.jconf \) -and -mtime +30m -delete > /dev/null 2>&1
${FIND_CMD} ${tmpdir} -depth 1 -maxdepth 1 -name inv.\*.updated -delete > /dev/null 2>&1

# retrinv
[ ${sqlreplica} -eq 1 ] && ${DAEMON_CMD} -f ${LOCKF_CMD} -s -t0 ${ftmpdir}/retrinv.lock /usr/local/bin/cbsd retrinv tryoffline=1

cbsdlogger NOTICE ${CBSD_APP}: cbsdd: max_simul_jobs:${max_simul_jobs},loop_timeout:${loop_timeout}

while [ 1 ]; do
	# internal tasks:
	update_jaillock
	[ ${ipfw_enable} -eq 1 ] && update_fw_counters

	unset ip port keyfile _ip _port _keyfile queue

	# make sshmux control master
	if [ ${sqlreplica} -eq 1 ]; then
		sqldelimer=" "
		cbsdsqlro nodes SELECT nodename,ip,port,keyfile FROM nodelist | while read _nodename _ip _port _keyfile; do
			[ -n "${_ip}" ] && shmux
			if [ ${retrinv_loop} -gt ${loop_per_retrinv} ]; then
				cbsdlogger NOTICE ${CBSD_APP}: update remote inventory for ${_ip} via loop_per_retrinv [${loop_per_retrinv}]
				update_inventory force
			else
				update_inventory
			fi
		done
		unset sqldelimer

		if [ ${retrinv_loop} -gt ${loop_per_retrinv} ]; then
			# reset loop
			retrinv_loop=0
		else
			retrinv_loop=$(( retrinv_loop + 1 ))
		fi
	fi

	cbsd_fwatch --file=${dbdir}/cbsdtaskd.sqlite --timeout=${loop_timeout} >/dev/null 2>&1
	_err=0
	queue=$( cbsdsqlro cbsdtaskd 'SELECT COUNT(id) FROM taskd WHERE status="0"' 2>/dev/null )

	if [ -z "${queue}" -o "${queue}" = "0" ]; then
		#echo "cbsdd: empty taskd queue, sleep for 15 sec.."
		sleep 15
		continue
	fi

	if [ ${queue} -gt ${max_simul_jobs} ]; then
		max=${max_simul_jobs}
	else
		max=${queue}
	fi

	if [ -z "${max}" ]; then
		#echo "cbsdd: wrong max variable"
		exit 1
	fi

	for i in $( ${SEQ_CMD} 1 ${max} ); do
		cbsdlogger NOTICE ${CBSD_APP}: runtask ${i}/${max} [$max_simul_jobs max]
		${ENV_CMD} workdir=${workdir} ${DAEMON_CMD} -f nexttask
		# wait for pid of daemon here!!!
		sleep 1
	done
done
