diff --git a/launcher.sh b/launcher.sh index 6ca139d..5f37f61 100755 --- a/launcher.sh +++ b/launcher.sh @@ -1,46 +1,27 @@ #!/bin/bash ############################################################################# -# Server Management Toolkit - BETA/DEV Version -# Version: 2.1-beta +# Server Management Toolkit - Main Launcher +# Version: 2.1 # -# Development and testing version - SEPARATE FROM PRODUCTION -# Uses independent cache, config, and data directories +# Streamlined menu showing only implemented features ############################################################################# set -eo pipefail -# Check if running in interactive mode -if [[ $- != *i* ]]; then - # Non-interactive mode - set flag for read operations - INTERACTIVE_MODE=0 -else - INTERACTIVE_MODE=1 -fi - # Configuration -SUITE_VERSION="2.1.0-BETA" +SUITE_VERSION="2.1.0" BASE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" MODULES_DIR="$BASE_DIR/modules" LIB_DIR="$BASE_DIR/lib" CONFIG_DIR="$BASE_DIR/config" # Load core libraries -source "$LIB_DIR/common-functions.sh" || { echo "ERROR: Failed to load common-functions.sh"; return 1; } -source "$LIB_DIR/system-detect.sh" || { echo "ERROR: Failed to load system-detect.sh"; return 1; } -source "$LIB_DIR/log-paths.sh" || { echo "ERROR: Failed to load log-paths.sh"; return 1; } -source "$LIB_DIR/database-paths.sh" || { echo "ERROR: Failed to load database-paths.sh"; return 1; } -source "$LIB_DIR/service-info.sh" || { echo "ERROR: Failed to load service-info.sh"; return 1; } -source "$LIB_DIR/control-panel-paths.sh" || { echo "ERROR: Failed to load control-panel-paths.sh"; return 1; } -source "$LIB_DIR/web-server-config.sh" || { echo "ERROR: Failed to load web-server-config.sh"; return 1; } -source "$LIB_DIR/firewall-operations.sh" || { echo "ERROR: Failed to load firewall-operations.sh"; return 1; } -source "$LIB_DIR/security-tools.sh" || { echo "ERROR: Failed to load security-tools.sh"; return 1; } -source "$LIB_DIR/system-authentication.sh" || { echo "ERROR: Failed to load system-authentication.sh"; return 1; } -source "$LIB_DIR/system-variables.sh" || { echo "ERROR: Failed to load system-variables.sh"; return 1; } -source "$LIB_DIR/domain-discovery.sh" || { echo "ERROR: Failed to load domain-discovery.sh"; return 1; } -source "$LIB_DIR/user-manager.sh" || { echo "ERROR: Failed to load user-manager.sh"; return 1; } -source "$LIB_DIR/reference-db.sh" || { echo "ERROR: Failed to load reference-db.sh"; return 1; } -source "$LIB_DIR/menu-functions.sh" || { echo "ERROR: Failed to load menu-functions.sh"; return 1; } +source "$LIB_DIR/common-functions.sh" +source "$LIB_DIR/system-detect.sh" +source "$LIB_DIR/domain-discovery.sh" +source "$LIB_DIR/user-manager.sh" +source "$LIB_DIR/reference-db.sh" # Color codes RED='\033[0;31m' @@ -55,12 +36,10 @@ NC='\033[0m' # Banner show_banner() { clear - echo "═══════════════════════════════════════════════════════════════" - echo " ⚠️ Server Management Toolkit v${SUITE_VERSION}" - echo " 🧪 BETA/DEV VERSION - Testing & Development" - echo " Complete cPanel/Linux Server Administration Suite" - echo "═══════════════════════════════════════════════════════════════" - echo " ⚠️ This is a SEPARATE INSTANCE from production" + echo -e "${CYAN}═══════════════════════════════════════════════════════════════${NC}" + echo -e "${CYAN} ⚡ Server Management Toolkit v${SUITE_VERSION}${NC}" + echo -e "${CYAN} Complete cPanel/Linux Server Administration Suite${NC}" + echo -e "${CYAN}═══════════════════════════════════════════════════════════════${NC}" echo "" } @@ -72,17 +51,15 @@ run_module() { if [ ! -f "$MODULES_DIR/$category/$module" ]; then echo "" - echo -e "✗ Module not found: $category/$module" + echo -e "${RED}✗ Module not found: $category/$module${NC}" echo "" - if ! read -p "Press Enter to continue..." /dev/null; then - true # Continue even if read fails - fi + read -p "Press Enter to continue..." < /dev/tty 2>/dev/null || true return 1 fi echo "" - echo -e "Launching: $category/$module" - echo -e "──────────────────────────────────────────────────────────────" + echo -e "${CYAN}Launching: $category/$module${NC}" + echo -e "${CYAN}──────────────────────────────────────────────────────────────${NC}" # Run module directly - keep SYS_ variables cached for performance # Modules will use cached detection instead of re-detecting on every run @@ -90,71 +67,14 @@ run_module() { local exit_code=$? echo "" - echo -e "──────────────────────────────────────────────────────────────" + echo -e "${CYAN}──────────────────────────────────────────────────────────────${NC}" if [ "${exit_code:-0}" -eq 0 ]; then - echo -e "✓ Completed successfully" + echo -e "${GREEN}✓ Completed successfully${NC}" else - echo -e "✗ Exited with code: $exit_code" + echo -e "${RED}✗ Exited with code: $exit_code${NC}" fi echo "" - if ! read -p "Press Enter to continue..." /dev/null; then - true # Continue even if read fails - fi -} - -############################################################################# -# SYSTEM INFO DISPLAY (Quick View) -############################################################################# - -show_system_overview() { - # Only show if detection is complete - if [ -z "${SYS_DETECTION_COMPLETE:-}" ]; then - return - fi - - echo "" - echo "🖥️ System Information:" - - # Control Panel - if [ "$SYS_CONTROL_PANEL" != "none" ]; then - echo -n " Control Panel: ${SYS_CONTROL_PANEL^^}" - [ -n "$SYS_CONTROL_PANEL_VERSION" ] && echo -n " v${SYS_CONTROL_PANEL_VERSION}" || echo -n " (version unknown)" - echo "" - else - echo " Control Panel: Standalone (no control panel)" - fi - - # OS - echo " OS: ${SYS_OS_TYPE^^} ${SYS_OS_VERSION}" - [ "${SYS_CLOUDLINUX:-}" = "yes" ] && echo " ➜ CloudLinux detected" - - # Web Server - echo -n " Web Server: ${SYS_WEB_SERVER^^}" - [ -n "$SYS_WEB_SERVER_VERSION" ] && echo " v${SYS_WEB_SERVER_VERSION}" || echo "" - - # Database - if [ "$SYS_DB_TYPE" != "none" ]; then - echo -n " Database: ${SYS_DB_TYPE^^}" - [ -n "$SYS_DB_VERSION" ] && echo " v${SYS_DB_VERSION}" || echo "" - fi - - # PHP Versions - if [ ${#SYS_PHP_VERSIONS[@]} -gt 0 ]; then - echo -n " PHP Versions: " - local php_list=$(printf '%s, ' "${SYS_PHP_VERSIONS[@]}") - echo "${php_list%, }" - fi - - # Firewall - if [ "$SYS_FIREWALL" != "none" ]; then - echo -n " Firewall: ${SYS_FIREWALL^^}" - [ "$SYS_FIREWALL_ACTIVE" = "yes" ] && echo " (active)" || echo " (inactive)" - fi - - # Cloudflare - [ "$SYS_CLOUDFLARE_ACTIVE" = "yes" ] && echo " Cloudflare: Detected" - - echo "" + read -p "Press Enter to continue..." < /dev/tty 2>/dev/null || true } ############################################################################# @@ -164,31 +84,26 @@ show_system_overview() { show_main_menu() { show_banner - # Show quick system overview if detection is complete - [ -n "${SYS_DETECTION_COMPLETE:-}" ] && show_system_overview - - menu_header "Server Management Toolkit" - - menu_section "Quick Diagnostics" - menu_option 1 "System Health Check" "Full server diagnostics" - + echo -e "${BOLD}Quick Diagnostics:${NC}" echo "" - menu_section "Main Categories" - menu_option 2 "Security & Monitoring" - menu_option 3 "Website Diagnostics" - menu_option 4 "Performance & Maintenance" - menu_option 5 "Backup & Recovery" - menu_option 6 "Email Troubleshooting" - + echo -e " ${MAGENTA}1)${NC} 🏥 System Health Check - Full server diagnostics" echo "" - menu_section "System" - menu_option 7 "Cleanup Toolkit Data" "Clear cached data" - + echo -e "${BOLD}Main Categories:${NC}" echo "" - menu_exit - menu_divider - - read_menu_choice "Select option" 0 7 + echo -e " ${GREEN}2)${NC} 🛡️ Security & Monitoring" + echo -e " ${BLUE}3)${NC} 🌐 Website Diagnostics" + echo -e " ${MAGENTA}4)${NC} 🔧 Performance & Maintenance" + echo -e " ${YELLOW}5)${NC} 💾 Backup & Recovery" + echo -e " ${CYAN}6)${NC} 📧 Email Troubleshooting" + echo "" + echo -e "${BOLD}System:${NC}" + echo "" + echo -e " ${YELLOW}7)${NC} 🗑️ Cleanup Toolkit Data - Clear cached data" + echo "" + echo -e " ${RED}0)${NC} Exit" + echo "" + echo -e "${CYAN}═══════════════════════════════════════════════════════════════${NC}" + echo -n "Select option: " } ############################################################################# @@ -202,31 +117,29 @@ show_main_menu() { # Threat Analysis Sub-Menu show_threat_analysis_menu() { show_banner - menu_header "Threat Analysis" - - menu_option 1 "Bot & Traffic Analyzer" "Full analysis (all logs)" - menu_option 2 "Quick Scan (1 hour)" "Recent activity only" - menu_option 3 "IP Reputation Manager" "Query/manage IP database" - menu_option 4 "Suspicious Login Monitor" "SSH/Panel login analysis" - menu_option 5 "Malware Scanner" "ImunifyAV, ClamAV, Maldet" - menu_option 6 "Historical Attack Analysis" "Scan past logs (ET Open)" - + echo -e "${GREEN}${BOLD}📊 Threat Analysis${NC}" echo "" - menu_back "Security Menu" - menu_divider - - read_menu_choice "Select option" 0 6 + echo -e " ${CYAN}1)${NC} 🤖 Bot & Traffic Analyzer - Full analysis (all logs)" + echo -e " ${CYAN}2)${NC} 🤖 Quick Scan (1 hour) - Recent activity only" + echo -e " ${CYAN}3)${NC} 📊 IP Reputation Manager - Query/manage IP database" + echo -e " ${CYAN}4)${NC} 🔐 Suspicious Login Monitor - SSH/Panel login analysis" + echo -e " ${CYAN}5)${NC} 🦠 Malware Scanner - ImunifyAV, ClamAV, Maldet" + echo -e " ${CYAN}6)${NC} 🛡️ Historical Attack Analysis - Scan past logs (ET Open)" + echo "" + echo -e " ${RED}0)${NC} Back to Security Menu" + echo "" + echo -e "${CYAN}──────────────────────────────────────────────────────────────${NC}" + echo -n "Select option: " } handle_threat_analysis_menu() { - if [ "$INTERACTIVE_MODE" -eq 0 ]; then - return 0 # Non-interactive mode, exit - fi - while true; do show_threat_analysis_menu + if ! read -r choice 2>/dev/null /dev/null /dev/null /dev/null /dev/null /dev/null /dev/null /dev/null /dev/null /dev/null /dev/null || true - touch "$CONFIG_DIR/whitelist-user-agents.txt" 2>/dev/null || true + mkdir -p "$MODULES_DIR"/{security,website,performance,backup,diagnostics,maintenance,email} + mkdir -p "$LIB_DIR" "$CONFIG_DIR" "$BASE_DIR/logs" + touch "$CONFIG_DIR/whitelist-ips.txt" 2>/dev/null + touch "$CONFIG_DIR/whitelist-user-agents.txt" 2>/dev/null } startup_detection() { - # Auto-clear cache if toolkit files are newer (fresh git pull) - # This ensures users always get fresh data after git updates - if [ -f "$BASE_DIR/.sysref.beta" ] && [ -f "$BASE_DIR/launcher.sh" ]; then - if [ "$BASE_DIR/launcher.sh" -nt "$BASE_DIR/.sysref.beta" ]; then - rm -f "$BASE_DIR/.sysref.beta" "$BASE_DIR/.sysref.beta.timestamp" 2>/dev/null || true - fi - fi - - # Also check production cache name for backward compatibility - if [ -f "$BASE_DIR/.sysref" ] && [ -f "$BASE_DIR/launcher.sh" ]; then - if [ "$BASE_DIR/launcher.sh" -nt "$BASE_DIR/.sysref" ]; then - rm -f "$BASE_DIR/.sysref" "$BASE_DIR/.sysref.timestamp" 2>/dev/null || true - fi - fi - - # Initialize system detection first (required for show_system_overview) + # Initialize system detection first (required for proper reference database) if [ -z "${SYS_DETECTION_COMPLETE:-}" ]; then initialize_system_detection fi @@ -731,18 +611,19 @@ startup_detection() { print_section "Detection Summary" echo "" - echo -e "System:" + echo -e "${BOLD}System:${NC}" echo " Control Panel: $SYS_CONTROL_PANEL $SYS_CONTROL_PANEL_VERSION" echo " OS: $SYS_OS_TYPE $SYS_OS_VERSION" echo " Web Server: $SYS_WEB_SERVER $SYS_WEB_SERVER_VERSION" echo " Database: $SYS_DB_TYPE $SYS_DB_VERSION" echo "" - # Count records in database with single awk pass (instead of 4 separate grep -c calls) - local counts=$(awk -F'|' '{a[$1]++} END {printf "%d %d %d %d", a["USER"]+0, a["DOMAIN"]+0, a["DB"]+0, a["WP"]+0}' "$SYSREF_DB" 2>/dev/null || echo "0 0 0 0") - read -r user_count domain_count db_count wp_count <<< "$counts" + local user_count=$(grep -c "^USER|" "$SYSREF_DB" 2>/dev/null || echo 0) + local domain_count=$(grep -c "^DOMAIN|" "$SYSREF_DB" 2>/dev/null || echo 0) + local db_count=$(grep -c "^DB|" "$SYSREF_DB" 2>/dev/null || echo 0) + local wp_count=$(grep -c "^WP|" "$SYSREF_DB" 2>/dev/null || echo 0) - echo -e "Server Content:" + echo -e "${BOLD}Server Content:${NC}" echo " Users: $user_count" echo " Domains: $domain_count" echo " Databases: $db_count" @@ -752,17 +633,7 @@ startup_detection() { print_success "Detection complete! Cached for 1 hour." echo "" - # Read from terminal (use /dev/tty directly) - if ! read -p "Press Enter to continue..." /dev/null; then - true # Continue even if read fails - fi - else - # Database is cached and fresh, but still ensure detection was completed - # (this addresses issue where detection output not shown on cached runs) - if [ -z "${SYS_DETECTION_COMPLETE:-}" ]; then - print_error "System detection failed - please check system configuration" - return 1 - fi + read -p "Press Enter to continue..." < /dev/tty 2>/dev/null || true fi } @@ -771,117 +642,19 @@ startup_detection() { ############################################################################# main() { - # Handle command-line arguments - case "${1:-}" in - --detect-only|--check-detection) - # Initialize directories - init_directories || { - echo "ERROR: Failed to initialize directories" - return 1 - } - - # Force fresh detection regardless of cache - echo "Forcing system re-detection..." - echo "" - rm -f "$BASE_DIR/.sysref.beta" "$BASE_DIR/.sysref.beta.timestamp" 2>/dev/null || true - - # Run detection - initialize_system_detection - - # Show results - echo "" - echo "═══════════════════════════════════════════════════════════════" - echo " DETECTION RESULTS" - echo "═══════════════════════════════════════════════════════════════" - echo "" - echo "Control Panel: ${SYS_CONTROL_PANEL:-unknown} ${SYS_CONTROL_PANEL_VERSION:-}" - echo "Operating System: ${SYS_OS_TYPE:-unknown} ${SYS_OS_VERSION:-}" - echo "Web Server: ${SYS_WEB_SERVER:-unknown} ${SYS_WEB_SERVER_VERSION:-}" - echo "Database: ${SYS_DB_TYPE:-unknown} ${SYS_DB_VERSION:-}" - echo "Firewall: ${SYS_FIREWALL:-unknown} ${SYS_FIREWALL_VERSION:-} (${SYS_FIREWALL_ACTIVE:-unknown})" - echo "PHP Versions: ${SYS_PHP_VERSIONS[*]:-none detected}" - echo "" - echo "═══════════════════════════════════════════════════════════════" - return 0 - ;; - --clear-cache) - # Initialize directories first - init_directories || { - echo "ERROR: Failed to initialize directories" - return 1 - } - - # Clear all caches - local cache_cleared=0 - - # Production cache - if [ -f "$BASE_DIR/.sysref" ] || [ -f "$BASE_DIR/.sysref.timestamp" ]; then - rm -f "$BASE_DIR/.sysref" "$BASE_DIR/.sysref.timestamp" 2>/dev/null || true - echo "✓ Cleared production cache (.sysref)" - cache_cleared=1 - fi - - # Dev cache - if [ -f "$BASE_DIR/.sysref.beta" ] || [ -f "$BASE_DIR/.sysref.beta.timestamp" ]; then - rm -f "$BASE_DIR/.sysref.beta" "$BASE_DIR/.sysref.beta.timestamp" 2>/dev/null || true - echo "✓ Cleared dev cache (.sysref.beta)" - cache_cleared=1 - fi - - # Temp files - if [ -d "$BASE_DIR/tmp" ]; then - rm -rf "$BASE_DIR/tmp"/* 2>/dev/null || true - echo "✓ Cleared temporary files" - cache_cleared=1 - fi - - if [ $cache_cleared -eq 0 ]; then - echo "ℹ️ No cache files to clear" - else - echo "" - echo "Cache cleared successfully!" - echo "System will auto-detect and rebuild cache on next run." - fi - return 0 - ;; - --help|--usage|-h|-?) - echo "Usage: launcher.sh [OPTIONS]" - echo "" - echo "Options:" - echo " --detect-only Show system detection results and exit" - echo " --clear-cache Clear all cache and temporary files" - echo " --help Show this help message" - echo "" - echo "Examples:" - echo " bash launcher.sh --detect-only # Check what was detected" - echo " bash launcher.sh --clear-cache # Clear stale cache data" - echo " bash launcher.sh # Normal interactive mode" - echo "" - return 0 - ;; - esac - - # Initialize directories once at startup - init_directories || { - echo "ERROR: Failed to initialize directories" - return 1 - } - - # Detect system configuration (builds database if cache expired) - startup_detection || true + init_directories + startup_detection while true; do show_main_menu - # Check if interactive mode - if [ "$INTERACTIVE_MODE" -eq 0 ]; then - echo "" - echo "Non-interactive mode: Use this toolkit in an interactive terminal." - echo "Try: source run.sh" + # Read from terminal (use /dev/tty directly for interaction) + if ! read -r choice 2>/dev/null &2 - if ! read -r clean_hist /dev/null; then - # Exit if read fails - just assume no cleanup - echo "" - echo "Thanks for using Server Management Toolkit!" - echo "" - return 0 - fi + read -p "Clean history and remove traces? (yes/no): " clean_hist if [ "$clean_hist" = "yes" ]; then - touch /tmp/.cleanup_requested 2>/dev/null || true + touch /tmp/.cleanup_requested echo "" echo "Cleanup will happen automatically..." echo "" else echo "" - echo "Thanks for using Server Management Toolkit!" + echo -e "${GREEN}Thanks for using Server Management Toolkit!${NC}" echo "" fi - return 0 + exit 0 ;; *) - menu_invalid_choice + echo -e "${RED}Invalid option${NC}" + sleep 1 ;; esac done diff --git a/lib/common-functions.sh b/lib/common-functions.sh index 8747604..dda7563 100755 --- a/lib/common-functions.sh +++ b/lib/common-functions.sh @@ -5,12 +5,6 @@ # Shared utilities for all Server Management Toolkit modules ############################################################################# -# Source guard - prevent re-sourcing -if [ -n "${_COMMON_FUNCTIONS_LOADED:-}" ]; then - return 0 -fi -readonly _COMMON_FUNCTIONS_LOADED=1 - ############################################################################# # Professional Color Scheme # - Uses ONLY basic ANSI colors (works on ANY terminal) diff --git a/lib/reference-db.sh b/lib/reference-db.sh index 3b923d7..97a0cfe 100755 --- a/lib/reference-db.sh +++ b/lib/reference-db.sh @@ -6,12 +6,6 @@ # Format: Pipe-delimited structured data ############################################################################# -# Source guard - prevent re-sourcing -if [ -n "${_REFERENCE_DB_LOADED:-}" ]; then - return 0 -fi -readonly _REFERENCE_DB_LOADED=1 - # Source dependencies if [ -z "$TOOLKIT_BASE_DIR" ]; then SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" @@ -21,34 +15,9 @@ if [ -z "$TOOLKIT_BASE_DIR" ]; then [ -f "$SCRIPT_DIR/user-manager.sh" ] && source "$SCRIPT_DIR/user-manager.sh" || { echo "ERROR: user-manager.sh not found" >&2; return 1; } fi -# Reference database location - BETA VERSION (separate from production) -export SYSREF_DB="${TOOLKIT_BASE_DIR}/.sysref.beta" -export SYSREF_TIMESTAMP="${TOOLKIT_BASE_DIR}/.sysref.beta.timestamp" - -# Timeout for domain HTTP checks -export DOMAIN_CHECK_TIMEOUT=${DOMAIN_CHECK_TIMEOUT:-3} - -############################################################################# -# URL Encoding Helper -############################################################################# - -# URL encode a string for safe use in curl requests -url_encode() { - local string="${1:-}" - local strlen=${#string} - local encoded="" - local pos c o - - for (( pos=0 ; pos> "$SYSREF_DB" - # Safely populate users array from function output - local users=() - while IFS= read -r user; do - [ -z "$user" ] && continue - users+=("$user") - done < <(list_all_users) - + local users=($(list_all_users)) local total_users=${#users[@]} local current=0 @@ -169,19 +133,15 @@ build_users_section() { current=$((current + 1)) show_progress $current $total_users "Indexing users..." - # Get all domains once and reuse (avoid duplicate function calls) - local user_all_domains=$(get_user_domains "$user") - local primary_domain=$(echo "$user_all_domains" | head -1) - # Use || echo 0 to handle grep failure with set -eo pipefail (when no domains exist) - local domain_count=$(echo "$user_all_domains" | grep -v "^$" | wc -l || echo 0) - local db_count=$(get_user_databases "$user" | grep -v "^$" | wc -l || echo 0) + local primary_domain=$(get_user_domains "$user" | head -1) + local domain_count=$(get_user_domains "$user" | grep -v "^$" | wc -l) + local db_count=$(get_user_databases "$user" | grep -v "^$" | wc -l) # Get disk usage (quick du) - # Use || echo "" to handle grep failure with set -eo pipefail - local home_dir=$(get_user_info "$user" | grep "^HOME_DIR=" | cut -d= -f2 || echo "") + local home_dir=$(get_user_info "$user" | grep "^HOME_DIR=" | cut -d= -f2) local disk_mb=0 if [ -n "$home_dir" ] && [ -d "$home_dir" ]; then - disk_mb=$(du -sm "$home_dir" 2>/dev/null | awk '{print $1}' || echo 0) + disk_mb=$(du -sm "$home_dir" 2>/dev/null | awk '{print $1}') fi echo "USER|$user|$primary_domain|$db_count|$domain_count|$disk_mb|$home_dir" >> "$SYSREF_DB" @@ -201,31 +161,15 @@ build_databases_section() { # Build MySQL command with credentials if needed local mysql_cmd="mysql" - local plesk_password="" if [ "$SYS_CONTROL_PANEL" = "plesk" ] && [ -f /etc/psa/.psa.shadow ]; then - plesk_password=$(cat /etc/psa/.psa.shadow) - # DO NOT export password - keep it in variable only + export MYSQL_PWD=$(cat /etc/psa/.psa.shadow) + mysql_cmd="mysql -uadmin" fi - # Query databases - set MYSQL_PWD only for this command - local total_dbs - if [ -n "$plesk_password" ]; then - # Use || echo 0 to handle grep failure (when all databases are system databases) - total_dbs=$(MYSQL_PWD="$plesk_password" mysql -u admin -Ns -e "SHOW DATABASES" 2>/dev/null | grep -v "^information_schema$\|^mysql$\|^performance_schema$\|^sys$" | wc -l || echo 0) - else - total_dbs=$(mysql -Ns -e "SHOW DATABASES" 2>/dev/null | grep -v "^information_schema$\|^mysql$\|^performance_schema$\|^sys$" | wc -l || echo 0) - fi + local total_dbs=$($mysql_cmd -Ns -e "SHOW DATABASES" 2>/dev/null | grep -v "^information_schema$\|^mysql$\|^performance_schema$\|^sys$" | wc -l) local current=0 # Use process substitution instead of pipe to avoid subshell shadowing (fixes current variable loss) - # Get database list - set MYSQL_PWD only for this command - local databases - if [ -n "$plesk_password" ]; then - databases=$(MYSQL_PWD="$plesk_password" mysql -u admin -Ns -e "SHOW DATABASES" 2>/dev/null | grep -v "^information_schema$\|^mysql$\|^performance_schema$\|^sys$" || echo "") - else - databases=$(mysql -Ns -e "SHOW DATABASES" 2>/dev/null | grep -v "^information_schema$\|^mysql$\|^performance_schema$\|^sys$" || echo "") - fi - while IFS= read -r db; do [ -z "$db" ] && continue current=$((current + 1)) @@ -234,35 +178,21 @@ build_databases_section() { local owner=$(get_database_owner "$db") local domain=$(get_database_domain "$db") - # Escape single quotes in database name for SQL safety - local db_escaped="${db//\'/\'\'}" - - # Query database size - set MYSQL_PWD only for this command - local size_mb - if [ -n "$plesk_password" ]; then - size_mb=$(MYSQL_PWD="$plesk_password" mysql -u admin -Ns -e "SELECT ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) - FROM information_schema.TABLES - WHERE table_schema='$db_escaped'" 2>/dev/null) - else - size_mb=$(mysql -Ns -e "SELECT ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) - FROM information_schema.TABLES - WHERE table_schema='$db_escaped'" 2>/dev/null) - fi + local size_mb=$($mysql_cmd -Ns -e "SELECT ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) + FROM information_schema.TABLES + WHERE table_schema=\`$db\`" 2>/dev/null) [ -z "$size_mb" ] && size_mb=0 - # Query table count - set MYSQL_PWD only for this command - local table_count - if [ -n "$plesk_password" ]; then - table_count=$(MYSQL_PWD="$plesk_password" mysql -u admin -Ns "$db" -e "SHOW TABLES" 2>/dev/null | wc -l) - else - table_count=$(mysql -Ns "$db" -e "SHOW TABLES" 2>/dev/null | wc -l) - fi + local table_count=$($mysql_cmd -Ns "$db" -e "SHOW TABLES" 2>/dev/null | wc -l) echo "DB|$db|$owner|$domain|$size_mb|$table_count" >> "$SYSREF_DB" - done <<< "$databases" + done < <($mysql_cmd -Ns -e "SHOW DATABASES" 2>/dev/null | grep -v "^information_schema$\|^mysql$\|^performance_schema$\|^sys$") finish_progress echo "" >> "$SYSREF_DB" + + # Clean up password environment variable + unset MYSQL_PWD } # Check domain HTTP/HTTPS status codes @@ -285,17 +215,14 @@ check_domain_status() { return 0 fi - # URL encode domain for safe curl request (handles special characters) - local encoded_domain=$(url_encode "$domain") - - # Try HTTP (with configurable timeout, max 2 redirects) - http_code=$(timeout "$DOMAIN_CHECK_TIMEOUT" curl -s -o /dev/null -w "%{http_code}" --max-redirs 2 -m "$DOMAIN_CHECK_TIMEOUT" "http://$encoded_domain" 2>/dev/null) + # Try HTTP (timeout 3 seconds, max 2 redirects, check for valid response) + http_code=$(timeout 3 curl -s -o /dev/null -w "%{http_code}" --max-redirs 2 -m 3 "http://$domain" 2>/dev/null) if [ $? -ne 0 ] || [ -z "$http_code" ]; then http_code="timeout" fi - # Try HTTPS (with configurable timeout, max 2 redirects, ignore cert errors) - https_code=$(timeout "$DOMAIN_CHECK_TIMEOUT" curl -s -o /dev/null -w "%{http_code}" --max-redirs 2 -m "$DOMAIN_CHECK_TIMEOUT" -k "https://$encoded_domain" 2>/dev/null) + # Try HTTPS (timeout 3 seconds, max 2 redirects, ignore cert errors) + https_code=$(timeout 3 curl -s -o /dev/null -w "%{http_code}" --max-redirs 2 -m 3 -k "https://$domain" 2>/dev/null) if [ $? -ne 0 ] || [ -z "$https_code" ]; then https_code="timeout" fi @@ -381,7 +308,7 @@ build_domains_section() { domain_type="primary" elif [[ "$domain" =~ \. ]] && [[ "$domain" =~ ^[^.]+\. ]]; then # Check if it's a subdomain of the primary - local base_domain=$(echo "$domain" | rev | cut -d. -f1-2 | rev || echo "$domain") + local base_domain=$(echo "$domain" | rev | cut -d. -f1-2 | rev) if [ "$base_domain" = "$primary_domain" ]; then domain_type="subdomain" fi @@ -406,32 +333,27 @@ build_domains_section() { # Also add aliases as separate entries if [ -n "$server_alias" ]; then # Convert space-separated aliases to newline-separated for safe iteration - # Use here-document instead of pipe to avoid subshell - while IFS= read -r alias; do + echo "$server_alias" | tr ' ' '\n' | while IFS= read -r alias; do [ -z "$alias" ] && continue [ -n "${seen_domains[$alias]:-}" ] && continue # Alias points to same document root and logs (inherit status from parent) echo "DOMAIN|$alias|$user|$doc_root|$log_path|$php_version|no|alias|$domain|$http_code|$https_code|alias_of_$status_summary" >> "$SYSREF_DB" seen_domains["$alias"]=1 - done <<< "$(echo "$server_alias" | tr ' ' '\n')" + done fi done else # Fallback for non-cPanel or if userdata not available - local user_domains=$(get_user_domains "$user") - local primary_domain=$(echo "$user_domains" | head -1) + local primary_domain=$(get_user_domains "$user" | head -1) - # Use here-document instead of pipe to avoid subshell (allows seen_domains updates to persist) - while IFS= read -r domain; do + # Use while read to safely iterate over domains (handles spaces) + get_user_domains "$user" | while IFS= read -r domain; do [ -z "$domain" ] && continue [ -n "${seen_domains[$domain]:-}" ] && continue local is_primary="no" - # Only mark as primary if primary_domain is not empty AND matches - if [ -n "$primary_domain" ] && [ "$domain" = "$primary_domain" ]; then - is_primary="yes" - fi + [ "$domain" = "$primary_domain" ] && is_primary="yes" # Find log path local log_path="${SYS_LOG_DIR}/${domain}" @@ -446,7 +368,7 @@ build_domains_section() { # Simple format for non-cPanel (with status codes) echo "DOMAIN|$domain|$user||$log_path||$is_primary|local||$http_code|$https_code|$status_summary" >> "$SYSREF_DB" seen_domains["$domain"]=1 - done <<< "$user_domains" + done fi done @@ -501,7 +423,7 @@ build_wordpress_section() { local username=$(echo "$wp_dir" | cut -d'/' -f3) # Try to get domain from path - check if it's in a subdomain or addon domain folder - local path_after_home=$(echo "$wp_dir" | sed "s|^/home/$username/||" || echo "$wp_dir") + local path_after_home=$(echo "$wp_dir" | sed "s|^/home/$username/||") local domain="" # Check for common domain folder patterns @@ -558,41 +480,9 @@ build_wordpress_section() { build_logs_section() { echo "[LOGS]" >> "$SYSREF_DB" - # Control panel-specific log discovery - case "$SYS_CONTROL_PANEL" in - cpanel) - # cPanel access and error logs - find "$SYS_LOG_DIR" -name "*.log" -o -name "access_log" -o -name "error_log" 2>/dev/null | \ - head -100 | while IFS= read -r logfile; do - echo "LOG|file|$logfile|" >> "$SYSREF_DB" - done - ;; - *) - # Standalone server - find Apache/Nginx logs safely - # Limit to recent logs and prevent hangs with large directories - if [ -d "$SYS_LOG_DIR" ]; then - # Apache access logs (with safety limits) - find "$SYS_LOG_DIR" -maxdepth 2 \( -name "*access*" -o -name "*access_log*" \) -type f -mtime -30 2>/dev/null | \ - head -50 | while IFS= read -r logfile; do - [ -n "$logfile" ] && echo "LOG|access|$logfile|" >> "$SYSREF_DB" - done - - # Apache error logs (with safety limits) - find "$SYS_LOG_DIR" -maxdepth 2 \( -name "*error*" -o -name "*error_log*" \) -type f -mtime -30 2>/dev/null | \ - head -50 | while IFS= read -r logfile; do - [ -n "$logfile" ] && echo "LOG|error|$logfile|" >> "$SYSREF_DB" - done - fi - - # Nginx logs for standalone - if [ -d "/var/log/nginx" ]; then - find /var/log/nginx -maxdepth 1 -type f -mtime -30 2>/dev/null | \ - head -20 | while IFS= read -r logfile; do - [ -n "$logfile" ] && echo "LOG|nginx|$logfile|" >> "$SYSREF_DB" - done - fi - ;; - esac + # Apache/Web server logs + # Temporarily disabled - causes hangs with large log directories + # TODO: Implement log scanning with progress indicator and limits echo "" >> "$SYSREF_DB" } @@ -814,7 +704,7 @@ get_domain_status() { fi # Get domain record (DOMAIN|domain|owner|doc_root|log_path|php|primary|type|alias|http|https|status) - local record=$(grep "^DOMAIN|${domain}|" "$SYSREF_DB" 2>/dev/null | head -1 || true) + local record=$(grep "^DOMAIN|${domain}|" "$SYSREF_DB" 2>/dev/null | head -1) if [ -z "$record" ]; then return 1