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 }