github.com/weaveworks/common@v0.0.0-20230728070032-dd9e68f319d5/signals/signals.go (about)

     1  package signals
     2  
     3  import (
     4  	"os"
     5  	"os/signal"
     6  	"runtime"
     7  	"syscall"
     8  
     9  	"github.com/weaveworks/common/logging"
    10  )
    11  
    12  // SignalReceiver represents a subsystem/server/... that can be stopped or
    13  // queried about the status with a signal
    14  type SignalReceiver interface {
    15  	Stop() error
    16  }
    17  
    18  // Handler handles signals, can be interrupted.
    19  // On SIGINT or SIGTERM it will exit, on SIGQUIT it
    20  // will dump goroutine stacks to the Logger.
    21  type Handler struct {
    22  	log       logging.Interface
    23  	receivers []SignalReceiver
    24  	quit      chan struct{}
    25  }
    26  
    27  // NewHandler makes a new Handler.
    28  func NewHandler(log logging.Interface, receivers ...SignalReceiver) *Handler {
    29  	return &Handler{
    30  		log:       log,
    31  		receivers: receivers,
    32  		quit:      make(chan struct{}),
    33  	}
    34  }
    35  
    36  // Stop the handler
    37  func (h *Handler) Stop() {
    38  	close(h.quit)
    39  }
    40  
    41  // Loop handles signals.
    42  func (h *Handler) Loop() {
    43  	sigs := make(chan os.Signal, 1)
    44  	signal.Notify(sigs, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGTERM)
    45  	defer signal.Stop(sigs)
    46  	buf := make([]byte, 1<<20)
    47  	for {
    48  		select {
    49  		case <-h.quit:
    50  			h.log.Infof("=== Handler.Stop()'d ===")
    51  			return
    52  		case sig := <-sigs:
    53  			switch sig {
    54  			case syscall.SIGINT, syscall.SIGTERM:
    55  				h.log.Infof("=== received SIGINT/SIGTERM ===\n*** exiting")
    56  				for _, subsystem := range h.receivers {
    57  					subsystem.Stop()
    58  				}
    59  				return
    60  			case syscall.SIGQUIT:
    61  				stacklen := runtime.Stack(buf, true)
    62  				h.log.Infof("=== received SIGQUIT ===\n*** goroutine dump...\n%s\n*** end", buf[:stacklen])
    63  			}
    64  		}
    65  	}
    66  }
    67  
    68  // SignalHandlerLoop blocks until it receives a SIGINT, SIGTERM or SIGQUIT.
    69  // For SIGINT and SIGTERM, it exits; for SIGQUIT is print a goroutine stack
    70  // dump.
    71  func SignalHandlerLoop(log logging.Interface, ss ...SignalReceiver) {
    72  	NewHandler(log, ss...).Loop()
    73  }