#!/bin/bash #=============================================================================== # RHEL 8.10 Offline Update Script # # This interactive script guides administrators through applying updates # from a locally-mounted ISO file. # # ISO: Full_Latest_RHEL8_x86_64_patches_2026-01-13.iso # Mount Point: /media/JanPatch # Repo File: /etc/yum.repos.d/JanPatch.repo #=============================================================================== # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Configuration ISO_NAME="Full_Latest_RHEL8_x86_64_patches_2026-01-13.iso" ISO_PATH="/var/isos/${ISO_NAME}" MOUNT_POINT="/media/JanPatch" REPO_FILE="/etc/yum.repos.d/JanPatch.repo" # Global variables for tracking FOUND_ISO_LOCATIONS=() UNMOUNTED_USB_DEVICES=() USB_MOUNT_DIR="" # Track if we mounted a USB drive #------------------------------------------------------------------------------- # Helper Functions #------------------------------------------------------------------------------- print_header() { echo "" echo -e "${BLUE}===============================================================================${NC}" echo -e "${BLUE} $1${NC}" echo -e "${BLUE}===============================================================================${NC}" echo "" } print_step() { echo -e "${GREEN}[STEP $1]${NC} $2" echo "" } print_info() { echo -e "${YELLOW}[INFO]${NC} $1" } print_error() { echo -e "${RED}[ERROR]${NC} $1" } print_success() { echo -e "${GREEN}[SUCCESS]${NC} $1" } pause_continue() { echo "" read -p "Press Enter to continue..." echo "" } confirm_action() { local prompt="$1" local response echo "" read -p "${prompt} (y/n): " response case "$response" in [yY]|[yY][eE][sS]) return 0 ;; *) return 1 ;; esac } check_root() { if [[ $EUID -ne 0 ]]; then print_error "This script must be run as root or with sudo." echo "Please run: sudo $0" exit 1 fi } #------------------------------------------------------------------------------- # ISO Detection Functions #------------------------------------------------------------------------------- detect_external_media() { # Find mounted external media (USB drives, external disks) # Looks in common mount locations for removable media local found_locations=() local search_paths=( "/run/media" "/media" "/mnt" ) print_info "Scanning for external media..." echo "" for base_path in "${search_paths[@]}"; do if [[ -d "$base_path" ]]; then # Find the ISO file recursively in media locations while IFS= read -r -d '' file; do found_locations+=("$file") done < <(find "$base_path" -maxdepth 3 -name "$ISO_NAME" -type f -print0 2>/dev/null) fi done # Return results via global variable FOUND_ISO_LOCATIONS=("${found_locations[@]}") } detect_unmounted_usb() { # Detect USB drives that might not be mounted yet # Returns list of unmounted USB block devices local unmounted_usb=() # Get list of USB block devices for device in /sys/block/sd*; do if [[ -d "$device" ]]; then local devname=$(basename "$device") local removable=$(cat "$device/removable" 2>/dev/null) # Check if it's removable (USB) if [[ "$removable" == "1" ]]; then # Check each partition for part in /dev/${devname}*; do if [[ -b "$part" && "$part" != "/dev/$devname" ]]; then # Check if partition is mounted if ! mountpoint -q "$part" 2>/dev/null && ! grep -q "^$part " /proc/mounts; then unmounted_usb+=("$part") fi fi done fi fi done UNMOUNTED_USB_DEVICES=("${unmounted_usb[@]}") } show_iso_search_menu() { # Display menu for ISO location options local options=() local option_paths=() local count=1 echo "Where would you like to load the ISO from?" echo "" # Option: Default location if exists if [[ -f "$ISO_PATH" ]]; then echo " ${count}) Default location: ${ISO_PATH}" options+=("default") option_paths+=("$ISO_PATH") ((count++)) fi # Options: Found on external media for location in "${FOUND_ISO_LOCATIONS[@]}"; do echo " ${count}) External media: ${location}" options+=("external") option_paths+=("$location") ((count++)) done # Option: Mount unmounted USB if [[ ${#UNMOUNTED_USB_DEVICES[@]} -gt 0 ]]; then echo "" echo " Unmounted USB devices detected:" for usb in "${UNMOUNTED_USB_DEVICES[@]}"; do echo " ${count}) Mount and scan: ${usb}" options+=("mount_usb") option_paths+=("$usb") ((count++)) done fi # Option: Manual path entry echo "" echo " ${count}) Enter path manually" options+=("manual") option_paths+=("") ((count++)) # Option: Exit echo " ${count}) Exit" options+=("exit") option_paths+=("") echo "" read -p "Select option [1-${count}]: " selection # Validate selection if [[ ! "$selection" =~ ^[0-9]+$ ]] || [[ "$selection" -lt 1 ]] || [[ "$selection" -gt ${#options[@]} ]]; then print_error "Invalid selection." return 1 fi local idx=$((selection - 1)) local chosen_option="${options[$idx]}" local chosen_path="${option_paths[$idx]}" case "$chosen_option" in "default"|"external") ISO_PATH="$chosen_path" return 0 ;; "mount_usb") mount_usb_device "$chosen_path" return $? ;; "manual") read -p "Enter full path to ISO file: " custom_path if [[ -f "$custom_path" ]]; then ISO_PATH="$custom_path" return 0 else print_error "File not found: ${custom_path}" return 1 fi ;; "exit") echo "Exiting." exit 0 ;; esac } mount_usb_device() { local device="$1" local mount_dir="/mnt/usb_temp_$$" print_info "Mounting ${device}..." mkdir -p "$mount_dir" if mount "$device" "$mount_dir"; then print_success "Mounted ${device} to ${mount_dir}" # Search for ISO on mounted device local found_iso=$(find "$mount_dir" -maxdepth 2 -name "$ISO_NAME" -type f 2>/dev/null | head -1) if [[ -n "$found_iso" ]]; then print_success "Found ISO: ${found_iso}" if confirm_action "Copy ISO to ${ISO_PATH}? (This will take several minutes for 30GB)"; then print_info "Copying ISO... This may take a while." # Ensure target directory exists mkdir -p "$(dirname "$ISO_PATH")" # Copy with progress using rsync if available, otherwise cp if command -v rsync &>/dev/null; then rsync -ah --progress "$found_iso" "$ISO_PATH" else cp -v "$found_iso" "$ISO_PATH" fi if [[ -f "$ISO_PATH" ]]; then print_success "ISO copied successfully." umount "$mount_dir" rmdir "$mount_dir" return 0 else print_error "Failed to copy ISO." fi else # Use directly from USB ISO_PATH="$found_iso" USB_MOUNT_DIR="$mount_dir" # Remember to cleanup later print_info "Using ISO directly from USB (slower but no copy needed)" return 0 fi else print_error "ISO file not found on ${device}" echo "Files on device:" ls -la "$mount_dir" | head -10 fi umount "$mount_dir" rmdir "$mount_dir" return 1 else print_error "Failed to mount ${device}" rmdir "$mount_dir" 2>/dev/null return 1 fi } #------------------------------------------------------------------------------- # Main Script Functions #------------------------------------------------------------------------------- show_welcome() { clear print_header "RHEL 8.10 Offline Update Script" echo "This script will guide you through applying updates from the ISO file:" echo "" echo " ISO File: ${ISO_NAME}" echo " Mount Point: ${MOUNT_POINT}" echo " Repo File: ${REPO_FILE}" echo "" echo "Steps this script will perform:" echo " 1. Verify the ISO file exists" echo " 2. Create mount point and mount the ISO" echo " 3. Create the local repository configuration" echo " 4. Refresh the package cache" echo " 5. Show available updates" echo " 6. Apply updates" echo " 7. Check if reboot is required" echo " 8. Cleanup (optional)" echo "" if ! confirm_action "Ready to begin?"; then echo "Exiting." exit 0 fi } step1_verify_iso() { print_header "Step 1: Locate ISO File" print_step "1" "Searching for ISO file..." # Detect external media with ISO detect_external_media detect_unmounted_usb # Check if ISO exists in default location local iso_found=false if [[ -f "$ISO_PATH" ]]; then iso_found=true fi # Check if found on external media if [[ ${#FOUND_ISO_LOCATIONS[@]} -gt 0 ]]; then iso_found=true fi # Show what we found if [[ "$iso_found" == true ]] || [[ ${#UNMOUNTED_USB_DEVICES[@]} -gt 0 ]]; then # If only one option (default location) and no USB, use it directly if [[ -f "$ISO_PATH" ]] && [[ ${#FOUND_ISO_LOCATIONS[@]} -eq 0 ]] && [[ ${#UNMOUNTED_USB_DEVICES[@]} -eq 0 ]]; then print_success "ISO file found: ${ISO_PATH}" local size=$(du -h "$ISO_PATH" | cut -f1) print_info "File size: ${size}" else # Multiple options available - show menu if ! show_iso_search_menu; then print_error "Failed to locate ISO file." exit 1 fi print_success "Using ISO: ${ISO_PATH}" fi else # Nothing found anywhere print_error "ISO file not found." echo "" echo "The script searched for: ${ISO_NAME}" echo "" echo "Locations checked:" echo " - ${ISO_PATH}" echo " - /run/media/*/ (mounted USB drives)" echo " - /media/*/" echo " - /mnt/*/" echo "" if confirm_action "Would you like to enter the path manually?"; then read -p "Enter full path to ISO file: " custom_path if [[ -f "$custom_path" ]]; then ISO_PATH="$custom_path" print_success "Using ISO: ${ISO_PATH}" else print_error "File not found: ${custom_path}" exit 1 fi else echo "" echo "Please copy the ISO to /var/isos/ and run this script again." echo "" echo "Example from USB drive:" echo " cp /run/media/\$(whoami)/USBDRIVE/${ISO_NAME} /var/isos/" exit 1 fi fi # Verify the file is readable if [[ ! -r "$ISO_PATH" ]]; then print_error "ISO file exists but is not readable. Check permissions." exit 1 fi pause_continue } step2_mount_iso() { print_header "Step 2: Mount the ISO" print_step "2" "Creating mount point and mounting ISO..." # Check if already mounted if mountpoint -q "$MOUNT_POINT" 2>/dev/null; then print_info "Mount point ${MOUNT_POINT} is already mounted." if confirm_action "Would you like to unmount and remount?"; then umount "$MOUNT_POINT" else print_info "Using existing mount." pause_continue return fi fi # Create mount point if it doesn't exist if [[ ! -d "$MOUNT_POINT" ]]; then print_info "Creating mount point: ${MOUNT_POINT}" mkdir -p "$MOUNT_POINT" fi # Mount the ISO print_info "Mounting ISO..." if mount -o loop "$ISO_PATH" "$MOUNT_POINT"; then print_success "ISO mounted successfully to ${MOUNT_POINT}" # Show contents echo "" print_info "ISO contents:" ls -la "$MOUNT_POINT" | head -15 else print_error "Failed to mount ISO." exit 1 fi pause_continue } step3_create_repo() { print_header "Step 3: Create Repository Configuration" print_step "3" "Creating local repository file..." # Check if repo file already exists if [[ -f "$REPO_FILE" ]]; then print_info "Repo file already exists: ${REPO_FILE}" echo "" echo "Current contents:" cat "$REPO_FILE" echo "" if ! confirm_action "Would you like to overwrite it?"; then print_info "Keeping existing repo file." pause_continue return fi fi # Create the repo file cat > "$REPO_FILE" << 'EOF' [dvd-rhel-patches] name=DVD for RHEL Patches baseurl=file:///media/JanPatch enabled=1 gpgcheck=0 EOF print_success "Repository file created: ${REPO_FILE}" echo "" echo "Contents:" cat "$REPO_FILE" pause_continue } step4_refresh_cache() { print_header "Step 4: Refresh Package Cache" print_step "4" "Cleaning and rebuilding DNF cache..." print_info "Running: dnf clean all" dnf clean all echo "" print_info "Running: dnf makecache" dnf makecache echo "" print_info "Verifying repository is available..." dnf repolist | grep -E "dvd-rhel-patches|repo id" if dnf repolist | grep -q "dvd-rhel-patches"; then print_success "Repository is available and ready." else print_error "Repository not found in repolist. Please check configuration." fi pause_continue } step5_check_updates() { print_header "Step 5: Check Available Updates" print_step "5" "Checking for available updates..." print_info "Running: dnf check-update" echo "" # dnf check-update returns exit code 100 if updates are available dnf check-update local exit_code=$? echo "" if [[ $exit_code -eq 100 ]]; then print_success "Updates are available (see list above)." elif [[ $exit_code -eq 0 ]]; then print_info "No updates available. System may already be up to date." else print_error "Error checking for updates." fi pause_continue } step6_apply_updates() { print_header "Step 6: Apply Updates" print_step "6" "Ready to apply updates..." echo "This will install all available updates from the ISO." echo "" if ! confirm_action "Do you want to proceed with installing updates?"; then print_info "Skipping update installation." pause_continue return fi echo "" print_info "Running: dnf update -y" echo "" if dnf update -y; then print_success "Updates installed successfully." else print_error "Some updates may have failed. Please review the output above." fi pause_continue } step7_check_reboot() { print_header "Step 7: Check Reboot Status" print_step "7" "Checking if a reboot is required..." print_info "Running: needs-restarting -r" echo "" if needs-restarting -r; then print_success "No reboot required." else print_info "A reboot is recommended to complete the update." echo "" if confirm_action "Would you like to reboot now?"; then print_info "System will reboot in 10 seconds. Press Ctrl+C to cancel." sleep 10 systemctl reboot else print_info "Please remember to reboot the system later." fi fi pause_continue } step8_cleanup() { print_header "Step 8: Cleanup (Optional)" print_step "8" "Cleanup options..." echo "You can now clean up the update configuration." echo "" # Disable repo if confirm_action "Disable the dvd-rhel-patches repository?"; then dnf config-manager --set-disabled dvd-rhel-patches print_success "Repository disabled." fi # Remove repo file if confirm_action "Remove the repo file (${REPO_FILE})?"; then rm -f "$REPO_FILE" print_success "Repo file removed." fi # Unmount ISO if mountpoint -q "$MOUNT_POINT" 2>/dev/null; then if confirm_action "Unmount the ISO from ${MOUNT_POINT}?"; then umount "$MOUNT_POINT" rmdir "$MOUNT_POINT" 2>/dev/null print_success "ISO unmounted." fi fi # Unmount USB if we mounted one if [[ -n "$USB_MOUNT_DIR" ]] && mountpoint -q "$USB_MOUNT_DIR" 2>/dev/null; then if confirm_action "Unmount the USB drive from ${USB_MOUNT_DIR}?"; then umount "$USB_MOUNT_DIR" rmdir "$USB_MOUNT_DIR" 2>/dev/null print_success "USB drive unmounted. Safe to remove." fi fi # Delete ISO (only if it's in /var/isos, not on USB) if [[ -f "/var/isos/$ISO_NAME" ]]; then if confirm_action "Delete the ISO file (/var/isos/${ISO_NAME})? This will free up ~30GB."; then rm -f "/var/isos/$ISO_NAME" print_success "ISO file deleted." fi fi echo "" print_success "Cleanup complete." } show_complete() { print_header "Update Process Complete" echo "The RHEL 8.10 update process has finished." echo "" echo "Summary:" echo " - Updates were applied from: ${ISO_NAME}" echo " - Check the output above for any errors" echo "" if needs-restarting -r 2>/dev/null; then echo -e " - Reboot status: ${GREEN}Not required${NC}" else echo -e " - Reboot status: ${YELLOW}Reboot recommended${NC}" fi echo "" echo "If you encounter any issues, contact the Cybersecurity Team." echo "" } #------------------------------------------------------------------------------- # Main Execution #------------------------------------------------------------------------------- main() { check_root show_welcome step1_verify_iso step2_mount_iso step3_create_repo step4_refresh_cache step5_check_updates step6_apply_updates step7_check_reboot step8_cleanup show_complete } # Run main function main