#!/usr/local/bin/cbsd
#v11.1.19
MYARG=""
MYOPTARG="jname inter"
MYDESC="Stop jail"
CBSDMODULE="jail"
EXTHELP="wf_jstop_jstart.html"

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

[ -z "${1}" ] && select_jail_by_list -s "List of online jail" -a "On" -r ${sqlreplica}
init $*

[ -n "${inter}" ] && shift

# for external_exec-related command
. ${workdir}/jcreate.subr

emulator="jail"		# for jname_is_multiple
jname_is_multiple

# MAIN

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
		/usr/sbin/daemon -p ${ftmpdir}/jstop.${jname}.$$ /usr/local/bin/cbsd jstop jname=${jname}
		#lets save .pid file
		sleep 1
		[ -f "${ftmpdir}/jstop.${jname}.$$" ] && cbsd_pwait --pid=$( /bin/cat ${ftmpdir}/jstop.${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 jail name${NORMAL}"

. ${jrcconf}
if [ $? -eq 1 ]; then
	[ ${sqlreplica} -eq 0 ] && err 1 "${MAGENTA}No such jail: ${GREEN}${jname}${NORMAL}"
	remotenode=$( jwhereis ${jname} )
	[ -z "${remotenode}" ] && err 1 "${MAGENTA}No such jail: ${GREEN}${jname}${NORMAL}"
	for i in ${remotenode}; do
		${ECHO} "${MAGENTA}Remote jstop: ${GREEN}${jname} ${MAGENTA}on${GREEN} ${i}${NORMAL}"
		rexe node=${i} cbsd jstop 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

[ ${jid} -eq 0 ] && err 1 "${MAGENTA}Not Running: ${GREEN}${jname}${NORMAL}"
[ ${status} -eq 3 ] && err 1 "${MAGENTA}Jail in maintenance mode${NORMAL}"
[ "${emulator}" = "bhyve" ] && err 1 "${MAGENTA}For bhyve jail use: ${GREEN}cbsd bstop ${jname} ${MAGENTA}instead${NORMAL}"
[ -z "${cbsd_queue_name}" ] && cbsd_queue_name="/clonos/jailscontainers/"

. ${workdir}/universe.subr
init_basedir

# 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=jstop status=1
fi

# VNC auto stop
if [ -x "${moduledir}/vncterm.d/cbsdvnc" ]; then
	${ECHO} "${MAGENTA}Stopping VNC daemon...${GREEN}"
	vncterm jname=${jname} mode=stop ||true
fi

if [ "${vnet}" = "1" -a "${vimage_feature}" = "0" ]; then
	${ECHO} "${MAGENTA}Jail ${GREEN}${jname}${MAGENTA} have vnet=1 flags but your kernel is not support VIMAGE${NORMAL}"
	${ECHO} "${MAGENTA}Please recompile kernel with: ${GREEN}options VIMAGE${NORMAL}"
	vnet=0
fi

# args for makejconf
epairb_list=

if [ ${vnet} -eq 1 ]; then
	. ${vimageconf}

	interfaces=$( cbsdsql ${jailsysdir}/${jname}/local.sqlite SELECT name FROM jailnic | while read _int; do
		printf "${_int} "
	done ) || err 1 "${MAGENTA}jstart: error get interfaces name for vnet nic map${NORMAL}"

	eth_seq=0
	myepair_list=

	for i in ${interfaces}; do

		nic_parent=
		nic_parent=$( cbsdsql ${jailsysdir}/${jname}/local.sqlite SELECT nic_parent FROM jailnic WHERE name=\"${i}\" )

		[ "${nic_parent}" = "0" -o "${nic_parent}" = "auto" ] && nic_parent=$( getnics-by-ip ip=0.0.0.0 )
		myepair=
		myepair=$( get_my_device epair ${jname}-eth${eth_seq} )
		if [ -n "${myepair}" ]; then
			myepair_list="${myepair_list} ${myepair}"
			# returned epairXa, but we need epairXb, so append 'end' to the end of
			# string and replace 'aend' to 'b'
			if [ -z "${epairb_list}" ]; then
				epairb_list="${myepair}end"
			else
				epairb_list="${epairb_list},${myepair}end"
			fi
			# not the best solution, but better than nothing
			epairb_list=$( echo ${epairb_list} | /usr/bin/sed 's:aend:b:g' )
		fi
		eth_seq=$(( eth_seq + 1 ))
	done
	[ -z "${myepair_list}" ] && ${ECHO} "${MAGENTA}Warning: Cant find epair_list for vnet-type jail:${GREEN}${jname}${NORMAL}"
fi

#determine that jail is FreeBSD. Useful for vnet operation in makejconf and
is_freebsd=0

if [ ${baserw} -eq 1 ]; then
	elftest=${data}/bin/sh
	path=${data}
else
	elftest="${BASE_DIR}/bin/sh"
fi

[ -f "${elftest}" ] && osname=$( ${miscdir}/elf_tables --osname ${elftest} )
[ "${osname}" = "freebsd" ] && is_freebsd=1

makejconf jname=${jname} out=${ftmpdir}/${jname}.conf epair=${epairb_list} fbsd=${is_freebsd} is_stop=1

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

# for external hook variables
geniplist ${ip4_addr}

# there is some problem with stopping cron:
# Stopping cron.
# Waiting for PIDS: 71597
# 90 second watchdog timeout expired. Shutdown terminated.
# jail: jail17: /bin/sh /etc/rc.shutdown: exited on signal 9
# always reproduced when the jail have persist flags:
# e.g for CBSD whith zfs attached fs or VNET-based.
# try to defend and kill the cron preventively

#[ -r ${path}/var/run/cron.pid ] && /bin/pkill -9 -F ${path}/var/run/cron.pid
# pid not always correct, so just kill it via jexec:
# roughly, but what to do with FreeBSD ;(
if [ ${is_freebsd} -eq 1 ]; then
	/usr/sbin/jexec ${jid} pkill -9 cron
fi

export_jail_data_for_external_hook
external_exec_master_script "master_prestop.d"
exec_master_prestop
exec_prestop

#rctl area
${ECHO} "${MAGENTA}Stoping jail: ${GREEN}${jname}, parallel timeout=${parallel}${NORMAL}"
jrctl jname=${jname} mode=unset >/dev/null 2>&1
sleep 2

external_exec_script "stop.d"
/usr/sbin/jail -f ${ftmpdir}/${jname}.conf -r ${jname} || true

for i in $( /bin/ps -J ${jid} -o pid |/usr/bin/grep -v PID ); do
	/bin/kill -9 ${i} > /dev/null 2>&1
done

exec_master_poststop
external_exec_master_script "master_poststop.d"
exec_poststop

if [ ${vnet} -eq 1 -a -n "${myepair_list}" ]; then
	printf "${MAGENTA}Destroy epair: ${LYELLOW}"

	# how necessary to make 'ifconfig -vnet ethX' before
	#no mem leak here and all correct?
	for i in ${myepair_list}; do
		printf "${i} "
		/sbin/ifconfig ${i} destroy
	done
	${ECHO} "${NORMAL}"
fi

# waiting for fixed kqueue in upstream
/usr/sbin/jail -r ${ST} > /dev/null 2>&1
/usr/sbin/jail -r ${ST} > /dev/null 2>&1
cbsdsql local UPDATE jails SET jid=0,state_time="(strftime('%s','now'))" WHERE jname=\"${jname}\"

jaillock="${jailsysdir}/${jname}/locked"

[ -f "${jaillock}" ] && /bin/rm -f ${jaillock}

if [ "${ver}" != "empty" ]; then
	${ECHO} "${MAGENTA}Updating: ${GREEN}pkg info${NORMAL}"
	pkg mode=info nomount=1 jname=${jname} > ${jailsysdir}/${jname}/pkg_info
fi

#determine that jail is FreeBSD. Useful for vnet operation in makejconf and
is_freebsd=0

if [ ${baserw} -eq 1 ]; then
	elftest=${data}/bin/sh
else
	elftest="${BASE_DIR}/bin/sh"
fi

[ -f "${elftest}" ] && osname=$( ${miscdir}/elf_tables --osname ${elftest} )
[ "${osname}" = "freebsd" ] && is_freebsd=1

[ -n "${mdsize}" -a "${mdsize}" != "0" ] && MDFILE=$( eval find_md_by_mountpath ${data} )

jcleanup jname=${jname} > /dev/null

for pureip in ${IPS}; do
	iptype ${pureip}
	[ -n "${VHID}" ] && continue
	_inet=$?
	if [ -n "${interface}" -a "${interface}" != "0" ]; then
		iface=$( getnics-by-ip ip=${pureip} )
		ipwmask ${pureip}
		if [ -n "${IWM}" ]; then
			case ${_inet} in
				1)
					MODIF="inet"
					;;
				2)
					MODIF="inet6" ;;
			esac
			/sbin/ifconfig ${iface} ${MODIF} ${pureip} -alias > /dev/null 2>&1 ||true
		fi
	fi
done

# make id file
UNDhost_hostname=$( ${ECHO} ${host_hostname} |/usr/bin/tr  "." "_" )
FID="/var/run/jail_${UNDhost_hostname}.id"
[ ! -f "${FID}" ] || /bin/rm -f ${FID}

[ -n "${mdsize}" -a "${mdsize}" != "0" -a -n "${MDFILE}" ] && unmountmd md=${MDFILE}
/bin/rm -f ${ftmpdir}/${jname}.conf

# 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=jstop status=2 data_status=0
fi

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

exit 0
