diff --git a/profile/active.go b/profile/active.go index 52e77424..2bf649d5 100644 --- a/profile/active.go +++ b/profile/active.go @@ -16,18 +16,26 @@ var ( activeProfilesLock sync.RWMutex ) -// getActiveProfile returns a cached copy of an active profile and nil if it isn't found. +// getActiveProfile returns a cached copy of an active profile and +// nil if it isn't found. func getActiveProfile(scopedID string) *Profile { activeProfilesLock.RLock() defer activeProfilesLock.RUnlock() - activeProfile, ok := activeProfiles[scopedID] - if ok { - activeProfile.MarkStillActive() - return activeProfile + return activeProfiles[scopedID] +} + +// getAllActiveProfiles returns a slice of active profiles. +func getAllActiveProfiles() []*Profile { + activeProfilesLock.RLock() + defer activeProfilesLock.RUnlock() + + result := make([]*Profile, 0, len(activeProfiles)) + for _, p := range activeProfiles { + result = append(result, p) } - return nil + return result } // findActiveProfile searched for an active local profile using the linked path. diff --git a/profile/get.go b/profile/get.go index e77b6dd5..587f585a 100644 --- a/profile/get.go +++ b/profile/get.go @@ -51,6 +51,8 @@ func GetProfile(source profileSource, id, linkedPath string) ( //nolint:gocognit // Check if there already is an active and not outdated profile. profile = getActiveProfile(scopedID) if profile != nil { + profile.MarkStillActive() + if profile.outdated.IsSet() { previousVersion = profile } else { diff --git a/profile/profile-layered-provider.go b/profile/profile-layered-provider.go index 0262fcad..10031401 100644 --- a/profile/profile-layered-provider.go +++ b/profile/profile-layered-provider.go @@ -5,11 +5,12 @@ import ( "strings" "github.com/safing/portbase/database/record" + "github.com/safing/portbase/log" "github.com/safing/portbase/runtime" ) const ( - revisionProviderPrefix = "runtime:layeredProfile/" + revisionProviderPrefix = "layeredProfile/" ) var ( @@ -18,24 +19,50 @@ var ( ) func registerRevisionProvider() error { - _, err := runtime.DefaultRegistry.Register( + _, err := runtime.Register( revisionProviderPrefix, - runtime.SimpleValueGetterFunc(getRevision), + runtime.SimpleValueGetterFunc(getRevisions), ) return err } -func getRevision(key string) ([]record.Record, error) { +func getRevisions(key string) ([]record.Record, error) { + log.Warningf("loading for key " + key) + key = strings.TrimPrefix(key, revisionProviderPrefix) - // Get active profile. - profile := getActiveProfile(key) - if profile == nil { - return nil, errProfileNotActive + var profiles []*Profile + + if key == "" { + profiles = getAllActiveProfiles() + } else { + // Get active profile. + profile := getActiveProfile(key) + if profile == nil { + return nil, errProfileNotActive + } } + records := make([]record.Record, 0, len(profiles)) + + for _, p := range profiles { + layered, err := getProfileRevision(p) + if err != nil { + log.Warningf("failed to get layered profile for %s: %s", p.ID, err) + continue + } + + records = append(records, layered) + } + + return records, nil +} + +// getProfileRevision returns the layered profile for p. +// It also updates the layered profile if required. +func getProfileRevision(p *Profile) (*LayeredProfile, error) { // Get layered profile. - layeredProfile := profile.LayeredProfile() + layeredProfile := p.LayeredProfile() if layeredProfile == nil { return nil, errNoLayeredProfile } @@ -45,5 +72,5 @@ func getRevision(key string) ([]record.Record, error) { layeredProfile.Update() } - return []record.Record{layeredProfile}, nil + return layeredProfile, nil } diff --git a/profile/profile-layered.go b/profile/profile-layered.go index 0cec6b74..057c6b97 100644 --- a/profile/profile-layered.go +++ b/profile/profile-layered.go @@ -7,6 +7,7 @@ import ( "github.com/safing/portbase/database/record" "github.com/safing/portbase/log" + "github.com/safing/portbase/runtime" "github.com/safing/portmaster/status" @@ -31,19 +32,24 @@ type LayeredProfile struct { // These functions give layered access to configuration options and require // the layered profile to be read locked. - DisableAutoPermit config.BoolOption - BlockScopeLocal config.BoolOption - BlockScopeLAN config.BoolOption - BlockScopeInternet config.BoolOption - BlockP2P config.BoolOption - BlockInbound config.BoolOption - RemoveOutOfScopeDNS config.BoolOption - RemoveBlockedDNS config.BoolOption - FilterSubDomains config.BoolOption - FilterCNAMEs config.BoolOption - PreventBypassing config.BoolOption - DomainHeuristics config.BoolOption - UseSPN config.BoolOption + + // TODO(ppacher): we need JSON tags here so the layeredProfile can be exposed + // via the API. If we ever switch away from JSON to something else supported + // by DSD this WILL BREAK! + + DisableAutoPermit config.BoolOption `json:"-"` + BlockScopeLocal config.BoolOption `json:"-"` + BlockScopeLAN config.BoolOption `json:"-"` + BlockScopeInternet config.BoolOption `json:"-"` + BlockP2P config.BoolOption `json:"-"` + BlockInbound config.BoolOption `json:"-"` + RemoveOutOfScopeDNS config.BoolOption `json:"-"` + RemoveBlockedDNS config.BoolOption `json:"-"` + FilterSubDomains config.BoolOption `json:"-"` + FilterCNAMEs config.BoolOption `json:"-"` + PreventBypassing config.BoolOption `json:"-"` + DomainHeuristics config.BoolOption `json:"-"` + UseSPN config.BoolOption `json:"-"` } // NewLayeredProfile returns a new layered profile based on the given local profile. @@ -118,7 +124,9 @@ func NewLayeredProfile(localProfile *Profile) *LayeredProfile { new.updateCaches() - new.SetKey(revisionProviderPrefix + localProfile.ID) + new.CreateMeta() + new.SetKey(runtime.DefaultRegistry.DatabaseName() + ":" + revisionProviderPrefix + localProfile.ID) + return new }