Something went wrong. Try again.
forked from tailscale/tailscale
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148// Copyright (c) Tailscale Inc & contributors// SPDX-License-Identifier: BSD-3-Clause
// Package source defines interfaces for policy stores,// facilitates the creation of policy sources, and provides// functionality for reading policy settings from these sources.package source
import ( "cmp" "errors" "fmt" "io"
"tailscale.com/types/lazy" "tailscale.com/util/syspolicy/pkey" "tailscale.com/util/syspolicy/setting")
// ErrStoreClosed is an error returned when attempting to use a [Store] after it// has been closed.var ErrStoreClosed = errors.New("the policy store has been closed")
// Store provides methods to read system policy settings from OS-specific storage.// Implementations must be concurrency-safe, and may also implement// [Lockable], [Changeable], [Expirable] and [io.Closer].//// If a [Store] implementation also implements [io.Closer],// it will be called by the package to release the resources// when the store is no longer needed.type Store interface { // ReadString returns the value of a [setting.StringValue] with the specified key, // an [setting.ErrNotConfigured] if the policy setting is not configured, or // an error on failure. ReadString(key pkey.Key) (string, error) // ReadUInt64 returns the value of a [setting.IntegerValue] with the specified key, // an [setting.ErrNotConfigured] if the policy setting is not configured, or // an error on failure. ReadUInt64(key pkey.Key) (uint64, error) // ReadBoolean returns the value of a [setting.BooleanValue] with the specified key, // an [setting.ErrNotConfigured] if the policy setting is not configured, or // an error on failure. ReadBoolean(key pkey.Key) (bool, error) // ReadStringArray returns the value of a [setting.StringListValue] with the specified key, // an [setting.ErrNotConfigured] if the policy setting is not configured, or // an error on failure. ReadStringArray(key pkey.Key) ([]string, error)}
// Lockable is an optional interface that [Store] implementations may support.// Locking a [Store] is not mandatory as [Store] must be concurrency-safe,// but is recommended to avoid issues where consecutive read calls for related// policies might return inconsistent results if a policy change occurs between// the calls. Implementations may use locking to pre-read policies or for// similar performance optimizations.type Lockable interface { // Lock acquires a read lock on the policy store, // ensuring the store's state remains unchanged while locked. // Multiple readers can hold the lock simultaneously. // It returns an error if the store cannot be locked. Lock() error // Unlock unlocks the policy store. // It is a run-time error if the store is not locked on entry to Unlock. Unlock()}
// Changeable is an optional interface that [Store] implementations may support// if the policy settings they contain can be externally changed after being initially read.type Changeable interface { // RegisterChangeCallback adds a function that will be called // whenever there's a policy change in the [Store]. // The returned function can be used to unregister the callback. RegisterChangeCallback(callback func()) (unregister func(), err error)}
// Expirable is an optional interface that [Store] implementations may support// if they can be externally closed or otherwise become invalid while in use.type Expirable interface { // Done returns a channel that is closed when the policy [Store] should no longer be used. // It should return nil if the store never expires. Done() <-chan struct{}}
// Source represents a named source of policy settings for a given [setting.PolicyScope].type Source struct { name string scope setting.PolicyScope store Store origin *setting.Origin
lazyReader lazy.SyncValue[*Reader]}
// NewSource returns a new [Source] with the specified name, scope, and store.func NewSource(name string, scope setting.PolicyScope, store Store) *Source { return &Source{name: name, scope: scope, store: store, origin: setting.NewNamedOrigin(name, scope)}}
// Name reports the name of the policy source.func (s *Source) Name() string { return s.name}
// Scope reports the management scope of the policy source.func (s *Source) Scope() setting.PolicyScope { return s.scope}
// Reader returns a [Reader] that reads from this source's [Store].func (s *Source) Reader() (*Reader, error) { return s.lazyReader.GetErr(func() (*Reader, error) { return newReader(s.store, s.origin) })}
// Description returns a formatted string with the scope and name of this policy source.// It can be used for logging or display purposes.func (s *Source) Description() string { if s.name != "" { return fmt.Sprintf("%s (%v)", s.name, s.Scope()) } return s.Scope().String()}
// Compare returns an integer comparing s and s2// by their precedence, following the "last-wins" model.// The result will be://// -1 if policy settings from s should be processed before policy settings from s2;// +1 if policy settings from s should be processed after policy settings from s2, overriding s2;// 0 if the relative processing order of policy settings in s and s2 is unspecified.func (s *Source) Compare(s2 *Source) int { return cmp.Compare(s2.Scope().Kind(), s.Scope().Kind())}
// Close closes the [Source] and the underlying [Store].func (s *Source) Close() error { // The [Reader], if any, owns the [Store]. if reader, _ := s.lazyReader.GetErr(func() (*Reader, error) { return nil, ErrStoreClosed }); reader != nil { return reader.Close() } // Otherwise, it is our responsibility to close it. if closer, ok := s.store.(io.Closer); ok { return closer.Close() } return nil}