github.com/docker/engine@v22.0.0-20211208180946-d456264580cf+incompatible/cmd/dockerd/trap/trap.go (about) 1 package trap // import "github.com/docker/docker/cmd/dockerd/trap" 2 3 import ( 4 "fmt" 5 "os" 6 gosignal "os/signal" 7 "sync/atomic" 8 "syscall" 9 10 "github.com/docker/docker/pkg/stack" 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(), logger interface { 26 Info(args ...interface{}) 27 }) { 28 c := make(chan os.Signal, 1) 29 // we will handle INT, TERM, QUIT, SIGPIPE here 30 signals := []os.Signal{os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGPIPE} 31 gosignal.Notify(c, signals...) 32 go func() { 33 interruptCount := uint32(0) 34 for sig := range c { 35 if sig == syscall.SIGPIPE { 36 continue 37 } 38 39 go func(sig os.Signal) { 40 logger.Info(fmt.Sprintf("Processing signal '%v'", sig)) 41 switch sig { 42 case os.Interrupt, syscall.SIGTERM: 43 if atomic.LoadUint32(&interruptCount) < 3 { 44 // Initiate the cleanup only once 45 if atomic.AddUint32(&interruptCount, 1) == 1 { 46 // Call the provided cleanup handler 47 cleanup() 48 os.Exit(0) 49 } else { 50 return 51 } 52 } else { 53 // 3 SIGTERM/INT signals received; force exit without cleanup 54 logger.Info("Forcing docker daemon shutdown without cleanup; 3 interrupts received") 55 } 56 case syscall.SIGQUIT: 57 stack.Dump() 58 logger.Info("Forcing docker daemon shutdown without cleanup on SIGQUIT") 59 } 60 // for the SIGINT/TERM, and SIGQUIT non-clean shutdown case, exit with 128 + signal # 61 os.Exit(128 + int(sig.(syscall.Signal))) 62 }(sig) 63 } 64 }() 65 }