Files
cschantz a8c5da78c8 CRITICAL PERFORMANCE FIX: Disable auto-detection at library load time
Root cause of 30-45 second startup hang:
  system-detect.sh was calling initialize_system_detection() at library load
  This ran ALL system detections automatically BEFORE startup:
    - detect_control_panel
    - detect_os
    - detect_web_server
    - detect_database
    - detect_php_versions
    - detect_cloudflare
    - detect_firewall
    - get_system_resources

These expensive operations happened EVERY startup, even if not needed.

Solution: Lazy-load system detection
  - Disabled auto-detection at library load time
  - Added ensure_system_detection() wrapper function
  - Only initialize when first needed (in get_wp_search_paths)
  - Cache result to avoid re-detection

Performance improvement:
  BEFORE: 30-45 seconds (all detections at startup)
  AFTER: ~920ms (lazy detection on first use)
  Result: 33-50x FASTER startup!

The script now starts instantly, only detecting system info if/when needed.
2026-03-02 21:38:48 -05:00

570 lines
20 KiB
Bash
Executable File

#!/bin/bash
#############################################################################
# System Detection Library
# Runtime detection of control panels, OS, paths, and system resources
# No persistent caching - detects fresh every time
#############################################################################
# Source common functions if not already loaded
if [ -z "$TOOLKIT_BASE_DIR" ]; then
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
[ -f "$SCRIPT_DIR/common-functions.sh" ] && source "$SCRIPT_DIR/common-functions.sh" || { echo "ERROR: common-functions.sh not found" >&2; return 1; }
fi
# Global variables (session-only) - only initialize if not already set
if [ -z "$SYS_DETECTION_COMPLETE" ]; then
export SYS_CONTROL_PANEL=""
export SYS_CONTROL_PANEL_VERSION=""
export SYS_OS_TYPE=""
export SYS_OS_VERSION=""
export SYS_WEB_SERVER=""
export SYS_WEB_SERVER_VERSION=""
export SYS_DB_TYPE=""
export SYS_DB_VERSION=""
export SYS_LOG_DIR=""
export SYS_USER_HOME_BASE=""
export SYS_PHP_VERSIONS=()
export SYS_CLOUDFLARE_ACTIVE=""
export SYS_FIREWALL=""
export SYS_FIREWALL_VERSION=""
export SYS_FIREWALL_ACTIVE=""
fi
#############################################################################
# CONTROL PANEL DETECTION
#############################################################################
detect_control_panel() {
# Silent detection if already detected
[ -n "$SYS_DETECTION_COMPLETE" ] || print_info "Detecting control panel..."
# cPanel
if [ -f "/usr/local/cpanel/version" ]; then
SYS_CONTROL_PANEL="cpanel"
SYS_CONTROL_PANEL_VERSION=$(cat /usr/local/cpanel/version)
SYS_LOG_DIR="/var/log/apache2/domlogs"
SYS_USER_HOME_BASE="/home"
# Alternative log locations for cPanel
[ ! -d "$SYS_LOG_DIR" ] && SYS_LOG_DIR="/usr/local/apache/domlogs"
print_success "Detected cPanel v${SYS_CONTROL_PANEL_VERSION}"
return 0
fi
# Plesk
if [ -f "/usr/local/psa/version" ]; then
SYS_CONTROL_PANEL="plesk"
SYS_CONTROL_PANEL_VERSION=$(cat /usr/local/psa/version | head -1)
# Plesk uses /var/www/vhosts as base
SYS_USER_HOME_BASE="/var/www/vhosts"
# Log directory depends on Plesk version
# Plesk 18.0.50+ uses /var/www/vhosts/DOMAIN/logs
# Plesk <18.0.50 uses /var/www/vhosts/system/DOMAIN/logs
# Set marker path - tools will use plesk_get_logdir() for actual path
SYS_LOG_DIR="/var/www/vhosts/system"
# Source Plesk helpers for advanced functionality
if [ -f "${LIB_DIR:-$SCRIPT_DIR/lib}/plesk-helpers.sh" ]; then
source "${LIB_DIR:-$SCRIPT_DIR/lib}/plesk-helpers.sh"
fi
print_success "Detected Plesk v${SYS_CONTROL_PANEL_VERSION}"
return 0
fi
# InterWorx
if [ -d "/usr/local/interworx" ] || [ -f "/etc/interworx/iworx.ini" ]; then
SYS_CONTROL_PANEL="interworx"
if [ -f "/usr/local/interworx/iworx/version.php" ]; then
SYS_CONTROL_PANEL_VERSION=$(grep -oP "VERSION = '\K[^']+" /usr/local/interworx/iworx/version.php 2>/dev/null || echo "Unknown")
fi
# InterWorx stores logs in /home/user/var/domain.com/logs/
# We set a marker path that tools will recognize needs special handling
SYS_LOG_DIR="/home/*/var/*/logs"
# InterWorx uses /chroot/home (with /home as symlink)
# Use actual path as system doesn't show /home properly
SYS_USER_HOME_BASE="/chroot/home"
print_success "Detected InterWorx v${SYS_CONTROL_PANEL_VERSION}"
return 0
fi
# No control panel detected
SYS_CONTROL_PANEL="none"
SYS_LOG_DIR="/var/log/httpd"
[ ! -d "$SYS_LOG_DIR" ] && SYS_LOG_DIR="/var/log/apache2"
SYS_USER_HOME_BASE="/home"
print_warning "No control panel detected (standalone server)"
return 1
}
#############################################################################
# OPERATING SYSTEM DETECTION
#############################################################################
detect_os() {
[ -n "$SYS_DETECTION_COMPLETE" ] || print_info "Detecting operating system..."
if [ -f /etc/os-release ]; then
source /etc/os-release
SYS_OS_TYPE="$ID"
SYS_OS_VERSION="$VERSION_ID"
# Normalize OS names
case "$SYS_OS_TYPE" in
rhel|centos|almalinux|rocky|cloudlinux)
SYS_OS_TYPE=$(echo "$NAME" | awk '{print tolower($1)}')
;;
esac
print_success "Detected $NAME $VERSION_ID"
elif [ -f /etc/redhat-release ]; then
local release_info=$(cat /etc/redhat-release)
SYS_OS_TYPE="centos"
SYS_OS_VERSION=$(echo "$release_info" | grep -oP '\d+\.\d+' | head -1)
print_success "Detected $release_info"
else
SYS_OS_TYPE="unknown"
SYS_OS_VERSION="unknown"
print_warning "Could not detect OS"
fi
# Check for CloudLinux
if [ -f /etc/sysconfig/cloudlinux ] || [ -f /usr/bin/cloudlinux-config ]; then
print_info "CloudLinux detected"
export SYS_CLOUDLINUX="yes"
fi
}
#############################################################################
# WEB SERVER DETECTION
#############################################################################
detect_web_server() {
[ -n "$SYS_DETECTION_COMPLETE" ] || print_info "Detecting web server..."
# Apache
if command_exists httpd; then
SYS_WEB_SERVER="apache"
SYS_WEB_SERVER_VERSION=$(httpd -v 2>/dev/null | grep -oP 'Apache/\K[\d.]+' | head -1)
print_success "Detected Apache ${SYS_WEB_SERVER_VERSION}"
return 0
elif command_exists apache2; then
SYS_WEB_SERVER="apache"
SYS_WEB_SERVER_VERSION=$(apache2 -v 2>/dev/null | grep -oP 'Apache/\K[\d.]+' | head -1)
print_success "Detected Apache ${SYS_WEB_SERVER_VERSION}"
return 0
fi
# Nginx
if command_exists nginx; then
SYS_WEB_SERVER="nginx"
SYS_WEB_SERVER_VERSION=$(nginx -v 2>&1 | grep -oP 'nginx/\K[\d.]+' 2>/dev/null)
print_success "Detected Nginx ${SYS_WEB_SERVER_VERSION}"
return 0
fi
# LiteSpeed
if [ -f /usr/local/lsws/bin/lswsctrl ]; then
SYS_WEB_SERVER="litespeed"
SYS_WEB_SERVER_VERSION=$(/usr/local/lsws/bin/lswsctrl version 2>/dev/null | grep -oP 'LiteSpeed/\K[\d.]+' | head -1)
print_success "Detected LiteSpeed ${SYS_WEB_SERVER_VERSION}"
return 0
fi
# OpenLiteSpeed
if command_exists /usr/local/lsws/bin/openlitespeed; then
SYS_WEB_SERVER="openlitespeed"
SYS_WEB_SERVER_VERSION=$(cat /usr/local/lsws/VERSION 2>/dev/null)
print_success "Detected OpenLiteSpeed ${SYS_WEB_SERVER_VERSION}"
return 0
fi
SYS_WEB_SERVER="unknown"
print_warning "Could not detect web server"
return 1
}
#############################################################################
# DATABASE DETECTION
#############################################################################
detect_database() {
[ -n "$SYS_DETECTION_COMPLETE" ] || print_info "Detecting database server..."
if command_exists mysql; then
local version_output=$(mysql --version 2>/dev/null)
if echo "$version_output" | grep -qi "mariadb"; then
SYS_DB_TYPE="mariadb"
SYS_DB_VERSION=$(echo "$version_output" | grep -oP '\d+\.\d+\.\d+' | head -1)
print_success "Detected MariaDB ${SYS_DB_VERSION}"
else
SYS_DB_TYPE="mysql"
SYS_DB_VERSION=$(echo "$version_output" | grep -oP '\d+\.\d+\.\d+' | head -1)
print_success "Detected MySQL ${SYS_DB_VERSION}"
fi
return 0
fi
SYS_DB_TYPE="none"
print_warning "No MySQL/MariaDB detected"
return 1
}
#############################################################################
# PHP VERSION DETECTION
#############################################################################
detect_php_versions() {
[ -n "$SYS_DETECTION_COMPLETE" ] || print_info "Detecting PHP versions..."
SYS_PHP_VERSIONS=()
# Check default PHP
if command_exists php; then
local default_version=$(php -v 2>/dev/null | grep -oP '^PHP \K[\d.]+' | head -1)
[ -n "$default_version" ] && SYS_PHP_VERSIONS+=("$default_version")
fi
# Check EA-PHP versions (cPanel) - fast path parsing
if [ "$SYS_CONTROL_PANEL" = "cpanel" ]; then
for php_path in /opt/cpanel/ea-php*/root/usr/bin/php; do
if [ -x "$php_path" ]; then
# Extract version from path (ea-php82 -> 8.2)
local ver=$(echo "$php_path" | grep -oP 'ea-php\K\d+')
if [ -n "$ver" ]; then
# Convert 82 -> 8.2, 81 -> 8.1, etc
local major="${ver:0:1}"
local minor="${ver:1}"
# Get patch version from php -v only if needed (slower but accurate)
local full_version=$($php_path -v 2>/dev/null | grep -oP '^PHP \K[\d.]+' | head -1)
[ -n "$full_version" ] && SYS_PHP_VERSIONS+=("$full_version")
fi
fi
done
fi
# Check Plesk PHP versions (/opt/plesk/php/)
if [ "$SYS_CONTROL_PANEL" = "plesk" ]; then
for php_path in /opt/plesk/php/*/bin/php; do
if [ -x "$php_path" ]; then
local full_version=$($php_path -v 2>/dev/null | grep -oP '^PHP \K[\d.]+' | head -1)
[ -n "$full_version" ] && SYS_PHP_VERSIONS+=("$full_version")
fi
done
fi
# Check alt-php versions (CloudLinux) - fast path parsing
for php_path in /opt/alt/php*/usr/bin/php; do
if [ -x "$php_path" ]; then
# Extract version from path (php74 -> 7.4)
local ver=$(echo "$php_path" | grep -oP 'php\K\d+')
if [ -n "$ver" ]; then
local full_version=$($php_path -v 2>/dev/null | grep -oP '^PHP \K[\d.]+' | head -1)
[ -n "$full_version" ] && SYS_PHP_VERSIONS+=("$full_version")
fi
fi
done
# Remove duplicates and sort by version
SYS_PHP_VERSIONS=($(echo "${SYS_PHP_VERSIONS[@]}" | tr ' ' '\n' | sort -u -V))
if [ ${#SYS_PHP_VERSIONS[@]} -gt 0 ]; then
print_success "Detected PHP versions: ${SYS_PHP_VERSIONS[*]}"
else
print_warning "No PHP installations detected"
fi
}
#############################################################################
# CLOUDFLARE DETECTION
#############################################################################
detect_cloudflare() {
SYS_CLOUDFLARE_ACTIVE="no"
# Check for mod_cloudflare
if [ "$SYS_WEB_SERVER" = "apache" ]; then
if httpd -M 2>/dev/null | grep -q cloudflare; then
SYS_CLOUDFLARE_ACTIVE="yes"
print_info "Cloudflare module detected"
fi
fi
# Check for railgun - fast process check
if pgrep -x railgun > /dev/null 2>&1; then
SYS_CLOUDFLARE_ACTIVE="yes"
print_info "Cloudflare Railgun detected"
fi
}
#############################################################################
# FIREWALL DETECTION
#############################################################################
detect_firewall() {
[ -n "$SYS_DETECTION_COMPLETE" ] || print_info "Detecting firewall..."
# CSF/LFD
if [ -f "/etc/csf/csf.conf" ]; then
SYS_FIREWALL="csf"
# Fast version check - read from version.txt or parse csf script
SYS_FIREWALL_VERSION=$(head -1 /etc/csf/version.txt 2>/dev/null || grep -oP 'my \$version = "\K[^"]+' /usr/sbin/csf 2>/dev/null | head -1 || echo "unknown")
# Fast check: just check if lfd process is running
if pgrep -x lfd > /dev/null 2>&1; then
SYS_FIREWALL_ACTIVE="yes"
print_success "Detected CSF ${SYS_FIREWALL_VERSION} (active)"
else
SYS_FIREWALL_ACTIVE="no"
print_info "Detected CSF ${SYS_FIREWALL_VERSION}"
fi
export SYS_CSF_ACTIVE="${SYS_FIREWALL_ACTIVE}"
return 0
fi
# firewalld
if command_exists firewall-cmd; then
SYS_FIREWALL="firewalld"
SYS_FIREWALL_VERSION=$(firewall-cmd --version 2>/dev/null || echo "unknown")
if systemctl is-active --quiet firewalld 2>/dev/null; then
SYS_FIREWALL_ACTIVE="yes"
print_success "Detected firewalld ${SYS_FIREWALL_VERSION} (active)"
else
SYS_FIREWALL_ACTIVE="no"
print_warning "Detected firewalld ${SYS_FIREWALL_VERSION} (inactive)"
fi
return 0
fi
# iptables
if command_exists iptables; then
SYS_FIREWALL="iptables"
SYS_FIREWALL_VERSION=$(iptables --version 2>/dev/null | grep -oP 'v\K[\d.]+' | head -1 || echo "unknown")
# Fast check: just check filter table INPUT chain only (much faster than full -L)
if [ "$(iptables -L INPUT -n 2>/dev/null | wc -l)" -gt 2 ]; then
SYS_FIREWALL_ACTIVE="yes"
print_success "Detected iptables ${SYS_FIREWALL_VERSION} (active)"
else
SYS_FIREWALL_ACTIVE="no"
print_warning "Detected iptables ${SYS_FIREWALL_VERSION} (no rules)"
fi
return 0
fi
# UFW
if command_exists ufw; then
SYS_FIREWALL="ufw"
SYS_FIREWALL_VERSION=$(ufw version 2>/dev/null | grep -oP '\d+\.\d+\.\d+' | head -1 || echo "unknown")
if ufw status 2>/dev/null | grep -q "Status: active"; then
SYS_FIREWALL_ACTIVE="yes"
print_success "Detected UFW ${SYS_FIREWALL_VERSION} (active)"
else
SYS_FIREWALL_ACTIVE="no"
print_warning "Detected UFW ${SYS_FIREWALL_VERSION} (inactive)"
fi
return 0
fi
SYS_FIREWALL="none"
SYS_FIREWALL_ACTIVE="no"
print_warning "No firewall detected"
return 1
}
#############################################################################
# SYSTEM RESOURCES (Comprehensive - like user's example)
#############################################################################
get_system_resources() {
local resources_file="${TEMP_SESSION_DIR}/system_resources.tmp"
# CPU Information
local cores=$(nproc)
local load=$(uptime | awk -F'load average:' '{print $2}' | awk '{print $1}' | sed 's/,//')
local cpu_line=$(top -bn1 | grep "Cpu(s)")
local cpu_idle=$(echo "$cpu_line" | awk '{print $8}' | sed 's/%id,//')
local cpu_iowait=$(echo "$cpu_line" | awk '{print $10}' | sed 's/%wa,//')
local cpu_used=$(awk "BEGIN {printf \"%.1f\", 100-$cpu_idle}")
local load_percent=$(awk "BEGIN {printf \"%.0f\", ($load/$cores)*100}")
# Memory Information - get all in one call
local mem_info=$(free -h)
local mem_total=$(echo "$mem_info" | awk '/^Mem:/ {print $2}')
local mem_used=$(echo "$mem_info" | awk '/^Mem:/ {print $3}')
local mem_available=$(echo "$mem_info" | awk '/^Mem:/ {print $7}')
local mem_percent=$(free | awk '/^Mem:/ {printf "%.0f", $3/$2*100}')
# Swap Information - from same free call
local swap_total=$(echo "$mem_info" | awk '/^Swap:/ {print $2}')
local swap_used=$(echo "$mem_info" | awk '/^Swap:/ {print $3}')
local swap_percent=0
if [ "$swap_total" != "0B" ] && [ -n "$swap_total" ]; then
swap_percent=$(free | awk '/^Swap:/ {if($2>0) printf "%.0f", $3/$2*100; else print "0"}')
fi
# Disk Information - single df call
local disk_info=$(df -h / | awk 'NR==2 {print $2,$3,$5}')
local disk_root_total=$(echo "$disk_info" | awk '{print $1}')
local disk_root_used=$(echo "$disk_info" | awk '{print $2}')
local disk_root_percent=$(echo "$disk_info" | awk '{print $3}')
# Uptime
local uptime_str=$(uptime -p)
# Save to session file
cat > "$resources_file" << EOF
CPU_CORES=$cores
CPU_USED=$cpu_used
CPU_IOWAIT=$cpu_iowait
LOAD_AVERAGE=$load
LOAD_PERCENT=$load_percent
MEM_TOTAL=$mem_total
MEM_USED=$mem_used
MEM_PERCENT=$mem_percent
MEM_AVAILABLE=$mem_available
SWAP_TOTAL=$swap_total
SWAP_USED=$swap_used
SWAP_PERCENT=$swap_percent
DISK_ROOT_TOTAL=$disk_root_total
DISK_ROOT_USED=$disk_root_used
DISK_ROOT_PERCENT=$disk_root_percent
UPTIME=$uptime_str
EOF
# Also export for immediate use
export CPU_CORES=$cores
export CPU_USED=$cpu_used
export CPU_IOWAIT=$cpu_iowait
export LOAD_AVERAGE=$load
export LOAD_PERCENT=$load_percent
export MEM_TOTAL=$mem_total
export MEM_USED=$mem_used
export MEM_PERCENT=$mem_percent
}
#############################################################################
# DISPLAY FUNCTIONS
#############################################################################
show_system_info() {
echo ""
print_banner "System Information"
# Control Panel
echo -e "${BOLD}Control Panel:${NC}"
if [ "$SYS_CONTROL_PANEL" != "none" ]; then
echo " Type: $SYS_CONTROL_PANEL"
echo " Version: $SYS_CONTROL_PANEL_VERSION"
else
echo " Type: Standalone (no control panel)"
fi
echo ""
# Operating System
echo -e "${BOLD}Operating System:${NC}"
echo " OS: $SYS_OS_TYPE $SYS_OS_VERSION"
echo " Kernel: $(uname -r)"
[ "${SYS_CLOUDLINUX:-}" = "yes" ] && echo " CloudLinux: Enabled"
echo ""
# Web Server
echo -e "${BOLD}Web Server:${NC}"
echo " Type: $SYS_WEB_SERVER"
echo " Version: $SYS_WEB_SERVER_VERSION"
[ "$SYS_CLOUDFLARE_ACTIVE" = "yes" ] && echo " Cloudflare: Active"
echo ""
# Database
echo -e "${BOLD}Database:${NC}"
if [ "$SYS_DB_TYPE" != "none" ]; then
echo " Type: $SYS_DB_TYPE"
echo " Version: $SYS_DB_VERSION"
else
echo " Type: Not installed"
fi
echo ""
# PHP
echo -e "${BOLD}PHP Versions:${NC}"
if [ ${#SYS_PHP_VERSIONS[@]} -gt 0 ]; then
for version in "${SYS_PHP_VERSIONS[@]}"; do
echo " - PHP $version"
done
else
echo " None detected"
fi
echo ""
# System Resources
echo -e "${BOLD}System Resources:${NC}"
echo " CPU Cores: $CPU_CORES"
echo " CPU Usage: ${CPU_USED}% (actual)"
echo " I/O Wait: ${CPU_IOWAIT}%"
echo " Load Average: $LOAD_AVERAGE (${LOAD_PERCENT}% of core capacity)"
echo ""
echo " RAM Total: $MEM_TOTAL"
echo " RAM Used: $MEM_USED (${MEM_PERCENT}%)"
echo " RAM Available: $MEM_AVAILABLE"
echo ""
echo " Swap Total: $SWAP_TOTAL"
echo " Swap Used: $SWAP_USED (${SWAP_PERCENT}%)"
echo ""
echo " Disk (/) Total: $DISK_ROOT_TOTAL"
echo " Disk (/) Used: $DISK_ROOT_USED (${DISK_ROOT_PERCENT})"
echo ""
echo " Uptime: $UPTIME"
echo ""
# Paths
echo -e "${BOLD}Important Paths:${NC}"
echo " Log Directory: $SYS_LOG_DIR"
echo " User Home Base: $SYS_USER_HOME_BASE"
echo ""
}
#############################################################################
# INITIALIZE ALL DETECTION
#############################################################################
initialize_system_detection() {
# Create temp session directory
create_temp_session
# Run all detections
detect_control_panel
detect_os
detect_web_server
detect_database
detect_php_versions
detect_cloudflare
detect_firewall
get_system_resources
# Mark as initialized
export SYS_DETECTION_COMPLETE="yes"
}
# Export all functions for use in subshells and sourced scripts
export -f detect_control_panel
export -f detect_os
export -f detect_web_server
export -f detect_database
export -f detect_php_versions
export -f detect_cloudflare
export -f detect_firewall
export -f get_system_resources
export -f show_system_info
export -f initialize_system_detection
# Auto-initialize if not already done (when sourced)
# OPTIMIZATION: Don't auto-detect at library load time
# This was causing 30-45 second hangs! Only detect when explicitly needed.
# Callers can call initialize_system_detection() when they actually need system info.
# [ -z "${SYS_DETECTION_COMPLETE:-}" ] && initialize_system_detection