#!/bin/bash # ============================================= # Script de vérification complète de la stack ADS-B # Version : 6.0 (7 bugs corrigés) # Date : 26/06/2026 # Auteur : JC Abiven (avec Mistral AI) # ============================================= # Vérifier que jq est installé if ! command -v jq &> /dev/null; then echo -e "\033[0;31m✗ jq n'est pas installé. Installez-le avec : sudo apt install jq\033[0m" exit 1 fi # Couleurs pour l'affichage GREEN='\033[0;32m' RED='\033[0;31m' YELLOW='\033[1;33m' BLUE='\033[0;34m' MAGENTA='\033[0;35m' CYAN='\033[0;36m' NC='\033[0m' # Fonction pour afficher un message avec une couleur print_status() { echo -e "${1}${2}${NC}" } # Fonction pour afficher un titre print_title() { echo -e "\n${BLUE}═══ ${1} ═══${NC}" } # Fonction pour afficher une section print_section() { echo -e "\n${CYAN}═══ ${1} ═══${NC}" } # Récupérer l'IP de l'hôte HOST_IP=$(hostname -I | awk '{print $1}') # Afficher l'en-tête echo -e "${MAGENTA}╔══════════════════════════════════════════════════════╗" echo -e "║ Stack ADS-B Linux-25 — Vérification complète ║" echo -e "║ $(date "+%a %d/%m/%Y %H:%M:%S") ║" echo -e "╚══════════════════════════════════════════════════════╝" print_status "$YELLOW" " → IP détectée : $HOST_IP" # ============================================= # 1. Vérification des conteneurs Docker # ============================================= print_section "1. Conteneurs Docker" DOCKER_CONTAINERS=("adsb-readsb" "adsb-tar1090" "adsb-adsb2pg" "adsb-webapp" "wordpress-postgres") for container in "${DOCKER_CONTAINERS[@]}"; do if docker ps | grep -q "$container"; then STATUS=$(docker inspect --format='{{.State.Health.Status}}' "$container" 2>/dev/null || echo "none") if [ "$STATUS" = "healthy" ]; then print_status "$GREEN" " ✓ $container [healthy]" else print_status "$GREEN" " ✓ $container" fi else print_status "$RED" " ✗ $container — non démarré" fi done # ============================================= # 2. Vérification du dongle RTL-SDR # ============================================= print_section "2. Dongle RTL-SDR" if docker logs adsb-readsb 2>&1 | grep -q "rtlsdr: using device #0: Generic RTL2832U OEM (AIRNAV, ADSB_1090, SN 00000010)"; then print_status "$GREEN" " ✓ AirNav FlightStick SN:00000010 détecté" # Correction : Pattern grep pour extraire le gain (ex: "49.6 dB") GAIN=$(docker logs adsb-readsb 2>&1 | grep -oP '\d+\.\d+ dB' | tail -1 || echo "inconnu") print_status "$GREEN" " → Gain actuel : $GAIN" else print_status "$RED" " ✗ Dongle RTL-SDR non détecté" fi # Vérifier le symlink /dev/adsb_dongle if [ -L "/dev/adsb_dongle" ]; then print_status "$GREEN" " ✓ Symlink /dev/adsb_dongle présent" else print_status "$YELLOW" " ⚠ Symlink /dev/adsb_dongle introuvable" fi # ============================================= # 3. Vérification des données ADS-B # ============================================= print_section "3. Données ADS-B" # Vérifier tar1090 if curl -s --max-time 5 http://127.0.0.1:8090 >/dev/null; then # Correction : Utiliser .messages au lieu de .totalMessages (champ réel dans tar1090) TAR1090_STATS=$(curl -s --max-time 5 http://127.0.0.1:8090/data/stats.json 2>/dev/null) if [ -n "$TAR1090_STATS" ]; then MESSAGES=$(echo "$TAR1090_STATS" | jq -r '.messages // "N/A"') print_status "$GREEN" " ✓ tar1090 actif — $MESSAGES messages décodés" else print_status "$YELLOW" " ⚠ Impossible de récupérer les stats de tar1090" fi AIRCRAFT_DATA=$(curl -s --max-time 5 http://127.0.0.1:8090/data/aircraft.json 2>/dev/null) if [ -n "$AIRCRAFT_DATA" ]; then # Correction : Utiliser .aircraft[] pour itérer sur les avions (pas sur tout le JSON) AIRCRAFT_COUNT=$(echo "$AIRCRAFT_DATA" | jq '.aircraft | length // 0') # Correction : Filtre valide pour .lat et .lon (évite l'erreur "Cannot index number with string") AIRCRAFT_WITH_POS=$(echo "$AIRCRAFT_DATA" | jq '[.aircraft[] | select(.lat != null and .lon != null)] | length // 0') print_status "$GREEN" " ✓ $AIRCRAFT_COUNT avion(s) en vue dont $AIRCRAFT_WITH_POS avec position GPS" else print_status "$YELLOW" " ⚠ Impossible de récupérer les données des avions" fi else print_status "$RED" " ✗ tar1090 non accessible" fi # Vérifier le volume readsb-run if docker exec adsb-readsb ls /run/readsb/aircraft.pb >/dev/null 2>&1; then print_status "$GREEN" " ✓ Volume readsb-run : aircraft.pb présent" else print_status "$YELLOW" " ⚠ Volume readsb-run : aircraft.pb introuvable" fi # ============================================= # 4. Vérification des ports réseau (avec adresses IP) # ============================================= print_section "4. Ports réseau" # Fonction pour obtenir l'adresse IP et le port d'écoute get_listening_info() { local port=$1 if command -v ss &> /dev/null; then ss -tulnp | grep -E ":$port\s" | awk '{print $5}' | head -1 else netstat -tulnp 2>/dev/null | grep -E ":$port\s" | awk '{print $4}' | head -1 fi } # Liste des ports à vérifier : format "port:service:ip_attendue" PORTS=( "8080:webapp PHP:127.0.0.1" "8090:tar1090 nginx:0.0.0.0" "30005:Beast TCP readsb:0.0.0.0" "5432:PostgreSQL:127.0.0.1" "30002:RAW TCP:127.0.0.1" "30003:SBS TCP:127.0.0.1" ) for port_info in "${PORTS[@]}"; do PORT=${port_info%%:*} SERVICE=${port_info#*:} SERVICE=${SERVICE%:*} EXPECTED_IP=${port_info##*:} LISTENING_INFO=$(get_listening_info "$PORT" || echo "inconnu") ACTUAL_IP=$(echo "$LISTENING_INFO" | cut -d: -f1) ACTUAL_PORT=$(echo "$LISTENING_INFO" | cut -d: -f2) if timeout 2 nc -z "$EXPECTED_IP" "$PORT" 2>/dev/null; then print_status "$GREEN" " ✓ Port $PORT ($SERVICE) — IP: ${ACTUAL_IP:-*}:${ACTUAL_PORT:-$PORT} (attendu: $EXPECTED_IP:$PORT)" else print_status "$RED" " ✗ Port $PORT ($SERVICE) — IP: ${ACTUAL_IP:-*}:${ACTUAL_PORT:-$PORT} (attendu: $EXPECTED_IP:$PORT)" fi done # Vérifier la connectivité Beast TCP via la gateway if timeout 2 nc -z 172.20.0.1 30005 2>/dev/null; then print_status "$GREEN" " ✓ readsb attaché à adsb-net : 172.20.0.2 (gateway: 172.20.0.1)" else print_status "$RED" " ✗ Problème de connectivité avec adsb-net (172.20.0.1:30005)" fi # ============================================= # 5. Vérification de PostgreSQL et du schéma adsb # ============================================= print_section "5. PostgreSQL — schéma adsb" # Vérifier l'accès à PostgreSQL if docker exec wordpress-postgres pg_isready -U pgz_admin -d wpz_postgres 2>/dev/null | grep -q "accepting connections"; then print_status "$GREEN" " ✓ PostgreSQL accessible" PG_VERSION=$(docker exec wordpress-postgres psql -U pgz_admin -d wpz_postgres -t -c "SELECT version();" 2>/dev/null | head -1 | tr -d ' ') print_status "$GREEN" " → $PG_VERSION" # Obtenir le port et l'IP d'écoute de PostgreSQL PG_LISTEN_INFO=$(docker exec wordpress-postgres ss -tulnp 2>/dev/null | grep -E ":5432\s" | awk '{print $5}' | head -1 || echo "127.0.0.1:5432") PG_ACTUAL_IP=$(echo "$PG_LISTEN_INFO" | cut -d: -f1) PG_ACTUAL_PORT=$(echo "$PG_LISTEN_INFO" | cut -d: -f2) print_status "$GREEN" " → Écoute sur : ${PG_ACTUAL_IP}:${PG_ACTUAL_PORT}" # Correction : Utiliser pg_class + pg_namespace pour obtenir la taille du schéma SCHEMA_SIZE=$(docker exec wordpress-postgres psql -U pgz_admin -d wpz_postgres -t -c " SELECT pg_size_pretty(SUM(pg_total_relation_size(C.oid))) FROM pg_class C JOIN pg_namespace N ON N.oid = C.relnamespace WHERE N.nspname = 'adsb';" 2>/dev/null | tr -d ' ') print_status "$GREEN" " → Taille totale schéma adsb : ${SCHEMA_SIZE:-inconnu}" else print_status "$RED" " ✗ PostgreSQL non accessible" fi # Vérifier les partitions PARTITIONS_POSITIONS=$(docker exec wordpress-postgres psql -U pgz_admin -d wpz_postgres -t -c "SELECT COUNT(*) FROM pg_tables WHERE schemaname = 'adsb' AND tablename LIKE 'positions_%';" 2>/dev/null | tr -d ' ') PARTITIONS_HISTORY=$(docker exec wordpress-postgres psql -U pgz_admin -d wpz_postgres -t -c "SELECT COUNT(*) FROM pg_tables WHERE schemaname = 'adsb' AND tablename LIKE 'aircraft_history_%';" 2>/dev/null | tr -d ' ') print_status "$GREEN" " ✓ $PARTITIONS_POSITIONS sous-partitions adsb.positions" print_status "$GREEN" " ✓ $PARTITIONS_HISTORY sous-partitions adsb.aircraft_history" # Vérifier les insertions récentes POSITIONS_5MIN=$(docker exec wordpress-postgres psql -U pgz_admin -d wpz_postgres -t -c "SELECT COUNT(*) FROM adsb.positions WHERE ts >= NOW() - INTERVAL '5 minutes';" 2>/dev/null | tr -d ' ') print_status "$GREEN" " ✓ $POSITIONS_5MIN positions insérées dans les 5 dernières minutes" # Vérifier le remplissage de aircraft_desc DESC_FILL=$(docker exec wordpress-postgres psql -U pgz_admin -d wpz_postgres -t -c "SELECT ROUND(COUNT(aircraft_desc)*100.0/COUNT(*),1) FROM adsb.positions WHERE ts >= NOW() - INTERVAL '30 minutes' AND lat IS NOT NULL;" 2>/dev/null | tr -d ' ') print_status "$GREEN" " ✓ aircraft_desc rempli à ${DESC_FILL:-0}% (30 dernières min)" # ============================================= # 6. Vérification de adsb2pg (collecteur) # ============================================= print_section "6. adsb2pg — collecteur" if docker ps | grep -q "adsb-adsb2pg"; then ADSB2PG_LOGS=$(docker logs adsb-adsb2pg 2>&1 | tail -1) if echo "$ADSB2PG_LOGS" | grep -q "positions insérées"; then print_status "$GREEN" " ✓ adsb2pg actif : $ADSB2PG_LOGS" else print_status "$YELLOW" " ⚠ adsb2pg actif mais aucun log récent" fi # Vérifier la dernière insertion (en secondes) LAST_INSERT_SEC=$(docker exec wordpress-postgres psql -U pgz_admin -d wpz_postgres -t -c "SELECT EXTRACT(EPOCH FROM (NOW() - MAX(inserted_at))) FROM adsb.positions WHERE inserted_at >= NOW() - INTERVAL '10 minutes';" 2>/dev/null | tr -d ' ') if [ -n "$LAST_INSERT_SEC" ]; then LAST_INSERT_SEC_INT=${LAST_INSERT_SEC%.*} # Supprimer la partie décimale if [ "$LAST_INSERT_SEC_INT" -le 10 ] 2>/dev/null; then print_status "$GREEN" " ✓ Dernière insertion il y a ${LAST_INSERT_SEC_INT}s (cycle 5s actif)" else print_status "$RED" " ✗ Dernière insertion trop ancienne (${LAST_INSERT_SEC_INT}s)" fi else print_status "$RED" " ✗ Impossible de vérifier la dernière insertion" fi else print_status "$RED" " ✗ adsb2pg non démarré" fi # ============================================= # 7. Vérification de la webapp # ============================================= print_section "7. Webapp — interface utilisateur" # Vérifier les endpoints API API_ENDPOINTS=("action=live" "action=kpi" "action=trajectories") for endpoint in "${API_ENDPOINTS[@]}"; do if curl -s --max-time 5 "http://127.0.0.1:8080/api.php?$endpoint" >/dev/null; then print_status "$GREEN" " ✓ api.php?$endpoint : OK (IP: 127.0.0.1:8080)" else print_status "$RED" " ✗ api.php?$endpoint : KO (IP: 127.0.0.1:8080)" fi done # Correction : Compter les
  • directement dans le conteneur webapp (pas via curl) ONGETS=$(docker exec adsb-webapp grep -c "
  • " /var/www/html/index.php 2>/dev/null || echo "0") print_status "$GREEN" " ✓ $ONGETS onglet(s) dans index.php (IP: 127.0.0.1:8080)" # Correction : Taille de app.js directement dans le conteneur APP_JS_SIZE=$(docker exec adsb-webapp wc -c < /var/www/html/app.js 2>/dev/null | awk '{print $1}' || echo "0") print_status "$GREEN" " → app.js : $APP_JS_SIZE octets (IP: 127.0.0.1:8080)" # Vérifier l'accès LAN if curl -s --max-time 5 "http://$HOST_IP:8080" >/dev/null; then print_status "$GREEN" " ✓ Accès LAN http://$HOST_IP:8080 : HTTP 200 (IP: $HOST_IP:8080)" else print_status "$RED" " ✗ Accès LAN http://$HOST_IP:8080 : KO (IP: $HOST_IP:8080)" fi # ============================================= # 8. Vérification de l'autogain # ============================================= print_section "8. Autogain" # Correction : Pattern grep pour extraire le gain (ex: "49.6 dB") GAIN_STATUS=$(docker logs adsb-readsb 2>&1 | grep -oP '\d+\.\d+ dB' | tail -1 || echo "inconnu") print_status "$GREEN" " ✓ Gain courant : $GAIN_STATUS" # Vérifier les cycles avec messages insuffisants INSUFFICIENT_CYCLES=$(docker logs adsb-readsb 2>&1 | grep -c "messages insuffisants" || echo "0") if [ "$INSUFFICIENT_CYCLES" -gt 0 ] 2>/dev/null; then print_status "$YELLOW" " ⚠ $INSUFFICIENT_CYCLES cycle(s) avec messages insuffisants (antenne/portée ?)" else print_status "$GREEN" " ✓ Aucun cycle avec messages insuffisants" fi # ============================================= # 9. Statistiques readsb # ============================================= print_section "9. Statistiques readsb" # Correction : Endpoint correct pour les stats readsb (via tar1090 ou lighttpd interne) STATS=$(curl -s --max-time 5 http://127.0.0.1:8090/data/readsb/stats.json 2>/dev/null) if [ -n "$STATS" ]; then TOTAL_MSG=$(echo "$STATS" | jq -r '.totalMessages // "N/A"') MSG_PER_SEC=$(echo "$STATS" | jq -r '.messagesPerSecond // "N/A"') print_status "$GREEN" " ✓ Statistiques readsb accessibles (IP: 127.0.0.1:8090) :" print_status "$GREEN" " - Messages totaux : $TOTAL_MSG" print_status "$GREEN" " - Messages/seconde : $MSG_PER_SEC" else # Fallback : Essayer via lighttpd interne (port 80 dans le conteneur readsb) STATS=$(docker exec adsb-readsb curl -s http://localhost/readsb/stats.json 2>/dev/null) if [ -n "$STATS" ]; then TOTAL_MSG=$(echo "$STATS" | jq -r '.totalMessages // "N/A"') MSG_PER_SEC=$(echo "$STATS" | jq -r '.messagesPerSecond // "N/A"') print_status "$GREEN" " ✓ Statistiques readsb accessibles (via lighttpd, IP: localhost:80) :" print_status "$GREEN" " - Messages totaux : $TOTAL_MSG" print_status "$GREEN" " - Messages/seconde : $MSG_PER_SEC" else print_status "$YELLOW" " ⚠ Impossible de récupérer les statistiques de readsb (essayez : docker exec adsb-readsb curl -s http://localhost/readsb/stats.json | jq)" fi fi # ============================================= # Résumé final # ============================================= print_title "RÉSUMÉ" # Compter les erreurs et avertissements dans le script actuel ERRORS=0 WARNINGS=0 SCRIPT_OUTPUT=$(mktemp) ./check_adsb_v6_fixed.sh > "$SCRIPT_OUTPUT" 2>&1 while IFS= read -r line; do if [[ "$line" == *"$RED✗"* ]]; then ((ERRORS++)) elif [[ "$line" == *"$YELLOW⚠"* ]]; then ((WARNINGS++)) fi done < "$SCRIPT_OUTPUT" rm "$SCRIPT_OUTPUT" if [ "$ERRORS" -eq 0 ] && [ "$WARNINGS" -eq 0 ]; then print_status "$GREEN" " ✓ Stack opérationnel — Aucun problème détecté" elif [ "$ERRORS" -eq 0 ]; then print_status "$YELLOW" " ⚠ Stack opérationnel — $WARNINGS avertissement(s)" else print_status "$RED" " ✗ Stack en erreur — $ERRORS erreur(s), $WARNINGS avertissement(s)" fi print_status "$BLUE" " Dashboard : http://$HOST_IP:8080" print_status "$BLUE" " tar1090 : http://$HOST_IP:8090"