#!/usr/local/bin/cbsd
#v16.1.17
MYARG="jname part mode"
MYOPTARG="out hdrver compress emulator"
MYDESC="Pack or extract chunk from from image"
ADDHELP="out=path_to_file for ascii-part or directory to extract for binary\n\
or for binary image from pack mode\n\
part variants: header,rcconf,fstab,pkginfo,descr,info,sysdata,data\n\
mode=pack,extract\n\
compress - XZ compress level ( 0 - 9 ). Default is: 6. 0 mean is compression disabled\n"

. ${subr}

init $*

extractchunk()
{
	local _dir

	if [ -n "${out}" ]; then
		_dir=$( /usr/bin/dirname ${out} )
		if [ ! -d "${_dir}" ]; then
			/bin/mkdir ${_dir}
			/usr/sbin/chown ${cbsduser}:${cbsduser} ${_dir}
		fi
	fi

	[ -n "${out}" ] && /usr/bin/touch ${out}

	case ${BINARY} in
		0)
			[ -z "${out}" ] && out="/dev/stdout"
			imghelper --start ${FROM} --end ${TO} --infile ${SRC} --outfile ${out}
			[ $? -ne 0 ] && err 1 "${MAGENTA}imghelper error${NORMAL}"
			echo >> ${out}
			;;
		1)
			[ -z "${out}" ] && out=$( /bin/pwd )
			imghelper --start ${FROM} --end ${TO} --infile ${SRC} | /usr/bin/tar xpf - -C "${out}" --numeric-owner
			[ $? -ne 0 ] && err 1 "${MAGENTA}imghelper error${NORMAL}"
			;;
	esac

	return 0

}

# $hdrver used for determine correct struct of header
extractme()
{
	local _imgpart=0

	if [ -f "${jname}" ]; then
		SRC="${jname}"
	else
		SRC="${importdir}/${jname}.img"
		[ ! -f ${SRC} ] && err 1 "${MAGENTA}No such image: ${GREEN}${SRC}${NORMAL}"
	fi

	[ -n "${hdrver}" ] && _imgpart=$1

	DEST="${DATA}/${jname}"

	BINARY=0
	TO=0

	case "${part}" in
		"header")
			FROM="___NCSTART_HEADER=1"
			TO="___NCSTOP_HEADER=1"
			;;
		"rcconf")
			FROM="___NCSTART_RCCONF=1"
			TO="___NCSTOP_RCCONF=1"
			;;
		"fstab")
			FROM="___NCSTART_FSTAB=1"
			TO="___NCSTOP_FSTAB=1"
			;;
		"pkginfo")
			FROM="___NCSTART_PKGINFO=1"
			TO="___NCSTART_PKGINFO=1"
			;;
		"descr")
			FROM="___NCSTART_DESCR=1"
			TO="___NCSTOP_DESCR=1"
			;;
		"info")
			FROM="___NCSTART_INFO=1"
			TO="___NCSTOP_INFO=1"
			;;
		"localfstab")
			FROM="___NCSTART_LOCALFSTAB=1"
			TO="___NCSTOP_LOCALFSTAB=1"
			;;
		"sysdata")
			FROM="___NCSTART_SYSDATA=1"
			TO="___NCSTOP_SYSDATA=1"
			BINARY=1
			;;
		"data")
			FROM="___NCSTART_DATA=1"
			TO="___NCSTOP_DATA=1"
			BINARY=1
			;;
	esac

	extractchunk || err 1 "${MAGENTA}Error: image has no FROM/TO symbols for ${part}. Probably wrong format version: ${GREEN}${jname}${NORMAL}"
}

packme()
{
	local _hwnum=$( /sbin/sysctl -qn hw.ncpu )
	local _dsk_list _zvol_list _zvol_name

	[ -z "${_hwnum}" ] && _hwnum="0"

	case ${part} in
		header)
			# use jailrcconfdir for back compatible with CBSD < 10.1.0
			[ ! -f "${jailrcconfdir}/rc.conf_${jname}" -a ! -f "${jailsysdir}/${jname}/rc.conf_${jname}" ] && err 1 "${MAGENTA}No such rcconf for: ${GREEN}$jname${NORMAL}"
			[ -f "${jailrcconfdir}/rc.conf_${jname}" ] && . ${jailrcconfdir}/rc.conf_${jname}
			[ -f "$jailsysdir/$jname/rc.conf_${jname}" ] && . $jailsysdir/$jname/rc.conf_${jname}
			echo "add header"
			DT=$( /bin/date "+%Y%m%d" )

			/bin/cat > ${out} <<EOF
___NCSTART_HEADER=1
hdrver="3"
jname="${jname}"
compress="${compress}"
arch="${arch}"
ver="${ver}"
date="${DT}"
emulator="${emulator}"
ip4_addr="${ip4_addr}"
host_hostname="${host_hostname}"
EOF

			# collect storage info
			case "${emulator}" in
				bhyve)
					# get info about storage

					_zvol_list=0

					# additional loop to check for symlink and zvol
					if [ ${zfsfeat} -eq 1 ]; then
						. ${zfstool}
						for i in $( /usr/bin/find ${data}/ -type l -depth 1 -maxdepth 1 ); do
							if is_getzvol ${i}; then
								_zvol_name=$( /usr/bin/basename ${i} | /usr/bin/sed 's:\.vhd::g' )
								imgbytes=$( /sbin/zfs get -Hp -o value volsize ${is_zvol} )
								echo "vm_zvol_size_${_zvol_name}=\"${imgbytes}\"" >> ${out}
								_zvol_list=$(( _zvol_list + 1 ))
							fi
						done
					fi
					;;
				*)
			esac

			_dsk_list=$( cbsdsql ${jailsysdir}/${jname}/local.sqlite SELECT dsk_path FROM bhyvedsk WHERE jname=\"${jname}\" |/usr/bin/sed 's:\.vhd::g' )
			echo "vm_dsk_list=\"${_dsk_list}\"" >> ${out}
			echo "vm_zvol_list=\"${_zvol_name}\"" >> ${out}
			echo "___NCSTOP_HEADER=1" >> ${out}
			;;
		rcconf)
			# use jailrcconfdir for back compatible with CBSD < 10.1.0
			echo "___NCSTART_RCCONF=1" >> ${out}
			if [ -f "${jailsysdir}/${jname}/rc.conf_${jname}" ]; then
				/bin/cat $jailsysdir/$jname/rc.conf_${jname} >> ${out}
			else
				[ -f "${jailrcconfdir}/rc.conf_${jname}" ] && /bin/cat ${jailrcconfdir}/rc.conf_${jname} >> ${out}
			fi
			echo "___NCSTOP_RCCONF=1" >> ${out}
			;;
		pkginfo)
			echo "___NCSTART_PKGINFO=1" >> ${out}
			PINFO="${jailsysdir}/${jname}/pkg_info"
			if [ -f "${PINFO}" ]; then
				/bin/cat ${PINFO} >> ${out}
			else
				echo "No pinfo" >> ${out}
			fi
			echo "___NCSTOP_PKGINFO=1" >> ${out}
			;;
		descr)
			echo "___NCSTART_DESCR=1" >> ${out}
			DESCR="${jailsysdir}/${jname}/descr"
			if [ -f "${DESCR}" ]; then
				/bin/cat ${DESCR} >> ${out}
			else
				echo "No descr" >> ${out}
			fi
			echo "___NCSTOP_DESCR=1" >> ${out}
			;;
		info)
			echo "___NCSTART_INFO=1" >> ${out}
			INFO="${jailsysdir}/${jname}/info"
			if [ -f "${INFO}" ]; then
				/bin/cat ${INFO} >> ${out}
			else
				echo "No info" >> ${out}
			fi
			echo "___NCSTOP_INFO=1" >> ${out}
			;;
		fstab)
			echo "___NCSTART_FSTAB=1" >> ${out}
			[ -f "${jailfstabdir}/${jailfstabpref}${jname}" ] && /bin/cat ${jailfstabdir}/${jailfstabpref}${jname} >> ${out}
			echo "___NCSTOP_FSTAB=1" >> ${out}
			;;
		localfstab)
			echo "___NCSTART_LOCALFSTAB=1" >> ${out}
			[ -f "${jailfstabdir}/${jailfstabpref}${jname}.local" ] && /bin/cat ${jailfstabdir}/${jailfstabpref}${jname}.local >> ${out}
			echo "___NCSTOP_LOCALFSTAB=1" >> ${out}
			;;
		sysdata)
			echo "add sysdata"
			echo "___NCSTART_SYSDATA=1" >> ${out}
			cd $jailsysdir
			[ ! -d "$jname" ] && /bin/mkdir -p ${jname}
			TMPCHUNK="$tmpdir/sys$$.tgz"
			trap "/bin/rm -f ${TMPCHUNK}" 0 1 2 3 4
			/usr/bin/tar cfz ${TMPCHUNK} ${jname} --numeric-owner > /dev/null 2>&1
			/bin/cat ${TMPCHUNK} >> ${out}
			echo >> ${out}
			echo "___NCSTOP_SYSDATA=1" >> ${out}
			;;
		data)
			echo "add data"
			# use jailrcconfdir for back compatible with CBSD < 10.1.0
			[ ! -f "${jailrcconfdir}/rc.conf_${jname}" -a ! -f "${jailsysdir}/${jname}/rc.conf_${jname}" ] && err 1 "${MAGENTA}No such rcconf for: ${GREEN}$jname${NORMAL}"
			[ -f ${jailrcconfdir}/rc.conf_${jname} ] && . ${jailrcconfdir}/rc.conf_${jname}
			[ -f $jailsysdir/$jname/rc.conf_${jname} ] && . $jailsysdir/$jname/rc.conf_${jname}
			eval data=\"\$data\"
			[ -n "${data}" -a -d "${data}" ] || err 1 "No such data directory $data."
			echo "___NCSTART_DATA=1" >> ${out}
			cd $( /usr/bin/dirname ${data} )

			case "${compress}" in
				0)
					# no compression
					/usr/bin/nice -n 20 /usr/bin/tar cf - --numeric-owner $( /usr/bin/basename ${data} ) >> ${out}
					;;
				*)
					/usr/bin/nice -n 20 /usr/bin/tar cf - --numeric-owner $( /usr/bin/basename ${data} ) |/usr/bin/nice -n 20 /usr/bin/xz -${compress}e -T${_hwnum} >> ${out}
					;;
			esac

			echo >> ${out}
			echo "___NCSTOP_DATA=1" >> ${out}

			# additional loop to check for symlink and zvol
			if [ ${zfsfeat} -eq 1 ]; then
				. ${zfstool}
				for i in $( /usr/bin/find ${data}/ -type l -depth 1 -maxdepth 1 ); do
					if is_getzvol ${i}; then
						_zvol_name=$( /usr/bin/basename ${i} | /usr/bin/sed 's:\.vhd::g' )
						echo "___NCSTART_ZVOL_${_zvol_name}=1" >> ${out}
						/bin/dd if=/dev/zvol/${is_zvol} |/usr/bin/gzip --fast -c >> ${out}
						echo >> ${out}
						echo "___NCSTOP_ZVOL_${_zvol_name}=1" >> ${out}
					fi
				done
			fi

			;;
	esac
}

# MAIN
# cwd necessary for the relative actions
cd ${workdir}

[ -z "${compress}" ] && compress=6

case "$mode" in
	extract)
		extractme || err 1 "Error extract"
		;;
	pack)
		. ${jrcconf}
		[ $? -eq 1 ] && err 1 "${MAGENTA}No such jail: ${GREEN}${jname}${NORMAL}"
		[ ${jid} -ne 0 -a "${emulator}" = "bhyve" ] && err 1 "${MAGENTA}VM is online${NORMAL}"
		[ -z "$out" ] && err 1 "${MAGENTA}out=tofile is mandatory${NORMAL}"
		packme
		;;
esac
