#!/usr/local/bin/cbsd
#v11.1.16
globalconf="${workdir}/cbsd.conf";
MYARG="jname out"
MYOPTARG="ip6wa epair fbsd is_stop"
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"

set -e
. ${globalconf}
set +e

. ${subr}
. ${tools}

init $*

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


# MAIN
readconf jail-freebsd-default.conf

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

if [ ${vnet} -eq 1 -a -z "${epair}" ]; then
	${ECHO} "${MAGENTA}Error: Jail is ${GREEN}vnet${MAGENTA}-feature enabled, but epair is not set.${NORMAL}"
	${ECHO} "${MAGENTA}vnet feature disabled!${NORMAL}"
	vnet=0
fi

if [ "${vnet}" = "1" -a "${vimage_feature}" = "0" ]; then
	${ECHO} "${MAGENTA}Jail ${GREEN}${jname}${MAGENTA} have vnet=1 flags but your kernel is not support VIMAGE${NORMAL}"
	${ECHO} "${MAGENTA}Please recompile kernel with: ${GREEN}options VIMAGE${NORMAL}"
	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

if [ "${interface}" = "auto" ]; then
	geniplist ${ip4_addr}
	for pureip in ${IPS}; do
		iface=$( getnics-by-ip ip=${pureip} )
		ipwmask ${pureip}
		if [ -n "$iface" ]; then
			${ECHO} "${MAGENTA}NIC automatically selected: ${GREEN}${iface}${NORMAL}"
			interface=${iface}
			break
		else
			err 1 "${MAGENTA}Can't determine interfaces for: ${GREEN}${pureip}${NORMAL}"
		fi
	done
fi

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

# attach and detach ZFS mount inside jail
if [ "${fbsd}" -eq 1 -a "${allow_zfs}" = "1" ]; then
	for _zfsfstab in "${jailfstabdir}/${jailfstabpref}${jname}" "${jailfstabdir}/${jailfstabpref}${jname}.local"; do
		[ ! -f "${_zfsfstab}" ] && continue
		ret=0
		/usr/bin/egrep -v "^#" "${_zfsfstab}" | /usr/bin/grep -q ' zfs '
		ret=$?
		if [ ${ret} -eq 0 ]; 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. Also we need persist mode
			exec_start=""
			persist=1
		fi
	done
fi

# 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" -a -n "${epair}" -a ${fbsd} -eq 1 ]; then
	# we need special route for this case: real exec_start command
	# will be executed via jstart after ifconfig vnet initialization. Also we need persist mode
	exec_start=""
	persist=1

	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
		#exec_start="/sbin/ifconfig ${i} vnet ${jname}; /sbin/ifconfig ${i} name eth${eth_seq} up; ${exec_start}"
		#if [ -n "${exec_stop}" ]; then
		#	exec_stop="${exec_stop}; /sbin/ifconfig ${i} -vnet ${jname};"
		#else
		#	exec_stop="/sbin/ifconfig ${i} -vnet ${jname};"
		#fi
		eth_seq=$(( eth_seq + 1 ))
	done
	IFS="${OIFS}"
fi

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

/bin/cat > ${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 >> ${out} << EOF
allow.raw_sockets;
EOF
fi

case "${allow_sysvipc}" in
	new)
		if [ ${freebsdhostversion} -gt 1100104 ]; then
			cat >> ${out} << EOF
sysvsem = "new";
sysvshm = "new";
sysvmsg = "new";
EOF
		else
			cat >> ${out} << EOF
allow.sysvipc;
EOF
fi
	;;
	inherit)
		if [ ${freebsdhostversion} -gt 1100104 ]; then
			cat >> ${out} << EOF
sysvsem = "inherit";
sysvmsg = "inherit";
sysvmsg = "inherit";
EOF
		else
			cat >> ${out} << EOF
allow.sysvipc;
EOF
fi
	;;
	*)
	;;
esac

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=" "
				${ECHO} "${BOLD}Notes: ${MAGENTA}The CARP address of the interface we don't remove as it is the shared IP${NORMAL}"
				${ECHO} "${MAGENTA}If you want to remove CARP IP of current jail, use: ${NORMAL}"
				${ECHO} "${GREEN}cbsd carpcfg mode=unset vhid=${VHID} ip=${IPWVHID}${NORMAL}"
				IFS=","
				continue
			fi

			cbsd carpcfg mode=getip vhid=${VHID} > /dev/null 2>&1

			vhid_set=$?

			[ ${vhid_set} -eq 1 ] && continue

			a=$( carpcfg inter=0 mode=show vhid=${VHID} ip=${IWM} )
			_res=$?
			if [ ${_res} -ne 0 ]; then
				err 1 "${MAGENTA}Error in CARP area from makejconf: ${a}${NORMAL}"
				continue
			fi
		else
			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
	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
		/bin/mkdir "${data}/dev"
		${ECHO} "${MAGENTA}makejconf: mount.devfs: ${GREEN}${data}/dev${MAGENTA} dir was created${NORMAL}"
	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 ! /sbin/kldstat -qm fuse >/dev/null 2>&1; then
				${ECHO} "${LRED}Warning: ${GREEN}allow_fusefs${MAGENTA} sets to '1', but ${GREEN}fuse.ko${MAGENTA} module is not loaded${NORMAL}"
				${ECHO} "${LRED}Warning: ${MAGENTA}Please load ${GREEN}fuse${MAGENTA} module. ${GREEN}allow.mount.fusefs${MAGENTA} currently not working: ${GREEN}${jname}${NORMAL}"
			else
				echo "allow.mount.fusefs = \"true\";" >> ${out}
			fi
		fi
	fi

	# this feature available for FreeBSD 10.2+
	_res=$( /sbin/sysctl -q security.jail.mount_fdescfs_allowed >/dev/null 2>&1 )
	if [ $? -ne 1 ]; then
		if [ "${allow_fdescfs}" = "1" ]; then
			echo "allow.mount.fdescfs = \"true\";" >> ${out}
		fi
	fi

	# this feature available for FreeBSD 10.3+
	_res=$( /sbin/sysctl -q security.jail.param.allow.mount.linsysfs >/dev/null 2>&1 )
	[ $? -ne 1 ] && echo "allow.mount.linsysfs = \"true\";" >> ${out}

	# this feature available for FreeBSD 10.3+
	_res=$( /sbin/sysctl -q security.jail.param.allow.mount.linprocfs >/dev/null 2>&1 )
	[ $? -ne 1 ] && echo "allow.mount.linprocfs=\"1\";" >> ${out}
fi

# this feature available for FreeBSD 12.0+
if [ ${freebsdhostversion} -gt 1200043 ]; then
	if [ "${allow_reserved_ports}" = "1" ]; then
		echo "allow.reserved_ports=true;" >> ${out}
	else
		echo "allow.reserved_ports=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

# this features only available on FreeBSD 10.0 BETA3+
if [ ${freebsdhostversion} -gt 1000500 -a -n "${mount_fdescfs}" ]; then
	echo "mount.fdescfs = \"${mount_fdescfs}\";" >> ${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
	. ${workdir}/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


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

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

_ret=$( /sbin/sysctl -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
				${ECHO} "${MAGENTA}Skipp for ${GREEN}enforce_statfs=${enforce_statfs}${MAGENTA}: ${GREEN}allow_mount${MAGENTA} require enforce_statfs value lower than 2${NORMAL}"
			else
				echo "enforce_statfs = \"${enforce_statfs}\";" >> ${out}
			fi
			;;
	esac
fi

echo "}" >> ${out}
