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 }