diff --git a/container.go b/container.go index c1f557724..90347a321 100644 --- a/container.go +++ b/container.go @@ -337,7 +337,7 @@ func (container *Container) startPty() error { utils.Debugf("startPty: end of stdin pipe") }() } - if err := container.runtime.execDriver.Start(container.process); err != nil { + if err := container.runtime.Start(container); err != nil { return err } ptySlave.Close() @@ -359,7 +359,7 @@ func (container *Container) start() error { utils.Debugf("start: end of stdin pipe") }() } - return container.runtime.execDriver.Start(container.process) + return container.runtime.Start(container) } func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, stdout io.Writer, stderr io.Writer) chan error { @@ -732,7 +732,7 @@ func (container *Container) Start() (err error) { } container.process = &execdriver.Process{ - Name: container.ID, + ID: container.ID, Privileged: container.hostConfig.Privileged, Rootfs: root, InitPath: "/.dockerinit", @@ -744,6 +744,7 @@ func (container *Container) Start() (err error) { Tty: container.Config.Tty, User: container.Config.User, } + container.process.SysProcAttr = &syscall.SysProcAttr{Setsid: true} // Setup logging of stdout and stderr to disk if err := container.runtime.LogToDisk(container.stdout, container.logPath("json"), "stdout"); err != nil { @@ -753,6 +754,10 @@ func (container *Container) Start() (err error) { return err } + // Init the lock + container.waitLock = make(chan struct{}) + go container.monitor() + if container.Config.Tty { err = container.startPty() } else { @@ -763,12 +768,7 @@ func (container *Container) Start() (err error) { } container.State.SetRunning(container.process.Pid()) - - // Init the lock - container.waitLock = make(chan struct{}) - container.ToDisk() - go container.monitor() return nil } @@ -1143,11 +1143,10 @@ func (container *Container) releaseNetwork() { } func (container *Container) monitor() { + time.Sleep(1 * time.Second) // Wait for the program to exit - if container.process == nil { - panic("Container process is nil") - } - if err := container.runtime.execDriver.Wait(container.process, time.Duration(0)); err != nil { + fmt.Printf("--->Before WAIT %s\n", container.ID) + if err := container.runtime.Wait(container, time.Duration(0)); err != nil { // Since non-zero exit status and signal terminations will cause err to be non-nil, // we have to actually discard it. Still, log it anyway, just in case. utils.Debugf("monitor: cmd.Wait reported exit status %s for container %s", err, container.ID) @@ -1156,6 +1155,7 @@ func (container *Container) monitor() { } } + fmt.Printf("--->After WAIT %s\n", container.ID) // Cleanup container.cleanup() @@ -1221,7 +1221,7 @@ func (container *Container) kill(sig int) error { if !container.State.IsRunning() { return nil } - return container.runtime.execDriver.Kill(container.process, sig) + return container.runtime.Kill(container, sig) } func (container *Container) Kill() error { @@ -1240,7 +1240,7 @@ func (container *Container) Kill() error { return fmt.Errorf("lxc-kill failed, impossible to kill the container %s", utils.TruncateID(container.ID)) } log.Printf("Container %s failed to exit within 10 seconds of lxc-kill %s - trying direct SIGKILL", "SIGKILL", utils.TruncateID(container.ID)) - if err := container.runtime.execDriver.Kill(container.process, 9); err != nil { + if err := container.runtime.Kill(container, 9); err != nil { return err } } diff --git a/execdriver/driver.go b/execdriver/driver.go index 503f7f2ae..eb468f3ef 100644 --- a/execdriver/driver.go +++ b/execdriver/driver.go @@ -13,7 +13,6 @@ var ( type Driver interface { Start(c *Process) error - Stop(c *Process) error Kill(c *Process, sig int) error Wait(c *Process, duration time.Duration) error } @@ -26,10 +25,11 @@ type Network struct { Mtu int } +// Process wrapps an os/exec.Cmd to add more metadata type Process struct { exec.Cmd - Name string // unique name for the conatienr + ID string Privileged bool User string Rootfs string // root fs of the container diff --git a/execdriver/lxc/driver.go b/execdriver/lxc/driver.go index e649263f8..f6306b4e1 100644 --- a/execdriver/lxc/driver.go +++ b/execdriver/lxc/driver.go @@ -4,10 +4,10 @@ import ( "errors" "fmt" "github.com/dotcloud/docker/execdriver" + "os" "os/exec" "strconv" "strings" - "syscall" "time" ) @@ -25,22 +25,20 @@ func init() { } type driver struct { - root string // root path for the driver to use - containers map[string]*execdriver.Process + root string // root path for the driver to use } func NewDriver(root string) (execdriver.Driver, error) { // setup unconfined symlink return &driver{ - root: root, - containers: make(map[string]*execdriver.Process), + root: root, }, nil } func (d *driver) Start(c *execdriver.Process) error { params := []string{ startPath, - "-n", c.Name, + "-n", c.ID, "-f", c.ConfigPath, "--", c.InitPath, @@ -80,8 +78,7 @@ func (d *driver) Start(c *execdriver.Process) error { c.Path = aname c.Args = append([]string{name}, arg...) - c.SysProcAttr = &syscall.SysProcAttr{Setsid: true} - + fmt.Printf("-->%s\n-->%v\n", name, arg) if err := c.Start(); err != nil { return err } @@ -93,21 +90,6 @@ func (d *driver) Start(c *execdriver.Process) error { return nil } -func (d *driver) Stop(c *execdriver.Process) error { - if err := d.kill(c, 15); err != nil { - if err := d.kill(c, 9); err != nil { - return err - } - } - - if err := d.wait(c, 10*time.Second); err != nil { - if err := d.kill(c, 9); err != nil { - return err - } - } - return nil -} - func (d *driver) Kill(c *execdriver.Process, sig int) error { return d.kill(c, sig) } @@ -148,40 +130,49 @@ begin: } func (d *driver) kill(c *execdriver.Process, sig int) error { - return exec.Command("lxc-kill", "-n", c.Name, strconv.Itoa(sig)).Run() + return exec.Command("lxc-kill", "-n", c.ID, strconv.Itoa(sig)).Run() } func (d *driver) waitForStart(c *execdriver.Process) error { + var ( + err error + output []byte + ) // We wait for the container to be fully running. // Timeout after 5 seconds. In case of broken pipe, just retry. // Note: The container can run and finish correctly before // the end of this loop for now := time.Now(); time.Since(now) < 5*time.Second; { - // If the container dies while waiting for it, just return - /* - if !c.State.IsRunning() { - return nil - } - */ - output, err := exec.Command("lxc-info", "-s", "-n", c.Name).CombinedOutput() + // If the process dies while waiting for it, just return + if c.ProcessState != nil && c.ProcessState.Exited() { + return nil + } + + output, err = d.getInfo(c) if err != nil { - output, err = exec.Command("lxc-info", "-s", "-n", c.Name).CombinedOutput() + output, err = d.getInfo(c) if err != nil { return err } - } if strings.Contains(string(output), "RUNNING") { return nil } time.Sleep(50 * time.Millisecond) } + fmt.Printf("-->%s\n", string(output)) + + os.Exit(1) return ErrNotRunning } func (d *driver) waitCmd(c *execdriver.Process) <-chan error { done := make(chan error) go func() { + if c == nil { + done <- execdriver.ErrCommandIsNil + return + } done <- c.Wait() }() return done @@ -191,7 +182,7 @@ func (d *driver) waitLxc(c *execdriver.Process, kill *bool) <-chan error { done := make(chan error) go func() { for *kill { - output, err := exec.Command("lxc-info", "-n", c.Name).CombinedOutput() + output, err := exec.Command("lxc-info", "-n", c.ID).CombinedOutput() if err != nil { done <- err return @@ -205,3 +196,7 @@ func (d *driver) waitLxc(c *execdriver.Process, kill *bool) <-chan error { }() return done } + +func (d *driver) getInfo(c *execdriver.Process) ([]byte, error) { + return exec.Command("lxc-info", "-s", "-n", c.ID).CombinedOutput() +} diff --git a/runtime.go b/runtime.go index 2686f51df..d302c9c21 100644 --- a/runtime.go +++ b/runtime.go @@ -838,6 +838,18 @@ func (runtime *Runtime) Diff(container *Container) (archive.Archive, error) { return archive.ExportChanges(cDir, changes) } +func (runtime *Runtime) Start(c *Container) error { + return runtime.execDriver.Start(c.process) +} + +func (runtime *Runtime) Kill(c *Container, sig int) error { + return runtime.execDriver.Kill(c.process, sig) +} + +func (runtime *Runtime) Wait(c *Container, duration time.Duration) error { + return runtime.execDriver.Wait(c.process, duration) +} + // Nuke kills all containers then removes all content // from the content root, including images, volumes and // container filesystems.