github.com/safing/portbase@v0.19.5/modules/stop.go (about) 1 package modules 2 3 import ( 4 "errors" 5 "fmt" 6 7 "github.com/tevino/abool" 8 9 "github.com/safing/portbase/log" 10 ) 11 12 var ( 13 shutdownSignal = make(chan struct{}) 14 shutdownFlag = abool.NewBool(false) 15 16 shutdownCompleteSignal = make(chan struct{}) 17 18 globalShutdownFn func() 19 ) 20 21 // SetGlobalShutdownFn sets a global shutdown function that is called first when shutting down. 22 func SetGlobalShutdownFn(fn func()) { 23 if globalShutdownFn == nil { 24 globalShutdownFn = fn 25 } 26 } 27 28 // IsShuttingDown returns whether the global shutdown is in progress. 29 func IsShuttingDown() bool { 30 return shutdownFlag.IsSet() 31 } 32 33 // ShuttingDown returns a channel read on the global shutdown signal. 34 func ShuttingDown() <-chan struct{} { 35 return shutdownSignal 36 } 37 38 // Shutdown stops all modules in the correct order. 39 func Shutdown() error { 40 // lock mgmt 41 mgmtLock.Lock() 42 defer mgmtLock.Unlock() 43 44 if shutdownFlag.SetToIf(false, true) { 45 close(shutdownSignal) 46 } else { 47 // shutdown was already issued 48 return errors.New("shutdown already initiated") 49 } 50 51 // Execute global shutdown function. 52 if globalShutdownFn != nil { 53 globalShutdownFn() 54 } 55 56 if initialStartCompleted.IsSet() { 57 log.Warning("modules: starting shutdown...") 58 } else { 59 log.Warning("modules: aborting, shutting down...") 60 } 61 62 err := stopModules() 63 if err != nil { 64 log.Errorf("modules: shutdown completed with error: %s", err) 65 } else { 66 log.Info("modules: shutdown completed") 67 } 68 69 log.Shutdown() 70 close(shutdownCompleteSignal) 71 return err 72 } 73 74 func stopModules() error { 75 var rep *report 76 var lastErr error 77 reports := make(chan *report) 78 execCnt := 0 79 reportCnt := 0 80 81 // get number of started modules 82 startedCnt := 0 83 for _, m := range modules { 84 if m.Status() >= StatusStarting { 85 startedCnt++ 86 } 87 } 88 89 for { 90 waiting := 0 91 92 // find modules to exec 93 for _, m := range modules { 94 switch m.readyToStop() { 95 case statusNothingToDo: 96 case statusWaiting: 97 waiting++ 98 case statusReady: 99 execCnt++ 100 m.stop(reports) 101 } 102 } 103 104 if reportCnt < execCnt { 105 // wait for reports 106 rep = <-reports 107 if rep.err != nil { 108 lastErr = rep.err 109 rep.module.NewErrorMessage("stop module", rep.err).Report() 110 log.Warningf("modules: could not stop module %s: %s", rep.module.Name, rep.err) 111 } 112 reportCnt++ 113 log.Infof("modules: stopped %s", rep.module.Name) 114 } else { 115 // finished 116 if waiting > 0 { 117 // check for dep loop 118 return fmt.Errorf("modules: dependency loop detected, cannot continue") 119 } 120 // return last error 121 return lastErr 122 } 123 } 124 }