mirror of
https://github.com/safing/portbase
synced 2025-09-01 18:19:57 +00:00
158 lines
3.5 KiB
Go
158 lines
3.5 KiB
Go
package subsystems
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/tevino/abool"
|
|
|
|
"github.com/safing/portbase/config"
|
|
"github.com/safing/portbase/modules"
|
|
)
|
|
|
|
var (
|
|
subsystems []*Subsystem
|
|
subsystemsMap = make(map[string]*Subsystem)
|
|
subsystemsLock sync.Mutex
|
|
subsystemsLocked = abool.New()
|
|
|
|
handlingConfigChanges = abool.New()
|
|
)
|
|
|
|
// Register registers a new subsystem. The given option must be a bool option. Should be called in the module's prep function. The config option must not yet be registered and will be registered for you.
|
|
func Register(name, description string, module *modules.Module, configKeySpace string, option *config.Option) error {
|
|
// lock slice and map
|
|
subsystemsLock.Lock()
|
|
defer subsystemsLock.Unlock()
|
|
|
|
// check if registration is closed
|
|
if subsystemsLocked.IsSet() {
|
|
return errors.New("subsystems can only be registered in prep phase")
|
|
}
|
|
|
|
// check if already registered
|
|
_, ok := subsystemsMap[name]
|
|
if ok {
|
|
return fmt.Errorf(`subsystem "%s" already registered`, name)
|
|
}
|
|
|
|
// create new
|
|
new := &Subsystem{
|
|
Name: name,
|
|
Description: description,
|
|
module: module,
|
|
|
|
Status: statusFromModule(module),
|
|
|
|
toggleOption: option,
|
|
ConfigKeySpace: configKeySpace,
|
|
}
|
|
if new.toggleOption != nil {
|
|
new.ToggleOptionKey = new.toggleOption.Key
|
|
new.ExpertiseLevel = new.toggleOption.ExpertiseLevel
|
|
new.ReleaseLevel = new.toggleOption.ReleaseLevel
|
|
}
|
|
|
|
// register config
|
|
if option != nil {
|
|
err := config.Register(option)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to register config: %s", err)
|
|
}
|
|
}
|
|
new.toggleValue = config.GetAsBool(new.ToggleOptionKey, false)
|
|
|
|
// add to lists
|
|
subsystemsMap[name] = new
|
|
subsystems = append(subsystems, new)
|
|
|
|
return nil
|
|
}
|
|
|
|
func handleModuleChanges(m *modules.Module) {
|
|
// check if ready
|
|
if !subsystemsLocked.IsSet() {
|
|
return
|
|
}
|
|
|
|
// find module status
|
|
var moduleSubsystem *Subsystem
|
|
var moduleStatus *ModuleStatus
|
|
subsystemLoop:
|
|
for _, subsystem := range subsystems {
|
|
if m.Name == subsystem.Status.Name {
|
|
moduleSubsystem = subsystem
|
|
moduleStatus = subsystem.Status
|
|
break subsystemLoop
|
|
}
|
|
for _, status := range subsystem.Dependencies {
|
|
if m.Name == status.Name {
|
|
moduleSubsystem = subsystem
|
|
moduleStatus = status
|
|
break subsystemLoop
|
|
}
|
|
}
|
|
}
|
|
// abort if not found
|
|
if moduleSubsystem == nil || moduleStatus == nil {
|
|
return
|
|
}
|
|
|
|
// update status
|
|
moduleSubsystem.Lock()
|
|
changed := compareAndUpdateStatus(m, moduleStatus)
|
|
if changed {
|
|
moduleSubsystem.makeSummary()
|
|
}
|
|
moduleSubsystem.Unlock()
|
|
|
|
// save
|
|
if changed {
|
|
moduleSubsystem.Save()
|
|
}
|
|
}
|
|
|
|
func handleConfigChanges(ctx context.Context, data interface{}) error {
|
|
// check if ready
|
|
if !subsystemsLocked.IsSet() {
|
|
return nil
|
|
}
|
|
|
|
// potentially catch multiple changes
|
|
if handlingConfigChanges.SetToIf(false, true) {
|
|
time.Sleep(100 * time.Millisecond)
|
|
handlingConfigChanges.UnSet()
|
|
} else {
|
|
return nil
|
|
}
|
|
|
|
// only run one instance at any time
|
|
subsystemsLock.Lock()
|
|
defer subsystemsLock.Unlock()
|
|
|
|
var changed bool
|
|
for _, subsystem := range subsystems {
|
|
if subsystem.module.SetEnabled(subsystem.toggleValue()) {
|
|
// if changed
|
|
changed = true
|
|
}
|
|
}
|
|
|
|
// trigger module management if any setting was changed
|
|
if changed {
|
|
err := modules.ManageModules()
|
|
if err != nil {
|
|
module.Error(
|
|
"modulemgmt-failed",
|
|
fmt.Sprintf("The subsystem framework failed to start or stop one or more modules.\nError: %s\nCheck logs for more information.", err),
|
|
)
|
|
} else {
|
|
module.Resolve("modulemgmt-failed")
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|