From d6fd0d40184601f8f1ba9c9c7a57620b2c8cfb9f Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 29 Mar 2021 13:50:29 +0200 Subject: [PATCH 1/5] Add Flag util --- utils/flag.go | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 utils/flag.go diff --git a/utils/flag.go b/utils/flag.go new file mode 100644 index 0000000..42e7aa3 --- /dev/null +++ b/utils/flag.go @@ -0,0 +1,84 @@ +package utils + +import ( + "sync" + + "github.com/tevino/abool" +) + +// FlagController is a simple system to broadcast a flag value. +type FlagController struct { + flag *abool.AtomicBool + signal chan struct{} + lock sync.Mutex +} + +// Flag receives changes from its FlagController. +// A Flag must only be used in one goroutine and is not concurrency safe, +// but fast. +type Flag struct { + flag *abool.AtomicBool + signal chan struct{} + controller *FlagController +} + +// NewFlagController returns a new FlagController. +// In the initial state, the flag is not set and the singal does not trigger. +func NewFlagController() *FlagController { + return &FlagController{ + flag: abool.New(), + signal: make(chan struct{}), + lock: sync.Mutex{}, + } +} + +// NewFlag returns a new Flag controlled by this controller. +// In the initial state, the flag is set and the singal triggers. +// You can call Refresh immediately to get the current state from the +// controller. +func (cfc *FlagController) NewFlag() *Flag { + newFlag := &Flag{ + flag: abool.NewBool(true), + signal: make(chan struct{}), + controller: cfc, + } + close(newFlag.signal) + return newFlag +} + +// NotifyAndReset notifies all flags of this controller and resets the +// controller state. +func (cfc *FlagController) NotifyAndReset() { + cfc.lock.Lock() + defer cfc.lock.Unlock() + + // Notify all flags of the change. + cfc.flag.Set() + close(cfc.signal) + + // Reset + cfc.flag = abool.New() + cfc.signal = make(chan struct{}) +} + +// Signal returns a channel that waits for the flag to be set. This does not +// reset the Flag itself, you'll need to call Refresh for that. +func (cf *Flag) Signal() <-chan struct{} { + return cf.signal +} + +// IsSet returns whether the flag was set since the last Refresh. +// This does not reset the Flag itself, you'll need to call Refresh for that. +func (cf *Flag) IsSet() bool { + return cf.flag.IsSet() +} + +// Refresh fetches the current state from the controller. +func (cf *Flag) Refresh() { + cf.controller.lock.Lock() + defer cf.controller.lock.Unlock() + + // Copy current flag and signal from the controller. + cf.flag = cf.controller.flag + cf.signal = cf.controller.signal +} From 0d6ed345202873a5d33b4fee7a309c00e1487692 Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 29 Mar 2021 13:50:50 +0200 Subject: [PATCH 2/5] Improve powershell flags --- utils/osdetail/powershell_windows.go | 1 + 1 file changed, 1 insertion(+) diff --git a/utils/osdetail/powershell_windows.go b/utils/osdetail/powershell_windows.go index 7478abb..52ce5c4 100644 --- a/utils/osdetail/powershell_windows.go +++ b/utils/osdetail/powershell_windows.go @@ -12,6 +12,7 @@ func RunPowershellCmd(script string) (output string, err error) { // Create command to execute. cmd := exec.Command( "powershell.exe", + "-ExecutionPolicy", "Bypass", "-NoProfile", "-NonInteractive", "[System.Console]::OutputEncoding = [System.Text.Encoding]::UTF8\n"+script, From 90939d8464a3173a56c924f0fddb02ac3360d93c Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 31 Mar 2021 10:30:01 +0200 Subject: [PATCH 3/5] Add log level config option --- config/basic_config.go | 113 +++++++++++++++++++++++++++++++++++++++++ config/devmode.go | 39 -------------- config/main.go | 11 ++-- config/set.go | 4 +- log/logging.go | 27 +++++++++- 5 files changed, 146 insertions(+), 48 deletions(-) create mode 100644 config/basic_config.go delete mode 100644 config/devmode.go diff --git a/config/basic_config.go b/config/basic_config.go new file mode 100644 index 0000000..ce4a716 --- /dev/null +++ b/config/basic_config.go @@ -0,0 +1,113 @@ +package config + +import ( + "context" + "flag" + + "github.com/safing/portbase/log" +) + +// Configuration Keys. +var ( + CfgDevModeKey = "core/devMode" + defaultDevMode bool + + CfgLogLevel = "core/log/level" + defaultLogLevel = log.InfoLevel.String() + logLevel StringOption +) + +func init() { + flag.BoolVar(&defaultDevMode, "devmode", false, "enable development mode") +} + +func registerBasicOptions() error { + // Get the default log level from the log package. + defaultLogLevel = log.GetLogLevel().Name() + + // Register logging setting. + // The log package cannot do that, as it would trigger and import loop. + if err := Register(&Option{ + Name: "Log Level", + Key: CfgLogLevel, + Description: "Configure the logging level.", + OptType: OptTypeString, + ExpertiseLevel: ExpertiseLevelDeveloper, + ReleaseLevel: ReleaseLevelStable, + DefaultValue: defaultLogLevel, + Annotations: Annotations{ + DisplayOrderAnnotation: 513, + DisplayHintAnnotation: DisplayHintOneOf, + CategoryAnnotation: "Development", + }, + PossibleValues: []PossibleValue{ + { + Name: "Critical", + Value: "critical", + Description: "The critical level only logs errors that lead to a partial, but imminent failure.", + }, + { + Name: "Error", + Value: "error", + Description: "The error level logs errors that potentially break functionality. Everything logged by the critical level is included here too.", + }, + { + Name: "Warning", + Value: "warning", + Description: "The warning level logs minor errors and worse. Everything logged by the error level is included here too.", + }, + { + Name: "Info", + Value: "info", + Description: "The info level logs the main events that are going on and are interesting to the user. Everything logged by the warning level is included here too.", + }, + { + Name: "Debug", + Value: "debug", + Description: "The debug level logs some additional debugging details. Everything logged by the info level is included here too.", + }, + { + Name: "Trace", + Value: "trace", + Description: "The trace level logs loads of detailed information as well as operation and request traces. Everything logged by the debug level is included here too.", + }, + }, + }); err != nil { + return err + } + logLevel = GetAsString(CfgLogLevel, defaultLogLevel) + + // Register to hook to update the log level. + if err := module.RegisterEventHook( + "config", + configChangeEvent, + "update log level", + setLogLevel, + ); err != nil { + return err + } + + return Register(&Option{ + Name: "Development Mode", + Key: CfgDevModeKey, + Description: "In Development Mode, security restrictions are lifted/softened to enable unrestricted access for debugging and testing purposes.", + OptType: OptTypeBool, + ExpertiseLevel: ExpertiseLevelDeveloper, + ReleaseLevel: ReleaseLevelStable, + DefaultValue: defaultDevMode, + Annotations: Annotations{ + DisplayOrderAnnotation: 512, + CategoryAnnotation: "Development", + }, + }) +} + +func loadLogLevel() { + setDefaultConfigOption(CfgLogLevel, log.GetLogLevel().Name(), false) +} + +func setLogLevel(ctx context.Context, data interface{}) error { + log.SetLogLevel(log.ParseLevel(logLevel())) + + return nil +} diff --git a/config/devmode.go b/config/devmode.go deleted file mode 100644 index 7dbd190..0000000 --- a/config/devmode.go +++ /dev/null @@ -1,39 +0,0 @@ -package config - -import ( - "flag" - - "github.com/safing/portbase/log" -) - -// Configuration Keys. -var ( - CfgDevModeKey = "core/devMode" - defaultDevMode bool -) - -func init() { - flag.BoolVar(&defaultDevMode, "devmode", false, "enable development mode") -} - -func logDevModeOverride() { - if defaultDevMode { - log.Warning("config: development mode is enabled by default by the -devmode flag") - } -} - -func registerDevModeOption() error { - return Register(&Option{ - Name: "Development Mode", - Key: CfgDevModeKey, - Description: "In Development Mode, security restrictions are lifted/softened to enable unrestricted access for debugging and testing purposes.", - OptType: OptTypeBool, - ExpertiseLevel: ExpertiseLevelDeveloper, - ReleaseLevel: ReleaseLevelStable, - DefaultValue: defaultDevMode, - Annotations: Annotations{ - DisplayOrderAnnotation: 512, - CategoryAnnotation: "Development", - }, - }) -} diff --git a/config/main.go b/config/main.go index 2857ce8..b064b95 100644 --- a/config/main.go +++ b/config/main.go @@ -47,18 +47,15 @@ func prep() error { modules.SetCmdLineOperation(exportConfigCmd) } - logDevModeOverride() - err := registerDevModeOption() - if err != nil { - return err - } - - return nil + return registerBasicOptions() } func start() error { configFilePath = filepath.Join(dataRoot.Path, "config.json") + // Load log level from log package after it started. + loadLogLevel() + err := registerAsDatabase() if err != nil && !os.IsNotExist(err) { return err diff --git a/config/set.go b/config/set.go index 6e1ef4b..b9b11ff 100644 --- a/config/set.go +++ b/config/set.go @@ -192,5 +192,7 @@ func setDefaultConfigOption(key string, value interface{}, push bool) (err error // finalize change, activate triggers signalChanges() - return saveConfig() + // Do not save the configuration, as it only saves the active values, not the + // active default value. + return nil } diff --git a/log/logging.go b/log/logging.go index 2af4390..27439dd 100644 --- a/log/logging.go +++ b/log/logging.go @@ -102,7 +102,7 @@ var ( logBuffer chan *logLine forceEmptyingOfBuffer = make(chan struct{}) - logLevelInt = uint32(3) + logLevelInt = uint32(InfoLevel) logLevel = &logLevelInt pkgLevelsActive = abool.NewBool(false) @@ -134,11 +134,36 @@ func UnSetPkgLevels() { pkgLevelsActive.UnSet() } +// GetLogLevel returns the current log level. +func GetLogLevel() Severity { + return Severity(atomic.LoadUint32(logLevel)) +} + // SetLogLevel sets a new log level. Only effective after Start(). func SetLogLevel(level Severity) { atomic.StoreUint32(logLevel, uint32(level)) } +// Name returns the name of the log level. +func (s Severity) Name() string { + switch s { + case TraceLevel: + return "trace" + case DebugLevel: + return "debug" + case InfoLevel: + return "info" + case WarningLevel: + return "warning" + case ErrorLevel: + return "error" + case CriticalLevel: + return "critical" + default: + return "none" + } +} + // ParseLevel returns the level severity of a log level name. func ParseLevel(level string) Severity { switch strings.ToLower(level) { From 2b07a6900e1ca969a45b01458972af2c27528b96 Mon Sep 17 00:00:00 2001 From: Daniel Date: Sat, 3 Apr 2021 14:14:26 +0200 Subject: [PATCH 4/5] Rename FlagController to BroadcastFlag --- utils/flag.go | 70 +++++++++++++++++++++++++-------------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/utils/flag.go b/utils/flag.go index 42e7aa3..51c79f2 100644 --- a/utils/flag.go +++ b/utils/flag.go @@ -6,79 +6,79 @@ import ( "github.com/tevino/abool" ) -// FlagController is a simple system to broadcast a flag value. -type FlagController struct { +// BroadcastFlag is a simple system to broadcast a flag value. +type BroadcastFlag struct { flag *abool.AtomicBool signal chan struct{} lock sync.Mutex } -// Flag receives changes from its FlagController. +// Flag receives changes from its broadcasting flag. // A Flag must only be used in one goroutine and is not concurrency safe, // but fast. type Flag struct { - flag *abool.AtomicBool - signal chan struct{} - controller *FlagController + flag *abool.AtomicBool + signal chan struct{} + broadcaster *BroadcastFlag } -// NewFlagController returns a new FlagController. +// NewBroadcastFlag returns a new BroadcastFlag. // In the initial state, the flag is not set and the singal does not trigger. -func NewFlagController() *FlagController { - return &FlagController{ +func NewBroadcastFlag() *BroadcastFlag { + return &BroadcastFlag{ flag: abool.New(), signal: make(chan struct{}), lock: sync.Mutex{}, } } -// NewFlag returns a new Flag controlled by this controller. +// NewFlag returns a new Flag that listens to this broadcasting flag. // In the initial state, the flag is set and the singal triggers. // You can call Refresh immediately to get the current state from the -// controller. -func (cfc *FlagController) NewFlag() *Flag { +// broadcasting flag. +func (bf *BroadcastFlag) NewFlag() *Flag { newFlag := &Flag{ - flag: abool.NewBool(true), - signal: make(chan struct{}), - controller: cfc, + flag: abool.NewBool(true), + signal: make(chan struct{}), + broadcaster: bf, } close(newFlag.signal) return newFlag } -// NotifyAndReset notifies all flags of this controller and resets the -// controller state. -func (cfc *FlagController) NotifyAndReset() { - cfc.lock.Lock() - defer cfc.lock.Unlock() +// NotifyAndReset notifies all flags of this broadcasting flag and resets the +// internal broadcast flag state. +func (bf *BroadcastFlag) NotifyAndReset() { + bf.lock.Lock() + defer bf.lock.Unlock() // Notify all flags of the change. - cfc.flag.Set() - close(cfc.signal) + bf.flag.Set() + close(bf.signal) // Reset - cfc.flag = abool.New() - cfc.signal = make(chan struct{}) + bf.flag = abool.New() + bf.signal = make(chan struct{}) } // Signal returns a channel that waits for the flag to be set. This does not // reset the Flag itself, you'll need to call Refresh for that. -func (cf *Flag) Signal() <-chan struct{} { - return cf.signal +func (f *Flag) Signal() <-chan struct{} { + return f.signal } // IsSet returns whether the flag was set since the last Refresh. // This does not reset the Flag itself, you'll need to call Refresh for that. -func (cf *Flag) IsSet() bool { - return cf.flag.IsSet() +func (f *Flag) IsSet() bool { + return f.flag.IsSet() } -// Refresh fetches the current state from the controller. -func (cf *Flag) Refresh() { - cf.controller.lock.Lock() - defer cf.controller.lock.Unlock() +// Refresh fetches the current state from the broadcasting flag. +func (f *Flag) Refresh() { + f.broadcaster.lock.Lock() + defer f.broadcaster.lock.Unlock() - // Copy current flag and signal from the controller. - cf.flag = cf.controller.flag - cf.signal = cf.controller.signal + // Copy current flag and signal from the broadcasting flag. + f.flag = f.broadcaster.flag + f.signal = f.broadcaster.signal } From 7be481362de2bacc9436d6165e908c00b74df451 Mon Sep 17 00:00:00 2001 From: Daniel Date: Sat, 3 Apr 2021 14:14:59 +0200 Subject: [PATCH 5/5] Rename file --- utils/{flag.go => broadcastflag.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename utils/{flag.go => broadcastflag.go} (100%) diff --git a/utils/flag.go b/utils/broadcastflag.go similarity index 100% rename from utils/flag.go rename to utils/broadcastflag.go