Initial commit: Server Management Toolkit v2.0
- Complete security menu restructure (3-mode: Analysis/Actions/Live) - Intelligent cPHulk enablement with CSF whitelist import - Live network security monitoring dashboard - Multi-source threat detection and classification - 50+ organized security tools across 4-level menu hierarchy - System health diagnostics with cPanel/WHM integration - Reference database for cross-module intelligence sharing
This commit is contained in:
Executable
+277
@@ -0,0 +1,277 @@
|
||||
#!/bin/bash
|
||||
|
||||
#############################################################################
|
||||
# Common Functions Library
|
||||
# Shared utilities for all Server Management Toolkit modules
|
||||
#############################################################################
|
||||
|
||||
#############################################################################
|
||||
# Professional Color Scheme
|
||||
# - Uses ONLY basic ANSI colors (works on ANY terminal)
|
||||
# - High contrast for readability
|
||||
# - No emojis (terminal-agnostic)
|
||||
# - Industry standard: Red=error, Yellow=warning, Green=success, Blue=info
|
||||
#
|
||||
# To disable colors completely: export TOOLKIT_NO_COLOR=1
|
||||
#############################################################################
|
||||
|
||||
# Detect if colors should be disabled or adjusted
|
||||
if [ "${TOOLKIT_NO_COLOR:-0}" = "1" ] || [ ! -t 1 ]; then
|
||||
# No color mode (monochrome terminals, piped output, etc.)
|
||||
export RED=''
|
||||
export YELLOW=''
|
||||
export GREEN=''
|
||||
export BLUE=''
|
||||
export CYAN=''
|
||||
export WHITE=''
|
||||
export BOLD=''
|
||||
export DIM=''
|
||||
export NC=''
|
||||
elif [ "${TOOLKIT_HIGH_CONTRAST:-0}" = "1" ]; then
|
||||
# High contrast mode - BOLD colors for light backgrounds
|
||||
# These work well on BOTH light and dark backgrounds
|
||||
export RED='\033[1;31m' # Errors, critical alerts (BOLD RED)
|
||||
export YELLOW='\033[1;33m' # Warnings, caution (BOLD YELLOW)
|
||||
export GREEN='\033[1;32m' # Success, completed (BOLD GREEN)
|
||||
export BLUE='\033[1;34m' # Info, neutral status (BOLD BLUE)
|
||||
export CYAN='\033[1;36m' # Headers, sections (BOLD CYAN)
|
||||
export WHITE='\033[1;37m' # Emphasis, highlights (BOLD WHITE)
|
||||
export BOLD='\033[1m' # Important text
|
||||
export DIM='\033[2m' # Secondary/less important
|
||||
export NC='\033[0m' # Reset/No Color
|
||||
else
|
||||
# Normal color mode (default - optimized for dark backgrounds)
|
||||
export RED='\033[0;31m' # Errors, critical alerts
|
||||
export YELLOW='\033[0;33m' # Warnings, caution
|
||||
export GREEN='\033[0;32m' # Success, completed
|
||||
export BLUE='\033[0;34m' # Info, neutral status
|
||||
export CYAN='\033[0;36m' # Headers, sections
|
||||
export WHITE='\033[1;37m' # Emphasis, highlights
|
||||
export BOLD='\033[1m' # Important text
|
||||
export DIM='\033[2m' # Secondary/less important
|
||||
export NC='\033[0m' # Reset/No Color
|
||||
fi
|
||||
|
||||
# Print functions - professional, no emojis
|
||||
print_banner() {
|
||||
local title="$1"
|
||||
echo ""
|
||||
echo "==============================================================================="
|
||||
echo " $title"
|
||||
echo "==============================================================================="
|
||||
echo ""
|
||||
}
|
||||
|
||||
print_section() {
|
||||
local title="$1"
|
||||
echo ""
|
||||
echo -e "${BOLD}$title${NC}"
|
||||
echo "-------------------------------------------------------------------------------"
|
||||
}
|
||||
|
||||
print_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
print_success() {
|
||||
echo -e "${GREEN}[OK]${NC} $1"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
print_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
print_critical() {
|
||||
echo -e "${RED}${BOLD}[CRITICAL]${NC} $1"
|
||||
}
|
||||
|
||||
print_alert() {
|
||||
echo -e "${RED}[ALERT]${NC} $1"
|
||||
}
|
||||
|
||||
print_header() {
|
||||
echo -e "${CYAN}${BOLD}$1${NC}"
|
||||
}
|
||||
|
||||
# Wait for user input
|
||||
press_enter() {
|
||||
echo ""
|
||||
read -p "Press Enter to continue..." _
|
||||
}
|
||||
|
||||
# Show banner (wrapper for print_banner for compatibility)
|
||||
show_banner() {
|
||||
if [ -n "$1" ]; then
|
||||
print_banner "$1"
|
||||
else
|
||||
print_banner "Server Toolkit"
|
||||
fi
|
||||
}
|
||||
|
||||
# Progress indicator - terminal safe (no fancy characters)
|
||||
show_progress() {
|
||||
local current=$1
|
||||
local total=$2
|
||||
local message="$3"
|
||||
local percent=$((current * 100 / total))
|
||||
local bars=$((percent / 5)) # 20 chars wide
|
||||
|
||||
printf "\r[INFO] Progress: ["
|
||||
printf "%${bars}s" | tr ' ' '#'
|
||||
printf "%$((20-bars))s" | tr ' ' '-'
|
||||
printf "] %3d%% - %s" "$percent" "$message"
|
||||
}
|
||||
|
||||
# Finish progress line
|
||||
finish_progress() {
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Display terminal info (for debugging)
|
||||
show_terminal_info() {
|
||||
echo ""
|
||||
echo "Terminal Information:"
|
||||
echo " TERM: ${TERM:-not set}"
|
||||
echo " Colors: $([ -t 1 ] && echo "enabled" || echo "disabled (not a tty)")"
|
||||
echo " TOOLKIT_NO_COLOR: ${TOOLKIT_NO_COLOR:-0}"
|
||||
echo " Columns: ${COLUMNS:-$(tput cols 2>/dev/null || echo "unknown")}"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Create temporary session directory
|
||||
create_temp_session() {
|
||||
export SESSION_ID=$$
|
||||
export TEMP_SESSION_DIR="/tmp/server-toolkit-${SESSION_ID}"
|
||||
mkdir -p "$TEMP_SESSION_DIR"
|
||||
|
||||
# Cleanup on exit
|
||||
trap "rm -rf $TEMP_SESSION_DIR 2>/dev/null" EXIT INT TERM
|
||||
}
|
||||
|
||||
# Ask user for confirmation
|
||||
confirm() {
|
||||
local prompt="$1"
|
||||
local default="${2:-N}"
|
||||
|
||||
if [[ "$default" == "Y" ]]; then
|
||||
read -p "$prompt [Y/n]: " -n 1 -r
|
||||
else
|
||||
read -p "$prompt [y/N]: " -n 1 -r
|
||||
fi
|
||||
echo
|
||||
|
||||
if [[ "$default" == "Y" ]]; then
|
||||
[[ ! $REPLY =~ ^[Nn]$ ]]
|
||||
else
|
||||
[[ $REPLY =~ ^[Yy]$ ]]
|
||||
fi
|
||||
}
|
||||
|
||||
# Format bytes to human readable
|
||||
format_bytes() {
|
||||
local bytes=$1
|
||||
if [ -z "$bytes" ] || [ "$bytes" -eq 0 ]; then
|
||||
echo "0 B"
|
||||
return
|
||||
fi
|
||||
|
||||
local units=("B" "KB" "MB" "GB" "TB")
|
||||
local unit=0
|
||||
local size=$bytes
|
||||
|
||||
while [ $size -gt 1024 ] && [ $unit -lt 4 ]; do
|
||||
size=$((size / 1024))
|
||||
unit=$((unit + 1))
|
||||
done
|
||||
|
||||
echo "${size} ${units[$unit]}"
|
||||
}
|
||||
|
||||
# Format seconds to human readable time
|
||||
format_duration() {
|
||||
local seconds=$1
|
||||
local days=$((seconds / 86400))
|
||||
local hours=$(((seconds % 86400) / 3600))
|
||||
local minutes=$(((seconds % 3600) / 60))
|
||||
local secs=$((seconds % 60))
|
||||
|
||||
if [ $days -gt 0 ]; then
|
||||
echo "${days}d ${hours}h ${minutes}m"
|
||||
elif [ $hours -gt 0 ]; then
|
||||
echo "${hours}h ${minutes}m ${secs}s"
|
||||
elif [ $minutes -gt 0 ]; then
|
||||
echo "${minutes}m ${secs}s"
|
||||
else
|
||||
echo "${secs}s"
|
||||
fi
|
||||
}
|
||||
|
||||
# Check if command exists
|
||||
command_exists() {
|
||||
command -v "$1" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# Check if running as root
|
||||
require_root() {
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
print_error "This script must be run as root"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Safe file append (creates backup)
|
||||
safe_append() {
|
||||
local file="$1"
|
||||
local content="$2"
|
||||
|
||||
if [ -f "$file" ]; then
|
||||
cp "$file" "${file}.bak.$(date +%s)"
|
||||
fi
|
||||
|
||||
echo "$content" >> "$file"
|
||||
}
|
||||
|
||||
# Log message to file
|
||||
log_message() {
|
||||
local level="$1"
|
||||
local message="$2"
|
||||
local log_file="${3:-/var/log/server-toolkit.log}"
|
||||
|
||||
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
||||
echo "[$timestamp] [$level] $message" >> "$log_file"
|
||||
}
|
||||
|
||||
# Get script directory (works even when sourced)
|
||||
get_script_dir() {
|
||||
local source="${BASH_SOURCE[0]}"
|
||||
while [ -h "$source" ]; do
|
||||
local dir="$(cd -P "$(dirname "$source")" && pwd)"
|
||||
source="$(readlink "$source")"
|
||||
[[ $source != /* ]] && source="$dir/$source"
|
||||
done
|
||||
echo "$(cd -P "$(dirname "$source")" && pwd)"
|
||||
}
|
||||
|
||||
# Detect toolkit base directory
|
||||
get_toolkit_dir() {
|
||||
local script_dir=$(get_script_dir)
|
||||
echo "$(cd "$script_dir/.." && pwd)"
|
||||
}
|
||||
|
||||
# Export toolkit directories
|
||||
export TOOLKIT_BASE_DIR=$(get_toolkit_dir)
|
||||
export TOOLKIT_LIB_DIR="${TOOLKIT_BASE_DIR}/lib"
|
||||
export TOOLKIT_MODULES_DIR="${TOOLKIT_BASE_DIR}/modules"
|
||||
export TOOLKIT_CONFIG_DIR="${TOOLKIT_BASE_DIR}/config"
|
||||
|
||||
# Load configuration if exists
|
||||
load_config() {
|
||||
local config_file="${TOOLKIT_CONFIG_DIR}/settings.conf"
|
||||
if [ -f "$config_file" ]; then
|
||||
source "$config_file"
|
||||
fi
|
||||
}
|
||||
Executable
+524
@@ -0,0 +1,524 @@
|
||||
#!/bin/bash
|
||||
|
||||
#############################################################################
|
||||
# MySQL/MariaDB Deep Analysis Library
|
||||
# Forensic-level query analysis with WordPress plugin identification
|
||||
#############################################################################
|
||||
|
||||
# Source dependencies
|
||||
if [ -z "$TOOLKIT_BASE_DIR" ]; then
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "$SCRIPT_DIR/common-functions.sh"
|
||||
source "$SCRIPT_DIR/system-detect.sh"
|
||||
source "$SCRIPT_DIR/user-manager.sh"
|
||||
fi
|
||||
|
||||
#############################################################################
|
||||
# WORDPRESS PLUGIN SIGNATURES
|
||||
#############################################################################
|
||||
|
||||
# Map table name patterns to plugin names
|
||||
declare -gA PLUGIN_SIGNATURES=(
|
||||
# E-Commerce
|
||||
["woocommerce"]="WooCommerce"
|
||||
["wc_admin|wc_order|wc_product"]="WooCommerce"
|
||||
["edd_"]="Easy Digital Downloads"
|
||||
|
||||
# SEO
|
||||
["yoast"]="Yoast SEO"
|
||||
["rank_math"]="Rank Math SEO"
|
||||
["aioseo"]="All in One SEO"
|
||||
|
||||
# Security
|
||||
["wfBlocks|wfConfig|wfCrawlers|wfHits|wfLocs|wfLogins"]="WordFence"
|
||||
["itsec_"]="iThemes Security"
|
||||
["defender_"]="Defender Security"
|
||||
|
||||
# Forms
|
||||
["wpforms"]="WPForms"
|
||||
["gf_|gravityforms"]="Gravity Forms"
|
||||
["ninja_forms"]="Ninja Forms"
|
||||
["frm_|formidable"]="Formidable Forms"
|
||||
["cf7_|contact_form_7"]="Contact Form 7"
|
||||
|
||||
# Page Builders
|
||||
["elementor"]="Elementor"
|
||||
["siteorigin"]="SiteOrigin Page Builder"
|
||||
["beaver_"]="Beaver Builder"
|
||||
["fusion_"]="Avada Fusion Builder"
|
||||
|
||||
# Multilingual
|
||||
["icl_|wpml"]="WPML"
|
||||
["translations_"]="Polylang"
|
||||
|
||||
# Caching/Performance
|
||||
["w3tc"]="W3 Total Cache"
|
||||
["wp_rocket"]="WP Rocket"
|
||||
["cache_"]="Various Cache Plugins"
|
||||
|
||||
# Email/Newsletter
|
||||
["mailpoet"]="MailPoet"
|
||||
["newsletter"]="Newsletter"
|
||||
["wysija"]="MailPoet 2"
|
||||
|
||||
# Events/Booking
|
||||
["em_|events_manager"]="Events Manager"
|
||||
["booking"]="Booking Calendar"
|
||||
["amelia"]="Amelia Booking"
|
||||
|
||||
# Backup
|
||||
["duplicator"]="Duplicator"
|
||||
["updraft"]="UpdraftPlus"
|
||||
|
||||
# Media/Gallery
|
||||
["ngg_|nextgen"]="NextGEN Gallery"
|
||||
["smush"]="Smush"
|
||||
["ewww"]="EWWW Image Optimizer"
|
||||
|
||||
# Membership
|
||||
["pmpro|members"]="Paid Memberships Pro"
|
||||
["mepr_"]="MemberPress"
|
||||
|
||||
# Search
|
||||
["searchwp"]="SearchWP"
|
||||
["relevanssi"]="Relevanssi"
|
||||
|
||||
# Social
|
||||
["social_warfare"]="Social Warfare"
|
||||
["monarcht"]="Monarch Social Sharing"
|
||||
|
||||
# Redirects
|
||||
["redirection"]="Redirection"
|
||||
["simple_301"]="Simple 301 Redirects"
|
||||
|
||||
# WP Core/Action Scheduler
|
||||
["actionscheduler"]="Action Scheduler (WooCommerce/Jetpack)"
|
||||
["jetpack"]="Jetpack"
|
||||
|
||||
# LMS
|
||||
["learndash"]="LearnDash"
|
||||
["tutor"]="Tutor LMS"
|
||||
|
||||
# Other Popular
|
||||
["acf_"]="Advanced Custom Fields"
|
||||
["pods_"]="Pods Framework"
|
||||
["tablepress"]="TablePress"
|
||||
)
|
||||
|
||||
# Known problematic query patterns
|
||||
declare -gA PROBLEM_PATTERNS=(
|
||||
["SELECT.*wp_options.*autoload"]="Autoloaded options bloat"
|
||||
["SELECT.*wp_postmeta.*meta_key"]="Postmeta table scan (missing index)"
|
||||
["wp_woocommerce_sessions.*session_expiry"]="Expired WooCommerce sessions"
|
||||
["actionscheduler.*scheduled_date.*pending"]="Action Scheduler backlog"
|
||||
["wp_posts.*post_type.*LIKE"]="Inefficient post type query"
|
||||
)
|
||||
|
||||
#############################################################################
|
||||
# DATABASE MAPPING
|
||||
#############################################################################
|
||||
|
||||
# Map database to user and domain
|
||||
map_database_to_user_domain() {
|
||||
local db_name="$1"
|
||||
local map_file="${TEMP_SESSION_DIR}/db_user_domain_map.tmp"
|
||||
|
||||
# Return cached if exists
|
||||
if [ -f "$map_file" ]; then
|
||||
grep "^${db_name}|" "$map_file" 2>/dev/null
|
||||
return
|
||||
fi
|
||||
|
||||
# Build map for all databases
|
||||
print_info "Building database to user/domain mapping..."
|
||||
|
||||
local all_dbs=$(mysql -Ns -e "SHOW DATABASES" 2>/dev/null | grep -v "^information_schema$\|^mysql$\|^performance_schema$\|^sys$")
|
||||
|
||||
for db in $all_dbs; do
|
||||
# Extract potential username from database name
|
||||
# Format: username_dbname
|
||||
local potential_user=$(echo "$db" | cut -d_ -f1)
|
||||
|
||||
# Verify user exists
|
||||
local users=($(list_all_users))
|
||||
if [[ " ${users[@]} " =~ " ${potential_user} " ]]; then
|
||||
local primary_domain=$(get_user_domains "$potential_user" | head -1)
|
||||
echo "${db}|${potential_user}|${primary_domain}" >> "$map_file"
|
||||
else
|
||||
echo "${db}|unknown|unknown" >> "$map_file"
|
||||
fi
|
||||
done
|
||||
|
||||
grep "^${db_name}|" "$map_file" 2>/dev/null
|
||||
}
|
||||
|
||||
# Get database owner
|
||||
get_database_owner() {
|
||||
local db_name="$1"
|
||||
map_database_to_user_domain "$db_name" | cut -d'|' -f2
|
||||
}
|
||||
|
||||
# Get database domain
|
||||
get_database_domain() {
|
||||
local db_name="$1"
|
||||
map_database_to_user_domain "$db_name" | cut -d'|' -f3
|
||||
}
|
||||
|
||||
#############################################################################
|
||||
# QUERY CAPTURE
|
||||
#############################################################################
|
||||
|
||||
# Capture live queries from processlist
|
||||
capture_live_queries() {
|
||||
local output_file="${TEMP_SESSION_DIR}/live_queries.tmp"
|
||||
|
||||
print_info "Capturing live queries..."
|
||||
|
||||
mysql -e "SHOW FULL PROCESSLIST" 2>/dev/null | grep -v "SHOW FULL PROCESSLIST" > "$output_file"
|
||||
|
||||
local query_count=$(wc -l < "$output_file")
|
||||
print_success "Captured $query_count active queries"
|
||||
|
||||
echo "$output_file"
|
||||
}
|
||||
|
||||
# Parse slow query log
|
||||
parse_slow_query_log() {
|
||||
local slow_log="${1:-/var/log/mysql/slow.log}"
|
||||
local output_file="${TEMP_SESSION_DIR}/slow_queries.tmp"
|
||||
|
||||
if [ ! -f "$slow_log" ]; then
|
||||
# Try alternative locations
|
||||
slow_log=$(mysql -Ns -e "SHOW VARIABLES LIKE 'slow_query_log_file'" | awk '{print $2}')
|
||||
fi
|
||||
|
||||
if [ ! -f "$slow_log" ]; then
|
||||
print_warning "Slow query log not found"
|
||||
touch "$output_file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
print_info "Parsing slow query log: $slow_log"
|
||||
|
||||
# Extract queries that took > 1 second (adjustable)
|
||||
grep -A 10 "Query_time:" "$slow_log" 2>/dev/null | tail -1000 > "$output_file"
|
||||
|
||||
local query_count=$(grep -c "Query_time:" "$output_file" 2>/dev/null || echo 0)
|
||||
print_success "Found $query_count slow queries"
|
||||
|
||||
echo "$output_file"
|
||||
}
|
||||
|
||||
#############################################################################
|
||||
# TABLE ANALYSIS
|
||||
#############################################################################
|
||||
|
||||
# Identify plugin from table name
|
||||
identify_plugin_from_table() {
|
||||
local table_name="$1"
|
||||
|
||||
# Remove prefix to get base table name
|
||||
local base_table=$(echo "$table_name" | sed 's/^[a-z0-9]*_wp_//; s/^wp_//')
|
||||
|
||||
# Check against signatures
|
||||
for pattern in "${!PLUGIN_SIGNATURES[@]}"; do
|
||||
if echo "$base_table" | grep -qiE "$pattern"; then
|
||||
echo "${PLUGIN_SIGNATURES[$pattern]}"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
# Check for WP core tables
|
||||
if echo "$table_name" | grep -qE "wp_(posts|postmeta|users|usermeta|options|terms|term_relationships|term_taxonomy|comments|commentmeta|links)$"; then
|
||||
echo "WordPress Core"
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "Unknown Plugin"
|
||||
}
|
||||
|
||||
# Get table size
|
||||
get_table_size() {
|
||||
local db_name="$1"
|
||||
local table_name="$2"
|
||||
|
||||
mysql -Ns -e "SELECT ROUND(((data_length + index_length) / 1024 / 1024), 2)
|
||||
FROM information_schema.TABLES
|
||||
WHERE table_schema='$db_name' AND table_name='$table_name'" 2>/dev/null
|
||||
}
|
||||
|
||||
# Get all tables for database
|
||||
get_database_tables() {
|
||||
local db_name="$1"
|
||||
|
||||
mysql -Ns "$db_name" -e "SHOW TABLES" 2>/dev/null
|
||||
}
|
||||
|
||||
# Analyze table for issues
|
||||
analyze_table_structure() {
|
||||
local db_name="$1"
|
||||
local table_name="$2"
|
||||
|
||||
# Get table status
|
||||
mysql -Ns -e "SHOW TABLE STATUS FROM \`$db_name\` LIKE '$table_name'" 2>/dev/null
|
||||
}
|
||||
|
||||
#############################################################################
|
||||
# QUERY ANALYSIS
|
||||
#############################################################################
|
||||
|
||||
# Extract database from query
|
||||
extract_database_from_query() {
|
||||
local query="$1"
|
||||
|
||||
# Try to extract from USE statement
|
||||
if echo "$query" | grep -qiE "^USE "; then
|
||||
echo "$query" | grep -oiE "^USE \K[a-z0-9_]+" | head -1
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Try to extract from db.table format
|
||||
if echo "$query" | grep -qE "\`[a-z0-9_]+\`\."; then
|
||||
echo "$query" | grep -oE "\`[a-z0-9_]+\`\." | head -1 | tr -d '`.'
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "unknown"
|
||||
}
|
||||
|
||||
# Extract tables from query
|
||||
extract_tables_from_query() {
|
||||
local query="$1"
|
||||
|
||||
# Extract FROM and JOIN clauses
|
||||
echo "$query" | grep -oiE "(FROM|JOIN)\s+\`?[a-z0-9_]+\`?" | awk '{print $2}' | tr -d '`' | sort -u
|
||||
}
|
||||
|
||||
# Analyze query performance with EXPLAIN
|
||||
explain_query() {
|
||||
local db_name="$1"
|
||||
local query="$2"
|
||||
local explain_file="${TEMP_SESSION_DIR}/explain_${db_name}_$$.tmp"
|
||||
|
||||
# Clean query for EXPLAIN
|
||||
local clean_query=$(echo "$query" | sed 's/^[^SELECT]*//')
|
||||
|
||||
mysql "$db_name" -e "EXPLAIN $clean_query" 2>/dev/null > "$explain_file"
|
||||
|
||||
# Check for problematic patterns
|
||||
if grep -qiE "Using filesort|Using temporary" "$explain_file"; then
|
||||
echo "WARNING: Inefficient query (filesort/temporary table)"
|
||||
fi
|
||||
|
||||
if grep -qE "type.*ALL" "$explain_file"; then
|
||||
echo "CRITICAL: Full table scan detected"
|
||||
fi
|
||||
|
||||
cat "$explain_file"
|
||||
}
|
||||
|
||||
#############################################################################
|
||||
# PROBLEM IDENTIFICATION
|
||||
#############################################################################
|
||||
|
||||
# Analyze queries and identify problems
|
||||
analyze_queries_for_problems() {
|
||||
local query_file="$1"
|
||||
local problems_file="${TEMP_SESSION_DIR}/query_problems.tmp"
|
||||
|
||||
print_info "Analyzing queries for problems..."
|
||||
|
||||
> "$problems_file"
|
||||
|
||||
local line_num=0
|
||||
while IFS= read -r line; do
|
||||
((line_num++))
|
||||
|
||||
# Extract query time if from slow log
|
||||
local query_time=""
|
||||
if echo "$line" | grep -qE "Query_time:"; then
|
||||
query_time=$(echo "$line" | grep -oE "Query_time: [0-9.]+" | awk '{print $2}')
|
||||
fi
|
||||
|
||||
# Extract the actual query
|
||||
local query=$(echo "$line" | grep -oiE "SELECT.*" | head -1)
|
||||
[ -z "$query" ] && continue
|
||||
|
||||
# Extract database
|
||||
local db_name=$(extract_database_from_query "$query")
|
||||
|
||||
# Extract tables
|
||||
local tables=$(extract_tables_from_query "$query")
|
||||
|
||||
# Identify plugins
|
||||
for table in $tables; do
|
||||
local plugin=$(identify_plugin_from_table "$table")
|
||||
local owner=$(get_database_owner "$db_name")
|
||||
local domain=$(get_database_domain "$db_name")
|
||||
|
||||
# Check against problem patterns
|
||||
for pattern in "${!PROBLEM_PATTERNS[@]}"; do
|
||||
if echo "$query" | grep -qiE "$pattern"; then
|
||||
local issue="${PROBLEM_PATTERNS[$pattern]}"
|
||||
echo "PROBLEM|$domain|$owner|$db_name|$plugin|$table|$issue|$query_time|$query" >> "$problems_file"
|
||||
fi
|
||||
done
|
||||
|
||||
# Record all plugin queries for statistics
|
||||
echo "QUERY|$domain|$owner|$db_name|$plugin|$table|$query_time" >> "$problems_file"
|
||||
done
|
||||
|
||||
# Progress indicator
|
||||
if [ $((line_num % 100)) -eq 0 ]; then
|
||||
show_progress $line_num 1000 "Analyzing queries..."
|
||||
fi
|
||||
done < "$query_file"
|
||||
|
||||
finish_progress
|
||||
echo "$problems_file"
|
||||
}
|
||||
|
||||
#############################################################################
|
||||
# STATISTICS & REPORTING
|
||||
#############################################################################
|
||||
|
||||
# Generate plugin query statistics
|
||||
generate_plugin_statistics() {
|
||||
local problems_file="$1"
|
||||
local stats_file="${TEMP_SESSION_DIR}/plugin_stats.tmp"
|
||||
|
||||
print_info "Generating plugin statistics..."
|
||||
|
||||
# Count queries per plugin per domain
|
||||
awk -F'|' '$1=="QUERY" {print $2"|"$5}' "$problems_file" | sort | uniq -c | sort -rn > "$stats_file"
|
||||
|
||||
echo "$stats_file"
|
||||
}
|
||||
|
||||
# Find largest tables
|
||||
find_largest_tables() {
|
||||
local limit="${1:-20}"
|
||||
local output_file="${TEMP_SESSION_DIR}/largest_tables.tmp"
|
||||
|
||||
print_info "Finding largest tables..."
|
||||
|
||||
mysql -Ns -e "SELECT
|
||||
table_schema,
|
||||
table_name,
|
||||
ROUND(((data_length + index_length) / 1024 / 1024), 2) AS size_mb
|
||||
FROM information_schema.TABLES
|
||||
WHERE table_schema NOT IN ('information_schema', 'mysql', 'performance_schema', 'sys')
|
||||
ORDER BY (data_length + index_length) DESC
|
||||
LIMIT $limit" 2>/dev/null > "$output_file"
|
||||
|
||||
echo "$output_file"
|
||||
}
|
||||
|
||||
# Check for bloated tables
|
||||
check_table_bloat() {
|
||||
local db_name="$1"
|
||||
local table_name="$2"
|
||||
|
||||
# Check data_free (fragmentation)
|
||||
local data_free=$(mysql -Ns -e "SELECT data_free FROM information_schema.TABLES
|
||||
WHERE table_schema='$db_name' AND table_name='$table_name'" 2>/dev/null)
|
||||
|
||||
local data_length=$(mysql -Ns -e "SELECT data_length FROM information_schema.TABLES
|
||||
WHERE table_schema='$db_name' AND table_name='$table_name'" 2>/dev/null)
|
||||
|
||||
if [ -n "$data_free" ] && [ -n "$data_length" ] && [ "$data_length" -gt 0 ]; then
|
||||
local bloat_percent=$(awk "BEGIN {printf \"%.0f\", ($data_free/$data_length)*100}")
|
||||
|
||||
if [ "$bloat_percent" -gt 20 ]; then
|
||||
echo "BLOATED: ${bloat_percent}% fragmentation"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "OK"
|
||||
return 1
|
||||
}
|
||||
|
||||
# Recommend fixes for common issues
|
||||
recommend_fix() {
|
||||
local issue="$1"
|
||||
local db_name="$2"
|
||||
local table_name="$3"
|
||||
local plugin="$4"
|
||||
|
||||
case "$issue" in
|
||||
*"Autoloaded options"*)
|
||||
echo "Run: wp option list --autoload=yes --format=count (check if > 500)"
|
||||
echo "Fix: Disable autoload for large options or remove unused plugins"
|
||||
;;
|
||||
*"Postmeta table scan"*)
|
||||
echo "ALTER TABLE \`$table_name\` ADD INDEX idx_meta_key (meta_key(191));"
|
||||
;;
|
||||
*"Expired WooCommerce sessions"*)
|
||||
echo "DELETE FROM \`$table_name\` WHERE session_expiry < UNIX_TIMESTAMP(NOW() - INTERVAL 7 DAY);"
|
||||
;;
|
||||
*"Action Scheduler"*)
|
||||
echo "wp action-scheduler clean --batch-size=100 (if WP-CLI available)"
|
||||
echo "Or: DELETE FROM \`$table_name\` WHERE status='complete' AND scheduled_date < NOW() - INTERVAL 30 DAY;"
|
||||
;;
|
||||
*"Full table scan"*)
|
||||
echo "Run EXPLAIN on the query to identify missing indexes"
|
||||
echo "Consider adding appropriate indexes based on WHERE/JOIN clauses"
|
||||
;;
|
||||
*)
|
||||
if [ "$plugin" = "WordFence" ]; then
|
||||
echo "Update WordFence to latest version"
|
||||
echo "Consider adjusting scan frequency"
|
||||
elif [ "$plugin" = "WooCommerce" ]; then
|
||||
echo "Verify WooCommerce database tables are optimized"
|
||||
echo "Enable WooCommerce session cleanup cron"
|
||||
elif [ "$plugin" = "Yoast SEO" ]; then
|
||||
echo "wp yoast index --reindex (rebuild indexables)"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
#############################################################################
|
||||
# SUMMARY REPORT
|
||||
#############################################################################
|
||||
|
||||
generate_summary_report() {
|
||||
local problems_file="$1"
|
||||
|
||||
print_banner "MySQL Query Analysis Summary"
|
||||
|
||||
# Critical issues
|
||||
local critical_count=$(grep -c "^PROBLEM" "$problems_file" 2>/dev/null || echo 0)
|
||||
|
||||
if [ "$critical_count" -gt 0 ]; then
|
||||
echo -e "${RED}${BOLD} CRITICAL ISSUES FOUND: $critical_count${NC}"
|
||||
echo ""
|
||||
|
||||
grep "^PROBLEM" "$problems_file" | head -10 | while IFS='|' read -r type domain owner db plugin table issue query_time query; do
|
||||
echo -e "${RED}[!] $plugin - $domain${NC}"
|
||||
echo " Database: $db"
|
||||
echo " Table: $table"
|
||||
echo " Issue: $issue"
|
||||
[ -n "$query_time" ] && echo " Query Time: ${query_time}s"
|
||||
echo " Fix: $(recommend_fix "$issue" "$db" "$table" "$plugin")"
|
||||
echo ""
|
||||
done
|
||||
else
|
||||
echo -e "${GREEN} No critical issues detected${NC}"
|
||||
echo ""
|
||||
fi
|
||||
}
|
||||
|
||||
#############################################################################
|
||||
# EXPORT FUNCTIONS
|
||||
#############################################################################
|
||||
|
||||
# Make functions available to other scripts
|
||||
export -f identify_plugin_from_table
|
||||
export -f map_database_to_user_domain
|
||||
export -f get_database_owner
|
||||
export -f get_database_domain
|
||||
export -f analyze_queries_for_problems
|
||||
export -f generate_plugin_statistics
|
||||
export -f recommend_fix
|
||||
Executable
+569
@@ -0,0 +1,569 @@
|
||||
#!/bin/bash
|
||||
|
||||
#############################################################################
|
||||
# System Reference Database
|
||||
# Quick-lookup database optimized for fast grep/awk queries
|
||||
# Format: Pipe-delimited structured data
|
||||
#############################################################################
|
||||
|
||||
# Source dependencies
|
||||
if [ -z "$TOOLKIT_BASE_DIR" ]; then
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "$SCRIPT_DIR/common-functions.sh"
|
||||
source "$SCRIPT_DIR/system-detect.sh"
|
||||
source "$SCRIPT_DIR/user-manager.sh"
|
||||
fi
|
||||
|
||||
# Reference database location
|
||||
export SYSREF_DB="${TOOLKIT_BASE_DIR}/.sysref"
|
||||
export SYSREF_TIMESTAMP="${TOOLKIT_BASE_DIR}/.sysref.timestamp"
|
||||
|
||||
#############################################################################
|
||||
# DATABASE STRUCTURE
|
||||
#############################################################################
|
||||
#
|
||||
# [SYSTEM] - System information
|
||||
# SYS|key|value|extra
|
||||
#
|
||||
# [USERS] - User accounts
|
||||
# USER|username|primary_domain|db_count|domain_count|disk_mb|home_dir
|
||||
#
|
||||
# [DATABASES] - Database information
|
||||
# DB|db_name|owner|primary_domain|size_mb|table_count
|
||||
#
|
||||
# [DOMAINS] - Domain to user mapping
|
||||
# DOMAIN|domain_name|owner|log_path|is_primary
|
||||
#
|
||||
# [DB_TABLES] - Database tables with plugin identification
|
||||
# TABLE|db_name|table_name|plugin|size_mb
|
||||
#
|
||||
# [WORDPRESS] - WordPress installations
|
||||
# WP|domain|path|db_name|version|plugin_count
|
||||
#
|
||||
# [LOGS] - Log file locations
|
||||
# LOG|type|domain|owner|path|size_mb
|
||||
#
|
||||
# [PROCESSES] - Top processes by user (at build time)
|
||||
# PROC|username|pid|cpu|mem|command
|
||||
#
|
||||
#############################################################################
|
||||
|
||||
#############################################################################
|
||||
# BUILD DATABASE
|
||||
#############################################################################
|
||||
|
||||
build_reference_database() {
|
||||
local start_time=$(date +%s)
|
||||
|
||||
print_info "Building system reference database..."
|
||||
echo "# System Reference Database" > "$SYSREF_DB"
|
||||
echo "# Generated: $(date)" >> "$SYSREF_DB"
|
||||
echo "# Format: Type|Field1|Field2|..." >> "$SYSREF_DB"
|
||||
echo "" >> "$SYSREF_DB"
|
||||
|
||||
# System information
|
||||
build_system_section
|
||||
|
||||
# User information
|
||||
build_users_section
|
||||
|
||||
# Database information
|
||||
build_databases_section
|
||||
|
||||
# Domain mapping
|
||||
build_domains_section
|
||||
|
||||
# WordPress detection
|
||||
build_wordpress_section
|
||||
|
||||
# Log file mapping
|
||||
build_logs_section
|
||||
|
||||
# Save timestamp
|
||||
date +%s > "$SYSREF_TIMESTAMP"
|
||||
|
||||
local end_time=$(date +%s)
|
||||
local duration=$((end_time - start_time))
|
||||
|
||||
print_success "Reference database built in ${duration}s"
|
||||
print_info "Database location: $SYSREF_DB"
|
||||
|
||||
# Show stats
|
||||
local total_lines=$(wc -l < "$SYSREF_DB")
|
||||
local user_count=$(grep -c "^USER|" "$SYSREF_DB" 2>/dev/null || echo 0)
|
||||
local db_count=$(grep -c "^DB|" "$SYSREF_DB" 2>/dev/null || echo 0)
|
||||
local domain_count=$(grep -c "^DOMAIN|" "$SYSREF_DB" 2>/dev/null || echo 0)
|
||||
local wp_count=$(grep -c "^WP|" "$SYSREF_DB" 2>/dev/null || echo 0)
|
||||
|
||||
echo " - $user_count users"
|
||||
echo " - $db_count databases"
|
||||
echo " - $domain_count domains"
|
||||
echo " - $wp_count WordPress sites"
|
||||
echo " - $total_lines total entries"
|
||||
}
|
||||
|
||||
build_system_section() {
|
||||
echo "[SYSTEM]" >> "$SYSREF_DB"
|
||||
echo "SYS|CONTROL_PANEL|$SYS_CONTROL_PANEL|$SYS_CONTROL_PANEL_VERSION" >> "$SYSREF_DB"
|
||||
echo "SYS|OS|$SYS_OS_TYPE|$SYS_OS_VERSION" >> "$SYSREF_DB"
|
||||
echo "SYS|WEB_SERVER|$SYS_WEB_SERVER|$SYS_WEB_SERVER_VERSION" >> "$SYSREF_DB"
|
||||
echo "SYS|DATABASE|$SYS_DB_TYPE|$SYS_DB_VERSION" >> "$SYSREF_DB"
|
||||
echo "SYS|LOG_DIR|$SYS_LOG_DIR|" >> "$SYSREF_DB"
|
||||
echo "SYS|USER_HOME|$SYS_USER_HOME_BASE|" >> "$SYSREF_DB"
|
||||
echo "SYS|CPU_CORES|$CPU_CORES|" >> "$SYSREF_DB"
|
||||
echo "SYS|HOSTNAME|$(hostname)|" >> "$SYSREF_DB"
|
||||
|
||||
# PHP versions
|
||||
for php_ver in "${SYS_PHP_VERSIONS[@]}"; do
|
||||
echo "SYS|PHP_VERSION|$php_ver|" >> "$SYSREF_DB"
|
||||
done
|
||||
|
||||
echo "" >> "$SYSREF_DB"
|
||||
}
|
||||
|
||||
build_users_section() {
|
||||
echo "[USERS]" >> "$SYSREF_DB"
|
||||
|
||||
local users=($(list_all_users))
|
||||
local total_users=${#users[@]}
|
||||
local current=0
|
||||
|
||||
for user in "${users[@]}"; do
|
||||
current=$((current + 1))
|
||||
show_progress $current $total_users "Indexing users..."
|
||||
|
||||
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)
|
||||
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}')
|
||||
fi
|
||||
|
||||
echo "USER|$user|$primary_domain|$db_count|$domain_count|$disk_mb|$home_dir" >> "$SYSREF_DB"
|
||||
done
|
||||
|
||||
finish_progress
|
||||
echo "" >> "$SYSREF_DB"
|
||||
}
|
||||
|
||||
build_databases_section() {
|
||||
echo "[DATABASES]" >> "$SYSREF_DB"
|
||||
|
||||
if [ "$SYS_DB_TYPE" = "none" ]; then
|
||||
echo "" >> "$SYSREF_DB"
|
||||
return
|
||||
fi
|
||||
|
||||
local all_dbs=$(mysql -Ns -e "SHOW DATABASES" 2>/dev/null | grep -v "^information_schema$\|^mysql$\|^performance_schema$\|^sys$" || true)
|
||||
local total_dbs=$(echo "$all_dbs" | wc -l)
|
||||
local current=0
|
||||
|
||||
for db in $all_dbs; do
|
||||
current=$((current + 1))
|
||||
show_progress $current $total_dbs "Indexing databases..."
|
||||
|
||||
local owner=$(get_database_owner "$db")
|
||||
local domain=$(get_database_domain "$db")
|
||||
|
||||
local size_mb=$(mysql -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
|
||||
|
||||
local table_count=$(mysql -Ns "$db" -e "SHOW TABLES" 2>/dev/null | wc -l)
|
||||
|
||||
echo "DB|$db|$owner|$domain|$size_mb|$table_count" >> "$SYSREF_DB"
|
||||
done
|
||||
|
||||
finish_progress
|
||||
echo "" >> "$SYSREF_DB"
|
||||
}
|
||||
|
||||
build_domains_section() {
|
||||
echo "[DOMAINS]" >> "$SYSREF_DB"
|
||||
|
||||
# Track domains we've already added
|
||||
declare -A seen_domains
|
||||
|
||||
local users=($(list_all_users))
|
||||
|
||||
# Get detailed domain information from cPanel userdata (if available)
|
||||
for user in "${users[@]}"; do
|
||||
local userdata_dir="/var/cpanel/userdata/${user}"
|
||||
|
||||
if [ -d "$userdata_dir" ]; then
|
||||
# Parse each domain configuration file in userdata
|
||||
for config_file in "$userdata_dir"/*; do
|
||||
[ ! -f "$config_file" ] && continue
|
||||
local basename=$(basename "$config_file")
|
||||
|
||||
# Skip cache files and special files
|
||||
[[ "$basename" =~ \.cache$ ]] && continue
|
||||
[[ "$basename" =~ \.yaml$ ]] && continue
|
||||
[[ "$basename" =~ \.json$ ]] && continue
|
||||
[[ "$basename" =~ ^main ]] && continue
|
||||
[[ "$basename" =~ ^cache$ ]] && continue
|
||||
[[ "$basename" =~ _SSL$ ]] && continue
|
||||
|
||||
# Extract domain info from config
|
||||
local domain="$basename"
|
||||
local doc_root=$(grep "^documentroot:" "$config_file" | awk '{print $2}' || true)
|
||||
local log_path=$(grep "target:.*domlogs" "$config_file" | head -1 | awk '{print $2}' || true)
|
||||
local server_alias=$(grep "^serveralias:" "$config_file" | awk '{print $2}' || true)
|
||||
local php_version=$(grep "^phpversion:" "$config_file" | awk '{print $2}' || true)
|
||||
|
||||
# Determine if primary domain
|
||||
local is_primary="no"
|
||||
local primary_domain=$(get_user_domains "$user" | head -1)
|
||||
[ "$domain" = "$primary_domain" ] && is_primary="yes"
|
||||
|
||||
# Determine domain type (addon, parked, subdomain, primary)
|
||||
local domain_type="addon"
|
||||
if [ "$is_primary" = "yes" ]; then
|
||||
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)
|
||||
if [ "$base_domain" = "$primary_domain" ]; then
|
||||
domain_type="subdomain"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Format: DOMAIN|domain|owner|doc_root|log_path|php_version|is_primary|type|aliases
|
||||
echo "DOMAIN|$domain|$user|$doc_root|$log_path|$php_version|$is_primary|$domain_type|$server_alias" >> "$SYSREF_DB"
|
||||
seen_domains["$domain"]=1
|
||||
|
||||
# Also add aliases as separate entries
|
||||
if [ -n "$server_alias" ]; then
|
||||
for alias in $server_alias; do
|
||||
[ -z "$alias" ] && continue
|
||||
[ -n "${seen_domains[$alias]:-}" ] && continue
|
||||
|
||||
# Alias points to same document root and logs
|
||||
echo "DOMAIN|$alias|$user|$doc_root|$log_path|$php_version|no|alias|$domain" >> "$SYSREF_DB"
|
||||
seen_domains["$alias"]=1
|
||||
done
|
||||
fi
|
||||
done
|
||||
else
|
||||
# Fallback for non-cPanel or if userdata not available
|
||||
local primary_domain=$(get_user_domains "$user" | head -1)
|
||||
local all_domains=$(get_user_domains "$user")
|
||||
|
||||
for domain in $all_domains; do
|
||||
[ -z "$domain" ] && continue
|
||||
[ -n "${seen_domains[$domain]:-}" ] && continue
|
||||
|
||||
local is_primary="no"
|
||||
[ "$domain" = "$primary_domain" ] && is_primary="yes"
|
||||
|
||||
# Find log path
|
||||
local log_path="${SYS_LOG_DIR}/${domain}"
|
||||
[ ! -f "$log_path" ] && log_path="${SYS_LOG_DIR}/${domain}.log"
|
||||
|
||||
# Simple format for non-cPanel
|
||||
echo "DOMAIN|$domain|$user||$log_path||$is_primary|local|" >> "$SYSREF_DB"
|
||||
seen_domains["$domain"]=1
|
||||
done
|
||||
fi
|
||||
done
|
||||
|
||||
# Check /etc/localdomains (cPanel local domains not yet added)
|
||||
if [ -f "/etc/localdomains" ]; then
|
||||
while read -r domain; do
|
||||
[ -z "$domain" ] && continue
|
||||
[ -n "${seen_domains[$domain]:-}" ] && continue
|
||||
|
||||
local owner=$(grep "^${domain}:" /etc/trueuserdomains 2>/dev/null | cut -d: -f2 | xargs || true)
|
||||
[ -z "$owner" ] && owner="unknown"
|
||||
|
||||
local log_path="${SYS_LOG_DIR}/${domain}"
|
||||
echo "DOMAIN|$domain|$owner||$log_path||unknown|local|" >> "$SYSREF_DB"
|
||||
seen_domains["$domain"]=1
|
||||
done < /etc/localdomains
|
||||
fi
|
||||
|
||||
# Check /etc/remotedomains (cPanel remote MX domains)
|
||||
if [ -f "/etc/remotedomains" ]; then
|
||||
while read -r domain; do
|
||||
[ -z "$domain" ] && continue
|
||||
[ -n "${seen_domains[$domain]:-}" ] && continue
|
||||
|
||||
local owner=$(grep "^${domain}:" /etc/trueuserdomains 2>/dev/null | cut -d: -f2 | xargs || true)
|
||||
[ -z "$owner" ] && owner="unknown"
|
||||
|
||||
echo "DOMAIN|$domain|$owner||||unknown|remote|" >> "$SYSREF_DB"
|
||||
seen_domains["$domain"]=1
|
||||
done < /etc/remotedomains
|
||||
fi
|
||||
|
||||
echo "" >> "$SYSREF_DB"
|
||||
}
|
||||
|
||||
build_wordpress_section() {
|
||||
echo "[WORDPRESS]" >> "$SYSREF_DB"
|
||||
|
||||
# Find all wp-config.php files
|
||||
local wp_configs=$(find $SYS_USER_HOME_BASE -name "wp-config.php" -type f 2>/dev/null)
|
||||
|
||||
for wp_config in $wp_configs; do
|
||||
local wp_dir=$(dirname "$wp_config")
|
||||
|
||||
# Extract username from path (/home/username/...)
|
||||
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/||")
|
||||
local domain=""
|
||||
|
||||
# Check for common domain folder patterns
|
||||
if [[ "$path_after_home" == public_html ]]; then
|
||||
# This is the primary domain - get it from user info
|
||||
domain=$(grep "^USER|${username}|" "$SYSREF_DB" | cut -d'|' -f3 || true)
|
||||
elif [[ "$path_after_home" =~ ^public_html/(.+) ]]; then
|
||||
# Could be subdomain or subdirectory - extract folder name
|
||||
local folder=$(echo "$path_after_home" | cut -d'/' -f2)
|
||||
domain="${folder}"
|
||||
else
|
||||
# Might be addon/parked domain with own directory
|
||||
domain=$(echo "$path_after_home" | cut -d'/' -f1)
|
||||
fi
|
||||
|
||||
# Try to get actual domain from WP database options (more reliable)
|
||||
local db_name=$(grep "DB_NAME" "$wp_config" | grep -oP "'[^']+'" | tail -1 | tr -d "'" || true)
|
||||
local db_user=$(grep "DB_USER" "$wp_config" | grep -oP "'[^']+'" | tail -1 | tr -d "'" || true)
|
||||
local db_host=$(grep "DB_HOST" "$wp_config" | grep -oP "'[^']+'" | tail -1 | tr -d "'" || true)
|
||||
|
||||
# Try to get site URL from wp-config defines
|
||||
local site_url=$(grep -E "WP_SITEURL|WP_HOME" "$wp_config" | head -1 | grep -oP "https?://\K[^/'\"']+" || true)
|
||||
if [ -n "$site_url" ]; then
|
||||
domain="$site_url"
|
||||
fi
|
||||
|
||||
# Get WP version
|
||||
local version=""
|
||||
if [ -f "${wp_dir}/wp-includes/version.php" ]; then
|
||||
version=$(grep "\$wp_version" "${wp_dir}/wp-includes/version.php" | grep -oP "'\K[^']+" | head -1 || true)
|
||||
fi
|
||||
|
||||
# Count plugins
|
||||
local plugin_count=0
|
||||
if [ -d "${wp_dir}/wp-content/plugins" ]; then
|
||||
plugin_count=$(find "${wp_dir}/wp-content/plugins" -maxdepth 1 -type d 2>/dev/null | wc -l)
|
||||
plugin_count=$((plugin_count - 1)) # Exclude parent dir
|
||||
fi
|
||||
|
||||
# Count themes
|
||||
local theme_count=0
|
||||
if [ -d "${wp_dir}/wp-content/themes" ]; then
|
||||
theme_count=$(find "${wp_dir}/wp-content/themes" -maxdepth 1 -type d 2>/dev/null | wc -l)
|
||||
theme_count=$((theme_count - 1)) # Exclude parent dir
|
||||
fi
|
||||
|
||||
# Format: WP|domain|owner|path|db_name|db_user|version|plugin_count|theme_count
|
||||
echo "WP|$domain|$username|$wp_dir|$db_name|$db_user|$version|$plugin_count|$theme_count" >> "$SYSREF_DB"
|
||||
done
|
||||
|
||||
echo "" >> "$SYSREF_DB"
|
||||
}
|
||||
|
||||
build_logs_section() {
|
||||
echo "[LOGS]" >> "$SYSREF_DB"
|
||||
|
||||
# Apache/Web server logs
|
||||
# Temporarily disabled - causes hangs with large log directories
|
||||
# TODO: Implement log scanning with progress indicator and limits
|
||||
|
||||
echo "" >> "$SYSREF_DB"
|
||||
}
|
||||
|
||||
#############################################################################
|
||||
# QUERY DATABASE
|
||||
#############################################################################
|
||||
|
||||
# Quick lookup functions - optimized for speed
|
||||
|
||||
# Get user info
|
||||
db_get_user() {
|
||||
local username="$1"
|
||||
grep "^USER|${username}|" "$SYSREF_DB" 2>/dev/null
|
||||
}
|
||||
|
||||
# Get all users
|
||||
db_get_all_users() {
|
||||
grep "^USER|" "$SYSREF_DB" 2>/dev/null
|
||||
}
|
||||
|
||||
# Get user's databases
|
||||
db_get_user_databases() {
|
||||
local username="$1"
|
||||
grep "^DB|.*|${username}|" "$SYSREF_DB" 2>/dev/null
|
||||
}
|
||||
|
||||
# Get user's domains
|
||||
db_get_user_domains() {
|
||||
local username="$1"
|
||||
grep "^DOMAIN|.*|${username}|" "$SYSREF_DB" 2>/dev/null
|
||||
}
|
||||
|
||||
# Get database owner
|
||||
db_get_database_owner() {
|
||||
local db_name="$1"
|
||||
grep "^DB|${db_name}|" "$SYSREF_DB" 2>/dev/null | cut -d'|' -f3
|
||||
}
|
||||
|
||||
# Get all WordPress sites
|
||||
db_get_all_wordpress() {
|
||||
grep "^WP|" "$SYSREF_DB" 2>/dev/null
|
||||
}
|
||||
|
||||
# Get WordPress site by domain
|
||||
db_get_wordpress_by_domain() {
|
||||
local domain="$1"
|
||||
grep "^WP|${domain}|" "$SYSREF_DB" 2>/dev/null
|
||||
}
|
||||
|
||||
# Get log files for domain
|
||||
db_get_domain_logs() {
|
||||
local domain="$1"
|
||||
grep "^LOG|.*|${domain}|" "$SYSREF_DB" 2>/dev/null
|
||||
}
|
||||
|
||||
# Get system info
|
||||
db_get_system_info() {
|
||||
local key="$1"
|
||||
if [ -n "$key" ]; then
|
||||
grep "^SYS|${key}|" "$SYSREF_DB" 2>/dev/null | cut -d'|' -f3-
|
||||
else
|
||||
grep "^SYS|" "$SYSREF_DB" 2>/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
# Get health/session metric
|
||||
db_get_health_metric() {
|
||||
local metric="$1"
|
||||
grep "^HEALTH|${metric}|" "$SYSREF_DB" 2>/dev/null | cut -d'|' -f3 | head -1
|
||||
}
|
||||
|
||||
# Check if system is under load (for cross-module intelligence)
|
||||
db_is_system_under_load() {
|
||||
local cpu_load=$(db_get_health_metric "CPU_LOAD_1MIN")
|
||||
local cpu_cores=$(db_get_health_metric "CPU_CORES")
|
||||
local mem_percent=$(db_get_health_metric "MEMORY_USED_PERCENT")
|
||||
|
||||
# Consider system under load if CPU > 80% or memory > 90%
|
||||
if [ -n "$cpu_load" ] && [ -n "$cpu_cores" ]; then
|
||||
local load_percent=$(echo "scale=0; ($cpu_load / $cpu_cores) * 100" | bc 2>/dev/null || echo "0")
|
||||
if [ "$load_percent" -gt 80 ] || [ "${mem_percent:-0}" -gt 90 ]; then
|
||||
return 0 # True - system is under load
|
||||
fi
|
||||
fi
|
||||
return 1 # False - system not under load
|
||||
}
|
||||
|
||||
# Check if network has issues (for cross-module intelligence)
|
||||
db_has_network_issues() {
|
||||
local tcp_retrans=$(db_get_health_metric "TCP_RETRANS_PERCENT")
|
||||
local rx_errors=$(db_get_health_metric "NETWORK_RX_ERRORS")
|
||||
local tx_errors=$(db_get_health_metric "NETWORK_TX_ERRORS")
|
||||
|
||||
# Consider network problematic if retrans > 5% or errors > 100
|
||||
if [ -n "$tcp_retrans" ]; then
|
||||
if (( $(echo "$tcp_retrans > 5" | bc -l 2>/dev/null || echo 0) )) || \
|
||||
[ "${rx_errors:-0}" -gt 100 ] || [ "${tx_errors:-0}" -gt 100 ]; then
|
||||
return 0 # True - network has issues
|
||||
fi
|
||||
fi
|
||||
return 1 # False - network OK
|
||||
}
|
||||
|
||||
# Check if under attack (for cross-module intelligence)
|
||||
db_is_under_attack() {
|
||||
local ssh_today=$(db_get_health_metric "SSH_ATTACKS_TODAY")
|
||||
# Consider under attack if > 100 failed SSH attempts today
|
||||
if [ "${ssh_today:-0}" -gt 100 ]; then
|
||||
return 0 # True - under attack
|
||||
fi
|
||||
return 1 # False - not under attack
|
||||
}
|
||||
|
||||
# Get all health metrics (for display/debugging)
|
||||
db_get_all_health() {
|
||||
grep "^HEALTH|" "$SYSREF_DB" 2>/dev/null
|
||||
}
|
||||
|
||||
#############################################################################
|
||||
# DATABASE MANAGEMENT
|
||||
#############################################################################
|
||||
|
||||
# Check if database exists and is fresh (< 1 hour old)
|
||||
db_is_fresh() {
|
||||
if [ ! -f "$SYSREF_DB" ] || [ ! -f "$SYSREF_TIMESTAMP" ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
local db_age=$(( $(date +%s) - $(cat "$SYSREF_TIMESTAMP") ))
|
||||
local max_age=3600 # 1 hour
|
||||
|
||||
[ $db_age -lt $max_age ]
|
||||
}
|
||||
|
||||
# Rebuild database if needed
|
||||
db_ensure_fresh() {
|
||||
if ! db_is_fresh; then
|
||||
print_info "Reference database is stale or missing, rebuilding..."
|
||||
build_reference_database
|
||||
fi
|
||||
}
|
||||
|
||||
# Force rebuild
|
||||
db_rebuild() {
|
||||
build_reference_database
|
||||
}
|
||||
|
||||
# Show database stats
|
||||
db_show_stats() {
|
||||
if [ ! -f "$SYSREF_DB" ]; then
|
||||
print_error "Reference database does not exist. Run 'build' first."
|
||||
return 1
|
||||
fi
|
||||
|
||||
print_banner "System Reference Database Stats"
|
||||
|
||||
local db_age=$(( $(date +%s) - $(cat "$SYSREF_TIMESTAMP") ))
|
||||
local age_str=$(format_duration $db_age)
|
||||
|
||||
echo "Database Age: $age_str"
|
||||
echo "Database Size: $(du -sh "$SYSREF_DB" | awk '{print $1}')"
|
||||
echo ""
|
||||
|
||||
echo -e "${BOLD}Content Summary:${NC}"
|
||||
printf " %-20s %s\n" "Users:" "$(grep -c '^USER|' "$SYSREF_DB" 2>/dev/null || echo 0)"
|
||||
printf " %-20s %s\n" "Databases:" "$(grep -c '^DB|' "$SYSREF_DB" 2>/dev/null || echo 0)"
|
||||
printf " %-20s %s\n" "Domains:" "$(grep -c '^DOMAIN|' "$SYSREF_DB" 2>/dev/null || echo 0)"
|
||||
printf " %-20s %s\n" "WordPress Sites:" "$(grep -c '^WP|' "$SYSREF_DB" 2>/dev/null || echo 0)"
|
||||
printf " %-20s %s\n" "Log Files:" "$(grep -c '^LOG|' "$SYSREF_DB" 2>/dev/null || echo 0)"
|
||||
printf " %-20s %s\n" "Total Entries:" "$(wc -l < "$SYSREF_DB")"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Export functions
|
||||
export -f db_get_user
|
||||
export -f db_get_all_users
|
||||
export -f db_get_user_databases
|
||||
export -f db_get_user_domains
|
||||
export -f db_get_database_owner
|
||||
export -f db_get_all_wordpress
|
||||
export -f db_get_system_info
|
||||
export -f db_get_health_metric
|
||||
export -f db_is_system_under_load
|
||||
export -f db_has_network_issues
|
||||
export -f db_is_under_attack
|
||||
export -f db_get_all_health
|
||||
export -f db_is_fresh
|
||||
export -f db_ensure_fresh
|
||||
export -f db_rebuild
|
||||
Executable
+437
@@ -0,0 +1,437 @@
|
||||
#!/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)"
|
||||
source "$SCRIPT_DIR/common-functions.sh"
|
||||
fi
|
||||
|
||||
# Global variables (session-only)
|
||||
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=""
|
||||
|
||||
#############################################################################
|
||||
# CONTROL PANEL DETECTION
|
||||
#############################################################################
|
||||
|
||||
detect_control_panel() {
|
||||
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)
|
||||
SYS_LOG_DIR="/var/www/vhosts/system"
|
||||
SYS_USER_HOME_BASE="/var/www/vhosts"
|
||||
|
||||
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
|
||||
SYS_LOG_DIR="/home"
|
||||
SYS_USER_HOME_BASE="/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() {
|
||||
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() {
|
||||
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.]+')
|
||||
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() {
|
||||
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() {
|
||||
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)
|
||||
if [ "$SYS_CONTROL_PANEL" = "cpanel" ]; then
|
||||
for php_bin in /opt/cpanel/ea-php*/root/usr/bin/php; do
|
||||
if [ -x "$php_bin" ]; then
|
||||
local version=$($php_bin -v 2>/dev/null | grep -oP '^PHP \K[\d.]+' | head -1)
|
||||
[ -n "$version" ] && SYS_PHP_VERSIONS+=("$version")
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Check alt-php versions (CloudLinux)
|
||||
for php_bin in /opt/alt/php*/usr/bin/php; do
|
||||
if [ -x "$php_bin" ]; then
|
||||
local version=$($php_bin -v 2>/dev/null | grep -oP '^PHP \K[\d.]+' | head -1)
|
||||
[ -n "$version" ] && SYS_PHP_VERSIONS+=("$version")
|
||||
fi
|
||||
done
|
||||
|
||||
# Remove duplicates
|
||||
SYS_PHP_VERSIONS=($(echo "${SYS_PHP_VERSIONS[@]}" | tr ' ' '\n' | sort -u))
|
||||
|
||||
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
|
||||
if systemctl is-active --quiet railgun 2>/dev/null || service railgun status 2>/dev/null | grep -q running; then
|
||||
SYS_CLOUDFLARE_ACTIVE="yes"
|
||||
print_info "Cloudflare Railgun detected"
|
||||
fi
|
||||
}
|
||||
|
||||
#############################################################################
|
||||
# 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
|
||||
local mem_total=$(free -h | awk '/^Mem:/ {print $2}')
|
||||
local mem_used=$(free -h | awk '/^Mem:/ {print $3}')
|
||||
local mem_percent=$(free | awk '/^Mem:/ {printf "%.0f", $3/$2*100}')
|
||||
local mem_available=$(free -h | awk '/^Mem:/ {print $7}')
|
||||
|
||||
# Swap Information
|
||||
local swap_total=$(free -h | awk '/^Swap:/ {print $2}')
|
||||
local swap_used=$(free -h | 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
|
||||
local disk_root_total=$(df -h / | awk 'NR==2 {print $2}')
|
||||
local disk_root_used=$(df -h / | awk 'NR==2 {print $3}')
|
||||
local disk_root_percent=$(df -h / | awk 'NR==2 {print $5}')
|
||||
|
||||
# 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
|
||||
get_system_resources
|
||||
|
||||
# Mark as initialized
|
||||
export SYS_DETECTION_COMPLETE="yes"
|
||||
}
|
||||
|
||||
# Auto-initialize if not already done (when sourced)
|
||||
if [ -z "${SYS_DETECTION_COMPLETE:-}" ]; then
|
||||
# Just run initialization - output suppression was breaking variable assignment
|
||||
initialize_system_detection
|
||||
fi
|
||||
Executable
+646
@@ -0,0 +1,646 @@
|
||||
#!/bin/bash
|
||||
|
||||
#############################################################################
|
||||
# User Manager Library
|
||||
# Dynamic user listing and management for cPanel/Plesk/InterWorx
|
||||
#############################################################################
|
||||
|
||||
# Source dependencies
|
||||
if [ -z "$TOOLKIT_BASE_DIR" ]; then
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "$SCRIPT_DIR/common-functions.sh"
|
||||
source "$SCRIPT_DIR/system-detect.sh"
|
||||
fi
|
||||
|
||||
#############################################################################
|
||||
# USER LISTING (Control Panel Specific)
|
||||
#############################################################################
|
||||
|
||||
list_all_users() {
|
||||
case "$SYS_CONTROL_PANEL" in
|
||||
cpanel)
|
||||
list_cpanel_users
|
||||
;;
|
||||
plesk)
|
||||
list_plesk_users
|
||||
;;
|
||||
interworx)
|
||||
list_interworx_users
|
||||
;;
|
||||
*)
|
||||
list_system_users
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# cPanel user listing
|
||||
list_cpanel_users() {
|
||||
if [ -d "/var/cpanel/users" ]; then
|
||||
ls /var/cpanel/users/ 2>/dev/null || true
|
||||
else
|
||||
# Fallback: parse /etc/trueuserdomains
|
||||
awk -F: '{print $2}' /etc/trueuserdomains 2>/dev/null | sort -u || true
|
||||
fi
|
||||
}
|
||||
|
||||
# Plesk user listing
|
||||
list_plesk_users() {
|
||||
if command_exists mysql && [ -f /etc/psa/.psa.shadow ]; then
|
||||
mysql -Ns psa -e "SELECT login FROM sys_users WHERE type='user'" 2>/dev/null
|
||||
else
|
||||
# Fallback: list directories
|
||||
find /var/www/vhosts -maxdepth 1 -type d -printf "%f\n" 2>/dev/null | grep -v "^system$\|^default$\|^chroot$"
|
||||
fi
|
||||
}
|
||||
|
||||
# InterWorx user listing
|
||||
list_interworx_users() {
|
||||
if [ -x "/usr/local/interworx/bin/listaccounts.pex" ]; then
|
||||
/usr/local/interworx/bin/listaccounts.pex --output user 2>/dev/null
|
||||
else
|
||||
# Fallback: parse InterWorx config
|
||||
find /home -maxdepth 1 -type d -name "*.conf" 2>/dev/null | xargs -I {} basename {} .conf
|
||||
fi
|
||||
}
|
||||
|
||||
# System users (no control panel)
|
||||
list_system_users() {
|
||||
# List users with home directories in /home and valid shells
|
||||
awk -F: '$6 ~ /^\/home\// && $7 !~ /nologin|false/ {print $1}' /etc/passwd
|
||||
}
|
||||
|
||||
#############################################################################
|
||||
# USER INFORMATION
|
||||
#############################################################################
|
||||
|
||||
get_user_info() {
|
||||
local username="$1"
|
||||
local info_file="${TEMP_SESSION_DIR}/user_${username}_info.tmp"
|
||||
|
||||
case "$SYS_CONTROL_PANEL" in
|
||||
cpanel)
|
||||
get_cpanel_user_info "$username" > "$info_file"
|
||||
;;
|
||||
plesk)
|
||||
get_plesk_user_info "$username" > "$info_file"
|
||||
;;
|
||||
interworx)
|
||||
get_interworx_user_info "$username" > "$info_file"
|
||||
;;
|
||||
*)
|
||||
get_system_user_info "$username" > "$info_file"
|
||||
;;
|
||||
esac
|
||||
|
||||
cat "$info_file"
|
||||
}
|
||||
|
||||
# cPanel user info
|
||||
get_cpanel_user_info() {
|
||||
local username="$1"
|
||||
local user_file="/var/cpanel/users/${username}"
|
||||
|
||||
if [ ! -f "$user_file" ]; then
|
||||
echo "USER_EXISTS=no"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Parse cPanel user file
|
||||
local primary_domain=$(grep "^DNS=" "$user_file" | cut -d= -f2)
|
||||
local email=$(grep "^CONTACTEMAIL=" "$user_file" | cut -d= -f2)
|
||||
|
||||
# cPanel doesn't store HOMEDIR in user file - it's always /home/username
|
||||
local home_dir="/home/${username}"
|
||||
|
||||
# Get addon/parked domains
|
||||
local all_domains=$(grep "^DNS" "$user_file" | cut -d= -f2 | tr '\n' ' ')
|
||||
|
||||
# Get disk usage
|
||||
local disk_used=$(du -sh "$home_dir" 2>/dev/null | awk '{print $1}')
|
||||
|
||||
echo "USER_EXISTS=yes"
|
||||
echo "USERNAME=$username"
|
||||
echo "PRIMARY_DOMAIN=$primary_domain"
|
||||
echo "ALL_DOMAINS=$all_domains"
|
||||
echo "EMAIL=$email"
|
||||
echo "HOME_DIR=$home_dir"
|
||||
echo "DISK_USED=$disk_used"
|
||||
}
|
||||
|
||||
# Plesk user info
|
||||
get_plesk_user_info() {
|
||||
local username="$1"
|
||||
|
||||
if ! command_exists mysql || [ ! -f /etc/psa/.psa.shadow ]; then
|
||||
echo "USER_EXISTS=no"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local home_dir="/var/www/vhosts/${username}"
|
||||
local primary_domain=$(mysql -Ns psa -e "SELECT name FROM domains WHERE id IN (SELECT domain_id FROM sys_users WHERE login='$username')" 2>/dev/null | head -1)
|
||||
local disk_used=$(du -sh "$home_dir" 2>/dev/null | awk '{print $1}')
|
||||
|
||||
echo "USER_EXISTS=yes"
|
||||
echo "USERNAME=$username"
|
||||
echo "PRIMARY_DOMAIN=$primary_domain"
|
||||
echo "HOME_DIR=$home_dir"
|
||||
echo "DISK_USED=$disk_used"
|
||||
}
|
||||
|
||||
# InterWorx user info
|
||||
get_interworx_user_info() {
|
||||
local username="$1"
|
||||
local home_dir="/home/${username}"
|
||||
|
||||
if [ ! -d "$home_dir" ]; then
|
||||
echo "USER_EXISTS=no"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local disk_used=$(du -sh "$home_dir" 2>/dev/null | awk '{print $1}')
|
||||
|
||||
echo "USER_EXISTS=yes"
|
||||
echo "USERNAME=$username"
|
||||
echo "HOME_DIR=$home_dir"
|
||||
echo "DISK_USED=$disk_used"
|
||||
}
|
||||
|
||||
# System user info (no control panel)
|
||||
get_system_user_info() {
|
||||
local username="$1"
|
||||
local user_entry=$(getent passwd "$username")
|
||||
|
||||
if [ -z "$user_entry" ]; then
|
||||
echo "USER_EXISTS=no"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local home_dir=$(echo "$user_entry" | cut -d: -f6)
|
||||
local disk_used=$(du -sh "$home_dir" 2>/dev/null | awk '{print $1}')
|
||||
|
||||
echo "USER_EXISTS=yes"
|
||||
echo "USERNAME=$username"
|
||||
echo "HOME_DIR=$home_dir"
|
||||
echo "DISK_USED=$disk_used"
|
||||
}
|
||||
|
||||
#############################################################################
|
||||
# USER DOMAINS
|
||||
#############################################################################
|
||||
|
||||
get_user_domains() {
|
||||
local username="$1"
|
||||
|
||||
case "$SYS_CONTROL_PANEL" in
|
||||
cpanel)
|
||||
get_cpanel_user_domains "$username"
|
||||
;;
|
||||
plesk)
|
||||
get_plesk_user_domains "$username"
|
||||
;;
|
||||
interworx)
|
||||
get_interworx_user_domains "$username"
|
||||
;;
|
||||
*)
|
||||
echo ""
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
get_cpanel_user_domains() {
|
||||
local username="$1"
|
||||
|
||||
# Primary domain (format: domain: user)
|
||||
grep ": ${username}$" /etc/trueuserdomains 2>/dev/null | cut -d: -f1 || true
|
||||
|
||||
# Addon domains
|
||||
if [ -f "/etc/userdatadomains" ]; then
|
||||
grep "==${username}$" /etc/userdatadomains 2>/dev/null | cut -d: -f1 || true
|
||||
fi
|
||||
}
|
||||
|
||||
get_plesk_user_domains() {
|
||||
local username="$1"
|
||||
|
||||
if command_exists mysql && [ -f /etc/psa/.psa.shadow ]; then
|
||||
mysql -Ns psa -e "SELECT d.name FROM domains d JOIN sys_users u ON d.id=u.domain_id WHERE u.login='$username'" 2>/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
get_interworx_user_domains() {
|
||||
local username="$1"
|
||||
|
||||
if [ -x "/usr/local/interworx/bin/listaccounts.pex" ]; then
|
||||
/usr/local/interworx/bin/listaccounts.pex --user "$username" --output domain 2>/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
#############################################################################
|
||||
# USER DATABASES
|
||||
#############################################################################
|
||||
|
||||
get_user_databases() {
|
||||
local username="$1"
|
||||
|
||||
case "$SYS_CONTROL_PANEL" in
|
||||
cpanel)
|
||||
get_cpanel_user_databases "$username"
|
||||
;;
|
||||
plesk)
|
||||
get_plesk_user_databases "$username"
|
||||
;;
|
||||
interworx)
|
||||
get_interworx_user_databases "$username"
|
||||
;;
|
||||
*)
|
||||
# Try to find databases matching username pattern
|
||||
mysql -e "SHOW DATABASES" 2>/dev/null | grep "^${username}_"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
get_cpanel_user_databases() {
|
||||
local username="$1"
|
||||
|
||||
# cPanel databases typically follow pattern: username_dbname
|
||||
mysql -e "SHOW DATABASES" 2>/dev/null | grep "^${username}_" || true
|
||||
}
|
||||
|
||||
get_plesk_user_databases() {
|
||||
local username="$1"
|
||||
|
||||
if command_exists mysql && [ -f /etc/psa/.psa.shadow ]; then
|
||||
mysql -Ns psa -e "SELECT db.name FROM data_bases db JOIN domains d ON db.dom_id=d.id JOIN sys_users u ON d.id=u.domain_id WHERE u.login='$username'" 2>/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
get_interworx_user_databases() {
|
||||
local username="$1"
|
||||
|
||||
# InterWorx databases typically follow pattern: username_dbname
|
||||
mysql -e "SHOW DATABASES" 2>/dev/null | grep "^${username}_" || true
|
||||
}
|
||||
|
||||
#############################################################################
|
||||
# USER LOG FILES
|
||||
#############################################################################
|
||||
|
||||
get_user_log_files() {
|
||||
local username="$1"
|
||||
local domains=$(get_user_domains "$username")
|
||||
|
||||
case "$SYS_CONTROL_PANEL" in
|
||||
cpanel)
|
||||
for domain in $domains; do
|
||||
echo "${SYS_LOG_DIR}/${domain}"
|
||||
echo "${SYS_LOG_DIR}/${domain}-ssl_log"
|
||||
done
|
||||
;;
|
||||
plesk)
|
||||
echo "/var/www/vhosts/${username}/statistics/logs/access_log"
|
||||
echo "/var/www/vhosts/${username}/statistics/logs/error_log"
|
||||
for domain in $domains; do
|
||||
echo "/var/www/vhosts/${domain}/statistics/logs/access_log"
|
||||
echo "/var/www/vhosts/${domain}/statistics/logs/error_log"
|
||||
done
|
||||
;;
|
||||
interworx)
|
||||
for domain in $domains; do
|
||||
echo "/home/${username}/var/${domain}/logs/access_log"
|
||||
echo "/home/${username}/var/${domain}/logs/error_log"
|
||||
done
|
||||
;;
|
||||
esac | grep -v "^$" | sort -u
|
||||
}
|
||||
|
||||
#############################################################################
|
||||
# USER SELECTION MENU
|
||||
#############################################################################
|
||||
|
||||
select_user_interactive() {
|
||||
local prompt="${1:-Select a user}"
|
||||
local users=($(list_all_users))
|
||||
local total_users=${#users[@]}
|
||||
|
||||
if [ $total_users -eq 0 ]; then
|
||||
print_error "No users found" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Build user info cache to avoid repeated get_user_domains calls
|
||||
declare -A user_primary_domain
|
||||
declare -A user_domain_count
|
||||
|
||||
for user in "${users[@]}"; do
|
||||
local domains=$(get_user_domains "$user" 2>/dev/null | grep -v "^$")
|
||||
if [ -n "$domains" ]; then
|
||||
user_domain_count["$user"]=$(echo "$domains" | wc -l)
|
||||
user_primary_domain["$user"]=$(echo "$domains" | head -1)
|
||||
else
|
||||
user_domain_count["$user"]=0
|
||||
user_primary_domain["$user"]="(no domains)"
|
||||
fi
|
||||
done
|
||||
|
||||
# Send all display output to stderr so it shows on screen (not captured by $(...))
|
||||
{
|
||||
echo ""
|
||||
print_section "$prompt"
|
||||
echo ""
|
||||
echo "Found $total_users user(s) on this server"
|
||||
echo "-------------------------------------------------------------------------------"
|
||||
|
||||
# Auto-show list if 10 or fewer users
|
||||
if [ $total_users -le 10 ]; then
|
||||
echo ""
|
||||
for user in "${users[@]}"; do
|
||||
echo -e " ${GREEN}$user${NC} - ${user_primary_domain[$user]} (${user_domain_count[$user]} domains)"
|
||||
done
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "-------------------------------------------------------------------------------"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
if [ $total_users -gt 10 ]; then
|
||||
echo " L - List all $total_users users"
|
||||
fi
|
||||
echo " S [text] - Search/filter users (e.g., 's pick' or 's example.com')"
|
||||
echo " [username] - Enter exact username"
|
||||
echo " A - All users (system-wide scan)"
|
||||
echo " 0 - Cancel/Go back"
|
||||
echo ""
|
||||
} >&2
|
||||
|
||||
read -p "Enter choice> " choice
|
||||
|
||||
case "$choice" in
|
||||
[Aa]|ALL|all)
|
||||
echo "ALL"
|
||||
return 0
|
||||
;;
|
||||
[Ss]\ *|[Ss])
|
||||
# Search mode
|
||||
local search_term="${choice#[Ss] }" # Remove 'S ' prefix
|
||||
search_term="${search_term#[Ss]}" # Remove 'S' if no space
|
||||
|
||||
if [ -z "$search_term" ]; then
|
||||
# No search term provided, ask for it
|
||||
read -p "Enter search term (username or domain)> " search_term >&2
|
||||
fi
|
||||
|
||||
if [ -z "$search_term" ]; then
|
||||
print_error "No search term provided" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Search and build match array
|
||||
local -a matched_users
|
||||
local -a menu_items
|
||||
|
||||
for user in "${users[@]}"; do
|
||||
# Case-insensitive partial match in username or domain
|
||||
if [[ "${user,,}" == *"${search_term,,}"* ]] || [[ "${user_primary_domain[$user],,}" == *"${search_term,,}"* ]]; then
|
||||
matched_users+=("$user")
|
||||
menu_items+=("$user - ${user_primary_domain[$user]} (${user_domain_count[$user]} domains)")
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${#matched_users[@]} -eq 0 ]; then
|
||||
print_error "No users found matching '$search_term'" >&2
|
||||
return 1
|
||||
elif [ ${#matched_users[@]} -eq 1 ]; then
|
||||
# Single match - ask for confirmation
|
||||
{
|
||||
echo ""
|
||||
echo "Found 1 match: ${matched_users[0]} - ${user_primary_domain[${matched_users[0]}]} (${user_domain_count[${matched_users[0]}]} domains)"
|
||||
echo ""
|
||||
} >&2
|
||||
read -p "Use this user? (Y/n)> " confirm >&2
|
||||
if [[ ! $confirm =~ ^[Nn]$ ]]; then
|
||||
echo "${matched_users[0]}"
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
# Multiple matches - show interactive arrow-key menu
|
||||
{
|
||||
echo ""
|
||||
echo "Found ${#matched_users[@]} matches for '$search_term'"
|
||||
echo "Use arrow keys (↑/↓) to navigate, Enter to select:"
|
||||
echo ""
|
||||
} >&2
|
||||
|
||||
PS3="Select user> "
|
||||
select option in "${menu_items[@]}" "Cancel"; do
|
||||
if [ "$option" = "Cancel" ]; then
|
||||
return 1
|
||||
elif [ -n "$option" ]; then
|
||||
# Extract username from selected menu item (before the ' - ')
|
||||
local selected_user="${matched_users[$((REPLY-1))]}"
|
||||
echo "$selected_user"
|
||||
return 0
|
||||
fi
|
||||
done >&2
|
||||
fi
|
||||
;;
|
||||
[Ll]|LIST|list)
|
||||
# Show full list
|
||||
{
|
||||
echo ""
|
||||
echo "Complete user list ($total_users users):"
|
||||
echo "-------------------------------------------------------------------------------"
|
||||
for user in "${users[@]}"; do
|
||||
echo -e " ${GREEN}$user${NC} - ${user_primary_domain[$user]} (${user_domain_count[$user]} domains)"
|
||||
done
|
||||
echo "-------------------------------------------------------------------------------"
|
||||
echo ""
|
||||
} >&2
|
||||
# Ask again after showing list
|
||||
read -p "Enter username (or A for all, 0 to cancel)> " choice
|
||||
# Re-evaluate choice
|
||||
if [[ " ${users[@]} " =~ " ${choice} " ]]; then
|
||||
echo "$choice"
|
||||
return 0
|
||||
elif [ "$choice" = "A" ] || [ "$choice" = "a" ]; then
|
||||
echo "ALL"
|
||||
return 0
|
||||
elif [ "$choice" = "0" ]; then
|
||||
return 1
|
||||
else
|
||||
print_error "User '$choice' not found" >&2
|
||||
return 1
|
||||
fi
|
||||
;;
|
||||
0|cancel|back)
|
||||
return 1
|
||||
;;
|
||||
"")
|
||||
print_error "No input provided" >&2
|
||||
return 1
|
||||
;;
|
||||
*)
|
||||
# Check if it's an exact username match
|
||||
if [[ " ${users[@]} " =~ " ${choice} " ]]; then
|
||||
echo "$choice"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Not exact match
|
||||
print_error "User '$choice' not found" >&2
|
||||
if [ $total_users -gt 10 ]; then
|
||||
echo " Tip: Type 'L' to list all users" >&2
|
||||
fi
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
#############################################################################
|
||||
# USER PROCESSES
|
||||
#############################################################################
|
||||
|
||||
get_user_processes() {
|
||||
local username="$1"
|
||||
|
||||
ps aux | grep "^${username}" | grep -v grep
|
||||
}
|
||||
|
||||
get_user_top_processes() {
|
||||
local username="$1"
|
||||
local limit="${2:-10}"
|
||||
|
||||
ps aux | grep "^${username}" | grep -v grep | sort -k3 -rn | head -n "$limit"
|
||||
}
|
||||
|
||||
#############################################################################
|
||||
# DATABASE HELPER FUNCTIONS
|
||||
#############################################################################
|
||||
|
||||
get_database_owner() {
|
||||
local db_name="$1"
|
||||
|
||||
# Try to determine owner from database name prefix (common cPanel convention)
|
||||
# Database names are typically: username_dbname
|
||||
local prefix=$(echo "$db_name" | cut -d_ -f1)
|
||||
|
||||
# Check if this prefix matches a user
|
||||
local users=$(list_all_users)
|
||||
for user in $users; do
|
||||
if [ "$user" = "$prefix" ]; then
|
||||
echo "$user"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
# If no match, return unknown
|
||||
echo "unknown"
|
||||
}
|
||||
|
||||
get_database_domain() {
|
||||
local db_name="$1"
|
||||
local owner=$(get_database_owner "$db_name")
|
||||
|
||||
# Try to get primary domain for the owner
|
||||
if [ "$owner" != "unknown" ]; then
|
||||
get_user_domains "$owner" 2>/dev/null | head -1
|
||||
else
|
||||
echo ""
|
||||
fi
|
||||
}
|
||||
|
||||
#############################################################################
|
||||
# WORDPRESS DETECTION
|
||||
#############################################################################
|
||||
|
||||
find_user_wordpress_sites() {
|
||||
local username="$1"
|
||||
local home_dir=$(get_user_info "$username" | grep "^HOME_DIR=" | cut -d= -f2)
|
||||
|
||||
if [ -z "$home_dir" ] || [ ! -d "$home_dir" ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Find wp-config.php files
|
||||
find "$home_dir" -name "wp-config.php" -type f 2>/dev/null | while read wp_config; do
|
||||
local wp_dir=$(dirname "$wp_config")
|
||||
local domain=$(basename "$(dirname "$wp_dir")" 2>/dev/null)
|
||||
|
||||
# Try to get actual domain from wp-config
|
||||
local site_url=$(grep "WP_SITEURL\|WP_HOME" "$wp_config" | head -1 | grep -oP "https?://\K[^/'\"]+")
|
||||
|
||||
if [ -n "$site_url" ]; then
|
||||
echo "${site_url}|${wp_dir}"
|
||||
else
|
||||
echo "${domain}|${wp_dir}"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
#############################################################################
|
||||
# DISPLAY FUNCTIONS
|
||||
#############################################################################
|
||||
|
||||
show_user_summary() {
|
||||
local username="$1"
|
||||
|
||||
print_section "User Summary: $username"
|
||||
|
||||
# Get user info
|
||||
local user_info=$(get_user_info "$username")
|
||||
|
||||
if echo "$user_info" | grep -q "USER_EXISTS=no"; then
|
||||
print_error "User does not exist"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Parse info
|
||||
local primary_domain=$(echo "$user_info" | grep "^PRIMARY_DOMAIN=" | cut -d= -f2)
|
||||
local home_dir=$(echo "$user_info" | grep "^HOME_DIR=" | cut -d= -f2)
|
||||
local disk_used=$(echo "$user_info" | grep "^DISK_USED=" | cut -d= -f2)
|
||||
|
||||
# Display
|
||||
echo " Username: $username"
|
||||
[ -n "$primary_domain" ] && echo " Primary Domain: $primary_domain"
|
||||
echo " Home Directory: $home_dir"
|
||||
echo " Disk Usage: $disk_used"
|
||||
echo ""
|
||||
|
||||
# Domains
|
||||
local domains=$(get_user_domains "$username")
|
||||
local domain_count=$(echo "$domains" | grep -v "^$" | wc -l)
|
||||
echo " Domains ($domain_count):"
|
||||
echo "$domains" | sed 's/^/ - /'
|
||||
echo ""
|
||||
|
||||
# Databases
|
||||
local databases=$(get_user_databases "$username")
|
||||
local db_count=$(echo "$databases" | grep -v "^$" | wc -l)
|
||||
echo " Databases ($db_count):"
|
||||
echo "$databases" | sed 's/^/ - /'
|
||||
echo ""
|
||||
}
|
||||
|
||||
show_all_users_summary() {
|
||||
print_section "All Users Summary"
|
||||
|
||||
local users=($(list_all_users))
|
||||
local total_users=${#users[@]}
|
||||
|
||||
echo " Total Users: $total_users"
|
||||
echo ""
|
||||
|
||||
printf " ${BOLD}%-20s %-30s %10s %10s${NC}\n" "Username" "Primary Domain" "Domains" "Databases"
|
||||
echo " ────────────────────────────────────────────────────────────────────"
|
||||
|
||||
for user in "${users[@]}"; do
|
||||
local primary=$(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)
|
||||
|
||||
printf " %-20s %-30s %10s %10s\n" "$user" "$primary" "$domain_count" "$db_count"
|
||||
done
|
||||
|
||||
echo ""
|
||||
}
|
||||
Reference in New Issue
Block a user