diff --git a/pkg/signal/trap.go b/pkg/signal/trap.go new file mode 100644 index 0000000000..0f44481c2f --- /dev/null +++ b/pkg/signal/trap.go @@ -0,0 +1,53 @@ +package signal + +import ( + "log" + "os" + gosignal "os/signal" + "sync/atomic" + "syscall" +) + +// Trap sets up a simplified signal "trap", appropriate for common +// behavior expected from a vanilla unix command-line tool in general +// (and the Docker engine in particular). +// +// * If SIGINT or SIGTERM are received, `cleanup` is called, then the process is terminated. +// * If SIGINT or SIGTERM are repeated 3 times before cleanup is complete, then cleanup is +// skipped and the process terminated directly. +// * If "DEBUG" is set in the environment, SIGQUIT causes an exit without cleanup. +// +func Trap(cleanup func()) { + c := make(chan os.Signal, 1) + signals := []os.Signal{os.Interrupt, syscall.SIGTERM} + if os.Getenv("DEBUG") == "" { + signals = append(signals, syscall.SIGQUIT) + } + gosignal.Notify(c, signals...) + go func() { + interruptCount := uint32(0) + for sig := range c { + go func(sig os.Signal) { + log.Printf("Received signal '%v', starting shutdown of docker...\n", sig) + switch sig { + case os.Interrupt, syscall.SIGTERM: + // If the user really wants to interrupt, let him do so. + if atomic.LoadUint32(&interruptCount) < 3 { + atomic.AddUint32(&interruptCount, 1) + // Initiate the cleanup only once + if atomic.LoadUint32(&interruptCount) == 1 { + // Call cleanup handler + cleanup() + } else { + return + } + } else { + log.Printf("Force shutdown of docker, interrupting cleanup\n") + } + case syscall.SIGQUIT: + } + os.Exit(128 + int(sig.(syscall.Signal))) + }(sig) + } + }() +} diff --git a/server/init.go b/server/init.go index 082fb0d1cd..c18fd90b4a 100644 --- a/server/init.go +++ b/server/init.go @@ -6,15 +6,11 @@ package server import ( "fmt" - "log" - "os" - gosignal "os/signal" - "sync/atomic" - "syscall" "github.com/docker/docker/daemon" "github.com/docker/docker/daemonconfig" "github.com/docker/docker/engine" + "github.com/docker/docker/pkg/signal" "github.com/docker/docker/utils" ) @@ -50,38 +46,10 @@ func InitServer(job *engine.Job) engine.Status { return job.Error(err) } job.Logf("Setting up signal traps") - c := make(chan os.Signal, 1) - signals := []os.Signal{os.Interrupt, syscall.SIGTERM} - if os.Getenv("DEBUG") == "" { - signals = append(signals, syscall.SIGQUIT) - } - gosignal.Notify(c, signals...) - go func() { - interruptCount := uint32(0) - for sig := range c { - go func(sig os.Signal) { - log.Printf("Received signal '%v', starting shutdown of docker...\n", sig) - switch sig { - case os.Interrupt, syscall.SIGTERM: - // If the user really wants to interrupt, let him do so. - if atomic.LoadUint32(&interruptCount) < 3 { - atomic.AddUint32(&interruptCount, 1) - // Initiate the cleanup only once - if atomic.LoadUint32(&interruptCount) == 1 { - utils.RemovePidFile(srv.daemon.Config().Pidfile) - srv.Close() - } else { - return - } - } else { - log.Printf("Force shutdown of docker, interrupting cleanup\n") - } - case syscall.SIGQUIT: - } - os.Exit(128 + int(sig.(syscall.Signal))) - }(sig) - } - }() + signal.Trap(func() { + utils.RemovePidFile(srv.daemon.Config().Pidfile) + srv.Close() + }) job.Eng.Hack_SetGlobalVar("httpapi.server", srv) job.Eng.Hack_SetGlobalVar("httpapi.daemon", srv.daemon)