github.com/safing/portbase@v0.19.5/modules/mgmt.go (about)

     1  package modules
     2  
     3  import (
     4  	"context"
     5  
     6  	"github.com/tevino/abool"
     7  
     8  	"github.com/safing/portbase/log"
     9  )
    10  
    11  var (
    12  	moduleMgmtEnabled     = abool.NewBool(false)
    13  	modulesChangeNotifyFn func(*Module)
    14  )
    15  
    16  // Enable enables the module. Only has an effect if module management
    17  // is enabled.
    18  func (m *Module) Enable() (changed bool) {
    19  	return m.enabled.SetToIf(false, true)
    20  }
    21  
    22  // Disable disables the module. Only has an effect if module management
    23  // is enabled.
    24  func (m *Module) Disable() (changed bool) {
    25  	return m.enabled.SetToIf(true, false)
    26  }
    27  
    28  // SetEnabled sets the module to the desired enabled state. Only has
    29  // an effect if module management is enabled.
    30  func (m *Module) SetEnabled(enable bool) (changed bool) {
    31  	if enable {
    32  		return m.Enable()
    33  	}
    34  	return m.Disable()
    35  }
    36  
    37  // Enabled returns whether or not the module is currently enabled.
    38  func (m *Module) Enabled() bool {
    39  	return m.enabled.IsSet()
    40  }
    41  
    42  // EnabledAsDependency returns whether or not the module is currently
    43  // enabled as a dependency.
    44  func (m *Module) EnabledAsDependency() bool {
    45  	return m.enabledAsDependency.IsSet()
    46  }
    47  
    48  // EnableModuleManagement enables the module management functionality
    49  // within modules. The supplied notify function will be called whenever
    50  // the status of a module changes. The affected module will be in the
    51  // parameter. You will need to manually enable modules, else nothing
    52  // will start.
    53  // EnableModuleManagement returns true if changeNotifyFn has been set
    54  // and it has been called for the first time.
    55  //
    56  // Example:
    57  //
    58  //	EnableModuleManagement(func(m *modules.Module) {
    59  //		// some module has changed ...
    60  //		// do what ever you like
    61  //
    62  //		// Run the built-in module management
    63  //		modules.ManageModules()
    64  //	})
    65  func EnableModuleManagement(changeNotifyFn func(*Module)) bool {
    66  	if moduleMgmtEnabled.SetToIf(false, true) {
    67  		modulesChangeNotifyFn = changeNotifyFn
    68  		return true
    69  	}
    70  	return false
    71  }
    72  
    73  // DisableModuleManagement disables module management and returns the module
    74  // system to the default start/stop behavior.
    75  func DisableModuleManagement() {
    76  	moduleMgmtEnabled.UnSet()
    77  }
    78  
    79  func (m *Module) notifyOfChange() {
    80  	if moduleMgmtEnabled.IsSet() && modulesChangeNotifyFn != nil {
    81  		m.StartWorker("notify of change", func(ctx context.Context) error {
    82  			modulesChangeNotifyFn(m)
    83  			return nil
    84  		})
    85  	}
    86  }
    87  
    88  // ManageModules triggers the module manager to react to recent changes of
    89  // enabled modules.
    90  func ManageModules() error {
    91  	// check if enabled
    92  	if !moduleMgmtEnabled.IsSet() {
    93  		return nil
    94  	}
    95  
    96  	// lock mgmt
    97  	mgmtLock.Lock()
    98  	defer mgmtLock.Unlock()
    99  
   100  	log.Info("modules: managing changes")
   101  
   102  	// build new dependency tree
   103  	buildEnabledTree()
   104  
   105  	// stop unneeded modules
   106  	lastErr := stopModules()
   107  	if lastErr != nil {
   108  		log.Warning(lastErr.Error())
   109  	}
   110  
   111  	// start needed modules
   112  	err := startModules()
   113  	if err != nil {
   114  		log.Warning(err.Error())
   115  		lastErr = err
   116  	}
   117  
   118  	log.Info("modules: finished managing")
   119  	return lastErr
   120  }
   121  
   122  func buildEnabledTree() {
   123  	// reset marked dependencies
   124  	for _, m := range modules {
   125  		m.enabledAsDependency.UnSet()
   126  	}
   127  
   128  	// mark dependencies
   129  	for _, m := range modules {
   130  		if m.enabled.IsSet() {
   131  			m.markDependencies()
   132  		}
   133  	}
   134  }
   135  
   136  func (m *Module) markDependencies() {
   137  	for _, dep := range m.depModules {
   138  		if dep.enabledAsDependency.SetToIf(false, true) {
   139  			dep.markDependencies()
   140  		}
   141  	}
   142  }