#!/bin/bash # Version: 20251218.GA02 # FileWave IVS Upgrade Script (Debian 12/13) # # Safer recommended 1-liner (runs in screen): # sudo DEBIAN_FRONTEND=noninteractive bash -c 'apt-get update -y && apt-get install -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" screen && screen -S fw_upgrade bash -c "wget -qO- https://kb.filewave.com/attachments/408 | bash -s -- -v 16.3.0 -r 1 -p -y"' # # Options: # -v, --version Target FileWave version (e.g., 16.3.0) # -r, --revision Legacy selector (accepted for compatibility; coerced to '1') # -d, --dev Use Dev download server # -b, --beta Use Beta download server # -p, --prod Use Production download server (default) # -y, --yes Auto-yes for routine prompts (install, OS patch upgrade, reboot) # -yy, --yesyes Auto-yes for ALL prompts (includes screen warning + Debian 12->13 release upgrade); # NOTE: if root has no password set, -yy will exit with an error (no prompting). # # Notes: # - Supports Debian 12 and Debian 13 (enforced via version matrix below). # - Debian 13 is required for FileWave versions >= 16.3.0. set -u # Ensure noninteractive package operations by default (we still prompt via /dev/tty where needed) export DEBIAN_FRONTEND=${DEBIAN_FRONTEND:-noninteractive} ############################################################################### # SUPPORT CASE CHECKLIST # # If this upgrade fails and you open a FileWave support case, please include: # # 1. The full output of this script (copy/paste or screenshot) # 2. The log file: # /var/log/filewave_ivs_update.log # 3. The script name and GA version (shown at the top of the log) # 4. The FileWave target version you attempted to install # 5. The OS version shown in the startup banner # ############################################################################### export DEBCONF_NONINTERACTIVE_SEEN=${DEBCONF_NONINTERACTIVE_SEEN:-true} #################### VARIABLES / LOGS ################## # Set default values for target FileWave version, legacy revision flag, and download server type # NOTE: FileWave IVS currently ships only revision 1 builds. The REVISION variable is kept # for backwards compatibility with existing 1-liners, but any value other than '1' will be # ignored and coerced back to '1' at runtime. VERSION="16.3.0" REVISION="1" SERVER_TYPE="prod" BASE_URL="https://fwdl.filewave.com" auto_yes=false auto_risky_yes=false OS_UPGRADED=false # Temp download directory tracked globally for safe cleanup DOWNLOAD_DIR="" cleanup_download_dir() { if [[ -n "${DOWNLOAD_DIR:-}" && -d "${DOWNLOAD_DIR:-}" ]]; then rm -rf "${DOWNLOAD_DIR}" fi } trap cleanup_download_dir EXIT # Function to handle errors and display messages ############################################# ##### BEGIN SHARED PLATFORM BLOCK (KEEP IN SYNC) ##### # This block is intentionally identical across: # - FileWave IVS Upgrade Script # - FileWave Server (fwxserver) Upgrade Script # - FileWave Booster (fwbooster) Upgrade Script # Any changes to Debian upgrade, screen safety, /boot safety, # root-password enforcement, and download validation should be # made in ALL THREE scripts. ############################################# die() { echo "[ERROR] $1" >&2 exit 1 } require_amd64() { local arch arch=$(dpkg --print-architecture 2>/dev/null || echo \"unknown\") if [[ \"$arch\" != \"amd64\" ]]; then die \"[ARCH] Unsupported architecture: $arch. This script supports amd64 only.\" fi } is_running_under_multiplexer() { # Detect GNU screen via env or process ancestry (helps when sudo strips env). if [[ -n "${STY:-}" ]]; then return 0 fi local pid=$$ local depth=0 while [[ "$pid" -gt 1 && $depth -lt 6 ]]; do local comm comm=$(ps -o comm= -p "$pid" 2>/dev/null | tr -d ' ') if [[ "$comm" == "screen" || "$comm" == "SCREEN" ]]; then return 0 fi pid=$(ps -o ppid= -p "$pid" 2>/dev/null | tr -d ' ') [[ -z "$pid" ]] && break depth=$((depth + 1)) done return 1 } check_screen_session_safety() { # Enforce screen usage for long-running operations. # This is intentionally NOT based on SSH_* env vars because sudo can strip them. if ! command -v screen >/dev/null 2>&1; then echo "[SESSION] Installing 'screen' (recommended)..." DEBIAN_FRONTEND=noninteractive apt-get install -y -q screen || \ die "[SESSION] Failed to install screen." fi if is_running_under_multiplexer; then return 0 fi echo "[SESSION] You are NOT running inside a 'screen' session." echo "[SESSION] This process can take a long time and an SSH disconnect can leave the system in a bad state." echo echo "[SESSION] Recommended:" echo "[SESSION] screen -S fw_upgrade" echo "[SESSION] " echo echo "[SESSION] If you are already in screen, reconnect with:" echo "[SESSION] screen -r fw_upgrade" echo if [[ "${auto_risky_yes:-false}" == "true" ]]; then echo "[SESSION] Risky auto-yes mode enabled; continuing despite not running inside 'screen'." return 0 fi local resp read -p "Continue without screen? (yes/no): " resp < /dev/tty if [[ ! "$resp" =~ ^[Yy]([Ee][Ss])?$ ]]; then die "[SESSION] Aborting. Please start a 'screen' session and re-run." fi } check_root_password_state() { # passwd -S output format: # root P ... -> password set # root L ... -> locked # root NP ... -> no password passwd -S root 2>/dev/null | awk '{print $2}' } ensure_root_password_set() { # Some package install steps invoke 'su' or call tools that enforce root auth policies. # For appliances where root is disabled by default, we require the operator to set it. while true; do local state state="$(check_root_password_state || true)" if [[ "$state" == "P" ]]; then echo "[ROOT] Root password is set." return 0 fi # If running in fully non-interactive "yesyes" mode, do not try to prompt. if [[ "${auto_risky_yes:-false}" == "true" ]]; then echo echo "[ROOT] ERROR: Root password is NOT set (state: ${state:-unknown})." echo "[ROOT] This run is in -yy/--yesyes mode and cannot prompt for passwords." echo "[ROOT] Set a root password (and write it down), then re-run WITHOUT -yy:" echo "[ROOT] sudo passwd root" echo exit 1 fi echo echo "[ROOT] Root password is NOT set (state: ${state:-unknown})." echo echo "This upgrade requires a valid root password because parts of the installation" echo "process invoke 'su'. You MUST set a root password now and write it down." echo echo "Running: passwd root" echo if passwd root < /dev/tty > /dev/tty 2>&1; then echo echo "[ROOT] Password command completed. Re-checking root password state..." else echo echo "[ROOT] passwd returned an error. Please try again." fi done } boot_usage_percent() { df -P /boot 2>/dev/null | awk 'NR==2 {gsub("%","",$5); print $5}' } has_separate_boot() { mountpoint -q /boot 2>/dev/null } boot_fs_use_pct() { df -P /boot 2>/dev/null | awk 'NR==2 {print $5}' || echo "n/a" } root_fs_use_pct() { df -P / 2>/dev/null | awk 'NR==2 {print $5}' || echo "n/a" } boot_dir_size() { du -sh /boot 2>/dev/null | awk 'NR==1 {print $1}' || echo "n/a" } check_boot_space() { if mountpoint -q /boot; then local usep usep="$(boot_usage_percent)" if [[ -n "$usep" ]]; then echo "[BOOT] /boot usage: ${usep}%" if (( usep >= 95 )); then die "[BOOT] /boot is critically full (${usep}%). Run kernel cleanup before continuing." elif (( usep >= 80 )); then echo "[BOOT] Warning: /boot is getting full (${usep}%). Kernel cleanup is recommended." fi fi fi } kernel_cleanup_keep2() { # Purge old Debian kernel image packages, keeping: # - currently running kernel # - one newest additional kernel (fallback) if ! mountpoint -q /boot; then echo "[KERNEL-CLEANUP] /boot is not a separate mountpoint; skipping kernel cleanup." return 0 fi echo "[KERNEL-CLEANUP] Checking for excess installed Debian kernels to prevent /boot filling..." local running_ver running_pkg running_ver="$(uname -r)" running_pkg="linux-image-${running_ver}" mapfile -t images < <(dpkg-query -W -f='${Package}\n' 'linux-image-[0-9]*-amd64' 2>/dev/null | sort -V || true) if ((${#images[@]} <= 2)); then echo "[KERNEL-CLEANUP] ${#images[@]} kernel image package(s) installed; nothing to clean." df -h /boot || true return 0 fi local keep=("$running_pkg") local i for (( i=${#images[@]}-1; i>=0; i-- )); do [[ "${images[$i]}" == "$running_pkg" ]] && continue keep+=("${images[$i]}") break done local remove=() local pkg k skip for pkg in "${images[@]}"; do skip=false for k in "${keep[@]}"; do [[ "$pkg" == "$k" ]] && skip=true && break done $skip || remove+=("$pkg") done echo "[KERNEL-CLEANUP] Running kernel : $running_ver" echo "[KERNEL-CLEANUP] Keeping : ${keep[*]}" echo "[KERNEL-CLEANUP] Removing : ${remove[*]:-(none)}" if ((${#remove[@]})); then DEBIAN_FRONTEND=noninteractive apt-get -y purge "${remove[@]}" || \ echo "[KERNEL-CLEANUP] Warning: kernel purge encountered issues; check logs." DEBIAN_FRONTEND=noninteractive apt-get -y autoremove --purge || true update-grub >/dev/null 2>&1 || true fi df -h /boot || true } ensure_boot_space() { local phase="${1:-unspecified}" echo "[BOOT] Preflight check before: ${phase}" # Proactively free space in /boot by purging old kernels (keeping running + one fallback). # If /boot is still critically full after cleanup, abort before attempting any upgrades that # may generate new initramfs images (which writes to /boot and can brick the upgrade). if ! mountpoint -q /boot; then return 0 fi local usep usep="$(boot_usage_percent)" # If /boot is getting full, attempt cleanup first. if [[ -n "$usep" ]] && (( usep >= 80 )); then echo "[BOOT] /boot usage is high (${usep}%). Attempting kernel cleanup..." kernel_cleanup_keep2 usep="$(boot_usage_percent)" fi # Hard stop if still critical after cleanup. if [[ -n "$usep" ]] && (( usep >= 95 )); then die "[BOOT] /boot is critically full (${usep}%) even after kernel cleanup. Manual remediation required before continuing." fi # If still high (but not critical), warn and continue. if [[ -n "$usep" ]] && (( usep >= 80 )); then echo "[BOOT] Warning: /boot is still high after cleanup (${usep}%). Upgrade may fail if additional kernels/initramfs are generated." fi } validate_download_exists() { # Validate a URL is reachable before download (clearer error than wget retries). local url="$1" echo "[DOWNLOAD] Validating download exists: $url" if command -v wget >/dev/null 2>&1; then if wget -q --spider "$url"; then return 0 fi elif command -v curl >/dev/null 2>&1; then if curl -fsSI "$url" >/dev/null; then return 0 fi else # Fall back: no validation tool available; caller will attempt download. echo "[DOWNLOAD] Warning: neither wget nor curl is available for existence check; skipping validation." return 0 fi return 1 } upgrade_to_debian13() { # Debian 12 -> 13 in-place upgrade. Requires screen session safety checks. check_screen_session_safety echo "[DEBIAN-UPGRADE] Debian 12 detected but target FileWave version requires Debian 13 (trixie)." echo "[DEBIAN-UPGRADE] This is a long-running in-place OS upgrade." local deb13_confirm if [[ "${auto_risky_yes:-false}" == "true" ]]; then deb13_confirm="yes" echo "[DEBIAN-UPGRADE] Risky auto-yes mode enabled; proceeding without interactive confirmation." else read -p "Proceed with Debian 12 -> 13 OS upgrade now? (yes/no): " deb13_confirm < /dev/tty fi if [[ ! "$deb13_confirm" =~ ^[Yy]([Ee][Ss])?$ ]]; then die "[DEBIAN-UPGRADE] Aborting. Debian 13 is required for this target version." fi local REQUIRED_GIB=5 local avail_gib avail_gib=$(df -BG / | awk 'NR==2 {gsub(/G/,"",$4); print $4}') [[ -z "$avail_gib" ]] && die "[DEBIAN-UPGRADE] Unable to determine free disk space on /." (( avail_gib < REQUIRED_GIB )) && die "[DEBIAN-UPGRADE] Not enough free disk space: ${avail_gib} GiB available, need ${REQUIRED_GIB} GiB." echo "[DEBIAN-UPGRADE] Disk space OK: ${avail_gib} GiB free." # Remove legacy FileWave APT lists to avoid stale suites rm -f /etc/apt/sources.list.d/filewave-beta.list >/dev/null 2>&1 || true rm -f /etc/apt/sources.list.d/filewave-dev.list >/dev/null 2>&1 || true rm -f /etc/apt/sources.list.d/filewave-release.list >/dev/null 2>&1 || true ensure_boot_space "Debian 12 pre-upgrade kernel space check" echo "[DEBIAN-UPGRADE] Updating Debian 12 (bookworm) fully before release upgrade..." apt-get update -y || die "[DEBIAN-UPGRADE] apt-get update failed on Debian 12." DEBIAN_FRONTEND=noninteractive apt-get dist-upgrade -y \ -o Dpkg::Options::="--force-confdef" \ -o Dpkg::Options::="--force-confold" \ --autoremove || die "[DEBIAN-UPGRADE] dist-upgrade failed on Debian 12." echo "[DEBIAN-UPGRADE] Switching APT sources from bookworm -> trixie..." sed -i 's/bookworm/trixie/g' /etc/apt/sources.list || die "[DEBIAN-UPGRADE] Failed to update /etc/apt/sources.list." find /etc/apt/sources.list.d -maxdepth 1 -type f -exec sed -i 's/bookworm/trixie/g' {} \; 2>/dev/null || true # Preseed grub-pc device selection (noninteractive safety) if dpkg-query -W -f='${Status}' grub-pc 2>/dev/null | grep -q "install ok installed"; then local ROOT_OR_BOOT_DEV BOOT_DISK PARENT_DISK ROOT_OR_BOOT_DEV=$(findmnt -no SOURCE /boot 2>/dev/null || findmnt -no SOURCE / 2>/dev/null || echo "") BOOT_DISK="" if [[ -n "$ROOT_OR_BOOT_DEV" ]]; then PARENT_DISK=$(lsblk -no PKNAME "$ROOT_OR_BOOT_DEV" 2>/dev/null || echo "") if [[ -n "$PARENT_DISK" ]]; then BOOT_DISK="$PARENT_DISK" else BOOT_DISK=$(basename "$ROOT_OR_BOOT_DEV") fi fi if [[ -n "$BOOT_DISK" ]]; then echo "[DEBIAN-UPGRADE] Preseeding grub-pc install device as /dev/$BOOT_DISK..." echo "grub-pc grub-pc/install_devices multiselect /dev/$BOOT_DISK" | debconf-set-selections echo "grub-pc grub-pc/install_devices_empty boolean false" | debconf-set-selections else echo "[DEBIAN-UPGRADE] Warning: Could not determine boot disk for grub preseeding." fi fi ensure_boot_space "Debian sources switched pre-upgrade kernel space check" echo "[DEBIAN-UPGRADE] Running Debian 13 dist-upgrade (with retry)..." local max_attempts=2 local attempt=1 while (( attempt <= max_attempts )); do echo "[DEBIAN-UPGRADE] Attempt $attempt of $max_attempts..." apt-get clean rm -rf /var/lib/apt/lists/* apt-get update -y || die "[DEBIAN-UPGRADE] apt-get update failed after switching to trixie." if DEBIAN_FRONTEND=noninteractive apt-get dist-upgrade -y \ -o Dpkg::Options::="--force-confdef" \ -o Dpkg::Options::="--force-confold" \ --autoremove; then echo "[DEBIAN-UPGRADE] Release upgrade succeeded." break fi if (( attempt == max_attempts )); then die "[DEBIAN-UPGRADE] Release upgrade failed after retries. See log for remediation steps." fi echo "[DEBIAN-UPGRADE] dist-upgrade failed; retrying after cache cleanup..." attempt=$((attempt + 1)) sleep 5 done # Optional: modernize sources on Debian 13 if command -v apt >/dev/null 2>&1; then if apt modernize-sources --help >/dev/null 2>&1; then echo "[DEBIAN-UPGRADE] Modernizing APT sources to deb822..." apt modernize-sources --assume-yes || echo "[DEBIAN-UPGRADE] Warning: apt modernize-sources failed." fi fi # Refresh version info and confirm Debian 13 DEBIAN_VERSION=$(grep -E '^VERSION_ID=' /etc/os-release | cut -d= -f2 | tr -d '"') DEBIAN_MAJOR=${DEBIAN_VERSION%%.*} (( DEBIAN_MAJOR != 13 )) && die "[DEBIAN-UPGRADE] Debian upgrade appears incomplete (VERSION_ID=$DEBIAN_VERSION)." echo "[DEBIAN-UPGRADE] Debian 13 confirmed. Continuing..." OS_UPGRADED=true } ############################################# # SHARED PLATFORM BLOCK END ############################################# # sudo/root privileges ##### END SHARED PLATFORM BLOCK ##### if [ "$EUID" -ne 0 ]; then die "This script must be run with sudo or as root." fi # Log all actions to a dedicated log file for audit and troubleshooting LOG_FILE="/var/log/filewave_ivs_update.log" touch "$LOG_FILE" || die "Failed to create log file $LOG_FILE. Check permissions." chmod 644 "$LOG_FILE" || die "Failed to set permissions on log file $LOG_FILE." if [[ ! -w "$LOG_FILE" ]]; then die "Cannot write to log file $LOG_FILE. Please check permissions." fi exec > >(stdbuf -oL tee -a "$LOG_FILE") 2>&1 #################### FAILURE HANDLING (screen-friendly) #################### # When this script runs inside a short-lived `screen` session started by the 1-liner, # a hard failure can close the screen window immediately and make the last error hard to read. # To improve operator UX: # - In normal interactive runs (including -y), pause on failure and prompt "Press Enter to exit". # - In unattended/risky mode (-yy/--yesyes), do NOT pause; exit immediately. pause_on_failure() { local rc="${1:-1}" # In risky auto-yes mode, never pause (assume unattended). if [[ "${auto_risky_yes:-false}" == "true" ]]; then return 0 fi # Best-effort: only pause when we appear to have a TTY. if [[ -e /dev/tty ]]; then echo echo "================================================================================" echo "[FAILURE] Upgrade failed with exit code: ${rc}" echo "[FAILURE] Log File: ${LOG_FILE:-/var/log/filewave_update.log}" echo "[FAILURE] The screen session will close when this script exits." echo "[FAILURE] Press Enter to exit (or Ctrl+C)." echo "================================================================================" # Read directly from the controlling terminal so piping doesn't break the prompt. read -r /dev/null || echo "ivs_upgrade.sh")" echo "Version : GA01" echo "Started : $(date -u '+%Y-%m-%d %H:%M:%S UTC')" echo "Hostname : $(hostname -f 2>/dev/null || hostname)" # OS information (best effort) if command -v lsb_release >/dev/null 2>&1; then echo "OS : $(lsb_release -ds 2>/dev/null)" else echo "OS : $(grep -E '^PRETTY_NAME=' /etc/os-release | cut -d= -f2- | tr -d '"')" fi echo "================================================================================" # Pre-flight environment snapshot for support diagnostics HYPERVISOR=$(systemd-detect-virt 2>/dev/null || echo "unknown") CPU_MODEL=$(grep -m1 "model name" /proc/cpuinfo 2>/dev/null | cut -d: -f2- | xargs || echo "unknown") TOTAL_MEM=$(awk '/MemTotal/ {printf "%.1f GB", $2/1024/1024}' /proc/meminfo 2>/dev/null) ROOT_FS_AVAIL=$(df -h / 2>/dev/null | awk 'NR==2 {print $4}') if has_separate_boot; then BOOT_FS_USE="$(boot_fs_use_pct)" echo "[PRE-FLIGHT] /boot Usage: ${BOOT_FS_USE:-unknown}" else echo "[PRE-FLIGHT] /boot Usage: N/A (no separate /boot filesystem; /boot is on /)" echo "[PRE-FLIGHT] Root FS Usage: $(root_fs_use_pct)" echo "[PRE-FLIGHT] /boot Directory Size: $(boot_dir_size)" fi echo "[PRE-FLIGHT] /boot Usage: ${BOOT_FS_USE:-unknown}" echo "[PRE-FLIGHT] Hypervisor: $HYPERVISOR" echo "[PRE-FLIGHT] CPU Model: ${CPU_MODEL:-unknown}" echo "[PRE-FLIGHT] Memory: ${TOTAL_MEM:-unknown}" echo "[PRE-FLIGHT] Root FS Free Space: ${ROOT_FS_AVAIL:-unknown}" ################# OS Check ############### # Validate Debian version (must be 12 or 13) if ! grep -iq "^ID=debian" /etc/os-release; then die "Unsupported operating system. This script must be run on Debian 12 or Debian 13." fi require_amd64 # Extract major version number (e.g., 12 from 12.5) DEBIAN_VERSION=$(grep -E '^VERSION_ID=' /etc/os-release | cut -d= -f2 | tr -d '"') DEBIAN_MAJOR=${DEBIAN_VERSION%%.*} if ! [[ "$DEBIAN_MAJOR" =~ ^[0-9]+$ ]]; then die "Unable to determine Debian version. Aborting." fi if (( DEBIAN_MAJOR < 12 || DEBIAN_MAJOR > 13 )); then die "Unsupported Debian version ($DEBIAN_VERSION). Only Debian 12 and Debian 13 are supported." fi require_amd64 is_running_under_multiplexer() { # Detect whether this shell is running under a GNU screen session. # First, trust STY if present. if [[ -n "$STY" ]]; then return 0 fi # Fallback: walk up the process tree looking for a 'screen' ancestor. # This helps when sudo or other wrappers strip environment variables. local pid=$$ local depth=0 while [[ "$pid" -gt 1 && $depth -lt 6 ]]; do local comm comm=$(ps -o comm= -p "$pid" 2>/dev/null | tr -d ' ') if [[ "$comm" == "screen" || "$comm" == "SCREEN" ]]; then return 0 fi pid=$(ps -o ppid= -p "$pid" 2>/dev/null | tr -d ' ') [[ -z "$pid" ]] && break depth=$((depth + 1)) done return 1 } check_screen_session_safety() { # Safety check: encourage running under GNU screen for long-running upgrades, # especially when a major OS upgrade (Debian 12 → 13) is about to be performed. # Ensure screen is installed before any messaging if ! command -v screen >/dev/null 2>&1; then echo "[SESSION] 'screen' is not installed; installing it now to support detachable upgrade sessions..." DEBIAN_FRONTEND=noninteractive apt-get install -y -q screen || \ echo "[SESSION] Warning: Failed to install 'screen'. Continuing without screen support." fi # --- CASE 1: User IS running inside screen --- if is_running_under_multiplexer; then echo "[SESSION] Running inside a 'screen' session; this is recommended for long-running upgrades." return 0 fi # --- CASE 2: User is NOT running inside screen --- echo "[SESSION] This upgrade is not running inside 'screen'." echo "[SESSION] Because we are about to perform a major OS upgrade (Debian 12 → 13)," echo "[SESSION] it is strongly recommended to run this process inside a 'screen' session." echo "[SESSION] This protects you from SSH disconnects during the long upgrade, which" echo "[SESSION] could otherwise leave the system in a partially upgraded or unstable state." echo "" if command -v screen >/dev/null 2>&1; then echo "[SESSION] To create a protected session, run the following command in your terminal:" echo "[SESSION] screen -S fw_upgrade" echo "[SESSION] After the screen session opens, paste the FileWave IVS upgrade 1-liner" echo "[SESSION] again so the entire upgrade runs inside that screen session." echo "" fi if [[ "$auto_risky_yes" == "true" ]]; then echo "[SESSION] Risky auto-yes mode enabled; continuing despite not running inside 'screen'." return 0 fi read -p "You are NOT running inside 'screen'. Do you still want to continue with this upgrade? (yes/no): " ssh_continue < /dev/tty if [[ ! "$ssh_continue" =~ ^[Yy]([Ee][Ss])?$ ]]; then die "[SESSION] Aborting at user request because the upgrade is not running inside 'screen'. Please start a 'screen' session and re-run the upgrade." fi } ################ Debian 13 Upgrade Helper ################ # Functions and constants to manage Debian 12 -> 13 release upgrades when required by FileWave version # Minimum FileWave version that requires Debian 13 MIN_DEBIAN13_FW_VERSION="16.3.0" # Placeholder for Debian 12 -> Debian 13 in-place upgrade upgrade_to_debian13() { check_screen_session_safety echo "[DEBIAN-UPGRADE] Detected Debian 12 but target FileWave version $VERSION requires Debian 13." echo "[DEBIAN-UPGRADE] Newer FileWave versions ($MIN_DEBIAN13_FW_VERSION and above) are only supported on Debian 13 (trixie)." echo "[DEBIAN-UPGRADE] This is a long-running, in-place OS upgrade and may take considerable time." if is_running_under_multiplexer; then echo "[DEBIAN-UPGRADE] You are already running this upgrade under a 'screen' session." if command -v screen >/dev/null 2>&1; then echo "[DEBIAN-UPGRADE] Current screen sessions:" screen -list || true fi echo "[DEBIAN-UPGRADE] If you disconnect, you can typically reconnect with:" echo "[DEBIAN-UPGRADE] screen -ls" echo "[DEBIAN-UPGRADE] screen -r " else echo "[DEBIAN-UPGRADE] It is strongly recommended that you run this under 'screen' to protect against disconnects." echo "[DEBIAN-UPGRADE] If you disconnect from an SSH session, you can later run: screen -ls" echo "[DEBIAN-UPGRADE] This will show something like: 'There is a screen on: 1234.pts-0.servername'" echo "[DEBIAN-UPGRADE] You can reconnect with: screen -r 1234" fi # Confirm with the admin before performing the Debian 12 -> 13 upgrade if [[ "$auto_risky_yes" == "true" ]]; then deb13_confirm="yes" echo "[DEBIAN-UPGRADE] Risky auto-yes mode enabled; proceeding with Debian 12 -> 13 upgrade without interactive confirmation." else read -p "Do you wish to proceed with the Debian 12 -> 13 OS upgrade now? (yes/no): " deb13_confirm < /dev/tty fi if [[ ! "$deb13_confirm" =~ ^[Yy]([Ee][Ss])?$ ]]; then die "[DEBIAN-UPGRADE] Aborting Debian 12 -> 13 upgrade at user request. FileWave version $VERSION requires Debian 13. Please re-run and allow the OS upgrade, or choose an older FileWave version supported on Debian 12." fi # Ensure screen is available (do not forcibly attach to a screen session here) if ! command -v screen >/dev/null 2>&1; then echo "[DEBIAN-UPGRADE] Installing 'screen' to support detachable sessions..." DEBIAN_FRONTEND=noninteractive apt-get install -y -q screen || echo "[DEBIAN-UPGRADE] Warning: Failed to install 'screen'. Continuing without screen." fi # Check for at least 5 GiB free on the root filesystem REQUIRED_GIB=5 avail_gib=$(df -BG / | awk 'NR==2 {gsub(/G/,"",$4); print $4}') if [[ -z "$avail_gib" ]]; then die "[DEBIAN-UPGRADE] Unable to determine free disk space on root filesystem. Aborting Debian upgrade." fi if (( avail_gib < REQUIRED_GIB )); then die "[DEBIAN-UPGRADE] Not enough free disk space for Debian 12 -> 13 upgrade. ${avail_gib} GiB available, but at least ${REQUIRED_GIB} GiB is required." fi echo "[DEBIAN-UPGRADE] Disk space check passed: ${avail_gib} GiB available. Proceeding with Debian upgrade..." # Clean up legacy FileWave APT lists to avoid stale suites rm -f /etc/apt/sources.list.d/filewave-beta.list > /dev/null 2>&1 || true rm -f /etc/apt/sources.list.d/filewave-dev.list > /dev/null 2>&1 || true rm -f /etc/apt/sources.list.d/filewave-release.list > /dev/null 2>&1 || true # Ensure current Debian 12 system is fully upgraded before changing suites ensure_boot_space "pre-install kernel space check" echo "[DEBIAN-UPGRADE] Bringing Debian 12 (bookworm) fully up to date before release upgrade..." apt-get update -y || die "[DEBIAN-UPGRADE] Failed to update package list on Debian 12." DEBIAN_FRONTEND=noninteractive apt-get dist-upgrade -y \ -o Dpkg::Options::="--force-confdef" \ -o Dpkg::Options::="--force-confold" \ --autoremove || die "[DEBIAN-UPGRADE] Failed to fully upgrade Debian 12 prior to release upgrade." # Switch APT sources from bookworm to trixie echo "[DEBIAN-UPGRADE] Updating APT sources from bookworm to trixie..." sed -i 's/bookworm/trixie/g' /etc/apt/sources.list || die "[DEBIAN-UPGRADE] Failed to update /etc/apt/sources.list to trixie." find /etc/apt/sources.list.d -maxdepth 1 -type f -exec sed -i 's/bookworm/trixie/g' {} \; 2>/dev/null || true # Preseed grub-pc install devices to avoid noninteractive failures during the Debian 13 upgrade. # On IVS appliances we assume a single-OS, single-disk layout; GRUB should be installed to the # disk that backs /boot (or / if /boot is not separate). if dpkg-query -W -f='${Status}' grub-pc 2>/dev/null | grep -q "install ok installed"; then ROOT_OR_BOOT_DEV=$(findmnt -no SOURCE /boot 2>/dev/null || findmnt -no SOURCE / 2>/dev/null || echo "") BOOT_DISK="" if [[ -n "$ROOT_OR_BOOT_DEV" ]]; then # Resolve the underlying disk name (e.g. sda, vda, nvme0n1) from the mount source. # This handles LVM and partitioned layouts by walking back to the parent disk. PARENT_DISK=$(lsblk -no PKNAME "$ROOT_OR_BOOT_DEV" 2>/dev/null || echo "") if [[ -n "$PARENT_DISK" ]]; then BOOT_DISK="$PARENT_DISK" else # Fallback: if PKNAME is empty, assume the source itself is the disk device. BOOT_DISK=$(basename "$ROOT_OR_BOOT_DEV") fi fi if [[ -n "$BOOT_DISK" ]]; then echo "[DEBIAN-UPGRADE] Preseeding grub-pc install device as /dev/$BOOT_DISK..." echo "grub-pc grub-pc/install_devices multiselect /dev/$BOOT_DISK" | debconf-set-selections echo "grub-pc grub-pc/install_devices_empty boolean false" | debconf-set-selections else echo "[DEBIAN-UPGRADE] Warning: Could not determine a boot disk for grub-pc preseeding; grub-pc may fail noninteractively during the upgrade." fi fi # Upgrade to Debian 13 (trixie) with basic retry-on-mirror-error logic ensure_boot_space "post FileWave package install kernel space check" echo "[DEBIAN-UPGRADE] Running dist-upgrade to Debian 13 (trixie)..." max_attempts=2 attempt=1 while (( attempt <= max_attempts )); do echo "[DEBIAN-UPGRADE] dist-upgrade attempt $attempt of $max_attempts..." # Clean APT cache and lists to mitigate hash sum mismatch or corrupted metadata apt-get clean rm -rf /var/lib/apt/lists/* apt-get update -y || die "[DEBIAN-UPGRADE] Failed to update package list after switching to trixie." if DEBIAN_FRONTEND=noninteractive apt-get dist-upgrade -y \ -o Dpkg::Options::="--force-confdef" \ -o Dpkg::Options::="--force-confold" \ --autoremove; then echo "[DEBIAN-UPGRADE] Debian 12 -> 13 dist-upgrade completed successfully on attempt $attempt." break fi if (( attempt == max_attempts )); then echo "[DEBIAN-UPGRADE] Debian 12 -> 13 dist-upgrade failed after $max_attempts attempts." echo "[DEBIAN-UPGRADE] Common causes include transient mirror issues, HTTP proxies, or APT cache corruption." echo "[DEBIAN-UPGRADE] You can manually try the following commands, then re-run this script:" echo "[DEBIAN-UPGRADE] apt-get clean" echo "[DEBIAN-UPGRADE] rm -rf /var/lib/apt/lists/*" echo "[DEBIAN-UPGRADE] apt-get update" echo "[DEBIAN-UPGRADE] apt-get dist-upgrade" echo "[DEBIAN-UPGRADE] If the problem persists, consider switching to a different Debian mirror (e.g., deb.debian.org) and repeating the above steps." die "[DEBIAN-UPGRADE] Debian 12 -> 13 release upgrade failed after retry. See log output above for remediation steps." fi echo "[DEBIAN-UPGRADE] dist-upgrade failed (likely network or mirror issue, e.g., hash sum mismatch). Retrying after cleaning APT cache and lists..." attempt=$((attempt + 1)) sleep 5 done # Optional: modernize APT sources to deb822 format on Debian 13 if command -v apt >/dev/null 2>&1; then if apt modernize-sources --help >/dev/null 2>&1; then echo "[DEBIAN-UPGRADE] Modernizing APT sources to deb822 format on Debian 13..." apt modernize-sources --assume-yes || echo "[DEBIAN-UPGRADE] Warning: 'apt modernize-sources' failed. You may want to run it manually later." else echo "[DEBIAN-UPGRADE] This version of apt does not support 'apt modernize-sources'; skipping sources modernization." fi fi # Refresh Debian version info after upgrade DEBIAN_VERSION=$(grep -E '^VERSION_ID=' /etc/os-release | cut -d= -f2 | tr -d '"') DEBIAN_MAJOR=${DEBIAN_VERSION%%.*} if (( DEBIAN_MAJOR != 13 )); then die "[DEBIAN-UPGRADE] Debian upgrade appears incomplete. Current VERSION_ID=$DEBIAN_VERSION (expected major version 13)." fi echo "[DEBIAN-UPGRADE] Debian upgrade to 13 (trixie) completed successfully. Continuing with FileWave $VERSION installation..." OS_UPGRADED=true } #################### SWITCHES ################## if [ "$#" -eq 0 ]; then echo "Usage: wget -qO- https://kb.filewave.com/attachments/408 | sudo bash -s -- -v -r [-d|--dev | -b|--beta | -p|--prod] [-y|--yes] [-yy|--yesyes]" echo " Options:" echo " -v, --version Specify the version of FileWave IVS to install (e.g., 16.3.0)" echo " -r, --revision Legacy revision selector (currently always '1'; other values are accepted but ignored)" echo " -d, --dev Use Dev server for downloads (default is production)" echo " -b, --beta Use beta server for downloads (default is production)" echo " -p, --prod Use production server for downloads" echo " -y, --yes Automatically answer yes to routine prompts (installation, OS patch upgrade, reboot). Does NOT auto-approve SSH-without-screen or Debian 12 -> 13 release upgrades." echo " -yy, --yesyes Automatically answer yes to all prompts, including SSH-without-screen warnings and Debian 12 -> 13 OS release upgrades." exit 1 fi # Parse input arguments (-v/-r/-d/-b/-p/-y/-yy) and override defaults while [[ "$#" -gt 0 ]]; do case $1 in -v|--version) VERSION="$2" shift 2 ;; -r|--revision) REVISION="$2" shift 2 ;; -d|--dev) SERVER_TYPE="dev" shift ;; -b|--beta) SERVER_TYPE="beta" shift ;; -p|--prod) SERVER_TYPE="prod" shift ;; -y|--yes) auto_yes=true shift ;; -yy|--yesyes) auto_yes=true auto_risky_yes=true shift ;; *) echo "Unknown option: $1" exit 1 ;; esac done # Normalize revision: current FileWave IVS builds use revision 1 only. # The -r/--revision switch is kept for legacy compatibility, but any value # other than '1' is ignored and coerced back to '1'. if [[ "$REVISION" != "1" ]]; then echo "[INFO] Ignoring requested revision '$REVISION' and using revision '1'. Current FileWave IVS builds ship with revision 1 only." REVISION="1" fi # Root password must be set for installer components that may invoke su. ensure_root_password_set # ===================== SUMMARY TAG BLOCK ===================== DETECTED_DEBIAN_VERSION=$(grep -E '^VERSION_ID=' /etc/os-release | cut -d= -f2 | tr -d '"') DETECTED_DEBIAN_MAJOR=${DETECTED_DEBIAN_VERSION%%.*} # Use filewave-ivs as the authoritative source for the installed FileWave version INSTALLED_FW_IVS_VERSION=$(dpkg-query -W -f='${Version}' filewave-ivs 2>/dev/null || echo "unknown") # Still record the ivs-kernel package version separately for diagnostics, since it may differ INSTALLED_IVS_KERNEL_VERSION=$(dpkg-query -W -f='${Version}' ivs-kernel 2>/dev/null || echo "unknown") PLANNED_DEBIAN_BEHAVIOR="Unknown" # IVS requires an existing installation (upgrade-only). Abort BEFORE any OS release upgrade prompts. INSTALLED_VERSION=$(dpkg-query -W -f='${Version}' filewave-ivs 2>/dev/null || true) if [[ -z "${INSTALLED_VERSION:-}" || "${INSTALLED_VERSION:-}" == "none" ]]; then die "No FileWave IVS packages are installed. This script upgrades an existing IVS installation only. Install the IVS appliance image first, then re-run this script for upgrades." fi if dpkg --compare-versions "$VERSION" ge "$MIN_DEBIAN13_FW_VERSION"; then if (( DETECTED_DEBIAN_MAJOR == 12 )); then PLANNED_DEBIAN_BEHAVIOR="Upgrade Debian 12 -> 13 (trixie) before FileWave upgrade" elif (( DETECTED_DEBIAN_MAJOR == 13 )); then PLANNED_DEBIAN_BEHAVIOR="Stay on Debian 13 (trixie) for this upgrade" else PLANNED_DEBIAN_BEHAVIOR="Unsupported Debian major $DETECTED_DEBIAN_MAJOR for this FileWave version" fi else if (( DETECTED_DEBIAN_MAJOR == 12 )); then PLANNED_DEBIAN_BEHAVIOR="Stay on Debian 12 (bookworm); no release upgrade" elif (( DETECTED_DEBIAN_MAJOR == 13 )); then PLANNED_DEBIAN_BEHAVIOR="Blocked combination: FileWave $VERSION is supported only on Debian 12" else PLANNED_DEBIAN_BEHAVIOR="Unsupported Debian major $DETECTED_DEBIAN_MAJOR for this FileWave version" fi fi echo "[SUMMARY] Detected Debian Version: $DETECTED_DEBIAN_VERSION" if has_separate_boot; then BOOT_SUMMARY="$(boot_fs_use_pct)" echo "[SUMMARY] /boot Usage: $BOOT_SUMMARY" else echo "[SUMMARY] /boot Usage: N/A (no separate /boot filesystem; /boot is on /)" echo "[SUMMARY] Root FS Usage: $(root_fs_use_pct)" echo "[SUMMARY] /boot Directory Size: $(boot_dir_size)" fi echo "[SUMMARY] Installed FileWave IVS Version (filewave-ivs): $INSTALLED_FW_IVS_VERSION" echo "[SUMMARY] IVS Kernel Package Version (ivs-kernel): $INSTALLED_IVS_KERNEL_VERSION" if [[ "$INSTALLED_FW_IVS_VERSION" == "unknown" || -z "$INSTALLED_FW_IVS_VERSION" ]]; then echo "[SUMMARY] IVS Presence Check: NO — this system does not appear to have an existing FileWave IVS installation" else echo "[SUMMARY] IVS Presence Check: YES — existing FileWave IVS installation detected" fi echo "[SUMMARY] Target FileWave Version: $VERSION-$REVISION" echo "[SUMMARY] Server Type: $SERVER_TYPE" echo "[SUMMARY] Auto-Yes Mode: $auto_yes" echo "[SUMMARY] Risky Auto-Yes Mode (yesyes): $auto_risky_yes" echo "[SUMMARY] Planned Debian behavior: $PLANNED_DEBIAN_BEHAVIOR" # Warn if the IVS kernel package version does not appear to match the FileWave IVS version KERNEL_VERSION_MAIN="$INSTALLED_IVS_KERNEL_VERSION" if [[ "$KERNEL_VERSION_MAIN" != "unknown" && -n "$KERNEL_VERSION_MAIN" ]]; then KERNEL_VERSION_MAIN="${KERNEL_VERSION_MAIN%%-*}" fi if [[ "$INSTALLED_FW_IVS_VERSION" != "unknown" && -n "$INSTALLED_FW_IVS_VERSION" && "$KERNEL_VERSION_MAIN" != "unknown" && -n "$KERNEL_VERSION_MAIN" && "$INSTALLED_FW_IVS_VERSION" != "$KERNEL_VERSION_MAIN" ]]; then echo "[SUMMARY] WARNING: Detected mismatch between FileWave IVS version (filewave-ivs=$INSTALLED_FW_IVS_VERSION) and IVS kernel package version (ivs-kernel=$INSTALLED_IVS_KERNEL_VERSION). This may indicate a troubleshooting kernel or an inconsistent installation state." fi echo "[SUMMARY] Beginning validation and upgrade process..." # ============================================================= #################### INSTALLED VERSION + DOWNGRADE PROTECTION (EARLY) #################### # IVS upgrades are only supported on existing IVS appliances. We enforce that early so we # do not perform OS upgrades (including Debian 12 -> 13) or install tools on non-IVS hosts. INSTALLED_VERSION=$(dpkg-query -W -f='${Version}' filewave-ivs 2>/dev/null || true) if [[ -z "$INSTALLED_VERSION" || "$INSTALLED_VERSION" == "none" ]]; then die "No FileWave IVS packages are installed. This script only supports upgrading an existing FileWave IVS appliance and cannot perform a fresh installation. Please deploy a supported FileWave IVS appliance image first, then re-run this script for upgrades." fi # filewave-ivs uses a semantic version (e.g., 16.3.0) without a '-' suffix. # For downgrade logic, treat that semantic version as the 'main' version and assume revision 1. INSTALLED_MAIN_VERSION="$INSTALLED_VERSION" INSTALLED_REVISION=1 # Prevent installing FileWave IVS 15.5.0 (known unsupported release); require 15.5.1 or newer if dpkg --compare-versions "$VERSION" eq "15.5.0"; then die "Installing FileWave IVS 15.5.0 is not supported. Please install version 15.5.1 or newer." fi # Prevent downgrades (but allow reinstalling the same version). # We compare only the semantic FileWave version (e.g., 16.3.0) because filewave-ivs does not # encode revision, and in practice all IVS revisions are '1'. if dpkg --compare-versions "$INSTALLED_MAIN_VERSION" gt "$VERSION"; then echo "Installed version ($INSTALLED_MAIN_VERSION) is newer than the requested version ($VERSION-$REVISION)." die "Aborting to prevent a downgrade of the FileWave IVS server." fi if dpkg --compare-versions "$INSTALLED_MAIN_VERSION" eq "$VERSION"; then echo "Requested version ($VERSION-$REVISION) matches the currently installed version ($INSTALLED_MAIN_VERSION)." echo "Proceeding with a reinstall of the same FileWave IVS version." fi ################ Debian / FileWave Version Matrix ################ # Enforce supported Debian major versions (12 vs 13) based on the requested FileWave version # Enforce supported Debian version based on target FileWave version if dpkg --compare-versions "$VERSION" ge "$MIN_DEBIAN13_FW_VERSION"; then # For FileWave 16.3.0 and newer, require Debian 13 if (( DEBIAN_MAJOR == 12 )); then upgrade_to_debian13 elif (( DEBIAN_MAJOR == 13 )); then echo "Debian 13 detected; proceeding with FileWave $VERSION." else die "Unsupported Debian major version $DEBIAN_MAJOR for FileWave $VERSION. Debian 13 is required." fi else # For FileWave versions older than 16.3.0, require Debian 12 and do not upgrade to Debian 13 if (( DEBIAN_MAJOR != 12 )); then die "FileWave $VERSION is only supported on Debian 12. Current Debian version is $DEBIAN_VERSION." fi fi #################### MAIN ################## # Confirm high-level IVS upgrade action with the operator (unless auto-yes mode applies) echo "This script will update your OS and install/upgrade the FileWave IVS packages." echo "Version: $VERSION, Revision: $REVISION, Server Type: $SERVER_TYPE" if [[ "$auto_yes" == "true" ]]; then confirm="yes" else read -p "Do you wish to proceed? (yes/no): " confirm < /dev/tty fi if [[ ! "$confirm" =~ ^[Yy]([Ee][Ss])?$ ]]; then echo "Aborting installation as requested." exit 0 fi # Resolve the base download URL according to server type (prod/beta/dev) if [ "$SERVER_TYPE" = "beta" ]; then BASE_URL="https://fwbetas.filewave.com" elif [ "$SERVER_TYPE" = "dev" ]; then BASE_URL="https://fwdevdl.filewave.com" else # Default to production download server BASE_URL="https://fwdl.filewave.com" fi ################################################### # 0. Remove legacy FileWave APT sources that were shipped with older IVS images # (avoids stale or conflicting repository entries during OS/FileWave upgrades) rm -f /etc/apt/sources.list.d/filewave-beta.list > /dev/null || true rm -f /etc/apt/sources.list.d/filewave-dev.list > /dev/null || true rm -f /etc/apt/sources.list.d/filewave-release.list > /dev/null || true ################################################### # 1. Ensure essential tools (python3, curl, zip, gdebi) are present for the upgrade process echo "Installing essential tools..." apt-get clean || die "Failed to clean apt cache." apt-get update -y || die "Failed to update package list." apt-get --fix-broken install -y || die "Failed to fix broken installs from apt." apt-get autoremove -y || die "Failed to autoremove from apt." ensure_boot_space "pre OS patch upgrade kernel space check" for dep in "python3" "curl" "zip" "gdebi"; do if ! command -v $dep &> /dev/null; then echo "$dep not found. Installing $dep..." apt-get update -qq DEBIAN_FRONTEND=noninteractive apt-get install -y -q "$dep" || die "Failed to install $dep." #apt-get install -y $dep || die "Failed to install $dep." fi done # Ensure Python development headers are present so pip can build C extensions (e.g., netifaces) # on Debian 13 and future Python versions. if ! dpkg-query -W -f='${Status}' python3-dev 2>/dev/null | grep -q "install ok installed"; then echo "python3-dev not found. Installing python3-dev..." apt-get update -qq DEBIAN_FRONTEND=noninteractive apt-get install -y -q python3-dev || die "Failed to install python3-dev." fi # Handle iperf3 separately because it prompts to start a systemd service; preseed and enable non-interactively if ! command -v iperf3 &>/dev/null; then echo "iperf3 not found. Installing iperf3 and enabling service..." # Preseed debconf to automatically accept starting the service echo "iperf3 iperf3/start_daemon boolean true" | debconf-set-selections # Install non-interactively DEBIAN_FRONTEND=noninteractive apt-get install -y -q iperf3 || die "Failed to install iperf3." # Ensure the systemd service is enabled and started (only if the unit exists) if systemctl list-unit-files --no-legend 2>/dev/null | awk '{print $1}' | grep -qx "iperf3.service"; then systemctl enable --now iperf3 || echo "[WARN] Failed to enable/start iperf3 service." else echo "[WARN] iperf3 installed but iperf3.service unit not found; skipping enable/start." fi # If UFW is installed and active, open TCP 5201 (iperf3). Do nothing otherwise. if command -v ufw >/dev/null 2>&1; then if ufw status | grep -q "Status: active"; then if ! ufw status | grep -q "5201/tcp"; then echo "UFW detected and active; allowing TCP 5201 for iperf3..." ufw allow 5201/tcp else echo "UFW rule for 5201/tcp already present; skipping." fi else echo "UFW installed but not active; not adding firewall rule." fi else echo "UFW not installed; skipping firewall configuration." fi fi if ! dpkg-query -W -f='${Status}' net-tools 2>/dev/null | grep -q "install ok installed"; then echo "net-tools not found. Installing net-tools..." apt-get install -y net-tools || die "Failed to install net-tools." fi install_packages() { echo "[INSTALL] Downloading and installing FileWave IVS packages..." PACKAGE_ORDER=( "filewave-admin_${VERSION}_amd64.deb" "filewave-imaging-client_${VERSION}_amd64.deb" "ivs-kernel-${VERSION}-${REVISION}.x86_64.deb" "filewave-ivs_${VERSION}_amd64.deb" ) DOWNLOAD_DIR="/tmp/filewave_install_$$" cleanup_download_dir mkdir -p "$DOWNLOAD_DIR" || die "Failed to create download directory." for package in "${PACKAGE_ORDER[@]}"; do local PACKAGE_URL="$BASE_URL/$VERSION/$package" echo "[DOWNLOAD] Validating download exists: $PACKAGE_URL" if ! validate_download_exists "$PACKAGE_URL" 2; then die "[DOWNLOAD] Package not found at: $PACKAGE_URL (server_type=$SERVER_TYPE). Verify the version/channel and try again." fi echo "[DOWNLOAD] Downloading $package..." for i in {1..3}; do wget -q -O "$DOWNLOAD_DIR/$package" "$PACKAGE_URL" && break echo "[DOWNLOAD] Retrying download ($i/3)..." sleep 5 done if [[ ! -f "$DOWNLOAD_DIR/$package" ]]; then die "[DOWNLOAD] Failed to download $package after multiple attempts." fi echo "Installing $package..." gdebi -n "$DOWNLOAD_DIR/$package" || die "Failed to install $package" done } # If we reach this point, the requested version is newer than the installed version. echo "Upgrading from $INSTALLED_VERSION to $VERSION-$REVISION..." install_packages ensure_boot_space "After IVS package install" ################################################### # 4. OS Upgrade # - If a full Debian 12 → 13 upgrade already occurred, skip. # - Otherwise, update the current Debian release to the latest patches. if [[ "$OS_UPGRADED" == "true" ]]; then echo "[OS-UPGRADE] Skipping generic OS upgrade step because a full Debian upgrade to 13 has already been performed in this run." else echo "Warning: This script will upgrade the entire OS. This is required for security." if [[ "$auto_yes" == "true" ]]; then upgrade_confirm="yes" else read -p "Do you wish to proceed with the OS upgrade? (yes/no): " upgrade_confirm < /dev/tty fi if [[ ! "$upgrade_confirm" =~ ^[Yy]([Ee][Ss])?$ ]]; then echo "[OS-UPGRADE] Skipping OS upgrade at user request." else ensure_boot_space "Before OS patch upgrade" echo "[OS-UPGRADE] Upgrading the system..." apt-get update -y || die "[OS-UPGRADE] Failed to update package list." DEBIAN_FRONTEND=noninteractive apt-get upgrade -y \ -o Dpkg::Options::="--force-confdef" \ -o Dpkg::Options::="--force-confold" \ --autoremove || die "[OS-UPGRADE] System upgrade failed." fi fi ################################################### # 5. Reboot to complete OS and FileWave IVS updates echo "The system will reboot in 15 seconds to complete the installation." if [[ "$auto_yes" == "true" ]]; then reboot_confirm="yes" else read -p "Do you want to reboot now? (yes/no): " reboot_confirm < /dev/tty fi if [[ "$reboot_confirm" =~ ^[Yy]([Ee][Ss])?$ ]]; then echo "[SUMMARY-END] Finalizing upgrade..." FINAL_IVS_VERSION=$(dpkg-query -W -f='${Version}' filewave-ivs 2>/dev/null || echo "unknown") FINAL_IVS_KERNEL_VERSION=$(dpkg-query -W -f='${Version}' ivs-kernel 2>/dev/null || echo "unknown") FINAL_KERNEL_VERSION_MAIN="$FINAL_IVS_KERNEL_VERSION" if [[ "$FINAL_KERNEL_VERSION_MAIN" != "unknown" && -n "$FINAL_KERNEL_VERSION_MAIN" ]]; then FINAL_KERNEL_VERSION_MAIN="${FINAL_KERNEL_VERSION_MAIN%%-*}" fi echo "[SUMMARY-END] Debian Version After Upgrade: $DEBIAN_VERSION" if has_separate_boot; then echo "[SUMMARY-END] /boot Usage After Upgrade: $(boot_fs_use_pct)" else echo "[SUMMARY-END] /boot Usage After Upgrade: N/A (no separate /boot filesystem; /boot is on /)" echo "[SUMMARY-END] Root FS Usage After Upgrade: $(root_fs_use_pct)" echo "[SUMMARY-END] /boot Directory Size After Upgrade: $(boot_dir_size)" fi echo "[SUMMARY-END] FileWave IVS Version Installed (filewave-ivs): $FINAL_IVS_VERSION" echo "[SUMMARY-END] IVS Kernel Package Version (ivs-kernel): $FINAL_IVS_KERNEL_VERSION" if [[ "$FINAL_IVS_VERSION" != "unknown" && -n "$FINAL_IVS_VERSION" && "$FINAL_KERNEL_VERSION_MAIN" != "unknown" && -n "$FINAL_KERNEL_VERSION_MAIN" && "$FINAL_IVS_VERSION" != "$FINAL_KERNEL_VERSION_MAIN" ]]; then echo "[SUMMARY-END] WARNING: Post-upgrade mismatch between FileWave IVS version (filewave-ivs=$FINAL_IVS_VERSION) and IVS kernel package version (ivs-kernel=$FINAL_IVS_KERNEL_VERSION). This may indicate a troubleshooting kernel or an inconsistent installation state." fi echo "[SUMMARY-END] Major OS Upgrade Performed (Debian 12 -> 13): $OS_UPGRADED" echo "[SUMMARY-END] Log File: $LOG_FILE" if [[ "${auto_risky_yes:-false}" == "true" ]]; then echo "Rebooting system now (unattended mode)..." reboot else echo "Rebooting system in 120 seconds... (press Enter to reboot now)" if read -r -t 120 _ < /dev/tty; then echo "[REBOOT] Rebooting now..." else echo "[REBOOT] Timeout reached; rebooting now..." fi reboot fi else echo "Skipping reboot. Please remember to reboot manually to apply all changes." fi # The end