#!/usr/local/bin/cbsd
#v12.0.14
MYARG=""
MYOPTARG="hard_timeout jname inter noacpi"
MYDESC="Stop XEN domain"
CBSDMODULE="xen"
EXTHELP="wf_jstop_jstart"
ADDHELP="
${H3_COLOR}Description${N0_COLOR}:

Stop the XEN 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}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 xstop.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 xstop.conf.

${H3_COLOR}Examples${N0_COLOR}:

 # cbsd xstop
 # cbsd xstop jname=diehard noacpi=1

${H3_COLOR}See also${N0_COLOR}:

 cbsd xstart --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 xls -r ${sqlreplica}
fi

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

readconf xstop.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 '^xen_[a-zA-Z0-9_@%:][-a-zA-Z0-9_@%:]*\(\)$' ${Makefile} | ${XARGS_CMD} | ${TR_CMD} -d "()" | ${SED_CMD} s#xen_##g )
	jname="${all_bhyve_list}"

	if [ -n "${ojname}" ]; then
		jname="${ojname}"
	fi

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

	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 domain specified${N0_COLOR}"

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

. ${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="xen"		# 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=$( 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 xstop 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}/xstop.${jname}.$$ /usr/local/bin/cbsd xstop jname=${jname} noacpi=${noacpi} hard_timeout=${hard_timeout}
		#lets save .pid file
		sleep 1
		[ -f "${ftmpdir}/xstop.${jname}.$$" ] && cbsd_pwait --pid=$( ${CAT_CMD} ${ftmpdir}/xstop.${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}/xen.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=$( xwhereis ${jname} )
	[ -z "${remotenode}" ] && err 1 "${N1_COLOR}No such domain: ${N2_COLOR}${jname}${N0_COLOR}"
	for i in ${remotenode}; do
		${ECHO} "${N1_COLOR}Remote xstop: ${N2_COLOR}${jname} ${N1_COLOR}on${N2_COLOR} ${i}${N0_COLOR}"
		rexe node=${i} cbsd xstop 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}" != "xen" ] && err 1 "${N1_COLOR}Not in XEN mode${N0_COLOR}"

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

# 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 ))
		${XL_CMD} shutdown ${jname} > /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
			_state=$( ${XL_CMD} list ${jname} > /dev/null 2>&1 )
			if [ $? -eq 0 ]; then
				sleep 1
			else
				break
			fi
			tleft=$(( tleft - 1 ))
		done
		printf "]${N0_COLOR}\n"
	fi
else
	${XL_CMD} destroy -F ${jname}
fi

_state=$( ${XL_CMD} list ${jname} > /dev/null 2>&1 )
if [ $? -eq 0 ]; then
	# still live
	cbsdlogger NOTICE ${CBSD_APP}: XEN domain ${jname} does not want to die via ACPI, soft timeout ${hart_timeout} exceeded. Kill him
	${XL_CMD} destroy ${jname}
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

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

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

fwcounters jname=${jname} mode=remove

# 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_xen_queue_name}" ] && ${cbsd_queue_backend} cbsd_xen_queue_name=${cbsd_xen_queue_name} id=${jname} cmd=xstop status=2 data_status=0 workdir="${workdir}"
fi

# 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}: XEN domain ${jname} stopped in ${diff_time}
exit 0
