Browse code

Merge pull request #7312 from tianon/update-libcontainer

Bump libcontainer dep

Michael Crosby authored on 2014/08/01 07:20:07
Showing 23 changed files
... ...
@@ -27,10 +27,10 @@ import (
27 27
 	"github.com/docker/docker/pkg/listenbuffer"
28 28
 	"github.com/docker/docker/pkg/parsers"
29 29
 	"github.com/docker/docker/pkg/systemd"
30
-	"github.com/docker/docker/pkg/user"
31 30
 	"github.com/docker/docker/pkg/version"
32 31
 	"github.com/docker/docker/registry"
33 32
 	"github.com/docker/docker/utils"
33
+	"github.com/docker/libcontainer/user"
34 34
 	"github.com/gorilla/mux"
35 35
 )
36 36
 
... ...
@@ -121,7 +121,7 @@ func (b *buildFile) CmdFrom(name string) error {
121 121
 		b.config = image.Config
122 122
 	}
123 123
 	if b.config.Env == nil || len(b.config.Env) == 0 {
124
-		b.config.Env = append(b.config.Env, "HOME=/", "PATH="+daemon.DefaultPathEnv)
124
+		b.config.Env = append(b.config.Env, "PATH="+daemon.DefaultPathEnv)
125 125
 	}
126 126
 	// Process ONBUILD triggers if they exist
127 127
 	if nTriggers := len(b.config.OnBuild); nTriggers != 0 {
... ...
@@ -1043,9 +1043,12 @@ func (container *Container) setupLinkedContainers() ([]string, error) {
1043 1043
 func (container *Container) createDaemonEnvironment(linkedEnv []string) []string {
1044 1044
 	// Setup environment
1045 1045
 	env := []string{
1046
-		"HOME=/",
1047 1046
 		"PATH=" + DefaultPathEnv,
1048 1047
 		"HOSTNAME=" + container.Config.Hostname,
1048
+		// Note: we don't set HOME here because it'll get autoset intelligently
1049
+		// based on the value of USER inside dockerinit, but only if it isn't
1050
+		// set already (ie, that can be overridden by setting HOME via -e or ENV
1051
+		// in a Dockerfile).
1049 1052
 	}
1050 1053
 	if container.Config.Tty {
1051 1054
 		env = append(env, "TERM=xterm")
... ...
@@ -63,4 +63,4 @@ mv tmp-tar src/code.google.com/p/go/src/pkg/archive/tar
63 63
 
64 64
 clone git github.com/godbus/dbus v1
65 65
 clone git github.com/coreos/go-systemd v2
66
-clone git github.com/docker/libcontainer e6a43c1c2b9f769deb96348a0a93417cd48a36d8
66
+clone git github.com/docker/libcontainer bc06326a5e7decdc4191d1367de8439b9d83c450
... ...
@@ -723,7 +723,7 @@ func TestBuildRelativeWorkdir(t *testing.T) {
723 723
 
724 724
 func TestBuildEnv(t *testing.T) {
725 725
 	name := "testbuildenv"
726
-	expected := "[HOME=/ PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin PORT=2375]"
726
+	expected := "[PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin PORT=2375]"
727 727
 	defer deleteImages(name)
728 728
 	_, err := buildImage(name,
729 729
 		`FROM busybox
... ...
@@ -657,7 +657,7 @@ func TestRunTwoConcurrentContainers(t *testing.T) {
657 657
 }
658 658
 
659 659
 func TestEnvironment(t *testing.T) {
660
-	cmd := exec.Command(dockerBinary, "run", "-h", "testing", "-e=FALSE=true", "-e=TRUE", "-e=TRICKY", "busybox", "env")
660
+	cmd := exec.Command(dockerBinary, "run", "-h", "testing", "-e=FALSE=true", "-e=TRUE", "-e=TRICKY", "-e=HOME=", "busybox", "env")
661 661
 	cmd.Env = append(os.Environ(),
662 662
 		"TRUE=false",
663 663
 		"TRICKY=tri\ncky\n",
... ...
@@ -676,13 +676,13 @@ func TestEnvironment(t *testing.T) {
676 676
 
677 677
 	goodEnv := []string{
678 678
 		"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
679
-		"HOME=/",
680 679
 		"HOSTNAME=testing",
681 680
 		"FALSE=true",
682 681
 		"TRUE=false",
683 682
 		"TRICKY=tri",
684 683
 		"cky",
685 684
 		"",
685
+		"HOME=/root",
686 686
 	}
687 687
 	sort.Strings(goodEnv)
688 688
 	if len(goodEnv) != len(actualEnv) {
689 689
deleted file mode 100644
... ...
@@ -1 +0,0 @@
1
-Tianon Gravi <admwiggin@gmail.com> (@tianon)
2 1
deleted file mode 100644
... ...
@@ -1,256 +0,0 @@
1
-package user
2
-
3
-import (
4
-	"bufio"
5
-	"fmt"
6
-	"io"
7
-	"os"
8
-	"strconv"
9
-	"strings"
10
-)
11
-
12
-const (
13
-	minId = 0
14
-	maxId = 1<<31 - 1 //for 32-bit systems compatibility
15
-)
16
-
17
-var (
18
-	ErrRange = fmt.Errorf("Uids and gids must be in range %d-%d", minId, maxId)
19
-)
20
-
21
-type User struct {
22
-	Name  string
23
-	Pass  string
24
-	Uid   int
25
-	Gid   int
26
-	Gecos string
27
-	Home  string
28
-	Shell string
29
-}
30
-
31
-type Group struct {
32
-	Name string
33
-	Pass string
34
-	Gid  int
35
-	List []string
36
-}
37
-
38
-func parseLine(line string, v ...interface{}) {
39
-	if line == "" {
40
-		return
41
-	}
42
-
43
-	parts := strings.Split(line, ":")
44
-	for i, p := range parts {
45
-		if len(v) <= i {
46
-			// if we have more "parts" than we have places to put them, bail for great "tolerance" of naughty configuration files
47
-			break
48
-		}
49
-
50
-		switch e := v[i].(type) {
51
-		case *string:
52
-			// "root", "adm", "/bin/bash"
53
-			*e = p
54
-		case *int:
55
-			// "0", "4", "1000"
56
-			// ignore string to int conversion errors, for great "tolerance" of naughty configuration files
57
-			*e, _ = strconv.Atoi(p)
58
-		case *[]string:
59
-			// "", "root", "root,adm,daemon"
60
-			if p != "" {
61
-				*e = strings.Split(p, ",")
62
-			} else {
63
-				*e = []string{}
64
-			}
65
-		default:
66
-			// panic, because this is a programming/logic error, not a runtime one
67
-			panic("parseLine expects only pointers!  argument " + strconv.Itoa(i) + " is not a pointer!")
68
-		}
69
-	}
70
-}
71
-
72
-func ParsePasswd() ([]*User, error) {
73
-	return ParsePasswdFilter(nil)
74
-}
75
-
76
-func ParsePasswdFilter(filter func(*User) bool) ([]*User, error) {
77
-	f, err := os.Open("/etc/passwd")
78
-	if err != nil {
79
-		return nil, err
80
-	}
81
-	defer f.Close()
82
-	return parsePasswdFile(f, filter)
83
-}
84
-
85
-func parsePasswdFile(r io.Reader, filter func(*User) bool) ([]*User, error) {
86
-	var (
87
-		s   = bufio.NewScanner(r)
88
-		out = []*User{}
89
-	)
90
-
91
-	for s.Scan() {
92
-		if err := s.Err(); err != nil {
93
-			return nil, err
94
-		}
95
-
96
-		text := strings.TrimSpace(s.Text())
97
-		if text == "" {
98
-			continue
99
-		}
100
-
101
-		// see: man 5 passwd
102
-		//  name:password:UID:GID:GECOS:directory:shell
103
-		// Name:Pass:Uid:Gid:Gecos:Home:Shell
104
-		//  root:x:0:0:root:/root:/bin/bash
105
-		//  adm:x:3:4:adm:/var/adm:/bin/false
106
-		p := &User{}
107
-		parseLine(
108
-			text,
109
-			&p.Name, &p.Pass, &p.Uid, &p.Gid, &p.Gecos, &p.Home, &p.Shell,
110
-		)
111
-
112
-		if filter == nil || filter(p) {
113
-			out = append(out, p)
114
-		}
115
-	}
116
-
117
-	return out, nil
118
-}
119
-
120
-func ParseGroup() ([]*Group, error) {
121
-	return ParseGroupFilter(nil)
122
-}
123
-
124
-func ParseGroupFilter(filter func(*Group) bool) ([]*Group, error) {
125
-	f, err := os.Open("/etc/group")
126
-	if err != nil {
127
-		return nil, err
128
-	}
129
-	defer f.Close()
130
-	return parseGroupFile(f, filter)
131
-}
132
-
133
-func parseGroupFile(r io.Reader, filter func(*Group) bool) ([]*Group, error) {
134
-	var (
135
-		s   = bufio.NewScanner(r)
136
-		out = []*Group{}
137
-	)
138
-
139
-	for s.Scan() {
140
-		if err := s.Err(); err != nil {
141
-			return nil, err
142
-		}
143
-
144
-		text := s.Text()
145
-		if text == "" {
146
-			continue
147
-		}
148
-
149
-		// see: man 5 group
150
-		//  group_name:password:GID:user_list
151
-		// Name:Pass:Gid:List
152
-		//  root:x:0:root
153
-		//  adm:x:4:root,adm,daemon
154
-		p := &Group{}
155
-		parseLine(
156
-			text,
157
-			&p.Name, &p.Pass, &p.Gid, &p.List,
158
-		)
159
-
160
-		if filter == nil || filter(p) {
161
-			out = append(out, p)
162
-		}
163
-	}
164
-
165
-	return out, nil
166
-}
167
-
168
-// Given a string like "user", "1000", "user:group", "1000:1000", returns the uid, gid, and list of supplementary group IDs, if possible.
169
-func GetUserGroupSupplementary(userSpec string, defaultUid int, defaultGid int) (int, int, []int, error) {
170
-	var (
171
-		uid      = defaultUid
172
-		gid      = defaultGid
173
-		suppGids = []int{}
174
-
175
-		userArg, groupArg string
176
-	)
177
-
178
-	// allow for userArg to have either "user" syntax, or optionally "user:group" syntax
179
-	parseLine(userSpec, &userArg, &groupArg)
180
-
181
-	users, err := ParsePasswdFilter(func(u *User) bool {
182
-		if userArg == "" {
183
-			return u.Uid == uid
184
-		}
185
-		return u.Name == userArg || strconv.Itoa(u.Uid) == userArg
186
-	})
187
-	if err != nil && !os.IsNotExist(err) {
188
-		if userArg == "" {
189
-			userArg = strconv.Itoa(uid)
190
-		}
191
-		return 0, 0, nil, fmt.Errorf("Unable to find user %v: %v", userArg, err)
192
-	}
193
-
194
-	haveUser := users != nil && len(users) > 0
195
-	if haveUser {
196
-		// if we found any user entries that matched our filter, let's take the first one as "correct"
197
-		uid = users[0].Uid
198
-		gid = users[0].Gid
199
-	} else if userArg != "" {
200
-		// we asked for a user but didn't find them...  let's check to see if we wanted a numeric user
201
-		uid, err = strconv.Atoi(userArg)
202
-		if err != nil {
203
-			// not numeric - we have to bail
204
-			return 0, 0, nil, fmt.Errorf("Unable to find user %v", userArg)
205
-		}
206
-		if uid < minId || uid > maxId {
207
-			return 0, 0, nil, ErrRange
208
-		}
209
-
210
-		// if userArg couldn't be found in /etc/passwd but is numeric, just roll with it - this is legit
211
-	}
212
-
213
-	if groupArg != "" || (haveUser && users[0].Name != "") {
214
-		groups, err := ParseGroupFilter(func(g *Group) bool {
215
-			if groupArg != "" {
216
-				return g.Name == groupArg || strconv.Itoa(g.Gid) == groupArg
217
-			}
218
-			for _, u := range g.List {
219
-				if u == users[0].Name {
220
-					return true
221
-				}
222
-			}
223
-			return false
224
-		})
225
-		if err != nil && !os.IsNotExist(err) {
226
-			return 0, 0, nil, fmt.Errorf("Unable to find groups for user %v: %v", users[0].Name, err)
227
-		}
228
-
229
-		haveGroup := groups != nil && len(groups) > 0
230
-		if groupArg != "" {
231
-			if haveGroup {
232
-				// if we found any group entries that matched our filter, let's take the first one as "correct"
233
-				gid = groups[0].Gid
234
-			} else {
235
-				// we asked for a group but didn't find id...  let's check to see if we wanted a numeric group
236
-				gid, err = strconv.Atoi(groupArg)
237
-				if err != nil {
238
-					// not numeric - we have to bail
239
-					return 0, 0, nil, fmt.Errorf("Unable to find group %v", groupArg)
240
-				}
241
-				if gid < minId || gid > maxId {
242
-					return 0, 0, nil, ErrRange
243
-				}
244
-
245
-				// if groupArg couldn't be found in /etc/group but is numeric, just roll with it - this is legit
246
-			}
247
-		} else if haveGroup {
248
-			suppGids = make([]int, len(groups))
249
-			for i, group := range groups {
250
-				suppGids[i] = group.Gid
251
-			}
252
-		}
253
-	}
254
-
255
-	return uid, gid, suppGids, nil
256
-}
257 1
deleted file mode 100644
... ...
@@ -1,94 +0,0 @@
1
-package user
2
-
3
-import (
4
-	"strings"
5
-	"testing"
6
-)
7
-
8
-func TestUserParseLine(t *testing.T) {
9
-	var (
10
-		a, b string
11
-		c    []string
12
-		d    int
13
-	)
14
-
15
-	parseLine("", &a, &b)
16
-	if a != "" || b != "" {
17
-		t.Fatalf("a and b should be empty ('%v', '%v')", a, b)
18
-	}
19
-
20
-	parseLine("a", &a, &b)
21
-	if a != "a" || b != "" {
22
-		t.Fatalf("a should be 'a' and b should be empty ('%v', '%v')", a, b)
23
-	}
24
-
25
-	parseLine("bad boys:corny cows", &a, &b)
26
-	if a != "bad boys" || b != "corny cows" {
27
-		t.Fatalf("a should be 'bad boys' and b should be 'corny cows' ('%v', '%v')", a, b)
28
-	}
29
-
30
-	parseLine("", &c)
31
-	if len(c) != 0 {
32
-		t.Fatalf("c should be empty (%#v)", c)
33
-	}
34
-
35
-	parseLine("d,e,f:g:h:i,j,k", &c, &a, &b, &c)
36
-	if a != "g" || b != "h" || len(c) != 3 || c[0] != "i" || c[1] != "j" || c[2] != "k" {
37
-		t.Fatalf("a should be 'g', b should be 'h', and c should be ['i','j','k'] ('%v', '%v', '%#v')", a, b, c)
38
-	}
39
-
40
-	parseLine("::::::::::", &a, &b, &c)
41
-	if a != "" || b != "" || len(c) != 0 {
42
-		t.Fatalf("a, b, and c should all be empty ('%v', '%v', '%#v')", a, b, c)
43
-	}
44
-
45
-	parseLine("not a number", &d)
46
-	if d != 0 {
47
-		t.Fatalf("d should be 0 (%v)", d)
48
-	}
49
-
50
-	parseLine("b:12:c", &a, &d, &b)
51
-	if a != "b" || b != "c" || d != 12 {
52
-		t.Fatalf("a should be 'b' and b should be 'c', and d should be 12 ('%v', '%v', %v)", a, b, d)
53
-	}
54
-}
55
-
56
-func TestUserParsePasswd(t *testing.T) {
57
-	users, err := parsePasswdFile(strings.NewReader(`
58
-root:x:0:0:root:/root:/bin/bash
59
-adm:x:3:4:adm:/var/adm:/bin/false
60
-this is just some garbage data
61
-`), nil)
62
-	if err != nil {
63
-		t.Fatalf("Unexpected error: %v", err)
64
-	}
65
-	if len(users) != 3 {
66
-		t.Fatalf("Expected 3 users, got %v", len(users))
67
-	}
68
-	if users[0].Uid != 0 || users[0].Name != "root" {
69
-		t.Fatalf("Expected users[0] to be 0 - root, got %v - %v", users[0].Uid, users[0].Name)
70
-	}
71
-	if users[1].Uid != 3 || users[1].Name != "adm" {
72
-		t.Fatalf("Expected users[1] to be 3 - adm, got %v - %v", users[1].Uid, users[1].Name)
73
-	}
74
-}
75
-
76
-func TestUserParseGroup(t *testing.T) {
77
-	groups, err := parseGroupFile(strings.NewReader(`
78
-root:x:0:root
79
-adm:x:4:root,adm,daemon
80
-this is just some garbage data
81
-`), nil)
82
-	if err != nil {
83
-		t.Fatalf("Unexpected error: %v", err)
84
-	}
85
-	if len(groups) != 3 {
86
-		t.Fatalf("Expected 3 groups, got %v", len(groups))
87
-	}
88
-	if groups[0].Gid != 0 || groups[0].Name != "root" || len(groups[0].List) != 1 {
89
-		t.Fatalf("Expected groups[0] to be 0 - root - 1 member, got %v - %v - %v", groups[0].Gid, groups[0].Name, len(groups[0].List))
90
-	}
91
-	if groups[1].Gid != 4 || groups[1].Name != "adm" || len(groups[1].List) != 3 {
92
-		t.Fatalf("Expected groups[1] to be 4 - adm - 3 members, got %v - %v - %v", groups[1].Gid, groups[1].Name, len(groups[1].List))
93
-	}
94
-}
... ...
@@ -21,10 +21,10 @@ install:
21 21
     - if [ -z "$TRAVIS_GLOBAL_WTF" ]; then go env; fi
22 22
     - go get -d -v ./...
23 23
     - if [ "$TRAVIS_GLOBAL_WTF" ]; then
24
-          export DOCKER_PATH="${GOPATH%%:*}/src/github.com/dotcloud/docker";
24
+          export DOCKER_PATH="${GOPATH%%:*}/src/github.com/docker/docker";
25 25
           mkdir -p "$DOCKER_PATH/hack/make";
26
-          ( cd "$DOCKER_PATH/hack/make" && wget -c 'https://raw.githubusercontent.com/dotcloud/docker/master/hack/make/'{.validate,validate-dco,validate-gofmt} );
27
-          sed -i 's!dotcloud/docker!docker/libcontainer!' "$DOCKER_PATH/hack/make/.validate";
26
+          ( cd "$DOCKER_PATH/hack/make" && wget -c 'https://raw.githubusercontent.com/docker/docker/master/hack/make/'{.validate,validate-dco,validate-gofmt} );
27
+          sed -i 's!docker/docker!docker/libcontainer!' "$DOCKER_PATH/hack/make/.validate";
28 28
       fi
29 29
 
30 30
 script:
... ...
@@ -176,7 +176,7 @@ One way to automate this, is customise your get ``commit.template`` by adding
176 176
 a ``prepare-commit-msg`` hook to your libcontainer checkout:
177 177
 
178 178
 ```
179
-curl -o .git/hooks/prepare-commit-msg https://raw.githubusercontent.com/dotcloud/docker/master/contrib/prepare-commit-msg.hook && chmod +x .git/hooks/prepare-commit-msg
179
+curl -o .git/hooks/prepare-commit-msg https://raw.githubusercontent.com/docker/docker/master/contrib/prepare-commit-msg.hook && chmod +x .git/hooks/prepare-commit-msg
180 180
 ```
181 181
 
182 182
 * Note: the above script expects to find your GitHub user name in ``git config --get github.user``
... ...
@@ -150,6 +150,10 @@ func (raw *data) parent(subsystem string) (string, error) {
150 150
 }
151 151
 
152 152
 func (raw *data) path(subsystem string) (string, error) {
153
+	// If the cgroup name/path is absolute do not look relative to the cgroup of the init process.
154
+	if filepath.IsAbs(raw.cgroup) {
155
+		return filepath.Join(raw.root, subsystem, raw.cgroup), nil
156
+	}
153 157
 	parent, err := raw.parent(subsystem)
154 158
 	if err != nil {
155 159
 		return "", err
... ...
@@ -5,6 +5,8 @@ import (
5 5
 	"os"
6 6
 	"path/filepath"
7 7
 	"testing"
8
+
9
+	"github.com/docker/libcontainer/cgroups"
8 10
 )
9 11
 
10 12
 const (
... ...
@@ -66,3 +68,20 @@ func TestGetCgroupParamsInt(t *testing.T) {
66 66
 		t.Fatal("Expecting error, got none")
67 67
 	}
68 68
 }
69
+
70
+func TestAbsolutePathHandling(t *testing.T) {
71
+	testCgroup := cgroups.Cgroup{
72
+		Name:   "bar",
73
+		Parent: "/foo",
74
+	}
75
+	cgroupData := data{
76
+		root:   "/sys/fs/cgroup",
77
+		cgroup: "/foo/bar",
78
+		c:      &testCgroup,
79
+		pid:    1,
80
+	}
81
+	expectedPath := filepath.Join(cgroupData.root, "cpu", testCgroup.Parent, testCgroup.Name)
82
+	if path, err := cgroupData.path("cpu"); path != expectedPath || err != nil {
83
+		t.Fatalf("expected path %s but got %s %s", expectedPath, path, err)
84
+	}
85
+}
... ...
@@ -2,6 +2,13 @@
2 2
 
3 3
 package label
4 4
 
5
+// InitLabels returns the process label and file labels to be used within
6
+// the container.  A list of options can be passed into this function to alter
7
+// the labels.
8
+func InitLabels(options []string) (string, string, error) {
9
+	return "", "", nil
10
+}
11
+
5 12
 func GenLabels(options string) (string, string, error) {
6 13
 	return "", "", nil
7 14
 }
... ...
@@ -22,7 +29,7 @@ func Relabel(path string, fileLabel string, relabel string) error {
22 22
 	return nil
23 23
 }
24 24
 
25
-func GetPidCon(pid int) (string, error) {
25
+func GetPidLabel(pid int) (string, error) {
26 26
 	return "", nil
27 27
 }
28 28
 
... ...
@@ -9,30 +9,49 @@ import (
9 9
 	"github.com/docker/libcontainer/selinux"
10 10
 )
11 11
 
12
-func GenLabels(options string) (string, string, error) {
12
+// InitLabels returns the process label and file labels to be used within
13
+// the container.  A list of options can be passed into this function to alter
14
+// the labels.  The labels returned will include a random MCS String, that is
15
+// guaranteed to be unique.
16
+func InitLabels(options []string) (string, string, error) {
13 17
 	if !selinux.SelinuxEnabled() {
14 18
 		return "", "", nil
15 19
 	}
16 20
 	var err error
17 21
 	processLabel, mountLabel := selinux.GetLxcContexts()
18 22
 	if processLabel != "" {
19
-		var (
20
-			s = strings.Fields(options)
21
-			l = len(s)
22
-		)
23
-		if l > 0 {
24
-			pcon := selinux.NewContext(processLabel)
25
-			for i := 0; i < l; i++ {
26
-				o := strings.Split(s[i], "=")
27
-				pcon[o[0]] = o[1]
23
+		pcon := selinux.NewContext(processLabel)
24
+		mcon := selinux.NewContext(mountLabel)
25
+		for _, opt := range options {
26
+			if opt == "disable" {
27
+				return "", "", nil
28
+			}
29
+			if i := strings.Index(opt, ":"); i == -1 {
30
+				return "", "", fmt.Errorf("Bad SELinux Option")
31
+			}
32
+			con := strings.SplitN(opt, ":", 2)
33
+			pcon[con[0]] = con[1]
34
+			if con[0] == "level" || con[0] == "user" {
35
+				mcon[con[0]] = con[1]
28 36
 			}
29
-			processLabel = pcon.Get()
30
-			mountLabel, err = selinux.CopyLevel(processLabel, mountLabel)
31 37
 		}
38
+		processLabel = pcon.Get()
39
+		mountLabel = mcon.Get()
32 40
 	}
33 41
 	return processLabel, mountLabel, err
34 42
 }
35 43
 
44
+// DEPRECATED: The GenLabels function is only to be used during the transition to the official API.
45
+func GenLabels(options string) (string, string, error) {
46
+	return InitLabels(strings.Fields(options))
47
+}
48
+
49
+// FormatMountLabel returns a string to be used by the mount command.
50
+// The format of this string will be used to alter the labeling of the mountpoint.
51
+// The string returned is suitable to be used as the options field of the mount command.
52
+// If you need to have additional mount point options, you can pass them in as
53
+// the first parameter.  Second parameter is the label that you wish to apply
54
+// to all content in the mount point.
36 55
 func FormatMountLabel(src, mountLabel string) string {
37 56
 	if mountLabel != "" {
38 57
 		switch src {
... ...
@@ -45,6 +64,8 @@ func FormatMountLabel(src, mountLabel string) string {
45 45
 	return src
46 46
 }
47 47
 
48
+// SetProcessLabel takes a process label and tells the kernel to assign the
49
+// label to the next program executed by the current process.
48 50
 func SetProcessLabel(processLabel string) error {
49 51
 	if selinux.SelinuxEnabled() {
50 52
 		return selinux.Setexeccon(processLabel)
... ...
@@ -52,6 +73,9 @@ func SetProcessLabel(processLabel string) error {
52 52
 	return nil
53 53
 }
54 54
 
55
+// GetProcessLabel returns the process label that the kernel will assign
56
+// to the next program executed by the current process.  If "" is returned
57
+// this indicates that the default labeling will happen for the process.
55 58
 func GetProcessLabel() (string, error) {
56 59
 	if selinux.SelinuxEnabled() {
57 60
 		return selinux.Getexeccon()
... ...
@@ -59,6 +83,7 @@ func GetProcessLabel() (string, error) {
59 59
 	return "", nil
60 60
 }
61 61
 
62
+// SetFileLabel modifies the "path" label to the specified file label
62 63
 func SetFileLabel(path string, fileLabel string) error {
63 64
 	if selinux.SelinuxEnabled() && fileLabel != "" {
64 65
 		return selinux.Setfilecon(path, fileLabel)
... ...
@@ -83,17 +108,22 @@ func Relabel(path string, fileLabel string, relabel string) error {
83 83
 	return selinux.Chcon(path, fileLabel, true)
84 84
 }
85 85
 
86
-func GetPidCon(pid int) (string, error) {
86
+// GetPidLabel will return the label of the process running with the specified pid
87
+func GetPidLabel(pid int) (string, error) {
87 88
 	if !selinux.SelinuxEnabled() {
88 89
 		return "", nil
89 90
 	}
90 91
 	return selinux.Getpidcon(pid)
91 92
 }
92 93
 
94
+// Init initialises the labeling system
93 95
 func Init() {
94 96
 	selinux.SelinuxEnabled()
95 97
 }
96 98
 
99
+// ReserveLabel will record the fact that the MCS label has already been used.
100
+// This will prevent InitLabels from using the MCS label in a newly created
101
+// container
97 102
 func ReserveLabel(label string) error {
98 103
 	selinux.ReserveLabel(label)
99 104
 	return nil
100 105
new file mode 100644
... ...
@@ -0,0 +1,48 @@
0
+// +build selinux,linux
1
+
2
+package label
3
+
4
+import (
5
+	"testing"
6
+
7
+	"github.com/docker/libcontainer/selinux"
8
+)
9
+
10
+func TestInit(t *testing.T) {
11
+	if selinux.SelinuxEnabled() {
12
+		var testNull []string
13
+		plabel, mlabel, err := InitLabels(testNull)
14
+		if err != nil {
15
+			t.Log("InitLabels Failed")
16
+			t.Fatal(err)
17
+		}
18
+		testDisabled := []string{"disable"}
19
+		plabel, mlabel, err = InitLabels(testDisabled)
20
+		if err != nil {
21
+			t.Log("InitLabels Disabled Failed")
22
+			t.Fatal(err)
23
+		}
24
+		if plabel != "" {
25
+			t.Log("InitLabels Disabled Failed")
26
+			t.Fatal()
27
+		}
28
+		testUser := []string{"user:user_u", "role:user_r", "type:user_t", "level:s0:c1,c15"}
29
+		plabel, mlabel, err = InitLabels(testUser)
30
+		if err != nil {
31
+			t.Log("InitLabels User Failed")
32
+			t.Fatal(err)
33
+		}
34
+		if plabel != "user_u:user_r:user_t:s0:c1,c15" || mlabel != "user_u:object_r:svirt_sandbox_file_t:s0:c1,c15" {
35
+			t.Log("InitLabels User Failed")
36
+			t.Log(plabel, mlabel)
37
+			t.Fatal(err)
38
+		}
39
+
40
+		testBadData := []string{"user", "role:user_r", "type:user_t", "level:s0:c1,c15"}
41
+		plabel, mlabel, err = InitLabels(testBadData)
42
+		if err == nil {
43
+			t.Log("InitLabels Bad Failed")
44
+			t.Fatal(err)
45
+		}
46
+	}
47
+}
... ...
@@ -9,7 +9,6 @@ import (
9 9
 	"strings"
10 10
 	"syscall"
11 11
 
12
-	"github.com/docker/docker/pkg/user"
13 12
 	"github.com/docker/libcontainer"
14 13
 	"github.com/docker/libcontainer/apparmor"
15 14
 	"github.com/docker/libcontainer/console"
... ...
@@ -21,6 +20,7 @@ import (
21 21
 	"github.com/docker/libcontainer/security/restrict"
22 22
 	"github.com/docker/libcontainer/syncpipe"
23 23
 	"github.com/docker/libcontainer/system"
24
+	"github.com/docker/libcontainer/user"
24 25
 	"github.com/docker/libcontainer/utils"
25 26
 )
26 27
 
... ...
@@ -119,7 +119,7 @@ func Init(container *libcontainer.Config, uncleanRootfs, consolePath string, syn
119 119
 		return fmt.Errorf("restore parent death signal %s", err)
120 120
 	}
121 121
 
122
-	return system.Execv(args[0], args[0:], container.Env)
122
+	return system.Execv(args[0], args[0:], os.Environ())
123 123
 }
124 124
 
125 125
 // RestoreParentDeathSignal sets the parent death signal to old.
... ...
@@ -152,7 +152,7 @@ func RestoreParentDeathSignal(old int) error {
152 152
 
153 153
 // SetupUser changes the groups, gid, and uid for the user inside the container
154 154
 func SetupUser(u string) error {
155
-	uid, gid, suppGids, err := user.GetUserGroupSupplementary(u, syscall.Getuid(), syscall.Getgid())
155
+	uid, gid, suppGids, home, err := user.GetUserGroupSupplementaryHome(u, syscall.Getuid(), syscall.Getgid(), "/")
156 156
 	if err != nil {
157 157
 		return fmt.Errorf("get supplementary groups %s", err)
158 158
 	}
... ...
@@ -169,6 +169,13 @@ func SetupUser(u string) error {
169 169
 		return fmt.Errorf("setuid %s", err)
170 170
 	}
171 171
 
172
+	// if we didn't get HOME already, set it based on the user's HOME
173
+	if envHome := os.Getenv("HOME"); envHome == "" {
174
+		if err := os.Setenv("HOME", home); err != nil {
175
+			return fmt.Errorf("set HOME %s", err)
176
+		}
177
+	}
178
+
172 179
 	return nil
173 180
 }
174 181
 
... ...
@@ -3,7 +3,6 @@
3 3
 package namespaces
4 4
 
5 5
 /*
6
-#include <dirent.h>
7 6
 #include <errno.h>
8 7
 #include <fcntl.h>
9 8
 #include <linux/limits.h>
... ...
@@ -12,7 +11,6 @@ package namespaces
12 12
 #include <stdio.h>
13 13
 #include <stdlib.h>
14 14
 #include <string.h>
15
-#include <sys/stat.h>
16 15
 #include <sys/types.h>
17 16
 #include <unistd.h>
18 17
 #include <getopt.h>
... ...
@@ -145,36 +143,31 @@ void nsenter() {
145 145
 	char ns_dir[PATH_MAX];
146 146
 	memset(ns_dir, 0, PATH_MAX);
147 147
 	snprintf(ns_dir, PATH_MAX - 1, "/proc/%d/ns/", init_pid);
148
-	struct dirent *dent;
149
-	DIR *dir = opendir(ns_dir);
150
-	if (dir == NULL) {
151
-		fprintf(stderr, "nsenter: Failed to open directory \"%s\" with error: \"%s\"\n", ns_dir, strerror(errno));
152
-		exit(1);
153
-	}
154 148
 
155
-	while((dent = readdir(dir)) != NULL) {
156
-		if(strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0 || strcmp(dent->d_name, "user") == 0) {
157
-			continue;
158
-		}
159
-
160
-		// Get and open the namespace for the init we are joining..
149
+	char* namespaces[] = {"ipc", "uts", "net", "pid", "mnt"};
150
+	const int num = sizeof(namespaces) / sizeof(char*);
151
+	int i;
152
+	for (i = 0; i < num; i++) {
161 153
 		char buf[PATH_MAX];
162 154
 		memset(buf, 0, PATH_MAX);
163
-		snprintf(buf, PATH_MAX - 1, "%s%s", ns_dir, dent->d_name);
155
+		snprintf(buf, PATH_MAX - 1, "%s%s", ns_dir, namespaces[i]);
164 156
 		int fd = open(buf, O_RDONLY);
165 157
 		if (fd == -1) {
166
-			fprintf(stderr, "nsenter: Failed to open ns file \"%s\" for ns \"%s\" with error: \"%s\"\n", buf, dent->d_name, strerror(errno));
158
+			// Ignore nonexistent namespaces.
159
+			if (errno == ENOENT)
160
+				continue;
161
+
162
+			fprintf(stderr, "nsenter: Failed to open ns file \"%s\" for ns \"%s\" with error: \"%s\"\n", buf, namespaces[i], strerror(errno));
167 163
 			exit(1);
168 164
 		}
169 165
 
170 166
 		// Set the namespace.
171 167
 		if (setns(fd, 0) == -1) {
172
-			fprintf(stderr, "nsenter: Failed to setns for \"%s\" with error: \"%s\"\n", dent->d_name, strerror(errno));
168
+			fprintf(stderr, "nsenter: Failed to setns for \"%s\" with error: \"%s\"\n", namespaces[i], strerror(errno));
173 169
 			exit(1);
174 170
 		}
175 171
 		close(fd);
176 172
 	}
177
-	closedir(dir);
178 173
 
179 174
 	// We must fork to actually enter the PID namespace.
180 175
 	int child = fork();
... ...
@@ -1,4 +1,4 @@
1
-// +build linux,cgo
1
+// +build cgo
2 2
 
3 3
 package system
4 4
 
... ...
@@ -1,4 +1,4 @@
1
-// +build linux,!cgo
1
+// +build !cgo
2 2
 
3 3
 package system
4 4
 
5 5
new file mode 100644
... ...
@@ -0,0 +1 @@
0
+Tianon Gravi <admwiggin@gmail.com> (@tianon)
0 1
new file mode 100644
... ...
@@ -0,0 +1,258 @@
0
+package user
1
+
2
+import (
3
+	"bufio"
4
+	"fmt"
5
+	"io"
6
+	"os"
7
+	"strconv"
8
+	"strings"
9
+)
10
+
11
+const (
12
+	minId = 0
13
+	maxId = 1<<31 - 1 //for 32-bit systems compatibility
14
+)
15
+
16
+var (
17
+	ErrRange = fmt.Errorf("Uids and gids must be in range %d-%d", minId, maxId)
18
+)
19
+
20
+type User struct {
21
+	Name  string
22
+	Pass  string
23
+	Uid   int
24
+	Gid   int
25
+	Gecos string
26
+	Home  string
27
+	Shell string
28
+}
29
+
30
+type Group struct {
31
+	Name string
32
+	Pass string
33
+	Gid  int
34
+	List []string
35
+}
36
+
37
+func parseLine(line string, v ...interface{}) {
38
+	if line == "" {
39
+		return
40
+	}
41
+
42
+	parts := strings.Split(line, ":")
43
+	for i, p := range parts {
44
+		if len(v) <= i {
45
+			// if we have more "parts" than we have places to put them, bail for great "tolerance" of naughty configuration files
46
+			break
47
+		}
48
+
49
+		switch e := v[i].(type) {
50
+		case *string:
51
+			// "root", "adm", "/bin/bash"
52
+			*e = p
53
+		case *int:
54
+			// "0", "4", "1000"
55
+			// ignore string to int conversion errors, for great "tolerance" of naughty configuration files
56
+			*e, _ = strconv.Atoi(p)
57
+		case *[]string:
58
+			// "", "root", "root,adm,daemon"
59
+			if p != "" {
60
+				*e = strings.Split(p, ",")
61
+			} else {
62
+				*e = []string{}
63
+			}
64
+		default:
65
+			// panic, because this is a programming/logic error, not a runtime one
66
+			panic("parseLine expects only pointers!  argument " + strconv.Itoa(i) + " is not a pointer!")
67
+		}
68
+	}
69
+}
70
+
71
+func ParsePasswd() ([]*User, error) {
72
+	return ParsePasswdFilter(nil)
73
+}
74
+
75
+func ParsePasswdFilter(filter func(*User) bool) ([]*User, error) {
76
+	f, err := os.Open("/etc/passwd")
77
+	if err != nil {
78
+		return nil, err
79
+	}
80
+	defer f.Close()
81
+	return parsePasswdFile(f, filter)
82
+}
83
+
84
+func parsePasswdFile(r io.Reader, filter func(*User) bool) ([]*User, error) {
85
+	var (
86
+		s   = bufio.NewScanner(r)
87
+		out = []*User{}
88
+	)
89
+
90
+	for s.Scan() {
91
+		if err := s.Err(); err != nil {
92
+			return nil, err
93
+		}
94
+
95
+		text := strings.TrimSpace(s.Text())
96
+		if text == "" {
97
+			continue
98
+		}
99
+
100
+		// see: man 5 passwd
101
+		//  name:password:UID:GID:GECOS:directory:shell
102
+		// Name:Pass:Uid:Gid:Gecos:Home:Shell
103
+		//  root:x:0:0:root:/root:/bin/bash
104
+		//  adm:x:3:4:adm:/var/adm:/bin/false
105
+		p := &User{}
106
+		parseLine(
107
+			text,
108
+			&p.Name, &p.Pass, &p.Uid, &p.Gid, &p.Gecos, &p.Home, &p.Shell,
109
+		)
110
+
111
+		if filter == nil || filter(p) {
112
+			out = append(out, p)
113
+		}
114
+	}
115
+
116
+	return out, nil
117
+}
118
+
119
+func ParseGroup() ([]*Group, error) {
120
+	return ParseGroupFilter(nil)
121
+}
122
+
123
+func ParseGroupFilter(filter func(*Group) bool) ([]*Group, error) {
124
+	f, err := os.Open("/etc/group")
125
+	if err != nil {
126
+		return nil, err
127
+	}
128
+	defer f.Close()
129
+	return parseGroupFile(f, filter)
130
+}
131
+
132
+func parseGroupFile(r io.Reader, filter func(*Group) bool) ([]*Group, error) {
133
+	var (
134
+		s   = bufio.NewScanner(r)
135
+		out = []*Group{}
136
+	)
137
+
138
+	for s.Scan() {
139
+		if err := s.Err(); err != nil {
140
+			return nil, err
141
+		}
142
+
143
+		text := s.Text()
144
+		if text == "" {
145
+			continue
146
+		}
147
+
148
+		// see: man 5 group
149
+		//  group_name:password:GID:user_list
150
+		// Name:Pass:Gid:List
151
+		//  root:x:0:root
152
+		//  adm:x:4:root,adm,daemon
153
+		p := &Group{}
154
+		parseLine(
155
+			text,
156
+			&p.Name, &p.Pass, &p.Gid, &p.List,
157
+		)
158
+
159
+		if filter == nil || filter(p) {
160
+			out = append(out, p)
161
+		}
162
+	}
163
+
164
+	return out, nil
165
+}
166
+
167
+// Given a string like "user", "1000", "user:group", "1000:1000", returns the uid, gid, list of supplementary group IDs, and home directory, if available and/or applicable.
168
+func GetUserGroupSupplementaryHome(userSpec string, defaultUid, defaultGid int, defaultHome string) (int, int, []int, string, error) {
169
+	var (
170
+		uid      = defaultUid
171
+		gid      = defaultGid
172
+		suppGids = []int{}
173
+		home     = defaultHome
174
+
175
+		userArg, groupArg string
176
+	)
177
+
178
+	// allow for userArg to have either "user" syntax, or optionally "user:group" syntax
179
+	parseLine(userSpec, &userArg, &groupArg)
180
+
181
+	users, err := ParsePasswdFilter(func(u *User) bool {
182
+		if userArg == "" {
183
+			return u.Uid == uid
184
+		}
185
+		return u.Name == userArg || strconv.Itoa(u.Uid) == userArg
186
+	})
187
+	if err != nil && !os.IsNotExist(err) {
188
+		if userArg == "" {
189
+			userArg = strconv.Itoa(uid)
190
+		}
191
+		return 0, 0, nil, "", fmt.Errorf("Unable to find user %v: %v", userArg, err)
192
+	}
193
+
194
+	haveUser := users != nil && len(users) > 0
195
+	if haveUser {
196
+		// if we found any user entries that matched our filter, let's take the first one as "correct"
197
+		uid = users[0].Uid
198
+		gid = users[0].Gid
199
+		home = users[0].Home
200
+	} else if userArg != "" {
201
+		// we asked for a user but didn't find them...  let's check to see if we wanted a numeric user
202
+		uid, err = strconv.Atoi(userArg)
203
+		if err != nil {
204
+			// not numeric - we have to bail
205
+			return 0, 0, nil, "", fmt.Errorf("Unable to find user %v", userArg)
206
+		}
207
+		if uid < minId || uid > maxId {
208
+			return 0, 0, nil, "", ErrRange
209
+		}
210
+
211
+		// if userArg couldn't be found in /etc/passwd but is numeric, just roll with it - this is legit
212
+	}
213
+
214
+	if groupArg != "" || (haveUser && users[0].Name != "") {
215
+		groups, err := ParseGroupFilter(func(g *Group) bool {
216
+			if groupArg != "" {
217
+				return g.Name == groupArg || strconv.Itoa(g.Gid) == groupArg
218
+			}
219
+			for _, u := range g.List {
220
+				if u == users[0].Name {
221
+					return true
222
+				}
223
+			}
224
+			return false
225
+		})
226
+		if err != nil && !os.IsNotExist(err) {
227
+			return 0, 0, nil, "", fmt.Errorf("Unable to find groups for user %v: %v", users[0].Name, err)
228
+		}
229
+
230
+		haveGroup := groups != nil && len(groups) > 0
231
+		if groupArg != "" {
232
+			if haveGroup {
233
+				// if we found any group entries that matched our filter, let's take the first one as "correct"
234
+				gid = groups[0].Gid
235
+			} else {
236
+				// we asked for a group but didn't find id...  let's check to see if we wanted a numeric group
237
+				gid, err = strconv.Atoi(groupArg)
238
+				if err != nil {
239
+					// not numeric - we have to bail
240
+					return 0, 0, nil, "", fmt.Errorf("Unable to find group %v", groupArg)
241
+				}
242
+				if gid < minId || gid > maxId {
243
+					return 0, 0, nil, "", ErrRange
244
+				}
245
+
246
+				// if groupArg couldn't be found in /etc/group but is numeric, just roll with it - this is legit
247
+			}
248
+		} else if haveGroup {
249
+			suppGids = make([]int, len(groups))
250
+			for i, group := range groups {
251
+				suppGids[i] = group.Gid
252
+			}
253
+		}
254
+	}
255
+
256
+	return uid, gid, suppGids, home, nil
257
+}
0 258
new file mode 100644
... ...
@@ -0,0 +1,94 @@
0
+package user
1
+
2
+import (
3
+	"strings"
4
+	"testing"
5
+)
6
+
7
+func TestUserParseLine(t *testing.T) {
8
+	var (
9
+		a, b string
10
+		c    []string
11
+		d    int
12
+	)
13
+
14
+	parseLine("", &a, &b)
15
+	if a != "" || b != "" {
16
+		t.Fatalf("a and b should be empty ('%v', '%v')", a, b)
17
+	}
18
+
19
+	parseLine("a", &a, &b)
20
+	if a != "a" || b != "" {
21
+		t.Fatalf("a should be 'a' and b should be empty ('%v', '%v')", a, b)
22
+	}
23
+
24
+	parseLine("bad boys:corny cows", &a, &b)
25
+	if a != "bad boys" || b != "corny cows" {
26
+		t.Fatalf("a should be 'bad boys' and b should be 'corny cows' ('%v', '%v')", a, b)
27
+	}
28
+
29
+	parseLine("", &c)
30
+	if len(c) != 0 {
31
+		t.Fatalf("c should be empty (%#v)", c)
32
+	}
33
+
34
+	parseLine("d,e,f:g:h:i,j,k", &c, &a, &b, &c)
35
+	if a != "g" || b != "h" || len(c) != 3 || c[0] != "i" || c[1] != "j" || c[2] != "k" {
36
+		t.Fatalf("a should be 'g', b should be 'h', and c should be ['i','j','k'] ('%v', '%v', '%#v')", a, b, c)
37
+	}
38
+
39
+	parseLine("::::::::::", &a, &b, &c)
40
+	if a != "" || b != "" || len(c) != 0 {
41
+		t.Fatalf("a, b, and c should all be empty ('%v', '%v', '%#v')", a, b, c)
42
+	}
43
+
44
+	parseLine("not a number", &d)
45
+	if d != 0 {
46
+		t.Fatalf("d should be 0 (%v)", d)
47
+	}
48
+
49
+	parseLine("b:12:c", &a, &d, &b)
50
+	if a != "b" || b != "c" || d != 12 {
51
+		t.Fatalf("a should be 'b' and b should be 'c', and d should be 12 ('%v', '%v', %v)", a, b, d)
52
+	}
53
+}
54
+
55
+func TestUserParsePasswd(t *testing.T) {
56
+	users, err := parsePasswdFile(strings.NewReader(`
57
+root:x:0:0:root:/root:/bin/bash
58
+adm:x:3:4:adm:/var/adm:/bin/false
59
+this is just some garbage data
60
+`), nil)
61
+	if err != nil {
62
+		t.Fatalf("Unexpected error: %v", err)
63
+	}
64
+	if len(users) != 3 {
65
+		t.Fatalf("Expected 3 users, got %v", len(users))
66
+	}
67
+	if users[0].Uid != 0 || users[0].Name != "root" {
68
+		t.Fatalf("Expected users[0] to be 0 - root, got %v - %v", users[0].Uid, users[0].Name)
69
+	}
70
+	if users[1].Uid != 3 || users[1].Name != "adm" {
71
+		t.Fatalf("Expected users[1] to be 3 - adm, got %v - %v", users[1].Uid, users[1].Name)
72
+	}
73
+}
74
+
75
+func TestUserParseGroup(t *testing.T) {
76
+	groups, err := parseGroupFile(strings.NewReader(`
77
+root:x:0:root
78
+adm:x:4:root,adm,daemon
79
+this is just some garbage data
80
+`), nil)
81
+	if err != nil {
82
+		t.Fatalf("Unexpected error: %v", err)
83
+	}
84
+	if len(groups) != 3 {
85
+		t.Fatalf("Expected 3 groups, got %v", len(groups))
86
+	}
87
+	if groups[0].Gid != 0 || groups[0].Name != "root" || len(groups[0].List) != 1 {
88
+		t.Fatalf("Expected groups[0] to be 0 - root - 1 member, got %v - %v - %v", groups[0].Gid, groups[0].Name, len(groups[0].List))
89
+	}
90
+	if groups[1].Gid != 4 || groups[1].Name != "adm" || len(groups[1].List) != 3 {
91
+		t.Fatalf("Expected groups[1] to be 4 - adm - 3 members, got %v - %v - %v", groups[1].Gid, groups[1].Name, len(groups[1].List))
92
+	}
93
+}