Browse code

Merge pull request #7428 from crosbymichael/update-libcontainer-aug2

Update libcontainer to 5589d4d879f1d7e31967a927d3e

Tianon Gravi authored on 2014/08/07 06:41:35
Showing 16 changed files
... ...
@@ -45,8 +45,6 @@ clone git github.com/gorilla/context 14f550f51a
45 45
 
46 46
 clone git github.com/gorilla/mux 136d54f81f
47 47
 
48
-clone git github.com/syndtr/gocapability 3c85049eae
49
-
50 48
 clone git github.com/tchap/go-patricia v1.0.1
51 49
 
52 50
 clone hg code.google.com/p/go.net 84a4013f96e0
... ...
@@ -61,6 +59,8 @@ rm -rf src/code.google.com/p/go
61 61
 mkdir -p src/code.google.com/p/go/src/pkg/archive
62 62
 mv tmp-tar src/code.google.com/p/go/src/pkg/archive/tar
63 63
 
64
-clone git github.com/godbus/dbus v1
65
-clone git github.com/coreos/go-systemd v2
66
-clone git github.com/docker/libcontainer 68ea1234a0b046803aacb2562df0da12eec2b2f9
64
+clone git github.com/docker/libcontainer 5589d4d879f1d7e31967a927d3e8b98144fbe06b
65
+# see src/github.com/docker/libcontainer/update-vendor.sh which is the "source of truth" for libcontainer deps (just like this file)
66
+rm -rf src/github.com/docker/libcontainer/vendor
67
+eval "$(grep '^clone ' src/github.com/docker/libcontainer/update-vendor.sh | grep -v 'github.com/codegangsta/cli')"
68
+# we exclude "github.com/codegangsta/cli" here because it's only needed for "nsinit", which Docker doesn't include
... ...
@@ -13,13 +13,15 @@ env:
13 13
     - _GOOS=linux _GOARCH=arm CGO_ENABLED=0
14 14
 
15 15
 install:
16
+    - go get code.google.com/p/go.tools/cmd/cover
16 17
     - mkdir -pv "${GOPATH%%:*}/src/github.com/docker" && [ -d "${GOPATH%%:*}/src/github.com/docker/libcontainer" ] || ln -sv "$(readlink -f .)" "${GOPATH%%:*}/src/github.com/docker/libcontainer"
17 18
     - if [ -z "$TRAVIS_GLOBAL_WTF" ]; then
18 19
           gvm cross "$_GOOS" "$_GOARCH";
19 20
           export GOOS="$_GOOS" GOARCH="$_GOARCH";
20 21
       fi
22
+    - export GOPATH="$GOPATH:$(pwd)/vendor"
21 23
     - if [ -z "$TRAVIS_GLOBAL_WTF" ]; then go env; fi
22
-    - go get -d -v ./...
24
+    - go get -d -v ./... # TODO remove this if /docker/docker gets purged from our includes
23 25
     - if [ "$TRAVIS_GLOBAL_WTF" ]; then
24 26
           export DOCKER_PATH="${GOPATH%%:*}/src/github.com/docker/docker";
25 27
           mkdir -p "$DOCKER_PATH/hack/make";
... ...
@@ -30,5 +32,5 @@ install:
30 30
 script:
31 31
     - if [ "$TRAVIS_GLOBAL_WTF" ]; then bash "$DOCKER_PATH/hack/make/validate-dco"; fi
32 32
     - if [ "$TRAVIS_GLOBAL_WTF" ]; then bash "$DOCKER_PATH/hack/make/validate-gofmt"; fi
33
-    - if [ -z "$TRAVIS_GLOBAL_WTF" ]; then go build -v ./...; fi
34
-    - if [ -z "$TRAVIS_GLOBAL_WTF" -a "$GOARCH" != 'arm' ]; then go test -test.short -v ./...; fi
33
+    - if [ -z "$TRAVIS_GLOBAL_WTF" ]; then make direct-build; fi
34
+    - if [ -z "$TRAVIS_GLOBAL_WTF" -a "$GOARCH" != 'arm' ]; then make direct-test-short; fi
... ...
@@ -1,6 +1,6 @@
1 1
 FROM crosbymichael/golang
2 2
 
3
-RUN apt-get update && apt-get install -y gcc
3
+RUN apt-get update && apt-get install -y gcc make
4 4
 RUN go get code.google.com/p/go.tools/cmd/cover
5 5
 
6 6
 # setup a playground for us to spawn containers in
... ...
@@ -14,8 +14,10 @@ COPY . /go/src/github.com/docker/libcontainer
14 14
 WORKDIR /go/src/github.com/docker/libcontainer
15 15
 RUN cp sample_configs/minimal.json /busybox/container.json
16 16
 
17
+ENV GOPATH $GOPATH:/go/src/github.com/docker/libcontainer/vendor
18
+
17 19
 RUN go get -d -v ./...
18
-RUN go install -v ./...
20
+RUN make direct-install
19 21
 
20 22
 ENTRYPOINT ["/dind"]
21
-CMD ["go", "test", "-cover", "./..."]
23
+CMD ["make", "direct-test"]
... ...
@@ -2,3 +2,4 @@ Michael Crosby <michael@docker.com> (@crosbymichael)
2 2
 Rohit Jnagal <jnagal@google.com> (@rjnagal)
3 3
 Victor Marmol <vmarmol@google.com> (@vmarmol)
4 4
 .travis.yml: Tianon Gravi <admwiggin@gmail.com> (@tianon)
5
+update-vendor.sh: Tianon Gravi <admwiggin@gmail.com> (@tianon)
... ...
@@ -4,7 +4,21 @@ all:
4 4
 
5 5
 test:
6 6
 	# we need NET_ADMIN for the netlink tests and SYS_ADMIN for mounting
7
-	docker run --rm --cap-add NET_ADMIN --cap-add SYS_ADMIN docker/libcontainer
7
+	docker run --rm -it --cap-add NET_ADMIN --cap-add SYS_ADMIN docker/libcontainer
8 8
 
9 9
 sh:
10
-	docker run --rm -ti -w /busybox --rm --cap-add NET_ADMIN --cap-add SYS_ADMIN docker/libcontainer nsinit exec sh
10
+	docker run --rm -it --cap-add NET_ADMIN --cap-add SYS_ADMIN -w /busybox docker/libcontainer nsinit exec sh
11
+
12
+GO_PACKAGES = $(shell find . -not \( -wholename ./vendor -prune \) -name '*.go' -print0 | xargs -0n1 dirname | sort -u)
13
+
14
+direct-test:
15
+	go test -cover -v $(GO_PACKAGES)
16
+
17
+direct-test-short:
18
+	go test -cover -test.short -v $(GO_PACKAGES)
19
+
20
+direct-build:
21
+	go build -v $(GO_PACKAGES)
22
+
23
+direct-install:
24
+	go install -v $(GO_PACKAGES)
... ...
@@ -18,8 +18,8 @@ var createCommand = cli.Command{
18 18
 	Name:  "create",
19 19
 	Usage: "Create a cgroup container using the supplied configuration and initial process.",
20 20
 	Flags: []cli.Flag{
21
-		cli.StringFlag{"config, c", "cgroup.json", "path to container configuration (cgroups.Cgroup object)"},
22
-		cli.IntFlag{"pid, p", 0, "pid of the initial process in the container"},
21
+		cli.StringFlag{Name: "config, c", Value: "cgroup.json", Usage: "path to container configuration (cgroups.Cgroup object)"},
22
+		cli.IntFlag{Name: "pid, p", Value: 0, Usage: "pid of the initial process in the container"},
23 23
 	},
24 24
 	Action: createAction,
25 25
 }
... ...
@@ -28,8 +28,8 @@ var destroyCommand = cli.Command{
28 28
 	Name:  "destroy",
29 29
 	Usage: "Destroy an existing cgroup container.",
30 30
 	Flags: []cli.Flag{
31
-		cli.StringFlag{"name, n", "", "container name"},
32
-		cli.StringFlag{"parent, p", "", "container parent"},
31
+		cli.StringFlag{Name: "name, n", Value: "", Usage: "container name"},
32
+		cli.StringFlag{Name: "parent, p", Value: "", Usage: "container parent"},
33 33
 	},
34 34
 	Action: destroyAction,
35 35
 }
... ...
@@ -38,8 +38,8 @@ var statsCommand = cli.Command{
38 38
 	Name:  "stats",
39 39
 	Usage: "Get stats for cgroup",
40 40
 	Flags: []cli.Flag{
41
-		cli.StringFlag{"name, n", "", "container name"},
42
-		cli.StringFlag{"parent, p", "", "container parent"},
41
+		cli.StringFlag{Name: "name, n", Value: "", Usage: "container name"},
42
+		cli.StringFlag{Name: "parent, p", Value: "", Usage: "container parent"},
43 43
 	},
44 44
 	Action: statsAction,
45 45
 }
... ...
@@ -48,8 +48,8 @@ var pauseCommand = cli.Command{
48 48
 	Name:  "pause",
49 49
 	Usage: "Pause cgroup",
50 50
 	Flags: []cli.Flag{
51
-		cli.StringFlag{"name, n", "", "container name"},
52
-		cli.StringFlag{"parent, p", "", "container parent"},
51
+		cli.StringFlag{Name: "name, n", Value: "", Usage: "container name"},
52
+		cli.StringFlag{Name: "parent, p", Value: "", Usage: "container parent"},
53 53
 	},
54 54
 	Action: pauseAction,
55 55
 }
... ...
@@ -58,8 +58,8 @@ var resumeCommand = cli.Command{
58 58
 	Name:  "resume",
59 59
 	Usage: "Resume a paused cgroup",
60 60
 	Flags: []cli.Flag{
61
-		cli.StringFlag{"name, n", "", "container name"},
62
-		cli.StringFlag{"parent, p", "", "container parent"},
61
+		cli.StringFlag{Name: "name, n", Value: "", Usage: "container name"},
62
+		cli.StringFlag{Name: "parent, p", Value: "", Usage: "container parent"},
63 63
 	},
64 64
 	Action: resumeAction,
65 65
 }
... ...
@@ -68,8 +68,8 @@ var psCommand = cli.Command{
68 68
 	Name:  "ps",
69 69
 	Usage: "Get list of pids for a cgroup",
70 70
 	Flags: []cli.Flag{
71
-		cli.StringFlag{"name, n", "", "container name"},
72
-		cli.StringFlag{"parent, p", "", "container parent"},
71
+		cli.StringFlag{Name: "name, n", Value: "", Usage: "container name"},
72
+		cli.StringFlag{Name: "parent, p", Value: "", Usage: "container parent"},
73 73
 	},
74 74
 	Action: psAction,
75 75
 }
... ...
@@ -4,52 +4,18 @@ package namespaces
4 4
 
5 5
 import (
6 6
 	"encoding/json"
7
+	"os"
8
+	"strconv"
9
+
7 10
 	"github.com/docker/libcontainer"
8 11
 	"github.com/docker/libcontainer/label"
9 12
 	"github.com/docker/libcontainer/system"
10
-	"io"
11
-	"os"
12
-	"os/exec"
13
-	"strconv"
14
-	"syscall"
15 13
 )
16 14
 
17
-// Runs the command under 'args' inside an existing container referred to by 'container'.
18
-// Returns the exitcode of the command upon success and appropriate error on failure.
19
-func RunIn(container *libcontainer.Config, state *libcontainer.State, args []string, nsinitPath string, stdin io.Reader, stdout, stderr io.Writer, console string, startCallback func(*exec.Cmd)) (int, error) {
20
-	initArgs, err := getNsEnterCommand(strconv.Itoa(state.InitPid), container, console, args)
21
-	if err != nil {
22
-		return -1, err
23
-	}
24
-
25
-	cmd := exec.Command(nsinitPath, initArgs...)
26
-	// Note: these are only used in non-tty mode
27
-	// if there is a tty for the container it will be opened within the namespace and the
28
-	// fds will be duped to stdin, stdiout, and stderr
29
-	cmd.Stdin = stdin
30
-	cmd.Stdout = stdout
31
-	cmd.Stderr = stderr
32
-
33
-	if err := cmd.Start(); err != nil {
34
-		return -1, err
35
-	}
36
-	if startCallback != nil {
37
-		startCallback(cmd)
38
-	}
39
-
40
-	if err := cmd.Wait(); err != nil {
41
-		if _, ok := err.(*exec.ExitError); !ok {
42
-			return -1, err
43
-		}
44
-	}
45
-
46
-	return cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus(), nil
47
-}
48
-
49 15
 // ExecIn uses an existing pid and joins the pid's namespaces with the new command.
50 16
 func ExecIn(container *libcontainer.Config, state *libcontainer.State, args []string) error {
51 17
 	// Enter the namespace and then finish setup
52
-	args, err := getNsEnterCommand(strconv.Itoa(state.InitPid), container, "", args)
18
+	args, err := GetNsEnterCommand(strconv.Itoa(state.InitPid), container, "", args)
53 19
 	if err != nil {
54 20
 		return err
55 21
 	}
... ...
@@ -73,14 +39,13 @@ func getContainerJson(container *libcontainer.Config) (string, error) {
73 73
 	return string(containerJson), nil
74 74
 }
75 75
 
76
-func getNsEnterCommand(initPid string, container *libcontainer.Config, console string, args []string) ([]string, error) {
76
+func GetNsEnterCommand(initPid string, container *libcontainer.Config, console string, args []string) ([]string, error) {
77 77
 	containerJson, err := getContainerJson(container)
78 78
 	if err != nil {
79 79
 		return nil, err
80 80
 	}
81 81
 
82 82
 	out := []string{
83
-		"nsenter",
84 83
 		"--nspid", initPid,
85 84
 		"--containerjson", containerJson,
86 85
 	}
... ...
@@ -88,6 +53,7 @@ func getNsEnterCommand(initPid string, container *libcontainer.Config, console s
88 88
 	if console != "" {
89 89
 		out = append(out, "--console", console)
90 90
 	}
91
+	out = append(out, "nsenter")
91 92
 	out = append(out, "--")
92 93
 	out = append(out, args...)
93 94
 
94 95
new file mode 100644
... ...
@@ -0,0 +1,235 @@
0
+// +build cgo
1
+//
2
+// formated with indent -linux nsenter.c
3
+
4
+#include <errno.h>
5
+#include <fcntl.h>
6
+#include <linux/limits.h>
7
+#include <linux/sched.h>
8
+#include <signal.h>
9
+#include <stdio.h>
10
+#include <stdlib.h>
11
+#include <string.h>
12
+#include <sys/types.h>
13
+#include <unistd.h>
14
+#include <getopt.h>
15
+
16
+static const kBufSize = 256;
17
+static const char *kNsEnter = "nsenter";
18
+
19
+void get_args(int *argc, char ***argv)
20
+{
21
+	// Read argv
22
+	int fd = open("/proc/self/cmdline", O_RDONLY);
23
+
24
+	// Read the whole commandline.
25
+	ssize_t contents_size = 0;
26
+	ssize_t contents_offset = 0;
27
+	char *contents = NULL;
28
+	ssize_t bytes_read = 0;
29
+	do {
30
+		contents_size += kBufSize;
31
+		contents = (char *)realloc(contents, contents_size);
32
+		bytes_read =
33
+		    read(fd, contents + contents_offset,
34
+			 contents_size - contents_offset);
35
+		contents_offset += bytes_read;
36
+	}
37
+	while (bytes_read > 0);
38
+	close(fd);
39
+
40
+	// Parse the commandline into an argv. /proc/self/cmdline has \0 delimited args.
41
+	ssize_t i;
42
+	*argc = 0;
43
+	for (i = 0; i < contents_offset; i++) {
44
+		if (contents[i] == '\0') {
45
+			(*argc)++;
46
+		}
47
+	}
48
+	*argv = (char **)malloc(sizeof(char *) * ((*argc) + 1));
49
+	int idx;
50
+	for (idx = 0; idx < (*argc); idx++) {
51
+		(*argv)[idx] = contents;
52
+		contents += strlen(contents) + 1;
53
+	}
54
+	(*argv)[*argc] = NULL;
55
+}
56
+
57
+// Use raw setns syscall for versions of glibc that don't include it (namely glibc-2.12)
58
+#if __GLIBC__ == 2 && __GLIBC_MINOR__ < 14
59
+#define _GNU_SOURCE
60
+#include <sched.h>
61
+#include "syscall.h"
62
+#ifdef SYS_setns
63
+int setns(int fd, int nstype)
64
+{
65
+	return syscall(SYS_setns, fd, nstype);
66
+}
67
+#endif
68
+#endif
69
+
70
+void print_usage()
71
+{
72
+	fprintf(stderr,
73
+		"<binary> nsenter --nspid <pid> --containerjson <container_json> -- cmd1 arg1 arg2...\n");
74
+}
75
+
76
+void nsenter()
77
+{
78
+	int argc, c;
79
+	char **argv;
80
+	get_args(&argc, &argv);
81
+
82
+	// Ignore if this is not for us.
83
+	if (argc < 6) {
84
+		return;
85
+	}
86
+	int found_nsenter = 0;
87
+	for (c = 0; c < argc; ++c) {
88
+		if (strcmp(argv[c], kNsEnter) == 0) {
89
+			found_nsenter = 1;
90
+			break;
91
+		}
92
+	}
93
+	if (!found_nsenter) {
94
+		return;
95
+	}
96
+	static const struct option longopts[] = {
97
+		{"nspid", required_argument, NULL, 'n'},
98
+		{"containerjson", required_argument, NULL, 'c'},
99
+		{"console", optional_argument, NULL, 't'},
100
+		{NULL, 0, NULL, 0}
101
+	};
102
+
103
+	pid_t init_pid = -1;
104
+	char *init_pid_str = NULL;
105
+	char *container_json = NULL;
106
+	char *console = NULL;
107
+	opterr = 0;
108
+	while ((c =
109
+		getopt_long_only(argc, argv, "-n:s:c:", longopts,
110
+				 NULL)) != -1) {
111
+		switch (c) {
112
+		case 'n':
113
+			init_pid_str = optarg;
114
+			break;
115
+		case 'c':
116
+			container_json = optarg;
117
+			break;
118
+		case 't':
119
+			console = optarg;
120
+			break;
121
+		}
122
+	}
123
+
124
+	if (strcmp(argv[optind - 2], kNsEnter) != 0) {
125
+		return;
126
+	}
127
+
128
+	if (container_json == NULL || init_pid_str == NULL) {
129
+		print_usage();
130
+		exit(1);
131
+	}
132
+
133
+	init_pid = strtol(init_pid_str, NULL, 10);
134
+	if ((init_pid == 0 && errno == EINVAL) || errno == ERANGE) {
135
+		fprintf(stderr,
136
+			"nsenter: Failed to parse PID from \"%s\" with output \"%d\" and error: \"%s\"\n",
137
+			init_pid_str, init_pid, strerror(errno));
138
+		print_usage();
139
+		exit(1);
140
+	}
141
+
142
+	argc -= 3;
143
+	argv += 3;
144
+
145
+	if (setsid() == -1) {
146
+		fprintf(stderr, "setsid failed. Error: %s\n", strerror(errno));
147
+		exit(1);
148
+	}
149
+	// before we setns we need to dup the console
150
+	int consolefd = -1;
151
+	if (console != NULL) {
152
+		consolefd = open(console, O_RDWR);
153
+		if (consolefd < 0) {
154
+			fprintf(stderr,
155
+				"nsenter: failed to open console %s %s\n",
156
+				console, strerror(errno));
157
+			exit(1);
158
+		}
159
+	}
160
+	// Setns on all supported namespaces.
161
+	char ns_dir[PATH_MAX];
162
+	memset(ns_dir, 0, PATH_MAX);
163
+	snprintf(ns_dir, PATH_MAX - 1, "/proc/%d/ns/", init_pid);
164
+
165
+	char *namespaces[] = { "ipc", "uts", "net", "pid", "mnt" };
166
+	const int num = sizeof(namespaces) / sizeof(char *);
167
+	int i;
168
+	for (i = 0; i < num; i++) {
169
+		char buf[PATH_MAX];
170
+		memset(buf, 0, PATH_MAX);
171
+		snprintf(buf, PATH_MAX - 1, "%s%s", ns_dir, namespaces[i]);
172
+		int fd = open(buf, O_RDONLY);
173
+		if (fd == -1) {
174
+			// Ignore nonexistent namespaces.
175
+			if (errno == ENOENT)
176
+				continue;
177
+
178
+			fprintf(stderr,
179
+				"nsenter: Failed to open ns file \"%s\" for ns \"%s\" with error: \"%s\"\n",
180
+				buf, namespaces[i], strerror(errno));
181
+			exit(1);
182
+		}
183
+		// Set the namespace.
184
+		if (setns(fd, 0) == -1) {
185
+			fprintf(stderr,
186
+				"nsenter: Failed to setns for \"%s\" with error: \"%s\"\n",
187
+				namespaces[i], strerror(errno));
188
+			exit(1);
189
+		}
190
+		close(fd);
191
+	}
192
+
193
+	// We must fork to actually enter the PID namespace.
194
+	int child = fork();
195
+	if (child == 0) {
196
+		if (consolefd != -1) {
197
+			if (dup2(consolefd, STDIN_FILENO) != 0) {
198
+				fprintf(stderr, "nsenter: failed to dup 0 %s\n",
199
+					strerror(errno));
200
+				exit(1);
201
+			}
202
+			if (dup2(consolefd, STDOUT_FILENO) != STDOUT_FILENO) {
203
+				fprintf(stderr, "nsenter: failed to dup 1 %s\n",
204
+					strerror(errno));
205
+				exit(1);
206
+			}
207
+			if (dup2(consolefd, STDERR_FILENO) != STDERR_FILENO) {
208
+				fprintf(stderr, "nsenter: failed to dup 2 %s\n",
209
+					strerror(errno));
210
+				exit(1);
211
+			}
212
+		}
213
+		// Finish executing, let the Go runtime take over.
214
+		return;
215
+	} else {
216
+		// Parent, wait for the child.
217
+		int status = 0;
218
+		if (waitpid(child, &status, 0) == -1) {
219
+			fprintf(stderr,
220
+				"nsenter: Failed to waitpid with error: \"%s\"\n",
221
+				strerror(errno));
222
+			exit(1);
223
+		}
224
+		// Forward the child's exit code or re-send its death signal.
225
+		if (WIFEXITED(status)) {
226
+			exit(WEXITSTATUS(status));
227
+		} else if (WIFSIGNALED(status)) {
228
+			kill(getpid(), WTERMSIG(status));
229
+		}
230
+		exit(1);
231
+	}
232
+
233
+	return;
234
+}
... ...
@@ -3,212 +3,6 @@
3 3
 package namespaces
4 4
 
5 5
 /*
6
-#include <errno.h>
7
-#include <fcntl.h>
8
-#include <linux/limits.h>
9
-#include <linux/sched.h>
10
-#include <signal.h>
11
-#include <stdio.h>
12
-#include <stdlib.h>
13
-#include <string.h>
14
-#include <sys/types.h>
15
-#include <unistd.h>
16
-#include <getopt.h>
17
-
18
-static const kBufSize = 256;
19
-
20
-void get_args(int *argc, char ***argv) {
21
-	// Read argv
22
-	int fd = open("/proc/self/cmdline", O_RDONLY);
23
-
24
-	// Read the whole commandline.
25
-	ssize_t contents_size = 0;
26
-	ssize_t contents_offset = 0;
27
-	char *contents = NULL;
28
-	ssize_t bytes_read = 0;
29
-	do {
30
-		contents_size += kBufSize;
31
-		contents = (char *) realloc(contents, contents_size);
32
-		bytes_read = read(fd, contents + contents_offset, contents_size - contents_offset);
33
-		contents_offset += bytes_read;
34
-	} while (bytes_read > 0);
35
-	close(fd);
36
-
37
-	// Parse the commandline into an argv. /proc/self/cmdline has \0 delimited args.
38
-	ssize_t i;
39
-	*argc = 0;
40
-	for (i = 0; i < contents_offset; i++) {
41
-		if (contents[i] == '\0') {
42
-			(*argc)++;
43
-		}
44
-	}
45
-	*argv = (char **) malloc(sizeof(char *) * ((*argc) + 1));
46
-	int idx;
47
-	for (idx = 0; idx < (*argc); idx++) {
48
-		(*argv)[idx] = contents;
49
-		contents += strlen(contents) + 1;
50
-	}
51
-	(*argv)[*argc] = NULL;
52
-}
53
-
54
-// Use raw setns syscall for versions of glibc that don't include it (namely glibc-2.12)
55
-#if __GLIBC__ == 2 && __GLIBC_MINOR__ < 14
56
-#define _GNU_SOURCE
57
-#include <sched.h>
58
-#include "syscall.h"
59
-#ifdef SYS_setns
60
-int setns(int fd, int nstype) {
61
-  return syscall(SYS_setns, fd, nstype);
62
-}
63
-#endif
64
-#endif
65
-
66
-void print_usage() {
67
-	fprintf(stderr, "<binary> nsenter --nspid <pid> --containerjson <container_json> -- cmd1 arg1 arg2...\n");
68
-}
69
-
70
-void nsenter() {
71
-	int argc;
72
-	char **argv;
73
-	get_args(&argc, &argv);
74
-
75
-	// Ignore if this is not for us.
76
-	if (argc < 2 || strcmp(argv[1], "nsenter") != 0) {
77
-		return;
78
-	}
79
-
80
-	// USAGE: <binary> nsenter <PID> <process label> <container JSON> <argv>...
81
-	if (argc < 6) {
82
-		fprintf(stderr, "nsenter: Incorrect usage, not enough arguments\n");
83
-		exit(1);
84
-	}
85
-
86
-	static const struct option longopts[] = {
87
-		{ "nspid",         required_argument, NULL, 'n' },
88
-		{ "containerjson", required_argument, NULL, 'c' },
89
-                { "console",       required_argument, NULL, 't' },
90
-		{ NULL,            0,                 NULL,  0  }
91
-	};
92
-
93
-	int c;
94
-	pid_t init_pid = -1;
95
-	char *init_pid_str = NULL;
96
-	char *container_json = NULL;
97
-        char *console = NULL;
98
-	while ((c = getopt_long_only(argc, argv, "n:s:c:", longopts, NULL)) != -1) {
99
-		switch (c) {
100
-		case 'n':
101
-			init_pid_str = optarg;
102
-			break;
103
-		case 'c':
104
-			container_json = optarg;
105
-			break;
106
-		case 't':
107
-			console = optarg;
108
-			break;
109
-		}
110
-	}
111
-
112
-	if (container_json == NULL || init_pid_str == NULL) {
113
-		print_usage();
114
-		exit(1);
115
-	}
116
-
117
-	init_pid = strtol(init_pid_str, NULL, 10);
118
-	if (errno != 0 || init_pid <= 0) {
119
-		fprintf(stderr, "nsenter: Failed to parse PID from \"%s\" with error: \"%s\"\n", init_pid_str, strerror(errno));
120
-		print_usage();
121
-		exit(1);
122
-	}
123
-
124
-	argc -= 3;
125
-	argv += 3;
126
-
127
-        if (setsid() == -1) {
128
-               fprintf(stderr, "setsid failed. Error: %s\n", strerror(errno));
129
-               exit(1);
130
-        }
131
-
132
-        // before we setns we need to dup the console
133
-        int consolefd = -1;
134
-        if (console != NULL) {
135
-               consolefd = open(console, O_RDWR);
136
-               if (consolefd < 0) {
137
-                    fprintf(stderr, "nsenter: failed to open console %s %s\n", console, strerror(errno));
138
-                    exit(1);
139
-              }
140
-        }
141
-
142
-	// Setns on all supported namespaces.
143
-	char ns_dir[PATH_MAX];
144
-	memset(ns_dir, 0, PATH_MAX);
145
-	snprintf(ns_dir, PATH_MAX - 1, "/proc/%d/ns/", init_pid);
146
-
147
-	char* namespaces[] = {"ipc", "uts", "net", "pid", "mnt"};
148
-	const int num = sizeof(namespaces) / sizeof(char*);
149
-	int i;
150
-	for (i = 0; i < num; i++) {
151
-		char buf[PATH_MAX];
152
-		memset(buf, 0, PATH_MAX);
153
-		snprintf(buf, PATH_MAX - 1, "%s%s", ns_dir, namespaces[i]);
154
-		int fd = open(buf, O_RDONLY);
155
-		if (fd == -1) {
156
-			// Ignore nonexistent namespaces.
157
-			if (errno == ENOENT)
158
-				continue;
159
-
160
-			fprintf(stderr, "nsenter: Failed to open ns file \"%s\" for ns \"%s\" with error: \"%s\"\n", buf, namespaces[i], strerror(errno));
161
-			exit(1);
162
-		}
163
-
164
-		// Set the namespace.
165
-		if (setns(fd, 0) == -1) {
166
-			fprintf(stderr, "nsenter: Failed to setns for \"%s\" with error: \"%s\"\n", namespaces[i], strerror(errno));
167
-			exit(1);
168
-		}
169
-		close(fd);
170
-	}
171
-
172
-	// We must fork to actually enter the PID namespace.
173
-	int child = fork();
174
-	if (child == 0) {
175
-       if (consolefd != -1) {
176
-        if (dup2(consolefd, STDIN_FILENO) != 0) {
177
-            fprintf(stderr, "nsenter: failed to dup 0 %s\n",  strerror(errno));
178
-            exit(1);
179
-        }
180
-        if (dup2(consolefd, STDOUT_FILENO) != STDOUT_FILENO) {
181
-            fprintf(stderr, "nsenter: failed to dup 1 %s\n",  strerror(errno));
182
-            exit(1);
183
-        }
184
-        if (dup2(consolefd, STDERR_FILENO) != STDERR_FILENO) {
185
-            fprintf(stderr, "nsenter: failed to dup 2 %s\n",  strerror(errno));
186
-            exit(1);
187
-        }
188
-}
189
-
190
-		// Finish executing, let the Go runtime take over.
191
-		return;
192
-	} else {
193
-		// Parent, wait for the child.
194
-		int status = 0;
195
-		if (waitpid(child, &status, 0) == -1) {
196
-			fprintf(stderr, "nsenter: Failed to waitpid with error: \"%s\"\n", strerror(errno));
197
-			exit(1);
198
-		}
199
-
200
-		// Forward the child's exit code or re-send its death signal.
201
-		if (WIFEXITED(status)) {
202
-			exit(WEXITSTATUS(status));
203
-		} else if (WIFSIGNALED(status)) {
204
-			kill(getpid(), WTERMSIG(status));
205
-		}
206
-		exit(1);
207
-	}
208
-
209
-	return;
210
-}
211
-
212 6
 __attribute__((constructor)) init() {
213 7
 	nsenter();
214 8
 }
... ...
@@ -189,13 +189,15 @@ func newRtAttrChild(parent *RtAttr, attrType int, data []byte) *RtAttr {
189 189
 }
190 190
 
191 191
 func (a *RtAttr) Len() int {
192
+	if len(a.children) == 0 {
193
+		return (syscall.SizeofRtAttr + len(a.Data))
194
+	}
195
+
192 196
 	l := 0
193 197
 	for _, child := range a.children {
194
-		l += child.Len() + syscall.SizeofRtAttr
195
-	}
196
-	if l == 0 {
197
-		l++
198
+		l += child.Len()
198 199
 	}
200
+	l += syscall.SizeofRtAttr
199 201
 	return rtaAlignOf(l + len(a.Data))
200 202
 }
201 203
 
... ...
@@ -203,7 +205,7 @@ func (a *RtAttr) ToWireFormat() []byte {
203 203
 	native := nativeEndian()
204 204
 
205 205
 	length := a.Len()
206
-	buf := make([]byte, rtaAlignOf(length+syscall.SizeofRtAttr))
206
+	buf := make([]byte, rtaAlignOf(length))
207 207
 
208 208
 	if a.Data != nil {
209 209
 		copy(buf[4:], a.Data)
... ...
@@ -216,11 +218,10 @@ func (a *RtAttr) ToWireFormat() []byte {
216 216
 		}
217 217
 	}
218 218
 
219
-	if l := uint16(rtaAlignOf(length)); l != 0 {
220
-		native.PutUint16(buf[0:2], l+1)
219
+	if l := uint16(length); l != 0 {
220
+		native.PutUint16(buf[0:2], l)
221 221
 	}
222 222
 	native.PutUint16(buf[2:4], a.Type)
223
-
224 223
 	return buf
225 224
 }
226 225
 
... ...
@@ -700,6 +701,10 @@ func nonZeroTerminated(s string) []byte {
700 700
 // Add a new network link of a specified type. This is identical to
701 701
 // running: ip add link $name type $linkType
702 702
 func NetworkLinkAdd(name string, linkType string) error {
703
+	if name == "" || linkType == "" {
704
+		return fmt.Errorf("Neither link name nor link type can be empty!")
705
+	}
706
+
703 707
 	s, err := getNetlinkSocket()
704 708
 	if err != nil {
705 709
 		return err
... ...
@@ -711,15 +716,43 @@ func NetworkLinkAdd(name string, linkType string) error {
711 711
 	msg := newIfInfomsg(syscall.AF_UNSPEC)
712 712
 	wb.AddData(msg)
713 713
 
714
-	if name != "" {
715
-		nameData := newRtAttr(syscall.IFLA_IFNAME, zeroTerminated(name))
716
-		wb.AddData(nameData)
714
+	linkInfo := newRtAttr(syscall.IFLA_LINKINFO, nil)
715
+	newRtAttrChild(linkInfo, IFLA_INFO_KIND, nonZeroTerminated(linkType))
716
+	wb.AddData(linkInfo)
717
+
718
+	nameData := newRtAttr(syscall.IFLA_IFNAME, zeroTerminated(name))
719
+	wb.AddData(nameData)
720
+
721
+	if err := s.Send(wb); err != nil {
722
+		return err
717 723
 	}
718 724
 
719
-	kindData := newRtAttr(IFLA_INFO_KIND, nonZeroTerminated(linkType))
725
+	return s.HandleAck(wb.Seq)
726
+}
727
+
728
+// Delete a network link. This is identical to
729
+// running: ip link del $name
730
+func NetworkLinkDel(name string) error {
731
+	if name == "" {
732
+		return fmt.Errorf("Network link name can not be empty!")
733
+	}
720 734
 
721
-	infoData := newRtAttr(syscall.IFLA_LINKINFO, kindData.ToWireFormat())
722
-	wb.AddData(infoData)
735
+	s, err := getNetlinkSocket()
736
+	if err != nil {
737
+		return err
738
+	}
739
+	defer s.Close()
740
+
741
+	iface, err := net.InterfaceByName(name)
742
+	if err != nil {
743
+		return err
744
+	}
745
+
746
+	wb := newNetlinkRequest(syscall.RTM_DELLINK, syscall.NLM_F_ACK)
747
+
748
+	msg := newIfInfomsg(syscall.AF_UNSPEC)
749
+	msg.Index = int32(iface.Index)
750
+	wb.AddData(msg)
723 751
 
724 752
 	if err := s.Send(wb); err != nil {
725 753
 		return err
... ...
@@ -27,10 +27,35 @@ func TestCreateBridgeWithMac(t *testing.T) {
27 27
 	}
28 28
 
29 29
 	if _, err := net.InterfaceByName(name); err == nil {
30
-		t.Fatal("expected error getting interface because bridge was deleted")
30
+		t.Fatalf("expected error getting interface because %s bridge was deleted", name)
31 31
 	}
32 32
 }
33 33
 
34
+func TestCreateBridgeLink(t *testing.T) {
35
+	if testing.Short() {
36
+		return
37
+	}
38
+
39
+	name := "mybrlink"
40
+
41
+	if err := NetworkLinkAdd(name, "bridge"); err != nil {
42
+		t.Fatal(err)
43
+	}
44
+
45
+	if _, err := net.InterfaceByName(name); err != nil {
46
+		t.Fatal(err)
47
+	}
48
+
49
+	if err := NetworkLinkDel(name); err != nil {
50
+		t.Fatal(err)
51
+	}
52
+
53
+	if _, err := net.InterfaceByName(name); err == nil {
54
+		t.Fatalf("expected error getting interface because %s bridge was deleted", name)
55
+	}
56
+
57
+}
58
+
34 59
 func TestCreateVethPair(t *testing.T) {
35 60
 	if testing.Short() {
36 61
 		return
... ...
@@ -19,6 +19,10 @@ func NetworkLinkAdd(name string, linkType string) error {
19 19
 	return ErrNotImplemented
20 20
 }
21 21
 
22
+func NetworkLinkDel(name string) error {
23
+	return ErrNotImplemented
24
+}
25
+
22 26
 func NetworkLinkUp(iface *net.Interface) error {
23 27
 	return ErrNotImplemented
24 28
 }
... ...
@@ -24,7 +24,10 @@ func NsInit() {
24 24
 	app.Name = "nsinit"
25 25
 	app.Version = "0.1"
26 26
 	app.Author = "libcontainer maintainers"
27
-
27
+	app.Flags = []cli.Flag{
28
+		cli.StringFlag{Name: "nspid"},
29
+		cli.StringFlag{Name: "containerjson"},
30
+		cli.StringFlag{Name: "console"}}
28 31
 	app.Before = preload
29 32
 	app.Commands = []cli.Command{
30 33
 		execCommand,
... ...
@@ -36,7 +36,7 @@ func execAction(context *cli.Context) {
36 36
 	}
37 37
 
38 38
 	if state != nil {
39
-		exitCode, err = runIn(container, state, []string(context.Args()))
39
+		err = namespaces.ExecIn(container, state, []string(context.Args()))
40 40
 	} else {
41 41
 		exitCode, err = startContainer(container, dataPath, []string(context.Args()))
42 42
 	}
... ...
@@ -48,59 +48,6 @@ func execAction(context *cli.Context) {
48 48
 	os.Exit(exitCode)
49 49
 }
50 50
 
51
-func runIn(container *libcontainer.Config, state *libcontainer.State, args []string) (int, error) {
52
-	var (
53
-		master  *os.File
54
-		console string
55
-		err     error
56
-
57
-		stdin  = os.Stdin
58
-		stdout = os.Stdout
59
-		stderr = os.Stderr
60
-		sigc   = make(chan os.Signal, 10)
61
-	)
62
-
63
-	signal.Notify(sigc)
64
-
65
-	if container.Tty {
66
-		stdin = nil
67
-		stdout = nil
68
-		stderr = nil
69
-
70
-		master, console, err = consolepkg.CreateMasterAndConsole()
71
-		if err != nil {
72
-			log.Fatal(err)
73
-		}
74
-
75
-		go io.Copy(master, os.Stdin)
76
-		go io.Copy(os.Stdout, master)
77
-
78
-		state, err := term.SetRawTerminal(os.Stdin.Fd())
79
-		if err != nil {
80
-			log.Fatal(err)
81
-		}
82
-
83
-		defer term.RestoreTerminal(os.Stdin.Fd(), state)
84
-	}
85
-
86
-	startCallback := func(cmd *exec.Cmd) {
87
-		go func() {
88
-			resizeTty(master)
89
-
90
-			for sig := range sigc {
91
-				switch sig {
92
-				case syscall.SIGWINCH:
93
-					resizeTty(master)
94
-				default:
95
-					cmd.Process.Signal(sig)
96
-				}
97
-			}
98
-		}()
99
-	}
100
-
101
-	return namespaces.RunIn(container, state, args, os.Args[0], stdin, stdout, stderr, console, startCallback)
102
-}
103
-
104 51
 // startContainer starts the container. Returns the exit status or -1 and an
105 52
 // error.
106 53
 //
... ...
@@ -2,6 +2,7 @@ package nsinit
2 2
 
3 3
 import (
4 4
 	"log"
5
+	"strconv"
5 6
 
6 7
 	"github.com/codegangsta/cli"
7 8
 	"github.com/docker/libcontainer/namespaces"
... ...
@@ -11,11 +12,6 @@ var nsenterCommand = cli.Command{
11 11
 	Name:   "nsenter",
12 12
 	Usage:  "init process for entering an existing namespace",
13 13
 	Action: nsenterAction,
14
-	Flags: []cli.Flag{
15
-		cli.IntFlag{Name: "nspid"},
16
-		cli.StringFlag{Name: "containerjson"},
17
-		cli.StringFlag{Name: "console"},
18
-	},
19 14
 }
20 15
 
21 16
 func nsenterAction(context *cli.Context) {
... ...
@@ -25,14 +21,14 @@ func nsenterAction(context *cli.Context) {
25 25
 		args = []string{"/bin/bash"}
26 26
 	}
27 27
 
28
-	container, err := loadContainerFromJson(context.String("containerjson"))
28
+	container, err := loadContainerFromJson(context.GlobalString("containerjson"))
29 29
 	if err != nil {
30 30
 		log.Fatalf("unable to load container: %s", err)
31 31
 	}
32 32
 
33
-	nspid := context.Int("nspid")
34
-	if nspid <= 0 {
35
-		log.Fatalf("cannot enter into namespaces without valid pid: %q", nspid)
33
+	nspid, err := strconv.Atoi(context.GlobalString("nspid"))
34
+	if nspid <= 0 || err != nil {
35
+		log.Fatalf("cannot enter into namespaces without valid pid: %q - %s", nspid, err)
36 36
 	}
37 37
 
38 38
 	if err := namespaces.NsEnter(container, args); err != nil {
39 39
new file mode 100755
... ...
@@ -0,0 +1,48 @@
0
+#!/usr/bin/env bash
1
+set -e
2
+
3
+cd "$(dirname "$BASH_SOURCE")"
4
+
5
+# Downloads dependencies into vendor/ directory
6
+mkdir -p vendor
7
+cd vendor
8
+
9
+clone() {
10
+	vcs=$1
11
+	pkg=$2
12
+	rev=$3
13
+	
14
+	pkg_url=https://$pkg
15
+	target_dir=src/$pkg
16
+	
17
+	echo -n "$pkg @ $rev: "
18
+	
19
+	if [ -d $target_dir ]; then
20
+		echo -n 'rm old, '
21
+		rm -fr $target_dir
22
+	fi
23
+	
24
+	echo -n 'clone, '
25
+	case $vcs in
26
+		git)
27
+			git clone --quiet --no-checkout $pkg_url $target_dir
28
+			( cd $target_dir && git reset --quiet --hard $rev )
29
+			;;
30
+		hg)
31
+			hg clone --quiet --updaterev $rev $pkg_url $target_dir
32
+			;;
33
+	esac
34
+	
35
+	echo -n 'rm VCS, '
36
+	( cd $target_dir && rm -rf .{git,hg} )
37
+	
38
+	echo done
39
+}
40
+
41
+# the following lines are in sorted order, FYI
42
+clone git github.com/codegangsta/cli 1.1.0
43
+clone git github.com/coreos/go-systemd v2
44
+clone git github.com/godbus/dbus v1
45
+clone git github.com/syndtr/gocapability 3c85049eae
46
+
47
+# intentionally not vendoring Docker itself...  that'd be a circle :)