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