github.com/containers/podman/v2@v2.2.2-0.20210501105131-c1e07d070c4c/libpod/shutdown/handler.go (about)

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