diff --git a/usr/local/bin/bastille b/usr/local/bin/bastille index b526e42..cf65d86 100755 --- a/usr/local/bin/bastille +++ b/usr/local/bin/bastille @@ -135,10 +135,10 @@ version|-v|--version) help|-h|--help) usage ;; -bootstrap|create|destroy|import|list|rdr|restart|start|update|upgrade|verify) +bootstrap|create|destroy|export|import|list|rdr|restart|start|update|upgrade|verify) # Nothing "extra" to do for these commands. -- cwells ;; -clone|config|cmd|console|convert|cp|edit|export|htop|limits|mount|pkg|rename|service|stop|sysrc|template|top|umount|zfs) +clone|config|cmd|console|convert|cp|edit|htop|limits|mount|pkg|rename|service|stop|sysrc|template|top|umount|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' diff --git a/usr/local/etc/bastille/bastille.conf.sample b/usr/local/etc/bastille/bastille.conf.sample index 08bbfb0..c618ef3 100644 --- a/usr/local/etc/bastille/bastille.conf.sample +++ b/usr/local/etc/bastille/bastille.conf.sample @@ -44,6 +44,8 @@ bastille_zfs_options="-o compress=lz4 -o atime=off" ## default ## Export/Import options bastille_compress_xz_options="-0 -v" ## default "-0 -v" bastille_decompress_xz_options="-c -d -v" ## default "-c -d -v" +bastille_compress_gz_options="-1 -v" ## default "-1 -v" +bastille_decompress_gz_options="-k -d -c -v" ## default "-k -d -c -v" ## Networking bastille_network_loopback="bastille0" ## default: "bastille0" diff --git a/usr/local/share/bastille/convert.sh b/usr/local/share/bastille/convert.sh index ae492ef..30d3469 100644 --- a/usr/local/share/bastille/convert.sh +++ b/usr/local/share/bastille/convert.sh @@ -57,6 +57,7 @@ convert_symlinks() { done # Copy new files to destination jail + info "Copying required base files to container..." for _link in ${SYMLINKS}; do if [ ! -d "${_link}" ]; then if [ -d "${bastille_releasesdir}/${RELEASE}/${_link}" ]; then @@ -100,13 +101,15 @@ revert_convert() { start_convert() { # Attempt container conversion and handle some errors + DATE=$(date) if [ -d "${bastille_jailsdir}/${TARGET}" ]; then info "Converting '${TARGET}' into a thickjail. This may take a while..." # Set some variables - RELEASE=$(grep -owE '([1-9]{2,2})\.[0-9](-RELEASE|-RELEASE-i386|-RC[1-2])|([0-9]{1,2}-stable-build-[0-9]{1,3})|(current-build)-([0-9]{1,3})|(current-BUILD-LATEST)|([0-9]{1,2}-stable-BUILD-LATEST)|(current-BUILD-LATEST)' "${bastille_jailsdir}/${TARGET}/fstab") + RELEASE=$(grep -w "${bastille_releasesdir}/.* ${bastille_jailsdir}/${TARGET}/root/.bastille" ${bastille_jailsdir}/${TARGET}/fstab | sed "s|${bastille_releasesdir}/||;s| .*||") FSTABMOD=$(grep -w "${bastille_releasesdir}/${RELEASE} ${bastille_jailsdir}/${TARGET}/root/.bastille" "${bastille_jailsdir}/${TARGET}/fstab") SYMLINKS="bin boot lib libexec rescue sbin usr/bin usr/include usr/lib usr/lib32 usr/libdata usr/libexec usr/ports usr/sbin usr/share usr/src" + HASPORTS=$(grep -w ${bastille_releasesdir}/${RELEASE}/usr/ports ${bastille_jailsdir}/${TARGET}/fstab) if [ -n "${RELEASE}" ]; then cd "${bastille_jailsdir}/${TARGET}/root" @@ -115,7 +118,12 @@ start_convert() { convert_symlinks # Comment the line containing .bastille and rename mountpoint - sed -i '' -E "s|${FSTABMOD}|# Converted from thin to thick container on $(date)|g" "${bastille_jailsdir}/${TARGET}/fstab" + sed -i '' -E "s|${FSTABMOD}|# Converted from thin to thick container on ${DATE}|g" "${bastille_jailsdir}/${TARGET}/fstab" + if [ -n "${HASPORTS}" ]; then + sed -i '' -E "s|${HASPORTS}|# Ports copied from base to container on ${DATE}|g" "${bastille_jailsdir}/${TARGET}/fstab" + info "Copying ports to container..." + cp -a "${bastille_releasesdir}/${RELEASE}/usr/ports" "${bastille_jailsdir}/${TARGET}/root/usr" + fi mv "${bastille_jailsdir}/${TARGET}/root/.bastille" "${bastille_jailsdir}/${TARGET}/root/.bastille.old" info "Conversion of '${TARGET}' completed successfully!" diff --git a/usr/local/share/bastille/create.sh b/usr/local/share/bastille/create.sh index 4c62bef..faa6dab 100644 --- a/usr/local/share/bastille/create.sh +++ b/usr/local/share/bastille/create.sh @@ -508,36 +508,40 @@ EMPTY_JAIL="" THICK_JAIL="" VNET_JAIL="" -## handle combined options then shift -if [ "${1}" = "-T" -o "${1}" = "--thick" -o "${1}" = "thick" ] && \ - [ "${2}" = "-V" -o "${2}" = "--vnet" -o "${2}" = "vnet" ]; then - THICK_JAIL="1" - VNET_JAIL="1" - shift 2 -else - ## handle single options +# Handle and parse options +while [ $# -gt 0 ]; do case "${1}" in -E|--empty|empty) - shift EMPTY_JAIL="1" + shift ;; -L|--linux|linux) shift LINUX_JAIL="1" ;; -T|--thick|thick) - shift THICK_JAIL="1" + shift ;; -V|--vnet|vnet) - shift VNET_JAIL="1" + shift ;; - -*) + -*|--*) error_notify "Unknown Option." usage ;; + *) + break + ;; esac +done + +## validate for combined options +if [ -n "${EMPTY_JAIL}" ]; then + if [ -n "${THICK_JAIL}" ] || [ -n "${VNET_JAIL}" ]; then + error_exit "Error: Empty jail option can't be used with other options." + fi fi NAME="$1" diff --git a/usr/local/share/bastille/export.sh b/usr/local/share/bastille/export.sh index 1a0a1d5..af1d284 100644 --- a/usr/local/share/bastille/export.sh +++ b/usr/local/share/bastille/export.sh @@ -32,7 +32,25 @@ . /usr/local/etc/bastille/bastille.conf usage() { - error_exit "Usage: bastille export TARGET [safe|tarball] | PATH" + # Build an independent usage for the export command + # Valid compress/options for ZFS systems are raw, .gz, .tgz, .txz and .xz + # Valid compress/options for non ZFS configured systems are .tgz and .txz + # If no compression option specified, user must redirect standard output + echo -e "${COLOR_RED}Usage: bastille export | option(s) | TARGET | PATH${COLOR_RESET}" + + cat << EOF + Options: + + --gz -- Export a ZFS jail using GZIP(.gz) compressed image. + -r | --raw -- Export a ZFS jail to an uncompressed RAW image. + -s | --safe -- Safely stop and start a ZFS jail before the exporting process. + --tgz -- Export a jail using simple .tgz compressed archive instead. + --txz -- Export a jail using simple .txz compressed archive instead. + -v | --verbose -- Be more verbose during the ZFS send operation. + --xz -- Export a ZFS jail using XZ(.xz) compressed image. + +EOF + exit 1 } # Handle special-case commands first @@ -47,90 +65,258 @@ if [ "${TARGET}" = "ALL" ]; then error_exit "Batch export is unsupported." fi -if [ $# -gt 2 ] || [ $# -lt 0 ]; then +if [ $# -gt 5 ] || [ $# -lt 1 ]; then usage fi -OPTION="${1}" -EXPATH="${2}" -SAFE_EXPORT= +zfs_enable_check() { + # Temporarily disable ZFS so we can create a standard backup archive + if [ "${bastille_zfs_enable}" = "YES" ]; then + bastille_zfs_enable="NO" + fi +} -# Handle some options -if [ -n "${OPTION}" ]; then - if [ "${OPTION}" = "-t" -o "${OPTION}" = "--txz" -o ${OPTION} = "tarball" ]; then - if [ "${bastille_zfs_enable}" = "YES" ]; then - # Temporarily disable ZFS so we can create a standard backup archive - bastille_zfs_enable="NO" - fi - elif [ "${OPTION}" = "-s" -o "${OPTION}" = "--safe" -o ${OPTION} = "safe" ]; then - SAFE_EXPORT="1" - elif echo "${OPTION}" | grep -q "\/"; then - if [ -d "${OPTION}" ]; then - EXPATH="${OPTION}" - else - error_exit "Error: Path not found." - fi - else - error_notify "Invalid option!" - usage +TARGET="${1}" +GZIP_EXPORT= +SAFE_EXPORT= +USER_EXPORT= +RAW_EXPORT= +DIR_EXPORT= +TXZ_EXPORT= +TGZ_EXPORT= +OPT_ZSEND="-R" +COMP_OPTION="0" + +opt_count() { + COMP_OPTION=$(expr ${COMP_OPTION} + 1) +} + +# Handle and parse option args +while [ $# -gt 0 ]; do + case "${1}" in + --gz) + GZIP_EXPORT="1" + TARGET="${2}" + opt_count + shift + ;; + --xz) + XZ_EXPORT="1" + TARGET="${2}" + opt_count + shift + ;; + --tgz) + TGZ_EXPORT="1" + TARGET="${2}" + opt_count + zfs_enable_check + shift + ;; + --txz) + TXZ_EXPORT="1" + TARGET="${2}" + opt_count + zfs_enable_check + shift + ;; + -s|--safe) + SAFE_EXPORT="1" + TARGET="${2}" + shift + ;; + -r|--raw) + RAW_EXPORT="1" + TARGET="${2}" + opt_count + shift + ;; + -v|--verbose) + OPT_ZSEND="-Rv" + TARGET="${2}" + shift + ;; + -*|--*) + error_notify "Unknown Option." + usage + ;; + *) + if echo "${1}" | grep -q "\/"; then + DIR_EXPORT="${1}" + else + if [ $# -gt 2 ] || [ $# -lt 1 ]; then + usage + fi + fi + shift + ;; + esac +done + +# Validate for combined options +if [ "${COMP_OPTION}" -gt "1" ]; then + error_exit "Error: Only one compression format can be used during export." +fi + +if [ -n "${TXZ_EXPORT}" -o -n "${TGZ_EXPORT}" ] && [ -n "${SAFE_EXPORT}" ]; then + error_exit "Error: Simple archive modes with safe ZFS export can't be used together." +fi + +if [ -z "${bastille_zfs_enable}" ]; then + if [ -n "${GZIP_EXPORT}" -o -n "${RAW_EXPORT}" -o -n "${SAFE_EXPORT}" -o "${OPT_ZSEND}" = "-Rv" ]; then + error_exit "Options --gz, --raw, --safe, --verbose are valid for ZFS configured systems only." + fi +fi + +if [ -n "${SAFE_EXPORT}" ]; then + # Check if container is running, otherwise just ignore + if [ -z "$(jls name | awk "/^${TARGET}$/")" ]; then + SAFE_EXPORT= fi fi # Export directory check -if [ -n "${EXPATH}" ]; then - if [ -d "${EXPATH}" ]; then +if [ -n "${DIR_EXPORT}" ]; then + if [ -d "${DIR_EXPORT}" ]; then # Set the user defined export directory - bastille_backupsdir="${EXPATH}" + bastille_backupsdir="${DIR_EXPORT}" else error_exit "Error: Path not found." fi fi -create_zfs_snap(){ +# Fallback to default if missing config parameters +if [ -z "${bastille_compress_xz_options}" ]; then + bastille_compress_xz_options="-0 -v" +fi +if [ -z "${bastille_compress_gz_options}" ]; then + bastille_compress_gz_options="-1 -v" +fi + +create_zfs_snap() { # Take a recursive temporary snapshot - info "Creating temporary ZFS snapshot for export..." + if [ -z "${USER_EXPORT}" ]; then + info "Creating temporary ZFS snapshot for export..." + fi zfs snapshot -r "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET}@bastille_export_${DATE}" } -jail_export() -{ +clean_zfs_snap() { + # Cleanup the recursive temporary snapshot + zfs destroy "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET}/root@bastille_export_${DATE}" + zfs destroy "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET}@bastille_export_${DATE}" +} + +export_check() { + # Inform the user about the exporting method + if [ -z "${USER_EXPORT}" ]; then + if [ -n "$(jls name | awk "/^${TARGET}$/")" ]; then + if [ -n "${SAFE_EXPORT}" ]; then + EXPORT_AS="Safely exporting" + else + EXPORT_AS="Hot exporting" + fi + else + EXPORT_AS="Exporting" + fi + + if [ "${FILE_EXT}" = ".xz" -o "${FILE_EXT}" = ".gz" -o "${FILE_EXT}" = "" ]; then + EXPORT_TYPE="image" + else + EXPORT_TYPE="archive" + fi + + if [ -n "${RAW_EXPORT}" ]; then + EXPORT_INFO="to a raw ${EXPORT_TYPE}" + else + EXPORT_INFO="to a compressed ${FILE_EXT} ${EXPORT_TYPE}" + fi + + info "${EXPORT_AS} '${TARGET}' ${EXPORT_INFO}..." + fi + + # Safely stop and snapshot the jail + if [ -n "${SAFE_EXPORT}" ]; then + bastille stop ${TARGET} + create_zfs_snap + bastille start ${TARGET} + else + create_zfs_snap + fi + + if [ "${bastille_zfs_enable}" = "YES" ]; then + if [ -z "${USER_EXPORT}" ]; then + info "Sending ZFS data stream..." + fi + fi +} + +jail_export() { # Attempt to export the container DATE=$(date +%F-%H%M%S) if [ "${bastille_zfs_enable}" = "YES" ]; then if [ -n "${bastille_zfs_zpool}" ]; then - FILE_EXT="xz" + if [ -n "${RAW_EXPORT}" ]; then + FILE_EXT="" + export_check - if [ -n "${SAFE_EXPORT}" ]; then - info "Safely exporting '${TARGET}' to a compressed .${FILE_EXT} archive." - bastille stop ${TARGET} - create_zfs_snap - bastille start ${TARGET} + # Export the raw container recursively and cleanup temporary snapshots + zfs send ${OPT_ZSEND} "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET}@bastille_export_${DATE}" \ + > "${bastille_backupsdir}/${TARGET}_${DATE}" + clean_zfs_snap + elif [ -n "${GZIP_EXPORT}" ]; then + FILE_EXT=".gz" + export_check + + # Export the raw container recursively and cleanup temporary snapshots + zfs send ${OPT_ZSEND} "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET}@bastille_export_${DATE}" | \ + gzip ${bastille_compress_gz_options} > "${bastille_backupsdir}/${TARGET}_${DATE}${FILE_EXT}" + clean_zfs_snap + elif [ -n "${XZ_EXPORT}" ]; then + FILE_EXT=".xz" + export_check + + # Export the container recursively and cleanup temporary snapshots + zfs send ${OPT_ZSEND} "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET}@bastille_export_${DATE}" | \ + xz ${bastille_compress_xz_options} > "${bastille_backupsdir}/${TARGET}_${DATE}${FILE_EXT}" + clean_zfs_snap else - info "Hot exporting '${TARGET}' to a compressed .${FILE_EXT} archive." - create_zfs_snap - fi + FILE_EXT="" + USER_EXPORT="1" + export_check - info "Sending ZFS data stream..." - # Export the container recursively and cleanup temporary snapshots - zfs send -R "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET}@bastille_export_${DATE}" | \ - xz ${bastille_compress_xz_options} > "${bastille_backupsdir}/${TARGET}_${DATE}.${FILE_EXT}" - zfs destroy "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET}/root@bastille_export_${DATE}" - zfs destroy "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET}@bastille_export_${DATE}" + # Quietly export the container recursively, user must redirect standard output + zfs send ${OPT_ZSEND} "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET}@bastille_export_${DATE}" + clean_zfs_snap + fi fi else - # Create standard backup archive - FILE_EXT="txz" - info "Exporting '${TARGET}' to a compressed .${FILE_EXT} archive..." - cd "${bastille_jailsdir}" && tar -cf - "${TARGET}" | xz ${bastille_compress_xz_options} > "${bastille_backupsdir}/${TARGET}_${DATE}.${FILE_EXT}" + if [ -n "${TGZ_EXPORT}" ]; then + FILE_EXT=".tgz" + + # Create standard tgz backup archive + info "Exporting '${TARGET}' to a compressed ${FILE_EXT} archive..." + cd "${bastille_jailsdir}" && tar -cf - "${TARGET}" | gzip ${bastille_compress_gz_options} > "${bastille_backupsdir}/${TARGET}_${DATE}${FILE_EXT}" + elif [ -n "${TXZ_EXPORT}" ]; then + FILE_EXT=".txz" + + # Create standard txz backup archive + info "Exporting '${TARGET}' to a compressed ${FILE_EXT} archive..." + cd "${bastille_jailsdir}" && tar -cf - "${TARGET}" | xz ${bastille_compress_xz_options} > "${bastille_backupsdir}/${TARGET}_${DATE}${FILE_EXT}" + else + error_exit "Error: export option required" + fi fi if [ "$?" -ne 0 ]; then error_exit "Failed to export '${TARGET}' container." else - # Generate container checksum file - cd "${bastille_backupsdir}" - sha256 -q "${TARGET}_${DATE}.${FILE_EXT}" > "${TARGET}_${DATE}.sha256" - info "Exported '${bastille_backupsdir}/${TARGET}_${DATE}.${FILE_EXT}' successfully." + if [ -z "${USER_EXPORT}" ]; then + # Generate container checksum file + cd "${bastille_backupsdir}" + sha256 -q "${TARGET}_${DATE}${FILE_EXT}" > "${TARGET}_${DATE}.sha256" + info "Exported '${bastille_backupsdir}/${TARGET}_${DATE}${FILE_EXT}' successfully." + fi exit 0 fi } @@ -140,12 +326,17 @@ if [ ! -d "${bastille_backupsdir}" ]; then error_exit "Backups directory/dataset does not exist. See 'bastille bootstrap'." fi -# Check if is a ZFS system -if [ "${bastille_zfs_enable}" != "YES" ]; then - # Check if container is running and ask for stop in UFS systems - if [ -n "$(jls name | awk "/^${TARGET}$/")" ]; then - error_exit "${TARGET} is running. See 'bastille stop'." +if [ -n "${TARGET}" ]; then + if [ ! -d "${bastille_jailsdir}/${TARGET}" ]; then + error_exit "[${TARGET}]: Not found." fi -fi -jail_export + # Check if is a ZFS system + if [ "${bastille_zfs_enable}" != "YES" ]; then + # Check if container is running and ask for stop in non ZFS systems + if [ -n "$(jls name | awk "/^${TARGET}$/")" ]; then + error_exit "${TARGET} is running. See 'bastille stop'." + fi + fi + jail_export +fi diff --git a/usr/local/share/bastille/import.sh b/usr/local/share/bastille/import.sh index fa542ba..48e88df 100644 --- a/usr/local/share/bastille/import.sh +++ b/usr/local/share/bastille/import.sh @@ -32,7 +32,18 @@ . /usr/local/etc/bastille/bastille.conf usage() { - error_exit "Usage: bastille import file [force]" + # Build an independent usage for the import command + # If no file/extension specified, will import from standard input + echo -e "${COLOR_RED}Usage: bastille import [option(s)] FILE${COLOR_RESET}" + + cat << EOF + Options: + + -f | --force -- Force an archive import regardless if the checksum file does not match or missing. + -v | --verbose -- Be more verbose during the ZFS receive operation. + +EOF + exit 1 } # Handle special-case commands first @@ -42,39 +53,70 @@ help|-h|--help) ;; esac -if [ $# -gt 2 ] || [ $# -lt 1 ]; then +if [ $# -gt 3 ] || [ $# -lt 1 ]; then usage fi TARGET="${1}" -OPTION="${2}" -shift +OPT_FORCE= +USER_IMPORT= +OPT_ZRECV="-u" + +# Handle and parse option args +while [ $# -gt 0 ]; do + case "${1}" in + -f|--force) + OPT_FORCE="1" + TARGET="${2}" + shift + ;; + -v|--verbose) + OPT_ZRECV="-u -v" + TARGET="${2}" + shift + ;; + -*|--*) + error_notify "Unknown Option." + usage + ;; + *) + if [ $# -gt 1 ] || [ $# -lt 1 ]; then + usage + fi + shift + ;; + esac +done + +# Fallback to default if missing config parameters +if [ -z "${bastille_decompress_xz_options}" ]; then + bastille_decompress_xz_options="-c -d -v" +fi +if [ -z "${bastille_decompress_gz_options}" ]; then + bastille_decompress_gz_options="-k -d -c -v" +fi validate_archive() { # Compare checksums on the target archive - # Skip validation for unsupported archives - if [ "${FILE_EXT}" != ".tar.gz" ] && [ "${FILE_EXT}" != ".tar" ]; then - if [ -f "${bastille_backupsdir}/${TARGET}" ]; then - if [ -f "${bastille_backupsdir}/${FILE_TRIM}.sha256" ]; then - info "Validating file: ${TARGET}..." - SHA256_DIST=$(cat "${bastille_backupsdir}/${FILE_TRIM}.sha256") - SHA256_FILE=$(sha256 -q "${bastille_backupsdir}/${TARGET}") - if [ "${SHA256_FILE}" != "${SHA256_DIST}" ]; then - error_exit "Failed validation for ${TARGET}." - else - info "File validation successful!" - fi + # Skip validation for unsupported archive + if [ -f "${bastille_backupsdir}/${TARGET}" ]; then + if [ -f "${bastille_backupsdir}/${FILE_TRIM}.sha256" ]; then + info "Validating file: ${TARGET}..." + SHA256_DIST=$(cat "${bastille_backupsdir}/${FILE_TRIM}.sha256") + SHA256_FILE=$(sha256 -q "${bastille_backupsdir}/${TARGET}") + if [ "${SHA256_FILE}" != "${SHA256_DIST}" ]; then + error_exit "Failed validation for ${TARGET}." else - # Check if user opt to force import - if [ "${OPTION}" = "-f" -o "${OPTION}" = "force" ]; then - warn "Warning: Skipping archive validation!" - else - error_exit "Checksum file not found. See 'bastille import TARGET -f'." - fi + info "File validation successful!" + fi + else + # Check if user opt to force import + if [ -n "${OPT_FORCE}" ]; then + warn "Warning: Skipping archive validation!" + else + error_exit "Checksum file not found. See 'bastille import [option(s)] FILE'." fi fi - else - warn "Warning: Skipping archive validation!" fi } @@ -313,23 +355,34 @@ remove_zfs_datasets() { jail_import() { # Attempt to import container from file - FILE_TRIM=$(echo "${TARGET}" | sed 's/\.xz//g;s/\.txz//g;s/\.zip//g;s/\.tar\.gz//g;s/\.tar//g') + FILE_TRIM=$(echo "${TARGET}" | sed 's/\.xz//g;s/\.gz//g;s/\.tgz//g;s/\.txz//g;s/\.zip//g;s/\.tar\.gz//g;s/\.tar//g') FILE_EXT=$(echo "${TARGET}" | sed "s/${FILE_TRIM}//g") - validate_archive if [ -d "${bastille_jailsdir}" ]; then if [ "${bastille_zfs_enable}" = "YES" ]; then if [ -n "${bastille_zfs_zpool}" ]; then if [ "${FILE_EXT}" = ".xz" ]; then + validate_archive # Import from compressed xz on ZFS systems - info "Importing '${TARGET_TRIM}' from compressed ${FILE_EXT} archive." + info "Importing '${TARGET_TRIM}' from compressed ${FILE_EXT} image." info "Receiving ZFS data stream..." xz ${bastille_decompress_xz_options} "${bastille_backupsdir}/${TARGET}" | \ - zfs receive -u "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET_TRIM}" + zfs receive ${OPT_ZRECV} "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET_TRIM}" + + # Update ZFS mountpoint property if required + update_zfsmount + elif [ "${FILE_EXT}" = ".gz" ]; then + validate_archive + # Import from compressed xz on ZFS systems + info "Importing '${TARGET_TRIM}' from compressed ${FILE_EXT} image." + info "Receiving ZFS data stream..." + gzip ${bastille_decompress_gz_options} "${bastille_backupsdir}/${TARGET}" | \ + zfs receive ${OPT_ZRECV} "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET_TRIM}" # Update ZFS mountpoint property if required update_zfsmount elif [ "${FILE_EXT}" = ".txz" ]; then + validate_archive # Prepare the ZFS environment and restore from existing .txz file create_zfs_datasets @@ -340,7 +393,20 @@ jail_import() { if [ "$?" -ne 0 ]; then remove_zfs_datasets fi + elif [ "${FILE_EXT}" = ".tgz" ]; then + validate_archive + # Prepare the ZFS environment and restore from existing .tgz file + create_zfs_datasets + + # Extract required files to the new datasets + info "Extracting files from '${TARGET}' archive..." + tar --exclude='root' -xf "${bastille_backupsdir}/${TARGET}" --strip-components 1 -C "${bastille_jailsdir}/${TARGET_TRIM}" + tar -xf "${bastille_backupsdir}/${TARGET}" --strip-components 2 -C "${bastille_jailsdir}/${TARGET_TRIM}/root" "${TARGET_TRIM}/root" + if [ "$?" -ne 0 ]; then + remove_zfs_datasets + fi elif [ "${FILE_EXT}" = ".zip" ]; then + validate_archive # Attempt to import a foreign/iocage container info "Importing '${TARGET_TRIM}' from foreign compressed ${FILE_EXT} archive." # Sane bastille ZFS options @@ -353,9 +419,9 @@ jail_import() { rm -f "${FILE_TRIM}" "${FILE_TRIM}_root" fi info "Receiving ZFS data stream..." - zfs receive -u "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET_TRIM}" < "${FILE_TRIM}" + zfs receive ${OPT_ZRECV} "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET_TRIM}" < "${FILE_TRIM}" zfs set ${ZFS_OPTIONS} "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET_TRIM}" - zfs receive -u "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET_TRIM}/root" < "${FILE_TRIM}_root" + zfs receive ${OPT_ZRECV} "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET_TRIM}/root" < "${FILE_TRIM}_root" # Update ZFS mountpoint property if required update_zfsmount @@ -403,6 +469,27 @@ jail_import() { else update_config fi + elif [ -z "${FILE_EXT}" ]; then + if echo "${TARGET}" | grep -q '_[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-[0-9]\{6\}$'; then + validate_archive + # Based on the file name, looks like we are importing a raw bastille image + # Import from uncompressed image file + info "Importing '${TARGET_TRIM}' from uncompressed image archive." + info "Receiving ZFS data stream..." + zfs receive ${OPT_ZRECV} "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET_TRIM}" < "${bastille_backupsdir}/${TARGET}" + + # Update ZFS mountpoint property if required + update_zfsmount + else + # Based on the file name, looks like we are importing from previous redirected bastille image + # Quietly import from previous redirected bastille image + if ! zfs receive ${OPT_ZRECV} "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET}"; then + exit 1 + else + # Update ZFS mountpoint property if required + update_zfsmount + fi + fi else error_exit "Unknown archive format." fi @@ -412,6 +499,9 @@ jail_import() { if [ "${FILE_EXT}" = ".txz" ]; then info "Extracting files from '${TARGET}' archive..." tar -Jxf "${bastille_backupsdir}/${TARGET}" -C "${bastille_jailsdir}" + elif [ "${FILE_EXT}" = ".tgz" ]; then + info "Extracting files from '${TARGET}' archive..." + tar -xf "${bastille_backupsdir}/${TARGET}" -C "${bastille_jailsdir}" elif [ "${FILE_EXT}" = ".tar.gz" ]; then # Attempt to import/configure foreign/ezjail container info "Extracting files from '${TARGET}' archive..." @@ -442,7 +532,9 @@ jail_import() { # This is required on foreign imports only update_jailconf update_fstab - info "Container '${TARGET_TRIM}' imported successfully." + if [ -z "${USER_IMPORT}" ]; then + info "Container '${TARGET_TRIM}' imported successfully." + fi exit 0 fi else @@ -465,22 +557,32 @@ fi # Check if archive exist then trim archive name if [ -f "${bastille_backupsdir}/${TARGET}" ]; then # Filter unsupported/unknown archives - if echo "${TARGET}" | grep -q '_[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-[0-9]\{6\}.xz$\|_[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-[0-9]\{6\}.txz$\|_[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}.zip$\|-[0-9]\{12\}.[0-9]\{2\}.tar.gz$\|@[0-9]\{12\}.[0-9]\{2\}.tar$'; then + if echo "${TARGET}" | grep -q '_[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-[0-9]\{6\}$\|_[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-[0-9]\{6\}.xz$\|_[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-[0-9]\{6\}$\|_[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-[0-9]\{6\}.gz$\|_[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-[0-9]\{6\}$\|_[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-[0-9]\{6\}.tgz$\|_[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-[0-9]\{6\}.txz$\|_[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}.zip$\|-[0-9]\{12\}.[0-9]\{2\}.tar.gz$\|@[0-9]\{12\}.[0-9]\{2\}.tar$'; then if ls "${bastille_backupsdir}" | awk "/^${TARGET}$/" >/dev/null; then - TARGET_TRIM=$(echo "${TARGET}" | sed "s/_[0-9]*-[0-9]*-[0-9]*-[0-9]*.xz//;s/_[0-9]*-[0-9]*-[0-9]*-[0-9]*.txz//;s/_[0-9]*-[0-9]*-[0-9]*.zip//;s/-[0-9]\{12\}.[0-9]\{2\}.tar.gz//;s/@[0-9]\{12\}.[0-9]\{2\}.tar//") + TARGET_TRIM=$(echo "${TARGET}" | sed "s/_[0-9]*-[0-9]*-[0-9]*-[0-9]*.xz//;s/_[0-9]*-[0-9]*-[0-9]*-[0-9]*.gz//;s/_[0-9]*-[0-9]*-[0-9]*-[0-9]*.tgz//;s/_[0-9]*-[0-9]*-[0-9]*-[0-9]*.txz//;s/_[0-9]*-[0-9]*-[0-9]*.zip//;s/-[0-9]\{12\}.[0-9]\{2\}.tar.gz//;s/@[0-9]\{12\}.[0-9]\{2\}.tar//;s/_[0-9]*-[0-9]*-[0-9]*-[0-9]*//") fi else error_exit "Unrecognized archive name." fi else - error_exit "Archive '${TARGET}' not found." + if echo "${TARGET}" | grep -q '_[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-[0-9]\{6\}.*$'; then + error_exit "Archive '${TARGET}' not found." + else + # Assume user will import from standard input + TARGET_TRIM=${TARGET} + USER_IMPORT="1" + fi fi # Check if a running jail matches name or already exist if [ -n "$(jls name | awk "/^${TARGET_TRIM}$/")" ]; then error_exit "A running jail matches name." -elif [ -d "${bastille_jailsdir}/${TARGET_TRIM}" ]; then - error_exit "Container: ${TARGET_TRIM} already exists." +elif [ -n "${TARGET_TRIM}" ]; then + if [ -d "${bastille_jailsdir}/${TARGET_TRIM}" ]; then + error_exit "Container: ${TARGET_TRIM} already exists." + fi fi -jail_import +if [ -n "${TARGET}" ]; then + jail_import +fi diff --git a/usr/local/share/bastille/stop.sh b/usr/local/share/bastille/stop.sh index c310655..60a33e0 100644 --- a/usr/local/share/bastille/stop.sh +++ b/usr/local/share/bastille/stop.sh @@ -55,9 +55,12 @@ for _jail in ${JAILS}; do pfctl -q -t jails -T delete "$(jls -j ${_jail} ip4.addr)" fi fi - - if [ "$(bastille rdr ${_jail} list)" ]; then - bastille rdr ${_jail} clear + + # Check if pfctl is present + if which -s pfctl; then + if [ "$(bastille rdr ${_jail} list)" ]; then + bastille rdr ${_jail} clear + fi fi ## remove rctl limits