From ae44c0355750aba25e23afdaf741809d842cb4f2 Mon Sep 17 00:00:00 2001 From: Solomon Hykes Date: Fri, 25 Oct 2013 00:30:34 -0600 Subject: [PATCH] Setup daemon pidfile/cleanup in Server.Daemon() instead of main() Upstream-commit: c1ae1a0e1cfae1aed681add5b1cb1c39e2c0e929 Component: engine --- components/engine/docker/docker.go | 53 +----------------------------- components/engine/server.go | 35 +++++++++++++++++--- components/engine/utils/daemon.go | 36 ++++++++++++++++++++ 3 files changed, 67 insertions(+), 57 deletions(-) create mode 100644 components/engine/utils/daemon.go diff --git a/components/engine/docker/docker.go b/components/engine/docker/docker.go index cd0369636d..d8d30ff341 100644 --- a/components/engine/docker/docker.go +++ b/components/engine/docker/docker.go @@ -7,13 +7,9 @@ import ( "github.com/dotcloud/docker/sysinit" "github.com/dotcloud/docker/utils" "github.com/dotcloud/docker/engine" - "io/ioutil" "log" "os" - "os/signal" - "strconv" "strings" - "syscall" ) var ( @@ -89,7 +85,7 @@ func main() { job.SetenvList("ProtoAddresses", flHosts) job.Setenv("DefaultIp", *flDefaultIp) job.SetenvBool("InterContainerCommunication", *flInterContainerComm) - if err := daemon(job, *pidfile); err != nil { + if err := job.Run(); err != nil { log.Fatal(err) } } else { @@ -109,50 +105,3 @@ func main() { func showVersion() { fmt.Printf("Docker version %s, build %s\n", VERSION, GITCOMMIT) } - -func createPidFile(pidfile string) error { - if pidString, err := ioutil.ReadFile(pidfile); err == nil { - pid, err := strconv.Atoi(string(pidString)) - if err == nil { - if _, err := os.Stat(fmt.Sprintf("/proc/%d/", pid)); err == nil { - return fmt.Errorf("pid file found, ensure docker is not running or delete %s", pidfile) - } - } - } - - file, err := os.Create(pidfile) - if err != nil { - return err - } - - defer file.Close() - - _, err = fmt.Fprintf(file, "%d", os.Getpid()) - return err -} - -func removePidFile(pidfile string) { - if err := os.Remove(pidfile); err != nil { - log.Printf("Error removing %s: %s", pidfile, err) - } -} - -// daemon runs `job` as a daemon. -// A pidfile is created for the duration of the job, -// and all signals are intercepted. -func daemon(job *engine.Job, pidfile string) error { - if err := createPidFile(pidfile); err != nil { - log.Fatal(err) - } - defer removePidFile(pidfile) - - c := make(chan os.Signal, 1) - signal.Notify(c, os.Interrupt, os.Kill, os.Signal(syscall.SIGTERM)) - go func() { - sig := <-c - log.Printf("Received signal '%v', exiting\n", sig) - removePidFile(pidfile) - os.Exit(0) - }() - return job.Run() -} diff --git a/components/engine/server.go b/components/engine/server.go index 08ee7a4974..1547a0e122 100644 --- a/components/engine/server.go +++ b/components/engine/server.go @@ -24,6 +24,7 @@ import ( "sync" "time" "syscall" + "os/signal" ) func (srv *Server) Close() error { @@ -40,8 +41,31 @@ func JobServeApi(job *engine.Job) string { return err.Error() } defer srv.Close() - // Parse addresses to serve on - protoAddrs := job.Args + if err := srv.Daemon(); err != nil { + return err.Error() + } + return "0" +} + +// Daemon runs the remote api server `srv` as a daemon, +// Only one api server can run at the same time - this is enforced by a pidfile. +// The signals SIGINT, SIGKILL and SIGTERM are intercepted for cleanup. +func (srv *Server) Daemon() error { + if err := utils.CreatePidFile(srv.runtime.config.Pidfile); err != nil { + log.Fatal(err) + } + defer utils.RemovePidFile(srv.runtime.config.Pidfile) + + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt, os.Kill, os.Signal(syscall.SIGTERM)) + go func() { + sig := <-c + log.Printf("Received signal '%v', exiting\n", sig) + utils.RemovePidFile(srv.runtime.config.Pidfile) + os.Exit(0) + }() + + protoAddrs := srv.runtime.config.ProtoAddresses chErrors := make(chan error, len(protoAddrs)) for _, protoAddr := range protoAddrs { protoAddrParts := strings.SplitN(protoAddr, "://", 2) @@ -52,7 +76,7 @@ func JobServeApi(job *engine.Job) string { log.Println("/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\") } } else { - return "Invalid protocol format." + return fmt.Errorf("Invalid protocol format.") } go func() { chErrors <- ListenAndServe(protoAddrParts[0], protoAddrParts[1], srv, true) @@ -61,12 +85,13 @@ func JobServeApi(job *engine.Job) string { for i := 0; i < len(protoAddrs); i += 1 { err := <-chErrors if err != nil { - return err.Error() + return err } } - return "0" + return nil } + func (srv *Server) DockerVersion() APIVersion { return APIVersion{ Version: VERSION, diff --git a/components/engine/utils/daemon.go b/components/engine/utils/daemon.go new file mode 100644 index 0000000000..179ff90e10 --- /dev/null +++ b/components/engine/utils/daemon.go @@ -0,0 +1,36 @@ +package utils + +import ( + "os" + "fmt" + "io/ioutil" + "log" + "strconv" +) + +func CreatePidFile(pidfile string) error { + if pidString, err := ioutil.ReadFile(pidfile); err == nil { + pid, err := strconv.Atoi(string(pidString)) + if err == nil { + if _, err := os.Stat(fmt.Sprintf("/proc/%d/", pid)); err == nil { + return fmt.Errorf("pid file found, ensure docker is not running or delete %s", pidfile) + } + } + } + + file, err := os.Create(pidfile) + if err != nil { + return err + } + + defer file.Close() + + _, err = fmt.Fprintf(file, "%d", os.Getpid()) + return err +} + +func RemovePidFile(pidfile string) { + if err := os.Remove(pidfile); err != nil { + log.Printf("Error removing %s: %s", pidfile, err) + } +}