feat(v4): consolidate audited data recovery economic replay and optional archival
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
function v4UnifiedWrite(string $path,string $text,int $mode=0644):void
|
||||
{
|
||||
$tmp=tempnam(dirname($path),'.v4-rc-');if($tmp===false)throw new RuntimeException('Temporaere Datei fehlt.');
|
||||
try {if(file_put_contents($tmp,$text,LOCK_EX)!==strlen($text)||!chmod($tmp,$mode)||!rename($tmp,$path))throw new RuntimeException('Datei konnte nicht geschrieben werden.');}
|
||||
finally {if(is_file($tmp))unlink($tmp);}
|
||||
}
|
||||
function v4UnifiedFiles(string $stage,string $target):array
|
||||
{
|
||||
$m=json_decode(file_get_contents($stage.'/MANIFEST.json'),true,64,JSON_THROW_ON_ERROR);
|
||||
$names=['libs/NetzfahrplanV4Datenarchiv.php','libs/NetzfahrplanV4Bilanzierung.php','libs/ManagerNetzfahrplanV4DatenTrait.php'];
|
||||
if($m['scope']!=='unified_native_data_only'||array_keys($m['files'])!==$names)throw new RuntimeException('Falscher Installationsumfang.');
|
||||
$old=[];$new=[];$complete=true;
|
||||
foreach($names as$name){
|
||||
$p=$target.'/'.$name;$s=$stage.'/source/'.$name;
|
||||
if(is_link($p)||is_link($s)||realpath(dirname($p))!==realpath($target).'/libs')throw new RuntimeException('Unerwarteter Dateipfad.');
|
||||
$new[$name]=file_get_contents($s);token_get_all($new[$name],TOKEN_PARSE);
|
||||
if(hash('sha256',$new[$name])!==$m['files'][$name]['after'])throw new RuntimeException('Vorbereitete Datei geaendert.');
|
||||
$old[$name]=is_file($p)?file_get_contents($p):null;$h=$old[$name]===null?null:hash('sha256',$old[$name]);
|
||||
if($h!==$m['files'][$name]['after'])$complete=false;
|
||||
if($h!==$m['files'][$name]['before']&&$h!==$m['files'][$name]['after'])throw new RuntimeException('Parallele Modulaenderung; nichts ueberschrieben.');
|
||||
}
|
||||
if($complete)return ['status'=>'already_installed'];
|
||||
foreach($names as$name)if(($old[$name]===null?null:hash('sha256',$old[$name]))!==$m['files'][$name]['before'])throw new RuntimeException('Teilstand zuerst pruefen.');
|
||||
$backup=$stage.'/backups/'.gmdate('YmdTHis').'-'.bin2hex(random_bytes(4));
|
||||
if(!mkdir($backup.'/libs',0700,true))throw new RuntimeException('Sicherung fehlt.');
|
||||
foreach($names as$name)if($old[$name]!==null)v4UnifiedWrite($backup.'/'.$name,$old[$name],0600);
|
||||
v4UnifiedWrite($backup.'/MANIFEST.json',json_encode($m,JSON_THROW_ON_ERROR|JSON_PRETTY_PRINT),0600);
|
||||
$written=[];
|
||||
try {foreach($names as$name){$p=$target.'/'.$name;if((is_file($p)?file_get_contents($p):null)!==$old[$name])throw new RuntimeException('Parallele Dateiaenderung.');v4UnifiedWrite($p,$new[$name]);$written[]=$name;}}
|
||||
catch(Throwable $e){foreach(array_reverse($written)as$name){$p=$target.'/'.$name;if(hash_file('sha256',$p)!==$m['files'][$name]['after'])continue;if($old[$name]===null)unlink($p);else v4UnifiedWrite($p,$old[$name]);}throw $e;}
|
||||
return ['status'=>'installed','backup'=>$backup];
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
if(!function_exists('IPS_GetKernelVersion'))throw new RuntimeException('Nur im IP-Symcon-Skripteditor ausfuehren.');
|
||||
require_once __DIR__.'/file_installer.php';
|
||||
$manager=17004;$lock=false;$report=['scope'=>'unified_native_data_only','startedAt'=>gmdate('c'),'newActuatorPermission'=>false,'cursorReset'=>false,'outboxRewritten'=>false];
|
||||
try {
|
||||
$package=json_decode(file_get_contents(__DIR__.'/PACKAGE_HASHES.json'),true,64,JSON_THROW_ON_ERROR);
|
||||
foreach($package as$name=>$hash)if(str_contains($name,'..')||str_starts_with($name,'/')||is_link(__DIR__.'/'.$name)||hash_file('sha256',__DIR__.'/'.$name)!==$hash)throw new RuntimeException('Installationspaket wurde geaendert.');
|
||||
if(!IPS_InstanceExists($manager)||IPS_GetInstance($manager)['ModuleInfo']['ModuleID']!=='{6F771B18-59D4-4C8A-B951-3B2FE9F6A2C4}')throw new RuntimeException('Erwarteter Manager fehlt.');
|
||||
if(IPS_HasChanges($manager))throw new RuntimeException('Offene Manageraenderungen zuerst speichern oder verwerfen.');
|
||||
if(IPS_GetProperty($manager,'NetzfahrplanAktiv')!==false||IPS_GetProperty($manager,'NetzfahrplanV4MessdatenAktiv')!==true)throw new RuntimeException('Erwartet: Datenaufnahme EIN, alter Netzfahrplan AUS.');
|
||||
$controls=[];foreach(IPS_GetModuleList()as$mid)if((IPS_GetModule($mid)['Prefix']??'')==='MC')$controls=array_merge($controls,IPS_GetInstanceListByModuleID($mid));
|
||||
if(count($controls)!==1||!function_exists('MC_ReloadModule'))throw new RuntimeException('Module Control nicht eindeutig.');
|
||||
$lock=IPS_SemaphoreEnter('ENELIX.V4.UnifiedRelease',3000);if(!$lock)throw new RuntimeException('Installation bereits aktiv.');
|
||||
$before=json_decode(IPS_GetConfiguration($manager),true,128,JSON_THROW_ON_ERROR);
|
||||
$report['files']=v4UnifiedFiles(__DIR__,'/var/lib/symcon/modules/Enelix-EMS');
|
||||
// No staged application classes are loaded into this script context.
|
||||
if(MC_ReloadModule($controls[0],'Enelix-EMS')===false)throw new RuntimeException('Bibliotheks-Reload fehlgeschlagen.');
|
||||
$after=json_decode(IPS_GetConfiguration($manager),true,128,JSON_THROW_ON_ERROR);
|
||||
foreach($before as$key=>$value)if(!array_key_exists($key,$after)||$after[$key]!==$value)throw new RuntimeException('Bestehender Parameter unerwartet geaendert: '.$key);
|
||||
$report['status']='native_data_candidate_installed';$report['parametersUnchanged']=true;
|
||||
echo "OK: Einheitliche Messdatenkennung und dauerhafte Versandbestaetigung installiert. Archivierung vorbereitet, standardmaessig AUS.\n";
|
||||
echo "Der bestehende Datentimer setzt den Versand fort. Sendezeiger und Originalbestand wurden nicht umgeschrieben.\n";
|
||||
echo "Keine V4-Stellfreigabe und kein Wechsel der Prognosequelle.\n";
|
||||
} catch(Throwable $e) {
|
||||
$report['status']='needs_review';$report['reason']=substr($e->getMessage(),0,250);
|
||||
echo 'FEHLER: '.$report['reason']."\nKeine neue Stellfreigabe.\n";
|
||||
} finally {
|
||||
$report['finishedAt']=gmdate('c');v4UnifiedWrite(__DIR__.'/INSTALL_RESULT.json',json_encode($report,JSON_THROW_ON_ERROR|JSON_PRETTY_PRINT),0644);
|
||||
if($lock)IPS_SemaphoreLeave('ENELIX.V4.UnifiedRelease');
|
||||
echo 'UNIFIED MANAGER REPORT: '.__DIR__."/INSTALL_RESULT.json\n";
|
||||
}
|
||||
Reference in New Issue
Block a user