#!/usr/local/bin/cbsd
#v12.1.10
MYARG="jname out"
MYOPTARG="ip6wa epair fbsd is_stop quiet"
MYDESC="Make jailv2 config file"
ADDHELP="ip6wa - ip6 work-around (sleep 3 seconds) enabled\n\
epair - this is mandatory for vnet=1 type jail. Sets for jail vnic, separated by ','\n\
quiet= 0,1: be quiet, dont output verbose message\n"

. ${subrdir}/nc.subr
. ${cbsdinit}

[ -z "${quiet}" ] && quiet=1

# fill $interface variable by physical NIC
get_iface_by_ip()
{
	local ip

	if [ -n "${1}" ]; then
		ip="${1}"
	else
		ip="0.0.0.0"
	fi

	# autodetect correct interface
	if [ "${interface}" = "auto" ]; then
		interface=$( getnics-by-ip ip=${ip} skip=bridge )
	fi
}

# MAIN
readconf jail-freebsd-default.conf

JAILRCCONF="${ftmpdir}/rc.conf_${jname}"
jmkrcconf jname=${jname} > ${JAILRCCONF}
. ${JAILRCCONF}
init_jail_path
${RM_CMD} -f ${JAILRCCONF}

if [ ${vnet} -eq 1 -a -z "${epair}" ]; then
	[ ${quiet} -ne 1 ] && ${ECHO} "${N1_COLOR}Error: Jail is ${N2_COLOR}vnet${N1_COLOR}-feature enabled, but epair is not set.${N0_COLOR}"
	[ ${quiet} -ne 1 ] && ${ECHO} "${N1_COLOR}vnet feature disabled!${N0_COLOR}"
	vnet=0
fi

if [ "${vnet}" = "1" -a "${vimage_feature}" = "0" ]; then
	[ ${quiet} -ne 1 ] && ${ECHO} "${N1_COLOR}Jail ${N2_COLOR}${jname}${N1_COLOR} have vnet=1 flags but your kernel is not support VIMAGE${N0_COLOR}"
	[ ${quiet} -ne 1 ] && ${ECHO} "${N1_COLOR}Please recompile kernel with: ${N2_COLOR}options VIMAGE${N0_COLOR}"
	vnet=0
fi

# sign of zfs attach inside jail: we need special route for this case
# remove orphaned sign if exist: then touched it by mountfstab script
with_zfs_attach="${jailsysdir}/${jname}/with_zfs_attach"

[ ${baserw} -eq 1 ] && path=${data}

#rewrite from cbsd zero to null
[ "${exec_start}" = "0" ] && unset exec_start
[ "${exec_stop}" = "0" ] && unset exec_stop
[ "${exec_poststart}" = "0" ] && unset exec_poststart
[ "${exec_poststop}" = "0" ] && unset exec_poststop
[ "${exec_prestart}" = "0" ]  && unset exec_prestart
[ "${exec_prestop}" = "0" ] && unset exec_prestop
[ "${interface}" = "0" ] && unset interface
[ "${exec_consolelog}" = "0" ] && unset exec_consolelog

[ -z "${fbsd}" ] && fbsd=0
[ -z "${is_stop}" ] && is_stop=0

special_ips=$( is_special_ip ${ip4_addr} )
is_special=$?

if [ ${is_special} -eq 0 ]; then
	if [ "${interface}" = "auto" ]; then
		geniplist ${ip4_addr}
		for pureip in ${IPS}; do
			iface=$( getnics-by-ip ip=${pureip} )
			ipwmask ${pureip}
			if [ -n "${iface}" ]; then
				[ ${quiet} -ne 1 ] && ${ECHO} "${N1_COLOR}Default NIC automatically selected: ${N2_COLOR}${iface}${N0_COLOR}"
				interface="${iface}"
				break
			else
				err 1 "${N1_COLOR}Can't determine interfaces for: ${N2_COLOR}${pureip}${N0_COLOR}"
			fi
		done
	fi
else
	iptype ${ip4_addr}	# todo: multiple records support
	if [ -n "${V_INTERFACE}" -a -n "${V_IP}" ]; then
		if [ "${interface}" = "auto" ]; then
			[ ${quiet} -ne 1 ] && ${ECHO} "${N1_COLOR}Skip for interface=auto: NIC specified as part of ${N2_COLOR}ip_addr${N1_COLOR} string${N0_COLOR}"
		fi
	elif [ -n "${VHID}" ]; then
		[ ${quiet} -ne 1 ] && ${ECHO} "${N1_COLOR}CARP records detected, VHID: ${N2_COLOR}${VHID}${N0_COLOR}"
	fi
fi

# ip6 work around: sleep for 3 seconds in prestart
[ "${ip6wa}" = "1" ] && exec_prestart="sleep 3; ${exec_prestart}"

# special route for ZFS-attach and VNET-based jail: manage exec_start
#attach and detach ZFS mount inside jail
if [ "${fbsd}" -eq 1 ]; then
	# ZFS
	if [ "${allow_zfs}" = "1" ]; then
		# with_zfs_attach managed by mountfstab
		if [ -r ${with_zfs_attach} ]; then
			exec_poststop="/usr/local/bin/cbsd detachzfs fstab=${_zfsfstab} jname=${jname}; ${exec_poststop}"
			# we need special route for this case: real exec_start command
			# will be executed via jstart after attachzfs script. See late_start in jstart.
			# also we need persist mode
			[ ${quiet} -ne 1 ] && ${ECHO} "${N1_COLOR}persist mode: ${N2_COLOR}on${N0_COLOR}"
			exec_start=
			persist=1
		fi
	fi

	# VNET
	# rename interface only for FreeBSD jail, not for Linux jail
	# since it overwrite exec_start, must follow 
	# after "attach and detach ZFS mount inside jail" where 
	# exec_start variables is cleaned
	if [ "${vnet}" = "1" ]; then
		# we need special route for this case: real exec_start command
		# will be executed via jstart after ifconfig vnet initialization. See late_start in jstart.
		# also we need persist mode
		exec_start=
		persist=1

		if [ -n "${epair}" ]; then
			my_epair_ifaces=
			OIFS="${IFS}"
			IFS=","
			eth_seq=0
			for i in ${epair}; do
				[ -z "${i}" ] && continue
				if [ -z "${my_epair_ifaces}" ]; then
					my_epair_ifaces="${i}"
				else
					my_epair_ifaces="${my_epair_ifaces},${i}"
				fi
				eth_seq=$(( eth_seq + 1 ))
			done
			IFS="${OIFS}"
		fi
	fi
fi

#exec.clean is dangerous with infinity exec.timeout and exec.stop and external password backend (login_getpwclass with broken ldap for example)

${CAT_CMD} > ${out} << EOF
${jname} {
exec.start = "${exec_start}";
exec.stop = "${exec_stop}";
exec.poststart="${exec_poststart}";
exec.poststop="${exec_poststop}";
exec.prestart="${exec_prestart}";
exec.prestop="${exec_prestop}";
host.hostname = "${host_hostname}";
path = "${path}";
allow.socket_af;
allow.chflags;
EOF

if [ "${allow_raw_sockets}" = "1" ]; then
	${CAT_CMD} >> ${out} << EOF
allow.raw_sockets;
EOF
fi

# IPC SYSV part
if [ ${freebsdhostversion} -gt 1100104 ]; then
	[ -n "${sysvsem}" ] && ${CAT_CMD} >> ${out} << EOF
sysvsem = "${sysvsem}";
EOF
	[ -n "${sysvshm}" ] && ${CAT_CMD} >> ${out} << EOF
sysvshm = "${sysvshm}";
EOF
	[ -n "${sysvmsg}" ] && ${CAT_CMD} >> ${out} << EOF
sysvmsg = "${sysvmsg}";
EOF
fi

ALIAS4=""
ALIAS6=""

# gen ipX.addr params
if [ ${vnet} -eq 0 ]; then
	# when vnet we not need ipX.addr
	OLDIFS="${IFS}"
	IFS=","
	for a in ${ip4_addr}; do
		unset IWM VHID
		iptype ${a}
		_inet=$?
		[ ${_inet} -eq 0 ] && continue

		# this is carp records
		if [ -n "${VHID}" ]; then

			if [ ${is_stop} -eq 1 ]; then
				# Do not insert any information about the interface/CARP IP
				# in jconf when is_stop=1 ( jail stop )
				# CARP IP should not be removed even if there is
				# interface != disable. Just show notes how to
				# destroy ip manually
				IFS=" "
				[ ${quiet} -ne 1 ] && ${ECHO} "${BOLD}Notes: ${N1_COLOR}The CARP address of the interface we don't remove as it is the shared IP${N0_COLOR}"
				[ ${quiet} -ne 1 ] && ${ECHO} "${N1_COLOR}If you want to remove CARP IP of current jail, use: ${N0_COLOR}"
				[ ${quiet} -ne 1 ] && ${ECHO} "${N2_COLOR}cbsd carpcfg mode=unset vhid=${VHID} ip=${IPWVHID}${N0_COLOR}"
				IFS=","
				continue
			fi

			IFS="${OLDIFS}"
			carpcfg mode=getip vhid=${VHID}
			vhid_set=$?

			if [ ${vhid_set} -eq 1 ]; then
				[ ${quiet} -ne 1 ] && ${ECHO} "${N1_COLOR}carpcfg getip failed: please configure CARP VHID first: ${N2_COLOR}cbsd vhidcfg-tui${N0_COLOR}"
				IFS=","
				continue
			fi

			a=$( carpcfg inter=0 mode=show vhid=${VHID} ip=${IWM} )
			_res=$?
			if [ ${_res} -ne 0 ]; then
				err 1 "${N1_COLOR}Error in CARP area from makejconf: ${a}${N0_COLOR}"
				IFS=","
				continue
			fi
		elif [ -n "${V_INTERFACE}" ]; then
			a="${V_INTERFACE}|${V_IP}"
		else

			if [ "${platform}" = "DragonFly" ]; then
				case ${_inet} in
					1)
						${IFCONFIG_CMD} ${interface} ${a}/32 alias
						;;
					2)
						err 1 "IPv6 DFLY unsupported yet"
						;;
				esac
			fi

			if [ -n "${interface}" ]; then
				get_iface_by_ip ${IWM}
				a="${interface}|${a}"
			fi
		fi

		case ${_inet} in
			1)
				if [ -z "$ALIAS4" ]; then
					echo "ip4.addr = \"${a}\";" >> ${out}
					[ "${mkhostsfile}" != "0" ] && mkjhosts ips="${IWM}" file="${data}/etc/hosts" hosts="${host_hostname}"
					ALIAS4="1"
				else
					[ "${mkhostsfile}" != "0" ] && mkjhosts ips="${IWM}" file="${data}/etc/hosts" hosts="${host_hostname}"
					echo "ip4.addr += \"${a}\";" >> ${out}
				fi
				;;
			2)
				if [ -z "$ALIAS6" ]; then
					echo "ip6.addr = \"${a}\";" >> ${out}
					ALIAS6="1"
				else
					echo "ip6.addr += \"${a}\";" >> ${out}
				fi
				;;
		esac
		IFS=","
	done
	IFS="${OLDIFS}"
else
	# vnet
	cat >> ${out} <<EOF
vnet = new;
#vnet = inherit;
# vnet.interface doesn't support more than one ifaces
# disable in favor exec_stop/start action
#vnet.interface = "${my_epair_ifaces}";
EOF
fi

if [ "$devfs" = "NO" ]; then
	echo "mount.nodevfs;" >> ${out}
else
	echo "mount.devfs;" >> ${out}
	if [ ! -d "${data}/dev" ]; then
		${MKDIR_CMD} "${data}/dev"
		[ ${quiet} -ne 1 ] && ${ECHO} "${N1_COLOR}makejconf: mount.devfs: ${N2_COLOR}${data}/dev${N1_COLOR} dir was created${N0_COLOR}"
	fi
fi

[ -n "${devfs_ruleset}" ] && echo "devfs_ruleset=\"${devfs_ruleset}\";" >> $out

if [ "${allow_mount}" = "1" ]; then
	echo "allow.mount = \"true\";" >> ${out}
	echo "enforce_statfs=\"1\";" >>${out}

	if [ "${allow_devfs}" = "1" ]; then
		echo "allow.mount.devfs = \"true\";" >> ${out}
	fi

	if [ "${allow_nullfs}" = "1" ]; then
		echo "allow.mount.nullfs = \"true\";" >> ${out}
	fi

	# https://svnweb.freebsd.org/base?view=revision&revision=r336565
	# For FreeBSD 11.2+
	# todo: determine elf after MFC in 11-STABLE
	if [ ${freebsdhostversion} -ge 1200074 ]; then
		if [ "${allow_fusefs}" = "1" ]; then
			if ! ${KLDSTAT_CMD} -qm fusefs >/dev/null 2>&1; then
				[ ${quiet} -ne 1 ] && ${ECHO} "${W1_COLOR}Warning: ${N2_COLOR}allow_fusefs${N1_COLOR} sets to '1', but ${N2_COLOR}fuse.ko${N1_COLOR} module is not loaded${N0_COLOR}"
				[ ${quiet} -ne 1 ] && ${ECHO} "${W1_COLOR}Warning: ${N1_COLOR}Please load ${N2_COLOR}fuse${N1_COLOR} module. ${N2_COLOR}allow.mount.fusefs${N1_COLOR} currently not working: ${N2_COLOR}${jname}${N0_COLOR}"
			else
				echo "allow.mount.fusefs = \"true\";" >> ${out}
			fi
		fi
	fi

	if [ "${allow_fdescfs}" = "1" ]; then
		echo "allow.mount.fdescfs = \"true\";" >> ${out}
	fi

	# check for Linux ABI
	# this feature available for FreeBSD 10.3+
	if [ "${allow_linsysfs}" = "1" ]; then
		echo "allow.mount.linsysfs = \"true\";" >> ${out}
	fi
	if [ "${allow_linprocfs}" = "1" ]; then
		echo "allow.mount.linprocfs = \"true\";" >> ${out}
	fi
fi

# this feature available for FreeBSD 15.0+
if [ ${freebsdhostversion} -gt 1500039 ]; then
	if [ "${allow_suser}" = "1" ]; then
		echo "allow.suser = \"1\";" >> ${out}
	else
		echo "allow.suser = \"0\";" >> ${out}
	fi

	if [ "${allow_extattr}" = "1" ]; then
		echo "allow.extattr = \"1\";" >> ${out}
	else
		echo "allow.extattr = \"0\";" >> ${out}
	fi

	if [ "${allow_adjtime}" = "1" ]; then
		echo "allow.adjtime = \"1\";" >> ${out}
	else
		echo "allow.adjtime = \"0\";" >> ${out}
	fi

	if [ "${allow_settime}" = "1" ]; then
		echo "allow.settime = \"1\";" >> ${out}
	else
		echo "allow.settime = \"0\";" >> ${out}
	fi
fi

if [ "${allow_reserved_ports}" = "1" ]; then
	echo "allow.reserved_ports = \"true\";" >> ${out}
else
	echo "allow.reserved_ports = \"false\";" >> ${out}
fi

if [ "${allow_mlock}" = "1" ]; then
	echo "allow.mlock = \"1\";" >> ${out}
else
	echo "allow.mlock = \"0\";" >> ${out}
fi

# allow.nfsd
nfs_feat=$( ${SYSCTL_CMD} -qn kern.features.nfsd 2>/dev/null )
if [ "${nfs_feat}" = "1" ]; then
	# this feature available for FreeBSD 14.0+
	if [ ${freebsdhostversion} -gt 1400090 ]; then
		if [ "${allow_nfsd}" = "1" ]; then
			echo "allow.nfsd = \"1\";" >> ${out}
		else
			echo "allow.nfsd = \"0\";" >> ${out}
		fi
	fi
fi

# this feature available for FreeBSD 13.0+
if [ ${freebsdhostversion} -gt 1300005 ]; then
	if [ "${allow_unprivileged_proc_debug}" = "1" ]; then
		echo "allow.unprivileged_proc_debug = \"true\";" >> ${out}
	else
		echo "allow.unprivileged_proc_debug = \"false\";" >> ${out}
	fi
fi

if [ -n "${exec_timeout}" ]; then
	echo "exec.timeout = \"${exec_timeout}\";" >> ${out}
fi

if [ -n "${exec_fib}" ]; then
	echo "exec.fib = \"${exec_fib}\";" >> ${out}
fi

if [ -n "${stop_timeout}" ]; then
	echo "stop.timeout = \"${exec_timeout}\";" >> ${out}
fi

if [ "${mount_fdescfs}" = "1" ]; then
	echo "mount.fdescfs = \"true\";" >> ${out}
fi
if [ "${mount_procfs}" = "1" ]; then
	echo "mount.procfs = \"true\";" >> ${out}
fi

#  kernel version spoofing support on FreeBSD 10.2+
#  https://svnweb.freebsd.org/base?view=revision&revision=r279361
if [ ${freebsdhostversion} -gt 1001510 ]; then
	orig_ver=$( ${miscdir}/elf_tables --freebsdver /bin/sh )

	readconf buildworld.conf
	. ${subrdir}/universe.subr

	init_target_arch
	init_supported_arch
	init_basedir

	if [ -f "${BASE_DIR}/bin/sh" ]; then
		base_osreldate=$( ${miscdir}/elf_tables --ver ${BASE_DIR}/bin/sh )
		base_ver=$( ${miscdir}/elf_tables --freebsdver ${BASE_DIR}/bin/sh )
		[ -n "${base_osreldate}" ] && echo "osreldate = \"${base_osreldate}\";" >> ${out}

		if [ "${orig_ver}" != "${base_ver}" ]; then
			base_osrelease="${ver}-RELEASE"
			echo "osrelease = \"${base_osrelease}\";" >> ${out}
		fi
	fi
fi

# https://cgit.freebsd.org/src/commit/?id=ed31b3f4a146b3aa38f416954b6dbffad02efa5d
#if [ -n "${allow_dying}" ]; then
#	echo "allow.dying = \"${allow_dying}\";" >> ${out}
#fi

if [ -n "${allow_procfs}" ]; then
	echo "allow.mount.procfs= \"${allow_procfs}\";" >> ${out}
fi

# this feature only available on FreeBSD 10.0 BETA3+
if [ ${freebsdhostversion} -gt 1000500 -a -n "${allow_tmpfs}" ]; then
	echo "allow.mount.tmpfs= \"${allow_tmpfs}\";" >> ${out}
fi

# this feature only available on FreeBSD 12.0-RC1
if [ ${freebsdhostversion} -gt 1200085 -a -n "${allow_read_msgbuf}" ]; then
	case ${allow_read_msgbuf} in
		0)
			echo "allow.read_msgbuf=\"false\";" >> ${out}
			;;
		1)
			echo "allow.read_msgbuf=\"true\";" >> ${out}
			;;
	esac
fi

if [ ${freebsdhostversion} -gt 1200085 -a -n "${allow_vmm}" ]; then
	case ${allow_vmm} in
#		skip settings
#		0)
#			echo "allow.vmm=\"false\";" >> ${out}
#			;;
		1)
			if ! ${KLDSTAT_CMD} -qm vmm >/dev/null 2>&1; then
				[ ${quiet} -ne 1 ] && ${ECHO} "${W1_COLOR}Warning: ${N2_COLOR}allow_vmm${N1_COLOR} sets to '1', but ${N2_COLOR}vmm.ko${N1_COLOR} module is not loaded${N0_COLOR}"
				[ ${quiet} -ne 1 ] && ${ECHO} "${W1_COLOR}Warning: ${N1_COLOR}Please load ${N2_COLOR}vmm${N1_COLOR} module. ${N2_COLOR}allow.vmm${N1_COLOR} currently not working: ${N2_COLOR}${jname}${N0_COLOR}"
			else
				echo "allow.vmm=\"true\";" >> ${out}
			fi
			;;
	esac
fi

if [ -n "${allow_zfs}" ]; then
	echo "allow.mount.zfs= \"${allow_zfs}\";" >> ${out}
fi

_ret=$( ${SYSCTL_CMD} -n security.jail.dev_io_access 2>/dev/null )
if [ -n "${_ret}" -a -n "${allow_kmem}" ]; then
	echo "allow.dev_io_access= \"${allow_kmem}\";" >> ${out}
	echo "allow.dev_dri_access= \"${allow_kmem}\";" >> ${out}
fi

if [ -n "${exec_consolelog}" ]; then
	[ "${exec_consolelog}" = "1" ] && exec_consolelog="${logdir}/${jname}.log"
	echo "exec.consolelog= \"${exec_consolelog}\";" >> ${out}
fi

if [ "${persist}" = "1" ]; then
	echo "persist;" >> ${out}
fi

if [ -n "${childrenmax}" ]; then
	echo "children.max = \"${childrenmax}\";" >> ${out}
fi

if [ -n "${enforce_statfs}" ]; then
	case "${allow_mount}" in
		0)
			echo "enforce_statfs = \"${enforce_statfs}\";" >> ${out}
			;;
		1)
			if [ ${enforce_statfs} -ge 2 ]; then
				[ ${quiet} -ne 1 ] && ${ECHO} "${N1_COLOR}Skipp for ${N2_COLOR}enforce_statfs=${enforce_statfs}${N1_COLOR}: ${N2_COLOR}allow_mount${N1_COLOR} require enforce_statfs value lower than 2${N0_COLOR}"
			else
				echo "enforce_statfs = \"${enforce_statfs}\";" >> ${out}
			fi
			;;
	esac
fi

echo "}" >> ${out}

exit 0
