#!/usr/local/bin/cbsd
#v11.1.19
globalconf="${workdir}/cbsd.conf";
MYARG=""
MYOPTARG="jname inter cbsd_queue_name noacpi hard_timeout"
MYDESC="Stop jail"
CBSDMODULE="bhyve"
EXTHELP="wf_jstop_jstart.html"
ADDHELP="cbsd_queue_name - realtime queue, can be: none\n\
noacpi= 1 - force hard poweroff immediately or 0 - send ACPI signal first\n\
    can be overwriten by bstop.conf\n\
hard_timeout=N, how many seconds wait after ACPI signal before hard poweroff,\n\
    default is: 30. Can be overwriten by bstop.conf\n"

. ${subr}
. ${system}
. ${mdtools}
. ${strings}
. ${tools}

[ -z "${1}" ] && select_jail_by_list -s "List of online VMs" -a "On" -e bls -r ${sqlreplica}

noacpi=0		# soft shutdown by default
hard_timeout=30		# default soft timeout: 30 seconds

readconf bstop.conf

init $*

[ -n "${inter}" ] && shift
[ -n "${cbsd_queue_name}" ] && shift									#TODO << substr

. ${workdir}/jcreate.subr	# external_exec_master_script

# MAIN
[ -z "${cbsd_queue_name}" ] && cbsd_queue_name="/clonos/bhyvevms/"

emulator="bhyve" # for jname_is_multiple
jname_is_multiple

if [ -n "${jail_list}" ]; then
	TMP_JLIST="${jail_list}"
else
	TMP_JLIST=$*
fi

JLIST=

# check for actual vm list in arg list
jail_num=0
for i in ${TMP_JLIST}; do
	exist=$( cbsdsql local SELECT jname FROM jails WHERE jname=\"${i}\" AND emulator=\"${emulator}\" LIMIT 1 )
	if [ -n "${exist}" ]; then
		JLIST="${exist} ${JLIST}"
		jail_num=$(( jail_num + 1 ))
	fi
done

# this is multiple list, split it by parallel bstop execution
if [ ${jail_num} -gt 1 ]; then
	st_time=$( /bin/date +%s )
	cbsdlogger NOTICE ${CBSD_APP}: executing for multiple stopping: ${JLIST}
	for jname in ${JLIST}; do
		[ "${jname}" = "cbsd_queue_name=none" ] && continue						#TODO << substr
		/usr/sbin/daemon -p ${ftmpdir}/bstop.${jname}.$$ /usr/local/bin/cbsd bstop jname=${jname} noacpi=${noacpi} hard_timeout=${hard_timeout}
		#lets save .pid file
		sleep 1
		[ -f "${ftmpdir}/bstop.${jname}.$$" ] && cbsd_pwait --pid=$( /bin/cat ${ftmpdir}/bstop.${jname}.$$ ) --timeout=${parallel}
	done

	wait_for_fpid -a stop

	end_time=$( /bin/date +%s )
	cbsdlogger NOTICE ${CBSD_APP}: executing for multiple done in $(( end_time - st_time ))s: ${JLIST}
	err 0 "${MAGENTA}Multiple stop: ${GREEN}done${NORMAL}"
fi

st_time=$( /bin/date +%s )

[ -z "${jname}" ] && jname=$( echo ${JLIST} | /usr/bin/awk '{printf $1}' )
[ -z "${jname}" ] && jname="$1"
[ -z "${jname}" ] && err 1 "${MAGENTA}Give me domain name${NORMAL}"

. ${sharedir}/bhyve.conf		# only for for MYCOL variables: used in exports below

. ${jrcconf}
if [ $? -eq 1 ]; then
	[ ${sqlreplica} -eq 0 ] && err 1 "${MAGENTA}No such domain: ${GREEN}${jname}${NORMAL}"
	remotenode=$( bwhereis ${jname} )
	[ -z "${remotenode}" ] && err 1 "${MAGENTA}No such domain: ${GREEN}${jname}${NORMAL}"
	for i in ${remotenode}; do
		${ECHO} "${MAGENTA}Remote bstop: ${GREEN}${jname} ${MAGENTA}on${GREEN} ${i}${NORMAL}"
		rexe node=${i} cbsd bstop jname=${jname}
		if [ $? -eq 0 ]; then
			# update inventory
			${ECHO} "${MAGENTA}Updating inventory...${NORMAL}"
			task mode=new cbsd retrinv node=${i} tryoffline=1 data=db > /dev/null 2>&1
		fi
	done
	exit 0
fi

[ "${emulator}" != "bhyve" ] && err 1 "${MAGENTA}Not in bhyve mode${NORMAL}"

# CBSD QUEUE
if [ -x "${moduledir}/cbsd_queue.d/cbsd_queue" ]; then
	[ "${cbsd_queue_name}" != "none" ] && [ "${cbsd_queue_name}" != "none" ] && cbsd_queue cbsd_queue_name=${cbsd_queue_name} id=${jname} cmd=bstop status=1 data_status=1
fi

pid=

pid=$( /bin/ps axopid,ucomm,command -ww | while read pid comm cmd; do
	case "${comm}" in
		bhyve|grub-bhyve)
			_res=$( echo ${cmd} | /usr/bin/grep " ${jname}" 2>/dev/null)
			[ -n "${_res}" ] && echo "${pid}" && exit 0
			;;
	esac
done 2>/dev/null)

# for external hook variables
geniplist ${ip4_addr}

export_bhyve_data_for_external_hook
external_exec_master_script "master_prestop.d"

TRAP=""
TRAP="${TRAP} jswmode jname=${jname} mode=master comment='0';"
trap "${TRAP}" HUP INT ABRT BUS TERM EXIT

jswmode jname=${jname} mode=maintenance comment='Stopping_VM'

if [ ${noacpi} -eq 0 ]; then
	if [ -n "${pid}" ]; then
		# soft stop, send SIGTERM
		tleft=$(( hard_timeout - 1 ))
		kill -15 ${pid} > /dev/null 2>&1
		for i in $( /usr/bin/seq 1 ${hard_timeout} ); do
			printf "${CLRLINE}"
			printf "${CURSORRST}"
			printf "${MAGENTA}Send SIGTERM to ${GREEN}${jname}${MAGENTA}. Soft timeout is ${GREEN}${hard_timeout}${MAGENTA} sec. ${LRED}${tleft}${MAGENTA} seconds left [${NORMAL}"
			for x in $( /usr/bin/seq 1 ${i} ); do
				printf "${MAGENTA}."
			done
			cbsd_pwait --pid=${pid} --timeout=1 > /dev/null 2>&1
			tleft=$(( tleft - 1 ))
		done
		printf "]${NORMAL}\n"
		/bin/ps -p ${pid} > /dev/null 2>&1
		if [ $? -eq 0 ]; then
			# still live
			cbsdlogger NOTICE ${CBSD_APP}: bhyve domain ${jname} does not want to die via ACPI, soft timeout ${hart_timeout} exceeded. Kill him
			kill -9 ${pid} > /dev/null 2>&1 || true
		fi
	else
		${ECHO} "${MAGENTA}Warning: unable to determine bhyve pid for: ${GREEN}${jname}${NORMAL}"
	fi
else
	kill -9 ${pid} > /dev/null 2>&1 || true
fi

[ -r ${jailsysdir}/${jname}/vnc_port ] && /bin/rm -f ${jailsysdir}/${jname}/vnc_port

_res=$( /usr/sbin/bhyvectl --destroy --vm="${jname}" 2>&1 )

# extra check for no any cbsd process related to this VM is active
epid=$( /bin/ps axopid,ucomm,command -ww | /usr/bin/grep "/tmp/bhyveload.${jname}.lock" | /usr/bin/grep -v grep | /usr/bin/awk '{printf $1" "}' )
[ -n "${epid}" ] && kill -9 ${epid} > /dev/null 2>&1
epid=$( /bin/ps axopid,ucomm,command -ww | /usr/bin/grep "${sharedir}/bhyverun.sh -c ${workdir}/jails-system/${jname}/bhyve.conf" | /usr/bin/grep -v grep | /usr/bin/awk '{printf $1" "}' )
[ -n "${epid}" ] && kill -9 ${epid} > /dev/null 2>&1

jswmode jname=${jname} mode=master comment='0'

# cleanup for ifaces
. ${vimageconf}

nic_num=$( cbsdsql ${jailsysdir}/${jname}/local.sqlite SELECT count\(id\) FROM bhyvenic WHERE jname=\"${jname}\" )

[ -z "${nic_num}" ] && exit 0

while [ ${nic_num} -ne 0 ]; do
	nic_num=$(( nic_num -1 ))
	mytap=$( get_my_device tap ${jname}-nic${nic_num} )
	if [ -n "${mytap}" ]; then
		/sbin/ifconfig ${mytap} >/dev/null 2>&1 && /sbin/ifconfig ${mytap} destroy
	fi
done

# clean ARP cache
/usr/sbin/arp -nda > /dev/null 2>&1

# update state_time and pid
cbsdsql local UPDATE jails SET jid=0,state_time="(strftime('%s','now'))" WHERE jname=\"${jname}\"

external_exec_master_script "master_poststop.d"

# CBSD QUEUE
if [ -x "${moduledir}/cbsd_queue.d/cbsd_queue" ]; then
	[ "${cbsd_queue_name}" != "none" ] && cbsd_queue cbsd_queue_name=${cbsd_queue_name} id=${jname} cmd=bstop status=2 data_status=0
fi

end_time=$( /bin/date +%s )
cbsdlogger NOTICE ${CBSD_APP}: bhyve domain ${jname} stopped in $(( end_time - st_time ))s
exit 0
