agrégat (= check=all) * check.php?check=swap&key=... -> une sonde précise * Sondes : all | disk | load | mem | swap | procs | users | file-age | uptime | http | tcp * Sortie JSON : {"status":"ok|warn|crit|unknown","value":...,"message":"...","at":"..."} * * Mises à jour : pas d'auto-update. Activez la newsletter « mises à jour outils CLI » * dans votre compte En Service ; quand une nouvelle version sort, retéléchargez ce fichier. */ /* ===== Configuration (modifiée par ?setup ; préservée par ?update) ===== */ const ENSERVICE_KEY = 'COLLEZ_VOTRE_CLE_PARTAGEE_ICI'; const ENSERVICE_ALLOW_IPS = []; // optionnel : ['51.158.0.10','51.158.0.11'] const ENSERVICE_PROBE_VERSION = '2.0.0'; /* ===== Bibliothèque ===== */ function es_qf($k, $def) { return isset($_GET[$k]) ? (float)$_GET[$k] : $def; } function es_qs($k, $def) { return isset($_GET[$k]) ? (string)$_GET[$k] : $def; } /* Seuils façon Nagios. $higher_is_bad : true => alerte quand v >= seuil. */ function es_thr($v, $warn, $crit, $higher_is_bad = true) { if ($higher_is_bad) return $v >= $crit ? 'crit' : ($v >= $warn ? 'warn' : 'ok'); return $v <= $crit ? 'crit' : ($v <= $warn ? 'warn' : 'ok'); } function es_meminfo() { $m = []; foreach (@file('/proc/meminfo') ?: [] as $line) { if (preg_match('/^(\w+):\s+(\d+)/', $line, $x)) $m[$x[1]] = (int)$x[2] * 1024; // kB -> octets } return $m; } function es_human_bytes($b) { $u = ['o', 'Ko', 'Mo', 'Go', 'To']; $i = 0; while ($b >= 1024 && $i < 4) { $b /= 1024; $i++; } return round($b, 1) . ' ' . $u[$i]; } function es_out($status, $value, $message, array $extra = []) { header('Content-Type: application/json; charset=utf-8'); echo json_encode(array_merge([ 'status' => $status, 'value' => $value, 'message' => $message, 'at' => date('c'), ], $extra), JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); exit; } function es_guard() { $key = isset($_GET['key']) ? $_GET['key'] : (isset($_SERVER['HTTP_X_ENSERVICE_KEY']) ? $_SERVER['HTTP_X_ENSERVICE_KEY'] : ''); $ok = ENSERVICE_KEY !== 'COLLEZ_VOTRE_CLE_PARTAGEE_ICI' && hash_equals(ENSERVICE_KEY, (string)$key); if ($ok && ENSERVICE_ALLOW_IPS) { $ok = in_array(isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '', ENSERVICE_ALLOW_IPS, true); } if (!$ok) { http_response_code(403); header('Content-Type: application/json'); echo json_encode(['status' => 'forbidden']); exit; } } /* Réécrit les lignes de config (clé + IP) dans un contenu PHP, sans toucher au reste. */ function es_inject_config($content, $key, array $ips) { $content = preg_replace("/const ENSERVICE_KEY\\s*=\\s*'(?:[^'\\\\]|\\\\.)*';/", 'const ENSERVICE_KEY = ' . var_export($key, true) . ';', $content, 1); $q = []; foreach ($ips as $ip) $q[] = "'" . addslashes($ip) . "'"; $ipsLit = $q ? '[' . implode(', ', $q) . ']' : '[]'; $content = preg_replace("/const ENSERVICE_ALLOW_IPS\\s*=\\s*\\[[^\\]]*\\];/", 'const ENSERVICE_ALLOW_IPS = ' . $ipsLit . ';', $content, 1); return $content; } /* ===== Installeur web : check.php?setup ===== */ function es_setup() { $placeholder = 'COLLEZ_VOTRE_CLE_PARTAGEE_ICI'; $configured = ENSERVICE_KEY !== $placeholder; $writable = is_writable(__FILE__); $msg = ''; $ok = false; if ($_SERVER['REQUEST_METHOD'] === 'POST') { $new = trim(isset($_POST['key']) ? $_POST['key'] : ''); $cur = trim(isset($_POST['current']) ? $_POST['current'] : ''); if ($configured && !hash_equals(ENSERVICE_KEY, $cur)) { $msg = 'Clé actuelle incorrecte.'; } elseif (strlen($new) < 16) { $msg = 'Clé invalide (collez la clé depuis votre tableau de bord En Service).'; } elseif (!$writable) { $msg = "Ce fichier n'est pas inscriptible. Éditez la ligne ENSERVICE_KEY à la main, ou : chmod u+w check.php."; } else { $content = es_inject_config(file_get_contents(__FILE__), $new, ENSERVICE_ALLOW_IPS); $tmp = __FILE__ . '.tmp'; if (@file_put_contents($tmp, $content) !== false && @rename($tmp, __FILE__)) { $ok = true; $configured = true; $msg = 'Configuration enregistrée. La sonde est prête.'; } else { @unlink($tmp); $msg = "Échec d'écriture du fichier."; } } } header('Content-Type: text/html; charset=utf-8'); $keyInput = isset($_POST['key']) ? $_POST['key'] : ''; ?> Sonde En Service - configuration

Sonde En Service

Surveillance de la santé serveur. Collez votre clé partagée (tableau de bord En Service).

Fichier non inscriptible : éditez ENSERVICE_KEY dans check.php, ou chmod u+w.

Test : check.php?check=all doit renvoyer du JSON.

(int)$free, 'total_bytes' => (int)$total]); } case 'load': { $warn = es_qf('warn', 1.0); $crit = es_qf('crit', 2.0); $la = function_exists('sys_getloadavg') ? sys_getloadavg() : [0, 0, 0]; $cores = (int)(@shell_exec('nproc 2>/dev/null') ?: 1) ?: 1; $per = round($la[0] / $cores, 2); es_out(es_thr($per, $warn, $crit), $per, "Charge " . round($la[0], 2) . " sur $cores coeur(s) = $per/coeur", ['load1' => round($la[0], 2), 'load5' => round($la[1], 2), 'load15' => round($la[2], 2), 'cores' => $cores]); } case 'mem': { $warn = es_qf('warn', 85); $crit = es_qf('crit', 95); $m = es_meminfo(); $total = isset($m['MemTotal']) ? $m['MemTotal'] : 0; $avail = isset($m['MemAvailable']) ? $m['MemAvailable'] : (isset($m['MemFree']) ? $m['MemFree'] : 0); if (!$total) es_out('unknown', null, '/proc/meminfo illisible'); $used = round(($total - $avail) / $total * 100, 1); es_out(es_thr($used, $warn, $crit), $used, "Memoire : $used% utilisee (" . es_human_bytes((int)$avail) . " dispo / " . es_human_bytes((int)$total) . ")", ['available_bytes' => (int)$avail, 'total_bytes' => (int)$total]); } case 'swap': { $warn = es_qf('warn', 40); $crit = es_qf('crit', 70); $m = es_meminfo(); $total = isset($m['SwapTotal']) ? $m['SwapTotal'] : 0; $free = isset($m['SwapFree']) ? $m['SwapFree'] : 0; if (!$total) es_out('ok', 0, 'Pas de swap configure', ['total_bytes' => 0]); $used = round(($total - $free) / $total * 100, 1); es_out(es_thr($used, $warn, $crit), $used, "Swap : $used% utilise", ['free_bytes' => (int)$free, 'total_bytes' => (int)$total]); } case 'procs': { $warn = es_qf('warn', 400); $crit = es_qf('crit', 600); $match = es_qs('match', ''); $out = @shell_exec('ps -e -o comm= 2>/dev/null') ?: ''; $lines = array_filter(explode("\n", trim($out))); if ($match !== '') $lines = array_filter($lines, function ($l) use ($match) { return stripos($l, $match) !== false; }); $n = count($lines); if ($match !== '' && $n === 0) es_out('crit', 0, "Aucun processus « $match »"); $status = $match !== '' ? 'ok' : es_thr($n, $warn, $crit); es_out($status, $n, $match !== '' ? "$n processus « $match »" : "$n processus"); } case 'users': { $warn = es_qf('warn', 5); $crit = es_qf('crit', 10); $out = trim((string)@shell_exec('who 2>/dev/null')); $n = $out === '' ? 0 : count(array_filter(explode("\n", $out))); es_out(es_thr($n, $warn, $crit), $n, "$n utilisateur(s) connecte(s)"); } case 'file-age': { $file = es_qs('file', ''); $warn = es_qf('warn', 90000); $crit = es_qf('crit', 172800); if ($file === '' || !@is_file($file)) es_out('crit', null, "Fichier introuvable : $file"); $age = time() - (int)@filemtime($file); es_out(es_thr($age, $warn, $crit), $age, "Modifie il y a " . round($age / 3600, 1) . " h", ['mtime' => (int)@filemtime($file)]); } case 'uptime': { $warn = es_qf('warn', 600); $crit = es_qf('crit', 120); // secondes : trop bas = reboot recent $up = (float)(@file_get_contents('/proc/uptime') ?: 0); es_out(es_thr($up, $warn, $crit, false), (int)$up, "Uptime : " . round($up / 86400, 1) . " j"); } case 'http': { $url = es_qs('url', ''); $expect = es_qs('expect', ''); $warn = (int)es_qf('warn_ms', 0); if ($url === '' || !preg_match('#^https?://#i', $url)) es_out('unknown', null, 'URL manquante/invalide'); $t0 = microtime(true); $ch = curl_init($url); curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 15, CURLOPT_FOLLOWLOCATION => true, CURLOPT_SSL_VERIFYPEER => false]); $body = curl_exec($ch); $code = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); $lat = (int)round((microtime(true) - $t0) * 1000); if ($code === 0 || $code >= 500) es_out('crit', $lat, "Injoignable / 5xx ($code)"); if ($code >= 400) es_out('crit', $lat, "HTTP $code"); if ($expect !== '' && stripos((string)$body, $expect) === false) es_out('crit', $lat, "Mot-cle « $expect » absent"); if ($warn > 0 && $lat > $warn) es_out('warn', $lat, "Lent ($lat ms)"); es_out('ok', $lat, "HTTP $code", ['latency_ms' => $lat]); } case 'tcp': { $host = es_qs('host', '127.0.0.1'); $port = (int)es_qf('port', 0); $to = (int)es_qf('timeout', 5); if (!$port) es_out('unknown', null, 'Port manquant (?port=)'); $t0 = microtime(true); $fp = @fsockopen($host, $port, $errno, $errstr, $to); $lat = (int)round((microtime(true) - $t0) * 1000); if (!$fp) es_out('crit', null, "$host:$port injoignable : " . ($errstr ?: 'timeout')); fclose($fp); es_out('ok', $lat, "$host:$port ouvert", ['latency_ms' => $lat]); } case 'all': default: { $checks = []; $t = @disk_total_space('/'); $f = @disk_free_space('/'); $checks['disk'] = $t ? es_thr(round(($t - $f) / $t * 100, 1), 80, 90) : 'unknown'; $la = function_exists('sys_getloadavg') ? sys_getloadavg() : [0, 0, 0]; $cores = (int)(@shell_exec('nproc 2>/dev/null') ?: 1) ?: 1; $checks['load'] = es_thr($la[0] / $cores, 1.0, 2.0); $m = es_meminfo(); if (isset($m['MemTotal']) && $m['MemTotal'] > 0) { $avail = isset($m['MemAvailable']) ? $m['MemAvailable'] : (isset($m['MemFree']) ? $m['MemFree'] : 0); $checks['mem'] = es_thr(round(($m['MemTotal'] - $avail) / $m['MemTotal'] * 100, 1), 85, 95); } if (isset($m['SwapTotal']) && $m['SwapTotal'] > 0) { $checks['swap'] = es_thr(round(($m['SwapTotal'] - (isset($m['SwapFree']) ? $m['SwapFree'] : 0)) / $m['SwapTotal'] * 100, 1), 40, 70); } $rank = ['ok' => 0, 'warn' => 1, 'crit' => 2, 'unknown' => 1]; $worst = 'ok'; foreach ($checks as $s) if ($rank[$s] > $rank[$worst]) $worst = $s; es_out($worst, null, $worst === 'ok' ? 'Tous les indicateurs serveur sont au vert' : 'Indicateur(s) serveur en anomalie', ['checks' => $checks, 'version' => ENSERVICE_PROBE_VERSION]); } }