Files
archy/scripts/c01_power_reactivate.py

90 lines
5.3 KiB
Python

#!/usr/bin/python3 -I -S
"""One independently delegated, exact-current-generation D09 wake activation.
No APPLY, rollback, peer-selected paths, old grants or retry loop. This journal
is outside D09's executable/frontend/workspace rollback objects. An uncertain
dispatch leaves its exclusive intent and requires exact reconciliation.
"""
import hashlib
import importlib.util
import json
import os
from pathlib import Path
import secrets
import stat
import sys
import time
ROOT=Path('/var/lib/archipelago-c01-power')
CONFIG=Path('/etc/archipelago-c01/power.json')
def checked(path):
for item in (path,*path.parents):
meta=item.lstat()
if meta.st_uid or meta.st_mode&0o022 or stat.S_ISLNK(meta.st_mode):
raise ValueError('reactivation source custody')
def main():
if os.getresuid()!=(0,0,0) or len(sys.argv)!=1:raise ValueError('root fixed reactivation entry required')
checked(CONFIG);checked(ROOT);checked(Path(__file__))
policy=json.loads(CONFIG.read_bytes())
delegation=policy['lifecycle_issuer']
if delegation['scope']!='reactivate-current-generation' or delegation['program_sha256']!=hashlib.sha256(Path(__file__).read_bytes()).hexdigest():
raise ValueError('lifecycle issuer not appointed')
state=json.loads((ROOT/'state.json').read_bytes())
if state['phase']!='RecoveryRequired' or state['configuration_sha256']!=hashlib.sha256(CONFIG.read_bytes()).hexdigest():
raise ValueError('no fresh power return boundary')
group=Path('/proc/self/cgroup').read_text().strip().split(':',2)[2].split('/')[-1]
if group not in (state['target']+'.service','archipelago-c01-power-abort@'+state['target']+'.service',
'archipelago-c01-power-observer.service'):
raise ValueError('lifecycle issuer outside returned power unit')
parent=Path(__file__).parent
for name in ('archipelago-d09-provision','d09-lifecycle-owner.py','d09_store.py','d09_wire.py','d09-wire-schema.json'):
checked(parent/name)
spec=importlib.util.spec_from_file_location('power_d09_provision',parent/'archipelago-d09-provision')
if spec is None:
from importlib.machinery import SourceFileLoader
spec=importlib.util.spec_from_loader('power_d09_provision',SourceFileLoader('power_d09_provision',str(parent/'archipelago-d09-provision')))
module=importlib.util.module_from_spec(spec);spec.loader.exec_module(module)
owner=module.owner
raw=owner.read(owner.ROOT/'ledger.json');value=json.loads(raw)
deadline=time.monotonic()+10
while (value.get('prerequisite') or {}).get('state')=='START_INTENT':
if time.monotonic()>=deadline:raise ValueError('D09 has not reconciled the prior start; no grant issued')
time.sleep(.1);raw=owner.read(owner.ROOT/'ledger.json');value=json.loads(raw)
binding=value.get('active_binding') or json.loads(owner.read(owner.BINDING))
receipt=value['receipt']
if receipt is not None and receipt['phase'] not in ('COMPLETE','AWAITING_OBSERVATION'):
raise ValueError('pending D09 operation needs independent reconciliation')
index={key:binding[key] for key in ('target_binding_digest','composition_digest','transaction_id')}
# Production's actual process/job/cgroup drain verifier. This method is
# stateless; no invented Owner instance, receipt or healthy-process result.
disposition=owner.Owner.drained_disposition(None,value)
record=dict(index,schema_version=1,allowed_action='REACTIVATE_PREREQUISITE' if receipt is None else 'REACTIVATE_RETAINED',
predecessor_epoch_id=value['epoch'],head_sequence=value['sequence'],observed_boot_id=owner.boot_id(),
preserved_head_digest=owner.digest(owner.canonical(receipt)) if receipt else owner.digest(raw),
preserved_journal_digest=owner.digest(owner.canonical({'receipts':value['journal'],'intents':value['intents']})),
preserved_consumed_history_digest=owner.digest(owner.canonical(owner.consumption_history(value))),
preserved_target_index_digest=owner.digest(owner.canonical(index)),disposition_digest=disposition,
new_epoch_id=secrets.token_hex(16),new_grant_digest=secrets.token_hex(32),execution_grant_digest=secrets.token_hex(32),
external_history_sequence=state['sequence'])
history=owner.canonical(dict(record,sequence=state['sequence'],issuer_id=delegation['id'],power_nonce=state['nonce']))
record['external_history_digest']=owner.digest(history)
intent=ROOT/f"reactivation-{state['sequence']}.intent"
fd=os.open(intent,os.O_WRONLY|os.O_CREAT|os.O_EXCL|os.O_NOFOLLOW|os.O_CLOEXEC,0o600)
try:
encoded=owner.canonical(record)
if os.write(fd,encoded)!=len(encoded):raise ValueError('reactivation intent write uncertain')
os.fsync(fd)
finally:os.close(fd)
parent_fd=os.open(ROOT,os.O_RDONLY|os.O_DIRECTORY|os.O_CLOEXEC)
try:os.fsync(parent_fd)
finally:os.close(parent_fd)
history_path=ROOT/f"reactivation-{state['sequence']}.history"
module.atomic(history_path,history)
module.readmit(intent,history_path,activation=True)
module.atomic(ROOT/f"reactivation-{state['sequence']}.submitted",owner.canonical({'record_sha256':owner.digest(encoded)}))
print(json.dumps({'submitted':True,'record_sha256':owner.digest(encoded),'power_sequence':state['sequence']}))
if __name__=='__main__':main()