14 "github.com/Sirupsen/logrus"
15 "github.com/pkg/errors"
18 // Trap sets up a simplified signal "trap", appropriate for common
19 // behavior expected from a vanilla unix command-line tool in general
20 // (and the Docker engine in particular).
22 // * If SIGINT or SIGTERM are received, `cleanup` is called, then the process is terminated.
23 // * If SIGINT or SIGTERM are received 3 times before cleanup is complete, then cleanup is
24 // skipped and the process is terminated immediately (allows force quit of stuck daemon)
25 // * A SIGQUIT always causes an exit without cleanup, with a goroutine dump preceding exit.
26 // * Ignore SIGPIPE events. These are generated by systemd when journald is restarted while
27 // the docker daemon is not restarted and also running under systemd.
28 // Fixes https://github.com/docker/docker/issues/19728
30 func Trap(cleanup func()) {
31 c := make(chan os.Signal, 1)
32 // we will handle INT, TERM, QUIT, SIGPIPE here
33 signals := []os.Signal{os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGPIPE}
34 gosignal.Notify(c, signals...)
36 interruptCount := uint32(0)
38 if sig == syscall.SIGPIPE {
42 go func(sig os.Signal) {
43 logrus.Infof("Processing signal '%v'", sig)
45 case os.Interrupt, syscall.SIGTERM:
46 if atomic.LoadUint32(&interruptCount) < 3 {
47 // Initiate the cleanup only once
48 if atomic.AddUint32(&interruptCount, 1) == 1 {
49 // Call the provided cleanup handler
56 // 3 SIGTERM/INT signals received; force exit without cleanup
57 logrus.Info("Forcing docker daemon shutdown without cleanup; 3 interrupts received")
61 logrus.Info("Forcing docker daemon shutdown without cleanup on SIGQUIT")
63 //for the SIGINT/TERM, and SIGQUIT non-clean shutdown case, exit with 128 + signal #
64 os.Exit(128 + int(sig.(syscall.Signal)))
70 const stacksLogNameTemplate = "goroutine-stacks-%s.log"
72 // DumpStacks appends the runtime stack into file in dir and returns full path
74 func DumpStacks(dir string) (string, error) {
80 for stackSize == len(buf) {
81 buf = make([]byte, bufferLen)
82 stackSize = runtime.Stack(buf, true)
88 path := filepath.Join(dir, fmt.Sprintf(stacksLogNameTemplate, strings.Replace(time.Now().Format(time.RFC3339), ":", "", -1)))
90 f, err = os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0666)
92 return "", errors.Wrap(err, "failed to open file to write the goroutine stacks")
99 if _, err := f.Write(buf); err != nil {
100 return "", errors.Wrap(err, "failed to write goroutine stacks")