diff --git a/RHEL8/rhel8-iso-update.sh b/RHEL8/rhel8-iso-update.sh index e69de29..5afe5fc 100644 --- a/RHEL8/rhel8-iso-update.sh +++ b/RHEL8/rhel8-iso-update.sh @@ -0,0 +1,788 @@ +#!/bin/bash + +#=============================================================================== +# RHEL 8.10 Offline Update Script +#=============================================================================== +# +# DESCRIPTION +# Interactive script for applying RHEL 8.10 updates from a local ISO file +# in air-gapped or disconnected environments. +# +# SUPPORTED ISOs +# - Full_Latest_RHEL8_x86_64_patches_*.iso (full monthly update, ~30GB) +# - Incremental_Latest_RHEL8_X86_64_patches_*.iso (incremental update, smaller) +# +# USAGE +# sudo ./rhel8-iso-update.sh +# +# OPTIONS +# The script is fully interactive and will prompt for all choices. +# No command-line arguments are required. +# +# FEATURES +# - Auto-detects ISOs in /var/isos/, /media/, /mnt/, and USB drives +# - Detects and can mount unmounted USB devices +# - Cleans up previous update configuration before applying new updates +# - Creates temporary repo pointing to mounted ISO +# - Checks if reboot is required after updates +# - Optional cleanup of ISO, mount, and repo files when finished +# +# PATHS USED +# ISO Search: /var/isos/, /run/media/, /media/, /mnt/ +# Mount Point: /media/rhel-patches +# Repo File: /etc/yum.repos.d/rhel-patches.repo +# +# REQUIREMENTS +# - Root or sudo access +# - RHEL 8.10 system +# - Sufficient disk space (~35GB for full ISO if copying locally) +# +# AUTHOR +# Cybersecurity Team +# +# LAST UPDATED +# January 2026 +# +#=============================================================================== + +# 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_PATTERN_FULL="Full_Latest_RHEL8_x86_64_patches_*.iso" +ISO_PATTERN_INCREMENTAL="Incremental_Latest_RHEL8_X86_64_patches_*.iso" +ISO_NAME="" # Will be set when ISO is found/selected +ISO_PATH="" # Will be set when ISO is found/selected +ISO_DIR="/var/isos" +MOUNT_POINT="/media/rhel-patches" +REPO_FILE="/etc/yum.repos.d/rhel-patches.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 +#------------------------------------------------------------------------------- + +check_previous_update() { + # Check if there's a previous update still mounted/configured + local needs_cleanup=false + + if mountpoint -q "$MOUNT_POINT" 2>/dev/null; then + needs_cleanup=true + fi + + if [[ -f "$REPO_FILE" ]]; then + needs_cleanup=true + fi + + if [[ "$needs_cleanup" == true ]]; then + print_header "Previous Update Detected" + echo "A previous update configuration was found:" + echo "" + + if mountpoint -q "$MOUNT_POINT" 2>/dev/null; then + echo " - ISO mounted at: ${MOUNT_POINT}" + fi + + if [[ -f "$REPO_FILE" ]]; then + echo " - Repo file exists: ${REPO_FILE}" + fi + + echo "" + + if confirm_action "Would you like to clean up the previous update before continuing?"; then + print_info "Cleaning up previous update..." + + # Disable repo if it exists + if [[ -f "$REPO_FILE" ]]; then + dnf config-manager --set-disabled dvd-rhel-patches 2>/dev/null + rm -f "$REPO_FILE" + print_success "Removed old repo file." + fi + + # Unmount if mounted + if mountpoint -q "$MOUNT_POINT" 2>/dev/null; then + umount "$MOUNT_POINT" + print_success "Unmounted previous ISO." + fi + + # Clean DNF cache + dnf clean all &>/dev/null + + echo "" + print_success "Previous update cleaned up." + else + print_info "Keeping previous configuration. The script will overwrite as needed." + fi + + pause_continue + fi +} + +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" + "$ISO_DIR" + ) + + print_info "Scanning for RHEL patch ISOs (Full and Incremental)..." + echo "" + + for base_path in "${search_paths[@]}"; do + if [[ -d "$base_path" ]]; then + # Find Full ISO files + while IFS= read -r -d '' file; do + found_locations+=("$file") + done < <(find "$base_path" -maxdepth 3 -name "$ISO_PATTERN_FULL" -type f -print0 2>/dev/null) + + # Find Incremental ISO files + while IFS= read -r -d '' file; do + found_locations+=("$file") + done < <(find "$base_path" -maxdepth 3 -name "$ISO_PATTERN_INCREMENTAL" -type f -print0 2>/dev/null) + fi + done + + # Sort by filename (newest date typically last) + IFS=$'\n' FOUND_ISO_LOCATIONS=($(sort -u <<< "${found_locations[*]}")); unset IFS +} +} + +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 "Found the following RHEL patch ISO files:" + echo "" + + # Options: Found ISO files + for location in "${FOUND_ISO_LOCATIONS[@]}"; do + local filename=$(basename "$location") + local dir=$(dirname "$location") + local iso_type="Unknown" + + # Determine ISO type + if [[ "$filename" == Full_* ]]; then + iso_type="Full" + elif [[ "$filename" == Incremental_* ]]; then + iso_type="Incremental" + fi + + echo " ${count}) [${iso_type}] ${filename}" + echo " Location: ${dir}" + options+=("found") + 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 + "found") + ISO_PATH="$chosen_path" + ISO_NAME=$(basename "$ISO_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" + ISO_NAME=$(basename "$ISO_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 using both patterns + local found_iso=$(find "$mount_dir" -maxdepth 2 \( -name "$ISO_PATTERN_FULL" -o -name "$ISO_PATTERN_INCREMENTAL" \) -type f 2>/dev/null | head -1) + + if [[ -n "$found_iso" ]]; then + print_success "Found ISO: ${found_iso}" + ISO_NAME=$(basename "$found_iso") + + if confirm_action "Copy ISO to ${ISO_DIR}/${ISO_NAME}? (This will take several minutes for large ISOs)"; then + print_info "Copying ISO... This may take a while." + + # Ensure target directory exists + mkdir -p "$ISO_DIR" + + # Copy with progress using rsync if available, otherwise cp + if command -v rsync &>/dev/null; then + rsync -ah --progress "$found_iso" "${ISO_DIR}/${ISO_NAME}" + else + cp -v "$found_iso" "${ISO_DIR}/${ISO_NAME}" + fi + + if [[ -f "${ISO_DIR}/${ISO_NAME}" ]]; then + print_success "ISO copied successfully." + ISO_PATH="${ISO_DIR}/${ISO_NAME}" + 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 "No RHEL patch ISO found on ${device}" + echo "Looking for files matching:" + echo " - ${ISO_PATTERN_FULL}" + echo " - ${ISO_PATTERN_INCREMENTAL}" + echo "" + 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 a patch ISO." + echo "" + echo " Supported ISOs:" + echo " - Full: ${ISO_PATTERN_FULL}" + echo " - Incremental: ${ISO_PATTERN_INCREMENTAL}" + echo "" + echo " Mount Point: ${MOUNT_POINT}" + echo " Repo File: ${REPO_FILE}" + echo "" + echo "Steps this script will perform:" + echo " 1. Search for and select the ISO file" + 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 RHEL patch ISO files..." + + # Detect ISOs in all locations + detect_external_media + detect_unmounted_usb + + # Check if any ISOs were found + if [[ ${#FOUND_ISO_LOCATIONS[@]} -gt 0 ]]; then + + # If only one ISO found, confirm and use it + if [[ ${#FOUND_ISO_LOCATIONS[@]} -eq 1 ]] && [[ ${#UNMOUNTED_USB_DEVICES[@]} -eq 0 ]]; then + ISO_PATH="${FOUND_ISO_LOCATIONS[0]}" + ISO_NAME=$(basename "$ISO_PATH") + print_success "Found ISO: ${ISO_NAME}" + print_info "Location: $(dirname "$ISO_PATH")" + + local size=$(du -h "$ISO_PATH" | cut -f1) + print_info "File size: ${size}" + + if ! confirm_action "Use this ISO?"; then + if ! show_iso_search_menu; then + exit 1 + fi + fi + else + # Multiple options - show menu + if ! show_iso_search_menu; then + print_error "Failed to locate ISO file." + exit 1 + fi + fi + + print_success "Selected: ${ISO_NAME}" + + elif [[ ${#UNMOUNTED_USB_DEVICES[@]} -gt 0 ]]; then + # No ISOs found but unmounted USB detected + print_info "No ISO files found, but unmounted USB devices detected." + if ! show_iso_search_menu; then + exit 1 + fi + else + # Nothing found anywhere + print_error "No RHEL patch ISO files found." + echo "" + echo "The script searched for:" + echo " - ${ISO_PATTERN_FULL}" + echo " - ${ISO_PATTERN_INCREMENTAL}" + echo "" + echo "Locations checked:" + echo " - ${ISO_DIR}/" + 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" + ISO_NAME=$(basename "$ISO_PATH") + print_success "Using ISO: ${ISO_NAME}" + else + print_error "File not found: ${custom_path}" + exit 1 + fi + else + echo "" + echo "Please copy the ISO to ${ISO_DIR}/ and run this script again." + 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://${MOUNT_POINT} +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 ISO_DIR, not on USB) + if [[ -n "$ISO_NAME" ]] && [[ -f "${ISO_DIR}/${ISO_NAME}" ]]; then + if confirm_action "Delete the ISO file (${ISO_DIR}/${ISO_NAME})? This will free up ~30GB."; then + rm -f "${ISO_DIR}/${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 + check_previous_update + 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 \ No newline at end of file