'unauthorized']); exit; } header('Content-Type: application/json; charset=utf-8'); // === Logging-Setup === $logDir = dirname(__DIR__) . DIRECTORY_SEPARATOR . 'logs'; if (!is_dir($logDir)) { @mkdir($logDir, 0755, true); } $logFile = $logDir . DIRECTORY_SEPARATOR . 'snmp_api.log'; function log_msg(string $msg): void { global $logFile; $timestamp = date('Y-m-d H:i:s'); @file_put_contents($logFile, "[$timestamp] $msg\n", FILE_APPEND); } $configPath = __DIR__ . '/../../config/config.php'; if (!is_readable($configPath)) { log_msg('ERROR: config.php nicht lesbar'); echo json_encode(['error' => 'config_not_found']); exit; } $config = require $configPath; $snmp = $config['snmp'] ?? []; log_msg('--- SNMP-Abfrage gestartet ---'); // === Cache-Logik (Datei) === // Cache-Datei wird nach 10 Sekunden als ungültig betrachtet $cacheDir = sys_get_temp_dir(); $cacheFile = $cacheDir . DIRECTORY_SEPARATOR . 'snmp_status_cache.json'; $cacheTTL = 10; // Sekunden if (file_exists($cacheFile)) { $cacheAge = time() - filemtime($cacheFile); if ($cacheAge < $cacheTTL) { // Cache ist noch gültig $cached = file_get_contents($cacheFile); if ($cached !== false) { log_msg("Cache HIT (Alter: {$cacheAge}s)"); echo $cached; exit; } } else { log_msg("Cache abgelaufen (Alter: {$cacheAge}s), neue Abfrage"); } } $host = $snmp['host'] ?? '127.0.0.1'; $community = $snmp['community'] ?? 'public'; $timeout = (int)($snmp['timeout'] ?? 2); $retries = (int)($snmp['retries'] ?? 1); log_msg("SNMP-Host: $host, Community: $community, Timeout: {$timeout}s"); if (!function_exists('snmpget')) { log_msg('ERROR: SNMP-Erweiterung nicht installiert'); echo json_encode(['error' => 'snmp_extension_missing']); exit; } snmp_set_quick_print(true); snmp_set_oid_output_format(SNMP_OID_OUTPUT_NUMERIC); // Hilfsfunktion: sichere snmpget-Rückgabe (numeric oder null) function sget(string $host, string $community, string $oid, int $timeout, int $retries) { $v = @snmpget($host, $community, $oid, $timeout, $retries); if ($v === false || $v === null) return null; return is_string($v) ? trim($v) : $v; } // System-Uptime (TimeTicks) $uptimeOid = $snmp['oids']['uptime'] ?? '1.3.6.1.2.1.1.3.0'; $upticksRaw = @sget($host, $community, $uptimeOid, $timeout, $retries); $upticks = $upticksRaw !== null ? (int)preg_replace('/[^0-9]/', '', (string)$upticksRaw) : null; log_msg("Uptime OID: $uptimeOid, Raw: " . ($upticksRaw ?? 'null')); function format_uptime(?int $ticks): ?string { if ($ticks === null) return null; // ticks sind Hundertstel-Sekunden $seconds = (int)floor($ticks / 100); $days = intdiv($seconds, 86400); $seconds -= $days * 86400; $hours = intdiv($seconds, 3600); $seconds -= $hours * 3600; $minutes = intdiv($seconds, 60); $seconds -= $minutes * 60; $parts = []; if ($days) $parts[] = $days . ' Tage'; if ($hours) $parts[] = $hours . ' Std'; if ($minutes) $parts[] = $minutes . ' Min'; $parts[] = $seconds . ' Sek'; return implode(' ', $parts); } $uptimeStr = format_uptime($upticks); // CPU: ersten Eintrag aus der CPU-Tabelle lesen $cpuTable = $snmp['oids']['cpu_table'] ?? '1.3.6.1.2.1.25.3.3.1.2'; $cpuOid = $cpuTable . '.1'; $cpuRaw = @sget($host, $community, $cpuOid, $timeout, $retries); $cpu = $cpuRaw !== null ? (float)preg_replace('/[^0-9.]/', '', (string)$cpuRaw) : null; log_msg("CPU OID: $cpuOid, Raw: " . ($cpuRaw ?? 'null') . ", Parsed: " . ($cpu ?? 'null')); // Storage-Tabellen: Versuche, C: (Windows) oder Root ("/") zu finden $descrOid = $snmp['oids']['storage_descr'] ?? '1.3.6.1.2.1.25.2.3.1.3'; $unitsOid = $snmp['oids']['storage_units'] ?? '1.3.6.1.2.1.25.2.3.1.4'; $sizeOid = $snmp['oids']['storage_size'] ?? '1.3.6.1.2.1.25.2.3.1.5'; $usedOid = $snmp['oids']['storage_used'] ?? '1.3.6.1.2.1.25.2.3.1.6'; log_msg("Starte Storage-Walk - Descr: $descrOid, Units: $unitsOid, Size: $sizeOid, Used: $usedOid"); $descrWalk = @snmprealwalk($host, $community, $descrOid); $unitsWalk = @snmprealwalk($host, $community, $unitsOid); $sizeWalk = @snmprealwalk($host, $community, $sizeOid); $usedWalk = @snmprealwalk($host, $community, $usedOid); if (!is_array($descrWalk)) { log_msg('WARNING: snmprealwalk für Beschreibungen fehlgeschlagen'); $descrWalk = []; } if (!is_array($unitsWalk)) { log_msg('WARNING: snmprealwalk für Units fehlgeschlagen'); $unitsWalk = []; } if (!is_array($sizeWalk)) { log_msg('WARNING: snmprealwalk für Größe fehlgeschlagen'); $sizeWalk = []; } if (!is_array($usedWalk)) { log_msg('WARNING: snmprealwalk für Used fehlgeschlagen'); $usedWalk = []; } log_msg('Storage-Einträge gefunden: ' . count($descrWalk)); $diskPercent = null; $memPercent = null; $storageEntries = []; // Sammeln aller Storage-Einträge für Debug-Logging if (is_array($descrWalk) && count($descrWalk) > 0) { foreach ($descrWalk as $descrOidFull => $descrRaw) { // Index extrahieren if (!preg_match('/\.(\d+)$/', $descrOidFull, $m)) continue; $idx = $m[1]; $descr = trim((string)$descrRaw, ' "'); // Suche nach passenden Einträgen in anderen Walks mit gleichem Index $units = null; $size = null; $used = null; foreach ($unitsWalk as $oid => $val) { if (preg_match('/\.(\d+)$/', $oid, $m2) && $m2[1] === $idx) { $units = (int)preg_replace('/[^0-9]/', '', (string)$val); break; } } foreach ($sizeWalk as $oid => $val) { if (preg_match('/\.(\d+)$/', $oid, $m2) && $m2[1] === $idx) { $size = (int)preg_replace('/[^0-9]/', '', (string)$val); break; } } foreach ($usedWalk as $oid => $val) { if (preg_match('/\.(\d+)$/', $oid, $m2) && $m2[1] === $idx) { $used = (int)preg_replace('/[^0-9]/', '', (string)$val); break; } } log_msg("Storage[$idx]: Desc='$descr', Size=$size, Used=$used, Units=$units"); if ($size === null || $units === null || $used === null || $size === 0 || $units === 0) { log_msg("Storage[$idx]: SKIP (fehlende oder ungültige Daten)"); continue; } $totalBytes = $size * $units; $usedBytes = $used * $units; $percent = $totalBytes > 0 ? ($usedBytes / $totalBytes) * 100 : 0; $storageEntries[] = [ 'idx' => $idx, 'descr' => $descr, 'percent' => $percent, 'totalGB' => $totalBytes / (1024 ** 3), 'usedGB' => $usedBytes / (1024 ** 3), ]; // Heuristik 1: Suche nach C: oder Root $lower = strtolower($descr); if ($diskPercent === null && (strpos($lower, 'c:') !== false || strpos($lower, 'c:\\') !== false || strpos($lower, 'c:/') !== false || $lower === '/' || strpos($lower, 'root') !== false)) { $diskPercent = $percent; log_msg("Datenträger erkannt (Index $idx): $descr → $percent%"); } // Heuristik 2: Suche nach Physical Memory if ($memPercent === null && ((strpos($lower, 'physical') !== false && strpos($lower, 'memory') !== false) || strpos($lower, 'ram') !== false || strpos($lower, 'physical memory') !== false)) { $memPercent = $percent; log_msg("Speicher erkannt (Index $idx): $descr → $percent%"); } } } // Fallback 1: Wenn keine Disk gefunden, nimm den größten Storage-Eintrag > 1GB if ($diskPercent === null && count($storageEntries) > 0) { log_msg('Fallback: Suche Disk (größter Eintrag > 1GB)'); usort($storageEntries, fn($a, $b) => $b['totalGB'] <=> $a['totalGB']); foreach ($storageEntries as $entry) { if ($entry['totalGB'] > 1) { $diskPercent = $entry['percent']; log_msg('Fallback Disk gefunden (Index ' . $entry['idx'] . '): ' . $entry['descr'] . ' → ' . $diskPercent . '%'); break; } } } // Fallback 2: Wenn kein Speicher gefunden, nimm den kleinsten Eintrag (meist Physical Memory) if ($memPercent === null && count($storageEntries) > 0) { log_msg('Fallback: Suche Memory (kleinster Eintrag)'); usort($storageEntries, fn($a, $b) => $a['totalGB'] <=> $b['totalGB']); $memPercent = $storageEntries[0]['percent']; log_msg('Fallback Memory gefunden (Index ' . $storageEntries[0]['idx'] . '): ' . $storageEntries[0]['descr'] . ' → ' . $memPercent . '%'); } $result = [ 'hostname' => @sget($host, $community, '1.3.6.1.2.1.1.5.0', $timeout, $retries) ?? null, 'uptime' => $uptimeStr, 'upticks' => $upticks, 'cpu_usage' => $cpu, 'memory_usage' => $memPercent !== null ? round($memPercent, 2) : null, 'disk_usage_c' => $diskPercent !== null ? round($diskPercent, 2) : null, 'last_update' => time(), ]; log_msg('RESULT: CPU=' . $result['cpu_usage'] . ', Mem=' . $result['memory_usage'] . ', Disk=' . $result['disk_usage_c']); $resultJson = json_encode($result); // === Cache schreiben === @file_put_contents($cacheFile, $resultJson); log_msg('Cache geschrieben, TTL: ' . $cacheTTL . 's'); echo $resultJson; exit;