diff --git a/config/registry.go b/config/registry.go index 04b0dbf6..01a7d722 100644 --- a/config/registry.go +++ b/config/registry.go @@ -11,6 +11,12 @@ import ( var ( optionsLock sync.RWMutex options = make(map[string]*Option) + + // unmappedValues holds a list of configuration values that have been + // read from the persistence layer but no option has been defined yet. + // This is mainly to support the plugin system of the Portmaster. + unmappedValuesLock sync.Mutex + unmappedValues map[string]interface{} ) // ForEachOption calls fn for each defined option. If fn returns @@ -98,9 +104,29 @@ func Register(option *Option) error { return fmt.Errorf("config: invalid default value: %w", vErr) } + if err := loadUnmappedValue(option); err != nil { + return err + } + optionsLock.Lock() defer optionsLock.Unlock() options[option.Key] = option return nil } + +func loadUnmappedValue(option *Option) error { + unmappedValuesLock.Lock() + defer unmappedValuesLock.Unlock() + if value, ok := unmappedValues[option.Key]; ok { + delete(unmappedValues, option.Key) + + var vErr *ValidationError + option.activeValue, vErr = validateValue(option, value) + if vErr != nil { + return fmt.Errorf("config: invalid value: %w", vErr) + } + } + + return nil +} diff --git a/config/set.go b/config/set.go index 41686b43..6626187c 100644 --- a/config/set.go +++ b/config/set.go @@ -41,6 +41,11 @@ func signalChanges() { func replaceConfig(newValues map[string]interface{}) []*ValidationError { var validationErrors []*ValidationError + valuesCopy := make(map[string]interface{}, len(newValues)) + for key, value := range newValues { + valuesCopy[key] = value + } + // RLock the options because we are not adding or removing // options from the registration but rather only update the // options value which is guarded by the option's lock itself @@ -48,7 +53,9 @@ func replaceConfig(newValues map[string]interface{}) []*ValidationError { defer optionsLock.RUnlock() for key, option := range options { - newValue, ok := newValues[key] + newValue, ok := valuesCopy[key] + + delete(valuesCopy, key) option.Lock() option.activeValue = nil @@ -65,6 +72,11 @@ func replaceConfig(newValues map[string]interface{}) []*ValidationError { option.Unlock() } + unmappedValuesLock.Lock() + defer unmappedValuesLock.Unlock() + + unmappedValues = valuesCopy + signalChanges() return validationErrors