container.go
a27b4b8c
 package docker
 
 import (
 	"encoding/json"
cf19be44
 	"flag"
7c57a4cf
 	"fmt"
88ef309a
 	"github.com/dotcloud/docker/term"
2e69e172
 	"github.com/dotcloud/docker/utils"
94b1cf4b
 	"github.com/kr/pty"
a27b4b8c
 	"io"
 	"io/ioutil"
24dac228
 	"log"
a27b4b8c
 	"os"
 	"os/exec"
 	"path"
a91b7109
 	"path/filepath"
931ca464
 	"sort"
09eacdfa
 	"strconv"
931ca464
 	"strings"
a27b4b8c
 	"syscall"
24dac228
 	"time"
a27b4b8c
 )
 
 type Container struct {
7c57a4cf
 	root string
 
fd224ee5
 	ID string
8e9bb02c
 
 	Created time.Time
 
a27b4b8c
 	Path string
 	Args []string
 
7c57a4cf
 	Config *Config
 	State  State
 	Image  string
a27b4b8c
 
09eacdfa
 	network         *NetworkInterface
 	NetworkSettings *NetworkSettings
a27b4b8c
 
1f9f5eed
 	SysInitPath    string
 	ResolvConfPath string
 
 	cmd       *exec.Cmd
2e69e172
 	stdout    *utils.WriteBroadcaster
 	stderr    *utils.WriteBroadcaster
1f9f5eed
 	stdin     io.ReadCloser
 	stdinPipe io.WriteCloser
7d889554
 	ptyMaster io.Closer
5252ab69
 
7a565a04
 	runtime *Runtime
329f4449
 
 	waitLock chan struct{}
6fb495bf
 	Volumes  map[string]string
a27b4b8c
 }
 
 type Config struct {
c04af2a3
 	Hostname     string
 	User         string
 	Memory       int64 // Memory limit (in bytes)
 	MemorySwap   int64 // Total memory usage (memory + swap); set `-1' to disable swap
efd9becb
 	CpuShares    int64 // CPU shares (relative weight vs. other containers)
c04af2a3
 	AttachStdin  bool
 	AttachStdout bool
 	AttachStderr bool
2aad4a34
 	PortSpecs    []string
c04af2a3
 	Tty          bool // Attach standard streams to a tty, including stdin if it is not closed.
 	OpenStdin    bool // Open stdin
 	StdinOnce    bool // If true, close stdin after the 1 attached client disconnects.
 	Env          []string
 	Cmd          []string
7673afc8
 	Dns          []string
c04af2a3
 	Image        string // Name of the image as it was passed by the operator (eg. could be symbolic)
faf8daa7
 	Volumes      map[string]struct{}
4099a313
 	VolumesFrom  string
031f91df
 }
 
10c0e990
 func ParseRun(args []string, capabilities *Capabilities) (*Config, *flag.FlagSet, error) {
b295239d
 	cmd := Subcmd("run", "[OPTIONS] IMAGE COMMAND [ARG...]", "Run a command in a new container")
2333be46
 	if len(args) > 0 && args[0] != "--help" {
 		cmd.SetOutput(ioutil.Discard)
 	}
 
f65fc1e7
 	flHostname := cmd.String("h", "", "Container host name")
a6da7f13
 	flUser := cmd.String("u", "", "Username or UID")
 	flDetach := cmd.Bool("d", false, "Detached mode: leave the container running in the background")
c04af2a3
 	flAttach := NewAttachOpts()
 	cmd.Var(flAttach, "a", "Attach to stdin, stdout or stderr.")
a6da7f13
 	flStdin := cmd.Bool("i", false, "Keep stdin open even if not attached")
 	flTty := cmd.Bool("t", false, "Allocate a pseudo-tty")
 	flMemory := cmd.Int64("m", 0, "Memory limit (in bytes)")
 
10c0e990
 	if capabilities != nil && *flMemory > 0 && !capabilities.MemoryLimit {
 		//fmt.Fprintf(stdout, "WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.\n")
1967c834
 		*flMemory = 0
 	}
 
e36752e0
 	flCpuShares := cmd.Int64("c", 0, "CPU shares (relative weight)")
efd9becb
 
2aad4a34
 	var flPorts ListOpts
b74d1c92
 	cmd.Var(&flPorts, "p", "Expose a container's port to the host (use 'docker port' to see the actual mapping)")
dcc9dfb2
 
a6da7f13
 	var flEnv ListOpts
 	cmd.Var(&flEnv, "e", "Set environment variables")
dcc9dfb2
 
7673afc8
 	var flDns ListOpts
 	cmd.Var(&flDns, "dns", "Set custom dns servers")
 
1df5f409
 	flVolumes := NewPathOpts()
 	cmd.Var(flVolumes, "v", "Attach a data volume")
 
4099a313
 	flVolumesFrom := cmd.String("volumes-from", "", "Mount volumes from the specified container")
 
031f91df
 	if err := cmd.Parse(args); err != nil {
cf19be44
 		return nil, cmd, err
031f91df
 	}
4f36039e
 	if *flDetach && len(flAttach) > 0 {
cf19be44
 		return nil, cmd, fmt.Errorf("Conflicting options: -a and -d")
c04af2a3
 	}
 	// If neither -d or -a are set, attach to everything by default
4f36039e
 	if len(flAttach) == 0 && !*flDetach {
c04af2a3
 		if !*flDetach {
 			flAttach.Set("stdout")
 			flAttach.Set("stderr")
 			if *flStdin {
 				flAttach.Set("stdin")
 			}
 		}
 	}
c8ca50b4
 	parsedArgs := cmd.Args()
 	runCmd := []string{}
 	image := ""
 	if len(parsedArgs) >= 1 {
 		image = cmd.Arg(0)
 	}
 	if len(parsedArgs) > 1 {
 		runCmd = parsedArgs[1:]
 	}
031f91df
 	config := &Config{
15c3096e
 		Hostname:     *flHostname,
2aad4a34
 		PortSpecs:    flPorts,
c04af2a3
 		User:         *flUser,
 		Tty:          *flTty,
 		OpenStdin:    *flStdin,
 		Memory:       *flMemory,
efd9becb
 		CpuShares:    *flCpuShares,
c04af2a3
 		AttachStdin:  flAttach.Get("stdin"),
 		AttachStdout: flAttach.Get("stdout"),
 		AttachStderr: flAttach.Get("stderr"),
 		Env:          flEnv,
 		Cmd:          runCmd,
7673afc8
 		Dns:          flDns,
c04af2a3
 		Image:        image,
1df5f409
 		Volumes:      flVolumes,
4099a313
 		VolumesFrom:  *flVolumesFrom,
031f91df
 	}
640efc2e
 
10c0e990
 	if capabilities != nil && *flMemory > 0 && !capabilities.SwapLimit {
 		//fmt.Fprintf(stdout, "WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.\n")
640efc2e
 		config.MemorySwap = -1
 	}
 
aea2675f
 	// When allocating stdin in attached mode, close stdin at client disconnect
c04af2a3
 	if config.OpenStdin && config.AttachStdin {
aea2675f
 		config.StdinOnce = true
031f91df
 	}
cf19be44
 	return config, cmd, nil
a27b4b8c
 }
 
09eacdfa
 type NetworkSettings struct {
fd224ee5
 	IPAddress   string
 	IPPrefixLen int
09eacdfa
 	Gateway     string
d9a9bfc9
 	Bridge      string
09eacdfa
 	PortMapping map[string]string
c08f5b2b
 }
 
931ca464
 // String returns a human-readable description of the port mapping defined in the settings
 func (settings *NetworkSettings) PortMappingHuman() string {
 	var mapping []string
 	for private, public := range settings.PortMapping {
 		mapping = append(mapping, fmt.Sprintf("%s->%s", public, private))
 	}
 	sort.Strings(mapping)
 	return strings.Join(mapping, ", ")
 }
 
97514831
 // Inject the io.Reader at the given path. Note: do not close the reader
 func (container *Container) Inject(file io.Reader, pth string) error {
9db4972a
 	// Make sure the directory exists
 	if err := os.MkdirAll(path.Join(container.rwPath(), path.Dir(pth)), 0755); err != nil {
 		return err
 	}
 	// FIXME: Handle permissions/already existing dest
 	dest, err := os.Create(path.Join(container.rwPath(), pth))
97514831
 	if err != nil {
 		return err
 	}
 	if _, err := io.Copy(dest, file); err != nil {
 		return err
 	}
 	return nil
 }
 
5d6dd22f
 func (container *Container) Cmd() *exec.Cmd {
 	return container.cmd
 }
 
c7a944ca
 func (container *Container) When() time.Time {
 	return container.Created
 }
 
7c57a4cf
 func (container *Container) FromDisk() error {
 	data, err := ioutil.ReadFile(container.jsonPath())
11b65a00
 	if err != nil {
 		return err
 	}
7c57a4cf
 	// Load container settings
 	if err := json.Unmarshal(data, container); err != nil {
11b65a00
 		return err
 	}
7c57a4cf
 	return nil
11b65a00
 }
 
7c57a4cf
 func (container *Container) ToDisk() (err error) {
8e9bb02c
 	data, err := json.Marshal(container)
a27b4b8c
 	if err != nil {
8e9bb02c
 		return
a27b4b8c
 	}
7c57a4cf
 	return ioutil.WriteFile(container.jsonPath(), data, 0666)
a27b4b8c
 }
 
 func (container *Container) generateLXCConfig() error {
7c57a4cf
 	fo, err := os.Create(container.lxcConfigPath())
a27b4b8c
 	if err != nil {
 		return err
 	}
 	defer fo.Close()
 	if err := LxcTemplateCompiled.Execute(fo, container); err != nil {
 		return err
 	}
 	return nil
 }
 
caea45dd
 func (container *Container) startPty() error {
7d889554
 	ptyMaster, ptySlave, err := pty.Open()
caea45dd
 	if err != nil {
 		return err
 	}
7d889554
 	container.ptyMaster = ptyMaster
 	container.cmd.Stdout = ptySlave
 	container.cmd.Stderr = ptySlave
caea45dd
 
 	// Copy the PTYs to our broadcasters
 	go func() {
a83d87ab
 		defer container.stdout.CloseWriters()
2e69e172
 		utils.Debugf("[startPty] Begin of stdout pipe")
7d889554
 		io.Copy(container.stdout, ptyMaster)
2e69e172
 		utils.Debugf("[startPty] End of stdout pipe")
caea45dd
 	}()
 
 	// stdin
 	if container.Config.OpenStdin {
7d889554
 		container.cmd.Stdin = ptySlave
33a5fe3b
 		container.cmd.SysProcAttr = &syscall.SysProcAttr{Setctty: true, Setsid: true}
caea45dd
 		go func() {
 			defer container.stdin.Close()
2e69e172
 			utils.Debugf("[startPty] Begin of stdin pipe")
7d889554
 			io.Copy(ptyMaster, container.stdin)
2e69e172
 			utils.Debugf("[startPty] End of stdin pipe")
caea45dd
 		}()
 	}
 	if err := container.cmd.Start(); err != nil {
 		return err
 	}
7d889554
 	ptySlave.Close()
caea45dd
 	return nil
 }
 
 func (container *Container) start() error {
 	container.cmd.Stdout = container.stdout
 	container.cmd.Stderr = container.stderr
 	if container.Config.OpenStdin {
 		stdin, err := container.cmd.StdinPipe()
 		if err != nil {
 			return err
 		}
 		go func() {
 			defer stdin.Close()
2e69e172
 			utils.Debugf("Begin of stdin pipe [start]")
caea45dd
 			io.Copy(stdin, container.stdin)
2e69e172
 			utils.Debugf("End of stdin pipe [start]")
caea45dd
 		}()
 	}
 	return container.cmd.Start()
 }
 
6882c78c
 func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, stdout io.Writer, stderr io.Writer) chan error {
 	var cStdout, cStderr io.ReadCloser
 
c808940c
 	var nJobs int
 	errors := make(chan error, 3)
 	if stdin != nil && container.Config.OpenStdin {
 		nJobs += 1
 		if cStdin, err := container.StdinPipe(); err != nil {
 			errors <- err
 		} else {
 			go func() {
2e69e172
 				utils.Debugf("[start] attach stdin\n")
 				defer utils.Debugf("[end] attach stdin\n")
ad2bbe23
 				// No matter what, when stdin is closed (io.Copy unblock), close stdout and stderr
 				if cStdout != nil {
 					defer cStdout.Close()
 				}
 				if cStderr != nil {
 					defer cStderr.Close()
 				}
64c1b6d9
 				if container.Config.StdinOnce && !container.Config.Tty {
c808940c
 					defer cStdin.Close()
 				}
faa88436
 				if container.Config.Tty {
2e69e172
 					_, err = utils.CopyEscapable(cStdin, stdin)
faa88436
 				} else {
 					_, err = io.Copy(cStdin, stdin)
 				}
c808940c
 				if err != nil {
2e69e172
 					utils.Debugf("[error] attach stdin: %s\n", err)
c808940c
 				}
6882c78c
 				// Discard error, expecting pipe error
 				errors <- nil
c808940c
 			}()
 		}
 	}
 	if stdout != nil {
 		nJobs += 1
 		if p, err := container.StdoutPipe(); err != nil {
 			errors <- err
 		} else {
 			cStdout = p
 			go func() {
2e69e172
 				utils.Debugf("[start] attach stdout\n")
 				defer utils.Debugf("[end]  attach stdout\n")
6882c78c
 				// If we are in StdinOnce mode, then close stdin
 				if container.Config.StdinOnce {
 					if stdin != nil {
 						defer stdin.Close()
 					}
 					if stdinCloser != nil {
 						defer stdinCloser.Close()
 					}
 				}
c808940c
 				_, err := io.Copy(stdout, cStdout)
 				if err != nil {
2e69e172
 					utils.Debugf("[error] attach stdout: %s\n", err)
c808940c
 				}
 				errors <- err
 			}()
 		}
0ca88443
 	} else {
 		go func() {
63e80384
 			if stdinCloser != nil {
 				defer stdinCloser.Close()
 			}
0ca88443
 
63e80384
 			if cStdout, err := container.StdoutPipe(); err != nil {
0ca88443
 				utils.Debugf("Error stdout pipe")
63e80384
 			} else {
 				io.Copy(&utils.NopWriter{}, cStdout)
0ca88443
 			}
 		}()
c808940c
 	}
 	if stderr != nil {
 		nJobs += 1
 		if p, err := container.StderrPipe(); err != nil {
 			errors <- err
 		} else {
 			cStderr = p
 			go func() {
2e69e172
 				utils.Debugf("[start] attach stderr\n")
 				defer utils.Debugf("[end]  attach stderr\n")
6882c78c
 				// If we are in StdinOnce mode, then close stdin
 				if container.Config.StdinOnce {
 					if stdin != nil {
 						defer stdin.Close()
 					}
 					if stdinCloser != nil {
 						defer stdinCloser.Close()
 					}
 				}
c808940c
 				_, err := io.Copy(stderr, cStderr)
 				if err != nil {
2e69e172
 					utils.Debugf("[error] attach stderr: %s\n", err)
c808940c
 				}
 				errors <- err
 			}()
 		}
0ca88443
 	} else {
 		go func() {
63e80384
 			if stdinCloser != nil {
 				defer stdinCloser.Close()
 			}
0ca88443
 
71692126
 			if cStderr, err := container.StderrPipe(); err != nil {
0ca88443
 				utils.Debugf("Error stdout pipe")
63e80384
 			} else {
 				io.Copy(&utils.NopWriter{}, cStderr)
0ca88443
 			}
 		}()
c808940c
 	}
0ca88443
 
2e69e172
 	return utils.Go(func() error {
c808940c
 		if cStdout != nil {
 			defer cStdout.Close()
 		}
 		if cStderr != nil {
 			defer cStderr.Close()
 		}
 		// FIXME: how do clean up the stdin goroutine without the unwanted side effect
 		// of closing the passed stdin? Add an intermediary io.Pipe?
 		for i := 0; i < nJobs; i += 1 {
2e69e172
 			utils.Debugf("Waiting for job %d/%d\n", i+1, nJobs)
c808940c
 			if err := <-errors; err != nil {
2e69e172
 				utils.Debugf("Job %d returned error %s. Aborting all jobs\n", i+1, err)
c808940c
 				return err
 			}
2e69e172
 			utils.Debugf("Job %d completed successfully\n", i+1)
c808940c
 		}
2e69e172
 		utils.Debugf("All jobs completed successfully\n")
c808940c
 		return nil
 	})
 }
 
a27b4b8c
 func (container *Container) Start() error {
7c2b085d
 	container.State.lock()
 	defer container.State.unlock()
 
d949e280
 	if container.State.Running {
fd224ee5
 		return fmt.Errorf("The container %s is already running.", container.ID)
d949e280
 	}
7c57a4cf
 	if err := container.EnsureMounted(); err != nil {
a27b4b8c
 		return err
 	}
c08f5b2b
 	if err := container.allocateNetwork(); err != nil {
 		return err
 	}
1967c834
 
f68d107a
 	// Make sure the config is compatible with the current kernel
 	if container.Config.Memory > 0 && !container.runtime.capabilities.MemoryLimit {
 		log.Printf("WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.\n")
1967c834
 		container.Config.Memory = 0
 	}
f68d107a
 	if container.Config.Memory > 0 && !container.runtime.capabilities.SwapLimit {
 		log.Printf("WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.\n")
 		container.Config.MemorySwap = -1
 	}
6fb495bf
 	container.Volumes = make(map[string]string)
1967c834
 
8d9aaee6
 	// Create the requested volumes volumes
 	for volPath := range container.Config.Volumes {
86ada2fa
 		c, err := container.runtime.volumes.Create(nil, container, "", "", nil)
 		if err != nil {
8d9aaee6
 			return err
 		}
86ada2fa
 		if err := os.MkdirAll(path.Join(container.RootfsPath(), volPath), 0755); err != nil {
 			return nil
 		}
fd224ee5
 		container.Volumes[volPath] = c.ID
8d9aaee6
 	}
 
4099a313
 	if container.Config.VolumesFrom != "" {
 		c := container.runtime.Get(container.Config.VolumesFrom)
 		if c == nil {
fd224ee5
 			return fmt.Errorf("Container %s not found. Impossible to mount its volumes", container.ID)
4099a313
 		}
 		for volPath, id := range c.Volumes {
 			if _, exists := container.Volumes[volPath]; exists {
fd224ee5
 				return fmt.Errorf("The requested volume %s overlap one of the volume of the container %s", volPath, c.ID)
4099a313
 			}
 			if err := os.MkdirAll(path.Join(container.RootfsPath(), volPath), 0755); err != nil {
 				return nil
 			}
 			container.Volumes[volPath] = id
 		}
 	}
1967c834
 
1793538a
 	if err := container.generateLXCConfig(); err != nil {
 		return err
 	}
8d9aaee6
 
a27b4b8c
 	params := []string{
fd224ee5
 		"-n", container.ID,
7c57a4cf
 		"-f", container.lxcConfigPath(),
a27b4b8c
 		"--",
58a22942
 		"/sbin/init",
a27b4b8c
 	}
5cecd548
 
 	// Networking
c08f5b2b
 	params = append(params, "-g", container.network.Gateway.String())
5cecd548
 
 	// User
6de3e8a2
 	if container.Config.User != "" {
 		params = append(params, "-u", container.Config.User)
 	}
5cecd548
 
7b0e96f1
 	if container.Config.Tty {
 		params = append(params, "-e", "TERM=xterm")
 	}
 
 	// Setup environment
 	params = append(params,
 		"-e", "HOME=/",
 		"-e", "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
 	)
 
 	for _, elem := range container.Config.Env {
 		params = append(params, "-e", elem)
 	}
 
5cecd548
 	// Program
6de3e8a2
 	params = append(params, "--", container.Path)
a27b4b8c
 	params = append(params, container.Args...)
5cecd548
 
6d72758f
 	container.cmd = exec.Command("lxc-start", params...)
a27b4b8c
 
0f7a4534
 	// Setup logging of stdout and stderr to disk
 	if err := container.runtime.LogToDisk(container.stdout, container.logPath("stdout")); err != nil {
 		return err
 	}
 	if err := container.runtime.LogToDisk(container.stderr, container.logPath("stderr")); err != nil {
 		return err
 	}
 
caea45dd
 	var err error
7a50153c
 	if container.Config.Tty {
caea45dd
 		err = container.startPty()
7a50153c
 	} else {
caea45dd
 		err = container.start()
7a50153c
 	}
caea45dd
 	if err != nil {
a27b4b8c
 		return err
 	}
7c57a4cf
 	// FIXME: save state on disk *first*, then converge
 	// this way disk state is used as a journal, eg. we can restore after crash etc.
a27b4b8c
 	container.State.setRunning(container.cmd.Process.Pid)
329f4449
 
 	// Init the lock
 	container.waitLock = make(chan struct{})
35d704c8
 
7c57a4cf
 	container.ToDisk()
a27b4b8c
 	go container.monitor()
f958bdba
 	return nil
a27b4b8c
 }
 
 func (container *Container) Run() error {
 	if err := container.Start(); err != nil {
 		return err
 	}
 	container.Wait()
 	return nil
 }
 
 func (container *Container) Output() (output []byte, err error) {
 	pipe, err := container.StdoutPipe()
 	if err != nil {
 		return nil, err
 	}
 	defer pipe.Close()
 	if err := container.Start(); err != nil {
 		return nil, err
 	}
 	output, err = ioutil.ReadAll(pipe)
 	container.Wait()
 	return output, err
 }
 
7a50153c
 // StdinPipe() returns a pipe connected to the standard input of the container's
 // active process.
 //
 func (container *Container) StdinPipe() (io.WriteCloser, error) {
 	return container.stdinPipe, nil
 }
 
a27b4b8c
 func (container *Container) StdoutPipe() (io.ReadCloser, error) {
 	reader, writer := io.Pipe()
 	container.stdout.AddWriter(writer)
2e69e172
 	return utils.NewBufReader(reader), nil
a27b4b8c
 }
 
 func (container *Container) StderrPipe() (io.ReadCloser, error) {
 	reader, writer := io.Pipe()
 	container.stderr.AddWriter(writer)
2e69e172
 	return utils.NewBufReader(reader), nil
a27b4b8c
 }
 
c08f5b2b
 func (container *Container) allocateNetwork() error {
d65983f3
 	iface, err := container.runtime.networkManager.Allocate()
c08f5b2b
 	if err != nil {
 		return err
 	}
09eacdfa
 	container.NetworkSettings.PortMapping = make(map[string]string)
2aad4a34
 	for _, spec := range container.Config.PortSpecs {
86ada2fa
 		nat, err := iface.AllocatePort(spec)
 		if err != nil {
09eacdfa
 			iface.Release()
 			return err
 		}
86ada2fa
 		container.NetworkSettings.PortMapping[strconv.Itoa(nat.Backend)] = strconv.Itoa(nat.Frontend)
09eacdfa
 	}
c08f5b2b
 	container.network = iface
d9a9bfc9
 	container.NetworkSettings.Bridge = container.runtime.networkManager.bridgeIface
fd224ee5
 	container.NetworkSettings.IPAddress = iface.IPNet.IP.String()
 	container.NetworkSettings.IPPrefixLen, _ = iface.IPNet.Mask.Size()
09eacdfa
 	container.NetworkSettings.Gateway = iface.Gateway.String()
c08f5b2b
 	return nil
 }
 
54443c09
 func (container *Container) releaseNetwork() {
6f9a67a7
 	container.network.Release()
c08f5b2b
 	container.network = nil
09eacdfa
 	container.NetworkSettings = &NetworkSettings{}
5d6dd22f
 }
 
82848d41
 // FIXME: replace this with a control socket within docker-init
 func (container *Container) waitLxc() error {
 	for {
fd224ee5
 		output, err := exec.Command("lxc-info", "-n", container.ID).CombinedOutput()
86ada2fa
 		if err != nil {
82848d41
 			return err
86ada2fa
 		}
 		if !strings.Contains(string(output), "RUNNING") {
 			return nil
82848d41
 		}
 		time.Sleep(500 * time.Millisecond)
 	}
7ef9833d
 	panic("Unreachable")
82848d41
 }
 
a27b4b8c
 func (container *Container) monitor() {
f0c08b57
 	// Wait for the program to exit
2e69e172
 	utils.Debugf("Waiting for process")
82848d41
 
 	// If the command does not exists, try to wait via lxc
 	if container.cmd == nil {
 		if err := container.waitLxc(); err != nil {
fd224ee5
 			utils.Debugf("%s: Process: %s", container.ID, err)
82848d41
 		}
 	} else {
 		if err := container.cmd.Wait(); err != nil {
 			// Discard the error as any signals or non 0 returns will generate an error
fd224ee5
 			utils.Debugf("%s: Process: %s", container.ID, err)
82848d41
 		}
69c2250e
 	}
2e69e172
 	utils.Debugf("Process finished")
ccac5b13
 
86ada2fa
 	exitCode := -1
82848d41
 	if container.cmd != nil {
 		exitCode = container.cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
 	}
24dac228
 
f0c08b57
 	// Cleanup
6f9a67a7
 	container.releaseNetwork()
47607494
 	if container.Config.OpenStdin {
 		if err := container.stdin.Close(); err != nil {
fd224ee5
 			utils.Debugf("%s: Error close stdin: %s", container.ID, err)
47607494
 		}
 	}
a83d87ab
 	if err := container.stdout.CloseWriters(); err != nil {
fd224ee5
 		utils.Debugf("%s: Error close stdout: %s", container.ID, err)
69c2250e
 	}
a83d87ab
 	if err := container.stderr.CloseWriters(); err != nil {
fd224ee5
 		utils.Debugf("%s: Error close stderr: %s", container.ID, err)
69c2250e
 	}
5252ab69
 
7d889554
 	if container.ptyMaster != nil {
 		if err := container.ptyMaster.Close(); err != nil {
fd224ee5
 			utils.Debugf("%s: Error closing Pty master: %s", container.ID, err)
5252ab69
 		}
 	}
 
7c57a4cf
 	if err := container.Unmount(); err != nil {
fd224ee5
 		log.Printf("%v: Failed to umount filesystem: %v", container.ID, err)
24dac228
 	}
 
0da9ccc1
 	// Re-create a brand new stdin pipe once the container exited
 	if container.Config.OpenStdin {
 		container.stdin, container.stdinPipe = io.Pipe()
 	}
 
24dac228
 	// Report status back
 	container.State.setStopped(exitCode)
329f4449
 
 	// Release the lock
 	close(container.waitLock)
 
69c2250e
 	if err := container.ToDisk(); err != nil {
a52a28b6
 		// FIXME: there is a race condition here which causes this to fail during the unit tests.
 		// If another goroutine was waiting for Wait() to return before removing the container's root
 		// from the filesystem... At this point it may already have done so.
 		// This is because State.setStopped() has already been called, and has caused Wait()
 		// to return.
 		// FIXME: why are we serializing running state to disk in the first place?
fd224ee5
 		//log.Printf("%s: Failed to dump configuration to the disk: %s", container.ID, err)
69c2250e
 	}
a27b4b8c
 }
 
24dac228
 func (container *Container) kill() error {
d440782e
 	if !container.State.Running {
b32436cd
 		return nil
 	}
e68c04b7
 
bb22cd49
 	// Sending SIGKILL to the process via lxc
fd224ee5
 	output, err := exec.Command("lxc-kill", "-n", container.ID, "9").CombinedOutput()
e68c04b7
 	if err != nil {
fd224ee5
 		log.Printf("error killing container %s (%s, %s)", container.ID, output, err)
a27b4b8c
 	}
e68c04b7
 
 	// 2. Wait for the process to die, in last resort, try to kill the process directly
 	if err := container.WaitTimeout(10 * time.Second); err != nil {
d440782e
 		if container.cmd == nil {
fd224ee5
 			return fmt.Errorf("lxc-kill failed, impossible to kill the container %s", container.ID)
d440782e
 		}
fd224ee5
 		log.Printf("Container %s failed to exit within 10 seconds of lxc SIGKILL - trying direct SIGKILL", container.ID)
e68c04b7
 		if err := container.cmd.Process.Kill(); err != nil {
 			return err
 		}
 	}
 
24dac228
 	// Wait for the container to be actually stopped
f958bdba
 	container.Wait()
24dac228
 	return nil
 }
 
 func (container *Container) Kill() error {
7c2b085d
 	container.State.lock()
 	defer container.State.unlock()
313d13ea
 	if !container.State.Running {
 		return nil
 	}
24dac228
 	return container.kill()
 }
 
1615bb08
 func (container *Container) Stop(seconds int) error {
7c2b085d
 	container.State.lock()
 	defer container.State.unlock()
24dac228
 	if !container.State.Running {
a27b4b8c
 		return nil
 	}
24dac228
 
 	// 1. Send a SIGTERM
fd224ee5
 	if output, err := exec.Command("lxc-kill", "-n", container.ID, "15").CombinedOutput(); err != nil {
c298a91f
 		log.Print(string(output))
 		log.Print("Failed to send SIGTERM to the process, force killing")
7c2b085d
 		if err := container.kill(); err != nil {
f4e25694
 			return err
 		}
24dac228
 	}
 
 	// 2. Wait for the process to exit on its own
1615bb08
 	if err := container.WaitTimeout(time.Duration(seconds) * time.Second); err != nil {
fd224ee5
 		log.Printf("Container %v failed to exit within %d seconds of SIGTERM - using the force", container.ID, seconds)
0d9e5436
 		if err := container.kill(); err != nil {
f4e25694
 			return err
 		}
24dac228
 	}
a27b4b8c
 	return nil
 }
 
1615bb08
 func (container *Container) Restart(seconds int) error {
 	if err := container.Stop(seconds); err != nil {
f0c08b57
 		return err
 	}
 	if err := container.Start(); err != nil {
 		return err
 	}
 	return nil
 }
 
ebaa50c4
 // Wait blocks until the container stops running, then returns its exit code.
 func (container *Container) Wait() int {
329f4449
 	<-container.waitLock
ebaa50c4
 	return container.State.ExitCode
a27b4b8c
 }
24dac228
 
70d2123e
 func (container *Container) Resize(h, w int) error {
88ef309a
 	pty, ok := container.ptyMaster.(*os.File)
 	if !ok {
 		return fmt.Errorf("ptyMaster does not have Fd() method")
 	}
 	return term.SetWinsize(pty.Fd(), &term.Winsize{Height: uint16(h), Width: uint16(w)})
70d2123e
 }
 
ef711962
 func (container *Container) ExportRw() (Archive, error) {
 	return Tar(container.rwPath(), Uncompressed)
7c57a4cf
 }
 
048fd671
 func (container *Container) RwChecksum() (string, error) {
be756089
 	rwData, err := Tar(container.rwPath(), Xz)
048fd671
 	if err != nil {
 		return "", err
 	}
2e69e172
 	return utils.HashData(rwData)
048fd671
 }
 
ef711962
 func (container *Container) Export() (Archive, error) {
7c57a4cf
 	if err := container.EnsureMounted(); err != nil {
 		return nil, err
 	}
ef711962
 	return Tar(container.RootfsPath(), Uncompressed)
7c57a4cf
 }
 
24dac228
 func (container *Container) WaitTimeout(timeout time.Duration) error {
 	done := make(chan bool)
 	go func() {
 		container.Wait()
 		done <- true
 	}()
 
 	select {
 	case <-time.After(timeout):
d17f78c3
 		return fmt.Errorf("Timed Out")
24dac228
 	case <-done:
 		return nil
 	}
7ef9833d
 
 	panic("Unreachable")
24dac228
 }
7c57a4cf
 
 func (container *Container) EnsureMounted() error {
 	if mounted, err := container.Mounted(); err != nil {
 		return err
 	} else if mounted {
 		return nil
 	}
 	return container.Mount()
 }
 
 func (container *Container) Mount() error {
 	image, err := container.GetImage()
 	if err != nil {
 		return err
 	}
 	return image.Mount(container.RootfsPath(), container.rwPath())
 }
 
ef711962
 func (container *Container) Changes() ([]Change, error) {
7c57a4cf
 	image, err := container.GetImage()
 	if err != nil {
 		return nil, err
 	}
 	return image.Changes(container.rwPath())
 }
 
ef711962
 func (container *Container) GetImage() (*Image, error) {
7c57a4cf
 	if container.runtime == nil {
 		return nil, fmt.Errorf("Can't get image of unregistered container")
 	}
 	return container.runtime.graph.Get(container.Image)
 }
 
 func (container *Container) Mounted() (bool, error) {
ef711962
 	return Mounted(container.RootfsPath())
7c57a4cf
 }
 
 func (container *Container) Unmount() error {
ef711962
 	return Unmount(container.RootfsPath())
7c57a4cf
 }
 
fd224ee5
 // ShortID returns a shorthand version of the container's id for convenience.
0b9a3c86
 // A collision with other container shorthands is very unlikely, but possible.
 // In case of a collision a lookup with Runtime.Get() will fail, and the caller
 // will need to use a langer prefix, or the full-length container Id.
fd224ee5
 func (container *Container) ShortID() string {
 	return utils.TruncateID(container.ID)
0b9a3c86
 }
 
7c57a4cf
 func (container *Container) logPath(name string) string {
fd224ee5
 	return path.Join(container.root, fmt.Sprintf("%s-%s.log", container.ID, name))
7c57a4cf
 }
 
 func (container *Container) ReadLog(name string) (io.Reader, error) {
 	return os.Open(container.logPath(name))
 }
 
 func (container *Container) jsonPath() string {
 	return path.Join(container.root, "config.json")
 }
 
 func (container *Container) lxcConfigPath() string {
 	return path.Join(container.root, "config.lxc")
 }
 
 // This method must be exported to be used from the lxc template
 func (container *Container) RootfsPath() string {
 	return path.Join(container.root, "rootfs")
 }
 
6fb495bf
 func (container *Container) GetVolumes() (map[string]string, error) {
 	ret := make(map[string]string)
 	for volPath, id := range container.Volumes {
 		volume, err := container.runtime.volumes.Get(id)
 		if err != nil {
 			return nil, err
 		}
 		root, err := volume.root()
 		if err != nil {
 			return nil, err
 		}
 		ret[volPath] = path.Join(root, "layer")
 	}
 	return ret, nil
 }
 
7c57a4cf
 func (container *Container) rwPath() string {
 	return path.Join(container.root, "rw")
 }
 
fd224ee5
 func validateID(id string) error {
7c57a4cf
 	if id == "" {
 		return fmt.Errorf("Invalid empty id")
 	}
 	return nil
 }
a91b7109
 
 // GetSize, return real size, virtual size
 func (container *Container) GetSize() (int64, int64) {
 	var sizeRw, sizeRootfs int64
 
 	filepath.Walk(container.rwPath(), func(path string, fileInfo os.FileInfo, err error) error {
 		if fileInfo != nil {
 			sizeRw += fileInfo.Size()
 		}
 		return nil
 	})
 
 	_, err := os.Stat(container.RootfsPath())
 	if err == nil {
 		filepath.Walk(container.RootfsPath(), func(path string, fileInfo os.FileInfo, err error) error {
 			if fileInfo != nil {
 				sizeRootfs += fileInfo.Size()
 			}
 			return nil
 		})
 	}
 	return sizeRw, sizeRootfs
 }