#!/usr/local/bin/cbsd
#v13.0.8
MYARG=""
# should be in sync with run_jail() func: tools/up script
MYOPTARG="jconf inter customskel fstablocal delpkglist removejconf pkglist jprofile zfs_snapsrc pkg_bootstrap autorestart runasap etcupdate_init quiet ci_user_pubkey \
ci_ip4_addr ci_gw4 sysrc from"
# allow all jail settings
. ${distsharedir}/jail-arg
[ "${racct}" = "1" ] && . ${distsharedir}/rctl.conf
JAIL_ARGS="${JARG}"
MYOPTARG="${MYOPTARG} ${JAIL_ARGS}"
MYDESC="Create jail from config file or args"
ADDHELP="

${H3_COLOR}Description${N0_COLOR}:

The jail is created according to configuration file generated by jconstruct-tui.
You can see this configuration file if you answer negatively to the 
'Do you want to create jail immediately?' question at the end of the dialogue.

In addition, you can override all parameters via command line arguments.
Many parameters are set by default via profiles, 
e.g: ~cbsd/etc/defaults/jail-freebsd-default.conf which can be overwrited via
~cbsd/etc/jail-freebsd-default.conf or personal profile.

You can create your own profile and specify it when creating the container.

If jail exist and autorestart=1 - jset will be used to update params.

Alternative methods of creating jail:
  CBSDfile, 'cbsd jconstruct', 'cbsd jconstruct-tui'.

${H3_COLOR}Options${N0_COLOR}:

 ${N2_COLOR}ci_user_pubkey${N0_COLOR}  - full/relative path to authorized_keys or may contain pubkey 
                   string itself, e.g: ci_user_pubkey=\"ssh-ed25519 XXXXX root@my.domain\".
                   This options will customize /root/.ssh/authorized_keys in jail.
 ${N2_COLOR}ci_gw4=${N0_COLOR}         - 0,IP to disable: manage/set defaultrouter= settings in jail rc.conf (for vnet).
 ${N2_COLOR}etcupdate_init=${N0_COLOR} - 1(enable),0(disable) for etcupdate init (overwrite config values).
 ${N2_COLOR}from=${N0_COLOR}           - <url> or MD5 of image to create jail from CBSD image.
 ${N2_COLOR}inter=${N0_COLOR}          - 0 to prevent any questions and to accept answers by default.
 ${N2_COLOR}customskel=${N0_COLOR}     - <path>: additional skel directory applyed above jail structrure.
 ${N2_COLOR}fstablocal=${N0_COLOR}     - <path>: additional fstab file stored as fstab.local.
 ${N2_COLOR}jprofile=${N0_COLOR}       - <name>:  specify jail profile for creating jail.
 ${N2_COLOR}zfs_snapsrc=${N0_COLOR}    - <name>: use ZFS snapshot as data source.
 ${N2_COLOR}pkg_bootstrap=${N0_COLOR}  - 0,1: overwrite pkg_bootstrap from conf file.
 ${N2_COLOR}removejconf=${N0_COLOR}    - 0,1: remove jconf after jcreate? 0 - don't remove.
 ${N2_COLOR}runasap=${N0_COLOR}        - 0,1: when 1 - run a jail immediately (atomic jcreate+jstart).
 ${N2_COLOR}quiet=${N0_COLOR}          - 0,1: be quiet, dont output verbose message.
 ${N2_COLOR}zfs_encryption=${N0_COLOR} - 0,1: enable native ZFS encryption.

Additional args when RACCT enabled: ${RCTL} ${RCTL_EXTRA}

 ${N2_COLOR}cpu=${N0_COLOR}            - limit number of CPU cores, e.g: 1
 ${N2_COLOR}fsquota=${N0_COLOR}        - set ZFS quota, e.g: 10g
 ${N2_COLOR}vmemoryuse=${N0_COLOR}     - limit memory use, e.g: 512m

${H3_COLOR}Examples${N0_COLOR}:

 # cbsd jcreate jname=test runasap=1 zfs_encryption=1
 # cbsd jcreate jname=test2 astart=0 pkglist=\"misc/mc net/fping\" ip4_addr=DHCP,DHCPv6 allow_sysvipc=1 allow_raw_sockets=1
 # cbsd jcreate jname=vnet1 runasap=1 ip4_addr=\"10.0.1.5/24\" ci_gw4=\"10.0.1.1\" ci_user_pubkey=\"/root/.ssh/authorized_keys\"
 # cbsd jcreate jname=deb jprofile=debian_buster allow_raw_sockets=1 vnet=1
 # cbsd jcreate jname=ubu jprofile=ubuntu_focal allow_raw_sockets=1
 # cbsd jcreate jname=cent jprofile=centos_7 allow_raw_sockets=1
 # cbsd jcreate jname=riscv pkg_bootstrap=0 arch=riscv emulator=\"qemu-riscv64-static\" exec_start=\"/bin/qemu-riscv64-static /bin/sh /etc/rc\" exec_stop=\"/bin/qemu-riscv64-static /bin/sh /etc/rc.shutdown\"
 # cbsd jcreate jname=xx ver=13.0 vnet=1 sysrc=\"ifconfig_eth0+='mtu 1450' inetd_enable=YES\"
 # cbsd jcreate jname=vmagent from=https://dl.bsdstore.ru/img/amd64/amd64/13.0/vmagent/vmagent.img pkg_bootstrap=0 runasap=1
 # cbsd jcreate jname=myapp from=fbbb4e8707f6794008cc6e8ed0d86082 runasap=1
 # cbsd jcreate jname=small cpu=1 fsquota=10g vmemoryuse=512m runasap=1

${H3_COLOR}See also${N0_COLOR}:

 cbsd jconstruct --help
 cbsd jconstruct-tui --help
 cbsd up --help
 cbsd images --help

"
CBSDMODULE="jail"
EXTHELP="wf_jcreate"

. ${subrdir}/nc.subr
. ${system}
. ${tools}
. ${mdtools}
. ${jfs}

ci_user_pubkey=
sysrc=
from=
. ${cbsdinit}

# if some of params specified via args, store them as temporary vars
for i in ${MYOPTARG}; do
	unset o${i}
	eval "o${i}=\$$i"
done

# jcreate global conf
readconf jcreate.conf

[ -n "${oquiet}" ] && quiet="${oquiet}"

if [ -z "${jconf}" ]; then
	jconf=$( ${MKTEMP_CMD} )
	export CBSD_INIT_SAVE2FILE=${jconf}			# save args and generate jconf
	[ -z "${removejconf}" ] && removejconf=1
	${SYSRC_CMD} -qf ${jconf} removejconf="${removejconf}" > /dev/null 2>&1
	. ${cbsdinit}
	unset CBSD_INIT_SAVE2FILE
fi

[ -z "${jconf}" -a -z "${jname}" ] && err 1 "${N1_COLOR}please set for jcreate: ${N2_COLOR}${jconf}${N0_COLOR}"
[ -z "${autorestart}" ] && autorestart=0
[ -z "${quiet}" ] && quiet=0
[ -n "${etcupdate_init}" ] && oetcupdate_init="${etcupdate_init}"
[ -n "${removejconf}" ] && oremovejconf="${removejconf}"

mkfstab()
{
	local _dir

	_dir=$( ${DIRNAME_CMD} ${mount_fstab} )

	[ ! -d ${_dir} ] && ${MKDIR_CMD} -p ${_dir}

	if [ ${baserw} -eq 0 ]; then
		${CAT_CMD} > ${mount_fstab} << EOF
# Please do not edit this file for additional fstabs
# Use ${mount_fstab}.local instead
${data}/etc /etc ${NULLFS} rw 0 0
${data}/root /root ${NULLFS} rw 0 0
${data}/tmp /tmp ${NULLFS} rw 0 0
${data}/usr/home /usr/home ${NULLFS} rw 0 0
${data}/usr/local /usr/local ${NULLFS} rw 0 0
${data}/compat /compat ${NULLFS} rw 0 0
${data}/var /var ${NULLFS} rw 0 0
#
EOF
	fi

	${TOUCH_CMD} ${mount_fstab}.local
}

install_and_apply_helpers()
{
	local _srcfile="${1}"

	local module=$( cbsdsqlro ${_srcfile} SELECT helpername FROM system | ${AWK_CMD} '{printf $1}' )

	[ ${quiet} -ne 1 ] && ${ECHO} "${N1_COLOR}Install and apply helpers ${_srcfile}: ${N2_COLOR}${module}${N0_COLOR}"

	local helperdir="${jailsysdir}/${jname}/helpers"

	[ ! -d "${helperdir}" ] && ${MKDIR_CMD} -p ${helperdir}

	local formfile="${helperdir}/${module}.sqlite"

	[ ${quiet} -ne 1 ] && echo ":: ${CP_CMD} -a ${_srcfile} ${formfile}"
	${CP_CMD} -a ${_srcfile} ${formfile}

	[ ${quiet} -ne 1 ] && echo ":: forms module=${module} mode=apply jname=${jname} inter=0"
	forms module=${module} mode=apply jname=${jname} inter=0
}

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

[ -n "${delpkglist}" ] && odelpkglist="${delpkglist}"
# push pkg_bootstrap to orig_ variable
[ -n "${pkg_bootstrap}" ] && opkg_bootstrap="${pkg_bootstrap}"

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

readconf zfs.conf
readconf buildworld.conf
readconf jail-freebsd-default.conf

# several defaults
[ -z "${mnt_start}" ] && mnt_start="0"
[ -z "${mnt_stop}" ] && mnt_stop="0"
[ -z "${etcupdate_init}" ] && etcupdate_init="1"

# set default nice from rctl_nice
[ -n "${rctl_nice}" ] && nice="${rctl_nice}"

. ${subrdir}/universe.subr
. ${subrdir}/freebsd_world.subr

[ ! -f "${jconf}" ] && err 1 "${N1_COLOR}no such jconf file${N0_COLOR}"

jconf=$( ${REALPATH_CMD} ${jconf} )

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

temprcconf="${ftmpdir}/jcreate_jconf.$$"

# TRIM DOS CRLF
${CAT_CMD} ${jconf} | ${TR_CMD} -d \\r > ${temprcconf}
${CAT_CMD} ${temprcconf} >${jconf}

[ -n "${oremovejconf}" ] && removejconf="${oremovejconf}"

if [ ${removejconf} = "1" ]; then
	trap "${RM_CMD} -f ${temprcconf} ${jconf}" HUP INT ABRT BUS TERM EXIT
else
	trap "${RM_CMD} -f ${temprcconf}" HUP INT ABRT BUS TERM EXIT
fi

# todo: when 'from' exist: use temprcconf settings to jset fromfile to re-configure default image options
[ -n "${ofrom}" ] && from="${ofrom}"

# todo: there is a lot of duplicate code (for example, create_fs/from_zfssnap): cleaning required.
if [ -n "${from}" ]; then
	. ${temprcconf}
	jstatus jname=${jname} > /dev/null 2>&1
	[ $? -ne 0 ] && stderr 1 "${N1_COLOR}${CBSD_APP}: jail already exist: ${N2_COLOR}${jname}${N0_COLOR}"

	[ ! -r ${dbdir}/images.sqlite ] && ${miscdir}/updatesql ${dbdir}/images.sqlite ${distdir}/share/local-images.schema images

	from_md5=

	# is from=MD5?
	_res=$( cbsdsqlro images "SELECT source FROM images WHERE md5=\"${from}\"" 2>/dev/null )
	if [ -n "${_res}" ]; then
		from_md5="${from}"
	else
		# second chance - is md5(from) exist?
		if [ -z "${_res}" ]; then
			tmpfrom=$( ${MD5_CMD} -qs "${from}" )
			_res=$( cbsdsqlro images "SELECT source FROM images WHERE md5=\"${tmpfrom}\"" 2>/dev/null )
			[ -n "${_res}" ] && from_md5="${tmpfrom}"
			unset tmpfrom
		fi
	fi

	jail_created=0

	if [ -z "${from_md5}" ]; then
		# cbsd image register ...
		images mode=register path="${from}"
		ret=$?
		if [ ${ret} -ne 0 ]; then
			stderr 1 "${N1_COLOR}${CBSD_APP}: images failed: cbsd images mode=register from=\"${N2_COLOR}\${from}${N1_COLOR}\"${N0_COLOR}"
		fi
		tmpfrom=$( ${MD5_CMD} -qs "${from}" )
		_res=$( cbsdsqlro images "SELECT source FROM images WHERE md5=\"${tmpfrom}\"" 2>/dev/null )
		[ -z "${_res}" ] && stderr 1 "${N1_COLOR}${CBSD_APP}: images failed: cbsd images mode=register from=\"${N2_COLOR}\${from}${N1_COLOR}\"${N0_COLOR}"
		from_md5="${tmpfrom}"
		unset tmpfrom
	fi

	# first of all: check zfs dataset existance for image
	if [ ${zfsfeat} -eq 1 ]; then
		. ${subrdir}/zfs.subr
		DATA=$( ${ZFS_CMD} get -Ho value name ${jaildatadir} )
		_zfssrc="${DATA}/${from_md5}"
		_zfssrc_snap="${DATA}/${from_md5}@start"

		${ZFS_CMD} list -t snapshot ${_zfsssrc_snap} > /dev/null 2>&1
		_ret=$?
		if [ ${_ret} -ne 0 ]; then
			#echo "Create snapshot..."
			${ZFS_CMD} snapshot ${_zfssrc}@start
			${ZFS_CMD} list -t snapshot ${_zfssrc_snap} > /dev/null 2>&1
			_ret=$?
			[ ${_ret} -ne 0 ] && stderr 1 "${N1_COLOR}${CBSD_APP}: unable to create snapshot: ${N2_COLOR}${ZFS_CMD} list -t snapshot ${_zfssrc_snap}${N0_COLOR}"
		fi

		[ ${quiet} -ne 1 ] && ${ECHO} "${N1_COLOR}create jail from image snapshot: ${N2_COLOR}${_zfssrc_snap}${N0_COLOR}" 1>&2
		jcreate jname="${jname}" zfs_snapsrc="${_zfssrc_snap}"
		jail_created=1
	fi

	#check for jail exist
	jstatus jname=${jname} > /dev/null 2>&1
	[ $? -ne 1 ] && err 1 "${N1_COLOR}${CBSD_APP}: unable to create jail ${jname} from: ${N2_COLOR}${from}${N0_COLOR}"

	. ${temprcconf}

	for i in ${MYOPTARG}; do

		case "${i}" in
			jname|from|removejconf)
				continue
				;;
		esac

		eval T="\$o${i}"

		if [ -n "${T}" ]; then
#			echo "jset jname=${jname} ${i}=\"${T}\""
			jset jname=${jname} ${i}="${T}"
		fi
	done

	# autostart asap upon jail created
	[ "${runasap}" = "1" ] && jstart jname=${jname} quiet=${quiet}

	exit 0
fi

. ${temprcconf}

# Redis
if [ "${mod_cbsd_redis_enabled}" = "YES" -a -z "${MOD_CBSD_REDIS_DISABLED}" ]; then
	cbsdredis publish cbsd_events '{"cmd":"jcreate", "node":"'${nodename}'", "jail":"'${jname}'", "status":1, "ip4_addr":"'${ip4_addr}'"}'
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_jail_queue_name} id=${jname} cmd=jcreate ip4_addr=${ip4_addr} protected=${protected} vnc_port=0 status=1
fi

over="${ver}"
# Determine stable value. Must be after buildconf
strpos --str="${over}" --search="."

# auto-detect for stable/release
pos=$?
if [ ${pos} -eq 0 ]; then
	stable=1
	ostable=1
else
	stable=0
	ostable=0
fi

# re-read default tpl for apply dynamic variable with $jname
readconf jail-freebsd-default.conf

if [ -n "${jprofile}" ]; then
	if [ -r "${etcdir}/jail-freebsd-${jprofile}.conf" ]; then
		${ECHO} "${N1_COLOR}Use profile: ${N2_COLOR}${etcdir}/jail-freebsd-${jprofile}.conf${N0_COLOR}"
		${CAT_CMD} ${etcdir}/jail-freebsd-${jprofile}.conf >> ${temprcconf}
	elif [ -r "${etcdir}/defaults/jail-freebsd-${jprofile}.conf" ]; then
		${ECHO} "${N1_COLOR}Use profile: ${N2_COLOR}${etcdir}/defaults/jail-freebsd-${jprofile}.conf${N0_COLOR}"
		${CAT_CMD} ${etcdir}/defaults/jail-freebsd-${jprofile}.conf >> ${temprcconf}
	fi
fi

. ${temprcconf}

# apply pkglist from tpl_pkglist
if [ -n "${tpl_pkglist}" ]; then
	${SYSRC_CMD} -qf ${temprcconf} pkglist="${tpl_pkglist}" > /dev/null 2>&1
	pkglist="${tpl_pkglist}"
fi

#re-init rctl_ vars
#[ -n "${fsquota}" ] && rctl_fsquota="${fsquota}"

. ${subrdir}/build.subr

[ -z "${jname}" ] && err 1 "${N1_COLOR}No such jname variable${N0_COLOR}"

init_jail_path
init_target_arch

if [ -n "${basename}" ]; then
	init_basedir -b ${basename}
	_basename_args="-b ${basename}"
else
	init_basedir
	_basename_args=
fi

init_kerneldir

jstatus jname=${jname} > /dev/null 2>&1

if [ $? -ne 0 ]; then
	# Redis
	if [ "${mod_cbsd_redis_enabled}" = "YES" -a -z "${MOD_CBSD_REDIS_DISABLED}" ]; then
		cbsdredis publish cbsd_events '{"cmd":"jcreate", "node":"'${nodename}'", "jail":"'${jname}'", "status":2, "ip4_addr":"'${ip4_addr}'"}'
	fi

	[ ${autorestart} -eq 0 ] && err 1 "${N1_COLOR}jail ${jname} already exist, use autorestart=1 for updating via jset${N0_COLOR}"
	jset jname=${jname} jconf=${jconf} autorestart=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_jail_queue_name} id=${jname} cmd=jcreate ip4_addr=${ip4_addr} astart=${astart} protected=${protected} vnc_port=0 status=2
	fi

	exit 0
fi

case "${emulator}" in
	qemu-aarch64-static)
		arch="arm64"
		target_arch="aarch64"
		;;
	qemu-ppc64-static)
		arch="powerpc"
		target_arch="powerpc64"
		;;
	qemu-riscv64-static)
		arch="riscv"
		target_arch="riscv64"
esac

get_base -v ${ver} ${_basename_args}

[ ${quiet} -ne 1 ] && ${ECHO} "${N1_COLOR}Please wait: ${N2_COLOR}this will take a while...${N0_COLOR}"

[ -d "${data}" ] && remove_data_dir ${data}
[ ! -d ${path} -a "${baserw}" -eq 0 ] && ${MKDIR_CMD} -m 0755 -p ${path}
[ ! -d ${jailsysdir}/${jname} ] && ${MKDIR_CMD} -p ${jailsysdir}/${jname}
[ -z "${mnt_start}" ] && mnt_start="0"

if [ "${mnt_start}" != "0" ]; then
	if [ ! -r "${mnt_start}" -o ! -x "${mnt_start}" ]; then
		err 1 "mnt_start script not exist or not executable: ${mnt_start}"
	fi
	[ ${quiet} -ne 1 ] && ${ECHO} "${N1_COLOR}Execute mnt_start script: ${N2_COLOR}${mnt_start}${N0_COLOR}..."
	# external mount, reset zfsfeat
	zfsfeat=0
	[ ! -d "${data}" ] && ${MKDIR_CMD} ${data}
	[ ! -d ${jailfstabdir}/${jname} ] && ${MKDIR_CMD} -p ${jailfstabdir}/${jname}
	[ ! -d ${jailsysdir}/${jname} ] && ${MKDIR_CMD} -p ${jailsysdir}/${jname}
	${mnt_start} -d ${data} -f ${jailfstabdir}/${jname} -j ${jname} -r ${jailrcconfdir} -s ${jailsysdir}/${jname}
	_ret=$?
	if [ ${_ret} -ne 0 ]; then
		err 1 "${W1_COLOR}error: ${N1_COLOR}mnt_start script failed: ${N2_COLOR}${mnt_start} -d ${data} -f ${jailfstabdir} -j ${jname} -r ${jailrcconfdir} -s ${jailsysdir}/${jname}${N0_COLOR}"
	fi
	create_fs ${data}
	[ $? -ne 0 ] && err 1 "${N1_COLOR}create_fs failed${N0_COLOR}"
else
	create_fs ${data}
	[ $? -ne 0 ] && err 1 "${N1_COLOR}create_fs failed${N0_COLOR}"
fi

[ ! -d "${data}" ] && err 1 "Can't create datadir ${data}"
[ -z "${jailsysskeldir}" ] && jailsysskeldir="${sharedir}/${platform}-${emulator}-${jail_profile}-system-skel"

if [ -d "${jailsysskeldir}" ]; then
	# we have custom skeldir. copy
	[ ${quiet} -ne 1 ] && ${ECHO} "${N1_COLOR}Applying custom skel system dir template from: ${N2_COLOR}${jailsysskeldir}${N1_COLOR}"
	${RSYNC_CMD} -a ${jailsysskeldir}/ ${jailsysdir}/${jname}/
	# local fstab ?
	[ -f "${jailsysskeldir}/fstab.local" ] && fstablocal="${jailsysskeldir}/fstab.local"
fi

system_dir="clone-local.d \
clone.d \
create.d \
facts.d \
master_create.d \
master_poststart.d \
master_poststop.d \
master_prestart.d \
master_prestop.d \
remove.d \
rename.d \
start.d \
stop.d"

for i in ${system_dir}; do
	if [ -n "${systemskeldir}" ]; then
		if [ -d "${systemskeldir}/${i}" ]; then
			${CP_CMD} -a ${systemskeldir}/${i} ${jailsysdir}/${jname}/${i} > /dev/null 2>&1
		else
			[ ! -d "${jailsysdir}/${jname}/${i}"  ] && ${MKDIR_CMD} -m 0775 -p ${jailsysdir}/${jname}/${i}
		fi
	else
		[ ! -d "${jailsysdir}/${jname}/${i}"  ] && ${MKDIR_CMD} -m 0775 -p ${jailsysdir}/${jname}/${i}
	fi
	[ -d ${jailsysdir}/${jname}/${i} ] && ${CHOWN_CMD} ${cbsduser}:${cbsduser} ${jailsysdir}/${jname}/${i}
done

## MD backend place
if [ "${mdsize}" != "0" ]; then
	conv2bytes "${mdsize}" || err 1 "conv2bytes error from ${mdsize}"
	imgbytes=${convval}
	blockcount=$(( imgbytes  / 1048576 ))
	mdimage="${jailsysdir}/${jname}/image.dat"
	${TOUCH_CMD} "${mdimage}"
	${DD_CMD} if="/dev/zero" of="${mdimage}" bs=1m count=0 seek=${blockcount} 1> /dev/null 2>&1 || err 1 "jcreate error: couldn't create the image file. ${mdimage}"
	# Attach the .img file as a memory disk.
	mdimagedevice=$( ${MDCONFIG_CMD} -a -t vnode -f "${mdimage}" )
	[ $? -eq 0 ] || err 1 "Error: Failed to mdconfig on ${mdimage}"
	${NEWFS_CMD} -j -n -U "/dev/${mdimagedevice}" 1> /dev/null 2>&1 || ${MDCONFIG_CMD} -d -u ${mdimagedevice} || err 1 "Error: Couldn't newfs the memory disk. ${mdimagedevice}"
	${MDCONFIG_CMD} -d -u ${mdimagedevice}
	# mount here
	mountmd jroot=${data} mdfile=${mdimage}
fi
## MD backend

if [ -z "${zfs_snapsrc}" ]; then
	[ "${ver}" != "empty" ] && populate_freebsd_world
fi

customskel
mkfstab

case "${ip4_addr}" in
	[Dd][Hh][Cc][Pp])
		ip4_addr=$( dhcpd )
		[ $? -eq 2 ] && log_err 1 "${W1_COLOR}${CBSD_APP} error: ${N1_COLOR}${jname}: no free IP address for DHCP in 'nodeippool', please check: ${N2_COLOR}cbsd initenv-tui${N0_COLOR}"
		${SYSRC_CMD} -qf ${temprcconf} ip4_addr="${ip4_addr}" > /dev/null
		# 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=${jname} cmd=update ip4_addr=${ip4_addr} vnc_port=0 status=1
		fi
		;;
	[Dd][Hh][Cc][Pp][vV]6)
		ip4_addr=$( dhcpdv6 )
		[ $? -eq 2 ] && log_err 1 "${W1_COLOR}${CBSD_APP} error: ${N1_COLOR}${jname}: no free IP address for DHCPv6 in 'nodeip6pool', please check: ${N2_COLOR}cbsd initenv-tui${N0_COLOR}"
		${SYSRC_CMD} -qf ${temprcconf} ip4_addr="${ip4_addr}" > /dev/null
		# 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=${jname} cmd=update ip4_addr=${ip4_addr} vnc_port=0 status=1
		fi
		;;
esac

${SYSRC_CMD} -qf ${temprcconf} arch="${arch}" > /dev/null
${SYSRC_CMD} -qf ${temprcconf} ver="${ver}" > /dev/null
${SYSRC_CMD} -qf ${temprcconf} stable="${stable}" > /dev/null

# update with normalize path
${SYSRC_CMD} -qf ${temprcconf} path="${path}" > /dev/null
${SYSRC_CMD} -qf ${temprcconf} mount_fstab="${mount_fstab}" > /dev/null
${SYSRC_CMD} -qf ${temprcconf} rcconf="${rcconf}" > /dev/null
${SYSRC_CMD} -qf ${temprcconf} data="${data}" > /dev/null

${CP_CMD} ${temprcconf} ${rcconf}

if [ -n "${fstablocal}" -a -f "${fstablocal}" ]; then
	[ ! -d ${jailfstabdir}/${jname} ] && ${MKDIR_CMD} ${jailfstabdir}/${jname}
	${CP_CMD} ${fstablocal} ${jailfstabdir}/${jname}/fstab.local
fi

# Finnaly export to SQLite
jregister jname=${jname} mode=new progress=3
res=$?

if [ ${res} -eq 0 ]; then
	. ${subrdir}/jcreate.subr

	jswmode jname=${jname} mode=maintenance comment='Jail Initialization...'

	# pkg bootstrap && user accounting
	if [ "${ver}" != "empty" ]; then
		postcreate_module_action ${jname} ${jconf}
		ret=$?
		# if ret != 0, remove jail: something wrong
		if [ ${ret} -ne 0 ]; then
			[ ${quiet} -ne 1 ] && ${ECHO}
			[ ${quiet} -ne 1 ] && ${ECHO} "${N1_COLOR}Creating ${jname} failed: ${N2_COLOR}postcreate_module_action${N0_COLOR}"
			# cleanup
			[ -f "${mount_fstab}" ] && ${RM_CMD} -f ${mount_fstab}
			jremove jname=${jname}
			exit ${ret}
		fi
	fi

	[ ${quiet} -ne 1 ] && echo
	[ ${quiet} -ne 1 ] && ${ECHO} "${N1_COLOR}To edit VM properties use: ${N2_COLOR}cbsd jconfig jname=${jname}${N0_COLOR}"
	[ ${quiet} -ne 1 ] && ${ECHO} "${N1_COLOR}To start VM use: ${N2_COLOR}cbsd jstart ${jname}${N0_COLOR}"
	[ ${quiet} -ne 1 ] && ${ECHO} "${N1_COLOR}To stop VM use: ${N2_COLOR}cbsd jstop ${jname}${N0_COLOR}"
	[ ${quiet} -ne 1 ] && ${ECHO} "${N1_COLOR}To remove VM use: ${N2_COLOR}cbsd jremove ${jname}${N0_COLOR}"
	[ ${quiet} -ne 1 ] && ${ECHO} "${N1_COLOR}For attach VM console use: ${N2_COLOR}cbsd jlogin ${jname}${N0_COLOR}"
	[ ${quiet} -ne 1 ] && echo
	[ ${quiet} -ne 1 ] && ${ECHO} "${N1_COLOR}Creating ${jname} complete: ${N2_COLOR}Enjoy!${N0_COLOR}"

	${RM_CMD} -f ${rcconf}
	[ "${mdsize}" != "0" ] && unmountmd jroot=${data}
else
	[ ${quiet} -ne 1 ] && ${ECHO}
	[ ${quiet} -ne 1 ] && ${ECHO} "${N1_COLOR}Creating ${jname} failed: ${N2_COLOR}cbsd jregister${N0_COLOR}"
	[ ${quiet} -ne 1 ] && ${ECHO} "${N1_COLOR}Please review bad config file: ${N2_COLOR}/tmp/rc.conf_${jname}${N0_COLOR}"
	${MV_CMD} ${rcconf} /tmp
	#cleanup
	[ -f "${mount_fstab}" ] && ${RM_CMD} -f ${mount_fstab}
	remove_data_dir ${data}
	exit 1
fi

# update state_time
cbsdsqlrw local UPDATE jails SET state_time="(strftime('%s','now'))" WHERE jname=\"${jname}\"

# always start jail when img_helpers exist
if [ -n "${with_img_helpers}" ]; then
	jswmode jname=${jname} mode=master comment='0'
	jstart jname=${jname} quiet=${quiet}

	for i in ${with_img_helpers}; do
		if [ ! -r "${i}" ]; then
			${ECHO} "${N1_COLOR}Helper doesn't available by path: ${N2_COLOR}${i}${N0_COLOR}"
			continue
		else
			install_and_apply_helpers ${i}
		fi
	done
fi

if [ -n "${jailnic_temp_sql}" ]; then
	[ ! -r "${jailnic_temp_sql}" ] && err 1 "${N1_COLOR}${CBSD_APP}: jailnic_temp_sql file not readable: ${N2_COLOR}${jailnic_temp_sql}${N0_COLOR}"
	[ ! -r ${jailsysdir}/${jname}/local.sqlite ] && /usr/local/bin/cbsd ${miscdir}/updatesql ${jailsysdir}/${jname}/local.sqlite ${distdir}/share/local-jailnic.schema jailnic
	# dump and import jailnic table
	_tmp_table_file=$( ${MKTEMP_CMD} )
	${ECHO} "${N1_COLOR}${CBSD_APP}: import jailnic data from: ${N2_COLOR}${jailnic_temp_sql} ${N1_COLOR}via${N2_COLOR} ${_tmp_table_file}${N1_COLOR} dump${N0_COLOR}"
	${SQLITE3_CMD} ${jailnic_temp_sql} .dump > ${_tmp_table_file}
	${SQLITE3_CMD} ${jailsysdir}/${jname}/local.sqlite "DROP table jailnic"
	${SQLITE3_CMD} ${jailsysdir}/${jname}/local.sqlite < ${_tmp_table_file}
	${RM_CMD} -f ${_tmp_table_file} ${jailnic_temp_sql} ${jailnic_temp_sql}-shm ${jailnic_temp_sql}-wal
else
	[ ! -r ${jailsysdir}/${jname}/local.sqlite ] && /usr/local/bin/cbsd ${miscdir}/updatesql ${jailsysdir}/${jname}/local.sqlite ${distdir}/share/local-jailnic.schema jailnic
	# create default record
	cbsdsqlrw ${jailsysdir}/${jname}/local.sqlite "INSERT INTO jailnic ( name,nic_order,nic_slot,nic_parent ) VALUES ( \"epairb\",\"0\",\"0\",\"auto\" )"

	if [ ${vnet} -eq 1 ]; then
		# created from cli? inherit nic_parent from interface
		cbsdsqlrw ${jailsysdir}/${jname}/local.sqlite "UPDATE jailnic SET nic_parent=\"${interface}\""
	fi
fi

# rctl
. ${distsharedir}/rctl.conf

for i in ${RCTL} ${RCTL_EXTRA}; do
	_val=
	eval _val="\$${i}"
	[ -z "{_val}" ] && continue
	#echo "INIT RCTL: jrctl jname=${jname} mode=set ${i}=${_val}"
	jrctl jname=${jname} mode=set ${i}=${_val} > /dev/null 2>&1 || true
done

if [ -n "${nic_hwaddr}" ]; then
	cbsdsqlrw ${jailsysdir}/${jname}/local.sqlite "UPDATE jailnic SET nic_hwaddr=\"${nic_hwaddr}\""
fi

# pubkey ?
if [ -n "${ci_user_pubkey}" ]; then
	# empty/linux jail test?
	${ECHO} "${N1_COLOR}${CBSD_APP}pubkey exist, enable service: ${N2_COLOR}sshd${N0_COLOR}" 2>&1
	${ECHO} "${N1_COLOR}${CBSD_APP}pubkey exist, adjust via jail rc.conf: ${N2_COLOR}PermitRootLogin=without-password${N0_COLOR}" 2>&1
	${SYSRC_CMD} -qf ${data}/etc/rc.conf sshd_flags+=" -oPermitRootLogin=without-password" sshd_enable=YES > /dev/null 2>&1
	. ${subrdir}/settings-tui-virtual.subr  # for is_valid_ssh_key
	# check for injection/strip..
	prefix=$( substr --pos=0 --len=1 --str="${ci_user_pubkey}" )
	# todo: check on ealry stage?
	if [ "${prefix}" = "/" ]; then
		# its full/realpath to file
		if [ -r "${ci_user_pubkey}" ]; then
			_keytest=$( ${GREP_CMD} -v '#' ${ci_user_pubkey} | ${GREP_CMD} . | ${HEAD_CMD} -n1 )
			if ! is_valid_ssh_key "${_keytest}"; then
				jdestroy jname=${jname} || true
				${ECHO} "${N1_COLOR}${CBSD_APP}: cloudinit: invalid ssh key from ${ci_user_pubkey} file. valid key: ssh-rsa,ssh-ed25519,ecdsa-*,ssh-dsa${N0_COLOR}"
				err 1 "${N1_COLOR}found: [${N2_COLOR}${_keytest}${N1_COLOR}]${N0_COLOR}"
			fi
		else
			jdestroy jname=${jname} || true
			err 1 "${N1_COLOR}${CBSD_APP}: cloudinit: keyfile not found: ${N2_COLOR}${ci_user_pubkey}${N0_COLOR}"
		fi
	else
		# its relative path or pubkey string itself.
		strpos --str="${ci_user_pubkey}" --search=" "
		if [ $? -ne 0 ]; then
			# not path, check syntax
			if ! is_valid_ssh_key "${ci_user_pubkey}"; then
				jdestroy jname=${jname} || true
				err 1 "${N1_COLOR}${CBSD_APP}: cloudinit: invalid ssh key: [${ci_user_pubkey}]. valid key: ssh-rsa,ssh-ed25519,ecdsa-*,ssh-dsa${N0_COLOR}"
			else
				_keytest="${ci_user_pubkey}"
			fi
		else
			# get realpath: lookup current dir and $workdir
			if [ -r "${CBSD_PWD}/${ci_user_pubkey}" ]; then
				ci_user_pubkey=$( ${REALPATH_CMD} ${CBSD_PWD}/${ci_user_pubkey} )
			elif [ -r "${workdir}/${ci_user_pubkey}" ]; then
				ci_user_pubkey=$( ${REALPATH_CMD} ${workdir}/${ci_user_pubkey} )
			else
				jdestroy jname=${jname} || true
				err 1 "${N1_COLOR}${CBSD_APP}: cloudinit: relative keyfile not found: ${N2_COLOR}${ci_user_pubkey} (in $CBSD_PWD or $workdir)"
			fi
			_keytest=$( ${GREP_CMD} -v '#' ${ci_user_pubkey} | ${GREP_CMD} . | ${HEAD_CMD} -n1 )
			if ! is_valid_ssh_key "${_keytest}"; then
				jdestroy jname=${jname} || true
				${ECHO} "${N1_COLOR}${CBSD_APP}: cloudinit: invalid ssh key from ${ci_user_pubkey} file. valid key: ssh-rsa,ssh-ed25519,ecdsa-*,ssh-dsa${N0_COLOR}"
				err 1 "${N1_COLOR}found: [${N2_COLOR}${_keytest}${N1_COLOR}]${N0_COLOR}"
			fi
		fi
	fi
	[ ! -d ${data}/root/.ssh ] && ${MKDIR_CMD} -m 0700 ${data}/root/.ssh
	echo "${_keytest}" >> ${data}/root/.ssh/authorized_keys
	${CHMOD_CMD} 0644 ${data}/root/.ssh/authorized_keys
fi

# ci_gw4?
if [ "${vnet}" = "1" -a "${ver}" != "empty" ]; then
	if [ -n "${ci_gw4}" ]; then
		if [ "${ci_gw4}" != "0" ]; then
			${SYSRC_CMD} -qf ${data}/etc/rc.conf defaultrouter="${ci_gw4}" > /dev/null 2>&1
		fi
	fi
fi

jswmode jname=${jname} mode=master comment='0'
myjid=$( cbsdsqlro local SELECT jid FROM jails WHERE jname=\"${jname}\" 2>/dev/null )
[ -z "${myjid}" ] && myjid=0

# sysrc: post-create sysrc(8) for any jail rc.conf customization
[ -n "${osysrc}" ] && sysrc="${osysrc}"
if [ -n "${sysrc}" ]; then
	/bin/sh <<EOF
	${SYSRC_CMD} -qf ${data}/etc/rc.conf ${sysrc} > /dev/null 2>&1
EOF
fi

# autostart asap upon jail created
[ "${runasap}" = "1" -a ${myjid} -eq 0 ] && jstart jname=${jname} quiet=${quiet}

myjid=$( cbsdsqlro local SELECT jid FROM jails WHERE jname=\"${jname}\" 2>/dev/null )
[ -z "${myjid}" ] && myjid=0

if [ ${myjid} -eq 0 ]; then
	data_status=0
else
	data_status=1
fi

if [ "${etcupdate_init}" = "1" ]; then
	# bootstrap for version
	etcupdate jname=${jname} mode=extract default_obtain_etcupdate_method=index ver=${ver} arch=${arch} target_arch=${target_arch}
fi

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="${jname}" cmd=jcreate status=2 data_status=${data_status}
fi

end_time=$( ${DATE_CMD} +%s )
diff_time=$(( end_time - st_time ))
if [ "${mod_cbsd_redis_enabled}" = "YES" -a -z "${MOD_CBSD_REDIS_DISABLED}" ]; then
	cbsdredis publish cbsd_events '{"cmd":"jcreate", "node":"'${nodename}'", "jail":"'${jname}'", "status":0, "ip4_addr":"'${ip4_addr}'","duration":'${diff_time}'}'
fi
diff_time=$( displaytime ${diff_time} )
[ ${quiet} -ne 1 ] && ${ECHO} "${N1_COLOR}${CBSD_APP} done ${N2_COLOR}in ${diff_time}${N0_COLOR}"
cbsdlogger NOTICE ${CBSD_APP}: jail ${jname} has been created in ${diff_time}

# create.d hook
geniplist	${ip4_addr}			# for ipvX_first_public-like vars
export_jail_data_for_external_hook
external_exec_master_script "master_create.d"
external_exec_script -s create.d -a

if [ ${removejconf} = "1" ]; then
	${RM_CMD} -f ${temprcconf} ${jconf} > /dev/null 2>&1
fi

exit 0
