1, 'out' => 'N/A']; // Timeout wrapper (Linux 'timeout' if available) $timeout_bin = trim(shell_exec('command -v timeout 2>/dev/null')) ?: ''; $safe = $timeout_bin ? escapeshellcmd($timeout_bin) . " " . intval($timeout) . "s " . $cmd : $cmd; $out = []; $code = 0; @exec($safe . ' 2>&1', $out, $code); return ['code' => $code, 'out' => implode("\n", $out)]; } function human_bytes($bytes) { $u = ['B','KB','MB','GB','TB','PB']; $i = 0; while ($bytes >= 1024 && $i < count($u)-1) { $bytes /= 1024; $i++; } return sprintf('%.1f %s', $bytes, $u[$i]); } function status_badge($status) { $s = strtolower(trim($status)); if (in_array($s, ['online','active','up','running','listening'])) return ['success','Online']; if (in_array($s, ['stopped','inactive','exited'])) return ['secondary','Stopped']; if (in_array($s, ['errored','failed','dead'])) return ['danger','Error']; if (in_array($s, ['unknown','n/a','not found'])) return ['dark','Unknown']; return ['warning', ucfirst($status ?: 'Unknown')]; } function parse_pm2() { // Prefer JSON output $res = run_cmd('pm2 jlist'); if ($res['code'] === 0 && strlen($res['out']) > 2) { $json = json_decode($res['out'], true); if (is_array($json)) return $json; } // Fallback: pm2 status tabular (parse minimally) $res2 = run_cmd('pm2 list --no-color'); $apps = []; if ($res2['code'] === 0) { $lines = explode("\n", $res2['out']); foreach ($lines as $ln) { if (preg_match('/^\s*\|\s*(\S+)\s*\|\s*(\d+)\s*\|\s*([A-Z]+)\s*\|\s*(\d+)\s*\|\s*([\dms:\.]+)\s*\|\s*(\S*)/i', $ln, $m)) { $apps[] = [ 'name' => $m[1], 'pm_id' => $m[2], 'status' => strtolower($m[3]), 'cpu' => null, 'mem' => null, 'uptime' => $m[5], 'mode' => $m[6] ?? '' ]; } } } return $apps; } function get_cpu_info() { $model = trim(run_cmd("awk -F': ' '/model name/ {print \$2; exit}' /proc/cpuinfo")['out']); $cores_p = intval(trim(run_cmd("getconf _NPROCESSORS_ONLN")['out'])) ?: 0; $loadavg = trim(run_cmd("cut -d' ' -f1-3 /proc/loadavg")['out']); return [$model ?: 'N/A', $cores_p, $loadavg ?: 'N/A']; } function get_mem_info() { // /proc/meminfo (bytes -> kB) $mem_total_kb = intval(trim(run_cmd("awk '/MemTotal/ {print \$2}' /proc/meminfo")['out'])); $mem_avail_kb = intval(trim(run_cmd("awk '/MemAvailable/ {print \$2}' /proc/meminfo")['out'])); $mem_used_kb = max(0, $mem_total_kb - $mem_avail_kb); return [ 'total' => human_bytes($mem_total_kb * 1024), 'used' => human_bytes($mem_used_kb * 1024), 'free' => human_bytes($mem_avail_kb * 1024), 'pct' => $mem_total_kb ? round(($mem_used_kb / $mem_total_kb) * 100) : 0 ]; } function get_disk_info() { $res = run_cmd("df -h --output=target,size,used,avail,pcent -x tmpfs -x devtmpfs"); $rows = []; if ($res['code'] === 0) { $lines = explode("\n", trim($res['out'])); foreach (array_slice($lines, 1) as $l) { $l = preg_replace('/\s+/', ' ', trim($l)); if (!$l) continue; [$mount, $size, $used, $avail, $pct] = array_pad(explode(' ', $l), 5, ''); $rows[] = compact('mount','size','used','avail','pct'); } } return $rows; } function get_gpu_info() { $nvsmi = trim(run_cmd('command -v nvidia-smi')['out']); if (!$nvsmi) return []; $q = '--query-gpu=name,driver_version,pstate,temperature.gpu,memory.total,memory.used,pcie.link.gen.current,pcie.link.width.current --format=csv,noheader'; $res = run_cmd("nvidia-smi $q", 4); $out = []; if ($res['code'] === 0) { foreach (explode("\n", trim($res['out'])) as $line) { if (!$line) continue; $parts = array_map('trim', explode(',', $line)); $out[] = [ 'name' => $parts[0] ?? 'NVIDIA GPU', 'driver' => $parts[1] ?? 'N/A', 'pstate' => $parts[2] ?? 'N/A', 'tempC' => $parts[3] ?? 'N/A', 'mem_total' => $parts[4] ?? 'N/A', 'mem_used' => $parts[5] ?? 'N/A', 'pcie_gen' => $parts[6] ?? 'N/A', 'pcie_w' => $parts[7] ?? 'N/A', ]; } } return $out; } function get_network_info() { $hostname = trim(run_cmd('hostname')['out']); $ips = trim(run_cmd("hostname -I")['out']); $gateway = trim(run_cmd("ip route | awk '/default/ {print \$3; exit}'")['out']); return [$hostname ?: 'N/A', $ips ?: 'N/A', $gateway ?: 'N/A']; } function get_os_info() { $pretty = trim(run_cmd("awk -F'=' '/^PRETTY_NAME/{gsub(/\"/ ,\"\",\$2); print \$2}' /etc/os-release")['out']); $kernel = trim(run_cmd('uname -r')['out']); $arch = trim(run_cmd('uname -m')['out']); $uptime = trim(run_cmd('uptime -p')['out']); $boot = trim(run_cmd('who -b | awk \'{print $3, $4}\'')['out']); $phpv = PHP_VERSION; $nginx = trim(run_cmd('nginx -v 2>&1')['out']); $nginx = $nginx ?: 'nginx not found'; return [$pretty ?: 'Linux', $kernel ?: 'N/A', $arch ?: 'N/A', $uptime ?: 'N/A', $boot ?: 'N/A', $phpv, $nginx]; } function get_services_status($services = ['nginx','php-fpm','pm2']) { $data = []; foreach ($services as $svc) { $cmd = "systemctl is-active $svc"; $r = run_cmd($cmd); $status = $r['code'] === 0 ? trim($r['out']) : 'unknown'; $data[] = ['name' => $svc, 'status' => $status]; } return $data; } function get_top_procs($limit = 8) { $r = run_cmd("ps -eo pid,comm,%cpu,%mem --sort=-%cpu | head -n " . intval($limit + 1)); $rows = []; if ($r['code'] === 0) { $lines = explode("\n", trim($r['out'])); foreach (array_slice($lines, 1) as $ln) { $ln = preg_replace('/\s+/', ' ', trim($ln)); if (!$ln) continue; [$pid,$comm,$cpu,$mem] = array_pad(explode(' ', $ln), 4, ''); $rows[] = compact('pid','comm','cpu','mem'); } } return $rows; } function get_log_tail($path, $lines = 80) { if (!is_readable($path)) return "Log not found or unreadable: $path"; $r = run_cmd("tail -n " . intval($lines) . " " . escapeshellarg($path)); return $r['code'] === 0 ? $r['out'] : "Unable to read log."; } // ---------- Page Settings ---------- $auto_refresh = isset($_GET['autorefresh']) ? (int)$_GET['autorefresh'] : 0; $refresh_secs = ($auto_refresh > 0 && $auto_refresh <= 600) ? $auto_refresh : 0; $custom_log = isset($_GET['log']) ? $_GET['log'] : ''; $log_path = $custom_log ?: '/var/log/nginx/access.log'; // ---------- Data ---------- [$os_name,$kernel,$arch,$uptime,$booted,$phpv,$nginxv] = get_os_info(); [$hostname,$ips,$gw] = get_network_info(); [$cpu_model,$cores,$load] = get_cpu_info(); $mem = get_mem_info(); $disks = get_disk_info(); $gpus = get_gpu_info(); $pm2 = parse_pm2(); $services = get_services_status(['nginx','php-fpm','pm2']); $procs = get_top_procs(8); $log_tail = get_log_tail($log_path, 100); ?> 🖥️ GhostAI Server Dashboard
💻System Overview
Host:
• Kernel
Uptime: • Boot:
PHP:
IP(s): • GW:
🧠CPU
Model
Cores:
Load:
📈Memory
Usage
/
Free: (%)
🎮GPU
NameDriverTempVRAMPCIeP-State
/ Gen ×
No NVIDIA GPU info (nvidia-smi not found or no GPU).
🗄️Disks
MountSizeUsedAvailUse%
🧩Services
:
📊Top Processes (CPU)
PIDCommandCPU%MEM%
🎛️PM2 Apps
0))): ?>
"; } } else { foreach ($pm2 as $app) { $b = status_badge($app['status'] ?? 'unknown'); echo ""; } } ?>
NameIDStatusModeCPUMemUptime
".htmlspecialchars($name)." ".htmlspecialchars($pm_id)." ".htmlspecialchars($b[1])." ".htmlspecialchars($mode)." ".($cpu !== null ? htmlspecialchars($cpu).'%' : 'N/A')." ".htmlspecialchars($memh)." ".htmlspecialchars($upt_h)."
".htmlspecialchars($app['name'] ?? 'app')." ".htmlspecialchars($app['pm_id'] ?? '')." ".htmlspecialchars($b[1])." ".htmlspecialchars($app['mode'] ?? '')." ".htmlspecialchars($app['cpu'] ?? 'N/A')." ".htmlspecialchars($app['mem'] ?? 'N/A')." ".htmlspecialchars($app['uptime'] ?? 'N/A')."
PM2 not detected or no apps configured.
🪵Logs
Enter a readable log file path to tail.