#!/usr/local/bin/cbsd
#v12.1.10
MYARG="jname"
CBSDMODULE="jail"
MYDESC="Modify parameters for jail"
ADDHELP="\

${H3_COLOR}Description${N0_COLOR}:

You can change container options using arguments or config file.
Some parameters can be applied while the container is running, 
but most will require a container restart.

You can change multiple parameters at one time separated by space.

For interactive (dialog(1)-based) edit, please use 'cbsd jconfig' instead.

${H3_COLOR}Options${N0_COLOR}:

 ${N2_COLOR}autorestart=${N0_COLOR} - auto-restart online jail after params update.
 ${N2_COLOR}mode=${N0_COLOR}         - set 'force' for modification on the running jail.
 ${N2_COLOR}jconf=${N0_COLOR}        - <path_to_file>, load and set settings from jconf.

${H3_COLOR}Examples${N0_COLOR}:

 # cbsd jset vnet=1 jname=myjail
 # cbsd jset jname='*' ver=13.0 allow_raw_sockets=1

${H3_COLOR}See also${N0_COLOR}:

  cbsd jget --help
  cbsd jconfig --help

"

EXTHELP="wf_jset"

. ${subrdir}/nc.subr

[ ! -f ${distsharedir}/jail-arg ] && err 1 "${N2_COLOR}no jail-arg file${N0_COLOR}";
. ${distsharedir}/jail-arg


MYOPTARG="autorestart jconf ${JARG} nice"

. ${tools}
. ${strings}
. ${cbsdinit}

emulator="jail"		# for jname_is_multiple
jname_is_multiple

if [ -n "${jail_list}" ]; then
	new_arg=

	for i in $*; do
		_is_jname=$( substr --pos=0 --len=5 --str=${i} )
		[ "${_is_jname}" = "jname" ] && continue
		new_arg="${new_arg} ${i}"
	done

	for jname in ${jail_list}; do
		jset jname=${jname} ${new_arg}
	done
	exit 0
fi

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

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

need_for_restart=0

# todo:
# this code duplicates in settings-tui.subr in get_construct_ip4_addr
# with small difference (dialog). merge into one
validate_ipaddr()
{
	[ -z "${ip4_addr}" ] && return 1

	# Yes, we wrote in the example that the valid separator between NIC and address
	# is '#' and not '|' as experienced jail.conf users can get used to.
	# But we will still try to convert the input with '|' to correct form
	strpos --str="${ip4_addr}" --search="|"
	_pos=$?
	 [ ${_pos} -ne 0 ] && ip4_addr=$( echo ${ip4_addr} | ${TR_CMD} '|' '#' )

	. ${subrdir}/vnet.subr	# for is_nic_exist
	for _i in ${ip4_addr}; do
		ipwmask ${_i}
		[ -z "${IWM}" -o "${_i}" = "0" ] && continue

		iptype ${IWM}
		if [ $? -ne 0 ]; then
			if [ -n "${V_INTERFACE}" -a -n "${V_IP}" ]; then
				if ! is_nic_exist -n ${V_INTERFACE}; then
					log_err 1 "${N1_COLOR}jset for ${jname} error: interface not exist: ${N2_COLOR}${V_INTERFACE}${N0_COLOR}"
				fi
				_myip="${_myip} ${V_IP}"
			else
				_myip="${_myip} ${IWM}"
			fi
		fi
	done

	# check for other jail
	IFS="|"
	_i=0
	eval $( cbsdsqlro local SELECT jname,ip4_addr FROM jails WHERE ip4_addr!="0" 2>/dev/null | while read _jname _ips; do
		echo jname${_i}=\"$_jname\"
		echo ips${_i}=\"${_ips}\"
		_i=$(( _i + 1 ))
	done )
	unset IFS

	for _i in $( ${SEQ_CMD} 0 255 ); do
		_ok=1
		unset _existing_ipjail _existing_ip
		eval _jname="\$jname$_i"
		[ -z "${_jname}" ] && break
		# skip for myself
		[ "${_jname}" = "${jname}" ] && continue

		eval _existing_ipjail="\$ips$_i"
		[ -z "${_existing_ipjail}" ] && break

		_existing_ipjail=$( echo ${_existing_ipjail} | ${TR_CMD} "," " " )

		for _x in ${_existing_ipjail}; do
			case "${_x}" in
				[Dd][Hh][Cc][Pp]|[Dd][Hh][Cc][Pp][Vv]6)
					continue
					;;
			esac
			ipwmask ${_x}
			[ -z "${IWM}" ] && continue
			iptype ${IWM}
			[ $? -eq 1 ] && _existing_ip="${_existing_ip} ${IWM}"
		done

		[ -z "${_existing_ip}" ] && continue

		for _x in ${_existing_ip}; do
			for _j in ${_myip}; do
				[ "${_x}" = "${_j}" ] && _ok=0 && break
			done
			[ ${_ok} -eq 0 ] && break
		done

		if [ ${_ok} -eq 0 ]; then
			${ECHO} "${W1_COLOR}jset warning: ${N1_COLOR}IP address ${N2_COLOR}${_j}${N1_COLOR} already assigned to jail: ${N2_COLOR}${_jname}${N0_COLOR}"
		fi
	done # check for local jail end
}


# jid and jname must be set
modify_ipaddr()
{
	local _A _ip4_addr_old _pureip _inet _face _interface _IP4 _IP6 _MODIF _nIPs IFS _dhcp
	local _is_freebsd _elftest

	_A=$( cbsdsqlro local "SELECT interface,ip4_addr,applytpl,vnet,baserw,data,host_hostname FROM jails WHERE jname=\"${jname}\"" )
	sqllist "${_A}" _interface _ip4_addr_old applytpl vnet baserw data host_hostname

	_is_freebsd=1

	# todo: check by elf?
	[ ! -r ${data}/etc/rc.conf ] && _is_freebsd=0

#	if [ ${baserw} -eq 1 ]; then
#		_elftest=${data}/bin/sh
#	else		
#		elftest="${BASE_DIR}/bin/sh"
#	fi
#	[ -f "${elftest}" ] && osname=$( ${miscdir}/elf_tables --osname ${elftest} )

	[ ${_is_freebsd} -ne 1 ] && empty=1

	if [ "${_interface}" = "0" ]; then
		${ECHO} "${argpart}: ${N1_COLOR}on-the-fly currently disabled because option interface is "0"${N0_COLOR}"
		cbsdlogger NOTICE ${CBSD_APP}: modify_ipaddr: on-the-fly currently disabled because option interface is 0
		return 0
	fi

	if [ "${empty}" = "1" ]; then
		${ECHO} "${argpart}: ${N1_COLOR}on-the-fly disabled because jail option empty is "1"${N0_COLOR}"
		cbsdlogger NOTICE ${CBSD_APP}: modify_ipaddr: on-the-fly disabled because jail option empty is 1
		return 0
	fi

	# extra check
	validate_ipaddr

	IFS=","

	for _pureip in ${_ip4_addr_old}; do
		iptype ${_pureip}
		_inet=$?
		if [ ${vnet} -eq 1 ]; then
			_iface="eth0"
		else
			_iface=$( getnics-by-ip ip=${_pureip} skip=bridge )
		fi
		ipwmask ${_pureip}
		if [ -n "${IWM}" -a "${IWM}" != "0" ]; then
			case ${_inet} in
				1)
					_MODIF="inet"
					;;
				2)
					_MODIF="inet6"
					;;
			esac
			IFS=" "
			${ECHO} "${N1_COLOR}Remove old IP: ${N2_COLOR}${IFCONFIG_CMD} ${_iface} ${_MODIF} ${IWM} -alias${N0_COLOR}"
			cbsdlogger NOTICE ${CBSD_APP}: modify_ipaddr: remove old IP: ${IFCONFIG_CMD} ${_iface} ${_MODIF} ${IWM} -alias
			if [ ${vnet} -eq 1 ]; then
				jexec jname=${jname} ${IFCONFIG_CMD} ${_iface} ${_MODIF} ${IWM} -alias >/dev/null 2>/dev/null
			else
				${IFCONFIG_CMD} ${_iface} ${_MODIF} ${IWM} -alias >/dev/null 2>/dev/null
			fi
			IFS=","
		fi
	done

	_IP4=
	_IP6=

	for _pureip in ${ip4_addr}; do
		case "${_pureip}" in
			[Dd][Hh][Cc][Pp])
				_dhcp=$( dhcpd )
				if [ $? -eq 2 ]; then
					cbsdlogger WARNING ${CBSD_APP}: modify_ipaddr: No free IP address for DHCP in nodeippool
					err 1 "${N1_COLOR}No free IP address for DHCP in nodeippool${N0_COLOR}"
				fi
				_pureip="${_dhcp}"
				;;
			[Dd][Hh][Cc][Pp][Vv][6])
				_dhcp=$( dhcpdv6 )
				if [ $? -eq 2 ]; then
					cbsdlogger WARNING ${CBSD_APP}: modify_ipaddr: No free IPv6 address for DHCP in nodeippool
					err 1 "${N1_COLOR}No free IPv6 address for DHCP in nodeippool${N0_COLOR}"
				fi
				# normalize IPv6 to compressed form
				eval $( ${miscdir}/sipcalc ${_dhcp} )
				_dhcp="${_compressed_ipv6_address}"
				_pureip="${_dhcp}"
				;;
		esac
		iptype ${_pureip}
		_inet=$?
		if [ ${vnet} -eq 1 ]; then
			_iface="eth0"
		else
			_iface=$( getnics-by-ip ip=${_pureip} skip=bridge )
		fi
		ipwmask ${_pureip}
		if [ -n "${IWM}" ]; then
			case ${_inet} in
				1)
					_MODIF="inet"
					if [ -z "${_IP4}" ]; then
						_IP4="${_pureip}"
					else
						_IP4="${_IP4},${_pureip}"
					fi
					;;
				2)
					_MODIF="inet6"
					if [ -z "${_IP6}" ]; then
						_IP6="${_pureip}"
					else
						_IP6="${_IP6},${_pureip}"
					fi
					;;
			esac
			if [ "${mkhostsfile}" != "0" ]; then
				[ "${IWM}" != "0" ] && mkjhosts ips="${IWM}" file="${data}/etc/hosts" hosts="${host_hostname}"
			fi
			IFS=" "
			${ECHO} "${N1_COLOR}Setup new IP: ${N2_COLOR}${IFCONFIG_CMD} ${_iface} ${_MODIF} ${_pureip} alias${N0_COLOR}"
			cbsdlogger NOTICE ${CBSD_APP}: modify_ipaddr: setup new IP: ${IFCONFIG_CMD} ${_iface} ${_MODIF} ${_pureip} alias
			if [ ${vnet} -eq 1 ]; then
				jexec jname=${jname} ${IFCONFIG_CMD} ${_iface} ${_MODIF} ${_pureip} alias
			else
				${IFCONFIG_CMD} ${_iface} ${_MODIF} ${_pureip} alias
			fi
			IFS=","
		fi
	done

	IFS=" "

	# replace old value by IP
	if [ -n "${_dhcp}" ]; then
		ip4_addr=$( echo ${ip4_addr} | ${SED_CMD} -e "s#DHCPv6#${_dhcp}#g" -e "s#DHCP#${_dhcp}#g" )
	fi

	#construct ipX.addr string 
	_nIPs="ip4.addr=${_IP4} ip6.addr=${_IP6}"

	cbsdlogger NOTICE ${CBSD_APP}: modify_ipaddr: ${JAIL_CMD} -m ${_nIPs} jid=${jid}
	if [ ${vnet} -eq 1 ]; then
		# alias for multiple? IPv6 ? restart routing ? [auto] restart services ?
		data="${jaildatadir}/${jname}-${jaildatapref}"
		${SYSRC_CMD} -qf ${data}/etc/rc.conf ifconfig_eth0="inet ${ip4_addr} up" > /dev/null 2>&1
		jexec jname=${jname} /etc/rc.d/routing restart
	else
		${JAIL_CMD} -m ${_nIPs} jid=${jid}
	fi

	cbsdsqlrw local UPDATE jails SET ${i}=\"${ip4_addr}\" WHERE jname=\"${jname}\"
	${ECHO} "${argpart}: ${N1_COLOR}${ip4_addr}${N0_COLOR}"

	if [ "${mod_cbsd_queue_enabled}" = "YES" -a -z "${MOD_CBSD_QUEUE_DISABLED}" ]; then
		[ -n "${cbsd_jail_queue_name}" ] && ${cbsd_queue_backend} cbsd_queue_name=${cbsd_jail_queue_name} id="${jname}" cmd=update ip4_addr="${ip4_addr}" status=1
	fi
}

# jid must be set
modify_allow_mount()
{
	cbsdsqlrw local "UPDATE jails SET ${i}=\"${allow_mount}\" WHERE jname=\"${jname}\""
	${JAIL_CMD} -m allow.mount=${allow_mount} jid=${jid}
	${ECHO} "${argpart}: ${N1_COLOR}${allow_mount}${N0_COLOR}"
}

# jid must be set
modify_allow_raw_sockets()
{
	cbsdsqlrw local "UPDATE jails SET ${i}=\"${allow_raw_sockets}\" WHERE jname=\"${jname}\""
	${JAIL_CMD} -m allow.raw_sockets=${allow_raw_sockets} jid=${jid}
	${ECHO} "${argpart}: ${N1_COLOR}${allow_raw_sockets}${N0_COLOR}"
}

# jid must be set
modify_allow_read_msgbuf()
{
	cbsdsqlrw local "UPDATE jails SET ${i}=\"${allow_read_msgbuf}\" WHERE jname=\"${jname}\""
	${JAIL_CMD} -m allow.read_msgbuf=${allow_read_msgbuf} jid=${jid}
	${ECHO} "${argpart}: ${N1_COLOR}${allow_read_msgbuf}${N0_COLOR}"
}

# jid must be set
modify_allow_vmm()
{
	cbsdsqlrw local "UPDATE jails SET ${i}=\"${allow_vmm}\" WHERE jname=\"${jname}\""
	${JAIL_CMD} -m allow.vmm=${allow_vmm} jid=${jid}
	${ECHO} "${argpart}: ${N1_COLOR}${allow_vmm}${N0_COLOR}"
}

# jid must be set
modify_allow_mlock()
{
	cbsdsqlrw local "UPDATE jails SET ${i}=\"${allow_mlock}\" WHERE jname=\"${jname}\""
	${JAIL_CMD} -m allow.mlock=${allow_vmm} jid=${jid}
	${ECHO} "${argpart}: ${N1_COLOR}${allow_mlock}${N0_COLOR}"
}

# jid must be set
modify_allow_nullfs()
{
	local allow_mount

	cbsdsqlrw local "UPDATE jails SET ${i}=\"${allow_nullfs}\" WHERE jname=\"${jname}\""
	${JAIL_CMD} -m allow.mount.nullfs=${allow_nullfs} jid=${jid}

	${ECHO} "${argpart}: ${N1_COLOR}${allow_nullfs}${N0_COLOR}"
	[ ${allow_nullfs} -eq 0 ] && return 0

	# we need to force allow_mount too when allow_tmpfs sets to 1 and allow_mount is zero
	allow_mount=$( cbsdsqlro local "SELECT allow_mount FROM jails WHERE jname=\"${jname}\"" )
	if [ "${allow_mount}" = "0" ]; then
		cbsdsqlrw local "UPDATE jails SET allow_mount=\"1\" WHERE jname=\"${jname}\""
		${JAIL_CMD} -m allow.mount=1 jid=${jid}
		${ECHO} "allow_mount: ${N1_COLOR}1${N0_COLOR}"
	fi
}

# jid must be set
modify_allow_fusefs()
{
	local allow_mount

	cbsdsqlrw local "UPDATE jails SET ${i}=\"${allow_fusefs}\" WHERE jname=\"${jname}\""
	${JAIL_CMD} -m allow.mount.fusefs=${allow_fusefs} jid=${jid}

	${ECHO} "${argpart}: ${N1_COLOR}${allow_fusefs}${N0_COLOR}"
	[ ${allow_fusefs} -eq 0 ] && return 0

	# we need to force allow_mount too when allow_tmpfs sets to 1 and allow_mount is zero
	allow_mount=$( cbsdsqlro local "SELECT allow_mount FROM jails WHERE jname=\"${jname}\"" )
	if [ "${allow_mount}" = "0" ]; then
		cbsdsqlrw local "UPDATE jails SET allow_mount=\"1\" WHERE jname=\"${jname}\""
		${JAIL_CMD} -m allow.mount=1 jid=${jid}
		${ECHO} "allow_mount: ${N1_COLOR}1${N0_COLOR}"
	fi
}

# jid must be set
modify_allow_linprocfs()
{
	local allow_mount

	cbsdsqlrw local "UPDATE jails SET ${i}=\"${allow_linprocfs}\" WHERE jname=\"${jname}\""
	${JAIL_CMD} -m allow.mount.linprocfs=${allow_linprocfs} jid=${jid}

	${ECHO} "${argpart}: ${N1_COLOR}${allow_linprocfs}${N0_COLOR}"
	[ ${allow_linprocfs} -eq 0 ] && return 0

	# we need to force allow_mount too when allow_linprocfs sets to 1 and allow_mount is zero
	allow_mount=$( cbsdsqlro local "SELECT allow_mount FROM jails WHERE jname=\"${jname}\"" )
	if [ "${allow_mount}" = "0" ]; then
		cbsdsqlrw local "UPDATE jails SET allow_mount=\"1\" WHERE jname=\"${jname}\""
		${JAIL_CMD} -m allow.mount=1 jid=${jid}
		${ECHO} "allow_mount: ${N1_COLOR}1${N0_COLOR}"
	fi
}

# jid must be set
modify_allow_linsysfs()
{
	local allow_mount

	cbsdsqlrw local "UPDATE jails SET ${i}=\"${allow_linsysfs}\" WHERE jname=\"${jname}\""
	${JAIL_CMD} -m allow.mount.linsysfs=${allow_linsysfs} jid=${jid}

	${ECHO} "${argpart}: ${N1_COLOR}${allow_linsysfs}${N0_COLOR}"
	[ ${allow_linsysfs} -eq 0 ] && return 0

	# we need to force allow_mount too when allow_linsysfs sets to 1 and allow_mount is zero
	allow_mount=$( cbsdsqlro local "SELECT allow_mount FROM jails WHERE jname=\"${jname}\"" )
	if [ "${allow_mount}" = "0" ]; then
		cbsdsqlrw local "UPDATE jails SET allow_mount=\"1\" WHERE jname=\"${jname}\""
		${JAIL_CMD} -m allow.mount=1 jid=${jid}
		${ECHO} "allow_mount: ${N1_COLOR}1${N0_COLOR}"
	fi
}

# jid must be set
modify_allow_procfs()
{
	local allow_mount

	cbsdsqlrw local "UPDATE jails SET ${i}=\"${allow_procfs}\" WHERE jname=\"${jname}\""
	${JAIL_CMD} -m allow.mount.procfs=${allow_procfs} jid=${jid}

	${ECHO} "${argpart}: ${N1_COLOR}${allow_procfs}${N0_COLOR}"
	[ ${allow_procfs} -eq 0 ] && return 0

	# we need to force allow_mount too when allow_tmpfs sets to 1 and allow_mount is zero
	allow_mount=$( cbsdsqlro local "SELECT allow_mount FROM jails WHERE jname=\"${jname}\"" )
	if [ "${allow_mount}" = "0" ]; then
		cbsdsqlrw local "UPDATE jails SET allow_mount=\"1\" WHERE jname=\"${jname}\""
		${JAIL_CMD} -m allow.mount=1 jid=${jid}
		${ECHO} "allow_mount: ${N1_COLOR}1${N0_COLOR}"
	fi
}

# jid must be set
modify_allow_tmpfs()
{
	cbsdsqlrw local "UPDATE jails SET ${i}=\"${allow_tmpfs}\" WHERE jname=\"${jname}\""
	${JAIL_CMD} -m allow.mount.tmpfs=${allow_tmpfs} jid=${jid}

	${ECHO} "${argpart}: ${N1_COLOR}${allow_tmpfs}${N0_COLOR}"
	[ ${allow_tmpfs} -eq 0 ] && return 0

	# we need to force allow_mount too when allow_tmpfs sets to 1 and allow_mount is zero
	allow_mount=$( cbsdsqlro local "SELECT allow_mount FROM jails WHERE jname=\"${jname}\"" )
	if [ "${allow_mount}" = "0" ]; then
		cbsdsqlrw local "UPDATE jails SET allow_mount=\"1\" WHERE jname=\"${jname}\""
		${JAIL_CMD} -m allow.mount=1 jid=${jid}
		${ECHO} "allow_mount: ${N1_COLOR}1${N0_COLOR}"
	fi
}

# jid must be set
modify_allow_fdescfs()
{
	cbsdsqlrw local "UPDATE jails SET ${i}=\"${allow_fdescfs}\" WHERE jname=\"${jname}\""
	${JAIL_CMD} -m allow.mount.fdescfs=${allow_fdescfs} jid=${jid}

	${ECHO} "${argpart}: ${N1_COLOR}${allow_fdescfs}${N0_COLOR}"
	[ ${allow_fdescfs} -eq 0 ] && return 0

	# we need to force allow_mount too when allow_fdescfs sets to 1 and allow_mount is zero
	allow_mount=$( cbsdsqlro local "SELECT allow_mount FROM jails WHERE jname=\"${jname}\"" )
	if [ "${allow_mount}" = "0" ]; then
		cbsdsqlrw local "UPDATE jails SET allow_mount=\"1\" WHERE jname=\"${jname}\""
		${JAIL_CMD} -m allow.mount=1 jid=${jid}
		${ECHO} "allow_mount: ${N1_COLOR}1${N0_COLOR}"
	fi
}

# jid, nice must be set
modify_nice()
{

	local _formfile="${jailsysdir}/${jname}/helpers/jrctl.sqlite"

	if [ ! -r ${_formfile} ]; then
		${ECHO} "${N1_COLOR}No such rctl file: ${N2_COLOR}${_formfile}${N0_COLOR}"
		return 1
	fi

	cbsdsqlrw ${_formfile} UPDATE forms set cur=\"${VAL}\" WHERE param=\"nice\"
	jrenice jname=${jname}
}

# jid, interface, ip4_addr and jname must be set
modify_cpuset()
{
	cbsdsqlrw local UPDATE jails SET ${i}=\"${cpuset}\" WHERE jname=\"${jname}\"
	${ECHO} "${argpart}: ${N1_COLOR}${cpuset}${N0_COLOR}"
	${CPUSET_CMD} -c -l ${cpuset} -j ${jid}
}

# mount_src
modify_mount_src()
{
	local _ver _dst _mount_src

	#push new variable in tmp stack
	_mount_src="${mount_src}"

	. ${system}
	. ${subrdir}/rcconf.subr

	#restore new variable
	mount_src="${_mount_src}"

	cbsdsqlrw local "UPDATE jails SET ${i}=\"${mount_src}\" WHERE jname=\"${jname}\""
	${ECHO} "${argpart}: ${N1_COLOR}${mount_src}${N0_COLOR}"
	[ ${jid} -eq 0 ] && return 0

	. ${subrdir}/universe.subr

	init_srcdir
	[ ! -d "${SRC_DIR}" ] && ${ECHO} "${N1_COLOR}No such src dir: ${N2_COLOR}${SRC_DIR}${N0_COLOR}"

	[ ${baserw} -eq 1 ] && path="${data}"
	_dst="${path}/usr/src"

	case "${mount_src}" in
		0)
			is_mounted ${_dst} && ${UMOUNT_CMD} -f ${_dst}
			;;
		1)
			is_mounted ${_dst} && err 1 "${N1_COLOR}already mounted: ${N2_COLOR}${_dst}${N0_COLOR}"
			[ ! -d "${_dst}" ] && mkdir -p ${_dst}
			${MOUNT_NULL_CMD} -oro ${SRC_DIR} ${_dst}
			;;
	esac
}


# mount_ports
modify_mount_ports()
{
	local _ver _dst _mount_ports

	#push new variable in tmp stack
	_mount_ports="${mount_ports}"

	. ${system}
	. ${subrdir}/rcconf.subr

	#restore new variable
	mount_ports="${_mount_ports}"

	cbsdsqlrw local "UPDATE jails SET ${i}=\"${mount_ports}\" WHERE jname=\"${jname}\""
	${ECHO} "${argpart}: ${N1_COLOR}${mount_ports}${N0_COLOR}"
	[ ${jid} -eq 0 ] && return 0

	[ ${baserw} -eq 1 ] && path="${data}"
	_dst="${path}/usr/ports"

	case "${mount_ports}" in
		0)
			is_mounted ${_dst}/distfiles && ${UMOUNT_CMD} -f ${_dst}/distfiles
			is_mounted ${_dst} && ${UMOUNT_CMD} -f ${_dst}
		;;
		1)
			[ ! -d /usr/ports ] && return 0
			is_mounted ${_dst} && err 1 "${N1_COLOR}already mounted: ${N2_COLOR}${_dst}${N0_COLOR}"
			[ ! -d "${_dst}" ] && mkdir -p ${_dst}
			${MOUNT_NULL_CMD} -oro /usr/ports ${_dst}
			[ ! -d /usr/ports/distfiles ] && mkdir -p /usr/ports/distfiles
			${MOUNT_NULL_CMD} -orw /usr/ports/distfiles ${_dst}/distfiles
		;;
	esac
}

# mount_kernel
modify_mount_kernel()
{
	local _ver _dst _mount_kernel _src

	#push new variable in tmp stack
	_mount_kernel="${mount_kernel}"

	. ${system}
	. ${subrdir}/rcconf.subr

	#restore new variable
	mount_kernel="${_mount_kernel}"

	cbsdsqlrw local "UPDATE jails SET ${i}=\"${mount_kernel}\" WHERE jname=\"${jname}\""
	${ECHO} "${argpart}: ${N1_COLOR}${mount_kernel}${N0_COLOR}"
	[ ${jid} -eq 0 ] && return 0

	. ${subrdir}/universe.subr
	init_kerneldir

	[ ${baserw} -eq 1 ] && path="${data}"
	_dst="${path}/boot/kernel"
	_src="${KERNEL_DIR}/boot/kernel"

	[ ! -d "${_src}" ] && err 1 "${N1_COLOR}No such source kernel dir. Use buildkernel or repo for fetching kernel: ${N2_COLOR}${_src}${N0_COLOR}"

	case "${mount_kernel}" in
		0)
			is_mounted ${_dst} && ${UMOUNT_CMD} -f ${_dst}
			;;
		1)
			is_mounted ${_dst} && err 1 "${N1_COLOR}already mounted: ${N2_COLOR}${_dst}${N0_COLOR}"
			[ ! -d "${_dst}" ] && ${MKDIR_CMD} -p ${_dst}
			${MOUNT_NULL_CMD} -oro ${_src} ${_dst}
			;;
	esac
}

# mount_fdescfs
modify_mount_fdescfs()
{
	local _ver _dst _oldval _src

	#push new variable in tmp stack
	_oldval="${mount_fdescfs}"

	. ${system}
	. ${subrdir}/rcconf.subr

	#restore new variable
	mount_fdescfs="${_oldval}"

	cbsdsqlrw local "UPDATE jails SET ${i}=\"${mount_fdescfs}\" WHERE jname=\"${jname}\""

	${ECHO} "${argpart}: ${N1_COLOR}${mount_fdescfs}${N0_COLOR}"
	[ ${jid} -eq 0 ] && return 0

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

	# check for online
	case "${mount_fdescfs}" in
		0)
			is_mounted ${path}/dev/fd && ${UMOUNT_CMD} -f ${path}/dev/fd
			;;
		1)
			is_mounted ${path}/dev/fd && err 1 "${N1_COLOR}already mounted: ${N2_COLOR}${path}/dev/fd${N0_COLOR}"
			${MOUNT_CMD} -t fdescfs descfs ${path}/dev/fd
			;;
	esac
}

# mount_procfs
modify_mount_procfs()
{
	local _ver _dst _oldval _src

	#push new variable in tmp stack
	_oldval="${mount_procfs}"

	. ${system}
	. ${subrdir}/rcconf.subr

	#restore new variable
	mount_procfs="${_oldval}"

	cbsdsqlrw local "UPDATE jails SET ${i}=\"${mount_procfs}\" WHERE jname=\"${jname}\""

	${ECHO} "${argpart}: ${N1_COLOR}${mount_procfs}${N0_COLOR}"
	[ ${jid} -eq 0 ] && return 0

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

	# check for online
	case "${mount_procfs}" in
		0)
			is_mounted ${path}/proc && ${UMOUNT_CMD} -f ${path}/proc
			;;
		1)
			is_mounted ${path}/proc && err 1 "${N1_COLOR}already mounted: ${N2_COLOR}${path}/proc${N0_COLOR}"
			${MOUNT_CMD} -t procfs procfs ${path}/proc
			;;
	esac
}

# mount_linsysfs
modify_mount_linsysfs()
{
	local _ver _dst _oldval _src

	#push new variable in tmp stack
	_oldval="${mount_linsysfs}"

	. ${system}
	. ${subrdir}/rcconf.subr

	#restore new variable
	mount_linsysfs="${_oldval}"

	cbsdsqlrw local "UPDATE jails SET ${i}=\"${mount_linsysfs}\" WHERE jname=\"${jname}\""

	${ECHO} "${argpart}: ${N1_COLOR}${mount_linsysfs}${N0_COLOR}"
	[ ${jid} -eq 0 ] && return 0

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

	_dst="${path}/compat/linux/sys"

	# check for online
	case "${mount_linsysfs}" in
		0)
			is_mounted ${_dst} && ${UMOUNT_CMD} -f ${_dst}
			;;
		1)
			is_mounted ${_dst} && err 1 "${N1_COLOR}already mounted: ${N2_COLOR}${_dst}${N0_COLOR}"
			[ ! -d "${_dst}" ] && ${MKDIR_CMD} -p ${_dst}
			${MOUNT_CMD} -t linsysfs linsysfs ${_dst}
			;;
	esac
}

# mount_linprocfs
modify_mount_linprocfs()
{
	local _ver _dst _oldval _src

	#push new variable in tmp stack
	_oldval="${mount_linprocfs}"

	. ${system}
	. ${subrdir}/rcconf.subr

	#restore new variable
	mount_linprocfs="${_oldval}"

	cbsdsqlrw local "UPDATE jails SET ${i}=\"${mount_linprocfs}\" WHERE jname=\"${jname}\""

	${ECHO} "${argpart}: ${N1_COLOR}${mount_linprocfs}${N0_COLOR}"
	[ ${jid} -eq 0 ] && return 0

	[ ${baserw} -eq 1 ] && path="${data}"
	_dst="${path}/compat/linux/proc"

	# check for online
	case "${mount_linprocfs}" in
		0)
			is_mounted ${_dst} && ${UMOUNT_CMD} -f ${_dst}
			;;
		1)
			is_mounted ${_dst} && err 1 "${N1_COLOR}already mounted: ${N2_COLOR}${_dst}${N0_COLOR}"
			[ ! -d "${_dst}" ] && ${MKDIR_CMD} -p ${_dst}
			${MOUNT_CMD} -t linprocfs linprocfs ${_dst}
			;;
	esac
}

need_action()
{
	eval VAL="\$$i"
	local oldval=

	case "${i}" in
		nic_hwaddr)
			oldval=$( cbsdsqlro ${jailsysdir}/${jname}/local.sqlite SELECT nic_hwaddr FROM jailnic )
			[ -z "${oldval}" ] && oldval="0"
			;;
		*)
			oldval=$( cbsdsqlro local SELECT ${i} FROM jails WHERE jname=\"${jname}\" LIMIT 1 2>/dev/null )
			;;
	esac

	[ "${oldval}" != "${VAL}" ] && return 1

	return 0
}

update_jails()
{
	local tmpver=

	eval VAL="\$$i"

	# special word - we not update arch/ver to 'native'
	case "${i}" in
		arch)
			if [ "${VAL}" = "native" ]; then
				VAL=$( ${UNAME_CMD} -m )
				export $i="${VAL}"
			fi
			;;
		ver)
			if [ "${VAL}" = "native" ]; then
				tmpver=$( ${UNAME_CMD} -r )
				VAL=${tmpver%%-*}
				[ "${stable}" = "1" ] && VAL=${VAL%%.*}
				export $i="${VAL}"
			fi
			;;
		nic_hwaddr)
			cbsdsqlrw ${jailsysdir}/${jname}/local.sqlite UPDATE jailnic SET nic_hwaddr=\"${VAL}\" WHERE name=\"epairb\"
			return 0
			;;
		ip4_addr)
			validate_ipaddr
			;;
	esac

	if ! need_action; then
		cbsdsqlrw local "UPDATE jails SET ${i}=\"${VAL}\" WHERE jname='${jname}'"
		cbsdlogger NOTICE ${CBSD_APP}: ${argpart}: ${VAL}
		${ECHO} "${argpart}: ${N1_COLOR}${VAL}${N0_COLOR}"

		# Update realtime info
		if [ "${mod_cbsd_queue_enabled}" = "YES" -a -z "${MOD_CBSD_QUEUE_DISABLED}" ]; then
			case "${i}" in
				ip4_addr|host_hostname|protected)
					[ -n "${cbsd_jail_queue_name}" ] && ${cbsd_queue_backend} cbsd_queue_name=${cbsd_jail_queue_name} id="${jname}" cmd=update ip4_addr="${ip4_addr}" host_hostname="${host_hostname}" protected="${protected}" status=1
					;;
			esac
		fi

		need_for_restart=1
	fi
}

# MAIN
# here we get status from jstatus, not via jrcconf for non-overwriting params in args
jid=$( jstatus jname=${jname} )
[ $? -eq 0 ] && err 1 "${N1_COLOR}No such jail: ${N2_COLOR}${jname}${N0_COLOR}"

if [ -n "${jconf}" ]; then
	[ ! -r "${jconf}" ] && err 1 "${N1_COLOR}No such jconf file: ${N2_COLOR}${jconf}${N0_COLOR}"
	#jset jname=${jname} `${GREP_CMD} -v '#' ${jconf} | ${GREP_CMD} .| ${TR_CMD} -d ";"`
	my_arg=$( ${GREP_CMD} -v '#' ${jconf} | ${GREP_CMD} . | ${TR_CMD} -d ";" | ${SED_CMD} 's/"/\\"/g' | ${XARGS_CMD} )
	exec /bin/sh << EOF
/usr/local/bin/cbsd jset jname=${jname} "${my_arg}" autorestart=${autorestart}
EOF
	exit 0
else
	my_arg="$@"
fi

for n in ${my_arg}; do

	argpart="${n%%=*}"

	[ "${argpart}" = "jconf" ] && continue
	[ "${argpart}" = "autorestart" ] && continue
	#[ "${argpart}" = "exec_stop" ] && continue
	#[ "${argpart}" = "exec_start" ] && continue
	[ "${argpart}" = "exec_poststop" ] && continue
	[ "${argpart}" = "exec_poststart" ] && continue

	for i in ${JARG} nice; do
		if [ "${argpart}" = "${i}" -a "${argpart}" != "jname" ]; then

			if need_action; then
				continue
			fi

			##### check for already running
			if [ ${jid} -ne 0  ]; then
				case "${argpart}" in
					ip4_addr)
						cbsdlogger NOTICE ${CBSD_APP}: modify_ipaddr for ${jname}
						modify_ipaddr
						;;
					"cpuset")
						cbsdlogger NOTICE ${CBSD_APP}: modify_cpuset for ${jname}
						modify_cpuset
						;;
					"nice")
						cbsdlogger NOTICE ${CBSD_APP}: modify_nicee for ${jname}
						modify_nice
						;;
					astart|applytpl|floatresolv|exec_consolelog|devfs_ruleset|protected|hidden)
						update_jails
						;;
					mount_src|mount_ports|mount_kernel|mount_fdescfs|mount_procfs|mount_linprocfs|mount_linsysfs)
						cbsdlogger NOTICE ${CBSD_APP}: modify_${argpart} for ${jname}
						modify_${argpart}
						;;
					allow_mount)
						cbsdlogger NOTICE ${CBSD_APP}: modify_allow_mount for ${jname}
						modify_allow_mount
						;;
					allow_raw_sockets)
						cbsdlogger NOTICE ${CBSD_APP}: modify_allow_raw_sockets for ${jname}
						modify_allow_raw_sockets
						;;
					allow_nullfs)
						cbsdlogger NOTICE ${CBSD_APP}: modify_allow_nullfs for ${jname}
						modify_allow_nullfs
						;;
					allow_fusefs)
						cbsdlogger NOTICE ${CBSD_APP}: modify_allow_fusefs for ${jname}
						modify_allow_fusefs
						;;
					allow_linprocfs)
						cbsdlogger NOTICE ${CBSD_APP}: modify_allow_linprocfs for ${jname}
						modify_allow_linprocfs
						;;
					allow_linsysfs)
						cbsdlogger NOTICE ${CBSD_APP}: modify_allow_linsysfs for ${jname}
						modify_allow_linsysfs
						;;
					allow_tmpfs)
						cbsdlogger NOTICE ${CBSD_APP}: modify_allow_tmpfs for ${jname}
						modify_allow_tmpfs
						;;
					allow_read_msgbuf)
						cbsdlogger NOTICE ${CBSD_APP}: modify_allow_read_msgbuf for ${jname}
						modify_allow_read_msgbuf
						;;
					allow_vmm)
						cbsdlogger NOTICE ${CBSD_APP}: modify_vmm for ${jname}
						modify_allow_vmm
						;;
					allow_mlock)
						cbsdlogger NOTICE ${CBSD_APP}: modify_mlock for ${jname}
						modify_allow_mlock
						;;
					allow_procfs)
						cbsdlogger NOTICE ${CBSD_APP}: modify_allow_procfs
						modify_allow_procfs
						;;
					allow_fdescfs)
						cbsdlogger NOTICE ${CBSD_APP}: modify_allow_fdescfs
						modify_allow_fdescfs
						;;
					boot_delay)
						# on-the-fly
						update_jails
						;;
					*)
						if [ ${autorestart} -eq 0 ]; then
							cbsdlogger NOTICE ${CBSD_APP}: on-the-fly currently unimplemented for ${argpart} for ${jname}
							${ECHO} "${argpart}: ${N1_COLOR}on-the-fly currently unimplemented${N0_COLOR}"
						else
							update_jails
						fi
						;;
				esac
			else
				update_jails
			fi
		fi
	done
done

if [ ${need_for_restart} -eq 1 -a ${autorestart} -eq 1 ]; then
	cbsdlogger NOTICE ${CBSD_APP}: restart jail from jset via need_for_restart/autorestart flags for ${jname}
	jrestart ${jname}
fi

# exit code 0 is nessesary for dot()
exit 0
