#!/usr/local/bin/cbsd
#v11.1.19
MYARG=""
MYOPTARG="checkpoint hard_timeout jname inter noacpi"
MYDESC="Stop bhyve domain"
CBSDMODULE="bhyve"
EXTHELP="wf_jstop_jstart"
ADDHELP="
${H3_COLOR}Description${N0_COLOR}:

Stop the bhyve domain. By default, the script sends the ACPI signal (through the
kill command) to bhyve process and expects completion during the timeout.
If during this time the virtual machine did not respond to the signal, the script
will terminate the bhyve process of forcibly.

${H3_COLOR}Options${N0_COLOR}:

 ${N2_COLOR}checkpoint=${N0_COLOR}   - Set to '1' for 'bcheckpoint suspend=1' instead of bstop;
 ${N2_COLOR}jname=${N0_COLOR}        - Target VM. If jail='*' or jail='vm*' then stop  all
                 bhyve or VM whose names begin with 'vm', e.g. 'vm1', 'vm2'...;
 ${N2_COLOR}hard_timeout=${N0_COLOR} - Wait N seconds (30 by default) before hard reset.
                 The parameter can be overridden globally through the bstop.conf;
 ${N2_COLOR}noacpi=${N0_COLOR}       - 0,1. Set to 1 to prevent ACPI signal sending, just kill,
                 immediate and merciless. The parameter can be overridden globally
                 through the bstop.conf;

${H3_COLOR}Examples${N0_COLOR}:

 # cbsd bstop
 # cbsd bstop jname=diehard noacpi=1

${H3_COLOR}See also${N0_COLOR}:

 cbsd bstart --help

"

. ${subrdir}/nc.subr
. ${tools}		# for select_jail_by_list

jname=

# check for cloud function when CBSDfile exist
Makefile="${CBSD_PWD}/CBSDfile"
if [ ! -r "${Makefile}" ]; then
	[ -z "${1}" ] && select_jail_by_list -s "List of online VMs" -a "On" -e bls -r ${sqlreplica}
fi

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

readconf bstop.conf

. ${cbsdinit}
ojname="${jname}"

if [ -r "${Makefile}" ]; then
	[ -z "${CBSDFILE_RECURSIVE}" ] && ${ECHO} "${N1_COLOR}found CBSDfile: ${N2_COLOR}${Makefile}${N0_COLOR}" 1>&2
	. ${Makefile}
	all_bhyve_list=$( ${GREP_CMD} -E '^bhyve_[a-zA-Z0-9_@%:][-a-zA-Z0-9_@%:]*\(\)$' ${Makefile} | ${XARGS_CMD} | ${TR_CMD} -d "()" | ${SED_CMD} s#bhyve_##g )
	jname="${all_bhyve_list}"

	jname_in_args=0

	if [ -n "${ojname}" ]; then
		jname="${ojname}"
	else
		# cbsd bstart <env1> <env2> route
		for i in $*; do
			strpos --str="${i}" --search="="
			if [ $? -eq 0 ]; then
				jname_in_args=1
				# not param=value - jail?
				jname_in_cbsdfile=0
				for j in ${all_bhyve_list}; do
					[ "${i}" != "${j}" ] && continue
					jname_in_cbsdfile=1
				done
				if [ ${jname_in_cbsdfile} -eq 1 ]; then
					if [ -z "${ojname}" ]; then
						ojname="${i}"
					else
						ojname="${ojname} ${i}"
					fi
				else
					${ECHO} "${N1_COLOR}${CBSD_APP}: env absent in CBSDfile: ${N2_COLOR}${i}${N0_COLOR}" 1>&2
				fi
			fi
		done
		if [ ${jname_in_args} -eq 1 ]; then
			jname="${ojname}"
		else
			jname="${all_bhyve_list}"
		fi
	fi

	unset ojname

	[ -z "${jname}" ] && err 1 "${N1_COLOR}${CBSD_APP}: give me jname${N0_COLOR}"

	# multiple?
	strpos --str="${jname}" --search=" "
	if [ $? -ne 0 ]; then
		jail_list="${jname}"
	fi

	if [ -n "${CLOUD_URL}" -a -n "${CLOUD_KEY}" ]; then
		cbsd_api=1
	else
		cbsd_api=0
	fi
else
	cbsd_api=0
fi

[ -z "${jname}" -a -z "$*" ] && err 1 "${N1_COLOR}No bhyve specified${N0_COLOR}"

TMUX_CMD=$( which tmux )
[ -z "${TMUX_CMD}" ] && err 1 "${N1_COLOR}no such tmux${N0_COLOR}"

. ${system}
. ${mdtools}
. ${subrdir}/bhyve.subr

[ -n "${inter}" ] && shift
. ${subrdir}/jcreate.subr	# external_exec_master_script

# MAIN
if [ "${mod_cbsd_queue_enabled}" = "YES" -a -z "${MOD_CBSD_QUEUE_DISABLED}" ]; then
	readconf cbsd_queue.conf
	[ -z "${cbsd_queue_backend}" ] && MOD_CBSD_QUEUE_DISABLED="1"
fi

emulator="bhyve" # for jname_is_multiple

# jail_list can be init in CBSDfile case
[ -z "${jail_list}" ] && 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=$( cbsdsqlro 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

. ${subrdir}/time.subr
st_time=$( ${DATE_CMD} +%s )

# this is multiple list, split it by parallel bstop execution
if [ ${jail_num} -gt 1 ]; then
	cbsdlogger NOTICE ${CBSD_APP}: executing for multiple stopping: ${JLIST}
	for jname in ${JLIST}; do
		/usr/local/cbsd/misc/daemonize -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=$( ${CAT_CMD} ${ftmpdir}/bstop.${jname}.$$ ) --timeout=${parallel}
	done

	wait_for_fpid -a stop

	end_time=$( ${DATE_CMD} +%s )
	diff_time=$(( end_time - st_time ))
	diff_time=$( displaytime ${diff_time} )
	cbsdlogger NOTICE ${CBSD_APP}: executing for multiple done in ${diff_time}: ${JLIST}
	err 0 "${N1_COLOR}Multiple stop: ${N2_COLOR}done in ${diff_time}${N0_COLOR}"
fi

[ -z "${jname}" ] && jname=$( echo ${JLIST} | ${AWK_CMD} '{printf $1}' )
[ -z "${jname}" ] && jname="${1}"
[ -z "${jname}" ] && err 1 "${N1_COLOR}Give me domain name${N0_COLOR}"

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

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

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

# CBSD QUEUE
if [ "${mod_cbsd_queue_enabled}" = "YES" -a -z "${MOD_CBSD_QUEUE_DISABLED}" ]; then
	[ -n "${cbsd_bhyve_queue_name}" ] && ${cbsd_queue_backend} cbsd_queue_name=${cbsd_bhyve_queue_name} id=${jname} cmd=bstop status=1 data_status=1 workdir="${workdir}"
fi

check_for_bhyve_process -j ${jname}

# 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 "${vm_pid}" ]; then
		# soft stop, send SIGTERM
		tleft=$(( hard_timeout - 1 ))
		kill -15 ${vm_pid} > /dev/null 2>&1
		for i in $( ${SEQ_CMD} 1 ${hard_timeout} ); do
			printf "${CLRLINE}"
			printf "${CURSORRST}"
			printf "${N1_COLOR}Send SIGTERM to ${N2_COLOR}${jname}${N1_COLOR}. Soft timeout is ${N2_COLOR}${hard_timeout}${N1_COLOR} sec. ${W1_COLOR}${tleft}${N1_COLOR} seconds left [${N0_COLOR}"
			for x in $( ${SEQ_CMD} 1 ${i} ); do
				printf "${N1_COLOR}."
			done
			cbsd_pwait --pid=${vm_pid} --timeout=1 > /dev/null 2>&1
			tleft=$(( tleft - 1 ))
			# send SIGTEM again when double_acpi == 1
			[ "${double_acpi}" = "1" ] && kill -15 ${vm_pid} > /dev/null 2>&1
		done
		printf "]${N0_COLOR}\n"
		${PS_CMD} -p ${vm_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 ${vm_pid} > /dev/null 2>&1 || true
		fi
	else
		${ECHO} "${N1_COLOR}Warning: unable to determine bhyve pid for: ${N2_COLOR}${jname}${N0_COLOR}"
	fi
else
	kill -9 ${vm_pid} > /dev/null 2>&1 || true
fi

for i in vnc_port vnc_bind ssh_port ssh_host rdp_port rdp_host; do
	[ -r ${jailsysdir}/${jname}/${i} ] && ${RM_CMD} -f ${jailsysdir}/${jname}/${i}
done

_res=$( ${BHYVECTL_CMD} --destroy --vm="${jname}" 2>&1 )

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

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

# cleanup for ifaces
. ${subrdir}/vnet.subr

fwcounters jname=${jname} mode=remove
expose mode=clear

# delete orphaned vale_ports if exists
#cbsdsqlrw local "DELETE FROM vale_ports WHERE jname=\"${jname}\""
bcleanup jname=${jname}

# update state_time
cbsdsqlrw ${jailsysdir}/${jname}/local.sqlite UPDATE settings SET state_time="(strftime('%s','now'))"

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

external_exec_master_script "master_poststop.d"

# CBSD QUEUE
if [ "${mod_cbsd_queue_enabled}" = "YES" -a -z "${MOD_CBSD_QUEUE_DISABLED}" ]; then
	[ -n "${cbsd_bhyve_queue_name}" ] && ${cbsd_queue_backend} cbsd_queue_name=${cbsd_bhyve_queue_name} id=${jname} cmd=bstop status=2 data_status=0 workdir="${workdir}"
fi

# jailed?
_jailed=$( cbsdsqlro ${jailsysdir}/${jname}/local.sqlite "SELECT jailed FROM settings LIMIT 1" )
[ -z "${_jailed}" ] && _jailed="0"

if [ "${_jailed}" != "0" ]; then
	${ECHO} "${N1_COLOR}Stopping jailhost: ${N2_COLOR}${_jailed}${N0_COLOR}"
	jstop jname="${_jailed}"
	echo
fi

# chrooted?
# disable for a while
_chrooted=0
#_chrooted=$( cbsdsqlro ${jailsysdir}/${jname}/local.sqlite "SELECT chrooted FROM settings LIMIT 1" )
#[ -z "${_chrooted}" ] && _chrooted="0"

#if [ "${_chrooted}" != "0" ]; then
#	${ECHO} "${N1_COLOR}CHROOTED bstop: ${N2_COLOR}${_chrooted}${N0_COLOR}"
#fi

${TMUX_CMD} -Lcbsd-"${jname}" kill-server 2>/dev/null || true

# API compat: ~cbsd/jails-system/info*.*
/usr/local/cbsd/misc/daemonize /usr/local/cbsd/tools/save-jail-info jname=${jname}

end_time=$( ${DATE_CMD} +%s )
diff_time=$(( end_time - st_time ))
diff_time=$( displaytime ${diff_time} )
${ECHO} "${N1_COLOR}${CBSD_APP} done ${N2_COLOR}in ${diff_time}${N0_COLOR}"
cbsdlogger NOTICE ${CBSD_APP}: bhyve domain ${jname} stopped in ${diff_time}

exit 0
