#!/bin/sh
# bastille-init
# Bastille Extension for XigmaNAS x64 12.x and later.
# Bastille Extension Forum:  https://www.xigmanas.com/forums/viewtopic.php?f=71&t=14848
# Bastille Extension GitHub: https://github.com/JRGTH/xigmanas-bastille-extension
# Bastille Homepage:         http://bastillebsd.org/
# Bastille GitHub:           https://github.com/BastilleBSD/bastille
#
# Debug script
#set -x

# Copyright (c) 2019-2024, José Rivera (joserprg@gmail.com).
# All rights reserved.

# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
# 3. Neither the name of the developer nor the names of contributors
#    may be used to endorse or promote products derived from this software
#    without specific prior written permission.

# THIS SOFTWARE IS PROVIDED BY THE DEVELOPER ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE DEVELOPER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.

# Set environment.
PATH=${PATH}:/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin

# Determine full working directory.
CWDIR=$(dirname $(realpath $0))

# Global variables.
CWDIR_TRIM=""
BASTILLE_ZFS_ZPOOL_MOUNTPOINT=""
BASTILLE_ZFS_ZPOOL_MOUNTPOINT_TRIM=""
REQUIRED_UPDATE=""
PLATFORM=$(uname -m)
PRODUCT=$(uname -i)
PRDVERSION=$(uname -r | cut -d '-' -f1 | tr -d '.')
HOSTVERSION=$(freebsd-version | cut -d '-' -f1)
PRDPLATFORM=$(cat /etc/platform)
PRDPRODUCT=$(cat /etc/prd.name)
SCRIPTNAME=$(basename $0)
CONFIG="/cf/conf/config.xml"
PRDNAME="Bastille"
APPNAME="bastille"
EXTLOGFILE="${CWDIR}/log/bastille_ext.log"
FULLAPPNAME="${APPNAME}-dist"
WWWPATH="/usr/local/www"
PKGCACHE="/var/cache/pkg"
USRLOCAL="/usr/local"
VARLOG="/var/log"
EXTCONF="/conf/${APPNAME}_config"
EXTCONFLINK="/var/etc/${APPNAME}_conf"
BASTILLERCD="/usr/local/etc/rc.d/${APPNAME}"
BASTILLEPATH="${USRLOCAL}/bin"
BASTILLECONF="${USRLOCAL}/etc/${APPNAME}/${APPNAME}.conf"
BASTILLECONFFILE="/conf/bastille_config"
BASTILLECONFLINK="/var/etc/bastille_conf"
BASTILLECONF_EXT="${CWDIR}/conf/bastille.conf.ext"
INCLUDE_PATH="${CWDIR}/conf/system"
FREEBSD_UPDATE="${INCLUDE_PATH}/freebsd-update/${HOSTVERSION}"
SYSTEM_INCLUDE="${INCLUDE_PATH}/include/${HOSTVERSION}"
INSTALLPATH="${CWDIR}/${FULLAPPNAME}"
BRANCH="master"
BATSILLE_URL="https://github.com/BastilleBSD/${APPNAME}/archive/${BRANCH}.zip" # Official Bastille Repository)
BATSILLE_ALT="https://github.com/JRGTH/${APPNAME}/archive/${BRANCH}.zip" # Alternate Bastille Repository, early updates)
BASTILE_VERSION="https://raw.githubusercontent.com/BastilleBSD/${APPNAME}/${BRANCH}/usr/local/bin/${APPNAME}"
GITURL="https://github.com/JRGTH/xigmanas-${APPNAME}-extension/archive/${BRANCH}.zip"
VERFILE="https://raw.githubusercontent.com/JRGTH/xigmanas-${APPNAME}-extension/${BRANCH}/version"
URL_FREEBSD="http://ftp.freebsd.org/pub/FreeBSD/releases/"
URL_HARDENEDBSD="https://installers.hardenedbsd.org/pub/"
URL_MIDNIGHTBSD="https://www.midnightbsd.org/ftp/MidnightBSD/releases/"
OPT="${1}"

# Bastille required
if [ -f "${BASTILLECONF}" ]; then
	. /${BASTILLECONF}
	if [ "${bastille_zfs_enable}" = "YES" -o "${bastille_zfs_enable}" = "yes" ]; then
		if [ -n "${bastille_zfs_prefix}" ] && [ -n "${bastille_zfs_zpool}" ]; then
			# Always enforce ZFS activation below "/mnt/" from the extension.
			if echo "${CWDIR}" | grep -q '/mnt/'; then
				CWDIR_TRIM=$(echo "${CWDIR}" | sed "s|/mnt/||;s|/${bastille_zfs_prefix}||")
			fi

			BASTILLE_ZFS_ZPOOL_MOUNTPOINT=$(zfs get -H -o value mountpoint "${bastille_zfs_zpool}")
			BASTILLE_ZFS_ZPOOL_MOUNTPOINT_TRIM=""
			if echo "${BASTILLE_ZFS_ZPOOL_MOUNTPOINT}" | grep -q '/mnt/'; then
				BASTILLE_ZFS_ZPOOL_MOUNTPOINT_TRIM=$(echo "${BASTILLE_ZFS_ZPOOL_MOUNTPOINT}" | sed "s|/mnt/||;s|/${bastille_zfs_prefix}||")
			fi
		fi
	fi
fi

error_notify()
{
	# Log/notify message on error and exit.
	MSG="$*"
	logger -t "${SCRIPTNAME}" "${MSG}"
	echo -e "$*" >&2; exit 1
}

runtime_config()
{
	# Run-time configuration and checks.
	if [ -f "${INSTALLPATH}/${BASTILLECONF}" ]; then
		if ! sysrc -f ${BASTILLECONF} -qn bastille_prefix | grep -q "${CWDIR}"; then
			sysrc -f ${INSTALLPATH}/${BASTILLECONF} bastille_prefix="${CWDIR}" >/dev/null 2>&1
		fi
	fi

	# Check for required directories and files.
	if [ ! -d "${CWDIR}/backups" ]; then
		mkdir -p ${CWDIR}/backups
	fi
	if [ ! -d "${CWDIR}/conf" ]; then
		mkdir -p ${CWDIR}/conf
	fi
	if [ ! -d "${CWDIR}/log" ]; then
		mkdir -p ${CWDIR}/log
	fi
	if [ ! -d "${CWDIR}/locale-bastille" ]; then
		mkdir -p ${CWDIR}/locale-bastille
	fi
	if [ ! -d "${CWDIR}/freebsd-update" ]; then
		mkdir ${CWDIR}/freebsd-update
	fi
	if [ ! -f "${CWDIR}${BASTILLECONFFILE}" ]; then
		touch ${CWDIR}${BASTILLECONFFILE}
	fi
	if [ ! -d "${CWDIR}/system" ]; then
		mkdir -p ${CWDIR}/system
	fi

	# Check for permissions.
	if [ -f "${FREEBSD_UPDATE}/freebsd-update" ]; then
		FREEBSD_UPDATE_PERMS=$(stat -f "%Op" ${FREEBSD_UPDATE}/freebsd-update)
		if [ "${FREEBSD_UPDATE_PERMS}" != 100555 ]; then
			chmod 0555 ${FREEBSD_UPDATE}/freebsd-update
		fi
	fi

	# Workaround to check for host /tmp sane permissions.
	# This is because after working with Linux jails, this may be changed to 0777 but XigmaNAS wants 1777.
	if grep -qw '\"chmod\ 777\ /tmp\"' ${INSTALLPATH}/usr/local/share/bastille/create.sh; then
		sed -i '' 's|\"chmod\ 777\ /tmp\"|\"chmod\ 1777\ /tmp\"|g' ${INSTALLPATH}/usr/local/share/bastille/create.sh
	else
		if [ -d "/tmp" ]; then
			TMP_PERMS=$(stat -f "%Op" "/tmp")
			if [ "${TMP_PERMS}" != "41777" ]; then
				chmod 1777 /tmp
			fi
		fi
	fi

	# Check and append new config parameters.
	update_config
}

bastille_initial_download()
{
	# Check if bastille already exist.
	
	if [ -n "${REQUIRED_UPDATE}" ] || [ ! -f "${CWDIR}/${FULLAPPNAME}${BASTILLEPATH}/${APPNAME}" ]; then
		# Fetch latest bastille package.
		echo "Fetching ${APPNAME} files..."
		fetch -ao ${CWDIR}/${BRANCH}.zip --no-verify-peer --timeout=30 ${BATSILLE_URL} || \
		error_notify "Error: A problem has occurred while fetching ${APPNAME}."
		bastille_pkg_extract
	fi
}

bastille_pkg_extract()
{
	# Extract bastille files from package.
	if [ -n "${REQUIRED_UPDATE}" ] || [ -f "${CWDIR}/${BRANCH}.zip" ]; then
		if [ -n "${REQUIRED_UPDATE}" ] || [ ! -f "${CWDIR}/${FULLAPPNAME}${BASTILLEPATH}/${APPNAME}" ]; then
			echo "Extracting ${APPNAME}..."
			tar -xf ${CWDIR}/${BRANCH}.zip --exclude='.git*' --exclude='docs' --exclude='bastille.conf' --strip-components 1 -C ${CWDIR}/${FULLAPPNAME} || \
			error_notify "Error: A problem has occurred while extractig ${APPNAME} files."
			chmod 555 ${CWDIR}/${FULLAPPNAME}${BASTILLEPATH}/${APPNAME}
			chmod 555 ${CWDIR}/${FULLAPPNAME}${BASTILLERCD}
			rm -f ${CWDIR}/${BRANCH}.zip
			echo "Done!"
		fi
	fi
}

bastille_upgrade()
{
	# Perform an online bastille upgrade.
	DATE=$(date +"%a %b %d %T %Y")
	echo "Looking for new ${APPNAME} package!"
	mkdir -p ${CWDIR}/update
	fetch -ao ${CWDIR}/update --no-verify-peer --timeout=30 ${BASTILE_VERSION} || \
	error_notify "Error: A problem has occurred while fetching version file."

	# Compare version files and fetch latest package if available.
	if [ -f "${CWDIR}/update/${APPNAME}" ]; then
		UPDATEVER=$(cat ${CWDIR}/update/${APPNAME} | grep BASTILLE_VERSION= | egrep -o "([0-9]{1,}\.)+[0-9]{1,}" | tr -d '.')
		CURRENTVER=$(cat ${BASTILLEPATH}/${APPNAME} | grep BASTILLE_VERSION= | egrep -o "([0-9]{1,}\.)+[0-9]{1,}" | tr -d '.')
		if [ "${UPDATEVER}" -gt "${CURRENTVER}" ]; then
			echo "New ${APPNAME} package found, performing upgrade..."
			fetch -ao ${CWDIR}/update --no-verify-peer --timeout=30 ${BATSILLE_URL} || \
			error_notify "Error: A problem has occurred while fetching ${APPNAME} package."
			tar -xf ${CWDIR}/update/${BRANCH}.zip --exclude='.git*' --exclude='docs' --exclude='bastille.conf' --strip-components 1 -C ${CWDIR}/update
			rm -f ${CWDIR}/update/${BRANCH}.zip
			rm -f ${CWDIR}/update/${APPNAME}
			chmod 555 ${CWDIR}/update/${BASTILLEPATH}/${APPNAME}
			chmod 555 ${CWDIR}/${FULLAPPNAME}${BASTILLERCD}
			cp -Rf ${CWDIR}/update/* ${CWDIR}/${FULLAPPNAME}/
			rm -R ${CWDIR}/update

			# Logging the update event.
			UPDATEVERSION=$(cat ${BASTILLEPATH}/${APPNAME} | grep BASTILLE_VERSION= | cut -d"=" -f2 | tr -d '."')
			echo "${DATE}: ${APPNAME} upgraded to ${UPDATEVERSION}" >> ${EXTLOGFILE}
			echo "${APPNAME} upgraded to version ${UPDATEVERSION}"
			echo "${APPNAME} package upgrade completed!"
		else
			echo "${APPNAME} is on the latest version!"
			rm -R ${CWDIR}/update
		fi

		# Workaround to update legacy config.
		if [ "${UPDATEVER}" -gt "0620200202" ]; then
			update_config
		fi
	fi
}

bastille_core_update()
{
	# Check if bastille already exist.
	
	if [ -f "${CWDIR}/${FULLAPPNAME}${BASTILLEPATH}/${APPNAME}" ]; then
		# Fetch latest bastille package.
		echo "Fetching ${APPNAME} files..."
		fetch -ao ${CWDIR}/${BRANCH}.zip --no-verify-peer --timeout=30 ${BATSILLE_URL} || \
		error_notify "Error: A problem has occurred while fetching ${APPNAME}."
		bastille_pkg_extract
	fi

	if [ -f "${CWDIR}/${BRANCH}.zip" ] && [ -f "${CWDIR}/${FULLAPPNAME}${BASTILLEPATH}/${APPNAME}" ]; then
		echo "Extracting ${APPNAME}..."
		tar -xf ${CWDIR}/${BRANCH}.zip --exclude='.git*' --exclude='docs' --exclude='bastille.conf' --strip-components 1 -C ${CWDIR}/${FULLAPPNAME} || \
		error_notify "Error: A problem has occurred while extractig ${APPNAME} files."
		chmod 555 ${CWDIR}/${FULLAPPNAME}${BASTILLEPATH}/${APPNAME}
		chmod 555 ${CWDIR}/${FULLAPPNAME}${BASTILLERCD}
		rm -f ${CWDIR}/${BRANCH}.zip
		echo "Done!"
	fi

	echo "${PRDNAME} core package update completed!"
	exit 0
}

ext_initial_download()
{
	# Always ensure the version file is present, otherwise update the extension files on startup.
	if [ ! -f "${CWDIR}/version" ]; then
		echo "Fetching and extracting extension files..."
		mkdir -p ${CWDIR}/update
		fetch -ao ${CWDIR}/update --no-verify-peer --timeout=30 ${GITURL} || \
		error_notify "Error: A problem has occurred while fetching extension package."
		tar -xf ${CWDIR}/update/${BRANCH}.zip --exclude='.git*' --strip-components 1 -C ${CWDIR}/update
		chmod +x ${CWDIR}/update/${SCRIPTNAME}
		rm -f ${CWDIR}/update/${BRANCH}.zip
		cp -Rf ${CWDIR}/update/* ${CWDIR}/
		rm -R ${CWDIR}/update
		echo "Done!"
	fi
}

extension_upgrade()
{
	# Perform an online extension upgrade.
	DATE=$(date +"%a %b %d %T %Y")
	echo "Looking for new Extension package!"
	mkdir -p ${CWDIR}/update
	fetch -ao ${CWDIR}/update --no-verify-peer --timeout=30 ${VERFILE} || \
	error_notify "Error: A problem has occurred while fetching version file."

	# Compare version files and fetch latest package if available.
	if [ -f "${CWDIR}/update/version" ]; then
		UPDATEVER=$(cat ${CWDIR}/update/version | tr -d .)
		CURRENTVER=$(cat ${CWDIR}/version | tr -d .)
		if [ "${UPDATEVER}" -gt "${CURRENTVER}" ]; then
			echo "New Extension package found, performing upgrade..."
			fetch -ao ${CWDIR}/update --no-verify-peer --timeout=30 ${GITURL} || \
			error_notify "Error: A problem has occurred while fetching extension package."
			tar -xf ${CWDIR}/update/${BRANCH}.zip --exclude='.git*' --strip-components 1 -C ${CWDIR}/update
			chmod +x ${CWDIR}/update/${SCRIPTNAME}
			rm -f ${CWDIR}/update/${BRANCH}.zip
			cp -Rf ${CWDIR}/update/* ${CWDIR}/
			rm -R ${CWDIR}/update

			# Logging the update event.
			UPDATEVERSION=$(cat ${CWDIR}/version)
			echo "${DATE}: Extension upgraded to ${UPDATEVERSION}" >> ${EXTLOGFILE}
			echo "Extension upgraded to version ${UPDATEVERSION}"
			echo "Extension package upgrade completed!"
		else
			echo "Extension is on the latest version!"
			rm -R ${CWDIR}/update
		fi
	fi
}

create_addon_env()
{
	# Set bastille dir required permissions.
	chmod 0750 ${CWDIR}

	# Create required directories.
	if [ ! -d "${CWDIR}/backups" ]; then
		mkdir -p ${CWDIR}/backups
	fi
	if [ ! -d "${CWDIR}/log" ]; then
		mkdir -p ${CWDIR}/log
	fi
	if [ ! -d "${CWDIR}/${FULLAPPNAME}" ]; then
		mkdir -p ${CWDIR}/${FULLAPPNAME}
	fi
	if [ ! -d "${CWDIR}/${FULLAPPNAME}${BASTILLEPATH}" ]; then
		mkdir -p ${CWDIR}/${FULLAPPNAME}${BASTILLEPATH}
	fi
	if [ ! -d "${CWDIR}/freebsd-update" ]; then
		mkdir ${CWDIR}/freebsd-update
	fi

	# Link bastille-init to /usr/local/sbin.
	if [ ! -f "${USRLOCAL}/sbin/${SCRIPTNAME}" ]; then
		ln -fs ${CWDIR}/${SCRIPTNAME} ${USRLOCAL}/sbin/${SCRIPTNAME}
	fi
}

platform_check()
{
	# Check for working platform.
	if [ "${PRDPLATFORM}" = "x64-embedded" ]; then
		create_addon_env
		ext_initial_download
		bastille_initial_download
		sys_symlinkdir
		include_files
	elif [ "${PRDPLATFORM}" = "x64-full" ]; then
		create_addon_env
		ext_initial_download
		bastille_initial_download
		sys_symlinkdir
		include_files
	fi
}

bin_symlinks()
{
	# Main bastille symlinks.
	if [ -d "${INSTALLPATH}/${BASTILLEPATH}" ]; then
		cd ${INSTALLPATH}/${BASTILLEPATH}
		for file in *
			do
				ln -fhs ${INSTALLPATH}/${BASTILLEPATH}/${file} ${USRLOCAL}/bin/${file}
		done
	fi
}

sys_symlinkdir()
{
	# Check and create/relink required symlinks/dirs for bastille.
	# This environment will be checked each time the script is started for consistency.

	# Link required binaries.
	bin_symlinks

	# Required directories for bastille.
	if [ ! -d "${USRLOCAL}/share/licenses" ]; then
		mkdir -p ${USRLOCAL}/share/licenses
	fi

	# Required symlinks for bastille. 
	if [ -d "${INSTALLPATH}${USRLOCAL}/share/licenses" ]; then
		cd ${INSTALLPATH}${USRLOCAL}/share/licenses
		for file in *
			do
				ln -fhs ${INSTALLPATH}${USRLOCAL}/share/licenses/${file} ${USRLOCAL}/share/licenses/${file}
		done
	fi

	# Link bastile config file directory.
	if [ -d "${INSTALLPATH}${USRLOCAL}/etc/${APPNAME}" ]; then
		ln -fhs ${INSTALLPATH}${USRLOCAL}/etc/${APPNAME} ${USRLOCAL}/etc/${APPNAME}
	fi

	# Link bastile config file.
	#if [ -f "${INSTALLPATH}${USRLOCAL}/etc/${APPNAME}/${APPNAME}.conf.sample" ]; then
	#	cd ${INSTALLPATH}${USRLOCAL}/etc/${APPNAME}
	#	if [ ! -f "${APPNAME}.conf" ]; then
	#		cp ${APPNAME}.conf.sample ${APPNAME}.conf
	#	fi
	#else
		if [ -f "${BASTILLECONF_EXT}" ]; then
			if [ ! -f "${INSTALLPATH}${USRLOCAL}/etc/${APPNAME}/${APPNAME}.conf" ]; then
				cp ${BASTILLECONF_EXT} ${INSTALLPATH}${USRLOCAL}/etc/${APPNAME}/${APPNAME}.conf
			fi
		fi
	#fi

	# Copy bastille shared.
	if [ -d "${INSTALLPATH}${USRLOCAL}/share/${APPNAME}" ]; then
		ln -fhs ${INSTALLPATH}${USRLOCAL}/share/${APPNAME} ${USRLOCAL}/share/${APPNAME}
	fi

	# Copy bastille rc.
	if [ -f "${INSTALLPATH}${USRLOCAL}/etc/rc.d/${APPNAME}" ]; then
		cp ${INSTALLPATH}${USRLOCAL}/etc/rc.d/${APPNAME} ${USRLOCAL}/etc/rc.d/${APPNAME}
	fi
}

include_files()
{
	if [ "$(freebsd-version | cut -d '.' -f1)" -ge 12 ]; then
		sysrc -f ${CWDIR}${EXTCONF} VNET_ENABLE="YES" >/dev/null 2>&1
		# Include missing system files.
		if [ ! -f "/usr/bin/ar" ]; then
			if [ -f "${SYSTEM_INCLUDE}/ar" ]; then
				install -m 0555 ${SYSTEM_INCLUDE}/ar /usr/bin/ar
			fi
		fi
		if [ ! -f "/usr/local/bin/jib" ]; then
			if [ -f "${SYSTEM_INCLUDE}/jib" ]; then
				install -m 0544 ${SYSTEM_INCLUDE}/jib /usr/local/bin/jib
			fi
		fi
		if [ ! -f "/usr/sbin/setfib" ]; then
			if [ -f "${SYSTEM_INCLUDE}/setfib" ]; then
				install -m 0555 ${SYSTEM_INCLUDE}/setfib /usr/sbin/setfib
			fi
		fi
		if [ ! -f "/usr/bin/sum" ]; then
			if [ -f "${SYSTEM_INCLUDE}/sum" ]; then
				install -m 0555 ${SYSTEM_INCLUDE}/sum /usr/bin/sum
			fi
		fi
		if [ ! -f "/usr/bin/diff3" ]; then
			if [ -f "${SYSTEM_INCLUDE}/diff3" ]; then
				install -m 0555 ${SYSTEM_INCLUDE}/diff3 /usr/bin/diff3
			fi
		fi
		if [ ! -f "/usr/bin/makewhatis" ]; then
			if [ -f "${SYSTEM_INCLUDE}/makewhatis" ]; then
				install -m 0555 ${SYSTEM_INCLUDE}/makewhatis /usr/bin/makewhatis
			fi
		fi
	else
		sysrc -f ${CWDIR}${EXTCONF} VNET_ENABLE="NO" >/dev/null 2>&1
	fi

	if [ "$(freebsd-version | cut -d '.' -f1)" -ge 12 ]; then
		# Include missing pf(packet filter) files.
		PF_FILES="/pfctl /pfilctl /pflogd /pf.os"
		for _file in ${PF_FILES}; do
			if [ ! -f "/sbin/${_file}" ]; then
				if [ "${_file}" = "/pf.os" ]; then
					if [ ! -f "/etc/${_file}" ]; then
						if [ -f "${SYSTEM_INCLUDE}/${_file}" ]; then
							install -m 0644 ${SYSTEM_INCLUDE}/${_file} /etc/${_file}
						fi
					fi
				else
					if [ -f "${SYSTEM_INCLUDE}/${_file}" ]; then
						install -m 0555 ${SYSTEM_INCLUDE}/${_file} /sbin/${_file}
					fi
				fi
			fi
		done
	fi
}

required_updates()
{
	# Check for critical and/or required updates and bug fixes and apply them.
	# This is because not always the bastille version is increased on updates and/or bug fixes.
	if [ -f "${INSTALLPATH}${USRLOCAL}/share/${APPNAME}/rename.sh" ]; then
		if ! grep -qw '{ZFS_DATASET_ORIGIN}.*{ZFS_DATASET_TARGET}' ${INSTALLPATH}${USRLOCAL}/share/${APPNAME}/rename.sh; then
			echo "Required update found, performing update..."
			echo "${DATE}: ${APPNAME} required update has been applied" >> ${EXTLOGFILE}
			REQUIRED_UPDATE="1"
			bastille_initial_download
		fi
	fi

	# Check for a critical bug that prevents VNET jail creation.
	if grep -q '\\"vnet host interface for Bastille jail ${NAME}"' ${INSTALLPATH}${USRLOCAL}/share/${APPNAME}/create.sh; then
		sed -i '' 's|\\"vnet host interface for Bastille jail ${NAME}"|\\"vnet host interface for Bastille jail ${NAME}\\"|g' ${INSTALLPATH}${USRLOCAL}/share/${APPNAME}/create.sh
	fi
}

postinit_cmd()
{
	# Check and generate temporary php script for postinit command.
	if ! grep -qw ${CWDIR}/${SCRIPTNAME} ${CONFIG}; then
		touch ${CWDIR}/postinit || error_notify "Error: A problem has occurred while creating the postinit file."
		chmod +x ${CWDIR}/postinit
		if [ "${PRDVERSION}" -ge "110" ]; then
			# Generate php script for start/stop commands.
			cat << EOF > ${CWDIR}/postinit
<?php
require_once("config.inc");
require_once("functions.inc");
\$cmd = dirname(__FILE__)."/${SCRIPTNAME}";
\$cmd2 = dirname(__FILE__)."/${SCRIPTNAME} -p";
\$name = "${PRDNAME} Extension";
\$comment = "Start ${PRDNAME} Container Manager";
\$comment2 = "Stop ${PRDNAME} Container Manager";
\$rc = &array_make_branch(\$config,'rc','param'); 
if(false === array_search_ex(\$cmd,\$rc,'cmd')):
	\$rc_param = [];
	\$rc_param['uuid'] = uuid();
	\$rc_param['name'] = \$name;
	\$rc_param['value'] = \$cmd;
	\$rc_param['comment'] = \$comment;
	\$rc_param['typeid'] = '2';
	\$rc_param['enable'] = true;
	\$rc[] = \$rc_param;
	write_config();
endif;
unset(\$rc);

\$rc = &array_make_branch(\$config,'rc','param'); 
if(false === array_search_ex(\$cmd2,\$rc,'cmd2')):
	\$rc_param = [];
	\$rc_param['uuid'] = uuid();
	\$rc_param['name'] = \$name;
	\$rc_param['value'] = \$cmd2;
	\$rc_param['comment'] = \$comment2;
	\$rc_param['typeid'] = '3';
	\$rc_param['enable'] = true;
	\$rc[] = \$rc_param;
	write_config();
endif;
unset(\$rc);
?>
EOF
		fi

		# Execute temporary php script.
		if [ "${OBI_INSTALL}" != "ON" ]; then
			echo "Creating postinit command..."
			php-cgi -f ${CWDIR}/postinit && rm ${CWDIR}/postinit || \
			error_notify "Error: A problem has occurred while executing postinit file."
			echo "Done!"
		fi

		# Set extension to enable by default.
		sysrc -f ${CWDIR}${EXTCONF} GUI_ENABLE=YES INSTALL_DIR=${CWDIR} >/dev/null 2>&1
	fi
}

gui_start()
{
	# Initialize the extension gui.
	if [ -d "${CWDIR}/gui" ]; then
		# Always ensure the config directory/file exist.
		if [ ! -f "${CWDIR}${EXTCONF}" ]; then
			# Try to restore default configuration.
			runtime_config
			# Set default config.
			sysrc -f ${CWDIR}${EXTCONF} GUI_ENABLE=YES INSTALL_DIR=${CWDIR} >/dev/null 2>&1
		fi
		GUI_STATUS=$(sysrc -f ${CWDIR}${EXTCONF} -qn GUI_ENABLE)
		if [ "${GUI_STATUS}" = "YES" ]; then
			# Store the installation path and link conf.
			if ! sysrc -f ${CWDIR}${EXTCONF} -n INSTALL_DIR | grep -q "${CWDIR}"; then
				sysrc -f ${CWDIR}${EXTCONF} INSTALL_DIR=${CWDIR} >/dev/null 2>&1
			fi
			mkdir -p ${BASTILLECONFLINK}
			ln -fhs ${CWDIR}/conf ${BASTILLECONFLINK}/conf
			# Link the gui files.
			if [ ! -d "${WWWPATH}/ext" ]; then
				mkdir -p ${WWWPATH}/ext
			fi
			ln -fhs ${CWDIR}/gui/ext/bastille ${WWWPATH}/ext/ || error_notify "Error: A problem has occurred while copying extension gui files."
			ln -fhs ${CWDIR}/gui/images ${WWWPATH}/ext/bastille/ || error_notify "Error: A problem has occurred while copying extension gui files."
			ln -fhs ${CWDIR}/gui/bastille_manager_*.php ${WWWPATH}/ || error_notify "Error: A problem has occurred while linking extension gui files."
		fi
	fi
}

gui_enable()
{
	# Relink conf and gui files.
	if [ -d "${CWDIR}/gui" ]; then
		mkdir -p ${BASTILLECONFLINK}
		ln -fhs ${CWDIR}/conf ${BASTILLECONFLINK}/conf
		sysrc -f ${CWDIR}${EXTCONF} GUI_ENABLE=YES >/dev/null 2>&1
		if [ ! -d "${WWWPATH}/ext" ]; then
			mkdir -p ${WWWPATH}/ext
		fi
		ln -fhs ${CWDIR}/gui/ext/bastille ${WWWPATH}/ext/ || error_notify "Error: A problem has occurred while copying extension gui files."
		ln -fhs ${CWDIR}/gui/images ${WWWPATH}/ext/bastille/ || error_notify "Error: A problem has occurred while copying extension gui files."
		ln -fhs ${CWDIR}/gui/bastille_manager_*.php ${WWWPATH}/ || error_notify "Error: A problem has occurred while copying extension gui files."
		exit 0
	else
		error_notify "Error: Extension gui files not found."
	fi
}

gui_disable()
{
	# Disable gui if -t option specified.
	if [ -d "${CWDIR}/gui" ]; then
		rm -f ${WWWPATH}bastille_manager_*.php
		rm -rf ${WWWPATH}/ext/bastille
		rm -rf ${WWWPATH}/ext/bastille/images
		rm -rf ${LOCALSHAREPATH}/locale-bastille
		rm -rf ${BASTILLECONFLINK}
		sysrc -f ${CWDIR}${EXTCONF} GUI_ENABLE=NO >/dev/null 2>&1 || error_notify "Error: A problem while removing extension gui files."
		exit 0
	else
		error_notify "Error: Extension gui files not found."
	fi

	# Remove empty ext folder to prevent empty "Extensions" tab.
	if [ -d "${WWWPATH}/ext" ]; then
		if [ ! "$(ls -A ${WWWPATH}/ext)" ]; then
			rm -r ${WWWPATH}/ext
		fi
	fi
}

jail_update()
{
	# Workaround since XigmaNAS does not ship with freebsd-update command.

	if [ "${PRDPRODUCT}" = "XigmaNAS" -o "${PRDPRODUCT}" = "NAS4Free" ]; then
		if [ ! -d "${FREEBSD_UPDATE}" ]; then
			echo "Not supported on ${PRDPRODUCT} platform."
			exit 1
		fi
	else
		echo "Unsupported platform!"; exit 1
	fi

	if [ ! -z "$(freebsd-version | grep -i HBSD)" ]; then
		echo "Not supported on HardenedBSD."
		exit 1
	fi

	if [ -d "${bastille_jailsdir}/${TARGET}" ]; then 
		if ! cat "${bastille_jailsdir}/${TARGET}/fstab" 2>/dev/null | grep -w "${TARGET}" | grep -qw "/.*/.bastille"; then
			if [ -f "${bastille_jailsdir}/${TARGET}/root/COPYRIGHT" ]; then
				if [ "$(jls name | grep -w "${TARGET}")" ]; then
					# Update a thick container.
					CURRENT_VERSION=$(jexec -l ${TARGET} freebsd-version)
					if [ -z "${CURRENT_VERSION}" ]; then
						echo "Can't determine '${TARGET}' version."
						exit 1
					else
						env PAGER="/bin/cat" ${FREEBSD_UPDATE}/freebsd-update --not-running-from-cron -f ${FREEBSD_UPDATE}/freebsd-update.conf \
						-d ${CWDIR}/freebsd-update -b "${bastille_jailsdir}/${TARGET}/root" fetch --currently-running "${CURRENT_VERSION}"
						${FREEBSD_UPDATE}/freebsd-update --not-running-from-cron -f ${FREEBSD_UPDATE}/freebsd-update.conf \
						-d ${CWDIR}/freebsd-update -b "${bastille_jailsdir}/${TARGET}/root" install --currently-running "${CURRENT_VERSION}"
					fi
				else
					echo "Container not running."
					echo "See 'bastille start ${TARGET}'."
					exit 1
				fi
			else
				echo "${TARGET} state is unknown."
				exit 1
			fi
		else
			echo "${TARGET} is not a thick container."
			exit 1
		fi
	else
		if [ -d "${bastille_releasesdir}/${TARGET}" ]; then
			# Update container base(affects base child containers).
			env PAGER="/bin/cat" ${FREEBSD_UPDATE}/freebsd-update --not-running-from-cron -f ${FREEBSD_UPDATE}/freebsd-update.conf \
			-d ${CWDIR}/freebsd-update -b "${bastille_releasesdir}/${TARGET}" fetch --currently-running "${TARGET}"
			${FREEBSD_UPDATE}/freebsd-update --not-running-from-cron -f ${FREEBSD_UPDATE}/freebsd-update.conf \
			-d ${CWDIR}/freebsd-update -b "${bastille_releasesdir}/${TARGET}" install --currently-running "${TARGET}"
		else
			echo "${TARGET} not found. See bootstrap."
			exit 1
		fi
	fi
	exit 0
}

release_upgrade()
{
	if [ -d "${bastille_releasesdir}/${TARGET}" ]; then
		if [ -f "${bastille_releasesdir}/${TARGET}/COPYRIGHT" ]; then
			if [ "${TARGET}" = "${RELEASE}" ]; then
				echo "Specified releases name match."
				exit 0
			fi
			# Upgrade a release base.
			echo "=> Run the command below several times when asked to finish installing updates."
			echo "bastille-init install ${TARGET}"
			echo
			env PAGER="/bin/cat" ${FREEBSD_UPDATE}/freebsd-update --not-running-from-cron -f ${FREEBSD_UPDATE}/freebsd-update.conf \
			-d ${CWDIR}/freebsd-update -b "${bastille_releasesdir}/${TARGET}" --currently-running "${TARGET}" -r ${RELEASE} upgrade
			echo
			echo "=> Please run: 'bastille-init install ${TARGET}' to finish installing updates."
		else
			echo "Unknown ${RELEASE}. See bootstrap."; exit 1
		fi
	else
		echo "${TARGET} not found. See bootstrap."; exit 1
	fi
	exit 0
}

release_install()
{
	if [ -d "${bastille_releasesdir}/${TARGET}" ]; then 
			if [ -f "${bastille_releasesdir}/${TARGET}/COPYRIGHT" ]; then
					# Finish installing upgrade on a thick container.
					env PAGER="/bin/cat" ${FREEBSD_UPDATE}/freebsd-update --not-running-from-cron -f ${FREEBSD_UPDATE}/freebsd-update.conf \
					-d ${CWDIR}/freebsd-update -b "${bastille_releasesdir}/${TARGET}" install
			else
				echo "${TARGET} state is unknown."
				exit 1
			fi
	else
		echo "${TARGET} not found. See bootstrap."; exit 1
	fi
	exit 0
}

release_change()
{
	if [ ! -z "$(freebsd-version | grep -i HBSD)" ]; then
		echo "Not supported on HardenedBSD."
		exit 1
	fi

	# Verify for user input and handle some errors.
	if [ -d "${bastille_jailsdir}/${TARGET}" ]; then
		if [ -d "${bastille_releasesdir}/${RELEASE}" ]; then
			if [ -f "${bastille_releasesdir}/${RELEASE}/COPYRIGHT" ]; then
				# Check if the container is running.
				if [ "$(jls name | grep -w "${TARGET}")" ]; then
					echo "${TARGET} running."
					echo "See 'bastille stop ${TARGET}'."
					exit 1
				elif [ "${RELEASE}" = "${NEWRELEASE}" ]; then
					echo "Specified releases name match."
					exit 0
				fi
				if [ -d "${bastille_releasesdir}/${NEWRELEASE}" ]; then
					if [ -f "${bastille_releasesdir}/${NEWRELEASE}/COPYRIGHT" ]; then
						if [ -f "${bastille_jailsdir}/${TARGET}/fstab" ]; then
							# Check if is a thin container.
							if cat "${bastille_jailsdir}/${TARGET}/fstab" | grep "${RELEASE}" | grep -qw "/.*/.bastille"; then
								# If the previous conditions meets, proceed with the container base upgrade.
								sed -i '' "s/${RELEASE}/${NEWRELEASE}/g" ${bastille_jailsdir}/${TARGET}/fstab
								echo "${TARGET} release changed to ${NEWRELEASE}."
							elif cat "${bastille_jailsdir}/${TARGET}/fstab" | grep "${NEWRELEASE}" | grep -qw "/.*/.bastille"; then
								echo "${TARGET} already using ${NEWRELEASE}."
								exit 0
							else
								if cat "${bastille_jailsdir}/${TARGET}/fstab" | grep -qw "/.*/.bastille"; then
									echo "${TARGET} container does not use ${RELEASE}."; exit 1
								else
									echo "${TARGET} is not a thin container."; exit 1
								fi
							fi
						else
							echo "${TARGET} fstab not found."; exit 1
						fi
					else
						echo "Unknown ${NEWRELEASE}. See bootstrap."; exit 1
					fi
				else
					echo "${NEWRELEASE} not found, bootstrap starting...."
					bastille bootstrap ${NEWRELEASE}
					if [ ! $? -ne 0 ]; then
						release_change
					fi
				fi
			else
				echo "Unknown ${RELEASE}. See bootstrap."; exit 1
			fi
		else
			echo "${RELEASE} not found. See bootstrap."; exit 1
		fi
	else
		echo "${TARGET} not found. See create."; exit 1
	fi
	exit 0
}

thickjail_upgrade()
{
	# Workaround since XigmaNAS does not ship with freebsd-update command.

	if [ "${PRDPRODUCT}" = "XigmaNAS" -o "${PRDPRODUCT}" = "NAS4Free" ]; then
		if [ ! -d "${FREEBSD_UPDATE}" ]; then
			echo "Not supported on ${PRDPRODUCT} platform."
			exit 1
		fi
	else
		echo "Unsupported platform!"; exit 1
	fi

	if [ ! -z "$(freebsd-version | grep -i HBSD)" ]; then
		echo "Not supported on HardenedBSD."
		exit 1
	fi

	# Verify for user input and handle some errors.
	if [ -d "${bastille_jailsdir}/${TARGET}" ]; then 
		if ! cat "${bastille_jailsdir}/${TARGET}/fstab" 2>/dev/null | grep -w "${TARGET}" | grep -qw "/.*/.bastille"; then
			if [ -f "${bastille_jailsdir}/${TARGET}/root/COPYRIGHT" ]; then
				if [ "$(jls name | grep -w "${TARGET}")" ]; then
					# Upgrade a thick container.
					echo "=> Run the command below several times when asked to finish installing updates."
					echo "bastille-init install ${TARGET}"
					echo
					CURRENT_VERSION=$(jexec -l ${TARGET} freebsd-version)
					env PAGER="/bin/cat" ${FREEBSD_UPDATE}/freebsd-update --not-running-from-cron -f ${FREEBSD_UPDATE}/freebsd-update.conf \
					-d ${CWDIR}/freebsd-update -b "${bastille_jailsdir}/${TARGET}/root" --currently-running "${CURRENT_VERSION}" -r ${RELEASE} upgrade
					echo
					echo "=> Please run: 'bastille-init install ${TARGET}' to finish installing updates."
				else
					echo "Container not running."
					echo "See 'bastille start ${TARGET}'."
					exit 1
				fi
			else
				echo "${TARGET} state is unknown."
				exit 1
			fi
		else
			echo "${TARGET} is not a thick container."
			exit 1
		fi
	elif [ -d "${bastille_releasesdir}/${RELEASE}" ]; then
		# Try to upgrade a release instead.
		release_upgrade
	fi
	exit 0
}

thickjail_install()
{
	# Workaround since XigmaNAS does not ship with freebsd-update command.

	if [ "${PRDPRODUCT}" = "XigmaNAS" -o "${PRDPRODUCT}" = "NAS4Free" ]; then
		if [ ! -d "${FREEBSD_UPDATE}" ]; then
			echo "Not supported on ${PRDPRODUCT} platform."
			exit 1
		fi
	else
		echo "Unsupported platform!"; exit 1
	fi

	if [ ! -z "$(freebsd-version | grep -i HBSD)" ]; then
		echo "Not supported on HardenedBSD."
		exit 1
	fi

	if [ -d "${bastille_jailsdir}/${TARGET}" ]; then 
		if ! cat "${bastille_jailsdir}/${TARGET}/fstab" 2>/dev/null | grep -w "${TARGET}" | grep -qw "/.*/.bastille"; then
			if [ -f "${bastille_jailsdir}/${TARGET}/root/COPYRIGHT" ]; then
				if [ "$(jls name | grep -w "${TARGET}")" ]; then
					# Finish installing upgrade on a thick container.
					env PAGER="/bin/cat" ${FREEBSD_UPDATE}/freebsd-update --not-running-from-cron -f ${FREEBSD_UPDATE}/freebsd-update.conf \
					-d ${CWDIR}/freebsd-update -b "${bastille_jailsdir}/${TARGET}/root" install
				else
					echo "Container not running."
					echo "See 'bastille start ${TARGET}'."
					exit 1
				fi
			else
				echo "${TARGET} state is unknown."
				exit 1
			fi
		else
			echo "${TARGET} is not a thick container."
			exit 1
		fi
	elif [ -d "${bastille_releasesdir}/${RELEASE}" ]; then
		# Try to upgrade a release instead.
		release_install
	fi
	exit 0
}

zfs_activate()
{
	# Check if ZFS is already configured.
	# Always enforce ZFS activation below "/mnt/" from the extension.
	if echo "${BASTILLE_ZFS_ZPOOL_MOUNTPOINT_TRIM}" | grep -qw "${CWDIR_TRIM}$"; then

		BASTILLE_DIR=$(echo "${CWDIR}" | grep -o '[^/]*$')
		if [ "${bastille_zfs_prefix}" != "${BASTILLE_DIR}" ]; then
			error_notify "Invalid ZFS configuration."
		fi
		if zfs list "${bastille_zfs_zpool}/${BASTILLE_DIR}" > /dev/null 2>&1; then
			echo "Bastille ZFS is already configured."
			sysrc -f ${CWDIR}${EXTCONF} ZFS_ACTIVATED="YES" >/dev/null 2>&1
			exit 0
		else
			BASTILLE_DIRS="cache jails logs releases templates"
			for dir in ${BASTILLE_DIRS}; do
				if [ -d "${CWDIR}/${dir}" ]; then
					# Stop if any of the listed dirs already exist.
					error_notify "Bastille has been bootstrapped already, aborting."
				fi
			done
		fi

		echo "Enabling ZFS on ${PRDNAME} Extension..."
		# Confirm before conversion.
		while :
			do
				read -p "Do you really wish to enable ZFS for ${PRDNAME} Extension? [y/N]:" yn
				case ${yn} in
				[Yy]) break;;
				[Nn]) exit 0;;
				esac
			done
		echo "Proceeding..."

		if [ "${bastille_zfs_enable}" = "YES" ]; then
			if [ ! -z "${bastille_zfs_zpool}" ]; then
				if zfs list "${bastille_zfs_zpool}" > /dev/null 2>&1; then
					if ! zfs list "${bastille_zfs_zpool}/${BASTILLE_DIR}" > /dev/null 2>&1; then
						echo "Renaming existing '${BASTILLE_DIR}' directory"
						mv ${CWDIR} ${CWDIR}.old
						echo "Creating a new ZFS dataset for '${BASTILLE_DIR}'"
						zfs create ${bastille_zfs_options} ${bastille_zfs_zpool}/${bastille_zfs_prefix}
						if [ $? -ne 0 ]; then
							MSG="Failed to enable ZFS, reverting changes."
							echo "${MSG}"
							mv ${CWDIR}.old ${CWDIR}
							logger -t "${SCRIPTNAME}" "${MSG}"
							error_notify "${MSG}"
						else
							echo "Synchronizing '${BASTILLE_DIR}' data on new dataset"
							rsync -a ${CWDIR}.old/ ${CWDIR}/
						fi
					else	
						echo "Bastille ZFS is already configured."
					fi
				else
					error_notify "ERROR: ${bastille_zfs_zpool} is not a ZFS pool/dataset."
				fi
			else
				error_notify "Bastille ZPOOL is not set."
			fi
			echo "ZFS Enabled for ${PRDNAME} Extension successfully."
		else
			error_notify "Bastille ZFS option is not set."
		fi
	else
		error_notify "Invalid ZFS configuration."
	fi
	sysrc -f ${CWDIR}${EXTCONF} ZFS_ACTIVATED="YES" >/dev/null 2>&1
	exit 0
}

pkg_upgrade()
{
	# Re-fetch bastille package and extract.
	if [ -f "${CWDIR}/${FULLAPPNAME}${BASTILLEPATH}/${APPNAME}" ]; then
		bastille_upgrade
	else
		bastille_initial_download
	fi

	# Check for extension updates.
	extension_upgrade
}

reset_install()
{
	# Reset the extension environment.
	echo "Removing extension files..."
	if [ -f "${CWDIR}/conf/bastille_config" ]; then
		rm -rf ${CWDIR}/conf/bastille_config
	fi
	if [ -d "${CWDIR}/${FULLAPPNAME}" ]; then
		rm -rf ${CWDIR}/${FULLAPPNAME}
	fi
	if [ -d "${CWDIR}/download" ]; then
		rm -rf ${CWDIR}/download
	fi
	if [ -f "${CWDIR}/version" ]; then
		rm -f ${CWDIR}/version
	fi

	# Set default config.
	sysrc -f ${CWDIR}${EXTCONF} GUI_ENABLE=YES INSTALL_DIR=${CWDIR} >/dev/null 2>&1

	echo ""
	echo "*************************************************************************************************************"
	echo "* The configuration was reset, please go to [Extensions > Bastille > Configuration] to configure bastille.  *"
	echo "* Alternatively you can edit the '/usr/local/etc/bastille/bastille.conf' file manually.                     *"
	echo "*************************************************************************************************************"
	echo ""
	echo "Notice: If Linux Jail support was enabled, please execute the below command to re-enable it:"
	echo "==> sysrc -f ${CWDIR}${EXTCONF} LINUX_COMPAT_SUPPORT=\"YES\""
	echo ""
}

remove_addon()
{
	# Confirm for addon removal.
	while :
		do
			read -p "Do you wish to proceed with the ${FULLAPPNAME} removal? [y/N]:" yn
			case ${yn} in
			[Yy]) break;;
			[Nn]) exit 0;;
			esac
		done
	echo "Proceeding..."

	# Check for working platform and remove symlinks.
	if [ "${PRDPLATFORM}" = "x64-embedded" ] || [ "${PRDPLATFORM}" = "x64-full" ]; then
		if [ -d "${USRLOCAL}/share/licenses/${APPNAME}-*" ]; then
			rm -rf ${USRLOCAL}/share/licenses/${APPNAME}-*
		fi
		if [ -d $"{USRLOCAL}/share/locale-bastille" ]; then
			rm -rf ${USRLOCAL}/share/locale-bastille
		fi
		if [ -f "${USRLOCAL}/etc/rc.d/${APPNAME}" ]; then
			rm -f ${USRLOCAL}/etc/rc.d/${APPNAME}
		fi
		if [ -f "${USRLOCAL}/etc/${APPNAME}.conf" ]; then
			rm -f ${USRLOCAL}/etc/${APPNAME}.conf
		fi
		if [ -f "${BASTILLEPATH}/${APPNAME}" ]; then
			rm -f ${BASTILLEPATH}/${APPNAME}
		fi
		if [ -f "${USRLOCAL}/sbin/${APPNAME}-init" ]; then
			rm -rf ${USRLOCAL}/sbin/${APPNAME}-init
		fi
		if [ -d "${VARLOG}/${APPNAME}" ]; then
			rm -rf ${VARLOG}/${APPNAME}
		fi
	fi

	# Remove extension and GUI components.
	if [ -f "${WWWPATH}/bastille_manager_gui.php" ]; then
		rm -f ${WWWPATH}/bastille_manager_*.php
	fi
	if [ -d "${WWWPATH}/ext/bastille" ]; then
		rm -rf ${WWWPATH}/ext/bastille
	fi
	if [ -f "${USRLOCAL}/bin/${APPNAME}" ]; then
		rm -rf ${USRLOCAL}/bin/${APPNAME}
	fi
	if [ -d "${VARLOG}/${APPNAME}" ]; then
		rm -rf ${VARLOG}/${APPNAME}
	fi

	# Remove empty ext folder to prevent empty "Extensions" tab.
	if [ -d "${WWWPATH}/ext" ]; then
		if [ ! "$(ls -A ${WWWPATH}/ext)" ]; then
			rm -R ${WWWPATH}/ext
		fi
	fi

	# Remove addon related files and folders only-
	# to protect any user-created custom files-
	# as well as for the containers dirs/files.
	FILES="conf download freebsd-update gui locale-bastille log bastille-dist LICENSE README.md postinit CHANGELOG version bastille-init"
	for FILE in ${FILES}; do
		if [ -f "${CWDIR}/${FILE}" ] || [ -d "${CWDIR}/${FILE}" ]; then
			rm -rf ${CWDIR}/${FILE}
		fi
	done

	BIN_FILES="/usr/local/bin/ar /usr/local/bin/jib /usr/sbin/setfib /usr/bin/sum /usr/bin/diff3 /usr/bin/makewhatis"
	for FILE in ${BIN_FILES}; do
		if [ -f "${FILE}" ]; then
			rm -rf ${FILE}
		fi
	done

	# Don't remove this files on 13.x versions since they are part of the base.
	if [ "$(freebsd-version | cut -d '.' -f1)" -le 12 ]; then
		PF_FILES="/sbin/pfctl /sbin/pfilctl /sbin/pflogd /etc/pf.os"
		for FILE in ${PF_FILES}; do
			if [ -f "${FILE}" ]; then
				rm -rf ${FILE}
			fi
		done
	fi

	echo "Done!"
	echo "Please manually remove the Bastile Extension Command Script from the WebGUI."
	exit 0
}

get_versions()
{
	# Get product versions.
	if [ -f "${CWDIR}/version" ]; then
		APPVERSION=$(cat ${CWDIR}/version)
	else
		APPVERSION="version file not found!"
	fi

	# Display product versions.
	BASTILLEVER=$(${USRLOCAL}/bin/bastille --version)
	echo "Bastille version: ${BASTILLEVER}"
	echo "Extension version: ${APPVERSION}"
	exit 0
}

ext_start()
{
	if sysrc -f ${CWDIR}${EXTCONF} -qn LINUX_COMPAT_SUPPORT | grep -q "YES"; then
		${CWDIR}/unionfs.sh load_kmods
		${CWDIR}/unionfs.sh unionfs_on
	fi

	# Start bastille jails.
	if [ -d "${CWDIR}/jails" ]; then
		JAIL_LIST=$(bastille list jail)
		for jail in ${JAIL_LIST}; do
			if grep -qw "${jail}_AUTO_START=\"YES\"" ${CWDIR}${EXTCONF}; then
				if ! jls | sed "1 d" | awk '{print $3}' | grep -qw ${jail}; then
					bastille start ${jail}
				fi
			fi
		done
	fi

	if [ $? -eq 0 ]; then
		MSG="script has been started successfully!"
		logger -t ${SCRIPTNAME} ${MSG}
		exit 0
	else
		MSG="script started with faults!"
		logger -t ${SCRIPTNAME} ${MSG}
		exit 1
	fi
}

rc_params()
{
	# Bastille required parameters.

	# Set bastille prefix.
	if ! sysrc -f ${BASTILLECONF} -qn bastille_prefix | grep -q "${CWDIR}"; then
		sysrc -f ${BASTILLECONF} bastille_prefix="${CWDIR}" >/dev/null 2>&1
	fi

	# Set required variables.
	if ! sysrc -f ${BASTILLECONF} -qc bastille_compress_gz_options; then
		sysrc -f ${BASTILLECONF} bastille_compress_gz_options="-1 -v" >/dev/null 2>&1
	fi
	if ! sysrc -f ${BASTILLECONF} -qc bastille_decompress_gz_options; then
		sysrc -f ${BASTILLECONF} bastille_decompress_gz_options="-k -d -c -v" >/dev/null 2>&1
	fi
	if ! sysrc -f ${BASTILLECONF} -qc bastille_pf_conf; then
		sysrc -f ${BASTILLECONF} bastille_pf_conf="${CWDIR}/pf.conf" >/dev/null 2>&1
	fi

	# Set bastille.conf location.
	if ! sysrc -f ${CWDIR}${EXTCONF} -n BASTILLE_CONFIG 2>/dev/null | grep -q "${CWDIR}/${FULLAPPNAME}${BASTILLECONF}"; then
		sysrc -f ${CWDIR}${EXTCONF} BASTILLE_CONFIG="${CWDIR}/${FULLAPPNAME}${BASTILLECONF}" >/dev/null 2>&1
	fi

	# Default first network interface.
	ACTIVE_NETIF=$(ifconfig | grep "UP,BROADCAST" | awk -F":" '{print $1}' | sed 1q)
	if ! sysrc -f ${BASTILLECONF} -qn bastille_network_shared | grep -q "${ACTIVE_NETIF}" >/dev/null 2>&1; then
		#echo "" >> ${BASTILLECONF} && echo "## default network interface" >> ${BASTILLECONF}
		sysrc -f ${BASTILLECONF} bastille_network_shared="${ACTIVE_NETIF}" >/dev/null 2>&1
	else
		EXT_NETIF=$(sysrc -f ${BASTILLECONF} -qn bastille_network_shared)
		if [ -z "${EXT_NETIF}" ]; then
			#echo "" >> ${BASTILLECONF} && echo "## default network interface" >> ${BASTILLECONF}
			sysrc -f ${BASTILLECONF} bastille_network_shared="${ACTIVE_NETIF}" >/dev/null 2>&1
		fi
	fi

	# Do't start containers by default.
	if ! sysrc -qn bastille_enable >/dev/null 2>&1; then
		sysrc bastille_enable="NO" >/dev/null 2>&1
	fi

	# Check if sane ZFS is enabled in this setup.
	if [ "${bastille_zfs_enable}" = "YES" ]; then
		if [ -n "${bastille_zfs_zpool}" ]; then
			if zfs list "${bastille_zfs_zpool}" > /dev/null 2>&1; then
				BASTILLE_DIR=$(echo ${CWDIR} | grep -o '[^/]*$')
				if zfs list "${bastille_zfs_zpool}/${BASTILLE_DIR}" > /dev/null 2>&1; then
					sysrc -f ${CWDIR}${EXTCONF} ZFS_SUPPORT="YES" >/dev/null 2>&1
				else
					if echo "${BASTILLE_ZFS_ZPOOL_MOUNTPOINT_TRIM}" | grep -qw "${CWDIR_TRIM}$"; then
						sysrc -f ${CWDIR}${EXTCONF} ZFS_SUPPORT="AVA" >/dev/null 2>&1
					else
						sysrc -f ${CWDIR}${EXTCONF} ZFS_SUPPORT="ERR" >/dev/null 2>&1
					fi
				fi
			else
				sysrc -f ${CWDIR}${EXTCONF} ZFS_SUPPORT="ERR" >/dev/null 2>&1
			fi
		else
			sysrc -f ${CWDIR}${EXTCONF} ZFS_SUPPORT="ERR" >/dev/null 2>&1
		fi
	else
		sysrc -f ${CWDIR}${EXTCONF} ZFS_SUPPORT="NO" >/dev/null 2>&1
	fi
}

update_config()
{
	# Update config based on minimum version.

	# Network parameters.
	if grep -qw 'bastille_jail_loopback=' ${INSTALLPATH}/${BASTILLECONF}; then
		sed -i '' 's/bastille_jail_loopback=/bastille_network_loopback=/' ${INSTALLPATH}/${BASTILLECONF}
	fi
	if grep -qw 'bastille_jail_external=' ${INSTALLPATH}/${BASTILLECONF}; then
		sed -i '' 's/bastille_jail_external=/bastille_network_shared=/' ${INSTALLPATH}/${BASTILLECONF}
	fi
	if grep -qw 'bastille_jail_gateway=' ${INSTALLPATH}/${BASTILLECONF}; then
		sed -i '' 's/bastille_jail_gateway=/bastille_network_gateway=/' ${INSTALLPATH}/${BASTILLECONF}
	fi
	if ! grep -qw 'bastille_url_freebsd=' ${INSTALLPATH}/${BASTILLECONF}; then
		sysrc -f ${INSTALLPATH}/${BASTILLECONF} bastille_url_freebsd="${URL_FREEBSD}"
	fi
	if ! grep -qw 'bastille_url_hardenedbsd=' ${INSTALLPATH}/${BASTILLECONF}; then
		sysrc -f ${INSTALLPATH}/${BASTILLECONF} bastille_url_hardenedbsd="${URL_HARDENEDBSD}"
	fi
	if ! grep -qw 'bastille_url_midnightbsd=' ${INSTALLPATH}/${BASTILLECONF}; then
		sysrc -f ${INSTALLPATH}/${BASTILLECONF} bastille_url_midnightbsd="${URL_MIDNIGHTBSD}"
	fi

	if ! grep -qw 'bastille_network_pf_ext_if=' ${INSTALLPATH}/${BASTILLECONF}; then
		sysrc -f ${INSTALLPATH}/${BASTILLECONF} bastille_network_pf_ext_if="ext_if"
	fi
	if ! grep -qw 'bastille_network_pf_table=' ${INSTALLPATH}/${BASTILLECONF}; then
		sysrc -f ${INSTALLPATH}/${BASTILLECONF} bastille_network_pf_table="jails"
	fi
	if ! grep -qw 'bastille_network_gateway6=' ${INSTALLPATH}/${BASTILLECONF}; then
		sysrc -f ${INSTALLPATH}/${BASTILLECONF} bastille_network_gateway6=""
	fi

	# Template parameters.
	if ! grep -qw 'bastille_template_base=' ${INSTALLPATH}/${BASTILLECONF}; then
		sysrc -f ${INSTALLPATH}/${BASTILLECONF} bastille_template_base="default/base"
	fi
	if ! grep -qw 'bastille_template_empty=' ${INSTALLPATH}/${BASTILLECONF}; then
		sysrc -f ${INSTALLPATH}/${BASTILLECONF} bastille_template_empty=""
	fi
	if ! grep -qw 'bastille_template_thick=' ${INSTALLPATH}/${BASTILLECONF}; then
		sysrc -f ${INSTALLPATH}/${BASTILLECONF} bastille_template_thick="default/thick"
	fi
	if ! grep -qw 'bastille_template_thin=' ${INSTALLPATH}/${BASTILLECONF}; then
		sysrc -f ${INSTALLPATH}/${BASTILLECONF} bastille_template_thin="default/thin"
	fi
	if ! grep -qw 'bastille_template_vnet=' ${INSTALLPATH}/${BASTILLECONF}; then
		sysrc -f ${INSTALLPATH}/${BASTILLECONF} bastille_template_vnet="default/vnet"
	fi

	# Remove deprecated parameters based on minimum version.
	if grep -qw 'bastille_jail_interface' ${INSTALLPATH}/${BASTILLECONF}; then
		sysrc -f ${INSTALLPATH}/${BASTILLECONF} -x bastille_jail_interface
	fi
	if grep -qw 'bastille_jail_addr' ${INSTALLPATH}/${BASTILLECONF}; then
		sysrc -f ${INSTALLPATH}/${BASTILLECONF} -x bastille_jail_addr
	fi

	# Update the bootstrap urls accordingly.
	if ! sysrc -f ${INSTALLPATH}/${BASTILLECONF} -n bastille_url_hardenedbsd | grep -qw "${URL_HARDENEDBSD}"; then
		sysrc -f ${INSTALLPATH}/${BASTILLECONF} bastille_url_hardenedbsd="${URL_HARDENEDBSD}"
	fi
}

bastille_start()
{
	# Start all bastille containers.
	echo "${PRDNAME} Extension: Starting all containers..."
	bastille start ALL
	if [ $? -eq 0 ]; then
		exit 0
	else
		exit 1
	fi
}

bastille_stop()
{
	# Stop all bastille containers.
	echo "${PRDNAME} Extension: Stopping all containers..."
	bastille stop ALL
	if [ $? -eq 0 ]; then
		exit 0
	else
		exit 1
	fi
}

bastille_restart()
{
	# Restart all bastille containers.
	echo "${PRDNAME} Extension: Restarting all containers..."
	bastille restart ALL
	if [ $? -eq 0 ]; then
		exit 0
	else
		exit 1
	fi
}

bastille_init()
{
	# Check for system compatibility.
	if [ ! "${PLATFORM}" = "amd64" ]; then
		echo "Unsupported platform!"; exit 1
	fi 
	# Check for product compatibility.
	if [ ! "${PRDVERSION}" -ge "112" ]; then
		echo "Unsupported version!"; exit 1
	fi

	echo "Initializing ${APPNAME}..."

	# Function calls.
	platform_check
	required_updates
	postinit_cmd
	gui_start
	rc_params
	ext_start

}

linux_compat()
{
	if ping -c1 -t5 freebsd.org > /dev/null; then
		# Manually enable Linux compatibility(Experimental).
		if ! sysrc -f ${CWDIR}${EXTCONF} -qn LINUX_COMPAT_SUPPORT | grep -q "YES"; then
			${CWDIR}/unionfs.sh fetch_pkg && ${CWDIR}/unionfs.sh load_kmods && ${CWDIR}/unionfs.sh unionfs_on && sysrc -f ${CWDIR}${EXTCONF} LINUX_COMPAT_SUPPORT="YES" >/dev/null 2>&1
		else
			echo "Already enabled."
		fi
		exit 0
	else
		echo "Internet connection required to enable this feature."
		exit 1
	fi
}

linux_compat_disable()
{
	if sysrc -f ${CWDIR}${EXTCONF} -qn LINUX_COMPAT_SUPPORT | grep -q "YES"; then
		echo "Disabling Linux jail compatibility support..."
		sysrc -f ${CWDIR}${EXTCONF} -x LINUX_COMPAT_SUPPORT
		echo "Done!, server reboot is recommended."
	fi
	exit 0
}

# Run-time configuration.
runtime_config

TARGET="${2}"
RELEASE="${3}"
NEWRELEASE="${4}"

# Handle additional commands.
case "${OPT}" in
install|--install)
	if [ $# -gt 2 ] || [ $# -lt 2 ]; then
		echo "Usage: ${SCRIPTNAME} [install|--install] [container]"
		exit 1
	fi
	thickjail_install
	;;
upgrade|--upgrade)
	# Check container type to upgrade
	if [ -z "${NEWRELEASE}" ]; then
		if [ $# -gt 3 ] || [ $# -lt 3 ]; then
			echo "Usage: ${SCRIPTNAME} [upgrade|--upgrade] [container|release] [release]"
			exit 1
		fi
		thickjail_upgrade
	else
		if [ $# -gt 4 ] || [ $# -lt 4 ]; then
			echo "Usage: ${SCRIPTNAME} [upgrade|--upgrade] [container] [release] [newrelease]"
			exit 1
		fi
		release_change
	fi
	;;
update|--update)
	if [ $# -gt 2 ] || [ $# -lt 2 ]; then
		echo "Usage: ${SCRIPTNAME} [update|--update] [container] | [release]"
		exit 1
	fi
	jail_update
	;;
clean|--clean)
	for file in ${CWDIR}/freebsd-update/*; do
		rm -rf ${file}
	done
	exit 0
	;;
linux_compat)
	linux_compat
	;;
linux_compat_disable)
	linux_compat_disable
	;;
bastillebsd_update)
	bastille_core_update
	;;
esac

while getopts ":ospruxUvgtZh" option; do
	case ${option} in
		[h]) echo "Usage: ${SCRIPTNAME} -[option] | [container] | [path]";
		echo "Options:"
		echo "        -s  Start All ${PRDNAME} Containers."
		echo "        -p  Stop All ${PRDNAME} Containers."
		echo "        -r  Restart All ${PRDNAME} Containers."
		echo "        -u  Upgrade ${PRDNAME}/Extension packages."
		echo "        -v  Display product versions."
		echo "        -g  Enables the addon GUI."
		echo "        -t  Disable the addon GUI."
		echo "        -Z  Activate ZFS for ${PRDNAME} Extension."
		echo "        -x  Reset ${PRDNAME}/Extension config."
		echo "        -U  Uninstall ${PRDNAME} (Extension files only)."
		#echo "        -L  Enable Linux compatibility(Experimental)."
		echo "        -h  Display this help message."
		echo
		echo "Advanced Usage: ${SCRIPTNAME} [option] [container] [release] | [newrelease]"
		echo "Options:"
		echo "        update|--update    Update a container/release to base -pX release."
		echo "        upgrade|--upgrade  Upgrade a container/release to X.Y-RELEASE."
		echo "        install|--install  Finish installing pending updates on container/release."
		echo "        clean|--clean      Cleanup the FreeBSD update/upgrade cached files/folders."
		echo ""
		echo ""
		echo "Experimental:"
		echo "To enable experimental Linux jail support please execute: \"${SCRIPTNAME} linux_compat\""
		echo "To disable experimental Linux jail support please execute: \"${SCRIPTNAME} linux_compat_disable\""
		echo ""
		echo ""
		echo "Support:"
		echo "To update BastilleBSD core files only to the latest patches and fixes, please execute: \"${SCRIPTNAME} bastillebsd_update\""
		echo ""; exit 0;;
		[o]) OBI_INSTALL="ON";; # To prevent nested PHP-CGI call for installation with OBI.
		[s]) bastille_start;;
		[p]) bastille_stop;;
		[r]) bastille_restart;;
		[u]) pkg_upgrade;;
		[x]) reset_install;;
		[U]) remove_addon;;
		[v]) get_versions;;
		[g]) gui_enable; exit 0 ;; # For enable the addon gui.
		[t]) gui_disable; exit 0 ;; # For disable the addon gui.
		[Z]) zfs_activate;;
		#[L]) linux_compat;;
		[?]) echo "Invalid option, -h for usage."; exit 1;;
	esac
done
bastille_init
