#!/bin/sh

# activate pool for iocell
__activate () {
    local _npool _opool _filesystems

    _npool="$1"
    _opool=$(__get_default_prop pool)
    _filesystems="iocell
                  iocell/download
                  iocell/jails
                  iocell/releases
                  iocell/templates"

    # Older releases did not have inherited mountpoints, we rely on that to move
    # active pools.
    for _fs in ${_filesystems} ; do
        if [ "${_fs}" = "iocell" ] ; then
            zfs set mountpoint="/${_fs}" "${pool}/${_fs}" > /dev/null 2>&1
        else
            zfs inherit -r mountpoint "${pool}/${_fs}" > /dev/null 2>&1
        fi
    done

    if [ ! -z "${_npool}" ] ; then
        if jls | grep -q iocell ; then
            __die "make sure all jails are stopped before changing pools!"
        elif [ "${_opool}" = "${_npool}" ] ; then
            __die "${_npool} already active!"
        elif [ "${_opool}" = "null" ] || [ "${_opool}" = "none" ]; then
            # Necessary to complete migrations
            unset _opool
        fi

        if [ "$(zfs get -H creation "${_npool}")" ] ; then
            if [ "$(zfs get -H creation "${_opool}" 2> /dev/null)" ] ; then
                  zfs set mountpoint="/${_opool}/iocell" "${_opool}/iocell"
            fi

            export pool="${_npool}"
            __check_filesystems
            uclcmd set -u -f "${iocroot}/.default" -t string -- "pool" \
                "${_npool}"
        else
            __die "${_npool} is not a valid pool!"
        fi
    else
        echo "pool: ${_opool}"
    fi

    return
}

__clean () {
    local _answer _force _opt

    _force=0

    # Check for any combination of a clean argument + force
    while getopts :arjtf _opt "$@" ; do
        case "${_opt}" in
            f)
                _force=1
            ;;
        esac
    done

    # Reset so we can check again
    OPTIND=1

    # Repeat same thing, this time looking for only clean arguments as force
    # will be set properly by now.
    while getopts :arjtf _opt "$@" ; do
        case "${_opt}" in
            f) # Needed to avoid not a switch below
            ;;
            a)
                __clean_all
            ;;
            r)
                __clean_releases
            ;;
            j)
                __clean_jails
            ;;
            t)
                __clean_templates
            ;;
            ?)
                __die "-${OPTARG} is not a valid switch!"
        esac
    done

    # Cleanup
    shift $((OPTIND - 1))
}

__clean_all () {
    local _answer

    if [ "${_force}" != "1" ] ; then
        echo " "
        echo "  WARNING: this will destroy ${pool}/iocell"
        echo " "
        echo -n "  Are you sure ? y[N]: "
        read -r _answer

        if [ "${_answer}" != "Y" ] && [ "${_answer}" != "y" ] ; then
            __die "command not confirmed.  no action taken."
        fi
    fi

    __stop_jail_all > /dev/null 2>&1

    if [ $? -eq 0 ] ; then
        __spinner "  INFO: destroying ${pool}/iocell:  " \
                  "zfs destroy -rRf ${pool}/iocell"
        rm -rf "${iocroot}"
        return 0
    else
        __die "some jails were unable to be stopped."
    fi
}

__clean_releases () {
    local _answer

    if [ "${_force}" != "1" ] ; then
        echo " "
        echo "  WARNING: this will destroy all RELEASEs"
        echo "  you cannot use any basejails without them!"
        echo " "
        echo -n "  Are you sure ? y[N]: "
        read -r _answer

        if [ "${_answer}" != "Y" ] && [ "${_answer}" != "y" ] ; then
            __die "command not confirmed.  no action taken."
        fi
    fi

    if [ $? -eq 0 ] ; then
        __spinner "  INFO: destroying ${pool}/iocell/download:  " \
                  "zfs destroy -rRf ${pool}/iocell/download"
        __spinner "  INFO: destroying ${pool}/iocell/releases:  " \
                  "zfs destroy -rRf ${pool}/iocell/releases"
        return 0
    fi
}

__clean_jails () {
    local _answer

    if [ "${_force}" != "1" ] ; then
        echo " "
        echo "  WARNING: this will destroy all jails"
        echo " "
        echo -n "  Are you sure ? y[N]: "
        read -r _answer

        if [ "${_answer}" != "Y" ] && [ "${_answer}" != "y" ] ; then
            __die "command not confirmed.  no action taken."
        fi
    fi

    __clean_snapshots
    __stop_jail_all > /dev/null 2>&1

    if [ $? -eq 0 ] ; then
        __spinner "  INFO: destroying ${pool}/iocell/jails:  " \
                  "zfs destroy -rRf ${pool}/iocell/jails"
        __spinner "  INFO: cleaning TAGs at ${pool}/iocell/tags:  " \
                  "rm -rf ${iocroot}/tags"
        return 0
    else
        __die "some jails were unable to be stopped."
    fi
}

__clean_templates () {
    local _answer _jail_datasets _jail_fs _origin _tag _uuid _count

    _count=0
    _jail_datasets=$(zfs list -d3 -rH -o name "${pool}/iocell/jails" | \
        awk '!/jails$/ && !/\/root/')

    if [ "${_force}" != "1" ] ; then
        echo " "
        echo "  WARNING: this will destroy all templates"
        echo " "
        echo -n "  Are you sure ? y[N]: "
        read -r _answer

        if [ "${_answer}" = "Y" ] || [ "${_answer}" = "y" ] ; then
            echo " "
            for _fs in ${_jail_datasets} ; do
                # _fs is actually a dataset, so fulluuid needs to be faked.
                _uuid=$(__check_name "name" "${_fs}" 2> /dev/null)
                _origin=$(__get_jail_prop origin "${_uuid}" "${_fs}" | \
                    cut -f1 -d@)
                _tag=$(__get_jail_prop tag "${_uuid}" "${_jail_fs}")

                if [ "${_origin}" != "-" ] ; then
                    echo "  WARNING: ${_uuid} (${_tag}) is a clone." >&2
                    _count=$((_count + 1))
                fi
            done

            if [ ${_count} -ne 0 ] ; then
                echo " "
                echo "  please destroy these first or use -f"
                return 1
            fi
          else
            __die "command not confirmed.  no action taken."
        fi
    fi

    __spinner "  INFO: cleaning templatess at ${pool}/iocell/templates:  " \
              "zfs destroy -rRf ${pool}/iocell/templates"
    return 0
}

__clean_snapshots () {
    local _snapshots

    _snapshots=$(zfs list -Hrt snapshot -o name "${pool}/iocell" | grep ioc-)
    for _snap in echo ${_snapshots} ; do
        zfs destroy -rR "${_snap}" > /dev/null 2>&1
    done
}

__deactivate () {
    local _pool

    _pool="$1"

    echo "The iocell jails and data on ${_pool} must be manually removed with:"
    echo "    zfs destroy -r ${_pool}/iocell"
}

# Accepts two arguments. Which is used as the UUID for the umount operations
# and what release to use.
__migrate_basejail () {
    local _bfs_list _fulluuid _release _basedir_list _spinner _oldtemplate \
          _grepstring _jail_type _clone_check _ofulluuid _orelease _error

    _fulluuid="$1"
    _ofulluuid="${_fulluuid}"
    _release="$2"
    _orelease="${_release}"
    _grepstring="jails$|base|releases|templates$|download|${pool}/iocell$"
    _grepstring="${_grepstring}|/data$|/root$"
    _bfs_list="bin
               boot
               lib
               libexec
               rescue
               sbin
               usr"
    _basedir_list="dev
                   tmp
                   var
                   etc
                   root
                   proc
                   mnt
                   var/run"

    # Spinner is being recreated here because of the number of commands we use
    _spinner='/-\|'

    printf "  INFO: migrating basejail: \b%2s"
    export migrate="yes"
    while true; do
        printf '\b%.1s' "${_spinner}"
        _spinner=${_spinner#?}${_spinner%???}
        sleep .1
    done &

    trap 'kill $!' 2 3

    # Destroy all the old datasets
    for _fs in ${_bfs_list} ; do
        zfs destroy -nrf "${pool}/iocell/jails/${_fulluuid}/root/${_fs}" \
        > /dev/null 2>&1

        if [ "$?" -ne 0 ] ; then
            _error="1"
        fi
    done

    if [ "${_error}" = "1" ] ; then
        printf "\b%1s\n" "error!" ; kill $! && trap " " 2 3 \
            ; echo "  ERROR: please destroy all clones before using" \
            "this jail." >&2
        exit 1
    fi

    # If the user has a /usr/local, move it for migration
    if [ -e "${iocroot}/jails/${_fulluuid}/root/usr/local" ] ; then
        if [ ! -z $(ls -A "${iocroot}/jails/${_fulluuid}/root/usr/local") ] ; then
            mkdir -p ${iocroot}/jails/${_fulluuid}/usr
            mv    ${iocroot}/jails/${_fulluuid}/root/usr/local \
                  ${iocroot}/jails/${_fulluuid}/usr/local
        fi
    elif [ ! -e "${iocroot}/jails/${_fulluuid}/root/usr/local" ] ; then
        mkdir -p ${iocroot}/jails/${_fulluuid}/usr/local
    fi

    if [ -e "${iocroot}/jails/${_fulluuid}/root/usr/home" ] ; then
        if [ ! -z "$(ls -A ${iocroot}/jails/${_fulluuid}/root/usr/home)" ] ; then
            mkdir -p ${iocroot}/jails/${_fulluuid}/usr
            mv    ${iocroot}/jails/${_fulluuid}/root/usr/home \
                  ${iocroot}/jails/${_fulluuid}/usr/home
        fi
    elif [ ! -e "${iocroot}/jails/${_fulluuid}/root/usr/home" ] ; then
        mkdir -p ${iocroot}/jails/${_fulluuid}/usr/home
    fi

    # Destroy all the old datasets
    for _fs in ${_bfs_list} ; do
        zfs destroy -rf ${pool}/iocell/jails/${_fulluuid}/root/${_fs} \
        > /dev/null 2>&1
    done

    # Create all the r/w directories needed
    for _bdir in ${basedirs} ; do
        mkdir -p ${iocroot}/jails/${_fulluuid}/root/${_bdir}
    done

    for _bdir in ${_basedir_list} ; do
        mkdir -p ${iocroot}/jails/${_fulluuid}/root/${_bdir}
    done

    mkdir -p ${iocroot}/releases/${_release}/root/usr/ports

    # Move the user data back
    mv    ${iocroot}/jails/${_fulluuid}/usr/home \
          ${iocroot}/jails/${_fulluuid}/root/usr/home
    mv    ${iocroot}/jails/${_fulluuid}/usr/local \
          ${iocroot}/jails/${_fulluuid}/root/usr/local

    # Make sure the etcupdate we need for upgrades exists
    if [ ! -e "${iocroot}/releases/${_release}/root/etcupdate" ] ; then
        mount -t devfs devfs ${iocroot}/releases/${_release}/root/dev
        mkdir ${iocroot}/releases/${_release}/root/etcupdate
        chroot ${iocroot}/releases/${_release}/root \
            etcupdate build /etcupdate/etcupdate-${_release}.tbz
        umount ${iocroot}/releases/${_release}/root/dev
    fi

    zfs destroy -fr ${pool}/iocell/base@${_fulluuid} > /dev/null 2>&1

    _oldtemplate="$(__get_jail_prop template ${_fulluuid} \
        ${pool}/iocell/jails/${_fulluuid})"

    _jail_datasets="$(zfs list -d3 -rH -o name "${pool}/iocell" \
        | egrep -v "${_grepstring}")"

    for _jail in ${_jail_datasets} ; do
        _mountpoint="$(zfs get -H -o value mountpoint "${_jail}")"

        if [ ! -e "${_mountpoint}/config" ] ; then
            _juuid="$(echo "${_jail}" | cut -d / -f 4)"
            __dump_config "${_juuid}"
            _clone_check=$(zfs get -H origin "${_jail}" | awk '{print $3}')
            _jail_type=$(__get_jail_prop type "${_juuid}" "${_jail}")
            if [ "${_jail_type}" = "jail" ] ; then
                # Check for thick or clone jails and migrate their type
                if [ "${_clone_check}" != "-" ] ; then
                    __set_jail_prop type=clonejail "${_juuid}" "${_jail}"
                fi
            fi
        fi
    done

    # These get mangled from above.
    _fulluuid="${_ofulluuid}"
    _release="${_orelease}"
    _mountpoint=$(__get_jail_prop mountpoint "${_fulluuid}" \
        "${pool}/iocell/jails/${_fulluuid}")
    # Normally we'd use __set_jail_prop but these are special properties that
    # have a ton happen with them. We don't need that, just need to shift the
    # values.
    __ucl_set "${_mountpoint}" "istemplate" "no"

    # We never stored this, so we need a sane default.
    __ucl_set "${_mountpoint}" "template" "none"

    # Set the jail up for the new template structures.
    if [ "${_oldtemplate}" = "yes" ] ; then
        mkdir -p "${iocroot}/jails/${_fulluuid}/_/var"
        mkdir -p "${iocroot}/jails/${_fulluuid}/_/etc"
        mkdir -p "${iocroot}/jails/${_fulluuid}/_/root"
        mkdir -p "${iocroot}/jails/${_fulluuid}/_/mnt"

        # Since this jail is now a new template, we need to finish migration
        # and exit.
        __ucl_set "${_mountpoint}" "template" "none"
        __set_jail_prop release="${_release}" "${_fulluuid}" \
            "${pool}/iocell/jails/${_fulluuid}"
        __set_jail_prop istemplate="yes" "${_fulluuid}" \
            "${pool}/iocell/jails/${_fulluuid}"

        if [ "$?" -eq 1 ] ; then
            printf "\b%1s\n" "error!" ; kill $! && trap " " 2 3 \
                ; echo "${error}" >&2
            exit 1
        fi
    else
        __ucl_set "${_mountpoint}" "template" "none"
        __set_jail_prop release="${_release}" "${_fulluuid}" \
            "${pool}/iocell/jails/${_fulluuid}"
    fi

    printf "\b%1s\n" "done!" ; kill $! && trap " " 2 3
}

__check_basejail () {
    local _fulluuid _dataset _jail_release _old_base_check _jailed_dataset \
          _jailed_dataset_check _hack88 _shortuuid _template

    _fulluuid="$1"
    _dataset="$2"
    _template=$(__get_jail_prop template "${_fulluuid}" "${_dataset}")
    _jail_release=$(__get_jail_prop release "${_fulluuid}" "${_dataset}")
    _old_base_check=$(zfs list -Hr "${pool}/iocell/jails/${_fulluuid}" | \
                     grep -cv data)
    _jailed_dataset="${pool}/iocell/jails/${_fulluuid}/root/data"
    _hack88=$(__get_jail_prop hack88 "${_fulluuid}" "${_dataset}")
    _shortuuid=$(echo "${_fulluuid}"|cut -f1 -d "-")
    _jailed_dataset_check=$(zfs get -H creation "${_jailed_dataset}" > \
                           /dev/null 2>&1 ; echo $?)

    # Check to see if the user has the old style of jailed datasets
    if [ "${_jailed_dataset_check}" = "0" ] ; then
        zfs set jailed=off "${_jailed_dataset}"
        zfs rename -f "${_jailed_dataset}" \
                      "${pool}/iocell/jails/${_fulluuid}/data" 2> /dev/null
        zfs set jailed=on "${pool}/iocell/jails/${_fulluuid}/data" 2> /dev/null
    fi

    # If the length is over this, it's an old-style basejail
    if [ "${_old_base_check}" -gt "15" ] ; then
        __migrate_basejail "${_fulluuid}" "${_jail_release}"
        # We will have a stale entry here.
        _template=$(__get_jail_prop template "${_fulluuid}" "${_dataset}")
    fi

    if [ "${_template}" != "none" ] ; then
        # Look for hack88, if it exists, pass the short uuid
        if [ "${_hack88}" = "1" ] ; then
            __mount_template "${_shortuuid}" "${_template}"
        else
            __mount_template "${_fulluuid}" "${_template}"
        fi
    else
        # Make sure the etcupdate we need for upgrades exists
        if [ ! -e "${iocroot}/releases/${_jail_release}/root/etcupdate" ] ; then
            mount -t devfs devfs "${iocroot}/releases/${_jail_release}/root/dev"
            mkdir "${iocroot}/releases/${_jail_release}/root/etcupdate"
            chroot "${iocroot}/releases/${_jail_release}/root" \
                etcupdate build "/etcupdate/etcupdate-${_jail_release}.tbz"
            # Extract the etcupdate since it did not exist
            mount -t devfs devfs "${iocroot}/jails/${_fulluuid}/root/dev"
            chroot "${iocroot}/jails/${_fulluuid}/root" etcupdate \
                extract -t "/etcupdate/etcupdate-${_jail_release}.tbz"
            umount "${iocroot}/releases/${_jail_release}/root/dev"
            umount "${iocroot}/jails/${_fulluuid}/root/dev"
        fi

        # Look for hack88, if it exists, pass the short uuid
        if [ "${_hack88}" = "1" ] ; then
            __mount_basejail "${_shortuuid}" "${_jail_release}"
        else
            __mount_basejail "${_fulluuid}" "${_jail_release}"
        fi
    fi
}

# This creates jails----------------------------------------------------
__create_jail () {
    local _installed _releases _zfsconfig _supported_release _configured \
          _pname _prop _isprop _jail_zfs_mountpoint _basedir_list _uuid \
          _template _zfs_dataset

    _installed=$(zfs list -rH ${pool}/iocell/releases | grep ${release})
    _releases=$(__list_jails -r | grep -v "Downloaded releases:")
    _supported_release=$(echo "${release}" | grep "RELEASE")
    _devfs_string="$(fgrep -xq \
                  "## IOCAGE -- Add DHCP to ruleset 4" /etc/devfs.rules \
                  ; echo $?)"
    _release_dataset="${pool}/iocell/releases"
    _release_dataset_check="$(zfs get -H creation ${_release_dataset} > \
                           /dev/null 2>&1 ; echo $?)"
    _configured="0"
    _basedir_list="dev
                   tmp
                   var
                   etc
                   root
                   proc
                   mnt
                   var/run"
    _template="0"

    if [ -z "${_supported_release}" -a "${2}" != "-e" -a "${1}" = "create" -a -z "${_installed}" ] ; then
        echo "  You are not running a production RELEASE. (${release})" >&2
        echo "  ERROR: Please specify release=RELEASE." >&2
        exit 1
    fi

    if [ -z "${_installed}" -a -z "${_releases}" -a "${2}" != "-e" ] ; then
        __die "no RELEASEs found locally, run iocell fetch first."
    elif [ -z "${_installed}" -a "${2}" != "-e" -a "${1}" != "-g" ] ; then
        echo "  RELEASE ${release} not found locally." >&2
        echo "  Please fetch ${release}" >&2
        echo "" >&2

        echo "  These are the RELEASEs you have already fetched:" >&2
        printf "${_releases}\n" >&2
        exit 1
    fi

    # Remove this as we only needed it to skip the checks for plugins
    if [ "${1}" = "-g" ] ; then
        shift 2
    fi

    _isprop="$(echo ${2} | awk 'BEGIN { FS = "=" } ; { print $2 }')"

    # Look for null output, if it is, then no property is following create
    if [ "${2}" != "-e" -a ! -z "${2}" ] ; then
        if [ ! "${_isprop}" ] ; then
            __die "invalid switch ${2}"
        else
            for _p in "$@" ; do
                if [ "$(echo $_p | grep -e ".*=.*")" ] ; then
                    _pname="$(echo ${_p} | \
                            awk 'BEGIN { FS = "=" } ; { print $1 }')"
                    if [ "${_pname}" = "istemplate" ] ; then
                        __die "please set template after creation"
                    elif [ "${_pname}" = "template" ] ; then
                        _template="1"
                    fi
                fi
            done
        fi
    fi

    if [ "${2}" = "-e" ] ; then
        # Look to see if the jail type is our default, otherwise let the type
        # be whatever the user wants, it does not matter for an empty jail.
        if [ "${type}" = "basejail" ] ; then
            type=emptyjail
        fi
    fi

    zfs create -p ${pool}/iocell/jails/${uuid}/root
    touch "${iocroot}/jails/${uuid}/config"
    # Work around the different UUID for jail datasets at creation.
    export jail_zfs_dataset="iocell/jails/${uuid}/data"
    _zfsconfig="$(__configure_jail generate ${uuid})"
    _configured="1"

    touch ${iocroot}/jails/${uuid}/fstab

    # Make sure the jail isn't a empty jail
    if [ "${2}" = "-e" ] ; then
        echo ${uuid}
        return 0
    fi

    # Create all the r/w directories needed
    for _bdir in ${basedirs} ; do
        mkdir -p ${iocroot}/jails/${uuid}/root/${_bdir}
    done

    for _bdir in ${_basedir_list} ; do
        mkdir -p ${iocroot}/jails/${uuid}/root/${_bdir}

        if [ "${_bdir}" = "tmp" ] ; then
            chmod 1777 ${iocroot}/jails/${uuid}/root/tmp
        fi
    done

    # Template jails are essentially overlays and not true basejails.
    if [ "${_template}" = "1" ] ; then
        mkdir ${iocroot}/jails/${uuid}/_
        for _bdir in ${_basedir_list} ; do
            mkdir -p ${iocroot}/jails/${uuid}/_/${_bdir}

            if [ "${_bdir}" = "tmp" ] ; then
                chmod 1777 ${iocroot}/jails/${uuid}/_/tmp
            fi
        done
    fi

    # configure the jail mountpoint
    # Supplying _uuid again so since we need to use that for mounting/unmounting
    __hack88_mount ${uuid} ${pool}/iocell/jails/${uuid} _uuid

    if [ "${_template}" != "1" ] ; then
        # We need the base's /etc and /root
        cd ${iocroot}/releases/${release}/root/etc && find . | \
            cpio -dp --quiet ${iocroot}/jails/${uuid}/root/etc
        cd ${iocroot}/releases/${release}/root/root && find . | \
            cpio -dp --quiet ${iocroot}/jails/${uuid}/root/root

        if [ -e "/etc/localtime" ] ; then
            cp /etc/localtime ${iocroot}/jails/${uuid}/root/etc/
        fi

        # remove any residual clone snapshots carried over from the RELEASE datasets
        __snapremove ${uuid}@ALL ${pool}/iocell/jails/${uuid}

        if [ ! -e "${iocroot}/jails/${uuid}/root/etc/resolv.conf" ] ; then
            __resolv_conf ${uuid} ${pool}/iocell/jails/${uuid} > \
                ${iocroot}/jails/${uuid}/root/etc/resolv.conf
        fi

        # Make sure the etcupdate we need for upgrades exists
        if [ ! -e "${iocroot}/releases/${release}/root/etcupdate" ] ; then
            mount -t devfs devfs ${iocroot}/releases/${release}/root/dev
            mkdir ${iocroot}/releases/${release}/root/etcupdate
            chroot ${iocroot}/releases/${release}/root \
                etcupdate build /etcupdate/etcupdate-${release}.tbz
            umount ${iocroot}/releases/${release}/root/dev
        fi

        # Goop to get basejails and the etcupdate ready to go
        mount -t devfs devfs ${iocroot}/jails/${_uuid}/root/dev
        __mount_basejail "${_uuid}" "${release}"
        mkdir -p ${iocroot}/jails/${_uuid}/root/var/db/etcupdate
        chroot ${iocroot}/jails/${_uuid}/root etcupdate \
            extract -t /etcupdate/etcupdate-${release}.tbz
        umount ${iocroot}/jails/${_uuid}/root/dev
        umount ${iocroot}/jails/${_uuid}/root/tmp
        __umount_basejail "${_uuid}" "${pool}/iocell/jails/${_uuid}" \
            > /dev/null 2>&1
    fi

    for _prop in "$@" ; do
        _pname="$(echo ${_prop} | awk 'BEGIN { FS = "=" } ; { print $1 }')"
        # Look to see if the user wants a jailed dataset,
        # if they do then create it
        if [ "${_pname}" = "jail_zfs" ] ; then
            if [ ! -z "${iocset_jail_zfs_mountpoint}" ] ; then
                _jail_zfs_mountpoint="${iocset_jail_zfs_mountpoint}"
            else
                _jail_zfs_mountpoint="${jail_zfs_mountpoint}"
            fi

            if [ ! -z "${iocset_jail_zfs_dataset}" ] ; then
                export jail_zfs_dataset="${iocset_jail_zfs_dataset}"
            else
                # Hack to get around different UUID during creation
                export jail_zfs_dataset="iocell/jails/${uuid}/data"
            fi

            __setup_jail_zfs_datasets \
                "${uuid}" "${pool}/iocell/jails/${uuid}" \
                "${jail_zfs_dataset}" "${jail_zfs_mountpoint}" \
                -u
            __set_jail_prop jail_zfs_dataset="${jail_zfs_dataset}" \
                "${uuid}" "${pool}/iocell/jails/${uuid}" # Another hack for this
        # Do they want bpf exposed?
        elif [ "${_pname}" = "bpf" ] ; then
            if [ "${_devfs_string}" != "0" ] ; then
                __bpf_devfs >> /etc/devfs.rules
                service devfs restart > /dev/null 2>&1
            fi

            # Set the right devfs ruleset for DHCP/BPF.
            __set_jail_prop "devfs_ruleset=5" "${uuid}" \
                "${pool}/iocell/jails/${uuid}"
        fi
    done

    # at create time set the default rc.conf
    if [ -e "${iocroot}/jails/${uuid}/root/etc/rc.conf" ] ; then
        sed -i '' "1s/hostname=.*/hostname=\"${host_hostname}\"/" \
            ${iocroot}/jails/${uuid}/root/etc/rc.conf
    else
        echo "hostname=\"${host_hostname}\"" > \
            ${iocroot}/jails/${uuid}/root/etc/rc.conf
        __jail_rc_conf >> \
            ${iocroot}/jails/${uuid}/root/etc/rc.conf
    fi

    # create tag link, this does not send the tag to __link_tag so the tag can
    # be a date if the user left tag empty.
    __link_tag ${pool}/iocell/jails/${uuid}

    # Install extra packages
    # this requires working resolv.conf in jail
    if [ "${pkglist}" != "none" ] ; then
        # Mount all the filesytems needed to properly install pkg's
        # in a basejail
        mount -t tmpfs tmpfs ${iocroot}/jails/${uuid}/root/tmp
        mount -t devfs devfs ${iocroot}/jails/${uuid}/root/dev

        if [ "${_template}" != "1" ] ; then
            __mount_basejail "${uuid}" "${template}"
        else
            __mount_basejail "${uuid}" "${release}"
        fi

        __pkg_install "${iocroot}/jails/${uuid}/root"
    fi

    echo "  Successfully created: ${uuid} (${tag})"

    if [ ! -z "${start}" ] ; then
        __start_jail "${uuid}"
    fi
}

# Cloning jails ----------------------------------------------------------
__clone_jail () {
    local  _name _snapshot _dataset _fs _fs_list _zfsconfig  _cfs _pname _prop \
           _isprop

    _name="$(echo $1 |  awk 'BEGIN { FS = "@" } ; { print $1 }')"
    _snapshot="$(echo $1 |  awk 'BEGIN { FS = "@" } ; { print $2 }')"
    _isprop="$(echo ${1} | awk 'BEGIN { FS = "=" } ; { print $2 }')"

    if [ ! -z "${_name}" -a ! -z "${_isprop}" ] ; then
        __die "please specify UUID or TAG before any properties"
    fi

    if [ -z "${_name}" ] ; then
        __die "missing UUID or TAG!"
    fi

    _dataset="$(__find_jail ${_name})" || exit $?
    _fulluuid="$(__check_name ${_name} ${_dataset})"

    if [ -z ${_dataset} ] ; then
        exit 1
    fi

    _fs_list="$(zfs list -rH -o name ${_dataset})"

    if [ -z "${_snapshot}" ] ; then
        zfs snapshot ${_dataset}/root@${uuid}
    fi

    for _fs in ${_fs_list} ; do
        _cfs="$(echo ${_fs} | \
             sed s#${_dataset}#${pool}/iocell/jails/${uuid}#g)"

        if [ "${_cfs}" = "${pool}/iocell/jails/${uuid}/root" ] ; then
            zfs clone ${_fs}@${uuid} ${_cfs}
        elif [ "${_cfs}" = "${pool}/iocell/jails/${uuid}" ] ; then
            zfs create ${pool}/iocell/jails/${uuid}
            touch ${iocroot}/jails/${uuid}/config
            __configure_jail generate ${uuid}
        fi

        # See if they had a jailed dataset before
        if [ $(echo ${_cfs} | grep data$ | wc -l) -eq 1 ] ; then
            zfs clone -o mountpoint=none -o jailed=on ${_fs}@${_snapshot} \
                ${_cfs}
        fi
    done

    # create tag link, this does not send the tag to __link_tag so the tag can
    # be a date if the user left tag empty.
    __link_tag ${pool}/iocell/jails/${uuid}

    # Check if fstab already exists otherwise create it
    if [ -e ${iocroot}/jails/${uuid}/fstab ] ; then
        mv ${iocroot}/jails/${uuid}/fstab \
           ${iocroot}/jails/${uuid}/fstab.${_name}
        touch ${iocroot}/jails/${uuid}/fstab
    else
        touch ${iocroot}/jails/${uuid}/fstab
    fi

    # Check if rc.conf already exists otherwise create it
    if [ -e ${iocroot}/jails/${uuid}/root/etc/rc.conf ] ; then
        if [ "${host_hostname}" = "${uuid}" ] ; then
            cat ${iocroot}/jails/${uuid}/root/etc/rc.conf | \
                sed -E "s/hostname.*/hostname=\"${uuid}\"/g" \
                > ${iocroot}/jails/${uuid}/rc.conf
        else
            cat ${iocroot}/jails/${uuid}/root/etc/rc.conf | \
                sed -E "s/hostname.*/hostname=\"${host_hostname}\"/g" \
                > ${iocroot}/jails/${uuid}/rc.conf
        fi

        mv ${iocroot}/jails/${uuid}/rc.conf \
           ${iocroot}/jails/${uuid}/root/etc/rc.conf
    else
        echo "hostname=\"${host_hostname}\"" > \
            ${iocroot}/jails/${uuid}/root/etc/rc.conf
        __jail_rc_conf >> \
            ${iocroot}/jails/${uuid}/root/etc/rc.conf
    fi

    # Check if localtime already exists otherwise create it
    if [ -e "/etc/localtime" -a ! -e "${iocroot}/jails/${uuid}/root/etc/localtime" ] ; then
        cp /etc/localtime ${iocroot}/jails/${uuid}/root/etc/
    fi

    # Check if resolv.conf already exists otherwise create it
    if [ ! -e ${iocroot}/jails/${uuid}/root/etc/resolv.conf ] ; then
        __resolv_conf ${uuid} ${pool}/iocell/jails/${uuid} > \
            ${iocroot}/jails/${uuid}/root/etc/resolv.conf
    fi

    # configure the jail mountpoint
    __hack88_mount ${uuid} ${pool}/iocell/jails/${uuid}
    __set_jail_prop type=clonejail "${uuid}" "${pool}/iocell/jails/${uuid}"

    echo "  Successfully created: ${uuid} (${tag})"

    if [ ! -z "${start}" ] ; then
        __start_jail "${uuid}"
    fi
}

# Destroy jails --------------------------------------------------------------
__destroy_jail () {
    local _name _answer _uuid_list _force _dataset _origin _fulluuid \
          _jail_path _state

    if [ -z "$1" ] ; then
        __die "missing UUID!"
    fi

    _name="$1"

    if [ "${_name}" = "ALL" ] ; then
        __die "please use iocell clean -j instead."
    fi

    _dataset="$(__find_jail ${_name})" || exit $?
    _fulluuid="$(__check_name ${_name} ${_dataset})"

    if [ -z ${_dataset} ] ; then
        __die "${_name} not found!"
    fi

    _origin="$(zfs get -H -o value origin ${_dataset})"
    _jail_path="$(__get_jail_prop mountpoint ${_fulluuid} ${_dataset})"
    _state="$(__is_running ${_fulluuid})"
    _jail_type="$(__get_jail_prop type ${_fulluuid} ${_dataset})"

    __check_children ${_fulluuid} ${_dataset}

    if [ "$?" -eq 1 ] ; then
        exit 1
    fi

    if [ "${_force}" -ne "1" ] ; then
            echo " "
            echo "  WARNING: this will destroy jail ${_fulluuid}"
            echo "  Dataset: ${_dataset}"
            echo " "
            echo -n "  Are you sure ? y[N]: "
            read _answer

        if [ "${_answer}" = "Y" -o "${_answer}" = "y" ] ; then
            if [ ! -z ${_state} ] ; then
                __die "cannot destroy ${_name} - jail is running!"
            fi

            __destroy_func ${_fulluuid} ${_dataset} ${_origin} ${_jail_type} ${_jail_path}
        else
            echo "  Command not confirmed.  No action taken."
        fi
    else
        if [ ! -z ${_state} ] ; then
            __stop_jail ${_fulluuid} ${_dataset}
        fi

        __destroy_func ${_fulluuid} ${_dataset} ${_origin} ${_jail_type} ${_jail_path}
    fi
}

__destroy_func () {
    local _fulluuid _dataset _origin _jail_type _base_inuse _jail_path

    _fulluuid="$1"
    _dataset="$2"
    _origin="$3"
    _jail_type="$4"
    _jail_path="$5"
    _base_inuse="$(zfs get -r -H -o value origin ${pool} | \
                 grep ${pool}/iocell/base > \
                 /dev/null 2>&1 ; echo $?)"

    echo "  Destroying: ${_fulluuid}"

    __unlink_tag ${_dataset}

    zfs destroy -fr ${_dataset}

    if [ "${_origin}" != "-" ] ; then
        echo "  Destroying clone origin: ${_origin}"
        zfs destroy -r ${_origin}

        if [ -d "${_jail_path}" ] ; then
            rm -rf ${_jail_path}
        fi
    fi

    if [ ${_jail_type} = "basejail" ] ; then
        if [ -d "${_jail_path}" ] ; then
            rm -rf ${_jail_path}
        fi
    fi

    if [ "${_base_inuse}" = "1" ] ; then
        zfs destroy -fr ${pool}/iocell/base > /dev/null 2>&1
    fi
}

# check if our datasets exist, if not create them
# fixes https://github.com/iocell/iocell/issues/80
__check_filesystems () {
    local _altroot _filesystems _missing

    _missing=0
    _altroot="$(zpool get -H altroot "${pool}" | awk '{print $3}')"
    # Prepend iocell/ so we can properly set a mount on the root 'iocell'
    # dataset.
    _filesystems="iocell
                  iocell/download
                  iocell/jails
                  iocell/releases
                  iocell/templates"

    # Check for /etc/devfs.rules, this avoids an annoying error from grep later
    if [ ! -e "/etc/devfs.rules" ] ; then
        touch /etc/devfs.rules
    fi

    # Loop through and make sure the filesystems exist. Make sure you are root
    for _fs in ${_filesystems} ; do
        zfs get -H creation "${pool}/${_fs}" > /dev/null 2>&1

        if [ $? -ne 0 ] ; then
            _missing=1
            if [ "$(id -un)" != "root" ] ; then
                __die "please run as root to create missing datasets."
            else
                __info "creating ${pool}/${_fs}"
                zfs create -p "${pool}/${_fs}"

                if [ "${_fs}" = "iocell" ] ; then
                    zfs set mountpoint="/${_fs}" "${pool}/${_fs}" \
                        > /dev/null 2>&1
                fi

                if [ "${_altroot}" != "-" -a ! -d "/iocell" ] ; then
                    sed -i '' s#.*iocroot.*#iocroot="${_altroot}/iocell"# \
                        "${LIB}/ioc-globals"
                    export iocroot="${_altroot}/iocell"
                elif [ "${_altroot}" = "/mnt" -a -d "/iocell" ] ; then
                    # we're in a chroot.
                    sed -i '' s#.*iocroot.*#iocroot="/iocell"# \
                        "${LIB}/ioc-globals"
                    export iocroot="${_altroot}/iocell"
                elif [ "${_altroot}" = "-" ] ; then
                    sed -i '' s#.*iocroot.*#iocroot="/iocell"# \
                        "${LIB}/ioc-globals"
                    export iocroot="/iocell"
                fi
            fi
        fi
    done

    if [ ${_missing} -gt 0 ] ; then
        zfs mount -a
    fi

    # shellcheck disable=SC2155
    export jail_datasets=$(zfs list -d3 -rH -o name "${pool}/iocell" \
        | egrep -v \
        "jails$|releases$|templates$|download|${pool}/iocell$|/root$")

    # Checks to see if the user changed the altroot and modify ioc-globals
    # If the user decided to unset their altroot, they need to run again as
    # root to set it back to the default location
    if [ "${_altroot}" != "-" ] && [ "$(id -un)" = "root" ] && [ ! -d "/iocell" ] ; then
        sed -i '' s#.*iocroot.*#iocroot="${_altroot}/iocell"# \
            "${LIB}/ioc-globals"
        export iocroot="${_altroot}/iocell"
    elif [ "${_altroot}" != "-" ] && [ "$(id -un)" != "root" ] && \
        [ "${iocroot}" != "${_altroot}/iocell" ] ; then
        __error "You changed the altroot of ${pool}"
        echo "  Please run as root to fix." >&2
        exit 1
    elif [ "${_altroot}" = "-" ] && [ "$(id -un)" = "root" ] ; then
        sed -i '' s#.*iocroot.*#iocroot="/iocell"# \
            "${LIB}/ioc-globals"
        export iocroot="/iocell"
    elif [ "${_altroot}" = "-" ] && [ "$(id -un)" != "root" ] && \
        [ "${iocroot}" != "/iocell" ] ; then
        __error "You changed the altroot of ${pool}"
        echo "  Please run as root to fix." >&2
        exit 1
    fi

    if [ ! -e "${iocroot}/.default" ] ; then
        if [ "$(id -un)" != "root" ] ; then
            __die "please run as root to migrate defaults to ucl."
        fi

        _CONF="$CONF_NET
               $CONF_JAIL
               $CONF_RCTL
               $CONF_CUSTOM
               $CONF_SYNC
               $CONF_FTP
               $CONF_GIT"

        for prop in $_CONF ; do
            prop_name=$prop
            eval prop="\$${prop}"
            echo "$prop_name = \"$prop\";" >> ${iocroot}/.default
        done

        # We no longer need this dataset or mountpoint
        zfs get -H creation "${pool}/iocell/.defaults" > /dev/null 2>&1
        if [ $? -eq 0 ] ; then
            zfs destroy "${pool}/iocell/.defaults"
            rmdir ${iocroot}/.defaults
            rm -rf -- ${iocroot}/tags/-
        fi
    fi
}

__check_children () {
    local _dataset _jail_datasets _fs _origin _tag _fulluuid \
          _uuid _grepstring _printf

    _fulluuid="$1"
    _dataset="$2"
    _grepstring="jails$|base|releases|templates$|download|${pool}/iocell$"
    _grepstring="${_grepstring}|/data$"
    _jail_datasets=$(zfs list -d3 -rH -o name "${pool}/iocell" \
        | egrep -v "${_grepstring}")

    for _fs in ${_jail_datasets} ; do
        _origin=$(zfs get -H -o value origin "${_fs}" | cut -f1 -d@)

        if ! echo "${_fs}" | grep -q "/root" ; then
            # _fs is actually a dataset, so fulluuid needs to be faked.
            _uuid=$(__check_name "name" "${_fs}" 2> /dev/null)
            _tag=$(__get_jail_prop tag "${_uuid}" "${_fs}")
        fi

        if [ "${_origin}" = "${_dataset}/root" ] ; then
            _printf=" ERROR: jail has dependent clone, uuid: ${_uuid} (${_tag})"
            error="${error} $(printf "\n%s" "${_printf}")"
        fi
    done

    if [ ! -z "${error}" ] ; then
        export error
        return 1
    fi
}

# Accepts two arguments. Which is used as the UUID for the mount operations and
# which base to use.
__mount_basejail () {
    local _fulluuid _release

    _fulluuid="$1"
    _release="$2"

    if [ ! -e "${iocroot}/jails/${_fulluuid}/root/var/ports" ] ; then
        mkdir ${iocroot}/jails/${_fulluuid}/root/var/ports
        mkdir ${iocroot}/jails/${_fulluuid}/root/var/ports/distfiles
        mkdir ${iocroot}/jails/${_fulluuid}/root/var/ports/packages
    fi

    # Make sure the /compat directory exists
    if [ ! -e "${iocroot}/jails/${_fulluuid}/root/compat" ] ; then
        mkdir ${iocroot}/jails/${_fulluuid}/root/compat
    fi

    # Make sure the /usr/obj directory exists
    if [ ! -e "${iocroot}/jails/${_fulluuid}/root/usr/obj" ] ; then
        mkdir ${iocroot}/jails/${_fulluuid}/root/usr/obj
    fi

    for _bdir in ${basedirs} ; do
        # Mount the nullfs needed for basejails
        mount -t nullfs -o ro \
            ${iocroot}/releases/${_release}/root/${_bdir} \
            ${iocroot}/jails/${_fulluuid}/root/${_bdir} 2> /dev/null
    done

    # Mount tmpfs
    mount -t tmpfs tmpfs ${iocroot}/jails/${_fulluuid}/root/tmp
}

# Accepts one argument. Which is used as the UUID for the umount operations,
__umount_basejail () {
    local _fulluuid _basedir_list _template

    _fulluuid="$1"
    _template="$(__get_jail_prop template ${_fulluuid} ${_dataset})"
    _basedir_list="var
                   etc
                   root
                   mnt"

    # Unmounts all the basejails overlays and tmpfs so the jail can be manipulated
    umount ${iocroot}/jails/${_fulluuid}/root/tmp > /dev/null 2>&1

    for _bdir in ${basedirs} ; do
        umount -ft nullfs ${iocroot}/jails/${_fulluuid}/root/${_bdir} \
            2> /dev/null
    done

    if [ "${_template}" != "none" ] ; then
        for _i in $(seq 2) ; do
            umount -ft nullfs ${iocroot}/jails/${_fulluuid}/root

            for _bdir in ${_basedir_list} ; do
                umount -ft nullfs ${iocroot}/jails/${_fulluuid}/root/${_bdir} \
                    2> /dev/null
            done
        done
    fi
}

# Accepts two arguments. One is used as the name for the mount operations, the
# other is the RELEASE the template needs to mount.
__mount_istemplate () {
    local _tag

    _tag="$1"
    _release="$2"

    for _bdir in ${basedirs} ; do
        # Mount the nullfs needed for basejails
        mount -t nullfs -o ro \
            ${iocroot}/releases/${_release}/root/${_bdir} \
            ${iocroot}/templates/${_tag}/root/${_bdir}
    done

    # Mount tmpfs
    mount -t tmpfs tmpfs ${iocroot}/templates/${_tag}/root/tmp
}

# Accepts two arguments. Which is used as the name for the mount operations,
# and which template to mount.
__mount_template () {
    local _fulluuid _template _mountcheck _template_release _basedir_list

    _fulluuid="$1"
    _template="$2"
    _dataset="${pool}/iocell/templates/${_template}"
    _template_release="$(__get_jail_prop release ${_fulluuid} ${_dataset})"
    _basedir_list="var
                   etc
                   root
                   mnt"

    if [ ! -e "${iocroot}/jails/${_fulluuid}/root/var/ports" ] ; then
        mkdir ${iocroot}/jails/${_fulluuid}/root/var/ports
        mkdir ${iocroot}/jails/${_fulluuid}/root/var/ports/distfiles
        mkdir ${iocroot}/jails/${_fulluuid}/root/var/ports/packages
    fi

    # Make sure the /compat directory exists
    if [ ! -e "${iocroot}/jails/${_fulluuid}/root/compat" ] ; then
        mkdir ${iocroot}/jails/${_fulluuid}/root/compat
    fi

    # Make sure the /usr/obj directory exists
    if [ ! -e "${iocroot}/jails/${_fulluuid}/root/usr/obj" ] ; then
        mkdir ${iocroot}/jails/${_fulluuid}/root/usr/obj
    fi

    for _bdir in ${basedirs} ; do
        # Mount the nullfs needed for basejails
        mount -t nullfs -o ro \
            ${iocroot}/releases/${_template_release}/root/${_bdir} \
            ${iocroot}/jails/${_fulluuid}/root/${_bdir}
    done

    for _bdir in ${_basedir_list} ; do
        mount -t nullfs -o ro \
            ${iocroot}/templates/${_template}/root/${_bdir} \
            ${iocroot}/jails/${_fulluuid}/root/${_bdir}
    done

    for _bdir in ${_basedir_list} ; do
        mount -t nullfs -o union \
            ${iocroot}/jails/${_fulluuid}/_/${_bdir} \
            ${iocroot}/jails/${_fulluuid}/root/${_bdir}
    done

    # Mount tmpfs
    mount -t tmpfs tmpfs ${iocroot}/jails/${_fulluuid}/root/tmp
}

# Accepts one argument. Which is used as the name for the umount operations
__umount_istemplate () {
    local _tag="$1"

    # Unmounts all the basejail mounts and tmpfs so the jail can be manipulated
    umount ${iocroot}/templates/${_tag}/root/tmp > /dev/null 2>&1

    for _bdir in ${basedirs} ; do
        umount -ft nullfs ${iocroot}/templates/${_tag}/root/${_bdir}
    done
}

__send () {
    local _clean _count _date _dest _exists _exit _incremental _Incremental \
          _mbuffertest _move _opt _pool _remote _remotehost _remoteuser \
          _snapshot _sshtest _state _switch _zfstest

    _date=$(date "+%F")

    # Hack to be able to see the last argument
    for _last ; do
        true
    done

    _exists="$(zfs get -H -o value creation ${_last} > /dev/null 2>&1 \
        ; echo $?)"

    if [ "${_exists}" -eq 1 ] ; then
        __die "please specify a valid pool!" >&2
    else
        _dest="${_last}"
    fi

    # Set up our variables
    _clean=0
    _incremental=0
    _Incremental=0
    _remote=0
    _move=0
    _count=0
    _state="$(jls -h | grep ioc-)"

    # Check for any combination of a send argument + remote
    while getopts :ci:I:rmh:u: _opt "$@" ; do
        case "${_opt}" in
            r)
                _remote=1
            ;;
        esac
    done

    # Reset so we can check again
    OPTIND=1

    # Look for any switches we want
    while getopts :ci:I:rmh:u: _opt "$@" ; do
        case "${_opt}" in
            r) # Needed to avoid not a switch below
            ;;
            c)
                _clean=1
            ;;
            i)
                # We don't want any nasty surprises, warn the user now.
                if [ "$(echo $OPTARG | grep -q "^-" ; echo $?)" -eq 1 ] ; then
                    if [ "${_Incremental}" -eq 1 ] ; then
                        __die "please use either -i or -I, not both!" >&2
                    fi

                    _incremental=1

                    if [ "${_count}" -gt 0 ] ; then
                        _snapshot="${_snapshot} ${OPTARG}"
                    else
                        _snapshot="i ${OPTARG}"
                    fi

                    _count=$((_count + 1))
                else
                    __die "please specify a snapshot!"
                fi
            ;;
            I)
                # We don't want any nasty surprises, warn the user now.
                if [ "$(echo $OPTARG | grep -q "^-" ; echo $?)" -eq 1 ] ; then
                    if [ "${_incremental}" -eq 1 ] ; then
                        __die "please use either -i or -I, not both!" >&2
                    fi

                    _Incremental=1

                    if [ "${_count}" -gt 0 ] ; then
                        _snapshot="${_snapshot} ${OPTARG}"
                    else
                        _snapshot="I ${OPTARG}"
                    fi

                    _count=$((_count + 1))
                else
                    __die "please specify a snapshot!"
                fi
            ;;
            h)
                # We don't want any nasty surprises, warn the user now.
                if [ "$(echo $OPTARG | grep -q "^-" ; echo $?)" -eq 1 ] ; then
                    _remotehost="${OPTARG}"
                else
                    __die "please specify a host!"
                fi
            ;;
            u)
                # We don't want any nasty surprises, warn the user now.
                if [ "$(echo $OPTARG | grep -q "^-" ; echo $?)" -eq 1 ] ; then
                    _remoteuser="${OPTARG}"
                else
                    __die "please specify a user!"
                fi
            ;;
            m)
                _move=1
            ;;
            ?)
                __die "-${OPTARG} is not a valid switch!" >&2
            ;;
        esac
    done

    if [ "${_move}" -eq 1 ] ; then
        if [ ! -z "${_state}" ] ; then
            __die "please stop all jails before using move!"
        fi
    fi

    if [ "${_move}" -eq 1 ] ; then
        if [ "${_remote}" -eq 1 ] ; then
            __die "move is only for local operations!"
        fi
    fi

    if [ ! -z "${_remotehost}" -o ! -z "${_remoteuser}" ] ; then
        if [ "${_remote}" -ne 1 ] ; then
            __die "-r is required to specify a user and host!"
        fi
    fi

    if [ "${_remote}" -eq 1 -a ! -z "${_remotehost}" -a ! -z "${_remoteuser}" ] ; then
        _sshtest="$(ssh -o PreferredAuthentications=publickey \
            ${_remoteuser}@${_remotehost} 'exit' > /dev/null 2>&1 ; echo $?)"

        if [ "${_sshtest}" -ne 0 ] ; then
            __die "please set up publickey authentication for ${_remoteuser}@${_remotehost}"
        fi

        _zfstest="$(ssh ${_remoteuser}@${_remotehost} '/usr/bin/which -s zfs' \
            ; echo $?)"

        if [ "${_zfstest}" -ne 0 ] ; then
            __die "zfs is needed on the receiving (${_remotehost} side!)"
        fi
    fi

    # Cleanup
    shift $(expr $OPTIND - 1)

    # __spinner needs to be recreated here as we want one dialog
    _spinner='/-\|'

    printf "  INFO: sending ${pool}/iocell to ${_dest}/iocell:  "

    while true; do
        printf '\b%.1s' "${_spinner}"
        _spinner=${_spinner#?}${_spinner%???}
        sleep .1
    done &

    trap "kill $!" 2 3
    # Clean all snapshots before send
    if [ "${_clean}" -eq 1 ] ; then
        if [ "${_incremental}" -eq 1 -o "${_Incremental}" -eq 1 ] ; then
            printf "\b%1s\n" "error!" ; kill $! && trap " " 2 3 \
                ; echo "  ERROR: cannot use clean with incrementals!" >&2
            exit 1
        fi

        __clean_snapshots > /dev/null 2>&1
        _pool="${pool}"
        # Change the global pool variable to the destination
        # so it can be cleaned of snapshots as well
        export pool="${_dest}"
        __clean_snapshots > /dev/null 2>&1
        export pool="${_pool}"
    fi

    if [ "${_remote}" -eq 1 ] ; then
        if [ -z "${_remoteuser}" ] ; then
            printf "\b%1s\n" "error!" ; kill $! && trap " " 2 3 \
              ; echo "  ERROR: please specify -u and a user" >&2
            exit 1
        elif [ -z "${_remotehost}" ] ; then
            printf "\b%1s\n" "error!" ; kill $! && trap " " 2 3 \
                ; echo "  ERROR: please specify -h and a host" >&2
            exit 1
        fi

        which -s mbuffer # Kudos to @dlangille for mbuffer idea

        if [ $? -eq 0 ] ; then
            _mbuffertest="$(ssh ${_remoteuser}@${_remotehost} \
                '/usr/bin/which -s mbuffer' \
                ; echo $?)"

            if [ "${_mbuffertest}" -ne 0 ] ; then
                printf "\b%1s\n" "error!" ; kill $! && trap " " 2 3 \
                    ; echo "  ERROR: mbuffer also needed on receiving ${_remotehost} side!" >&2
                exit 1
            fi

            if [ "${_incremental}" -eq 1 -o "${_Incremental}" -eq 1 ] ; then
                if [ "${_count}" -lt 2 ] ; then
                    printf "\b%1s\n" "error!" ; kill $! && trap " " 2 3 \
                        ; echo "  ERROR: please supply a minimium of 2 snapshots!" >&2
                    exit 1
                fi

                zfs send -R${_snapshot} | \
                    mbuffer -s 128k -m 1G 2> /dev/null | \
                    ssh ${_remoteuser}@${_remotehost} "mbuffer -s 128k -m 1G | \
                    zfs receive -F ${_dest}/iocell"
                _exit="$?"
            else
                zfs snapshot -r ${pool}/iocell@ioc-send-${_date} > /dev/null 2>&1
                zfs send -R ${pool}/iocell@ioc-send-${_date} | \
                    mbuffer -s 128k -m 1G 2> /dev/null | \
                    ssh ${_remoteuser}@${_remotehost} "mbuffer -s 128k -m 1G | \
                    zfs receive -F ${_dest}/iocell"
                _exit="$?"
            fi
        else
            if [ "${_incremental}" -eq 1 -o "${_Incremental}" -eq 1 ] ; then
                zfs send -R${_snapshot} | \
                    ssh ${_remoteuser}@${_remotehost} \
                    "zfs receive -F ${_dest}/iocell"
                _exit="$?"
            else
                zfs snapshot -r ${pool}/iocell@ioc-send-${_date} > /dev/null 2>&1
                zfs send -R ${pool}/iocell@ioc-send-${_date} | \
                    ssh ${_remoteuser}@${_remotehost} \
                    "zfs receive -F ${_dest}/iocell"
                _exit="$?"
            fi
        fi
    else
        if [ "${_incremental}" -eq 1 -o "${_Incremental}" -eq 1 ] ; then
            if [ "${_count}" -lt 2 ] ; then
                printf "\b%1s\n" "error!" ; kill $! && trap " " 2 3 \
                    ; echo "  ERROR: please supply a minimum of 2 snapshots!" >&2
                exit 1
            fi

            zfs send -R${_snapshot} | \
                zfs receive -Fu ${_dest}/iocell
            _exit="$?"
        else
            zfs snapshot -r ${pool}/iocell@ioc-send-${_date} > /dev/null 2>&1
            zfs send -R ${pool}/iocell@ioc-send-${_date} | \
                zfs receive -Fu ${_dest}/iocell
            _exit="$?"
        fi
    fi

    # Clean snapshots after send
    if [ "${_clean}" -eq 1 ] ; then
        __clean_snapshots > /dev/null 2>&1
        # Change the global pool variable to the destination
        # so it can be cleaned of snapshots as well
        export pool="${_dest}"
        __clean_snapshots > /dev/null 2>&1
        export pool="${_pool}"
    fi

    if [ "${_move}" -eq 1 ] ; then
        if [ "${_exit}" -eq 0 ] ; then
            __activate "${_dest}"
            zfs destroy -rf "${pool}/iocell"
            export pool="${_dest}"
            zfs mount -a
        fi
    fi

    printf "\b%1s\n" "done!" ; kill $! && trap " " 2 3
}

# Accepts one argument, and that is the UUID to match everything.
__dump_config () {
    local _fulluuid _dataset _config _mountpoint

    _fulluuid="$1"
    _dataset="${pool}/iocell/jails/${_fulluuid}"
    # shellcheck disable=SC1004
    _config=$(zfs get -H -o property,value all \
        "${_dataset}" | awk -v dq='"' '/org.freebsd.iocell/ \
        { $2=dq$2; sub(" "," VALUE ",$0); \
        sub("org.freebsd.iocell:","PROP ",$1); print $0dq }')
    _mountpoint=$(zfs get -H -o value mountpoint "${_dataset}")
    _v="no"
    _p="no"

    touch "${_mountpoint}/config"
    __configure_jail generate "${_fulluuid}" "${_dataset}" "${_mountpoint}"

    for _conf in ${_config} ; do
        if [ "${_conf}" = "PROP" ] ; then
            _p="yes"
            _v="no"
            unset _value
            continue
        fi

        if [ "${_conf}" = "VALUE" ] ; then
            _v="yes"
            continue
        fi

        if [ "${_p}" = "yes" ] ; then
            _prop="${_conf}"
            _p="no"
            continue
        elif [ "${_v}" = "yes" ] ; then
            if [ ! -z "${_value}" ] ; then
                _value="${_value} ${_conf}"
            else
                _value="${_conf}"
            fi

            if ! echo "${_value}" | egrep -q '"$' ; then
                continue
            fi
        fi

        # Old jails had an invalid duplicate property for host_hostname.
        if [ "${_prop}" = "hostname" ] ; then
            continue
        fi

        eval __ucl_set "${_mountpoint}" "${_prop}" "${_value}" "1"
    done
}

# Add the poolname to ZFS datasets that live under iocell/jails --------------
__zfs_dataset_add_poolname () {
    local _dataset _dataset_olen _dataset_nlen

    _dataset="$1"
    _dataset_olen="${#_dataset}"
    _dataset="${_dataset#iocell/jails}"
    _dataset_nlen="${#_dataset}"
    if [ "${_dataset_olen}" -ne "${_dataset_nlen}" ] ; then
        echo "${pool}/iocell/jails${_dataset}"
    else
        echo "${_dataset}"
    fi
}

# Set up ZFS datasets owned by the jail --------------------------------------
__setup_jail_zfs_datasets () {
    local _uuid _dataset _jail_zfs_datasets _jail_zfs_mountpoint _create_args

    _uuid="$1"
    _dataset="$2"
    _jail_zfs_datasets="$3"
    _jail_zfs_mountpoint="$4"
    _create_args="$5"

    if [ -z "${_jail_zfs_datasets}" ] ; then
        _jail_zfs_datasets="$(__get_jail_prop jail_zfs_dataset \
            "${_uuid}" "${_dataset}")"
    fi
    if [ -z "${_jail_zfs_mountpoint}" ] ; then
        _jail_zfs_mountpoint="$(__get_jail_prop jail_zfs_mountpoint \
            "${_uuid}" "${_dataset}")"
    fi

    # Check to make sure that jail_zfs_mountpoint is "none" if there are
    # multiple datasets assigned to the jail
    if [ "${_jail_zfs_mountpoint}" != "none" ] ; then
        if [ "$(echo "${_jail_zfs_datasets}" | grep "[ 	]")" ] ; then
            __info "With multiple ZFS datasets per jail," \
                "jail_zfs_mountpoint must be set to 'none'."

            # Note that this will not modify any currently set mountpoint
            # properties for the current jail_zfs_datasets.
            __set_jail_prop "jail_zfs_mountpoint=none" "${_uuid}" \
                "${_dataset}"

            _jail_zfs_mountpoint="none"
        fi
    fi

    for _zfs_dataset in ${_jail_zfs_datasets} ; do

        _zfs_dataset="$(__zfs_dataset_add_poolname "${_zfs_dataset}")"
        _zfs_dataset_check="$(zfs get -H creation "${_zfs_dataset}" \
            > /dev/null 2>&1 ; echo $?)"
        if [ "${_zfs_dataset_check}" != "0" ] ; then
            # Only try to create datasets that aren't pre-existing
            eval "zfs create -o jailed=on -o compression=${compression} \
                -o \"mountpoint=${_jail_zfs_mountpoint}\" ${_create_args} \
                \"${_zfs_dataset}\""
        else
            # Otherwise, just mark it as jailed and assume that all
            # other props are set correctly
            zfs set jailed=on "${_zfs_dataset}"
        fi

    done
}
