May 30May 30 I added my scripts to this FR (works on 7.3):https://product.unraid.net/p/optimize-boot-backup-by-disabling-compression-or-adding-a-toggleQuite easy to add notifications if you'd wan't that. Edited May 30May 30 by Niklas
June 26Jun 26 The expanded and corrected versions for V7.3.x and the changes to ‘boot’, ‘Internal Boot’, … instead of ‘flash’. Older operating system versions should also work, but have not really been tested as there are currently no older versions, such as V6.x available.#!/bin/bash ############################################################################# # Flash or Boot Backup, License & Boot Analysis # Description: Automates Flash or Boot Backup Creation, Retention, License & Boot Analysis ############################################################################# # ============================================================================= # 1. CONFIGURATION & IDENTIFIERS # ============================================================================= # ----------------------------------------------------------------------------- # GENERAL SETTINGS # ----------------------------------------------------------------------------- HOST_NAME=$(hostname) MIN_DISKS=3 # ----------------------------------------------------------------------------- # CENTRALIZED IDENTIFIER # ----------------------------------------------------------------------------- SCRIPT_TITLE="Flash or Boot Backup, License & Boot Analysis" # File Naming Conventions LOG_BASENAME="Boot_Backup_Analysis" LOG_FILE_PATTERN="${LOG_BASENAME}_*.log" # ----------------------------------------------------------------------------- # RETENTION POLICIES # ----------------------------------------------------------------------------- BACKUP_RETENTION_DAYS=150 MAX_BACKUPS_TO_KEEP=15 LOG_RETENTION_DAYS=90 MAX_LOG_FILES_TO_KEEP=30 # ----------------------------------------------------------------------------- # FEATURE FLAGS # ----------------------------------------------------------------------------- ENABLE_LICENSE_CHECK=true ENABLE_BOOT_CHECK=true ENABLE_HARDWARE_CHECK=true ENABLE_SYSTEM_CHECK=true ENABLE_BACKUP_CREATE=true ENABLE_BACKUP_RETENTION=true ENABLE_LOG_RETENTION=true ENABLE_NOTIFICATION=true SHOW_CLI_OUTPUT=true # ----------------------------------------------------------------------------- # PATH DEFINITIONS # ----------------------------------------------------------------------------- BACKUP_BASE_DIR="/mnt/user/Backup_${HOST_NAME}/boot" ARCHIVE_SUBDIR="Backup_Reports" LOG_LOCAL_SUBDIR="Backup_Logs_on_Boot" ARCHIVE_LOG_DIR="${BACKUP_BASE_DIR}/${ARCHIVE_SUBDIR}" LOCAL_LOG_DIR="/boot/${LOG_LOCAL_SUBDIR}" LOG_FILE_NAME="${LOG_BASENAME}_${HOST_NAME}_$(date '+%Y-%m-%d_%H-%M-%S').log" LOCAL_LOG_PATH="${LOCAL_LOG_DIR}/${LOG_FILE_NAME}" ARCHIVE_LOG_PATH="${ARCHIVE_LOG_DIR}/${LOG_FILE_NAME}" LAST_RUN_LOG="${ARCHIVE_LOG_DIR}/Last_Run.log" LOG_FILE_PATH="$ARCHIVE_LOG_PATH" VAR_INI_PATH="/var/local/emhttp/var.ini" # ----------------------------------------------------------------------------- # MODULE NAMES # ----------------------------------------------------------------------------- MOD_TITLE_1="LICENSE STATUS" MOD_TITLE_2="BOOT MEDIUM ANALYSIS" MOD_TITLE_3="HARDWARE CHECK" MOD_TITLE_4="SYSTEM INTEGRITY CHECK" MOD_TITLE_5="CREATE BACKUP" MOD_TITLE_6="APPLY BACKUP RETENTION" MOD_TITLE_7="APPLY LOG RETENTION" MOD_TITLE_8="SEND NOTIFICATION" # ----------------------------------------------------------------------------- # MESSAGE TEXTS # ----------------------------------------------------------------------------- MSG_SUCCESS="SUCCESS" MSG_ERROR="ERROR" MSG_WARNING="WARNING" MSG_INFO="INFO" MSG_COPYING_BACKUP="Copying Backup to:" MSG_SUCCESS_COPY="Copy completed successfully." MSG_INTEGRITY_CHECK="Checking Integrity Check of" MSG_SUCCESS_INTEGRITY="Integrity Check passed." MSG_SYMLINK_REMOVED="Temporary Symlink removed." MSG_BACKUP_CREATED="Backup File created:" MSG_LOCATION="Location:" MSG_SUCCESS_ALL_STEPS="All Routine Steps completed successfully." MSG_BACKUP_ANALYSIS_MAINTENANCE_COMPLETED="Backup, Analysis & Maintenance completed." MSG_CONFIG_FILE_NOT_FOUND="Configuration File not found." MSG_VARINI_NOT_FOUND="var.ini not found." MSG_NO_TPM_DEVICE="No TPM Device available." MSG_USB_STICK_DETECTED="USB Stick detected for License." MSG_NO_USB_STICK="No USB Stick detected, but Flash License active!" MSG_TP_M_MODE="TPM Mode: No USB Stick required for License." MSG_UNZIP_NOT_FOUND="'unzip' Command not found. Integrity Check impossible." MSG_FLASH_BACKUP_FAILED="Flash Backup Tool failed." MSG_INVALID_FILENAME="Invalid Filename generated." MSG_SOURCE_FILE_MISSING="Source File does not exist." MSG_COPY_FAILED="Copying Backup File failed." MSG_BACKUP_CORRUPTED="Backup File corrupted! Removing defective File." MSG_CHANGE_DIR_FAILED="Change to Directory failed." MSG_PROCESS_ABORTED="Process aborted: Manual Cleanup required." MSG_WARNING_IGNORED="Warning ignored." MSG_NO_STALE_FILES="No stale temporary Files found." MSG_STALE_FILES_FOUND="stale temporary Files found." MSG_ARRAY_INTEGRITY_CONFIRMED="Array Integrity confirmed." MSG_NO_LICENSE_DETECTED="License Type not detected or invalid." # ============================================================================= # 2. HELPER FUNCTIONS # ============================================================================= print_header() { echo "==============================================================" echo " ${SCRIPT_TITLE}" echo "==============================================================" echo "HOST: $(hostname)" echo "TIMESTAMP: $(date '+%d.%m.%Y %H:%M')" echo "----------------------------------------" } print_section() { echo "" echo "[${1}]" echo "--------------------------------------------------------------" } print_msg() { local level="$1" msg="$2" case "$level" in SUCCESS) echo "[${MSG_SUCCESS}] $msg" ;; ERROR) echo "[${MSG_ERROR}] $msg" ;; WARNING) echo "[${MSG_WARNING}] $msg" ;; INFO) echo "[${MSG_INFO}] $msg" ;; *) echo "$msg" ;; esac } log_to_file() { local msg="$1" local timestamped_msg="[$(date '+%Y-%m-%d %H:%M:%S')] [$(hostname)] $msg" echo "$timestamped_msg" >> "$LOG_FILE_PATH" } mask_guid_format() { local guid="$1" if [[ -z "$guid" ]]; then echo "---"; return; fi local result="" sep="" IFS='-' read -ra PARTS <<< "$guid" for part in "${PARTS[@]}"; do if [[ ${#part} -gt 2 ]]; then result+="${sep}${part:0:2}-****" else result+="${sep}${part}-****"; fi sep="-" done echo "$result" } clean_vendor_name() { local raw_vendor="$1" local vendor=$(echo "$raw_vendor" | awk '{print $1}') if [[ "$vendor" == *"Cruzer"* ]]; then vendor=$(echo "$vendor" | sed 's/Cruzer$//g') elif [[ "$vendor" == *"DataTraveler"* ]]; then vendor=$(echo "$vendor" | sed 's/DataTraveler$//g') elif [[ "$vendor" == *"Flash"* ]]; then vendor=$(echo "$vendor" | sed 's/Flash$//g'); fi echo "$vendor" | xargs } clean_model_name() { local raw_model="$1" local cleaned=$(echo "$raw_model" | sed -E 's/[[:space:]]*[0-9]+\.?[0-9]*(GB|TB|MB|GiB|MiB)[[:space:]]*$//gi') if [[ -z "$cleaned" ]]; then echo "$raw_model"; else echo "$cleaned"; fi } # ============================================================================= # 3. INITIALIZATION # ============================================================================= mkdir -p "$BACKUP_BASE_DIR" || { echo "[${MSG_ERROR}] Cannot create Backup Directory."; exit 1; } mkdir -p "${ARCHIVE_LOG_DIR}" || { echo "[${MSG_ERROR}] Cannot create Archive Log Directory."; exit 1; } mkdir -p "${LOCAL_LOG_DIR}" || { echo "[${MSG_ERROR}] Cannot create Local Log Directory."; exit 1; } touch "$LOCAL_LOG_PATH" 2>/dev/null || exit 1 touch "$ARCHIVE_LOG_PATH" 2>/dev/null || exit 1 { echo "==========================================" echo "START ${SCRIPT_TITLE}" echo "Hostname: ${HOST_NAME}" echo "Backup Target: ${BACKUP_BASE_DIR}" echo "Local Log: ${LOCAL_LOG_PATH}" echo "Archive Log: ${ARCHIVE_LOG_PATH}" echo "==========================================" } > "$LOG_FILE_PATH" if [ "$SHOW_CLI_OUTPUT" = true ]; then print_header fi # ============================================================================= # 4. MODULES # ============================================================================= # --- MODULE 1: LICENSE STATUS --- if [ "$ENABLE_LICENSE_CHECK" = true ]; then log_to_file "---[MODULE 1: ${MOD_TITLE_1}]---" if [ "$SHOW_CLI_OUTPUT" = true ]; then print_section "${MOD_TITLE_1}"; fi SERVER_NAME="" OS_VERSION="" LICENSE_TYPE="" REGTY_RAW="" FLASH_GUID="" TPM_GUID="" REG_GUID="" if [ -f "$VAR_INI_PATH" ]; then SERVER_NAME=$(awk -F'=' '/^NAME=/ {gsub(/"/, "", $2); gsub(/ /, "_", $2); print $2}' "$VAR_INI_PATH") OS_VERSION=$(awk -F'=' '/^version=/ {gsub(/"/, "", $2); print $2}' "$VAR_INI_PATH") REGTY_RAW=$(awk -F'=' '/^regTy=/ {gsub(/"/, "", $2); print $2}' "$VAR_INI_PATH" 2>/dev/null) LICENSE_TYPE="$REGTY_RAW" FLASH_GUID=$(grep '^flashGUID=' "$VAR_INI_PATH" 2>/dev/null | cut -d'"' -f2 | tr -d '[:space:]') TPM_GUID=$(grep '^tpmGUID=' "$VAR_INI_PATH" 2>/dev/null | cut -d'"' -f2 | tr -d '[:space:]') REG_GUID=$(grep '^regGUID=' "$VAR_INI_PATH" 2>/dev/null | cut -d'"' -f2 | tr -d '[:space:]') else print_msg "$MSG_ERROR" "$MSG_CONFIG_FILE_NOT_FOUND" log_to_file "[${MSG_WARNING}] $MSG_VARINI_NOT_FOUND" fi LICENSE_STATUS="UNKNOWN" DEVICE_BINDING="NONE" if [[ "$FLASH_GUID" == "$REG_GUID" ]] && [[ "$FLASH_GUID" != "$TPM_GUID" || -z "$TPM_GUID" ]]; then LICENSE_STATUS="USB_FLASH" DEVICE_BINDING="USB Stick" if [[ -n "$TPM_GUID" ]] && [[ "$FLASH_GUID" != "$TPM_GUID" ]]; then if [ "$SHOW_CLI_OUTPUT" = true ]; then print_msg "$MSG_INFO" "TPM Key present, currently not used."; fi log_to_file "[${MSG_INFO}] TPM Key present, currently not used." fi elif [[ "$FLASH_GUID" == "$TPM_GUID" ]] && [[ "$FLASH_GUID" == "$REG_GUID" ]]; then LICENSE_STATUS="FULL_TPM" DEVICE_BINDING="TPM" if [ "$SHOW_CLI_OUTPUT" = true ]; then print_msg "$MSG_SUCCESS" "System running on full TPM Basis."; fi log_to_file "Binding detected: TPM." elif [[ "$REG_GUID" == "$TPM_GUID" ]]; then LICENSE_STATUS="TPM_MIGRATION" DEVICE_BINDING="TPM" log_to_file "Binding detected: Migration to TPM." else LICENSE_STATUS="INVALID_CONFIG" DEVICE_BINDING="UNKNOWN" log_to_file "[${MSG_WARNING}] GUID Configuration could not be uniquely identified." fi FLASH_MASKED=$(mask_guid_format "$FLASH_GUID") TPM_MASKED=$(mask_guid_format "$TPM_GUID") REG_MASKED=$(mask_guid_format "$REG_GUID") if [ "$SHOW_CLI_OUTPUT" = true ]; then echo "USB LICENSE ID: $FLASH_MASKED" echo "TPM KEY: $TPM_MASKED" echo "ACTIVE REGISTRATION: $REG_MASKED" print_section "${MOD_TITLE_1}" echo "SERVER NAME: ${SERVER_NAME:-Unavailable}" echo "OS VERSION: ${OS_VERSION:-Unavailable}" echo "LICENSE TYPE: ${LICENSE_TYPE:-$MSG_NO_LICENSE_DETECTED}" echo "DETECTED BINDING: ${DEVICE_BINDING} (${LICENSE_STATUS})" echo "----------------------------------------" echo "DETECTED MODE: $LICENSE_STATUS" echo "LICENSE DEVICE TYPE: ${DEVICE_BINDING}" fi log_to_file "Server Name: ${SERVER_NAME:-Unavailable}" log_to_file "OS Version: ${OS_VERSION:-Unavailable}" log_to_file "License Type: ${LICENSE_TYPE:-UNDETERMINED}" log_to_file "Detected Binding: ${DEVICE_BINDING} (${LICENSE_STATUS})" else log_to_file "---[MODULE 1: ${MOD_TITLE_1}]---" log_to_file "[DISABLED]" fi # --- MODULE 2: BOOT MEDIUM ANALYSIS --- if [ "$ENABLE_BOOT_CHECK" = true ]; then log_to_file "---[MODULE 2: ${MOD_TITLE_2}]---" BOOT_MEDIUM_TYPE="UNKNOWN" system_dev_final="" sys_boot_type="Unknown" sys_vendor="" sys_model_clean="" nvme_dev_global=$(lsblk -d -no NAME,TYPE 2>/dev/null | grep 'nvme' | awk '{print $1}' | head -1) sda_dev_global=$(lsblk -d -no NAME,TYPE 2>/dev/null | grep -E '^sd[a-z]$' | awk '{print $1}' | head -1) mmc_dev_global=$(lsblk -d -no NAME,TYPE 2>/dev/null | grep 'mmc' | awk '{print $1}' | head -1) if [ -e "/dev/disk/by-label/flash" ]; then REAL_LINK=$(readlink -f "/dev/disk/by-label/flash") DEVICE_NAME=$(basename "$REAL_LINK") if [[ "$DEVICE_NAME" =~ [0-9]+$ ]]; then PARENT_DEVICE="${DEVICE_NAME%[0-9]*}"; else PARENT_DEVICE="$DEVICE_NAME"; fi TRANSPORT=$(lsblk -no TRAN "$PARENT_DEVICE" 2>/dev/null | tr -d '[:space:]') case "$TRANSPORT" in usb) BOOT_MEDIUM_TYPE="USB Stick Boot" ;; *) SYS_PATH="/sys/block/$PARENT_DEVICE/device" if [ -L "$SYS_PATH" ]; then FULL_PATH=$(readlink -f "$SYS_PATH") if [[ "$FULL_PATH" =~ usb ]]; then BOOT_MEDIUM_TYPE="USB Stick Boot" else BOOT_MEDIUM_TYPE="Internal Boot" fi else BOOT_MEDIUM_TYPE="Internal Boot" fi ;; esac if [[ "$BOOT_MEDIUM_TYPE" == "Internal Boot" ]]; then if [[ -n "$nvme_dev_global" ]]; then system_dev_final="$nvme_dev_global" elif [[ -n "$sda_dev_global" ]]; then system_dev_final="$sda_dev_global" elif [[ -n "$mmc_dev_global" ]]; then system_dev_final="$mmc_dev_global"; fi sys_boot_type="Internal Boot" if [[ -n "$system_dev_final" ]] && [[ -b "/dev/$system_dev_final" ]]; then vendor_raw=$(lsblk -no VENDOR "/dev/$system_dev_final" 2>/dev/null | head -1 | xargs) model_raw=$(lsblk -no MODEL "/dev/$system_dev_final" 2>/dev/null | head -1 | xargs) if [[ -z "$vendor_raw" || "$vendor_raw" == "" ]]; then sys_vendor="(see Model)" else sys_vendor="$vendor_raw"; fi sys_model_clean=$(clean_model_name "$model_raw") fi fi log_to_file "Boot Medium Type: ${BOOT_MEDIUM_TYPE}" else log_to_file "[${MSG_WARNING}] /dev/disk/by-label/flash not found." fi if [ "$SHOW_CLI_OUTPUT" = true ]; then echo "BOOT MEDIUM TYPE: ${BOOT_MEDIUM_TYPE}" echo "----------------------------------------" print_section "${MOD_TITLE_2}" lic_vendor="-" lic_model_clean="-" lic_type="" if [[ "$DEVICE_BINDING" == "USB Stick" ]]; then usb_line=$(lsusb 2>/dev/null | grep -iE 'Kingston|Sandisk|Lexar|Transcend|ADATA|SanDisk' | head -1) if [[ -n "$usb_line" ]]; then raw_full=$(echo "$usb_line" | sed 's/.*ID [0-9a-f:]* //' | sed 's/ *Corp\. //g' | sed 's/ *Inc\. //g' | xargs) lic_vendor=$(echo "$raw_full" | awk '{print $1}') lic_vendor=$(clean_vendor_name "$lic_vendor") if [[ "$lic_vendor" == "$raw_full" ]]; then lic_model_clean="Unknown USB Device" else mod_part=$(echo "$raw_full" | sed "s/^${lic_vendor}[[:space:]]*//"); lic_model_clean=$(clean_model_name "$mod_part"); fi fi [[ -z "$lic_vendor" ]] && lic_vendor="Unknown" [[ -z "$lic_model_clean" ]] && lic_model_clean="Unknown" lic_type="USB Stick" else lic_type="TPM" fi echo "LICENSE DEVICE:" echo " TYPE: $lic_type" echo " MANUFACTURER: $lic_vendor" echo " MODEL: $lic_model_clean" echo "" echo "SYSTEM DEVICE (OS):" echo " TYPE: $sys_boot_type" echo " MANUFACTURER: $sys_vendor" echo " MODEL: $sys_model_clean" fi else log_to_file "---[MODULE 2: ${MOD_TITLE_2}]---" log_to_file "[DISABLED]" fi # --- MODULE 3: HARDWARE CHECK --- if [ "$ENABLE_HARDWARE_CHECK" = true ]; then if [ "$SHOW_CLI_OUTPUT" = true ]; then print_section "${MOD_TITLE_3}"; fi log_to_file "---[MODULE 3: ${MOD_TITLE_3}]---" if [ -c /dev/tpm0 ] 2>/dev/null || [ -c /dev/tpmrm0 ] 2>/dev/null; then if [ "$SHOW_CLI_OUTPUT" = true ]; then print_msg "$MSG_SUCCESS" "TPM Hardware detected and usable."; fi log_to_file "[${MSG_SUCCESS}] TPM Hardware detected." else if [ "$SHOW_CLI_OUTPUT" = true ]; then print_msg "$MSG_WARNING" "$MSG_NO_TPM_DEVICE"; fi log_to_file "[${MSG_WARNING}] $MSG_NO_TPM_DEVICE" fi if [[ "$LICENSE_STATUS" == "USB_FLASH" ]]; then if [[ $(lsusb 2>/dev/null | grep -ciE 'Kingston|Sandisk|Lexar') -eq 0 ]]; then if [ "$SHOW_CLI_OUTPUT" = true ]; then print_msg "$MSG_ERROR" "$MSG_NO_USB_STICK"; fi log_to_file "[${MSG_ERROR}] $MSG_NO_USB_STICK" else if [ "$SHOW_CLI_OUTPUT" = true ]; then print_msg "$MSG_INFO" "$MSG_USB_STICK_DETECTED"; fi log_to_file "[${MSG_INFO}] $MSG_USB_STICK_DETECTED" fi else if [ "$SHOW_CLI_OUTPUT" = true ]; then print_msg "$MSG_INFO" "$MSG_TP_M_MODE"; fi log_to_file "[${MSG_INFO}] $MSG_TP_M_MODE" fi log_to_file "---[${MOD_TITLE_3} COMPLETED]---" log_to_file "" else log_to_file "---[MODULE 3: ${MOD_TITLE_3}]---" log_to_file "[DISABLED]" fi # --- MODULE 4: SYSTEM INTEGRITY CHECK --- if [ "$ENABLE_SYSTEM_CHECK" = true ]; then log_to_file "---[MODULE 4: ${MOD_TITLE_4}]---" STALE_COUNT=$(find /usr/local/emhttp/ -maxdepth 1 -name "*boot-backup*.zip" \( -type f -o -xtype l \) 2>/dev/null | wc -l) if [ "$STALE_COUNT" -gt 0 ]; then log_to_file "[${MSG_WARNING}] ${STALE_COUNT} $MSG_STALE_FILES_FOUND" HAS_STALE_FILES=true if [ -z "$SKIP_DIRTY_CHECK" ] || [ "$SKIP_DIRTY_CHECK" = false ]; then log_to_file "[${MSG_ERROR}] $MSG_PROCESS_ABORTED" exit 1 else log_to_file "[${MSG_INFO}] $MSG_WARNING_IGNORED (SKIP_DIRTY_CHECK=true)." fi else HAS_STALE_FILES=false log_to_file "[${MSG_SUCCESS}] $MSG_NO_STALE_FILES" fi if [ "$MIN_DISKS" -gt 0 ]; then DISK_COUNT=$(ls -d /mnt/disk* 2>/dev/null | wc -l) if [ "$DISK_COUNT" -lt "$MIN_DISKS" ]; then log_to_file "[${MSG_ERROR}] Only ${DISK_COUNT} Disks found. Minimum ${MIN_DISKS} required." exit 1 fi log_to_file "[${MSG_SUCCESS}] $MSG_ARRAY_INTEGRITY_CONFIRMED (${DISK_COUNT} Disks present)." fi log_to_file "---[${MOD_TITLE_4} COMPLETED]---" log_to_file "" else log_to_file "---[MODULE 4: ${MOD_TITLE_4}]---" log_to_file "[DISABLED]" fi # --- MODULE 5: CREATE BACKUP --- BACKUP_FILE="" if [ "$ENABLE_BACKUP_CREATE" = true ]; then log_to_file "---[MODULE 5: ${MOD_TITLE_5}]---" log_to_file "Target Directory: ${BACKUP_BASE_DIR}" log_to_file "Starting native Flash Backup Script..." if ! command -v unzip >/dev/null 2>&1; then log_to_file "[${MSG_ERROR}] $MSG_UNZIP_NOT_FOUND" exit 1 fi BACKUP_RAW_OUTPUT=$(/usr/bin/php -q /usr/local/emhttp/webGui/scripts/flash_backup 2>&1) PHP_EXIT_CODE=$? if [ $PHP_EXIT_CODE -ne 0 ]; then log_to_file "[${MSG_ERROR}] $MSG_FLASH_BACKUP_FAILED (Exit Code: ${PHP_EXIT_CODE})." log_to_file "[DEBUG] PHP Output: ${BACKUP_RAW_OUTPUT}" exit 1 fi BACKUP_FILE=$(echo "$BACKUP_RAW_OUTPUT" | tail -n 1 | xargs) if [ -z "$BACKUP_FILE" ]; then log_to_file "[${MSG_ERROR}] Invalid Filename generated (Empty output)." log_to_file "[DEBUG] PHP Output: ${BACKUP_RAW_OUTPUT}" exit 1 fi SYMLINK_PATH="/usr/local/emhttp/${BACKUP_FILE}" if [ ! -f "$SYMLINK_PATH" ] && [ ! -L "$SYMLINK_PATH" ]; then log_to_file "[${MSG_ERROR}] Source File does not exist: ${SYMLINK_PATH}" exit 1 fi SOURCE_FILE=$(readlink -f "$SYMLINK_PATH" 2>/dev/null || echo "$SYMLINK_PATH") DEST_FILE="${BACKUP_BASE_DIR}/${BACKUP_FILE}" if [ ! -f "$SOURCE_FILE" ]; then log_to_file "[${MSG_ERROR}] Resolved Source File missing: ${SOURCE_FILE}" exit 1 fi if [ "$SHOW_CLI_OUTPUT" = true ]; then echo "[${MSG_INFO}] ${MSG_COPYING_BACKUP} ${DEST_FILE}" fi cp -v "$SOURCE_FILE" "$DEST_FILE" > /dev/null 2>&1 COPY_EXIT=$? if [ $COPY_EXIT -ne 0 ]; then log_to_file "[${MSG_ERROR}] $MSG_COPY_FAILED" if [ "$SHOW_CLI_OUTPUT" = true ]; then print_msg "$MSG_ERROR" "$MSG_COPY_FAILED"; fi exit 1 fi if [ "$SHOW_CLI_OUTPUT" = true ]; then echo "[${MSG_SUCCESS}] ${MSG_SUCCESS_COPY}" fi log_to_file "Copied Backup to: ${DEST_FILE}" if [ "$SHOW_CLI_OUTPUT" = true ]; then echo "[${MSG_INFO}] ${MSG_INTEGRITY_CHECK} ${BACKUP_FILE}..." fi if ! unzip -t -q "$DEST_FILE" >/dev/null 2>&1; then log_to_file "[${MSG_ERROR}] $MSG_BACKUP_CORRUPTED" rm -f "$DEST_FILE" if [ "$SHOW_CLI_OUTPUT" = true ]; then print_msg "$MSG_ERROR" "$MSG_BACKUP_CORRUPTED"; fi exit 1 fi log_to_file "[${MSG_SUCCESS}] ${MSG_SUCCESS_INTEGRITY}" if [ "$SHOW_CLI_OUTPUT" = true ]; then echo "[${MSG_SUCCESS}] ${MSG_SUCCESS_INTEGRITY}" fi if [ -L "$SYMLINK_PATH" ]; then rm -f "$SYMLINK_PATH" log_to_file "[${MSG_SUCCESS}] ${MSG_SYMLINK_REMOVED}" if [ "$SHOW_CLI_OUTPUT" = true ]; then echo "[${MSG_SUCCESS}] ${MSG_SYMLINK_REMOVED}" fi fi log_to_file "---[BACKUP CREATION COMPLETED]---" log_to_file "" if [ "$SHOW_CLI_OUTPUT" = true ]; then print_section "${MOD_TITLE_5} COMPLETED" echo "${MSG_BACKUP_CREATED} ${BACKUP_FILE}" echo "${MSG_LOCATION} ${DEST_FILE}" fi log_to_file "---[UPDATE ARCHIVE LOG]---" { echo "=== LAST RUN: ${HOST_NAME} ===" echo "Timestamp: $(date '+%Y-%m-%d %H:%M:%S')" echo "Backup File: ${BACKUP_FILE}" echo "Backup Path: ${DEST_FILE}" echo "Archive Log Path: ${ARCHIVE_LOG_PATH}" echo "Status: SUCCESSFUL" [ -n "$LICENSE_TYPE" ] && echo "License Type: ${LICENSE_TYPE}" [ "${HAS_STALE_FILES:-false}" = true ] && echo "WARNING: Stale Files present" echo "===============================" } > "$LAST_RUN_LOG" cp "$LOCAL_LOG_PATH" "$ARCHIVE_LOG_PATH" log_to_file "Status Log saved: ${LAST_RUN_LOG}" log_to_file "Session Log archived: ${ARCHIVE_LOG_PATH}" log_to_file "---[ARCHIVE LOG UPDATED]---" if [ "$SHOW_CLI_OUTPUT" = true ]; then print_section "ARCHIVE LOG UPDATED" echo "Log saved to: ${LAST_RUN_LOG}" fi else log_to_file "---[MODULE 5: ${MOD_TITLE_5}]---" log_to_file "[DISABLED]" if [ "$SHOW_CLI_OUTPUT" = true ]; then print_section "${MOD_TITLE_5} SKIPPED" fi fi # --- MODULE 6: APPLY BACKUP RETENTION --- if [ "$ENABLE_BACKUP_RETENTION" = true ]; then log_to_file "---[MODULE 6: ${MOD_TITLE_6}]---" cd "$BACKUP_BASE_DIR" || { log_to_file "[${MSG_ERROR}] $MSG_CHANGE_DIR_FAILED"; exit 1; } DELETED_BY_AGE=$(find . -maxdepth 1 -name "*boot-backup*.zip" -type f -mtime +"${BACKUP_RETENTION_DAYS}" -print 2>/dev/null | wc -l) if [ "$DELETED_BY_AGE" -gt 0 ]; then log_to_file "[${MSG_INFO}] ${DELETED_BY_AGE} Backups older than ${BACKUP_RETENTION_DAYS} days removed." find . -maxdepth 1 -name "*boot-backup*.zip" -type f -mtime +"${BACKUP_RETENTION_DAYS}" -delete 2>/dev/null if [ "$SHOW_CLI_OUTPUT" = true ]; then echo "[${MSG_INFO}] Removed ${DELETED_BY_AGE} old Backups (older than ${BACKUP_RETENTION_DAYS} days)." fi fi if [ -n "$MAX_BACKUPS_TO_KEEP" ] && [ "$MAX_BACKUPS_TO_KEEP" -gt 0 ]; then CURRENT_COUNT=$(ls -1 *boot-backup*.zip 2>/dev/null | wc -l) if [ "$CURRENT_COUNT" -gt "$MAX_BACKUPS_TO_KEEP" ]; then TO_DELETE=$((CURRENT_COUNT - MAX_BACKUPS_TO_KEEP)) log_to_file "[${MSG_INFO}] Current Backups: ${CURRENT_COUNT} | Max: ${MAX_BACKUPS_TO_KEEP} → Deleting ${TO_DELETE} Files." ls -t *boot-backup*.zip 2>/dev/null | tail -n +$((MAX_BACKUPS_TO_KEEP + 1)) | while read -r old_backup; do rm -f "$old_backup" log_to_file "[${MSG_INFO}] Deleted: $(basename "$old_backup")" done if [ "$SHOW_CLI_OUTPUT" = true ]; then echo "[${MSG_INFO}] Deleted ${TO_DELETE} old Backups to stay within Limit of ${MAX_BACKUPS_TO_KEEP}." fi else if [ "$SHOW_CLI_OUTPUT" = true ]; then echo "[${MSG_INFO}] Backup Count (${CURRENT_COUNT}) is within Limit (${MAX_BACKUPS_TO_KEEP})." fi fi fi FINAL_COUNT=$(ls -1 *boot-backup*.zip 2>/dev/null | wc -l) log_to_file "[${MSG_INFO}] Result: ${FINAL_COUNT} Flash or Boot Backups remaining." log_to_file "---[${MOD_TITLE_6} COMPLETED]---" if [ "$SHOW_CLI_OUTPUT" = true ]; then print_section "${MOD_TITLE_6} COMPLETED" echo "Total Backups remaining: ${FINAL_COUNT}" fi else log_to_file "---[MODULE 6: ${MOD_TITLE_6}]---" log_to_file "[DISABLED]" if [ "$SHOW_CLI_OUTPUT" = true ]; then print_section "${MOD_TITLE_6} SKIPPED" fi fi # --- MODULE 7: APPLY LOG RETENTION --- if [ "$ENABLE_LOG_RETENTION" = true ]; then log_to_file "---[MODULE 7: ${MOD_TITLE_7}]---" cd "$ARCHIVE_LOG_DIR" || { log_to_file "[${MSG_ERROR}] $MSG_CHANGE_DIR_FAILED"; exit 1; } DELETED_LOGS_BY_AGE=$(find . -maxdepth 1 -name "*.log" -type f ! -name "last_run.log" -mtime +"${LOG_RETENTION_DAYS}" -print 2>/dev/null | wc -l) if [ "$DELETED_LOGS_BY_AGE" -gt 0 ]; then log_to_file "[${MSG_INFO}] ${DELETED_LOGS_BY_AGE} Log Files older than ${LOG_RETENTION_DAYS} Days removed." find . -maxdepth 1 -name "*.log" -type f ! -name "last_run.log" -mtime +"${LOG_RETENTION_DAYS}" -delete 2>/dev/null if [ "$SHOW_CLI_OUTPUT" = true ]; then echo "[${MSG_INFO}] Removed ${DELETED_LOGS_BY_AGE} old Logs (older than ${LOG_RETENTION_DAYS} Days)." fi fi if [ -n "$MAX_LOG_FILES_TO_KEEP" ] && [ "$MAX_LOG_FILES_TO_KEEP" -gt 0 ]; then LOG_FILE_COUNT=$(find . -maxdepth 1 -name "${LOG_FILE_PATTERN}" -type f 2>/dev/null | wc -l) if [ "$LOG_FILE_COUNT" -gt "$MAX_LOG_FILES_TO_KEEP" ]; then TO_DELETE_COUNT=$((LOG_FILE_COUNT - MAX_LOG_FILES_TO_KEEP)) log_to_file "[${MSG_INFO}] Current Logs: ${LOG_FILE_COUNT} | Max: ${MAX_LOG_FILES_TO_KEEP} → Deleting ${TO_DELETE_COUNT} Files." mapfile -t files_to_delete < <( for f in $(find . -maxdepth 1 -name "${LOG_FILE_PATTERN}" -type f); do ts=$(stat -c '%Y' "$f" 2>/dev/null || stat -f '%m' "$f" 2>/dev/null) printf '%s %s\n' "$ts" "$f" done | sort -n | head -n "$TO_DELETE_COUNT" | awk '{print $2}' ) for old_log in "${files_to_delete[@]}"; do if [ -n "$old_log" ]; then rm -f "$old_log" log_to_file "[${MSG_INFO}] Deleted: $(basename "$old_log")" fi done if [ "$SHOW_CLI_OUTPUT" = true ]; then echo "[${MSG_INFO}] Deleted ${TO_DELETE_COUNT} old Logs to stay within Limit of ${MAX_LOG_FILES_TO_KEEP}." fi else if [ "$SHOW_CLI_OUTPUT" = true ]; then echo "[${MSG_INFO}] Log Count (${LOG_FILE_COUNT}) is within Limit (${MAX_LOG_FILES_TO_KEEP})." fi fi fi FINAL_LOG_COUNT=$(find . -maxdepth 1 -name "${LOG_FILE_PATTERN}" -type f | wc -l) log_to_file "[${MSG_INFO}] Result: ${FINAL_LOG_COUNT} Log Files remaining (excl. last_run.log)." log_to_file "---[${MOD_TITLE_7} COMPLETED]---" if [ "$SHOW_CLI_OUTPUT" = true ]; then print_section "${MOD_TITLE_7} COMPLETED" echo "Total Logs remaining: ${FINAL_LOG_COUNT}" fi else log_to_file "---[MODULE 7: ${MOD_TITLE_7}]---" log_to_file "[DISABLED]" if [ "$SHOW_CLI_OUTPUT" = true ]; then print_section "${MOD_TITLE_7} SKIPPED" fi fi # --- MODULE 8: SEND NOTIFICATION --- if [ "$ENABLE_NOTIFICATION" = true ]; then log_to_file "---[MODULE 8: ${MOD_TITLE_8}]---" NOTIFY_TITLE="Flash or Boot Backup Completed - ${HOST_NAME}" NOTIFY_MSG="Flash or Boot Backup '${BACKUP_FILE}' successfully created." DETAIL_MSG="" if [ "$ENABLE_LICENSE_CHECK" = true ]; then case "$DEVICE_BINDING" in "USB Stick") DETAIL_MSG="License: ${LICENSE_TYPE:-Unknown} (USB)" ;; "TPM") DETAIL_MSG="License: ${LICENSE_TYPE:-Unknown} (TPM)" ;; esac fi if [ "$ENABLE_BOOT_CHECK" = true ] && [ "$BOOT_MEDIUM_TYPE" != "UNKNOWN" ]; then DETAIL_MSG="${DETAIL_MSG:+${DETAIL_MSG} | }Boot: ${BOOT_MEDIUM_TYPE}" fi if [ "${HAS_STALE_FILES:-false}" = true ]; then DETAIL_MSG="${DETAIL_MSG:+${DETAIL_MSG} | }WARNING: Stale Files" fi /usr/local/emhttp/webGui/scripts/notify -e "$NOTIFY_TITLE" \ -s "Flash or Boot Backup Success" \ -d "${NOTIFY_MSG} - ${DETAIL_MSG}" \ -i "normal" log_to_file "[${MSG_INFO}] Notification sent." log_to_file "---[${MOD_TITLE_8} COMPLETED]---" if [ "$SHOW_CLI_OUTPUT" = true ]; then print_section "${MOD_TITLE_8} SENT" echo "Notification sent successfully." fi else log_to_file "---[MODULE 8: ${MOD_TITLE_8}]---" log_to_file "[DISABLED]" if [ "$SHOW_CLI_OUTPUT" = true ]; then print_section "${MOD_TITLE_8} SKIPPED" fi fi # Final Output if [ "$SHOW_CLI_OUTPUT" = true ]; then echo "" echo "==============================================================" echo " ${MSG_BACKUP_ANALYSIS_MAINTENANCE_COMPLETED}" echo "==============================================================" fi log_to_file "[${MSG_SUCCESS}] ${MSG_SUCCESS_ALL_STEPS}" if [ "$SHOW_CLI_OUTPUT" = true ]; then print_msg "$MSG_SUCCESS" "${MSG_SUCCESS_ALL_STEPS}" fi exit 0
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.