github.com/containers/podman/v4@v4.9.4/libpod/shutdown/handler.go (about) 1 package shutdown 2 3 import ( 4 "errors" 5 "os" 6 "os/signal" 7 "sync" 8 "syscall" 9 "time" 10 11 logrusImport "github.com/sirupsen/logrus" 12 ) 13 14 var ( 15 ErrHandlerExists = errors.New("handler with given name already exists") 16 ) 17 18 var ( 19 stopped bool 20 sigChan chan os.Signal 21 cancelChan chan bool 22 // Synchronize accesses to the map 23 handlerLock sync.Mutex 24 // Definitions of all on-shutdown handlers 25 handlers map[string]func(os.Signal) error 26 // Ordering that on-shutdown handlers will be invoked. 27 handlerOrder []string 28 shutdownInhibit sync.RWMutex 29 logrus = logrusImport.WithField("PID", os.Getpid()) 30 ErrNotStarted = errors.New("shutdown signal handler has not yet been started") 31 ) 32 33 // Start begins handling SIGTERM and SIGINT and will run the given on-signal 34 // handlers when one is called. This can be cancelled by calling Stop(). 35 func Start() error { 36 if sigChan != nil { 37 // Already running, do nothing. 38 return nil 39 } 40 41 sigChan = make(chan os.Signal, 2) 42 cancelChan = make(chan bool, 1) 43 stopped = false 44 45 signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) 46 47 go func() { 48 select { 49 case <-cancelChan: 50 logrus.Infof("Received shutdown.Stop(), terminating!") 51 signal.Stop(sigChan) 52 close(sigChan) 53 close(cancelChan) 54 stopped = true 55 return 56 case sig := <-sigChan: 57 logrus.Infof("Received shutdown signal %q, terminating!", sig.String()) 58 shutdownInhibit.Lock() 59 handlerLock.Lock() 60 61 for _, name := range handlerOrder { 62 handler, ok := handlers[name] 63 if !ok { 64 logrus.Errorf("Shutdown handler %q definition not found!", name) 65 continue 66 } 67 68 logrus.Infof("Invoking shutdown handler %q", name) 69 start := time.Now() 70 if err := handler(sig); err != nil { 71 logrus.Errorf("Running shutdown handler %q: %v", name, err) 72 } 73 logrus.Debugf("Completed shutdown handler %q, duration %v", name, 74 time.Since(start).Round(time.Second)) 75 } 76 handlerLock.Unlock() 77 shutdownInhibit.Unlock() 78 return 79 } 80 }() 81 82 return nil 83 } 84 85 // Stop the shutdown signal handler. 86 func Stop() error { 87 if cancelChan == nil { 88 return ErrNotStarted 89 } 90 if stopped { 91 return nil 92 } 93 94 cancelChan <- true 95 96 return nil 97 } 98 99 // Inhibit temporarily inhibit signals from shutting down Libpod. 100 func Inhibit() { 101 shutdownInhibit.RLock() 102 } 103 104 // Uninhibit stop inhibiting signals from shutting down Libpod. 105 func Uninhibit() { 106 shutdownInhibit.RUnlock() 107 } 108 109 // Register registers a function that will be executed when Podman is terminated 110 // by a signal. Handlers are invoked LIFO - the last handler registered is the 111 // first run. 112 func Register(name string, handler func(os.Signal) error) error { 113 handlerLock.Lock() 114 defer handlerLock.Unlock() 115 116 if handlers == nil { 117 handlers = make(map[string]func(os.Signal) error) 118 } 119 120 if _, ok := handlers[name]; ok { 121 return ErrHandlerExists 122 } 123 124 handlers[name] = handler 125 handlerOrder = append([]string{name}, handlerOrder...) 126 127 return nil 128 } 129 130 // Unregister un-registers a given shutdown handler. 131 func Unregister(name string) error { 132 handlerLock.Lock() 133 defer handlerLock.Unlock() 134 135 if handlers == nil { 136 return nil 137 } 138 139 if _, ok := handlers[name]; !ok { 140 return nil 141 } 142 143 delete(handlers, name) 144 145 newOrder := []string{} 146 for _, checkName := range handlerOrder { 147 if checkName != name { 148 newOrder = append(newOrder, checkName) 149 } 150 } 151 handlerOrder = newOrder 152 153 return nil 154 }