浏览代码

Update libcontainer to 1597c68f7b941fd97881155d7f077852e2914e7b

This commit contains changes for docker:
* user.GetGroupFile to user.GetGroupPath docker/libcontainer#301
* Add systemd support for OOM docker/libcontainer#307
* Support for custom namespaces docker/libcontainer#279, docker/libcontainer#312
* Fixes #9699 docker/libcontainer#308

Signed-off-by: Alexander Morozov <lk4d4@docker.com>
Alexander Morozov 10 年之前
父节点
当前提交
50905a6d6c
共有 43 个文件被更改,包括 515 次插入360 次删除
  1. 1 1
      api/server/server.go
  2. 4 7
      daemon/execdriver/native/create.go
  3. 8 7
      daemon/execdriver/native/driver.go
  4. 6 6
      daemon/execdriver/native/template/default_template.go
  5. 1 1
      project/vendor.sh
  6. 3 3
      vendor/src/github.com/docker/libcontainer/Makefile
  7. 25 0
      vendor/src/github.com/docker/libcontainer/SPEC.md
  8. 1 0
      vendor/src/github.com/docker/libcontainer/cgroups/cgroups.go
  9. 11 6
      vendor/src/github.com/docker/libcontainer/cgroups/fs/cpuset.go
  10. 7 2
      vendor/src/github.com/docker/libcontainer/cgroups/fs/memory.go
  11. 4 0
      vendor/src/github.com/docker/libcontainer/cgroups/fs/stats_util_test.go
  12. 1 1
      vendor/src/github.com/docker/libcontainer/cgroups/systemd/apply_systemd.go
  13. 24 6
      vendor/src/github.com/docker/libcontainer/cgroups/utils.go
  14. 52 4
      vendor/src/github.com/docker/libcontainer/config.go
  15. 2 2
      vendor/src/github.com/docker/libcontainer/config_test.go
  16. 5 6
      vendor/src/github.com/docker/libcontainer/integration/exec_test.go
  17. 140 0
      vendor/src/github.com/docker/libcontainer/integration/execin_test.go
  18. 54 11
      vendor/src/github.com/docker/libcontainer/integration/init_test.go
  19. 6 6
      vendor/src/github.com/docker/libcontainer/integration/template_test.go
  20. 0 29
      vendor/src/github.com/docker/libcontainer/ipc/ipc.go
  21. 4 0
      vendor/src/github.com/docker/libcontainer/namespaces/execin.go
  22. 27 8
      vendor/src/github.com/docker/libcontainer/namespaces/init.go
  23. 11 6
      vendor/src/github.com/docker/libcontainer/namespaces/nsenter/nsenter.c
  24. 0 50
      vendor/src/github.com/docker/libcontainer/namespaces/types.go
  25. 0 16
      vendor/src/github.com/docker/libcontainer/namespaces/types_linux.go
  26. 0 30
      vendor/src/github.com/docker/libcontainer/namespaces/types_test.go
  27. 14 7
      vendor/src/github.com/docker/libcontainer/namespaces/utils.go
  28. 2 3
      vendor/src/github.com/docker/libcontainer/netlink/netlink_linux.go
  29. 0 39
      vendor/src/github.com/docker/libcontainer/network/netns.go
  30. 12 0
      vendor/src/github.com/docker/libcontainer/network/network.go
  31. 0 1
      vendor/src/github.com/docker/libcontainer/network/strategy.go
  32. 0 5
      vendor/src/github.com/docker/libcontainer/network/types.go
  33. 15 35
      vendor/src/github.com/docker/libcontainer/notify_linux.go
  34. 27 15
      vendor/src/github.com/docker/libcontainer/notify_linux_test.go
  35. 7 7
      vendor/src/github.com/docker/libcontainer/sample_configs/apparmor.json
  36. 7 7
      vendor/src/github.com/docker/libcontainer/sample_configs/attach_to_bridge.json
  37. 7 7
      vendor/src/github.com/docker/libcontainer/sample_configs/minimal.json
  38. 7 7
      vendor/src/github.com/docker/libcontainer/sample_configs/route_source_address_selection.json
  39. 7 7
      vendor/src/github.com/docker/libcontainer/sample_configs/selinux.json
  40. 1 0
      vendor/src/github.com/docker/libcontainer/user/MAINTAINERS
  41. 8 8
      vendor/src/github.com/docker/libcontainer/user/lookup_unix.go
  42. 2 2
      vendor/src/github.com/docker/libcontainer/user/lookup_unsupported.go
  43. 2 2
      vendor/src/github.com/docker/libcontainer/user/user.go

+ 1 - 1
api/server/server.go

@@ -1399,7 +1399,7 @@ func serveFd(addr string, job *engine.Job) error {
 }
 }
 
 
 func lookupGidByName(nameOrGid string) (int, error) {
 func lookupGidByName(nameOrGid string) (int, error) {
-	groupFile, err := user.GetGroupFile()
+	groupFile, err := user.GetGroupPath()
 	if err != nil {
 	if err != nil {
 		return -1, err
 		return -1, err
 	}
 	}

+ 4 - 7
daemon/execdriver/native/create.go

@@ -82,7 +82,7 @@ func (d *driver) createContainer(c *execdriver.Command) (*libcontainer.Config, e
 
 
 func (d *driver) createNetwork(container *libcontainer.Config, c *execdriver.Command) error {
 func (d *driver) createNetwork(container *libcontainer.Config, c *execdriver.Command) error {
 	if c.Network.HostNetworking {
 	if c.Network.HostNetworking {
-		container.Namespaces["NEWNET"] = false
+		container.Namespaces.Remove(libcontainer.NEWNET)
 		return nil
 		return nil
 	}
 	}
 
 
@@ -119,10 +119,7 @@ func (d *driver) createNetwork(container *libcontainer.Config, c *execdriver.Com
 		cmd := active.cmd
 		cmd := active.cmd
 
 
 		nspath := filepath.Join("/proc", fmt.Sprint(cmd.Process.Pid), "ns", "net")
 		nspath := filepath.Join("/proc", fmt.Sprint(cmd.Process.Pid), "ns", "net")
-		container.Networks = append(container.Networks, &libcontainer.Network{
-			Type:   "netns",
-			NsPath: nspath,
-		})
+		container.Namespaces.Add(libcontainer.NEWNET, nspath)
 	}
 	}
 
 
 	return nil
 	return nil
@@ -130,7 +127,7 @@ func (d *driver) createNetwork(container *libcontainer.Config, c *execdriver.Com
 
 
 func (d *driver) createIpc(container *libcontainer.Config, c *execdriver.Command) error {
 func (d *driver) createIpc(container *libcontainer.Config, c *execdriver.Command) error {
 	if c.Ipc.HostIpc {
 	if c.Ipc.HostIpc {
-		container.Namespaces["NEWIPC"] = false
+		container.Namespaces.Remove(libcontainer.NEWIPC)
 		return nil
 		return nil
 	}
 	}
 
 
@@ -144,7 +141,7 @@ func (d *driver) createIpc(container *libcontainer.Config, c *execdriver.Command
 		}
 		}
 		cmd := active.cmd
 		cmd := active.cmd
 
 
-		container.IpcNsPath = filepath.Join("/proc", fmt.Sprint(cmd.Process.Pid), "ns", "ipc")
+		container.Namespaces.Add(libcontainer.NEWIPC, filepath.Join("/proc", fmt.Sprint(cmd.Process.Pid), "ns", "ipc"))
 	}
 	}
 
 
 	return nil
 	return nil

+ 8 - 7
daemon/execdriver/native/driver.go

@@ -61,10 +61,6 @@ func NewDriver(root, initPath string) (*driver, error) {
 	}, nil
 	}, nil
 }
 }
 
 
-func (d *driver) notifyOnOOM(config *libcontainer.Config) (<-chan struct{}, error) {
-	return fs.NotifyOnOOM(config.Cgroups)
-}
-
 type execOutput struct {
 type execOutput struct {
 	exitCode int
 	exitCode int
 	err      error
 	err      error
@@ -152,11 +148,16 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba
 	}
 	}
 
 
 	oomKill := false
 	oomKill := false
-	oomKillNotification, err := d.notifyOnOOM(container)
+	state, err := libcontainer.GetState(filepath.Join(d.root, c.ID))
 	if err == nil {
 	if err == nil {
-		_, oomKill = <-oomKillNotification
+		oomKillNotification, err := libcontainer.NotifyOnOOM(state)
+		if err == nil {
+			_, oomKill = <-oomKillNotification
+		} else {
+			log.Warnf("WARNING: Your kernel does not support OOM notifications: %s", err)
+		}
 	} else {
 	} else {
-		log.Warnf("WARNING: Your kernel does not support OOM notifications: %s", err)
+		log.Warnf("Failed to get container state, oom notify will not work: %s", err)
 	}
 	}
 	// wait for the container to exit.
 	// wait for the container to exit.
 	execOutput := <-execOutputChan
 	execOutput := <-execOutputChan

+ 6 - 6
daemon/execdriver/native/template/default_template.go

@@ -25,12 +25,12 @@ func New() *libcontainer.Config {
 			"KILL",
 			"KILL",
 			"AUDIT_WRITE",
 			"AUDIT_WRITE",
 		},
 		},
-		Namespaces: map[string]bool{
-			"NEWNS":  true,
-			"NEWUTS": true,
-			"NEWIPC": true,
-			"NEWPID": true,
-			"NEWNET": true,
+		Namespaces: libcontainer.Namespaces{
+			{Type: "NEWNS"},
+			{Type: "NEWUTS"},
+			{Type: "NEWIPC"},
+			{Type: "NEWPID"},
+			{Type: "NEWNET"},
 		},
 		},
 		Cgroups: &cgroups.Cgroup{
 		Cgroups: &cgroups.Cgroup{
 			Parent:          "docker",
 			Parent:          "docker",

+ 1 - 1
project/vendor.sh

@@ -66,7 +66,7 @@ if [ "$1" = '--go' ]; then
 	mv tmp-tar src/code.google.com/p/go/src/pkg/archive/tar
 	mv tmp-tar src/code.google.com/p/go/src/pkg/archive/tar
 fi
 fi
 
 
-clone git github.com/docker/libcontainer 53eca435e63db58b06cf796d3a9326db5fd42253
+clone git github.com/docker/libcontainer 1597c68f7b941fd97881155d7f077852e2914e7b
 # see src/github.com/docker/libcontainer/update-vendor.sh which is the "source of truth" for libcontainer deps (just like this file)
 # see src/github.com/docker/libcontainer/update-vendor.sh which is the "source of truth" for libcontainer deps (just like this file)
 rm -rf src/github.com/docker/libcontainer/vendor
 rm -rf src/github.com/docker/libcontainer/vendor
 eval "$(grep '^clone ' src/github.com/docker/libcontainer/update-vendor.sh | grep -v 'github.com/codegangsta/cli')"
 eval "$(grep '^clone ' src/github.com/docker/libcontainer/update-vendor.sh | grep -v 'github.com/codegangsta/cli')"

+ 3 - 3
vendor/src/github.com/docker/libcontainer/Makefile

@@ -1,13 +1,13 @@
 
 
 all:
 all:
-	docker build -t docker/libcontainer .
+	docker build -t dockercore/libcontainer .
 
 
 test: 
 test: 
 	# we need NET_ADMIN for the netlink tests and SYS_ADMIN for mounting
 	# we need NET_ADMIN for the netlink tests and SYS_ADMIN for mounting
-	docker run --rm -it --privileged docker/libcontainer
+	docker run --rm -it --privileged dockercore/libcontainer
 
 
 sh:
 sh:
-	docker run --rm -it --privileged -w /busybox docker/libcontainer nsinit exec sh
+	docker run --rm -it --privileged -w /busybox dockercore/libcontainer nsinit exec sh
 
 
 GO_PACKAGES = $(shell find . -not \( -wholename ./vendor -prune -o -wholename ./.git -prune \) -name '*.go' -print0 | xargs -0n1 dirname | sort -u)
 GO_PACKAGES = $(shell find . -not \( -wholename ./vendor -prune -o -wholename ./.git -prune \) -name '*.go' -print0 | xargs -0n1 dirname | sort -u)
 
 

+ 25 - 0
vendor/src/github.com/docker/libcontainer/SPEC.md

@@ -318,4 +318,29 @@ a container.
 | Resume         | Resume all processes inside the container if paused                |
 | Resume         | Resume all processes inside the container if paused                |
 | Exec           | Execute a new process inside of the container  ( requires setns )  |
 | Exec           | Execute a new process inside of the container  ( requires setns )  |
 
 
+### Execute a new process inside of a running container.
 
 
+User can execute a new process inside of a running container. Any binaries to be
+executed must be accessible within the container's rootfs.
+
+The started process will run inside the container's rootfs. Any changes
+made by the process to the container's filesystem will persist after the
+process finished executing.
+
+The started process will join all the container's existing namespaces. When the
+container is paused, the process will also be paused and will resume when
+the container is unpaused.  The started process will only run when the container's
+primary process (PID 1) is running, and will not be restarted when the container
+is restarted.
+
+#### Planned additions
+
+The started process will have its own cgroups nested inside the container's
+cgroups. This is used for process tracking and optionally resource allocation
+handling for the new process. Freezer cgroup is required, the rest of the cgroups
+are optional. The process executor must place its pid inside the correct
+cgroups before starting the process. This is done so that no child processes or
+threads can escape the cgroups.
+
+When the process is stopped, the process executor will try (in a best-effort way)
+to stop all its children and remove the sub-cgroups.

+ 1 - 0
vendor/src/github.com/docker/libcontainer/cgroups/cgroups.go

@@ -50,6 +50,7 @@ type Cgroup struct {
 	CpuQuota          int64             `json:"cpu_quota,omitempty"`          // CPU hardcap limit (in usecs). Allowed cpu time in a given period.
 	CpuQuota          int64             `json:"cpu_quota,omitempty"`          // CPU hardcap limit (in usecs). Allowed cpu time in a given period.
 	CpuPeriod         int64             `json:"cpu_period,omitempty"`         // CPU period to be used for hardcapping (in usecs). 0 to use system default.
 	CpuPeriod         int64             `json:"cpu_period,omitempty"`         // CPU period to be used for hardcapping (in usecs). 0 to use system default.
 	CpusetCpus        string            `json:"cpuset_cpus,omitempty"`        // CPU to use
 	CpusetCpus        string            `json:"cpuset_cpus,omitempty"`        // CPU to use
+	CpusetMems        string            `json:"cpuset_mems,omitempty"`        // MEM to use
 	Freezer           FreezerState      `json:"freezer,omitempty"`            // set the freeze value for the process
 	Freezer           FreezerState      `json:"freezer,omitempty"`            // set the freeze value for the process
 	Slice             string            `json:"slice,omitempty"`              // Parent slice to use for systemd
 	Slice             string            `json:"slice,omitempty"`              // Parent slice to use for systemd
 }
 }

+ 11 - 6
vendor/src/github.com/docker/libcontainer/cgroups/fs/cpuset.go

@@ -18,7 +18,7 @@ func (s *CpusetGroup) Set(d *data) error {
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	return s.SetDir(dir, d.c.CpusetCpus, d.pid)
+	return s.SetDir(dir, d.c.CpusetCpus, d.c.CpusetMems, d.pid)
 }
 }
 
 
 func (s *CpusetGroup) Remove(d *data) error {
 func (s *CpusetGroup) Remove(d *data) error {
@@ -29,7 +29,7 @@ func (s *CpusetGroup) GetStats(path string, stats *cgroups.Stats) error {
 	return nil
 	return nil
 }
 }
 
 
-func (s *CpusetGroup) SetDir(dir, value string, pid int) error {
+func (s *CpusetGroup) SetDir(dir, cpus string, mems string, pid int) error {
 	if err := s.ensureParent(dir); err != nil {
 	if err := s.ensureParent(dir); err != nil {
 		return err
 		return err
 	}
 	}
@@ -40,10 +40,15 @@ func (s *CpusetGroup) SetDir(dir, value string, pid int) error {
 		return err
 		return err
 	}
 	}
 
 
-	// If we don't use --cpuset, the default cpuset.cpus is set in
-	// s.ensureParent, otherwise, use the value we set
-	if value != "" {
-		if err := writeFile(dir, "cpuset.cpus", value); err != nil {
+	// If we don't use --cpuset-xxx, the default value inherit from parent cgroup
+	// is set in s.ensureParent, otherwise, use the value we set
+	if cpus != "" {
+		if err := writeFile(dir, "cpuset.cpus", cpus); err != nil {
+			return err
+		}
+	}
+	if mems != "" {
+		if err := writeFile(dir, "cpuset.mems", mems); err != nil {
 			return err
 			return err
 		}
 		}
 	}
 	}

+ 7 - 2
vendor/src/github.com/docker/libcontainer/cgroups/fs/memory.go

@@ -38,12 +38,17 @@ func (s *MemoryGroup) Set(d *data) error {
 			}
 			}
 		}
 		}
 		// By default, MemorySwap is set to twice the size of RAM.
 		// By default, MemorySwap is set to twice the size of RAM.
-		// If you want to omit MemorySwap, set it to `-1'.
-		if d.c.MemorySwap != -1 {
+		// If you want to omit MemorySwap, set it to '-1'.
+		if d.c.MemorySwap == 0 {
 			if err := writeFile(dir, "memory.memsw.limit_in_bytes", strconv.FormatInt(d.c.Memory*2, 10)); err != nil {
 			if err := writeFile(dir, "memory.memsw.limit_in_bytes", strconv.FormatInt(d.c.Memory*2, 10)); err != nil {
 				return err
 				return err
 			}
 			}
 		}
 		}
+		if d.c.MemorySwap > 0 {
+			if err := writeFile(dir, "memory.memsw.limit_in_bytes", strconv.FormatInt(d.c.MemorySwap, 10)); err != nil {
+				return err
+			}
+		}
 	}
 	}
 	return nil
 	return nil
 }
 }

+ 4 - 0
vendor/src/github.com/docker/libcontainer/cgroups/fs/stats_util_test.go

@@ -90,4 +90,8 @@ func expectMemoryStatEquals(t *testing.T, expected, actual cgroups.MemoryStats)
 			t.Fail()
 			t.Fail()
 		}
 		}
 	}
 	}
+	if expected.Failcnt != actual.Failcnt {
+		log.Printf("Expected memory failcnt %d but found %d\n", expected.Failcnt, actual.Failcnt)
+		t.Fail()
+	}
 }
 }

+ 1 - 1
vendor/src/github.com/docker/libcontainer/cgroups/systemd/apply_systemd.go

@@ -313,5 +313,5 @@ func joinCpuset(c *cgroups.Cgroup, pid int) error {
 
 
 	s := &fs.CpusetGroup{}
 	s := &fs.CpusetGroup{}
 
 
-	return s.SetDir(path, c.CpusetCpus, pid)
+	return s.SetDir(path, c.CpusetCpus, c.CpusetMems, pid)
 }
 }

+ 24 - 6
vendor/src/github.com/docker/libcontainer/cgroups/utils.go

@@ -9,6 +9,7 @@ import (
 	"path/filepath"
 	"path/filepath"
 	"strconv"
 	"strconv"
 	"strings"
 	"strings"
+	"time"
 
 
 	"github.com/docker/docker/pkg/mount"
 	"github.com/docker/docker/pkg/mount"
 )
 )
@@ -193,13 +194,30 @@ func EnterPid(cgroupPaths map[string]string, pid int) error {
 }
 }
 
 
 // RemovePaths iterates over the provided paths removing them.
 // RemovePaths iterates over the provided paths removing them.
-// If an error is encountered the removal proceeds and the first error is
-// returned to ensure a partial removal is not possible.
+// We trying to remove all paths five times with increasing delay between tries.
+// If after all there are not removed cgroups - appropriate error will be
+// returned.
 func RemovePaths(paths map[string]string) (err error) {
 func RemovePaths(paths map[string]string) (err error) {
-	for _, path := range paths {
-		if rerr := os.RemoveAll(path); err == nil {
-			err = rerr
+	delay := 10 * time.Millisecond
+	for i := 0; i < 5; i++ {
+		if i != 0 {
+			time.Sleep(delay)
+			delay *= 2
+		}
+		for s, p := range paths {
+			os.RemoveAll(p)
+			// TODO: here probably should be logging
+			_, err := os.Stat(p)
+			// We need this strange way of checking cgroups existence because
+			// RemoveAll almost always returns error, even on already removed
+			// cgroups
+			if os.IsNotExist(err) {
+				delete(paths, s)
+			}
+		}
+		if len(paths) == 0 {
+			return nil
 		}
 		}
 	}
 	}
-	return err
+	return fmt.Errorf("Failed to remove paths: %s", paths)
 }
 }

+ 52 - 4
vendor/src/github.com/docker/libcontainer/config.go

@@ -10,6 +10,57 @@ type MountConfig mount.MountConfig
 
 
 type Network network.Network
 type Network network.Network
 
 
+type NamespaceType string
+
+const (
+	NEWNET  NamespaceType = "NEWNET"
+	NEWPID  NamespaceType = "NEWPID"
+	NEWNS   NamespaceType = "NEWNS"
+	NEWUTS  NamespaceType = "NEWUTS"
+	NEWIPC  NamespaceType = "NEWIPC"
+	NEWUSER NamespaceType = "NEWUSER"
+)
+
+// Namespace defines configuration for each namespace.  It specifies an
+// alternate path that is able to be joined via setns.
+type Namespace struct {
+	Type NamespaceType `json:"type"`
+	Path string        `json:"path,omitempty"`
+}
+
+type Namespaces []Namespace
+
+func (n Namespaces) Remove(t NamespaceType) bool {
+	i := n.index(t)
+	if i == -1 {
+		return false
+	}
+	n = append(n[:i], n[i+1:]...)
+	return true
+}
+
+func (n Namespaces) Add(t NamespaceType, path string) {
+	i := n.index(t)
+	if i == -1 {
+		n = append(n, Namespace{Type: t, Path: path})
+		return
+	}
+	n[i].Path = path
+}
+
+func (n Namespaces) index(t NamespaceType) int {
+	for i, ns := range n {
+		if ns.Type == t {
+			return i
+		}
+	}
+	return -1
+}
+
+func (n Namespaces) Contains(t NamespaceType) bool {
+	return n.index(t) != -1
+}
+
 // Config defines configuration options for executing a process inside a contained environment.
 // Config defines configuration options for executing a process inside a contained environment.
 type Config struct {
 type Config struct {
 	// Mount specific options.
 	// Mount specific options.
@@ -38,7 +89,7 @@ type Config struct {
 
 
 	// Namespaces specifies the container's namespaces that it should setup when cloning the init process
 	// Namespaces specifies the container's namespaces that it should setup when cloning the init process
 	// If a namespace is not provided that namespace is shared from the container's parent process
 	// If a namespace is not provided that namespace is shared from the container's parent process
-	Namespaces map[string]bool `json:"namespaces,omitempty"`
+	Namespaces Namespaces `json:"namespaces,omitempty"`
 
 
 	// Capabilities specify the capabilities to keep when executing the process inside the container
 	// Capabilities specify the capabilities to keep when executing the process inside the container
 	// All capbilities not specified will be dropped from the processes capability mask
 	// All capbilities not specified will be dropped from the processes capability mask
@@ -47,9 +98,6 @@ type Config struct {
 	// Networks specifies the container's network setup to be created
 	// Networks specifies the container's network setup to be created
 	Networks []*Network `json:"networks,omitempty"`
 	Networks []*Network `json:"networks,omitempty"`
 
 
-	// Ipc specifies the container's ipc setup to be created
-	IpcNsPath string `json:"ipc,omitempty"`
-
 	// Routes can be specified to create entries in the route table as the container is started
 	// Routes can be specified to create entries in the route table as the container is started
 	Routes []*Route `json:"routes,omitempty"`
 	Routes []*Route `json:"routes,omitempty"`
 
 

+ 2 - 2
vendor/src/github.com/docker/libcontainer/config_test.go

@@ -64,12 +64,12 @@ func TestConfigJsonFormat(t *testing.T) {
 		t.Fail()
 		t.Fail()
 	}
 	}
 
 
-	if !container.Namespaces["NEWNET"] {
+	if !container.Namespaces.Contains(NEWNET) {
 		t.Log("namespaces should contain NEWNET")
 		t.Log("namespaces should contain NEWNET")
 		t.Fail()
 		t.Fail()
 	}
 	}
 
 
-	if container.Namespaces["NEWUSER"] {
+	if container.Namespaces.Contains(NEWUSER) {
 		t.Log("namespaces should not contain NEWUSER")
 		t.Log("namespaces should not contain NEWUSER")
 		t.Fail()
 		t.Fail()
 	}
 	}

+ 5 - 6
vendor/src/github.com/docker/libcontainer/integration/exec_test.go

@@ -4,6 +4,8 @@ import (
 	"os"
 	"os"
 	"strings"
 	"strings"
 	"testing"
 	"testing"
+
+	"github.com/docker/libcontainer"
 )
 )
 
 
 func TestExecPS(t *testing.T) {
 func TestExecPS(t *testing.T) {
@@ -55,7 +57,6 @@ func TestIPCPrivate(t *testing.T) {
 	}
 	}
 
 
 	config := newTemplateConfig(rootfs)
 	config := newTemplateConfig(rootfs)
-	config.Namespaces["NEWIPC"] = true
 	buffers, exitCode, err := runContainer(config, "", "readlink", "/proc/self/ns/ipc")
 	buffers, exitCode, err := runContainer(config, "", "readlink", "/proc/self/ns/ipc")
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -87,7 +88,7 @@ func TestIPCHost(t *testing.T) {
 	}
 	}
 
 
 	config := newTemplateConfig(rootfs)
 	config := newTemplateConfig(rootfs)
-	config.Namespaces["NEWIPC"] = false
+	config.Namespaces.Remove(libcontainer.NEWIPC)
 	buffers, exitCode, err := runContainer(config, "", "readlink", "/proc/self/ns/ipc")
 	buffers, exitCode, err := runContainer(config, "", "readlink", "/proc/self/ns/ipc")
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -119,8 +120,7 @@ func TestIPCJoinPath(t *testing.T) {
 	}
 	}
 
 
 	config := newTemplateConfig(rootfs)
 	config := newTemplateConfig(rootfs)
-	config.Namespaces["NEWIPC"] = false
-	config.IpcNsPath = "/proc/1/ns/ipc"
+	config.Namespaces.Add(libcontainer.NEWIPC, "/proc/1/ns/ipc")
 
 
 	buffers, exitCode, err := runContainer(config, "", "readlink", "/proc/self/ns/ipc")
 	buffers, exitCode, err := runContainer(config, "", "readlink", "/proc/self/ns/ipc")
 	if err != nil {
 	if err != nil {
@@ -148,8 +148,7 @@ func TestIPCBadPath(t *testing.T) {
 	defer remove(rootfs)
 	defer remove(rootfs)
 
 
 	config := newTemplateConfig(rootfs)
 	config := newTemplateConfig(rootfs)
-	config.Namespaces["NEWIPC"] = false
-	config.IpcNsPath = "/proc/1/ns/ipcc"
+	config.Namespaces.Add(libcontainer.NEWIPC, "/proc/1/ns/ipcc")
 
 
 	_, _, err = runContainer(config, "", "true")
 	_, _, err = runContainer(config, "", "true")
 	if err == nil {
 	if err == nil {

+ 140 - 0
vendor/src/github.com/docker/libcontainer/integration/execin_test.go

@@ -0,0 +1,140 @@
+package integration
+
+import (
+	"os"
+	"os/exec"
+	"strings"
+	"sync"
+	"testing"
+
+	"github.com/docker/libcontainer"
+	"github.com/docker/libcontainer/namespaces"
+)
+
+func TestExecIn(t *testing.T) {
+	if testing.Short() {
+		return
+	}
+
+	rootfs, err := newRootFs()
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer remove(rootfs)
+
+	config := newTemplateConfig(rootfs)
+	if err := writeConfig(config); err != nil {
+		t.Fatalf("failed to write config %s", err)
+	}
+
+	containerCmd, statePath, containerErr := startLongRunningContainer(config)
+	defer func() {
+		// kill the container
+		if containerCmd.Process != nil {
+			containerCmd.Process.Kill()
+		}
+		if err := <-containerErr; err != nil {
+			t.Fatal(err)
+		}
+	}()
+
+	// start the exec process
+	state, err := libcontainer.GetState(statePath)
+	if err != nil {
+		t.Fatalf("failed to get state %s", err)
+	}
+	buffers := newStdBuffers()
+	execErr := make(chan error, 1)
+	go func() {
+		_, err := namespaces.ExecIn(config, state, []string{"ps"},
+			os.Args[0], "exec", buffers.Stdin, buffers.Stdout, buffers.Stderr,
+			"", nil)
+		execErr <- err
+	}()
+	if err := <-execErr; err != nil {
+		t.Fatalf("exec finished with error %s", err)
+	}
+
+	out := buffers.Stdout.String()
+	if !strings.Contains(out, "sleep 10") || !strings.Contains(out, "ps") {
+		t.Fatalf("unexpected running process, output %q", out)
+	}
+}
+
+func TestExecInRlimit(t *testing.T) {
+	if testing.Short() {
+		return
+	}
+
+	rootfs, err := newRootFs()
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer remove(rootfs)
+
+	config := newTemplateConfig(rootfs)
+	if err := writeConfig(config); err != nil {
+		t.Fatalf("failed to write config %s", err)
+	}
+
+	containerCmd, statePath, containerErr := startLongRunningContainer(config)
+	defer func() {
+		// kill the container
+		if containerCmd.Process != nil {
+			containerCmd.Process.Kill()
+		}
+		if err := <-containerErr; err != nil {
+			t.Fatal(err)
+		}
+	}()
+
+	// start the exec process
+	state, err := libcontainer.GetState(statePath)
+	if err != nil {
+		t.Fatalf("failed to get state %s", err)
+	}
+	buffers := newStdBuffers()
+	execErr := make(chan error, 1)
+	go func() {
+		_, err := namespaces.ExecIn(config, state, []string{"/bin/sh", "-c", "ulimit -n"},
+			os.Args[0], "exec", buffers.Stdin, buffers.Stdout, buffers.Stderr,
+			"", nil)
+		execErr <- err
+	}()
+	if err := <-execErr; err != nil {
+		t.Fatalf("exec finished with error %s", err)
+	}
+
+	out := buffers.Stdout.String()
+	if limit := strings.TrimSpace(out); limit != "1024" {
+		t.Fatalf("expected rlimit to be 1024, got %s", limit)
+	}
+}
+
+// start a long-running container so we have time to inspect execin processes
+func startLongRunningContainer(config *libcontainer.Config) (*exec.Cmd, string, chan error) {
+	containerErr := make(chan error, 1)
+	containerCmd := &exec.Cmd{}
+	var statePath string
+
+	createCmd := func(container *libcontainer.Config, console, dataPath, init string,
+		pipe *os.File, args []string) *exec.Cmd {
+		containerCmd = namespaces.DefaultCreateCommand(container, console, dataPath, init, pipe, args)
+		statePath = dataPath
+		return containerCmd
+	}
+
+	var containerStart sync.WaitGroup
+	containerStart.Add(1)
+	go func() {
+		buffers := newStdBuffers()
+		_, err := namespaces.Exec(config,
+			buffers.Stdin, buffers.Stdout, buffers.Stderr,
+			"", config.RootFs, []string{"sleep", "10"},
+			createCmd, containerStart.Done)
+		containerErr <- err
+	}()
+	containerStart.Wait()
+
+	return containerCmd, statePath, containerErr
+}

+ 54 - 11
vendor/src/github.com/docker/libcontainer/integration/init_test.go

@@ -1,33 +1,76 @@
 package integration
 package integration
 
 
 import (
 import (
+	"encoding/json"
 	"log"
 	"log"
 	"os"
 	"os"
 	"runtime"
 	"runtime"
 
 
+	"github.com/docker/libcontainer"
 	"github.com/docker/libcontainer/namespaces"
 	"github.com/docker/libcontainer/namespaces"
+	_ "github.com/docker/libcontainer/namespaces/nsenter"
 )
 )
 
 
 // init runs the libcontainer initialization code because of the busybox style needs
 // init runs the libcontainer initialization code because of the busybox style needs
 // to work around the go runtime and the issues with forking
 // to work around the go runtime and the issues with forking
 func init() {
 func init() {
-	if len(os.Args) < 2 || os.Args[1] != "init" {
+	if len(os.Args) < 2 {
 		return
 		return
 	}
 	}
-	runtime.LockOSThread()
+	// handle init
+	if len(os.Args) >= 2 && os.Args[1] == "init" {
+		runtime.LockOSThread()
 
 
-	container, err := loadConfig()
-	if err != nil {
-		log.Fatal(err)
+		container, err := loadConfig()
+		if err != nil {
+			log.Fatal(err)
+		}
+
+		rootfs, err := os.Getwd()
+		if err != nil {
+			log.Fatal(err)
+		}
+
+		if err := namespaces.Init(container, rootfs, "", os.NewFile(3, "pipe"), os.Args[3:]); err != nil {
+			log.Fatalf("unable to initialize for container: %s", err)
+		}
+		os.Exit(1)
 	}
 	}
 
 
-	rootfs, err := os.Getwd()
-	if err != nil {
-		log.Fatal(err)
+	// handle execin
+	if len(os.Args) >= 2 && os.Args[0] == "nsenter-exec" {
+		runtime.LockOSThread()
+
+		// User args are passed after '--' in the command line.
+		userArgs := findUserArgs()
+
+		config, err := loadConfigFromFd()
+		if err != nil {
+			log.Fatalf("docker-exec: unable to receive config from sync pipe: %s", err)
+		}
+
+		if err := namespaces.FinalizeSetns(config, userArgs); err != nil {
+			log.Fatalf("docker-exec: failed to exec: %s", err)
+		}
+		os.Exit(1)
 	}
 	}
+}
+
+func findUserArgs() []string {
+	for i, a := range os.Args {
+		if a == "--" {
+			return os.Args[i+1:]
+		}
+	}
+	return []string{}
+}
 
 
-	if err := namespaces.Init(container, rootfs, "", os.NewFile(3, "pipe"), os.Args[3:]); err != nil {
-		log.Fatalf("unable to initialize for container: %s", err)
+// loadConfigFromFd loads a container's config from the sync pipe that is provided by
+// fd 3 when running a process
+func loadConfigFromFd() (*libcontainer.Config, error) {
+	var config *libcontainer.Config
+	if err := json.NewDecoder(os.NewFile(3, "child")).Decode(&config); err != nil {
+		return nil, err
 	}
 	}
-	os.Exit(1)
+	return config, nil
 }
 }

+ 6 - 6
vendor/src/github.com/docker/libcontainer/integration/template_test.go

@@ -32,12 +32,12 @@ func newTemplateConfig(rootfs string) *libcontainer.Config {
 			"KILL",
 			"KILL",
 			"AUDIT_WRITE",
 			"AUDIT_WRITE",
 		},
 		},
-		Namespaces: map[string]bool{
-			"NEWNS":  true,
-			"NEWUTS": true,
-			"NEWIPC": true,
-			"NEWPID": true,
-			"NEWNET": true,
+		Namespaces: libcontainer.Namespaces{
+			{Type: libcontainer.NEWNS},
+			{Type: libcontainer.NEWUTS},
+			{Type: libcontainer.NEWIPC},
+			{Type: libcontainer.NEWPID},
+			{Type: libcontainer.NEWNET},
 		},
 		},
 		Cgroups: &cgroups.Cgroup{
 		Cgroups: &cgroups.Cgroup{
 			Parent:          "integration",
 			Parent:          "integration",

+ 0 - 29
vendor/src/github.com/docker/libcontainer/ipc/ipc.go

@@ -1,29 +0,0 @@
-package ipc
-
-import (
-	"fmt"
-	"os"
-	"syscall"
-
-	"github.com/docker/libcontainer/system"
-)
-
-// Join the IPC Namespace of specified ipc path if it exists.
-// If the path does not exist then you are not joining a container.
-func Initialize(nsPath string) error {
-	if nsPath == "" {
-		return nil
-	}
-	f, err := os.OpenFile(nsPath, os.O_RDONLY, 0)
-	if err != nil {
-		return fmt.Errorf("failed get IPC namespace fd: %v", err)
-	}
-
-	err = system.Setns(f.Fd(), syscall.CLONE_NEWIPC)
-	f.Close()
-
-	if err != nil {
-		return fmt.Errorf("failed to setns current IPC namespace: %v", err)
-	}
-	return nil
-}

+ 4 - 0
vendor/src/github.com/docker/libcontainer/namespaces/execin.go

@@ -97,6 +97,10 @@ func FinalizeSetns(container *libcontainer.Config, args []string) error {
 		return err
 		return err
 	}
 	}
 
 
+	if err := setupRlimits(container); err != nil {
+		return fmt.Errorf("setup rlimits %s", err)
+	}
+
 	if err := FinalizeNamespace(container); err != nil {
 	if err := FinalizeNamespace(container); err != nil {
 		return err
 		return err
 	}
 	}

+ 27 - 8
vendor/src/github.com/docker/libcontainer/namespaces/init.go

@@ -13,7 +13,6 @@ import (
 	"github.com/docker/libcontainer"
 	"github.com/docker/libcontainer"
 	"github.com/docker/libcontainer/apparmor"
 	"github.com/docker/libcontainer/apparmor"
 	"github.com/docker/libcontainer/console"
 	"github.com/docker/libcontainer/console"
-	"github.com/docker/libcontainer/ipc"
 	"github.com/docker/libcontainer/label"
 	"github.com/docker/libcontainer/label"
 	"github.com/docker/libcontainer/mount"
 	"github.com/docker/libcontainer/mount"
 	"github.com/docker/libcontainer/netlink"
 	"github.com/docker/libcontainer/netlink"
@@ -65,7 +64,10 @@ func Init(container *libcontainer.Config, uncleanRootfs, consolePath string, pip
 	if err := json.NewDecoder(pipe).Decode(&networkState); err != nil {
 	if err := json.NewDecoder(pipe).Decode(&networkState); err != nil {
 		return err
 		return err
 	}
 	}
-
+	// join any namespaces via a path to the namespace fd if provided
+	if err := joinExistingNamespaces(container.Namespaces); err != nil {
+		return err
+	}
 	if consolePath != "" {
 	if consolePath != "" {
 		if err := console.OpenAndDup(consolePath); err != nil {
 		if err := console.OpenAndDup(consolePath); err != nil {
 			return err
 			return err
@@ -79,9 +81,7 @@ func Init(container *libcontainer.Config, uncleanRootfs, consolePath string, pip
 			return fmt.Errorf("setctty %s", err)
 			return fmt.Errorf("setctty %s", err)
 		}
 		}
 	}
 	}
-	if err := ipc.Initialize(container.IpcNsPath); err != nil {
-		return fmt.Errorf("setup IPC %s", err)
-	}
+
 	if err := setupNetwork(container, networkState); err != nil {
 	if err := setupNetwork(container, networkState); err != nil {
 		return fmt.Errorf("setup networking %s", err)
 		return fmt.Errorf("setup networking %s", err)
 	}
 	}
@@ -178,17 +178,17 @@ func SetupUser(u string) error {
 		Home: "/",
 		Home: "/",
 	}
 	}
 
 
-	passwdFile, err := user.GetPasswdFile()
+	passwdPath, err := user.GetPasswdPath()
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	groupFile, err := user.GetGroupFile()
+	groupPath, err := user.GetGroupPath()
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	execUser, err := user.GetExecUserFile(u, &defaultExecUser, passwdFile, groupFile)
+	execUser, err := user.GetExecUserPath(u, &defaultExecUser, passwdPath, groupPath)
 	if err != nil {
 	if err != nil {
 		return fmt.Errorf("get supplementary groups %s", err)
 		return fmt.Errorf("get supplementary groups %s", err)
 	}
 	}
@@ -308,3 +308,22 @@ func LoadContainerEnvironment(container *libcontainer.Config) error {
 	}
 	}
 	return nil
 	return nil
 }
 }
+
+// joinExistingNamespaces gets all the namespace paths specified for the container and
+// does a setns on the namespace fd so that the current process joins the namespace.
+func joinExistingNamespaces(namespaces []libcontainer.Namespace) error {
+	for _, ns := range namespaces {
+		if ns.Path != "" {
+			f, err := os.OpenFile(ns.Path, os.O_RDONLY, 0)
+			if err != nil {
+				return err
+			}
+			err = system.Setns(f.Fd(), uintptr(namespaceInfo[ns.Type]))
+			f.Close()
+			if err != nil {
+				return err
+			}
+		}
+	}
+	return nil
+}

+ 11 - 6
vendor/src/github.com/docker/libcontainer/namespaces/nsenter/nsenter.c

@@ -15,6 +15,10 @@
 #include <unistd.h>
 #include <unistd.h>
 #include <getopt.h>
 #include <getopt.h>
 
 
+#ifndef PR_SET_CHILD_SUBREAPER
+#define PR_SET_CHILD_SUBREAPER 36
+#endif
+
 static const kBufSize = 256;
 static const kBufSize = 256;
 static const char *kNsEnter = "nsenter";
 static const char *kNsEnter = "nsenter";
 
 
@@ -32,8 +36,8 @@ void get_args(int *argc, char ***argv)
 		contents_size += kBufSize;
 		contents_size += kBufSize;
 		contents = (char *)realloc(contents, contents_size);
 		contents = (char *)realloc(contents, contents_size);
 		bytes_read =
 		bytes_read =
-			read(fd, contents + contents_offset,
-			     contents_size - contents_offset);
+		    read(fd, contents + contents_offset,
+			 contents_size - contents_offset);
 		contents_offset += bytes_read;
 		contents_offset += bytes_read;
 	}
 	}
 	while (bytes_read > 0);
 	while (bytes_read > 0);
@@ -90,16 +94,17 @@ void nsenter()
 	}
 	}
 
 
 	if (prctl(PR_SET_CHILD_SUBREAPER, 1, 0, 0, 0) == -1) {
 	if (prctl(PR_SET_CHILD_SUBREAPER, 1, 0, 0, 0) == -1) {
-                fprintf(stderr, "nsenter: failed to set child subreaper: %s", strerror(errno));
-                exit(1);
-        }
+		fprintf(stderr, "nsenter: failed to set child subreaper: %s",
+			strerror(errno));
+		exit(1);
+	}
 
 
 	static const struct option longopts[] = {
 	static const struct option longopts[] = {
 		{"nspid", required_argument, NULL, 'n'},
 		{"nspid", required_argument, NULL, 'n'},
 		{"console", required_argument, NULL, 't'},
 		{"console", required_argument, NULL, 't'},
 		{NULL, 0, NULL, 0}
 		{NULL, 0, NULL, 0}
 	};
 	};
-    
+
 	pid_t init_pid = -1;
 	pid_t init_pid = -1;
 	char *init_pid_str = NULL;
 	char *init_pid_str = NULL;
 	char *console = NULL;
 	char *console = NULL;

+ 0 - 50
vendor/src/github.com/docker/libcontainer/namespaces/types.go

@@ -1,50 +0,0 @@
-package namespaces
-
-import "errors"
-
-type (
-	Namespace struct {
-		Key   string `json:"key,omitempty"`
-		Value int    `json:"value,omitempty"`
-		File  string `json:"file,omitempty"`
-	}
-	Namespaces []*Namespace
-)
-
-// namespaceList is used to convert the libcontainer types
-// into the names of the files located in /proc/<pid>/ns/* for
-// each namespace
-var (
-	namespaceList      = Namespaces{}
-	ErrUnkownNamespace = errors.New("Unknown namespace")
-	ErrUnsupported     = errors.New("Unsupported method")
-)
-
-func (ns *Namespace) String() string {
-	return ns.Key
-}
-
-func GetNamespace(key string) *Namespace {
-	for _, ns := range namespaceList {
-		if ns.Key == key {
-			cpy := *ns
-			return &cpy
-		}
-	}
-	return nil
-}
-
-// Contains returns true if the specified Namespace is
-// in the slice
-func (n Namespaces) Contains(ns string) bool {
-	return n.Get(ns) != nil
-}
-
-func (n Namespaces) Get(ns string) *Namespace {
-	for _, nsp := range n {
-		if nsp != nil && nsp.Key == ns {
-			return nsp
-		}
-	}
-	return nil
-}

+ 0 - 16
vendor/src/github.com/docker/libcontainer/namespaces/types_linux.go

@@ -1,16 +0,0 @@
-package namespaces
-
-import (
-	"syscall"
-)
-
-func init() {
-	namespaceList = Namespaces{
-		{Key: "NEWNS", Value: syscall.CLONE_NEWNS, File: "mnt"},
-		{Key: "NEWUTS", Value: syscall.CLONE_NEWUTS, File: "uts"},
-		{Key: "NEWIPC", Value: syscall.CLONE_NEWIPC, File: "ipc"},
-		{Key: "NEWUSER", Value: syscall.CLONE_NEWUSER, File: "user"},
-		{Key: "NEWPID", Value: syscall.CLONE_NEWPID, File: "pid"},
-		{Key: "NEWNET", Value: syscall.CLONE_NEWNET, File: "net"},
-	}
-}

+ 0 - 30
vendor/src/github.com/docker/libcontainer/namespaces/types_test.go

@@ -1,30 +0,0 @@
-package namespaces
-
-import (
-	"testing"
-)
-
-func TestNamespacesContains(t *testing.T) {
-	ns := Namespaces{
-		GetNamespace("NEWPID"),
-		GetNamespace("NEWNS"),
-		GetNamespace("NEWUTS"),
-	}
-
-	if ns.Contains("NEWNET") {
-		t.Fatal("namespaces should not contain NEWNET")
-	}
-
-	if !ns.Contains("NEWPID") {
-		t.Fatal("namespaces should contain NEWPID but does not")
-	}
-
-	withNil := Namespaces{
-		GetNamespace("UNDEFINED"), // this element will be nil
-		GetNamespace("NEWPID"),
-	}
-
-	if !withNil.Contains("NEWPID") {
-		t.Fatal("namespaces should contain NEWPID but does not")
-	}
-}

+ 14 - 7
vendor/src/github.com/docker/libcontainer/namespaces/utils.go

@@ -5,6 +5,8 @@ package namespaces
 import (
 import (
 	"os"
 	"os"
 	"syscall"
 	"syscall"
+
+	"github.com/docker/libcontainer"
 )
 )
 
 
 type initError struct {
 type initError struct {
@@ -15,6 +17,15 @@ func (i initError) Error() string {
 	return i.Message
 	return i.Message
 }
 }
 
 
+var namespaceInfo = map[libcontainer.NamespaceType]int{
+	libcontainer.NEWNET:  syscall.CLONE_NEWNET,
+	libcontainer.NEWNS:   syscall.CLONE_NEWNS,
+	libcontainer.NEWUSER: syscall.CLONE_NEWUSER,
+	libcontainer.NEWIPC:  syscall.CLONE_NEWIPC,
+	libcontainer.NEWUTS:  syscall.CLONE_NEWUTS,
+	libcontainer.NEWPID:  syscall.CLONE_NEWPID,
+}
+
 // New returns a newly initialized Pipe for communication between processes
 // New returns a newly initialized Pipe for communication between processes
 func newInitPipe() (parent *os.File, child *os.File, err error) {
 func newInitPipe() (parent *os.File, child *os.File, err error) {
 	fds, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_STREAM|syscall.SOCK_CLOEXEC, 0)
 	fds, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_STREAM|syscall.SOCK_CLOEXEC, 0)
@@ -26,13 +37,9 @@ func newInitPipe() (parent *os.File, child *os.File, err error) {
 
 
 // GetNamespaceFlags parses the container's Namespaces options to set the correct
 // GetNamespaceFlags parses the container's Namespaces options to set the correct
 // flags on clone, unshare, and setns
 // flags on clone, unshare, and setns
-func GetNamespaceFlags(namespaces map[string]bool) (flag int) {
-	for key, enabled := range namespaces {
-		if enabled {
-			if ns := GetNamespace(key); ns != nil {
-				flag |= ns.Value
-			}
-		}
+func GetNamespaceFlags(namespaces libcontainer.Namespaces) (flag int) {
+	for _, v := range namespaces {
+		flag |= namespaceInfo[v.Type]
 	}
 	}
 	return flag
 	return flag
 }
 }

+ 2 - 3
vendor/src/github.com/docker/libcontainer/netlink/netlink_linux.go

@@ -522,11 +522,10 @@ func NetworkSetMacAddress(iface *net.Interface, macaddr string) error {
 
 
 	var (
 	var (
 		MULTICAST byte = 0x1
 		MULTICAST byte = 0x1
-		LOCALOUI  byte = 0x2
 	)
 	)
 
 
-	if hwaddr[0]&0x1 == MULTICAST || hwaddr[0]&0x2 != LOCALOUI {
-		return fmt.Errorf("Incorrect Local MAC Address specified: %s", macaddr)
+	if hwaddr[0]&0x1 == MULTICAST {
+		return fmt.Errorf("Multicast MAC Address is not supported: %s", macaddr)
 	}
 	}
 
 
 	wb := newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
 	wb := newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)

+ 0 - 39
vendor/src/github.com/docker/libcontainer/network/netns.go

@@ -1,39 +0,0 @@
-// +build linux
-
-package network
-
-import (
-	"fmt"
-	"os"
-	"syscall"
-
-	"github.com/docker/libcontainer/system"
-)
-
-//  crosbymichael: could make a network strategy that instead of returning veth pair names it returns a pid to an existing network namespace
-type NetNS struct {
-}
-
-func (v *NetNS) Create(n *Network, nspid int, networkState *NetworkState) error {
-	networkState.NsPath = n.NsPath
-	return nil
-}
-
-func (v *NetNS) Initialize(config *Network, networkState *NetworkState) error {
-	if networkState.NsPath == "" {
-		return fmt.Errorf("nspath does is not specified in NetworkState")
-	}
-
-	f, err := os.OpenFile(networkState.NsPath, os.O_RDONLY, 0)
-	if err != nil {
-		return fmt.Errorf("failed get network namespace fd: %v", err)
-	}
-
-	if err := system.Setns(f.Fd(), syscall.CLONE_NEWNET); err != nil {
-		f.Close()
-		return fmt.Errorf("failed to setns current network namespace: %v", err)
-	}
-
-	f.Close()
-	return nil
-}

+ 12 - 0
vendor/src/github.com/docker/libcontainer/network/network.go

@@ -88,6 +88,18 @@ func SetInterfaceIp(name string, rawIp string) error {
 	return netlink.NetworkLinkAddIp(iface, ip, ipNet)
 	return netlink.NetworkLinkAddIp(iface, ip, ipNet)
 }
 }
 
 
+func DeleteInterfaceIp(name string, rawIp string) error {
+	iface, err := net.InterfaceByName(name)
+	if err != nil {
+		return err
+	}
+	ip, ipNet, err := net.ParseCIDR(rawIp)
+	if err != nil {
+		return err
+	}
+	return netlink.NetworkLinkDelIp(iface, ip, ipNet)
+}
+
 func SetMtu(name string, mtu int) error {
 func SetMtu(name string, mtu int) error {
 	iface, err := net.InterfaceByName(name)
 	iface, err := net.InterfaceByName(name)
 	if err != nil {
 	if err != nil {

+ 0 - 1
vendor/src/github.com/docker/libcontainer/network/strategy.go

@@ -13,7 +13,6 @@ var (
 var strategies = map[string]NetworkStrategy{
 var strategies = map[string]NetworkStrategy{
 	"veth":     &Veth{},
 	"veth":     &Veth{},
 	"loopback": &Loopback{},
 	"loopback": &Loopback{},
-	"netns":    &NetNS{},
 }
 }
 
 
 // NetworkStrategy represents a specific network configuration for
 // NetworkStrategy represents a specific network configuration for

+ 0 - 5
vendor/src/github.com/docker/libcontainer/network/types.go

@@ -8,9 +8,6 @@ type Network struct {
 	// Type sets the networks type, commonly veth and loopback
 	// Type sets the networks type, commonly veth and loopback
 	Type string `json:"type,omitempty"`
 	Type string `json:"type,omitempty"`
 
 
-	// Path to network namespace
-	NsPath string `json:"ns_path,omitempty"`
-
 	// The bridge to use.
 	// The bridge to use.
 	Bridge string `json:"bridge,omitempty"`
 	Bridge string `json:"bridge,omitempty"`
 
 
@@ -50,6 +47,4 @@ type NetworkState struct {
 	VethHost string `json:"veth_host,omitempty"`
 	VethHost string `json:"veth_host,omitempty"`
 	// The name of the veth interface created inside the container for the child.
 	// The name of the veth interface created inside the container for the child.
 	VethChild string `json:"veth_child,omitempty"`
 	VethChild string `json:"veth_child,omitempty"`
-	// Net namespace path.
-	NsPath string `json:"ns_path,omitempty"`
 }
 }

+ 15 - 35
vendor/src/github.com/docker/libcontainer/cgroups/fs/notify_linux.go → vendor/src/github.com/docker/libcontainer/notify_linux.go

@@ -1,33 +1,29 @@
 // +build linux
 // +build linux
 
 
-package fs
+package libcontainer
 
 
 import (
 import (
 	"fmt"
 	"fmt"
+	"io/ioutil"
 	"os"
 	"os"
 	"path/filepath"
 	"path/filepath"
 	"syscall"
 	"syscall"
-
-	"github.com/docker/libcontainer/cgroups"
 )
 )
 
 
-// NotifyOnOOM sends signals on the returned channel when the cgroup reaches
-// its memory limit. The channel is closed when the cgroup is removed.
-func NotifyOnOOM(c *cgroups.Cgroup) (<-chan struct{}, error) {
-	d, err := getCgroupData(c, 0)
-	if err != nil {
-		return nil, err
-	}
-
-	return notifyOnOOM(d)
-}
+const oomCgroupName = "memory"
 
 
-func notifyOnOOM(d *data) (<-chan struct{}, error) {
-	dir, err := d.path("memory")
+// NotifyOnOOM returns channel on which you can expect event about OOM,
+// if process died without OOM this channel will be closed.
+// s is current *libcontainer.State for container.
+func NotifyOnOOM(s *State) (<-chan struct{}, error) {
+	dir := s.CgroupPaths[oomCgroupName]
+	if dir == "" {
+		return nil, fmt.Errorf("There is no path for %q in state", oomCgroupName)
+	}
+	oomControl, err := os.Open(filepath.Join(dir, "memory.oom_control"))
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
-
 	fd, _, syserr := syscall.RawSyscall(syscall.SYS_EVENTFD2, 0, syscall.FD_CLOEXEC, 0)
 	fd, _, syserr := syscall.RawSyscall(syscall.SYS_EVENTFD2, 0, syscall.FD_CLOEXEC, 0)
 	if syserr != 0 {
 	if syserr != 0 {
 		return nil, syserr
 		return nil, syserr
@@ -35,48 +31,32 @@ func notifyOnOOM(d *data) (<-chan struct{}, error) {
 
 
 	eventfd := os.NewFile(fd, "eventfd")
 	eventfd := os.NewFile(fd, "eventfd")
 
 
-	oomControl, err := os.Open(filepath.Join(dir, "memory.oom_control"))
-	if err != nil {
-		eventfd.Close()
-		return nil, err
-	}
-
-	var (
-		eventControlPath = filepath.Join(dir, "cgroup.event_control")
-		data             = fmt.Sprintf("%d %d", eventfd.Fd(), oomControl.Fd())
-	)
-
-	if err := writeFile(dir, "cgroup.event_control", data); err != nil {
+	eventControlPath := filepath.Join(dir, "cgroup.event_control")
+	data := fmt.Sprintf("%d %d", eventfd.Fd(), oomControl.Fd())
+	if err := ioutil.WriteFile(eventControlPath, []byte(data), 0700); err != nil {
 		eventfd.Close()
 		eventfd.Close()
 		oomControl.Close()
 		oomControl.Close()
 		return nil, err
 		return nil, err
 	}
 	}
-
 	ch := make(chan struct{})
 	ch := make(chan struct{})
-
 	go func() {
 	go func() {
 		defer func() {
 		defer func() {
 			close(ch)
 			close(ch)
 			eventfd.Close()
 			eventfd.Close()
 			oomControl.Close()
 			oomControl.Close()
 		}()
 		}()
-
 		buf := make([]byte, 8)
 		buf := make([]byte, 8)
-
 		for {
 		for {
 			if _, err := eventfd.Read(buf); err != nil {
 			if _, err := eventfd.Read(buf); err != nil {
 				return
 				return
 			}
 			}
-
 			// When a cgroup is destroyed, an event is sent to eventfd.
 			// When a cgroup is destroyed, an event is sent to eventfd.
 			// So if the control path is gone, return instead of notifying.
 			// So if the control path is gone, return instead of notifying.
 			if _, err := os.Lstat(eventControlPath); os.IsNotExist(err) {
 			if _, err := os.Lstat(eventControlPath); os.IsNotExist(err) {
 				return
 				return
 			}
 			}
-
 			ch <- struct{}{}
 			ch <- struct{}{}
 		}
 		}
 	}()
 	}()
-
 	return ch, nil
 	return ch, nil
 }
 }

+ 27 - 15
vendor/src/github.com/docker/libcontainer/cgroups/fs/notify_linux_test.go → vendor/src/github.com/docker/libcontainer/notify_linux_test.go

@@ -1,38 +1,48 @@
 // +build linux
 // +build linux
 
 
-package fs
+package libcontainer
 
 
 import (
 import (
 	"encoding/binary"
 	"encoding/binary"
 	"fmt"
 	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
 	"syscall"
 	"syscall"
 	"testing"
 	"testing"
 	"time"
 	"time"
 )
 )
 
 
 func TestNotifyOnOOM(t *testing.T) {
 func TestNotifyOnOOM(t *testing.T) {
-	helper := NewCgroupTestUtil("memory", t)
-	defer helper.cleanup()
-
-	helper.writeFileContents(map[string]string{
-		"memory.oom_control":   "",
-		"cgroup.event_control": "",
-	})
-
+	memoryPath, err := ioutil.TempDir("", "testnotifyoom-")
+	if err != nil {
+		t.Fatal(err)
+	}
+	oomPath := filepath.Join(memoryPath, "memory.oom_control")
+	eventPath := filepath.Join(memoryPath, "cgroup.event_control")
+	if err := ioutil.WriteFile(oomPath, []byte{}, 0700); err != nil {
+		t.Fatal(err)
+	}
+	if err := ioutil.WriteFile(eventPath, []byte{}, 0700); err != nil {
+		t.Fatal(err)
+	}
 	var eventFd, oomControlFd int
 	var eventFd, oomControlFd int
-
-	ooms, err := notifyOnOOM(helper.CgroupData)
+	st := &State{
+		CgroupPaths: map[string]string{
+			"memory": memoryPath,
+		},
+	}
+	ooms, err := NotifyOnOOM(st)
 	if err != nil {
 	if err != nil {
 		t.Fatal("expected no error, got:", err)
 		t.Fatal("expected no error, got:", err)
 	}
 	}
 
 
-	memoryPath, _ := helper.CgroupData.path("memory")
-	data, err := readFile(memoryPath, "cgroup.event_control")
+	data, err := ioutil.ReadFile(eventPath)
 	if err != nil {
 	if err != nil {
 		t.Fatal("couldn't read event control file:", err)
 		t.Fatal("couldn't read event control file:", err)
 	}
 	}
 
 
-	if _, err := fmt.Sscanf(data, "%d %d", &eventFd, &oomControlFd); err != nil {
+	if _, err := fmt.Sscanf(string(data), "%d %d", &eventFd, &oomControlFd); err != nil {
 		t.Fatalf("invalid control data %q: %s", data, err)
 		t.Fatalf("invalid control data %q: %s", data, err)
 	}
 	}
 
 
@@ -62,7 +72,9 @@ func TestNotifyOnOOM(t *testing.T) {
 
 
 	// simulate what happens when a cgroup is destroyed by cleaning up and then
 	// simulate what happens when a cgroup is destroyed by cleaning up and then
 	// writing to the eventfd.
 	// writing to the eventfd.
-	helper.cleanup()
+	if err := os.RemoveAll(memoryPath); err != nil {
+		t.Fatal(err)
+	}
 	if _, err := syscall.Write(efd, buf); err != nil {
 	if _, err := syscall.Write(efd, buf); err != nil {
 		t.Fatal("unable to write to eventfd:", err)
 		t.Fatal("unable to write to eventfd:", err)
 	}
 	}

+ 7 - 7
vendor/src/github.com/docker/libcontainer/sample_configs/apparmor.json

@@ -176,13 +176,13 @@
         "TERM=xterm"
         "TERM=xterm"
     ],
     ],
     "hostname": "koye",
     "hostname": "koye",
-    "namespaces": {
-        "NEWIPC": true,
-        "NEWNET": true,
-        "NEWNS": true,
-        "NEWPID": true,
-        "NEWUTS": true
-    },
+    "namespaces": [
+        {"type":"NEWIPC"},
+        {"type": "NEWNET"},
+        {"type": "NEWNS"},
+        {"type": "NEWPID"},
+        {"type": "NEWUTS"}
+    ],
     "networks": [
     "networks": [
         {
         {
             "address": "127.0.0.1/0",
             "address": "127.0.0.1/0",

+ 7 - 7
vendor/src/github.com/docker/libcontainer/sample_configs/attach_to_bridge.json

@@ -175,13 +175,13 @@
         "TERM=xterm"
         "TERM=xterm"
     ],
     ],
     "hostname": "koye",
     "hostname": "koye",
-    "namespaces": {
-        "NEWIPC": true,
-        "NEWNET": true,
-        "NEWNS": true,
-        "NEWPID": true,
-        "NEWUTS": true
-    },
+    "namespaces": [
+        {"type": "NEWIPC"},
+        {"type": "NEWNET"},
+        {"type": "NEWNS"},
+        {"type": "NEWPID"},
+        {"type": "NEWUTS"}
+    ],
     "networks": [
     "networks": [
         {
         {
             "address": "127.0.0.1/0",
             "address": "127.0.0.1/0",

+ 7 - 7
vendor/src/github.com/docker/libcontainer/sample_configs/minimal.json

@@ -181,13 +181,13 @@
         "TERM=xterm"
         "TERM=xterm"
     ],
     ],
     "hostname": "koye",
     "hostname": "koye",
-    "namespaces": {
-        "NEWIPC": true,
-        "NEWNET": true,
-        "NEWNS": true,
-        "NEWPID": true,
-        "NEWUTS": true
-    },
+    "namespaces": [
+        {"type": "NEWIPC"},
+        {"type": "NEWNET"},
+        {"type": "NEWNS"},
+        {"type": "NEWPID"},
+        {"type": "NEWUTS"}
+    ],
     "networks": [
     "networks": [
         {
         {
             "address": "127.0.0.1/0",
             "address": "127.0.0.1/0",

+ 7 - 7
vendor/src/github.com/docker/libcontainer/sample_configs/route_source_address_selection.json

@@ -175,13 +175,13 @@
         "TERM=xterm"
         "TERM=xterm"
     ],
     ],
     "hostname": "koye",
     "hostname": "koye",
-    "namespaces": {
-        "NEWIPC": true,
-        "NEWNET": true,
-        "NEWNS": true,
-        "NEWPID": true,
-        "NEWUTS": true
-    },
+    "namespaces": [
+        {"type": "NEWIPC"},
+        {"type": "NEWNET"},
+        {"type": "NEWNS"},
+        {"type": "NEWPID"},
+        {"type": "NEWUTS"}
+    ],
     "networks": [
     "networks": [
         {
         {
             "address": "127.0.0.1/0",
             "address": "127.0.0.1/0",

+ 7 - 7
vendor/src/github.com/docker/libcontainer/sample_configs/selinux.json

@@ -177,13 +177,13 @@
         "TERM=xterm"
         "TERM=xterm"
     ],
     ],
     "hostname": "koye",
     "hostname": "koye",
-    "namespaces": {
-        "NEWIPC": true,
-        "NEWNET": true,
-        "NEWNS": true,
-        "NEWPID": true,
-        "NEWUTS": true
-    },
+    "namespaces": [
+        {"type": "NEWIPC"},
+        {"type": "NEWNET"},
+        {"type": "NEWNS"},
+        {"type": "NEWPID"},
+        {"type": "NEWUTS"}
+    ],
     "networks": [
     "networks": [
         {
         {
             "address": "127.0.0.1/0",
             "address": "127.0.0.1/0",

+ 1 - 0
vendor/src/github.com/docker/libcontainer/user/MAINTAINERS

@@ -1 +1,2 @@
 Tianon Gravi <admwiggin@gmail.com> (@tianon)
 Tianon Gravi <admwiggin@gmail.com> (@tianon)
+Aleksa Sarai <cyphar@cyphar.com> (@cyphar)

+ 8 - 8
vendor/src/github.com/docker/libcontainer/user/lookup_unix.go

@@ -9,22 +9,22 @@ import (
 
 
 // Unix-specific path to the passwd and group formatted files.
 // Unix-specific path to the passwd and group formatted files.
 const (
 const (
-	unixPasswdFile = "/etc/passwd"
-	unixGroupFile  = "/etc/group"
+	unixPasswdPath = "/etc/passwd"
+	unixGroupPath  = "/etc/group"
 )
 )
 
 
-func GetPasswdFile() (string, error) {
-	return unixPasswdFile, nil
+func GetPasswdPath() (string, error) {
+	return unixPasswdPath, nil
 }
 }
 
 
 func GetPasswd() (io.ReadCloser, error) {
 func GetPasswd() (io.ReadCloser, error) {
-	return os.Open(unixPasswdFile)
+	return os.Open(unixPasswdPath)
 }
 }
 
 
-func GetGroupFile() (string, error) {
-	return unixGroupFile, nil
+func GetGroupPath() (string, error) {
+	return unixGroupPath, nil
 }
 }
 
 
 func GetGroup() (io.ReadCloser, error) {
 func GetGroup() (io.ReadCloser, error) {
-	return os.Open(unixGroupFile)
+	return os.Open(unixGroupPath)
 }
 }

+ 2 - 2
vendor/src/github.com/docker/libcontainer/user/lookup_unsupported.go

@@ -4,7 +4,7 @@ package user
 
 
 import "io"
 import "io"
 
 
-func GetPasswdFile() (string, error) {
+func GetPasswdPath() (string, error) {
 	return "", ErrUnsupported
 	return "", ErrUnsupported
 }
 }
 
 
@@ -12,7 +12,7 @@ func GetPasswd() (io.ReadCloser, error) {
 	return nil, ErrUnsupported
 	return nil, ErrUnsupported
 }
 }
 
 
-func GetGroupFile() (string, error) {
+func GetGroupPath() (string, error) {
 	return "", ErrUnsupported
 	return "", ErrUnsupported
 }
 }
 
 

+ 2 - 2
vendor/src/github.com/docker/libcontainer/user/user.go

@@ -197,11 +197,11 @@ type ExecUser struct {
 	Home     string
 	Home     string
 }
 }
 
 
-// GetExecUserFile is a wrapper for GetExecUser. It reads data from each of the
+// GetExecUserPath is a wrapper for GetExecUser. It reads data from each of the
 // given file paths and uses that data as the arguments to GetExecUser. If the
 // given file paths and uses that data as the arguments to GetExecUser. If the
 // files cannot be opened for any reason, the error is ignored and a nil
 // files cannot be opened for any reason, the error is ignored and a nil
 // io.Reader is passed instead.
 // io.Reader is passed instead.
-func GetExecUserFile(userSpec string, defaults *ExecUser, passwdPath, groupPath string) (*ExecUser, error) {
+func GetExecUserPath(userSpec string, defaults *ExecUser, passwdPath, groupPath string) (*ExecUser, error) {
 	passwd, err := os.Open(passwdPath)
 	passwd, err := os.Open(passwdPath)
 	if err != nil {
 	if err != nil {
 		passwd = nil
 		passwd = nil