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

Stop the qemu domain. By default, the script sends the ACPI signal (through the
system_powerdown QEMU monitor command) to qemu 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 qemu process of forcibly.

${H3_COLOR}Options${N0_COLOR}:

 ${N2_COLOR}jname=${N0_COLOR}        - Target VM. If jail='*' or jail='vm*' then stop  all
                 qemu 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 qstop.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 qstop.conf.

${H3_COLOR}Examples${N0_COLOR}:

 # cbsd qstop
 # cbsd qstop jname=diehard noacpi=1

${H3_COLOR}See also${N0_COLOR}:

 cbsd qstart --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 qls -r ${sqlreplica}
fi

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

readconf qstop.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_qemu_list=$( ${GREP_CMD} -E '^qemu_[a-zA-Z0-9_@%:][-a-zA-Z0-9_@%:]*\(\)$' ${Makefile} | ${XARGS_CMD} | ${TR_CMD} -d "()" | ${SED_CMD} s#qemu_##g )
	jname="${all_qemu_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 qemu specified${N0_COLOR}"

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

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

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

check_for_qemu_process -j ${jname}

# TODO: errcode = 0 - exit

# for external hook variables
geniplist ${ip4_addr}

export_qemu_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 ))
		printf "system_powerdown\r\n" | /usr/local/cbsd/misc/daemonize ${TIMEOUT_CMD} ${hard_timeout} ${NC_CMD} -U ${jailsysdir}/${jname}/qemu-monitor.sock 1>/dev/null
		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}: qemu 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 qemu 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

# extra check for no any cbsd process related to this VM is active
#epid=$( ${PS_CMD} axopid,ucomm,command -ww | ${GREP_CMD} "${tmpdir}/qemuload.${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/qemurun.sh -c ${workdir}/jails-system/${jname}/qemu.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}\""
qcleanup 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_qemu_queue_name}" ] && ${cbsd_queue_backend} cbsd_queue_name=${cbsd_qemu_queue_name} id=${jname} cmd=qstop 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?
_chrooted=$( cbsdsqlro ${jailsysdir}/${jname}/local.sqlite "SELECT chrooted FROM settings LIMIT 1" )
[ -z "${_chrooted}" ] && _chrooted="0"

if [ "${_chrooted}" != "0" ]; then
	${ECHO} "${N1_COLOR}CHROOTED qstop: ${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}: qemu domain ${jname} stopped in ${diff_time}
exit 0
