diff --git a/docs/chapters/subcommands/mount.rst b/docs/chapters/subcommands/mount.rst index f7fb0ee3..9add58f0 100644 --- a/docs/chapters/subcommands/mount.rst +++ b/docs/chapters/subcommands/mount.rst @@ -6,11 +6,40 @@ To mount storage within the container use `bastille mount`. .. code-block:: shell - ishmael ~ # bastille mount azkaban /storage/foo /media/foo nullfs ro 0 0 + ishmael ~ # bastille mount azkaban /storage/foo media/foo nullfs ro 0 0 [azkaban]: + Added: /media/foo /usr/local/bastille/jails/azkaban/root/media/foo nullfs ro 0 0 + ishmael ~ # bastille mount azkaban /storage/bar /media/bar nullfs ro 0 0 + [azkaban]: + Added: /media/bar /usr/local/bastille/jails/azkaban/root/media/bar nullfs ro 0 0 + +Notice the JAIL_PATH format can be /media/foo or simply media/bar. The leading slash / is optional. The HOST_PATH howerver, must be the full path including the leading slash /. + +It is also possible to mount individual files into a jail as seen below. +Bastille will not mount if a file is already present at the specified mount point. +If you do not specify a file name, bastille will mount the file underneath the specified directory as seen in the second example below. + +.. code-block:: shell + + ishmael ~ # bastille mount azkaban /etc/rc.conf /mnt/etc/rc.conf nullfs ro 0 0 + [azkaban]: + Added: /etc/rc.conf /usr/local/bastille/jails/azkaban/root/mnt/etc/rc.conf nullfs ro 0 0 + ishmael ~ # bastille mount azkaban /etc/rc.conf /media/bar nullfs ro 0 0 + [azkaban]: + Added: /etc/rc.conf usr/local/bastille/jails/azkaban/root/media/bar/rc.conf nullfs ro 0 0 + +It is also possible (but not recommended) to have spaces in the directories that are mounted. +It is necessary to escape each space with a backslash \ and enclose the mount point in quotes "" as seen below. +It is possible to do the same for the jail path, but again, not recommemded. + +.. code-block:: shell + + ishmael ~ # bastille mount azkaban "/storage/my\ directory\ with\ spaces" /media/foo nullfs ro 0 0 + [azkaban]: + Added: /storage/my\040directory\040with\040spaces /usr/local/bastille/jails/azkaban/root/media/foo nullfs ro 0 0 Syntax follows standard `/etc/fstab` format: .. code-block:: shell - Usage: bastille mount TARGET host_path container_path [filesystem_type options dump pass_number] + Usage: bastille mount TARGET HOST_PATH JAIL_PATH [filesystem_type options dump pass_number] diff --git a/docs/chapters/subcommands/umount.rst b/docs/chapters/subcommands/umount.rst index f4aaeb49..cdcdabdb 100644 --- a/docs/chapters/subcommands/umount.rst +++ b/docs/chapters/subcommands/umount.rst @@ -8,9 +8,21 @@ To unmount storage from a container use `bastille umount`. ishmael ~ # bastille umount azkaban /media/foo [azkaban]: + Unmounted: /usr/local/bastille/jails/jail4/root/media/foo + ishmael ~ # bastille umount azkaban /mnt/etc/rc.conf + [azkaban]: + Unmounted: /usr/local/bastille/jails/jail4/root/mnt/etc/rc.conf -Syntax requires only the container path to unmount: +Syntax requires only the jail path to unmount. .. code-block:: shell - Usage: bastille umount TARGET container_path + Usage: bastille umount TARGET JAIL_PATH + +If the directory you are unmounting has spaces, make sure to escape them with a backslash \, and enclode the mount point in quotes "". + +.. code-block:: shell + + ishmael ~ # bastille umount azkaban "/media/foo\ with\ spaces" + [azkaban]: + Unmounted: /usr/local/bastille/jails/jail4/root/media/foo with spaces diff --git a/usr/local/bin/bastille b/usr/local/bin/bastille index 98cd52a1..5c78318a 100755 --- a/usr/local/bin/bastille +++ b/usr/local/bin/bastille @@ -157,10 +157,10 @@ version|-v|--version) help|-h|--help) usage ;; -bootstrap|create|destroy|export|import|list|rdr|restart|setup|start|update|upgrade|verify) +bootstrap|create|destroy|export|htop|import|list|mount|rdr|restart|setup|start|top|umount|update|upgrade|verify) # Nothing "extra" to do for these commands. -- cwells ;; -clone|config|cmd|console|convert|cp|edit|htop|limits|mount|pkg|rcp|rename|service|stop|sysrc|tags|template|top|umount|zfs) +clone|config|cmd|console|convert|cp|edit|limits|pkg|rcp|rename|service|stop|sysrc|tags|template|zfs) # Parse the target and ensure it exists. -- cwells if [ $# -eq 0 ]; then # No target was given, so show the command's help. -- cwells PARAMS='help' @@ -205,7 +205,7 @@ clone|config|cmd|console|convert|cp|edit|htop|limits|mount|pkg|rcp|rename|servic fi case "${CMD}" in - cmd|console|htop|pkg|service|stop|sysrc|template|top) + cmd|console|pkg|service|stop|sysrc|template) check_target_is_running ;; convert|rename) diff --git a/usr/local/share/bastille/common.sh b/usr/local/share/bastille/common.sh index 38181b5e..b9b0986f 100644 --- a/usr/local/share/bastille/common.sh +++ b/usr/local/share/bastille/common.sh @@ -28,6 +28,9 @@ # 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. +# Source config file +. /usr/local/etc/bastille/bastille.conf + COLOR_RED= COLOR_GREEN= COLOR_YELLOW= @@ -51,11 +54,18 @@ if [ -z "${NO_COLOR}" ] && [ -t 1 ]; then enable_color fi -# Notify message on error, but do not exit +# Error/Info functions error_notify() { echo -e "${COLOR_RED}$*${COLOR_RESET}" 1>&2 } +error_continue() { + error_notify "$@" + # Disabling this shellcheck as we only ever call it inside of a loop + # shellcheck disable=SC2104 + continue +} + # Notify message on error and exit error_exit() { error_notify "$@" @@ -70,6 +80,33 @@ warn() { echo -e "${COLOR_YELLOW}$*${COLOR_RESET}" } +check_target_exists() { + local _TARGET="${1}" + if [ ! -d "${bastille_jailsdir}"/"${_TARGET}" ]; then + return 1 + else + return 0 + fi +} + +check_target_is_running() { + local _TARGET="${1}" + if [ ! "$(/usr/sbin/jls name | awk "/^${_TARGET}$/")" ]; then + return 1 + else + return 0 + fi +} + +check_target_is_stopped() { + local _TARGET="${1}" + if [ "$(/usr/sbin/jls name | awk "/^${_TARGET}$/")" ]; then + return 1 + else + return 0 + fi +} + generate_static_mac() { local jail_name="${1}" local external_interface="${2}" @@ -136,6 +173,43 @@ EOF fi } +set_target() { + local _TARGET="${1}" + if [ "${_TARGET}" = ALL ] || [ "${_TARGET}" = all ]; then + target_all_jails + else + check_target_exists "${_TARGET}" || error_exit "Jail not found \"${_TARGET}\"" + JAILS="${_TARGET}" + TARGET="${_TARGET}" + export JAILS + export TARGET + fi +} + +set_target_single() { + local _TARGET="${1}" + if [ "${_TARGET}" = ALL ] || [ "${_TARGET}" = all ]; then + error_exit "[all|ALL] not supported with this command." + else + check_target_exists "${_TARGET}" || error_exit "Jail not found \"${_TARGET}\"" + JAILS="${_TARGET}" + TARGET="${_TARGET}" + export JAILS + export TARGET + fi +} + +target_all_jails() { + local _JAILS="$(bastille list jails)" + JAILS="" + for _jail in ${_JAILS}; do + if [ -d "${bastille_jailsdir}/${_jail}" ]; then + JAILS="${JAILS} ${_jail}" + fi + done + export JAILS +} + checkyesno() { ## copied from /etc/rc.subr -- cedwards (20231125) ## issue #368 (lowercase values should be parsed) @@ -156,3 +230,4 @@ checkyesno() { ;; esac } + diff --git a/usr/local/share/bastille/htop.sh b/usr/local/share/bastille/htop.sh index de82387b..10795da1 100644 --- a/usr/local/share/bastille/htop.sh +++ b/usr/local/share/bastille/htop.sh @@ -32,29 +32,56 @@ . /usr/local/etc/bastille/bastille.conf usage() { - error_exit "Usage: bastille htop TARGET" + error_exit "Usage: bastille htop [option(s)] TARGET" + cat << EOF + Options: + + -f | --force -- Start the jail if it is stopped. + +EOF + exit 1 } -# Handle special-case commands first. -case "$1" in -help|-h|--help) - usage - ;; -esac +# Handle options. +FORCE=0 +while [ "$#" -gt 0 ]; do + case "${1}" in + -h|--help|help) + usage + ;; + -f|--force) + FORCE=1 + shift + ;; + -*) + error_exit "Unknown option: \"${1}\"" + ;; + *) + break + ;; + esac +done -if [ $# -ne 0 ]; then +if [ "$#" -ne 1 ]; then usage fi -bastille_root_check +TARGET="${1}" -for _jail in ${JAILS}; do - bastille_jail_path=$(/usr/sbin/jls -j "${_jail}" path) - if [ ! -x "${bastille_jail_path}/usr/local/bin/htop" ]; then - error_notify "htop not found on ${_jail}." - elif [ -x "${bastille_jail_path}/usr/local/bin/htop" ]; then - info "[${_jail}]:" - jexec -l ${_jail} /usr/local/bin/htop - fi - echo -e "${COLOR_RESET}" -done +bastille_root_check +set_target_single "${TARGET}" + +info "[${TARGET}]:" +check_target_is_running "${TARGET}" || if [ "${FORCE}" -eq 1 ]; then + bastille start "${TARGET}" +else + error_notify "Jail is not running." + error_continue "Use [-f|--force] to force start the jail." +fi + +bastille_jail_path="${bastille_jailsdir}/${TARGET}/root" +if [ ! -x "${bastille_jail_path}/usr/local/bin/htop" ]; then + error_notify "htop not found on ${TARGET}." +elif [ -x "${bastille_jail_path}/usr/local/bin/htop" ]; then + jexec -l ${TARGET} /usr/local/bin/htop +fi diff --git a/usr/local/share/bastille/mount.sh b/usr/local/share/bastille/mount.sh index bb0e6615..95e84071 100644 --- a/usr/local/share/bastille/mount.sh +++ b/usr/local/share/bastille/mount.sh @@ -32,96 +32,127 @@ . /usr/local/etc/bastille/bastille.conf usage() { - error_exit "Usage: bastille mount TARGET host_path container_path [filesystem_type options dump pass_number]" + error_exit "Usage: bastille mount TARGET HOST_PATH JAIL_PATH [filesystem_type options dump pass_number]" } # Handle special-case commands first. -case "$1" in -help|-h|--help) - usage - ;; +case "${1}" in + help|-h|--help) + usage + ;; esac -if [ $# -lt 2 ]; then +if [ "$#" -lt 3 ] || [ "$#" -gt 6 ]; then usage -elif [ $# -eq 2 ]; then - _fstab="$@ nullfs ro 0 0" +fi + +TARGET="${1}" +shift + +if [ "$#" -eq 2 ]; then + _fstab="$(echo "$* nullfs ro 0 0" | sed 's#\\ #\\040#g')" else - _fstab="$@" + _fstab="$(echo "$*" | sed 's#\\ #\\040#g')" fi bastille_root_check +set_target "${TARGET}" -## assign needed variables -_hostpath=$(echo "${_fstab}" | awk '{print $1}') -_jailpath=$(echo "${_fstab}" | awk '{print $2}') +# Assign variables +_hostpath_fstab=$(echo "${_fstab}" | awk '{print $1}') +_hostpath="$(echo "${_hostpath_fstab}" 2>/dev/null | sed 's#\\040# #g')" +_jailpath_fstab=$(echo "${_fstab}" | awk '{print $2}') +_jailpath="$(echo "${_jailpath_fstab}" 2>/dev/null | sed 's#\\040# #g')" _type=$(echo "${_fstab}" | awk '{print $3}') _perms=$(echo "${_fstab}" | awk '{print $4}') _checks=$(echo "${_fstab}" | awk '{print $5" "$6}') -## if any variables are empty, bail out +# Exit if any variables are empty if [ -z "${_hostpath}" ] || [ -z "${_jailpath}" ] || [ -z "${_type}" ] || [ -z "${_perms}" ] || [ -z "${_checks}" ]; then error_notify "FSTAB format not recognized." - warn "Format: /host/path jail/path nullfs ro 0 0" + warn "Format: /host/path /jail/path nullfs ro 0 0" warn "Read: ${_fstab}" - exit 1 + usage fi -# if host path doesn't exist, type is not "nullfs" or are using advanced mount type "tmpfs,linprocfs,linsysfs, fdescfs, -# procfs" +# Exit if host path doesn't exist, type is not "nullfs", or mount is an advanced mount type "tmpfs,linprocfs,linsysfs,fdescfs,procfs" if { [ "${_hostpath}" = "tmpfs" ] && [ "$_type" = "tmpfs" ]; } || \ { [ "${_hostpath}" = "linprocfs" ] && [ "${_type}" = "linprocfs" ]; } || \ { [ "${_hostpath}" = "linsysfs" ] && [ "${_type}" = "linsysfs" ]; } || \ { [ "${_hostpath}" = "proc" ] && [ "${_type}" = "procfs" ]; } || \ { [ "${_hostpath}" = "fdesc" ] && [ "${_type}" = "fdescfs" ]; } then warn "Detected advanced mount type ${_hostpath}" -elif [ ! -d "${_hostpath}" ] || [ "${_type}" != "nullfs" ]; then - error_notify "Detected invalid host path or incorrect mount type in FSTAB." - warn "Format: /host/path jail/path nullfs ro 0 0" +elif [ ! -e "${_hostpath}" ] || [ "${_type}" != "nullfs" ]; then + error_notify "Invalid host path or incorrect mount type in FSTAB." + warn "Format: /host/path /jail/path nullfs ro 0 0" warn "Read: ${_fstab}" - exit 1 + usage fi -## if mount permissions are not "ro" or "rw" +# Mount permissions need to be "ro" or "rw" if [ "${_perms}" != "ro" ] && [ "${_perms}" != "rw" ]; then error_notify "Detected invalid mount permissions in FSTAB." - warn "Format: /host/path jail/path nullfs ro 0 0" + warn "Format: /host/path /jail/path nullfs ro 0 0" warn "Read: ${_fstab}" - exit 1 + usage fi -## if check & pass are not "0 0 - 1 1"; bail out +# Dump and pass need to be "0 0 - 1 1" if [ "${_checks}" != "0 0" ] && [ "${_checks}" != "1 0" ] && [ "${_checks}" != "0 1" ] && [ "${_checks}" != "1 1" ]; then error_notify "Detected invalid fstab options in FSTAB." - warn "Format: /host/path jail/path nullfs ro 0 0" + warn "Format: /host/path /jail/path nullfs ro 0 0" warn "Read: ${_fstab}" - exit 1 + usage fi for _jail in ${JAILS}; do + info "[${_jail}]:" - ## aggregate variables into FSTAB entry - _fullpath="${bastille_jailsdir}/${_jail}/root/${_jailpath}" - _fstab_entry="${_hostpath} ${_fullpath} ${_type} ${_perms} ${_checks}" + _fullpath_fstab="$( echo "${bastille_jailsdir}/${_jail}/root/${_jailpath_fstab}" 2>/dev/null | sed 's#//#/#' )" + _fullpath="$( echo "${bastille_jailsdir}/${_jail}/root/${_jailpath}" 2>/dev/null | sed 's#//#/#' )" + _fstab_entry="${_hostpath_fstab} ${_fullpath_fstab} ${_type} ${_perms} ${_checks}" - ## Create mount point if it does not exist. -- cwells - if [ ! -d "${_fullpath}" ]; then - if ! mkdir -p "${_fullpath}"; then - error_exit "Failed to create mount point inside jail." - fi - fi - - ## if entry doesn't exist, add; else show existing entry - if ! egrep -q "[[:blank:]]${_fullpath}[[:blank:]]" "${bastille_jailsdir}/${_jail}/fstab" 2> /dev/null; then - if ! echo "${_fstab_entry}" >> "${bastille_jailsdir}/${_jail}/fstab"; then - error_exit "Failed to create fstab entry: ${_fstab_entry}" - fi - echo "Added: ${_fstab_entry}" - else + # Check if mount point has already been added + _existing_mount="$(echo ${_fullpath_fstab} 2>/dev/null | sed 's#\\#\\\\#g')" + if grep -Eq "[[:blank:]]${_existing_mount}.*[[:blank:]]" "${bastille_jailsdir}/${_jail}/fstab"; then warn "Mountpoint already present in ${bastille_jailsdir}/${_jail}/fstab" - egrep "[[:blank:]]${_fullpath}[[:blank:]]" "${bastille_jailsdir}/${_jail}/fstab" + grep -E "[[:blank:]]${_existing_mount}" "${bastille_jailsdir}/${_jail}/fstab" + continue fi - mount -F "${bastille_jailsdir}/${_jail}/fstab" -a - echo + + + # Create mount point if it does not exist + if [ -d "${_hostpath}" ] && [ ! -d "${_fullpath}" ]; then + mkdir -p "${_fullpath}" || error_continue "Failed to create mount point." + elif [ -f "${_hostpath}" ] ; then + _filename="$( basename ${_hostpath} )" + if echo "${_fullpath}" 2>/dev/null | grep -qow "${_filename}"; then + mkdir -p "$( dirname "${_fullpath}" )" || error_continue "Failed to create mount point." + if [ ! -f "${_fullpath}" ]; then + touch "${_fullpath}" || error_continue "Failed to create mount point." + else + error_notify "Failed. File exists at mount point." + warn "${_fullpath}" + continue + fi + else + _fullpath_fstab="$( echo "${bastille_jailsdir}/${_jail}/root/${_jailpath_fstab}/${_filename}" 2>/dev/null | sed 's#//#/#' )" + _fullpath="$( echo "${bastille_jailsdir}/${_jail}/root/${_jailpath}/${_filename}" 2>/dev/null | sed 's#//#/#' )" + _fstab_entry="${_hostpath_fstab} ${_fullpath_fstab} ${_type} ${_perms} ${_checks}" + mkdir -p "$( dirname "${_fullpath}" )" || error_continue "Failed to create mount point." + if [ ! -f "${_fullpath}" ]; then + touch "${_fullpath}" || error_continue "Failed to create mount point." + else + error_notify "Failed. File exists at mount point." + warn "${_fullpath}" + continue + fi + fi + fi + + # Add entry to fstab and mount + echo "${_fstab_entry}" >> "${bastille_jailsdir}/${_jail}/fstab" || error_continue "Failed to create fstab entry: ${_fstab_entry}" + mount -F "${bastille_jailsdir}/${_jail}/fstab" -a || error_continue "Failed to mount volume: ${_fullpath}" + echo "Added: ${_fstab_entry}" done diff --git a/usr/local/share/bastille/template.sh b/usr/local/share/bastille/template.sh index 53f50bc0..203dc109 100644 --- a/usr/local/share/bastille/template.sh +++ b/usr/local/share/bastille/template.sh @@ -281,7 +281,7 @@ for _jail in ${JAILS}; do # First word converted to lowercase is the Bastille command. -- cwells _cmd=$(echo "${_line}" | awk '{print tolower($1);}') # Rest of the line with "arg" variables replaced will be the arguments. -- cwells - _args=$(echo "${_line}" | awk '{$1=""; sub(/^ */, ""); print;}' | eval "sed ${ARG_REPLACEMENTS}") + _args=$(echo "${_line}" | awk -F '[ ]' '{$1=""; sub(/^ */, ""); print;}' | eval "sed ${ARG_REPLACEMENTS}") # Apply overrides for commands/aliases and arguments. -- cwells case $_cmd in diff --git a/usr/local/share/bastille/top.sh b/usr/local/share/bastille/top.sh index 1e8cbb9c..669c1164 100644 --- a/usr/local/share/bastille/top.sh +++ b/usr/local/share/bastille/top.sh @@ -29,26 +29,53 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. . /usr/local/share/bastille/common.sh +. /usr/local/etc/bastille/bastille.conf usage() { - error_exit "Usage: bastille top TARGET" + error_notify "Usage: bastille top [options(s)] TARGET" + cat << EOF + Options: + + -f | --force -- Start the jail if it is stopped. + +EOF + exit 1 } -# Handle special-case commands first. -case "$1" in -help|-h|--help) - usage - ;; -esac +# Handle options. +FORCE=0 +while [ "$#" -gt 0 ]; do + case "${1}" in + -h|--help|help) + usage + ;; + -f|--force) + FORCE=1 + shift + ;; + -*) + error_exit "Unknown option: \"${1}\"" + ;; + *) + break + ;; + esac +done -if [ $# -ne 0 ]; then +if [ "$#" -ne 1 ]; then usage fi -bastille_root_check +TARGET="${1}" -for _jail in ${JAILS}; do - info "[${_jail}]:" - jexec -l "${_jail}" /usr/bin/top - echo -e "${COLOR_RESET}" -done +bastille_root_check +set_target_single "${TARGET}" + +info "[${TARGET}]:" +check_target_is_running "${TARGET}" || if [ "${FORCE}" -eq 1 ]; then + bastille start "${TARGET}" +else + error_notify "Jail is not running." + error_continue "Use [-f|--force] to force start the jail." +fi +jexec -l "${TARGET}" /usr/bin/top diff --git a/usr/local/share/bastille/umount.sh b/usr/local/share/bastille/umount.sh index dfd57664..ebbb52c5 100644 --- a/usr/local/share/bastille/umount.sh +++ b/usr/local/share/bastille/umount.sh @@ -32,43 +32,57 @@ . /usr/local/etc/bastille/bastille.conf usage() { - error_exit "Usage: bastille umount TARGET container_path" + error_exit "Usage: bastille umount TARGET JAIL_PATH" } # Handle special-case commands first. -case "$1" in -help|-h|--help) - usage - ;; +case "${1}" in + help|-h|--help) + usage + ;; esac -if [ $# -ne 1 ]; then +if [ "$#" -ne 2 ]; then usage fi -bastille_root_check +TARGET="${1}" +MOUNT_PATH="${2}" -MOUNT_PATH=$1 +bastille_root_check +set_target "${TARGET}" for _jail in ${JAILS}; do + info "[${_jail}]:" - _jailpath="${bastille_jailsdir}/${_jail}/root/${MOUNT_PATH}" + _jailpath="$( echo "${bastille_jailsdir}/${_jail}/root/${MOUNT_PATH}" 2>/dev/null | sed 's#//#/#' | sed 's#\\##g')" + _mount="$( mount | grep -Eo "[[:blank:]]${_jailpath}[[:blank:]]" )" + _jailpath_fstab="$(echo "${bastille_jailsdir}/${_jail}/root/${MOUNT_PATH}" | sed 's#//#/#g' | sed 's# #\\#g' | sed 's#\\#\\\\040#g')" + _fstab_entry="$(grep -Eo "[[:blank:]]${_jailpath_fstab}[[:blank:]]" ${bastille_jailsdir}/${_jail}/fstab)" - if [ ! -d "${_jailpath}" ]; then - error_exit "The specified mount point does not exist inside the jail." + # Exit if mount point non-existent + if [ -z "${_mount}" ] && [ -z "${_fstab_entry}" ]; then + error_continue "The specified mount point does not exist." fi - # Unmount the volume. -- cwells - if ! umount "${_jailpath}"; then - error_exit "Failed to unmount volume: ${MOUNT_PATH}" + # Unmount + if [ -n "${_mount}" ]; then + umount "${_jailpath}" || error_continue "Failed to unmount volume: ${MOUNT_PATH}" fi - # Remove the entry from fstab so it is not automounted in the future. -- cwells - if ! sed -E -i '' "\, +${_jailpath} +,d" "${bastille_jailsdir}/${_jail}/fstab"; then - error_exit "Failed to delete fstab entry: ${_fstab_entry}" + # Remove entry from fstab + if [ -n "${_fstab_entry}" ]; then + if ! sed -E -i '' "\, +${_jailpath_fstab} +,d" "${bastille_jailsdir}/${_jail}/fstab"; then + error_continue "Failed to delete fstab entry: ${MOUNT_PATH}" + fi fi - echo "Unmounted: ${MOUNT_PATH}" - echo + # Delete if mount point was a file + if [ -f "${_jailpath}" ]; then + rm -f "${_jailpath}" || error_continue "Failed to unmount volume: ${MOUNT_PATH}" + fi + + echo "Unmounted: ${_jailpath}" + done