#!/usr/local/bin/cbsd
# shellcheck shell=sh
# shellcheck disable=SC2154,SC2034,SC1090,SC1091,SC3043,SC2166,SC2086
# v13.0.8
MYARG=""
# should be in sync with run_jail() func: tools/up script
MYOPTARG="autorestart ci_gw4 ci_gw42 ci_interface2 ci_interface_mtu ci_interface_mtu2 ci_ip4_addr ci_ip4_addr2 \
ci_user_pubkey customskel delpkglist environment etcupdate_init flavor from fstablocal inter interface2 jconf \
jprofile nic2_flags nic_flags pkg_bootstrap pkglist quiet removejconf runasap sysrc zfs_snapsrc"
# 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.

To use CARP without VNET with jail ( in 'vhid_X#<IP_ADDR> form' for ip4_addr= ), please
use 'cbsd vhidcfg-tui' first.

If the 'buildah' package is installed on the system, you can use the OCI images 
(including Linux) from the official docker registry - see the examples section 
with the [*] marker.

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.
 If the container is running and any parameter has been changed, the container will be restarted.
 Mostly useful for management system (Ansible/Puppet/Salt) which generate the container template.

If you use 'zfs_encryption' settings, you may want to customize 'zfs_always_unload_key' settings
via ~cbsd/etc/zfs.conf (globally) or ~cbsd/jails-system/<jail>/etc/zfs.conf (per jail) to unload
key to unload the key every time the container stops.

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

To get available 'jprofile' profiles list, just checkout 
  ~cbsd/etc/defaults/jail-freebsd-XXXX.conf files, e.g.:

 ls ~cbsd/etc/defaults/ | grep '^jail-freebsd-' | sed 's/jail-freebsd-//g;s/.conf//g'

${H3_COLOR}  Environment Variables${N0_COLOR}:

Environment variables are stored in the ~cbsd/jails-system/\$jname directory in files:

  - environment ( usually this file is copied from system SKEL directory )
  - environment.local ( for custom user's env )

If you use an environment= arguments, these values are added to ~cbsd/jails-system/\$jname/environment file 


${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}emulator=${N0_COLOR}       - specify emulator engine (e.g. for qemu-user mode or linuxulator;
 ${N2_COLOR}environment${N0_COLOR}     - pass environment, e.g.: 'environment=\"FOO=bar\" environment=\"VAR1=boo\"'
                   or path to 'env' file;
 ${N2_COLOR}etcupdate_init=${N0_COLOR} - 1(enable),0(disable) for etcupdate init (overwrite config values).
 ${N2_COLOR}flavor${N0_COLOR}          - Use flavor (named group of vm_cpus/vm_ram/imgsize): see 'cbsd vm-packages';
 ${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}interface=${N0_COLOR}      - <name>:  specify jail interface. Use 'ppt-XX' prefix (+vnet=1) to 
                   pass XX as vnet interface instead of 'epair', e.g.: 'ppt-em0'.
                   Warning! the PPT interface will disappear from the host system while the container is running.
 ${N2_COLOR}jprofile=${N0_COLOR}       - <name>:  specify jail profile for creating jail.
 ${N2_COLOR}jnameserver=${N0_COLOR}    - <IP>: override default 'jnameserver' settings for 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}ver=${N0_COLOR}            - Can be: '13.1' for RELEASE, '13' for RELENG, 'auto' - inherits base version,
                   or 'empty' for empty dataset.
 ${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 interface=ppt-em
 # 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_bookworm allow_raw_sockets=1 environment=\"VAR1=var1\" environment=\"VAR2=var2\"
 # cbsd jcreate jname=ubu jprofile=ubuntu_jammy allow_raw_sockets=1
 # cbsd jcreate jname=rock jprofile=rocky_9 allow_raw_sockets=1
 # cbsd jcreate jname=dev jprofile=devuan_daedalus 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=nictest vnet=1 interface=ppt-em0
 # cbsd jcreate jname=xx ver=14.2 vnet=1 sysrc=\"ifconfig_eth0+='mtu 1450' inetd_enable=YES\"
 # cbsd jcreate jname=vmagent from=https://dl.convectix.com/img/amd64/amd64/14.2/vmagent/vmagent.img pkg_bootstrap=0 runasap=1
 # cbsd jcreate jname=myapp from=fbbb4e8707f6794008cc6e8ed0d86082 runasap=1
 # cbsd jcreate jname=small flavor=small1 runasap=1 jnameserver=\"8.8.8.8,8.8.4.4\"
 #[*] cbsd jcreate jname=test ver=empty baserw=1 pkg_bootstrap=0 floatresolv=0 applytpl=0 etcupdate_init=0 from=docker.io/convectix/freebsd14-base
 #[*] cbsd jcreate jname=test ver=empty baserw=1 pkg_bootstrap=0 floatresolv=0 applytpl=0 etcupdate_init=0 from=docker.io/library/alpine emulator=linux
 #[*] cbsd jcreate jname=influx ip4_addr=DHCP platform=Linux from=docker.io/library/influxdb:2.7 environment=\"INFLUXD_INIT_PORT=9099\" environment=\"INFLUXD_INIT_PING_ATTEMPTS=600\" environment=\"DOCKER_INFLUXDB_INIT_MODE=setup\" environment=\"DOCKER_INFLUXDB_INIT_USERNAME=my-user\" environment=\"DOCKER_INFLUXDB_INIT_PASSWORD=my-password\" environment=\"DOCKER_INFLUXDB_INIT_ORG=my-org\" environment=\"DOCKER_INFLUXDB_INIT_BUCKET=my-bucket\" environment=\"DOCKER_INFLUXDB_INIT_RETENTION=1w\" environment=\"DOCKER_INFLUXDB_INIT_ADMIN_TOKEN=my-super-secret-auth-token\"
 #[*] cbsd jcreate jname=influx ip4_addr=DHCP platform=Linux from=docker.io/library/influxdb:2.7 exec_start=\"influxd &\"
 #[*] cbsd jcreate jname=redis ip4_addr=DHCP platform=Linux from=docker.io/library/redis
 #[*] cbsd jcreate jname=memcached ip4_addr=DHCP platform=Linux from=docker.io/library/memcached environment=\"MEMCACHED_MEMORY_LIMIT=512m\" exec_start=\"memcached -u root &\"


Nice example HOW TO create micro-jail (~8MB, 'busybox' like) with SSH root access:

 # cbsd jcreate jname=micro1 baserw=1 ver=empty applytpl=0
 # cbsd copy-binlib basedir=/ chaselibs=1 dstdir=/usr/jails/jails-data/micro1-data filelist=/usr/local/cbsd/share/FreeBSD-microjail.txt.xz
 # cbsd sysrc jname=micro1 sshd_flags=\"-oUseDNS=no -oPermitRootLogin=yes\" sshd_enable=YES
 # cp -a /etc/ssh /usr/jails/jails-data/micro1-data/etc/
 # cp -a /etc/gss /usr/jails/jails-data/micro1-data/etc/
 # cp -a /etc/pam.d /usr/jails/jails-data/micro1-data/etc/
 # mkdir -p /usr/jails/jails-data/micro1-data/var/empty /usr/jails/jails-data/micro1-data/var/log /usr/jails/jails-data/micro1-data/var/run /usr/jails/jails-data/micro1-data/root /usr/jails/jails-data/micro1-data/dev
 # chmod 0700 /usr/jails/jails-data/micro1-data/var/empty
 # pw -R /usr/jails/jails-data/micro1-data usermod root -s /bin/sh

${H3_COLOR}See also${N0_COLOR}:

 cbsd jconstruct --help
 cbsd jconstruct-tui --help
 cbsd up --help
 cbsd images --help
 cbsd jset --help
 cbsd makeresolv --help
 cbsd vhidcfg --help
 cbsd vhidcfg-tui --help

"
CBSDMODULE="jail"
EXTHELP="wf_jcreate"


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

ci_user_pubkey=
sysrc=
from=
flavor=
oflavor=
ver=
over=
oplatform=
oenvironment=
environment=

# hack to avoid conflict with global jnameserver
ojnameserver="${jnameserver}"
. ${cbsdinit}

# hack to avoid conflict with global jnameserver
if [ "${ojnameserver}" = "${jnameserver}" ]; then
	# no jnameserver args: inherits jnameserver by default
	jnameserver="0"
	ojnameserver="0"
else
	ojnameserver="${jnameserver}"
fi

# if some of params specified via args, store them as temporary vars
for i in ${MYOPTARG}; do
	case "${i}" in
		jnameserver)
			# hack to avoid conflict with global jnameserver
			# do not init ojnameserver
			[ "${jnameserver}" = "0" ] && continue
			;;
		*)
			unset o${i}
			eval "o${i}=\$$i"
	esac
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
	[ -n "${oremovejconf}" ] && removejconf="${oremovejconf}"
	[ -z "${removejconf}" ] && removejconf=1
	/usr/local/cbsd/misc/cbsdsysrc -qf ${jconf} removejconf="${removejconf}" > /dev/null 2>&1
	. ${cbsdinit}
	unset CBSD_INIT_SAVE2FILE
fi

[ -n "${ojnameserver}" ] && jnameserver="${ojnameserver}"

[ -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}/home /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

		if [ "${platform}" = "DragonFly" ]; then
			# DFLY use OBJDIR=/usr/obj by default (which we can't overwrite via /etc/make.conf for some reason?)
			${CAT_CMD} >> ${mount_fstab} <<EOF
${data}/usr/obj /usr/obj ${NULLFS} rw 0 0
EOF
		fi
	fi

	${TOUCH_CMD} ${mount_fstab}.local
}

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

	local module
	# shellcheck disable=SC2016
	# $1 is expanded by awk
	module=$( cbsdsqlro "${_srcfile}" "SELECT helpername FROM system ORDER BY helpername ASC" | ${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
# map flavor
[ -n "${oflavor}" ] && flavor="${oflavor}"
if [ -n "${flavor}" ]; then
	unset ocpu cpu ovmemoryuse vmemoryuse ofsquota fsquota
	flavors_available=$( cbsdsqlro ${dbdir}/local.sqlite "SELECT name FROM vmpackages ORDER BY name ASC" | ${XARGS_CMD} )
	flavor_exist=0
	for i in ${flavors_available}; do
		[ "${flavor}" = "${i}" ] && flavor_exist=1 && break
	done

	[ ${flavor_exist} -eq 0 ] && log_err 1 "${N1_COLOR}${CBSD_APP}: no such flavor [${flavor}], available flavors: ${N2_COLOR}${flavors_available}${N0_COLOR}"

	_res=$( cbsdsqlro ${dbdir}/local.sqlite "SELECT pkg_vm_cpus,pkg_vm_ram,pkg_vm_disk FROM vmpackages WHERE name='${flavor}' LIMIT 1" 2>/dev/null )

	[ -z "${_res}" ] && log_err 1 "${N1_COLOR}${CBSD_APP}: unable to get vm_cpus/vm_ram/imgsize for flavor: ${N2_COLOR}${flavor}${N0_COLOR}"
	sqllistdelimer="|"

	sqllist "${_res}" ocpu ovmemoryuse ofsquota
	unset sqllistdelimer
	${ECHO} "${N1_COLOR}${CBSD_APP}: flavor ${flavor} -> cpu/vmemoryusem/fsquota: ${N2_COLOR}[${_res}]${N0_COLOR}"

	[ -z "${ovmemoryuse}" ] && log_err 1 "${N1_COLOR}${CBSD_APP}: unable to get 'vmemoryuse' for flavor: ${N2_COLOR}${flavor}${N0_COLOR}"
	[ -z "${ofsquota}" ] && log_err 1 "${N1_COLOR}${CBSD_APP}: unable to get 'fsquota' for flavor: ${N2_COLOR}${flavor}${N0_COLOR}"
	[ -z "${ocpu}" ] && log_err 1 "${N1_COLOR}${CBSD_APP}: unable to get 'cpu' for flavor: ${N2_COLOR}${flavor}${N0_COLOR}"

	cpu="${ocpu}"
	vmemoryuse="${ovmemoryuse}"
	fsquota="${ofsquota}"

	unset flavor oflavor flavor_exist flavors_available _res
fi

. ${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

[ -n "${ver}" ] && over="${ver}"

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
	# shellcheck disable=SC2064
	# we need expansion right now
	trap "${RM_CMD} -f ${temprcconf} ${jconf}" HUP INT ABRT BUS TERM EXIT
else
	# shellcheck disable=SC2064
	trap "${RM_CMD} -f ${temprcconf}" HUP INT ABRT BUS TERM EXIT
fi

## environment manage
xenvironment=
for i in ${*}; do
	strpos --str="${i}" --search="="
	_pos=$?

	if [ ${_pos} -ne 0 ]; then
		_arg_len=$( strlen ${i} )
		_pref=$(( _arg_len - _pos ))
		ARG=$( substr --pos=0 --len=${_pos} --str="${i}" )

		case "${ARG}" in
			environment)
				VAL=$( substr --pos=$(( _pos + 2 )) --len=${_pref} --str="${i}" | ${TR_CMD} -d '"' )
				if [ -z "${xenvironment}" ]; then
					xenvironment="${VAL}"
				else
					xenvironment="${xenvironment} ${VAL}"
					fi
				;;
		esac
		shift
		continue
	fi
done

# 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 \
	|| 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 md5 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=$( ${miscdir}/cbsd_md5 "${from}" )
			_res=$( cbsdsqlro images "SELECT md5 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 ...
		[ ${quiet} -ne 1 ] && ${ECHO} "${N1_COLOR}${CBSD_APP}: image not exist, call 'images mode=register': ${N2_COLOR}${from}${N0_COLOR}"
		if [ "${emulator}" != "jail" ]; then
			images mode=register path="${from}" platform="${emulator}"
			ret=$?
		else
			[ -n "${oplatform}" ] && platform="${oplatform}"
			images mode=register path="${from}" platform="${platform}"
			ret=$?
		fi
		ret=$?
		if [ ${ret} -ne 0 ]; then
			stderr 1 "${N1_COLOR}${CBSD_APP}: images failed (${ret}: cbsd images mode=register from=\"${N2_COLOR}${from}${N1_COLOR}\"${N0_COLOR}"
		fi
		tmpfrom=$( ${miscdir}/cbsd_md5 "${from}" )
		_res=$( cbsdsqlro images "SELECT md5 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
			[ ${quiet} -ne 1 ] && ${ECHO} "${N1_COLOR}${CBSD_APP}: create snapshot: ${N2_COLOR}${_zfssrc}@start${N0_COLOR}"
			${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}"
		else
			[ ${quiet} -ne 1 ] && ${ECHO} "${N1_COLOR}${CBSD_APP}: create snapshot: ${N2_COLOR}${_zfssrc}@start${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}" ver=empty baserw=1 pkg_bootstrap=0 floatresolv=0 applytpl=0 etcupdate_init=0
		jail_created=1
	else
		[ ! -d "${workdir}/basejail/${from_md5}" ] && err 1 "${N1_COLOR}${CBSD_APP}: no such resources: ${from_md5}: ${N2_COLOR}${workdir}/basejail/${from_md5}${N0_COLOR}"
		jcreate jname="${jname}" baserw=1 pkg_bootstrap=0 ver=empty etcupdate_init=0 floatresolv=0 applytpl=0
		. ${subrdir}/rcconf.subr
		[ $? -eq 1 ] && err 1 "${N1_COLOR}${CBSD_APP}: unable to create jail ${jname} from: ${N2_COLOR}${from}${N0_COLOR}"
		if [ ! "${data}" ]; then
			jremove jname="${jname}" > /dev/null 2>&1 || true
			err 1 "${N1_COLOR}${CBSD_APP}: no such data directory: ${N2_COLOR}${data}${N0_COLOR}"
		fi
		${RSYNC_CMD} -a --hard-links --links --acls --xattrs --numeric-ids --recursive --partial --delete ${workdir}/basejail/${from_md5}/ ${data}/
	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}

	if [ -n "${from_md5}" -a -n "${BUILDAH_CMD}" ]; then
		${BUILDAH_CMD} --root ${workdir}/basejail/buildah images -n | while read _path _tag _image_id _rest; do
			_md5_ver=$(${miscdir}/cbsd_md5 "${_path}:${_tag}")
			_md5_nover=$(${miscdir}/cbsd_md5 "${_path}")
			if [ "${from_md5}" = "${_md5_ver}" -o "${from_md5}" = "${_md5_nover}" ]; then

				# sh -c docker-entrypoint.sh redis-server & ??
				# sh -c /entrypoint.sh influxd & ??

				_cmd=$( ${BUILDAH_CMD} --root ${workdir}/basejail/buildah inspect ${_image_id} | ${JQ_CMD} -r '.OCIv1.config | ( [(.Cmd) | join(" ")]) | map("" + . + "") | join(" ")' 2>/dev/null )

				#_env=$( ${BUILDAH_CMD} --root ${workdir}/basejail/buildah inspect ${_image_id} | ${JQ_CMD} -r '.OCIv1.config | ( [(.Env) | join(" ")]) | map("" + . + "") | join(" ")' )
				_env=$( ${BUILDAH_CMD} --root ${workdir}/basejail/buildah inspect ${_image_id} | ${JQ_CMD} -r '.OCIv1.config | ( [(.Env) | join("\n")]) | map("" + . + "") | join(" ")' 2>/dev/null )

#				${BUILDAH_CMD} --root ${workdir}/basejail/buildah inspect ${_image_id} | ${JQ_CMD} -r '.OCIv1.config | ( [(.Env) | join("\n")]) | map("" + . + "") | join(" ")' > ${workdir}/jails-system/${jname}/environment

				_entrypoint=$( ${BUILDAH_CMD} --root ${workdir}/basejail/buildah inspect ${_image_id} | ${JQ_CMD} -r '.OCIv1.config | ( [(.Entrypoint) | join(" ")]) | map("" + . + "") | join(" ")' 2>/dev/null )

#				_exec_start=$(${BUILDAH_CMD} --root ${workdir}/basejail/buildah inspect ${_image_id} \
#					| ${JQ_CMD}  -r '.OCIv1.config | (.Env + ["sh", "-c"] + [(.Entrypoint + .Cmd + ["&"]) | join(" ")]) | map("\"" + . + "\"") | join(" ")' \
#				)
#				_exec_start=$(${BUILDAH_CMD} --root ${workdir}/basejail/buildah inspect ${_image_id} \
#					| ${JQ_CMD}  -r '.OCIv1.config | (.Env + ["sh", "-c"] + [(.Cmd + ["&"]) | join(" ")]) | map("\"" + . + "\"") | join(" ")' \
#				)

				if [ -n "${_env}" ]; then
					echo "${_env}" > ${workdir}/jails-system/${jname}/environment
				fi

				_exec_start=
				if [ -n "${_entrypoint}" ]; then
					if [ -r "${workdir}/jails-data/${jname}-data${_entrypoint}" ]; then
						_entrypoint="${_entrypoint}"
					elif [ -r "${workdir}/jails-data/${jname}-data/entrypoint.sh" ]; then
						_entrypoint="/entrypoint.sh"
					elif [ -r "${workdir}/jails-data/${jname}-data/usr/local/bin/docker-entrypoint.sh" ]; then
						_entrypoint="/usr/local/bin/docker-entrypoint.sh"
					else
						echo "entrypoint not found: ${workdir}/jails-data/${jname}-data${_entrypoint}"
						unset _entrypoint
					fi
				fi

				if [ -n "${_entrypoint}" ]; then
					_exec_start="${_entrypoint}"
					echo "entrypoint found: ${_entrypoint}"
				fi

				if [ -n "${_cmd}" ]; then
					if [ -n "${_exec_start}" ]; then
						_exec_start="${_exec_start} ${_cmd}"
					else
						_exec_start="${_cmd}"
					fi
				fi

				[ -z "${_exec_start}" ] && err 1 "no entrypoint or cmd"

				jset jname=${jname} exec_start="${_exec_start} &" exec_stop="/bin/kill -TERM -1"
				break
			fi
		done
	fi

	#echo "PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin:/root/bin" > ${jailsysdir}/${jname}/environment
	if [ -n "${xenvironment}" ]; then
		# save env
		for i in ${xenvironment}; do
			echo "${i}" >> ${jailsysdir}/${jname}/environment
		done
	fi

	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


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

[ -n "${over}" ] && ver="${over}"
[ -n "${ojnameserver}" -a "${ojnameserver}" != "0" ] && jnameserver="${ojnameserver}"

# map 'native' to ver/arch
. ${subrdir}/build.subr

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

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

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

#re-init rctl_ vars
#[ -n "${fsquota}" ] && rctl_fsquota="${fsquota}"
#old
#. ${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

if ! jstatus jname="${jname}" > /dev/null 2>&1; 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}/traffic ] && ${MKDIR_CMD} -p ${jailsysdir}/${jname}/traffic
${TOUCH_CMD} ${jailsysdir}/${jname}/descr

[ -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}" || err 1 "${N1_COLOR}create_fs failed${N0_COLOR}"
else
	create_fs "${data}" || err 1 "${N1_COLOR}create_fs failed${N0_COLOR}"
fi

[ ! -d "${data}" ] && err 1 "Can't create datadir ${data}"

# inherit /var/db/pkg for base-in-pkg bases
if [ -d "${BASE_DIR}/var/db/pkg" ]; then
	[ -d "${data}/var/db/pkg" ] && ${RM_CMD} -rf "${data}/var/db/pkg"
	[ ! -d "${data}/var/db" ] && ${MKDIR_CMD} -p ${data}/var/db
	${CP_CMD} -a "${BASE_DIR}/var/db/pkg" ${data}/var/db/
fi

[ -z "${jailsysskeldir}" ] && jailsysskeldir="${sharedir}/${platform}-${emulator}-${jail_profile}-system-skel"

if [ -d "${jailsysskeldir}" ]; then
	[ ${quiet} -ne 1 ] && ${ECHO} "${N1_COLOR}${CBSD_APP}: copy system skel from: ${N2_COLOR}${jailsysskeldir}${N0_COLOR}"
	# we have custom skeldir. copy
	[ ${quiet} -ne 1 ] && ${ECHO} "${N1_COLOR}Applying custom skel system dir template from: ${N2_COLOR}${jailsysskeldir}${N0_COLOR}"
	${RSYNC_CMD} -a ${jailsysskeldir}/ ${jailsysdir}/${jname}/
	# local fstab ?
	[ -f "${jailsysskeldir}/fstab.local" ] && fstablocal="${jailsysskeldir}/fstab.local"
else
	[ ${quiet} -ne 1 ] && ${ECHO} "${N1_COLOR}${CBSD_APP}: system skel dir not found: ${N2_COLOR}${jailsysskeldir}${N0_COLOR}"
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 [ ! -d "${jailsysdir}/${jname}/${i}" ]; then
		${MKDIR_CMD} -m 0775 -p ${jailsysdir}/${jname}/${i}
	fi
	${CHOWN_CMD} -R ${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}"
	# Linux does not support postfix in bs=, e.g. bs=1m
	case "${platform}" in
		Linux)
			${DD_CMD} if="/dev/zero" of="${mdimage}" bs=1000000 count=0 seek=${blockcount} 1> /dev/null 2>&1 || err 1 "jcreate error: couldn't create the image file. ${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}"
			;;
	esac
	# Attach the .img file as a memory disk.
	mdimagedevice=$( ${MDCONFIG_CMD} -a -t vnode -f "${mdimage}" ) \
	|| 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
	case "${platform}" in
		Linux)
			true
			;;
		*)
			[ "${ver}" != "empty" ] && populate_freebsd_world
			;;
	esac
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}"
		/usr/local/cbsd/misc/cbsdsysrc -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}"
		/usr/local/cbsd/misc/cbsdsysrc -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

/usr/local/cbsd/misc/cbsdsysrc -qf ${temprcconf} \
	arch="${arch}" \
	ver="${ver}" \
	stable="${stable}" \
	path="${path}" \
	mount_fstab="${mount_fstab}" \
	rcconf="${rcconf}" \
	data="${data}" \
	jnameserver="${jnameserver}" >/dev/null

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

if [ -n "${fstablocal}" ]; then
	# relativepath?
	prefix=$( substr --pos=0 --len=1 --str="${fstablocal}" )
	if [ "${prefix}" != "/" ]; then
		fstablocal=$( ${REALPATH_CMD} "${CBSD_PWD}/${fstablocal}" )
	else
		fstablocal=$( ${REALPATH_CMD} "${fstablocal}" )
	fi
	if [ -r "${fstablocal}" ]; then
		[ ! -d ${jailfstabdir}/${jname} ] && ${MKDIR_CMD} ${jailfstabdir}/${jname}
		${CP_CMD} ${fstablocal} ${jailfstabdir}/${jname}/fstab.local
	else
		err 1 "${W1_COLOR}Warning: fstablocal set but file not accesible: ${N2_COLOR}${fstablocal}${N0_COLOR}"
	fi
fi

#echo "PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin:/root/bin" > ${jailsysdir}/${jname}/environment
if [ -n "${xenvironment}" ]; then
	# save env
	for i in ${xenvironment}; do
		echo "${i}" >> ${jailsysdir}/${jname}/environment
	done
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

	tmp_type=${interface%%-*}		# cut all after '#'
	tmp_iface=${interface##*-}		# cut all before '#'

	case "${tmp_type}" in
		ppt|epair|ovs)
			cbsdsqlrw ${jailsysdir}/${jname}/local.sqlite "INSERT INTO jailnic ( name,nic_order,nic_slot,nic_parent ) VALUES ( '${tmp_iface}','0','0','auto' )"
			;;
		*)
			cbsdsqlrw ${jailsysdir}/${jname}/local.sqlite "INSERT INTO jailnic ( name,nic_order,nic_slot,nic_parent ) VALUES ( 'epairb','0','0','auto' )"
			;;
	esac

	unset tmp_iface= tmp_type=

	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
	/usr/local/cbsd/misc/cbsdsysrc -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.
		if ! strpos --str="${ci_user_pubkey}" --search=" "; 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
	_check_ip=0
	case "${ip4_addr}" in
		[Rr][Ee][Aa][Ll][Dd][Hh][Cc][Pp])
			/usr/local/cbsd/misc/cbsdsysrc -qf ${data}/etc/rc.conf ifconfig_eth0="DHCP" > /dev/null 2>&1
			;;
		[Dd][Hh][Cc][Pp])
			_check_ip=1
			ip4_addr=$( dhcpd )
			_ret=$?
			case "${_ret}" in
				0)
					true
					;;
				1)
					${ECHO} "${W1_COLOR}${CBSD_APP} dhcp error, please check: ${N2_COLOR}cbsd dhcpd${N0_COLOR}"
					;;
				2)
					${ECHO} "${W1_COLOR}${CBSD_APP} dhcp error: pools are exhausted (no free IPs in 'nodeippool'?, please check: ${N2_COLOR}cbsd dhcpd${N0_COLOR}"
					;;
			esac
			;;
		[Dd][Hh][Cc][Pp][vV]6)
			_check_ip=1
			ip4_addr=$( dhcpdv6 )
			_ret=$?
			case "${_ret}" in
				0)
					true
					;;
				1)
					${ECHO} "${W1_COLOR}${CBSD_APP} dhcp error, please check: ${N2_COLOR}cbsd dhcpd${N0_COLOR}"
					;;
				2)
					${ECHO} "${W1_COLOR}${CBSD_APP} dhcp error: pools are exhausted (no free IPs in 'nodeippool'?, please check: ${N2_COLOR}cbsd dhcpd${N0_COLOR}"
					;;
			esac
			;;
		*)
			;;
	esac

	if [ ${_check_ip} -eq 1 ]; then
		iptype ${ip4_addr}
			_ret=$?
			case "${_ret}" in
				0)
					${ECHO} "${W1_COLOR}${CBSD_APP} dhcp error, unknown IP type: ${N2_COLOR}cbsd dhcpd${N0_COLOR}"
					;;
				1)
					/usr/local/cbsd/misc/cbsdsysrc -qf ${data}/etc/rc.conf ifconfig_eth0="inet ${ip4_addr} up" > /dev/null 2>&1
					jset jname=${jname} ip4_addr="${ip4_addr}"
					;;
				2)
					/usr/local/cbsd/misc/cbsdsysrc -qf ${data}/etc/rc.conf ifconfig_eth0="inet6 ${ip4_addr} up" > /dev/null 2>&1
					jset jname=${jname} ip4_addr="${ip4_addr}"
					;;
			esac
	fi

	if [ -n "${ci_gw4}" ]; then
		if [ "${ci_gw4}" != "0" ]; then
			/usr/local/cbsd/misc/cbsdsysrc -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
	/usr/local/cbsd/misc/cbsdsysrc -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

# 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 ))
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

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

exit 0
