Browse code

Initial commit of libcontainer Docker-DCO-1.1-Signed-off-by: Michael Crosby <michael@crosbymichael.com> (github: crosbymichael)

Michael Crosby authored on 2014/02/19 09:56:11
Showing 20 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,2 @@
0
+Michael Crosby <michael@crosbymichael.com> (@crosbymichael)
1
+Guillaume Charmes <guillaume@dotcloud.com> (@creack)
0 2
new file mode 100644
... ...
@@ -0,0 +1,63 @@
0
+## libcontainer - reference implementation for containers
1
+
2
+#### playground
3
+
4
+
5
+Use the cli package to test out functionality
6
+
7
+First setup a container configuration.  You will need a root fs, better go the path to a
8
+stopped docker container and use that.
9
+
10
+
11
+```json
12
+{
13
+    "id": "koye",
14
+    "namespace_pid": 12265,
15
+    "command": {
16
+        "args": [
17
+            "/bin/bash"
18
+        ],
19
+        "environment": [
20
+            "HOME=/",
21
+            "PATH=PATH=$PATH:/bin:/usr/bin:/sbin:/usr/sbin",
22
+            "container=docker",
23
+            "TERM=xterm"
24
+        ]
25
+    },
26
+    "rootfs": "/root/development/gocode/src/github.com/docker/libcontainer/namespaces/ubuntu",
27
+    "network": null,
28
+    "user": "",
29
+    "working_dir": "",
30
+    "namespaces": [
31
+        "NEWNET",
32
+        "NEWIPC",
33
+        "NEWNS",
34
+        "NEWPID",
35
+        "NEWUTS"
36
+    ],
37
+    "capabilities": [
38
+        "SETPCAP",
39
+        "SYS_MODULE",
40
+        "SYS_RAWIO",
41
+        "SYS_PACCT",
42
+        "SYS_ADMIN",
43
+        "SYS_NICE",
44
+        "SYS_RESOURCE",
45
+        "SYS_TIME",
46
+        "SYS_TTY_CONFIG",
47
+        "MKNOD",
48
+        "AUDIT_WRITE",
49
+        "AUDIT_CONTROL",
50
+        "MAC_OVERRIDE",
51
+        "MAC_ADMIN"
52
+    ]
53
+}
54
+```
55
+
56
+After you have a json file and a rootfs path to use just run:
57
+`./cli exec container.json`
58
+
59
+
60
+If you want to attach to an existing namespace just use the same json
61
+file with the container still running and do:
62
+`./cli execin container.json`
0 63
new file mode 100644
... ...
@@ -0,0 +1,49 @@
0
+package capabilities
1
+
2
+import (
3
+	"github.com/dotcloud/docker/pkg/libcontainer"
4
+	"github.com/syndtr/gocapability/capability"
5
+	"os"
6
+)
7
+
8
+var capMap = map[libcontainer.Capability]capability.Cap{
9
+	libcontainer.CAP_SETPCAP:        capability.CAP_SETPCAP,
10
+	libcontainer.CAP_SYS_MODULE:     capability.CAP_SYS_MODULE,
11
+	libcontainer.CAP_SYS_RAWIO:      capability.CAP_SYS_RAWIO,
12
+	libcontainer.CAP_SYS_PACCT:      capability.CAP_SYS_PACCT,
13
+	libcontainer.CAP_SYS_ADMIN:      capability.CAP_SYS_ADMIN,
14
+	libcontainer.CAP_SYS_NICE:       capability.CAP_SYS_NICE,
15
+	libcontainer.CAP_SYS_RESOURCE:   capability.CAP_SYS_RESOURCE,
16
+	libcontainer.CAP_SYS_TIME:       capability.CAP_SYS_TIME,
17
+	libcontainer.CAP_SYS_TTY_CONFIG: capability.CAP_SYS_TTY_CONFIG,
18
+	libcontainer.CAP_MKNOD:          capability.CAP_MKNOD,
19
+	libcontainer.CAP_AUDIT_WRITE:    capability.CAP_AUDIT_WRITE,
20
+	libcontainer.CAP_AUDIT_CONTROL:  capability.CAP_AUDIT_CONTROL,
21
+	libcontainer.CAP_MAC_OVERRIDE:   capability.CAP_MAC_OVERRIDE,
22
+	libcontainer.CAP_MAC_ADMIN:      capability.CAP_MAC_ADMIN,
23
+}
24
+
25
+// DropCapabilities drops capabilities for the current process based
26
+// on the container's configuration.
27
+func DropCapabilities(container *libcontainer.Container) error {
28
+	if drop := getCapabilities(container); len(drop) > 0 {
29
+		c, err := capability.NewPid(os.Getpid())
30
+		if err != nil {
31
+			return err
32
+		}
33
+		c.Unset(capability.CAPS|capability.BOUNDS, drop...)
34
+
35
+		if err := c.Apply(capability.CAPS | capability.BOUNDS); err != nil {
36
+			return err
37
+		}
38
+	}
39
+	return nil
40
+}
41
+
42
+func getCapabilities(container *libcontainer.Container) []capability.Cap {
43
+	drop := []capability.Cap{}
44
+	for _, c := range container.Capabilities {
45
+		drop = append(drop, capMap[c])
46
+	}
47
+	return drop
48
+}
0 49
new file mode 100644
... ...
@@ -0,0 +1,171 @@
0
+package main
1
+
2
+import (
3
+	"encoding/json"
4
+	"flag"
5
+	"fmt"
6
+	"github.com/dotcloud/docker/pkg/libcontainer"
7
+	"github.com/dotcloud/docker/pkg/libcontainer/namespaces"
8
+	"github.com/dotcloud/docker/pkg/libcontainer/network"
9
+	"github.com/dotcloud/docker/pkg/libcontainer/utils"
10
+	"os"
11
+)
12
+
13
+var (
14
+	displayPid bool
15
+	newCommand string
16
+	usrNet     bool
17
+)
18
+
19
+func init() {
20
+	flag.BoolVar(&displayPid, "pid", false, "display the pid before waiting")
21
+	flag.StringVar(&newCommand, "cmd", "/bin/bash", "command to run in the existing namespace")
22
+	flag.BoolVar(&usrNet, "net", false, "user a net namespace")
23
+	flag.Parse()
24
+}
25
+
26
+func exec(container *libcontainer.Container) error {
27
+	var (
28
+		netFile *os.File
29
+		err     error
30
+	)
31
+	container.NetNsFd = 0
32
+
33
+	if usrNet {
34
+		netFile, err = os.Open("/root/nsroot/test")
35
+		if err != nil {
36
+			return err
37
+		}
38
+		container.NetNsFd = netFile.Fd()
39
+	}
40
+
41
+	pid, err := namespaces.Exec(container)
42
+	if err != nil {
43
+		return fmt.Errorf("error exec container %s", err)
44
+	}
45
+
46
+	if displayPid {
47
+		fmt.Println(pid)
48
+	}
49
+
50
+	exitcode, err := utils.WaitOnPid(pid)
51
+	if err != nil {
52
+		return fmt.Errorf("error waiting on child %s", err)
53
+	}
54
+	fmt.Println(exitcode)
55
+	if usrNet {
56
+		netFile.Close()
57
+		if err := network.DeleteNetworkNamespace("/root/nsroot/test"); err != nil {
58
+			return err
59
+		}
60
+	}
61
+	os.Exit(exitcode)
62
+	return nil
63
+}
64
+
65
+func execIn(container *libcontainer.Container) error {
66
+	f, err := os.Open("/root/nsroot/test")
67
+	if err != nil {
68
+		return err
69
+	}
70
+	container.NetNsFd = f.Fd()
71
+	pid, err := namespaces.ExecIn(container, &libcontainer.Command{
72
+		Env: container.Command.Env,
73
+		Args: []string{
74
+			newCommand,
75
+		},
76
+	})
77
+	if err != nil {
78
+		return fmt.Errorf("error exexin container %s", err)
79
+	}
80
+	exitcode, err := utils.WaitOnPid(pid)
81
+	if err != nil {
82
+		return fmt.Errorf("error waiting on child %s", err)
83
+	}
84
+	os.Exit(exitcode)
85
+	return nil
86
+}
87
+
88
+func createNet(config *libcontainer.Network) error {
89
+	root := "/root/nsroot"
90
+	if err := network.SetupNamespaceMountDir(root); err != nil {
91
+		return err
92
+	}
93
+
94
+	nspath := root + "/test"
95
+	if err := network.CreateNetworkNamespace(nspath); err != nil {
96
+		return nil
97
+	}
98
+	if err := network.CreateVethPair("veth0", config.TempVethName); err != nil {
99
+		return err
100
+	}
101
+	if err := network.SetInterfaceMaster("veth0", config.Bridge); err != nil {
102
+		return err
103
+	}
104
+	if err := network.InterfaceUp("veth0"); err != nil {
105
+		return err
106
+	}
107
+
108
+	f, err := os.Open(nspath)
109
+	if err != nil {
110
+		return err
111
+	}
112
+	defer f.Close()
113
+
114
+	if err := network.SetInterfaceInNamespaceFd("veth1", int(f.Fd())); err != nil {
115
+		return err
116
+	}
117
+
118
+	/*
119
+		if err := network.SetupVethInsideNamespace(f.Fd(), config); err != nil {
120
+			return err
121
+		}
122
+	*/
123
+	return nil
124
+}
125
+
126
+func printErr(err error) {
127
+	fmt.Fprintln(os.Stderr, err)
128
+	os.Exit(1)
129
+}
130
+
131
+func main() {
132
+	var (
133
+		err    error
134
+		cliCmd = flag.Arg(0)
135
+		config = flag.Arg(1)
136
+	)
137
+	f, err := os.Open(config)
138
+	if err != nil {
139
+		printErr(err)
140
+	}
141
+
142
+	dec := json.NewDecoder(f)
143
+	var container *libcontainer.Container
144
+
145
+	if err := dec.Decode(&container); err != nil {
146
+		printErr(err)
147
+	}
148
+	f.Close()
149
+
150
+	switch cliCmd {
151
+	case "exec":
152
+		err = exec(container)
153
+	case "execin":
154
+		err = execIn(container)
155
+	case "net":
156
+		err = createNet(&libcontainer.Network{
157
+			TempVethName: "veth1",
158
+			IP:           "172.17.0.100/16",
159
+			Gateway:      "172.17.42.1",
160
+			Mtu:          1500,
161
+			Bridge:       "docker0",
162
+		})
163
+	default:
164
+		err = fmt.Errorf("command not supported: %s", cliCmd)
165
+	}
166
+
167
+	if err != nil {
168
+		printErr(err)
169
+	}
170
+}
0 171
new file mode 100644
... ...
@@ -0,0 +1,27 @@
0
+package libcontainer
1
+
2
+type Container struct {
3
+	ID           string       `json:"id,omitempty"`
4
+	NsPid        int          `json:"namespace_pid,omitempty"`
5
+	Command      *Command     `json:"command,omitempty"`
6
+	RootFs       string       `json:"rootfs,omitempty"`
7
+	ReadonlyFs   bool         `json:"readonly_fs,omitempty"`
8
+	NetNsFd      uintptr      `json:"network_namespace_fd,omitempty"`
9
+	User         string       `json:"user,omitempty"`
10
+	WorkingDir   string       `json:"working_dir,omitempty"`
11
+	Namespaces   Namespaces   `json:"namespaces,omitempty"`
12
+	Capabilities Capabilities `json:"capabilities,omitempty"`
13
+}
14
+
15
+type Command struct {
16
+	Args []string `json:"args,omitempty"`
17
+	Env  []string `json:"environment,omitempty"`
18
+}
19
+
20
+type Network struct {
21
+	TempVethName string `json:"temp_veth,omitempty"`
22
+	IP           string `json:"ip,omitempty"`
23
+	Gateway      string `json:"gateway,omitempty"`
24
+	Bridge       string `json:"bridge,omitempty"`
25
+	Mtu          int    `json:"mtu,omitempty"`
26
+}
0 27
new file mode 100644
... ...
@@ -0,0 +1,38 @@
0
+{
1
+    "id": "koye",
2
+    "namespace_pid": 3117,
3
+    "command": {
4
+        "args": [
5
+            "/bin/bash"
6
+        ],
7
+        "environment": [
8
+            "HOME=/",
9
+            "PATH=PATH=$PATH:/bin:/usr/bin:/sbin:/usr/sbin",
10
+            "container=docker",
11
+            "TERM=xterm"
12
+        ]
13
+    },
14
+    "rootfs": "/root/main/mycontainer",
15
+    "namespaces": [
16
+        "NEWIPC",
17
+        "NEWNS",
18
+        "NEWPID",
19
+        "NEWUTS"
20
+    ],
21
+    "capabilities": [
22
+        "SETPCAP",
23
+        "SYS_MODULE",
24
+        "SYS_RAWIO",
25
+        "SYS_PACCT",
26
+        "SYS_ADMIN",
27
+        "SYS_NICE",
28
+        "SYS_RESOURCE",
29
+        "SYS_TIME",
30
+        "SYS_TTY_CONFIG",
31
+        "MKNOD",
32
+        "AUDIT_WRITE",
33
+        "AUDIT_CONTROL",
34
+        "MAC_OVERRIDE",
35
+        "MAC_ADMIN"
36
+    ]
37
+}
0 38
new file mode 100644
... ...
@@ -0,0 +1,9 @@
0
+package libcontainer
1
+
2
+import (
3
+	"errors"
4
+)
5
+
6
+var (
7
+	ErrInvalidPid = errors.New("no ns pid found")
8
+)
0 9
new file mode 100644
... ...
@@ -0,0 +1,164 @@
0
+package namespaces
1
+
2
+import (
3
+	"fmt"
4
+	"os"
5
+	"syscall"
6
+	"unsafe"
7
+)
8
+
9
+const (
10
+	TIOCGPTN   = 0x80045430
11
+	TIOCSPTLCK = 0x40045431
12
+)
13
+
14
+func chroot(dir string) error {
15
+	return syscall.Chroot(dir)
16
+}
17
+
18
+func chdir(dir string) error {
19
+	return syscall.Chdir(dir)
20
+}
21
+
22
+func exec(cmd string, args []string, env []string) error {
23
+	return syscall.Exec(cmd, args, env)
24
+}
25
+
26
+func fork() (int, error) {
27
+	syscall.ForkLock.Lock()
28
+	pid, _, err := syscall.Syscall(syscall.SYS_FORK, 0, 0, 0)
29
+	syscall.ForkLock.Unlock()
30
+	if err != 0 {
31
+		return -1, err
32
+	}
33
+	return int(pid), nil
34
+}
35
+
36
+func vfork() (int, error) {
37
+	syscall.ForkLock.Lock()
38
+	pid, _, err := syscall.Syscall(syscall.SYS_VFORK, 0, 0, 0)
39
+	syscall.ForkLock.Unlock()
40
+	if err != 0 {
41
+		return -1, err
42
+	}
43
+	return int(pid), nil
44
+}
45
+
46
+func mount(source, target, fstype string, flags uintptr, data string) error {
47
+	return syscall.Mount(source, target, fstype, flags, data)
48
+}
49
+
50
+func unmount(target string, flags int) error {
51
+	return syscall.Unmount(target, flags)
52
+}
53
+
54
+func pivotroot(newroot, putold string) error {
55
+	return syscall.PivotRoot(newroot, putold)
56
+}
57
+
58
+func unshare(flags int) error {
59
+	return syscall.Unshare(flags)
60
+}
61
+
62
+func clone(flags uintptr) (int, error) {
63
+	syscall.ForkLock.Lock()
64
+	pid, _, err := syscall.RawSyscall(syscall.SYS_CLONE, flags, 0, 0)
65
+	syscall.ForkLock.Unlock()
66
+	if err != 0 {
67
+		return -1, err
68
+	}
69
+	return int(pid), nil
70
+}
71
+
72
+func setns(fd uintptr, flags uintptr) error {
73
+	_, _, err := syscall.RawSyscall(SYS_SETNS, fd, flags, 0)
74
+	if err != 0 {
75
+		return err
76
+	}
77
+	return nil
78
+}
79
+
80
+func usetCloseOnExec(fd uintptr) error {
81
+	if _, _, err := syscall.Syscall(syscall.SYS_FCNTL, fd, syscall.F_SETFD, 0); err != 0 {
82
+		return err
83
+	}
84
+	return nil
85
+}
86
+
87
+func setgroups(gids []int) error {
88
+	return syscall.Setgroups(gids)
89
+}
90
+
91
+func setresgid(rgid, egid, sgid int) error {
92
+	return syscall.Setresgid(rgid, egid, sgid)
93
+}
94
+
95
+func setresuid(ruid, euid, suid int) error {
96
+	return syscall.Setresuid(ruid, euid, suid)
97
+}
98
+
99
+func sethostname(name string) error {
100
+	return syscall.Sethostname([]byte(name))
101
+}
102
+
103
+func setsid() (int, error) {
104
+	return syscall.Setsid()
105
+}
106
+
107
+func ioctl(fd uintptr, flag, data uintptr) error {
108
+	if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, flag, data); err != 0 {
109
+		return err
110
+	}
111
+	return nil
112
+}
113
+
114
+func openpmtx() (*os.File, error) {
115
+	return os.OpenFile("/dev/ptmx", syscall.O_RDONLY|syscall.O_NOCTTY|syscall.O_CLOEXEC, 0)
116
+}
117
+
118
+func unlockpt(f *os.File) error {
119
+	var u int
120
+	return ioctl(f.Fd(), TIOCSPTLCK, uintptr(unsafe.Pointer(&u)))
121
+}
122
+
123
+func ptsname(f *os.File) (string, error) {
124
+	var n int
125
+	if err := ioctl(f.Fd(), TIOCGPTN, uintptr(unsafe.Pointer(&n))); err != nil {
126
+		return "", err
127
+	}
128
+	return fmt.Sprintf("/dev/pts/%d", n), nil
129
+}
130
+
131
+func closefd(fd uintptr) error {
132
+	return syscall.Close(int(fd))
133
+}
134
+
135
+func dup2(fd1, fd2 uintptr) error {
136
+	return syscall.Dup2(int(fd1), int(fd2))
137
+}
138
+
139
+func mknod(path string, mode uint32, dev int) error {
140
+	return syscall.Mknod(path, mode, dev)
141
+}
142
+
143
+func parentDeathSignal() error {
144
+	if _, _, err := syscall.RawSyscall6(syscall.SYS_PRCTL, syscall.PR_SET_PDEATHSIG, uintptr(syscall.SIGKILL), 0, 0, 0, 0); err != 0 {
145
+		return err
146
+	}
147
+	return nil
148
+}
149
+
150
+func setctty() error {
151
+	if _, _, err := syscall.RawSyscall(syscall.SYS_IOCTL, 0, uintptr(syscall.TIOCSCTTY), 0); err != 0 {
152
+		return err
153
+	}
154
+	return nil
155
+}
156
+
157
+func mkfifo(name string, mode uint32) error {
158
+	return syscall.Mkfifo(name, mode)
159
+}
160
+
161
+func umask(mask int) int {
162
+	return syscall.Umask(mask)
163
+}
0 164
new file mode 100644
... ...
@@ -0,0 +1,266 @@
0
+/*
1
+   Higher level convience functions for setting up a container
2
+*/
3
+
4
+package namespaces
5
+
6
+import (
7
+	"errors"
8
+	"fmt"
9
+	"github.com/dotcloud/docker/pkg/libcontainer"
10
+	"github.com/dotcloud/docker/pkg/libcontainer/capabilities"
11
+	"github.com/dotcloud/docker/pkg/libcontainer/utils"
12
+	"io"
13
+	"log"
14
+	"os"
15
+	"path/filepath"
16
+	"syscall"
17
+)
18
+
19
+var (
20
+	ErrExistingNetworkNamespace = errors.New("specified both CLONE_NEWNET and an existing network namespace")
21
+)
22
+
23
+// Exec will spawn new namespaces with the specified Container configuration
24
+// in the RootFs path and return the pid of the new containerized process.
25
+//
26
+// If an existing network namespace is specified the container
27
+// will join that namespace.  If an existing network namespace is not specified but CLONE_NEWNET is,
28
+// the container will be spawned with a new network namespace with no configuration.  Omiting an
29
+// existing network namespace and the CLONE_NEWNET option in the container configuration will allow
30
+// the container to the the host's networking options and configuration.
31
+func Exec(container *libcontainer.Container) (pid int, err error) {
32
+	// a user cannot pass CLONE_NEWNET and an existing net namespace fd to join
33
+	if container.NetNsFd > 0 && container.Namespaces.Contains(libcontainer.CLONE_NEWNET) {
34
+		return -1, ErrExistingNetworkNamespace
35
+	}
36
+
37
+	rootfs, err := resolveRootfs(container)
38
+	if err != nil {
39
+		return -1, err
40
+	}
41
+
42
+	master, console, err := createMasterAndConsole()
43
+	if err != nil {
44
+		return -1, err
45
+	}
46
+
47
+	logger, err := os.OpenFile("/root/logs", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0755)
48
+	if err != nil {
49
+		return -1, err
50
+	}
51
+	log.SetOutput(logger)
52
+
53
+	// we need CLONE_VFORK so we can wait on the child
54
+	flag := getNamespaceFlags(container.Namespaces) | CLONE_VFORK
55
+
56
+	if pid, err = clone(uintptr(flag | SIGCHLD)); err != nil {
57
+		return -1, fmt.Errorf("error cloning process: %s", err)
58
+	}
59
+
60
+	if pid == 0 {
61
+		// welcome to your new namespace ;)
62
+		//
63
+		// any errors encoutered inside the namespace we should write
64
+		// out to a log or a pipe to our parent and exit(1)
65
+		// because writing to stderr will not work after we close
66
+		if err := closeMasterAndStd(master); err != nil {
67
+			writeError("close master and std %s", err)
68
+		}
69
+		slave, err := openTerminal(console, syscall.O_RDWR)
70
+		if err != nil {
71
+			writeError("open terminal %s", err)
72
+		}
73
+		if err := dupSlave(slave); err != nil {
74
+			writeError("dup2 slave %s", err)
75
+		}
76
+
77
+		if container.NetNsFd > 0 {
78
+			if err := JoinExistingNamespace(container.NetNsFd, libcontainer.CLONE_NEWNET); err != nil {
79
+				writeError("join existing net namespace %s", err)
80
+			}
81
+		}
82
+
83
+		if _, err := setsid(); err != nil {
84
+			writeError("setsid %s", err)
85
+		}
86
+		if err := setctty(); err != nil {
87
+			writeError("setctty %s", err)
88
+		}
89
+		if err := parentDeathSignal(); err != nil {
90
+			writeError("parent deth signal %s", err)
91
+		}
92
+		if err := SetupNewMountNamespace(rootfs, console, container.ReadonlyFs); err != nil {
93
+			writeError("setup mount namespace %s", err)
94
+		}
95
+		if err := sethostname(container.ID); err != nil {
96
+			writeError("sethostname %s", err)
97
+		}
98
+		if err := capabilities.DropCapabilities(container); err != nil {
99
+			writeError("drop capabilities %s", err)
100
+		}
101
+		if err := setupUser(container); err != nil {
102
+			writeError("setup user %s", err)
103
+		}
104
+		if container.WorkingDir != "" {
105
+			if err := chdir(container.WorkingDir); err != nil {
106
+				writeError("chdir to %s %s", container.WorkingDir, err)
107
+			}
108
+		}
109
+		if err := exec(container.Command.Args[0], container.Command.Args[0:], container.Command.Env); err != nil {
110
+			writeError("exec %s", err)
111
+		}
112
+		panic("unreachable")
113
+	}
114
+
115
+	go func() {
116
+		if _, err := io.Copy(os.Stdout, master); err != nil {
117
+			log.Println(err)
118
+		}
119
+	}()
120
+	go func() {
121
+		if _, err := io.Copy(master, os.Stdin); err != nil {
122
+			log.Println(err)
123
+		}
124
+	}()
125
+	return pid, nil
126
+}
127
+
128
+// ExecIn will spawn a new command inside an existing container's namespaces.  The existing container's
129
+// pid and namespace configuration is needed along with the specific capabilities that should
130
+// be dropped once inside the namespace.
131
+func ExecIn(container *libcontainer.Container, cmd *libcontainer.Command) (int, error) {
132
+	if container.NsPid <= 0 {
133
+		return -1, libcontainer.ErrInvalidPid
134
+	}
135
+
136
+	fds, err := getNsFds(container)
137
+	if err != nil {
138
+		return -1, err
139
+	}
140
+
141
+	if container.NetNsFd > 0 {
142
+		fds = append(fds, container.NetNsFd)
143
+	}
144
+
145
+	pid, err := fork()
146
+	if err != nil {
147
+		for _, fd := range fds {
148
+			syscall.Close(int(fd))
149
+		}
150
+		return -1, err
151
+	}
152
+
153
+	if pid == 0 {
154
+		for _, fd := range fds {
155
+			if fd > 0 {
156
+				if err := JoinExistingNamespace(fd, ""); err != nil {
157
+					for _, fd := range fds {
158
+						syscall.Close(int(fd))
159
+					}
160
+					writeError("join existing namespace for %d %s", fd, err)
161
+				}
162
+			}
163
+			syscall.Close(int(fd))
164
+		}
165
+
166
+		if container.Namespaces.Contains(libcontainer.CLONE_NEWNS) &&
167
+			container.Namespaces.Contains(libcontainer.CLONE_NEWPID) {
168
+			// important:
169
+			//
170
+			// we need to fork and unshare so that re can remount proc and sys within
171
+			// the namespace so the CLONE_NEWPID namespace will take effect
172
+			// if we don't fork we would end up unmounting proc and sys for the entire
173
+			// namespace
174
+			child, err := fork()
175
+			if err != nil {
176
+				writeError("fork child %s", err)
177
+			}
178
+
179
+			if child == 0 {
180
+				if err := unshare(CLONE_NEWNS); err != nil {
181
+					writeError("unshare newns %s", err)
182
+				}
183
+				if err := remountProc(); err != nil {
184
+					writeError("remount proc %s", err)
185
+				}
186
+				if err := remountSys(); err != nil {
187
+					writeError("remount sys %s", err)
188
+				}
189
+				if err := capabilities.DropCapabilities(container); err != nil {
190
+					writeError("drop caps %s", err)
191
+				}
192
+				if err := exec(cmd.Args[0], cmd.Args[0:], cmd.Env); err != nil {
193
+					writeError("exec %s", err)
194
+				}
195
+				panic("unreachable")
196
+			}
197
+			exit, err := utils.WaitOnPid(child)
198
+			if err != nil {
199
+				writeError("wait on child %s", err)
200
+			}
201
+			os.Exit(exit)
202
+		}
203
+		if err := exec(cmd.Args[0], cmd.Args[0:], cmd.Env); err != nil {
204
+			writeError("exec %s", err)
205
+		}
206
+		panic("unreachable")
207
+	}
208
+	return pid, err
209
+}
210
+
211
+func resolveRootfs(container *libcontainer.Container) (string, error) {
212
+	rootfs, err := filepath.Abs(container.RootFs)
213
+	if err != nil {
214
+		return "", err
215
+	}
216
+	return filepath.EvalSymlinks(rootfs)
217
+}
218
+
219
+func createMasterAndConsole() (*os.File, string, error) {
220
+	master, err := os.OpenFile("/dev/ptmx", syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_CLOEXEC, 0)
221
+	if err != nil {
222
+		return nil, "", err
223
+	}
224
+
225
+	console, err := ptsname(master)
226
+	if err != nil {
227
+		return nil, "", err
228
+	}
229
+
230
+	if err := unlockpt(master); err != nil {
231
+		return nil, "", err
232
+	}
233
+	return master, console, nil
234
+}
235
+
236
+func closeMasterAndStd(master *os.File) error {
237
+	closefd(master.Fd())
238
+	closefd(0)
239
+	closefd(1)
240
+	closefd(2)
241
+
242
+	return nil
243
+}
244
+
245
+func dupSlave(slave *os.File) error {
246
+	// we close Stdin,etc so our pty slave should have fd 0
247
+	if slave.Fd() != 0 {
248
+		return fmt.Errorf("slave fd not 0 %d", slave.Fd())
249
+	}
250
+	if err := dup2(slave.Fd(), 1); err != nil {
251
+		return err
252
+	}
253
+	if err := dup2(slave.Fd(), 2); err != nil {
254
+		return err
255
+	}
256
+	return nil
257
+}
258
+
259
+func openTerminal(name string, flag int) (*os.File, error) {
260
+	r, e := syscall.Open(name, flag, 0)
261
+	if e != nil {
262
+		return nil, &os.PathError{"open", name, e}
263
+	}
264
+	return os.NewFile(uintptr(r), name), nil
265
+}
0 266
new file mode 100644
... ...
@@ -0,0 +1,7 @@
0
+// +build linux,x86_64
1
+package namespaces
2
+
3
+// Via http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=7b21fddd087678a70ad64afc0f632e0f1071b092
4
+const (
5
+	SYS_SETNS = 308
6
+)
0 7
new file mode 100644
... ...
@@ -0,0 +1,207 @@
0
+package namespaces
1
+
2
+import (
3
+	"fmt"
4
+	"log"
5
+	"os"
6
+	"path/filepath"
7
+	"syscall"
8
+)
9
+
10
+var (
11
+	// default mount point options
12
+	defaults = syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NODEV
13
+)
14
+
15
+func SetupNewMountNamespace(rootfs, console string, readonly bool) error {
16
+	if err := mount("", "/", "", syscall.MS_SLAVE|syscall.MS_REC, ""); err != nil {
17
+		return fmt.Errorf("mounting / as slave %s", err)
18
+	}
19
+
20
+	if err := mount(rootfs, rootfs, "bind", syscall.MS_BIND|syscall.MS_REC, ""); err != nil {
21
+		return fmt.Errorf("mouting %s as bind %s", rootfs, err)
22
+	}
23
+
24
+	if readonly {
25
+		if err := mount(rootfs, rootfs, "bind", syscall.MS_BIND|syscall.MS_REMOUNT|syscall.MS_RDONLY|syscall.MS_REC, ""); err != nil {
26
+			return fmt.Errorf("mounting %s as readonly %s", rootfs, err)
27
+		}
28
+	}
29
+
30
+	if err := mountSystem(rootfs); err != nil {
31
+		return fmt.Errorf("mount system %s", err)
32
+	}
33
+
34
+	if err := copyDevNodes(rootfs); err != nil {
35
+		return fmt.Errorf("copy dev nodes %s", err)
36
+	}
37
+
38
+	ptmx := filepath.Join(rootfs, "dev/ptmx")
39
+	if err := os.Remove(ptmx); err != nil && !os.IsNotExist(err) {
40
+		return err
41
+	}
42
+	if err := os.Symlink(filepath.Join(rootfs, "pts/ptmx"), ptmx); err != nil {
43
+		return fmt.Errorf("symlink dev ptmx %s", err)
44
+	}
45
+
46
+	if err := setupDev(rootfs); err != nil {
47
+		return err
48
+	}
49
+
50
+	if err := setupConsole(rootfs, console); err != nil {
51
+		return err
52
+	}
53
+
54
+	if err := chdir(rootfs); err != nil {
55
+		return fmt.Errorf("chdir into %s %s", rootfs, err)
56
+	}
57
+
58
+	if err := mount(rootfs, "/", "", syscall.MS_MOVE, ""); err != nil {
59
+		return fmt.Errorf("mount move %s into / %s", rootfs, err)
60
+	}
61
+
62
+	if err := chroot("."); err != nil {
63
+		return fmt.Errorf("chroot . %s", err)
64
+	}
65
+
66
+	if err := chdir("/"); err != nil {
67
+		return fmt.Errorf("chdir / %s", err)
68
+	}
69
+
70
+	umask(0022)
71
+
72
+	return nil
73
+}
74
+
75
+func copyDevNodes(rootfs string) error {
76
+	umask(0000)
77
+
78
+	for _, node := range []string{
79
+		"null",
80
+		"zero",
81
+		"full",
82
+		"random",
83
+		"urandom",
84
+		"tty",
85
+	} {
86
+		stat, err := os.Stat(filepath.Join("/dev", node))
87
+		if err != nil {
88
+			return err
89
+		}
90
+
91
+		var (
92
+			dest = filepath.Join(rootfs, "dev", node)
93
+			st   = stat.Sys().(*syscall.Stat_t)
94
+		)
95
+
96
+		log.Printf("copy %s to %s %d\n", node, dest, st.Rdev)
97
+		if err := mknod(dest, st.Mode, int(st.Rdev)); err != nil && !os.IsExist(err) {
98
+			return fmt.Errorf("copy %s %s", node, err)
99
+		}
100
+	}
101
+	return nil
102
+}
103
+
104
+func setupDev(rootfs string) error {
105
+	for _, link := range []struct {
106
+		from string
107
+		to   string
108
+	}{
109
+		{"/proc/kcore", "/dev/core"},
110
+		{"/proc/self/fd", "/dev/fd"},
111
+		{"/proc/self/fd/0", "/dev/stdin"},
112
+		{"/proc/self/fd/1", "/dev/stdout"},
113
+		{"/proc/self/fd/2", "/dev/stderr"},
114
+	} {
115
+		dest := filepath.Join(rootfs, link.to)
116
+		if err := os.Remove(dest); err != nil && !os.IsNotExist(err) {
117
+			return fmt.Errorf("remove %s %s", dest, err)
118
+		}
119
+		if err := os.Symlink(link.from, dest); err != nil {
120
+			return fmt.Errorf("symlink %s %s", dest, err)
121
+		}
122
+	}
123
+	return nil
124
+}
125
+
126
+func setupConsole(rootfs, console string) error {
127
+	umask(0000)
128
+
129
+	stat, err := os.Stat(console)
130
+	if err != nil {
131
+		return fmt.Errorf("stat console %s %s", console, err)
132
+	}
133
+	st := stat.Sys().(*syscall.Stat_t)
134
+
135
+	dest := filepath.Join(rootfs, "dev/console")
136
+	if err := os.Remove(dest); err != nil && !os.IsNotExist(err) {
137
+		return fmt.Errorf("remove %s %s", dest, err)
138
+	}
139
+
140
+	if err := os.Chmod(console, 0600); err != nil {
141
+		return err
142
+	}
143
+	if err := os.Chown(console, 0, 0); err != nil {
144
+		return err
145
+	}
146
+
147
+	if err := mknod(dest, (st.Mode&^07777)|0600, int(st.Rdev)); err != nil {
148
+		return fmt.Errorf("mknod %s %s", dest, err)
149
+	}
150
+
151
+	if err := mount(console, dest, "bind", syscall.MS_BIND, ""); err != nil {
152
+		return fmt.Errorf("bind %s to %s %s", console, dest, err)
153
+	}
154
+	return nil
155
+}
156
+
157
+// mountSystem sets up linux specific system mounts like sys, proc, shm, and devpts
158
+// inside the mount namespace
159
+func mountSystem(rootfs string) error {
160
+	mounts := []struct {
161
+		source string
162
+		path   string
163
+		device string
164
+		flags  int
165
+		data   string
166
+	}{
167
+		{source: "proc", path: filepath.Join(rootfs, "proc"), device: "proc", flags: defaults},
168
+		{source: "sysfs", path: filepath.Join(rootfs, "sys"), device: "sysfs", flags: defaults},
169
+		{source: "tmpfs", path: filepath.Join(rootfs, "dev"), device: "tmpfs", flags: syscall.MS_NOSUID | syscall.MS_STRICTATIME, data: "mode=755"},
170
+		{source: "shm", path: filepath.Join(rootfs, "dev", "shm"), device: "tmpfs", flags: defaults, data: "mode=1777"},
171
+		{source: "devpts", path: filepath.Join(rootfs, "dev", "pts"), device: "devpts", flags: syscall.MS_NOSUID | syscall.MS_NOEXEC, data: "newinstance,ptmxmode=0666,mode=620,gid=5"},
172
+		{source: "tmpfs", path: filepath.Join(rootfs, "run"), device: "tmpfs", flags: syscall.MS_NOSUID | syscall.MS_NODEV | syscall.MS_STRICTATIME, data: "mode=755"},
173
+	}
174
+	for _, m := range mounts {
175
+		if err := os.MkdirAll(m.path, 0755); err != nil && !os.IsExist(err) {
176
+			return fmt.Errorf("mkdirall %s %s", m.path, err)
177
+		}
178
+		if err := mount(m.source, m.path, m.device, uintptr(m.flags), m.data); err != nil {
179
+			return fmt.Errorf("mounting %s into %s %s", m.source, m.path, err)
180
+		}
181
+	}
182
+	return nil
183
+}
184
+
185
+func remountProc() error {
186
+	if err := unmount("/proc", syscall.MNT_DETACH); err != nil {
187
+		return err
188
+	}
189
+	if err := mount("proc", "/proc", "proc", uintptr(defaults), ""); err != nil {
190
+		return err
191
+	}
192
+	return nil
193
+}
194
+
195
+func remountSys() error {
196
+	if err := unmount("/sys", syscall.MNT_DETACH); err != nil {
197
+		if err != syscall.EINVAL {
198
+			return err
199
+		}
200
+	} else {
201
+		if err := mount("sysfs", "/sys", "sysfs", uintptr(defaults), ""); err != nil {
202
+			return err
203
+		}
204
+	}
205
+	return nil
206
+}
0 207
new file mode 100644
... ...
@@ -0,0 +1,70 @@
0
+/*
1
+   TODO
2
+   pivot root
3
+   cgroups
4
+   more mount stuff that I probably am forgetting
5
+   apparmor
6
+*/
7
+
8
+package namespaces
9
+
10
+import (
11
+	"fmt"
12
+	"github.com/dotcloud/docker/pkg/libcontainer"
13
+	"github.com/dotcloud/docker/pkg/libcontainer/utils"
14
+	"os"
15
+	"path/filepath"
16
+	"syscall"
17
+)
18
+
19
+// CreateNewNamespace creates a new namespace and binds it's fd to the specified path
20
+func CreateNewNamespace(namespace libcontainer.Namespace, bindTo string) error {
21
+	var (
22
+		flag   = namespaceMap[namespace]
23
+		name   = namespaceFileMap[namespace]
24
+		nspath = filepath.Join("/proc/self/ns", name)
25
+	)
26
+	// TODO: perform validation on name and flag
27
+
28
+	pid, err := fork()
29
+	if err != nil {
30
+		return err
31
+	}
32
+
33
+	if pid == 0 {
34
+		if err := unshare(flag); err != nil {
35
+			writeError("unshare %s", err)
36
+		}
37
+		if err := mount(nspath, bindTo, "none", syscall.MS_BIND, ""); err != nil {
38
+			writeError("bind mount %s", err)
39
+		}
40
+		os.Exit(0)
41
+	}
42
+	exit, err := utils.WaitOnPid(pid)
43
+	if err != nil {
44
+		return err
45
+	}
46
+	if exit != 0 {
47
+		return fmt.Errorf("exit status %d", exit)
48
+	}
49
+	return err
50
+}
51
+
52
+// JoinExistingNamespace uses the fd of an existing linux namespace and
53
+// has the current process join that namespace or the spacespace specified by ns
54
+func JoinExistingNamespace(fd uintptr, ns libcontainer.Namespace) error {
55
+	flag := namespaceMap[ns]
56
+	if err := setns(fd, uintptr(flag)); err != nil {
57
+		return err
58
+	}
59
+	return nil
60
+}
61
+
62
+// getNamespaceFlags parses the container's Namespaces options to set the correct
63
+// flags on clone, unshare, and setns
64
+func getNamespaceFlags(namespaces libcontainer.Namespaces) (flag int) {
65
+	for _, ns := range namespaces {
66
+		flag |= namespaceMap[ns]
67
+	}
68
+	return
69
+}
0 70
new file mode 100644
... ...
@@ -0,0 +1,35 @@
0
+package namespaces
1
+
2
+import (
3
+	"github.com/dotcloud/docker/pkg/libcontainer"
4
+)
5
+
6
+const (
7
+	SIGCHLD       = 0x14
8
+	CLONE_VFORK   = 0x00004000
9
+	CLONE_NEWNS   = 0x00020000
10
+	CLONE_NEWUTS  = 0x04000000
11
+	CLONE_NEWIPC  = 0x08000000
12
+	CLONE_NEWUSER = 0x10000000
13
+	CLONE_NEWPID  = 0x20000000
14
+	CLONE_NEWNET  = 0x40000000
15
+)
16
+
17
+var namespaceMap = map[libcontainer.Namespace]int{
18
+	"": 0,
19
+	libcontainer.CLONE_NEWNS:   CLONE_NEWNS,
20
+	libcontainer.CLONE_NEWUTS:  CLONE_NEWUTS,
21
+	libcontainer.CLONE_NEWIPC:  CLONE_NEWIPC,
22
+	libcontainer.CLONE_NEWUSER: CLONE_NEWUSER,
23
+	libcontainer.CLONE_NEWPID:  CLONE_NEWPID,
24
+	libcontainer.CLONE_NEWNET:  CLONE_NEWNET,
25
+}
26
+
27
+var namespaceFileMap = map[libcontainer.Namespace]string{
28
+	libcontainer.CLONE_NEWNS:   "mnt",
29
+	libcontainer.CLONE_NEWUTS:  "uts",
30
+	libcontainer.CLONE_NEWIPC:  "ipc",
31
+	libcontainer.CLONE_NEWUSER: "user",
32
+	libcontainer.CLONE_NEWPID:  "pid",
33
+	libcontainer.CLONE_NEWNET:  "net",
34
+}
0 35
new file mode 100644
... ...
@@ -0,0 +1,108 @@
0
+package namespaces
1
+
2
+import (
3
+	"fmt"
4
+	"github.com/dotcloud/docker/pkg/libcontainer"
5
+	"os"
6
+	"path/filepath"
7
+	"strconv"
8
+	"strings"
9
+	"syscall"
10
+)
11
+
12
+func addEnvIfNotSet(container *libcontainer.Container, key, value string) {
13
+	jv := fmt.Sprintf("%s=%s", key, value)
14
+	if len(container.Command.Env) == 0 {
15
+		container.Command.Env = []string{jv}
16
+		return
17
+	}
18
+
19
+	for _, v := range container.Command.Env {
20
+		parts := strings.Split(v, "=")
21
+		if parts[0] == key {
22
+			return
23
+		}
24
+	}
25
+	container.Command.Env = append(container.Command.Env, jv)
26
+}
27
+
28
+// print and error to stderr and exit(1)
29
+func writeError(format string, v ...interface{}) {
30
+	fmt.Fprintf(os.Stderr, format, v...)
31
+	os.Exit(1)
32
+}
33
+
34
+// getNsFds inspects the container's namespace configuration and opens the fds to
35
+// each of the namespaces.
36
+func getNsFds(container *libcontainer.Container) ([]uintptr, error) {
37
+	var (
38
+		namespaces = []string{}
39
+		fds        = []uintptr{}
40
+	)
41
+
42
+	for _, ns := range container.Namespaces {
43
+		namespaces = append(namespaces, namespaceFileMap[ns])
44
+	}
45
+
46
+	for _, ns := range namespaces {
47
+		fd, err := getNsFd(container.NsPid, ns)
48
+		if err != nil {
49
+			for _, fd = range fds {
50
+				syscall.Close(int(fd))
51
+			}
52
+			return nil, err
53
+		}
54
+		fds = append(fds, fd)
55
+	}
56
+	return fds, nil
57
+}
58
+
59
+// getNsFd returns the fd for a specific pid and namespace option
60
+func getNsFd(pid int, ns string) (uintptr, error) {
61
+	nspath := filepath.Join("/proc", strconv.Itoa(pid), "ns", ns)
62
+	// OpenFile adds closOnExec
63
+	f, err := os.OpenFile(nspath, os.O_RDONLY, 0666)
64
+	if err != nil {
65
+		return 0, err
66
+	}
67
+	return f.Fd(), nil
68
+}
69
+
70
+// setupEnvironment adds additional environment variables to the container's
71
+// Command such as USER, LOGNAME, container, and TERM
72
+func setupEnvironment(container *libcontainer.Container) {
73
+	addEnvIfNotSet(container, "container", "docker")
74
+	// TODO: check if pty
75
+	addEnvIfNotSet(container, "TERM", "xterm")
76
+	// TODO: get username from container
77
+	addEnvIfNotSet(container, "USER", "root")
78
+	addEnvIfNotSet(container, "LOGNAME", "root")
79
+}
80
+
81
+func setupUser(container *libcontainer.Container) error {
82
+	// TODO: honor user passed on container
83
+	if err := setgroups(nil); err != nil {
84
+		return err
85
+	}
86
+	if err := setresgid(0, 0, 0); err != nil {
87
+		return err
88
+	}
89
+	if err := setresuid(0, 0, 0); err != nil {
90
+		return err
91
+	}
92
+	return nil
93
+}
94
+
95
+func getMasterAndConsole(container *libcontainer.Container) (string, *os.File, error) {
96
+	master, err := openpmtx()
97
+	if err != nil {
98
+		return "", nil, err
99
+	}
100
+
101
+	console, err := ptsname(master)
102
+	if err != nil {
103
+		master.Close()
104
+		return "", nil, err
105
+	}
106
+	return console, master, nil
107
+}
0 108
new file mode 100644
... ...
@@ -0,0 +1,104 @@
0
+package network
1
+
2
+import (
3
+	"errors"
4
+	"github.com/dotcloud/docker/pkg/netlink"
5
+	"net"
6
+)
7
+
8
+var (
9
+	ErrNoDefaultRoute = errors.New("no default network route found")
10
+)
11
+
12
+func InterfaceUp(name string) error {
13
+	iface, err := net.InterfaceByName(name)
14
+	if err != nil {
15
+		return err
16
+	}
17
+	return netlink.NetworkLinkUp(iface)
18
+}
19
+
20
+func InterfaceDown(name string) error {
21
+	iface, err := net.InterfaceByName(name)
22
+	if err != nil {
23
+		return err
24
+	}
25
+	return netlink.NetworkLinkDown(iface)
26
+}
27
+
28
+func ChangeInterfaceName(old, newName string) error {
29
+	iface, err := net.InterfaceByName(old)
30
+	if err != nil {
31
+		return err
32
+	}
33
+	return netlink.NetworkChangeName(iface, newName)
34
+}
35
+
36
+func CreateVethPair(name1, name2 string) error {
37
+	return netlink.NetworkCreateVethPair(name1, name2)
38
+}
39
+
40
+func SetInterfaceInNamespacePid(name string, nsPid int) error {
41
+	iface, err := net.InterfaceByName(name)
42
+	if err != nil {
43
+		return err
44
+	}
45
+	return netlink.NetworkSetNsPid(iface, nsPid)
46
+}
47
+
48
+func SetInterfaceInNamespaceFd(name string, fd int) error {
49
+	iface, err := net.InterfaceByName(name)
50
+	if err != nil {
51
+		return err
52
+	}
53
+	return netlink.NetworkSetNsFd(iface, fd)
54
+}
55
+
56
+func SetInterfaceMaster(name, master string) error {
57
+	iface, err := net.InterfaceByName(name)
58
+	if err != nil {
59
+		return err
60
+	}
61
+	masterIface, err := net.InterfaceByName(master)
62
+	if err != nil {
63
+		return err
64
+	}
65
+	return netlink.NetworkSetMaster(iface, masterIface)
66
+}
67
+
68
+func SetDefaultGateway(ip string) error {
69
+	return netlink.AddDefaultGw(net.ParseIP(ip))
70
+}
71
+
72
+func SetInterfaceIp(name string, rawIp string) error {
73
+	iface, err := net.InterfaceByName(name)
74
+	if err != nil {
75
+		return err
76
+	}
77
+	ip, ipNet, err := net.ParseCIDR(rawIp)
78
+	if err != nil {
79
+		return err
80
+	}
81
+	return netlink.NetworkLinkAddIp(iface, ip, ipNet)
82
+}
83
+
84
+func SetMtu(name string, mtu int) error {
85
+	iface, err := net.InterfaceByName(name)
86
+	if err != nil {
87
+		return err
88
+	}
89
+	return netlink.NetworkSetMTU(iface, mtu)
90
+}
91
+
92
+func GetDefaultMtu() (int, error) {
93
+	routes, err := netlink.NetworkGetRoutes()
94
+	if err != nil {
95
+		return -1, err
96
+	}
97
+	for _, r := range routes {
98
+		if r.Default {
99
+			return r.Iface.MTU, nil
100
+		}
101
+	}
102
+	return -1, ErrNoDefaultRoute
103
+}
0 104
new file mode 100644
... ...
@@ -0,0 +1,85 @@
0
+package network
1
+
2
+import (
3
+	"fmt"
4
+	"github.com/dotcloud/docker/pkg/libcontainer"
5
+	"github.com/dotcloud/docker/pkg/libcontainer/namespaces"
6
+	"os"
7
+	"syscall"
8
+)
9
+
10
+// SetupVeth sets up an existing network namespace with the specified
11
+// network configuration.
12
+func SetupVeth(config *libcontainer.Network) error {
13
+	if err := InterfaceDown(config.TempVethName); err != nil {
14
+		return fmt.Errorf("interface down %s %s", config.TempVethName, err)
15
+	}
16
+	if err := ChangeInterfaceName(config.TempVethName, "eth0"); err != nil {
17
+		return fmt.Errorf("change %s to eth0 %s", config.TempVethName, err)
18
+	}
19
+	if err := SetInterfaceIp("eth0", config.IP); err != nil {
20
+		return fmt.Errorf("set eth0 ip %s", err)
21
+	}
22
+
23
+	if err := SetMtu("eth0", config.Mtu); err != nil {
24
+		return fmt.Errorf("set eth0 mtu to %d %s", config.Mtu, err)
25
+	}
26
+	if err := InterfaceUp("eth0"); err != nil {
27
+		return fmt.Errorf("eth0 up %s", err)
28
+	}
29
+
30
+	if err := SetMtu("lo", config.Mtu); err != nil {
31
+		return fmt.Errorf("set lo mtu to %d %s", config.Mtu, err)
32
+	}
33
+	if err := InterfaceUp("lo"); err != nil {
34
+		return fmt.Errorf("lo up %s", err)
35
+	}
36
+
37
+	if config.Gateway != "" {
38
+		if err := SetDefaultGateway(config.Gateway); err != nil {
39
+			return fmt.Errorf("set gateway to %s %s", config.Gateway, err)
40
+		}
41
+	}
42
+	return nil
43
+}
44
+
45
+// SetupNamespaceMountDir prepares a new root for use as a mount
46
+// source for bind mounting namespace fd to an outside path
47
+func SetupNamespaceMountDir(root string) error {
48
+	if err := os.MkdirAll(root, 0666); err != nil {
49
+		return err
50
+	}
51
+	// make sure mounts are not unmounted by other mnt namespaces
52
+	if err := syscall.Mount("", root, "none", syscall.MS_SHARED|syscall.MS_REC, ""); err != nil && err != syscall.EINVAL {
53
+		return err
54
+	}
55
+	if err := syscall.Mount(root, root, "none", syscall.MS_BIND, ""); err != nil {
56
+		return err
57
+	}
58
+	return nil
59
+}
60
+
61
+// CreateNetworkNamespace creates a new network namespace and binds it's fd
62
+// at the binding path
63
+func CreateNetworkNamespace(bindingPath string) error {
64
+	f, err := os.OpenFile(bindingPath, os.O_RDONLY|os.O_CREATE|os.O_EXCL, 0)
65
+	if err != nil {
66
+		return err
67
+	}
68
+	f.Close()
69
+
70
+	if err := namespaces.CreateNewNamespace(libcontainer.CLONE_NEWNET, bindingPath); err != nil {
71
+		return err
72
+	}
73
+	return nil
74
+}
75
+
76
+// DeleteNetworkNamespace unmounts the binding path and removes the
77
+// file so that no references to the fd are present and the network
78
+// namespace is automatically cleaned up
79
+func DeleteNetworkNamespace(bindingPath string) error {
80
+	if err := syscall.Unmount(bindingPath, 0); err != nil {
81
+		return err
82
+	}
83
+	return os.Remove(bindingPath)
84
+}
0 85
new file mode 100644
... ...
@@ -0,0 +1,22 @@
0
+{
1
+    "id": "koye",
2
+    "namespace_pid": 3745,
3
+    "command": {
4
+        "args": [
5
+            "/usr/lib/systemd/systemd"
6
+        ],
7
+        "environment": [
8
+            "HOME=/",
9
+            "PATH=PATH=$PATH:/bin:/usr/bin:/sbin:/usr/sbin",
10
+            "container=docker",
11
+            "TERM="
12
+        ]
13
+    },
14
+    "rootfs": "/root/main/mycontainer",
15
+    "namespaces": [
16
+        "NEWIPC",
17
+        "NEWNS",
18
+        "NEWPID",
19
+        "NEWUTS"
20
+    ]
21
+}
0 22
new file mode 100644
... ...
@@ -0,0 +1,49 @@
0
+package libcontainer
1
+
2
+type Namespace string
3
+type Namespaces []Namespace
4
+
5
+func (n Namespaces) Contains(ns Namespace) bool {
6
+	for _, nns := range n {
7
+		if nns == ns {
8
+			return true
9
+		}
10
+	}
11
+	return false
12
+}
13
+
14
+type Capability string
15
+type Capabilities []Capability
16
+
17
+func (c Capabilities) Contains(capp Capability) bool {
18
+	for _, cc := range c {
19
+		if cc == capp {
20
+			return true
21
+		}
22
+	}
23
+	return false
24
+}
25
+
26
+const (
27
+	CAP_SETPCAP        Capability = "SETPCAP"
28
+	CAP_SYS_MODULE     Capability = "SYS_MODULE"
29
+	CAP_SYS_RAWIO      Capability = "SYS_RAWIO"
30
+	CAP_SYS_PACCT      Capability = "SYS_PACCT"
31
+	CAP_SYS_ADMIN      Capability = "SYS_ADMIN"
32
+	CAP_SYS_NICE       Capability = "SYS_NICE"
33
+	CAP_SYS_RESOURCE   Capability = "SYS_RESOURCE"
34
+	CAP_SYS_TIME       Capability = "SYS_TIME"
35
+	CAP_SYS_TTY_CONFIG Capability = "SYS_TTY_CONFIG"
36
+	CAP_MKNOD          Capability = "MKNOD"
37
+	CAP_AUDIT_WRITE    Capability = "AUDIT_WRITE"
38
+	CAP_AUDIT_CONTROL  Capability = "AUDIT_CONTROL"
39
+	CAP_MAC_OVERRIDE   Capability = "MAC_OVERRIDE"
40
+	CAP_MAC_ADMIN      Capability = "MAC_ADMIN"
41
+
42
+	CLONE_NEWNS   Namespace = "NEWNS"   // mount
43
+	CLONE_NEWUTS  Namespace = "NEWUTS"  // utsname
44
+	CLONE_NEWIPC  Namespace = "NEWIPC"  // ipc
45
+	CLONE_NEWUSER Namespace = "NEWUSER" // user
46
+	CLONE_NEWPID  Namespace = "NEWPID"  // pid
47
+	CLONE_NEWNET  Namespace = "NEWNET"  // network
48
+)
0 49
new file mode 100644
... ...
@@ -0,0 +1,22 @@
0
+{
1
+    "id": "koye",
2
+    "namespace_pid": 3745,
3
+    "command": {
4
+        "args": [
5
+            "/sbin/init"
6
+        ],
7
+        "environment": [
8
+            "HOME=/",
9
+            "PATH=PATH=$PATH:/bin:/usr/bin:/sbin:/usr/sbin",
10
+            "container=docker",
11
+            "TERM=xterm"
12
+        ]
13
+    },
14
+    "rootfs": "/var/lib/docker/btrfs/subvolumes/7c0f15df1ad2e2fe04d7a6e079aec17406e9465a6a37dd16cb0dd754fc0167b3",
15
+    "namespaces": [
16
+        "NEWIPC",
17
+        "NEWNS",
18
+        "NEWPID",
19
+        "NEWUTS"
20
+    ]
21
+}
0 22
new file mode 100644
... ...
@@ -0,0 +1,33 @@
0
+package utils
1
+
2
+import (
3
+	"crypto/rand"
4
+	"encoding/hex"
5
+	"io"
6
+	"os"
7
+	"syscall"
8
+)
9
+
10
+func WaitOnPid(pid int) (exitcode int, err error) {
11
+	child, err := os.FindProcess(pid)
12
+	if err != nil {
13
+		return -1, err
14
+	}
15
+	state, err := child.Wait()
16
+	if err != nil {
17
+		return -1, err
18
+	}
19
+	return getExitCode(state), nil
20
+}
21
+
22
+func getExitCode(state *os.ProcessState) int {
23
+	return state.Sys().(syscall.WaitStatus).ExitStatus()
24
+}
25
+
26
+func GenerateRandomName(size int) (string, error) {
27
+	id := make([]byte, size)
28
+	if _, err := io.ReadFull(rand.Reader, id); err != nil {
29
+		return "", err
30
+	}
31
+	return hex.EncodeToString(id), nil
32
+}