diff --git a/rhel8-iso-update.sh b/rhel8-iso-update.sh new file mode 100644 index 0000000..65b8a46 --- /dev/null +++ b/rhel8-iso-update.sh @@ -0,0 +1,665 @@ +#!/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