package rootparticipation import ( "context" "errors" "os" "sync" "github.com/carlosframework/platform/internal/hostwrite" "github.com/carlosframework/platform/internal/rootstate" ) var launchVerifier struct { sync.RWMutex verify func(context.Context, uint64) error exclude func(context.Context, func(context.Context) error) error } // RegisterLaunchVerifier binds E2's root reservation and kernel census reader. // It runs under participation exclusion and must not call the agent or broker. func RegisterLaunchVerifier(verify func(context.Context, uint64) error, exclude func(context.Context, func(context.Context) error) error) error { launchVerifier.Lock() defer launchVerifier.Unlock() if verify == nil || exclude == nil || launchVerifier.verify != nil { return ErrUnavailable } launchVerifier.verify = verify launchVerifier.exclude = exclude return nil } func Open() (Ledger, error) { state, err := rootstate.Open(Namespace, false, false) if err != nil { return Ledger{}, err } launchVerifier.RLock() verify, exclude := launchVerifier.verify, launchVerifier.exclude launchVerifier.RUnlock() return Ledger{State: state, VerifyLaunches: verify, ExcludeLaunches: exclude}, nil } func VerifyLaunchCensus(ctx context.Context, revision uint64) error { launchVerifier.RLock() verify := launchVerifier.verify launchVerifier.RUnlock() if verify == nil { return ErrUnavailable } return verify(ctx, revision) } // UpdateRequired keeps a retained census authoritative after claims are disabled. // Only an absent census with the flag off selects the legacy update protocol. func (l Ledger) UpdateRequired() (bool, error) { if hostwrite.Enabled(os.Getenv) { return true, nil } if l.State == nil { return false, ErrUnavailable } _, err := l.State.Read("census.json") if errors.Is(err, os.ErrNotExist) { return false, nil } return true, err } func OpenForUpdate() (Ledger, bool, error) { l, err := Open() if errors.Is(err, os.ErrNotExist) { return Ledger{}, false, nil } if err != nil { return Ledger{}, false, err } _, err = l.State.Read("census.json") if errors.Is(err, os.ErrNotExist) { l.State.Close() return Ledger{}, false, nil } required, err := l.UpdateRequired() if err != nil { l.State.Close() return Ledger{}, false, err } return l, required, nil } func (l Ledger) ActivationReady(ctx context.Context) error { c, err := l.Census() if err != nil { return err } if l.VerifyLaunches == nil { return ErrUnavailable } return l.VerifyLaunches(ctx, c.LaunchRevision) } func WithLaunchExclusion(ctx context.Context, fn func(context.Context) error) error { launchVerifier.RLock() exclude := launchVerifier.exclude launchVerifier.RUnlock() if exclude == nil { return ErrUnavailable } return exclude(ctx, fn) } func (l Ledger) WithLaunchExclusion(ctx context.Context, fn func(context.Context) error) error { if l.ExcludeLaunches == nil { return ErrUnavailable } return l.ExcludeLaunches(ctx, fn) }