Archivage : verrou exclusif par boîtier, chdir dans cron_quarantaine

This commit is contained in:
2026-10-02 14:56:27 +00:00
parent 3a9056f5bc
commit b584d411e4
3 changed files with 306 additions and 220 deletions
+148 -131
View File
@@ -1,14 +1,17 @@
<?php <?php
// ============================================================================ // ============================================================================
// cron_archivage.php - Traitement périodique des archives de la flotte // cron_archivage.php - Traitement périodique des archives de la flotte
// (cf synthèse §32-36). Exécuté via cron, dans le conteneur ship2net-php : // (cf synthèse §32-40, §43). Exécuté NATIVEMENT sur le VPS (pas dans le
// docker exec ship2net-php php /var/www/cron_archivage.php // conteneur ship2net-php, cf synthèse §38.1), de façon planifiée :
// /usr/bin/php /var/www/cron_archivage.php
// //
// Construction progressive, bloc par bloc : // Pour chaque boîtier connu (TB_Cartes avec une adresse NetBird) : liste des
// Bloc 1 (ce fichier) : résolution des bateaux actifs + liste des archives // archives en attente sur son API, récupération, vérification d'intégrité,
// en attente sur chacun. // déchiffrement, affectation/MMSI (quarantaine si besoin), génération,
// Blocs suivants (à venir) : récupération, vérification d'intégrité, // chargement, enregistrement dans TB_Archives_traitees, confirmation de
// résolution de génération, chargement, confirmation. // suppression côté CM5. Chaque boîtier est traité sous verrou exclusif
// (Verrouiller_boitier(), synthèse §44.6) : un boîtier déjà en cours de
// traitement par un autre processus est sauté pour ce passage.
// ============================================================================ // ============================================================================
// Certains fichiers du site (fonctions_mail.php notamment) utilisent des // Certains fichiers du site (fonctions_mail.php notamment) utilisent des
@@ -315,163 +318,177 @@ function Executer_cron_archivage()
foreach ($boitiers as $boitier) foreach ($boitiers as $boitier)
{ {
$adresse_mac = $boitier['Adresse_mac']; $adresse_mac = $boitier['Adresse_mac'];
$resultat = Lister_archives_en_attente($boitier['Adresse_netbird']); $verrou = Verrouiller_boitier($adresse_mac);
if (!$verrou['ok'])
if (!$resultat['ok'])
{ {
echo "Boîtier {$adresse_mac} : " . $resultat['raison'] . " - ignoré pour ce cycle.\n"; echo "Boîtier {$adresse_mac} : " . $verrou['raison'] . " - ignoré pour ce cycle.\n";
continue; continue;
} }
$nb = count($resultat['archives']); try
if ($nb === 0) continue; // rien à signaler pour ce boîtier ce cycle-ci
// Résolu UNE SEULE FOIS par boîtier (pas par archive) : toutes les archives d'un même passage
// partagent la même adresse MAC, et l'affectation ne change jamais en cours d'exécution du
// script - seul un geste administratif explicite la modifie (cf synthèse §38).
$affectation = Resoudre_affectation_active($db, $adresse_mac);
$libelle = $affectation['existe'] ? "Bateau {$affectation['id_bateau']}" : "Boîtier {$adresse_mac} (aucune affectation active)";
echo "{$libelle} : {$nb} archive(s) en attente.\n";
foreach ($resultat['archives'] as $archive)
{ {
$nom = $archive['nom']; $resultat = Lister_archives_en_attente($boitier['Adresse_netbird']);
if (Archive_deja_traitee($db, $nom)) if (!$resultat['ok'])
{ {
echo " - {$nom} : déjà traitée - confirmation de suppression seulement.\n"; echo "Boîtier {$adresse_mac} : " . $resultat['raison'] . " - ignoré pour ce cycle.\n";
Confirmer_suppression_archive($boitier['Adresse_netbird'], $nom);
continue; continue;
} }
$recuperation = Recuperer_archive($boitier['Adresse_netbird'], $nom); $nb = count($resultat['archives']);
if (!$recuperation['ok']) if ($nb === 0) continue; // rien à signaler pour ce boîtier ce cycle-ci
{
echo " - {$nom} : ÉCHEC de récupération (" . $recuperation['raison'] . ") - nouvelle tentative au prochain cycle.\n";
continue;
}
if (!Integrite_confirmee($nom, $recuperation['contenu'])) // Résolu UNE SEULE FOIS par boîtier (pas par archive) : toutes les archives d'un même passage
{ // partagent la même adresse MAC, et l'affectation ne change jamais en cours d'exécution du
echo " - {$nom} : empreinte invalide, archive potentiellement corrompue - nouvelle tentative au prochain cycle.\n"; // script - seul un geste administratif explicite la modifie (cf synthèse §38).
continue; $affectation = Resoudre_affectation_active($db, $adresse_mac);
} $libelle = $affectation['existe'] ? "Bateau {$affectation['id_bateau']}" : "Boîtier {$adresse_mac} (aucune affectation active)";
echo "{$libelle} : {$nb} archive(s) en attente.\n";
// Aucune affectation active (jamais affecté, ou retiré) : quarantaine directe, sans foreach ($resultat['archives'] as $archive)
// déchiffrement - ni l'en-tête ni le contenu ne servent à rien sans bateau à qui l'attribuer
// (cf synthèse §38) :
if (!$affectation['existe'])
{ {
echo " - {$nom} : aucune affectation active pour ce boîtier - mise en quarantaine.\n"; $nom = $archive['nom'];
Mettre_en_quarantaine_sans_affectation($db, $boitier['Adresse_netbird'], $nom, $recuperation['contenu'], $adresse_mac);
continue;
}
$id_bateau = $affectation['id_bateau'];
$dechiffrement = Decrypter_archive($recuperation['contenu']); if (Archive_deja_traitee($db, $nom))
if (!$dechiffrement['ok'])
{
echo " - {$nom} : ÉCHEC de déchiffrement (" . $dechiffrement['raison'] . ") - rejetée.\n";
Rejeter_archive($db, $boitier['Adresse_netbird'], $nom, $recuperation['contenu'], $adresse_mac, $id_bateau, "Échec de déchiffrement : " . $dechiffrement['raison']);
continue;
}
$origine_bdd = $dechiffrement['entete']['Origine_bdd'] ?? 'normale';
$mmsi_recu = $dechiffrement['entete']['No_mmsi'] ?? null;
$mmsi_attendu_connu = null; // reste null si No_mmsi absent de l'en-tête - sinon toujours renseigné (cf synthèse §39)
if ($mmsi_recu !== null)
{
$verification_mmsi = Verifier_mmsi($db, $id_bateau, $mmsi_recu);
$mmsi_attendu_connu = $verification_mmsi['attendu'];
if ($verification_mmsi['statut'] === 'quarantaine')
{ {
echo " - {$nom} : MMSI reçu ({$mmsi_recu}) différent de celui attendu ({$verification_mmsi['attendu']}) - mise en quarantaine.\n"; echo " - {$nom} : déjà traitée - confirmation de suppression seulement.\n";
Mettre_en_quarantaine_mmsi($db, $boitier['Adresse_netbird'], $nom, $recuperation['contenu'], $adresse_mac, $id_bateau, $mmsi_recu, $verification_mmsi['attendu']); Confirmer_suppression_archive($boitier['Adresse_netbird'], $nom);
continue; continue;
} }
}
$version_cible = $dechiffrement['entete']['Version_soft_no_package'] ?? null; $recuperation = Recuperer_archive($boitier['Adresse_netbird'], $nom);
$generation_active = Resoudre_generation_active($db, $id_bateau); if (!$recuperation['ok'])
$nouvelle_generation = Nouvelle_generation_necessaire($origine_bdd, $generation_active);
echo " - {$nom} : récupérée et intègre (" . strlen($recuperation['contenu']) . " octets), origine=" . $origine_bdd . "\n";
$fichier_sql = Resoudre_fichier_sql($db, $version_cible);
if ($fichier_sql === false)
{
echo " ÉCHEC : version logicielle '{$version_cible}' inconnue de TB_Versions - rejetée.\n";
Rejeter_archive($db, $boitier['Adresse_netbird'], $nom, $recuperation['contenu'], $adresse_mac, $id_bateau, "Version logicielle '{$version_cible}' inconnue de TB_Versions", $mmsi_recu, $mmsi_attendu_connu);
continue;
}
$contenu_fichier_sql = @file_get_contents(REPERTOIRE_VERSIONS_SQL . "/" . $fichier_sql);
if ($contenu_fichier_sql === false)
{
echo " ÉCHEC : fichier {$fichier_sql} introuvable sur le VPS - rejetée.\n";
Rejeter_archive($db, $boitier['Adresse_netbird'], $nom, $recuperation['contenu'], $adresse_mac, $id_bateau, "Fichier {$fichier_sql} introuvable sur le VPS", $mmsi_recu, $mmsi_attendu_connu);
continue;
}
if ($nouvelle_generation)
{
$zone_usine = Extraire_zone_usine($contenu_fichier_sql);
if ($zone_usine === false)
{ {
echo " ÉCHEC : marqueur Factory_structure introuvable dans {$fichier_sql} - rejetée.\n"; echo " - {$nom} : ÉCHEC de récupération (" . $recuperation['raison'] . ") - nouvelle tentative au prochain cycle.\n";
Rejeter_archive($db, $boitier['Adresse_netbird'], $nom, $recuperation['contenu'], $adresse_mac, $id_bateau, "Marqueur Factory_structure introuvable dans {$fichier_sql}", $mmsi_recu, $mmsi_attendu_connu);
continue; continue;
} }
// 'factory'/'restauration' est toujours un fait rapporté honnêtement par le bateau - à
// conserver tel quel pour le diagnostic, qu'une génération existait déjà côté serveur ou if (!Integrite_confirmee($nom, $recuperation['contenu']))
// non. Seul 'normale' sans aucune génération existante (un bateau vraiment neuf, qui n'a
// jamais rien envoyé) devient 'creation_initiale' (cf synthèse §37).
if ($origine_bdd === 'factory' || $origine_bdd === 'restauration')
{ {
$motif = $origine_bdd; echo " - {$nom} : empreinte invalide, archive potentiellement corrompue - nouvelle tentative au prochain cycle.\n";
continue;
}
// Aucune affectation active (jamais affecté, ou retiré) : quarantaine directe, sans
// déchiffrement - ni l'en-tête ni le contenu ne servent à rien sans bateau à qui l'attribuer
// (cf synthèse §38) :
if (!$affectation['existe'])
{
echo " - {$nom} : aucune affectation active pour ce boîtier - mise en quarantaine.\n";
Mettre_en_quarantaine_sans_affectation($db, $boitier['Adresse_netbird'], $nom, $recuperation['contenu'], $adresse_mac);
continue;
}
$id_bateau = $affectation['id_bateau'];
$dechiffrement = Decrypter_archive($recuperation['contenu']);
if (!$dechiffrement['ok'])
{
echo " - {$nom} : ÉCHEC de déchiffrement (" . $dechiffrement['raison'] . ") - rejetée.\n";
Rejeter_archive($db, $boitier['Adresse_netbird'], $nom, $recuperation['contenu'], $adresse_mac, $id_bateau, "Échec de déchiffrement : " . $dechiffrement['raison']);
continue;
}
$origine_bdd = $dechiffrement['entete']['Origine_bdd'] ?? 'normale';
$mmsi_recu = $dechiffrement['entete']['No_mmsi'] ?? null;
$mmsi_attendu_connu = null; // reste null si No_mmsi absent de l'en-tête - sinon toujours renseigné (cf synthèse §39)
if ($mmsi_recu !== null)
{
$verification_mmsi = Verifier_mmsi($db, $id_bateau, $mmsi_recu);
$mmsi_attendu_connu = $verification_mmsi['attendu'];
if ($verification_mmsi['statut'] === 'quarantaine')
{
echo " - {$nom} : MMSI reçu ({$mmsi_recu}) différent de celui attendu ({$verification_mmsi['attendu']}) - mise en quarantaine.\n";
Mettre_en_quarantaine_mmsi($db, $boitier['Adresse_netbird'], $nom, $recuperation['contenu'], $adresse_mac, $id_bateau, $mmsi_recu, $verification_mmsi['attendu']);
continue;
}
}
$version_cible = $dechiffrement['entete']['Version_soft_no_package'] ?? null;
$generation_active = Resoudre_generation_active($db, $id_bateau);
$nouvelle_generation = Nouvelle_generation_necessaire($origine_bdd, $generation_active);
echo " - {$nom} : récupérée et intègre (" . strlen($recuperation['contenu']) . " octets), origine=" . $origine_bdd . "\n";
$fichier_sql = Resoudre_fichier_sql($db, $version_cible);
if ($fichier_sql === false)
{
echo " ÉCHEC : version logicielle '{$version_cible}' inconnue de TB_Versions - rejetée.\n";
Rejeter_archive($db, $boitier['Adresse_netbird'], $nom, $recuperation['contenu'], $adresse_mac, $id_bateau, "Version logicielle '{$version_cible}' inconnue de TB_Versions", $mmsi_recu, $mmsi_attendu_connu);
continue;
}
$contenu_fichier_sql = @file_get_contents(REPERTOIRE_VERSIONS_SQL . "/" . $fichier_sql);
if ($contenu_fichier_sql === false)
{
echo " ÉCHEC : fichier {$fichier_sql} introuvable sur le VPS - rejetée.\n";
Rejeter_archive($db, $boitier['Adresse_netbird'], $nom, $recuperation['contenu'], $adresse_mac, $id_bateau, "Fichier {$fichier_sql} introuvable sur le VPS", $mmsi_recu, $mmsi_attendu_connu);
continue;
}
if ($nouvelle_generation)
{
$zone_usine = Extraire_zone_usine($contenu_fichier_sql);
if ($zone_usine === false)
{
echo " ÉCHEC : marqueur Factory_structure introuvable dans {$fichier_sql} - rejetée.\n";
Rejeter_archive($db, $boitier['Adresse_netbird'], $nom, $recuperation['contenu'], $adresse_mac, $id_bateau, "Marqueur Factory_structure introuvable dans {$fichier_sql}", $mmsi_recu, $mmsi_attendu_connu);
continue;
}
// 'factory'/'restauration' est toujours un fait rapporté honnêtement par le bateau - à
// conserver tel quel pour le diagnostic, qu'une génération existait déjà côté serveur ou
// non. Seul 'normale' sans aucune génération existante (un bateau vraiment neuf, qui n'a
// jamais rien envoyé) devient 'creation_initiale' (cf synthèse §37).
if ($origine_bdd === 'factory' || $origine_bdd === 'restauration')
{
$motif = $origine_bdd;
}
else
{
$motif = 'creation_initiale';
}
$resultat_generation = Creer_generation($db, $id_bateau, $motif, $zone_usine);
if (!$resultat_generation['ok'])
{
echo " ÉCHEC de création de génération : " . $resultat_generation['raison'] . " - rejetée.\n";
Rejeter_archive($db, $boitier['Adresse_netbird'], $nom, $recuperation['contenu'], $adresse_mac, $id_bateau, "Échec de création de génération : " . $resultat_generation['raison'], $mmsi_recu, $mmsi_attendu_connu);
continue;
}
$nom_base_cible = $resultat_generation['nom_base'];
echo " Nouvelle génération créée : {$nom_base_cible} (motif={$motif}).\n";
} }
else else
{ {
$motif = 'creation_initiale'; $nom_base_cible = $generation_active['nom_base'];
echo " Génération existante : {$nom_base_cible}.\n";
// Bloc suivant : rejeu des segments incrémentaux si la version a changé,
// via Extraire_segments_incrementaux() + exécution (même mécanique que Creer_generation).
} }
$resultat_generation = Creer_generation($db, $id_bateau, $motif, $zone_usine); $chargement = Charger_contenu_archive($nom_base_cible, $dechiffrement['sql']);
if (!$resultat_generation['ok']) if (!$chargement['ok'])
{ {
echo " ÉCHEC de création de génération : " . $resultat_generation['raison'] . " - rejetée.\n"; echo " ÉCHEC de chargement dans {$nom_base_cible} : " . $chargement['raison'] . " - rejetée.\n";
Rejeter_archive($db, $boitier['Adresse_netbird'], $nom, $recuperation['contenu'], $adresse_mac, $id_bateau, "Échec de création de génération : " . $resultat_generation['raison'], $mmsi_recu, $mmsi_attendu_connu); Rejeter_archive($db, $boitier['Adresse_netbird'], $nom, $recuperation['contenu'], $adresse_mac, $id_bateau, "Échec de chargement dans {$nom_base_cible} : " . $chargement['raison'], $mmsi_recu, $mmsi_attendu_connu);
continue; continue;
} }
$nom_base_cible = $resultat_generation['nom_base']; echo " Contenu chargé dans {$nom_base_cible}.\n";
echo " Nouvelle génération créée : {$nom_base_cible} (motif={$motif}).\n";
}
else
{
$nom_base_cible = $generation_active['nom_base'];
echo " Génération existante : {$nom_base_cible}.\n";
// Bloc suivant : rejeu des segments incrémentaux si la version a changé,
// via Extraire_segments_incrementaux() + exécution (même mécanique que Creer_generation).
}
$chargement = Charger_contenu_archive($nom_base_cible, $dechiffrement['sql']);
if (!$chargement['ok'])
{
echo " ÉCHEC de chargement dans {$nom_base_cible} : " . $chargement['raison'] . " - rejetée.\n";
Rejeter_archive($db, $boitier['Adresse_netbird'], $nom, $recuperation['contenu'], $adresse_mac, $id_bateau, "Échec de chargement dans {$nom_base_cible} : " . $chargement['raison'], $mmsi_recu, $mmsi_attendu_connu);
continue;
}
echo " Contenu chargé dans {$nom_base_cible}.\n";
// Résolution de la vraie Cle_primaire de la génération (celle juste créée, ou l'existante) : // Résolution de la vraie Cle_primaire de la génération (celle juste créée, ou l'existante) :
$generation_pour_enregistrement = Resoudre_generation_active($db, $id_bateau); $generation_pour_enregistrement = Resoudre_generation_active($db, $id_bateau);
Enregistrer_archive_traitee($db, $nom, $adresse_mac, $id_bateau, $generation_pour_enregistrement['cle'], 'traitee', $mmsi_recu, $mmsi_attendu_connu); Enregistrer_archive_traitee($db, $nom, $adresse_mac, $id_bateau, $generation_pour_enregistrement['cle'], 'traitee', $mmsi_recu, $mmsi_attendu_connu);
if (Confirmer_suppression_archive($boitier['Adresse_netbird'], $nom)) if (Confirmer_suppression_archive($boitier['Adresse_netbird'], $nom))
{ {
echo " Archive confirmée et supprimée côté CM5.\n"; echo " Archive confirmée et supprimée côté CM5.\n";
} }
else else
{ {
echo " AVERTISSEMENT : confirmation de suppression échouée (le fichier restera côté CM5, sans conséquence - cf synthèse §32.5).\n"; echo " AVERTISSEMENT : confirmation de suppression échouée (le fichier restera côté CM5, sans conséquence - cf synthèse §32.5).\n";
}
} }
} }
finally
{
Liberer_boitier($verrou['poignee']);
}
} }
Fermeture_bdd(); Fermeture_bdd();
+107 -89
View File
@@ -4,7 +4,7 @@
// (MMSI divergent, ou aucune affectation active - cf synthèse §38-39). // (MMSI divergent, ou aucune affectation active - cf synthèse §38-39).
// Exécuté manuellement ou via cron, après qu'un administrateur ait mis à jour // Exécuté manuellement ou via cron, après qu'un administrateur ait mis à jour
// les données concernées (TB_Bateaux.MMSI, TB_Affectations_bateaux) : // les données concernées (TB_Bateaux.MMSI, TB_Affectations_bateaux) :
// php /var/www/cron_quarantaine.php // /usr/bin/php /var/www/cron_quarantaine.php
// //
// Ne retente PAS automatiquement : parcourt les fichiers du répertoire de // Ne retente PAS automatiquement : parcourt les fichiers du répertoire de
// quarantaine, et pour chacun, relit sa ligne TB_Archives_traitees existante // quarantaine, et pour chacun, relit sa ligne TB_Archives_traitees existante
@@ -15,6 +15,10 @@
// modifié, nouvelle tentative au prochain lancement. // modifié, nouvelle tentative au prochain lancement.
// ============================================================================ // ============================================================================
// Même précaution que cron_archivage.php : certains fichiers inclus utilisent des chemins relatifs,
// résolus par rapport au répertoire de travail - imprévisible pour une tâche planifiée.
chdir("/var/www/html");
require_once __DIR__ . "/config/config.php"; require_once __DIR__ . "/config/config.php";
require_once __DIR__ . "/config/fonctions_bdd.php"; require_once __DIR__ . "/config/fonctions_bdd.php";
require_once __DIR__ . "/fonctions_archivage.php"; require_once __DIR__ . "/fonctions_archivage.php";
@@ -101,117 +105,131 @@ function Executer_cron_quarantaine()
{ {
$chemin = REPERTOIRE_QUARANTAINE . "/" . $nom; $chemin = REPERTOIRE_QUARANTAINE . "/" . $nom;
$ligne = Lire_ligne_en_attente($db, $nom);
if ($ligne === null)
{
echo " - {$nom} : aucune ligne TB_Archives_traitees en attente trouvée - incohérence, à examiner manuellement.\n";
continue;
}
$adresse_mac = Adresse_mac_depuis_nom($nom); $adresse_mac = Adresse_mac_depuis_nom($nom);
$contenu_chiffre = file_get_contents($chemin); $verrou = Verrouiller_boitier($adresse_mac);
if (!$verrou['ok'])
$affectation = Resoudre_affectation_active($db, $adresse_mac);
if (!$affectation['existe'])
{ {
echo " - {$nom} : toujours aucune affectation active - reste en quarantaine.\n"; echo " - {$nom} : " . $verrou['raison'] . " - réexamen au prochain passage.\n";
continue;
}
$id_bateau = $affectation['id_bateau'];
$dechiffrement = Decrypter_archive($contenu_chiffre);
if (!$dechiffrement['ok'])
{
// Un échec de déchiffrement ne se résoudra pas en attendant (contrairement à l'affectation ou
// au MMSI, qui dépendent d'une action administrative future) - rejet direct, comme
// cron_archivage.php (cf synthèse §38) :
echo " - {$nom} : ÉCHEC de déchiffrement (" . $dechiffrement['raison'] . ") - rejetée.\n";
Trancher_archive_en_attente($db, $nom, $id_bateau, null, 'rejetee', "Échec de déchiffrement : " . $dechiffrement['raison']);
if (!is_dir(REPERTOIRE_ERREURS)) @mkdir(REPERTOIRE_ERREURS, 0750, true);
rename($chemin, REPERTOIRE_ERREURS . "/" . $nom);
continue; continue;
} }
$mmsi_recu = $dechiffrement['entete']['No_mmsi'] ?? null; try
$mmsi_attendu_connu = null;
if ($mmsi_recu !== null)
{ {
$verification_mmsi = Verifier_mmsi($db, $id_bateau, $mmsi_recu); $ligne = Lire_ligne_en_attente($db, $nom);
$mmsi_attendu_connu = $verification_mmsi['attendu']; if ($ligne === null)
if ($verification_mmsi['statut'] === 'quarantaine')
{ {
echo " - {$nom} : MMSI toujours divergent ({$mmsi_recu} / {$verification_mmsi['attendu']}) - reste en quarantaine.\n"; echo " - {$nom} : aucune ligne TB_Archives_traitees en attente trouvée - incohérence, à examiner manuellement.\n";
continue; continue;
} }
}
// La cause de la quarantaine est levée : traitement complet, même déroulé que cron_archivage.php : $contenu_chiffre = file_get_contents($chemin);
$origine_bdd = $dechiffrement['entete']['Origine_bdd'] ?? 'normale';
$version_cible = $dechiffrement['entete']['Version_soft_no_package'] ?? null;
$generation_active = Resoudre_generation_active($db, $id_bateau);
$nouvelle_generation = Nouvelle_generation_necessaire($origine_bdd, $generation_active);
$fichier_sql = Resoudre_fichier_sql($db, $version_cible); $affectation = Resoudre_affectation_active($db, $adresse_mac);
if ($fichier_sql === false) if (!$affectation['existe'])
{
echo " - {$nom} : version logicielle '{$version_cible}' inconnue de TB_Versions - rejetée.\n";
Trancher_archive_en_attente($db, $nom, $id_bateau, null, 'rejetee', "Version logicielle '{$version_cible}' inconnue de TB_Versions", $mmsi_recu, $mmsi_attendu_connu);
if (!is_dir(REPERTOIRE_ERREURS)) @mkdir(REPERTOIRE_ERREURS, 0750, true);
rename($chemin, REPERTOIRE_ERREURS . "/" . $nom);
continue;
}
$contenu_fichier_sql = @file_get_contents(REPERTOIRE_VERSIONS_SQL . "/" . $fichier_sql);
if ($contenu_fichier_sql === false)
{
echo " - {$nom} : fichier {$fichier_sql} introuvable sur le VPS - rejetée.\n";
Trancher_archive_en_attente($db, $nom, $id_bateau, null, 'rejetee', "Fichier {$fichier_sql} introuvable sur le VPS", $mmsi_recu, $mmsi_attendu_connu);
if (!is_dir(REPERTOIRE_ERREURS)) @mkdir(REPERTOIRE_ERREURS, 0750, true);
rename($chemin, REPERTOIRE_ERREURS . "/" . $nom);
continue;
}
if ($nouvelle_generation)
{
$zone_usine = Extraire_zone_usine($contenu_fichier_sql);
if ($zone_usine === false)
{ {
echo " - {$nom} : marqueur Factory_structure introuvable dans {$fichier_sql} - rejetée.\n"; echo " - {$nom} : toujours aucune affectation active - reste en quarantaine.\n";
Trancher_archive_en_attente($db, $nom, $id_bateau, null, 'rejetee', "Marqueur Factory_structure introuvable dans {$fichier_sql}", $mmsi_recu, $mmsi_attendu_connu); continue;
}
$id_bateau = $affectation['id_bateau'];
$dechiffrement = Decrypter_archive($contenu_chiffre);
if (!$dechiffrement['ok'])
{
// Un échec de déchiffrement ne se résoudra pas en attendant (contrairement à l'affectation ou
// au MMSI, qui dépendent d'une action administrative future) - rejet direct, comme
// cron_archivage.php (cf synthèse §38) :
echo " - {$nom} : ÉCHEC de déchiffrement (" . $dechiffrement['raison'] . ") - rejetée.\n";
Trancher_archive_en_attente($db, $nom, $id_bateau, null, 'rejetee', "Échec de déchiffrement : " . $dechiffrement['raison']);
if (!is_dir(REPERTOIRE_ERREURS)) @mkdir(REPERTOIRE_ERREURS, 0750, true); if (!is_dir(REPERTOIRE_ERREURS)) @mkdir(REPERTOIRE_ERREURS, 0750, true);
rename($chemin, REPERTOIRE_ERREURS . "/" . $nom); rename($chemin, REPERTOIRE_ERREURS . "/" . $nom);
continue; continue;
} }
$motif = ($origine_bdd === 'factory' || $origine_bdd === 'restauration') ? $origine_bdd : 'creation_initiale';
$resultat_generation = Creer_generation($db, $id_bateau, $motif, $zone_usine); $mmsi_recu = $dechiffrement['entete']['No_mmsi'] ?? null;
if (!$resultat_generation['ok']) $mmsi_attendu_connu = null;
if ($mmsi_recu !== null)
{ {
echo " - {$nom} : échec de création de génération : " . $resultat_generation['raison'] . " - rejetée.\n"; $verification_mmsi = Verifier_mmsi($db, $id_bateau, $mmsi_recu);
Trancher_archive_en_attente($db, $nom, $id_bateau, null, 'rejetee', "Échec de création de génération : " . $resultat_generation['raison'], $mmsi_recu, $mmsi_attendu_connu); $mmsi_attendu_connu = $verification_mmsi['attendu'];
if ($verification_mmsi['statut'] === 'quarantaine')
{
echo " - {$nom} : MMSI toujours divergent ({$mmsi_recu} / {$verification_mmsi['attendu']}) - reste en quarantaine.\n";
continue;
}
}
// La cause de la quarantaine est levée : traitement complet, même déroulé que cron_archivage.php :
$origine_bdd = $dechiffrement['entete']['Origine_bdd'] ?? 'normale';
$version_cible = $dechiffrement['entete']['Version_soft_no_package'] ?? null;
$generation_active = Resoudre_generation_active($db, $id_bateau);
$nouvelle_generation = Nouvelle_generation_necessaire($origine_bdd, $generation_active);
$fichier_sql = Resoudre_fichier_sql($db, $version_cible);
if ($fichier_sql === false)
{
echo " - {$nom} : version logicielle '{$version_cible}' inconnue de TB_Versions - rejetée.\n";
Trancher_archive_en_attente($db, $nom, $id_bateau, null, 'rejetee', "Version logicielle '{$version_cible}' inconnue de TB_Versions", $mmsi_recu, $mmsi_attendu_connu);
if (!is_dir(REPERTOIRE_ERREURS)) @mkdir(REPERTOIRE_ERREURS, 0750, true);
rename($chemin, REPERTOIRE_ERREURS . "/" . $nom);
continue;
}
$contenu_fichier_sql = @file_get_contents(REPERTOIRE_VERSIONS_SQL . "/" . $fichier_sql);
if ($contenu_fichier_sql === false)
{
echo " - {$nom} : fichier {$fichier_sql} introuvable sur le VPS - rejetée.\n";
Trancher_archive_en_attente($db, $nom, $id_bateau, null, 'rejetee', "Fichier {$fichier_sql} introuvable sur le VPS", $mmsi_recu, $mmsi_attendu_connu);
if (!is_dir(REPERTOIRE_ERREURS)) @mkdir(REPERTOIRE_ERREURS, 0750, true); if (!is_dir(REPERTOIRE_ERREURS)) @mkdir(REPERTOIRE_ERREURS, 0750, true);
rename($chemin, REPERTOIRE_ERREURS . "/" . $nom); rename($chemin, REPERTOIRE_ERREURS . "/" . $nom);
continue; continue;
} }
$nom_base_cible = $resultat_generation['nom_base'];
}
else
{
$nom_base_cible = $generation_active['nom_base'];
}
$chargement = Charger_contenu_archive($nom_base_cible, $dechiffrement['sql']); if ($nouvelle_generation)
if (!$chargement['ok']) {
{ $zone_usine = Extraire_zone_usine($contenu_fichier_sql);
echo " - {$nom} : échec de chargement dans {$nom_base_cible} : " . $chargement['raison'] . " - rejetée.\n"; if ($zone_usine === false)
Trancher_archive_en_attente($db, $nom, $id_bateau, null, 'rejetee', "Échec de chargement dans {$nom_base_cible} : " . $chargement['raison'], $mmsi_recu, $mmsi_attendu_connu); {
if (!is_dir(REPERTOIRE_ERREURS)) @mkdir(REPERTOIRE_ERREURS, 0750, true); echo " - {$nom} : marqueur Factory_structure introuvable dans {$fichier_sql} - rejetée.\n";
rename($chemin, REPERTOIRE_ERREURS . "/" . $nom); Trancher_archive_en_attente($db, $nom, $id_bateau, null, 'rejetee', "Marqueur Factory_structure introuvable dans {$fichier_sql}", $mmsi_recu, $mmsi_attendu_connu);
continue; if (!is_dir(REPERTOIRE_ERREURS)) @mkdir(REPERTOIRE_ERREURS, 0750, true);
} rename($chemin, REPERTOIRE_ERREURS . "/" . $nom);
continue;
}
$motif = ($origine_bdd === 'factory' || $origine_bdd === 'restauration') ? $origine_bdd : 'creation_initiale';
$resultat_generation = Creer_generation($db, $id_bateau, $motif, $zone_usine);
if (!$resultat_generation['ok'])
{
echo " - {$nom} : échec de création de génération : " . $resultat_generation['raison'] . " - rejetée.\n";
Trancher_archive_en_attente($db, $nom, $id_bateau, null, 'rejetee', "Échec de création de génération : " . $resultat_generation['raison'], $mmsi_recu, $mmsi_attendu_connu);
if (!is_dir(REPERTOIRE_ERREURS)) @mkdir(REPERTOIRE_ERREURS, 0750, true);
rename($chemin, REPERTOIRE_ERREURS . "/" . $nom);
continue;
}
$nom_base_cible = $resultat_generation['nom_base'];
}
else
{
$nom_base_cible = $generation_active['nom_base'];
}
$generation_pour_enregistrement = Resoudre_generation_active($db, $id_bateau); $chargement = Charger_contenu_archive($nom_base_cible, $dechiffrement['sql']);
Trancher_archive_en_attente($db, $nom, $id_bateau, $generation_pour_enregistrement['cle'], 'traitee', null, $mmsi_recu, $mmsi_attendu_connu); if (!$chargement['ok'])
unlink($chemin); {
echo " - {$nom} : quarantaine levée, chargée dans {$nom_base_cible}.\n"; echo " - {$nom} : échec de chargement dans {$nom_base_cible} : " . $chargement['raison'] . " - rejetée.\n";
Trancher_archive_en_attente($db, $nom, $id_bateau, null, 'rejetee', "Échec de chargement dans {$nom_base_cible} : " . $chargement['raison'], $mmsi_recu, $mmsi_attendu_connu);
if (!is_dir(REPERTOIRE_ERREURS)) @mkdir(REPERTOIRE_ERREURS, 0750, true);
rename($chemin, REPERTOIRE_ERREURS . "/" . $nom);
continue;
}
$generation_pour_enregistrement = Resoudre_generation_active($db, $id_bateau);
Trancher_archive_en_attente($db, $nom, $id_bateau, $generation_pour_enregistrement['cle'], 'traitee', null, $mmsi_recu, $mmsi_attendu_connu);
unlink($chemin);
echo " - {$nom} : quarantaine levée, chargée dans {$nom_base_cible}.\n";
}
finally
{
Liberer_boitier($verrou['poignee']);
}
} }
Fermeture_bdd(); Fermeture_bdd();
+51
View File
@@ -22,6 +22,57 @@ const REPERTOIRE_VERSIONS_SQL = "/home/koriolan/versions/shipheart/ressources/sq
const REPERTOIRE_QUARANTAINE = "/home/koriolan/bateaux/archives/quarantaines"; const REPERTOIRE_QUARANTAINE = "/home/koriolan/bateaux/archives/quarantaines";
const REPERTOIRE_ERREURS = "/home/koriolan/bateaux/archives/erreurs"; const REPERTOIRE_ERREURS = "/home/koriolan/bateaux/archives/erreurs";
// ----------------------------------------------------------------------------
// Répertoire des verrous par boîtier (cf Verrouiller_boitier()). Les fichiers
// de verrou ne sont JAMAIS supprimés : supprimer un fichier de verrou pendant
// qu'un autre processus le détient ouvrirait une fenêtre où deux processus
// verrouillent chacun un fichier différent portant le même nom.
// ----------------------------------------------------------------------------
const REPERTOIRE_VERROUS = "/home/koriolan/bateaux/verrous";
// ----------------------------------------------------------------------------
// Verrou exclusif NON bloquant sur un boîtier (adresse MAC), partagé par
// cron_archivage.php, cron_quarantaine.php et, plus tard, le traitement à la
// demande depuis le portail (synthèse §44.6) : deux traitements simultanés du
// même boîtier pourraient sinon créer deux générations ou charger deux fois
// dans la même base. Un boîtier déjà verrouillé est simplement sauté, il sera
// repris au passage suivant - jamais d'attente.
// Le verrou est libéré par Liberer_boitier(), et de toute façon par le
// système à la fin du processus (y compris en cas d'arrêt brutal) - aucun
// verrou "orphelin" possible, contrairement à un simple fichier témoin.
// Renvoie ['ok'=>true, 'poignee'=>resource] ou ['ok'=>false, 'raison'=>string].
// ----------------------------------------------------------------------------
function Verrouiller_boitier($adresse_mac)
{
// Nom de fichier construit uniquement à partir des 12 caractères hexadécimaux de l'adresse
// (quel que soit son format d'écriture, avec ou sans ':') - jamais de chemin arbitraire :
$hex = strtoupper(preg_replace('/[^0-9A-Fa-f]/', '', (string)$adresse_mac));
if (strlen($hex) !== 12) return ['ok' => false, 'raison' => "adresse MAC invalide pour un verrou"];
if (!is_dir(REPERTOIRE_VERROUS) && !@mkdir(REPERTOIRE_VERROUS, 0770, true) && !is_dir(REPERTOIRE_VERROUS))
{
return ['ok' => false, 'raison' => "répertoire de verrous " . REPERTOIRE_VERROUS . " impossible à créer"];
}
// Mode 'c' : crée le fichier s'il n'existe pas, sans jamais le tronquer.
$poignee = @fopen(REPERTOIRE_VERROUS . "/boitier_{$hex}.lock", 'c');
if ($poignee === false) return ['ok' => false, 'raison' => "fichier de verrou impossible à ouvrir"];
if (!flock($poignee, LOCK_EX | LOCK_NB))
{
fclose($poignee);
return ['ok' => false, 'raison' => "déjà en cours de traitement par un autre processus"];
}
return ['ok' => true, 'poignee' => $poignee];
}
function Liberer_boitier($poignee)
{
if (!is_resource($poignee)) return;
flock($poignee, LOCK_UN);
fclose($poignee);
}
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Résout l'affectation active d'un boîtier (par adresse MAC) - cf synthèse // Résout l'affectation active d'un boîtier (par adresse MAC) - cf synthèse