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

. ${subrdir}/nc.subr
cixinit
. ${system}
: "${reload:=0}"
. ${tools}

# reset hour for fwupdate
last_hour_fwupdate=0

shmux()
{
	local _mylock= SSH_ARGS= _locallockfile= _locallock= _remotelockfile= _remotelock=

	_locallockfile="${ftmpdir}/shmux_${_ip}.lock"
	_remotelockfile="ftmp/from_${nodename}.lock"

	case "${platform}" in
		Linux)
			_locallock="${FLOCK_CMD} -w0 -x ${_locallockfile}"
			_remotelock="${FLOCK_CMD} -w0 -x ${_remotelockfile}"
			;;
		*)
			_locallock="${LOCKF_CMD} -s -t0 ${_locallockfile}"
			_remotelock="${LOCKF_CMD} -s -t0 ${_remotelockfile}"
			;;
	esac


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

	SSH_ARGS="-tt -oControlPersist=30m -oBatchMode=yes -oStrictHostKeyChecking=no -oConnectTimeout=5 -q -oPort=${_port} -i ${_keyfile} -l ${cbsduser} ${_ip}"
	env workdir="${workdir}" ${miscdir}/daemon -u ${cbsduser} ${_locallock} ${SSH_CMD} ${SSH_ARGS} "${_remotelock} ${miscdir}/cbsd_dot --file=var/db/local.sqlite" > ${tmpdir}/inv.${_ip}.updated 2>${tmpdir}/inv.${_ip}.updated.err
}

update_jaillock()
{
	local jname=
	local _res=

	capture _res cbsdsqlro local "SELECT jname FROM jails WHERE status=1"

	for jname in ${_res}; do
		jaillock="${jailsysdir}/${jname}/locked"
		case "${platform}" in
			Linux)
				${miscdir}/daemonize -u ${cbsduser} ${FLOCK_CMD} -w0 -x ${ftmpdir}/update_jaillock.lock ${TOUCH_CMD} ${jaillock} 2>/dev/null
				;;
			*)
				${miscdir}/daemonize -u ${cbsduser} ${LOCKF_CMD} -s -t0 ${ftmpdir}/update_jaillock.lock ${TOUCH_CMD} ${jaillock} 2>/dev/null
				;;
		esac
	done

	unset _res
}

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

	[ "${platform}" = "Linux" ] && return 0

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

# if $1 = "force" skip for cbsd_dot check
update_inventory()
{
	local _ip="${1}"

	[ -z "${_ip}" ] && return 0
	if [ "${2}" != "force" ]; then
		[ ! -s "${tmpdir}/inv.${_ip}.updated" ] && return 0
		cbsdlogger NOTICE ${CIX_APP}: update remote inventory for ${_ip}: new changes
	fi
	${TRUNCATE_CMD} -s0 ${tmpdir}/inv.${_ip}.updated
	echo "task mode=new exclusive=1 autoflush=2 owner=cbsddsys jname=#retrinv_${nodename} /usr/local/bin/cbsd retrinv node=${_nodename}" >> /tmp/go.log
	task mode=new exclusive=1 autoflush=2 owner=cbsddsys jname=#retrinv_${nodename} /usr/local/bin/cbsd retrinv node=${_nodename} >/dev/null 2>&1
	${miscdir}/daemonize ${CIX_DISTDIR}/tools/nexttask
}

configure_default_vs_bridge()
{
	local _cbsd_vs=0 _ret=0

	[ -z "${configure_default_cbsd_vs_name}" ] && configure_default_cbsd_vs_name="cbsdbr0"

	case "${platform}" in
		Linux)
			${IP_CMD} link show ${configure_default_cbsd_vs_name} >/dev/null 2>&1
			_ret=$?
			;;
		*)
			${IFCONFIG_CMD} ${configure_default_cbsd_vs_name} >/dev/null 2>&1
			_ret=$?
			;;
	esac

	[ ${_ret} -eq 0 ] && return 0

	capture _cbsd_vs ${CIX_DISTDIR}/tools/get-next-nic name=bridge
	_ret=$?
	[ ${_ret} -ne 0 ] && err 1 "${CIX_APP} configure_default_vs, ret: ${_ret}: ${_cbsd_vs}"
	[ -z "${_cbsd_vs}" ] && err 1 "${CIX_APP} configure_default_vs, ret: ${_ret}: empty _cbsd_vs"

	eval $( ${miscdir}/sipcalc ${configure_default_cbsd_vs_cidr4} )
	if [ -z "${_usable_range_start}" ]; then
		cbsdlogger NOTICE ${CIX_APP}: configure_default_vs warning: unable to find _usable_range_start via sipcalc for: ${configure_default_cbsd_vs_cidr4}
		return 1
	fi

	case "${platform}" in
		Linux)
			${BRCTL_CMD} addbr ${configure_default_cbsd_vs_name}
			${IP_CMD} link set ${configure_default_cbsd_vs_name} up
			${IP_CMD} addr add ${_usable_range_start}/24 dev ${configure_default_cbsd_vs_name}
			${IP_CMD} link show ${configure_default_cbsd_vs_name} >/dev/null 2>&1
			_ret=$?
			;;
		*)
			${KLDSTAT_CMD} -qn if_bridge;
			if [ ${_ret} -ne 0 ]; then
				${ECHO} "${N1_COLOR}${CIX_APP}: loading if_bridge.ko${N2_COLOR}"
				${KLDLOAD_CMD} if_bridge
			fi
			${IFCONFIG_CMD} ${_cbsd_vs} create
			${IFCONFIG_CMD} ${_cbsd_vs} name ${configure_default_cbsd_vs_name}
			${IFCONFIG_CMD} ${configure_default_cbsd_vs_name} ${_usable_range_start}/24
			${IFCONFIG_CMD} ${configure_default_cbsd_vs_name} up
			${IFCONFIG_CMD} ${configure_default_cbsd_vs_name} >/dev/null 2>&1
			_ret=$?
			;;
	esac

	if [ ${_ret} -ne 0 ]; then
		echo "${CIX_APP} error: unable to create default bridge: ${configure_default_cbsd_vs_name}"
		return 1
	fi

	return 0
}

configure_default_vs()
{
	local _ret=

	if [ "${configure_default_cbsd_vs_cidr4}" = "auto" ]; then
		capture configure_default_cbsd_vs_cidr4 find_free_cidr4
		_ret=$?
		if [ ${_ret} -ne 0 ]; then
			cbsdlogger NOTICE ${CIX_APP}: configure_default_vs warning: unable to find available free cidr4, please set 'configure_default_cbsd_vs_cidr4' manually via global.conf
			echo "warning: ${CIX_APP}: configure_default_vs warning: unable to find available free cidr4, please set 'configure_default_cbsd_vs_cidr4' manually via global.conf"
			return ${_ret}
		fi

		cbsdlogger NOTICE ${CIX_APP}: configure_default_vs: configure_default_cbsd_vs_cidr4 auto: use ${configure_default_cbsd_vs_cidr4}, ${workdir}/etc/global.conf updated
		echo "${CIX_APP}: configure_default_vs: configure_default_cbsd_vs_cidr4 auto: use ${configure_default_cbsd_vs_cidr4} ( ${workdir}/etc/global.conf updated )"
		${CIX_DISTDIR}/misc/cbsdsysrc -qf ${workdir}/etc/global.conf configure_default_cbsd_vs_cidr4="${configure_default_cbsd_vs_cidr4}" > /dev/null 2>&1
		export configure_default_cbsd_vs_cidr4="${configure_default_cbsd_vs_cidr4}"
	fi

	case "${configure_default_cbsd_vs_engine}" in
		bridge)
			configure_default_vs_${configure_default_cbsd_vs_engine}
			;;
		*)
			cbsdlogger NOTICE ${CIX_APP}: configure_default_vs warning: unsupported configure_default_cbsd_vs_engine engine: ${configure_default_cbsd_vs_engine}
			echo "warning: ${CIX_APP}: configure_default_vs: unsupported configure_default_cbsd_vs_engine engine: ${configure_default_cbsd_vs_engine}"
			;;
	esac
}

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

readconf global.conf

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

# default virtual switch
if [ "${default_vs}" != "0" ]; then
	configure_default_vs
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} -mindepth 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} -mindepth 1 -maxdepth 1 -name inv.\*.updated -delete > /dev/null 2>&1
${FIND_CMD} ${tmpdir} -mindepth 1 -maxdepth 1 -name \*.desc -delete > /dev/null 2>&1

# retrinv
if [ ${sqlreplica} -eq 1 ]; then
	case "${platform}" in
		Linux)
			${miscdir}/daemonize ${FLOCK_CMD} -w0 -x ${ftmpdir}/retrinv.lock /usr/local/bin/cbsd retrinv tryoffline=1
			;;
		*)
			${miscdir}/daemonize ${LOCKF_CMD} -s -t0 ${ftmpdir}/retrinv.lock /usr/local/bin/cbsd retrinv tryoffline=1
			;;
	esac
fi

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

tik=0

. ${nodes}

while [ 1 ]; do
	tik=$(( tik + 1 ))

	if [ ${sqlreplica} -eq 1 -a ${tik} -eq 1 ]; then
		nip=
		cbsdsqlro_vars nodes "SELECT ip FROM nodelist" nip
		tik=301	# init shmux
	fi

	if [ ${sqlreplica} -eq 1 -a -n "${nip}" ]; then
		for i in ${nip}; do
			if [ -r ${tmpdir}/inv.${i}.updated ]; then
				capture _bytes get_file_bytes ${tmpdir}/inv.${i}.updated
				[ "${_bytes}" != "0" ] && update_inventory ${i} force
			fi
		done
	fi


	# ~5min
	if [ ${tik} -gt 300 ]; then
		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
			cbsdsqlquery nodes "SELECT nodename,ip,port,keyfile FROM nodelist" | while read q; do
				_ip=${q@.ip}
				_port=${q@.port}
				_keyfile=${q@.keyfile}
				[ -n "${_ip}" ] && shmux
#				if [ ${retrinv_loop} -gt ${loop_per_retrinv} ]; then
					cbsdlogger NOTICE ${CIX_APP}: update remote inventory for ${_ip} via loop_per_retrinv [${loop_per_retrinv}]
					update_inventory ${i} force
#				else
#					update_inventory ${i}
#				fi
			done
		fi
		tik=0
	fi

	#queue=$( cbsdsqlro cbsdtaskd 'SELECT COUNT(id) FROM taskd WHERE status="0"' 2>/dev/null )
	cbsdsqlro_vars cbsdtaskd "SELECT COUNT(id) FROM taskd WHERE status='0'" queue

	if [ -z "${queue}" -o "${queue}" = "0" ]; then
		cbsd_fwatch --file=${dbdir}/cbsdtaskd.sqlite --timeout=${loop_timeout} >/dev/null 2>&1
		tik=$(( tik + loop_timeout ))
	fi

	unset queue
	#queue=$( cbsdsqlro cbsdtaskd 'SELECT COUNT(id) FROM taskd WHERE status="0"' 2>/dev/null )
	cbsdsqlro_vars cbsdtaskd "SELECT COUNT(id) FROM taskd WHERE status='0'" queue

	if [ -z "${queue}" -o "${queue}" = "0" ]; then
		unset queue
		sleep 1
		continue
	fi

	#queue_active=$( cbsdsqlro cbsdtaskd 'SELECT COUNT(id) FROM taskd WHERE status="1"' 2>/dev/null )
	cbsdsqlro_vars cbsdtaskd "SELECT COUNT(id) FROM taskd WHERE status='1'" queue_active

	if [ ${queue_active} -gt ${max_simul_jobs} ]; then
		cbsdlogger NOTICE ${CIX_APP}: active: ${queue_active}/${max_simul_jobs}
		unset queue_active
		# too many in progress
		sleep 1
		continue
	fi

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

	unset queue

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

	for ((i=1; i<=${max}; i++)); do
		cbsdlogger NOTICE ${CIX_APP}: runtask ${i}/${max} [$max_simul_jobs max]
		${ENV_CMD} workdir=${workdir} ${miscdir}/daemonize -E workdir=${workdir} ${CIX_DISTDIR}/tools/nexttask
		# wait for pid of daemon here!!!
		sleep 1
	done
done

exit 0
