1<?php 2/** 3 * System Stats API Endpoint 4 * Returns real-time CPU and memory usage 5 */ 6 7header('Content-Type: application/json'); 8header('Cache-Control: no-cache, must-revalidate'); 9 10$stats = [ 11 'cpu' => 0, 12 'cpu_5min' => 0, 13 'memory' => 0, 14 'timestamp' => time(), 15 'load' => ['1min' => 0, '5min' => 0, '15min' => 0], 16 'uptime' => '', 17 'memory_details' => [], 18 'top_processes' => [] 19]; 20 21// Get CPU usage and load averages 22if (function_exists('sys_getloadavg')) { 23 $load = sys_getloadavg(); 24 if ($load !== false) { 25 // Use 1-minute load average for real-time feel 26 // Normalize to percentage (assuming max load of 2.0 = 100%) 27 $stats['cpu'] = min(100, ($load[0] / 2.0) * 100); 28 29 // 5-minute average for green bar 30 $stats['cpu_5min'] = min(100, ($load[1] / 2.0) * 100); 31 32 // Store all three load averages for tooltip 33 $stats['load'] = [ 34 '1min' => round($load[0], 2), 35 '5min' => round($load[1], 2), 36 '15min' => round($load[2], 2) 37 ]; 38 } 39} 40 41// Get memory usage 42if (stristr(PHP_OS, 'linux')) { 43 // Linux: Read from /proc/meminfo 44 $meminfo = file_get_contents('/proc/meminfo'); 45 if ($meminfo) { 46 preg_match('/MemTotal:\s+(\d+)/', $meminfo, $total); 47 preg_match('/MemAvailable:\s+(\d+)/', $meminfo, $available); 48 49 if (isset($total[1]) && isset($available[1])) { 50 $totalMem = $total[1]; 51 $availableMem = $available[1]; 52 $usedMem = $totalMem - $availableMem; 53 $stats['memory'] = ($usedMem / $totalMem) * 100; 54 } 55 } 56} elseif (stristr(PHP_OS, 'darwin') || stristr(PHP_OS, 'bsd')) { 57 // macOS/BSD: Use vm_stat 58 $vm_stat = shell_exec('vm_stat'); 59 if ($vm_stat) { 60 preg_match('/Pages free:\s+(\d+)\./', $vm_stat, $free); 61 preg_match('/Pages active:\s+(\d+)\./', $vm_stat, $active); 62 preg_match('/Pages inactive:\s+(\d+)\./', $vm_stat, $inactive); 63 preg_match('/Pages wired down:\s+(\d+)\./', $vm_stat, $wired); 64 65 if (isset($free[1], $active[1], $inactive[1], $wired[1])) { 66 $pageSize = 4096; // bytes 67 $totalPages = $free[1] + $active[1] + $inactive[1] + $wired[1]; 68 $usedPages = $active[1] + $inactive[1] + $wired[1]; 69 70 if ($totalPages > 0) { 71 $stats['memory'] = ($usedPages / $totalPages) * 100; 72 } 73 } 74 } 75} elseif (stristr(PHP_OS, 'win')) { 76 // Windows: Use wmic 77 $wmic = shell_exec('wmic OS get FreePhysicalMemory,TotalVisibleMemorySize /Value'); 78 if ($wmic) { 79 preg_match('/FreePhysicalMemory=(\d+)/', $wmic, $free); 80 preg_match('/TotalVisibleMemorySize=(\d+)/', $wmic, $total); 81 82 if (isset($free[1]) && isset($total[1])) { 83 $freeMem = $free[1]; 84 $totalMem = $total[1]; 85 $usedMem = $totalMem - $freeMem; 86 $stats['memory'] = ($usedMem / $totalMem) * 100; 87 } 88 } 89} 90 91// Fallback: Use PHP memory if system memory unavailable 92if ($stats['memory'] == 0) { 93 $memLimit = ini_get('memory_limit'); 94 if ($memLimit != '-1') { 95 $memLimitBytes = return_bytes($memLimit); 96 $memUsage = memory_get_usage(true); 97 $stats['memory'] = ($memUsage / $memLimitBytes) * 100; 98 } 99} 100 101// Get uptime (Linux/Unix) 102if (file_exists('/proc/uptime')) { 103 $uptime = file_get_contents('/proc/uptime'); 104 if ($uptime) { 105 $uptimeSeconds = floatval(explode(' ', $uptime)[0]); 106 $days = floor($uptimeSeconds / 86400); 107 $hours = floor(($uptimeSeconds % 86400) / 3600); 108 $minutes = floor(($uptimeSeconds % 3600) / 60); 109 $stats['uptime'] = sprintf('%dd %dh %dm', $days, $hours, $minutes); 110 } 111} elseif (stristr(PHP_OS, 'win')) { 112 // Windows uptime 113 $wmic = shell_exec('wmic os get lastbootuptime'); 114 if ($wmic && preg_match('/(\d{14})/', $wmic, $matches)) { 115 $bootTime = DateTime::createFromFormat('YmdHis', $matches[1]); 116 $now = new DateTime(); 117 $diff = $now->diff($bootTime); 118 $stats['uptime'] = sprintf('%dd %dh %dm', $diff->days, $diff->h, $diff->i); 119 } 120} 121 122// Get detailed memory info (Linux) 123if (stristr(PHP_OS, 'linux') && file_exists('/proc/meminfo')) { 124 $meminfo = file_get_contents('/proc/meminfo'); 125 if ($meminfo) { 126 preg_match('/MemTotal:\s+(\d+)/', $meminfo, $total); 127 preg_match('/MemAvailable:\s+(\d+)/', $meminfo, $available); 128 preg_match('/MemFree:\s+(\d+)/', $meminfo, $free); 129 preg_match('/Buffers:\s+(\d+)/', $meminfo, $buffers); 130 preg_match('/Cached:\s+(\d+)/', $meminfo, $cached); 131 132 if (isset($total[1])) { 133 $totalMB = round($total[1] / 1024, 1); 134 $availableMB = isset($available[1]) ? round($available[1] / 1024, 1) : 0; 135 $usedMB = round(($total[1] - ($available[1] ?? $free[1] ?? 0)) / 1024, 1); 136 $buffersMB = isset($buffers[1]) ? round($buffers[1] / 1024, 1) : 0; 137 $cachedMB = isset($cached[1]) ? round($cached[1] / 1024, 1) : 0; 138 139 $stats['memory_details'] = [ 140 'total' => $totalMB . ' MB', 141 'used' => $usedMB . ' MB', 142 'available' => $availableMB . ' MB', 143 'buffers' => $buffersMB . ' MB', 144 'cached' => $cachedMB . ' MB' 145 ]; 146 } 147 } 148} 149 150// Get top 5 processes by CPU (Linux/Unix) 151if (stristr(PHP_OS, 'linux') || stristr(PHP_OS, 'darwin')) { 152 $ps = shell_exec('ps aux --sort=-%cpu | head -6 | tail -5 2>/dev/null'); 153 if (!$ps) { 154 // Try BSD/macOS format 155 $ps = shell_exec('ps aux -r | head -6 | tail -5 2>/dev/null'); 156 } 157 if ($ps) { 158 $lines = explode("\n", trim($ps)); 159 foreach ($lines as $line) { 160 if (empty($line)) continue; 161 $parts = preg_split('/\s+/', $line, 11); 162 if (count($parts) >= 11) { 163 $stats['top_processes'][] = [ 164 'cpu' => $parts[2] . '%', 165 'mem' => $parts[3] . '%', 166 'command' => substr($parts[10], 0, 30) 167 ]; 168 } 169 } 170 } 171} elseif (stristr(PHP_OS, 'win')) { 172 // Windows top processes 173 $wmic = shell_exec('wmic process get Caption,KernelModeTime /format:csv | findstr /V "^$" | sort /R /+1 | more +1 | findstr /N "^" | findstr "^[1-5]:"'); 174 if ($wmic) { 175 $lines = explode("\n", trim($wmic)); 176 foreach ($lines as $line) { 177 if (preg_match('/^\d+:(.+),(.+),(\d+)/', $line, $matches)) { 178 $stats['top_processes'][] = [ 179 'command' => substr($matches[2], 0, 30), 180 'cpu' => '-' 181 ]; 182 } 183 } 184 } 185} 186 187echo json_encode($stats); 188 189function return_bytes($val) { 190 $val = trim($val); 191 $last = strtolower($val[strlen($val)-1]); 192 $val = (int)$val; 193 switch($last) { 194 case 'g': 195 $val *= 1024; 196 case 'm': 197 $val *= 1024; 198 case 'k': 199 $val *= 1024; 200 } 201 return $val; 202} 203