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  }