#!/bin/sh
#
# SPDX-License-Identifier: BSD-3-Clause
#
# Copyright (c) 2018-2025, Christer Edwards <christer.edwards@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:
#
# * Redistributions of source code must retain the above copyright notice, this
#   list of conditions and the following disclaimer.
#
# * 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.
#
# * Neither the name of the copyright holder nor the names of its
#   contributors may be used to endorse or promote products derived from
#   this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER 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.

. /usr/local/share/bastille/common.sh

usage() {
    # Build an independent usage for the create command
    # If no option specified, will create a thin jail by default
    error_notify "Usage: bastille create [option(s)] NAME RELEASE IP [INTERFACE]"
    cat << EOF

    Options:

    -B | --bridge                   Enable VNET. INTERFACE must be a bridge.
    -C | --clone                    Create a clone jail (ZFS only).
    -D | --dual                     Use dual (IPv4+6) networking (IP=[inherit|ip_hostname] only).
    -E | --empty                    Create an empty jail (NAME only).
    -g | --gateway IP               Specify a default router/gateway.
    -L | --linux                    Create a Linux jail (experimental).
    -M | --static-mac               Use a static/persistent MAC address (VNET only).
    -n | --nameserver IP            Specify nameserver(s) for the jail. Comma-separated.
         --no-boot                  Set boot=off.
         --no-validate              Do not validate the release name.
         --no-ip                    Create jail without an ip (VNET only).
    -P | --passthrough              Enable VNET. INTERFACE is used as-is.
    -p | --priority VALUE           Set priority value.
    -T | --thick                    Create a thick jail.
         --tags TAG1,TAG2           Apply specified tag(s) to jail. Comma-separated.
    -V | --vnet                     Enable VNET. INTERFACE must be a physical interface.
    -v | --vlan VLANID              Set VLAN ID (VNET only).
    -x | --debug                    Enable debug mode.
    -Z | --zfs-opts zfs,options     Custom zfs options. Comma-separated.

EOF
    exit 1
}

validate_name() {

    local NAME_VERIFY=${NAME}
    local NAME_SANITY="$(echo "${NAME_VERIFY}" | tr -c -d 'a-zA-Z0-9-_')"

    if check_target_exists "${NAME}"; then
        error_exit "[ERROR]: Jail already exists: ${NAME}"
    fi

    # Make sure NAME has only allowed characters
    if [ -n "$(echo "${NAME_SANITY}" | awk "/^[-_].*$/" )" ]; then
        error_exit "[ERROR]: Jail names may not begin with (-|_) characters!"
    elif [ "${VNET_JAIL}" -eq 1 ] && echo "${NAME_VERIFY}" | grep -qE '(-|_)'; then
        error_exit "[ERROR]: VNET jail names may not contain (-|_) characters."
    elif [ "${NAME_VERIFY}" != "${NAME_SANITY}" ]; then
        error_exit "[ERROR]: Jail names may not contain special characters!"
    elif echo "${NAME_VERIFY}" | grep -qE '^[0-9]+$'; then
        error_exit "[ERROR]: Jail names may not contain only digits."
    fi
}

validate_release() {

    # Validate sane release name
    if [ -n "${NAME_VERIFY}" ]; then
        RELEASE="${NAME_VERIFY}"
    else
        usage
    fi

    # Validate Linux releases
    if [ "${PLATFORM_OS}" = "Ubuntu" ] || [ "${PLATFORM_OS}" = "Debian" ]; then
        if [ "${LINUX_JAIL}" -eq 0 ]; then
            error_exit "[ERROR]: Linux releases can only be used with [-l|--linux]"
        fi
    elif [ "${PLATFORM_OS}" = "FreeBSD" ] || [ "${PLATFORM_OS}" = "HardenedBSD" ] || [ "${PLATFORM_OS}" = "MidnightBSD" ]; then
        # Validate release existence
        if [ ! -d "${bastille_releasesdir}/${RELEASE}" ]; then
            error_notify "[ERROR]: Release must be bootstrapped first."
            error_exit "See 'bastille bootstrap'."
        fi
    else
        error_exit "[ERROR]: Unable to validate Platform OS."
    fi

    # Set OS_RELEASE
    OS_RELEASE="$( ${bastille_releasesdir}/${RELEASE}/bin/freebsd-version )"
}

define_ips() {

    IP6_MODE="disable"
    IP4_DEFINITION=""
    IP6_DEFINITION=""
    IP4_ADDR=""
    IP6_ADDR=""

    for ip in ${IP}; do
        validate_ip "${ip}" "${VNET_JAIL}"
    done

    if [ -n "${IP4_ADDR}" ]; then
        if [ "${IP4_ADDR}" = "inherit" ] || [ "${IP4_ADDR}" = "ip_hostname" ]; then
            if [ "${VNET_JAIL}" -eq 1 ]; then
                error_exit "[ERROR]: Unsupported IP option for VNET jail: ${IP4_ADDR}"
            fi
        elif [ "${IP4_ADDR}" = "DHCP" ] || [ "${IP4_ADDR}" = "SYNCDHCP" ] || [ "${IP4_ADDR}" = "0.0.0.0" ]; then
            if [ "${VNET_JAIL}" -eq 0 ]; then
                error_exit "[ERROR]: Unsupported IP option for non-VNET jail: ${IP4_ADDR}"
            fi
        # Warn if IP is in use
        elif ifconfig | grep -qwF "${IP4_ADDR}"; then
            warn 1 "[WARNING]: IP address in use: ${IP4_ADDR}"
        fi
    fi

    if [ -n "${IP6_ADDR}" ]; then
        if [ "${IP6_ADDR}" = "SLAAC" ] && [ "${VNET_JAIL}" -eq 0 ]; then
            error_exit "[ERROR]: Unsupported IP option for standard jail: ${IP6_ADDR}"
        fi
    fi

    # Set interface value
    if [ ! -f "${bastille_jail_conf}" ]; then
        if [ -z "${bastille_network_loopback}" ] && [ -n "${bastille_network_shared}" ]; then
            local bastille_jail_conf_interface=${bastille_network_shared}
        fi
        if [ -n "${bastille_network_loopback}" ] && [ -z "${bastille_network_shared}" ]; then
            local bastille_jail_conf_interface=${bastille_network_loopback}
        fi
        if [ -n "${INTERFACE}" ]; then
            local bastille_jail_conf_interface=${INTERFACE}
        fi
    fi

    # Determine IP/Interface mode
    if [ "${IP4_ADDR}" = "inherit" ]; then
        if [ "${DUAL_STACK}" -eq 1 ]; then
            IP4_DEFINITION="ip4 = ${IP4_ADDR};"
            IP6_DEFINITION="ip6 = ${IP6_ADDR};"
            IP6_MODE="new"
        else
            IP4_DEFINITION="ip4 = ${IP4_ADDR};"
            IP6_DEFINITION=""
            IP6_MODE="disable"
            IP6_ADDR=""
        fi
    elif [ "${IP4_ADDR}" = "ip_hostname" ]; then
        if [ "${DUAL_STACK}" -eq 1 ]; then
            IP4_DEFINITION="${IP4_ADDR};"
            IP6_DEFINITION="${IP6_ADDR};"
            IP6_MODE="new"
        else
            IP4_DEFINITION="${IP4_ADDR};"
            IP6_DEFINITION=""
            IP6_MODE="disable"
            IP6_ADDR=""
        fi
    elif [ "${VNET_JAIL}" -eq 0 ]; then
        if [ -n "${IP4_ADDR}" ]; then
            IP4_DEFINITION="ip4.addr = ${bastille_jail_conf_interface}|${IP4_ADDR};"
        fi
        if [ -n "${IP6_ADDR}" ]; then
            IP6_DEFINITION="ip6.addr = ${bastille_jail_conf_interface}|${IP6_ADDR};"
            IP6_MODE="new"
        fi
    fi
}

validate_netif() {

    local LIST_INTERFACES="$(ifconfig -l)"

    if ! echo "${LIST_INTERFACES} VNET" | grep -qwo "${INTERFACE}"; then
        error_exit "[ERROR]: Invalid interface: ${INTERFACE}"
    elif [ "${VNET_JAIL_STANDARD}" -eq 1 ]; then
        for bridge in $(ifconfig -g bridge | grep -vw "${INTERFACE}bridge"); do
            if ifconfig ${bridge} | grep "member" | grep -owq "${INTERFACE}"; then
                error_exit "[ERROR]: Interface '${INTERFACE}' is already a member of bridge: ${bridge}"
            fi
        done
    else
        info 1 "\nValid interface: ${INTERFACE}"
    fi

    # Don't allow dots in INTERFACE for -V|--vnet jails
    if [ "${VNET_JAIL_STANDARD}" -eq 1 ]; then
        if echo "${INTERFACE}" | grep -q "\."; then
            error_exit "[ERROR]: [-V|--vnet] does not support dots (.) in interface names."
        fi
    fi
}

generate_minimal_conf() {

    cat << EOF > "${bastille_jail_conf}"
${NAME} {
  host.hostname = ${NAME};
  mount.fstab = ${bastille_jail_fstab};
  path = ${bastille_jail_path};
}
EOF
    touch "${bastille_jail_fstab}"
}

generate_jail_conf() {

    if [ "$(sysctl -n security.jail.jailed)" -eq 1 ]; then
        devfs_ruleset_value=0
    else
        devfs_ruleset_value=4
    fi
    cat << EOF > "${bastille_jail_conf}"
${NAME} {
  enforce_statfs = 2;
  devfs_ruleset = ${devfs_ruleset_value};
  exec.clean;
  exec.consolelog = ${bastille_jail_log};
  exec.start = '/bin/sh /etc/rc';
  exec.stop = '/bin/sh /etc/rc.shutdown';
  host.hostname = ${NAME};
  mount.devfs;
  mount.fstab = ${bastille_jail_fstab};
  path = ${bastille_jail_path};
  securelevel = 2;
  osrelease = ${OS_RELEASE};

  ${IP4_DEFINITION}
  ${IP6_DEFINITION}
  ip6 = ${IP6_MODE};
}
EOF
}

generate_linux_jail_conf() {

    cat << EOF > "${bastille_jail_conf}"
${NAME} {
  host.hostname = ${NAME};
  mount.fstab = ${bastille_jail_fstab};
  path = ${bastille_jail_path};
  devfs_ruleset = 4;
  enforce_statfs = 1;

  exec.start = '/bin/true';
  exec.stop = '/bin/true';
  persist;

  allow.mount;
  allow.mount.devfs;

  ${IP4_DEFINITION}
  ${IP6_DEFINITION}
  ip6 = ${IP6_MODE};
}
EOF
}

generate_vnet_jail_conf() {

    if [ "$(sysctl -n security.jail.jailed)" -eq 1 ]; then
        devfs_ruleset_value=0
    else
        devfs_ruleset_value=13
    fi

    NETBLOCK=$(generate_vnet_jail_netblock "${NAME}" "${VNET_INTERFACE_TYPE}" "${bastille_jail_conf_interface}" "${STATIC_MAC}")

    cat << EOF > "${bastille_jail_conf}"
${NAME} {
  enforce_statfs = 2;
  devfs_ruleset = ${devfs_ruleset_value};
  exec.clean;
  exec.consolelog = ${bastille_jail_log};
  exec.start = '/bin/sh /etc/rc';
  exec.stop = '/bin/sh /etc/rc.shutdown';
  host.hostname = ${NAME};
  mount.devfs;
  mount.fstab = ${bastille_jail_fstab};
  path = ${bastille_jail_path};
  securelevel = 2;
  osrelease = ${OS_RELEASE};

${NETBLOCK}
}
EOF
}

post_create_jail() {

    # Common config checks and settings.
    # Using relative paths here.
    # MAKE SURE WE'RE IN THE RIGHT PLACE.
    cd "${bastille_jail_path}" || error_exit "Could not access directory: ${bastille_jail_path}"

    if [ ! -f "${bastille_jail_conf}" ]; then
        if [ -z "${bastille_network_loopback}" ] && [ -n "${bastille_network_shared}" ]; then
            local bastille_jail_conf_interface=${bastille_network_shared}
        fi
        if [ -n "${bastille_network_loopback}" ] && [ -z "${bastille_network_shared}" ]; then
            local bastille_jail_conf_interface=${bastille_network_loopback}
        fi
        if [ -n "${INTERFACE}" ]; then
            local bastille_jail_conf_interface=${INTERFACE}
        fi
    fi

    if [ ! -f "${bastille_jail_fstab}" ]; then
        if [ "${THICK_JAIL}" -eq 0 ] && [ "${CLONE_JAIL}" -eq 0 ]; then
            echo -e "${bastille_releasesdir}/${RELEASE} ${bastille_jail_base} nullfs ro 0 0" > "${bastille_jail_fstab}"
        else
            touch "${bastille_jail_fstab}"
        fi
    fi

    # Generate the jail configuration file.
    if [ "${VNET_JAIL}" -eq 1 ]; then
        generate_vnet_jail_conf
    else
        generate_jail_conf
    fi

}

create_jail() {

    bastille_jail_base="${bastille_jailsdir}/${NAME}/root/.bastille"  ## dir
    bastille_jail_template="${bastille_jailsdir}/${NAME}/root/.template"  ## dir
    bastille_jail_path="${bastille_jailsdir}/${NAME}/root"  ## dir
    bastille_jail_fstab="${bastille_jailsdir}/${NAME}/fstab"  ## file
    bastille_jail_conf="${bastille_jailsdir}/${NAME}/jail.conf"  ## file
    bastille_jail_log="${bastille_logsdir}/${NAME}_console.log"  ## file
    # shellcheck disable=SC2034
    bastille_jail_rc_conf="${bastille_jailsdir}/${NAME}/root/etc/rc.conf" ## file
    # shellcheck disable=SC2034
    bastille_jail_resolv_conf="${bastille_jailsdir}/${NAME}/root/etc/resolv.conf" ## file

    if [ ! -d "${bastille_jailsdir}/${NAME}" ]; then
        if checkyesno bastille_zfs_enable; then
            if [ -n "${bastille_zfs_zpool}" ]; then
                ## create required zfs datasets, mountpoint inherited from system
                if [ "${CLONE_JAIL}" -eq 0 ]; then
                    if ! zfs create ${bastille_zfs_options} "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${NAME}"; then
                        error_exit "[ERROR]: Failed to create jail dataset: ${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${NAME}"
                    fi
                fi
                if [ "${THICK_JAIL}" -eq 0 ] && [ "${CLONE_JAIL}" -eq 0 ]; then
                    if ! zfs create ${bastille_zfs_options} "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${NAME}/root"; then
                        error_exit "[ERROR]: Failed to create jail root dataset: ${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${NAME}/root"
                    fi
                fi
            fi
        else
            if ! mkdir -p "${bastille_jailsdir}/${NAME}/root"; then
                error_exit "[ERROR]: Failed to create jail directory: ${bastille_jailsdir}/${NAME}/root"
            fi
        fi
    # Check if the jail directory has been mounted under UFS (not supported)
    elif [ ! -d "${bastille_jailsdir}/${NAME}/root" ] && ! checkyesno bastille_zfs_enable; then
        if mount | grep "${bastille_jailsdir}/${NAME}" | grep -oq "ufs"; then
            error_exit "[ERROR]: Using UFS mounts for the jail directory is not supported."
        fi
    fi


    ## PoC for Linux jails @hackacad
    if [ "${LINUX_JAIL}" -eq 1 ]; then
        info 1 "\nCreating a linuxjail. This may take a while...\n"
        if [ ! -d "${bastille_jail_base}" ]; then
            mkdir -p "${bastille_jail_base}"
        fi
        mkdir -p "${bastille_jail_path}/dev"
        mkdir -p "${bastille_jail_path}/proc"
        mkdir -p "${bastille_jail_path}/sys"
        mkdir -p "${bastille_jail_path}/home"
        mkdir -p "${bastille_jail_path}/tmp"
        touch "${bastille_jail_path}/dev/shm"
        touch "${bastille_jail_path}/dev/fd"
        cp -RPf ${bastille_releasesdir}/${RELEASE}/* ${bastille_jail_path}/
        echo "${NAME}" > ${bastille_jail_path}/etc/hostname

        if [ ! -d "${bastille_jail_template}" ]; then
            mkdir -p "${bastille_jail_template}"
        fi

        if [ ! -f "${bastille_jail_fstab}" ]; then
            touch "${bastille_jail_fstab}"
        fi
        echo -e "devfs           ${bastille_jail_path}/dev      devfs           rw                      0       0" >> "${bastille_jail_fstab}"
        echo -e "tmpfs           ${bastille_jail_path}/dev/shm  tmpfs           rw,size=1g,mode=1777    0       0" >> "${bastille_jail_fstab}"
        echo -e "fdescfs         ${bastille_jail_path}/dev/fd   fdescfs         rw,linrdlnk             0       0" >> "${bastille_jail_fstab}"
        echo -e "linprocfs       ${bastille_jail_path}/proc     linprocfs       rw                      0       0" >> "${bastille_jail_fstab}"
        echo -e "linsysfs        ${bastille_jail_path}/sys      linsysfs        rw                      0       0" >> "${bastille_jail_fstab}"
        echo -e "/tmp            ${bastille_jail_path}/tmp      nullfs          rw                      0       0" >> "${bastille_jail_fstab}"
        ## removed temporarely / only for X11 jails? @hackacad
        #echo -e "/home           ${bastille_jail_path}/home     nullfs          rw                      0       0" >> "${bastille_jail_fstab}"

        if [ ! -f "${bastille_jail_conf}" ]; then
            if [ -z "${bastille_network_loopback}" ] && [ -n "${bastille_network_shared}" ]; then
                local bastille_jail_conf_interface=${bastille_network_shared}
            fi
            if [ -n "${bastille_network_loopback}" ] && [ -z "${bastille_network_shared}" ]; then
                local bastille_jail_conf_interface=${bastille_network_loopback}
            fi
            if [ -n "${INTERFACE}" ]; then
                local bastille_jail_conf_interface=${INTERFACE}
            fi
        fi
    fi

    if [ "${EMPTY_JAIL}" -eq 0 ] && [ "${LINUX_JAIL}" -eq 0 ]; then
        if [ "${THICK_JAIL}" -eq 0 ] && [ "${CLONE_JAIL}" -eq 0 ]; then
            if [ ! -d "${bastille_jail_base}" ]; then
                mkdir -p "${bastille_jail_base}"
            fi
            if [ ! -d "${bastille_jail_template}" ]; then
                mkdir -p "${bastille_jail_template}"
            fi
        fi

        if [ ! -d "${bastille_jail_path}/usr/local" ]; then
            mkdir -p "${bastille_jail_path}/usr/local"
        fi

        # Check and apply required settings.
        post_create_jail

        if [ "${THICK_JAIL}" -eq 0 ] && [ "${CLONE_JAIL}" -eq 0 ]; then
            LINK_LIST="bin boot lib libexec rescue sbin usr/bin usr/include usr/lib usr/lib32 usr/libdata usr/libexec usr/sbin usr/share usr/src"
            info 1 "\nCreating a thinjail..."
            for link in ${LINK_LIST}; do
                ln -sf /.bastille/${link} ${link}
            done

            # Properly link shared ports on thin jails in read-write.
            if [ -d "${bastille_releasesdir}/${RELEASE}/usr/ports" ]; then
                if [ ! -d "${bastille_jail_path}/usr/ports" ]; then
                    mkdir ${bastille_jail_path}/usr/ports
                fi
                echo -e "${bastille_releasesdir}/${RELEASE}/usr/ports ${bastille_jail_path}/usr/ports nullfs rw 0 0" >> "${bastille_jail_fstab}"
            fi
        fi

        if [ "${THICK_JAIL}" -eq 0 ] && [ "${CLONE_JAIL}" -eq 0 ]; then
            ## rw
            ## copy only required files for thin jails
            FILE_LIST=".cshrc .profile COPYRIGHT dev etc media mnt net proc root tmp var usr/obj usr/tests"
            for files in ${FILE_LIST}; do
                if [ -f "${bastille_releasesdir}/${RELEASE}/${files}" ] || [ -d "${bastille_releasesdir}/${RELEASE}/${files}" ]; then
                    if ! cp -a "${bastille_releasesdir}/${RELEASE}/${files}" "${bastille_jail_path}/${files}"; then
                        ## notify and clean stale files/directories
                        bastille destroy -af "${NAME}"
                        error_exit "[ERROR]: Failed to copy release files. Please retry create!"
                    fi
                fi
            done
        else
            if checkyesno bastille_zfs_enable; then
                if [ -n "${bastille_zfs_zpool}" ]; then
                    if [ "${CLONE_JAIL}" -eq 1 ]; then
                        info 1 "\nCreating a clonejail...\n"
                        ## clone the release base to the new basejail
                        SNAP_NAME="bastille-clone-$(date +%Y-%m-%d-%H%M%S)"
                        # shellcheck disable=SC2140
                        zfs snapshot "${bastille_zfs_zpool}/${bastille_zfs_prefix}/releases/${RELEASE}"@"${SNAP_NAME}"
                        # shellcheck disable=SC2140
                        zfs clone -p "${bastille_zfs_zpool}/${bastille_zfs_prefix}/releases/${RELEASE}"@"${SNAP_NAME}" \
                        "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${NAME}/root"

                        # Check and apply required settings.
                        post_create_jail
                    elif [ "${THICK_JAIL}" -eq 1 ]; then

                        info 1 "\nCreating a thickjail. This may take a while..."

                        ## perform release base replication
                        ## sane bastille zfs options
                        ZFS_OPTIONS=$(echo ${bastille_zfs_options} | sed 's/-o//g')
                        ## send without -R if encryption is enabled
                        if [ "$(zfs get -H -o value encryption "${bastille_zfs_zpool}/${bastille_zfs_prefix}")" = "off" ]; then
                            OPT_SEND="-R"
                        else
                            OPT_SEND=""
                        fi

                        ## take a temp snapshot of the base release
                        SNAP_NAME="bastille-$(date +%Y-%m-%d-%H%M%S)"
                        # shellcheck disable=SC2140
                        zfs snapshot "${bastille_zfs_zpool}/${bastille_zfs_prefix}/releases/${RELEASE}"@"${SNAP_NAME}"

                        ## replicate the release base to the new thickjail and set the default mountpoint
                        # shellcheck disable=SC2140
                        zfs send ${OPT_SEND} "${bastille_zfs_zpool}/${bastille_zfs_prefix}/releases/${RELEASE}"@"${SNAP_NAME}" | \
                        zfs receive "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${NAME}/root"

                        # Do not attempt to set/inherit mountpoint if 'bastille_prefix' and 'bastille_zfs_zpool/bastille_zfs_prefix' are the same
                        if [ "$(zfs get -H -o value mountpoint "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${NAME}/root")" = "/${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${NAME}/root" ]; then
                            # Just set the zfs options to the jail dataset
                            zfs set ${ZFS_OPTIONS} "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${NAME}/root"
                        else
                            zfs set ${ZFS_OPTIONS} mountpoint=none "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${NAME}/root"
                            zfs inherit mountpoint "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${NAME}/root"
                        fi

                        ## cleanup temp snapshots initially
                        # shellcheck disable=SC2140
                        zfs destroy "${bastille_zfs_zpool}/${bastille_zfs_prefix}/releases/${RELEASE}"@"${SNAP_NAME}"
                        # shellcheck disable=SC2140
                        zfs destroy "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${NAME}/root"@"${SNAP_NAME}"
                    fi

                    if [ "$?" -ne 0 ]; then
                        ## notify and clean stale files/directories
                        bastille destroy -af "${NAME}"
                        error_exit "[ERROR]: Failed release base replication. Please retry create!"
                    fi
                fi
            else
                ## copy all files for thick jails
                cp -a "${bastille_releasesdir}/${RELEASE}/" "${bastille_jail_path}"
                if [ "$?" -ne 0 ]; then
                    ## notify and clean stale files/directories
                    bastille destroy -afy "${NAME}"
                    error_exit "[ERROR]: Failed to copy release files. Please retry create!"
                fi
            fi
        fi

        if [ "${LINUX_JAIL}" -eq 0 ]; then
            # Create home directory if missing
            if [ ! -d "${bastille_jail_path}/home" ]; then
                mkdir -p "${bastille_jail_path}/home"
            fi

            ## TZ: configurable (default: empty to use host's time zone)
            if [ -z "${bastille_tzdata}" ]; then
                if [ -e /var/db/zoneinfo ]; then
                    local tzdata="$(cat /var/db/zoneinfo)"
                    ln -s "/usr/share/zoneinfo/${tzdata}" etc/localtime
                # Note that if host has no time zone, FreeBSD assumes UTC anyway
                elif [ -e /etc/localtime ]; then
                    # uses cp as a way to prevent issues with symlinks if the host happens to use that for tz configuration
                    cp -P /etc/localtime etc/localtime
                fi
            else
                ln -s "/usr/share/zoneinfo/${bastille_tzdata}" etc/localtime
            fi

            # Post-creation jail misc configuration
            # Create a dummy fstab file
            touch "etc/fstab"
            # Disables adjkerntz, avoids spurious error messages
            sed -i '' 's|[0-9],[0-9]\{2\}.*[0-9]-[0-9].*root.*kerntz -a|#& # Disabled by bastille|' "etc/crontab"
        fi

        # VNET specific
        if [ "${VNET_JAIL}" -eq 1 ]; then
            # VNET requires jib or jng script
            if [ "${bastille_network_vnet_type}" = "if_bridge" ]; then
                if [ ! "$(command -v jib)" ]; then
                    if [ -f /usr/share/examples/jails/jib ] && [ ! -f /usr/local/bin/jib ]; then
                        install -m 0544 /usr/share/examples/jails/jib /usr/local/bin/jib
                    fi
                fi
            elif [ "${bastille_network_vnet_type}" = "netgraph" ]; then
                if [ ! "$(command -v jng)" ]; then
                    if [ -f /usr/share/examples/jails/jng ] && [ ! -f /usr/local/bin/jng ]; then
                        install -m 0544 /usr/share/examples/jails/jng /usr/local/bin/jng
                    fi
                fi
            fi
        fi
    elif [ "${LINUX_JAIL}" -eq 1 ]; then
        ## Generate configuration for Linux jail
        generate_linux_jail_conf
    elif [ "${EMPTY_JAIL}" -eq 1 ]; then
        ## Generate minimal configuration for empty jail
        generate_minimal_conf
    fi

    # Set strict permissions on the jail by default
    chmod 0700 "${bastille_jailsdir}/${NAME}"

    # Apply boot, depend and priority settings before starting jail
    sysrc -f "${bastille_jailsdir}/${NAME}/settings.conf" boot=${BOOT} >/dev/null
    sysrc -f "${bastille_jailsdir}/${NAME}/settings.conf" depend="" >/dev/null
    sysrc -f "${bastille_jailsdir}/${NAME}/settings.conf" priority="${PRIORITY}" >/dev/null

    # Jail must be started before applying the default template. -- cwells
    if [ "${EMPTY_JAIL}" -eq 0 ]; then
        bastille start "${NAME}"
    elif [ "${EMPTY_JAIL}" -eq 1 ]; then
        # Don't start empty jails unless a template defined.
        if [ -n "${bastille_template_empty}" ]; then
            bastille start "${NAME}"
        fi
    fi

    # Exit if jail was not started, except for empty jails
    if [ "${EMPTY_JAIL}" -eq 0 ]; then
        if ! check_target_is_running "${NAME}"; then
            bastille destroy -afy "${NAME}"
            error_exit "[ERROR]: Failed to create jail: ${NAME}"
        fi
    fi

    if [ "${VNET_JAIL}" -eq 1 ]; then

        if [ -n "${bastille_template_vnet}" ]; then

            # Retrieve epair name from jail.conf
            uniq_epair=$(grep vnet.interface "${bastille_jailsdir}/${NAME}/jail.conf" | awk '{print $3}' | sed 's/;//; s/-/_/g')
            gateway="NO"
            gateway6="NO"
            ifconfig_inet=""
            ifconfig_inet6=""

            # Enable IPv4 if set
            if [ -n "${IP4_ADDR}" ]; then
                if echo "${IP4_ADDR}" | grep -qE '(0[.]0[.]0[.]0|DHCP|SYNCDHCP)'; then
                    ifconfig_inet="SYNCDHCP"
                else
                    # Set IP and Gateway
                    ifconfig_inet="inet ${IP4_ADDR}"
                    if [ -n "${OPT_GATEWAY}" ]; then
                        gateway="${OPT_GATEWAY}"
                    elif [ -n "${bastille_network_gateway}" ]; then
                        gateway="${bastille_network_gateway}"
                    else
                        gateway="$(netstat -4rn | awk '/default/ {print $2}')"
                    fi

                fi
            fi

            # Enable IPv6 if set
            if [ -n "${IP6_ADDR}" ]; then
                ifconfig_inet6="inet6 -ifdisabled"
                if echo "${IP6_ADDR}" | grep -qE 'SLAAC'; then
                    # Enable SLAAC if requested
                    ifconfig_inet6="${ifconfig_inet6} accept_rtadv"
                else
                    # Set IP and Gateway
                    ifconfig_inet6="${ifconfig_inet6} ${IP6_ADDR}"
                    if [ -n "${bastille_network_gateway6}" ]; then
                        gateway6="${bastille_network_gateway6}"
                    else
                        gateway6="$(netstat -6rn | awk '/default/ {print $2}')"
                    fi
                fi
            fi

            # We need to pass IP4 and IP6 separately
            ifconfig="${ifconfig_inet}"
            ifconfig6="${ifconfig_inet6}"

            # Set jail interface description if "if_bridge"
            if [ "${bastille_network_vnet_type}" = "if_bridge" ]; then
                # Use interface name as INTERFACE+VNET when PASSTHROUGH is selected
                # Use default "vnet0" otherwise
                if [ "${VNET_JAIL_PASSTHROUGH}" -eq 1 ]; then
                    bastille template "${NAME}" ${bastille_template_vnet} --arg EXT_INTERFACE="${INTERFACE}" --arg INTERFACE="${uniq_epair}" --arg VNET="${INTERFACE}" --arg GATEWAY="${gateway}" --arg GATEWAY6="${gateway6}" --arg IFCONFIG="${ifconfig}" --arg IFCONFIG6="${ifconfig6}"
                else
                    bastille template "${NAME}" ${bastille_template_vnet} --arg EXT_INTERFACE="${INTERFACE}" --arg INTERFACE="${uniq_epair}" --arg VNET="vnet0" --arg GATEWAY="${gateway}" --arg GATEWAY6="${gateway6}" --arg IFCONFIG="${ifconfig}" --arg IFCONFIG6="${ifconfig6}"
                fi
            elif [ "${bastille_network_vnet_type}" = "netgraph" ]; then
                # Use interface name as INTERFACE+VNET when PASSTHROUGH is selected
                # Use default "vnet0" otherwise
                if [ "${VNET_JAIL_PASSTHROUGH}" -eq 1 ]; then
                    bastille template "${NAME}" ${bastille_template_vnet} --arg EXT_INTERFACE="${INTERFACE}" --arg INTERFACE="${uniq_epair}" --arg VNET="${INTERFACE}" --arg GATEWAY="${gateway}" --arg GATEWAY6="${gateway6}" --arg IFCONFIG="${ifconfig}" --arg IFCONFIG6="${ifconfig6}"
                else
                    bastille template "${NAME}" ${bastille_template_vnet} --arg EXT_INTERFACE="${INTERFACE}" --arg INTERFACE="${uniq_epair}" --arg VNET="vnet0" --arg GATEWAY="${gateway}" --arg GATEWAY6="${gateway6}" --arg IFCONFIG="${ifconfig}" --arg IFCONFIG6="${ifconfig6}"
                fi
            fi

            # Add VLAN ID if it was given
            if [ -n "${VLAN_ID}" ]; then
                bastille template "${NAME}" ${bastille_template_vlan} --arg VLANID="${VLAN_ID}" --arg IFCONFIG="${ifconfig}"
            fi

        fi
    fi

    # Apply thick/clone/empty/linux/thin template
    if [ "${THICK_JAIL}" -eq 1 ]; then
        if [ -n "${bastille_template_thick}" ]; then
            bastille template "${NAME}" ${bastille_template_thick} --arg BASE_TEMPLATE="${bastille_template_base}" --arg HOST_RESOLV_CONF="${bastille_resolv_conf}"
        fi
    elif [ "${CLONE_JAIL}" -eq 1 ]; then
        if [ -n "${bastille_template_clone}" ]; then
            bastille template "${NAME}" ${bastille_template_clone} --arg BASE_TEMPLATE="${bastille_template_base}" --arg HOST_RESOLV_CONF="${bastille_resolv_conf}"
        fi
    elif [ "${EMPTY_JAIL}" -eq 1 ]; then
        if [ -n "${bastille_template_empty}" ]; then
            bastille template "${NAME}" ${bastille_template_empty} --arg BASE_TEMPLATE="${bastille_template_base}" --arg HOST_RESOLV_CONF="${bastille_resolv_conf}"
        fi
    ## Using templating function to fetch necessary packges @hackacad
    elif [ "${LINUX_JAIL}" -eq 1 ]; then
        info 1 "\nFetching packages..."
        jexec -l "${NAME}" /bin/bash -c "DEBIAN_FRONTEND=noninteractive rm /var/cache/apt/archives/rsyslog*.deb"
        jexec -l "${NAME}" /bin/bash -c "DEBIAN_FRONTEND=noninteractive dpkg --force-depends --force-confdef --force-confold -i /var/cache/apt/archives/*.deb"
        jexec -l "${NAME}" /bin/bash -c "DEBIAN_FRONTEND=noninteractive dpkg --force-depends --force-confdef --force-confold -i /var/cache/apt/archives/*.deb"
        jexec -l "${NAME}" /bin/bash -c "chmod 777 /tmp"
        jexec -l "${NAME}" /bin/bash -c "apt update"
    else
        # Thin jail.
        if [ -n "${bastille_template_thin}" ]; then
            bastille template "${NAME}" ${bastille_template_thin} --arg BASE_TEMPLATE="${bastille_template_base}" --arg HOST_RESOLV_CONF="${bastille_resolv_conf}"
        fi
    fi

    # Apply nameserver (if set)
    if [ -n "${OPT_NAMESERVER}" ]; then
        sed -i '' "/^nameserver.*/d" "${bastille_jail_resolv_conf}"
        for ns in $(echo ${OPT_NAMESERVER} | sed 's/,/ /g'); do
            echo "nameserver ${ns}" >> "${bastille_jail_resolv_conf}"
        done
    fi

    # Apply tags (if set)
    if [ -n "${OPT_TAGS}" ]; then
        bastille tags "${NAME}" add "${OPT_TAGS}"
    fi

    # Apply values changed by the template. -- cwells
    if [ "${EMPTY_JAIL}" -eq 0 ] && [ "${LINUX_JAIL}" -eq 0 ]; then
        bastille restart "${NAME}"
    elif [ "${EMPTY_JAIL}" -eq 1 ]; then
        # Don't restart empty jails unless a template defined.
        if [ -n "${bastille_template_empty}" ]; then
            bastille restart "${NAME}"
        fi
    fi
}

bastille_root_check

# Handle options.
BOOT="on"
EMPTY_JAIL=0
THICK_JAIL=0
CLONE_JAIL=0
VNET_JAIL=0
VNET_JAIL_STANDARD=0
VNET_JAIL_BRIDGE=0
VNET_JAIL_PASSTHROUGH=0
NO_IP=0
VLAN_ID=""
LINUX_JAIL=0
STATIC_MAC=0
DUAL_STACK=0
VALIDATE_RELEASE=1
PRIORITY="99"
OPT_GATEWAY=""
OPT_NAMESERVER=""
OPT_TAGS=""
while [ $# -gt 0 ]; do
    case "${1}" in
        -h|--help|help)
            usage
            ;;
        -B|--bridge)
            VNET_JAIL=1
            VNET_JAIL_BRIDGE=1
            shift
            ;;
        -C|--clone)
            CLONE_JAIL=1
            shift
            ;;
        -D|--dual)
            DUAL_STACK=1
            shift
            ;;
        -E|--empty)
            EMPTY_JAIL=1
            shift
            ;;
        -g|--gateway|--defaultrouter)
            OPT_GATEWAY="${2}"
            # Validate gateway
            if [ -n "${OPT_GATEWAY}" ]; then
                if ! validate_ip "${OPT_GATEWAY}" >/dev/null 2>/dev/null; then
                    error_exit "[ERROR]: Not a valid gateway: ${OPT_GATEWAY}"
                fi
            fi
            shift 2
            ;;
        -L|--linux)
            LINUX_JAIL=1
            shift
            ;;
        -M|--static-mac)
            STATIC_MAC=1
            shift
            ;;
        -n|--nameserver)
            OPT_NAMESERVER="${2}"
            # Validate nameserver
            if [ -n "${OPT_NAMESERVER}" ]; then
                for nameserver in $(echo ${OPT_NAMESERVER} | sed 's/,/ /g'); do
                    if ! validate_ip "${nameserver}" >/dev/null 2>/dev/null; then
                        error_exit "[ERROR]: Invalid nameserver(s): ${OPT_NAMESERVER}"
                    fi
                done
            fi
            shift 2
            ;;
        --no-boot)
            BOOT="off"
            shift
            ;;
        --no-ip)
            NO_IP=1
            shift
            ;;
        --no-validate|no-validate)
            VALIDATE_RELEASE=0
            shift
            ;;
        -P|--passthrough)
            VNET_JAIL=1
            VNET_JAIL_PASSTHROUGH=1
            shift
            ;;
        -p|--priority)
            if echo "${2}" | grep -Eoq "^[0-9]+$"; then
                PRIORITY="${2}"
                shift 2
            else
                error_exit "Not a valid priority value: \"${2}\""
            fi
            ;;
        --tag|--tags)
            OPT_TAGS="${2}"
            shift 2
            ;;
        -T|--thick)
            THICK_JAIL=1
            shift
            ;;
        -V|--vnet)
            VNET_JAIL=1
            VNET_JAIL_STANDARD=1
            shift
            ;;
        -v|--vlan)
            if echo "${2}" | grep -Eq '^[0-9]+$'; then
                VLAN_ID="${2}"
            else
                error_exit "Not a valid VLAN ID: ${2}"
            fi
            shift 2
            ;;
        -x|--debug)
            enable_debug
            shift
            ;;
        -Z|--zfs-opts)
            bastille_zfs_options="${2}"
            shift 2
            ;;
        -*)
            for opt in $(echo ${1} | sed 's/-//g' | fold -w1); do
                case ${opt} in
                    B) VNET_JAIL=1 VNET_JAIL_BRIDGE=1 ;;
                    C) CLONE_JAIL=1 ;;
                    D) DUAL_STACK=1 ;;
                    E) EMPTY_JAIL=1 ;;
                    L) LINUX_JAIL=1 ;;
                    M) STATIC_MAC=1 ;;
                    P) VNET_JAIL=1 VNET_JAIL_PASSTHROUGH=1 ;;
                    T) THICK_JAIL=1 ;;
                    V) VNET_JAIL=1 VNET_JAIL_STANDARD=1 ;;
                    x) enable_debug ;;
                    *) error_exit "[ERROR]: Unknown Option: \"${1}\"" ;;
                esac
            done
            shift
            ;;
        *)
            break
            ;;
    esac
done

# Validate options
# Do not allow EMPTY_JAIL with any other jail type
if [ "${EMPTY_JAIL}" -eq 1 ]; then
    if [ "${CLONE_JAIL}" -eq 1 ] || [ "${THICK_JAIL}" -eq 1 ] || [ "${VNET_JAIL}" -eq 1 ] || [ "${LINUX_JAIL}" -eq 1 ]; then
        error_exit "[ERROR]: Empty jail option can't be used with other options."
    fi
# Do not allow LINUX_JAIL with any other jail type
elif [ "${LINUX_JAIL}" -eq 1 ]; then
    if [ "${EMPTY_JAIL}" -eq 1 ] || [ "${VNET_JAIL}" -eq 1 ] || [ "${THICK_JAIL}" -eq 1 ] || [ "${CLONE_JAIL}" -eq 1 ]; then
        error_exit "[ERROR]: Linux jail option can't be used with other options."
    fi
# Do not allow CLONE_JAIL and THICK_JAIL together
elif [ "${CLONE_JAIL}" -eq 1 ] && [ "${THICK_JAIL}" -eq 1 ]; then
    error_exit "[ERROR]: Clonejail and Thickjail can't be used together."
# VLAN_ID can only be used with VNET jails
elif [ "${VNET_JAIL}" -eq 0 ] && [ -n "${VLAN_ID}" ]; then
    error_exit "[ERROR]: VLANs can only be used with VNET jails."
elif [ "${VNET_JAIL}" -eq 0 ] && [ "${NO_IP}" -eq 1 ]; then
    error_exit "[ERROR]: [--no-ip] can only be used with VNET jails."
# Don't allow multiple VNET jail types
elif { [ "${VNET_JAIL_STANDARD}" -eq 1 ] && [ "${VNET_JAIL_BRIDGE}" -eq 1 ]; } || \
     { [ "${VNET_JAIL_STANDARD}" -eq 1 ] && [ "${VNET_JAIL_PASSTHROUGH}" -eq 1 ]; } || \
     { [ "${VNET_JAIL_BRIDGE}" -eq 1 ] && [ "${VNET_JAIL_PASSTHROUGH}" -eq 1 ]; } then
    error_exit "[ERROR]: Only one of [-V|--vnet], [-B|--bridge] or [-P|--passthrough] can be selected."
# Don't allow STATIC_MAC with VNET_JAIL_PASSTHROUGH
elif [ "${VNET_JAIL_PASSTHROUGH}" -eq 1 ] && [ "${STATIC_MAC}" -eq 1 ]; then
    error_exit "[ERROR]: [-P|--passthrough] does not support [-M|--static-mac]."
fi

# Set interface type for VNET jails
if [ "${VNET_JAIL_STANDARD}" -eq 1 ]; then
    VNET_INTERFACE_TYPE="standard"
elif [ "${VNET_JAIL_BRIDGE}" -eq 1 ]; then
    VNET_INTERFACE_TYPE="bridge"
elif [ "${VNET_JAIL_PASSTHROUGH}" -eq 1 ]; then
    VNET_INTERFACE_TYPE="passthrough"
fi

NAME="${1}"
RELEASE="${2}"
if [ "${NO_IP}" -eq 1 ]; then
    IP=""
    INTERFACE="${3}"
else
    IP="${3}"
    INTERFACE="${4}"
fi

info 1 "\nAttempting to create jail: ${NAME}"

if [ "${EMPTY_JAIL}" -eq 1 ]; then
    if [ $# -ne 1 ]; then
        usage
    fi
else
    if [ $# -gt 4 ] || [ $# -lt 3 ]; then
        usage
    fi
fi

# Validate jail name
if [ -n "${NAME}" ]; then
    validate_name
fi

# Validate interface type
if [ "${VNET_INTERFACE_TYPE}" = "bridge" ]; then
    if ! ifconfig -g bridge | grep -owq "${INTERFACE}"; then
        error_exit "[ERROR]: Interface is not a bridge: ${INTERFACE}"
    fi
elif [ "${VNET_INTERFACE_TYPE}" = "standard" ]; then
    if ifconfig -g bridge | grep -owq "${INTERFACE}"; then
        error_exit "[ERROR]: Interface is a bridge: ${INTERFACE}"
    fi
elif [ "${VNET_INTERFACE_TYPE}" = "passthrough" ]; then
    if ! ifconfig -l | grep -owq "${INTERFACE}"; then
        error_exit "[ERROR]: Interface does not exist: ${INTERFACE}"
    fi
fi

# Do not allow netgraph with -B|--bridge yet...
if { [ "${bastille_network_vnet_type}" = "netgraph" ] && [ "${VNET_INTERFACE_TYPE}" = "bridge" ]; } || \
   { [ "${bastille_network_vnet_type}" = "netgraph" ] && [ "${VNET_INTERFACE_TYPE}" = "bridge" ]; } then
    error_exit "[ERROR]: Netgraph does not support the [-B|--bridge] or [-P|--passthrough] option."
fi

# Filter sane release names
if [ "${EMPTY_JAIL}" -eq 0 ]; then
    if [ "${VALIDATE_RELEASE}" -eq 1 ]; then
        # Verify release
        case "${RELEASE}" in
            [2-4].[0-9]*)
                ### MidnightBSD ##
                PLATFORM_OS="MidnightBSD"
                NAME_VERIFY=$(echo "${RELEASE}")
                validate_release
                ;;
            *-current|*-CURRENT|*-current-I386|*-CURRENT-i386)
                ### FreeBSD ###
                PLATFORM_OS="FreeBSD"
                NAME_VERIFY=$(echo "${RELEASE}" | grep -iwE '^([1-9]+)\.[0-9](-CURRENT|-CURRENT-i386)$' | tr '[:lower:]' '[:upper:]' | sed 's/I/i/g')
                validate_release
                ;;
            *\.*-stable|*\.*-STABLE)
                ### FreeBSD ###
                PLATFORM_OS="FreeBSD"
                NAME_VERIFY=$(echo "${RELEASE}" | grep -iwE '^([1-9]+)\.[0-9](-STABLE)$' | tr '[:lower:]' '[:upper:]')
                validate_release
                ;;
            *-release|*-RELEASE|*-release-I386|*-RELEASE-i386|*-rc[1-9]|*-RC[1-9]|*-beta[1-9]|*-BETA[1-9])
                ### FreeBSD ###
                PLATFORM_OS="FreeBSD"
                NAME_VERIFY=$(echo "${RELEASE}" | grep -iwE '^([1-9]+)\.[0-9](-RELEASE|-RELEASE-i386|-RC[1-9]|-BETA[1-9])$' | tr '[:lower:]' '[:upper:]' | sed 's/I/i/g')
                validate_release
                ;;
            current|CURRENT)
                ### HardenedBSD ###
                PLATFORM_OS="HardenedBSD"
                NAME_VERIFY=$(echo "${RELEASE}" | sed 's/CURRENT/current/g')
                validate_release
                ;;
            [1-9]*-stable|[1-9]*-STABLE)
                ### HardenedBSD ###
                PLATFORM_OS="HardenedBSD"
                NAME_VERIFY=$(echo "${RELEASE}" | sed 's/STABLE/stable/g')
                validate_release
                ;;
            ubuntu_bionic|bionic|ubuntu-bionic)
                PLATFORM_OS="Ubuntu"
                NAME_VERIFY=Ubuntu_1804
                validate_release
                ;;
            ubuntu_focal|focal|ubuntu-focal)
                PLATFORM_OS="Ubuntu"
                NAME_VERIFY=Ubuntu_2004
                validate_release
                ;;
            ubuntu_jammy|jammy|ubuntu-jammy)
                PLATFORM_OS="Ubuntu"
                NAME_VERIFY=Ubuntu_2204
                validate_release
                ;;
            ubuntu_noble|noble|ubuntu-noble)
                PLATFORM_OS="Ubuntu"
                NAME_VERIFY=Ubuntu_2404
                validate_release
                ;;
            debian_buster|buster|debian-buster|debian10|Debian10)
                PLATFORM_OS="Debian"
                NAME_VERIFY=Debian10
                validate_release
                ;;
            debian_bullseye|bullseye|debian-bullseye|debian11|Debian11)
                PLATFORM_OS="Debian"
                NAME_VERIFY=Debian11
                validate_release
                ;;
            debian_bookworm|bookworm|debian-bookworm|debian12|Debian12)
                PLATFORM_OS="Debian"
                NAME_VERIFY=Debian12
                validate_release
                ;;
            debian_trixie|trixie|debian-trixie|debian23|Debian13)
                PLATFORM_OS="Debian"
                NAME_VERIFY=Debian13
                validate_release
                ;;
            *)
                error_notify "Unknown release: ${RELEASE}"
                usage
                ;;
        esac
    fi

    # Validate IP address
    if [ -n "${IP}" ]; then
        define_ips
    fi

    # Validate interface
    # Interface must be set with vnet jails
    if [ -n "${INTERFACE}" ]; then
        validate_netif
        validate_netconf
    elif [ "${VNET_JAIL}" -eq 1 ]; then
        if [ -z "${INTERFACE}" ]; then
            if [ -z "${bastille_network_shared}" ]; then
                error_exit "[ERROR]: Network interface not set."
            else
                validate_netconf
            fi
        fi
    else
        validate_netconf
    fi
else
    info 1 "\nCreating empty jail: ${NAME}."
fi

# May not exist on deployments created before Bastille 0.7.20200714, so creating it. -- cwells
if [ ! -e "${bastille_templatesdir}/default" ]; then
    ln -s "${bastille_sharedir}/templates/default" "${bastille_templatesdir}/default"
fi

# These variables were added after Bastille 0.7.20200714, so they may not exist in the user's config.
# We're checking for existence of the variables rather than empty since empty is a valid value. -- cwells
if [ -z ${bastille_template_base+x} ]; then
    bastille_template_base='default/base'
fi
if [ -z ${bastille_template_empty+x} ]; then
    bastille_template_empty='default/empty'
fi
if [ -z ${bastille_template_linux+x} ]; then
    bastille_template_linux='default/linux'
fi
if [ -z ${bastille_template_thick+x} ]; then
    bastille_template_thick='default/thick'
fi
if [ -z ${bastille_template_clone+x} ]; then
    bastille_template_clone='default/clone'
fi
if [ -z ${bastille_template_thin+x} ]; then
    bastille_template_thin='default/thin'
fi
if [ -z ${bastille_template_vnet+x} ]; then
    bastille_template_vnet='default/vnet'
fi
if [ -z ${bastille_template_vlan+x} ]; then
    bastille_template_vlan='default/vlan'
fi

create_jail "${NAME}" "${RELEASE}" "${IP}" "${INTERFACE}"
