From fba9bc89e2e99b26fc4f53846eb666f58f9d2d6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9drix?= Date: Wed, 18 Mar 2026 08:20:13 +0100 Subject: [PATCH] =?UTF-8?q?mise=20=C3=A0=20jour=20de=20la=20logique=20d'in?= =?UTF-8?q?stall=20et=20update?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../monitoring/bin/install-monitoring.sh | 129 ++++---- .../monitoring/bin/monitoring-update.php | 288 +++--------------- 2 files changed, 104 insertions(+), 313 deletions(-) diff --git a/servers/linux/monitoring/bin/install-monitoring.sh b/servers/linux/monitoring/bin/install-monitoring.sh index ce9f152..c5f9201 100755 --- a/servers/linux/monitoring/bin/install-monitoring.sh +++ b/servers/linux/monitoring/bin/install-monitoring.sh @@ -17,45 +17,42 @@ MANIFEST_URL="${UPDATE_BASE_URL}/manifest.txt" INSTALL_DEPS="${INSTALL_DEPS:-true}" -# --- Fonctions --- +# --- Fonctions d'affichage --- +info() { echo -e "\e[34m[INFO]\e[0m $1"; } +ok() { echo -e "\e[32m[OK]\e[0m $1"; } +warn() { echo -e "\e[33m[WARN]\e[0m $1"; } +err() { echo -e "\e[31m[ERR]\e[0m $1"; } + +# --- Fonctions Techniques --- require_root() { if [ "${EUID}" -ne 0 ]; then - echo "ERREUR: Ce script doit être exécuté en root." >&2 + err "Ce script doit être exécuté en root." exit 1 fi } install_deps() { - if [ "${INSTALL_DEPS}" != "true" ]; then - return 0 - fi - - echo "--- Installation des dépendances ---" + if [ "${INSTALL_DEPS}" != "true" ]; then return 0; fi + info "Vérification des dépendances système..." if command -v apt-get >/dev/null 2>&1; then - apt-get update - apt-get install -y curl coreutils findutils grep sed gawk util-linux ca-certificates - # Ajout des modules PHP nécessaires pour vos scripts (curl pour ntfy) - apt-get install -y php-cli php-curl php-common - else - echo "AVERTISSEMENT: Gestionnaire de paquets apt non détecté. Assurez-vous que php-cli et php-curl sont installés." + apt-get update -qq + apt-get install -y -qq curl coreutils findutils grep sed gawk util-linux ca-certificates php-cli php-curl php-common > /dev/null + ok "Dépendances installées." fi } prepare_dirs() { - echo "--- Préparation des répertoires ---" + info "Préparation de l'arborescence dans ${BASE_DIR}..." mkdir -p "${BASE_DIR}/bin" "${BASE_DIR}/lib" "${CONF_DIR}" "${LOG_DIR}" "${STATE_DIR}" "${LOCK_DIR}" "${TMP_DIR}" - chmod 755 "${BASE_DIR}" "${CONF_DIR}" "${LOG_DIR}" "${STATE_DIR}" "${LOCK_DIR}" } fetch_manifest() { - echo "--- Récupération du manifeste ---" + info "Téléchargement du manifeste distant..." curl -fsS "${MANIFEST_URL}" -o "${TMP_DIR}/manifest.txt" } validate_manifest() { - # Validation du format : Hash Mode Chemin - # Exemple : a1b2... 755 bin/script.php awk ' NF == 3 && $1 ~ /^[0-9a-fA-F]{64}$/ && @@ -65,35 +62,36 @@ validate_manifest() { ' "${TMP_DIR}/manifest.txt" } -download_one() { +download_and_install() { local expected_hash="$1" local mode="$2" local rel_path="$3" - - local url="${UPDATE_BASE_URL}/${rel_path}" local dst="${BASE_DIR}/${rel_path}" - - # On ignore le téléchargement si c'est un fichier de conf qui existe déjà - if [[ "$rel_path" == conf/* ]] && [ -f "$dst" ]; then - echo "Skip: $rel_path (existe déjà)" - return 0 + + if [ -f "$dst" ]; then + local current_hash + current_hash=$(sha256sum "$dst" | awk '{print $1}') + if [ "$current_hash" == "$expected_hash" ]; then + return 0 # Déjà à jour + fi + info "Mise à jour : $rel_path" + else + info "Installation : $rel_path" fi - echo "Téléchargement: $rel_path" local tmp_file tmp_file="$(mktemp "${TMP_DIR}/file.XXXXXX")" - - if ! curl -fsS "$url" -o "$tmp_file"; then - echo "ERREUR: Échec du téléchargement de ${url}" >&2 + + if ! curl -fsS "${UPDATE_BASE_URL}/${rel_path}" -o "$tmp_file"; then + err "Échec du téléchargement pour $rel_path" rm -f "$tmp_file" return 1 fi local got_hash - got_hash="$(sha256sum "$tmp_file" | awk '{print $1}')" - + got_hash=$(sha256sum "$tmp_file" | awk '{print $1}') if [ "$got_hash" != "$expected_hash" ]; then - echo "ERREUR: Hash invalide pour ${rel_path}" >&2 + err "Hash invalide pour $rel_path" rm -f "$tmp_file" return 1 fi @@ -103,34 +101,24 @@ download_one() { chmod "$mode" "$dst" } -install_from_manifest() { - echo "--- Installation des fichiers ---" - while read -r hash mode rel_path; do - [ -n "${hash:-}" ] || continue - download_one "$hash" "$mode" "$rel_path" - done < "${TMP_DIR}/manifest-valid.txt" -} +purge_obsolete_files() { + info "Analyse des fichiers obsolètes (Synchronisation avec Git)..." + # On scanne bin, lib et conf + find "${BASE_DIR}/bin" "${BASE_DIR}/lib" "${BASE_DIR}/conf" -type f | while read -r local_file; do + + local rel_path="${local_file#$BASE_DIR/}" -show_next_steps() { - cat < "${TMP_DIR}/manifest-valid.txt"; then - echo "ERREUR: Le manifeste est invalide ou corrompu." >&2 + err "Le manifeste est invalide ou corrompu." exit 1 fi - install_from_manifest - - # Nettoyage + echo "--------------------------------------------------" + info "Phase 1 : Installation et mises à jour" + while read -r hash mode rel_path; do + [ -n "${hash:-}" ] || continue + download_and_install "$hash" "$mode" "$rel_path" + done < "${TMP_DIR}/manifest-valid.txt" + + echo "--------------------------------------------------" + info "Phase 2 : Nettoyage" + purge_obsolete_files + rm -rf "${TMP_DIR}" + echo "--------------------------------------------------" + ok "Opération terminée avec succès." - show_next_steps + # Rappel final + if [ ! -f "${CONF_DIR}/monitoring.local.conf.php" ]; then + warn "Pensez à créer votre fichier ${CONF_DIR}/monitoring.local.conf.php" + fi } main "$@" \ No newline at end of file diff --git a/servers/linux/monitoring/bin/monitoring-update.php b/servers/linux/monitoring/bin/monitoring-update.php index 726d0b5..a59fdc2 100755 --- a/servers/linux/monitoring/bin/monitoring-update.php +++ b/servers/linux/monitoring/bin/monitoring-update.php @@ -1,268 +1,58 @@ #!/usr/bin/env php $install_script]); + echo "\e[31m[ERR]\e[0m Script d'installation introuvable : $install_script\n"; + exit(1); } -if (!is_dir($UPDATE_TMP_DIR)) { - mkdir($UPDATE_TMP_DIR, 0755, true); -} +// On ouvre un pipe pour lire la sortie du Bash en temps réel +$command = "bash " . escapeshellarg($install_script) . " --auto 2>&1"; +$handle = popen($command, 'r'); -/** - * Télécharge et valide le manifeste - */ -function fetch_manifest() { - global $UPDATE_MANIFEST_URL, $UPDATE_TIMEOUT_TOTAL; - - $content = @file_get_contents($UPDATE_MANIFEST_URL, false, stream_context_create([ - 'http' => ['timeout' => $UPDATE_TIMEOUT_TOTAL] - ])); - - if ($content === false) { - log_error("manifest_download_failed", "Impossible de télécharger le manifeste", ["url=$UPDATE_MANIFEST_URL"]); - return false; - } - - $lines = explode("\n", trim($content)); - $manifest_data = []; - - foreach ($lines as $line) { - if (empty(trim($line))) continue; - - $parts = preg_split('/\s+/', trim($line)); - if (count($parts) !== 3) continue; - - list($hash, $mode, $path) = $parts; - - // Validation stricte (Regex identique au Bash) - if (preg_match('/^[0-9a-f]{64}$/i', $hash) && - preg_match('/^(644|755)$/', $mode) && - preg_match('/^(bin|lib|conf)\/[A-Za-z0-9._\/-]+$/', $path) && - strpos($path, '..') === false) { - $manifest_data[] = ['hash' => strtolower($hash), 'mode' => $mode, 'path' => $path]; - } - } - - if (empty($manifest_data)) { - log_error("manifest_invalid", "Le manifeste distant est vide ou invalide"); - return false; - } - - log_info("manifest_downloaded", "Manifeste téléchargé", ["url=$UPDATE_MANIFEST_URL"]); - return $manifest_data; -} - -/** - * Mise à jour d'un fichier unique - */ -function update_one_file($expected_hash, $mode, $rel_path) { - global $MONITORING_BASE_DIR, $UPDATE_BASE_URL, $UPDATE_TMP_DIR, $UPDATE_TIMEOUT_TOTAL; - - $local_file = $MONITORING_BASE_DIR . '/' . $rel_path; - $remote_url = rtrim($UPDATE_BASE_URL, '/') . '/' . $rel_path; - $tmp_file = $UPDATE_TMP_DIR . '/' . basename($rel_path) . '.' . bin2hex(random_bytes(4)); - - $local_hash = file_exists($local_file) ? hash_file('sha256', $local_file) : null; - - if ($local_hash === $expected_hash) { - log_debug("update_not_needed", "Fichier déjà à jour", ["file=$rel_path"]); - return true; - } - - // Téléchargement via cURL pour gérer les timeouts proprement - $ch = curl_init($remote_url); - $fp = fopen($tmp_file, 'w'); - curl_setopt($ch, CURLOPT_FILE, $fp); - curl_setopt($ch, CURLOPT_TIMEOUT, $UPDATE_TIMEOUT_TOTAL); - curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); - $success = curl_exec($ch); - $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); - curl_close($ch); - fclose($fp); - - if (!$success || $http_code !== 200) { - log_error("update_download_failed", "Téléchargement impossible", ["file=$rel_path", "url=$remote_url"]); - @unlink($tmp_file); - return false; - } - - $downloaded_hash = hash_file('sha256', $tmp_file); - if ($downloaded_hash !== $expected_hash) { - log_error("update_hash_mismatch", "Hash téléchargé invalide", ["file=$rel_path", "expected=$expected_hash", "got=$downloaded_hash"]); - @unlink($tmp_file); - return false; - } - - // Installation - ensure_parent_dir($local_file); - chmod($tmp_file, ($mode == '755' ? 0755 : 0644)); - - if (rename($tmp_file, $local_file)) { - if (!$local_hash) { - log_notice("file_created", "Fichier créé", ["file=$rel_path", "mode=$mode"]); - } else { - log_notice("update_applied", "Mise à jour appliquée", ["file=$rel_path", "new_hash=$expected_hash"]); - } - return true; - } - - return false; -} - -/** - * Vérifie et ajoute les tâches cron si elles sont absentes - */ -function ensure_crontab_entries() { - global $MONITORING_BASE_DIR; - - // Définition des tâches souhaitées (Format: "cron_schedule command") - $required_jobs = [ - "*/5 * * * * php {$MONITORING_BASE_DIR}/bin/check_disk.php > /dev/null 2>&1", - "*/5 * * * * php {$MONITORING_BASE_DIR}/bin/check_ram.php > /dev/null 2>&1", - "10 3 * * * php {$MONITORING_BASE_DIR}/bin/monitoring-update.php > /dev/null 2>&1", - "* * * * * php {$MONITORING_BASE_DIR}/bin/alert-engine.php > /dev/null 2>&1" - ]; - - // Récupération du crontab actuel de root - $current_cron = shell_exec("crontab -l 2>/dev/null") ?: ""; - $lines = explode("\n", trim($current_cron)); - $updated = false; - - foreach ($required_jobs as $job) { - // On extrait la commande sans les arguments de temps pour la recherche - // On cherche si le chemin du script est déjà présent - $script_path = explode(' ', $job)[5]; - - $found = false; - foreach ($lines as $line) { - if (strpos($line, $script_path) !== false) { - $found = true; - break; - } - } - - if (!$found) { - log_notice("cron_added", "Ajout d'une tâche au crontab", ["job" => $job]); - $lines[] = $job; - $updated = true; - } - } - - if ($updated) { - // Réécriture du crontab - $tmp_cron = tempnam(sys_get_temp_dir(), 'cron'); - file_put_contents($tmp_cron, implode("\n", $lines) . "\n"); - exec("crontab " . escapeshellarg($tmp_cron)); - unlink($tmp_cron); - } -} - -/** - * Suppression des fichiers obsolètes - */ -function delete_extra_files($manifest_paths) { - global $UPDATE_ALLOW_DELETE, $MONITORING_BASE_DIR; - if (!$UPDATE_ALLOW_DELETE) return; - - $dirs = ['bin', 'lib', 'conf']; - foreach ($dirs as $dir) { - $full_dir = $MONITORING_BASE_DIR . '/' . $dir; - if (!is_dir($full_dir)) continue; - - $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($full_dir)); - foreach ($iterator as $file) { - if ($file->isDir()) continue; - - $rel_path = str_replace($MONITORING_BASE_DIR . '/', '', $file->getPathname()); +if ($handle) { + while (!feof($handle)) { + $line = fgets($handle); + if ($line) { + // 1. On affiche à l'écran en temps réel + echo $line; - // Protection des .local.conf - if (str_ends_with($rel_path, '.local.conf') || str_ends_with($rel_path, '.local.php')) { - continue; - } - - if (!in_array($rel_path, $manifest_paths)) { - if (@unlink($file->getPathname())) { - log_notice("file_deleted", "Fichier obsolète supprimé", ["file=$rel_path"]); - } + // 2. On loggue les erreurs importantes dans le JSONL + if (strpos($line, '[ERR]') !== false) { + log_error("update_process_error", trim($line)); } } } -} - -/** - * Lancement du script de synchronisation local - */ -function run_local_conf_sync() { - global $MONITORING_BASE_DIR; - $sync_script = $MONITORING_BASE_DIR . '/bin/monitoring-update-config.php'; // On cherche la version PHP - - if (file_exists($sync_script)) { - log_info("local_conf_sync_start", "Synchronisation des configs locales"); - passthru("php " . escapeshellarg($sync_script), $return_var); - if ($return_var !== 0) { - log_warning("local_conf_sync_failed", "Échec de synchronisation"); - } - } -} - -// --- Exécution principale --- - -$manifest = fetch_manifest(); -if (!$manifest) exit(2); - -$total = count($manifest); -$updated = 0; -$failed = 0; -$remote_paths = []; - -foreach ($manifest as $item) { - $remote_paths[] = $item['path']; - if (update_one_file($item['hash'], $item['mode'], $item['path'])) { - $updated++; - } else { - $failed++; - } -} - -delete_extra_files($remote_paths); -run_local_conf_sync(); -ensure_crontab_entries(); - -if ($failed > 0) { - log_warning("update_finished_with_errors", "Mise à jour terminée avec erreurs", ["total=$total", "failed=$failed"]); + $exit_code = pclose($handle); } else { - log_info("update_finished", "Mise à jour terminée", ["total=$total", "checked=$updated"]); + $exit_code = 1; +} + +if ($exit_code === 0) { + // Tâches de finalisation PHP + echo "\e[1m--- Finalisation des configurations ---\e[0m\n"; + + run_local_conf_sync(); // Audit des fichiers .local + ensure_crontab_entries(); // Vérification du cron + + log_info("update_finished", "Mise à jour réussie"); + echo "\e[32m[OK]\e[0m Mise à jour terminée avec succès.\n"; +} else { + log_error("update_failed", "Le script de synchronisation a échoué", ["code" => $exit_code]); + echo "\e[31m[ERR]\e[0m Échec de la mise à jour (Code: $exit_code).\n"; + exit($exit_code); } exit_with_status(); \ No newline at end of file