ソースを参照

Merge pull request #5640 from crosbymichael/bump_v0.11.0

Bump version to v0.11.0
Michael Crosby 11 年 前
コミット
57aa2f2ef4
100 ファイル変更3587 行追加1248 行削除
  1. 3 0
      .gitignore
  2. 2 14
      .travis.yml
  3. 2 0
      AUTHORS
  4. 12 0
      CHANGELOG.md
  5. 5 1
      CONTRIBUTING.md
  6. 4 0
      Dockerfile
  7. 1 1
      MAINTAINERS
  8. 19 6
      Makefile
  9. 1 1
      README.md
  10. 1 1
      VERSION
  11. 7 0
      api/client/cli.go
  12. 24 15
      api/client/commands.go
  13. 133 0
      api/client/hijack.go
  14. 27 160
      api/client/utils.go
  15. 1 1
      api/common.go
  16. 65 5
      api/server/server.go
  17. 40 72
      api/server/server_unit_test.go
  18. 1 1
      archive/diff.go
  19. 17 9
      builtins/builtins.go
  20. 18 11
      contrib/check-config.sh
  21. 1 1
      contrib/docker-device-tool/device_tool.go
  22. 2 0
      contrib/man/.gitignore
  23. 5 0
      contrib/man/md/Dockerfile
  24. 71 0
      contrib/man/md/README.md
  25. 57 0
      contrib/man/md/docker-attach.1.md
  26. 82 0
      contrib/man/md/docker-build.1.md
  27. 34 0
      contrib/man/md/docker-commit.1.md
  28. 24 0
      contrib/man/md/docker-cp.1.md
  29. 44 0
      contrib/man/md/docker-diff.1.md
  30. 46 0
      contrib/man/md/docker-events.1.md
  31. 26 0
      contrib/man/md/docker-export.1.md
  32. 32 0
      contrib/man/md/docker-history.1.md
  33. 99 0
      contrib/man/md/docker-images.1.md
  34. 39 0
      contrib/man/md/docker-import.1.md
  35. 46 0
      contrib/man/md/docker-info.1.md
  36. 229 0
      contrib/man/md/docker-inspect.1.md
  37. 21 0
      contrib/man/md/docker-kill.1.md
  38. 36 0
      contrib/man/md/docker-load.1.md
  39. 35 0
      contrib/man/md/docker-login.1.md
  40. 26 0
      contrib/man/md/docker-logs.1.md
  41. 15 0
      contrib/man/md/docker-port.1.md
  42. 68 0
      contrib/man/md/docker-ps.1.md
  43. 37 0
      contrib/man/md/docker-pull.1.md
  44. 44 0
      contrib/man/md/docker-push.1.md
  45. 21 0
      contrib/man/md/docker-restart.1.md
  46. 56 0
      contrib/man/md/docker-rm.1.md
  47. 35 0
      contrib/man/md/docker-rmi.1.md
  48. 343 0
      contrib/man/md/docker-run.1.md
  49. 35 0
      contrib/man/md/docker-save.1.md
  50. 55 0
      contrib/man/md/docker-search.1.md
  51. 25 0
      contrib/man/md/docker-start.1.md
  52. 22 0
      contrib/man/md/docker-stop.1.md
  53. 48 0
      contrib/man/md/docker-tag.1.md
  54. 27 0
      contrib/man/md/docker-top.1.md
  55. 23 0
      contrib/man/md/docker-wait.1.md
  56. 187 0
      contrib/man/md/docker.1.md
  57. 22 0
      contrib/man/md/md2man-all.sh
  58. 0 0
      contrib/man/old-man/docker-attach.1
  59. 0 0
      contrib/man/old-man/docker-build.1
  60. 0 0
      contrib/man/old-man/docker-images.1
  61. 0 0
      contrib/man/old-man/docker-info.1
  62. 0 0
      contrib/man/old-man/docker-inspect.1
  63. 0 0
      contrib/man/old-man/docker-rm.1
  64. 50 0
      contrib/man/old-man/docker-rm.md
  65. 0 0
      contrib/man/old-man/docker-rmi.1
  66. 0 0
      contrib/man/old-man/docker-run.1
  67. 0 0
      contrib/man/old-man/docker-tag.1
  68. 0 0
      contrib/man/old-man/docker.1
  69. 82 0
      contrib/mkimage-alpine.sh
  70. 1 0
      contrib/mkimage-arch.sh
  71. 0 1
      contrib/zfs/MAINTAINERS
  72. 0 23
      contrib/zfs/README.md
  73. 153 0
      daemon/attach.go
  74. 405 528
      daemon/container.go
  75. 1 1
      daemon/container_unit_test.go
  76. 215 214
      daemon/daemon.go
  77. 3 3
      daemon/daemon_aufs.go
  78. 7 0
      daemon/daemon_btrfs.go
  79. 7 0
      daemon/daemon_devicemapper.go
  80. 2 2
      daemon/daemon_no_aufs.go
  81. 0 0
      daemon/execdriver/MAINTAINERS
  82. 4 2
      daemon/execdriver/driver.go
  83. 3 3
      daemon/execdriver/execdrivers/execdrivers.go
  84. 24 9
      daemon/execdriver/lxc/driver.go
  85. 0 0
      daemon/execdriver/lxc/info.go
  86. 0 0
      daemon/execdriver/lxc/info_test.go
  87. 6 4
      daemon/execdriver/lxc/init.go
  88. 0 0
      daemon/execdriver/lxc/lxc_init_linux.go
  89. 0 0
      daemon/execdriver/lxc/lxc_init_unsupported.go
  90. 11 10
      daemon/execdriver/lxc/lxc_template.go
  91. 1 1
      daemon/execdriver/lxc/lxc_template_unit_test.go
  92. 36 26
      daemon/execdriver/native/configuration/parse.go
  93. 27 10
      daemon/execdriver/native/configuration/parse_test.go
  94. 44 17
      daemon/execdriver/native/create.go
  95. 56 94
      daemon/execdriver/native/driver.go
  96. 0 0
      daemon/execdriver/native/info.go
  97. 47 0
      daemon/execdriver/native/template/default_template.go
  98. 1 1
      daemon/execdriver/native/term.go
  99. 0 0
      daemon/execdriver/pipes.go
  100. 0 0
      daemon/execdriver/termconsole.go

+ 3 - 0
.gitignore

@@ -23,3 +23,6 @@ bundles/
 vendor/pkg/
 vendor/pkg/
 pyenv
 pyenv
 Vagrantfile
 Vagrantfile
+docs/AWS_S3_BUCKET
+docs/GIT_BRANCH
+docs/VERSION

+ 2 - 14
.travis.yml

@@ -10,21 +10,9 @@ install: true
 
 
 before_script:
 before_script:
   - env | sort
   - env | sort
-  - sudo apt-get update -qq
-  - sudo apt-get install -qq python-yaml
-  - git remote add upstream git://github.com/dotcloud/docker.git
-  - upstream=master;
-    if [ "$TRAVIS_PULL_REQUEST" != false ]; then
-      upstream=$TRAVIS_BRANCH;
-    fi;
-    git fetch --append --no-tags upstream refs/heads/$upstream:refs/remotes/upstream/$upstream
-# sometimes we have upstream master already as origin/master (PRs), but other times we don't, so let's just make sure we have a completely unambiguous way to specify "upstream master" from here out
-# but if it's a PR against non-master, we need that upstream branch instead :)
-  - sudo pip install -r docs/requirements.txt
 
 
 script:
 script:
-  - hack/travis/dco.py
-  - hack/travis/gofmt.py
-  - make -sC docs SPHINXOPTS=-qW docs man
+  - hack/make.sh validate-dco
+  - hack/make.sh validate-gofmt
 
 
 # vim:set sw=2 ts=2:
 # vim:set sw=2 ts=2:

+ 2 - 0
AUTHORS

@@ -20,6 +20,7 @@ Andrew Munsell <andrew@wizardapps.net>
 Andrews Medina <andrewsmedina@gmail.com>
 Andrews Medina <andrewsmedina@gmail.com>
 Andy Chambers <anchambers@paypal.com>
 Andy Chambers <anchambers@paypal.com>
 andy diller <dillera@gmail.com>
 andy diller <dillera@gmail.com>
+Andy Goldstein <agoldste@redhat.com>
 Andy Rothfusz <github@metaliveblog.com>
 Andy Rothfusz <github@metaliveblog.com>
 Andy Smith <github@anarkystic.com>
 Andy Smith <github@anarkystic.com>
 Anthony Bishopric <git@anthonybishopric.com>
 Anthony Bishopric <git@anthonybishopric.com>
@@ -44,6 +45,7 @@ Brian Olsen <brian@maven-group.org>
 Brian Shumate <brian@couchbase.com>
 Brian Shumate <brian@couchbase.com>
 Briehan Lombaard <briehan.lombaard@gmail.com>
 Briehan Lombaard <briehan.lombaard@gmail.com>
 Bruno Bigras <bigras.bruno@gmail.com>
 Bruno Bigras <bigras.bruno@gmail.com>
+Bryan Matsuo <bryan.matsuo@gmail.com>
 Caleb Spare <cespare@gmail.com>
 Caleb Spare <cespare@gmail.com>
 Calen Pennington <cale@edx.org>
 Calen Pennington <cale@edx.org>
 Carl X. Su <bcbcarl@gmail.com>
 Carl X. Su <bcbcarl@gmail.com>

+ 12 - 0
CHANGELOG.md

@@ -1,5 +1,17 @@
 # Changelog
 # Changelog
 
 
+## 0.11.0 (2014-05-07)
+
+#### Notable features since 0.10.0
+
+* SELinux support for mount and process labels
+* Linked containers can be accessed by hostname
+* Use the net `--net` flag to allow advanced network configuration such as host networking so that containers can use the host's network interfaces
+* Add a ping endpoint to the Remote API to do healthchecks of your docker daemon
+* Logs can now be returned with an optional timestamp
+* Docker now works with registries that support SHA-512
+* Multiple registry endpoints are supported to allow registry mirrors
+
 ## 0.10.0 (2014-04-08)
 ## 0.10.0 (2014-04-08)
 
 
 #### Builder
 #### Builder

+ 5 - 1
CONTRIBUTING.md

@@ -82,7 +82,7 @@ editors have plugins that do this automatically, and there's also a git
 pre-commit hook:
 pre-commit hook:
 
 
 ```
 ```
-curl -o .git/hooks/pre-commit https://raw.github.com/edsrzf/gofmt-git-hook/master/fmt-check && chmod +x .git/hooks/pre-commit
+curl -o .git/hooks/pre-commit https://raw.githubusercontent.com/edsrzf/gofmt-git-hook/master/fmt-check && chmod +x .git/hooks/pre-commit
 ```
 ```
 
 
 Pull requests descriptions should be as clear as possible and include a
 Pull requests descriptions should be as clear as possible and include a
@@ -90,6 +90,10 @@ reference to all the issues that they address.
 
 
 Pull requests must not contain commits from other users or branches.
 Pull requests must not contain commits from other users or branches.
 
 
+Commit messages must start with a capitalized and short summary (max. 50
+chars) written in the imperative, followed by an optional, more detailed
+explanatory text which is separated from the summary by an empty line.
+
 Code review comments may be added to your pull request. Discuss, then make the
 Code review comments may be added to your pull request. Discuss, then make the
 suggested modifications and push additional commits to your feature branch. Be
 suggested modifications and push additional commits to your feature branch. Be
 sure to post a comment after pushing. The new commits will show up in the pull
 sure to post a comment after pushing. The new commits will show up in the pull

+ 4 - 0
Dockerfile

@@ -42,6 +42,7 @@ RUN	apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -yq \
 	libcap-dev \
 	libcap-dev \
 	libsqlite3-dev \
 	libsqlite3-dev \
 	mercurial \
 	mercurial \
+	pandoc \
 	reprepro \
 	reprepro \
 	ruby1.9.1 \
 	ruby1.9.1 \
 	ruby1.9.1-dev \
 	ruby1.9.1-dev \
@@ -82,6 +83,9 @@ RUN	go get code.google.com/p/go.tools/cmd/cover
 # TODO replace FPM with some very minimal debhelper stuff
 # TODO replace FPM with some very minimal debhelper stuff
 RUN	gem install --no-rdoc --no-ri fpm --version 1.0.2
 RUN	gem install --no-rdoc --no-ri fpm --version 1.0.2
 
 
+# Get the "busybox" image source so we can build locally instead of pulling
+RUN	git clone https://github.com/jpetazzo/docker-busybox.git /docker-busybox
+
 # Setup s3cmd config
 # Setup s3cmd config
 RUN	/bin/echo -e '[default]\naccess_key=$AWS_ACCESS_KEY\nsecret_key=$AWS_SECRET_KEY' > /.s3cfg
 RUN	/bin/echo -e '[default]\naccess_key=$AWS_ACCESS_KEY\nsecret_key=$AWS_SECRET_KEY' > /.s3cfg
 
 

+ 1 - 1
MAINTAINERS

@@ -1,4 +1,4 @@
-Solomon Hykes <solomon@dotcloud.com> (@shykes)
+Solomon Hykes <solomon@docker.com> (@shykes)
 Guillaume J. Charmes <guillaume@docker.com> (@creack)
 Guillaume J. Charmes <guillaume@docker.com> (@creack)
 Victor Vieux <vieux@docker.com> (@vieux)
 Victor Vieux <vieux@docker.com> (@vieux)
 Michael Crosby <michael@crosbymichael.com> (@crosbymichael)
 Michael Crosby <michael@crosbymichael.com> (@crosbymichael)

+ 19 - 6
Makefile

@@ -1,4 +1,4 @@
-.PHONY: all binary build cross default docs docs-build docs-shell shell test test-integration test-integration-cli
+.PHONY: all binary build cross default docs docs-build docs-shell shell test test-unit test-integration test-integration-cli validate
 
 
 # to allow `make BINDDIR=. shell` or `make BINDDIR= test`
 # to allow `make BINDDIR=. shell` or `make BINDDIR= test`
 BINDDIR := bundles
 BINDDIR := bundles
@@ -10,8 +10,9 @@ DOCKER_IMAGE := docker$(if $(GIT_BRANCH),:$(GIT_BRANCH))
 DOCKER_DOCS_IMAGE := docker-docs$(if $(GIT_BRANCH),:$(GIT_BRANCH))
 DOCKER_DOCS_IMAGE := docker-docs$(if $(GIT_BRANCH),:$(GIT_BRANCH))
 DOCKER_MOUNT := $(if $(BINDDIR),-v "$(CURDIR)/$(BINDDIR):/go/src/github.com/dotcloud/docker/$(BINDDIR)")
 DOCKER_MOUNT := $(if $(BINDDIR),-v "$(CURDIR)/$(BINDDIR):/go/src/github.com/dotcloud/docker/$(BINDDIR)")
 
 
-DOCKER_RUN_DOCKER := docker run --rm -it --privileged -e TESTFLAGS -e DOCKER_GRAPHDRIVER -e DOCKER_EXECDRIVER $(DOCKER_MOUNT) "$(DOCKER_IMAGE)"
-DOCKER_RUN_DOCS := docker run --rm -it -p $(if $(DOCSPORT),$(DOCSPORT):)8000 "$(DOCKER_DOCS_IMAGE)"
+DOCKER_RUN_DOCKER := docker run --rm -it --privileged -e TESTFLAGS -e TESTDIRS -e DOCKER_GRAPHDRIVER -e DOCKER_EXECDRIVER $(DOCKER_MOUNT) "$(DOCKER_IMAGE)"
+# to allow `make DOCSDIR=docs docs-shell`
+DOCKER_RUN_DOCS := docker run --rm -it $(if $(DOCSDIR),-v $(CURDIR)/$(DOCSDIR):/$(DOCSDIR)) -e AWS_S3_BUCKET
 
 
 default: binary
 default: binary
 
 
@@ -25,13 +26,19 @@ cross: build
 	$(DOCKER_RUN_DOCKER) hack/make.sh binary cross
 	$(DOCKER_RUN_DOCKER) hack/make.sh binary cross
 
 
 docs: docs-build
 docs: docs-build
-	$(DOCKER_RUN_DOCS)
+	$(DOCKER_RUN_DOCS) -p $(if $(DOCSPORT),$(DOCSPORT):)8000 "$(DOCKER_DOCS_IMAGE)" mkdocs serve
 
 
 docs-shell: docs-build
 docs-shell: docs-build
-	$(DOCKER_RUN_DOCS) bash
+	$(DOCKER_RUN_DOCS) -p $(if $(DOCSPORT),$(DOCSPORT):)8000 "$(DOCKER_DOCS_IMAGE)" bash
+
+docs-release: docs-build
+	$(DOCKER_RUN_DOCS) "$(DOCKER_DOCS_IMAGE)" ./release.sh
 
 
 test: build
 test: build
-	$(DOCKER_RUN_DOCKER) hack/make.sh binary test test-integration test-integration-cli
+	$(DOCKER_RUN_DOCKER) hack/make.sh binary test-unit test-integration test-integration-cli
+
+test-unit: build
+	$(DOCKER_RUN_DOCKER) hack/make.sh test-unit
 
 
 test-integration: build
 test-integration: build
 	$(DOCKER_RUN_DOCKER) hack/make.sh test-integration
 	$(DOCKER_RUN_DOCKER) hack/make.sh test-integration
@@ -39,6 +46,9 @@ test-integration: build
 test-integration-cli: build
 test-integration-cli: build
 	$(DOCKER_RUN_DOCKER) hack/make.sh binary test-integration-cli
 	$(DOCKER_RUN_DOCKER) hack/make.sh binary test-integration-cli
 
 
+validate: build
+	$(DOCKER_RUN_DOCKER) hack/make.sh validate-gofmt validate-dco
+
 shell: build
 shell: build
 	$(DOCKER_RUN_DOCKER) bash
 	$(DOCKER_RUN_DOCKER) bash
 
 
@@ -46,6 +56,9 @@ build: bundles
 	docker build -t "$(DOCKER_IMAGE)" .
 	docker build -t "$(DOCKER_IMAGE)" .
 
 
 docs-build:
 docs-build:
+	cp ./VERSION docs/VERSION
+	echo "$(GIT_BRANCH)" > docs/GIT_BRANCH
+	echo "$(AWS_S3_BUCKET)" > docs/AWS_S3_BUCKET
 	docker build -t "$(DOCKER_DOCS_IMAGE)" docs
 	docker build -t "$(DOCKER_DOCS_IMAGE)" docs
 
 
 bundles:
 bundles:

+ 1 - 1
README.md

@@ -18,7 +18,7 @@ It benefits directly from the experience accumulated over several years
 of large-scale operation and support of hundreds of thousands of
 of large-scale operation and support of hundreds of thousands of
 applications and databases.
 applications and databases.
 
 
-![Docker L](docs/theme/docker/static/img/dockerlogo-h.png "Docker")
+![Docker L](docs/theme/mkdocs/img/logo_compressed.png "Docker")
 
 
 ## Better than VMs
 ## Better than VMs
 
 

+ 1 - 1
VERSION

@@ -1 +1 @@
-0.10.0
+0.11.0

+ 7 - 0
api/client/cli.go

@@ -65,8 +65,13 @@ func NewDockerCli(in io.ReadCloser, out, err io.Writer, proto, addr string, tlsC
 	var (
 	var (
 		isTerminal = false
 		isTerminal = false
 		terminalFd uintptr
 		terminalFd uintptr
+		scheme     = "http"
 	)
 	)
 
 
+	if tlsConfig != nil {
+		scheme = "https"
+	}
+
 	if in != nil {
 	if in != nil {
 		if file, ok := in.(*os.File); ok {
 		if file, ok := in.(*os.File); ok {
 			terminalFd = file.Fd()
 			terminalFd = file.Fd()
@@ -86,6 +91,7 @@ func NewDockerCli(in io.ReadCloser, out, err io.Writer, proto, addr string, tlsC
 		isTerminal: isTerminal,
 		isTerminal: isTerminal,
 		terminalFd: terminalFd,
 		terminalFd: terminalFd,
 		tlsConfig:  tlsConfig,
 		tlsConfig:  tlsConfig,
+		scheme:     scheme,
 	}
 	}
 }
 }
 
 
@@ -99,4 +105,5 @@ type DockerCli struct {
 	isTerminal bool
 	isTerminal bool
 	terminalFd uintptr
 	terminalFd uintptr
 	tlsConfig  *tls.Config
 	tlsConfig  *tls.Config
+	scheme     string
 }
 }

+ 24 - 15
api/client/commands.go

@@ -1491,7 +1491,8 @@ func (cli *DockerCli) CmdCommit(args ...string) error {
 
 
 func (cli *DockerCli) CmdEvents(args ...string) error {
 func (cli *DockerCli) CmdEvents(args ...string) error {
 	cmd := cli.Subcmd("events", "[OPTIONS]", "Get real time events from the server")
 	cmd := cli.Subcmd("events", "[OPTIONS]", "Get real time events from the server")
-	since := cmd.String([]string{"#since", "-since"}, "", "Show previously created events and then stream.")
+	since := cmd.String([]string{"#since", "-since"}, "", "Show all events created since timestamp")
+	until := cmd.String([]string{"-until"}, "", "Stream events until this timestamp")
 	if err := cmd.Parse(args); err != nil {
 	if err := cmd.Parse(args); err != nil {
 		return nil
 		return nil
 	}
 	}
@@ -1500,22 +1501,27 @@ func (cli *DockerCli) CmdEvents(args ...string) error {
 		cmd.Usage()
 		cmd.Usage()
 		return nil
 		return nil
 	}
 	}
-
-	v := url.Values{}
-	if *since != "" {
-		loc := time.FixedZone(time.Now().Zone())
+	var (
+		v   = url.Values{}
+		loc = time.FixedZone(time.Now().Zone())
+	)
+	var setTime = func(key, value string) {
 		format := "2006-01-02 15:04:05 -0700 MST"
 		format := "2006-01-02 15:04:05 -0700 MST"
-		if len(*since) < len(format) {
-			format = format[:len(*since)]
+		if len(value) < len(format) {
+			format = format[:len(value)]
 		}
 		}
-
-		if t, err := time.ParseInLocation(format, *since, loc); err == nil {
-			v.Set("since", strconv.FormatInt(t.Unix(), 10))
+		if t, err := time.ParseInLocation(format, value, loc); err == nil {
+			v.Set(key, strconv.FormatInt(t.Unix(), 10))
 		} else {
 		} else {
-			v.Set("since", *since)
+			v.Set(key, value)
 		}
 		}
 	}
 	}
-
+	if *since != "" {
+		setTime("since", *since)
+	}
+	if *until != "" {
+		setTime("until", *until)
+	}
 	if err := cli.stream("GET", "/events?"+v.Encode(), nil, cli.out, nil); err != nil {
 	if err := cli.stream("GET", "/events?"+v.Encode(), nil, cli.out, nil); err != nil {
 		return err
 		return err
 	}
 	}
@@ -1577,6 +1583,7 @@ func (cli *DockerCli) CmdDiff(args ...string) error {
 func (cli *DockerCli) CmdLogs(args ...string) error {
 func (cli *DockerCli) CmdLogs(args ...string) error {
 	cmd := cli.Subcmd("logs", "CONTAINER", "Fetch the logs of a container")
 	cmd := cli.Subcmd("logs", "CONTAINER", "Fetch the logs of a container")
 	follow := cmd.Bool([]string{"f", "-follow"}, false, "Follow log output")
 	follow := cmd.Bool([]string{"f", "-follow"}, false, "Follow log output")
+	times := cmd.Bool([]string{"t", "-timestamps"}, false, "Show timestamps")
 	if err := cmd.Parse(args); err != nil {
 	if err := cmd.Parse(args); err != nil {
 		return nil
 		return nil
 	}
 	}
@@ -1597,14 +1604,16 @@ func (cli *DockerCli) CmdLogs(args ...string) error {
 	}
 	}
 
 
 	v := url.Values{}
 	v := url.Values{}
-	v.Set("logs", "1")
 	v.Set("stdout", "1")
 	v.Set("stdout", "1")
 	v.Set("stderr", "1")
 	v.Set("stderr", "1")
+	if *times {
+		v.Set("timestamps", "1")
+	}
 	if *follow && container.State.Running {
 	if *follow && container.State.Running {
-		v.Set("stream", "1")
+		v.Set("follow", "1")
 	}
 	}
 
 
-	if err := cli.hijack("POST", "/containers/"+name+"/attach?"+v.Encode(), container.Config.Tty, nil, cli.out, cli.err, nil); err != nil {
+	if err := cli.streamHelper("GET", "/containers/"+name+"/logs?"+v.Encode(), container.Config.Tty, nil, cli.out, cli.err, nil); err != nil {
 		return err
 		return err
 	}
 	}
 	return nil
 	return nil

+ 133 - 0
api/client/hijack.go

@@ -0,0 +1,133 @@
+package client
+
+import (
+	"crypto/tls"
+	"fmt"
+	"io"
+	"net"
+	"net/http"
+	"net/http/httputil"
+	"os"
+	"runtime"
+	"strings"
+
+	"github.com/dotcloud/docker/api"
+	"github.com/dotcloud/docker/dockerversion"
+	"github.com/dotcloud/docker/pkg/term"
+	"github.com/dotcloud/docker/utils"
+)
+
+func (cli *DockerCli) dial() (net.Conn, error) {
+	if cli.tlsConfig != nil && cli.proto != "unix" {
+		return tls.Dial(cli.proto, cli.addr, cli.tlsConfig)
+	}
+	return net.Dial(cli.proto, cli.addr)
+}
+
+func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.ReadCloser, stdout, stderr io.Writer, started chan io.Closer) error {
+	defer func() {
+		if started != nil {
+			close(started)
+		}
+	}()
+
+	req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", api.APIVERSION, path), nil)
+	if err != nil {
+		return err
+	}
+	req.Header.Set("User-Agent", "Docker-Client/"+dockerversion.VERSION)
+	req.Header.Set("Content-Type", "plain/text")
+	req.Host = cli.addr
+
+	dial, err := cli.dial()
+	if err != nil {
+		if strings.Contains(err.Error(), "connection refused") {
+			return fmt.Errorf("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?")
+		}
+		return err
+	}
+	clientconn := httputil.NewClientConn(dial, nil)
+	defer clientconn.Close()
+
+	// Server hijacks the connection, error 'connection closed' expected
+	clientconn.Do(req)
+
+	rwc, br := clientconn.Hijack()
+	defer rwc.Close()
+
+	if started != nil {
+		started <- rwc
+	}
+
+	var receiveStdout chan error
+
+	var oldState *term.State
+
+	if in != nil && setRawTerminal && cli.isTerminal && os.Getenv("NORAW") == "" {
+		oldState, err = term.SetRawTerminal(cli.terminalFd)
+		if err != nil {
+			return err
+		}
+		defer term.RestoreTerminal(cli.terminalFd, oldState)
+	}
+
+	if stdout != nil || stderr != nil {
+		receiveStdout = utils.Go(func() (err error) {
+			defer func() {
+				if in != nil {
+					if setRawTerminal && cli.isTerminal {
+						term.RestoreTerminal(cli.terminalFd, oldState)
+					}
+					// For some reason this Close call blocks on darwin..
+					// As the client exists right after, simply discard the close
+					// until we find a better solution.
+					if runtime.GOOS != "darwin" {
+						in.Close()
+					}
+				}
+			}()
+
+			// When TTY is ON, use regular copy
+			if setRawTerminal {
+				_, err = io.Copy(stdout, br)
+			} else {
+				_, err = utils.StdCopy(stdout, stderr, br)
+			}
+			utils.Debugf("[hijack] End of stdout")
+			return err
+		})
+	}
+
+	sendStdin := utils.Go(func() error {
+		if in != nil {
+			io.Copy(rwc, in)
+			utils.Debugf("[hijack] End of stdin")
+		}
+		if tcpc, ok := rwc.(*net.TCPConn); ok {
+			if err := tcpc.CloseWrite(); err != nil {
+				utils.Debugf("Couldn't send EOF: %s\n", err)
+			}
+		} else if unixc, ok := rwc.(*net.UnixConn); ok {
+			if err := unixc.CloseWrite(); err != nil {
+				utils.Debugf("Couldn't send EOF: %s\n", err)
+			}
+		}
+		// Discard errors due to pipe interruption
+		return nil
+	})
+
+	if stdout != nil || stderr != nil {
+		if err := <-receiveStdout; err != nil {
+			utils.Debugf("Error receiveStdout: %s", err)
+			return err
+		}
+	}
+
+	if !cli.isTerminal {
+		if err := <-sendStdin; err != nil {
+			utils.Debugf("Error sendStdin: %s", err)
+			return err
+		}
+	}
+	return nil
+}

+ 27 - 160
api/client/utils.go

@@ -2,7 +2,6 @@ package client
 
 
 import (
 import (
 	"bytes"
 	"bytes"
-	"crypto/tls"
 	"encoding/base64"
 	"encoding/base64"
 	"encoding/json"
 	"encoding/json"
 	"errors"
 	"errors"
@@ -11,12 +10,9 @@ import (
 	"io/ioutil"
 	"io/ioutil"
 	"net"
 	"net"
 	"net/http"
 	"net/http"
-	"net/http/httputil"
 	"net/url"
 	"net/url"
 	"os"
 	"os"
 	gosignal "os/signal"
 	gosignal "os/signal"
-	"regexp"
-	goruntime "runtime"
 	"strconv"
 	"strconv"
 	"strings"
 	"strings"
 	"syscall"
 	"syscall"
@@ -33,11 +29,14 @@ var (
 	ErrConnectionRefused = errors.New("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?")
 	ErrConnectionRefused = errors.New("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?")
 )
 )
 
 
-func (cli *DockerCli) dial() (net.Conn, error) {
-	if cli.tlsConfig != nil && cli.proto != "unix" {
-		return tls.Dial(cli.proto, cli.addr, cli.tlsConfig)
+func (cli *DockerCli) HTTPClient() *http.Client {
+	tr := &http.Transport{
+		TLSClientConfig: cli.tlsConfig,
+		Dial: func(network, addr string) (net.Conn, error) {
+			return net.Dial(cli.proto, cli.addr)
+		},
 	}
 	}
-	return net.Dial(cli.proto, cli.addr)
+	return &http.Client{Transport: tr}
 }
 }
 
 
 func (cli *DockerCli) call(method, path string, data interface{}, passAuthInfo bool) (io.ReadCloser, int, error) {
 func (cli *DockerCli) call(method, path string, data interface{}, passAuthInfo bool) (io.ReadCloser, int, error) {
@@ -57,9 +56,6 @@ func (cli *DockerCli) call(method, path string, data interface{}, passAuthInfo b
 			}
 			}
 		}
 		}
 	}
 	}
-	// fixme: refactor client to support redirect
-	re := regexp.MustCompile("/+")
-	path = re.ReplaceAllString(path, "/")
 
 
 	req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", api.APIVERSION, path), params)
 	req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", api.APIVERSION, path), params)
 	if err != nil {
 	if err != nil {
@@ -86,28 +82,20 @@ func (cli *DockerCli) call(method, path string, data interface{}, passAuthInfo b
 		}
 		}
 	}
 	}
 	req.Header.Set("User-Agent", "Docker-Client/"+dockerversion.VERSION)
 	req.Header.Set("User-Agent", "Docker-Client/"+dockerversion.VERSION)
-	req.Host = cli.addr
+	req.URL.Host = cli.addr
+	req.URL.Scheme = cli.scheme
 	if data != nil {
 	if data != nil {
 		req.Header.Set("Content-Type", "application/json")
 		req.Header.Set("Content-Type", "application/json")
 	} else if method == "POST" {
 	} else if method == "POST" {
 		req.Header.Set("Content-Type", "plain/text")
 		req.Header.Set("Content-Type", "plain/text")
 	}
 	}
-	dial, err := cli.dial()
+	resp, err := cli.HTTPClient().Do(req)
 	if err != nil {
 	if err != nil {
 		if strings.Contains(err.Error(), "connection refused") {
 		if strings.Contains(err.Error(), "connection refused") {
 			return nil, -1, ErrConnectionRefused
 			return nil, -1, ErrConnectionRefused
 		}
 		}
 		return nil, -1, err
 		return nil, -1, err
 	}
 	}
-	clientconn := httputil.NewClientConn(dial, nil)
-	resp, err := clientconn.Do(req)
-	if err != nil {
-		clientconn.Close()
-		if strings.Contains(err.Error(), "connection refused") {
-			return nil, -1, ErrConnectionRefused
-		}
-		return nil, -1, err
-	}
 
 
 	if resp.StatusCode < 200 || resp.StatusCode >= 400 {
 	if resp.StatusCode < 200 || resp.StatusCode >= 400 {
 		body, err := ioutil.ReadAll(resp.Body)
 		body, err := ioutil.ReadAll(resp.Body)
@@ -119,31 +107,25 @@ func (cli *DockerCli) call(method, path string, data interface{}, passAuthInfo b
 		}
 		}
 		return nil, resp.StatusCode, fmt.Errorf("Error: %s", bytes.TrimSpace(body))
 		return nil, resp.StatusCode, fmt.Errorf("Error: %s", bytes.TrimSpace(body))
 	}
 	}
-
-	wrapper := utils.NewReadCloserWrapper(resp.Body, func() error {
-		if resp != nil && resp.Body != nil {
-			resp.Body.Close()
-		}
-		return clientconn.Close()
-	})
-	return wrapper, resp.StatusCode, nil
+	return resp.Body, resp.StatusCode, nil
 }
 }
 
 
 func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer, headers map[string][]string) error {
 func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer, headers map[string][]string) error {
+	return cli.streamHelper(method, path, true, in, out, nil, headers)
+}
+
+func (cli *DockerCli) streamHelper(method, path string, setRawTerminal bool, in io.Reader, stdout, stderr io.Writer, headers map[string][]string) error {
 	if (method == "POST" || method == "PUT") && in == nil {
 	if (method == "POST" || method == "PUT") && in == nil {
 		in = bytes.NewReader([]byte{})
 		in = bytes.NewReader([]byte{})
 	}
 	}
 
 
-	// fixme: refactor client to support redirect
-	re := regexp.MustCompile("/+")
-	path = re.ReplaceAllString(path, "/")
-
-	req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", api.APIVERSION, path), in)
+	req, err := http.NewRequest(method, fmt.Sprintf("http://v%s%s", api.APIVERSION, path), in)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 	req.Header.Set("User-Agent", "Docker-Client/"+dockerversion.VERSION)
 	req.Header.Set("User-Agent", "Docker-Client/"+dockerversion.VERSION)
-	req.Host = cli.addr
+	req.URL.Host = cli.addr
+	req.URL.Scheme = cli.scheme
 	if method == "POST" {
 	if method == "POST" {
 		req.Header.Set("Content-Type", "plain/text")
 		req.Header.Set("Content-Type", "plain/text")
 	}
 	}
@@ -153,17 +135,7 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer, h
 			req.Header[k] = v
 			req.Header[k] = v
 		}
 		}
 	}
 	}
-
-	dial, err := cli.dial()
-	if err != nil {
-		if strings.Contains(err.Error(), "connection refused") {
-			return fmt.Errorf("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?")
-		}
-		return err
-	}
-	clientconn := httputil.NewClientConn(dial, nil)
-	resp, err := clientconn.Do(req)
-	defer clientconn.Close()
+	resp, err := cli.HTTPClient().Do(req)
 	if err != nil {
 	if err != nil {
 		if strings.Contains(err.Error(), "connection refused") {
 		if strings.Contains(err.Error(), "connection refused") {
 			return fmt.Errorf("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?")
 			return fmt.Errorf("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?")
@@ -184,124 +156,19 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer, h
 	}
 	}
 
 
 	if api.MatchesContentType(resp.Header.Get("Content-Type"), "application/json") {
 	if api.MatchesContentType(resp.Header.Get("Content-Type"), "application/json") {
-		return utils.DisplayJSONMessagesStream(resp.Body, out, cli.terminalFd, cli.isTerminal)
+		return utils.DisplayJSONMessagesStream(resp.Body, stdout, cli.terminalFd, cli.isTerminal)
 	}
 	}
-	if _, err := io.Copy(out, resp.Body); err != nil {
-		return err
-	}
-	return nil
-}
-
-func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.ReadCloser, stdout, stderr io.Writer, started chan io.Closer) error {
-	defer func() {
-		if started != nil {
-			close(started)
-		}
-	}()
-	// fixme: refactor client to support redirect
-	re := regexp.MustCompile("/+")
-	path = re.ReplaceAllString(path, "/")
-
-	req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", api.APIVERSION, path), nil)
-	if err != nil {
-		return err
-	}
-	req.Header.Set("User-Agent", "Docker-Client/"+dockerversion.VERSION)
-	req.Header.Set("Content-Type", "plain/text")
-	req.Host = cli.addr
-
-	dial, err := cli.dial()
-	if err != nil {
-		if strings.Contains(err.Error(), "connection refused") {
-			return fmt.Errorf("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?")
-		}
-		return err
-	}
-	clientconn := httputil.NewClientConn(dial, nil)
-	defer clientconn.Close()
-
-	// Server hijacks the connection, error 'connection closed' expected
-	clientconn.Do(req)
-
-	rwc, br := clientconn.Hijack()
-	defer rwc.Close()
-
-	if started != nil {
-		started <- rwc
-	}
-
-	var receiveStdout chan error
-
-	var oldState *term.State
-
-	if in != nil && setRawTerminal && cli.isTerminal && os.Getenv("NORAW") == "" {
-		oldState, err = term.SetRawTerminal(cli.terminalFd)
-		if err != nil {
-			return err
-		}
-		defer term.RestoreTerminal(cli.terminalFd, oldState)
-	}
-
-	if stdout != nil || stderr != nil {
-		receiveStdout = utils.Go(func() (err error) {
-			defer func() {
-				if in != nil {
-					if setRawTerminal && cli.isTerminal {
-						term.RestoreTerminal(cli.terminalFd, oldState)
-					}
-					// For some reason this Close call blocks on darwin..
-					// As the client exists right after, simply discard the close
-					// until we find a better solution.
-					if goruntime.GOOS != "darwin" {
-						in.Close()
-					}
-				}
-			}()
-
-			// When TTY is ON, use regular copy
-			if setRawTerminal {
-				_, err = io.Copy(stdout, br)
-			} else {
-				_, err = utils.StdCopy(stdout, stderr, br)
-			}
-			utils.Debugf("[hijack] End of stdout")
-			return err
-		})
-	}
-
-	sendStdin := utils.Go(func() error {
-		if in != nil {
-			io.Copy(rwc, in)
-			utils.Debugf("[hijack] End of stdin")
-		}
-		if tcpc, ok := rwc.(*net.TCPConn); ok {
-			if err := tcpc.CloseWrite(); err != nil {
-				utils.Debugf("Couldn't send EOF: %s\n", err)
-			}
-		} else if unixc, ok := rwc.(*net.UnixConn); ok {
-			if err := unixc.CloseWrite(); err != nil {
-				utils.Debugf("Couldn't send EOF: %s\n", err)
-			}
-		}
-		// Discard errors due to pipe interruption
-		return nil
-	})
-
 	if stdout != nil || stderr != nil {
 	if stdout != nil || stderr != nil {
-		if err := <-receiveStdout; err != nil {
-			utils.Debugf("Error receiveStdout: %s", err)
-			return err
-		}
-	}
-
-	if !cli.isTerminal {
-		if err := <-sendStdin; err != nil {
-			utils.Debugf("Error sendStdin: %s", err)
-			return err
+		// When TTY is ON, use regular copy
+		if setRawTerminal {
+			_, err = io.Copy(stdout, resp.Body)
+		} else {
+			_, err = utils.StdCopy(stdout, stderr, resp.Body)
 		}
 		}
+		utils.Debugf("[stream] End of stdout")
+		return err
 	}
 	}
 	return nil
 	return nil
-
 }
 }
 
 
 func (cli *DockerCli) resizeTty(id string) {
 func (cli *DockerCli) resizeTty(id string) {

+ 1 - 1
api/common.go

@@ -10,7 +10,7 @@ import (
 )
 )
 
 
 const (
 const (
-	APIVERSION        version.Version = "1.10"
+	APIVERSION        version.Version = "1.11"
 	DEFAULTHTTPHOST                   = "127.0.0.1"
 	DEFAULTHTTPHOST                   = "127.0.0.1"
 	DEFAULTUNIXSOCKET                 = "/var/run/docker.sock"
 	DEFAULTUNIXSOCKET                 = "/var/run/docker.sock"
 )
 )

+ 65 - 5
api/server/server.go

@@ -3,7 +3,6 @@ package server
 import (
 import (
 	"bufio"
 	"bufio"
 	"bytes"
 	"bytes"
-	"code.google.com/p/go.net/websocket"
 	"crypto/tls"
 	"crypto/tls"
 	"crypto/x509"
 	"crypto/x509"
 	"encoding/base64"
 	"encoding/base64"
@@ -21,6 +20,8 @@ import (
 	"strings"
 	"strings"
 	"syscall"
 	"syscall"
 
 
+	"code.google.com/p/go.net/websocket"
+
 	"github.com/dotcloud/docker/api"
 	"github.com/dotcloud/docker/api"
 	"github.com/dotcloud/docker/engine"
 	"github.com/dotcloud/docker/engine"
 	"github.com/dotcloud/docker/pkg/listenbuffer"
 	"github.com/dotcloud/docker/pkg/listenbuffer"
@@ -246,6 +247,7 @@ func getEvents(eng *engine.Engine, version version.Version, w http.ResponseWrite
 	var job = eng.Job("events", r.RemoteAddr)
 	var job = eng.Job("events", r.RemoteAddr)
 	streamJSON(job, w, true)
 	streamJSON(job, w, true)
 	job.Setenv("since", r.Form.Get("since"))
 	job.Setenv("since", r.Form.Get("since"))
+	job.Setenv("until", r.Form.Get("until"))
 	return job.Run()
 	return job.Run()
 }
 }
 
 
@@ -327,6 +329,48 @@ func getContainersJSON(eng *engine.Engine, version version.Version, w http.Respo
 	return nil
 	return nil
 }
 }
 
 
+func getContainersLogs(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+	if err := parseForm(r); err != nil {
+		return err
+	}
+	if vars == nil {
+		return fmt.Errorf("Missing parameter")
+	}
+
+	var (
+		job    = eng.Job("inspect", vars["name"], "container")
+		c, err = job.Stdout.AddEnv()
+	)
+	if err != nil {
+		return err
+	}
+	if err = job.Run(); err != nil {
+		return err
+	}
+
+	var outStream, errStream io.Writer
+	outStream = utils.NewWriteFlusher(w)
+
+	if c.GetSubEnv("Config") != nil && !c.GetSubEnv("Config").GetBool("Tty") && version.GreaterThanOrEqualTo("1.6") {
+		errStream = utils.NewStdWriter(outStream, utils.Stderr)
+		outStream = utils.NewStdWriter(outStream, utils.Stdout)
+	} else {
+		errStream = outStream
+	}
+
+	job = eng.Job("logs", vars["name"])
+	job.Setenv("follow", r.Form.Get("follow"))
+	job.Setenv("stdout", r.Form.Get("stdout"))
+	job.Setenv("stderr", r.Form.Get("stderr"))
+	job.Setenv("timestamps", r.Form.Get("timestamps"))
+	job.Stdout.Add(outStream)
+	job.Stderr.Set(errStream)
+	if err := job.Run(); err != nil {
+		fmt.Fprintf(outStream, "Error: %s\n", err)
+	}
+	return nil
+}
+
 func postImagesTag(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 func postImagesTag(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if err := parseForm(r); err != nil {
 	if err := parseForm(r); err != nil {
 		return err
 		return err
@@ -828,8 +872,6 @@ func postBuild(eng *engine.Engine, version version.Version, w http.ResponseWrite
 		return fmt.Errorf("Multipart upload for build is no longer supported. Please upgrade your docker client.")
 		return fmt.Errorf("Multipart upload for build is no longer supported. Please upgrade your docker client.")
 	}
 	}
 	var (
 	var (
-		authEncoded       = r.Header.Get("X-Registry-Auth")
-		authConfig        = &registry.AuthConfig{}
 		configFileEncoded = r.Header.Get("X-Registry-Config")
 		configFileEncoded = r.Header.Get("X-Registry-Config")
 		configFile        = &registry.ConfigFile{}
 		configFile        = &registry.ConfigFile{}
 		job               = eng.Job("build")
 		job               = eng.Job("build")
@@ -839,12 +881,18 @@ func postBuild(eng *engine.Engine, version version.Version, w http.ResponseWrite
 	// Both headers will be parsed and sent along to the daemon, but if a non-empty
 	// Both headers will be parsed and sent along to the daemon, but if a non-empty
 	// ConfigFile is present, any value provided as an AuthConfig directly will
 	// ConfigFile is present, any value provided as an AuthConfig directly will
 	// be overridden. See BuildFile::CmdFrom for details.
 	// be overridden. See BuildFile::CmdFrom for details.
+	var (
+		authEncoded = r.Header.Get("X-Registry-Auth")
+		authConfig  = &registry.AuthConfig{}
+	)
 	if version.LessThan("1.9") && authEncoded != "" {
 	if version.LessThan("1.9") && authEncoded != "" {
 		authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
 		authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
 		if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
 		if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
 			// for a pull it is not an error if no auth was given
 			// for a pull it is not an error if no auth was given
 			// to increase compatibility with the existing api it is defaulting to be empty
 			// to increase compatibility with the existing api it is defaulting to be empty
 			authConfig = &registry.AuthConfig{}
 			authConfig = &registry.AuthConfig{}
+		} else {
+			configFile.Configs[authConfig.ServerAddress] = *authConfig
 		}
 		}
 	}
 	}
 
 
@@ -869,8 +917,7 @@ func postBuild(eng *engine.Engine, version version.Version, w http.ResponseWrite
 	job.Setenv("q", r.FormValue("q"))
 	job.Setenv("q", r.FormValue("q"))
 	job.Setenv("nocache", r.FormValue("nocache"))
 	job.Setenv("nocache", r.FormValue("nocache"))
 	job.Setenv("rm", r.FormValue("rm"))
 	job.Setenv("rm", r.FormValue("rm"))
-	job.SetenvJson("authConfig", authConfig)
-	job.SetenvJson("configFile", configFile)
+	job.SetenvJson("auth", configFile)
 
 
 	if err := job.Run(); err != nil {
 	if err := job.Run(); err != nil {
 		if !job.Stdout.Used() {
 		if !job.Stdout.Used() {
@@ -930,6 +977,11 @@ func writeCorsHeaders(w http.ResponseWriter, r *http.Request) {
 	w.Header().Add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS")
 	w.Header().Add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS")
 }
 }
 
 
+func ping(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+	_, err := w.Write([]byte{'O', 'K'})
+	return err
+}
+
 func makeHttpHandler(eng *engine.Engine, logging bool, localMethod string, localRoute string, handlerFunc HttpApiFunc, enableCors bool, dockerVersion version.Version) http.HandlerFunc {
 func makeHttpHandler(eng *engine.Engine, logging bool, localMethod string, localRoute string, handlerFunc HttpApiFunc, enableCors bool, dockerVersion version.Version) http.HandlerFunc {
 	return func(w http.ResponseWriter, r *http.Request) {
 	return func(w http.ResponseWriter, r *http.Request) {
 		// log the request
 		// log the request
@@ -998,6 +1050,7 @@ func createRouter(eng *engine.Engine, logging, enableCors bool, dockerVersion st
 	}
 	}
 	m := map[string]map[string]HttpApiFunc{
 	m := map[string]map[string]HttpApiFunc{
 		"GET": {
 		"GET": {
+			"/_ping":                          ping,
 			"/events":                         getEvents,
 			"/events":                         getEvents,
 			"/info":                           getInfo,
 			"/info":                           getInfo,
 			"/version":                        getVersion,
 			"/version":                        getVersion,
@@ -1013,6 +1066,7 @@ func createRouter(eng *engine.Engine, logging, enableCors bool, dockerVersion st
 			"/containers/{name:.*}/changes":   getContainersChanges,
 			"/containers/{name:.*}/changes":   getContainersChanges,
 			"/containers/{name:.*}/json":      getContainersByName,
 			"/containers/{name:.*}/json":      getContainersByName,
 			"/containers/{name:.*}/top":       getContainersTop,
 			"/containers/{name:.*}/top":       getContainersTop,
+			"/containers/{name:.*}/logs":      getContainersLogs,
 			"/containers/{name:.*}/attach/ws": wsContainersAttach,
 			"/containers/{name:.*}/attach/ws": wsContainersAttach,
 		},
 		},
 		"POST": {
 		"POST": {
@@ -1220,6 +1274,9 @@ func ListenAndServe(proto, addr string, job *engine.Job) error {
 // ServeApi loops through all of the protocols sent in to docker and spawns
 // ServeApi loops through all of the protocols sent in to docker and spawns
 // off a go routine to setup a serving http.Server for each.
 // off a go routine to setup a serving http.Server for each.
 func ServeApi(job *engine.Job) engine.Status {
 func ServeApi(job *engine.Job) engine.Status {
+	if len(job.Args) == 0 {
+		return job.Errorf("usage: %s PROTO://ADDR [PROTO://ADDR ...]", job.Name)
+	}
 	var (
 	var (
 		protoAddrs = job.Args
 		protoAddrs = job.Args
 		chErrors   = make(chan error, len(protoAddrs))
 		chErrors   = make(chan error, len(protoAddrs))
@@ -1232,6 +1289,9 @@ func ServeApi(job *engine.Job) engine.Status {
 
 
 	for _, protoAddr := range protoAddrs {
 	for _, protoAddr := range protoAddrs {
 		protoAddrParts := strings.SplitN(protoAddr, "://", 2)
 		protoAddrParts := strings.SplitN(protoAddr, "://", 2)
+		if len(protoAddrParts) != 2 {
+			return job.Errorf("usage: %s PROTO://ADDR [PROTO://ADDR ...]", job.Name)
+		}
 		go func() {
 		go func() {
 			log.Printf("Listening for HTTP on %s (%s)\n", protoAddrParts[0], protoAddrParts[1])
 			log.Printf("Listening for HTTP on %s (%s)\n", protoAddrParts[0], protoAddrParts[1])
 			chErrors <- ListenAndServe(protoAddrParts[0], protoAddrParts[1], job)
 			chErrors <- ListenAndServe(protoAddrParts[0], protoAddrParts[1], job)

+ 40 - 72
api/server/server_unit_test.go

@@ -1,14 +1,14 @@
 package server
 package server
 
 
 import (
 import (
+	"bytes"
+	"encoding/json"
 	"fmt"
 	"fmt"
 	"github.com/dotcloud/docker/api"
 	"github.com/dotcloud/docker/api"
 	"github.com/dotcloud/docker/engine"
 	"github.com/dotcloud/docker/engine"
-	"github.com/dotcloud/docker/utils"
 	"io"
 	"io"
 	"net/http"
 	"net/http"
 	"net/http/httptest"
 	"net/http/httptest"
-	"os"
 	"testing"
 	"testing"
 )
 )
 
 
@@ -57,15 +57,7 @@ func TesthttpError(t *testing.T) {
 }
 }
 
 
 func TestGetVersion(t *testing.T) {
 func TestGetVersion(t *testing.T) {
-	tmp, err := utils.TestDirectory("")
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer os.RemoveAll(tmp)
-	eng, err := engine.New(tmp)
-	if err != nil {
-		t.Fatal(err)
-	}
+	eng := engine.New()
 	var called bool
 	var called bool
 	eng.Register("version", func(job *engine.Job) engine.Status {
 	eng.Register("version", func(job *engine.Job) engine.Status {
 		called = true
 		called = true
@@ -80,49 +72,21 @@ func TestGetVersion(t *testing.T) {
 		}
 		}
 		return engine.StatusOK
 		return engine.StatusOK
 	})
 	})
-
-	r := httptest.NewRecorder()
-	req, err := http.NewRequest("GET", "/version", nil)
-	if err != nil {
-		t.Fatal(err)
-	}
-	// FIXME getting the version should require an actual running Server
-	if err := ServeRequest(eng, api.APIVERSION, r, req); err != nil {
-		t.Fatal(err)
-	}
+	r := serveRequest("GET", "/version", nil, eng, t)
 	if !called {
 	if !called {
 		t.Fatalf("handler was not called")
 		t.Fatalf("handler was not called")
 	}
 	}
-	out := engine.NewOutput()
-	v, err := out.AddEnv()
-	if err != nil {
-		t.Fatal(err)
-	}
-	if _, err := io.Copy(out, r.Body); err != nil {
-		t.Fatal(err)
-	}
-	out.Close()
-	expected := "42.1"
-	if result := v.Get("Version"); result != expected {
-		t.Errorf("Expected version %s, %s found", expected, result)
+	v := readEnv(r.Body, t)
+	if v.Get("Version") != "42.1" {
+		t.Fatalf("%#v\n", v)
 	}
 	}
-	expected = "application/json"
-	if result := r.HeaderMap.Get("Content-Type"); result != expected {
-		t.Errorf("Expected Content-Type %s, %s found", expected, result)
+	if r.HeaderMap.Get("Content-Type") != "application/json" {
+		t.Fatalf("%#v\n", r)
 	}
 	}
 }
 }
 
 
 func TestGetInfo(t *testing.T) {
 func TestGetInfo(t *testing.T) {
-	tmp, err := utils.TestDirectory("")
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer os.RemoveAll(tmp)
-	eng, err := engine.New(tmp)
-	if err != nil {
-		t.Fatal(err)
-	}
-
+	eng := engine.New()
 	var called bool
 	var called bool
 	eng.Register("info", func(job *engine.Job) engine.Status {
 	eng.Register("info", func(job *engine.Job) engine.Status {
 		called = true
 		called = true
@@ -134,47 +98,51 @@ func TestGetInfo(t *testing.T) {
 		}
 		}
 		return engine.StatusOK
 		return engine.StatusOK
 	})
 	})
+	r := serveRequest("GET", "/info", nil, eng, t)
+	if !called {
+		t.Fatalf("handler was not called")
+	}
+	v := readEnv(r.Body, t)
+	if v.GetInt("Images") != 42000 {
+		t.Fatalf("%#v\n", v)
+	}
+	if v.GetInt("Containers") != 1 {
+		t.Fatalf("%#v\n", v)
+	}
+	if r.HeaderMap.Get("Content-Type") != "application/json" {
+		t.Fatalf("%#v\n", r)
+	}
+}
 
 
+func serveRequest(method, target string, body io.Reader, eng *engine.Engine, t *testing.T) *httptest.ResponseRecorder {
 	r := httptest.NewRecorder()
 	r := httptest.NewRecorder()
-	req, err := http.NewRequest("GET", "/info", nil)
+	req, err := http.NewRequest(method, target, body)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
-	// FIXME getting the version should require an actual running Server
 	if err := ServeRequest(eng, api.APIVERSION, r, req); err != nil {
 	if err := ServeRequest(eng, api.APIVERSION, r, req); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
-	if !called {
-		t.Fatalf("handler was not called")
-	}
+	return r
+}
 
 
+func readEnv(src io.Reader, t *testing.T) *engine.Env {
 	out := engine.NewOutput()
 	out := engine.NewOutput()
-	i, err := out.AddEnv()
+	v, err := out.AddEnv()
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
-	if _, err := io.Copy(out, r.Body); err != nil {
+	if _, err := io.Copy(out, src); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	out.Close()
 	out.Close()
-	{
-		expected := 42000
-		result := i.GetInt("Images")
-		if expected != result {
-			t.Fatalf("%#v\n", result)
-		}
-	}
-	{
-		expected := 1
-		result := i.GetInt("Containers")
-		if expected != result {
-			t.Fatalf("%#v\n", result)
-		}
-	}
-	{
-		expected := "application/json"
-		if result := r.HeaderMap.Get("Content-Type"); result != expected {
-			t.Fatalf("%#v\n", result)
-		}
+	return v
+}
+
+func toJson(data interface{}, t *testing.T) io.Reader {
+	var buf bytes.Buffer
+	if err := json.NewEncoder(&buf).Encode(data); err != nil {
+		t.Fatal(err)
 	}
 	}
+	return &buf
 }
 }

+ 1 - 1
archive/diff.go

@@ -68,7 +68,7 @@ func ApplyLayer(dest string, layer ArchiveReader) error {
 			parent := filepath.Dir(hdr.Name)
 			parent := filepath.Dir(hdr.Name)
 			parentPath := filepath.Join(dest, parent)
 			parentPath := filepath.Join(dest, parent)
 			if _, err := os.Lstat(parentPath); err != nil && os.IsNotExist(err) {
 			if _, err := os.Lstat(parentPath); err != nil && os.IsNotExist(err) {
-				err = os.MkdirAll(parentPath, 600)
+				err = os.MkdirAll(parentPath, 0600)
 				if err != nil {
 				if err != nil {
 					return err
 					return err
 				}
 				}

+ 17 - 9
builtins/builtins.go

@@ -2,19 +2,25 @@ package builtins
 
 
 import (
 import (
 	api "github.com/dotcloud/docker/api/server"
 	api "github.com/dotcloud/docker/api/server"
+	"github.com/dotcloud/docker/daemon/networkdriver/bridge"
 	"github.com/dotcloud/docker/engine"
 	"github.com/dotcloud/docker/engine"
-	"github.com/dotcloud/docker/runtime/networkdriver/bridge"
+	"github.com/dotcloud/docker/registry"
 	"github.com/dotcloud/docker/server"
 	"github.com/dotcloud/docker/server"
 )
 )
 
 
-func Register(eng *engine.Engine) {
-	daemon(eng)
-	remote(eng)
+func Register(eng *engine.Engine) error {
+	if err := daemon(eng); err != nil {
+		return err
+	}
+	if err := remote(eng); err != nil {
+		return err
+	}
+	return registry.NewService().Install(eng)
 }
 }
 
 
 // remote: a RESTful api for cross-docker communication
 // remote: a RESTful api for cross-docker communication
-func remote(eng *engine.Engine) {
-	eng.Register("serveapi", api.ServeApi)
+func remote(eng *engine.Engine) error {
+	return eng.Register("serveapi", api.ServeApi)
 }
 }
 
 
 // daemon: a default execution and storage backend for Docker on Linux,
 // daemon: a default execution and storage backend for Docker on Linux,
@@ -32,7 +38,9 @@ func remote(eng *engine.Engine) {
 //
 //
 // These components should be broken off into plugins of their own.
 // These components should be broken off into plugins of their own.
 //
 //
-func daemon(eng *engine.Engine) {
-	eng.Register("initserver", server.InitServer)
-	eng.Register("init_networkdriver", bridge.InitDriver)
+func daemon(eng *engine.Engine) error {
+	if err := eng.Register("initserver", server.InitServer); err != nil {
+		return err
+	}
+	return eng.Register("init_networkdriver", bridge.InitDriver)
 }
 }

+ 18 - 11
contrib/check-config.sh

@@ -4,7 +4,13 @@ set -e
 # bits of this were adapted from lxc-checkconfig
 # bits of this were adapted from lxc-checkconfig
 # see also https://github.com/lxc/lxc/blob/lxc-1.0.2/src/lxc/lxc-checkconfig.in
 # see also https://github.com/lxc/lxc/blob/lxc-1.0.2/src/lxc/lxc-checkconfig.in
 
 
-: ${CONFIG:=/proc/config.gz}
+possibleConfigs=(
+	'/proc/config.gz'
+	"/boot/config-$(uname -r)"
+	"/usr/src/linux-$(uname -r)/.config"
+	'/usr/src/linux/.config'
+)
+: ${CONFIG:="${possibleConfigs[0]}"}
 
 
 if ! command -v zgrep &> /dev/null; then
 if ! command -v zgrep &> /dev/null; then
 	zgrep() {
 	zgrep() {
@@ -74,11 +80,7 @@ check_flags() {
 
 
 if [ ! -e "$CONFIG" ]; then
 if [ ! -e "$CONFIG" ]; then
 	wrap_warning "warning: $CONFIG does not exist, searching other paths for kernel config..."
 	wrap_warning "warning: $CONFIG does not exist, searching other paths for kernel config..."
-	for tryConfig in \
-		'/proc/config.gz' \
-		"/boot/config-$(uname -r)" \
-		'/usr/src/linux/.config' \
-	; do
+	for tryConfig in "${possibleConfigs[@]}"; do
 		if [ -e "$tryConfig" ]; then
 		if [ -e "$tryConfig" ]; then
 			CONFIG="$tryConfig"
 			CONFIG="$tryConfig"
 			break
 			break
@@ -98,12 +100,16 @@ echo
 echo 'Generally Necessary:'
 echo 'Generally Necessary:'
 
 
 echo -n '- '
 echo -n '- '
-cgroupCpuDir="$(awk '/[, ]cpu([, ]|$)/ && $8 == "cgroup" { print $5 }' /proc/$$/mountinfo | head -n1)"
-cgroupDir="$(dirname "$cgroupCpuDir")"
-if [ -d "$cgroupDir/cpu" ]; then
+cgroupSubsystemDir="$(awk '/[, ](cpu|cpuacct|cpuset|devices|freezer|memory)([, ]|$)/ && $8 == "cgroup" { print $5 }' /proc/$$/mountinfo | head -n1)"
+cgroupDir="$(dirname "$cgroupSubsystemDir")"
+if [ -d "$cgroupDir/cpu" -o -d "$cgroupDir/cpuacct" -o -d "$cgroupDir/cpuset" -o -d "$cgroupDir/devices" -o -d "$cgroupDir/freezer" -o -d "$cgroupDir/memory" ]; then
 	echo "$(wrap_good 'cgroup hierarchy' 'properly mounted') [$cgroupDir]"
 	echo "$(wrap_good 'cgroup hierarchy' 'properly mounted') [$cgroupDir]"
 else
 else
-	echo "$(wrap_bad 'cgroup hierarchy' 'single mountpoint!') [$cgroupCpuDir]"
+	if [ "$cgroupSubsystemDir" ]; then
+		echo "$(wrap_bad 'cgroup hierarchy' 'single mountpoint!') [$cgroupSubsystemDir]"
+	else
+		echo "$(wrap_bad 'cgroup hierarchy' 'nonexistent??')"
+	fi
 	echo "    $(wrap_color '(see https://github.com/tianon/cgroupfs-mount)' yellow)"
 	echo "    $(wrap_color '(see https://github.com/tianon/cgroupfs-mount)' yellow)"
 fi
 fi
 
 
@@ -112,7 +118,8 @@ flags=(
 	DEVPTS_MULTIPLE_INSTANCES
 	DEVPTS_MULTIPLE_INSTANCES
 	CGROUPS CGROUP_DEVICE
 	CGROUPS CGROUP_DEVICE
 	MACVLAN VETH BRIDGE
 	MACVLAN VETH BRIDGE
-	IP_NF_TARGET_MASQUERADE NETFILTER_XT_MATCH_{ADDRTYPE,CONNTRACK}
+	NF_NAT_IPV4 IP_NF_TARGET_MASQUERADE
+	NETFILTER_XT_MATCH_{ADDRTYPE,CONNTRACK}
 	NF_NAT NF_NAT_NEEDED
 	NF_NAT NF_NAT_NEEDED
 )
 )
 check_flags "${flags[@]}"
 check_flags "${flags[@]}"

+ 1 - 1
contrib/docker-device-tool/device_tool.go

@@ -3,7 +3,7 @@ package main
 import (
 import (
 	"flag"
 	"flag"
 	"fmt"
 	"fmt"
-	"github.com/dotcloud/docker/runtime/graphdriver/devmapper"
+	"github.com/dotcloud/docker/daemon/graphdriver/devmapper"
 	"os"
 	"os"
 	"path"
 	"path"
 	"sort"
 	"sort"

+ 2 - 0
contrib/man/.gitignore

@@ -0,0 +1,2 @@
+# these are generated by the md/md2man-all.sh script
+man*

+ 5 - 0
contrib/man/md/Dockerfile

@@ -0,0 +1,5 @@
+FROM fedora:20
+MAINTAINER ipbabble <emailwhenry@redhat.com>
+# Update and install pandoc
+RUN yum -y update; yum clean all;
+RUN yum -y install pandoc;

+ 71 - 0
contrib/man/md/README.md

@@ -0,0 +1,71 @@
+Docker Documentation
+====================
+
+This directory contains the Docker user manual in the Markdown format.
+Do *not* edit the man pages in the man1 directory. Instead, amend the
+Markdown (*.md) files.
+
+# File List
+
+    docker.md
+    docker-attach.md
+    docker-build.md
+    docker-commit.md
+    docker-cp.md
+    docker-diff.md
+    docker-events.md
+    docker-export.md
+    docker-history.md
+    docker-images.md
+    docker-import.md
+    docker-info.md
+    docker-inspect.md
+    docker-kill.md
+    docker-load.md
+    docker-login.md
+    docker-logs.md
+    docker-port.md
+    docker-ps.md
+    docker-pull.md
+    docker-push.md
+    docker-restart.md
+    docker-rmi.md
+    docker-rm.md
+    docker-run.md
+    docker-save.md
+    docker-search.md
+    docker-start.md
+    docker-stop.md
+    docker-tag.md
+    docker-top.md
+    docker-wait.md
+    Dockerfile
+    md2man-all.sh
+
+# Generating man pages from the Markdown files
+
+The recommended approach for generating the man pages is via a  Docker 
+container. Using the supplied Dockerfile, Docker will create a Fedora based 
+container and isolate the Pandoc installation. This is a seamless process, 
+saving you from dealing with Pandoc and dependencies on your own computer.
+
+## Building the Fedora / Pandoc image
+
+There is a Dockerfile provided in the `docker/contrib/man/md` directory.
+
+Using this Dockerfile, create a Docker image tagged `fedora/pandoc`:
+
+    docker build  -t fedora/pandoc .
+
+## Utilizing the Fedora / Pandoc image
+
+Once the image is built, run a container using the image with *volumes*:
+
+    docker run -v /<path-to-git-dir>/docker/contrib/man:/pandoc:rw \
+    -w /pandoc -i fedora/pandoc /pandoc/md/md2man-all.sh
+
+The Pandoc Docker container will process the Markdown files and generate
+the man pages inside the `docker/contrib/man/man1` directory using
+Docker volumes. For more information on Docker volumes see the man page for
+`docker run` and also look at the article [Sharing Directories via Volumes]
+(http://docs.docker.io/use/working_with_volumes/).

+ 57 - 0
contrib/man/md/docker-attach.1.md

@@ -0,0 +1,57 @@
+% DOCKER(1) Docker User Manuals
+% William Henry
+% APRIL 2014
+# NAME
+docker-attach - Attach to a running container
+
+# SYNOPSIS
+**docker attach** **--no-stdin**[=*false*] **--sig-proxy**[=*true*] CONTAINER
+
+# DESCRIPTION
+If you **docker run** a container in detached mode (**-d**), you can reattach to
+ the detached container with **docker attach** using the container's ID or name.
+
+You can detach from the container again (and leave it running) with `CTRL-c` (for
+a quiet exit) or `CTRL-\` to get a stacktrace of the Docker client when it quits.
+When you detach from a container the exit code will be returned to
+the client.
+
+# OPTIONS
+**--no-stdin**=*true*|*false*
+When set to true, do not attach to stdin. The default is *false*.
+
+**--sig-proxy**=*true*|*false*:
+When set to true, proxify all received signal to the process (even in non-tty
+mode). The default is *true*.
+
+# EXAMPLES
+
+## Attaching to a container
+
+In this example the top command is run inside a container, from an image called
+fedora, in detached mode. The ID from the container is passed into the **docker
+attach** command:
+
+    # ID=$(sudo docker run -d fedora /usr/bin/top -b)
+    # sudo docker attach $ID
+    top - 02:05:52 up  3:05,  0 users,  load average: 0.01, 0.02, 0.05
+    Tasks:   1 total,   1 running,   0 sleeping,   0 stopped,   0 zombie
+    Cpu(s):  0.1%us,  0.2%sy,  0.0%ni, 99.7%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
+    Mem:    373572k total,   355560k used,    18012k free,    27872k buffers
+    Swap:   786428k total,        0k used,   786428k free,   221740k cached
+
+    PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
+    1 root      20   0 17200 1116  912 R    0  0.3   0:00.03 top
+
+    top - 02:05:55 up  3:05,  0 users,  load average: 0.01, 0.02, 0.05
+    Tasks:   1 total,   1 running,   0 sleeping,   0 stopped,   0 zombie
+    Cpu(s):  0.0%us,  0.2%sy,  0.0%ni, 99.8%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
+    Mem:    373572k total,   355244k used,    18328k free,    27872k buffers
+    Swap:   786428k total,        0k used,   786428k free,   221776k cached
+
+    PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
+    1 root      20   0 17208 1144  932 R    0  0.3   0:00.03 top
+
+# HISTORY
+April 2014, Originally compiled by William Henry (whenry at redhat dot com)
+based on docker.io source material and internal work.

+ 82 - 0
contrib/man/md/docker-build.1.md

@@ -0,0 +1,82 @@
+% DOCKER(1) Docker User Manuals
+% William Henry
+% APRIL 2014
+# NAME
+docker-build - Build a container image from a Dockerfile source at PATH
+
+# SYNOPSIS
+**docker build** [**--no-cache**[=*false*]] [**-q**|**--quiet**[=*false*]]
+ [**--rm**] [**-t**|**--tag**=TAG] PATH | URL | -
+
+# DESCRIPTION
+This will read the Dockerfile from the directory specified in **PATH**.
+It also sends any other files and directories found in the current
+directory to the Docker daemon. The contents of this directory would
+be used by **ADD** commands found within the Dockerfile.
+
+Warning, this will send a lot of data to the Docker daemon depending
+on the contents of the current directory. The build is run by the Docker 
+daemon, not by the CLI, so the whole context must be transferred to the daemon. 
+The Docker CLI reports "Uploading context" when the context is sent to 
+the daemon.
+
+When a single Dockerfile is given as the URL, then no context is set.
+When a Git repository is set as the **URL**, the repository is used
+as context.
+
+# OPTIONS
+
+**-q**, **--quiet**=*true*|*false*
+   When set to true, suppress verbose build output. Default is *false*.
+
+**--rm**=*true*|*false*
+   When true, remove intermediate containers that are created during the
+build process. The default is true.
+
+**-t**, **--tag**=*tag*
+   Tag to be applied to the resulting image on successful completion of
+the build.
+
+**--no-cache**=*true*|*false*
+   When set to true, do not use a cache when building the image. The
+default is *false*.
+
+# EXAMPLES
+
+## Building an image using a Dockefile located inside the current directory
+
+Docker images can be built using the build command and a Dockerfile:
+
+    docker build .
+
+During the build process Docker creates intermediate images. In order to
+keep them, you must explicitly set `--rm=false`.
+
+    docker build --rm=false .
+
+A good practice is to make a sub-directory with a related name and create
+the Dockerfile in that directory. For example, a directory called mongo may
+contain a Dockerfile to create a Docker MongoDB image. Likewise, another
+directory called httpd may be used to store Dockerfiles for Apache web
+server images.
+
+It is also a good practice to add the files required for the image to the
+sub-directory. These files will then be specified with the `ADD` instruction
+in the Dockerfile. Note: If you include a tar file (a good practice!), then
+Docker will automatically extract the contents of the tar file
+specified within the `ADD` instruction into the specified target.
+
+## Building an image using a URL
+
+This will clone the specified Github repository from the URL and use it
+as context. The Dockerfile at the root of the repository is used as
+Dockerfile. This only works if the Github repository is a dedicated
+repository.
+
+    docker build github.com/scollier/Fedora-Dockerfiles/tree/master/apache
+
+Note: You can set an arbitrary Git repository via the `git://` schema.
+
+# HISTORY
+March 2014, Originally compiled by William Henry (whenry at redhat dot com)
+based on docker.io source material and internal work.

+ 34 - 0
contrib/man/md/docker-commit.1.md

@@ -0,0 +1,34 @@
+% DOCKER(1) Docker User Manuals
+% William Henry
+% APRIL 2014
+# NAME
+docker-commit - Create a new image from the changes to an existing
+container
+
+# SYNOPSIS
+**docker commit** **-a**|**--author**[=""] **-m**|**--message**[=""]
+CONTAINER [REPOSITORY[:TAG]]
+
+# DESCRIPTION
+Using an existing container's name or ID you can create a new image.
+
+# OPTIONS
+**-a, --author**=""
+   Author name. (eg. "John Hannibal Smith <hannibal@a-team.com>"
+
+**-m, --message**=""
+   Commit message
+
+# EXAMPLES
+
+## Creating a new image from an existing container
+An existing Fedora based container has had Apache installed while running
+in interactive mode with the bash shell. Apache is also running. To
+create a new image run docker ps to find the container's ID and then run:
+
+    # docker commit -me= "Added Apache to Fedora base image" \
+      --a="A D Ministrator" 98bd7fc99854 fedora/fedora_httpd:20
+
+# HISTORY
+April 2014, Originally compiled by William Henry (whenry at redhat dot com)
+based on docker.io source material and in

+ 24 - 0
contrib/man/md/docker-cp.1.md

@@ -0,0 +1,24 @@
+% DOCKER(1) Docker User Manuals
+% William Henry
+% APRIL 2014
+# NAME
+docker-cp - Copy files/folders from the PATH to the HOSTPATH
+
+# SYNOPSIS
+**docker cp** CONTAINER:PATH HOSTPATH
+
+# DESCRIPTION
+Copy files/folders from the containers filesystem to the host
+path. Paths are relative to the root of the filesystem. Files
+can be copied from a running or stopped container.
+
+# EXAMPLE
+An important shell script file, created in a bash shell, is copied from
+the exited container to the current dir on the host:
+
+    # docker cp c071f3c3ee81:setup.sh .
+
+# HISTORY
+April 2014, Originally compiled by William Henry (whenry at redhat dot com)
+based on docker.io source material and internal work.
+

+ 44 - 0
contrib/man/md/docker-diff.1.md

@@ -0,0 +1,44 @@
+% DOCKER(1) Docker User Manuals
+% William Henry
+% APRIL 2014
+# NAME
+docker-diff - Inspect changes on a container's filesystem
+
+# SYNOPSIS
+**docker diff** CONTAINER
+
+# DESCRIPTION
+Inspect changes on a container's filesystem. You can use the full or
+shortened container ID or the container name set using
+**docker run --name** option.
+
+# EXAMPLE
+Inspect the changes to on a nginx container:
+
+    # docker diff 1fdfd1f54c1b
+    C /dev
+    C /dev/console
+    C /dev/core
+    C /dev/stdout
+    C /dev/fd
+    C /dev/ptmx
+    C /dev/stderr
+    C /dev/stdin
+    C /run
+    A /run/nginx.pid
+    C /var/lib/nginx/tmp
+    A /var/lib/nginx/tmp/client_body
+    A /var/lib/nginx/tmp/fastcgi
+    A /var/lib/nginx/tmp/proxy
+    A /var/lib/nginx/tmp/scgi
+    A /var/lib/nginx/tmp/uwsgi
+    C /var/log/nginx
+    A /var/log/nginx/access.log
+    A /var/log/nginx/error.log
+
+
+# HISTORY
+April 2014, Originally compiled by William Henry (whenry at redhat dot com)
+based on docker.io source material and internal work.
+
+

+ 46 - 0
contrib/man/md/docker-events.1.md

@@ -0,0 +1,46 @@
+% DOCKER(1) Docker User Manuals
+% William Henry
+% APRIL 2014
+# NAME
+docker-events - Get real time events from the server
+
+**docker events** **--since**=""|*epoch-time*
+
+# DESCRIPTION
+Get event information from the Docker daemon. Information can include historical
+information and real-time information.
+
+# OPTIONS
+**--since**=""
+Show previously created events and then stream. This can be in either
+seconds since epoch, or date string.
+
+# EXAMPLES
+
+## Listening for Docker events
+
+After running docker events a container 786d698004576 is started and stopped
+(The container name has been shortened in the ouput below):
+
+    # docker events
+    [2014-04-12 18:23:04 -0400 EDT] 786d69800457: (from whenry/testimage:latest) start
+    [2014-04-12 18:23:13 -0400 EDT] 786d69800457: (from whenry/testimage:latest) die
+    [2014-04-12 18:23:13 -0400 EDT] 786d69800457: (from whenry/testimage:latest) stop
+
+## Listening for events since a given date
+Again the output container IDs have been shortened for the purposes of this document:
+
+    # docker events --since '2014-04-12'
+    [2014-04-12 18:11:28 -0400 EDT] c655dbf640dc: (from whenry/testimage:latest) create
+    [2014-04-12 18:11:28 -0400 EDT] c655dbf640dc: (from whenry/testimage:latest) start
+    [2014-04-12 18:14:13 -0400 EDT] 786d69800457: (from whenry/testimage:latest) create
+    [2014-04-12 18:14:13 -0400 EDT] 786d69800457: (from whenry/testimage:latest) start
+    [2014-04-12 18:22:44 -0400 EDT] 786d69800457: (from whenry/testimage:latest) die
+    [2014-04-12 18:22:44 -0400 EDT] 786d69800457: (from whenry/testimage:latest) stop
+    [2014-04-12 18:23:04 -0400 EDT] 786d69800457: (from whenry/testimage:latest) start
+    [2014-04-12 18:23:13 -0400 EDT] 786d69800457: (from whenry/testimage:latest) die
+    [2014-04-12 18:23:13 -0400 EDT] 786d69800457: (from whenry/testimage:latest) stop
+
+# HISTORY
+April 2014, Originally compiled by William Henry (whenry at redhat dot com)
+based on docker.io source material and internal work.

+ 26 - 0
contrib/man/md/docker-export.1.md

@@ -0,0 +1,26 @@
+% DOCKER(1) Docker User Manuals
+% William Henry
+% APRIL 2014
+# NAME
+docker-export - Export the contents of a filesystem as a tar archive to
+STDOUT.
+
+# SYNOPSIS
+**docker export** CONTAINER
+
+# DESCRIPTION
+Export the contents of a container's filesystem using the full or shortened
+container ID or container name. The output is exported to STDOUT and can be
+redirected to a tar file.
+
+# EXAMPLE
+Export the contents of the container called angry_bell to a tar file
+called test.tar:
+
+    # docker export angry_bell > test.tar
+    # ls *.tar
+    test.tar
+
+# HISTORY
+April 2014, Originally compiled by William Henry (whenry at redhat dot com)
+based on docker.io source material and internal work.

+ 32 - 0
contrib/man/md/docker-history.1.md

@@ -0,0 +1,32 @@
+% DOCKER(1) Docker User Manuals
+% William Henry
+% APRIL 2014
+# NAME
+docker-history - Show the history of an image
+
+# SYNOPSIS
+**docker history** **--no-trunc**[=*false*] [**-q**|**--quiet**[=*false*]]
+ IMAGE
+
+# DESCRIPTION
+
+Show the history of when and how an image was created.
+
+# OPTIONS
+
+**--no-trunc**=*true*|*false*
+   When true don't truncate output. Default is false
+
+**-q**, **--quiet=*true*|*false*
+   When true only show numeric IDs. Default is false.
+
+# EXAMPLE
+    $ sudo docker history fedora
+    IMAGE          CREATED          CREATED BY                                      SIZE
+    105182bb5e8b   5 days ago       /bin/sh -c #(nop) ADD file:71356d2ad59aa3119d   372.7 MB
+    73bd853d2ea5   13 days ago      /bin/sh -c #(nop) MAINTAINER Lokesh Mandvekar   0 B
+    511136ea3c5a   10 months ago                                                    0 B
+
+# HISTORY
+April 2014, Originally compiled by William Henry (whenry at redhat dot com)
+based on docker.io source material and internal work.

+ 99 - 0
contrib/man/md/docker-images.1.md

@@ -0,0 +1,99 @@
+% DOCKER(1) Docker User Manuals
+% William Henry
+% APRIL 2014
+# NAME
+docker-images - List the images in the local repository
+
+# SYNOPSIS
+**docker images**
+[**-a**|**--all**=*false*]
+[**--no-trunc**[=*false*]
+[**-q**|**--quiet**[=*false*]
+[**-t**|**--tree**=*false*]
+[**-v**|**--viz**=*false*]
+[NAME]
+
+# DESCRIPTION
+This command lists the images stored in the local Docker repository.
+
+By default, intermediate images, used during builds, are not listed. Some of the
+output, e.g. image ID, is truncated, for space reasons. However the truncated
+image ID, and often the first few characters, are enough to be used in other
+Docker commands that use the image ID. The output includes repository, tag, image
+ID, date created and the virtual size.
+
+The title REPOSITORY for the first title may seem confusing. It is essentially
+the image name. However, because you can tag a specific image, and multiple tags
+(image instances) can be associated with a single name, the name is really a
+repository for all tagged images of the same name. For example consider an image
+called fedora. It may be tagged with 18, 19, or 20, etc. to manage different
+versions.
+
+# OPTIONS
+
+**-a**, **--all**=*true*|*false*
+   When set to true, also include all intermediate images in the list. The
+default is false.
+
+**--no-trunc**=*true*|*false*
+   When set to true, list the full image ID and not the truncated ID. The
+default is false.
+
+**-q**, **--quiet**=*true*|*false*
+   When set to true, list the complete image ID as part of the output. The
+default is false.
+
+**-t**, **--tree**=*true*|*false*
+   When set to true, list the images in a tree dependency tree (hierarchy)
+format. The default is false.
+
+**-v**, **--viz**=*true*|*false*
+   When set to true, list the graph in graphviz format. The default is
+*false*.
+
+# EXAMPLES
+
+## Listing the images
+
+To list the images in a local repository (not the registry) run:
+
+    docker images
+
+The list will contain the image repository name, a tag for the image, and an
+image ID, when it was created and its virtual size. Columns: REPOSITORY, TAG,
+IMAGE ID, CREATED, and VIRTUAL SIZE.
+
+To get a verbose list of images which contains all the intermediate images
+used in builds use **-a**:
+
+    docker images -a
+
+## List images dependency tree hierarchy
+
+To list the images in the local repository (not the registry) in a dependency
+tree format, use the **-t** option.
+
+    docker images -t
+
+This displays a staggered hierarchy tree where the less indented image is
+the oldest with dependent image layers branching inward (to the right) on
+subsequent lines. The newest or top level image layer is listed last in
+any tree branch.
+
+## List images in GraphViz format
+
+To display the list in a format consumable by a GraphViz tools run with
+**-v**. For example to produce a .png graph file of the hierarchy use:
+
+    docker images --viz | dot -Tpng -o docker.png
+
+## Listing only the shortened image IDs
+
+Listing just the shortened image IDs. This can be useful for some automated
+tools.
+
+    docker images -q
+
+# HISTORY
+April 2014, Originally compiled by William Henry (whenry at redhat dot com)
+based on docker.io source material and internal work.

+ 39 - 0
contrib/man/md/docker-import.1.md

@@ -0,0 +1,39 @@
+% DOCKER(1) Docker User Manuals
+% William Henry
+% APRIL 2014
+# NAME
+docker-import - Create an empty filesystem image and import the contents
+of the tarball into it.
+
+# SYNOPSIS
+**docker import** URL|- [REPOSITORY[:TAG]]
+
+# DESCRIPTION
+Create a new filesystem image from the contents of a tarball (.tar,
+.tar.gz, .tgz, .bzip, .tar.xz, .txz) into it, then optionally tag it.
+
+# EXAMPLES
+
+## Import from a remote location
+
+    # docker import http://example.com/exampleimage.tgz example/imagerepo
+
+## Import from a local file
+
+Import to docker via pipe and stdin:
+
+    # cat exampleimage.tgz | docker import - example/imagelocal
+
+## Import from a local file and tag
+
+Import to docker via pipe and stdin:
+
+    # cat exampleimageV2.tgz | docker import - example/imagelocal:V-2.0
+
+## Import from a local directory
+
+    # tar -c . | docker import - exampleimagedir
+
+# HISTORY
+April 2014, Originally compiled by William Henry (whenry at redhat dot com)
+based on docker.io source material and internal work.

+ 46 - 0
contrib/man/md/docker-info.1.md

@@ -0,0 +1,46 @@
+% DOCKER(1) Docker User Manuals
+% William Henry
+% APRIL 2014
+# NAME
+docker-info - Display system wide information
+
+# SYNOPSIS
+**docker info**
+
+# DESCRIPTION
+This command displays system wide information regarding the Docker installation.
+Information displayed includes the number of containers and images, pool name,
+data file, metadata file, data space used, total data space, metadata space used
+, total metadata space, execution driver, and the kernel version.
+
+The data file is where the images are stored and the metadata file is where the
+meta data regarding those images are stored. When run for the first time Docker
+allocates a certain amount of data space and meta data space from the space
+available on the volume where `/var/lib/docker` is mounted.
+
+# OPTIONS
+There are no available options.
+
+# EXAMPLES
+
+## Display Docker system information
+
+Here is a sample output:
+
+    # docker info
+    Containers: 18
+    Images: 95
+    Storage Driver: devicemapper
+     Pool Name: docker-8:1-170408448-pool
+     Data file: /var/lib/docker/devicemapper/devicemapper/data
+     Metadata file: /var/lib/docker/devicemapper/devicemapper/metadata
+     Data Space Used: 9946.3 Mb
+     Data Space Total: 102400.0 Mb
+     Metadata Space Used: 9.9 Mb
+     Metadata Space Total: 2048.0 Mb
+    Execution Driver: native-0.1
+    Kernel Version: 3.10.0-116.el7.x86_64
+
+# HISTORY
+April 2014, Originally compiled by William Henry (whenry at redhat dot com)
+based on docker.io source material and internal work.

+ 229 - 0
contrib/man/md/docker-inspect.1.md

@@ -0,0 +1,229 @@
+% DOCKER(1) Docker User Manuals
+% William Henry
+% APRIL 2014
+# NAME
+docker-inspect - Return low-level information on a container/image
+
+# SYNOPSIS
+**docker inspect** [**-f**|**--format**="" CONTAINER|IMAGE
+[CONTAINER|IMAGE...]
+
+# DESCRIPTION
+
+This displays all the information available in Docker for a given
+container or image. By default, this will render all results in a JSON
+array. If a format is specified, the given template will be executed for
+each result.
+
+# OPTIONS
+**-f**, **--format**=""
+   The text/template package of Go describes all the details of the
+format. See examples section
+
+# EXAMPLES
+
+## Getting information on a container
+
+To get information on a container use it's ID or instance name:
+
+    #docker inspect 1eb5fabf5a03
+    [{
+       "ID": "1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b",
+       "Created": "2014-04-04T21:33:52.02361335Z",
+       "Path": "/usr/sbin/nginx",
+       "Args": [],
+       "Config": {
+            "Hostname": "1eb5fabf5a03",
+            "Domainname": "",
+            "User": "",
+            "Memory": 0,
+            "MemorySwap": 0,
+            "CpuShares": 0,
+            "AttachStdin": false,
+            "AttachStdout": false,
+            "AttachStderr": false,
+            "PortSpecs": null,
+            "ExposedPorts": {
+                "80/tcp": {}
+        },
+	    "Tty": true,
+            "OpenStdin": false,
+            "StdinOnce": false,
+            "Env": [
+               "HOME=/",
+	       "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
+            ],
+            "Cmd": [
+                "/usr/sbin/nginx"
+            ],
+            "Dns": null,
+            "DnsSearch": null,
+            "Image": "summit/nginx",
+            "Volumes": null,
+            "VolumesFrom": "",
+            "WorkingDir": "",
+            "Entrypoint": null,
+            "NetworkDisabled": false,
+            "OnBuild": null,
+            "Context": {
+               "mount_label": "system_u:object_r:svirt_sandbox_file_t:s0:c0,c650",
+	       "process_label": "system_u:system_r:svirt_lxc_net_t:s0:c0,c650"
+	    }
+        },
+        "State": {
+            "Running": true,
+            "Pid": 858,
+            "ExitCode": 0,
+            "StartedAt": "2014-04-04T21:33:54.16259207Z",
+            "FinishedAt": "0001-01-01T00:00:00Z",
+            "Ghost": false
+        },
+        "Image": "df53773a4390e25936f9fd3739e0c0e60a62d024ea7b669282b27e65ae8458e6",
+        "NetworkSettings": {
+            "IPAddress": "172.17.0.2",
+            "IPPrefixLen": 16,
+            "Gateway": "172.17.42.1",
+            "Bridge": "docker0",
+            "PortMapping": null,
+            "Ports": {
+                "80/tcp": [
+                    {
+                        "HostIp": "0.0.0.0",
+                        "HostPort": "80"
+                    }
+                ]
+            }
+        },
+        "ResolvConfPath": "/etc/resolv.conf",
+        "HostnamePath": "/var/lib/docker/containers/1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b/hostname",
+        "HostsPath": "/var/lib/docker/containers/1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b/hosts",
+        "Name": "/ecstatic_ptolemy",
+        "Driver": "devicemapper",
+        "ExecDriver": "native-0.1",
+        "Volumes": {},
+        "VolumesRW": {},
+        "HostConfig": {
+        "Binds": null,
+            "ContainerIDFile": "",
+            "LxcConf": [],
+            "Privileged": false,
+            "PortBindings": {
+                "80/tcp": [
+                    {
+                        "HostIp": "0.0.0.0",
+                        "HostPort": "80"
+                    }
+                ]
+            },
+            "Links": null,
+            "PublishAllPorts": false,
+            "DriverOptions": {
+                "lxc": null
+            },
+            "CliAddress": ""
+        }
+
+## Getting the IP address of a container instance
+
+To get the IP address of a container use:
+
+    # docker inspect --format='{{.NetworkSettings.IPAddress}}' 1eb5fabf5a03
+    172.17.0.2
+
+## Listing all port bindings
+
+One can loop over arrays and maps in the results to produce simple text
+output:
+
+    # docker inspect --format='{{range $p, $conf := .NetworkSettings.Ports}} \
+     {{$p}} -> {{(index $conf 0).HostPort}} {{end}}' 1eb5fabf5a03
+
+    80/tcp -> 80
+
+## Getting information on an image
+
+Use an image's ID or name (e.g. repository/name[:tag]) to get information
+ on it.
+
+    # docker inspect 58394af37342
+    [{
+        "id": "58394af373423902a1b97f209a31e3777932d9321ef10e64feaaa7b4df609cf9",
+        "parent": "8abc22bad04266308ff408ca61cb8f6f4244a59308f7efc64e54b08b496c58db",
+        "created": "2014-02-03T16:10:40.500814677Z",
+        "container": "f718f19a28a5147da49313c54620306243734bafa63c76942ef6f8c4b4113bc5",
+        "container_config": {
+            "Hostname": "88807319f25e",
+            "Domainname": "",
+            "User": "",
+            "Memory": 0,
+            "MemorySwap": 0,
+            "CpuShares": 0,
+            "AttachStdin": false,
+            "AttachStdout": false,
+            "AttachStderr": false,
+            "PortSpecs": null,
+            "ExposedPorts": null,
+            "Tty": false,
+            "OpenStdin": false,
+            "StdinOnce": false,
+            "Env": [
+                "HOME=/",
+                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
+            ],
+            "Cmd": [
+                "/bin/sh",
+                "-c",
+		 "#(nop) ADD fedora-20-dummy.tar.xz in /"
+            ],
+            "Dns": null,
+            "DnsSearch": null,
+            "Image": "8abc22bad04266308ff408ca61cb8f6f4244a59308f7efc64e54b08b496c58db",
+            "Volumes": null,
+            "VolumesFrom": "",
+            "WorkingDir": "",
+            "Entrypoint": null,
+            "NetworkDisabled": false,
+            "OnBuild": null,
+            "Context": null
+        },
+        "docker_version": "0.6.3",
+        "author": "I P Babble \u003clsm5@ipbabble.com\u003e - ./buildcontainers.sh",
+        "config": {
+            "Hostname": "88807319f25e",
+            "Domainname": "",
+            "User": "",
+            "Memory": 0,
+            "MemorySwap": 0,
+            "CpuShares": 0,
+            "AttachStdin": false,
+            "AttachStdout": false,
+            "AttachStderr": false,
+            "PortSpecs": null,
+            "ExposedPorts": null,
+            "Tty": false,
+            "OpenStdin": false,
+            "StdinOnce": false,
+            "Env": [
+                "HOME=/",
+		        "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
+            ],
+            "Cmd": null,
+            "Dns": null,
+            "DnsSearch": null,
+            "Image": "8abc22bad04266308ff408ca61cb8f6f4244a59308f7efc64e54b08b496c58db",
+            "Volumes": null,
+            "VolumesFrom": "",
+            "WorkingDir": "",
+            "Entrypoint": null,
+            "NetworkDisabled": false,
+            "OnBuild": null,
+            "Context": null
+        },
+	"architecture": "x86_64",
+	"Size": 385520098
+    }]
+
+# HISTORY
+
+April 2014, Originally compiled by William Henry (whenry at redhat dot com)
+based on docker.io source material and internal work.

+ 21 - 0
contrib/man/md/docker-kill.1.md

@@ -0,0 +1,21 @@
+% DOCKER(1) Docker User Manuals
+% William Henry
+% APRIL 2014
+# NAME
+docker-kill - Kill a running container (send SIGKILL, or specified signal)
+
+# SYNOPSIS
+**docker kill** **--signal**[=*"KILL"*] CONTAINER [CONTAINER...]
+
+# DESCRIPTION
+
+The main process inside each container specified will be sent SIGKILL,
+ or any signal specified with option --signal.
+
+# OPTIONS
+**-s**, **--signal**=*"KILL"*
+   Signal to send to the container
+
+# HISTORY
+April 2014, Originally compiled by William Henry (whenry at redhat dot com)
+ based on docker.io source material and internal work.

+ 36 - 0
contrib/man/md/docker-load.1.md

@@ -0,0 +1,36 @@
+% DOCKER(1) Docker User Manuals
+% William Henry
+% APRIL 2014
+# NAME
+docker-load - Load an image from a tar archive on STDIN
+
+# SYNOPSIS
+**docker load**  **--input**=""
+
+# DESCRIPTION
+
+Loads a tarred repository from a file or the standard input stream.
+Restores both images and tags.
+
+# OPTIONS
+
+**-i**, **--input**=""
+   Read from a tar archive file, instead of STDIN
+
+# EXAMPLE
+
+    $ sudo docker images
+    REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
+    busybox             latest              769b9341d937        7 weeks ago         2.489 MB
+    $ sudo docker load --input fedora.tar
+    $ sudo docker images
+    REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
+    busybox             latest              769b9341d937        7 weeks ago         2.489 MB
+    fedora              rawhide             0d20aec6529d        7 weeks ago         387 MB
+    fedora              20                  58394af37342        7 weeks ago         385.5 MB
+    fedora              heisenbug           58394af37342        7 weeks ago         385.5 MB
+    fedora              latest              58394af37342        7 weeks ago         385.5 MB
+
+# HISTORY
+April 2014, Originally compiled by William Henry (whenry at redhat dot com)
+based on docker.io source material and internal work.

+ 35 - 0
contrib/man/md/docker-login.1.md

@@ -0,0 +1,35 @@
+% DOCKER(1) Docker User Manuals
+% William Henry
+% APRIL 2014
+# NAME
+docker-login - Register or Login to a docker registry server.
+
+# SYNOPSIS
+**docker login** [**-e**|**-email**=""] [**-p**|**--password**=""]
+ [**-u**|**--username**=""] [SERVER]
+
+# DESCRIPTION
+Register or Login to a docker registry server, if no server is
+specified "https://index.docker.io/v1/" is the default. If you want to
+login to a private registry you can specify this by adding the server name.
+
+# OPTIONS
+**-e**, **--email**=""
+   Email address
+
+**-p**, **--password**=""
+   Password
+
+**-u**, **--username**=""
+   Username
+
+# EXAMPLE
+
+## Login to a local registry
+
+    # docker login localhost:8080
+
+# HISTORY
+April 2014, Originally compiled by William Henry (whenry at redhat dot com)
+based on docker.io source material and internal work.
+

+ 26 - 0
contrib/man/md/docker-logs.1.md

@@ -0,0 +1,26 @@
+% DOCKER(1) Docker User Manuals
+% William Henry
+% APRIL 2014
+# NAME
+docker-logs - Fetch the logs of a container
+
+# SYNOPSIS
+**docker logs** **--follow**[=*false*] CONTAINER
+
+# DESCRIPTION
+The **docker logs** command batch-retrieves whatever logs are present for
+a container at the time of execution. This does not guarantee execution
+order when combined with a docker run (i.e. your run may not have generated
+any logs at the time you execute docker logs).
+
+The **docker logs --follow** command combines commands **docker logs** and
+**docker attach**. It will first return all logs from the beginning and
+then continue streaming new output from the container’s stdout and stderr.
+
+# OPTIONS
+**-f, --follow**=*true*|*false*
+   When *true*, follow log output. The default is false.
+
+# HISTORY
+April 2014, Originally compiled by William Henry (whenry at redhat dot com)
+based on docker.io source material and internal work.

+ 15 - 0
contrib/man/md/docker-port.1.md

@@ -0,0 +1,15 @@
+% DOCKER(1) Docker User Manuals
+% William Henry
+% APRIL 2014
+# NAME
+docker-port - Lookup the public-facing port which is NAT-ed to PRIVATE_PORT
+
+# SYNOPSIS
+**docker port** CONTAINER PRIVATE_PORT
+
+# DESCRIPTION
+Lookup the public-facing port which is NAT-ed to PRIVATE_PORT
+
+# HISTORY
+April 2014, Originally compiled by William Henry (whenry at redhat dot com)
+based on docker.io source material and internal work.

+ 68 - 0
contrib/man/md/docker-ps.1.md

@@ -0,0 +1,68 @@
+% DOCKER(1) Docker User Manuals
+% William Henry
+% APRIL 2014
+# NAME
+docker-ps - List containers
+
+# SYNOPSIS
+**docker ps** [**-a**|**--all**=*false*] [**--before**=""]
+[**-l**|**--latest**=*false*] [**-n**=*-1*] [**--no-trunc**=*false*]
+[**-q**|**--quiet**=*false*] [**-s**|**--size**=*false*]
+[**--since**=""]
+
+# DESCRIPTION
+
+List the containers in the local repository. By default this show only
+the running containers.
+
+# OPTIONS
+
+**-a**, **--all**=*true*|*false*
+   When true show all containers. Only running containers are shown by
+default. Default is false.
+
+**--before**=""
+   Show only container created before Id or Name, include non-running
+ones.
+
+**-l**, **--latest**=*true*|*false*
+   When true show only the latest created container, include non-running
+ones. The default is false.
+
+**-n**=NUM
+   Show NUM (integer) last created containers, include non-running ones.
+The default is -1 (none)
+
+**--no-trunc**=*true*|*false*
+   When true truncate output. Default is false.
+
+**-q**, **--quiet**=*true*|*false*
+   When false only display numeric IDs. Default is false.
+
+**-s**, **--size**=*true*|*false*
+   When true display container sizes. Default is false.
+
+**--since**=""
+   Show only containers created since Id or Name, include non-running ones.
+
+# EXAMPLE
+# Display all containers, including non-running
+
+    # docker ps -a
+    CONTAINER ID        IMAGE                 COMMAND                CREATED             STATUS      PORTS    NAMES
+    a87ecb4f327c        fedora:20             /bin/sh -c #(nop) MA   20 minutes ago      Exit 0               desperate_brattain
+    01946d9d34d8        vpavlin/rhel7:latest  /bin/sh -c #(nop) MA   33 minutes ago      Exit 0               thirsty_bell
+    c1d3b0166030        acffc0358b9e          /bin/sh -c yum -y up   2 weeks ago         Exit 1               determined_torvalds
+    41d50ecd2f57        fedora:20             /bin/sh -c #(nop) MA   2 weeks ago         Exit 0               drunk_pike
+
+# Display only IDs of all containers, including non-running
+
+    # docker ps -a -q
+    a87ecb4f327c
+    01946d9d34d8
+    c1d3b0166030
+    41d50ecd2f57
+
+# HISTORY
+April 2014, Originally compiled by William Henry (whenry at redhat dot com)
+based on docker.io source material and internal work.

+ 37 - 0
contrib/man/md/docker-pull.1.md

@@ -0,0 +1,37 @@
+% DOCKER(1) Docker User Manuals
+% William Henry
+% APRIL 2014
+# NAME
+docker-pull - Pull an image or a repository from the registry
+
+# SYNOPSIS
+**docker pull** NAME[:TAG]
+
+# DESCRIPTION
+
+This command pulls down an image or a repository from the registry. If
+there is more than one image for a repository (e.g. fedora) then all
+images for that repository name are pulled down including any tags.
+
+# EXAMPLE
+
+# Pull a reposiotry with multiple images
+
+    $ sudo docker pull fedora
+    Pulling repository fedora
+    ad57ef8d78d7: Download complete
+    105182bb5e8b: Download complete
+    511136ea3c5a: Download complete
+    73bd853d2ea5: Download complete
+
+    $ sudo docker images
+    REPOSITORY   TAG         IMAGE ID        CREATED      VIRTUAL SIZE
+    fedora       rawhide     ad57ef8d78d7    5 days ago   359.3 MB
+    fedora       20          105182bb5e8b    5 days ago   372.7 MB
+    fedora       heisenbug   105182bb5e8b    5 days ago   372.7 MB
+    fedora       latest      105182bb5e8b    5 days ago   372.7 MB
+
+# HISTORY
+April 2014, Originally compiled by William Henry (whenry at redhat dot com)
+based on docker.io source material and internal work.
+

+ 44 - 0
contrib/man/md/docker-push.1.md

@@ -0,0 +1,44 @@
+% DOCKER(1) Docker User Manuals
+% William Henry
+% APRIL 2014
+# NAME
+docker-push - Push an image or a repository to the registry
+
+# SYNOPSIS
+**docker push** NAME[:TAG]
+
+# DESCRIPTION
+Push an image or a repository to a registry. The default registry is the Docker 
+Index located at [index.docker.io](https://index.docker.io/v1/). However the 
+image can be pushed to another, perhaps private, registry as demonstrated in 
+the example below.
+
+# EXAMPLE
+
+# Pushing a new image to a registry
+
+First save the new image by finding the container ID (using **docker ps**)
+and then committing it to a new image name:
+
+    # docker commit c16378f943fe rhel-httpd
+
+Now push the image to the registry using the image ID. In this example
+the registry is on host named registry-host and listening on port 5000.
+Default Docker commands will push to the default `index.docker.io`
+registry. Instead, push to the local registry, which is on a host called
+registry-host*. To do this, tag the image with the host name or IP
+address, and the port of the registry:
+
+    # docker tag rhel-httpd registry-host:5000/myadmin/rhel-httpd
+    # docker push registry-host:5000/myadmin/rhel-httpd
+
+Check that this worked by running:
+
+    # docker images
+
+You should see both `rhel-httpd` and `registry-host:5000/myadmin/rhel-httpd`
+listed.
+
+# HISTORY
+April 2014, Originally compiled by William Henry (whenry at redhat dot com)
+based on docker.io source material and internal work.

+ 21 - 0
contrib/man/md/docker-restart.1.md

@@ -0,0 +1,21 @@
+% DOCKER(1) Docker User Manuals
+% William Henry
+% APRIL 2014
+# NAME
+docker-restart - Restart a running container
+
+# SYNOPSIS
+**docker restart** [**-t**|**--time**[=*10*]] CONTAINER [CONTAINER...]
+
+# DESCRIPTION
+Restart each container listed.
+
+# OPTIONS
+**-t**, **--time**=NUM
+   Number of seconds to try to stop for before killing the container. Once
+killed it will then be restarted. Default=10
+
+# HISTORY
+April 2014, Originally compiled by William Henry (whenry at redhat dot com)
+based on docker.io source material and internal work.
+

+ 56 - 0
contrib/man/md/docker-rm.1.md

@@ -0,0 +1,56 @@
+% DOCKER(1) Docker User Manuals
+% William Henry
+% APRIL 2014
+
+# NAME
+
+docker-rm - Remove one or more containers.
+
+# SYNOPSIS
+
+**docker rm** [**-f**|**--force**[=*false*] [**-l**|**--link**[=*false*] [**-v**|
+**--volumes**[=*false*]
+CONTAINER [CONTAINER...]
+
+# DESCRIPTION
+
+**docker rm** will remove one or more containers from the host node. The
+container name or ID can be used. This does not remove images. You cannot
+remove a running container unless you use the \fB-f\fR option. To see all
+containers on a host use the **docker ps -a** command.
+
+# OPTIONS
+
+**-f**, **--force**=*true*|*false*
+   When set to true, force the removal of the container. The default is
+*false*.
+
+**-l**, **--link**=*true*|*false*
+   When set to true, remove the specified link and not the underlying
+container. The default is *false*.
+
+**-v**, **--volumes**=*true*|*false*
+   When set to true, remove the volumes associated to the container. The
+default is *false*.
+
+# EXAMPLES
+
+##Removing a container using its ID##
+
+To remove a container using its ID, find either from a **docker ps -a**
+command, or use the ID returned from the **docker run** command, or retrieve
+it from a file used to store it using the **docker run --cidfile**:
+
+    docker rm abebf7571666
+
+##Removing a container using the container name##
+
+The name of the container can be found using the **docker ps -a**
+command. The use that name as follows:
+
+    docker rm hopeful_morse
+
+# HISTORY
+
+April 2014, Originally compiled by William Henry (whenry at redhat dot com)
+based on docker.io source material and internal work.

+ 35 - 0
contrib/man/md/docker-rmi.1.md

@@ -0,0 +1,35 @@
+% DOCKER(1) Docker User Manuals
+% William Henry
+% APRIL 2014
+# NAME
+docker-rmi \- Remove one or more images.
+
+# SYNOPSIS
+
+**docker rmi** [**-f**|**--force**[=*false*] IMAGE [IMAGE...]
+
+# DESCRIPTION
+
+This will remove one or more images from the host node. This does not
+remove images from a registry. You cannot remove an image of a running
+container unless you use the **-f** option. To see all images on a host
+use the **docker images** command.
+
+# OPTIONS
+
+**-f**, **--force**=*true*|*false*
+   When set to true, force the removal of the image. The default is
+*false*.
+
+# EXAMPLES
+
+## Removing an image
+
+Here is an example of removing and image:
+
+    docker rmi fedora/httpd
+
+# HISTORY
+
+April 2014, Originally compiled by William Henry (whenry at redhat dot com)
+based on docker.io source material and internal work.

+ 343 - 0
contrib/man/md/docker-run.1.md

@@ -0,0 +1,343 @@
+% DOCKER(1) Docker User Manuals
+% William Henry
+% APRIL 2014
+# NAME
+docker-run - Run a process in an isolated container
+
+# SYNOPSIS
+**docker run**
+[**-a**|**--attach**[=]] [**-c**|**--cpu-shares**[=0]
+[**-m**|**--memory**=*memory-limit*]
+[**--cidfile**=*file*] [**-d**|**--detach**[=*false*]] [**--dns**=*IP-address*]
+[**--name**=*name*] [**-u**|**--user**=*username*|*uid*]
+[**--link**=*name*:*alias*]
+[**-e**|**--env**=*environment*] [**--entrypoint**=*command*]
+[**--expose**=*port*] [**-P**|**--publish-all**[=*false*]]
+[**-p**|**--publish**=*port-mappping*] [**-h**|**--hostname**=*hostname*]
+[**--rm**[=*false*]] [**--priviledged**[=*false*]
+[**-i**|**--interactive**[=*false*]
+[**-t**|**--tty**[=*false*]] [**--lxc-conf**=*options*]
+[**-n**|**--networking**[=*true*]]
+[**-v**|**--volume**=*volume*] [**--volumes-from**=*container-id*]
+[**-w**|**--workdir**=*directory*] [**--sig-proxy**[=*true*]]
+IMAGE [COMMAND] [ARG...]
+
+# DESCRIPTION
+
+Run a process in a new container. **docker run** starts a process with its own
+file system, its own networking, and its own isolated process tree. The IMAGE
+which starts the process may define defaults related to the process that will be
+run in the container, the networking to expose, and more, but **docker run**
+gives final control to the operator or administrator who starts the container
+from the image. For that reason **docker run** has more options than any other
+Docker command.
+
+If the IMAGE is not already loaded then **docker run** will pull the IMAGE, and
+all image dependencies, from the repository in the same way running **docker
+pull** IMAGE, before it starts the container from that image.
+
+# OPTIONS
+
+**-a**, **--attach**=*stdin*|*stdout*|*stderr*
+   Attach to stdin, stdout or stderr. In foreground mode (the default when
+**-d** is not specified), **docker run** can start the process in the container
+and attach the console to the process’s standard input, output, and standard
+error. It can even pretend to be a TTY (this is what most commandline
+executables expect) and pass along signals. The **-a** option can be set for
+each of stdin, stdout, and stderr.
+
+**-c**, **--cpu-shares**=0
+   CPU shares in relative weight. You can increase the priority of a container
+with the -c option. By default, all containers run at the same priority and get
+the same proportion of CPU cycles, but you can tell the kernel to give more
+shares of CPU time to one or more containers when you start them via **docker
+run**.
+
+**--cidfile**=*file*
+   Write the container ID to the file specified.
+
+
+**-d**, **-detach**=*true*|*false*
+   Detached mode. This runs the container in the background. It outputs the new
+container's ID and any error messages. At any time you can run **docker ps** in
+the other shell to view a list of the running containers. You can reattach to a
+detached container with **docker attach**. If you choose to run a container in
+the detached mode, then you cannot use the **-rm** option.
+
+
+**--dns**=*IP-address*
+   Set custom DNS servers. This option can be used to override the DNS
+configuration passed to the container. Typically this is necessary when the
+host DNS configuration is invalid for the container (eg. 127.0.0.1). When this
+is the case the **-dns** flags is necessary for every run.
+
+
+**-e**, **-env**=*environment*
+   Set environment variables. This option allows you to specify arbitrary
+environment variables that are available for the process that will be launched
+inside of the container.
+
+
+**--entrypoint**=*command*
+   This option allows you to overwrite the default entrypoint of the image that
+is set in the Dockerfile. The ENTRYPOINT of an image is similar to a COMMAND
+because it specifies what executable to run when the container starts, but it is
+(purposely) more difficult to override. The ENTRYPOINT gives a container its
+default nature or behavior, so that when you set an ENTRYPOINT you can run the
+container as if it were that binary, complete with default options, and you can
+pass in more options via the COMMAND. But, sometimes an operator may want to run
+something else inside the container, so you can override the default ENTRYPOINT
+at runtime by using a **--entrypoint** and a string to specify the new
+ENTRYPOINT.
+
+**--expose**=*port*
+   Expose a port from the container without publishing it to your host. A
+containers port can be exposed to other containers in three ways: 1) The
+developer can expose the port using the EXPOSE parameter of the Dockerfile, 2)
+the operator can use the **--expose** option with **docker run**, or 3) the
+container can be started with the **--link**.
+
+**-m**, **-memory**=*memory-limit*
+   Allows you to constrain the memory available to a container. If the host
+supports swap memory, then the -m memory setting can be larger than physical
+RAM. The memory limit format: <number><optional unit>, where unit = b, k, m or
+g.
+
+**-P**, **-publish-all**=*true*|*false*
+   When set to true publish all exposed ports to the host interfaces. The
+default is false. If the operator uses -P (or -p) then Docker will make the
+exposed port accessible on the host and the ports will be available to any
+client that can reach the host. To find the map between the host ports and the
+exposed ports, use **docker port**.
+
+
+**-p**, **-publish**=[]
+   Publish a container's port to the host (format: ip:hostPort:containerPort |
+ip::containerPort | hostPort:containerPort) (use **docker port** to see the
+actual mapping)
+
+
+**-h**, **-hostname**=*hostname*
+   Sets the container host name that is available inside the container.
+
+
+**-i**, **-interactive**=*true*|*false*
+   When set to true, keep stdin open even if not attached. The default is false.
+
+
+**--link**=*name*:*alias*
+   Add link to another container. The format is name:alias. If the operator
+uses **--link** when starting the new client container, then the client
+container can access the exposed port via a private networking interface. Docker
+will set some environment variables in the client container to help indicate
+which interface and port to use.
+
+
+**-n**, **-networking**=*true*|*false*
+   By default, all containers have networking enabled (true) and can make
+outgoing connections. The operator can disable networking with **--networking**
+to false. This disables all incoming and outgoing networking. In cases like this
+, I/O can only be performed through files or by using STDIN/STDOUT.
+
+Also by default, the container will use the same DNS servers as the host. The
+operator may override this with **-dns**.
+
+
+**--name**=*name*
+   Assign a name to the container. The operator can identify a container in
+three ways:
+
+    UUID long identifier (“f78375b1c487e03c9438c729345e54db9d20cfa2ac1fc3494b6eb60872e74778”)
+    UUID short identifier (“f78375b1c487”)
+    Name (“jonah”)
+
+The UUID identifiers come from the Docker daemon, and if a name is not assigned
+to the container with **--name** then the daemon will also generate a random
+string name. The name is useful when defining links (see **--link**) (or any
+other place you need to identify a container). This works for both background
+and foreground Docker containers.
+
+
+**--privileged**=*true*|*false*
+   Give extended privileges to this container. By default, Docker containers are
+“unprivileged” (=false) and cannot, for example, run a Docker daemon inside the
+Docker container. This is because by default a container is not allowed to
+access any devices. A “privileged” container is given access to all devices.
+
+When the operator executes **docker run -privileged**, Docker will enable access
+to all devices on the host as well as set some configuration in AppArmor to
+allow the container nearly all the same access to the host as processes running
+outside of a container on the host.
+
+
+**--rm**=*true*|*false*
+   If set to *true* the container is automatically removed when it exits. The
+default is *false*. This option is incompatible with **-d**.
+
+
+**--sig-proxy**=*true*|*false*
+   When set to true, proxify all received signals to the process (even in
+non-tty mode). The default is true.
+
+
+**-t**, **-tty**=*true*|*false*
+   When set to true Docker can allocate a pseudo-tty and attach to the standard
+input of any container. This can be used, for example, to run a throwaway
+interactive shell. The default is value is false.
+
+
+**-u**, **-user**=*username*,*uid*
+   Set a username or UID for the container.
+
+
+**-v**, **-volume**=*volume*
+   Bind mount a volume to the container. The **-v** option can be used one or
+more times to add one or more mounts to a container. These mounts can then be
+used in other containers using the **--volumes-from** option. See examples.
+
+
+**--volumes-from**=*container-id*
+   Will mount volumes from the specified container identified by container-id.
+Once a volume is mounted in a one container it can be shared with other
+containers using the **--volumes-from** option when running those other
+containers. The volumes can be shared even if the original container with the
+mount is not running.
+
+
+**-w**, **-workdir**=*directory*
+   Working directory inside the container. The default working directory for
+running binaries within a container is the root directory (/). The developer can
+set a different default with the Dockerfile WORKDIR instruction. The operator
+can override the working directory by using the **-w** option.
+
+
+**IMAGE**
+   The image name or ID.
+
+
+**COMMAND**
+   The command or program to run inside the image.
+
+
+**ARG**
+   The arguments for the command to be run in the container.
+
+# EXAMPLES
+
+## Exposing log messages from the container to the host's log
+
+If you want messages that are logged in your container to show up in the host's
+syslog/journal then you should bind mount the /var/log directory as follows.
+
+    # docker run -v /dev/log:/dev/log -i -t fedora /bin/bash
+
+From inside the container you can test this by sending a message to the log.
+
+    (bash)# logger "Hello from my container"
+
+Then exit and check the journal.
+
+    # exit
+
+    # journalctl -b | grep Hello
+
+This should list the message sent to logger.
+
+## Attaching to one or more from STDIN, STDOUT, STDERR
+
+If you do not specify -a then Docker will attach everything (stdin,stdout,stderr)
+. You can specify to which of the three standard streams (stdin, stdout, stderr)
+you’d like to connect instead, as in:
+
+    # docker run -a stdin -a stdout -i -t fedora /bin/bash
+
+## Linking Containers
+
+The link feature allows multiple containers to communicate with each other. For
+example, a container whose Dockerfile has exposed port 80 can be run and named
+as follows:
+
+    # docker run --name=link-test -d -i -t fedora/httpd
+
+A second container, in this case called linker, can communicate with the httpd
+container, named link-test, by running with the **--link=<name>:<alias>**
+
+    # docker run -t -i --link=link-test:lt --name=linker fedora /bin/bash
+
+Now the container linker is linked to container link-test with the alias lt.
+Running the **env** command in the linker container shows environment variables
+ with the LT (alias) context (**LT_**)
+
+    # env
+    HOSTNAME=668231cb0978
+    TERM=xterm
+    LT_PORT_80_TCP=tcp://172.17.0.3:80
+    LT_PORT_80_TCP_PORT=80
+    LT_PORT_80_TCP_PROTO=tcp
+    LT_PORT=tcp://172.17.0.3:80
+    PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
+    PWD=/
+    LT_NAME=/linker/lt
+    SHLVL=1
+    HOME=/
+    LT_PORT_80_TCP_ADDR=172.17.0.3
+    _=/usr/bin/env
+
+When linking two containers Docker will use the exposed ports of the container
+to create a secure tunnel for the parent to access.
+
+
+## Mapping Ports for External Usage
+
+The exposed port of an application can be mapped to a host port using the **-p**
+flag. For example a httpd port 80 can be mapped to the host port 8080 using the
+following:
+
+    # docker run -p 8080:80 -d -i -t fedora/httpd
+
+## Creating and Mounting a Data Volume Container
+
+Many applications require the sharing of persistent data across several
+containers. Docker allows you to create a Data Volume Container that other
+containers can mount from. For example, create a named container that contains
+directories /var/volume1 and /tmp/volume2. The image will need to contain these
+directories so a couple of RUN mkdir instructions might be required for you
+fedora-data image:
+
+    # docker run --name=data -v /var/volume1 -v /tmp/volume2 -i -t fedora-data true
+    # docker run --volumes-from=data --name=fedora-container1 -i -t fedora bash
+
+Multiple -volumes-from parameters will bring together multiple data volumes from
+multiple containers. And it's possible to mount the volumes that came from the
+DATA container in yet another container via the fedora-container1 intermidiery
+container, allowing to abstract the actual data source from users of that data:
+
+    # docker run --volumes-from=fedora-container1 --name=fedora-container2 -i -t fedora bash
+
+## Mounting External Volumes
+
+To mount a host directory as a container volume, specify the absolute path to
+the directory and the absolute path for the container directory separated by a
+colon:
+
+    # docker run -v /var/db:/data1 -i -t fedora bash
+
+When using SELinux, be aware that the host has no knowledge of container SELinux
+policy. Therefore, in the above example, if SELinux policy is enforced, the
+`/var/db` directory is not writable to the container. A "Permission Denied"
+message will occur and an avc: message in the host's syslog.
+
+
+To work around this, at time of writing this man page, the following command
+needs to be run in order for the proper SELinux policy type label to be attached
+to the host directory:
+
+    # chcon -Rt svirt_sandbox_file_t /var/db
+
+
+Now, writing to the /data1 volume in the container will be allowed and the
+changes will also be reflected on the host in /var/db.
+
+# HISTORY
+April 2014, Originally compiled by William Henry (whenry at redhat dot com)
+based on docker.io source material and internal work.

+ 35 - 0
contrib/man/md/docker-save.1.md

@@ -0,0 +1,35 @@
+% DOCKER(1) Docker User Manuals
+% William Henry
+% APRIL 2014
+# NAME
+docker-save - Save an image to a tar archive (streamed to STDOUT by default)
+
+# SYNOPSIS
+**docker save** [**-o**|**--output**=""] IMAGE
+
+# DESCRIPTION
+Produces a tarred repository to the standard output stream. Contains all
+parent layers, and all tags + versions, or specified repo:tag.
+
+Stream to a file instead of STDOUT by using **-o**.
+
+# OPTIONS
+**-o**, **--output**=""
+   Write to an file, instead of STDOUT
+
+# EXAMPLE
+
+Save all fedora repository images to a fedora-all.tar and save the latest
+fedora image to a fedora-latest.tar:
+
+    $ sudo docker save fedora > fedora-all.tar
+    $ sudo docker save --output=fedora-latest.tar fedora:latest
+    $ ls -sh fedora-all.tar
+    721M fedora-all.tar
+    $ ls -sh fedora-latest.tar
+    367M fedora-latest.tar
+
+# HISTORY
+April 2014, Originally compiled by William Henry (whenry at redhat dot com)
+based on docker.io source material and internal work.
+

+ 55 - 0
contrib/man/md/docker-search.1.md

@@ -0,0 +1,55 @@
+% DOCKER(1) Docker User Manuals
+% William Henry
+% APRIL 2014
+# NAME
+docker-search - Search the docker index for images
+
+# SYNOPSIS
+**docker search** **--no-trunc**[=*false*] **-t**|**--trusted**[=*false*]
+ **-s**|**--stars**[=*0*] TERM
+
+# DESCRIPTION
+
+Search an index for an image with that matches the term TERM. The table
+of images returned displays the name, description (truncated by default),
+number of stars awarded, whether the image is official, and whether it
+is trusted.
+
+# OPTIONS
+**--no-trunc**=*true*|*false*
+   When true display the complete description. The default is false.
+
+**-s**, **--stars**=NUM
+   Only displays with at least NUM (integer) stars. I.e. only those images
+ranked >=NUM.
+
+**-t**, **--trusted**=*true*|*false*
+   When true only show trusted builds. The default is false.
+
+# EXAMPLE
+
+## Search the registry for ranked images
+
+Search the registry for the term 'fedora' and only display those images
+ranked 3 or higher:
+
+    $ sudo docker search -s 3 fedora
+    NAME                  DESCRIPTION                                    STARS OFFICIAL  TRUSTED
+    mattdm/fedora         A basic Fedora image corresponding roughly...  50
+    fedora                (Semi) Official Fedora base image.             38
+    mattdm/fedora-small   A small Fedora image on which to build. Co...  8
+    goldmann/wildfly      A WildFly application server running on a ...  3               [OK]
+
+## Search the registry for trusted images
+
+Search the registry for the term 'fedora' and only display trusted images
+ranked 1 or higher:
+
+    $ sudo docker search -s 1 -t fedora
+    NAME               DESCRIPTION                                     STARS OFFICIAL  TRUSTED
+    goldmann/wildfly   A WildFly application server running on a ...   3               [OK]
+    tutum/fedora-20    Fedora 20 image with SSH access. For the r...   1               [OK]
+
+# HISTORY
+April 2014, Originally compiled by William Henry (whenry at redhat dot com)
+based on docker.io source material and internal work.

+ 25 - 0
contrib/man/md/docker-start.1.md

@@ -0,0 +1,25 @@
+% DOCKER(1) Docker User Manuals
+% William Henry
+% APRIL 2014
+# NAME
+docker-start - Restart a stopped container
+
+# SYNOPSIS
+**docker start** [**a**|**--attach**[=*false*]] [**-i**|**--interactive**
+[=*true*] CONTAINER [CONTAINER...]
+
+# DESCRIPTION
+
+Start a stopped container.
+
+# OPTION
+**-a**, **--attach**=*true*|*false*
+   When true attach to container's stdout/stderr and forward all signals to
+the process
+
+**-i**, **--interactive**=*true*|*false*
+   When true attach to container's stdin
+
+# HISTORY
+April 2014, Originally compiled by William Henry (whenry at redhat dot com)
+based on docker.io source material and internal work.

+ 22 - 0
contrib/man/md/docker-stop.1.md

@@ -0,0 +1,22 @@
+% DOCKER(1) Docker User Manuals
+% William Henry
+% APRIL 2014
+# NAME
+docker-stop - Stop a running container
+ grace period)
+
+# SYNOPSIS
+**docker stop** [**-t**|**--time**[=*10*]] CONTAINER [CONTAINER...]
+
+# DESCRIPTION
+Stop a running container (Send SIGTERM, and then SIGKILL after
+ grace period)
+
+# OPTIONS
+**-t**, **--time**=NUM
+   Wait NUM number of seconds for the container to stop before killing it.
+The default is 10 seconds.
+
+# HISTORY
+April 2014, Originally compiled by William Henry (whenry at redhat dot com)
+based on docker.io source material and internal work.

+ 48 - 0
contrib/man/md/docker-tag.1.md

@@ -0,0 +1,48 @@
+% DOCKER(1) Docker User Manuals
+% William Henry
+% APRIL 2014
+# NAME
+docker-tag - Tag an image in the repository
+
+# SYNOPSIS
+**docker tag** [**-f**|**--force**[=*false*]
+IMAGE [REGISTRYHOST/][USERNAME/]NAME[:TAG]
+
+# DESCRIPTION
+This will tag an image in the repository.
+
+# "OPTIONS"
+**-f**, **--force**=*true*|*false*
+   When set to true, force the tag name. The default is *false*.
+
+**REGISTRYHOST**
+   The hostname of the registry if required. This may also include the port
+separated by a ':'
+
+**USERNAME**
+   The username or other qualifying identifier for the image.
+
+**NAME**
+   The image name.
+
+**TAG**
+   The tag you are assigning to the image.
+
+# EXAMPLES
+
+## Tagging an image
+
+Here is an example of tagging an image with the tag version1.0 :
+
+    docker tag 0e5574283393 fedora/httpd:version1.0
+
+## Tagging an image for a private repository
+
+To push an image to an private registry and not the central Docker
+registry you must tag it with the registry hostname and port (if needed).
+
+    docker tag 0e5574283393 myregistryhost:5000/fedora/httpd:version1.0
+
+# HISTORY
+April 2014, Originally compiled by William Henry (whenry at redhat dot com)
+based on docker.io source material and internal work.

+ 27 - 0
contrib/man/md/docker-top.1.md

@@ -0,0 +1,27 @@
+% DOCKER(1) Docker User Manuals
+% William Henry
+% APRIL 2014
+# NAME
+docker-top - Lookup the running processes of a container
+
+# SYNOPSIS
+**docker top** CONTAINER [ps-OPTION]
+
+# DESCRIPTION
+
+Look up the running process of the container. ps-OPTION can be any of the
+ options you would pass to a Linux ps command.
+
+# EXAMPLE
+
+Run **docker top** with the ps option of -x:
+
+    $ sudo docker top 8601afda2b -x
+    PID      TTY       STAT       TIME         COMMAND
+    16623    ?         Ss         0:00         sleep 99999
+
+
+# HISTORY
+April 2014, Originally compiled by William Henry (whenry at redhat dot com)
+based on docker.io source material and internal work.
+

+ 23 - 0
contrib/man/md/docker-wait.1.md

@@ -0,0 +1,23 @@
+% DOCKER(1) Docker User Manuals
+% William Henry
+% APRIL 2014
+# NAME
+docker-wait - Block until a container stops, then print its exit code.
+
+# SYNOPSIS
+**docker wait** CONTAINER [CONTAINER...]
+
+# DESCRIPTION
+Block until a container stops, then print its exit code.
+
+#EXAMPLE
+
+    $ sudo docker run -d fedora sleep 99
+    079b83f558a2bc52ecad6b2a5de13622d584e6bb1aea058c11b36511e85e7622
+    $ sudo docker wait 079b83f558a2bc
+    0
+
+# HISTORY
+April 2014, Originally compiled by William Henry (whenry at redhat dot com)
+based on docker.io source material and internal work.
+

+ 187 - 0
contrib/man/md/docker.1.md

@@ -0,0 +1,187 @@
+% DOCKER(1) Docker User Manuals
+% William Henry
+% APRIL 2014
+# NAME
+docker \- Docker image and container command line interface
+
+# SYNOPSIS
+**docker** [OPTIONS] COMMAND [arg...]
+
+# DESCRIPTION
+**docker** has two distinct functions. It is used for starting the Docker
+daemon and to run the CLI (i.e., to command the daemon to manage images,
+containers etc.) So **docker** is both a server, as a deamon, and a client
+to the daemon, through the CLI.
+
+To run the Docker deamon you do not specify any of the commands listed below but
+must specify the **-d** option.  The other options listed below are for the
+daemon only.
+
+The Docker CLI has over 30 commands. The commands are listed below and each has
+its own man page which explain usage and arguements.
+
+To see the man page for a command run **man docker <command>**.
+
+# OPTIONS
+**-D**=*true*|*false*
+   Enable debug mode. Default is false.
+
+**-H**, **--host**=[unix:///var/run/docker.sock]: tcp://[host[:port]] to bind or
+unix://[/path/to/socket] to use.
+   Enable both the socket support and TCP on localhost. When host=[0.0.0.0],
+port=[4243] or path =[/var/run/docker.sock] is omitted, default values are used.
+
+**--api-enable-cors**=*true*|*false*
+  Enable CORS headers in the remote API. Default is false.
+
+**-b**=""
+  Attach containers to a pre\-existing network bridge; use 'none' to disable container networking
+
+**--bip**=""
+  Use the provided CIDR notation address for the dynamically created bridge (docker0); Mutually exclusive of \-b
+
+**-d**=*true*|*false*
+  Enable daemon mode. Default is false.
+
+**--dns**=""
+  Force Docker to use specific DNS servers
+
+**-g**=""
+  Path to use as the root of the Docker runtime. Default is `/var/lib/docker`.
+
+**--icc**=*true*|*false*
+  Enable inter\-container communication. Default is true.
+
+**--ip**=""
+  Default IP address to use when binding container ports. Default is `0.0.0.0`.
+
+**--iptables**=*true*|*false*
+  Disable Docker's addition of iptables rules. Default is true.
+
+**--mtu**=VALUE
+  Set the containers network mtu. Default is `1500`.
+
+**-p**=""
+  Path to use for daemon PID file. Default is `/var/run/docker.pid`
+
+**-r**=*true*|*false*
+  Restart previously running containers. Default is true.
+
+**-s**=""
+  Force the Docker runtime to use a specific storage driver.
+
+**-v**=*true*|*false*
+  Print version information and quit. Default is false.
+
+**--selinux-enabled=*true*|*false*
+  Enable selinux support. Default is false.
+
+# COMMANDS
+**docker-attach(1)**
+  Attach to a running container
+
+**docker-build(1)**
+  Build a container from a Dockerfile
+
+**docker-commit(1)**
+  Create a new image from a container's changes
+
+**docker-cp(1)**
+  Copy files/folders from the containers filesystem to the host at path
+
+**docker-diff(1)**
+  Inspect changes on a container's filesystem
+
+
+**docker-events(1)**
+  Get real time events from the server
+
+**docker-export(1)**
+  Stream the contents of a container as a tar archive
+
+**docker-history(1)**
+  Show the history of an image
+
+**docker-images(1)**
+  List images
+
+**docker-import(1)**
+  Create a new filesystem image from the contents of a tarball
+
+**docker-info(1)**
+  Display system-wide information
+
+**docker-inspect(1)**
+  Return low-level information on a container
+
+**docker-kill(1)**
+  Kill a running container (which includes the wrapper process and everything
+inside it)
+
+**docker-load(1)**
+  Load an image from a tar archive
+
+**docker-login(1)**
+  Register or Login to a Docker registry server
+
+**docker-logs(1)**
+  Fetch the logs of a container
+
+**docker-port(1)**
+  Lookup the public-facing port which is NAT-ed to PRIVATE_PORT
+
+**docker-ps(1)**
+  List containers
+
+**docker-pull(1)**
+  Pull an image or a repository from a Docker registry server
+
+**docker-push(1)**
+  Push an image or a repository to a Docker registry server
+
+**docker-restart(1)**
+  Restart a running container
+
+**docker-rm(1)**
+  Remove one or more containers
+
+**docker-rmi(1)**
+  Remove one or more images
+
+**docker-run(1)**
+  Run a command in a new container
+
+**docker-save(1)**
+  Save an image to a tar archive
+
+**docker-search(1)**
+  Search for an image in the Docker index
+
+**docker-start(1)**
+  Start a stopped container
+
+**docker-stop(1)**
+  Stop a running container
+
+**docker-tag(1)**
+  Tag an image into a repository
+
+**docker-top(1)**
+  Lookup the running processes of a container
+
+**version**
+  Show the Docker version information
+
+**docker-wait(1)**
+  Block until a container stops, then print its exit code
+
+# EXAMPLES
+
+For specific examples please see the man page for the specific Docker command.
+For example:
+
+    man docker run
+
+# HISTORY
+April 2014, Originally compiled by William Henry (whenry at redhat dot com) based
+ on docker.io source material and internal work.

+ 22 - 0
contrib/man/md/md2man-all.sh

@@ -0,0 +1,22 @@
+#!/bin/bash
+set -e
+
+# get into this script's directory
+cd "$(dirname "$(readlink -f "$BASH_SOURCE")")"
+
+[ "$1" = '-q' ] || {
+	set -x
+	pwd
+}
+
+for FILE in *.md; do
+	base="$(basename "$FILE")"
+	name="${base%.md}"
+	num="${name##*.}"
+	if [ -z "$num" -o "$base" = "$num" ]; then
+		# skip files that aren't of the format xxxx.N.md (like README.md)
+		continue
+	fi
+	mkdir -p "../man${num}"
+	pandoc -s -t man "$FILE" -o "../man${num}/${name}"
+done

+ 0 - 0
contrib/man/man1/docker-attach.1 → contrib/man/old-man/docker-attach.1


+ 0 - 0
contrib/man/man1/docker-build.1 → contrib/man/old-man/docker-build.1


+ 0 - 0
contrib/man/man1/docker-images.1 → contrib/man/old-man/docker-images.1


+ 0 - 0
contrib/man/man1/docker-info.1 → contrib/man/old-man/docker-info.1


+ 0 - 0
contrib/man/man1/docker-inspect.1 → contrib/man/old-man/docker-inspect.1


+ 0 - 0
contrib/man/man1/docker-rm.1 → contrib/man/old-man/docker-rm.1


+ 50 - 0
contrib/man/old-man/docker-rm.md

@@ -0,0 +1,50 @@
+DOCKER "1" "APRIL 2014" "0.1" "Docker"
+=======================================
+
+NAME
+----
+
+docker-rm - Remove one or more containers.
+
+SYNOPSIS
+--------
+
+`docker rm` [`-f`|`--force`[=*false*] [`-l`|`--link`[=*false*] [`-v`|`--volumes`[=*false*] 
+CONTAINER [CONTAINER...]
+
+DESCRIPTION
+-----------
+
+`docker rm` will remove one or more containers from the host node. The container name or ID can be used. This does not remove images. You cannot remove a running container unless you use the \fB-f\fR option. To see all containers on a host use the `docker ps -a` command.
+
+OPTIONS
+-------
+
+`-f`, `--force`=*true*|*false*: 
+  When set to true, force the removal of the container. The default is *false*.
+
+`-l`, `--link`=*true*|*false*: 
+  When set to true, remove the specified link and not the underlying container. The default is *false*.
+
+`-v`, `--volumes`=*true*|*false*: 
+  When set to true, remove the volumes associated to the container. The default is *false*.
+
+EXAMPLES
+--------
+
+##Removing a container using its ID##
+
+To remove a container using its ID, find either from a `docker ps -a` command, or use the ID returned from the `docker run` command, or retrieve it from a file used to store it using the `docker run --cidfile`:
+
+    docker rm abebf7571666
+
+##Removing a container using the container name##
+
+The name of the container can be found using the \fBdocker ps -a\fR command. The use that name as follows:
+
+    docker rm hopeful_morse
+
+HISTORY
+-------
+
+April 2014, Originally compiled by William Henry (whenry at redhat dot com) based on dockier.io source material and internal work.

+ 0 - 0
contrib/man/man1/docker-rmi.1 → contrib/man/old-man/docker-rmi.1


+ 0 - 0
contrib/man/man1/docker-run.1 → contrib/man/old-man/docker-run.1


+ 0 - 0
contrib/man/man1/docker-tag.1 → contrib/man/old-man/docker-tag.1


+ 0 - 0
contrib/man/man1/docker.1 → contrib/man/old-man/docker.1


+ 82 - 0
contrib/mkimage-alpine.sh

@@ -0,0 +1,82 @@
+#!/bin/sh
+
+set -e
+
+[ $(id -u) -eq 0 ] || {
+	printf >&2 '%s requires root\n' "$0"
+	exit 1
+}
+
+usage() {
+	printf >&2 '%s: [-r release] [-m mirror] [-s]\n' "$0"
+	exit 1
+}
+
+tmp() {
+	TMP=$(mktemp -d /tmp/alpine-docker-XXXXXXXXXX)
+	ROOTFS=$(mktemp -d /tmp/alpine-docker-rootfs-XXXXXXXXXX)
+	trap "rm -rf $TMP $ROOTFS" EXIT TERM INT
+}
+
+apkv() {
+	curl -s $REPO/$ARCH/APKINDEX.tar.gz | tar -Oxz |
+		grep '^P:apk-tools-static$' -A1 | tail -n1 | cut -d: -f2
+}
+
+getapk() {
+	curl -s $REPO/$ARCH/apk-tools-static-$(apkv).apk |
+		tar -xz -C $TMP sbin/apk.static
+}
+
+mkbase() {
+	$TMP/sbin/apk.static --repository $REPO --update-cache --allow-untrusted \
+		--root $ROOTFS --initdb add alpine-base
+}
+
+conf() {
+	printf '%s\n' $REPO > $ROOTFS/etc/apk/repositories
+}
+
+pack() {
+	local id
+	id=$(tar --numeric-owner -C $ROOTFS -c . | docker import - alpine:$REL)
+
+	docker tag $id alpine:latest
+	docker run -i -t alpine printf 'alpine:%s with id=%s created!\n' $REL $id
+}
+
+save() {
+	[ $SAVE -eq 1 ] || return
+
+	tar --numeric-owner -C $ROOTFS -c . | xz > rootfs.tar.xz
+}
+
+while getopts "hr:m:s" opt; do
+	case $opt in
+		r)
+			REL=$OPTARG
+			;;
+		m)
+			MIRROR=$OPTARG
+			;;
+		s)
+			SAVE=1
+			;;
+		*)
+			usage
+			;;
+	esac
+done
+
+REL=${REL:-edge}
+MIRROR=${MIRROR:-http://nl.alpinelinux.org/alpine}
+SAVE=${SAVE:-0}
+REPO=$MIRROR/$REL/main
+ARCH=$(uname -m)
+
+tmp
+getapk
+mkbase
+conf
+pack
+save

+ 1 - 0
contrib/mkimage-arch.sh

@@ -57,6 +57,7 @@ mknod -m 666 $DEV/tty0 c 4 0
 mknod -m 666 $DEV/full c 1 7
 mknod -m 666 $DEV/full c 1 7
 mknod -m 600 $DEV/initctl p
 mknod -m 600 $DEV/initctl p
 mknod -m 666 $DEV/ptmx c 5 2
 mknod -m 666 $DEV/ptmx c 5 2
+ln -sf /proc/self/fd $DEV/fd
 
 
 tar --numeric-owner -C $ROOTFS -c . | docker import - archlinux
 tar --numeric-owner -C $ROOTFS -c . | docker import - archlinux
 docker run -i -t archlinux echo Success.
 docker run -i -t archlinux echo Success.

+ 0 - 1
contrib/zfs/MAINTAINERS

@@ -1 +0,0 @@
-Gurjeet Singh <gurjeet@singh.im> (gurjeet.singh.im)

+ 0 - 23
contrib/zfs/README.md

@@ -1,23 +0,0 @@
-# ZFS Storage Driver
-
-This is a placeholder to declare the presence and status of ZFS storage driver
-for containers.
-
-The current development is done in Gurjeet Singh's fork of Docker, under the
-branch named [zfs_driver].
-
-[zfs_driver]: https://github.com/gurjeet/docker/tree/zfs_driver
-
-
-# Status
-
-Alpha: The code is now capable of creating, running and destroying containers
-and images.
-
-The code is under development. Contributions in the form of suggestions,
-code-reviews, and patches are welcome.
-
-Please send the communication to gurjeet@singh.im and CC at least one Docker
-mailing list.
-
-

+ 153 - 0
daemon/attach.go

@@ -0,0 +1,153 @@
+package daemon
+
+import (
+	"io"
+
+	"github.com/dotcloud/docker/utils"
+)
+
+func (daemon *Daemon) Attach(container *Container, stdin io.ReadCloser, stdinCloser io.Closer, stdout io.Writer, stderr io.Writer) chan error {
+	var (
+		cStdout, cStderr io.ReadCloser
+		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() {
+				utils.Debugf("attach: stdin: begin")
+				defer utils.Debugf("attach: stdin: end")
+				// No matter what, when stdin is closed (io.Copy unblock), close stdout and stderr
+				if container.Config.StdinOnce && !container.Config.Tty {
+					defer cStdin.Close()
+				} else {
+					defer func() {
+						if cStdout != nil {
+							cStdout.Close()
+						}
+						if cStderr != nil {
+							cStderr.Close()
+						}
+					}()
+				}
+				if container.Config.Tty {
+					_, err = utils.CopyEscapable(cStdin, stdin)
+				} else {
+					_, err = io.Copy(cStdin, stdin)
+				}
+				if err == io.ErrClosedPipe {
+					err = nil
+				}
+				if err != nil {
+					utils.Errorf("attach: stdin: %s", err)
+				}
+				errors <- err
+			}()
+		}
+	}
+	if stdout != nil {
+		nJobs += 1
+		if p, err := container.StdoutPipe(); err != nil {
+			errors <- err
+		} else {
+			cStdout = p
+			go func() {
+				utils.Debugf("attach: stdout: begin")
+				defer utils.Debugf("attach: stdout: end")
+				// If we are in StdinOnce mode, then close stdin
+				if container.Config.StdinOnce && stdin != nil {
+					defer stdin.Close()
+				}
+				if stdinCloser != nil {
+					defer stdinCloser.Close()
+				}
+				_, err := io.Copy(stdout, cStdout)
+				if err == io.ErrClosedPipe {
+					err = nil
+				}
+				if err != nil {
+					utils.Errorf("attach: stdout: %s", err)
+				}
+				errors <- err
+			}()
+		}
+	} else {
+		go func() {
+			if stdinCloser != nil {
+				defer stdinCloser.Close()
+			}
+			if cStdout, err := container.StdoutPipe(); err != nil {
+				utils.Errorf("attach: stdout pipe: %s", err)
+			} else {
+				io.Copy(&utils.NopWriter{}, cStdout)
+			}
+		}()
+	}
+	if stderr != nil {
+		nJobs += 1
+		if p, err := container.StderrPipe(); err != nil {
+			errors <- err
+		} else {
+			cStderr = p
+			go func() {
+				utils.Debugf("attach: stderr: begin")
+				defer utils.Debugf("attach: stderr: end")
+				// If we are in StdinOnce mode, then close stdin
+				if container.Config.StdinOnce && stdin != nil {
+					defer stdin.Close()
+				}
+				if stdinCloser != nil {
+					defer stdinCloser.Close()
+				}
+				_, err := io.Copy(stderr, cStderr)
+				if err == io.ErrClosedPipe {
+					err = nil
+				}
+				if err != nil {
+					utils.Errorf("attach: stderr: %s", err)
+				}
+				errors <- err
+			}()
+		}
+	} else {
+		go func() {
+			if stdinCloser != nil {
+				defer stdinCloser.Close()
+			}
+
+			if cStderr, err := container.StderrPipe(); err != nil {
+				utils.Errorf("attach: stdout pipe: %s", err)
+			} else {
+				io.Copy(&utils.NopWriter{}, cStderr)
+			}
+		}()
+	}
+
+	return utils.Go(func() error {
+		defer func() {
+			if cStdout != nil {
+				cStdout.Close()
+			}
+			if cStderr != nil {
+				cStderr.Close()
+			}
+		}()
+
+		// FIXME: how to 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 {
+			utils.Debugf("attach: waiting for job %d/%d", i+1, nJobs)
+			if err := <-errors; err != nil {
+				utils.Errorf("attach: job %d returned error %s, aborting all jobs", i+1, err)
+				return err
+			}
+			utils.Debugf("attach: job %d completed successfully", i+1)
+		}
+		utils.Debugf("attach: all jobs completed successfully")
+		return nil
+	})
+}

+ 405 - 528
runtime/container.go → daemon/container.go

@@ -1,18 +1,9 @@
-package runtime
+package daemon
 
 
 import (
 import (
 	"encoding/json"
 	"encoding/json"
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
-	"github.com/dotcloud/docker/archive"
-	"github.com/dotcloud/docker/engine"
-	"github.com/dotcloud/docker/image"
-	"github.com/dotcloud/docker/links"
-	"github.com/dotcloud/docker/nat"
-	"github.com/dotcloud/docker/runconfig"
-	"github.com/dotcloud/docker/runtime/execdriver"
-	"github.com/dotcloud/docker/runtime/graphdriver"
-	"github.com/dotcloud/docker/utils"
 	"io"
 	"io"
 	"io/ioutil"
 	"io/ioutil"
 	"log"
 	"log"
@@ -22,6 +13,19 @@ import (
 	"sync"
 	"sync"
 	"syscall"
 	"syscall"
 	"time"
 	"time"
+
+	"github.com/dotcloud/docker/archive"
+	"github.com/dotcloud/docker/daemon/execdriver"
+	"github.com/dotcloud/docker/daemon/graphdriver"
+	"github.com/dotcloud/docker/engine"
+	"github.com/dotcloud/docker/image"
+	"github.com/dotcloud/docker/links"
+	"github.com/dotcloud/docker/nat"
+	"github.com/dotcloud/docker/pkg/label"
+	"github.com/dotcloud/docker/pkg/networkfs/etchosts"
+	"github.com/dotcloud/docker/pkg/networkfs/resolvconf"
+	"github.com/dotcloud/docker/runconfig"
+	"github.com/dotcloud/docker/utils"
 )
 )
 
 
 const DefaultPathEnv = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
 const DefaultPathEnv = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
@@ -64,7 +68,8 @@ type Container struct {
 	stdin     io.ReadCloser
 	stdin     io.ReadCloser
 	stdinPipe io.WriteCloser
 	stdinPipe io.WriteCloser
 
 
-	runtime *Runtime
+	daemon                   *Daemon
+	MountLabel, ProcessLabel string
 
 
 	waitLock chan struct{}
 	waitLock chan struct{}
 	Volumes  map[string]string
 	Volumes  map[string]string
@@ -76,42 +81,6 @@ type Container struct {
 	activeLinks map[string]*links.Link
 	activeLinks map[string]*links.Link
 }
 }
 
 
-// FIXME: move deprecated port stuff to nat to clean up the core.
-type PortMapping map[string]string // Deprecated
-
-type NetworkSettings struct {
-	IPAddress   string
-	IPPrefixLen int
-	Gateway     string
-	Bridge      string
-	PortMapping map[string]PortMapping // Deprecated
-	Ports       nat.PortMap
-}
-
-func (settings *NetworkSettings) PortMappingAPI() *engine.Table {
-	var outs = engine.NewTable("", 0)
-	for port, bindings := range settings.Ports {
-		p, _ := nat.ParsePort(port.Port())
-		if len(bindings) == 0 {
-			out := &engine.Env{}
-			out.SetInt("PublicPort", p)
-			out.Set("Type", port.Proto())
-			outs.Add(out)
-			continue
-		}
-		for _, binding := range bindings {
-			out := &engine.Env{}
-			h, _ := nat.ParsePort(binding.HostPort)
-			out.SetInt("PrivatePort", p)
-			out.SetInt("PublicPort", h)
-			out.Set("Type", port.Proto())
-			out.Set("IP", binding.HostIp)
-			outs.Add(out)
-		}
-	}
-	return outs
-}
-
 // Inject the io.Reader at the given path. Note: do not close the reader
 // Inject the io.Reader at the given path. Note: do not close the reader
 func (container *Container) Inject(file io.Reader, pth string) error {
 func (container *Container) Inject(file io.Reader, pth string) error {
 	if err := container.Mount(); err != nil {
 	if err := container.Mount(); err != nil {
@@ -148,10 +117,6 @@ func (container *Container) Inject(file io.Reader, pth string) error {
 	return nil
 	return nil
 }
 }
 
 
-func (container *Container) When() time.Time {
-	return container.Created
-}
-
 func (container *Container) FromDisk() error {
 func (container *Container) FromDisk() error {
 	data, err := ioutil.ReadFile(container.jsonPath())
 	data, err := ioutil.ReadFile(container.jsonPath())
 	if err != nil {
 	if err != nil {
@@ -162,6 +127,10 @@ func (container *Container) FromDisk() error {
 	if err := json.Unmarshal(data, container); err != nil && !strings.Contains(err.Error(), "docker.PortMapping") {
 	if err := json.Unmarshal(data, container); err != nil && !strings.Contains(err.Error(), "docker.PortMapping") {
 		return err
 		return err
 	}
 	}
+
+	if err := label.ReserveLabel(container.ProcessLabel); err != nil {
+		return err
+	}
 	return container.readHostConfig()
 	return container.readHostConfig()
 }
 }
 
 
@@ -201,186 +170,46 @@ func (container *Container) WriteHostConfig() (err error) {
 	return ioutil.WriteFile(container.hostConfigPath(), data, 0666)
 	return ioutil.WriteFile(container.hostConfigPath(), data, 0666)
 }
 }
 
 
-func (container *Container) generateEnvConfig(env []string) error {
-	data, err := json.Marshal(env)
-	if err != nil {
-		return err
-	}
-	p, err := container.EnvConfigPath()
-	if err != nil {
-		return err
-	}
-	ioutil.WriteFile(p, data, 0600)
-	return nil
-}
-
-func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, stdout io.Writer, stderr io.Writer) chan error {
-	var cStdout, cStderr io.ReadCloser
-
-	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() {
-				utils.Debugf("attach: stdin: begin")
-				defer utils.Debugf("attach: stdin: end")
-				// No matter what, when stdin is closed (io.Copy unblock), close stdout and stderr
-				if container.Config.StdinOnce && !container.Config.Tty {
-					defer cStdin.Close()
-				} else {
-					defer func() {
-						if cStdout != nil {
-							cStdout.Close()
-						}
-						if cStderr != nil {
-							cStderr.Close()
-						}
-					}()
-				}
-				if container.Config.Tty {
-					_, err = utils.CopyEscapable(cStdin, stdin)
-				} else {
-					_, err = io.Copy(cStdin, stdin)
-				}
-				if err == io.ErrClosedPipe {
-					err = nil
-				}
-				if err != nil {
-					utils.Errorf("attach: stdin: %s", err)
-				}
-				errors <- err
-			}()
-		}
-	}
-	if stdout != nil {
-		nJobs += 1
-		if p, err := container.StdoutPipe(); err != nil {
-			errors <- err
-		} else {
-			cStdout = p
-			go func() {
-				utils.Debugf("attach: stdout: begin")
-				defer utils.Debugf("attach: stdout: end")
-				// If we are in StdinOnce mode, then close stdin
-				if container.Config.StdinOnce && stdin != nil {
-					defer stdin.Close()
-				}
-				if stdinCloser != nil {
-					defer stdinCloser.Close()
-				}
-				_, err := io.Copy(stdout, cStdout)
-				if err == io.ErrClosedPipe {
-					err = nil
-				}
-				if err != nil {
-					utils.Errorf("attach: stdout: %s", err)
-				}
-				errors <- err
-			}()
-		}
-	} else {
-		go func() {
-			if stdinCloser != nil {
-				defer stdinCloser.Close()
-			}
-			if cStdout, err := container.StdoutPipe(); err != nil {
-				utils.Errorf("attach: stdout pipe: %s", err)
-			} else {
-				io.Copy(&utils.NopWriter{}, cStdout)
-			}
-		}()
-	}
-	if stderr != nil {
-		nJobs += 1
-		if p, err := container.StderrPipe(); err != nil {
-			errors <- err
-		} else {
-			cStderr = p
-			go func() {
-				utils.Debugf("attach: stderr: begin")
-				defer utils.Debugf("attach: stderr: end")
-				// If we are in StdinOnce mode, then close stdin
-				if container.Config.StdinOnce && stdin != nil {
-					defer stdin.Close()
-				}
-				if stdinCloser != nil {
-					defer stdinCloser.Close()
-				}
-				_, err := io.Copy(stderr, cStderr)
-				if err == io.ErrClosedPipe {
-					err = nil
-				}
-				if err != nil {
-					utils.Errorf("attach: stderr: %s", err)
-				}
-				errors <- err
-			}()
-		}
-	} else {
-		go func() {
-			if stdinCloser != nil {
-				defer stdinCloser.Close()
-			}
-
-			if cStderr, err := container.StderrPipe(); err != nil {
-				utils.Errorf("attach: stdout pipe: %s", err)
-			} else {
-				io.Copy(&utils.NopWriter{}, cStderr)
-			}
-		}()
-	}
-
-	return utils.Go(func() error {
-		defer func() {
-			if cStdout != nil {
-				cStdout.Close()
-			}
-			if cStderr != nil {
-				cStderr.Close()
-			}
-		}()
-
-		// FIXME: how to 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 {
-			utils.Debugf("attach: waiting for job %d/%d", i+1, nJobs)
-			if err := <-errors; err != nil {
-				utils.Errorf("attach: job %d returned error %s, aborting all jobs", i+1, err)
-				return err
-			}
-			utils.Debugf("attach: job %d completed successfully", i+1)
-		}
-		utils.Debugf("attach: all jobs completed successfully")
-		return nil
-	})
-}
-
-func populateCommand(c *Container) {
+func populateCommand(c *Container, env []string) error {
 	var (
 	var (
-		en           *execdriver.Network
-		driverConfig = make(map[string][]string)
+		en      *execdriver.Network
+		context = make(map[string][]string)
 	)
 	)
+	context["process_label"] = []string{c.GetProcessLabel()}
+	context["mount_label"] = []string{c.GetMountLabel()}
 
 
 	en = &execdriver.Network{
 	en = &execdriver.Network{
-		Mtu:       c.runtime.config.Mtu,
+		Mtu:       c.daemon.config.Mtu,
 		Interface: nil,
 		Interface: nil,
 	}
 	}
 
 
-	if !c.Config.NetworkDisabled {
-		network := c.NetworkSettings
-		en.Interface = &execdriver.NetworkInterface{
-			Gateway:     network.Gateway,
-			Bridge:      network.Bridge,
-			IPAddress:   network.IPAddress,
-			IPPrefixLen: network.IPPrefixLen,
+	parts := strings.SplitN(string(c.hostConfig.NetworkMode), ":", 2)
+	switch parts[0] {
+	case "none":
+	case "host":
+		en.HostNetworking = true
+	case "bridge", "": // empty string to support existing containers
+		if !c.Config.NetworkDisabled {
+			network := c.NetworkSettings
+			en.Interface = &execdriver.NetworkInterface{
+				Gateway:     network.Gateway,
+				Bridge:      network.Bridge,
+				IPAddress:   network.IPAddress,
+				IPPrefixLen: network.IPPrefixLen,
+			}
 		}
 		}
+	case "container":
+		nc, err := c.getNetworkedContainer()
+		if err != nil {
+			return err
+		}
+		en.ContainerID = nc.ID
+	default:
+		return fmt.Errorf("invalid network mode: %s", c.hostConfig.NetworkMode)
 	}
 	}
 
 
 	// TODO: this can be removed after lxc-conf is fully deprecated
 	// TODO: this can be removed after lxc-conf is fully deprecated
-	mergeLxcConfIntoOptions(c.hostConfig, driverConfig)
+	mergeLxcConfIntoOptions(c.hostConfig, context)
 
 
 	resources := &execdriver.Resources{
 	resources := &execdriver.Resources{
 		Memory:     c.Config.Memory,
 		Memory:     c.Config.Memory,
@@ -398,22 +227,12 @@ func populateCommand(c *Container) {
 		Network:    en,
 		Network:    en,
 		Tty:        c.Config.Tty,
 		Tty:        c.Config.Tty,
 		User:       c.Config.User,
 		User:       c.Config.User,
-		Config:     driverConfig,
+		Config:     context,
 		Resources:  resources,
 		Resources:  resources,
 	}
 	}
 	c.command.SysProcAttr = &syscall.SysProcAttr{Setsid: true}
 	c.command.SysProcAttr = &syscall.SysProcAttr{Setsid: true}
-}
-
-func (container *Container) ArgsAsString() string {
-	var args []string
-	for _, arg := range container.Args {
-		if strings.Contains(arg, " ") {
-			args = append(args, fmt.Sprintf("'%s'", arg))
-		} else {
-			args = append(args, arg)
-		}
-	}
-	return strings.Join(args, " ")
+	c.command.Env = env
+	return nil
 }
 }
 
 
 func (container *Container) Start() (err error) {
 func (container *Container) Start() (err error) {
@@ -423,186 +242,47 @@ func (container *Container) Start() (err error) {
 	if container.State.IsRunning() {
 	if container.State.IsRunning() {
 		return nil
 		return nil
 	}
 	}
-
+	// if we encounter and error during start we need to ensure that any other
+	// setup has been cleaned up properly
 	defer func() {
 	defer func() {
 		if err != nil {
 		if err != nil {
 			container.cleanup()
 			container.cleanup()
 		}
 		}
 	}()
 	}()
 
 
-	if container.ResolvConfPath == "" {
-		if err := container.setupContainerDns(); err != nil {
-			return err
-		}
+	if err := container.setupContainerDns(); err != nil {
+		return err
 	}
 	}
-
 	if err := container.Mount(); err != nil {
 	if err := container.Mount(); err != nil {
 		return err
 		return err
 	}
 	}
-
-	if container.runtime.config.DisableNetwork {
-		container.Config.NetworkDisabled = true
-		container.buildHostnameAndHostsFiles("127.0.1.1")
-	} else {
-		if err := container.allocateNetwork(); err != nil {
-			return err
-		}
-		container.buildHostnameAndHostsFiles(container.NetworkSettings.IPAddress)
-	}
-
-	// Make sure the config is compatible with the current kernel
-	if container.Config.Memory > 0 && !container.runtime.sysInfo.MemoryLimit {
-		log.Printf("WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.\n")
-		container.Config.Memory = 0
-	}
-	if container.Config.Memory > 0 && !container.runtime.sysInfo.SwapLimit {
-		log.Printf("WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.\n")
-		container.Config.MemorySwap = -1
-	}
-
-	if container.runtime.sysInfo.IPv4ForwardingDisabled {
-		log.Printf("WARNING: IPv4 forwarding is disabled. Networking will not work")
+	if err := container.initializeNetworking(); err != nil {
+		return err
 	}
 	}
-
+	container.verifyDaemonSettings()
 	if err := prepareVolumesForContainer(container); err != nil {
 	if err := prepareVolumesForContainer(container); err != nil {
 		return err
 		return err
 	}
 	}
-
-	// Setup environment
-	env := []string{
-		"HOME=/",
-		"PATH=" + DefaultPathEnv,
-		"HOSTNAME=" + container.Config.Hostname,
-	}
-
-	if container.Config.Tty {
-		env = append(env, "TERM=xterm")
-	}
-
-	// Init any links between the parent and children
-	runtime := container.runtime
-
-	children, err := runtime.Children(container.Name)
+	linkedEnv, err := container.setupLinkedContainers()
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-
-	if len(children) > 0 {
-		container.activeLinks = make(map[string]*links.Link, len(children))
-
-		// If we encounter an error make sure that we rollback any network
-		// config and ip table changes
-		rollback := func() {
-			for _, link := range container.activeLinks {
-				link.Disable()
-			}
-			container.activeLinks = nil
-		}
-
-		for linkAlias, child := range children {
-			if !child.State.IsRunning() {
-				return fmt.Errorf("Cannot link to a non running container: %s AS %s", child.Name, linkAlias)
-			}
-
-			link, err := links.NewLink(
-				container.NetworkSettings.IPAddress,
-				child.NetworkSettings.IPAddress,
-				linkAlias,
-				child.Config.Env,
-				child.Config.ExposedPorts,
-				runtime.eng)
-
-			if err != nil {
-				rollback()
-				return err
-			}
-
-			container.activeLinks[link.Alias()] = link
-			if err := link.Enable(); err != nil {
-				rollback()
-				return err
-			}
-
-			for _, envVar := range link.ToEnv() {
-				env = append(env, envVar)
-			}
-		}
-	}
-
-	// because the env on the container can override certain default values
-	// we need to replace the 'env' keys where they match and append anything
-	// else.
-	env = utils.ReplaceOrAppendEnvValues(env, container.Config.Env)
-	if err := container.generateEnvConfig(env); err != nil {
+	if err := container.setupWorkingDirectory(); err != nil {
 		return err
 		return err
 	}
 	}
-
-	if container.Config.WorkingDir != "" {
-		container.Config.WorkingDir = path.Clean(container.Config.WorkingDir)
-
-		pthInfo, err := os.Stat(path.Join(container.basefs, container.Config.WorkingDir))
-		if err != nil {
-			if !os.IsNotExist(err) {
-				return err
-			}
-			if err := os.MkdirAll(path.Join(container.basefs, container.Config.WorkingDir), 0755); err != nil {
-				return err
-			}
-		}
-		if pthInfo != nil && !pthInfo.IsDir() {
-			return fmt.Errorf("Cannot mkdir: %s is not a directory", container.Config.WorkingDir)
-		}
-	}
-
-	envPath, err := container.EnvConfigPath()
-	if err != nil {
-		return err
-	}
-
-	populateCommand(container)
-	container.command.Env = env
-
-	if err := setupMountsForContainer(container, envPath); err != nil {
+	env := container.createDaemonEnvironment(linkedEnv)
+	if err := populateCommand(container, env); err != nil {
 		return err
 		return err
 	}
 	}
-
-	// Setup logging of stdout and stderr to disk
-	if err := container.runtime.LogToDisk(container.stdout, container.logPath("json"), "stdout"); err != nil {
+	if err := setupMountsForContainer(container); err != nil {
 		return err
 		return err
 	}
 	}
-	if err := container.runtime.LogToDisk(container.stderr, container.logPath("json"), "stderr"); err != nil {
+	if err := container.startLoggingToDisk(); err != nil {
 		return err
 		return err
 	}
 	}
 	container.waitLock = make(chan struct{})
 	container.waitLock = make(chan struct{})
 
 
-	callbackLock := make(chan struct{})
-	callback := func(command *execdriver.Command) {
-		container.State.SetRunning(command.Pid())
-		if command.Tty {
-			// The callback is called after the process Start()
-			// so we are in the parent process. In TTY mode, stdin/out/err is the PtySlace
-			// which we close here.
-			if c, ok := command.Stdout.(io.Closer); ok {
-				c.Close()
-			}
-		}
-		if err := container.ToDisk(); err != nil {
-			utils.Debugf("%s", err)
-		}
-		close(callbackLock)
-	}
-
-	// We use a callback here instead of a goroutine and an chan for
-	// syncronization purposes
-	cErr := utils.Go(func() error { return container.monitor(callback) })
-
-	// Start should not return until the process is actually running
-	select {
-	case <-callbackLock:
-	case err := <-cErr:
-		return err
-	}
-	return nil
+	return container.waitForStart()
 }
 }
 
 
 func (container *Container) Run() error {
 func (container *Container) Run() error {
@@ -651,74 +331,69 @@ func (container *Container) StderrPipe() (io.ReadCloser, error) {
 	return utils.NewBufReader(reader), nil
 	return utils.NewBufReader(reader), nil
 }
 }
 
 
-func (container *Container) buildHostnameAndHostsFiles(IP string) {
+func (container *Container) StdoutLogPipe() io.ReadCloser {
+	reader, writer := io.Pipe()
+	container.stdout.AddWriter(writer, "stdout")
+	return utils.NewBufReader(reader)
+}
+
+func (container *Container) StderrLogPipe() io.ReadCloser {
+	reader, writer := io.Pipe()
+	container.stderr.AddWriter(writer, "stderr")
+	return utils.NewBufReader(reader)
+}
+
+func (container *Container) buildHostnameFile() error {
 	container.HostnamePath = path.Join(container.root, "hostname")
 	container.HostnamePath = path.Join(container.root, "hostname")
-	ioutil.WriteFile(container.HostnamePath, []byte(container.Config.Hostname+"\n"), 0644)
+	if container.Config.Domainname != "" {
+		return ioutil.WriteFile(container.HostnamePath, []byte(fmt.Sprintf("%s.%s\n", container.Config.Hostname, container.Config.Domainname)), 0644)
+	}
+	return ioutil.WriteFile(container.HostnamePath, []byte(container.Config.Hostname+"\n"), 0644)
+}
 
 
-	hostsContent := []byte(`
-127.0.0.1	localhost
-::1		localhost ip6-localhost ip6-loopback
-fe00::0		ip6-localnet
-ff00::0		ip6-mcastprefix
-ff02::1		ip6-allnodes
-ff02::2		ip6-allrouters
-`)
+func (container *Container) buildHostnameAndHostsFiles(IP string) error {
+	if err := container.buildHostnameFile(); err != nil {
+		return err
+	}
 
 
 	container.HostsPath = path.Join(container.root, "hosts")
 	container.HostsPath = path.Join(container.root, "hosts")
 
 
-	if container.Config.Domainname != "" {
-		hostsContent = append([]byte(fmt.Sprintf("%s\t%s.%s %s\n", IP, container.Config.Hostname, container.Config.Domainname, container.Config.Hostname)), hostsContent...)
-	} else if !container.Config.NetworkDisabled {
-		hostsContent = append([]byte(fmt.Sprintf("%s\t%s\n", IP, container.Config.Hostname)), hostsContent...)
+	extraContent := make(map[string]string)
+
+	children, err := container.daemon.Children(container.Name)
+	if err != nil {
+		return err
 	}
 	}
 
 
-	ioutil.WriteFile(container.HostsPath, hostsContent, 0644)
+	for linkAlias, child := range children {
+		_, alias := path.Split(linkAlias)
+		extraContent[alias] = child.NetworkSettings.IPAddress
+	}
+
+	return etchosts.Build(container.HostsPath, IP, container.Config.Hostname, container.Config.Domainname, &extraContent)
 }
 }
 
 
 func (container *Container) allocateNetwork() error {
 func (container *Container) allocateNetwork() error {
-	if container.Config.NetworkDisabled {
+	mode := container.hostConfig.NetworkMode
+	if container.Config.NetworkDisabled || mode.IsContainer() || mode.IsHost() {
 		return nil
 		return nil
 	}
 	}
 
 
 	var (
 	var (
 		env *engine.Env
 		env *engine.Env
 		err error
 		err error
-		eng = container.runtime.eng
+		eng = container.daemon.eng
 	)
 	)
 
 
-	if container.State.IsGhost() {
-		if container.runtime.config.DisableNetwork {
-			env = &engine.Env{}
-		} else {
-			currentIP := container.NetworkSettings.IPAddress
-
-			job := eng.Job("allocate_interface", container.ID)
-			if currentIP != "" {
-				job.Setenv("RequestIP", currentIP)
-			}
-
-			env, err = job.Stdout.AddEnv()
-			if err != nil {
-				return err
-			}
-
-			if err := job.Run(); err != nil {
-				return err
-			}
-		}
-	} else {
-		job := eng.Job("allocate_interface", container.ID)
-		env, err = job.Stdout.AddEnv()
-		if err != nil {
-			return err
-		}
-		if err := job.Run(); err != nil {
-			return err
-		}
+	job := eng.Job("allocate_interface", container.ID)
+	if env, err = job.Stdout.AddEnv(); err != nil {
+		return err
+	}
+	if err := job.Run(); err != nil {
+		return err
 	}
 	}
 
 
 	if container.Config.PortSpecs != nil {
 	if container.Config.PortSpecs != nil {
-		utils.Debugf("Migrating port mappings for container: %s", strings.Join(container.Config.PortSpecs, ", "))
 		if err := migratePortMappings(container.Config, container.hostConfig); err != nil {
 		if err := migratePortMappings(container.Config, container.hostConfig); err != nil {
 			return err
 			return err
 		}
 		}
@@ -733,58 +408,23 @@ func (container *Container) allocateNetwork() error {
 		bindings  = make(nat.PortMap)
 		bindings  = make(nat.PortMap)
 	)
 	)
 
 
-	if !container.State.IsGhost() {
-		if container.Config.ExposedPorts != nil {
-			portSpecs = container.Config.ExposedPorts
-		}
-		if container.hostConfig.PortBindings != nil {
-			bindings = container.hostConfig.PortBindings
-		}
-	} else {
-		if container.NetworkSettings.Ports != nil {
-			for port, binding := range container.NetworkSettings.Ports {
-				portSpecs[port] = struct{}{}
-				bindings[port] = binding
-			}
-		}
+	if container.Config.ExposedPorts != nil {
+		portSpecs = container.Config.ExposedPorts
+	}
+	if container.hostConfig.PortBindings != nil {
+		bindings = container.hostConfig.PortBindings
 	}
 	}
 
 
 	container.NetworkSettings.PortMapping = nil
 	container.NetworkSettings.PortMapping = nil
 
 
 	for port := range portSpecs {
 	for port := range portSpecs {
-		binding := bindings[port]
-		if container.hostConfig.PublishAllPorts && len(binding) == 0 {
-			binding = append(binding, nat.PortBinding{})
-		}
-
-		for i := 0; i < len(binding); i++ {
-			b := binding[i]
-
-			portJob := eng.Job("allocate_port", container.ID)
-			portJob.Setenv("HostIP", b.HostIp)
-			portJob.Setenv("HostPort", b.HostPort)
-			portJob.Setenv("Proto", port.Proto())
-			portJob.Setenv("ContainerPort", port.Port())
-
-			portEnv, err := portJob.Stdout.AddEnv()
-			if err != nil {
-				return err
-			}
-			if err := portJob.Run(); err != nil {
-				eng.Job("release_interface", container.ID).Run()
-				return err
-			}
-			b.HostIp = portEnv.Get("HostIP")
-			b.HostPort = portEnv.Get("HostPort")
-
-			binding[i] = b
+		if err := container.allocatePort(eng, port, bindings); err != nil {
+			return err
 		}
 		}
-		bindings[port] = binding
 	}
 	}
 	container.WriteHostConfig()
 	container.WriteHostConfig()
 
 
 	container.NetworkSettings.Ports = bindings
 	container.NetworkSettings.Ports = bindings
-
 	container.NetworkSettings.Bridge = env.Get("Bridge")
 	container.NetworkSettings.Bridge = env.Get("Bridge")
 	container.NetworkSettings.IPAddress = env.Get("IP")
 	container.NetworkSettings.IPAddress = env.Get("IP")
 	container.NetworkSettings.IPPrefixLen = env.GetInt("IPPrefixLen")
 	container.NetworkSettings.IPPrefixLen = env.GetInt("IPPrefixLen")
@@ -797,7 +437,7 @@ func (container *Container) releaseNetwork() {
 	if container.Config.NetworkDisabled {
 	if container.Config.NetworkDisabled {
 		return
 		return
 	}
 	}
-	eng := container.runtime.eng
+	eng := container.daemon.eng
 
 
 	eng.Job("release_interface", container.ID).Run()
 	eng.Job("release_interface", container.ID).Run()
 	container.NetworkSettings = &NetworkSettings{}
 	container.NetworkSettings = &NetworkSettings{}
@@ -810,12 +450,12 @@ func (container *Container) monitor(callback execdriver.StartCallback) error {
 	)
 	)
 
 
 	pipes := execdriver.NewPipes(container.stdin, container.stdout, container.stderr, container.Config.OpenStdin)
 	pipes := execdriver.NewPipes(container.stdin, container.stdout, container.stderr, container.Config.OpenStdin)
-	exitCode, err = container.runtime.Run(container, pipes, callback)
+	exitCode, err = container.daemon.Run(container, pipes, callback)
 	if err != nil {
 	if err != nil {
 		utils.Errorf("Error running container: %s", err)
 		utils.Errorf("Error running container: %s", err)
 	}
 	}
 
 
-	if container.runtime != nil && container.runtime.srv != nil && container.runtime.srv.IsRunning() {
+	if container.daemon != nil && container.daemon.srv != nil && container.daemon.srv.IsRunning() {
 		container.State.SetStopped(exitCode)
 		container.State.SetStopped(exitCode)
 
 
 		// FIXME: there is a race condition here which causes this to fail during the unit tests.
 		// FIXME: there is a race condition here which causes this to fail during the unit tests.
@@ -838,8 +478,8 @@ func (container *Container) monitor(callback execdriver.StartCallback) error {
 		container.stdin, container.stdinPipe = io.Pipe()
 		container.stdin, container.stdinPipe = io.Pipe()
 	}
 	}
 
 
-	if container.runtime != nil && container.runtime.srv != nil {
-		container.runtime.srv.LogEvent("die", container.ID, container.runtime.repositories.ImageName(container.Image))
+	if container.daemon != nil && container.daemon.srv != nil {
+		container.daemon.srv.LogEvent("die", container.ID, container.daemon.repositories.ImageName(container.Image))
 	}
 	}
 
 
 	close(container.waitLock)
 	close(container.waitLock)
@@ -885,7 +525,7 @@ func (container *Container) KillSig(sig int) error {
 	if !container.State.IsRunning() {
 	if !container.State.IsRunning() {
 		return nil
 		return nil
 	}
 	}
-	return container.runtime.Kill(container, sig)
+	return container.daemon.Kill(container, sig)
 }
 }
 
 
 func (container *Container) Kill() error {
 func (container *Container) Kill() error {
@@ -900,9 +540,12 @@ func (container *Container) Kill() error {
 
 
 	// 2. Wait for the process to die, in last resort, try to kill the process directly
 	// 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 {
 	if err := container.WaitTimeout(10 * time.Second); err != nil {
-		log.Printf("Container %s failed to exit within 10 seconds of kill - trying direct SIGKILL", utils.TruncateID(container.ID))
-		if err := syscall.Kill(container.State.Pid, 9); err != nil {
-			return err
+		// Ensure that we don't kill ourselves
+		if pid := container.State.Pid; pid != 0 {
+			log.Printf("Container %s failed to exit within 10 seconds of kill - trying direct SIGKILL", utils.TruncateID(container.ID))
+			if err := syscall.Kill(pid, 9); err != nil {
+				return err
+			}
 		}
 		}
 	}
 	}
 
 
@@ -962,10 +605,10 @@ func (container *Container) ExportRw() (archive.Archive, error) {
 	if err := container.Mount(); err != nil {
 	if err := container.Mount(); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
-	if container.runtime == nil {
+	if container.daemon == nil {
 		return nil, fmt.Errorf("Can't load storage driver for unregistered container %s", container.ID)
 		return nil, fmt.Errorf("Can't load storage driver for unregistered container %s", container.ID)
 	}
 	}
-	archive, err := container.runtime.Diff(container)
+	archive, err := container.daemon.Diff(container)
 	if err != nil {
 	if err != nil {
 		container.Unmount()
 		container.Unmount()
 		return nil, err
 		return nil, err
@@ -1012,22 +655,22 @@ func (container *Container) WaitTimeout(timeout time.Duration) error {
 }
 }
 
 
 func (container *Container) Mount() error {
 func (container *Container) Mount() error {
-	return container.runtime.Mount(container)
+	return container.daemon.Mount(container)
 }
 }
 
 
 func (container *Container) Changes() ([]archive.Change, error) {
 func (container *Container) Changes() ([]archive.Change, error) {
-	return container.runtime.Changes(container)
+	return container.daemon.Changes(container)
 }
 }
 
 
 func (container *Container) GetImage() (*image.Image, error) {
 func (container *Container) GetImage() (*image.Image, error) {
-	if container.runtime == nil {
+	if container.daemon == nil {
 		return nil, fmt.Errorf("Can't get image of unregistered container")
 		return nil, fmt.Errorf("Can't get image of unregistered container")
 	}
 	}
-	return container.runtime.graph.Get(container.Image)
+	return container.daemon.graph.Get(container.Image)
 }
 }
 
 
 func (container *Container) Unmount() error {
 func (container *Container) Unmount() error {
-	return container.runtime.Unmount(container)
+	return container.daemon.Unmount(container)
 }
 }
 
 
 func (container *Container) logPath(name string) string {
 func (container *Container) logPath(name string) string {
@@ -1046,22 +689,6 @@ func (container *Container) jsonPath() string {
 	return path.Join(container.root, "config.json")
 	return path.Join(container.root, "config.json")
 }
 }
 
 
-func (container *Container) EnvConfigPath() (string, error) {
-	p := path.Join(container.root, "config.env")
-	if _, err := os.Stat(p); err != nil {
-		if os.IsNotExist(err) {
-			f, err := os.Create(p)
-			if err != nil {
-				return "", err
-			}
-			f.Close()
-		} else {
-			return "", err
-		}
-	}
-	return p, nil
-}
-
 // This method must be exported to be used from the lxc template
 // This method must be exported to be used from the lxc template
 // This directory is only usable when the container is running
 // This directory is only usable when the container is running
 func (container *Container) RootfsPath() string {
 func (container *Container) RootfsPath() string {
@@ -1080,7 +707,7 @@ func (container *Container) GetSize() (int64, int64) {
 	var (
 	var (
 		sizeRw, sizeRootfs int64
 		sizeRw, sizeRootfs int64
 		err                error
 		err                error
-		driver             = container.runtime.driver
+		driver             = container.daemon.driver
 	)
 	)
 
 
 	if err := container.Mount(); err != nil {
 	if err := container.Mount(); err != nil {
@@ -1089,7 +716,7 @@ func (container *Container) GetSize() (int64, int64) {
 	}
 	}
 	defer container.Unmount()
 	defer container.Unmount()
 
 
-	if differ, ok := container.runtime.driver.(graphdriver.Differ); ok {
+	if differ, ok := container.daemon.driver.(graphdriver.Differ); ok {
 		sizeRw, err = differ.DiffSize(container.ID)
 		sizeRw, err = differ.DiffSize(container.ID)
 		if err != nil {
 		if err != nil {
 			utils.Errorf("Warning: driver %s couldn't return diff size of container %s: %s", driver, container.ID, err)
 			utils.Errorf("Warning: driver %s couldn't return diff size of container %s: %s", driver, container.ID, err)
@@ -1182,48 +809,298 @@ func (container *Container) DisableLink(name string) {
 }
 }
 
 
 func (container *Container) setupContainerDns() error {
 func (container *Container) setupContainerDns() error {
+	if container.ResolvConfPath != "" {
+		return nil
+	}
+
 	var (
 	var (
-		config  = container.hostConfig
-		runtime = container.runtime
+		config = container.hostConfig
+		daemon = container.daemon
 	)
 	)
-	resolvConf, err := utils.GetResolvConf()
+
+	if config.NetworkMode == "host" {
+		container.ResolvConfPath = "/etc/resolv.conf"
+		return nil
+	}
+
+	resolvConf, err := resolvconf.Get()
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
+
 	// If custom dns exists, then create a resolv.conf for the container
 	// If custom dns exists, then create a resolv.conf for the container
-	if len(config.Dns) > 0 || len(runtime.config.Dns) > 0 || len(config.DnsSearch) > 0 || len(runtime.config.DnsSearch) > 0 {
+	if len(config.Dns) > 0 || len(daemon.config.Dns) > 0 || len(config.DnsSearch) > 0 || len(daemon.config.DnsSearch) > 0 {
 		var (
 		var (
-			dns       = utils.GetNameservers(resolvConf)
-			dnsSearch = utils.GetSearchDomains(resolvConf)
+			dns       = resolvconf.GetNameservers(resolvConf)
+			dnsSearch = resolvconf.GetSearchDomains(resolvConf)
 		)
 		)
 		if len(config.Dns) > 0 {
 		if len(config.Dns) > 0 {
 			dns = config.Dns
 			dns = config.Dns
-		} else if len(runtime.config.Dns) > 0 {
-			dns = runtime.config.Dns
+		} else if len(daemon.config.Dns) > 0 {
+			dns = daemon.config.Dns
 		}
 		}
 		if len(config.DnsSearch) > 0 {
 		if len(config.DnsSearch) > 0 {
 			dnsSearch = config.DnsSearch
 			dnsSearch = config.DnsSearch
-		} else if len(runtime.config.DnsSearch) > 0 {
-			dnsSearch = runtime.config.DnsSearch
+		} else if len(daemon.config.DnsSearch) > 0 {
+			dnsSearch = daemon.config.DnsSearch
 		}
 		}
 		container.ResolvConfPath = path.Join(container.root, "resolv.conf")
 		container.ResolvConfPath = path.Join(container.root, "resolv.conf")
-		f, err := os.Create(container.ResolvConfPath)
+		return resolvconf.Build(container.ResolvConfPath, dns, dnsSearch)
+	} else {
+		container.ResolvConfPath = "/etc/resolv.conf"
+	}
+	return nil
+}
+
+func (container *Container) initializeNetworking() error {
+	var err error
+	if container.hostConfig.NetworkMode.IsHost() {
+		container.Config.Hostname, err = os.Hostname()
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
-		defer f.Close()
-		for _, dns := range dns {
-			if _, err := f.Write([]byte("nameserver " + dns + "\n")); err != nil {
-				return err
+
+		parts := strings.SplitN(container.Config.Hostname, ".", 2)
+		if len(parts) > 1 {
+			container.Config.Hostname = parts[0]
+			container.Config.Domainname = parts[1]
+		}
+		container.HostsPath = "/etc/hosts"
+
+		return container.buildHostnameFile()
+	} else if container.hostConfig.NetworkMode.IsContainer() {
+		// we need to get the hosts files from the container to join
+		nc, err := container.getNetworkedContainer()
+		if err != nil {
+			return err
+		}
+		container.HostsPath = nc.HostsPath
+		container.ResolvConfPath = nc.ResolvConfPath
+		container.Config.Hostname = nc.Config.Hostname
+		container.Config.Domainname = nc.Config.Domainname
+	} else if container.daemon.config.DisableNetwork {
+		container.Config.NetworkDisabled = true
+		return container.buildHostnameAndHostsFiles("127.0.1.1")
+	} else {
+		if err := container.allocateNetwork(); err != nil {
+			return err
+		}
+		return container.buildHostnameAndHostsFiles(container.NetworkSettings.IPAddress)
+	}
+	return nil
+}
+
+// Make sure the config is compatible with the current kernel
+func (container *Container) verifyDaemonSettings() {
+	if container.Config.Memory > 0 && !container.daemon.sysInfo.MemoryLimit {
+		log.Printf("WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.\n")
+		container.Config.Memory = 0
+	}
+	if container.Config.Memory > 0 && !container.daemon.sysInfo.SwapLimit {
+		log.Printf("WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.\n")
+		container.Config.MemorySwap = -1
+	}
+	if container.daemon.sysInfo.IPv4ForwardingDisabled {
+		log.Printf("WARNING: IPv4 forwarding is disabled. Networking will not work")
+	}
+}
+
+func (container *Container) setupLinkedContainers() ([]string, error) {
+	var (
+		env    []string
+		daemon = container.daemon
+	)
+	children, err := daemon.Children(container.Name)
+	if err != nil {
+		return nil, err
+	}
+
+	if len(children) > 0 {
+		container.activeLinks = make(map[string]*links.Link, len(children))
+
+		// If we encounter an error make sure that we rollback any network
+		// config and ip table changes
+		rollback := func() {
+			for _, link := range container.activeLinks {
+				link.Disable()
+			}
+			container.activeLinks = nil
+		}
+
+		for linkAlias, child := range children {
+			if !child.State.IsRunning() {
+				return nil, fmt.Errorf("Cannot link to a non running container: %s AS %s", child.Name, linkAlias)
+			}
+
+			link, err := links.NewLink(
+				container.NetworkSettings.IPAddress,
+				child.NetworkSettings.IPAddress,
+				linkAlias,
+				child.Config.Env,
+				child.Config.ExposedPorts,
+				daemon.eng)
+
+			if err != nil {
+				rollback()
+				return nil, err
+			}
+
+			container.activeLinks[link.Alias()] = link
+			if err := link.Enable(); err != nil {
+				rollback()
+				return nil, err
+			}
+
+			for _, envVar := range link.ToEnv() {
+				env = append(env, envVar)
 			}
 			}
 		}
 		}
-		if len(dnsSearch) > 0 {
-			if _, err := f.Write([]byte("search " + strings.Join(dnsSearch, " ") + "\n")); err != nil {
+	}
+	return env, nil
+}
+
+func (container *Container) createDaemonEnvironment(linkedEnv []string) []string {
+	// Setup environment
+	env := []string{
+		"HOME=/",
+		"PATH=" + DefaultPathEnv,
+		"HOSTNAME=" + container.Config.Hostname,
+	}
+	if container.Config.Tty {
+		env = append(env, "TERM=xterm")
+	}
+	env = append(env, linkedEnv...)
+	// because the env on the container can override certain default values
+	// we need to replace the 'env' keys where they match and append anything
+	// else.
+	env = utils.ReplaceOrAppendEnvValues(env, container.Config.Env)
+
+	return env
+}
+
+func (container *Container) setupWorkingDirectory() error {
+	if container.Config.WorkingDir != "" {
+		container.Config.WorkingDir = path.Clean(container.Config.WorkingDir)
+
+		pthInfo, err := os.Stat(path.Join(container.basefs, container.Config.WorkingDir))
+		if err != nil {
+			if !os.IsNotExist(err) {
+				return err
+			}
+			if err := os.MkdirAll(path.Join(container.basefs, container.Config.WorkingDir), 0755); err != nil {
 				return err
 				return err
 			}
 			}
 		}
 		}
-	} else {
-		container.ResolvConfPath = "/etc/resolv.conf"
+		if pthInfo != nil && !pthInfo.IsDir() {
+			return fmt.Errorf("Cannot mkdir: %s is not a directory", container.Config.WorkingDir)
+		}
+	}
+	return nil
+}
+
+func (container *Container) startLoggingToDisk() error {
+	// Setup logging of stdout and stderr to disk
+	if err := container.daemon.LogToDisk(container.stdout, container.logPath("json"), "stdout"); err != nil {
+		return err
+	}
+	if err := container.daemon.LogToDisk(container.stderr, container.logPath("json"), "stderr"); err != nil {
+		return err
 	}
 	}
 	return nil
 	return nil
 }
 }
+
+func (container *Container) waitForStart() error {
+	callbackLock := make(chan struct{})
+	callback := func(command *execdriver.Command) {
+		container.State.SetRunning(command.Pid())
+		if command.Tty {
+			// The callback is called after the process Start()
+			// so we are in the parent process. In TTY mode, stdin/out/err is the PtySlace
+			// which we close here.
+			if c, ok := command.Stdout.(io.Closer); ok {
+				c.Close()
+			}
+		}
+		if err := container.ToDisk(); err != nil {
+			utils.Debugf("%s", err)
+		}
+		close(callbackLock)
+	}
+
+	// We use a callback here instead of a goroutine and an chan for
+	// syncronization purposes
+	cErr := utils.Go(func() error { return container.monitor(callback) })
+
+	// Start should not return until the process is actually running
+	select {
+	case <-callbackLock:
+	case err := <-cErr:
+		return err
+	}
+	return nil
+}
+
+func (container *Container) allocatePort(eng *engine.Engine, port nat.Port, bindings nat.PortMap) error {
+	binding := bindings[port]
+	if container.hostConfig.PublishAllPorts && len(binding) == 0 {
+		binding = append(binding, nat.PortBinding{})
+	}
+
+	for i := 0; i < len(binding); i++ {
+		b := binding[i]
+
+		job := eng.Job("allocate_port", container.ID)
+		job.Setenv("HostIP", b.HostIp)
+		job.Setenv("HostPort", b.HostPort)
+		job.Setenv("Proto", port.Proto())
+		job.Setenv("ContainerPort", port.Port())
+
+		portEnv, err := job.Stdout.AddEnv()
+		if err != nil {
+			return err
+		}
+		if err := job.Run(); err != nil {
+			eng.Job("release_interface", container.ID).Run()
+			return err
+		}
+		b.HostIp = portEnv.Get("HostIP")
+		b.HostPort = portEnv.Get("HostPort")
+
+		binding[i] = b
+	}
+	bindings[port] = binding
+	return nil
+}
+
+func (container *Container) GetProcessLabel() string {
+	// even if we have a process label return "" if we are running
+	// in privileged mode
+	if container.hostConfig.Privileged {
+		return ""
+	}
+	return container.ProcessLabel
+}
+
+func (container *Container) GetMountLabel() string {
+	if container.hostConfig.Privileged {
+		return ""
+	}
+	return container.MountLabel
+}
+
+func (container *Container) getNetworkedContainer() (*Container, error) {
+	parts := strings.SplitN(string(container.hostConfig.NetworkMode), ":", 2)
+	switch parts[0] {
+	case "container":
+		nc := container.daemon.Get(parts[1])
+		if nc == nil {
+			return nil, fmt.Errorf("no such container to join network: %s", parts[1])
+		}
+		if !nc.State.IsRunning() {
+			return nil, fmt.Errorf("cannot join network of a non running container: %s", parts[1])
+		}
+		return nc, nil
+	default:
+		return nil, fmt.Errorf("network mode not set to container")
+	}
+}

+ 1 - 1
runtime/container_unit_test.go → daemon/container_unit_test.go

@@ -1,4 +1,4 @@
-package runtime
+package daemon
 
 
 import (
 import (
 	"github.com/dotcloud/docker/nat"
 	"github.com/dotcloud/docker/nat"

+ 215 - 214
runtime/runtime.go → daemon/daemon.go

@@ -1,36 +1,39 @@
-package runtime
+package daemon
 
 
 import (
 import (
 	"container/list"
 	"container/list"
 	"fmt"
 	"fmt"
+	"io"
+	"io/ioutil"
+	"log"
+	"os"
+	"path"
+	"regexp"
+	"strings"
+	"sync"
+	"time"
+
 	"github.com/dotcloud/docker/archive"
 	"github.com/dotcloud/docker/archive"
+	"github.com/dotcloud/docker/daemon/execdriver"
+	"github.com/dotcloud/docker/daemon/execdriver/execdrivers"
+	"github.com/dotcloud/docker/daemon/execdriver/lxc"
+	"github.com/dotcloud/docker/daemon/graphdriver"
+	_ "github.com/dotcloud/docker/daemon/graphdriver/vfs"
+	_ "github.com/dotcloud/docker/daemon/networkdriver/bridge"
+	"github.com/dotcloud/docker/daemon/networkdriver/portallocator"
 	"github.com/dotcloud/docker/daemonconfig"
 	"github.com/dotcloud/docker/daemonconfig"
 	"github.com/dotcloud/docker/dockerversion"
 	"github.com/dotcloud/docker/dockerversion"
 	"github.com/dotcloud/docker/engine"
 	"github.com/dotcloud/docker/engine"
 	"github.com/dotcloud/docker/graph"
 	"github.com/dotcloud/docker/graph"
 	"github.com/dotcloud/docker/image"
 	"github.com/dotcloud/docker/image"
 	"github.com/dotcloud/docker/pkg/graphdb"
 	"github.com/dotcloud/docker/pkg/graphdb"
+	"github.com/dotcloud/docker/pkg/label"
 	"github.com/dotcloud/docker/pkg/mount"
 	"github.com/dotcloud/docker/pkg/mount"
+	"github.com/dotcloud/docker/pkg/networkfs/resolvconf"
 	"github.com/dotcloud/docker/pkg/selinux"
 	"github.com/dotcloud/docker/pkg/selinux"
 	"github.com/dotcloud/docker/pkg/sysinfo"
 	"github.com/dotcloud/docker/pkg/sysinfo"
 	"github.com/dotcloud/docker/runconfig"
 	"github.com/dotcloud/docker/runconfig"
-	"github.com/dotcloud/docker/runtime/execdriver"
-	"github.com/dotcloud/docker/runtime/execdriver/execdrivers"
-	"github.com/dotcloud/docker/runtime/execdriver/lxc"
-	"github.com/dotcloud/docker/runtime/graphdriver"
-	_ "github.com/dotcloud/docker/runtime/graphdriver/vfs"
-	_ "github.com/dotcloud/docker/runtime/networkdriver/bridge"
-	"github.com/dotcloud/docker/runtime/networkdriver/portallocator"
 	"github.com/dotcloud/docker/utils"
 	"github.com/dotcloud/docker/utils"
-	"io"
-	"io/ioutil"
-	"log"
-	"os"
-	"path"
-	"regexp"
-	"strings"
-	"sync"
-	"time"
 )
 )
 
 
 // Set the max depth to the aufs default that most
 // Set the max depth to the aufs default that most
@@ -44,7 +47,7 @@ var (
 	validContainerNamePattern = regexp.MustCompile(`^/?` + validContainerNameChars + `+$`)
 	validContainerNamePattern = regexp.MustCompile(`^/?` + validContainerNameChars + `+$`)
 )
 )
 
 
-type Runtime struct {
+type Daemon struct {
 	repository     string
 	repository     string
 	sysInitPath    string
 	sysInitPath    string
 	containers     *list.List
 	containers     *list.List
@@ -76,17 +79,17 @@ func remountPrivate(mountPoint string) error {
 	return mount.ForceMount("", mountPoint, "none", "private")
 	return mount.ForceMount("", mountPoint, "none", "private")
 }
 }
 
 
-// List returns an array of all containers registered in the runtime.
-func (runtime *Runtime) List() []*Container {
+// List returns an array of all containers registered in the daemon.
+func (daemon *Daemon) List() []*Container {
 	containers := new(History)
 	containers := new(History)
-	for e := runtime.containers.Front(); e != nil; e = e.Next() {
+	for e := daemon.containers.Front(); e != nil; e = e.Next() {
 		containers.Add(e.Value.(*Container))
 		containers.Add(e.Value.(*Container))
 	}
 	}
 	return *containers
 	return *containers
 }
 }
 
 
-func (runtime *Runtime) getContainerElement(id string) *list.Element {
-	for e := runtime.containers.Front(); e != nil; e = e.Next() {
+func (daemon *Daemon) getContainerElement(id string) *list.Element {
+	for e := daemon.containers.Front(); e != nil; e = e.Next() {
 		container := e.Value.(*Container)
 		container := e.Value.(*Container)
 		if container.ID == id {
 		if container.ID == id {
 			return e
 			return e
@@ -97,17 +100,17 @@ func (runtime *Runtime) getContainerElement(id string) *list.Element {
 
 
 // Get looks for a container by the specified ID or name, and returns it.
 // Get looks for a container by the specified ID or name, and returns it.
 // If the container is not found, or if an error occurs, nil is returned.
 // If the container is not found, or if an error occurs, nil is returned.
-func (runtime *Runtime) Get(name string) *Container {
-	if c, _ := runtime.GetByName(name); c != nil {
+func (daemon *Daemon) Get(name string) *Container {
+	if c, _ := daemon.GetByName(name); c != nil {
 		return c
 		return c
 	}
 	}
 
 
-	id, err := runtime.idIndex.Get(name)
+	id, err := daemon.idIndex.Get(name)
 	if err != nil {
 	if err != nil {
 		return nil
 		return nil
 	}
 	}
 
 
-	e := runtime.getContainerElement(id)
+	e := daemon.getContainerElement(id)
 	if e == nil {
 	if e == nil {
 		return nil
 		return nil
 	}
 	}
@@ -116,43 +119,40 @@ func (runtime *Runtime) Get(name string) *Container {
 
 
 // Exists returns a true if a container of the specified ID or name exists,
 // Exists returns a true if a container of the specified ID or name exists,
 // false otherwise.
 // false otherwise.
-func (runtime *Runtime) Exists(id string) bool {
-	return runtime.Get(id) != nil
+func (daemon *Daemon) Exists(id string) bool {
+	return daemon.Get(id) != nil
 }
 }
 
 
-func (runtime *Runtime) containerRoot(id string) string {
-	return path.Join(runtime.repository, id)
+func (daemon *Daemon) containerRoot(id string) string {
+	return path.Join(daemon.repository, id)
 }
 }
 
 
 // Load reads the contents of a container from disk
 // Load reads the contents of a container from disk
 // This is typically done at startup.
 // This is typically done at startup.
-func (runtime *Runtime) load(id string) (*Container, error) {
-	container := &Container{root: runtime.containerRoot(id)}
+func (daemon *Daemon) load(id string) (*Container, error) {
+	container := &Container{root: daemon.containerRoot(id)}
 	if err := container.FromDisk(); err != nil {
 	if err := container.FromDisk(); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 	if container.ID != id {
 	if container.ID != id {
 		return container, fmt.Errorf("Container %s is stored at %s", container.ID, id)
 		return container, fmt.Errorf("Container %s is stored at %s", container.ID, id)
 	}
 	}
-	if container.State.IsRunning() {
-		container.State.SetGhost(true)
-	}
 	return container, nil
 	return container, nil
 }
 }
 
 
-// Register makes a container object usable by the runtime as <container.ID>
-func (runtime *Runtime) Register(container *Container) error {
-	if container.runtime != nil || runtime.Exists(container.ID) {
+// Register makes a container object usable by the daemon as <container.ID>
+func (daemon *Daemon) Register(container *Container) error {
+	if container.daemon != nil || daemon.Exists(container.ID) {
 		return fmt.Errorf("Container is already loaded")
 		return fmt.Errorf("Container is already loaded")
 	}
 	}
 	if err := validateID(container.ID); err != nil {
 	if err := validateID(container.ID); err != nil {
 		return err
 		return err
 	}
 	}
-	if err := runtime.ensureName(container); err != nil {
+	if err := daemon.ensureName(container); err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	container.runtime = runtime
+	container.daemon = daemon
 
 
 	// Attach to stdout and stderr
 	// Attach to stdout and stderr
 	container.stderr = utils.NewWriteBroadcaster()
 	container.stderr = utils.NewWriteBroadcaster()
@@ -164,55 +164,50 @@ func (runtime *Runtime) Register(container *Container) error {
 		container.stdinPipe = utils.NopWriteCloser(ioutil.Discard) // Silently drop stdin
 		container.stdinPipe = utils.NopWriteCloser(ioutil.Discard) // Silently drop stdin
 	}
 	}
 	// done
 	// done
-	runtime.containers.PushBack(container)
-	runtime.idIndex.Add(container.ID)
+	daemon.containers.PushBack(container)
+	daemon.idIndex.Add(container.ID)
 
 
 	// FIXME: if the container is supposed to be running but is not, auto restart it?
 	// FIXME: if the container is supposed to be running but is not, auto restart it?
 	//        if so, then we need to restart monitor and init a new lock
 	//        if so, then we need to restart monitor and init a new lock
 	// If the container is supposed to be running, make sure of it
 	// If the container is supposed to be running, make sure of it
 	if container.State.IsRunning() {
 	if container.State.IsRunning() {
-		if container.State.IsGhost() {
-			utils.Debugf("killing ghost %s", container.ID)
+		utils.Debugf("killing old running container %s", container.ID)
 
 
-			existingPid := container.State.Pid
-			container.State.SetGhost(false)
-			container.State.SetStopped(0)
+		existingPid := container.State.Pid
+		container.State.SetStopped(0)
 
 
-			// We only have to handle this for lxc because the other drivers will ensure that
-			// no ghost processes are left when docker dies
-			if container.ExecDriver == "" || strings.Contains(container.ExecDriver, "lxc") {
-				lxc.KillLxc(container.ID, 9)
-			} else {
-				// use the current driver and ensure that the container is dead x.x
-				cmd := &execdriver.Command{
-					ID: container.ID,
-				}
-				var err error
-				cmd.Process, err = os.FindProcess(existingPid)
-				if err != nil {
-					utils.Debugf("cannot find existing process for %d", existingPid)
-				}
-				runtime.execDriver.Terminate(cmd)
-			}
-			if err := container.Unmount(); err != nil {
-				utils.Debugf("ghost unmount error %s", err)
+		// We only have to handle this for lxc because the other drivers will ensure that
+		// no processes are left when docker dies
+		if container.ExecDriver == "" || strings.Contains(container.ExecDriver, "lxc") {
+			lxc.KillLxc(container.ID, 9)
+		} else {
+			// use the current driver and ensure that the container is dead x.x
+			cmd := &execdriver.Command{
+				ID: container.ID,
 			}
 			}
-			if err := container.ToDisk(); err != nil {
-				utils.Debugf("saving ghost state to disk %s", err)
+			var err error
+			cmd.Process, err = os.FindProcess(existingPid)
+			if err != nil {
+				utils.Debugf("cannot find existing process for %d", existingPid)
 			}
 			}
+			daemon.execDriver.Terminate(cmd)
+		}
+		if err := container.Unmount(); err != nil {
+			utils.Debugf("unmount error %s", err)
+		}
+		if err := container.ToDisk(); err != nil {
+			utils.Debugf("saving stopped state to disk %s", err)
 		}
 		}
 
 
-		info := runtime.execDriver.Info(container.ID)
+		info := daemon.execDriver.Info(container.ID)
 		if !info.IsRunning() {
 		if !info.IsRunning() {
 			utils.Debugf("Container %s was supposed to be running but is not.", container.ID)
 			utils.Debugf("Container %s was supposed to be running but is not.", container.ID)
-			if runtime.config.AutoRestart {
+			if daemon.config.AutoRestart {
 				utils.Debugf("Restarting")
 				utils.Debugf("Restarting")
 				if err := container.Unmount(); err != nil {
 				if err := container.Unmount(); err != nil {
 					utils.Debugf("restart unmount error %s", err)
 					utils.Debugf("restart unmount error %s", err)
 				}
 				}
 
 
-				container.State.SetGhost(false)
-				container.State.SetStopped(0)
 				if err := container.Start(); err != nil {
 				if err := container.Start(); err != nil {
 					return err
 					return err
 				}
 				}
@@ -234,9 +229,9 @@ func (runtime *Runtime) Register(container *Container) error {
 	return nil
 	return nil
 }
 }
 
 
-func (runtime *Runtime) ensureName(container *Container) error {
+func (daemon *Daemon) ensureName(container *Container) error {
 	if container.Name == "" {
 	if container.Name == "" {
-		name, err := generateRandomName(runtime)
+		name, err := generateRandomName(daemon)
 		if err != nil {
 		if err != nil {
 			name = utils.TruncateID(container.ID)
 			name = utils.TruncateID(container.ID)
 		}
 		}
@@ -245,8 +240,8 @@ func (runtime *Runtime) ensureName(container *Container) error {
 		if err := container.ToDisk(); err != nil {
 		if err := container.ToDisk(); err != nil {
 			utils.Debugf("Error saving container name %s", err)
 			utils.Debugf("Error saving container name %s", err)
 		}
 		}
-		if !runtime.containerGraph.Exists(name) {
-			if _, err := runtime.containerGraph.Set(name, container.ID); err != nil {
+		if !daemon.containerGraph.Exists(name) {
+			if _, err := daemon.containerGraph.Set(name, container.ID); err != nil {
 				utils.Debugf("Setting default id - %s", err)
 				utils.Debugf("Setting default id - %s", err)
 			}
 			}
 		}
 		}
@@ -254,7 +249,7 @@ func (runtime *Runtime) ensureName(container *Container) error {
 	return nil
 	return nil
 }
 }
 
 
-func (runtime *Runtime) LogToDisk(src *utils.WriteBroadcaster, dst, stream string) error {
+func (daemon *Daemon) LogToDisk(src *utils.WriteBroadcaster, dst, stream string) error {
 	log, err := os.OpenFile(dst, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600)
 	log, err := os.OpenFile(dst, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
@@ -263,13 +258,13 @@ func (runtime *Runtime) LogToDisk(src *utils.WriteBroadcaster, dst, stream strin
 	return nil
 	return nil
 }
 }
 
 
-// Destroy unregisters a container from the runtime and cleanly removes its contents from the filesystem.
-func (runtime *Runtime) Destroy(container *Container) error {
+// Destroy unregisters a container from the daemon and cleanly removes its contents from the filesystem.
+func (daemon *Daemon) Destroy(container *Container) error {
 	if container == nil {
 	if container == nil {
 		return fmt.Errorf("The given container is <nil>")
 		return fmt.Errorf("The given container is <nil>")
 	}
 	}
 
 
-	element := runtime.getContainerElement(container.ID)
+	element := daemon.getContainerElement(container.ID)
 	if element == nil {
 	if element == nil {
 		return fmt.Errorf("Container %v not found - maybe it was already destroyed?", container.ID)
 		return fmt.Errorf("Container %v not found - maybe it was already destroyed?", container.ID)
 	}
 	}
@@ -278,42 +273,45 @@ func (runtime *Runtime) Destroy(container *Container) error {
 		return err
 		return err
 	}
 	}
 
 
-	if err := runtime.driver.Remove(container.ID); err != nil {
-		return fmt.Errorf("Driver %s failed to remove root filesystem %s: %s", runtime.driver, container.ID, err)
+	// Deregister the container before removing its directory, to avoid race conditions
+	daemon.idIndex.Delete(container.ID)
+	daemon.containers.Remove(element)
+
+	if err := daemon.driver.Remove(container.ID); err != nil {
+		return fmt.Errorf("Driver %s failed to remove root filesystem %s: %s", daemon.driver, container.ID, err)
 	}
 	}
 
 
 	initID := fmt.Sprintf("%s-init", container.ID)
 	initID := fmt.Sprintf("%s-init", container.ID)
-	if err := runtime.driver.Remove(initID); err != nil {
-		return fmt.Errorf("Driver %s failed to remove init filesystem %s: %s", runtime.driver, initID, err)
+	if err := daemon.driver.Remove(initID); err != nil {
+		return fmt.Errorf("Driver %s failed to remove init filesystem %s: %s", daemon.driver, initID, err)
 	}
 	}
 
 
-	if _, err := runtime.containerGraph.Purge(container.ID); err != nil {
+	if _, err := daemon.containerGraph.Purge(container.ID); err != nil {
 		utils.Debugf("Unable to remove container from link graph: %s", err)
 		utils.Debugf("Unable to remove container from link graph: %s", err)
 	}
 	}
 
 
-	// Deregister the container before removing its directory, to avoid race conditions
-	runtime.idIndex.Delete(container.ID)
-	runtime.containers.Remove(element)
 	if err := os.RemoveAll(container.root); err != nil {
 	if err := os.RemoveAll(container.root); err != nil {
 		return fmt.Errorf("Unable to remove filesystem for %v: %v", container.ID, err)
 		return fmt.Errorf("Unable to remove filesystem for %v: %v", container.ID, err)
 	}
 	}
+	selinux.FreeLxcContexts(container.ProcessLabel)
+
 	return nil
 	return nil
 }
 }
 
 
-func (runtime *Runtime) restore() error {
+func (daemon *Daemon) restore() error {
 	if os.Getenv("DEBUG") == "" && os.Getenv("TEST") == "" {
 	if os.Getenv("DEBUG") == "" && os.Getenv("TEST") == "" {
 		fmt.Printf("Loading containers: ")
 		fmt.Printf("Loading containers: ")
 	}
 	}
-	dir, err := ioutil.ReadDir(runtime.repository)
+	dir, err := ioutil.ReadDir(daemon.repository)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 	containers := make(map[string]*Container)
 	containers := make(map[string]*Container)
-	currentDriver := runtime.driver.String()
+	currentDriver := daemon.driver.String()
 
 
 	for _, v := range dir {
 	for _, v := range dir {
 		id := v.Name()
 		id := v.Name()
-		container, err := runtime.load(id)
+		container, err := daemon.load(id)
 		if os.Getenv("DEBUG") == "" && os.Getenv("TEST") == "" {
 		if os.Getenv("DEBUG") == "" && os.Getenv("TEST") == "" {
 			fmt.Print(".")
 			fmt.Print(".")
 		}
 		}
@@ -332,12 +330,12 @@ func (runtime *Runtime) restore() error {
 	}
 	}
 
 
 	register := func(container *Container) {
 	register := func(container *Container) {
-		if err := runtime.Register(container); err != nil {
+		if err := daemon.Register(container); err != nil {
 			utils.Debugf("Failed to register container %s: %s", container.ID, err)
 			utils.Debugf("Failed to register container %s: %s", container.ID, err)
 		}
 		}
 	}
 	}
 
 
-	if entities := runtime.containerGraph.List("/", -1); entities != nil {
+	if entities := daemon.containerGraph.List("/", -1); entities != nil {
 		for _, p := range entities.Paths() {
 		for _, p := range entities.Paths() {
 			if os.Getenv("DEBUG") == "" && os.Getenv("TEST") == "" {
 			if os.Getenv("DEBUG") == "" && os.Getenv("TEST") == "" {
 				fmt.Print(".")
 				fmt.Print(".")
@@ -353,12 +351,12 @@ func (runtime *Runtime) restore() error {
 	// Any containers that are left over do not exist in the graph
 	// Any containers that are left over do not exist in the graph
 	for _, container := range containers {
 	for _, container := range containers {
 		// Try to set the default name for a container if it exists prior to links
 		// Try to set the default name for a container if it exists prior to links
-		container.Name, err = generateRandomName(runtime)
+		container.Name, err = generateRandomName(daemon)
 		if err != nil {
 		if err != nil {
 			container.Name = utils.TruncateID(container.ID)
 			container.Name = utils.TruncateID(container.ID)
 		}
 		}
 
 
-		if _, err := runtime.containerGraph.Set(container.Name, container.ID); err != nil {
+		if _, err := daemon.containerGraph.Set(container.Name, container.ID); err != nil {
 			utils.Debugf("Setting default id - %s", err)
 			utils.Debugf("Setting default id - %s", err)
 		}
 		}
 		register(container)
 		register(container)
@@ -372,38 +370,38 @@ func (runtime *Runtime) restore() error {
 }
 }
 
 
 // Create creates a new container from the given configuration with a given name.
 // Create creates a new container from the given configuration with a given name.
-func (runtime *Runtime) Create(config *runconfig.Config, name string) (*Container, []string, error) {
+func (daemon *Daemon) Create(config *runconfig.Config, name string) (*Container, []string, error) {
 	var (
 	var (
 		container *Container
 		container *Container
 		warnings  []string
 		warnings  []string
 	)
 	)
 
 
-	img, err := runtime.repositories.LookupImage(config.Image)
+	img, err := daemon.repositories.LookupImage(config.Image)
 	if err != nil {
 	if err != nil {
 		return nil, nil, err
 		return nil, nil, err
 	}
 	}
-	if err := runtime.checkImageDepth(img); err != nil {
+	if err := daemon.checkImageDepth(img); err != nil {
 		return nil, nil, err
 		return nil, nil, err
 	}
 	}
-	if warnings, err = runtime.mergeAndVerifyConfig(config, img); err != nil {
+	if warnings, err = daemon.mergeAndVerifyConfig(config, img); err != nil {
 		return nil, nil, err
 		return nil, nil, err
 	}
 	}
-	if container, err = runtime.newContainer(name, config, img); err != nil {
+	if container, err = daemon.newContainer(name, config, img); err != nil {
 		return nil, nil, err
 		return nil, nil, err
 	}
 	}
-	if err := runtime.createRootfs(container, img); err != nil {
+	if err := daemon.createRootfs(container, img); err != nil {
 		return nil, nil, err
 		return nil, nil, err
 	}
 	}
 	if err := container.ToDisk(); err != nil {
 	if err := container.ToDisk(); err != nil {
 		return nil, nil, err
 		return nil, nil, err
 	}
 	}
-	if err := runtime.Register(container); err != nil {
+	if err := daemon.Register(container); err != nil {
 		return nil, nil, err
 		return nil, nil, err
 	}
 	}
 	return container, warnings, nil
 	return container, warnings, nil
 }
 }
 
 
-func (runtime *Runtime) checkImageDepth(img *image.Image) error {
+func (daemon *Daemon) checkImageDepth(img *image.Image) error {
 	// We add 2 layers to the depth because the container's rw and
 	// We add 2 layers to the depth because the container's rw and
 	// init layer add to the restriction
 	// init layer add to the restriction
 	depth, err := img.Depth()
 	depth, err := img.Depth()
@@ -416,7 +414,7 @@ func (runtime *Runtime) checkImageDepth(img *image.Image) error {
 	return nil
 	return nil
 }
 }
 
 
-func (runtime *Runtime) checkDeprecatedExpose(config *runconfig.Config) bool {
+func (daemon *Daemon) checkDeprecatedExpose(config *runconfig.Config) bool {
 	if config != nil {
 	if config != nil {
 		if config.PortSpecs != nil {
 		if config.PortSpecs != nil {
 			for _, p := range config.PortSpecs {
 			for _, p := range config.PortSpecs {
@@ -429,9 +427,9 @@ func (runtime *Runtime) checkDeprecatedExpose(config *runconfig.Config) bool {
 	return false
 	return false
 }
 }
 
 
-func (runtime *Runtime) mergeAndVerifyConfig(config *runconfig.Config, img *image.Image) ([]string, error) {
+func (daemon *Daemon) mergeAndVerifyConfig(config *runconfig.Config, img *image.Image) ([]string, error) {
 	warnings := []string{}
 	warnings := []string{}
-	if runtime.checkDeprecatedExpose(img.Config) || runtime.checkDeprecatedExpose(config) {
+	if daemon.checkDeprecatedExpose(img.Config) || daemon.checkDeprecatedExpose(config) {
 		warnings = append(warnings, "The mapping to public ports on your host via Dockerfile EXPOSE (host:port:port) has been deprecated. Use -p to publish the ports.")
 		warnings = append(warnings, "The mapping to public ports on your host via Dockerfile EXPOSE (host:port:port) has been deprecated. Use -p to publish the ports.")
 	}
 	}
 	if img.Config != nil {
 	if img.Config != nil {
@@ -445,14 +443,14 @@ func (runtime *Runtime) mergeAndVerifyConfig(config *runconfig.Config, img *imag
 	return warnings, nil
 	return warnings, nil
 }
 }
 
 
-func (runtime *Runtime) generateIdAndName(name string) (string, string, error) {
+func (daemon *Daemon) generateIdAndName(name string) (string, string, error) {
 	var (
 	var (
 		err error
 		err error
 		id  = utils.GenerateRandomID()
 		id  = utils.GenerateRandomID()
 	)
 	)
 
 
 	if name == "" {
 	if name == "" {
-		name, err = generateRandomName(runtime)
+		name, err = generateRandomName(daemon)
 		if err != nil {
 		if err != nil {
 			name = utils.TruncateID(id)
 			name = utils.TruncateID(id)
 		}
 		}
@@ -465,19 +463,19 @@ func (runtime *Runtime) generateIdAndName(name string) (string, string, error) {
 		name = "/" + name
 		name = "/" + name
 	}
 	}
 	// Set the enitity in the graph using the default name specified
 	// Set the enitity in the graph using the default name specified
-	if _, err := runtime.containerGraph.Set(name, id); err != nil {
+	if _, err := daemon.containerGraph.Set(name, id); err != nil {
 		if !graphdb.IsNonUniqueNameError(err) {
 		if !graphdb.IsNonUniqueNameError(err) {
 			return "", "", err
 			return "", "", err
 		}
 		}
 
 
-		conflictingContainer, err := runtime.GetByName(name)
+		conflictingContainer, err := daemon.GetByName(name)
 		if err != nil {
 		if err != nil {
 			if strings.Contains(err.Error(), "Could not find entity") {
 			if strings.Contains(err.Error(), "Could not find entity") {
 				return "", "", err
 				return "", "", err
 			}
 			}
 
 
 			// Remove name and continue starting the container
 			// Remove name and continue starting the container
-			if err := runtime.containerGraph.Delete(name); err != nil {
+			if err := daemon.containerGraph.Delete(name); err != nil {
 				return "", "", err
 				return "", "", err
 			}
 			}
 		} else {
 		} else {
@@ -490,7 +488,7 @@ func (runtime *Runtime) generateIdAndName(name string) (string, string, error) {
 	return id, name, nil
 	return id, name, nil
 }
 }
 
 
-func (runtime *Runtime) generateHostname(id string, config *runconfig.Config) {
+func (daemon *Daemon) generateHostname(id string, config *runconfig.Config) {
 	// Generate default hostname
 	// Generate default hostname
 	// FIXME: the lxc template no longer needs to set a default hostname
 	// FIXME: the lxc template no longer needs to set a default hostname
 	if config.Hostname == "" {
 	if config.Hostname == "" {
@@ -498,7 +496,7 @@ func (runtime *Runtime) generateHostname(id string, config *runconfig.Config) {
 	}
 	}
 }
 }
 
 
-func (runtime *Runtime) getEntrypointAndArgs(config *runconfig.Config) (string, []string) {
+func (daemon *Daemon) getEntrypointAndArgs(config *runconfig.Config) (string, []string) {
 	var (
 	var (
 		entrypoint string
 		entrypoint string
 		args       []string
 		args       []string
@@ -513,18 +511,18 @@ func (runtime *Runtime) getEntrypointAndArgs(config *runconfig.Config) (string,
 	return entrypoint, args
 	return entrypoint, args
 }
 }
 
 
-func (runtime *Runtime) newContainer(name string, config *runconfig.Config, img *image.Image) (*Container, error) {
+func (daemon *Daemon) newContainer(name string, config *runconfig.Config, img *image.Image) (*Container, error) {
 	var (
 	var (
 		id  string
 		id  string
 		err error
 		err error
 	)
 	)
-	id, name, err = runtime.generateIdAndName(name)
+	id, name, err = daemon.generateIdAndName(name)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
-	runtime.generateHostname(id, config)
-	entrypoint, args := runtime.getEntrypointAndArgs(config)
+	daemon.generateHostname(id, config)
+	entrypoint, args := daemon.getEntrypointAndArgs(config)
 
 
 	container := &Container{
 	container := &Container{
 		// FIXME: we should generate the ID here instead of receiving it as an argument
 		// FIXME: we should generate the ID here instead of receiving it as an argument
@@ -537,34 +535,38 @@ func (runtime *Runtime) newContainer(name string, config *runconfig.Config, img
 		Image:           img.ID, // Always use the resolved image id
 		Image:           img.ID, // Always use the resolved image id
 		NetworkSettings: &NetworkSettings{},
 		NetworkSettings: &NetworkSettings{},
 		Name:            name,
 		Name:            name,
-		Driver:          runtime.driver.String(),
-		ExecDriver:      runtime.execDriver.Name(),
+		Driver:          daemon.driver.String(),
+		ExecDriver:      daemon.execDriver.Name(),
+	}
+	container.root = daemon.containerRoot(container.ID)
+
+	if container.ProcessLabel, container.MountLabel, err = label.GenLabels(""); err != nil {
+		return nil, err
 	}
 	}
-	container.root = runtime.containerRoot(container.ID)
 	return container, nil
 	return container, nil
 }
 }
 
 
-func (runtime *Runtime) createRootfs(container *Container, img *image.Image) error {
+func (daemon *Daemon) createRootfs(container *Container, img *image.Image) error {
 	// Step 1: create the container directory.
 	// Step 1: create the container directory.
 	// This doubles as a barrier to avoid race conditions.
 	// This doubles as a barrier to avoid race conditions.
 	if err := os.Mkdir(container.root, 0700); err != nil {
 	if err := os.Mkdir(container.root, 0700); err != nil {
 		return err
 		return err
 	}
 	}
 	initID := fmt.Sprintf("%s-init", container.ID)
 	initID := fmt.Sprintf("%s-init", container.ID)
-	if err := runtime.driver.Create(initID, img.ID, ""); err != nil {
+	if err := daemon.driver.Create(initID, img.ID); err != nil {
 		return err
 		return err
 	}
 	}
-	initPath, err := runtime.driver.Get(initID)
+	initPath, err := daemon.driver.Get(initID, "")
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	defer runtime.driver.Put(initID)
+	defer daemon.driver.Put(initID)
 
 
 	if err := graph.SetupInitLayer(initPath); err != nil {
 	if err := graph.SetupInitLayer(initPath); err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	if err := runtime.driver.Create(container.ID, initID, ""); err != nil {
+	if err := daemon.driver.Create(container.ID, initID); err != nil {
 		return err
 		return err
 	}
 	}
 	return nil
 	return nil
@@ -572,7 +574,7 @@ func (runtime *Runtime) createRootfs(container *Container, img *image.Image) err
 
 
 // Commit creates a new filesystem image from the current state of a container.
 // Commit creates a new filesystem image from the current state of a container.
 // The image can optionally be tagged into a repository
 // The image can optionally be tagged into a repository
-func (runtime *Runtime) Commit(container *Container, repository, tag, comment, author string, config *runconfig.Config) (*image.Image, error) {
+func (daemon *Daemon) Commit(container *Container, repository, tag, comment, author string, config *runconfig.Config) (*image.Image, error) {
 	// FIXME: freeze the container before copying it to avoid data corruption?
 	// FIXME: freeze the container before copying it to avoid data corruption?
 	if err := container.Mount(); err != nil {
 	if err := container.Mount(); err != nil {
 		return nil, err
 		return nil, err
@@ -595,13 +597,13 @@ func (runtime *Runtime) Commit(container *Container, repository, tag, comment, a
 		containerImage = container.Image
 		containerImage = container.Image
 		containerConfig = container.Config
 		containerConfig = container.Config
 	}
 	}
-	img, err := runtime.graph.Create(rwTar, containerID, containerImage, comment, author, containerConfig, config)
+	img, err := daemon.graph.Create(rwTar, containerID, containerImage, comment, author, containerConfig, config)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 	// Register the image if needed
 	// Register the image if needed
 	if repository != "" {
 	if repository != "" {
-		if err := runtime.repositories.Set(repository, tag, img.ID, true); err != nil {
+		if err := daemon.repositories.Set(repository, tag, img.ID, true); err != nil {
 			return img, err
 			return img, err
 		}
 		}
 	}
 	}
@@ -618,31 +620,31 @@ func GetFullContainerName(name string) (string, error) {
 	return name, nil
 	return name, nil
 }
 }
 
 
-func (runtime *Runtime) GetByName(name string) (*Container, error) {
+func (daemon *Daemon) GetByName(name string) (*Container, error) {
 	fullName, err := GetFullContainerName(name)
 	fullName, err := GetFullContainerName(name)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
-	entity := runtime.containerGraph.Get(fullName)
+	entity := daemon.containerGraph.Get(fullName)
 	if entity == nil {
 	if entity == nil {
 		return nil, fmt.Errorf("Could not find entity for %s", name)
 		return nil, fmt.Errorf("Could not find entity for %s", name)
 	}
 	}
-	e := runtime.getContainerElement(entity.ID())
+	e := daemon.getContainerElement(entity.ID())
 	if e == nil {
 	if e == nil {
 		return nil, fmt.Errorf("Could not find container for entity id %s", entity.ID())
 		return nil, fmt.Errorf("Could not find container for entity id %s", entity.ID())
 	}
 	}
 	return e.Value.(*Container), nil
 	return e.Value.(*Container), nil
 }
 }
 
 
-func (runtime *Runtime) Children(name string) (map[string]*Container, error) {
+func (daemon *Daemon) Children(name string) (map[string]*Container, error) {
 	name, err := GetFullContainerName(name)
 	name, err := GetFullContainerName(name)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 	children := make(map[string]*Container)
 	children := make(map[string]*Container)
 
 
-	err = runtime.containerGraph.Walk(name, func(p string, e *graphdb.Entity) error {
-		c := runtime.Get(e.ID())
+	err = daemon.containerGraph.Walk(name, func(p string, e *graphdb.Entity) error {
+		c := daemon.Get(e.ID())
 		if c == nil {
 		if c == nil {
 			return fmt.Errorf("Could not get container for name %s and id %s", e.ID(), p)
 			return fmt.Errorf("Could not get container for name %s and id %s", e.ID(), p)
 		}
 		}
@@ -656,29 +658,28 @@ func (runtime *Runtime) Children(name string) (map[string]*Container, error) {
 	return children, nil
 	return children, nil
 }
 }
 
 
-func (runtime *Runtime) RegisterLink(parent, child *Container, alias string) error {
+func (daemon *Daemon) RegisterLink(parent, child *Container, alias string) error {
 	fullName := path.Join(parent.Name, alias)
 	fullName := path.Join(parent.Name, alias)
-	if !runtime.containerGraph.Exists(fullName) {
-		_, err := runtime.containerGraph.Set(fullName, child.ID)
+	if !daemon.containerGraph.Exists(fullName) {
+		_, err := daemon.containerGraph.Set(fullName, child.ID)
 		return err
 		return err
 	}
 	}
 	return nil
 	return nil
 }
 }
 
 
 // FIXME: harmonize with NewGraph()
 // FIXME: harmonize with NewGraph()
-func NewRuntime(config *daemonconfig.Config, eng *engine.Engine) (*Runtime, error) {
-	runtime, err := NewRuntimeFromDirectory(config, eng)
+func NewDaemon(config *daemonconfig.Config, eng *engine.Engine) (*Daemon, error) {
+	daemon, err := NewDaemonFromDirectory(config, eng)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
-	return runtime, nil
+	return daemon, nil
 }
 }
 
 
-func NewRuntimeFromDirectory(config *daemonconfig.Config, eng *engine.Engine) (*Runtime, error) {
+func NewDaemonFromDirectory(config *daemonconfig.Config, eng *engine.Engine) (*Daemon, error) {
 	if !config.EnableSelinuxSupport {
 	if !config.EnableSelinuxSupport {
 		selinux.SetDisabled()
 		selinux.SetDisabled()
 	}
 	}
-
 	// Set the default driver
 	// Set the default driver
 	graphdriver.DefaultDriver = config.GraphDriver
 	graphdriver.DefaultDriver = config.GraphDriver
 
 
@@ -693,9 +694,9 @@ func NewRuntimeFromDirectory(config *daemonconfig.Config, eng *engine.Engine) (*
 		return nil, err
 		return nil, err
 	}
 	}
 
 
-	runtimeRepo := path.Join(config.Root, "containers")
+	daemonRepo := path.Join(config.Root, "containers")
 
 
-	if err := os.MkdirAll(runtimeRepo, 0700); err != nil && !os.IsExist(err) {
+	if err := os.MkdirAll(daemonRepo, 0700); err != nil && !os.IsExist(err) {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
@@ -774,12 +775,12 @@ func NewRuntimeFromDirectory(config *daemonconfig.Config, eng *engine.Engine) (*
 		return nil, err
 		return nil, err
 	}
 	}
 
 
-	runtime := &Runtime{
-		repository:     runtimeRepo,
+	daemon := &Daemon{
+		repository:     daemonRepo,
 		containers:     list.New(),
 		containers:     list.New(),
 		graph:          g,
 		graph:          g,
 		repositories:   repositories,
 		repositories:   repositories,
-		idIndex:        utils.NewTruncIndex(),
+		idIndex:        utils.NewTruncIndex([]string{}),
 		sysInfo:        sysInfo,
 		sysInfo:        sysInfo,
 		volumes:        volumes,
 		volumes:        volumes,
 		config:         config,
 		config:         config,
@@ -790,19 +791,19 @@ func NewRuntimeFromDirectory(config *daemonconfig.Config, eng *engine.Engine) (*
 		eng:            eng,
 		eng:            eng,
 	}
 	}
 
 
-	if err := runtime.checkLocaldns(); err != nil {
+	if err := daemon.checkLocaldns(); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
-	if err := runtime.restore(); err != nil {
+	if err := daemon.restore(); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
-	return runtime, nil
+	return daemon, nil
 }
 }
 
 
-func (runtime *Runtime) shutdown() error {
+func (daemon *Daemon) shutdown() error {
 	group := sync.WaitGroup{}
 	group := sync.WaitGroup{}
 	utils.Debugf("starting clean shutdown of all containers...")
 	utils.Debugf("starting clean shutdown of all containers...")
-	for _, container := range runtime.List() {
+	for _, container := range daemon.List() {
 		c := container
 		c := container
 		if c.State.IsRunning() {
 		if c.State.IsRunning() {
 			utils.Debugf("stopping %s", c.ID)
 			utils.Debugf("stopping %s", c.ID)
@@ -823,22 +824,22 @@ func (runtime *Runtime) shutdown() error {
 	return nil
 	return nil
 }
 }
 
 
-func (runtime *Runtime) Close() error {
+func (daemon *Daemon) Close() error {
 	errorsStrings := []string{}
 	errorsStrings := []string{}
-	if err := runtime.shutdown(); err != nil {
-		utils.Errorf("runtime.shutdown(): %s", err)
+	if err := daemon.shutdown(); err != nil {
+		utils.Errorf("daemon.shutdown(): %s", err)
 		errorsStrings = append(errorsStrings, err.Error())
 		errorsStrings = append(errorsStrings, err.Error())
 	}
 	}
 	if err := portallocator.ReleaseAll(); err != nil {
 	if err := portallocator.ReleaseAll(); err != nil {
 		utils.Errorf("portallocator.ReleaseAll(): %s", err)
 		utils.Errorf("portallocator.ReleaseAll(): %s", err)
 		errorsStrings = append(errorsStrings, err.Error())
 		errorsStrings = append(errorsStrings, err.Error())
 	}
 	}
-	if err := runtime.driver.Cleanup(); err != nil {
-		utils.Errorf("runtime.driver.Cleanup(): %s", err.Error())
+	if err := daemon.driver.Cleanup(); err != nil {
+		utils.Errorf("daemon.driver.Cleanup(): %s", err.Error())
 		errorsStrings = append(errorsStrings, err.Error())
 		errorsStrings = append(errorsStrings, err.Error())
 	}
 	}
-	if err := runtime.containerGraph.Close(); err != nil {
-		utils.Errorf("runtime.containerGraph.Close(): %s", err.Error())
+	if err := daemon.containerGraph.Close(); err != nil {
+		utils.Errorf("daemon.containerGraph.Close(): %s", err.Error())
 		errorsStrings = append(errorsStrings, err.Error())
 		errorsStrings = append(errorsStrings, err.Error())
 	}
 	}
 	if len(errorsStrings) > 0 {
 	if len(errorsStrings) > 0 {
@@ -847,55 +848,55 @@ func (runtime *Runtime) Close() error {
 	return nil
 	return nil
 }
 }
 
 
-func (runtime *Runtime) Mount(container *Container) error {
-	dir, err := runtime.driver.Get(container.ID)
+func (daemon *Daemon) Mount(container *Container) error {
+	dir, err := daemon.driver.Get(container.ID, container.GetMountLabel())
 	if err != nil {
 	if err != nil {
-		return fmt.Errorf("Error getting container %s from driver %s: %s", container.ID, runtime.driver, err)
+		return fmt.Errorf("Error getting container %s from driver %s: %s", container.ID, daemon.driver, err)
 	}
 	}
 	if container.basefs == "" {
 	if container.basefs == "" {
 		container.basefs = dir
 		container.basefs = dir
 	} else if container.basefs != dir {
 	} else if container.basefs != dir {
 		return fmt.Errorf("Error: driver %s is returning inconsistent paths for container %s ('%s' then '%s')",
 		return fmt.Errorf("Error: driver %s is returning inconsistent paths for container %s ('%s' then '%s')",
-			runtime.driver, container.ID, container.basefs, dir)
+			daemon.driver, container.ID, container.basefs, dir)
 	}
 	}
 	return nil
 	return nil
 }
 }
 
 
-func (runtime *Runtime) Unmount(container *Container) error {
-	runtime.driver.Put(container.ID)
+func (daemon *Daemon) Unmount(container *Container) error {
+	daemon.driver.Put(container.ID)
 	return nil
 	return nil
 }
 }
 
 
-func (runtime *Runtime) Changes(container *Container) ([]archive.Change, error) {
-	if differ, ok := runtime.driver.(graphdriver.Differ); ok {
+func (daemon *Daemon) Changes(container *Container) ([]archive.Change, error) {
+	if differ, ok := daemon.driver.(graphdriver.Differ); ok {
 		return differ.Changes(container.ID)
 		return differ.Changes(container.ID)
 	}
 	}
-	cDir, err := runtime.driver.Get(container.ID)
+	cDir, err := daemon.driver.Get(container.ID, "")
 	if err != nil {
 	if err != nil {
-		return nil, fmt.Errorf("Error getting container rootfs %s from driver %s: %s", container.ID, container.runtime.driver, err)
+		return nil, fmt.Errorf("Error getting container rootfs %s from driver %s: %s", container.ID, container.daemon.driver, err)
 	}
 	}
-	defer runtime.driver.Put(container.ID)
-	initDir, err := runtime.driver.Get(container.ID + "-init")
+	defer daemon.driver.Put(container.ID)
+	initDir, err := daemon.driver.Get(container.ID+"-init", "")
 	if err != nil {
 	if err != nil {
-		return nil, fmt.Errorf("Error getting container init rootfs %s from driver %s: %s", container.ID, container.runtime.driver, err)
+		return nil, fmt.Errorf("Error getting container init rootfs %s from driver %s: %s", container.ID, container.daemon.driver, err)
 	}
 	}
-	defer runtime.driver.Put(container.ID + "-init")
+	defer daemon.driver.Put(container.ID + "-init")
 	return archive.ChangesDirs(cDir, initDir)
 	return archive.ChangesDirs(cDir, initDir)
 }
 }
 
 
-func (runtime *Runtime) Diff(container *Container) (archive.Archive, error) {
-	if differ, ok := runtime.driver.(graphdriver.Differ); ok {
+func (daemon *Daemon) Diff(container *Container) (archive.Archive, error) {
+	if differ, ok := daemon.driver.(graphdriver.Differ); ok {
 		return differ.Diff(container.ID)
 		return differ.Diff(container.ID)
 	}
 	}
 
 
-	changes, err := runtime.Changes(container)
+	changes, err := daemon.Changes(container)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
-	cDir, err := runtime.driver.Get(container.ID)
+	cDir, err := daemon.driver.Get(container.ID, "")
 	if err != nil {
 	if err != nil {
-		return nil, fmt.Errorf("Error getting container rootfs %s from driver %s: %s", container.ID, container.runtime.driver, err)
+		return nil, fmt.Errorf("Error getting container rootfs %s from driver %s: %s", container.ID, container.daemon.driver, err)
 	}
 	}
 
 
 	archive, err := archive.ExportChanges(cDir, changes)
 	archive, err := archive.ExportChanges(cDir, changes)
@@ -904,26 +905,26 @@ func (runtime *Runtime) Diff(container *Container) (archive.Archive, error) {
 	}
 	}
 	return utils.NewReadCloserWrapper(archive, func() error {
 	return utils.NewReadCloserWrapper(archive, func() error {
 		err := archive.Close()
 		err := archive.Close()
-		runtime.driver.Put(container.ID)
+		daemon.driver.Put(container.ID)
 		return err
 		return err
 	}), nil
 	}), nil
 }
 }
 
 
-func (runtime *Runtime) Run(c *Container, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) {
-	return runtime.execDriver.Run(c.command, pipes, startCallback)
+func (daemon *Daemon) Run(c *Container, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) {
+	return daemon.execDriver.Run(c.command, pipes, startCallback)
 }
 }
 
 
-func (runtime *Runtime) Kill(c *Container, sig int) error {
-	return runtime.execDriver.Kill(c.command, sig)
+func (daemon *Daemon) Kill(c *Container, sig int) error {
+	return daemon.execDriver.Kill(c.command, sig)
 }
 }
 
 
 // Nuke kills all containers then removes all content
 // Nuke kills all containers then removes all content
 // from the content root, including images, volumes and
 // from the content root, including images, volumes and
 // container filesystems.
 // container filesystems.
-// Again: this will remove your entire docker runtime!
-func (runtime *Runtime) Nuke() error {
+// Again: this will remove your entire docker daemon!
+func (daemon *Daemon) Nuke() error {
 	var wg sync.WaitGroup
 	var wg sync.WaitGroup
-	for _, container := range runtime.List() {
+	for _, container := range daemon.List() {
 		wg.Add(1)
 		wg.Add(1)
 		go func(c *Container) {
 		go func(c *Container) {
 			c.Kill()
 			c.Kill()
@@ -931,63 +932,63 @@ func (runtime *Runtime) Nuke() error {
 		}(container)
 		}(container)
 	}
 	}
 	wg.Wait()
 	wg.Wait()
-	runtime.Close()
+	daemon.Close()
 
 
-	return os.RemoveAll(runtime.config.Root)
+	return os.RemoveAll(daemon.config.Root)
 }
 }
 
 
 // FIXME: this is a convenience function for integration tests
 // FIXME: this is a convenience function for integration tests
-// which need direct access to runtime.graph.
+// which need direct access to daemon.graph.
 // Once the tests switch to using engine and jobs, this method
 // Once the tests switch to using engine and jobs, this method
 // can go away.
 // can go away.
-func (runtime *Runtime) Graph() *graph.Graph {
-	return runtime.graph
+func (daemon *Daemon) Graph() *graph.Graph {
+	return daemon.graph
 }
 }
 
 
-func (runtime *Runtime) Repositories() *graph.TagStore {
-	return runtime.repositories
+func (daemon *Daemon) Repositories() *graph.TagStore {
+	return daemon.repositories
 }
 }
 
 
-func (runtime *Runtime) Config() *daemonconfig.Config {
-	return runtime.config
+func (daemon *Daemon) Config() *daemonconfig.Config {
+	return daemon.config
 }
 }
 
 
-func (runtime *Runtime) SystemConfig() *sysinfo.SysInfo {
-	return runtime.sysInfo
+func (daemon *Daemon) SystemConfig() *sysinfo.SysInfo {
+	return daemon.sysInfo
 }
 }
 
 
-func (runtime *Runtime) SystemInitPath() string {
-	return runtime.sysInitPath
+func (daemon *Daemon) SystemInitPath() string {
+	return daemon.sysInitPath
 }
 }
 
 
-func (runtime *Runtime) GraphDriver() graphdriver.Driver {
-	return runtime.driver
+func (daemon *Daemon) GraphDriver() graphdriver.Driver {
+	return daemon.driver
 }
 }
 
 
-func (runtime *Runtime) ExecutionDriver() execdriver.Driver {
-	return runtime.execDriver
+func (daemon *Daemon) ExecutionDriver() execdriver.Driver {
+	return daemon.execDriver
 }
 }
 
 
-func (runtime *Runtime) Volumes() *graph.Graph {
-	return runtime.volumes
+func (daemon *Daemon) Volumes() *graph.Graph {
+	return daemon.volumes
 }
 }
 
 
-func (runtime *Runtime) ContainerGraph() *graphdb.Database {
-	return runtime.containerGraph
+func (daemon *Daemon) ContainerGraph() *graphdb.Database {
+	return daemon.containerGraph
 }
 }
 
 
-func (runtime *Runtime) SetServer(server Server) {
-	runtime.srv = server
+func (daemon *Daemon) SetServer(server Server) {
+	daemon.srv = server
 }
 }
 
 
-func (runtime *Runtime) checkLocaldns() error {
-	resolvConf, err := utils.GetResolvConf()
+func (daemon *Daemon) checkLocaldns() error {
+	resolvConf, err := resolvconf.Get()
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	if len(runtime.config.Dns) == 0 && utils.CheckLocalDns(resolvConf) {
+	if len(daemon.config.Dns) == 0 && utils.CheckLocalDns(resolvConf) {
 		log.Printf("Local (127.0.0.1) DNS resolver found in resolv.conf and containers can't use it. Using default external servers : %v\n", DefaultDns)
 		log.Printf("Local (127.0.0.1) DNS resolver found in resolv.conf and containers can't use it. Using default external servers : %v\n", DefaultDns)
-		runtime.config.Dns = DefaultDns
+		daemon.config.Dns = DefaultDns
 	}
 	}
 	return nil
 	return nil
 }
 }

+ 3 - 3
runtime/runtime_aufs.go → daemon/daemon_aufs.go

@@ -1,11 +1,11 @@
 // +build !exclude_graphdriver_aufs
 // +build !exclude_graphdriver_aufs
 
 
-package runtime
+package daemon
 
 
 import (
 import (
+	"github.com/dotcloud/docker/daemon/graphdriver"
+	"github.com/dotcloud/docker/daemon/graphdriver/aufs"
 	"github.com/dotcloud/docker/graph"
 	"github.com/dotcloud/docker/graph"
-	"github.com/dotcloud/docker/runtime/graphdriver"
-	"github.com/dotcloud/docker/runtime/graphdriver/aufs"
 	"github.com/dotcloud/docker/utils"
 	"github.com/dotcloud/docker/utils"
 )
 )
 
 

+ 7 - 0
daemon/daemon_btrfs.go

@@ -0,0 +1,7 @@
+// +build !exclude_graphdriver_btrfs
+
+package daemon
+
+import (
+	_ "github.com/dotcloud/docker/daemon/graphdriver/btrfs"
+)

+ 7 - 0
daemon/daemon_devicemapper.go

@@ -0,0 +1,7 @@
+// +build !exclude_graphdriver_devicemapper
+
+package daemon
+
+import (
+	_ "github.com/dotcloud/docker/daemon/graphdriver/devmapper"
+)

+ 2 - 2
runtime/runtime_no_aufs.go → daemon/daemon_no_aufs.go

@@ -1,9 +1,9 @@
 // +build exclude_graphdriver_aufs
 // +build exclude_graphdriver_aufs
 
 
-package runtime
+package daemon
 
 
 import (
 import (
-	"github.com/dotcloud/docker/runtime/graphdriver"
+	"github.com/dotcloud/docker/daemon/graphdriver"
 )
 )
 
 
 func migrateIfAufs(driver graphdriver.Driver, root string) error {
 func migrateIfAufs(driver graphdriver.Driver, root string) error {

+ 0 - 0
runtime/execdriver/MAINTAINERS → daemon/execdriver/MAINTAINERS


+ 4 - 2
runtime/execdriver/driver.go → daemon/execdriver/driver.go

@@ -89,8 +89,10 @@ type Driver interface {
 
 
 // Network settings of the container
 // Network settings of the container
 type Network struct {
 type Network struct {
-	Interface *NetworkInterface `json:"interface"` // if interface is nil then networking is disabled
-	Mtu       int               `json:"mtu"`
+	Interface      *NetworkInterface `json:"interface"` // if interface is nil then networking is disabled
+	Mtu            int               `json:"mtu"`
+	ContainerID    string            `json:"container_id"` // id of the container to join network.
+	HostNetworking bool              `json:"host_networking"`
 }
 }
 
 
 type NetworkInterface struct {
 type NetworkInterface struct {

+ 3 - 3
runtime/execdriver/execdrivers/execdrivers.go → daemon/execdriver/execdrivers/execdrivers.go

@@ -2,10 +2,10 @@ package execdrivers
 
 
 import (
 import (
 	"fmt"
 	"fmt"
+	"github.com/dotcloud/docker/daemon/execdriver"
+	"github.com/dotcloud/docker/daemon/execdriver/lxc"
+	"github.com/dotcloud/docker/daemon/execdriver/native"
 	"github.com/dotcloud/docker/pkg/sysinfo"
 	"github.com/dotcloud/docker/pkg/sysinfo"
-	"github.com/dotcloud/docker/runtime/execdriver"
-	"github.com/dotcloud/docker/runtime/execdriver/lxc"
-	"github.com/dotcloud/docker/runtime/execdriver/native"
 	"path"
 	"path"
 )
 )
 
 

+ 24 - 9
runtime/execdriver/lxc/driver.go → daemon/execdriver/lxc/driver.go

@@ -1,11 +1,8 @@
 package lxc
 package lxc
 
 
 import (
 import (
+	"encoding/json"
 	"fmt"
 	"fmt"
-	"github.com/dotcloud/docker/pkg/cgroups"
-	"github.com/dotcloud/docker/pkg/label"
-	"github.com/dotcloud/docker/runtime/execdriver"
-	"github.com/dotcloud/docker/utils"
 	"io/ioutil"
 	"io/ioutil"
 	"log"
 	"log"
 	"os"
 	"os"
@@ -16,6 +13,12 @@ import (
 	"strings"
 	"strings"
 	"syscall"
 	"syscall"
 	"time"
 	"time"
+
+	"github.com/dotcloud/docker/daemon/execdriver"
+	"github.com/dotcloud/docker/pkg/cgroups"
+	"github.com/dotcloud/docker/pkg/label"
+	"github.com/dotcloud/docker/pkg/system"
+	"github.com/dotcloud/docker/utils"
 )
 )
 
 
 const DriverName = "lxc"
 const DriverName = "lxc"
@@ -25,23 +28,21 @@ func init() {
 		if err := setupEnv(args); err != nil {
 		if err := setupEnv(args); err != nil {
 			return err
 			return err
 		}
 		}
-
 		if err := setupHostname(args); err != nil {
 		if err := setupHostname(args); err != nil {
 			return err
 			return err
 		}
 		}
-
 		if err := setupNetworking(args); err != nil {
 		if err := setupNetworking(args); err != nil {
 			return err
 			return err
 		}
 		}
-
 		if err := setupCapabilities(args); err != nil {
 		if err := setupCapabilities(args); err != nil {
 			return err
 			return err
 		}
 		}
-
 		if err := setupWorkingDirectory(args); err != nil {
 		if err := setupWorkingDirectory(args); err != nil {
 			return err
 			return err
 		}
 		}
-
+		if err := system.CloseFdsFrom(3); err != nil {
+			return err
+		}
 		if err := changeUser(args); err != nil {
 		if err := changeUser(args); err != nil {
 			return err
 			return err
 		}
 		}
@@ -85,6 +86,9 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba
 	if err := execdriver.SetTerminal(c, pipes); err != nil {
 	if err := execdriver.SetTerminal(c, pipes); err != nil {
 		return -1, err
 		return -1, err
 	}
 	}
+	if err := d.generateEnvConfig(c); err != nil {
+		return -1, err
+	}
 	configPath, err := d.generateLXCConfig(c)
 	configPath, err := d.generateLXCConfig(c)
 	if err != nil {
 	if err != nil {
 		return -1, err
 		return -1, err
@@ -416,3 +420,14 @@ func (d *driver) generateLXCConfig(c *execdriver.Command) (string, error) {
 	}
 	}
 	return root, nil
 	return root, nil
 }
 }
+
+func (d *driver) generateEnvConfig(c *execdriver.Command) error {
+	data, err := json.Marshal(c.Env)
+	if err != nil {
+		return err
+	}
+	p := path.Join(d.root, "containers", c.ID, "config.env")
+	c.Mounts = append(c.Mounts, execdriver.Mount{p, "/.dockerenv", false, true})
+
+	return ioutil.WriteFile(p, data, 0600)
+}

+ 0 - 0
runtime/execdriver/lxc/info.go → daemon/execdriver/lxc/info.go


+ 0 - 0
runtime/execdriver/lxc/info_test.go → daemon/execdriver/lxc/info_test.go


+ 6 - 4
runtime/execdriver/lxc/init.go → daemon/execdriver/lxc/init.go

@@ -3,15 +3,16 @@ package lxc
 import (
 import (
 	"encoding/json"
 	"encoding/json"
 	"fmt"
 	"fmt"
-	"github.com/dotcloud/docker/pkg/netlink"
-	"github.com/dotcloud/docker/pkg/user"
-	"github.com/dotcloud/docker/runtime/execdriver"
-	"github.com/syndtr/gocapability/capability"
 	"io/ioutil"
 	"io/ioutil"
 	"net"
 	"net"
 	"os"
 	"os"
 	"strings"
 	"strings"
 	"syscall"
 	"syscall"
+
+	"github.com/dotcloud/docker/daemon/execdriver"
+	"github.com/dotcloud/docker/pkg/netlink"
+	"github.com/dotcloud/docker/pkg/user"
+	"github.com/syndtr/gocapability/capability"
 )
 )
 
 
 // Clear environment pollution introduced by lxc-start
 // Clear environment pollution introduced by lxc-start
@@ -149,6 +150,7 @@ func setupCapabilities(args *execdriver.InitArgs) error {
 		capability.CAP_MAC_OVERRIDE,
 		capability.CAP_MAC_OVERRIDE,
 		capability.CAP_MAC_ADMIN,
 		capability.CAP_MAC_ADMIN,
 		capability.CAP_NET_ADMIN,
 		capability.CAP_NET_ADMIN,
+		capability.CAP_SYSLOG,
 	}
 	}
 
 
 	c, err := capability.NewPid(os.Getpid())
 	c, err := capability.NewPid(os.Getpid())

+ 0 - 0
runtime/execdriver/lxc/lxc_init_linux.go → daemon/execdriver/lxc/lxc_init_linux.go


+ 0 - 0
runtime/execdriver/lxc/lxc_init_unsupported.go → daemon/execdriver/lxc/lxc_init_unsupported.go


+ 11 - 10
runtime/execdriver/lxc/lxc_template.go → daemon/execdriver/lxc/lxc_template.go

@@ -1,10 +1,11 @@
 package lxc
 package lxc
 
 
 import (
 import (
-	"github.com/dotcloud/docker/pkg/label"
-	"github.com/dotcloud/docker/runtime/execdriver"
 	"strings"
 	"strings"
 	"text/template"
 	"text/template"
+
+	"github.com/dotcloud/docker/daemon/execdriver"
+	"github.com/dotcloud/docker/pkg/label"
 )
 )
 
 
 const LxcTemplate = `
 const LxcTemplate = `
@@ -13,12 +14,13 @@ const LxcTemplate = `
 lxc.network.type = veth
 lxc.network.type = veth
 lxc.network.link = {{.Network.Interface.Bridge}}
 lxc.network.link = {{.Network.Interface.Bridge}}
 lxc.network.name = eth0
 lxc.network.name = eth0
-{{else}}
+lxc.network.mtu = {{.Network.Mtu}}
+{{else if not .Network.HostNetworking}}
 # network is disabled (-n=false)
 # network is disabled (-n=false)
 lxc.network.type = empty
 lxc.network.type = empty
 lxc.network.flags = up
 lxc.network.flags = up
-{{end}}
 lxc.network.mtu = {{.Network.Mtu}}
 lxc.network.mtu = {{.Network.Mtu}}
+{{end}}
 
 
 # root filesystem
 # root filesystem
 {{$ROOTFS := .Rootfs}}
 {{$ROOTFS := .Rootfs}}
@@ -82,12 +84,11 @@ lxc.pivotdir = lxc_putold
 
 
 # NOTICE: These mounts must be applied within the namespace
 # NOTICE: These mounts must be applied within the namespace
 
 
-#  WARNING: procfs is a known attack vector and should probably be disabled
-#           if your userspace allows it. eg. see http://blog.zx2c4.com/749
+# WARNING: mounting procfs and/or sysfs read-write is a known attack vector.
+# See e.g. http://blog.zx2c4.com/749 and http://bit.ly/T9CkqJ
+# We mount them read-write here, but later, dockerinit will call the Restrict() function to remount them read-only.
+# We cannot mount them directly read-only, because that would prevent loading AppArmor profiles.
 lxc.mount.entry = proc {{escapeFstabSpaces $ROOTFS}}/proc proc nosuid,nodev,noexec 0 0
 lxc.mount.entry = proc {{escapeFstabSpaces $ROOTFS}}/proc proc nosuid,nodev,noexec 0 0
-
-# WARNING: sysfs is a known attack vector and should probably be disabled
-# if your userspace allows it. eg. see http://bit.ly/T9CkqJ
 lxc.mount.entry = sysfs {{escapeFstabSpaces $ROOTFS}}/sys sysfs nosuid,nodev,noexec 0 0
 lxc.mount.entry = sysfs {{escapeFstabSpaces $ROOTFS}}/sys sysfs nosuid,nodev,noexec 0 0
 
 
 {{if .Tty}}
 {{if .Tty}}
@@ -109,7 +110,7 @@ lxc.mount.entry = {{$value.Source}} {{escapeFstabSpaces $ROOTFS}}/{{escapeFstabS
 {{if .AppArmor}}
 {{if .AppArmor}}
 lxc.aa_profile = unconfined
 lxc.aa_profile = unconfined
 {{else}}
 {{else}}
-#lxc.aa_profile = unconfined
+# Let AppArmor normal confinement take place (i.e., not unconfined)
 {{end}}
 {{end}}
 {{end}}
 {{end}}
 
 

+ 1 - 1
runtime/execdriver/lxc/lxc_template_unit_test.go → daemon/execdriver/lxc/lxc_template_unit_test.go

@@ -3,7 +3,7 @@ package lxc
 import (
 import (
 	"bufio"
 	"bufio"
 	"fmt"
 	"fmt"
-	"github.com/dotcloud/docker/runtime/execdriver"
+	"github.com/dotcloud/docker/daemon/execdriver"
 	"io/ioutil"
 	"io/ioutil"
 	"math/rand"
 	"math/rand"
 	"os"
 	"os"

+ 36 - 26
runtime/execdriver/native/configuration/parse.go → daemon/execdriver/native/configuration/parse.go

@@ -2,12 +2,13 @@ package configuration
 
 
 import (
 import (
 	"fmt"
 	"fmt"
-	"github.com/dotcloud/docker/pkg/libcontainer"
-	"github.com/dotcloud/docker/utils"
 	"os/exec"
 	"os/exec"
 	"path/filepath"
 	"path/filepath"
 	"strconv"
 	"strconv"
 	"strings"
 	"strings"
+
+	"github.com/dotcloud/docker/pkg/libcontainer"
+	"github.com/dotcloud/docker/utils"
 )
 )
 
 
 type Action func(*libcontainer.Container, interface{}, string) error
 type Action func(*libcontainer.Container, interface{}, string) error
@@ -21,10 +22,13 @@ var actions = map[string]Action{
 
 
 	"net.join": joinNetNamespace, // join another containers net namespace
 	"net.join": joinNetNamespace, // join another containers net namespace
 
 
-	"cgroups.cpu_shares":  cpuShares,  // set the cpu shares
-	"cgroups.memory":      memory,     // set the memory limit
-	"cgroups.memory_swap": memorySwap, // set the memory swap limit
-	"cgroups.cpuset.cpus": cpusetCpus, // set the cpus used
+	"cgroups.cpu_shares":         cpuShares,         // set the cpu shares
+	"cgroups.memory":             memory,            // set the memory limit
+	"cgroups.memory_reservation": memoryReservation, // set the memory reservation
+	"cgroups.memory_swap":        memorySwap,        // set the memory swap limit
+	"cgroups.cpuset.cpus":        cpusetCpus,        // set the cpus used
+
+	"systemd.slice": systemdSlice, // set parent Slice used for systemd unit
 
 
 	"apparmor_profile": apparmorProfile, // set the apparmor profile to apply
 	"apparmor_profile": apparmorProfile, // set the apparmor profile to apply
 
 
@@ -40,6 +44,15 @@ func cpusetCpus(container *libcontainer.Container, context interface{}, value st
 	return nil
 	return nil
 }
 }
 
 
+func systemdSlice(container *libcontainer.Container, context interface{}, value string) error {
+	if container.Cgroups == nil {
+		return fmt.Errorf("cannot set slice when cgroups are disabled")
+	}
+	container.Cgroups.Slice = value
+
+	return nil
+}
+
 func apparmorProfile(container *libcontainer.Container, context interface{}, value string) error {
 func apparmorProfile(container *libcontainer.Container, context interface{}, value string) error {
 	container.Context["apparmor_profile"] = value
 	container.Context["apparmor_profile"] = value
 	return nil
 	return nil
@@ -70,6 +83,19 @@ func memory(container *libcontainer.Container, context interface{}, value string
 	return nil
 	return nil
 }
 }
 
 
+func memoryReservation(container *libcontainer.Container, context interface{}, value string) error {
+	if container.Cgroups == nil {
+		return fmt.Errorf("cannot set cgroups when they are disabled")
+	}
+
+	v, err := utils.RAMInBytes(value)
+	if err != nil {
+		return err
+	}
+	container.Cgroups.MemoryReservation = v
+	return nil
+}
+
 func memorySwap(container *libcontainer.Container, context interface{}, value string) error {
 func memorySwap(container *libcontainer.Container, context interface{}, value string) error {
 	if container.Cgroups == nil {
 	if container.Cgroups == nil {
 		return fmt.Errorf("cannot set cgroups when they are disabled")
 		return fmt.Errorf("cannot set cgroups when they are disabled")
@@ -83,38 +109,22 @@ func memorySwap(container *libcontainer.Container, context interface{}, value st
 }
 }
 
 
 func addCap(container *libcontainer.Container, context interface{}, value string) error {
 func addCap(container *libcontainer.Container, context interface{}, value string) error {
-	c := container.CapabilitiesMask.Get(value)
-	if c == nil {
-		return fmt.Errorf("%s is not a valid capability", value)
-	}
-	c.Enabled = true
+	container.CapabilitiesMask[value] = true
 	return nil
 	return nil
 }
 }
 
 
 func dropCap(container *libcontainer.Container, context interface{}, value string) error {
 func dropCap(container *libcontainer.Container, context interface{}, value string) error {
-	c := container.CapabilitiesMask.Get(value)
-	if c == nil {
-		return fmt.Errorf("%s is not a valid capability", value)
-	}
-	c.Enabled = false
+	container.CapabilitiesMask[value] = false
 	return nil
 	return nil
 }
 }
 
 
 func addNamespace(container *libcontainer.Container, context interface{}, value string) error {
 func addNamespace(container *libcontainer.Container, context interface{}, value string) error {
-	ns := container.Namespaces.Get(value)
-	if ns == nil {
-		return fmt.Errorf("%s is not a valid namespace", value[1:])
-	}
-	ns.Enabled = true
+	container.Namespaces[value] = true
 	return nil
 	return nil
 }
 }
 
 
 func dropNamespace(container *libcontainer.Container, context interface{}, value string) error {
 func dropNamespace(container *libcontainer.Container, context interface{}, value string) error {
-	ns := container.Namespaces.Get(value)
-	if ns == nil {
-		return fmt.Errorf("%s is not a valid namespace", value[1:])
-	}
-	ns.Enabled = false
+	container.Namespaces[value] = false
 	return nil
 	return nil
 }
 }
 
 

+ 27 - 10
runtime/execdriver/native/configuration/parse_test.go → daemon/execdriver/native/configuration/parse_test.go

@@ -1,8 +1,9 @@
 package configuration
 package configuration
 
 
 import (
 import (
-	"github.com/dotcloud/docker/runtime/execdriver/native/template"
 	"testing"
 	"testing"
+
+	"github.com/dotcloud/docker/daemon/execdriver/native/template"
 )
 )
 
 
 func TestSetReadonlyRootFs(t *testing.T) {
 func TestSetReadonlyRootFs(t *testing.T) {
@@ -38,10 +39,10 @@ func TestConfigurationsDoNotConflict(t *testing.T) {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	if !container1.CapabilitiesMask.Get("NET_ADMIN").Enabled {
+	if !container1.CapabilitiesMask["NET_ADMIN"] {
 		t.Fatal("container one should have NET_ADMIN enabled")
 		t.Fatal("container one should have NET_ADMIN enabled")
 	}
 	}
-	if container2.CapabilitiesMask.Get("NET_ADMIN").Enabled {
+	if container2.CapabilitiesMask["NET_ADMIN"] {
 		t.Fatal("container two should not have NET_ADMIN enabled")
 		t.Fatal("container two should not have NET_ADMIN enabled")
 	}
 	}
 }
 }
@@ -93,7 +94,7 @@ func TestCpuShares(t *testing.T) {
 	}
 	}
 }
 }
 
 
-func TestCgroupMemory(t *testing.T) {
+func TestMemory(t *testing.T) {
 	var (
 	var (
 		container = template.New()
 		container = template.New()
 		opts      = []string{
 		opts      = []string{
@@ -109,6 +110,22 @@ func TestCgroupMemory(t *testing.T) {
 	}
 	}
 }
 }
 
 
+func TestMemoryReservation(t *testing.T) {
+	var (
+		container = template.New()
+		opts      = []string{
+			"cgroups.memory_reservation=500m",
+		}
+	)
+	if err := ParseConfiguration(container, nil, opts); err != nil {
+		t.Fatal(err)
+	}
+
+	if expected := int64(500 * 1024 * 1024); container.Cgroups.MemoryReservation != expected {
+		t.Fatalf("expected memory reservation %d got %d", expected, container.Cgroups.MemoryReservation)
+	}
+}
+
 func TestAddCap(t *testing.T) {
 func TestAddCap(t *testing.T) {
 	var (
 	var (
 		container = template.New()
 		container = template.New()
@@ -121,10 +138,10 @@ func TestAddCap(t *testing.T) {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	if !container.CapabilitiesMask.Get("MKNOD").Enabled {
+	if !container.CapabilitiesMask["MKNOD"] {
 		t.Fatal("container should have MKNOD enabled")
 		t.Fatal("container should have MKNOD enabled")
 	}
 	}
-	if !container.CapabilitiesMask.Get("SYS_ADMIN").Enabled {
+	if !container.CapabilitiesMask["SYS_ADMIN"] {
 		t.Fatal("container should have SYS_ADMIN enabled")
 		t.Fatal("container should have SYS_ADMIN enabled")
 	}
 	}
 }
 }
@@ -137,14 +154,14 @@ func TestDropCap(t *testing.T) {
 		}
 		}
 	)
 	)
 	// enabled all caps like in privileged mode
 	// enabled all caps like in privileged mode
-	for _, c := range container.CapabilitiesMask {
-		c.Enabled = true
+	for key := range container.CapabilitiesMask {
+		container.CapabilitiesMask[key] = true
 	}
 	}
 	if err := ParseConfiguration(container, nil, opts); err != nil {
 	if err := ParseConfiguration(container, nil, opts); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	if container.CapabilitiesMask.Get("MKNOD").Enabled {
+	if container.CapabilitiesMask["MKNOD"] {
 		t.Fatal("container should not have MKNOD enabled")
 		t.Fatal("container should not have MKNOD enabled")
 	}
 	}
 }
 }
@@ -160,7 +177,7 @@ func TestDropNamespace(t *testing.T) {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	if container.Namespaces.Get("NEWNET").Enabled {
+	if container.Namespaces["NEWNET"] {
 		t.Fatal("container should not have NEWNET enabled")
 		t.Fatal("container should not have NEWNET enabled")
 	}
 	}
 }
 }

+ 44 - 17
runtime/execdriver/native/create.go → daemon/execdriver/native/create.go

@@ -3,12 +3,13 @@ package native
 import (
 import (
 	"fmt"
 	"fmt"
 	"os"
 	"os"
+	"path/filepath"
 
 
-	"github.com/dotcloud/docker/pkg/label"
+	"github.com/dotcloud/docker/daemon/execdriver"
+	"github.com/dotcloud/docker/daemon/execdriver/native/configuration"
+	"github.com/dotcloud/docker/daemon/execdriver/native/template"
+	"github.com/dotcloud/docker/pkg/apparmor"
 	"github.com/dotcloud/docker/pkg/libcontainer"
 	"github.com/dotcloud/docker/pkg/libcontainer"
-	"github.com/dotcloud/docker/runtime/execdriver"
-	"github.com/dotcloud/docker/runtime/execdriver/native/configuration"
-	"github.com/dotcloud/docker/runtime/execdriver/native/template"
 )
 )
 
 
 // createContainer populates and configures the container type with the
 // createContainer populates and configures the container type with the
@@ -24,6 +25,7 @@ func (d *driver) createContainer(c *execdriver.Command) (*libcontainer.Container
 	container.Cgroups.Name = c.ID
 	container.Cgroups.Name = c.ID
 	// check to see if we are running in ramdisk to disable pivot root
 	// check to see if we are running in ramdisk to disable pivot root
 	container.NoPivotRoot = os.Getenv("DOCKER_RAMDISK") != ""
 	container.NoPivotRoot = os.Getenv("DOCKER_RAMDISK") != ""
+	container.Context["restrictions"] = "true"
 
 
 	if err := d.createNetwork(container, c); err != nil {
 	if err := d.createNetwork(container, c); err != nil {
 		return nil, err
 		return nil, err
@@ -32,6 +34,8 @@ func (d *driver) createContainer(c *execdriver.Command) (*libcontainer.Container
 		if err := d.setPrivileged(container); err != nil {
 		if err := d.setPrivileged(container); err != nil {
 			return nil, err
 			return nil, err
 		}
 		}
+	} else {
+		container.Mounts = append(container.Mounts, libcontainer.Mount{Type: "devtmpfs"})
 	}
 	}
 	if err := d.setupCgroups(container, c); err != nil {
 	if err := d.setupCgroups(container, c); err != nil {
 		return nil, err
 		return nil, err
@@ -49,6 +53,10 @@ func (d *driver) createContainer(c *execdriver.Command) (*libcontainer.Container
 }
 }
 
 
 func (d *driver) createNetwork(container *libcontainer.Container, c *execdriver.Command) error {
 func (d *driver) createNetwork(container *libcontainer.Container, c *execdriver.Command) error {
+	if c.Network.HostNetworking {
+		container.Namespaces["NEWNET"] = false
+		return nil
+	}
 	container.Networks = []*libcontainer.Network{
 	container.Networks = []*libcontainer.Network{
 		{
 		{
 			Mtu:     c.Network.Mtu,
 			Mtu:     c.Network.Mtu,
@@ -72,15 +80,34 @@ func (d *driver) createNetwork(container *libcontainer.Container, c *execdriver.
 		}
 		}
 		container.Networks = append(container.Networks, &vethNetwork)
 		container.Networks = append(container.Networks, &vethNetwork)
 	}
 	}
+
+	if c.Network.ContainerID != "" {
+		cmd := d.activeContainers[c.Network.ContainerID]
+		if cmd == nil || cmd.Process == nil {
+			return fmt.Errorf("%s is not a valid running container to join", c.Network.ContainerID)
+		}
+		nspath := filepath.Join("/proc", fmt.Sprint(cmd.Process.Pid), "ns", "net")
+		container.Networks = append(container.Networks, &libcontainer.Network{
+			Type: "netns",
+			Context: libcontainer.Context{
+				"nspath": nspath,
+			},
+		})
+	}
 	return nil
 	return nil
 }
 }
 
 
 func (d *driver) setPrivileged(container *libcontainer.Container) error {
 func (d *driver) setPrivileged(container *libcontainer.Container) error {
-	for _, c := range container.CapabilitiesMask {
-		c.Enabled = true
+	for key := range container.CapabilitiesMask {
+		container.CapabilitiesMask[key] = true
 	}
 	}
 	container.Cgroups.DeviceAccess = true
 	container.Cgroups.DeviceAccess = true
-	container.Context["apparmor_profile"] = "unconfined"
+
+	delete(container.Context, "restrictions")
+
+	if apparmor.IsEnabled() {
+		container.Context["apparmor_profile"] = "unconfined"
+	}
 	return nil
 	return nil
 }
 }
 
 
@@ -88,6 +115,7 @@ func (d *driver) setupCgroups(container *libcontainer.Container, c *execdriver.C
 	if c.Resources != nil {
 	if c.Resources != nil {
 		container.Cgroups.CpuShares = c.Resources.CpuShares
 		container.Cgroups.CpuShares = c.Resources.CpuShares
 		container.Cgroups.Memory = c.Resources.Memory
 		container.Cgroups.Memory = c.Resources.Memory
+		container.Cgroups.MemoryReservation = c.Resources.Memory
 		container.Cgroups.MemorySwap = c.Resources.MemorySwap
 		container.Cgroups.MemorySwap = c.Resources.MemorySwap
 	}
 	}
 	return nil
 	return nil
@@ -95,20 +123,19 @@ func (d *driver) setupCgroups(container *libcontainer.Container, c *execdriver.C
 
 
 func (d *driver) setupMounts(container *libcontainer.Container, c *execdriver.Command) error {
 func (d *driver) setupMounts(container *libcontainer.Container, c *execdriver.Command) error {
 	for _, m := range c.Mounts {
 	for _, m := range c.Mounts {
-		container.Mounts = append(container.Mounts, libcontainer.Mount{m.Source, m.Destination, m.Writable, m.Private})
+		container.Mounts = append(container.Mounts, libcontainer.Mount{
+			Type:        "bind",
+			Source:      m.Source,
+			Destination: m.Destination,
+			Writable:    m.Writable,
+			Private:     m.Private,
+		})
 	}
 	}
 	return nil
 	return nil
 }
 }
 
 
 func (d *driver) setupLabels(container *libcontainer.Container, c *execdriver.Command) error {
 func (d *driver) setupLabels(container *libcontainer.Container, c *execdriver.Command) error {
-	labels := c.Config["label"]
-	if len(labels) > 0 {
-		process, mount, err := label.GenLabels(labels[0])
-		if err != nil {
-			return err
-		}
-		container.Context["mount_label"] = mount
-		container.Context["process_label"] = process
-	}
+	container.Context["process_label"] = c.Config["process_label"][0]
+	container.Context["mount_label"] = c.Config["mount_label"][0]
 	return nil
 	return nil
 }
 }

+ 56 - 94
runtime/execdriver/native/driver.go → daemon/execdriver/native/driver.go

@@ -3,35 +3,31 @@ package native
 import (
 import (
 	"encoding/json"
 	"encoding/json"
 	"fmt"
 	"fmt"
-	"github.com/dotcloud/docker/pkg/cgroups"
-	"github.com/dotcloud/docker/pkg/libcontainer"
-	"github.com/dotcloud/docker/pkg/libcontainer/apparmor"
-	"github.com/dotcloud/docker/pkg/libcontainer/nsinit"
-	"github.com/dotcloud/docker/pkg/system"
-	"github.com/dotcloud/docker/runtime/execdriver"
-	"io"
 	"io/ioutil"
 	"io/ioutil"
-	"log"
 	"os"
 	"os"
 	"os/exec"
 	"os/exec"
 	"path/filepath"
 	"path/filepath"
 	"strconv"
 	"strconv"
 	"strings"
 	"strings"
 	"syscall"
 	"syscall"
+
+	"github.com/dotcloud/docker/daemon/execdriver"
+	"github.com/dotcloud/docker/pkg/apparmor"
+	"github.com/dotcloud/docker/pkg/cgroups"
+	"github.com/dotcloud/docker/pkg/libcontainer"
+	"github.com/dotcloud/docker/pkg/libcontainer/nsinit"
+	"github.com/dotcloud/docker/pkg/system"
 )
 )
 
 
 const (
 const (
 	DriverName                = "native"
 	DriverName                = "native"
-	Version                   = "0.1"
+	Version                   = "0.2"
 	BackupApparmorProfilePath = "apparmor/docker.back" // relative to docker root
 	BackupApparmorProfilePath = "apparmor/docker.back" // relative to docker root
 )
 )
 
 
 func init() {
 func init() {
 	execdriver.RegisterInitFunc(DriverName, func(args *execdriver.InitArgs) error {
 	execdriver.RegisterInitFunc(DriverName, func(args *execdriver.InitArgs) error {
-		var (
-			container *libcontainer.Container
-			ns        = nsinit.NewNsInit(&nsinit.DefaultCommandFactory{}, &nsinit.DefaultStateWriter{args.Root}, createLogger(""))
-		)
+		var container *libcontainer.Container
 		f, err := os.Open(filepath.Join(args.Root, "container.json"))
 		f, err := os.Open(filepath.Join(args.Root, "container.json"))
 		if err != nil {
 		if err != nil {
 			return err
 			return err
@@ -42,7 +38,7 @@ func init() {
 		}
 		}
 		f.Close()
 		f.Close()
 
 
-		cwd, err := os.Getwd()
+		rootfs, err := os.Getwd()
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
@@ -50,7 +46,7 @@ func init() {
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
-		if err := ns.Init(container, cwd, args.Console, syncPipe, args.Args); err != nil {
+		if err := nsinit.Init(container, rootfs, args.Console, syncPipe, args.Args); err != nil {
 			return err
 			return err
 		}
 		}
 		return nil
 		return nil
@@ -87,35 +83,49 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba
 	d.activeContainers[c.ID] = &c.Cmd
 	d.activeContainers[c.ID] = &c.Cmd
 
 
 	var (
 	var (
-		term        nsinit.Terminal
-		factory     = &dockerCommandFactory{c: c, driver: d}
-		stateWriter = &dockerStateWriter{
-			callback: startCallback,
-			c:        c,
-			dsw:      &nsinit.DefaultStateWriter{filepath.Join(d.root, c.ID)},
-		}
-		ns   = nsinit.NewNsInit(factory, stateWriter, createLogger(os.Getenv("DEBUG")))
-		args = append([]string{c.Entrypoint}, c.Arguments...)
+		dataPath = filepath.Join(d.root, c.ID)
+		args     = append([]string{c.Entrypoint}, c.Arguments...)
 	)
 	)
 	if err := d.createContainerRoot(c.ID); err != nil {
 	if err := d.createContainerRoot(c.ID); err != nil {
 		return -1, err
 		return -1, err
 	}
 	}
 	defer d.removeContainerRoot(c.ID)
 	defer d.removeContainerRoot(c.ID)
 
 
-	if c.Tty {
-		term = &dockerTtyTerm{
-			pipes: pipes,
-		}
-	} else {
-		term = &dockerStdTerm{
-			pipes: pipes,
-		}
-	}
-	c.Terminal = term
 	if err := d.writeContainerFile(container, c.ID); err != nil {
 	if err := d.writeContainerFile(container, c.ID); err != nil {
 		return -1, err
 		return -1, err
 	}
 	}
-	return ns.Exec(container, term, args)
+
+	term := getTerminal(c, pipes)
+
+	return nsinit.Exec(container, term, c.Rootfs, dataPath, args, func(container *libcontainer.Container, console, rootfs, dataPath, init string, child *os.File, args []string) *exec.Cmd {
+		// we need to join the rootfs because nsinit will setup the rootfs and chroot
+		initPath := filepath.Join(c.Rootfs, c.InitPath)
+
+		c.Path = d.initPath
+		c.Args = append([]string{
+			initPath,
+			"-driver", DriverName,
+			"-console", console,
+			"-pipe", "3",
+			"-root", filepath.Join(d.root, c.ID),
+			"--",
+		}, args...)
+
+		// set this to nil so that when we set the clone flags anything else is reset
+		c.SysProcAttr = nil
+		system.SetCloneFlags(&c.Cmd, uintptr(nsinit.GetNamespaceFlags(container.Namespaces)))
+		c.ExtraFiles = []*os.File{child}
+
+		c.Env = container.Env
+		c.Dir = c.Rootfs
+
+		return &c.Cmd
+	}, func() {
+		if startCallback != nil {
+			c.ContainerPid = c.Process.Pid
+			startCallback(c)
+		}
+	})
 }
 }
 
 
 func (d *driver) Kill(p *execdriver.Command, sig int) error {
 func (d *driver) Kill(p *execdriver.Command, sig int) error {
@@ -228,65 +238,17 @@ func getEnv(key string, env []string) string {
 	return ""
 	return ""
 }
 }
 
 
-type dockerCommandFactory struct {
-	c      *execdriver.Command
-	driver *driver
-}
-
-// createCommand will return an exec.Cmd with the Cloneflags set to the proper namespaces
-// defined on the container's configuration and use the current binary as the init with the
-// args provided
-func (d *dockerCommandFactory) Create(container *libcontainer.Container, console string, syncFile *os.File, args []string) *exec.Cmd {
-	// we need to join the rootfs because nsinit will setup the rootfs and chroot
-	initPath := filepath.Join(d.c.Rootfs, d.c.InitPath)
-
-	d.c.Path = d.driver.initPath
-	d.c.Args = append([]string{
-		initPath,
-		"-driver", DriverName,
-		"-console", console,
-		"-pipe", "3",
-		"-root", filepath.Join(d.driver.root, d.c.ID),
-		"--",
-	}, args...)
-
-	// set this to nil so that when we set the clone flags anything else is reset
-	d.c.SysProcAttr = nil
-	system.SetCloneFlags(&d.c.Cmd, uintptr(nsinit.GetNamespaceFlags(container.Namespaces)))
-	d.c.ExtraFiles = []*os.File{syncFile}
-
-	d.c.Env = container.Env
-	d.c.Dir = d.c.Rootfs
-
-	return &d.c.Cmd
-}
-
-type dockerStateWriter struct {
-	dsw      nsinit.StateWriter
-	c        *execdriver.Command
-	callback execdriver.StartCallback
-}
-
-func (d *dockerStateWriter) WritePid(pid int, started string) error {
-	d.c.ContainerPid = pid
-	err := d.dsw.WritePid(pid, started)
-	if d.callback != nil {
-		d.callback(d.c)
-	}
-	return err
-}
-
-func (d *dockerStateWriter) DeletePid() error {
-	return d.dsw.DeletePid()
-}
-
-func createLogger(debug string) *log.Logger {
-	var w io.Writer
-	// if we are in debug mode set the logger to stderr
-	if debug != "" {
-		w = os.Stderr
+func getTerminal(c *execdriver.Command, pipes *execdriver.Pipes) nsinit.Terminal {
+	var term nsinit.Terminal
+	if c.Tty {
+		term = &dockerTtyTerm{
+			pipes: pipes,
+		}
 	} else {
 	} else {
-		w = ioutil.Discard
+		term = &dockerStdTerm{
+			pipes: pipes,
+		}
 	}
 	}
-	return log.New(w, "[libcontainer] ", log.LstdFlags)
+	c.Terminal = term
+	return term
 }
 }

+ 0 - 0
runtime/execdriver/native/info.go → daemon/execdriver/native/info.go


+ 47 - 0
daemon/execdriver/native/template/default_template.go

@@ -0,0 +1,47 @@
+package template
+
+import (
+	"github.com/dotcloud/docker/pkg/apparmor"
+	"github.com/dotcloud/docker/pkg/cgroups"
+	"github.com/dotcloud/docker/pkg/libcontainer"
+)
+
+// New returns the docker default configuration for libcontainer
+func New() *libcontainer.Container {
+	container := &libcontainer.Container{
+		CapabilitiesMask: map[string]bool{
+			"SETPCAP":        false,
+			"SYS_MODULE":     false,
+			"SYS_RAWIO":      false,
+			"SYS_PACCT":      false,
+			"SYS_ADMIN":      false,
+			"SYS_NICE":       false,
+			"SYS_RESOURCE":   false,
+			"SYS_TIME":       false,
+			"SYS_TTY_CONFIG": false,
+			"AUDIT_WRITE":    false,
+			"AUDIT_CONTROL":  false,
+			"MAC_OVERRIDE":   false,
+			"MAC_ADMIN":      false,
+			"NET_ADMIN":      false,
+			"MKNOD":          true,
+			"SYSLOG":         false,
+		},
+		Namespaces: map[string]bool{
+			"NEWNS":  true,
+			"NEWUTS": true,
+			"NEWIPC": true,
+			"NEWPID": true,
+			"NEWNET": true,
+		},
+		Cgroups: &cgroups.Cgroup{
+			Parent:       "docker",
+			DeviceAccess: false,
+		},
+		Context: libcontainer.Context{},
+	}
+	if apparmor.IsEnabled() {
+		container.Context["apparmor_profile"] = "docker-default"
+	}
+	return container
+}

+ 1 - 1
runtime/execdriver/native/term.go → daemon/execdriver/native/term.go

@@ -5,7 +5,7 @@
 package native
 package native
 
 
 import (
 import (
-	"github.com/dotcloud/docker/runtime/execdriver"
+	"github.com/dotcloud/docker/daemon/execdriver"
 	"io"
 	"io"
 	"os"
 	"os"
 	"os/exec"
 	"os/exec"

+ 0 - 0
runtime/execdriver/pipes.go → daemon/execdriver/pipes.go


+ 0 - 0
runtime/execdriver/termconsole.go → daemon/execdriver/termconsole.go


この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません