#!/usr/local/bin/cbsd
#v12.1.6
MYARG="old new host_hostname"
MYOPTARG="ip4_addr checkstate verbose promote clone_method mac_reinit"
MYDESC="Jail cloning"
CBSDMODULE="jail"
ADDHELP="\
  ip4_addr - can be DHCP\n\
  checkstate - 0 do not check for jail online. Default is 1 - check\n\
  promote - Promotes clone to no longer be dependent from origin: 0 or 1. Default is 0 (not promote)\n\
  clone_method - can be 'auto' or 'rsync'. Default is: auto\n\
  mac_reinit=0,1 (default 1). 0 - leave old MAC. 1 - set mac to 0 for re-generate new\n"
EXTHELP="wf_jclone"

. ${subrdir}/nc.subr

mac_reinit=1			# regenerate new MAC by default

. ${jfs}
. ${strings}

checkstate=1

readconf jclone.conf		# read users promote and clone_method settings

. ${cbsdinit}

[ -z "${promote}" ] && promote=0
[ -z "${clone_method}" ] && clone_method="auto"

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

myjid=$( cbsdsqlro local SELECT jid FROM jails WHERE jname=\"${old}\" 2>/dev/null )
[ -z "${myjid}" ] && err 1 "${N1_COLOR}No such jail: ${N2_COLOR}${old}${N0_COLOR}"

if [ ${checkstate} -eq 1 ]; then
	[ ${myjid} -ne 0 ] && log_err 1 "${N1_COLOR}jail is online, please stop them: ${N2_COLOR}${old}${N1_COLOR} (or use ${N2_COLOR}checkstate=0${N1_COLOR})${N0_COLOR}"
fi

myjid=$( cbsdsqlro local SELECT jid FROM jails WHERE jname=\"${new}\" 2>/dev/null )
[ -n "${myjid}" ] && err 1 "${N1_COLOR}Jail already exist: ${N2_COLOR}${new}${N0_COLOR}"

jname="${old}"

protected=$( cbsdsqlro local SELECT protected FROM jails WHERE jname=\"${old}\" )

# CBSD QUEUE
if [ "${mod_cbsd_queue_enabled}" = "YES" -a -z "${MOD_CBSD_QUEUE_DISABLED}" ]; then
	[ -n "${cbsd_jail_queue_name}" ] && ${cbsd_queue_backend} cbsd_queue_name=${cbsd_jail_queue_name} id="${new}" cmd=jclone ip4_addr="${ip4_addr}" protected="${protected}" status=1
fi

case "${ip4_addr}" in
	[Dd][Hh][Cc][Pp])
		dhcpip=$( dhcpd )
		if [ $? -eq 2 ]; then
			cbsdlogger WARNING ${CBSD_APP}: no free IPv4 address for DHCP in nodeippool
			err 1 "${N1_COLOR}no free IPv4 address for DHCP in nodeippool${N0_COLOR}"
		fi
		[ -z "${dhcpip}" ] && err 1 "${N1_COLOR}unable to dhcpd${N0_COLOR}"
		ipwmask ${dhcpip}
		[ -z "${IWM}" ] && err 1 "${N1_COLOR}Unable IWM for dhcpd${N0_COLOR}"
		iptype ${IWM}
		[ $? -eq 0 ] && err 1 "${N1_COLOR}Not ip from dhcpd?: ${dhcpip} / ${IWM}"
		ip4_addr="${dhcpip}"
		${ECHO} "${N1_COLOR}Selected IPv4: ${N2_COLOR}${ip4_addr}${N0_COLOR}"
		if [ "${mod_cbsd_queue_enabled}" = "YES" -a -z "${MOD_CBSD_QUEUE_DISABLED}" ]; then
			[ -n "${cbsd_jail_queue_name}" ] && ${cbsd_queue_backend} cbsd_queue_name=${cbsd_jail_queue_name} id="${new}" cmd=update ip4_addr="${ip4_addr}" status=1
		fi
		;;
	[Dd][Hh][Cc][Pp][Vv][6])
		dhcpip=$( dhcpdv6 )
		if [ $? -eq 2 ]; then
			cbsdlogger WARNING ${CBSD_APP}:no free IPv6 address for DHCP in nodeippool
			err 1 "${N1_COLOR}no free IPv6 address for DHCP in nodeippool${N0_COLOR}"
		fi
		[ -z "${dhcpip}" ] && err 1 "${N1_COLOR}unable to dhcpdv6${N0_COLOR}"
		# normalize IPv6 to compressed form
		eval $( ${miscdir}/sipcalc ${dhcpip} )
		dhcpip="${_compressed_ipv6_address}"
		ip4_addr="${dhcpip}"
		${ECHO} "${N1_COLOR}Selected IPv6: ${N2_COLOR}${ip4_addr}${N0_COLOR}"
		if [ "${mod_cbsd_queue_enabled}" = "YES" -a -z "${MOD_CBSD_QUEUE_DISABLED}" ]; then
			[ -n "${cbsd_jail_queue_name}" ] && ${cbsd_queue_backend} cbsd_queue_name=${cbsd_jail_queue_name} id="${new}" cmd=update ip4_addr="${ip4_addr}" status=1
		fi
		;;
	*)
		[ -z "${ip4_addr}" ] && ip4_addr=$( cbsdsqlro local SELECT ip4_addr FROM jails WHERE jname=\"${old}\" )
		if [ "${mod_cbsd_queue_enabled}" = "YES" -a -z "${MOD_CBSD_QUEUE_DISABLED}" ]; then
			[ -n "${cbsd_jail_queue_name}" ] && ${cbsd_queue_backend} cbsd_queue_name=${cbsd_jail_queue_name} id="${new}" cmd=update ip4_addr="${ip4_addr}" status=1
		fi
		;;
esac

ip4_addr_new="${ip4_addr}"
host_hostname_new="${host_hostname}"

. ${subrdir}/rcconf.subr
[ "${emulator}" = "bhyve" ] && err 1 "${N1_COLOR}Jail in bhyve mode. Use ${N2_COLOR}cbsd brename${N1_COLOR} instead${N0_COLOR}"

DST="${jaildatadir}/${new}-${jaildatapref}"
SRC="${jaildatadir}/${old}-${jaildatapref}"
JAILDIR="${jaildir}/${new}"
JAIL_FSTAB_LEGACY="${jailfstabdir}/${jailfstabpref}${new}"		# legacy/new fstab location, < CBSD 12.1.5
JAIL_FSTAB_ORIG_LEGACY="${jailfstabdir}/${jailfstabpref}${old}"		# legacy/old fstab location, < CBSD 12.1.5
JAIL_LOCAL_FSTAB_LEGACY="${jailfstabdir}/${jailfstabpref}${new}.local"	# legacy/new fstab.local location, < CBSD 12.1.5
JAIL_LOCAL_FSTAB_ORIG="${jailfstabdir}/${jailfstabpref}${old}.local"	# legacy/old fstab.local location, < CBSD 12.1.5

JAIL_FSTAB_DIR_OLD="${jailfstabdir}/${old}"				# old jail fstab dir, CBSD > 12.1.5
JAIL_FSTAB_DIR_NEW="${jailfstabdir}/${new}"				# new jail fstab dir, CBSD > 12.1.5

JAILRCCONF="${jailrcconfdir}/rc.conf_${new}"
JAILRCCONFORIG="${jailrcconfdir}/rc.conf_${old}"

JAIL_SYS_DIR_OLD="${jailsysdir}/${old}"
JAIL_SYS_DIR_NEW="${jailsysdir}/${new}"

. ${subrdir}/jcreate.subr
geniplist ${ip4_addr}				# for ipvX_first_public-like vars
newdata="${DST}"
newjname="${new}"
export_jail_data_for_external_hook newdata newjname
external_exec_master_script "clone.d"

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

external_exec_local_script "clone-local.d"
_ret=$?

case "${_ret}" in
	0)
		# no local script, continue as usual
		skip_data_operation=0
		;;
	1)
		# local script exist and successful, skip data operation
		${ECHO} "${N1_COLOR}clone-local.d hook success${N0_COLOR}"
		skip_data_operation=1
		;;
	2)
		err 1 "${N1_COLOR}clone-local.d hook failed, cancel clone${N0_COLOR}"
		;;
	*)
		skip_data_operation=0
		;;
esac

#rename zfs fs source
case ${zfsfeat} in
	1)
		. ${subrdir}/zfs.subr
		zfsmnt ${SRC}
		[ $? -eq 2  ] && ${ZFS_CMD} mount ${ZPOOL}
	;;
esac

[ ! -d "${SRC}" ] && err 1 "${N1_COLOR}No jail data: ${N2_COLOR}${SRC}${N0_COLOR}"

if [ -d "${DST}" -a ${skip_data_operation} -eq 0 ]; then
	${RMDIR_CMD} ${DST} >/dev/null 2>&1

	# CBSD QUEUE
	if [ "${mod_cbsd_queue_enabled}" = "YES" -a -z "${MOD_CBSD_QUEUE_DISABLED}" ]; then
		[ -n "${cbsd_jail_queue_name}" ] && ${cbsd_queue_backend} cbsd_queue_name=${cbsd_queue_name} id="${new}" cmd=jclone status=2 data_status=0
	fi

	[ -d "${DST}" ] && err 1 "${N1_COLOR}Jail data already exist and it not empty: ${N2_COLOR}${DST}${N0_COLOR}"
fi

for i in ${JAILDIR} ${jailfstab} ${jailrcconf}; do
	[ ! -d "${i}" ] && ${MKDIR_CMD} -p ${i}
done

printf "${N1_COLOR}Cloning.${N0_COLOR}"
jmkrcconf jname=${old} > ${JAILRCCONF}

/usr/local/cbsd/misc/cbsdsysrc -qf ${JAILRCCONF} jname="${new}" > /dev/null 2>&1
# set status to offline if original jail was active
/usr/local/cbsd/misc/cbsdsysrc -qf ${JAILRCCONF} status="0" > /dev/null 2>&1

jregister jname=${new}

${RM_CMD} -f ${JAILRCCONF}

# CBSD > 12.1.5
if [ -d ${JAIL_FSTAB_DIR_OLD} ]; then
	${CP_CMD} -a ${JAIL_FSTAB_DIR_OLD} ${JAIL_FSTAB_DIR_NEW}
	[ -r ${JAIL_FSTAB_DIR_NEW}/fstab ] && ${SED_CMD} -i${SED_DELIMER}'' s:/${old}:/${new}:g ${JAIL_FSTAB_DIR_NEW}/fstab
	[ -r ${JAIL_FSTAB_DIR_NEW}/fstab.local ] && ${SED_CMD} -i${SED_DELIMER}'' s:/${old}:/${new}:g ${JAIL_FSTAB_DIR_NEW}/fstab.local
else
	[ ! -d ${JAIL_FSTAB_DIR_NEW} ] && ${MKDIR_CMD} -p ${JAIL_FSTAB_DIR_NEW}
fi

# check for legacy ( CBSD < 12.1.5) fstabs file
if [ -r "${JAIL_FSTAB_ORIG_LEGACY}" ]; then
	${SED_CMD} s:/${old}:/${new}:g ${JAIL_FSTAB_ORIG_LEGACY} > ${JAIL_FSTAB_DIR_NEW}/fstab
fi
if [ -r "${JAIL_LOCAL_FSTAB_ORIG_LEGACY}" ]; then
	${SED_CMD} s:/${old}:/${new}:g ${JAIL_LOCAL_FSTAB_ORIG_LEGACY} > ${JAIL_FSTAB_DIR_NEW}/fstab.local
fi
dot "fstab"

cbsdsqlrw local UPDATE jails SET path=\"${jaildir}/${new}\",mount_fstab=\"${JAIL_FSTAB_DIR_NEW}/fstab\",data=\"${jaildatadir}/${new}-${jaildatapref}\",rcconf=\"${jailrcconfdir}/rc.conf_${new}\",host_hostname=\"${host_hostname}\" WHERE jname=\"${new}\"

[ -n "${ip4_addr_new}" ] && cbsdsqlrw local UPDATE jails SET ip4_addr=\"${ip4_addr_new}\" WHERE jname=\"${new}\"
[ -n "${host_hostname_new}" ] && cbsdsqlrw local UPDATE jails SET host_hostname=\"${host_hostname_new}\" WHERE jname=\"${new}\"

jname=${new}

[ "${zfsfeat}" != "1" ] && clone_method="rsync"

# jclone-local data exist/passed ?
if [ ${skip_data_operation} -eq 0 ]; then
	case ${clone_method} in
		auto)
			clonedata -s ${SRC} -d ${DST} -m zfs -n ${new} -p ${promote}
			;;
		*)
			if [ "${verbose}" = "1" ]; then
				clonedata -s ${SRC} -d ${DST} -m rsync
			else
				clonedata -s ${SRC} -d ${DST} -m rsync > /dev/null 2>&1
			fi
	esac
else
	if [ ! -d "${DST}" ]; then
		${ECHO} "${N1_COLOR}jclone error: local script succeeded, however destination dir doesn't exist: ${N2_COLOR}${DST}${N0_COLOR}"
		${ECHO} "${N1_COLOR}make sure your scripts are copying the data correctly, we expect ${N2_COLOR}${DST}${N1_COLOR} to be the same as ${N2_COLOR}${SRC}${N0_COLOR}"
		jremove jname=${new}
		exit 1
	fi
fi

dot "clone rsync"

if [ -d "${JAIL_SYS_DIR_OLD}" ]; then
	[ -d "${JAIL_SYS_DIR_NEW}" ] && ${RM_CMD} -rf ${JAIL_SYS_DIR_NEW}
	${CP_CMD} -Rp ${JAIL_SYS_DIR_OLD} ${JAIL_SYS_DIR_NEW}
#	if [ -d ${JAIL_SYS_DIR_NEW}/clone-local.d ]; then
#		printf "${N1_COLOR}.[purge ${JAIL_SYS_DIR_NEW}/clone-local.d/ entry].${N0_COLOR}"
#		${FIND_CMD} ${JAIL_SYS_DIR_NEW}/clone-local.d/ -mindepth -type f -delete
#	fi
	dot "cp sysdir"
fi

#rctl
LIMITS="${jailsysdir}/${old}/jail.limits"
jmkrctlconf jname=${old} type=rctl mode=tofile file=${LIMITS}
jmkrctlconf jname=${old} type=extra mode=tofile file=${LIMITS}.extra
jmkrctlconf jname=${new} type=rctl mode=tosql file=${LIMITS}
jmkrctlconf jname=${new} type=extra mode=tosql file=${LIMITS}.extra
#

# Re-init all MAC address
if [ ${mac_reinit} -eq 1 ]; then
	cbsdlogger NOTICE ${CBSD_APP}: re-init MAC address, set to 0: ${new}
	cbsdsqlrw ${jailsysdir}/${new}/local.sqlite "UPDATE jailnic SET nic_hwaddr=\"0\""
fi

# CBSD QUEUE
if [ "${mod_cbsd_queue_enabled}" = "YES" -a -z "${MOD_CBSD_QUEUE_DISABLED}" ]; then
	[ -n "${cbsd_jail_queue_name}" ] && ${cbsd_queue_backend} cbsd_queue_name=${cbsd_queue_name} id="${new}" cmd=jclone status=2 data_status=0
fi

end_time=$( ${DATE_CMD} +%s )
diff_time=$(( end_time - st_time ))
diff_time=$( displaytime ${diff_time} )

${ECHO} "${N2_COLOR}ok${N0_COLOR}"

cbsdlogger NOTICE ${CBSD_APP}: jail ${old} has been cloned to ${new} in ${diff_time}
${ECHO} "${N1_COLOR}${CBSD_APP} done ${N2_COLOR}in ${diff_time}${N0_COLOR}"
exit 0
