diff --git a/launcher.sh b/launcher.sh index 2034f47..a4908fa 100755 --- a/launcher.sh +++ b/launcher.sh @@ -312,6 +312,7 @@ show_backup_menu() { echo -e "${BOLD}Maintenance:${NC}" echo "" echo -e " ${RED}3)${NC} 🗑️ Cleanup Toolkit Data - Remove IP reputation & temp files" + echo -e " ${CYAN}4)${NC} 💿 Disk Space Analyzer - Find space issues & cleanup files" echo "" echo -e " ${RED}0)${NC} Back to Main Menu" echo "" @@ -359,6 +360,7 @@ handle_backup_menu() { 1) handle_acronis_menu ;; 2) run_module "backup" "mysql-restore-to-sql.sh" ;; 3) run_module "maintenance" "cleanup-toolkit-data.sh" ;; + 4) run_module "maintenance" "disk-space-analyzer.sh" ;; 0) return ;; *) echo -e "${RED}Invalid option${NC}"; sleep 1 ;; esac diff --git a/modules/maintenance/disk-space-analyzer.sh b/modules/maintenance/disk-space-analyzer.sh new file mode 100755 index 0000000..bd1d57e --- /dev/null +++ b/modules/maintenance/disk-space-analyzer.sh @@ -0,0 +1,1320 @@ +#!/bin/bash + +################################################################################ +# Disk Space Analyzer (WinDirStat for Linux) +################################################################################ +# Purpose: Find space hogs, identify cleanup candidates, visualize disk usage +# Features: +# - Top space consumers by directory +# - Largest files scanner +# - Old/temporary file detection +# - Log/email/backup/database analysis +# - WordPress-specific analysis +# - Safe cleanup with preview +# - Export reports +################################################################################ + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +source "$SCRIPT_DIR/lib/common-functions.sh" +source "$SCRIPT_DIR/lib/system-detect.sh" + +# Require root +if [ "$EUID" -ne 0 ]; then + print_error "This script must be run as root" + exit 1 +fi + +# Temp file for results +TEMP_DIR="/tmp/disk-analysis-$$" +mkdir -p "$TEMP_DIR" + +# Cache file for faster repeated scans +CACHE_FILE="$TEMP_DIR/scan_cache.txt" + +# Cleanup on exit +trap 'rm -rf "$TEMP_DIR"' EXIT + +################################################################################ +# UTILITY FUNCTIONS +################################################################################ + +format_size() { + local bytes=$1 + + # Handle empty or non-numeric input + if ! [[ "$bytes" =~ ^[0-9]+$ ]]; then + echo "0B" + return + fi + + if [ "$bytes" -ge 1099511627776 ]; then + awk "BEGIN {printf \"%.2fTB\", $bytes/1099511627776}" + elif [ "$bytes" -ge 1073741824 ]; then + awk "BEGIN {printf \"%.2fGB\", $bytes/1073741824}" + elif [ "$bytes" -ge 1048576 ]; then + awk "BEGIN {printf \"%.2fMB\", $bytes/1048576}" + elif [ "$bytes" -ge 1024 ]; then + awk "BEGIN {printf \"%.2fKB\", $bytes/1024}" + else + echo "${bytes}B" + fi +} + +# Convert human-readable size to bytes (no bc dependency) +size_to_bytes() { + local size="$1" + local num=$(echo "$size" | sed 's/[^0-9.]//g') + local unit=$(echo "$size" | sed 's/[0-9.]//g') + + # Convert to integer bytes using awk + case "$unit" in + T|TB) awk "BEGIN {printf \"%.0f\", $num * 1099511627776}" ;; + G|GB) awk "BEGIN {printf \"%.0f\", $num * 1073741824}" ;; + M|MB) awk "BEGIN {printf \"%.0f\", $num * 1048576}" ;; + K|KB) awk "BEGIN {printf \"%.0f\", $num * 1024}" ;; + *) echo "${num%%.*}" ;; + esac +} + +show_progress() { + local message="$1" + echo -ne "\r${CYAN}⏳${NC} $message... " +} + +# Progress bar for long operations +show_progress_bar() { + local current=$1 + local total=$2 + local width=50 + + if [ "$total" -eq 0 ]; then + return + fi + + local percent=$((current * 100 / total)) + local filled=$((current * width / total)) + + printf "\r${CYAN}[" + printf "%${filled}s" | tr ' ' '=' + printf "%$((width - filled))s" | tr ' ' '-' + printf "]${NC} %d%%" "$percent" +} + +################################################################################ +# ANALYSIS FUNCTIONS +################################################################################ + +analyze_disk_overview() { + clear + print_banner "Disk Space Overview" + echo "" + + # Show all mounted filesystems + df -h | grep -E '^/dev/' | while read -r line; do + filesystem=$(echo "$line" | awk '{print $1}') + size=$(echo "$line" | awk '{print $2}') + used=$(echo "$line" | awk '{print $3}') + avail=$(echo "$line" | awk '{print $4}') + use_pct=$(echo "$line" | awk '{print $5}' | tr -d '%') + mount=$(echo "$line" | awk '{print $6}') + + # Color code by usage percentage + if [ "$use_pct" -ge 90 ]; then + color="${RED}" + status="CRITICAL" + icon="🔴" + elif [ "$use_pct" -ge 75 ]; then + color="${YELLOW}" + status="WARNING" + icon="🟡" + else + color="${GREEN}" + status="OK" + icon="🟢" + fi + + echo -e "${BOLD}$mount${NC} ($filesystem) $icon" + echo -e " Size: $size | Used: ${color}$used ($use_pct%)${NC} | Available: $avail" + echo -e " Status: ${color}${status}${NC}" + echo "" + done + + # Inodes check (often overlooked!) + echo -e "${BOLD}Inode Usage:${NC}" + echo "" + df -i | grep -E '^/dev/' | while read -r line; do + filesystem=$(echo "$line" | awk '{print $1}') + iused=$(echo "$line" | awk '{print $3}') + iavail=$(echo "$line" | awk '{print $4}') + iuse_pct=$(echo "$line" | awk '{print $5}' | tr -d '%') + mount=$(echo "$line" | awk '{print $6}') + + if [ "$iuse_pct" -ge 90 ]; then + echo -e " ${RED}$mount: ${iuse_pct}% used (CRITICAL - out of inodes!)${NC}" + elif [ "$iuse_pct" -ge 75 ]; then + echo -e " ${YELLOW}$mount: ${iuse_pct}% used${NC}" + fi + done + + echo "" + press_enter +} + +find_largest_directories() { + local scan_path="${1:-/}" + local depth="${2:-2}" + + clear + print_banner "Largest Directories" + echo "" + echo "Scanning: $scan_path (depth: $depth)" + echo "This may take a few minutes..." + echo "" + + # Scan directories, excluding system paths that cause errors + show_progress "Analyzing directory sizes" + + du -h --max-depth="$depth" \ + --exclude="/proc" \ + --exclude="/sys" \ + --exclude="/dev" \ + --exclude="/run" \ + "$scan_path" 2>/dev/null | \ + sort -rh | \ + head -50 > "$TEMP_DIR/largest_dirs.txt" + + echo -e "\r${GREEN}✓${NC} Analysis complete " + echo "" + + # Display results in a table + echo -e "${BOLD}Top 30 Largest Directories:${NC}" + echo "───────────────────────────────────────────────────────────────" + printf "%-15s %s\n" "SIZE" "PATH" + echo "───────────────────────────────────────────────────────────────" + + head -30 "$TEMP_DIR/largest_dirs.txt" | \ + awk '{printf "%-15s %s\n", $1, $2}' | \ + while read -r size path; do + # Highlight paths over 10GB + size_bytes=$(size_to_bytes "$size") + if [ "$size_bytes" -ge 10737418240 ]; then + echo -e " ${RED}$size${NC} $path" + elif [ "$size_bytes" -ge 1073741824 ]; then + echo -e " ${YELLOW}$size${NC} $path" + else + echo -e " ${CYAN}$size${NC} $path" + fi + done + + echo "" + echo -e "${DIM}Tip: Select a specific directory to drill down deeper${NC}" + echo "" + + # Offer drill-down + read -p "Enter path to analyze deeper (or press Enter to skip): " drill_path + if [ -n "$drill_path" ] && [ -d "$drill_path" ]; then + find_largest_directories "$drill_path" 3 + else + press_enter + fi +} + +find_largest_files() { + local scan_path="${1:-/}" + local min_size="${2:-100M}" + + clear + print_banner "Largest Files Scanner" + echo "" + echo "Scanning: $scan_path" + echo "Minimum size: $min_size" + echo "This may take several minutes..." + echo "" + + show_progress "Finding large files" + + find "$scan_path" -type f -size +"$min_size" \ + -not -path "*/proc/*" \ + -not -path "*/sys/*" \ + -not -path "*/dev/*" \ + -exec du -h {} + 2>/dev/null | \ + sort -rh | \ + head -100 > "$TEMP_DIR/largest_files.txt" + + local count=$(wc -l < "$TEMP_DIR/largest_files.txt") + echo -e "\r${GREEN}✓${NC} Found $count large files " + echo "" + + if [ "$count" -eq 0 ]; then + echo -e "${GREEN}No files found larger than $min_size${NC}" + echo "" + press_enter + return + fi + + # Display results + echo -e "${BOLD}Top 50 Largest Files:${NC}" + echo "───────────────────────────────────────────────────────────────" + printf "%-15s %-50s %s\n" "SIZE" "FILE" "TYPE" + echo "───────────────────────────────────────────────────────────────" + + head -50 "$TEMP_DIR/largest_files.txt" | \ + while read -r size path; do + # Determine file type + ext="${path##*.}" + case "$ext" in + log|gz|bz2|xz) filetype="Log" ;; + sql|dump) filetype="Database" ;; + tar|zip|rar|7z) filetype="Archive" ;; + mp4|avi|mkv|mov) filetype="Video" ;; + jpg|jpeg|png|gif) filetype="Image" ;; + pdf) filetype="PDF" ;; + *) filetype="Data" ;; + esac + + # Color code by size + size_bytes=$(size_to_bytes "$size") + if [ "$size_bytes" -ge 1073741824 ]; then + color="${RED}" + elif [ "$size_bytes" -ge 524288000 ]; then # 500MB + color="${YELLOW}" + else + color="${CYAN}" + fi + + # Truncate path if too long + display_path="$path" + if [ "${#path}" -gt 50 ]; then + display_path="...${path: -47}" + fi + + printf " ${color}%-15s${NC} %-50s %s\n" "$size" "$display_path" "$filetype" + done + + echo "" + press_enter +} + +analyze_old_files() { + local scan_path="${1:-/home}" + local days="${2:-90}" + + clear + print_banner "Old Files Detection" + echo "" + echo "Scanning: $scan_path" + echo "Finding files not accessed in $days days..." + echo "" + + show_progress "Searching for old files" + + find "$scan_path" -type f -atime +$days -size +10M \ + -not -path "*/.*" \ + -exec du -h {} + 2>/dev/null | \ + sort -rh | \ + head -100 > "$TEMP_DIR/old_files.txt" + + local count=$(wc -l < "$TEMP_DIR/old_files.txt") + echo -e "\r${GREEN}✓${NC} Found $count old large files " + echo "" + + if [ "$count" -eq 0 ]; then + echo -e "${GREEN}No old large files found (>10MB, not accessed in $days days)${NC}" + echo "" + press_enter + return + fi + + # Calculate total space + local total_bytes=0 + while read -r size path; do + bytes=$(size_to_bytes "$size") + total_bytes=$((total_bytes + bytes)) + done < "$TEMP_DIR/old_files.txt" + + echo -e "${BOLD}Potential Space Savings: ${GREEN}$(format_size $total_bytes)${NC}" + echo -e "${BOLD}File Count: ${CYAN}$count${NC}" + echo "" + echo -e "${BOLD}Top 30 Old Files:${NC}" + echo "───────────────────────────────────────────────────────────────" + + head -30 "$TEMP_DIR/old_files.txt" | \ + while read -r size path; do + # Get last access time + access_time=$(stat -c %x "$path" 2>/dev/null | cut -d' ' -f1) + echo -e " ${YELLOW}$size${NC} $path ${DIM}(Last access: $access_time)${NC}" + done + + echo "" + press_enter +} + +analyze_log_files() { + clear + print_banner "Log Files Analysis" + echo "" + echo "Scanning common log directories..." + echo "" + + show_progress "Analyzing log files" + + # Find all log files efficiently + { + find /var/log -type f \( -name "*.log" -o -name "*.log.*" -o -name "*.gz" \) -size +1M 2>/dev/null + find /home/*/logs -type f -size +1M 2>/dev/null + find /var/www/vhosts/*/logs -type f -size +1M 2>/dev/null + find /usr/local/apache/domlogs -type f -size +1M 2>/dev/null + } | while read -r file; do + du -h "$file" 2>/dev/null + done | sort -rh > "$TEMP_DIR/log_files.txt" + + local count=$(wc -l < "$TEMP_DIR/log_files.txt") + echo -e "\r${GREEN}✓${NC} Found $count log files " + echo "" + + if [ "$count" -eq 0 ]; then + echo -e "${GREEN}No large log files found (>1MB)${NC}" + echo "" + press_enter + return + fi + + # Calculate totals by type + local total_bytes=0 + local compressed=0 + local active=0 + + while read -r size path; do + bytes=$(size_to_bytes "$size") + total_bytes=$((total_bytes + bytes)) + + if [[ "$path" =~ \.gz$ ]] || [[ "$path" =~ \.bz2$ ]] || [[ "$path" =~ \.xz$ ]]; then + compressed=$((compressed + 1)) + else + active=$((active + 1)) + fi + done < "$TEMP_DIR/log_files.txt" + + echo -e "${BOLD}Log File Statistics:${NC}" + echo -e " Total Size: ${YELLOW}$(format_size $total_bytes)${NC}" + echo -e " Active Logs: ${CYAN}$active files${NC}" + echo -e " Compressed Logs: ${CYAN}$compressed files${NC}" + echo "" + + echo -e "${BOLD}Top 30 Largest Log Files:${NC}" + echo "───────────────────────────────────────────────────────────────" + + head -30 "$TEMP_DIR/log_files.txt" | \ + while read -r size path; do + # Check if actively being written to + if lsof "$path" &>/dev/null; then + status="${GREEN}[ACTIVE]${NC}" + else + status="${DIM}[IDLE]${NC}" + fi + + echo -e " ${YELLOW}$size${NC} $path $status" + done + + echo "" + echo -e "${BOLD}Cleanup Suggestions:${NC}" + echo " • Rotate logs: logrotate -f /etc/logrotate.conf" + echo " • Compress old logs: find /var/log -name '*.log' -mtime +7 -exec gzip {} \\;" + echo " • Delete old compressed: find /var/log -name '*.gz' -mtime +30 -delete" + echo "" + press_enter +} + +analyze_email_storage() { + clear + print_banner "Email Storage Analysis" + echo "" + + # Find mail directories + local mail_dirs=() + [ -d "/var/spool/mail" ] && mail_dirs+=("/var/spool/mail") + [ -d "/home/*/mail" ] && mail_dirs+=(/home/*/mail) + [ -d "/home/*/Maildir" ] && mail_dirs+=(/home/*/Maildir) + [ -d "/var/vmail" ] && mail_dirs+=("/var/vmail") + + if [ ${#mail_dirs[@]} -eq 0 ]; then + echo -e "${DIM}No mail directories found${NC}" + echo "" + press_enter + return + fi + + echo "Analyzing email storage..." + echo "" + + show_progress "Scanning mail directories" + + # Scan each mail directory + for dir in "${mail_dirs[@]}"; do + du -sh "$dir" 2>/dev/null + done | sort -rh > "$TEMP_DIR/email_usage.txt" + + echo -e "\r${GREEN}✓${NC} Analysis complete " + echo "" + + # Calculate total + local total_bytes=0 + while read -r size path; do + bytes=$(size_to_bytes "$size") + total_bytes=$((total_bytes + bytes)) + done < "$TEMP_DIR/email_usage.txt" + + echo -e "${BOLD}Total Email Storage: ${YELLOW}$(format_size $total_bytes)${NC}" + echo "" + + # Show breakdown + echo -e "${BOLD}Mail Directories:${NC}" + echo "───────────────────────────────────────────────────────────────" + + while read -r size path; do + echo -e " ${CYAN}$size${NC} $path" + done < "$TEMP_DIR/email_usage.txt" + + echo "" + echo -e "${BOLD}Cleanup Suggestions:${NC}" + echo " • Delete old mail: find /home/*/mail -type f -mtime +180 -delete" + echo " • Archive large mailboxes: tar -czf backup.tar.gz /home/user/mail" + echo " • Check quota settings in control panel" + echo "" + press_enter +} + +analyze_databases() { + clear + print_banner "Database Storage Analysis" + echo "" + + local found=0 + + # MySQL/MariaDB + if [ -d "/var/lib/mysql" ]; then + echo -e "${BOLD}MySQL/MariaDB Databases:${NC}" + echo "" + + show_progress "Analyzing MySQL databases" + + du -sh /var/lib/mysql/* 2>/dev/null | sort -rh | head -20 > "$TEMP_DIR/mysql_dbs.txt" + + local mysql_total=$(du -sh /var/lib/mysql 2>/dev/null | awk '{print $1}') + echo -e "\r Total Size: ${YELLOW}$mysql_total${NC}" + echo "" + + echo " Top 15 Databases:" + echo " ───────────────────────────────────────────────────────────" + + head -15 "$TEMP_DIR/mysql_dbs.txt" | while read -r size path; do + dbname=$(basename "$path") + echo -e " ${CYAN}$size${NC} $dbname" + done + + echo "" + found=1 + fi + + # PostgreSQL + if [ -d "/var/lib/pgsql" ] || [ -d "/var/lib/postgresql" ]; then + local pgdir="/var/lib/pgsql" + [ -d "/var/lib/postgresql" ] && pgdir="/var/lib/postgresql" + + echo -e "${BOLD}PostgreSQL Databases:${NC}" + echo "" + + local pgsql_total=$(du -sh "$pgdir" 2>/dev/null | awk '{print $1}') + echo -e " Total Size: ${YELLOW}$pgsql_total${NC}" + echo "" + found=1 + fi + + if [ "$found" -eq 0 ]; then + echo -e "${DIM}No database directories found${NC}" + else + echo -e "${BOLD}Cleanup Suggestions:${NC}" + echo " • Drop unused databases" + echo " • Optimize tables: mysqlcheck -o --all-databases" + echo " • Archive old data: mysqldump and compress" + fi + + echo "" + press_enter +} + +analyze_backups() { + clear + print_banner "Backup Files Analysis" + echo "" + + show_progress "Searching for backup files" + + # Find common backup files and directories + { + find /backup -type f -size +100M 2>/dev/null + find /backups -type f -size +100M 2>/dev/null + find /home -name "backup*.tar.gz" -o -name "*.sql.gz" -o -name "*.dump" 2>/dev/null | head -100 + find /var/backups -type f -size +100M 2>/dev/null + find / -maxdepth 3 -name "*backup*" -type f -size +100M 2>/dev/null + } | while read -r file; do + du -h "$file" 2>/dev/null + done | sort -rh | head -50 > "$TEMP_DIR/backup_files.txt" + + local count=$(wc -l < "$TEMP_DIR/backup_files.txt") + echo -e "\r${GREEN}✓${NC} Found $count backup files " + echo "" + + if [ "$count" -eq 0 ]; then + echo -e "${GREEN}No large backup files found (>100MB)${NC}" + echo "" + press_enter + return + fi + + # Calculate total + local total_bytes=0 + while read -r size path; do + bytes=$(size_to_bytes "$size") + total_bytes=$((total_bytes + bytes)) + done < "$TEMP_DIR/backup_files.txt" + + echo -e "${BOLD}Total Backup Storage: ${YELLOW}$(format_size $total_bytes)${NC}" + echo -e "${BOLD}File Count: ${CYAN}$count${NC}" + echo "" + + echo -e "${BOLD}Top 30 Largest Backups:${NC}" + echo "───────────────────────────────────────────────────────────────" + + head -30 "$TEMP_DIR/backup_files.txt" | \ + while read -r size path; do + # Get age + age_days=$(( ($(date +%s) - $(stat -c %Y "$path" 2>/dev/null || echo 0)) / 86400 )) + + if [ "$age_days" -gt 90 ]; then + age_color="${RED}" + age_text="$age_days days old" + elif [ "$age_days" -gt 30 ]; then + age_color="${YELLOW}" + age_text="$age_days days old" + else + age_color="${GREEN}" + age_text="$age_days days old" + fi + + echo -e " ${YELLOW}$size${NC} $path ${age_color}($age_text)${NC}" + done + + echo "" + echo -e "${BOLD}Cleanup Suggestions:${NC}" + echo " • Delete backups older than 90 days" + echo " • Move old backups to off-server storage" + echo " • Verify backup rotation is working" + echo "" + press_enter +} + +analyze_wordpress() { + clear + print_banner "WordPress Storage Analysis" + echo "" + + # Find WordPress installations + show_progress "Finding WordPress installations" + + local wp_paths=() + + # Common locations + if [ -d "/home" ]; then + while IFS= read -r wp_config; do + wp_dir=$(dirname "$wp_config") + wp_paths+=("$wp_dir") + done < <(find /home -name "wp-config.php" -type f 2>/dev/null) + fi + + if [ -d "/var/www" ]; then + while IFS= read -r wp_config; do + wp_dir=$(dirname "$wp_config") + wp_paths+=("$wp_dir") + done < <(find /var/www -name "wp-config.php" -type f 2>/dev/null) + fi + + if [ ${#wp_paths[@]} -eq 0 ]; then + echo -e "\r${DIM}No WordPress installations found${NC} " + echo "" + press_enter + return + fi + + echo -e "\r${GREEN}✓${NC} Found ${#wp_paths[@]} WordPress installations " + echo "" + + echo -e "${BOLD}WordPress Space Usage:${NC}" + echo "───────────────────────────────────────────────────────────────" + + for wp_dir in "${wp_paths[@]}"; do + # Get domain/user from path + domain=$(echo "$wp_dir" | awk -F'/' '{for(i=1;i<=NF;i++) if($i~/public_html|httpdocs|www/) print $(i-1)}' | tail -1) + + # Calculate sizes + total_size=$(du -sh "$wp_dir" 2>/dev/null | awk '{print $1}') + uploads_size=$(du -sh "$wp_dir/wp-content/uploads" 2>/dev/null | awk '{print $1}') + plugins_size=$(du -sh "$wp_dir/wp-content/plugins" 2>/dev/null | awk '{print $1}') + cache_size=$(du -sh "$wp_dir/wp-content/cache" 2>/dev/null | awk '{print $1}') + + echo -e "${BOLD}$domain${NC} ($total_size)" + echo -e " Uploads: ${CYAN}${uploads_size:-0}${NC}" + echo -e " Plugins: ${CYAN}${plugins_size:-0}${NC}" + echo -e " Cache: ${CYAN}${cache_size:-0}${NC}" + echo "" + done + + echo -e "${BOLD}Cleanup Suggestions:${NC}" + echo " • Delete old revisions: wp post delete \$(wp post list --post_type=revision --format=ids)" + echo " • Optimize images: Use WP plugins like Imagify or compress manually" + echo " • Clear cache: rm -rf wp-content/cache/*" + echo " • Remove unused plugins/themes" + echo "" + press_enter +} + +analyze_temp_files() { + clear + print_banner "Temporary Files Analysis" + echo "" + echo "Scanning /tmp and /var/tmp..." + echo "" + + show_progress "Analyzing temporary files" + + du -h --max-depth=2 /tmp /var/tmp 2>/dev/null | \ + sort -rh | \ + head -50 > "$TEMP_DIR/temp_files.txt" + + echo -e "\r${GREEN}✓${NC} Analysis complete " + echo "" + + # Calculate total + local tmp_size=$(du -sh /tmp 2>/dev/null | awk '{print $1}') + local var_tmp_size=$(du -sh /var/tmp 2>/dev/null | awk '{print $1}') + + echo -e "${BOLD}Temporary Directory Sizes:${NC}" + echo -e " /tmp: ${CYAN}$tmp_size${NC}" + echo -e " /var/tmp: ${CYAN}$var_tmp_size${NC}" + echo "" + + # Count old files + local old_count=$(find /tmp -type f -mtime +7 2>/dev/null | wc -l) + if [ "$old_count" -gt 0 ]; then + echo -e " ${YELLOW}Found $old_count files older than 7 days in /tmp${NC}" + echo "" + fi + + echo -e "${BOLD}Top 30 Items:${NC}" + echo "───────────────────────────────────────────────────────────────" + + head -30 "$TEMP_DIR/temp_files.txt" | \ + while read -r size path; do + echo -e " ${CYAN}$size${NC} $path" + done + + echo "" + echo -e "${BOLD}Safe Cleanup Commands:${NC}" + echo " • Clear old temp files: find /tmp -type f -mtime +7 -delete" + echo " • Clear package cache: yum clean all (or apt-get clean)" + echo " • Clear user cache: rm -rf /home/*/.cache/*" + echo "" + press_enter +} + +analyze_by_user() { + clear + print_banner "Disk Usage by User/Domain" + echo "" + + local base_dir="$SYS_USER_HOME_BASE" + + echo "Analyzing $base_dir directory..." + echo "" + + show_progress "Calculating user disk usage" + du -sh "$base_dir"/* 2>/dev/null | sort -rh > "$TEMP_DIR/user_usage.txt" + + local count=$(wc -l < "$TEMP_DIR/user_usage.txt") + echo -e "\r${GREEN}✓${NC} Analysis complete ($count accounts) " + echo "" + + # Calculate total + local total_bytes=0 + while read -r size path; do + bytes=$(size_to_bytes "$size") + total_bytes=$((total_bytes + bytes)) + done < "$TEMP_DIR/user_usage.txt" + + echo -e "${BOLD}Total Usage: ${YELLOW}$(format_size $total_bytes)${NC}" + echo "" + + if [ "$SYS_CONTROL_PANEL" = "cPanel" ] || [ "$SYS_CONTROL_PANEL" = "InterWorx" ]; then + echo -e "${BOLD}Top 30 Users by Disk Usage:${NC}" + echo "───────────────────────────────────────────────────────────────" + + head -30 "$TEMP_DIR/user_usage.txt" | \ + while read -r size path; do + username=$(basename "$path") + + # Highlight users over 10GB + size_bytes=$(size_to_bytes "$size") + if [ "$size_bytes" -ge 10737418240 ]; then + echo -e " ${RED}$size${NC} $username" + elif [ "$size_bytes" -ge 5368709120 ]; then + echo -e " ${YELLOW}$size${NC} $username" + else + echo -e " ${CYAN}$size${NC} $username" + fi + done + + elif [ "$SYS_CONTROL_PANEL" = "Plesk" ]; then + echo -e "${BOLD}Top 30 Domains by Disk Usage:${NC}" + echo "───────────────────────────────────────────────────────────────" + + head -30 "$TEMP_DIR/user_usage.txt" | \ + while read -r size path; do + domain=$(basename "$path") + echo -e " ${CYAN}$size${NC} $domain" + done + else + echo -e "${BOLD}Top 30 Directories by Disk Usage:${NC}" + echo "───────────────────────────────────────────────────────────────" + + head -30 "$TEMP_DIR/user_usage.txt" | \ + while read -r size path; do + echo -e " ${CYAN}$size${NC} $path" + done + fi + + echo "" + press_enter +} + +analyze_package_cache() { + clear + print_banner "Package Manager Cache" + echo "" + + # Detect package manager + if command -v yum &>/dev/null; then + echo "Analyzing YUM/DNF cache..." + echo "" + + local cache_dir="/var/cache/yum" + [ -d "/var/cache/dnf" ] && cache_dir="/var/cache/dnf" + + if [ -d "$cache_dir" ]; then + local cache_size=$(du -sh "$cache_dir" 2>/dev/null | awk '{print $1}') + local pkg_count=$(find "$cache_dir" -name "*.rpm" 2>/dev/null | wc -l) + + echo -e " Cache location: $cache_dir" + echo -e " Cache size: ${YELLOW}$cache_size${NC}" + echo -e " Cached packages: $pkg_count" + echo "" + echo -e "${BOLD}Cleanup command:${NC}" + echo " yum clean all (or dnf clean all)" + fi + + elif command -v apt-get &>/dev/null; then + echo "Analyzing APT cache..." + echo "" + + local cache_dir="/var/cache/apt/archives" + if [ -d "$cache_dir" ]; then + local cache_size=$(du -sh "$cache_dir" 2>/dev/null | awk '{print $1}') + local count=$(find "$cache_dir" -name "*.deb" 2>/dev/null | wc -l) + echo -e " Cache location: $cache_dir" + echo -e " Cache size: ${YELLOW}$cache_size${NC}" + echo -e " Packages cached: $count" + echo "" + echo -e "${BOLD}Cleanup commands:${NC}" + echo " apt-get clean # Remove all cached packages" + echo " apt-get autoclean # Remove old cached packages" + fi + fi + + echo "" + press_enter +} + +generate_report() { + clear + print_banner "Generate Disk Usage Report" + echo "" + + local report_file="/root/disk-analysis-report-$(date +%Y%m%d-%H%M%S).txt" + + echo "Generating comprehensive disk usage report..." + echo "" + + { + echo "========================================================================" + echo "DISK SPACE ANALYSIS REPORT" + echo "Generated: $(date)" + echo "Server: $(hostname)" + echo "========================================================================" + echo "" + + echo "DISK OVERVIEW:" + echo "------------------------------------------------------------------------" + df -h | grep -E '^/dev/' + echo "" + + echo "INODE USAGE:" + echo "------------------------------------------------------------------------" + df -i | grep -E '^/dev/' + echo "" + + if [ -f "$TEMP_DIR/largest_dirs.txt" ]; then + echo "LARGEST DIRECTORIES:" + echo "------------------------------------------------------------------------" + head -20 "$TEMP_DIR/largest_dirs.txt" + echo "" + fi + + if [ -f "$TEMP_DIR/largest_files.txt" ]; then + echo "LARGEST FILES:" + echo "------------------------------------------------------------------------" + head -20 "$TEMP_DIR/largest_files.txt" + echo "" + fi + + if [ -f "$TEMP_DIR/user_usage.txt" ]; then + echo "USER/DOMAIN USAGE:" + echo "------------------------------------------------------------------------" + head -20 "$TEMP_DIR/user_usage.txt" + echo "" + fi + + echo "========================================================================" + echo "END OF REPORT" + echo "========================================================================" + + } > "$report_file" + + echo -e "${GREEN}✓${NC} Report generated: $report_file" + echo "" + echo "Report contains:" + echo " • Disk and inode usage" + echo " • Largest directories" + echo " • Largest files" + echo " • User/domain breakdown" + echo "" + + read -p "View report now? (y/n): " view + if [ "$view" = "y" ]; then + less "$report_file" + fi + + press_enter +} + +################################################################################ +# INTERACTIVE CLEANUP +################################################################################ + +interactive_cleanup_menu() { + while true; do + clear + print_banner "Interactive Cleanup" + echo "" + echo -e "${BOLD}Safe Cleanup Options:${NC}" + echo "" + echo -e " ${GREEN}1)${NC} Clear old log files (>30 days)" + echo -e " ${GREEN}2)${NC} Clear package manager cache" + echo -e " ${GREEN}3)${NC} Clear old temp files (>7 days)" + echo -e " ${GREEN}4)${NC} Clear old compressed logs (*.gz >30 days)" + echo -e " ${GREEN}5)${NC} Clear WordPress cache files" + echo -e " ${GREEN}6)${NC} Show cleanup summary (dry-run all)" + echo "" + echo -e " ${RED}0)${NC} Back" + echo "" + echo -e "${CYAN}──────────────────────────────────────────────────────────────${NC}" + read -p "Select option: " choice + + case "$choice" in + 1) cleanup_old_logs ;; + 2) cleanup_package_cache ;; + 3) cleanup_temp_files ;; + 4) cleanup_compressed_logs ;; + 5) cleanup_wordpress_cache ;; + 6) show_cleanup_summary ;; + 0) return ;; + *) echo -e "${RED}Invalid option${NC}"; sleep 1 ;; + esac + done +} + +cleanup_old_logs() { + clear + print_banner "Clear Old Log Files" + echo "" + echo "Finding log files older than 30 days..." + echo "" + + # Dry run first - show what will be deleted + local files=$(find /var/log -name "*.log" -mtime +30 -type f 2>/dev/null) + local count=$(echo "$files" | grep -v '^$' | wc -l) + + if [ "$count" -eq 0 ]; then + echo -e "${GREEN}No old log files found${NC}" + echo "" + press_enter + return + fi + + # Calculate size + local total_bytes=0 + while IFS= read -r file; do + [ -z "$file" ] && continue + size=$(stat -c%s "$file" 2>/dev/null || echo 0) + total_bytes=$((total_bytes + size)) + done <<< "$files" + + echo "Found $count log files totaling $(format_size $total_bytes)" + echo "" + echo "Preview (first 10 files):" + echo "$files" | head -10 | while read -r file; do + size=$(stat -c%s "$file" 2>/dev/null || echo 0) + echo -e " ${YELLOW}$(format_size $size)${NC} $file" + done + + echo "" + echo -e "${YELLOW}WARNING: This will permanently delete $count log files${NC}" + echo "" + read -p "Continue? (type 'yes' to confirm): " confirm + + if [ "$confirm" = "yes" ]; then + find /var/log -name "*.log" -mtime +30 -type f -delete 2>/dev/null + echo "" + echo -e "${GREEN}✓ Deleted $count old log files (freed $(format_size $total_bytes))${NC}" + else + echo "" + echo "Cancelled" + fi + + echo "" + press_enter +} + +cleanup_package_cache() { + clear + print_banner "Clear Package Cache" + echo "" + + if command -v yum &>/dev/null; then + echo "Clearing YUM/DNF cache..." + echo "" + yum clean all + elif command -v apt-get &>/dev/null; then + echo "Clearing APT cache..." + echo "" + apt-get clean + else + echo "No supported package manager found" + fi + + echo "" + echo -e "${GREEN}✓ Package cache cleared${NC}" + echo "" + press_enter +} + +cleanup_temp_files() { + clear + print_banner "Clear Old Temp Files" + echo "" + echo "Finding temp files older than 7 days..." + echo "" + + local files=$(find /tmp -type f -mtime +7 2>/dev/null) + local count=$(echo "$files" | grep -v '^$' | wc -l) + + if [ "$count" -eq 0 ]; then + echo -e "${GREEN}No old temp files found${NC}" + echo "" + press_enter + return + fi + + # Calculate size + local total_bytes=0 + while IFS= read -r file; do + [ -z "$file" ] && continue + size=$(stat -c%s "$file" 2>/dev/null || echo 0) + total_bytes=$((total_bytes + size)) + done <<< "$files" + + echo "Found $count temp files totaling $(format_size $total_bytes)" + echo "" + echo -e "${YELLOW}WARNING: This will delete temp files older than 7 days${NC}" + echo "" + read -p "Continue? (type 'yes' to confirm): " confirm + + if [ "$confirm" = "yes" ]; then + find /tmp -type f -mtime +7 -delete 2>/dev/null + echo "" + echo -e "${GREEN}✓ Deleted $count old temp files (freed $(format_size $total_bytes))${NC}" + else + echo "" + echo "Cancelled" + fi + + echo "" + press_enter +} + +cleanup_compressed_logs() { + clear + print_banner "Clear Old Compressed Logs" + echo "" + echo "Finding compressed logs older than 30 days..." + echo "" + + local files=$(find /var/log -name "*.gz" -mtime +30 -type f 2>/dev/null) + local count=$(echo "$files" | grep -v '^$' | wc -l) + + if [ "$count" -eq 0 ]; then + echo -e "${GREEN}No old compressed logs found${NC}" + echo "" + press_enter + return + fi + + # Calculate size + local total_bytes=0 + while IFS= read -r file; do + [ -z "$file" ] && continue + size=$(stat -c%s "$file" 2>/dev/null || echo 0) + total_bytes=$((total_bytes + size)) + done <<< "$files" + + echo "Found $count compressed log files totaling $(format_size $total_bytes)" + echo "" + echo -e "${YELLOW}WARNING: This will delete *.gz files older than 30 days${NC}" + echo "" + read -p "Continue? (type 'yes' to confirm): " confirm + + if [ "$confirm" = "yes" ]; then + find /var/log -name "*.gz" -mtime +30 -type f -delete 2>/dev/null + echo "" + echo -e "${GREEN}✓ Deleted $count compressed logs (freed $(format_size $total_bytes))${NC}" + else + echo "" + echo "Cancelled" + fi + + echo "" + press_enter +} + +cleanup_wordpress_cache() { + clear + print_banner "Clear WordPress Cache Files" + echo "" + + # Find WordPress cache directories + local wp_caches=() + + while IFS= read -r cache_dir; do + wp_caches+=("$cache_dir") + done < <(find /home -path "*/wp-content/cache" -type d 2>/dev/null) + + while IFS= read -r cache_dir; do + wp_caches+=("$cache_dir") + done < <(find /var/www -path "*/wp-content/cache" -type d 2>/dev/null) + + if [ ${#wp_caches[@]} -eq 0 ]; then + echo -e "${DIM}No WordPress cache directories found${NC}" + echo "" + press_enter + return + fi + + # Calculate total size + local total_bytes=0 + for cache_dir in "${wp_caches[@]}"; do + size=$(du -sb "$cache_dir" 2>/dev/null | awk '{print $1}') + total_bytes=$((total_bytes + size)) + done + + echo "Found ${#wp_caches[@]} WordPress cache directories" + echo "Total size: $(format_size $total_bytes)" + echo "" + + echo "Cache directories:" + for cache_dir in "${wp_caches[@]}"; do + size=$(du -sh "$cache_dir" 2>/dev/null | awk '{print $1}') + echo -e " ${CYAN}$size${NC} $cache_dir" + done + + echo "" + echo -e "${YELLOW}WARNING: This will clear all WordPress cache files${NC}" + echo "" + read -p "Continue? (type 'yes' to confirm): " confirm + + if [ "$confirm" = "yes" ]; then + for cache_dir in "${wp_caches[@]}"; do + rm -rf "$cache_dir"/* 2>/dev/null + done + echo "" + echo -e "${GREEN}✓ Cleared WordPress cache (freed $(format_size $total_bytes))${NC}" + else + echo "" + echo "Cancelled" + fi + + echo "" + press_enter +} + +show_cleanup_summary() { + clear + print_banner "Cleanup Summary (Dry Run)" + echo "" + echo "Calculating potential space savings..." + echo "" + + local total_bytes=0 + + # Old logs + show_progress "Checking old logs" + local old_logs_files=$(find /var/log -name "*.log" -mtime +30 -type f 2>/dev/null) + local old_logs=0 + while IFS= read -r file; do + [ -z "$file" ] && continue + size=$(stat -c%s "$file" 2>/dev/null || echo 0) + old_logs=$((old_logs + size)) + done <<< "$old_logs_files" + total_bytes=$((total_bytes + old_logs)) + local old_logs_count=$(echo "$old_logs_files" | grep -v '^$' | wc -l) + echo -e "\r Old logs (>30 days): ${CYAN}$(format_size $old_logs)${NC} ($old_logs_count files)" + + # Compressed logs + show_progress "Checking compressed logs" + local comp_logs_files=$(find /var/log -name "*.gz" -mtime +30 -type f 2>/dev/null) + local comp_logs=0 + while IFS= read -r file; do + [ -z "$file" ] && continue + size=$(stat -c%s "$file" 2>/dev/null || echo 0) + comp_logs=$((comp_logs + size)) + done <<< "$comp_logs_files" + total_bytes=$((total_bytes + comp_logs)) + local comp_logs_count=$(echo "$comp_logs_files" | grep -v '^$' | wc -l) + echo -e "\r Compressed logs (>30 days): ${CYAN}$(format_size $comp_logs)${NC} ($comp_logs_count files)" + + # Old temp + show_progress "Checking temp files" + local old_temp_files=$(find /tmp -type f -mtime +7 2>/dev/null) + local old_temp=0 + while IFS= read -r file; do + [ -z "$file" ] && continue + size=$(stat -c%s "$file" 2>/dev/null || echo 0) + old_temp=$((old_temp + size)) + done <<< "$old_temp_files" + total_bytes=$((total_bytes + old_temp)) + local old_temp_count=$(echo "$old_temp_files" | grep -v '^$' | wc -l) + echo -e "\r Old temp files (>7 days): ${CYAN}$(format_size $old_temp)${NC} ($old_temp_count files)" + + # Package cache + show_progress "Checking package cache" + local pkg_cache=0 + if [ -d "/var/cache/yum" ]; then + pkg_cache=$(du -sb /var/cache/yum 2>/dev/null | awk '{print $1}') + elif [ -d "/var/cache/dnf" ]; then + pkg_cache=$(du -sb /var/cache/dnf 2>/dev/null | awk '{print $1}') + elif [ -d "/var/cache/apt/archives" ]; then + pkg_cache=$(du -sb /var/cache/apt/archives 2>/dev/null | awk '{print $1}') + fi + total_bytes=$((total_bytes + pkg_cache)) + echo -e "\r Package cache: ${CYAN}$(format_size $pkg_cache)${NC}" + + echo "" + echo "───────────────────────────────────────────────────────────────" + echo -e "${BOLD}Total Potential Savings: ${GREEN}$(format_size $total_bytes)${NC}" + echo "" + + press_enter +} + +################################################################################ +# MAIN MENU +################################################################################ + +show_main_menu() { + clear + print_banner "Disk Space Analyzer" + echo "" + echo -e "${BOLD}Quick Analysis:${NC}" + echo "" + echo -e " ${GREEN}1)${NC} Disk Overview - Current disk usage summary" + echo -e " ${GREEN}2)${NC} Largest Directories - Find space-consuming directories" + echo -e " ${GREEN}3)${NC} Largest Files - Find individual large files" + echo -e " ${GREEN}4)${NC} Old Files - Files not accessed recently" + echo "" + echo -e "${BOLD}Specific Analysis:${NC}" + echo "" + echo -e " ${CYAN}5)${NC} Log Files - Analyze log file usage" + echo -e " ${CYAN}6)${NC} Email Storage - Mail directory analysis" + echo -e " ${CYAN}7)${NC} Database Storage - MySQL/PostgreSQL data" + echo -e " ${CYAN}8)${NC} Backup Files - Find old backups" + echo -e " ${CYAN}9)${NC} WordPress Sites - WP uploads/cache/plugins" + echo -e " ${CYAN}10)${NC} Temporary Files - /tmp and /var/tmp analysis" + echo -e " ${CYAN}11)${NC} User/Domain Usage - Breakdown by account" + echo -e " ${CYAN}12)${NC} Package Cache - Package manager cache size" + echo "" + echo -e "${BOLD}Actions:${NC}" + echo "" + echo -e " ${YELLOW}13)${NC} Interactive Cleanup - Safe cleanup wizard" + echo -e " ${YELLOW}14)${NC} Generate Report - Export analysis to file" + echo "" + echo -e " ${RED}0)${NC} Exit" + echo "" + echo -e "${CYAN}──────────────────────────────────────────────────────────────${NC}" + read -p "Select option: " choice +} + +################################################################################ +# MAIN LOOP +################################################################################ + +main() { + while true; do + show_main_menu + + case "$choice" in + 1) analyze_disk_overview ;; + 2) find_largest_directories "/" 2 ;; + 3) find_largest_files "/" "100M" ;; + 4) analyze_old_files "/home" 90 ;; + 5) analyze_log_files ;; + 6) analyze_email_storage ;; + 7) analyze_databases ;; + 8) analyze_backups ;; + 9) analyze_wordpress ;; + 10) analyze_temp_files ;; + 11) analyze_by_user ;; + 12) analyze_package_cache ;; + 13) interactive_cleanup_menu ;; + 14) generate_report ;; + 0) + echo "" + echo -e "${GREEN}Exiting...${NC}" + exit 0 + ;; + *) + echo -e "${RED}Invalid option${NC}" + sleep 1 + ;; + esac + done +} + +# Run main function +main