#!/usr/local/bin/cbsd
#v11.1.17
globalconf="${distdir}/cbsd.conf";
MYARG=""
MYOPTARG="compress dstdir genmd5 gensize header_extra imgname jexport_exclude jname ls"
MYDESC="Export jail into image"
CBSDMODULE="jail"
ADDHELP="\
 compress - XZ compress level ( 0 - 9 ). Default is: 6. 0 mean is compression disabled\n\
 dstdir - alternative export dir ( by default - $workdir/exports )\n\
 genmd5 - generate $imgname.md5 file with md5sum of image\n\
 gensize - generate $imgname.size file with size of image in bytes\n\
 header_extra - can be as header_extra=\"test1=4,param=val\" or path to file. set extra/custom header data\n\
 imgname - alternative image dir ( by default - $jname.img\n\
 jexport_exclude - skip/exclude path in jail when export\n\
 jname - environment name\n\
 ls - filter for list: bls or jls\n\
 \n\
 Examples:\n\
  cbsd jexport jname=test jexport_exclude=\"/tmp/* /usr/ports /var/run/*\"      (exclude /tmp and /usr/ports in image)\n\
  cbsd jexport jname=test jexport_exclude=\"/\"                                 (export settings only, without data!)\n\
  cbsd jexport jname=test compress=0                                          (tar only, without compression)\n\
"
EXTHELP="wf_jexport"

. ${subrdir}/nc.subr
. ${strings}
. ${tools}

# generate md5 file?
genmd5=0
# generate size file?
gensize=0

header_extra=
jexport_exclude=
ojexport_exclude=
. ${cbsdinit}
[ -n "${jexport_exclude}" ] && ojexport_exclude="${jexport_exclude}"

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

jexport_me()
{
	local _ret
	local _filestats _header_extra_opt
	local _sys_exclude _jexport_exclude_args

	${ECHO} "${N1_COLOR}Exporting (with compress level:${N2_COLOR}${compress}${N1_COLOR}), please stand by: ${N2_COLOR}${jname}${N0_COLOR}"
	local DEST="${dstdir}/${imgname}"
	local JAILRCCONF="${jailsysdir}/${jname}/rc.conf_${jname}"

	# check for already existance and offline
	if [ ! -d "${data}" ]; then
		${ECHO} "${N1_COLOR}No jail data for ${jname}: ${N2_COLOR}${data}${N0_COLOR}"
		return 1
	fi

	_filestats=$( ${MKTEMP_CMD} )

	trap "${RM_CMD} -f ${_filestats}" HUP INT ABRT BUS TERM EXIT

SPACER="___NCSTART_HEADER=1_ \
___NCSTART_RCCONF=1 \
___NCSTART_FSTAB=1 \
___NCSTART_PKGINFO=1 \
___NCSTART_DESCR=1 \
___NCSTART_INFO=1 \
___NCSTART_LOCALFSTAB=1
___NCSTART_SYSDATA=1 \
___NCSTART_DATA=1 \
"

	for _part in ${SPACER}; do
		case ${_part} in
			___NCSTART_HEADER*)
				# here we have a historical conflict where the bhyve stores some CI-related information in the same file (JAILRCCONF)
				# save original ci_\* settings as ${JAILRCCONF}.orig and restore when import, as temporary workaround.
				# todo: storage for CI-related settings, in SQL?
				# workaround for #603: https://github.com/cbsd/cbsd/issues/603
				[ -r ${JAILRCCONF} ] && ${GREP_CMD} ^ci_ ${JAILRCCONF} > ${JAILRCCONF}.orig
				jmkrcconf jname=${jname} > ${JAILRCCONF}
				[ -n "${header_extra}" ] && _header_extra_opt="header_extra=\"${header_extra}\""
				res=$( imgpart mode=pack jname=${jname} compress=${compress} part=header emulator=${emulator} out=${DEST} ${_header_extra_opt} )
				_ret=$?
				${RM_CMD} -f ${JAILRCCONF}
				[ ${_ret} -ne 0 ] && err 1 "${N1_COLOR}Error in ${_part}: ${res}${N0_COLOR}"
				;;
			___NCSTART_RCCONF*)
				#currenlty we import/export operation for rc.conf via ascii format
				#cause diffrence node can have version of cbsd with incompattible jail schema
				#so create rc.conf for pack operation
				jmkrcconf jname=${jname} > ${JAILRCCONF}
				res=$( replacewdir file0="${JAILRCCONF}" old=${workdir} new="CBSDROOT" )
				_ret=$?
				if [ ${_ret} -ne 0 ]; then
					${RM_CMD} -f ${JAILRCCONF}
					err 1 "${N1_COLOR}Error in ${_part} replacewdir: ${res}${N0_COLOR}"
				fi
				res=$( imgpart mode=pack jname=${jname} part=rcconf out=${DEST} )
				_ret=$?
				${RM_CMD} -f ${JAILRCCONF}
				[ ${_ret} -ne 0 ] && err 1 "${N1_COLOR}Error in ${_part}: ${res}${N0_COLOR}"
				;;
			___NCSTART_PKGINFO*)
				res=$( imgpart mode=pack jname=${jname} part=pkginfo out=${DEST} )
				[ $? -ne 0 ] && err 1 "${N1_COLOR}Error in ${_part}: ${res}${N0_COLOR}"
				;;
			___NCSTART_DESCR*)
				res=$( imgpart mode=pack jname=${jname} part=descr out=${DEST} )
				[ $? -ne 0 ] && err 1 "${N1_COLOR}Error in ${_part}: ${res}${N0_COLOR}"
				;;
			___NCSTART_INFO*)
				imgpart mode=pack jname=${jname} part=info out=${DEST}
				[ $? -ne 0 ] && err 1 "${N1_COLOR}Error in ${_part}: ${res}${N0_COLOR}"
				;;
			___NCSTART_FSTAB*)
				TMPFSTAB="${ftmpdir}/fstab.$$"
				[ ! -d ${jailfstabdir}/${jname} ] && ${MKDIR_CMD} ${jailfstabdir}/${jname}
				[ ! -f "${mount_fstab}" ] && ${TOUCH_CMD} ${mount_fstab}
				${CP_CMD} ${mount_fstab} ${TMPFSTAB}
				res=$( replacewdir file0="${mount_fstab}" old=${workdir} new="CBSDROOT" )
				[ $? -ne 0 ] && err 1 "${N1_COLOR}Error in ${_part} replacewdir: ${res}${N0_COLOR}"
				res=$( imgpart mode=pack jname=${jname} part=fstab out=${DEST} )
				[ $? -ne 0 ] && err 1 "${N1_COLOR}Error in ${_part}: ${res}${N0_COLOR}"
				${MV_CMD} ${TMPFSTAB} ${mount_fstab}
				;;
			___NCSTART_LOCALFSTAB*)
				# localfstab is not mandatory file. Errcode is always 0
				if [ -r ${mount_fstab}.local ]; then
					res=$( replacewdir file0="${mount_fstab}.local" old=${workdir} new="CBSDROOT" )
					[ $? -ne 0 ] && err 1 "${N1_COLOR}Error in ${_part} replacewdir: ${res}${N0_COLOR}"
				fi
				res=$( imgpart mode=pack jname=${jname} part=localfstab out=${DEST} || true )
				[ $? -ne 0 ] && err 1 "${N1_COLOR}Error in ${_part}: ${res}${N0_COLOR}"
				;;
			___NCSTART_SYSDATA*)
				res=$( imgpart mode=pack jname=${jname} part=sysdata out=${DEST} )
				[ $? -ne 0 ] && err 1 "${N1_COLOR}Error in ${_part}: ${res}${N0_COLOR}"
				;;
			___NCSTART_DATA*)
				#test for zfs mounted & mount if not
				_ebytes=0

				case "${emulator}" in
					jail)
						readconf jexport.conf
						_sys_exclude=
						[ -n "${ojexport_exclude}" ] && jexport_exclude="${ojexport_exclude}"
						if [ -n "${jexport_exclude}" ]; then
							${ECHO} "${W1_COLOR}${CBSD_APP} warning: ${N1_COLOR}will be excluded by the jexport_exclude settings: ${N1_COLOR}"
							for i in ${jexport_exclude}; do
								${ECHO} "  ${N2_COLOR}* ${i}${N0_COLOR}"
								if [ -n "${_sys_exclude}" ]; then
									_sys_exclude="${_sys_exclude} ${i}"
								else
									_sys_exclude="${i}"
								fi
							done
							_jexport_exclude_args="jexport_exclude=\"${_sys_exclude}\""
						fi
						;;
				esac

				case ${zfsfeat} in
					1)
						. ${subrdir}/zfs.subr
						zfsmnt ${data}
						if [ $? -eq 2 ]; then
							WASNOMOUNT=1
						else
							WASNOMOUNT=0
						fi
						[ ${WASNOMOUNT} -eq 1 ] && ${ZFS_CMD} mount "${ZPOOL}"
						_ebytes=$( ${ZFS_CMD} get -Hpo value used ${ZPOOL} )
						;;
					0)
						case "${emulator}" in
							xen|bhyve)
								# for bhyve/xen we can calculate disk size by stat
								_ebytes=$( ${FIND_CMD} ${jaildatadir}/${jname}-data/ -depth 1 -maxdepth 1 -type f | while read _myfile; do
									_tmp=$( ${STAT_CMD} -f %z ${_myfile} )
									_ebytes=$(( _size + _tmp ))
									echo ${_ebytes}
									done | ${TAIL_CMD} -n1 )
								;;
							*)
								;;
						esac
						;;
				esac
				jmkrcconf jname=${jname} > ${JAILRCCONF}
				printf "${H3_COLOR}"
				res=$( imgpart mode=pack jname=${jname} compress=${compress} part=data out=${DEST} filestats=${_filestats} ebytes=${_ebytes} ${_jexport_exclude_args} )
				_ret=$?
				printf "${N0_COLOR}"
				[ ${_ret} -ne 0 ] && err 1 "${N1_COLOR}Error in ${_part}: ${res}${N0_COLOR}"
				${RM_CMD} -f ${JAILRCCONF}
				[ "${WASNOMOUNT}" = "1" ] && ${ZFS_CMD} unmount "${ZPOOL}"
				;;
		esac
	done

	# Summary stat size
	if [ -s "${DEST}" ]; then
		imgfile=$( ${BASENAME_CMD} ${DEST} )
		img_bsize=$( ${STAT_CMD} -f "%z" ${DEST} 2>/dev/null )

		if [ ${gensize} -eq 1 ]; then
			echo ${img_bsize} > ${DEST}.size
		fi

		if [ ${genmd5} -eq 1 ]; then
			${MD5_CMD} -q ${DEST} > ${DEST}.md5
		fi

		if conv2human "${img_bsize}"; then
			img_size=${convval}
		fi

		img_flat_size=0
		if [ -r ${_filestats} ]; then
			. ${_filestats}
		fi

		if conv2human "${img_flat_size}"; then
			img_flat_size=${convval}
		fi

		${ECHO} "${N1_COLOR}environment flat size: ${N2_COLOR}${img_flat_size}${N1_COLOR}, images size ${imgfile}: ${N2_COLOR}${img_size}${N0_COLOR}"
		${ECHO} "${N1_COLOR}exported image file: ${N2_COLOR}${DEST}${N0_COLOR}"

		return 0
	else
		${ECHO} "${N1_COLOR}Unknown error in jexport${N0_COLOR}"
		return 1
	fi
}

[ -z "${compress}" ] && compress=6
[ -z "${dstdir}" ] && dstdir="${exportdir}"
[ ! -d "${dstdir}" ] && ${MKDIR_CMD} -p ${dstdir}

if [ -z "${ls}" ]; then
	ls="jls"
else
	shift
fi

[ -z "${jname}" -a -n "${1}" ] && jname="${1}"

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

	case "${ls}" in
		bls)
			emulator="bhyve"	# for jname_is_multiple
			;;
		*)
			emulator="jail"		# for jname_is_multiple
			;;
	esac
	jname_is_multiple

	if [ -n "${jail_list}" ]; then
		# recursion for masked jails
		for i in ${jail_list}; do
			jexport jname=${i} compress=${compress}
		done
		exit 0
	fi
else
	select_jail_by_list -s "List of all jails" -e ${ls}
	[ -z "${jname}" ] && err 1 "${N1_COLOR}Give me jname${N0_COLOR}"
fi

if [ -z "${imgname}" ]; then
	imgname="${jname}.img"
else
	imgname="${imgname}.img"
fi

. ${subrdir}/rcconf.subr

[ $? -eq 1 ] && err 1 "${N1_COLOR}No such jail: ${N2_COLOR}${jname}${N0_COLOR}"

if [ ${jid} -ne 0 -a "${emulator}" = "bhyve" ]; then
	err 1 "${N1_COLOR}VM is online${N0_COLOR}"
fi

# CBSD QUEUE
if [ "${mod_cbsd_queue_enabled}" = "YES" -a -z "${MOD_CBSD_QUEUE_DISABLED}" ]; then

	case "${emulator}" in
		jail)
			[ -n "${cbsd_jail_queue_name}" ] && ${cbsd_queue_backend} cbsd_queue_name=${cbsd_jail_queue_name} id=${jname} cmd=jexport status=1
			;;
		bhyve)
			[ -n "${cbsd_bhyve_queue_name}" ] && ${cbsd_queue_backend} cbsd_queue_name=${cbsd_bhyve_queue_name} id=${jname} cmd=jexport status=1
			;;
	esac

	${cbsd_queue_backend} cbsd_queue_name=${cbsd_import_queue_name} cmd=message msg="{\"id\":\"${jname}.img\",\"cmd\":\"jexport\",\"status\":\"1\",\"data\":{\"status\":\"1\",\"node\":\"local\",\"jname\":\"${jname}.img\",\"impsize\":\"0\",\"imptype\":\"${emulator}\"}}"
	${cbsd_queue_backend} cbsd_queue_name=${cbsd_import_queue_name} cmd=message msg="{\"cmd\":\"tooltip\",\"type\":\"information\",\"timeout\":10000,\"author\":\"Export\",\"msg\":\"${jname}#export#started...\"}"

fi

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

jexport_me
ret=$?

# CBSD QUEUE
if [ "${mod_cbsd_queue_enabled}" = "YES" -a -z "${MOD_CBSD_QUEUE_DISABLED}" ]; then

	case "${emulator}" in
		jail)
			[ -n "${cbsd_jail_queue_name}" ] && ${cbsd_queue_backend} cbsd_queue_name=${cbsd_jail_queue_name} id=${jname} cmd=jexport status=2
			;;
		bhyve)
			[ -n "${cbsd_bhyve_queue_name}" ] && ${cbsd_queue_backend} cbsd_queue_name=${cbsd_bhyve_queue_name} id=${jname} cmd=jexport status=2
			;;
	esac

	${cbsd_queue_backend} cbsd_queue_name=${cbsd_import_queue_name} id=${jname}.img cmd=update impsize=${img_bsize} status=1
	${cbsd_queue_backend} cbsd_queue_name=${cbsd_import_queue_name} cmd=message msg="{\"id\":\"${jname}.img\",\"cmd\":\"jexport\",\"status\":\"2\",\"data\":{\"status\":\"2\",\"node\":\"local\",\"jname\":\"${jname}.img\",\"impsize\":\"${img_bsize}\",\"imptype\":\"${emulator}\"}}"
	tmp_img_size=$( echo ${img_size} | ${TR_CMD} ' ' '#' )
	${cbsd_queue_backend} cbsd_queue_name=${cbsd_import_queue_name} cmd=message msg="{\"cmd\":\"tooltip\",\"type\":\"success\",\"timeout\":10000,\"author\":\"Export\",\"msg\":\"${jname}#export#completed:${tmp_img_size}\"}"
fi

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}"

exit ${ret}
