Tizen_4.0 base
[platform/upstream/docker-engine.git] / pkg / signal / trap.go
1 package signal
2
3 import (
4         "fmt"
5         "os"
6         gosignal "os/signal"
7         "path/filepath"
8         "runtime"
9         "strings"
10         "sync/atomic"
11         "syscall"
12         "time"
13
14         "github.com/Sirupsen/logrus"
15         "github.com/pkg/errors"
16 )
17
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).
21 //
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
29 //
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...)
35         go func() {
36                 interruptCount := uint32(0)
37                 for sig := range c {
38                         if sig == syscall.SIGPIPE {
39                                 continue
40                         }
41
42                         go func(sig os.Signal) {
43                                 logrus.Infof("Processing signal '%v'", sig)
44                                 switch 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
50                                                         cleanup()
51                                                         os.Exit(0)
52                                                 } else {
53                                                         return
54                                                 }
55                                         } else {
56                                                 // 3 SIGTERM/INT signals received; force exit without cleanup
57                                                 logrus.Info("Forcing docker daemon shutdown without cleanup; 3 interrupts received")
58                                         }
59                                 case syscall.SIGQUIT:
60                                         DumpStacks("")
61                                         logrus.Info("Forcing docker daemon shutdown without cleanup on SIGQUIT")
62                                 }
63                                 //for the SIGINT/TERM, and SIGQUIT non-clean shutdown case, exit with 128 + signal #
64                                 os.Exit(128 + int(sig.(syscall.Signal)))
65                         }(sig)
66                 }
67         }()
68 }
69
70 const stacksLogNameTemplate = "goroutine-stacks-%s.log"
71
72 // DumpStacks appends the runtime stack into file in dir and returns full path
73 // to that file.
74 func DumpStacks(dir string) (string, error) {
75         var (
76                 buf       []byte
77                 stackSize int
78         )
79         bufferLen := 16384
80         for stackSize == len(buf) {
81                 buf = make([]byte, bufferLen)
82                 stackSize = runtime.Stack(buf, true)
83                 bufferLen *= 2
84         }
85         buf = buf[:stackSize]
86         var f *os.File
87         if dir != "" {
88                 path := filepath.Join(dir, fmt.Sprintf(stacksLogNameTemplate, strings.Replace(time.Now().Format(time.RFC3339), ":", "", -1)))
89                 var err error
90                 f, err = os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0666)
91                 if err != nil {
92                         return "", errors.Wrap(err, "failed to open file to write the goroutine stacks")
93                 }
94                 defer f.Close()
95                 defer f.Sync()
96         } else {
97                 f = os.Stderr
98         }
99         if _, err := f.Write(buf); err != nil {
100                 return "", errors.Wrap(err, "failed to write goroutine stacks")
101         }
102         return f.Name(), nil
103 }