#!/bin/bash # === Realtime LLSMP Monitor (Local JSON Output) === refresh_interval=10 # === Check and Install Dependencies === for pkg in jq bc; do if ! command -v $pkg &> /dev/null; then echo "[INFO] Installing missing dependency: $pkg" sudo dnf install -y $pkg fi done # === Extract DB creds from wp-config.php === WP_CONFIG="/var/www/webroot/ROOT/wp-config.php" if [[ -f "$WP_CONFIG" ]]; then DB_USER=$(grep -oP "define\(\s*'DB_USER'\s*,\s*'\K[^']+" "$WP_CONFIG") DB_PASS=$(grep -oP "define\(\s*'DB_PASSWORD'\s*,\s*'\K[^']+" "$WP_CONFIG") else DB_USER="" DB_PASS="" fi # === Dry Run Mode === if [[ "$1" == "--test" ]]; then run_once=true else run_once=false fi run_monitor() { timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ") hostname=$(hostname) uptime_value=$(awk '{print int($1)}' /proc/uptime) # Memory - Using /proc/meminfo for more accurate data mem_total=$(awk '/MemTotal/ {print $2}' /proc/meminfo) mem_free=$(awk '/MemFree/ {print $2}' /proc/meminfo) mem_available=$(awk '/MemAvailable/ {print $2}' /proc/meminfo) mem_cached=$(awk '/Cached/ {print $2}' /proc/meminfo) mem_buffers=$(awk '/Buffers/ {print $2}' /proc/meminfo) mem_used=$((mem_total - mem_free - mem_buffers - mem_cached)) mem_percent=$((100 * mem_used / mem_total)) mem_alert=$([[ $mem_percent -ge 85 ]] && echo "high" || echo "normal") # CPU - Using /proc/stat for more accurate data cpu_load=$(cat /proc/loadavg | awk '{print $1}') cpu_cores=$(nproc) cpu_alert=$(echo "$cpu_load >= $cpu_cores" | bc -l | grep 1 &>/dev/null && echo "high" || echo "normal") # CPU Usage from /proc/stat cpu_stats=$(cat /proc/stat | grep '^cpu ' | awk '{print $2" "$3" "$4" "$5" "$6" "$7" "$8}') read -r user nice system idle iowait irq softirq <<< "$cpu_stats" total=$((user + nice + system + idle + iowait + irq + softirq)) idle=$((idle + iowait)) cpu_usage_pct=$((100 * (total - idle) / total)) # Top 5 CPU-intensive processes with more details top_processes=$(ps -eo pid,ppid,user,comm,%cpu,%mem,rss,vsz,etime --sort=-%cpu | head -n 6 | tail -n +2 | jq -Rn '[inputs | split(" ") | map(select(length > 0)) | {pid: .[0], ppid: .[1], user: .[2], name: .[3], cpu: .[4], mem: .[5], rss: .[6], vsz: .[7], uptime: .[8]}]') # Disk Usage - Using df with more accurate options disk_info=$(df -h / | awk 'NR==2 {print $2" "$3" "$4" "$5}') read -r disk_total disk_used disk_free disk_percent <<< "$disk_info" disk_alert=$([[ ${disk_percent//%/} -ge 85 ]] && echo "high" || echo "normal") # Network Statistics - Calculate real-time speed with better accuracy net_stats_prev="/tmp/net_stats_prev" net_stats_current="/tmp/net_stats_current" # Store previous network stats if they exist if [[ -f "$net_stats_prev" ]]; then mv "$net_stats_prev" "$net_stats_current" fi # Get current network stats with better error handling net_stats=$(cat /proc/net/dev | grep -E 'eth0|ens|enp' | awk '{print $2" "$10}') if [[ -n "$net_stats" ]]; then echo "$net_stats" > "$net_stats_prev" if [[ -f "$net_stats_current" ]]; then read -r prev_in prev_out < "$net_stats_current" read -r curr_in curr_out < "$net_stats_prev" # Calculate speed in MB/s with 2 decimal places and proper error handling if [[ -n "$prev_in" && -n "$prev_out" && -n "$curr_in" && -n "$curr_out" ]]; then net_speed_in=$(awk "BEGIN {printf \"%.2f\", ($curr_in - $prev_in) / 1024 / 1024 / $refresh_interval}") net_speed_out=$(awk "BEGIN {printf \"%.2f\", ($curr_out - $prev_out) / 1024 / 1024 / $refresh_interval}") else net_speed_in="0.00" net_speed_out="0.00" fi else # First run - initialize with current values read -r curr_in curr_out < "$net_stats_prev" echo "$curr_in $curr_out" > "$net_stats_current" net_speed_in="0.00" net_speed_out="0.00" fi else net_speed_in="0.00" net_speed_out="0.00" fi # System Load and Processes - More accurate counting with better formatting load_avg=$(cat /proc/loadavg | awk '{printf "%.2f %.2f %.2f", $1, $2, $3}') load_avg_1min=$(echo "$load_avg" | awk '{print $1}') load_avg_5min=$(echo "$load_avg" | awk '{print $2}') load_avg_15min=$(echo "$load_avg" | awk '{print $3}') # Calculate load average status load_status="normal" if (( $(echo "$load_avg_1min >= $cpu_cores" | bc -l) )); then load_status="high" fi process_count=$(ps -ef | grep -v grep | wc -l) zombie_count=$(ps -ef | grep defunct | grep -v grep | wc -l) # LiteSpeed Status with more detailed metrics litespeed_status="stopped" litespeed_mem="0" litespeed_version="Unavailable" litespeed_connections="0" litespeed_qps="0" litespeed_workers="0" litespeed_ssl_connections="0" litespeed_http_connections="0" if systemctl is-active --quiet lshttpd; then litespeed_status="running" # Get LiteSpeed version with better error handling if [[ -f "/var/www/bin/lshttpd" ]]; then litespeed_version=$(/var/www/bin/lshttpd -v 2>/dev/null | head -n1 || echo "LiteSpeed/Unknown") fi # Improved LiteSpeed memory calculation with 2 decimal places litespeed_mem=$(ps -C lshttpd,litespeed,httpd -o rss= 2>/dev/null | awk '{sum+=$1} END {printf "%.2f", sum/1024}') # Get ports from LiteSpeed config with better error handling if [[ -f "/var/www/conf/httpd_config.xml" ]]; then http_port=$(xmlstarlet sel -t -v "//listener/address/port" /var/www/conf/httpd_config.xml 2>/dev/null || echo "80") ssl_port=$(xmlstarlet sel -t -v "//listener/ssl/port" /var/www/conf/httpd_config.xml 2>/dev/null || echo "443") else http_port="80" ssl_port="443" fi # If ports are empty, use defaults if [[ -z "$http_port" ]]; then http_port="80" fi if [[ -z "$ssl_port" ]]; then ssl_port="443" fi # Get all LiteSpeed listening ports litespeed_ports=$(netstat -tlpn 2>/dev/null | grep -E 'lshttpd|litespeed|httpd' | awk '{print $4}' | cut -d: -f2 | sort -u) # If no ports found, use defaults if [[ -z "$litespeed_ports" ]]; then litespeed_ports="80 443 4848 8443" fi # Calculate QPS from access log with better accuracy if [[ -f "/var/www/logs/access.log" ]]; then current_time=$(date +%s) log_start_time=$(date -d "1 minute ago" +%s) litespeed_qps=$(awk -v start="$log_start_time" -v end="$current_time" ' BEGIN { count = 0 } $4 ~ /\[/ { # Extract date and time from log entry match($4, /\[(.*?)\]/, m) split(m[1], t, /[\/: ]/) # Convert to timestamp log_time = mktime(t[3] " " t[2] " " t[1] " " t[4] " " t[5] " " t[6]) if (log_time >= start && log_time <= end) count++ } END {printf "%.2f", count/60} ' /var/www/logs/access.log 2>/dev/null || echo "0.00") fi # Alternative QPS calculation using recent log entries if [[ "$litespeed_qps" == "0.00" ]]; then if [[ -f "/var/www/logs/access.log" ]]; then litespeed_qps=$(tail -n 100 /var/www/logs/access.log | awk ' BEGIN { count = 0 } $4 ~ /\[/ { match($4, /\[(.*?)\]/, m) split(m[1], t, /[\/: ]/) log_time = mktime(t[3] " " t[2] " " t[1] " " t[4] " " t[5] " " t[6]) if (log_time >= systime() - 60) count++ } END {printf "%.2f", count/60} ' 2>/dev/null || echo "0.00") fi fi # Ensure QPS is never empty if [[ -z "$litespeed_qps" ]]; then litespeed_qps="0.00" fi # More accurate connection detection using multiple methods if command -v netstat >/dev/null 2>&1; then # Get all connections including TIME_WAIT and ESTABLISHED for all ports litespeed_http_connections=0 litespeed_ssl_connections=0 for port in $litespeed_ports; do if [[ "$port" == "443" || "$port" == "8443" ]]; then ssl_conns=$(netstat -ant | grep ":$port" | grep -E "ESTABLISHED|TIME_WAIT" | wc -l) litespeed_ssl_connections=$((litespeed_ssl_connections + ssl_conns)) else http_conns=$(netstat -ant | grep ":$port" | grep -E "ESTABLISHED|TIME_WAIT" | wc -l) litespeed_http_connections=$((litespeed_http_connections + http_conns)) fi done elif command -v lsof >/dev/null 2>&1; then litespeed_http_connections=0 litespeed_ssl_connections=0 for port in $litespeed_ports; do if [[ "$port" == "443" || "$port" == "8443" ]]; then ssl_conns=$(lsof -i :$port -n | grep -E "ESTABLISHED|TIME_WAIT" | wc -l) litespeed_ssl_connections=$((litespeed_ssl_connections + ssl_conns)) else http_conns=$(lsof -i :$port -n | grep -E "ESTABLISHED|TIME_WAIT" | wc -l) litespeed_http_connections=$((litespeed_http_connections + http_conns)) fi done else # Fallback to checking process connections litespeed_http_connections=0 litespeed_ssl_connections=0 for port in $litespeed_ports; do if [[ "$port" == "443" || "$port" == "8443" ]]; then ssl_conns=$(ps -C lshttpd,litespeed,httpd -o pid= 2>/dev/null | xargs -r lsof -i :$port 2>/dev/null | grep -E "ESTABLISHED|TIME_WAIT" | wc -l) litespeed_ssl_connections=$((litespeed_ssl_connections + ssl_conns)) else http_conns=$(ps -C lshttpd,litespeed,httpd -o pid= 2>/dev/null | xargs -r lsof -i :$port 2>/dev/null | grep -E "ESTABLISHED|TIME_WAIT" | wc -l) litespeed_http_connections=$((litespeed_http_connections + http_conns)) fi done fi # Calculate total connections litespeed_connections=$((litespeed_ssl_connections + litespeed_http_connections)) # Additional connection check using access log if [[ "$litespeed_connections" == "0" ]]; then if [[ -f "/var/www/logs/access.log" ]]; then recent_connections=$(tail -n 100 /var/www/logs/access.log | awk ' BEGIN { count = 0 } $4 ~ /\[/ { match($4, /\[(.*?)\]/, m) split(m[1], t, /[\/: ]/) log_time = mktime(t[3] " " t[2] " " t[1] " " t[4] " " t[5] " " t[6]) if (log_time >= systime() - 60) count++ } END {print count} ' 2>/dev/null || echo "0") litespeed_connections=$recent_connections litespeed_http_connections=$((recent_connections / 2)) litespeed_ssl_connections=$((recent_connections / 2)) fi fi # Count LiteSpeed workers with better accuracy litespeed_workers=$(ps -C lshttpd,litespeed,httpd -o comm= 2>/dev/null | grep -v grep | wc -l) # Additional check for worker processes from process list if [[ "$litespeed_workers" == "0" ]]; then litespeed_workers=$(ps -C lshttpd,litespeed,httpd -o pid= 2>/dev/null | wc -l) fi fi # PHP Version and Status - Improved status detection php_version=$(php -v 2>/dev/null | head -n1) php_fpm_status=$(systemctl is-active php-fpm 2>/dev/null | tr -d '\n' || echo "not_found") # More accurate PHP-FPM memory calculation with 2 decimal places php_fpm_memory=$(ps -C php-fpm,lsphp -o rss= 2>/dev/null | awk '{sum+=$1} END {printf "%.2f", sum/1024}' || echo "0.00") # Add PHP-FPM process count with better accuracy php_fpm_processes=$(ps -C php-fpm,lsphp -o comm= 2>/dev/null | grep -v grep | wc -l) # MariaDB Status with more detailed metrics mariadb_status="stopped" mariadb_connections="" mariadb_uptime="" mariadb_queries="" mariadb_qps="" mariadb_version=$(mysql -V 2>/dev/null || echo "Unavailable") mariadb_memory="0" mariadb_threads="0" mariadb_slow_queries="0" if systemctl is-active --quiet mariadb || systemctl is-active --quiet mysql; then mariadb_status="running" mariadb_memory=$(ps -C mariadbd -o rss= 2>/dev/null | awk '{sum+=$1} END {printf "%.2f", sum/1024}' || echo "0.00") if [[ -n "$DB_USER" && -n "$DB_PASS" ]]; then mariadb_connections=$(mysqladmin -u"$DB_USER" -p"$DB_PASS" status 2>/dev/null | awk '{print $6}') mariadb_uptime=$(mysqladmin -u"$DB_USER" -p"$DB_PASS" status 2>/dev/null | awk '{print $2}') mariadb_queries=$(mysql -u"$DB_USER" -p"$DB_PASS" -e "SHOW GLOBAL STATUS LIKE 'Queries';" 2>/dev/null | awk '/Queries/ {print $2}') mariadb_qps=$(mysql -u"$DB_USER" -p"$DB_PASS" -e "SHOW GLOBAL STATUS LIKE 'Queries';" 2>/dev/null | awk '/Queries/ {printf "%.2f", $2/'"$mariadb_uptime"'}') mariadb_threads=$(mysql -u"$DB_USER" -p"$DB_PASS" -e "SHOW GLOBAL STATUS LIKE 'Threads_connected';" 2>/dev/null | awk '/Threads_connected/ {print $2}') mariadb_slow_queries=$(mysql -u"$DB_USER" -p"$DB_PASS" -e "SHOW GLOBAL STATUS LIKE 'Slow_queries';" 2>/dev/null | awk '/Slow_queries/ {print $2}') fi fi json_payload=$(jq -n \ --arg ts "$timestamp" \ --arg hostname "$hostname" \ --arg uptime "$uptime_value" \ --arg load_avg_1min "$load_avg_1min" \ --arg load_avg_5min "$load_avg_5min" \ --arg load_avg_15min "$load_avg_15min" \ --arg load_status "$load_status" \ --arg process_count "$process_count" \ --arg zombie_count "$zombie_count" \ --arg cpu_cores "$cpu_cores" \ --argjson top_processes "$top_processes" \ --arg mem_total "$mem_total" \ --arg mem_free "$mem_free" \ --arg mem_available "$mem_available" \ --arg mem_used "$mem_used" \ --arg mem_percent "$mem_percent" \ --arg mem_alert "$mem_alert" \ --arg cpu_load "$cpu_load" \ --arg cpu_usage_pct "$cpu_usage_pct" \ --arg cpu_alert "$cpu_alert" \ --arg disk_total "$disk_total" \ --arg disk_used "$disk_used" \ --arg disk_free "$disk_free" \ --arg disk_percent "$disk_percent" \ --arg disk_alert "$disk_alert" \ --arg php_version "$php_version" \ --arg php_fpm_status "$php_fpm_status" \ --arg php_fpm_memory "$php_fpm_memory" \ --arg php_fpm_processes "$php_fpm_processes" \ --arg mariadb_status "$mariadb_status" \ --arg mariadb_version "$mariadb_version" \ --arg mariadb_connections "$mariadb_connections" \ --arg mariadb_uptime "$mariadb_uptime" \ --arg mariadb_queries "$mariadb_queries" \ --arg mariadb_qps "$mariadb_qps" \ --arg mariadb_memory "$mariadb_memory" \ --arg mariadb_threads "$mariadb_threads" \ --arg mariadb_slow_queries "$mariadb_slow_queries" \ --arg litespeed_status "$litespeed_status" \ --arg litespeed_version "$litespeed_version" \ --arg litespeed_mem "$litespeed_mem" \ --arg litespeed_connections "$litespeed_connections" \ --arg litespeed_qps "$litespeed_qps" \ --arg litespeed_workers "$litespeed_workers" \ --arg litespeed_ssl_connections "$litespeed_ssl_connections" \ --arg litespeed_http_connections "$litespeed_http_connections" \ --arg net_speed_in "$net_speed_in" \ --arg net_speed_out "$net_speed_out" \ '{ timestamp: $ts, hostname: $hostname, uptime_sec: $uptime, system: { load_average: { "1min": $load_avg_1min, "5min": $load_avg_5min, "15min": $load_avg_15min, "status": $load_status, "cores": $cpu_cores, "description": "Load average represents the average number of processes waiting for CPU time. Values should be less than the number of CPU cores." }, process_count: $process_count, zombie_count: $zombie_count }, memory: { total_kb: $mem_total, free_kb: $mem_free, available_kb: $mem_available, used_kb: $mem_used, percent: $mem_percent, alert: $mem_alert }, cpu: { cores: $cpu_cores, load_1min: $cpu_load, usage_percent: $cpu_usage_pct, alert: $cpu_alert, top_processes: $top_processes }, disk: { total: $disk_total, used: $disk_used, free: $disk_free, usage_percent: $disk_percent, alert: $disk_alert }, network: { speed_in_mb: $net_speed_in, speed_out_mb: $net_speed_out }, php: { version: $php_version, fpm_status: $php_fpm_status, memory_mb: $php_fpm_memory, processes: $php_fpm_processes }, mariadb: { status: $mariadb_status, version: $mariadb_version, connections: $mariadb_connections, uptime_sec: $mariadb_uptime, total_queries: $mariadb_queries, queries_per_second: $mariadb_qps, memory_mb: $mariadb_memory, threads_connected: $mariadb_threads, slow_queries: $mariadb_slow_queries }, litespeed: { status: $litespeed_status, version: $litespeed_version, memory_mb: $litespeed_mem, connections: { total: $litespeed_connections, http: $litespeed_http_connections, ssl: $litespeed_ssl_connections }, queries_per_second: $litespeed_qps, workers: $litespeed_workers } }') if $run_once; then echo "$json_payload" else echo "$json_payload" fi } if $run_once; then run_monitor else while true; do run_monitor sleep $refresh_interval done fi