github.com/portworx/docker@v1.12.1/pkg/signal/trap.go (about) 1 package signal 2 3 import ( 4 "os" 5 gosignal "os/signal" 6 "runtime" 7 "sync/atomic" 8 "syscall" 9 10 "github.com/Sirupsen/logrus" 11 ) 12 13 // Trap sets up a simplified signal "trap", appropriate for common 14 // behavior expected from a vanilla unix command-line tool in general 15 // (and the Docker engine in particular). 16 // 17 // * If SIGINT or SIGTERM are received, `cleanup` is called, then the process is terminated. 18 // * If SIGINT or SIGTERM are received 3 times before cleanup is complete, then cleanup is 19 // skipped and the process is terminated immediately (allows force quit of stuck daemon) 20 // * A SIGQUIT always causes an exit without cleanup, with a goroutine dump preceding exit. 21 // * Ignore SIGPIPE events. These are generated by systemd when journald is restarted while 22 // the docker daemon is not restarted and also running under systemd. 23 // Fixes https://github.com/docker/docker/issues/19728 24 // 25 func Trap(cleanup func()) { 26 c := make(chan os.Signal, 1) 27 // we will handle INT, TERM, QUIT, SIGPIPE here 28 signals := []os.Signal{os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGPIPE} 29 gosignal.Notify(c, signals...) 30 go func() { 31 interruptCount := uint32(0) 32 for sig := range c { 33 if sig == syscall.SIGPIPE { 34 continue 35 } 36 37 go func(sig os.Signal) { 38 logrus.Infof("Processing signal '%v'", sig) 39 switch sig { 40 case os.Interrupt, syscall.SIGTERM: 41 if atomic.LoadUint32(&interruptCount) < 3 { 42 // Initiate the cleanup only once 43 if atomic.AddUint32(&interruptCount, 1) == 1 { 44 // Call the provided cleanup handler 45 cleanup() 46 os.Exit(0) 47 } else { 48 return 49 } 50 } else { 51 // 3 SIGTERM/INT signals received; force exit without cleanup 52 logrus.Info("Forcing docker daemon shutdown without cleanup; 3 interrupts received") 53 } 54 case syscall.SIGQUIT: 55 DumpStacks() 56 logrus.Info("Forcing docker daemon shutdown without cleanup on SIGQUIT") 57 } 58 //for the SIGINT/TERM, and SIGQUIT non-clean shutdown case, exit with 128 + signal # 59 os.Exit(128 + int(sig.(syscall.Signal))) 60 }(sig) 61 } 62 }() 63 } 64 65 // DumpStacks dumps the runtime stack. 66 func DumpStacks() { 67 var ( 68 buf []byte 69 stackSize int 70 ) 71 bufferLen := 16384 72 for stackSize == len(buf) { 73 buf = make([]byte, bufferLen) 74 stackSize = runtime.Stack(buf, true) 75 bufferLen *= 2 76 } 77 buf = buf[:stackSize] 78 // Note that if the daemon is started with a less-verbose log-level than "info" (the default), the goroutine 79 // traces won't show up in the log. 80 logrus.Infof("=== BEGIN goroutine stack dump ===\n%s\n=== END goroutine stack dump ===", buf) 81 }